summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.cvsignore2
-rw-r--r--COPYING339
-rw-r--r--Manifest78
-rw-r--r--README225
-rw-r--r--Read-Manifest-Now0
-rw-r--r--Roadmap30
-rw-r--r--WHATSNEW.txt1072
-rw-r--r--docs/README-NOW10
-rw-r--r--examples/LDAP/README74
-rwxr-xr-xexamples/LDAP/convertSambaAccount233
-rw-r--r--examples/LDAP/ldapsync.pl122
-rw-r--r--examples/LDAP/samba-nds.schema151
-rw-r--r--examples/LDAP/samba-schema-netscapeds4.x112
-rw-r--r--examples/LDAP/samba-schema-netscapeds5.x55
-rw-r--r--examples/LDAP/samba-schema.IBMSecureWay43
-rw-r--r--examples/LDAP/samba.schema424
-rw-r--r--examples/LDAP/samba.schema.at.IBM-DS59
-rw-r--r--examples/LDAP/samba.schema.oc.IBM-DS19
-rw-r--r--examples/LDAP/smbldap-tools/CONTRIBUTORS33
-rw-r--r--examples/LDAP/smbldap-tools/COPYING340
-rw-r--r--examples/LDAP/smbldap-tools/ChangeLog89
-rw-r--r--examples/LDAP/smbldap-tools/FILES43
-rw-r--r--examples/LDAP/smbldap-tools/INFRASTRUCTURE93
-rw-r--r--examples/LDAP/smbldap-tools/INSTALL28
-rw-r--r--examples/LDAP/smbldap-tools/Makefile35
-rw-r--r--examples/LDAP/smbldap-tools/README87
-rw-r--r--examples/LDAP/smbldap-tools/TODO28
-rw-r--r--examples/LDAP/smbldap-tools/cgi/README27
-rwxr-xr-xexamples/LDAP/smbldap-tools/cgi/ldappass.cgi202
-rw-r--r--examples/LDAP/smbldap-tools/mkntpwd/Makefile62
-rw-r--r--examples/LDAP/smbldap-tools/mkntpwd/getopt.c756
-rw-r--r--examples/LDAP/smbldap-tools/mkntpwd/getopt.h133
-rw-r--r--examples/LDAP/smbldap-tools/mkntpwd/md4.c171
-rw-r--r--examples/LDAP/smbldap-tools/mkntpwd/mkntpwd.c253
-rw-r--r--examples/LDAP/smbldap-tools/mkntpwd/mkntpwd.h17
-rw-r--r--examples/LDAP/smbldap-tools/mkntpwd/smbdes.c337
-rwxr-xr-xexamples/LDAP/smbldap-tools/smbldap-groupadd.pl158
-rwxr-xr-xexamples/LDAP/smbldap-tools/smbldap-groupdel.pl93
-rwxr-xr-xexamples/LDAP/smbldap-tools/smbldap-groupmod.pl283
-rwxr-xr-xexamples/LDAP/smbldap-tools/smbldap-groupshow.pl74
-rwxr-xr-xexamples/LDAP/smbldap-tools/smbldap-migrate-accounts.pl230
-rw-r--r--examples/LDAP/smbldap-tools/smbldap-migrate-groups.pl225
-rwxr-xr-xexamples/LDAP/smbldap-tools/smbldap-passwd.pl227
-rwxr-xr-xexamples/LDAP/smbldap-tools/smbldap-populate.pl370
-rwxr-xr-xexamples/LDAP/smbldap-tools/smbldap-tools.spec140
-rwxr-xr-xexamples/LDAP/smbldap-tools/smbldap-useradd.pl522
-rwxr-xr-xexamples/LDAP/smbldap-tools/smbldap-userdel.pl125
-rwxr-xr-xexamples/LDAP/smbldap-tools/smbldap-usermod.pl488
-rwxr-xr-xexamples/LDAP/smbldap-tools/smbldap-usershow.pl72
-rw-r--r--examples/LDAP/smbldap-tools/smbldap_conf.pm248
-rwxr-xr-xexamples/LDAP/smbldap-tools/smbldap_tools.pm771
-rw-r--r--examples/README11
-rw-r--r--examples/VFS/.cvsignore9
-rw-r--r--examples/VFS/Makefile.in43
-rw-r--r--examples/VFS/README20
-rwxr-xr-xexamples/VFS/autogen.sh60
-rw-r--r--examples/VFS/configure.in350
-rw-r--r--examples/VFS/install-sh238
-rw-r--r--examples/VFS/shadow_copy_test.c83
-rw-r--r--examples/VFS/skel_opaque.c581
-rw-r--r--examples/VFS/skel_transparent.c548
-rw-r--r--examples/appliance/Makefile68
-rw-r--r--examples/appliance/README52
-rw-r--r--examples/appliance/appliance.spec389
-rwxr-xr-xexamples/appliance/build.sh4
-rw-r--r--examples/appliance/smb.conf-appliance8
-rw-r--r--examples/auth/Makefile31
-rw-r--r--examples/auth/auth_skel.c59
-rw-r--r--examples/autofs/auto.a15
-rw-r--r--examples/dce-dfs/README4
-rw-r--r--examples/dce-dfs/smb.conf42
-rw-r--r--examples/genlogon/genlogon.pl71
-rw-r--r--examples/libsmbclient/Makefile36
-rw-r--r--examples/libsmbclient/README8
-rw-r--r--examples/libsmbclient/testacl.c280
-rw-r--r--examples/libsmbclient/testbrowse.c91
-rw-r--r--examples/libsmbclient/testsmbc.c335
-rw-r--r--examples/libsmbclient/tree.c813
-rw-r--r--examples/misc/extra_smbstatus50
-rwxr-xr-xexamples/misc/modify_samba_config.pl154
-rw-r--r--examples/misc/swat.pl122
-rw-r--r--examples/misc/wall.perl69
-rw-r--r--examples/nss/nss_winbind.c422
-rw-r--r--examples/nss/nss_winbind.h97
-rw-r--r--examples/nss/wbtest.c111
-rw-r--r--examples/ntlogon/README160
-rw-r--r--examples/ntlogon/ntlogon.conf44
-rwxr-xr-xexamples/ntlogon/ntlogon.py376
-rw-r--r--examples/pdb/Makefile31
-rw-r--r--examples/pdb/README16
-rw-r--r--examples/pdb/mysql/mysql.dump37
-rw-r--r--examples/pdb/mysql/smb.conf11
-rw-r--r--examples/pdb/pdb_test.c145
-rw-r--r--examples/pdb/sambapdb.dtd46
-rw-r--r--examples/printer-accounting/README63
-rw-r--r--examples/printer-accounting/acct-all9
-rw-r--r--examples/printer-accounting/acct-sum29
-rw-r--r--examples/printer-accounting/hp5-redir40
-rw-r--r--examples/printer-accounting/lp-acct35
-rw-r--r--examples/printer-accounting/printcap22
-rw-r--r--examples/printing/prtpub.c238
-rw-r--r--examples/printing/readme.prtpub12
-rwxr-xr-xexamples/printing/smbprint144
-rw-r--r--examples/printing/smbprint-new.sh144
-rwxr-xr-xexamples/printing/smbprint.old95
-rw-r--r--examples/printing/smbprint.sysv52
-rw-r--r--examples/simple/README2
-rw-r--r--examples/simple/smb.conf167
-rw-r--r--examples/smb.conf.default273
-rw-r--r--examples/svr4-startup/README24
-rwxr-xr-xexamples/svr4-startup/samba.server38
-rw-r--r--examples/thoralf/smb.conf152
-rw-r--r--examples/tridge/README8
-rw-r--r--examples/tridge/smb.conf101
-rw-r--r--examples/tridge/smb.conf.WinNT14
-rw-r--r--examples/tridge/smb.conf.fjall21
-rw-r--r--examples/tridge/smb.conf.lapland14
-rw-r--r--examples/tridge/smb.conf.vittjokk14
-rw-r--r--examples/validchars/msdos70.out257
-rw-r--r--examples/validchars/nwdos70.out257
-rw-r--r--examples/validchars/readme101
-rw-r--r--examples/validchars/validchr.c123
-rw-r--r--examples/validchars/validchr.com77
-rw-r--r--examples/wins_hook/README8
-rw-r--r--examples/wins_hook/dns_update94
-rw-r--r--make-tarball.sh43
-rw-r--r--packaging/Debian/README79
-rw-r--r--packaging/Debian/debian/README.build397
-rw-r--r--packaging/Debian/debian/README.build-upstream79
-rw-r--r--packaging/Debian/debian/README.debian163
-rw-r--r--packaging/Debian/debian/TODO4
-rw-r--r--packaging/Debian/debian/changelog2266
-rw-r--r--packaging/Debian/debian/config.cache221
-rw-r--r--packaging/Debian/debian/config.cache.alpha-linux12
-rw-r--r--packaging/Debian/debian/config.cache.sparc-linux13
-rw-r--r--packaging/Debian/debian/control188
-rw-r--r--packaging/Debian/debian/copyright28
-rw-r--r--packaging/Debian/debian/gdbcommands2
-rw-r--r--packaging/Debian/debian/libpam-smbpass.docs2
-rw-r--r--packaging/Debian/debian/libpam-smbpass.examples5
-rw-r--r--packaging/Debian/debian/libpam-smbpass.files1
-rw-r--r--packaging/Debian/debian/libsmbclient-dev.examples1
-rw-r--r--packaging/Debian/debian/libsmbclient-dev.files3
-rw-r--r--packaging/Debian/debian/libsmbclient.files2
-rw-r--r--packaging/Debian/debian/libsmbclient.shlibs1
-rw-r--r--packaging/Debian/debian/mksmbpasswd.828
-rw-r--r--packaging/Debian/debian/mksmbpasswd.awk5
-rw-r--r--packaging/Debian/debian/panic-action48
-rw-r--r--packaging/Debian/debian/patches/VERSION.patch8
-rw-r--r--packaging/Debian/debian/patches/documentation.patch68
-rw-r--r--packaging/Debian/debian/patches/fhs.patch570
-rw-r--r--packaging/Debian/debian/patches/installswat.sh.patch76
-rw-r--r--packaging/Debian/debian/patches/nmbd-signalling.patch20
-rw-r--r--packaging/Debian/debian/patches/samba.patch93
-rw-r--r--packaging/Debian/debian/patches/smbclient-pager.patch12
-rw-r--r--packaging/Debian/debian/patches/smbclient-tar.patch.unused43
-rw-r--r--packaging/Debian/debian/patches/smbmount-mtab-flags.patch15
-rw-r--r--packaging/Debian/debian/patches/smbmount-nomtab.patch160
-rw-r--r--packaging/Debian/debian/patches/smbstatus-locking.patch20
-rw-r--r--packaging/Debian/debian/po/POTFILES.in3
-rw-r--r--packaging/Debian/debian/po/es.po298
-rw-r--r--packaging/Debian/debian/po/fr.po301
-rw-r--r--packaging/Debian/debian/po/nl.po302
-rw-r--r--packaging/Debian/debian/po/pt_BR.po308
-rw-r--r--packaging/Debian/debian/po/templates.pot233
-rw-r--r--packaging/Debian/debian/python2.3-samba.files1
-rwxr-xr-xpackaging/Debian/debian/rules316
-rw-r--r--packaging/Debian/debian/samba-common.config154
-rw-r--r--packaging/Debian/debian/samba-common.dhcp34
-rw-r--r--packaging/Debian/debian/samba-common.dirs2
-rw-r--r--packaging/Debian/debian/samba-common.files15
-rw-r--r--packaging/Debian/debian/samba-common.postinst139
-rw-r--r--packaging/Debian/debian/samba-common.postrm10
-rw-r--r--packaging/Debian/debian/samba-common.templates66
-rw-r--r--packaging/Debian/debian/samba-doc.docs7
-rw-r--r--packaging/Debian/debian/samba-doc.examples2
-rw-r--r--packaging/Debian/debian/samba.config92
-rw-r--r--packaging/Debian/debian/samba.cron.daily16
-rw-r--r--packaging/Debian/debian/samba.dirs7
-rw-r--r--packaging/Debian/debian/samba.docs5
-rw-r--r--packaging/Debian/debian/samba.files19
-rw-r--r--packaging/Debian/debian/samba.init83
-rw-r--r--packaging/Debian/debian/samba.logrotate21
-rw-r--r--packaging/Debian/debian/samba.pamd3
-rw-r--r--packaging/Debian/debian/samba.postinst228
-rw-r--r--packaging/Debian/debian/samba.postrm26
-rw-r--r--packaging/Debian/debian/samba.prerm10
-rw-r--r--packaging/Debian/debian/samba.templates50
-rwxr-xr-xpackaging/Debian/debian/scripts/patch-source28
-rwxr-xr-xpackaging/Debian/debian/scripts/unpatch-source20
-rw-r--r--packaging/Debian/debian/smb.conf237
-rw-r--r--packaging/Debian/debian/smbclient.files15
-rw-r--r--packaging/Debian/debian/smbfs.files10
-rw-r--r--packaging/Debian/debian/smbwrapper.dirs2
-rw-r--r--packaging/Debian/debian/smbwrapper.docs2
-rw-r--r--packaging/Debian/debian/smbwrapper.files1
-rw-r--r--packaging/Debian/debian/swat.config11
-rw-r--r--packaging/Debian/debian/swat.dirs2
-rw-r--r--packaging/Debian/debian/swat.files2
-rw-r--r--packaging/Debian/debian/swat.postinst23
-rw-r--r--packaging/Debian/debian/swat.postrm22
-rw-r--r--packaging/Debian/debian/swat.templates6
-rw-r--r--packaging/Debian/debian/winbind.dirs1
-rw-r--r--packaging/Debian/debian/winbind.files7
-rw-r--r--packaging/Debian/debian/winbind.init48
-rw-r--r--packaging/Debian/debian/winbind.lintian6
-rw-r--r--packaging/Debian/debian/winbind.logrotate10
-rw-r--r--packaging/Debian/debian/wins2dns.awk38
-rw-r--r--packaging/Example/Instructions41
-rw-r--r--packaging/Example/PackageDate1
-rw-r--r--packaging/Example/Packager1
-rw-r--r--packaging/Example/Packaging-instructions16
-rwxr-xr-xpackaging/Example/package-prep51
-rwxr-xr-xpackaging/Example/samba.init34
-rwxr-xr-xpackaging/Example/setup.sh27
-rw-r--r--packaging/Example/skeleton.tarbin0 -> 30720 bytes
-rwxr-xr-xpackaging/Fedora/filter-requires-samba.sh3
-rw-r--r--packaging/Fedora/makerpms.sh.tmpl68
-rw-r--r--packaging/Fedora/samba.log9
-rw-r--r--packaging/Fedora/samba.pamd4
-rw-r--r--packaging/Fedora/samba.spec.tmpl398
-rw-r--r--packaging/Fedora/samba.sysconfig6
-rw-r--r--packaging/Fedora/samba.xinetd15
-rw-r--r--packaging/Fedora/smb.conf286
-rw-r--r--packaging/Fedora/smb.init119
-rw-r--r--packaging/Fedora/smbprint84
-rw-r--r--packaging/Fedora/smbusers3
-rw-r--r--packaging/Fedora/swat.desktop8
-rw-r--r--packaging/Fedora/winbind.init102
-rw-r--r--packaging/LSB/README6
-rw-r--r--packaging/LSB/lsb-samba.spec100
-rwxr-xr-xpackaging/LSB/samba.sh80
-rw-r--r--packaging/LSB/samba.xinetd15
-rw-r--r--packaging/LSB/smb.conf290
-rw-r--r--packaging/Mandrake/.cvsignore2
-rw-r--r--packaging/Mandrake/README11
-rw-r--r--packaging/Mandrake/README.mandrake117
-rw-r--r--packaging/Mandrake/empty.patch0
-rwxr-xr-xpackaging/Mandrake/findsmb141
-rw-r--r--packaging/Mandrake/makerpms-cvs.sh37
-rw-r--r--packaging/Mandrake/makerpms.sh.tmpl77
-rw-r--r--packaging/Mandrake/mount.cifs.8181
-rw-r--r--packaging/Mandrake/samba-2.2.0-buildroot.patch15
-rw-r--r--packaging/Mandrake/samba-3.0-smbmount-sbin.patch11
-rw-r--r--packaging/Mandrake/samba-print-pdf.sh111
-rw-r--r--packaging/Mandrake/samba-slapd-include.conf15
-rw-r--r--packaging/Mandrake/samba.log15
-rw-r--r--packaging/Mandrake/samba.pamd5
-rw-r--r--packaging/Mandrake/samba.xinetd15
-rw-r--r--packaging/Mandrake/samba2.spec.tmpl2344
-rw-r--r--packaging/Mandrake/smb.conf529
-rwxr-xr-xpackaging/Mandrake/smb.init100
-rwxr-xr-xpackaging/Mandrake/smbprint77
-rw-r--r--packaging/Mandrake/smbusers3
-rw-r--r--packaging/Mandrake/smbw.patch10
-rw-r--r--packaging/Mandrake/swat_16.png.bz2bin0 -> 1142 bytes
-rw-r--r--packaging/Mandrake/swat_32.png.bz2bin0 -> 2870 bytes
-rw-r--r--packaging/Mandrake/swat_48.png.bz2bin0 -> 5072 bytes
-rw-r--r--packaging/Mandrake/system-auth-winbind.pamd17
-rw-r--r--packaging/Mandrake/winbind.init93
-rw-r--r--packaging/Mandrake/wrepld.init93
-rw-r--r--packaging/README32
-rw-r--r--packaging/RedHat/.cvsignore6
-rw-r--r--packaging/RedHat/README13
-rwxr-xr-xpackaging/RedHat/filter-requires-samba_rh8.sh2
-rwxr-xr-xpackaging/RedHat/filter-requires-samba_rh9.sh2
-rw-r--r--packaging/RedHat/makerpms.sh.tmpl71
-rw-r--r--packaging/RedHat/samba.log11
-rw-r--r--packaging/RedHat/samba.pamd4
-rw-r--r--packaging/RedHat/samba.pamd.stack6
-rw-r--r--packaging/RedHat/samba.spec.tmpl457
-rw-r--r--packaging/RedHat/samba.xinetd15
-rw-r--r--packaging/RedHat/smb.conf286
-rwxr-xr-xpackaging/RedHat/smb.init59
-rwxr-xr-xpackaging/RedHat/smbprint77
-rw-r--r--packaging/RedHat/smbusers3
-rw-r--r--packaging/RedHat/winbind.init90
-rw-r--r--packaging/SGI/.cvsignore8
-rw-r--r--packaging/SGI/README44
-rwxr-xr-xpackaging/SGI/findsmb141
-rwxr-xr-xpackaging/SGI/idb.pl378
-rwxr-xr-xpackaging/SGI/inetd.sh37
-rwxr-xr-xpackaging/SGI/inst.msg31
-rw-r--r--packaging/SGI/legal_notice.html53
-rwxr-xr-xpackaging/SGI/mkman18
-rwxr-xr-xpackaging/SGI/mkprintcap.sh15
-rwxr-xr-xpackaging/SGI/mkrelease.sh126
-rw-r--r--packaging/SGI/printcap5
-rw-r--r--packaging/SGI/relnotes.html233
-rwxr-xr-xpackaging/SGI/removeswat.sh25
-rw-r--r--packaging/SGI/samba.config1
-rw-r--r--packaging/SGI/samba.rc43
-rw-r--r--packaging/SGI/sambalp157
-rw-r--r--packaging/SGI/smb.conf135
-rw-r--r--packaging/SGI/smbpasswd1
-rw-r--r--packaging/SGI/smbprint54
-rwxr-xr-xpackaging/SGI/spec.pl97
-rwxr-xr-xpackaging/SGI/startswat.sh29
-rw-r--r--packaging/SGI/winbindd.config1
-rw-r--r--packaging/SGI/winbindd.rc38
-rw-r--r--packaging/Solaris/.cvsignore4
-rw-r--r--packaging/Solaris/README18
-rw-r--r--packaging/Solaris/copyright1
-rw-r--r--packaging/Solaris/i.swat44
-rw-r--r--packaging/Solaris/inetd.conf.master1
-rwxr-xr-xpackaging/Solaris/makepkg.sh.tmpl176
-rw-r--r--packaging/Solaris/pkg-specs/pkginfo12
-rw-r--r--packaging/Solaris/pkginfo.master12
-rw-r--r--packaging/Solaris/postinstall21
-rw-r--r--packaging/Solaris/preremove12
-rw-r--r--packaging/Solaris/prototype.master54
-rw-r--r--packaging/Solaris/r.swat16
-rw-r--r--packaging/Solaris/request17
-rwxr-xr-xpackaging/Solaris/samba.server.master48
-rw-r--r--packaging/Solaris/services1
-rw-r--r--packaging/SuSE/README18
-rw-r--r--packaging/SuSE/samba-3.0.0.files.tar.bz2bin0 -> 7266 bytes
-rw-r--r--packaging/SuSE/samba-mutual-auth.diff247
-rwxr-xr-xpackaging/SuSE/samba-vscan-0.3.2b.tar.bz2bin0 -> 69479 bytes
-rw-r--r--packaging/SuSE/samba3-3.0.0-Makefiles-heimdal.diff22
-rw-r--r--packaging/SuSE/samba3-3.0.0-heimdal-06.diff14
-rw-r--r--packaging/SuSE/samba3-3.0.0-pdb.diff13
-rw-r--r--packaging/SuSE/samba3-Makefile.diff16
-rw-r--r--packaging/SuSE/samba3-com_err.diff60
-rw-r--r--packaging/SuSE/samba3-net_ads_password.diff58
-rw-r--r--packaging/SuSE/samba3-smbwrapper.diff11
-rw-r--r--packaging/SuSE/samba3-vscan.diff269
-rw-r--r--packaging/SuSE/samba3.spec766
-rwxr-xr-xpackaging/bin/update-pkginfo16
-rwxr-xr-xpcp/Install64
-rw-r--r--pcp/Makefile74
-rw-r--r--pcp/README94
-rwxr-xr-xpcp/Remove52
-rw-r--r--pcp/domain.h4
-rw-r--r--pcp/help77
-rwxr-xr-xpcp/mkheader.pl63
-rw-r--r--pcp/pmns45
-rw-r--r--pcp/root10
-rw-r--r--pcp/samba.c390
-rw-r--r--source/.cvsignore31
-rw-r--r--source/.dmallocrc2
-rw-r--r--source/Doxyfile176
-rw-r--r--source/Makefile.in1526
-rw-r--r--source/VERSION123
-rw-r--r--source/aclocal.m4775
-rw-r--r--source/aparser/Makefile17
-rwxr-xr-xsource/aparser/build13
-rw-r--r--source/aparser/cifs.struct1029
-rw-r--r--source/aparser/dump.awk70
-rw-r--r--source/aparser/harness.awk17
-rw-r--r--source/aparser/header.awk80
-rw-r--r--source/aparser/main.awk25
-rw-r--r--source/aparser/parsefn.awk271
-rw-r--r--source/aparser/parser.c471
-rw-r--r--source/aparser/parser.h103
-rw-r--r--source/aparser/parserel.awk213
-rw-r--r--source/aparser/parsetree.awk224
-rw-r--r--source/aparser/spool.struct90
-rw-r--r--source/aparser/spool_io_printer_driver_info_level_3.prsbin0 -> 5636 bytes
-rw-r--r--source/aparser/spool_io_printer_driver_info_level_6.prsbin0 -> 5636 bytes
-rw-r--r--source/aparser/srvsvc.struct184
-rw-r--r--source/aparser/srvsvc2.struct655
-rw-r--r--source/aparser/template.awk18
-rw-r--r--source/aparser/templates/fn_end.tpl13
-rw-r--r--source/aparser/templates/fn_end0.tpl8
-rw-r--r--source/aparser/templates/fn_i_end.tpl12
-rw-r--r--source/aparser/templates/fn_i_start.tpl15
-rw-r--r--source/aparser/templates/fn_mid.tpl6
-rw-r--r--source/aparser/templates/fn_start.tpl17
-rw-r--r--source/aparser/templates/harness.tpl5
-rw-r--r--source/aparser/templates/harness_end.tpl7
-rw-r--r--source/aparser/templates/harness_start.tpl7
-rw-r--r--source/aparser/templates/ifptr_end.tpl1
-rw-r--r--source/aparser/templates/ifptr_start.tpl2
-rw-r--r--source/aparser/templates/module_end.tpl3
-rw-r--r--source/aparser/templates/module_start.tpl5
-rw-r--r--source/aparser/templates/prs_.align.tpl1
-rw-r--r--source/aparser/templates/prs_align2.tpl1
-rw-r--r--source/aparser/templates/prs_align4.tpl1
-rw-r--r--source/aparser/templates/prs_array.tpl8
-rw-r--r--source/aparser/templates/prs_array_optional.tpl5
-rw-r--r--source/aparser/templates/prs_array_remainder.tpl17
-rw-r--r--source/aparser/templates/prs_break.tpl1
-rw-r--r--source/aparser/templates/prs_case.tpl1
-rw-r--r--source/aparser/templates/prs_case_end.tpl1
-rw-r--r--source/aparser/templates/prs_element.tpl1
-rw-r--r--source/aparser/templates/prs_pointer.tpl2
-rw-r--r--source/aparser/templates/prs_struct.tpl1
-rw-r--r--source/aparser/templates/prs_struct_alloc.tpl1
-rw-r--r--source/aparser/templates/prs_uint16.tpl1
-rw-r--r--source/aparser/templates/prs_uint32.tpl1
-rw-r--r--source/aparser/templates/prs_uint8s.tpl2
-rw-r--r--source/aparser/templates/prs_uint8s_fixed.tpl1
-rw-r--r--source/aparser/templates/prs_wstring.tpl2
-rw-r--r--source/aparser/templates/prs_wstring_fixed.tpl2
-rw-r--r--source/aparser/templates/union_end.tpl5
-rw-r--r--source/aparser/templates/union_start.tpl1
-rw-r--r--source/aparser/token.awk180
-rw-r--r--source/aparser/util.awk39
-rw-r--r--source/aparser/util.c112
-rw-r--r--source/aparser/vluke.c41
-rw-r--r--source/auth/auth.c534
-rw-r--r--source/auth/auth_builtin.c175
-rw-r--r--source/auth/auth_compat.c122
-rw-r--r--source/auth/auth_domain.c424
-rw-r--r--source/auth/auth_ntlmssp.c200
-rw-r--r--source/auth/auth_rhosts.c257
-rw-r--r--source/auth/auth_sam.c343
-rw-r--r--source/auth/auth_server.c417
-rw-r--r--source/auth/auth_unix.c136
-rw-r--r--source/auth/auth_util.c1500
-rw-r--r--source/auth/auth_winbind.c171
-rw-r--r--source/auth/pampass.c875
-rw-r--r--source/auth/pass_check.c783
-rwxr-xr-xsource/autogen.sh65
-rw-r--r--source/bin/.cvsignore56
-rw-r--r--source/change-log1878
-rw-r--r--source/client/.cvsignore1
-rw-r--r--source/client/client.c3085
-rw-r--r--source/client/clitar.c1807
-rwxr-xr-xsource/client/mount.cifs.c825
-rw-r--r--source/client/smbmnt.c306
-rw-r--r--source/client/smbmount.c940
-rw-r--r--source/client/smbspool.c363
-rw-r--r--source/client/smbumount.c186
-rw-r--r--source/client/tree.c811
-rw-r--r--source/codepages/.cvsignore0
-rw-r--r--source/codepages/lowcase.datbin0 -> 131072 bytes
-rw-r--r--source/codepages/upcase.datbin0 -> 131072 bytes
-rw-r--r--source/codepages/valid.datbin0 -> 65536 bytes
-rwxr-xr-xsource/config.guess1409
-rwxr-xr-xsource/config.sub1421
-rwxr-xr-xsource/configure.developer2
-rw-r--r--source/configure.in4442
-rw-r--r--source/dynconfig.c72
-rw-r--r--source/groupdb/.cvsignore2
-rw-r--r--source/groupdb/mapping.c1438
-rw-r--r--source/include/.cvsignore7
-rw-r--r--source/include/MacExtensions.h246
-rw-r--r--source/include/ads.h226
-rw-r--r--source/include/adt_tree.h38
-rw-r--r--source/include/asn_1.h69
-rw-r--r--source/include/auth.h172
-rw-r--r--source/include/authdata.h152
-rw-r--r--source/include/byteorder.h171
-rw-r--r--source/include/charset.h127
-rw-r--r--source/include/client.h169
-rw-r--r--source/include/clitar.h41
-rw-r--r--source/include/debug.h201
-rw-r--r--source/include/dlinklist.h78
-rw-r--r--source/include/doserr.h233
-rw-r--r--source/include/dynconfig.h38
-rw-r--r--source/include/fake_file.h46
-rw-r--r--source/include/genparser.h78
-rw-r--r--source/include/genparser_samba.h63
-rw-r--r--source/include/gums.h272
-rw-r--r--source/include/hash.h74
-rw-r--r--source/include/hmacmd5.h32
-rw-r--r--source/include/idmap.h57
-rw-r--r--source/include/includes.h1333
-rw-r--r--source/include/interfaces.h12
-rw-r--r--source/include/intl.h25
-rw-r--r--source/include/libsmb_internal.h67
-rw-r--r--source/include/libsmbclient.h1947
-rw-r--r--source/include/local.h233
-rw-r--r--source/include/mangle.h14
-rw-r--r--source/include/mapping.h33
-rw-r--r--source/include/md5.h24
-rw-r--r--source/include/messages.h71
-rw-r--r--source/include/modconf.h34
-rw-r--r--source/include/module.h38
-rw-r--r--source/include/msdfs.h85
-rw-r--r--source/include/nameserv.h627
-rw-r--r--source/include/nt_printing.h484
-rw-r--r--source/include/nt_status.h63
-rw-r--r--source/include/ntdomain.h417
-rw-r--r--source/include/nterr.h564
-rw-r--r--source/include/ntioctl.h87
-rw-r--r--source/include/ntlmssp.h169
-rw-r--r--source/include/ntquotas.h97
-rw-r--r--source/include/passdb.h573
-rw-r--r--source/include/popt_common.h49
-rw-r--r--source/include/printing.h91
-rw-r--r--source/include/privileges.h99
-rw-r--r--source/include/pstring.h36
-rwxr-xr-xsource/include/rap.h507
-rw-r--r--source/include/rpc_brs.h80
-rw-r--r--source/include/rpc_client.h28
-rw-r--r--source/include/rpc_creds.h96
-rw-r--r--source/include/rpc_dce.h361
-rw-r--r--source/include/rpc_dfs.h197
-rw-r--r--source/include/rpc_ds.h171
-rw-r--r--source/include/rpc_echo.h75
-rw-r--r--source/include/rpc_epmapper.h118
-rw-r--r--source/include/rpc_lsa.h744
-rw-r--r--source/include/rpc_misc.h408
-rw-r--r--source/include/rpc_netlogon.h930
-rw-r--r--source/include/rpc_parse.h30
-rw-r--r--source/include/rpc_reg.h644
-rw-r--r--source/include/rpc_samr.h1862
-rw-r--r--source/include/rpc_secdes.h469
-rw-r--r--source/include/rpc_shutdown.h70
-rwxr-xr-xsource/include/rpc_spoolss.h2234
-rw-r--r--source/include/rpc_srvsvc.h954
-rw-r--r--source/include/rpc_wkssvc.h72
-rw-r--r--source/include/safe_string.h228
-rw-r--r--source/include/samba_linux_quota.h336
-rw-r--r--source/include/samba_xfs_quota.h165
-rw-r--r--source/include/secrets.h105
-rw-r--r--source/include/session.h40
-rw-r--r--source/include/smb.h1679
-rw-r--r--source/include/smb_acls.h275
-rw-r--r--source/include/smb_macros.h264
-rw-r--r--source/include/smbldap.h159
-rw-r--r--source/include/smbprofile.h485
-rw-r--r--source/include/spnego.h65
-rw-r--r--source/include/srvstr.h33
-rw-r--r--source/include/stamp-h.in1
-rw-r--r--source/include/sysquotas.h76
-rw-r--r--source/include/talloc.h72
-rw-r--r--source/include/tdbsam2_parse_info.h2
-rw-r--r--source/include/trans2.h455
-rw-r--r--source/include/util_getent.h61
-rw-r--r--source/include/vfs.h489
-rw-r--r--source/include/vfs_macros.h318
-rw-r--r--source/include/xfile.h49
-rwxr-xr-xsource/install-sh238
-rw-r--r--source/intl/.cvsignore2
-rw-r--r--source/intl/lang_tdb.c269
-rw-r--r--source/intl/linux-msg.sed100
-rw-r--r--source/lib/.cvsignore3
-rw-r--r--source/lib/access.c333
-rw-r--r--source/lib/account_pol.c162
-rw-r--r--source/lib/adt_tree.c464
-rw-r--r--source/lib/afs.c486
-rw-r--r--source/lib/bitmap.c177
-rw-r--r--source/lib/charcnv.c1339
-rw-r--r--source/lib/clobber.c60
-rw-r--r--source/lib/crc32.c67
-rw-r--r--source/lib/data_blob.c115
-rw-r--r--source/lib/debug.c994
-rw-r--r--source/lib/dmallocmsg.c72
-rw-r--r--source/lib/dprintf.c113
-rw-r--r--source/lib/dummyroot.c33
-rw-r--r--source/lib/dummysmbd.c29
-rw-r--r--source/lib/fault.c83
-rw-r--r--source/lib/fsusage.c148
-rw-r--r--source/lib/gencache.c379
-rw-r--r--source/lib/genparser.c783
-rw-r--r--source/lib/genparser_samba.c218
-rw-r--r--source/lib/genrand.c267
-rw-r--r--source/lib/getsmbpass.c181
-rw-r--r--source/lib/hash.c316
-rw-r--r--source/lib/hmacmd5.c134
-rw-r--r--source/lib/iconv.c591
-rw-r--r--source/lib/interface.c338
-rw-r--r--source/lib/interfaces.c407
-rw-r--r--source/lib/ldap_escape.c90
-rw-r--r--source/lib/md4.c169
-rw-r--r--source/lib/md5.c247
-rw-r--r--source/lib/messages.c596
-rw-r--r--source/lib/module.c304
-rw-r--r--source/lib/ms_fnmatch.c249
-rw-r--r--source/lib/pam_errors.c126
-rw-r--r--source/lib/pidfile.c109
-rw-r--r--source/lib/popt_common.c394
-rw-r--r--source/lib/privileges.c442
-rw-r--r--source/lib/readline.c161
-rw-r--r--source/lib/replace.c452
-rw-r--r--source/lib/replace1.c42
-rw-r--r--source/lib/secace.c285
-rw-r--r--source/lib/secacl.c118
-rw-r--r--source/lib/secdesc.c522
-rw-r--r--source/lib/select.c159
-rw-r--r--source/lib/sendfile.c382
-rw-r--r--source/lib/server_mutex.c56
-rw-r--r--source/lib/signal.c139
-rw-r--r--source/lib/smbldap.c1234
-rw-r--r--source/lib/smbldap_util.c203
-rw-r--r--source/lib/smbrun.c180
-rw-r--r--source/lib/snprintf.c1021
-rw-r--r--source/lib/sock_exec.c115
-rw-r--r--source/lib/substitute.c802
-rw-r--r--source/lib/sysacls.c3198
-rw-r--r--source/lib/sysquotas.c505
-rw-r--r--source/lib/sysquotas_4A.c339
-rw-r--r--source/lib/sysquotas_linux.c560
-rw-r--r--source/lib/sysquotas_xfs.c333
-rw-r--r--source/lib/system.c1582
-rw-r--r--source/lib/system_smbd.c137
-rw-r--r--source/lib/talloc.c449
-rw-r--r--source/lib/tallocmsg.c58
-rw-r--r--source/lib/talloctort.c65
-rw-r--r--source/lib/time.c756
-rw-r--r--source/lib/ufc.c771
-rw-r--r--source/lib/username.c690
-rw-r--r--source/lib/util.c2501
-rw-r--r--source/lib/util_file.c606
-rw-r--r--source/lib/util_getent.c306
-rw-r--r--source/lib/util_pw.c89
-rw-r--r--source/lib/util_seaccess.c357
-rw-r--r--source/lib/util_sec.c467
-rw-r--r--source/lib/util_sid.c636
-rw-r--r--source/lib/util_smbd.c65
-rw-r--r--source/lib/util_sock.c1078
-rw-r--r--source/lib/util_str.c2047
-rw-r--r--source/lib/util_unistr.c847
-rw-r--r--source/lib/util_uuid.c174
-rw-r--r--source/lib/version.c42
-rw-r--r--source/lib/wins_srv.c356
-rw-r--r--source/lib/xfile.c378
-rw-r--r--source/libads/.cvsignore2
-rw-r--r--source/libads/ads_ldap.c234
-rw-r--r--source/libads/ads_status.c142
-rw-r--r--source/libads/ads_struct.c143
-rw-r--r--source/libads/ads_utils.c137
-rw-r--r--source/libads/authdata.c617
-rw-r--r--source/libads/disp_sec.c159
-rw-r--r--source/libads/kerberos.c175
-rw-r--r--source/libads/kerberos_verify.c271
-rw-r--r--source/libads/krb5_setpw.c690
-rw-r--r--source/libads/ldap.c2135
-rw-r--r--source/libads/ldap_printer.c354
-rw-r--r--source/libads/ldap_user.c119
-rw-r--r--source/libads/ldap_utils.c108
-rw-r--r--source/libads/sasl.c438
-rw-r--r--source/libads/util.c62
-rw-r--r--source/libsmb/.cvsignore3
-rw-r--r--source/libsmb/asn1.c441
-rw-r--r--source/libsmb/cliconnect.c1683
-rw-r--r--source/libsmb/clidgram.c264
-rw-r--r--source/libsmb/clientgen.c455
-rw-r--r--source/libsmb/clierror.c405
-rw-r--r--source/libsmb/clifile.c1417
-rw-r--r--source/libsmb/clifsinfo.c76
-rw-r--r--source/libsmb/clikrb5.c421
-rw-r--r--source/libsmb/clilist.c487
-rw-r--r--source/libsmb/climessage.c156
-rw-r--r--source/libsmb/clioplock.c68
-rw-r--r--source/libsmb/cliprint.c158
-rw-r--r--source/libsmb/cliquota.c633
-rw-r--r--source/libsmb/clirap.c743
-rw-r--r--source/libsmb/clirap2.c1970
-rw-r--r--source/libsmb/clireadwrite.c418
-rw-r--r--source/libsmb/clisecdesc.c139
-rw-r--r--source/libsmb/clispnego.c539
-rw-r--r--source/libsmb/clistr.c58
-rw-r--r--source/libsmb/clitrans.c658
-rw-r--r--source/libsmb/conncache.c158
-rw-r--r--source/libsmb/credentials.c215
-rw-r--r--source/libsmb/doserr.c92
-rw-r--r--source/libsmb/errormap.c1548
-rw-r--r--source/libsmb/libsmb_cache.c196
-rw-r--r--source/libsmb/libsmb_compat.c434
-rw-r--r--source/libsmb/libsmbclient.c4534
-rw-r--r--source/libsmb/namecache.c321
-rw-r--r--source/libsmb/namequery.c1406
-rw-r--r--source/libsmb/namequery_dc.c185
-rw-r--r--source/libsmb/nmblib.c1343
-rw-r--r--source/libsmb/nterr.c719
-rw-r--r--source/libsmb/ntlm_check.c377
-rw-r--r--source/libsmb/ntlmssp.c1112
-rw-r--r--source/libsmb/ntlmssp_parse.c315
-rw-r--r--source/libsmb/ntlmssp_sign.c407
-rw-r--r--source/libsmb/passchange.c152
-rw-r--r--source/libsmb/pwd_cache.c63
-rw-r--r--source/libsmb/samlogon_cache.c238
-rw-r--r--source/libsmb/smb_signing.c1052
-rw-r--r--source/libsmb/smbdes.c455
-rw-r--r--source/libsmb/smbencrypt.c526
-rw-r--r--source/libsmb/smberr.c256
-rw-r--r--source/libsmb/spnego.c343
-rw-r--r--source/libsmb/trustdom_cache.c342
-rw-r--r--source/libsmb/trusts_util.c190
-rw-r--r--source/libsmb/unexpected.c164
-rw-r--r--source/locking/brlock.c714
-rw-r--r--source/locking/locking.c881
-rw-r--r--source/locking/posix.c1312
-rw-r--r--source/mainpage.dox7
-rw-r--r--source/modules/.cvsignore1
-rw-r--r--source/modules/CP437.c136
-rw-r--r--source/modules/CP850.c122
-rw-r--r--source/modules/charset_macosxfs.c602
-rw-r--r--source/modules/developer.c132
-rw-r--r--source/modules/getdate.c2460
-rw-r--r--source/modules/getdate.h46
-rw-r--r--source/modules/getdate.y1115
-rw-r--r--source/modules/vfs_audit.c280
-rw-r--r--source/modules/vfs_cap.c448
-rw-r--r--source/modules/vfs_default_quota.c180
-rw-r--r--source/modules/vfs_expand_msdfs.c191
-rw-r--r--source/modules/vfs_extd_audit.c343
-rw-r--r--source/modules/vfs_fake_perms.c81
-rw-r--r--source/modules/vfs_netatalk.c401
-rw-r--r--source/modules/vfs_readonly.c98
-rw-r--r--source/modules/vfs_recycle.c498
-rw-r--r--source/modules/weird.c131
-rw-r--r--source/msdfs/msdfs.c1017
-rw-r--r--source/nmbd/.cvsignore0
-rw-r--r--source/nmbd/asyncdns.c353
-rw-r--r--source/nmbd/nmbd.c771
-rw-r--r--source/nmbd/nmbd_become_dmb.c386
-rw-r--r--source/nmbd/nmbd_become_lmb.c594
-rw-r--r--source/nmbd/nmbd_browserdb.c175
-rw-r--r--source/nmbd/nmbd_browsesync.c676
-rw-r--r--source/nmbd/nmbd_elections.c391
-rw-r--r--source/nmbd/nmbd_incomingdgrams.c844
-rw-r--r--source/nmbd/nmbd_incomingrequests.c587
-rw-r--r--source/nmbd/nmbd_lmhosts.c93
-rw-r--r--source/nmbd/nmbd_logonnames.c173
-rw-r--r--source/nmbd/nmbd_mynames.c224
-rw-r--r--source/nmbd/nmbd_namelistdb.c584
-rw-r--r--source/nmbd/nmbd_namequery.c268
-rw-r--r--source/nmbd/nmbd_nameregister.c537
-rw-r--r--source/nmbd/nmbd_namerelease.c222
-rw-r--r--source/nmbd/nmbd_nodestatus.c92
-rw-r--r--source/nmbd/nmbd_packets.c1885
-rw-r--r--source/nmbd/nmbd_processlogon.c522
-rw-r--r--source/nmbd/nmbd_responserecordsdb.c248
-rw-r--r--source/nmbd/nmbd_sendannounce.c581
-rw-r--r--source/nmbd/nmbd_serverlistdb.c422
-rw-r--r--source/nmbd/nmbd_subnetdb.c344
-rw-r--r--source/nmbd/nmbd_synclists.c304
-rw-r--r--source/nmbd/nmbd_winsproxy.c213
-rw-r--r--source/nmbd/nmbd_winsserver.c2015
-rw-r--r--source/nmbd/nmbd_workgroupdb.c319
-rw-r--r--source/nsswitch/.cvsignore4
-rw-r--r--source/nsswitch/pam_winbind.c754
-rw-r--r--source/nsswitch/pam_winbind.h96
-rw-r--r--source/nsswitch/wb_client.c693
-rw-r--r--source/nsswitch/wb_common.c494
-rw-r--r--source/nsswitch/wbinfo.c1313
-rw-r--r--source/nsswitch/winbind_client.h16
-rw-r--r--source/nsswitch/winbind_nss.h71
-rw-r--r--source/nsswitch/winbind_nss_aix.c1019
-rw-r--r--source/nsswitch/winbind_nss_config.h142
-rw-r--r--source/nsswitch/winbind_nss_freebsd.c81
-rw-r--r--source/nsswitch/winbind_nss_hpux.h139
-rw-r--r--source/nsswitch/winbind_nss_irix.c515
-rw-r--r--source/nsswitch/winbind_nss_irix.h48
-rw-r--r--source/nsswitch/winbind_nss_linux.c1149
-rw-r--r--source/nsswitch/winbind_nss_linux.h35
-rw-r--r--source/nsswitch/winbind_nss_solaris.c315
-rw-r--r--source/nsswitch/winbind_nss_solaris.h61
-rw-r--r--source/nsswitch/winbindd.c947
-rw-r--r--source/nsswitch/winbindd.h238
-rw-r--r--source/nsswitch/winbindd_acct.c1223
-rw-r--r--source/nsswitch/winbindd_ads.c1018
-rw-r--r--source/nsswitch/winbindd_cache.c1377
-rw-r--r--source/nsswitch/winbindd_cm.c773
-rw-r--r--source/nsswitch/winbindd_dual.c214
-rw-r--r--source/nsswitch/winbindd_group.c1211
-rw-r--r--source/nsswitch/winbindd_misc.c307
-rw-r--r--source/nsswitch/winbindd_nss.h288
-rw-r--r--source/nsswitch/winbindd_pam.c568
-rw-r--r--source/nsswitch/winbindd_passdb.c339
-rw-r--r--source/nsswitch/winbindd_rpc.c1007
-rw-r--r--source/nsswitch/winbindd_sid.c456
-rw-r--r--source/nsswitch/winbindd_user.c667
-rw-r--r--source/nsswitch/winbindd_util.c994
-rw-r--r--source/nsswitch/winbindd_wins.c226
-rw-r--r--source/nsswitch/wins.c379
-rw-r--r--source/pam_smbpass/.cvsignore1
-rw-r--r--source/pam_smbpass/CHANGELOG31
-rw-r--r--source/pam_smbpass/INSTALL64
-rw-r--r--source/pam_smbpass/README60
-rw-r--r--source/pam_smbpass/TODO7
-rw-r--r--source/pam_smbpass/general.h130
-rw-r--r--source/pam_smbpass/pam_smb_acct.c121
-rw-r--r--source/pam_smbpass/pam_smb_auth.c256
-rw-r--r--source/pam_smbpass/pam_smb_passwd.c349
-rw-r--r--source/pam_smbpass/samples/README3
-rw-r--r--source/pam_smbpass/samples/kdc-pdc15
-rw-r--r--source/pam_smbpass/samples/password-mature14
-rw-r--r--source/pam_smbpass/samples/password-migration18
-rw-r--r--source/pam_smbpass/samples/password-sync15
-rw-r--r--source/pam_smbpass/support.c635
-rw-r--r--source/pam_smbpass/support.h50
-rw-r--r--source/param/.cvsignore3
-rw-r--r--source/param/config_ldap.c351
-rw-r--r--source/param/loadparm.c4277
-rw-r--r--source/param/modconf.c96
-rw-r--r--source/param/params.c599
-rw-r--r--source/passdb/.cvsignore2
-rw-r--r--source/passdb/login_cache.c174
-rw-r--r--source/passdb/lookup_sid.c529
-rw-r--r--source/passdb/machine_sid.c213
-rw-r--r--source/passdb/passdb.c2388
-rw-r--r--source/passdb/pdb_compat.c104
-rw-r--r--source/passdb/pdb_get_set.c1158
-rw-r--r--source/passdb/pdb_guest.c179
-rw-r--r--source/passdb/pdb_gums.c464
-rw-r--r--source/passdb/pdb_interface.c1601
-rw-r--r--source/passdb/pdb_ldap.c3117
-rw-r--r--source/passdb/pdb_mysql.c497
-rw-r--r--source/passdb/pdb_pgsql.c488
-rw-r--r--source/passdb/pdb_plugin.c80
-rw-r--r--source/passdb/pdb_smbpasswd.c1517
-rw-r--r--source/passdb/pdb_sql.c493
-rw-r--r--source/passdb/pdb_tdb.c1374
-rw-r--r--source/passdb/pdb_xml.c563
-rw-r--r--source/passdb/privileges.c341
-rw-r--r--source/passdb/secrets.c824
-rw-r--r--source/passdb/util_sam_sid.c332
-rw-r--r--source/po/de.msg593
-rw-r--r--source/po/en.msg593
-rw-r--r--source/po/fr.msg593
-rwxr-xr-xsource/po/genmsg40
-rw-r--r--source/po/it.msg593
-rw-r--r--source/po/ja.msg595
-rw-r--r--source/po/nl.msg593
-rw-r--r--source/po/pl.msg593
-rw-r--r--source/po/tr.msg594
-rw-r--r--source/popt/.cvsignore8
-rw-r--r--source/popt/CHANGES43
-rw-r--r--source/popt/COPYING22
-rw-r--r--source/popt/README18
-rw-r--r--source/popt/dummy.in0
-rw-r--r--source/popt/findme.c46
-rw-r--r--source/popt/findme.h10
-rw-r--r--source/popt/popt.c782
-rw-r--r--source/popt/popt.h130
-rw-r--r--source/popt/poptconfig.c142
-rw-r--r--source/popt/popthelp.c301
-rw-r--r--source/popt/poptint.h71
-rw-r--r--source/popt/poptparse.c102
-rw-r--r--source/popt/system.h53
-rw-r--r--source/printing/.cvsignore0
-rw-r--r--source/printing/load.c73
-rw-r--r--source/printing/lpq_parse.c1016
-rw-r--r--source/printing/notify.c555
-rw-r--r--source/printing/nt_printing.c4990
-rw-r--r--source/printing/pcap.c413
-rw-r--r--source/printing/print_cups.c1303
-rw-r--r--source/printing/print_generic.c247
-rw-r--r--source/printing/print_svid.c144
-rw-r--r--source/printing/printfsp.c120
-rw-r--r--source/printing/printing.c2420
-rw-r--r--source/printing/printing_db.c206
-rw-r--r--source/profile/profile.c166
-rw-r--r--source/python/.cvsignore1
-rw-r--r--source/python/README28
-rwxr-xr-xsource/python/examples/spoolss/changeid.py34
-rwxr-xr-xsource/python/examples/spoolss/enumprinters.py36
-rwxr-xr-xsource/python/examples/spoolss/psec.py88
-rw-r--r--source/python/examples/tdbpack/.cvsignore2
-rw-r--r--source/python/examples/tdbpack/oldtdbutil.py144
-rwxr-xr-xsource/python/examples/tdbpack/tdbtimetrial.py12
-rwxr-xr-xsource/python/examples/tdbpack/test_tdbpack.py253
-rwxr-xr-xsource/python/gprinterdata39
-rwxr-xr-xsource/python/gtdbtool39
-rwxr-xr-xsource/python/gtkdictbrowser.py272
-rw-r--r--source/python/py_common.c259
-rw-r--r--source/python/py_common.h67
-rw-r--r--source/python/py_conv.c223
-rw-r--r--source/python/py_conv.h44
-rw-r--r--source/python/py_lsa.c484
-rw-r--r--source/python/py_lsa.h41
-rw-r--r--source/python/py_ntsec.c288
-rw-r--r--source/python/py_samr.c676
-rw-r--r--source/python/py_samr.h86
-rw-r--r--source/python/py_samr_conv.c131
-rw-r--r--source/python/py_smb.c442
-rw-r--r--source/python/py_smb.h39
-rw-r--r--source/python/py_spoolss.c485
-rw-r--r--source/python/py_spoolss.h160
-rw-r--r--source/python/py_spoolss_common.c35
-rw-r--r--source/python/py_spoolss_drivers.c422
-rw-r--r--source/python/py_spoolss_drivers_conv.c179
-rw-r--r--source/python/py_spoolss_forms.c266
-rw-r--r--source/python/py_spoolss_forms_conv.c91
-rw-r--r--source/python/py_spoolss_jobs.c377
-rw-r--r--source/python/py_spoolss_jobs_conv.c102
-rw-r--r--source/python/py_spoolss_ports.c137
-rw-r--r--source/python/py_spoolss_ports_conv.c58
-rw-r--r--source/python/py_spoolss_printerdata.c450
-rw-r--r--source/python/py_spoolss_printers.c475
-rw-r--r--source/python/py_spoolss_printers_conv.c354
-rw-r--r--source/python/py_srvsvc.c215
-rw-r--r--source/python/py_srvsvc.h30
-rw-r--r--source/python/py_srvsvc_conv.c41
-rw-r--r--source/python/py_tdb.c702
-rw-r--r--source/python/py_tdb.h26
-rw-r--r--source/python/py_tdbpack.c721
-rw-r--r--source/python/py_winbind.c800
-rw-r--r--source/python/py_winbind.h30
-rw-r--r--source/python/py_winbind_conv.c41
-rw-r--r--source/python/py_winreg.c82
-rw-r--r--source/python/py_winreg.h26
-rw-r--r--source/python/samba/.cvsignore1
-rw-r--r--source/python/samba/__init__.py7
-rw-r--r--source/python/samba/printerdata.py65
-rwxr-xr-xsource/python/setup.py200
-rw-r--r--source/registry/.cvsignore2
-rw-r--r--source/registry/reg_cachehook.c112
-rw-r--r--source/registry/reg_db.c311
-rw-r--r--source/registry/reg_frontend.c260
-rw-r--r--source/registry/reg_objects.c374
-rw-r--r--source/registry/reg_printing.c826
-rw-r--r--source/rpc_client/.cvsignore3
-rw-r--r--source/rpc_client/cli_dfs.c247
-rw-r--r--source/rpc_client/cli_ds.c157
-rw-r--r--source/rpc_client/cli_echo.c187
-rw-r--r--source/rpc_client/cli_epmapper.c61
-rw-r--r--source/rpc_client/cli_lsarpc.c1466
-rw-r--r--source/rpc_client/cli_netlogon.c802
-rw-r--r--source/rpc_client/cli_pipe.c1651
-rw-r--r--source/rpc_client/cli_reg.c103
-rw-r--r--source/rpc_client/cli_samr.c2102
-rw-r--r--source/rpc_client/cli_shutdown.c104
-rw-r--r--source/rpc_client/cli_spoolss.c2466
-rw-r--r--source/rpc_client/cli_spoolss_notify.c272
-rw-r--r--source/rpc_client/cli_srvsvc.c442
-rw-r--r--source/rpc_client/cli_wkssvc.c93
-rw-r--r--source/rpc_parse/.cvsignore2
-rw-r--r--source/rpc_parse/parse_dfs.c546
-rw-r--r--source/rpc_parse/parse_ds.c302
-rw-r--r--source/rpc_parse/parse_echo.c166
-rw-r--r--source/rpc_parse/parse_epmapper.c482
-rw-r--r--source/rpc_parse/parse_lsa.c2474
-rw-r--r--source/rpc_parse/parse_misc.c1765
-rw-r--r--source/rpc_parse/parse_net.c3048
-rw-r--r--source/rpc_parse/parse_prs.c1624
-rw-r--r--source/rpc_parse/parse_reg.c1856
-rw-r--r--source/rpc_parse/parse_rpc.c1219
-rw-r--r--source/rpc_parse/parse_samr.c7387
-rw-r--r--source/rpc_parse/parse_sec.c366
-rw-r--r--source/rpc_parse/parse_shutdown.c163
-rw-r--r--source/rpc_parse/parse_spoolss.c7741
-rw-r--r--source/rpc_parse/parse_srv.c3575
-rw-r--r--source/rpc_parse/parse_wks.c178
-rw-r--r--source/rpc_server/.cvsignore0
-rw-r--r--source/rpc_server/srv_dfs.c179
-rw-r--r--source/rpc_server/srv_dfs_nt.c377
-rw-r--r--source/rpc_server/srv_echo.c150
-rw-r--r--source/rpc_server/srv_echo_nt.c78
-rw-r--r--source/rpc_server/srv_epmapper.c88
-rw-r--r--source/rpc_server/srv_epmapper_nt.c70
-rw-r--r--source/rpc_server/srv_lsa.c734
-rw-r--r--source/rpc_server/srv_lsa_ds.c93
-rw-r--r--source/rpc_server/srv_lsa_ds_nt.c127
-rw-r--r--source/rpc_server/srv_lsa_hnd.c265
-rw-r--r--source/rpc_server/srv_lsa_nt.c1342
-rw-r--r--source/rpc_server/srv_netlog.c385
-rw-r--r--source/rpc_server/srv_netlog_nt.c795
-rw-r--r--source/rpc_server/srv_pipe.c1625
-rw-r--r--source/rpc_server/srv_pipe_hnd.c1169
-rw-r--r--source/rpc_server/srv_reg.c402
-rw-r--r--source/rpc_server/srv_reg_nt.c664
-rw-r--r--source/rpc_server/srv_samr.c1508
-rw-r--r--source/rpc_server/srv_samr_nt.c4476
-rw-r--r--source/rpc_server/srv_samr_util.c554
-rwxr-xr-xsource/rpc_server/srv_spoolss.c1652
-rw-r--r--source/rpc_server/srv_spoolss_nt.c9188
-rw-r--r--source/rpc_server/srv_srvsvc.c561
-rw-r--r--source/rpc_server/srv_srvsvc_nt.c2195
-rw-r--r--source/rpc_server/srv_util.c580
-rw-r--r--source/rpc_server/srv_wkssvc.c78
-rw-r--r--source/rpc_server/srv_wkssvc_nt.c79
-rw-r--r--source/rpcclient/cmd_dfs.c237
-rw-r--r--source/rpcclient/cmd_ds.c79
-rw-r--r--source/rpcclient/cmd_echo.c159
-rw-r--r--source/rpcclient/cmd_epmapper.c76
-rw-r--r--source/rpcclient/cmd_lsarpc.c762
-rw-r--r--source/rpcclient/cmd_netlogon.c345
-rw-r--r--source/rpcclient/cmd_reg.c1007
-rw-r--r--source/rpcclient/cmd_samr.c1562
-rw-r--r--source/rpcclient/cmd_shutdown.c112
-rw-r--r--source/rpcclient/cmd_spoolss.c2338
-rw-r--r--source/rpcclient/cmd_srvsvc.c361
-rw-r--r--source/rpcclient/cmd_wkssvc.c84
-rw-r--r--source/rpcclient/display_sec.c144
-rw-r--r--source/rpcclient/rpcclient.c803
-rw-r--r--source/rpcclient/rpcclient.h42
-rw-r--r--source/sam/account.c305
-rw-r--r--source/sam/group.c193
-rw-r--r--source/sam/gums.c173
-rw-r--r--source/sam/gums_api.c1426
-rw-r--r--source/sam/gums_helper.c383
-rw-r--r--source/sam/gums_tdbsam2.c1220
-rw-r--r--source/sam/idmap.c317
-rw-r--r--source/sam/idmap_ldap.c790
-rw-r--r--source/sam/idmap_tdb.c676
-rw-r--r--source/sam/idmap_util.c295
-rw-r--r--source/sam/interface.c1338
-rw-r--r--source/script/.cvsignore2
-rwxr-xr-xsource/script/build_env.sh41
-rwxr-xr-xsource/script/creategroup27
-rwxr-xr-xsource/script/extract_allparms.sh2
-rwxr-xr-xsource/script/findsmb.in161
-rwxr-xr-xsource/script/findstatic.pl70
-rw-r--r--source/script/gap.awk39
-rw-r--r--source/script/gaptab.awk48
-rw-r--r--source/script/gen-8bit-gap.awk18
-rwxr-xr-xsource/script/gen-8bit-gap.sh.in49
-rwxr-xr-xsource/script/genstruct.pl299
-rwxr-xr-xsource/script/installbin.sh45
-rwxr-xr-xsource/script/installdat.sh23
-rwxr-xr-xsource/script/installdirs.sh19
-rwxr-xr-xsource/script/installman.sh71
-rwxr-xr-xsource/script/installmodules.sh27
-rw-r--r--source/script/installmsg.sh23
-rwxr-xr-xsource/script/installscripts.sh47
-rwxr-xr-xsource/script/installswat.sh153
-rwxr-xr-xsource/script/linkmodules.sh12
-rw-r--r--source/script/makeunicodecasemap.awk59
-rw-r--r--source/script/mkbuildoptions.awk262
-rwxr-xr-xsource/script/mkinstalldirs38
-rwxr-xr-xsource/script/mknissmbpasswd.sh31
-rwxr-xr-xsource/script/mknissmbpwdtbl.sh42
-rw-r--r--source/script/mkproto.awk155
-rwxr-xr-xsource/script/mkproto.sh43
-rwxr-xr-xsource/script/mksmbpasswd.sh6
-rwxr-xr-xsource/script/mkversion.sh87
-rwxr-xr-xsource/script/revert.sh18
-rwxr-xr-xsource/script/scancvslog.pl112
-rw-r--r--source/script/smbtar165
-rwxr-xr-xsource/script/uninstallbin.sh42
-rwxr-xr-xsource/script/uninstallman.sh37
-rwxr-xr-xsource/script/uninstallmodules.sh37
-rwxr-xr-xsource/script/uninstallscripts.sh36
-rwxr-xr-xsource/script/updatesmbpasswd.sh14
-rw-r--r--source/smbadduser.in79
-rw-r--r--source/smbd/.cvsignore3
-rw-r--r--source/smbd/blocking.c726
-rw-r--r--source/smbd/change_trust_pw.c99
-rw-r--r--source/smbd/chgpasswd.c1054
-rw-r--r--source/smbd/close.c311
-rw-r--r--source/smbd/conn.c305
-rw-r--r--source/smbd/connection.c243
-rw-r--r--source/smbd/dfree.c164
-rw-r--r--source/smbd/dir.c1096
-rw-r--r--source/smbd/dosmode.c483
-rw-r--r--source/smbd/error.c137
-rw-r--r--source/smbd/fake_file.c166
-rw-r--r--source/smbd/fileio.c773
-rw-r--r--source/smbd/filename.c497
-rw-r--r--source/smbd/files.c449
-rw-r--r--source/smbd/ipc.c599
-rw-r--r--source/smbd/lanman.c3619
-rw-r--r--source/smbd/mangle.c124
-rw-r--r--source/smbd/mangle_hash.c783
-rw-r--r--source/smbd/mangle_hash2.c709
-rw-r--r--source/smbd/mangle_map.c212
-rw-r--r--source/smbd/message.c237
-rw-r--r--source/smbd/negprot.c550
-rw-r--r--source/smbd/noquotas.c38
-rw-r--r--source/smbd/notify.c225
-rw-r--r--source/smbd/notify_hash.c225
-rw-r--r--source/smbd/notify_kernel.c245
-rw-r--r--source/smbd/ntquotas.c262
-rw-r--r--source/smbd/nttrans.c2780
-rw-r--r--source/smbd/open.c1462
-rw-r--r--source/smbd/oplock.c1260
-rw-r--r--source/smbd/oplock_irix.c285
-rw-r--r--source/smbd/oplock_linux.c309
-rw-r--r--source/smbd/password.c551
-rw-r--r--source/smbd/pipes.c264
-rw-r--r--source/smbd/posix_acls.c3382
-rw-r--r--source/smbd/process.c1367
-rw-r--r--source/smbd/quotas.c1279
-rw-r--r--source/smbd/reply.c4974
-rw-r--r--source/smbd/sec_ctx.c449
-rw-r--r--source/smbd/server.c909
-rw-r--r--source/smbd/service.c838
-rw-r--r--source/smbd/session.c250
-rw-r--r--source/smbd/sesssetup.c938
-rw-r--r--source/smbd/srvstr.c44
-rw-r--r--source/smbd/statcache.c339
-rw-r--r--source/smbd/tdbutil.c85
-rw-r--r--source/smbd/trans2.c4165
-rw-r--r--source/smbd/uid.c430
-rw-r--r--source/smbd/utmp.c595
-rw-r--r--source/smbd/vfs-wrap.c1031
-rw-r--r--source/smbd/vfs.c951
-rw-r--r--source/smbwrapper/.cvsignore8
-rw-r--r--source/smbwrapper/PORTING77
-rw-r--r--source/smbwrapper/README94
-rw-r--r--source/smbwrapper/realcalls.c48
-rw-r--r--source/smbwrapper/realcalls.h263
-rw-r--r--source/smbwrapper/shared.c203
-rw-r--r--source/smbwrapper/smbsh.c127
-rw-r--r--source/smbwrapper/smbsh.in54
-rw-r--r--source/smbwrapper/smbw.c1562
-rw-r--r--source/smbwrapper/smbw.h71
-rw-r--r--source/smbwrapper/smbw_cache.c207
-rw-r--r--source/smbwrapper/smbw_dir.c689
-rw-r--r--source/smbwrapper/smbw_stat.c254
-rw-r--r--source/smbwrapper/wrapped.c705
-rw-r--r--source/stf/.cvsignore2
-rw-r--r--source/stf/README.stf3
-rw-r--r--source/stf/comfychair.py445
-rwxr-xr-xsource/stf/example.py38
-rwxr-xr-xsource/stf/info3cache.py54
-rw-r--r--source/stf/notes.txt175
-rwxr-xr-xsource/stf/osver.py55
-rwxr-xr-xsource/stf/pythoncheck.py48
-rw-r--r--source/stf/sambalib.py41
-rwxr-xr-xsource/stf/smbcontrol.py238
-rwxr-xr-xsource/stf/spoolss.py288
-rw-r--r--source/stf/standardcheck.py34
-rwxr-xr-xsource/stf/stf.py101
-rwxr-xr-xsource/stf/strings.py151
-rwxr-xr-xsource/stf/test.py33
-rw-r--r--source/stf/unicodenames.py33
-rw-r--r--source/tdb/.cvsignore11
-rw-r--r--source/tdb/Makefile29
-rw-r--r--source/tdb/README167
-rw-r--r--source/tdb/spinlock.c481
-rw-r--r--source/tdb/spinlock.h59
-rw-r--r--source/tdb/tdb.c2090
-rw-r--r--source/tdb/tdb.h151
-rw-r--r--source/tdb/tdb.magic10
-rw-r--r--source/tdb/tdbback.c211
-rw-r--r--source/tdb/tdbback.h23
-rw-r--r--source/tdb/tdbbackup.c149
-rw-r--r--source/tdb/tdbdump.c88
-rw-r--r--source/tdb/tdbtest.c263
-rw-r--r--source/tdb/tdbtool.c547
-rw-r--r--source/tdb/tdbtorture.c227
-rw-r--r--source/tdb/tdbutil.c829
-rw-r--r--source/tdb/tdbutil.h38
-rw-r--r--source/tests/.cvsignore1
-rw-r--r--source/tests/README10
-rw-r--r--source/tests/crack.c12
-rw-r--r--source/tests/crypttest.c852
-rw-r--r--source/tests/fcntl_lock.c121
-rw-r--r--source/tests/fcntl_lock64.c96
-rw-r--r--source/tests/ftruncate.c27
-rw-r--r--source/tests/getgroups.c66
-rw-r--r--source/tests/shared_mmap.c68
-rw-r--r--source/tests/shlib.c6
-rw-r--r--source/tests/summary.c30
-rw-r--r--source/tests/sysquotas.c94
-rw-r--r--source/tests/trivial.c4
-rw-r--r--source/tests/unixsock.c93
-rw-r--r--source/torture/cmd_vfs.c1060
-rw-r--r--source/torture/denytest.c1577
-rw-r--r--source/torture/locktest.c678
-rw-r--r--source/torture/locktest2.c560
-rw-r--r--source/torture/mangle_test.c213
-rw-r--r--source/torture/masktest.c538
-rw-r--r--source/torture/msgtest.c91
-rw-r--r--source/torture/nbio.c318
-rw-r--r--source/torture/nsstest.c451
-rw-r--r--source/torture/rpctorture.c541
-rw-r--r--source/torture/samtest.h38
-rw-r--r--source/torture/scanner.c430
-rw-r--r--source/torture/smbiconv.c243
-rw-r--r--source/torture/t_doschar.c42
-rw-r--r--source/torture/t_push_ucs2.c54
-rw-r--r--source/torture/t_strcmp.c32
-rw-r--r--source/torture/t_stringoverflow.c23
-rw-r--r--source/torture/t_strstr.c35
-rw-r--r--source/torture/torture.c4994
-rw-r--r--source/torture/utable.c193
-rw-r--r--source/torture/vfstest.c571
-rw-r--r--source/torture/vfstest.h45
-rw-r--r--source/ubiqx/.cvsignore3
-rw-r--r--source/ubiqx/COPYING.LIB481
-rw-r--r--source/ubiqx/README.UBI18
-rw-r--r--source/ubiqx/debugparse.c308
-rw-r--r--source/ubiqx/debugparse.h127
-rw-r--r--source/ubiqx/sys_include.h52
-rw-r--r--source/ubiqx/ubi_BinTree.c1132
-rw-r--r--source/ubiqx/ubi_BinTree.h864
-rw-r--r--source/ubiqx/ubi_Cache.c505
-rw-r--r--source/ubiqx/ubi_Cache.h412
-rw-r--r--source/ubiqx/ubi_SplayTree.c512
-rw-r--r--source/ubiqx/ubi_SplayTree.h377
-rw-r--r--source/ubiqx/ubi_dLinkList.c171
-rw-r--r--source/ubiqx/ubi_dLinkList.h242
-rw-r--r--source/ubiqx/ubi_sLinkList.c187
-rw-r--r--source/ubiqx/ubi_sLinkList.h254
-rw-r--r--source/utils/.cvsignore1
-rw-r--r--source/utils/debug2html.c253
-rw-r--r--source/utils/editreg.c4145
-rw-r--r--source/utils/log2pcaphex.c294
-rw-r--r--source/utils/net.c796
-rw-r--r--source/utils/net.h69
-rw-r--r--source/utils/net_ads.c1328
-rw-r--r--source/utils/net_ads_cldap.c364
-rw-r--r--source/utils/net_cache.c348
-rw-r--r--source/utils/net_groupmap.c767
-rw-r--r--source/utils/net_help.c220
-rw-r--r--source/utils/net_idmap.c264
-rw-r--r--source/utils/net_lookup.c258
-rw-r--r--source/utils/net_privileges.c362
-rw-r--r--source/utils/net_rap.c1054
-rw-r--r--source/utils/net_rpc.c3396
-rw-r--r--source/utils/net_rpc_join.c389
-rw-r--r--source/utils/net_rpc_samsync.c1080
-rw-r--r--source/utils/net_status.c257
-rw-r--r--source/utils/net_time.c180
-rw-r--r--source/utils/nmblookup.c292
-rw-r--r--source/utils/ntlm_auth.c2182
-rw-r--r--source/utils/pdbedit.c1179
-rw-r--r--source/utils/profiles.c742
-rw-r--r--source/utils/rpccheck.c62
-rw-r--r--source/utils/smbcacls.c877
-rw-r--r--source/utils/smbcontrol.c747
-rw-r--r--source/utils/smbcquotas.c546
-rw-r--r--source/utils/smbfilter.c245
-rw-r--r--source/utils/smbget.c589
-rw-r--r--source/utils/smbpasswd.c614
-rw-r--r--source/utils/smbtree.c237
-rw-r--r--source/utils/smbw_sample.c94
-rw-r--r--source/utils/status.c693
-rw-r--r--source/utils/testparm.c367
-rw-r--r--source/utils/testprns.c61
-rw-r--r--source/web/.cvsignore1
-rw-r--r--source/web/cgi.c626
-rw-r--r--source/web/diagnose.c79
-rw-r--r--source/web/neg_lang.c117
-rw-r--r--source/web/startstop.c131
-rw-r--r--source/web/statuspage.c441
-rw-r--r--source/web/swat.c1389
-rw-r--r--source/wrepld/parser.c759
-rw-r--r--source/wrepld/partners.c200
-rw-r--r--source/wrepld/process.c983
-rw-r--r--source/wrepld/server.c666
-rw-r--r--source/wrepld/socket.c69
-rw-r--r--source/wrepld/wins_repl.h161
-rw-r--r--swat/help/welcome.html67
-rw-r--r--swat/images/globals.gifbin0 -> 2004 bytes
-rw-r--r--swat/images/home.gifbin0 -> 2044 bytes
-rw-r--r--swat/images/passwd.gifbin0 -> 1993 bytes
-rw-r--r--swat/images/printers.gifbin0 -> 2077 bytes
-rw-r--r--swat/images/samba.gifbin0 -> 3643 bytes
-rw-r--r--swat/images/shares.gifbin0 -> 2006 bytes
-rw-r--r--swat/images/status.gifbin0 -> 2080 bytes
-rw-r--r--swat/images/viewconfig.gifbin0 -> 1939 bytes
-rw-r--r--swat/images/wizard.gifbin0 -> 2554 bytes
-rw-r--r--swat/include/footer.html3
-rw-r--r--swat/include/header.html10
-rw-r--r--swat/lang/ja/help/welcome.html69
-rw-r--r--swat/lang/ja/include/footer.html3
-rw-r--r--swat/lang/ja/include/header.html12
-rw-r--r--swat/lang/ja/include/header.nocss.html11
-rw-r--r--swat/lang/ja/include/header_css.html1
-rw-r--r--swat/lang/tr/help/welcome.html69
-rw-r--r--swat/lang/tr/images/globals.gifbin0 -> 442 bytes
-rw-r--r--swat/lang/tr/images/home.gifbin0 -> 608 bytes
-rw-r--r--swat/lang/tr/images/passwd.gifbin0 -> 326 bytes
-rw-r--r--swat/lang/tr/images/printers.gifbin0 -> 519 bytes
-rw-r--r--swat/lang/tr/images/samba.gifbin0 -> 3643 bytes
-rw-r--r--swat/lang/tr/images/shares.gifbin0 -> 474 bytes
-rw-r--r--swat/lang/tr/images/status.gifbin0 -> 431 bytes
-rw-r--r--swat/lang/tr/images/viewconfig.gifbin0 -> 455 bytes
-rw-r--r--swat/lang/tr/include/header.html10
-rw-r--r--testsuite/README19
-rwxr-xr-xtestsuite/build_farm/backtrace15
-rw-r--r--testsuite/build_farm/basicsmb-domainsec-nt4.test28
-rw-r--r--testsuite/build_farm/basicsmb-domainsec.test27
-rw-r--r--testsuite/build_farm/basicsmb-hostsdeny.test18
-rw-r--r--testsuite/build_farm/basicsmb-hostsequiv.test26
-rw-r--r--testsuite/build_farm/basicsmb-invalidusers.test10
-rw-r--r--testsuite/build_farm/basicsmb-local-pass-change.test10
-rw-r--r--testsuite/build_farm/basicsmb-preexec.test28
-rw-r--r--testsuite/build_farm/basicsmb-remote-pass-change.test10
-rw-r--r--testsuite/build_farm/basicsmb-serversec.test9
-rw-r--r--testsuite/build_farm/basicsmb-shareguest.test20
-rw-r--r--testsuite/build_farm/basicsmb-sharelist.test22
-rw-r--r--testsuite/build_farm/basicsmb-sharesec.test9
-rw-r--r--testsuite/build_farm/basicsmb-usersec.test9
-rw-r--r--testsuite/build_farm/basicsmb.fns204
-rw-r--r--testsuite/build_farm/runlist18
-rw-r--r--testsuite/build_farm/template/basicsmb.smb.conf49
-rw-r--r--testsuite/build_farm/template/basicsmb.smb.conf.domain2
-rw-r--r--testsuite/build_farm/template/basicsmb.smb.conf.hostsdeny1
-rw-r--r--testsuite/build_farm/template/basicsmb.smb.conf.hostsequiv3
-rw-r--r--testsuite/build_farm/template/basicsmb.smb.conf.invalidusers1
-rw-r--r--testsuite/build_farm/template/basicsmb.smb.conf.preexec1
-rw-r--r--testsuite/build_farm/template/basicsmb.smb.conf.preexec_cl_fl2
-rw-r--r--testsuite/build_farm/template/basicsmb.smb.conf.preexec_close2
-rw-r--r--testsuite/build_farm/template/basicsmb.smb.conf.server3
-rw-r--r--testsuite/build_farm/template/basicsmb.smb.conf.share1
-rw-r--r--testsuite/build_farm/template/basicsmb.smb.conf.user1
-rw-r--r--testsuite/build_farm/template/basicsmb.smb.conf.validusers1
-rw-r--r--testsuite/build_farm/template/preexec3
-rw-r--r--testsuite/build_farm/torture-ATTR.test2
-rw-r--r--testsuite/build_farm/torture-BROWSE.test2
-rw-r--r--testsuite/build_farm/torture-DELETE.test2
-rw-r--r--testsuite/build_farm/torture-DENY1.test2
-rw-r--r--testsuite/build_farm/torture-DENY2.test2
-rw-r--r--testsuite/build_farm/torture-DIR.test2
-rw-r--r--testsuite/build_farm/torture-DIR1.test2
-rw-r--r--testsuite/build_farm/torture-FDPASS.test2
-rw-r--r--testsuite/build_farm/torture-FDSESS.test2
-rw-r--r--testsuite/build_farm/torture-LOCK1.test2
-rw-r--r--testsuite/build_farm/torture-LOCK2.test2
-rw-r--r--testsuite/build_farm/torture-LOCK3.test2
-rw-r--r--testsuite/build_farm/torture-LOCK4.test2
-rw-r--r--testsuite/build_farm/torture-LOCK5.test2
-rw-r--r--testsuite/build_farm/torture-LOCK6.test2
-rw-r--r--testsuite/build_farm/torture-LOCK7.test2
-rw-r--r--testsuite/build_farm/torture-MANGLE.test2
-rw-r--r--testsuite/build_farm/torture-OPEN.test2
-rw-r--r--testsuite/build_farm/torture-OPLOCK1.test2
-rw-r--r--testsuite/build_farm/torture-OPLOCK3.test2
-rw-r--r--testsuite/build_farm/torture-PROPERTIES.test2
-rw-r--r--testsuite/build_farm/torture-RANDOMIPC.test2
-rw-r--r--testsuite/build_farm/torture-RENAME.test2
-rw-r--r--testsuite/build_farm/torture-RW1.test2
-rw-r--r--testsuite/build_farm/torture-RW2.test2
-rw-r--r--testsuite/build_farm/torture-TCON.test2
-rw-r--r--testsuite/build_farm/torture-TCON1.test2
-rw-r--r--testsuite/build_farm/torture-TCON2.test2
-rw-r--r--testsuite/build_farm/torture-TCONDEV.test2
-rw-r--r--testsuite/build_farm/torture-TORTURE.test2
-rw-r--r--testsuite/build_farm/torture-TRANS2.test2
-rw-r--r--testsuite/build_farm/torture-UNLINK.test2
-rw-r--r--testsuite/build_farm/torture-XCOPY.test2
-rw-r--r--testsuite/build_farm/torture_setup.fns19
-rw-r--r--testsuite/config/unix.exp27
-rw-r--r--testsuite/lib/compile.exp79
-rw-r--r--testsuite/lib/default-nt-names.exp20
-rw-r--r--testsuite/lib/env-single.exp36
-rw-r--r--testsuite/lib/nsswitch-config.exp21
-rw-r--r--testsuite/lib/smbclient.exp54
-rw-r--r--testsuite/libsmbclient/src/.cvsignore1
-rw-r--r--testsuite/libsmbclient/src/Makefile817
-rw-r--r--testsuite/libsmbclient/src/chmod/chmod_1.c59
-rw-r--r--testsuite/libsmbclient/src/chown/chown_1.c59
-rw-r--r--testsuite/libsmbclient/src/close/close_1.c59
-rw-r--r--testsuite/libsmbclient/src/close/close_2.c57
-rw-r--r--testsuite/libsmbclient/src/closedir/closedir_1.c65
-rw-r--r--testsuite/libsmbclient/src/closedir/closedir_2.c61
-rw-r--r--testsuite/libsmbclient/src/closedir/closedir_3.c63
-rw-r--r--testsuite/libsmbclient/src/closedir/closedir_4.c59
-rw-r--r--testsuite/libsmbclient/src/creat/creat_1.c60
-rw-r--r--testsuite/libsmbclient/src/creat/creat_2.c63
-rw-r--r--testsuite/libsmbclient/src/creat/creat_3.c56
-rw-r--r--testsuite/libsmbclient/src/fstat/fstat_1.c62
-rw-r--r--testsuite/libsmbclient/src/fstat/fstat_2.c58
-rw-r--r--testsuite/libsmbclient/src/fstat/fstat_3.c69
-rw-r--r--testsuite/libsmbclient/src/fstat/fstat_4.c69
-rw-r--r--testsuite/libsmbclient/src/fstat/fstat_5.c77
-rw-r--r--testsuite/libsmbclient/src/fstat/fstat_6.c80
-rw-r--r--testsuite/libsmbclient/src/getdents/getdents_1.c72
-rw-r--r--testsuite/libsmbclient/src/getdents/getdents_2.c67
-rw-r--r--testsuite/libsmbclient/src/getdents/getdents_3.c155
-rw-r--r--testsuite/libsmbclient/src/getdents/getdents_4.c101
-rw-r--r--testsuite/libsmbclient/src/getdents/getdents_5.c106
-rw-r--r--testsuite/libsmbclient/src/init/init_1.c18
-rw-r--r--testsuite/libsmbclient/src/init/init_2.c23
-rw-r--r--testsuite/libsmbclient/src/init/init_3.c58
-rw-r--r--testsuite/libsmbclient/src/init/init_4.c18
-rw-r--r--testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_1.c109
-rw-r--r--testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_2.c105
-rw-r--r--testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_3.c103
-rw-r--r--testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_4.c99
-rw-r--r--testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_5.c101
-rw-r--r--testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_6.c110
-rw-r--r--testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_7.c106
-rw-r--r--testsuite/libsmbclient/src/lseek/lseek_1.c61
-rw-r--r--testsuite/libsmbclient/src/lseek/lseek_2.c57
-rw-r--r--testsuite/libsmbclient/src/lseek/lseek_3.c80
-rw-r--r--testsuite/libsmbclient/src/lseek/lseek_4.c80
-rw-r--r--testsuite/libsmbclient/src/lseek/lseek_5.c80
-rw-r--r--testsuite/libsmbclient/src/lseek/lseek_6.c75
-rw-r--r--testsuite/libsmbclient/src/lseek/lseek_7.c80
-rw-r--r--testsuite/libsmbclient/src/lseek/lseek_8.c75
-rw-r--r--testsuite/libsmbclient/src/lseekdir/lseekdir_1.c102
-rw-r--r--testsuite/libsmbclient/src/lseekdir/lseekdir_2.c95
-rw-r--r--testsuite/libsmbclient/src/lseekdir/lseekdir_3.c67
-rw-r--r--testsuite/libsmbclient/src/lseekdir/lseekdir_4.c61
-rw-r--r--testsuite/libsmbclient/src/lseekdir/lseekdir_5.c119
-rw-r--r--testsuite/libsmbclient/src/lseekdir/lseekdir_6.c124
-rw-r--r--testsuite/libsmbclient/src/mkdir/mkdir_1.c60
-rw-r--r--testsuite/libsmbclient/src/mkdir/mkdir_2.c56
-rw-r--r--testsuite/libsmbclient/src/mkdir/mkdir_3.c58
-rw-r--r--testsuite/libsmbclient/src/mkdir/mkdir_4.c62
-rw-r--r--testsuite/libsmbclient/src/open/open_1.c60
-rw-r--r--testsuite/libsmbclient/src/open/open_2.c56
-rw-r--r--testsuite/libsmbclient/src/open/open_3.c60
-rw-r--r--testsuite/libsmbclient/src/open/open_4.c63
-rw-r--r--testsuite/libsmbclient/src/open/open_5.c58
-rw-r--r--testsuite/libsmbclient/src/open_print_job/open_print_job_1.c60
-rw-r--r--testsuite/libsmbclient/src/open_print_job/open_print_job_2.c56
-rw-r--r--testsuite/libsmbclient/src/opendir/opendir_1.c62
-rw-r--r--testsuite/libsmbclient/src/opendir/opendir_2.c55
-rw-r--r--testsuite/libsmbclient/src/opendir/opendir_3.c65
-rw-r--r--testsuite/libsmbclient/src/opendir/opendir_4.c61
-rw-r--r--testsuite/libsmbclient/src/print_file/print_file_1.c76
-rw-r--r--testsuite/libsmbclient/src/print_file/print_file_2.c72
-rw-r--r--testsuite/libsmbclient/src/print_file/print_file_3.c59
-rw-r--r--testsuite/libsmbclient/src/print_file/print_file_4.c55
-rw-r--r--testsuite/libsmbclient/src/read/read_1.c83
-rw-r--r--testsuite/libsmbclient/src/read/read_10.c68
-rw-r--r--testsuite/libsmbclient/src/read/read_11.c83
-rw-r--r--testsuite/libsmbclient/src/read/read_12.c87
-rw-r--r--testsuite/libsmbclient/src/read/read_13.c91
-rw-r--r--testsuite/libsmbclient/src/read/read_2.c76
-rw-r--r--testsuite/libsmbclient/src/read/read_3.c83
-rw-r--r--testsuite/libsmbclient/src/read/read_4.c77
-rw-r--r--testsuite/libsmbclient/src/read/read_5.c83
-rw-r--r--testsuite/libsmbclient/src/read/read_6.c77
-rw-r--r--testsuite/libsmbclient/src/read/read_7.c60
-rw-r--r--testsuite/libsmbclient/src/read/read_8.c56
-rw-r--r--testsuite/libsmbclient/src/read/read_9.c70
-rw-r--r--testsuite/libsmbclient/src/readdir/readdir_1.c107
-rw-r--r--testsuite/libsmbclient/src/readdir/readdir_2.c102
-rw-r--r--testsuite/libsmbclient/src/readdir/readdir_3.c71
-rw-r--r--testsuite/libsmbclient/src/readdir/readdir_4.c67
-rw-r--r--testsuite/libsmbclient/src/readdir/readdir_5.c155
-rw-r--r--testsuite/libsmbclient/src/rename/rename_1.c57
-rw-r--r--testsuite/libsmbclient/src/rename/rename_10.c64
-rw-r--r--testsuite/libsmbclient/src/rename/rename_11.c63
-rw-r--r--testsuite/libsmbclient/src/rename/rename_12.c58
-rw-r--r--testsuite/libsmbclient/src/rename/rename_13.c63
-rw-r--r--testsuite/libsmbclient/src/rename/rename_14.c58
-rw-r--r--testsuite/libsmbclient/src/rename/rename_2.c54
-rw-r--r--testsuite/libsmbclient/src/rename/rename_3.c56
-rw-r--r--testsuite/libsmbclient/src/rename/rename_4.c55
-rw-r--r--testsuite/libsmbclient/src/rename/rename_5.c59
-rw-r--r--testsuite/libsmbclient/src/rename/rename_6.c57
-rw-r--r--testsuite/libsmbclient/src/rename/rename_7.c66
-rw-r--r--testsuite/libsmbclient/src/rename/rename_8.c68
-rw-r--r--testsuite/libsmbclient/src/rename/rename_9.c68
-rw-r--r--testsuite/libsmbclient/src/rmdir/rmdir_1.c59
-rw-r--r--testsuite/libsmbclient/src/rmdir/rmdir_2.c55
-rw-r--r--testsuite/libsmbclient/src/rmdir/rmdir_3.c61
-rw-r--r--testsuite/libsmbclient/src/rmdir/rmdir_4.c57
-rw-r--r--testsuite/libsmbclient/src/rmdir/rmdir_5.c63
-rw-r--r--testsuite/libsmbclient/src/rmdir/rmdir_6.c59
-rw-r--r--testsuite/libsmbclient/src/stat/stat_1.c62
-rw-r--r--testsuite/libsmbclient/src/stat/stat_2.c58
-rw-r--r--testsuite/libsmbclient/src/stat/stat_3.c66
-rw-r--r--testsuite/libsmbclient/src/stat/stat_4.c62
-rw-r--r--testsuite/libsmbclient/src/stat/stat_5.c77
-rw-r--r--testsuite/libsmbclient/src/stat/stat_6.c80
-rw-r--r--testsuite/libsmbclient/src/telldir/telldir_1.c102
-rw-r--r--testsuite/libsmbclient/src/telldir/telldir_2.c95
-rw-r--r--testsuite/libsmbclient/src/telldir/telldir_3.c67
-rw-r--r--testsuite/libsmbclient/src/telldir/telldir_4.c61
-rw-r--r--testsuite/libsmbclient/src/telldir/telldir_5.c122
-rw-r--r--testsuite/libsmbclient/src/unlink/unlink_1.c61
-rw-r--r--testsuite/libsmbclient/src/unlink/unlink_10.c62
-rw-r--r--testsuite/libsmbclient/src/unlink/unlink_11.c66
-rw-r--r--testsuite/libsmbclient/src/unlink/unlink_12.c65
-rw-r--r--testsuite/libsmbclient/src/unlink/unlink_2.c61
-rw-r--r--testsuite/libsmbclient/src/unlink/unlink_3.c57
-rw-r--r--testsuite/libsmbclient/src/unlink/unlink_4.c64
-rw-r--r--testsuite/libsmbclient/src/unlink/unlink_5.c62
-rw-r--r--testsuite/libsmbclient/src/unlink/unlink_6.c58
-rw-r--r--testsuite/libsmbclient/src/unlink/unlink_7.c62
-rw-r--r--testsuite/libsmbclient/src/unlink/unlink_8.c55
-rw-r--r--testsuite/libsmbclient/src/unlink/unlink_9.c57
-rw-r--r--testsuite/libsmbclient/src/unlink_print_job/unlink_print_job_1.c107
-rw-r--r--testsuite/libsmbclient/src/unlink_print_job/unlink_print_job_2.c102
-rw-r--r--testsuite/libsmbclient/src/unlink_print_job/unlink_print_job_3.c106
-rw-r--r--testsuite/libsmbclient/src/unlink_print_job/unlink_print_job_4.c101
-rw-r--r--testsuite/libsmbclient/src/unlink_print_job/unlink_print_job_5.c141
-rw-r--r--testsuite/libsmbclient/src/write/write_1.c77
-rw-r--r--testsuite/libsmbclient/src/write/write_10.c70
-rw-r--r--testsuite/libsmbclient/src/write/write_11.c83
-rw-r--r--testsuite/libsmbclient/src/write/write_12.c83
-rw-r--r--testsuite/libsmbclient/src/write/write_13.c87
-rw-r--r--testsuite/libsmbclient/src/write/write_2.c71
-rw-r--r--testsuite/libsmbclient/src/write/write_3.c79
-rw-r--r--testsuite/libsmbclient/src/write/write_4.c74
-rw-r--r--testsuite/libsmbclient/src/write/write_5.c79
-rw-r--r--testsuite/libsmbclient/src/write/write_6.c74
-rw-r--r--testsuite/libsmbclient/src/write/write_7.c60
-rw-r--r--testsuite/libsmbclient/src/write/write_8.c56
-rw-r--r--testsuite/libsmbclient/src/write/write_9.c72
-rw-r--r--testsuite/nsswitch/.cvsignore12
-rw-r--r--testsuite/nsswitch/Makefile.longarg5
-rw-r--r--testsuite/nsswitch/bigfd.c38
-rw-r--r--testsuite/nsswitch/bigfd.exp28
-rw-r--r--testsuite/nsswitch/domusers.exp38
-rw-r--r--testsuite/nsswitch/envvar.exp282
-rw-r--r--testsuite/nsswitch/finger.exp39
-rw-r--r--testsuite/nsswitch/getent.c151
-rw-r--r--testsuite/nsswitch/getent.exp148
-rw-r--r--testsuite/nsswitch/getent_grent.c101
-rw-r--r--testsuite/nsswitch/getent_pwent.c113
-rwxr-xr-xtestsuite/nsswitch/getent_r.sh35
-rw-r--r--testsuite/nsswitch/getgrent_r.c84
-rw-r--r--testsuite/nsswitch/getgrent_r.exp41
-rw-r--r--testsuite/nsswitch/getgrgid.c57
-rw-r--r--testsuite/nsswitch/getgrgid.exp50
-rw-r--r--testsuite/nsswitch/getgrnam.c51
-rw-r--r--testsuite/nsswitch/getgrnam.exp28
-rw-r--r--testsuite/nsswitch/getpwent_r.c85
-rw-r--r--testsuite/nsswitch/getpwent_r.exp41
-rw-r--r--testsuite/nsswitch/getpwnam.c37
-rw-r--r--testsuite/nsswitch/getpwnam.exp28
-rw-r--r--testsuite/nsswitch/getpwuid.c43
-rw-r--r--testsuite/nsswitch/getpwuid.exp59
-rw-r--r--testsuite/nsswitch/groupmem_dom.exp33
-rw-r--r--testsuite/nsswitch/initgroups.c42
-rw-r--r--testsuite/nsswitch/initgroups.exp37
-rw-r--r--testsuite/nsswitch/login.exp102
-rw-r--r--testsuite/nsswitch/longarg.exp29
-rw-r--r--testsuite/nsswitch/longarg_getgrnam.c42
-rw-r--r--testsuite/nsswitch/longarg_getpwnam.c42
-rw-r--r--testsuite/nsswitch/longarg_utils.h27
-rw-r--r--testsuite/nsswitch/nss_winbind_syms.c63
-rw-r--r--testsuite/nsswitch/nss_winbind_syms.exp42
-rw-r--r--testsuite/nsswitch/pam_winbind_syms.c55
-rw-r--r--testsuite/nsswitch/pam_winbind_syms.exp44
-rw-r--r--testsuite/nsswitch/wbinfo.exp360
-rw-r--r--testsuite/printing/.cvsignore2
-rw-r--r--testsuite/printing/Makefile.psec22
-rw-r--r--testsuite/printing/Makefile.vlp14
-rw-r--r--testsuite/printing/README.vlp35
-rw-r--r--testsuite/printing/psec.c436
-rw-r--r--testsuite/printing/vlp.c426
-rw-r--r--testsuite/server/ipc.exp44
-rw-r--r--testsuite/server/masktest.exp57
-rw-r--r--testsuite/server/rename.exp59
-rw-r--r--testsuite/server/xfer.exp48
-rw-r--r--testsuite/smbd/Makefile.se_access_check24
-rw-r--r--testsuite/smbd/Makefile.sec_ctx57
-rw-r--r--testsuite/smbd/se_access_check.exp54
-rw-r--r--testsuite/smbd/se_access_check_allowall.c87
-rw-r--r--testsuite/smbd/se_access_check_allowsome.c104
-rw-r--r--testsuite/smbd/se_access_check_denyall.c86
-rw-r--r--testsuite/smbd/se_access_check_denysome.c106
-rw-r--r--testsuite/smbd/se_access_check_empty.c109
-rw-r--r--testsuite/smbd/se_access_check_nullsd.c74
-rw-r--r--testsuite/smbd/se_access_check_printer.c212
-rw-r--r--testsuite/smbd/se_access_check_utils.c158
-rw-r--r--testsuite/smbd/se_access_check_utils.h46
-rw-r--r--testsuite/smbd/sec_ctx.exp67
-rw-r--r--testsuite/smbd/sec_ctx1.c40
-rw-r--r--testsuite/smbd/sec_ctx_current_user.c114
-rw-r--r--testsuite/smbd/sec_ctx_flow.c73
-rw-r--r--testsuite/smbd/sec_ctx_groups.c131
-rw-r--r--testsuite/smbd/sec_ctx_nonroot.c42
-rw-r--r--testsuite/smbd/sec_ctx_root.c61
-rw-r--r--testsuite/smbd/sec_ctx_stack.c86
-rw-r--r--testsuite/smbd/sec_ctx_torture.c103
-rw-r--r--testsuite/smbd/sec_ctx_utils.c65
-rw-r--r--testsuite/smbd/sec_ctx_utils.h30
-rw-r--r--testsuite/smbd/sighup.exp107
1538 files changed, 477836 insertions, 0 deletions
diff --git a/.cvsignore b/.cvsignore
new file mode 100644
index 00000000000..30433041802
--- /dev/null
+++ b/.cvsignore
@@ -0,0 +1,2 @@
+ID
+testtmp
diff --git a/COPYING b/COPYING
new file mode 100644
index 00000000000..a43ea2126fb
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/Manifest b/Manifest
new file mode 100644
index 00000000000..608c7b1be98
--- /dev/null
+++ b/Manifest
@@ -0,0 +1,78 @@
+Copyright (C) 1997-2003 - Samba-Team
+
+The Samba package you have just unpacked contains the following:
+
+Directory Notes:
+========= ======
+
+docs (Samba Documentation):
+---- ----------------------
+
+ All the Samba documentation for the 3.0 release have been converted to
+ docbook format. Because of this the man pages are now available
+ in both traditional man page format (in the docs/manpages directory)
+ and in HTML format (in the docs/htmldocs directory).
+
+ The Samba HOWTO Collection has undergone some rather large changes
+ and covers all parts of configuration now. It is available
+ as PDF (docs/Samba-HOWTO-Collection.pdf) or in HTML format (in
+ the docs/htmldocs directory). Those with the docbook utilities installed
+ can generate PostScript and text versions of the HOWTO as well.
+
+ The Samba FAQ is still a work in progress, but can be found in
+ HTML format in docs/htmldocs.
+
+
+examples (Example configuration files):
+-------- ------------------------------
+
+ Please pay close attention to the reference smb.conf file
+ smb.conf.default that has now been included as the master guide.
+
+ Do read the smb.conf manual page in considering what settings are
+ appropriate for your site.
+
+
+packaging (Only for those wishing to build binary distributions):
+--------- -------------------------------------------------------
+
+ Currently support is included for the following Linux Distributions :
+
+ Pacfic HiTech, RedHat and SuSE.
+
+ In addition, packaging support is available for SGI and Solaris systems.
+ We hope that other Unix OS vendors will contribute their binary
+ distribution packaging control files - and we hope to make their binary
+ packages available on the master ftp site under:
+
+ ftp://samba.org/pub/samba/Binary_Packages/"OS_Vendor"
+
+
+source (The official Samba source files - expect more of these!):
+------ ----------------------------------------------------------
+
+ To build your own binary files you will need a suitable ansi C
+ compiler.
+
+ Samba uses the GNU autoconf system. In
+ order to build a default Samba for your platform cd into
+ the source/ directory and then type :
+
+ ./configure
+
+ followed by :
+
+ make
+
+ To install the binaries built by the above type :
+
+ make install
+
+ then set up your configuration files.
+
+ NOTE: OS Vendors who provide Samba binary packages will generally
+ integrate all Samba files into their preferred directory locations.
+ These may differ from the default location ALWAYS used by the Samba
+ sources. Please be careful when upgrading a vendor provided binary
+ distribution from files you have built yourself.
+
diff --git a/README b/README
new file mode 100644
index 00000000000..f529610ef43
--- /dev/null
+++ b/README
@@ -0,0 +1,225 @@
+This is a development version of Samba, the free SMB and CIFS client and
+server for UNIX and other operating systems. Samba is maintained by
+the Samba Team, who support the original author, Andrew Tridgell.
+
+>>>> Please read THE WHOLE of this file as it gives important information
+>>>> about the configuration and use of Samba.
+
+NOTE: Installation instructions may be found in
+ docs/htmldocs/install.html
+
+This software is freely distributable under the GNU public license, a
+copy of which you should have received with this software (in a file
+called COPYING).
+
+
+WHAT IS SMB/CIFS?
+=================
+
+This is a big question.
+
+The very short answer is that it is the protocol by which a lot of
+PC-related machines share files and printers and other information
+such as lists of available files and printers. Operating systems that
+support this natively include Windows 9x, Windows NT (and derivatives),
+OS/2, Mac OS X and Linux. Add on packages that achieve the same
+thing are available for DOS, Windows 3.1, VMS, Unix of all kinds,
+MVS, and more. Some Web Browsers can speak this protocol as well
+(smb://). Alternatives to SMB include Netware, NFS, Appletalk,
+Banyan Vines, Decnet etc; many of these have advantages but none are
+both public specifications and widely implemented in desktop machines
+by default.
+
+The Common Internet File system (CIFS) is what the new SMB initiative
+is called. For details watch http://samba.org/cifs.
+
+
+WHY DO PEOPLE WANT TO USE SMB?
+==============================
+
+1. Many people want to integrate their Microsoft desktop clients
+ with their Unix servers.
+
+2. Others want to integrate their Microsoft (etc) servers with Unix
+ servers. This is a different problem to integrating desktop
+ clients.
+
+3. Others want to replace protocols like NFS, DecNet and Novell NCP,
+ especially when used with PCs.
+
+
+WHAT CAN SAMBA DO?
+==================
+
+Please refer to the WHATSNEW.txt included with this README for
+a list of features in the latest Samba release.
+
+Here is a very short list of what samba includes, and what it does.
+For many networks this can be simply summarized by "Samba provides
+a complete replacement for Windows NT, Warp, NFS or Netware servers."
+
+- a SMB server, to provide Windows NT and LAN Manager-style file and print
+ services to SMB clients such as Windows 95, Warp Server, smbfs and others.
+
+- a Windows NT 4.0 Domain Controller replacement.
+
+- a file/print server that can act as a member of a Windows NT 4.0
+ or Active Directory domain.
+
+- a NetBIOS (rfc1001/1002) nameserver, which amongst other things gives
+ browsing support. Samba can be the master browser on your LAN if you wish.
+
+- a ftp-like SMB client so you can access PC resources (disks and
+ printers) from UNIX, Netware, and other operating systems
+
+- a tar extension to the client for backing up PCs
+
+- limited command-line tool that supports some of the NT administrative
+ functionality, which can be used on Samba, NT workstation and NT server.
+
+For a much better overview have a look at the web site at
+http://samba.org/samba, and browse the user survey.
+
+Related packages include:
+
+- smbfs, a Linux-only filesystem allowing you to mount remote SMB
+filesystems from PCs on your Linux box. This is included as standard with
+Linux 2.0 and later.
+
+- cifsvfs, a more advanced Linux-only filesystem allowing you to mount
+remote SMB filesystems from PCs on your Linux box. This is included
+as standard with Linux 2.5 and later.
+
+
+
+CONTRIBUTIONS
+=============
+
+If you want to contribute to the development of the software then
+please join the mailing list. The Samba team accepts patches
+(preferably in "diff -u" format, see http://samba.org/samba/devel/
+for more details) and are always glad to receive feedback or
+suggestions to the address samba@lists.samba.org. More information
+on the various Samba mailing lists can be found at http://lists.samba.org/.
+
+You can also get the Samba sourcecode straight from the CVS tree - see
+http://samba.org/cvs.html.
+
+You could also send hardware/software/money/jewelry or pre-paid pizza
+vouchers directly to Andrew. The pizza vouchers would be especially
+welcome, in fact there is a special field in the survey for people who
+have paid up their pizza :-)
+
+If you like a particular feature then look through the CVS change-log
+(on the web at http://samba.org/cgi-bin/cvsweb/samba) and see
+who added it, then send them an email.
+
+Remember that free software of this kind lives or dies by the response
+we get. If no one tells us they like it then we'll probably move onto
+something else. However, as you can see from the user survey quite a lot of
+people do seem to like it at the moment :-)
+
+
+MORE INFO
+=========
+
+DOCUMENTATION
+-------------
+
+There is quite a bit of documentation included with the package,
+including man pages, and lots of .html files with hints and useful
+info. This is also available from the web page. There is a growing
+collection of information under docs/.
+
+A list of Samba documentation in languages other than English is
+available on the web page.
+
+If you would like to help with the documentation (and we _need_ help!)
+then have a look at the mailing list samba-docs, archived at
+http://lists.samba.org/listinfo/samba-docs/
+
+
+MAILING LIST
+------------
+
+Please do NOT send subscription/unsubscription requests to the lists!
+
+There is a mailing list for discussion of Samba. For details go to
+<http://lists.samba.org/> or send mail to <samba-subscribe@lists.samba.org>
+
+There is also an announcement mailing list where new versions are
+announced. To subscribe go to <http://lists.samba.org/> or send mail
+to <samba-announce-subscribe@lists.samba.org>. All announcements also
+go to the samba list, so you only need to be on one.
+
+For details of other Samba mailing lists and for access to archives, see
+<http://lists.samba.org/>
+
+
+MAILING LIST ETIQUETTE
+----------------------
+
+A few tips when submitting to this or any mailing list.
+
+1. Make your subject short and descriptive. Avoid the words "help" or
+ "Samba" in the subject. The readers of this list already know that
+ a) you need help, and b) you are writing about samba (of course,
+ you may need to distinguish between Samba PDC and other file
+ sharing software). Avoid phrases such as "what is" and "how do
+ i". Some good subject lines might look like "Slow response with
+ Excel files" or "Migrating from Samba PDC to NT PDC".
+
+2. If you include the original message in your reply, trim it so that
+ only the relevant lines, enough to establish context, are
+ included. Chances are (since this is a mailing list) we've already
+ read the original message.
+
+3. Trim irrelevant headers from the original message in your
+ reply. All we need to see is a) From, b) Date, and c) Subject. We
+ don't even really need the Subject, if you haven't changed
+ it. Better yet is to just preface the original message with "On
+ [date] [someone] wrote:".
+
+4. Please don't reply to or argue about spam, spam filters or viruses
+ on any Samba lists. We do have a spam filtering system that is
+ working quite well thank you very much but occasionally unwanted
+ messages slip through. Deal with it.
+
+5. Never say "Me too." It doesn't help anyone solve the
+ problem. Instead, if you ARE having the same problem, give more
+ information. Have you seen something that the other writer hasn't
+ mentioned, which may be helpful?
+
+6. If you ask about a problem, then come up with the solution on your
+ own or through another source, by all means post it. Someone else
+ may have the same problem and is waiting for an answer, but never
+ hears of it.
+
+7. Give as much *relevant* information as possible such as Samba
+ release number, OS, kernel version, etc...
+
+8. RTFM. Google. groups.google.com.
+
+
+NEWS GROUP
+----------
+
+You might also like to look at the usenet news group comp.protocols.smb
+as it often contains lots of useful info and is frequented by lots of
+Samba users. The newsgroup was initially setup by people on the Samba
+mailing list. It is not, however, exclusive to Samba, it is a forum for
+discussing the SMB protocol (which Samba implements). The samba list
+is gatewayed to this newsgroup.
+
+
+WEB SITE
+--------
+
+A Samba WWW site has been setup with lots of useful info. Connect to:
+
+http://samba.org/samba/
+
+As well as general information and documentation, this also has searchable
+archives of the mailing list and a user survey that shows who else is using
+this package. Have you registered with the survey yet? :-)
+
diff --git a/Read-Manifest-Now b/Read-Manifest-Now
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/Read-Manifest-Now
diff --git a/Roadmap b/Roadmap
new file mode 100644
index 00000000000..42c63f78098
--- /dev/null
+++ b/Roadmap
@@ -0,0 +1,30 @@
+Copyright (C) 1997-2003 Samba-Team
+
+The Samba-Team are committed to an aggressive program to deliver quality
+controlled software to a well defined roadmap.
+
+The current Samba Beta series of Samba 3.0.0 is called the "Domain Integration"
+release.
+
+The following development objectives for future releases
+are in progress:
+----------------------------------------------------------------------------
+Samba-3.0.0 The Domain Integration Release.
+
+Samba-3.0.x Improvements in Management and Migration tools, &
+ general code stabilization work.
+
+Samba-3.x.x Requirements are currently under discussion.
+
+Samba-4 Danger Will Robinson, a big code clean up with major
+ system redesign. More will be announced as this work
+ starts to take shape.
+
+
+Note that it is a given that the Samba-Team will continue to track
+Windows (NT/200x) update releases, ensuring that Samba will work
+well with whatever "Beta" releases Redmond throws our way :-).
+
+You may also note that the release numbers get fuzzier the
+further into the future the objectives get. This is intentional
+as we cannot yet commit to exact timeframes.
diff --git a/WHATSNEW.txt b/WHATSNEW.txt
new file mode 100644
index 00000000000..69036fae3c6
--- /dev/null
+++ b/WHATSNEW.txt
@@ -0,0 +1,1072 @@
+ WHATS NEW IN Samba 3.0.0
+ September 24, 2003
+ ==============================
+
+This is the first official release of Samba 3.0.0 code base. Work
+on the SAMBA_3_0 CVS branch continues. Please refer to the section
+on "Known Issues" for more details.
+
+
+Major new features:
+-------------------
+
+1) Active Directory support. Samba 3.0 is now able to
+ join a ADS realm as a member server and authenticate
+ users using LDAP/Kerberos.
+
+2) Unicode support. Samba will now negotiate UNICODE on the wire
+ and internally there is now a much better infrastructure for
+ multi-byte and UNICODE character sets.
+
+3) New authentication system. The internal authentication system
+ has been almost completely rewritten. Most of the changes are
+ internal, but the new auth system is also very configurable.
+
+4) New default filename mangling system.
+
+5) A new "net" command has been added. It is somewhat similar to
+ the "net" command in windows. Eventually we plan to replace
+ numerous other utilities (such as smbpasswd) with subcommands
+ in "net".
+
+6) Samba now negotiates NT-style status32 codes on the wire. This
+ improves error handling a lot.
+
+7) Better Windows 2000/XP/2003 printing support including publishing
+ printer attributes in active directory.
+
+8) New loadable module support for passdb backends and character
+ sets.
+
+9) New default dual-daemon winbindd support for better performance.
+
+10) Support for migrating from a Windows NT 4.0 domain to a Samba
+ domain and maintaining user, group and domain SIDs.
+
+11) Support for establishing trust relationships with Windows NT 4.0
+ domain controllers.
+
+12) Initial support for a distributed Winbind architecture using
+ an LDAP directory for storing SID to uid/gid mappings.
+
+13) Major updates to the Samba documentation tree.
+
+14) Full support for client and server SMB signing to ensure
+ compatibility with default Windows 2003 security settings.
+
+15) Improvement of ACL mapping features based on code donated by
+ Andreas Grünbacher.
+
+
+Plus lots of other improvements!
+
+
+Additional Documentation
+------------------------
+
+Please refer to Samba documentation tree (included in the docs/
+subdirectory) for extensive explanations of installing, configuring
+and maintaining Samba 3.0 servers and clients. It is advised to
+begin with the Samba-HOWTO-Collection for overviews and specific
+tasks (the current book is up to approximately 400 pages) and to
+refer to the various man pages for information on individual options.
+
+We are very glad to be able to include the second edition of
+"Using Samba" by Jay Ts, Robert Eckstein, and David Collier-Brown
+(O'Reilly & Associates) in this release. The book is available
+on-line at http://samba.org/samba/docs/ and is included with
+the Samba Web Administration Tool (SWAT). Thanks to the authors and
+publisher for making "Using Samba" under the GNU Free Documentation
+License.
+
+
+######################################################################
+Changes since 3.0rc4
+####################
+
+Please refer to the CVS log for the SAMBA_3_0 branch for complete
+details:
+
+1) Fix bug that prevented restoring filenames of length
+ >100 characters.
+2) Fix bug that prevented fast path code in strchr_m
+ from being used.
+3) Make sure we store the desired access flag on incoming
+ SAMR rpc calls.
+4) Fix smbd crash when dealing with mangled file names.
+5) Ensure that the group comment field is not overwritten
+ if it already exists.
+6) Fix bug that prevented 'net rpc join' from working
+ with mixed mode AD domains (bug 442).
+7) Fix crash in smbd when a Samba PDC is not able to
+ enumerate trusted domains (bug 450).
+8) Fix crash bug found by the Samba4 testsuite.
+9) Fix bug that prevented smbd from returning an ACL list
+ if one of the SIDs could not be resolved (bug 470).
+10) Remove -P option from smbclient printing scripts since it
+ has a different meaning in Samba 3.0 (bug 473).
+11) Sync smbldap-tools with latest version
+12) Cleanup some warnings produced by the Sun C compiler.
+13) Several fixes for SWAT relating to international character
+ sets.
+
+
+Changes since 3.0rc3
+####################
+
+1) Fix incorrect error message in testparm.c regarding 'map system'.
+2) Protect against core dump if ioctl for print job sends invalid
+ fid.
+3) Fix bug in generic hash cacluation.
+4) Remove references to unused 'strip dot' parameter
+5) Fix CPU burn bug in multi-byte character conversion.
+6) Use opt_target_workgroup instead of lp_workgroup() in vampire
+ code so we can override the value in smb.conf with the -w option.
+7) Display an error if we can't create a posix account for the
+ user when running 'net rpc vampire' (bug 323).
+8) Fix UTF8 conversion bugs in LDAP passdb and idmap code (bug 296).
+9) Fix smbd crash when changing the machine trust account password
+ (bug 273).
+10) Remove getpwnam() calls from init_sam_from_xxx(). This means
+ that %u & %g will no longer expand in the "login ..." set of
+ smb.conf options, but %U and %G still do. The payback is that
+ winbindd local accounts for users work with 'wbinfo -u'
+ when winbind is running on a Samba PDC.
+11) Fix unitiailized timestamp where merging print_jobs and
+ lpq listing.
+12) Fix bug in debian packaging files affecting non-i386 platforms.
+
+
+Changes since 3.0rc2
+####################
+
+1) Remove Perl module dependencies in generated RedHat 8/9 RPMS.
+2) Update mount helper to take synonyms for file_mode and
+ dir_mode (fmask and dmask).
+3) Fix portability bug with log2pcaphex.
+4) Use different algorithm to generate codepages source code which
+ allows to take gaps into account thus making unnecessary
+ extended [index] = value, syntax in to_ucs2 array (bug 380).
+5) Fix comment strings to 43 bytes as per spec.
+6) Fix pam_winbind compile bug on FreeBSD (bug 261).
+7) Support for in-memory keytabs, which are needed to make heimdal
+ work properly. MIT does not support them, so this check will be
+ used to decide whether to use them. (partial fix for bug 372).
+8) Disable RC4-HMAC on broken heimdal setups. (remainder of bug
+ 372).
+9) Correct bug in smbclient that resulted in errors when untarring
+ long filenames (bug 308).
+10) Improve autoconf checks for PAM header files and libs.
+11) Added fast path to convert_string() when dealing with
+ ASCII->ASCII, UCS2-LE->ASCII, and ASCII->UCS2-LE with
+ values <= 0x7F.
+12) Quiet debug messages when we don't find a module and it is not
+ a critical error (bug 375).
+13) Fix UNIX passwd sync properly.
+14) Fix more transitive trust issues in winbindd (bug 305).
+15) Ensure that winbindd functions with 'disable netbios = yes'
+16) Store the real short domain name in secrets.tdb as soon as we
+ know it. Also display an error message when joining an AD
+ domain and the 'workgroup' parameter has not been specified.
+17) Return 0 DFS links instead of -1 when dfs support is not enabled.
+18) Update LDAP schema for Netscape DS 4.x and Novell eDirectory 8.7
+19) Ensure that name types can be specified using name#type notation
+ in the 'net' command (bug 73).
+20) Add retry looks to ADS sequence number and domain SID lookups
+ (bug 364).
+21) use a variant of alloc_sub_basic() for string lists such as
+ 'valid users', 'write list', and 'read list' (bug 397).
+22) Fix seg fault when winbindd receives an error from the AD server
+ in response to an LDAP search (bug 282).
+23) Update findsmb to use the new syntax for smbclient and nmblookup.
+24) Fix bug that prevented variables from being used in explicitly
+ defined path in [homes].
+25) Only set SIDs when they're returned by the MySQL query
+ (pdb_mysql.so).
+26) Include support for NTLMv2 key exchange.
+27) Revert default for 'client ntlmv2 auth' to off (bug 359).
+28) Fix crash in winbindd when the trust account password gets
+ changed underneath us via 'net rpc changetrustpw' (bug 382).
+29) Use djb-algorithm string hash - faster than the tdb one we
+ used to use. Does not change on disk format or hashing location.
+30) Implements some kind of improved AFS support for Samba on
+ Linux with OpenAFS 1.2.10. './configure --with-fake-kaserver'
+ assumes that you have OpenAFS on your machine.
+31) When enumerating dfs shares loop from 0 to lp_numservices() instead
+ of relying on lp_servicename(n) to return an empty string for
+ invalid service numbers (bug 403).
+32) Fix crash bug in 'net rpc samdump' (bug 334).
+33) Fix crash bug in WINS NSS module (bug 299).
+34) Fix a few minor compile errors on HP-UX.
+
+
+
+Changes since 3.0rc1
+####################
+
+1) Add levels 261 and 262 to search. Found using Samba4 tester.
+2) Correct bad error return code in session setup reply
+3) Fix bug where smbd returned DOS error codes from SMBsearch
+ even when NT1 protocol was negotiated.
+4) Implement SMBexit properly.
+5) Return group lists from a Samba PDC to a Windows 9x/ME box
+ in implementing user level access control (bug 314).
+6) Prevent SWAT from crashing when adding shares (bug 254)
+7) Fix various documentation issues (bugs 304 & 214)
+8) Fix wins server listing in SWAT (bug 197)
+9) Fix problem in rpcclient that caused enumerating printer
+ drivers to report failure (bug 294).
+10) Use kerberos 5 authentication in our client code whenever possible
+11) Fix schannel bug that caused Active Directory DC's to downgrade our
+ machine account to an NT member.
+12) Implement missing SAMR_REMOVE_USER_FOREIGN_DOMAIN call (bug 252).
+13) Implement automatic generation of include/version.h
+14) Include initial version of smbldap-tool scripts for the Samba
+ 3.0 schema.
+15) Implement numerous fixes for multi-byte character strings.
+16) Enable 'unix extensions' parameter by default.
+17) Make sure we set the SID type when falling back to the rid
+ algorithm (bug 245).
+18) Correct linking problems with pam_smbpass (bug 327).
+19) Add SYSV defines for Irix and Solaris to ensure the 'printing'
+ parameter default to the correct value (bug 230)
+20) Fix recursion bug in alloc_string_sub() (bug 289, et. al.)
+21) Ensure that 'make install' includes the static and shared
+ versions of the libsmbclient libraries.
+22) Add CP850 and CP437 internal character set support (bug 150).
+23) Add support to examples/LDAP/convertSambaAccount for generating
+ LDIF modify files instead of just add (303).
+24) Fix support for -W option in smbclient (bug 39)
+25) Remove 'ldap trust ids' parameter since it could not be supported
+ by the current architecture.
+26) Don't crash when no argument is given to -T in smbclient (bug 345).
+27) Ensure smbadduser contains the same paths for the smbpasswd file
+ as the other Samba tools (bug 290).
+28) Port of 'available = no' fix for [homes] from SAMBA_2_2 cvs tree.
+29) Add sanity checks to DeletePrinterData[Ex]() and ensure that the
+ modified printer is written to disk.
+30) Force winbindd to periodically update the trusted domain cache.
+31) Remove outdated import/export script to convert an smbpasswd file
+ to and from and LDAP directory. Use the pdbedit tool instead.
+32) Ensure that %U substitution is restored on next valid packet
+ if a logon fails.
+
+
+Changes since 3.0beta3
+######################
+
+1) Various memory leak fixes.
+2) Provide full support for SMB signing (server and client)
+3) Check for broken getgrouplist() in glibc.
+4) Don't get stuck in an infinite loop listing directories
+ recursively if the server returns an empty directory name
+ (bug 222).
+5) Idle LDAP connections after 150 seconds.
+6) Patched make uninstallmodules (bug 236).
+7) Fix bug that caused smbd to return incomplete directory listings
+ when UNIX files contained MS wildcard characters.
+8) Quiet default debug messages in command line tools.
+9) Fixes to avoid panics on invalid multi-byte strings.
+10) Fix error messages when creating a new smbpasswd file (bug 198).
+11) Implemented better detection routines in autoconf scripts for
+ locating ads support on the host OS.
+12) Fix bug that caused libraries in /usr/local/lib to be ignored
+ (bug 174).
+13) Ensure winbindd_ads uses the correct realm or domain name when
+ connecting to trusted DC.
+14) Ensure a correct prototype is created for snprintf() (bug 187)
+15) Stop files being created on read-only shares in some circumstances.
+16) Fix wbinfo -p (bug 251)
+17) Support schannel on any tcp/ip connection if necessary
+18) Correct bug in user_in_list() so that it works with winbind groups
+ again.
+19) Ensure the schannel bind credentials default to the domain
+ of the destination host.
+20) Default password expiration time in account_pol.tdb to never
+ expire. Remove any existing account_pol.tdb file to reset
+ the new default policy (bug 184).
+21) Add buttons to SWAT to change the view of smb.conf (bug 212)
+22) Fix incorrect checks that determine whether or not the 'add user
+ script' has been set.
+23) More cleanup for internal character set conversions.
+24) Fixes for multi-byte strings in stat cache code.
+25) Ensure that the net command honors the 'workgroup' parameter
+ in smb.conf when not overridden from the command line.
+26) Add gss-spnego support to the ntlm_auth tool.
+27) Add vfs_default_quota VFS module.
+28) Added server support for NT quota interfaces.
+29) Prevent Krb5 replay attacks by adding a replay_cache.
+30) Fix problems with winbindd and transitive trusts in AD domains.
+31) Added -S to client tools for setting SMB signing options on the
+ command line.
+32) Fix bug causing the 'passwd change program' to be called as the
+ connected user and not root.
+33) Fixed data corruption bug in byte-range locking (e.g. affected MS Excel).
+34) Support winbindd on FreeBSD is possible.
+35) Look at only the first OID in the security blob sent in the session
+ setup request to determine the token type.
+36) Only push locks onto a blocking lock queue if the posix lock failed with
+ EACCES or EAGAIN (this means another lock conflicts). Else return an
+ error and don't queue the request.
+37) Fix command line argument processing for smbtar.
+38) Correct issue that caused smbd to return generic unix_user.<uid>
+ for lookupsid().
+39) Default to algorithmic mapping when generating a rid for a group
+ mapping.
+40) Expand %g and %G in logon script, profile path, etc... during
+ a domain logon (bug 208).
+41) Make sure smbclient obeys '-s <config>'
+42) Added win2k3 shadow copy operations to VFS interface.
+43) Allow connections to samba domain member as SERVER\user (don't
+ always default to DOMAIN\user).
+44) Remove checks in winbindd that caused it to attempt to use
+ non-transitive trust relationships.
+45) Remove delays in winbindd caused by invalid DNS lookups.
+46) Fix supplementary group memberships on systems with slightly
+ broken NSS implementations (bug 267).
+47) Correct issue that prevented smbclient from viewing shares on
+ a win2k server when using a non-anonymous connection (bug 284).
+48) Add --domain=DOMAIN_NAME to wbinfo for limiting operations like
+ 'wbinfo -u' to a single domain. The '.' character represents
+ our domain.
+49) Fix group enumeration bug when using an LDAP directory for
+ storing group mappings.
+50) Default to use NTLMv2 if available. Fallback to not use LM/NTLM
+ when the extended security capability bit is not set.
+51) Fix crash in 'wbinfo -a' when using extended characters in the
+ username (bug 269).
+52) Fix multi-byte strupper() panics (bug 205).
+53) Add vfs_readonly VFS module.
+54) Make sure to initialize the sambaNextUserRid and sambaNextGroupRid
+ attributes when using 'idmap backend = ldap' (bug 280).
+55) Make sure that users shared between a Samba PDC and member
+ samba server are seen as domain users and not local users on the
+ domain member.
+56) Fix Query FS Info level 2.
+57) Allow enumeration of users and groups by win9x "file server" (bug
+ 286).
+58) Create symlinks during install for modules that support mutliple
+ functions (bug 91).
+59) More iconv detection fixes.
+60) Fix path length error in vfs_recycle module (bug 291).
+61) Added server support for the LSA_DS UUID on the \lsarpc pipe.
+ (server DsRoleGetPrimaryDomainInfo() is currently disabled).
+62) Fix SMBseek and get/set position calls.
+62) Fix SetFileInfo level 1.
+63) Added tool to convert smbd log file to a pcap file (log2pcaphex).
+
+
+
+Changes since 3.0beta2
+######################
+
+1) Added fix for Japanese case names in statcache code;
+ these can change size on upper casing.
+2) Correct issues with iconv detection in configure script
+ (support needed to find iconv libraries on FreeBSD).
+3) Fix bug that caused a WINS server to be marked as dead
+ incorrectly (bug #190).
+4) Removing additional deadlocks conditions that prevented
+ winbindd from running on a Samba PDC (used for trust
+ relationships).
+5) Add support for searching for Active Directory for
+ published printers (net ads printer search).
+6) Separate UNIX username from DOMAIN\username in pipe
+ credentials.
+7) Auth modules now support returning NT_STATUS_NOT_IMPLEMENTED
+ for cases that they cannot handle.
+8) Flush winbindd connection cache when the machine trust account
+ password is changed while a connection is open (bug #200).
+9) Add support for 'OSVersion' server printer data string
+ (corrects problem with uploading printer drivers from
+ WinXP clients).
+10) Numerous memory leak fixes.
+11) LDAP fixes ("passdb backend = ldapsam" & "idmap backend = ldap"):
+ - Store domain SID in LDAP directory.
+ - store idmap information in existing entries (use sambaSID=...
+ if adding a new entry).
+12) Fix incorrect usage of primary group SID when looking up user
+ groups (bug #109).
+13) Remove idmap_XX_to_XX calls from smbd. Move back to the the
+ winbind_XXX and local_XXX calls used in 2.2.
+14) All uid/gid allocation must involve winbindd now (we do not
+ attempt to map unknown SIDs to a UNIX identify).
+15) Add 'winbind trusted domains only' parameter to force a domain
+ member. The server to use matching users names from /etc/passwd
+ for its domain (needed for domain member of a Samba domain).
+16) Rename 'idmap only' to 'enable rid algorithm' for better clarity
+ (defaults to "yes").
+17) Add support for multi-byte statcache code (bug #185)
+18) Fix open mode race condition.
+19) Implement winbindd local account management functions. Refer to
+ the "Winbind Changes" section for details.
+20) Move RID allocation functions into idmap backend.
+21) Fix parsing error that prevented publishing printers from a
+ Samba server in an AD domain.
+22) Revive NTLMSSP support for named pipes.
+23) More SCHANNEL fixes.
+24) Correct SMB signing with NTLMSSP.
+25) Fix coherency bug in print handle/printer object caching code
+ that could cause XP clients to infinitely loop while updating
+ their local printer cache.
+26) Make winbindd use its dual-daemon mode by default (use -Y to
+ start as a single process).
+27) Add support to nmbd and winbindd for 'smbcontrol <pid>
+ reload-config'.
+28) Correct problem with smbtar when dealing with files > 8Gb
+ (bug #102).
+
+
+
+Changes since 3.0beta1
+######################
+
+1) Rework our smb signing code again, this factors out some of
+ the common MAC calculation code, and now supports multiple
+ outstanding packets (bug #40).
+2) Enforce 'client plaintext auth', 'client lanman auth' and 'client
+ ntlmv2 auth'.
+3) Correct timestamp problem on 64-bit machines (bug #140).
+4) Add extra debugging statements to winbindd for tracking down
+ failures.
+5) Fix bug when aliased 'winbind uid/gid' parameters are used.
+ ('winbind uid/gid' are now replaced with 'idmap uid/gid').
+6) Added an auth flag that indicates if we should be allowed
+ to fall back to NTLMSSP for SASL if krb5 fails.
+7) Fixed the bug that forced us not to use the winbindd cache when
+ we have a primary ADS domain and a secondary (trusted) NT4
+ domain.
+8) Use lp_realm() to find the default realm for 'net ads password'.
+9) Removed editreg from standard build until it is portable..
+10) Fix domain membership for servers not running winbindd.
+11) Correct race condition in determining the high water mark
+ in the idmap backend (bug #181).
+12) Set the user's primary unix group from usrmgr.exe (partial
+ fix for bug #45).
+13) Show comments when doing 'net group -l' (bug #3).
+14) Add trivial extension to 'net' to dump current local idmap
+ and restore mappings as well.
+15) Modify 'net rpc vampire' to add new and existing users to
+ both the idmap and the SAM. This code needs further testing.
+16) Fix crash bug in ADS searches.
+17) Build libnss_wins.so as part of nsswitch target (bug #160).
+18) Make net rpc vampire return an error if the sam sync RPC
+ returns an error.
+19) Fail to join an NT 4 domain as a BDC if a workstation account
+ using our name exists.
+20) Fix various memory leaks in server and client code
+21) Remove the short option to --set-auth-user for wbinfo (-A) to
+ prevent confusion with the -a option (bug #158).
+22) Added new 'map acl inherit' parameter.
+23) Removed unused 'privileges' code from group mapping database.
+24) Don't segfault on empty passdb backend list (bug #136).
+25) Fixed acl sorting algorithm for Windows 2000 clients.
+26) Replace universal group cache with netsamlogon_cache
+ from APPLIANCE_HEAD branch.
+27) Fix autoconf detection issues surrounding --with-ads=yes
+ but no Krb5 header files installed (bug #152).
+28) Add LDAP lookup for domain sequence number in case we are
+ joined using NT4 protocols to a native mode AD domain.
+29) Fix backend method selection for trusted NT 4 (or 2k
+ mixed mode) domains.
+30) Fixed bug that caused us to enumerate domain local groups
+ from native mode AD domains other than our own.
+31) Correct group enumeration for viewing in the Windows
+ security tab (bug #110).
+32) Consolidate the DC location code.
+33) Moved 'ads server' functionality into 'password server' for
+ backwards compatibility.
+34) Fix winbindd_idmap tdb upgrades from a 2.2 installation.
+ ( if you installed beta1, be sure to
+ 'mv idmap.tdb winbindd_idmap.tdb' ).
+35) Fix pdb_ldap segfaults, and wrong default values for
+ ldapsam_compat.
+36) Enable negative connection cache for winbindd's ADS backend
+ functions.
+37) Enable address caching for active directory DC's so we don't
+ have to hit DNS so much.
+38) Fix bug in idmap code that caused mapping to randomly be
+ redefined.
+39) Add tdb locking code to prevent race condition when adding a
+ new mapping to idmap.
+40) Fix 'map to guest = bad user' when acting as a PDC supporting
+ trust relationships.
+41) Prevent deadlock issues when running winbindd on a Samba PDC
+ to handle allocating uids & gids for trusted users and groups
+42) added LOCALE patch from Steve Langasek (bug #122).
+43) Add the 'guest' passdb backend automatically to the end of
+ the 'passdb backend' list if 'guest account' has a valid
+ username.
+44) Remove samstrict_dc auth method. Rework 'samstrict' to only
+ handle our local names (or domain name if we are a PDC).
+ Move existing permissive 'sam' method to 'sam_ignoredomain'
+ and make 'samstrict' the new default 'sam' auth method.
+45) Match Windows NT4/2k behavior when authenticating a user with
+ and unknown domain (default to our domain if we are a DC or
+ domain member; default to our local name if we are a
+ standalone server).
+46) Fix Get_Pwnam() to always fall back to lookup 'user' if the
+ 'DOMAIN\user' lookup fails. This matches 2.2. behavior.
+47) Fix the trustdom_cache code to update the list of trusted
+ domains when operating as a domain member and not using
+ winbindd.
+48) Remove 'nisplussam' passdb backend since it has suffered for
+ too long without a maintainer.
+
+
+
+
+######################################################################
+Upgrading from a previous Samba 3.0 beta
+########################################
+
+Beginning with Samba 3.0.0beta3, the RID allocation functions
+have been moved into winbindd. Previously these were handled
+by each passdb backend. This means that winbindd must be running
+to automatically allocate RIDs for users and/or groups. Otherwise,
+smbd will use the 2.2 algorithm for generating new RIDs.
+
+If you are using 'passdb backend = tdbsam' with a previous Samba
+3.0 beta release (or possibly alpha), it may be necessary to
+move the RID_COUNTER entry from /usr/local/samba/private/passdb.tdb
+to winbindd_idmap.tdb. To do this:
+
+1) Ensure that winbindd_idmap.tdb exists (launch winbindd at least
+ once)
+2) build tdbtool by executing 'make tdbtool' in the source/tdb/
+ directory
+3) run: (note that 'tdb>' is the tool's prompt for input)
+
+ root# ./tdbtool /usr/local/samba/private/passdb.tdb
+ tdb> show RID_COUNTER
+ key 12 bytes
+ RID_COUNTER
+ data 4 bytes
+ [000] 0A 52 00 00 .R.
+
+ tdb> move RID_COUNTER /usr/local/samba/var/locks/winbindd_idmap.tdb
+ ....
+ record moved
+
+If you are using 'passdb backend = ldapsam', it will be necessary to
+store idmap entries in the LDAP directory as well (i.e. idmap backend
+= ldap). Refer to the 'net idmap' command for more information on
+migrating SID<->UNIX id mappings from one backend to another.
+
+If the RID_COUNTER record does not exist, then these instructions are
+unneccessary and the new RID_COUNTER record will be correctly generated
+if needed.
+
+
+
+########################
+Upgrading from Samba 2.2
+########################
+
+This section is provided to help administrators understand the details
+involved with upgrading a Samba 2.2 server to Samba 3.0.
+
+
+Building
+--------
+
+Many of the options to the GNU autoconf script have been modified
+in the 3.0 release. The most noticeable are:
+
+ * removal of --with-tdbsam (is now included by default; see section
+ on passdb backends and authentication for more details)
+
+ * --with-ldapsam is now on used to provided backward compatible
+ parameters for LDAP enabled Samba 2.2 servers. Refer to the passdb
+ backend and authentication section for more details
+
+ * inclusion of non-standard passdb modules may be enabled using
+ --with-expsam. This includes an XML backend and a mysql backend.
+
+ * removal of --with-msdfs (is now enabled by default)
+
+ * removal of --with-ssl (no longer supported)
+
+ * --with-utmp now defaults to 'yes' on supported systems
+
+ * --with-sendfile-support is now enabled by default on supported
+ systems
+
+
+Parameters
+----------
+
+This section contains a brief listing of changes to smb.conf options
+in the 3.0.0 release. Please refer to the smb.conf(5) man page for
+complete descriptions of new or modified parameters.
+
+Removed Parameters (order alphabetically):
+
+ * admin log
+ * alternate permissions
+ * character set
+ * client codepage
+ * code page directory
+ * coding system
+ * domain admin group
+ * domain guest group
+ * force unknown acl user
+ * nt smb support
+ * postscript
+ * printer driver
+ * printer driver file
+ * printer driver location
+ * status
+ * strip dot
+ * total print jobs
+ * use rhosts
+ * valid chars
+ * vfs options
+
+New Parameters (new parameters have been grouped by function):
+
+ Remote management
+ -----------------
+ * abort shutdown script
+ * shutdown script
+
+ User and Group Account Management
+ ---------------------------------
+ * add group script
+ * add machine script
+ * add user to group script
+ * algorithmic rid base
+ * delete group script
+ * delete user from group script
+ * passdb backend
+ * set primary group script
+
+ Authentication
+ --------------
+ * auth methods
+ * realm
+
+ Protocol Options
+ ----------------
+ * client lanman auth
+ * client NTLMv2 auth
+ * client schannel
+ * client signing
+ * client use spnego
+ * disable netbios
+ * ntlm auth
+ * paranoid server security
+ * server schannel
+ * server signing
+ * smb ports
+ * use spnego
+
+ File Service
+ ------------
+ * get quota command
+ * hide special files
+ * hide unwriteable files
+ * hostname lookups
+ * kernel change notify
+ * mangle prefix
+ * map acl inherit
+ * msdfs proxy
+ * set quota command
+ * use sendfile
+ * vfs objects
+
+ Printing
+ --------
+ * max reported print jobs
+
+ UNICODE and Character Sets
+ --------------------------
+ * display charset
+ * dos charset
+ * unicode
+ * unix charset
+
+ SID to uid/gid Mappings
+ -----------------------
+ * idmap backend
+ * idmap gid
+ * idmap uid
+ * winbind enable local accounts
+ * winbind trusted domains only
+ * template primary group
+ * enable rid algorithm
+
+ LDAP
+ ----
+ * ldap delete dn
+ * ldap group suffix
+ * ldap idmap suffix
+ * ldap machine suffix
+ * ldap passwd sync
+ * ldap user suffix
+
+ General Configuration
+ ---------------------
+ * preload modules
+ * private dir
+
+Modified Parameters (changes in behavior):
+
+ * encrypt passwords (enabled by default)
+ * mangling method (set to 'hash2' by default)
+ * passwd chat
+ * passwd program
+ * restrict anonymous (integer value)
+ * security (new 'ads' value)
+ * strict locking (enabled by default)
+ * unix extensions (enabled by default)
+ * winbind cache time (increased to 5 minutes)
+ * winbind uid (deprecated in favor of 'idmap uid')
+ * winbind gid (deprecated in favor of 'idmap gid')
+
+
+Databases
+---------
+
+This section contains brief descriptions of any new databases
+introduced in Samba 3.0. Please remember to backup your existing
+${lock directory}/*tdb before upgrading to Samba 3.0. Samba will
+upgrade databases as they are opened (if necessary), but downgrading
+from 3.0 to 2.2 is an unsupported path.
+
+Name Description Backup?
+---- ----------- -------
+account_policy User policy settings yes
+gencache Generic caching db no
+group_mapping Mapping table from Windows yes
+ groups/SID to unix groups
+winbindd_idmap ID map table from SIDS to UNIX yes
+ uids/gids.
+namecache Name resolution cache entries no
+netsamlogon_cache Cache of NET_USER_INFO_3 structure no
+ returned as part of a successful
+ net_sam_logon request
+printing/*.tdb Cached output from 'lpq no
+ command' created on a per print
+ service basis
+registry Read-only samba registry skeleton no
+ that provides support for exporting
+ various db tables via the winreg RPCs
+
+
+Changes in Behavior
+-------------------
+
+The following issues are known changes in behavior between Samba 2.2 and
+Samba 3.0 that may affect certain installations of Samba.
+
+ 1) When operating as a member of a Windows domain, Samba 2.2 would
+ map any users authenticated by the remote DC to the 'guest account'
+ if a uid could not be obtained via the getpwnam() call. Samba 3.0
+ rejects the connection as NT_STATUS_LOGON_FAILURE. There is no
+ current work around to re-establish the 2.2 behavior.
+
+ 2) When adding machines to a Samba 2.2 controlled domain, the
+ 'add user script' was used to create the UNIX identity of the
+ machine trust account. Samba 3.0 introduces a new 'add machine
+ script' that must be specified for this purpose. Samba 3.0 will
+ not fall back to using the 'add user script' in the absence of
+ an 'add machine script'
+
+
+######################################################################
+Passdb Backends and Authentication
+##################################
+
+There have been a few new changes that Samba administrators should be
+aware of when moving to Samba 3.0.
+
+ 1) encrypted passwords have been enabled by default in order to
+ inter-operate better with out-of-the-box Windows client
+ installations. This does mean that either (a) a samba account
+ must be created for each user, or (b) 'encrypt passwords = no'
+ must be explicitly defined in smb.conf.
+
+ 2) Inclusion of new 'security = ads' option for integration
+ with an Active Directory domain using the native Windows
+ Kerberos 5 and LDAP protocols.
+
+ MIT kerberos 1.3.1 supports the ARCFOUR-HMAC-MD5 encryption
+ type which is neccessary for servers on which the
+ administrator password has not been changed, or kerberos-enabled
+ SMB connections to servers that require Kerberos SMB signing.
+ Besides this one difference, either MIT or Heimdal Kerberos
+ distributions are usable by Samba 3.0.
+
+
+Samba 3.0 also includes the possibility of setting up chains
+of authentication methods (auth methods) and account storage
+backends (passdb backend). Please refer to the smb.conf(5)
+man page for details. While both parameters assume sane default
+values, it is likely that you will need to understand what the
+values actually mean in order to ensure Samba operates correctly.
+
+The recommended passdb backends at this time are
+
+ * smbpasswd - 2.2 compatible flat file format
+ * tdbsam - attribute rich database intended as an smbpasswd
+ replacement for stand alone servers
+ * ldapsam - attribute rich account storage and retrieval
+ backend utilizing an LDAP directory.
+ * ldapsam_compat - a 2.2 backward compatible LDAP account
+ backend
+
+Certain functions of the smbpasswd(8) tool have been split between the
+new smbpasswd(8) utility, the net(8) tool, and the new pdbedit(8)
+utility. See the respective man pages for details.
+
+
+######################################################################
+LDAP
+####
+
+This section outlines the new features affecting Samba / LDAP
+integration.
+
+New Schema
+----------
+
+A new object class (sambaSamAccount) has been introduced to replace
+the old sambaAccount. This change aids us in the renaming of attributes
+to prevent clashes with attributes from other vendors. There is a
+conversion script (examples/LDAP/convertSambaAccount) to modify and LDIF
+file to the new schema.
+
+Example:
+
+ $ ldapsearch .... -b "ou=people,dc=..." > old.ldif
+ $ convertSambaAccount <DOM SID> old.ldif new.ldif
+
+The <DOM SID> can be obtained by running 'net getlocalsid <DOMAINNAME>'
+on the Samba PDC as root.
+
+The old sambaAccount schema may still be used by specifying the
+"ldapsam_compat" passdb backend. However, the sambaAccount and
+associated attributes have been moved to the historical section of
+the schema file and must be uncommented before use if needed.
+The 2.2 object class declaration for a sambaAccount has not changed
+in the 3.0 samba.schema file.
+
+Other new object classes and their uses include:
+
+ * sambaDomain - domain information used to allocate rids
+ for users and groups as necessary. The attributes are added
+ in 'ldap suffix' directory entry automatically if
+ an idmap uid/gid range has been set and the 'ldapsam'
+ passdb backend has been selected.
+
+ * sambaGroupMapping - an object representing the
+ relationship between a posixGroup and a Windows
+ group/SID. These entries are stored in the 'ldap
+ group suffix' and managed by the 'net groupmap' command.
+
+ * sambaUnixIdPool - created in the 'ldap idmap suffix' entry
+ automatically and contains the next available 'idmap uid' and
+ 'idmap gid'
+
+ * sambaIdmapEntry - object storing a mapping between a
+ SID and a UNIX uid/gid. These objects are created by the
+ idmap_ldap module as needed.
+
+ * sambaSidEntry - object representing a SID alone, as a Structural
+ class on which to build the sambaIdmapEntry.
+
+
+New Suffix for Searching
+------------------------
+
+The following new smb.conf parameters have been added to aid in directing
+certain LDAP queries when 'passdb backend = ldapsam://...' has been
+specified.
+
+ * ldap suffix - used to search for user and computer accounts
+ * ldap user suffix - used to store user accounts
+ * ldap machine suffix - used to store machine trust accounts
+ * ldap group suffix - location of posixGroup/sambaGroupMapping entries
+ * ldap idmap suffix - location of sambaIdmapEntry objects
+
+If an 'ldap suffix' is defined, it will be appended to all of the
+remaining sub-suffix parameters. In this case, the order of the suffix
+listings in smb.conf is important. Always place the 'ldap suffix' first
+in the list.
+
+Due to a limitation in Samba's smb.conf parsing, you should not surround
+the DN's with quotation marks.
+
+
+IdMap LDAP support
+------------------
+
+Samba 3.0 supports an ldap backend for the idmap subsystem. The
+following options would inform Samba that the idmap table should be
+stored on the directory server onterose in the "ou=idmap,dc=plainjoe,
+dc=org" partition.
+
+ [global]
+ ...
+ idmap backend = ldap:ldap://onterose/
+ ldap idmap suffix = ou=idmap,dc=plainjoe,dc=org
+ idmap uid = 40000-50000
+ idmap gid = 40000-50000
+
+This configuration allows winbind installations on multiple servers to
+share a uid/gid number space, thus avoiding the interoperability problems
+with NFS that were present in Samba 2.2.
+
+
+
+######################################################################
+Trust Relationships and a Samba Domain
+######################################
+
+Samba 3.0.0beta2 is able to utilize winbindd as the means of
+allocating uids and gids to trusted users and groups. More
+information regarding Samba's support for establishing trust
+relationships can be found in the Samba-HOWTO-Collection included
+in the docs/ directory of this release.
+
+First create your Samba PDC and ensure that everything is
+working correctly before moving on the trusts.
+
+To establish Samba as the trusting domain (named SAMBA) from a Windows NT
+4.0 domain named WINDOWS:
+
+ 1) create the trust account for SAMBA in "User Manager for Domains"
+ 2) connect the trust from the Samba domain using
+ 'net rpc trustdom establish GLASS'
+
+To create a trustlationship with SAMBA as the trusted domain:
+
+ 1) create the initial trust account for GLASS using
+ 'smbpasswd -a -i GLASS'. You may need to create a UNIX
+ account for GLASS$ prior to this step (depending on your
+ local configuration).
+ 2) connect the trust from a WINDOWS DC using "User Manager
+ for Domains"
+
+Now join winbindd on the Samba PDC to the SAMBA domain using
+the normal steps for adding a Samba server to an NT4 domain:
+(note that smbd & nmbd must be running at this point)
+
+ root# net rpc join -U root
+ Password: <enter root password from smbpasswd file here>
+
+Start winbindd and test the join with 'wbinfo -t'.
+
+Now test the trust relationship by connecting to the SAMBA DC
+(e.g. POGO) as a user from the WINDOWS domain:
+
+ $ smbclient //pogo/netlogon -U Administrator -W WINDOWS
+ Password:
+
+Now connect to the WINDOWS DC (e.g. CRYSTAL) as a Samba user:
+
+ $ smbclient //crystal/netlogon -U root -W WINDOWS
+ Password:
+
+######################################################################
+Changes in Winbind
+##################
+
+Beginning with Samba3.0.0beta3, winbindd has been given new account
+manage functionality equivalent to the 'add user script' family of
+smb.conf parameters. The idmap design has also been changed to
+centralize control of foreign SID lookups and matching to UNIX
+uids and gids.
+
+
+Brief Description of Changes
+----------------------------
+
+1) The sid_to_uid() family of functions (smbd/uid.c) have been
+ reverted to the 2.2.x design. This means that when resolving a
+ SID to a UID or similar mapping:
+
+ a) First consult winbindd
+ b) perform a local lookup only if winbindd fails to
+ return a successful answer
+
+ There are some variations to this, but these two rules generally
+ apply.
+
+2) All idmap lookups have been moved into winbindd. This means that
+ a server must run winbindd (and support NSS) in order to achieve
+ any mappings of SID to dynamically allocated UNIX ids. This was
+ a conscious design choice.
+
+3) New functions have been added to winbindd to emulate the 'add user
+ script' family of smbd functions without requiring that external
+ scripts be defined. This functionality is controlled by the 'winbind
+ enable local accounts' smb.conf parameter (enabled by default).
+
+ However, this account management functionality is only supported
+ in a local tdb (winbindd_idmap.tdb). If these new UNIX accounts
+ must be shared among multiple Samba servers (such as a PDC and BDCs),
+ it will be necessary to define your own 'add user script', et. al.
+ programs that place the accounts/groups in some form of directory
+ such as NIS or LDAP. This requirement was deemed beyond the scope
+ of winbind's account management functions. Solutions for
+ distributing UNIX system information have been deployed and tested
+ for many years. We saw no need to reinvent the wheel.
+
+4) A member of a Samba controlled domain running winbindd is now able
+ to map domain users directly onto existing UNIX accounts while still
+ automatically creating accounts for trusted users and groups. This
+ behavior is controlled by the 'winbind trusted domains only' smb.conf
+ parameter (disabled by default to provide 2.2.x winbind behavior).
+
+5) Group mapping support is wrapped in the local_XX_to_XX() functions
+ in smbd/uid.c. The reason that group mappings are not included
+ in winbindd is because the purpose of Samba's group map is to
+ match any Windows SID with an existing UNIX group. These UNIX
+ groups can be created by winbindd (see next section), but the
+ SID<->gid mapping is retreived by smbd, not winbindd.
+
+
+Examples
+--------
+
+* security = server running winbindd to allocate accounts on demand
+
+* Samba PDC running winbindd to handle the automatic creation of UNIX
+ identities for machine trust accounts
+
+* Automtically creating UNIX user and groups when migrating a Windows NT
+ 4.0 PDC to a Samba PDC. Winbindd must be running when executing
+ 'net rpc vampire' for this to work.
+
+
+######################################################################
+Known Issues
+############
+
+* There are several bugs currently logged against the 3.0 codebase
+ that affect the use of NT 4.0 GUI domain management tools when run
+ against a Samba 3.0 PDC. This bugs should be released in an early
+ 3.0.x release.
+
+Please refer to https://bugzilla.samba.org/ for a current list of bugs
+filed against the Samba 3.0 codebase.
+
+
+######################################################################
+Reporting bugs & Development Discussion
+#######################################
+
+Please discuss this release on the samba-technical mailing list or by
+joining the #samba-technical IRC channel on irc.freenode.net.
+
+If you do report problems then please try to send high quality
+feedback. If you don't provide vital information to help us track down
+the problem then you will probably be ignored.
+
+A new bugzilla installation has been established to help support the
+Samba 3.0 community of users. This server, located at
+https://bugzilla.samba.org/, has replaced the older jitterbug server
+previously located at http://bugs.samba.org/.
+
diff --git a/docs/README-NOW b/docs/README-NOW
new file mode 100644
index 00000000000..46a772385cc
--- /dev/null
+++ b/docs/README-NOW
@@ -0,0 +1,10 @@
+ ATTENTION
+ DOCS TREE REMOVED
+---------------------------------------------------
+
+This docs tree has been moved to a separate CVS
+module on cvs.samba.org name 'samba-docs'.
+See http://cvs.samba.org/ for details on accessing
+Samba cvs trees.
+
+
diff --git a/examples/LDAP/README b/examples/LDAP/README
new file mode 100644
index 00000000000..2f4b4f2a056
--- /dev/null
+++ b/examples/LDAP/README
@@ -0,0 +1,74 @@
+!==
+!== README File for various LDAP examples
+!==
+!== written by Gerald Carter <jerry@samba.org>
+!==
+
+OpenLDAP 2.x
+------------
+
+A sample schema file (samba.schema) has been included for use
+with OpenLDAP 2.0.x. The OIDs used in this file are owned by
+the Samba team and generated from its own Enterprise number
+of 7165 (as issued by IANA).
+
+Copy the samba.schema file into your /etc/openldap/schema directory,
+and add an include for it in the /etc/openldap/slapd.conf file.
+Note that samba.schema relies upon the uid and uidNumber attributes
+from the RFC2307 schema (i.e. nis.schema)
+
+If you choose to import /etc/passwd, nis, or nisplus tables
+into ldap, you can use migration tools provided by PADL Software
+which are located at
+
+ http://www.padl.com/tools.html
+
+It is not a requirement that a user's /etc/passwd account
+is stored in LDAP for the samba.schema file to work (although
+the whole point of storing smbpasswd in LDAP is to have a
+single location for user accounts, right?)
+
+The padl tools will leave you with LDIF files which you can import
+into OpenLDAP. Before you can import them, you need to include
+nis.schema and cosine.schema in your slapd.conf file.
+
+You must restart the LDAP server for these new included schema files
+to become active.
+
+SunOne/Netscape DS
+------------------
+
+The schema file has not been updated for the sambaSamAccount
+objectclass.
+
+
+Novell eDirectory
+-----------------
+
+The schema file has not been updated for the sambaSamAccount
+objectclass.
+
+
+smbldap-tools/
+--------------
+
+This is a collection of perl scripts (wrapped around the standard
+OpenLDAP command line tools) for managing Samba and posix accounts
+in an LDAP directory. See the README file included with the scripts
+for more details.
+
+
+ldapsync.pl
+-----------
+For more information on these scripts, see
+
+ http://www.mami.net/univr/tng-ldap/howto/
+
+
+The ldapsync.pl script requires a small command (smbencrypt)
+for generating LanMan and NT password hashes which
+can be found at ftp://samba.org/pub/samba/contributed/
+
+!==
+!== end of README
+!==
diff --git a/examples/LDAP/convertSambaAccount b/examples/LDAP/convertSambaAccount
new file mode 100755
index 00000000000..4357dbc8f8d
--- /dev/null
+++ b/examples/LDAP/convertSambaAccount
@@ -0,0 +1,233 @@
+#!/usr/bin/perl -w
+##
+## Convert an LDIF file containing sambaAccount entries
+## to the new sambaSamAccount objectclass
+##
+## Copyright Gerald (Jerry) Carter 2003
+##
+## Usage: convertSambaAccount --sid=<Domain SID> \
+## --input=<input ldif> --output=<output ldif> \
+## --changetype=[modify|add]
+##
+## You can generate an input ldif file using:
+## $ ldapsearch -LL -x -h ldapsrv -D cn=root,dc=company,dc=com \
+## -b dc=copmany,dc=com > /tmp/samba3.alpha23.ldif
+##
+## Note the "-LL" so no additional comments are generated
+##
+
+
+use strict;
+use Net::LDAP::LDIF;
+use Getopt::Long;
+
+
+##############################################################################
+## local variables
+
+my ( $domain, $domsid, $changetype );
+my ( $ldif, $ldif2 );
+my ( $entry, @objclasses, $obj );
+my ( $is_samba_account, $is_samba_group );
+my ( %attr_map, %group_attr_map, $key );
+my ( @dels, $deletion, @adds, $addition );
+my ( $result, %options );
+
+
+##############################################################################
+## Print the option usage
+
+sub usage {
+
+ print "convertSambaAccount <options>\n";
+ print "Options:\n";
+ print " --help print this help message\n";
+ print " --input input LDIF filename\n";
+ print " --output output LDIF filename\n";
+ print " --sid domain SID\n";
+ print " --changetype [modify|add] (default is 'add')\n";
+}
+
+
+##############################################################################
+## MAIN DRIVER ##
+##############################################################################
+
+##
+## hashes to map old attribute names to new ones
+##
+
+%attr_map = (
+ lmPassword => 'sambaLMPassword',
+ ntPassword => 'sambaNTPassword',
+ pwdLastSet => 'sambaPwdLastSet',
+ pwdMustChange => 'sambaPwdMustChange',
+ pwdCanChange => 'sambaPwdCanChange',
+ homeDrive => 'sambaHomeDrive',
+ smbHome => 'sambaHomePath',
+ scriptPath => 'sambaLogonScript',
+ profilePath => 'sambaProfilePath',
+ kickoffTime => 'sambaKickoffTime',
+ logonTime => 'sambaLogonTime',
+ logoffTime => 'sambaLogoffTime',
+ userWorkstations => 'sambaUserWorkstations',
+ domain => 'sambaDomainName',
+ acctFlags => 'sambaAcctFlags',
+);
+
+%group_attr_map = (
+ ntSid => 'sambaSID',
+ ntGroupType => 'sambaGroupType',
+);
+
+##
+## process command line args
+##
+
+$result = GetOptions(\%options,
+ "help",
+ "input=s",
+ "output=s",
+ "sid=s",
+ "changetype=s");
+
+if (!$result && ($#ARGV != -1)) {
+ usage();
+ exit 1;
+}
+
+if ( defined($options{'help'}) ) {
+ usage();
+ exit 0;
+}
+
+
+if ( !defined( $options{'sid'} ) ) {
+ print "You must provide a domain sid\n";
+ exit 1;
+}
+
+$domsid = $options{'sid'};
+
+$changetype = 'add';
+if ( defined( $options{'changetype'} ) ) {
+ $changetype = $options{'changetype'};
+}
+
+##
+## open files
+##
+
+$ldif = Net::LDAP::LDIF->new ($options{'input'}, "r") or die $!;
+
+if ( "$changetype" eq "add" ) {
+ $ldif2 = Net::LDAP::LDIF->new ($options{'output'}, "w") or die $!;
+}
+elsif ( "$changetype" eq "modify" ) {
+ open( OUTPUT, ">$options{'output'}" ) or die $!;
+}
+else {
+ print "Bad changetype!\n";
+ exit 1;
+}
+
+##
+## process LDIF
+##
+
+while ( !$ldif->eof ) {
+ undef ( $entry );
+ $entry = $ldif->read_entry();
+
+ ## skip entry if we find an error
+ if ( $ldif->error() ) {
+ print "Error msg: ",$ldif->error(),"\n";
+ print "Error lines:\n",$ldif->error_lines(),"\n";
+ next;
+ }
+
+ ##
+ ## check to see if we have anything to do on this
+ ## entry. If not just write it out
+ ##
+ @objclasses = $entry->get_value( "objectClass" );
+ undef ( $is_samba_account );
+ undef ( $is_samba_group );
+ @adds = ();
+ @dels = ();
+ foreach $obj ( @objclasses ) {
+ if ( "$obj" eq "sambaAccount" ) {
+ $is_samba_account = 1;
+ } elsif ( "$obj" eq "sambaGroupMapping" ) {
+ $is_samba_group = 1;
+ }
+ }
+
+ if ( defined ( $is_samba_account ) ) {
+ ##
+ ## start editing the sambaAccount
+ ##
+
+ @dels = ( 'objectclass: sambaAccount', 'rid' );
+ @adds = ('objectclass: sambaSamAccount', "sambaSID: " . ${domsid} . "-" . ${entry}->get_value( 'rid' ) );
+ $entry->delete( 'objectclass' => [ 'sambaAccount' ] );
+ $entry->add( 'objectclass' => 'sambaSamAccount' );
+
+ $entry->add( 'sambaSID' => $domsid."-".$entry->get_value( "rid" ) );
+ $entry->delete( 'rid' );
+
+ if ( defined($entry->get_value( "primaryGroupID" )) ) {
+ push @adds, "sambaPrimaryGroupSID: " . $domsid."-".$entry->get_value( "primaryGroupID" );
+ push @dels, "primaryGroupID";
+ $entry->add( 'sambaPrimaryGroupSID' => $domsid."-".$entry->get_value( "primaryGroupID" ) );
+ $entry->delete( 'primaryGroupID' );
+ }
+
+
+ foreach $key ( keys %attr_map ) {
+ if ( defined($entry->get_value($key)) ) {
+ push @adds, "$attr_map{$key}: " . $entry->get_value($key);
+ push @dels, "$key";
+ $entry->add( $attr_map{$key} => $entry->get_value($key) );
+ $entry->delete( $key );
+ }
+ }
+ } elsif ( defined ( $is_samba_group ) ) {
+ foreach $key ( keys %group_attr_map ) {
+ if ( defined($entry->get_value($key)) ) {
+ push @adds, "$group_attr_map{$key}: " . $entry->get_value($key);
+ push @dels, "$key";
+ $entry->add( $group_attr_map{$key} => $entry->get_value($key) );
+ $entry->delete( $key );
+ }
+ }
+ }
+
+ ## see if we should write full entries or only the changes
+
+ if ( "$changetype" eq "add" ) {
+ $ldif2->write_entry( $entry );
+ }
+ else {
+ if ( defined ( $is_samba_account ) || defined ( $is_samba_group ) ){
+ if ( @adds + @dels > 0 ) {
+ print OUTPUT "dn: " . $entry->dn . "\n";
+ foreach $addition (@adds) {
+ $addition =~ /(^\w+):/;
+ print OUTPUT "add: " . $1 . "\n";
+ print OUTPUT "$addition\n-\n";
+ }
+ foreach $deletion (@dels) {
+ if ( $deletion =~ /^(\w+):\s(.*)/ ) {
+ print OUTPUT "delete: $1\n$1: $2\n-\n";
+ } else {
+ print OUTPUT "delete: $deletion\n-\n"
+ }
+ }
+ print OUTPUT "\n"
+ }
+ }
+ }
+}
+
+
diff --git a/examples/LDAP/ldapsync.pl b/examples/LDAP/ldapsync.pl
new file mode 100644
index 00000000000..c112bcc34cb
--- /dev/null
+++ b/examples/LDAP/ldapsync.pl
@@ -0,0 +1,122 @@
+#!/usr/bin/perl -w
+
+# LDAP to unix password sync script for samba-tng
+# originally by Jody Haynes <Jody.Haynes@isunnetworks.com>
+# 12/12/2000 milos@interactivesi.com
+# modified for use with MD5 passwords
+# 12/16/2000 mami@arena.sci.univr.it
+# modified to change lmpassword and ntpassword for samba
+# 05/01/2001 mami@arena.sci.univr.it
+# modified for being also a /bin/passwd replacement
+#
+# ACHTUNG!! For servers that support the LDAP Modify password
+# extended op (e.g. OpenLDAP), see the "ldap password
+# sync" option in smb.conf(5).
+#
+
+$basedn = "ou=Students,dc=univr, dc=it";
+$binddn = "uid=root,dc=univr,dc=it";
+$scope = "sub";
+$passwd = "mysecret";
+
+foreach $arg (@ARGV) {
+ if ($< != 0) {
+ die "Only root can specify parameters\n";
+ } else {
+ if ( ($arg eq '-?') || ($arg eq '--help') ) {
+ print "Usage: $0 [-o] [username]\n";
+ print " -o, --without-old-password do not ask for old password (root only)\n";
+ print " -?, --help show this help message\n";
+ exit (-1);
+ } elsif ( ($arg eq '-o') || ($arg eq '--without-old-password') ) {
+ $oldpass = 1;
+ } elsif (substr($arg,0) ne '-') {
+ $user = $arg;
+ if (!defined(getpwnam($user))) {
+ die "$0: Unknown user name '$user'\n"; ;
+ }
+ }
+ }
+}
+
+if (!defined($user)) {
+ $user=$ENV{"USER"};
+}
+
+if (!defined($oldpass)) {
+ system "stty -echo";
+ print "Old password for user $user: ";
+ chomp($oldpass=<STDIN>);
+ print "\n";
+ system "stty echo";
+
+ $ntpwd = `/usr/local/sbin/smbencrypt '$oldpass'`;
+ $lmpassword = substr($ntpwd, 0, index($ntpwd, ':')); chomp $lmpassword;
+ $ntpassword = substr($ntpwd, index($ntpwd, ':')+1); chomp $ntpassword;
+
+ # Find dn for user $user (maybe check unix password too?)
+ $dn=`ldapsearch -b '$basedn' -s '$scope' '(&(uid=$user)(lmpassword=$lmpassword)(ntpassword=$ntpassword))'|head -1`;
+ chomp $dn;
+
+ if ($dn eq '') {
+ print "Wrong password for user $user!\n";
+ exit (-1);
+ }
+} else {
+ # Find dn for user $user
+ $dn=`ldapsearch -b '$basedn' -s '$scope' '(uid=$user)'|head -1`;
+ chomp $dn;
+}
+
+system "stty -echo";
+print "New password for user $user: ";
+chomp($pass=<STDIN>);
+print "\n";
+system "stty echo";
+
+system "stty -echo";
+print "Retype new password for user $user: ";
+chomp($pass2=<STDIN>);
+print "\n";
+system "stty echo";
+
+if ($pass ne $pass2) {
+ die "Wrong password!\n";
+} else {
+# MD5 password
+$random = join '', ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64, rand 64, rand 64, rand 64, rand 64, rand 64, rand 64];
+$bsalt = "\$1\$"; $esalt = "\$";
+$modsalt = $bsalt.$random.$esalt;
+$password = crypt($pass, $modsalt);
+
+# LanManager and NT clear text passwords
+$ntpwd = `/usr/local/sbin/smbencrypt '$pass'`;
+chomp($lmpassword = substr($ntpwd, 0, index($ntpwd, ':')));
+chomp($ntpassword = substr($ntpwd, index($ntpwd, ':')+1));
+
+$FILE="|/usr/bin/ldapmodify -D '$binddn' -w $passwd";
+
+open FILE or die;
+
+print FILE <<EOF;
+dn: $dn
+changetype: modify
+replace: userPassword
+userPassword: {crypt}$password
+-
+changetype: modify
+replace: lmpassword
+lmpassword: $lmpassword
+-
+changetype: modify
+replace: ntpassword
+ntpassword: $ntpassword
+-
+
+EOF
+close FILE;
+
+}
+
+exit 0;
+
diff --git a/examples/LDAP/samba-nds.schema b/examples/LDAP/samba-nds.schema
new file mode 100644
index 00000000000..8369c8404ec
--- /dev/null
+++ b/examples/LDAP/samba-nds.schema
@@ -0,0 +1,151 @@
+##
+## Schema file for Novell eDirectory 8.7.x by Uli Iske
+## Schema for storing Samba's smbpasswd file in LDAP
+## OIDs are owned by the Samba Team
+##
+#######################################################################
+## Attributes used by Samba 3.0 schema ##
+#######################################################################
+
+dn: cn=schema
+changetype: modify
+add: attributetypes
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.24 NAME 'sambaLMPassword' DESC 'LanManager Password' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+dn: cn=schema
+changetype: modify
+add: attributetypes
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.25 NAME 'sambaNTPassword' DESC 'MD4 hash of the unicode password' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+dn: cn=schema
+changetype: modify
+add: attributetypes
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.26 NAME 'sambaAcctFlags' DESC 'Account Flags' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+dn: cn=schema
+changetype: modify
+add: attributetypes
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.27 NAME 'sambaPwdLastSet' DESC 'Timestamp of the last password update' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+dn: cn=schema
+changetype: modify
+add: attributetypes
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.28 NAME 'sambaPwdCanChange' DESC 'Timestamp of when the user is allowed to update the password' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+dn: cn=schema
+changetype: modify
+add: attributetypes
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.29 NAME 'sambaPwdMustChange' DESC 'Timestamp of when the password will expire' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+dn: cn=schema
+changetype: modify
+add: attributetypes
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.30 NAME 'sambaLogonTime' DESC 'Timestamp of last logon' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+dn: cn=schema
+changetype: modify
+add: attributetypes
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.31 NAME 'sambaLogoffTime' DESC 'Timestamp of last logoff' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+dn: cn=schema
+changetype: modify
+add: attributetypes
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.32 NAME 'sambaKickoffTime' DESC 'Timestamp of when the user will be logged off automatically' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+dn: cn=schema
+changetype: modify
+add: attributetypes
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.33 NAME 'sambaHomeDrive' DESC 'Driver letter of home directory mapping' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+dn: cn=schema
+changetype: modify
+add: attributetypes
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.34 NAME 'sambaLogonScript' DESC 'Logon script path' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
+
+dn: cn=schema
+changetype: modify
+add: attributetypes
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.35 NAME 'sambaProfilePath' DESC 'Roaming profile path' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
+
+dn: cn=schema
+changetype: modify
+add: attributetypes
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.36 NAME 'sambaUserWorkstations' DESC 'List of user workstations the user is allowed to logon to' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
+
+dn: cn=schema
+changetype: modify
+add: attributetypes
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.37 NAME 'sambaHomePath' DESC 'Home directory UNC path' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+dn: cn=schema
+changetype: modify
+add: attributetypes
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.38 NAME 'sambaDomainName' DESC 'Windows NT domain to which the user belongs' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+dn: cn=schema
+changetype: modify
+add: attributetypes
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.20 NAME 'sambaSID' DESC 'Security ID' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+dn: cn=schema
+changetype: modify
+add: attributetypes
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.23 NAME 'sambaPrimaryGroupSID' DESC 'Primary Group Security ID' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+dn: cn=schema
+changetype: modify
+add: attributetypes
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.19 NAME 'sambaGroupType' DESC 'NT Group Type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+dn: cn=schema
+changetype: modify
+add: attributetypes
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.21 NAME 'sambaNextUserRid' DESC 'Next NT rid to give our for users' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+dn: cn=schema
+changetype: modify
+add: attributetypes
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.22 NAME 'sambaNextGroupRid' DESC 'Next NT rid to give out for groups' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+dn: cn=schema
+changetype: modify
+add: attributetypes
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.39 NAME 'sambaNextRid' DESC 'Next NT rid to give out for anything' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+dn: cn=schema
+changetype: modify
+add: attributetypes
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.40 NAME 'sambaAlgorithmicRidBase' DESC 'Base at which the samba RID generation algorithm should operate' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+#######################################################################
+## objectClasses used by Samba 3.0 schema ##
+#######################################################################
+dn: cn=schema
+changetype: modify
+add: objectClasses
+objectClasses: ( 1.3.6.1.4.1.7165.2.2.6 NAME 'sambaSamAccount' DESC 'Samba 3.0 Auxilary SAM Account' SUP top AUXILIARY MUST ( uid $ sambaSID ) MAY ( cn $ sambaLMPassword $ sambaNTPassword $ sambaPwdLastSet $sambaLogonTime $ sambaLogoffTime $sambaKickoffTime $sambaPwdCanChange $ sambaPwdMustChange $ sambaAcctFlags $displayName $ sambaHomePath $ sambaHomeDrive $ sambaLogonScript $sambaProfilePath $ description $ sambaUserWorkstations $sambaPrimaryGroupSID $ sambaDomainName ))
+
+dn: cn=schema
+changetype: modify
+add: objectClasses
+objectClasses: ( 1.3.6.1.4.1.7165.2.2.4 NAME 'sambaGroupMapping' DESC 'Samba Group Mapping' SUP top AUXILIARY MUST ( gidNumber $ sambaSID $ sambaGroupType ) MAY ( displayName $ description ))
+
+dn: cn=schema
+changetype: modify
+add: objectClasses
+objectClasses: ( 1.3.6.1.4.1.7165.2.2.5 NAME 'sambaDomain' DESC 'Samba Domain Information' SUP top STRUCTURAL MUST ( sambaDomainName $sambaSID ) MAY ( sambaNextRid $ sambaNextGroupRid $ sambaNextUserRid $sambaAlgorithmicRidBase ) )
+
+dn: cn=schema
+changetype: modify
+add: objectClasses
+objectClasses: ( 1.3.6.1.4.1.7165.1.2.2.7 NAME 'sambaUnixIdPool' DESC 'Pool for allocating UNIX uids/gids' SUP top AUXILIARY MUST ( uidNumber $ gidNumber ) )
+
+dn: cn=schema
+changetype: modify
+add: objectClasses
+objectClasses: ( 1.3.6.1.4.1.7165.1.2.2.8 NAME 'sambaIdmapEntry' DESC 'Mapping from a SID to an ID' SUP top AUXILIARY MUST ( sambaSID ) MAY ( uidNumber $ gidNumber ) )
+
+dn: cn=schema
+changetype: modify
+add: objectClasses
+objectClasses: ( 1.3.6.1.4.1.7165.1.2.2.9 NAME 'sambaSidEntry' DESC 'Structural Class for a SID' SUP top STRUCTURAL MUST ( sambaSID ) )
diff --git a/examples/LDAP/samba-schema-netscapeds4.x b/examples/LDAP/samba-schema-netscapeds4.x
new file mode 100644
index 00000000000..9f409664187
--- /dev/null
+++ b/examples/LDAP/samba-schema-netscapeds4.x
@@ -0,0 +1,112 @@
+#
+# LDAP Schema file for SAMBA 3.0 attribute storage
+# For Netscape Directory Server 4.1x
+# Prepared by Osman Demirhan
+
+attribute sambaLMPassword 1.3.6.1.4.1.7165.2.1.24 cis single
+attribute sambaNTPassword 1.3.6.1.4.1.7165.2.1.25 cis single
+attribute sambaAcctFlags 1.3.6.1.4.1.7165.2.1.26 cis single
+attribute sambaPwdLastSet 1.3.6.1.4.1.7165.2.1.27 int single
+attribute sambaPwdCanChange 1.3.6.1.4.1.7165.2.1.28 int single
+attribute sambaPwdMustChange 1.3.6.1.4.1.7165.2.1.29 int single
+attribute sambaLogonTime 1.3.6.1.4.1.7165.2.1.30 int single
+attribute sambaLogoffTime 1.3.6.1.4.1.7165.2.1.31 int single
+attribute sambaKickoffTime 1.3.6.1.4.1.7165.2.1.32 int single
+attribute sambaHomeDrive 1.3.6.1.4.1.7165.2.1.33 cis single
+attribute sambaLogonScript 1.3.6.1.4.1.7165.2.1.34 cis single
+attribute sambaProfilePath 1.3.6.1.4.1.7165.2.1.35 cis single
+attribute sambaUserWorkstations 1.3.6.1.4.1.7165.2.1.36 cis single
+attribute sambaHomePath 1.3.6.1.4.1.7165.2.1.37 cis single
+attribute sambaDomainName 1.3.6.1.4.1.7165.2.1.38 cis single
+attribute sambaSID 1.3.6.1.4.1.7165.2.1.20 cis single
+attribute sambaPrimaryGroupSID 1.3.6.1.4.1.7165.2.1.23 cis single
+attribute sambaGroupType 1.3.6.1.4.1.7165.2.1.19 int single
+attribute sambaNextUserRid 1.3.6.1.4.1.7165.2.1.21 int single
+attribute sambaNextGroupRid 1.3.6.1.4.1.7165.2.1.22 int single
+attribute sambaNextRid 1.3.6.1.4.1.7165.2.1.39 int single
+attribute sambaAlgorithmicRidBase 1.3.6.1.4.1.7165.2.1.40 int single
+
+objectclass sambaSamAccount
+ oid
+ 1.3.6.1.4.1.7165.2.2.6
+ superior
+ top
+ requires
+ objectClass,
+ uid,
+ sambaSID
+ allows
+ cn,
+ sambaLMPassword,
+ sambaNTPassword,
+ sambaPwdLastSet,
+ sambaLogonTime,
+ sambaLogoffTime,
+ sambaKickoffTime,
+ sambaPwdCanChange,
+ sambaPwdMustChange,
+ sambaAcctFlags,
+ displayName,
+ sambaHomePath,
+ sambaHomeDrive,
+ sambaLogonScript,
+ sambaProfilePath,
+ description,
+ sambaUserWorkstations,
+ sambaPrimaryGroupSID,
+ sambaDomainName
+
+objectclass sambaGroupMapping
+ oid
+ 1.3.6.1.4.1.7165.2.2.4
+ superior
+ top
+ requires
+ gidNumber,
+ sambaSID,
+ sambaGroupType
+ allows
+ displayName,
+ description
+
+objectclass sambaDomain
+ oid
+ 1.3.6.1.4.1.7165.2.2.5
+ superior
+ top
+ requires
+ sambaDomainName,
+ sambaSID
+ allows
+ sambaNextRid,
+ sambaNextGroupRid,
+ sambaNextUserRid,
+ sambaAlgorithmicRidBase
+
+objectclass sambaUnixIdPool
+ oid
+ 1.3.6.1.4.1.7165.1.2.2.7
+ superior
+ top
+ requires
+ uidNumber,
+ gidNumber
+
+objectclass sambaIdmapEntry
+ oid
+ 1.3.6.1.4.1.7165.1.2.2.8
+ superior
+ top
+ requires
+ sambaSID
+ allows
+ uidNumber,
+ gidNumber
+
+objectclass sambaSidEntry
+ oid
+ 1.3.6.1.4.1.7165.1.2.2.9
+ superior
+ top
+ requires
+ sambaSID
diff --git a/examples/LDAP/samba-schema-netscapeds5.x b/examples/LDAP/samba-schema-netscapeds5.x
new file mode 100644
index 00000000000..e750039acbf
--- /dev/null
+++ b/examples/LDAP/samba-schema-netscapeds5.x
@@ -0,0 +1,55 @@
+##
+## Darren Chew <darren.chew at vicscouts dot asn dot au>
+## Andre Fiebach <andre dot fiebach at stud dot uni-rostock dot de>
+##
+## Samba 3.0 schema file for Netscape DS 5.x
+##
+## INSTALL-DIRECTORY/slapd-your_name/config/schema/samba-schema-netscapeds5.ldif
+####################################################################
+# Sun One DS do not load the schema without this lines
+# André Fiebach <af123@uni-rostock.de>
+dn: cn=schema
+objectClass: top
+objectClass: ldapSubentry
+objectClass: subschema
+cn: schema
+aci: (target="ldap:///cn=schema")(targetattr !="aci")(version 3.0;acl "anonymo
+ us, no acis"; allow (read, search, compare) userdn = "ldap:///anyone";)
+aci: (targetattr = "*")(version 3.0; acl "Configuration Administrator"; allow
+ (all) userdn = "ldap:///uid=admin,ou=Administrators, ou=TopologyManagement,
+ o=NetscapeRoot";)
+aci: (targetattr = "*")(version 3.0; acl "Local Directory Administrators Group
+ "; allow (all) groupdn = "ldap:///cn=Directory Administrators, dc=samba,dc=org";)
+aci: (targetattr = "*")(version 3.0; acl "SIE Group"; allow (all)groupdn = "ld
+ ap:///cn=slapd-sambaldap, cn=iPlanet Directory Server, cn=Server Group, cn=iPlanetDirectory.samba.org, ou=samba.org, o=NetscapeRoot";)
+modifiersName: cn=directory manager
+modifyTimestamp: 20020322124844Z
+####################################################################
+objectClasses: ( 1.3.6.1.4.1.7165.2.2.6 NAME 'sambaSamAccount' SUP top AUXILIARY DESC 'Samba 3.0 Auxilary SAM Account' MUST ( uid $ sambaSID ) MAY ( cn $ sambaLMPassword $ sambaNTPassword $ sambaPwdLastSet $ sambaLogonTime $ sambaLogoffTime $ sambaKickoffTime $ sambaPwdCanChange $ sambaPwdMustChange $ sambaAcctFlags $ displayName $ sambaHomePath $ sambaHomeDrive $ sambaLogonScript $ sambaProfilePath $ description $ sambaUserWorkstations $ sambaPrimaryGroupSID $ sambaDomainName ) X-ORIGIN 'user defined' )
+objectClasses: ( 1.3.6.1.4.1.7165.2.2.4 NAME 'sambaGroupMapping' SUP top AUXILIARY DESC 'Samba Group Mapping' MUST ( gidNumber $ sambaSID $ sambaGroupType ) MAY ( displayName $ description ) X-ORIGIN 'user defined' )
+objectClasses: ( 1.3.6.1.4.1.7165.2.2.5 NAME 'sambaDomain' SUP top STRUCTURAL DESC 'Samba Domain Information' MUST ( sambaDomainName $ sambaSID ) MAY ( sambaNextRid $ sambaNextGroupRid $ sambaNextUserRid $ sambaAlgorithmicRidBase ) X-ORIGIN 'user defined' )
+objectClasses: ( 1.3.6.1.4.1.7165.1.2.2.7 NAME 'sambaUnixIdPool' SUP top AUXILIARY DESC 'Pool for allocating UNIX uids/gids' MUST ( uidNumber $ gidNumber ) X-ORIGIN 'user defined' )
+objectClasses: ( 1.3.6.1.4.1.7165.1.2.2.8 NAME 'sambaIdmapEntry' SUP top AUXILIARY DESC 'Mapping from a SID to an ID' MUST ( sambaSID ) MAY ( uidNumber $ gidNumber ) X-ORIGIN 'user defined' )
+objectClasses: ( 1.3.6.1.4.1.7165.1.2.2.9 NAME 'sambaSidEntry' SUP top STRUCTURAL DESC 'Structural Class for a SID' MUST ( sambaSID ) X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.24 NAME 'sambaLMPassword' DESC 'LanManager Password' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.25 NAME 'sambaNTPassword' DESC 'MD4 hash of the unicode password' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.26 NAME 'sambaAcctFlags' DESC 'Account Flags' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{16} SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.27 NAME 'sambaPwdLastSet' DESC 'Timestamp of the last password update' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.28 NAME 'sambaPwdCanChange' DESC 'Timestamp of when the user is allowed to update the password' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.29 NAME 'sambaPwdMustChange' DESC 'Timestamp of when the password will expire' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.30 NAME 'sambaLogonTime' DESC 'Timestamp of last logon' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.31 NAME 'sambaLogoffTime' DESC 'Timestamp of last logoff' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.32 NAME 'sambaKickoffTime' DESC 'Timestamp of when the user will be logged off automatically' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.33 NAME 'sambaHomeDrive' DESC 'Driver letter of home directory mapping' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{4} SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.34 NAME 'sambaLogonScript' DESC 'Logon script path' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255} SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.35 NAME 'sambaProfilePath' DESC 'Roaming profile path' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255} SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.36 NAME 'sambaUserWorkstations' DESC 'List of user workstations the user is allowed to logon to' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255} SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.37 NAME 'sambaHomePath' DESC 'Home directory UNC path' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.38 NAME 'sambaDomainName' DESC 'Windows NT domain to which the user belongs' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.20 NAME 'sambaSID' DESC 'Security ID' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{64} SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.23 NAME 'sambaPrimaryGroupSID' DESC 'Primary Group Security ID' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{64} SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.19 NAME 'sambaGroupType' DESC 'NT Group Type' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.21 NAME 'sambaNextUserRid' DESC 'Next NT rid to give our for users' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.22 NAME 'sambaNextGroupRid' DESC 'Next NT rid to give out for groups' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.39 NAME 'sambaNextRid' DESC 'Next NT rid to give out for anything' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.7165.2.1.40 NAME 'sambaAlgorithmicRidBase' DESC 'Base at which the samba RID generation algorithm should operate' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'user defined' )
diff --git a/examples/LDAP/samba-schema.IBMSecureWay b/examples/LDAP/samba-schema.IBMSecureWay
new file mode 100644
index 00000000000..1fca4a749a6
--- /dev/null
+++ b/examples/LDAP/samba-schema.IBMSecureWay
@@ -0,0 +1,43 @@
+##
+## Submitted by Dirk Kastens <Dirk.Kastens@Uni-Osnabrueck.de>
+##
+## I translated the samba.schema to be used with IBM
+## SecureWay directoy server 3.2.2. You have to load
+## it in your slapd32.conf with:
+##
+## dn: cn=IBM SecureWay, cn=Schemas, cn=Configuration
+## cn: IBM SecureWay
+## ibm-slapdIncludeSchema: /etc/lapschema/samba.schema
+##
+objectClasses {
+( 1.3.1.5.1.4.1.7165.2.2.2 NAME 'sambaAccount' DESC 'Samba Account' SUP top MUST uid $ rid MAY ( acctFlags $ cn $ description $ displayName $ homeDrive $ kickoffTime $ lmPassword $ logoffTime $ logonTime $ ntPassword $ primaryGroupID $ profilePath $ pwdCanChange $ pwdLastSet $ pwdMustChange $ scriptPath $ smbHome $ userWorkstations ) )
+}
+
+attributeTypes {
+( 1.3.6.1.4.1.7165.2.1.1 NAME 'lmPassword' DESC 'LanManager Passwd' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE )
+( 1.3.6.1.4.1.7165.2.1.10 NAME 'homeDrive' DESC 'NT homeDrive' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{4} SINGLE-VALUE )
+( 1.3.6.1.4.1.7165.2.1.11 NAME 'scriptPath' DESC 'NT scriptPath' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{255} SINGLE-VALUE )
+( 1.3.6.1.4.1.7165.2.1.12 NAME 'profilePath' DESC 'NT profilePath' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{255} SINGLE-VALUE )
+( 1.3.6.1.4.1.7165.2.1.13 NAME 'userWorkstations' DESC 'userWorkstations' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{255} SINGLE-VALUE )
+( 1.3.6.1.4.1.7165.2.1.14 NAME 'rid' DESC 'NT rid' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{255} SINGLE-VALUE )
+( 1.3.6.1.4.1.7165.2.1.15 NAME 'primaryGroupID' DESC 'NT Group RID' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{255} SINGLE-VALUE )
+( 1.3.6.1.4.1.7165.2.1.17 NAME 'smbHome' DESC 'smbHome' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} )
+( 1.3.6.1.4.1.7165.2.1.2 NAME 'ntPassword' DESC 'NT Passwd' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE )
+( 1.3.6.1.4.1.7165.2.1.3 NAME 'pwdLastSet' DESC 'NT pwdLastSet' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE )
+( 1.3.6.1.4.1.7165.2.1.4 NAME 'acctFlags' DESC 'Account Flags' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{16} SINGLE-VALUE )
+( 1.3.6.1.4.1.7165.2.1.5 NAME 'logonTime' DESC 'NT logonTime' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE )
+( 1.3.6.1.4.1.7165.2.1.6 NAME 'logoffTime' DESC 'NT logoffTime' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE )
+( 1.3.6.1.4.1.7165.2.1.7 NAME 'kickoffTime' DESC 'NT kickoffTime' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE )
+( 1.3.6.1.4.1.7165.2.1.8 NAME 'pwdCanChange' DESC 'NT pwdCanChange' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE )
+( 1.3.6.1.4.1.7165.2.1.9 NAME 'pwdMustChange' DESC 'NT pwdMustChange' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE )
+}
+
+IBMattributeTypes {
+}
+
+ldapSyntaxes {
+}
+
+matchingRules {
+}
+
diff --git a/examples/LDAP/samba.schema b/examples/LDAP/samba.schema
new file mode 100644
index 00000000000..7d7fe346b2b
--- /dev/null
+++ b/examples/LDAP/samba.schema
@@ -0,0 +1,424 @@
+##
+## schema file for OpenLDAP 2.x
+## Schema for storing Samba user accounts and group maps in LDAP
+## OIDs are owned by the Samba Team
+##
+## Prerequisite schemas - uid (cosine.schema)
+## - displayName (inetorgperson.schema)
+## - gidNumber (nis.schema)
+##
+## 1.3.6.1.4.1.7165.2.1.x - attributetypes
+## 1.3.6.1.4.1.7165.2.2.x - objectclasses
+##
+
+########################################################################
+## HISTORICAL ##
+########################################################################
+
+##
+## Password hashes
+##
+#attributetype ( 1.3.6.1.4.1.7165.2.1.1 NAME 'lmPassword'
+# DESC 'LanManager Passwd'
+# EQUALITY caseIgnoreIA5Match
+# SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE )
+
+#attributetype ( 1.3.6.1.4.1.7165.2.1.2 NAME 'ntPassword'
+# DESC 'NT Passwd'
+# EQUALITY caseIgnoreIA5Match
+# SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE )
+
+##
+## Account flags in string format ([UWDX ])
+##
+#attributetype ( 1.3.6.1.4.1.7165.2.1.4 NAME 'acctFlags'
+# DESC 'Account Flags'
+# EQUALITY caseIgnoreIA5Match
+# SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{16} SINGLE-VALUE )
+
+##
+## Password timestamps & policies
+##
+#attributetype ( 1.3.6.1.4.1.7165.2.1.3 NAME 'pwdLastSet'
+# DESC 'NT pwdLastSet'
+# EQUALITY integerMatch
+# SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+#attributetype ( 1.3.6.1.4.1.7165.2.1.5 NAME 'logonTime'
+# DESC 'NT logonTime'
+# EQUALITY integerMatch
+# SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+#attributetype ( 1.3.6.1.4.1.7165.2.1.6 NAME 'logoffTime'
+# DESC 'NT logoffTime'
+# EQUALITY integerMatch
+# SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+#attributetype ( 1.3.6.1.4.1.7165.2.1.7 NAME 'kickoffTime'
+# DESC 'NT kickoffTime'
+# EQUALITY integerMatch
+# SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+#attributetype ( 1.3.6.1.4.1.7165.2.1.8 NAME 'pwdCanChange'
+# DESC 'NT pwdCanChange'
+# EQUALITY integerMatch
+# SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+#attributetype ( 1.3.6.1.4.1.7165.2.1.9 NAME 'pwdMustChange'
+# DESC 'NT pwdMustChange'
+# EQUALITY integerMatch
+# SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+##
+## string settings
+##
+#attributetype ( 1.3.6.1.4.1.7165.2.1.10 NAME 'homeDrive'
+# DESC 'NT homeDrive'
+# EQUALITY caseIgnoreIA5Match
+# SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{4} SINGLE-VALUE )
+
+#attributetype ( 1.3.6.1.4.1.7165.2.1.11 NAME 'scriptPath'
+# DESC 'NT scriptPath'
+# EQUALITY caseIgnoreIA5Match
+# SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{255} SINGLE-VALUE )
+
+#attributetype ( 1.3.6.1.4.1.7165.2.1.12 NAME 'profilePath'
+# DESC 'NT profilePath'
+# EQUALITY caseIgnoreIA5Match
+# SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{255} SINGLE-VALUE )
+
+#attributetype ( 1.3.6.1.4.1.7165.2.1.13 NAME 'userWorkstations'
+# DESC 'userWorkstations'
+# EQUALITY caseIgnoreIA5Match
+# SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{255} SINGLE-VALUE )
+
+#attributetype ( 1.3.6.1.4.1.7165.2.1.17 NAME 'smbHome'
+# DESC 'smbHome'
+# EQUALITY caseIgnoreIA5Match
+# SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} )
+
+#attributetype ( 1.3.6.1.4.1.7165.2.1.18 NAME 'domain'
+# DESC 'Windows NT domain to which the user belongs'
+# EQUALITY caseIgnoreIA5Match
+# SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} )
+
+##
+## user and group RID
+##
+#attributetype ( 1.3.6.1.4.1.7165.2.1.14 NAME 'rid'
+# DESC 'NT rid'
+# EQUALITY integerMatch
+# SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+#attributetype ( 1.3.6.1.4.1.7165.2.1.15 NAME 'primaryGroupID'
+# DESC 'NT Group RID'
+# EQUALITY integerMatch
+# SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+##
+## The smbPasswordEntry objectclass has been depreciated in favor of the
+## sambaAccount objectclass
+##
+#objectclass ( 1.3.6.1.4.1.7165.2.2.1 NAME 'smbPasswordEntry' SUP top AUXILIARY
+# DESC 'Samba smbpasswd entry'
+# MUST ( uid $ uidNumber )
+# MAY ( lmPassword $ ntPassword $ pwdLastSet $ acctFlags ))
+
+#objectclass ( 1.3.6.1.4.1.7165.2.2.2 NAME 'sambaAccount' SUP top STRUCTURAL
+# DESC 'Samba Account'
+# MUST ( uid $ rid )
+# MAY ( cn $ lmPassword $ ntPassword $ pwdLastSet $ logonTime $
+# logoffTime $ kickoffTime $ pwdCanChange $ pwdMustChange $ acctFlags $
+# displayName $ smbHome $ homeDrive $ scriptPath $ profilePath $
+# description $ userWorkstations $ primaryGroupID $ domain ))
+
+#objectclass ( 1.3.6.1.4.1.7165.2.2.3 NAME 'sambaAccount' SUP top AUXILIARY
+# DESC 'Samba Auxiliary Account'
+# MUST ( uid $ rid )
+# MAY ( cn $ lmPassword $ ntPassword $ pwdLastSet $ logonTime $
+# logoffTime $ kickoffTime $ pwdCanChange $ pwdMustChange $ acctFlags $
+# displayName $ smbHome $ homeDrive $ scriptPath $ profilePath $
+# description $ userWorkstations $ primaryGroupID $ domain ))
+
+########################################################################
+## END OF HISTORICAL ##
+########################################################################
+
+#######################################################################
+## Attributes used by Samba 3.0 schema ##
+#######################################################################
+
+##
+## Password hashes
+##
+attributetype ( 1.3.6.1.4.1.7165.2.1.24 NAME 'sambaLMPassword'
+ DESC 'LanManager Password'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.25 NAME 'sambaNTPassword'
+ DESC 'MD4 hash of the unicode password'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE )
+
+##
+## Account flags in string format ([UWDX ])
+##
+attributetype ( 1.3.6.1.4.1.7165.2.1.26 NAME 'sambaAcctFlags'
+ DESC 'Account Flags'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{16} SINGLE-VALUE )
+
+##
+## Password timestamps & policies
+##
+attributetype ( 1.3.6.1.4.1.7165.2.1.27 NAME 'sambaPwdLastSet'
+ DESC 'Timestamp of the last password update'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.28 NAME 'sambaPwdCanChange'
+ DESC 'Timestamp of when the user is allowed to update the password'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.29 NAME 'sambaPwdMustChange'
+ DESC 'Timestamp of when the password will expire'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.30 NAME 'sambaLogonTime'
+ DESC 'Timestamp of last logon'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.31 NAME 'sambaLogoffTime'
+ DESC 'Timestamp of last logoff'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.32 NAME 'sambaKickoffTime'
+ DESC 'Timestamp of when the user will be logged off automatically'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.48 NAME 'sambaBadPasswordCount'
+ DESC 'Bad password attempt count'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.49 NAME 'sambaBadPasswordTime'
+ DESC 'Time of the last bad password attempt'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+
+##
+## string settings
+##
+attributetype ( 1.3.6.1.4.1.7165.2.1.33 NAME 'sambaHomeDrive'
+ DESC 'Driver letter of home directory mapping'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{4} SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.34 NAME 'sambaLogonScript'
+ DESC 'Logon script path'
+ EQUALITY caseIgnoreMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255} SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.35 NAME 'sambaProfilePath'
+ DESC 'Roaming profile path'
+ EQUALITY caseIgnoreMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255} SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.36 NAME 'sambaUserWorkstations'
+ DESC 'List of user workstations the user is allowed to logon to'
+ EQUALITY caseIgnoreMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255} SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.37 NAME 'sambaHomePath'
+ DESC 'Home directory UNC path'
+ EQUALITY caseIgnoreMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.38 NAME 'sambaDomainName'
+ DESC 'Windows NT domain to which the user belongs'
+ EQUALITY caseIgnoreMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.47 NAME 'sambaMungedDial'
+ DESC ''
+ EQUALITY caseExactMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1050} )
+
+##
+## SID, of any type
+##
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.20 NAME 'sambaSID'
+ DESC 'Security ID'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{64} SINGLE-VALUE )
+
+
+##
+## Primary group SID, compatible with ntSid
+##
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.23 NAME 'sambaPrimaryGroupSID'
+ DESC 'Primary Group Security ID'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{64} SINGLE-VALUE )
+
+##
+## group mapping attributes
+##
+attributetype ( 1.3.6.1.4.1.7165.2.1.19 NAME 'sambaGroupType'
+ DESC 'NT Group Type'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+##
+## Store info on the domain
+##
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.21 NAME 'sambaNextUserRid'
+ DESC 'Next NT rid to give our for users'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.22 NAME 'sambaNextGroupRid'
+ DESC 'Next NT rid to give out for groups'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.39 NAME 'sambaNextRid'
+ DESC 'Next NT rid to give out for anything'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.40 NAME 'sambaAlgorithmicRidBase'
+ DESC 'Base at which the samba RID generation algorithm should operate'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.41 NAME 'sambaShareName'
+ DESC 'Share Name'
+ EQUALITY caseIgnoreMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.42 NAME 'sambaOptionName'
+ DESC 'Option Name'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.43 NAME 'sambaBoolOption'
+ DESC 'A boolean option'
+ EQUALITY booleanMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.44 NAME 'sambaIntegerOption'
+ DESC 'An integer option'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.45 NAME 'sambaStringOption'
+ DESC 'A string option'
+ EQUALITY caseExactIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.46 NAME 'sambaStringListOption'
+ DESC 'A string list option'
+ EQUALITY caseIgnoreMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.50 NAME ( 'sambaPrivName' ) SUP name )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.51 NAME 'sambaSIDList'
+ DESC 'Security ID List'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{64} )
+
+#######################################################################
+## objectClasses used by Samba 3.0 schema ##
+#######################################################################
+
+## The X.500 data model (and therefore LDAPv3) says that each entry can
+## only have one structural objectclass. OpenLDAP 2.0 does not enforce
+## this currently but will in v2.1
+
+##
+## added new objectclass (and OID) for 3.0 to help us deal with backwards
+## compatibility with 2.2 installations (e.g. ldapsam_compat) --jerry
+##
+objectclass ( 1.3.6.1.4.1.7165.2.2.6 NAME 'sambaSamAccount' SUP top AUXILIARY
+ DESC 'Samba 3.0 Auxilary SAM Account'
+ MUST ( uid $ sambaSID )
+ MAY ( cn $ sambaLMPassword $ sambaNTPassword $ sambaPwdLastSet $
+ sambaLogonTime $ sambaLogoffTime $ sambaKickoffTime $
+ sambaPwdCanChange $ sambaPwdMustChange $ sambaAcctFlags $
+ displayName $ sambaHomePath $ sambaHomeDrive $ sambaLogonScript $
+ sambaProfilePath $ description $ sambaUserWorkstations $
+ sambaPrimaryGroupSID $ sambaDomainName $ sambaMungedDial $
+ sambaBadPasswordCount $ sambaBadPasswordTime))
+
+##
+## Group mapping info
+##
+objectclass ( 1.3.6.1.4.1.7165.2.2.4 NAME 'sambaGroupMapping' SUP top AUXILIARY
+ DESC 'Samba Group Mapping'
+ MUST ( gidNumber $ sambaSID $ sambaGroupType )
+ MAY ( displayName $ description $ sambaSIDList ))
+
+##
+## Whole-of-domain info
+##
+objectclass ( 1.3.6.1.4.1.7165.2.2.5 NAME 'sambaDomain' SUP top STRUCTURAL
+ DESC 'Samba Domain Information'
+ MUST ( sambaDomainName $
+ sambaSID )
+ MAY ( sambaNextRid $ sambaNextGroupRid $ sambaNextUserRid $
+ sambaAlgorithmicRidBase ) )
+
+## used for idmap_ldap module
+objectclass ( 1.3.6.1.4.1.7165.1.2.2.7 NAME 'sambaUnixIdPool' SUP top AUXILIARY
+ DESC 'Pool for allocating UNIX uids/gids'
+ MUST ( uidNumber $ gidNumber ) )
+
+
+objectclass ( 1.3.6.1.4.1.7165.1.2.2.8 NAME 'sambaIdmapEntry' SUP top AUXILIARY
+ DESC 'Mapping from a SID to an ID'
+ MUST ( sambaSID )
+ MAY ( uidNumber $ gidNumber ) )
+
+objectclass ( 1.3.6.1.4.1.7165.1.2.2.9 NAME 'sambaSidEntry' SUP top STRUCTURAL
+ DESC 'Structural Class for a SID'
+ MUST ( sambaSID ) )
+
+
+
+
+objectclass ( 1.3.6.1.4.1.7165.1.2.2.10 NAME 'sambaConfig' SUP top AUXILIARY
+ DESC 'Samba Configuration Section'
+ MAY ( description ) )
+
+objectclass ( 1.3.6.1.4.1.7165.1.2.2.11 NAME 'sambaShare' SUP top STRUCTURAL
+ DESC 'Samba Share Section'
+ MUST ( sambaShareName )
+ MAY ( description ) )
+
+objectclass ( 1.3.6.1.4.1.7165.1.2.2.12 NAME 'sambaConfigOption' SUP top STRUCTURAL
+ DESC 'Samba Configuration Option'
+ MUST ( sambaOptionName )
+ MAY ( sambaBoolOption $ sambaIntegerOption $ sambaStringOption $ sambaStringListoption $ description ) )
+
+
+objectclass ( 1.3.6.1.4.1.7165.1.2.2.13 NAME 'sambaPrivilege' SUP top STRUCTURAL
+ DESC 'Samba Privilege'
+ MUST ( sambaPrivName )
+ MAY ( sambaSIDList $ description ) )
+
diff --git a/examples/LDAP/samba.schema.at.IBM-DS b/examples/LDAP/samba.schema.at.IBM-DS
new file mode 100644
index 00000000000..bc39d520fb6
--- /dev/null
+++ b/examples/LDAP/samba.schema.at.IBM-DS
@@ -0,0 +1,59 @@
+## Samba 3.0 schema for IBM Directory Server 5.1 - object classes only
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.24 NAME 'sambaLMPassword' DESC 'LanManager Password' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.25 NAME 'sambaNTPassword' DESC 'MD4 hash of the unicode password'EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.26 NAME 'sambaAcctFlags' DESC 'Account Flags' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{16} SINGLE-VALUE )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.27 NAME 'sambaPwdLastSet' DESC 'Timestamp of the last password update' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.28 NAME 'sambaPwdCanChange' DESC 'Timestamp of when the user is allowed to update the password' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.29 NAME 'sambaPwdMustChange' DESC 'Timestamp of when the password will expire' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.30 NAME 'sambaLogonTime' DESC 'Timestamp of last logon' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.31 NAME 'sambaLogoffTime' DESC 'Timestamp of last logoff' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.32 NAME 'sambaKickoffTime' DESC 'Timestamp of when the user will be logged off automatically' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.33 NAME 'sambaHomeDrive' DESC 'Driver letter of home directory mapping' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{4} SINGLE-VALUE )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.34 NAME 'sambaLogonScript' DESC 'Logon script path' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255} SINGLE-VALUE )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.35 NAME 'sambaProfilePath' DESC 'Roaming profile path' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255} SINGLE-VALUE )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.36 NAME 'sambaUserWorkstations' DESC 'List of user workstations the user is allowed to logon to' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255} SINGLE-VALUE )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.37 NAME 'sambaHomePath' DESC 'Home directory UNC path' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.38 NAME 'sambaDomainName' DESC 'Windows NT domain to which the user belongs' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.20 NAME 'sambaSID' DESC 'Security ID' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{64} SINGLE-VALUE )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.23 NAME 'sambaPrimaryGroupSID' DESC 'Primary Group Security ID' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{64} SINGLE-VALUE )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.19 NAME 'sambaGroupType' DESC 'NT Group Type' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.21 NAME 'sambaNextUserRid' DESC 'Next NT rid to give our for users' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.22 NAME 'sambaNextGroupRid' DESC 'Next NT rid to give out for groups' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.39 NAME 'sambaNextRid' DESC 'Next NT rid to give out for anything' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.40 NAME 'sambaAlgorithmicRidBase' DESC 'Base at which the samba RID generation algorithm should operate' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.41 NAME 'sambaShareName' DESC 'Share Name' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.42 NAME 'sambaOptionName' DESC 'Option Name' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.43 NAME 'sambaBoolOption' DESC 'A boolean option' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.44 NAME 'sambaIntegerOption' DESC 'An integer option' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.45 NAME 'sambaStringOption' DESC 'A string option' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.46 NAME 'sambaStringListOption' DESC 'A string list option' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetypes=( 1.3.6.1.4.1.7165.2.1.47 NAME 'sambaMungedDial' DESC '' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1050} )
diff --git a/examples/LDAP/samba.schema.oc.IBM-DS b/examples/LDAP/samba.schema.oc.IBM-DS
new file mode 100644
index 00000000000..8f556520de9
--- /dev/null
+++ b/examples/LDAP/samba.schema.oc.IBM-DS
@@ -0,0 +1,19 @@
+## Samba 3.0 schema for IBM Directory Server 5.1 - object classes only
+
+objectclasses=( 1.3.6.1.4.1.7165.2.2.6 NAME 'sambaSamAccount' SUP top AUXILIARY DESC 'Samba 3.0 Auxilary SAM Account' MUST ( uid $ sambaSID ) MAY ( cn $ sambaLMPassword $ sambaNTPassword $ sambaPwdLastSet $ sambaLogonTime $ sambaLogoffTime $ sambaKickoffTime $ sambaPwdCanChange $ sambaPwdMustChange $ sambaAcctFlags $ displayName $ sambaHomePath $ sambaHomeDrive $ sambaLogonScript $ sambaProfilePath $ description $ sambaUserWorkstations $ sambaPrimaryGroupSID $ sambaDomainName $ sambaMungedDial ))
+
+objectclasses=( 1.3.6.1.4.1.7165.2.2.4 NAME 'sambaGroupMapping' SUP top AUXILIARY DESC 'Samba Group Mapping' MUST ( gidNumber $ sambaSID $ sambaGroupType ) MAY ( displayName $ description ))
+
+objectclasses=( 1.3.6.1.4.1.7165.2.2.5 NAME 'sambaDomain' SUP top STRUCTURAL DESC 'Samba Domain Information' MUST ( sambaDomainName $ sambaSID ) MAY ( sambaNextRid $ sambaNextGroupRid $ sambaNextUserRid $ sambaAlgorithmicRidBase ) )
+
+objectclasses=( 1.3.6.1.4.1.7165.1.2.2.7 NAME 'sambaUnixIdPool' SUP top AUXILIARY DESC 'Pool for allocating UNIX uids/gids' MUST ( uidNumber $ gidNumber ) )
+
+objectclasses=( 1.3.6.1.4.1.7165.1.2.2.8 NAME 'sambaIdmapEntry' SUP top AUXILIARY DESC 'Mapping from a SID to an ID' MUST ( sambaSID ) MAY ( uidNumber $ gidNumber ) )
+
+objectclasses=( 1.3.6.1.4.1.7165.1.2.2.9 NAME 'sambaSidEntry' SUP top STRUCTURAL DESC 'Structural Class for a SID' MUST ( sambaSID ) )
+
+objectclasses=( 1.3.6.1.4.1.7165.1.2.2.10 NAME 'sambaConfig' SUP top AUXILIARY DESC 'Samba Configuration Section' MAY ( description ) )
+
+objectclasses=( 1.3.6.1.4.1.7165.1.2.2.11 NAME 'sambaShare' SUP top STRUCTURAL DESC 'Samba Share Section' MUST ( sambaShareName ) MAY ( description ) )
+
+objectclasses=( 1.3.6.1.4.1.7165.1.2.2.12 NAME 'sambaConfigOption' SUP top STRUCTURAL DESC 'Samba Configuration Option' MUST ( sambaOptionName ) MAY ( sambaBoolOption $ sambaIntegerOption $ sambaStringOption $ sambaStringListoption $ description ) )
diff --git a/examples/LDAP/smbldap-tools/CONTRIBUTORS b/examples/LDAP/smbldap-tools/CONTRIBUTORS
new file mode 100644
index 00000000000..697f93b7f49
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/CONTRIBUTORS
@@ -0,0 +1,33 @@
+# $Source: /home/cvs/samba/examples/LDAP/smbldap-tools/CONTRIBUTORS,v $
+#
+## Authors and actives contributors to SMBLDAP-TOOLS
+
+Have contributed directly to this tools, or are always in charge of
+some aspects of it developments:
+ . Jérôme Tournier <jerome.tournier@IDEALX.com>
+ . Terry Davis <terry@terryd.net>
+ . David Le Corfec <dlc@freesurf.fr>
+ . Olivier Lemaire <olivier.lemaire@IDEALX.com>
+
+Many thanks to contributors for bug report and patches:
+ . Marc Schoechlin <ms@LF.net>
+ load the perl-modules without setting environment-variables or making symlinks
+ . Alexander Bergolth <leo@strike.wu-wien.ac.at>
+ more Net::LDAP support
+ . Gert-Jan Braas <braas@wyldebeast-wunderliebe.com>
+ bug report for 2.2.3 samba.schema
+ . Jody Haynes <Jody.Haynes@isunnetworks.com>
+ originaly passwd.pl
+ . Brad Langhorst <brad@langhorst.com>
+ package relocatability
+ . Mirko Manea <mami@arena.sci.univr.it>
+ originaly useradd.pl
+ . Alain Richard <alain.richard@equation.fr>
+ bug report and Perl tips
+ . Roland Schulz <mail@r2s2.de>
+ bug report for smbldap-passwd
+ . Xavier Boschian <Xavier.Boschian@rtlgroup.net>
+ bug report for smbldap-populate
+ . Christophe DUBREUIL <christophe.dubreuil@laposte.net>
+ Net::LDAP support in smbldap_tools.pm
+# - The End
diff --git a/examples/LDAP/smbldap-tools/COPYING b/examples/LDAP/smbldap-tools/COPYING
new file mode 100644
index 00000000000..32d0e6014a7
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You 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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/examples/LDAP/smbldap-tools/ChangeLog b/examples/LDAP/smbldap-tools/ChangeLog
new file mode 100644
index 00000000000..54ba1aed66f
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/ChangeLog
@@ -0,0 +1,89 @@
+# $Source: /home/cvs/samba/examples/LDAP/smbldap-tools/ChangeLog,v $
+# $id: $
+#
+## ChangeLog for SMBLDAP-TOOLS
+
+2003-11-18:
+ . new option '-a' to smbldap-usermod.pl that allow adding the sambaSamAccount
+ objectclass to an existing posixAccount
+2003-11-07:
+ . patch that allow adding user to a group when the group is in a higher level depth
+ then ou=Groups (for example, ou=grp1,ou=Groups,...)
+ . check the unicity of a group when adding/removing a user to this group
+2003-10-28:
+ . new option '-p' in smbldap-groupadd.pl to 'print' the gidNumber
+ of the group to STDOUT. This is needed by samba (see the man page)
+2003-10-19:
+ . new function does_sid_exist that check if samaSID sttribute is already
+ defined for another use or another group
+2003-10-13:
+ . smbldap-populate.pl now also add the group mapping
+2003-10-01:
+ . one can now comment the two directives '$_userSmbHome' and '$_userProfile'
+ if you want to use the smb.conf directives instead ('logon home' and
+ 'logon path' respectively), or if you want to desable roaming profiles
+ . Patch from Alexander Bergolth <leo@strike.wu-wien.ac.at>: the sambaPrimaryGroupSID
+ of a user is now set to the sambaSID of his primary group
+2003-09-29:
+ . added new option '$_defaultMaxPasswordAge' in smbldap_conf.pm to specifie
+ how long a password is valid
+ . The '-B' option was not always valid: to force a user to change his password:
+ . the attribut sambaPwdLastSet must be != 0
+ . the attribut sambaAcctFlags must not match the 'X' flag
+ . logon script is set (for every one) to the default '_userScript' value if it is defined
+ . Patch from Alexander Bergolth <leo@strike.wu-wien.ac.at>:
+ gid-sid group mapping to smbldap-groupadd.pl and smbldap-groupmod.pl
+2003-09-19: Patch from Marc Schoechlin <ms@LF.net>
+ . load the perl-modules without setting environment-variables or making symlinks
+2003-09-18: Patch from Alexander Bergolth <leo@strike.wu-wien.ac.at>
+ . options "-u", "-g", "-s" and "-c" are now functionnal
+ . the existence of samba account was made on sambaAccount and
+ not sambaSamAccount as it should be for samba3
+ . new function read_user_entry to smbldap_tools.pm that returns
+ a Net::LDAP:Entry object of the user
+ . Use this object to get the dn and user attributes instead of
+ producing an ldif and searching for attributes within that ldif
+2003-09-15:
+ . change machine account creation to not add the sambaSamAccount objectclass.
+ It is now added directly by samba when joigning the domain
+ . new option in smbldap-usermod.pl: '-e' to set an expire date
+ . Start_tls support activated when ldapSSL is set to 1
+ . Net::LDAP support more scripts
+ . bugs correction
+2003-09-02:
+ . sambaPwdLastSet is updated when smbldap-passwd.pl is used
+ . add a function is_group_member to test the existence of a
+ user in a particular group
+ . add a function is_unix_user to test if a particular user exist
+ . Net::LDAP support more scripts
+2003-08-15:
+ . Samba3.0 support
+2003-08-01:
+ . Final version for samba 2.2.8a (cvs tag SAMBA-2-2-8a-FINAL)
+ . OpenLDAP 2.1 support (only one structural objectclass allowed)
+2002-07-24: top and account objectclasses replaced with inetorgperson
+2002-06-03: notes to webmin.idealx.org (idxldapaccounts)
+2002-06-01: release 0.7. tested with 2.2.4
+2002-05-31: fixed smbldap-populate compliance to smbldap_conf
+ cleaned up smbldap_conf to be more readable
+ some more documentation
+ bugfixes on smbldap-passwd and smbldap-populate
+2002-05-16: modified default mode on homes: now 700
+2002-05-13: fixed spec (relocation and reqs)
+2002-03-02: fixed 2.2.3 sambaAccount bug with smbldap-useradd.pl
+ (rid is now mandatory in the sambaAccount objectClass)
+2002-02-14: just modified default populate for Administrator
+2002-02-05: release 0.6. enable/disable user in usermod
+2002-02-04: release 0.5. added smbldap-migrate-groups to migrate NT groups
+ from a net group dump. added samba parameters to smbldap-useradd
+ and smbldap-usermod.
+2002-01-12: added smbldap-migrate-accounts to migrate users/machines
+ accounts from a PWDUMP dump
+2001-12-13: added smbldap-populate to create the initial base
+2001-12-13: initial release 0.1
+2001-12-12: fixed the SPEC file for RedHat
+2001-12-03: cleaned the code and use strict;
+2001-11-20: initial needs (for testing purpose on Samba-2.2.2 an Samba-TNG)
+
+
+# - The End
diff --git a/examples/LDAP/smbldap-tools/FILES b/examples/LDAP/smbldap-tools/FILES
new file mode 100644
index 00000000000..2d2dabd93e9
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/FILES
@@ -0,0 +1,43 @@
+# $Source: /home/cvs/samba/examples/LDAP/smbldap-tools/FILES,v $
+#
+## File listing for SMBLDAP-TOOLS
+
+CONTRIBUTORS : authors and contributors
+COPYING : licence
+FILES : this file listing
+README : introduction and usage
+TODO : feature request and bug report list
+ChangeLog : changelog
+
+Core:
+=-=-=
+smbldap-groupadd.pl : to add a new group
+ (objectclass: posixGroup)
+smbldap-groupdel.pl : to delete a group
+ (objectclass: posixGroup)
+smbldap-groupmod.pl : to modify a group (mostly used to add user to a group)
+ (objectclass: posixGroup)
+smbldap-groupshow.pl : to view a group
+ (objectclass: posixGroup)
+smbldap_conf.pm : global configuration datas
+smbldap_tools.pm : functions
+smbldap-useradd.pl : to add a new user
+ (objectclass: posixAccount and/or sambaAccount)
+smbldap-userdel.pl : to delete a user
+ (objectclass: posixAccount and/or sambaAccount)
+smbldap-usermod.pl : to modify an user datas
+ (objectclass: posixAccount and/or sambaAccount)
+smbldap-usershow.pl : to view an user datas
+ (objectclass: posixAccount and/or sambaAccount)
+smbldap-passwd.pl : to sync passwd (Unix and Samba)
+ (a replacement for the system passwd utility)
+smbldap-populate.pl : to add a builtin ldif to initialize your LDAP master for
+ smbldap use, or to add a specified ldif
+smbldap-tools.spec : SPEC file for RedHat RPM package format
+
+Migration:
+=-=-=-=-=-
+smbldap-migrate-accounts.pl : add NT sam entries from pwdump to ldap
+smbldap-migrate-groups.pl : add any LDAP posixGroups from the output of the 'net group' NT command
+
+# - The End
diff --git a/examples/LDAP/smbldap-tools/INFRASTRUCTURE b/examples/LDAP/smbldap-tools/INFRASTRUCTURE
new file mode 100644
index 00000000000..8ea07ead44b
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/INFRASTRUCTURE
@@ -0,0 +1,93 @@
+# $Source: /home/cvs/samba/examples/LDAP/smbldap-tools/INFRASTRUCTURE,v $
+#
+## Some notes about the architecture
+
+
+Global Architecture for smbdlap-tools
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+smbldap-tools help you manage users and groups for Unix and Samba,
+using LDAP. They may be used in any context, and are kept relatively
+simplier enought to let you customize them to you needs.
+
+They need the following objectClasses to work:
+ . sambaAccount: from samba.schema for Samba 2.2 branch
+ . posixAccount and posixGroup : from nis.schema
+ . organizationalUnit and dcObject: from core.schema
+
+They will probably use in a near future some additional objectClasses
+to support :
+ . mail features (sendmail/postfix/qmail/courier).
+ . conform to RFC2307 best practices (and so some maps too like merging
+ Netbios computers (sambaAccounts) with ipHosts
+
+For ease of visualization of the LDAP objects by human standards, we
+used a DIT like this one :
+ . dc=IDEALX,dc=org : the company/organization suffix
+ . ou=Users : to store users accounts
+ . ou=Computers : to store computers accounts
+ . ou=Groups : to store system groups
+Of course, you're free to use a different naming scheme and DIT (see
+smbldap_conf.pm).
+
+
+Built in groups initial population
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+smbldap-populate.pl populate the LDAP directory with some built in groups
+using gidNumber according to Well Know RID of Windows NT4 Srv. In fact, As
+far a Samba 2.2.x is concerned, only the 'Domain Admins' (gidNumber 512) have
+real inpact on the Samba and Windows population. To activate this group as
+the Domain Administrators Group, use the following smb.conf directive (see
+man smb.conf for more):
+
+ domain admin group = " @"Domain Admins" "
+
+However, to make pdb_ldap accept bind without being uid=0, a quick and
+dirty patch must be applied to 2.2.4 (see samba-2.2.4-ldapbindnotuid0.patch).
+This patch is Q&D because the check is there because Samba store admin
+credentials to establish the LDAP connection. The uid == 0 check was to
+ensure that a normal user could not get write access to the LDAP backend.
+A more logical situation should be done for 2.2.5 by checking if the user
+is a member of the domain admin group (reported to Jerremy and Gerald
+2002-05-28).
+
+Other built in groups are really cosmetic ones with Samba 2.2.x. We did not
+removed them because one of these days, we whish to use Samba 3.0 where
+Windows Group Support should be operational.
+
+Why these specific gidNumbers ?
+It's about unix/windows mapping of numerical ids with Samba. Ids below 1024
+are NT special ids. In fact, 512 is the RID (Windows uid/gid) for the
+"Domain Administrators" NT group. The magic number is found in Samba sources
+and possibly other Samba/Windows documentations.
+
+The goal is to have a set of Unix users who are Domain Administrators and can
+modify Samba datas (eg. LDAP content), with commandline tools or within
+Windows via Samba.
+
+Say you want to add a NT4 ws to an NT domain (controlled by a samba/ldap
+server). You give the domain administrator's login and password in the
+appropriate ws settings, then the ws contacts the samba server, which checks
+the credentials and use them as unix user to run the smbldap-tools (if I
+remember). Giving 512 as a RID to a LDAP entry marks it as a domain admin
+for Samba (thus Windows). Using nss_ldap, you also have an account with
+gid 512.
+
+
+Known BUGS and WORKAROUND used
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+The 2.2.2 has at least a bug : rid/primaryGroupID are read as hex in LDAP,
+but written as decimal. Fixed in CVS by reading as decimal. By default
+smbldap-useradd.pl writes decimal to LDAP. Use -x to support the odd
+behaviour.
+
+The samba-2.2.4-ldapbindnotuid0.patch is not a perfect solution however
+as the check is there because Samba store admin credentials to establish the
+LDAP connection. The uid == 0 check was to ensure that a normal user could
+not get write access to the LDAP backend. A more logical situation should be
+done for 2.2.5 by checking if the user is a member of the domain admin group
+(reported to Jerremy and Gerald 2002-05-28).
+
+# - The End
diff --git a/examples/LDAP/smbldap-tools/INSTALL b/examples/LDAP/smbldap-tools/INSTALL
new file mode 100644
index 00000000000..38221b12433
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/INSTALL
@@ -0,0 +1,28 @@
+# $Source: /home/cvs/samba/examples/LDAP/smbldap-tools/INSTALL,v $
+#
+## How To Install SMBLDAP-TOOLS
+
+Quick & Dirty:
+=-=-=-=-=-=-=-
+ . Copy all those scripts in /usr/local/sbin/
+ . Modify smbldap_conf.pm to match your configuration
+ . If not already done : "smbpasswd -w secret" to set up
+ the ldap admin password in secrets.tdb
+ . Either add /usr/local/sbin in $PERLLIB or run the scripts
+ from this directory, or make a symlink from /usr/local/sbin/*.pm
+ to /usr/lib/perl5/.
+ . to allow a domain admin to add user using "add user script" in smb.conf :
+ chmod 753 smbldap_conf.pm
+ chmod 750 smbldap-useradd.pl
+ chgrp 512 smbldap_conf.pm smbldap-useradd.pl (512 = 0x200 = Domain Admins)
+ Have your admin belong to this group
+ In smb.conf : domain admin group = " @"Domain Admins" "
+
+RedHat RPM:
+=-=-=-=-=-=
+Install smbldap-tools-0.7-1.i386.rpm
+Modify /usr/local/sbin/smbldap_conf.pm to match you configuration
+If not already done : "smbpasswd -w secret" to set up
+the ldap admin password in secrets.tdb
+
+# - The End
diff --git a/examples/LDAP/smbldap-tools/Makefile b/examples/LDAP/smbldap-tools/Makefile
new file mode 100644
index 00000000000..783a3571a99
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/Makefile
@@ -0,0 +1,35 @@
+PACKAGE=smbldap-tools
+RELEASE=0.8.2-1
+DESTDIR = $(PACKAGE)-$(RELEASE)
+
+dist: distclean $(DESTDIR).tgz
+
+$(DESTDIR).tgz: .diststamp
+ rm -rf $(DESTDIR)
+ mkdir $(DESTDIR)
+ # copy files
+ cp CONTRIBUTORS $(DESTDIR)
+ cp COPYING $(DESTDIR)
+ cp ChangeLog $(DESTDIR)
+ cp FILES $(DESTDIR)
+ cp INSTALL $(DESTDIR)
+ cp README $(DESTDIR)
+ cp TODO $(DESTDIR)
+ cp INFRASTRUCTURE $(DESTDIR)
+ tar cf mkntpwd.tar mkntpwd
+ gzip mkntpwd.tar
+ cp mkntpwd.tar.gz $(DESTDIR)
+ cp smbldap-*.pl $(DESTDIR)
+ cp smbldap_*.pm $(DESTDIR)
+ # copy directories
+ tar cvzf $(DESTDIR).tgz $(DESTDIR)
+ rm -rf $(DESTDIR)
+ touch .diststamp
+
+.diststamp:
+
+distclean:
+ rm -f *~
+ rm -f $(DESTDIR).tgz
+ rm -f mkntpwd.tar.gz
+
diff --git a/examples/LDAP/smbldap-tools/README b/examples/LDAP/smbldap-tools/README
new file mode 100644
index 00000000000..c6133e19692
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/README
@@ -0,0 +1,87 @@
+# $Source: /home/cvs/samba/examples/LDAP/smbldap-tools/README,v $
+#
+
+Latest version may be found at http://samba.idealx.org/
+
+
+What those tools are for?
+=-=-=-=-=-=-=-=-=-=-=-=-=
+
+A collection of scripts, «over» user{add,del,mod} and group{add,del,mod}
+system tools to manipulate users and groups stored in LDAP directory,
+for DEN system like SAMBA-LDAP and pam/nss_ldap systems.
+
+Additionnaly, some scripts are designed to ease your migration from
+a Windows NT 4.0 PDC Server to a Samba-LDAP PDC Server (Killer?;-):
+smbldap-populate, smbldap-migrate-groups, smbldap-migrate-accounts.
+
+They are currently used with Samba 2.2.4, therefore you may (will) have
+to make some fixes for Samba TNG and Samba 3.0. Hint: contribs welcome :)
+
+In the future, some other function may come (like backup and restore,
+Novell migration tools, samba system activity report, dealing with
+mail functions, compliance to RFC2307...): consult TODO.
+
+
+What do SMBLDAP-TOOLS provide?
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+Those tools provide the following functions :
+ . populate LDAP database with a basic LDIF
+ . add a user or group
+ . delete a user or group
+ . modify all users or groups data (all attributes types stored in
+ posixAccount and sambaAccount object class)
+Taking care of :
+ . staying compatible with all standard system tools options
+ (user/group{add,del,mod})
+ . be extensible for future developments
+ (manipulation of shadow account options, for example)
+ . error management, in the way system tools do
+Constraints :
+ . usage of PERL (portability)
+ . all options must be placed in an external configuration file
+ . english localization
+
+The current release uses the "mkntpwd" program, in mkntpwd.tar.gz
+in the current directory. It comes from
+http://www.demog.berkeley.edu/~aperrin/tips/src/mkntpwd.tar.gz
+It allows to not use smbpasswd (if $with_smbpasswd == 0 in smbldap_conf.pm)
+
+What do SMBLDAP-TOOLS deliver?
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+Contents :
+ . scripts (see FILES)
+ . user documentation in pod format, included in the sources
+ (or just use the -? option)
+
+These tools aim at delivering the same functionality as the corresponding
+system tools. However they may not be all implemented yet.
+Current limitations :
+ . no shadow support
+ . cannot change uid with usermod
+ . no UTF-8 support (thus ASCII-7 only)
+
+
+How to generate documentation?
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+Just issue the following command:
+ perldoc name_of_script.pl (ex: perldoc smbldap-useradd.pl)
+
+Where can I find the latest release of those scripts?
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+Just fire any web browser to http://samba.IDEALX.org/
+and/or contact samba@IDEALX.org
+
+Additionnaly, you will find an useful Webmin module
+at http://webmin.IDEALX.org/ if interested in a graphical
+user interface to manager user and groups accounts via Webmin
+for your Samba+LDAP PDC.
+
+Let us know if these tools helped you, or if we should enhance
+them with some functions you want them to support.
+
+Sincerly,
+ LEM
+
+# - The End
diff --git a/examples/LDAP/smbldap-tools/TODO b/examples/LDAP/smbldap-tools/TODO
new file mode 100644
index 00000000000..8fdc2a55994
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/TODO
@@ -0,0 +1,28 @@
+# $Source: /home/cvs/samba/examples/LDAP/smbldap-tools/TODO,v $
+#
+## TODO list - First In, Last in the list...
+## (BF: Bug Report / FR: Feature Request)
+
+
+FR * add 'LDAP port' for both slave and master LDAP server in smbldap_conf.pm
+FR * use RFC2307 best practices (Luke, next time you visit Paris, have a
+ beer at IDEALX'cantina ;-)
+FR * add mail (sendmail/postfix/qmail/courier) support
+FR * bugfix, really : allow non-root users to change passwd
+ (currently the config must be unreadable because of bindpasswd)
+FR * make smbldap-tools to use system configuration files
+ (/etc/login.defs and /etc/ldap.conf for example)
+FR * rewrite smbldap-tools using perl-ldap. In fact, this 0.x
+ release use ldap system tools (ldapadd,ldapdelete,ldapmodify)
+FR * add shadowAccounts manipulation support
+FR * internationalize the SMBLDAP-TOOLS
+FR * add smbldap-sar : Samba System Activity Report to help
+ supporting Samba/LDAP sysadmin activity
+FR * add smbldap-backup/smbldap-restore to backup and restore
+ a SAM (in LDAP) database. No sorcery, just LDIF, but usefull
+ for non-LDAP gurus
+FR * adding migration tools from migration from W2K and NetWare to Samba-LDAP
+FR * adapt smbldap-tools to use Samba 3.0
+
+
+# - The End
diff --git a/examples/LDAP/smbldap-tools/cgi/README b/examples/LDAP/smbldap-tools/cgi/README
new file mode 100644
index 00000000000..7a4fc0c02b9
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/cgi/README
@@ -0,0 +1,27 @@
+Description:
+ A cgi to allow users to change their passwords via a web browser.
+
+Installation:
+ Drop this into a cgi-enabled directory on your webserver.
+ Edit it and change the CONFIGURATION section to suit your environment.
+ READ THE NOTES SECTION.
+
+Notes: This script will run as the user who runs your web server. So, to invoke the smbpasswd call, you must implement sudo.
+ Example of /etc/sudoers:
+
+ # Host alias specification
+ # User alias specification
+ User_Alias PASSWD = apache
+ # Cmnd alias specification
+ Cmnd_Alias PASSWD = /usr/bin/smbpasswd
+ # User privilege specification
+ root ALL=(ALL) ALL
+ PASSWD ALL= NOPASSWD: PASSWD
+
+ This concept is probably very insecure and broken. That is why this is a 0.1 release. :)
+
+
+Feel free to drop me suggestions. I am a perl learner so I am always open to suggestions.
+
+Terry Davis
+tdavis@approbation.org
diff --git a/examples/LDAP/smbldap-tools/cgi/ldappass.cgi b/examples/LDAP/smbldap-tools/cgi/ldappass.cgi
new file mode 100755
index 00000000000..4a5ecb8f3a9
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/cgi/ldappass.cgi
@@ -0,0 +1,202 @@
+#!/usr/bin/perl
+
+################################################################################
+#
+# changepass.pl - A program to allow users to change their passwords
+# via a web browser.
+# Terry Davis
+#
+# URLs
+# Net::LDAP - http://
+# usermod and this file - http://www.cloudamster.com/cloudmaster/projects
+#
+# Release History:
+# Version 0.1 - initial write
+#
+# ToDo:
+# ... the ToDo section is on the ToDo list...
+#
+# Limitations:
+# The password cannot contain single and double quotes.....welcome to quoting hell....
+#
+# Notes:
+# This code is largely based on work done by Danny Sauer - http://www.cloudamster.com/cloudmaster/projects
+# His work is not licensed and is marked as 'freely distributable'.
+# Thank you to Danny for his hard work on the initial work.
+#
+################################################################################
+
+use CGI qw(:standard);
+use Net::LDAP;
+
+# CONFIGURATION SECTION
+$masterLDAP = "ldap.idealx.org";
+$basedn = "dc=IDEALX,dc=org";
+$masterPw = "";
+$masterDN = "cn=manager,$basedn";
+$ldap_path = "/usr/bin";
+$ldap_opts = "-x";
+$ldappasswd = "$ldap_path/ldappasswd $ldap_opts -h $masterLDAP -D '$masterDN' -w '$masterPw'";
+$usersdn = "ou=Users,$basedn";
+# END CONFIGURATION
+
+
+
+# DONT EDIT ANYTHING BELOW THIS LINE
+$logtag = "Login:";
+$passtag = "Current password:";
+$npasstag1 = "New password:";
+$npasstag2 = "Retype new pasword:";
+$error = "";
+$color = "<FONT color='red'>";
+$stopcolor = "</FONT>";
+
+if(param()){
+ nologin() unless ($username = param('login'));
+ nopass() unless ($oldpass = param('oldpass'));
+ nonewpass(1) unless ($newpass1 = param('newpass'));
+ nonewpass(2) unless ($newpass2 = param('newpass2'));
+ verifyuser($username) or die "bad user";
+ verifypass($username, $oldpass) or die "bad pass";
+ testnewpass($newpass1, $newpass2) or die "bad new pass";
+ changepass($username, $newpass1) or die "couldn't change pass";
+ printsuccess();
+}else{
+ printpage();
+}
+exit(0);
+
+sub verifyuser{
+ local $user = shift;
+ $ldap = Net::LDAP->new($masterLDAP) or die "can't make new LDAP object: $@";
+ $ldap->bind();
+ if (0 < $ldap->search(base => $basedn, filter => "(uid=$user)")->count){
+ return 1;
+ }
+ $logtag = $color . $logtag . $color;
+ $error = "No such user";
+ printpage();
+ return 0;
+}
+
+sub verifypass{
+ $uid = shift;
+ $pass = shift;
+ $ldap = Net::LDAP->new($masterLDAP) or die "can't make new LDAP object: $@";
+ $binddn = "uid=$uid,ou=People,$basedn";
+ return 1 if($ldap->bind( $binddn, password => $pass)->code == 0);
+ if($ldap->bind()){
+ $passtag = $color . $passtag . $color;
+ $error = "Incorrect password";
+ printpage();
+ return 0;
+ }else{
+ print header, start_html(-title=>"LDAP dead");
+ print h2("<CENTER>The LDAP server is temporarily unavailable."),
+ p,"Please try again later</CENTER>";
+ return 0;
+ }die "Something (or someone) is defective, contact your friendly Systems Administrator";
+}
+
+sub testnewpass{
+ $p1 = shift; $p2 = shift;
+ if ($p1 ne $p2){
+ $npasstag1 = $color . $npasstag1 . $color;
+ $npasstag2 = $color . $npasstag2 . $color;
+ $error = "Passwords don't match ($p1 vs $p2)";
+ printpage();
+ return 0;
+ }
+ if ($p1 =~ /"/ ){
+ $npasstag1 = $color . $npasstag1 . $color;
+ $npasstag2 = $color . $npasstag2 . $color;
+ $error = "Passwords cannot contain double quotes. Sorry";
+ printpage();
+ return 0;
+ }
+ if ($p1 =~ /'/ ){
+ $npasstag1 = $color . $npasstag1 . $color;
+ $npasstag2 = $color . $npasstag2 . $color;
+ $error = "Passwords cannot contain single quotes. Sorry";
+ printpage();
+ return 0;
+ }
+ return 1;
+}
+
+sub changepass{
+ local $user = shift;
+ local $newpass = shift;
+ local $dn = "uid=$user,$usersdn";
+ system "$ldappasswd $dn -s '$newpass' > /dev/null";
+ `/usr/bin/sudo /usr/bin/smbpasswd $user "$newpass"`;
+ exit(1);
+}
+
+sub nologin{
+ $logtag = $color . $logtag . $color;
+ $error = "You need to enter a Login Name";
+ printpage();
+ exit(1);
+}
+
+sub nopass{
+ $passtag = $color . $passtag . $color;
+ $error = "Please enter your old password";
+ printpage();
+ exit(1);
+}
+
+sub nonewpass{
+ $f=shift;
+ $npasstag1 = $color . $npasstag1 . $color if($f==1);
+ $npasstag2 = $color . $npasstag2 . $color if($f==2);
+ $error = "You need to enter your new password";
+ $error .= " twice" if($f==2);
+ printpage();
+ exit(1);
+}
+
+sub printpage{
+ print header,
+ start_html(-title=> "Password Change Page",
+ -author=> 'tdavis@birddog.com',
+ -BGCOLOR=> 'WHITE'),
+ h3('Password Change Page'),
+ startform(-method=>'POST'),
+ "<TABLE BORDER=0 WIDTH=50%>",
+ "<font size=2>",
+ "<TR><TD>",
+ $logtag,
+ "</TD><TD>",
+ textfield(-name=>'login', -default=>$login,
+ -size=>15, -maxlength=>20),
+ "</TD><TR><TD>",
+ $passtag,
+ "</TD><TD>",
+ password_field(-name=>'oldpass', -size=>15, -maxlength=>25),
+ "</TD><TR><TD>",
+ $npasstag1,
+ "</TD><TD>",
+ password_field(-name=>'newpass', -size=>15, -maxlength=>25),
+ "</TD><TR><TD>",
+ $npasstag2,
+ "</TD><TD>",
+ password_field(-name=>'newpass2', -size=>15, -maxlength=>25),
+ "</TD><TR><TD></TD><TD>",
+ submit(-name=>"change"),reset(),
+ "</TD></TR></TABLE>",
+ "</font>",
+ endform(),
+ "<FONT color='red'>$error</FONT>",
+ end_html;
+}
+
+sub printsuccess(){
+ print header,
+ start_html(-title=> "Success",
+ -BGCOLOR=> 'WHITE'),
+ h1("Password Succesfully Changed"),
+ "<br>",
+ end_html;
+}
diff --git a/examples/LDAP/smbldap-tools/mkntpwd/Makefile b/examples/LDAP/smbldap-tools/mkntpwd/Makefile
new file mode 100644
index 00000000000..23c9d471b0a
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/mkntpwd/Makefile
@@ -0,0 +1,62 @@
+# Makefile for l0phtcrack - mudge@l0pht.com 11/1/96
+
+# C compiler
+#CC=cc
+CC=gcc
+
+# Uncomment the following to add symbols to the code for debugging
+#DEBUG=-g -Wall -D_DEBUG
+#DEBUG=-D_DEBUG
+
+# Optimization for the compiler
+#OPTIMIZE=
+OPTIMIZE=-O2
+
+# Choose your architecture
+# note that if you are on a big-endian machine like SUN's
+# I haven't tweaked the mem-cmp's and md4 stuff to be in
+# the correct order yet. You're on your own right now.
+#
+# FreeBSD
+ARCH=-DMPU8086
+STATIC=
+XLIBS=
+#
+# SUNOS
+#ARCH=-DBIGENDIAN
+#STATIC=
+#OPTIMIZE=-O2
+#XLIBS=
+#
+# ULTRA_SPARC w/ native compiler
+#ARCH=-DBIGENDIAN
+#STATIC=
+#OPTIMIZE=-fast -xO4 -xdepend -xchip=ultra -xarch=v8plus
+#XLIBS=
+#
+# SunOS/Solaris w/gcc
+#ARCH=-DBIGENDIAN -DTEST
+#STATIC=
+#OPTIMIZE=-O2
+#XLIBS=
+#
+# NeXTStep 3.2
+#CC=cc
+#ARCH=-DBIGENDIAN
+#STATIC=-Bstatic
+#OPTIMIZE=
+#XLIBS=
+
+CFLAGS= $(DEBUG) $(OPTIMIZE) $(ARCH) $(VISUAL) $(PERMUTE) $(STATIC)
+
+OBJS = getopt.o md4.o mkntpwd.o smbdes.o
+
+mkntpwd: $(OBJS)
+ $(CC) $(CFLAGS) $(XLIBS) -o mkntpwd $(OBJS)
+
+clean:
+ rm -f core *.o mkntpwd
+
+install: mkntpwd
+ install -m 555 mkntpwd $(PREFIX)/sbin/mkntpwd
+
diff --git a/examples/LDAP/smbldap-tools/mkntpwd/getopt.c b/examples/LDAP/smbldap-tools/mkntpwd/getopt.c
new file mode 100644
index 00000000000..5b2e7a9100b
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/mkntpwd/getopt.c
@@ -0,0 +1,756 @@
+/* Getopt for GNU.
+ NOTE: getopt is now part of the C library, so if you don't know what
+ "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
+ before changing it!
+
+ Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94
+ Free Software Foundation, Inc.
+
+This file is part of the GNU C Library. Its master source is NOT part of
+the C library, however. The master source lives in /gd/gnu/lib.
+
+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. */
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
+ Ditto for AIX 3.2 and <stdlib.h>. */
+#ifndef _NO_PROTO
+#define _NO_PROTO
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if !defined (__STDC__) || !__STDC__
+/* This is a separate conditional since some stdc systems
+ reject `defined (const)'. */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+#ifdef WIN32
+#include <string.h>
+#endif
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+ contain conflicting prototypes for getopt. */
+#include <stdlib.h>
+#endif /* GNU C library. */
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+ but it behaves differently for the user, since it allows the user
+ to intersperse the options with the other arguments.
+
+ As `getopt' works, it permutes the elements of ARGV so that,
+ when it is done, all the options precede everything else. Thus
+ all application programs are extended to handle flexible argument order.
+
+ Setting the environment variable POSIXLY_CORRECT disables permutation.
+ Then the behavior is completely standard.
+
+ GNU application programs can use a third alternative mode in which
+ they can distinguish the relative order of options and other arguments. */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+char *optarg = NULL;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+/* XXX 1003.2 says this must be 1 before any call. */
+int optind = 0;
+
+/* The next char to be scanned in the option-element
+ in which the last option character we returned was found.
+ This allows us to pick up the scan where we left off.
+
+ If this is zero, or a null string, it means resume the scan
+ by advancing to the next ARGV-element. */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+ for unrecognized options. */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+ This must be initialized on some systems to avoid linking in the
+ system's own getopt implementation. */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+ If the caller did not specify anything,
+ the default is REQUIRE_ORDER if the environment variable
+ POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+ REQUIRE_ORDER means don't recognize them as options;
+ stop option processing when the first non-option is seen.
+ This is what Unix does.
+ This mode of operation is selected by either setting the environment
+ variable POSIXLY_CORRECT, or using `+' as the first character
+ of the list of option characters.
+
+ PERMUTE is the default. We permute the contents of ARGV as we scan,
+ so that eventually all the non-options are at the end. This allows options
+ to be given in any order, even with programs that were not written to
+ expect this.
+
+ RETURN_IN_ORDER is an option available to programs that were written
+ to expect options and other ARGV-elements in any order and that care about
+ the ordering of the two. We describe each non-option ARGV-element
+ as if it were the argument of an option with character code 1.
+ Using `-' as the first character of the list of option characters
+ selects this mode of operation.
+
+ The special argument `--' forces an end of option-scanning regardless
+ of the value of `ordering'. In the case of RETURN_IN_ORDER, only
+ `--' can cause `getopt' to return EOF with `optind' != ARGC. */
+
+static enum
+{
+ REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+/* Value of POSIXLY_CORRECT environment variable. */
+static char *posixly_correct;
+
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+ because there are many ways it can cause trouble.
+ On some systems, it contains special magic macros that don't work
+ in GCC. */
+#include <string.h>
+#define my_index strchr
+#else
+
+/* Avoid depending on library functions or files
+ whose names are inconsistent. */
+
+char *getenv ();
+
+static char *
+my_index (str, chr)
+ const char *str;
+ int chr;
+{
+ while (*str)
+ {
+ if (*str == chr)
+ return (char *) str;
+ str++;
+ }
+ return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+ If not using GCC, it is ok not to declare it. */
+#ifdef __GNUC__
+/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
+ That was relevant to code that was here before. */
+#if !defined (__STDC__) || !__STDC__
+/* gcc with -traditional declares the built-in strlen to return int,
+ and has done so at least since version 2.4.5. -- rms. */
+extern int strlen (const char *);
+#endif /* not __STDC__ */
+#endif /* __GNUC__ */
+
+#endif /* not __GNU_LIBRARY__ */
+
+/* Handle permutation of arguments. */
+
+/* Describe the part of ARGV that contains non-options that have
+ been skipped. `first_nonopt' is the index in ARGV of the first of them;
+ `last_nonopt' is the index after the last of them. */
+
+static int first_nonopt;
+static int last_nonopt;
+
+/* Exchange two adjacent subsequences of ARGV.
+ One subsequence is elements [first_nonopt,last_nonopt)
+ which contains all the non-options that have been skipped so far.
+ The other is elements [last_nonopt,optind), which contains all
+ the options processed since those non-options were skipped.
+
+ `first_nonopt' and `last_nonopt' are relocated so that they describe
+ the new indices of the non-options in ARGV after they are moved. */
+
+static void
+exchange (argv)
+ char **argv;
+{
+ int bottom = first_nonopt;
+ int middle = last_nonopt;
+ int top = optind;
+ char *tem;
+
+ /* Exchange the shorter segment with the far end of the longer segment.
+ That puts the shorter segment into the right place.
+ It leaves the longer segment in the right place overall,
+ but it consists of two parts that need to be swapped next. */
+
+ while (top > middle && middle > bottom)
+ {
+ if (top - middle > middle - bottom)
+ {
+ /* Bottom segment is the short one. */
+ int len = middle - bottom;
+ register int i;
+
+ /* Swap it with the top part of the top segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[top - (middle - bottom) + i];
+ argv[top - (middle - bottom) + i] = tem;
+ }
+ /* Exclude the moved bottom segment from further swapping. */
+ top -= len;
+ }
+ else
+ {
+ /* Top segment is the short one. */
+ int len = top - middle;
+ register int i;
+
+ /* Swap it with the bottom part of the bottom segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[middle + i];
+ argv[middle + i] = tem;
+ }
+ /* Exclude the moved top segment from further swapping. */
+ bottom += len;
+ }
+ }
+
+ /* Update records for the slots the non-options now occupy. */
+
+ first_nonopt += (optind - last_nonopt);
+ last_nonopt = optind;
+}
+
+/* Initialize the internal data when the first call is made. */
+
+static const char *
+_getopt_initialize (optstring)
+ const char *optstring;
+{
+ /* Start processing options with ARGV-element 1 (since ARGV-element 0
+ is the program name); the sequence of previously skipped
+ non-option ARGV-elements is empty. */
+
+ first_nonopt = last_nonopt = optind = 1;
+
+ nextchar = NULL;
+
+ posixly_correct = getenv ("POSIXLY_CORRECT");
+
+ /* Determine how to handle the ordering of options and nonoptions. */
+
+ if (optstring[0] == '-')
+ {
+ ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == '+')
+ {
+ ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ else if (posixly_correct != NULL)
+ ordering = REQUIRE_ORDER;
+ else
+ ordering = PERMUTE;
+
+ return optstring;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+ given in OPTSTRING.
+
+ If an element of ARGV starts with '-', and is not exactly "-" or "--",
+ then it is an option element. The characters of this element
+ (aside from the initial '-') are option characters. If `getopt'
+ is called repeatedly, it returns successively each of the option characters
+ from each of the option elements.
+
+ If `getopt' finds another option character, it returns that character,
+ updating `optind' and `nextchar' so that the next call to `getopt' can
+ resume the scan with the following option character or ARGV-element.
+
+ If there are no more option characters, `getopt' returns `EOF'.
+ Then `optind' is the index in ARGV of the first ARGV-element
+ that is not an option. (The ARGV-elements have been permuted
+ so that those that are not options now come last.)
+
+ OPTSTRING is a string containing the legitimate option characters.
+ If an option character is seen that is not listed in OPTSTRING,
+ return '?' after printing an error message. If you set `opterr' to
+ zero, the error message is suppressed but we still return '?'.
+
+ If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+ so the following text in the same ARGV-element, or the text of the following
+ ARGV-element, is returned in `optarg'. Two colons mean an option that
+ wants an optional arg; if there is text in the current ARGV-element,
+ it is returned in `optarg', otherwise `optarg' is set to zero.
+
+ If OPTSTRING starts with `-' or `+', it requests different methods of
+ handling the non-option ARGV-elements.
+ See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+ Long-named options begin with `--' instead of `-'.
+ Their names may be abbreviated as long as the abbreviation is unique
+ or is an exact match for some defined option. If they have an
+ argument, it follows the option name in the same ARGV-element, separated
+ from the option name by a `=', or else the in next ARGV-element.
+ When `getopt' finds a long-named option, it returns 0 if that option's
+ `flag' field is nonzero, the value of the option's `val' field
+ if the `flag' field is zero.
+
+ The elements of ARGV aren't really const, because we permute them.
+ But we pretend they're const in the prototype to be compatible
+ with other systems.
+
+ LONGOPTS is a vector of `struct option' terminated by an
+ element containing a name which is zero.
+
+ LONGIND returns the index in LONGOPT of the long-named option found.
+ It is only valid when a long-named option has been found by the most
+ recent call.
+
+ If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+ long-named options. */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+ const struct option *longopts;
+ int *longind;
+ int long_only;
+{
+ optarg = NULL;
+
+ if (optind == 0)
+ optstring = _getopt_initialize (optstring);
+
+ if (nextchar == NULL || *nextchar == '\0')
+ {
+ /* Advance to the next ARGV-element. */
+
+ if (ordering == PERMUTE)
+ {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (last_nonopt != optind)
+ first_nonopt = optind;
+
+ /* Skip any additional non-options
+ and extend the range of non-options previously skipped. */
+
+ while (optind < argc
+ && (argv[optind][0] != '-' || argv[optind][1] == '\0'))
+ optind++;
+ last_nonopt = optind;
+ }
+
+ /* The special ARGV-element `--' means premature end of options.
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
+
+ if (optind != argc && !strcmp (argv[optind], "--"))
+ {
+ optind++;
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (first_nonopt == last_nonopt)
+ first_nonopt = optind;
+ last_nonopt = argc;
+
+ optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if (optind == argc)
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (first_nonopt != last_nonopt)
+ optind = first_nonopt;
+ return EOF;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if ((argv[optind][0] != '-' || argv[optind][1] == '\0'))
+ {
+ if (ordering == REQUIRE_ORDER)
+ return EOF;
+ optarg = argv[optind++];
+ return 1;
+ }
+
+ /* We have found another option-ARGV-element.
+ Skip the initial punctuation. */
+
+ nextchar = (argv[optind] + 1
+ + (longopts != NULL && argv[optind][1] == '-'));
+ }
+
+ /* Decode the current option-ARGV-element. */
+
+ /* Check whether the ARGV-element is a long option.
+
+ If long_only and the ARGV-element has the form "-f", where f is
+ a valid short option, don't consider it an abbreviated form of
+ a long option that starts with f. Otherwise there would be no
+ way to give the -f short option.
+
+ On the other hand, if there's a long option "fubar" and
+ the ARGV-element is "-fu", do consider that an abbreviation of
+ the long option, just like "--fu", and not "-f" with arg "u".
+
+ This distinction seems to be the most useful approach. */
+
+ if (longopts != NULL
+ && (argv[optind][1] == '-'
+ || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
+ {
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = 0; /* set to zero by Anton */
+ int option_index;
+
+ for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
+ /* Do nothing. */ ;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp(p->name, nextchar, nameend - nextchar))
+ {
+ if ((unsigned int)(nameend - nextchar) == (unsigned int)strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+
+ if (ambig && !exact)
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' is ambiguous\n",
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ return '?';
+ }
+
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ optind++;
+ if (*nameend)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = nameend + 1;
+ else
+ {
+ if (opterr)
+ {
+ if (argv[optind - 1][1] == '-')
+ /* --option */
+ fprintf (stderr,
+ "%s: option `--%s' doesn't allow an argument\n",
+ argv[0], pfound->name);
+ else
+ /* +option or -option */
+ fprintf (stderr,
+ "%s: option `%c%s' doesn't allow an argument\n",
+ argv[0], argv[optind - 1][0], pfound->name);
+ }
+ nextchar += strlen (nextchar);
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' requires an argument\n",
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+
+ /* Can't find it as a long option. If this is not getopt_long_only,
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
+ if (!long_only || argv[optind][1] == '-'
+ || my_index (optstring, *nextchar) == NULL)
+ {
+ if (opterr)
+ {
+ if (argv[optind][1] == '-')
+ /* --option */
+ fprintf (stderr, "%s: unrecognized option `--%s'\n",
+ argv[0], nextchar);
+ else
+ /* +option or -option */
+ fprintf (stderr, "%s: unrecognized option `%c%s'\n",
+ argv[0], argv[optind][0], nextchar);
+ }
+ nextchar = (char *) "";
+ optind++;
+ return '?';
+ }
+ }
+
+ /* Look at and handle the next short option-character. */
+
+ {
+ char c = *nextchar++;
+ char *temp = my_index (optstring, c);
+
+ /* Increment `optind' when we start to process its last character. */
+ if (*nextchar == '\0')
+ ++optind;
+
+ if (temp == NULL || c == ':')
+ {
+ if (opterr)
+ {
+ if (posixly_correct)
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c);
+ else
+ fprintf (stderr, "%s: invalid option -- %c\n", argv[0], c);
+ }
+ optopt = c;
+ return '?';
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ optind++;
+ }
+ else
+ optarg = NULL;
+ nextchar = NULL;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr)
+ {
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, "%s: option requires an argument -- %c\n",
+ argv[0], c);
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+ nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+
+int
+getopt (argc, argv, optstring)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+{
+ return _getopt_internal (argc, argv, optstring,
+ (const struct option *) 0,
+ (int *) 0,
+ 0);
+}
+
+#endif /* _LIBC or not __GNU_LIBRARY__. */
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+ the above definition of `getopt'. */
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+
+ c = getopt (argc, argv, "abc:d:0123456789");
+ if (c == EOF)
+ break;
+
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/examples/LDAP/smbldap-tools/mkntpwd/getopt.h b/examples/LDAP/smbldap-tools/mkntpwd/getopt.h
new file mode 100644
index 00000000000..f3696d955dc
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/mkntpwd/getopt.h
@@ -0,0 +1,133 @@
+/* Declarations for getopt.
+ Copyright (C) 1989, 90, 91, 92, 93, 94 Free Software Foundation, Inc.
+
+This file is part of the GNU C Library. Its master source is NOT part of
+the C library, however. The master source lives in /gd/gnu/lib.
+
+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. */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+ for unrecognized options. */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized. */
+
+extern int optopt;
+
+/* Describe the long-named options requested by the application.
+ The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+ of `struct option' terminated by an element containing a name which is
+ zero.
+
+ The field `has_arg' is:
+ no_argument (or 0) if the option does not take an argument,
+ required_argument (or 1) if the option requires an argument,
+ optional_argument (or 2) if the option takes an optional argument.
+
+ If the field `flag' is not NULL, it points to a variable that is set
+ to the value given in the field `val' when the option is found, but
+ left unchanged if the option is not found.
+
+ To have a long-named option do something other than set an `int' to
+ a compiled-in constant, such as set a value from `optarg', set the
+ option's `flag' field to zero and its `val' field to a nonzero
+ value (the equivalent single-letter option character, if there is
+ one). For long options that have a zero `flag' field, `getopt'
+ returns the contents of the `val' field. */
+
+struct option
+{
+#if defined (__STDC__) && __STDC__
+ const char *name;
+#else
+ char *name;
+#endif
+ /* has_arg can't be an enum because some compilers complain about
+ type mismatches in all the code that assumes it is an int. */
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'. */
+
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
+
+#if defined (__STDC__) && __STDC__
+#ifdef __GNU_LIBRARY__
+/* Many other libraries have conflicting prototypes for getopt, with
+ differences in the consts, in stdlib.h. To avoid compilation
+ errors, only prototype getopt for the GNU C library. */
+extern int getopt (int argc, char *const *argv, const char *shortopts);
+#else /* not __GNU_LIBRARY__ */
+extern int getopt ();
+#endif /* __GNU_LIBRARY__ */
+extern int getopt_long (int argc, char *const *argv, const char *shortopts,
+ const struct option *longopts, int *longind);
+extern int getopt_long_only (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind);
+
+/* Internal only. Users should not call this directly. */
+extern int _getopt_internal (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind,
+ int long_only);
+#else /* not __STDC__ */
+extern int getopt ();
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+#endif /* __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GETOPT_H */
diff --git a/examples/LDAP/smbldap-tools/mkntpwd/md4.c b/examples/LDAP/smbldap-tools/mkntpwd/md4.c
new file mode 100644
index 00000000000..1c9c2e6ecd5
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/mkntpwd/md4.c
@@ -0,0 +1,171 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ a implementation of MD4 designed for use in the SMB authentication protocol
+ Copyright (C) Andrew Tridgell 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.
+*/
+
+
+/* NOTE: This code makes no attempt to be fast!
+
+ It assumes that a int is at least 32 bits long
+*/
+
+typedef unsigned int uint32;
+
+static uint32 A, B, C, D;
+
+static uint32 F(uint32 X, uint32 Y, uint32 Z)
+{
+ return (X&Y) | ((~X)&Z);
+}
+
+static uint32 G(uint32 X, uint32 Y, uint32 Z)
+{
+ return (X&Y) | (X&Z) | (Y&Z);
+}
+
+static uint32 H(uint32 X, uint32 Y, uint32 Z)
+{
+ return X^Y^Z;
+}
+
+static uint32 lshift(uint32 x, int s)
+{
+ x &= 0xFFFFFFFF;
+ return ((x<<s)&0xFFFFFFFF) | (x>>(32-s));
+}
+
+#define ROUND1(a,b,c,d,k,s) a = lshift(a + F(b,c,d) + X[k], s)
+#define ROUND2(a,b,c,d,k,s) a = lshift(a + G(b,c,d) + X[k] + (uint32)0x5A827999,s)
+#define ROUND3(a,b,c,d,k,s) a = lshift(a + H(b,c,d) + X[k] + (uint32)0x6ED9EBA1,s)
+
+/* this applies md4 to 64 byte chunks */
+static void mdfour64(uint32 *M)
+{
+ int j;
+ uint32 AA, BB, CC, DD;
+ uint32 X[16];
+
+ for (j=0;j<16;j++)
+ X[j] = M[j];
+
+ AA = A; BB = B; CC = C; DD = D;
+
+ ROUND1(A,B,C,D, 0, 3); ROUND1(D,A,B,C, 1, 7);
+ ROUND1(C,D,A,B, 2, 11); ROUND1(B,C,D,A, 3, 19);
+ ROUND1(A,B,C,D, 4, 3); ROUND1(D,A,B,C, 5, 7);
+ ROUND1(C,D,A,B, 6, 11); ROUND1(B,C,D,A, 7, 19);
+ ROUND1(A,B,C,D, 8, 3); ROUND1(D,A,B,C, 9, 7);
+ ROUND1(C,D,A,B, 10, 11); ROUND1(B,C,D,A, 11, 19);
+ ROUND1(A,B,C,D, 12, 3); ROUND1(D,A,B,C, 13, 7);
+ ROUND1(C,D,A,B, 14, 11); ROUND1(B,C,D,A, 15, 19);
+
+ ROUND2(A,B,C,D, 0, 3); ROUND2(D,A,B,C, 4, 5);
+ ROUND2(C,D,A,B, 8, 9); ROUND2(B,C,D,A, 12, 13);
+ ROUND2(A,B,C,D, 1, 3); ROUND2(D,A,B,C, 5, 5);
+ ROUND2(C,D,A,B, 9, 9); ROUND2(B,C,D,A, 13, 13);
+ ROUND2(A,B,C,D, 2, 3); ROUND2(D,A,B,C, 6, 5);
+ ROUND2(C,D,A,B, 10, 9); ROUND2(B,C,D,A, 14, 13);
+ ROUND2(A,B,C,D, 3, 3); ROUND2(D,A,B,C, 7, 5);
+ ROUND2(C,D,A,B, 11, 9); ROUND2(B,C,D,A, 15, 13);
+
+ ROUND3(A,B,C,D, 0, 3); ROUND3(D,A,B,C, 8, 9);
+ ROUND3(C,D,A,B, 4, 11); ROUND3(B,C,D,A, 12, 15);
+ ROUND3(A,B,C,D, 2, 3); ROUND3(D,A,B,C, 10, 9);
+ ROUND3(C,D,A,B, 6, 11); ROUND3(B,C,D,A, 14, 15);
+ ROUND3(A,B,C,D, 1, 3); ROUND3(D,A,B,C, 9, 9);
+ ROUND3(C,D,A,B, 5, 11); ROUND3(B,C,D,A, 13, 15);
+ ROUND3(A,B,C,D, 3, 3); ROUND3(D,A,B,C, 11, 9);
+ ROUND3(C,D,A,B, 7, 11); ROUND3(B,C,D,A, 15, 15);
+
+ A += AA; B += BB; C += CC; D += DD;
+
+ A &= 0xFFFFFFFF; B &= 0xFFFFFFFF;
+ C &= 0xFFFFFFFF; D &= 0xFFFFFFFF;
+
+ for (j=0;j<16;j++)
+ X[j] = 0;
+}
+
+static void copy64(uint32 *M, 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, unsigned char *in, int n)
+{
+ unsigned char buf[128];
+ uint32 M[16];
+ uint32 b = n * 8;
+ int i;
+
+ A = 0x67452301;
+ B = 0xefcdab89;
+ C = 0x98badcfe;
+ D = 0x10325476;
+
+ while (n > 64) {
+ copy64(M, in);
+ mdfour64(M);
+ in += 64;
+ n -= 64;
+ }
+
+ for (i=0;i<128;i++)
+ buf[i] = 0;
+ memcpy(buf, in, n);
+ buf[n] = 0x80;
+
+ if (n <= 55) {
+ copy4(buf+56, b);
+ copy64(M, buf);
+ mdfour64(M);
+ } else {
+ copy4(buf+120, b);
+ copy64(M, buf);
+ mdfour64(M);
+ copy64(M, buf+64);
+ mdfour64(M);
+ }
+
+ for (i=0;i<128;i++)
+ buf[i] = 0;
+ copy64(M, buf);
+
+ copy4(out, A);
+ copy4(out+4, B);
+ copy4(out+8, C);
+ copy4(out+12, D);
+
+ A = B = C = D = 0;
+}
+
+
diff --git a/examples/LDAP/smbldap-tools/mkntpwd/mkntpwd.c b/examples/LDAP/smbldap-tools/mkntpwd/mkntpwd.c
new file mode 100644
index 00000000000..0c7d61e1341
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/mkntpwd/mkntpwd.c
@@ -0,0 +1,253 @@
+/*
+ This code is based on work from
+ L0phtcrack 1.5 06.02.97 mudge@l0pht.com
+
+ The code also contains sources from:
+ . routines from the samba code source
+ md4.c smbdes.c
+
+ Anton Roeckseisen (anton@genua.de)
+
+*/
+
+/*
+ * 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 OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "mkntpwd.h"
+
+void str_to_key(unsigned char *,unsigned char *);
+void usage(char *);
+int PutUniCode(char *dst,char *src);
+void printlanhash(char *tmp);
+void mdfour(unsigned char *out, unsigned char *in, int n);
+void E_P16(unsigned char *p14,unsigned char *p16);
+
+
+void main(int argc, char **argv) {
+ extern char *optarg;
+ int c;
+
+ int printlan = 0;
+ char lanpwd[LMPASSWDLEN+1];
+ int printnt = 0;
+ char inputfile[FILENAMEBUFFER+1] = "";
+ FILE* InputFilePtr;
+ int just_pwd = 0;
+ int i;
+ char hashout[17];
+
+ char ntpasswd[NTPASSWDLEN+1];
+ char *hold;
+ unsigned char *p16;
+ int uni_len;
+ char passwd[NTPASSWDLEN+1];
+
+ if (argc==1)
+ usage(argv[0]);
+
+ if (argc==2)
+ just_pwd=1;
+ else
+ just_pwd=0;
+
+ lanpwd[0] = '\0';
+ ntpasswd[0] = '\0';
+
+ while ( (c = getopt(argc, argv, "L:N:f:")) != EOF){
+ switch(c) {
+ case 'L':
+ printlan++;
+ strncpy(lanpwd,optarg,LMPASSWDLEN);
+ lanpwd[LMPASSWDLEN]='\0';
+ for (i=0;i<LMPASSWDLEN;i++)
+ lanpwd[i]=toupper(lanpwd[i]);
+ break;
+ case 'N':
+ printnt++;
+ strncpy(passwd,optarg,NTPASSWDLEN);
+ passwd[NTPASSWDLEN]='\0';
+ break;
+ case 'f':
+ strncpy(inputfile,optarg,FILENAMEBUFFER);
+ inputfile[FILENAMEBUFFER]='\0';
+ break;
+ default:
+ usage(argv[0]);
+ }
+ }
+
+ /* Get password from file or STDIN */
+ if (inputfile[0]!='\0') {
+
+ just_pwd=0; /* make sure no shit is happening... */
+
+ /* get NT-password (longer) */
+ if (strcmp(inputfile,"-")==0) {
+ fgets(passwd,NTPASSWDLEN,stdin);
+ } else {
+ if ((InputFilePtr=fopen(inputfile,"r")) == NULL)
+ fprintf(stderr,"Couldn't open passwordfile: %s",inputfile) ;
+ fgets(passwd,NTPASSWDLEN,InputFilePtr);
+ fclose(InputFilePtr);
+ }
+ while (strlen(passwd)>0 && passwd[strlen(passwd)-1]=='\n')
+ passwd[strlen(passwd)-1]='\0';
+
+ /* create LANMAN-password (shorter) */
+ strncpy(lanpwd,passwd,LMPASSWDLEN);
+ lanpwd[LMPASSWDLEN]='\0';
+ for (i=0;i<LMPASSWDLEN;i++)
+ lanpwd[i]=toupper(lanpwd[i]);
+ printlan++;
+ printnt++;
+
+ }
+
+
+ /* Assume the one and only Arg is the new password! */
+
+ if (argc>1 && just_pwd==1) {
+ strncpy(lanpwd,argv[1],LMPASSWDLEN);
+ lanpwd[LMPASSWDLEN]='\0';
+ for (i=0;i<LMPASSWDLEN;i++)
+ lanpwd[i]=toupper(lanpwd[i]);
+ printlan++;
+
+ strncpy(passwd,argv[1],NTPASSWDLEN);
+ passwd[NTPASSWDLEN]='\0';
+ printnt++;
+ }
+
+ if (printlan >0) {
+ memset(hashout,'\0',17);
+ E_P16((uchar *)lanpwd,hashout);
+ printlanhash(hashout);
+ }
+
+ if (printnt >0) {
+
+ if (printlan>0) printf(":");
+
+ memset(ntpasswd, '\0', sizeof(ntpasswd));
+
+ if (passwd[strlen(passwd)-1] == '\n') /* strip the \n - this
+ is done in LowerString for the case sensitive
+ check */
+ passwd[strlen(passwd)-1] = '\0';
+
+ hold = (char *)malloc(NTPASSWDLEN * 2); /* grab space for
+ unicode */
+ if (hold == NULL){
+ fprintf(stderr, "out of memory...crackntdialog hold\n");
+ exit(1);
+ }
+
+ uni_len = PutUniCode(hold, passwd); /* convert to
+ unicode and return correct
+ unicode length for md4 */
+
+ p16 = (unsigned char*)malloc(17); /* grab space for md4 hash */
+ if (p16 == NULL){
+ fprintf(stderr, "out of memory...crackntdialect p16\n");
+ exit(1);
+ }
+
+ memset(p16,'\0',17);
+ mdfour(p16,hold, uni_len);
+
+ printlanhash(p16);
+
+ free(p16);
+ free(hold);
+ }
+
+ printf("\n");
+
+ exit(0);
+
+}
+
+/*****************************************************************************/
+/*****************************************************************************/
+/*****************************************************************************/
+
+void usage(char *progname){
+ char *p;
+
+ p = strrchr(progname, '\\');
+ if (p == NULL)
+ p = progname;
+ else
+ p++;
+
+ fprintf(stderr, "Usage: %s [-L lanmgrpwd] [-N ntpasswd]\n",p);
+ fprintf(stderr, " %s password\n",p);
+ fprintf(stderr, " %s -f [-] [filename]\n\n",p);
+ fprintf(stderr, " -L lanmgrpasswd LanManager cleartextpwd <= 14 chars\n");
+ fprintf(stderr, " -N ntpasswd NT cleartextpwd <=128 chars (usually <=14)\n\n");
+ fprintf(stderr, " with both options present the encrypted LanManager-Pwd is \n");
+ fprintf(stderr, " printed first, followed by a ':' and the encrypted NT-Pwd.\n\n");
+ fprintf(stderr, " The second usage behaves like %s -L pwd -N pwd\n\n",p);
+ fprintf(stderr, " The third usage reads the password from STDIN or a File. Printout\n");
+ fprintf(stderr, " is the same as second.\n\n");
+ fprintf(stderr, "anton@genua.de\n\n");
+ exit(1);
+}
+
+
+/*******************************************************************
+write a string in unicoode format
+********************************************************************/
+int PutUniCode(char *dst,char *src)
+{
+ int ret = 0;
+ while (*src) {
+ dst[ret++] = src[0];
+ dst[ret++] = 0;
+ src++;
+ }
+ dst[ret++]=0;
+ dst[ret++]=0;
+ return(ret-2); /* the way they do the md4 hash they don't represent
+ the last null. ie 'A' becomes just 0x41 0x00 - not
+ 0x41 0x00 0x00 0x00 */
+}
+
+/*
+ print binary buffer as hex-string
+*/
+void printlanhash(char *tmp) {
+
+ int i;
+ unsigned char c;
+ char outbuffer[33];
+
+
+ /* build string from binary hash */
+ for(i=0;i<16;i++) {
+ c=tmp[i];
+ sprintf(outbuffer+2*i,"%x",(c>>4) & 0x0f);
+ sprintf(outbuffer+2*i+1,"%x",c & 0x0f);
+ }
+
+ /* convert to uppercase */
+ for(i=0;i<32;i++)
+ outbuffer[i] = toupper(outbuffer[i]);
+ outbuffer[32]='\0';
+
+ /* print out hex-string */
+ printf("%s",outbuffer);
+}
+
+
diff --git a/examples/LDAP/smbldap-tools/mkntpwd/mkntpwd.h b/examples/LDAP/smbldap-tools/mkntpwd/mkntpwd.h
new file mode 100644
index 00000000000..9a020b8d286
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/mkntpwd/mkntpwd.h
@@ -0,0 +1,17 @@
+#include <memory.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+
+typedef short int16;
+typedef int int32;
+typedef unsigned short uint16;
+typedef unsigned int uint32;
+typedef unsigned char uchar;
+
+#define MAX_STRING 255
+#define MAX_WORD 128
+#define LMPASSWDLEN 14
+#define NTPASSWDLEN 128
+#define FILENAMEBUFFER 128
diff --git a/examples/LDAP/smbldap-tools/mkntpwd/smbdes.c b/examples/LDAP/smbldap-tools/mkntpwd/smbdes.c
new file mode 100644
index 00000000000..e4f8280f9bc
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/mkntpwd/smbdes.c
@@ -0,0 +1,337 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+
+ a partial implementation of DES designed for use in the
+ SMB authentication protocol
+
+ Copyright (C) Andrew Tridgell 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.
+*/
+
+
+/* NOTES:
+
+ This code makes no attempt to be fast! In fact, it is a very
+ slow implementation
+
+ This code is NOT a complete DES implementation. It implements only
+ the minimum necessary for SMB authentication, as used by all SMB
+ products (including every copy of Microsoft Windows95 ever sold)
+
+ In particular, it can only do a unchained forward DES pass. This
+ means it is not possible to use this code for encryption/decryption
+ of data, instead it is only useful as a "hash" algorithm.
+
+ There is no entry point into this code that allows normal DES operation.
+
+ I believe this means that this code does not come under ITAR
+ regulations but this is NOT a legal opinion. If you are concerned
+ about the applicability of ITAR regulations to this code then you
+ should confirm it for yourself (and maybe let me know if you come
+ up with a different answer to the one above)
+*/
+
+
+
+static int 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 int 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 int 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 int 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 int 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 int 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 int sc[16] = {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1};
+
+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}}};
+
+static void permute(char *out, char *in, int *p, int n)
+{
+ int i;
+ for (i=0;i<n;i++)
+ out[i] = in[p[i]-1];
+}
+
+static void lshift(char *d, int count, int n)
+{
+ char out[64];
+ int i;
+ for (i=0;i<n;i++)
+ out[i] = d[(i+count)%n];
+ for (i=0;i<n;i++)
+ d[i] = out[i];
+}
+
+static void concat(char *out, char *in1, char *in2, int l1, int l2)
+{
+ while (l1--)
+ *out++ = *in1++;
+ while (l2--)
+ *out++ = *in2++;
+}
+
+static void xor(char *out, char *in1, char *in2, int n)
+{
+ int i;
+ for (i=0;i<n;i++)
+ out[i] = in1[i] ^ in2[i];
+}
+
+static void dohash(char *out, char *in, char *key)
+{
+ int i, j, k;
+ char pk1[56];
+ char c[28];
+ char d[28];
+ char cd[56];
+ char ki[16][48];
+ char pd1[64];
+ char l[32], r[32];
+ char rl[64];
+
+ permute(pk1, key, perm1, 56);
+
+ for (i=0;i<28;i++)
+ c[i] = pk1[i];
+ for (i=0;i<28;i++)
+ d[i] = pk1[i+28];
+
+ for (i=0;i<16;i++) {
+ lshift(c, sc[i], 28);
+ lshift(d, sc[i], 28);
+
+ concat(cd, c, d, 28, 28);
+ permute(ki[i], cd, perm2, 48);
+ }
+
+ permute(pd1, in, perm3, 64);
+
+ for (j=0;j<32;j++) {
+ l[j] = pd1[j];
+ r[j] = pd1[j+32];
+ }
+
+ for (i=0;i<16;i++) {
+ char er[48];
+ char erk[48];
+ char b[8][6];
+ char cb[32];
+ char pcb[32];
+ char r2[32];
+
+ permute(er, r, perm4, 48);
+
+ xor(erk, er, ki[i], 48);
+
+ for (j=0;j<8;j++)
+ for (k=0;k<6;k++)
+ b[j][k] = erk[j*6 + k];
+
+ for (j=0;j<8;j++) {
+ int m, n;
+ m = (b[j][0]<<1) | b[j][5];
+
+ n = (b[j][1]<<3) | (b[j][2]<<2) | (b[j][3]<<1) | b[j][4];
+
+ for (k=0;k<4;k++)
+ b[j][k] = (sbox[j][m][n] & (1<<(3-k)))?1:0;
+ }
+
+ for (j=0;j<8;j++)
+ for (k=0;k<4;k++)
+ cb[j*4+k] = b[j][k];
+ permute(pcb, cb, perm5, 32);
+
+ xor(r2, l, pcb, 32);
+
+ for (j=0;j<32;j++)
+ l[j] = r[j];
+
+ for (j=0;j<32;j++)
+ r[j] = r2[j];
+ }
+
+ concat(rl, r, l, 32, 32);
+
+ permute(out, rl, perm6, 64);
+}
+
+static void str_to_key(unsigned char *str,unsigned char *key)
+{
+ int i;
+
+ key[0] = str[0]>>1;
+ key[1] = ((str[0]&0x01)<<6) | (str[1]>>2);
+ key[2] = ((str[1]&0x03)<<5) | (str[2]>>3);
+ key[3] = ((str[2]&0x07)<<4) | (str[3]>>4);
+ key[4] = ((str[3]&0x0F)<<3) | (str[4]>>5);
+ key[5] = ((str[4]&0x1F)<<2) | (str[5]>>6);
+ key[6] = ((str[5]&0x3F)<<1) | (str[6]>>7);
+ key[7] = str[6]&0x7F;
+ for (i=0;i<8;i++) {
+ key[i] = (key[i]<<1);
+ }
+}
+
+
+static void smbhash(unsigned char *out, unsigned char *in, unsigned char *key)
+{
+ 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);
+
+ 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(unsigned char *p14,unsigned char *p16)
+{
+ unsigned char sp8[8] = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25};
+ smbhash(p16, sp8, p14);
+ smbhash(p16+8, sp8, p14+7);
+}
+
+void E_P24(unsigned char *p21, unsigned char *c8, unsigned char *p24)
+{
+ smbhash(p24, c8, p21);
+ smbhash(p24+8, c8, p21+7);
+ smbhash(p24+16, c8, p21+14);
+}
+
+void cred_hash1(unsigned char *out,unsigned char *in,unsigned char *key)
+{
+ unsigned char buf[8];
+
+ smbhash(buf, in, key);
+ smbhash(out, buf, key+9);
+}
+
+void cred_hash2(unsigned char *out,unsigned char *in,unsigned char *key)
+{
+ unsigned char buf[8];
+ static unsigned char key2[8];
+
+ smbhash(buf, in, key);
+ key2[0] = key[7];
+ smbhash(out, buf, key2);
+}
+
diff --git a/examples/LDAP/smbldap-tools/smbldap-groupadd.pl b/examples/LDAP/smbldap-tools/smbldap-groupadd.pl
new file mode 100755
index 00000000000..e242d6e223f
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/smbldap-groupadd.pl
@@ -0,0 +1,158 @@
+#!/usr/bin/perl -w
+
+# This code was developped by IDEALX (http://IDEALX.org/) and
+# contributors (their names can be found in the CONTRIBUTORS file).
+#
+# Copyright (C) 2001-2002 IDEALX
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You 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.
+
+# Purpose of smbldap-groupadd : group (posix) add
+
+use strict;
+use FindBin;
+use FindBin qw($RealBin);
+use lib "$RealBin/";
+use smbldap_tools;
+use smbldap_conf;
+use Getopt::Std;
+my %Options;
+
+my $ok = getopts('ag:or:s:t:p?', \%Options);
+if ( (!$ok) || (@ARGV < 1) || ($Options{'?'}) ) {
+ print "Usage: $0 [-agorst?] groupname\n";
+ print " -a add automatic group mapping entry\n";
+ print " -g gid\n";
+ print " -o gid is not unique\n";
+ print " -r group-rid\n";
+ print " -s group-sid\n";
+ print " -t group-type\n";
+ print " -p print the gidNumber to stdout\n";
+ print " -? show this help message\n";
+ exit (1);
+}
+
+my $_groupName = $ARGV[0];
+
+if (defined(get_group_dn($_groupName))) {
+ warn "$0: group $_groupName exists\n";
+ exit (6);
+}
+
+my $_groupGidNumber = $Options{'g'};
+if (! defined ($_groupGidNumber = group_add($_groupName, $_groupGidNumber, $Options{'o'}))) {
+ warn "$0: error adding group $_groupName\n";
+ exit (6);
+}
+
+my $group_sid;
+my $tmp;
+if ($tmp= $Options{'s'}) {
+ if ($tmp =~ /^S-(?:\d+-)+\d+$/) {
+ $group_sid = $tmp;
+ } else {
+ warn "$0: illegal group-rid $tmp\n";
+ exit(7);
+ }
+} elsif ($Options{'r'} || $Options{'a'}) {
+ my $group_rid;
+ if ($tmp= $Options{'r'}) {
+ if ($tmp =~ /^\d+$/) {
+ $group_rid = $tmp;
+ } else {
+ warn "$0: illegal group-rid $tmp\n";
+ exit(7);
+ }
+ } else {
+ # algorithmic mapping
+ $group_rid = 2*$_groupGidNumber+1001;
+ }
+ $group_sid = $SID.'-'.$group_rid;
+}
+
+if ($Options{'r'} || $Options{'a'} || $Options{'s'}) {
+ # let's test if this SID already exist
+ my $test_exist_sid=does_sid_exist($group_sid,$groupsdn);
+ if ($test_exist_sid->count == 1) {
+ warn "Group SID already owned by\n";
+ # there should not exist more than one entry, but ...
+ foreach my $entry ($test_exist_sid->all_entries) {
+ my $dn= $entry->dn;
+ chomp($dn);
+ warn "$dn\n";
+ }
+ exit(7);
+ }
+}
+
+if ($group_sid) {
+ my $group_type;
+ my $tmp;
+ if ($tmp= $Options{'t'}) {
+ unless (defined($group_type = &group_type_by_name($tmp))) {
+ warn "$0: unknown group type $tmp\n";
+ exit(8);
+ }
+ } else {
+ $group_type = group_type_by_name('domain');
+ }
+ my $ldap_master=connect_ldap_master();
+ my $modify = $ldap_master->modify ( "cn=$_groupName,$groupsdn",
+ add => {
+ 'objectClass' => 'sambaGroupMapping',
+ 'sambaSID' => $group_sid,
+ 'sambaGroupType' => $group_type
+ }
+ );
+ $modify->code && warn "failed to delete entry: ", $modify->error ;
+ # take down session
+ $ldap_master->unbind
+}
+
+if ($Options{'p'}) {
+ print STDOUT "$_groupGidNumber";
+}
+exit(0);
+
+########################################
+
+=head1 NAME
+
+ smbldap-groupadd.pl - Create a new group
+
+=head1 SYNOPSIS
+
+ smbldap-groupadd.pl [-g gid [-o]] group
+
+=head1 DESCRIPTION
+ The smbldap-groupadd.pl command creates a new group account using
+ the values specified on the command line and the default values
+ from the system. The new group will be entered into the system
+ files as needed. The options which apply to the groupadd command are
+
+ -g gid The numerical value of the group's ID. This value must be
+ unique, unless the -o option is used. The value must be non-
+ negative. The default is to use the smallest ID value greater
+ than 1000 and greater than every other group.
+
+=head1 SEE ALSO
+
+ groupadd(1)
+
+=cut
+
+#'
+
diff --git a/examples/LDAP/smbldap-tools/smbldap-groupdel.pl b/examples/LDAP/smbldap-tools/smbldap-groupdel.pl
new file mode 100755
index 00000000000..4f6839ebe59
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/smbldap-groupdel.pl
@@ -0,0 +1,93 @@
+#!/usr/bin/perl -w
+
+# This code was developped by IDEALX (http://IDEALX.org/) and
+# contributors (their names can be found in the CONTRIBUTORS file).
+#
+# Copyright (C) 2001-2002 IDEALX
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You 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.
+
+# Purpose of smbldap-groupdel : group (posix) deletion
+
+use strict;
+use FindBin;
+use FindBin qw($RealBin);
+use lib "$RealBin/";
+use smbldap_tools;
+use smbldap_conf;
+
+#####################
+use Getopt::Std;
+my %Options;
+
+my $ok = getopts('?', \%Options);
+if ( (!$ok) || (@ARGV < 1) || ($Options{'?'}) ) {
+ print "Usage: $0 groupname\n";
+ print " -? show this help message\n";
+ exit (1);
+}
+
+my $_groupName = $ARGV[0];
+
+my $dn_line;
+if (!defined($dn_line = get_group_dn($_groupName))) {
+ print "$0: group $_groupName doesn't exist\n";
+ exit (6);
+}
+
+my $dn = get_dn_from_line($dn_line);
+
+group_del($dn);
+
+my $nscd_status = system "/etc/init.d/nscd status >/dev/null 2>&1";
+
+if ($nscd_status == 0) {
+ system "/etc/init.d/nscd restart > /dev/null 2>&1";
+}
+
+#if (defined($dn_line = get_group_dn($_groupName))) {
+# print "$0: failed to delete group\n";
+# exit (7);
+#}
+
+
+exit (0);
+
+############################################################
+
+=head1 NAME
+
+ smbldap-groupdel.pl - Delete a group
+
+=head1 SYNOPSIS
+
+ smbldap-groupdel.pl group
+
+=head1 DESCRIPTION
+
+ The smbldap-groupdel.pl command modifies the system account files,
+ deleting all entries that refer to group. The named group must exist.
+
+ You must manually check all filesystems to insure that no files remain
+ with the named group as the file group ID.
+
+=head1 SEE ALSO
+
+ groupdel(1)
+
+=cut
+
+#'
diff --git a/examples/LDAP/smbldap-tools/smbldap-groupmod.pl b/examples/LDAP/smbldap-tools/smbldap-groupmod.pl
new file mode 100755
index 00000000000..3f9741e0152
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/smbldap-groupmod.pl
@@ -0,0 +1,283 @@
+#!/usr/bin/perl -w
+
+# This code was developped by IDEALX (http://IDEALX.org/) and
+# contributors (their names can be found in the CONTRIBUTORS file).
+#
+# Copyright (C) 2001-2002 IDEALX
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You 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.
+
+# Purpose of smbldap-groupmod : group (posix) modification
+
+
+use strict;
+use FindBin;
+use FindBin qw($RealBin);
+use lib "$RealBin/";
+use smbldap_tools;
+use smbldap_conf;
+
+#####################
+
+use Getopt::Std;
+my %Options;
+
+my $ok = getopts('ag:n:m:or:s:t:x:?', \%Options);
+if ( (!$ok) || (@ARGV < 1) || ($Options{'?'}) ) {
+ print "Usage: $0 [-a] [-g gid [-o]] [-n name] [-m members(,)] [-x members (,)] [-r rid] [-s sid] [-t type] groupname\n";
+ print " -a add automatic group mapping entry\n";
+ print " -g new gid\n";
+ print " -o gid is not unique\n";
+ print " -n new group name\n";
+ print " -m add members (comma delimited)\n";
+ print " -r group-rid\n";
+ print " -s group-sid\n";
+ print " -t group-type\n";
+ print " -x delete members (comma delimted)\n";
+ print " -? show this help message\n";
+ exit (1);
+}
+
+my $groupName = $ARGV[0];
+my $group_entry;
+
+if (! ($group_entry = read_group_entry($groupName))) {
+ print "$0: group $groupName doesn't exist\n";
+ exit (6);
+}
+
+my $newname = $Options{'n'};
+
+my $nscd_status = system "/etc/init.d/nscd status >/dev/null 2>&1";
+
+if ($nscd_status == 0) {
+ system "/etc/init.d/nscd restart > /dev/null 2>&1";
+}
+
+my $gid = getgrnam($groupName);
+unless (defined ($gid)) {
+ print "$0: group $groupName not found!\n";
+ exit(6);
+}
+
+my $tmp;
+if (defined($tmp = $Options{'g'}) and $tmp =~ /\d+/) {
+ if (!defined($Options{'o'})) {
+ if (defined(getgrgid($tmp))) {
+ print "$0: gid $tmp exists\n";
+ exit (6);
+ }
+ }
+ if (!($gid == $tmp)) {
+ my $ldap_master=connect_ldap_master();
+ my $modify = $ldap_master->modify ( "cn=$groupName,$groupsdn",
+ changes => [
+ replace => [gidNumber => $tmp]
+ ]
+ );
+ $modify->code && die "failed to modify entry: ", $modify->error ;
+ # take down session
+ $ldap_master->unbind
+ }
+}
+
+
+if (defined($newname)) {
+ my $ldap_master=connect_ldap_master();
+ my $modify = $ldap_master->moddn (
+ "cn=$groupName,$groupsdn",
+ newrdn => "cn=$newname",
+ deleteoldrdn => "1",
+ newsuperior => "$groupsdn"
+ );
+ $modify->code && die "failed to modify entry: ", $modify->error ;
+ # take down session
+ $ldap_master->unbind
+}
+
+# Add members
+if (defined($Options{'m'})) {
+ my $members = $Options{'m'};
+ my @members = split( /,/, $members );
+ my $member;
+ foreach $member ( @members ) {
+ my $group_entry=read_group_entry($groupName);
+ $groupsdn=$group_entry->dn;
+ if (is_unix_user($member)) {
+ if (is_group_member($groupsdn,$member)) {
+ print "User $member already in the group\n";
+ } else {
+ print "adding user $member to group $groupName\n";
+ my $ldap_master=connect_ldap_master();
+ my $modify = $ldap_master->modify ($groupsdn,
+ changes => [
+ add => [memberUid => $member]
+ ]
+ );
+ $modify->code && warn "failed to add entry: ", $modify->error ;
+ # take down session
+ $ldap_master->unbind
+ }
+ } else {
+ print "User $member does not exist: create it first !\n";
+ }
+ }
+}
+
+# Delete members
+if (defined($Options{'x'})) {
+ my $members = $Options{'x'};
+ my @members = split( /,/, $members );
+ my $member;
+ foreach $member ( @members ) {
+ my $group_entry=read_group_entry($groupName);
+ $groupsdn=$group_entry->dn;
+ if (is_group_member("$groupsdn",$member)) {
+ print "deleting user $member from group $groupName\n";
+ my $ldap_master=connect_ldap_master();
+ my $modify = $ldap_master->modify ($groupsdn,
+ changes => [
+ delete => [memberUid => $member]
+ ]
+ );
+ $modify->code && warn "failed to delete entry: ", $modify->error ;
+ # take down session
+ $ldap_master->unbind
+ } else {
+ print "User $member is not in the group $groupName!\n";
+ }
+ }
+}
+
+my $group_sid;
+if ($tmp= $Options{'s'}) {
+ if ($tmp =~ /^S-(?:\d+-)+\d+$/) {
+ $group_sid = $tmp;
+ } else {
+ print "$0: illegal group-rid $tmp\n";
+ exit(7);
+ }
+} elsif ($Options{'r'} || $Options{'a'}) {
+ my $group_rid;
+ if ($tmp= $Options{'r'}) {
+ if ($tmp =~ /^\d+$/) {
+ $group_rid = $tmp;
+ } else {
+ print "$0: illegal group-rid $tmp\n";
+ exit(7);
+ }
+ } else {
+ # algorithmic mapping
+ $group_rid = 2*$gid+1001;
+ }
+ $group_sid = $SID.'-'.$group_rid;
+}
+
+if ($group_sid) {
+ my @adds;
+ my @mods;
+ push(@mods, 'sambaSID' => $group_sid);
+
+ if ($tmp= $Options{'t'}) {
+ my $group_type;
+ if (defined($group_type = &group_type_by_name($tmp))) {
+ push(@mods, 'sambaGroupType' => $group_type);
+ } else {
+ print "$0: unknown group type $tmp\n";
+ exit(8);
+ }
+ } else {
+ if (! defined($group_entry->get_value('sambaGroupType'))) {
+ push(@mods, 'sambaGroupType' => group_type_by_name('domain'));
+ }
+ }
+
+ my @oc = $group_entry->get_value('objectClass');
+ unless (grep($_ =~ /^sambaGroupMapping$/i, @oc)) {
+ push (@adds, 'objectClass' => 'sambaGroupMapping');
+ }
+
+ my $ldap_master=connect_ldap_master();
+ my $modify = $ldap_master->modify ( "cn=$groupName,$groupsdn",
+ changes => [
+ 'add' => [ @adds ],
+ 'replace' => [ @mods ]
+ ]
+ );
+ $modify->code && warn "failed to delete entry: ", $modify->error ;
+ # take down session
+ $ldap_master->unbind
+}
+
+$nscd_status = system "/etc/init.d/nscd status >/dev/null 2>&1";
+
+if ($nscd_status == 0) {
+ system "/etc/init.d/nscd restart > /dev/null 2>&1";
+}
+
+exit (0);
+
+############################################################
+
+=head1 NAME
+
+smbldap-groupmod.pl - Modify a group
+
+=head1 SYNOPSIS
+
+smbldap-groupmod.pl [-g gid [-o]] [-n group_name ] group
+
+=head1 DESCRIPTION
+
+The smbldap-groupmod.pl command modifies the system account files to
+ reflect the changes that are specified on the command line.
+ The options which apply to the smbldap-groupmod command are
+
+ -g gid The numerical value of the group's ID. This value must be
+ unique, unless the -o option is used. The value must be non-
+ negative. Any files which the old group ID is the file
+ group ID must have the file group ID changed manually.
+
+ -n group_name
+ The name of the group will be changed from group to group_name.
+
+ -m members
+ The members to be added to the group in comma-delimeted form.
+
+ -x members
+ The members to be removed from the group in comma-delimted form.
+
+=head1 EXAMPLES
+
+ smbldap-groupmod.pl -g 253 development
+ This will change the GID of the 'development' group to '253'.
+
+ smbldap-groupmod.pl -n Idiots Managers
+ This will change the name of the 'Managers' group to 'Idiots'.
+
+ smbldap-groupmod.pl -m "jdoe,jsmith" "Domain Admins"
+ This will add 'jdoe' and 'jsmith' to the 'Domain Admins' group.
+
+ smbldap-groupmod.pl -x "jdoe,jsmith" "Domain Admins"
+ This will remove 'jdoe' and 'jsmith' from the 'Domain Admins' group.
+
+=head1 SEE ALSO
+
+ groupmod(1)
+
+=cut
+
+#'
diff --git a/examples/LDAP/smbldap-tools/smbldap-groupshow.pl b/examples/LDAP/smbldap-tools/smbldap-groupshow.pl
new file mode 100755
index 00000000000..a9d368763ee
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/smbldap-groupshow.pl
@@ -0,0 +1,74 @@
+#!/usr/bin/perl -w
+
+# This code was developped by IDEALX (http://IDEALX.org/) and
+# contributors (their names can be found in the CONTRIBUTORS file).
+#
+# Copyright (C) 2001-2002 IDEALX
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You 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.
+
+# Purpose of smbldap-groupshow : user (posix,shadow,samba) display
+#
+# History :
+# . originally by David Le Corfec <david.le-corfec@IDEALX.com>
+
+use strict;
+use FindBin;
+use FindBin qw($RealBin);
+use lib "$RealBin/";
+use smbldap_tools;
+use Getopt::Std;
+my %Options;
+
+my $ok = getopts('?', \%Options);
+
+if ( (!$ok) || (@ARGV < 1) || ($Options{'?'}) ) {
+ print "Usage: $0 [-?] group\n";
+ print " -? show this help message\n";
+ exit (1);
+}
+
+# Read only first @ARGV
+my $group = $ARGV[0];
+
+my $lines = read_group($group);
+if (!defined($lines)) {
+ print "$0: group $group doesn't exist\n";
+ exit (1);
+}
+
+print "$lines\n";
+
+exit(0);
+
+############################################################
+
+=head1 NAME
+
+ smbldap-groupshow.pl - Display group informations
+
+=head1 SYNOPSIS
+
+ smbldap-groupshow.pl groupname
+
+=head1 DESCRIPTION
+
+ The smbldap-groupshow.pl command displays informations
+ associated with the given group.
+
+=cut
+
+#'
diff --git a/examples/LDAP/smbldap-tools/smbldap-migrate-accounts.pl b/examples/LDAP/smbldap-tools/smbldap-migrate-accounts.pl
new file mode 100755
index 00000000000..54e4d7f7e3f
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/smbldap-migrate-accounts.pl
@@ -0,0 +1,230 @@
+#!/usr/bin/perl -w
+
+# This code was developped by IDEALX (http://IDEALX.org/) and
+# contributors (their names can be found in the CONTRIBUTORS file).
+#
+# Copyright (C) 2002 IDEALX
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You 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.
+
+# Purpose of smbldap-migrate-accounts : add NT sam entries from pwdump
+# to ldap
+
+use strict;
+use Getopt::Std;
+use FindBin;
+use FindBin qw($RealBin);
+use lib "$RealBin/";
+use smbldap_tools;
+use smbldap_conf;
+
+# smbldap-migrate.pl (-? or -h for help)
+#
+# Read pwdump entries on stdin, and add them to the ldap server.
+# Output uncreated/unmodified entries (see parameters -C -U)
+# in pwdump format to stdout.
+# Errors, debug and stats are output to stderr.
+
+sub modify_account
+ {
+ my ($login, $basedn, $lmpwd, $ntpwd, $gecos, $homedir) = @_;
+ # bind to a directory with dn and password
+ my $ldap_master=connect_ldap_master();
+ my $modify = $ldap_master->modify ("uid=$login,$basedn",
+ changes => [
+ replace => [sambaLMPassword => "$lmpwd"],
+ replace => [sambaNTpassword => "$ntpwd"],
+ replace => [gecos => "$gecos"],
+ replace => [sambaHomePath => "$homedir"]
+ ]
+ );
+ $modify->code && die "failed to modify entry: ", $modify->error ;
+ # take down the session
+ $ldap_master->unbind;
+ }
+
+#####################
+
+
+my %Options;
+
+my $ok = getopts('awA:CUW:?h', \%Options);
+
+if ( (!$ok) || ($Options{'?'}) || ($Options{'h'}) ) {
+ print "Usage: $0 [-awAWCU?]\n";
+ print " -a process only people, ignore computers\n";
+ print " -w process only computers, ignore persons\n";
+ print " -A <opts> option string passed verbatim to smbldap-useradd for persons\n";
+ print " -W <opts> option string passed verbatim to smbldap-useradd for computers\n";
+ print " -C if entry not found, don't create it and log it to stdout (default: create it)\n";
+ print " -U if entry found, don't update it and log it to stdout (default: update it)\n";
+ print " -?|-h show this help message\n";
+ exit (1);
+}
+
+my %processed = ( 'user' => 0, 'machine' => 0);
+my %created = ( 'user' => 0, 'machine' => 0);
+my %updated = ( 'user' => 0, 'machine' => 0);
+my %logged = ( 'user' => 0, 'machine' => 0);
+my %errors = ( 'user' => 0, 'machine' => 0);
+my %existing = ( 'user' => 0, 'machine' => 0);
+my $specialskipped = 0;
+
+while (<>) {
+ my ($login, $rid, $lmpwd, $ntpwd, $gecos, $homedir, $b) = split(/:/, $_);
+ my $usertype;
+ my $userbasedn;
+
+ my $entry_type = 'user';
+
+ if ($login =~ m/.*\$$/ ) { # computer
+ $processed{'machine'}++;
+ $entry_type = 'machine';
+ if (defined($Options{'a'})) {
+ print STDERR "ignoring $login\n";
+ next;
+ }
+
+ $usertype = "-w $Options{'W'}";
+ $userbasedn = $computersdn;
+ } else { # people
+ $processed{'user'}++;
+ if (defined($Options{'w'})) {
+ print STDERR "ignoring $login\n";
+ next;
+ }
+ if ($rid < 1000) {
+ $specialskipped++;
+ print STDERR "$login seems to be a special Win account (rid=$rid), skipping\n";
+ next;
+ }
+
+ $usertype = "-a $Options{'A'}";
+ $userbasedn = $usersdn;
+ }
+
+ # normalize homedir
+ # uncomment to replace configured share with share from pwdump
+ # if ($homedir eq "") {
+ $homedir = $_userSmbHome;
+ # }
+
+ # normalize gecos
+ if (!($gecos eq "")) {
+ $gecos =~ tr/ÁÀÂÄáàâäÇçÉÈÊËÆéèêëæÍÌÏÎíìîÏÑñÓÒÔÖóòôöÚÙÜÛúùüûÝýÿ/AAAAaaaaCcEEEEEeeeeeIIIIiiiiNnOOOOooooUUUUuuuuYyy/;
+ } else {
+ $gecos = $_userGecos;
+ }
+
+ my $user_exists = is_samba_user($login);
+
+ if (!$user_exists) {
+ if (!defined($Options{'C'})) {
+ # uid doesn't exist and we want to create it
+ my $addcmd = "/usr/local/sbin/smbldap-useradd.pl $usertype $login > /dev/null";
+ print STDERR "$addcmd\n";
+ my $r = system "$addcmd";
+ if ($r != 0) {
+ print STDERR "error adding $login, skipping\n";
+ next;
+ }
+ # lem modif... a retirer si pb
+ if ($entry_type eq "user") {
+ modify_account($login, $userbasedn, $lmpwd, $ntpwd, $gecos, $homedir);
+ }
+
+ $created{$entry_type}++;
+ } else { # uid doesn't exist and no create => log
+ print "$_";
+ $logged{$entry_type}++;
+ }
+ } else { # account exists
+ $existing{$entry_type}++;
+ if (!defined($Options{'U'})) { # exists and modify
+ modify_account($login, $userbasedn, $lmpwd, $ntpwd, $gecos, $homedir);
+ $updated{$entry_type}++;
+ } else { # exists and log
+ print "$_";
+ $logged{$entry_type}++;
+ }
+ }
+}
+
+my $sum;
+
+$sum = $processed{'user'} + $processed{'machine'};
+print STDERR "processed: all=$sum user=$processed{'user'} machine=$processed{'machine'}\n";
+
+$sum = $existing{'user'} + $existing{'machine'};
+print STDERR "existing: all=$sum user=$existing{'user'} machine=$existing{'machine'}\n";
+
+$sum = $created{'user'} + $created{'machine'};
+print STDERR "created: all=$sum user=$created{'user'} machine=$created{'machine'}\n";
+
+$sum = $updated{'user'} + $updated{'machine'};
+print STDERR "updated: all=$sum user=$updated{'user'} machine=$updated{'machine'}\n";
+
+$sum = $logged{'user'} + $logged{'machine'};
+print STDERR "logged: all=$sum user=$logged{'user'} machine=$logged{'machine'}\n";
+
+print STDERR "special users skipped: $specialskipped\n";
+
+
+########################################
+
+=head1 NAME
+
+smbldap-migrate.pl - Migrate NT accounts to LDAP
+
+=head1 SYNOPSIS
+
+ smbldap-migrate.pl [-a] [-w] [-A opts] [-W opts] [-C] [-U] [-?]
+
+=head1 DESCRIPTION
+
+ This command reads from stdin account entries as created by pwdump,
+ a tool to dump an user database on NT.
+ Depending of the options, some account entries may be output on
+ stdout. All errors and informations are sent to stderr.
+
+ -a process only people, ignore computers
+
+ -w process only computers, ignore persons
+
+ -A opts
+ a string containing arguments to pass verbatim to
+ smbldap-useradd when adding users, eg "-m -x".
+ You don't have to specify -a in this string.
+
+ -W opts
+ a string containing arguments to pass verbatim to
+ smbldap-useradd when adding computers, eg "-m -x".
+ You don't have to specify -w in this string.
+
+ -C if NT account not found in LDAP, don't create it and log it to stdout
+ (default: create it)
+
+ -U if NT account found in LDAP, don't update it and log it to stdout
+ (default: update it)
+
+ -? show the help message
+
+=cut
+
+#'
+
+# The End
+
diff --git a/examples/LDAP/smbldap-tools/smbldap-migrate-groups.pl b/examples/LDAP/smbldap-tools/smbldap-migrate-groups.pl
new file mode 100644
index 00000000000..a2b07bf817c
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/smbldap-migrate-groups.pl
@@ -0,0 +1,225 @@
+#!/usr/bin/perl
+
+# This code was developped by IDEALX (http://IDEALX.org/) and
+# contributors (their names can be found in the CONTRIBUTORS file).
+#
+# Copyright (C) 2002 IDEALX
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You 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.
+
+# Purpose of smbldap-migrate-groups : to parse a Windows
+# group dump and populate Unix groups
+# Reads group dump on stdin
+
+
+use strict;
+use FindBin;
+use FindBin qw($RealBin);
+use lib "$RealBin/";
+use smbldap_tools;
+use smbldap_conf;
+use Getopt::Std;
+
+sub process_rec_group
+ {
+ my ($group, $mb) = @_;
+ my @members;
+
+ if (!(@members = group_get_members($group))) {
+ return 0;
+ }
+
+ foreach my $m (@members) {
+ if ( !($m =~ m/^\*/) ) {
+ push @{$mb}, $m;
+ } else {
+ my $gname = $m;
+ $gname =~ s/^.//;
+ if (!process_rec_group($gname, $mb)) {
+ print "recursive group not added : $gname\n";
+ }
+ }
+ }
+ }
+
+
+# given a group dn and a list of members, update the group
+sub modify_group
+ {
+ my ($group, $dn_line, @members, $recgroup) = @_;
+ my $m;
+ my @new_mb;
+
+ foreach $m (@members) {
+ if ( ($m =~ m/^\*/) ) {
+ my $gname = $m;
+ $gname =~ s/^.//;
+ if (!$recgroup) {
+ print "recursive group not added : $gname\n";
+ } else {
+ if (!process_rec_group($gname, \@new_mb)) {
+ print "recursive group not added : $gname\n";
+ }
+ }
+ } else {
+ push @new_mb, $m;
+ }
+ }
+
+ # new_mb contains flat members from group dump
+ # now append them to existing members
+ push @new_mb, group_get_members($group);
+ # uniq them
+ my %saw;
+ @saw{@new_mb} = ();
+ @new_mb = keys %saw;
+
+ my $nmb = $#new_mb + 1;
+ print STDERR "Group $group now has $nmb member(s)\n";
+
+ my $mbs;
+ foreach $m (@new_mb) {
+ $mbs .= "memberUid: $m\n";
+ }
+
+ my $mods="$dn_line
+changetype: modify
+replace: memberUid
+$mbs
+";
+
+ #print "$mods\n";
+ my $tmpldif =
+ "$mods
+";
+
+ die "$0: error while modifying group $group\n"
+ unless (do_ldapmodify($tmpldif) == 0);
+ undef $tmpldif;
+ }
+
+sub display_group
+ {
+ my ($group, @members) = @_;
+
+ print "Group name $group\n";
+ print "Members\n";
+ my $m;
+ my $i = 0;
+ foreach $m (@members) {
+ print "$m ";
+ if ($i % 5 == 0) {
+ print "\n";
+ }
+ $i++;
+ }
+ }
+
+sub process_group
+ {
+ my ($group, @members, $nocreate, $noupdate, $recgroup) = @_;
+
+ my $dn_line;
+ if (!defined($dn_line = get_group_dn($group))) {
+ # group not found, create it ?
+ if (!$nocreate) {
+ system "/usr/local/sbin/smbldap-groupadd.pl \"$group\"; sleep 5";
+ if (!defined($dn_line = get_group_dn($group))) {
+ return 1;
+ }
+ modify_group($group, $dn_line, @members, $recgroup);
+ } else {
+ # don't create
+ print "not created:\n";
+ display_group($group, @members);
+ }
+ } else {
+ # group found, update it ?
+ if (!$noupdate) {
+ modify_group($group, $dn_line, @members, $recgroup);
+ } else {
+ # don't update
+ print "not updated:\n";
+ display_group($group, @members);
+ }
+ }
+ }
+
+###################################################
+
+my %Options;
+
+my $ok = getopts('CUr?', \%Options);
+if ( (!$ok) || ($Options{'?'}) ) {
+ print "Usage: $0 [-CUr?] < group_dump\n";
+ print " -C don't create group if it doesn't exist\n";
+ print " -U don't update group if it exists\n";
+ print " -r recursively process groups\n";
+ exit(1);
+}
+
+my $group_name;
+my $group_desc;
+my $has_members = 0;
+my @members = ();
+
+while (<>) {
+ my $line = $_;
+ chomp($line);
+ next if ( $line =~ m/^\s*$/ );
+
+ if ($group_name eq "") {
+ if ( $line =~ m/^Group name\s+(.+).$/ ) {
+ $group_name = $1;
+ next;
+ }
+ }
+ if ($group_desc eq "") {
+ if ( $line =~ m/^Comment\s+(.*)$/ ) {
+ $group_desc = $1;
+ next;
+ }
+ }
+ next if ( $line =~ m/^-+.$/ );
+ if (!$has_members) {
+ if ( $line =~ m/^Members/ ) {
+ $has_members = 1;
+ next;
+ }
+ } else {
+ if ( $line =~ m/^The command completed successfully/ ) {
+ last;
+ } else {
+ push(@members, split(/\s+/, $line));
+ next;
+ }
+ }
+
+ #print;
+}
+
+if ( $#members > -1) {
+ process_group($group_name, @members, $Options{'C'}, $Options{'U'}, $Options{'r'});
+}
+
+#print "gn=$group_name\n";
+#print "gd=$group_desc\n";
+#my $m;
+#foreach $m (@members)
+#{
+# print "$m ";
+#}
+#print "\n";
diff --git a/examples/LDAP/smbldap-tools/smbldap-passwd.pl b/examples/LDAP/smbldap-tools/smbldap-passwd.pl
new file mode 100755
index 00000000000..afbc87a058d
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/smbldap-passwd.pl
@@ -0,0 +1,227 @@
+#!/usr/bin/perl -w
+
+# LDAP to unix password sync script for samba
+
+# This code was developped by IDEALX (http://IDEALX.org/) and
+# contributors (their names can be found in the CONTRIBUTORS file).
+#
+# Copyright (C) 2001-2002 IDEALX
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You 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.
+
+# Purpose :
+# . ldap-unix passwd sync for SAMBA>2.2.2 + LDAP
+# . may also replace /bin/passwd
+
+use strict;
+use FindBin;
+use FindBin qw($RealBin);
+use lib "$RealBin/";
+use smbldap_tools;
+use smbldap_conf;
+
+my $user;
+my $oldpass;
+my $ret;
+
+my $arg;
+
+foreach $arg (@ARGV) {
+ if ($< != 0) {
+ die "Only root can specify parameters\n";
+ } else {
+ if ( ($arg eq '-?') || ($arg eq '--help') ) {
+ print "Usage: $0 [username]\n";
+ print " -?, --help show this help message\n";
+ exit (6);
+ } elsif (substr($arg,0) ne '-') {
+ $user = $arg;
+ }
+ $oldpass = 1;
+ }
+}
+
+if (!defined($user)) {
+ $user=$ENV{"USER"};
+}
+
+# test existence of user in LDAP
+my $dn_line;
+if (!defined($dn_line = get_user_dn($user))) {
+ print "$0: user $user doesn't exist\n";
+ exit (10);
+}
+
+my $dn = get_dn_from_line($dn_line);
+
+my $samba = is_samba_user($user);
+
+print "Changing password for $user\n";
+
+# non-root user
+if (!defined($oldpass)) {
+ # prompt for current password
+ system "stty -echo";
+ print "(current) UNIX password: ";
+ chomp($oldpass=<STDIN>);
+ print "\n";
+ system "stty echo";
+
+ if (!is_user_valid($user, $dn, $oldpass)) {
+ print "Authentication failure\n";
+ exit (10);
+ }
+}
+
+# prompt for new password
+
+my $pass;
+my $pass2;
+
+system "stty -echo";
+print "New password : ";
+chomp($pass=<STDIN>);
+print "\n";
+system "stty echo";
+
+system "stty -echo";
+print "Retype new password : ";
+chomp($pass2=<STDIN>);
+print "\n";
+system "stty echo";
+
+if ($pass ne $pass2) {
+ print "New passwords don't match!\n";
+ exit (10);
+}
+
+# First, connecting to the directory
+my $ldap_master=connect_ldap_master();
+
+# only modify smb passwords if smb user
+if ($samba == 1) {
+ if (!$with_smbpasswd) {
+ # generate LanManager and NT clear text passwords
+ if ($mk_ntpasswd eq '') {
+ print "Either set \$with_smbpasswd = 1 or specify \$mk_ntpasswd\n";
+ exit(1);
+ }
+ my $ntpwd = `$mk_ntpasswd '$pass'`;
+ chomp(my $sambaLMPassword = substr($ntpwd, 0, index($ntpwd, ':')));
+ chomp(my $sambaNTPassword = substr($ntpwd, index($ntpwd, ':')+1));
+ # the sambaPwdLastSet must be updating
+ my $date=time;
+ my @mods;
+ push(@mods, 'sambaLMPassword' => $sambaLMPassword);
+ push(@mods, 'sambaNTPassword' => $sambaNTPassword);
+ push(@mods, 'sambaPwdLastSet' => $date);
+ if (defined $_defaultMaxPasswordAge) {
+ my $new_sambaPwdMustChange=$date+$_defaultMaxPasswordAge*24*60*60;
+ push(@mods, 'sambaPwdMustChange' => $new_sambaPwdMustChange);
+ push(@mods, 'sambaAcctFlags' => '[U]');
+ }
+ # Let's change nt/lm passwords
+ my $modify = $ldap_master->modify ( "$dn",
+ 'replace' => { @mods }
+ );
+ $modify->code && warn "failed to modify entry: ", $modify->error ;
+
+ } else {
+ if ($< != 0) {
+ my $FILE="|$smbpasswd -s >/dev/null";
+ open (FILE, $FILE) || die "$!\n";
+ print FILE <<EOF;
+'$oldpass'
+'$pass'
+'$pass'
+EOF
+ ;
+ close FILE;
+ } else {
+ my $FILE="|$smbpasswd $user -s >/dev/null";
+ open (FILE, $FILE) || die "$!\n";
+ print FILE <<EOF;
+'$pass'
+'$pass'
+EOF
+ ;
+ close FILE;
+ }
+ }
+}
+
+# change unix password
+my $hash_password = `slappasswd -h {$hash_encrypt} -s '$pass'`;
+chomp($hash_password);
+my $modify = $ldap_master->modify ( "$dn",
+ changes => [
+ replace => [userPassword => "$hash_password"]
+ ]
+ );
+$modify->code && warn "Unable to change password : ", $modify->error ;
+
+# take down session
+$ldap_master->unbind;
+
+exit 0;
+
+
+# - The End
+
+=head1 NAME
+
+smbldap-passwd.pl - change user password
+
+=head1 SYNOPSIS
+
+ smbldap-passwd.pl [name]
+
+=head1 DESCRIPTION
+
+smbldap-passwd.pl changes passwords for user accounts. A normal user
+ may only change the password for their own account, the super user may
+ change the password for any account.
+
+ Password Changes
+ The user is first prompted for their old password, if one is present.
+ This password is then tested against the stored password by binding
+ to the server. The user has only one chance to enter the correct pass-
+ word. The super user is permitted to bypass this step so that forgot-
+ ten passwords may be changed.
+
+ The user is then prompted for a replacement password. As a general
+ guideline, passwords should consist of 6 to 8 characters including
+ one or more from each of following sets:
+
+ Lower case alphabetics
+
+ Upper case alphabetics
+
+ Digits 0 thru 9
+
+ Punctuation marks
+
+ passwd will prompt again and compare the second entry against the first.
+ Both entries are require to match in order for the password to be
+ changed.
+
+=head1 SEE ALSO
+
+ passwd(1)
+
+=cut
+
+#'
diff --git a/examples/LDAP/smbldap-tools/smbldap-populate.pl b/examples/LDAP/smbldap-tools/smbldap-populate.pl
new file mode 100755
index 00000000000..b691a848500
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/smbldap-populate.pl
@@ -0,0 +1,370 @@
+#!/usr/bin/perl -w
+
+# Populate a LDAP base for Samba-LDAP usage
+#
+
+# This code was developped by IDEALX (http://IDEALX.org/) and
+# contributors (their names can be found in the CONTRIBUTORS file).
+#
+# Copyright (C) 2001-2002 IDEALX
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You 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.
+
+# Purpose :
+# . Create an initial LDAP database suitable for Samba 2.2
+# . For lazy people, replace ldapadd (with only an ldif parameter)
+
+use strict;
+use FindBin;
+use FindBin qw($RealBin);
+use lib "$RealBin/";
+use smbldap_tools;
+use smbldap_conf;
+use Getopt::Std;
+use Net::LDAP::LDIF;
+
+use vars qw(%oc);
+
+# objectclass of the suffix
+%oc = (
+ "ou" => "organizationalUnit",
+ "o" => "organization",
+ "dc" => "dcObject",
+ );
+
+
+my %Options;
+
+my $ok = getopts('a:b:?', \%Options);
+if ( (!$ok) || ($Options{'?'}) ) {
+ print "Usage: $0 [-ab?] [ldif]\n";
+ print " -a administrator login name (default: Administrator)\n";
+ print " -b guest login name (default: nobody)\n";
+ print " -? show this help message\n";
+ print " ldif file to add to ldap (default: suffix, Groups,";
+ print " Users, Computers and builtin users )\n";
+ exit (1);
+}
+
+my $_ldifName;
+my $tmp_ldif_file="/tmp/$$.ldif";
+
+if (@ARGV >= 1) {
+ $_ldifName = $ARGV[0];
+}
+
+my $adminName = $Options{'a'};
+if (!defined($adminName)) {
+ $adminName = "Administrator";
+}
+
+my $guestName = $Options{'b'};
+if (!defined($guestName)) {
+ $guestName = "nobody";
+}
+
+if (!defined($_ldifName)) {
+ my $attr;
+ my $val;
+ my $objcl;
+
+ print "Using builtin directory structure\n";
+ if ($suffix =~ m/([^=]+)=([^,]+)/) {
+ $attr = $1;
+ $val = $2;
+ $objcl = $oc{$attr} if (exists $oc{$attr});
+ if (!defined($objcl)) {
+ $objcl = "myhardcodedobjectclass";
+ }
+ } else {
+ die "can't extract first attr and value from suffix $suffix";
+ }
+ #print "$attr=$val\n";
+ my ($organisation,$ext) = ($suffix =~ m/dc=(.*),dc=(.*)$/);
+
+ #my $FILE="|cat";
+ my $FILE=$tmp_ldif_file;
+ open (FILE, ">$FILE") || die "Can't open file $FILE: $!\n";
+
+ print FILE <<EOF;
+dn: $suffix
+objectClass: $objcl
+objectclass: organization
+$attr: $val
+o: $organisation
+
+dn: $usersdn
+objectClass: organizationalUnit
+ou: $usersou
+
+dn: $groupsdn
+objectClass: organizationalUnit
+ou: $groupsou
+
+dn: $computersdn
+objectClass: organizationalUnit
+ou: $computersou
+
+dn: uid=$adminName,$usersdn
+cn: $adminName
+sn: $adminName
+objectClass: inetOrgPerson
+objectClass: sambaSamAccount
+objectClass: posixAccount
+gidNumber: 512
+uid: $adminName
+uidNumber: 998
+homeDirectory: $_userHomePrefix
+sambaPwdLastSet: 0
+sambaLogonTime: 0
+sambaLogoffTime: 2147483647
+sambaKickoffTime: 2147483647
+sambaPwdCanChange: 0
+sambaPwdMustChange: 2147483647
+sambaHomePath: $_userSmbHome
+sambaHomeDrive: $_userHomeDrive
+sambaProfilePath: $_userProfile
+sambaPrimaryGroupSID: $SID-512
+sambaLMPassword: XXX
+sambaNTPassword: XXX
+sambaAcctFlags: [U ]
+sambaSID: $SID-2996
+loginShell: /bin/false
+gecos: Netbios Domain Administrator
+
+dn: uid=$guestName,$usersdn
+cn: $guestName
+sn: $guestName
+objectClass: inetOrgPerson
+objectClass: sambaSamAccount
+objectClass: posixAccount
+gidNumber: 514
+uid: $guestName
+uidNumber: 999
+homeDirectory: /dev/null
+sambaPwdLastSet: 0
+sambaLogonTime: 0
+sambaLogoffTime: 2147483647
+sambaKickoffTime: 2147483647
+sambaPwdCanChange: 0
+sambaPwdMustChange: 2147483647
+sambaHomePath: $_userSmbHome
+sambaHomeDrive: $_userHomeDrive
+sambaProfilePath: $_userProfile
+sambaPrimaryGroupSID: $SID-514
+sambaLMPassword: NO PASSWORDXXXXXXXXXXXXXXXXXXXXX
+sambaNTPassword: NO PASSWORDXXXXXXXXXXXXXXXXXXXXX
+sambaAcctFlags: [NU ]
+sambaSID: $SID-2998
+loginShell: /bin/false
+
+dn: cn=Domain Admins,$groupsdn
+objectClass: posixGroup
+objectClass: sambaGroupMapping
+gidNumber: 512
+cn: Domain Admins
+memberUid: $adminName
+description: Netbios Domain Administrators
+sambaSID: $SID-512
+sambaGroupType: 2
+displayName: Domain Admins
+
+dn: cn=Domain Users,$groupsdn
+objectClass: posixGroup
+objectClass: sambaGroupMapping
+gidNumber: 513
+cn: Domain Users
+description: Netbios Domain Users
+sambaSID: $SID-513
+sambaGroupType: 2
+displayName: Domain Users
+
+dn: cn=Domain Guests,$groupsdn
+objectClass: posixGroup
+objectClass: sambaGroupMapping
+gidNumber: 514
+cn: Domain Guests
+description: Netbios Domain Guests Users
+sambaSID: $SID-514
+sambaGroupType: 2
+displayName: Domain Guests
+
+dn: cn=Administrators,$groupsdn
+objectClass: posixGroup
+objectClass: sambaGroupMapping
+gidNumber: 544
+cn: Administrators
+description: Netbios Domain Members can fully administer the computer/sambaDomainName
+sambaSID: $SID-544
+sambaGroupType: 2
+displayName: Administrators
+
+dn: cn=Users,$groupsdn
+objectClass: posixGroup
+objectClass: sambaGroupMapping
+gidNumber: 545
+cn: Users
+description: Netbios Domain Ordinary users
+sambaSID: $SID-545
+sambaGroupType: 2
+displayName: users
+
+dn: cn=Guests,$groupsdn
+objectClass: posixGroup
+objectClass: sambaGroupMapping
+gidNumber: 546
+cn: Guests
+memberUid: $guestName
+description: Netbios Domain Users granted guest access to the computer/sambaDomainName
+sambaSID: $SID-546
+sambaGroupType: 2
+displayName: Guests
+
+dn: cn=Power Users,$groupsdn
+objectClass: posixGroup
+objectClass: sambaGroupMapping
+gidNumber: 547
+cn: Power Users
+description: Netbios Domain Members can share directories and printers
+sambaSID: $SID-547
+sambaGroupType: 2
+displayName: Power Users
+
+dn: cn=Account Operators,$groupsdn
+objectClass: posixGroup
+objectClass: sambaGroupMapping
+gidNumber: 548
+cn: Account Operators
+description: Netbios Domain Users to manipulate users accounts
+sambaSID: $SID-548
+sambaGroupType: 2
+displayName: Account Operators
+
+dn: cn=Server Operators,$groupsdn
+objectClass: posixGroup
+objectClass: sambaGroupMapping
+gidNumber: 549
+cn: Server Operators
+description: Netbios Domain Server Operators
+sambaSID: $SID-549
+sambaGroupType: 2
+displayName: Server Operators
+
+dn: cn=Print Operators,$groupsdn
+objectClass: posixGroup
+objectClass: sambaGroupMapping
+gidNumber: 550
+cn: Print Operators
+description: Netbios Domain Print Operators
+sambaSID: $SID-550
+sambaGroupType: 2
+displayName: Print Operators
+
+dn: cn=Backup Operators,$groupsdn
+objectClass: posixGroup
+objectClass: sambaGroupMapping
+gidNumber: 551
+cn: Backup Operators
+description: Netbios Domain Members can bypass file security to back up files
+sambaSID: $SID-551
+sambaGroupType: 2
+displayName: Backup Operators
+
+dn: cn=Replicator,$groupsdn
+objectClass: posixGroup
+objectClass: sambaGroupMapping
+gidNumber: 552
+cn: Replicator
+description: Netbios Domain Supports file replication in a sambaDomainName
+sambaSID: $SID-552
+sambaGroupType: 2
+displayName: Replicator
+
+dn: cn=Domain Computers,$groupsdn
+objectClass: posixGroup
+objectClass: sambaGroupMapping
+gidNumber: 553
+cn: Domain Computers
+description: Netbios Domain Computers accounts
+sambaSID: $SID-553
+sambaGroupType: 2
+displayName: Domain Computers
+
+EOF
+ close FILE;
+} else {
+ $tmp_ldif_file=$_ldifName;
+}
+
+my $ldap_master=connect_ldap_master();
+my $ldif = Net::LDAP::LDIF->new($tmp_ldif_file, "r", onerror => 'undef' );
+while( not $ldif->eof() ) {
+ my $entry = $ldif->read_entry();
+ if ( $ldif->error() ) {
+ print "Error msg: ",$ldif->error(),"\n";
+ print "Error lines:\n",$ldif->error_lines(),"\n";
+ } else {
+ my $dn = $entry->dn;
+ print "adding new entry: $dn\n";
+ my $result=$ldap_master->add($entry);
+ $result->code && warn "failed to add entry: ", $result->error ;
+ }
+}
+$ldap_master->unbind;
+system "rm -f $tmp_ldif_file";
+exit(0);
+
+
+########################################
+
+=head1 NAME
+
+smbldap-populate.pl - Populate your LDAP database
+
+=head1 SYNOPSIS
+
+ smbldap-populate.pl [ldif-file]
+
+=head1 DESCRIPTION
+
+ The smbldap-populate.pl command helps to populate an LDAP server
+ by adding the necessary entries : base suffix (doesn't abort
+ if already there), organizational units for users, groups and
+ computers, builtin users : Administrator and guest, builtin
+ groups (though posixAccount only, no SambaTNG support).
+
+ -a name Your local administrator login name (default: Administrator)
+ -b name Your local guest login name (default: nobody)
+
+ If you give an extra parameter, it is assumed to be the ldif
+ file to use instead of the builtin one. Options -a and -b
+ will be ignored.
+
+=head1 FILES
+
+ /usr/lib/perl5/site-perl/smbldap_conf.pm : Global parameters.
+
+=head1 SEE ALSO
+
+ smp(1)
+
+=cut
+
+#'
+
+
+
+# - The End
diff --git a/examples/LDAP/smbldap-tools/smbldap-tools.spec b/examples/LDAP/smbldap-tools/smbldap-tools.spec
new file mode 100755
index 00000000000..4bd2968defe
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/smbldap-tools.spec
@@ -0,0 +1,140 @@
+# $Source: /home/cvs/samba/examples/LDAP/smbldap-tools/smbldap-tools.spec,v $
+%define version 0.8.2
+%define release 1
+%define name smbldap-tools
+%define realname smbldap-tools
+
+Summary: User & Group administration tools for Samba-OpenLDAP
+Name: %{name}
+version: %{version}
+Release: %{release}
+Group: System Environment/Base
+License: GPL
+
+Vendor: IDEALX S.A.S.
+URL: http://samba.IDEALX.org/
+Packager: Jerome Tournier <jerome.tournier@IDEALX.com>
+Source0: smbldap-groupadd.pl
+Source1: smbldap-groupdel.pl
+Source2: smbldap-groupmod.pl
+Source3: smbldap-groupshow.pl
+Source4: smbldap-passwd.pl
+Source5: smbldap-useradd.pl
+Source6: smbldap-userdel.pl
+Source7: smbldap-usermod.pl
+Source8: smbldap-usershow.pl
+Source9: smbldap_conf.pm
+Source10: smbldap_tools.pm
+Source11: CONTRIBUTORS
+Source12: COPYING
+Source13: ChangeLog
+Source14: FILES
+Source15: README
+Source16: TODO
+Source17: mkntpwd.tar.gz
+Source18: smbldap-populate.pl
+Source19: smbldap-migrate-accounts.pl
+Source20: smbldap-migrate-groups.pl
+Source21: INFRA
+Source22: smb.conf
+BuildRoot: /%{_tmppath}/%{name}
+Prefix: /usr/local
+BuildRequires: perl >= 5.6
+Requires: perl >= 5.6, openldap, openldap-clients, samba
+
+%description
+In settings with OpenLDAP and Samba-LDAP servers, this collection is
+useful to add, modify and delete users and groups, and to change
+Unix and Samba passwords. In those context they replace the system
+tools to manage users, groups and passwords.
+
+%prep
+
+%setup -c -T
+
+%build
+tar zxvf %{SOURCE17}
+cd mkntpwd
+make
+
+%install
+rm -rf $RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT/%{prefix}/sbin
+mkdir -p $RPM_BUILD_ROOT/%{prefix}/share
+mkdir -p $RPM_BUILD_ROOT/usr/share/doc
+mkdir -p $RPM_BUILD_ROOT/usr/share/doc/smbldap-tools
+
+cd mkntpwd ; make PREFIX=$RPM_BUILD_ROOT/%{prefix} install
+
+install -m 550 %{SOURCE0} $RPM_BUILD_ROOT/%{prefix}/sbin/smbldap-groupadd.pl
+install -m 550 %{SOURCE1} $RPM_BUILD_ROOT/%{prefix}/sbin/smbldap-groupdel.pl
+install -m 550 %{SOURCE2} $RPM_BUILD_ROOT/%{prefix}/sbin/smbldap-groupmod.pl
+install -m 555 %{SOURCE3} $RPM_BUILD_ROOT/%{prefix}/sbin/smbldap-groupshow.pl
+install -m 555 %{SOURCE4} $RPM_BUILD_ROOT/%{prefix}/sbin/smbldap-passwd.pl
+install -m 550 %{SOURCE5} $RPM_BUILD_ROOT/%{prefix}/sbin/smbldap-useradd.pl
+install -m 550 %{SOURCE6} $RPM_BUILD_ROOT/%{prefix}/sbin/smbldap-userdel.pl
+install -m 550 %{SOURCE7} $RPM_BUILD_ROOT/%{prefix}/sbin/smbldap-usermod.pl
+install -m 555 %{SOURCE8} $RPM_BUILD_ROOT/%{prefix}/sbin/smbldap-usershow.pl
+install -m 550 %{SOURCE18} $RPM_BUILD_ROOT/%{prefix}/sbin/smbldap-populate.pl
+install -m 751 %{SOURCE9} $RPM_BUILD_ROOT/%{prefix}/sbin/smbldap_conf.pm
+install -m 555 %{SOURCE10} $RPM_BUILD_ROOT/%{prefix}/sbin/smbldap_tools.pm
+install -m 550 %{SOURCE19} $RPM_BUILD_ROOT/%{prefix}/sbin/smbldap-migrate-accounts.pl
+install -m 550 %{SOURCE20} $RPM_BUILD_ROOT/%{prefix}/sbin/smbldap-migrate-groups.pl
+
+install -m 644 %{SOURCE11} $RPM_BUILD_ROOT/usr/share/doc/smbldap-tools/CONTRIBUTORS
+install -m 644 %{SOURCE12} $RPM_BUILD_ROOT/usr/share/doc/smbldap-tools/COPYING
+install -m 644 %{SOURCE13} $RPM_BUILD_ROOT/usr/share/doc/smbldap-tools/ChangeLog
+install -m 644 %{SOURCE14} $RPM_BUILD_ROOT/usr/share/doc/smbldap-tools/FILES
+install -m 644 %{SOURCE15} $RPM_BUILD_ROOT/usr/share/doc/smbldap-tools/README
+install -m 644 %{SOURCE16} $RPM_BUILD_ROOT/usr/share/doc/smbldap-tools/TODO
+install -m 644 %{SOURCE21} $RPM_BUILD_ROOT/usr/share/doc/smbldap-tools/INFRA
+install -m 644 %{SOURCE22} $RPM_BUILD_ROOT/usr/share/doc/smbldap-tools/smb.conf
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+# from smbldap-tools-0.8-2, librairies are loaded with the FindBin perl package
+if [ -f /usr/lib/perl5/site_perl/smbldap_tools.pm ];
+then
+ rm -f /usr/lib/perl5/site_perl/smbldap_tools.pm
+fi
+if [ -f /usr/lib/perl5/site_perl/smbldap_conf.pm ];
+then
+ rm -f /usr/lib/perl5/site_perl/smbldap_conf.pm
+fi
+chgrp 512 %{prefix}/sbin/smbldap-useradd.pl %{prefix}/sbin/smbldap_conf.pm || echo "An error occured while changing groups of smbldap-useradd.pl and smbldap_conf.pm in /usr/local/sbin. For proper operations, please ensure that they have the same posix group as the Samba domain administrator if there's a local Samba PDC."
+perl -i -pe 's/_SLAVELDAP_/localhost/' %{prefix}/sbin/smbldap_conf.pm
+perl -i -pe 's/_MASTERLDAP_/localhost/' %{prefix}/sbin/smbldap_conf.pm
+perl -i -pe 's/_SUFFIX_/dc=IDEALX,dc=org/' %{prefix}/sbin/smbldap_conf.pm
+perl -i -pe 's/_USERS_/Users/' %{prefix}/sbin/smbldap_conf.pm
+perl -i -pe 's/_COMPUTERS_/Computers/' %{prefix}/sbin/smbldap_conf.pm
+perl -i -pe 's/_GROUPS_/Groups/' %{prefix}/sbin/smbldap_conf.pm
+perl -i -pe 's/_LOGINSHELL_/\/bin\/bash/' %{prefix}/sbin/smbldap_conf.pm
+perl -i -pe 's/_HOMEPREFIX_/\/home/' %{prefix}/sbin/smbldap_conf.pm
+perl -i -pe 's/_BINDDN_/cn=Manager,\$suffix/' %{prefix}/sbin/smbldap_conf.pm
+perl -i -pe 's/_BINDPW_/secret/' %{prefix}/sbin/smbldap_conf.pm
+perl -i -pe 's/_PDCNAME_/PDC-SRV/' %{prefix}/sbin/smbldap_conf.pm
+perl -i -pe 's/_HOMEDRIVE_/H:/' %{prefix}/sbin/smbldap_conf.pm
+
+# FIXME: links should not be removed on upgrade
+#%postun
+#if [ $1 = 0 ] ; then
+# rm -f /usr/lib/perl5/site_perl/smbldap_tools.pm
+# rm -f /usr/lib/perl5/site_perl/smbldap_conf.pm
+#fi
+
+%files
+%defattr(-,root,root)
+%{prefix}/sbin/*.pl
+%{prefix}/sbin/smbldap_tools.pm
+%config(noreplace) %{prefix}/sbin/smbldap_conf.pm
+%{prefix}/sbin/mkntpwd
+%doc /usr/share/doc/%{name}/
+
+
+%changelog
+* Fri Nov 28 2003 Jerome Tournier <jerome.tournier@idealx.com> 0.8.2-1
+- new smb.conf file as example configuration file
+- see Changelog file for updates in scripts
+
diff --git a/examples/LDAP/smbldap-tools/smbldap-useradd.pl b/examples/LDAP/smbldap-tools/smbldap-useradd.pl
new file mode 100755
index 00000000000..918bd4a4f66
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/smbldap-useradd.pl
@@ -0,0 +1,522 @@
+#!/usr/bin/perl -w
+
+# This code was developped by IDEALX (http://IDEALX.org/) and
+# contributors (their names can be found in the CONTRIBUTORS file).
+#
+# Copyright (C) 2002 IDEALX
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You 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.
+
+# Purpose of smbldap-useradd : user (posix,shadow,samba) add
+
+use strict;
+
+use FindBin;
+use FindBin qw($RealBin);
+use lib "$RealBin/";
+use smbldap_tools;
+use smbldap_conf;
+
+#####################
+
+use Getopt::Std;
+my %Options;
+
+my $ok = getopts('anmwPG:u:g:d:s:c:k:A:B:C:D:E:F:H:N:S:?', \%Options);
+
+if ( (!$ok) || (@ARGV < 1) || ($Options{'?'}) ) {
+ print "Usage: $0 [-awmugdsckGPABCDEFH?] username\n";
+ print " -a is a Windows User (otherwise, Posix stuff only)\n";
+ print " -w is a Windows Workstation (otherwise, Posix stuff only)\n";
+ print " -u uid\n";
+ print " -g gid\n";
+ print " -G supplementary comma-separated groups\n";
+ print " -n do not create a group\n";
+ print " -d home\n";
+ print " -s shell\n";
+ print " -c gecos\n";
+ print " -m creates home directory and copies /etc/skel\n";
+ print " -k skeleton dir (with -m)\n";
+ print " -P ends by invoking smbldap-passwd.pl\n";
+ print " -A can change password ? 0 if no, 1 if yes\n";
+ print " -B must change password ? 0 if no, 1 if yes\n";
+ print " -C sambaHomePath (SMB home share, like '\\\\PDC-SRV\\homes')\n";
+ print " -D sambaHomeDrive (letter associated with home share, like 'H:')\n";
+ print " -E sambaLogonScript (DOS script to execute on login)\n";
+ print " -F sambaProfilePath (profile directory, like '\\\\PDC-SRV\\profiles\\foo')\n";
+ print " -H sambaAcctFlags (samba account control bits like '[NDHTUMWSLKI]')\n";
+ print " -N canonical name\n";
+ print " -S surname\n";
+ print " -? show this help message\n";
+ exit (1);
+}
+
+
+# cause problems when dealing with getpwuid because of the
+# negative ttl and ldap modification
+my $nscd_status = system "/etc/init.d/nscd status >/dev/null 2>&1";
+
+if ($nscd_status == 0) {
+ system "/etc/init.d/nscd stop > /dev/null 2>&1";
+}
+
+
+# Read options
+my $userUidNumber = $Options{'u'};
+if (!defined($userUidNumber)) {
+ # find first unused uid starting from $UID_START
+ while (defined(getpwuid($UID_START))) {
+ $UID_START++;
+ }
+ $userUidNumber = $UID_START;
+} elsif (getpwuid($userUidNumber)) {
+ die "Uid already exists.\n";
+}
+
+if ($nscd_status == 0) {
+ system "/etc/init.d/nscd start > /dev/null 2>&1";
+}
+
+
+my $createGroup = 0;
+my $userGidNumber = $Options{'g'};
+# gid not specified ?
+if (!defined($userGidNumber)) {
+ # windows machine => $_defaultComputerGid
+ if (defined($Options{'w'})) {
+ $userGidNumber = $_defaultComputerGid;
+ # } elsif (!defined($Options{'n'})) {
+ # create new group (redhat style)
+ # find first unused gid starting from $GID_START
+ # while (defined(getgrgid($GID_START))) {
+ # $GID_START++;
+ # }
+ # $userGidNumber = $GID_START;
+
+ # $createGroup = 1;
+
+ } else {
+ # user will have gid = $_defaultUserGid
+ $userGidNumber = $_defaultUserGid;
+ }
+} else {
+ my $gid;
+ if (($gid = parse_group($userGidNumber)) < 0) {
+ print "$0: unknown group $userGidNumber\n";
+ exit (6);
+ }
+ $userGidNumber = $gid;
+}
+
+# Read only first @ARGV
+my $userName = $ARGV[0];
+
+# untaint $userName (can finish with one or two $)
+if ($userName =~ /^([\w -]+\$?)$/) {
+ $userName = $1;
+} else {
+ print "$0: illegal username\n";
+ exit (1);
+}
+
+# user must not exist in LDAP (should it be nss-wide ?)
+my ($rc, $dn) = get_user_dn2($userName);
+if ($rc and defined($dn)) {
+ print "$0: user $userName exists\n";
+ exit (9);
+} elsif (!$rc) {
+ print "$0: error in get_user_dn2\n";
+ exit(10);
+}
+
+my $group_entry;
+my $userGroupSID;
+my $userRid;
+if ($Options{'a'}) {
+ # as grouprid we use the value of the sambaSID attribute for
+ # group of gidNumber=$userGidNumber
+ $group_entry = read_group_entry_gid($userGidNumber);
+ $userGroupSID = $group_entry->get_value('sambaSID');
+ unless ($userGroupSID) {
+ print "$0: unknown group SID not set for unix group $userGidNumber\n";
+ print "check if your unix group is mapped to an NT group\n";
+ exit (7);
+ }
+
+ # as rid we use 2 * uid + 1000
+ $userRid = 2 * $userUidNumber + 1000;
+ # let's test if this SID already exist
+ my $user_sid="$SID-$userRid";
+ my $test_exist_sid=does_sid_exist($user_sid,$usersdn);
+ if ($test_exist_sid->count == 1) {
+ print "User SID already owned by\n";
+ # there should not exist more than one entry, but ...
+ foreach my $entry ($test_exist_sid->all_entries) {
+ my $dn= $entry->dn;
+ chomp($dn);
+ print "$dn\n";
+ }
+ exit(7);
+ }
+}
+
+my $userHomeDirectory;
+my ($userCN, $userSN);
+my $tmp;
+if (!defined($userHomeDirectory = $Options{'d'})) {
+ $userHomeDirectory = $_userHomePrefix."/".$userName;
+}
+$_userLoginShell = $tmp if (defined($tmp = $Options{'s'}));
+$_userGecos = $tmp if (defined($tmp = $Options{'c'}));
+$_skeletonDir = $tmp if (defined($tmp = $Options{'k'}));
+$userCN = ($Options{'c'} || $userName);
+$userCN = $tmp if (defined($tmp = $Options{'N'}));
+$userSN = $userName;
+$userSN = $tmp if (defined($tmp = $Options{'S'}));
+
+
+########################
+
+my $ldap_master=connect_ldap_master();
+
+# MACHINE ACCOUNT
+if (defined($tmp = $Options{'w'})) {
+
+ # add a trailing dollar if missing
+ if ($userName =~ /[^\$]$/s) {
+ $userName .= "\$";
+ }
+
+ #print "About to create machine $userName:\n";
+
+ if (!add_posix_machine ($userName, $userUidNumber, $userGidNumber)) {
+ die "$0: error while adding posix account\n";
+ }
+
+ if (!$with_smbpasswd) {
+ # (jtournier)
+ # Objectclass sambaSamAccount is now added directly by samba when joigning the domain (for samba3)
+ #if (!add_samba_machine_mkntpwd($userName, $userUidNumber)) {
+ # die "$0: error while adding samba account\n";
+ #}
+ } else {
+ if (!add_samba_machine($userName)) {
+ die "$0: error while adding samba account\n";
+ }
+ my $modify = $ldap_master->modify ( "$dn",
+ changes => [
+ replace => [sambaAcctFlags => '[W ]']
+ ]
+ );
+ $modify->code && warn "failed to modify entry: ", $modify->error ;
+ }
+
+ exit 0;
+}
+
+# USER ACCOUNT
+# add posix account first
+
+my $add = $ldap_master->add ("uid=$userName,$usersdn",
+ attr => [
+ 'objectclass' => ['top','inetOrgPerson', 'posixAccount'],
+ 'cn' => "$userCN",
+ 'sn' => "$userSN",
+ 'uid' => "$userName",
+ 'uidNumber' => "$userUidNumber",
+ 'gidNumber' => "$userGidNumber",
+ 'homeDirectory' => "$userHomeDirectory",
+ 'loginShell' => "$_userLoginShell",
+ 'gecos' => "$_userGecos",
+ 'description' => "$_userGecos",
+ 'userPassword' => "{crypt}x"
+ ]
+ );
+
+$add->code && warn "failed to add entry: ", $add->error ;
+
+
+#if ($createGroup) {
+# group_add($userName, $userGidNumber);
+#}
+
+group_add_user($userGidNumber, $userName);
+
+my $grouplist;
+# adds to supplementary groups
+if (defined($grouplist = $Options{'G'})) {
+ add_grouplist_user($grouplist, $userName);
+}
+
+# If user was created successfully then we should create his/her home dir
+if (defined($tmp = $Options{'m'})) {
+ unless ( $userName =~ /\$$/ ) {
+ if ( !(-e $userHomeDirectory) ) {
+ system "mkdir $userHomeDirectory 2>/dev/null";
+ system "cp -a $_skeletonDir/.[a-z,A-Z]* $_skeletonDir/* $userHomeDirectory 2>/dev/null";
+ system "chown -R $userUidNumber:$userGidNumber $userHomeDirectory 2>/dev/null";
+ system "chmod 700 $userHomeDirectory 2>/dev/null";
+ }
+ }
+}
+
+
+# Add Samba user infos
+if (defined($Options{'a'})) {
+ if (!$with_smbpasswd) {
+
+ my $winmagic = 2147483647;
+ my $valpwdcanchange = 0;
+ my $valpwdmustchange = $winmagic;
+ my $valpwdlastset = 0;
+ my $valacctflags = "[UX]";
+
+ if (defined($tmp = $Options{'A'})) {
+ if ($tmp != 0) {
+ $valpwdcanchange = "0";
+ } else {
+ $valpwdcanchange = "$winmagic";
+ }
+ }
+
+ if (defined($tmp = $Options{'B'})) {
+ if ($tmp != 0) {
+ $valpwdmustchange = "0";
+ # To force a user to change his password:
+ # . the attribut sambaPwdLastSet must be != 0
+ # . the attribut sambaAcctFlags must not match the 'X' flag
+ $valpwdlastset=$winmagic;
+ $valacctflags = "[U]";
+ } else {
+ $valpwdmustchange = "$winmagic";
+ }
+ }
+
+ if (defined($tmp = $Options{'H'})) {
+ $valacctflags = "$tmp";
+ }
+
+
+ my $modify = $ldap_master->modify ( "uid=$userName,$usersdn",
+ changes => [
+ add => [objectClass => 'sambaSamAccount'],
+ add => [sambaPwdLastSet => "$valpwdlastset"],
+ add => [sambaLogonTime => '0'],
+ add => [sambaLogoffTime => '2147483647'],
+ add => [sambaKickoffTime => '2147483647'],
+ add => [sambaPwdCanChange => "$valpwdcanchange"],
+ add => [sambaPwdMustChange => "$valpwdmustchange"],
+ add => [displayName => "$_userGecos"],
+ add => [sambaAcctFlags => "$valacctflags"],
+ add => [sambaSID => "$SID-$userRid"]
+ ]
+ );
+
+ $modify->code && die "failed to add entry: ", $modify->error ;
+
+ } else {
+ my $FILE="|smbpasswd -s -a $userName >/dev/null" ;
+ open (FILE, $FILE) || die "$!\n";
+ print FILE <<EOF;
+x
+x
+EOF
+ ;
+ close FILE;
+ if ($?) {
+ print "$0: error adding samba account\n";
+ exit (10);
+ }
+ } # with_smbpasswd
+
+ my @mods;
+ my $valscriptpath;
+ if (defined $_userScript) {
+ $valscriptpath="$_userScript";
+ } else {
+ $valscriptpath = "$userName.cmd";
+ }
+ if (defined($tmp = $Options{'E'})) {
+ $valscriptpath = "$tmp";
+ }
+
+ my $valsmbhome;
+ if (defined $_userSmbHome) {
+ $valsmbhome = "$_userSmbHome";
+ }
+ if (defined($tmp = $Options{'C'})) {
+ $valsmbhome = "$tmp";
+ }
+ if (defined $valsmbhome) {
+ push(@mods, 'sambaHomePath', $valsmbhome);
+ }
+
+ my $valhomedrive = "$_userHomeDrive";
+ if (defined($tmp = $Options{'D'})) {
+ $tmp = $tmp.":" unless ($tmp =~ /:/);
+ $valhomedrive = "$tmp";
+ }
+
+ my $valprofilepath;
+ if (defined $_userProfile) {
+ $valprofilepath = "$_userProfile$userName";
+ }
+
+ if (defined($tmp = $Options{'F'})) {
+ $valprofilepath = "$tmp";
+ }
+ if (defined $valprofilepath) {
+ push(@mods, 'sambaProfilePath', $valprofilepath);
+ }
+
+ my $modify = $ldap_master->modify ( "uid=$userName,$usersdn",
+ changes => [
+ add => [sambaPrimaryGroupSID => "$userGroupSID"],
+ add => [sambaHomeDrive => "$valhomedrive"],
+ add => [sambaLogonScript => "$valscriptpath"],
+ add => [sambaLMPassword => 'XXX'],
+ add => [sambaNTPassword => 'XXX']
+ ]
+ );
+ $modify = $ldap_master->modify ( "uid=$userName,$usersdn",
+ 'replace' => { @mods }
+ );
+
+
+ $modify->code && die "failed to add entry: ", $modify->error ;
+
+}
+$ldap_master->unbind; # take down session
+
+
+if (defined($Options{'P'})) {
+ exec "/usr/local/sbin/smbldap-passwd.pl $userName"
+}
+
+exit 0;
+
+########################################
+
+=head1 NAME
+
+smbldap-useradd.pl - Create a new user or update default new
+ user information
+
+=head1 SYNOPSIS
+
+smbldap-useradd.pl [-c comment] [-d home_dir]
+ [-g initial_group] [-G group[,...]]
+ [-m [-k skeleton_dir]]
+ [-s shell] [-u uid [ -o]] [-P]
+ [-A canchange] [-B mustchange] [-C smbhome]
+ [-D homedrive] [-E scriptpath] [-F profilepath]
+ [-H acctflags] login
+
+=head1 DESCRIPTION
+
+Creating New Users
+ The smbldap-useradd.pl command creates a new user account using
+ the values specified on the command line and the default
+ values from the system.
+ The new user account will be entered into the system
+ files as needed, the home directory will be created, and
+ initial files copied, depending on the command line options.
+
+ You have to use smbldap-passwd to set the user password.
+ For Samba users, rid is 2*uidNumber+1000, and primaryGroupID
+ is 2*gidNumber+1001. Thus you may want to use
+ smbldap-useradd.pl -a -g "Domain Admins" -u 500 Administrator
+ to create a sambaDomainName administrator (admin rid is 0x1F4 = 500 and
+ grouprid is 0x200 = 512)
+
+ Without any option, the account created will be an Unix (Posix)
+ account. The following options may be used to add information:
+
+-a The user will have a Samba account (and Unix).
+
+ -w Creates an account for a Samba machine (Workstation), so that
+ it can join a sambaDomainName.
+
+ -x Creates rid and primaryGroupID in hex (for Samba 2.2.2 bug). Else
+ decimal (2.2.2 patched from cvs or 2.2.x, x > 2)
+
+ -c comment
+ The new user's comment field (gecos).
+
+ -d home_dir
+ The new user will be created using home_dir as the value for the
+ user's login directory. The default is to append the login name
+ to default_home and use that as the login directory name.
+
+ -g initial_group
+ The group name or number of the user's initial login group. The
+ group name must exist. A group number must refer to an already
+ existing group. The default group number is 1.
+
+ -G group,[...]
+ A list of supplementary groups which the user is also a member
+ of. Each group is separated from the next by a comma, with no
+ intervening whitespace. The groups are subject to the same
+ restrictions as the group given with the -g option. The default
+ is for the user to belong only to the initial group.
+
+ -m The user's home directory will be created if it does not exist.
+ The files contained in skeleton_dir will be copied to the home
+ directory if the -k option is used, otherwise the files con­
+ tained in /etc/skel will be used instead. Any directories con­
+ tained in skeleton_dir or /etc/skel will be created in the
+ user's home directory as well. The -k option is only valid in
+ conjunction with the -m option. The default is to not create
+ the directory and to not copy any files.
+
+ -s shell
+ The name of the user's login shell. The default is to leave
+ this field blank, which causes the system to select the default
+ login shell.
+
+ -u uid The numerical value of the user's ID. This value must be
+ unique, unless the -o option is used. The value must be non-
+ negative. The default is to use the smallest ID value greater
+ than 1000 and greater than every other user.
+
+ -P ends by invoking smbldap-passwd.pl
+
+ -A can change password ? 0 if no, 1 if yes
+
+ -B must change password ? 0 if no, 1 if yes
+
+ -C sambaHomePath (SMB home share, like '\\\\PDC-SRV\\homes')
+
+ -D sambaHomeDrive (letter associated with home share, like 'H:')
+
+ -E sambaLogonScript, relative to the [netlogon] share (DOS script to execute on login, like 'foo.bat')
+
+ -F sambaProfilePath (profile directory, like '\\\\PDC-SRV\\profiles\\foo')
+
+ -H sambaAcctFlags, spaces and trailing bracket are ignored (samba account control bits like '[NDHTUMWSLKI]')
+
+ -N canonical name (defaults to gecos or username, if gecos not set)
+
+ -S surname (defaults to username)
+
+
+=head1 SEE ALSO
+
+ useradd(1)
+
+=cut
+
+#'
diff --git a/examples/LDAP/smbldap-tools/smbldap-userdel.pl b/examples/LDAP/smbldap-tools/smbldap-userdel.pl
new file mode 100755
index 00000000000..f1e69e209c5
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/smbldap-userdel.pl
@@ -0,0 +1,125 @@
+#!/usr/bin/perl
+
+# This code was developped by IDEALX (http://IDEALX.org/) and
+# contributors (their names can be found in the CONTRIBUTORS file).
+#
+# Copyright (C) 2001-2002 IDEALX
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You 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.
+
+# Purpose of smbldap-userdel : user (posix,shadow,samba) deletion
+
+use strict;
+use FindBin;
+use FindBin qw($RealBin);
+use lib "$RealBin/";
+use smbldap_tools;
+
+
+#####################
+
+use Getopt::Std;
+my %Options;
+
+my $ok = getopts('r?', \%Options);
+
+if ( (!$ok) || (@ARGV < 1) || ($Options{'?'}) ) {
+ print "Usage: $0 [-r?] username\n";
+ print " -r remove home directory\n";
+ exit (1);
+}
+
+# Read only first @ARGV
+my $user = $ARGV[0];
+
+my $dn;
+# user must not exist in LDAP
+if (!defined($dn=get_user_dn($user))) {
+ print "$0: user $user does not exist\n";
+ exit (6);
+}
+
+if ($< != 0) {
+ print "You must be root to delete an user\n";
+ exit (1);
+}
+
+my $homedir;
+if (defined($Options{'r'})) {
+ $homedir=get_homedir($user);
+}
+
+# remove user from groups
+my $groups = find_groups_of $user;
+my @grplines = split(/\n/,$groups);
+
+my $grp;
+foreach $grp (@grplines) {
+ my $gname = "";
+ if ( $grp =~ /dn: cn=([^,]+),/) {
+ $gname = $1;
+ #print "xx $gname\n";
+ }
+ if ($gname ne "") {
+ group_remove_member($gname, $user);
+ }
+}
+
+# XXX
+delete_user($user);
+
+# delete dir -- be sure that homeDir is not a strange value
+if (defined($Options{'r'})) {
+ if ($homedir !~ /^\/dev/ and $homedir !~ /^\/$/) {
+ system "rm -rf $homedir";
+ }
+}
+
+my $nscd_status = system "/etc/init.d/nscd status >/dev/null 2>&1";
+
+if ($nscd_status == 0) {
+ system "/etc/init.d/nscd restart > /dev/null 2>&1";
+}
+
+exit (0);
+
+############################################################
+
+=head1 NAME
+
+ smbldap-userdel.pl - Delete a user account and related files
+
+=head1 SYNOPSIS
+
+ smbldap-userdel.pl [-r] login
+
+=head1 DESCRIPTION
+
+ The smbldap-userdel.pl command modifies the system
+ account files, deleting all entries that refer to login.
+ The named user must exist.
+
+ -r Files in the user's home directory will be removed along with
+ the home directory itself. Files located in other file
+ systems will have to be searched for and deleted manually.
+
+=head1 SEE ALSO
+
+ userdel(1)
+
+=cut
+
+#'
diff --git a/examples/LDAP/smbldap-tools/smbldap-usermod.pl b/examples/LDAP/smbldap-tools/smbldap-usermod.pl
new file mode 100755
index 00000000000..70151b74122
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/smbldap-usermod.pl
@@ -0,0 +1,488 @@
+#!/usr/bin/perl -w
+
+# This code was developped by IDEALX (http://IDEALX.org/) and
+# contributors (their names can be found in the CONTRIBUTORS file).
+#
+# Copyright (C) 2001-2002 IDEALX
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You 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.
+
+# Purpose of smbldap-usermod : user (posix,shadow,samba) modification
+
+use strict;
+use FindBin;
+use FindBin qw($RealBin);
+use lib "$RealBin/";
+use smbldap_tools;
+use smbldap_conf;
+
+#####################
+
+use Getopt::Std;
+my %Options;
+my $nscd_status;
+
+my $ok = getopts('A:B:C:D:E:F:H:IJN:S:Pame:f:u:g:G:d:l:s:c:ok:?h', \%Options);
+if ( (!$ok) || (@ARGV < 1) || ($Options{'?'}) || ($Options{'h'}) ) {
+ print "Usage: $0 [-awmugdsckxABCDEFGHI?h] username\n";
+ print "Available options are:\n";
+ print " -c gecos\n";
+ print " -d home directory\n";
+ #print " -m move home directory\n";
+ #print " -f inactive days\n";
+ print " -u uid\n";
+ print " -o uid can be non unique\n";
+ print " -g gid\n";
+ print " -G supplementary groups (comma separated)\n";
+ print " -l login name\n";
+ print " -s shell\n";
+ print " -N canonical name\n";
+ print " -S surname\n";
+ print " -P ends by invoking smbldap-passwd.pl\n";
+ print " For samba users:\n";
+ print " -a add sambaSamAccount objectclass\n";
+ print " -e expire date (\"YYYY-MM-DD HH:MM:SS\")\n";
+ print " -A can change password ? 0 if no, 1 if yes\n";
+ print " -B must change password ? 0 if no, 1 if yes\n";
+ print " -C sambaHomePath (SMB home share, like '\\\\PDC-SRV\\homes')\n";
+ print " -D sambaHomeDrive (letter associated with home share, like 'H:')\n";
+ print " -E sambaLogonScript (DOS script to execute on login)\n";
+ print " -F sambaProfilePath (profile directory, like '\\\\PDC-SRV\\profiles\\foo')\n";
+ print " -H sambaAcctFlags (samba account control bits like '[NDHTUMWSLKI]')\n";
+ print " -I disable an user. Can't be used with -H or -J\n";
+ print " -J enable an user. Can't be used with -H or -I\n";
+ print " -?|-h show this help message\n";
+ exit (1);
+}
+
+if ($< != 0) {
+ print "You must be root to modify an user\n";
+ exit (1);
+}
+
+# Read only first @ARGV
+my $user = $ARGV[0];
+
+# Read user data
+my $user_entry = read_user_entry($user);
+if (!defined($user_entry)) {
+ print "$0: user $user doesn't exist\n";
+ exit (1);
+}
+
+my $samba = 0;
+if (grep ($_ =~ /^sambaSamAccount$/i, $user_entry->get_value('objectClass'))) {
+ $samba = 1;
+}
+
+# get the dn of the user
+my $dn= $user_entry->dn();
+
+my $tmp;
+my @mods;
+if (defined($tmp = $Options{'a'})) {
+ # Let's connect to the directory first
+ my $ldap_master=connect_ldap_master();
+ my $winmagic = 2147483647;
+ my $valpwdcanchange = 0;
+ my $valpwdmustchange = $winmagic;
+ my $valpwdlastset = 0;
+ my $valacctflags = "[UX]";
+ my $user_entry=read_user_entry($user);
+ my $uidNumber = $user_entry->get_value('uidNumber');
+ my $userRid = 2 * $uidNumber + 1000;
+ # apply changes
+ my $modify = $ldap_master->modify ( "$dn",
+ changes => [
+ add => [objectClass => 'sambaSamAccount'],
+ add => [sambaPwdLastSet => "$valpwdlastset"],
+ add => [sambaLogonTime => '0'],
+ add => [sambaLogoffTime => '2147483647'],
+ add => [sambaKickoffTime => '2147483647'],
+ add => [sambaPwdCanChange => "$valpwdcanchange"],
+ add => [sambaPwdMustChange => "$valpwdmustchange"],
+ add => [displayName => "$_userGecos"],
+ add => [sambaSID=> "$SID-$userRid"],
+ add => [sambaAcctFlags => "$valacctflags"],
+ ]
+ );
+ $modify->code && warn "failed to modify entry: ", $modify->error ;
+}
+
+# Process options
+my $changed_uid;
+my $_userUidNumber;
+my $_userRid;
+if (defined($tmp = $Options{'u'})) {
+ if (defined($Options{'o'})) {
+ $nscd_status = system "/etc/init.d/nscd status >/dev/null 2>&1";
+
+ if ($nscd_status == 0) {
+ system "/etc/init.d/nscd stop > /dev/null 2>&1";
+ }
+
+ if (getpwuid($tmp)) {
+ if ($nscd_status == 0) {
+ system "/etc/init.d/nscd start > /dev/null 2>&1";
+ }
+
+ print "$0: uid number $tmp exists\n";
+ exit (6);
+ }
+ if ($nscd_status == 0) {
+ system "/etc/init.d/nscd start > /dev/null 2>&1";
+ }
+
+ }
+ push(@mods, 'uidNumber', $tmp);
+ $_userUidNumber = $tmp;
+ if ($samba) {
+ # as rid we use 2 * uid + 1000
+ my $_userRid = 2 * $_userUidNumber + 1000;
+ if (defined($Options{'x'})) {
+ $_userRid= sprint("%x", $_userRid);
+ }
+ push(@mods, 'sambaSID', $SID.'-'.$_userRid);
+ }
+ $changed_uid = 1;
+}
+
+my $changed_gid;
+my $_userGidNumber;
+my $_userGroupSID;
+if (defined($tmp = $Options{'g'})) {
+ $_userGidNumber = parse_group($tmp);
+ if ($_userGidNumber < 0) {
+ print "$0: group $tmp doesn't exist\n";
+ exit (6);
+ }
+ push(@mods, 'gidNumber', $_userGidNumber);
+ if ($samba) {
+ # as grouprid we use the sambaSID attribute's value of the group
+ my $group_entry = read_group_entry_gid($_userGidNumber);
+ my $_userGroupSID = $group_entry->get_value('sambaSID');
+ unless ($_userGroupSID) {
+ print "$0: unknown group SID not set for unix group $_userGidNumber\n";
+ exit (7);
+ }
+ push(@mods, 'sambaPrimaryGroupSid', $_userGroupSID);
+ }
+ $changed_gid = 1;
+}
+
+if (defined($tmp = $Options{'s'})) {
+ push(@mods, 'loginShell' => $tmp);
+}
+
+
+if (defined($tmp = $Options{'c'})) {
+ push(@mods, 'gecos' => $tmp,
+ 'description' => $tmp);
+ if ($samba == 1) {
+ push(@mods, 'displayName' => $tmp);
+ }
+}
+
+if (defined($tmp = $Options{'d'})) {
+ push(@mods, 'homeDirectory' => $tmp);
+}
+
+if (defined($tmp = $Options{'N'})) {
+ push(@mods, 'cn' => $tmp);
+}
+
+if (defined($tmp = $Options{'S'})) {
+ push(@mods, 'sn' => $tmp);
+}
+
+if (defined($tmp = $Options{'G'})) {
+
+ # remove user from old groups
+ my $groups = find_groups_of $user;
+ my @grplines = split(/\n/,$groups);
+
+ my $grp;
+ foreach $grp (@grplines) {
+ my $gname = "";
+ if ( $grp =~ /dn: cn=([^,]+),/) {
+ $gname = $1;
+ #print "xx $gname\n";
+ }
+ if ($gname ne "") {
+ group_remove_member($gname, $user);
+ }
+ }
+
+ # add user to new groups
+ add_grouplist_user($tmp, $user);
+}
+
+#
+# A : sambaPwdCanChange
+# B : sambaPwdMustChange
+# C : sambaHomePath
+# D : sambaHomeDrive
+# E : sambaLogonScript
+# F : sambaProfilePath
+# H : sambaAcctFlags
+
+my $attr;
+my $winmagic = 2147483647;
+
+$samba = is_samba_user($user);
+
+if (defined($tmp = $Options{'e'})) {
+ if ($samba == 1) {
+ my $kickoffTime=`date --date='$tmp' +%s`;
+ chomp($kickoffTime);
+ push(@mods, 'sambakickoffTime' => $kickoffTime);
+ } else {
+ print "User $user is not a samba user\n";
+ }
+}
+
+my $_sambaPwdCanChange;
+if (defined($tmp = $Options{'A'})) {
+ if ($samba == 1) {
+ $attr = "sambaPwdCanChange";
+ if ($tmp != 0) {
+ $_sambaPwdCanChange=0;
+ } else {
+ $_sambaPwdCanChange=$winmagic;
+ }
+ push(@mods, 'sambaPwdCanChange' => $_sambaPwdCanChange);
+ } else {
+ print "User $user is not a samba user\n";
+ }
+}
+
+my $_sambaPwdMustChange;
+if (defined($tmp = $Options{'B'})) {
+ if ($samba == 1) {
+ if ($tmp != 0) {
+ $_sambaPwdMustChange=0;
+ # To force a user to change his password:
+ # . the attribut sambaPwdLastSet must be != 0
+ # . the attribut sambaAcctFlags must not match the 'X' flag
+ my $_sambaAcctFlags;
+ my $flags = $user_entry->get_value('sambaAcctFlags');
+ if ( $flags =~ /X/ ) {
+ my $letters;
+ if ($flags =~ /(\w+)/) {
+ $letters = $1;
+ }
+ $letters =~ s/X//;
+ $_sambaAcctFlags="\[$letters\]";
+ push(@mods, 'sambaAcctFlags' => $_sambaAcctFlags);
+ }
+ my $_sambaPwdLastSet = $user_entry->get_value('sambaPwdLastSet');
+ if ($_sambaPwdLastSet == 0) {
+ push(@mods, 'sambaPwdLastSet' => $winmagic);
+ }
+ } else {
+ $_sambaPwdMustChange=$winmagic;
+ }
+ push(@mods, 'sambaPwdMustChange' => $_sambaPwdMustChange);
+ } else {
+ print "User $user is not a samba user\n";
+ }
+}
+
+if (defined($tmp = $Options{'C'})) {
+ if ($samba == 1) {
+ #$tmp =~ s/\\/\\\\/g;
+ push(@mods, 'sambaHomePath' => $tmp);
+ } else {
+ print "User $user is not a samba user\n";
+ }
+}
+
+my $_sambaHomeDrive;
+if (defined($tmp = $Options{'D'})) {
+ if ($samba == 1) {
+ $tmp = $tmp.":" unless ($tmp =~ /:/);
+ push(@mods, 'sambaHomeDrive' => $tmp);
+ } else {
+ print "User $user is not a samba user\n";
+ }
+}
+
+if (defined($tmp = $Options{'E'})) {
+ if ($samba == 1) {
+ #$tmp =~ s/\\/\\\\/g;
+ push(@mods, 'sambaLogonScript' => $tmp);
+ } else {
+ print "User $user is not a samba user\n";
+ }
+}
+
+if (defined($tmp = $Options{'F'})) {
+ if ($samba == 1) {
+ #$tmp =~ s/\\/\\\\/g;
+ push(@mods, 'sambaProfilePath' => $tmp);
+ } else {
+ print "User $user is not a samba user\n";
+ }
+}
+
+if ($samba == 1 and (defined $Options{'H'} or defined $Options{'I'} or defined $Options{'J'})) {
+ my $_sambaAcctFlags;
+ if (defined($tmp = $Options{'H'})) {
+ #$tmp =~ s/\\/\\\\/g;
+ $_sambaAcctFlags=$tmp;
+ } else {
+ # I or J
+ my $flags;
+ $flags = $user_entry->get_value('sambaAcctFlags');
+
+ if (defined($tmp = $Options{'I'})) {
+ if ( !($flags =~ /D/) ) {
+ my $letters;
+ if ($flags =~ /(\w+)/) {
+ $letters = $1;
+ }
+ $_sambaAcctFlags="\[D$letters\]";
+ }
+ } elsif (defined($tmp = $Options{'J'})) {
+ if ( $flags =~ /D/ ) {
+ my $letters;
+ if ($flags =~ /(\w+)/) {
+ $letters = $1;
+ }
+ $letters =~ s/D//;
+ $_sambaAcctFlags="\[$letters\]";
+ }
+ }
+ }
+
+
+ if ("$_sambaAcctFlags" ne '') {
+ push(@mods, 'sambaAcctFlags' => $_sambaAcctFlags);
+ }
+
+} elsif (!$samba == 1 and (defined $Options{'H'} or defined $Options{'I'} or defined $Options{'J'})) {
+ print "User $user is not a samba user\n";
+}
+
+# Let's connect to the directory first
+my $ldap_master=connect_ldap_master();
+
+# apply changes
+my $modify = $ldap_master->modify ( "$dn",
+ 'replace' => { @mods }
+ );
+$modify->code && warn "failed to modify entry: ", $modify->error ;
+
+# take down session
+$ldap_master->unbind;
+
+$nscd_status = system "/etc/init.d/nscd status >/dev/null 2>&1";
+
+if ($nscd_status == 0) {
+ system "/etc/init.d/nscd restart > /dev/null 2>&1";
+}
+
+if (defined($Options{'P'})) {
+ exec "/usr/local/sbin/smbldap-passwd.pl $user"
+}
+
+
+############################################################
+
+=head1 NAME
+
+smbldap-usermod.pl - Modify a user account
+
+=head1 SYNOPSIS
+
+smbldap-usermod.pl [-c comment] [-d home_dir]
+ [-g initial_group] [-G group[,...]]
+ [-l login_name] [-p passwd]
+ [-s shell] [-u uid [ -o]] [-x]
+ [-A canchange] [-B mustchange] [-C smbhome]
+ [-D homedrive] [-E scriptpath] [-F profilepath]
+ [-H acctflags] login
+
+=head1 DESCRIPTION
+
+The smbldap-usermod.pl command modifies the system account files
+ to reflect the changes that are specified on the command line.
+ The options which apply to the usermod command are
+
+ -c comment
+ The new value of the user's comment field (gecos).
+
+ -d home_dir
+ The user's new login directory.
+
+ -g initial_group
+ The group name or number of the user's new initial login group.
+ The group name must exist. A group number must refer to an
+ already existing group. The default group number is 1.
+
+ -G group,[...]
+ A list of supplementary groups which the user is also a member
+ of. Each group is separated from the next by a comma, with no
+ intervening whitespace. The groups are subject to the same
+ restrictions as the group given with the -g option. If the user
+ is currently a member of a group which is not listed, the user
+ will be removed from the group
+
+ -l login_name
+ The name of the user will be changed from login to login_name.
+ Nothing else is changed. In particular, the user's home direc­
+ tory name should probably be changed to reflect the new login
+ name.
+
+ -s shell
+ The name of the user's new login shell. Setting this field to
+ blank causes the system to select the default login shell.
+
+ -u uid The numerical value of the user's ID. This value must be
+ unique, unless the -o option is used. The value must be non-
+ negative. Any files which the user owns and which are
+ located in the directory tree rooted at the user's home direc­
+ tory will have the file user ID changed automatically. Files
+ outside of the user's home directory must be altered manually.
+
+ -x Creates rid and primaryGroupID in hex instead of decimal (for
+ Samba 2.2.2 unpatched only - higher versions always use decimal)
+
+ -A can change password ? 0 if no, 1 if yes
+
+ -B must change password ? 0 if no, 1 if yes
+
+ -C sambaHomePath (SMB home share, like '\\\\PDC-SRV\\homes')
+
+ -D sambaHomeDrive (letter associated with home share, like 'H:')
+
+ -E sambaLogonScript, relative to the [netlogon] share (DOS script to execute on login, like 'foo.bat')
+
+ -F sambaProfilePath (profile directory, like '\\\\PDC-SRV\\profiles\\foo')
+
+ -H sambaAcctFlags, spaces and trailing bracket are ignored (samba account control bits like '[NDHTUMWSLKI]')
+
+ -I disable user. Can't be used with -H or -J
+
+ -J enable user. Can't be used with -H or -I
+
+=head1 SEE ALSO
+
+ usermod(1)
+
+=cut
+
+#'
diff --git a/examples/LDAP/smbldap-tools/smbldap-usershow.pl b/examples/LDAP/smbldap-tools/smbldap-usershow.pl
new file mode 100755
index 00000000000..173480d76c2
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/smbldap-usershow.pl
@@ -0,0 +1,72 @@
+#!/usr/bin/perl -w
+
+# This code was developped by IDEALX (http://IDEALX.org/) and
+# contributors (their names can be found in the CONTRIBUTORS file).
+#
+# Copyright (C) 2001-2002 IDEALX
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You 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.
+
+# Purpose of smbldap-userdisplay : user (posix,shadow,samba) display
+
+use strict;
+use FindBin;
+use FindBin qw($RealBin);
+use lib "$RealBin/";
+use smbldap_tools;
+
+use Getopt::Std;
+my %Options;
+
+my $ok = getopts('?', \%Options);
+
+if ( (!$ok) || (@ARGV < 1) || ($Options{'?'}) ) {
+ print "Usage: $0 [-?] username\n";
+ print " -? show this help message\n";
+ exit (1);
+}
+
+# Read only first @ARGV
+my $user = $ARGV[0];
+
+my $lines = read_user($user);
+if (!defined($lines)) {
+ print "$0: user $user doesn't exist\n";
+ exit (1);
+}
+
+print "$lines\n";
+
+exit(0);
+
+############################################################
+
+=head1 NAME
+
+ smbldap-usershow.pl - Show a user account informations
+
+=head1 SYNOPSIS
+
+ smbldap-usershow.pl login
+
+=head1 DESCRIPTION
+
+ The smbldap-usershow.pl command displays the informations
+ associated with the login. The named user must exist.
+
+=cut
+
+#'
diff --git a/examples/LDAP/smbldap-tools/smbldap_conf.pm b/examples/LDAP/smbldap-tools/smbldap_conf.pm
new file mode 100644
index 00000000000..257c205a2cd
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/smbldap_conf.pm
@@ -0,0 +1,248 @@
+#!/usr/bin/perl
+use strict;
+package smbldap_conf;
+
+# smbldap-tools.conf : Q & D configuration file for smbldap-tools
+
+# This code was developped by IDEALX (http://IDEALX.org/) and
+# contributors (their names can be found in the CONTRIBUTORS file).
+#
+# Copyright (C) 2001-2002 IDEALX
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You 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.
+
+# Purpose :
+# . be the configuration file for all smbldap-tools scripts
+
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS
+ $UID_START $GID_START $smbpasswd $slaveLDAP $masterLDAP
+ $slavePort $masterPort $ldapSSL $slaveURI $masterURI $with_smbpasswd $mk_ntpasswd
+ $ldap_path $ldap_opts $ldapmodify $suffix $usersdn $computersdn
+ $groupsdn $scope $binddn $bindpasswd
+ $slaveDN $slavePw $masterDN $masterPw
+ $_userLoginShell $_userHomePrefix $_userGecos
+ $_defaultUserGid $_defaultComputerGid
+ $_skeletonDir $_userSmbHome
+ $_userProfile $_userHomeDrive
+ $_userScript $usersou $computersou $groupsou $SID $hash_encrypt $_defaultMaxPasswordAge
+ );
+
+use Exporter;
+$VERSION = 1.00;
+@ISA = qw(Exporter);
+
+@EXPORT = qw(
+ $UID_START $GID_START $smbpasswd $slaveLDAP $masterLDAP
+ $slavePort $masterPort $ldapSSL $slaveURI $masterURI $with_smbpasswd $mk_ntpasswd
+ $ldap_path $ldap_opts $ldapmodify $suffix $usersdn
+ $computersdn $groupsdn $scope $binddn $bindpasswd
+ $slaveDN $slavePw $masterDN $masterPw
+ $_userLoginShell $_userHomePrefix $_userGecos
+ $_defaultUserGid $_defaultComputerGid $_skeletonDir
+ $_userSmbHome $_userProfile $_userHomeDrive $_userScript
+ $usersou $computersou $groupsou $SID $hash_encrypt $_defaultMaxPasswordAge
+ );
+
+
+##############################################################################
+#
+# General Configuration
+#
+##############################################################################
+
+# UID and GID starting at...
+$UID_START = 1000;
+$GID_START = 1000;
+
+# Put your own SID
+# to obtain this number do: "net getlocalsid"
+$SID='S-1-5-21-3516781642-1962875130-3438800523';
+
+##############################################################################
+#
+# LDAP Configuration
+#
+##############################################################################
+
+# Notes: to use to dual ldap servers backend for Samba, you must patch
+# Samba with the dual-head patch from IDEALX. If not using this patch
+# just use the same server for slaveLDAP and masterLDAP.
+# Those two servers declarations can also be used when you have
+# . one master LDAP server where all writing operations must be done
+# . one slave LDAP server where all reading operations must be done
+# (typically a replication directory)
+
+# Ex: $slaveLDAP = "127.0.0.1";
+$slaveLDAP = "127.0.0.1";
+$slavePort = "389";
+
+# Master LDAP : needed for write operations
+# Ex: $masterLDAP = "127.0.0.1";
+$masterLDAP = "127.0.0.1";
+$masterPort = "389";
+
+# Use SSL for LDAP
+# If set to "1", this option will use start_tls for connection
+# (you should also used the port 389)
+$ldapSSL = "0";
+
+# LDAP Suffix
+# Ex: $suffix = "dc=IDEALX,dc=ORG";
+$suffix = "dc=IDEALX,dc=COM";
+
+
+# Where are stored Users
+# Ex: $usersdn = "ou=Users,$suffix"; for ou=Users,dc=IDEALX,dc=ORG
+$usersou = q(_USERS_);
+$usersdn = "ou=$usersou,$suffix";
+
+# Where are stored Computers
+# Ex: $computersdn = "ou=Computers,$suffix"; for ou=Computers,dc=IDEALX,dc=ORG
+$computersou = q(_COMPUTERS_);
+$computersdn = "ou=$computersou,$suffix";
+
+# Where are stored Groups
+# Ex $groupsdn = "ou=Groups,$suffix"; for ou=Groups,dc=IDEALX,dc=ORG
+$groupsou = q(_GROUPS_);
+$groupsdn = "ou=$groupsou,$suffix";
+
+# Default scope Used
+$scope = "sub";
+
+# Unix password encryption (CRYPT, MD5, SMD5, SSHA, SHA)
+$hash_encrypt="SSHA";
+
+############################
+# Credential Configuration #
+############################
+# Bind DN used
+# Ex: $binddn = "cn=Manager,$suffix"; for cn=Manager,dc=IDEALX,dc=org
+$binddn = "cn=Manager,$suffix";
+
+# Bind DN passwd used
+# Ex: $bindpasswd = 'secret'; for 'secret'
+$bindpasswd = "secret";
+
+# Notes: if using dual ldap patch, you can specify to different configuration
+# By default, we will use the same DN (so it will work for standard Samba
+# release)
+$slaveDN = $binddn;
+$slavePw = $bindpasswd;
+$masterDN = $binddn;
+$masterPw = $bindpasswd;
+
+##############################################################################
+#
+# Unix Accounts Configuration
+#
+##############################################################################
+
+# Login defs
+# Default Login Shell
+# Ex: $_userLoginShell = q(/bin/bash);
+$_userLoginShell = q(_LOGINSHELL_);
+
+# Home directory prefix (without username)
+# Ex: $_userHomePrefix = q(/home/);
+$_userHomePrefix = q(_HOMEPREFIX_);
+
+# Gecos
+$_userGecos = q(System User);
+
+# Default User (POSIX and Samba) GID
+$_defaultUserGid = 513;
+
+# Default Computer (Samba) GID
+$_defaultComputerGid = 553;
+
+# Skel dir
+$_skeletonDir = q(/etc/skel);
+
+# Default password validation time (time in days) Comment the next line if
+# you don't want password to be enable for $_defaultMaxPasswordAge days (be
+# careful to the sambaPwdMustChange attribute's value)
+$_defaultMaxPasswordAge = 45;
+
+##############################################################################
+#
+# SAMBA Configuration
+#
+##############################################################################
+
+# The UNC path to home drives location without the username last extension
+# (will be dynamically prepended)
+# Ex: q(\\\\My-PDC-netbios-name\\homes) for \\My-PDC-netbios-name\homes
+# Just comment this if you want to use the smb.conf 'logon home' directive
+# and/or desabling roaming profiles
+$_userSmbHome = q(\\\\_PDCNAME_\\homes);
+
+# The UNC path to profiles locations without the username last extension
+# (will be dynamically prepended)
+# Ex: q(\\\\My-PDC-netbios-name\\profiles\\) for \\My-PDC-netbios-name\profiles
+# Just comment this if you want to use the smb.conf 'logon path' directive
+# and/or desabling roaming profiles
+$_userProfile = q(\\\\_PDCNAME_\\profiles\\);
+
+# The default Home Drive Letter mapping
+# (will be automatically mapped at logon time if home directory exist)
+# Ex: q(U:) for U:
+$_userHomeDrive = q(_HOMEDRIVE_);
+
+# The default user netlogon script name
+# if not used, will be automatically username.cmd
+# $_userScript = q(startup.cmd); # make sure script file is edited under dos
+
+
+##############################################################################
+#
+# SMBLDAP-TOOLS Configuration (default are ok for a RedHat)
+#
+##############################################################################
+
+# Allows not to use smbpasswd (if $with_smbpasswd == 0 in smbldap_conf.pm) but
+# prefer mkntpwd... most of the time, it's a wise choice :-)
+$with_smbpasswd = 0;
+$smbpasswd = "/usr/bin/smbpasswd";
+$mk_ntpasswd = "/usr/local/sbin/mkntpwd";
+
+# those next externals commands are kept fot the migration scripts and
+# for the populate script: this will be updated as soon as possible
+$slaveURI = "ldap://$slaveLDAP:$slavePort";
+$masterURI = "ldap://$masterLDAP:$masterPort";
+
+$ldap_path = "/usr/bin";
+
+if ( $ldapSSL eq "0" ) {
+ $ldap_opts = "-x";
+} elsif ( $ldapSSL eq "1" ) {
+ $ldap_opts = "-x -Z";
+} else {
+ die "ldapSSL option must be either 0 or 1.\n";
+}
+
+#$ldapsearch = "$ldap_path/ldapsearch $ldap_opts -H $slaveURI -D '$slaveDN' -w '$slavePw'";
+#$ldapsearchnobind = "$ldap_path/ldapsearch $ldap_opts -H $slaveURI";
+$ldapmodify = "$ldap_path/ldapmodify $ldap_opts -H $masterURI -D '$masterDN' -w '$masterPw'";
+#$ldappasswd = "$ldap_path/ldappasswd $ldap_opts -H $masterURI -D '$masterDN' -w '$masterPw'";
+#$ldapadd = "$ldap_path/ldapadd $ldap_opts -H $masterURI -D '$masterDN' -w '$masterPw'";
+#$ldapdelete = "$ldap_path/ldapdelete $ldap_opts -H $masterURI -D '$masterDN' -w '$masterPw'";
+#$ldapmodrdn = "$ldap_path/ldapmodrdn $ldap_opts -H $masterURI -D '$masterDN' -w '$masterPw'";
+
+
+
+1;
+
+# - The End
diff --git a/examples/LDAP/smbldap-tools/smbldap_tools.pm b/examples/LDAP/smbldap-tools/smbldap_tools.pm
new file mode 100755
index 00000000000..d33a65b7d17
--- /dev/null
+++ b/examples/LDAP/smbldap-tools/smbldap_tools.pm
@@ -0,0 +1,771 @@
+#! /usr/bin/perl -w
+use strict;
+package smbldap_tools;
+use smbldap_conf;
+use Net::LDAP;
+
+# This code was developped by IDEALX (http://IDEALX.org/) and
+# contributors (their names can be found in the CONTRIBUTORS file).
+#
+# Copyright (C) 2001-2002 IDEALX
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You 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.
+
+
+# ugly funcs using global variables and spawning openldap clients
+
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
+use Exporter;
+$VERSION = 1.00;
+
+@ISA = qw(Exporter);
+
+@EXPORT = qw(
+ get_user_dn
+ get_group_dn
+ is_group_member
+ is_samba_user
+ is_unix_user
+ is_user_valid
+ does_sid_exist
+ get_dn_from_line
+ add_posix_machine
+ add_samba_machine
+ add_samba_machine_mkntpwd
+ group_add_user
+ add_grouplist_user
+ disable_user
+ delete_user
+ group_add
+ group_del
+ get_homedir
+ read_user
+ read_user_entry
+ read_group
+ read_group_entry
+ read_group_entry_gid
+ find_groups_of
+ parse_group
+ group_remove_member
+ group_get_members
+ do_ldapadd
+ do_ldapmodify
+ get_user_dn2
+ connect_ldap_master
+ connect_ldap_slave
+ group_type_by_name
+ );
+
+sub connect_ldap_master
+ {
+ # bind to a directory with dn and password
+ my $ldap_master = Net::LDAP->new(
+ "$masterLDAP",
+ port => "$masterPort",
+ version => 3,
+ # debug => 0xffff,
+ )
+ or die "erreur LDAP: Can't contact master ldap server ($@)";
+ if ($ldapSSL == 1) {
+ $ldap_master->start_tls(
+ # verify => 'require',
+ # clientcert => 'mycert.pem',
+ # clientkey => 'mykey.pem',
+ # decryptkey => sub { 'secret'; },
+ # capath => '/usr/local/cacerts/'
+ );
+ }
+ $ldap_master->bind ( "$binddn",
+ password => "$masterPw"
+ );
+ return($ldap_master);
+ }
+
+sub connect_ldap_slave
+ {
+ # bind to a directory with dn and password
+ my $ldap_slave = Net::LDAP->new(
+ "$slaveLDAP",
+ port => "$slavePort",
+ version => 3,
+ # debug => 0xffff,
+ )
+ or die "erreur LDAP: Can't contact slave ldap server ($@)";
+ if ($ldapSSL == 1) {
+ $ldap_slave->start_tls(
+ # verify => 'require',
+ # clientcert => 'mycert.pem',
+ # clientkey => 'mykey.pem',
+ # decryptkey => sub { 'secret'; },
+ # capath => '/usr/local/cacerts/'
+ );
+ }
+ $ldap_slave->bind ( "$binddn",
+ password => "$slavePw"
+ );
+ return($ldap_slave);
+ }
+
+sub get_user_dn
+ {
+ my $user = shift;
+ my $dn='';
+ my $ldap_slave=connect_ldap_slave();
+ my $mesg = $ldap_slave->search ( base => $suffix,
+ scope => $scope,
+ filter => "(&(objectclass=posixAccount)(uid=$user))"
+ );
+ $mesg->code && die $mesg->error;
+ foreach my $entry ($mesg->all_entries) {
+ $dn= $entry->dn;
+ }
+ $ldap_slave->unbind;
+ chomp($dn);
+ if ($dn eq '') {
+ return undef;
+ }
+ $dn="dn: ".$dn;
+ return $dn;
+ }
+
+
+sub get_user_dn2
+ {
+ my $user = shift;
+ my $dn='';
+ my $ldap_slave=connect_ldap_slave();
+ my $mesg = $ldap_slave->search ( base => $suffix,
+ scope => $scope,
+ filter => "(&(objectclass=posixAccount)(uid=$user))"
+ );
+ $mesg->code && warn "failed to perform search; ", $mesg->error;
+
+ foreach my $entry ($mesg->all_entries) {
+ $dn= $entry->dn;
+ }
+ $ldap_slave->unbind;
+ chomp($dn);
+ if ($dn eq '') {
+ return (1,undef);
+ }
+ $dn="dn: ".$dn;
+ return (1,$dn);
+ }
+
+
+sub get_group_dn
+ {
+ my $group = shift;
+ my $dn='';
+ my $ldap_slave=connect_ldap_slave();
+ my $mesg = $ldap_slave->search ( base => $groupsdn,
+ scope => $scope,
+ filter => "(&(objectclass=posixGroup)(|(cn=$group)(gidNumber=$group)))"
+ );
+ $mesg->code && die $mesg->error;
+ foreach my $entry ($mesg->all_entries) {
+ $dn= $entry->dn;
+ }
+ $ldap_slave->unbind;
+ chomp($dn);
+ if ($dn eq '') {
+ return undef;
+ }
+ $dn="dn: ".$dn;
+ return $dn;
+ }
+
+# return (success, dn)
+# bool = is_samba_user($username)
+sub is_samba_user
+ {
+ my $user = shift;
+ my $ldap_slave=connect_ldap_slave();
+ my $mesg = $ldap_slave->search ( base => $suffix,
+ scope => $scope,
+ filter => "(&(objectClass=sambaSamAccount)(uid=$user))"
+ );
+ $mesg->code && die $mesg->error;
+ $ldap_slave->unbind;
+ return ($mesg->count ne 0);
+ }
+
+sub is_unix_user
+ {
+ my $user = shift;
+ my $ldap_slave=connect_ldap_slave();
+ my $mesg = $ldap_slave->search ( base => $suffix,
+ scope => $scope,
+ filter => "(&(objectClass=posixAccount)(uid=$user))"
+ );
+ $mesg->code && die $mesg->error;
+ $ldap_slave->unbind;
+ return ($mesg->count ne 0);
+ }
+
+sub is_group_member
+ {
+ my $dn_group = shift;
+ my $user = shift;
+ my $ldap_slave=connect_ldap_slave();
+ my $mesg = $ldap_slave->search ( base => $dn_group,
+ scope => 'base',
+ filter => "(&(memberUid=$user))"
+ );
+ $mesg->code && die $mesg->error;
+ $ldap_slave->unbind;
+ return ($mesg->count ne 0);
+ }
+
+# all entries = does_sid_exist($sid,$scope)
+sub does_sid_exist
+ {
+ my $sid = shift;
+ my $dn_group=shift;
+ my $ldap_slave=connect_ldap_slave();
+ my $mesg = $ldap_slave->search ( base => $dn_group,
+ scope => $scope,
+ filter => "(sambaSID=$sid)"
+ #filter => "(&(objectClass=sambaSamAccount|objectClass=sambaGroupMapping)(sambaSID=$sid))"
+ );
+ $mesg->code && die $mesg->error;
+ $ldap_slave->unbind;
+ return ($mesg);
+ }
+
+# try to bind with user dn and password to validate current password
+sub is_user_valid
+ {
+ my ($user, $dn, $pass) = @_;
+ my $ldap = Net::LDAP->new($slaveLDAP) or die "erreur LDAP";
+ my $mesg= $ldap->bind (dn => $dn, password => $pass );
+ if ($mesg->code eq 0) {
+ $ldap->unbind;
+ return 1;
+ } else {
+ if ($ldap->bind()) {
+ $ldap->unbind;
+ return 0;
+ } else {
+ print ("The LDAP directory is not available.\n Check the server, cables ...");
+ $ldap->unbind;
+ return 0;
+ }
+ die "Problem : contact your administrator";
+ }
+ }
+
+
+# dn = get_dn_from_line ($dn_line)
+# helper to get "a=b,c=d" from "dn: a=b,c=d"
+sub get_dn_from_line
+ {
+ my $dn = shift;
+ $dn =~ s/^dn: //;
+ return $dn;
+ }
+
+
+# success = add_posix_machine($user, $uid, $gid)
+sub add_posix_machine
+ {
+ my ($user, $uid, $gid) = @_;
+ # bind to a directory with dn and password
+ my $ldap_master=connect_ldap_master();
+ my $add = $ldap_master->add ( "uid=$user,$computersdn",
+ attr => [
+ 'objectclass' => ['top','inetOrgPerson', 'posixAccount'],
+ 'cn' => "$user",
+ 'sn' => "$user",
+ 'uid' => "$user",
+ 'uidNumber' => "$uid",
+ 'gidNumber' => "$gid",
+ 'homeDirectory' => '/dev/null',
+ 'loginShell' => '/bin/false',
+ 'description' => 'Computer',
+ ]
+ );
+
+ $add->code && warn "failed to add entry: ", $add->error ;
+ # take down the session
+ $ldap_master->unbind;
+
+ }
+
+
+# success = add_samba_machine($computername)
+sub add_samba_machine
+ {
+ my $user = shift;
+ system "smbpasswd -a -m $user";
+ return 1;
+ }
+
+sub add_samba_machine_mkntpwd
+ {
+ my ($user, $uid) = @_;
+ my $sambaSID = 2 * $uid + 1000;
+ my $name = $user;
+ $name =~ s/.$//s;
+
+ if ($mk_ntpasswd eq '') {
+ print "Either set \$with_smbpasswd = 1 or specify \$mk_ntpasswd\n";
+ return 0;
+ }
+
+ my $ntpwd = `$mk_ntpasswd '$name'`;
+ chomp(my $lmpassword = substr($ntpwd, 0, index($ntpwd, ':')));
+ chomp(my $ntpassword = substr($ntpwd, index($ntpwd, ':')+1));
+
+ my $ldap_master=connect_ldap_master();
+ my $modify = $ldap_master->modify ( "uid=$user,$computersdn",
+ changes => [
+ replace => [objectClass => ['inetOrgPerson', 'posixAccount', 'sambaSamAccount']],
+ add => [sambaPwdLastSet => '0'],
+ add => [sambaLogonTime => '0'],
+ add => [sambaLogoffTime => '2147483647'],
+ add => [sambaKickoffTime => '2147483647'],
+ add => [sambaPwdCanChange => '0'],
+ add => [sambaPwdMustChange => '0'],
+ add => [sambaAcctFlags => '[W ]'],
+ add => [sambaLMPassword => "$lmpassword"],
+ add => [sambaNTPassword => "$ntpassword"],
+ add => [sambaSID => "$SID-$sambaSID"],
+ add => [sambaPrimaryGroupSID => "$SID-0"]
+ ]
+ );
+
+ $modify->code && die "failed to add entry: ", $modify->error ;
+
+ return 1;
+ # take down the session
+ $ldap_master->unbind;
+
+ }
+
+
+sub group_add_user
+ {
+ my ($group, $userid) = @_;
+ my $members='';
+ my $dn_line = get_group_dn($group);
+ if (!defined(get_group_dn($group))) {
+ print "$0: group \"$group\" doesn't exist\n";
+ exit (6);
+ }
+ if (!defined($dn_line)) {
+ return 1;
+ }
+ my $dn = get_dn_from_line("$dn_line");
+ # on look if the user is already present in the group
+ my $is_member=is_group_member($dn,$userid);
+ if ($is_member == 1) {
+ print "User \"$userid\" already member of the group \"$group\".\n";
+ } else {
+ # bind to a directory with dn and password
+ my $ldap_master=connect_ldap_master();
+ # It does not matter if the user already exist, Net::LDAP will add the user
+ # if he does not exist, and ignore him if his already in the directory.
+ my $modify = $ldap_master->modify ( "$dn",
+ changes => [
+ add => [memberUid => $userid]
+ ]
+ );
+ $modify->code && die "failed to modify entry: ", $modify->error ;
+ # take down session
+ $ldap_master->unbind;
+ }
+ }
+
+sub group_del
+ {
+ my $group_dn=shift;
+ # bind to a directory with dn and password
+ my $ldap_master=connect_ldap_master();
+ my $modify = $ldap_master->delete ($group_dn);
+ $modify->code && die "failed to delete group : ", $modify->error ;
+ # take down session
+ $ldap_master->unbind;
+ }
+
+sub add_grouplist_user
+ {
+ my ($grouplist, $user) = @_;
+ my @array = split(/,/, $grouplist);
+ foreach my $group (@array) {
+ group_add_user($group, $user);
+ }
+ }
+
+sub disable_user
+ {
+ my $user = shift;
+ my $dn_line;
+ my $dn = get_dn_from_line($dn_line);
+
+ if (!defined($dn_line = get_user_dn($user))) {
+ print "$0: user $user doesn't exist\n";
+ exit (10);
+ }
+ my $ldap_master=connect_ldap_master();
+ my $modify = $ldap_master->modify ( "$dn",
+ changes => [
+ replace => [userPassword => '{crypt}!x']
+ ]
+ );
+ $modify->code && die "failed to modify entry: ", $modify->error ;
+
+ if (is_samba_user($user)) {
+ my $modify = $ldap_master->modify ( "$dn",
+ changes => [
+ replace => [sambaAcctFlags => '[D ]']
+ ]
+ );
+ $modify->code && die "failed to modify entry: ", $modify->error ;
+ }
+ # take down session
+ $ldap_master->unbind;
+ }
+
+# delete_user($user)
+sub delete_user
+ {
+ my $user = shift;
+ my $dn_line;
+
+ if (!defined($dn_line = get_user_dn($user))) {
+ print "$0: user $user doesn't exist\n";
+ exit (10);
+ }
+
+ my $dn = get_dn_from_line($dn_line);
+ my $ldap_master=connect_ldap_master();
+ my $modify = $ldap_master->delete($dn);
+ $ldap_master->unbind;
+ }
+
+# $gid = group_add($groupname, $group_gid, $force_using_existing_gid)
+sub group_add
+ {
+ my ($gname, $gid, $force) = @_;
+ my $nscd_status = system "/etc/init.d/nscd status >/dev/null 2>&1";
+ if ($nscd_status == 0) {
+ system "/etc/init.d/nscd stop > /dev/null 2>&1";
+ }
+ if (!defined($gid)) {
+ while (defined(getgrgid($GID_START))) {
+ $GID_START++;
+ }
+ $gid = $GID_START;
+ } else {
+ if (!defined($force)) {
+ if (defined(getgrgid($gid))) {
+ return undef;
+ }
+ }
+ }
+ if ($nscd_status == 0) {
+ system "/etc/init.d/nscd start > /dev/null 2>&1";
+ }
+ my $ldap_master=connect_ldap_master();
+ my $modify = $ldap_master->add ( "cn=$gname,$groupsdn",
+ attrs => [
+ objectClass => 'posixGroup',
+ cn => "$gname",
+ gidNumber => "$gid"
+ ]
+ );
+
+ $modify->code && die "failed to add entry: ", $modify->error ;
+ # take down session
+ $ldap_master->unbind;
+ return $gid;
+ }
+
+# $homedir = get_homedir ($user)
+sub get_homedir
+ {
+ my $user = shift;
+ my $homeDir='';
+ my $ldap_slave=connect_ldap_slave();
+ my $mesg = $ldap_slave->search (
+ base =>$suffix,
+ scope => $scope,
+ filter => "(&(objectclass=posixAccount)(uid=$user))"
+ );
+ $mesg->code && die $mesg->error;
+ foreach my $entry ($mesg->all_entries) {
+ foreach my $attr ($entry->attributes) {
+ if ($attr=~/\bhomeDirectory\b/) {
+ foreach my $ent ($entry->get_value($attr)) {
+ $homeDir.= $attr.": ".$ent."\n";
+ }
+ }
+ }
+ }
+ $ldap_slave->unbind;
+ chomp $homeDir;
+ if ($homeDir eq '') {
+ return undef;
+ }
+ $homeDir =~ s/^homeDirectory: //;
+ return $homeDir;
+ }
+
+# search for an user
+sub read_user
+ {
+ my $user = shift;
+ my $lines ='';
+ my $ldap_slave=connect_ldap_slave();
+ my $mesg = $ldap_slave->search ( # perform a search
+ base => $suffix,
+ scope => $scope,
+ filter => "(&(objectclass=posixAccount)(uid=$user))"
+ );
+
+ $mesg->code && die $mesg->error;
+ foreach my $entry ($mesg->all_entries) {
+ $lines.= "dn: " . $entry->dn."\n";
+ foreach my $attr ($entry->attributes) {
+ {
+ $lines.= $attr.": ".join(',', $entry->get_value($attr))."\n";
+ }
+ }
+ }
+ # take down session
+ $ldap_slave->unbind;
+ chomp $lines;
+ if ($lines eq '') {
+ return undef;
+ }
+ return $lines;
+ }
+
+# search for a user
+# return the attributes in an array
+sub read_user_entry
+ {
+ my $user = shift;
+ my $ldap_slave=connect_ldap_slave();
+ my $mesg = $ldap_slave->search ( # perform a search
+ base => $suffix,
+ scope => $scope,
+ filter => "(&(objectclass=posixAccount)(uid=$user))"
+ );
+
+ $mesg->code && die $mesg->error;
+ my $entry = $mesg->entry();
+ $ldap_slave->unbind;
+ return $entry;
+ }
+
+# search for a group
+sub read_group
+ {
+ my $user = shift;
+ my $lines ='';
+ my $ldap_slave=connect_ldap_slave();
+ my $mesg = $ldap_slave->search ( # perform a search
+ base => $groupsdn,
+ scope => $scope,
+ filter => "(&(objectclass=posixGroup)(cn=$user))"
+ );
+
+ $mesg->code && die $mesg->error;
+ foreach my $entry ($mesg->all_entries) {
+ $lines.= "dn: " . $entry->dn."\n";
+ foreach my $attr ($entry->attributes) {
+ {
+ $lines.= $attr.": ".join(',', $entry->get_value($attr))."\n";
+ }
+ }
+ }
+ # take down session
+ $ldap_slave->unbind;
+ chomp $lines;
+ if ($lines eq '') {
+ return undef;
+ }
+ return $lines;
+ }
+
+# find groups of a given user
+##### MODIFIE ########
+sub find_groups_of
+ {
+ my $user = shift;
+ my $lines ='';
+ my $ldap_slave=connect_ldap_slave;
+ my $mesg = $ldap_slave->search ( # perform a search
+ base => $groupsdn,
+ scope => $scope,
+ filter => "(&(objectclass=posixGroup)(memberuid=$user))"
+ );
+ $mesg->code && die $mesg->error;
+ foreach my $entry ($mesg->all_entries) {
+ $lines.= "dn: ".$entry->dn."\n";
+ }
+ $ldap_slave->unbind;
+ chomp($lines);
+ if ($lines eq '') {
+ return undef;
+ }
+ return $lines;
+ }
+
+sub read_group_entry {
+ my $group = shift;
+ my $entry;
+ my %res;
+ my $ldap_slave=connect_ldap_slave();
+ my $mesg = $ldap_slave->search ( # perform a search
+ base => $groupsdn,
+ scope => $scope,
+ filter => "(&(objectclass=posixGroup)(cn=$group))"
+ );
+
+ $mesg->code && die $mesg->error;
+ my $nb=$mesg->count;
+ if ($nb > 1) {
+ print "Error: $nb groups exist \"cn=$group\"\n";
+ foreach $entry ($mesg->all_entries) { my $dn=$entry->dn; print " $dn\n"; }
+ exit 11;
+ } else {
+ $entry = $mesg->shift_entry();
+ }
+ return $entry;
+}
+
+sub read_group_entry_gid {
+ my $group = shift;
+ my %res;
+ my $ldap_slave=connect_ldap_slave();
+ my $mesg = $ldap_slave->search ( # perform a search
+ base => $groupsdn,
+ scope => $scope,
+ filter => "(&(objectclass=posixGroup)(gidNumber=$group))"
+ );
+
+ $mesg->code && die $mesg->error;
+ my $entry = $mesg->shift_entry();
+ return $entry;
+}
+
+# return the gidnumber for a group given as name or gid
+# -1 : bad group name
+# -2 : bad gidnumber
+sub parse_group
+ {
+ my $userGidNumber = shift;
+ if ($userGidNumber =~ /[^\d]/ ) {
+ my $gname = $userGidNumber;
+ my $gidnum = getgrnam($gname);
+ if ($gidnum !~ /\d+/) {
+ return -1;
+ } else {
+ $userGidNumber = $gidnum;
+ }
+ } elsif (!defined(getgrgid($userGidNumber))) {
+ return -2;
+ }
+ return $userGidNumber;
+ }
+
+# remove $user from $group
+sub group_remove_member
+ {
+ my ($group, $user) = @_;
+ my $members='';
+ my $grp_line = get_group_dn($group);
+ if (!defined($grp_line)) {
+ return 0;
+ }
+ my $dn = get_dn_from_line($grp_line);
+ # we test if the user exist in the group
+ my $is_member=is_group_member($dn,$user);
+ if ($is_member == 1) {
+ my $ldap_master=connect_ldap_master();
+ # delete only the user from the group
+ my $modify = $ldap_master->modify ( "$dn",
+ changes => [
+ delete => [memberUid => ["$user"]]
+ ]
+ );
+ $modify->code && die "failed to delete entry: ", $modify->error ;
+ $ldap_master->unbind;
+ }
+ return 1;
+ }
+
+sub group_get_members
+ {
+ my ($group) = @_;
+ my $members;
+ my @resultat;
+ my $grp_line = get_group_dn($group);
+ if (!defined($grp_line)) {
+ return 0;
+ }
+
+ my $ldap = Net::LDAP->new($slaveLDAP) or die "erreur LDAP";
+ $ldap->bind ;
+ my $mesg = $ldap->search (
+ base => $groupsdn,
+ scope => $scope,
+ filter => "(&(objectclass=posixgroup)(cn=$group))"
+ );
+ $mesg->code && die $mesg->error;
+ foreach my $entry ($mesg->all_entries) {
+ foreach my $attr ($entry->attributes) {
+ if ($attr=~/\bmemberUid\b/) {
+ foreach my $ent ($entry->get_value($attr)) {
+ push (@resultat,$ent);
+ }
+ }
+ }
+ }
+ return @resultat;
+ }
+
+sub do_ldapmodify
+ {
+ my $ldif = shift;
+ my $FILE = "|$ldapmodify -r >/dev/null";
+ open (FILE, $FILE) || die "$!\n";
+ print FILE <<EOF;
+$ldif
+EOF
+ ;
+ close FILE;
+ my $rc = $?;
+ return $rc;
+ }
+
+sub group_type_by_name {
+ my $type_name = shift;
+ my %groupmap = (
+ 'domain' => 2,
+ 'local' => 4,
+ 'builtin' => 5
+ );
+ return $groupmap{$type_name};
+}
+
+
+
+1;
+
diff --git a/examples/README b/examples/README
new file mode 100644
index 00000000000..22226067140
--- /dev/null
+++ b/examples/README
@@ -0,0 +1,11 @@
+Copyright(C) Samba-Team 1993-2001
+
+This directory contains example config files and related material for
+Samba.
+
+At a minimum please refer to the smb.conf.default file for current
+information regarding global and share parameter settings.
+
+Send additions to: samba@samba.org
+
+
diff --git a/examples/VFS/.cvsignore b/examples/VFS/.cvsignore
new file mode 100644
index 00000000000..f269c98273c
--- /dev/null
+++ b/examples/VFS/.cvsignore
@@ -0,0 +1,9 @@
+.libs
+*.so
+*.o
+*.bak
+autom4te.cache
+autom4te-2.53.cache
+Makefile
+configure
+config.*
diff --git a/examples/VFS/Makefile.in b/examples/VFS/Makefile.in
new file mode 100644
index 00000000000..c368974bd5e
--- /dev/null
+++ b/examples/VFS/Makefile.in
@@ -0,0 +1,43 @@
+CC = @CC@
+CFLAGS = @CFLAGS@
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+LDSHFLAGS = @LDSHFLAGS@
+INSTALLCMD = @INSTALL@
+SAMBA_SOURCE = @SAMBA_SOURCE@
+SHLIBEXT = @SHLIBEXT@
+OBJEXT = @OBJEXT@
+FLAGS = $(CFLAGS) -Iinclude -I$(SAMBA_SOURCE)/include -I$(SAMBA_SOURCE)/ubiqx -I$(SAMBA_SOURCE)/smbwrapper -I. $(CPPFLAGS) -I$(SAMBA_SOURCE)
+
+
+prefix = @prefix@
+libdir = @libdir@
+
+VFS_LIBDIR = $(libdir)/vfs
+
+# Auto target
+default: $(patsubst %.c,%.$(SHLIBEXT),$(wildcard *.c))
+
+# Pattern rules
+
+%.$(SHLIBEXT): %.$(OBJEXT)
+ @echo "Linking $@"
+ @$(CC) $(LDSHFLAGS) $(LDFLAGS) -o $@ $<
+
+%.$(OBJEXT): %.c
+ @echo "Compiling $<"
+ @$(CC) $(FLAGS) -c $<
+
+
+install: default
+ $(INSTALLCMD) -d $(VFS_LIBDIR)
+ $(INSTALLCMD) -m 755 *.$(SHLIBEXT) $(VFS_LIBDIR)
+
+# Misc targets
+clean:
+ rm -rf .libs
+ rm -f core *~ *% *.bak *.o *.$(SHLIBEXT)
+
+distclean: clean
+ rm config.* Makefile
+
diff --git a/examples/VFS/README b/examples/VFS/README
new file mode 100644
index 00000000000..2f6196d1178
--- /dev/null
+++ b/examples/VFS/README
@@ -0,0 +1,20 @@
+README for Samba Virtual File System (VFS) Example
+===================================================
+
+This directory contains skeleton VFS modules. When used,
+this module simply passes all requests back to the disk functions
+(i.e it operates as a passthrough filter). It should be
+useful as a starting point for developing new VFS
+modules.
+
+Please look at skel_opaque.c when you want your module to provide
+final functions, like a database filesystem.
+
+Please look at skel_transport.c when you want your module to provide
+passthrough functions, like audit modules.
+
+Please read the VFS chapter in the HOWTO collection for general help
+on the usage of VFS modules.
+
+Further documentation on writing VFS modules for Samba can be found in
+Samba Developers Guide.
diff --git a/examples/VFS/autogen.sh b/examples/VFS/autogen.sh
new file mode 100755
index 00000000000..fcae16ec5c8
--- /dev/null
+++ b/examples/VFS/autogen.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+# Run this script to build samba from CVS.
+
+## insert all possible names (only works with
+## autoconf 2.x
+#TESTAUTOHEADER="autoheader autoheader-2.53"
+TESTAUTOCONF="autoconf autoconf-2.53"
+
+#AUTOHEADERFOUND="0"
+AUTOCONFFOUND="0"
+
+
+##
+## Look for autoheader
+##
+#for i in $TESTAUTOHEADER; do
+# if which $i > /dev/null 2>&1; then
+# if [ `$i --version | head -n 1 | cut -d. -f 2` -ge 53 ]; then
+# AUTOHEADER=$i
+# AUTOHEADERFOUND="1"
+# break
+# fi
+# fi
+#done
+
+##
+## Look for autoconf
+##
+
+for i in $TESTAUTOCONF; do
+ if which $i > /dev/null 2>&1; then
+ if [ `$i --version | head -n 1 | cut -d. -f 2` -ge 53 ]; then
+ AUTOCONF=$i
+ AUTOCONFFOUND="1"
+ break
+ fi
+ fi
+done
+
+
+##
+## do we have it?
+##
+if [ "$AUTOCONFFOUND" = "0" -o "$AUTOHEADERFOUND" = "0" ]; then
+ echo "$0: need autoconf 2.53 or later to build samba from CVS" >&2
+ exit 1
+fi
+
+
+
+#echo "$0: running $AUTOHEADER"
+#$AUTOHEADER || exit 1
+
+echo "$0: running $AUTOCONF"
+$AUTOCONF || exit 1
+
+echo "Now run ./configure and then make."
+exit 0
+
diff --git a/examples/VFS/configure.in b/examples/VFS/configure.in
new file mode 100644
index 00000000000..fda4cf3a316
--- /dev/null
+++ b/examples/VFS/configure.in
@@ -0,0 +1,350 @@
+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(Makefile.in)
+
+#dnl Uncomment this if you want to use your own define's too
+#AC_CONFIG_HEADER(module_config.h)
+#dnl To make sure that didn't get #define PACKAGE_* in modules_config.h
+#echo "" > confdefs.h
+
+dnl Checks for programs.
+AC_PROG_CC
+AC_PROG_INSTALL
+
+#################################################
+# 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)],
+ libdir="\${prefix}/lib/samba",
+ libdir="\${prefix}/lib")
+
+AC_SUBST(libdir)
+
+SAMBA_SOURCE="../../source"
+####################################################
+# set the location location of the samba source tree
+AC_ARG_WITH(samba-source,
+[ --with-samba-source=DIR Where is the samba source tree (../../source)],
+[ case "$withval" in
+ yes|no)
+ #
+ # Just in case anybody calls it without argument
+ #
+ AC_MSG_WARN([--with-samba-source called without argument - will use default])
+ ;;
+ * )
+ SAMBA_SOURCE="$withval"
+ ;;
+ esac])
+
+AC_SUBST(SAMBA_SOURCE)
+
+dnl Unique-to-Samba variables we'll be playing with.
+AC_SUBST(CC)
+AC_SUBST(SHELL)
+AC_SUBST(LDSHFLAGS)
+AC_SUBST(SONAMEFLAG)
+AC_SUBST(SHLD)
+AC_SUBST(HOST_OS)
+AC_SUBST(PICFLAGS)
+AC_SUBST(PICSUFFIX)
+AC_SUBST(SHLIBEXT)
+AC_SUBST(INSTALLCLIENTCMD_SH)
+AC_SUBST(INSTALLCLIENTCMD_A)
+AC_SUBST(SHLIB_PROGS)
+AC_SUBST(EXTRA_BIN_PROGS)
+AC_SUBST(EXTRA_SBIN_PROGS)
+AC_SUBST(EXTRA_ALL_TARGETS)
+
+AC_ARG_ENABLE(debug,
+[ --enable-debug Turn on compiler debugging information (default=no)],
+ [if eval "test x$enable_debug = xyes"; then
+ CFLAGS="${CFLAGS} -g"
+ fi])
+
+AC_ARG_ENABLE(developer, [ --enable-developer Turn on developer warnings and debugging (default=no)],
+ [if eval "test x$enable_developer = xyes"; then
+ developer=yes
+ CFLAGS="${CFLAGS} -g -Wall -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings -DDEBUG_PASSWORD -DDEVELOPER"
+ fi])
+
+# compile with optimization and without debugging by default, but
+# allow people to set their own preference.
+if test "x$CFLAGS" = x
+then
+ CFLAGS="-O ${CFLAGS}"
+fi
+
+ #################################################
+ # 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
+ CFLAGS="$CFLAGS `$KRB5_CONFIG --cflags | sed s/@INCLUDE_des@//`"
+ CPPFLAGS="$CPPFLAGS `$KRB5_CONFIG --cflags | sed s/@INCLUDE_des@//`"
+ 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)
+ CFLAGS="$CFLAGS -I$withval/include"
+ CPPFLAGS="$CPPFLAGS -I$withval/include"
+ 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
+ CFLAGS="$CFLAGS -I/usr/include/heimdal"
+ CPPFLAGS="$CPPFLAGS -I/usr/include/heimdal"
+ AC_MSG_RESULT(yes)
+ else
+ 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
+ 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)
+
+#dnl Check if we use GNU ld
+#LD=ld
+#AC_PROG_LD_GNU
+
+#dnl look for executable suffix
+#AC_EXEEXT
+
+builddir=`pwd`
+AC_SUBST(builddir)
+
+# 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}"
+PICFLAGS=""
+PICSUFFIX="po"
+SHLIBEXT="so"
+
+if test "$enable_shared" = "yes"; then
+ # this bit needs to be modified for each OS that is suported by
+ # smbwrapper. You need to specify how to created a shared library and
+ # how to compile C code to produce PIC object files
+
+ AC_MSG_CHECKING([ability to build shared libraries])
+
+ # and these are for particular systems
+ case "$host_os" in
+ *linux*)
+ BLDSHARED="true"
+ LDSHFLAGS="-shared"
+ DYNEXP="-Wl,--export-dynamic"
+ PICFLAGS="-fPIC"
+ SONAMEFLAG="-Wl,-soname="
+ ;;
+ *solaris*)
+ BLDSHARED="true"
+ LDSHFLAGS="-G"
+ SONAMEFLAG="-h "
+ if test "${GCC}" = "yes"; then
+ PICFLAGS="-fPIC"
+ if test "${ac_cv_prog_gnu_ld}" = "yes"; then
+ DYNEXP="-Wl,-E"
+ fi
+ else
+ PICFLAGS="-KPIC"
+ ## ${CFLAGS} added for building 64-bit shared
+ ## libs using Sun's Compiler
+ LDSHFLAGS="-G \${CFLAGS}"
+ PICSUFFIX="po.o"
+ fi
+ ;;
+ *sunos*)
+ BLDSHARED="true"
+ LDSHFLAGS="-G"
+ SONAMEFLAG="-Wl,-h,"
+ PICFLAGS="-KPIC" # Is this correct for SunOS
+ ;;
+ *netbsd* | *freebsd*) BLDSHARED="true"
+ LDSHFLAGS="-shared"
+ DYNEXP="-Wl,--export-dynamic"
+ SONAMEFLAG="-Wl,-soname,"
+ PICFLAGS="-fPIC -DPIC"
+ ;;
+ *openbsd*) BLDSHARED="true"
+ LDSHFLAGS="-shared"
+ DYNEXP="-Wl,-Bdynamic"
+ SONAMEFLAG="-Wl,-soname,"
+ PICFLAGS="-fPIC"
+ ;;
+ *irix*)
+ case "$host_os" in
+ *irix6*)
+ ;;
+ esac
+ ATTEMPT_WRAP32_BUILD=yes
+ BLDSHARED="true"
+ LDSHFLAGS="-set_version sgi1.0 -shared"
+ SONAMEFLAG="-soname "
+ SHLD="\${LD}"
+ if test "${GCC}" = "yes"; then
+ PICFLAGS="-fPIC"
+ else
+ PICFLAGS="-KPIC"
+ fi
+ ;;
+ *aix*)
+ BLDSHARED="true"
+ LDSHFLAGS="-Wl,-bexpall,-bM:SRE,-bnoentry,-berok"
+ DYNEXP="-Wl,-brtl,-bexpall"
+ PICFLAGS="-O2"
+ if test "${GCC}" != "yes"; then
+ ## for funky AIX compiler using strncpy()
+ CFLAGS="$CFLAGS -D_LINUX_SOURCE_COMPAT -qmaxmem=32000"
+ fi
+ ;;
+ *hpux*)
+ SHLIBEXT="sl"
+ # Use special PIC flags for the native HP-UX compiler.
+ if test $ac_cv_prog_cc_Ae = yes; then
+ BLDSHARED="true"
+ SHLD="/usr/bin/ld"
+ LDSHFLAGS="-B symbolic -b -z"
+ SONAMEFLAG="+h "
+ PICFLAGS="+z"
+ fi
+ DYNEXP="-Wl,-E"
+ ;;
+ *qnx*)
+ ;;
+ *osf*)
+ BLDSHARED="true"
+ LDSHFLAGS="-shared"
+ SONAMEFLAG="-Wl,-soname,"
+ PICFLAGS="-fPIC"
+ ;;
+ *sco*)
+ ;;
+ *unixware*)
+ BLDSHARED="true"
+ LDSHFLAGS="-shared"
+ SONAMEFLAG="-Wl,-soname,"
+ PICFLAGS="-KPIC"
+ ;;
+ *next2*)
+ ;;
+ *dgux*) AC_CHECK_PROG( ROFF, groff, [groff -etpsR -Tascii -man])
+ ;;
+ *sysv4*)
+ case "$host" in
+ *-univel-*)
+ LDSHFLAGS="-G"
+ DYNEXP="-Bexport"
+ ;;
+ *mips-sni-sysv4*)
+ ;;
+ esac
+ ;;
+
+ *sysv5*)
+ LDSHFLAGS="-G"
+ ;;
+ *vos*)
+ BLDSHARED="false"
+ LDSHFLAGS=""
+ ;;
+ *)
+ ;;
+ 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 $PICFLAGS -c -o shlib.po ${srcdir-.}/tests/shlib.c &&
+ $CC $CPPFLAGS $CFLAGS `eval echo $LDSHFLAGS` -o "shlib.$SHLIBEXT" shlib.po &&
+ ac_cv_shlib_works=yes
+ else
+ $CC $CPPFLAGS $CFLAGS $PICFLAGS -c -o shlib.$PICSUFFIX ${srcdir-.}/tests/shlib.c &&
+ mv shlib.$PICSUFFIX shlib.po &&
+ $CC $CPPFLAGS $CFLAGS `eval echo $LDSHFLAGS` -o "shlib.$SHLIBEXT" shlib.po &&
+ ac_cv_shlib_works=yes
+ fi
+ rm -f "shlib.$SHLIBEXT" shlib.po
+])
+if test $ac_cv_shlib_works = no; then
+ BLDSHARED=false
+fi
+fi
+
+
+
+
+AC_OUTPUT(Makefile)
diff --git a/examples/VFS/install-sh b/examples/VFS/install-sh
new file mode 100644
index 00000000000..58719246f04
--- /dev/null
+++ b/examples/VFS/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/examples/VFS/shadow_copy_test.c b/examples/VFS/shadow_copy_test.c
new file mode 100644
index 00000000000..d2b7206cd35
--- /dev/null
+++ b/examples/VFS/shadow_copy_test.c
@@ -0,0 +1,83 @@
+/*
+ * TEST implementation of an Shadow Copy module
+ *
+ * Copyright (C) Stefan Metzmacher 2003
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_VFS
+
+/* USE THIS MODULE ONLY FOR TESTING!!!! */
+
+/*
+ For this share
+ Z:\
+
+ the ShadowCopies are in this directories
+
+ Z:\@GMT-2003.08.05-12.00.00\
+ Z:\@GMT-2003.08.05-12.01.00\
+ Z:\@GMT-2003.08.05-12.02.00\
+
+ e.g.
+
+ Z:\testfile.txt
+ Z:\@GMT-2003.08.05-12.02.00\testfile.txt
+
+ or:
+
+ Z:\testdir\testfile.txt
+ Z:\@GMT-2003.08.05-12.02.00\testdir\testfile.txt
+
+
+ Note: Files must differ to be displayed via Windows Explorer!
+ Directories are always displayed...
+*/
+
+static int test_get_shadow_copy_data(vfs_handle_struct *handle, files_struct *fsp, SHADOW_COPY_DATA *shadow_copy_data, BOOL labels)
+{
+ uint32 num = 3;
+ uint32 i;
+
+ shadow_copy_data->num_volumes = num;
+
+ if (labels) {
+ shadow_copy_data->labels = (SHADOW_COPY_LABEL *)talloc_zero(shadow_copy_data->mem_ctx,(num)*sizeof(SHADOW_COPY_LABEL));
+ for (i=0;i<num;i++) {
+ snprintf(shadow_copy_data->labels[i], sizeof(SHADOW_COPY_LABEL), "@GMT-2003.08.05-12.%02u.00",i);
+ }
+ } else {
+ shadow_copy_data->labels = NULL;
+ }
+
+ return 0;
+}
+
+/* VFS operations structure */
+
+static vfs_op_tuple shadow_copy_test_ops[] = {
+ {SMB_VFS_OP(test_get_shadow_copy_data), SMB_VFS_OP_GET_SHADOW_COPY_DATA,SMB_VFS_LAYER_OPAQUE},
+
+ {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+NTSTATUS init_module(void)
+{
+ return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "shadow_copy_test", shadow_copy_test_ops);
+}
diff --git a/examples/VFS/skel_opaque.c b/examples/VFS/skel_opaque.c
new file mode 100644
index 00000000000..310d305cee3
--- /dev/null
+++ b/examples/VFS/skel_opaque.c
@@ -0,0 +1,581 @@
+/*
+ * Skeleton VFS module. Implements passthrough operation of all VFS
+ * calls to disk functions.
+ *
+ * Copyright (C) Tim Potter, 1999-2000
+ * Copyright (C) Alexander Bokovoy, 2002
+ * Copyright (C) Stefan (metze) Metzmacher, 2003
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include "includes.h"
+
+/* PLEASE,PLEASE READ THE VFS MODULES CHAPTER OF THE
+ SAMBA DEVELOPERS GUIDE!!!!!!
+ */
+
+/* If you take this file as template for your module
+ * please make sure that you remove all vfswrap_* functions and
+ * implement your own function!!
+ *
+ * for functions you didn't want to provide implement dummy functions
+ * witch return ERROR and errno = ENOSYS; !
+ *
+ * --metze
+ */
+
+static int skel_connect(vfs_handle_struct *handle, connection_struct *conn, const char *service, const char *user)
+{
+ return 0;
+}
+
+static void skel_disconnect(vfs_handle_struct *handle, connection_struct *conn)
+{
+ return;
+}
+
+static SMB_BIG_UINT skel_disk_free(vfs_handle_struct *handle, connection_struct *conn, const char *path,
+ BOOL small_query, SMB_BIG_UINT *bsize,
+ SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ return vfswrap_disk_free(NULL, conn, path, small_query, bsize,
+ dfree, dsize);
+}
+
+static int skel_get_quota(vfs_handle_struct *handle, connection_struct *conn, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dq)
+{
+ return vfswrap_get_quota(NULL, conn, qtype, id, dq);
+}
+
+static int skel_set_quota(vfs_handle_struct *handle, connection_struct *conn, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dq)
+{
+ return vfswrap_set_quota(NULL, conn, qtype, id, dq);
+}
+
+static int skel_get_shadow_copy_data(vfs_handle_struct *handle, files_struct *fsp, SHADOW_COPY_DATA *shadow_copy_data, BOOL labels)
+{
+ return vfswrap_get_shadow_copy_data(NULL, fsp, shadow_copy_data, labels);
+}
+
+static DIR *skel_opendir(vfs_handle_struct *handle, connection_struct *conn, const char *fname)
+{
+ return vfswrap_opendir(NULL, conn, fname);
+}
+
+static struct dirent *skel_readdir(vfs_handle_struct *handle, connection_struct *conn, DIR *dirp)
+{
+ return vfswrap_readdir(NULL, conn, dirp);
+}
+
+static int skel_mkdir(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode)
+{
+ return vfswrap_mkdir(NULL, conn, path, mode);
+}
+
+static int skel_rmdir(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ return vfswrap_rmdir(NULL, conn, path);
+}
+
+static int skel_closedir(vfs_handle_struct *handle, connection_struct *conn, DIR *dir)
+{
+ return vfswrap_closedir(NULL, conn, dir);
+}
+
+static int skel_open(vfs_handle_struct *handle, connection_struct *conn, const char *fname, int flags, mode_t mode)
+{
+ return vfswrap_open(NULL, conn, fname, flags, mode);
+}
+
+static int skel_close(vfs_handle_struct *handle, files_struct *fsp, int fd)
+{
+ return vfswrap_close(NULL, fsp, fd);
+}
+
+static ssize_t skel_read(vfs_handle_struct *handle, files_struct *fsp, int fd, void *data, size_t n)
+{
+ return vfswrap_read(NULL, fsp, fd, data, n);
+}
+
+static ssize_t skel_pread(vfs_handle_struct *handle, struct files_struct *fsp, int fd, void *data, size_t n, SMB_OFF_T offset)
+{
+ return vfswrap_pread(NULL, fsp, fd, data, n, offset);
+}
+
+static ssize_t skel_write(vfs_handle_struct *handle, files_struct *fsp, int fd, const void *data, size_t n)
+{
+ return vfswrap_write(NULL, fsp, fd, data, n);
+}
+
+ssize_t skel_pwrite(vfs_handle_struct *handle, struct files_struct *fsp, int fd, const void *data, size_t n, SMB_OFF_T offset)
+{
+ return vfswrap_pwrite(NULL, fsp, fd, data, n, offset);
+}
+
+static SMB_OFF_T skel_lseek(vfs_handle_struct *handle, files_struct *fsp, int filedes, SMB_OFF_T offset, int whence)
+{
+ return vfswrap_lseek(NULL, fsp, filedes, offset, whence);
+}
+
+static int skel_rename(vfs_handle_struct *handle, connection_struct *conn, const char *old, const char *new)
+{
+ return vfswrap_rename(NULL, conn, old, new);
+}
+
+static int skel_fsync(vfs_handle_struct *handle, files_struct *fsp, int fd)
+{
+ return vfswrap_fsync(NULL, fsp, fd);
+}
+
+static int skel_stat(vfs_handle_struct *handle, connection_struct *conn, const char *fname, SMB_STRUCT_STAT *sbuf)
+{
+ return vfswrap_stat(NULL, conn, fname, sbuf);
+}
+
+static int skel_fstat(vfs_handle_struct *handle, files_struct *fsp, int fd, SMB_STRUCT_STAT *sbuf)
+{
+ return vfswrap_fstat(NULL, fsp, fd, sbuf);
+}
+
+static int skel_lstat(vfs_handle_struct *handle, connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf)
+{
+ return vfswrap_lstat(NULL, conn, path, sbuf);
+}
+
+static int skel_unlink(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ return vfswrap_unlink(NULL, conn, path);
+}
+
+static int skel_chmod(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode)
+{
+ return vfswrap_chmod(NULL, conn, path, mode);
+}
+
+static int skel_fchmod(vfs_handle_struct *handle, files_struct *fsp, int fd, mode_t mode)
+{
+ return vfswrap_fchmod(NULL, fsp, fd, mode);
+}
+
+static int skel_chown(vfs_handle_struct *handle, connection_struct *conn, const char *path, uid_t uid, gid_t gid)
+{
+ return vfswrap_chown(NULL, conn, path, uid, gid);
+}
+
+static int skel_fchown(vfs_handle_struct *handle, files_struct *fsp, int fd, uid_t uid, gid_t gid)
+{
+ return vfswrap_fchown(NULL, fsp, fd, uid, gid);
+}
+
+static int skel_chdir(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ return vfswrap_chdir(NULL, conn, path);
+}
+
+static char *skel_getwd(vfs_handle_struct *handle, connection_struct *conn, char *buf)
+{
+ return vfswrap_getwd(NULL, conn, buf);
+}
+
+static int skel_utime(vfs_handle_struct *handle, connection_struct *conn, const char *path, struct utimbuf *times)
+{
+ return vfswrap_utime(NULL, conn, path, times);
+}
+
+static int skel_ftruncate(vfs_handle_struct *handle, files_struct *fsp, int fd, SMB_OFF_T offset)
+{
+ return vfswrap_ftruncate(NULL, fsp, fd, offset);
+}
+
+static BOOL skel_lock(vfs_handle_struct *handle, files_struct *fsp, int fd, int op, SMB_OFF_T offset, SMB_OFF_T count, int type)
+{
+ return vfswrap_lock(NULL, fsp, fd, op, offset, count, type);
+}
+
+static BOOL skel_symlink(vfs_handle_struct *handle, connection_struct *conn, const char *oldpath, const char *newpath)
+{
+ return vfswrap_symlink(NULL, conn, oldpath, newpath);
+}
+
+static BOOL skel_readlink(vfs_handle_struct *handle, connection_struct *conn, const char *path, char *buf, size_t bufsiz)
+{
+ return vfswrap_readlink(NULL, conn, path, buf, bufsiz);
+}
+
+static int skel_link(vfs_handle_struct *handle, connection_struct *conn, const char *oldpath, const char *newpath)
+{
+ return vfswrap_link(NULL, conn, oldpath, newpath);
+}
+
+static int skel_mknod(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode, SMB_DEV_T dev)
+{
+ return vfswrap_mknod(NULL, conn, path, mode, dev);
+}
+
+static char *skel_realpath(vfs_handle_struct *handle, connection_struct *conn, const char *path, char *resolved_path)
+{
+ return vfswrap_realpath(NULL, conn, path, resolved_path);
+}
+
+static size_t skel_fget_nt_acl(vfs_handle_struct *handle, files_struct *fsp, int fd, uint32 security_info, struct security_descriptor_info **ppdesc)
+{
+ errno = ENOSYS;
+ return 0;
+}
+
+static size_t skel_get_nt_acl(vfs_handle_struct *handle, files_struct *fsp, const char *name, uint32 security_info, struct security_descriptor_info **ppdesc)
+{
+ errno = ENOSYS;
+ return 0;
+}
+
+static BOOL skel_fset_nt_acl(vfs_handle_struct *handle, files_struct *fsp, int fd, uint32 security_info_sent, struct security_descriptor_info *psd)
+{
+ errno = ENOSYS;
+ return False;
+}
+
+static BOOL skel_set_nt_acl(vfs_handle_struct *handle, files_struct *fsp, const char *name, uint32 security_info_sent, struct security_descriptor_info *psd)
+{
+ errno = ENOSYS;
+ return False;
+}
+
+static int skel_chmod_acl(vfs_handle_struct *handle, connection_struct *conn, const char *name, mode_t mode)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int skel_fchmod_acl(vfs_handle_struct *handle, files_struct *fsp, int fd, mode_t mode)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int skel_sys_acl_get_entry(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_T theacl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int skel_sys_acl_get_tag_type(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int skel_sys_acl_get_permset(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static void *skel_sys_acl_get_qualifier(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_ENTRY_T entry_d)
+{
+ errno = ENOSYS;
+ return NULL;
+}
+
+static SMB_ACL_T skel_sys_acl_get_file(vfs_handle_struct *handle, connection_struct *conn, const char *path_p, SMB_ACL_TYPE_T type)
+{
+ errno = ENOSYS;
+ return NULL;
+}
+
+static SMB_ACL_T skel_sys_acl_get_fd(vfs_handle_struct *handle, files_struct *fsp, int fd)
+{
+ errno = ENOSYS;
+ return NULL;
+}
+
+static int skel_sys_acl_clear_perms(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_PERMSET_T permset)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int skel_sys_acl_add_perm(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static char *skel_sys_acl_to_text(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_T theacl, ssize_t *plen)
+{
+ errno = ENOSYS;
+ return NULL;
+}
+
+static SMB_ACL_T skel_sys_acl_init(vfs_handle_struct *handle, connection_struct *conn, int count)
+{
+ errno = ENOSYS;
+ return NULL;
+}
+
+static int skel_sys_acl_create_entry(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int skel_sys_acl_set_tag_type(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int skel_sys_acl_set_qualifier(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_ENTRY_T entry, void *qual)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int skel_sys_acl_set_permset(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int skel_sys_acl_valid(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_T theacl )
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int skel_sys_acl_set_file(vfs_handle_struct *handle, connection_struct *conn, const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int skel_sys_acl_set_fd(vfs_handle_struct *handle, files_struct *fsp, int fd, SMB_ACL_T theacl)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int skel_sys_acl_delete_def_file(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int skel_sys_acl_get_perm(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int skel_sys_acl_free_text(vfs_handle_struct *handle, connection_struct *conn, char *text)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int skel_sys_acl_free_acl(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_T posix_acl)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int skel_sys_acl_free_qualifier(vfs_handle_struct *handle, connection_struct *conn, void *qualifier, SMB_ACL_TAG_T tagtype)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static ssize_t skel_getxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, void *value, size_t size)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static ssize_t skel_lgetxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, void *value, size_t
+size)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static ssize_t skel_fgetxattr(vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name, void *value, size_t size)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static ssize_t skel_listxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, char *list, size_t size)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static ssize_t skel_llistxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, char *list, size_t size)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static ssize_t skel_flistxattr(vfs_handle_struct *handle, struct files_struct *fsp,int fd, char *list, size_t size)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int skel_removexattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int skel_lremovexattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int skel_fremovexattr(vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int skel_setxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, const void *value, size_t size, int flags)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int skel_lsetxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, const void *value, size_t size, int flags)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int skel_fsetxattr(vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name, const void *value, size_t size, int flags)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+/* VFS operations structure */
+
+static vfs_op_tuple skel_op_tuples[] = {
+
+ /* Disk operations */
+
+ {SMB_VFS_OP(skel_connect), SMB_VFS_OP_CONNECT, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_disconnect), SMB_VFS_OP_DISCONNECT, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_disk_free), SMB_VFS_OP_DISK_FREE, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_get_quota), SMB_VFS_OP_GET_QUOTA, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_set_quota), SMB_VFS_OP_SET_QUOTA, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_get_shadow_copy_data), SMB_VFS_OP_GET_SHADOW_COPY_DATA,SMB_VFS_LAYER_OPAQUE},
+
+ /* Directory operations */
+
+ {SMB_VFS_OP(skel_opendir), SMB_VFS_OP_OPENDIR, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_readdir), SMB_VFS_OP_READDIR, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_mkdir), SMB_VFS_OP_MKDIR, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_rmdir), SMB_VFS_OP_RMDIR, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_closedir), SMB_VFS_OP_CLOSEDIR, SMB_VFS_LAYER_OPAQUE},
+
+ /* File operations */
+
+ {SMB_VFS_OP(skel_open), SMB_VFS_OP_OPEN, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_close), SMB_VFS_OP_CLOSE, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_read), SMB_VFS_OP_READ, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_pread), SMB_VFS_OP_PREAD, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_write), SMB_VFS_OP_WRITE, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_pwrite), SMB_VFS_OP_PWRITE, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_lseek), SMB_VFS_OP_LSEEK, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_rename), SMB_VFS_OP_RENAME, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_fsync), SMB_VFS_OP_FSYNC, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_stat), SMB_VFS_OP_STAT, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_fstat), SMB_VFS_OP_FSTAT, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_lstat), SMB_VFS_OP_LSTAT, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_unlink), SMB_VFS_OP_UNLINK, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_chmod), SMB_VFS_OP_CHMOD, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_fchmod), SMB_VFS_OP_FCHMOD, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_chown), SMB_VFS_OP_CHOWN, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_fchown), SMB_VFS_OP_FCHOWN, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_chdir), SMB_VFS_OP_CHDIR, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_getwd), SMB_VFS_OP_GETWD, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_utime), SMB_VFS_OP_UTIME, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_ftruncate), SMB_VFS_OP_FTRUNCATE, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_lock), SMB_VFS_OP_LOCK, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_symlink), SMB_VFS_OP_SYMLINK, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_readlink), SMB_VFS_OP_READLINK, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_link), SMB_VFS_OP_LINK, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_mknod), SMB_VFS_OP_MKNOD, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_realpath), SMB_VFS_OP_REALPATH, SMB_VFS_LAYER_OPAQUE},
+
+ /* NT File ACL operations */
+
+ {SMB_VFS_OP(skel_fget_nt_acl), SMB_VFS_OP_FGET_NT_ACL, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_get_nt_acl), SMB_VFS_OP_GET_NT_ACL, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_fset_nt_acl), SMB_VFS_OP_FSET_NT_ACL, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_set_nt_acl), SMB_VFS_OP_SET_NT_ACL, SMB_VFS_LAYER_OPAQUE},
+
+ /* POSIX ACL operations */
+
+ {SMB_VFS_OP(skel_chmod_acl), SMB_VFS_OP_CHMOD_ACL, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_fchmod_acl), SMB_VFS_OP_FCHMOD_ACL, SMB_VFS_LAYER_OPAQUE},
+
+ {SMB_VFS_OP(skel_sys_acl_get_entry), SMB_VFS_OP_SYS_ACL_GET_ENTRY, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_sys_acl_get_tag_type), SMB_VFS_OP_SYS_ACL_GET_TAG_TYPE, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_sys_acl_get_permset), SMB_VFS_OP_SYS_ACL_GET_PERMSET, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_sys_acl_get_qualifier), SMB_VFS_OP_SYS_ACL_GET_QUALIFIER, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_sys_acl_get_file), SMB_VFS_OP_SYS_ACL_GET_FILE, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_sys_acl_get_fd), SMB_VFS_OP_SYS_ACL_GET_FD, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_sys_acl_clear_perms), SMB_VFS_OP_SYS_ACL_CLEAR_PERMS, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_sys_acl_add_perm), SMB_VFS_OP_SYS_ACL_ADD_PERM, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_sys_acl_to_text), SMB_VFS_OP_SYS_ACL_TO_TEXT, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_sys_acl_init), SMB_VFS_OP_SYS_ACL_INIT, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_sys_acl_create_entry), SMB_VFS_OP_SYS_ACL_CREATE_ENTRY, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_sys_acl_set_tag_type), SMB_VFS_OP_SYS_ACL_SET_TAG_TYPE, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_sys_acl_set_qualifier), SMB_VFS_OP_SYS_ACL_SET_QUALIFIER, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_sys_acl_set_permset), SMB_VFS_OP_SYS_ACL_SET_PERMSET, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_sys_acl_valid), SMB_VFS_OP_SYS_ACL_VALID, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_sys_acl_set_file), SMB_VFS_OP_SYS_ACL_SET_FILE, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_sys_acl_set_fd), SMB_VFS_OP_SYS_ACL_SET_FD, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_sys_acl_delete_def_file), SMB_VFS_OP_SYS_ACL_DELETE_DEF_FILE, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_sys_acl_get_perm), SMB_VFS_OP_SYS_ACL_GET_PERM, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_sys_acl_free_text), SMB_VFS_OP_SYS_ACL_FREE_TEXT, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_sys_acl_free_acl), SMB_VFS_OP_SYS_ACL_FREE_ACL, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_sys_acl_free_qualifier), SMB_VFS_OP_SYS_ACL_FREE_QUALIFIER, SMB_VFS_LAYER_OPAQUE},
+
+ /* EA operations. */
+ {SMB_VFS_OP(skel_getxattr), SMB_VFS_OP_GETXATTR, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_lgetxattr), SMB_VFS_OP_LGETXATTR, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_fgetxattr), SMB_VFS_OP_FGETXATTR, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_listxattr), SMB_VFS_OP_LISTXATTR, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_llistxattr), SMB_VFS_OP_LLISTXATTR, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_flistxattr), SMB_VFS_OP_FLISTXATTR, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_removexattr), SMB_VFS_OP_REMOVEXATTR, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_lremovexattr), SMB_VFS_OP_LREMOVEXATTR, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_fremovexattr), SMB_VFS_OP_FREMOVEXATTR, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_setxattr), SMB_VFS_OP_SETXATTR, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_lsetxattr), SMB_VFS_OP_LSETXATTR, SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(skel_fsetxattr), SMB_VFS_OP_FSETXATTR, SMB_VFS_LAYER_OPAQUE},
+
+ {NULL, SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+NTSTATUS init_module(void)
+{
+ return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "skel_opaque", skel_op_tuples);
+}
diff --git a/examples/VFS/skel_transparent.c b/examples/VFS/skel_transparent.c
new file mode 100644
index 00000000000..448390e72f4
--- /dev/null
+++ b/examples/VFS/skel_transparent.c
@@ -0,0 +1,548 @@
+/*
+ * Skeleton VFS module. Implements passthrough operation of all VFS
+ * calls to disk functions.
+ *
+ * Copyright (C) Tim Potter, 1999-2000
+ * Copyright (C) Alexander Bokovoy, 2002
+ * Copyright (C) Stefan (metze) Metzmacher, 2003
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include "includes.h"
+
+/* PLEASE,PLEASE READ THE VFS MODULES CHAPTER OF THE
+ SAMBA DEVELOPERS GUIDE!!!!!!
+ */
+
+/* If you take this file as template for your module
+ * please make sure that you remove all functions you didn't
+ * want to implement!!
+ *
+ * This passthrough operations are useless in reall vfs modules!
+ *
+ * --metze
+ */
+
+static int skel_connect(vfs_handle_struct *handle, connection_struct *conn, const char *service, const char *user)
+{
+ return SMB_VFS_NEXT_CONNECT(handle, conn, service, user);
+}
+
+static void skel_disconnect(vfs_handle_struct *handle, connection_struct *conn)
+{
+ SMB_VFS_NEXT_DISCONNECT(handle, conn);
+}
+
+static SMB_BIG_UINT skel_disk_free(vfs_handle_struct *handle, connection_struct *conn, const char *path,
+ BOOL small_query, SMB_BIG_UINT *bsize,
+ SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ return SMB_VFS_NEXT_DISK_FREE(handle, conn, path, small_query, bsize,
+ dfree, dsize);
+}
+
+static int skel_get_quota(vfs_handle_struct *handle, connection_struct *conn, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dq)
+{
+ return SMB_VFS_NEXT_GET_QUOTA(handle, conn, qtype, id, dq);
+}
+
+static int skel_set_quota(vfs_handle_struct *handle, connection_struct *conn, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dq)
+{
+ return SMB_VFS_NEXT_SET_QUOTA(handle, conn, qtype, id, dq);
+}
+
+static int skel_get_shadow_copy_data(vfs_handle_struct *handle, files_struct *fsp, SHADOW_COPY_DATA *shadow_copy_data, BOOL labels)
+{
+ return SMB_VFS_NEXT_GET_SHADOW_COPY_DATA(handle, fsp, shadow_copy_data, labels);
+}
+
+static DIR *skel_opendir(vfs_handle_struct *handle, connection_struct *conn, const char *fname)
+{
+ return SMB_VFS_NEXT_OPENDIR(handle, conn, fname);
+}
+
+static struct dirent *skel_readdir(vfs_handle_struct *handle, connection_struct *conn, DIR *dirp)
+{
+ return SMB_VFS_NEXT_READDIR(handle, conn, dirp);
+}
+
+static int skel_mkdir(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode)
+{
+ return SMB_VFS_NEXT_MKDIR(handle, conn, path, mode);
+}
+
+static int skel_rmdir(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ return SMB_VFS_NEXT_RMDIR(handle, conn, path);
+}
+
+static int skel_closedir(vfs_handle_struct *handle, connection_struct *conn, DIR *dir)
+{
+ return SMB_VFS_NEXT_CLOSEDIR(handle, conn, dir);
+}
+
+static int skel_open(vfs_handle_struct *handle, connection_struct *conn, const char *fname, int flags, mode_t mode)
+{
+ return SMB_VFS_NEXT_OPEN(handle, conn, fname, flags, mode);
+}
+
+static int skel_close(vfs_handle_struct *handle, files_struct *fsp, int fd)
+{
+ return SMB_VFS_NEXT_CLOSE(handle, fsp, fd);
+}
+
+static ssize_t skel_read(vfs_handle_struct *handle, files_struct *fsp, int fd, void *data, size_t n)
+{
+ return SMB_VFS_NEXT_READ(handle, fsp, fd, data, n);
+}
+
+static ssize_t skel_pread(vfs_handle_struct *handle, files_struct *fsp, int fd, void *data, size_t n, SMB_OFF_T offset)
+{
+ return SMB_VFS_NEXT_PREAD(handle, fsp, fd, data, n, offset);
+}
+
+static ssize_t skel_write(vfs_handle_struct *handle, files_struct *fsp, int fd, const void *data, size_t n)
+{
+ return SMB_VFS_NEXT_WRITE(handle, fsp, fd, data, n);
+}
+
+static ssize_t skel_pwrite(vfs_handle_struct *handle, files_struct *fsp, int fd, const void *data, size_t n, SMB_OFF_T offset)
+{
+ return SMB_VFS_NEXT_PWRITE(handle, fsp, fd, data, n, offset);
+}
+
+static SMB_OFF_T skel_lseek(vfs_handle_struct *handle, files_struct *fsp, int filedes, SMB_OFF_T offset, int whence)
+{
+ return SMB_VFS_NEXT_LSEEK(handle, fsp, filedes, offset, whence);
+}
+
+static int skel_rename(vfs_handle_struct *handle, connection_struct *conn, const char *old, const char *new)
+{
+ return SMB_VFS_NEXT_RENAME(handle, conn, old, new);
+}
+
+static int skel_fsync(vfs_handle_struct *handle, files_struct *fsp, int fd)
+{
+ return SMB_VFS_NEXT_FSYNC(handle, fsp, fd);
+}
+
+static int skel_stat(vfs_handle_struct *handle, connection_struct *conn, const char *fname, SMB_STRUCT_STAT *sbuf)
+{
+ return SMB_VFS_NEXT_STAT(handle, conn, fname, sbuf);
+}
+
+static int skel_fstat(vfs_handle_struct *handle, files_struct *fsp, int fd, SMB_STRUCT_STAT *sbuf)
+{
+ return SMB_VFS_NEXT_FSTAT(handle, fsp, fd, sbuf);
+}
+
+static int skel_lstat(vfs_handle_struct *handle, connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf)
+{
+ return SMB_VFS_NEXT_LSTAT(handle, conn, path, sbuf);
+}
+
+static int skel_unlink(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ return SMB_VFS_NEXT_UNLINK(handle, conn, path);
+}
+
+static int skel_chmod(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode)
+{
+ return SMB_VFS_NEXT_CHMOD(handle, conn, path, mode);
+}
+
+static int skel_fchmod(vfs_handle_struct *handle, files_struct *fsp, int fd, mode_t mode)
+{
+ return SMB_VFS_NEXT_FCHMOD(handle, fsp, fd, mode);
+}
+
+static int skel_chown(vfs_handle_struct *handle, connection_struct *conn, const char *path, uid_t uid, gid_t gid)
+{
+ return SMB_VFS_NEXT_CHOWN(handle, conn, path, uid, gid);
+}
+
+static int skel_fchown(vfs_handle_struct *handle, files_struct *fsp, int fd, uid_t uid, gid_t gid)
+{
+ return SMB_VFS_NEXT_FCHOWN(handle, fsp, fd, uid, gid);
+}
+
+static int skel_chdir(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ return SMB_VFS_NEXT_CHDIR(handle, conn, path);
+}
+
+static char *skel_getwd(vfs_handle_struct *handle, connection_struct *conn, char *buf)
+{
+ return SMB_VFS_NEXT_GETWD(handle, conn, buf);
+}
+
+static int skel_utime(vfs_handle_struct *handle, connection_struct *conn, const char *path, struct utimbuf *times)
+{
+ return SMB_VFS_NEXT_UTIME(handle, conn, path, times);
+}
+
+static int skel_ftruncate(vfs_handle_struct *handle, files_struct *fsp, int fd, SMB_OFF_T offset)
+{
+ return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, fd, offset);
+}
+
+static BOOL skel_lock(vfs_handle_struct *handle, files_struct *fsp, int fd, int op, SMB_OFF_T offset, SMB_OFF_T count, int type)
+{
+ return SMB_VFS_NEXT_LOCK(handle, fsp, fd, op, offset, count, type);
+}
+
+static BOOL skel_symlink(vfs_handle_struct *handle, connection_struct *conn, const char *oldpath, const char *newpath)
+{
+ return SMB_VFS_NEXT_SYMLINK(handle, conn, oldpath, newpath);
+}
+
+static BOOL skel_readlink(vfs_handle_struct *handle, connection_struct *conn, const char *path, char *buf, size_t bufsiz)
+{
+ return SMB_VFS_NEXT_READLINK(handle, conn, path, buf, bufsiz);
+}
+
+static int skel_link(vfs_handle_struct *handle, connection_struct *conn, const char *oldpath, const char *newpath)
+{
+ return SMB_VFS_NEXT_LINK(handle, conn, oldpath, newpath);
+}
+
+static int skel_mknod(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode, SMB_DEV_T dev)
+{
+ return SMB_VFS_NEXT_MKNOD(handle, conn, path, mode, dev);
+}
+
+static char *skel_realpath(vfs_handle_struct *handle, connection_struct *conn, const char *path, char *resolved_path)
+{
+ return SMB_VFS_NEXT_REALPATH(handle, conn, path, resolved_path);
+}
+
+static size_t skel_fget_nt_acl(vfs_handle_struct *handle, files_struct *fsp, int fd, uint32 security_info, struct security_descriptor_info **ppdesc)
+{
+ return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, fd, security_info, ppdesc);
+}
+
+static size_t skel_get_nt_acl(vfs_handle_struct *handle, files_struct *fsp, const char *name, uint32 security_info, struct security_descriptor_info **ppdesc)
+{
+ return SMB_VFS_NEXT_GET_NT_ACL(handle, fsp, name, security_info, ppdesc);
+}
+
+static BOOL skel_fset_nt_acl(vfs_handle_struct *handle, files_struct *fsp, int fd, uint32 security_info_sent, struct security_descriptor_info *psd)
+{
+ return SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, fd, security_info_sent, psd);
+}
+
+static BOOL skel_set_nt_acl(vfs_handle_struct *handle, files_struct *fsp, const char *name, uint32 security_info_sent, struct security_descriptor_info *psd)
+{
+ return SMB_VFS_NEXT_SET_NT_ACL(handle, fsp, name, security_info_sent, psd);
+}
+
+static int skel_chmod_acl(vfs_handle_struct *handle, connection_struct *conn, const char *name, mode_t mode)
+{
+ /* If the underlying VFS doesn't have ACL support... */
+ if (!handle->vfs_next.ops.chmod_acl) {
+ errno = ENOSYS;
+ return -1;
+ }
+ return SMB_VFS_NEXT_CHMOD_ACL(handle, conn, name, mode);
+}
+
+static int skel_fchmod_acl(vfs_handle_struct *handle, files_struct *fsp, int fd, mode_t mode)
+{
+ /* If the underlying VFS doesn't have ACL support... */
+ if (!handle->vfs_next.ops.fchmod_acl) {
+ errno = ENOSYS;
+ return -1;
+ }
+ return SMB_VFS_NEXT_FCHMOD_ACL(handle, fsp, fd, mode);
+}
+
+static int skel_sys_acl_get_entry(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_T theacl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ return SMB_VFS_NEXT_SYS_ACL_GET_ENTRY(handle, conn, theacl, entry_id, entry_p);
+}
+
+static int skel_sys_acl_get_tag_type(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+ return SMB_VFS_NEXT_SYS_ACL_GET_TAG_TYPE(handle, conn, entry_d, tag_type_p);
+}
+
+static int skel_sys_acl_get_permset(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+ return SMB_VFS_NEXT_SYS_ACL_GET_PERMSET(handle, conn, entry_d, permset_p);
+}
+
+static void *skel_sys_acl_get_qualifier(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_ENTRY_T entry_d)
+{
+ return SMB_VFS_NEXT_SYS_ACL_GET_QUALIFIER(handle, conn, entry_d);
+}
+
+static SMB_ACL_T skel_sys_acl_get_file(vfs_handle_struct *handle, connection_struct *conn, const char *path_p, SMB_ACL_TYPE_T type)
+{
+ return SMB_VFS_NEXT_SYS_ACL_GET_FILE(handle, conn, path_p, type);
+}
+
+static SMB_ACL_T skel_sys_acl_get_fd(vfs_handle_struct *handle, files_struct *fsp, int fd)
+{
+ return SMB_VFS_NEXT_SYS_ACL_GET_FD(handle, fsp, fd);
+}
+
+static int skel_sys_acl_clear_perms(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_PERMSET_T permset)
+{
+ return SMB_VFS_NEXT_SYS_ACL_CLEAR_PERMS(handle, conn, permset);
+}
+
+static int skel_sys_acl_add_perm(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+ return SMB_VFS_NEXT_SYS_ACL_ADD_PERM(handle, conn, permset, perm);
+}
+
+static char *skel_sys_acl_to_text(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_T theacl, ssize_t *plen)
+{
+ return SMB_VFS_NEXT_SYS_ACL_TO_TEXT(handle, conn, theacl, plen);
+}
+
+static SMB_ACL_T skel_sys_acl_init(vfs_handle_struct *handle, connection_struct *conn, int count)
+{
+ return SMB_VFS_NEXT_SYS_ACL_INIT(handle, conn, count);
+}
+
+static int skel_sys_acl_create_entry(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+ return SMB_VFS_NEXT_SYS_ACL_CREATE_ENTRY(handle, conn, pacl, pentry);
+}
+
+static int skel_sys_acl_set_tag_type(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype)
+{
+ return SMB_VFS_NEXT_SYS_ACL_SET_TAG_TYPE(handle, conn, entry, tagtype);
+}
+
+static int skel_sys_acl_set_qualifier(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_ENTRY_T entry, void *qual)
+{
+ return SMB_VFS_NEXT_SYS_ACL_SET_QUALIFIER(handle, conn, entry, qual);
+}
+
+static int skel_sys_acl_set_permset(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset)
+{
+ return SMB_VFS_NEXT_SYS_ACL_SET_PERMSET(handle, conn, entry, permset);
+}
+
+static int skel_sys_acl_valid(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_T theacl )
+{
+ return SMB_VFS_NEXT_SYS_ACL_VALID(handle, conn, theacl);
+}
+
+static int skel_sys_acl_set_file(vfs_handle_struct *handle, connection_struct *conn, const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+ return SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, conn, name, acltype, theacl);
+}
+
+static int skel_sys_acl_set_fd(vfs_handle_struct *handle, files_struct *fsp, int fd, SMB_ACL_T theacl)
+{
+ return SMB_VFS_NEXT_SYS_ACL_SET_FD(handle, fsp, fd, theacl);
+}
+
+static int skel_sys_acl_delete_def_file(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ return SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(handle, conn, path);
+}
+
+static int skel_sys_acl_get_perm(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+ return SMB_VFS_NEXT_SYS_ACL_GET_PERM(handle, conn, permset, perm);
+}
+
+static int skel_sys_acl_free_text(vfs_handle_struct *handle, connection_struct *conn, char *text)
+{
+ return SMB_VFS_NEXT_SYS_ACL_FREE_TEXT(handle, conn, text);
+}
+
+static int skel_sys_acl_free_acl(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_T posix_acl)
+{
+ return SMB_VFS_NEXT_SYS_ACL_FREE_ACL(handle, conn, posix_acl);
+}
+
+static int skel_sys_acl_free_qualifier(vfs_handle_struct *handle, connection_struct *conn, void *qualifier, SMB_ACL_TAG_T tagtype)
+{
+ return SMB_VFS_NEXT_SYS_ACL_FREE_QUALIFIER(handle, conn, qualifier, tagtype);
+}
+
+static ssize_t skel_getxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, void *value, size_t size)
+{
+ return SMB_VFS_NEXT_GETXATTR(handle, conn, path, name, value, size);
+}
+
+static ssize_t skel_lgetxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, void *value, size_t
+size)
+{
+ return SMB_VFS_NEXT_LGETXATTR(handle, conn, path, name, value, size);
+}
+
+static ssize_t skel_fgetxattr(vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name, void *value, size_t size)
+{
+ return SMB_VFS_NEXT_FGETXATTR(handle, fsp, fd, name, value, size);
+}
+
+static ssize_t skel_listxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, char *list, size_t size)
+{
+ return SMB_VFS_NEXT_LISTXATTR(handle, conn, path, list, size);
+}
+
+static ssize_t skel_llistxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, char *list, size_t size)
+{
+ return SMB_VFS_NEXT_LLISTXATTR(handle, conn, path, list, size);
+}
+
+static ssize_t skel_flistxattr(vfs_handle_struct *handle, struct files_struct *fsp,int fd, char *list, size_t size)
+{
+ return SMB_VFS_NEXT_FLISTXATTR(handle, fsp, fd, list, size);
+}
+
+static int skel_removexattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name)
+{
+ return SMB_VFS_NEXT_REMOVEXATTR(handle, conn, path, name);
+}
+
+static int skel_lremovexattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name)
+{
+ return SMB_VFS_NEXT_LREMOVEXATTR(handle, conn, path, name);
+}
+
+static int skel_fremovexattr(vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name)
+{
+ return SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp, fd, name);
+}
+
+static int skel_setxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, const void *value, size_t size, int flags)
+{
+ return SMB_VFS_NEXT_SETXATTR(handle, conn, path, name, value, size, flags);
+}
+
+static int skel_lsetxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, const void *value, size_t size, int flags)
+{
+ return SMB_VFS_NEXT_LSETXATTR(handle, conn, path, name, value, size, flags);
+}
+
+static int skel_fsetxattr(vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name, const void *value, size_t size, int flags)
+{
+ return SMB_VFS_NEXT_FSETXATTR(handle, fsp, fd, name, value, size, flags);
+}
+
+/* VFS operations structure */
+
+static vfs_op_tuple skel_op_tuples[] = {
+
+ /* Disk operations */
+
+ {SMB_VFS_OP(skel_connect), SMB_VFS_OP_CONNECT, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_disconnect), SMB_VFS_OP_DISCONNECT, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_disk_free), SMB_VFS_OP_DISK_FREE, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_get_quota), SMB_VFS_OP_GET_QUOTA, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_set_quota), SMB_VFS_OP_SET_QUOTA, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_get_shadow_copy_data), SMB_VFS_OP_GET_SHADOW_COPY_DATA,SMB_VFS_LAYER_TRANSPARENT},
+
+ /* Directory operations */
+
+ {SMB_VFS_OP(skel_opendir), SMB_VFS_OP_OPENDIR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_readdir), SMB_VFS_OP_READDIR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_mkdir), SMB_VFS_OP_MKDIR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_rmdir), SMB_VFS_OP_RMDIR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_closedir), SMB_VFS_OP_CLOSEDIR, SMB_VFS_LAYER_TRANSPARENT},
+
+ /* File operations */
+
+ {SMB_VFS_OP(skel_open), SMB_VFS_OP_OPEN, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_close), SMB_VFS_OP_CLOSE, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_read), SMB_VFS_OP_READ, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_write), SMB_VFS_OP_WRITE, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_lseek), SMB_VFS_OP_LSEEK, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_rename), SMB_VFS_OP_RENAME, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_fsync), SMB_VFS_OP_FSYNC, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_stat), SMB_VFS_OP_STAT, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_fstat), SMB_VFS_OP_FSTAT, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_lstat), SMB_VFS_OP_LSTAT, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_unlink), SMB_VFS_OP_UNLINK, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_chmod), SMB_VFS_OP_CHMOD, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_fchmod), SMB_VFS_OP_FCHMOD, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_chown), SMB_VFS_OP_CHOWN, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_fchown), SMB_VFS_OP_FCHOWN, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_chdir), SMB_VFS_OP_CHDIR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_getwd), SMB_VFS_OP_GETWD, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_utime), SMB_VFS_OP_UTIME, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_ftruncate), SMB_VFS_OP_FTRUNCATE, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_lock), SMB_VFS_OP_LOCK, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_symlink), SMB_VFS_OP_SYMLINK, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_readlink), SMB_VFS_OP_READLINK, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_link), SMB_VFS_OP_LINK, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_mknod), SMB_VFS_OP_MKNOD, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_realpath), SMB_VFS_OP_REALPATH, SMB_VFS_LAYER_TRANSPARENT},
+
+ /* NT File ACL operations */
+
+ {SMB_VFS_OP(skel_fget_nt_acl), SMB_VFS_OP_FGET_NT_ACL, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_get_nt_acl), SMB_VFS_OP_GET_NT_ACL, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_fset_nt_acl), SMB_VFS_OP_FSET_NT_ACL, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_set_nt_acl), SMB_VFS_OP_SET_NT_ACL, SMB_VFS_LAYER_TRANSPARENT},
+
+ /* POSIX ACL operations */
+
+ {SMB_VFS_OP(skel_chmod_acl), SMB_VFS_OP_CHMOD_ACL, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_fchmod_acl), SMB_VFS_OP_FCHMOD_ACL, SMB_VFS_LAYER_TRANSPARENT},
+
+ {SMB_VFS_OP(skel_sys_acl_get_entry), SMB_VFS_OP_SYS_ACL_GET_ENTRY, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_sys_acl_get_tag_type), SMB_VFS_OP_SYS_ACL_GET_TAG_TYPE, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_sys_acl_get_permset), SMB_VFS_OP_SYS_ACL_GET_PERMSET, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_sys_acl_get_qualifier), SMB_VFS_OP_SYS_ACL_GET_QUALIFIER, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_sys_acl_get_file), SMB_VFS_OP_SYS_ACL_GET_FILE, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_sys_acl_get_fd), SMB_VFS_OP_SYS_ACL_GET_FD, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_sys_acl_clear_perms), SMB_VFS_OP_SYS_ACL_CLEAR_PERMS, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_sys_acl_add_perm), SMB_VFS_OP_SYS_ACL_ADD_PERM, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_sys_acl_to_text), SMB_VFS_OP_SYS_ACL_TO_TEXT, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_sys_acl_init), SMB_VFS_OP_SYS_ACL_INIT, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_sys_acl_create_entry), SMB_VFS_OP_SYS_ACL_CREATE_ENTRY, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_sys_acl_set_tag_type), SMB_VFS_OP_SYS_ACL_SET_TAG_TYPE, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_sys_acl_set_qualifier), SMB_VFS_OP_SYS_ACL_SET_QUALIFIER, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_sys_acl_set_permset), SMB_VFS_OP_SYS_ACL_SET_PERMSET, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_sys_acl_valid), SMB_VFS_OP_SYS_ACL_VALID, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_sys_acl_set_file), SMB_VFS_OP_SYS_ACL_SET_FILE, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_sys_acl_set_fd), SMB_VFS_OP_SYS_ACL_SET_FD, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_sys_acl_delete_def_file), SMB_VFS_OP_SYS_ACL_DELETE_DEF_FILE, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_sys_acl_get_perm), SMB_VFS_OP_SYS_ACL_GET_PERM, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_sys_acl_free_text), SMB_VFS_OP_SYS_ACL_FREE_TEXT, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_sys_acl_free_acl), SMB_VFS_OP_SYS_ACL_FREE_ACL, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_sys_acl_free_qualifier), SMB_VFS_OP_SYS_ACL_FREE_QUALIFIER, SMB_VFS_LAYER_TRANSPARENT},
+
+ /* EA operations. */
+ {SMB_VFS_OP(skel_getxattr), SMB_VFS_OP_GETXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_lgetxattr), SMB_VFS_OP_LGETXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_fgetxattr), SMB_VFS_OP_FGETXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_listxattr), SMB_VFS_OP_LISTXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_llistxattr), SMB_VFS_OP_LLISTXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_flistxattr), SMB_VFS_OP_FLISTXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_removexattr), SMB_VFS_OP_REMOVEXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_lremovexattr), SMB_VFS_OP_LREMOVEXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_fremovexattr), SMB_VFS_OP_FREMOVEXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_setxattr), SMB_VFS_OP_SETXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_lsetxattr), SMB_VFS_OP_LSETXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(skel_fsetxattr), SMB_VFS_OP_FSETXATTR, SMB_VFS_LAYER_TRANSPARENT},
+
+ {NULL, SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+NTSTATUS init_module(void)
+{
+ return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "skel_transparent", skel_op_tuples);
+}
diff --git a/examples/appliance/Makefile b/examples/appliance/Makefile
new file mode 100644
index 00000000000..c49451add86
--- /dev/null
+++ b/examples/appliance/Makefile
@@ -0,0 +1,68 @@
+PREFIX=/usr/local/samba
+CONFIGOPTS=--with-pam --prefix=$(PREFIX) --with-smbmount
+
+
+all: headb tngb
+
+config:
+ (cd head/source; CFLAGS="-Wall -O2 -g" ./configure $(CONFIGOPTS))
+ (cd tng; CFLAGS="-Wall -O2 -g" ./configure $(CONFIGOPTS) --enable-shared=no)
+
+headb:
+ (cd head/source; make)
+
+tngb:
+ (cd tng; make bin/samedit bin/winbindd nsswitch)
+
+clean:
+ (cd head/source; make clean)
+ (cd tng; make clean)
+
+proto:
+ (cd head/source; make proto)
+ (cd tng; make proto)
+
+distclean:
+ (cd head/source; make clean; rm -f config.cache; rm -f Makefile)
+ (cd tng; make clean; rm -f config.cache; rm -f Makefile)
+
+install: installhead installtng
+
+installbin: installheadbin installtng
+
+installhead:
+ (cd head/source; make install)
+
+installheadbin:
+ (cd head/source; make installbin)
+
+installtng: tngb
+ (cd tng; \
+ rm -f $(PREFIX)/bin/samedit $(PREFIX)/bin/winbindd; \
+ cp bin/samedit bin/winbindd $(PREFIX)/bin; \
+ rm -f /lib/libnss_winbind.so.2 /lib/security/pam_winbind.so; \
+ cp nsswitch/libnss_winbind.so /lib/libnss_winbind.so.2; \
+ cp nsswitch/pam_winbind.so /lib/security/)
+
+stop:
+ -killall winbindd smbd nmbd
+
+start:
+ $(PREFIX)/bin/smbd
+ $(PREFIX)/bin/nmbd
+ $(PREFIX)/bin/winbindd
+
+restart: stop start
+
+updatehead:
+ (cd head; cvs -z9 update -d)
+
+updatetng:
+ (cd tng; cvs -z9 update -d)
+
+update: updatehead updatetng
+
+checkout:
+ -mkdir head tng
+ (cd head ; cvs -z9 co samba; mv samba head)
+ (cd head ; cvs -z9 co -r SAMBA_TNG samba/source; mv source tng)
diff --git a/examples/appliance/README b/examples/appliance/README
new file mode 100644
index 00000000000..3c9028fd89a
--- /dev/null
+++ b/examples/appliance/README
@@ -0,0 +1,52 @@
+This directory provides build tools for building a Samba based domain
+appliance.
+
+A appliance is a box that gets its username and group database from a
+domain controller, and does its authentication via a domain
+controller. Right now this is only supported by combining two CVS
+branches of Samba, which is what the files in this directory do.
+
+SETUP
+-----
+
+To setup an appliance do the following:
+
+1) build and install Samba using the .spec file or Makefile in this
+ directory.
+
+2) setup winbindd by following the directions in the winbindd man
+ page.
+
+3) test winbindd, validating that domain users and groups are visible
+ and that domain authentication works, both using unix tools and
+ smbclient.
+
+for appliance printing support also do this:
+
+4) create a print$ share with write permission for print
+ administrators.
+
+4) create the "nt printer driver" directory (by default
+ /usr/local/samba/lib/ntprinters) and make it writable by print
+ adminisrators.
+
+5) populate your /etc/printcap printer database
+
+6) add printers using the NT "add printer wizard" in the Printers
+ network folder.
+
+
+PACKAGING
+---------
+
+- Checkout the Samba CVS head branch into a directory call
+ samba-appliance-0.2/head
+
+- Checkout the source subdirectory of the Samba CVS SAMBA_TNG branch
+ into a directory call samba-appliance-0.2/tng
+
+- Copy Makefile and smb.conf-appliance to samba-appliance-0.2/
+
+- run build.sh
+
+That should build source and binary RPMs in /usr/src/redhat/{RPMS,SRPMS}
diff --git a/examples/appliance/appliance.spec b/examples/appliance/appliance.spec
new file mode 100644
index 00000000000..6fb631b4e2f
--- /dev/null
+++ b/examples/appliance/appliance.spec
@@ -0,0 +1,389 @@
+Summary: Samba SMB client and server
+Name: samba-appliance
+Version: 0.2
+Release: 1
+Copyright: GNU GPL version 2
+Group: Networking
+Source: %{name}-%{version}-src.tar.gz
+Packager: John H Terpstra [Samba-Team] <jht@samba.org>
+Requires: pam >= 0.64
+Prereq: chkconfig fileutils
+BuildRoot: /var/tmp/samba
+Provides: winbind
+
+%define prefix /usr/local/samba
+
+%define tng_build_dir $RPM_BUILD_DIR/%{name}-%{version}/tng
+%define head_build_dir $RPM_BUILD_DIR/%{name}-%{version}/head
+
+%description
+Samba provides an SMB server which can be used to provide
+network services to SMB (sometimes called "Lan Manager")
+clients, including various versions of MS Windows, OS/2,
+and other Linux machines. Samba also provides some SMB
+clients, which complement the built-in SMB filesystem
+in Linux. Samba uses NetBIOS over TCP/IP (NetBT) protocols
+and does NOT need NetBEUI (Microsoft Raw NetBIOS frame)
+protocol.
+
+Samba-2 features an almost working NT Domain Control
+capability and includes the new SWAT (Samba Web Administration
+Tool) that allows samba's smb.conf file to be remotely managed
+using your favourite web browser. For the time being this is
+being enabled on TCP port 901 via inetd.
+
+Please refer to the WHATSNEW.txt document for fixup information.
+This binary release includes encrypted password support.
+Please read the smb.conf file and ENCRYPTION.txt in the
+docs directory for implementation details.
+
+NOTE: Red Hat Linux 5.X Uses PAM which has integrated support
+for Shadow passwords. Do NOT recompile with the SHADOW_PWD option
+enabled. Red Hat Linux has built in support for quotas in PAM.
+
+%changelog
+* Mon Jun 5 2000 Tim Potter <tpot@samba.org>
+ - Modified to use prefix=/usr/local/samba everywhere
+
+* Sat Nov 29 1999 Matthew Vanecek <mev0003@unt.edu>
+ - Added a Prefix and changed "/usr" to "%{prefix}"
+
+* Sat Nov 11 1999 Tridge <tridge@linuxcare.com>
+ - changed from mount.smb to mount.smbfs
+
+* Sat Oct 9 1999 Tridge <tridge@linuxcare.com>
+ - removed smbwrapper
+ - added smbmnt and smbmount
+
+* Sun Apr 25 1999 John H Terpstra <jht@samba.org>
+ - added smbsh.1 man page
+
+* Fri Mar 26 1999 Andrew Tridgell <tridge@samba.org>
+ - added --with-pam as pam is no longer used by default
+
+* Sat Jan 27 1999 Jeremy Allison <jra@samba.org>
+ - Removed smbrun binary and tidied up some loose ends
+
+* Sun Oct 25 1998 John H Terpstra <jht@samba.org>
+ - Added parameters to /config to ensure smb.conf, lmhosts,
+ and smbusers never gets over-written.
+
+* Sat Oct 24 1998 John H Terpstra <jht@samba.org>
+ - removed README.smbsh file from docs area
+
+* Mon Oct 05 1998 John H Terpstra <jht@samba.org>
+ - Added rpcclient to binaries list
+ - Added smbwrapper stuff
+
+* Fri Aug 21 1998 John H Terpstra <jht@samba.org>
+ - Updated for Samba version 2.0 building
+
+* Tue Jul 07 1998 Erik Troan <ewt@redhat.com>
+ - updated postun triggerscript to check $0
+ - clear /etc/codepages from %preun instead of %postun
+
+* Sat Jul 04 1998 John H Terpstra <jht@samba.org>
+ - fixed codepage preservation during update via -Uvh
+
+* Mon Jun 08 1998 Erik Troan <ewt@redhat.com>
+ - made the %postun script a tad less agressive; no reason to remove
+ the logs or lock file
+ - the %postun and %preun should only exectute if this is the final
+ removal
+ - migrated %triggerpostun from Red Hat's samba package to work around
+ packaging problems in some Red Hat samba releases
+
+* Sun Apr 26 1998 John H Terpstra <jht@samba.org>
+ - Tidy up for early alpha releases
+ - added findsmb from SGI packaging
+
+* Thu Apr 09 1998 John H Terpstra <jht@samba.org>
+ - Updated spec file
+ - Included new codepage.936
+
+* Sat Mar 20 1998 John H Terpstra <jht@samba.org>
+ - Added swat facility
+
+* Sat Jan 24 1998 John H Terpstra <jht@samba.org>
+ - Many optimisations (some suggested by Manoj Kasichainula <manojk@io.com>
+ - Use of chkconfig in place of individual symlinks to /etc/rc.d/init/smb
+ - Compounded make line
+ - Updated smb.init restart mechanism
+ - Use compound mkdir -p line instead of individual calls to mkdir
+ - Fixed smb.conf file path for log files
+ - Fixed smb.conf file path for incoming smb print spool directory
+ - Added a number of options to smb.conf file
+ - Added smbadduser command (missed from all previous RPMs) - Doooh!
+ - Added smbuser file and smb.conf file updates for username map
+
+%prep
+%setup
+
+%build
+make config
+make
+
+%install
+rm -rf $RPM_BUILD_ROOT
+
+# Install stuff for tng
+
+mkdir -p $RPM_BUILD_ROOT%{prefix}/bin
+mkdir -p $RPM_BUILD_ROOT/lib/security
+cp %{tng_build_dir}/bin/samedit $RPM_BUILD_ROOT%{prefix}/bin
+cp %{tng_build_dir}/bin/winbindd $RPM_BUILD_ROOT%{prefix}/bin
+cp %{tng_build_dir}/nsswitch/libnss_winbind.so $RPM_BUILD_ROOT/lib
+cp %{tng_build_dir}/nsswitch/pam_winbind.so $RPM_BUILD_ROOT/lib/security
+
+# Install stuff for head
+
+mkdir -p $RPM_BUILD_ROOT%{prefix}/lib/codepages/src
+mkdir -p $RPM_BUILD_ROOT/etc/{logrotate.d,pam.d}
+mkdir -p $RPM_BUILD_ROOT/etc/rc.d/{init.d,rc0.d,rc1.d,rc2.d,rc3.d,rc5.d,rc6.d}
+mkdir -p $RPM_BUILD_ROOT%{prefix}/bin
+mkdir -p $RPM_BUILD_ROOT%{prefix}/share/swat/{images,help,include}
+mkdir -p $RPM_BUILD_ROOT%{prefix}/man/{man1,man5,man7,man8}
+mkdir -p $RPM_BUILD_ROOT%{prefix}/var/locks
+mkdir -p $RPM_BUILD_ROOT%{prefix}/private
+
+# Install standard binary files
+for i in nmblookup smbclient smbspool smbpasswd smbstatus testparm testprns \
+ make_smbcodepage make_printerdef
+do
+install -m755 -s %{head_build_dir}/source/bin/$i $RPM_BUILD_ROOT%{prefix}/bin
+done
+for i in addtosmbpass mksmbpasswd.sh smbtar
+do
+install -m755 %{head_build_dir}/source/script/$i $RPM_BUILD_ROOT%{prefix}/bin
+done
+
+# Install secure binary files
+for i in smbd nmbd swat smbmount smbmnt smbumount
+do
+install -m755 -s %{head_build_dir}/source/bin/$i $RPM_BUILD_ROOT%{prefix}/bin
+done
+
+# we need a symlink for mount to recognise the smb filesystem type
+#ln -sf %{prefix}/bin/smbmount $RPM_BUILD_ROOT%{prefix}/bin/mount.smbfs
+
+# Install level 1 man pages
+for i in smbclient.1 smbrun.1 smbstatus.1 smbtar.1 testparm.1 testprns.1 make_smbcodepage.1 nmblookup.1
+do
+install -m644 %{head_build_dir}/docs/manpages/$i $RPM_BUILD_ROOT%{prefix}/man/man1
+done
+
+# Install codepage source files
+for i in 437 737 850 852 861 866 932 936 949 950
+do
+install -m644 %{head_build_dir}/source/codepages/codepage_def.$i $RPM_BUILD_ROOT%{prefix}/lib/codepages/src
+done
+
+# Install SWAT helper files
+for i in %{head_build_dir}/swat/help/*.html %{head_build_dir}/docs/htmldocs/*.html
+do
+install -m644 $i $RPM_BUILD_ROOT%{prefix}/share/swat/help
+done
+for i in %{head_build_dir}/swat/images/*.gif
+do
+install -m644 $i $RPM_BUILD_ROOT%{prefix}/share/swat/images
+done
+for i in %{head_build_dir}/swat/include/*.html
+do
+install -m644 $i $RPM_BUILD_ROOT%{prefix}/share/swat/include
+done
+
+# Install the miscellany
+install -m644 %{head_build_dir}/swat/README $RPM_BUILD_ROOT%{prefix}/share/swat
+install -m644 %{head_build_dir}/docs/manpages/smb.conf.5 $RPM_BUILD_ROOT%{prefix}/man/man5
+install -m644 %{head_build_dir}/docs/manpages/lmhosts.5 $RPM_BUILD_ROOT%{prefix}/man/man5
+install -m644 %{head_build_dir}/docs/manpages/smbpasswd.5 $RPM_BUILD_ROOT%{prefix}/man/man5
+install -m644 %{head_build_dir}/docs/manpages/samba.7 $RPM_BUILD_ROOT%{prefix}/man/man7
+install -m644 %{head_build_dir}/docs/manpages/smbd.8 $RPM_BUILD_ROOT%{prefix}/man/man8
+install -m644 %{head_build_dir}/docs/manpages/nmbd.8 $RPM_BUILD_ROOT%{prefix}/man/man8
+install -m644 %{head_build_dir}/docs/manpages/swat.8 $RPM_BUILD_ROOT%{prefix}/man/man8
+install -m644 %{head_build_dir}/docs/manpages/smbmnt.8 $RPM_BUILD_ROOT%{prefix}/man/man8
+install -m644 %{head_build_dir}/docs/manpages/smbmount.8 $RPM_BUILD_ROOT%{prefix}/man/man8
+install -m644 %{head_build_dir}/docs/manpages/smbpasswd.8 $RPM_BUILD_ROOT%{prefix}/man/man8
+install -m644 %{head_build_dir}/docs/manpages/smbspool.8 $RPM_BUILD_ROOT%{prefix}/man/man8
+install -m644 $RPM_BUILD_DIR/%{name}-%{version}/smb.conf-appliance $RPM_BUILD_ROOT%{prefix}/lib/smb.conf
+install -m644 %{head_build_dir}/packaging/RedHat/smbusers $RPM_BUILD_ROOT/etc/smbusers
+install -m755 %{head_build_dir}/packaging/RedHat/smbprint $RPM_BUILD_ROOT%{prefix}/bin
+install -m755 %{head_build_dir}/packaging/RedHat/findsmb $RPM_BUILD_ROOT%{prefix}/bin
+install -m755 %{head_build_dir}/packaging/RedHat/smbadduser $RPM_BUILD_ROOT%{prefix}/bin
+install -m755 %{head_build_dir}/packaging/RedHat/smb.init $RPM_BUILD_ROOT/etc/rc.d/init.d/smb
+install -m755 %{head_build_dir}/packaging/RedHat/smb.init $RPM_BUILD_ROOT%{prefix}/bin/samba
+install -m644 %{head_build_dir}/packaging/RedHat/samba.pamd $RPM_BUILD_ROOT/etc/pam.d/samba
+install -m644 %{head_build_dir}/packaging/RedHat/samba.log $RPM_BUILD_ROOT/etc/logrotate.d/samba
+echo 127.0.0.1 localhost > $RPM_BUILD_ROOT/etc/lmhosts
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+#/sbin/chkconfig --add smb
+
+# Build codepage load files
+for i in 437 737 850 852 861 866 932 936 949 950
+do
+%{prefix}/bin/make_smbcodepage c $i %{prefix}/lib/codepages/src/codepage_def.$i %{prefix}/lib/codepages/codepage.$i
+done
+
+# Add swat entry to /etc/services if not already there
+if !( grep ^[:space:]*swat /etc/services > /dev/null ) then
+ echo 'swat 901/tcp # Add swat service used via inetd' >> /etc/services
+fi
+
+# Add swat entry to /etc/inetd.conf if needed
+if !( grep ^[:space:]*swat /etc/inetd.conf > /dev/null ) then
+ echo 'swat stream tcp nowait.400 root %{prefix}/sbin/swat swat' >> /etc/inetd.conf
+killall -1 inetd || :
+fi
+
+%preun
+if [ $1 = 0 ] ; then
+ /sbin/chkconfig --del smb
+
+ for n in %{prefix}/lib/codepages/*; do
+ if [ $n != %{prefix}/lib/codepages/src ]; then
+ rm -rf $n
+ fi
+ done
+ # We want to remove the browse.dat and wins.dat files so they can not interfer with a new version of samba!
+ if [ -e %{prefix}/var/locks/browse.dat ]; then
+ rm -f %{prefix}/var/locks/browse.dat
+ fi
+ if [ -e %{prefix}/var/locks/wins.dat ]; then
+ rm -f %{prefix}/var/locks/wins.dat
+ fi
+fi
+
+%postun
+# Only delete remnants of samba if this is the final deletion.
+if [ $1 = 0 ] ; then
+ if [ -x /etc/pam.d/samba ]; then
+ rm -f /etc/pam.d/samba
+ fi
+ if [ -e /var/log/samba ]; then
+ rm -rf /var/log/samba
+ fi
+ if [ -e /var/lock/samba ]; then
+ rm -rf /var/lock/samba
+ fi
+
+ # Remove swat entries from /etc/inetd.conf and /etc/services
+ cd /etc
+ tmpfile=/etc/tmp.$$
+ sed -e '/^[:space:]*swat.*$/d' /etc/inetd.conf > $tmpfile
+ mv $tmpfile inetd.conf
+ sed -e '/^[:space:]*swat.*$/d' /etc/services > $tmpfile
+ mv $tmpfile services
+fi
+
+%triggerpostun -- samba < samba-2.0.0
+if [ $0 != 0 ]; then
+ /sbin/chkconfig --add smb
+fi
+
+
+%files
+%doc %{head_build_dir}/README %{head_build_dir}/COPYING
+%doc %{head_build_dir}/Manifest %{head_build_dir}/Read-Manifest-Now
+%doc %{head_build_dir}/WHATSNEW.txt %{head_build_dir}/Roadmap
+%doc %{head_build_dir}/docs
+%doc %{head_build_dir}/swat/README
+%doc %{head_build_dir}/examples
+%attr(-,root,root) %{prefix}/bin/smbd
+%attr(-,root,root) %{prefix}/bin/nmbd
+%attr(-,root,root) %{prefix}/bin/swat
+%attr(-,root,root) %{prefix}/bin/smbmnt
+%attr(-,root,root) %{prefix}/bin/smbmount
+%attr(-,root,root) %{prefix}/bin/smbumount
+%attr(0750,root,root) %{prefix}/bin/samba
+%attr(-,root,root) %{prefix}/bin/addtosmbpass
+%attr(-,root,root) %{prefix}/bin/mksmbpasswd.sh
+%attr(-,root,root) %{prefix}/bin/smbclient
+%attr(-,root,root) %{prefix}/bin/smbspool
+%attr(-,root,root) %{prefix}/bin/testparm
+%attr(-,root,root) %{prefix}/bin/testprns
+%attr(-,root,root) %{prefix}/bin/findsmb
+%attr(-,root,root) %{prefix}/bin/smbstatus
+%attr(-,root,root) %{prefix}/bin/nmblookup
+%attr(-,root,root) %{prefix}/bin/make_smbcodepage
+%attr(-,root,root) %{prefix}/bin/make_printerdef
+%attr(-,root,root) %{prefix}/bin/smbpasswd
+%attr(-,root,root) %{prefix}/bin/smbtar
+%attr(-,root,root) %{prefix}/bin/smbprint
+%attr(-,root,root) %{prefix}/bin/smbadduser
+%attr(-,root,root) %{prefix}/share/swat/help/welcome.html
+%attr(-,root,root) %{prefix}/share/swat/help/DOMAIN_MEMBER.html
+%attr(-,root,root) %{prefix}/share/swat/help/NT_Security.html
+%attr(-,root,root) %{prefix}/share/swat/help/lmhosts.5.html
+%attr(-,root,root) %{prefix}/share/swat/help/make_smbcodepage.1.html
+%attr(-,root,root) %{prefix}/share/swat/help/nmbd.8.html
+%attr(-,root,root) %{prefix}/share/swat/help/nmblookup.1.html
+%attr(-,root,root) %{prefix}/share/swat/help/samba.7.html
+%attr(-,root,root) %{prefix}/share/swat/help/smb.conf.5.html
+%attr(-,root,root) %{prefix}/share/swat/help/smbclient.1.html
+%attr(-,root,root) %{prefix}/share/swat/help/smbspool.8.html
+%attr(-,root,root) %{prefix}/share/swat/help/smbd.8.html
+%attr(-,root,root) %{prefix}/share/swat/help/smbpasswd.5.html
+%attr(-,root,root) %{prefix}/share/swat/help/smbpasswd.8.html
+%attr(-,root,root) %{prefix}/share/swat/help/smbrun.1.html
+%attr(-,root,root) %{prefix}/share/swat/help/smbstatus.1.html
+%attr(-,root,root) %{prefix}/share/swat/help/smbtar.1.html
+%attr(-,root,root) %{prefix}/share/swat/help/swat.8.html
+%attr(-,root,root) %{prefix}/share/swat/help/testparm.1.html
+%attr(-,root,root) %{prefix}/share/swat/help/testprns.1.html
+%attr(-,root,root) %{prefix}/share/swat/images/globals.gif
+%attr(-,root,root) %{prefix}/share/swat/images/home.gif
+%attr(-,root,root) %{prefix}/share/swat/images/passwd.gif
+%attr(-,root,root) %{prefix}/share/swat/images/printers.gif
+%attr(-,root,root) %{prefix}/share/swat/images/shares.gif
+%attr(-,root,root) %{prefix}/share/swat/images/samba.gif
+%attr(-,root,root) %{prefix}/share/swat/images/status.gif
+%attr(-,root,root) %{prefix}/share/swat/images/viewconfig.gif
+%attr(-,root,root) %{prefix}/share/swat/include/header.html
+%attr(-,root,root) %{prefix}/share/swat/include/footer.html
+%attr(-,root,root) %config(noreplace) /etc/lmhosts
+%attr(-,root,root) %config(noreplace) %{prefix}/lib/smb.conf
+%attr(-,root,root) %config(noreplace) /etc/smbusers
+%attr(-,root,root) /etc/rc.d/init.d/smb
+%attr(-,root,root) /etc/logrotate.d/samba
+%attr(-,root,root) /etc/pam.d/samba
+%attr(-,root,root) %{prefix}/lib/codepages/src/codepage_def.437
+%attr(-,root,root) %{prefix}/lib/codepages/src/codepage_def.737
+%attr(-,root,root) %{prefix}/lib/codepages/src/codepage_def.850
+%attr(-,root,root) %{prefix}/lib/codepages/src/codepage_def.852
+%attr(-,root,root) %{prefix}/lib/codepages/src/codepage_def.861
+%attr(-,root,root) %{prefix}/lib/codepages/src/codepage_def.866
+%attr(-,root,root) %{prefix}/lib/codepages/src/codepage_def.932
+%attr(-,root,root) %{prefix}/lib/codepages/src/codepage_def.936
+%attr(-,root,root) %{prefix}/lib/codepages/src/codepage_def.949
+%attr(-,root,root) %{prefix}/lib/codepages/src/codepage_def.950
+%attr(-,root,root) %{prefix}/man/man1/smbstatus.1
+%attr(-,root,root) %{prefix}/man/man1/smbclient.1
+%attr(-,root,root) %{prefix}/man/man1/make_smbcodepage.1
+%attr(-,root,root) %{prefix}/man/man1/smbrun.1
+%attr(-,root,root) %{prefix}/man/man1/smbtar.1
+%attr(-,root,root) %{prefix}/man/man1/testparm.1
+%attr(-,root,root) %{prefix}/man/man1/testprns.1
+%attr(-,root,root) %{prefix}/man/man1/nmblookup.1
+%attr(-,root,root) %{prefix}/man/man5/smb.conf.5
+%attr(-,root,root) %{prefix}/man/man5/lmhosts.5
+%attr(-,root,root) %{prefix}/man/man5/smbpasswd.5
+%attr(-,root,root) %{prefix}/man/man7/samba.7
+%attr(-,root,root) %{prefix}/man/man8/smbd.8
+%attr(-,root,root) %{prefix}/man/man8/nmbd.8
+%attr(-,root,root) %{prefix}/man/man8/smbpasswd.8
+%attr(-,root,root) %{prefix}/man/man8/swat.8
+%attr(-,root,root) %{prefix}/man/man8/smbmnt.8
+%attr(-,root,root) %{prefix}/man/man8/smbmount.8
+%attr(-,root,root) %{prefix}/man/man8/smbspool.8
+%attr(-,root,root) %dir %{prefix}/lib/codepages
+%attr(-,root,root) %dir %{prefix}/lib/codepages/src
+%attr(-,root,root) %dir %{prefix}/var/locks
+%attr(-,root,root) %dir %{prefix}/private
+%attr(-,root,root) %{prefix}/bin/winbindd
+%attr(-,root,root) %{prefix}/bin/samedit
+%attr(-,root,root) /lib/libnss_winbind.so
+%attr(-,root,root) /lib/security/pam_winbind.so
diff --git a/examples/appliance/build.sh b/examples/appliance/build.sh
new file mode 100755
index 00000000000..ad7a4eb5fb3
--- /dev/null
+++ b/examples/appliance/build.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+tar --exclude=CVS -czf /usr/src/redhat/SOURCES/samba-appliance-0.2-src.tar.gz samba-appliance-0.2
+rpm -ba appliance.spec
diff --git a/examples/appliance/smb.conf-appliance b/examples/appliance/smb.conf-appliance
new file mode 100644
index 00000000000..a3c62256c5a
--- /dev/null
+++ b/examples/appliance/smb.conf-appliance
@@ -0,0 +1,8 @@
+[global]
+ workgroup = DOMAIN
+ security = domain
+ encrypt passwords = true
+ stat cache = false
+ winbind uid = 10000-20000
+ winbind gid = 10000-20000
+ password server = PDC
diff --git a/examples/auth/Makefile b/examples/auth/Makefile
new file mode 100644
index 00000000000..dac28fdd40a
--- /dev/null
+++ b/examples/auth/Makefile
@@ -0,0 +1,31 @@
+# Makefile for samba-pdb examples
+# Variables
+
+CC = gcc
+LIBTOOL = libtool
+
+SAMBA_SRC = ../../source
+SAMBA_INCL = ../../source/include
+UBIQX_SRC = ../../source/ubiqx
+SMBWR_SRC = ../../source/smbwrapper
+CFLAGS = -I$(SAMBA_SRC) -I$(SAMBA_INCL) -I$(UBIQX_SRC) -I$(SMBWR_SRC) -Wall -g
+AUTH_OBJS = auth_skel.so
+
+# Default target
+
+default: $(AUTH_OBJS)
+
+# Pattern rules
+
+%.so: %.lo
+ $(LIBTOOL) $(CC) -shared -o $@ $< $(LDFLAGS)
+
+%.lo: %.c
+ $(LIBTOOL) $(CC) $(CPPFLAGS) $(CFLAGS) -c $<
+
+# Misc targets
+
+clean:
+ rm -rf .libs
+ rm -f core *~ *% *.bak \
+ $(AUTH_OBJS) $(AUTH_OBJS:.so=.o) $(AUTH_OBJS:.so=.lo)
diff --git a/examples/auth/auth_skel.c b/examples/auth/auth_skel.c
new file mode 100644
index 00000000000..bcc3bdd96a3
--- /dev/null
+++ b/examples/auth/auth_skel.c
@@ -0,0 +1,59 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett 2001
+ Copyright (C) Jelmer Vernooij 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+static NTSTATUS check_skel_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;
+ }
+
+ /* Insert your authentication checking code here,
+ * and return NT_STATUS_OK if authentication succeeds */
+
+ /* For now, just refuse all connections */
+ return NT_STATUS_LOGON_FAILURE;
+}
+
+/* module initialisation */
+NTSTATUS auth_init_skel(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_skel_security;
+ (*auth_method)->name = "skel";
+ return NT_STATUS_OK;
+}
+
+NTSTATUS init_module(void)
+{
+ return smb_register_auth(AUTH_INTERFACE_VERSION, "skel", auth_init_skel);
+}
diff --git a/examples/autofs/auto.a b/examples/autofs/auto.a
new file mode 100644
index 00000000000..0fa8f4efc30
--- /dev/null
+++ b/examples/autofs/auto.a
@@ -0,0 +1,15 @@
+# automount points below /a
+
+# This is an automounter map and it has the following format
+# key [ -mount-options-separated-by-comma ] location
+# Details may be found in the autofs(5) manpage
+
+# nfs servers
+valepp -fstype=nfs,rsize=8192,wsize=8192 valepp:/
+galaun -fstype=nfs,rsize=8192,wsize=8192 galaun:/
+
+# smb-servers
+supra_andreas -fstype=smb,username=andreas,password=foo ://supra/aheinrich
+supra_cspiel -fstype=smb,username=cspiel ://supra/cspiel
+phonon_andreas -fstype=smb,username=andreas ://phonon/andreas
+helium_cspiel -fstype=smb,username=cspiel ://helium/cspiel
diff --git a/examples/dce-dfs/README b/examples/dce-dfs/README
new file mode 100644
index 00000000000..4aaba8bb33e
--- /dev/null
+++ b/examples/dce-dfs/README
@@ -0,0 +1,4 @@
+this is a sample configuration file from Jim Doyle <doyle@oec.com> who
+did the DCE/DFS patches for Samba. It shows how to make DCE/DFS shares
+available.
+
diff --git a/examples/dce-dfs/smb.conf b/examples/dce-dfs/smb.conf
new file mode 100644
index 00000000000..f5f155b8e6c
--- /dev/null
+++ b/examples/dce-dfs/smb.conf
@@ -0,0 +1,42 @@
+[global]
+ printing = bsd
+ printcap name = /etc/printcap
+ load printers = no
+ guest account = guest
+ log file = /usr/local/samba/var/log.%m
+ log level = 8
+ password level = 8
+
+[homes]
+ comment = Home Directories
+ browseable = no
+ read only = no
+ create mode = 0750
+
+[test]
+ comment = test stuff
+ path = /dept/mis/home/testacct
+ valid users = testacct
+ public = no
+ writable = yes
+
+[namespace]
+ comment = DCE-DFS Global Root
+ path = /...
+ public = no
+ writable = yes
+
+[oecdfs]
+ comment = Corporate Cell
+ path = /.../corp.boston.oec.com/fs
+ browseable = no
+ read only = no
+ create mode = 0750
+
+[develdfs]
+ comment = Technology Development Cell
+ path = /.../devel.boston.oec.com/fs
+ browseable = no
+ read only = no
+ create mode = 0750
+
diff --git a/examples/genlogon/genlogon.pl b/examples/genlogon/genlogon.pl
new file mode 100644
index 00000000000..8ebf3921411
--- /dev/null
+++ b/examples/genlogon/genlogon.pl
@@ -0,0 +1,71 @@
+#!/usr/bin/perl
+#
+# genlogon.pl
+#
+# Perl script to generate user logon scripts on the fly, when users
+# connect from a Windows client. This script should be called from smb.conf
+# with the %U, %G and %L parameters. I.e:
+#
+# root preexec = genlogon.pl %U %G %L
+#
+# The script generated will perform
+# the following:
+#
+# 1. Log the user connection to /var/log/samba/netlogon.log
+# 2. Set the PC's time to the Linux server time (which is maintained
+# daily to the National Institute of Standard's Atomic clock on the
+# internet.
+# 3. Connect the user's home drive to H: (H for Home).
+# 4. Connect common drives that everyone uses.
+# 5. Connect group-specific drives for certain user groups.
+# 6. Connect user-specific drives for certain users.
+# 7. Connect network printers.
+
+# Log client connection
+#($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
+($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
+open LOG, ">>/var/log/samba/netlogon.log";
+print LOG "$mon/$mday/$year $hour:$min:$sec - User $ARGV[0] logged into $ARGV[1]\n";
+close LOG;
+
+# Start generating logon script
+open LOGON, ">/shared/netlogon/$ARGV[0].bat";
+print LOGON "\@ECHO OFF\r\n";
+
+# Connect shares just use by Software Development group
+if ($ARGV[1] eq "SOFTDEV" || $ARGV[0] eq "softdev")
+{
+ print LOGON "NET USE M: \\\\$ARGV[2]\\SOURCE\r\n";
+}
+
+# Connect shares just use by Technical Support staff
+if ($ARGV[1] eq "SUPPORT" || $ARGV[0] eq "support")
+{
+ print LOGON "NET USE S: \\\\$ARGV[2]\\SUPPORT\r\n";
+}
+
+# Connect shares just used by Administration staff
+If ($ARGV[1] eq "ADMIN" || $ARGV[0] eq "admin")
+{
+ print LOGON "NET USE L: \\\\$ARGV[2]\\ADMIN\r\n";
+ print LOGON "NET USE K: \\\\$ARGV[2]\\MKTING\r\n";
+}
+
+# Now connect Printers. We handle just two or three users a little
+# differently, because they are the exceptions that have desktop
+# printers on LPT1: - all other user's go to the LaserJet on the
+# server.
+if ($ARGV[0] eq 'jim'
+ || $ARGV[0] eq 'yvonne')
+{
+ print LOGON "NET UsE LPT2: \\\\$ARGV[2]\\LJET3\r\n";
+ print LOGON "NET USE LPT3: \\\\$ARGV[2]\\FAXQ\r\n";
+}
+else
+{
+ print LOGON "NET USE LPT1: \\\\$ARGV[2]\\LJET3\r\n";
+ print LOGON "NET USE LPT3: \\\\$ARGV[2]\\FAXQ\r\n";
+}
+
+# All done! Close the output file.
+close LOGON;
diff --git a/examples/libsmbclient/Makefile b/examples/libsmbclient/Makefile
new file mode 100644
index 00000000000..fcd5ef29003
--- /dev/null
+++ b/examples/libsmbclient/Makefile
@@ -0,0 +1,36 @@
+#
+CC = gcc
+
+SAMBA_INCL = ../../source/include
+EXTLIB_INCL = -I/usr/include/gtk-1.2 \
+ -I/usr/include/glib-1.2 \
+ -I/usr/lib/glib/include
+
+CFLAGS = -I$(SAMBA_INCL) $(EXTLIB_INCL)
+
+LDFLAGS = -L/usr/lib
+
+all: testsmbc tree testacl testbrowse
+
+testsmbc: testsmbc.o
+ @echo Linking testsmbc
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lsmbclient -L/usr/local/lib
+
+testsmbc-static: testsmbc.o
+ @echo Linking testsmbc
+ @$(CC) $(CFLAGS) -static $(LDFLAGS) -o $@ $< -lsmbclient -ldl -lnsl
+
+tree: tree.o
+ @echo Linking tree
+ @$(CC) `gtk-config --cflags` $(CFLAGS) $(LDFLAGS) -o $@ `gtk-config --libs` -lsmbclient $<
+
+testacl: testacl.o
+ @echo Linking testacl
+ @$(CC) `gtk-config --cflags` $(CFLAGS) $(LDFLAGS) -o $@ `gtk-config --libs` -lsmbclient -lpopt $<
+
+testbrowse: testbrowse.o
+ @echo Linking testbrowse
+ @$(CC) $(CFLAGS) $(LDFLAGS) -o $@ -lsmbclient -lpopt $<
+
+clean:
+ @rm -f *.o *~
diff --git a/examples/libsmbclient/README b/examples/libsmbclient/README
new file mode 100644
index 00000000000..d9a9f829174
--- /dev/null
+++ b/examples/libsmbclient/README
@@ -0,0 +1,8 @@
+Some simple example programs for libsmbclient ...
+
+testsmbc.c is kinda broken as it has many hardcoded bits in it
+
+tree.c is an example of how you might do some of these things with GTK+
+It needs lots of work but shows you some ways to use libsmbclient.
+
+Richard Sharpe, 17-May-2001 ...
diff --git a/examples/libsmbclient/testacl.c b/examples/libsmbclient/testacl.c
new file mode 100644
index 00000000000..47668f7c9c4
--- /dev/null
+++ b/examples/libsmbclient/testacl.c
@@ -0,0 +1,280 @@
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <popt.h>
+#include "libsmbclient.h"
+
+enum acl_mode
+{
+ SMB_ACL_GET,
+ SMB_ACL_SET,
+ SMB_ACL_DELETE,
+ SMB_ACL_MODIFY,
+ SMB_ACL_ADD,
+ SMB_ACL_CHOWN,
+ SMB_ACL_CHGRP
+};
+
+static void
+get_auth_data_fn(const char * pServer,
+ const char * pShare,
+ char * pWorkgroup,
+ int maxLenWorkgroup,
+ char * pUsername,
+ int maxLenUsername,
+ char * pPassword,
+ int maxLenPassword)
+
+{
+ char temp[128];
+
+ fprintf(stdout, "Workgroup: [%s] ", pWorkgroup);
+ fgets(temp, sizeof(temp), stdin);
+
+ if (temp[strlen(temp) - 1] == '\n') /* A new line? */
+ {
+ temp[strlen(temp) - 1] = '\0';
+ }
+
+ if (temp[0] != '\0')
+ {
+ strncpy(pWorkgroup, temp, maxLenWorkgroup - 1);
+ }
+
+ fprintf(stdout, "Username: [%s] ", pUsername);
+ fgets(temp, sizeof(temp), stdin);
+
+ if (temp[strlen(temp) - 1] == '\n') /* A new line? */
+ {
+ temp[strlen(temp) - 1] = '\0';
+ }
+
+ if (temp[0] != '\0')
+ {
+ strncpy(pUsername, temp, maxLenUsername - 1);
+ }
+
+ fprintf(stdout, "Password: ");
+ fgets(temp, sizeof(temp), stdin);
+
+ if (temp[strlen(temp) - 1] == '\n') /* A new line? */
+ {
+ temp[strlen(temp) - 1] = '\0';
+ }
+
+ if (temp[0] != '\0')
+ {
+ strncpy(pPassword, temp, maxLenPassword - 1);
+ }
+}
+
+
+int main(int argc, const char *argv[])
+{
+ int opt;
+ int flags;
+ int debug = 0;
+ int numeric = 0;
+ enum acl_mode mode = SMB_ACL_GET;
+ static char *the_acl = NULL;
+ int ret;
+ char *p;
+ char *debugstr;
+ char path[1024];
+ char value[1024];
+ poptContext pc;
+ struct poptOption long_options[] =
+ {
+ POPT_AUTOHELP
+ {
+ "numeric", 'n', POPT_ARG_NONE, &numeric,
+ 1, "Don't resolve sids or masks to names"
+ },
+ {
+ "debug", 'd', POPT_ARG_INT, &debug,
+ 0, "Set debug level (0-100)"
+ },
+ {
+ "delete", 'D', POPT_ARG_STRING, NULL,
+ 'D', "Delete an acl", "ACL"
+ },
+ {
+ "modify", 'M', POPT_ARG_STRING, NULL,
+ 'M', "Modify an acl", "ACL"
+ },
+ {
+ "add", 'a', POPT_ARG_STRING, NULL,
+ 'a', "Add an acl", "ACL"
+ },
+ {
+ "set", 'S', POPT_ARG_STRING, NULL,
+ 'S', "Set acls", "ACLS"
+ },
+ {
+ "chown", 'C', POPT_ARG_STRING, NULL,
+ 'C', "Change ownership of a file", "USERNAME"
+ },
+ {
+ "chgrp", 'G', POPT_ARG_STRING, NULL,
+ 'G', "Change group ownership of a file", "GROUPNAME"
+ },
+ {
+ "get", 'g', POPT_ARG_STRING, NULL,
+ 'g', "Get a specific acl attribute", "ACL"
+ },
+ {
+ NULL
+ }
+ };
+
+ setbuf(stdout, NULL);
+
+ pc = poptGetContext("smbcacls", argc, argv, long_options, 0);
+
+ poptSetOtherOptionHelp(pc, "smb://server1/share1/filename");
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'S':
+ the_acl = strdup(poptGetOptArg(pc));
+ mode = SMB_ACL_SET;
+ break;
+
+ case 'D':
+ the_acl = strdup(poptGetOptArg(pc));
+ mode = SMB_ACL_DELETE;
+ break;
+
+ case 'M':
+ the_acl = strdup(poptGetOptArg(pc));
+ mode = SMB_ACL_MODIFY;
+ break;
+
+ case 'a':
+ the_acl = strdup(poptGetOptArg(pc));
+ mode = SMB_ACL_ADD;
+ break;
+
+ case 'g':
+ the_acl = strdup(poptGetOptArg(pc));
+ mode = SMB_ACL_GET;
+ break;
+
+ case 'C':
+ the_acl = strdup(poptGetOptArg(pc));
+ mode = SMB_ACL_CHOWN;
+ break;
+
+ case 'G':
+ the_acl = strdup(poptGetOptArg(pc));
+ mode = SMB_ACL_CHGRP;
+ break;
+ }
+ }
+
+ /* Make connection to server */
+ if(!poptPeekArg(pc)) {
+ poptPrintUsage(pc, stderr, 0);
+ return 1;
+ }
+
+ strcpy(path, poptGetArg(pc));
+
+ if (smbc_init(get_auth_data_fn, debug) != 0)
+ {
+ printf("Could not initialize smbc_ library\n");
+ return 1;
+ }
+
+ /* Perform requested action */
+
+ switch(mode)
+ {
+ case SMB_ACL_GET:
+ if (the_acl == NULL)
+ {
+ if (numeric)
+ {
+ the_acl = "system.nt_sec_desc.*";
+ }
+ else
+ {
+ the_acl = "system.nt_sec_desc.*+";
+ }
+ }
+ ret = smbc_getxattr(path, the_acl, value, sizeof(value));
+ if (ret < 0)
+ {
+ printf("Could not get attributes for [%s] %d: %s\n",
+ path, errno, strerror(errno));
+ return 1;
+ }
+
+ printf("Attributes for [%s] are:\n%s\n", path, value);
+ break;
+
+ case SMB_ACL_ADD:
+ flags = SMBC_XATTR_FLAG_CREATE;
+ debugstr = "add attributes";
+ goto do_set;
+
+ case SMB_ACL_MODIFY:
+ flags = SMBC_XATTR_FLAG_REPLACE;
+ debugstr = "modify attributes";
+ goto do_set;
+
+ case SMB_ACL_CHOWN:
+ snprintf(value, sizeof(value),
+ "system.nt_sec_desc.owner%s:%s",
+ numeric ? "" : "+", the_acl);
+ the_acl = value;
+ debugstr = "chown owner";
+ goto do_set;
+
+ case SMB_ACL_CHGRP:
+ snprintf(value, sizeof(value),
+ "system.nt_sec_desc.group%s:%s",
+ numeric ? "" : "+", the_acl);
+ the_acl = value;
+ debugstr = "change group";
+ goto do_set;
+
+ case SMB_ACL_SET:
+ flags = 0;
+ debugstr = "set attributes";
+
+ do_set:
+ if ((p = strchr(the_acl, ':')) == NULL)
+ {
+ printf("Missing value. ACL must be name:value pair\n");
+ return 1;
+ }
+
+ *p++ = '\0';
+
+ ret = smbc_setxattr(path, the_acl, p, strlen(p), flags);
+ if (ret < 0)
+ {
+ printf("Could not %s for [%s] %d: %s\n",
+ debugstr, path, errno, strerror(errno));
+ return 1;
+ }
+ break;
+
+ case SMB_ACL_DELETE:
+ ret = smbc_removexattr(path, the_acl);
+ if (ret < 0)
+ {
+ printf("Could not remove attribute %s for [%s] %d:%s\n",
+ the_acl, path, errno, strerror(errno));
+ return 1;
+ }
+ break;
+
+ default:
+ printf("operation not yet implemented\n");
+ break;
+ }
+
+ return 0;
+}
diff --git a/examples/libsmbclient/testbrowse.c b/examples/libsmbclient/testbrowse.c
new file mode 100644
index 00000000000..d2472230a20
--- /dev/null
+++ b/examples/libsmbclient/testbrowse.c
@@ -0,0 +1,91 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client library test program for browsing with different master browsers
+ Copyright (C) Derrell Lipman 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <libsmbclient.h>
+
+static void
+auth_fn(const char * pServer,
+ const char * pShare,
+ char * pWorkgroup,
+ int workgroup_len,
+ char * pUsername,
+ int username_len,
+ char * pPassword,
+ int password_len)
+
+{
+ strncpy(pUsername, "anonymous", username_len); /* doesn't matter what */
+ strncpy(pPassword, "password", password_len); /* ditto */
+}
+
+
+int
+main(int argc, char * argv[])
+{
+ int debug = 4;
+ int opt;
+ char * p;
+ char buf[1024];
+ int dir;
+ struct smbc_dirent * dirent;
+ char ** ppUrl;
+ char * urlList[] =
+ {
+ "smb://",
+ "smb://?mb=.any",
+ "smb://?mb=.all",
+ "smb://?mb=xx", /* this one is suupposed to fail */
+ NULL
+ };
+
+ if (smbc_init(auth_fn, debug) != 0)
+ {
+ printf("Could not initialize smbc_ library\n");
+ return 1;
+ }
+
+ for (ppUrl = urlList; *ppUrl != NULL; ppUrl++)
+ {
+ printf("Opening (%s)...\n", *ppUrl);
+
+ if ((dir = smbc_opendir(*ppUrl)) < 0)
+ {
+ printf("Could not open [%s] (%d:%s)\n",
+ *ppUrl, errno, strerror(errno));
+ continue;
+ }
+
+ while ((dirent = smbc_readdir(dir)) != NULL)
+ {
+ printf("%s\n", dirent->name);
+ }
+
+ smbc_closedir(dir);
+ }
+
+ exit(0);
+}
+
diff --git a/examples/libsmbclient/testsmbc.c b/examples/libsmbclient/testsmbc.c
new file mode 100644
index 00000000000..888a9c0d4f9
--- /dev/null
+++ b/examples/libsmbclient/testsmbc.c
@@ -0,0 +1,335 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client library test program
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000
+ Copyright (C) John Terpsra 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 <stdio.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <libsmbclient.h>
+
+void auth_fn(const char *server, const char *share,
+ char *workgroup, int wgmaxlen, char *username, int unmaxlen,
+ char *password, int pwmaxlen)
+{
+ char temp[128];
+
+ fprintf(stdout, "Need password for //%s/%s\n", server, share);
+
+ fprintf(stdout, "Enter workgroup: [%s] ", workgroup);
+ fgets(temp, sizeof(temp), stdin);
+
+ if (temp[strlen(temp) - 1] == 0x0a) /* A new line? */
+ temp[strlen(temp) - 1] = 0x00;
+
+ if (temp[0]) strncpy(workgroup, temp, wgmaxlen - 1);
+
+ fprintf(stdout, "Enter username: [%s] ", username);
+ fgets(temp, sizeof(temp), stdin);
+
+ if (temp[strlen(temp) - 1] == 0x0a) /* A new line? */
+ temp[strlen(temp) - 1] = 0x00;
+
+ if (temp[0]) strncpy(username, temp, unmaxlen - 1);
+
+ fprintf(stdout, "Enter password: [%s] ", password);
+ fgets(temp, sizeof(temp), stdin);
+
+ if (temp[strlen(temp) - 1] == 0x0a) /* A new line? */
+ temp[strlen(temp) - 1] = 0x00;
+
+ if (temp[0]) strncpy(password, temp, pwmaxlen - 1);
+
+}
+
+int global_id = 0;
+
+void print_list_fn(struct print_job_info *pji)
+{
+
+ fprintf(stdout, "Print job: ID: %u, Prio: %u, Size: %u, User: %s, Name: %s\n",
+ pji->id, pji->priority, pji->size, pji->user, pji->name);
+
+ global_id = pji->id;
+
+}
+
+int main(int argc, char *argv[])
+{
+ int err, fd, dh1, dh2, dh3, dsize, dirc;
+ const char *file = "smb://samba/public/testfile.txt";
+ const char *file2 = "smb://samba/public/testfile2.txt";
+ char buff[256];
+ char dirbuf[512];
+ char *dirp;
+ struct stat st1, st2;
+
+ err = smbc_init(auth_fn, 10); /* Initialize things */
+
+ if (err < 0) {
+
+ fprintf(stderr, "Initializing the smbclient library ...: %s\n", strerror(errno));
+
+ }
+
+ if (argc > 1) {
+
+ if ((dh1 = smbc_opendir(argv[1]))<1) {
+
+ fprintf(stderr, "Could not open directory: %s: %s\n",
+ argv[1], strerror(errno));
+
+ exit(1);
+
+ }
+
+ fprintf(stdout, "Directory handles: %u, %u, %u\n", dh1, dh2, dh3);
+
+ /* Now, list those directories, but in funny ways ... */
+
+ dirp = (char *)dirbuf;
+
+ if ((dirc = smbc_getdents(dh1, (struct smbc_dirent *)dirp,
+ sizeof(dirbuf))) < 0) {
+
+ fprintf(stderr, "Problems getting directory entries: %s\n",
+ strerror(errno));
+
+ exit(1);
+
+ }
+
+ /* Now, process the list of names ... */
+
+ fprintf(stdout, "Directory listing, size = %u\n", dirc);
+
+ while (dirc > 0) {
+
+ dsize = ((struct smbc_dirent *)dirp)->dirlen;
+ fprintf(stdout, "Dir Ent, Type: %u, Name: %s, Comment: %s\n",
+ ((struct smbc_dirent *)dirp)->smbc_type,
+ ((struct smbc_dirent *)dirp)->name,
+ ((struct smbc_dirent *)dirp)->comment);
+
+ dirp += dsize;
+ (char *)dirc -= dsize;
+
+ }
+
+ dirp = (char *)dirbuf;
+
+ exit(1);
+
+ }
+
+ /* For now, open a file on a server that is hard coded ... later will
+ * read from the command line ...
+ */
+
+ fd = smbc_open(file, O_RDWR | O_CREAT | O_TRUNC, 0666);
+
+ if (fd < 0) {
+
+ fprintf(stderr, "Creating file: %s: %s\n", file, strerror(errno));
+ exit(0);
+
+ }
+
+ fprintf(stdout, "Opened or created file: %s\n", file);
+
+ /* Now, write some date to the file ... */
+
+ bzero(buff, sizeof(buff));
+ strcpy(buff, "Some test data for the moment ...");
+
+ err = smbc_write(fd, buff, sizeof(buff));
+
+ if (err < 0) {
+
+ fprintf(stderr, "writing file: %s: %s\n", file, strerror(errno));
+ exit(0);
+
+ }
+
+ fprintf(stdout, "Wrote %d bytes to file: %s\n", sizeof(buff), buff);
+
+ /* Now, seek the file back to offset 0 */
+
+ err = smbc_lseek(fd, SEEK_SET, 0);
+
+ if (err < 0) {
+
+ fprintf(stderr, "Seeking file: %s: %s\n", file, strerror(errno));
+ exit(0);
+
+ }
+
+ fprintf(stdout, "Completed lseek on file: %s\n", file);
+
+ /* Now, read the file contents back ... */
+
+ err = smbc_read(fd, buff, sizeof(buff));
+
+ if (err < 0) {
+
+ fprintf(stderr, "Reading file: %s: %s\n", file, strerror(errno));
+ exit(0);
+
+ }
+
+ fprintf(stdout, "Read file: %s\n", buff); /* Should check the contents */
+
+ fprintf(stdout, "Now fstat'ing file: %s\n", file);
+
+ err = smbc_fstat(fd, &st1);
+
+ if (err < 0) {
+
+ fprintf(stderr, "Fstat'ing file: %s: %s\n", file, strerror(errno));
+ exit(0);
+
+ }
+
+
+ /* Now, close the file ... */
+
+ err = smbc_close(fd);
+
+ if (err < 0) {
+
+ fprintf(stderr, "Closing file: %s: %s\n", file, strerror(errno));
+
+ }
+
+ /* Now, rename the file ... */
+
+ err = smbc_rename(file, file2);
+
+ if (err < 0) {
+
+ fprintf(stderr, "Renaming file: %s to %s: %s\n", file, file2, strerror(errno));
+
+ }
+
+ fprintf(stdout, "Renamed file %s to %s\n", file, file2);
+
+ /* Now, create a file and delete it ... */
+
+ fprintf(stdout, "Now, creating file: %s so we can delete it.\n", file);
+
+ fd = smbc_open(file, O_RDWR | O_CREAT, 0666);
+
+ if (fd < 0) {
+
+ fprintf(stderr, "Creating file: %s: %s\n", file, strerror(errno));
+ exit(0);
+
+ }
+
+ fprintf(stdout, "Opened or created file: %s\n", file);
+
+ err = smbc_close(fd);
+
+ if (err < 0) {
+
+ fprintf(stderr, "Closing file: %s: %s\n", file, strerror(errno));
+ exit(0);
+
+ }
+
+ /* Now, delete the file ... */
+
+ fprintf(stdout, "File %s created, now deleting ...\n", file);
+
+ err = smbc_unlink(file);
+
+ if (err < 0) {
+
+ fprintf(stderr, "Deleting file: %s: %s\n", file, strerror(errno));
+ exit(0);
+
+ }
+
+ /* Now, stat the file, file 2 ... */
+
+ fprintf(stdout, "Now stat'ing file: %s\n", file);
+
+ err = smbc_stat(file2, &st2);
+
+ if (err < 0) {
+
+ fprintf(stderr, "Stat'ing file: %s: %s\n", file, strerror(errno));
+ exit(0);
+
+ }
+
+ fprintf(stdout, "Stat'ed file: %s. Size = %d, mode = %04X\n", file2,
+ (int)st2.st_size, st2.st_mode);
+ fprintf(stdout, " time: %s\n", ctime(&st2.st_atime));
+ fprintf(stdout, "Earlier stat: %s, Size = %d, mode = %04X\n", file,
+ (int)st1.st_size, st1.st_mode);
+ fprintf(stdout, " time: %s\n", ctime(&st1.st_atime));
+
+ /* Now, make a directory ... */
+
+ fprintf(stdout, "Making directory smb://samba/public/make-dir\n");
+
+ if (smbc_mkdir("smb://samba/public/make-dir", 0666) < 0) {
+
+ fprintf(stderr, "Error making directory: smb://samba/public/make-dir: %s\n",
+ strerror(errno));
+
+ if (errno == EEXIST) { /* Try to delete the directory */
+
+ fprintf(stdout, "Trying to delete directory: smb://samba/public/make-dir\n");
+
+ if (smbc_rmdir("smb://samba/public/make-dir") < 0) { /* Error */
+
+ fprintf(stderr, "Error removing directory: smb://samba/public/make-dir: %s\n", strerror(errno));
+
+ exit(0);
+
+ }
+
+ fprintf(stdout, "Making directory: smb://samba/public/make-dir\n");
+
+ if (smbc_mkdir("smb://samba/public/make-dir", 666) < 0) {
+
+ fprintf(stderr, "Error making directory: smb://samba/public/make-dir: %s\n",
+ strerror(errno));
+
+ fprintf(stderr, "I give up!\n");
+
+ exit(1);
+
+ }
+
+ }
+
+ exit(0);
+
+ }
+
+ fprintf(stdout, "Made dir: make-dir\n");
+ return 0;
+}
diff --git a/examples/libsmbclient/tree.c b/examples/libsmbclient/tree.c
new file mode 100644
index 00000000000..f50b3670cff
--- /dev/null
+++ b/examples/libsmbclient/tree.c
@@ -0,0 +1,813 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 2.0
+ SMB client GTK+ tree-based application
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2001
+ Copyright (C) John Terpstra 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* example-gtk+ application, ripped off from the gtk+ tree.c sample */
+
+#include <stdio.h>
+#include <errno.h>
+#include <gtk/gtk.h>
+#include "libsmbclient.h"
+
+static GtkWidget *clist;
+
+struct tree_data {
+
+ guint32 type; /* Type of tree item, an SMBC_TYPE */
+ char name[256]; /* May need to change this later */
+
+};
+
+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),
+ GTK_OBJECT(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));
+
+ snprintf(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));
+
+ snprintf(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 workgroups: 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 */
+
+ 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);
+
+ }
+
+ dirp = (struct smbc_dirent *)dirbuf;
+
+ 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/examples/misc/extra_smbstatus b/examples/misc/extra_smbstatus
new file mode 100644
index 00000000000..7f77d07d00c
--- /dev/null
+++ b/examples/misc/extra_smbstatus
@@ -0,0 +1,50 @@
+Here's something that Paul Blackman sent me that may be useful:
+
+-------------------
+I created this script to do a few things that smbstatus doesn't at the
+moment. Perhaps you might want to include these. Sorry I haven't
+added things at source level, script was quick&easy.
+
+*******
+#!/bin/csh
+if ($1 == "-p") then
+ smbstatus -p |sort -u
+else if ($1 == "-c") then
+ echo There are `smbstatus -p |sort -u |grep -n -v z |grep -c :` unique smbd processes running.
+ else if ($1 == "-l") then
+ echo `date '+ %d/%m/%y %H:%M:%S'` `smbstatus -p |sort -u |grep -n -v z |grep -c :` >>$2
+else if ($1 == "-cs") then
+ echo There are `smbstatus |awk '$1==share {n++;} END {print n}' share=$2` concurrent connections to share: $2
+else if ($1 == "-csl") then
+ echo `date '+ %d/%m/%y %H:%M:%S'` `smbstatus |awk '$1==share {n++;} END {print n}' share=$2` >>$3
+else
+ echo "'smbstat -c' ==> Count unique smbd processes."
+ echo "'smbstat -p' ==> List unique smbd processes."
+ echo "'smbstat -l logfile' ==> Append a log entry for the number of"
+ echo " concurrent and unique processes to logfile."
+ echo "'smbstat -cs sharename'"
+ echo " ==> Count processes connected to sharename (assumed unique)"
+ echo "'smbstat -csl sharename logfile'"
+ echo " ==> Append a log entry for the number of concurrent"
+ echo " processes connected to sharename (assumed unique)"
+endif
+******
+
+Run this script from cron eg.
+
+0,5,10,15,20,25,30,35,40,50,55 * * * * /usr/local/samba/bin/smbstat -l /usr/local/samba/var/smbdcount.log
+
+and you get a good idea of usage over time.
+
+Cheers,
+~^ MIME OK ^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~
+ o | Paul Blackman ictinus@lake.canberra.edu.au
+ o | Co-operative Research ------------------------
+ o _ | Centre For Freshwater Ecology. Ph. (Aus) 06 2012518
+ -- (") o | University of Canberra, Australia. Fax. " 06 2015038
+ \_|_-- |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | | "Spend a little love and get high"
+ _/ \_ | - Lenny Kravitz
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~ SAMBA Web Pages: http://samba.org/samba/ ~~~~~~~~~~~~~~
+
diff --git a/examples/misc/modify_samba_config.pl b/examples/misc/modify_samba_config.pl
new file mode 100755
index 00000000000..ad958625d66
--- /dev/null
+++ b/examples/misc/modify_samba_config.pl
@@ -0,0 +1,154 @@
+#!/usr/bin/perl
+
+##
+## Simple example of how to implement a '[add|delete] share command' for
+## use with the Windows NT Server Manager. See smb.conf(5) for details
+## on the '[add|delete] share command'
+##
+## Author : Gerald (Jerry) Carter <jerry@samba.org>
+##
+
+use POSIX qw(tmpnam);
+
+##
+## local variables
+##
+my $delete_mode = undef;
+my $add_mode = undef;
+my $tmp_file_name = undef;
+
+
+## check for correct parameters
+if ($#ARGV == 1) {
+ $delete_mode = 1;
+}
+elsif ($#ARGV == 3) {
+ $add_mode = 1;
+}
+else {
+ print "Usage: $0 configfile share [path] [comment]\n";
+ exit -1;
+}
+
+## first param is always the config file
+open (CONFIGFILE, "$ARGV[0]") || die "Unable to open $ARGV[0] for reading!\n";
+
+## FIXME!! Right now we throw away all comments in the file.
+while (<CONFIGFILE>) {
+
+ chomp($_);
+
+ ## eat leading whitespace
+ $_ =~ s/^\s*//;
+
+ ## eat trailing whitespace
+ $_ =~ s/\s*$//;
+
+
+ ## throw away comments
+ next if (($_ =~ /^#/) || ($_ =~ /^;/));
+
+ ## set the current section name for storing the hash
+ if ($_ =~ /^\[.*\]$/) {
+
+ $_ = substr($_, 1, length($_)-2);
+
+ if ( length($_) ) {
+ $section = $_;
+ }
+ else {
+ print "Bad Section Name - no closing ]\n";
+ exit -1;
+ }
+
+ next;
+ }
+
+ ## check for a param = value
+ if ($_ =~ /=/) {
+ ($param, $value) = split (/=/, $_,2);
+ $param =~ s/./\l$&/g;
+ $param =~ s/\s+//g;
+ $value =~ s/^\s+//;
+
+ $config{$section}{$param} = $value;
+
+ next;
+ }
+
+ ## should have a hash of hashes indexed by section name
+}
+close (CONFIGFILE);
+
+##
+## We have the smb.conf in our hash of hashes now.
+## Add or delete
+##
+if ($add_mode) {
+ $config{$ARGV[1]}{'path'} = $ARGV[2];
+ $config{$ARGV[1]}{'comment'} = $ARGV[3];
+}
+elsif ($delete_mode) {
+ delete $config{$ARGV[1]};
+}
+
+##
+## Print the resulting configuration
+##
+#do {
+# $tmp_file_name = tmpnam();
+# print "Using temporary file - $tmp_file_name\n";
+#} while (!sysopen(TMP, $tmp_file_name, O_RDWR|O_CREAT|O_EXCL));
+$tmp_file_name = tmpnam();
+open (TMP, ">$tmp_file_name") || die "Unable to open temporary file for writing!\n";
+
+PrintConfigFile(TMP);
+
+## now overwrite the original config file
+close (TMP);
+system ("cp -pf $ARGV[0] $ARGV[0].bak");
+system ("cp -pf $tmp_file_name $ARGV[0]");
+unlink $tmp_file_name;
+
+
+exit 0;
+
+
+
+
+
+#######################################################################################
+## PrintConfigFile()
+##
+sub PrintConfigFile {
+ my ($output) = @_;
+
+ ## print the file back out, beginning with the global section
+ print $output "#\n# Generated by $0\n#\n";
+
+ PrintSection ($output, 'global', $config{'global'});
+
+ foreach $section (keys %config) {
+
+ if ("$section" ne "global") {
+ print $output "## Section - [$section]\n";
+ PrintSection ($output, $section, $config{$section});
+ }
+ }
+
+ print $output "#\n# end of generated smb.conf\n#\n";
+}
+
+#######################################################################################
+## PrintSection()
+##
+sub PrintSection {
+ my ($outfile, $name, $section) = @_;
+
+ print $outfile "[$name]\n";
+ foreach $param (keys %$section) {
+ print $outfile "\t$param".' 'x(25-length($param)). " = $$section{$param}\n";
+ }
+ print $outfile "\n";
+
+}
diff --git a/examples/misc/swat.pl b/examples/misc/swat.pl
new file mode 100644
index 00000000000..f6414b63497
--- /dev/null
+++ b/examples/misc/swat.pl
@@ -0,0 +1,122 @@
+#! /usr/bin/perl5
+##
+## This is a simple script written by Herb Lewis @ SGI <herb@samba.org>
+## for reporting which parameters are supported by loadparm.c but
+## not by SWAT I just thought it looked fun and might be of interest to others
+## --jerry@samba.org
+##
+## Here is a little info on the usage and output format so you don't have
+## to dig through the code to understand what is printed.
+##
+## Useage: swat.pl [path_to_loadparm.c]
+##
+## The output consists of 4 columns of information
+## Option Name, Global Page, Share Page, Printer Page
+## The section separaters will also be printed (preceded by 16 *) to show
+## which options are grouped in the various sections.
+##
+## If the option name is preceded by an * it means this is a deprecated option.
+## If the option name is preceded by 5 spaces it means this is an alias for the
+## previous option.
+##
+## Under the Global Page, Share Page, and Printer Page columns there will be
+## one of 3 entries, BASIC, ADVANCED, or no. "BASIC" indicates this option will
+## show in the Basic View of that page in SWAT. "ADVANCED" indicates this
+## option will show in the Advanced View of that page in SWAT. "No" indicates
+## that this option is not available on that page in SWAT.
+##
+## Under the Global Page column, if an entry begins with an * it indicates that
+## this is actually specified in Samba as a "service parameter" not a "global
+## parameter" but you can set a default value for this on the Global Page in
+## SWAT.
+##
+## --herb@samba.org
+
+$lastone = "nothing";
+
+if (@ARGV[0]) {
+ $filename = @ARGV[0];
+} else {
+ $filename = "/usr3/samba20/samba/source/param/loadparm.c";
+}
+
+open (INFILE,$filename) || die "unable to open $filename\n";
+while (not eof(INFILE))
+{
+ $_ = <INFILE>;
+ last if ( /^static struct parm_struct parm_table/) ;
+}
+print "Option Name Global Page Share Page Printer Page\n";
+print "---------------------------------------------------------------------";
+while (not eof(INFILE))
+{
+ $_ = <INFILE>;
+ last if (/};/);
+ @fields = split(/,/,$_);
+ next if not ($fields[0] =~ /^.*{"/);
+ $fields[0] =~ s/.*{"//;
+ $fields[0] =~ s/"//;
+ if ($fields[3] eq $lastone) {
+ print " $fields[0]\n";
+ next;
+ }
+ $lastone = $fields[3];
+ $fields[2] =~ s/^\s+//;
+ $fields[2] =~ s/\s+$//;
+ $fields[2] =~ s/}.*$//;
+ $fields[6] =~ s/^\s+//;
+ $fields[6] =~ s/\s+$//;
+ $fields[6] =~ s/}.*$//;
+ if ($fields[2] =~ /P_SEPARATOR/) {
+ print "\n****************$fields[0]\n";
+ next;
+ }
+ else {
+ if ($fields[6] =~ /FLAG_DEPRECATED/) {
+ print "*$fields[0]".' 'x(31-length($fields[0]));
+ }
+ else {
+ print "$fields[0]".' 'x(32-length($fields[0]));
+ }
+ }
+ if (($fields[2] =~ /P_GLOBAL/) || ($fields[6] =~ /FLAG_GLOBAL/)) {
+ if ($fields[6] =~ /FLAG_GLOBAL/) {
+ print "*";
+ }
+ else {
+ print " ";
+ }
+ if ($fields[6] =~ /FLAG_BASIC/) {
+ print "BASIC ";
+ }
+ else {
+ print "ADVANCED ";
+ }
+ }
+ else {
+ print " no ";
+ }
+ if ($fields[6] =~ /FLAG_SHARE/) {
+ if ($fields[6] =~ /FLAG_BASIC/) {
+ print "BASIC ";
+ }
+ else {
+ print "ADVANCED ";
+ }
+ }
+ else {
+ print "no ";
+ }
+ if ($fields[6] =~ /FLAG_PRINT/) {
+ if ($fields[6] =~ /FLAG_BASIC/) {
+ print "BASIC";
+ }
+ else {
+ print "ADVANCED";
+ }
+ }
+ else {
+ print "no";
+ }
+ print "\n";
+}
diff --git a/examples/misc/wall.perl b/examples/misc/wall.perl
new file mode 100644
index 00000000000..9303658ce14
--- /dev/null
+++ b/examples/misc/wall.perl
@@ -0,0 +1,69 @@
+#!/usr/local/bin/perl
+#
+#@(#) smb-wall.pl Description:
+#@(#) A perl script which allows you to announce whatever you choose to
+#@(#) every PC client currently connected to a Samba Server...
+#@(#) ...using "smbclient -M" message to winpopup service.
+#@(#) Default usage is to message every connected PC.
+#@(#) Alternate usage is to message every pc on the argument list.
+#@(#) Hacked up by Keith Farrar <farrar@parc.xerox.com>
+#
+# Cleanup and corrections by
+# Michal Jaegermann <michal@ellpspace.math.ualberta.ca>
+# Message to send can be now also fed (quietly) from stdin; a pipe will do.
+#=============================================================================
+
+$smbstatus = "/usr/local/bin/smbstatus";
+$smbshout = "/usr/local/bin/smbclient -M";
+
+if (@ARGV) {
+ @clients = @ARGV;
+ undef @ARGV;
+}
+else { # no clients specified explicitly
+ open(PCLIST, "$smbstatus |") || die "$smbstatus failed!.\n$!\n";
+ while(<PCLIST>) {
+ last if /^Locked files:/;
+ split(' ', $_, 6);
+ # do not accept this line if less then six fields
+ next unless $_[5];
+ # if you have A LOT of clients you may speed things up by
+ # checking pid - no need to look further if this pid was already
+ # seen; left as an exercise :-)
+ $client = $_[4];
+ next unless $client =~ /^\w+\./; # expect 'dot' in a client name
+ next if grep($_ eq $client, @clients); # we want this name once
+ push(@clients, $client);
+ }
+ close(PCLIST);
+}
+
+if (-t) {
+ print <<'EOT';
+
+Enter message for Samba clients of this host
+(terminated with single '.' or end of file):
+EOT
+
+ while (<>) {
+ last if /^\.$/;
+ push(@message, $_);
+ }
+}
+else { # keep quiet and read message from stdin
+ @message = <>;
+}
+
+foreach(@clients) {
+## print "To $_:\n";
+ if (open(SENDMSG,"|$smbshout $_")) {
+ print SENDMSG @message;
+ close(SENDMSG);
+ }
+ else {
+ warn "Cannot notify $_ with $smbshout:\n$!\n";
+ }
+}
+
+exit 0;
+
diff --git a/examples/nss/nss_winbind.c b/examples/nss/nss_winbind.c
new file mode 100644
index 00000000000..968cc7adddc
--- /dev/null
+++ b/examples/nss/nss_winbind.c
@@ -0,0 +1,422 @@
+/*
+ nss sample code for extended winbindd functionality
+
+ Copyright (C) Andrew Tridgell (tridge@samba.org)
+
+ you are free to use this code in any way you see fit, including
+ without restriction, using this code in your own products. You do
+ not need to give any attribution.
+*/
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <nss.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "nss_winbind.h"
+
+/*
+ find a function in the nss library
+*/
+static void *find_fn(struct nss_state *nss, const char *name)
+{
+ void *res;
+ char *s = NULL;
+
+ asprintf(&s, "_nss_%s_%s", nss->nss_name, name);
+ if (!s) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ res = dlsym(nss->dl_handle, s);
+ free(s);
+ if (!res) {
+ errno = ENOENT;
+ return NULL;
+ }
+ return res;
+}
+
+/*
+ establish a link to the nss library
+ Return 0 on success and -1 on error
+*/
+int nss_open(struct nss_state *nss, const char *nss_path)
+{
+ char *p;
+ p = strrchr(nss_path, '_');
+ if (!p) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ nss->nss_name = strdup(p+1);
+ p = strchr(nss->nss_name, '.');
+ if (p) *p = 0;
+
+ nss->dl_handle = dlopen(nss_path, RTLD_LAZY);
+ if (!nss->dl_handle) {
+ free(nss->nss_name);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ close and cleanup a nss state
+*/
+void nss_close(struct nss_state *nss)
+{
+ free(nss->nss_name);
+ dlclose(nss->dl_handle);
+}
+
+/*
+ make a getpwnam call.
+ Return 0 on success and -1 on error
+*/
+int nss_getpwent(struct nss_state *nss, struct passwd *pwd)
+{
+ enum nss_status (*_nss_getpwent_r)(struct passwd *, char *,
+ size_t , int *);
+ enum nss_status status;
+ int nss_errno = 0;
+
+ _nss_getpwent_r = find_fn(nss, "getpwent_r");
+
+ if (!_nss_getpwent_r) {
+ return -1;
+ }
+
+ status = _nss_getpwent_r(pwd, nss->pwnam_buf, sizeof(nss->pwnam_buf),
+ &nss_errno);
+ if (status == NSS_STATUS_NOTFOUND) {
+ errno = ENOENT;
+ return -1;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ errno = nss_errno;
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ make a setpwent call.
+ Return 0 on success and -1 on error
+*/
+int nss_setpwent(struct nss_state *nss)
+{
+ enum nss_status (*_nss_setpwent)(void) = find_fn(nss, "setpwent");
+ enum nss_status status;
+ if (!_nss_setpwent) {
+ return -1;
+ }
+ status = _nss_setpwent();
+ if (status != NSS_STATUS_SUCCESS) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ make a endpwent call.
+ Return 0 on success and -1 on error
+*/
+int nss_endpwent(struct nss_state *nss)
+{
+ enum nss_status (*_nss_endpwent)(void) = find_fn(nss, "endpwent");
+ enum nss_status status;
+ if (!_nss_endpwent) {
+ return -1;
+ }
+ status = _nss_endpwent();
+ if (status != NSS_STATUS_SUCCESS) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+
+/*
+ convert a name to a SID
+ caller frees
+ Return 0 on success and -1 on error
+*/
+int nss_nametosid(struct nss_state *nss, const char *name, char **sid)
+{
+ enum nss_status (*_nss_nametosid)(const char *, char **, char *,
+ size_t, int *);
+ enum nss_status status;
+ int nss_errno = 0;
+ char buf[200];
+
+ _nss_nametosid = find_fn(nss, "nametosid");
+
+ if (!_nss_nametosid) {
+ return -1;
+ }
+
+ status = _nss_nametosid(name, sid, buf, sizeof(buf), &nss_errno);
+ if (status == NSS_STATUS_NOTFOUND) {
+ errno = ENOENT;
+ return -1;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ errno = nss_errno;
+ return -1;
+ }
+
+ *sid = strdup(*sid);
+
+ return 0;
+}
+
+/*
+ convert a SID to a name
+ caller frees
+ Return 0 on success and -1 on error
+*/
+int nss_sidtoname(struct nss_state *nss, const char *sid, char **name)
+{
+ enum nss_status (*_nss_sidtoname)(const char *, char **, char *,
+ size_t, int *);
+ enum nss_status status;
+ int nss_errno = 0;
+ char buf[200];
+
+ _nss_sidtoname = find_fn(nss, "sidtoname");
+
+ if (!_nss_sidtoname) {
+ return -1;
+ }
+
+ status = _nss_sidtoname(sid, name, buf, sizeof(buf), &nss_errno);
+ if (status == NSS_STATUS_NOTFOUND) {
+ errno = ENOENT;
+ return -1;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ errno = nss_errno;
+ return -1;
+ }
+
+ *name = strdup(*name);
+
+ return 0;
+}
+
+/*
+ return a list of group SIDs for a user SID
+ the returned list is NULL terminated
+ Return 0 on success and -1 on error
+*/
+int nss_getusersids(struct nss_state *nss, const char *user_sid, char ***sids)
+{
+ enum nss_status (*_nss_getusersids)(const char *, char **, int *,
+ char *, size_t, int *);
+ enum nss_status status;
+ int nss_errno = 0;
+ char *s;
+ int i, num_groups = 0;
+ unsigned bufsize = 10;
+ char *buf;
+
+ _nss_getusersids = find_fn(nss, "getusersids");
+
+ if (!_nss_getusersids) {
+ return -1;
+ }
+
+again:
+ buf = malloc(bufsize);
+ if (!buf) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ status = _nss_getusersids(user_sid, &s, &num_groups, buf, bufsize,
+ &nss_errno);
+
+ if (status == NSS_STATUS_NOTFOUND) {
+ errno = ENOENT;
+ free(buf);
+ return -1;
+ }
+
+ if (status == NSS_STATUS_TRYAGAIN) {
+ bufsize *= 2;
+ free(buf);
+ goto again;
+ }
+
+ if (status != NSS_STATUS_SUCCESS) {
+ free(buf);
+ errno = nss_errno;
+ return -1;
+ }
+
+ if (num_groups == 0) {
+ free(buf);
+ return 0;
+ }
+
+ *sids = (char **)malloc(sizeof(char *) * (num_groups+1));
+ if (! *sids) {
+ errno = ENOMEM;
+ free(buf);
+ return -1;
+ }
+
+ for (i=0;i<num_groups;i++) {
+ (*sids)[i] = strdup(s);
+ s += strlen(s) + 1;
+ }
+ (*sids)[i] = NULL;
+
+ free(buf);
+
+ return 0;
+}
+
+/*
+ convert a sid to a uid
+ Return 0 on success and -1 on error
+*/
+int nss_sidtouid(struct nss_state *nss, const char *sid, uid_t *uid)
+{
+ enum nss_status (*_nss_sidtouid)(const char*, uid_t *, int*);
+
+ enum nss_status status;
+ int nss_errno = 0;
+
+ _nss_sidtouid = find_fn(nss, "sidtouid");
+
+ if (!_nss_sidtouid) {
+ return -1;
+ }
+
+ status = _nss_sidtouid(sid, uid, &nss_errno);
+
+ if (status == NSS_STATUS_NOTFOUND) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ if (status != NSS_STATUS_SUCCESS) {
+ errno = nss_errno;
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ convert a sid to a gid
+ Return 0 on success and -1 on error
+*/
+int nss_sidtogid(struct nss_state *nss, const char *sid, gid_t *gid)
+{
+ enum nss_status (*_nss_sidtogid)(const char*, gid_t *, int*);
+
+ enum nss_status status;
+ int nss_errno = 0;
+
+ _nss_sidtogid = find_fn(nss, "sidtogid");
+
+ if (!_nss_sidtogid) {
+ return -1;
+ }
+
+ status = _nss_sidtogid(sid, gid, &nss_errno);
+
+ if (status == NSS_STATUS_NOTFOUND) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ if (status != NSS_STATUS_SUCCESS) {
+ errno = nss_errno;
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ convert a uid to a sid
+ caller frees
+ Return 0 on success and -1 on error
+*/
+int nss_uidtosid(struct nss_state *nss, uid_t uid, char **sid)
+{
+ enum nss_status (*_nss_uidtosid)(uid_t, char **, char *,
+ size_t, int *);
+ enum nss_status status;
+ int nss_errno = 0;
+ char buf[200];
+
+ _nss_uidtosid = find_fn(nss, "uidtosid");
+
+ if (!_nss_uidtosid) {
+ return -1;
+ }
+
+ status = _nss_uidtosid(uid, sid, buf, sizeof(buf), &nss_errno);
+ if (status == NSS_STATUS_NOTFOUND) {
+ errno = ENOENT;
+ return -1;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ errno = nss_errno;
+ return -1;
+ }
+
+ *sid = strdup(*sid);
+
+ return 0;
+}
+
+/*
+ convert a gid to a sid
+ caller frees
+ Return 0 on success and -1 on error
+*/
+int nss_gidtosid(struct nss_state *nss, gid_t gid, char **sid)
+{
+ enum nss_status (*_nss_gidtosid)(gid_t, char **, char *,
+ size_t, int *);
+ enum nss_status status;
+ int nss_errno = 0;
+ char buf[200];
+
+ _nss_gidtosid = find_fn(nss, "gidtosid");
+
+ if (!_nss_gidtosid) {
+ return -1;
+ }
+
+ status = _nss_gidtosid(gid, sid, buf, sizeof(buf), &nss_errno);
+ if (status == NSS_STATUS_NOTFOUND) {
+ errno = ENOENT;
+ return -1;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ errno = nss_errno;
+ return -1;
+ }
+
+ *sid = strdup(*sid);
+
+ return 0;
+}
+
diff --git a/examples/nss/nss_winbind.h b/examples/nss/nss_winbind.h
new file mode 100644
index 00000000000..5a124a5a8ec
--- /dev/null
+++ b/examples/nss/nss_winbind.h
@@ -0,0 +1,97 @@
+/*
+ nss sample code for extended winbindd functionality
+
+ Copyright (C) Andrew Tridgell (tridge@samba.org)
+ Copyright (C) Volker Lendecke (vl@samba.org)
+
+ you are free to use this code in any way you see fit, including
+ without restriction, using this code in your own products. You do
+ not need to give any attribution.
+*/
+
+#define _GNU_SOURCE
+
+#include <pwd.h>
+#include <grp.h>
+
+struct nss_state {
+ void *dl_handle;
+ char *nss_name;
+ char pwnam_buf[512];
+};
+
+/*
+ establish a link to the nss library
+ Return 0 on success and -1 on error
+*/
+int nss_open(struct nss_state *nss, const char *nss_path);
+
+/*
+ close and cleanup a nss state
+*/
+void nss_close(struct nss_state *nss);
+
+/*
+ make a getpwnam call.
+ Return 0 on success and -1 on error
+*/
+int nss_getpwent(struct nss_state *nss, struct passwd *pwd);
+
+/*
+ make a setpwent call.
+ Return 0 on success and -1 on error
+*/
+int nss_setpwent(struct nss_state *nss);
+
+/*
+ make a endpwent call.
+ Return 0 on success and -1 on error
+*/
+int nss_endpwent(struct nss_state *nss);
+
+/*
+ convert a name to a SID
+ caller frees
+ Return 0 on success and -1 on error
+*/
+int nss_nametosid(struct nss_state *nss, const char *name, char **sid);
+
+/*
+ convert a SID to a name
+ caller frees
+ Return 0 on success and -1 on error
+*/
+int nss_sidtoname(struct nss_state *nss, const char *sid, char **name);
+
+/*
+ return a list of group SIDs for a user SID
+ the returned list is NULL terminated
+ Return 0 on success and -1 on error
+*/
+int nss_getusersids(struct nss_state *nss, const char *user_sid, char ***sids);
+
+/*
+ convert a sid to a uid
+ Return 0 on success and -1 on error
+*/
+int nss_sidtouid(struct nss_state *nss, const char *sid, uid_t *uid);
+
+/*
+ convert a sid to a gid
+ Return 0 on success and -1 on error
+*/
+int nss_sidtogid(struct nss_state *nss, const char *sid, gid_t *gid);
+
+/*
+ convert a uid to a sid
+ caller frees
+ Return 0 on success and -1 on error
+*/
+int nss_uidtosid(struct nss_state *nss, uid_t uid, char **sid);
+
+/*
+ convert a gid to a sid
+ caller frees
+ Return 0 on success and -1 on error
+*/
+int nss_gidtosid(struct nss_state *nss, gid_t gid, char **sid);
diff --git a/examples/nss/wbtest.c b/examples/nss/wbtest.c
new file mode 100644
index 00000000000..14265bd54c1
--- /dev/null
+++ b/examples/nss/wbtest.c
@@ -0,0 +1,111 @@
+/*
+ nss sample code for extended winbindd functionality
+
+ Copyright (C) Andrew Tridgell (tridge@samba.org)
+
+ you are free to use this code in any way you see fit, including
+ without restriction, using this code in your own products. You do
+ not need to give any attribution.
+*/
+
+/*
+ compile like this:
+
+ cc -o wbtest wbtest.c nss_winbind.c -ldl
+
+ and run like this:
+
+ ./wbtest /lib/libnss_winbind.so
+*/
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <nss.h>
+#include <dlfcn.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "nss_winbind.h"
+
+static int nss_test_users(struct nss_state *nss)
+{
+ struct passwd pwd;
+
+ if (nss_setpwent(nss) != 0) {
+ perror("setpwent");
+ return -1;
+ }
+
+ /* loop over all users */
+ while ((nss_getpwent(nss, &pwd) == 0)) {
+ char *sid, **group_sids, *name2;
+ int i;
+
+ printf("User %s\n", pwd.pw_name);
+ if (nss_nametosid(nss, pwd.pw_name, &sid) != 0) {
+ perror("nametosid");
+ return -1;
+ }
+ printf("\tSID %s\n", sid);
+
+ if (nss_sidtoname(nss, sid, &name2) != 0) {
+ perror("sidtoname");
+ return -1;
+ }
+ printf("\tSID->name %s\n", name2);
+
+ if (nss_getusersids(nss, sid, &group_sids) != 0) {
+ perror("getusersids");
+ return -1;
+ }
+
+ printf("\tGroups:\n");
+ for (i=0; group_sids[i]; i++) {
+ printf("\t\t%s\n", group_sids[i]);
+ free(group_sids[i]);
+ }
+
+ free(sid);
+ free(name2);
+ free(group_sids);
+ }
+
+
+ if (nss_endpwent(nss) != 0) {
+ perror("endpwent");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ main program. It lists all users, listing user SIDs for each user
+ */
+int main(int argc, char *argv[])
+{
+ struct nss_state nss;
+ const char *so_path = "/lib/libnss_winbind.so";
+ int ret;
+
+ if (argc > 1) {
+ so_path = argv[1];
+ }
+
+ if (nss_open(&nss, so_path) != 0) {
+ perror("nss_open");
+ exit(1);
+ }
+
+ ret = nss_test_users(&nss);
+
+ nss_close(&nss);
+
+ return ret;
+}
diff --git a/examples/ntlogon/README b/examples/ntlogon/README
new file mode 100644
index 00000000000..e33c565d717
--- /dev/null
+++ b/examples/ntlogon/README
@@ -0,0 +1,160 @@
+ntlogon.py v0.8b Copyright 2002 by Timothy (rhacer) Grant
+This programme is released under the terms of the GNU Public License
+This programme has NO WARRANTY of any kind, use at your own risk.
+
+===================
+CHANGES SINCE v0.7b
+===================
+included patch that made machine name %m a macro substitution value.
+Thanks to: Nick Lopez <kimo_sabe@atdot.org>
+
+==================
+CHANGES SINCE v0.6
+==================
+PLEASE NOT THAT I AM CONSIDERING THIS A BETA UNTIL I GET SOME FEEDBACK
+FROM USERS ON WHETHER THE MACRO FEATURE WORKS FOR THEM.
+
+added the ability to define substitution macros: see the useage section
+
+removed the large docstring from the file and moved the information to
+the USEAGE section of this file
+
+cleaned up the code and made more flexible
+
+improved the code comments
+
+==================
+CHANGES SINCE v0.5
+==================
+added a -v --version switch
+
+added a --pause switch which will put a pause statement between each
+non-blank line of the script.
+
+===============
+A PERSONAL NOTE
+===============
+When I originally posted v0.5 on Freshmeat, I really expected *some*
+feedback. To date this little script has been downloaded over 700 times, but
+absolutely nobody has sent me an e-mail to tell me if it is useful, or if
+it is absolutely the stupidest waste of bandwidth they have ever seen.
+I'd really love to know if even one person other than me and the other techs
+here at Avalon have found it useful.
+
+Thanks.
+ rhacer (rhacer@craigelachie.org)
+
+September 27, 2000
+Thanks to those that did respond to my plea! I'd still love to hear from
+any other users!
+
+============
+INTRODUCTION
+============
+As my experience with Linux and Samba increased, I had no trouble whipping up
+a custom Perl, Python or Bash script to create Samba Logon Scripts, but I
+noticed that I changed things from place to place, and that my users had *no*
+chance of ever figuring out how to modify those scripts themselves.
+
+In an attempt to create a company wide standard that both my co-workers and my
+customers could *easily* modify I hit upon the scheme I used here.
+
+I settled on an INI file feel, because most who have experience with Win boxes
+have some concept of INI files.
+
+============
+INSTALLATION
+============
+The distribution archive contains three files:
+
+README This file
+ntlogon.py The actual Python script (place in /usr/local/samba/bin)
+ntlogon.conf A sample configuration file (place in /etc)
+
+This script was created using Python v1.5.2, and I believe it uses only
+standard libraries.
+
+Your smb.conf file needs to contain a netlogon section similar to the
+following (These settings *are not* normal on a RH box. These settings
+are all based on the excellent book that I learned Samba from: Samba
+Integrating Unix and Windows by John D. Blair. It is somewhat out of
+date now, but that's the history of the strange file locations):
+
+[netlogon]
+ path = /usr/local/samba/netlogon
+ writeable = no
+ guest ok = no
+ root preexec = /usr/local/samba/bin/ntlogon --user=%U --os=%m
+ root postexec = rm /usr/local/samba/netlogon/%U.bat
+
+======
+USEAGE
+======
+PLEASE SEE NTLOGON.CONF FOR A DETAILED CONFIGURATION EXAMPLE
+
+This programme will build a Windows NT logon script for users connecting
+to a Samba server. Samba macros that are curently understood:
+
+ %U user name
+ %G group name
+ %a machine architecture
+ %m machine netbios name
+
+This programme reads a configuration that looks strikingly similar to both
+the Samba configuration file, and a DOS "boot menu" AUTOEXEC.BAT file.
+
+The default file name is /etc/ntlogon.conf (though I really think it ought
+to be called ntlogon.batch!) You may change the filename by using the -f
+or --templatefile startup option.
+
+The default netlogon directory is /usr/local/samba/netlogon though this
+can be changed with the -d or --dir startup option.
+
+The default batch file name generated is username.bat if no username is
+specified the default value is logon.bat (e.g., if --user=fred is specified
+on the command line then the logon script generated will be stored in
+fred.bat)
+
+Use the --debug option to print the logon script to screen instead of the
+output file
+
+Use the --pause option to place a pause statement between each line of the
+script to assist in debugging a logon script.
+
+The file is divided into sections that have headers in square brackets
+
+[Global]
+[User-username]
+[Group-groupname]
+[OS-osname]
+
+The file may also contain user defined substitution macros. They are
+defined by placing the macro name on the left side of an equal sign,
+and the substitution text on the right side of the equal sign. They
+are also case sensitive:
+
+MAINDRIVE = G:
+USERDRIVE = U:
+SERVERNAME = myservername
+
+They are referenced by prepending a "%" sign to the variable name:
+
+NET USE %MAINDRIVE \\\\servername\\mainshare /YES
+NET USE %USERDRIVE \\\\%SERVERNAME\\usershare /YES
+
+==============
+SPECIAL THANKS
+==============
+Nick Lopez <kimo_sabe@atdot.org> for the net bios name patch.
+
+===================
+CONTACT INFORMATION
+===================
+Author : Timothy (rhacer) Grant
+
+I can be reached at tjg@craigelachie.org
+ntlogon website www.craigelachie.org/rhacer/ntlogon
+
+Please feel free to contact me with any suggestions, improvements, bugs you
+might find.
+
diff --git a/examples/ntlogon/ntlogon.conf b/examples/ntlogon/ntlogon.conf
new file mode 100644
index 00000000000..e1573a6118f
--- /dev/null
+++ b/examples/ntlogon/ntlogon.conf
@@ -0,0 +1,44 @@
+# Everything in the Global section applies to all users logging on to the
+# network
+[Global]
+
+#Some substitution macro definitions
+MAINDRIVE = G:
+USERDRIVE = U:
+SERVERNAME = myservername
+
+@ECHO "Welcome to our network!!!"
+NET TIME \\servername /SET /YES
+NET USE %MAINDRIVE \\%SERVERNAME\globalshare /YES
+
+# Map the private user area in the global section so we don't have to
+# create individual user entries for each user!
+NET USE %USERDRIVE \\servername\%U /YES
+
+# Group entries, User entries and OS entries each start with the
+# keyword followed by a dash followed by--appropriately enough the Group
+# name, the User name, or the OS name.
+[Group-admin]
+@ECHO "Welcome administrators!"
+NET USE G: \\servername\adminshare1 /YES
+NET USE I: \\servername\adminshare2 /YES
+
+[Group-peons]
+@ECHO "Be grateful we let you use computers!"
+NET USE G: \\servername\peonshare1 /YES
+
+[Group-hackers]
+@ECHO "What can I do for you today great one?"
+NET USE G: \\servername\hackershare1 /YES
+NET USE I: \\servername\adminshare2 /YES
+
+[User-fred]
+@ECHO "Hello there Fred!"
+NET USE F: \\servername\fredsspecialshare /YES
+
+[OS-WfWg]
+@ECHO "Time to upgrade isn't it?"
+
+# End configuration file
+
+X = Will this break?
diff --git a/examples/ntlogon/ntlogon.py b/examples/ntlogon/ntlogon.py
new file mode 100755
index 00000000000..ba46ba8ffcf
--- /dev/null
+++ b/examples/ntlogon/ntlogon.py
@@ -0,0 +1,376 @@
+#!/usr/bin/env python
+"""
+ntlogon.py written by Timothy (rhacer) Grant
+
+Copyright 1999 - 2002 by Timothy Grant
+
+is distributed under the terms of the GNU Public License.
+
+The format for the configuration file is as follows:
+
+While there is some room for confusion, we attempt to process things in
+order of specificity: Global first, Group second, User third, OS Type
+forth. This order can be debated forever, but it seems to make the most
+sense.
+
+# Everything in the Global section applies to all users logging on to the
+# network
+[Global]
+@ECHO "Welcome to our network!!!"
+NET TIME \\\\servername /SET /YES
+NET USE F: \\\\servername\\globalshare /YES
+
+# Map the private user area in the global section so we don't have to
+# create individual user entries for each user!
+NET USE U: \\\\servername\\%U /YES
+
+# Group entries, User entries and OS entries each start with the
+# keyword followed by a dash followed by--appropriately enough the Group
+# name, the User name, or the OS name.
+[Group-admin]
+@ECHO "Welcome administrators!"
+NET USE G: \\\\servername\\adminshare1 /YES
+NET USE I: \\\\servername\\adminshare2 /YES
+
+[Group-peons]
+@ECHO "Be grateful we let you use computers!"
+NET USE G: \\\\servername\\peonshare1 /YES
+
+[Group-hackers]
+@ECHO "What can I do for you today great one?"
+NET USE G: \\\\servername\\hackershare1 /YES
+NET USE I: \\\\servername\\adminshare2 /YES
+
+[User-fred]
+@ECHO "Hello there Fred!"
+NET USE F: \\\\servername\\fredsspecialshare /YES
+
+[OS-WfWg]
+@ECHO "Time to upgrade it?"
+
+# End configuration file
+
+usage: ntlogon [-g | --group=groupname]
+ [-u | --user=username]
+ [-o | --os=osname]
+ [-m | --machine=netbiosname]
+ [-f | --templatefile=filename]
+ [-d | --dir=netlogon directory]
+ [-v | --version]
+ [-h | --help]
+ [--pause]
+ [--debug]
+"""
+#
+#" This quote mark is an artifact of the inability of my editor to
+# correctly colour code anything after the triple-quoted docstring.
+# if your editor does not have this flaw, feel free to remove it.
+
+
+import sys
+import getopt
+import re
+import string
+import os
+
+version = "ntlogon.py v0.8"
+
+def buildScript(buf, sections, group, user, ostype, machine, debug, pause):
+ """
+ buildScript() Takes the contents of the template file and builds
+ a DOS batch file to be executed as an NT logon script. It does this
+ by determining which sections of the configuration file should be included
+ and creating a list object that contains each line contained in each
+ included section. The list object is then returned to the calling
+ routine.
+
+ All comments (#) are removed. A REM is inserted to show
+ which section of the configuration file each line comes from.
+ We leave blanklines as they are sometimes useful for debugging
+
+ We also replace all of the Samba macros (e.g., %U, %G, %a, %m) with their
+ expanded versions which have been passed to us by smbd
+ """
+ hdrstring = ''
+ script = []
+
+ #
+ # These are the Samba macros that we currently know about.
+ # any user defined macros will also be added to this dictionary.
+ # We do not store the % sign as part of the macro name.
+ # The replace routine will prepend the % sign to all possible
+ # replacements.
+ #
+ macros = {
+ 'U': user,
+ 'G': group,
+ 'a': ostype,
+ 'm': machine
+ }
+
+ #
+ # Process each section defined in the list sections
+ #
+ for s in sections:
+ # print 'searching for: ' + s
+
+ idx = 0
+
+ while idx < len(buf):
+ ln = buf[idx]
+
+ #
+ # We need to set up a regex for each possible section we
+ # know about. This is slightly complicated due to the fact
+ # that section headers contain user defined text.
+ #
+ if s == 'Global':
+ hdrstring = '\[ *' + s + ' *\]'
+ elif s == 'Group':
+ hdrstring = '\[ *' + s + ' *- *' + group + ' *\]'
+ elif s == 'User':
+ hdrstring = '\[ *' + s + ' *- *' + user + ' *\]'
+ elif s == 'OS':
+ hdrstring = '\[ *' + s + ' *- *' + ostype + ' *\]'
+ elif s == 'Machine':
+ hdrstring = '\[ *' + s + ' *- *' + machine + ' *\]'
+
+ #
+ # See if we have found a section header
+ #
+ if re.search(r'(?i)' + hdrstring, ln):
+ idx = idx + 1 # increment the counter to move to the next
+ # line.
+
+ x = re.match(r'([^#\r\n]*)', ln) # Determine the section
+ # name and strip out CR/LF
+ # and comment information
+
+ if debug:
+ print 'rem ' + x.group(1) + ' commands'
+ else:
+ # create the rem at the beginning of each section of the
+ # logon script.
+ script.append('rem ' + x.group(1) + ' commands')
+
+ #
+ # process each line until we have found another section
+ # header
+ #
+ while not re.search(r'.*\[.*\].*', buf[idx]):
+
+ #
+ # strip comments and line endings
+ #
+ x = re.match(r'([^#\r\n]*)', buf[idx])
+
+ if string.strip(x.group(1)) != '' :
+ # if there is still content after stripping comments and
+ # line endings then this is a line to process
+
+ line = x.group(1)
+
+ #
+ # Check to see if this is a macro definition line
+ #
+ vardef = re.match(r'(.*)=(.*)', line)
+
+ if vardef:
+ varname = string.strip(vardef.group(1)) # Strip leading and
+ varsub = string.strip(vardef.group(2)) # and trailing spaces
+
+ if varname == '':
+ print "Error: No substition name specified line: %d" % idx
+ sys.exit(1)
+
+ if varsub == '':
+ print "Error: No substitution text provided line: %d" % idx
+ sys.exit(1)
+
+ if macros.has_key(varname):
+ print "Warning: macro %s redefined line: %d" % (varname, idx)
+
+ macros[varname] = varsub
+ idx = idx + 1
+ continue
+
+ #
+ # Replace all the macros that we currently
+ # know about.
+ #
+ # Iterate over the dictionary that contains all known
+ # macro substitutions.
+ #
+ # We test for a macro name by prepending % to each dictionary
+ # key.
+ #
+ for varname in macros.keys():
+ line = re.sub(r'%' + varname + r'(\W)',
+ macros[varname] + r'\1', line)
+
+ if debug:
+ print line
+ if pause:
+ print 'pause'
+ else:
+ script.append(line)
+
+ idx = idx + 1
+
+ if idx == len(buf):
+ break # if we have reached the end of the file
+ # stop processing.
+
+ idx = idx + 1 # increment the line counter
+
+ if debug:
+ print ''
+ else:
+ script.append('')
+
+ return script
+
+# End buildScript()
+
+def run():
+ """
+ run() everything starts here. The main routine reads the command line
+ arguments, opens and reads the configuration file.
+ """
+ configfile = '/etc/ntlogon.conf' # Default configuration file
+ group = '' # Default group
+ user = '' # Default user
+ ostype = '' # Default os
+ machine = '' # Default machine type
+ outfile = 'logon.bat' # Default batch file name
+ # this file name WILL take on the form
+ # username.bat if a username is specified
+ debug = 0 # Default debugging mode
+ pause = 0 # Default pause mode
+ outdir = '/usr/local/samba/netlogon/' # Default netlogon directory
+
+ sections = ['Global', 'Machine', 'OS', 'Group', 'User'] # Currently supported
+ # configuration file
+ # sections
+
+ options, args = getopt.getopt(sys.argv[1:], 'd:f:g:ho:u:m:v',
+ ['templatefile=',
+ 'group=',
+ 'help',
+ 'os=',
+ 'user=',
+ 'machine=',
+ 'dir=',
+ 'version',
+ 'pause',
+ 'debug'])
+
+ #
+ # Process the command line arguments
+ #
+ for i in options:
+ # template file to process
+ if (i[0] == '-f') or (i[0] == '--templatefile'):
+ configfile = i[1]
+ # print 'configfile = ' + configfile
+
+ # define the group to be used
+ elif (i[0] == '-g') or (i[0] == '--group'):
+ group = i[1]
+ # print 'group = ' + group
+
+ # define the os type
+ elif (i[0] == '-o') or (i[0] == '--os'):
+ ostype = i[1]
+ # print 'os = ' + os
+
+ # define the user
+ elif (i[0] == '-u') or (i[0] == '--user'):
+ user = i[1]
+ outfile = user + '.bat' # Setup the output file name
+ # print 'user = ' + user
+
+ # define the machine
+ elif (i[0] == '-m') or (i[0] == '--machine'):
+ machine = i[1]
+
+ # define the netlogon directory
+ elif (i[0] == '-d') or (i[0] == '--dir'):
+ outdir = i[1]
+ # print 'outdir = ' + outdir
+
+ # if we are asked to turn on debug info, do so.
+ elif (i[0] == '--debug'):
+ debug = 1
+ # print 'debug = ' + debug
+
+ # if we are asked to turn on the automatic pause functionality, do so
+ elif (i[0] == '--pause'):
+ pause = 1
+ # print 'pause = ' + pause
+
+ # if we are asked for the version number, print it.
+ elif (i[0] == '-v') or (i[0] == '--version'):
+ print version
+ sys.exit(0)
+
+ # if we are asked for help print the docstring.
+ elif (i[0] == '-h') or (i[0] == '--help'):
+ print __doc__
+ sys.exit(0)
+
+ #
+ # open the configuration file
+ #
+ try:
+ iFile = open(configfile, 'r')
+ except IOError:
+ print 'Unable to open configuration file: ' + configfile
+ sys.exit(1)
+
+
+ #
+ # open the output file
+ #
+ if not debug:
+ try:
+ oFile = open(outdir + outfile, 'w')
+ except IOError:
+ print 'Unable to open logon script file: ' + outdir + outfile
+ sys.exit(1)
+
+ buf = iFile.readlines() # read in the entire configuration file
+
+ #
+ # call the script building routine
+ #
+ script = buildScript(buf, sections, group, user, ostype, machine, debug, pause)
+
+ #
+ # write out the script file
+ #
+ if not debug:
+ for ln in script:
+ oFile.write(ln + '\r\n')
+ if pause:
+ if string.strip(ln) != '': # Because whitespace
+ oFile.write('pause' + '\r\n') # is a useful tool, we
+ # don't put pauses after
+ # an empty line.
+
+
+# End run()
+
+#
+# immediate-mode commands, for drag-and-drop or execfile() execution
+#
+if __name__ == '__main__':
+ run()
+else:
+ print "Module ntlogon.py imported."
+ print "To run, type: ntlogon.run()"
+ print "To reload after changes to the source, type: reload(ntlogon)"
+
+#
+# End NTLogon.py
+#
diff --git a/examples/pdb/Makefile b/examples/pdb/Makefile
new file mode 100644
index 00000000000..a53cd5d5e2d
--- /dev/null
+++ b/examples/pdb/Makefile
@@ -0,0 +1,31 @@
+# Makefile for samba-pdb examples
+# Variables
+
+CC = gcc
+LIBTOOL = libtool
+
+SAMBA_SRC = ../../source
+SAMBA_INCL = ../../source/include
+UBIQX_SRC = ../../source/ubiqx
+SMBWR_SRC = ../../source/smbwrapper
+CFLAGS = -I$(SAMBA_SRC) -I$(SAMBA_INCL) -I$(UBIQX_SRC) -I$(SMBWR_SRC) -Wall -g -I/usr/include/heimdal -fPIC
+PDB_OBJS = pdb_test.so
+
+# Default target
+
+default: $(PDB_OBJS)
+
+# Pattern rules
+
+%.so: %.lo
+ $(LIBTOOL) $(CC) -shared -o $@ $< $(LDFLAGS)
+
+%.lo: %.c
+ $(LIBTOOL) $(CC) $(CPPFLAGS) $(CFLAGS) -c $<
+
+# Misc targets
+
+clean:
+ rm -rf .libs
+ rm -f core *~ *% *.bak \
+ $(PDB_OBJS) $(PDB_OBJS:.so=.o) $(PDB_OBJS:.so=.lo)
diff --git a/examples/pdb/README b/examples/pdb/README
new file mode 100644
index 00000000000..ab1baed2366
--- /dev/null
+++ b/examples/pdb/README
@@ -0,0 +1,16 @@
+README for Samba Password Database (PDB) examples
+====================================================
+Jelmer Vernooij <jelmer@nl.linux.org>
+Stefan (metze) Metzmacher <metze@samba.org>
+
+The pdb_test.c file in this directory contains a very basic example of
+a pdb plugin. It just prints the name of the function that is executed using
+DEBUG. Maybe it's nice to include some of the arguments to the function in the
+future too..
+
+To debug passdb backends, try to run gdb on the 'pdbedit' executable. That's
+really much easier than restarting smbd constantly and attaching with your
+debugger.
+
+New passdb plugins should go into the samba lib directory, (/usr/lib/samba/pdb/
+for most distributions). An example would be: /usr/lib/samba/pdb/test.so
diff --git a/examples/pdb/mysql/mysql.dump b/examples/pdb/mysql/mysql.dump
new file mode 100644
index 00000000000..5da75f5745c
--- /dev/null
+++ b/examples/pdb/mysql/mysql.dump
@@ -0,0 +1,37 @@
+# Put this in your MySQL database by running
+# mysql -u<user> -p<password> -h<host> <database> < mysql.dump
+
+CREATE TABLE user (
+ logon_time int(9),
+ logoff_time int(9),
+ kickoff_time int(9),
+ pass_last_set_time int(9),
+ pass_can_change_time int(9),
+ pass_must_change_time int(9),
+ username varchar(255),
+ domain varchar(255),
+ nt_username varchar(255),
+ nt_fullname varchar(255),
+ home_dir varchar(255),
+ dir_drive varchar(4),
+ logon_script varchar(255),
+ profile_path varchar(255),
+ acct_desc varchar(255),
+ workstations varchar(255),
+ unknown_str varchar(255),
+ munged_dial varchar(255),
+ uid int(9) NOT NULL DEFAULT "0" PRIMARY KEY auto_increment,
+ gid int(9),
+ user_sid varchar(255),
+ group_sid varchar(255),
+ lm_pw varchar(255),
+ nt_pw varchar(255),
+ acct_ctrl int(9),
+ unknown_3 int(9),
+ logon_divs int(9),
+ hours_len int(9),
+ unknown_5 int(9),
+ unknown_6 int(9),
+ bad_password_count int(9),
+ logon_count(9)
+);
diff --git a/examples/pdb/mysql/smb.conf b/examples/pdb/mysql/smb.conf
new file mode 100644
index 00000000000..61a3f198168
--- /dev/null
+++ b/examples/pdb/mysql/smb.conf
@@ -0,0 +1,11 @@
+[global]
+netbios name = FOOBAR
+workgroup = TESTGROUP
+security = domain
+domain logons = yes
+domain master = yes
+passdb backend = plugin:/usr/local/samba/lib/pdb_mysql.so:mysql
+mysql:mysql host = rhonwyn
+mysql:mysql user = samba
+mysql:mysql password = ambas
+mysql:mysql database = samba
diff --git a/examples/pdb/pdb_test.c b/examples/pdb/pdb_test.c
new file mode 100644
index 00000000000..e46c621d48e
--- /dev/null
+++ b/examples/pdb/pdb_test.c
@@ -0,0 +1,145 @@
+/*
+ * Test 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"
+
+static int testsam_debug_level = DBGC_ALL;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS testsam_debug_level
+
+/***************************************************************
+ Start enumeration of the passwd list.
+****************************************************************/
+
+static NTSTATUS testsam_setsampwent(struct pdb_methods *methods, BOOL update)
+{
+ DEBUG(10, ("testsam_setsampwent called\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/***************************************************************
+ End enumeration of the passwd list.
+****************************************************************/
+
+static void testsam_endsampwent(struct pdb_methods *methods)
+{
+ DEBUG(10, ("testsam_endsampwent called\n"));
+}
+
+/*****************************************************************
+ Get one SAM_ACCOUNT from the list (next in line)
+*****************************************************************/
+
+static NTSTATUS testsam_getsampwent(struct pdb_methods *methods, SAM_ACCOUNT *user)
+{
+ DEBUG(10, ("testsam_getsampwent called\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/******************************************************************
+ Lookup a name in the SAM database
+******************************************************************/
+
+static NTSTATUS testsam_getsampwnam (struct pdb_methods *methods, SAM_ACCOUNT *user, const char *sname)
+{
+ DEBUG(10, ("testsam_getsampwnam called\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/***************************************************************************
+ Search by sid
+ **************************************************************************/
+
+static NTSTATUS testsam_getsampwsid (struct pdb_methods *methods, SAM_ACCOUNT *user, const DOM_SID *sid)
+{
+ DEBUG(10, ("testsam_getsampwsid called\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/***************************************************************************
+ Delete a SAM_ACCOUNT
+****************************************************************************/
+
+static NTSTATUS testsam_delete_sam_account(struct pdb_methods *methods, SAM_ACCOUNT *sam_pass)
+{
+ DEBUG(10, ("testsam_delete_sam_account called\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/***************************************************************************
+ Modifies an existing SAM_ACCOUNT
+****************************************************************************/
+
+static NTSTATUS testsam_update_sam_account (struct pdb_methods *methods, SAM_ACCOUNT *newpwd)
+{
+ DEBUG(10, ("testsam_update_sam_account called\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/***************************************************************************
+ Adds an existing SAM_ACCOUNT
+****************************************************************************/
+
+static NTSTATUS testsam_add_sam_account (struct pdb_methods *methods, SAM_ACCOUNT *newpwd)
+{
+ DEBUG(10, ("testsam_add_sam_account called\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS testsam_init(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
+{
+ NTSTATUS nt_status;
+
+ if (!NT_STATUS_IS_OK(nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
+ return nt_status;
+ }
+
+ (*pdb_method)->name = "testsam";
+
+ /* Functions your pdb module doesn't provide should be set
+ * to NULL */
+
+ (*pdb_method)->setsampwent = testsam_setsampwent;
+ (*pdb_method)->endsampwent = testsam_endsampwent;
+ (*pdb_method)->getsampwent = testsam_getsampwent;
+ (*pdb_method)->getsampwnam = testsam_getsampwnam;
+ (*pdb_method)->getsampwsid = testsam_getsampwsid;
+ (*pdb_method)->add_sam_account = testsam_add_sam_account;
+ (*pdb_method)->update_sam_account = testsam_update_sam_account;
+ (*pdb_method)->delete_sam_account = testsam_delete_sam_account;
+
+ testsam_debug_level = debug_add_class("testsam");
+ if (testsam_debug_level == -1) {
+ testsam_debug_level = DBGC_ALL;
+ DEBUG(0, ("testsam: Couldn't register custom debugging class!\n"));
+ } else DEBUG(0, ("testsam: Debug class number of 'testsam': %d\n", testsam_debug_level));
+
+ DEBUG(0, ("Initializing testsam\n"));
+ if (location)
+ DEBUG(10, ("Location: %s\n", location));
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS init_module(void) {
+ return smb_register_passdb(PASSDB_INTERFACE_VERSION, "testsam",
+ testsam_init);
+}
diff --git a/examples/pdb/sambapdb.dtd b/examples/pdb/sambapdb.dtd
new file mode 100644
index 00000000000..1f4054ddec4
--- /dev/null
+++ b/examples/pdb/sambapdb.dtd
@@ -0,0 +1,46 @@
+<!ELEMENT samba:crypt (#PCDATA)* >
+<!ATTLIST samba:crypt type CDATA #REQUIRED >
+
+<!ELEMENT samba:password (samba:crypt*) >
+<!ATTLIST samba:password last_set CDATA #IMPLIED
+ must_change CDATA #IMPLIED
+ can_change CDATA #IMPLIED>
+
+<!ELEMENT samba:group (#PCDATA)* >
+<!ATTLIST samba:group sid CDATA #REQUIRED
+ gid CDATA #IMPLIED >
+
+<!ELEMENT samba:domain (#PCDATA)* >
+<!ELEMENT samba:fullname (#PCDATA)* >
+<!ELEMENT samba:nt_username (#PCDATA)* >
+<!ELEMENT samba:logon_script (#PCDATA)* >
+<!ELEMENT samba:profile_path (#PCDATA)* >
+<!ELEMENT samba:logon_time (#PCDATA)* >
+<!ELEMENT samba:logoff_time (#PCDATA)* >
+<!ELEMENT samba:kickoff_time (#PCDATA)* >
+<!ELEMENT samba:logon_divs (#PCDATA)* >
+<!ELEMENT samba:hours_len (#PCDATA)* >
+<!ELEMENT samba:unknown_3 (#PCDATA)* >
+<!ELEMENT samba:unknown_5 (#PCDATA)* >
+<!ELEMENT samba:unknown_6 (#PCDATA)* >
+<!ELEMENT samba:homedir (#PCDATA)* >
+<!ELEMENT samba:unknown_str (#PCDATA)* >
+<!ELEMENT samba:dir_drive (#PCDATA)* >
+<!ELEMENT samba:munged_dial (#PCDATA)* >
+<!ELEMENT samba:acct_desc (#PCDATA)* >
+<!ELEMENT samba:acct_ctrl (#PCDATA)* >
+<!ELEMENT samba:workstations (#PCDATA)* >
+
+<!ELEMENT samba:user ( samba:group?, samba:domain?, samba:nt_username?, samba:fullname?, samba:homedir?, samba:dir_drive?, samba:logon_script?, samba:profile_path?, samba:password?, samba:acct_ctrl?,samba:unknown_3?, samba:logon_divs?, samba:hours_len?, samba:logon_time?, samba:logoff_time?, samba:kickoff_time?, samba:unknown_5?, samba:unknown_6?, samba:unknown_str?, samba:munged_dial?, samba:acct_desc?, samba:workstations? ) >
+
+<!ATTLIST samba:user sid CDATA #REQUIRED
+ uid CDATA #IMPLIED
+ name CDATA #REQUIRED>
+
+<!ELEMENT samba:users (samba:user*) >
+
+<!ELEMENT samba (samba:users?) >
+
+<!ATTLIST samba
+ xmlns CDATA #FIXED 'http://samba.org/~jelmer/sambapdb.dtd'>
+
diff --git a/examples/printer-accounting/README b/examples/printer-accounting/README
new file mode 100644
index 00000000000..b7ab42acb96
--- /dev/null
+++ b/examples/printer-accounting/README
@@ -0,0 +1,63 @@
+These are just a few examples of what you can do for printer accounting;
+they are really just hacks to show a manager how may pages were being
+printed out on his new hp5n :)
+
+acct-all will run acct-sum and read the log files to generate some
+stats.
+
+Here is a sample output of the raw stats :
+
+1996-06-10.15:02:15 pkelly master.fcp.oypi.com 538 0
+1996-06-10.15:06:40 pkelly master.fcp.oypi.com 537 0
+1996-06-10.15:32:12 ted master.fcp.oypi.com 547 0
+1996-06-11.09:06:15 violet master.fcp.oypi.com 2667 0
+1996-06-11.09:48:02 violet master.fcp.oypi.com 66304 5
+1996-06-11.09:50:04 violet master.fcp.oypi.com 116975 9
+1996-06-11.09:57:20 violet master.fcp.oypi.com 3013 1
+1996-06-11.10:13:17 pkelly master.fcp.oypi.com 3407 1
+1996-06-11.12:37:06 craig master.fcp.oypi.com 13639 2
+1996-06-11.12:42:23 pkelly master.fcp.oypi.com 13639 2
+1996-06-11.12:45:11 marlene master.fcp.oypi.com 515 0
+1996-06-11.14:17:10 lucie master.fcp.oypi.com 1405 1
+1996-06-11.14:36:03 laura master.fcp.oypi.com 45486 5
+1996-06-11.15:08:21 violet master.fcp.oypi.com 1923 1
+1996-06-11.15:09:42 laura master.fcp.oypi.com 4821 1
+1996-06-11.15:12:28 laura master.fcp.oypi.com 46277 5
+1996-06-11.15:19:38 violet master.fcp.oypi.com 3503 1
+1996-06-11.15:21:49 lucie master.fcp.oypi.com 493 0
+1996-06-11.15:43:36 al master.fcp.oypi.com 3067 1
+
+And the output after the acct-sum is done on a full set of files
+in /var/log/lp/*
+
+master[1072] /var/log/lp$ /etc/conf/acct-all
+
+Sun Jul 21 23:03:16 EDT 1996
+
+Pages are approximate ...
+
+User Jobs Pages Size
+al 1 1 2 KB
+craig 1 2 13 KB
+jack 68 235 1995 KB
+laura 88 328 3050 KB
+lucie 221 379 3529 KB
+marlene 12 151 1539 KB
+melanie 83 365 3691 KB
+michelle 68 219 1987 KB
+mike 2 10 81 KB
+neil 111 225 2753 KB
+operator 44 137 1132 KB
+pkelly 368 984 11154 KB
+root 8 0 29 KB
+ted 158 257 2337 KB
+tony 244 368 2455 KB
+violet 419 1002 10072 KB
+
+
+Printer Jobs Pages
+hp2p 3 4
+hp5 915 2135
+lp 978 2524
+
+<pkelly@ets.net>
diff --git a/examples/printer-accounting/acct-all b/examples/printer-accounting/acct-all
new file mode 100644
index 00000000000..dc8f175b3cb
--- /dev/null
+++ b/examples/printer-accounting/acct-all
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+echo ""
+date
+echo ""
+echo "Pages are approximate ..."
+echo ""
+/etc/conf/acct-sum /var/log/lp/*
+echo ""
diff --git a/examples/printer-accounting/acct-sum b/examples/printer-accounting/acct-sum
new file mode 100644
index 00000000000..ffbfc8d8801
--- /dev/null
+++ b/examples/printer-accounting/acct-sum
@@ -0,0 +1,29 @@
+#!/usr/bin/perl
+
+while (<>) {
+ ($date, $user, $machine, $size, $pages) = split(' ');
+
+ $Printer{$ARGV}++;
+ $PrinterPages{$ARGV} += $pages;
+
+ $Jobs{$user}++;
+ $Size{$user}+= $size;
+ $Pages{$user}+= $pages;
+}
+
+printf "%-15s %5s %8s %8s\n", qw(User Jobs Pages Size);
+foreach $user (sort keys %Jobs) {
+ printf "%-15s %5d %8d %8d \KB\n",
+ $user, $Jobs{$user}, $Pages{$user}, $Size{$user}/1024;
+}
+
+
+print "\n\n";
+printf "%-15s %5s %8s %8s\n", qw(Printer Jobs Pages);
+foreach $prn (sort keys %Printer) {
+ ($name = $prn) =~ s=.*/==;
+ printf "%-15s %5d %8d\n",
+ $name, $Printer{$prn}, $PrinterPages{$prn};
+}
+
+
diff --git a/examples/printer-accounting/hp5-redir b/examples/printer-accounting/hp5-redir
new file mode 100644
index 00000000000..ea1299068a3
--- /dev/null
+++ b/examples/printer-accounting/hp5-redir
@@ -0,0 +1,40 @@
+#!/usr/bin/perl
+#
+# 0 == stdin == docuement
+# 1 == stdout == printer
+# 2 == stderr == logging
+#
+# With redirection to another valid /etc/printcap entry
+#
+
+umask(002);
+
+# -w132 -l66 -i0 -n pkelly -h master.fcp.oypi.com /var/log/lp-acct
+require "getopts.pl";
+&Getopts("w:l:i:n:h:");
+
+chomp($date = `date '+%Y-%m-%d.%T'`);
+
+($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
+ $atime,$mtime,$ctime,$blksize,$blocks)
+ = stat(STDIN);
+
+# send to the real printer now.
+open(P, "|lpr -Pmgmt0") || die "Can't print to hp5-real ($!)\n";
+$cnt = 0;
+while (sysread(STDIN, $buf, 10240)) {
+ print P $buf;
+ # this is ugly, but it gives the approx in pages. We
+ # don't print graphics, so ... There must be a better way :)
+ $cnt += ($buf =~ /^L/g);
+}
+close(P);
+
+$acct = shift;
+if (open(ACCT, ">>$acct")) {
+ print ACCT "$date $opt_n $opt_h $size $cnt\n";
+ close(ACCT);
+} else {
+ warn "Err: Can't account for it ($!)\n";
+ warn "Log: $date $opt_n $opt_h $size $cnt\n";
+}
diff --git a/examples/printer-accounting/lp-acct b/examples/printer-accounting/lp-acct
new file mode 100644
index 00000000000..91a3def08f8
--- /dev/null
+++ b/examples/printer-accounting/lp-acct
@@ -0,0 +1,35 @@
+#!/usr/bin/perl
+#
+# 0 == stdin == docuement
+# 1 == stdout == printer
+# 2 == stderr == logging
+#
+# Regular, with no redirection
+#
+
+umask(002);
+
+# -w132 -l66 -i0 -n pkelly -h master.fcp.oypi.com /var/log/lp-acct
+require "getopts.pl";
+&Getopts("w:l:i:n:h:");
+
+chomp($date = `date '+%Y-%m-%d.%T'`);
+
+($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
+ $atime,$mtime,$ctime,$blksize,$blocks)
+ = stat(STDIN);
+
+$cnt = 0;
+while (sysread(STDIN, $buf, 10240)) {
+ print $buf;
+ $cnt += ($buf =~ /^L/g);
+}
+
+$acct = shift;
+if (open(ACCT, ">>$acct")) {
+ print ACCT "$date $opt_n $opt_h $size $cnt\n";
+ close(ACCT);
+} else {
+ warn "Err: Can't account for it ($!)\n";
+ warn "Log: $date $opt_n $opt_h $size $cnt\n";
+}
diff --git a/examples/printer-accounting/printcap b/examples/printer-accounting/printcap
new file mode 100644
index 00000000000..976005a097c
--- /dev/null
+++ b/examples/printer-accounting/printcap
@@ -0,0 +1,22 @@
+# HP5N - Accounting entry
+#
+# This file calls the filter, hp5-redir to do the numbers and then
+# is redirected to the real entry, mgmt0, which is a remote HP5N
+# on the LAN with it's own IP number.
+#
+hp5:lp=/dev/lp1:\
+ :sd=/usr/spool/lpd/hp5-acct:\
+ :lf=/var/log/lp-err:\
+ :af=/var/log/lp/hp5:\
+ :if=/usr/local/bin/lp/hp5-redir:\
+ :sh:sf:\
+ :mx#0:
+
+# HP5N - Real printer location
+mgmt0:\
+ :rm=hp5.fcp.oypi.com:\
+ :rp=hp5.fcp.oypi.com:\
+ :sd=/usr/spool/lpd/mgmt0:\
+ :sh:sf:\
+ :mx#0:
+
diff --git a/examples/printing/prtpub.c b/examples/printing/prtpub.c
new file mode 100644
index 00000000000..ea71e03d073
--- /dev/null
+++ b/examples/printing/prtpub.c
@@ -0,0 +1,238 @@
+/*
+ * Set printer capabilities in DsDriver Keys on remote printer
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This needs to be defined for certain compilers */
+#define WINVER 0x0500
+
+#include <tchar.h>
+#include <windows.h>
+#include <stdio.h>
+
+#define SAMBA_PORT _T("Samba")
+
+TCHAR *PrintLastError(void)
+{
+ static TCHAR msgtxt[1024*sizeof(TCHAR)];
+
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, GetLastError(),
+ 0, msgtxt, 0, NULL);
+
+ return msgtxt;
+}
+
+void map_orientation(HANDLE ph, TCHAR *printer, TCHAR *port)
+{
+ DWORD rot;
+ TCHAR portrait_only[] = _T("PORTRAIT\0");
+ TCHAR both[] = _T("LANDSCAPE\0PORTRAIT\0");
+
+ /* orentation of 90 or 270 indicates landscape supported, 0 means it isn't */
+ rot = DeviceCapabilities(printer, port, DC_BINNAMES, NULL, NULL);
+
+ printf("printOrientationsSupported:\n");
+
+ if (rot) {
+ printf("\tPORTRAIT\n\tLANDSCAPE\n");
+ SetPrinterDataEx(ph, SPLDS_DRIVER_KEY, _T("printOrientationsSupported"), REG_MULTI_SZ,
+ both, sizeof(both));
+ } else {
+ printf("\tPORTRAIT\n");
+ SetPrinterDataEx(ph, SPLDS_DRIVER_KEY, _T("printOrientationsSupported"), REG_MULTI_SZ,
+ portrait_only, sizeof(portrait_only));
+ }
+}
+
+void map_resolution(HANDLE ph, TCHAR *printer, TCHAR *port)
+{
+ DWORD num, *res, maxres = 0, i;
+
+ num = DeviceCapabilities(printer, port, DC_ENUMRESOLUTIONS, NULL, NULL);
+ if ((DWORD) -1 == num)
+ return;
+ res = malloc(num*2*sizeof(DWORD));
+ num = DeviceCapabilities(printer, port, DC_ENUMRESOLUTIONS, (BYTE *) res, NULL);
+ for (i=0; i < num*2; i++) {
+ maxres = (res[i] > maxres) ? res[i] : maxres;
+ }
+ printf("printMaxResolutionSupported: %d\n", maxres);
+ SetPrinterDataEx(ph, SPLDS_DRIVER_KEY, _T("printMaxResolutionSupported"), REG_DWORD,
+ (BYTE *) &maxres, sizeof(maxres));
+}
+
+void map_extents(HANDLE ph, TCHAR *printer, TCHAR *port)
+{
+ DWORD extentval, xval, yval;
+
+ extentval = DeviceCapabilities(printer, port, DC_MINEXTENT, NULL, NULL);
+ xval = (DWORD) (LOWORD(extentval));
+ yval = (DWORD) (HIWORD(extentval));
+ printf("printMinXExtent: %d\n", xval);
+ printf("printMinYExtent: %d\n", yval);
+ SetPrinterDataEx(ph, SPLDS_DRIVER_KEY, _T("printMinXExtent"), REG_DWORD, (BYTE *) &xval, sizeof(xval));
+ SetPrinterDataEx(ph, SPLDS_DRIVER_KEY, _T("printMinYExtent"), REG_DWORD, (BYTE *) &yval, sizeof(yval));
+ extentval = DeviceCapabilities(printer, port, DC_MAXEXTENT, NULL, NULL);
+ xval = (DWORD) (LOWORD(extentval));
+ yval = (DWORD) (HIWORD(extentval));
+ printf("printMaxXExtent: %d\n", xval);
+ printf("printMaxYExtent: %d\n", yval);
+ SetPrinterDataEx(ph, SPLDS_DRIVER_KEY, _T("printMaxXExtent"), REG_DWORD, (BYTE *) &xval, sizeof(xval));
+ SetPrinterDataEx(ph, SPLDS_DRIVER_KEY, _T("printMaxYExtent"), REG_DWORD, (BYTE *) &yval, sizeof(yval));
+}
+
+void map_printrateunit(HANDLE ph, TCHAR *printer, TCHAR *port)
+{
+ DWORD unit;
+ TCHAR ppm[] = _T("PagesPerMinute");
+ TCHAR ipm[] = _T("InchesPerMinute");
+ TCHAR lpm[] = _T("LinesPerMinute");
+ TCHAR cps[] = _T("CharactersPerSecond");
+
+ unit = DeviceCapabilities(printer, port, DC_PRINTRATEUNIT, NULL, NULL);
+ switch(unit) {
+ case PRINTRATEUNIT_PPM:
+ printf("printRateUnit: %s\n", ppm);
+ SetPrinterDataEx(ph, SPLDS_DRIVER_KEY, _T("printRateUnit"), REG_SZ, ppm, sizeof(ppm));
+ break;
+ case PRINTRATEUNIT_IPM:
+ printf("printRateUnit: %s\n", ipm);
+ SetPrinterDataEx(ph, SPLDS_DRIVER_KEY, _T("printRateUnit"), REG_SZ, ipm, sizeof(ipm));
+ break;
+ case PRINTRATEUNIT_LPM:
+ printf("printRateUnit: %s\n", lpm);
+ SetPrinterDataEx(ph, SPLDS_DRIVER_KEY, _T("printRateUnit"), REG_SZ, lpm, sizeof(lpm));
+ break;
+ case PRINTRATEUNIT_CPS:
+ printf("printRateUnit: %s\n", cps);
+ SetPrinterDataEx(ph, SPLDS_DRIVER_KEY, _T("printRateUnit"), REG_SZ, cps, sizeof(cps));
+ break;
+ default:
+ printf("printRateUnit: unknown value %d\n", unit);
+ }
+}
+
+void map_generic_boolean(HANDLE ph, TCHAR *printer, TCHAR *port, WORD cap, TCHAR *key)
+{
+ BYTE boolval;
+ /* DeviceCapabilities doesn't always return 1 for true...just nonzero */
+ boolval = (BYTE) (DeviceCapabilities(printer, port, cap, NULL, NULL) ? 1 : 0);
+ printf("%s: %s\n", key, boolval ? "TRUE" : "FALSE");
+ SetPrinterDataEx(ph, SPLDS_DRIVER_KEY, key, REG_BINARY, &boolval, sizeof(boolval));
+}
+
+void map_generic_dword(HANDLE ph, TCHAR *printer, TCHAR *port, WORD cap, TCHAR *key)
+{
+ DWORD dword;
+
+ dword = DeviceCapabilities(printer, port, cap, NULL, NULL);
+ if ((DWORD) -1 == dword)
+ return;
+
+ printf("%s: %d\n", key, dword);
+ SetPrinterDataEx(ph, SPLDS_DRIVER_KEY, key, REG_DWORD, (BYTE *) &dword, sizeof(dword));
+}
+
+void map_generic_multi_sz(HANDLE ph, TCHAR *printer, TCHAR *port, WORD cap, TCHAR *key, int size)
+{
+ TCHAR *strings_in;
+ TCHAR *strings_out, *strings_cur;
+ DWORD num_items, i;
+
+ num_items = DeviceCapabilities(printer, port, cap, NULL, NULL);
+ if ((DWORD) -1 == num_items)
+ return;
+ strings_in = malloc(num_items * size);
+ strings_out = calloc(num_items, size);
+ num_items = DeviceCapabilities(printer, port, cap, strings_in, NULL);
+ printf("%s:\n", key);
+ for (i=0, strings_cur = strings_out; i < num_items; i++) {
+ _tcsncpy(strings_cur, &strings_in[i*size], size);
+ printf("\t%s\n", strings_cur);
+ strings_cur += _tcslen(strings_cur) + 1;
+ }
+
+ SetPrinterDataEx(ph, SPLDS_DRIVER_KEY, key, REG_MULTI_SZ, strings_out,
+ (strings_cur - strings_out + 1) * sizeof(TCHAR));
+
+ free(strings_in);
+ free(strings_out);
+}
+
+int main(int argc, char *argv[])
+{
+ HANDLE ph;
+ BYTE *driver_info;
+ DWORD needed;
+ TCHAR *printer;
+ TCHAR *port = SAMBA_PORT;
+ PRINTER_DEFAULTS admin_access = {NULL, NULL, PRINTER_ACCESS_ADMINISTER};
+ PRINTER_INFO_7 publish = {NULL, DSPRINT_PUBLISH};
+
+ if (argc < 2) {
+ printf("Usage: %s <printername>\n", argv[0]);
+ return -1;
+ }
+
+ printer = argv[1];
+
+ if (!(OpenPrinter(printer, &ph, &admin_access))) {
+ printf("OpenPrinter failed, error = %s\n", PrintLastError());
+ return -1;
+ }
+
+ GetPrinterDriver(ph, NULL, 1, NULL, 0, &needed);
+ if (!needed) {
+ printf("GetPrinterDriver failed, error = %s\n", PrintLastError());
+ ClosePrinter(ph);
+ return -1;
+ }
+ driver_info = malloc(needed);
+ if (!(GetPrinterDriver(ph, NULL, 1, driver_info, needed, &needed))) {
+ printf("GetPrinterDriver failed, error = %s\n", PrintLastError());
+ ClosePrinter(ph);
+ return -1;
+ }
+
+ map_generic_multi_sz(ph, printer, port, DC_BINNAMES, _T("printBinNames"), 24);
+ map_generic_boolean(ph, printer, port, DC_COLLATE, _T("printCollate"));
+ map_generic_dword(ph, printer, port, DC_COPIES, _T("printMaxCopies"));
+ map_generic_dword(ph, printer, port, DC_DRIVER, _T("driverVersion"));
+ map_generic_boolean(ph, printer, port, DC_DUPLEX, _T("printDuplexSupported"));
+ map_extents(ph, printer, port);
+ map_resolution(ph, printer, port);
+ map_orientation(ph, printer, port);
+ map_generic_multi_sz(ph, printer, port, DC_PAPERNAMES, _T("printMediaSupported"), 64);
+#if (WINVER >= 0x0500)
+ map_generic_boolean(ph, printer, port, DC_COLORDEVICE, _T("printColor"));
+ map_generic_multi_sz(ph, printer, port, DC_PERSONALITY, _T("printLanguage"), 64);
+ map_generic_multi_sz(ph, printer, port, DC_MEDIAREADY, _T("printMediaReady"),64);
+ map_generic_dword(ph, printer, port, DC_PRINTERMEM, _T("printMemory"));
+ map_generic_dword(ph, printer, port, DC_PRINTRATE, _T("printRate"));
+ map_printrateunit(ph, printer, port);
+#ifdef DC_STAPLE
+ map_generic_boolean(ph, printer, port, DC_STAPLE, _T("printStaplingSupported"));
+#endif
+#ifdef DC_PRINTRATEPPM
+ map_generic_dword(ph, printer, port, DC_PRINTRATEPPM, _T("printPagesPerMinute"));
+#endif
+#endif
+ SetPrinter(ph, 7, (BYTE *) &publish, 0);
+ ClosePrinter(ph);
+ return 0;
+}
diff --git a/examples/printing/readme.prtpub b/examples/printing/readme.prtpub
new file mode 100644
index 00000000000..319ce605c3b
--- /dev/null
+++ b/examples/printing/readme.prtpub
@@ -0,0 +1,12 @@
+prtpub.c contains a program which, when compiled with Visual C, can
+download a driver for a printer, query the capabilities of the driver,
+then write back the DsDriver keys necessary to publish all the fields
+of a printer in the directory. After writing back the fields, it issues
+a SetPrinter with info level 7, telling the server to publish the
+printer.
+
+It also writes the fields to stdout.
+
+In order to be distributed, it should be compiled using DLLs for C runtime.
+
+The program takes the UNC name of a printer as the only argument.
diff --git a/examples/printing/smbprint b/examples/printing/smbprint
new file mode 100755
index 00000000000..61ee41f4440
--- /dev/null
+++ b/examples/printing/smbprint
@@ -0,0 +1,144 @@
+#!/bin/sh
+
+# This script is an input filter for printcap printing on a unix machine. It
+# uses the smbclient program to print the file to the specified smb-based
+# server and service.
+# For example you could have a printcap entry like this
+#
+# smb:lp=/dev/null:sd=/usr/spool/smb:sh:if=/usr/local/samba/smbprint
+#
+# which would create a unix printer called "smb" that will print via this
+# script. You will need to create the spool directory /usr/spool/smb with
+# appropriate permissions and ownerships for your system.
+
+# Set these to the server and service you wish to print to
+# In this example I have a WfWg PC called "lapland" that has a printer
+# exported called "printer" with no password.
+
+#
+# Script further altered by hamiltom@ecnz.co.nz (Michael Hamilton)
+# so that the server, service, and password can be read from
+# a /usr/var/spool/lpd/PRINTNAME/.config file.
+#
+# Script further modified by Richard Sharpe to fix some things.
+# Get rid of the -x on the first line, and add parameters
+#
+# -t now causes translate to be used when sending files
+#
+# Further modifications by Alfred Perlstein to fix some problems and
+# improve the quality of the code (3-Dec-2001).
+#
+# More hacking by Richard Sharpe to improve portability. 9-Dec-2001.
+#
+# In order for this to work the /etc/printcap entry must include an
+# accounting file (af=...):
+#
+# cdcolour:\
+# :cm=CD IBM Colorjet on 6th:\
+# :sd=/var/spool/lpd/cdcolour:\
+# :af=/var/spool/lpd/cdcolour/acct:\
+# :if=/usr/local/etc/smbprint:\
+# :mx=0:\
+# :lp=/dev/null:
+#
+# The /usr/var/spool/lpd/PRINTNAME/.config file should contain:
+# server=PC_SERVER
+# service=PR_SHARENAME
+# password="password"
+#
+# E.g.
+# server=PAULS_PC
+# service=CJET_371
+# password=""
+
+#smbclient=/usr/pkg/bin/smbclient
+# Assume that smbclient will be in the same place as smbprint
+
+smbclient="`dirname $0`/smbclient"
+
+#
+# The last parameter to the filter is the accounting file name.
+# Extract the directory name from the file name.
+# Concat this with /.config to get the config file.
+#
+TRANS=0
+eval acct_file=\${$#}
+spool_dir=`dirname $acct_file`
+config_file=$spool_dir/.config
+
+# Should read the following variables set in the config file:
+# server
+# service
+# password
+# username (optional)
+# IP (optional)
+# debug (optional)
+# debugsmb (optional)
+# debugfile (optional)
+. $config_file
+
+if [ "x$password" = "x" ] ; then
+ password="-N"
+fi
+
+if [ "x$username" == "x" ] ; then
+ username="$server";
+fi
+
+while test $# -gt 0; do
+ case "$1" in
+ -t)
+ TRANS=1
+ ;;
+
+ *) # Bad Parameters, ignore them ...
+ ;;
+ esac
+ shift
+done
+
+command="print - ;"
+if [ $TRANS -eq 1 ]; then
+ command="translate;$command";
+fi
+
+debugfile="/tmp/smb-print.log"
+if [ "x$debug" = "x" ] ; then
+ debugfile=/dev/null debugargs=
+else
+ if [ $debug -eq 0 ] ; then
+ debugfile=/dev/null debugargs=
+ else
+ set -x; exec >>$debugfile 2>&1
+ debugargs="$debugfile."
+ #[ "x$debugsmb" == "x" ] || debugargs="$debugargs -d $debugsmb"
+ fi
+fi
+
+if [ "x$smbconf" != "x" ]; then
+
+ smbconf="-s $smbconf"
+
+fi
+
+if [ "x$IP" != "x" ]; then
+
+ IP="-I $IP"
+
+fi
+
+if [ "x$debugargs" != "x" ]; then
+
+ debugargs="-l $debugargs"
+
+fi
+
+$smbclient \
+ "\\\\$server\\$service" \
+ $password \
+ $smbconf \
+ $IP \
+ $debugargs \
+ -U $username \
+ -c "$command"
+#
diff --git a/examples/printing/smbprint-new.sh b/examples/printing/smbprint-new.sh
new file mode 100644
index 00000000000..61ee41f4440
--- /dev/null
+++ b/examples/printing/smbprint-new.sh
@@ -0,0 +1,144 @@
+#!/bin/sh
+
+# This script is an input filter for printcap printing on a unix machine. It
+# uses the smbclient program to print the file to the specified smb-based
+# server and service.
+# For example you could have a printcap entry like this
+#
+# smb:lp=/dev/null:sd=/usr/spool/smb:sh:if=/usr/local/samba/smbprint
+#
+# which would create a unix printer called "smb" that will print via this
+# script. You will need to create the spool directory /usr/spool/smb with
+# appropriate permissions and ownerships for your system.
+
+# Set these to the server and service you wish to print to
+# In this example I have a WfWg PC called "lapland" that has a printer
+# exported called "printer" with no password.
+
+#
+# Script further altered by hamiltom@ecnz.co.nz (Michael Hamilton)
+# so that the server, service, and password can be read from
+# a /usr/var/spool/lpd/PRINTNAME/.config file.
+#
+# Script further modified by Richard Sharpe to fix some things.
+# Get rid of the -x on the first line, and add parameters
+#
+# -t now causes translate to be used when sending files
+#
+# Further modifications by Alfred Perlstein to fix some problems and
+# improve the quality of the code (3-Dec-2001).
+#
+# More hacking by Richard Sharpe to improve portability. 9-Dec-2001.
+#
+# In order for this to work the /etc/printcap entry must include an
+# accounting file (af=...):
+#
+# cdcolour:\
+# :cm=CD IBM Colorjet on 6th:\
+# :sd=/var/spool/lpd/cdcolour:\
+# :af=/var/spool/lpd/cdcolour/acct:\
+# :if=/usr/local/etc/smbprint:\
+# :mx=0:\
+# :lp=/dev/null:
+#
+# The /usr/var/spool/lpd/PRINTNAME/.config file should contain:
+# server=PC_SERVER
+# service=PR_SHARENAME
+# password="password"
+#
+# E.g.
+# server=PAULS_PC
+# service=CJET_371
+# password=""
+
+#smbclient=/usr/pkg/bin/smbclient
+# Assume that smbclient will be in the same place as smbprint
+
+smbclient="`dirname $0`/smbclient"
+
+#
+# The last parameter to the filter is the accounting file name.
+# Extract the directory name from the file name.
+# Concat this with /.config to get the config file.
+#
+TRANS=0
+eval acct_file=\${$#}
+spool_dir=`dirname $acct_file`
+config_file=$spool_dir/.config
+
+# Should read the following variables set in the config file:
+# server
+# service
+# password
+# username (optional)
+# IP (optional)
+# debug (optional)
+# debugsmb (optional)
+# debugfile (optional)
+. $config_file
+
+if [ "x$password" = "x" ] ; then
+ password="-N"
+fi
+
+if [ "x$username" == "x" ] ; then
+ username="$server";
+fi
+
+while test $# -gt 0; do
+ case "$1" in
+ -t)
+ TRANS=1
+ ;;
+
+ *) # Bad Parameters, ignore them ...
+ ;;
+ esac
+ shift
+done
+
+command="print - ;"
+if [ $TRANS -eq 1 ]; then
+ command="translate;$command";
+fi
+
+debugfile="/tmp/smb-print.log"
+if [ "x$debug" = "x" ] ; then
+ debugfile=/dev/null debugargs=
+else
+ if [ $debug -eq 0 ] ; then
+ debugfile=/dev/null debugargs=
+ else
+ set -x; exec >>$debugfile 2>&1
+ debugargs="$debugfile."
+ #[ "x$debugsmb" == "x" ] || debugargs="$debugargs -d $debugsmb"
+ fi
+fi
+
+if [ "x$smbconf" != "x" ]; then
+
+ smbconf="-s $smbconf"
+
+fi
+
+if [ "x$IP" != "x" ]; then
+
+ IP="-I $IP"
+
+fi
+
+if [ "x$debugargs" != "x" ]; then
+
+ debugargs="-l $debugargs"
+
+fi
+
+$smbclient \
+ "\\\\$server\\$service" \
+ $password \
+ $smbconf \
+ $IP \
+ $debugargs \
+ -U $username \
+ -c "$command"
+#
diff --git a/examples/printing/smbprint.old b/examples/printing/smbprint.old
new file mode 100755
index 00000000000..5a00a2a8aa8
--- /dev/null
+++ b/examples/printing/smbprint.old
@@ -0,0 +1,95 @@
+#!/bin/sh
+
+# This script is an input filter for printcap printing on a unix machine. It
+# uses the smbclient program to print the file to the specified smb-based
+# server and service.
+# For example you could have a printcap entry like this
+#
+# smb:lp=/dev/null:sd=/usr/spool/smb:sh:if=/usr/local/samba/smbprint
+#
+# which would create a unix printer called "smb" that will print via this
+# script. You will need to create the spool directory /usr/spool/smb with
+# appropriate permissions and ownerships for your system.
+
+# Set these to the server and service you wish to print to
+# In this example I have a WfWg PC called "lapland" that has a printer
+# exported called "printer" with no password.
+
+#
+# Script further altered by hamiltom@ecnz.co.nz (Michael Hamilton)
+# so that the server, service, and password can be read from
+# a /usr/var/spool/lpd/PRINTNAME/.config file.
+#
+# Script further modified by Richard Sharpe to fix some things.
+# Get rid of the -x on the first line, and add parameters
+#
+# -t now causes translate to be used when sending files
+#
+# In order for this to work the /etc/printcap entry must include an
+# accounting file (af=...):
+#
+# cdcolour:\
+# :cm=CD IBM Colorjet on 6th:\
+# :sd=/var/spool/lpd/cdcolour:\
+# :af=/var/spool/lpd/cdcolour/acct:\
+# :if=/usr/local/etc/smbprint:\
+# :mx=0:\
+# :lp=/dev/null:
+#
+# The /usr/var/spool/lpd/PRINTNAME/.config file should contain:
+# server=PC_SERVER
+# service=PR_SHARENAME
+# password="password"
+#
+# E.g.
+# server=PAULS_PC
+# service=CJET_371
+# password=""
+
+#
+# Debugging log file, change to /dev/null if you like.
+#
+logfile=/tmp/smb-print.log
+# logfile=/dev/null
+
+
+#
+# The last parameter to the filter is the accounting file name.
+# Extract the directory name from the file name.
+# Concat this with /.config to get the config file.
+#
+TRANS=0
+eval acct_file=\${$#}
+spool_dir=`dirname $acct_file`
+config_file=$spool_dir/.config
+
+# Should read the following variables set in the config file:
+# server
+# service
+# password
+eval `cat $config_file`
+
+while getopts t c; do
+ case $c in
+ t)
+ TRANS=1
+ ;;
+
+ '?') # Bad parameters, ignore it ...
+ ;;
+ esac
+done
+#
+# Some debugging help, change the >> to > if you want to same space.
+#
+echo "server $server, service $service" >> $logfile
+
+(
+# NOTE You may wish to add the line `echo translate' if you want automatic
+# CR/LF translation when printing.
+ if [ $TRANS -eq 1 ]; then
+ echo translate
+ fi
+ echo "print -"
+ cat
+) | /usr/local/samba/bin/smbclient "\\\\$server\\$service" $password -U $server -N -P >> $logfile
diff --git a/examples/printing/smbprint.sysv b/examples/printing/smbprint.sysv
new file mode 100644
index 00000000000..11fea21441b
--- /dev/null
+++ b/examples/printing/smbprint.sysv
@@ -0,0 +1,52 @@
+#!/bin/sh
+#
+# @(#) smbprint.sysv version 1.0 Ross Wakelin <r.wakelin@march.co.uk>
+#
+# Version 1.0 13 January 1995
+# modified from the original smbprint (bsd) script
+#
+# this script is a System 5 printer interface script. It
+# uses the smbclient program to print the file to the specified smb-based
+# server and service.
+#
+# To add this to your lp system, copy this file into your samba directory
+# (the example here is /opt/samba), modify the server and service variables
+# and then execute the following command (as root)
+#
+# lpadmin -punixprintername -v/dev/null -i/opt/samba/smbprint
+#
+# where unixprintername is the name that the printer will be known as
+# on your unix box.
+#
+# the script smbprint will be copied into your printer administration
+# directory (/usr/lib/lp or /etc/lp) as a new interface
+# (interface/unixprintername)
+# Then you have to enable unixprintername and accept unixprintername
+#
+# This script will then be called by the lp service to print the files
+# This script will have 6 or more parameters passed to it by the lp service.
+# The first five will contain details of the print job, who queued it etc,
+# while parameters 6 onwards are a list of files to print. We just
+# cat these at the samba client.
+#
+# Set these to the server and service you wish to print to
+# In this example I have a WfWg PC called "lapland" that has a printer
+# exported called "printer" with no password.
+#
+# clear out the unwanted parameters
+shift;shift;shift;shift;shift
+# now the argument list is just the files to print
+
+server=admin
+service=hplj2
+password=""
+
+(
+# NOTE You may wish to add the line `echo translate' if you want automatic
+# CR/LF translation when printing.
+ echo translate
+ echo "print -"
+ cat $*
+) | /opt/samba/smbclient "\\\\$server\\$service" $password -N > /dev/null
+exit $?
+
diff --git a/examples/simple/README b/examples/simple/README
new file mode 100644
index 00000000000..9628aa8260d
--- /dev/null
+++ b/examples/simple/README
@@ -0,0 +1,2 @@
+This is the "original" sample config file.
+
diff --git a/examples/simple/smb.conf b/examples/simple/smb.conf
new file mode 100644
index 00000000000..786bf49057c
--- /dev/null
+++ b/examples/simple/smb.conf
@@ -0,0 +1,167 @@
+; Configuration file for smbd.
+; ============================================================================
+; For the format of this file and comprehensive descriptions of all the
+; configuration option, please refer to the man page for smb.conf(5).
+;
+; The following configuration should suit most systems for basic usage and
+; initial testing. It gives all clients access to their home directories and
+; allows access to all printers specified in /etc/printcap.
+;
+; Things you need to check:
+; --------------------------
+;
+; 1: Check the path to your printcap file. If you are using a system that does
+; not use printcap (eg., Solaris), create a file containing lines of the
+; form
+;
+; printername|printername|printername|
+;
+; where each "printername" is the name of a printer you want to provide
+; access to. Then alter the "printcap =" entry to point to the new file.
+;
+; If using Solaris, the following command will generate a suitable printcap
+; file:
+;
+; lpc status | grep ":" | sed s/:/\|/ > myprintcap
+;
+; 2: Make sure the "print command" entry is correct for your system. This
+; command should submit a file (represented by %s) to a printer
+; (represented by %p) for printing and should REMOVE the file after
+; printing.
+;
+; One most systems the default will be OK, as long as you get "printing ="
+; right.
+;
+; It is also a good idea to use an absolute path in the print command
+; as there is no guarantee the search path will be set correctly.
+;
+; 3: Make sure the "printing =" option is set correctly for your system.
+; Possible values are "sysv", "bsd" or "aix".
+;
+; 4: Make sure the "lpq command" entry is correct for your system. The default
+; may not work for you.
+;
+; 5: Make sure that the user specified in "guest account" exists. Typically
+; this will be a user that cannot log in and has minimal privileges.
+; Often the "nobody" account doesn't work (very system dependant).
+;
+; 6: You should consider the "security =" option. See a full description
+; in the main documentation and the smb.conf(5) manual page
+;
+; 7: Look at the "hosts allow" option, unless you want everyone on the internet
+; to be able to access your files.
+;
+[global]
+ printing = bsd
+ printcap name = /etc/printcap
+ load printers = yes
+ guest account = pcguest
+; This next option sets a separate log file for each client. Remove
+; it if you want a combined log file.
+ log file = /usr/local/samba/log.%m
+
+; You will need a world readable lock directory and "share modes=yes"
+; if you want to support the file sharing modes for multiple users
+; of the same files
+; lock directory = /usr/local/samba/var/locks
+; share modes = yes
+
+[homes]
+ comment = Home Directories
+ browseable = no
+ read only = no
+ create mode = 0750
+
+[printers]
+ comment = All Printers
+ browseable = no
+ printable = yes
+ public = no
+ writable = no
+ create mode = 0700
+
+; you might also want this one, notice that it is read only so as not to give
+; people without an account write access.
+;
+; [tmp]
+; comment = Temporary file space
+; path = /tmp
+; read only = yes
+; public = yes
+
+;
+; Other examples.
+;
+; A private printer, usable only by fred. Spool data will be placed in fred's
+; home directory. Note that fred must have write access to the spool directory,
+; wherever it is.
+;[fredsprn]
+; comment = Fred's Printer
+; valid users = fred
+; path = /homes/fred
+; printer = freds_printer
+; public = no
+; writable = no
+; printable = yes
+;
+; A private directory, usable only by fred. Note that fred requires write
+; access to the directory.
+;[fredsdir]
+; comment = Fred's Service
+; path = /usr/somewhere/private
+; valid users = fred
+; public = no
+; writable = yes
+; printable = no
+;
+; A publicly accessible directory, but read only, except for people in
+; the staff group
+;[public]
+; comment = Public Stuff
+; path = /usr/somewhere/public
+; public = yes
+; writable = no
+; printable = no
+; write list = @staff
+;
+; a service which has a different directory for each machine that connects
+; this allows you to tailor configurations to incoming machines. You could
+; also use the %u option to tailor it by user name.
+; The %m gets replaced with the machine name that is connecting.
+;[pchome]
+; comment = PC Directories
+; path = /usr/pc/%m
+; public = no
+; writeable = yes
+;
+;
+; A publicly accessible directory, read/write to all users. Note that all files
+; created in the directory by users will be owned by the default user, so
+; any user with access can delete any other user's files. Obviously this
+; directory must be writable by the default user. Another user could of course
+; be specified, in which case all files would be owned by that user instead.
+;[public]
+; path = /usr/somewhere/else/public
+; public = yes
+; only guest = yes
+; writable = yes
+; printable = no
+;
+;
+; The following two entries demonstrate how to share a directory so that two
+; users can place files there that will be owned by the specific users. In this
+; setup, the directory should be writable by both users and should have the
+; sticky bit set on it to prevent abuse. Obviously this could be extended to
+; as many users as required.
+;[myshare]
+; comment = Mary's and Fred's stuff
+; path = /usr/somewhere/shared
+; valid users = mary fred
+; public = no
+; writable = yes
+; printable = no
+; create mask = 0765
+
+
+
+
diff --git a/examples/smb.conf.default b/examples/smb.conf.default
new file mode 100644
index 00000000000..839fede1233
--- /dev/null
+++ b/examples/smb.conf.default
@@ -0,0 +1,273 @@
+# This is the main Samba configuration file. You should read the
+# smb.conf(5) manual page in order to understand the options listed
+# here. Samba has a huge number of configurable options (perhaps too
+# many!) most of which are not shown in this example
+#
+# For a step to step guide on installing, configuring and using samba,
+# read the Samba HOWTO Collection.
+#
+# Any line which starts with a ; (semi-colon) or a # (hash)
+# is a comment and is ignored. In this example we will use a #
+# for commentry and a ; for parts of the config file that you
+# may wish to enable
+#
+# NOTE: Whenever you modify this file you should run the command "testparm"
+# to check that you have not made any basic syntactic errors.
+#
+#======================= Global Settings =====================================
+[global]
+
+# workgroup = NT-Domain-Name or Workgroup-Name, eg: REDHAT4
+ workgroup = MYGROUP
+
+# server string is the equivalent of the NT Description field
+ server string = Samba Server
+
+# Security mode. Defines in which mode Samba will operate. Possible
+# values are share, user, server, domain and ads. Most people will want
+# user level security. See the HOWTO Collection for details.
+ security = user
+
+# This option is important for security. It allows you to restrict
+# connections to machines which are on your local network. The
+# following example restricts access to two C class networks and
+# the "loopback" interface. For more examples of the syntax see
+# the smb.conf man page
+; hosts allow = 192.168.1. 192.168.2. 127.
+
+# If you want to automatically load your printer list rather
+# than setting them up individually then you'll need this
+ load printers = yes
+
+# you may wish to override the location of the printcap file
+; printcap name = /etc/printcap
+
+# on SystemV system setting printcap name to lpstat should allow
+# you to automatically obtain a printer list from the SystemV spool
+# system
+; printcap name = lpstat
+
+# It should not be necessary to specify the print system type unless
+# it is non-standard. Currently supported print systems include:
+# bsd, cups, sysv, plp, lprng, aix, hpux, qnx
+; printing = cups
+
+# Uncomment this if you want a guest account, you must add this to /etc/passwd
+# otherwise the user "nobody" is used
+; guest account = pcguest
+
+# this tells Samba to use a separate log file for each machine
+# that connects
+ log file = /usr/local/samba/var/log.%m
+
+# Put a capping on the size of the log files (in Kb).
+ max log size = 50
+
+# Use password server option only with security = server
+# The argument list may include:
+# password server = My_PDC_Name [My_BDC_Name] [My_Next_BDC_Name]
+# or to auto-locate the domain controller/s
+# password server = *
+; password server = <NT-Server-Name>
+
+# Use the realm option only with security = ads
+# Specifies the Active Directory realm the host is part of
+; realm = MY_REALM
+
+# Backend to store user information in. New installations should
+# use either tdbsam or ldapsam. smbpasswd is available for backwards
+# compatibility. tdbsam requires no further configuration.
+; passdb backend = tdbsam
+
+# Using the following line enables you to customise your configuration
+# on a per machine basis. The %m gets replaced with the netbios name
+# of the machine that is connecting.
+# Note: Consider carefully the location in the configuration file of
+# this line. The included file is read at that point.
+; include = /usr/local/samba/lib/smb.conf.%m
+
+# Most people will find that this option gives better performance.
+# See the chapter 'Samba performance issues' in the Samba HOWTO Collection
+# and the manual pages for details.
+# You may want to add the following on a Linux system:
+# SO_RCVBUF=8192 SO_SNDBUF=8192
+ socket options = TCP_NODELAY
+
+# Configure Samba to use multiple interfaces
+# If you have multiple network interfaces then you must list them
+# here. See the man page for details.
+; interfaces = 192.168.12.2/24 192.168.13.2/24
+
+# Browser Control Options:
+# set local master to no if you don't want Samba to become a master
+# browser on your network. Otherwise the normal election rules apply
+; local master = no
+
+# OS Level determines the precedence of this server in master browser
+# elections. The default value should be reasonable
+; os level = 33
+
+# Domain Master specifies Samba to be the Domain Master Browser. This
+# allows Samba to collate browse lists between subnets. Don't use this
+# if you already have a Windows NT domain controller doing this job
+; domain master = yes
+
+# Preferred Master causes Samba to force a local browser election on startup
+# and gives it a slightly higher chance of winning the election
+; preferred master = yes
+
+# Enable this if you want Samba to be a domain logon server for
+# Windows95 workstations.
+; domain logons = yes
+
+# if you enable domain logons then you may want a per-machine or
+# per user logon script
+# run a specific logon batch file per workstation (machine)
+; logon script = %m.bat
+# run a specific logon batch file per username
+; logon script = %U.bat
+
+# Where to store roving profiles (only for Win95 and WinNT)
+# %L substitutes for this servers netbios name, %U is username
+# You must uncomment the [Profiles] share below
+; logon path = \\%L\Profiles\%U
+
+# Windows Internet Name Serving Support Section:
+# WINS Support - Tells the NMBD component of Samba to enable it's WINS Server
+; wins support = yes
+
+# WINS Server - Tells the NMBD components of Samba to be a WINS Client
+# Note: Samba can be either a WINS Server, or a WINS Client, but NOT both
+; wins server = w.x.y.z
+
+# WINS Proxy - Tells Samba to answer name resolution queries on
+# behalf of a non WINS capable client, for this to work there must be
+# at least one WINS Server on the network. The default is NO.
+; wins proxy = yes
+
+# DNS Proxy - tells Samba whether or not to try to resolve NetBIOS names
+# via DNS nslookups. The default is NO.
+ dns proxy = no
+
+# These scripts are used on a domain controller or stand-alone
+# machine to add or delete corresponding unix accounts
+; add user script = /usr/sbin/useradd %u
+; add group script = /usr/sbin/groupadd %g
+; add machine script = /usr/sbin/adduser -n -g machines -c Machine -d /dev/null -s /bin/false %u
+; delete user script = /usr/sbin/userdel %u
+; delete user from group script = /usr/sbin/deluser %u %g
+; delete group script = /usr/sbin/groupdel %g
+
+
+#============================ Share Definitions ==============================
+[homes]
+ comment = Home Directories
+ browseable = no
+ writable = yes
+
+# Un-comment the following and create the netlogon directory for Domain Logons
+; [netlogon]
+; comment = Network Logon Service
+; path = /usr/local/samba/lib/netlogon
+; guest ok = yes
+; writable = no
+; share modes = no
+
+
+# Un-comment the following to provide a specific roving profile share
+# the default is to use the user's home directory
+;[Profiles]
+; path = /usr/local/samba/profiles
+; browseable = no
+; guest ok = yes
+
+
+# NOTE: If you have a BSD-style print system there is no need to
+# specifically define each individual printer
+[printers]
+ comment = All Printers
+ path = /usr/spool/samba
+ browseable = no
+# Set public = yes to allow user 'guest account' to print
+ guest ok = no
+ writable = no
+ printable = yes
+
+# This one is useful for people to share files
+;[tmp]
+; comment = Temporary file space
+; path = /tmp
+; read only = no
+; public = yes
+
+# A publicly accessible directory, but read only, except for people in
+# the "staff" group
+;[public]
+; comment = Public Stuff
+; path = /home/samba
+; public = yes
+; writable = yes
+; printable = no
+; write list = @staff
+
+# Other examples.
+#
+# A private printer, usable only by fred. Spool data will be placed in fred's
+# home directory. Note that fred must have write access to the spool directory,
+# wherever it is.
+;[fredsprn]
+; comment = Fred's Printer
+; valid users = fred
+; path = /homes/fred
+; printer = freds_printer
+; public = no
+; writable = no
+; printable = yes
+
+# A private directory, usable only by fred. Note that fred requires write
+# access to the directory.
+;[fredsdir]
+; comment = Fred's Service
+; path = /usr/somewhere/private
+; valid users = fred
+; public = no
+; writable = yes
+; printable = no
+
+# a service which has a different directory for each machine that connects
+# this allows you to tailor configurations to incoming machines. You could
+# also use the %U option to tailor it by user name.
+# The %m gets replaced with the machine name that is connecting.
+;[pchome]
+; comment = PC Directories
+; path = /usr/pc/%m
+; public = no
+; writable = yes
+
+# A publicly accessible directory, read/write to all users. Note that all files
+# created in the directory by users will be owned by the default user, so
+# any user with access can delete any other user's files. Obviously this
+# directory must be writable by the default user. Another user could of course
+# be specified, in which case all files would be owned by that user instead.
+;[public]
+; path = /usr/somewhere/else/public
+; public = yes
+; only guest = yes
+; writable = yes
+; printable = no
+
+# The following two entries demonstrate how to share a directory so that two
+# users can place files there that will be owned by the specific users. In this
+# setup, the directory should be writable by both users and should have the
+# sticky bit set on it to prevent abuse. Obviously this could be extended to
+# as many users as required.
+;[myshare]
+; comment = Mary's and Fred's stuff
+; path = /usr/somewhere/shared
+; valid users = mary fred
+; public = no
+; writable = yes
+; printable = no
+; create mask = 0765
+
+
diff --git a/examples/svr4-startup/README b/examples/svr4-startup/README
new file mode 100644
index 00000000000..8ed9f744770
--- /dev/null
+++ b/examples/svr4-startup/README
@@ -0,0 +1,24 @@
+Hi and thanks for this great software.
+
+Solaris (and other sysv) machines have a standardized way of
+starting and shutting down services (you may well know that already).
+
+Here's a piece of code one could place under /etc/init.d
+and create appropriate link from, say
+
+ /etc/rc2.d/S99samba.server
+
+to make smbd start and stop automatically with system bootups and
+shutdowns. Each one should edit the lines containing the
+daemon calls to agree with his/her installation (the code below
+works with the defaults) and workgroup setup (we use the -G and -n
+options).
+
+
+I hope this will be of use --- at least it is for me.
+
+Yours,
+
+Timo Knuutila
+knuutila@cs.utu.fi
+
diff --git a/examples/svr4-startup/samba.server b/examples/svr4-startup/samba.server
new file mode 100755
index 00000000000..0a47fdb10ce
--- /dev/null
+++ b/examples/svr4-startup/samba.server
@@ -0,0 +1,38 @@
+#!/bin/sh
+#ident "@(#)samba.server 1.0 96/06/19 TK" /* SVr4.0 1.1.13.1*/
+#
+# Please send info on modifications to knuutila@cs.utu.fi
+#
+# This file should have uid root, gid sys and chmod 744
+#
+if [ ! -d /usr/bin ]
+then # /usr not mounted
+ exit
+fi
+
+killproc() { # kill the named process(es)
+ pid=`/usr/bin/ps -e |
+ /usr/bin/grep -w $1 |
+ /usr/bin/sed -e 's/^ *//' -e 's/ .*//'`
+ [ "$pid" != "" ] && kill $pid
+}
+
+# Start/stop processes required for samba server
+
+case "$1" in
+
+'start')
+#
+# Edit these lines to suit your installation (paths, workgroup, host)
+#
+ /opt/samba/bin/smbd -D -s/opt/samba/smb.conf
+ /opt/samba/bin/nmbd -D -l/opt/samba/log -s/opt/samba/smb.conf
+ ;;
+'stop')
+ killproc nmbd
+ killproc smbd
+ ;;
+*)
+ echo "Usage: /etc/init.d/samba.server { start | stop }"
+ ;;
+esac
diff --git a/examples/thoralf/smb.conf b/examples/thoralf/smb.conf
new file mode 100644
index 00000000000..f9f147474a8
--- /dev/null
+++ b/examples/thoralf/smb.conf
@@ -0,0 +1,152 @@
+; Configuration file for smbd (Samba 1.9.15p8)
+; created by Thoralf Freitag. Send comments to:
+; <Thoralf.Freitag@remserv.rz.fhtw-berlin.de> or
+; <Thoralf.Freitag@t-online.de>
+; last edit 24.04.1995 01:11
+;
+;
+
+[global]
+
+ protocol = NT1
+ ;long filenames for win95
+ mangle case = yes
+ ;lower and upper letters
+ mangled names = yes
+ default case = lower
+ case sensitive = no
+ preserve case = yes
+ short preserve case = yes
+
+ printing = bsd
+ printcap name = /etc/printcap
+ lpq cache time = 0
+ workgroup = WORKGROUP
+ admin users = su
+ ;su is allowed to do all !!!
+ guest account = ftp
+ ;guest is same as user ftp
+ default service = reference
+ ;is possibly helpful to browsing under win 95
+ os level = 2
+ log file = /var/adm/log.smb
+ max log size = 10
+ debug level = 1
+ share modes = yes
+ lock directory = /var/adm
+
+[JP_360_raw]
+ comment = Networkprinter queue for Olivetti JP 360 (untreated RAW format)
+ browseable = yes
+ available = yes
+ public = no
+ force user = root
+ writable = no
+ printable = yes
+ printer name = samba
+ ;samba is an alias name for an raw_printer in your /etc/printcap
+ path = /samba/tmp
+ create mode = 0700
+
+[JP_360_mono]
+ comment = Networkprinter queue for Olivetti JP 360 Mono (with apsfilter)
+ browseable = yes
+ available = yes
+ public = no
+ force user = root
+ writable = no
+ printable = yes
+ printer name = lp
+ ;lp means the standard printer in your /etc/printcap
+ path = /samba/tmp
+ create mode = 0700
+
+[JP_360_color]
+ comment = Networkprinter queue for Olivetti JP 360 Color (with apsfilter)
+ browseable = yes
+ available = yes
+ public = no
+ force user = root
+ writable = no
+ printable = yes
+ printer name = lp4
+ ;my printer need this to print with his color cartridge
+ ;--> the lpd is drive to the printer as an color printer
+ path = /samba/tmp
+ create mode = 0700
+
+[tmp]
+ comment = the garbage dump
+ browseable = yes
+ available = yes
+ public = yes
+ read only = no
+ printable = no
+ path = /samba/tmp
+ create mask = 0777
+
+[transfer]
+ comment = the market place
+ browseable = yes
+ available = yes
+ public = yes
+ read only = no
+ printable = no
+ path = /samba/transfer
+ create mask = 0777
+
+[homes]
+ comment = home directories
+ browseable = no
+ ;ONLY the home-dirs are visible, not the service itself
+ available = yes
+ guest ok = no
+ read only = no
+ printable = no
+ create mode = 0700
+
+[install]
+ comment = all of the many install files
+ browsable = yes
+ available = yes
+ public = no
+ username = @root, @users
+ writable = yes
+ read list = @users
+ printable = no
+ path = /samba/install
+ create mode = 0755
+
+[doc-help]
+ comment = documentations, helpfiles, FAQ's
+ browsable = yes
+ available = yes
+ public = no
+ username = @root, @users
+ writable = yes
+ read list = @users
+ printable = no
+ path = /samba/doc
+ create mode = 0755
+
+[cd_rom_2]
+ comment = the CD in the CD-ROM drive on PANDORA
+ browsable = yes
+ available = yes
+ public = yes
+ writable = no
+ printable = no
+ path = /cdrom
+
+[reference]
+ ;the default, if invalid accesses
+ comment = PANDORA: Samba LAN manager
+ browsable = yes
+ ;only as an hint
+ available = no
+ ;however no access possible
+ public = yes
+ writable = no
+ printable = no
+ path = /samba/tmp
+
diff --git a/examples/tridge/README b/examples/tridge/README
new file mode 100644
index 00000000000..11c72f20b3a
--- /dev/null
+++ b/examples/tridge/README
@@ -0,0 +1,8 @@
+This is the configuration I use at home. I have 2 client PCs, one
+running Win95, one running alternately WfWg and NTAS3.5. My server is
+a 486dx2-66 Linux box.
+
+Note that I use the %a and %m macros to load smb.conf extensions
+particular to machines and architectures. This gives me a lot of
+flexibility in how I handle each of the machines.
+
diff --git a/examples/tridge/smb.conf b/examples/tridge/smb.conf
new file mode 100644
index 00000000000..a2f269f3b76
--- /dev/null
+++ b/examples/tridge/smb.conf
@@ -0,0 +1,101 @@
+[global]
+ config file = /usr/local/samba/smb.conf.%m
+ status = yes
+ security = user
+ encrypt passwords = yes
+ server string = Tridge (%v,%h)
+ load printers = yes
+ log level = 1
+ log file = /usr/local/samba/var/log.%m
+ guest account = pcguest
+ hosts allow = 192.0.2. localhost
+ password level = 2
+ auto services = tridge susan
+ message command = csh -c '/usr/bin/X11/xedit -display :0 %s;rm %s' &
+ read prediction = yes
+ socket options = TCP_NODELAY
+ valid chars = ö:Ö å:Å ä:Ä
+ share modes = yes
+ locking = yes
+ strict locking = yes
+ keepalive = 30
+ include = /usr/local/samba/lib/smb.conf.%m
+ include = /usr/local/samba/lib/smb.conf.%a
+
+
+[uniprint]
+ comment = University Printing
+ path = /home/susan/print
+ user = susan
+ postscript = yes
+ print ok = yes
+ print command = xmenu -heading "%s" OK&
+
+[testprn]
+ comment = Test printer
+ path = /tmp
+ user = susan
+ print ok = yes
+ print command = cp %s /tmp/smb.%U.prn
+ lpq command = cat /tmp/xxyz
+
+[amd]
+ comment = amd area
+ path = /mount
+ force user = tridge
+ read only = no
+
+[homes]
+ browseable = no
+ guest ok = no
+ read only = no
+ create mask = 0755
+
+[printers]
+ browseable = no
+ comment = Printer in Printcap
+ guest ok = no
+ path = /tmp
+ read only = no
+ print ok = yes
+
+[dos]
+ browseable = yes
+ comment = Dos Files
+ force group = samba
+ create mode = 0775
+ path = /home/tridge/dos
+ copy = homes
+
+[msoffice]
+ browseable = yes
+ comment = Microsoft Office
+ force group = samba
+ create mode = 0775
+ path = /data/msoffice
+ read only = yes
+
+[root]
+ comment = Root Dir
+ copy = dos
+ path = /
+ dont descend = /proc ./proc /etc
+
+[tmp]
+ comment = tmp files
+ copy = dos
+ path = /tmp
+ read only = no
+
+
+[cdrom]
+ comment = Tridge's CdRom
+ path = /mount/cdrom
+ read only = yes
+ locking = no
+
+[data]
+ comment = Data Partition
+ path = /data
+ read only = yes
+ guest ok = yes
diff --git a/examples/tridge/smb.conf.WinNT b/examples/tridge/smb.conf.WinNT
new file mode 100644
index 00000000000..f490f830ca7
--- /dev/null
+++ b/examples/tridge/smb.conf.WinNT
@@ -0,0 +1,14 @@
+#log level = 4
+#readraw = no
+#writeraw = no
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/tridge/smb.conf.fjall b/examples/tridge/smb.conf.fjall
new file mode 100644
index 00000000000..76f4d0e3cad
--- /dev/null
+++ b/examples/tridge/smb.conf.fjall
@@ -0,0 +1,21 @@
+;log level = 4
+;readraw = no
+;writeraw = no
+;password level = 4
+;mangled map = (;1 )
+;protocol = lanman1
+;user = susan
+;getwd cache = no
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/tridge/smb.conf.lapland b/examples/tridge/smb.conf.lapland
new file mode 100644
index 00000000000..f490f830ca7
--- /dev/null
+++ b/examples/tridge/smb.conf.lapland
@@ -0,0 +1,14 @@
+#log level = 4
+#readraw = no
+#writeraw = no
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/tridge/smb.conf.vittjokk b/examples/tridge/smb.conf.vittjokk
new file mode 100644
index 00000000000..919ecd15420
--- /dev/null
+++ b/examples/tridge/smb.conf.vittjokk
@@ -0,0 +1,14 @@
+;protocol = LANMAN2
+log level = 2
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/validchars/msdos70.out b/examples/validchars/msdos70.out
new file mode 100644
index 00000000000..a722b83604f
--- /dev/null
+++ b/examples/validchars/msdos70.out
@@ -0,0 +1,257 @@
+255: ok
+254: ok
+253: ok
+252: ok
+251: ok
+250: ok
+249: ok
+248: ok
+247: ok
+246: ok
+245: ok
+244: ok
+243: ok
+242: ok
+241: ok
+240: ok
+239: ok
+238: ok
+237: ok
+236: 237
+235: ok
+234: ok
+233: ok
+232: ok
+231: 232
+230: ok
+229: ok
+228: 229
+227: ok
+226: ok
+225: ok
+224: ok
+223: ok
+222: ok
+221: ok
+220: ok
+219: ok
+218: ok
+217: ok
+216: ok
+215: ok
+214: ok
+213: 73
+212: ok
+211: ok
+210: ok
+209: ok
+208: 209
+207: ok
+206: ok
+205: ok
+204: ok
+203: ok
+202: ok
+201: ok
+200: ok
+199: ok
+198: 199
+197: ok
+196: ok
+195: ok
+194: ok
+193: ok
+192: ok
+191: ok
+190: ok
+189: ok
+188: ok
+187: ok
+186: ok
+185: ok
+184: ok
+183: ok
+182: ok
+181: ok
+180: ok
+179: ok
+178: ok
+177: ok
+176: ok
+175: ok
+174: ok
+173: ok
+172: ok
+171: ok
+170: ok
+169: ok
+168: ok
+167: ok
+166: ok
+165: ok
+164: 165
+163: 233
+162: 224
+161: 214
+160: 181
+159: ok
+158: ok
+157: ok
+156: ok
+155: 157
+154: ok
+153: ok
+152: length 0
+151: 235
+150: 234
+149: 227
+148: 153
+147: 226
+146: ok
+145: 146
+144: ok
+143: ok
+142: ok
+141: 222
+140: 215
+139: 216
+138: 212
+137: 211
+136: 210
+135: 128
+134: 143
+133: 183
+132: 142
+131: 182
+130: 144
+129: 154
+128: ok
+127: ok
+126: ok
+125: ok
+124: open unlink 0
+123: ok
+122: 90
+121: 89
+120: 88
+119: 87
+118: 86
+117: 85
+116: 84
+115: 83
+114: 82
+113: 81
+112: 80
+111: 79
+110: 78
+109: 77
+108: 76
+107: 75
+106: 74
+105: 73
+104: 72
+103: 71
+102: 70
+101: 69
+100: 68
+99: 67
+98: 66
+97: 65
+96: ok
+95: ok
+94: ok
+93: open unlink 0
+92: open unlink 0
+91: open unlink 0
+90: ok
+89: ok
+88: ok
+87: ok
+86: ok
+85: ok
+84: ok
+83: ok
+82: ok
+81: ok
+80: ok
+79: ok
+78: ok
+77: ok
+76: ok
+75: ok
+74: ok
+73: ok
+72: ok
+71: ok
+70: ok
+69: ok
+68: ok
+67: ok
+66: ok
+65: ok
+64: ok
+63: open unlink 0
+62: open unlink 0
+61: open unlink 0
+60: open unlink 0
+59: open unlink 0
+58: open unlink 0
+57: ok
+56: ok
+55: ok
+54: ok
+53: ok
+52: ok
+51: ok
+50: ok
+49: ok
+48: ok
+47: open unlink 0
+46: open unlink 0
+45: ok
+44: open unlink 0
+43: open unlink 0
+42: open unlink 0
+41: ok
+40: ok
+39: ok
+38: ok
+37: ok
+36: ok
+35: ok
+34: open unlink 0
+33: ok
+32: open unlink 0
+31: open unlink 0
+30: open unlink 0
+29: open unlink 0
+28: open unlink 0
+27: open unlink 0
+26: open unlink 0
+25: open unlink 0
+24: open unlink 0
+23: open unlink 0
+22: open unlink 0
+21: open unlink 0
+20: open unlink 0
+19: open unlink 0
+18: open unlink 0
+17: open unlink 0
+16: open unlink 0
+15: open unlink 0
+14: open unlink 0
+13: open unlink 0
+12: open unlink 0
+11: open unlink 0
+10: open unlink 0
+9: open unlink 0
+8: open unlink 0
+7: open unlink 0
+6: open unlink 0
+5: open unlink 0
+4: open unlink 0
+3: open unlink 0
+2: open unlink 0
+1: open unlink 0
+
+ valid chars = 73:213 213:73 73:73 33 35 36 37 38 39 40 41 45 48 49 50 51 52 53 54 55 56 57 64 97:65 98:66 99:67 100:68 101:69 102:70 103:71 104:72 105:73 106:74 107:75 108:76 109:77 110:78 111:79 112:80 113:81 114:82 115:83 116:84 117:85 118:86 119:87 120:88 121:89 122:90 94 95 96 123 125 126 127 135:128 132:142 134:143 130:144 145:146 148:153 129:154 156 155:157 158 159 164:165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 160:181 131:182 133:183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198:199 200 201 202 203 204 205 206 207 208:209 136:210 137:211 138:212 161:214 140:215 139:216 217 218 219 220 221 141:222 223 162:224 225 147:226 149:227 228:229 230 231:232 163:233 150:234 151:235 236:237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
diff --git a/examples/validchars/nwdos70.out b/examples/validchars/nwdos70.out
new file mode 100644
index 00000000000..b0dbf628531
--- /dev/null
+++ b/examples/validchars/nwdos70.out
@@ -0,0 +1,257 @@
+255: ok
+254: ok
+253: ok
+252: ok
+251: ok
+250: ok
+249: ok
+248: ok
+247: ok
+246: ok
+245: ok
+244: ok
+243: ok
+242: ok
+241: ok
+240: ok
+239: ok
+238: ok
+237: ok
+236: ok
+235: ok
+234: ok
+233: ok
+232: ok
+231: ok
+230: ok
+229: ok
+228: ok
+227: ok
+226: ok
+225: ok
+224: ok
+223: ok
+222: ok
+221: ok
+220: ok
+219: ok
+218: ok
+217: ok
+216: ok
+215: ok
+214: ok
+213: ok
+212: ok
+211: ok
+210: ok
+209: ok
+208: ok
+207: ok
+206: ok
+205: ok
+204: ok
+203: ok
+202: ok
+201: ok
+200: ok
+199: ok
+198: ok
+197: ok
+196: ok
+195: ok
+194: ok
+193: ok
+192: ok
+191: ok
+190: ok
+189: ok
+188: ok
+187: ok
+186: ok
+185: ok
+184: ok
+183: ok
+182: ok
+181: ok
+180: ok
+179: ok
+178: ok
+177: ok
+176: ok
+175: ok
+174: ok
+173: ok
+172: ok
+171: ok
+170: ok
+169: ok
+168: ok
+167: ok
+166: ok
+165: ok
+164: 165
+163: 85
+162: 79
+161: 73
+160: 65
+159: ok
+158: ok
+157: ok
+156: ok
+155: ok
+154: ok
+153: ok
+152: 89
+151: 85
+150: 85
+149: 79
+148: 153
+147: 79
+146: ok
+145: 146
+144: ok
+143: ok
+142: ok
+141: 73
+140: 73
+139: 73
+138: 69
+137: 69
+136: 69
+135: 128
+134: 143
+133: 65
+132: 142
+131: 65
+130: 69
+129: 154
+128: ok
+127: ok
+126: ok
+125: ok
+124: open unlink 0
+123: ok
+122: 90
+121: 89
+120: 88
+119: 87
+118: 86
+117: 85
+116: 84
+115: 83
+114: 82
+113: 81
+112: 80
+111: 79
+110: 78
+109: 77
+108: 76
+107: 75
+106: 74
+105: 73
+104: 72
+103: 71
+102: 70
+101: 69
+100: 68
+99: 67
+98: 66
+97: 65
+96: ok
+95: ok
+94: ok
+93: open unlink 0
+92: open unlink 0
+91: open unlink 0
+90: ok
+89: ok
+88: ok
+87: ok
+86: ok
+85: ok
+84: ok
+83: ok
+82: ok
+81: ok
+80: ok
+79: ok
+78: ok
+77: ok
+76: ok
+75: ok
+74: ok
+73: ok
+72: ok
+71: ok
+70: ok
+69: ok
+68: ok
+67: ok
+66: ok
+65: ok
+64: ok
+63: open unlink 0
+62: open unlink 0
+61: open unlink 0
+60: open unlink 0
+59: open unlink 0
+58: open unlink 0
+57: ok
+56: ok
+55: ok
+54: ok
+53: ok
+52: ok
+51: ok
+50: ok
+49: ok
+48: ok
+47: open unlink 0
+46: open unlink 0
+45: ok
+44: open unlink 0
+43: open unlink 0
+42: open unlink 0
+41: ok
+40: ok
+39: ok
+38: ok
+37: ok
+36: ok
+35: ok
+34: open unlink 0
+33: ok
+32: length 0
+31: open unlink 0
+30: open unlink 0
+29: open unlink 0
+28: open unlink 0
+27: open unlink 0
+26: open unlink 0
+25: open unlink 0
+24: open unlink 0
+23: open unlink 0
+22: open unlink 0
+21: open unlink 0
+20: open unlink 0
+19: open unlink 0
+18: open unlink 0
+17: open unlink 0
+16: open unlink 0
+15: open unlink 0
+14: open unlink 0
+13: open unlink 0
+12: open unlink 0
+11: open unlink 0
+10: open unlink 0
+9: open unlink 0
+8: open unlink 0
+7: open unlink 0
+6: open unlink 0
+5: open unlink 0
+4: open unlink 0
+3: open unlink 0
+2: open unlink 0
+1: open unlink 0
+
+ valid chars = 69:130 130:69 69:69 65:131 131:65 65:65 65:133 133:65 65:65 69:136 136:69 69:69 69:137 137:69 69:69 69:138 138:69 69:69 73:139 139:73 73:73 73:140 140:73 73:73 73:141 141:73 73:73 79:147 147:79 79:79 79:149 149:79 79:79 85:150 150:85 85:85 85:151 151:85 85:85 89:152 152:89 89:89 65:160 160:65 65:65 73:161 161:73 73:73 79:162 162:79 79:79 85:163 163:85 85:85 33 35 36 37 38 39 40 41 45 48 49 50 51 52 53 54 55 56 57 64 97:65 98:66 99:67 100:68 101:69 102:70 103:71 104:72 105:73 106:74 107:75 108:76 109:77 110:78 111:79 112:80 113:81 114:82 115:83 116:84 117:85 118:86 119:87 120:88 121:89 122:90 94 95 96 123 125 126 127 135:128 132:142 134:143 144 145:146 148:153 129:154 155 156 157 158 159 164:165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
diff --git a/examples/validchars/readme b/examples/validchars/readme
new file mode 100644
index 00000000000..6487fbd766a
--- /dev/null
+++ b/examples/validchars/readme
@@ -0,0 +1,101 @@
+Note: All files in this directory are DOS formatted (CRLF line terminator).
+
+!!! VIRUS WARNING !!! I do not know if VALIDCHR.COM is virus free !!!
+I think that my system is virus free here because I do not run any games
+or other copied software. I only run Shareware/Freeware etc. from CD-ROMs
+or from registered disks, however I do not use viral scanners because
+I have not registered any (I consider `having no sex' is better than
+`testing for AIDS on a regular basis', if you know what I mean).
+
+This is VALIDCHR, a little DOS program I wrote to create
+an apropriate `valid chars =' config parameter.
+It is freeware and is thought to be distributed freely with Samba.
+
+WARNING:
+ Your SMB driver may use another character map as the one VALIDCHR
+ sees. The only way you can tell this is that some file names fail.
+ Under Win95 everything is fine, though.
+
+Usage:
+ c:
+ mkdir junk_dir
+ cd junk_dir
+ a:validchr > a:output.log
+ cd ..
+ rmdir junk_dir
+
+Siedeffects:
+ Files named *.TST may be deleted.
+
+Verification:
+ For diagnostic purpose you can run VALIDCHR on a Samba mounted drive.
+ Then you can use unix diff to compare the output of the network and
+ the hard drive. These two outputs usually differ! However there
+ should be few differences. I get following on Win95 (c: visa e:)
+ 104c104
+ < 152: length 0
+ ---
+ > 152: 95
+ (diff line for `valid chars =' deleted because it's uninteresting)
+ You can see, `y diaresis' can be mapped on the network drive while
+ it cannot be mapped on the hard drive. Everything else is identical.
+ However this gives a hint that one can improve the mapping.
+
+Bugs:
+ Yes, probably some.
+
+
+VALIDCHR must be run on the system which character mapping should be probed.
+It must be run on the hard drive for this. VALIDCHR ALTERS THE CURRENT
+DIRECTORY AND REMOVES SOME FILES, SO ALWAYS RUN IT IN A junk DIRECTORY !!!
+You should redirect the output of VALIDCHR. At the end of the output is a
+line like
+ valid chars = x:y y:x x:x ... a:b c ...
+which is suitable for your smb.conf file. (you should remove the DOS CR
+character, because DOS uses CRLF while Unix uses LF only.)
+
+Note that some mappings at the beginning of the `valid chars =' line like
+A:B B:A B:B
+might look a little bit strange to you, however sometimes character A
+has to be mapped to character B independently of a default mapping
+to uppercase or lowercase while character B must not be touched. I found
+this out the hard way ... Consider it a crude workaround, because Samba
+lacks the possibility to map characters in one direction only!
+
+VALIDCHR usually issues one warning for character 32.
+You may ignore these and any other warnings.
+
+VALIDCHR does not test for character NUL (this is the directory end marker).
+
+validchr.c is the source code to validchr.com
+ You may do anything with the source code (copy, change, sell, burn)
+validchr.com is a Borland C compiled binary.
+ Beware, it may contain a virus (if my system contains one).
+nwdos70.out is the output of an VALIDCHR-run under Novell DOS 7.0
+ while no codepage (no display.sys) was active.
+msdos70.out is the output of an VALIDCHR-run under MS-DOS 7.0 (Win95 DOS)
+ while codepage 850 was active.
+
+I have no other MS-DOS systems at home currently.
+(I have access to MS-DOS 3.0, 3.2, 3.3, 5.0 and 6.22, however I have no time
+ to run VALIDCHR there)
+
+Some words to the output
+(for people not fammiliar with programming language C):
+
+probed_char: [text] mapped_char
+
+probed_char is the character probed to be written to disk
+text may be empty or contain:
+ open File could not be opened.
+ close File could not be closed (should not happen)
+ length File name was shortened (usually occurs on SPC)
+ unlink File cannot be unlinked (usually when open fails)
+mapped_char is the character which is used by MS-DOS in the directory
+ This is usually the uppercase character.
+ The mapped character is 0 if something failed (you may say
+ that the character is not supported)
+
+The last line in the output is documented in the smb.conf manual page ;)
+
+tino@augsburg.net
diff --git a/examples/validchars/validchr.c b/examples/validchars/validchr.c
new file mode 100644
index 00000000000..415546cb841
--- /dev/null
+++ b/examples/validchars/validchr.c
@@ -0,0 +1,123 @@
+/* by tino@augsburg.net
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <dirent.h>
+
+unsigned char
+test(void)
+{
+ DIR *dir;
+ struct dirent *dp;
+ unsigned char c;
+
+ if ((dir=opendir("."))==0)
+ {
+ perror("open .");
+ return 0;
+ }
+ c = 0;
+ while ((dp=readdir(dir))!=0)
+ {
+ size_t len;
+
+ len = strlen(dp->d_name);
+ if (len<4)
+ continue;
+ if (strcmp(dp->d_name+len-4, ".TST"))
+ continue;
+ if (len!=5)
+ {
+ fprintf(stderr, "warning: %s\n", dp->d_name);
+ printf(" length");
+ continue;
+ }
+ if (c)
+ printf(" double%d\n", c);
+ c = dp->d_name[0];
+ }
+ if (closedir(dir))
+ perror("close .");
+ return c;
+}
+
+int
+main(void)
+{
+ char name[256];
+ unsigned char map[256], upper[256], lower[256];
+ int i, j, c;
+ FILE *fd;
+
+ if (test())
+ {
+ printf("There are *.TST files, please remove\n");
+ return 0;
+ }
+ for (i=0; ++i<256; )
+ {
+ lower[i] = i;
+ upper[i] = 0;
+ }
+ for (i=256; --i; )
+ {
+ map[i] = i;
+ strcpy(name, "..TST");
+ name[0] = i;
+ printf("%d:", i);
+ if ((fd=fopen(name, "w"))==0)
+ printf(" open");
+ else
+ fclose(fd);
+ c = test();
+ if (unlink(name))
+ printf(" unlink");
+ if (c==i)
+ printf(" ok");
+ else
+ printf(" %d", c);
+ printf("\n");
+ if (c!=i)
+ {
+ upper[c]++;
+ lower[c] = i;
+ }
+ map[i] = c;
+ }
+
+ /* Uppercase characters are detected above on:
+ * The character is mapped to itself and there is a
+ * character which maps to it.
+ * Lowercase characters are the lowest character pointing to another one.
+ * Else it is a one way character.
+ *
+ * For this reason we have to process the list
+ * 1) for 'one way' characters
+ * 'one way' is something which is no upper and no lower character.
+ * This is an awful, crude and ugly hack due to missing Samba support.
+ * 2) for true uppercase/lowercase characters
+ * 3) for standalone characters
+ * Note that there might be characters which do not fall into 1 to 3.
+ */
+ printf("\n valid chars =");
+ for (i=0; ++i<256; )
+ if (map[i] && map[i]!=i && lower[map[i]]!=i)
+ {
+ if (!upper[i])
+ printf(" %d:%d %d:%d %d:%d", /*1*/
+ map[i], i, i, map[i], map[i], map[i]);
+ else
+ fprintf(stderr, "ignoring map %d->%d because of %d->%d\n",
+ lower[i], i, i, map[i]);
+ }
+ for (i=0; ++i<256; )
+ if (map[i] && map[i]==i)
+ if (upper[i])
+ printf(" %d:%d", lower[i], i); /*2*/
+ else
+ printf(" %d", i); /*3*/
+ printf("\n");
+ return 0;
+}
diff --git a/examples/validchars/validchr.com b/examples/validchars/validchr.com
new file mode 100644
index 00000000000..a5dbb39bedf
--- /dev/null
+++ b/examples/validchars/validchr.com
@@ -0,0 +1,77 @@
+ŒÊ.‰–´0Í!‹.‹,ŽÚ£é Œç ‰ã ‰.ÿ è]¡ã ŽÀ3À‹Ø‹ø¹ÿüò®ãaC&8uö€Í€÷Ù‰á ¹ÓãƒÃƒãø‰å ŒÚ+ê‹>º#ÿs¿‰>º#Ç>'r(>R#r"±ÓïG;ïrƒ>º#tƒ>R#u¿;ïw‹ýëéã‹ßÚ‰÷ ‰û ¡ç +ØŽÀ´JWÍ!_ÓçúŽÒ‹çû3À.Ž–¿î&¹>'+Ïüóªƒ>$#vG€>é r@w€>ê r7¸X»Í!r*´g‹$#Í!r ´H»Í!r@£ÿ HŽÀ´IÍ!r
+¸X»Í!séb´Í‰í ‰ï
+Àt ¸@ŽÀ»p&Æ3í.Ž–¾è&¿î&è©ÿ6ß ÿ6Ý ÿ6Û èóPèy.Ž–VW¾î&¿î&èÊ_^ÃËì´LŠFÍ!¹ºŸ é¸5Í!‰Ë ŒÍ ¸5Í!‰Ï ŒÑ ¸5Í!‰Ó ŒÕ ¸5Í!‰× ŒÙ ¸%ŒÊŽÚº{Í!ø%ÅË Í!¸%ÅÏ Í!¸%ÅÓ Í!¸%Å× Í!ø‹×‹Þ;ßt&€?ÿt&ŠO2í;Ès‹Á‹ÓƒÃëã;×t‹Ú&€?&Æÿt&ÿ_ëÄ&ÿWë½Ã´‹×‹Þ;ßt&€?ÿt &8gr&Šg‹ÓƒÃëå;×t‹Ú&€?&Æÿt&ÿ_ëÇ&ÿWëÀô@»Í!ùº­ .Ž–èêÿ¸PèPU‹ìƒìVW¸!Pè6Y‰Fþ Àu ¸!PèƒY°é‰ÆFýë]VèúY‹øƒÿsëO¸ !P‹ÆÇüÿPèYY Àtë8ƒÿtV¸!P¸"Pèƃĸ!PèmYë€~ýtŠFý´P¸%!PèWYYŠˆFýÿvþètY‹ð Àu–ÿvþè›Y Àt¸0!PèûYŠFýétÿ_^‹å]ÃU‹ììèCÿ
+Àt ¸8!PèY3ÀéaÇFþë†úû‹^þØŠFþˆ†úü‹^þØÆÿFþ‹Fþ=|ÛÇFþéƆúý‹^þØŠFþˆ¸^!P†úþPèêYYŠFþˆ†úþÿvþ¸d!Pè¨YY¸h!P†úþPèÊYY‰Fú Àu ¸j!PèŠëÿvúèYè§þ´‰Fü†úþPèJY Àt¸p!PèeY‹Fü;Fþu
+¸x!PèUYë ÿvü¸|!PèHYY¸€!Pè?Y‹Fü;Fþt†úü‹^üØþ†úû‹^üØŠFþˆ†úý‹^þØŠFüˆÿNþté2ÿ¸‚!PèYÇFþéÒ†úý‹^þØ€?uéÁ†úý‹^þØŠ´;Fþu鬆úý‹^þØŠ´–úû‹؊´;Fþu鋆úü‹^þØŠ´ ÀuJ†úý‹^þØŠ´P†úý‹^þØŠ´P†úý‹^þØŠ´Pÿvþÿvþ†úý‹^þØŠ´P¸”!PèZƒÄë0†úý‹^þØŠ´Pÿvþÿvþ†úû‹^þØŠ´P¸§!P¸"PèwƒÄ ÿFþ‹Fþ=}é ÿÇFþëW†úý‹^þØ€?tI†úý‹^þØŠ´;Fþu7†úü‹^þØ€?tÿvþ†úû‹^þØŠ´P¸Î!PèʃÄë ÿvþ¸Õ!Pè»YYÿFþ‹Fþ=|ž¸Ù!é•ý‹å]ÃU‹ìƒ>Ü! u¸ë‹Ü!Ñã‹F‰‡î&ÿÜ!3À]ÃU‹ì´C2À‹VÍ!r ‹^‰3ÀëPè?]ÃU‹ì´C°‹V‹NÍ!r3ÀëPè$]ÃU‹ì´/Í!S´‹VÍ!´N‹N‹VÍ!œY“´ZÍ!Qr3ÀëSèñ]ÃU‹ì´/Í!S´‹VÍ!´OÍ!œY“´ZÍ!Qr3ÀëSèÄ]ÃÃU‹ìV‹v öuëÿÜ!‹Ü!Ñãÿ—î&ƒ>Ü!uëè¯úÿÞ!èûè¸úƒ~u öuÿà!ÿâ!ÿvè¡úY^]ÂU‹ì3ÀPPÿvè¦ÿ]ÃU‹ì¸P3ÀPÿvè”ÿ]Ã3ÀP¸P3ÀPè…ÿøPP3ÀPèyÿÃU‹ìV‹v ö|ƒþX~¾W‰6T#Š„V#˜‹ðë ÷Þƒþ0éÇT#ÿÿ‰6ë ¸ÿÿ^]ÂU‹ìV‹vVè¼ÿ‹Æ^]ÂU‹ì¸D‹^Í!’%€]ÃU‹ìƒì"VW‹~
+‹^ƒû$wX€ûrS‹F ‹N É}€~t Æ-G÷Ù÷؃ÙvÞã‘+Ò÷ó‘÷óˆFã ëñ+Ò÷óˆF ÀuõNÞ÷ÙÎüNŠ,
+s:ëFªâï°ª‹F
+_^‹å]Â U‹ì3ÀPÿvÿv¸
+P°P°aPèjÿ]ÂU‹ì‹^Ñã§&#ÿý´BŠF
+‹^‹N‹VÍ!rëPèäþ™]ÃU‹ìV‹v‹V öu¾.'ÿv Òu¸°#ë‹ÂPVèÖYYPèÿ¸´#PVèÕYY‹Æ^]ÂU‹ìƒìVW‹v‹~V3ÀPƒ=ÿu¸ë¸‹Pèžÿ‹ðFþPVèTýYY Àt׋Æ_^‹å]º;$#s+‹ÚÑãLJ&#‹Ú±ÓãƇè!ÿ‹ÂÓàä!‹ÚÓ㉇ò!B;$#rÕ è!˜PètþY Àu&æ!ÿý¸P÷æ!t¸ë3ÀP3ÀP¸ä!PèZƒÄ ø!˜Pè@þY Àu&ö!ÿý¸P÷ö!t¸ë3ÀP3ÀP¸ô!Pè&ƒÄÃU‹ì´A‹VÍ!r3ÀëPè´ý]ÊÆèŠÂÔ†àè†à'@'ªÃU‹ìì–VWÇFîÇFìPÇFêëFW¹ÿÿ2Àò®÷ÑI_Ã6ˆGþNìu/SQR†jÿ+ø†jÿPWÿvÿV
+ ÀuÇFêÇFìP~î¾jÿZY[Ãü¾jÿ‰~ü‹~ü‹v¬
+Àt<%t6ˆGþNìîè¬ÿëééÚ‰vð¬<%tç‰~ü3ɉNò‰NþˆNõÇFøÿÿÇFöÿÿë¬2ä‹Ð‹Ø€ë €û`sŠŸÃ#ƒûvéÑã.ÿ§û郀ýwøƒNþëЀýwíƒNþëÅ€ýwâ€~õ+tˆVõ뵃fþßëƒNþ µ맀ýwM÷Fþu)ƒNþµë“é8‹~6‹ƒF€ýs Ày÷؃Nþ‰Føµéoÿ€ýu׉FöþÅébÿ€ýsʵÿFöéUÿ’,0˜€ýwµ‡Fø À|ÑÑà‹ÐÑàÑàÂFøé3ÿ€ýu›‡Fö ÀtµÑà‹ÐÑàÑàÂFöéÿƒNþéeÿNþƒfþïéYÿ·ë
+·³éÚÆFõˆVû3ÒˆVú‹~6‹ë·
+ÆFúˆVû‹~6‹™GG‰v÷Fþt6‹GG‰~~» Àu Òuƒ~öu 6Æ‹ÇëƒNþRPWŠÇ˜PŠFúPSèÝû‹Vö Ò}éòéýˆVû‰v~º‹^6ÿ7CC‰^÷Fþ t6‹CC‰^è˜ý°:ªZèý6ÆÆFúƒfþûNº+ù‡Ï‹Vö;Ñ‹Ñ韉vˆVû‹~6‹ƒF~»2ä6‰¹é‰vˆVû‹~÷Fþ u 6‹=ƒF ÿë 6Ä=ƒFŒÀ Çu¿¼#èPý;Növ‹Nöé…‰vˆVû‹~‹Nö É}¹WQ^»SR¸#FþP‹Fþ©t ¸ƒF
+ëƒF¸Pèò~»÷Fþt‹Vø Ò~èòü&€=-uI+Ñ~‰Vò&€=-t ŠFõ
+ÀtO&ˆƒ~ò~
+‹Nö É}ÿNòèÀü‹÷‹~ü‹^ø¸#Fþ=uŠfû€üou ƒ~òÇFòë€üxt€üXuƒNþ@KKƒnò}ÇFòNò÷Fþu ë° èyüK;Ùö÷Fþ@t °0èhüŠFûèbü‹Vò Ò~'+Ê+Ú&Š<-t< t<+u&¬èCüIK‡Êã°0è8üâù‡Êã+Ù&¬6ˆGþNìè+üâð Û~ ‹Ë° èüâùéUü‰v‹~÷Fþ u 6‹=ƒFë6Ä=ƒF¸P*FìFî&‰÷FþtGG&Çéü‹vð‹~ü°%èÉû¬
+Àuø€~ìP}èÄûƒ~êt¸ÿÿë‹Fî_^‹å]Â^ H “ S Á Î   | A  # '   M ð  ËËËn t U‹ìVW‹~ÿvè‰Y‹ð@PÿvWè ƒÄ‹ÇÆ_^]ÃU‹ì‹F‹Ôê;Âs£ó 3Àë Çë ¸ÿÿ]ÃU‹ì‹F‹Vó ƒÒ‹È ÒuÁr
+;Ìs‡ó ë Çë ¸ÿÿ]ÃU‹ìÿvè¤ÿY]ÃU‹ì‹F™RPè·ÿYY]ú$$ëº)$¹´@»Í!¹'º.$´@Í!é£óU‹ì‹V´DŠF‹^‹N
+Í!r ƒ~u‹ÂëëPèø]ÃVW‹ô‹\ƒër;X$tèBëè_^Ã9V$t#‹wöt‰6X$ë ;6V$t ‹ÞèT‹G£X$ë ‹Þ3À£V$£X$£Z$Sèöþ[Ãÿ;V$t‹w‹¨u‰‹?û‰u‹Þëè2‹?û‹¨tË÷ð‰\‹ß‹;ßt‰>Z$‹w‰u‰|ÃÇZ$Ë6Z$ öt‹|‰\‰]‰‰wÉZ$‰_‰_ÃVW‹ô‹D ÀtRr6%þÿ=s¸ƒ>V$t‹Z$ Ût ‹Ó9s‹_;Úuõèfë!èŠëèë3Àë‹ðƒÆ97séèkÿÿ‹Ã_^ÃP3ÀPPè6þ[[%t 3ÒRPè(þ[[XP3ÛSPèþ[[=ÿÿt‹Ø‰V$‰X$X@‰ƒÃ‹ÃÃ[3ÀÃP3ÛSPèöý[[=ÿÿt‹Ø¡X$‰G‰X$X@‰ƒÃ‹ÃÃX3ÀÃ)‹ó7‹þø@‰‰\‰uƒÆ‹ÆËìSPQPèÿ[‹Ø Àtü‹ø‹vþ‹ ƒÆVƒéÑéó¥‰Fþè$þ[‹^þƒÄ˃Â;Ñw5‹Ñ;X$u‰ÿÃSPèKý[[ë‹ûø‰]+Ð)‹÷ò‰|B‰‹Ë‹ßè7þ‹ÙƒÃÃVWU‹ì‹^‹F
+ Àt7 Ût-ƒë‹I‹ÐƒÂƒâþƒúsº;Êr wƒÃëè‡ÿëèOÿ‹Ãë PèeþëSèý3À[]_^Ãÿ&à&U‹ì‹N´CŠF‹VÍ!r‘ëPèyõ]ÃU‹ì‹V;$#r ¸Pèdõë‹ÚÑãLJ&#RèY]ÃU‹ì´>‹^Í!r ÑãLJ&#3ÀëPè5õ]ÃU‹ìVWÿvèØ Y‹øPèàýY‹ð Àu
+Çë 3ÀëpÿvVè” YY‹ÇO Àt‹ß€8:t€8\t
+€8/t¸\$ë¸a$PVè YY¸/Pè˜ýY‹ø Àu Çë VëW¸PVèÔóƒÄ Àt Vè¥üYWè üY뙉u+ÆE-ÆE.Ý‹Ç_^]ÃU‹ìV‹v€|.ÝuV¸Pÿt+è˜óƒÄÆD-^]ÃU‹ìV‹v€|.Ýt
+Çë 3ÀëŠD-˜ Àu VèžóY ÀuëÆD-‹Æ^]ÃU‹ìV‹v öt€|.Ýt Çë ¸ÿÿëÆD.ÿt+èüYVè üY3À^]ÃU‹ìVW‹~¾ÿÿ ÿtd9}u_ƒ}tƒ=} WèVY ÀuK÷EtÿuèÐûY€}| ŠE˜PèWþY‹ðÇEÇEÇÆEÿƒ} t3ÀPPÿu èÈôPèÝõYÇE ‹Æ_^]ÃU‹ìVW‹~ ÿuènëf9}t¸ÿÿë^ƒ=|)÷Eu
+‹Ç9E
+uFÇ‹Ç9E
+u8‹E‰E
+ë0ë.‹E@‹ð)5P‹E‰E
+PŠE˜Pè
+ƒÄ;Æt ÷EuƒMëŸ3À_^]ÃU‹ìƒìVWÇFþ‹>$#¾ä!ë÷DtVèbÿYÿFþƒÆ‹ÇO Àuç‹Fþ_^‹å]ÃU‹ìƒìVW‹vÇFþ‹ÞFŠŠÁ<ruº¿ë €ùwuºë€ùau º ÇFþ€¿ë3ÀëiŠ F€ù+t€<+u€ùtt€ùbu€ù+uŠ ƒâüƒÊÇFþ€¿€ùtuÊ@ë€ùbuÊ€ë¡N#%À Ћ©€tƒÏ@Çà! ‹^‰‹^‹Fþ‰‹Ç_^‹å]ÂU‹ìƒìV‹v
+ÿvFþPFüPè4ÿ‰D Àt €|}'ÿvü‹Fþ FPÿv苃ĈD
+À} ÆDÿÇD3ÀëAŠD˜Pè'òY ÀtL¸P÷Dt¸ë3ÀP3ÀPVèƒÄ ÀtVè›ýYëÂÇD ‹Æ^‹å]ÂV¾ä!€||¡$#±Óàä!‹ÖƒÆ;Âwç€||3Àë‹Æ^ÃU‹ìèÏÿ‹Ð Àu3Àë Rÿvÿv3ÀPè$ÿ]ÃU‹ì¸×PÿvÿvFPè¤ó]ÃU‹ìVW‹vƒ<}
+‹TB‹úë ‹™3Â+‹Ћø÷D@u,‹L
+ƒ<}ë I‹Ù€?
+uG‹ÂJ Àuðë‹ÙA€?
+uG‹ÂJ Àuð‹Ç_^]ÂU‹ìVW‹v‹~
+Vè7ýY Àt¸ÿÿëGƒÿuƒ<~ Vè|ÿ™)FVd_þÇ‹D‰D
+WÿvÿvŠD˜Pè‰ñƒÄƒúÿu
+=ÿÿu¸ÿÿë3À_^]ÃU‹ìƒìV‹v¸P3À3ÒPRŠD˜PèUñƒÄ‰Vþ‰Füƒúÿu=ÿÿu鄃<}tŠD˜Ñà‹Ø÷‡&#tW¸P3À3ÒPRŠD˜PèñƒÄ‰Vú‰Føƒúÿu=ÿÿtP3ÀPÿvþÿvüŠD˜PèôðƒÄƒúÿu =ÿÿuºÿÿ¸ÿÿë*‹Fú‹Vø‰Fþ‰VüVèþ™FüVþë Vèþ™)FüVþ‹Vþ‹Fü^‹å]ÃU‹ìVW‹~‹v‹NÑéüó¥s¤‹F_^]ÃU‹ì‹N´<‹VÍ!rëPè†ï]ÂU‹ì‹^+É+Ò´@Í!]ÂU‹ìƒìVW‹v‹~÷ÆÀu¡N#%À ð3ÀPÿvè¹ùYY‰Fþ÷Ætx#>P#‹Ç©€u¸Pè.ïƒ~þÿu#ƒ>T#t
+ÿ6T#èïéí÷Ç€t3À븉Fþë ÷Æt7¸PPëÜ÷Æðtÿv3ÀPèOÿ‹ø À}éµWèŒùYëÿvÿvþè6ÿ‹ø À}léœVÿvèYY‹ø À|Z3ÀPWè~öYY‰Fü©€tÎ ÷Æ€t%ÿ P¸PWè[öƒÄë
+÷ÆtWèþþ÷Fþt÷Æt÷Æðt¸PPÿvèÐøƒÄ ÿ|/÷Æt¸ë3À‹Öâÿø ÐR÷Fþt3Àë¸Z ЋßÑ㉗&#‹Ç_^‹å]ÃU‹ìƒì°‹N÷Áu
+°÷Áu°‹V±ð"N
+Á´=Í!r‰Fþ‹F%ÿ¸ €‹^þÑ㉇&#‹FþëPèÛí‹å]ÃU‹ì¡ë ;Æ$}ƒ>ë | ‹ë Ñã‹—f$ëºÃ&Rÿv¸Ñ&P¸"PèTüƒÄ]ÃU‹ì¸×P¸ô!PÿvFPèòï]ÃU‹ìV‹vÿ VŠF˜PèYY^]ÃU‹ìVW‹~ŠF¢<'ƒ=ÿ}:ÿ‹]
+ÿE
+ˆ÷Eué÷€><'
+t
+€><' téæWèžùY ÀuéÚ¸ÿÿéÙéÑ÷Eu÷EuƒMëãMƒ}tEƒ=t WèeùY ÀuÊ‹E÷؉‹]
+ÿE
+ <'ˆ÷Eu鈀><'
+t€><' uzWè2ùY Àtqë•ëmŠE˜Ñà‹Ø÷‡&#t¸P3À3ÒPRŠE˜Pè–íƒÄ€><'
+u÷E@u¸P¸Ú&PŠE˜PèOƒÄ=u¸P¸<'PŠE˜Pè7ƒÄ=t
+÷Eué=ÿ <'´_^]ÃU‹ì¸ô!PÿvèÌþYY]ÃU‹ìƒìVW‹~‹F‰Fþ÷Et)ëW‹^ÿFŠ˜Pè þYY=ÿÿu3ÀéY‹FÿN ÀuÜéI÷E@uéãƒ}uéš‹E;FsQƒ=t Wè=øY ÀuÅŠE˜Ñà‹Ø÷‡&#t¸P3À3ÒPRŠE˜Pè¥ìƒÄÿvÿvŠE˜PènƒÄ;Fuéâë…éÝ‹F|ƒ=u
+¸ÿÿ+E‰ë WèÛ÷Y Àté`ÿÿvÿvÿu
+è¦ûƒÄ‹F‰‹FE
+霊E˜Ñà‹Ø÷‡&#t¸P3À3ÒPRŠE˜Pè!ìƒÄÿvÿvŠE˜PèêƒÄ;Ftaéÿë\ƒ}t=ë/ÿ}‹]
+ÿE
+‹vÿFŠˆ´ëW‹^ÿFŠPèVýYY=ÿÿuéÊþ‹FÿN ÀuÇëÿvÿvŠE˜P膃Ä;Fté¥þ‹Fþ_^‹å]ÂU‹ìVW‹v‹~
+9tu ƒ~ÿÿv¸ÿÿ馃>Þ&uþô!uÇÞ&ëƒ>Ü&u þä!uÇÜ&ƒ<t¸P3À3ÒPRVèjùƒÄ÷Dtÿtè,òYƒdóÇD‹Æ‰D‰D
+ƒ~t> ÿv:ÇÞ!B ƒ~uWèÌòY‰F ÀuéuÿƒLëélÿ‹F‰D
+‰D‰|ƒ~uƒL3À_^]ÃU‹ìVWü‹~‹×2À¹ÿÿò®uÿ‹~¹ÿÿò®÷Ñ+ù‡÷÷Æt¤IÑéó¥s¤’_^]ÃU‹ìVWŒØŽÀü3À‹Ø‹~‹÷2À¹ÿÿò®÷Ñ‹þ‹vó¦ŠDÿŠ]ÿ+Ã_^]ÃU‹ìVWü‹~‹÷2À¹ÿÿò®÷Ñ‹~ó¤‹F_^]ÃU‹ìWŒØŽÀ‹~3Àü¹ÿÿò®‘÷ÐH_]ÃU‹ììˆVW‹~‹v;>$#r
+¸Pèééá‹F@=s3ÀéÓ‹ßÑã÷‡&#t¸P3À3ÒPRWèåéƒÄ‹ßÑã÷‡&#@uÿvVW訃Ä霋ßÑã§&#ÿý‰vú‹F‰FþëMÿNþ‹^úÿFúŠˆFý<
+uÆ FŠFýˆF†xÿ‹Ö+Ðú€|'+ðVPWè[ƒÄ‹Ð;Ætƒúÿu¸ÿÿë=‹F+Fþë1¶xÿƒ~þu©†xÿ+ð‹Æ Àv!V†xÿPWè ƒÄ‹Ð;ÆtƒúÿtÅ‹FÂ+Æë‹F_^‹å]ÃU‹ì‹^Ñã÷‡&#t¸Pë´@‹^‹N‹VÍ!rP‹^Ñã&#XëPèøç]ÃVW3ÿ¾ä!;>$#s÷DtVèÌóYƒÆG;>$#rê_^ÃVW¿¾ä!ë÷DtVè"ôYOƒÆ ÿuì_^ÃBorland C++ - Copyright 1991 Borland Intl.Divide error
+Abnormal program termination
+>'>'.open ..TSTwarning: %s
+ length double%d
+close .There are *.TST files, please remove
+..TST%d:w open unlink ok %d
+
+ valid chars = %d:%d %d:%d %d:%dignoring map %d->%d because of %d->%d
+ %d:%d %d
+‰‰‰ ä!
+ô!"C"B$"```  @ÿÿ),(((((),(((),#,*((((**#(#%(TMP.$$$(null)  
+
+   print scanf : floating point formats not linked
+\*.**.*È$Ð$è$%%%%7%G%\%n%‹%Ÿ%®%Â%Ï%Ð%ß%&&#&4&E&W&i&j&k&l&m&n&o&p&q&r&s&&’&¦&¸&¹&º&»&¼&½&¾&¿&À&Á&Â&0Error 0Invalid function numberNo such file or directoryPath not foundToo many open filesPermission deniedBad file numberMemory arena trashedNot enough memoryInvalid memory block addressInvalid environmentInvalid formatInvalid access codeInvalid dataNo such deviceAttempted to remove current directoryNot same deviceNo more filesInvalid argumentArg list too bigExec format errorCross-device linkMath argumentResult too largeFile already existsPossible deadlockUnknown error%s: %s
diff --git a/examples/wins_hook/README b/examples/wins_hook/README
new file mode 100644
index 00000000000..1147f57e22f
--- /dev/null
+++ b/examples/wins_hook/README
@@ -0,0 +1,8 @@
+This is an example script for doing dynamic DNS updates from the WINS
+database. You use this by putting the full path to the script in the
+"wins hook" option in smb.conf. Remember to mark the script executable
+and to set the right domain at the top of the script.
+
+See the BIND documentation for how to enable dynamic DNS
+updates. Remember to restrict the updates as far as you can to reduce
+the security risks inherent in dynamic DNS.
diff --git a/examples/wins_hook/dns_update b/examples/wins_hook/dns_update
new file mode 100644
index 00000000000..a4c1a79ab9c
--- /dev/null
+++ b/examples/wins_hook/dns_update
@@ -0,0 +1,94 @@
+#!/bin/sh
+#
+# Example script for "wins hook". This attempts to update the DNS with
+# new A records for the NETBIOS name that Samba passes us. We do this
+# the simple way, by deleting all DNS records for the name and then
+# readding all the expected 'A' records.
+#
+# Written by Stephen Rothwell <sfr@linuxcare.com>
+#
+
+#
+# Configurable things
+#
+# The domain in which to create names
+# YOU MUST CHANGE THIS
+# N.B. include the trailing dot
+#
+# It is a good idea to use a subdomain of your primary domain to ensure
+# that rogue machines can't take over (or delete) important names on
+# your network.
+DOMAIN=wins.example.com.
+
+#
+# The DNS TTL to give the records (in seconds)
+#
+TTL=3600
+#
+# NETBIOS name types that we want to create DNS records for:
+# 20 is server
+# 00 is workstation
+# 03 is user
+#
+USEFUL_TYPES="20 00 03"
+#
+# The name of a cache file to use to avoid continual updates
+# of the same name and IP addresses. If you comment this out
+# then the cache is not kept at all.
+#
+#CACHE_FILE=/usr/local/samba/var/wins_update.cache
+
+if [ $# -lt 4 ]; then
+ echo "Usage: $0 op name type ttl [ip_addr ...]" 1>&2
+ echo " op is one of add, refresh, delete" 1>&2
+ echo " name is the NETBIOS name" 1>&2
+ echo " type is the NETBIOS name type" 1>&2
+ echo " ttl is the NETBIOS time to live" 1>&2
+ echo " ip_addr's are the remaining IP addresses for this name" 1>&2
+ exit 1
+fi
+
+NSUPDATE=`which nsupdate`
+[ -x "$NSUPDATE" ] || NSUPDATE=/usr/bin/nsupdate
+[ -x "$NSUPDATE" ] || NSUPDATE=/sbin/nsupdate
+[ -x "$NSUPDATE" ] || NSUPDATE=/usr/sbin/nsupdate
+[ -x "$NSUPDATE" ] || {
+ echo "Cannot find nsupdate." 1>&2
+ exit 1
+}
+
+OP=$1
+NAME=$2
+TYPE=$3
+WINS_TTL=$4
+shift 4
+IP_ADDRS="$@"
+
+do_update=0
+for i in $USEFUL_TYPES
+do
+ [ "$TYPE" = "$i" ] && do_update=1
+done
+[ $do_update = 1 ] || exit 0
+
+if [ -n "$CACHE_FILE" ]; then
+ if [ -r "$CACHE_FILE" ]; then
+ fgrep -q -x -i "$NAME $IP_ADDRS" "$CACHE_FILE" &&
+ exit 0
+ grep -v -i "^$NAME " "$CACHE_FILE" >"$CACHE_FILE".$$
+ fi
+ echo "$NAME $IP_ADDRS" >>"$CACHE_FILE".$$
+ mv "$CACHE_FILE" "$CACHE_FILE".old 2>/dev/null
+ mv "$CACHE_FILE".$$ "$CACHE_FILE"
+fi
+
+{
+ echo update delete $NAME.$DOMAIN
+ for i in $IP_ADDRS
+ do
+ echo update add $NAME.$DOMAIN $TTL A $i
+ done
+ echo
+} 2>/dev/null | $NSUPDATE >/dev/null 2>&1 &
+
+exit 0
diff --git a/make-tarball.sh b/make-tarball.sh
new file mode 100644
index 00000000000..1eb05db70dc
--- /dev/null
+++ b/make-tarball.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+## A simple script to build a tarball of the current CVS tree.
+## You either need to include the using_samba cvs module in the
+## parent directory or tell the script where to find it
+##
+## Usgae: ./make-tarball.sh
+
+USING_SAMBA=../using_samba/
+SRCDIR=`pwd`
+
+if [ ! -d $USING_SAMBA ]; then
+
+ echo Cannot find "Using Samba" directory \(assuming $USING_SAMBA\).
+ echo Please set the USING_SAMBA variable in this script to the correct
+ echo location. The html files are available in the using_samba CVS
+ echo module on cvs.samba.org. See http://cvs/samba.org/ for details
+ echo about anonymous CVS access. Exiting now....
+
+ exit 1
+
+fi
+
+VERSION=`grep SAMBA_VERSION_OFFICIAL_STRING source/include/version.h | cut -d\" -f2 | sed 's/ /_/g'`
+TARBALLDIR=/tmp/samba-$VERSION
+
+echo Creating the tarball source directory in $TARBALLDIR
+
+/bin/rm -rf $TARBALLDIR
+/bin/rm -f samba-$VERSION.tar
+
+mkdir $TARBALLDIR
+rsync -aC ./ $TARBALLDIR
+rsync -aC $USING_SAMBA $TARBALLDIR/docs/htmldocs/
+
+echo Creating packaging scripts...
+( cd $TARBALLDIR/packaging; sh bin/update-pkginfo $VERSION 1 )
+
+echo Creating source/configure...
+( cd $TARBALLDIR/source; ./autogen.sh )
+
+echo Making tarball samba-$VERSION.tar in current directory...
+( cd `dirname $TARBALLDIR`; tar cf $SRCDIR/samba-$VERSION.tar samba-$VERSION )
diff --git a/packaging/Debian/README b/packaging/Debian/README
new file mode 100644
index 00000000000..95c75d5fc51
--- /dev/null
+++ b/packaging/Debian/README
@@ -0,0 +1,79 @@
+Building Samba Packages for Debian GNU/Linux
+--------------------------------------------
+
+Building Debian packages is not as hard as some people might think.
+The following instructions will allow you to build your own Samba
+Debian packages. These instructions and the files in packaging/Debian/
+are current as of Samba 3.0.0, and allow you to build Debian packages
+for Debian unstable (so you need some development packages available
+only in Debian unstable.) If you are using something newer than 3.0.0
+you might want to try to follow the instructions to see if patches
+apply cleanly. If some patches don't apply cleanly please e-mail
+samba@packages.debian.org since we might have fixed patches that we have
+not yet integrated into upstream Samba.
+
+We try to maintain as much compatibility with previous releases
+of Debian as possible, so it is possible that the files in
+packaging/Debian/ can also be used to build Samba Debian packages for
+other Debian releases. However, sometimes this is just not possible
+because we need to use stuff that is only available on Debian unstable.
+
+Instructions
+------------
+
+If you want to build Samba packages for Debian and you just want to use
+upstream sources, i.e. you don't want to wait for us to put official
+packages out, or you want packages for a Debian version for which we
+don't provide deb's, or you don't want to use official packages, or
+you want to add --this-cool-switch to configure, or whatever, follow
+these instructions:
+
+0) Make sure you have the following packages installed (in addition
+to the normal Debian development packages -- dpkg-dev, libc6-dev,
+devscripts, etc.):
+
+ autoconf
+ debhelper
+ libpam0g-dev
+ libreadline4-dev
+ libcupsys2-dev
+ libacl1-dev, libacl1 (>= 2.2.11-1)
+ libkrb5-dev
+ libldap2-dev
+ po-debconf
+ python2.3-dev
+
+ Notes regarding the packages required to build Samba Debian packages:
+
+ * The libcupsys2-dev is not available in Debian Potato (Debian 2.2).
+ That's fine; the configure script won't detect CUPS support and the
+ resulting binaries won't support CUPS.
+
+ * The list above is current as of samba-3.0.0rc2, but it can get
+ out of date. The best way to check what packages are required to
+ build the samba packages on Debian is to look for the Build-Depends:
+ field in the file debian/control.
+
+1) cd samba[-<version>]. For example, "cd samba-3.0.0rc2".
+2) cp -a packaging/Debian/debian/ debian
+ It's important that you copy instead of symlink because the build
+ tools in Potato have a problem that prevents the build to work with
+ a symlink.
+3) dch -i (this is completely optional - only do it if you understand
+ Debian version numbers! Don't complain later if you can't upgrade
+ to official versions of the Samba packages for Debian.)
+ - Edit the changelog and make sure the version is right. For example,
+ for Samba 3.0.0beta3, the version number should something like
+ 3.0.0beta3-0.1.
+4) Run 'debian/rules binary'.
+ - It is better that you prefix the above command with 'fakeroot'.
+ If you have problems you might try building as root.
+5) That's it. Your new packages should be in ../. Install with dpkg.
+
+Please e-mail samba@packages.debian.org with comments, questions or
+suggestions. Please talk to us and not to the Samba Team. They have
+better things to do and know nothing about the Debian packaging system.
+
+Eloy A. Paris <peloy@debian.org>
+Steve Langasek <vorlon@debian.org>
+
diff --git a/packaging/Debian/debian/README.build b/packaging/Debian/debian/README.build
new file mode 100644
index 00000000000..0a11a1f6ea6
--- /dev/null
+++ b/packaging/Debian/debian/README.build
@@ -0,0 +1,397 @@
+From: Steve Langasek <vorlon@netexpress.net>
+To: "Eloy A. Paris" <eloy.paris@usa.net>
+Date: Thu, 23 Aug 2001 21:20:05 -0500 (CDT)
+Subject: Re: autobuilder failure on arm for samba-2.2.1a-3
+In-Reply-To: <20010823100906.A1092@antenas>
+Message-ID: <Pine.LNX.4.30.0108231744090.11071-100000@tennyson.netexpress.net>
+MIME-Version: 1.0
+Content-Type: TEXT/PLAIN; charset=US-ASCII
+
+On Thu, 23 Aug 2001, Eloy A. Paris wrote:
+
+> On Wed, Aug 22, 2001 at 03:01:01PM -0500, Steve Langasek wrote:
+
+> > Hmm. Maybe the thing to do is to focus on getting config.cache (not log,
+> > cache) support into the package. Issues like this are frequent enough with
+> > Samba, and the configure tests add enough time to the build process, that I
+> > think there'd be much benefit in being able to step past a lot of these.
+
+> Uhhmmm... I don't know, I guess I don't like much the idea of
+> maintaining a config.cache. It looks like extra work plus a
+> compilation process that is "synthetic" or atificial. What happens if
+> the Samba Team adds a new test, or modifies the configure script, will
+> the config.cache pick those up?
+
+> In any case, you are the expert, so if you think that's the way to go,
+> and the burden far exceeds the problems we have right now I say let's
+> go for it. I am not well versed on autoconf and the configure process,
+> that's all...
+
+Well, I'll attach my work to the bottom of this message and let you judge it
+for yourself.
+
+The config.cache I'm trying to generate here is not equivalent to what
+a configure script outputs. The only values I'm including are those which 1)
+are no-brainers on any glibc-based platform, 2) are questions we need to force
+a particular value for regardless of the kernel being built against, or 3) are
+questions about specific bizarre features of proprietary Unices that we'll
+always get an answer of 'no' to.
+
+I've removed all of the config.cache variables related to headers, or to
+checks for particular libraries; I think it's pretty safe to assert that glibc
+provides basic C functions like select(), setenv(), and waitpid() on all our
+build targets, but I think it's less safe to assert that they'll always be
+provided by particular header files.
+
+So the config.cache won't automatically be updated with answers to new
+configure tests, but it also doesn't need to in order to be useful. There's
+really only a handful of variables in there that we /need/ in order to
+guarantee correctly-built packages, and if you want to leave out everything
+else, that's perfectly ok too. Everything from the fifth stanza on down is
+just a build-time speed-up for some of the slower architectures. Well, it
+also has the fringe benefit that the packages will FAIL to build if someone
+tries rebuilding for a really bizarre (non-Linux, non-glibc) architecture. I
+see that as a plus :), you may disagree, but in any case my next trick would
+be to add a global variable developers can set to bypass the provided
+config.cache.
+
+
+It is a little artificial, but the whole point of .debs is to be able to build
+binaries in a controlled environment. Right now, we don't really have control
+over what happens in the autobuilders. We have even /less/ control over what
+happens in a stable release: it's been two weeks now since I built binaries
+for bug #94380, and they haven't been uploaded to security.d.o yet. I'm
+guessing they won't be uploaded until Wichert is back from vacation, either --
+which is fine, but it would be nice if we didn't have to worry about
+mis-builds by the security team, or about putting the security team to extra
+trouble after the fact to get packages fixed.
+
+With a pre-loaded config.cache, we can ensure that bugs of this kind don't
+happen in woody. We can take the arm autobuilder problem into our own hands,
+and not have to worry about quirkiness in the build environment. We can even
+close bug #109773, since we no longer have to worry about detecting the
+setuid() routines.
+
+
+So to me, it definitely seems worth it. But you're the maintainer, and I
+won't ask you to put anything in the package that you're not comfortable with.
+
+
+> By the way, I think I remember someone was able to build 2.2.x succesfully on
+> the ARM. If this is the case, could it be that there's something weird
+> with Phillip's setup?
+
+It could be. There are arm packages for 2.2.1a-1, so at /some/ point the
+autobuilder was able to pass the locking test.
+
+
+> P.S. How did you know about the ARM build problems? I don't see any
+> bugs about this...
+
+<http://ftp-master.debian.org/testing/>, follow the links for samba... The
+exact reference for the arm autobuilder is at
+<http://buildd.armlinux.org/~buildd/build.php?pkg=samba&ver=2.2.1a-4&arch=arm>.
+
+Regards,
+Steve Langasek
+postmodern programmer
+
+
+diff -uNrw samba-2.2.1a-bak/debian/changelog samba-2.2.1a/debian/changelog
+--- samba-2.2.1a-bak/debian/changelog Thu Aug 23 10:27:54 2001
++++ samba-2.2.1a/debian/changelog Thu Aug 23 10:28:08 2001
+@@ -1,3 +1,12 @@
++samba (2.2.1a-4.1) unstable; urgency=low
++
++ * Fix up the build system to avoid needing to run configure as root to
++ answer questions we already know the answers to.
++ * In the process, make surprising progress towards being able to
++ cross-compile the samba packages.
++
++ -- Steve Langasek <vorlon@debian.org> Wed, 22 Aug 2001 23:35:00 -0500
++
+ samba (2.2.1a-4) unstable; urgency=low
+
+ * Fixed typo in smbmount's mount page.
+diff -uNrw samba-2.2.1a-bak/debian/config.cache samba-2.2.1a/debian/config.cache
+--- samba-2.2.1a-bak/debian/config.cache Wed Dec 31 18:00:00 1969
++++ samba-2.2.1a/debian/config.cache Thu Aug 23 10:28:08 2001
+@@ -0,0 +1,231 @@
++#
++# 22 August 2001 Steve Langasek <vorlon@debian.org>
++#
++# This file is a shell script that caches the results of configure
++# tests run on this system so they can be shared between configure
++# scripts and configure runs. It is not useful on other systems.
++# If it contains results you don't want to keep, you may remove or edit it.
++#
++# By default, configure uses ./config.cache as the cache file,
++# creating it if it does not exist already. You can give configure
++# the --cache-file=FILE option to use a different cache file; that is
++# what configure does when it calls configure scripts in
++# subdirectories, so they share the cache.
++# Giving --cache-file=/dev/null disables caching, for debugging configure.
++# config.status only pays attention to the cache file if you give it the
++# --recheck option to rerun configure.
++#
++#
++# This config.cache file contains a list of acceptable autoconf
++# values which can be used in compiling Samba for Debian woody/sid.
++#
++# Autoconf sorts options alphabetically in its output. This file
++# groups options logically.
++
++
++# Load any architecture-specific settings
++if [ -n "$DEB_HOST_GNU_TYPE" \
++ -a -f ../debian/config.cache.${DEB_HOST_GNU_TYPE} ]; then
++ . ../debian/config.cache.${DEB_HOST_GNU_TYPE}
++fi
++
++
++# This is at the top because it's most in need of regular tweaking.
++# These are options which are supported on 2.4 kernels, but not on 2.2
++# kernels.
++
++samba_cv_HAVE_KERNEL_OPLOCKS_LINUX=${samba_cv_HAVE_KERNEL_OPLOCKS_LINUX=no}
++samba_cv_HAVE_KERNEL_CHANGE_NOTIFY=${samba_cv_HAVE_KERNEL_CHANGE_NOTIFY=no}
++samba_cv_HAVE_KERNEL_SHARE_MODES=${samba_cv_HAVE_KERNEL_SHARE_MODES=no}
++
++
++# These are present in 2.2 kernels, but not in 2.0...
++
++samba_cv_have_setresuid=${samba_cv_have_setresuid=yes}
++samba_cv_have_setresgid=${samba_cv_have_setresgid=yes}
++samba_cv_USE_SETRESUID=${samba_cv_USE_SETRESUID=yes}
++
++
++# POSIX ACL support not present in Linux 2.2; not allowed in the
++# Debian packages, even if present on the build machine.
++
++ac_cv_header_sys_acl_h=${ac_cv_header_sys_acl_h=no}
++
++
++# Various basic libc/compiler stuff that it's blindingly obvious that
++# Linux supports (now watch me get bitten for saying that)
++
++ac_cv_c_const=${ac_cv_c_const=yes}
++ac_cv_c_inline=${ac_cv_c_inline=inline}
++samba_cv_volatile=${samba_cv_volatile=yes}
++ac_cv_dirent_d_off=${ac_cv_dirent_d_off=yes}
++ac_cv_func_bzero=${ac_cv_func_bzero=yes}
++ac_cv_func_chmod=${ac_cv_func_chmod=yes}
++ac_cv_func_chown=${ac_cv_func_chown=yes}
++ac_cv_func_chroot=${ac_cv_func_chroot=yes}
++ac_cv_func_connect=${ac_cv_func_connect=yes}
++ac_cv_func_dup2=${ac_cv_func_dup2=yes}
++ac_cv_func_execl=${ac_cv_func_execl=yes}
++ac_cv_func_fchmod=${ac_cv_func_fchmod=yes}
++ac_cv_func_fchown=${ac_cv_func_fchown=yes}
++ac_cv_func_fstat=${ac_cv_func_fstat=yes}
++ac_cv_func_fsync=${ac_cv_func_fsync=yes}
++ac_cv_func_ftruncate=${ac_cv_func_ftruncate=yes}
++ac_cv_func_getcwd=${ac_cv_func_getcwd=yes}
++ac_cv_func_getgrent=${ac_cv_func_getgrent=yes}
++ac_cv_func_getgrnam=${ac_cv_func_getgrnam=yes}
++ac_cv_func_getspnam=${ac_cv_func_getspnam=yes}
++ac_cv_func_glob=${ac_cv_func_glob=yes}
++ac_cv_func_grantpt=${ac_cv_func_grantpt=yes}
++ac_cv_func_initgroups=${ac_cv_func_initgroups=yes}
++ac_cv_func_llseek=${ac_cv_func_llseek=yes}
++ac_cv_func_memcmp_clean=${ac_cv_func_memcmp_clean=yes}
++ac_cv_func_memmove=${ac_cv_func_memmove=yes}
++ac_cv_func_memset=${ac_cv_func_memset=yes}
++ac_cv_func_mktime=${ac_cv_func_mktime=yes}
++ac_cv_func_pipe=${ac_cv_func_pipe=yes}
++ac_cv_func_poll=${ac_cv_func_poll=yes}
++ac_cv_func_pread=${ac_cv_func_pread=yes}
++ac_cv_func_pwrite=${ac_cv_func_pwrite=yes}
++ac_cv_func_rand=${ac_cv_func_rand=yes}
++ac_cv_func_random=${ac_cv_func_random=yes}
++ac_cv_func_readlink=${ac_cv_func_readlink=yes}
++ac_cv_func_rename=${ac_cv_func_rename=yes}
++ac_cv_func_select=${ac_cv_func_select=yes}
++ac_cv_func_setenv=${ac_cv_func_setenv=yes}
++ac_cv_func_setgroups=${ac_cv_func_setgroups=yes}
++ac_cv_func_setsid=${ac_cv_func_setsid=yes}
++ac_cv_func_sigaction=${ac_cv_func_sigaction=yes}
++ac_cv_func_sigblock=${ac_cv_func_sigblock=yes}
++ac_cv_func_sigprocmask=${ac_cv_func_sigprocmask=yes}
++ac_cv_func_snprintf=${ac_cv_func_snprintf=yes}
++ac_cv_func_srand=${ac_cv_func_srand=yes}
++ac_cv_func_srandom=${ac_cv_func_srandom=yes}
++ac_cv_func_strcasecmp=${ac_cv_func_strcasecmp=yes}
++ac_cv_func_strchr=${ac_cv_func_strchr=yes}
++ac_cv_func_strdup=${ac_cv_func_strdup=yes}
++ac_cv_func_strerror=${ac_cv_func_strerror=yes}
++ac_cv_func_strftime=${ac_cv_func_strftime=yes}
++ac_cv_func_strpbrk=${ac_cv_func_strpbrk=yes}
++ac_cv_func_strtoul=${ac_cv_func_strtoul=yes}
++ac_cv_func_symlink=${ac_cv_func_symlink=yes}
++ac_cv_func_usleep=${ac_cv_func_usleep=yes}
++ac_cv_func_utime=${ac_cv_func_utime=yes}
++ac_cv_func_utimes=${ac_cv_func_utimes=yes}
++ac_cv_func_vsnprintf=${ac_cv_func_vsnprintf=yes}
++ac_cv_func_waitpid=${ac_cv_func_waitpid=yes}
++ac_cv_type_ino_t=${ac_cv_type_ino_t=yes}
++ac_cv_type_mode_t=${ac_cv_type_mode_t=yes}
++ac_cv_type_pid_t=${ac_cv_type_pid_t=yes}
++ac_cv_type_size_t=${ac_cv_type_size_t=yes}
++ac_cv_type_uid_t=${ac_cv_type_uid_t=yes}
++samba_cv_socklen_t=${samba_cv_socklen_t=yes}
++
++# Yes, we know Linux supports fcntl locking. Just ignore
++# any errors caused by building on an NFS mount.
++samba_cv_HAVE_FCNTL_LOCK=${samba_cv_HAVE_FCNTL_LOCK=yes}
++
++
++# smbwrapper doesn't work because the glibc maintainers don't want
++# to support transparent userland VFS. We might as well preempt
++# any checks for shadowed symbols that are only useful for smbwrapper.
++
++ac_cv_func___acl=${ac_cv_func___acl=no}
++ac_cv_func__acl=${ac_cv_func__acl=no}
++ac_cv_func___chdir=${ac_cv_func___chdir=no}
++ac_cv_func__chdir=${ac_cv_func__chdir=no}
++ac_cv_func___close=${ac_cv_func___close=no}
++ac_cv_func__close=${ac_cv_func__close=no}
++ac_cv_func___closedir=${ac_cv_func___closedir=no}
++ac_cv_func__closedir=${ac_cv_func__closedir=no}
++ac_cv_func___dup=${ac_cv_func___dup=no}
++ac_cv_func__dup=${ac_cv_func__dup=no}
++ac_cv_func___dup2=${ac_cv_func___dup2=no}
++ac_cv_func__dup2=${ac_cv_func__dup2=no}
++ac_cv_func___facl=${ac_cv_func___facl=no}
++ac_cv_func__facl=${ac_cv_func__facl=no}
++ac_cv_func___fchdir=${ac_cv_func___fchdir=no}
++ac_cv_func__fchdir=${ac_cv_func__fchdir=no}
++ac_cv_func___fcntl=${ac_cv_func___fcntl=no}
++ac_cv_func__fcntl=${ac_cv_func__fcntl=no}
++ac_cv_func___fork=${ac_cv_func___fork=no}
++ac_cv_func__fork=${ac_cv_func__fork=no}
++ac_cv_func___fstat=${ac_cv_func___fstat=no}
++ac_cv_func__fstat=${ac_cv_func__fstat=no}
++ac_cv_func___fstat64=${ac_cv_func___fstat64=no}
++ac_cv_func__fstat64=${ac_cv_func__fstat64=no}
++ac_cv_func___fxstat=${ac_cv_func___fxstat=no}
++ac_cv_func___getcwd=${ac_cv_func___getcwd=no}
++ac_cv_func__getcwd=${ac_cv_func__getcwd=no}
++ac_cv_func___getdents=${ac_cv_func___getdents=no}
++ac_cv_func__getdents=${ac_cv_func__getdents=no}
++ac_cv_func___llseek=${ac_cv_func___llseek=no}
++ac_cv_func___sys_llseek=${ac_cv_func___sys_llseek=no}
++ac_cv_func__llseek=${ac_cv_func__llseek=no}
++ac_cv_func___lseek=${ac_cv_func___lseek=no}
++ac_cv_func__lseek=${ac_cv_func__lseek=no}
++ac_cv_func___lstat=${ac_cv_func___lstat=no}
++ac_cv_func__lstat=${ac_cv_func__lstat=no}
++ac_cv_func___lstat64=${ac_cv_func___lstat64=no}
++ac_cv_func__lstat64=${ac_cv_func__lstat64=no}
++ac_cv_func___lxstat=${ac_cv_func___lxstat=no}
++ac_cv_func___open=${ac_cv_func___open=no}
++ac_cv_func__open=${ac_cv_func__open=no}
++ac_cv_func___open64=${ac_cv_func___open64=no}
++ac_cv_func__open64=${ac_cv_func__open64=no}
++ac_cv_func___opendir=${ac_cv_func___opendir=no}
++ac_cv_func__opendir=${ac_cv_func__opendir=no}
++ac_cv_func___pread=${ac_cv_func___pread=no}
++ac_cv_func__pread=${ac_cv_func__pread=no}
++ac_cv_func___pread64=${ac_cv_func___pread64=no}
++ac_cv_func__pread64=${ac_cv_func__pread64=no}
++ac_cv_func___pwrite=${ac_cv_func___pwrite=no}
++ac_cv_func__pwrite=${ac_cv_func__pwrite=no}
++ac_cv_func___pwrite64=${ac_cv_func___pwrite64=no}
++ac_cv_func__pwrite64=${ac_cv_func__pwrite64=no}
++ac_cv_func___read=${ac_cv_func___read=no}
++ac_cv_func__read=${ac_cv_func__read=no}
++ac_cv_func___readdir=${ac_cv_func___readdir=no}
++ac_cv_func__readdir=${ac_cv_func__readdir=no}
++ac_cv_func___readdir64=${ac_cv_func___readdir64=no}
++ac_cv_func__readdir64=${ac_cv_func__readdir64=no}
++ac_cv_func___seekdir=${ac_cv_func___seekdir=no}
++ac_cv_func__seekdir=${ac_cv_func__seekdir=no}
++ac_cv_func___stat=${ac_cv_func___stat=no}
++ac_cv_func__stat=${ac_cv_func__stat=no}
++ac_cv_func___stat64=${ac_cv_func___stat64=no}
++ac_cv_func__stat64=${ac_cv_func__stat64=no}
++ac_cv_func___telldir=${ac_cv_func___telldir=no}
++ac_cv_func__telldir=${ac_cv_func__telldir=no}
++ac_cv_func___write=${ac_cv_func___write=no}
++ac_cv_func__write=${ac_cv_func__write=no}
++ac_cv_func___xstat=${ac_cv_func___xstat=no}
++
++
++
++# Miscellaneous stuff that isn't, and shouldn't be, available
++# in Debian. Those interested in building debs for other systems may
++# need to remove some of these defines.
++
++ac_cv_func_bigcrypt=${ac_cv_func_bigcrypt=no}
++ac_cv_func_crypt16=${ac_cv_func_crypt16=no}
++ac_cv_func_getauthuid=${ac_cv_func_getauthuid=no}
++ac_cv_func_getprpwnam=${ac_cv_func_getprpwnam=no}
++ac_cv_func_getpwanam=${ac_cv_func_getpwanam=no}
++ac_cv_func_putprpwnam=${ac_cv_func_putprpwnam=no}
++ac_cv_func_rdchk=${ac_cv_func_rdchk=no}
++ac_cv_func_set_auth_parameters=${ac_cv_func_set_auth_parameters=no}
++ac_cv_func_setgidx=${ac_cv_func_setgidx=no}
++ac_cv_func_setluid=${ac_cv_func_setluid=no}
++ac_cv_func_setpriv=${ac_cv_func_setpriv=no}
++ac_cv_func_setuidx=${ac_cv_func_setuidx=no}
++ac_cv_lib_sec_bigcrypt=${ac_cv_lib_sec_bigcrypt=no}
++ac_cv_lib_sec_getprpwnam=${ac_cv_lib_sec_getprpwnam=no}
++ac_cv_lib_sec_getspnam=${ac_cv_lib_sec_getspnam=no}
++ac_cv_lib_sec_putprpwnam=${ac_cv_lib_sec_putprpwnam=no}
++ac_cv_lib_sec_set_auth_parameters=${ac_cv_lib_sec_set_auth_parameters=no}
++ac_cv_lib_security_bigcrypt=${ac_cv_lib_security_bigcrypt=no}
++ac_cv_lib_security_getprpwnam=${ac_cv_lib_security_getprpwnam=no}
++ac_cv_lib_security_getspnam=${ac_cv_lib_security_getspnam=no}
++ac_cv_lib_security_putprpwnam=${ac_cv_lib_security_putprpwnam=no}
++ac_cv_lib_security_set_auth_parameters=${ac_cv_lib_security_set_auth_parameters=no}
+diff -uNrw samba-2.2.1a-bak/debian/config.cache.alpha-linux samba-2.2.1a/debian/config.cache.alpha-linux
+--- samba-2.2.1a-bak/debian/config.cache.alpha-linux Wed Dec 31 18:00:00 1969
++++ samba-2.2.1a/debian/config.cache.alpha-linux Thu Aug 23 10:28:08 2001
+@@ -0,0 +1,12 @@
++# 22 Aug 2001 Steve Langasek <vorlon@debian.org>
++
++# This file contains autoconf settings specific to the alpha-linux
++# platform that should be preloaded when building for this architecture.
++
++
++# Linux 2.2 on Alpha doesn't have a functional setresgid() call, but
++# Linux 2.4 does. Ensure that packages compiled for woody remain
++# compatible with 2.2 kernels, even if the build machine is running 2.4.
++samba_cv_have_setresgid=${samba_cv_have_setresgid=no}
++samba_cv_USE_SETRESUID=${samba_cv_USE_SETRESUID=no}
++samba_cv_USE_SETREUID=${samba_cv_USE_SETREUID=yes}
+diff -uNrw samba-2.2.1a-bak/debian/rules samba-2.2.1a/debian/rules
+--- samba-2.2.1a-bak/debian/rules Thu Aug 23 10:27:54 2001
++++ samba-2.2.1a/debian/rules Thu Aug 23 10:28:08 2001
+@@ -15,6 +15,14 @@
+ # This has to be exported to make some magic below work.
+ export DH_OPTIONS
+
++# Set the host and build architectures for use with config.cache loading,
++# cross-building, etc.
++DEB_HOST_GNU_TYPE := $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
++DEB_BUILD_GNU_TYPE := $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
++
++export DEB_HOST_GNU_TYPE
++export DEB_BUILD_GNU_TYPE
++
+ BVARS = SMBLOGFILE=/var/log/smb NMBLOGFILE=/var/log/nmb
+
+ DESTDIR=`pwd`/debian/samba
+@@ -48,8 +56,11 @@
+ # ./configure --with-fhs --prefix=/usr --sysconfdir=/etc \
+ # --localstatedir=/var
+
++ if [ -f debian/config.cache ]; then \
++ cp -f debian/config.cache source/config.cache; \
++ fi
+ # [ -f source/Makefile ] || (cd source && ./configure --with-fhs --prefix=/usr --exec-prefix=/usr --with-netatalk --with-smbmount --with-pam --with-syslog --with-sambabook --with-utmp)
+- [ -f source/Makefile ] || (cd source && ./configure --with-fhs --prefix=/usr --sysconfdir=/etc --with-privatedir=/etc/samba --with-lockdir=/var/state/samba --localstatedir=/var --with-netatalk --with-smbmount --with-pam --with-syslog --with-sambabook --with-utmp --with-readline --with-pam_smbpass)
++ [ -f source/Makefile ] || (cd source && ./configure --host=$(DEB_HOST_GNU_TYPE)-gnu --build=$(DEB_BUILD_GNU_TYPE)-gnu --with-fhs --prefix=/usr --sysconfdir=/etc --with-privatedir=/etc/samba --with-lockdir=/var/state/samba --localstatedir=/var --with-netatalk --with-smbmount --with-pam --with-syslog --with-sambabook --with-utmp --with-readline --with-pam_smbpass)
+
+ touch configure-stamp
+
diff --git a/packaging/Debian/debian/README.build-upstream b/packaging/Debian/debian/README.build-upstream
new file mode 100644
index 00000000000..95c75d5fc51
--- /dev/null
+++ b/packaging/Debian/debian/README.build-upstream
@@ -0,0 +1,79 @@
+Building Samba Packages for Debian GNU/Linux
+--------------------------------------------
+
+Building Debian packages is not as hard as some people might think.
+The following instructions will allow you to build your own Samba
+Debian packages. These instructions and the files in packaging/Debian/
+are current as of Samba 3.0.0, and allow you to build Debian packages
+for Debian unstable (so you need some development packages available
+only in Debian unstable.) If you are using something newer than 3.0.0
+you might want to try to follow the instructions to see if patches
+apply cleanly. If some patches don't apply cleanly please e-mail
+samba@packages.debian.org since we might have fixed patches that we have
+not yet integrated into upstream Samba.
+
+We try to maintain as much compatibility with previous releases
+of Debian as possible, so it is possible that the files in
+packaging/Debian/ can also be used to build Samba Debian packages for
+other Debian releases. However, sometimes this is just not possible
+because we need to use stuff that is only available on Debian unstable.
+
+Instructions
+------------
+
+If you want to build Samba packages for Debian and you just want to use
+upstream sources, i.e. you don't want to wait for us to put official
+packages out, or you want packages for a Debian version for which we
+don't provide deb's, or you don't want to use official packages, or
+you want to add --this-cool-switch to configure, or whatever, follow
+these instructions:
+
+0) Make sure you have the following packages installed (in addition
+to the normal Debian development packages -- dpkg-dev, libc6-dev,
+devscripts, etc.):
+
+ autoconf
+ debhelper
+ libpam0g-dev
+ libreadline4-dev
+ libcupsys2-dev
+ libacl1-dev, libacl1 (>= 2.2.11-1)
+ libkrb5-dev
+ libldap2-dev
+ po-debconf
+ python2.3-dev
+
+ Notes regarding the packages required to build Samba Debian packages:
+
+ * The libcupsys2-dev is not available in Debian Potato (Debian 2.2).
+ That's fine; the configure script won't detect CUPS support and the
+ resulting binaries won't support CUPS.
+
+ * The list above is current as of samba-3.0.0rc2, but it can get
+ out of date. The best way to check what packages are required to
+ build the samba packages on Debian is to look for the Build-Depends:
+ field in the file debian/control.
+
+1) cd samba[-<version>]. For example, "cd samba-3.0.0rc2".
+2) cp -a packaging/Debian/debian/ debian
+ It's important that you copy instead of symlink because the build
+ tools in Potato have a problem that prevents the build to work with
+ a symlink.
+3) dch -i (this is completely optional - only do it if you understand
+ Debian version numbers! Don't complain later if you can't upgrade
+ to official versions of the Samba packages for Debian.)
+ - Edit the changelog and make sure the version is right. For example,
+ for Samba 3.0.0beta3, the version number should something like
+ 3.0.0beta3-0.1.
+4) Run 'debian/rules binary'.
+ - It is better that you prefix the above command with 'fakeroot'.
+ If you have problems you might try building as root.
+5) That's it. Your new packages should be in ../. Install with dpkg.
+
+Please e-mail samba@packages.debian.org with comments, questions or
+suggestions. Please talk to us and not to the Samba Team. They have
+better things to do and know nothing about the Debian packaging system.
+
+Eloy A. Paris <peloy@debian.org>
+Steve Langasek <vorlon@debian.org>
+
diff --git a/packaging/Debian/debian/README.debian b/packaging/Debian/debian/README.debian
new file mode 100644
index 00000000000..3802e329e53
--- /dev/null
+++ b/packaging/Debian/debian/README.debian
@@ -0,0 +1,163 @@
+Samba for Debian
+----------------
+
+This package was built by Eloy Paris <peloy@debian.org> and Steve Langasek
+<vorlon@debian.org>, current maintainers of the Samba packages for Debian,
+based on previous work from Bruce Perens <Bruce@Pixar.com>, Andrew
+Howell <andrew@it.com.au>, Klee Dienes <klee@debian.org> and Michael
+Meskes <meskes@topsystem.de>, all previous maintainers of the packages
+samba and sambades (merged together for longer than we can remember.)
+
+Contents of this README file:
+
+1. Notes
+2. Upgrading from Samba 2.2
+3. Packages Generated from the Samba Sources
+4. Support for NT Domains
+5. Reporting bugs
+
+
+1. Notes
+--------
+
+- As of Samba 2.0.6-1, the Debian version of Samba is compiled with
+ Pluggable Authentication Modules (PAM) support. PAM support was
+ discontinued during the libc5 -> libc6 migration process and I never
+ brought it back until 2.0.6-1.
+
+- The smbfs package does not support the 2.0.x Linux kernels anymore.
+ This has been the case since the very first packages of the CVS sources
+ that eventually became Samba 2.2. To use the smbfs package you need to
+ run a 2.2.x kernel or later.
+
+- Starting with the Debian packages for Samba 2.2, the Samba log files (for
+ nmbd and smbd) have been moved to a new location: /var/log/samba/. The
+ files also have new names: log.nmbd and log.smbd. The old files
+ (/var/log/{nmb,smb} were moved to the new location.
+
+
+2. Upgrading from Samba 2.2
+---------------------------
+
+Samba 3.0 provides greatly improved support for modern Windows systems,
+including support for Unicode and LDAP. In the process, Samba 3.0
+necessarily also breaks backward compatiblity with past releases. These
+issues are documented herein; if you are aware of other problems related
+to upgrading from Samba 2.2, please let us know at
+<samba@packages.debian.org>.
+
+Samba and LDAP
+--------------
+Starting with Samba 2.999+3.0cvs20020723-1 we are building Samba with
+LDAP support. However, the LDAP schema for Samba 3.0 differs
+substantially from the schema used by many sites with Samba 2.2 (not
+enabled in the Debian packages). If upgrading from an LDAP-enabled 2.2,
+you will need to run the convertSambaAccount script found in
+/usr/share/doc/samba-doc/examples/LDAP. A copy of the schema itself can
+also be found at /usr/share/doc/samba-doc/examples/LDAP/samba.schema.
+
+Character Sets
+--------------
+Samba 3.0 introduces support for negotiating Unicode (UCS-2LE) with
+Windows clients. Owing to the close similarity between Windows and Unix
+NLS charsets, in the past, many users were able to pass filenames
+containing non-ASCII characters between clients and servers without
+configuring Samba to know what character set was in use. Now, Samba
+must be able to convert Unix filenames to Unicode before sending to the
+client, so Samba must know what character set the filenames are being
+converted from. If you will be sharing files with non-ASCII names, and
+the filenames are not encoded with UTF-8, you will need to tell Samba
+which character set to use with the 'unix charset' option.
+
+If you had previously specified 'character set' and 'client code page'
+options under 2.2, these settings should be automatically converted for
+you.
+
+
+3. Packages Generated from the Samba Sources
+--------------------------------------------
+
+Currently, the Samba sources produce the following binary packages:
+
+samba: A LanManager like file and printer server for Unix.
+samba-common: Samba common files used by both the server and the client.
+smbclient: A LanManager like simple client for Unix.
+swat: Samba Web Administration Tool
+samba-doc: Samba documentation.
+smbfs: Mount and umount commands for the smbfs (works with 2.2.x and
+ above kernels, not with 2.0.x kernels.)
+libpam-smbpass: pluggable authentication module for SMB password
+ database.
+libsmbclient: Shared library that allows applications to talk to SMB servers.
+libsmbclient-dev: libsmbclient shared libraries.
+winbind: Service to resolve user and group information from a Windows NT
+ server.
+python2.2-samba: Python bindings that allow access to various aspects of
+ Samba.
+
+Please note that the package smbwrapper (a shared library that provides
+SMB client services that existed between Samba 2.0.0-1 and Samba-2.0.5a-4
+does not exist any more. The reason is that starting with Samba 2.0.6-1, that
+code does not even compile, and the upstream author (Andrew Tridgell)
+recommended to disable the compilation of smbwrapper until some issues
+with glibc2.1 get cleared out (the problem is with glibc, not with Samba
+itself).
+
+
+4. Support for NT Domains
+-------------------------
+
+Samba 2.2 includes preliminary support for NT domains. A Samba server
+can now be part of a Windows NT domain whose Primary Domain Controller
+is a Windows NT server. This feature is supposed to be stable although I
+haven't tried it myself. Read the documentation in the samba-doc package
+for help on how to do this (hint: "security = domain" in the smb.conf
+file).
+
+Samba 2.2 has also experimental support for Primary Domain
+Controller. This means that a Samba server can act now as a PDC. There
+are no special flags needed to compile Samba with NT domain PDC
+support. Please read the NTDOM PDC FAQ at www.samba.org (Documentation
+section).
+
+Please note that NT domain PDC support is far from complete and is still
+experimental.
+
+
+5. Reporting Bugs
+-----------------
+
+If you believe you have found a bug please make sure the possible bug
+also exists in the latest version of Samba that is available for the
+unstable Debian distribution. If you are running Debian stable this
+means that you will probably have to build your own packages. And if the
+problem does not exist in the latest version of Samba we have packaged it
+means that you will have to run the version of Samba you built yourself
+since it is not easy to upload new packages to the stable distribution,
+unless they fix critical security problems.
+
+If you can reproduce the problem in the latest version of Samba then
+it is likely to be a real bug. Your best shot is to search the Samba
+mailing lists to see if it is something that has already been reported
+and fixed - if it is a simple fix we can add the patch to our packages
+without waiting for a new Samba release.
+
+If you decide that your problem deserves to be submitted to the Debian
+Bug Tracking System (BTS) we expect you to be responsive if we request
+more information. If we request more information and do not receive
+any in a reasonable time frame expect to see your bug closed without
+explanation - we can't fix bugs we can't reproduce, and most of the
+time we need more information to be able to reproduce them.
+
+When submitting a bug to the Debian BTS please include the version of
+the Debian package you are using as well as the Debian distribution you
+are using. Think _twice_ about the severity you assign to the bug: we
+are _very_ sensitive about bug severities; the fact that it doesn't
+work for you doesn't mean that the severity must be such that it holds
+a major Debian release. In fact, that it doesn't work for you it
+doesn't mean that it doesn't work for others. So again: think _twice_.
+
+
+Eloy A. Paris <peloy@debian.org>
+Steve Langasek <vorlon@debian.org>
+
diff --git a/packaging/Debian/debian/TODO b/packaging/Debian/debian/TODO
new file mode 100644
index 00000000000..5883f72a92b
--- /dev/null
+++ b/packaging/Debian/debian/TODO
@@ -0,0 +1,4 @@
+Nothing in our list right now.
+
+Debian Samba Maintainers.-
+Sun Apr 6 01:34:21 EST 2003
diff --git a/packaging/Debian/debian/changelog b/packaging/Debian/debian/changelog
new file mode 100644
index 00000000000..0561f063fa6
--- /dev/null
+++ b/packaging/Debian/debian/changelog
@@ -0,0 +1,2266 @@
+samba (3.0.0-1) unstable; urgency=low
+
+ * Local build.
+
+ -- Debian User <somebody@somewhere.com> Tue, 23 Sep 2003 21:50:26 -0400
+
+samba (3.0.0beta2+3.0.0rc4-1) unstable; urgency=low
+
+ * New upstream version.
+
+ -- Eloy A. Paris <peloy@debian.org> Sat, 13 Sep 2003 08:47:56 -0400
+
+samba (3.0.0beta2+3.0.0rc3-1) unstable; urgency=low
+
+ * New upstream release. Last Release Candidate according to the
+ Samba Team. Samba 3.0.0 is around the corner, in a week or so.
+ - Fixes use of non-PIC code in nss shared libraries (closes: #208773)
+ - 'unix password sync' option now runs the unix password program as
+ root again (closes: #209739).
+ * One-line patch to make packages buildable with distcc (closes: #210227)
+
+ -- Eloy A. Paris <peloy@debian.org> Tue, 9 Sep 2003 07:57:16 -0400
+
+samba (3.0.0beta2+3.0.0rc2-1) unstable; urgency=low
+
+ * New upstream release.
+ * Incorporate Dutch debconf translations; thanks to Bart Cornelis
+ <cobaco@linux.be>. (closes: #207824)
+ * Link against libgnutls7 instead of libgnutls5. (closes: #208151)
+
+ -- Eloy A. Paris <peloy@debian.org> Tue, 2 Sep 2003 21:37:13 -0400
+
+samba (3.0.0beta2+3.0.0rc1-1) unstable; urgency=low
+
+ * New upstream version (skipped samba 3.0.0beta3 due to time
+ constraints.) This ugly version number will go away when the final
+ Samba 3.0.0 is released.
+ * Drag new unpackaged tools into the packages: smbcquotas (smbclient),
+ vfs modules (samba), smbtree(1) manpage (smbclient), tdbbackup(8)
+ manpage (samba). (closes: #151158)
+ * Switch to DH_COMPAT level 4:
+ - no explicit conffile listings needed
+ - the postinst for libsmbclient is now completely autogenerated
+ - use the default init script handling (with support for
+ invoke-rc.d) in debhelper, instead of the currently buggy upgrade
+ path (closes: #185439)
+ - add support for ${misc:Depends} in control for those packages with
+ init scripts
+ * Add versioned dependency on libpam-runtime and change
+ /etc/pam.d/samba to use the new common PAM config blocks.
+ * New python2.3-samba package (old python2.2-samba is no more.)
+ (closes: #206171)
+
+ -- Eloy A. Paris <peloy@debian.org> Mon, 25 Aug 2003 17:05:14 -0400
+
+samba (3.0.0beta2-1) unstable; urgency=low
+
+ * New upstream release
+ - The smb.conf(5) manpage documents config options again
+ (closes: #197963).
+ - Handling of winbind/idmap has been restructured; domain members
+ should be able to map domain accounts to local accounts again
+ (closes: #196815).
+ - Use the locale charset for 'display charset' by default
+ (closes: #194406).
+ - Fix for segfault in smbclient when using the -b option
+ (closes: #196833).
+ - Handle an empty 'passdb backend' list gracefully (closes: #193946).
+ * Don't set 'display charset' anymore on upgrade, since this is now
+ grabbed from the locale by default -- a much better option.
+ * Removed time.c.patch which is now in the upstream sources.
+ * Update FHS patch for two new tdb files (netsamlogon_cache.tdb,
+ privilege.tdb).
+ * Remove python-linker.patch, since the Kerberos package has been
+ fixed to no longer use rpath
+ * Remove configure.patch: the hppa glibc bug this was added for is
+ long since fixed, and upstream isn't interested in supporting this
+ kludge.
+ * Update references to missing documentation in sample smb.conf file
+ (closes: #187632).
+ * Fix handling of krb5 link line, building on a patch from Stefan
+ Metzmacher <metze@metzemix.de>.
+ * Add patch so smbclient's tar support works with popt
+ (closes: #194921).
+
+ -- Steve Langasek <vorlon@debian.org> Wed, 2 Jul 2003 20:59:09 -0500
+
+samba (3.0.0beta1-2) unstable; urgency=low
+
+ * Update build-deps to libacl1-dev (>= 2.2.11-1), libacl1 (>= 2.2.11-1)
+ to make sure we get the right shlib dependencies (closes: #193149).
+ * Update the dhcp config hooks so they're suitable for sourcing (i.e.,
+ don't call "exit") (closes: #196477).
+ * Bring package into line with current policy by adding support for
+ the DEB_BUILD_OPTIONS flag, and enabling debugging symbols (-gstabs)
+ by default
+ * Make sure libpam-smbpass is a self-contained DSO.
+ * Fix a typo in samba-common.dhcp that caused us to spuriously rewrite
+ the server list.
+ * Fix python install script to ignore -Wl linker flags, as seen in the
+ output from the latest krb5-config.
+ * Add LDAP and Unicode information about upgrading from 2.2 to
+ README.debian.
+ * Remove dangerous and confusing browse options from the default
+ smb.conf (closes: #198804).
+ * Reorder smb.conf options for clearer grouping, and clarify the
+ comments.
+ * Add a default [print$] share to the sample smb.conf, and create the
+ necessary tree under /var/lib/samba/printers. (closes: #168173)
+ * s/winbind/idmap/ in smb.conf, since the option names have changed.
+ * Fix the patch for postexec handling, so that we chdir("/") at the
+ right time.
+
+ -- Steve Langasek <vorlon@debian.org> Thu, 12 Jun 2003 15:02:00 -0500
+
+samba (3.0.0beta1-1) unstable; urgency=low
+
+ * New upstream version.
+ - fix for empty browselist bug (closes: #194553)
+ - fix for tab completion segfault in smbclient (closes: #194776)
+ - Samba now works as a domain member again without segfaulting
+ (closes: #194134, #194394, #194775)
+ - WinXP machines can join a Samba-controlled domain again
+ (closes: #195362)
+ * Build-depend on python-dev >= 2.2 instead of on just python-dev
+ (without version).
+ * Added Vorlon'n patch to source/lib/time.c to fix #194075.
+ (closes: #194075)
+
+ -- Eloy A. Paris <peloy@debian.org> Sun, 8 Jun 2003 22:26:43 -0400
+
+samba (2.999+3.0.alpha24-3) unstable; urgency=low
+
+ * Make sure Samba DSOs are compiled with -fPIC. (closes: #194324)
+ * Rebuild against pristine Kerberos libs, to squelch warnings about
+ versioned symbols. (closes: #194431, #194396)
+
+ -- Steve Langasek <vorlon@debian.org> Thu, 22 May 2003 15:32:00 -0500
+
+samba (2.999+3.0.alpha24-2) unstable; urgency=low
+
+ * Fixed description of the smbfs package. (closes: #194183)
+ * Negate the sense of the unixsam check when upgrading. (closes: #194234)
+
+ -- Steve Langasek <vorlon@debian.org> Wed, 21 May 2003 12:21:53 -0400
+
+samba (2.999+3.0.alpha24-1) unstable; urgency=low
+
+ * New upstream version. (closes: #189354)
+
+ -- Eloy A. Paris <peloy@debian.org> Tue, 20 May 2003 13:55:57 -0400
+
+samba (2.999+3.0.alpha23-5) unstable; urgency=low
+
+ * Move the python package from section "net" to section "python".
+ * Make sure we use PIC code for python on all platforms.
+ * French translation of an additional debconf template, courtesy of
+ Christian Perrier <bubulle@debian.org>. (closes: #188832)
+ * Updated Brazilian Portuguese translation from André Luís Lopes
+ <andrelop@ig.com.br>.
+ * s/unixsam/guest/ everywhere, since the unixsam backend is now
+ deprecated. (closes: #190095)
+ * Create our temp config file as /etc/samba/smb.conf.dpkg-tmp; not
+ only does using /tmp violate SELinux policies, it introduces the
+ possibility of data loss during the final copy if /tmp is a separate
+ filesystem. (closes: #189823)
+ * Pull in fix for SWAT, so that logins work again
+ (closes: #188255, #192077).
+ * Move passdb.tdb into /var/lib/samba, since it's not user-editable.
+ * Make sure with don't ship any .cvsignore files.
+ * Don't ship examples for python2.2-samba and samba-doc in an
+ "examples" directory inside another "examples" directory.
+
+ -- Eloy A. Paris <peloy@debian.org> Tue, 6 May 2003 12:05:46 -0400
+
+samba (2.999+3.0.alpha23-4) unstable; urgency=low
+
+ * Instead of s/LPT1:/LPT:/, we need to do s/LPT:/LPT1:/ -- now all
+ non-RPC printing clients are working again.
+ * Change shlibs to 0 instead of 0.1. The library already in the
+ archive is using this soname, and there are no packages depending
+ on libsmbclient, so skip changing the package name for now.
+ (closes: #188661)
+
+ -- Steve Langasek <vorlon@debian.org> Fri, 11 Apr 2003 14:42:00 -0500
+
+samba (2.999+3.0.alpha23-3) unstable; urgency=low
+
+ * Put the Samba Python modules in /usr/lib/python2.2/site-packages/,
+ not in /usr/lib/python2.2/lib-dynload/.
+
+ -- Eloy A. Paris <peloy@debian.org> Wed, 9 Apr 2003 19:49:25 -0400
+
+samba (2.999+3.0.alpha23-2) unstable; urgency=low
+
+ * New package python2.2-samba that includes the Python modules
+ included in the Samba sources. Feedback on these modules and the new
+ package is welcome, as we (Debian Samba maintainers) don't use them.
+ (closes: #170731, #173322)
+ * Move libsmbclient-dev from section "devel" to "libdevel".
+ * Fix panic action script to give a sensible error message instead of
+ an empty backtrace when we don't have permission to attach to the
+ process. (closes: #188164)
+ * Fix libpam-smbpass so that it really does something. (closes: #178245)
+ * Apply patch to fix printing-related segfaults. (closes: #188076)
+
+ -- Eloy A. Paris <peloy@debian.org> Sun, 6 Apr 2003 21:40:33 -0400
+
+samba (2.999+3.0.alpha23-1) unstable; urgency=high
+
+ * new upstream release, includes security fix for DSA-262
+ * tweak the debconf templates to avoid references to specific
+ front-ends (closes: #183718)
+
+ -- Steve Langasek <vorlon@debian.org> Sun, 9 Mar 2003 14:58:00 -0600
+
+samba (2.999+3.0.alpha21-5) unstable; urgency=low
+
+ * touch up the package descriptions a little bit (caps, punctuation)
+ * remove addtosmbpass, which snuck back in when we weren't looking
+ * reverse the position of the wins server tag, after looking more
+ closely at the code (closes: #183061)
+ * fix a glitch in the Spanish .po that rendered it invalid, plus a typo
+ * updated Brazilian Portuguese templates (closes: #183295)
+ * fix a typo in upstream manpage (s/shave/share/) (closes: #180546)
+ * run sed before we run sed, to deal with crazybad special chars
+ in the workgroup name (!) (closes: #176717)
+
+ -- Steve Langasek <vorlon@debian.org> Sat, 1 Mar 2003 15:14:00 -0600
+
+samba (2.999+3.0.alpha21-4) unstable; urgency=low
+
+ * add scripts to samba-common to grab the netbios-name-servers options
+ if we're running a DHCP client (closes: #38413)
+ * major rearrangement of build scripts: install target now operates on
+ debian/tmp, not debian/samba, so we can see when new files are
+ added and decide where to put them; several files that should have
+ been in samba-common but were in samba (for the above reason) --
+ smbcacls, -- have been moved, with a replaces: added.
+ * Fix rc script so that whitespace is consistent between inetd and
+ daemon modes (closes: #174677).
+ * smbclient -M must always connect to port 139, because port 445
+ doesn't support messaging and we can't do the port 135 stuff yet
+ (closes: #175292, #167859).
+ * Import the diff from upstream CVS, which has fixed a few bugs
+ (closes: #178219, #177583, #181467, #181487, #181603, #175864).
+ Remove a few patches of ours which are now superseded.
+ * Add po-debconf support to the tree, for better i18n.
+ * Install the libsmbclient.so symlink in the libsmbclient-dev package,
+ per policy (closes: #181466).
+
+ -- Steve Langasek <vorlon@debian.org> Fri, 27 Dec 2002 00:37:00 -0600
+
+samba (2.999+3.0.alpha21-3) unstable; urgency=low
+
+ * Drop --with-ldapsam from the configure options, since this no longer
+ means what we thought it did. Revert patch for changing the 'passdb
+ backend' defaults.
+ * Add patch from CVS HEAD to fix pdbedit segfault; postinst script
+ should work better now. (Closes: #173936)
+
+ -- Steve Langasek <vorlon@debian.org> Sun, 22 Dec 2002 13:29:00 -0600
+
+samba (2.999+3.0.alpha21-2) unstable; urgency=low
+
+ * add CONFIGDIR to the set of directories exported in the install
+ target, so we don't try to write to /etc/ on the autobuilders.
+ * Reset the default 'passdb backend' value to something sensible, so
+ that we don't unnecessarily break upgrading systems (closes: #173731).
+
+ -- Steve Langasek <vorlon@debian.org> Fri, 20 Dec 2002 09:13:00 -0600
+
+samba (2.999+3.0.alpha21-1) unstable; urgency=low
+
+ * new upstream release, many patches now incorporated upstream
+
+ -- Steve Langasek <vorlon@debian.org> Mon, 16 Dec 2002 23:39:00 -0600
+
+samba (2.999+3.0.alpha20-4) unstable; urgency=low
+
+ * Remove obsolete comments about non-existant LDAP support in the
+ Debian Samba packages. (Closes: #165035)
+ * Apply patch for segfault in pam_smbpass when using the unixsam
+ backend.
+ * Drop support for nmbd in inetd, since it's not supported by
+ upstream and is reported to cause problems (closes: #23243, #137726,
+ 165037).
+ * Clarify example printing configs in smb.conf (closes: #168174).
+ * Make sure nmbd still responds to SIGTERM if it has no interfaces to
+ listen on (closes: #168079).
+ * Fix to get samba working again on 64-bit archs, after a
+ pointer<->int size mismatch bug. Already fixed in upstream CVS.
+ * Merge fix from CVS for broken libsmbclient.h references to internal
+ structures (closes: #162956).
+ * Add a default 'panic action' for Samba that will give us genuinely
+ useful debugging information after a crash.
+ * Fixed correct patch to example configurations in the libpam-smbpass
+ packages (closes: #169350).
+ * acl-dev is not in sid anymore; Build-Depend on libacl1-dev instead
+ (closes: #169682).
+ * Only ask the user for permission to edit if there's a chance of us
+ damaging something.
+
+ -- Steve Langasek <vorlon@debian.org> Mon, 18 Nov 2002 19:53:00 -0500
+
+samba (2.999+3.0.alpha20-3) unstable; urgency=low
+
+ * Make sure smbstatus behavior is sane when Samba *has* been started,
+ as well as when it has not (closes: #164179). Thank to Robbert Kouprie
+ <robbert@radium.jvb.tudelft.nl> for this patch.
+ * Not using 'killall' in any of the maintainer scripts (the last one
+ remaining was winbind.logrotate.) We now just use 'kill' to send
+ a SIGHUP to a specific PID (which is stored in a file in
+ /var/run/samba.)
+ * Do not depend on procps because we're not using killall anymore.
+
+ -- Eloy A. Paris <peloy@debian.org> Tue, 15 Oct 2002 22:15:57 -0400
+
+samba (2.999+3.0.alpha20-2) unstable; urgency=low
+
+ * fix an off-by-one error in smbd/lanman.c, which should shut off the
+ flood of log messages (closes: #157432)
+ * add a --config-cache option to the configure invocation, since
+ autoconf 2.5 doesn't load config.cache by default (closes: #163504)
+
+ -- Steve Langasek <vorlon@debian.org> Sat, 5 Oct 2002 01:40:00 -0500
+
+samba (2.999+3.0.alpha20-1) unstable; urgency=low
+
+ * new upstream release
+ - non-primary groups appear to work again (closes: #161271)
+ * the official beginning of the upstream 3.0 branch
+ * exit without error from smbstatus when no connections have
+ been seen yet (closes: #161489)
+
+ -- Steve Langasek <vorlon@debian.org> Wed, 2 Oct 2002 19:02:00 -0500
+
+samba (2.999+3.0cvs20020906-1) unstable; urgency=low
+
+ * CVS update
+ - domain authentication works again (closes: #158698)
+ * Factor out common code in samba-common.config
+ * Handle character set settings in smb.conf on upgrade
+ (closes: #153913, #158770)
+ * Don't use killall in logrotate script; there are better ways
+ (closes: #160076)
+ * Ignore value of 'hostname lookups' for hosts allow/hosts deny
+ (closes: #154376)
+
+ -- Steve Langasek <vorlon@debian.org> Sat, 7 Sep 2002 11:46:00 -0500
+
+samba (2.999+3.0cvs20020829-1) unstable; urgency=low
+
+ * CVS update.
+ * Move the smb.conf manpage to the samba-common package (closes: #159572)
+
+ -- Steve Langasek <vorlon@debian.org> Thu, 29 Aug 2002 17:53:25 -0500
+
+samba (2.999+3.0cvs20020827-1) unstable; urgency=low
+
+ * CVS update. (Closes: #158508)
+ * Part 1 of 3 of the library separation patch that Vorlon wrote has
+ gone upstream - removed the patch from our patches/ directory.
+ * Debconf note to warn users that their smb.conf will be re-written
+ and changed if they use Swat to maintain it. (Closes: #158479)
+ * Fixed typo in samba.prerm.
+
+ -- Eloy A. Paris <peloy@debian.org> Tue, 27 Aug 2002 15:23:23 -0400
+
+samba (2.999+3.0cvs20020825-2) unstable; urgency=low
+
+ * scale back the tdbsam migration support, because of undesirable
+ side-effects; now always defaults to 'no'.
+ * strip out hyperactive library dependencies that are only needed by
+ smbd (closes: #155156).
+ * nuke any broken registry.tdb files left by previous CVS snapshots.
+ * support rolling back the smbpasswd->tdbsam conversion on downgrade,
+ since many people are likely to need to downgrade for a while.
+ * remove postrm handling of legacy directories, and add handling of
+ current ones.
+
+ -- Steve Langasek <vorlon@debian.org> Sun, 28 Jul 2002 09:44:24 -0500
+
+samba (2.999+3.0cvs20020825-1) unstable; urgency=low
+
+ * CVS update. These packages are based on Samba 3.0alpha19 + any
+ code commited after 3.0alpha19 was released.
+
+ -- Eloy A. Paris <peloy@debian.org> Sun, 25 Aug 2002 14:56:46 -0400
+
+samba (2.999+3.0cvs20020723-1) unstable; urgency=medium
+
+ * remove spurious line from samba.config
+ * migrate from smbpasswd to tdbsam
+ * re-add the pdbedit util and manpage
+ * compile in ldapsam support (closes: #146935)
+ * add PRIVATEDIR to the list of vars we override for the install
+ target, so Samba doesn't try to create /etc/samba (closes: #153746).
+ * fix makefile handling of LOGBASEDIR, so that logs always end up in
+ the right place (closes: 153727).
+ * Fixed bug in the FHS migration path that causes nmbd to read its
+ state from one location, but write it out to another. (closes: #154210)
+ * Make sure nmbd is always looking for wins.tdb in the same place.
+
+ -- Steve Langasek <vorlon@debian.org> Fri, 19 Jul 2002 21:38:54 -0500
+
+samba (2.99.cvs.20020713-1) unstable; urgency=low
+
+ * first attempt for 3.0pre.
+ * only post a debconf note about moving logfiles if we're upgrading
+ from a version that had the logfiles in the old location
+ (closes: #152924).
+
+ -- Steve Langasek <vorlon@debian.org> Sat, 13 Jul 2002 12:54:25 -0500
+
+samba (2.2.5-2) unstable; urgency=low
+
+ * No longer ship make_printerdef, which is deprecated. (closes: #63059)
+ * Clean up some empty directories from the samba package.
+ * Add call to dh_installinit for winbind rc.x symlinks (closes: #151860).
+ * Clean up per-package documentation lists, to reduce clutter
+ (closes: #147638).
+ * Make sure we don't ship pdbedit's man page since we are still using
+ smbpasswd passwords. (closes: #152208)
+ * move libnss_wins.so to libnss_wins.so.2, where glibc expects to find
+ it (closes: #148586).
+ * reorder postinst, so that installing samba-common from scratch loads
+ the debconf answers properly (closes: #151985).
+ * add lintian overrides for winbind, to eliminate some noise.
+ * rename pam_smbpass changelog to comply with policy.
+
+ -- Steve Langasek <vorlon@debian.org> Sun, 23 Jun 2002 22:45:04 -0500
+
+samba (2.2.5-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Eloy A. Paris <peloy@debian.org> Sun, 9 Jun 2002 15:49:21 -0400
+
+samba (2.2.4+2.2.5pre1-1) experimental; urgency=low
+
+ * Getting ready for Samba 2.2.5.
+ * Remove patches/parse_spoolss.patch, now included upstream.
+ * Fixed thinko WRT POSIX ACL support, which we "half-enabled" in
+ 2.2.4-1. We don't use POSIX ACL support ourselves, so we'd
+ appreciate reports from those using this feature so we can
+ be sure this works.
+ * Fix the filename-matching algorithm used for smbtar's 'exclude'
+ functionality. (closes: #131571)
+ * Look for secrets.tdb in /var/lib/samba, and handle in the postinst.
+ This is not really a config file, because users don't edit it.
+ (closes: #147429)
+ * Doxygen fix for libsmbclient.h, thanks to Tommi Komulainen
+ <Tommi.Komulainen@iki.fi> for the patch. (closes: #144847)
+
+ -- Eloy A. Paris <peloy@debian.org> Tue, 28 May 2002 11:33:51 -0400
+
+samba (2.2.4-1) unstable; urgency=low
+
+ * New upstream release (closes: #144713)
+ * Building with POSIX ACL support (closes: #137819)
+ * Include samples, exclude INSTALL from libpam-smbpass (closes: #145055)
+ * Compile with --with-automount, for NIS homedir support (closes: #123396)
+ * Add a proper 'flags' field to the mount entry we write to /etc/mtab;
+ fixes a display bug with mount (closes: #140397)
+ * Added logic to /etc/init.d/samba so a help message is printed out
+ when Samba is running from inetd _and_ we are not booting, i.e. the
+ user called the init script manually. Thanks to Francesco
+ Potorti <pot@gnu.org> for the suggestion on how to implement this.
+ (Closes: #139807, #140204)
+ * samba.postinst: added logic so we don't call /etc/init.d/samba if
+ we are running from inetd (this prevents the stupid help message
+ to be printed during package upgrades if we are running from inetd.)
+ * samba.prerm: idem.
+ * /etc/init.d/samba: delete stale PID files after nmbd and smbd are
+ stopped. This prevents start-stop-daemon from printing an ugly
+ error message when called from '/etc/init.d/samba stop'. I prefer
+ this than running start-stop-daemon with --oknodo because
+ start-stop-daemon might print other important error messages that with
+ --oknodo it would otherwise not print. (Closes: #102187, #109301)
+ * Patch from jerry@samba.org to fix parsing of spoolss structures.
+
+ -- Eloy A. Paris <peloy@debian.org> Thu, 23 May 2002 23:16:52 -0400
+
+samba (2.2.3a-7) unstable; urgency=medium
+
+ * More README.debian updates.
+ * Neutralize the smb.conf 'lock dir' directive, which doesn't mean
+ what the FHS says it should, and causes us no end of grief.
+ (Closes: #122299)
+ * LPRng-handling patch so that jobs printed to recent versions of
+ LPRng show up properly as 'done' instead of 'paused' in the Windows
+ print queue. Thanks to Jaroslav Serdula <serdula_jaroslav@vse.sk>
+ for this patch. (Closes: #139458)
+ * Applied patch from Urban Widmark <urban@teststation.com>
+ (smbfs upstream maintainer) to add a '-n' option to smbmount
+ that does the same as mount's '-n'. (Closes: #139590)
+ * Minor tweak to unpatch-source so we unpatch sources in the
+ reverse order we patched them.
+ * Don't depend on grep in samba.prerm to determine if Samba was
+ running before the upgrade starts.
+ * Tweak the wording of debconf templates.
+ * Incorporate debconf translations for French, Spanish and Portuguese;
+ thanks to Carlos Valdivia Yagüe <valyag@hotpop.com> (es),
+ Andre Luis Lopes <andrelop@ig.com.br> (pt_BR), and Philippe
+ Batailler and Denis Barbier <barbier@debian.org> (fr).
+ (closes: #142657, #142659, #141551, #141699, #141682)
+ * Fixed symlinks in the swat package so the point to /usr/share/doc/
+ instead of /usr/doc/. Added note to the description of the
+ swat packages that says that samba-doc must be installed for
+ the on-line documentation to work. Thanks to Torne Wuff
+ <torne@wolfpuppy.org.uk>. (Closes: #95437)
+ * 'dh_installinit -n' gives us no initscript handling -- we need to
+ handle all starting and stopping of daemons ourselves, which wasn't
+ happening in the {pre,post}rm scripts.
+ * Vary the priority of the debconf question "Do you want to generate
+ /etc/samba/smbpassd?" depending on whether the file already exists.
+ File exists -> priority 'medium', file does not exist -> priority
+ 'low'. Changed priorities of all other questions from 'high' to 'medium'.
+
+ -- Steve Langasek <vorlon@debian.org> Sat, 20 Apr 2002 17:48:27 -0400
+
+samba (2.2.3a-6) unstable; urgency=low
+
+ * Call db_stop as soon as we're done with debconf in the postinst, to
+ avoid hanging bugs (closes: #137813)
+ * Ony call 'update-inetd --add' on first installation, just as we only
+ call 'update-inetd --remove' on package purge.
+ * Bring our shipped smb.conf closer in line with the upstream
+ defaults: don't twiddle the send/recv buffer sizes, since the Linux
+ kernel already provides a much better default setting
+ (closes: #80966, #80934, #137415, #133477)
+ * Added libnss_wins.so to the winbind package (closes: #137201)
+ * Updates to README.debian.
+
+ -- Eloy A. Paris <peloy@debian.org> Tue, 12 Mar 2002 10:57:40 -0500
+
+samba (2.2.3a-5) unstable; urgency=low
+
+ * Having multiple workgroup lines in your smb.conf, though wacky, is
+ perfectly valid. Account for this in samba-common.config.
+ (closes: #137157)
+
+ -- Steve Langasek <vorlon@debian.org> Sun, 10 Mar 2002 21:52:51 -0600
+
+samba (2.2.3a-4) unstable; urgency=low
+
+ * Fixed typo in samba.postinst. Cosmetic fixes there as well.
+ * Fix to improper usage of /usr/bin/tr in samba-common config script
+ (closes: #137744)
+
+ -- Steve Langasek <vorlon@debian.org> Sat, 9 Mar 2002 14:14:02 -0500
+
+samba (2.2.3a-3) unstable; urgency=medium
+
+ * Make sure /etc/init.d/samba is executable before calling it
+ in the postinst. Quickly checked all other maintainer scripts
+ to make sure we are not calling an init script if it is not
+ executable. (closes: #137321)
+ * Fix up maintainer scripts to detect if samba was not running before
+ an upgrade. (closes: #33520, #130534)
+ * Make sure /etc/samba/ is included in the samba-common package.
+ Closes: #137157
+
+ -- Steve Langasek <vorlon@debian.org> Fri, 8 Mar 2002 11:13:21 -0500
+
+samba (2.2.3a-2) unstable; urgency=low
+
+ * merge in debconf support:
+ - Moved all smb.conf-related questions to samba-common (smb.conf
+ is part of the samba-common package, not the samba package.)
+ - smb.conf is not a samba-common conffile anymore since it is
+ being managed by debconf. It is ABSOLUTELY necessary to make
+ sure /etc/samba/smb.conf _NEVER_ gets overwritten by changes
+ made via debconf. In other words, any changes made by the user
+ should be honored by the debconf interface.
+ - samba.postinst now moves old log files from /var/log/ to
+ /var/log/samba/. There's a Debconf note that informs the user
+ the log files are stored now in a new location.
+ - debian/control:
+ + Make samba depend on debconf.
+ - New file debian/samba.templates.
+ - New file debian/samba.config.
+ - Re-worked debian/samba.postinst.
+ + Got rid of all /etc/samba/debian_config sillyness.
+ - remove /usr/sbin/sambaconfig; "dpkg-reconfigure samba" replaces
+ it.
+ - Removed debian/samba.prerm.
+ - Cleaned up /etc/init.d/samba.
+ + Added infrastructure for debconf.
+ + Got rid of all /etc/samba/debian_config sillyness.
+ + Got rid of /etc/samba/smbpasswd conversion stuff for
+ compatibility with versions of Samba < 2.0.0final-2.
+ (closes: #127959, #34408, #113594)
+ * make samba.postinst ignore the absence of /var/log/{s,n}mb*;
+ makes for a clean upgrade path.
+ * Building with MSDFS support (closes: #116793)
+
+ -- Steve Langasek <vorlon@debian.org> Tue, 5 Mar 2002 14:14:33 -0600
+
+samba (2.2.3a-1) unstable; urgency=low
+
+ * New upstream version (closes: #135001)
+ * Potato builds were failing because debian/rules was not creating
+ debian/winbind/etc/logrotate.d/. A user having problems creating
+ Potato packages brought this to my attention. dh_installlogrotate
+ takes care of creating the directory for us, that's why we didn't
+ notice.
+ * Removed code that converts /etc/samba/smbpasswd from an ancient
+ format to the new format of Samba 2.0.0 and later.
+ Closes: #134375 - samba: postinst failed due to missing
+ /usr/bin/convert_smbpasswd.
+ * Re-organized FHS migration code in samba.postinst. Make sure we
+ don't fail when we move files that don't exist.
+ Closes: #133813 - samba: Install failed.
+ * Adding docs. to the libpam-smbpass package.
+ * Remove man pages for findsmb because we are not providing this
+ script.
+ Closes: #134181 - findsmb referenced, but not included.
+ * Removed replace.patch because it is now present upstream.
+ * Added patch from Jerry Carter to fix a problem when saving
+ document preferences for printing from NT clients.
+ * The real winbindd daemon is a forked process so we can't use
+ --make-pidfile when calling start-stop-daemon. Fixed
+ /etc/init.d/winbind to work around the issue. Thanks to
+ Lin Li <linl@xandros.com> for the patience and for reporting
+ the problems. Hopefully I got it right this time.
+
+ -- Eloy A. Paris <peloy@debian.org> Wed, 20 Feb 2002 18:39:03 -0500
+
+samba (2.2.3-6) unstable; urgency=low
+
+ * Make sure there are actual files in /var/state/samba before trying
+ to move them (Closes: #133534, #133510).
+ * Fix up the 2.2.3 makefile so that pam_smbpass builds correctly
+ again.
+
+ -- Steve Langasek <vorlon@debian.org> Tue, 12 Feb 2002 09:19:29 -0600
+
+samba (2.2.3-5) unstable; urgency=low
+
+ * Whoops, missed a spot on the samba.postinst -- will fail badly if
+ /var/state/samba/ no longer exists. Better get this fix into the
+ next upload. ;) (Closes: #133088)
+ * Regenerate configure only if it is older than configure.in.
+ * Fix smbd handling of network neighborhood lists, which was missed
+ in the FHS conversion (Closes: #133091)
+
+ -- Eloy A. Paris <peloy@debian.org> Sat, 9 Feb 2002 16:37:57 -0500
+
+samba (2.2.3-4) unstable; urgency=low
+
+ * FHS cleanup; files in /var are now properly sorted according to
+ their nature. (Closes: #102101)
+ * Remove patches to source/configure, since we now call autoconf to
+ regenerate this file cleanly.
+ * lintian fixes:
+ - Create winbind.conffiles and add /etc/logrotate.d/winbind and
+ /etc/init.d/winbind to it.
+ - Use a relative symlink for /usr/lib/cups/backend/smb.
+ - Removal of a .cvsignore file in the samba-doc package.
+ * winbind.init fixes:
+ - Corrected name of the pid file (Steve)
+ - Make start-stop-daemon create a pid file for winbindd since it
+ does not create one on his own.
+ * #DEBHELPER# is not needed in samba.postinst because we are adding
+ manually everything that debhelper adds automatically. In fact,
+ since we are calling update-rc.d without standard paramaters I
+ think we can't use #DEBHELPER#.
+ * Fix fatal syntax error in samba.prerm.
+
+ -- Steve Langasek <vorlon@debian.org> Thu, 7 Feb 2002 13:12:08 -0500
+
+samba (2.2.3-3) unstable; urgency=low
+
+ * work on lintian-cleanness in the package (wrong permissions,
+ maintainer scripts in need of debhelpering)
+ * /lib/security/pam_smbpass.so is now being included in the
+ libpam-smbpass package only, and not in both the libpam-smbpass and
+ samba packages (which was the case prior to 2.2.3-3.)
+ * Instead of making our patch scripts executable in the rules file
+ we run them through /bin/sh.
+ * New 'winbind' package that has all the winbind stuff that was in the
+ samba package in 2.2.3-2 and before.
+ * Added replace.patch: patch from Jeremy Allison to fix problems when
+ replacing or overwriting files in a Samba share. Patch was sent to
+ the samba mailing list.
+
+ -- Eloy A. Paris <peloy@debian.org> Tue, 5 Feb 2002 21:12:48 -0500
+
+samba (2.2.3-2) unstable; urgency=low
+
+ * add support to debian/scripts/{patch-source,unpatch-source} for
+ automatic updating and cleaning of <version.h>. This was a request
+ from the Samba Team: they wanted us to clearly mark our packages
+ so it is always known a user is running Samba with (possibly)
+ Debian-specific patches.
+ * Change init.d killscript link to K19samba, so we stop before autofs
+ (closes: 117327)
+ * Make our patch scripts executable in the rules file -- dpkg won't do
+ this for us (closes: #132415).
+
+ -- Steve Langasek <vorlon@debian.org> Mon, 4 Feb 2002 09:51:00 -0600
+
+samba (2.2.3-1) unstable; urgency=low
+
+ * New upstream release (closes: #131228).
+ * Restructured build system that provides DBS-like separation of
+ patches
+ * Fix typo in smbfs description (closes: #116209).
+ * Use killall -q in logrotate.d script, to avoid spurious cron
+ emails (closes: #130100).
+
+ -- Steve Langasek <vorlon@debian.org> Sat, 2 Feb 2002 19:56:18 -0500
+
+samba (2.2.2-12) unstable; urgency=high
+
+ * (Steve) Patch for source/client/client.c.
+ Closes: #86438 smbclient: Transfering several GB causes the average
+ speed to be messed up.
+ * Uploading with urgency=high to expedite the move from unstable
+ to testing because of the security problem fixed in -11.
+
+ -- Eloy A. Paris <peloy@debian.org> Fri, 25 Jan 2002 22:31:12 -0500
+
+samba (2.2.2-11) unstable; urgency=low
+
+ * Building with --with-libsmbclient. We have created two new
+ packages: libsmbclient and libsmbclient-dev. Hopefully this
+ will help some people that want to add the capability of
+ speaking SMB to their applications.
+ Closes: #117132 - libsmbclient support library?
+ * (Steve) Make swat do the right thing when reading (parsing)
+ the saved preferences in smb.conf.
+ Closes: #55617 swat mutilates the linpopup message command.
+ * Updated README.Debian. Updated descriptions in debian/control.
+ * Remembered to bump up version number in source/include/version.h
+ (need to automate this or else I'll keep forgetting.)
+ * (Steve) one liner for source/web/diagnose.c.
+ Closes: #106976 - smbd/nmbd not running message with swat/linuxconf.
+ * Added '|| true' to the post-rotate script so logrotate doesn't
+ fail if either nmbd or smbd is not running.
+ Closes: #127897 - /etc/logrotate.d/samba fails if there is no smbd process.
+ * Fixed incorrect file locations in swat's man page and added a
+ Debian-specific note to /usr/share/doc/swat/README.
+ Closes: #71586 swat: needs documentation fixes for debian.
+ * smbmount in the smbfs package does not have the setuid bit set.
+ Apparently, smbmount uses libsmb without checking the environment.
+ Thanks to Christian Jaeger <christian.jaeger@sl.ethz.ch> for
+ finding the local root exploit.
+ * Applied old patch from Jerry) Carter" <jerry@samba.org> to correct
+ the following two problems in Samba 2.2.2:
+ - %U and %G could not be used in services names
+ in smb.conf.
+ - %G would fail to be expanded in an "include = ..."
+ line.
+
+ -- Eloy A. Paris <peloy@debian.org> Sat, 19 Jan 2002 21:35:26 -0500
+
+samba (2.2.2-10) unstable; urgency=low
+
+ * (Steve) Add missing manual pages.
+ Closes: Bug#128928: missing manpages in smbfs.
+
+ -- Eloy A. Paris <peloy@debian.org> Sun, 13 Jan 2002 14:39:55 -0500
+
+samba (2.2.2-9) unstable; urgency=low
+
+ * (Steve) Fix broken URL's in HTML docs.
+ Closes: Bug#17741: bad links in html docs (at last!!!)
+
+ -- Eloy A. Paris <peloy@debian.org> Fri, 11 Jan 2002 13:37:07 -0500
+
+samba (2.2.2-8) unstable; urgency=low
+
+ * Added "Replaces: samba (<= 2.2.2-5)" to the smbclient section in
+ debian/control so rpcclient.1, which was in samba-2.2.2-5, does not
+ cause problems now that it is part of smbclient (>= 2.2.2-6). Closes:
+ Closes: Bug#128684: error upgrading smbclient in sid.
+
+ -- Eloy A. Paris <peloy@debian.org> Fri, 11 Jan 2002 11:42:40 -0500
+
+samba (2.2.2-7) unstable; urgency=low
+
+ * (Steve) Patch to make behavior honor what the docs. say about "hosts allow"
+ taking precedence over "hosts deny".
+ Closes: Bug#49249: swat: error with host deny ?!
+
+ -- Eloy A. Paris <peloy@debian.org> Thu, 10 Jan 2002 12:36:58 -0500
+
+samba (2.2.2-6) unstable; urgency=low
+
+ * (Steve) Adds manpage for rpcclient to the proper file,
+ removes smbtorture from the distro because this tool isn't intended for
+ widespread consumption.
+ Closes: #63057 - no manual page for smbtorture.
+ * (Steve) Removed -gnu from the configure arguments (--build, --host) in
+ debian/rules so config.sub is able to properly create the host and target
+ tuples.
+
+ -- Eloy A. Paris <peloy@debian.org> Wed, 9 Jan 2002 14:39:51 -0500
+
+samba (2.2.2-5) unstable; urgency=low
+
+ * Fixes from vorlon:
+ * Use /usr/bin/pager instead of more.
+ Closes: #125603: smbclient violates pager policy.
+ * Make /etc/logrotate.d/samba a conffile, send smbd and nmbd
+ a SIGHUP to have the log files reopened, fixes to
+ /etc/logrotate.d/samba.
+ Closes: #127897: log file rotation.
+ Closes: #118277: /etc/logrotate.d/samba not listed in conffiles.
+ * s/covert/convert/.
+ Closes: #121653 probable typo in install message.
+
+ -- Eloy A. Paris <peloy@debian.org> Sun, 6 Jan 2002 03:14:58 -0500
+
+samba (2.2.2-4) unstable; urgency=low
+
+ * Applied patch from Steve to work around problem in glibc that affects the
+ HPPA architecure. The patch detects the error condition at configure time
+ and compiles without LFS support if necessary.
+ Closes: Bug#126763: samba completely broken on hppa.
+ * Including unicode_map.1251.
+ Closes: Bug#126719: samba-common: unicode_map.1251 missing.
+ * Updated smbd daemon version to match Debian package version.
+ Closes: Bug#127199: Package version and smbd daemon version don't match.
+
+ -- Eloy A. Paris <peloy@debian.org> Mon, 31 Dec 2001 14:32:47 -0500
+
+samba (2.2.2-3) unstable; urgency=low
+
+ * Added some spaces in package description in debian/control.
+ Closes: #120730 - missing spaces in package description for nice
+ alignment.
+ * Spelling fixes.
+ Closes: #125328, #125329, #125330, #125367, #125365, #125403.
+ * Steve Langasek <vorlon@debian.org> is the co-maintainer of the Debian
+ Samba packages!!! Added him to the uploaders field in debian/control.
+
+ -- Eloy A. Paris <peloy@debian.org> Tue, 18 Dec 2001 00:54:25 -0500
+
+samba (2.2.2-2) unstable; urgency=low
+
+ * Backed out changes to source/filename.c per Andrew Tridgell's request.
+ This changes were introduced in 2.2.1a-7 as an attempt to fix #47493.
+ Tridge found out that they break smbd.
+ * Changed version number in source/includes/version.h so it is clear that
+ this is a version of Samba packaged for Debian. This is another request from
+ Tridge and will help the Samba Team to get bogus bug reports.
+ * Added Samba-HOWTO-Collection.pdf and other README files to the
+ /usr/share/doc/<package>/ directories.
+ * Installing libnss_winbind.so and pam_winbind.so.
+ Closes: #116790: nss and pam modules for winbind missing.
+ * Removed user-emacs-settings from changelog.
+
+ -- Eloy A. Paris <peloy@debian.org> Mon, 29 Oct 2001 19:16:26 -0500
+
+samba (2.2.2-1) unstable; urgency=low
+
+ * New upstream version.
+ * Temporary fix for #113763 (Steve Langasek)
+ * Quick hack to avoid smbmount reveal password length. Please note
+ that even with this hack there is a small window when password is
+ completely visible with 'ps aux'. There are other methods that should
+ be used to automate mounting of SMB shares.
+ Closes: #112195: smbmount-2.2.x reveals password length.
+ * Applied patch from Steve Langasek <vorlon@debian.org> to prevent
+ forcing use of setresuid() in Sparc.
+ Closes: #112779: samba build forces use of setresuid, which causes
+ smbd to fail on Sparc.
+
+ -- Eloy A. Paris <peloy@debian.org> Mon, 15 Oct 2001 10:26:10 -0400
+
+samba (2.2.1a-9) unstable; urgency=low
+
+ * Replaced $(LD) with $(CC) all the way through source/Makefile.
+ Closes: #111036: ld shouldn't be used to link shlibs.
+ * s/\/bin\/mail/\/usr\/bin\/mail/ in smb.conf's man page (HTML and
+ sgml as well.)
+ Closes: #110963: smb.conf: mail should be /usr/bin/mail.
+ * Documented better smbclient's -W behavior. Patch from Steve
+ Langasek.
+ Closes: #53672: smbclient: -W flag is interpreted as domain, not
+ workgroup.
+
+ -- Eloy A. Paris <peloy@debian.org> Tue, 4 Sep 2001 23:10:41 -0400
+
+samba (2.2.1a-8) unstable; urgency=low
+
+ * Set some reasonable default perms for the samba logdir (again,
+ thanks to vorlon :-)
+ Closes: #72529: insecure permissions on log files.
+
+ -- Eloy A. Paris <peloy@debian.org> Sun, 26 Aug 2001 15:40:47 -0400
+
+samba (2.2.1a-7) unstable; urgency=low
+
+ * Another attempt at fixing #47493. Patch from Steve Langasek
+ <vorlon@netexpress.net>. Let's keep our fingers crossed Steve!
+
+ -- Eloy A. Paris <peloy@debian.org> Sun, 26 Aug 2001 13:37:06 -0400
+
+samba (2.2.1a-6) unstable; urgency=low
+
+ * Backed out fix to #47493 introduced in 2.2.1a-4 as it is causing
+ smbd to die with signal 11 under some unidentified situations.
+ Closes: #109774: Latest debian version breaks printer driver download.
+ Closes: #109946: not all files appear in samba-exported directories.
+ * Another patch from Steve Langasek. This one adds quotes around
+ printer names for print systems it's reasonable for Debian to
+ support. Together with the patch in #29957 (see changelog for
+ 2.2.1a-4), this should take care of the problems with multi-word
+ printer names in Samba.
+
+ -- Eloy A. Paris <peloy@debian.org> Fri, 24 Aug 2001 21:12:27 -0400
+
+samba (2.2.1a-5) unstable; urgency=low
+
+ * Important changes that affect how Samba is built on Debian
+ machines are implemented in this release. All of this changes
+ were suggested by the energetic Steve Langasek <vorlon@debian.org>,
+ and his arguments were so sound and reasonable that I decided
+ to implement them. Here's Steve's original changelog:
+
+ * Fix up the build system to avoid needing to run configure
+ as root to answer questions we already know the answers to.
+ * In the process, make surprising progress towards being able to
+ cross-compile the samba packages.
+
+ -- Eloy A. Paris <peloy@debian.org> Fri, 24 Aug 2001 01:08:06 -0400
+
+samba (2.2.1a-4) unstable; urgency=low
+
+ * Fixed typo in smbmount's mount page.
+ Closes: #109317: smbfs: mistype in smbmount manpage.
+ * Included symlink to smbspool to better support CUPS printing.
+ Closes: #109509: include symlink for cups samba support.
+ * Applied patch from Steve Langasek <vorlon@netexpress.net> to
+ fix bug #29957.
+ Closes: #29957: samba strips trailing " from strings in smb.conf.
+ * First attempt at fixing #47493. Another patch from Steve "I want
+ a bug-free Samba" Langasek.
+ Closes: #47493: Samba doesn't handle ':' in dir names right.
+
+ -- Eloy A. Paris <peloy@debian.org> Tue, 21 Aug 2001 23:26:38 -0400
+
+samba (2.2.1a-3) unstable; urgency=low
+
+ * Steve Langasek <vorlon@netexpress.net> has been hard at work in
+ the last few days looking at the long list of open bugs filed
+ against the Samba packages. I don't know how to thank him. It's been
+ a pleasure working with Steve, and all the fixes, patches, etc. in
+ this release come from him. The bug list is greatly reduced thanks
+ to Steve's efforts.
+ * Steve's additions/modifications/patches/etc. are:
+ - New package that (libpam-smbpass) provides pam_smbpass. Before, this
+ was provided in another package but now the sources are part of
+ the Samba sources so we can start providing it from here.
+ Closes: #107043 - pam_smbpass now present in Samba source,
+ should be built from there
+ - Patch to source/smbd/service.c that allows admins to call
+ /bin/umount from the root postexec of a Samba share.
+ Closes: #40561 - samba pre/postexec commands do not work.
+ - Clear TMPDIR before starting smbd in /etc/init.d/samba.
+ Closes: #51295 - Problems with Samba and TMPDIR.
+ - Correction to documentation of "guest only".
+ Closes #38282 - "guest only" share still requires a password.
+ * Applied patch from Santiago Vila <sanvila@unex.es> to convert
+ /usr/sbin/mksmbpasswd from a shell script into a real awk script.
+ Sorry it took so long, Santiago; I hadn't realized you even
+ provided a patch :-)
+ Closes: #77891 - mksmbpasswd could be a real awk script.
+ * Updated description of the smbfs and smbclient packages. Also have
+ each package recommend the other.
+ Closes: #108650: Should suggest or recommend smbfs.
+
+ -- Eloy A. Paris <peloy@debian.org> Mon, 13 Aug 2001 22:21:55 -0400
+
+samba (2.2.1a-2) unstable; urgency=low
+
+ * Build-depends: depend on debhelper (>=2.0.103).
+ Closes: #105795: Build-Depends are wrong.
+ * Run samba's preinst and postinst scripts without -e so failed commands
+ do not abort installation.
+ Closes: #106384: postinstall crashes abnormally. (And really closes
+ #104471.)
+
+ -- Eloy A. Paris <peloy@debian.org> Thu, 26 Jul 2001 00:30:37 -0400
+
+samba (2.2.1a-1) unstable; urgency=low
+
+ * New upstream version.
+ * Make sure samba's postinst script exits with a zero status.
+ Closes: #104471: Samba postinst problem.
+
+ -- Eloy A. Paris <peloy@debian.org> Thu, 12 Jul 2001 21:55:21 -0400
+
+samba (2.2.1-1) unstable; urgency=low
+
+ * New upstream version.
+ Closes: #103339: config.guess and config.sub update required.
+ Closes: #98518: Samba 2.2 can't act as PDC for NT4/W2K due to
+ incompatibility with PAM.
+ Closes: #97447: nmbd crashes due to bugs in DAVE 2.5.2.
+ Closes: #95777: Samba 2.2 is unable to join or authenticate against
+ Samba 2.2 PDC domain.
+ Closes: #68842: samba should use PAM for password changing (I
+ haven't personally tried this one, but it's been
+ advertised this works.)
+ Closes: #102506: PAM account checking fails.
+ Closes: #102518: Complains about unknown paramter "obey pam
+ restrictions"
+ Closes: #94774: Build failure on PARISC machines.
+ * Moved away from /etc/cron.weekly/samba for log file rotation.
+ Now using logrotate.
+ Closes: #95548: typo in /etc/cron.weekly/samba.
+ Closes: #74951: nmbd does not rename its log file.
+ * Removed Debian-specific addtosmbpass.8 man page since this script
+ is not longer provided upstream. Users should use the smbpasswd
+ program instead.
+ * Updated sample /etc/samba/smb.conf to reflect the recent changes
+ affecting handling of PAM authentication. Also updated
+ /etc/pam.d/samba.
+
+ -- Eloy A. Paris <peloy@debian.org> Wed, 11 Jul 2001 00:44:14 -0400
+
+samba (2.2.0.final.a-1) unstable; urgency=high
+
+ * New upstream version (contains security fix from DSA-065-1.)
+ Closes: #97241: samba 2.2.0 fails to process hostnames in
+ "hosts allow" config line.
+ * Removed Debian-specific addtosmbpass.8 man page since this script
+ is not longer provided upstream. Users should use the smbpasswd
+ program instead.
+ Closes: #98365: addtosmbpass is missing from 2.2.0.final-2.
+ * Updated sample /etc/samba/smb.conf to reflect the recent changes
+ affecting handling of PAM authentication. Also updated
+ /etc/pam.d/samba.
+
+ -- Eloy A. Paris <peloy@debian.org> Sun, 24 Jun 2001 11:11:59 -0400
+
+samba (2.2.0.final-2) unstable; urgency=low
+
+ * Added libcupsys2-dev to Build-Depends.
+ * Samba depends now (again) on netbase so update-inetd is always
+ available for the Samba maintainer scripts.
+ Closes: #86063: Fails to uninstall if inetd is not installed.
+ * Updated source/config.{sub,guess} so ARM built doesn't fail.
+ Closes: #94480: config.sub out of date; can't build on arm.
+ Closes: #85801: config.sub/guess out of date.
+ * Not using brace expansion, i.e. {foo,bar} in any of the maintainers
+ scripts nor in debian/rules.
+ Closes: #88007: samba postrm has is not POSIX sh compliant.
+
+ -- Eloy A. Paris <peloy@debian.org> Sat, 21 Apr 2001 17:27:18 -0400
+
+samba (2.2.0.final-1) unstable; urgency=low
+
+ * New upstream release. Lots of new things. See WHATSNEW.txt.
+ * Goofy version number because of my stupidity when assigning version
+ numbers to the CVS packages I have been uploading to experimental.
+ Will be fixed when 2.2.1 is released. I've no doubts a 2.2.1 release
+ will follow soon.
+
+ -- Eloy A. Paris <peloy@debian.org> Tue, 17 Apr 2001 22:58:14 -0400
+
+samba (2.2.0.cvs20010416-1) experimental; urgency=low
+
+ * CVS update.
+
+ -- Eloy A. Paris <peloy@debian.org> Mon, 16 Apr 2001 21:25:15 -0400
+
+samba (2.2.0.cvs20010410-1) experimental; urgency=low
+
+ * CVS update.
+ * Added libreadline4-dev to Build-Depends.
+
+ -- Eloy A. Paris <peloy@debian.org> Tue, 10 Apr 2001 16:53:45 -0400
+
+samba (2.2.0.cvs20010407-1) experimental; urgency=low
+
+ * CVS update. Includes what is in 2.2.0alpha3.
+
+ -- Eloy A. Paris <peloy@debian.org> Sat, 7 Apr 2001 16:00:33 -0400
+
+samba (2.2.0.cvs20010316-1) experimental; urgency=low
+
+ * Started working on Samba 2.2.0. Using the SAMBA_2_2_0 branch
+ from Samba CVS.
+ * Not compiling rpctorture as it has compile errors. Change in
+ debian/rules.
+ * Removed Linux kernel 2.0.x and smbfs compatibility baggage. Now
+ the smbfs does not support 2.0.x kernels; a kernel > 2.2.x is
+ needed to use smbfs. Updated debian/control, debian/rules and
+ README.Debian to reflect this change.
+ * Added to swat a versioned dependency on samba (so a user is forced to
+ install a new version of swat each time a new version of samba is
+ installed.)
+
+ -- Eloy A. Paris <peloy@debian.org> Sun, 18 Mar 2001 14:21:14 -0500
+
+samba (2.0.7-5) unstable; urgency=medium
+
+ * Transition from suidmanager to dpkg-statoverride.
+
+ -- Eloy A. Paris <peloy@debian.org> Thu, 18 Jan 2001 23:51:56 -0500
+
+samba (2.0.7-4) unstable; urgency=medium
+
+ * Applied Urban Widmark <urban@teststation.com> fixes to smbmount. Urban
+ is the maintainer of the smbfs in the kernel and of the userland
+ utilities.
+ * Links to HTML documents are correct now.
+ Closes: #69439: swat: Broken help file symlinks
+ Closes: #72615: samba-doc directory changed: removed htmldocs from path
+ Closes: #75847: swat: Wrong symlink
+ Closes: #66857: Wrong links to html documents.
+ Closes: #77912: misplaced documentation symlinks for swat
+ * Building Samba with CUPS support. For this I reverted the change to
+ source/configure.in that I did in 2.0.7-3 and re-ran autoconf.
+ Closes: #59038: samba: not compiled with cups support.
+ * Fix against previous known/unknown user time difference patch to swat
+ (make username / password lookups take the same time.) Remove CGI
+ logging code in Swat.
+ Closes: #76341 - Security holes in swat
+ * Updated Build-depends.
+ * Updated debian/copyright to refer to the correct location of the GPL.
+ * debian/rules: changed DESTDIR to `pwd`/debian/samba (was
+ `pwd`/debian/tmp.)
+ * debian/rules: added '--sourcedir=debian/samba' to dh_movefiles (for some
+ strange reason dh_installdirs is not creating debian/tmp/ so I needed
+ to tweak everything to install stuff in debian/samba rather than in
+ debian/tmp.)
+ * debian/control: changed section of samba-docs to 'doc' (was 'docs')
+ * Using relative symlinks in /usr/share/samba/swat/ (changed debian/rules
+ and source/scripts/installswat.sh.)
+ * Fixed (by tweaking debian/rules)
+ /usr/bin/{smbmnt,smbumount-2.*,smbmount-2.*} to be suid.
+ * Added "Provides: samba-client" to smbclient's section in control.
+ Closes: #71143: smbclient: Smbclient should provide samba-client.
+ * Fix for desired_access being zero in map_share_mode() (patch to
+ source/smbd/nttrans.c.) Thanks to Gary Wilson
+ <wilsong@sergievsky.cpmc.columbia.edu> for bringing this patch to my
+ attention.
+ * Hacked source/lib/util_sec.c so smbd works fine in both 2.0.x and
+ 2.2.x kernels even when the build is done in a system running
+ a 2.2.x kernel.
+ Closes: #78858: samba-common: samba2.0.7 needs kernel 2.2.x but
+ doesnt depend on it.
+ Closes: #72758: README.Debian should comment on 2.0.x kernels.
+ Closes: #56935: Samba 2.0.6 and Kernel 2.0.x.
+ Closes: #58126: Samba 2.0.6 and Kernel 2.0.x -- more info.
+ Closes: #60580: samba: failed to set gid.
+ Closes: #64280: Samba panics, can't set gid.
+ Closes: #66816: Must deal with brokenness under 2.0.x.
+ Closes: #67682: potatoe samba 2.0.7-3 out of order, 2.0.5a-1 OK.
+ Closes: #69735: PANIC: failed to set gid
+ Closes: #66122: "smbclient -L localhost -U%" returns with "tree
+ connect failed: code 0".
+ Closes: #57637: Samba says tree connect error.
+ Closes: #58015: potato samba wins support is broken.
+ * Fixed comments in sample smb.conf to point to the correct location.
+ Closes: #69578: comments in smb.conf points to wrong path.
+ * Move codepages from /etc/samba/codepages/ to
+ /usr/share/samba/codepages/.
+ Closes: #63813: samba; codepages should go in /usr/lib.
+ * Moved /var/samba/ to /var/state/samba/.
+ Closes: #49011: samba package not FHS compliant.
+ * Hacked source/configure.in (and re-ran autoconf) so yp_get_default_domain()
+ is found.
+ Closes: #44558: netgroup support missing in samba 2.0.5a-1.
+ * /etc/init.d/samba was calling start-stop-daemon with both --pidfile and
+ --exec. Got rid of --exec so --pidfile works.
+
+ -- Eloy A. Paris <peloy@debian.org> Thu, 11 Jan 2001 00:15:57 -0500
+
+samba (2.0.7-3) frozen unstable; urgency=high
+
+ * Release manager: this closes a RC bug.
+ * Commented out the section in source/configure.in that auto-detects
+ CUPS support and then ran autoconf to generate a new configure
+ script. This was done to prevent machines that have libcupsys-dev
+ installed from detecting CUPS support and adding an unwanted
+ dependency on libcupsys. This way the whole printing system
+ won't break on upgrades. CUPS support should be added after
+ Potato is released.
+ Closes: #65185: samba-common: Upgrading removes printing system.
+ Closes: #64496: smbfs: smbfs on powerpc has a dependency on cupsys.
+ * Updated README.debian.
+ Closes: #64594: Old README.Debian in /usr/share/doc/samba.
+
+ -- Eloy A. Paris <peloy@debian.org> Tue, 20 Jun 2000 19:16:04 -0400
+
+samba (2.0.7-2) frozen unstable; urgency=high
+
+ * Release manager: this closes RC bug #63839 that prevents Samba
+ to be built from source.
+ * Fixed a stupid typo in debian/rules that was preventing Samba
+ to be built from source.
+ Closes: #63839: samba_2.0.7-1(frozen): build error (SAMBABOOK dir)
+ * I forgot to mention that O'Reilly's book "Using Samba" was donated
+ to the Open Source community. The book was included in Samba 2.0.7
+ in HTML format and is part of the Debian Samba package since
+ Samba 2.0.7-1.
+ * In Samba 2.0.7-1, the "Using Samba" book and a number of HTML help
+ files were supposed to be provided in both the swat and the samba-doc
+ packages. This duplication was a waste of space. Starting with
+ Samba 2.0.7-2, swat recommends samba-doc and the book and the HTML
+ files are included only in samba-doc, and are accessed via symlinks
+ from within swat.
+ Closes: #58810: superfluous files in swat?
+ * Added a 'echo "."' to /etc/init.d/samba in the reload) section.
+ Closes: #63394: "echo ." missing in reload section of init.d script
+ * Fixed typo in docs/htmldocs/using_samba/ch06_05.html.
+ Closes: #64344: typo "encrypted passwords"
+ * Cleaned up samba's postrm script so important common files aren't
+ deleted when samba is purged. Created a samba-common.postrm script.
+ Closes: #62675: purging samba removes /etc/samba/smb.conf.
+ Closes: #63386: samba --purge removes /etc/samba dir even though
+ smbclient/smbfs/samba-common packages are still installed
+
+ -- Eloy A. Paris <peloy@debian.org> Wed, 3 May 2000 02:42:07 -0400
+
+samba (2.0.7-1) frozen unstable; urgency=low
+
+ * New upstream version. Dear Release Manager: please allow this
+ package to go to frozen as it contains fixes to a _lot_ of problems.
+ You can take a look at all the problems fixed by this release in
+ the official upstream announcement at
+ http://us1.samba.org/samba/whatsnew/samba-2.0.7.html.
+ * Added --with-utmp to add utmp support to smbd (this is new in Samba
+ 2.0.7)
+ * Closes: #62148 - samba not rotating filled logs.
+ * Closes: #56711: Samba doesn't manage well long share name (please note
+ that it's possible to connect to shares with names longer than
+ 14 characters but the share will be listed with a name truncated to
+ 13 characters.)
+ * Closes: #51752 - NT DOMAIN - NET USE * /HOME not mapping (error 67).
+ Closes: #50907 - logon path not working.
+ This is not a bug, it's just Samba doing the same thing an NT server
+ does. See WHATSNEW.txt and smb.conf's man page for details.
+ * Closes: #48497 - error executing smbsh in debian-potato. (smbwrapper
+ is not supported anymore.)
+ * Closes: #58994 swat: typo in swat description.
+ * Closes: #45931 - Samba dies with SIGILL on startup. (Hardware
+ problems, person that reported the bug never came back.)
+ Closes: #54398 - smbadduser fails, looks for ypcat.
+ * Fixed swat's man page to include Debian specific installation
+ instructions. There's not necessary to edit /etc/services or
+ /etc/inetd.conf.
+ (Closes: #58616 - incomplete install config && incorrect installation
+ instructions.)
+ * s/SBINDIR/\"/usr/sbin\"/g in source/web/startstop.c to prevent swat
+ to look for smbd and nmbd in the wrong place when requested to start or
+ stop smbd or nmbd.
+ (Closes: #55028 - swat can't start samba servers.)
+ * Closes: #37274: smbclient does not honour pot. (Tested and seems to be
+ working now.)
+ * Not confirmed, but should fix #56699, #62185, #56247, #52218, #43492,
+ #50479, #39818, #54383, #59411.
+ (please re-open any of this if the problem still exists - I was unable
+ to confirm any of this because I could never reproduce them.)
+ Closes: #56699 - Samba's nmbd causes random kernel oops several
+ times in a row.
+ Closes: #62185 - nmbd's forking until no more file descriptors are
+ available.
+ Closes: #56247 - session setup failed: ERRSRV - ERRbadpw.
+ Closes: #52218 - Either wins proxy does not work, or I don't understand
+ it.
+ Closes: #43492 - intermittent problem changing password.
+ Closes: #50479 - Can't access windows 2000 shares with samba.
+ Closes: #39818 - samba-common: Upgrading Samba from the Slink version.
+ Closes: #54383 - samba-common: Missing /etc/smb.conf.
+ Closes: #59411 - smbclient: cannot browse Win2k shares.
+
+ -- Eloy A. Paris <peloy@debian.org> Thu, 27 Apr 2000 16:07:45 -0400
+
+samba (2.0.6-5) frozen unstable; urgency=low
+
+ * Oppsss! samba-common doesn't depend on libcupsys1 so the binaries
+ in this package are broken unless libcupsys1 is installed.
+ samba-common has a "grave" bug because of this. Instead of adding
+ libcupsys1 to the Depends: list of each package in debian/control
+ I investigated why dh_shlibs was not picking the dependency
+ automatically. It turns out that it's probably a bug in libcupsys1
+ because the format of its shlibs file is not correct. I fixed that
+ file (/var/lib/dpkg/info/libcupsys1.shlibs) and now dependencies are
+ picked correctly. I'll talk to the libcupsys1 maintainer.
+
+ I think the addition of CUPS support to Samba is a big change that
+ should not go into Frozen. So, I decided to back up the addition
+ of CUPS support I did in 2.0.6-4 to minimize problems. I'll add
+ CUPS support again when I start working on Samba for Woody.
+ (Closes: #59337 - samba-common has a missing dependency)
+
+ -- Eloy A. Paris <peloy@debian.org> Wed, 1 Mar 2000 08:40:02 -0500
+
+samba (2.0.6-4) frozen unstable; urgency=low
+
+ * It seems that sometimes nmbd or smbd are not killed when upgrading.
+ I think it is because in samba's prerm script I was calling
+ start-stop-daemon with the --pidfile switch and in old versions of
+ Samba the nmbd and smbd daemons did not store their PIDs in a file in
+ /var/samba/. I changed debian/samba.prerm so the existence of the
+ PID files is checked before calling "start-stop-daemon --pidfile ..."
+ If the PID files do not exist then start-stop-daemon is called
+ without the --pidfile parameter.
+ (Closes: #58058 - upgrade from slink went badly)
+ * Fixed typo in description of swat package in debian/control.
+ * Installed libcupsys1-dev so the configure script picks up CUPS
+ and Samba is compiled with CUPS support. Also added libcupsys1 to
+ the Depends: list of package samba in debian/control.
+ (Closes: #59038 - samba not compiled with cups support)
+ * Added a small paragraph to debian/README.debian warning about possible
+ problems with the WINS code in Samba 2.0.6.
+
+ -- Eloy A. Paris <peloy@debian.org> Mon, 28 Feb 2000 14:00:42 -0500
+
+samba (2.0.6-3) frozen unstable; urgency=low
+
+ * Applied patch posted by Jeremy Allison to the samba mailing list
+ that should take care of the internal errors reported in bug #52698
+ (release-critical). Wichert: please test as I never could reproduce
+ it here.
+ (Closes: #52698 - samba gets interbal errors)
+ * Moved samba-docs to the 'docs' section.
+ (Closes: #51077 - samba-doc: wrong section)
+ * Added reload capability to /etc/init.d/samba (only for smbd because
+ nmbd does not support reloading after receiving a signal).
+ (Closes: #50954 - patch to add reload support to /etc/init.d/samba)
+ * Corrected "passwd chat" parameter in sample /etc/samba/smb.conf so
+ Unix password syncronization works with the passwd program currently
+ in Potato. Thanks to Augustin Luton <aluton@hybrigenics.fr> for
+ the correct chat script.
+ * Stole source/lib/util_sec.c from the CVS tree of what will become
+ Samba 2.0.7 or whatever so we can use the same binaries under
+ both 2.0.x and 2.2.x kernels.
+ (Closes: #51331 - PANIC: failed to set gid)
+ * smbadduser is now provided as an example and it's customized for Debian.
+ I am not providing this script in /usr/sbin/ because then I would need
+ a dependency on csh, something that I don't want to do.
+ (Closes: #51697, #54052)
+ * Fixed the short description of the smbfs package in debian/control.
+ (Closes: 53534 - one-line description out of date).
+
+ -- Eloy A. Paris <peloy@debian.org> Tue, 23 Nov 1999 16:32:12 -0500
+
+samba (2.0.6-2) unstable; urgency=low
+
+ * samba-common now depends on libpam-modules (not on libpam-pwdb, which
+ I have been told is obsolete). I modified /etc/pam.d/samba accordingly
+ to reflect the change.
+ (Closes: Bug#50722: pam pwdb dependence?).
+ * The old /etc/pam.d/samba file which had references to pam_pwdb caused
+ smbd to die with a signal 11. The new /etc/pam.d/samba file fixes
+ this problem.
+ (Closes: #50876, #50838, #50698)
+ * Compiled with syslog support (use at your own risk: syslog support
+ is still experimental in Samba). I added the parameters "syslog = 0"
+ and "syslog only = no" to the sample smb.conf to avoid pestering
+ users that do not want Samba to log through syslog.
+ (Closes: Bug#50703 - syslog only option doesn't work)
+ * Removed the stupid code in the smbmount wrapper script that tries
+ to load the smbfs module if smbfs is not listed in /proc/filesystems.
+ (Closes: Bug#50759 - Non-root can't run smbmount if SMBFS is compiled
+ as a module in the kernel)
+ * Added /bin/mount.smb as a symlink pointing to /usr/bin/smbmount so
+ 'mount -t smb ...' works just as 'mount -t smbfs ...'.
+ (Closes: Bug#50763 - 'mount -t smb' doesn't work)
+
+ -- Eloy A. Paris <peloy@debian.org> Sat, 20 Nov 1999 18:53:35 -0500
+
+samba (2.0.6-1) unstable; urgency=low
+
+ * Samba 2.0.6 has been released. This is the first try of the Debian
+ Samba packages. I know for sure that smbd won't work properly on
+ 2.0.x kernels because the patch that Wichert sent me does not apply
+ to the new source/lib/util_sec.c in Samba 2.0.6. That file was
+ completely re-written by Tridge.
+ * Updated README.Debian.
+ * A new client utility called smbspool appeared in Samba 2.0.6. I added
+ this utility to the smbclient package, although I haven't tried it yet.
+ * Added the symlink /sbin/mount.smbfs that points to /usr/bin/smbmount.
+ This is to be able to type "mouont -t smbfs ...". This symlink goes
+ in the smbfs package, of course.
+ * This new release should close the following bugs (some of these
+ are fixed for sure in this new upstream release, some others I could
+ not reproduce but I believe they are fixed if they were real bugs.
+ As always, please feel free to re-open the bugs if the problem is not
+ solved).
+ Closes: Bug#33240: icmp mask needs a bug workaround.
+ Closes: Bug#37692: samba: Has problems detecting interfaces.
+ Closes: Bug#38988: samba: Truly bizzare behavour from nmbd.
+ Closes: Bug#46432: samba-2.0.5a-2: nmbd does not appear to broadcast
+ properly.
+ Closes: Bug#44131: smbfs: no longer possible to set file and
+ directory-modes.
+ Closes: Bug#46992: smbmount-2.2.x manpage wrong.
+ Closes: Bug#42335: smbfs: missing options from the new 2.2.x commandline.
+ Closes: Bug#46605: smbmnt segfaults.
+ Closes: Bug#48186: smbmount.
+ Closes: Bug#38040: smbfs: Please add /sbin/mount.smb [included].
+ Closes: Bug#47332: smbmount: could -f and -P be added back?
+ * Samba has been compiled with PAM support (closes: Bug#39512 - samba PAM
+ module). To succesfully add PAM support, I created /etc/pam.d/samba and
+ added this file as a conffile for the samba-common package. I also made
+ samba-common depend on libpam-pwdb.
+ * Added simple man pages for the wrapper scripts smbmount and smbmount.
+ (Closes: Bug#44705 - Missing smbmount man page)
+ * Installed libreadlineg2-dev in my system so smbclient now has a
+ "history" command and libreadline support :-)
+ * This time I did add a check to the smbmount wrapper script to see if
+ the kernel has support for smbfs, as suggested by Jeroen Schaap
+ <J.Schaap@physiology.medfac.leidenuniv.nl>. I mentioned in the changelog
+ for samba-2.0.5a-3 that I did this but I forgot at the end.
+
+ -- Eloy A. Paris <peloy@debian.org> Thu, 11 Nov 1999 12:08:15 -0500
+
+samba (2.0.5a-5) unstable; urgency=low
+
+ * I am sorry to report that the smbwrapper package is gone for the
+ moment. The reason for this is twofold: first of all, smbwrapper
+ is completely broken in Samba-2.0.5a (it compiles but it doesn't
+ run) and in the upcoming Samba-2.0.6 it doesn't even compile. Second,
+ when I asked Andrew Tridgell (father of Samba) about the state of
+ smbwrapper he told me that Ulrich Drepper (head of the glibc project)
+ broke on purpose the glibc stuff in which smbwrapper is based.
+ Consequently, Tridge recommended me to compile Samba without
+ support for smbwrapper. When, I have no idea. Sorry folks. Here is
+ the original message I received from Andrew:
+
+ > 1) 2.0.5a's smbwrapper doesn't work under glibc2.1, and pre-2.0.6's
+ > smbwrapper doesn't even compile under glibc2.1.
+
+ yep, Ulrich deliberately broke it. It won't get fixed till glibc
+ allows the sorts of games it plays to work again. I suggest you turn
+ it off in your build scripts until that gets sorted out.
+
+ * Swat's file are now in /usr/share/samba/ instead of
+ /usr/lib/samba/ (bug #49011).
+ * Man pages now in /usr/share/man/ instead of /usr/man/ (bug #49011).
+
+ -- Eloy A. Paris <peloy@debian.org> Tue, 2 Nov 1999 12:59:13 -0500
+
+samba (2.0.5a-4) unstable; urgency=low
+
+ * Applied patch from our fearless leader (Wichert) to fix the darn bug
+ that prevents Samba to work on 2.0.x kernels if it was compiled
+ in a system running a 2.2.x kernel. This closes #40645 (build uses
+ setresuid which doesn't work under 2.0.34 (does apparently under
+ 2.2.x) ).
+ * Fixed the entry that swat's postinst script adds to /etc/inetd.conf
+ so it is '#<off># swat\t\tstream\ttcp\tnowait.400 ...' instead of
+ '#<off>#swat\t\tstream\ttcp\tnowait.400 ...'. The old way caused
+ 'update-inetd --enable swat' to leave the entry for swat disabled.
+ Thanks to Dave Burchell <burchell@inetnebr.com> for finding out
+ this problem. This closes #48762 (swat uses non-standard syntax to
+ comment out inetd.conf entry).
+ * /usr/sbin/swat does not think anymore that the smbd daemon lives
+ in /usr/local/samba/bin/. To fix this I am running now source/configure
+ with "--prefix=/usr --exec-prefix=/usr". This closes #47716 (samba
+ 'swat' fails: incorrect hardwired path in the binary).
+
+ -- Eloy A. Paris <peloy@debian.org> Sun, 31 Oct 1999 03:42:38 -0500
+
+samba (2.0.5a-3) unstable; urgency=low
+
+ * I am pretty darn busy with my MBA, I apologize for the long time it's
+ taking to squash bugs in the Samba packages.
+ * Built with debhelper v2 for FHS compliancy. Changed a couple of
+ things in debian/rules to accomodate for the new place for the docs.
+ I also had to change debian/{samba.postinst,samba.prerm,swat.postinst}
+ to make sure that the symlink from /usr/doc/xxx exists and points to
+ /usr/share/doc/xxx (the reason for this is that I am not letting
+ debhelper to create these scripts for me automatically).
+ * Built with latest libc6.
+ * smbfs: finally, the nasty bug that causes smbmount to die after
+ a while is gone thanks to Ben Tilly <Ben_Tilly@trepp.com>.
+ The problem was just a typo in source/client/smbmount.c.
+ This closes grave bug #42764 (smbmount dies) and #43341
+ (smbfs-2.2.x won't function after a while).
+ * Fixed the smbmount wrapper script to eliminate a bashism (closes
+ #45202 - "wrapper scripts use $* instead of "$@") and to recognize
+ 2.3.x and 2.4.x kernels (closes #47688 - "smbfs: does not recognize
+ kernel 2.3.x").
+ * Added a check to the smbmount wrapper script to see if the
+ kernel has support for smbfs, as suggested by Jeroen Schaap
+ <J.Schaap@physiology.medfac.leidenuniv.nl>.
+ * swat's man page is now part of the swat package, not of the samba
+ package. This closes #44808 (Samba has a man page for swat, but
+ the binary is not included).
+ * The interface program smbrun is not longer needed by smbd because
+ of the availability of execl() under Linux. Because of this, the
+ smbrun is not even being compiled. Since there is no need for smbrun
+ now, the smbrun man page was taken out of the samba package. This
+ closes #45266 (/usr/bin/smbrun missing).
+ * smbpasswd is now part of the samba-common package, and not part of
+ the samba package. This is to let administrators that do not want
+ to install a full Samba server administer passwords in remote
+ machines. This closes bug #42624 (smbpasswd should be included in
+ smbclient). This bug report also suggests that swat becomes part of
+ the samba package, that smbfs becomes part of the smbclient package,
+ and that the binary smbpasswd becomes part of the smbclient package.
+ I moved smbpasswd to the samba-common package but I am reluctant to
+ do the other things the bug report suggests.
+ * In order to keep dpkg happy when moving smbpasswd from the samba
+ package to samba-common, I had to add a "Replaces: samba (<= 2.0.5a-2)"
+ in the control section of the samba-common package and a
+ "Replaces: samba-common (<= 2.0.5a-2)" in the control section of the
+ samba package (in debian.control).
+ * Samba is now being compiled with the "--with-netatalk" option. This
+ closes #47480 (Could samba be compiled with the --with-netatalk option).
+ * All packages that depend on samba-common have a versioned dependency
+ now. This was accomplished by adding "(= ${Source-Version})" to the
+ relevant sections of debian/control. Thanks t Antti-Juhani Kaijanaho
+ <gaia@iki.fi> for the hint. This closes #42985 (samba should probably
+ have a versioned depends on samba-common).
+ * Made sure the file docs/textdocs/DIAGNOSIS.txt gets installed in all
+ the Samba packages. This closes bug #42049 (no DIAGNOSTICS.txt file).
+ * Added the smbadduser helper script to the samba package. This closes
+ #44480 (Samba doesn't come with the smbadduser program).
+ * Applied patch from szasz@triton.sch.bme.hu that prevents smbmount
+ to leave an entry in /etc/mtab for a share that could not be mounted
+ because of invalid user of password. The patch also allows smbumount
+ to unmount the share in the event that something goes wrong with the
+ smbmount process. This closes bug #48613 (Mount/umount problems +
+ patch) as well as #44130 (failed mount is still mounted).
+ * smbmount-2.2.x is now setuid root. This is needed for the patch
+ applied above to be effective. If smbmount-2.2.x is not setuid root
+ then an entry will be left in /etc/mtab even when the mount
+ fails. I had to add "usr/bin/smbmount-2.2.x" to debian/smbfs.suid
+ for this to work.
+
+ -- Eloy A. Paris <peloy@debian.org> Wed, 27 Oct 1999 10:36:13 -0400
+
+samba (2.0.5a-2) unstable; urgency=low
+
+ * This version is basically the same as 2.0.5a-1 but it was compiled
+ on a Potato system with glibc2.1. See below the change log for 2.0.5a-1
+ for more information.
+
+ -- Eloy A. Paris <peloy@debian.org> Tue, 27 Jul 1999 02:25:29 -0400
+
+samba (2.0.5a-1) stable; urgency=high
+
+ * I'm back from the Honey Moon. We are pretty busy because we are moving
+ to Pittsburgh (from Caracas, Venezuela) in aprox. 24 hours and we still
+ have plenty of things to pack and to do. Samba 2.0.5 was released
+ while I was in the Honey Moon and it is just now (almost 3 AM) when
+ I have time to package it.
+ * Because of the security problems fixed in 2.0.5, this upload goes
+ to both stable and unstable (the Security Team asked for this).
+ * This release (2.0.5a-1) was compiled on a Slink system. 2.0.5a-2 will
+ be compiled on a Potato system.
+ * Added a "Replaces: samba (<= 1.9.18p10-7)" to the samba-common
+ section in debian/control (as suggested by Steve Haslam
+ <araqnid@debian.org>) to fix the problems that appear when upgrading
+ from the Samba package in Slink. Please test this as I am completely
+ unable to do so. This should fix bug #39818 (Upgrading Samba from the
+ Slink version).
+ * Removed the hacks to the autoconf stuff that I added to 2.0.4b-2 in
+ order to have defined several socket options when compiling with
+ Linux 2.2.x kernel headers - the fix is now upstream.
+ * Finally!!! smbmount was re-written (thanks Tridge :-) to use a command
+ line syntax similar to the one used by the old smbmount (for 2.0.x
+ kernels). This means that the wrapper script is no longer necessary
+ so I removed it. In its place there is a simple wrapper script that
+ calls smbmount-2.0.x or smbmount-2.2.x depending on the kernel that is
+ running.
+ * Because of the wedding, the Honey Moon, and our move to Pittsburgh,
+ I can't work on fixing other bugs in this release.
+
+ -- Eloy A. Paris <peloy@debian.org> Tue, 27 Jul 1999 02:18:51 -0400
+
+samba (2.0.4b-3) unstable; urgency=low
+
+ * Stupid mistake: I forgot to add /usr/bin/smbumount to debian/smbfs.files
+ and because of this /usr/bin/smbumount was part of the samba package
+ instead of part of the smbfs package.
+
+ -- Eloy A. Paris <peloy@debian.org> Thu, 1 Jul 1999 01:51:24 -0400
+
+samba (2.0.4b-2) unstable; urgency=low
+
+ * Dark (and archive maintainers): please remove from Potato the smbfsx
+ binary package and also the old source package for smbfs. smbfs and
+ smbfsx have been merged starting with this version.
+ * Merged the old smbfs package with Samba. Now there is only one package
+ for the smbfs utilities and is called "smbfs". The package smbfsx
+ does not exist any more and this new smbfs package must be used
+ for both 2.0.x and > 2.1.x kernels.
+ * A wrapper script was added to handle the syntax change in smbmount
+ in the new smbfs utilities (required for kernels > 2.1.70). The
+ home page for this script is http://www.wittsend.com/mhw/smbmount.html.
+ Please _note_ that this will change (for good) in Samba 2.0.5 :-)
+ * Added debian/smbumount.sh. It's another wrapper that calls smbumount-2.2.x
+ or smbumount-2.0.x depending on the kernel currently running.
+ * Not using -t for savelog in cron.weekly script.
+ * Recompiled without libreadlineg-dev (Samba does not seem to be using
+ it so unnecessary dependencies are produced).
+ * glibc2.1 build.
+ * Removed smbpasswd.8 man page from the debian/ directory because it is
+ now being provided upstream.
+ * Got rid of the ugly hack I put in source/lib/util_sock.c to have
+ IPTOS_LOWDELAY and IPTOS_THROUGHPUT defined. Now I patched the
+ autoconf stuff to #include <netinet/ip.h>. I've sent the patch to
+ Jeremy Allison so we have this upstream.
+
+ -- Eloy A. Paris <peloy@debian.org> Mon, 28 Jun 1999 17:47:19 -0400
+
+samba (2.0.4b-1) unstable; urgency=low
+
+ * New upstream release. This release fixes the following Debian bugs:
+ #33838 (Amanda/ Samba 2.0.2 and backing up large filesystems) and
+ #33867 (Amanda 2.4.1 and Samba 2.0.2 and large filesystems). Jeremy
+ Allison released Samba 2.0.4 and found out that there were a couple
+ of minor bugs so he released 2.0.4a. Then he found out about more
+ serious bugs and released 2.0.4b. I have built this package several
+ times between yesterday and today because of this. Now I am releasing
+ the Debian packages for Samba with what I believe will be the latest
+ release the Samba Team will make at least in the next 4 days (Jeremy
+ is taking a short vacation).
+ * Still compiling against glibc2.0 (sorry about that :-)
+ * Hacked source/smbwrapper/smbsh.c to fix the problem
+ of smbsh not finding the shared library smbwrapper.so. It looks
+ now in /usr/lib/samba/ for this file. This fixes #32971, #32989,
+ #33278, #34911 and #36317.
+ * Made smbfsx depend on samba-common because smbfsx uses /etc/samba/smb.conf
+ and /etc/samba/codepages/. This fixes #33128 (smbmount complains about
+ missing /etc/smb.conf).
+ * Package swat does not depend on httpd anymore (there's no need to).
+ This fixes #35795 (swat requires httpd).
+ * Renamed smbmount-2.1.x and smbumount-2.1.x to smbmount-2.2.x and
+ smbumount-2.2.x. Same applies to the man pages.
+ * Changed minor type in smbmount's man page (changed "\"" by "\'"). This
+ fixes #34070 (wrong quotes in manpage).
+ * Used Fabrizio Polacco's <fpolacco@icenet.fi> procedure to create the
+ Debian package for Samba. This closes #35781 (samba has no pristine
+ source).
+ * Changes to /etc/cron.weely/samba: rotate /var/log/{nmb,smb}.old only
+ if the size of either is different than 0. Also, added comments at the
+ beginning of this script to explain how rotation of log files works in
+ Samba. Thanks to ujr@physik.phy.tu-dresden.de (Ulf Jaenicke-Roessler)
+ for the suggestions. This closes #37490 (cron.weekly script rotates not
+ used [sn]mb.old files). As I side effect, this should also close
+ #31462 (still trouble with /etc/cron.weekly/samba).
+ * Check for old /etc/pam.d/samba file which is not provided by this version
+ of the Debian Samba package but was provided in older versions. If this
+ file exists we delete it. We check for this in the postinst. This closes
+ #37356 (samba put stuff in pam.d that pam complains about) and #34312
+ (libpam0g: questions during upgrade).
+ * Make sure the mode of /etc/samba/smbpasswd is set to 600. This is done
+ in the postinst script. This closes #35730 (Security problem with
+ /etc/samba/smbpasswd when upgrading from samba 1.9.18p8-2 to 2.0.3-1).
+ * I have just checked and it looks like #28748 (smbfsx doesn't "return ")
+ has been fixed. This might have been fixed since a long time ago.
+ * Long long standing bug #18488 (smbclient: internal tar is broken) is
+ closed in this release of Samba. The bug might have been closed for a
+ long long time, but I did not check for this before.
+ * Temporary fix to the annoying "Unknown socket option IPTOS_LOWDELAY"
+ message. This fixes #33698 (socket option IPTOS_LOWDELAY no longer works),
+ #34148 (warnings from smbd) and #35333 (samba warnings).
+
+ -- Eloy A. Paris <peloy@debian.org> Thu, 20 May 1999 00:35:57 -0400
+
+samba (2.0.3-1) unstable; urgency=low
+
+ * New upstream version.
+ * Removed the convert_smbpasswd.pl program I created and put in
+ /usr/doc/samba/ because there's a convert_smbpasswd script in the
+ upstream sources that does the same thing. I modified the postinst
+ script to use this script instead of the one I created.
+
+ -- Eloy A. Paris <peloy@debian.org> Sun, 28 Feb 1999 01:35:37 -0400
+
+samba (2.0.2-2) unstable; urgency=low
+
+ * Updated the README.Debian file.
+ * Updated the description of the samba package in the control file.
+ * The binaries smbmnt and smbumount-2.1.x in the smbfsx package are now
+ installed setuid root as they should be. This was done by doing a
+ a "chmod u+s" for each binary in debian/rules and by creating the
+ file debian/smbfsx.suid.
+ * Minor patch to source/client/smbumount.c to allow normal users
+ to umount what they have mounted (problem was a kernel vs. libc6
+ size mismatch). I sent the patch upstream.
+ * Created debian/smbwrapper.dirs so the directory /usr/lib/samba/ is
+ created.
+ * Modified debian/rules to move smbwrapper.so from debian/tmp/usr/bin/ to
+ debian/smbwrapper/usr/lib/samba/.
+ * Hacked source/smbwrapper/smbsh.c to fix the problem
+ of smbsh not finding the shared library smbwrapper.so.
+
+ -- Eloy A. Paris <peloy@debian.org> Thu, 11 Feb 1999 18:11:34 -0400
+
+samba (2.0.2-1) unstable; urgency=low
+
+ * New upstream version.
+
+ -- Eloy A. Paris <peloy@debian.org> Thu, 11 Feb 1999 01:35:51 -0400
+
+samba (2.0.1-1) unstable; urgency=low
+
+ * New upstream version.
+
+ -- Eloy A. Paris <peloy@debian.org> Sat, 6 Feb 1999 06:51:18 -0400
+
+samba (2.0.0final-4) unstable; urgency=low
+
+ * The samba postinst made an unwarranted assumption that the file
+ /etc/samba/smbpasswd exists. If the file did not exist (which is
+ perfectly valid) the postinst will fail. This fixes #32953.
+
+ -- Eloy A. Paris <peloy@debian.org> Fri, 5 Feb 1999 23:32:46 -0400
+
+samba (2.0.0final-3) unstable; urgency=low
+
+ * Added to debian/control a "Depends: ${shlibs:Depends}" line for the
+ samba-common package so dependencies for this package are set
+ correctly (thanks to Dark for pointing this out).
+
+ -- Eloy A. Paris <peloy@debian.org> Thu, 4 Feb 1999 09:45:21 -0400
+
+samba (2.0.0final-2) unstable; urgency=low
+
+ * Finally!!! The first upload to unstable. Sorry for the delay folks
+ but I have been quite busy lately :-) Another reason for the delay
+ is that I wanted to ease the migration from Samba 1.9.18p10 and
+ before to Samba 2.0.0. I changed the location of the config. files
+ from /etc/ to /etc/samba/ and this made things a little bit harder.
+ * This package needs 2.2 kernel headers to compile (well, this is
+ true for the smbfsx package, all others compile fine with 2.0 kernel
+ headers).
+ * Created a preinst script for the samba package to take care of the
+ location migration of smb.conf (from /etc/ to /etc/samba/). The
+ preinst script also takes care of moving /etc/smbpasswd to its new
+ location (/etc/samba/).
+ * Created postinst and postrm scripts to add/remove an entry for swat
+ in /etc/inetd.conf.
+ * I had forgotten to install the sambaconfig script so I changed
+ debian/rules to install this script.
+ * Added a postrm script for the samba package (I had forgotten to add
+ this script to the new Samba packages after the migration from 1.9.18
+ to 2.0.0).
+ * Created a small Perl script that is called from the samba postinst
+ to convert the smbpasswd from the old format used in version prior
+ to 2.0.0 to the new one used in 2.0.0 and beyond.
+ * The upgrade process should be automatically now. Please let me know
+ of any problems you encounter.
+
+ -- Eloy A. Paris <peloy@debian.org> Sat, 23 Jan 1999 09:34:10 -0400
+
+samba (2.0.0final-1) experimental; urgency=low
+
+ * Finally!!! Samba 2.0.0 is here! I am not uploading to unstable
+ because I still have to work out the migration from the old
+ samba packages to the new ones. I also need to work more on the
+ new swat package.
+
+ -- Eloy A. Paris <peloy@debian.org> Thu, 14 Jan 1999 22:40:02 -0400
+
+samba (2.0.0beta5-1) experimental; urgency=low
+
+ * New upstream version.
+
+ -- Eloy A. Paris <peloy@debian.org> Tue, 5 Jan 1999 00:37:57 -0400
+
+samba (2.0.0beta4-1) experimental; urgency=low
+
+ * New upstream version.
+
+ -- Eloy A. Paris <peloy@debian.org> Wed, 23 Dec 1998 18:37:45 -0400
+
+samba (2.0.0beta3-1) experimental; urgency=low
+
+ * New upstream version.
+ * I have just realized that the documentation patches (for man pages)
+ that I used for the 1.9.18 release are not longer necessary because
+ there was a major re-write of all the Samba documentation that added
+ the missing bits of information. So, I have just removed these minor
+ patches.
+
+ -- Eloy A. Paris <peloy@debian.org> Tue, 8 Dec 1998 12:00:30 -0400
+
+samba (2.0.0beta2-1) experimental; urgency=low
+
+ * New upstream version.
+ * This new version fixes the potential security problem that
+ was posted to debian-private (using the "message command" parameter
+ to execute arbitrary commands from messages sent from LinPopUp).
+ * Changed /etc/init.d/samba to use one of the variables stored in
+ /etc/samba/debian_config to know how Samba is being run (from inetd or
+ as daemons) instead of grepping /etc/inetd.conf which may not exist
+ if the user is running xinetd (this fixes bug #29687 - assumes using
+ vanilla inetd)
+
+ -- Eloy A. Paris <peloy@debian.org> Mon, 23 Nov 1998 23:32:03 -0400
+
+samba (2.0.0beta1-1) experimental; urgency=low
+
+ * First beta release of the samba-2.0.0 code. Before the beta I was
+ working with sources downloaded directly from the CVS server. This
+ package goes into experimental and I plan to release the new
+ samba to unstable as soon as it gets out of beta.
+ * Created several packages out of the Samba sources. They are:
+ samba (nmbd and smbd daemons + related programs), smbclient (FTP
+ like command line utility to retrieve files from SMB servers),
+ swat (Samba Web Administration Tool), samba-common (common files
+ used by samba, smbclient and swat), smbfsx (smbfs utilities for
+ kernels >= 2.1.70), smbwrapper and samba-doc (Samba documentation).
+ * Refreshed debian/samba-doc.docs so recently added docs. are
+ installed in the samba-doc package. New additions include man
+ pages in the /usr/doc/samba-doc/htmldocs/ directory.
+ * Deleted Debian specific nmblookup(1) man page as it is now upstream.
+ * Added smbtorture to smbclient package.
+ * Moved rpcclient from the samba package to the smbclient package.
+ * The Samba daemons (nmbd and smbd) now create a PID file so I changed
+ all calls to start-stop-daemon to use the PID file.
+ * Fixed debian/rules to install mksmbpasswd (fixes #27655).
+ * Modified /etc/init.d/samba so nmbd is started without the -a (append
+ to the log file instead of overwrite) switch. The new behavior of
+ nmbd is to NOT overwrite log files, so the -a switch can be deleted
+ safely.
+ * Moved from debstd to debhelper.
+
+ -- Eloy A. Paris <peloy@debian.org> Thu, 1 Oct 1998 08:37:41 -0400
+
+samba (1.9.18p10-5) frozen unstable; urgency=high
+
+ * Oppsss!!! While fixing bug #26884 I introduced a bug even worse than
+ the one I was trying to fix: in /etc/init.d/samba I got rid of the test
+ that tells us whether the Samba daemons are running from inetd or as
+ standalone daemons. I corrected the problem by editing again
+ /etc/init.d/samba to uncomment the test.
+ * Wishlist bug #28298 (typos in samba) was fixed.
+ * Wishlist bug #28309 (typos in smb.conf) was fixed.
+
+ -- Eloy A. Paris <peloy@debian.org> Wed, 28 Oct 1998 09:11:47 -0400
+
+samba (1.9.18p10-4) unstable; urgency=low
+
+ * Minor patch to debian/rules to delete *substvars instead of only
+ substvars when doing a "debian/rules clean" (thanks to Daniel Jacobowitz
+ <dmj@andrew.cmu.edu> for this).
+ * Small patch to source/shmem_sysv.c that eases compilation under
+ glibc-2.1 (thanks to Daniel <dmj@andrew.cmu.edu> for this).
+
+ -- Eloy A. Paris <peloy@debian.org> Thu, 17 Sep 1998 15:33:49 -0400
+
+samba (1.9.18p10-3) unstable; urgency=low
+
+ * Patched smbclient again to fix minor formatting problem introduced
+ by Magosanyi Arpad's smbclient patch.
+
+ -- Eloy A. Paris <peloy@debian.org> Thu, 3 Sep 1998 11:03:23 -0400
+
+samba (1.9.18p10-2) unstable; urgency=low
+
+ * Sync'ed include files for the smbfs utilities with the ones in
+ kernel 2.1.119.
+ * Added to the /usr/doc/samba/examples/ directory a new script called
+ wins2dns (courtesy of Jason Gunthorpe <jgg@deltatee.com>) that
+ generates BIND sonze files for hosts in the WINS database.
+ * Patched smbclient to include enhancements by Magosanyi Arpad
+ <mag@bunuel.tii.matav.hu> that make scripting easier.
+
+ -- Eloy A. Paris <peloy@debian.org> Fri, 28 Aug 1998 13:34:54 -0400
+
+samba (1.9.18p10-1) stable unstable; urgency=low
+
+ * New upstream version (see /usr/doc/samba/WHATSNEW.txt for a
+ description of what has changed). I built a 1.9.18p9-1 but I
+ never released it because an obscure bug was found just a couple
+ of days before the official release, so the Samba Team stopped
+ the rollover of 1.9.18p9.
+ * Updated documentation (new files were added to the docs/ directory
+ that were not installed in /usr/doc/samba/).
+ * Fixed long standing bug #7695 (smb.conf's man page doesn't document
+ 'printing=lprng') - I made a couple of changes to the man page to
+ include references to lprng.
+ * Fixes bug #24930 (samba needs to suggest psmisc?). I don't think it
+ is necessary to make samba suggest psmisc just because the postinst
+ script mentions to call killall. So, I removed all references to
+ "killall" in the scripts.
+ * Fixes bug #25999 (Samba does not by default work with unix password
+ sync): I added the "passwd program" and "passwd chat" parameters to
+ the sample smb.conf to reflect the Debian environment.
+
+ -- Eloy A. Paris <peloy@debian.org> Fri, 21 Aug 1998 08:59:18 -0400
+
+samba (1.9.18p9-1) unstable; urgency=low
+
+ * New upstream version (see /usr/doc/samba/WHATSNEW.txt for a
+ description of what has changed).
+ * Removed Jeremy Allison's patch applied to 1.9.18p8-2 because it is
+ now part of the new upstream version.
+ * Corrected small typo in addtosmbpass' man page (fixes #25629).
+
+ -- Eloy A. Paris <peloy@debian.org> Tue, 11 Aug 1998 08:53:08 -0400
+
+samba (1.9.18p8-2) frozen unstable; urgency=medium
+
+ * Applied patch received from Jeremy Allison (Samba Team) that fixes
+ "grave" bug #23903 (samba maps username before authenicating with
+ NT password server).
+ * Added a "sleep 2" between "start-stop-daemon --stop" and
+ "start-stop-daemon --start" in /etc/init.d/samba so when this script
+ is called with the "restart" parameter the Samba daemons are restarted
+ properly. This fixes bug #24211 (init.d script doesn't restart).
+ * Sent start-stop-daemon output in /etc/init.d/samba to /dev/null to
+ avoid annoying warning messages.
+ * Added perfomance tune parameters to sample /etc/smb.conf (SO_SNDBUF=4096
+ and SO_RCVBUF=4096 to "socket options" in /etc/smb.conf). I can't
+ find who sent this suggestion to me. If you are listening, drop me a
+ note and I'll put your name here :-)
+
+ -- Eloy A. Paris <peloy@debian.org> Mon, 29 Jun 1998 08:45:01 -0400
+
+samba (1.9.18p8-1) frozen unstable; urgency=low
+
+ * New upstream release that fixes _lots_ of "ugly" bugs. The list of
+ fixed bugs is too long to include here (see /usr/doc/samba/WHATSNEW.txt).
+ * Fixed postinst to quote arguments to if [ arg .. ] constructs
+ (fixes #22881).
+ * Applied Jeremy Allison's patch (posted to the samba-ntdom mailing
+ list) that solves a problem with username maps (the Samba Team did
+ not catch this problem before final 1.9.18p8).
+ * Made /etc/init.d/samba to print out a warning when Samba is running
+ from inetd and the user runs /etc/init.d/samba to start|stop|restart
+ Samba (there's no point on doing this because inetd will start the
+ daemons again when there is traffic on UDP port 137-139).
+
+ -- Eloy A. Paris <peloy@debian.org> Sat, 13 Jun 1998 00:18:25 -0400
+
+samba (1.9.18p7-4) frozen unstable; urgency=medium
+
+ * Fixes the serious problem of having the WINS name server
+ database getting deleted at boot time. That happened because the
+ WINS database was being stored under /var/lock/samba/ and all files
+ under /var/lock/ are deleted at boot time. The place where the WINS
+ database is stored was moved to /var/samba/.
+
+ -- Eloy A. Paris <peloy@debian.org> Mon, 18 May 1998 20:24:29 -0400
+
+samba (1.9.18p7-3) stable; urgency=high
+
+ * Libc5 version for Bo (stable) that fixes the recently reported
+ security hole.
+
+ -- Eloy A. Paris <peloy@debian.org> Mon, 18 May 1998 20:19:33 -0400
+
+samba (1.9.18p7-2) frozen unstable; urgency=low
+
+ * Added patches from the non-mantainer upload that make us able
+ to compile Samba on Alpha systems. This fixes bug #22379.
+
+ -- Eloy A. Paris <peloy@debian.org> Wed, 13 May 1998 20:38:51 -0400
+
+samba (1.9.18p7-1) frozen unstable; urgency=low
+
+ * New upstream release (just bug fixes, no new functionality).
+
+ -- Eloy A. Paris <peloy@debian.org> Wed, 13 May 1998 11:47:32 -0400
+
+samba (1.9.18p6-2) frozen unstable; urgency=low
+
+ * Uploaded to frozen (I forgot to upload last version to frozen
+ so it got installed only in unstable).
+
+ -- Eloy A. Paris <peloy@debian.org> Tue, 12 May 1998 18:10:17 -0400
+
+samba (1.9.18p6-1.1) unstable; urgency=low
+
+ * non-maintainer upload for Alpha
+ * patch needed for source/quota.c (_syscall4() confusion)
+
+ -- Paul Slootman <paul@debian.org> Tue, 12 May 1998 20:39:13 +0200
+
+samba (1.9.18p6-1) unstable; urgency=low
+
+ * New upstream release that fixes a possible buffer overflow.
+ This security hole was reported on BugTraq by Drago. The
+ previous Debian version (1.9.18p5-1) was not released because
+ 1.9.18p5 and 1.9.18p6 were released very closely.
+
+ -- Eloy A. Paris <peloy@debian.org> Mon, 11 May 1998 20:28:33 -0400
+
+samba (1.9.18p5-1) unstable; urgency=low
+
+ * New upstream release (no new funcionality, just bug fixes - see
+ /usr/doc/samba/WHATSNEW.txt.gz).
+ * Backed off Debian patches that were added upstream.
+
+ -- Eloy A. Paris <peloy@debian.org> Mon, 11 May 1998 08:43:53 -0400
+
+samba (1.9.18p4-2) frozen unstable; urgency=low
+
+ * Patched smbclient(1) man page to not reference the unsopported
+ -A parameter (fixes #6863).
+ * Changes to start nmbd with the -a option (in /etc/init.d/samba
+ and in the entry added to /etc/inetd.conf).
+ * Fixed typo in sample smb.conf (fixes #21484).
+ * Fixed yet another typo in sample smb.conf (fixes #21447).
+
+ -- Eloy A. Paris <peloy@debian.org> Fri, 17 Apr 1998 22:19:23 -0400
+
+samba (1.9.18p4-1) frozen unstable; urgency=low
+
+ * New upstream version that fixes several bugs.
+ * New scheme for keeping track of Debian specific configuration.
+ This new scheme fixes bug #18624 (Samba always asks the user about
+ configuration options). New scheme stores Debian specific
+ configuration information in /etc/samba/debian_config.
+ * Changes to /usr/sbin/sambaconfig, prerm and postinst to support the
+ new configuration scheme.
+ * Moved required kernel 2.1.x include files inside the source tree
+ so I don't have to do very nasty things like creating crazy
+ symlinks in /usr/include to make this package compile. This
+ allows non-root users to build the package and fixes bug
+ #20104.
+ * Fixed address of the FSF in /usr/doc/samba/copyright (problem
+ reported by lintian).
+ * The /etc/init.d/samba script now supports the force-reload
+ argument, as required by the policy (problem reported by lintian).
+ * Added a "rm /etc/cron.weekly/samba" at the end of the postinst.
+ * Now the samba package can be installed even if no nmbd or smbd processes
+ are running. This fixes the following bugs: #8917, #9334, #10268,
+ #10411, #11146 and #13387.
+ * Provides the original README in /usr/doc/samba. This fixes bug #9693.
+ * Added a --no-reload option to sambaconfig to not reload Samba
+ after configuration.
+ * Created man pages for sambaconfig(8), addtosmbpass(8),
+ mksmbpasswd(8) and nmblookup(1).
+ * Corrected small typo in sample /etc/smb.conf.
+ * Added two new parameters to /etc/smb.conf: "preserver case" and
+ "short preserve case".
+ * "rm -Rf /var/lock/samba" in postrm when package is being purged.
+ * Patched upstream source (nmbd.c) to not overwrite log files when
+ nmbd is called with the -a parameter (fixes #17704: nmbd ignores
+ -a option).
+ * /etc/init.d/samba now starts the nmbd daemon with the -a parameter
+ to not overwrite log files.
+
+ -- Eloy A. Paris <peloy@debian.org> Mon, 23 Mar 1998 21:22:03 -0400
+
+samba (1.9.18p3-1) unstable; urgency=low
+
+ * New upstream version.
+ * Oppsss!!! I really screwed it up (actually, debstd did).
+ 1.9.18p2-2 still contained man pages (smbmount and smbumount) part
+ of other packages. This version does have this corrected. If not,
+ I no longer deserve to be a Debian developer! So, this version
+ fixes bug #18438 and some of the bugs I claimed to fix in
+ 1.9.18p2-2. Oh, by the way, I fixed the problem by running debstd
+ with -m in debian/rules (man pages are installed by "make install"
+ so it's a bad idea to re-install man pages with debstd).
+
+ -- Eloy A. Paris <peloy@debian.org> Mon, 23 Feb 1998 17:32:42 -0400
+
+samba (1.9.18p2-2) unstable; urgency=low
+
+ * Fixes bugs #18017, #17999, #17961, #17932: old 1.9.18p2-1 provided
+ a man page for smbmount, which conflicts with package smbfs. This
+ was solved by creating a multi-binary package that produces
+ package samba and new package smbfsx.
+ * Fixes bug #18000 (typo in postinst).
+ * Fixes bug #17958 (postinst asks obsolete question). Actually,
+ the question is still asked, but only if Samba is run as daemons.
+ * Created a multi-binary package from the Samba sources: package
+ samba and new package smbfsx which provides SMB mount utilities
+ for kernels > 2.1.70.
+
+ -- Eloy A. Paris <peloy@debian.org> Mon, 9 Feb 1998 19:47:05 -0400
+
+samba (1.9.18p2-1) unstable; urgency=low
+
+ * New upstream version.
+ * Removed /etc/cron.weekly/samba because Samba does not handle well
+ rotation of log files (if the log file is rotated Samba will
+ continue to log to the rotated file, instead of the just created
+ one). In any case, Samba will rotate log files after an specific
+ file size.
+
+ -- Eloy A. Paris <peloy@debian.org> Tue, 27 Jan 1998 22:34:27 -0400
+
+samba (1.9.18p1-2) unstable; urgency=low
+
+ * Created a multi-binary package out of the Samba sources to provide
+ packages samba and smbfsx (userland utilities to work with
+ smbfs with kernels > 2.1.x.
+
+ -- Eloy A. Paris <peloy@debian.org> Sat, 17 Jan 1998 09:23:48 -0400
+
+samba (1.9.18p1-1) unstable; urgency=low
+
+ * New upstream version.
+ * Created /etc/cron.daily/samba to save a copy of /etc/smbpasswd in
+ /var/backups/smbpasswd.bak.
+
+ -- Eloy A. Paris <peloy@debian.org> Wed, 14 Jan 1998 13:40:56 -0400
+
+samba (1.9.18alpha14-1) unstable; urgency=low
+
+ * New upstream version.
+ * Added a note to the postinst script telling the user that he/she
+ needs to run smbpasswd manually after creating a new /etc/smbpasswd
+ from /etc/passwd.
+
+ -- Eloy A. Paris <peloy@debian.org> Tue, 23 Dec 1997 23:44:37 -0400
+
+samba (1.9.18alpha13-1) unstable; urgency=low
+
+ * New upstream version.
+
+ -- Eloy A. Paris <peloy@debian.org> Tue, 16 Dec 1997 13:02:32 -0400
+
+samba (1.9.18alpha12-1) unstable; urgency=low
+
+ * New upstream version.
+ * Conflicts with the sambades package because the new Samba 1.9.18
+ series do not depend on the DES libraries to support encrypted
+ passwords.
+ * Added parameter "encrypt passwords = yes" to /etc/smb.conf.
+ * Compiled with support for quotas in disk_free().
+ * Home directories are now exported read only by default.
+ * Re-worked debian/rules.
+ * Re-worked sample smb.conf.
+
+ -- Eloy A. Paris <peloy@debian.org> Thu, 4 Dec 1997 22:50:34 -0400
+
+samba (1.9.17p4-1) unstable; urgency=low
+
+ * New upstream version.
+ * Made /etc/smb.conf readable by everybody because some Samba utilities
+ will fail otherwise when run by non-root users.
+ * Dropped PAM support while the PAM libraries are ported to libc6.
+
+ -- Eloy A. Paris <peloy@debian.org> Tue, 21 Oct 1997 18:08:49 -0400
+
+samba (1.9.17p3-1) unstable; urgency=low
+
+ * New upstream version.
+ * Made /etc/smb.conf readable only by root as suggested by smbd's man page.
+
+ -- Eloy A. Paris <peloy@debian.org> Wed, 15 Oct 1997 09:21:25 -0400
+
+samba (1.9.17p2-2) unstable; urgency=low
+
+ * Running Samba as daemons instead of from inetd.
+ * Removing netbios entries in /etc/inetd.conf.
+
+ -- Eloy A. Paris <peloy@debian.org> Thu, 9 Oct 1997 23:37:25 -0400
+
+samba (1.9.17p2-1) unstable; urgency=low
+
+ * New upstream version that fixes a serious security hole.
+ * Removed Debian patches added in 1.9.17-1 and 1.9.17p1-1 because
+ these patches are now part of the upstream release.
+
+ -- Eloy A. Paris <peloy@debian.org> Sun, 28 Sep 1997 22:54:33 -0400
+
+samba (1.9.17p1-1) unstable; urgency=low
+
+ * New upstream version.
+ * Defined symbol _LINUX_C_LIB_VERSION_MAJOR as 6 in includes.h to shut up
+ compiler warnings.
+ * Included rpcsvc/ypclnt.h in includes.h to shut up compiler warnings.
+ * Included crypt.h to have function prototype for crypt().
+ * Included netinet/tcp.h to have some socket options included.
+ * Included netinet/ip.h to have some socket options included.
+ * Linking with libcrypt (LIBM='... -lcrypt'). Without including this
+ library smbd generates a seg. fault when authenticating users (?).
+
+ -- Eloy A. Paris <debian.org> Wed, 10 Sep 1997 22:09:18 -0400
+
+samba (1.9.17-1) unstable; urgency=low
+
+ * New upstream version (called the "Browse Fix Release")
+ * Added the option --oknodo to the start-stop-daemon invocation in prerm
+ script. This was because the prerm was failing because start-stop-daemon
+ was returning an error code if no nmbd or smbd daemons were found
+ to kill.
+ * The function yp_get_default_domain(), referenced in three source
+ files was part of libc5 but with libc6 (glibc2) it has been moved
+ to libnss_nis. Since the linker was unable to find the function
+ I had to add LIBSM='-lnss_nis' to debian/rules.
+ * Added -DNO_ASMSIGNALH and -DGLIBC2 to FLAGSM in debian/rules
+ because compiling was failing because of conflicts with glibc2.
+ * Patched source/includes.h to include termios.h if GLIBC2 is defined.
+
+ -- Eloy A. Paris <peloy@debian.org> Wed, 27 Aug 1997 08:39:32 -0400
+
+samba (1.9.17alpha5-1) unstable; urgency=low
+
+ * New upstream version.
+
+ -- Eloy A. Paris <peloy@debian.org> Thu, 14 Aug 1997 18:05:02 -0400
+
+samba (1.9.16p11-3) unstable; urgency=low
+
+ * Fixed accidental omission of /etc/pam.d/samba.
+
+ -- Klee Dienes <klee@debian.org> Sat, 15 Mar 1997 22:31:26 -0500
+
+samba (1.9.16p11-2) unstable; urgency=low
+
+ * Recompiled against newer PAM libraries.
+ * Added /etc/pam.d/samba.
+
+ -- Klee Dienes <klee@debian.org> Sat, 8 Mar 1997 01:16:28 -0500
+
+samba (1.9.16p11-1) unstable; urgency=low
+
+ * New upstream release.
+ * Added PAM support.
+
+ -- Klee Dienes <klee@debian.org> Tue, 25 Feb 1997 18:00:12 -0500
+
+samba (1.9.16p9-2) unstable; urgency=low
+
+ * minor packaging changes
+
+ -- Klee Dienes <klee@sauron.sedona.com> Sun, 3 Nov 1996 11:45:37 -0700
+
+samba (1.9.16p9-1) unstable; urgency=low
+
+ * upgraded to new upstream version
+
+ -- Klee Dienes <klee@sauron.sedona.com> Sat, 26 Oct 1996 21:38:20 -0700
+
+1.9.16alpha10-1:
+ 960714
+ * Removed Package_Revision from control file.
+ * Removed -m486 compiler option.
+ * Added Architecture, Section and Priority fields to control file.
+ * Upgraded to latest upstream version.
+ * Uses update-inetd now.
+ * Added shadow passwords support.
+ * Fixed Bug#1946: nmbd won't browse
+
+1.9.15p4-1:
+ 951128
+ * Upgraded to latest upstream version.
+ * Fixed many bugs.
+ * Adds Master Browsing support.
+ * Converted to ELF.
+ * Fixed bug #1825 - nmbd is now killed when removing samba.
+
+1.9.14-1:
+ 950926 Andrew Howell <andrew@it.com.au>
+ * Upgraded to latest version.
+ * Fixed Bug #1139 - samba won't print
+
+1.9.14alpha5-1:
+ * Fixes killing of inetd problem in debian.postint and debian.postrm
+
+1.9.14alpha5-0:
+ 950704 Andrew Howell <andrew@it.com.au>
+ * Taken over samba package from Bruce Perens.
+ * Upgraded to newest version of samba.
+
+1.9.02-1:
+ 9-January-1994 Bruce Perens <Bruce@Pixar.com>
+ * Added Debian GNU/Linux package maintenance system files, and
+ configured for Debian systems.
diff --git a/packaging/Debian/debian/config.cache b/packaging/Debian/debian/config.cache
new file mode 100644
index 00000000000..8872a27b761
--- /dev/null
+++ b/packaging/Debian/debian/config.cache
@@ -0,0 +1,221 @@
+#
+# 22 August 2001 Steve Langasek <vorlon@debian.org>
+#
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs. It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already. You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+#
+# This config.cache file contains a list of acceptable autoconf
+# values which can be used in compiling Samba for Debian woody/sid.
+#
+# Autoconf sorts options alphabetically in its output. This file
+# groups options logically.
+
+
+# Load any architecture-specific settings
+if [ -n "$DEB_HOST_GNU_TYPE" \
+ -a -f ../debian/config.cache.${DEB_HOST_GNU_TYPE} ]; then
+ . ../debian/config.cache.${DEB_HOST_GNU_TYPE}
+fi
+
+
+# This is at the top because it's most in need of regular tweaking.
+# These are options which are supported on 2.4 kernels, but not on 2.2
+# kernels.
+
+samba_cv_HAVE_KERNEL_OPLOCKS_LINUX=${samba_cv_HAVE_KERNEL_OPLOCKS_LINUX=no}
+samba_cv_HAVE_KERNEL_CHANGE_NOTIFY=${samba_cv_HAVE_KERNEL_CHANGE_NOTIFY=no}
+samba_cv_HAVE_KERNEL_SHARE_MODES=${samba_cv_HAVE_KERNEL_SHARE_MODES=no}
+
+
+# These are present in 2.2 kernels, but not in 2.0...
+
+samba_cv_have_setresuid=${samba_cv_have_setresuid=yes}
+samba_cv_have_setresgid=${samba_cv_have_setresgid=yes}
+samba_cv_USE_SETRESUID=${samba_cv_USE_SETRESUID=yes}
+
+
+# Various basic libc/compiler stuff that it's blindingly obvious that
+# Linux supports (now watch me get bitten for saying that)
+
+ac_cv_c_const=${ac_cv_c_const=yes}
+ac_cv_c_inline=${ac_cv_c_inline=inline}
+samba_cv_volatile=${samba_cv_volatile=yes}
+ac_cv_dirent_d_off=${ac_cv_dirent_d_off=yes}
+ac_cv_func_bzero=${ac_cv_func_bzero=yes}
+ac_cv_func_chmod=${ac_cv_func_chmod=yes}
+ac_cv_func_chown=${ac_cv_func_chown=yes}
+ac_cv_func_chroot=${ac_cv_func_chroot=yes}
+ac_cv_func_connect=${ac_cv_func_connect=yes}
+ac_cv_func_dup2=${ac_cv_func_dup2=yes}
+ac_cv_func_execl=${ac_cv_func_execl=yes}
+ac_cv_func_fchmod=${ac_cv_func_fchmod=yes}
+ac_cv_func_fchown=${ac_cv_func_fchown=yes}
+ac_cv_func_fstat=${ac_cv_func_fstat=yes}
+ac_cv_func_fsync=${ac_cv_func_fsync=yes}
+ac_cv_func_ftruncate=${ac_cv_func_ftruncate=yes}
+ac_cv_func_getcwd=${ac_cv_func_getcwd=yes}
+ac_cv_func_getgrent=${ac_cv_func_getgrent=yes}
+ac_cv_func_getgrnam=${ac_cv_func_getgrnam=yes}
+ac_cv_func_getspnam=${ac_cv_func_getspnam=yes}
+ac_cv_func_glob=${ac_cv_func_glob=yes}
+ac_cv_func_grantpt=${ac_cv_func_grantpt=yes}
+ac_cv_func_initgroups=${ac_cv_func_initgroups=yes}
+ac_cv_func_llseek=${ac_cv_func_llseek=yes}
+ac_cv_func_memcmp_clean=${ac_cv_func_memcmp_clean=yes}
+ac_cv_func_memmove=${ac_cv_func_memmove=yes}
+ac_cv_func_memset=${ac_cv_func_memset=yes}
+ac_cv_func_mktime=${ac_cv_func_mktime=yes}
+ac_cv_func_pipe=${ac_cv_func_pipe=yes}
+ac_cv_func_poll=${ac_cv_func_poll=yes}
+ac_cv_func_pread=${ac_cv_func_pread=yes}
+ac_cv_func_pwrite=${ac_cv_func_pwrite=yes}
+ac_cv_func_rand=${ac_cv_func_rand=yes}
+ac_cv_func_random=${ac_cv_func_random=yes}
+ac_cv_func_readlink=${ac_cv_func_readlink=yes}
+ac_cv_func_rename=${ac_cv_func_rename=yes}
+ac_cv_func_select=${ac_cv_func_select=yes}
+ac_cv_func_setenv=${ac_cv_func_setenv=yes}
+ac_cv_func_setgroups=${ac_cv_func_setgroups=yes}
+ac_cv_func_setsid=${ac_cv_func_setsid=yes}
+ac_cv_func_sigaction=${ac_cv_func_sigaction=yes}
+ac_cv_func_sigblock=${ac_cv_func_sigblock=yes}
+ac_cv_func_sigprocmask=${ac_cv_func_sigprocmask=yes}
+ac_cv_func_snprintf=${ac_cv_func_snprintf=yes}
+ac_cv_func_srand=${ac_cv_func_srand=yes}
+ac_cv_func_srandom=${ac_cv_func_srandom=yes}
+ac_cv_func_strcasecmp=${ac_cv_func_strcasecmp=yes}
+ac_cv_func_strchr=${ac_cv_func_strchr=yes}
+ac_cv_func_strdup=${ac_cv_func_strdup=yes}
+ac_cv_func_strerror=${ac_cv_func_strerror=yes}
+ac_cv_func_strftime=${ac_cv_func_strftime=yes}
+ac_cv_func_strpbrk=${ac_cv_func_strpbrk=yes}
+ac_cv_func_strtoul=${ac_cv_func_strtoul=yes}
+ac_cv_func_symlink=${ac_cv_func_symlink=yes}
+ac_cv_func_usleep=${ac_cv_func_usleep=yes}
+ac_cv_func_utime=${ac_cv_func_utime=yes}
+ac_cv_func_utimes=${ac_cv_func_utimes=yes}
+ac_cv_func_vsnprintf=${ac_cv_func_vsnprintf=yes}
+ac_cv_func_waitpid=${ac_cv_func_waitpid=yes}
+ac_cv_type_ino_t=${ac_cv_type_ino_t=yes}
+ac_cv_type_mode_t=${ac_cv_type_mode_t=yes}
+ac_cv_type_pid_t=${ac_cv_type_pid_t=yes}
+ac_cv_type_size_t=${ac_cv_type_size_t=yes}
+ac_cv_type_uid_t=${ac_cv_type_uid_t=yes}
+samba_cv_socklen_t=${samba_cv_socklen_t=yes}
+
+# Yes, we know Linux supports fcntl locking. Just ignore
+# any errors caused by building on an NFS mount.
+samba_cv_HAVE_FCNTL_LOCK=${samba_cv_HAVE_FCNTL_LOCK=yes}
+
+
+# smbwrapper doesn't work because the glibc maintainers don't want
+# to support transparent userland VFS. We might as well preempt
+# any checks for shadowed symbols that are only useful for smbwrapper.
+
+ac_cv_func___chdir=${ac_cv_func___chdir=no}
+ac_cv_func__chdir=${ac_cv_func__chdir=no}
+ac_cv_func___close=${ac_cv_func___close=no}
+ac_cv_func__close=${ac_cv_func__close=no}
+ac_cv_func___closedir=${ac_cv_func___closedir=no}
+ac_cv_func__closedir=${ac_cv_func__closedir=no}
+ac_cv_func___dup=${ac_cv_func___dup=no}
+ac_cv_func__dup=${ac_cv_func__dup=no}
+ac_cv_func___dup2=${ac_cv_func___dup2=no}
+ac_cv_func__dup2=${ac_cv_func__dup2=no}
+ac_cv_func___fchdir=${ac_cv_func___fchdir=no}
+ac_cv_func__fchdir=${ac_cv_func__fchdir=no}
+ac_cv_func___fcntl=${ac_cv_func___fcntl=no}
+ac_cv_func__fcntl=${ac_cv_func__fcntl=no}
+ac_cv_func___fork=${ac_cv_func___fork=no}
+ac_cv_func__fork=${ac_cv_func__fork=no}
+ac_cv_func___fstat=${ac_cv_func___fstat=no}
+ac_cv_func__fstat=${ac_cv_func__fstat=no}
+ac_cv_func___fstat64=${ac_cv_func___fstat64=no}
+ac_cv_func__fstat64=${ac_cv_func__fstat64=no}
+ac_cv_func___fxstat=${ac_cv_func___fxstat=no}
+ac_cv_func___getcwd=${ac_cv_func___getcwd=no}
+ac_cv_func__getcwd=${ac_cv_func__getcwd=no}
+ac_cv_func___getdents=${ac_cv_func___getdents=no}
+ac_cv_func__getdents=${ac_cv_func__getdents=no}
+ac_cv_func___llseek=${ac_cv_func___llseek=no}
+ac_cv_func___sys_llseek=${ac_cv_func___sys_llseek=no}
+ac_cv_func__llseek=${ac_cv_func__llseek=no}
+ac_cv_func___lseek=${ac_cv_func___lseek=no}
+ac_cv_func__lseek=${ac_cv_func__lseek=no}
+ac_cv_func___lstat=${ac_cv_func___lstat=no}
+ac_cv_func__lstat=${ac_cv_func__lstat=no}
+ac_cv_func___lstat64=${ac_cv_func___lstat64=no}
+ac_cv_func__lstat64=${ac_cv_func__lstat64=no}
+ac_cv_func___lxstat=${ac_cv_func___lxstat=no}
+ac_cv_func___open=${ac_cv_func___open=no}
+ac_cv_func__open=${ac_cv_func__open=no}
+ac_cv_func___open64=${ac_cv_func___open64=no}
+ac_cv_func__open64=${ac_cv_func__open64=no}
+ac_cv_func___opendir=${ac_cv_func___opendir=no}
+ac_cv_func__opendir=${ac_cv_func__opendir=no}
+ac_cv_func___pread=${ac_cv_func___pread=no}
+ac_cv_func__pread=${ac_cv_func__pread=no}
+ac_cv_func___pread64=${ac_cv_func___pread64=no}
+ac_cv_func__pread64=${ac_cv_func__pread64=no}
+ac_cv_func___pwrite=${ac_cv_func___pwrite=no}
+ac_cv_func__pwrite=${ac_cv_func__pwrite=no}
+ac_cv_func___pwrite64=${ac_cv_func___pwrite64=no}
+ac_cv_func__pwrite64=${ac_cv_func__pwrite64=no}
+ac_cv_func___read=${ac_cv_func___read=no}
+ac_cv_func__read=${ac_cv_func__read=no}
+ac_cv_func___readdir=${ac_cv_func___readdir=no}
+ac_cv_func__readdir=${ac_cv_func__readdir=no}
+ac_cv_func___readdir64=${ac_cv_func___readdir64=no}
+ac_cv_func__readdir64=${ac_cv_func__readdir64=no}
+ac_cv_func___seekdir=${ac_cv_func___seekdir=no}
+ac_cv_func__seekdir=${ac_cv_func__seekdir=no}
+ac_cv_func___stat=${ac_cv_func___stat=no}
+ac_cv_func__stat=${ac_cv_func__stat=no}
+ac_cv_func___stat64=${ac_cv_func___stat64=no}
+ac_cv_func__stat64=${ac_cv_func__stat64=no}
+ac_cv_func___telldir=${ac_cv_func___telldir=no}
+ac_cv_func__telldir=${ac_cv_func__telldir=no}
+ac_cv_func___write=${ac_cv_func___write=no}
+ac_cv_func__write=${ac_cv_func__write=no}
+ac_cv_func___xstat=${ac_cv_func___xstat=no}
+
+
+
+# Miscellaneous stuff that isn't, and shouldn't be, available
+# in Debian. Those interested in building debs for other systems may
+# need to remove some of these defines.
+
+ac_cv_func_bigcrypt=${ac_cv_func_bigcrypt=no}
+ac_cv_func_crypt16=${ac_cv_func_crypt16=no}
+ac_cv_func_getauthuid=${ac_cv_func_getauthuid=no}
+ac_cv_func_getprpwnam=${ac_cv_func_getprpwnam=no}
+ac_cv_func_getpwanam=${ac_cv_func_getpwanam=no}
+ac_cv_func_putprpwnam=${ac_cv_func_putprpwnam=no}
+ac_cv_func_rdchk=${ac_cv_func_rdchk=no}
+ac_cv_func_set_auth_parameters=${ac_cv_func_set_auth_parameters=no}
+ac_cv_func_setgidx=${ac_cv_func_setgidx=no}
+ac_cv_func_setluid=${ac_cv_func_setluid=no}
+ac_cv_func_setpriv=${ac_cv_func_setpriv=no}
+ac_cv_func_setuidx=${ac_cv_func_setuidx=no}
+ac_cv_lib_sec_bigcrypt=${ac_cv_lib_sec_bigcrypt=no}
+ac_cv_lib_sec_getprpwnam=${ac_cv_lib_sec_getprpwnam=no}
+ac_cv_lib_sec_getspnam=${ac_cv_lib_sec_getspnam=no}
+ac_cv_lib_sec_putprpwnam=${ac_cv_lib_sec_putprpwnam=no}
+ac_cv_lib_sec_set_auth_parameters=${ac_cv_lib_sec_set_auth_parameters=no}
+ac_cv_lib_security_bigcrypt=${ac_cv_lib_security_bigcrypt=no}
+ac_cv_lib_security_getprpwnam=${ac_cv_lib_security_getprpwnam=no}
+ac_cv_lib_security_getspnam=${ac_cv_lib_security_getspnam=no}
+ac_cv_lib_security_putprpwnam=${ac_cv_lib_security_putprpwnam=no}
+ac_cv_lib_security_set_auth_parameters=${ac_cv_lib_security_set_auth_parameters=no}
diff --git a/packaging/Debian/debian/config.cache.alpha-linux b/packaging/Debian/debian/config.cache.alpha-linux
new file mode 100644
index 00000000000..6d171920263
--- /dev/null
+++ b/packaging/Debian/debian/config.cache.alpha-linux
@@ -0,0 +1,12 @@
+# 22 Aug 2001 Steve Langasek <vorlon@debian.org>
+
+# This file contains autoconf settings specific to the alpha-linux
+# platform that should be preloaded when building for this architecture.
+
+
+# Linux 2.2 on Alpha doesn't have a functional setresgid() call, but
+# Linux 2.4 does. Ensure that packages compiled for woody remain
+# compatible with 2.2 kernels, even if the build machine is running 2.4.
+samba_cv_have_setresgid=${samba_cv_have_setresgid=no}
+samba_cv_USE_SETRESUID=${samba_cv_USE_SETRESUID=no}
+samba_cv_USE_SETREUID=${samba_cv_USE_SETREUID=yes}
diff --git a/packaging/Debian/debian/config.cache.sparc-linux b/packaging/Debian/debian/config.cache.sparc-linux
new file mode 100644
index 00000000000..a2a21b1d3ad
--- /dev/null
+++ b/packaging/Debian/debian/config.cache.sparc-linux
@@ -0,0 +1,13 @@
+# 24 Spe 2001 Steve Langasek <vorlon@debian.org>
+
+# This file contains autoconf settings specific to the sparc-linux
+# platform that should be preloaded when building for this architecture.
+
+
+# Linux 2.2 on Sparc doesn't have setresgid() or setresuid(), but
+# Linux 2.4 does. Ensure that packages compiled for woody remain
+# compatible with 2.2 kernels, even if the build machine is running 2.4.
+samba_cv_have_setresuid=${samba_cv_have_setresuid=no}
+samba_cv_have_setresgid=${samba_cv_have_setresgid=no}
+samba_cv_USE_SETRESUID=${samba_cv_USE_SETRESUID=no}
+samba_cv_USE_SETREUID=${samba_cv_USE_SETREUID=yes}
diff --git a/packaging/Debian/debian/control b/packaging/Debian/debian/control
new file mode 100644
index 00000000000..7bfb41b79d9
--- /dev/null
+++ b/packaging/Debian/debian/control
@@ -0,0 +1,188 @@
+Source: samba
+Section: net
+Priority: optional
+Maintainer: Eloy A. Paris <peloy@debian.org>
+Uploaders: Steve Langasek <vorlon@debian.org>
+Build-Depends: debhelper (>= 4.1.13), libpam0g-dev, libreadline4-dev, libcupsys2-dev, autoconf, libacl1-dev (>= 2.2.11-1), libacl1 (>= 2.2.11-1), libkrb5-dev, libldap2-dev, po-debconf, python2.3-dev
+Standards-Version: 3.5.10
+
+Package: samba
+Architecture: any
+Depends: samba-common (= ${Source-Version}), netbase, logrotate, ${shlibs:Depends}, ${misc:Depends}, libpam-runtime (>= 0.76-13.1), libpam-modules
+Replaces: samba-common (<= 2.0.5a-2)
+Suggests: samba-doc
+Description: a LanManager-like file and printer server for Unix
+ The Samba software suite is a collection of programs that
+ implements the SMB protocol for unix systems, allowing you to serve
+ files and printers to Windows, NT, OS/2 and DOS clients. This protocol
+ is sometimes also referred to as the LanManager or NetBIOS protocol.
+ .
+ This package contains all the components necessary to turn your
+ Debian GNU/Linux box into a powerful file and printer server.
+ .
+ Currently, the Samba Debian packages consist of the following:
+ .
+ samba - LanManager-like file and printer server for Unix.
+ samba-common - Samba common files used by both the server and the client.
+ smbclient - LanManager-like simple client for Unix.
+ swat - Samba Web Administration Tool
+ samba-doc - Samba documentation.
+ smbfs - Mount and umount commands for the smbfs (kernels 2.2.x and above).
+ libpam-smbpass - pluggable authentication module for SMB password database
+ libsmbclient - Shared library that allows applications to talk to SMB servers
+ libsmbclient-dev - libsmbclient shared libraries
+ winbind: Service to resolve user and group information from Windows NT servers
+ python2.3-samba: Python bindings that allow access to various aspects of Samba
+ .
+ It is possible to install a subset of these packages depending on
+ your particular needs. For example, to access other SMB servers you
+ should only need the smbclient and samba-common packages.
+
+Package: samba-common
+Architecture: any
+Depends: debconf, libpam-modules, ${shlibs:Depends}
+Replaces: samba (<< 2.999+3.0.alpha21-4)
+Description: Samba common files used by both the server and the client
+ The Samba software suite is a collection of programs that
+ implements the SMB protocol for unix systems, allowing you to serve
+ files and printers to Windows, NT, OS/2 and DOS clients. This protocol
+ is sometimes also referred to as the LanManager or NetBIOS protocol.
+ .
+ This package contains the common files that are used by both the server
+ (provided in the samba package) and the client (provided in the smbclient
+ package).
+
+Package: smbclient
+Architecture: any
+Depends: samba-common (= ${Source-Version}), ${shlibs:Depends}
+Replaces: samba (<< 2.999+3.0.alpha21-4)
+Provides: samba-client
+Suggests: smbfs
+Description: a LanManager-like simple client for Unix
+ The Samba software suite is a collection of programs that
+ implements the SMB protocol for unix systems, allowing you to serve
+ files and printers to Windows, NT, OS/2 and DOS clients. This protocol
+ is sometimes also referred to as the LanManager or NetBIOS protocol.
+ .
+ This package contains some client components of the Samba suite. In
+ particular it includes the command line utilities smbclient, smbtar,
+ and smbspool. If you want to mount shares exported from Microsoft
+ Windows machines or a Samba server you must install the smbfs package.
+
+Package: swat
+Architecture: any
+Depends: debconf, samba (= ${Source-Version}), ${shlibs:Depends}
+Recommends: samba-doc
+Description: Samba Web Administration Tool
+ The Samba software suite is a collection of programs that
+ implements the SMB protocol for unix systems, allowing you to serve
+ files and printers to Windows, NT, OS/2 and DOS clients. This protocol
+ is sometimes also referred to as the LanManager or NetBIOS protocol.
+ .
+ This package contains the components of the Samba suite that are needed
+ for Web administration of the Samba server.
+ .
+ Note: if you want to use the on-line documentation that is accesible
+ through the Swat front-end you must install the samba-doc package.
+
+Package: samba-doc
+Section: doc
+Architecture: all
+Description: Samba documentation
+ The Samba software suite is a collection of programs that
+ implements the SMB protocol for unix systems, allowing you to serve
+ files and printers to Windows, NT, OS/2 and DOS clients. This protocol
+ is sometimes also referred to as the LanManager or NetBIOS protocol.
+ .
+ This package contains all the documentation that comes in the original
+ tarball.
+
+Package: smbfs
+Section: otherosfs
+Priority: optional
+Architecture: any
+Depends: netbase (>= 2.02), samba-common (= ${Source-Version}), ${shlibs:Depends}
+Suggests: smbclient
+Replaces: smbfsx
+Conflicts: smbfsx, suidmanager (<< 0.50)
+Description: mount and umount commands for the smbfs (for kernels >= than 2.2.x)
+ Smbfs is a filesystem which understands the SMB protocol.
+ This is the protocol Windows for Workgroups, Windows NT or
+ LAN Manager use to talk to each other. It was inspired by
+ samba, the program by Andrew Tridgell that turns any unix
+ site into a file server for DOS or Windows clients.
+ .
+ If you want to use command-line utilities like smbclient, smbtar
+ and/or smbspool you just need to install the smbclient package.
+ .
+ Starting with the Debian Samba packages version 2.2.0-1, the old smbfs
+ utilities for 2.0.x have been removed. There are no wrapper scripts
+ that call a specific smbmount/smbumount depending on the kernel
+ version. If you are using a 2.0.x kernel please upgrade or use the
+ latest Samba 2.0.7 Debian package.
+
+Package: libpam-smbpass
+Section: admin
+Priority: extra
+Architecture: any
+Depends: ${shlibs:Depends}
+Suggests: samba
+Description: pluggable authentication module for SMB password database
+ This is a stackable PAM module that allows a system administrator to easily
+ migrate to using encrypted passwords for Samba and to keep smb passwords in
+ sync with unix passwords. Unlike other solutions, it does this without
+ requiring users to change their existing passwords or login to Samba using
+ cleartext passwords.
+
+Package: libsmbclient
+Section: libs
+Priority: extra
+Architecture: any
+Depends: ${shlibs:Depends}
+Description: shared library that allows applications to talk to SMB servers
+ libsmbclient allows to write applications that use the SMB protocol.
+ This gives applications the ability to talk to Microsoft Windows servers
+ and Unix servers running Samba.
+ .
+ This package contains the libsmbclient shared library.
+
+Package: libsmbclient-dev
+Section: libdevel
+Priority: extra
+Architecture: any
+Depends: libsmbclient (= ${Source-Version})
+Description: libsmbclient static libraries and headers
+ libsmbclient allows to write applications that use the SMB protocol.
+ This gives applications the ability to talk to Microsoft Windows servers
+ and Unix servers running Samba.
+ .
+ This package contains the libsmbclient static libraries and headers
+ needed to build applications that use SMB services.
+
+Package: winbind
+Section: net
+Priority: optional
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Replaces: samba (<= 2.2.3-2)
+Description: service to resolve user and group information from Windows NT servers
+ This package provides the winbindd daemon, which provides a
+ service for the Name Service Switch capability that is present
+ in most modern C libraries (like the GNU C Library - glibc.)
+ .
+ The service provided by winbindd is called `winbind' and
+ can be used to resolve user and group information from a
+ Windows NT server. The service can also provide authentication
+ services via an associated PAM module.
+
+Package: python2.3-samba
+Section: python
+Priority: optional
+Architecture: any
+Depends: ${shlibs:Depends}, python2.3
+Description: Python bindings that allow access to various aspects of Samba
+ The Samba Python bindings allow you to access various aspects of Samba.
+ At the moment their status is "experimental" but they have been reported
+ to work well.
+ .
+ See /usr/share/doc/python2.3-samba/examples for a couple of examples.
diff --git a/packaging/Debian/debian/copyright b/packaging/Debian/debian/copyright
new file mode 100644
index 00000000000..e74a64fa5b8
--- /dev/null
+++ b/packaging/Debian/debian/copyright
@@ -0,0 +1,28 @@
+This is the Debian Linux prepackaged version of the Samba SMB
+(LAN-Manager) server. Samba was written by Andrew Tridgell
+<Andrew.Tridgell@anu.edu.au> and many others.
+
+This package was put together by Eloy Paris <peloy@debian.org>
+and Steve Langasek <vorlon@debian.org> based on previous work by
+Klee Dienes <klee@debian.org>, Andrew Howell <andrew@it.com.au>
+and Bruce Perens <bruce@pixar.com> from sources found at
+<URL:ftp://ftp.samba.org/pub/samba>.
+
+Copyright:
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 dated June, 1991.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You 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
+
+On Debian GNU/Linux systems, the complete text of the GNU General
+Public License can be found in `/usr/share/common-licenses/GPL'.
diff --git a/packaging/Debian/debian/gdbcommands b/packaging/Debian/debian/gdbcommands
new file mode 100644
index 00000000000..5774b9ae311
--- /dev/null
+++ b/packaging/Debian/debian/gdbcommands
@@ -0,0 +1,2 @@
+bt
+quit
diff --git a/packaging/Debian/debian/libpam-smbpass.docs b/packaging/Debian/debian/libpam-smbpass.docs
new file mode 100644
index 00000000000..89fbbf90425
--- /dev/null
+++ b/packaging/Debian/debian/libpam-smbpass.docs
@@ -0,0 +1,2 @@
+source/pam_smbpass/README
+source/pam_smbpass/TODO
diff --git a/packaging/Debian/debian/libpam-smbpass.examples b/packaging/Debian/debian/libpam-smbpass.examples
new file mode 100644
index 00000000000..48d841b2c15
--- /dev/null
+++ b/packaging/Debian/debian/libpam-smbpass.examples
@@ -0,0 +1,5 @@
+source/pam_smbpass/samples/README
+source/pam_smbpass/samples/kdc-pdc
+source/pam_smbpass/samples/password-mature
+source/pam_smbpass/samples/password-migration
+source/pam_smbpass/samples/password-sync
diff --git a/packaging/Debian/debian/libpam-smbpass.files b/packaging/Debian/debian/libpam-smbpass.files
new file mode 100644
index 00000000000..4263df5c0f0
--- /dev/null
+++ b/packaging/Debian/debian/libpam-smbpass.files
@@ -0,0 +1 @@
+lib/security/pam_smbpass.so
diff --git a/packaging/Debian/debian/libsmbclient-dev.examples b/packaging/Debian/debian/libsmbclient-dev.examples
new file mode 100644
index 00000000000..2094c40b556
--- /dev/null
+++ b/packaging/Debian/debian/libsmbclient-dev.examples
@@ -0,0 +1 @@
+examples/libsmbclient/
diff --git a/packaging/Debian/debian/libsmbclient-dev.files b/packaging/Debian/debian/libsmbclient-dev.files
new file mode 100644
index 00000000000..a52c17cfc56
--- /dev/null
+++ b/packaging/Debian/debian/libsmbclient-dev.files
@@ -0,0 +1,3 @@
+usr/lib/libsmbclient.a
+usr/lib/libsmbclient.so
+usr/include/libsmbclient.h
diff --git a/packaging/Debian/debian/libsmbclient.files b/packaging/Debian/debian/libsmbclient.files
new file mode 100644
index 00000000000..18b7f3b1654
--- /dev/null
+++ b/packaging/Debian/debian/libsmbclient.files
@@ -0,0 +1,2 @@
+usr/lib/libsmbclient.so.0.1
+usr/lib/libsmbclient.so.0
diff --git a/packaging/Debian/debian/libsmbclient.shlibs b/packaging/Debian/debian/libsmbclient.shlibs
new file mode 100644
index 00000000000..9c6eea200a9
--- /dev/null
+++ b/packaging/Debian/debian/libsmbclient.shlibs
@@ -0,0 +1 @@
+libsmbclient 0 libsmbclient (>= 2.2.2-11)
diff --git a/packaging/Debian/debian/mksmbpasswd.8 b/packaging/Debian/debian/mksmbpasswd.8
new file mode 100644
index 00000000000..0a500102e8a
--- /dev/null
+++ b/packaging/Debian/debian/mksmbpasswd.8
@@ -0,0 +1,28 @@
+.TH MKSMBPASSWD 8 12-Apr-1998
+.SH NAME
+mksmbpasswd \- formats a /etc/passwd entry for a smbpasswd file
+.SH SYNOPSIS
+mksmbpasswd cat /etc/passwd | /usr/sbin/mksmbpasswd > /etc/samba/smbpasswd
+.SH DESCRIPTION
+.B mksmbpasswd
+should be used only once, the first time Samba is installed. The idea
+is to ease accounts creation by transferring all user accounts from
+/etc/passwd to /etc/samba/smbpasswd.
+.PP
+Please note that passwords are not transferred automatically from
+/etc/passwd to the new /etc/samba/smbpasswd file. After running
+.B mksmbpasswd
+all accounts are disabled so the system administrator must run
+smbpasswd for each account that needs to be enable.
+.SH FILES
+.TP
+/etc/passwd
+System wide accounts file
+.TP
+/etc/samba/smbpasswd
+Encrypted passwords file for the Samba daemons
+.SH SEE ALSO
+samba(7), nmbd(8), smbd(8)
+.SH AUTHOR
+Eloy A. Paris <peloy@debian.org> (man page based on sendmailconfig's man page
+by Robert Leslie <rob@mars.org>)
diff --git a/packaging/Debian/debian/mksmbpasswd.awk b/packaging/Debian/debian/mksmbpasswd.awk
new file mode 100644
index 00000000000..a7b41a725d3
--- /dev/null
+++ b/packaging/Debian/debian/mksmbpasswd.awk
@@ -0,0 +1,5 @@
+#!/usr/bin/awk -f
+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/packaging/Debian/debian/panic-action b/packaging/Debian/debian/panic-action
new file mode 100644
index 00000000000..13f773c1ef8
--- /dev/null
+++ b/packaging/Debian/debian/panic-action
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+# Redirect all output to our mail command
+(
+ # We must be given a pid to look at
+ if [ -z "$1" ]; then
+ echo "$0 called with no arguments."
+ exit 1
+ fi
+
+ if [ ! -d "/proc/$1" ]; then
+ echo "$0: No such process: $1"
+ exit 1
+ fi
+
+ # Find out what binary we're debugging
+ BINARYNAME=`readlink "/proc/$1/exe"`
+
+ # Generic header for our email
+ echo "The Samba 'panic action' script, $0,"
+ echo "was called for pid $1 ($BINARYNAME)."
+ echo
+
+ if [ -z "$BINARYNAME" ]; then
+ echo "This means there was a problem with the program, such as a segfault."
+ echo "However, the executable could not be found for process $1."
+ echo "It may have died unexpectedly, or you may not have permission to"
+ echo "debug the process."
+ exit 1
+ fi
+
+ # No debugger
+ if [ ! -x /usr/bin/gdb ]; then
+ echo "This means there was a problem with the program, such as a segfault."
+ echo "However, gdb was not found on your system, so the error could not be"
+ echo "debugged. Please install the gdb package so that debugging information is"
+ echo "available the next time such a problem occurs."
+ exit 1
+ fi
+
+ echo "Below is a backtrace for this process generated with gdb, which shows"
+ echo "the state of the program at the time the error occured. You are"
+ echo "encouraged to submit this information as a bug report to Debian. For"
+ echo "information about the procedure for submitting bug reports , please see"
+ echo "http://www.debian.org/Bugs/Reporting or the reportbug(1) manpage."
+ echo
+ gdb -x /etc/samba/gdbcommands -batch "$BINARYNAME" "$1"
+) | mail -s "Segfault in Samba" root
diff --git a/packaging/Debian/debian/patches/VERSION.patch b/packaging/Debian/debian/patches/VERSION.patch
new file mode 100644
index 00000000000..0f5c129c564
--- /dev/null
+++ b/packaging/Debian/debian/patches/VERSION.patch
@@ -0,0 +1,8 @@
+--- samba-3.0.0rc2/source/VERSION.orig 2003-09-02 21:56:11.000000000 -0400
++++ samba-3.0.0rc2/source/VERSION 2003-09-02 21:56:30.000000000 -0400
+@@ -120,4 +120,4 @@
+ # e.g. SAMBA_VERSION_VENDOR_SUFFIX=vendor_version() #
+ # -> "CVS 3.0.0rc2-VendorVersion" #
+ ########################################################
+-SAMBA_VERSION_VENDOR_SUFFIX=
++SAMBA_VERSION_VENDOR_SUFFIX="Debian"
diff --git a/packaging/Debian/debian/patches/documentation.patch b/packaging/Debian/debian/patches/documentation.patch
new file mode 100644
index 00000000000..c5e66232f9c
--- /dev/null
+++ b/packaging/Debian/debian/patches/documentation.patch
@@ -0,0 +1,68 @@
+--- samba_3_0/docs/manpages/swat.8.orig 2003-06-06 16:16:24.000000000 -0400
++++ samba_3_0/docs/manpages/swat.8 2003-06-06 16:25:13.000000000 -0400
+@@ -89,6 +89,13 @@
+ .SH "INSTALLATION"
+
+ .PP
++\fBDebian-specific Note\fR: all these steps have already been done for
++you. However, by default, swat is not enabled. This has been done for
++security reasons. To enable swat you need to edit /etc/inetd.conf,
++uncomment the swat entry (usually at the end of the file), and then
++restart inetd.
++
++.PP
+ Swat is included as binary package with most distributions\&. The package manager in this case takes care of the installation and configuration\&. This section is only for those who have compiled swat from scratch\&.
+
+ .PP
+@@ -96,15 +103,15 @@
+
+ .TP 3
+ \(bu
+-/usr/local/samba/bin/swat
++/usr/sbin/swat
+
+ .TP
+ \(bu
+-/usr/local/samba/swat/images/*
++/usr/share/samba/swat/images/*
+
+ .TP
+ \(bu
+-/usr/local/samba/swat/help/*
++/usr/share/samba/swat/help/*
+
+ .LP
+
+@@ -114,7 +121,7 @@
+ You need to edit your \fI/etc/inetd\&.conf \fR and \fI/etc/services\fR to enable SWAT to be launched via \fBinetd\fR\&.
+
+ .PP
+-In \fI/etc/services\fR you need to add a line like this:
++In \fI/etc/services\fR you need to add a line like this (not needed for Debian):
+
+ .PP
+ \fBswat 901/tcp\fR
+@@ -126,10 +133,10 @@
+ the choice of port number isn't really important except that it should be less than 1024 and not currently used (using a number above 1024 presents an obscure security hole depending on the implementation details of your\fBinetd\fR daemon)\&.
+
+ .PP
+-In \fI/etc/inetd\&.conf\fR you should add a line like this:
++In \fI/etc/inetd\&.conf\fR you should add a line like this (not needed for Debian since the maintainer scripts do it. You need to uncomment the line, though, because it is added commented out for security reasons):
+
+ .PP
+-\fBswat stream tcp nowait.400 root /usr/local/samba/bin/swat swat\fR
++\fBswat stream tcp nowait.400 root /usr/sbin/swat swat\fR
+
+ .PP
+ One you have edited \fI/etc/services\fR and \fI/etc/inetd\&.conf\fR you need to send a HUP signal to inetd\&. To do this use \fBkill -1 PID \fR where PID is the process ID of the inetd daemon\&.
+@@ -155,8 +162,8 @@
+
+
+ .TP
+-\fI/usr/local/samba/lib/smb\&.conf\fR
+-This is the default location of the \fBsmb.conf\fR(5) server configuration file that swat edits\&. Other common places that systems install this file are \fI /usr/samba/lib/smb\&.conf\fR and \fI/etc/smb\&.conf \fR\&. This file describes all the services the server is to make available to clients\&.
++\fI/etc/samba/smb\&.conf\fR
++This is the default location of the \fBsmb.conf\fR(5) server configuration file that swat edits\&. This file describes all the services the server is to make available to clients\&.
+
+
+ .SH "WARNINGS"
diff --git a/packaging/Debian/debian/patches/fhs.patch b/packaging/Debian/debian/patches/fhs.patch
new file mode 100644
index 00000000000..652641216b6
--- /dev/null
+++ b/packaging/Debian/debian/patches/fhs.patch
@@ -0,0 +1,570 @@
+diff -uNr samba-3.0.0beta2.orig/source/Makefile.in samba-3.0.0beta2/source/Makefile.in
+--- samba-3.0.0beta2.orig/source/Makefile.in 2003-07-02 23:26:46.000000000 -0500
++++ samba-3.0.0beta2/source/Makefile.in 2003-07-02 23:19:46.000000000 -0500
+@@ -67,6 +67,7 @@
+ CONFIGDIR = @configdir@
+ VARDIR = @localstatedir@
+ MANDIR = @mandir@
++DATADIR = @datadir@
+
+ # The permissions to give the executables
+ INSTALLPERMS = 0755
+@@ -90,6 +91,13 @@
+ # the directory where lock files go
+ LOCKDIR = @lockdir@
+
++# FHS directories; equal to LOCKDIR if not using --with-fhs
++CACHEDIR = @cachedir@
++STATEDIR = @statedir@
++
++# Where to look for (and install) codepage databases.
++CODEPAGEDIR = @codepagedir@
++
+ # the directory where pid files go
+ PIDDIR = @piddir@
+ # man pages language(s)
+@@ -114,7 +122,7 @@
+ 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_FLAGS6 = $(PATH_FLAGS5) -DCONFIGDIR=\"$(CONFIGDIR)\" -DCODEPAGEDIR=\"$(CODEPAGEDIR)\" -DCACHEDIR=\"$(CACHEDIR)\" -DSTATEDIR=\"$(STATEDIR)\"
+ PATH_FLAGS = $(PATH_FLAGS6) $(PASSWD_FLAGS)
+
+ # Note that all executable programs now provide for an optional executable suffix.
+@@ -1203,7 +1211,7 @@
+ @$(SHELL) $(srcdir)/script/installscripts.sh $(INSTALLPERMS) $(DESTDIR)$(BINDIR) $(SCRIPTS)
+
+ installdat: installdirs
+- @$(SHELL) $(srcdir)/script/installdat.sh $(DESTDIR)$(LIBDIR) $(srcdir)
++ @$(SHELL) $(srcdir)/script/installdat.sh $(DESTDIR)$(CODEPAGEDIR) $(srcdir)
+
+ installswat: installdirs
+ @$(SHELL) $(srcdir)/script/installswat.sh $(DESTDIR)$(SWATDIR) $(srcdir)
+diff -uNr samba-3.0.0beta2.orig/source/configure.in samba-3.0.0beta2/source/configure.in
+--- samba-3.0.0beta2.orig/source/configure.in 2003-07-02 23:26:47.000000000 -0500
++++ samba-3.0.0beta2/source/configure.in 2003-07-02 23:19:02.000000000 -0500
+@@ -17,18 +17,25 @@
+ AC_ARG_WITH(fhs,
+ [ --with-fhs Use FHS-compliant paths (default=no)],
+ configdir="${sysconfdir}/samba"
+- lockdir="\${VARDIR}/cache/samba"
++ lockdir="\${VARDIR}/run/samba"
+ piddir="\${VARDIR}/run/samba"
+ logfilebase="\${VARDIR}/log/samba"
+ privatedir="\${CONFIGDIR}/private"
+ libdir="\${prefix}/lib/samba"
+- swatdir="\${DATADIR}/samba/swat",
++ swatdir="\${DATADIR}/samba/swat"
++ codepagedir="\${DATADIR}/samba"
++ statedir="\${VARDIR}/lib/samba"
++ cachedir="\${VARDIR}/cache/samba"
++ AC_DEFINE(FHS_COMPATIBLE, 1, [Whether to use fully FHS-compatible paths]),
+ configdir="\${LIBDIR}"
+ logfilebase="\${VARDIR}"
+ lockdir="\${VARDIR}/locks"
+ piddir="\${VARDIR}/locks"
+ privatedir="\${prefix}/private"
+- swatdir="\${prefix}/swat")
++ codepagedir="\${LIBDIR}"
++ swatdir="\${prefix}/swat"
++ statedir="\${LOCKDIR}"
++ cachedir="\${LOCKDIR}")
+
+ #################################################
+ # set private directory location
+@@ -134,6 +141,9 @@
+ AC_SUBST(swatdir)
+ AC_SUBST(bindir)
+ AC_SUBST(sbindir)
++AC_SUBST(codepagedir)
++AC_SUBST(statedir)
++AC_SUBST(cachedir)
+
+ dnl Unique-to-Samba variables we'll be playing with.
+ AC_SUBST(SHELL)
+diff -uNr samba-3.0.0beta2.orig/source/dynconfig.c samba-3.0.0beta2/source/dynconfig.c
+--- samba-3.0.0beta2.orig/source/dynconfig.c 2003-07-02 23:26:47.000000000 -0500
++++ samba-3.0.0beta2/source/dynconfig.c 2003-07-02 23:19:02.000000000 -0500
+@@ -53,6 +53,13 @@
+ pstring dyn_LMHOSTSFILE = LMHOSTSFILE;
+
+ /**
++ * @brief Samba data directory.
++ *
++ * @sa data_path() to get the path to a file inside the CODEPAGEDIR.
++ **/
++pstring dyn_CODEPAGEDIR = CODEPAGEDIR;
++
++/**
+ * @brief Samba library directory.
+ *
+ * @sa lib_path() to get the path to a file inside the LIBDIR.
+@@ -70,3 +77,27 @@
+
+ const pstring dyn_SMB_PASSWD_FILE = SMB_PASSWD_FILE;
+ const pstring dyn_PRIVATE_DIR = PRIVATE_DIR;
++
++
++/* In non-FHS mode, these should be configurable using 'lock dir =';
++ but in FHS mode, they are their own directory. Implement as wrapper
++ functions so that everything can still be kept in dynconfig.c.
++ */
++
++char *dyn_STATEDIR(void)
++{
++#ifdef FHS_COMPATIBLE
++ return STATEDIR;
++#else
++ return lp_lockdir();
++#endif
++}
++
++char *dyn_CACHEDIR(void)
++{
++#ifdef FHS_COMPATIBLE
++ return CACHEDIR;
++#else
++ return lp_lockdir();
++#endif
++}
+diff -uNr samba-3.0.0beta2.orig/source/groupdb/mapping.c samba-3.0.0beta2/source/groupdb/mapping.c
+--- samba-3.0.0beta2.orig/source/groupdb/mapping.c 2003-07-02 23:26:47.000000000 -0500
++++ samba-3.0.0beta2/source/groupdb/mapping.c 2003-07-02 23:19:02.000000000 -0500
+@@ -134,7 +134,7 @@
+
+ if (tdb && local_pid == sys_getpid())
+ return True;
+- tdb = tdb_open_log(lock_path("group_mapping.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
++ tdb = tdb_open_log(state_path("group_mapping.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+ if (!tdb) {
+ DEBUG(0,("Failed to open group mapping database\n"));
+ return False;
+diff -uNr samba-3.0.0beta2.orig/source/include/dynconfig.h samba-3.0.0beta2/source/include/dynconfig.h
+--- samba-3.0.0beta2.orig/source/include/dynconfig.h 2003-07-02 23:26:47.000000000 -0500
++++ samba-3.0.0beta2/source/include/dynconfig.h 2003-07-02 23:19:02.000000000 -0500
+@@ -31,8 +31,12 @@
+ extern pstring dyn_CONFIGFILE;
+ extern pstring dyn_LOGFILEBASE, dyn_LMHOSTSFILE;
+ extern pstring dyn_LIBDIR;
++extern pstring dyn_CODEPAGEDIR;
+ 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;
++
++char *dyn_STATEDIR(void);
++char *dyn_CACHEDIR(void);
+diff -uNr samba-3.0.0beta2.orig/source/intl/lang_tdb.c samba-3.0.0beta2/source/intl/lang_tdb.c
+--- samba-3.0.0beta2.orig/source/intl/lang_tdb.c 2003-07-02 23:26:47.000000000 -0500
++++ samba-3.0.0beta2/source/intl/lang_tdb.c 2003-07-02 23:19:02.000000000 -0500
+@@ -123,7 +123,7 @@
+ /* if no lang then we don't translate */
+ if (!lang) return True;
+
+- asprintf(&msg_path, "%s.msg", lib_path((const char *)lang));
++ asprintf(&msg_path, "%s.msg", data_path((const char *)lang));
+ if (stat(msg_path, &st) != 0) {
+ /* the msg file isn't available */
+ free(msg_path);
+@@ -131,7 +131,7 @@
+ }
+
+
+- asprintf(&path, "%s%s.tdb", lock_path("lang_"), lang);
++ asprintf(&path, "%s%s.tdb", state_path("lang_"), lang);
+
+ tdb = tdb_open_log(path, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0644);
+ if (!tdb) {
+diff -uNr samba-3.0.0beta2.orig/source/lib/account_pol.c samba-3.0.0beta2/source/lib/account_pol.c
+--- samba-3.0.0beta2.orig/source/lib/account_pol.c 2003-07-02 23:26:47.000000000 -0500
++++ samba-3.0.0beta2/source/lib/account_pol.c 2003-07-02 23:19:02.000000000 -0500
+@@ -36,7 +36,7 @@
+
+ if (tdb && local_pid == sys_getpid())
+ return True;
+- tdb = tdb_open_log(lock_path("account_policy.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
++ tdb = tdb_open_log(state_path("account_policy.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+ if (!tdb) {
+ DEBUG(0,("Failed to open account policy database\n"));
+ return False;
+diff -uNr samba-3.0.0beta2.orig/source/lib/util.c samba-3.0.0beta2/source/lib/util.c
+--- samba-3.0.0beta2.orig/source/lib/util.c 2003-07-02 23:26:47.000000000 -0500
++++ samba-3.0.0beta2/source/lib/util.c 2003-07-02 23:19:02.000000000 -0500
+@@ -2190,6 +2190,61 @@
+ }
+
+ /**
++ * @brief Returns an absolute path to a file in the Samba data directory.
++ *
++ * @param name File to find, relative to CODEPAGEDIR.
++ *
++ * @retval Pointer to a static #pstring containing the full path.
++ **/
++
++char *data_path(const char *name)
++{
++ static pstring fname;
++ snprintf(fname, sizeof(fname), "%s/%s", dyn_CODEPAGEDIR, name);
++ return fname;
++}
++
++/*****************************************************************
++a useful function for returning a path in the Samba state directory
++ *****************************************************************/
++char *state_path(char *name)
++{
++ static pstring fname;
++
++ pstrcpy(fname,dyn_STATEDIR());
++ trim_string(fname,"","/");
++
++ if (!directory_exist(fname,NULL)) {
++ mkdir(fname,0755);
++ }
++
++ pstrcat(fname,"/");
++ pstrcat(fname,name);
++
++ return fname;
++}
++
++/*****************************************************************
++a useful function for returning a path in the Samba cache directory
++ *****************************************************************/
++char *cache_path(char *name)
++{
++ static pstring fname;
++
++ pstrcpy(fname,dyn_CACHEDIR());
++ trim_string(fname,"","/");
++
++ if (!directory_exist(fname,NULL)) {
++ mkdir(fname,0755);
++ }
++
++ pstrcat(fname,"/");
++ pstrcat(fname,name);
++
++ return fname;
++}
++
++/**
+ * @brief Returns the platform specific shared library extension.
+ *
+ * @retval Pointer to a static #fstring containing the extension.
+diff -uNr samba-3.0.0beta2.orig/source/lib/util_unistr.c samba-3.0.0beta2/source/lib/util_unistr.c
+--- samba-3.0.0beta2.orig/source/lib/util_unistr.c 2003-07-02 23:26:47.000000000 -0500
++++ samba-3.0.0beta2/source/lib/util_unistr.c 2003-07-02 23:19:02.000000000 -0500
+@@ -54,8 +54,8 @@
+ if (initialised) return;
+ initialised = 1;
+
+- upcase_table = map_file(lib_path("upcase.dat"), 0x20000);
+- lowcase_table = map_file(lib_path("lowcase.dat"), 0x20000);
++ upcase_table = map_file(data_path("upcase.dat"), 0x20000);
++ lowcase_table = map_file(data_path("lowcase.dat"), 0x20000);
+
+ /* we would like Samba to limp along even if these tables are
+ not available */
+@@ -161,7 +161,7 @@
+ return;
+ }
+
+- valid_file = map_file(lib_path("valid.dat"), 0x10000);
++ valid_file = map_file(data_path("valid.dat"), 0x10000);
+ if (valid_file) {
+ valid_table = valid_file;
+ mapped_file = 1;
+diff -uNr samba-3.0.0beta2.orig/source/libsmb/samlogon_cache.c samba-3.0.0beta2/source/libsmb/samlogon_cache.c
+--- samba-3.0.0beta2.orig/source/libsmb/samlogon_cache.c 2003-07-01 09:40:37.000000000 -0500
++++ samba-3.0.0beta2/source/libsmb/samlogon_cache.c 2003-07-02 23:19:02.000000000 -0500
+@@ -34,7 +34,7 @@
+ BOOL netsamlogon_cache_init(void)
+ {
+ if (!netsamlogon_tdb) {
+- netsamlogon_tdb = tdb_open_log(lock_path(NETSAMLOGON_TDB), 0,
++ netsamlogon_tdb = tdb_open_log(cache_path(NETSAMLOGON_TDB), 0,
+ TDB_DEFAULT, O_RDWR | O_CREAT, 0600);
+ }
+
+@@ -67,7 +67,7 @@
+ winbindd_cache.tdb open. Open the tdb if a NULL is passed. */
+
+ if (!tdb) {
+- tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000,
++ tdb = tdb_open_log(cache_path("winbindd_cache.tdb"), 5000,
+ TDB_DEFAULT, O_RDWR, 0600);
+ if (!tdb) {
+ DEBUG(5, ("netsamlogon_clear_cached_user: failed to open cache\n"));
+--- samba_3_0/source/nmbd/nmbd_serverlistdb.c.orig 2003-09-08 10:53:13.000000000 -0400
++++ samba_3_0/source/nmbd/nmbd_serverlistdb.c 2003-09-08 11:22:03.000000000 -0400
+@@ -327,7 +327,7 @@
+
+ updatecount++;
+
+- pstrcpy(fname,lp_lockdir());
++ pstrcpy(fname,dyn_CACHEDIR());
+ trim_char(fname,'\0' ,'/');
+ pstrcat(fname,"/");
+ pstrcat(fname,SERVER_LIST);
+--- samba-3.0.0rc2/source/nmbd/nmbd_winsserver.c.orig 2003-08-28 17:42:44.000000000 -0400
++++ samba-3.0.0rc2/source/nmbd/nmbd_winsserver.c 2003-08-31 08:09:11.000000000 -0400
+@@ -228,7 +228,7 @@
+
+ add_samba_names_to_subnet(wins_server_subnet);
+
+- if((fp = x_fopen(lock_path(WINS_LIST),O_RDONLY,0)) == NULL) {
++ if((fp = x_fopen(state_path(WINS_LIST),O_RDONLY,0)) == NULL) {
+ DEBUG(2,("initialise_wins: Can't open wins database file %s. Error was %s\n",
+ WINS_LIST, strerror(errno) ));
+ return True;
+@@ -1759,7 +1759,7 @@
+ }
+ }
+
+- slprintf(fname,sizeof(fname)-1,"%s/%s", lp_lockdir(), WINS_LIST);
++ slprintf(fname,sizeof(fname)-1,"%s/%s", dyn_STATEDIR(), WINS_LIST);
+ all_string_sub(fname,"//", "/", 0);
+ slprintf(fnamenew,sizeof(fnamenew)-1,"%s.%u", fname, (unsigned int)sys_getpid());
+
+diff -uNr samba-3.0.0beta2.orig/source/nsswitch/winbindd_cache.c samba-3.0.0beta2/source/nsswitch/winbindd_cache.c
+--- samba-3.0.0beta2.orig/source/nsswitch/winbindd_cache.c 2003-07-02 23:26:47.000000000 -0500
++++ samba-3.0.0beta2/source/nsswitch/winbindd_cache.c 2003-07-02 23:19:02.000000000 -0500
+@@ -56,7 +56,7 @@
+ if (opt_nocache)
+ return;
+
+- wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000,
++ wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"), 5000,
+ TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0600);
+
+ if (!wcache->tdb) {
+diff -uNr samba-3.0.0beta2.orig/source/nsswitch/winbindd_util.c samba-3.0.0beta2/source/nsswitch/winbindd_util.c
+--- samba-3.0.0beta2.orig/source/nsswitch/winbindd_util.c 2003-07-01 15:44:25.000000000 -0500
++++ samba-3.0.0beta2/source/nsswitch/winbindd_util.c 2003-07-02 23:19:02.000000000 -0500
+@@ -813,7 +813,7 @@
+ SMB_STRUCT_STAT stbuf;
+ TDB_CONTEXT *idmap_tdb;
+
+- pstrcpy(idmap_name, lock_path("winbindd_idmap.tdb"));
++ pstrcpy(idmap_name, state_path("winbindd_idmap.tdb"));
+
+ if (!file_exist(idmap_name, &stbuf)) {
+ /* nothing to convert return */
+diff -uNr samba-3.0.0beta2.orig/source/param/loadparm.c samba-3.0.0beta2/source/param/loadparm.c
+--- samba-3.0.0beta2.orig/source/param/loadparm.c 2003-07-02 23:26:47.000000000 -0500
++++ samba-3.0.0beta2/source/param/loadparm.c 2003-07-02 23:19:02.000000000 -0500
+@@ -104,6 +104,9 @@
+ char *szAddPrinterCommand;
+ char *szDeletePrinterCommand;
+ char *szOs2DriverMap;
++#ifdef FHS_COMPATIBLE
++ char *szLockDirStub;
++#endif
+ char *szLockDir;
+ char *szPidDir;
+ char *szRootdir;
+@@ -1083,8 +1083,13 @@
+ {"config file", P_STRING, P_GLOBAL, &Globals.szConfigFile, NULL, NULL, FLAG_HIDE},
+ {"preload", P_STRING, P_GLOBAL, &Globals.szAutoServices, NULL, NULL, FLAG_ADVANCED},
+ {"auto services", P_STRING, P_GLOBAL, &Globals.szAutoServices, NULL, NULL, FLAG_ADVANCED},
++#ifdef FHS_COMPATIBLE
++ {"lock directory", P_STRING, P_GLOBAL, &Globals.szLockDirStub, NULL, NULL, 0},
++ {"lock dir", P_STRING, P_GLOBAL, &Globals.szLockDirStub, NULL, NULL, 0},
++#else
+ {"lock directory", P_STRING, P_GLOBAL, &Globals.szLockDir, NULL, NULL, FLAG_ADVANCED},
+ {"lock dir", P_STRING, P_GLOBAL, &Globals.szLockDir, NULL, NULL, FLAG_HIDE},
++#endif
+ {"pid directory", P_STRING, P_GLOBAL, &Globals.szPidDir, NULL, NULL, FLAG_ADVANCED},
+ #ifdef WITH_UTMP
+ {"utmp directory", P_STRING, P_GLOBAL, &Globals.szUtmpDir, NULL, NULL, FLAG_ADVANCED},
+diff -uNr samba-3.0.0beta2.orig/source/passdb/pdb_tdb.c samba-3.0.0beta2/source/passdb/pdb_tdb.c
+--- samba-3.0.0beta2.orig/source/passdb/pdb_tdb.c 2003-07-02 23:26:47.000000000 -0500
++++ samba-3.0.0beta2/source/passdb/pdb_tdb.c 2003-07-02 23:19:02.000000000 -0500
+@@ -501,10 +501,7 @@
+ 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);
++ char *tdbfile = state_path(PASSDB_FILE_NAME);
+ tdb_state->tdbsam_location = talloc_strdup(pdb_context->mem_ctx, tdbfile);
+ }
+
+diff -uNr samba-3.0.0beta2.orig/source/passdb/privileges.c samba-3.0.0beta2/source/passdb/privileges.c
+--- samba-3.0.0beta2.orig/source/passdb/privileges.c 2003-06-07 12:57:35.000000000 -0500
++++ samba-3.0.0beta2/source/passdb/privileges.c 2003-07-02 23:19:02.000000000 -0500
+@@ -62,7 +62,7 @@
+ /* initialise the privilege database */
+ BOOL privilege_init(void)
+ {
+- tdb = tdb_open_log(lock_path("privilege.tdb"), 0, TDB_DEFAULT,
++ tdb = tdb_open_log(state_path("privilege.tdb"), 0, TDB_DEFAULT,
+ O_RDWR|O_CREAT, 0600);
+ if (!tdb) {
+ DEBUG(0,("Failed to open privilege database\n"));
+diff -uNr samba-3.0.0beta2.orig/source/passdb/secrets.c samba-3.0.0beta2/source/passdb/secrets.c
+--- samba-3.0.0beta2.orig/source/passdb/secrets.c 2003-07-02 23:26:47.000000000 -0500
++++ samba-3.0.0beta2/source/passdb/secrets.c 2003-07-02 23:19:02.000000000 -0500
+@@ -37,8 +37,7 @@
+ if (tdb)
+ return True;
+
+- pstrcpy(fname, lp_private_dir());
+- pstrcat(fname,"/secrets.tdb");
++ pstrcpy(fname, state_path("secrets.tdb"));
+
+ tdb = tdb_open_log(fname, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+
+diff -uNr samba-3.0.0beta2.orig/source/printing/nt_printing.c samba-3.0.0beta2/source/printing/nt_printing.c
+--- samba-3.0.0beta2.orig/source/printing/nt_printing.c 2003-07-02 23:26:47.000000000 -0500
++++ samba-3.0.0beta2/source/printing/nt_printing.c 2003-07-02 23:19:02.000000000 -0500
+@@ -293,28 +293,28 @@
+
+ if (tdb_drivers)
+ tdb_close(tdb_drivers);
+- tdb_drivers = tdb_open_log(lock_path("ntdrivers.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
++ tdb_drivers = tdb_open_log(state_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) ));
++ state_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);
++ tdb_printers = tdb_open_log(state_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) ));
++ state_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);
++ tdb_forms = tdb_open_log(state_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) ));
++ state_path("ntforms.tdb"), strerror(errno) ));
+ return False;
+ }
+
+diff -uNr samba-3.0.0beta2.orig/source/printing/printing.c samba-3.0.0beta2/source/printing/printing.c
+--- samba-3.0.0beta2.orig/source/printing/printing.c 2003-07-02 23:26:47.000000000 -0500
++++ samba-3.0.0beta2/source/printing/printing.c 2003-07-02 23:19:02.000000000 -0500
+@@ -174,8 +174,8 @@
+ if (local_pid == sys_getpid())
+ return True;
+
+- unlink(lock_path("printing.tdb"));
+- pstrcpy(printing_path,lock_path("printing"));
++ unlink(cache_path("printing.tdb"));
++ pstrcpy(printing_path,cache_path("printing"));
+ mkdir(printing_path,0755);
+
+ local_pid = sys_getpid();
+diff -uNr samba-3.0.0beta2.orig/source/printing/printing_db.c samba-3.0.0beta2/source/printing/printing_db.c
+--- samba-3.0.0beta2.orig/source/printing/printing_db.c 2003-07-02 23:26:47.000000000 -0500
++++ samba-3.0.0beta2/source/printing/printing_db.c 2003-07-02 23:19:02.000000000 -0500
+@@ -86,7 +86,7 @@
+ DLIST_ADD(print_db_head, p);
+ }
+
+- pstrcpy(printdb_path, lock_path("printing/"));
++ pstrcpy(printdb_path, cache_path("printing/"));
+ pstrcat(printdb_path, printername);
+ pstrcat(printdb_path, ".tdb");
+
+diff -uNr samba-3.0.0beta2.orig/source/registry/reg_db.c samba-3.0.0beta2/source/registry/reg_db.c
+--- samba-3.0.0beta2.orig/source/registry/reg_db.c 2003-07-02 23:26:47.000000000 -0500
++++ samba-3.0.0beta2/source/registry/reg_db.c 2003-07-02 23:19:02.000000000 -0500
+@@ -131,13 +131,13 @@
+ * 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);
++ tdb_reg = tdb_open_log(state_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);
++ tdb_reg = tdb_open_log(state_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) ));
++ state_path("registry.tdb"), strerror(errno) ));
+ return False;
+ }
+
+diff -uNr samba-3.0.0beta2.orig/source/rpc_server/srv_srvsvc_nt.c samba-3.0.0beta2/source/rpc_server/srv_srvsvc_nt.c
+--- samba-3.0.0beta2.orig/source/rpc_server/srv_srvsvc_nt.c 2003-07-02 23:26:47.000000000 -0500
++++ samba-3.0.0beta2/source/rpc_server/srv_srvsvc_nt.c 2003-07-02 23:19:02.000000000 -0500
+@@ -133,10 +133,10 @@
+
+ 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);
++ share_tdb = tdb_open_log(state_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) ));
++ state_path("share_info.tdb"), strerror(errno) ));
+ return False;
+ }
+
+diff -uNr samba-3.0.0beta2.orig/source/sam/idmap_tdb.c samba-3.0.0beta2/source/sam/idmap_tdb.c
+--- samba-3.0.0beta2.orig/source/sam/idmap_tdb.c 2003-07-01 15:44:26.000000000 -0500
++++ samba-3.0.0beta2/source/sam/idmap_tdb.c 2003-07-02 23:19:02.000000000 -0500
+@@ -487,7 +487,7 @@
+ BOOL tdb_is_new = False;
+
+ /* use the old database if present */
+- tdbfile = strdup(lock_path("winbindd_idmap.tdb"));
++ tdbfile = strdup(state_path("winbindd_idmap.tdb"));
+ if (!tdbfile) {
+ DEBUG(0, ("idmap_init: out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+diff -uNr samba-3.0.0beta2.orig/source/smbd/lanman.c samba-3.0.0beta2/source/smbd/lanman.c
+--- samba-3.0.0beta2.orig/source/smbd/lanman.c 2003-07-02 23:26:47.000000000 -0500
++++ samba-3.0.0beta2/source/smbd/lanman.c 2003-07-02 23:19:02.000000000 -0500
+@@ -966,9 +966,9 @@
+ BOOL local_list_only;
+ int i;
+
+- lines = file_lines_load(lock_path(SERVER_LIST), NULL);
++ lines = file_lines_load(cache_path(SERVER_LIST), NULL);
+ if (!lines) {
+- DEBUG(4,("Can't open %s - %s\n",lock_path(SERVER_LIST),strerror(errno)));
++ DEBUG(4,("Can't open %s - %s\n",cache_path(SERVER_LIST),strerror(errno)));
+ return(0);
+ }
+
+diff -uNr samba-3.0.0beta2.orig/source/wrepld/process.c samba-3.0.0beta2/source/wrepld/process.c
+--- samba-3.0.0beta2.orig/source/wrepld/process.c 2003-07-02 23:26:47.000000000 -0500
++++ samba-3.0.0beta2/source/wrepld/process.c 2003-07-02 23:19:02.000000000 -0500
+@@ -197,7 +197,7 @@
+ {
+ TDB_CONTEXT *tdb;
+
+- tdb = tdb_open_log(lock_path(WINS_LIST), 0, TDB_DEFAULT, O_RDONLY, 0600);
++ tdb = tdb_open_log(state_path(WINS_LIST), 0, TDB_DEFAULT, O_RDONLY, 0600);
+ if (!tdb) {
+ DEBUG(2,("get_our_last_id: Can't open wins database file %s. Error was %s\n", WINS_LIST, strerror(errno) ));
+ return;
+@@ -489,7 +489,7 @@
+ }
+
+
+- tdb = tdb_open_log(lock_path(WINS_LIST), 0, TDB_DEFAULT, O_RDONLY, 0600);
++ tdb = tdb_open_log(state_path(WINS_LIST), 0, TDB_DEFAULT, O_RDONLY, 0600);
+ if (!tdb) {
+ DEBUG(2,("send_entry_request: Can't open wins database file %s. Error was %s\n", WINS_LIST, strerror(errno) ));
+ return;
diff --git a/packaging/Debian/debian/patches/installswat.sh.patch b/packaging/Debian/debian/patches/installswat.sh.patch
new file mode 100644
index 00000000000..b425e3bef76
--- /dev/null
+++ b/packaging/Debian/debian/patches/installswat.sh.patch
@@ -0,0 +1,76 @@
+--- samba_3_0/source/script/installswat.sh.orig 2003-08-28 18:03:10.000000000 -0400
++++ samba_3_0/source/script/installswat.sh 2003-08-28 18:11:14.000000000 -0400
+@@ -9,7 +9,7 @@
+ echo Installing the Samba Web Administration Tool
+
+ LANGS=". `cd $SRCDIR../swat/; /bin/echo lang/??`"
+-echo Installing langs are `cd $SRCDIR../swat/lang/; /bin/echo ??`
++echo Installing the following additional langs: `cd $SRCDIR../swat/lang/; /bin/echo ??`
+
+ for ln in $LANGS; do
+ SWATLANGDIR=$SWATDIR/$ln
+@@ -23,21 +23,17 @@
+ fi
+ fi
+ done
+-done
+-
+-# Install images
+-for ln in $LANGS; do
+
+-for f in $SRCDIR../swat/$ln/images/*.gif; do
++ # Install images
++ 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
++ done
+
+-for f in $SRCDIR../swat/$ln/help/*.html; do
++ # 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
+@@ -49,26 +45,24 @@
+ 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
++ done
+
+-for f in $SRCDIR../swat/$ln/include/*.html; do
++ # 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 html documentation
++for f in $SRCDIR../docs/htmldocs/*.html; do
++ FNAME=$SWATDIR/help/`basename $f`
++ echo $FNAME
++ ln -s ../../../doc/samba-doc/htmldocs/`basename $f` $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
++ chmod 0644 $FNAME
+ done
+
+ # Install Using Samba book (but only if it is there)
diff --git a/packaging/Debian/debian/patches/nmbd-signalling.patch b/packaging/Debian/debian/patches/nmbd-signalling.patch
new file mode 100644
index 00000000000..ca04cce3bfa
--- /dev/null
+++ b/packaging/Debian/debian/patches/nmbd-signalling.patch
@@ -0,0 +1,20 @@
+diff -uNr samba-2.999+3.0.alpha21.orig/source/nmbd/nmbd_subnetdb.c samba-2.999+3.0.alpha21/source/nmbd/nmbd_subnetdb.c
+--- samba-2.999+3.0.alpha21.orig/source/nmbd/nmbd_subnetdb.c 2002-11-26 20:54:19.000000000 -0600
++++ samba-2.999+3.0.alpha21/source/nmbd/nmbd_subnetdb.c 2002-12-16 23:34:13.000000000 -0600
+@@ -214,12 +214,16 @@
+ extern struct in_addr loopback_ip;
+
+ if(num_interfaces == 0) {
++ void (*old_handler)(int);
++
+ DEBUG(0,("create_subnets: No local interfaces !\n"));
+ DEBUG(0,("create_subnets: Waiting for an interface to appear ...\n"));
++ old_handler = CatchSignal( SIGTERM, SIGNAL_CAST SIG_DFL );
+ while (iface_count() == 0) {
+ sleep(5);
+ load_interfaces();
+ }
++ CatchSignal( SIGTERM, SIGNAL_CAST old_handler );
+ }
+
+ num_interfaces = iface_count();
diff --git a/packaging/Debian/debian/patches/samba.patch b/packaging/Debian/debian/patches/samba.patch
new file mode 100644
index 00000000000..8707ec517ba
--- /dev/null
+++ b/packaging/Debian/debian/patches/samba.patch
@@ -0,0 +1,93 @@
+diff -uNr samba-3.0.0beta1.orig/source/client/smbmount.c samba-3.0.0beta1/source/client/smbmount.c
+--- samba-3.0.0beta1.orig/source/client/smbmount.c 2003-06-07 12:57:32.000000000 -0500
++++ samba-3.0.0beta1/source/client/smbmount.c 2003-06-30 20:12:22.000000000 -0500
+@@ -765,7 +765,7 @@
+ *lp = 0;
+ pstrcpy(password,lp+1);
+ got_pass = True;
+- memset(strchr_m(opteq+1,'%')+1,'X',strlen(password));
++ memset(strchr_m(opteq+1,'%')+1,'\0',strlen(password));
+ }
+ if ((lp=strchr_m(username,'/'))) {
+ *lp = 0;
+@@ -775,7 +775,7 @@
+ !strcmp(opts, "password")) {
+ pstrcpy(password,opteq+1);
+ got_pass = True;
+- memset(opteq+1,'X',strlen(password));
++ memset(opteq+1,'\0',strlen(password));
+ } else if(!strcmp(opts, "credentials")) {
+ pstrcpy(credentials,opteq+1);
+ } else if(!strcmp(opts, "netbiosname")) {
+@@ -889,7 +901,7 @@
+ *p = 0;
+ pstrcpy(password,p+1);
+ got_pass = True;
+- memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password));
++ memset(strchr_m(getenv("USER"),'%')+1,'\0',strlen(password));
+ }
+ strupper_m(username);
+ }
+diff -uNr samba-3.0.0beta1.orig/source/script/installbin.sh samba-3.0.0beta1/source/script/installbin.sh
+--- samba-3.0.0beta1.orig/source/script/installbin.sh 2002-04-22 13:16:20.000000000 -0500
++++ samba-3.0.0beta1/source/script/installbin.sh 2003-06-30 20:12:22.000000000 -0500
+@@ -22,9 +22,11 @@
+ 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
++# Commented out for the Debian Samba package. We take care of this
++# important symlink in debian/rules. (peloy@debian.org)
++# if [ $p2 = smbmount ]; then
++# ln -sf $BINDIR/$p2 /sbin/mount.smbfs
++# fi
+ done
+
+
+diff -uNr samba-3.0.0beta1.orig/source/smbd/service.c samba-3.0.0beta1/source/smbd/service.c
+--- samba-3.0.0beta1.orig/source/smbd/service.c 2003-06-07 12:57:39.000000000 -0500
++++ samba-3.0.0beta1/source/smbd/service.c 2003-06-30 20:12:57.000000000 -0500
+@@ -887,6 +887,9 @@
+ file_close_conn(conn);
+ dptr_closecnum(conn);
+
++ /* make sure we leave the directory available for unmount */
++ vfs_ChDir(conn, "/");
++
+ /* execute any "postexec = " line */
+ if (*lp_postexec(SNUM(conn)) &&
+ change_to_user(conn, vuid)) {
+@@ -906,8 +909,5 @@
+ smbrun(cmd,NULL);
+ }
+
+- /* make sure we leave the directory available for unmount */
+- vfs_ChDir(conn, "/");
+-
+ conn_free(conn);
+ }
+diff -uNr samba-3.0.0beta1.orig/source/smbwrapper/smbsh.c samba-3.0.0beta1/source/smbwrapper/smbsh.c
+--- samba-3.0.0beta1.orig/source/smbwrapper/smbsh.c 2003-06-07 12:57:40.000000000 -0500
++++ samba-3.0.0beta1/source/smbwrapper/smbsh.c 2003-06-30 20:12:22.000000000 -0500
+@@ -36,7 +36,7 @@
+ int main(int argc, char *argv[])
+ {
+ char *p, *u;
+- const char *libd = dyn_BINDIR;
++ const char *libd = dyn_LIBDIR;
+ pstring line, wd;
+ int opt;
+ extern char *optarg;
+diff -uNr samba-3.0.0beta1.orig/source/web/diagnose.c samba-3.0.0beta1/source/web/diagnose.c
+--- samba-3.0.0beta1.orig/source/web/diagnose.c 2003-06-07 12:57:41.000000000 -0500
++++ samba-3.0.0beta1/source/web/diagnose.c 2003-06-30 20:12:22.000000000 -0500
+@@ -70,6 +70,7 @@
+ static struct cli_state cli;
+ extern struct in_addr loopback_ip;
+
++ loopback_ip.s_addr = htonl((127 << 24) + 1);
+ if (!cli_initialise(&cli))
+ return False;
+
diff --git a/packaging/Debian/debian/patches/smbclient-pager.patch b/packaging/Debian/debian/patches/smbclient-pager.patch
new file mode 100644
index 00000000000..3ee85d4118c
--- /dev/null
+++ b/packaging/Debian/debian/patches/smbclient-pager.patch
@@ -0,0 +1,12 @@
+diff -uNr samba-2.999+3.0cvs20020906.orig/source/include/local.h samba-2.999+3.0cvs20020906/source/include/local.h
+--- samba-2.999+3.0cvs20020906.orig/source/include/local.h 2002-09-04 14:13:06.000000000 -0500
++++ samba-2.999+3.0cvs20020906/source/include/local.h 2002-09-08 14:19:24.000000000 -0500
+@@ -109,7 +109,7 @@
+ /* the default pager to use for the client "more" command. Users can
+ override this with the PAGER environment variable */
+ #ifndef PAGER
+-#define PAGER "more"
++#define PAGER "/usr/bin/pager"
+ #endif
+
+ /* the size of the uid cache used to reduce valid user checks */
diff --git a/packaging/Debian/debian/patches/smbclient-tar.patch.unused b/packaging/Debian/debian/patches/smbclient-tar.patch.unused
new file mode 100644
index 00000000000..e2a4c3ce2c0
--- /dev/null
+++ b/packaging/Debian/debian/patches/smbclient-tar.patch.unused
@@ -0,0 +1,43 @@
+diff -uNr samba-3.0.0beta2.orig/source/client/client.c samba-3.0.0beta2/source/client/client.c
+--- samba-3.0.0beta2.orig/source/client/client.c 2003-07-01 22:36:24.000000000 -0500
++++ samba-3.0.0beta2/source/client/client.c 2003-07-06 15:17:36.000000000 -0500
+@@ -2773,6 +2773,7 @@
+ int opt;
+ pstring query_host;
+ BOOL message = False;
++ char* tar_args = NULL;
+ extern char tar_type;
+ pstring term_code;
+ static const char *new_name_resolve_order = NULL;
+@@ -2816,7 +2817,7 @@
+ max_protocol = interpret_protocol(poptGetOptArg(pc), max_protocol);
+ break;
+ case 'T':
+- if (!tar_parseargs(argc, argv, poptGetOptArg(pc), optind)) {
++ if (!(tar_args = poptGetOptArg(pc))) {
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+@@ -2917,6 +2917,22 @@
+ pstrcpy(cmdline_auth_info.password,poptGetArg(pc));
+ }
+
++ /* The tar command may take a number of string options; pass
++ everything we have left to tar_parseargs(). */
++ if (tar_args) {
++ const char **argv2 = poptGetArgs(pc);
++ int argc2 = 0;
++
++ if (argv2) {
++ while (argv2[argc2]) argc2++;
++ }
++
++ if (!tar_parseargs(argc2, argv2, tar_args, 0)) {
++ poptPrintUsage(pc, stderr, 0);
++ exit(1);
++ }
++ }
++
+ init_names();
+
+ if(new_name_resolve_order)
diff --git a/packaging/Debian/debian/patches/smbmount-mtab-flags.patch b/packaging/Debian/debian/patches/smbmount-mtab-flags.patch
new file mode 100644
index 00000000000..dac999dd63c
--- /dev/null
+++ b/packaging/Debian/debian/patches/smbmount-mtab-flags.patch
@@ -0,0 +1,15 @@
+diff -ur samba-2.2.4.orig/source/client/smbmnt.c samba-2.2.4/source/client/smbmnt.c
+--- samba-2.2.4.orig/source/client/smbmnt.c Sun Apr 8 15:22:51 2001
++++ samba-2.2.4/source/client/smbmnt.c Sun May 5 16:42:29 2002
+@@ -259,7 +259,10 @@
+ ment.mnt_fsname = share_name ? share_name : "none";
+ ment.mnt_dir = mount_point;
+ ment.mnt_type = "smbfs";
+- ment.mnt_opts = "";
++ if (mount_ro)
++ ment.mnt_opts = "ro";
++ else
++ ment.mnt_opts = "rw";
+ ment.mnt_freq = 0;
+ ment.mnt_passno= 0;
+
diff --git a/packaging/Debian/debian/patches/smbmount-nomtab.patch b/packaging/Debian/debian/patches/smbmount-nomtab.patch
new file mode 100644
index 00000000000..88071481705
--- /dev/null
+++ b/packaging/Debian/debian/patches/smbmount-nomtab.patch
@@ -0,0 +1,160 @@
+diff -uNr samba-3.0alpha22.orig/source/client/smbmnt.c samba-3.0alpha22/source/client/smbmnt.c
+--- samba-3.0alpha22.orig/source/client/smbmnt.c 2003-03-15 12:11:39.000000000 -0600
++++ samba-3.0alpha22/source/client/smbmnt.c 2003-03-15 12:11:49.000000000 -0600
+@@ -28,6 +28,7 @@
+ static uid_t mount_uid;
+ static gid_t mount_gid;
+ static int mount_ro;
++static int no_mtab;
+ static unsigned mount_fmask;
+ static unsigned mount_dmask;
+ static int user_mount;
+@@ -40,6 +41,7 @@
+ printf("Usage: smbmnt mount-point [options]\n");
+ printf("Version %s\n\n",VERSION);
+ printf("-s share share name on server\n"
++ "-n don't update /etc/mtab\n"
+ "-r mount read-only\n"
+ "-u uid mount as uid\n"
+ "-g gid mount as gid\n"
+@@ -54,7 +56,7 @@
+ {
+ int opt;
+
+- while ((opt = getopt (argc, argv, "s:u:g:rf:d:o:")) != EOF)
++ while ((opt = getopt (argc, argv, "s:u:g:nrf:d:o:")) != EOF)
+ {
+ switch (opt)
+ {
+@@ -71,6 +73,9 @@
+ mount_gid = strtol(optarg, NULL, 0);
+ }
+ break;
++ case 'n':
++ no_mtab = 1;
++ break;
+ case 'r':
+ mount_ro = 1;
+ break;
+@@ -274,36 +279,38 @@
+ 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 (!no_mtab) {
++ 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 ((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 (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;
+- }
++ if (unlink(MOUNTED"~") == -1)
++ {
++ fprintf(stderr, "Can't remove "MOUNTED"~");
++ return 1;
++ }
++ }
+
+ return 0;
+ }
+diff -uNr samba-3.0alpha22.orig/source/client/smbmount.c samba-3.0alpha22/source/client/smbmount.c
+--- samba-3.0alpha22.orig/source/client/smbmount.c 2003-03-15 12:04:29.000000000 -0600
++++ samba-3.0alpha22/source/client/smbmount.c 2003-03-15 12:15:10.000000000 -0600
+@@ -48,6 +48,7 @@
+ static int mount_ro;
+ static unsigned mount_fmask;
+ static unsigned mount_dmask;
++static BOOL no_mtab = False;
+ static BOOL use_kerberos;
+ /* TODO: Add code to detect smbfs version in kernel */
+ static BOOL status32_smbfs = False;
+@@ -273,6 +274,9 @@
+ return;
+ }
+
++ if (no_mtab)
++ 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;
+@@ -466,6 +470,9 @@
+ args[i++] = "-s";
+ args[i++] = svc2;
+
++ if (no_mtab) {
++ args[i++] = "-n";
++ }
+ if (mount_ro) {
+ args[i++] = "-r";
+ }
+@@ -661,7 +668,7 @@
+ ****************************************************************************/
+ static void usage(void)
+ {
+- printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
++ printf("Usage: mount.smbfs service mountpoint [-n] [-o options,...]\n");
+
+ printf("Version %s\n\n",VERSION);
+
+@@ -739,8 +746,13 @@
+ argc -= 2;
+ argv += 2;
+
+- opt = getopt(argc, argv, "o:");
+- if(opt != 'o') {
++ opt = getopt(argc, argv, "no:");
++ if (opt == 'n') {
++ DEBUG(3,("No mtab!\n"));
++ no_mtab = True;
++ opt = getopt(argc, argv, "o:");
++ }
++ if (opt != 'o') {
+ return;
+ }
+
diff --git a/packaging/Debian/debian/patches/smbstatus-locking.patch b/packaging/Debian/debian/patches/smbstatus-locking.patch
new file mode 100644
index 00000000000..d37896a3699
--- /dev/null
+++ b/packaging/Debian/debian/patches/smbstatus-locking.patch
@@ -0,0 +1,20 @@
+diff -uNr samba-2.999+3.0.alpha21.orig/source/utils/status.c samba-2.999+3.0.alpha21/source/utils/status.c
+--- samba-2.999+3.0.alpha21.orig/source/utils/status.c 2002-11-26 20:54:22.000000000 -0600
++++ samba-2.999+3.0.alpha21/source/utils/status.c 2002-12-16 23:37:14.000000000 -0600
+@@ -630,6 +630,16 @@
+ if (!shares_only) {
+ int ret;
+
++ tdb = tdb_open_log(lock_path("locking.tdb"), 0, TDB_DEFAULT, O_RDONLY, 0);
++
++ if (!tdb) {
++ d_printf("%s not initialised\n", lock_path("locking.tdb"));
++ d_printf("This is normal if an SMB client has never connected to your server.\n");
++ exit(0);
++ } else {
++ tdb_close(tdb);
++ }
++
+ if (!locking_init(1)) {
+ d_printf("Can't initialise locking module - exiting\n");
+ exit(1);
diff --git a/packaging/Debian/debian/po/POTFILES.in b/packaging/Debian/debian/po/POTFILES.in
new file mode 100644
index 00000000000..95a7ea81506
--- /dev/null
+++ b/packaging/Debian/debian/po/POTFILES.in
@@ -0,0 +1,3 @@
+[type: gettext/rfc822deb] samba-common.templates
+[type: gettext/rfc822deb] samba.templates
+[type: gettext/rfc822deb] swat.templates
diff --git a/packaging/Debian/debian/po/es.po b/packaging/Debian/debian/po/es.po
new file mode 100644
index 00000000000..0d1d1a770de
--- /dev/null
+++ b/packaging/Debian/debian/po/es.po
@@ -0,0 +1,298 @@
+#
+# Translators, if you are not familiar with the PO format, gettext
+# documentation is worth reading, especially sections dedicated to
+# this format, e.g. by running:
+# info -n '(gettext)PO Files'
+# info -n '(gettext)Header Entry'
+#
+# Some information specific to po-debconf are available at
+# /usr/share/doc/po-debconf/README-trans
+# or http://www.debian.org/intl/l10n/po-debconf/README-trans
+#
+# Developers do not need to manually edit POT or PO files.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Samba for Debian 3.0.0beta2-1\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2003-08-30 19:08-0500\n"
+"PO-Revision-Date: 2003-07-02 21:51-0500\n"
+"Last-Translator: Steve Langasek <vorlon@debian.org>\n"
+"Language-Team: Spanish <debian-l10n-spanish@lists.debian.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. Description
+#: ../samba-common.templates:3
+msgid "Character Set for Unix filesystem"
+msgstr "Juego de caracteres para el sistema de ficheros Unix"
+
+#
+#. Description
+#: ../samba-common.templates:3
+msgid ""
+"You currently have a \"character set\" configured in your smb.conf. In "
+"Samba 3.0, this option is replaced by a new option, \"unix charset\". Please "
+"specify the character set you wish to use for theis new option, which "
+"controls how Samba interprets filenames on the file system."
+msgstr ""
+"Actualmente hay una opción de \"character set\" en smb.conf. En Samba 3.0 "
+"esta opción ha sido reemplazado por una opción nueva: \"unix charset\". Por "
+"favor, indique el juego de caracteres que desea usar para esta opción nueva, "
+"que controla cómo Samba interpretará los nombres en el sistema de ficheros."
+
+#. Description
+#: ../samba-common.templates:3
+msgid "If you leave this option blank, your smb.conf will not be changed."
+msgstr "Si deja en blanco este campo, no se cambiará smb.conf."
+
+#. Description
+#: ../samba-common.templates:13
+msgid "Character Set for DOS clients"
+msgstr "Juego de caracteres para los clientes DOS"
+
+#. Description
+#: ../samba-common.templates:13
+msgid ""
+"You currently have a \"client code page\" set in your smb.conf. In Samba "
+"3.0, this option is replaced by the option \"dos charset\". Please specify "
+"the character set you wish to use for this new option. In most cases, the "
+"default chosen for you will be sufficient. Note that this option is not "
+"needed to support Windows clients, it is only for DOS clients. If you leave "
+"this option blank, your smb.conf will not be changed."
+msgstr ""
+"Actualmente hay una opción de \"client code page\" en smb.conf. En Samba "
+"3.0 esta opción ha sido reemplazado por la opción \"dos charset\". Por "
+"favor, indique el juego de caracteres que desea usar para esta opción "
+"nueva. Esta opción no se necesita en absoluto para clientes Windows; es "
+"exclusivamente para los clientes DOS. Si deja en blanco este campo, no se "
+"cambiará smb.conf."
+
+#. Description
+#: ../samba-common.templates:24
+msgid "Modify smb.conf to use WINS settings from DHCP?"
+msgstr ""
+"¿Modificar smb.conf para usar la configuración WINS que proviene de DHCP?"
+
+#. Description
+#: ../samba-common.templates:24
+msgid ""
+"If your computer gets IP address information from a DHCP server on the "
+"network, the DHCP server may also provide information about WINS servers "
+"(\"NetBIOS name servers\") present on the network. This requires a change "
+"to your smb.conf file so that DHCP-provided WINS settings will automatically "
+"be read from /etc/samba/dhcp.conf."
+msgstr ""
+"Si su sistema recibe la dirección IP desde un servidor DHCP en la red, el "
+"servidor DHCP también puede proveerle informaciones sobre los servidores de "
+"WINS que haya en la red. Esto requiere un cambio en el fichero smb.conf "
+"para que la configuración de WINS proveniente de DHCP se lea automaticamente "
+"de /etc/samba/dhcp.conf."
+
+#. Description
+#: ../samba-common.templates:24
+msgid ""
+"You must have the dhcp3-client package installed to take advantage of this "
+"feature."
+msgstr ""
+"Hay que instalar el paquete dhcp3-client para aprovechar esta funcionalidad."
+
+#. Description
+#: ../samba-common.templates:37
+msgid "Configure smb.conf through debconf?"
+msgstr "¿Configurar smb.conf mediante debconf?"
+
+#. Description
+#: ../samba-common.templates:37
+msgid ""
+"The rest of the configuration of Samba deals with questions that affect "
+"parameters in /etc/samba/smb.conf, which is the file used to configure the "
+"Samba programs (nmbd and smbd.) Your current smb.conf contains an 'include' "
+"line or an option that spans multiple lines, which could confuse debconf and "
+"require you to edit your smb.conf by hand to get it working again."
+msgstr ""
+"El resto de la configuración de Samba trata sobre cuestiones que afectan al "
+"contenido de /etc/samba/smb.conf, que es el fichero utilizado para "
+"configurar los programas de Samba (nmbd y smbd). Su smb.conf actual contiene "
+"una línea 'include' o una opción que atraviesa más de una línea, así que "
+"debconf puede dejarlo con un smb.conf descompuesto, requiriendo que lo "
+"arregle a mano."
+
+#. Description
+#: ../samba-common.templates:37
+msgid ""
+"If you don't use debconf to configure smb.conf, you will have to handle any "
+"configuration changes yourself, and will not be able to take advantage of "
+"periodic configuration enhancements. Therefore, use of debconf is "
+"recommended if possible."
+msgstr ""
+"Si no usa debconf para configurar smb.conf, tendrá que cuidar a mano "
+"cualquier cambio a la configuración de Samba y no disfrutará de aumentos "
+"periódicos de configuración. Por eso se recomienda el uso de debconf cuando "
+"sea posible."
+
+#. Description
+#: ../samba-common.templates:52
+msgid "Workgroup/Domain Name?"
+msgstr "Nombre del dominio o del grupo de trabajo"
+
+#. Description
+#: ../samba-common.templates:52
+msgid ""
+"This controls what workgroup your server will appear to be in when queried "
+"by clients. Note that this parameter also controls the Domain name used with "
+"the security=domain setting."
+msgstr ""
+"Es el grupo de trabajo en el que aparecerá su servidor cuando se lo "
+"pregunten los clientes de la red. Este parámetro también controla el nombre "
+"de dominio que se usa con la configuración security=domain."
+
+#. Description
+#: ../samba-common.templates:60
+msgid "Use password encryption?"
+msgstr "¿Utilizar contraseñas cifradas?"
+
+#. Description
+#: ../samba-common.templates:60
+msgid ""
+"Recent Windows clients communicate with SMB servers using encrypted "
+"passwords. If you want to use clear text passwords you will need to change a "
+"parameter in your Windows registry. It is recommended that you use encrypted "
+"passwords. If you do, make sure you have a valid /etc/samba/smbpasswd file "
+"and that you set passwords in there for each user using the smbpasswd "
+"command."
+msgstr ""
+"Los clientes Windows más modernos se comunican con los servidores SMB "
+"utilizando contraseñas cifradas. Si quiere usar contraseñas en texto plano, "
+"tendrá que cambiar un parámetro en el registro de Windows. Es muy "
+"recomendable usar cifrado en las contraseñas. Si elige hacerlo, compruebe "
+"que tiene un fichero /etc/samba/smbpasswd válido y que ha puesto las "
+"contraseñas con el programa smbpasswd."
+
+#. Description
+#: ../samba.templates:4
+msgid "Create samba password database, /var/lib/samba/passdb.tdb?"
+msgstr "¿Crear el base de dados de contraseñas /var/lib/samba/passdb.tdb?"
+
+#. Description
+#: ../samba.templates:4
+msgid ""
+"To be compatible with the defaults in most versions of Windows, Samba must "
+"be configured to use encrypted passwords. This requires user passwords to "
+"be stored in a file separate from /etc/passwd. This file can be created "
+"automatically, but the passwords must be added manually (by you or the user) "
+"by running smbpasswd, and you must arrange to keep it up-to-date in the "
+"future. If you do not create it, you will have to reconfigure samba (and "
+"probably your client machines) to use plaintext passwords. See /usr/share/"
+"doc/samba-doc/htmldocs/ENCRYPTION.html from the samba-doc package for more "
+"details."
+msgstr ""
+"Para manterner la compatibilidad con el comportamiento por defecto de la "
+"mayoria de los sistemas Windows, hay que configurar Samba para que use "
+"contraseñas cifradas, lo cual requiere la creación de un fichero, distinto "
+"del /etc/passwd, donde se guarden las contraseñas de los usuarios. El "
+"fichero se puede crear automaticamente, aunque es necesario añadir las "
+"contraseñas manualmente (por usted o por el usuario) mediante el programa "
+"'smbpasswd', y debe encargarse de mantener las contraseñas al día. Si no "
+"crea este fichero, es imprescindible configurar Samba (y posiblemente los "
+"sistemas Windows) para que use contraseñas no cifradas. Véa /usr/share/doc/"
+"samba-doc/htmldocs/ENCRYPTION.html del paquete samba-doc para más "
+"información."
+
+#. Description
+#: ../samba.templates:17
+msgid "Samba's log files have moved."
+msgstr "Se han cambiado de lugar los ficheros de registro de Samba."
+
+#. Description
+#: ../samba.templates:17
+msgid ""
+"Starting with the first packages of Samba 2.2 for Debian the log files for "
+"both Samba daemons (nmbd and smbd) are now stored in /var/log/samba/. The "
+"names of the files are log.nmbd and log.smbd, for nmbd and smbd respectively."
+msgstr ""
+"A partir de los primeros paquetes Debian de Samba 2.2, los ficheros de "
+"registro de los dos demonios del Samba (nmbd y smbd) se encuentran en /var/"
+"log/samba/. Los nombres de estos ficheros son log.nmbd y log.smbd, para "
+"nmbd y smbd respectivamente."
+
+#. Description
+#: ../samba.templates:17
+msgid ""
+"The old log files that were in /var/log/ will be moved to the new location "
+"for you."
+msgstr ""
+"Los ficheros de registro antiguos que había en /var/log se moverán "
+"automáticamente a su nueva ubicación."
+
+#. Description
+#: ../samba.templates:28
+msgid "Running nmbd from inetd is no longer supported"
+msgstr "Ya no se puede ejecutar nmbd desde el inetd"
+
+#. Description
+#: ../samba.templates:28
+msgid ""
+"Your system was previously configured to start nmbd and smbd from inetd. As "
+"of version 2.999+3.0.alpha20-4, nmbd will no longer be started from inetd. "
+"If you have modified your /etc/init.d/samba startup script, you may need to "
+"adjust it by hand now so that nmbd will start."
+msgstr ""
+"Su sistema estaba configurado anteriormente para ejectuar nmbd y smbd desde "
+"inetd. A partir de la version 2.999+3.0.alpha20-4, nmbd ya no se ejecuta "
+"desde inetd. Si ha modificado el script de arranque /etc/init.d/samba, "
+"puede que tenga que editarlo ahora para que nmbd se ejecute."
+
+#. Choices
+#: ../samba.templates:36
+msgid "daemons, inetd"
+msgstr "demonios, inetd"
+
+#. Description
+#: ../samba.templates:38
+msgid "How do you want to run Samba?"
+msgstr "¿Cómo quiere que se ejecute Samba?"
+
+#. Description
+#: ../samba.templates:38
+msgid ""
+"The Samba daemon smbd can run as a normal daemon or from inetd. Running as a "
+"daemon is the recommended approach."
+msgstr ""
+"El servicio Samba smbd puede ejecutarse como demonio normal o desde el "
+"inetd. Se recomienda que se ejecute como demonio independiente."
+
+#. Description
+#: ../samba.templates:45
+msgid "Move /etc/samba/smbpasswd to /var/lib/samba/passdb.tdb?"
+msgstr ""
+"¿Convertir el fichero /etc/samba/smbpasswd en /var/lib/samba/passdb.tdb?"
+
+#. Description
+#: ../samba.templates:45
+#, fuzzy
+msgid ""
+"Samba 3.0 introduces a newer, more complete SAM database interface which "
+"supersedes the /etc/samba/smbpasswd file. Would you like your existing "
+"smbpasswd file to be migrated to /var/lib/samba/passdb.tdb for you? If you "
+"plan to use another pdb backend (e.g., LDAP) instead, you should answer 'no' "
+"here."
+msgstr ""
+"La versión 3.0 de Samba introduce un nuevo base de dados \"SAM\" más "
+"completo que suplanta al fichero /etc/samba/smbpasswd. ¿Quiere que el "
+"fichero corriente smbpasswd sea convertido en /var/lib/samba/passdb.tdb? Si "
+"pretende usar otro "
+
+#. Description
+#: ../swat.templates:3
+msgid "Your smb.conf will be re-written!"
+msgstr "¡Se sobrescribirá su configuración smb.conf!"
+
+#. Description
+#: ../swat.templates:3
+msgid ""
+"SWAT will rewrite your smb.conf file. It will rearrange the entries and "
+"delete all comments, include= and copy= options. If you have a carefully "
+"crafted smb.conf then back it up or don't use SWAT!"
+msgstr ""
diff --git a/packaging/Debian/debian/po/fr.po b/packaging/Debian/debian/po/fr.po
new file mode 100644
index 00000000000..6be14c19b43
--- /dev/null
+++ b/packaging/Debian/debian/po/fr.po
@@ -0,0 +1,301 @@
+#
+# Translators, if you are not familiar with the PO format, gettext
+# documentation is worth reading, especially sections dedicated to
+# this format, e.g. by running:
+# info -n '(gettext)PO Files'
+# info -n '(gettext)Header Entry'
+#
+# Some information specific to po-debconf are available at
+# /usr/share/doc/po-debconf/README-trans
+# or http://www.debian.org/intl/l10n/po-debconf/README-trans
+#
+# Developers do not need to manually edit POT or PO files.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Samba for Debian 3.0.0beta2-1\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2003-08-30 19:08-0500\n"
+"PO-Revision-Date: 2003-07-02 21:52-0500\n"
+"Last-Translator: Steve Langasek <vorlon@debian.org>\n"
+"Language-Team: French <debian-l10n-french@lists.debian.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-15\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. Description
+#: ../samba-common.templates:3
+msgid "Character Set for Unix filesystem"
+msgstr "Jeu de caractères pour les systèmes de fichiers Unix"
+
+#. Description
+#: ../samba-common.templates:3
+msgid ""
+"You currently have a \"character set\" configured in your smb.conf. In "
+"Samba 3.0, this option is replaced by a new option, \"unix charset\". Please "
+"specify the character set you wish to use for theis new option, which "
+"controls how Samba interprets filenames on the file system."
+msgstr ""
+"Une instruction « character set » (jeu de caractères) est utilisée dans "
+"votre fichier smb.conf. Avec Samba 3.0, cette option est remplacée par une "
+"nouvelle option, « unix charset » (jeu de caractères Unix). Veuillez "
+"indiquer le jeu de caractères que vous voulez utiliser avec cette option, "
+"qui permet d'interpréter les noms de fichiers sur le système."
+
+#. Description
+#: ../samba-common.templates:3
+msgid "If you leave this option blank, your smb.conf will not be changed."
+msgstr ""
+"Le fichier smb.conf ne sera pas modifié si vous laissez cette option sans "
+"réponse."
+
+#. Description
+#: ../samba-common.templates:13
+msgid "Character Set for DOS clients"
+msgstr "Jeu de caractères pour les clients DOS"
+
+#. Description
+#: ../samba-common.templates:13
+msgid ""
+"You currently have a \"client code page\" set in your smb.conf. In Samba "
+"3.0, this option is replaced by the option \"dos charset\". Please specify "
+"the character set you wish to use for this new option. In most cases, the "
+"default chosen for you will be sufficient. Note that this option is not "
+"needed to support Windows clients, it is only for DOS clients. If you leave "
+"this option blank, your smb.conf will not be changed."
+msgstr ""
+"Votre fichier smb.conf contient une option « client code page » (page de "
+"codes du client). Avec Samba 3.0, cette option a été remplacée par l'option "
+"« dos charset » (jeu de caractères DOS). Veuillez indiquer le jeu de "
+"caractères que vous voulez utiliser avec la nouvelle option. En général le "
+"jeu de caractères choisi par défaut est suffisant. Veuillez noter que cette "
+"option ne sert pas pour les clients Windows mais seulement pour les clients "
+"DOS. Le fichier smb.conf ne sera pas modifié si vous laissez cette option "
+"sans réponse. "
+
+#. Description
+#: ../samba-common.templates:24
+msgid "Modify smb.conf to use WINS settings from DHCP?"
+msgstr "Modifier smb.conf pour utiliser les paramètres WINS fournis par DHCP ?"
+
+#. Description
+#: ../samba-common.templates:24
+msgid ""
+"If your computer gets IP address information from a DHCP server on the "
+"network, the DHCP server may also provide information about WINS servers "
+"(\"NetBIOS name servers\") present on the network. This requires a change "
+"to your smb.conf file so that DHCP-provided WINS settings will automatically "
+"be read from /etc/samba/dhcp.conf."
+msgstr ""
+"Si votre ordinateur obtient ses paramètres IP à partir d'un serveur DHCP du "
+"réseau, ce serveur peut aussi fournir des informations sur les serveurs WINS "
+"(serveurs de noms NetBIOS) présents sur le réseau. Une modification du "
+"fichier smb.conf est nécessaire afin que les réglages WINS fournis par le "
+"serveur DHCP soient lus dans /etc/samba/dhcp.conf."
+
+#. Description
+#: ../samba-common.templates:24
+msgid ""
+"You must have the dhcp3-client package installed to take advantage of this "
+"feature."
+msgstr "Pour cela, le paquet dhcp3-client doit être installé."
+
+#. Description
+#: ../samba-common.templates:37
+msgid "Configure smb.conf through debconf?"
+msgstr "Voulez-vous configurer smb.conf avec debconf ?"
+
+#. Description
+#: ../samba-common.templates:37
+msgid ""
+"The rest of the configuration of Samba deals with questions that affect "
+"parameters in /etc/samba/smb.conf, which is the file used to configure the "
+"Samba programs (nmbd and smbd.) Your current smb.conf contains an 'include' "
+"line or an option that spans multiple lines, which could confuse debconf and "
+"require you to edit your smb.conf by hand to get it working again."
+msgstr ""
+"La suite de la configuration de Samba pose des questions sur des paramètres "
+"de /etc/samba/smb.conf, le fichier utilisé pour configurer les programmes de "
+"Samba (nmbd et smbd). Votre fichier actuel contient une ligne « include » ou "
+"une option qui s'étale sur plusieurs lignes : cela peut perturber debconf et "
+"il vaut mieux gérer manuellement le contenu de ce fichier."
+
+#. Description
+#: ../samba-common.templates:37
+msgid ""
+"If you don't use debconf to configure smb.conf, you will have to handle any "
+"configuration changes yourself, and will not be able to take advantage of "
+"periodic configuration enhancements. Therefore, use of debconf is "
+"recommended if possible."
+msgstr ""
+"Si vous n'utilisez pas debconf pour configurer smb.conf, vous devrez vous-"
+"même gérer les modifications de configuration et vous ne pourrez pas "
+"bénéficier des améliorations faites dans la configuration. Si possible, il "
+"est conseillé d'utiliser debconf."
+
+#. Description
+#: ../samba-common.templates:52
+msgid "Workgroup/Domain Name?"
+msgstr "Groupe de travail et nom de domaine ?"
+
+#. Description
+#: ../samba-common.templates:52
+msgid ""
+"This controls what workgroup your server will appear to be in when queried "
+"by clients. Note that this parameter also controls the Domain name used with "
+"the security=domain setting."
+msgstr ""
+"Ce paramètre indique le groupe de travail où les clients trouveront le "
+"serveur. Il indique aussi le nom de domaine utilisé par le paramètre "
+"« security=domain »."
+
+#. Description
+#: ../samba-common.templates:60
+msgid "Use password encryption?"
+msgstr "Voulez-vous chiffrer les mots de passe ?"
+
+#. Description
+#: ../samba-common.templates:60
+msgid ""
+"Recent Windows clients communicate with SMB servers using encrypted "
+"passwords. If you want to use clear text passwords you will need to change a "
+"parameter in your Windows registry. It is recommended that you use encrypted "
+"passwords. If you do, make sure you have a valid /etc/samba/smbpasswd file "
+"and that you set passwords in there for each user using the smbpasswd "
+"command."
+msgstr ""
+"Des clients Windows récents communiquent avec les serveurs SMB en utilisant "
+"des mots de passe chiffrés. Si vous voulez utiliser des mots de passe sans "
+"chiffrement, vous devez modifier un paramètre dans le registre de Windows. "
+"Il est recommandé d'utiliser des mots de passe chiffrés. Si vous le faites, "
+"n'oubliez pas de créer un fichier /etc/samba/smbpasswd et d'y mettre les "
+"mots de passe de tous les utilisateurs qui se servent de la commande "
+"smbpasswd."
+
+#. Description
+#: ../samba.templates:4
+msgid "Create samba password database, /var/lib/samba/passdb.tdb?"
+msgstr "Faut-il créer une base de données /var/lib/samba/passdb.tdb ?"
+
+#. Description
+#: ../samba.templates:4
+msgid ""
+"To be compatible with the defaults in most versions of Windows, Samba must "
+"be configured to use encrypted passwords. This requires user passwords to "
+"be stored in a file separate from /etc/passwd. This file can be created "
+"automatically, but the passwords must be added manually (by you or the user) "
+"by running smbpasswd, and you must arrange to keep it up-to-date in the "
+"future. If you do not create it, you will have to reconfigure samba (and "
+"probably your client machines) to use plaintext passwords. See /usr/share/"
+"doc/samba-doc/htmldocs/ENCRYPTION.html from the samba-doc package for more "
+"details."
+msgstr ""
+"Pour préserver la compatibilité avec le comportement par défaut de la "
+"plupart des systèmes Windows, Samba doit utiliser les mots de passe "
+"chiffrés. Cela exige la création d'un fichier, distinct du fichier /etc/"
+"passwd, pour mettre les mots de passe des utilisateurs. Ce fichier peut être "
+"créé automatiquement, mais quelqu'un (vous ou l'utilisateur) devra ajouter "
+"les mots de passe manuellement en utilisant la commande smbpasswd ; et vous "
+"devrez maintenir à jour ce fichier. Si vous ne voulez pas créer le fichier "
+"maintenant, Samba (et peut-être les ordinateurs Windows) devra utiliser des "
+"mots de passe non chiffrés. Voyez /usr/share/doc/samba-doc/htmldocs/"
+"ENCRYPTION.html dans le paquet samba-doc pour plus de détails."
+
+#. Description
+#: ../samba.templates:17
+msgid "Samba's log files have moved."
+msgstr "Les fichiers-journaux de Samba ont changé de place."
+
+#. Description
+#: ../samba.templates:17
+msgid ""
+"Starting with the first packages of Samba 2.2 for Debian the log files for "
+"both Samba daemons (nmbd and smbd) are now stored in /var/log/samba/. The "
+"names of the files are log.nmbd and log.smbd, for nmbd and smbd respectively."
+msgstr ""
+"À partir des premiers paquets de Samba 2.2 pour Debian, les fichiers-"
+"journaux des démons nmbd et smbd se trouvent dans /var/log/samba/. Les noms "
+"de ces fichiers sont log.nmbd et log.smbd pour, respectivement, nmbd et smbd."
+
+#. Description
+#: ../samba.templates:17
+msgid ""
+"The old log files that were in /var/log/ will be moved to the new location "
+"for you."
+msgstr ""
+"Les anciens fichiers-journaux dans /var/log/ seront mis au bon endroit."
+
+#. Description
+#: ../samba.templates:28
+msgid "Running nmbd from inetd is no longer supported"
+msgstr "L'utilisation de nmbd à partir d'inetd n'est plus supportée"
+
+#. Description
+#: ../samba.templates:28
+msgid ""
+"Your system was previously configured to start nmbd and smbd from inetd. As "
+"of version 2.999+3.0.alpha20-4, nmbd will no longer be started from inetd. "
+"If you have modified your /etc/init.d/samba startup script, you may need to "
+"adjust it by hand now so that nmbd will start."
+msgstr ""
+"Votre système lançait nmbd et smbd à partir d'inetd. Depuis la version 2.999"
+"+3.0.alpha20-4, on ne peut plus exécuter nmbd à partir d'inetd. Si vous avez "
+"modifié le script de lancement /etc/init.d/samba, vous avez peut-être besoin "
+"de le corriger manuellement pour permettre le lancement de nmbd. "
+
+#. Choices
+#: ../samba.templates:36
+msgid "daemons, inetd"
+msgstr "démons, inetd"
+
+#. Description
+#: ../samba.templates:38
+msgid "How do you want to run Samba?"
+msgstr "Comment voulez-vous lancer Samba ?"
+
+#. Description
+#: ../samba.templates:38
+msgid ""
+"The Samba daemon smbd can run as a normal daemon or from inetd. Running as a "
+"daemon is the recommended approach."
+msgstr ""
+"Le service de Samba smbd peut s'exécuter en tant que démon classique ou bien "
+"être lancé par inetd. L'approche recommandée est qu'il s'exécute en tant que "
+"démon."
+
+#. Description
+#: ../samba.templates:45
+msgid "Move /etc/samba/smbpasswd to /var/lib/samba/passdb.tdb?"
+msgstr "Faut-il convertir /etc/samba/smbpasswd en /var/lib/samba/passdb.tdb ?"
+
+#. Description
+#: ../samba.templates:45
+msgid ""
+"Samba 3.0 introduces a newer, more complete SAM database interface which "
+"supersedes the /etc/samba/smbpasswd file. Would you like your existing "
+"smbpasswd file to be migrated to /var/lib/samba/passdb.tdb for you? If you "
+"plan to use another pdb backend (e.g., LDAP) instead, you should answer 'no' "
+"here."
+msgstr ""
+"Avec Samba 3.0, une nouvelle interface pour les bases de données SAM, plus "
+"complète, vient remplacer le fichier /etc/samba/smbpasswd. Voulez-vous que "
+"votre fichier existant soit remplacé par /var/lib/samba/passdb.tdb ? Si vous "
+"envisagez d'utiliser une autre interface d'authentification (p.ex. LDAP), "
+"refusez la proposition maintenant."
+
+#. Description
+#: ../swat.templates:3
+msgid "Your smb.conf will be re-written!"
+msgstr "Ré-écriture de votre fichier smb.conf !"
+
+#. Description
+#: ../swat.templates:3
+msgid ""
+"SWAT will rewrite your smb.conf file. It will rearrange the entries and "
+"delete all comments, include= and copy= options. If you have a carefully "
+"crafted smb.conf then back it up or don't use SWAT!"
+msgstr ""
+"SWAT va modifier votre fichier smb.conf. Les entrées seront réorganisées et "
+"les commentaires détruits ainsi que les entrées « include = » et « copy = ». "
+"Si votre smb.conf était soigneusement construit, sauvegardez-le ou "
+"n'utilisez pas SWAT !"
diff --git a/packaging/Debian/debian/po/nl.po b/packaging/Debian/debian/po/nl.po
new file mode 100644
index 00000000000..61e8742dbb5
--- /dev/null
+++ b/packaging/Debian/debian/po/nl.po
@@ -0,0 +1,302 @@
+#
+# Translators, if you are not familiar with the PO format, gettext
+# documentation is worth reading, especially sections dedicated to
+# this format, e.g. by running:
+# info -n '(gettext)PO Files'
+# info -n '(gettext)Header Entry'
+#
+# Some information specific to po-debconf are available at
+# /usr/share/doc/po-debconf/README-trans
+# or http://www.debian.org/intl/l10n/po-debconf/README-trans
+#
+# Developers do not need to manually edit POT or PO files.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: samba\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2003-08-30 19:08-0500\n"
+"PO-Revision-Date: 2003-08-30 02:03+0100\n"
+"Last-Translator: Bart Cornelis <cobaco@linux.be>\n"
+"Language-Team: dutch <debian-l10n-dutch@lists.debian.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. Description
+#: ../samba-common.templates:3
+msgid "Character Set for Unix filesystem"
+msgstr "Karakterset voor Unix bestandsysteem"
+
+#. Description
+#: ../samba-common.templates:3
+msgid ""
+"You currently have a \"character set\" configured in your smb.conf. In "
+"Samba 3.0, this option is replaced by a new option, \"unix charset\". Please "
+"specify the character set you wish to use for theis new option, which "
+"controls how Samba interprets filenames on the file system."
+msgstr ""
+"U heeft momenteel een \"karakterset\" ingesteld in uw smb.conf. In Samba 3.0 "
+"werd deze optie vervangen door een nieuwe \"unix karakterset\" optie. "
+"Gelieve de karakterset die u voor deze nieuwe optie wil gebruiken aan te "
+"geven, deze bepaald hoe Samba de bestandsnamen in het bestandsysteem "
+"interpreteerd."
+
+#. Description
+#: ../samba-common.templates:3
+msgid "If you leave this option blank, your smb.conf will not be changed."
+msgstr ""
+"Uw smb.conf bestand wordt niet aangepast indien u deze optie leeg laat."
+
+#. Description
+#: ../samba-common.templates:13
+msgid "Character Set for DOS clients"
+msgstr "Karakterset voor DOS-clients"
+
+#. Description
+#: ../samba-common.templates:13
+msgid ""
+"You currently have a \"client code page\" set in your smb.conf. In Samba "
+"3.0, this option is replaced by the option \"dos charset\". Please specify "
+"the character set you wish to use for this new option. In most cases, the "
+"default chosen for you will be sufficient. Note that this option is not "
+"needed to support Windows clients, it is only for DOS clients. If you leave "
+"this option blank, your smb.conf will not be changed."
+msgstr ""
+"U heeft momenteel een \"client code pagina\" ingesteld in uw smb.conf. In "
+"Samba 3.0 werd deze optie vervangen door de optie \"dos karakterset\". "
+"Gelieve de karakterset die u voor deze nieuwe optie wilt gebruiken aan te "
+"geven. In de meeste gevallen zal het standaard ingevulde antwoord voldoen. "
+"Merk op dat deze optie niet nodig is voor ondersteuning van Windows-clients, "
+"enkel voor DOS-clients. Uw smb.conf wordt niet veranderd wanneer u deze "
+"optie blank laat."
+
+#. Description
+#: ../samba-common.templates:24
+msgid "Modify smb.conf to use WINS settings from DHCP?"
+msgstr "smb.conf aanpassen om de WINS instellingen van DHCP te gebruiken?"
+
+#. Description
+#: ../samba-common.templates:24
+msgid ""
+"If your computer gets IP address information from a DHCP server on the "
+"network, the DHCP server may also provide information about WINS servers "
+"(\"NetBIOS name servers\") present on the network. This requires a change "
+"to your smb.conf file so that DHCP-provided WINS settings will automatically "
+"be read from /etc/samba/dhcp.conf."
+msgstr ""
+"Indien uw computer zijn ip-addres informatie van een DHCP-server op het "
+"netwerk haalt, voorziet deze mogelijks ook informatie betreffende de op het "
+"netwerk aanwezige WINS-servers (\"NetBIOS naam servers\").\" Dit vereist een "
+"verandering aan uw smb.conf bestand, opdat de door DHCP doorgegeven WINS "
+"instellingen automatisch gelezen worden van /etc/samba/dhcp.conf."
+
+#. Description
+#: ../samba-common.templates:24
+msgid ""
+"You must have the dhcp3-client package installed to take advantage of this "
+"feature."
+msgstr ""
+"U dient het dhcp3-client-pakket geïnstalleert te hebben om van deze optie "
+"gebruik te maken."
+
+#. Description
+#: ../samba-common.templates:37
+msgid "Configure smb.conf through debconf?"
+msgstr "Configuratie in smb.conf met debconf beheren?"
+
+#. Description
+#: ../samba-common.templates:37
+msgid ""
+"The rest of the configuration of Samba deals with questions that affect "
+"parameters in /etc/samba/smb.conf, which is the file used to configure the "
+"Samba programs (nmbd and smbd.) Your current smb.conf contains an 'include' "
+"line or an option that spans multiple lines, which could confuse debconf and "
+"require you to edit your smb.conf by hand to get it working again."
+msgstr ""
+"De resterende Samba configuratie beslaat parameters in /etc/samba/smb.conf, "
+"het configuratiebestand dat gebruikt wordt voor de Samba programmas (nmbd en "
+"smbd). Uw huidige smb.conf bevat een 'include' regel of een optie die "
+"meerdere regels beslaat, dit kan debconf mogelijks in verwarring brenngen. "
+"Indien debconf in verwarring raakt dient u uw smb.conf handmatig aan te "
+"passen om samba terug werkend te krijgen."
+
+#. Description
+#: ../samba-common.templates:37
+msgid ""
+"If you don't use debconf to configure smb.conf, you will have to handle any "
+"configuration changes yourself, and will not be able to take advantage of "
+"periodic configuration enhancements. Therefore, use of debconf is "
+"recommended if possible."
+msgstr ""
+"Indien u geen gebruik maakt van debconf voor het beheer van smb.conf dient u "
+"alle configuratie instellingen zelf te doen, en zult u geen voordeel halen "
+"uit de periodieke configuratie verbeteringen. Het gebruik van debconf wordt "
+"dan ook aangeraden. "
+
+#. Description
+#: ../samba-common.templates:52
+msgid "Workgroup/Domain Name?"
+msgstr "Werkgroep/Domeinnaam?"
+
+#. Description
+#: ../samba-common.templates:52
+msgid ""
+"This controls what workgroup your server will appear to be in when queried "
+"by clients. Note that this parameter also controls the Domain name used with "
+"the security=domain setting."
+msgstr ""
+"Dit bepaald van welke werkgroep uw server deel zal lijken uit te maken bij "
+"het beantwoorden van vragen door clients. Merk op dat deze parameter ook de "
+"domeinnaam die gebruikt wordt voor de security=domein instelling bepaald."
+
+#. Description
+#: ../samba-common.templates:60
+msgid "Use password encryption?"
+msgstr "Wachtwoord versleuteling gebruiken?"
+
+#. Description
+#: ../samba-common.templates:60
+msgid ""
+"Recent Windows clients communicate with SMB servers using encrypted "
+"passwords. If you want to use clear text passwords you will need to change a "
+"parameter in your Windows registry. It is recommended that you use encrypted "
+"passwords. If you do, make sure you have a valid /etc/samba/smbpasswd file "
+"and that you set passwords in there for each user using the smbpasswd "
+"command."
+msgstr ""
+"Recente Windows clients maken gebruik van versleutelde wachtwoorden voor de "
+"communicatie met SMB-servers. Indien u onversleutelde tekst wachtwoorden "
+"wilt gebruiken dient u een parameter te veranderen in de Windows registry. "
+"Het gebruik van versleutelde wachtwoorden wordt aangeraden. Indien u dit "
+"doet zorg er dan voor dat u een geldig /etc/samba/smbpasswd bestand heeft en "
+"dat u de wachtwoorden daar voor elke gebruiker met behulp van smbpasswd "
+"insteld. "
+
+#. Description
+#: ../samba.templates:4
+msgid "Create samba password database, /var/lib/samba/passdb.tdb?"
+msgstr "Samba wachtwoord database, /var/lib/samba/passdb.tdb, aanmaken?"
+
+#. Description
+#: ../samba.templates:4
+msgid ""
+"To be compatible with the defaults in most versions of Windows, Samba must "
+"be configured to use encrypted passwords. This requires user passwords to "
+"be stored in a file separate from /etc/passwd. This file can be created "
+"automatically, but the passwords must be added manually (by you or the user) "
+"by running smbpasswd, and you must arrange to keep it up-to-date in the "
+"future. If you do not create it, you will have to reconfigure samba (and "
+"probably your client machines) to use plaintext passwords. See /usr/share/"
+"doc/samba-doc/htmldocs/ENCRYPTION.html from the samba-doc package for more "
+"details."
+msgstr ""
+"Om compatibel te zijn met de standaard instellingen van de meeste "
+"Windowsversies dient Samba gebruik te maken van versleutelde wachtwoorden. "
+"Dit vereist dat de gebruikerswachtwoorden opgeslagen worden in een bestand "
+"dat verschilt van /etc/passwd. Dit bestand kan automatisch aangemaakt "
+"worden, maar de wachtwoorden dienen handmatig (door de gebuiker) toegevoegt "
+"te worden met het smbpasswd commando. U dient dit bestand in de toekomst ook "
+"aktueel te houden. Indien u dit bestand niet aanmaakt zult u samba (en naar "
+"alle waarschijnlijkheid de client machines) moeten herconfigureren zodat "
+"deze onversleutelde wachtwoorden gebruiken. Zie /usr/share/doc/samba-doc/"
+"htmldocs/ENCRYPTION.html uit het samba-doc pakket voor meer details."
+
+#. Description
+#: ../samba.templates:17
+msgid "Samba's log files have moved."
+msgstr "Samba's log bestanden zijn verhuisd."
+
+#. Description
+#: ../samba.templates:17
+msgid ""
+"Starting with the first packages of Samba 2.2 for Debian the log files for "
+"both Samba daemons (nmbd and smbd) are now stored in /var/log/samba/. The "
+"names of the files are log.nmbd and log.smbd, for nmbd and smbd respectively."
+msgstr ""
+"Vanaf de eerste Debian pakketten vaan Samba versie 2.2 zijn de log bestanden "
+"van beide Samba daemons (nmbd en smbd) opgeslagen in /var/log/samba/. De log "
+"bestanden voor nmbd en smbd zijn, respectievelijk, log.nmbd en log.smbd."
+
+#. Description
+#: ../samba.templates:17
+msgid ""
+"The old log files that were in /var/log/ will be moved to the new location "
+"for you."
+msgstr ""
+"De oude logbestanden in /var/log worden voor u naar de nieuwe locatie "
+"verplaatst."
+
+#. Description
+#: ../samba.templates:28
+msgid "Running nmbd from inetd is no longer supported"
+msgstr "nmbd vanuit inetd draaien wordt niet langer ondersteund"
+
+#. Description
+#: ../samba.templates:28
+msgid ""
+"Your system was previously configured to start nmbd and smbd from inetd. As "
+"of version 2.999+3.0.alpha20-4, nmbd will no longer be started from inetd. "
+"If you have modified your /etc/init.d/samba startup script, you may need to "
+"adjust it by hand now so that nmbd will start."
+msgstr ""
+"Uw systeem was geconfigureerd om nmbd en smbd vanuit inetd te starten. Vanaf "
+"versie 2.999+3.0.alpha20-4 kan nmbd niet langer door inetd gestart worden. "
+"Indien u uw /etc/init.d/samba startup-script aangepast had, dient u dit "
+"mogelijks aan te passen opdat nmbd zou starten."
+
+#. Choices
+#: ../samba.templates:36
+msgid "daemons, inetd"
+msgstr "daemons, inetd"
+
+#. Description
+#: ../samba.templates:38
+msgid "How do you want to run Samba?"
+msgstr "Hoe wilt u Samba draaien?"
+
+#. Description
+#: ../samba.templates:38
+msgid ""
+"The Samba daemon smbd can run as a normal daemon or from inetd. Running as a "
+"daemon is the recommended approach."
+msgstr ""
+"De Samba daemon smbd kan als een normale daemon of via inetd draaien. Als "
+"een daemon is de aangeraden aanpak."
+
+#. Description
+#: ../samba.templates:45
+msgid "Move /etc/samba/smbpasswd to /var/lib/samba/passdb.tdb?"
+msgstr "/etc/samba/smbpasswd verplaatsen naar /var/lib/samba/passdb.tdb?"
+
+#. Description
+#: ../samba.templates:45
+msgid ""
+"Samba 3.0 introduces a newer, more complete SAM database interface which "
+"supersedes the /etc/samba/smbpasswd file. Would you like your existing "
+"smbpasswd file to be migrated to /var/lib/samba/passdb.tdb for you? If you "
+"plan to use another pdb backend (e.g., LDAP) instead, you should answer 'no' "
+"here."
+msgstr ""
+"Samba 3.0 introduceert een nieuwe, meer complete SAM databaseinterface die "
+"het /etc/samba/smbpasswd bestand overstijgt. \"Wilt u dat ik uw bestaand "
+"smbpasswd bestand voor u naar /var/lib/samba/passdb.tdb migreer? Indien u "
+"van plan bent om, in plaats hiervan, een ander pdb-backend (e.g. LDAP) te "
+"gebruiken dient u hier 'nee' te antwoorden."
+
+#. Description
+#: ../swat.templates:3
+msgid "Your smb.conf will be re-written!"
+msgstr "Uw smb.conf wordt herschreven!"
+
+#. Description
+#: ../swat.templates:3
+msgid ""
+"SWAT will rewrite your smb.conf file. It will rearrange the entries and "
+"delete all comments, include= and copy= options. If you have a carefully "
+"crafted smb.conf then back it up or don't use SWAT!"
+msgstr ""
+"SWAT zal uw smb.conf bestand herschrijven. Het zal de ingangen anders "
+"schikken en alle comments, include= en copy= opties verwijderen. Als u een, "
+"met veel zorg, handmatig opgebouwd smb.conf bestand heeft, maak dan een "
+"reservekopie of zie van het gebruik van SWAT af! "
diff --git a/packaging/Debian/debian/po/pt_BR.po b/packaging/Debian/debian/po/pt_BR.po
new file mode 100644
index 00000000000..2af42a562b9
--- /dev/null
+++ b/packaging/Debian/debian/po/pt_BR.po
@@ -0,0 +1,308 @@
+#
+# Translators, if you are not familiar with the PO format, gettext
+# documentation is worth reading, especially sections dedicated to
+# this format, e.g. by running:
+# info -n '(gettext)PO Files'
+# info -n '(gettext)Header Entry'
+#
+# Some information specific to po-debconf are available at
+# /usr/share/doc/po-debconf/README-trans
+# or http://www.debian.org/intl/l10n/po-debconf/README-trans
+#
+# Developers do not need to manually edit POT or PO files.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: samba_3.0.0beta2-1\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2003-08-30 19:08-0500\n"
+"PO-Revision-Date: 2003-07-02 21:56-0500\n"
+"Last-Translator: Steve Langasek <vorlon@debian.org>\n"
+"Language-Team: Debian-BR Project <debian-l10n-portuguese@lists.debian.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. Description
+#: ../samba-common.templates:3
+msgid "Character Set for Unix filesystem"
+msgstr "Conjunto de caracteres para sistema de arquivos Unix"
+
+#. Description
+#: ../samba-common.templates:3
+#, fuzzy
+msgid ""
+"You currently have a \"character set\" configured in your smb.conf. In "
+"Samba 3.0, this option is replaced by a new option, \"unix charset\". Please "
+"specify the character set you wish to use for theis new option, which "
+"controls how Samba interprets filenames on the file system."
+msgstr ""
+"Atualmente existe um \"conjunto de caracteres\" (\"character set\") "
+"configurado em seu arquivo smb.conf. No Samba 3.0, esta opção foi "
+"substituída por uma nova opção, \"unix charset\". Por favor especifique o "
+"conjunto de caracteres a ser usado para essa nova opção, a qual controla "
+"como o Samba interpreta nomes de arquivos no sistema de arquivos."
+
+#. Description
+#: ../samba-common.templates:3
+msgid "If you leave this option blank, your smb.conf will not be changed."
+msgstr ""
+"Caso você deixe esta opção em branco, seu arquivo smb.conf não será "
+"modificado."
+
+#. Description
+#: ../samba-common.templates:13
+msgid "Character Set for DOS clients"
+msgstr "Conjunto de caracteres para clientes DOS"
+
+#. Description
+#: ../samba-common.templates:13
+msgid ""
+"You currently have a \"client code page\" set in your smb.conf. In Samba "
+"3.0, this option is replaced by the option \"dos charset\". Please specify "
+"the character set you wish to use for this new option. In most cases, the "
+"default chosen for you will be sufficient. Note that this option is not "
+"needed to support Windows clients, it is only for DOS clients. If you leave "
+"this option blank, your smb.conf will not be changed."
+msgstr ""
+"Atualmente existe um \"código de página do cliente\" (\"client code page\") "
+"definido em seu arquivo smb.conf. No Samba 3.0, essa opção foi substituída "
+"pela opção \"dos charset\". Por favor, especifique o conjunto de caracteres "
+"que você deseja usar para essa nova opção. Note que essa opção não é "
+"necessária para suportar clientes Windows mas sim válida somente para "
+"clientes DOS. Caso você deixe essa opção em branco, seu arquivo smb.conf não "
+"será modificado."
+
+#. Description
+#: ../samba-common.templates:24
+msgid "Modify smb.conf to use WINS settings from DHCP?"
+msgstr "Modificar smb.conf para usar configurações WINS fornecidas via DHCP ?"
+
+#. Description
+#: ../samba-common.templates:24
+msgid ""
+"If your computer gets IP address information from a DHCP server on the "
+"network, the DHCP server may also provide information about WINS servers "
+"(\"NetBIOS name servers\") present on the network. This requires a change "
+"to your smb.conf file so that DHCP-provided WINS settings will automatically "
+"be read from /etc/samba/dhcp.conf."
+msgstr ""
+"Caso seu computador obtenha as informações de endereçamento IP de um "
+"servidor DHCP na rede, o servidor DHCP pode também fornecer informações "
+"sobre servidor WINS (\"Servidor de Nomes NetBIOS\") presentes na rede. Para "
+"o Samba ler as configurações WINS fornecidas pelo servidor DHCP "
+"automaticamente do arquivo /etc/samba/dhcp.conf, é preciso modificar seu "
+"arquivo smb.conf."
+
+#. Description
+#: ../samba-common.templates:24
+msgid ""
+"You must have the dhcp3-client package installed to take advantage of this "
+"feature."
+msgstr ""
+"Você deve possuir o pacote dhcp3-client instalado para poder usar este "
+"recurso."
+
+#. Description
+#: ../samba-common.templates:37
+msgid "Configure smb.conf through debconf?"
+msgstr "Configurar smb.conf através do debconf ?"
+
+#. Description
+#: ../samba-common.templates:37
+msgid ""
+"The rest of the configuration of Samba deals with questions that affect "
+"parameters in /etc/samba/smb.conf, which is the file used to configure the "
+"Samba programs (nmbd and smbd.) Your current smb.conf contains an 'include' "
+"line or an option that spans multiple lines, which could confuse debconf and "
+"require you to edit your smb.conf by hand to get it working again."
+msgstr ""
+"O restante da configuração do Samba lida com questões que afetam parâmetros "
+"no arquivo /etc/samba/smb.conf, que é o arquivo usado para configurar os "
+"programas Samba (nmbd e smbd). Seu arquivo smb.conf atual contém uma linha "
+"'include' ou uma opção que ocupa diversas linhas, o que pode confundir o "
+"debconf e requerer que você edite seu arquivo smb.conf manualmente para tê-"
+"lo funcional novamente."
+
+#. Description
+#: ../samba-common.templates:37
+msgid ""
+"If you don't use debconf to configure smb.conf, you will have to handle any "
+"configuration changes yourself, and will not be able to take advantage of "
+"periodic configuration enhancements. Therefore, use of debconf is "
+"recommended if possible."
+msgstr ""
+"Caso você opte por não usar o debconf para configurar o smb.conf, será "
+"necessário que você lide com quaisquer mudanças de configurações manualmente "
+"e você não poderá aproveitar os melhoramentos periódicos de configuração. "
+"Por esse motivo, é recomendado usar o debconf, caso seja possível."
+
+#. Description
+#: ../samba-common.templates:52
+msgid "Workgroup/Domain Name?"
+msgstr "Nome de Domínio/Grupo de Trabalho ?"
+
+#. Description
+#: ../samba-common.templates:52
+msgid ""
+"This controls what workgroup your server will appear to be in when queried "
+"by clients. Note that this parameter also controls the Domain name used with "
+"the security=domain setting."
+msgstr ""
+"Este parâmetro controla em qual grupo de trabalho (workgroup) seu servidor "
+"parecerá estar quando o mesmo for pesquisado por clientes. Note que este "
+"parâmetro também controla o nome de Domínio usado com a configuração "
+"security=domain."
+
+#. Description
+#: ../samba-common.templates:60
+msgid "Use password encryption?"
+msgstr "Usar encriptação de senhas ?"
+
+#. Description
+#: ../samba-common.templates:60
+msgid ""
+"Recent Windows clients communicate with SMB servers using encrypted "
+"passwords. If you want to use clear text passwords you will need to change a "
+"parameter in your Windows registry. It is recommended that you use encrypted "
+"passwords. If you do, make sure you have a valid /etc/samba/smbpasswd file "
+"and that you set passwords in there for each user using the smbpasswd "
+"command."
+msgstr ""
+"Clientes Windows atuais comunicam-se com servidores SMB usando senhas "
+"encriptadas. Caso você queira usar senhas em texto puro você precisará "
+"modificar um parâmetro no registro de seu Windows. É recomendado que você "
+"use senhas encriptadas. Se for usá-las, certifique-se de possuir um arquivo /"
+"etc/samba/smbpasswd válido e que você tenha definido senhas no mesmo para "
+"cada usuário, utilizando o comando smbpasswd."
+
+#. Description
+#: ../samba.templates:4
+msgid "Create samba password database, /var/lib/samba/passdb.tdb?"
+msgstr "Gerar a base de dados para senhas /var/lib/samba/passdb.tdb ?"
+
+#. Description
+#: ../samba.templates:4
+msgid ""
+"To be compatible with the defaults in most versions of Windows, Samba must "
+"be configured to use encrypted passwords. This requires user passwords to "
+"be stored in a file separate from /etc/passwd. This file can be created "
+"automatically, but the passwords must be added manually (by you or the user) "
+"by running smbpasswd, and you must arrange to keep it up-to-date in the "
+"future. If you do not create it, you will have to reconfigure samba (and "
+"probably your client machines) to use plaintext passwords. See /usr/share/"
+"doc/samba-doc/htmldocs/ENCRYPTION.html from the samba-doc package for more "
+"details."
+msgstr ""
+"Para compatibilidade com os padrões adotados em todas as versões atuais do "
+"Windows o Samba deve ser configurado para usar senhas encriptadas. Isso "
+"requer que as senhas dos usuários sejam armazenadas em um arquivo diferente "
+"do /etc/passwd. Esse arquivo pode ser criado automaticamente, mas as senhas "
+"devem ser definidas manualmente (por você ou pelo usuário) executando o "
+"utilitário smbpasswd. Você deve certificar-se de manter esse arquivo "
+"atualizado futuramente. Caso você não crie esse arquivo, você terá que "
+"reconfigurar o Samba (e provavelmente suas máquinas clientes) para "
+"utilização de senhas em texto puro. Consulte a documentação do pacote samba-"
+"doc em /usr/share/doc/samba-doc/htmldocs/ENCRYPTION.html para maiores "
+"detalhes."
+
+#. Description
+#: ../samba.templates:17
+msgid "Samba's log files have moved."
+msgstr "Arquivos de log do Samba foram movidos."
+
+#. Description
+#: ../samba.templates:17
+msgid ""
+"Starting with the first packages of Samba 2.2 for Debian the log files for "
+"both Samba daemons (nmbd and smbd) are now stored in /var/log/samba/. The "
+"names of the files are log.nmbd and log.smbd, for nmbd and smbd respectively."
+msgstr ""
+"Desde os primeiros pacotes Debian do Samba 2.2 os arquivos de log para ambos "
+"os daemons Samba (nmbd e smbd) são armazenados no diretório /var/log/samba/. "
+"Os nomes desses arquivos são log.nmbd e log.smbd, para o nmbd e para o smbd, "
+"respectivamente."
+
+#. Description
+#: ../samba.templates:17
+msgid ""
+"The old log files that were in /var/log/ will be moved to the new location "
+"for you."
+msgstr ""
+"Os antigos arquivos de log que estavam em /var/log/ serão movidos para a "
+"nova localização automaticamente para você."
+
+#. Description
+#: ../samba.templates:28
+msgid "Running nmbd from inetd is no longer supported"
+msgstr "A execução do nmbd a partir do inetd não é mais suportada"
+
+#. Description
+#: ../samba.templates:28
+msgid ""
+"Your system was previously configured to start nmbd and smbd from inetd. As "
+"of version 2.999+3.0.alpha20-4, nmbd will no longer be started from inetd. "
+"If you have modified your /etc/init.d/samba startup script, you may need to "
+"adjust it by hand now so that nmbd will start."
+msgstr ""
+"Seu sistema foi previamente configurado para iniciar os daemons nmbd e smbd "
+"a partir do inetd. Desde a versão 2.999+3.0.alpha20-4, o nmbd não mais será "
+"iniciado a partir do inetd. Caso você tenha modificado seu script de "
+"inicialização /etc/init.d/samba, você pode precisar ajustá-lo manualmente "
+"agora para que o nmbd seja iniciado."
+
+#. Choices
+#: ../samba.templates:36
+msgid "daemons, inetd"
+msgstr "daemons, inetd"
+
+#. Description
+#: ../samba.templates:38
+msgid "How do you want to run Samba?"
+msgstr "Como você deseja que o Samba seja executado ?"
+
+#. Description
+#: ../samba.templates:38
+msgid ""
+"The Samba daemon smbd can run as a normal daemon or from inetd. Running as a "
+"daemon is the recommended approach."
+msgstr ""
+"O serviço Samba smbd pode ser executado como daemon normal ou a partir do "
+"inetd. Executá-lo como daemon é o método recomendado."
+
+#. Description
+#: ../samba.templates:45
+msgid "Move /etc/samba/smbpasswd to /var/lib/samba/passdb.tdb?"
+msgstr "Mover /etc/samba/smbpasswd para /var/lib/samba/passdb.tdb ?"
+
+#. Description
+#: ../samba.templates:45
+msgid ""
+"Samba 3.0 introduces a newer, more complete SAM database interface which "
+"supersedes the /etc/samba/smbpasswd file. Would you like your existing "
+"smbpasswd file to be migrated to /var/lib/samba/passdb.tdb for you? If you "
+"plan to use another pdb backend (e.g., LDAP) instead, you should answer 'no' "
+"here."
+msgstr ""
+"O Samba 3.0 introduz uma nova e mais complete interface de base de dados SAM "
+"que substitui o arquivo /etc/samba/smbpasswd. Você gostaria que o arquivo "
+"smbpasswd existente fosse migrado para /var/lib/samba/passdb.tdb para você ? "
+"Caso você planeje usar um outro backend (como LDAP, por exemplo) você deverá "
+"escolher 'não' aqui."
+
+#. Description
+#: ../swat.templates:3
+msgid "Your smb.conf will be re-written!"
+msgstr "Seu arquivo smb.conf será reescrito !"
+
+#. Description
+#: ../swat.templates:3
+msgid ""
+"SWAT will rewrite your smb.conf file. It will rearrange the entries and "
+"delete all comments, include= and copy= options. If you have a carefully "
+"crafted smb.conf then back it up or don't use SWAT!"
+msgstr ""
+"O SWAT irá reescrever seu arquivo smb.conf. Ele irá rearrumar as entradas e "
+"apagar todos os comentários, opções include= e copy=. Caso você possua um "
+"arquivo smb.conf cuidadosamente criado e comentado, faça uma cópia de "
+"segurança (backup) do mesmo ou não use o SWAT !"
diff --git a/packaging/Debian/debian/po/templates.pot b/packaging/Debian/debian/po/templates.pot
new file mode 100644
index 00000000000..465f4719b0d
--- /dev/null
+++ b/packaging/Debian/debian/po/templates.pot
@@ -0,0 +1,233 @@
+#
+# Translators, if you are not familiar with the PO format, gettext
+# documentation is worth reading, especially sections dedicated to
+# this format, e.g. by running:
+# info -n '(gettext)PO Files'
+# info -n '(gettext)Header Entry'
+#
+# Some information specific to po-debconf are available at
+# /usr/share/doc/po-debconf/README-trans
+# or http://www.debian.org/intl/l10n/po-debconf/README-trans
+#
+# Developers do not need to manually edit POT or PO files.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2003-08-30 19:08-0500\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. Description
+#: ../samba-common.templates:3
+msgid "Character Set for Unix filesystem"
+msgstr ""
+
+#. Description
+#: ../samba-common.templates:3
+msgid ""
+"You currently have a \"character set\" configured in your smb.conf. In "
+"Samba 3.0, this option is replaced by a new option, \"unix charset\". Please "
+"specify the character set you wish to use for theis new option, which "
+"controls how Samba interprets filenames on the file system."
+msgstr ""
+
+#. Description
+#: ../samba-common.templates:3
+msgid "If you leave this option blank, your smb.conf will not be changed."
+msgstr ""
+
+#. Description
+#: ../samba-common.templates:13
+msgid "Character Set for DOS clients"
+msgstr ""
+
+#. Description
+#: ../samba-common.templates:13
+msgid ""
+"You currently have a \"client code page\" set in your smb.conf. In Samba "
+"3.0, this option is replaced by the option \"dos charset\". Please specify "
+"the character set you wish to use for this new option. In most cases, the "
+"default chosen for you will be sufficient. Note that this option is not "
+"needed to support Windows clients, it is only for DOS clients. If you leave "
+"this option blank, your smb.conf will not be changed."
+msgstr ""
+
+#. Description
+#: ../samba-common.templates:24
+msgid "Modify smb.conf to use WINS settings from DHCP?"
+msgstr ""
+
+#. Description
+#: ../samba-common.templates:24
+msgid ""
+"If your computer gets IP address information from a DHCP server on the "
+"network, the DHCP server may also provide information about WINS servers "
+"(\"NetBIOS name servers\") present on the network. This requires a change "
+"to your smb.conf file so that DHCP-provided WINS settings will automatically "
+"be read from /etc/samba/dhcp.conf."
+msgstr ""
+
+#. Description
+#: ../samba-common.templates:24
+msgid ""
+"You must have the dhcp3-client package installed to take advantage of this "
+"feature."
+msgstr ""
+
+#. Description
+#: ../samba-common.templates:37
+msgid "Configure smb.conf through debconf?"
+msgstr ""
+
+#. Description
+#: ../samba-common.templates:37
+msgid ""
+"The rest of the configuration of Samba deals with questions that affect "
+"parameters in /etc/samba/smb.conf, which is the file used to configure the "
+"Samba programs (nmbd and smbd.) Your current smb.conf contains an 'include' "
+"line or an option that spans multiple lines, which could confuse debconf and "
+"require you to edit your smb.conf by hand to get it working again."
+msgstr ""
+
+#. Description
+#: ../samba-common.templates:37
+msgid ""
+"If you don't use debconf to configure smb.conf, you will have to handle any "
+"configuration changes yourself, and will not be able to take advantage of "
+"periodic configuration enhancements. Therefore, use of debconf is "
+"recommended if possible."
+msgstr ""
+
+#. Description
+#: ../samba-common.templates:52
+msgid "Workgroup/Domain Name?"
+msgstr ""
+
+#. Description
+#: ../samba-common.templates:52
+msgid ""
+"This controls what workgroup your server will appear to be in when queried "
+"by clients. Note that this parameter also controls the Domain name used with "
+"the security=domain setting."
+msgstr ""
+
+#. Description
+#: ../samba-common.templates:60
+msgid "Use password encryption?"
+msgstr ""
+
+#. Description
+#: ../samba-common.templates:60
+msgid ""
+"Recent Windows clients communicate with SMB servers using encrypted "
+"passwords. If you want to use clear text passwords you will need to change a "
+"parameter in your Windows registry. It is recommended that you use encrypted "
+"passwords. If you do, make sure you have a valid /etc/samba/smbpasswd file "
+"and that you set passwords in there for each user using the smbpasswd "
+"command."
+msgstr ""
+
+#. Description
+#: ../samba.templates:4
+msgid "Create samba password database, /var/lib/samba/passdb.tdb?"
+msgstr ""
+
+#. Description
+#: ../samba.templates:4
+msgid ""
+"To be compatible with the defaults in most versions of Windows, Samba must "
+"be configured to use encrypted passwords. This requires user passwords to "
+"be stored in a file separate from /etc/passwd. This file can be created "
+"automatically, but the passwords must be added manually (by you or the user) "
+"by running smbpasswd, and you must arrange to keep it up-to-date in the "
+"future. If you do not create it, you will have to reconfigure samba (and "
+"probably your client machines) to use plaintext passwords. See /usr/share/"
+"doc/samba-doc/htmldocs/ENCRYPTION.html from the samba-doc package for more "
+"details."
+msgstr ""
+
+#. Description
+#: ../samba.templates:17
+msgid "Samba's log files have moved."
+msgstr ""
+
+#. Description
+#: ../samba.templates:17
+msgid ""
+"Starting with the first packages of Samba 2.2 for Debian the log files for "
+"both Samba daemons (nmbd and smbd) are now stored in /var/log/samba/. The "
+"names of the files are log.nmbd and log.smbd, for nmbd and smbd respectively."
+msgstr ""
+
+#. Description
+#: ../samba.templates:17
+msgid ""
+"The old log files that were in /var/log/ will be moved to the new location "
+"for you."
+msgstr ""
+
+#. Description
+#: ../samba.templates:28
+msgid "Running nmbd from inetd is no longer supported"
+msgstr ""
+
+#. Description
+#: ../samba.templates:28
+msgid ""
+"Your system was previously configured to start nmbd and smbd from inetd. As "
+"of version 2.999+3.0.alpha20-4, nmbd will no longer be started from inetd. "
+"If you have modified your /etc/init.d/samba startup script, you may need to "
+"adjust it by hand now so that nmbd will start."
+msgstr ""
+
+#. Choices
+#: ../samba.templates:36
+msgid "daemons, inetd"
+msgstr ""
+
+#. Description
+#: ../samba.templates:38
+msgid "How do you want to run Samba?"
+msgstr ""
+
+#. Description
+#: ../samba.templates:38
+msgid ""
+"The Samba daemon smbd can run as a normal daemon or from inetd. Running as a "
+"daemon is the recommended approach."
+msgstr ""
+
+#. Description
+#: ../samba.templates:45
+msgid "Move /etc/samba/smbpasswd to /var/lib/samba/passdb.tdb?"
+msgstr ""
+
+#. Description
+#: ../samba.templates:45
+msgid ""
+"Samba 3.0 introduces a newer, more complete SAM database interface which "
+"supersedes the /etc/samba/smbpasswd file. Would you like your existing "
+"smbpasswd file to be migrated to /var/lib/samba/passdb.tdb for you? If you "
+"plan to use another pdb backend (e.g., LDAP) instead, you should answer 'no' "
+"here."
+msgstr ""
+
+#. Description
+#: ../swat.templates:3
+msgid "Your smb.conf will be re-written!"
+msgstr ""
+
+#. Description
+#: ../swat.templates:3
+msgid ""
+"SWAT will rewrite your smb.conf file. It will rearrange the entries and "
+"delete all comments, include= and copy= options. If you have a carefully "
+"crafted smb.conf then back it up or don't use SWAT!"
+msgstr ""
diff --git a/packaging/Debian/debian/python2.3-samba.files b/packaging/Debian/debian/python2.3-samba.files
new file mode 100644
index 00000000000..82759d81b89
--- /dev/null
+++ b/packaging/Debian/debian/python2.3-samba.files
@@ -0,0 +1 @@
+usr/lib/python2.3/site-packages/
diff --git a/packaging/Debian/debian/rules b/packaging/Debian/debian/rules
new file mode 100755
index 00000000000..73e5d16bc19
--- /dev/null
+++ b/packaging/Debian/debian/rules
@@ -0,0 +1,316 @@
+#!/usr/bin/make -f
+#
+# Important modifications (introduction of a saved config.cache to
+# solve build problems) introduced in Samba 2.2.1a-5. These
+# modification were made by Steve Langasek <vorlon@netexpress.net>.
+#
+#
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+# This is the debhelper compatability version to use.
+export DH_COMPAT=4
+
+# This has to be exported to make some magic below work.
+export DH_OPTIONS
+
+# Set the host and build architectures for use with config.cache loading,
+# cross-building, etc.
+DEB_HOST_GNU_TYPE := $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
+DEB_BUILD_GNU_TYPE := $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
+
+export DEB_HOST_GNU_TYPE
+export DEB_BUILD_GNU_TYPE
+
+
+# Support the DEB_BUILD_OPTIONS variable
+CFLAGS = -gstabs -Wall
+INSTALL = install
+
+ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
+ CFLAGS += -O0
+else
+ CFLAGS += -O2
+endif
+
+ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
+ INSTALL += -s
+endif
+
+
+DESTDIR=`pwd`/debian/tmp
+SWATDIR=`pwd`/debian/swat
+
+IVARS = BASEDIR=$(DESTDIR)/usr \
+ prefix=$(DESTDIR)/usr \
+ BINDIR=$(DESTDIR)/usr/bin \
+ SBINDIR=$(DESTDIR)/usr/sbin \
+ MANDIR=$(DESTDIR)/usr/share/man \
+ LIBDIR=$(DESTDIR)/usr/lib/samba \
+ VARDIR=$(DESTDIR)/var \
+ INCLUDEDIR=$(DESTDIR)/usr/include \
+ SWATDIR=$(SWATDIR)/usr/share/samba/swat \
+ CODEPAGEDIR=$(DESTDIR)/usr/share/samba/ \
+ PRIVATEDIR=$(DESTDIR)/etc/samba \
+ CONFIGDIR=$(DESTDIR)/etc/samba
+
+patch: patch-stamp
+patch-stamp:
+ dh_testdir
+ if [ ! -f patch-stamp ]; then /bin/sh debian/scripts/patch-source; fi
+ touch patch-stamp
+
+unpatch:
+ dh_testdir
+ if [ -f patch-stamp ]; then /bin/sh debian/scripts/unpatch-source; fi
+ rm -f patch-stamp
+
+configure: patch-stamp configure-stamp
+configure-stamp:
+ dh_testdir
+
+ if [ -f debian/config.cache ]; then \
+ cp -f debian/config.cache source/config.cache; \
+ fi
+
+ [ -f source/Makefile ] || (cd source && CFLAGS="$(CFLAGS)" ./configure \
+ --host=$(DEB_HOST_GNU_TYPE) \
+ --build=$(DEB_BUILD_GNU_TYPE) \
+ --cache-file=./config.cache \
+ --with-fhs \
+ --enable-shared \
+ --enable-static \
+ --prefix=/usr \
+ --sysconfdir=/etc \
+ --libdir=/etc/samba \
+ --with-privatedir=/etc/samba \
+ --localstatedir=/var \
+ --with-netatalk \
+ --with-smbmount \
+ --with-pam \
+ --with-syslog \
+ --with-utmp \
+ --with-readline \
+ --with-pam_smbpass \
+ --with-libsmbclient \
+ --with-winbind \
+ --with-msdfs \
+ --with-automount \
+ --with-acl-support \
+ --with-tdbsam \
+ --with-ldap \
+ --with-python=python2.3)
+
+ touch configure-stamp
+
+build: patch-stamp configure-stamp build-stamp
+build-stamp:
+ dh_testdir
+
+ $(MAKE) -C source headers
+ $(MAKE) -C source all nsswitch/libnss_wins.so python_ext
+
+ touch build-stamp
+
+clean: unpatch
+ dh_testdir
+ dh_testroot
+ rm -f build-stamp configure-stamp
+
+ # Clean first the Samba package
+# -$(MAKE) -C source realclean
+# -$(MAKE) -C source clean
+ -$(MAKE) -C source python_clean distclean
+
+ # Delete stuff left after a build that is not deleted by 'make clean'
+ rm -f source/bin/wbinfo source/bin/winbindd source/bin/debug2html \
+ source/bin/libsmbclient.a source/include/stamp-h
+
+ dh_clean
+
+install: DH_OPTIONS=
+install: build
+ dh_testdir
+ dh_testroot
+ dh_clean -k
+ dh_installdirs
+
+ mkdir -p $(DESTDIR)/usr/share/man $(DESTDIR)/usr/lib \
+ $(DESTDIR)/lib/security $(DESTDIR)/sbin \
+ $(DESTDIR)/usr/lib/cups/backend $(DESTDIR)/usr/share/samba \
+ $(DESTDIR)/etc/pam.d $(DESTDIR)/etc/dhcp3/dhclient-enter-hooks.d \
+ $(DESTDIR)/usr/lib/python2.3/site-packages
+
+ # Add here commands to install the package into debian/tmp.
+ $(MAKE) -C source install $(IVARS)
+
+ # libsmbclient files are not installed by the standard
+ # 'make install' - do it manually.
+ $(MAKE) -C source installclientlib $(IVARS)
+ mv $(DESTDIR)/usr/lib/libsmbclient.so $(DESTDIR)/usr/lib/libsmbclient.so.0.1
+ ln -s libsmbclient.so.0.1 $(DESTDIR)/usr/lib/libsmbclient.so.0
+ ln -s libsmbclient.so.0.1 $(DESTDIR)/usr/lib/libsmbclient.so
+
+ # Install other stuff not installed by "make install"
+ install -m 0755 debian/mksmbpasswd.awk $(DESTDIR)/usr/sbin/mksmbpasswd
+
+ # Install winbind stuff not installed by 'make install'
+ install -m 0644 source/nsswitch/libnss_winbind.so \
+ $(DESTDIR)/lib/libnss_winbind.so.2
+ install -m 0644 source/nsswitch/pam_winbind.so \
+ $(DESTDIR)/lib/security/
+
+ # Install libnss_wins.so, which is not installed by 'make install' either.
+ install -m 0644 source/nsswitch/libnss_wins.so \
+ $(DESTDIR)/lib/libnss_wins.so.2
+
+ # pam_smbpass.so isn't being installed by 'make install'.
+ # We'll move it here to $(DESTDIR)/lib/security/ and then
+ # libpam-smbpass.files will make dh_movefiles move it to the
+ # right location in the libpam-smbpass package.
+ install -m 0644 source/bin/pam_smbpass.so $(DESTDIR)/lib/security/
+
+ # Create the symlink that will allow us to do "mount -t smbfs ...".
+ # Create also a symlink that will allow "mount -t smb ..." to
+ # work too. The symlink is created in $(DESTDIR)/sbin/ but
+ # will be moved by dh_movefiles to the smbfs package later on.
+ ln -s /usr/bin/smbmount $(DESTDIR)/sbin/mount.smbfs
+ ln -s /usr/bin/smbmount $(DESTDIR)/sbin/mount.smb
+ ln -s smbmount.8 $(DESTDIR)/usr/share/man/man8/mount.smb.8
+ ln -s smbmount.8 $(DESTDIR)/usr/share/man/man8/mount.smbfs.8
+
+ # For CUPS to support printing to samba printers, it's necessary
+ # to make the following symlink (according to
+ # Erich Schubert <debian@vitavonni.de> in #109509):
+ ln -s ../../../bin/smbspool $(DESTDIR)/usr/lib/cups/backend/smb
+
+ # Install man pages for files without man pages in the upstream sources
+ install -m 0644 debian/mksmbpasswd.8 $(DESTDIR)/usr/share/man/man8/mksmbpasswd.8
+
+ # Delete unwanted stuff leftover from "make install"
+
+ # The smbwrapper package is not being generated anymore, so we must
+ # delete the related man pages.
+ rm $(DESTDIR)/usr/share/man/man1/smbsh.1
+
+ # We're not providing findsmb (should we?) so let's remove the man
+ # pages.
+ find debian/ -name 'findsmb*' -exec rm -f {} \;
+
+ # Install samba-common's conffiles - they'll get moved later to their
+ # correct place by dh_movefiles.
+ cp debian/smb.conf $(DESTDIR)/usr/share/samba/
+ install -m755 debian/panic-action $(DESTDIR)/usr/share/samba/
+ cp debian/gdbcommands $(DESTDIR)/etc/samba/
+ cp debian/samba.pamd $(DESTDIR)/etc/pam.d/samba
+ install -m755 debian/samba-common.dhcp $(DESTDIR)/etc/dhcp3/dhclient-enter-hooks.d/samba
+
+ # Install the Python modules
+ #
+ # Hmmm... need to figure this out. We have lib.linux-i686-2.2
+ # and lib.linux-i686-2.3 directories. Using only the stuff from
+ # the 2.3 directory for now. peloy.-
+ #cp source/build/lib.*/samba/*.so $(DESTDIR)/usr/lib/python2.3/site-packages/
+ cp source/build/lib.linux-*-2.3/samba/*.so $(DESTDIR)/usr/lib/python2.3/site-packages/
+
+ dh_movefiles
+
+# Build architecture-independent files here.
+# Pass -i to all debhelper commands in this target to reduce clutter.
+binary-indep: DH_OPTIONS=-i
+binary-indep: build install
+ dh_testdir
+ dh_testroot
+ dh_installdebconf
+ dh_installdocs -A debian/README.build
+ # dh_installexamples is not available in Debian Potato...
+ [ -x /usr/bin/dh_installexamples ] && DH_OPTIONS= dh_installexamples -v -psamba-doc examples/*
+# dh_installmenu
+# dh_installemacsen
+# dh_installpam
+# dh_installinit
+# dh_installcron
+# dh_installmanpages
+# dh_installinfo
+# dh_undocumented
+ dh_installchangelogs
+ dh_link
+ dh_compress
+ dh_fixperms
+
+ # Get rid of those pesky .cvsignore files to make lintian happy
+ find debian/ -name .cvsignore -exec rm -f {} \;
+
+ dh_installdeb
+# dh_perl
+ dh_gencontrol
+ dh_md5sums
+ dh_builddeb
+
+# Build architecture-dependent files here.
+# Pass -a to all debhelper commands in this target to reduce clutter.
+binary-arch: DH_OPTIONS=-a
+binary-arch: build install
+ dh_testdir
+ dh_testroot
+ dh_installdebconf
+ dh_installdocs -A debian/README.build
+ # dh_installexamples is not available in Debian Potato...
+ [ -x /usr/bin/dh_installexamples ] && DH_OPTIONS= dh_installexamples -v -ppython2.3-samba source/python/examples/*
+# dh_installmenu
+ # dh_installlogrotate is not available in Debian Potato...
+ if [ -x /usr/bin/dh_installlogrotate ]; then \
+ dh_installlogrotate; \
+ else \
+ mkdir -p debian/samba/etc/logrotate.d; \
+ cp debian/samba.logrotate debian/samba/etc/logrotate.d/samba; \
+ mkdir -p debian/winbind/etc/logrotate.d; \
+ cp debian/winbind.logrotate debian/winbind/etc/logrotate.d/winbind; \
+ fi
+# dh_installemacsen
+# dh_installpam
+ DH_OPTIONS= dh_installinit -psamba -- "defaults 20 19"
+ DH_OPTIONS= dh_installinit -pwinbind
+ dh_installcron
+# dh_installmanpages
+# dh_installinfo
+ cp debian/winbind.lintian debian/winbind/usr/share/lintian/overrides/winbind
+# dh_undocumented
+ dh_installchangelogs -Nlibpam-smbpass
+ DH_OPTIONS= dh_installchangelogs -plibpam-smbpass source/pam_smbpass/CHANGELOG
+ dh_strip
+ dh_link
+ dh_compress
+ dh_fixperms
+
+ # Why this is executable, I have NO idea...
+ chmod a-x debian/libsmbclient-dev/usr/include/libsmbclient.h
+
+ # You may want to make some executables suid here.
+ # The smbmnt and smbumount binaries should be setuid-root. This
+ # has security implications because these programs haven't had
+ # a thorough security audit. smbmount _does not_ have to have
+ # the setuid bit set. In fact, it is a security hole.
+ chmod u+s debian/smbfs/usr/bin/smbmnt
+ chmod u+s debian/smbfs/usr/bin/smbumount
+
+ # Set some reasonable default perms for the samba logdir.
+ chmod 0750 debian/samba/var/log/samba/
+ chown root.adm debian/samba/var/log/samba/
+
+ # Get rid of those pesky .cvsignore files to make lintian happy
+ # (maybe we only need the "find ... -exec rm -f {} ;" we have
+ # in the binary-indep target?) peloy.-
+ find debian/ -name .cvsignore -exec rm -f {} \;
+
+ dh_installdeb
+# dh_makeshlibs
+# dh_perl
+ dh_shlibdeps
+ dh_gencontrol
+ dh_md5sums
+ dh_builddeb
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install configure
diff --git a/packaging/Debian/debian/samba-common.config b/packaging/Debian/debian/samba-common.config
new file mode 100644
index 00000000000..ed76b95cb84
--- /dev/null
+++ b/packaging/Debian/debian/samba-common.config
@@ -0,0 +1,154 @@
+#/bin/sh -e
+
+# Source debconf library.
+. /usr/share/debconf/confmodule
+
+# Function for grabbing a parameter from an smb.conf file
+smbconf_retr() {
+ if [ -z "$1" ]; then
+ return
+ fi
+
+ if [ -n "$2" ]; then
+ local FILE="$2"
+ fi
+
+ if [ -z "$FILE" ]; then
+ return
+ fi
+
+ sed -n -e"
+ s/^[[:space:]]*\[global\]/\[global\]/i
+ /^\[global\]/,/^[[:space:]]*\[/ {
+ s/^[[:space:]]*$1[[:space:]]*=[[:space:]]*//pi
+ }" $FILE \
+ | tail -1
+}
+
+FILE=/etc/samba/smb.conf
+
+db_title "Samba Server"
+
+# We ask the question IFF the config contains complex options that could
+# cause us to break the config.
+if [ -f "$FILE" ] && grep -v dhcp.conf $FILE \
+ | grep -qEi '\\$|^[[:space:]]*include[[:space:]]*='
+then
+ db_input high samba-common/do_debconf || true
+ db_go
+else
+ db_set samba-common/do_debconf true
+fi
+
+# If user doesn't want to use debconf to configure Samba the leave...
+db_get samba-common/do_debconf || true
+if [ "${RET}" = "false" ]; then
+ exit 0
+fi
+
+# User wants to use debconf, let's continue...
+
+# Adjust priority of the question about the workgroup name depending
+# on whether a workgroup name has already being specified.
+db_get samba-common/workgroup || true
+if [ "${RET}" ]; then
+ WGPRIORITY=medium
+else
+ WGPRIORITY=high
+fi
+
+# Preload any values from the existing smb.conf file
+if [ -f $FILE ]; then
+ WORKGROUP=`smbconf_retr workgroup`
+ if [ "$WORKGROUP" ]; then
+ db_set samba-common/workgroup "$WORKGROUP"
+ fi
+
+ ENCRYPT=`smbconf_retr "encrypt passwords"`
+ if [ "$ENCRYPT" ]; then
+ ENCRYPT=`echo $ENCRYPT | tr '[A-Z]' '[a-z]'`
+ if [ "$ENCRYPT" = "yes" ]; then
+ ENCRYPT=true
+ elif [ "$ENCRYPT" = "no" ]; then
+ ENCRYPT=false
+ fi
+
+ db_set samba-common/encrypt_passwords "$ENCRYPT"
+ fi
+
+ CHARSET=`smbconf_retr "character set"`
+ CODEPAGE=`smbconf_retr "client code page"`
+ UNIXCHARSET=`smbconf_retr "unix charset"`
+ DOSCHARSET=`smbconf_retr "dos charset"`
+
+ # If we're upgrading from an old version and there's no
+ # 'passdb backend' setting, add one.
+ if [ "$1" = "configure" -a -n "$2" ] \
+ && dpkg --compare-versions "$2" lt 2.99.cvs.20020713-2 \
+ && ! grep -q -i '^[[:space:]]*passdb backend[[:space:]]*=' $FILE
+ then
+ TMPFILE=/etc/samba/smb.conf.dpkg-tmp
+ sed -e'
+ s/^\([[:space:]]*\)\[global\]/\1\[global\]/i
+ s/^\([[:space:]]*\)encrypt passwords/\1encrypt passwords/i
+ /^[[:space:]]*\[global\]/,/^[[:space:]]*\[/ {
+ /^[[:space:]]*encrypt passwords[[:space:]]*=/a \
+ passdb backend = smbpasswd guest
+ }' < $FILE > ${TMPFILE}
+ chmod a+r ${TMPFILE}
+ mv -f ${TMPFILE} /etc/samba/smb.conf
+ fi
+fi
+
+# Get workgroup name
+db_input $WGPRIORITY samba-common/workgroup || true
+db_go
+
+# Use encrypted passwords?
+db_input medium samba-common/encrypt_passwords || true
+db_go
+
+# Handle migrating character sets
+if [ -n "$CHARSET" -a -z "$UNIXCHARSET" ]
+then
+ UNIXCHARSET=`echo $CHARSET | sed -e's/iso-/ISO/i'`
+ db_set samba-common/character_set "$UNIXCHARSET"
+ # FIXME: should eventually be low.
+ db_input medium samba-common/character_set || true
+ db_go
+fi
+
+if [ -n "$CODEPAGE" -a -z "$DOSCHARSET" ]
+then
+ DOSCHARSET=CP`echo $CODEPAGE | sed -e's/[[:alpha:]]*//g'`
+ db_set samba-common/codepage "$DOSCHARSET"
+ # FIXME: should eventually be low.
+ db_input medium samba-common/codepage || true
+ db_go
+fi
+
+DHCPPRIORITY=medium
+#if [ "$DEBCONF_RECONFIGURE" = 1 ] && [ -f /sbin/dhclient3 ]
+if [ -f /sbin/dhclient3 ]
+then
+ DHCPPRIORITY=high
+# TODO: see if we can detect that dhcp3-client is *going* to be installed,
+# even if it isn't yet.
+#elif dpkg-query -W --showformat='${Status}\n' dhcp3-client | grep ???
+# unknown ok not-installed ?
+# DHCPPRIORITY=high
+fi
+
+if [ ! -f $FILE ] || grep -q -i 'wins server' $FILE
+then
+ # check the values before and after; unset the 'applied' flag
+ # if they don't match.
+ db_get samba-common/dhcp || true
+ OLDDHCP="$RET"
+ db_input $DHCPPRIORITY samba-common/dhcp || true
+ db_go
+ db_get samba-common/dhcp || true
+ if [ "$OLDDHCP" != "$RET" ]; then
+ db_fset samba-common/dhcp applied false
+ fi
+fi
diff --git a/packaging/Debian/debian/samba-common.dhcp b/packaging/Debian/debian/samba-common.dhcp
new file mode 100644
index 00000000000..3b2fa4ba191
--- /dev/null
+++ b/packaging/Debian/debian/samba-common.dhcp
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+netbios_setup() {
+ # No need to continue if we're called with an unsupported option
+
+ if [ "$reason" != BOUND ] && [ "$reason" != RENEW ] \
+ && [ "$reason" != REBIND ] && [ "$reason" != REBOOT ] \
+ && [ "$reason" != EXPIRE ] && [ "$reason" != FAIL ]
+ then
+ return
+ fi
+
+ umask 022
+
+ if [ -z "$new_netbios_name_servers" ] || [ "$reason" = FAIL ] \
+ || [ "$reason" = EXPIRE ]
+ then
+ # FIXME: add sed magic to only remove wins servers
+ # associated with this interface
+ echo -n > /etc/samba/dhcp.conf
+ elif [ "$new_netbios_name_servers" != "$old_netbios_name_servers" ]
+ then
+ local serverlist=""
+ for server in $new_netbios_name_servers
+ do
+ serverlist="$serverlist $interface:$server"
+ done
+ # FIXME: add sed magic to only update wins servers
+ # associated with this interface
+ echo " wins server =$serverlist" > /etc/samba/dhcp.conf
+ fi
+}
+
+netbios_setup
diff --git a/packaging/Debian/debian/samba-common.dirs b/packaging/Debian/debian/samba-common.dirs
new file mode 100644
index 00000000000..c089ad73573
--- /dev/null
+++ b/packaging/Debian/debian/samba-common.dirs
@@ -0,0 +1,2 @@
+etc/samba
+etc/dhcp3/dhclient-enter-hooks.d
diff --git a/packaging/Debian/debian/samba-common.files b/packaging/Debian/debian/samba-common.files
new file mode 100644
index 00000000000..9fb3a3a1623
--- /dev/null
+++ b/packaging/Debian/debian/samba-common.files
@@ -0,0 +1,15 @@
+etc/samba/
+etc/dhcp3/
+etc/pam.d/
+usr/bin/net
+usr/bin/nmblookup
+usr/bin/smbpasswd
+usr/bin/testparm
+usr/share/man/man1/nmblookup.1
+usr/share/man/man1/testparm.1
+usr/share/man/man5/lmhosts.5
+usr/share/man/man5/smb.conf.5
+usr/share/man/man7/samba.7
+usr/share/man/man8/net.8
+usr/share/man/man8/smbpasswd.8
+usr/share/samba/
diff --git a/packaging/Debian/debian/samba-common.postinst b/packaging/Debian/debian/samba-common.postinst
new file mode 100644
index 00000000000..6c6eb9bf537
--- /dev/null
+++ b/packaging/Debian/debian/samba-common.postinst
@@ -0,0 +1,139 @@
+#!/bin/sh
+#
+#
+
+set -e
+
+# Do debconf stuff here
+. /usr/share/debconf/confmodule
+
+# We need a default smb.conf file. If one doesn't exist we put in place
+# one that has some basic defaults.
+if [ ! -e /etc/samba/smb.conf ]; then
+ cp -a /usr/share/samba/smb.conf /etc/samba/
+fi
+
+# Static tempfile location, dpkg-style
+TMPFILE=/etc/samba/smb.conf.dpkg-tmp
+
+# ------------------------- Debconf questions start ---------------------
+
+# Is the user configuring with debconf, or he/she prefers swat/manual
+# config?
+db_get samba-common/do_debconf || true
+if [ "${RET}" = "true" ]; then
+ # Get workgroup name
+ db_get samba-common/workgroup || true
+ WORKGROUP="${RET}"
+
+ # Oh my GOD, this is ugly. Why would anyone put these
+ # characters in a workgroup name? Why, Lord, why???
+ WORKGROUP=`echo $WORKGROUP | \
+ sed -e's/\\\\/\\\\\\\\/g
+ s#/#\\\\/#g
+ s/&/\\\&/g
+ s/\\\$/\\\\\\\$/g'`
+
+ sed -e "s/^\([[:space:]]*\)\[global\]/\1\[global\]/i
+ /^[[:space:]]*\[global\]/,/^[[:space:]]*\[/ \
+ s/^\([[:space:]]*\)workgroup[[:space:]]*=.*/\1workgroup = ${WORKGROUP}/i" \
+ < /etc/samba/smb.conf >${TMPFILE}
+ mv -f ${TMPFILE} /etc/samba/smb.conf
+
+ # Encrypt passwords?
+ db_get samba-common/encrypt_passwords || true
+ ENCRYPT_PASSWORDS="${RET}"
+
+ sed -e "s/^\([[:space:]]*\)\[global\]/\1\[global\]/i
+ /^[[:space:]]*\[global\]/,/^[[:space:]]*\[/ \
+ s/^\([[:space:]]*\)encrypt passwords[[:space:]]*=.*/\1encrypt passwords = ${ENCRYPT_PASSWORDS}/i" \
+ < /etc/samba/smb.conf >${TMPFILE}
+ mv -f ${TMPFILE} /etc/samba/smb.conf
+
+ # Install DHCP support
+ db_get samba-common/dhcp && DHCPVAL="$RET"
+ db_fget samba-common/dhcp applied || true
+ if [ "$DHCPVAL" = true ] && [ "$RET" != true ] && \
+ ! grep -q dhcp.conf /etc/samba/smb.conf
+ then
+ sed -e "s/^\([[:space:]]*\)\[global\]/\1\[global\]/i
+ /^[[:space:]]*\[global\]/,/^[[:space:]]*\[/ {
+ /wins server[[:space:]]*=/a \\
+\\
+# If we receive WINS server info from DHCP, override the options above. \\
+ include = /etc/samba/dhcp.conf
+}" < /etc/samba/smb.conf > ${TMPFILE}
+ mv -f ${TMPFILE} /etc/samba/smb.conf
+ elif [ "$RET" != true ] && grep -q dhcp.conf /etc/samba/smb.conf
+ then
+ :
+ # FIXME: here we /delete/ the lines?
+ fi
+ # Once we get here, the config has been applied, whatever
+ # it is.
+ if [ "$RET" != true ]; then
+ db_fset samba-common/dhcp applied true
+ fi
+
+ # Update charset settings?
+ if ! grep -q "^[[:space:]]*unix charset[[:space:]]*=" /etc/samba/smb.conf
+ then
+ db_get samba-common/character_set || true
+ UNIXCHARSET="${RET}"
+ if [ -n "$UNIXCHARSET" ]
+ then
+ sed -e "s/^\([[:space:]]*\)\[global\]/\1\[global\]/i
+ s/^\([[:space:]]*\)character set/\1character set/i
+ /^[[:space:]]*\[global\]/,/^[[:space:]]*\[/ {
+ /^[[:space:]]*character set[[:space:]]*=/c \\
+ unix charset = $UNIXCHARSET
+ }" < /etc/samba/smb.conf > ${TMPFILE}
+ mv -f ${TMPFILE} /etc/samba/smb.conf
+ fi
+ fi
+
+ if grep -qi "^[[:space:]]*passdb backend[[:space:]]*=.*unixsam" /etc/samba/smb.conf
+ then
+ sed -e 's/^\([[:space:]]*\)passdb backend/\1passdb backend/i
+ /^[[:space:]]*passdb backend/ {
+ s/unixsam/guest/i
+ }' < /etc/samba/smb.conf > ${TMPFILE}
+ mv -f ${TMPFILE} /etc/samba/smb.conf
+ fi
+
+ if ! grep -q "^[[:space:]]*dos charset[[:space:]]*=" /etc/samba/smb.conf
+ then
+ db_get samba-common/codepage || true
+ DOSCHARSET="${RET}"
+ if [ -n "$DOSCHARSET" ]
+ then
+ sed -e "s/^\([[:space:]]*\)\[global\]/\1\[global\]/i
+ s/^\([[:space:]]*\)client code page/\1client code page/i
+ /^[[:space:]]*\[global\]/,/^[[:space:]]*\[/ {
+ /^[[:space:]]*client code page[[:space:]]*=/c \\
+ dos charset = $DOSCHARSET
+}" < /etc/samba/smb.conf > ${TMPFILE}
+ mv -f ${TMPFILE} /etc/samba/smb.conf
+ fi
+ fi
+
+ if dpkg --compare-versions "$2" lt 2.999+3.0.alpha20-4 \
+ && ! grep -q "^[[:space:]]*panic action[[:space:]]*=" /etc/samba/smb.conf
+ then
+ sed -e "s/^\([[:space:]]*\)\[global\]/\1\[global\]/i
+ /^[[:space:]]*\[global\]/a \\
+\\
+# Do something sensible when Samba crashes: mail the admin a backtrace\\
+ panic action = /usr/share/samba/panic-action %d" < /etc/samba/smb.conf > ${TMPFILE}
+ mv -f ${TMPFILE} /etc/samba/smb.conf
+ fi
+
+fi
+
+chmod a+r /etc/samba/smb.conf
+
+# ------------------------- Debconf questions end ---------------------
+
+db_stop
+
+#DEBHELPER#
diff --git a/packaging/Debian/debian/samba-common.postrm b/packaging/Debian/debian/samba-common.postrm
new file mode 100644
index 00000000000..8a4b6d3d55e
--- /dev/null
+++ b/packaging/Debian/debian/samba-common.postrm
@@ -0,0 +1,10 @@
+#!/bin/sh
+#
+#
+
+if [ "$1" = purge ]; then
+ # Remove any files left in /etc/samba/
+ rm -Rf /etc/samba/
+fi
+
+#DEBHELPER#
diff --git a/packaging/Debian/debian/samba-common.templates b/packaging/Debian/debian/samba-common.templates
new file mode 100644
index 00000000000..e5f7b1ae0cc
--- /dev/null
+++ b/packaging/Debian/debian/samba-common.templates
@@ -0,0 +1,66 @@
+Template: samba-common/character_set
+Type: string
+_Description: Character Set for Unix filesystem
+ You currently have a "character set" configured in your smb.conf. In
+ Samba 3.0, this option is replaced by a new option, "unix charset".
+ Please specify the character set you wish to use for theis new option,
+ which controls how Samba interprets filenames on the file system.
+ .
+ If you leave this option blank, your smb.conf will not be changed.
+
+Template: samba-common/codepage
+Type: string
+_Description: Character Set for DOS clients
+ You currently have a "client code page" set in your smb.conf. In Samba
+ 3.0, this option is replaced by the option "dos charset". Please specify
+ the character set you wish to use for this new option. In most cases, the
+ default chosen for you will be sufficient. Note that this option is not
+ needed to support Windows clients, it is only for DOS clients. If you
+ leave this option blank, your smb.conf will not be changed.
+
+Template: samba-common/dhcp
+Type: boolean
+Default: false
+_Description: Modify smb.conf to use WINS settings from DHCP?
+ If your computer gets IP address information from a DHCP server on the
+ network, the DHCP server may also provide information about WINS servers
+ ("NetBIOS name servers") present on the network. This requires a
+ change to your smb.conf file so that DHCP-provided WINS settings will
+ automatically be read from /etc/samba/dhcp.conf.
+ .
+ You must have the dhcp3-client package installed to take advantage of this
+ feature.
+
+Template: samba-common/do_debconf
+Type: boolean
+Default: true
+_Description: Configure smb.conf through debconf?
+ The rest of the configuration of Samba deals with questions that affect
+ parameters in /etc/samba/smb.conf, which is the file used to configure the
+ Samba programs (nmbd and smbd.) Your current smb.conf contains an
+ 'include' line or an option that spans multiple lines, which could confuse
+ debconf and require you to edit your smb.conf by hand to get it working
+ again.
+ .
+ If you don't use debconf to configure smb.conf, you will have to handle
+ any configuration changes yourself, and will not be able to take
+ advantage of periodic configuration enhancements. Therefore, use of
+ debconf is recommended if possible.
+
+Template: samba-common/workgroup
+Type: string
+_Description: Workgroup/Domain Name?
+ This controls what workgroup your server will appear to be in when queried
+ by clients. Note that this parameter also controls the Domain name used
+ with the security=domain setting.
+
+Template: samba-common/encrypt_passwords
+Type: boolean
+Default: true
+_Description: Use password encryption?
+ Recent Windows clients communicate with SMB servers using encrypted
+ passwords. If you want to use clear text passwords you will need to change
+ a parameter in your Windows registry. It is recommended that you use
+ encrypted passwords. If you do, make sure you have a valid
+ /etc/samba/smbpasswd file and that you set passwords in there for each
+ user using the smbpasswd command.
diff --git a/packaging/Debian/debian/samba-doc.docs b/packaging/Debian/debian/samba-doc.docs
new file mode 100644
index 00000000000..4d2cec2ee22
--- /dev/null
+++ b/packaging/Debian/debian/samba-doc.docs
@@ -0,0 +1,7 @@
+README
+docs/Samba-HOWTO-Collection.pdf
+docs/THANKS
+docs/history
+docs/faq/
+docs/htmldocs/
+docs/Registry/
diff --git a/packaging/Debian/debian/samba-doc.examples b/packaging/Debian/debian/samba-doc.examples
new file mode 100644
index 00000000000..e71180364cf
--- /dev/null
+++ b/packaging/Debian/debian/samba-doc.examples
@@ -0,0 +1,2 @@
+debian/wins2dns.awk
+source/smbadduser
diff --git a/packaging/Debian/debian/samba.config b/packaging/Debian/debian/samba.config
new file mode 100644
index 00000000000..89792d436e4
--- /dev/null
+++ b/packaging/Debian/debian/samba.config
@@ -0,0 +1,92 @@
+#/bin/sh -e
+#
+#
+
+# Source debconf library.
+. /usr/share/debconf/confmodule
+
+# Function for grabbing a parameter from an smb.conf file
+smbconf_retr() {
+ if [ -z "$1" ]; then
+ return
+ fi
+
+ if [ -n "$2" ]; then
+ local FILE="$2"
+ fi
+
+ if [ -z "$FILE" ]; then
+ return
+ fi
+
+ sed -n -e"
+ s/^[[:space:]]*\[global\]/\[global\]/i
+ /^\[global\]/,/^[[:space:]]*\[/ {
+ s/^[[:space:]]*$1[[:space:]]*=[[:space:]]*//pi
+ }" $FILE \
+ | tail -1
+}
+
+FILE=/etc/samba/smb.conf
+
+db_title "Samba Server"
+
+# Babysit users who don't read README.Debian
+if [ -n "$2" ] && dpkg --compare-versions "$2" lt "2.2"
+then
+ db_input medium samba/log_files_moved || true
+ db_go
+fi
+
+db_input medium samba/run_mode || true
+db_go
+
+
+# Offer to move the password database for existing users
+if [ "$1" = "configure" -a -n "$2" -a -e /etc/samba/smbpasswd \
+ -a ! -e /var/lib/samba/passdb.tdb ] \
+ && dpkg --compare-versions "$2" lt 2.99.cvs.20020713-2
+then
+ FILE=/etc/samba/smb.conf
+ PASSDB=""
+ if [ -f "$FILE" ]; then
+ PASSDB=`smbconf_retr "passdb backend"`
+ fi
+ TDBPRIORITY=medium
+ if echo "$PASSDB" | grep -q ldapsam; then
+ TDBPRIORITY=low
+ fi
+ db_get samba-common/do_debconf || true
+ if [ "${RET}" = "false" ]; then
+ TDBPRIORITY=low
+ fi
+
+ db_input "$TDBPRIORITY" samba/tdbsam || true
+fi
+
+# We vary the priority of the next question depending on whether
+# the password database already exists...
+if [ -e /etc/samba/smbpasswd -o -e /var/lib/samba/passdb.tdb ]; then
+ PRIORITY="low"
+else
+ # If 'encrypt passwords' is true in smb.conf, and smbpasswd
+ # does not exist, default to yes here.
+ FILE=/etc/samba/smb.conf
+ if [ -f "$FILE" ]; then
+ ENCRYPT=`smbconf_retr "encrypt passwords"`
+ if [ "$ENCRYPT" ]; then
+ ENCRYPT=`echo $ENCRYPT | tr '[A-Z]' '[a-z]'`
+ if [ "$ENCRYPT" = "yes" ]; then
+ ENCRYPT=true
+ fi
+ if [ "$ENCRYPT" = "no" ]; then
+ ENCRYPT=false
+ fi
+ fi
+ db_set samba/generate_smbpasswd "$ENCRYPT"
+ fi
+ PRIORITY="medium"
+fi
+
+db_input $PRIORITY samba/generate_smbpasswd || true
+db_go
diff --git a/packaging/Debian/debian/samba.cron.daily b/packaging/Debian/debian/samba.cron.daily
new file mode 100644
index 00000000000..42fc98d8f6d
--- /dev/null
+++ b/packaging/Debian/debian/samba.cron.daily
@@ -0,0 +1,16 @@
+#!/bin/sh
+#
+# cron script to save a backup copy of /etc/samba/smbpasswd in /var/backups.
+#
+# Written by Eloy A. Paris <peloy@debian.org> for the Debian project.
+#
+
+BAK=/var/backups
+
+umask 022
+if cd $BAK; then
+ # Make sure /etc/samba/smbpasswd exists
+ if [ -f /etc/samba/smbpasswd ]; then
+ cmp -s smbpasswd.bak /etc/samba/smbpasswd || cp -p /etc/samba/smbpasswd smbpasswd.bak
+ fi
+fi
diff --git a/packaging/Debian/debian/samba.dirs b/packaging/Debian/debian/samba.dirs
new file mode 100644
index 00000000000..a58e4e98929
--- /dev/null
+++ b/packaging/Debian/debian/samba.dirs
@@ -0,0 +1,7 @@
+usr/bin
+usr/sbin
+var/log/samba
+var/lib/samba/printers/W32X86
+var/lib/samba/printers/WIN40
+var/run/samba
+var/cache/samba
diff --git a/packaging/Debian/debian/samba.docs b/packaging/Debian/debian/samba.docs
new file mode 100644
index 00000000000..b8cc5419fb1
--- /dev/null
+++ b/packaging/Debian/debian/samba.docs
@@ -0,0 +1,5 @@
+README
+Roadmap
+WHATSNEW.txt
+docs/htmldocs/diagnosis.html
+docs/README.ldap
diff --git a/packaging/Debian/debian/samba.files b/packaging/Debian/debian/samba.files
new file mode 100644
index 00000000000..f52e6c5e4f7
--- /dev/null
+++ b/packaging/Debian/debian/samba.files
@@ -0,0 +1,19 @@
+usr/bin/testprns
+usr/bin/smbstatus
+usr/bin/smbcontrol
+usr/bin/tdbbackup
+usr/bin/pdbedit
+usr/sbin/smbd
+usr/sbin/nmbd
+usr/sbin/mksmbpasswd
+usr/lib/samba/vfs
+usr/share/man/man1/smbcontrol.1
+usr/share/man/man1/smbstatus.1
+usr/share/man/man1/testprns.1
+usr/share/man/man5/smbpasswd.5
+usr/share/man/man8/nmbd.8
+usr/share/man/man8/pdbedit.8
+usr/share/man/man8/smbd.8
+usr/share/man/man8/mksmbpasswd.8
+usr/share/man/man8/tdbbackup.8
+
diff --git a/packaging/Debian/debian/samba.init b/packaging/Debian/debian/samba.init
new file mode 100644
index 00000000000..5d0f4671a00
--- /dev/null
+++ b/packaging/Debian/debian/samba.init
@@ -0,0 +1,83 @@
+#!/bin/sh
+#
+# Start/stops the Samba daemons (nmbd and smbd).
+#
+#
+
+# Defaults
+RUN_MODE="daemons"
+
+# Reads config file (will override defaults above)
+[ -r /etc/default/samba ] && . /etc/default/samba
+
+NMBDPID=/var/run/samba/nmbd.pid
+SMBDPID=/var/run/samba/smbd.pid
+
+# clear conflicting settings from the environment
+unset TMPDIR
+
+# See if the daemons are there
+test -x /usr/sbin/nmbd -a -x /usr/sbin/smbd || exit 0
+
+case "$1" in
+ start)
+ echo -n "Starting Samba daemons:"
+
+ echo -n " nmbd"
+ start-stop-daemon --start --quiet --exec /usr/sbin/nmbd -- -D
+
+ if [ "$RUN_MODE" != "inetd" ]; then
+ echo -n " smbd"
+ start-stop-daemon --start --quiet --exec /usr/sbin/smbd -- -D
+ fi
+
+ echo "."
+ ;;
+ stop)
+ echo -n "Stopping Samba daemons: "
+
+ start-stop-daemon --stop --quiet --pidfile $NMBDPID
+ # Wait a little and remove stale PID file
+ sleep 1
+ if [ -f $NMBDPID ] && ! ps h `cat $NMBDPID` > /dev/null
+ then
+ # Stale PID file (nmbd was succesfully stopped),
+ # remove it (should be removed by nmbd itself IMHO.)
+ rm -f $NMBDPID
+ fi
+ echo -n "nmbd"
+
+ if [ "$RUN_MODE" != "inetd" ]; then
+ start-stop-daemon --stop --quiet --pidfile $SMBDPID
+ # Wait a little and remove stale PID file
+ sleep 1
+ if [ -f $SMBDPID ] && ! ps h `cat $SMBDPID` > /dev/null
+ then
+ # Stale PID file (nmbd was succesfully stopped),
+ # remove it (should be removed by smbd itself IMHO.)
+ rm -f $SMBDPID
+ fi
+ echo -n " smbd"
+ fi
+
+ echo "."
+
+ ;;
+ reload)
+ echo -n "Reloading /etc/samba/smb.conf (smbd only)"
+ start-stop-daemon --stop --signal HUP --pidfile $SMBDPID
+
+ echo "."
+ ;;
+ restart|force-reload)
+ $0 stop
+ sleep 1
+ $0 start
+ ;;
+ *)
+ echo "Usage: /etc/init.d/samba {start|stop|reload|restart|force-reload}"
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/packaging/Debian/debian/samba.logrotate b/packaging/Debian/debian/samba.logrotate
new file mode 100644
index 00000000000..f90437bf2ad
--- /dev/null
+++ b/packaging/Debian/debian/samba.logrotate
@@ -0,0 +1,21 @@
+/var/log/samba/log.smbd {
+ weekly
+ missingok
+ rotate 7
+ postrotate
+ invoke-rc.d --quiet samba reload > /dev/null
+ endscript
+ compress
+ notifempty
+}
+
+/var/log/samba/log.nmbd {
+ weekly
+ missingok
+ rotate 7
+ postrotate
+ [ -f /var/run/samba/nmbd.pid ] && kill -HUP `cat /var/run/samba/nmbd.pid`
+ endscript
+ compress
+ notifempty
+}
diff --git a/packaging/Debian/debian/samba.pamd b/packaging/Debian/debian/samba.pamd
new file mode 100644
index 00000000000..e2c7a99356d
--- /dev/null
+++ b/packaging/Debian/debian/samba.pamd
@@ -0,0 +1,3 @@
+@include common-auth
+@include common-account
+@include common-session
diff --git a/packaging/Debian/debian/samba.postinst b/packaging/Debian/debian/samba.postinst
new file mode 100644
index 00000000000..1a25290ed27
--- /dev/null
+++ b/packaging/Debian/debian/samba.postinst
@@ -0,0 +1,228 @@
+#!/bin/sh -e
+#
+# Post-installation script for the Samba package for Debian GNU/Linux
+#
+#
+
+case "$1" in
+ configure)
+ # continue below
+ ;;
+
+ abort-upgrade|abort-remove|abort-deconfigure)
+ exit 0
+ ;;
+
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 0
+ ;;
+esac
+
+# Handle debconf
+. /usr/share/debconf/confmodule
+
+INITCONFFILE=/etc/default/samba
+
+# We generate several files during the postinst, and we don't want
+# them to be readable only by root.
+umask 022
+
+
+# Generate configuration file if it does not exist, using default values.
+[ -r "${INITCONFFILE}" ] || {
+ echo Generating ${INITCONFFILE}... >&2
+ cat >${INITCONFFILE} <<'EOFMAGICNUMBER1234'
+# Defaults for samba initscript
+# sourced by /etc/init.d/samba
+# installed at /etc/default/samba by the maintainer scripts
+#
+
+#
+# This is a POSIX shell fragment
+#
+
+# How should Samba (smbd) run? Possible values are "daemons"
+# or "inetd".
+RUN_MODE=""
+EOFMAGICNUMBER1234
+}
+
+# --- Begin of FHS migration code ---
+
+# Starting with Samba 2.2.3-4 the WINS database, the browse
+# database and other important run-time files are stored in
+# FHS-compliant directories. The following code takes care of
+# moving the files in the old directories (/var/samba/ and
+# /var/state/samba) to the new FHS-compliant directories.
+
+if [ -d /var/samba/ ]; then
+ mv /var/samba/* /var/lib/samba/ 2>/dev/null || true
+ rmdir /var/samba/
+fi
+
+# Default for anything we don't know about (see next two 'for' loops)
+# is /var/lib/samba -- guaranteed not to accidentally tromp on any
+# files the admin thought were safe.
+if [ -d /var/state/samba ]; then
+ mv /var/state/samba/* /var/lib/samba/ 2>/dev/null || true
+ rmdir /var/state/samba/
+
+ # It's not FHS, and it's probably our fault this is here,
+ # so delete it if we can.
+ rmdir /var/state/ 2> /dev/null || true
+fi
+
+# All these files are now placed in their respective FHS-compliant
+# directories. Separate out the individual files accordingly.
+for F in browse.dat printing.tdb winbindd_cache.tdb
+do
+ if [ -e /var/lib/samba/"$F" ]; then
+ mv /var/lib/samba/"$F" /var/cache/samba/
+ fi
+done
+
+for F in brlock.tdb connections.tdb locking.tdb messages.tdb nmbd.pid \
+ sessionid.tdb smbd.pid unexpected.tdb
+do
+ if [ -e /var/lib/samba/"$F" ]; then
+ mv /var/lib/samba/"$F" /var/run/samba/
+ fi
+done
+
+# Beginning with Samba 2.2.5-1, we also move the domain secrets file
+# to a more suitable location, since no one really edits this by hand.
+if [ -e /etc/samba/secrets.tdb -a ! -e /var/lib/samba/secrets.tdb ]
+then
+ mv /etc/samba/secrets.tdb /var/lib/samba/
+fi
+
+# If upgrading from a previous 2.999 snapshot, move the passdb.tdb
+# database into /var/lib.
+
+if [ -n "$2" ] && dpkg --compare-versions "$2" lt 2.999+3.0.alpha23-5 \
+ && [ -e /etc/samba/passdb.tdb -a ! -e /var/lib/samba/passdb.tdb ]
+then
+ mv /etc/samba/passdb.tdb /var/lib/samba/
+fi
+
+# --- End of FHS migration code ---
+
+# If upgrading from a previous 2.999 snapshot, clear the broken
+# registry.tdb file.
+if [ -n "$2" ] && dpkg --compare-versions "$2" gt 2.99.cvs.20020713-1 \
+ && dpkg --compare-versions "$2" lt 2.999+3.0cvs20020805-1
+then
+ rm -f /var/lib/samba/registry.tdb
+fi
+
+# ------------------------- Debconf questions start ---------------------
+
+# Run Samba as daemons or from inetd?
+db_get samba/run_mode || true
+RUN_MODE="${RET}"
+
+TMPFILE=/etc/default/samba.dpkg-tmp
+sed -e "s/^[[:space:]]*RUN_MODE[[:space:]]*=.*/RUN_MODE=\"${RUN_MODE}\"/" \
+ < ${INITCONFFILE} >${TMPFILE}
+chmod a+r ${TMPFILE}
+mv -f ${TMPFILE} ${INITCONFFILE}
+
+# Generate a smbpasswd file?
+db_get samba/generate_smbpasswd || true
+GENERATE_SMBPASSWD="${RET}"
+
+db_get samba/tdbsam || true
+PDB_MIGRATE="${RET}"
+
+# Done with debconf now.
+db_stop
+
+umask 066
+
+# FIXME: disable if ldapsam support is enabled?
+# FIXME: we don't want to pass these through the smbpasswd backend,
+# some of the faking can cause us problems!
+if [ "${GENERATE_SMBPASSWD}" = "true" -a ! -e /var/lib/samba/passdb.tdb -a ! -e /etc/samba/smbpasswd ]; then
+ getent passwd | /usr/sbin/mksmbpasswd > /etc/samba/smbpasswd
+ pdbedit -i smbpasswd -e tdbsam
+ rm /etc/samba/smbpasswd
+fi
+
+umask 022
+
+if [ -n "$2" -a -e /etc/samba/smbpasswd \
+ -a ! -e /var/lib/samba/passdb.tdb -a "$PDB_MIGRATE" = "true" ] \
+ && dpkg --compare-versions "$2" lt 2.99.cvs.20020713-2
+then
+ umask 066
+ pdbedit -i smbpasswd -e tdbsam
+ rm /etc/samba/smbpasswd
+ umask 022
+
+ # The database has been moved, now make sure we can still find it.
+ PASSDB=`sed -n -e"s/^[[:space:]]*\[global\]/\[global\]/i
+ /^\[global\]/,/^[[:space:]]*\[/ \
+ s/^[[:space:]]*passdb backend[[:space:]]*=[[:space:]]*//pi" \
+ < /etc/samba/smb.conf \
+ | tail -1`
+ if echo "$PASSDB" | egrep -q "(^|[[:space:]])smbpasswd"; then
+ if ! echo "$PASSDB" | egrep -q "(^|[[:space:]])tdbsam"; then
+ PASSDB=`echo $PASSDB | sed -e's/\(^\|[[:space:]]\)smbpasswd/\1tdbsam/'`
+ fi
+ fi
+ if ! echo "$PASSDB" | egrep -q "(^|[[:space:]])tdbsam"; then
+ PASSDB="tdbsam $PASSDB"
+ fi
+ TMPFILE=/etc/samba/smb.conf.dpkg-tmp
+ sed -e "s/^\([[:space:]]*\)\[global\]/\1\[global\]/i
+ /^[[:space:]]*\[global\]/,/^[[:space:]]*\[/ \
+ s/^\([[:space:]]*\)passdb backend[[:space:]]*=.*/\1passdb backend = ${PASSDB}/i" \
+ < /etc/samba/smb.conf >${TMPFILE}
+ chmod a+r ${TMPFILE}
+ mv -f ${TMPFILE} /etc/samba/smb.conf
+fi
+
+# ------------------------- Debconf questions end ---------------------
+
+# Handle removal of nmbd from inetd.conf, which is no longer a supported
+# configuration.
+if dpkg --compare-versions "$2" lt 2.999+3.0.alpha20-4; then
+ update-inetd --remove netbios-ns
+fi
+
+# We want to add these entries to inetd.conf commented out. Otherwise
+# UDP traffic could make inetd to start nmbd or smbd right during
+# the configuration stage.
+if [ -z "$2" ]; then
+ update-inetd --add "#<off># netbios-ssn stream tcp nowait root /usr/sbin/tcpd /usr/sbin/smbd"
+fi
+
+if [ "$RUN_MODE" = "daemons" ]; then
+ update-inetd --disable netbios-ssn
+else
+ update-inetd --enable netbios-ssn
+fi
+
+# This check is a safety net: the /etc/samba/smbpasswd file must have
+# permissions 600.
+if [ -f /etc/samba/smbpasswd ]; then
+ chmod 600 /etc/samba/smbpasswd
+fi
+
+# Do the same check for /var/backup/smbpasswd.bak, just in case.
+if [ -f /var/backups/smbpasswd.bak ]; then
+ chmod 600 /var/backups/smbpasswd.bak
+fi
+
+# Delete old /etc/samba/debian_config file, which is not used anymore
+# now that we are using debconf.
+rm -f /etc/samba/debian_config
+
+# Move old log files to the new location of Samba's log files
+mv -f /var/log/nmb* /var/log/samba/ 2> /dev/null || true
+mv -f /var/log/smb* /var/log/samba/ 2> /dev/null || true
+
+#DEBHELPER#
+
+exit 0
diff --git a/packaging/Debian/debian/samba.postrm b/packaging/Debian/debian/samba.postrm
new file mode 100644
index 00000000000..b79fe1d0099
--- /dev/null
+++ b/packaging/Debian/debian/samba.postrm
@@ -0,0 +1,26 @@
+#!/bin/sh -e
+#
+#
+
+if [ "$1" = purge ]; then
+
+ # Remove Samba's state files, both volatile and non-volatile
+ rm -Rf /var/run/samba/ /var/cache/samba/ /var/lib/samba
+
+ # Remove log files
+ rm -Rf /var/log/samba/
+
+ # Remove init.d configuration file
+ echo Removing configuration file /etc/default/samba... >&2
+ rm -f /etc/default/samba
+
+ # Remove NetBIOS entries from /etc/inetd.conf
+ update-inetd --remove netbios-ssn
+
+else
+ # Not purging, do not remove NetBIOS entries from /etc/inetd.conf
+ update-inetd --disable netbios-ssn
+
+fi
+
+#DEBHELPER#
diff --git a/packaging/Debian/debian/samba.prerm b/packaging/Debian/debian/samba.prerm
new file mode 100644
index 00000000000..ab62c706d85
--- /dev/null
+++ b/packaging/Debian/debian/samba.prerm
@@ -0,0 +1,10 @@
+#!/bin/sh -e
+
+if [ "$1" = upgrade -a -n "$2" ] && dpkg --compare-versions "$2" lt 2.99 \
+ && [ -e /var/lib/samba/passdb.tdb -a ! -e /etc/samba/smbpasswd ]
+then
+ pdbedit -i tdbsam -e smbpasswd
+ rm -f /var/lib/samba/passdb.tdb
+fi
+
+#DEBHELPER#
diff --git a/packaging/Debian/debian/samba.templates b/packaging/Debian/debian/samba.templates
new file mode 100644
index 00000000000..ce503aea5c4
--- /dev/null
+++ b/packaging/Debian/debian/samba.templates
@@ -0,0 +1,50 @@
+Template: samba/generate_smbpasswd
+Type: boolean
+Default: false
+_Description: Create samba password database, /var/lib/samba/passdb.tdb?
+ To be compatible with the defaults in most versions of Windows, Samba must
+ be configured to use encrypted passwords. This requires user passwords to
+ be stored in a file separate from /etc/passwd. This file can be created
+ automatically, but the passwords must be added manually (by you or the
+ user) by running smbpasswd, and you must arrange to keep it up-to-date in
+ the future. If you do not create it, you will have to reconfigure samba
+ (and probably your client machines) to use plaintext passwords. See
+ /usr/share/doc/samba-doc/htmldocs/ENCRYPTION.html from the samba-doc
+ package for more details.
+
+Template: samba/log_files_moved
+Type: note
+_Description: Samba's log files have moved.
+ Starting with the first packages of Samba 2.2 for Debian the log files for
+ both Samba daemons (nmbd and smbd) are now stored in /var/log/samba/. The
+ names of the files are log.nmbd and log.smbd, for nmbd and smbd
+ respectively.
+ .
+ The old log files that were in /var/log/ will be moved to the new location
+ for you.
+
+Template: samba/nmbd_from_inetd
+Type: note
+_Description: Running nmbd from inetd is no longer supported
+ Your system was previously configured to start nmbd and smbd from inetd.
+ As of version 2.999+3.0.alpha20-4, nmbd will no longer be started from
+ inetd. If you have modified your /etc/init.d/samba startup script, you
+ may need to adjust it by hand now so that nmbd will start.
+
+Template: samba/run_mode
+Type: select
+_Choices: daemons, inetd
+Default: daemons
+_Description: How do you want to run Samba?
+ The Samba daemon smbd can run as a normal daemon or from inetd. Running as
+ a daemon is the recommended approach.
+
+Template: samba/tdbsam
+Type: boolean
+Default: false
+_Description: Move /etc/samba/smbpasswd to /var/lib/samba/passdb.tdb?
+ Samba 3.0 introduces a newer, more complete SAM database interface which
+ supersedes the /etc/samba/smbpasswd file. Would you like your existing
+ smbpasswd file to be migrated to /var/lib/samba/passdb.tdb for you? If you
+ plan to use another pdb backend (e.g., LDAP) instead, you should answer
+ 'no' here.
diff --git a/packaging/Debian/debian/scripts/patch-source b/packaging/Debian/debian/scripts/patch-source
new file mode 100755
index 00000000000..a8559b41676
--- /dev/null
+++ b/packaging/Debian/debian/scripts/patch-source
@@ -0,0 +1,28 @@
+#!/bin/sh -e
+#
+#
+
+for patch in debian/patches/*.patch; do
+ echo '->'`basename $patch`:
+ patch -p1 --ignore-whitespace < $patch
+done
+
+# This code is currently not used because it creates a long version
+# number string. For now we're sticking to <upstream version>-Debian,
+# as in 3.0.0rc2-Debian. peloy@debian.org.-
+
+# ---- Begin unused code ----
+# Get Debian version number from 1st line of the Debian changelog
+#DEBIAN_VERSION=`sed -n -e '1s/^.*(\(.*\)).*$/\1/p' debian/changelog`
+
+# Insert Debian version number in source/VERSION, which will then be
+# used to create source/include/version.h.
+#TMPFILE=source/VERSION.debian
+#sed -e "s/^\(SAMBA_VERSION_VENDOR_SUFFIX=\).*$/\1\"Debian-${DEBIAN_VERSION}\"/" source/VERSION > ${TMPFILE}
+#mv -f ${TMPFILE} source/VERSION
+# ---- End unused code ----
+
+# Regenerate configure only if it is older than configure.in
+[ source/configure -ot source/configure.in ] && (cd source && sh ./autogen.sh)
+
+exit 0
diff --git a/packaging/Debian/debian/scripts/unpatch-source b/packaging/Debian/debian/scripts/unpatch-source
new file mode 100755
index 00000000000..d3681cfa504
--- /dev/null
+++ b/packaging/Debian/debian/scripts/unpatch-source
@@ -0,0 +1,20 @@
+#!/bin/sh -e
+#
+#
+
+# We want to reverse the patches in the opposite order we applied
+# them, hence the 'ls|sort -r'.
+for patch in `ls debian/patches/*.patch | sort -r`; do
+ patch -p1 -R --ignore-whitespace < $patch
+done
+
+# Unused code. See comment in the patch-source script.
+
+#TMPFILE=source/VERSION.debian
+#sed -e "s/^\(SAMBA_VERSION_VENDOR_SUFFIX=\).*$/\1/" source/VERSION > ${TMPFILE}
+#mv -f ${TMPFILE} source/VERSION
+
+# Regenerate configure only if it is older than configure.in
+[ source/configure -ot source/configure.in ] && (cd source && autoheader && autoconf)
+
+exit 0
diff --git a/packaging/Debian/debian/smb.conf b/packaging/Debian/debian/smb.conf
new file mode 100644
index 00000000000..8a75979945a
--- /dev/null
+++ b/packaging/Debian/debian/smb.conf
@@ -0,0 +1,237 @@
+#
+# Sample configuration file for the Samba suite for Debian GNU/Linux.
+#
+#
+# This is the main Samba configuration file. You should read the
+# smb.conf(5) manual page in order to understand the options listed
+# here. Samba has a huge number of configurable options most of which
+# are not shown in this example
+#
+# Any line which starts with a ; (semi-colon) or a # (hash)
+# is a comment and is ignored. In this example we will use a #
+# for commentary and a ; for parts of the config file that you
+# may wish to enable
+#
+# NOTE: Whenever you modify this file you should run the command
+# "testparm" to check that you have not many any basic syntactic
+# errors.
+#
+
+#======================= Global Settings =======================
+
+[global]
+
+## Browsing/Identification ###
+
+# Change this to the workgroup/NT-domain name your Samba server will part of
+ workgroup = DEBIAN_FANS
+
+# server string is the equivalent of the NT Description field
+ server string = %h server (Samba %v)
+
+# Windows Internet Name Serving Support Section:
+# WINS Support - Tells the NMBD component of Samba to enable its WINS Server
+; wins support = no
+
+# WINS Server - Tells the NMBD components of Samba to be a WINS Client
+# Note: Samba can be either a WINS Server, or a WINS Client, but NOT both
+; wins server = w.x.y.z
+
+# This will prevent nmbd to search for NetBIOS names through DNS.
+ dns proxy = no
+
+# What naming service and in what order should we use to resolve host names
+# to IP addresses
+; name resolve order = lmhosts host wins bcast
+
+
+#### Debugging/Accounting ####
+
+# This tells Samba to use a separate log file for each machine
+# that connects
+ log file = /var/log/samba/log.%m
+
+# Put a capping on the size of the log files (in Kb).
+ max log size = 1000
+
+# If you want Samba to only log through syslog then set the following
+# parameter to 'yes'.
+; syslog only = no
+
+# We want Samba to log a minimum amount of information to syslog. Everything
+# should go to /var/log/samba/log.{smbd,nmbd} instead. If you want to log
+# through syslog you should set the following parameter to something higher.
+ syslog = 0
+
+# Do something sensible when Samba crashes: mail the admin a backtrace
+ panic action = /usr/share/samba/panic-action %d
+
+
+####### Authentication #######
+
+# "security = user" is always a good idea. This will require a Unix account
+# in this server for every user accessing the server. See
+# /usr/share/doc/samba-doc/htmldocs/ServerType.html in the samba-doc
+# package for details.
+; security = user
+
+# You may wish to use password encryption. See the section on
+# 'encrypt passwords' in the smb.conf(5) manpage before enabling.
+ encrypt passwords = no
+
+# If you are using encrypted passwords, Samba will need to know what
+# password database type you are using.
+ passdb backend = tdbsam guest
+
+ obey pam restrictions = yes
+
+; guest account = nobody
+ invalid users = root
+
+# This boolean parameter controls whether Samba attempts to sync the Unix
+# password with the SMB password when the encrypted SMB password in the
+# passdb is changed.
+; unix password sync = no
+
+# For Unix password sync to work on a Debian GNU/Linux system, the following
+# parameters must be set (thanks to Augustin Luton <aluton@hybrigenics.fr> for
+# sending the correct chat script for the passwd program in Debian Potato).
+ passwd program = /usr/bin/passwd %u
+ passwd chat = *Enter\snew\sUNIX\spassword:* %n\n *Retype\snew\sUNIX\spassword:* %n\n .
+
+# This boolean controls whether PAM will be used for password changes
+# when requested by an SMB client instead of the program listed in
+# 'passwd program'. The default is 'no'.
+; pam password change = no
+
+
+########## Printing ##########
+
+# If you want to automatically load your printer list rather
+# than setting them up individually then you'll need this
+; load printers = yes
+
+# lpr(ng) printing. You may wish to override the location of the
+# printcap file
+; printing = bsd
+; printcap name = /etc/printcap
+
+# CUPS printing. See also the cupsaddsmb(8) manpage in the
+# cupsys-client package.
+; printing = cups
+; printcap name = cups
+
+# When using [print$], root is implicitly a 'printer admin', but you can
+# also give this right to other users to add drivers and set printer
+# properties
+; printer admin = @ntadmin
+
+
+######## File sharing ########
+
+# Name mangling options
+; preserve case = yes
+; short preserve case = yes
+
+
+############ Misc ############
+
+# Using the following line enables you to customise your configuration
+# on a per machine basis. The %m gets replaced with the netbios name
+# of the machine that is connecting
+; include = /home/samba/etc/smb.conf.%m
+
+# Most people will find that this option gives better performance.
+# See smb.conf(5) and /usr/share/doc/samba-doc/htmldocs/speed.html
+# for details
+# You may want to add the following on a Linux system:
+# SO_RCVBUF=8192 SO_SNDBUF=8192
+ socket options = TCP_NODELAY
+
+# The following parameter is useful only if you have the linpopup package
+# installed. The samba maintainer and the linpopup maintainer are
+# working to ease installation and configuration of linpopup and samba.
+; message command = /bin/sh -c '/usr/bin/linpopup "%f" "%m" %s; rm %s' &
+
+# Domain Master specifies Samba to be the Domain Master Browser. If this
+# machine will be configured as a BDC (a secondary logon server), you
+# must set this to 'no'; otherwise, the default behavior is recommended.
+; domain master = auto
+
+# Some defaults for winbind (make sure you're not using the ranges
+# for something else.)
+; idmap uid = 10000-20000
+; idmap gid = 10000-20000
+; template shell = /bin/bash
+
+#======================= Share Definitions =======================
+
+[homes]
+ comment = Home Directories
+ browseable = no
+
+# By default, the home directories are exported read-only. Change next
+# parameter to 'yes' if you want to be able to write to them.
+ writable = no
+
+# File creation mask is set to 0700 for security reasons. If you want to
+# create files with group=rw permissions, set next parameter to 0775.
+ create mask = 0700
+
+# Directory creation mask is set to 0700 for security reasons. If you want to
+# create dirs. with group=rw permissions, set next parameter to 0775.
+ directory mask = 0700
+
+# Un-comment the following and create the netlogon directory for Domain Logons
+# (you need to configure Samba to act as a domain controller too.)
+;[netlogon]
+; comment = Network Logon Service
+; path = /home/samba/netlogon
+; guest ok = yes
+; writable = no
+; share modes = no
+
+[printers]
+ comment = All Printers
+ browseable = no
+ path = /tmp
+ printable = yes
+ public = no
+ writable = no
+ create mode = 0700
+
+# Windows clients look for this share name as a source of downloadable
+# printer drivers
+[print$]
+ comment = Printer Drivers
+ path = /var/lib/samba/printers
+ browseable = yes
+ read only = yes
+ guest ok = no
+# Uncomment to allow remote administration of Windows print drivers.
+# Replace 'ntadmin' with the name of the group your admin users are
+# members of.
+; write list = root, @ntadmin
+
+# A sample share for sharing your CD-ROM with others.
+;[cdrom]
+; comment = Samba server's CD-ROM
+; writable = no
+; locking = no
+; path = /cdrom
+; public = yes
+
+# The next two parameters show how to auto-mount a CD-ROM when the
+# cdrom share is accesed. For this to work /etc/fstab must contain
+# an entry like this:
+#
+# /dev/scd0 /cdrom iso9660 defaults,noauto,ro,user 0 0
+#
+# The CD-ROM gets unmounted automatically after the connection to the
+#
+# If you don't want to use auto-mounting/unmounting make sure the CD
+# is mounted on /cdrom
+#
+; preexec = /bin/mount /cdrom
+; postexec = /bin/umount /cdrom
+
diff --git a/packaging/Debian/debian/smbclient.files b/packaging/Debian/debian/smbclient.files
new file mode 100644
index 00000000000..96e8945bf3d
--- /dev/null
+++ b/packaging/Debian/debian/smbclient.files
@@ -0,0 +1,15 @@
+usr/bin/smbclient
+usr/bin/smbtar
+usr/bin/rpcclient
+usr/bin/smbspool
+usr/bin/smbtree
+usr/bin/smbcacls
+usr/bin/smbcquotas
+usr/share/man/man1/smbclient.1
+usr/share/man/man1/smbtar.1
+usr/share/man/man1/rpcclient.1
+usr/share/man/man8/smbspool.8
+usr/share/man/man1/smbcacls.1
+usr/share/man/man1/smbcquotas.1
+usr/share/man/man1/smbtree.1
+usr/lib/cups/backend/smb
diff --git a/packaging/Debian/debian/smbfs.files b/packaging/Debian/debian/smbfs.files
new file mode 100644
index 00000000000..870db7d6453
--- /dev/null
+++ b/packaging/Debian/debian/smbfs.files
@@ -0,0 +1,10 @@
+sbin/mount.smbfs
+sbin/mount.smb
+usr/bin/smbmount
+usr/bin/smbumount
+usr/bin/smbmnt
+usr/share/man/man8/smbmount.8
+usr/share/man/man8/smbumount.8
+usr/share/man/man8/smbmnt.8
+usr/share/man/man8/mount.smb.8
+usr/share/man/man8/mount.smbfs.8
diff --git a/packaging/Debian/debian/smbwrapper.dirs b/packaging/Debian/debian/smbwrapper.dirs
new file mode 100644
index 00000000000..fd727bddf05
--- /dev/null
+++ b/packaging/Debian/debian/smbwrapper.dirs
@@ -0,0 +1,2 @@
+usr/bin
+usr/share/samba
diff --git a/packaging/Debian/debian/smbwrapper.docs b/packaging/Debian/debian/smbwrapper.docs
new file mode 100644
index 00000000000..2924e78734a
--- /dev/null
+++ b/packaging/Debian/debian/smbwrapper.docs
@@ -0,0 +1,2 @@
+source/smbwrapper/README
+source/smbwrapper/PORTING
diff --git a/packaging/Debian/debian/smbwrapper.files b/packaging/Debian/debian/smbwrapper.files
new file mode 100644
index 00000000000..08edbead6e6
--- /dev/null
+++ b/packaging/Debian/debian/smbwrapper.files
@@ -0,0 +1 @@
+usr/bin/smbsh
diff --git a/packaging/Debian/debian/swat.config b/packaging/Debian/debian/swat.config
new file mode 100644
index 00000000000..e210fae55ea
--- /dev/null
+++ b/packaging/Debian/debian/swat.config
@@ -0,0 +1,11 @@
+#/bin/sh -e
+#
+#
+
+# Source debconf library.
+. /usr/share/debconf/confmodule
+
+db_title "Samba Web Administration Tool (SWAT)"
+
+db_input medium swat/smb_conf_warn || true
+db_go
diff --git a/packaging/Debian/debian/swat.dirs b/packaging/Debian/debian/swat.dirs
new file mode 100644
index 00000000000..d5df7df4b8e
--- /dev/null
+++ b/packaging/Debian/debian/swat.dirs
@@ -0,0 +1,2 @@
+usr/sbin
+usr/share/samba/swat
diff --git a/packaging/Debian/debian/swat.files b/packaging/Debian/debian/swat.files
new file mode 100644
index 00000000000..6fed39111be
--- /dev/null
+++ b/packaging/Debian/debian/swat.files
@@ -0,0 +1,2 @@
+usr/sbin/swat
+usr/share/man/man8/swat.8
diff --git a/packaging/Debian/debian/swat.postinst b/packaging/Debian/debian/swat.postinst
new file mode 100644
index 00000000000..338f8a07c23
--- /dev/null
+++ b/packaging/Debian/debian/swat.postinst
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+#
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin
+
+case "$1" in
+ configure)
+ ;;
+ abort-upgrade|abort-remove|abort-deconfigure)
+ exit 0
+ ;;
+ *)
+ echo "$0: Unknown action \"$1\""
+ exit 0
+ ;;
+esac
+
+# Set up swat, turned off by default.
+update-inetd --group OTHER --add \
+ '#<off># swat\t\tstream\ttcp\tnowait.400\troot\t/usr/sbin/tcpd\t/usr/sbin/swat'
+
+#DEBHELPER#
diff --git a/packaging/Debian/debian/swat.postrm b/packaging/Debian/debian/swat.postrm
new file mode 100644
index 00000000000..6bc5873096e
--- /dev/null
+++ b/packaging/Debian/debian/swat.postrm
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+#
+
+case "$1" in
+ purge)
+ update-inetd --remove '/usr/sbin/swat$'
+ ;;
+ remove)
+ ;;
+ upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+ ;;
+ *)
+ echo "$0: unknown action \"$1\""
+ exit 0
+ ;;
+esac
+
+#DEBHELPER#
+
+exit 0
+
diff --git a/packaging/Debian/debian/swat.templates b/packaging/Debian/debian/swat.templates
new file mode 100644
index 00000000000..3407f7efbc1
--- /dev/null
+++ b/packaging/Debian/debian/swat.templates
@@ -0,0 +1,6 @@
+Template: swat/smb_conf_warn
+Type: note
+_Description: Your smb.conf will be re-written!
+ SWAT will rewrite your smb.conf file. It will rearrange the entries and
+ delete all comments, include= and copy= options. If you have a carefully
+ crafted smb.conf then back it up or don't use SWAT!
diff --git a/packaging/Debian/debian/winbind.dirs b/packaging/Debian/debian/winbind.dirs
new file mode 100644
index 00000000000..1da8fba83ad
--- /dev/null
+++ b/packaging/Debian/debian/winbind.dirs
@@ -0,0 +1 @@
+usr/share/lintian/overrides
diff --git a/packaging/Debian/debian/winbind.files b/packaging/Debian/debian/winbind.files
new file mode 100644
index 00000000000..2834acf7caf
--- /dev/null
+++ b/packaging/Debian/debian/winbind.files
@@ -0,0 +1,7 @@
+usr/sbin/winbindd
+usr/bin/wbinfo
+usr/share/man/man1/wbinfo.1
+usr/share/man/man8/winbindd.8
+lib/security/pam_winbind.so
+lib/libnss_winbind.so.2
+lib/libnss_wins.so.2
diff --git a/packaging/Debian/debian/winbind.init b/packaging/Debian/debian/winbind.init
new file mode 100644
index 00000000000..2dfdf3b8352
--- /dev/null
+++ b/packaging/Debian/debian/winbind.init
@@ -0,0 +1,48 @@
+#!/bin/sh
+#
+# Start/stops the winbindd daemon.
+#
+#
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin
+
+DAEMON=/usr/sbin/winbindd
+
+# clear conflicting settings from the environment
+unset TMPDIR
+
+# See if the daemon is there
+test -x $DAEMON || exit 0
+
+case "$1" in
+ start)
+ echo -n "Starting the Winbind daemon: winbindd"
+
+ start-stop-daemon --start --quiet --exec $DAEMON
+
+ echo "."
+ ;;
+ stop)
+ echo -n "Stopping the Winbind daemon: winbindd"
+
+ start-stop-daemon --stop --quiet --oknodo --exec $DAEMON
+
+ echo "."
+ ;;
+ restart|force-reload)
+ echo -n "Restarting the Winbind daemon: winbindd"
+
+ start-stop-daemon --stop --quiet --oknodo --exec $DAEMON
+ sleep 2
+ start-stop-daemon --start --quiet --exec $DAEMON
+
+ echo "."
+ ;;
+ *)
+ echo "Usage: /etc/init.d/winbind {start|stop|restart|force-reload}"
+ exit 1
+ ;;
+esac
+
+exit 0
+
diff --git a/packaging/Debian/debian/winbind.lintian b/packaging/Debian/debian/winbind.lintian
new file mode 100644
index 00000000000..fca17d3cca1
--- /dev/null
+++ b/packaging/Debian/debian/winbind.lintian
@@ -0,0 +1,6 @@
+winbind: ldconfig-symlink-missing-for-shlib lib/libnss_winbind.so lib/libnss_winbind.so.2 libnss_winbind.so
+winbind: ldconfig-symlink-missing-for-shlib lib/libnss_wins.so lib/libnss_wins.so.2 libnss_wins.so
+winbind: no-shlibs-control-file lib/libnss_winbind.so.2
+winbind: no-shlibs-control-file lib/libnss_wins.so.2
+winbind: postinst-must-call-ldconfig lib/libnss_wins.so.2
+winbind: postrm-should-call-ldconfig lib/libnss_wins.so.2
diff --git a/packaging/Debian/debian/winbind.logrotate b/packaging/Debian/debian/winbind.logrotate
new file mode 100644
index 00000000000..e36cd1281c5
--- /dev/null
+++ b/packaging/Debian/debian/winbind.logrotate
@@ -0,0 +1,10 @@
+/var/log/samba/log.winbindd {
+ weekly
+ missingok
+ rotate 7
+ postrotate
+ [ -f /var/run/samba/winbindd.pid ] && kill -HUP `cat /var/run/samba/winbindd.pid`
+ endscript
+ compress
+ notifempty
+}
diff --git a/packaging/Debian/debian/wins2dns.awk b/packaging/Debian/debian/wins2dns.awk
new file mode 100644
index 00000000000..176868a115d
--- /dev/null
+++ b/packaging/Debian/debian/wins2dns.awk
@@ -0,0 +1,38 @@
+#!/usr/bin/awk -f
+#
+# Date: Wed, 26 Aug 1998 10:37:39 -0600 (MDT)
+# From: Jason Gunthorpe <jgg@deltatee.com>
+# To: samba@packages.debian.org
+# Subject: Nifty samba script
+#
+# Here is a really nifty script I just wrote for samba, it takes the wins
+# database in /var/samba/wins and writes out two dns files for it. In this
+# way network wide wins clients can get into the dns for use by unix
+# machines.
+#
+# Perhaps this could be included in /usr/doc/examples or somesuch.
+#
+
+BEGIN {
+ FS="#|\"";
+FORWARD="/tmp/wins.hosts"
+REVERSE="/tmp/wins.rev"
+DOMAIN="ven.ra.rockwell.com"
+}
+$3 == "00" {
+ split($4,a," " );
+ split(a[2],b,".");
+ while (sub(" ","-",$2));
+ $2=tolower($2);
+ if (b[1] == "255")
+ next;
+ if (length($2) >= 8)
+ print $2"\ta\t"a[2] > FORWARD
+ else
+ print $2"\t\ta\t"a[2] > FORWARD
+ print b[4]"."b[3]"\t\tptr\t"$2"."DOMAIN"." > REVERSE
+}
+END {
+ system("echo killall -HUP named");
+}
+
diff --git a/packaging/Example/Instructions b/packaging/Example/Instructions
new file mode 100644
index 00000000000..02ffa7b6a8c
--- /dev/null
+++ b/packaging/Example/Instructions
@@ -0,0 +1,41 @@
+Copyright (C) 1997-1998 Samba-Team
+E-mail: samba-binaries@samba.org
+
+Subject: Installation Instructions for SuperNewOS X.X
+--------------------------------------------------------
+
+1) cd /
+2) tar xvf [path-to-samba-package]/install.tar
+3) cd /usr/local/samba/lib
+4) vi smb.conf
+
+Now modify smb.conf to reflect your site needs.
+
+5) samba start
+
+To stop samba:
+
+ samba stop
+
+You could install samba to run from the system start-up scripts
+(recommended) by running ./setup.sh
+
+Start / Stop Samba as follows:-
+
+ samba [start | stop]
+
+
+Subject: New Users Must Read This
+-----------------------------------
+Above ALL else, read the smb.conf man pages _AND_ all text documentation.
+
+To enable SMB encrypted password support do the following:
+
+1) Put /usr/local/samba/bin in your PATH
+2) Edit /usr/local/samba/lib/smb.conf and uncomment the
+ line "encrypt passwd = yes"
+3) Execute: smbpasswd -a "username" "password"
+
+The above will create your /usr/local/samba/private/smbpasswd file
+in which will be the NT and LanMAN hashed passwords.
+
diff --git a/packaging/Example/PackageDate b/packaging/Example/PackageDate
new file mode 100644
index 00000000000..95cbb0972bf
--- /dev/null
+++ b/packaging/Example/PackageDate
@@ -0,0 +1 @@
+# Month, WeekDay, Date, Year, PreparerCity, Country
diff --git a/packaging/Example/Packager b/packaging/Example/Packager
new file mode 100644
index 00000000000..f5db3f8c303
--- /dev/null
+++ b/packaging/Example/Packager
@@ -0,0 +1 @@
+Packager: John Doe <doej@somewhere.org>
diff --git a/packaging/Example/Packaging-instructions b/packaging/Example/Packaging-instructions
new file mode 100644
index 00000000000..b598fd68b15
--- /dev/null
+++ b/packaging/Example/Packaging-instructions
@@ -0,0 +1,16 @@
+The package building files should be located in a
+directory called: samba-X.X.X
+
+Where X.X.X is the version ID.
+
+Step Directions
+==== ============================================
+1. Copy the samba distribution tarball into the packaging directory
+2. Make sure you have a installed on your system the GNU gzip/gunzip files
+3. Edit "package-prep" script as required
+4. Run "package-prep"
+
+If all goes well, you should now have a usable distribution package.
+
+Note: Update the Instructions file as required.
+
diff --git a/packaging/Example/package-prep b/packaging/Example/package-prep
new file mode 100755
index 00000000000..e8f5089a865
--- /dev/null
+++ b/packaging/Example/package-prep
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+# Extract the skeleton directory structure into which samba will be installed.
+tar xvf skeleton.tar
+
+# Now link the skeleton directory structure into the final install tree.
+( cd /usr/local;
+ mv man man.orig;
+ mv samba samba.orig;
+ NOWDIR=`pwd`;
+ ln -sf $NOWDIR/usr/local/man man;
+ ln -sf $NOWDIR/usr/local/samba samba; )
+
+# Unpack the master source tarball
+gunzip samba-X.X.X.tar.gz
+tar xvf samba-X.X.X.tar
+
+# Now build the binary files
+cd samba-X.X.X/source
+./configure
+make
+make install
+
+# Install into the packaging tree that full reflects the final install tree
+cd $NOWDIR/usr/local/samba
+cp -pr man ../
+rm -rf man
+cd $NOWDIR
+
+# Create the package tarball
+tar cvf install.tar usr var
+
+# Clean up original sources preserving all configured files
+# Note: This will allow installers to check build options
+cd samba-X.X.X/source
+rm -f ../source/bin/*
+make clean
+cd ../..
+tar cvf samba-X.X.X.tar samba-X.X.X
+rm -rf samba-X.X.X
+rm -rf usr var
+cd ..
+tar cvf samba-X.X.X-OS-Version-CPU.tar samba-X.X.X
+gzip samba-X.X.X-OS-Version-CPU.tar
+
+# We now have the distribution package, now restore our runtime system
+cd samba-X.X.X
+tar xcf install.tar
+
+# Please test operation before shipping the binary distribution package
+# to the samba-team.
diff --git a/packaging/Example/samba.init b/packaging/Example/samba.init
new file mode 100755
index 00000000000..c1d605cda06
--- /dev/null
+++ b/packaging/Example/samba.init
@@ -0,0 +1,34 @@
+#!/bin/sh
+#
+if [ ! -d /usr/bin ]; then
+ echo "The /usr file system is not mounted."
+ exit 1
+fi
+
+killproc() {
+ pid=`/bin/ps ax | grep -w $1 | sed -e 's/^ *//' -e 's/ .*//'`
+ echo "Stopping $1 now."
+ [ "$pid" != "" ] && kill -15 $pid
+ echo $pid
+}
+
+
+# Start/stop processes required for samba server
+
+case "$1" in
+
+ 'start')
+ echo "Starting Samba"
+ /usr/local/samba/sbin/smbd
+ /usr/local/samba/sbin/nmbd
+ echo "Done."
+ ;;
+ 'stop')
+ killproc smbd
+ killproc nmbd
+ ;;
+ *)
+ echo "Usage: /sbin/init.d/samba.init [ start | stop ]"
+ ;;
+esac
+exit 0
diff --git a/packaging/Example/setup.sh b/packaging/Example/setup.sh
new file mode 100755
index 00000000000..994b16d5ef0
--- /dev/null
+++ b/packaging/Example/setup.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+#
+# Note: This file MUST be edited to suit the target OS environment.
+#
+
+echo "Setting up for SWAT - The Samba Web Administration Tool"
+
+echo 'swat 901/tcp' >> /etc/services
+uniq /etc/services /tmp/tempserv
+cp /tmp/tempserv /etc/services
+rm /tmp/tempserv
+echo 'swat stream tcp nowait.400 root /usr/local/samba/bin/swat swat' >> /etc/inetd.conf
+uniq /etc/inetd.conf /tmp/tempinetd
+cp /tmp/tempinetd /etc/inetd.conf
+rm /tmp/tempinetd
+echo "Creating Symbolic Links for Start up Scripts"
+cp -f samba.init /sbin/init.d
+chown bin.bin /sbin/init.d/samba.init
+chmod 750 /sbin/init.d/samba.init
+ln -sf /sbin/init.d/samba.init /sbin/rc0.d/K01samba
+ln -sf /sbin/init.d/samba.init /sbin/rc2.d/K91samba
+ln -sf /sbin/init.d/samba.init /sbin/rc3.d/S91samba
+echo "Done. Now settting up samba command"
+ln /sbin/init.d/samba.init /sbin/samba
+echo "Done."
+echo "To start / stop samba:"
+echo " execute: samba [start | stop]
diff --git a/packaging/Example/skeleton.tar b/packaging/Example/skeleton.tar
new file mode 100644
index 00000000000..92598d0c5e7
--- /dev/null
+++ b/packaging/Example/skeleton.tar
Binary files differ
diff --git a/packaging/Fedora/filter-requires-samba.sh b/packaging/Fedora/filter-requires-samba.sh
new file mode 100755
index 00000000000..1ba10a0ee3d
--- /dev/null
+++ b/packaging/Fedora/filter-requires-samba.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+/usr/lib/rpm/perl.req $* | grep -v "Net::LDAP"
diff --git a/packaging/Fedora/makerpms.sh.tmpl b/packaging/Fedora/makerpms.sh.tmpl
new file mode 100644
index 00000000000..361d8418761
--- /dev/null
+++ b/packaging/Fedora/makerpms.sh.tmpl
@@ -0,0 +1,68 @@
+#!/bin/sh
+# Copyright (C) John H Terpstra 1998-2002
+# Gerald (Jerry) Carter 2003
+
+# The following allows environment variables to override the target directories
+# the alternative is to have a file in your home directory calles .rpmmacros
+# containing the following:
+# %_topdir /home/mylogin/redhat
+#
+# Note: Under this directory rpm expects to find the same directories that are under the
+# /usr/src/redhat directory
+#
+
+SPECDIR=`rpm --eval %_specdir`
+SRCDIR=`rpm --eval %_sourcedir`
+
+# At this point the SPECDIR and SRCDIR vaiables must have a value!
+
+USERID=`id -u`
+GRPID=`id -g`
+VERSION='PVERSION'
+SPECFILE="samba.spec"
+RPMVER=`rpm --version | awk '{print $3}'`
+RPM="rpmbuild"
+
+##
+## Check the RPM version (paranoid)
+##
+case $RPMVER in
+ 4*)
+ echo "Supported RPM version [$RPMVER]"
+ ;;
+ *)
+ echo "Unknown RPM version: `rpm --version`"
+ exit 1
+ ;;
+esac
+
+( cd ../../source; if [ -f Makefile ]; then make distclean; fi )
+( cd ../../.. ; chown -R ${USERID}.${GRPID} samba-${VERSION} )
+
+( cd ../../.. ; tar --exclude=CVS -cf - samba-${VERSION}/. | bzip2 > ${SRCDIR}/samba-${VERSION}.tar.bz2 )
+
+##
+## copy additional source files
+##
+for file in samba.pamd samba.sysconfig samba.spec \
+ smb.init swat.desktop filter-requires-samba.sh \
+ samba.log samba.xinetd smbprint winbind.init \
+ smb.conf smbusers
+do
+ cp -p $file ${SRCDIR}
+
+done
+
+chmod 755 ${SRCDIR}/filter-requires-samba.sh
+
+cp -p ${SPECFILE} ${SPECDIR}
+
+##
+## Build
+##
+echo Getting Ready to build release package
+cd ${SPECDIR}
+${RPM} -ba --clean --rmsource $SPECFILE
+
+echo Done.
+
diff --git a/packaging/Fedora/samba.log b/packaging/Fedora/samba.log
new file mode 100644
index 00000000000..04106239fb1
--- /dev/null
+++ b/packaging/Fedora/samba.log
@@ -0,0 +1,9 @@
+/var/log/samba/*.log {
+ notifempty
+ missingok
+ sharedscripts
+ copytruncate
+ postrotate
+ /bin/kill -HUP `cat /var/run/smbd.pid /var/run/nmbd.pid /var/run/winbindd.pid 2> /dev/null` 2> /dev/null || true
+ endscript
+}
diff --git a/packaging/Fedora/samba.pamd b/packaging/Fedora/samba.pamd
new file mode 100644
index 00000000000..f88aae628c2
--- /dev/null
+++ b/packaging/Fedora/samba.pamd
@@ -0,0 +1,4 @@
+auth required /lib/security/pam_stack.so service=system-auth
+session required /lib/security/pam_stack.so service=system-auth
+account required /lib/security/pam_stack.so service=system-auth
+password required /lib/security/pam_stack.so service=system-auth
diff --git a/packaging/Fedora/samba.spec.tmpl b/packaging/Fedora/samba.spec.tmpl
new file mode 100644
index 00000000000..ae6f95c9005
--- /dev/null
+++ b/packaging/Fedora/samba.spec.tmpl
@@ -0,0 +1,398 @@
+%define initdir %{_sysconfdir}/rc.d/init.d
+%define auth %(test -f /etc/pam.d/system-auth && echo /etc/pam.d/system-auth || echo)
+
+Summary: The Samba SMB server.
+Name: samba
+Version: PVERSION
+Release: PRELEASE
+License: GNU GPL Version 2
+Group: System Environment/Daemons
+URL: http://www.samba.org/
+
+Source: ftp://www.samba.org/pub/samba/%{name}-%{version}.tar.bz2
+
+# Red Hat specific replacement-files
+Source1: samba.log
+Source2: samba.xinetd
+Source4: samba.sysconfig
+Source5: smb.init
+Source6: winbind.init
+Source7: samba.pamd
+Source8: smbprint
+Source9: smbusers
+Source10: smb.conf
+
+# Don't depend on Net::LDAP
+Source999: filter-requires-samba.sh
+
+# generic patches
+
+Requires: pam >= 0.64 %{auth} samba-common = %{version}
+Requires: logrotate >= 3.4 initscripts >= 5.54-1
+BuildRoot: %{_tmppath}/%{name}-%{version}-root
+Prereq: /sbin/chkconfig /bin/mktemp /usr/bin/killall
+Prereq: fileutils sed /etc/init.d
+BuildRequires: pam-devel, readline-devel, ncurses-devel, fileutils, libacl-devel, openldap-devel, krb5-devel
+
+
+# Working around perl dependency problem from docs
+%define __perl_requires %{SOURCE999}
+
+%description
+Samba is the protocol by which a lot of PC-related machines share
+files, printers, and other information (such as lists of available
+files and printers). The Windows NT, OS/2, and Linux operating systems
+support this natively, and add-on packages can enable the same thing
+for DOS, Windows, VMS, UNIX of all kinds, MVS, and more. This package
+provides an SMB server that can be used to provide network services to
+SMB (sometimes called "Lan Manager") clients. Samba uses NetBIOS over
+TCP/IP (NetBT) protocols and does NOT need the NetBEUI (Microsoft Raw
+NetBIOS frame) protocol.
+
+%package client
+Summary: Samba (SMB) client programs.
+Group: Applications/System
+Requires: samba-common = %{version}
+Obsoletes: smbfs
+
+%description client
+The samba-client package provides some SMB clients to compliment the
+built-in SMB filesystem in Linux. These clients allow access of SMB
+shares and printing to SMB printers.
+
+%package common
+Summary: Files used by both Samba servers and clients.
+Group: Applications/System
+
+%description common
+Samba-common provides files necessary for both the server and client
+packages of Samba.
+
+%package swat
+Summary: The Samba SMB server configuration program.
+Group: Applications/System
+Requires: samba = %{version} xinetd
+
+%description swat
+The samba-swat package includes the new SWAT (Samba Web Administration
+Tool), for remotely managing Samba's smb.conf file using your favorite
+Web browser.
+
+%prep
+%setup -q
+
+# copy Red Hat specific scripts
+cp %{SOURCE5} packaging/Fedora/
+cp %{SOURCE6} packaging/Fedora/
+cp %{SOURCE7} packaging/Fedora/
+cp %{SOURCE8} packaging/Fedora/winbind.init
+
+%build
+
+cd source
+%ifarch i386 sparc
+RPM_OPT_FLAGS="$RPM_OPT_FLAGS -D_FILE_OFFSET_BITS=64"
+%endif
+%ifarch ia64
+libtoolize --copy --force # get it to recognize IA-64
+autoheader
+autoconf
+EXTRA="-D_LARGEFILE64_SOURCE"
+%endif
+
+## run autogen if missing the configure script
+if [ ! -f "configure" ]; then
+ ./autogen.sh
+fi
+
+CFLAGS="$RPM_OPT_FLAGS" ./configure \
+ --prefix=%{_prefix} \
+ --localstatedir=/var \
+ --sysconfdir=/etc \
+ --with-privatedir=%{_sysconfdir}/samba \
+ --with-fhs \
+ --with-quotas \
+ --with-smbmount \
+ --with-pam \
+ --with-pam_smbpass \
+ --with-syslog \
+ --with-utmp \
+ --with-sambabook=%{_datadir}/swat/using_samba \
+ --with-swatdir=%{_datadir}/swat \
+ --with-libsmbclient \
+ --with-acl-support
+make showlayout
+make proto
+make %{?_smp_mflags} all nsswitch/libnss_wins.so debug2html
+
+
+%install
+rm -rf $RPM_BUILD_ROOT
+
+mkdir -p $RPM_BUILD_ROOT/sbin
+mkdir -p $RPM_BUILD_ROOT/usr/{sbin,bin}
+mkdir -p $RPM_BUILD_ROOT/%{initdir}
+mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/{pam.d,logrotate.d}
+mkdir -p $RPM_BUILD_ROOT/var/{log,spool,lib}/samba
+mkdir -p $RPM_BUILD_ROOT/%{_datadir}/swat/using_samba
+mkdir -p $RPM_BUILD_ROOT/%{_datadir}/samba/codepages
+
+cd source
+
+make DESTDIR=$RPM_BUILD_ROOT \
+ install
+
+cd ..
+
+# Install other stuff
+install -m644 %{SOURCE10} $RPM_BUILD_ROOT%{_sysconfdir}/samba/smb.conf
+install -m644 %{SOURCE8} $RPM_BUILD_ROOT/etc/samba/smbusers
+install -m755 %{SOURCE8} $RPM_BUILD_ROOT%{_bindir}
+install -m644 %{SOURCE7} $RPM_BUILD_ROOT/etc/pam.d/samba
+install -m644 %{SOURCE1} $RPM_BUILD_ROOT/etc/logrotate.d/samba
+install -m755 source/script/mksmbpasswd.sh $RPM_BUILD_ROOT%{_bindir}
+
+install -m755 %{SOURCE5} $RPM_BUILD_ROOT%{initdir}/smb
+install -m755 %{SOURCE6} $RPM_BUILD_ROOT%{initdir}/winbind
+ln -s ../..%{initdir}/smb $RPM_BUILD_ROOT%{_sbindir}/samba
+ln -s ../..%{initdir}/winbind $RPM_BUILD_ROOT%{_sbindir}/winbind
+
+ln -s ../usr/bin/smbmount $RPM_BUILD_ROOT/sbin/mount.smb
+## Samba's Makefile is breaking this currently. Remove it and set our own
+/bin/rm -f $RPM_BUILD_ROOT/sbin/mount.smbfs
+ln -s ../usr/bin/smbmount $RPM_BUILD_ROOT/sbin/mount.smbfs
+
+echo 127.0.0.1 localhost > $RPM_BUILD_ROOT%{_sysconfdir}/samba/lmhosts
+
+
+# pam_smbpass
+mkdir -p $RPM_BUILD_ROOT/%{_lib}/security
+mv source/bin/pam_smbpass.so $RPM_BUILD_ROOT/%{_lib}/security/pam_smbpass.so
+
+# winbind
+mkdir -p $RPM_BUILD_ROOT/%{_lib}/security
+install -m 755 source/nsswitch/pam_winbind.so $RPM_BUILD_ROOT/%{_lib}/security/pam_winbind.so
+install -m 755 source/nsswitch/libnss_winbind.so $RPM_BUILD_ROOT/%{_lib}/libnss_winbind.so
+install -m 755 source/nsswitch/libnss_wins.so $RPM_BUILD_ROOT/%{_lib}/libnss_wins.so
+( cd $RPM_BUILD_ROOT/%{_lib};
+ ln -sf libnss_winbind.so libnss_winbind.so.2;
+ ln -sf libnss_wins.so libnss_wins.so.2 )
+
+# libsmbclient
+
+# make install puts libsmbclient.so in the wrong place on x86_64
+rm -f $RPM_BUILD_ROOT/usr/lib || true
+mkdir -p $RPM_BUILD_ROOT%{_libdir} $RPM_BUILD_ROOT%{_includedir}
+install -m 644 source/bin/libsmbclient.so $RPM_BUILD_ROOT%{_libdir}/libsmbclient.so
+install -m 644 source/bin/libsmbclient.a $RPM_BUILD_ROOT%{_libdir}/libsmbclient.a
+install -m 644 source/include/libsmbclient.h $RPM_BUILD_ROOT%{_includedir}
+
+mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/xinetd.d
+install -m644 %{SOURCE2} $RPM_BUILD_ROOT%{_sysconfdir}/xinetd.d/swat
+
+mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig
+install -m644 %{SOURCE4} $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/samba
+
+##
+## Clean out man pages for tools not installed here
+##
+rm -f $RPM_BUILD_ROOT/%{_mandir}/man1/editreg.1*
+rm -f $RPM_BUILD_ROOT%{_mandir}/man1/log2pcap.1*
+rm -f $RPM_BUILD_ROOT%{_mandir}/man1/smbsh.1*
+rm -f $RPM_BUILD_ROOT%{_mandir}/man1/smbget.1*
+rm -f $RPM_BUILD_ROOT/%{_mandir}/man8/mount.cifs.8*
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+/sbin/chkconfig --add smb
+
+%preun
+if [ $1 = 0 ] ; then
+ /sbin/chkconfig --del smb
+ rm -rf /var/log/samba/* /var/cache/samba/*
+ /sbin/service smb stop >/dev/null 2>&1
+fi
+exit 0
+
+%postun
+if [ "$1" -ge "1" ]; then
+ %{initdir}/smb condrestart >/dev/null 2>&1
+fi
+
+
+%post swat
+# Add swat entry to /etc/services if not already there.
+if [ ! "`grep ^\s**swat /etc/services`" ]; then
+ echo 'swat 901/tcp # Add swat service used via inetd' >> /etc/services
+fi
+
+%post common
+/sbin/chkconfig --add winbind
+/sbin/ldconfig
+
+%preun common
+if [ $1 = 0 ] ; then
+ /sbin/chkconfig --del winbind
+ /sbin/service winbind stop >/dev/null 2>&1
+fi
+exit 0
+
+%postun common -p /sbin/ldconfig
+
+%triggerpostun -- samba < 1.9.18p7
+if [ $1 != 0 ]; then
+ /sbin/chkconfig --add smb
+fi
+
+%triggerpostun -- samba < 2.0.5a-3
+if [ $1 != 0 ]; then
+ [ ! -d /var/lock/samba ] && mkdir -m 0755 /var/lock/samba
+ [ ! -d /var/spool/samba ] && mkdir -m 1777 /var/spool/samba
+ chmod 644 /etc/services
+ [ -f /etc/inetd.conf ] && chmod 644 /etc/inetd.conf
+fi
+
+%files
+%defattr(-,root,root)
+%doc README COPYING Manifest
+%doc WHATSNEW.txt Roadmap
+%doc docs
+%doc examples/autofs examples/LDAP examples/libsmbclient examples/misc examples/printer-accounting
+%doc examples/printing
+
+%attr(755,root,root) /%{_lib}/security/pam_smbpass.so
+%{_sbindir}/smbd
+%{_sbindir}/nmbd
+# %{_bindir}/make_unicodemap
+%{_bindir}/mksmbpasswd.sh
+%{_bindir}/smbcontrol
+%{_bindir}/smbstatus
+# %{_bindir}/smbadduser
+%{_bindir}/tdbbackup
+%config(noreplace) %{_sysconfdir}/sysconfig/samba
+%config(noreplace) %{_sysconfdir}/samba/smbusers
+%attr(755,root,root) %config %{initdir}/smb
+%config(noreplace) %{_sysconfdir}/logrotate.d/samba
+%config(noreplace) %{_sysconfdir}/pam.d/samba
+# %{_mandir}/man1/make_unicodemap.1*
+%{_mandir}/man1/smbcontrol.1*
+%{_mandir}/man1/smbstatus.1*
+%{_mandir}/man5/smbpasswd.5*
+%{_mandir}/man7/samba.7*
+%{_mandir}/man8/nmbd.8*
+%{_mandir}/man8/pdbedit.8*
+%{_mandir}/man8/smbd.8*
+%{_mandir}/man8/tdbbackup.8*
+#%{_mandir}/ja/man1/smbstatus.1*
+#%{_mandir}/ja/man5/smbpasswd.5*
+#%{_mandir}/ja/man7/samba.7*
+#%{_mandir}/ja/man8/smbd.8*
+#%{_mandir}/ja/man8/nmbd.8*
+%{_libdir}/samba/vfs
+
+%attr(0700,root,root) %dir /var/log/samba
+%attr(1777,root,root) %dir /var/spool/samba
+
+%files swat
+%defattr(-,root,root)
+%config(noreplace) %{_sysconfdir}/xinetd.d/swat
+%{_datadir}/swat
+%{_sbindir}/swat
+%{_mandir}/man8/swat.8*
+#%{_mandir}/ja/man8/swat.8*
+%attr(755,root,root) %{_libdir}/samba/*.msg
+
+%files client
+%defattr(-,root,root)
+/sbin/mount.smb
+/sbin/mount.smbfs
+%{_libdir}/samba/lowcase.dat
+%{_libdir}/samba/upcase.dat
+%{_libdir}/samba/valid.dat
+%{_bindir}/rpcclient
+%{_bindir}/smbcacls
+%{_bindir}/smbmount
+%{_bindir}/smbmnt
+%{_bindir}/smbumount
+%{_bindir}/findsmb
+%{_bindir}/tdbdump
+%{_mandir}/man8/tdbdump.8*
+%{_mandir}/man8/smbmnt.8*
+%{_mandir}/man8/smbmount.8*
+%{_mandir}/man8/smbumount.8*
+%{_mandir}/man8/smbspool.8*
+%{_bindir}/nmblookup
+%{_bindir}/smbclient
+%{_bindir}/smbprint
+%{_bindir}/smbspool
+%{_bindir}/smbtar
+%{_bindir}/net
+%{_bindir}/smbtree
+%{_mandir}/man1/findsmb.1*
+%{_mandir}/man1/nmblookup.1*
+%{_mandir}/man1/rpcclient.1*
+%{_mandir}/man1/smbcacls.1*
+%{_mandir}/man1/smbclient.1*
+%{_mandir}/man1/smbtar.1*
+%{_mandir}/man1/smbtree.1*
+%{_mandir}/man8/net.8*
+#%{_mandir}/ja/man1/smbtar.1*
+#%{_mandir}/ja/man1/smbclient.1*
+#%{_mandir}/ja/man1/nmblookup.1*
+
+%files common
+%defattr(-,root,root)
+/%{_lib}/libnss_wins.so*
+/%{_lib}/libnss_winbind.so*
+/%{_lib}/security/pam_winbind.so
+%{_libdir}/libsmbclient.a
+%{_libdir}/libsmbclient.so
+%{_libdir}/samba/charset/CP*.so
+%{_includedir}/libsmbclient.h
+%{_bindir}/testparm
+%{_bindir}/testprns
+%{_bindir}/smbpasswd
+# %{_bindir}/make_printerdef
+%{_bindir}/wbinfo
+# %{_bindir}/editreg
+%{_bindir}/ntlm_auth
+%{_bindir}/pdbedit
+%{_bindir}/profiles
+%{_bindir}/smbcquotas
+#%{_bindir}/vfstest
+%{_sbindir}/winbindd
+%config(noreplace) %{_sysconfdir}/samba/smb.conf
+%config(noreplace) %{_sysconfdir}/samba/lmhosts
+%dir %{_datadir}/samba
+%dir %{_datadir}/samba/codepages
+%dir %{_sysconfdir}/samba
+%{initdir}/winbind
+# %{_datadir}/samba/codepages/*
+# %{_mandir}/man1/make_smbcodepage.1*
+%{_mandir}/man1/ntlm_auth.1*
+%{_mandir}/man1/profiles.1*
+%{_mandir}/man1/smbcquotas.1*
+%{_mandir}/man1/testparm.1*
+%{_mandir}/man1/testprns.1*
+%{_mandir}/man5/smb.conf.5*
+%{_mandir}/man5/lmhosts.5*
+%{_mandir}/man8/smbpasswd.8*
+%{_mandir}/man1/wbinfo.1*
+%{_mandir}/man8/winbindd.8*
+%{_mandir}/man1/vfstest.1*
+
+# #%lang(ja) %{_mandir}/ja/man1/make_smbcodepage.1*
+#%lang(ja) %{_mandir}/ja/man1/testparm.1*
+#%lang(ja) %{_mandir}/ja/man1/testprns.1*
+#%lang(ja) %{_mandir}/ja/man5/smb.conf.5*
+#%lang(ja) %{_mandir}/ja/man5/lmhosts.5*
+#%lang(ja) %{_mandir}/ja/man8/smbpasswd.8*
+
+%changelog
+* Fri Jan 16 2004 Gerald (Jerry) Carter <jerry@samba,org>
+- Removed ChangeLog entries since they are kept in CVS
+
+
+
diff --git a/packaging/Fedora/samba.sysconfig b/packaging/Fedora/samba.sysconfig
new file mode 100644
index 00000000000..944b72fcc28
--- /dev/null
+++ b/packaging/Fedora/samba.sysconfig
@@ -0,0 +1,6 @@
+# Options to smbd
+SMBDOPTIONS="-D"
+# Options to nmbd
+NMBDOPTIONS="-D"
+# Options for winbindd
+WINBINDOPTIONS=""
diff --git a/packaging/Fedora/samba.xinetd b/packaging/Fedora/samba.xinetd
new file mode 100644
index 00000000000..8b62348dde3
--- /dev/null
+++ b/packaging/Fedora/samba.xinetd
@@ -0,0 +1,15 @@
+# default: off
+# description: SWAT is the Samba Web Admin Tool. Use swat \
+# to configure your Samba server. To use SWAT, \
+# connect to port 901 with your favorite web browser.
+service swat
+{
+ port = 901
+ socket_type = stream
+ wait = no
+ only_from = 127.0.0.1
+ user = root
+ server = /usr/sbin/swat
+ log_on_failure += USERID
+ disable = yes
+}
diff --git a/packaging/Fedora/smb.conf b/packaging/Fedora/smb.conf
new file mode 100644
index 00000000000..74806da16bb
--- /dev/null
+++ b/packaging/Fedora/smb.conf
@@ -0,0 +1,286 @@
+# This is the main Samba configuration file. You should read the
+# smb.conf(5) manual page in order to understand the options listed
+# here. Samba has a huge number of configurable options (perhaps too
+# many!) most of which are not shown in this example
+#
+# Any line which starts with a ; (semi-colon) or a # (hash)
+# is a comment and is ignored. In this example we will use a #
+# for commentry and a ; for parts of the config file that you
+# may wish to enable
+#
+# NOTE: Whenever you modify this file you should run the command "testparm"
+# to check that you have not made any basic syntactic errors.
+#
+#======================= Global Settings =====================================
+[global]
+
+# workgroup = NT-Domain-Name or Workgroup-Name
+ workgroup = MYGROUP
+
+# server string is the equivalent of the NT Description field
+ server string = Samba Server
+
+# This option is important for security. It allows you to restrict
+# connections to machines which are on your local network. The
+# following example restricts access to two C class networks and
+# the "loopback" interface. For more examples of the syntax see
+# the smb.conf man page
+; hosts allow = 192.168.1. 192.168.2. 127.
+
+# if you want to automatically load your printer list rather
+# than setting them up individually then you'll need this
+ printcap name = /etc/printcap
+ load printers = yes
+
+# It should not be necessary to spell out the print system type unless
+# yours is non-standard. Currently supported print systems include:
+# bsd, sysv, plp, lprng, aix, hpux, qnx
+; printing = bsd
+
+# Uncomment this if you want a guest account, you must add this to /etc/passwd
+# otherwise the user "nobody" is used
+; guest account = pcguest
+
+# this tells Samba to use a separate log file for each machine
+# that connects
+ log file = /var/log/samba/log.%m
+
+# Put a capping on the size of the log files (in Kb).
+ max log size = 50
+
+# Security mode. Most people will want user level security. See
+# security_level.txt for details.
+ security = user
+# Use password server option only with security = server
+; password server = <NT-Server-Name>
+
+# Password Level allows matching of _n_ characters of the password for
+# all combinations of upper and lower case.
+; password level = 8
+; username level = 8
+
+# You may wish to use password encryption. Please read
+# ENCRYPTION.txt, Win95.txt and WinNT.txt in the Samba documentation.
+# Do not enable this option unless you have read those documents
+; encrypt passwords = yes
+; smb passwd file = /etc/samba/smbpasswd
+
+# The following are needed to allow password changing from Windows to
+# update the Linux system password also.
+# NOTE: Use these with 'encrypt passwords' and 'smb passwd file' above.
+# NOTE2: You do NOT need these to allow workstations to change only
+# the encrypted SMB passwords. They allow the Unix password
+# to be kept in sync with the SMB password.
+; unix password sync = Yes
+; passwd program = /usr/bin/passwd %u
+; passwd chat = *New*UNIX*password* %n\n *ReType*new*UNIX*password* %n\n *passwd:*all*authentication*tokens*updated*successfully*
+
+# Unix users can map to different SMB User names
+; username map = /etc/samba/smbusers
+
+# Using the following line enables you to customise your configuration
+# on a per machine basis. The %m gets replaced with the netbios name
+# of the machine that is connecting
+; include = /etc/samba/smb.conf.%m
+
+# Most people will find that this option gives better performance.
+# See speed.txt and the manual pages for details
+ socket options = TCP_NODELAY SO_RCVBUF=8192 SO_SNDBUF=8192
+
+# Configure Samba to use multiple interfaces
+# If you have multiple network interfaces then you must list them
+# here. See the man page for details.
+; interfaces = 192.168.12.2/24 192.168.13.2/24
+
+# Configure remote browse list synchronisation here
+# request announcement to, or browse list sync from:
+# a specific host or from / to a whole subnet (see below)
+; remote browse sync = 192.168.3.25 192.168.5.255
+# Cause this host to announce itself to local subnets here
+; remote announce = 192.168.1.255 192.168.2.44
+
+# Browser Control Options:
+# set local master to no if you don't want Samba to become a master
+# browser on your network. Otherwise the normal election rules apply
+; local master = no
+
+# OS Level determines the precedence of this server in master browser
+# elections. The default value should be reasonable
+; os level = 33
+
+# Domain Master specifies Samba to be the Domain Master Browser. This
+# allows Samba to collate browse lists between subnets. Don't use this
+# if you already have a Windows NT domain controller doing this job
+; domain master = yes
+
+# Preferred Master causes Samba to force a local browser election on startup
+# and gives it a slightly higher chance of winning the election
+; preferred master = yes
+
+# Enable this if you want Samba to be a domain logon server for
+# Windows95 workstations.
+; domain logons = yes
+
+# if you enable domain logons then you may want a per-machine or
+# per user logon script
+# run a specific logon batch file per workstation (machine)
+; logon script = %m.bat
+# run a specific logon batch file per username
+; logon script = %U.bat
+
+# Where to store roving profiles (only for Win95 and WinNT)
+# %L substitutes for this servers netbios name, %U is username
+# You must uncomment the [Profiles] share below
+; logon path = \\%L\Profiles\%U
+
+# All NetBIOS names must be resolved to IP Addresses
+# 'Name Resolve Order' allows the named resolution mechanism to be specified
+# the default order is "host lmhosts wins bcast". "host" means use the unix
+# system gethostbyname() function call that will use either /etc/hosts OR
+# DNS or NIS depending on the settings of /etc/host.config, /etc/nsswitch.conf
+# and the /etc/resolv.conf file. "host" therefore is system configuration
+# dependant. This parameter is most often of use to prevent DNS lookups
+# in order to resolve NetBIOS names to IP Addresses. Use with care!
+# The example below excludes use of name resolution for machines that are NOT
+# on the local network segment
+# - OR - are not deliberately to be known via lmhosts or via WINS.
+; name resolve order = wins lmhosts bcast
+
+# Windows Internet Name Serving Support Section:
+# WINS Support - Tells the NMBD component of Samba to enable it's WINS Server
+; wins support = yes
+
+# WINS Server - Tells the NMBD components of Samba to be a WINS Client
+# Note: Samba can be either a WINS Server, or a WINS Client, but NOT both
+; wins server = w.x.y.z
+
+# WINS Proxy - Tells Samba to answer name resolution queries on
+# behalf of a non WINS capable client, for this to work there must be
+# at least one WINS Server on the network. The default is NO.
+; wins proxy = yes
+
+# DNS Proxy - tells Samba whether or not to try to resolve NetBIOS names
+# via DNS nslookups. The built-in default for versions 1.9.17 is yes,
+# this has been changed in version 1.9.18 to no.
+ dns proxy = no
+
+# Case Preservation can be handy - system default is _no_
+# NOTE: These can be set on a per share basis
+; preserve case = no
+; short preserve case = no
+# Default case is normally upper case for all DOS files
+; default case = lower
+# Be very careful with case sensitivity - it can break things!
+; case sensitive = no
+
+#============================ Share Definitions ==============================
+[homes]
+ comment = Home Directories
+ browseable = no
+ writable = yes
+
+# Un-comment the following and create the netlogon directory for Domain Logons
+; [netlogon]
+; comment = Network Logon Service
+; path = /home/netlogon
+; guest ok = yes
+; writable = no
+; share modes = no
+
+
+# Un-comment the following to provide a specific roving profile share
+# the default is to use the user's home directory
+;[Profiles]
+; path = /home/profiles
+; browseable = no
+; guest ok = yes
+
+
+# NOTE: If you have a BSD-style print system there is no need to
+# specifically define each individual printer
+[printers]
+ comment = All Printers
+ path = /var/spool/samba
+ browseable = no
+# Set public = yes to allow user 'guest account' to print
+ guest ok = no
+ writable = no
+ printable = yes
+
+# This one is useful for people to share files
+;[tmp]
+; comment = Temporary file space
+; path = /tmp
+; read only = no
+; public = yes
+
+# A publicly accessible directory, but read only, except for people in
+# the "staff" group
+;[public]
+; comment = Public Stuff
+; path = /home/samba
+; public = yes
+; read only = yes
+; write list = @staff
+
+# Other examples.
+#
+# A private printer, usable only by fred. Spool data will be placed in fred's
+# home directory. Note that fred must have write access to the spool directory,
+# wherever it is.
+;[fredsprn]
+; comment = Fred's Printer
+; valid users = fred
+; path = /homes/fred
+; printer = freds_printer
+; public = no
+; writable = no
+; printable = yes
+
+# A private directory, usable only by fred. Note that fred requires write
+# access to the directory.
+;[fredsdir]
+; comment = Fred's Service
+; path = /usr/somewhere/private
+; valid users = fred
+; public = no
+; writable = yes
+; printable = no
+
+# a service which has a different directory for each machine that connects
+# this allows you to tailor configurations to incoming machines. You could
+# also use the %u option to tailor it by user name.
+# The %m gets replaced with the machine name that is connecting.
+;[pchome]
+; comment = PC Directories
+; path = /usr/pc/%m
+; public = no
+; writable = yes
+
+# A publicly accessible directory, read/write to all users. Note that all files
+# created in the directory by users will be owned by the default user, so
+# any user with access can delete any other user's files. Obviously this
+# directory must be writable by the default user. Another user could of course
+# be specified, in which case all files would be owned by that user instead.
+;[public]
+; path = /usr/somewhere/else/public
+; public = yes
+; only guest = yes
+; writable = yes
+; printable = no
+
+# The following two entries demonstrate how to share a directory so that two
+# users can place files there that will be owned by the specific users. In this
+# setup, the directory should be writable by both users and should have the
+# sticky bit set on it to prevent abuse. Obviously this could be extended to
+# as many users as required.
+;[myshare]
+; comment = Mary's and Fred's stuff
+; path = /usr/somewhere/shared
+; valid users = mary fred
+; public = no
+; writable = yes
+; printable = no
+; create mask = 0765
+
+
diff --git a/packaging/Fedora/smb.init b/packaging/Fedora/smb.init
new file mode 100644
index 00000000000..11a011883f9
--- /dev/null
+++ b/packaging/Fedora/smb.init
@@ -0,0 +1,119 @@
+#!/bin/sh
+#
+# chkconfig: - 91 35
+# description: Starts and stops the Samba smbd and nmbd daemons \
+# used to provide SMB network services.
+#
+# pidfile: /var/run/samba/smbd.pid
+# pidfile: /var/run/samba/nmbd.pid
+# config: /etc/samba/smb.conf
+
+
+# Source function library.
+if [ -f /etc/init.d/functions ] ; then
+ . /etc/init.d/functions
+elif [ -f /etc/rc.d/init.d/functions ] ; then
+ . /etc/rc.d/init.d/functions
+else
+ exit 0
+fi
+
+# Avoid using root's TMPDIR
+unset TMPDIR
+
+# Source networking configuration.
+. /etc/sysconfig/network
+
+if [ -f /etc/sysconfig/samba ]; then
+ . /etc/sysconfig/samba
+fi
+
+# Check that networking is up.
+[ ${NETWORKING} = "no" ] && exit 0
+
+# Check that smb.conf exists.
+[ -f /etc/samba/smb.conf ] || exit 0
+
+# Check that we can write to it... so non-root users stop here
+[ -w /etc/samba/smb.conf ] || exit 0
+
+
+RETVAL=0
+
+
+start() {
+ KIND="SMB"
+ echo -n $"Starting $KIND services: "
+ daemon smbd $SMBDOPTIONS
+ RETVAL=$?
+ echo
+ KIND="NMB"
+ echo -n $"Starting $KIND services: "
+ daemon nmbd $NMBDOPTIONS
+ RETVAL2=$?
+ echo
+ [ $RETVAL -eq 0 -a $RETVAL2 -eq 0 ] && touch /var/lock/subsys/smb || \
+ RETVAL=1
+ return $RETVAL
+}
+
+stop() {
+ KIND="SMB"
+ echo -n $"Shutting down $KIND services: "
+ killproc smbd -TERM
+ RETVAL=$?
+ [ $RETVAL -eq 0 ] && rm -f /var/run/smbd.pid
+ echo
+ KIND="NMB"
+ echo -n $"Shutting down $KIND services: "
+ killproc nmbd -TERM
+ RETVAL2=$?
+ [ $RETVAL2 -eq 0 ] && rm -f /var/run/nmbd.pid
+ [ $RETVAL -eq 0 -a $RETVAL2 -eq 0 ] && rm -f /var/lock/subsys/smb
+ echo ""
+ return $RETVAL
+}
+
+restart() {
+ stop
+ start
+}
+
+reload() {
+ echo -n $"Reloading smb.conf file: "
+ killproc smbd -HUP
+ RETVAL=$?
+ echo
+ return $RETVAL
+}
+
+rhstatus() {
+ status smbd
+ status nmbd
+}
+
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ restart)
+ restart
+ ;;
+ reload)
+ reload
+ ;;
+ status)
+ rhstatus
+ ;;
+ condrestart)
+ [ -f /var/lock/subsys/smb ] && restart || :
+ ;;
+ *)
+ echo $"Usage: $0 {start|stop|restart|reload|status|condrestart}"
+ exit 1
+esac
+
+exit $?
diff --git a/packaging/Fedora/smbprint b/packaging/Fedora/smbprint
new file mode 100644
index 00000000000..1c3959d49b5
--- /dev/null
+++ b/packaging/Fedora/smbprint
@@ -0,0 +1,84 @@
+#!/bin/sh
+# This script is an input filter for printcap printing on a unix machine. It
+# uses the smbclient program to print the file to the specified smb-based
+# server and service.
+# For example you could have a printcap entry like this
+#
+# smb:lp=/dev/null:sd=/usr/spool/smb:sh:if=/usr/local/samba/smbprint
+#
+# which would create a unix printer called "smb" that will print via this
+# script. You will need to create the spool directory /usr/spool/smb with
+# appropriate permissions and ownerships for your system.
+
+# Set these to the server and service you wish to print to
+# In this example I have a WfWg PC called "lapland" that has a printer
+# exported called "printer" with no password.
+
+#
+# Script further altered by hamiltom@ecnz.co.nz (Michael Hamilton)
+# so that the server, service, and password can be read from
+# a /usr/var/spool/lpd/PRINTNAME/.config file.
+#
+# In order for this to work the /etc/printcap entry must include an
+# accounting file (af=...):
+#
+# cdcolour:\
+# :cm=CD IBM Colorjet on 6th:\
+# :sd=/var/spool/lpd/cdcolour:\
+# :af=/var/spool/lpd/cdcolour/acct:\
+# :if=/usr/local/etc/smbprint:\
+# :mx=0:\
+# :lp=/dev/null:
+#
+# The /usr/var/spool/lpd/PRINTNAME/.config file should contain:
+# share=PC_SERVER
+# user="user"
+# password="password"
+#
+# Please, do not modify the order in the file.
+# Example:
+# share=\\server\deskjet
+# user="fred"
+# password=""
+
+#
+# The last parameter to the filter is the accounting file name.
+# Extract the directory name from the file name.
+# Concat this with /.config to get the config file.
+#
+eval acct_file=\${$#}
+spool_dir=`dirname $acct_file`
+config_file=$spool_dir/.config
+
+# Should read the following variables set in the config file:
+# share
+# hostip
+# user
+# password
+
+eval `cat $config_file`
+
+share=`echo $share | sed "s/[\]/\//g"`
+
+if [ "$user" != "" ]; then
+ usercmd="-U"
+else
+ usercmd=""
+fi
+
+if [ "$workgroup" != "" ]; then
+ workgroupcmd="-W"
+else
+ workgroupcmd=""
+fi
+
+if [ "$translate" = "yes" ]; then
+ command="translate ; print -"
+else
+ command="print -"
+fi
+#echo $share $password $translate $x_command > /tmp/smbprint.log
+
+cat | /usr/bin/smbclient "$share" "$password" -E ${hostip:+-I} \
+ $hostip -N -P $usercmd "$user" $workgroupcmd "$workgroup" \
+ -c "$command" 2>/dev/null
diff --git a/packaging/Fedora/smbusers b/packaging/Fedora/smbusers
new file mode 100644
index 00000000000..ae3389f53f8
--- /dev/null
+++ b/packaging/Fedora/smbusers
@@ -0,0 +1,3 @@
+# Unix_name = SMB_name1 SMB_name2 ...
+root = administrator admin
+nobody = guest pcguest smbguest
diff --git a/packaging/Fedora/swat.desktop b/packaging/Fedora/swat.desktop
new file mode 100644
index 00000000000..0d7b4b5c48c
--- /dev/null
+++ b/packaging/Fedora/swat.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Name=Samba Configuration
+Name[de]=Samba Konfiguration
+Type=Application
+Comment=Configure Samba with a web based interface
+Exec=htmlview http://127.0.0.1:901/
+Terminal=false
+Categories=X-Red-Hat-Extra;Application;System;X-Red-Hat-ServerConfig;
diff --git a/packaging/Fedora/winbind.init b/packaging/Fedora/winbind.init
new file mode 100644
index 00000000000..05e0eb4d006
--- /dev/null
+++ b/packaging/Fedora/winbind.init
@@ -0,0 +1,102 @@
+#!/bin/sh
+#
+# chkconfig: - 91 35
+# description: Starts and stops the Samba winbind daemon
+# #
+# pidfile: /var/cache/samba/winbind.pid
+# config: /etc/samba/smb.conf
+
+
+# Source function library.
+if [ -f /etc/init.d/functions ] ; then
+ . /etc/init.d/functions
+elif [ -f /etc/rc.d/init.d/functions ] ; then
+ . /etc/rc.d/init.d/functions
+else
+ exit 0
+fi
+
+# Avoid using root's TMPDIR
+unset TMPDIR
+
+# Source networking configuration.
+. /etc/sysconfig/network
+
+if [ -f /etc/sysconfig/samba ]; then
+ . /etc/sysconfig/samba
+fi
+
+# Check that networking is up.
+[ ${NETWORKING} = "no" ] && exit 0
+
+# Check that smb.conf exists.
+[ -f /etc/samba/smb.conf ] || exit 0
+
+RETVAL=0
+
+
+start() {
+ KIND="Winbind"
+ echo -n $"Starting $KIND services: "
+ daemon winbindd "$WINBINDOPTIONS"
+ RETVAL=$?
+ echo
+ [ $RETVAL -eq 0 ] && touch /var/lock/subsys/winbindd || RETVAL=1
+ return $RETVAL
+}
+
+stop() {
+ echo
+ KIND="Winbind"
+ echo -n $"Shutting down $KIND services: "
+ killproc winbindd -TERM
+ RETVAL=$?
+ [ $RETVAL -eq 0 ] \
+ && && rm -f /var/run/nmbd.pid \
+ && rm -f /var/lock/subsys/winbindd
+ echo ""
+ return $RETVAL
+}
+
+restart() {
+ stop
+ start
+}
+
+reload() {
+ echo -n $"Reloading smb.conf file: "
+ killproc winbindd -HUP
+ RETVAL=$?
+ echo
+ return $RETVAL
+}
+
+rhstatus() {
+ status winbindd
+}
+
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ restart)
+ restart
+ ;;
+ reload)
+ reload
+ ;;
+ status)
+ rhstatus
+ ;;
+ condrestart)
+ [ -f /var/lock/subsys/winbindd ] && restart || :
+ ;;
+ *)
+ echo $"Usage: $0 {start|stop|restart|reload|status|condrestart}"
+ exit 1
+esac
+
+exit $?
diff --git a/packaging/LSB/README b/packaging/LSB/README
new file mode 100644
index 00000000000..4ff0b99d769
--- /dev/null
+++ b/packaging/LSB/README
@@ -0,0 +1,6 @@
+README.lsb - 1 July 2001
+------------------------
+
+The files in this directory allow you to build an LSB-compliant
+version of SAMBA using the RPM software and the LSB development
+environment.
diff --git a/packaging/LSB/lsb-samba.spec b/packaging/LSB/lsb-samba.spec
new file mode 100644
index 00000000000..516eaa430eb
--- /dev/null
+++ b/packaging/LSB/lsb-samba.spec
@@ -0,0 +1,100 @@
+#
+# "$Id: lsb-samba.spec,v 1.2 2001/07/03 01:01:12 jra Exp $"
+#
+# Linux Standards Based RPM "spec" file for SAMBA.
+#
+
+Summary: SAMBA
+Name: lsb-samba
+Version: 2.2.1
+Release: 0
+Copyright: GPL
+Group: System Environment/Daemons
+Source: ftp://ftp.samba.org/pub/samba/samba-%{version}.tar.gz
+Url: http://www.samba.org
+Packager: Michael Sweet <mike@easysw.com>
+Vendor: SAMBA Team
+
+# Require the "lsb" package, which guarantees LSB compliance.
+Requires: lsb
+
+# use BuildRoot so as not to disturb the version already installed
+BuildRoot: /var/tmp/%{name}-root
+
+%description
+
+%prep
+%setup
+
+%build
+export LDFLAGS="-L/usr/lib/lsb --dynamic-linker=/lib/ld-lsb.so.1"
+
+./configure --with-fhs --prefix=/usr --sysconfdir=/etc \
+ --sharedstatedir=/var --datadir=/usr/share \
+ --with-configdir=/etc/samba \
+ --with-swatdir=/usr/share/samba/swat
+
+# If we got this far, all prerequisite libraries must be here.
+make
+
+%install
+# Make sure the RPM_BUILD_ROOT directory exists.
+rm -rf $RPM_BUILD_ROOT
+mkdir $RPM_BUILD_ROOT
+
+make \
+ BASEDIR=$RPM_BUILD_ROOT/usr \
+ BINDIR=$RPM_BUILD_ROOT/usr/bin \
+ CODEPAGEDIR=$RPM_BUILD_ROOT/usr/share/samba/codepages \
+ CONFIGDIR=$RPM_BUILD_ROOT/etc/samba \
+ INCLUDEDIR=$RPM_BUILD_ROOT/usr/include \
+ LIBDIR=$RPM_BUILD_ROOT/usr/lib \
+ LOCKDIR=$RPM_BUILD_ROOT/var/lock/samba \
+ LOGFILEBASE=$RPM_BUILD_ROOT/var/log/samba \
+ MANDIR=$RPM_BUILD_ROOT/usr/share/man \
+ SBINDIR=$RPM_BUILD_ROOT/usr/sbin \
+ SWATDIR=$RPM_BUILD_ROOT/usr/share/samba/swat \
+ VARDIR=$RPM_BUILD_ROOT/var \
+ install
+
+mkdir -p $RPM_BUILD_ROOT/etc/init.d
+install -m 700 packaging/LSB/samba.sh /etc/init.d/samba
+
+mkdir -p $RPM_BUILD_ROOT/etc/samba
+install -m 644 packaging/LSB/smb.conf /etc/samba
+
+mkdir -p $RPM_BUILD_ROOT/etc/xinetd.d
+install -m 644 packaging/LSB/samba.xinetd /etc/xinetd.d/samba
+
+%post
+/usr/lib/lsb/install_initd /etc/init.d/samba
+
+%preun
+/usr/lib/lsb/remove_initd /etc/init.d/samba
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%files
+%defattr(-,root,root)
+%dir /etc/init.d
+/etc/init.d/samba
+%dir /etc/samba
+%config(noreplace) /etc/samba/smb.conf
+%dir /etc/samba/private
+%dir /etc/xinetd.d
+%config(noreplace) /etc/xinetd.d/samba
+%dir /usr/bin
+/usr/bin/*
+%dir /usr/sbin
+/usr/sbin/*
+%dir /usr/share/man
+/usr/share/man/*
+%dir /usr/share/samba
+/usr/share/samba/*
+%dir /var/lock/samba
+%dir /var/log/samba
+
+#
+# End of "$Id: lsb-samba.spec,v 1.2 2001/07/03 01:01:12 jra Exp $".
+#
diff --git a/packaging/LSB/samba.sh b/packaging/LSB/samba.sh
new file mode 100755
index 00000000000..99fa1b0117d
--- /dev/null
+++ b/packaging/LSB/samba.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+#
+# "$Id: samba.sh,v 1.2 2001/07/03 01:01:12 jra Exp $"
+#
+# SAMBA startup (init) script for LSB-compliant systems.
+#
+# Provides: smbd nmbd
+# Required-Start: 3 5
+# Required-Stop: 0 2 1 6
+# Default-Start: 3 5
+# Default-Stop: 0 2 1 6
+# Description: Starts and stops the SAMBA smbd and nmbd daemons \
+# used to provide SMB network services.
+#
+
+# Source LSB function library.
+. /lib/lsb/init-functions
+
+# Check that smb.conf exists.
+if test ! -f /etc/samba/smb.conf; then
+ log_failure_msg "The smb.conf file does not exist."
+ exit 6
+fi
+
+# Make sure that smbd and nmbd exist...
+if test ! -f /usr/sbin/nmbd -o ! -f /usr/sbin/smbd; then
+ log_failure_msg "The nmbd and/or smbd daemons are not installed."
+ exit 5
+fi
+
+# See how we were called.
+case "$1" in
+ start)
+ start_daemon nmbd -D
+ start_daemon smbd -D
+ log_success_msg "Started SMB services."
+ ;;
+
+ stop)
+ killproc smbd
+ killproc nmbd
+ log_success_msg "Shutdown SMB services."
+ ;;
+
+ reload)
+ # smbd and nmbd automatically re-read the smb.conf file...
+ log_success_msg "Reload not necessary with SAMBA."
+ ;;
+
+ status)
+ if test -z "`pidofproc smbd`"; then
+ log_success_msg "smbd is not running."
+ else
+ log_success_msg "smbd is running."
+ fi
+ if test -z "`pidofproc nmbd`"; then
+ log_success_msg "nmbd is not running."
+ else
+ log_success_msg "nmbd is running."
+ fi
+ ;;
+
+
+ restart | force-reload)
+ $0 stop
+ $0 start
+ ;;
+
+ *)
+ echo "Usage: smb {start|stop|reload|force-reload|restart|status}"
+ exit 1
+ ;;
+esac
+
+# Return "success"
+exit 0
+
+#
+# End of "$Id: samba.sh,v 1.2 2001/07/03 01:01:12 jra Exp $".
+#
diff --git a/packaging/LSB/samba.xinetd b/packaging/LSB/samba.xinetd
new file mode 100644
index 00000000000..8c38b354218
--- /dev/null
+++ b/packaging/LSB/samba.xinetd
@@ -0,0 +1,15 @@
+# default: off
+# description: SWAT is the Samba Web Admin Tool. Use swat \
+# to configure your Samba server. To use SWAT, \
+# connect to port 901 with your favorite web browser.
+service swat
+{
+ port = 901
+ socket_type = stream
+ wait = no
+ only_from = localhost
+ user = root
+ server = /usr/sbin/swat
+ log_on_failure += USERID
+ disable = yes
+}
diff --git a/packaging/LSB/smb.conf b/packaging/LSB/smb.conf
new file mode 100644
index 00000000000..71ff9463884
--- /dev/null
+++ b/packaging/LSB/smb.conf
@@ -0,0 +1,290 @@
+# This is the main Samba configuration file. You should read the
+# smb.conf(5) manual page in order to understand the options listed
+# here. Samba has a huge number of configurable options (perhaps too
+# many!) most of which are not shown in this example
+#
+# Any line which starts with a ; (semi-colon) or a # (hash)
+# is a comment and is ignored. In this example we will use a #
+# for commentry and a ; for parts of the config file that you
+# may wish to enable
+#
+# NOTE: Whenever you modify this file you should run the command "testparm"
+# to check that you have not made any basic syntactic errors.
+#
+#======================= Global Settings =====================================
+[global]
+
+# workgroup = NT-Domain-Name or Workgroup-Name
+ workgroup = MYGROUP
+
+# server string is the equivalent of the NT Description field
+ server string = Samba Server
+
+# This option is important for security. It allows you to restrict
+# connections to machines which are on your local network. The
+# following example restricts access to two C class networks and
+# the "loopback" interface. For more examples of the syntax see
+# the smb.conf man page
+; hosts allow = 192.168.1. 192.168.2. 127.
+
+# if you want to automatically load your printer list rather
+# than setting them up individually then you'll need this
+ printcap name = /etc/printcap
+ load printers = yes
+
+# It should not be necessary to spell out the print system type unless
+# yours is non-standard. Currently supported print systems include:
+# bsd, sysv, plp, lprng, aix, hpux, qnx
+; printing = bsd
+
+# Uncomment this if you want a guest account, you must add this to /etc/passwd
+# otherwise the user "nobody" is used
+; guest account = pcguest
+
+# this tells Samba to use a separate log file for each machine
+# that connects
+ log file = /var/log/samba/log.%m
+
+# Put a capping on the size of the log files (in Kb).
+ max log size = 50
+
+# Security mode. Most people will want user level security. See
+# security_level.txt for details.
+ security = user
+# Use password server option only with security = server
+; password server = <NT-Server-Name>
+
+# Password Level allows matching of _n_ characters of the password for
+# all combinations of upper and lower case.
+; password level = 8
+; username level = 8
+
+# You may wish to use password encryption. Please read
+# ENCRYPTION.txt, Win95.txt and WinNT.txt in the Samba documentation.
+# Do not enable this option unless you have read those documents
+; encrypt passwords = yes
+; smb passwd file = /etc/samba/smbpasswd
+
+# The following are needed to allow password changing from Windows to
+# update the Linux sytsem password also.
+# NOTE: Use these with 'encrypt passwords' and 'smb passwd file' above.
+# NOTE2: You do NOT need these to allow workstations to change only
+# the encrypted SMB passwords. They allow the Unix password
+# to be kept in sync with the SMB password.
+; unix password sync = Yes
+; passwd program = /usr/bin/passwd %u
+; passwd chat = *New*UNIX*password* %n\n *ReType*new*UNIX*password* %n\n *passwd:*all*authentication*tokens*updated*successfully*
+
+# Unix users can map to different SMB User names
+; username map = /etc/samba/smbusers
+
+# Using the following line enables you to customise your configuration
+# on a per machine basis. The %m gets replaced with the netbios name
+# of the machine that is connecting
+; include = /etc/samba/smb.conf.%m
+
+# Most people will find that this option gives better performance.
+# See speed.txt and the manual pages for details
+ socket options = TCP_NODELAY SO_RCVBUF=8192 SO_SNDBUF=8192
+
+# Configure Samba to use multiple interfaces
+# If you have multiple network interfaces then you must list them
+# here. See the man page for details.
+; interfaces = 192.168.12.2/24 192.168.13.2/24
+
+# Configure remote browse list synchronisation here
+# request announcement to, or browse list sync from:
+# a specific host or from / to a whole subnet (see below)
+; remote browse sync = 192.168.3.25 192.168.5.255
+# Cause this host to announce itself to local subnets here
+; remote announce = 192.168.1.255 192.168.2.44
+
+# Browser Control Options:
+# set local master to no if you don't want Samba to become a master
+# browser on your network. Otherwise the normal election rules apply
+; local master = no
+
+# OS Level determines the precedence of this server in master browser
+# elections. The default value should be reasonable
+; os level = 33
+
+# Domain Master specifies Samba to be the Domain Master Browser. This
+# allows Samba to collate browse lists between subnets. Don't use this
+# if you already have a Windows NT domain controller doing this job
+; domain master = yes
+
+# Preferred Master causes Samba to force a local browser election on startup
+# and gives it a slightly higher chance of winning the election
+; preferred master = yes
+
+# Use only if you have an NT server on your network that has been
+# configured at install time to be a primary domain controller.
+; domain controller = <NT-Domain-Controller-SMBName>
+
+# Enable this if you want Samba to be a domain logon server for
+# Windows95 workstations.
+; domain logons = yes
+
+# if you enable domain logons then you may want a per-machine or
+# per user logon script
+# run a specific logon batch file per workstation (machine)
+; logon script = %m.bat
+# run a specific logon batch file per username
+; logon script = %U.bat
+
+# Where to store roving profiles (only for Win95 and WinNT)
+# %L substitutes for this servers netbios name, %U is username
+# You must uncomment the [Profiles] share below
+; logon path = \\%L\Profiles\%U
+
+# All NetBIOS names must be resolved to IP Addresses
+# 'Name Resolve Order' allows the named resolution mechanism to be specified
+# the default order is "host lmhosts wins bcast". "host" means use the unix
+# system gethostbyname() function call that will use either /etc/hosts OR
+# DNS or NIS depending on the settings of /etc/host.config, /etc/nsswitch.conf
+# and the /etc/resolv.conf file. "host" therefore is system configuration
+# dependant. This parameter is most often of use to prevent DNS lookups
+# in order to resolve NetBIOS names to IP Addresses. Use with care!
+# The example below excludes use of name resolution for machines that are NOT
+# on the local network segment
+# - OR - are not deliberately to be known via lmhosts or via WINS.
+; name resolve order = wins lmhosts bcast
+
+# Windows Internet Name Serving Support Section:
+# WINS Support - Tells the NMBD component of Samba to enable it's WINS Server
+; wins support = yes
+
+# WINS Server - Tells the NMBD components of Samba to be a WINS Client
+# Note: Samba can be either a WINS Server, or a WINS Client, but NOT both
+; wins server = w.x.y.z
+
+# WINS Proxy - Tells Samba to answer name resolution queries on
+# behalf of a non WINS capable client, for this to work there must be
+# at least one WINS Server on the network. The default is NO.
+; wins proxy = yes
+
+# DNS Proxy - tells Samba whether or not to try to resolve NetBIOS names
+# via DNS nslookups. The built-in default for versions 1.9.17 is yes,
+# this has been changed in version 1.9.18 to no.
+ dns proxy = no
+
+# Case Preservation can be handy - system default is _no_
+# NOTE: These can be set on a per share basis
+; preserve case = no
+; short preserve case = no
+# Default case is normally upper case for all DOS files
+; default case = lower
+# Be very careful with case sensitivity - it can break things!
+; case sensitive = no
+
+#============================ Share Definitions ==============================
+[homes]
+ comment = Home Directories
+ browseable = no
+ writable = yes
+
+# Un-comment the following and create the netlogon directory for Domain Logons
+; [netlogon]
+; comment = Network Logon Service
+; path = /home/netlogon
+; guest ok = yes
+; writable = no
+; share modes = no
+
+
+# Un-comment the following to provide a specific roving profile share
+# the default is to use the user's home directory
+;[Profiles]
+; path = /home/profiles
+; browseable = no
+; guest ok = yes
+
+
+# NOTE: If you have a BSD-style print system there is no need to
+# specifically define each individual printer
+[printers]
+ comment = All Printers
+ path = /var/spool/samba
+ browseable = no
+# Set public = yes to allow user 'guest account' to print
+ guest ok = no
+ writable = no
+ printable = yes
+
+# This one is useful for people to share files
+;[tmp]
+; comment = Temporary file space
+; path = /tmp
+; read only = no
+; public = yes
+
+# A publicly accessible directory, but read only, except for people in
+# the "staff" group
+;[public]
+; comment = Public Stuff
+; path = /home/samba
+; public = yes
+; read only = yes
+; write list = @staff
+
+# Other examples.
+#
+# A private printer, usable only by fred. Spool data will be placed in fred's
+# home directory. Note that fred must have write access to the spool directory,
+# wherever it is.
+;[fredsprn]
+; comment = Fred's Printer
+; valid users = fred
+; path = /homes/fred
+; printer = freds_printer
+; public = no
+; writable = no
+; printable = yes
+
+# A private directory, usable only by fred. Note that fred requires write
+# access to the directory.
+;[fredsdir]
+; comment = Fred's Service
+; path = /usr/somewhere/private
+; valid users = fred
+; public = no
+; writable = yes
+; printable = no
+
+# a service which has a different directory for each machine that connects
+# this allows you to tailor configurations to incoming machines. You could
+# also use the %u option to tailor it by user name.
+# The %m gets replaced with the machine name that is connecting.
+;[pchome]
+; comment = PC Directories
+; path = /usr/pc/%m
+; public = no
+; writable = yes
+
+# A publicly accessible directory, read/write to all users. Note that all files
+# created in the directory by users will be owned by the default user, so
+# any user with access can delete any other user's files. Obviously this
+# directory must be writable by the default user. Another user could of course
+# be specified, in which case all files would be owned by that user instead.
+;[public]
+; path = /usr/somewhere/else/public
+; public = yes
+; only guest = yes
+; writable = yes
+; printable = no
+
+# The following two entries demonstrate how to share a directory so that two
+# users can place files there that will be owned by the specific users. In this
+# setup, the directory should be writable by both users and should have the
+# sticky bit set on it to prevent abuse. Obviously this could be extended to
+# as many users as required.
+;[myshare]
+; comment = Mary's and Fred's stuff
+; path = /usr/somewhere/shared
+; valid users = mary fred
+; public = no
+; writable = yes
+; printable = no
+; create mask = 0765
+
+
diff --git a/packaging/Mandrake/.cvsignore b/packaging/Mandrake/.cvsignore
new file mode 100644
index 00000000000..ffcc2e7e5ee
--- /dev/null
+++ b/packaging/Mandrake/.cvsignore
@@ -0,0 +1,2 @@
+makerpms.sh
+samba2.spec
diff --git a/packaging/Mandrake/README b/packaging/Mandrake/README
new file mode 100644
index 00000000000..5ccfb1d22b3
--- /dev/null
+++ b/packaging/Mandrake/README
@@ -0,0 +1,11 @@
+Preparation Date: Sat Apr 14 2001
+Preparer: Gerald Carter <jerry@samba.org>
+
+Instructions: Preparing Samba Packages for Mandrake Linux 8.x
+===============================================================
+
+We provide support only for current versions of Mandrake Linux.
+
+To produce the RPMS simply type:
+ sh makerpms.sh
+
diff --git a/packaging/Mandrake/README.mandrake b/packaging/Mandrake/README.mandrake
new file mode 100644
index 00000000000..83920f238f1
--- /dev/null
+++ b/packaging/Mandrake/README.mandrake
@@ -0,0 +1,117 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA1
+
+The Mandrake samba spec file used for packaging for Mandrake and cooker
+is now also used by the makerpms.sh script, and you can pass any options
+that you would normally pass to rpm via the makerpms.sh script.
+
+Note that only release after 8.1 support passing options using
+"--with <option>" syntax. The following options are available when
+building with the script:
+
+- --with|--without ldap
+- --with|--without acl
+- --with|--without winbind
+- --with|--without wins
+- --with vscan
+Using "--with vscan" requires that you place a copy of the samba-vscan
+tarball (coverted to bz2 using bzme) from:
+http://prdownloads.sourceforge.net/openantivirus/samba-vscan-0.2.5a.tar.gz?download in your rpm source directory:
+
+cp samba-vscan-0.2.5a.tar.gz `rpm --eval "%_sourcedir"`
+bzme `rpm --eval "%_sourcedir"`/samba-vscan-0.2.5a.tar.gz
+
+When using "--with vscan" you will probably want to enable a virus scanner
+also:
+- --with fprot
+- --with kaspersky
+- --with mks
+- --with openantivirus
+- --with sophos
+- --with symantec
+- --with trend
+
+You may need to do additional setup for your virus scanner to get this to
+work, please see ../../examples/VFS/<scanner>/INSTALL (after having started
+a build with "sh makerpms.sh --with vscan")
+
+Note that only sophos has been tested with the spec, but the rest should work
+(after the preparation). Reports of working scanners are welcome, and please
+report any that do not work (preferably with a fix :-)).
+
+So, to build ldap-enable RPMs for with antivirus support with sophos, but
+no acl support, use:
+sh makerpms.sh --with ldap --with vscan --with sophos --without acl
+
+The same features can be enabled on Mandrake 7.2/8.0 by editing samba2.spec,
+and changing the 0 to 1 to enable the option:
+
+%if %build_mdk80
+%define build_acl 0
+%define build_winbind 0
+%define build_wins 0
+%define build_ldap 0
+%endif
+
+Similar sections exist for the virus scanning options.
+
+(If you haven't rebuilt RPMs before, it is suggested that you take a
+brief look through http://www.linux-mandrake.com/howtos/mdk-rpm/,
+specifically sections 3.1 and 3.2)
+
+The defaults for Mandrake 8.1 and 8.2 are shown below:
+%if %build_mdk82
+%define build_acl 1
+%define build_winbind 1
+%define build_wins 1
+%define build_ldap 0
+%endif
+
+%if %build_mdk81
+%define build_acl 1
+%define build_winbind 0
+%define build_wins 0
+%define build_ldap 0
+%endif
+
+Note that building with LDAP support will only allow you to use smbpasswd's
+stored in ldap, not in the local smbpasswd file. From 2.2.5 onwards in the
+2.2 tree, ldap-enable RPMs now carry an ldap modifier to indicate this (for
+example, samba-server becomes samba-server-ldap).
+
+Building with acl requires that you have the acl development libraries.
+The libraries are standard on 8.1 and 8.2.
+ACLs can be used on Mandrake 8.0 with the updated kernel (to support XFS)
+if you install the RPMs available at
+http://ranger.dnsalias.com/mandrake/samba/RPMS/8.0/samba-2.2.1a_xfs/
+
+Note that precompiled binaries of samba are also available on my site:
+http://ranger.dnsalias.com/mandrake/samba
+and that of Sylvestre Taburet:
+http://people.mandrakesoft.com/~staburet/samba/
+
+If you have any problems with these RPMs, please send email to both
+bgmilne@cae.co.za and staburet@mandrakesoft.com
+
+These RPMs are provided as-is, are not official, and can not be
+supported by Mandrakesoft. However, we will do our best to provide
+reliable and well-packaged RPMS, since we use them on our own servers
+and those of clients.
+
+This file is signed with my gpg key, a copy of my public key is available
+at http://ranger.dnsalias.com/bgmilne.asc. The finger-print for this key
+follows:
+1024D/60D204A7 2919 E232 5610 A038 87B1 72D6 AC92 BA50 60D2 04A7
+
+Recent posts by me on samba@samba.org have been signed with this key,
+in case you really want to verify who I am to some extent.
+
+Buchan Milne
+18 June 2002
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.0.6 (GNU/Linux)
+
+iD8DBQE9D2h3rJK6UGDSBKcRAhtJAKC0fVkIMETgRxccrwLBcKZKHllCIACeNcZ9
+bxGZSTOHs57ir3wFR+3s0XM=
+=ifKf
+-----END PGP SIGNATURE-----
diff --git a/packaging/Mandrake/empty.patch b/packaging/Mandrake/empty.patch
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/packaging/Mandrake/empty.patch
diff --git a/packaging/Mandrake/findsmb b/packaging/Mandrake/findsmb
new file mode 100755
index 00000000000..f70d18dcbdc
--- /dev/null
+++ b/packaging/Mandrake/findsmb
@@ -0,0 +1,141 @@
+#!/usr/bin/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 [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.
+#
+
+$SAMBABIN = "/usr/bin";
+
+for ($i = 0; $i < 2; $i++) { # test for -d option and broadcast address
+ $_ = shift;
+ if (m/-d|-D/) {
+ $DEBUG = 1;
+ } else {
+ if ($_) {
+ $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>.*$//,<NMBLOOKUP>);
+
+# 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 -A $ip|") ||
+ die("Can't get nmb name list.\n");
+ @nmblookup = <NMBLOOKUP>;
+ close NMBLOOKUP;
+
+# get the first <00> name
+
+ @name = grep(/<00>/,@nmblookup);
+ $_ = @name[0];
+ if ($_) { # we have a netbios name
+ if (/GROUP/) { # is it a group name
+ ($name, $aliases, $type, $length, @addresses) =
+ gethostbyaddr(pack('C4',split('\.',$ip)),2);
+ if (! $name) { # could not get name
+ $name = "unknown nis name";
+ }
+ } else {
+ /(.{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 = <SMB>;
+ close SMB;
+
+ if ($DEBUG) { # if -d flag print results of nmblookup and smbclient
+ print "===============================================================\n";
+ print @nmblookup;
+ print @smb;
+ }
+
+# look for the OS= string
+
+ @info = grep(/OS=/,@smb);
+ $_ = @info[0];
+ if ($_) { # we found response
+ s/Domain=|OS=|Server=|\n//g; # strip out descriptions to make line shorter
+
+ } else { # no OS= string in response (WIN95 client)
+
+# for WIN95 clients get workgroup name from nmblookup response
+ @name = grep(/<00> - <GROUP>/,@nmblookup);
+ $_ = @name[0];
+ if ($_) {
+ /(.{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/packaging/Mandrake/makerpms-cvs.sh b/packaging/Mandrake/makerpms-cvs.sh
new file mode 100644
index 00000000000..08c4370b314
--- /dev/null
+++ b/packaging/Mandrake/makerpms-cvs.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+# makerpms-cvs.sh
+# A quick script to build RPMs from cvs to test packaging
+# Buchan Milne <bgmilne@cae.co.za>
+
+[ $# -lt 1 ] && echo "Usage: $0 <Samba version>" && exit 1
+
+VERSION=$1
+RELEASE=0.`date +%Y%m%d`
+shift
+
+# Replace PRELEASE and PVERSION with release number in all files ending with
+# .tmpl
+
+FILES=$(find . -name "*.tmpl" -type f)
+
+for i in $FILES;do
+ NEW=$(echo $i|sed -e 's/\.tmpl//g');
+ cat $i |sed -e 's/PVERSION/'$VERSION'/g; s/PRELEASE/'$RELEASE'/g'> $NEW ;
+done
+
+#Change up three directories, rename directory to samba-$VERSION, change back
+#then run makerpms.sh
+
+
+CURRENT=$(pwd)
+pushd $(dirname $(dirname $(dirname $CURRENT)))
+SAMBA_DIR=$(basename $(dirname $(dirname $CURRENT)))
+mv $SAMBA_DIR samba-$VERSION
+pushd samba-$VERSION/source
+./autogen.sh
+popd
+pushd samba-$VERSION/packaging/Mandrake
+sh makerpms.sh $@
+popd
+mv samba-$VERSION $SAMBA_DIR
+popd
diff --git a/packaging/Mandrake/makerpms.sh.tmpl b/packaging/Mandrake/makerpms.sh.tmpl
new file mode 100644
index 00000000000..5d06e2bbcca
--- /dev/null
+++ b/packaging/Mandrake/makerpms.sh.tmpl
@@ -0,0 +1,77 @@
+#!/bin/sh
+# Copyright (C) John H Terpstra 1998-2002
+# Updated for RPM 3 by Jochen Wiedmann, joe@ispsoft.de
+# Changed for a generic tar file rebuild by abartlet@pcug.org.au
+# Taken from Red Hat build area by JHT
+# Changed by John H Terpstra to build on RH8.1 - should also work for earlier versions jht@samba.org
+# Changes from Buchan Milne <bgmilne@cae.co.za>
+
+# The following allows environment variables to override the target directories
+# the alternative is to have a file in your home directory calles .rpmmacros
+# containing the following:
+# %_topdir /home/mylogin/RPM
+#
+
+# rpm --eval should always give a correct answer for this
+SPECDIR=`rpm "$@" --eval "%{_specdir}"`
+SRCDIR=`rpm "$@" --eval "%{_sourcedir}"`
+
+# At this point the (SPECDIR and) SRCDIR vaiables must have a value!
+
+USERID=`id -u`
+GRPID=`id -g`
+VERSION='PVERSION'
+
+RPMVER=`rpm --version | awk '{print $3}'`
+echo The RPM Version on this machine is: $RPMVER
+
+case $RPMVER in
+ 2*)
+ echo Building for RPM v2.x
+ sed -e "s/MANDIR_MACRO/\%\{prefix\}\/man/g" < samba2.spec > samba.spec
+ ;;
+ 3*)
+ echo Building for RPM v3.x
+ sed -e "s/MANDIR_MACRO/\%\{prefix\}\/man/g" < samba2.spec > samba.spec
+ ;;
+ 4*)
+ echo Building for RPM v4.x
+ sed -e "s/MANDIR_MACRO/\%\{_mandir\}/g" < samba2.spec > samba.spec
+ ;;
+ *)
+ echo "Unknown RPM version: `rpm --version`"
+ exit 1
+ ;;
+esac
+
+( cd ../../source; if [ -f Makefile ]; then make distclean; fi )
+( cd ../../.. ; chown -R ${USERID}.${GRPID} samba-${VERSION} )
+echo "Compressing the source as bzip2, may take a while ..."
+( cd ../../.. ; tar --exclude=CVS -cjf ${SRCDIR}/samba-${VERSION}.tar.bz2 samba-${VERSION} )
+
+cp -av samba.spec ${SPECDIR}
+# cp -a *.patch.bz2 *.xpm.bz2 smb.* samba.xinetd samba.log $SRCDIR
+# Prepare to allow straight patches synced from Mandrake cvs:
+# Updating of sources and patches can be done more easily and accurately
+# by using info in the spec file. It won't work for files that use an rpm
+# macro in their name, but that shouldn't be a problem.
+
+SOURCES=`awk '/^Source/ {print $2}' samba.spec |grep -v "%{"`
+PATCHES=`awk '/^Patch/ {print $2}' samba.spec`
+
+for i in $PATCHES $SOURCES;do
+ # We have two cases to fix, one where it's bzip2'ed
+ # in the spec and not in CVS, one where it's bzip2'ed
+ # in CVS but not in the spec
+ [ -e $i ] && cp -av $i $SRCDIR
+ i_nobz2=`echo $i|sed -e 's/.bz2$//'`
+ i_bz2=$i.bz2
+ [ -e $i_nobz2 ] && bzip2 -kf $i_nobz2 && mv -fv $i $SRCDIR
+ [ -e $i_bz2 ] && bunzip2 -kf $i_bz2 && mv -fv $i $SRCDIR
+done
+
+echo Getting Ready to build release package
+cd ${SPECDIR}
+rpm -ba -v --clean --rmsource samba.spec $@
+
+echo Done.
diff --git a/packaging/Mandrake/mount.cifs.8 b/packaging/Mandrake/mount.cifs.8
new file mode 100644
index 00000000000..7ab1be305c2
--- /dev/null
+++ b/packaging/Mandrake/mount.cifs.8
@@ -0,0 +1,181 @@
+.\" This manpage has been automatically generated by docbook2man
+.\" from a DocBook document. This tool can be found at:
+.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/>
+.\" Please send any bug reports, improvements, comments, patches,
+.\" etc. to Steve Cheng <steve@ggi-project.org>.
+.TH "MOUNT.CIFS" "8" "03 August 2002" "" ""
+.SH NAME
+mount.cifs \- mount using the Common Internet File System (CIFS)
+.SH SYNOPSIS
+
+\fBmount.cifs\fR \fBservice\fR \fBmount-point\fR [ \fB-o options\fR]
+
+.SH "DESCRIPTION"
+.PP
+\fBmount.cifs\fR mounts a Linux CIFS filesystem. It
+is usually invoked as \fBmount.cifs\fR by
+the \fBmount(8)\fR command when using the
+"-t cifs" option. This command only works in Linux, and the kernel must
+support the cifs filesystem. The CIFS protocol is the successor to the
+SMB protocol and is supported by most Windows servers and many other
+commercial servers and Network Attached Storage appliances as well as
+by the popular Open Source server Samba.
+.PP
+Options to \fBmount.cifs\fR are specified as a comma-separated
+list of key=value pairs. It is possible to send options other
+than those listed here, assuming that cifs supports them. If
+you get mount failures, check your kernel log for errors on
+unknown options.
+.PP
+\fBmount.cifs\fR is a daemon. After mounting it keeps running until
+the mounted cifs is umounted. It will log things that happen
+when in daemon mode using the "machine name" mount.cifs, so
+typically this output will end up in
+\fIlog.mount.cifs\fR.
+.SH "OPTIONS"
+.TP
+\fBusername=<arg>\fR
+specifies the username to connect as. If
+this is not given, then the environment variable \fB USER\fR is used. This option can also take the
+form "user%password" or "user/workgroup" or
+"user/workgroup%password" to allow the password and workgroup
+to be specified as part of the username.
+.TP
+\fBpassword=<arg>\fR
+specifies the CIFS password. If this
+option is not given then the environment variable
+\fBPASSWD\fR is used. If it can find
+no password \fBmount.cifs\fR will prompt
+for a passeword, unless the guest option is
+given.
+
+Note that password which contain the arguement delimiter
+character (i.e. a comma ',') will failed to be parsed correctly
+on the command line. However, the same password defined
+in the PASSWD environment variable or a credentials file (see
+below) will be read correctly.
+.TP
+\fBcredentials=<filename>\fR
+specifies a file that contains a username
+and/or password. The format of the file is:
+
+
+.nf
+ username = <value>
+ password = <value>
+
+.fi
+
+This is preferred over having passwords in plaintext in a
+shared file, such as \fI/etc/fstab\fR. Be sure to protect any
+credentials file properly.
+.TP
+\fBuid=<arg>\fR
+sets the uid that will own all files on
+the mounted filesystem.
+It may be specified as either a username or a numeric uid.
+.TP
+\fBgid=<arg>\fR
+sets the gid that will own all files on
+the mounted filesystem.
+It may be specified as either a groupname or a numeric
+gid.
+.TP
+\fBport=<arg>\fR
+sets the remote cifs port number. By default
+port 445 is tried then if no response port 139 is tried.
+.TP
+\fBfmask=<arg>\fR
+sets the file mask. This determines the
+permissions that remote files have in the local filesystem.
+The default is based on the current umask.
+.TP
+\fBdmask=<arg>\fR
+sets the directory mask. This determines the
+permissions that remote directories have in the local filesystem.
+The default is based on the current umask.
+.TP
+\fBdebug=<arg>\fR
+sets the debug level. This is useful for
+tracking down cifs connection problems. A suggested value to
+start with is 4. If set too high there will be a lot of
+output, possibly hiding the useful output.
+.TP
+\fBip=<arg>\fR
+sets the destination host or IP address.
+.TP
+\fBdomain=<arg>\fR
+sets the domain (workgroup) of the user
+.TP
+\fBguest\fR
+don't prompt for a password
+.TP
+\fBro\fR
+mount read-only
+.TP
+\fBrw\fR
+mount read-write
+.TP
+\fBiocharset=<arg>\fR
+sets the charset used by the Linux side for codepage
+to charset translations (NLS). Argument should be the
+name of a charset, like iso8859-1. (Note: only kernel
+2.4.0 or later)
+.TP
+\fBcodepage=<arg>\fR
+sets the codepage the server uses. See the iocharset
+option. Example value cp850. (Note: only kernel 2.4.0
+or later)
+.TP
+\fBttl=<arg>\fR
+how long a directory listing is cached in milliseconds
+(also affects visibility of file size and date
+changes). A higher value means that changes on the
+server take longer to be noticed but it can give
+better performance on large directories, especially
+over long distances. Default is 1000ms but something
+like 10000ms (10 seconds) is probably more reasonable
+in many cases.
+(Note: only kernel 2.4.2 or later)
+.SH "ENVIRONMENT VARIABLES"
+.PP
+The variable \fBUSER\fR may contain the username of the
+person using the client. This information is used only if the
+protocol level is high enough to support session-level
+passwords. The variable can be used to set both username and
+password by using the format username%password.
+.PP
+The variable \fBPASSWD\fR may contain the password of the
+person using the client. This information is used only if the
+protocol level is high enough to support session-level
+passwords.
+.PP
+The variable \fBPASSWD_FILE\fR may contain the pathname
+of a file to read the password from. A single line of input is
+read and used as the password.
+.SH "BUGS"
+.PP
+Passwords and other options containing , can not be handled.
+For passwords an alternative way of passing them is in a credentials
+file or in the PASSWD environment.
+.PP
+The credentials file does not handle usernames or passwords with
+leading space.
+.PP
+Note that the typical response to a bug report is suggestion
+to try the latest version first. So please try doing that first,
+and always include which versions you use of relevant software
+when reporting bugs (minimum: samba, kernel, distribution)
+.SH "SEE ALSO"
+.PP
+Documentation/filesystems/cifs.txt in the linux kernel
+source tree may contain additional options and information.
+.SH "AUTHOR"
+.PP
+Steve French
+The syntax and manpage were loosely based on that of smbmount.
+.PP
+The current maintainer of the Linux cifs vfs and the userspace
+tool \fBmount.cifs\fR is Steve French <URL:mailto:sfrench@samba.org>.
+The SAMBA Mailing list <URL:mailto:samba@samba.org>
+is the preferred place to ask questions regarding these programs.
diff --git a/packaging/Mandrake/samba-2.2.0-buildroot.patch b/packaging/Mandrake/samba-2.2.0-buildroot.patch
new file mode 100644
index 00000000000..72091a13bcb
--- /dev/null
+++ b/packaging/Mandrake/samba-2.2.0-buildroot.patch
@@ -0,0 +1,15 @@
+--- samba-2.2.0/source/script/installbin.sh Fri Jan 12 21:28:02 2001
++++ samba-2.2.0/source/script/installbin.sh.207 Thu Apr 19 09:40:57 2001
+@@ -33,9 +33,9 @@
+ 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
++ #if [ $p2 = smbmount ]; then
++ # ln -sf $BINDIR/$p2 /sbin/mount.smbfs
++ #fi
+ done
+
+
diff --git a/packaging/Mandrake/samba-3.0-smbmount-sbin.patch b/packaging/Mandrake/samba-3.0-smbmount-sbin.patch
new file mode 100644
index 00000000000..586fb3cff9c
--- /dev/null
+++ b/packaging/Mandrake/samba-3.0-smbmount-sbin.patch
@@ -0,0 +1,11 @@
+--- samba-3.0alpha1/source/client/smbmount.c.orig Fri Nov 30 02:29:22 2001
++++ samba-3.0alpha1/source/client/smbmount.c Thu Dec 6 00:48:57 2001
+@@ -468,7 +468,7 @@
+ if (sys_fork() == 0) {
+ char *smbmnt_path;
+
+- asprintf(&smbmnt_path, "%s/smbmnt", dyn_BINDIR);
++ asprintf(&smbmnt_path, "%s/smbmnt", dyn_SBINDIR);
+
+ if (file_exist(smbmnt_path, NULL)) {
+ execv(smbmnt_path, args);
diff --git a/packaging/Mandrake/samba-print-pdf.sh b/packaging/Mandrake/samba-print-pdf.sh
new file mode 100644
index 00000000000..973bb4f90ee
--- /dev/null
+++ b/packaging/Mandrake/samba-print-pdf.sh
@@ -0,0 +1,111 @@
+#!/bin/bash
+# samba-print-pdf
+# This is a script which allows you to set up a virtual printer on samba
+# which will take the file (generated by a postscript filter on windows)
+# and turn it into a PDF, informing the user of where it is when it
+# is done
+#
+# (c) Buchan Milne <bgmilne@cae.co.za> 2002
+# License: GPLv2
+# Changelog
+# v0.0.6 20030428
+# - Allow options passed as env. variables from print command
+# - Inline and simplify sed (use tr) clean script
+# - Ensure file arrives in PREFIX even if TEMP is used without provided name
+# - Changes from Joshua M. Schmidlkofer <joshua@imr-net.com> 20030425
+# - Debugging, adjustments, and corrections.
+# - Stupid sed sanitizing script. [probably horribly inefficient also].
+# - Temp file usage cleanup.
+# v0.0.5 20020723
+# - Add support for preset settings
+# - Allow passing of filename provided by client as final filename
+#
+# Arguments:
+# $1 = file (usually passed with %s from samba)
+# $2 = unix prefix to where to place the file (~%u should work)
+# $3 = windows prefix to the same location (//%L/%u should work)
+# $4 = user/computer to send a notification to (%u or %m)
+# $5 = IP address of client (%I)
+# $6 = Name of destination file without extension (%J)
+# $7 = PDF setting (prepress,print,screen etc)
+#
+# If you want to customise any of the following configuration defaults,
+# you can place them in the file /etc/samba/print-pdf.conf.
+# If you need to modify anything in this script, please provide me with your
+# changes, preferably in such a way that the changes are configurable.
+
+PS2PDF=ps2pdf13
+OPTIONS="-dAutoFilterColorImages=false -sColorImageFilter=FlateEncode"
+#Values taken from arguments:
+INPUT=$1
+PREFIX="$2"
+WINBASE=$(echo "$3"|sed -e 's,/,\\\\,g')
+#NAME=`echo "$6"|sed -e 's/[&/:{}\\\[<>$#@*^!?=|]/-/g;s/\]/-/g'`
+NAME=`echo "$6"|tr '[:punct:]' '[-*]'`
+
+# Source config file if it exists:
+CONFFILE=/etc/samba/print-pdf.conf
+[ -e $CONFFILE ] && . $CONFFILE
+
+#Values not taken as arguments, could be set via env. vars (?) or config file
+KEEP_PS=${KEEP_PS=0}
+PERMS=${PERMS=640}
+BASEFILE=${BASEFILE=pdf-service}
+TEMP="${TEMP=$2}"
+UMASK=${UMASK=006}
+
+#Make sure that destination directory exists
+mkdir -p "$PREFIX"
+
+INFILE=$(basename $INPUT)
+
+umask $UMASK
+
+[ -n "$NAME" ] && TEMP="$PREFIX"
+
+#make a temp file to use for the output of the PDF
+OUTPUT=`mktemp -q $TEMP/$BASEFILE-XXXXXX`
+if [ $? -ne 0 ]; then
+ echo "$0: Can't create temp file $TEMP/$OUTPUT, exiting..."
+ exit 1
+fi
+if [ -n "$NAME" ]; then
+ FINALOUTPUT="$PREFIX/$NAME"
+else
+ FINALOUTPUT="$OUTPUT"
+fi
+if [ -n "$7" ]; then
+ OPTIONS="$OPTIONS -dPDFSETTINGS=/${7#pdf-}"
+else
+ OPTIONS="$OPTIONS -dPDFSETTINGS=/default"
+fi
+
+WIN_OUTPUT="$WINBASE\\"`basename "$FINALOUTPUT"`
+#mv "$INPUT" "$INPUT.ps";INPUT="$INPUT.ps"
+
+# create the pdf
+$PS2PDF $OPTIONS "$INPUT" "$OUTPUT.pdf" >/dev/null 2>&1
+mv -f "${OUTPUT}.pdf" "${FINALOUTPUT}".pdf
+
+# Generate a message to send to the user, and deal with the original file:
+MESSAGE=$(echo "Your PDF file has been created as $WIN_OUTPUT.pdf\n")
+
+
+# Cleanup
+if [ $KEEP_PS != 0 ];then
+ mv -f $INPUT "${FINALOUTPUT}".ps
+ MESSAGE=$(echo "$MESSAGE and your postscript file as $WIN_OUTPUT.ps")
+ # Fix permissions on the generated files
+ chmod $PERMS "${FINALOUTPUT}".ps "${FINALOUTPUT}".pdf
+else
+ rm -f $INPUT
+ # Fix permissions on the generated files
+ chmod $PERMS "${FINALOUTPUT}".pdf
+fi
+
+#Remove empty file from mktemp:
+rm -f $OUTPUT
+
+# Send notification to user
+echo -e $MESSAGE|smbclient -M $4 -I $5 -U "PDF Generator" >/dev/null 2>&1
+
diff --git a/packaging/Mandrake/samba-slapd-include.conf b/packaging/Mandrake/samba-slapd-include.conf
new file mode 100644
index 00000000000..9c065b6f737
--- /dev/null
+++ b/packaging/Mandrake/samba-slapd-include.conf
@@ -0,0 +1,15 @@
+# You should either include this file into your
+# /etc/openldap/slapd.conf, or add the contents (after editing), inside
+# the db definition your samba server will use.
+
+
+# Index the rid for samba:
+index rid eq
+
+
+# Basic samba acl:
+access to attrs=lmPassword,ntPassword
+ by dn="cn=root,dc=mydomain,dc=com" write
+ by dn="uid=root,ou=People,dc=mydomain,dc=com" write
+ by * none
+
diff --git a/packaging/Mandrake/samba.log b/packaging/Mandrake/samba.log
new file mode 100644
index 00000000000..4e8b44b60e7
--- /dev/null
+++ b/packaging/Mandrake/samba.log
@@ -0,0 +1,15 @@
+/var/log/samba/log.nmb {
+ notifempty
+ missingok
+ postrotate
+ /usr/bin/killall -HUP nmbd
+ endscript
+}
+
+/var/log/samba/log.smb {
+ notifempty
+ missingok
+ postrotate
+ /usr/bin/killall -HUP smbd
+ endscript
+}
diff --git a/packaging/Mandrake/samba.pamd b/packaging/Mandrake/samba.pamd
new file mode 100644
index 00000000000..30912de1726
--- /dev/null
+++ b/packaging/Mandrake/samba.pamd
@@ -0,0 +1,5 @@
+#%PAM-1.0
+auth required /lib/security/pam_nologin.so
+auth required /lib/security/pam_stack.so service=system-auth
+account required /lib/security/pam_stack.so service=system-auth
+session required /lib/security/pam_stack.so service=system-auth
diff --git a/packaging/Mandrake/samba.xinetd b/packaging/Mandrake/samba.xinetd
new file mode 100644
index 00000000000..59910336bba
--- /dev/null
+++ b/packaging/Mandrake/samba.xinetd
@@ -0,0 +1,15 @@
+# default: on
+# description: SWAT is the Samba Web Admin Tool. Use swat \
+# to configure your Samba server. To use SWAT, \
+# connect to port 901 with your favorite web browser.
+service swat
+{
+ port = 901
+ socket_type = stream
+ wait = no
+ only_from = 127.0.0.1
+ user = root
+ server = /usr/sbin/swat
+ log_on_failure += USERID
+ disable = no
+}
diff --git a/packaging/Mandrake/samba2.spec.tmpl b/packaging/Mandrake/samba2.spec.tmpl
new file mode 100644
index 00000000000..127aee48d8e
--- /dev/null
+++ b/packaging/Mandrake/samba2.spec.tmpl
@@ -0,0 +1,2344 @@
+# Note that this file exists in Mandrake packaging cvs (as samba3.spec)
+# and samba cvs (as packaging/Mandrake/samba2.spec.tmpl).
+# Keep in mind that any changes should take both locations into account
+# Considerable effort has gone into making this possible, so that only
+# one spec file is maintained, please don't break it.
+# It should be possible, without any changes to this file, to build
+# binary packages on most recent Mandrake releases:
+# 1)from official source releases, using 'cd packaging/Mandrake; sh makerpms.sh'
+# 2)from cvs snapshots, using 'cd packaging/Mandrake; sh makerpms-cvs.sh <ver>'
+# 3)using official source releases and updated Mandrake packaging, by
+# 'rpm -ba samba.spec'
+# As such, any sources or patches used in a build from a samba release or
+# cvs should be submitted for inclusion in samba cvs.
+
+%define pkg_name samba
+%define ver 3.0.1pre3
+%define rel 5mdk
+%define vscanver 0.3.3beta1
+%define libsmbmajor 0
+
+%{!?lib: %global lib lib}
+%{!?mklibname: %global mklibname(ds) %lib%{1}%{?2:%{2}}%{?3:_%{3}}%{-s:-static}%{-d:-devel}}
+
+%define libname %mklibname smbclient %libsmbmajor
+
+# Version and release replaced by samba-team at release from samba cvs
+%define pversion PVERSION
+%define prelease PRELEASE
+
+#Check to see if p(version|release) has been replaced (1 if replaced)
+%define have_pversion %(if [ "%pversion" = `echo "pversion" |tr '[:lower:]' '[:upper:]'` ];then echo 0; else echo 1; fi)
+%define have_prelease %(if [ "%prelease" = `echo "prelease" |tr '[:lower:]' '[:upper:]'` ];then echo 0; else echo 1; fi)
+
+%if %have_pversion
+%define source_ver %{pversion}
+# Don't abort for stupid reasons on builds from tarballs:
+%global _unpackaged_files_terminate_build 0
+%global _missing_doc_files_terminate_build 0
+%else
+%define source_ver %{ver}
+%endif
+
+# We might have a prerelease:
+%define have_pre %(echo %source_ver|awk '{p=0} /[a-z,A-Z][a-z,A-Z]/ {p=1} {print p}')
+%if %have_pre
+%define pre_ver %(perl -e '$name="%source_ver"; print ($name =~ /(.*?)[a-z]/);')
+%define pre_pre %(echo %source_ver|sed -e 's/%pre_ver//g')
+%endif
+
+# Check to see if we are running a build from a tarball release from samba.org
+# (%have_pversion) If so, disable vscan, unless explicitly requested
+# (--with vscan).
+%define build_vscan 1
+%if %have_pversion
+%define build_vscan 0
+%{?_with_vscan: %define build_vscan 1}
+%endif
+
+# We now do detection of the Mandrake release we are building on:
+#%define build_cooker %(if [ `awk '{print $3}' /etc/mandrake-release` = "Cooker" ];then echo 1; else echo 0; fi)
+#%define build_cooker %(if [[ `cat /etc/mandrake-release|grep Cooker` ]];then echo 1; else echo 0; fi)
+%define build_mdk100 %(if [ `awk '{print $4}' /etc/mandrake-release` = 10.0 ];then echo 1; else echo 0; fi)
+%define build_mdk92 %(if [ `awk '{print $4}' /etc/mandrake-release` = 9.2 ];then echo 1; else echo 0; fi)
+%define build_mdk91 %(if [ `awk '{print $4}' /etc/mandrake-release` = 9.1 ];then echo 1; else echo 0; fi)
+%define build_mdk90 %(if [ `awk '{print $4}' /etc/mandrake-release` = 9.0 ];then echo 1; else echo 0; fi)
+%define build_mdk82 %(if [ `awk '{print $4}' /etc/mandrake-release` = 8.2 ];then echo 1; else echo 0; fi)
+%define build_mdk81 %(if [ `awk '{print $4}' /etc/mandrake-release` = 8.1 ];then echo 1; else echo 0; fi)
+%define build_mdk80 %(if [ `awk '{print $4}' /etc/mandrake-release` = 8.0 ];then echo 1; else echo 0; fi)
+%define build_mdk72 %(if [ `awk '{print $4}' /etc/mandrake-release` = 7.2 ];then echo 1; else echo 0; fi)
+%define build_non_default 0
+
+# Default options
+%define build_alternatives 0
+%define build_system 0
+%define build_acl 1
+%define build_winbind 1
+%define build_wins 1
+%define build_ldap 0
+%define build_ads 1
+%define build_scanners 0
+# CUPS supports functionality for 'printcap name = cups' (9.0 and later):
+%define build_cupspc 0
+# %_{pre,postun}_service are provided by rpm-helper in 9.0 and later
+%define have_rpmhelper 1
+
+# Set defaults for each version
+%if %build_mdk100
+%define build_system 1
+%define build_alternatives 1
+%define build_cupspc 1
+%endif
+
+%if %build_mdk92
+%define build_alternatives 1
+%define build_cupspc 1
+%endif
+
+%if %build_mdk91
+%define build_cupspc 1
+%endif
+
+%if %build_mdk90
+%endif
+
+%if %build_mdk82
+%define have_rpmhelper 0
+%endif
+
+%if %build_mdk81
+%define build_winbind 0
+%define build_wins 0
+%define have_rpmhelper 0
+%endif
+
+%if %build_mdk80
+%define build_acl 0
+%define build_winbind 0
+%define build_wins 0
+%define build_ads 0
+%define have_rpmhelper 1
+%endif
+
+%if %build_mdk72
+%define build_acl 0
+%define build_winbind 0
+%define build_wins 0
+%define build_ads 0
+%define have_rpmhelper 1
+%endif
+
+
+# Allow commandline option overrides (borrowed from Vince's qmail srpm):
+# To use it, do rpm [-ba|--rebuild] --with 'xxx'
+# Check if the rpm was built with the defaults, otherwise we inform the user
+%define build_non_default 0
+%{?_with_system: %global build_system 1}
+%{?_without_system: %global build_system 0}
+%{?_with_acl: %global build_acl 1}
+%{?_with_acl: %global build_non_default 1}
+%{?_without_acl: %global build_acl 0}
+%{?_without_acl: %global build_non_default 1}
+%{?_with_winbind: %global build_winbind 1}
+%{?_with_winbind: %global build_non_default 1}
+%{?_without_winbind: %global build_winbind 0}
+%{?_without_winbind: %global build_non_default 1}
+%{?_with_wins: %global build_wins 1}
+%{?_with_wins: %global build_non_default 1}
+%{?_without_wins: %global build_wins 0}
+%{?_without_wins: %global build_non_default 1}
+%{?_with_ldap: %global build_ldap 1}
+%{?_with_ldap: %global build_non_default 1}
+%{?_without_ldap: %global build_ldap 0}
+%{?_without_ldap: %global build_non_default 1}
+%{?_with_ads: %global build_ads 1}
+%{?_with_ads: %global build_non_default 1}
+%{?_without_ads: %global build_ads 0}
+%{?_without_ads: %global build_non_default 1}
+%{?_with_scanners: %global build_scanners 1}
+%{?_with_scanners: %global build_non_default 1}
+
+# As if that weren't enough, we're going to try building with antivirus
+# support as an option also
+%define build_fprot 0
+%define build_kaspersky 0
+%define build_mks 0
+%define build_openav 0
+%define build_sophos 0
+%define build_symantec 0
+%define build_trend 0
+%if %build_vscan && %build_scanners
+#These can be enabled here by default
+# (kaspersky requires their library present)
+%define build_fprot 1
+%define build_mks 1
+%define build_openav 1
+%define build_sophos 1
+%define build_trend 1
+%endif
+%if %build_vscan
+%{?_with_fprot: %{expand: %%global build_fprot 1}}
+%{?_with_kaspersky: %{expand: %%global build_kaspersky 1}}
+%{?_with_mks: %{expand: %%global build_mks 1}}
+%{?_with_openav: %{expand: %%global build_openav 1}}
+%{?_with_sophos: %{expand: %%global build_sophos 1}}
+#%{?_with_symantec: %{expand: %%global build_symantec 1}}
+%{?_with_trend: %{expand: %%global build_trend 1}}
+%global vscandir samba-vscan-%{vscanver}
+%endif
+%global vfsdir examples.bin/VFS
+
+#Standard texts for descriptions:
+%define message_bugzilla() %(echo -e -n "Please file bug reports for this package at Mandrake bugzilla \\n(http://qa.mandrakesoft.com) under the product name %{1}")
+%define message_system %(echo -e -n "NOTE: These packages of samba-%{version}, are provided, parallel installable\\nwith samba-2.2.x, to allow easy migration from samba-2.2.x to samba-%{version},\\nbut are not officially supported")
+
+#check gcc version to disable some optimisations on gcc-3.3.1
+%define gcc331 %(gcc -dumpversion|awk '{if ($1>3.3) print 1; else print 0}')
+
+#Define sets of binaries that we can use in globs and loops:
+%global commonbin net,ntlm_auth,rpcclient,smbcacls,smbcquotas,smbpasswd,smbtree,testparm,testprns
+
+%global serverbin editreg,pdbedit,profiles,smbcontrol,smbstatus,tdbbackup,tdbdump
+%global serversbin nmbd,samba,smbd,mkntpwd
+
+%global clientbin findsmb,nmblookup,smbclient,smbmnt,smbmount,smbprint,smbspool,smbtar,smbumount
+%global client_bin mount.cifs
+%global client_sbin mount.smb,mount.smbfs
+
+%global testbin debug2html,smbtorture,msgtest,masktest,locktest,locktest2,nsstest,vfstest
+
+%ifarch alpha
+%define build_expsam xml
+%else
+%define build_expsam mysql,xml
+%endif
+
+#Workaround missing macros in 8.x:
+%{!?perl_vendorlib: %{expand: %%global perl_vendorlib %{perl_sitearch}/../}}
+
+# Determine whether this is the system samba or not.
+%if %build_system
+%define samba_major %{nil}
+%else
+%define samba_major 3
+%endif
+# alternatives_major is %{nil} if we aren't system and not using alternatives
+%if !%build_system || %build_alternatives
+%define alternative_major 3
+%else
+%define alternative_major %{nil}
+%endif
+
+Summary: Samba SMB server.
+Name: %{pkg_name}%{samba_major}
+
+%if %have_pre
+Version: %{pre_ver}
+%else
+Version: %{source_ver}
+%endif
+
+%if %have_prelease && !%have_pre
+Release: 1.%{prelease}mdk
+%endif
+%if %have_prelease && %have_pre
+Release: 0.%{pre_pre}.%{prelease}mdk
+%endif
+%if !%have_prelease && !%have_pre
+Release: %{rel}
+%endif
+%if !%have_prelease && %have_pre
+Release: 0.%{pre_pre}.%{rel}
+%endif
+
+License: GPL
+Group: System/Servers
+Source: ftp://samba.org/pub/samba/samba-%{source_ver}.tar.bz2
+URL: http://www.samba.org
+Source1: samba.log
+Source3: samba.xinetd
+Source4: swat_48.png.bz2
+Source5: swat_32.png.bz2
+Source6: swat_16.png.bz2
+Source7: README.%{name}-mandrake-rpm
+%if %build_vscan
+Source8: samba-vscan-%{vscanver}.tar.bz2
+%endif
+Source10: samba-print-pdf.sh.bz2
+Patch1: smbw.patch.bz2
+Patch4: samba-3.0-smbmount-sbin.patch.bz2
+%if !%have_pversion
+# Version specific patches: current version
+%else
+# Version specific patches: upcoming version
+%endif
+# Limbo patches (applied to prereleases, but not preleases, ie destined for
+# samba CVS)
+%if %have_pversion && %have_pre
+%endif
+Requires: pam >= 0.64, samba-common = %{version}
+BuildRequires: pam-devel readline-devel libncurses-devel popt-devel
+BuildRequires: libxml2-devel
+%ifnarch alpha
+BuildRequires: mysql-devel
+%endif
+%if %build_acl
+BuildRequires: libacl-devel
+%endif
+%if %build_mdk72
+BuildRequires: cups-devel
+%else
+BuildRequires: libcups-devel
+%endif
+BuildRequires: libldap-devel
+%if %build_ads
+BuildRequires: libldap-devel krb5-devel
+%endif
+BuildRoot: %{_tmppath}/%{name}-%{version}-root
+Prefix: /usr
+Prereq: /sbin/chkconfig /bin/mktemp /usr/bin/killall
+Prereq: fileutils sed /bin/grep
+
+%description
+Samba provides an SMB server which can be used to provide
+network services to SMB (sometimes called "Lan Manager")
+clients, including various versions of MS Windows, OS/2,
+and other Linux machines. Samba also provides some SMB
+clients, which complement the built-in SMB filesystem
+in Linux. Samba uses NetBIOS over TCP/IP (NetBT) protocols
+and does NOT need NetBEUI (Microsoft Raw NetBIOS frame)
+protocol.
+
+Samba-3.0 features working NT Domain Control capability and
+includes the SWAT (Samba Web Administration Tool) that
+allows samba's smb.conf file to be remotely managed using your
+favourite web browser. For the time being this is being
+enabled on TCP port 901 via xinetd. SWAT is now included in
+it's own subpackage, samba-swat.
+
+Please refer to the WHATSNEW.txt document for fixup information.
+This binary release includes encrypted password support.
+
+Please read the smb.conf file and ENCRYPTION.txt in the
+docs directory for implementation details.
+%if %have_pversion
+%message_bugzilla samba3
+%endif
+%if !%build_system
+%message_system
+%endif
+%if %build_non_default
+WARNING: This RPM was built with command-line options. Please
+see README.%{name}-mandrake-rpm in the documentation for
+more information.
+%endif
+
+%package server
+URL: http://www.samba.org
+Summary: Samba (SMB) server programs.
+Requires: %{name}-common = %{version}
+%if %have_rpmhelper
+PreReq: rpm-helper
+%endif
+Group: Networking/Other
+%if %build_system
+Provides: samba
+Obsoletes: samba
+Provides: samba-server-ldap
+Obsoletes: samba-server-ldap
+Provides: samba3-server
+Obsoletes: samba3-server
+%else
+#Provides: samba-server
+%endif
+
+%description server
+Samba-server provides a SMB server which can be used to provide
+network services to SMB (sometimes called "Lan Manager")
+clients. Samba uses NetBIOS over TCP/IP (NetBT) protocols
+and does NOT need NetBEUI (Microsoft Raw NetBIOS frame)
+protocol.
+
+Samba-3.0 features working NT Domain Control capability and
+includes the SWAT (Samba Web Administration Tool) that
+allows samba's smb.conf file to be remotely managed using your
+favourite web browser. For the time being this is being
+enabled on TCP port 901 via xinetd. SWAT is now included in
+it's own subpackage, samba-swat.
+
+Please refer to the WHATSNEW.txt document for fixup information.
+This binary release includes encrypted password support.
+
+Please read the smb.conf file and ENCRYPTION.txt in the
+docs directory for implementation details.
+%if %have_pversion
+%message_bugzilla samba3-server
+%endif
+%if !%build_system
+%message_system
+%endif
+
+%package client
+URL: http://www.samba.org
+Summary: Samba (SMB) client programs.
+Group: Networking/Other
+Requires: %{name}-common = %{version}
+%if %build_alternatives
+#Conflicts: samba-client < 2.2.8a-9mdk
+%endif
+%if %build_system
+Provides: samba3-client
+Obsoletes: samba3-client
+Obsoletes: smbfs
+%else
+#Provides: samba-client
+%endif
+%if !%build_system && %build_alternatives
+Provides: samba-client
+%endif
+
+%description client
+Samba-client provides some SMB clients, which complement the built-in
+SMB filesystem in Linux. These allow the accessing of SMB shares, and
+printing to SMB printers.
+%if %have_pversion
+%message_bugzilla samba3-client
+%endif
+%if !%build_system
+%message_system
+%endif
+
+%package common
+URL: http://www.samba.org
+Summary: Files used by both Samba servers and clients.
+Group: System/Servers
+%if %build_system
+Provides: samba-common-ldap
+Obsoletes: samba-common-ldap
+Provides: samba3-common
+Obsoletes: samba3-common
+%else
+#Provides: samba-common
+%endif
+
+%description common
+Samba-common provides files necessary for both the server and client
+packages of Samba.
+%if %have_pversion
+%message_bugzilla samba3-common
+%endif
+%if !%build_system
+%message_system
+%endif
+
+%package doc
+URL: http://www.samba.org
+Summary: Documentation for Samba servers and clients.
+Group: System/Servers
+Requires: %{name}-common = %{version}
+%if %build_system
+Obsoletes: samba3-doc
+Provides: samba3-doc
+%else
+#Provides: samba-doc
+%endif
+
+%description doc
+Samba-doc provides documentation files for both the server and client
+packages of Samba.
+%if %have_pversion
+%message_bugzilla samba3-doc
+%endif
+%if !%build_system
+%message_system
+%endif
+
+%package swat
+URL: http://www.samba.org
+Summary: The Samba Web Administration Tool.
+Requires: %{name}-server = %{version}
+Requires: xinetd
+Group: System/Servers
+%if %build_system
+Provides: samba-swat-ldap
+Obsoletes: samba-swat-ldap
+Provides: samba3-swat
+Obsoletes: samba3-swat
+%else
+#Provides: samba-swat
+%endif
+
+%description swat
+SWAT (the Samba Web Administration Tool) allows samba's smb.conf file
+to be remotely managed using your favourite web browser. For the time
+being this is being enabled on TCP port 901 via xinetd. Note that
+SWAT does not use SSL encryption, nor does it preserve comments in
+your smb.conf file. Webmin uses SSL encryption by default, and
+preserves comments in configuration files, even if it does not display
+them, and is therefore the preferred method for remotely managing
+Samba.
+%if %have_pversion
+%message_bugzilla samba3-swat
+%endif
+%if !%build_system
+%message_system
+%endif
+
+%if %build_winbind
+%package winbind
+URL: http://www.samba.org
+Summary: Samba-winbind daemon, utilities and documentation
+Group: System/Servers
+Requires: %{name}-common = %{version}
+%endif
+%if %build_winbind && !%build_system
+Conflicts: samba-winbind
+%endif
+%if %build_winbind
+%description winbind
+Provides the winbind daemon and testing tools to allow authentication
+and group/user enumeration from a Windows or Samba domain controller.
+%endif
+%if %have_pversion
+%message_bugzilla samba3-winbind
+%endif
+%if !%build_system
+%message_system
+%endif
+
+%if %build_wins
+%package -n nss_wins%{samba_major}
+URL: http://www.samba.org
+Summary: Name Service Switch service for WINS
+Group: System/Servers
+Requires: %{name}-common = %{version}
+PreReq: glibc
+%endif
+%if %build_wins && !%build_system
+Conflicts: nss_wins
+%endif
+%if %build_wins
+%description -n nss_wins%{samba_major}
+Provides the libnss_wins shared library which resolves NetBIOS names to
+IP addresses.
+%endif
+%if %have_pversion
+%message_bugzilla nss_wins3
+%endif
+%if !%build_system
+%message_system
+%endif
+
+%if %{?_with_test:1}%{!?_with_test:0}
+%package test
+URL: http://www.samba.org
+Summary: Debugging and benchmarking tools for samba
+Group: System/Servers
+Requires: %{name}-common = %{version}
+%endif
+%if %build_system && %{?_with_test:1}%{!?_with_test:0}
+Provides: samba3-test samba3-debug
+Obsoletes: samba3-test samba3-debug
+%endif
+%if !%build_system && %{?_with_test:1}%{!?_with_test:0}
+Provides: samba-test samba3-debug
+Obsoletes: samba3-debug
+%endif
+%if %{?_with_test:1}%{!?_with_test:0}
+
+%description test
+This package provides tools for benchmarking samba, and debugging
+the correct operation of tools against smb servers.
+%endif
+
+%if %build_system
+%package -n %{libname}
+URL: http://www.samba.org
+Summary: SMB Client Library
+Group: System/Libraries
+Provides: libsmbclient
+
+%description -n %{libname}
+This package contains the SMB client library, part of the samba
+suite of networking software, allowing other software to access
+SMB shares.
+%endif
+%if %have_pversion && %build_system
+%message_bugzilla %{libname}
+%endif
+
+%if %build_system
+%package -n %{libname}-devel
+URL: http://www.samba.org
+Summary: SMB Client Library Development files
+Group: System/Libraries
+Provides: libsmbclient-devel
+Requires: %{libname} = %{version}-%{release}
+
+%description -n %{libname}-devel
+This package contains the development files for the SMB client
+library, part of the samba suite of networking software, allowing
+the development of other software to access SMB shares.
+%endif
+%if %have_pversion && %build_system
+%message_bugzilla %{libname}-devel
+%endif
+
+%if %build_system
+%package -n %{libname}-static-devel
+URL: http://www.samba.org
+Summary: SMB Client Static Library Development files
+Group: System/Libraries
+Provides: libsmbclient-static-devel = %{version}-%{release}
+Requires: %{libname}-devel = %{version}-%{release}
+
+%description -n %{libname}-static-devel
+This package contains the static development files for the SMB
+client library, part of the samba suite of networking software,
+allowing the development of other software to access SMB shares.
+%endif
+%if %have_pversion && %build_system
+%message_bugzilla %{libname}-devel
+%endif
+
+#%package passdb-ldap
+#URL: http://www.samba.org
+#Summary: Samba password database plugin for MySQL
+#Group: System/Libraries
+#
+#%description passdb-ldap
+#The passdb-ldap package for samba provides a password database
+#backend allowing samba to store account details in an LDAP
+#database
+#_if %have_pversion
+#_message_bugzilla samba3-passdb-ldap
+#_endif
+#_if !%build_system
+#_message_system
+#_endif
+
+%ifnarch alpha
+%package passdb-mysql
+URL: http://www.samba.org
+Summary: Samba password database plugin for MySQL
+Group: System/Libraries
+Requires: %{name}-server = %{version}-%{release}
+%endif
+%ifnarch alpha && %build_system
+Obsoletes: samba3-passdb-mysql
+Provides: samba3-passdb-mysql
+%endif
+%ifnarch alpha
+
+%description passdb-mysql
+The passdb-mysql package for samba provides a password database
+backend allowing samba to store account details in a MySQL
+database
+%endif
+
+%package passdb-xml
+URL: http://www.samba.org
+Summary: Samba password database plugin for XML files
+Group: System/Libraries
+Requires: %{name}-server = %{version}-%{release}
+%if %build_system
+Obsoletes: samba3-passdb-xml
+Provides: samba3-passdb-xml
+%endif
+
+%description passdb-xml
+The passdb-xml package for samba provides a password database
+backend allowing samba to store account details in XML files.
+%if %have_pversion
+%message_bugzilla samba3-passdb-xml
+%endif
+%if !%build_system
+%message_system
+%endif
+
+#Antivirus packages:
+%if %build_fprot
+%package vscan-fprot
+Summary: On-access virus scanning for samba using FPROT
+Group: System/Servers
+Requires: %{name}-server = %{version}
+Provides: %{name}-vscan
+Autoreq: 0
+%description vscan-fprot
+A vfs-module for samba to implement on-access scanning using the
+FPROT antivirus software (which must be installed to use this).
+%endif
+
+%if %build_kaspersky
+%package vscan-kaspersky
+Summary: On-access virus scanning for samba using Kaspersky
+Group: System/Servers
+Requires: %{name}-server = %{version}
+Provides: %{name}-vscan
+Autoreq: 0
+%description vscan-kaspersky
+A vfs-module for samba to implement on-access scanning using the
+Kaspersky antivirus software (which must be installed to use this).
+%endif
+
+%if %build_mks
+%package vscan-mks
+Summary: On-access virus scanning for samba using MKS
+Group: System/Servers
+Requires: %{name}-server = %{version}
+Provides: %{name}-vscan
+Autoreq: 0
+%description vscan-mks
+A vfs-module for samba to implement on-access scanning using the
+MKS antivirus software (which must be installed to use this).
+%endif
+
+%if %build_openav
+%package vscan-openav
+Summary: On-access virus scanning for samba using OpenAntivirus
+Group: System/Servers
+Requires: %{name}-server = %{version}
+Provides: %{name}-vscan
+Autoreq: 0
+%description vscan-openav
+A vfs-module for samba to implement on-access scanning using the
+OpenAntivirus antivirus software (which must be installed to use this).
+%endif
+
+%if %build_sophos
+%package vscan-sophos
+Summary: On-access virus scanning for samba using Sophos
+Group: System/Servers
+Requires: %{name}-server = %{version}
+Provides: %{name}-vscan
+Autoreq: 0
+%description vscan-sophos
+A vfs-module for samba to implement on-access scanning using the
+Sophos antivirus software (which must be installed to use this).
+%endif
+
+%if %build_symantec
+%package vscan-symantec
+Summary: On-access virus scanning for samba using Symantec
+Group: System/Servers
+Requires: %{name}-server = %{version}
+Provides: %{name}-vscan
+Autoreq: 0
+%description vscan-symantec
+A vfs-module for samba to implement on-access scanning using the
+Symantec antivirus software (which must be installed to use this).
+%endif
+
+%if %build_trend
+%package vscan-trend
+Summary: On-access virus scanning for samba using Trend
+Group: System/Servers
+Requires: %{name}-server = %{version}
+Provides: %{name}-vscan
+Autoreq: 0
+%description vscan-trend
+A vfs-module for samba to implement on-access scanning using the
+Trend antivirus software (which must be installed to use this).
+%endif
+
+%prep
+
+# Allow users to query build options with --with options:
+#%define opt_status(%1) %(echo %{1})
+%if %{?_with_options:1}%{!?_with_options:0}
+%define opt_status(%{1}) %(if [ %{1} -eq 1 ];then echo enabled;else echo disabled;fi)
+#exit 1
+%{error: }
+%{error:Build options available are:}
+%{error:--with[out] system Build as the system samba package [or as samba3]}
+%{error:--with[out] acl Build with support for file ACLs - %opt_status %build_acl}
+%{error:--with[out] winbind Build with Winbind support - %opt_status %build_winbind}
+%{error:--with[out] wins Build with WINS name resolution support - %opt_status %build_wins}
+%{error:--with[out] ldap Build with legacy (samba2) LDAP support - %opt_status %build_ldap}
+%{error:--with[out] ads Build with Active Directory support - %opt_status %build_ads}
+%{error:--with[out] scanners Enable on-access virus scanners - %opt_status %build_scanners}
+%{error: }
+%else
+%{error: }
+%{error: This rpm has build options available, use --with options to see them}
+%{error: }
+%endif
+
+%if %{?_with_options:1}%{!?_with_options:0} && %build_scanners
+%{error:--with scanners enables fprot,mks,openav,sophos and trend by default}
+%{error: }
+%{error:To enable others (requires development libraries for the scanner):}
+%{error:--with kaspersky Enable on-access scanning with Kaspersky - %opt_status %build_kaspersky}
+%{error: }
+%endif
+
+%if %{?_with_options:1}%{!?_with_options:0}
+clear
+exit 1
+%endif
+
+
+%if %build_non_default
+RPM_EXTRA_OPTIONS="\
+%{?_with_system: --with system}\
+%{?_without_system: --without system}\
+%{?_with_acl: --with acl}\
+%{?_without_acl: --without acl}\
+%{?_with_winbind: --with winbind}\
+%{?_without_winbind: --without winbind}\
+%{?_with_wins: --with wins}\
+%{?_without_wins: --without wins}\
+%{?_with_ldap: --with ldap}\
+%{?_without_ldap: --without ldap}\
+%{?_with_ads: --with ads}\
+%{?_without_ads: --without ads}\
+%{?_with_scanners: --with scanners}\
+%{?_without_scanners: --without scanners}\
+"
+echo "Building a non-default rpm with the following command-line arguments:"
+echo "$RPM_EXTRA_OPTIONS"
+echo "This rpm was built with non-default options, thus, to build ">%{SOURCE7}
+echo "an identical rpm, you need to supply the following options">>%{SOURCE7}
+echo "at build time: $RPM_EXTRA_OPTIONS">>%{SOURCE7}
+echo -e "\n%{name}-%{version}-%{release}\n">>%{SOURCE7}
+%else
+echo "This rpm was built with default options">%{SOURCE7}
+echo -e "\n%{name}-%{version}-%{release}\n">>%{SOURCE7}
+%endif
+
+%if %build_vscan
+%setup -q -a 8 -n %{pkg_name}-%{source_ver}
+%else
+%setup -q -n %{pkg_name}-%{source_ver}
+%endif
+#%patch111 -p1
+%patch1 -p1 -b .smbw
+%patch4 -p1 -b .sbin
+# Version specific patches: current version
+%if !%have_pversion
+echo "Applying patches for current version: %{ver}"
+%else
+# Version specific patches: upcoming version
+echo "Applying patches for new versions: %{pversion}"
+%endif
+
+# Limbo patches
+%if %have_pversion && %have_pre
+echo "Appling patches which should only be applied to prereleases"
+%endif
+
+# Fix quota compilation in glibc>2.3
+%if %build_mdk91 || %build_mdk92
+#grep "<linux/quota.h>" source/smbd/quotas.c >/dev/null && \
+perl -pi -e 's@<linux/quota.h>@<sys/quota.h>@' source/smbd/quotas.c
+%endif
+
+cp %{SOURCE7} .
+
+# Make a copy of examples so that we have a clean one for doc:
+cp -a examples examples.bin
+
+%if %build_vscan
+cp -a %{vscandir} %{vfsdir}
+#fix stupid directory names:
+mv %{vfsdir}/%{vscandir}/openantivirus %{vfsdir}/%{vscandir}/oav
+%endif
+# Inline replacement of config dir
+for av in fprot kaspersky mks oav sophos trend; do
+ [ -e %{vfsdir}/%{vscandir}/$av/vscan-$av.h ] && perl -pi -e \
+ 's,^#define PARAMCONF "/etc/samba,#define PARAMCONF "/etc/%{name},' \
+ %{vfsdir}/%{vscandir}/$av/vscan-$av.h
+done
+#Inline edit vscan header:
+perl -pi -e 's/^# define SAMBA_VERSION_MAJOR 2/# define SAMBA_VERSION_MAJOR 3/g' %{vfsdir}/%{vscandir}/include/vscan-global.h
+
+# Edit some files when not building system samba:
+%if !%build_system
+perl -pi -e 's/%{pkg_name}/%{name}/g' source/auth/pampass.c
+%endif
+
+#remove cvs internal files from docs:
+find docs examples -name '.cvsignore' -exec rm -f {} \;
+
+#make better doc trees:
+mkdir -p clean-docs/samba-doc
+cp -a examples docs clean-docs/samba-doc
+mv -f clean-docs/samba-doc/examples/libsmbclient clean-docs/
+rm -Rf clean-docs/samba-doc/docs/{docbook,manpages,htmldocs,using_samba}
+ln -s %{_datadir}/swat%{samba_major}/using_samba/ clean-docs/samba-doc/docs/using_samba
+ln -sf %{_datadir}/swat%{samba_major}/help/ clean-docs/samba-doc/docs/htmldocs
+
+%build
+#%serverbuild
+(cd source
+CFLAGS=`echo "$RPM_OPT_FLAGS"|sed -e 's/-g//g'`
+%if %gcc331
+#CFLAGS=`echo "$CFLAGS"|sed -e 's/-O2/-Os/g'`
+%endif
+# Don't use --with-fhs now, since it overrides libdir, it sets configdir,
+# lockdir,piddir logfilebase,privatedir and swatdir
+%configure --prefix=%{_prefix} \
+ --sysconfdir=%{_sysconfdir}/%{name} \
+ --localstatedir=/var \
+ --libdir=%{_libdir}/%{name} \
+ --with-privatedir=%{_sysconfdir}/%{name} \
+ --with-lockdir=/var/cache/%{name} \
+ --with-piddir=/var/run/%{name} \
+ --with-swatdir=%{_datadir}/swat%{samba_major} \
+ --with-configdir=%{_sysconfdir}/%{name} \
+ --with-logfilebase=/var/log/%{name} \
+%if !%build_ads
+ --with-ads=no \
+%endif
+ --with-automount \
+ --with-smbmount \
+ --with-pam \
+ --with-pam_smbpass \
+%if %build_ldap
+ --with-ldapsam \
+%endif
+ --with-tdbsam \
+ --with-syslog \
+ --with-quotas \
+ --with-utmp \
+ --with-manpages-langs=en \
+%if %build_acl
+ --with-acl-support \
+%endif
+ --disable-mysqltest \
+ --with-expsam=%build_expsam \
+ --program-suffix=%{samba_major}
+# --with-shared-modules=pdb_ldap,idmap_ldap \
+# --with-manpages-langs=en,ja,pl \
+#_if !%build_system
+# --with-smbwrapper \
+#_endif
+# --with-nisplussam \
+# --with-fhs \
+
+#Fix the make file so we don't create debug information on 9.2
+%if %build_mdk92
+perl -pi -e 's/-g //g' Makefile
+%endif
+
+perl -pi -e 's|-Wl,-rpath,%{_libdir}||g;s|-Wl,-rpath -Wl,%{_libdir}||g' Makefile
+
+make proto_exists
+%make all libsmbclient smbfilter wins modules %{?_with_test: torture debug2html bin/log2pcap} bin/editreg client/mount.cifs
+
+
+# Build VFS modules (experimental)
+#cd ../%vfsdir
+#_configure --prefix=%{prefix} \
+# --mandir=%{prefix}/share/man
+#make
+#make CFLAGS="$RPM_OPT_FLAGS -I../../source -I../../source/include -I../../source/ubiqx \
+# -I../../source/smbwrapper -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE"
+)
+
+# Build mkntpasswd in examples/LDAP/ for smbldaptools
+make -C examples.bin/LDAP/smbldap-tools/mkntpwd
+
+# Build antivirus vfs objects:
+%if %build_fprot
+echo -e "\n\nBuild antivirus VFS modules\n\n"
+echo "Building fprot"
+(cd %{vfsdir}/%{vscandir}/fprot;make)
+%endif
+%if %build_kaspersky
+echo "Building Kaspersky"
+(cd %{vfsdir}/%{vscandir}/kavp
+ perl -p -i -e "s|/usr/local/|/usr/|g" Makefile.KAV4
+ make -f Makefile.KAV4
+)
+%endif
+%if %build_mks
+echo "Building mks"
+(cd %{vfsdir}/%{vscandir}/mks;make)
+%endif
+%if %build_openav
+echo "Building OpenAntivirus"
+(cd %{vfsdir}/%{vscandir}/oav;make)
+%endif
+%if %build_sophos
+echo "building sophos"
+(cd %{vfsdir}/%{vscandir}/sophos;make)
+%endif
+%if %build_symantec
+echo "Building symantec"
+(cd %{vfsdir}/%{vscandir}/symantec;make)
+%endif
+%if %build_trend
+echo "Building Trend"
+(cd %{vfsdir}/%{vscandir}/trend;make)
+%endif
+
+%install
+rm -rf $RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT
+
+#Ensure all docs are readable
+chmod a+r docs -R
+
+# Any entries here mean samba makefile is *really* broken:
+mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/%{name}
+mkdir -p $RPM_BUILD_ROOT/%{_datadir}
+mkdir -p $RPM_BUILD_ROOT%{_libdir}/%{name}/vfs
+
+(cd source
+make DESTDIR=$RPM_BUILD_ROOT LIBDIR=%{_libdir}/%{name} install installclientlib installmodules)
+
+install -m755 source/bin/editreg %{buildroot}/%{_bindir}
+
+#need to stay
+mkdir -p $RPM_BUILD_ROOT/{sbin,bin}
+mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/{logrotate.d,pam.d,xinetd.d}
+mkdir -p $RPM_BUILD_ROOT/%{_initrddir}
+mkdir -p $RPM_BUILD_ROOT/var/cache/%{name}
+mkdir -p $RPM_BUILD_ROOT/var/log/%{name}
+mkdir -p $RPM_BUILD_ROOT/var/run/%{name}
+mkdir -p $RPM_BUILD_ROOT/var/spool/%{name}
+mkdir -p $RPM_BUILD_ROOT/%{_localstatedir}/%{name}/{netlogon,profiles,printers}
+mkdir -p $RPM_BUILD_ROOT/%{_localstatedir}/%{name}/printers/{W32X86,WIN40,W32ALPHA,W32MIPS,W32PPC}
+mkdir -p $RPM_BUILD_ROOT/%{_localstatedir}/%{name}/codepages/src
+mkdir -p $RPM_BUILD_ROOT/%{_lib}/security
+mkdir -p $RPM_BUILD_ROOT%{_libdir}
+mkdir -p $RPM_BUILD_ROOT%{_libdir}/%{name}/vfs
+mkdir -p $RPM_BUILD_ROOT%{_datadir}/%{name}/scripts
+
+#smbwrapper and pam_winbind not handled by make, pam_smbpass.so doesn't build
+#install -m 755 source/bin/smbwrapper.so $RPM_BUILD_ROOT%{_libdir}/smbwrapper%{samba_major}.so
+install -m 755 source/bin/pam_smbpass.so $RPM_BUILD_ROOT/%{_lib}/security/pam_smbpass%{samba_major}.so
+install -m 755 source/nsswitch/pam_winbind.so $RPM_BUILD_ROOT/%{_lib}/security/pam_winbind.so
+
+install -m755 source/bin/libsmbclient.a $RPM_BUILD_ROOT%{_libdir}/libsmbclient.a
+pushd $RPM_BUILD_ROOT/%{_libdir}
+[ -f libsmbclient.so ] && mv -f libsmbclient.so libsmbclient.so.%{libsmbmajor}
+ln -sf libsmbclient.so.%{libsmbmajor} libsmbclient.so
+popd
+
+# smbsh forgotten
+#install -m 755 source/bin/smbsh $RPM_BUILD_ROOT%{_bindir}/
+
+# Install VFS modules
+#install -m755 %vfsdir/audit.so $RPM_BUILD_ROOT/%{_libdir}/samba/vfs
+#for i in block recycle
+#do
+# install -m755 %vfsdir/$i/$i.so $RPM_BUILD_ROOT/%{_libdir}/samba/vfs
+#done
+
+# Antivirus support:
+# mkdir -p $RPM_BUILD_ROOT%{_libdir}/samba/vfs/vscan
+ for av in fprot kavp mks oav sophos symantec trend; do
+ if [ -d %{vfsdir}/%{vscandir}/$av -a -e %{vfsdir}/%{vscandir}/$av/vscan-$av*.so ];then
+ cp %{vfsdir}/%{vscandir}/$av/vscan-$av*.so \
+ $RPM_BUILD_ROOT%{_libdir}/%{name}/vfs/
+ cp %{vfsdir}/%{vscandir}/$av/vscan-$av*.conf \
+ $RPM_BUILD_ROOT%{_sysconfdir}/%{name}
+ fi
+ done
+
+#libnss_* not handled by make:
+# Install the nsswitch library extension file
+for i in wins winbind; do
+ install -m755 source/nsswitch/libnss_${i}.so $RPM_BUILD_ROOT/%{_lib}/libnss_${i}.so
+done
+# Make link for wins and winbind resolvers
+( cd $RPM_BUILD_ROOT/%{_lib}; ln -s libnss_wins.so libnss_wins.so.2; ln -s libnss_winbind.so libnss_winbind.so.2)
+
+%if %{?_with_test:1}%{!?_with_test:0}
+for i in {%{testbin}};do
+ install -m755 source/bin/${i} $RPM_BUILD_ROOT/%{_bindir}/${i}%{samba_major}
+done
+%endif
+
+# Install other stuff
+
+# install -m644 examples/VFS/recycle/recycle.conf $RPM_BUILD_ROOT%{_sysconfdir}/samba/
+ install -m644 packaging/Mandrake/smbusers $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/smbusers
+ install -m755 packaging/Mandrake/smbprint $RPM_BUILD_ROOT/%{_bindir}
+ #install -m755 packaging/RedHat/smbadduser $RPM_BUILD_ROOT/usr/bin
+ install -m755 packaging/Mandrake/findsmb $RPM_BUILD_ROOT/%{_bindir}
+ install -m755 packaging/Mandrake/smb.init $RPM_BUILD_ROOT/%{_initrddir}/smb%{samba_major}
+ install -m755 packaging/Mandrake/smb.init $RPM_BUILD_ROOT/%{_sbindir}/%{name}
+ install -m755 packaging/Mandrake/winbind.init $RPM_BUILD_ROOT/%{_initrddir}/winbind
+# install -m755 packaging/Mandrake/wrepld.init $RPM_BUILD_ROOT/%{_initrddir}/wrepld%{samba_major}
+ install -m755 packaging/Mandrake/winbind.init $RPM_BUILD_ROOT/%{_sbindir}/winbind
+ install -m644 packaging/Mandrake/samba.pamd $RPM_BUILD_ROOT/%{_sysconfdir}/pam.d/%{name}
+ install -m644 packaging/Mandrake/system-auth-winbind.pamd $RPM_BUILD_ROOT/%{_sysconfdir}/pam.d/system-auth-winbind
+#
+ install -m644 %{SOURCE1} $RPM_BUILD_ROOT/%{_sysconfdir}/logrotate.d/%{name}
+# install -m644 packaging/Mandrake/samba-slapd-include.conf $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/samba-slapd.include
+
+# Install smbldap-tools scripts:
+for i in examples/LDAP/smbldap-tools/*.pl; do
+ install -m 750 $i $RPM_BUILD_ROOT/%{_datadir}/%{name}/scripts/
+ ln -s %{_datadir}/%{name}/scripts/`basename $i` $RPM_BUILD_ROOT/%{_bindir}/`basename $i|sed -e 's/\.pl//g'`%{samba_major}
+done
+
+install -m 750 examples/LDAP/smbldap-tools/smbldap_tools.pm $RPM_BUILD_ROOT/%{_datadir}/%{name}/scripts/
+
+# The conf file
+install -m 640 examples/LDAP/smbldap-tools/smbldap_conf.pm $RPM_BUILD_ROOT/%{_sysconfdir}/%{name}
+
+#Fix the smbldap-tools when not system samba:
+%if !%build_system
+perl -pi -e 's/^(use|package)(\s+)smbldap_(\w+);$/${1}${2}smbldap_${3}%{samba_major};/g' \
+%{buildroot}/%{_sysconfdir}/%{name}/smbldap_conf.pm \
+%{buildroot}/%{_datadir}/%{name}/scripts/smbldap*.p?
+perl -pi -e 's,/usr/local/sbin/mkntpwd,/usr/sbin/mkntpwd%{samba_major},g;s,553,421,g' %{buildroot}/%{_sysconfdir}/%{name}/smbldap_conf.pm
+perl -pi -e 's,\$smbldap_conf::SID,\$smbldap_conf3::SID,g' %{buildroot}/%{_datadir}/%{name}/scripts/smbldap*.p?
+%endif
+perl -pi -e 's,/usr/local/sbin/smbldap-passwd.pl,%{_datadir}/%{name}/scripts/smbldap-passwd.pl,g' %{buildroot}/%{_datadir}/%{name}/scripts/smbldap-useradd.pl
+
+# Link both smbldap*.pm into vendor-perl (any better ideas?)
+mkdir -p %{buildroot}/%{perl_vendorlib}
+ln -s %{_sysconfdir}/%{name}/smbldap_conf.pm $RPM_BUILD_ROOT/%{perl_vendorlib}/smbldap_conf%{samba_major}.pm
+ln -s %{_datadir}/%{name}/scripts/smbldap_tools.pm $RPM_BUILD_ROOT/%{perl_vendorlib}/smbldap_tools%{samba_major}.pm
+#mkntpwd
+install -m750 examples.bin/LDAP/smbldap-tools/mkntpwd/mkntpwd %{buildroot}/%{_sbindir}/mkntpwd%{samba_major}
+
+# Samba smbpasswd migration script:
+install -m755 examples/LDAP/convertSambaAccount $RPM_BUILD_ROOT/%{_datadir}/%{name}/scripts/
+
+# make a conf file for winbind from the default one:
+ cat packaging/Mandrake/smb.conf|sed -e 's/^; winbind/ winbind/g;s/^; obey pam/ obey pam/g; s/^; printer admin = @"D/ printer admin = @"D/g;s/^; password server = \*/ password server = \*/g;s/^; template/ template/g; s/^ security = user/ security = domain/g' > packaging/Mandrake/smb-winbind.conf
+ install -m644 packaging/Mandrake/smb-winbind.conf $RPM_BUILD_ROOT/%{_sysconfdir}/%{name}/smb-winbind.conf
+
+# Some inline fixes for smb.conf for non-winbind use
+install -m644 packaging/Mandrake/smb.conf $RPM_BUILD_ROOT/%{_sysconfdir}/%{name}/smb.conf
+cat packaging/Mandrake/smb.conf | \
+sed -e 's/^; printer admin = @adm/ printer admin = @adm/g' >$RPM_BUILD_ROOT/%{_sysconfdir}/%{name}/smb.conf
+%if %build_cupspc
+perl -pi -e 's/printcap name = lpstat/printcap name = cups/g' $RPM_BUILD_ROOT/%{_sysconfdir}/%{name}/smb.conf
+perl -pi -e 's/printcap name = lpstat/printcap name = cups/g' $RPM_BUILD_ROOT/%{_sysconfdir}/%{name}/smb-winbind.conf
+%endif
+
+#%if !%build_system
+# Fix script paths in smb.conf
+#perl -pi -e 's,%{_datadir}/samba,%{_datadir}/%{name},g' %{buildroot}/%{_sysconfdir}/%{name}/smb*.conf
+#%endif
+
+
+#install mount.cifs
+install -m755 source/client/mount.cifs %{buildroot}/bin/mount.cifs%{samba_major}
+
+ echo 127.0.0.1 localhost > $RPM_BUILD_ROOT/%{_sysconfdir}/%{name}/lmhosts
+
+# Link smbspool to CUPS (does not require installed CUPS)
+
+ mkdir -p $RPM_BUILD_ROOT/%{_libdir}/cups/backend
+ ln -s %{_bindir}/smbspool%{alternative_major} $RPM_BUILD_ROOT/%{_libdir}/cups/backend/smb%{alternative_major}
+
+# xinetd support
+
+ mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/xinetd.d
+ install -m644 %{SOURCE3} $RPM_BUILD_ROOT/%{_sysconfdir}/xinetd.d/swat%{samba_major}
+
+# menu support
+
+mkdir -p $RPM_BUILD_ROOT%{_menudir}
+cat > $RPM_BUILD_ROOT%{_menudir}/%{name}-swat << EOF
+?package(%{name}-swat):\
+command="gnome-moz-remote http://localhost:901/" \
+needs="gnome" \
+icon="swat%{samba_major}.png" \
+section="Configuration/Networking" \
+title="Samba Configuration (SWAT)" \
+longtitle="The Swat Samba Administration Tool"
+?package(%{name}-swat):\
+command="sh -c '\$BROWSER http://localhost:901/'" \
+needs="x11" \
+icon="swat%{samba_major}.png" \
+section="Configuration/Networking" \
+title="Samba Configuration (SWAT)" \
+longtitle="The Swat Samba Administration Tool"
+EOF
+
+mkdir -p $RPM_BUILD_ROOT%{_liconsdir} $RPM_BUILD_ROOT%{_iconsdir} $RPM_BUILD_ROOT%{_miconsdir}
+
+bzcat %{SOURCE4} > $RPM_BUILD_ROOT%{_liconsdir}/swat%{samba_major}.png
+bzcat %{SOURCE5} > $RPM_BUILD_ROOT%{_iconsdir}/swat%{samba_major}.png
+bzcat %{SOURCE6} > $RPM_BUILD_ROOT%{_miconsdir}/swat%{samba_major}.png
+
+bzcat %{SOURCE10}> $RPM_BUILD_ROOT%{_datadir}/%{name}/scripts/print-pdf
+
+# Fix configs when not building system samba:
+
+#Client binaries will have suffixes while we use alternatives, even
+# if we are system samba
+%if !%build_system || %build_alternatives
+for OLD in %{buildroot}/%{_bindir}/{%{clientbin}} %{buildroot}/bin/%{client_bin} %{buildroot}/%{_libdir}/cups/backend/smb
+do
+ NEW=`echo ${OLD}%{alternative_major}`
+ [ -e $OLD ] && mv -f $OLD $NEW
+done
+for OLD in %{buildroot}/%{_mandir}/man?/{%{clientbin}}* %{buildroot}/%{_mandir}/man?/%{client_bin}*
+do
+ if [ -e $OLD ]
+ then
+ BASE=`perl -e '$_="'${OLD}'"; m,(%buildroot)(.*?)(\.[0-9]),;print "$1$2\n";'`
+ EXT=`echo $OLD|sed -e 's,'${BASE}',,g'`
+ NEW=`echo ${BASE}%{alternative_major}${EXT}`
+ mv $OLD $NEW
+ fi
+done
+%endif
+# Link smbmount to /sbin/mount.smb and /sbin/mount.smbfs
+#I don't think it's possible for make to do this ...
+(cd $RPM_BUILD_ROOT/sbin
+ ln -s ..%{_bindir}/smbmount%{alternative_major} mount.smb%{alternative_major}
+ ln -s ..%{_bindir}/smbmount%{alternative_major} mount.smbfs%{alternative_major}
+)
+# Server/common binaries are versioned only if not system samba:
+%if !%build_system
+for OLD in %{buildroot}/%{_bindir}/{%{commonbin}} %{buildroot}/%{_bindir}/{%{serverbin}} %{buildroot}/%{_sbindir}/{%{serversbin},swat}
+do
+ NEW=`echo ${OLD}%{alternative_major}`
+ mv $OLD $NEW -f ||:
+done
+# And the man pages too:
+for OLD in %{buildroot}/%{_mandir}/man?/{%{commonbin},%{serverbin},%{serversbin},swat,{%testbin},smb.conf,lmhosts}*
+do
+ if [ -e $OLD ]
+ then
+ BASE=`perl -e '$_="'${OLD}'"; m,(%buildroot)(.*?)(\.[0-9]),;print "$1$2\n";'`
+# BASE=`perl -e '$name="'${OLD}'"; print "",($name =~ /(.*?)\.[0-9]/), "\n";'`
+ EXT=`echo $OLD|sed -e 's,'${BASE}',,g'`
+ NEW=`echo ${BASE}%{samba_major}${EXT}`
+ mv $OLD $NEW
+ fi
+done
+# Replace paths in config files and init scripts:
+for i in smb ;do
+ perl -pi -e 's,/subsys/'$i',/subsys/'$i'%{samba_major},g' $RPM_BUILD_ROOT/%{_initrddir}/${i}%{samba_major}
+done
+for i in %{_sysconfdir}/%{name}/smb.conf %{_initrddir}/smb%{samba_major} %{_sbindir}/%{name} %{_initrddir}/winbind /%{_sysconfdir}/logrotate.d/%{name} /%{_sysconfdir}/xinetd.d/swat%{samba_major} %{_initrddir}/wrepld%{samba_major}; do
+ perl -pi -e 's,/%{pkg_name},/%{name},g; s,smbd,%{_sbindir}/smbd%{samba_major},g; s,nmbd,%{_sbindir}/nmbd%{samba_major},g; s,/usr/sbin/swat,%{_sbindir}/swat%{samba_major},g;s,wrepld,%{_sbindir}/wrepld%{samba_major},g' $RPM_BUILD_ROOT/$i;
+done
+# Fix xinetd file for swat:
+perl -pi -e 's,/usr/sbin,%{_sbindir},g' $RPM_BUILD_ROOT/%{_sysconfdir}/xinetd.d/swat%{samba_major}
+%endif
+
+#Clean up unpackaged files:
+for i in %{_bindir}/pam_smbpass.so %{_bindir}/smbwrapper.so;do
+rm -f %{buildroot}/$i
+done
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post server
+
+%_post_service smb%{samba_major}
+#%_post_service wrepld%{samba_major}
+
+# Add a unix group for samba machine accounts
+groupadd -frg 421 machines
+
+# Migrate tdb's from /var/lock/samba (taken from official samba spec file):
+for i in /var/lock/samba/*.tdb
+do
+if [ -f $i ]; then
+ newname=`echo $i | sed -e's|var\/lock\/samba|var\/cache\/samba|'`
+ echo "Moving $i to $newname"
+ mv $i $newname
+fi
+done
+
+%post common
+# Basic migration script for pre-2.2.1 users,
+# since smb config moved from /etc to %{_sysconfdir}/samba
+
+# Let's create a proper %{_sysconfdir}/samba/smbpasswd file
+[ -f %{_sysconfdir}/%{name}/smbpasswd ] || {
+ echo "Creating password file for samba..."
+ touch %{_sysconfdir}/%{name}/smbpasswd
+}
+
+# And this too, in case we don't have smbd to create it for us
+[ -f /var/cache/%{name}/unexpected.tdb ] || {
+ touch /var/cache/%{name}/unexpected.tdb
+}
+
+# Let's define the proper paths for config files
+perl -pi -e 's/(\/etc\/)(smb)/\1%{name}\/\2/' %{_sysconfdir}/%{name}/smb.conf
+
+# Fix the logrotate.d file from smb and nmb to smbd and nmbd
+if [ -f %{_sysconfdir}/logrotate.d/samba ]; then
+ perl -pi -e 's/smb /smbd /' %{_sysconfdir}/logrotate.d/samba
+ perl -pi -e 's/nmb /nmbd /' %{_sysconfdir}/logrotate.d/samba
+fi
+
+# And not loose our machine account SID
+[ -f %{_sysconfdir}/MACHINE.SID ] && mv -f %{_sysconfdir}/MACHINE.SID %{_sysconfdir}/%{name}/ ||:
+
+%if %build_winbind
+%post winbind
+if [ $1 = 1 ]; then
+ /sbin/chkconfig winbind on
+ cp -af %{_sysconfdir}/nsswitch.conf %{_sysconfdir}/nsswitch.conf.rpmsave
+ cp -af %{_sysconfdir}/nsswitch.conf %{_sysconfdir}/nsswitch.conf.rpmtemp
+ for i in passwd group;do
+ grep ^$i %{_sysconfdir}/nsswitch.conf |grep -v 'winbind' >/dev/null
+ if [ $? = 0 ];then
+ echo "Adding a winbind entry to the $i section of %{_sysconfdir}/nsswitch.conf"
+ awk '/^'$i'/ {print $0 " winbind"};!/^'$i'/ {print}' %{_sysconfdir}/nsswitch.conf.rpmtemp >%{_sysconfdir}/nsswitch.conf;
+ cp -af %{_sysconfdir}/nsswitch.conf %{_sysconfdir}/nsswitch.conf.rpmtemp
+ else
+ echo "$i entry found in %{_sysconfdir}/nsswitch.conf"
+ fi
+ done
+ if [ -f %{_sysconfdir}/nsswitch.conf.rpmtemp ];then rm -f %{_sysconfdir}/nsswitch.conf.rpmtemp;fi
+fi
+
+%preun winbind
+if [ $1 = 0 ]; then
+ echo "Removing winbind entries from %{_sysconfdir}/nsswitch.conf"
+ perl -pi -e 's/ winbind//' %{_sysconfdir}/nsswitch.conf
+
+ /sbin/chkconfig winbind reset
+fi
+%endif %build_winbind
+
+%if %build_wins
+%post -n nss_wins%{samba_major}
+if [ $1 = 1 ]; then
+ cp -af %{_sysconfdir}/nsswitch.conf %{_sysconfdir}/nsswitch.conf.rpmsave
+ grep '^hosts' %{_sysconfdir}/nsswitch.conf |grep -v 'wins' >/dev/null
+ if [ $? = 0 ];then
+ echo "Adding a wins entry to the hosts section of %{_sysconfdir}/nsswitch.conf"
+ awk '/^hosts/ {print $0 " wins"};!/^hosts/ {print}' %{_sysconfdir}/nsswitch.conf.rpmsave >%{_sysconfdir}/nsswitch.conf;
+ else
+ echo "wins entry found in %{_sysconfdir}/nsswitch.conf"
+ fi
+# else
+# echo "Upgrade, leaving nsswitch.conf intact"
+fi
+
+%preun -n nss_wins%{samba_major}
+if [ $1 = 0 ]; then
+ echo "Removing wins entry from %{_sysconfdir}/nsswitch.conf"
+ perl -pi -e 's/ wins//' %{_sysconfdir}/nsswitch.conf
+#else
+# echo "Leaving %{_sysconfdir}/nsswitch.conf intact"
+fi
+%endif %build_wins
+
+%preun server
+
+%_preun_service smb%{samba_major}
+#%_preun_service wrepld%{samba_major}
+
+if [ $1 = 0 ] ; then
+# /sbin/chkconfig --level 35 smb reset
+# Let's not loose /var/cache/samba
+
+ if [ -d /var/cache/%{name} ]; then
+ mv -f /var/cache/%{name} /var/cache/%{name}.BAK
+ fi
+fi
+
+%post swat
+if [ -f /var/lock/subsys/xinetd ]; then
+ service xinetd reload >/dev/null 2>&1 || :
+fi
+%update_menus
+
+%postun swat
+
+# Remove swat entry from xinetd
+if [ $1 = 0 -a -f %{_sysconfdir}/xinetd.conf ] ; then
+rm -f %{_sysconfdir}/xinetd.d/swat%{samba_major}
+ service xinetd reload &>/dev/null || :
+fi
+
+if [ "$1" = "0" -a -x /usr/bin/update-menus ]; then /usr/bin/update-menus || true ; fi
+
+%clean_menus
+
+%if %build_system
+%post -n %{libname} -p /sbin/ldconfig
+%postun -n %{libname} -p /sbin/ldconfig
+%endif
+
+%if %build_alternatives
+%post client
+
+update-alternatives --install %{_bindir}/smbclient smbclient \
+%{_bindir}/smbclient%{alternative_major} 10 \
+$(for i in {/bin/mount.cifs,/sbin/{%{client_sbin}},%{_bindir}/{%{clientbin}}};do
+j=`basename $i`
+[ "$j" = "smbclient" ] || \
+echo -n " --slave ${i} ${j} ${i}%{alternative_major}";done) \
+--slave %{_libdir}/cups/backend/smb cups_smb %{_libdir}/cups/backend/smb%{alternative_major} || \
+update-alternatives --auto smbclient
+
+%preun client
+[ $1 = 0 ] && update-alternatives --remove smbclient %{_bindir}/smbclient%{alternative_major} ||:
+%endif
+
+%if %build_alternatives
+%triggerpostun client -- samba-client, samba2-client
+[ ! -e %{_bindir}/smbclient ] && update-alternatives --auto smbclient || :
+%endif
+
+%files server
+%defattr(-,root,root)
+#%attr(-,root,root) /sbin/*
+%(for i in %{_sbindir}/{%{serversbin}}%{samba_major};do echo $i;done)
+#%{_sbindir}/%{name}
+#%{_sbindir}/smbd%{samba_major}
+#%{_sbindir}/nmbd%{samba_major}
+#%{_sbindir}/mkntpwd%{samba_major}
+#%{_sbindir}/wrepld%{samba_major}
+%(for i in %{_bindir}/{%{serverbin}}%{samba_major};do echo $i;done)
+#%{_bindir}/smbcontrol%{samba_major}
+#%{_bindir}/smbstatus%{samba_major}
+#%{_bindir}/pdbedit%{samba_major}
+#%{_bindir}/tdbbackup%{samba_major}
+#%{_bindir}/profiles%{samba_major}
+#%{_bindir}/editreg%{samba_major}
+%attr(755,root,root) /%{_lib}/security/pam_smbpass*
+%dir %{_libdir}/%{name}/vfs
+%{_libdir}/%{name}/vfs/*.so
+%dir %{_libdir}/%{name}/pdb
+
+%attr(-,root,root) %config(noreplace) %{_sysconfdir}/%{name}/smbusers
+%attr(-,root,root) %config(noreplace) %{_initrddir}/smb%{samba_major}
+#%attr(-,root,root) %config(noreplace) %{_initrddir}/wrepld%{samba_major}
+%attr(-,root,root) %config(noreplace) %{_sysconfdir}/logrotate.d/%{name}
+%attr(-,root,root) %config(noreplace) %{_sysconfdir}/pam.d/%{name}
+#%attr(-,root,root) %config(noreplace) %{_sysconfdir}/%{name}/samba-slapd.include
+%{_mandir}/man1/smbstatus*.1*
+%{_mandir}/man5/smbpasswd*.5*
+%{_mandir}/man7/samba*.7*
+%{_mandir}/man8/smbd*.8*
+%{_mandir}/man8/nmbd*.8*
+%{_mandir}/man8/pdbedit*.8*
+%{_mandir}/man1/smbcontrol*.1*
+%{_mandir}/man8/tdbbackup*.8*
+%{_mandir}/man1/profiles*.1*
+%{_mandir}/man1/editreg*.1*
+%attr(775,root,adm) %dir %{_localstatedir}/%{name}/netlogon
+%attr(755,root,root) %dir %{_localstatedir}/%{name}/profiles
+%attr(755,root,root) %dir %{_localstatedir}/%{name}/printers
+%attr(2775,root,adm) %dir %{_localstatedir}/%{name}/printers/*
+%attr(1777,root,root) %dir /var/spool/%{name}
+%dir %{_datadir}/%{name}
+%dir %{_datadir}/%{name}/scripts
+%attr(0755,root,root) %{_datadir}/%{name}/scripts/print-pdf
+%attr(0750,root,adm) %{_datadir}/%{name}/scripts/smbldap*.pl
+%attr(0750,root,adm) %{_bindir}/smbldap*
+%attr(0640,root,adm) %config(noreplace) %{_sysconfdir}/%{name}/smbldap_conf.pm
+%attr(0644,root,root) %{_datadir}/%{name}/scripts/smbldap_tools.pm
+%{perl_vendorlib}/*.pm
+#%attr(0700,root,root) %{_datadir}/%{name}/scripts/*port_smbpasswd.pl
+%attr(0755,root,root) %{_datadir}/%{name}/scripts/convertSambaAccount
+
+
+%files doc
+%defattr(-,root,root)
+%doc README COPYING Manifest Read-Manifest-Now
+%doc WHATSNEW.txt Roadmap
+%doc README.%{name}-mandrake-rpm
+%doc clean-docs/samba-doc/docs
+%doc clean-docs/samba-doc/examples
+%attr(-,root,root) %{_datadir}/swat%{samba_major}/using_samba/
+
+%files swat
+%defattr(-,root,root)
+%config(noreplace) %{_sysconfdir}/xinetd.d/swat%{samba_major}
+#%attr(-,root,root) /sbin/*
+%{_sbindir}/swat%{samba_major}
+%{_menudir}/%{name}-swat
+%{_miconsdir}/*.png
+%{_liconsdir}/*.png
+%{_iconsdir}/*.png
+%attr(-,root,root) %{_datadir}/swat%{samba_major}/help/
+%attr(-,root,root) %{_datadir}/swat%{samba_major}/images/
+%attr(-,root,root) %{_datadir}/swat%{samba_major}/include/
+%lang(ja) %{_datadir}/swat%{samba_major}/lang/ja
+%lang(tr) %{_datadir}/swat%{samba_major}/lang/tr
+%{_mandir}/man8/swat*.8*
+%lang(de) %{_libdir}/%{name}/de.msg
+%lang(en) %{_libdir}/%{name}/en.msg
+%lang(fr) %{_libdir}/%{name}/fr.msg
+%lang(it) %{_libdir}/%{name}/it.msg
+%lang(ja) %{_libdir}/%{name}/ja.msg
+%lang(nl) %{_libdir}/%{name}/nl.msg
+%lang(pl) %{_libdir}/%{name}/pl.msg
+%lang(tr) %{_libdir}/%{name}/tr.msg
+#%doc swat/README
+
+%files client
+%defattr(-,root,root)
+%(for i in %{_bindir}/{%{clientbin}}%{alternative_major};do echo $i;done)
+%(for i in %{_mandir}/man?/{%{clientbin}}%{alternative_major}.?.*;do echo $i|grep -v smbprint;done)
+%ifnarch alpha
+%(for i in /sbin/{%{client_sbin}}%{alternative_major};do echo $i;done)
+%attr(4755,root,root) /bin/mount.cifs%{alternative_major}
+%attr(755,root,root) %{_bindir}/smbmount%{alternative_major}
+%attr(4755,root,root) %{_bindir}/smbumount%{alternative_major}
+%attr(4755,root,root) %{_bindir}/smbmnt%{alternative_major}
+%{_mandir}/man8/smbmnt*.8*
+%{_mandir}/man8/smbmount*.8*
+%{_mandir}/man8/smbumount*.8*
+%{_mandir}/man8/mount.cifs*.8*
+%else
+%exclude %{_bindir}/smb*m*nt%{samba_major}
+%exclude %{_mandir}/man8/smb*m*nt*.8*
+%endif
+# Link of smbspool to CUPS
+/%{_libdir}/cups/backend/smb%{alternative_major}
+
+%files common
+%defattr(-,root,root)
+%dir /var/cache/%{name}
+%dir /var/log/%{name}
+%dir /var/run/%{name}
+%(for i in %{_bindir}/{%{commonbin}}%{samba_major};do echo $i;done)
+%(for i in %{_mandir}/man?/{%{commonbin}}%{samba_major}\.[0-9]*;do echo $i;done)
+#%{_libdir}/smbwrapper%{samba_major}.so
+%dir %{_libdir}/%{name}
+%{_libdir}/%{name}/*.dat
+%{_libdir}/%{name}/charset
+#%{_libdir}/%{name}/lowcase.dat
+#%{_libdir}/%{name}/valid.dat
+%dir %{_sysconfdir}/%{name}
+%attr(-,root,root) %config(noreplace) %{_sysconfdir}/%{name}/smb.conf
+%attr(-,root,root) %config(noreplace) %{_sysconfdir}/%{name}/smb-winbind.conf
+%attr(-,root,root) %config(noreplace) %{_sysconfdir}/%{name}/lmhosts
+%dir %{_localstatedir}/%{name}
+%attr(-,root,root) %{_localstatedir}/%{name}/codepages
+%{_mandir}/man5/smb.conf*.5*
+%{_mandir}/man5/lmhosts*.5*
+#%{_mandir}/man7/Samba*.7*
+%dir %{_datadir}/swat%{samba_major}
+
+%if %build_winbind
+%files winbind
+%defattr(-,root,root)
+%{_sbindir}/winbindd
+%{_sbindir}/winbind
+%{_bindir}/wbinfo
+%attr(755,root,root) /%{_lib}/security/pam_winbind*
+%attr(755,root,root) /%{_lib}/libnss_winbind*
+%attr(-,root,root) %config(noreplace) %{_initrddir}/winbind
+%attr(-,root,root) %config(noreplace) %{_sysconfdir}/pam.d/system-auth-winbind*
+%{_mandir}/man8/winbindd*.8*
+%{_mandir}/man1/wbinfo*.1*
+%endif
+
+%if %build_wins
+%files -n nss_wins%{samba_major}
+%defattr(-,root,root)
+%attr(755,root,root) /%{_lib}/libnss_wins.so*
+%endif
+
+%if %{?_with_test:1}%{!?_with_test:0}
+%files test
+%defattr(-,root,root)
+%(for i in %{_bindir}/{%{testbin}}%{samba_major};do echo $i;done)
+%{_mandir}/man1/vfstest%{samba_major}*.1*
+%exclude %{_mandir}/man1/log2pcap*.1*
+%else
+%exclude %{_mandir}/man1/vfstest%{samba_major}*.1*
+%exclude %{_mandir}/man1/log2pcap*.1*
+%endif
+
+%if %build_system
+%files -n %{libname}
+%defattr(-,root,root)
+%{_libdir}/libsmbclient.so.*
+%else
+%exclude %{_libdir}/libsmbclient.so.*
+%endif
+
+%if %build_system
+%files -n %{libname}-devel
+%defattr(-,root,root)
+%{_includedir}/*
+%{_libdir}/libsmbclient.so
+%doc clean-docs/libsmbclient/*
+%else
+%exclude %{_includedir}/*
+%exclude %{_libdir}/libsmbclient.so
+%endif
+
+%if %build_system
+%files -n %{libname}-static-devel
+%defattr(-,root,root)
+%{_libdir}/libsmbclient.a
+%else
+%exclude %{_libdir}/libsmbclient.a
+%endif
+
+#%files passdb-ldap
+#%defattr(-,root,root)
+#%{_libdir}/%{name}/*/*ldap.so
+
+%ifnarch alpha
+%files passdb-mysql
+%defattr(-,root,root)
+%{_libdir}/%{name}/pdb/*mysql.so
+%endif
+
+%files passdb-xml
+%defattr(-,root,root)
+%{_libdir}/%{name}/pdb/*xml.so
+
+#Files for antivirus support:
+%if %build_fprot
+%files vscan-fprot
+%defattr(-,root,root)
+%{_libdir}/%{name}/vfs/vscan-fprotd.so
+%config(noreplace) %{_sysconfdir}/%{name}/vscan-fprotd.conf
+%doc %{vfsdir}/%{vscandir}/INSTALL
+%endif
+
+%if %build_kaspersky
+%files vscan-kaspersky
+%defattr(-,root,root)
+%{_libdir}/%{name}/vfs/vscan-kavp.so
+%config(noreplace) %{_sysconfdir}/%{name}/vscan-kavp.conf
+%doc %{vfsdir}/%{vscandir}/INSTALL
+%endif
+
+%if %build_mks
+%files vscan-mks
+%defattr(-,root,root)
+%{_libdir}/%{name}/vfs/vscan-mksd.so
+%config(noreplace) %{_sysconfdir}/%{name}/vscan-mks*.conf
+%doc %{vfsdir}/%{vscandir}/INSTALL
+%endif
+
+%if %build_openav
+%files vscan-openav
+%defattr(-,root,root)
+%{_libdir}/%{name}/vfs/vscan-oav.so
+%config(noreplace) %{_sysconfdir}/%{name}/vscan-oav.conf
+%doc %{vfsdir}/%{vscandir}/INSTALL
+%endif
+
+%if %build_sophos
+%files vscan-sophos
+%defattr(-,root,root)
+%{_libdir}/%{name}/vfs/vscan-sophos.so
+%config(noreplace) %{_sysconfdir}/%{name}/vscan-sophos.conf
+%doc %{vfsdir}/%{vscandir}/INSTALL
+%endif
+
+%if %build_symantec
+%files vscan-symantec
+%defattr(-,root,root)
+%{_libdir}/%{name}/vfs/vscan-symantec.so
+%config(noreplace) %{_sysconfdir}/%{name}/vscan-symantec.conf
+%doc %{vfsdir}/%{vscandir}/INSTALL
+%endif
+
+%if %build_trend
+%files vscan-trend
+%defattr(-,root,root)
+%{_libdir}/%{name}/vfs/vscan-trend.so
+%config(noreplace) %{_sysconfdir}/%{name}/vscan-trend.conf
+%doc %{vfsdir}/%{vscandir}/INSTALL
+%endif
+
+%exclude %{_mandir}/man1/smbsh*.1*
+
+%changelog
+* Fri Dec 05 2003 Buchan Milne <bgmilne@linux-mandrake.com> 3.0.1-0.pre3.5mdk
+- Allow winbind to start if old winbind ranges are used (ease upgrades)
+
+* Tue Nov 18 2003 Buchan Milne <bgmilne@linux-mandrake.com> 3.0.1-0.pre3.4mdk
+- Fix build as system on 8.2 (and probably earlier)
+
+* Sun Nov 16 2003 Buchan Milne <bgmilne@linux-mandrake.com> 3.0.1-0.pre3.3mdk
+- Ensure printer drivers keep permissions by default (setgid and inherit perms)
+
+* Fri Nov 14 2003 Buchan Milne <bgmilne@linux-mandrake.com> 3.0.1-0.pre3.2mdk
+- 3.0.1pre3
+- Add support for Mandrake 10.0 (as system samba)
+- Fix alternatives triggers
+- Fix obsoletes
+
+* Mon Nov 10 2003 Buchan Milne <bgmilne@linux-mandrake.com> 3.0.1-0.pre2.2mdk
+- 3.0.1pre2
+- misc spec files (pointed out by Luca Olivetti)
+- Fix path to smbldap-passwd.pl
+- Only allow one copy of winbind and nss_wins
+- Add trigger for alternatives
+
+* Sun Oct 12 2003 Buchan Milne <bgmilne@linux-mandrake.com> 3.0.1-0.pre1.2mdk
+- 3.0.1pre1
+- remove buildroot patch (p3), fixed upstream
+
+* Thu Sep 25 2003 Buchan Milne <bgmilne@linux-mandrake.com> 3.0.0-2mdk
+- 3.0.0 final
+
+* Sat Sep 13 2003 Buchan Milne <bgmilne@linux-mandrake.com> 3.0.0-0.rc4.2mdk
+- rc4
+- Don't update alternatives in pre/post scripts when not using alternatives
+- Fix case of --with-system without alternatives
+- Final fixes to smbldap-tools for non-system case
+- Remove duplicate docs (really - 1 character typo ...)
+- Update configs (fix winbind init script, add example scripts in smb.conf)
+
+* Tue Sep 09 2003 Buchan Milne <bgmilne@linux-mandrake.com> 3.0.0-0.rc3.2mdk
+- rc3
+- Fix mount.smb{,fs} alternatives (spotted by Laurent Culioli)
+
+* Thu Sep 04 2003 Buchan Milne <bgmilne@linux-mandrake.com> 3.0.0-0.rc2.3mdk
+- Fix alternatives
+- Fix libname (can I blame guillomovitch's evil line-wrapping spec mode?)
+- Fix smbldap-tools package/use names when not system samba
+- Don't conflict samba3-client with samba-client for now so we can install it
+
+* Fri Aug 29 2003 Buchan Milne <bgmilne@linux-mandrake.com> 3.0.0-0.rc2.2mdk
+- rc2
+- Remove patches 100-102 (upstream)
+- Fix libname
+- Alternatavise client
+- Better solution to avoid rpath
+
+* Fri Aug 22 2003 Buchan Milne <bgmilne@linux-mandrake.com> 3.0.0-0.rc1.3mdk
+- Fix build with test package (p100), but not by default (too big)
+- Fix (p101) for SID resolution when member of samba-2.2.x domain
+- Fix libsmbclient packages (thanks Gotz)
+- version mount.cifs, patch from CVS (p102), and setuid it
+- Clean up docs (guillomovitch spam ;-)
+
+* Sat Aug 16 2003 Buchan Milne <bgmilne@linux-mandrake.com> 3.0.0-0.rc1.2mdk
+- rc1
+- disable test subpackage since it's broken again
+
+* Mon Jul 28 2003 Buchan Milne <bgmilne@linux-mandrake.com> 3.0.0-0.beta3.3mdk
+- Rebuild for kerberos-1.3 on cooker
+- Put printer directories back
+- Add mount.cifs
+- Go back to standard optimisations
+
+* Thu Jul 17 2003 Buchan Milne <bgmilne@linux-mandrake.com> 3.0.0-0.beta3.2mdk
+- beta3
+- remove -g from cflags to avoid large static libraries
+- drop optimisation from O2 to O1 for gcc 3.3.1
+- own some directories for distriblint's benefit
+- use chrpath on distro's that have it to drastically reduce rpmlint score
+
+* Mon Jul 14 2003 Buchan Milne <bgmilne@linux-mandrake.com> 3.0.0-0.beta2.3mdk
+- place non-conditional excludes at the end of files list, to prevent causing
+ rpm in Mandrake <=8.2 from segfaulting when processing files.
+- Update default config
+
+* Wed Jul 02 2003 Buchan Milne <bgmilne@linux-mandrake.com> 3.0.0-0.beta2.2mdk
+- 3.0.0beta2
+- manually build editreg
+- Add some new man pages
+
+* Tue Jun 10 2003 Buchan Milne <bgmilne@linux-mandrake.com> 3.0.0-0.beta1.3mdk
+- add provision for passdb-ldap subpackage (it doesn't build like that yet)
+- avoid debugging info on cooker/9.2 for the moment
+- We probably don't need to autoconf (and can thus build on 8.1)
+- We can probably build without kerberos support (and thus on 8.0)
+- Don't require mysql-devel on alpha's (maybe we want to be able to disable
+ mysql support for other arches?)
+- We shouldn't need to specifically add openssl to include path, since ssl
+ support is deprecated.
+- png icons, change menu title to not conflict with ksambaplugin
+- update to samba-vscan-0.3.3beta1, but it still does not build the vscan
+ modules.
+- add -static-devel package
+- Add buildrequires for lib packages that are picked up if installed
+ (ncurses, popt) in an attempt to get slbd to build samba3
+- Fix default config (P100)
+
+* Sun Jun 08 2003 Buchan Milne <bgmilne@linux-mandrake.com> 3.0.0-0.beta1.2mdk
+- Get packages into cooker (klama doesn't want to build this package ..)
+- samba-vscan-0.3.2b
+
+* Fri Jun 06 2003 Buchan Milne <bgmilne@linux-mandrake.com> 3.0-0.alpha24.2mdk
+- Rename debug package to test and other fixes for rpm-4.2
+- prepare for beta1
+
+* Wed Apr 30 2003 Buchan Milne <bgmilne@linux-mandrake.com> 3.0-0.alpha24.1mdk
+- Remove some files removed upstream
+- In builds from source, don't terminate on missing docs or unpackaged files
+ (if only we could do it for other missing files ...)
+
+* Mon Apr 28 2003 Buchan Milne <bgmilne@linux-mandrake.com> 3.0-0.alpha24.0mdk
+- Reenable debug package by (--without debug to not build it), fixed post-a23
+- Add bugzilla note for builds from source (also intended for packages made
+ available on samba FTP site) at samba team request
+- Fix build from CVS (run autogen.sh, pass options to all rpm commands)
+- Appease distriblint, but not much to be done about /usr/share/swat3/ since
+ samba-doc owns some subdirs, and samba-swat others, and they can be installed
+ independantly.
+- Apply kaspersky vscan build fix from samba2
+- Final for alpha24
+
+* Wed Apr 23 2003 Buchan Milne <bgmilne@linux-mandrake.com> 3.0-0.alpha23.3mdk
+- Small fixes in preparation for testing as system samba
+- Make debug package optional (--with debug) since it's often broken
+- Add support for 9.2 (including in-line smbd quota patch for glibc2.3)
+- Add --with options option, which will just show you the available options and exit
+
+* Sun Apr 06 2003 Buchan Milne <bgmilne@linux-mandrake.com> 3.0-0.alpha23.2mdk
+- Alpha23
+- buildrequire autconf2.5
+- samba-vscan 0.3.2a
+- Remove patch 102 (upstreamed)
+
+* Thu Mar 06 2003 Buchan Milne <bgmilne@linux-mandrake.com> 3.0-0.alpha22.2mdk
+- Alpha22
+- Add profiles binary to server and ntlm_auth to common
+- smbwrapper and torture target broken (only in 9.0?)
+- remove unused source 2
+
+* Tue Mar 04 2003 Buchan Milne <bgmilne@linux-mandrake.com> 3.0-0.alpha21.4mdk
+- Don't provide samba-{server,client,common} when not system samba (bug #2617)
+- Don't build libsmbclient packages when not system samba
+- Fix conflict between samba-server and samba3-server (pam_smbpass)
+- Fix smbwrapper (from 2.2.7a-5mdk for bug #2356)
+- Fix codepage/charset example (bug #1574)
+
+* Thu Jan 23 2003 Buchan Milne <bgmilne@linux-mandrake.com> 3.0-0.alpha21.3mdk
+- samba-vscan 0.3.1 (and make it build again), including required inline edits
+- Make all vscan packages provide samba(3)-vscan
+- Build all vscan except kav (requires kaspersky lib) with --with-scanners
+- Add vscan-(scanner).conf files
+- Explicitly add ldapsam for 2.2 compatability when building --with ldap,
+ default build now uses new ldap passdb backend (ie you always get ldap)
+- Enable (experimental) tdb passdb backend
+- Fix file ownership conflicts between server and common
+- Cleanup configure, to match order of --help
+- Fix libdir location, was being overridden by --with-fhs
+- Split off a libsmbclient and -devel package
+- Add wins replication init script (patch 102)
+- Workaround passdb/pdb_xml.c not compiling
+- Workaround missing install targets for smbsh/smbwrapper.so in cvs
+- Inline patch smbd/quotas.c for Mandrake >9.0
+
+* Wed Nov 27 2002 Buchan Milne <bgmilne@linux-mandrake.com> 3.0-0.alpha21.2mdk
+- Remove patch 20,21,22,23,25,26 (upstream)
+- New destdir patch from cvs (18)
+- package installed but non-packaged files
+- new debug subpackage for vfstest and related files (it was that or nuke the
+ manpage ;-))
+- use _libdir for libdir instead of _sysconfdir
+- Update samba-vscan (untested)
+
+* Mon Oct 28 2002 Buchan Milne <bgmilne@linux-mandrake.com> 3.0-0.alpha20.3mdk
+- Fix mount.smbfs3 pointing to smbmount not in package
+- Remove unnecessary lines from install (now done by make)
+- Build with ldap and ads on all releases by default
+- Put av-stuff back
+
+* Mon Oct 21 2002 Buchan Milne <bgmilne@linux-mandrake.com> 3.0-0.alpha20.2mdk
+- When not building as system samba, avoid conflicting with system samba
+- Macro-ize as much as possible for above (aka finish cleanups)
+- Fix paths in init scripts and logrotate and xinetd
+- Fix provides and obsoletes so as to provide samba, but not obsolete
+ current stable until we have a stable release (when it's the system samba).
+- Add warnings to descriptions when not system samba.
+- This is now parallel installable with the normal samba release, for easy
+ testing. It shouldn't touch existing installations. Of course, only
+ one samba at a time on the same interface!
+
+* Sat Sep 28 2002 Buchan Milne <bgmilne@linux-mandrake.com> 3.0-0.alpha20.1mdk
+- Merge with 2.2.6pre2.2mdk
+- Detect alpha- and beta-, along with pre-releases
+
+* Tue Feb 05 2002 Buchan Milne <bgmilne@cae.co.za> 3.0-alpha14-0.1mdk
+- Sync with 2.2.3-2mdk (new --without options, detect when
+ building for a different distribution.
+
+* Mon Feb 04 2002 Buchan Milne <bgmilne@cae.co.za> 3.0-alpha14-0.0mdk
+- Sync with 2.2.2-10mdk, which added build-time options --with ldap,
+ winbind, acl, wins, mdk72, mdk80, mdk81, mdk82, cooker. Added
+ warning in description if built with these options.
+
+* Wed Jan 23 2002 Buchan Milne <bgmilne@cae.co.za> 3.0-alpha13-0.2mdk
+- Added if's for build_ads, which hopefully will add Active Directory
+ Support (by request).
+
+* Thu Jan 17 2002 Buchan Milne <bgmilne@cae.co.za> 3.0-alpha13-0.1mdk
+- More syncing with 2.2 rpm (post and postun scripts)
+- Testing without ldap
+
+* Thu Jan 17 2002 Buchan Milne <bgmilne@cae.co.za> 3.0-alpha13-0.0mdk
+- 3.0-alpha13
+- Fixed installman.sh patch.
+
+* Wed Jan 09 2002 Buchan Milne <bgmilne@cae.co.za> 3.0-alpha12-0.1mdk
+- Fixed %post and %preun for nss_wins, added %post and %preun for
+ samba-winbind (chkconfig and winbind entries in nsswitch.conf)
+
+* Sun Dec 23 2001 Buchan Milne <bgmilne@cae.co.za> 3.0-alpha12-0.0mdk
+- 3.0-alpha12
+- Sync up with changes made in 2.2.2 to support Mandrake 8.0, 7.2
+- Added new subpackage for swat
+- More if's for ldap.
+
+* Thu Dec 20 2001 Buchan Milne <bgmilne@cae.co.za> 3.0-alpha11-0.0mdk
+- 3.0-alpha11
+
+* Wed Dec 19 2001 Buchan Milne <bgmilne@cae.co.za> 3.0alpha10-0.0mdk
+- 3.0-alpha10
+
+* Tue Dec 18 2001 Buchan Milne <bgmilne@cae.co.za> 3.0alpha9-0.0mdk
+- 3.0-alpha9
+
+* Mon Dec 17 2001 Buchan Milne <bgmilne@cae.co.za> 3.0alpha8-0.1mdk
+- Added net command to %files common, pdbedit and smbgroupedit to
+ %files, s/%{prefix}\/bin/%{_bindir}/ (the big cleanup).
+ Added patch to smb.init from 2.2.2 (got missed with 3.0-alpha1 patches)
+
+* Sun Dec 16 2001 Buchan Milne <bgmilne@cae.co.za> 3.0alpha8-0.0mdk
+- Patch for installman.sh to handle lang=en correctly (p24)
+- added --with-manpages-langs=en,ja,pl (translated manpages), but there
+ aren't any manpages for these languages yet ... so we still
+ need %dir and %doc entries for them ...
+- patch (p25) to configure.in to support more than 2 languages.
+- addtosmbpass seems to have returned for now, but make_* have disappeared!
+
+* Fri Dec 14 2001 Buchan Milne <bgmilne@cae.co.za> 3.0alpha6-0.0mdk
+- DESTDIR patch for Makefile.in (p23), remove a lot of %%install scripts
+ this forces move of smbcontrol and smbmnt to %{prefix}/bin
+ removed --with-pam_smbpass as it doesn't compile.
+
+* Thu Dec 06 2001 Buchan Milne <bgmilne@cae.co.za> 3.0-0.0alpha1mdk
+- Samba 3.0alpha1 released (we missed Samba 3.0alpha0!)
+- Redid smbmount-sbin patch and smb.conf patch (20), removed xfs quota patch
+ (applied upstream), removed ook-patch (codepage directory totally different).
+- Added winbind.init (21) and system-auth-winbind.pamd (22). Patches 20-23
+ should be applied upstream before 3.0 ships ...
+
+* Wed Dec 05 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.2-6mdk
+- fixed typo in system-auth-winbind.pamd (--Thanks J. Gluck).
+- fixed %post xxx problem (smb not started in chkconfig --Thanks Viet & B. Kenworthy).
+
+* Fri Nov 23 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.2-5mdk
+- Had to remove the network recycle bin patch: it seems to mess up
+ file deletion from windows (files appear to be "already in use")
+
+* Tue Nov 13 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.2-4mdk
+- added network recycle bin patch:
+ <http://www.amherst.edu/~bbstone/howto/samba.html>
+- added "recycle bin = .recycled" parameter in smb.conf [homes].
+- fixed winbind/nss_wins perms (oh no I don't own that stuff ;o)
+
+* Mon Nov 12 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.2-3mdk
+- added %build 8.0 and 7.2, for tweakers to play around.
+- changed configure options:
+ . removed --with-mmap, --with-netatalk (obsolete).
+ . added --with-msdfs, --with-vfs (seems stable, but still need testing).
+
+* Mon Nov 12 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.2-2mdk
+- rebuilt with winbind and nss_wins enabled.
+
+* Wed Oct 31 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.2-1mdk
+- Rebuilt on cooker.
+
+* Wed Oct 31 2001 Buchan Milne <bgmilne@cae.co.za> 2.2.2-0.992mdk
+- Patch for smb.conf to fix incorrect lpq command, typo in winbind,
+ and add sample linpopup command. Added print driver directories.
+- New XFS quota patch (untested!, samba runs, but do quotas work? We
+ can't check yet since the kernel doesn't seem to support XFS quotas!)
+
+* Fri Oct 19 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.2-0.99mdk
+- New samba.spec, almost ready for winbind operations. OLA for Buchan Milne
+ Who did a tremendous integration work on 2.2.2.
+ Rebuild on cooker, please test XFS (ACLs and quotas) again...
+
+* Mon Oct 15 2001 Buchan Milne <bgmilne@cae.co.za> 2.2.2-0.9mdk
+- Samba-2.2.2. released! Use %defines to determine which subpackages
+ are built and which Mandrake release we are buiding on/for (hint: define
+ build_mdk81 1 for Mandrake 8.1 updates)
+
+* Sun Oct 14 2001 Buchan Milne <bgmilne@cae.co.za> 2.2.2-0.20011014mdk
+- %post and %postun for nss_wins
+
+* Wed Oct 10 2001 Buchan Milne <bgmilne@cae.co.za> 2.2.2-0.20011010mdk
+- New CVS snapshot, /etc/pam.d/system-auth-winbind added
+ with configuration to allow easy winbind setup.
+
+* Sun Oct 7 2001 Buchan Milne <bgmilne@cae.co.za> 2.2.2-0.20011007mdk
+- Added new package nss_wins and moved smbpasswd to common (required by
+ winbind).
+
+* Sat Oct 6 2001 Buchan Milne <bgmilne@cae.co.za> 2.2.2-0.20011006mdk
+- Added new package winbind.
+
+* Mon Oct 1 2001 Buchan Milne <bgmilne@cae.co.za> 2.2.2-0.20011001mdk
+- Removed patch to smb init.d file (applied in cvs)
+
+* Sun Sep 30 2001 Buchan Milne <bgmilne@cae.co.za> 2.2.2-0.20010930mdk
+- Added winbind init script, which still needs to check for running nmbd.
+
+* Thu Sep 27 2001 Buchan Milne <bgmilne@cae.co.za> 2.2.2-0.20010927mdk
+- Built from samba-2.2.2-pre cvs, added winbindd, wbinfo, nss_winbind and
+ pam_winbind, moved pam_smbpass from samba-common to samba. We still
+ need a start-up script for winbind, or need to modify existing one.
+
+* Mon Sep 10 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.1a-15mdk
+- Enabled acl support (XFS acls now supported by kernel-2.4.8-21mdk thx Chmou)
+ Added smbd patch to support XFS quota (Nathan Scott)
+
+* Mon Sep 10 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.1a-14mdk
+- Oops! smbpasswd created in wrong directory...
+
+* Tue Sep 06 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.1a-13mdk
+- Removed a wrong comment in smb.conf.
+ Added creation of smbpasswd during install.
+
+* Mon Aug 27 2001 Pixel <pixel@mandrakesoft.com> 2.2.1a-12mdk
+- really less verbose %%post
+
+* Sat Aug 25 2001 Geoffrey Lee <snailtalk@mandrakesoft.com> 2.2.1a-11mdk
+- Fix shared libs in /usr/bin silliness.
+
+* Thu Aug 23 2001 Pixel <pixel@mandrakesoft.com> 2.2.1a-10mdk
+- less verbose %%post
+
+* Wed Aug 22 2001 Buchan Milne <bgmilne@cae.co.za> 2.2.1a-9mdk
+- Added smbcacls (missing in %files), modification to smb.conf: ([printers]
+ is still needed, even with point-and-print!, user add script should
+ use name and not gid, since we may not get the gid . New script for
+ putting manpages in place (still need to be added in %files!). Moved
+ smbcontrol to sbin and added it and its man page to %files.
+
+* Wed Aug 22 2001 Pixel <pixel@mandrakesoft.com> 2.2.1a-8mdk
+- cleanup /var/lib/samba/codepage/src
+
+* Tue Aug 21 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.1a-7mdk
+- moved codepage generation to %%install and codepage dir to /var/lib/samba
+
+* Tue Aug 21 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.1a-6mdk
+- /lib/* was in both samba and samba-common
+ Introducing samba-doc: "alas, for the sake of thy modem, shalt thou remember
+ when Samba was under the Megabyte..."
+
+* Fri Aug 03 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.1a-5mdk
+- Added "the gc touch" to smbinit through the use of killall -0 instead of
+ grep cupsd | grep -v grep (too many greps :o)
+
+* Wed Jul 18 2001 Stefan van der Eijk <stefan@eijk.nu> 2.2.1a-4mdk
+- BuildRequires: libcups-devel
+- Removed BuildRequires: openssl-devel
+
+* Fri Jul 13 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.1a-3mdk
+- replace chkconfig --add/del with --level 35 on/reset.
+
+* Fri Jul 13 2001 Geoffrey Lee <snailtalk@mandrakesoft.cm> 2.2.1a-2mdk
+- Replace discription s/inetd/xinetd/, we all love xinetd, blah.
+
+* Thu Jul 12 2001 Buchan Milne <bgmilne@cae.co.za> 2.2.1a-1mdk
+- Bugfix release. Fixed add user script, added print$ share and printer admin
+ We need to test interaction of new print support with CUPS, but printer
+ driver uploads should work.
+
+* Wed Jul 11 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.1-17mdk
+- fixed smb.conf a bit, rebuilt on cooker.
+
+* Tue Jul 10 2001 Buchan Milne <bgmilne@cae.co.za> 2.2.1-16mdk
+- Finally, samba 2.2.1 has actually been release. At least we were ready!
+ Cleaned up smb.conf, and added some useful entries for domain controlling.
+ Migrated changes made in samba's samba2.spec for 2.2.1 to this file.
+ Added groupadd command in post to create a group for samba machine accounts.
+ (We should still check the postun, samba removes pam, logs and cache)
+
+* Tue Jun 26 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.1-15mdk
+- fixed smbwrapper compile options.
+
+* Tue Jun 26 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.1-14mdk
+- added LFS support.
+ added smbwrapper support (smbsh)
+
+* Wed Jun 20 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.1-13mdk
+- /sbin/mount.smb and /sbin/mount.smbfs now point to the correct location
+ of smbmount (/usr/bin/smbmount)
+
+* Tue Jun 19 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.1-12mdk
+- smbmount and smbumount are now in /usr/bin and SUID.
+ added ||: to triggerpostun son you don't get error 1 anymore when rpm -e
+ Checked the .bz2 sources with file *: everything is OK now (I'm so stupid ;o)!
+
+* Tue Jun 19 2001 Geoffrey Lee <snailtalk@mandrakesoft.com> 2.2.1-11mdk
+- s/Copyright/License/;
+- Stop Sylvester from pretending .gz source to be .bz2 source via filename
+ aka really bzip2 the source.
+
+* Mon Jun 18 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.1-10mdk
+- changed Till's startup script modifications: now samba is being reloaded
+ automatically 1 minute after it has started (same reasons as below in 9mdk)
+ added _post_ and _preun_ for service smb
+ fixed creation of /var/lib/samba/{netlogon,profiles} (%dir was missing)
+
+* Thu Jun 14 2001 Till Kamppeter <till@mandrakesoft.com> 2.2.1-9mdk
+- Modified the Samba startup script so that in case of CUPS being used as
+ printing system Samba only starts when the CUPS daemon is ready to accept
+ requests. Otherwise the CUPS queues would not appear as Samba shares.
+
+* Mon Jun 11 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.1-8mdk
+- patched smbmount.c to have it call smbmnt in sbin (thanks Seb).
+
+* Wed May 30 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.1-7mdk
+- put SWAT menu icons back in place.
+
+* Mon May 28 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.1-6mdk
+- OOPS! fixed smbmount symlinks
+
+* Mon May 28 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.1-5mdk
+- removed inetd postun script, replaced with xinetd.
+ updated binary list (smbcacls...)
+ cleaned samba.spec
+
+* Mon May 28 2001 Buchan Milne <bgmilne@cae.co.za> 2.2.1-4mdk
+- Changed configure options to point to correct log and codepage directories,
+ added crude script to fix logrotate file for new log file names, updated
+ patches to work with current CVS.
+
+* Thu May 24 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.1-3mdk
+- Cleaned and updated the %files section.
+
+* Sat May 19 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.1-2mdk
+- Moved all samba files from /etc to /etc/samba (Thanks DomS!).
+ Fixed fixinit patch (/etc/samba/smb.conf)
+
+* Fri May 18 2001 Buchan Milne <bgmilne@cae.co.za> 2.2.1-1mdk
+- Now use packaging/Mandrake/smb.conf, removed unused and obsolete
+ patches, moved netlogon and profile shares to /var/lib/samba in the
+ smb.conf to match the spec file. Added configuration for ntlogon to
+ smb.conf. Removed pam-foo, fixinit and makefilepath patches. Removed
+ symlink I introduced in 2.2.0-1mdk
+
+* Thu May 3 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.0-5mdk
+- Added more configure options. Changed Description field (thx John T).
+
+* Wed Apr 25 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.0-4mdk
+- moved netlogon and profiles to /var/lib/samba by popular demand ;o)
+
+* Tue Apr 24 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.0-3mdk
+- moved netlogon and profiles back to /home.
+
+* Fri Apr 20 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.2.0-2mdk
+- fixed post inetd/xinetd script&
+
+* Thu Apr 19 2001 Buchan Milne <bgmilne@cae.co.za> 2.2.0-1mdk
+- Upgrade to 2.2.0. Merged most of 2.0.7-25mdk's patches (beware
+ nasty "ln -sf samba-%{ver} ../samba-2.0.7" hack to force some patches
+ to take. smbadduser and addtosmbpass seem to have disappeared. Moved
+ all Mandrake-specific files to packaging/Mandrake and made patches
+ from those shipped with samba. Moved netlogon to /home/samba and added
+ /home/samba/profiles. Added winbind,smbfilter and debug2html to make command.
+
+* Thu Apr 12 2001 Frederic Crozat <fcrozat@mandrakesoft.com> 2.0.7-25mdk
+- Fix menu entry and provide separate menu entry for GNOME
+ (nautilus doesn't support HTTP authentication yet)
+- Add icons in package
+
+* Fri Mar 30 2001 Frederic Lepied <flepied@mandrakesoft.com> 2.0.7-24mdk
+- use new server macros
+
+* Wed Mar 21 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.0.7-23mdk
+- check whether /etc/inetd.conf exists (upgrade) or not (fresh install).
+
+* Thu Mar 15 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.0.7-22mdk
+- spec cosmetics, added '-r' option to lpr-cups command line so files are
+ removed from /var/spool/samba after printing.
+
+* Tue Mar 06 2001 Sylvestre Taburet <staburet@mandrakesoft.com> 2.0.7-21mdk
+- merged last rh patches.
+
+* Thu Nov 23 2000 Sylvestre Taburet <staburet@mandrakesoft.com> 2.0.7-20mdk
+- removed dependencies on cups and cups-devel so one can install samba without using cups
+- added /home/netlogon
+
+* Mon Nov 20 2000 Till Kamppeter <till@mandrakesoft.com> 2.0.7-19mdk
+- Changed default print command in /etc/smb.conf, so that the Windows
+ driver of the printer has to be used on the client.
+- Fixed bug in smbspool which prevented from printing from a
+ Linux-Samba-CUPS client to a Windows server through the guest account.
+
+* Mon Oct 16 2000 Till Kamppeter <till@mandrakesoft.com> 2.0.7-18mdk
+- Moved "smbspool" (Samba client of CUPS) to the samba-client package
+
+* Sat Oct 7 2000 Stefan van der Eijk <s.vandereijk@chello.nl> 2.0.7-17mdk
+- Added RedHat's "quota" patch to samba-glibc21.patch.bz2, this fixes
+ quota related compile problems on the alpha.
+
+* Wed Oct 4 2000 Sylvestre Taburet <staburet@mandrakesoft.com> 2.0.7-16mdk
+- Fixed 'guest ok = ok' flag in smb.conf
+
+* Tue Oct 3 2000 Sylvestre Taburet <staburet@mandrakesoft.com> 2.0.7-15mdk
+- Allowed guest account to print in smb.conf
+- added swat icon in menu
+
+* Tue Oct 3 2000 Sylvestre Taburet <staburet@mandrakesoft.com> 2.0.7-14mdk
+- Removed rh ssl patch and --with-ssl flag: not appropriate for 7.2
+
+* Tue Oct 3 2000 Sylvestre Taburet <staburet@mandrakesoft.com> 2.0.7-13mdk
+- Changed fixinit patch.
+- Changed smb.conf for better CUPS configuration.
+- Thanks Fred for doing this ---vvv.
+
+* Tue Oct 3 2000 Frederic Lepied <flepied@mandrakesoft.com> 2.0.7-12mdk
+- menu entry for web configuration tool.
+- merge with rh: xinetd + ssl + pam_stack.
+- Added smbadduser rh-bugfix w/o relocation of config-files.
+
+* Mon Oct 2 2000 Frederic Lepied <flepied@mandrakesoft.com> 2.0.7-11mdk
+- added build requires on cups-devel and pam-devel.
+
+* Mon Oct 2 2000 Till Kamppeter <till@mandrakesoft.com> 2.0.7-10mdk
+- Fixed smb.conf entry for CUPS: "printcap name = lpstat", "lpstats" was
+ wrong.
+
+* Mon Sep 25 2000 Sylvestre Taburet <staburet@mandrakesoft.com> 2.0.7-9mdk
+- Cosmetic changes to make rpmlint more happy
+
+* Wed Sep 11 2000 Sylvestre Taburet <staburet@mandrakesoft.com> 2.0.7-8mdk
+- added linkage to the using_samba book in swat
+
+* Fri Sep 01 2000 Sylvestre Taburet <staburet@mandrakesoft.com> 2.0.7-7mdk
+- Added CUPS support to smb.conf
+- Added internationalization options to smb.conf [Global]
+
+* Wed Aug 30 2000 Till Kamppeter <till@mandrakesoft.com> 2.0.7-6mdk
+- Put "smbspool" to the files to install
+
+* Wed Aug 30 2000 Sylvestre Taburet <staburet@mandrakesoft.com> 2.0.7-5mdk
+- Did some cleaning in the patches
+
+* Fri Jul 28 2000 Sylvestre Taburet <staburet@mandrakesoft.com> 2.0.7-4mdk
+- relocated man pages from /usr/man to /usr/share/man for compatibility reasons
+
+* Fri Jul 28 2000 Sylvestre Taburet <staburet@mandrakesoft.com> 2.0.7-3mdk
+- added make_unicodemap and build of unicode_map.$i in the spec file
+
+* Fri Jul 28 2000 Sylvestre Taburet <staburet@mandrakesoft.com> 2.0.7-2mdk
+- renamed /etc/codepage/codepage.$i into /etc/codepage/unicode_map.$i to fix smbmount bug.
+
+* Fri Jul 07 2000 Sylvestre Taburet <staburet@mandrakesoft.com> 2.0.7-1mdk
+- 2.0.7
+
+* Wed Apr 05 2000 Francis Galiegue <fg@mandrakesoft.com> 2.0.6-4mdk
+
+- Titi sucks, does not put versions in changelog
+- Fixed groups for -common and -client
+- /usr/sbin/samba is no config file
+
+* Thu Mar 23 2000 Thierry Vignaud <tvignaud@mandrakesoft.com>
+- fix buggy post install script (pixel)
+
+* Fri Mar 17 2000 Francis Galiegue <francis@mandrakesoft.com> 2.0.6-2mdk
+
+- Changed group according to 7.1 specs
+- Some spec file changes
+- Let spec-helper do its job
+
+* Thu Nov 25 1999 Chmouel Boudjnah <chmouel@mandrakesoft.com>
+- 2.0.6.
+
+* Tue Nov 2 1999 Chmouel Boudjnah <chmouel@mandrakesoft.com>
+- Merge with rh changes.
+- Split in 3 packages.
+
+* Fri Aug 13 1999 Pablo Saratxaga <pablo@@mandrakesoft.com>
+- corrected a bug with %post (the $1 parameter is "1" in case of
+ a first install, not "0". That parameter is the number of packages
+ of the same name that will exist after running all the steps if nothing
+ is removed; so it is "1" after first isntall, "2" for a second install
+ or an upgrade, and "0" for a removal)
+
+* Wed Jul 28 1999 Pablo Saratxaga <pablo@@mandrakesoft.com>
+- made smbmnt and smbumount suid root, and only executable by group 'smb'
+ add to 'smb' group any user that should be allowed to mount/unmount
+ SMB shared directories
+
+* Fri Jul 23 1999 Chmouel Boudjnah <chmouel@mandrakesoft.com>
+- 2.0.5a (bug security fix).
+
+* Wed Jul 21 1999 Axalon Bloodstone <axalon@linux-mandrake.com>
+- 2.0.5
+- cs/da/de/fi/fr/it/tr descriptions/summaries
+
+* Sun Jun 13 1999 Bernhard Rosenkränzer <bero@mandrakesoft.com>
+- 2.0.4b
+- recompile on a system that works ;)
+
+* Wed Apr 21 1999 Chmouel Boudjnah <chmouel@mandrakesoft.com>
+- Mandrake adaptations.
+- Bzip2 man-pages.
+
+* Fri Mar 26 1999 Bill Nottingham <notting@redhat.com>
+- add a mount.smb to make smb mounting a little easier.
+- smb filesystems apparently do not work on alpha. Oops.
+
+* Thu Mar 25 1999 Bill Nottingham <notting@redhat.com>
+- always create codepages
+
+* Tue Mar 23 1999 Bill Nottingham <notting@redhat.com>
+- logrotate changes
+
+* Sun Mar 21 1999 Cristian Gafton <gafton@redhat.com>
+- auto rebuild in the new build environment (release 3)
+
+* Fri Mar 19 1999 Preston Brown <pbrown@redhat.com>
+- updated init script to use graceful restart (not stop/start)
+
+* Tue Mar 9 1999 Bill Nottingham <notting@redhat.com>
+- update to 2.0.3
+
+* Thu Feb 18 1999 Bill Nottingham <notting@redhat.com>
+- update to 2.0.2
+
+* Mon Feb 15 1999 Bill Nottingham <notting@redhat.com>
+- swat swat
+
+* Tue Feb 9 1999 Bill Nottingham <notting@redhat.com>
+- fix bash2 breakage in post script
+
+* Fri Feb 5 1999 Bill Nottingham <notting@redhat.com>
+- update to 2.0.0
+
+* Mon Oct 12 1998 Cristian Gafton <gafton@redhat.com>
+- make sure all binaries are stripped
+
+* Thu Sep 17 1998 Jeff Johnson <jbj@redhat.com>
+- update to 1.9.18p10.
+- fix %triggerpostun.
+
+* Tue Jul 07 1998 Erik Troan <ewt@redhat.com>
+- updated postun triggerscript to check $0
+- clear /etc/codepages from %preun instead of %postun
+
+* Mon Jun 08 1998 Erik Troan <ewt@redhat.com>
+- made the %postun script a tad less agressive; no reason to remove
+ the logs or lock file (after all, if the lock file is still there,
+ samba is still running)
+- the %postun and %preun should only exectute if this is the final
+ removal
+- migrated %triggerpostun from Red Hat's samba package to work around
+ packaging problems in some Red Hat samba releases
+
+* Sun Apr 26 1998 John H Terpstra <jht@samba.anu.edu.au>
+- minor tidy up in preparation for release of 1.9.18p5
+- added findsmb utility from SGI package
+
+* Wed Mar 18 1998 John H Terpstra <jht@samba.anu.edu.au>
+- Updated version and codepage info.
+- Release to test name resolve order
+
+* Sat Jan 24 1998 John H Terpstra <jht@samba.anu.edu.au>
+- Many optimisations (some suggested by Manoj Kasichainula <manojk@io.com>
+- Use of chkconfig in place of individual symlinks to /etc/rc.d/init/smb
+- Compounded make line
+- Updated smb.init restart mechanism
+- Use compound mkdir -p line instead of individual calls to mkdir
+- Fixed smb.conf file path for log files
+- Fixed smb.conf file path for incoming smb print spool directory
+- Added a number of options to smb.conf file
+- Added smbadduser command (missed from all previous RPMs) - Doooh!
+- Added smbuser file and smb.conf file updates for username map
diff --git a/packaging/Mandrake/smb.conf b/packaging/Mandrake/smb.conf
new file mode 100644
index 00000000000..6c1c05fa52e
--- /dev/null
+++ b/packaging/Mandrake/smb.conf
@@ -0,0 +1,529 @@
+
+# This is the main Samba configuration file. You should read the
+# smb.conf(5) manual page in order to understand the options listed
+# here. Samba has a huge number of configurable options (perhaps too
+# many!) most of which are not shown in this example
+#
+# Any line which starts with a ; (semi-colon) or a # (hash)
+# is a comment and is ignored. In this example we will use a #
+# for commentry and a ; for parts of the config file that you
+# may wish to enable
+#
+# NOTE: Whenever you modify this file you should run the command "testparm"
+# to check that you have not made any basic syntactic errors.
+#
+#======================= Global Settings =====================================
+[global]
+
+# 1. Server Naming Options:
+# workgroup = NT-Domain-Name or Workgroup-Name
+ workgroup = MDKGROUP
+
+# netbios name is the name you will see in "Network Neighbourhood",
+# but defaults to your hostname
+; netbios name = <name_of_this_server>
+
+# server string is the equivalent of the NT Description field
+ server string = Samba Server %v
+
+# Message command is run by samba when a "popup" message is sent to it.
+# The example below is for use with LinPopUp:
+; message command = /usr/bin/linpopup "%f" "%m" %s; rm %s
+
+# 2. Printing Options:
+# CHANGES TO ENABLE PRINTING ON ALL CUPS PRINTERS IN THE NETWORK
+# (as cups is now used in linux-mandrake 7.2 by default)
+# if you want to automatically load your printer list rather
+# than setting them up individually then you'll need this
+ printcap name = lpstat
+ load printers = yes
+
+# It should not be necessary to spell out the print system type unless
+# yours is non-standard. Currently supported print systems include:
+# bsd, sysv, plp, lprng, aix, hpux, qnx, cups
+ printing = cups
+
+# Samba 2.2 supports the Windows NT-style point-and-print feature. To
+# use this, you need to be able to upload print drivers to the samba
+# server. The printer admins (or root) may install drivers onto samba.
+# Note that this feature uses the print$ share, so you will need to
+# enable it below.
+# printer admin = @<group> <user>
+; printer admin = @adm
+# This should work well for winbind:
+; printer admin = @"Domain Admins"
+
+# 3. Logging Options:
+# this tells Samba to use a separate log file for each machine
+# that connects
+ log file = /var/log/samba/log.%m
+
+# Put a capping on the size of the log files (in Kb).
+ max log size = 50
+
+# Set the log (verbosity) level (0 <= log level <= 10)
+; log level = 3
+
+# 4. Security and Domain Membership Options:
+# This option is important for security. It allows you to restrict
+# connections to machines which are on your local network. The
+# following example restricts access to two C class networks and
+# the "loopback" interface. For more examples of the syntax see
+# the smb.conf man page. Do not enable this if (tcp/ip) name resolution does
+# not work for all the hosts in your network.
+; hosts allow = 192.168.1. 192.168.2. 127.
+
+# Uncomment this if you want a guest account, you must add this to /etc/passwd
+# otherwise the user "nobody" is used
+; guest account = pcguest
+# Allow users to map to guest:
+ map to guest = bad user
+
+# Security mode. Most people will want user level security. See
+# security_level.txt for details.
+ security = user
+# Use password server option only with security = server or security = domain
+# When using security = domain, you should use password server = *
+; password server = <NT-Server-Name>
+; password server = *
+
+# Password Level allows matching of _n_ characters of the password for
+# all combinations of upper and lower case.
+; password level = 8
+; username level = 8
+
+# You may wish to use password encryption. Please read
+# ENCRYPTION.txt, Win95.txt and WinNT.txt in the Samba documentation.
+# Do not enable this option unless you have read those documents
+# Encrypted passwords are required for any use of samba in a Windows NT domain
+# The smbpasswd file is only required by a server doing authentication, thus
+# members of a domain do not need one.
+ encrypt passwords = yes
+ smb passwd file = /etc/samba/smbpasswd
+
+# The following are needed to allow password changing from Windows to
+# also update the Linux system password.
+# NOTE: Use these with 'encrypt passwords' and 'smb passwd file' above.
+# NOTE2: You do NOT need these to allow workstations to change only
+# the encrypted SMB passwords. They allow the Unix password
+# to be kept in sync with the SMB password.
+; unix password sync = Yes
+# You either need to setup a passwd program and passwd chat, or
+# enable pam password change
+; pam password change = yes
+; passwd program = /usr/bin/passwd %u
+; passwd chat = *New*UNIX*password* %n\n *Re*ype*new*UNIX*password* %n\n \
+;*passwd:*all*authentication*tokens*updated*successfully*
+
+# Unix users can map to different SMB User names
+; username map = /etc/samba/smbusers
+
+# Using the following line enables you to customise your configuration
+# on a per machine basis. The %m gets replaced with the netbios name
+# of the machine that is connecting
+; include = /etc/samba/smb.conf.%m
+
+# Options for using winbind. Winbind allows you to do all account and
+# authentication from a Windows or samba domain controller, creating
+# accounts on the fly, and maintaining a mapping of Windows RIDs to unix uid's
+# and gid's. winbind uid and winbind gid are the only required parameters.
+#
+# winbind uid is the range of uid's winbind can use when mapping RIDs to uid's
+; winbind uid = 10000-20000
+#
+# winbind gid is the range of uid's winbind can use when mapping RIDs to gid's
+; winbind gid = 10000-20000
+#
+# winbind separator is the character a user must use between their domain
+# name and username, defaults to "\"
+; winbind separator = +
+#
+# winbind use default domain allows you to have winbind return usernames
+# in the form user instead of DOMAIN+user for the domain listed in the
+# workgroup parameter.
+; winbind use default domain = yes
+#
+# template homedir determines the home directory for winbind users, with
+# %D expanding to their domain name and %U expanding to their username:
+; template homedir = /home/%D/%U
+
+# When using winbind, you may want to have samba create home directories
+# on the fly for authenticated users. Ensure that /etc/pam.d/samba is
+# using 'service=system-auth-winbind' in pam_stack modules, and then
+# enable obedience of pam restrictions below:
+; obey pam restrictions = yes
+
+#
+# template shell determines the shell users authenticated by winbind get
+; template shell = /bin/bash
+
+# 5. Browser Control and Networking Options:
+# Most people will find that this option gives better performance.
+# See speed.txt and the manual pages for details
+ socket options = TCP_NODELAY SO_RCVBUF=8192 SO_SNDBUF=8192
+
+# Configure Samba to use multiple interfaces
+# If you have multiple network interfaces then you must list them
+# here. See the man page for details.
+; interfaces = 192.168.12.2/24 192.168.13.2/24
+
+# Configure remote browse list synchronisation here
+# request announcement to, or browse list sync from:
+# a specific host or from / to a whole subnet (see below)
+; remote browse sync = 192.168.3.25 192.168.5.255
+# Cause this host to announce itself to local subnets here
+; remote announce = 192.168.1.255 192.168.2.44
+
+# set local master to no if you don't want Samba to become a master
+# browser on your network. Otherwise the normal election rules apply
+; local master = no
+
+# OS Level determines the precedence of this server in master browser
+# elections. The default value should be reasonable
+; os level = 33
+
+# Domain Master specifies Samba to be the Domain Master Browser. This
+# allows Samba to collate browse lists between subnets. Don't use this
+# if you already have a Windows NT domain controller doing this job
+; domain master = yes
+
+# Preferred Master causes Samba to force a local browser election on startup
+# and gives it a slightly higher chance of winning the election
+; preferred master = yes
+
+# 6. Domain Control Options:
+# Enable this if you want Samba to be a domain logon server for
+# Windows95 workstations or Primary Domain Controller for WinNT and Win2k
+; domain logons = yes
+
+# if you enable domain logons then you may want a per-machine or
+# per user logon script
+# run a specific logon batch file per workstation (machine)
+; logon script = %m.bat
+# run a specific logon batch file per username
+; logon script = %U.bat
+
+# Where to store roaming profiles for WinNT and Win2k
+# %L substitutes for this servers netbios name, %U is username
+# You must uncomment the [Profiles] share below
+; logon path = \\%L\Profiles\%U
+
+# Where to store roaming profiles for Win9x. Be careful with this as it also
+# impacts where Win2k finds it's /HOME share
+; logon home = \\%L\%U\.profile
+
+
+# The add user script is used by a domain member to add local user accounts
+# that have been authenticated by the domain controller, or when adding
+# users via the Windows NT Tools (ie User Manager for Domains).
+
+# Scripts for file (passwd, smbpasswd) backend:
+; add user script = /usr/sbin/useradd -s /bin/false '%u'
+; delete user script = /usr/sbin/userdel '%s'
+; add user to group script = /usr/bin/gpasswd -a '%u' '%g'
+; delete user from group script = /usr/bin/gpasswd -d '%u' '%g'
+; set primary group script = /usr/sbin/usermod -g '%g' '%u'
+; add group script = /usr/sbin/groupadd %g && getent group '%g'|awk -F: '{print $3}'
+; delete group script = /usr/sbin/groupdel '%g'
+
+# Scripts for LDAP backend (assumes nss_ldap is in use on the domain controller,
+# and needs configuration in smbldap_conf.pm
+; add user script = /usr/share/samba/scripts/smbldap-useradd.pl '%u'
+; delete user script = /usr/share/samba/scripts/smbldap-userdel.pl '%u'
+; add user to group script = /usr/share/samba/scripts/smbldap-groupmod.pl -m '%u' '%g'
+; delete user from group script = /usr/share/samba/scripts/smbldap-groupmod.pl -x '%u' '%g'
+; set primary group script = /usr/share/samba/scripts/smbldap-usermod.pl -g '%g' '%u'
+; add group script = /usr/share/samba/scripts/smbldap-groupadd.pl '%g' && /usr/share/samba/scripts/smbldap-groupshow.pl %g|awk '/^gidNumber:/ {print $2}'
+; delete group script = /usr/share/samba/scripts/smbldap-userdel.pl '%g'
+
+
+# The add machine script is use by a samba server configured as a domain
+# controller to add local machine accounts when adding machines to the domain.
+# The script must work from the command line when replacing the macros,
+# or the operation will fail. Check that groups exist if forcing a group.
+# Script for domain controller for adding machines:
+; add machine script = /usr/sbin/useradd -d /dev/null -g machines -c 'Machine Account' -s /bin/false -M %u
+# Script for domain controller with LDAP backend for adding machines (please
+# configure in /etc/samba/smbldap_conf.pm first):
+; add machine script = /usr/share/samba/scripts/smbldap-useradd.pl -w -d /dev/null -g machines -c 'Machine Account' -s /bin/false %u
+
+# Domain groups:
+# Domain groups are now configured by using the 'net groupmap' tool
+
+# Samba Password Database configuration:
+# Samba now has runtime-configurable password database backends. Multiple
+# passdb backends may be used, but users will only be added to the first one
+# Default:
+; passdb backend = smbpasswd guest
+# TDB backen with fallback to smbpasswd and guest
+; passdb backend = tdbsam smbpasswd guest
+# LDAP with fallback to smbpasswd guest
+# Enable SSL by using an ldaps url, or enable tls with 'ldap ssl' below.
+; passdb backend = ldapsam:ldaps://ldap.mydomain.com smbpasswd guest
+# Use the samba2 LDAP schema:
+; passdb backend = ldapsam_compat:ldaps://ldap.mydomain.com smbpasswd guest
+
+# Idmap settings:
+# Idmap backend to use:
+; idmap backend = ldap:ldap://ldap.mydomain.com
+
+# This is a range of unix user-id's that samba will map non-unix RIDs to,
+# such as when using Winbind
+; idmap uid = 10000-20000
+; idmap gid = 10000-20000
+
+# LDAP configuration for Domain Controlling:
+# The account (dn) that samba uses to access the LDAP server
+# This account needs to have write access to the LDAP tree
+# You will need to give samba the password for this dn, by
+# running 'smbpasswd -w mypassword'
+; ldap admin dn = cn=root,dc=mydomain,dc=com
+; ldap ssl = start_tls
+# start_tls should run on 389, but samba defaults incorrectly to 636
+; ldap port = 389
+; ldap suffix = dc=mydomain,dc=com
+# Seperate suffixes are available for machines, users, groups, and idmap, if
+# ldap suffix appears first, it is appended to the specific suffix.
+# Example for a unix-ish directory layout:
+; ldap machine suffix = ou=Hosts
+; ldap user suffix = ou=People
+; ldap group suffix = ou=Group
+; ldap idmap suffix = ou=Idmap
+# Example for AD-ish layout:
+; ldap machine suffix = cn=Computers
+; ldap user suffix = cn=Users
+; ldap group suffix = cn=Groups
+; ldap idmap suffix = cn=Idmap
+
+
+# 7. Name Resolution Options:
+# All NetBIOS names must be resolved to IP Addresses
+# 'Name Resolve Order' allows the named resolution mechanism to be specified
+# the default order is "host lmhosts wins bcast". "host" means use the unix
+# system gethostbyname() function call that will use either /etc/hosts OR
+# DNS or NIS depending on the settings of /etc/host.config, /etc/nsswitch.conf
+# and the /etc/resolv.conf file. "host" therefore is system configuration
+# dependant. This parameter is most often of use to prevent DNS lookups
+# in order to resolve NetBIOS names to IP Addresses. Use with care!
+# The example below excludes use of name resolution for machines that are NOT
+# on the local network segment
+# - OR - are not deliberately to be known via lmhosts or via WINS.
+; name resolve order = wins lmhosts bcast
+
+# Windows Internet Name Serving Support Section:
+# WINS Support - Tells the NMBD component of Samba to enable it's WINS Server
+; wins support = yes
+
+# WINS Server - Tells the NMBD components of Samba to be a WINS Client
+# Note: Samba can be either a WINS Server, or a WINS Client, but NOT both
+; wins server = w.x.y.z
+
+# WINS Proxy - Tells Samba to answer name resolution queries on
+# behalf of a non WINS capable client, for this to work there must be
+# at least one WINS Server on the network. The default is NO.
+; wins proxy = yes
+
+# DNS Proxy - tells Samba whether or not to try to resolve NetBIOS names
+# via DNS nslookups. The built-in default for versions 1.9.17 is yes,
+# this has been changed in version 1.9.18 to no.
+ dns proxy = no
+
+# 8. File Naming Options:
+# Case Preservation can be handy - system default is _no_
+# NOTE: These can be set on a per share basis
+; preserve case = no
+; short preserve case = no
+# Default case is normally upper case for all DOS files
+; default case = lower
+# Be very careful with case sensitivity - it can break things!
+; case sensitive = no
+
+# Enabling internationalization:
+# you can match a Windows code page with a UNIX character set.
+# Windows: 437 (US), 737 (GREEK), 850 (Latin1 - Western European),
+# 852 (Eastern Eu.), 861 (Icelandic), 932 (Cyrillic - Russian),
+# 936 (Japanese - Shift-JIS), 936 (Simpl. Chinese), 949 (Korean Hangul),
+# 950 (Trad. Chin.).
+# UNIX: ISO8859-1 (Western European), ISO8859-2 (Eastern Eu.),
+# ISO8859-5 (Russian Cyrillic), KOI8-R (Alt-Russ. Cyril.)
+# This is an example for french users:
+; dos charset = 850
+; unix charset = ISO8859-1
+
+
+#============================ Share Definitions ==============================
+[homes]
+ comment = Home Directories
+ browseable = no
+ writable = yes
+# You can enable VFS recycle bin on a per share basis:
+# Uncomment the next 2 lines (make sure you create a
+# .recycle folder in the base of the share and ensure
+# all users will have write access to it. See
+# examples/VFS/recycle/REAME in samba-doc for details
+; vfs object = /usr/lib/samba/vfs/recycle.so
+
+# Un-comment the following and create the netlogon directory for Domain Logons
+; [netlogon]
+; comment = Network Logon Service
+; path = /var/lib/samba/netlogon
+; guest ok = yes
+; writable = no
+
+#Uncomment the following 2 lines if you would like your login scripts to
+#be created dynamically by ntlogon (check that you have it in the correct
+#location (the default of the ntlogon rpm available in contribs)
+;root preexec = /usr/bin/ntlogon -u %U -g %G -o %a -d /var/lib/samba/netlogon
+;root postexec = rm -f /var/lib/samba/netlogon/%U.bat
+
+# Un-comment the following to provide a specific roving profile share
+# the default is to use the user's home directory
+;[Profiles]
+; path = /var/lib/samba/profiles
+; browseable = no
+; guest ok = yes
+# This script can be enabled to create profile directories on the fly
+# You may want to turn off guest acces if you enable this, as it
+# hasn't been thoroughly tested.
+;root preexec = PROFILE=/var/lib/samba/profiles/%u; if [ ! -e $PROFILE ]; \
+; then mkdir -pm700 $PROFILE; chown %u.%g $PROFILE;fi
+
+# NOTE: If you have a CUPS print system there is no need to
+# specifically define each individual printer.
+# You must configure the samba printers with the appropriate Windows
+# drivers on your Windows clients or upload the printer driver to the
+# server from Windows (NT/2000/XP). On the Samba server no filtering is
+# done. If you wish that the server provides the driver and the clients
+# send PostScript ("Generic PostScript Printer" under Windows), you have
+# to use 'printcap name = cups' or swap the 'print command' line below
+# with the commented one. Note that print commands only work if not using
+# 'printing=cups'
+[printers]
+ comment = All Printers
+ path = /var/spool/samba
+ browseable = no
+# to allow user 'guest account' to print.
+ guest ok = yes
+ writable = no
+ printable = yes
+ create mode = 0700
+# =====================================
+# print command: see above for details.
+# =====================================
+ print command = lpr-cups -P %p -o raw %s -r # using client side printer drivers.
+; print command = lpr-cups -P %p %s # using cups own drivers (use generic PostScript on clients).
+
+# This share is used for Windows NT-style point-and-print support.
+# To be able to install drivers, you need to be either root, or listed
+# in the printer admin parameter above. Note that you also need write access
+# to the directory and share definition to be able to upload the drivers.
+# For more information on this, please see the Printing Support Section of
+# /usr/share/doc/samba-<version>/docs/Samba-HOWTO-Collection.pdf
+#
+# A special case is using the CUPS Windows Postscript driver, which allows
+# all features available via CUPS on the client, by publishing the ppd file
+# and the cups driver by using the 'cupsaddsmb' tool. This requires the
+# installation of the CUPS driver (http://www.cups.org/windows.php)
+# on the server, but doesn't require you to use Windows at all :-).
+[print$]
+ path = /var/lib/samba/printers
+ browseable = yes
+ write list = @adm root
+ guest ok = yes
+ inherit permissions = yes
+ # Settings suitable for Winbind:
+ ; write list = @"Domain Admins" root
+ ; force group = +@"Domain Admins"
+
+# A useful application of samba is to make a PDF-generation service
+# To streamline this, install windows postscript drivers (preferably colour)
+# on the samba server, so that clients can automatically install them.
+# Note that this only works if 'printing' is *not* set to 'cups'
+
+[pdf-generator]
+ path = /var/tmp
+ guest ok = No
+ printable = Yes
+ comment = PDF Generator (only valid users)
+ #print command = /usr/share/samba/scripts/print-pdf file path win_path recipient IP &
+ print command = /usr/share/samba/scripts/print-pdf %s ~%u //%L/%u %m %I "%J" &
+
+# This one is useful for people to share files
+;[tmp]
+; comment = Temporary file space
+; path = /tmp
+; read only = no
+; public = yes
+
+# A publicly accessible directory, but read only, except for people in
+# the "staff" group
+;[public]
+; comment = Public Stuff
+; path = /home/samba/public
+; public = yes
+; writable = no
+; write list = @staff
+# Audited directory through experimental VFS audit.so module:
+# Uncomment next line.
+; vfs object = /usr/lib/samba/vfs/audit.so
+
+# Other examples.
+#
+# A private printer, usable only by Fred. Spool data will be placed in Fred's
+# home directory. Note that fred must have write access to the spool directory,
+# wherever it is.
+;[fredsprn]
+; comment = Fred's Printer
+; valid users = fred
+; path = /homes/fred
+; printer = freds_printer
+; public = no
+; writable = no
+; printable = yes
+
+# A private directory, usable only by Fred. Note that Fred requires write
+# access to the directory.
+;[fredsdir]
+; comment = Fred's Service
+; path = /usr/somewhere/private
+; valid users = fred
+; public = no
+; writable = yes
+; printable = no
+
+# a service which has a different directory for each machine that connects
+# this allows you to tailor configurations to incoming machines. You could
+# also use the %u option to tailor it by user name.
+# The %m gets replaced with the machine name that is connecting.
+;[pchome]
+; comment = PC Directories
+; path = /usr/pc/%m
+; public = no
+; writable = yes
+
+# A publicly accessible directory, read/write to all users. Note that all files
+# created in the directory by users will be owned by the default user, so
+# any user with access can delete any other user's files. Obviously this
+# directory must be writable by the default user. Another user could of course
+# be specified, in which case all files would be owned by that user instead.
+;[public]
+; path = /usr/somewhere/else/public
+; public = yes
+; only guest = yes
+; writable = yes
+; printable = no
+
+# The following two entries demonstrate how to share a directory so that two
+# users can place files there that will be owned by the specific users. In this
+# setup, the directory should be writable by both users and should have the
+# sticky bit set on it to prevent abuse. Obviously this could be extended to
+# as many users as required.
+;[myshare]
+; comment = Mary's and Fred's stuff
+; path = /usr/somewhere/shared
+; valid users = mary fred
+; public = no
+; writable = yes
+; printable = no
+; create mask = 0765
+
diff --git a/packaging/Mandrake/smb.init b/packaging/Mandrake/smb.init
new file mode 100755
index 00000000000..c5d3c8dc21e
--- /dev/null
+++ b/packaging/Mandrake/smb.init
@@ -0,0 +1,100 @@
+#!/bin/sh
+#
+# chkconfig: 35 91 9
+# description: Starts and stops the Samba smbd and nmbd daemons \
+# used to provide SMB network services.
+
+# Source function library.
+if [ -f /etc/init.d/functions ] ; then
+ . /etc/init.d/functions
+elif [ -f /etc/rc.d/init.d/functions ] ; then
+ . /etc/rc.d/init.d/functions
+else
+ exit 0
+fi
+
+# Source networking configuration.
+. /etc/sysconfig/network
+
+# Check that networking is up.
+[ ${NETWORKING} = "no" ] && exit 0
+
+# Check that smb.conf exists.
+[ -f /etc/samba/smb.conf ] || exit 0
+
+RETVAL=0
+
+
+start() {
+ # If CUPS is used as printing system, reload smb after a 1 minute delay
+ # to allow the printers to appear properly as samba shares.
+ if killall -0 cupsd 2>/dev/null; then
+ ( sleep 60 && killproc smbd -HUP 1>/dev/null) &
+ fi
+ export TMPDIR="/var/tmp"
+ echo -n "Starting SMB services: "
+ daemon smbd -D
+ RETVAL=$?
+ echo
+ echo -n "Starting NMB services: "
+ daemon nmbd -D
+ RETVAL2=$?
+ echo
+ [ $RETVAL -eq 0 -a $RETVAL2 -eq 0 ] && touch /var/lock/subsys/smb || \
+ RETVAL=1
+ return $RETVAL
+}
+stop() {
+ echo -n "Shutting down SMB services: "
+ killproc smbd
+ RETVAL=$?
+ echo
+ echo -n "Shutting down NMB services: "
+ killproc nmbd
+ RETVAL2=$?
+ [ $RETVAL -eq 0 -a $RETVAL2 -eq 0 ] && rm -f /var/lock/subsys/smb
+ echo ""
+ return $RETVAL
+}
+restart() {
+ stop
+ start
+}
+reload() {
+ export TMPDIR="/var/tmp"
+ echo -n "Reloading smb.conf file: "
+ killproc smbd -HUP
+ RETVAL=$?
+ echo
+ return $RETVAL
+}
+mdkstatus() {
+ status smbd
+ status nmbd
+}
+
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ restart)
+ restart
+ ;;
+ reload)
+ reload
+ ;;
+ status)
+ mdkstatus
+ ;;
+ condrestart)
+ [ -f /var/lock/subsys/smb ] && restart || :
+ ;;
+ *)
+ echo "Usage: $0 {start|stop|restart|status|condrestart}"
+ exit 1
+esac
+
+exit $?
diff --git a/packaging/Mandrake/smbprint b/packaging/Mandrake/smbprint
new file mode 100755
index 00000000000..b5f689f2912
--- /dev/null
+++ b/packaging/Mandrake/smbprint
@@ -0,0 +1,77 @@
+#!/bin/sh
+
+# This script is an input filter for printcap printing on a unix machine. It
+# uses the smbclient program to print the file to the specified smb-based
+# server and service.
+# For example you could have a printcap entry like this
+#
+# smb:lp=/dev/null:sd=/usr/spool/smb:sh:if=/usr/local/samba/smbprint
+#
+# which would create a unix printer called "smb" that will print via this
+# script. You will need to create the spool directory /usr/spool/smb with
+# appropriate permissions and ownerships for your system.
+
+# Set these to the server and service you wish to print to
+# In this example I have a WfWg PC called "lapland" that has a printer
+# exported called "printer" with no password.
+
+#
+# Script further altered by hamiltom@ecnz.co.nz (Michael Hamilton)
+# so that the server, service, and password can be read from
+# a /var/spool/lpd/PRINTNAME/.config file.
+#
+# In order for this to work the /etc/printcap entry must include an
+# accounting file (af=...):
+#
+# cdcolour:\
+# :cm=CD IBM Colorjet on 6th:\
+# :sd=/var/spool/lpd/cdcolour:\
+# :af=/var/spool/lpd/cdcolour/acct:\
+# :if=/usr/local/etc/smbprint:\
+# :mx=0:\
+# :lp=/dev/null:
+#
+# The /usr/var/spool/lpd/PRINTNAME/.config file should contain:
+# server=PC_SERVER
+# service=PR_SHARENAME
+# password="password"
+#
+# E.g.
+# server=PAULS_PC
+# service=CJET_371
+# password=""
+
+#
+# Debugging log file, change to /dev/null if you like.
+#
+# logfile=/tmp/smb-print.log
+logfile=/dev/null
+
+
+#
+# The last parameter to the filter is the accounting file name.
+# Extract the directory name from the file name.
+# Concat this with /.config to get the config file.
+#
+eval acct_file=\${$#}
+spool_dir=`dirname $acct_file`
+config_file=$spool_dir/.config
+
+# Should read the following variables set in the config file:
+# server
+# service
+# password
+eval `cat $config_file`
+
+#
+# Some debugging help, change the >> to > if you want to same space.
+#
+echo "server $server, service $service" >> $logfile
+
+(
+# NOTE You may wish to add the line `echo translate' if you want automatic
+# CR/LF translation when printing.
+# echo translate
+ echo "print -"
+ cat
+) | /usr/bin/smbclient "//$server/$service" $password -U $server -N >> $logfile
diff --git a/packaging/Mandrake/smbusers b/packaging/Mandrake/smbusers
new file mode 100644
index 00000000000..ae3389f53f8
--- /dev/null
+++ b/packaging/Mandrake/smbusers
@@ -0,0 +1,3 @@
+# Unix_name = SMB_name1 SMB_name2 ...
+root = administrator admin
+nobody = guest pcguest smbguest
diff --git a/packaging/Mandrake/smbw.patch b/packaging/Mandrake/smbw.patch
new file mode 100644
index 00000000000..0abbfdf73f6
--- /dev/null
+++ b/packaging/Mandrake/smbw.patch
@@ -0,0 +1,10 @@
+--- samba-2.0.0/source/smbwrapper/smbsh.in.orig Mon Oct 5 22:37:01 1998
++++ samba-2.0.0/source/smbwrapper/smbsh.in Mon Oct 5 22:37:51 1998
+@@ -1,6 +1,6 @@
+ #! /bin/sh
+
+-SMBW_LIBDIR=${SMBW_LIBDIR-@builddir@/smbwrapper}
++SMBW_LIBDIR=${SMBW_LIBDIR-/usr/bin}
+
+ if [ ! -f ${SMBW_LIBDIR}/smbwrapper.so ]; then
+ echo You need to set LIBDIR in smbsh
diff --git a/packaging/Mandrake/swat_16.png.bz2 b/packaging/Mandrake/swat_16.png.bz2
new file mode 100644
index 00000000000..25522cab06b
--- /dev/null
+++ b/packaging/Mandrake/swat_16.png.bz2
Binary files differ
diff --git a/packaging/Mandrake/swat_32.png.bz2 b/packaging/Mandrake/swat_32.png.bz2
new file mode 100644
index 00000000000..737d16034fa
--- /dev/null
+++ b/packaging/Mandrake/swat_32.png.bz2
Binary files differ
diff --git a/packaging/Mandrake/swat_48.png.bz2 b/packaging/Mandrake/swat_48.png.bz2
new file mode 100644
index 00000000000..3e921c1feb5
--- /dev/null
+++ b/packaging/Mandrake/swat_48.png.bz2
Binary files differ
diff --git a/packaging/Mandrake/system-auth-winbind.pamd b/packaging/Mandrake/system-auth-winbind.pamd
new file mode 100644
index 00000000000..af859af72b2
--- /dev/null
+++ b/packaging/Mandrake/system-auth-winbind.pamd
@@ -0,0 +1,17 @@
+#%PAM-1.0
+
+auth required /lib/security/pam_env.so
+auth sufficient /lib/security/pam_winbind.so
+auth sufficient /lib/security/pam_unix.so likeauth nullok use_first_pass
+auth required /lib/security/pam_deny.so
+
+account sufficient /lib/security/pam_winbind.so
+account required /lib/security/pam_unix.so
+
+password required /lib/security/pam_cracklib.so retry=3
+password sufficient /lib/security/pam_unix.so nullok use_authtok md5 shadow
+password required /lib/security/pam_deny.so
+
+session required /lib/security/pam_mkhomedir.so skel=/etc/skel/ umask=0022
+session required /lib/security/pam_limits.so
+session required /lib/security/pam_unix.so
diff --git a/packaging/Mandrake/winbind.init b/packaging/Mandrake/winbind.init
new file mode 100644
index 00000000000..06e5b375d0c
--- /dev/null
+++ b/packaging/Mandrake/winbind.init
@@ -0,0 +1,93 @@
+#!/bin/sh
+#
+# chkconfig: 345 81 45
+# description: Starts and stops the Samba winbind daemon to provide \
+# user and group information from a domain controller to linux.
+
+# Source function library.
+if [ -f /etc/init.d/functions ] ; then
+ . /etc/init.d/functions
+elif [ -f /etc/rc.d/init.d/functions ] ; then
+ . /etc/rc.d/init.d/functions
+else
+ exit 0
+fi
+
+# Source networking configuration.
+. /etc/sysconfig/network
+
+# Check that networking is up.
+[ ${NETWORKING} = "no" ] && exit 0
+
+# Check that smb.conf exists.
+[ -f /etc/samba/smb.conf ] || exit 0
+
+RETVAL=0
+
+
+start() {
+ echo -n "Starting Winbind services: "
+ RETVAL=1
+ if [ "`grep -i -E '(idmap|winbind) uid' /etc/samba/smb.conf | egrep -v [\#\;]`" -a "`grep -i -E '(idmap|winbind) gid' /etc/samba/smb.conf | egrep -v [\#\;]`" ]; then
+ daemon winbindd
+ RETVAL=$?
+ else
+ echo "Winbind is not configured in /etc/samba/smb.conf, not starting"
+ fi
+ echo
+ [ $RETVAL -eq 0 ] && touch /var/lock/subsys/winbind || \
+ RETVAL=1
+ return $RETVAL
+}
+stop() {
+ echo -n "Shutting down Winbind services: "
+ RETVAL=1
+ if [ "`grep -i 'winbind uid' /etc/samba/smb.conf | egrep -v [\#\;]`" -a "`grep -i 'idmap gid' /etc/samba/smb.conf | egrep -v [\#\;]`" ]; then
+ killproc winbindd
+ RETVAL=$?
+ fi
+ echo
+ [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/winbind
+ return $RETVAL
+}
+restart() {
+ stop
+ start
+}
+reload() {
+ export TMPDIR="/var/tmp"
+ echo -n "Checking domain trusts: "
+ killproc winbindd -HUP
+ RETVAL=$?
+ echo
+ return $RETVAL
+}
+mdkstatus() {
+ status winbindd
+}
+
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ restart)
+ restart
+ ;;
+ reload)
+ reload
+ ;;
+ status)
+ mdkstatus
+ ;;
+ condrestart)
+ [ -f /var/lock/subsys/winbindd ] && restart || :
+ ;;
+ *)
+ echo "Usage: $0 {start|stop|restart|status|condrestart}"
+ exit 1
+esac
+
+exit $?
diff --git a/packaging/Mandrake/wrepld.init b/packaging/Mandrake/wrepld.init
new file mode 100644
index 00000000000..b8057f5f40a
--- /dev/null
+++ b/packaging/Mandrake/wrepld.init
@@ -0,0 +1,93 @@
+#!/bin/sh
+#
+# chkconfig: 345 81 45
+# description: Starts and stops the Samba wrepld daemon to provide \
+# WINS replication services between WINS partner servers.
+
+# Source function library.
+if [ -f /etc/init.d/functions ] ; then
+ . /etc/init.d/functions
+elif [ -f /etc/rc.d/init.d/functions ] ; then
+ . /etc/rc.d/init.d/functions
+else
+ exit 0
+fi
+
+# Source networking configuration.
+. /etc/sysconfig/network
+
+# Check that networking is up.
+[ ${NETWORKING} = "no" ] && exit 0
+
+# Check that smb.conf exists.
+[ -f /etc/samba/smb.conf ] || exit 0
+
+RETVAL=0
+
+
+start() {
+ echo -n "Starting WINS Replication services: "
+ RETVAL=1
+ if [ "`grep -i 'wins partners' /etc/samba/smb.conf | egrep -v [\#\;]`" ]; then
+ daemon wrepld
+ RETVAL=$?
+ else
+ echo "WINS replication is not configured in /etc/samba/smb.conf, not starting"
+ fi
+ echo
+ [ $RETVAL -eq 0 ] && touch /var/lock/subsys/wrepld || \
+ RETVAL=1
+ return $RETVAL
+}
+stop() {
+ echo -n "Shutting down WINS replication services: "
+ RETVAL=1
+ if [ "`grep -i 'wins partners' /etc/samba/smb.conf | egrep -v [\#\;]`" ]; then
+ killproc wrepld
+ RETVAL=$?
+ fi
+ echo
+ [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/wrepld
+ return $RETVAL
+}
+restart() {
+ stop
+ start
+}
+reload() {
+ export TMPDIR="/var/tmp"
+ echo -n "Reloading WINS replication: "
+ killproc wrepld -HUP
+ RETVAL=$?
+ echo
+ return $RETVAL
+}
+mdkstatus() {
+ status wrepld
+}
+
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ restart)
+ restart
+ ;;
+ reload)
+ reload
+ ;;
+ status)
+ mdkstatus
+ ;;
+ condrestart)
+ [ -f /var/lock/subsys/wrepld ] && restart || :
+ ;;
+ *)
+ echo "Usage: $0 {start|stop|restart|status|condrestart}"
+ exit 1
+esac
+
+exit $?
diff --git a/packaging/README b/packaging/README
new file mode 100644
index 00000000000..74445508e0b
--- /dev/null
+++ b/packaging/README
@@ -0,0 +1,32 @@
+Copyright (C) 1997-2003 Samba-Team
+Date: November 16, 1998
+Updates: First Release - 19970819
+ 19981116
+ 20030329
+ 20030905
+===============================================================================
+
+Note:
+=====
+This directory is a public repository for platform specific files including
+build files for binary package distributions for specific operating systems
+as well as for source file distribution packages for those systems.
+
+The Example directory should be used as a guide for preparation of binary
+packages for distribution via the official samba ftp sites.
+
+The files contained here are intended for use only by those wishing to build
+distribution packages and are NOT considered suitable material for anyone who
+wants to just install Samba from the pristine source files contained under
+the ../source directory.
+
+All contributions / modifications / additions / etc. to the packaging files
+should be logged in https://bugzilla.samba.org/.
+
+In the event that anyone wishes to contribute package build information please
+indicate in your response how we may access a suitable system to ensure our
+ability to keep the binary distribution itself current with the released source.
+
+The future of cooperatively developed software such as Samba depends on the
+willingness of all partners to share the fruit of their labours.
+
diff --git a/packaging/RedHat/.cvsignore b/packaging/RedHat/.cvsignore
new file mode 100644
index 00000000000..4ce9d934e6e
--- /dev/null
+++ b/packaging/RedHat/.cvsignore
@@ -0,0 +1,6 @@
+makefile-path.patch
+makerpms.sh
+samba2.spec
+smbadduser
+smbw.patch
+samba2.rpm?.spec \ No newline at end of file
diff --git a/packaging/RedHat/README b/packaging/RedHat/README
new file mode 100644
index 00000000000..646b10dbbbf
--- /dev/null
+++ b/packaging/RedHat/README
@@ -0,0 +1,13 @@
+Preparer: Gerald Carter <jerry@samba.org>
+
+Instructions: Preparing Samba Packages for Red Hat Linux
+===============================================================
+
+We provide support only for the latest stable release of major
+branches (e.g 6.2, 7.3, and 8.0). The makerpms.sh script
+supports rpm version 2.x, 3.x, and 4.x
+
+To produce the RPMS simply type:
+
+ root# sh makerpms.sh
+
diff --git a/packaging/RedHat/filter-requires-samba_rh8.sh b/packaging/RedHat/filter-requires-samba_rh8.sh
new file mode 100755
index 00000000000..8428a6ad013
--- /dev/null
+++ b/packaging/RedHat/filter-requires-samba_rh8.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+/usr/lib/rpm/find-requires $* | egrep -v '(Net::LDAP|CGI)'
diff --git a/packaging/RedHat/filter-requires-samba_rh9.sh b/packaging/RedHat/filter-requires-samba_rh9.sh
new file mode 100755
index 00000000000..8378523bceb
--- /dev/null
+++ b/packaging/RedHat/filter-requires-samba_rh9.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+/usr/lib/rpm/perl.req $* | egrep -v '(Net::LDAP|CGI)'
diff --git a/packaging/RedHat/makerpms.sh.tmpl b/packaging/RedHat/makerpms.sh.tmpl
new file mode 100644
index 00000000000..a37e03a1447
--- /dev/null
+++ b/packaging/RedHat/makerpms.sh.tmpl
@@ -0,0 +1,71 @@
+#!/bin/sh
+# Copyright (C) John H Terpstra 1998-2002
+# Updated for RPM 3 by Jochen Wiedmann, joe@ispsoft.de
+# Changed for a generic tar file rebuild by abartlet@pcug.org.au
+# Changed by John H Terpstra to build on RH7.2 - should also work for earlier versions jht@samba.org
+
+# The following allows environment variables to override the target directories
+# the alternative is to have a file in your home directory calles .rpmmacros
+# containing the following:
+# %_topdir /home/mylogin/redhat
+#
+# Note: Under this directory rpm expects to find the same directories that are under the
+# /usr/src/redhat directory
+#
+
+SPECDIR=`rpm --eval %_specdir`
+SRCDIR=`rpm --eval %_sourcedir`
+
+# At this point the SPECDIR and SRCDIR vaiables must have a value!
+
+USERID=`id -u`
+GRPID=`id -g`
+VERSION='PVERSION'
+SPECFILE="samba3.spec"
+RPMVER=`rpm --version | awk '{print $3}'`
+RPM="rpm"
+echo The RPM Version on this machine is: $RPMVER
+
+##
+## fix the mandir macro
+##
+case $RPMVER in
+ [23]*)
+ sed -e "s/MANDIR_MACRO/\%\{prefix\}\/man/g" < samba.spec > $SPECFILE
+ ;;
+ 4*)
+ sed -e "s/MANDIR_MACRO/\%\{_mandir\}/g" < samba.spec > $SPECFILE
+ ;;
+ *)
+ echo "Unknown RPM version: `rpm --version`"
+ exit 1
+ ;;
+esac
+
+##
+## now catch the right command to build an RPM (defaults ro 'rpm'
+##
+case $RPMVER in
+ 4.[12]*)
+ RPM="rpmbuild"
+ ;;
+esac
+
+echo "RPM build command is \"$RPM\""
+
+( cd ../../source; if [ -f Makefile ]; then make distclean; fi )
+( cd ../../.. ; chown -R ${USERID}.${GRPID} samba-${VERSION} )
+
+( cd ../../.. ; tar --exclude=CVS -cf - samba-${VERSION}/. | bzip2 > ${SRCDIR}/samba-${VERSION}.tar.bz2 )
+
+/bin/cp -p filter-requires-samba_rh8.sh ${SRCDIR}
+/bin/cp -p filter-requires-samba_rh9.sh ${SRCDIR}
+chmod 755 ${SRCDIR}/filter-requires-samba_rh?.sh
+/bin/cp -av $SPECFILE ${SPECDIR}
+
+echo Getting Ready to build release package
+cd ${SPECDIR}
+${RPM} -ba --clean --rmsource $SPECFILE
+
+echo Done.
+
diff --git a/packaging/RedHat/samba.log b/packaging/RedHat/samba.log
new file mode 100644
index 00000000000..4b244099c4f
--- /dev/null
+++ b/packaging/RedHat/samba.log
@@ -0,0 +1,11 @@
+/var/log/samba/log.nmbd {
+ postrotate
+ /usr/bin/killall -HUP nmbd
+ endscript
+}
+
+/var/log/samba/log.smbd {
+ postrotate
+ /usr/bin/killall -HUP smbd
+ endscript
+}
diff --git a/packaging/RedHat/samba.pamd b/packaging/RedHat/samba.pamd
new file mode 100644
index 00000000000..bf7a5b392ca
--- /dev/null
+++ b/packaging/RedHat/samba.pamd
@@ -0,0 +1,4 @@
+auth required /lib/security/pam_pwdb.so nullok
+account required /lib/security/pam_pwdb.so
+session required /lib/security/pam_pwdb.so
+password required /lib/security/pam_pwdb.so # shadow md5 nullok audit
diff --git a/packaging/RedHat/samba.pamd.stack b/packaging/RedHat/samba.pamd.stack
new file mode 100644
index 00000000000..6a948f92cbd
--- /dev/null
+++ b/packaging/RedHat/samba.pamd.stack
@@ -0,0 +1,6 @@
+#%PAM-1.0
+auth required pam_nologin.so
+auth required pam_stack.so service=system-auth
+account required pam_stack.so service=system-auth
+session required pam_stack.so service=system-auth
+password required pam_stack.so service=system-auth
diff --git a/packaging/RedHat/samba.spec.tmpl b/packaging/RedHat/samba.spec.tmpl
new file mode 100644
index 00000000000..787fe87d4ae
--- /dev/null
+++ b/packaging/RedHat/samba.spec.tmpl
@@ -0,0 +1,457 @@
+## grab the major and minor version of rpm
+%define rpm_version `rpm --version | awk '{print $3}' | awk -F. '{print $1$2}'`
+
+Summary: Samba SMB client and server
+Vendor: Samba Team
+Name: samba
+Version: PVERSION
+Release: PRELEASE
+License: GNU GPL version 2
+Group: Networking
+Source: http://download.samba.org/samba/ftp/samba-%{version}.tar.bz2
+
+# Don't depend on Net::LDAP
+# one filter for RH 8 and one for 9
+Source998: filter-requires-samba_rh8.sh
+Source999: filter-requires-samba_rh9.sh
+
+Packager: Gerald Carter [Samba-Team] <jerry@samba.org>
+Requires: pam >= 0.72 kernel >= 2.2.1 glibc >= 2.1.2
+Prereq: chkconfig fileutils /sbin/ldconfig
+Provides: samba = %{version}
+Obsoletes: samba-common, samba-client, samba-swat
+BuildRoot: %{_tmppath}/%{name}-%{version}-root
+Prefix: /usr
+
+%description
+Samba provides an SMB/CIFS server which can be used to provide
+network file and print services to SMB/CIFS clients, including
+various versions of MS Windows, OS/2, and other Linux machines.
+Samba also provides some SMB clients, which complement the
+built-in SMB filesystem in Linux. Samba uses NetBIOS over TCP/IP
+(NetBT) protocols and does NOT need NetBEUI (Microsoft Raw NetBIOS
+frame) protocol.
+
+Samba 3.0 also introduces UNICODE support and kerberos/ldap
+integration as a member server in a Windows 2000 domain.
+
+Please refer to the WHATSNEW.txt document for fixup information.
+docs directory for implementation details.
+
+%changelog
+* Mon Nov 18 2002 Gerald Carter <jerry@samba.org>
+ - removed change log entries since history
+ is being maintained in CVS
+
+%prep
+%setup
+
+%build
+
+# Working around perl dependency problem from docs
+# Only > RH 8.0 seems to care here
+
+echo "rpm_version == %{rpm_version}"
+if [ "%{rpm_version}" == "42" ]; then
+ %define __perl_requires %{SOURCE999}
+ echo "%{__perl_requires}"
+elif [ "%{rpm_version}" == "41" ]; then
+ %define __find_requires %{SOURCE998}
+ echo "%{__find_requires}"
+fi
+
+## Build main Samba source
+cd source
+
+%ifarch ia64
+libtoolize --copy --force # get it to recognize IA-64
+autoheader
+autoconf
+EXTRA="-D_LARGEFILE64_SOURCE"
+%endif
+
+## Get number of cpu's, default for 1 cpu on error
+NUMCPU=`grep processor /proc/cpuinfo | wc -l`
+if [ $NUMCPU -eq 0 ]; then
+ NUMCPU=1;
+fi
+
+## run autogen if missing the configure script
+if [ ! -f "configure" ]; then
+ ./autogen.sh
+fi
+
+CFLAGS="$RPM_OPT_FLAGS $EXTRA" ./configure \
+ --prefix=%{prefix} \
+ --localstatedir=/var \
+ --with-configdir=/etc/samba \
+ --with-privatedir=/etc/samba \
+ --with-fhs \
+ --with-quotas \
+ --with-smbmount \
+ --with-pam \
+ --with-pam_smbpass \
+ --with-syslog \
+ --with-utmp \
+ --with-sambabook=%{prefix}/share/swat/using_samba \
+ --with-swatdir=%{prefix}/share/swat \
+ --with-libsmbclient
+make -j${NUMCPU} proto
+make -j${NUMCPU} all nsswitch/libnss_wins.so
+make -j${NUMCPU} debug2html
+
+# Remove some permission bits to avoid to many dependencies
+find examples docs -type f | xargs -r chmod -x
+
+%install
+rm -rf $RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT/sbin
+mkdir -p $RPM_BUILD_ROOT/etc/samba
+mkdir -p $RPM_BUILD_ROOT/etc/{logrotate.d,pam.d,samba}
+mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d
+mkdir -p $RPM_BUILD_ROOT%{prefix}/{bin,sbin}
+mkdir -p $RPM_BUILD_ROOT%{prefix}/share/swat/{images,help,include,using_samba}
+mkdir -p $RPM_BUILD_ROOT%{prefix}/share/swat/using_samba/{figs,gifs}
+mkdir -p $RPM_BUILD_ROOTMANDIR_MACRO
+mkdir -p $RPM_BUILD_ROOT/var/lib/samba
+mkdir -p $RPM_BUILD_ROOT/var/{log,run}/samba
+mkdir -p $RPM_BUILD_ROOT/var/spool/samba
+mkdir -p $RPM_BUILD_ROOT/lib/security
+mkdir -p $RPM_BUILD_ROOT%{prefix}/lib/samba/vfs
+mkdir -p $RPM_BUILD_ROOT%{prefix}/{lib,include}
+
+# Install standard binary files
+for i in nmblookup smbclient smbpasswd smbstatus testparm testprns \
+ rpcclient smbspool smbcacls smbcontrol wbinfo smbmnt net \
+ smbcacls pdbedit tdbbackup smbtree ntlm_auth smbcquotas
+do
+ install -m755 source/bin/$i $RPM_BUILD_ROOT%{prefix}/bin
+done
+
+for i in mksmbpasswd.sh smbtar findsmb
+do
+ install -m755 source/script/$i $RPM_BUILD_ROOT%{prefix}/bin
+done
+
+# Install secure binary files
+for i in smbd nmbd swat smbmount smbumount debug2html winbindd
+do
+ install -m755 source/bin/$i $RPM_BUILD_ROOT%{prefix}/sbin
+done
+
+# we need a symlink for mount to recognise the smb and smbfs filesystem types
+ln -sf %{prefix}/sbin/smbmount $RPM_BUILD_ROOT/sbin/mount.smbfs
+ln -sf %{prefix}/sbin/smbmount $RPM_BUILD_ROOT/sbin/mount.smb
+
+# This allows us to get away without duplicating code that
+# sombody else can maintain for us.
+cd source
+make DESTDIR=$RPM_BUILD_ROOT \
+ BASEDIR=/usr \
+ CONFIGDIR=/etc/samba \
+ LIBDIR=%{prefix}/lib/samba \
+ VARDIR=/var \
+ SBINDIR=%{prefix}/sbin \
+ BINDIR=%{prefix}/bin \
+ MANDIR=MANDIR_MACRO \
+ SWATDIR=%{prefix}/share/swat \
+ SAMBABOOK=%{prefix}/share/swat/using_samba \
+ installman installswat installdat installmodules
+cd ..
+
+# Install the nsswitch wins library
+install -m755 source/nsswitch/libnss_wins.so $RPM_BUILD_ROOT/lib
+( cd $RPM_BUILD_ROOT/lib; ln -sf libnss_wins.so libnss_wins.so.2 )
+
+# Install winbind shared libraries
+install -m755 source/nsswitch/libnss_winbind.so $RPM_BUILD_ROOT/lib
+( cd $RPM_BUILD_ROOT/lib; ln -sf libnss_winbind.so libnss_winbind.so.2 )
+install -m755 source/nsswitch/pam_winbind.so $RPM_BUILD_ROOT/lib/security
+
+# Install pam_smbpass.so
+install -m755 source/bin/pam_smbpass.so $RPM_BUILD_ROOT/lib/security
+
+# libsmbclient
+install -m 755 source/bin/libsmbclient.so $RPM_BUILD_ROOT%{prefix}/lib/
+install -m 755 source/bin/libsmbclient.a $RPM_BUILD_ROOT%{prefix}/lib/
+install -m 644 source/include/libsmbclient.h $RPM_BUILD_ROOT%{prefix}/include/
+
+# Install the miscellany
+install -m755 packaging/RedHat/smbprint $RPM_BUILD_ROOT%{prefix}/bin
+install -m755 packaging/RedHat/smb.init $RPM_BUILD_ROOT/etc/rc.d/init.d/smb
+install -m755 packaging/RedHat/winbind.init $RPM_BUILD_ROOT/etc/rc.d/init.d/winbind
+install -m755 packaging/RedHat/smb.init $RPM_BUILD_ROOT%{prefix}/sbin/samba
+install -m644 packaging/RedHat/samba.log $RPM_BUILD_ROOT/etc/logrotate.d/samba
+install -m644 packaging/RedHat/smb.conf $RPM_BUILD_ROOT/etc/samba/smb.conf
+install -m644 packaging/RedHat/smbusers $RPM_BUILD_ROOT/etc/samba/smbusers
+install -m644 packaging/RedHat/samba.pamd $RPM_BUILD_ROOT/etc/pam.d/samba
+install -m644 packaging/RedHat/samba.pamd.stack $RPM_BUILD_ROOT/etc/samba/samba.stack
+install -m644 packaging/RedHat/samba.xinetd $RPM_BUILD_ROOT/etc/samba/samba.xinetd
+echo 127.0.0.1 localhost > $RPM_BUILD_ROOT/etc/samba/lmhosts
+
+# Remove "*.old" files
+find $RPM_BUILD_ROOT -name "*.old" -exec rm -f {} \;
+
+##
+## Clean out man pages for tools not installed here
+##
+rm -f $RPM_BUILD_ROOT/%{_mandir}/man1/editreg.1*
+rm -f $RPM_BUILD_ROOT%{_mandir}/man1/log2pcap.1*
+rm -f $RPM_BUILD_ROOT%{_mandir}/man1/smbsh.1*
+rm -f $RPM_BUILD_ROOT%{_mandir}/man1/smbget.1*
+rm -f $RPM_BUILD_ROOT/%{_mandir}/man8/mount.cifs.8*
+
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+##
+## only needed if this is a new install (not an upgrade)
+##
+if [ "$1" -eq "1" ]; then
+ /sbin/chkconfig --add smb
+ /sbin/chkconfig --add winbind
+ /sbin/chkconfig smb off
+ /sbin/chkconfig winbind off
+fi
+
+##
+## we only have to wory about this if we are upgrading
+##
+if [ "$1" -eq "2" ]; then
+ if [ -f /etc/smb.conf -a ! -f /etc/samba/smb.conf ]; then
+ echo "Moving old /etc/smb.conf to /etc/samba/smb.conf"
+ mv /etc/smb.conf /etc/samba/smb.conf
+ fi
+
+ if [ -f /etc/smbusers -a ! -f /etc/samba/smbusers ]; then
+ echo "Moving old /etc/smbusers to /etc/samba/smbusers"
+ mv /etc/smbusers /etc/samba/smbusers
+ fi
+
+ if [ -f /etc/lmhosts -a ! -f /etc/samba/lmhosts ]; then
+ echo "Moving old /etc/lmhosts to /etc/samba/lmhosts"
+ mv /etc/lmhosts /etc/samba/lmhosts
+ fi
+
+ if [ -f /etc/MACHINE.SID -a ! -f /etc/samba/MACHINE.SID ]; then
+ echo "Moving old /etc/MACHINE.SID to /etc/samba/MACHINE.SID"
+ mv /etc/MACHINE.SID /etc/samba/MACHINE.SID
+ fi
+
+ if [ -f /etc/smbpasswd -a ! -f /etc/samba/smbpasswd ]; then
+ echo "Moving old /etc/smbpasswd to /etc/samba/smbpasswd"
+ mv /etc/smbpasswd /etc/samba/smbpasswd
+ fi
+
+ #
+ # For 2.2.1 we move the tdb files from /var/lock/samba to /var/cache/samba
+ # to preserve across reboots.
+ #
+ for i in /var/lock/samba/*.tdb; do
+ if [ -f $i ]; then
+ newname="/var/lib/samba/`basename $i`"
+ echo "Moving $i to $newname"
+ mv $i $newname
+ fi
+ done
+
+ #
+ # For 3.0.1 we move the tdb files from /var/cache/samba to /var/lib/samba
+ #
+ echo "Moving tdb files in /var/cache/samba/*.tdb to /var/lib/samba/*.tdb"
+ for i in /var/cache/samba/*.tdb; do
+ if [ -f $i ]; then
+ newname="/var/lib/samba/`basename $i`"
+ echo "Moving $i to $newname"
+ mv $i $newname
+ fi
+ done
+fi
+
+##
+## New things
+##
+
+# Add swat entry to /etc/services if not already there.
+if [ ! "`grep ^\s**swat /etc/services`" ]; then
+ echo 'swat 901/tcp # Add swat service used via inetd' >> /etc/services
+fi
+
+# Add swat entry to /etc/inetd.conf if needed.
+if [ -f /etc/inetd.conf ]; then
+ if [ ! "`grep ^\s*swat /etc/inetd.conf`" ]; then
+ echo 'swat stream tcp nowait.400 root %{prefix}/sbin/swat swat' >> /etc/inetd.conf
+ killall -HUP inetd || :
+ fi
+fi
+
+# Add swat entry to xinetd.d if needed.
+if [ -d /etc/xinetd.d -a ! -f /etc/xinetd.d/swat ]; then
+ mv /etc/samba/samba.xinetd /etc/xinetd.d/swat
+else
+ rm -f /etc/samba/samba.xinetd
+fi
+
+# Install the correct version of the samba pam file
+if [ -f /lib/security/pam_stack.so ]; then
+ echo "Installing stack version of /etc/pam.d/samba..."
+ mv /etc/samba/samba.stack /etc/pam.d/samba
+else
+ echo "Installing non-stack version of /etc/pam.d/samba..."
+ rm -f /etc/samba/samba.stack
+fi
+
+## call ldconfig to create the version symlink for libsmbclient.so
+/sbin/ldconfig
+
+%preun
+if [ "$1" -eq "0" ] ; then
+ /sbin/chkconfig --del smb
+ /sbin/chkconfig --del winbind
+
+ # We want to remove the browse.dat and wins.dat files
+ # so they can not interfer with a new version of samba!
+ if [ -e /var/lib/samba/browse.dat ]; then
+ rm -f /var/lib/samba/browse.dat
+ fi
+ if [ -e /var/lib/samba/wins.dat ]; then
+ rm -f /var/lib/samba/wins.dat
+ fi
+
+ # Remove the transient tdb files.
+ if [ -e /var/lib/samba/brlock.tdb ]; then
+ rm -f /var/lib/samba/brlock.tdb
+ fi
+
+ if [ -e /var/lib/samba/unexpected.tdb ]; then
+ rm -f /var/lib/samba/unexpected.tdb
+ fi
+
+ if [ -e /var/lib/samba/connections.tdb ]; then
+ rm -f /var/lib/samba/connections.tdb
+ fi
+
+ if [ -e /var/lib/samba/locking.tdb ]; then
+ rm -f /var/lib/samba/locking.tdb
+ fi
+
+ if [ -e /var/lib/samba/messages.tdb ]; then
+ rm -f /var/lib/samba/messages.tdb
+ fi
+fi
+
+%postun
+# Only delete remnants of samba if this is the final deletion.
+if [ "$1" -eq "0" ] ; then
+ if [ -x /etc/pam.d/samba ]; then
+ rm -f /etc/pam.d/samba
+ fi
+
+ if [ -e /var/log/samba ]; then
+ rm -rf /var/log/samba
+ fi
+
+ if [ -e /var/lib/samba ]; then
+ rm -rf /var/lib/samba
+ fi
+
+ # Remove swat entries from /etc/inetd.conf and /etc/services
+ cd /etc
+ tmpfile=/etc/tmp.$$
+ if [ -f /etc/inetd.conf ]; then
+ # preserve inetd.conf permissions.
+ cp -p /etc/inetd.conf $tmpfile
+ sed -e '/^[:space:]*swat.*$/d' /etc/inetd.conf > $tmpfile
+ mv $tmpfile inetd.conf
+ fi
+
+ # preserve services permissions.
+ cp -p /etc/services $tmpfile
+ sed -e '/^[:space:]*swat.*$/d' /etc/services > $tmpfile
+ mv $tmpfile /etc/services
+
+ # Remove swat entry from /etc/xinetd.d
+ if [ -f /etc/xinetd.d/swat ]; then
+ rm -r /etc/xinetd.d/swat
+ fi
+fi
+
+/sbin/ldconfig
+
+%files
+%defattr(-,root,root)
+%doc README COPYING Manifest Read-Manifest-Now
+%doc WHATSNEW.txt Roadmap
+%doc docs
+%doc examples
+%{prefix}/sbin/smbd
+%{prefix}/sbin/nmbd
+%{prefix}/sbin/swat
+%{prefix}/bin/smbmnt
+%{prefix}/sbin/smbmount
+%{prefix}/sbin/smbumount
+%{prefix}/sbin/winbindd
+%{prefix}/sbin/samba
+%{prefix}/sbin/debug2html
+/sbin/mount.smbfs
+/sbin/mount.smb
+%{prefix}/bin/mksmbpasswd.sh
+%{prefix}/bin/smbclient
+%{prefix}/bin/smbspool
+%{prefix}/bin/rpcclient
+%{prefix}/bin/testparm
+%{prefix}/bin/testprns
+%{prefix}/bin/findsmb
+%{prefix}/bin/smbstatus
+%{prefix}/bin/nmblookup
+%{prefix}/bin/smbpasswd
+%{prefix}/bin/smbtar
+%{prefix}/bin/smbprint
+%{prefix}/bin/smbcontrol
+%{prefix}/bin/wbinfo
+%{prefix}/bin/net
+%{prefix}/bin/ntlm_auth
+%{prefix}/bin/smbcquotas
+%{prefix}/bin/smbcacls
+%{prefix}/bin/pdbedit
+%{prefix}/bin/tdbbackup
+%{prefix}/bin/smbtree
+%attr(755,root,root) /lib/libnss_wins.s*
+%attr(755,root,root) %{prefix}/lib/samba/vfs/*.so
+%attr(755,root,root) %{prefix}/lib/samba/charset/*.so
+#%attr(755,root,root) %{prefix}/lib/samba/pdb/*.so
+%attr(755,root,root) %{prefix}/lib/samba/*.dat
+%attr(755,root,root) %{prefix}/lib/samba/*.msg
+%{prefix}/include/libsmbclient.h
+%{prefix}/lib/libsmbclient.a
+%{prefix}/lib/libsmbclient.so
+%{prefix}/share/swat/help/*
+%{prefix}/share/swat/images/*
+%{prefix}/share/swat/include/*.html
+%{prefix}/share/swat/lang/*/help/*
+%{prefix}/share/swat/lang/*/images/*
+%{prefix}/share/swat/lang/*/include/*.html
+%{prefix}/share/swat/using_samba/*
+%config(noreplace) /etc/samba/lmhosts
+%config(noreplace) /etc/samba/smb.conf
+%config(noreplace) /etc/samba/smbusers
+/etc/samba/samba.stack
+/etc/samba/samba.xinetd
+/etc/rc.d/init.d/smb
+/etc/rc.d/init.d/winbind
+/etc/logrotate.d/samba
+%config(noreplace) /etc/pam.d/samba
+MANDIR_MACRO/man1/*
+MANDIR_MACRO/man5/*
+MANDIR_MACRO/man7/*
+MANDIR_MACRO/man8/*
+%attr(755,root,root) %dir /var/lib/samba
+%dir /var/log/samba
+%dir /var/run/samba
+%attr(1777,root,root) %dir /var/spool/samba
+%attr(-,root,root) /lib/libnss_winbind.so*
+%attr(-,root,root) /lib/security/pam_winbind.so
+%attr(-,root,root) /lib/security/pam_smbpass.so
diff --git a/packaging/RedHat/samba.xinetd b/packaging/RedHat/samba.xinetd
new file mode 100644
index 00000000000..8c38b354218
--- /dev/null
+++ b/packaging/RedHat/samba.xinetd
@@ -0,0 +1,15 @@
+# default: off
+# description: SWAT is the Samba Web Admin Tool. Use swat \
+# to configure your Samba server. To use SWAT, \
+# connect to port 901 with your favorite web browser.
+service swat
+{
+ port = 901
+ socket_type = stream
+ wait = no
+ only_from = localhost
+ user = root
+ server = /usr/sbin/swat
+ log_on_failure += USERID
+ disable = yes
+}
diff --git a/packaging/RedHat/smb.conf b/packaging/RedHat/smb.conf
new file mode 100644
index 00000000000..74806da16bb
--- /dev/null
+++ b/packaging/RedHat/smb.conf
@@ -0,0 +1,286 @@
+# This is the main Samba configuration file. You should read the
+# smb.conf(5) manual page in order to understand the options listed
+# here. Samba has a huge number of configurable options (perhaps too
+# many!) most of which are not shown in this example
+#
+# Any line which starts with a ; (semi-colon) or a # (hash)
+# is a comment and is ignored. In this example we will use a #
+# for commentry and a ; for parts of the config file that you
+# may wish to enable
+#
+# NOTE: Whenever you modify this file you should run the command "testparm"
+# to check that you have not made any basic syntactic errors.
+#
+#======================= Global Settings =====================================
+[global]
+
+# workgroup = NT-Domain-Name or Workgroup-Name
+ workgroup = MYGROUP
+
+# server string is the equivalent of the NT Description field
+ server string = Samba Server
+
+# This option is important for security. It allows you to restrict
+# connections to machines which are on your local network. The
+# following example restricts access to two C class networks and
+# the "loopback" interface. For more examples of the syntax see
+# the smb.conf man page
+; hosts allow = 192.168.1. 192.168.2. 127.
+
+# if you want to automatically load your printer list rather
+# than setting them up individually then you'll need this
+ printcap name = /etc/printcap
+ load printers = yes
+
+# It should not be necessary to spell out the print system type unless
+# yours is non-standard. Currently supported print systems include:
+# bsd, sysv, plp, lprng, aix, hpux, qnx
+; printing = bsd
+
+# Uncomment this if you want a guest account, you must add this to /etc/passwd
+# otherwise the user "nobody" is used
+; guest account = pcguest
+
+# this tells Samba to use a separate log file for each machine
+# that connects
+ log file = /var/log/samba/log.%m
+
+# Put a capping on the size of the log files (in Kb).
+ max log size = 50
+
+# Security mode. Most people will want user level security. See
+# security_level.txt for details.
+ security = user
+# Use password server option only with security = server
+; password server = <NT-Server-Name>
+
+# Password Level allows matching of _n_ characters of the password for
+# all combinations of upper and lower case.
+; password level = 8
+; username level = 8
+
+# You may wish to use password encryption. Please read
+# ENCRYPTION.txt, Win95.txt and WinNT.txt in the Samba documentation.
+# Do not enable this option unless you have read those documents
+; encrypt passwords = yes
+; smb passwd file = /etc/samba/smbpasswd
+
+# The following are needed to allow password changing from Windows to
+# update the Linux system password also.
+# NOTE: Use these with 'encrypt passwords' and 'smb passwd file' above.
+# NOTE2: You do NOT need these to allow workstations to change only
+# the encrypted SMB passwords. They allow the Unix password
+# to be kept in sync with the SMB password.
+; unix password sync = Yes
+; passwd program = /usr/bin/passwd %u
+; passwd chat = *New*UNIX*password* %n\n *ReType*new*UNIX*password* %n\n *passwd:*all*authentication*tokens*updated*successfully*
+
+# Unix users can map to different SMB User names
+; username map = /etc/samba/smbusers
+
+# Using the following line enables you to customise your configuration
+# on a per machine basis. The %m gets replaced with the netbios name
+# of the machine that is connecting
+; include = /etc/samba/smb.conf.%m
+
+# Most people will find that this option gives better performance.
+# See speed.txt and the manual pages for details
+ socket options = TCP_NODELAY SO_RCVBUF=8192 SO_SNDBUF=8192
+
+# Configure Samba to use multiple interfaces
+# If you have multiple network interfaces then you must list them
+# here. See the man page for details.
+; interfaces = 192.168.12.2/24 192.168.13.2/24
+
+# Configure remote browse list synchronisation here
+# request announcement to, or browse list sync from:
+# a specific host or from / to a whole subnet (see below)
+; remote browse sync = 192.168.3.25 192.168.5.255
+# Cause this host to announce itself to local subnets here
+; remote announce = 192.168.1.255 192.168.2.44
+
+# Browser Control Options:
+# set local master to no if you don't want Samba to become a master
+# browser on your network. Otherwise the normal election rules apply
+; local master = no
+
+# OS Level determines the precedence of this server in master browser
+# elections. The default value should be reasonable
+; os level = 33
+
+# Domain Master specifies Samba to be the Domain Master Browser. This
+# allows Samba to collate browse lists between subnets. Don't use this
+# if you already have a Windows NT domain controller doing this job
+; domain master = yes
+
+# Preferred Master causes Samba to force a local browser election on startup
+# and gives it a slightly higher chance of winning the election
+; preferred master = yes
+
+# Enable this if you want Samba to be a domain logon server for
+# Windows95 workstations.
+; domain logons = yes
+
+# if you enable domain logons then you may want a per-machine or
+# per user logon script
+# run a specific logon batch file per workstation (machine)
+; logon script = %m.bat
+# run a specific logon batch file per username
+; logon script = %U.bat
+
+# Where to store roving profiles (only for Win95 and WinNT)
+# %L substitutes for this servers netbios name, %U is username
+# You must uncomment the [Profiles] share below
+; logon path = \\%L\Profiles\%U
+
+# All NetBIOS names must be resolved to IP Addresses
+# 'Name Resolve Order' allows the named resolution mechanism to be specified
+# the default order is "host lmhosts wins bcast". "host" means use the unix
+# system gethostbyname() function call that will use either /etc/hosts OR
+# DNS or NIS depending on the settings of /etc/host.config, /etc/nsswitch.conf
+# and the /etc/resolv.conf file. "host" therefore is system configuration
+# dependant. This parameter is most often of use to prevent DNS lookups
+# in order to resolve NetBIOS names to IP Addresses. Use with care!
+# The example below excludes use of name resolution for machines that are NOT
+# on the local network segment
+# - OR - are not deliberately to be known via lmhosts or via WINS.
+; name resolve order = wins lmhosts bcast
+
+# Windows Internet Name Serving Support Section:
+# WINS Support - Tells the NMBD component of Samba to enable it's WINS Server
+; wins support = yes
+
+# WINS Server - Tells the NMBD components of Samba to be a WINS Client
+# Note: Samba can be either a WINS Server, or a WINS Client, but NOT both
+; wins server = w.x.y.z
+
+# WINS Proxy - Tells Samba to answer name resolution queries on
+# behalf of a non WINS capable client, for this to work there must be
+# at least one WINS Server on the network. The default is NO.
+; wins proxy = yes
+
+# DNS Proxy - tells Samba whether or not to try to resolve NetBIOS names
+# via DNS nslookups. The built-in default for versions 1.9.17 is yes,
+# this has been changed in version 1.9.18 to no.
+ dns proxy = no
+
+# Case Preservation can be handy - system default is _no_
+# NOTE: These can be set on a per share basis
+; preserve case = no
+; short preserve case = no
+# Default case is normally upper case for all DOS files
+; default case = lower
+# Be very careful with case sensitivity - it can break things!
+; case sensitive = no
+
+#============================ Share Definitions ==============================
+[homes]
+ comment = Home Directories
+ browseable = no
+ writable = yes
+
+# Un-comment the following and create the netlogon directory for Domain Logons
+; [netlogon]
+; comment = Network Logon Service
+; path = /home/netlogon
+; guest ok = yes
+; writable = no
+; share modes = no
+
+
+# Un-comment the following to provide a specific roving profile share
+# the default is to use the user's home directory
+;[Profiles]
+; path = /home/profiles
+; browseable = no
+; guest ok = yes
+
+
+# NOTE: If you have a BSD-style print system there is no need to
+# specifically define each individual printer
+[printers]
+ comment = All Printers
+ path = /var/spool/samba
+ browseable = no
+# Set public = yes to allow user 'guest account' to print
+ guest ok = no
+ writable = no
+ printable = yes
+
+# This one is useful for people to share files
+;[tmp]
+; comment = Temporary file space
+; path = /tmp
+; read only = no
+; public = yes
+
+# A publicly accessible directory, but read only, except for people in
+# the "staff" group
+;[public]
+; comment = Public Stuff
+; path = /home/samba
+; public = yes
+; read only = yes
+; write list = @staff
+
+# Other examples.
+#
+# A private printer, usable only by fred. Spool data will be placed in fred's
+# home directory. Note that fred must have write access to the spool directory,
+# wherever it is.
+;[fredsprn]
+; comment = Fred's Printer
+; valid users = fred
+; path = /homes/fred
+; printer = freds_printer
+; public = no
+; writable = no
+; printable = yes
+
+# A private directory, usable only by fred. Note that fred requires write
+# access to the directory.
+;[fredsdir]
+; comment = Fred's Service
+; path = /usr/somewhere/private
+; valid users = fred
+; public = no
+; writable = yes
+; printable = no
+
+# a service which has a different directory for each machine that connects
+# this allows you to tailor configurations to incoming machines. You could
+# also use the %u option to tailor it by user name.
+# The %m gets replaced with the machine name that is connecting.
+;[pchome]
+; comment = PC Directories
+; path = /usr/pc/%m
+; public = no
+; writable = yes
+
+# A publicly accessible directory, read/write to all users. Note that all files
+# created in the directory by users will be owned by the default user, so
+# any user with access can delete any other user's files. Obviously this
+# directory must be writable by the default user. Another user could of course
+# be specified, in which case all files would be owned by that user instead.
+;[public]
+; path = /usr/somewhere/else/public
+; public = yes
+; only guest = yes
+; writable = yes
+; printable = no
+
+# The following two entries demonstrate how to share a directory so that two
+# users can place files there that will be owned by the specific users. In this
+# setup, the directory should be writable by both users and should have the
+# sticky bit set on it to prevent abuse. Obviously this could be extended to
+# as many users as required.
+;[myshare]
+; comment = Mary's and Fred's stuff
+; path = /usr/somewhere/shared
+; valid users = mary fred
+; public = no
+; writable = yes
+; printable = no
+; create mask = 0765
+
+
diff --git a/packaging/RedHat/smb.init b/packaging/RedHat/smb.init
new file mode 100755
index 00000000000..79f4f322d03
--- /dev/null
+++ b/packaging/RedHat/smb.init
@@ -0,0 +1,59 @@
+#!/bin/sh
+#
+# chkconfig: 345 81 35
+# description: Starts and stops the Samba smbd and nmbd daemons \
+# used to provide SMB network services.
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+# Source networking configuration.
+. /etc/sysconfig/network
+
+# Check that networking is up.
+[ ${NETWORKING} = "no" ] && exit 0
+
+CONFIG=/etc/samba/smb.conf
+
+# Check that smb.conf exists.
+[ -f $CONFIG ] || exit 0
+
+# See how we were called.
+case "$1" in
+ start)
+ echo -n "Starting SMB services: "
+ daemon smbd -D
+ daemon nmbd -D
+ echo
+ touch /var/lock/subsys/smb
+ ;;
+ stop)
+ echo -n "Shutting down SMB services: "
+
+ ## we have to get all the smbd process here instead of just the
+ ## main parent (i.e. killproc) because it can take a long time
+ ## for an individual process to process a TERM signal
+ smbdpids=`ps guax | grep smbd | grep -v grep | awk '{print $2}'`
+ for pid in $smbdpids; do
+ kill -TERM $pid
+ done
+ ## nmbd is ok to kill using killproc()
+ killproc nmbd -TERM
+ rm -f /var/lock/subsys/smb
+ echo ""
+ ;;
+ status)
+ status smbd
+ status nmbd
+ ;;
+ restart)
+ echo -n "Restarting SMB services: "
+ $0 stop
+ $0 start
+ echo "done."
+ ;;
+ *)
+ echo "Usage: smb {start|stop|restart|status}"
+ exit 1
+esac
+
diff --git a/packaging/RedHat/smbprint b/packaging/RedHat/smbprint
new file mode 100755
index 00000000000..a0fd2e481b5
--- /dev/null
+++ b/packaging/RedHat/smbprint
@@ -0,0 +1,77 @@
+#!/bin/sh
+
+# This script is an input filter for printcap printing on a unix machine. It
+# uses the smbclient program to print the file to the specified smb-based
+# server and service.
+# For example you could have a printcap entry like this
+#
+# smb:lp=/dev/null:sd=/usr/spool/smb:sh:if=/usr/local/samba/smbprint
+#
+# which would create a unix printer called "smb" that will print via this
+# script. You will need to create the spool directory /usr/spool/smb with
+# appropriate permissions and ownerships for your system.
+
+# Set these to the server and service you wish to print to
+# In this example I have a WfWg PC called "lapland" that has a printer
+# exported called "printer" with no password.
+
+#
+# Script further altered by hamiltom@ecnz.co.nz (Michael Hamilton)
+# so that the server, service, and password can be read from
+# a /var/spool/lpd/PRINTNAME/.config file.
+#
+# In order for this to work the /etc/printcap entry must include an
+# accounting file (af=...):
+#
+# cdcolour:\
+# :cm=CD IBM Colorjet on 6th:\
+# :sd=/var/spool/lpd/cdcolour:\
+# :af=/var/spool/lpd/cdcolour/acct:\
+# :if=/usr/local/etc/smbprint:\
+# :mx=0:\
+# :lp=/dev/null:
+#
+# The /usr/var/spool/lpd/PRINTNAME/.config file should contain:
+# server=PC_SERVER
+# service=PR_SHARENAME
+# password="password"
+#
+# E.g.
+# server=PAULS_PC
+# service=CJET_371
+# password=""
+
+#
+# Debugging log file, change to /dev/null if you like.
+#
+# logfile=/tmp/smb-print.log
+logfile=/dev/null
+
+
+#
+# The last parameter to the filter is the accounting file name.
+# Extract the directory name from the file name.
+# Concat this with /.config to get the config file.
+#
+eval acct_file=\${$#}
+spool_dir=`dirname $acct_file`
+config_file=$spool_dir/.config
+
+# Should read the following variables set in the config file:
+# server
+# service
+# password
+eval `cat $config_file`
+
+#
+# Some debugging help, change the >> to > if you want to same space.
+#
+echo "server $server, service $service" >> $logfile
+
+(
+# NOTE You may wish to add the line `echo translate' if you want automatic
+# CR/LF translation when printing.
+# echo translate
+ echo "print -"
+ cat
+) | /usr/bin/smbclient "\\\\$server\\$service" $password -U $server -N >> $logfile
diff --git a/packaging/RedHat/smbusers b/packaging/RedHat/smbusers
new file mode 100644
index 00000000000..ae3389f53f8
--- /dev/null
+++ b/packaging/RedHat/smbusers
@@ -0,0 +1,3 @@
+# Unix_name = SMB_name1 SMB_name2 ...
+root = administrator admin
+nobody = guest pcguest smbguest
diff --git a/packaging/RedHat/winbind.init b/packaging/RedHat/winbind.init
new file mode 100644
index 00000000000..1635dca93be
--- /dev/null
+++ b/packaging/RedHat/winbind.init
@@ -0,0 +1,90 @@
+#!/bin/sh
+#
+# chkconfig: 345 91 45
+# description: Starts and stops the Samba winbind daemon to provide \
+# user and group information from a domain controller to linux.
+
+# Source function library.
+if [ -f /etc/init.d/functions ] ; then
+ . /etc/init.d/functions
+elif [ -f /etc/rc.d/init.d/functions ] ; then
+ . /etc/rc.d/init.d/functions
+else
+ exit 0
+fi
+
+# Source networking configuration.
+. /etc/sysconfig/network
+
+# Check that networking is up.
+[ ${NETWORKING} = "no" ] && exit 0
+
+CONFIG=/etc/samba/smb.conf
+
+# Check that smb.conf exists.
+[ -f $CONFIG ] || exit 0
+
+start() {
+ echo -n "Starting Winbind services: "
+ RETVAL=1
+ if [ "`egrep -i '(idmap.*uid|winbind.*uid)' $CONFIG | egrep -v [\#\;]`" ]; then
+ daemon winbindd
+ RETVAL=$?
+ fi
+ echo
+ [ $RETVAL -eq 0 ] && touch /var/lock/subsys/winbind || \
+ RETVAL=1
+ return $RETVAL
+}
+stop() {
+ echo -n "Shutting down Winbind services: "
+ RETVAL=1
+ if [ "`egrep -i '(idmap.*uid|winbind.*uid)' $CONFIG | egrep -v [\#\;]`" ]; then
+ killproc winbindd
+ RETVAL=$?
+ fi
+ echo
+ [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/winbind
+ return $RETVAL
+}
+restart() {
+ stop
+ start
+}
+reload() {
+ export TMPDIR="/var/tmp"
+ echo -n "Checking domain trusts: "
+ killproc winbindd -HUP
+ RETVAL=$?
+ echo
+ return $RETVAL
+}
+mdkstatus() {
+ status winbindd
+}
+
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ restart)
+ restart
+ ;;
+ reload)
+ reload
+ ;;
+ status)
+ mdkstatus
+ ;;
+ condrestart)
+ [ -f /var/lock/subsys/winbindd ] && restart || :
+ ;;
+ *)
+ echo "Usage: $0 {start|stop|restart|status|condrestart}"
+ exit 1
+esac
+
+exit $?
diff --git a/packaging/SGI/.cvsignore b/packaging/SGI/.cvsignore
new file mode 100644
index 00000000000..7b74def5aea
--- /dev/null
+++ b/packaging/SGI/.cvsignore
@@ -0,0 +1,8 @@
+bins
+catman
+html
+codepages
+swat
+Makefile
+samba.idb
+samba.spec
diff --git a/packaging/SGI/README b/packaging/SGI/README
new file mode 100644
index 00000000000..f13164af4a7
--- /dev/null
+++ b/packaging/SGI/README
@@ -0,0 +1,44 @@
+This directory contains sample files for using Samba on an IRIX
+system. These were taken from a system running IRIX 6.2. The
+client machines were running Win95 and connected via the Ethernet
+using TCP/IP and DNS. Consult the Samba documentation for tips
+on configuring Samba "properly"; this smb.conf file is very simple.
+Consult the Microsoft help/documentation to understand how to
+configure the networking support on the PC clients (Win95, WfW,
+etc.).
+
+This distribution is configured so that various Samba configuration,
+binary, and log files are placed in the /usr/samba file hierarchy.
+Man pages are placed in the /usr/share/catman/u_man hierarchy.
+
+The version number of the distribution is a 10 digit number that
+is created from the samba version number. Each section of the samba
+version number forms 2 digits of the version number (with leading
+zeros if necessary). The alpha versions add 00 and 2 digits for
+the alpha number. The first release adds 0100. Patch releases add
+2 digits for the patch level plus 1 and 00.
+
+samba version 1.9.18alpha9 would become 0109180009
+samba version 1.9.18 would become 0109180100
+samba version 1.9.18p9 would become 0109181000
+
+You can enable all printers on your system to be used by samba
+by running the script /usr/samba/mkprintcap.sh
+
+This distribution automatically configures samba to run as deamons
+by the script /etc/init.d/samba and the file /etc/config/samba
+(used by chkconfig). If you would prefer to have samba started by
+inetd you can run the script /usr/samba/inetd.sh.
+
+To create a Samba distribution you must have the Documenter's WorkBench
+package installed to format the manual pages. In addition you need
+to have the Software Packager software (inst_dev) installed to
+generate the inst images, and Perl to generate the spec and idb files.
+
+From /usr/samba/packaging/SGI directory run the mkrelease.sh script.
+There is one optional argument which is the major release number of the
+OS version (4, 5, or 6) you desire. If no number is specified it defaults
+to 6. This script uses Perl to generate the Makefile with the proper
+defines and the packaging files samba.spec and samba.idb. The binary
+package files will be placed in ./bins
+
diff --git a/packaging/SGI/findsmb b/packaging/SGI/findsmb
new file mode 100755
index 00000000000..336ff07c16f
--- /dev/null
+++ b/packaging/SGI/findsmb
@@ -0,0 +1,141 @@
+#!/bin/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 [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.
+#
+
+$SAMBABIN = "/usr/samba/bin";
+
+for ($i = 0; $i < 2; $i++) { # test for -d option and broadcast address
+ $_ = shift;
+ if (m/-d|-D/) {
+ $DEBUG = 1;
+ } else {
+ if ($_) {
+ $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>.*$//,<NMBLOOKUP>);
+
+# 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 -A $ip|") ||
+ die("Can't get nmb name list.\n");
+ @nmblookup = <NMBLOOKUP>;
+ close NMBLOOKUP;
+
+# get the first <00> name
+
+ @name = grep(/<00> - /,@nmblookup);
+ $_ = @name[0];
+ if ($_) { # we have a netbios name
+ if (/GROUP/) { # is it a group name
+ ($name, $aliases, $type, $length, @addresses) =
+ gethostbyaddr(pack('C4',split('\.',$ip)),2);
+ if (! $name) { # could not get name
+ $name = "unknown nis name";
+ }
+ } else {
+ /(.{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 = <SMB>;
+ close SMB;
+
+ if ($DEBUG) { # if -d flag print results of nmblookup and smbclient
+ print "===============================================================\n";
+ print @nmblookup;
+ print @smb;
+ }
+
+# look for the OS= string
+
+ @info = grep(/OS=/,@smb);
+ $_ = @info[0];
+ if ($_) { # we found response
+ s/.*Domain=|OS=|Server=|\n//g; # strip out descriptions to make line shorter
+
+ } else { # no OS= string in response (WIN95 client)
+
+# for WIN95 clients get workgroup name from nmblookup response
+ @name = grep(/<00> - <GROUP>/,@nmblookup);
+ $_ = @name[0];
+ if ($_) {
+ /(.{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/packaging/SGI/idb.pl b/packaging/SGI/idb.pl
new file mode 100755
index 00000000000..529695b14b2
--- /dev/null
+++ b/packaging/SGI/idb.pl
@@ -0,0 +1,378 @@
+#!/usr/bin/perl
+require "pwd.pl" || die "Required pwd.pl not found";
+
+# This perl script automatically generates the idb file
+
+$PKG = 'samba';
+$SRCDIR = '../..';
+$SRCPFX = '.';
+
+&initpwd;
+$curdir = $ENV{"PWD"};
+
+if ($PKG eq "samba_irix") {
+ open(BOOKS,"IDB.books") || die "Unable to open IDB.books file\n";
+ @books = sort idbsort <BOOKS>;
+ close BOOKS;
+}
+
+# We don't want the files listed in .cvsignore in the source tree
+open(IGNORES,"$SRCDIR/source/.cvsignore") || die "Unable to open .cvsignore file\n";
+while (<IGNORES>) {
+ chop;
+ next if /cvs\.log/;
+ $ignores{$_}++;
+}
+close IGNORES;
+
+# We don't want the files listed in .cvsignore in the source/include tree
+open(IGNORES,"$SRCDIR/source/include/.cvsignore") || die "Unable to open include/.cvsignore file\n";
+while (<IGNORES>) {
+ chop;
+ $ignores{$_}++;
+}
+close IGNORES;
+
+# get the names of all the binary files to be installed
+open(MAKEFILE,"$SRCDIR/source/Makefile") || die "Unable to open Makefile\n";
+while (not eof(MAKEFILE)) {
+ $_ = <MAKEFILE>;
+ chomp;
+ last if /^# object file lists/ ;
+ if (/^EXEEXT/) {
+ /^.*=(.*)/;
+ $EXEEXT = $1;
+ }
+ if (/^srcdir/) {
+ /^.*=(.*)/;
+ $srcdir = $1;
+ }
+ if (/^builddir/) {
+ /^.*=(.*)/;
+ $builddir = $1;
+ }
+ if (/^SBIN_PROGS/) { @sbinprogs = get_line($_); }
+ if (/^BIN_PROGS1/) { @binprogs1 = get_line($_); }
+ if (/^BIN_PROGS2/) { @binprogs2 = get_line($_); }
+ if (/^BIN_PROGS3/) { @binprogs3 = get_line($_); }
+ if (/^BIN_PROGS/) { @binprogs = get_line($_); }
+ if (/^SCRIPTS/) { @scripts = get_line($_); }
+ if (/^TORTURE_PROGS/) { @tortureprogs = get_line($_); }
+ if (/^SHLIBS/) { @shlibs = get_line($_); }
+}
+close MAKEFILE;
+
+# add my local files to the list of binaries to install
+@bins = sort byfilename (@sbinprogs,@binprogs,@binprogs1,@binprogs2,@binprogs3,@scripts,("sambalp","smbprint"));
+
+@nsswitch = sort byfilename (@shlibs);
+
+# install the swat files
+chdir "$SRCDIR/source";
+system("chmod +x ./script/installswat.sh");
+system("./script/installswat.sh ../packaging/SGI/swat ./ ../packaging/SGI/swat/using_samba");
+system("cp -f ../swat/README ../packaging/SGI/swat");
+chdir $curdir;
+
+# get a complete list of all files in the tree
+chdir "$SRCDIR/";
+&dodir('.');
+chdir $curdir;
+
+# the files installed in docs include all the original files in docs plus all
+# the "*.doc" files from the source tree
+@docs = sort bynextdir grep (!/CVS/ & (/^source\/.*\.doc$/ | /^docs\/\w/),@allfiles);
+@docs = grep(!/htmldocs/ & !/manpages/, @docs);
+@docs = grep(!/docbook/, @docs);
+
+@libfiles = sort byfilename (grep (/^source\/codepages\/\w/,@allfiles),("packaging/SGI/smb.conf","source/bin/libsmbclient.a","source/bin/libsmbclient.so"));
+
+@swatfiles = sort grep(/^packaging\/SGI\/swat/, @allfiles);
+@catman = sort grep(/^packaging\/SGI\/catman/ & !/\/$/, @allfiles);
+@catman = sort bydirnum @catman;
+
+# strip out all the generated directories and the "*.o" files from the source
+# release
+@allfiles = grep(!/^.*\.o$/ & !/^.*\.po$/ & !/^.*\.po32$/ & !/^.*\.so$/ & !/^source\/bin/ & !/^packaging\/SGI\/bins/ & !/^packaging\/SGI\/catman/ & !/^packaging\/SGI\/html/ & !/^packaging\/SGI\/codepages/ & !/^packaging\/SGI\/swat/, @allfiles);
+
+open(IDB,"> $curdir/$PKG.idb") || die "Unable to open $PKG.idb for output\n";
+
+print IDB "f 0644 root sys etc/config/samba $SRCPFX/packaging/SGI/samba.config $PKG.sw.base config(update)\n";
+print IDB "f 0644 root sys etc/config/winbind $SRCPFX/packaging/SGI/winbindd.config $PKG.sw.base config(update)\n";
+print IDB "f 0755 root sys etc/init.d/samba $SRCPFX/packaging/SGI/samba.rc $PKG.sw.base\n";
+print IDB "f 0755 root sys etc/init.d/winbind $SRCPFX/packaging/SGI/winbindd.rc $PKG.sw.base\n";
+print IDB "l 0000 root sys etc/rc0.d/K36winbind $SRCPFX/packaging/SGI $PKG.sw.base symval(../init.d/winbind)\n";
+print IDB "l 0000 root sys etc/rc0.d/K37samba $SRCPFX/packaging/SGI $PKG.sw.base symval(../init.d/samba)\n";
+print IDB "l 0000 root sys etc/rc2.d/S81samba $SRCPFX/packaging/SGI $PKG.sw.base symval(../init.d/samba)\n";
+print IDB "l 0000 root sys etc/rc2.d/S82winbind $SRCPFX/packaging/SGI $PKG.sw.base symval(../init.d/winbind)\n";
+
+if ($PKG eq "samba_irix") {
+ print IDB "d 0755 root sys usr/relnotes/samba_irix $SRCPFX/packaging/SGI $PKG.man.relnotes\n";
+ print IDB "f 0644 root sys usr/relnotes/samba_irix/TC relnotes/TC $PKG.man.relnotes\n";
+ print IDB "f 0644 root sys usr/relnotes/samba_irix/ch1.z relnotes/ch1.z $PKG.man.relnotes\n";
+ print IDB "f 0644 root sys usr/relnotes/samba_irix/ch2.z relnotes/ch2.z $PKG.man.relnotes\n";
+ print IDB "f 0644 root sys usr/relnotes/samba_irix/ch3.z relnotes/ch3.z $PKG.man.relnotes\n";
+}
+else {
+ @copyfile = grep (/^COPY/,@allfiles);
+ print IDB "d 0755 root sys usr/relnotes/samba $SRCPFX/packaging/SGI $PKG.man.relnotes\n";
+ print IDB "f 0644 root sys usr/relnotes/samba/@copyfile[0] $SRCPFX/@copyfile[0] $PKG.man.relnotes\n";
+ print IDB "f 0644 root sys usr/relnotes/samba/legal_notice.html $SRCPFX/packaging/SGI/legal_notice.html $PKG.man.relnotes\n";
+ print IDB "f 0644 root sys usr/relnotes/samba/samba-relnotes.html $SRCPFX/packaging/SGI/relnotes.html $PKG.man.relnotes\n";
+}
+
+print IDB "d 0755 root sys usr/samba $SRCPFX/packaging/SGI $PKG.sw.base\n";
+
+print IDB "d 0755 root sys usr/samba/bin $SRCPFX/packaging/SGI $PKG.sw.base\n";
+while(@bins) {
+ $nextfile = shift @bins;
+ ($filename = $nextfile) =~ s/^.*\///;;
+
+ if (index($nextfile,'$')) {
+ if ($filename eq "smbpasswd") {
+ print IDB "f 0755 root sys usr/samba/bin/$filename $SRCPFX/source/$nextfile $PKG.sw.base \n";
+ }
+ elsif ($filename eq "swat") {
+ print IDB "f 4755 root sys usr/samba/bin/$filename $SRCPFX/source/$nextfile $PKG.sw.base preop(\"chroot \$rbase /etc/init.d/samba stop\") exitop(\"chroot \$rbase /usr/samba/scripts/startswat.sh\") removeop(\"chroot \$rbase /sbin/cp /etc/inetd.conf /etc/inetd.conf.O ; chroot \$rbase /sbin/sed -e '/^swat/D' -e '/^#SWAT/D' /etc/inetd.conf.O >/etc/inetd.conf; /etc/killall -HUP inetd || true\")\n";
+ }
+ elsif ($filename eq "sambalp") {
+ print IDB "f 0755 root sys usr/samba/bin/$filename $SRCPFX/packaging/SGI/$filename $PKG.sw.base \n";
+ }
+ elsif ($filename eq "smbprint") {
+ print IDB "f 0755 root sys usr/samba/bin/$filename $SRCPFX/packaging/SGI/$filename $PKG.sw.base\n";
+ }
+ elsif ($filename eq "smbd") {
+ print IDB "f 0755 root sys usr/samba/bin/$filename $SRCPFX/source/$nextfile $PKG.sw.base \n";
+ if (-e "$SRCDIR/source/$nextfile.noquota") {
+ print IDB "f 0755 root sys usr/samba/bin/$filename.noquota $SRCPFX/source/$nextfile.noquota $PKG.sw.base \n";
+ }
+ if (-e "$SRCDIR/source/$nextfile.profile") {
+ print IDB "f 0755 root sys usr/samba/bin/$filename.profile $SRCPFX/source/$nextfile.profile $PKG.sw.base \n";
+ }
+ }
+ elsif ($filename eq "nmbd") {
+ print IDB "f 0755 root sys usr/samba/bin/$filename $SRCPFX/source/$nextfile $PKG.sw.base \n";
+ if (-e "$SRCDIR/source/$nextfile.profile") {
+ print IDB "f 0755 root sys usr/samba/bin/$filename.profile $SRCPFX/source/$nextfile.profile $PKG.sw.base \n";
+ }
+ }
+ else {
+ print IDB "f 0755 root sys usr/samba/bin/$filename $SRCPFX/source/$nextfile $PKG.sw.base \n";
+ }
+ }
+}
+
+print IDB "d 0755 root sys usr/samba/docs $SRCPFX/docs $PKG.man.doc\n";
+while (@docs) {
+ $nextfile = shift @docs;
+ ($junk,$file) = split(/\//,$nextfile,2);
+ if (grep(/\/$/,$nextfile)) {
+ $file =~ s/\/$//;
+ $nextfile =~ s/\/$//;
+ print IDB "d 0755 root sys usr/samba/docs/$file $SRCPFX/$nextfile $PKG.man.doc\n";
+ }
+ else {
+ print IDB "f 0644 root sys usr/samba/docs/$file $SRCPFX/$nextfile $PKG.man.doc\n";
+ }
+}
+
+print IDB "d 0755 root sys usr/samba/include $SRCPFX/packaging/SGI $PKG.sw.base\n";
+print IDB "f 0644 root sys usr/samba/include/libsmbclient.h $SRCPFX/source/include/libsmbclient.h $PKG.sw.base\n";
+
+print IDB "d 0755 root sys usr/samba/lib $SRCPFX/packaging/SGI $PKG.sw.base\n";
+while (@libfiles) {
+ $nextfile = shift @libfiles;
+ ($file = $nextfile) =~ s/.*\///;
+ if ($file eq "smb.conf") {
+ print IDB "f 0644 root sys usr/samba/lib/$file $SRCPFX/$nextfile $PKG.sw.base config(suggest)\n";
+ } else {
+ print IDB "f 0644 root sys usr/samba/lib/$file $SRCPFX/$nextfile $PKG.sw.base nostrip \n";
+ }
+}
+
+print IDB "d 0755 lp sys usr/samba/printer $SRCPFX/packaging/SGI $PKG.sw.base\n";
+print IDB "d 0755 lp sys usr/samba/printer/W32ALPHA $SRCPFX/packaging/SGI $PKG.sw.base\n";
+print IDB "d 0755 lp sys usr/samba/printer/W32MIPS $SRCPFX/packaging/SGI $PKG.sw.base\n";
+print IDB "d 0755 lp sys usr/samba/printer/W32PPC $SRCPFX/packaging/SGI $PKG.sw.base\n";
+print IDB "d 0755 lp sys usr/samba/printer/W32X86 $SRCPFX/packaging/SGI $PKG.sw.base\n";
+print IDB "d 0755 lp sys usr/samba/printer/WIN40 $SRCPFX/packaging/SGI $PKG.sw.base\n";
+
+print IDB "d 0644 root sys usr/samba/private $SRCPFX/packaging/SGI $PKG.sw.base\n";
+print IDB "f 0600 root sys usr/samba/private/smbpasswd $SRCPFX/packaging/SGI/smbpasswd $PKG.sw.base config(suggest)\n";
+
+print IDB "d 0755 root sys usr/samba/scripts $SRCPFX/packaging/SGI $PKG.src.samba\n";
+print IDB "f 0755 root sys usr/samba/scripts/inetd.sh $SRCPFX/packaging/SGI/inetd.sh $PKG.sw.base\n";
+print IDB "f 0755 root sys usr/samba/scripts/inst.msg $SRCPFX/packaging/SGI/inst.msg $PKG.sw.base exitop(\"chroot \$rbase /usr/samba/scripts/inst.msg\")\n";
+print IDB "f 0755 root sys usr/samba/scripts/mkprintcap.sh $SRCPFX/packaging/SGI/mkprintcap.sh $PKG.sw.base\n";
+print IDB "f 0755 root sys usr/samba/scripts/removeswat.sh $SRCPFX/packaging/SGI/removeswat.sh $PKG.sw.base\n";
+print IDB "f 0755 root sys usr/samba/scripts/startswat.sh $SRCPFX/packaging/SGI/startswat.sh $PKG.sw.base\n";
+
+print IDB "d 0755 root sys usr/samba/src $SRCPFX/packaging/SGI $PKG.src.samba\n";
+@sorted = sort(@allfiles);
+while (@sorted) {
+ $nextfile = shift @sorted;
+ ($file = $nextfile) =~ s/^.*\///;
+ next if grep(/packaging\/SGI/& (/Makefile/ | /samba\.spec/ | /samba\.idb/),$nextfile);
+ next if grep(/source/,$nextfile) && ($ignores{$file});
+ next if ($nextfile eq "CVS");
+ if (grep(/\/$/,$nextfile)) {
+ $nextfile =~ s/\/$//;
+ print IDB "d 0755 root sys usr/samba/src/$nextfile $SRCPFX/$nextfile $PKG.src.samba\n";
+ }
+ else {
+ if (grep((/\.sh$/ | /configure$/ | /configure\.developer/ | /config\.guess/ | /config\.sub/ | /\.pl$/ | /mkman$/ | /pcp\/Install/ | /pcp\/Remove/),$nextfile)) {
+ print IDB "f 0755 root sys usr/samba/src/$nextfile $SRCPFX/$nextfile $PKG.src.samba\n";
+ }
+ else {
+ print IDB "f 0644 root sys usr/samba/src/$nextfile $SRCPFX/$nextfile $PKG.src.samba\n";
+ }
+ }
+}
+
+print IDB "d 0755 root sys usr/samba/swat $SRCPFX/packaging/SGI/swat $PKG.sw.base\n";
+while (@swatfiles) {
+ $nextfile = shift @swatfiles;
+ ($file = $nextfile) =~ s/^packaging\/SGI\/swat\///;
+ next if !$file;
+ if (grep(/\/$/,$file)) {
+ $file =~ s/\/$//;
+ print IDB "d 0755 root sys usr/samba/swat/$file $SRCPFX/packaging/SGI/swat/$file $PKG.sw.base\n";
+ }
+ else {
+ print IDB "f 0444 root sys usr/samba/swat/$file $SRCPFX/packaging/SGI/swat/$file $PKG.sw.base\n";
+ }
+}
+
+print IDB "d 0755 root sys usr/samba/var $SRCPFX/packaging/SGI $PKG.sw.base\n";
+print IDB "d 0755 root sys usr/samba/var/locks $SRCPFX/packaging/SGI $PKG.sw.base\n";
+
+if ($PKG eq "samba_irix") {
+ while(@books) {
+ $nextfile = shift @books;
+ print IDB $nextfile;
+ }
+}
+
+print IDB "d 0755 root sys usr/share/catman/u_man $SRCPFX/packaging/SGI $PKG.man.manpages\n";
+$olddirnum = "0";
+while (@catman) {
+ $nextfile = shift @catman;
+ ($file = $nextfile) =~ s/^packaging\/SGI\/catman\///;
+ ($dirnum = $file) =~ s/^[\D]*//;
+ $dirnum =~ s/\.z//;
+ if ($dirnum ne $olddirnum) {
+ print IDB "d 0755 root sys usr/share/catman/u_man/cat$dirnum $SRCPFX/packaging/SGI $PKG.man.manpages\n";
+ $olddirnum = $dirnum;
+ }
+ print IDB "f 0664 root sys usr/share/catman/u_man/cat$dirnum/$file $SRCPFX/$nextfile $PKG.man.manpages\n";
+}
+
+if (@nsswitch) {
+ print IDB "d 0755 root sys var/ns/lib $SRCPFX/packaging/SGI $PKG.sw.base\n";
+ while(@nsswitch) {
+ $nextfile = shift @nsswitch;
+ next if $nextfile eq 'libsmbclient';
+ ($filename = $nextfile) =~ s/^.*\///;
+ $filename =~ s/libnss/libns/;
+ print IDB "f 0644 root sys var/ns/lib/$filename $SRCPFX/source/$nextfile $PKG.sw.base \n";
+ }
+}
+
+print IDB "d 01777 lp sys var/spool/samba $SRCPFX/packaging/SGI $PKG.sw.base\n";
+
+close IDB;
+print "\n\n$PKG.idb file has been created\n";
+
+sub dodir {
+ local($dir, $nlink) = @_;
+ local($dev,$ino,$mode,$subcount);
+
+ ($dev,$ino,$mode,$nlink) = stat('.') unless $nlink;
+
+ opendir(DIR,'.') || die "Can't open current directory";
+ local(@filenames) = sort readdir(DIR);
+ closedir(DIR);
+
+ if ($nlink ==2) { # This dir has no subdirectories.
+ for (@filenames) {
+ next if $_ eq '.';
+ next if $_ eq '..';
+ $this = substr($dir,2)."/$_";
+ push(@allfiles,$this);
+ }
+ }
+ else {
+ $subcount = $nlink -2;
+ for (@filenames) {
+ next if $_ eq '.';
+ next if $_ eq '..';
+ next if $_ eq 'CVS';
+ ($dev,$ino,$mode,$nlink) = lstat($_);
+ $name = "$dir/$_";
+ $this = substr($name,2);
+ $this .= '/' if -d;
+ push(@allfiles,$this);
+ next if $subcount == 0; # seen all the subdirs?
+
+ next unless -d _;
+
+ chdir $_ || die "Can't cd to $name";
+ &dodir($name,$nlink);
+ chdir '..';
+ --$subcount;
+ }
+ }
+}
+
+sub bynextdir {
+ ($f0,$f1) = split(/\//,$a,2);
+ ($f0,$f2) = split(/\//,$b,2);
+ $f1 cmp $f2;
+}
+
+sub byfilename {
+ ($f0,$f1) = split(/.*\//,$a,2);
+ if ($f1 eq "") { $f1 = $f0 };
+ ($f0,$f2) = split(/.*\//,$b,2);
+ if ($f2 eq "") { $f2 = $f0 };
+ $f1 cmp $f2;
+}
+
+sub bydirnum {
+ ($f1 = $a) =~ s/^.*\///;
+ ($f2 = $b) =~ s/^.*\///;
+ ($dir1 = $a) =~ s/^[\D]*//;
+ ($dir2 = $b) =~ s/^[\D]*//;
+ if (!($dir1 <=> $dir2)) {
+ $f1 cmp $f2;
+ }
+ else {
+ $dir1 <=> $dir2;
+ }
+}
+
+sub idbsort {
+ ($f0,$f1,$f2,$f3) = split(/ /,$a,4);
+ ($f0,$f1,$f2,$f4) = split(/ /,$b,4);
+ $f3 cmp $f4;
+}
+
+sub get_line {
+ local ($line) = @_;
+
+ $line =~ s/^.*=//;
+ while (($cont = index($line,"\\")) > 0) {
+ $_ = <MAKEFILE>;
+ chomp;
+ s/^\s*/ /;
+ substr($line,$cont,1) = $_;
+ }
+ $line =~ s/\$\(EXEEXT\)/$EXEEXT/g;
+ $line =~ s/\$\(srcdir\)//g;
+ $line =~ s/\$\(builddir\)//g;
+ $line =~ s/\$\(\S*\)\s*//g;
+ $line =~ s/\s\s*/ /g;
+ @line = split(' ',$line);
+ return @line;
+}
+
diff --git a/packaging/SGI/inetd.sh b/packaging/SGI/inetd.sh
new file mode 100755
index 00000000000..1d403978aec
--- /dev/null
+++ b/packaging/SGI/inetd.sh
@@ -0,0 +1,37 @@
+#! /bin/sh
+#
+# kill any running samba processes
+#
+/etc/killall smbd nmbd
+chkconfig samba off
+
+#
+# add SAMBA deamons to inetd.conf
+#
+cp /etc/inetd.conf /etc/inetd.conf.O
+
+if [ $? -ne 0 ]; then exit 1; fi
+if [ ! -r /etc/inetd.conf.O -o ! -w /etc/inetd.conf ]; then exit 1; fi
+
+sed -e "/^netbios/D" -e "/^#SAMBA/D" /etc/inetd.conf.O > /etc/inetd.conf
+echo '#SAMBA services' >> /etc/inetd.conf
+echo netbios-ssn stream tcp nowait root /usr/samba/bin/smbd smbd >> /etc/inetd.conf
+echo netbios-ns dgram udp wait root /usr/samba/bin/nmbd nmbd -S >> /etc/inetd.conf
+
+#
+# add SAMBA service ports to /etc/services
+#
+cp /etc/services /etc/services.O
+
+if [ $? -ne 0 ]; then exit 1; fi
+if [ ! -r /etc/services.O -o ! -w /etc/services ]; then exit 1; fi
+
+sed -e "/^netbios/D" -e "/^#SAMBA/D" /etc/services.O > /etc/services
+echo '#SAMBA services' >> /etc/services
+echo 'netbios-ns 137/udp # SAMBA' >> /etc/services
+echo 'netbios-ssn 139/tcp # SAMBA' >> /etc/services
+
+#
+# restart inetd to start SAMBA
+#
+/etc/killall -HUP inetd
diff --git a/packaging/SGI/inst.msg b/packaging/SGI/inst.msg
new file mode 100755
index 00000000000..4d8bab389cb
--- /dev/null
+++ b/packaging/SGI/inst.msg
@@ -0,0 +1,31 @@
+#! /bin/sh
+
+echo
+echo
+echo Samba has been installed on your system.
+echo
+echo Your /etc/services and /etc/inetd.conf files have
+echo been modified to automatically start the
+echo Samba Web Administration Tool \(SWAT\) when you
+echo connect with a web browser to
+echo
+echo http://`hostname`:901
+echo
+echo The original versions of /etc/services and
+echo /etc/inetd.conf were saved with a .O extension.
+echo
+echo If you do not wish SWAT to be enabled you may
+echo run the script /usr/samba/scripts/removeswat.sh
+echo which will remove the entries from /etc/services
+echo and /etc/inetd.conf
+echo
+echo Please review your configuration settings by
+echo connecting to SWAT or editing the file
+echo /usr/samba/lib/smb.conf and then starting
+echo the smbd and nmbd daemons to complete the
+echo installation. You may start the daemons from
+echo the SWAT "Status" page or by executing the
+echo following command as root.
+echo
+echo /etc/init.d/samba start
+echo
diff --git a/packaging/SGI/legal_notice.html b/packaging/SGI/legal_notice.html
new file mode 100644
index 00000000000..fdb76456289
--- /dev/null
+++ b/packaging/SGI/legal_notice.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<HTML VERSION="2.0">
+<HEAD>
+<TITLE>Silicon Graphics Freeware Legal Notice</TITLE>
+</HEAD>
+
+<BODY>
+<H1><A NAME="LEGAL">Silicon Graphics Freeware Legal Notice</A></H1>
+<HR>
+Copyright 1995, Silicon Graphics, Inc. -- ALL RIGHTS RESERVED
+<P>
+You may copy, modify, use and distribute this software, (i)
+provided that you include the entirety of this reservation of
+rights notice in all such copies, and (ii) you comply with any
+additional or different obligations and/or use restrictions
+specified by any third party owner or supplier of the software
+in other notices that may be included with the software.
+
+<P>
+SGI DISCLAIMS ALL WARRANTIES WITH RESPECT TO THIS SOFTWARE,
+EXPRESS, IMPLIED, OR OTHERWISE, INCLUDING WITHOUT LIMITATION,
+ALL WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE OR NONINFRINGEMENT. SGI SHALL NOT BE LIABLE FOR ANY
+SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING,
+WITHOUT LIMITATION, LOST REVENUES, LOST PROFITS, OR LOSS OF
+PROSPECTIVE ECONOMIC ADVANTAGE, RESULTING FROM THE USE OR MISUSE
+OF THIS SOFTWARE.
+
+<P>
+U.S. GOVERNMENT RESTRICTED RIGHTS LEGEND:
+
+<P>
+
+Use, duplication or disclosure by the Government is subject to
+restrictions as set forth in FAR 52.227.19(c)(2) or subparagraph
+(c)(1)(ii) of the Rights in Technical Data and Computer Software
+clause at DFARS 252.227-7013 and/or in similar or successor
+clauses in the FAR, or the DOD or NASA FAR Supplement.
+Unpublished - rights reserved under the Copyright Laws of United
+States. Contractor/manufacturer is Silicon Graphics, Inc., 2011
+N. Shoreline Blvd. Mountain View, CA 94039-7311.
+
+<H3><A NAME="SUPPORT">Product Support</A></H3>
+
+<P>
+Freeware products are not supported by Silicon Graphics or any
+of its support providers. The software contained in this package
+is made available through the generous efforts of their authors.
+Although they are interested in your feedback, they are under no
+obligation to address bugs, enhancements, or answer questions.
+
+</BODY>
+</HTML>
diff --git a/packaging/SGI/mkman b/packaging/SGI/mkman
new file mode 100755
index 00000000000..a39ed9fdd0c
--- /dev/null
+++ b/packaging/SGI/mkman
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+if [ ! -d catman ]; then
+ mkdir catman
+fi
+
+
+FILES="*.?"
+
+cd ../../docs/manpages
+for FILE in $FILES ; do
+ if [ "$FILE" = "smbmnt.8" ]; then continue; fi;
+ if [ "$FILE" = "smbmount.8" -o "$FILE" = "smbumount.8" ]; then continue; fi;
+ if [ "$FILE" = "smbrun.1" ]; then continue; fi;
+ neqn $FILE | tbl | nroff -man > ../../packaging/SGI/catman/`basename $FILE`
+ pack -f ../../packaging/SGI/catman/`basename $FILE`
+done
+cd ../../packaging/SGI
diff --git a/packaging/SGI/mkprintcap.sh b/packaging/SGI/mkprintcap.sh
new file mode 100755
index 00000000000..f610e757f06
--- /dev/null
+++ b/packaging/SGI/mkprintcap.sh
@@ -0,0 +1,15 @@
+#! /bin/sh
+#
+# create printcap file
+#
+if [ -r /usr/samba/printcap ]
+then
+ cp /usr/samba/printcap /usr/samba/printcap.O
+fi
+
+echo "#" > /usr/samba/printcap
+echo "# Samba printcap file" >> /usr/samba/printcap
+echo "# Alias names are separated by |, any name with spaces is taken as a comment" >> /usr/samba/printcap
+echo "#" >> /usr/samba/printcap
+lpstat -a | sed -e "s/ .*//" >> /usr/samba/printcap
+
diff --git a/packaging/SGI/mkrelease.sh b/packaging/SGI/mkrelease.sh
new file mode 100755
index 00000000000..89854300aaf
--- /dev/null
+++ b/packaging/SGI/mkrelease.sh
@@ -0,0 +1,126 @@
+#!/bin/sh
+
+# This file goes through all the necessary steps to build a release package.
+# syntax:
+# mkrelease.sh [clean]
+#
+# You can specify clean to do a make clean before building. Make clean
+# will also run configure and generate the required Makefile.
+#
+# This will build an smbd.noquota, smbd.profile, nmbd.profile and the
+# entire package with quota support and acl support.
+
+doclean=""
+SGI_ABI=-n32
+ISA=-mips3
+CC=cc
+
+if [ ! -f ../../source/Makefile ]; then
+ doclean="clean"
+fi
+
+if [ "$1" = "clean" ] || [ "$1" = "cleanonly" ]; then
+ doclean=$1
+ shift
+fi
+
+export SGI_ABI ISA CC
+
+if [ "$doclean" = "clean" ] || [ "$doclean" = "cleanonly" ]; then
+ cd ../../source
+ if [ -f Makefile ]; then
+ make distclean
+ fi
+ rm -rf bin/*.profile bin/*.noquota
+ cd ../packaging/SGI
+ rm -rf bins catman html codepages swat samba.idb samba.spec
+ if [ "$doclean" = "cleanonly" ]; then exit 0 ; fi
+fi
+
+# create the catman versions of the manual pages
+#
+if [ "$doclean" = "clean" ]; then
+ echo Making manual pages
+ ./mkman
+ errstat=$?
+ if [ $errstat -ne 0 ]; then
+ echo "Error $errstat making manual pages\n";
+ exit $errstat;
+ fi
+fi
+
+cd ../../source
+if [ "$doclean" = "clean" ]; then
+ echo Create SGI specific Makefile
+ ./configure --prefix=/usr/samba --sbindir=/usr/samba/bin --mandir=/usr/share/catman --with-acl-support --with-quotas --with-smbwrapper
+ errstat=$?
+ if [ $errstat -ne 0 ]; then
+ echo "Error $errstat creating Makefile\n";
+ exit $errstat;
+ fi
+fi
+
+
+# build the sources
+#
+echo Making binaries
+
+echo "===================== Making Profile versions ======================="
+make clean
+make headers
+make -P "CFLAGS=-O -g3 -woff 1188 -D WITH_PROFILE" bin/smbd bin/nmbd
+errstat=$?
+if [ $errstat -ne 0 ]; then
+ echo "Error $errstat building profile sources\n";
+ exit $errstat;
+fi
+mv bin/smbd bin/smbd.profile
+mv bin/nmbd bin/nmbd.profile
+
+echo "===================== Making No Quota versions ======================="
+make clean
+make headers
+make -P "CFLAGS=-O -g3 -woff 1188 -D QUOTAOBJS=smbd/noquotas.o" bin/smbd
+errstat=$?
+if [ $errstat -ne 0 ]; then
+ echo "Error $errstat building noquota sources\n";
+ exit $errstat;
+fi
+mv bin/smbd bin/smbd.noquota
+
+echo "===================== Making Regular versions ======================="
+make -P "CFLAGS=-O -g3 -woff 1188" all libsmbclient
+errstat=$?
+if [ $errstat -ne 0 ]; then
+ echo "Error $errstat building sources\n";
+ exit $errstat;
+fi
+
+cd ../packaging/SGI
+
+# generate the packages
+#
+echo Generating Inst Packages
+./spec.pl # create the samba.spec file
+errstat=$?
+if [ $errstat -ne 0 ]; then
+ echo "Error $errstat creating samba.spec\n";
+ exit $errstat;
+fi
+
+./idb.pl # create the samba.idb file
+errstat=$?
+if [ $errstat -ne 0 ]; then
+ echo "Error $errstat creating samba.idb\n";
+ exit $errstat;
+fi
+sort +4 samba.idb > xxx
+mv xxx samba.idb
+
+if [ ! -d bins ]; then
+ mkdir bins
+fi
+
+# do the packaging
+/usr/sbin/gendist -rbase / -sbase ../.. -idb samba.idb -spec samba.spec -dist ./bins -all
+
diff --git a/packaging/SGI/printcap b/packaging/SGI/printcap
new file mode 100644
index 00000000000..b67b9cb167c
--- /dev/null
+++ b/packaging/SGI/printcap
@@ -0,0 +1,5 @@
+#
+# Sample printcap file
+# Alias names are separated by |, any name with spaces is taken as a comment
+#
+lp4js|lp12|LaserJet on the third floor by the coffee machine
diff --git a/packaging/SGI/relnotes.html b/packaging/SGI/relnotes.html
new file mode 100644
index 00000000000..afcf5796776
--- /dev/null
+++ b/packaging/SGI/relnotes.html
@@ -0,0 +1,233 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<HTML>
+<HEAD>
+ <TITLE>Samba Release Notes</TITLE>
+</HEAD>
+<BODY>
+
+<H1>Samba Release Notes</H1>
+
+<P>
+<HR></P>
+
+<H2>Table of Contents</H2>
+
+<MENU>
+<LI><B><A HREF="#WHATIS">What is Samba?</A></B> </LI>
+
+<LI><B><A HREF="#Support">Support Policy</A> </B></LI>
+
+<LI><B><A HREF="#Installation">Installation Information</A> </B></LI>
+
+<LI><B><A HREF="legal_notice.html">Silicon
+Graphics Legal Notice</A> </B></LI>
+
+<LI><B><A HREF="#AUTHORNOTES">Author's Notice(s)</A> </B></LI>
+
+<LI><B><A HREF="#Documentation">Documentation Information</A> </B></LI>
+</MENU>
+
+<P>
+<HR></P>
+
+<H2><A NAME="WHATIS"></A>What is Samba?</H2>
+
+<P>Samba is an SMB client and server for Unix. It makes it possible for
+client machines running Windows 95 and Windows for Workgroups to access
+files and/or print services on a Unix system. Samba includes an SMB server
+to provide LanManager-style file and print services to PCs, a Netbios (RFC10001/1002)
+name server, and an FTP-like client application for accessing PC resources
+from Unix. </P>
+
+<P>To make Samba work you'll need to configure your server host to run
+<B>smbd</B> and <B>nmbd</B> whenever you connect to a certain Internet
+port from the client machine. <B>Smbd</B> and <B>nmbd</B> can be started
+either as daemons or from inetd.</P>
+
+<P>By default <B>smbd</B> and <B>nmbd</B> are started as daemons by the
+file <I>/etc/init.d/samba</I> in conjunction with the chkconfig variable
+samba being set to on. If you set chkconfig samba off then the deamons
+will not be automatically started on reboot. In this case you must type
+the following at a shell prompt to start samba after a reboot: </P>
+
+<PRE><B> chkconfig samba on
+ /etc/init.d/samba start</B>
+</PRE>
+
+<P>If you make changes to your configuration files, <B>smbd</B> and <B>nmbd</B>
+may be restarted by typing the following at a shell prompt: </P>
+
+<PRE><B> /etc/init.d/samba start</B>
+</PRE>
+
+<P><B>smbd</B> and <B>nmbd</B> may be killed by typing the following at
+a shell prompt: </P>
+
+<PRE><B> /etc/init.d/samba stop</B>
+</PRE>
+
+<P>To have <B>smbd</B> and <B>nmbd</B> started by inetd you can execute
+the shell script <I>/usr/samba/inetd.sh</I> to automatically configure
+the various files and start the processes. This shell script first kills
+any running <B>smbd</B> and <B>nmbd</B> processes. It then removes any
+existing entries for &quot;netbios*&quot; from <I>/etc/inetd.conf</I> and
+adds the following lines </P>
+
+<PRE><B> netbios-ssn stream tcp nowait root /usr/samba/bin/smbd smbd
+ netbios-ns dgram udp wait root /usr/samba/bin/nmbd nmbd -S</B>
+</PRE>
+
+<P>It then removes any existing entries for &quot;netbios*&quot; from <I>/etc/services</I>
+and adds the following lines </P>
+
+<PRE><B> netbios-ns 137/udp # SAMBA
+ netbios-ssn 139/tcp # SAMBA</B>
+</PRE>
+
+<P><I>Inetd</I> is then restarted by executing:</P>
+
+<PRE><B> /etc/killall -HUP inetd</B>
+</PRE>
+
+<P>If you make changes to your configuration files, <B>smbd</B> and <B>nmbd</B>
+may be restarted by typing the following at a shell prompt: </P>
+
+<PRE><B> /etc/killall smbd nmbd
+ /etc/killall -HUP inetd</B>
+</PRE>
+
+<H3><A NAME="AUTHORNOTES"></A>Author's Notice(s):</H3>
+
+<P>The author of this product is: Andrew Tridgell </P>
+
+<P>Samba is distributed freely under the <A HREF="COPYING">GNU
+public license</A>. </P>
+
+<H3><A NAME="Support"></A>Support:</H3>
+
+<P>The software in this package is considered unsupported by Silicon Graphics.
+Neither the authors or Silicon Graphics are compelled to help resolve problems
+you may encounter in the installation, setup, or execution of this software.
+To be more to the point, if you call us with an issue regarding products
+in the Freeware package, we'll have to gracefully terminate the call. The
+<A HREF="http://samba.org/pub/samba/">
+Samba Web Page</A> has a listing of companies and individuals that offer
+commercial support for a fee.
+</P>
+
+<H2><A NAME="Installation"></A>Installation Information</H2>
+
+<P>Samba includes these subsystems: </P>
+
+<TABLE>
+<TR>
+<TD ALIGN=LEFT><B>samba.sw.base</B> (<I>default</I>)</TD>
+
+<TD>Execution environment for Samba.</TD>
+</TR>
+
+<TR>
+<TD ALIGN=left><B>samba.man.manpages</B>(<I>default</I>)</TD>
+
+<TD>Samba's online manual pages (preformatted).</TD>
+</TR>
+
+<TR>
+<TD ALIGN=LEFT VALIGN=TOP><B>samba.man.doc</B> (<I>default</I>)</TD>
+
+<TD>Samba documentation: hints on installation and configuration, an FAQ
+(Frequently Asked Questions), help in diagnosing problems, etc..</TD>
+</TR>
+
+<TR>
+<TD ALIGN=left><B>samba.man.relnotes</B> (<I>default</I>) </TD>
+
+<TD>Samba online release notes.</TD>
+</TR>
+
+<TR>
+<TD ALIGN=LEFT VALIGN=TOP><B>samba.src.samba</B> </TD>
+
+<TD>The Samba software distribution from which this product was
+built (including the packaging/SGI directory which will allow this distribution
+to be rebuilt).</TD>
+</TR>
+</TABLE>
+
+<H3>Installation Method</H3>
+
+<P>All of the subsystems for Samba can be installed using IRIX. You do
+not need to use the miniroot. Refer to the <I>Software Installation Administrator's
+Guide</I> for complete installation instructions. </P>
+
+<H3>Prerequisites</H3>
+
+<P>Your workstation must be running IRIX 5.3 or later in order to use this
+product. </P>
+
+<H3>Configuration Files</H3>
+
+<P>Because configuration files often contain modifications, inst treats
+them specially during the installation process. If they have not been modified,
+inst removes the old file and installs the new version during software
+updates. For configuration files that have been modified, the new version
+is installed and the old version is renamed by adding the suffix .O (for
+older) to the name. The no-suffix version contains changes that are required
+for compatibility with the rest of the newly installed software, that increase
+functionality, or that fix bugs. You should use diff(1) or gdiff(1) to
+compare the two versions of the files and transfer information that you
+recognize as machine or site-specific from the .O version to the no-suffix
+version. </P>
+
+<DL>
+<DT><B>/usr/samba/lib/smb.conf</B> </DT>
+
+<DD>Configuration definitions for the <B>smbd</B> program; the SMB server
+process. The default configuration sets up password-based access to home
+directories on a machine as well as open access to to all printers and
+/tmp. The workgroup is set by default to &quot;workgroup&quot;. It is highly
+recommended that administrators review the content of this file when installing
+Samba for the first time.</DD>
+
+<DT><B>/usr/samba/printcap</B> </DT>
+
+<DD>A file that specifies the available printers on a system. It is included
+as an example; administrators may want to replace it or override the reference
+to it in the <B>smb.conf</B> file. The script <B>/usr/samba/mkprintcap.sh</B>
+was used by inst to create a printcap file that contains all printers on
+your system. You may wish to remove some printers or add a comment to each
+printer name to describe its location.</DD>
+</DL>
+
+<H2><A NAME="Documentation"></A>Documentation Information</H2>
+
+<P>Preformatted manual pages are installed by default as are the contents
+of the <B>docs</B> directory from the Samba distribution; consult <I>samba</I>(7)
+for an introduction. </P>
+
+<P>There is a mailing list for discussion of Samba. To subscribe send mail
+to <A HREF="mailto:listproc@samba.org">listproc@samba.org</A>
+with a body of &quot;subscribe samba Your Name&quot; </P>
+
+<P>To send mail to everyone on the list mail to <A HREF="mailto:samba@samba.org">samba@samba.org</A>.
+</P>
+
+<P>There is also an announcement mailing list where new versions are announced.
+To subscribe send mail to <A HREF="mailto:listproc@samba.org">listproc@samba.org</A>
+with a body of &quot;subscribe samba-announce Your Name&quot;. All announcements
+also go to the samba list. </P>
+
+<P>You might also like to look at the Usenet news group <A HREF="news:comp.protocols.smb">comp.protocols.smb</A>
+as it often contains lots of useful info and is frequented by lots of Samba
+users. The newsgroup was initially setup by people on the Samba mailing
+list. It is not, however, exclusive to Samba, it is a forum for discussing
+the SMB protocol (which Samba implements). </P>
+
+<P>A Samba WWW site has been setup with lots of useful info. Connect to:
+<A HREF="http://samba.org/pub/samba/">http://samba.org/pub/samba/</A>.
+It is maintained by Paul Blackman (thanks Paul!). You can contact him at
+<A HREF="mailto:ictinus@lake.canberra.edu.au">ictinus@lake.canberra.edu.au</A>.
+</P>
+
+</BODY>
+</HTML>
diff --git a/packaging/SGI/removeswat.sh b/packaging/SGI/removeswat.sh
new file mode 100755
index 00000000000..7a4745345be
--- /dev/null
+++ b/packaging/SGI/removeswat.sh
@@ -0,0 +1,25 @@
+#! /bin/sh
+#
+# remove SWAT deamon from inetd.conf
+#
+cp /etc/inetd.conf /etc/inetd.conf.O
+
+if [ $? -ne 0 ]; then exit 1; fi
+if [ ! -r /etc/inetd.conf.O -o ! -w /etc/inetd.conf ]; then exit 1; fi
+
+sed -e "/^swat/D" -e "/^#SWAT/D" /etc/inetd.conf.O > /etc/inetd.conf
+
+#
+# remove SWAT service port from /etc/services
+#
+cp /etc/services /etc/services.O
+
+if [ $? -ne 0 ]; then exit 1; fi
+if [ ! -r /etc/services.O -o ! -w /etc/services ]; then exit 1; fi
+
+sed -e "/^swat/D" -e "/^#SWAT/D" /etc/services.O > /etc/services
+
+#
+# restart inetd to reread config files
+#
+/etc/killall -HUP inetd
diff --git a/packaging/SGI/samba.config b/packaging/SGI/samba.config
new file mode 100644
index 00000000000..b3d86404ab5
--- /dev/null
+++ b/packaging/SGI/samba.config
@@ -0,0 +1 @@
+on
diff --git a/packaging/SGI/samba.rc b/packaging/SGI/samba.rc
new file mode 100644
index 00000000000..bc0f90ee77f
--- /dev/null
+++ b/packaging/SGI/samba.rc
@@ -0,0 +1,43 @@
+#! /bin/sh
+
+#
+# Samba server control
+#
+
+IS_ON=/etc/chkconfig
+KILLALL=/sbin/killall
+
+SAMBAD=/usr/samba/bin/smbd
+#SAMBA_OPTS=-d2
+NMBD=/usr/samba/bin/nmbd
+#NMBD_OPTS=-d1
+
+if test ! -x $IS_ON ; then
+ IS_ON=true
+fi
+
+if $IS_ON verbose ; then
+ ECHO=echo
+else # For a quiet startup and shutdown
+ ECHO=:
+fi
+
+case $1 in
+'start')
+ if $IS_ON samba && test -x $SAMBAD; then
+ $KILLALL -15 smbd nmbd
+ $ECHO "Samba:\c"
+ $SAMBAD $SAMBA_OPTS -D; $ECHO " smbd\c"
+ $NMBD $NMBD_OPTS -D; $ECHO " nmbd\c"
+ $ECHO "."
+ fi
+ ;;
+'stop')
+ $ECHO "Stopping Samba Servers."
+ $KILLALL -15 smbd nmbd
+ exit 0
+ ;;
+*)
+ echo "usage: /etc/init.d/samba {start|stop}"
+ ;;
+esac
diff --git a/packaging/SGI/sambalp b/packaging/SGI/sambalp
new file mode 100644
index 00000000000..61e62215c91
--- /dev/null
+++ b/packaging/SGI/sambalp
@@ -0,0 +1,157 @@
+#!/bin/perl
+#
+# Hacked by Alan Stebbens <aks@sgi.com> to setuid to the username if
+# valid on this system. Written as a secure Perl script. To enable,
+#
+# chown root /usr/samba/bin/sambalp
+# chmod u+s,+x /usr/samba/bin/sambalp
+#
+# If setuidshells is not enabled on your system, you must also do this:
+#
+# systune -i
+# nosuidshells = 0
+# y
+# quit
+#
+# reboot
+#
+# This script will still work as a normal user; it will not try
+# to setuid in this case.
+#
+# If the "$PSFIX" variable is set below...
+#
+# Workaround Win95 printer driver/Impressario bug by removing
+# the PS check for available virtual memory. Note that this
+# bug appears to be in all Win95 print drivers that generate
+# PostScript; but is for certain there with a QMS-PS 810 (the
+# printer type I configure on the Win95-side for printing with
+# Samba).
+#
+# the perl script fixes 3 different bugs.
+# 1. remove the JCL statements added by some HP printer drivers to the
+# beginning of the postscript output.
+# 2. Fix a bug in output from word files with long filenames. A non-printing
+# character added to the end of the title comment by word is
+# removed.
+# 3. The VM fix described above.
+#
+#
+# Modified for Perl4 compatibility.
+#
+
+$PROG = "sambalp";
+
+$PSFIX = 1; # set to 0 if you don't want to run
+ # the "psfix" portion
+
+# Untaint the PATH variable
+@PATH = split(' ',<<EOF);
+ /usr/sbin /usr/bsd /sbin /usr/bin /bin /usr/lib /usr/local/bin
+EOF
+$ENV{'PATH'} = join(':',@PATH);
+
+if ($#ARGV < 3) {
+ print STDERR "usage: $PROG printer file user system\n";
+ exit;
+}
+
+$printer = $ARGV[0];
+$file = $ARGV[1];
+$user = $ARGV[2];
+$system = $ARGV[3];
+
+open(LPSTAT,"/usr/bin/lpstat -t|") || die("Can't get printer list.\n");
+@printers = ();
+while (<LPSTAT>) {
+ next unless /^printer (\w+)/;
+ push(@printers,$1);
+}
+close LPSTAT;
+# Create a hash list
+@printers{@printers} = @printers;
+
+# Untaint the printer name
+if (defined($prtname = $printers{$printer})) {
+ $printer = $prtname;
+} else {
+ die("Unknown printer: \"$printer\"\n");
+}
+
+if ($> == 0) { # are we root?
+ # yes -- then perform a taint checks and possibly do a suid check
+
+ # Untaint the file and system names (pretend to filter them)
+ $file = $file =~ /^(.*)/ ? $1 : die("Bad file: $file\n");
+ $system = $system =~ /^(.*)/ ? $1 : die("Bad system: $system\n");
+
+ # Get the valid users
+ setpwent;
+ %users = ();
+ while (@pwe = getpwent()) {
+ $uids{$pwe[0]} = $pwe[2];
+ $users{$pwe[2]} = $pwe[0];
+ }
+ endpwent();
+
+ # Check out the user -- if the user is a real user on this system,
+ # then become that user so that the printer header page looks right
+ # otherwise, remain as the default user (probably "nobody").
+
+ if (defined($uid = $uids{$user})) {
+
+ # before we change UID, we must ensure that the file is still
+ # readable after the UID change.
+ chown($uid, 9, $file); # make the file owned by the user
+
+ # Now, go ahead and become the user
+ $name = $users{$uid};
+ $> = $uid; # become the user
+ $< = $uid;
+ } else { # do untaint filtering
+ $name = $user =~ /^(\w+)/ ? $1 : die("Bad user: $user\n");
+ }
+} else { # otherwise, just be me
+ $name = $user; # whomever that is
+}
+
+$lpcommand = "/usr/bin/lp -c -d$printer -t'$name on $system'";
+
+# This code is from the original "psfix" but it has been completely
+# rewritten for speed.
+
+if ($PSFIX) { # are we running a "psfix"?
+ open(FILE, $file) || die("Can't read $file: $!\n");
+ open(LP, "|$lpcommand -") || die("Can't open pipe to \"lp\": $!\n");
+ select(LP);
+ while (<FILE>) { #
+ $_ =~ s/^\004//; # strip any ctrl-d's
+ if (/^\e%/) { # get rid of any non-postscript commands
+ while (<FILE>) { # remove text until next %!PS
+ s/^\001M//; # lenmark driver prefixes Ctrl-A M to %!PS
+ last if /^%!PS/;
+ }
+ last if eof(FILE);
+ } elsif (/^%%Title:/) { # fix bug in long titles from MS Word
+ s/.\r$/\r/; # remove trailing character on the title
+ } elsif (/^\/VM\?/) { # remove VM test
+ print "/VM? { pop } bind def\r\n";
+ while (<FILE>) { last if /def\r/; }
+ next; # don't print
+ }
+ print;
+ }
+ close FILE;
+ close LP;
+} else { # we're not running psfix?
+ system("$lpcommand $file");
+}
+
+if ($file =~ m(^/)) {
+ # $file is a fully specified path
+ # Remove the file only if it lives in a directory ending in /tmp.
+ unlink($file) if ($file =~ m(/tmp/[^/]+$));
+} else {
+ # $file is NOT a fully specified path
+ # Remove the file only if current directory ends in /tmp.
+ unlink($file) if (`pwd` =~ m(/tmp$));
+}
diff --git a/packaging/SGI/smb.conf b/packaging/SGI/smb.conf
new file mode 100644
index 00000000000..03f2a4c9f81
--- /dev/null
+++ b/packaging/SGI/smb.conf
@@ -0,0 +1,135 @@
+; Configuration file for smbd.
+; ============================================================================
+; For the format of this file and comprehensive descriptions of all the
+; configuration option, please refer to the man page for smb.conf(5).
+
+; This is a sample configuration for IRIX 6.x systems
+;
+; The following configuration should suit most systems for basic usage and
+; initial testing. It gives all clients access to their home directories and
+; /usr/tmp and allows access to all printers returned by lpstat.
+;
+[global]
+ comment = Samba %v
+ workgroup = workgroup
+ printing = sysv
+;
+; The default for printcap name is lpstat which will export all printers.
+; If you want to limit the printers that are visible to clients, you can
+; use a printcap file. The script mkprintcap.sh will create a printcap
+; file that contains all your printers. Edit this file to only contain the
+; printers that you wish to be visible. Names longer than 15 characters
+; in the printcap file will not be visible to clients.
+;
+; printcap name = /usr/samba/printcap
+ printcap name = lpstat
+;
+; If you are using Impressario 1.x then you'll want to use the
+; sambalp script provided with this package. It works around
+; a problem in the PostScript generated by the standard Windows
+; drivers--there is a check to verify sufficient virtual memory
+; is available in the printer to print the job, but this fails
+; under Impressario because of a bug in Impressario 1.x. The sambalp
+; script strips out the vmstatus check. BTW, when using this
+; setup to print be sure to configure a Windows printer driver
+; that generates PostScript--QMS-PS 810 is one that should work
+; with the sambalp script. This version of sambalp (if installed
+; as a setuid script - see the comments at the beginning of the
+; script) will setuid to the username if valid on the system. This
+; makes the banner pages print the proper username. You can disable
+; the PostScript fixes by changing a variable in sambalp.
+;
+ print command = /usr/samba/bin/sambalp %p %s %U %m
+; print command = /usr/bin/lp -c -d%p -t"%U on machine %m" %s ; rm %s
+
+; clear the default lppause and lpresume commands since these are not
+; supported in IRIX
+ lppause command =
+ lpresume command =
+
+ load printers = yes
+ guest account = nobody
+ browseable = yes
+
+; this tells Samba to use a separate log file for each machine
+; that connects - default is single file named /usr/samba/var/log.smb
+; log file = /usr/samba/var/log.%m
+
+; Set a max size for log files in Kb
+ max log size = 50
+
+; You will need a world readable lock directory
+; if you want to support the file sharing modes for multiple users
+; of the same files
+ locking = yes
+ lock directory = /usr/samba/var/locks
+
+ security = user
+
+; You need to test to see if this makes a difference on your system
+ socket options = TCP_NODELAY
+
+; Set the os level to > 32 if there is no NT server for your workgroup
+ os level = 0
+ preferred master = no
+ domain master = no
+ local master = no
+ wins support = no
+ wins server =
+
+ preserve case = yes
+ short preserve case = yes
+
+; These are the settings required for IRIX password sync
+ passwd program = /usr/bin/passwd %u
+ passwd chat = *ew*password:* %n\n *e-enter*new*password:* %n\n
+
+; Uncomment the following if you wish to use encrypted passwords.
+; encrypt passwords = yes
+
+; Uncomment the following if you wish to sync unix and smbpasswd
+; unix password sync = yes
+
+; Printer admin account to allow uploading printer drivers
+ printer admin = lp
+
+; Sample winbindd configuration parameters - uncomment and
+; change if necessary for your desired configuration
+; winbind uid = 50000-60000
+; winbind gid = 50000-60000
+; winbind separator = +
+; winbind cache time = 10
+; password server = *
+
+; Sample add user command for automatically adding machine accounts
+; add user script = /usr/sbin/passmgmt -a -h/dev/null -g20 -s/usr/bin/false %u
+
+[homes]
+ comment = Home Directories
+ browseable = no
+ writeable = yes
+
+; Share for printer drivers for automatic driver download
+;
+[print$]
+ comment = printer driver directory
+ path = /usr/samba/printer
+ guest ok = yes
+ browseable = yes
+ read only = yes
+ write list = lp
+
+[printers]
+ comment = All Printers
+ path = /var/spool/samba
+ browseable = no
+ printable = yes
+ guest ok = yes
+ writeable = no
+ create mask = 0700
+
+[tmp]
+ comment = Temporary file space
+ path = /usr/tmp
+ writeable = yes
+ guest ok = yes
diff --git a/packaging/SGI/smbpasswd b/packaging/SGI/smbpasswd
new file mode 100644
index 00000000000..8e7ab34cadd
--- /dev/null
+++ b/packaging/SGI/smbpasswd
@@ -0,0 +1 @@
+# Samba SMB password file
diff --git a/packaging/SGI/smbprint b/packaging/SGI/smbprint
new file mode 100644
index 00000000000..07923a42b1e
--- /dev/null
+++ b/packaging/SGI/smbprint
@@ -0,0 +1,54 @@
+#!/bin/sh
+#
+# @(#) smbprint.sysv version 1.0 Ross Wakelin <r.wakelin@march.co.uk>
+#
+# Version 1.0 13 January 1995
+# modified from the original smbprint (bsd) script
+#
+# this script is a System 5 printer interface script. It uses the smbclient
+# program to print the file to the specified smb-based server and service.
+#
+# To add this to your lp system, modify the server and service variables
+# and then execute the following command (as root):
+#
+# lpadmin -punixprintername -v/dev/null -i/usr/samba/bin/smbprint
+#
+# where unixprintername is the name that the printer will be known as
+# on your unix box.
+#
+# the script smbprint will be copied into your printer administration
+# directory (/usr/spool/lp) as a new interface (interface/unixprintername)
+# Then you have to execute the following commands:
+#
+# enable unixprintername
+# accept unixprintername
+#
+# This script will then be called by the lp service to print the files.
+# This script will have 6 or more parameters passed to it by the lp service.
+# The first five will contain details of the print job, who queued it etc,
+# while parameters 6 onwards are a list of files to print. We just
+# cat these to the samba client.
+#
+# clear out the unwanted parameters
+
+shift;shift;shift;shift;shift
+
+# now the argument list is just the files to print
+
+# Set these to the server and service you wish to print to
+# In this example I have a PC called "admin" that has a printer
+# exported called "hplj2" with no password.
+#
+server=admin
+service=hplj2
+password=""
+
+# NOTE: The line `echo translate' provides automatic CR/LF translation
+# when printing.
+(
+ echo translate
+ echo "print -"
+ cat $*
+) | /usr/samba/bin/smbclient "//$server/$service" $password -N > /dev/null
+exit $?
+
diff --git a/packaging/SGI/spec.pl b/packaging/SGI/spec.pl
new file mode 100755
index 00000000000..d581db7043f
--- /dev/null
+++ b/packaging/SGI/spec.pl
@@ -0,0 +1,97 @@
+#!/usr/bin/perl
+
+# This perl script generates the samba.spec file based on the version
+# information in the version.h file in the source tree
+
+open (VER,'../../source/include/version.h') || die "Unable to open version.h\n";
+while ( <VER> ) {
+ chomp;
+ if ( /SAMBA_VERSION_OFFICIAL_STRING/ ) {
+ s/^.*SAMBA_VERSION_OFFICIAL_STRING "//;
+ s/".*$//;
+ $SambaVersion = $_;
+ }
+}
+close (VER);
+
+# create the package name
+$vername = " id \"Samba Version ".$SambaVersion."\"\n";
+
+$_ = $SambaVersion;
+s/^.* //;
+$patch = 0;
+#create the subsystem version numbers
+if (/alpha/) {
+ $_ =~ s/alpha/.00./;
+}
+elsif (/-HEAD/) {
+ $_ =~ s/-HEAD/.01/;
+ $_ .= '.99';
+}
+elsif (/pre-/) {
+ $_ =~ s/pre-//;
+ $_ .= '.00';
+}
+elsif (/p/) {
+ $_ =~ s/p/./;
+ $_ .= '.00';
+ $patch = 1;
+}
+else {
+ $_ .='.01.00';
+}
+
+($v1,$v2,$v3,$v4,$v5) = split('\.');
+$v4 = $v4 + $patch;
+$vernum = sprintf(" version %02d%02d%02d%02d%02d\n",$v1,$v2,$v3,$v4,$v5);
+
+# generate the samba.spec file
+open(SPEC,">samba.spec") || die "Unable to open samba.spec for output\n";
+print SPEC "product samba\n";
+print SPEC $vername;
+print SPEC " image sw\n";
+print SPEC " id \"Samba Execution Environment\"\n";
+print SPEC $vernum;
+print SPEC " order 0\n";
+print SPEC " subsys base default\n";
+print SPEC " id \"Samba Execution Environment\"\n";
+print SPEC " replaces fw_samba.sw.base 0 9999999999\n";
+print SPEC " replaces fw_samba.sw.samba 0 9999999999\n";
+print SPEC " exp samba.sw.base\n";
+print SPEC " endsubsys\n";
+print SPEC " endimage\n";
+print SPEC " image man\n";
+print SPEC " id \"Samba Online Documentation\"\n";
+print SPEC $vernum;
+print SPEC " order 1\n";
+print SPEC " subsys manpages default\n";
+print SPEC " id \"Samba Man Page\"\n";
+print SPEC " replaces fw_samba.man.manpages 0 9999999999\n";
+print SPEC " replaces fw_samba.man.samba 0 9999999999\n";
+print SPEC " exp samba.man.manpages\n";
+print SPEC " endsubsys\n";
+print SPEC " subsys doc default\n";
+print SPEC " id \"Samba Documentation\"\n";
+print SPEC " replaces fw_samba.man.doc 0 9999999999\n";
+print SPEC " exp samba.man.doc\n";
+print SPEC " endsubsys\n";
+print SPEC " subsys relnotes default\n";
+print SPEC " id \"Samba Release Notes\"\n";
+print SPEC " replaces fw_samba.man.relnotes 0 9999999999\n";
+print SPEC " exp samba.man.relnotes\n";
+print SPEC " endsubsys\n";
+print SPEC " endimage\n";
+print SPEC " image src\n";
+print SPEC " id \"Samba Source Code\"\n";
+print SPEC $vernum;
+print SPEC " order 2\n";
+print SPEC " subsys samba\n";
+print SPEC " id \"Samba Source Code\"\n";
+print SPEC " replaces fw_samba.src.samba 0 9999999999\n";
+print SPEC " exp samba.src.samba\n";
+print SPEC " endsubsys\n";
+print SPEC " endimage\n";
+print SPEC "endproduct\n";
+close SPEC || die "Error on close of samba.spec\n";
+
+print "\nsamba.spec file has been created\n\n";
diff --git a/packaging/SGI/startswat.sh b/packaging/SGI/startswat.sh
new file mode 100755
index 00000000000..2a0333020fb
--- /dev/null
+++ b/packaging/SGI/startswat.sh
@@ -0,0 +1,29 @@
+#! /bin/sh
+#
+# add SWAT deamon to inetd.conf
+#
+cp /etc/inetd.conf /etc/inetd.conf.O
+
+if [ $? -ne 0 ]; then exit 1; fi
+if [ ! -r /etc/inetd.conf.O -o ! -w /etc/inetd.conf ]; then exit 1; fi
+
+sed -e "/^swat/D" -e "/^#SWAT/D" /etc/inetd.conf.O > /etc/inetd.conf
+echo '#SWAT services' >> /etc/inetd.conf
+echo swat stream tcp nowait root /usr/samba/bin/swat swat >> /etc/inetd.conf
+
+#
+# add SWAT service port to /etc/services
+#
+cp /etc/services /etc/services.O
+
+if [ $? -ne 0 ]; then exit 1; fi
+if [ ! -r /etc/services.O -o ! -w /etc/services ]; then exit 1; fi
+
+sed -e "/^swat/D" -e "/^#SWAT/D" /etc/services.O > /etc/services
+echo '#SWAT services' >> /etc/services
+echo 'swat 901/tcp # SWAT' >> /etc/services
+
+#
+# restart inetd to start SWAT
+#
+/etc/killall -HUP inetd
diff --git a/packaging/SGI/winbindd.config b/packaging/SGI/winbindd.config
new file mode 100644
index 00000000000..cfb931e4705
--- /dev/null
+++ b/packaging/SGI/winbindd.config
@@ -0,0 +1 @@
+off
diff --git a/packaging/SGI/winbindd.rc b/packaging/SGI/winbindd.rc
new file mode 100644
index 00000000000..deb4708c667
--- /dev/null
+++ b/packaging/SGI/winbindd.rc
@@ -0,0 +1,38 @@
+#! /bin/sh
+
+#
+# winbindd control
+#
+
+IS_ON=/etc/chkconfig
+KILLALL=/sbin/killall
+
+WINBINDD=/usr/samba/bin/winbindd
+
+if test ! -x $IS_ON ; then
+ IS_ON=true
+fi
+
+if $IS_ON verbose ; then
+ ECHO=echo
+else # For a quiet startup and shutdown
+ ECHO=:
+fi
+
+case $1 in
+'start')
+ if $IS_ON winbind && test -x $WINBINDD; then
+ $KILLALL -15 winbindd
+ $ECHO "winbindd:\c"
+ $WINBINDD ; $ECHO " winbindd."
+ fi
+ ;;
+'stop')
+ $ECHO "Stopping winbindd."
+ $KILLALL -15 winbindd
+ exit 0
+ ;;
+*)
+ echo "usage: /etc/init.d/winbind {start|stop}"
+ ;;
+esac
diff --git a/packaging/Solaris/.cvsignore b/packaging/Solaris/.cvsignore
new file mode 100644
index 00000000000..3adf27434de
--- /dev/null
+++ b/packaging/Solaris/.cvsignore
@@ -0,0 +1,4 @@
+inetd.conf
+pkginfo
+prototype
+samba.server
diff --git a/packaging/Solaris/README b/packaging/Solaris/README
new file mode 100644
index 00000000000..b918cf91732
--- /dev/null
+++ b/packaging/Solaris/README
@@ -0,0 +1,18 @@
+
+INSTRUCTIONS: Preparing Samba packages for Solaris
+
+To produce a package:
+
+* Build the binaries (by running ./configure; make; in the source directory)
+* Type sh makepkg.sh
+
+The package will be created in the /tmp directory.
+
+By default, the package will be built to install samba in /usr/local
+To change the default, modify the INSTALL_BASE variable in makepkg.sh
+This is after you have configured samba with a --prefix option of the
+alternate samba location and then created the binaries.
+
+Shirish Kalele <kalele@samba.org>
+Date: 2000.01.12
+
diff --git a/packaging/Solaris/copyright b/packaging/Solaris/copyright
new file mode 100644
index 00000000000..1792668d174
--- /dev/null
+++ b/packaging/Solaris/copyright
@@ -0,0 +1 @@
+Copyright (C) 2001 Samba Team
diff --git a/packaging/Solaris/i.swat b/packaging/Solaris/i.swat
new file mode 100644
index 00000000000..d07d2798d85
--- /dev/null
+++ b/packaging/Solaris/i.swat
@@ -0,0 +1,44 @@
+while read src dest
+do
+ sed -e '/^swat.*swat$/d' $dest >/tmp/$$swat || exit 2
+ cat $src >>/tmp/$$swat || exit 2
+
+ # Use cp;rm instead of mv because $dest might be a symlink
+ cp -f /tmp/$$swat $dest || exit 2
+ rm -f /tmp/$$swat
+done
+
+if [ "$1" = ENDOFCLASS ]
+then
+
+ # If local install, restart inetd
+ if [ -z "${PKG_INSTALL_ROOT}" ]
+ then
+ TARGET=`hostname`
+ kill -HUP `ps -e -o pid,comm | grep inetd | awk '{print $1}'`
+ else
+ TARGET="<servername>"
+ fi
+
+ cat <<EOF
+The Samba Web Administration Tool (SWAT) has been installed on your system.
+You can connect to it from a web browser on TCP port 901 at
+http://${TARGET}:901/.
+
+If you use NIS/NIS+, check the ${PKG_INSTALL_ROOT}/etc/nsswitch.conf file
+to verify that the local services file is being used as a backend for the
+services database, or you won't be able to connect to the Samba Admin Tool.
+
+EOF
+
+ if [ ! -z "$PKG_INSTALL_ROOT" ]
+ then
+ cat <<EOF
+The SWAT settings will not take effect till you send a hangup (HUP) signal
+to inetd on the target system.
+
+EOF
+ fi
+
+fi
+
diff --git a/packaging/Solaris/inetd.conf.master b/packaging/Solaris/inetd.conf.master
new file mode 100644
index 00000000000..b11fb7c3db2
--- /dev/null
+++ b/packaging/Solaris/inetd.conf.master
@@ -0,0 +1 @@
+swat stream tcp nowait.400 root __BASEDIR__/samba/bin/swat swat
diff --git a/packaging/Solaris/makepkg.sh.tmpl b/packaging/Solaris/makepkg.sh.tmpl
new file mode 100755
index 00000000000..2a46321f818
--- /dev/null
+++ b/packaging/Solaris/makepkg.sh.tmpl
@@ -0,0 +1,176 @@
+#!/bin/sh
+#
+# Copyright (C) Shirish A Kalele 2000
+#
+# Builds a Samba package from the samba distribution.
+# By default, the package will be built to install samba in /usr/local
+# Change the INSTALL_BASE variable to change this: will modify the pkginfo
+# and samba.server files to point to the new INSTALL_BASE
+#
+INSTALL_BASE=/usr/local
+
+add_dynamic_entries()
+{
+ # Add the binaries, docs and SWAT files
+
+ echo "#\n# Binaries \n#"
+ cd $DISTR_BASE/source/bin
+ for binfile in *
+ do
+ if [ -f $binfile ]; then
+ case $file in
+ CP*.so)
+ echo echo f none samba/lib/charset/$binfile=source/bin/$binfile 0755 root other
+ ;;
+ *)
+ echo f none samba/bin/$binfile=source/bin/$binfile 0755 root other
+ ;;
+ esac
+ fi
+ done
+
+ # Add the scripts to bin/
+ echo "#\n# Scripts \n#"
+ cd $DISTR_BASE/source/script
+ for shfile in *
+ do
+ if [ -f $shfile ]; then
+ echo f none samba/bin/$shfile=source/script/$shfile 0755 root other
+ fi
+ done
+
+ # add libraries to /lib for winbind
+ echo "#\n# Libraries \n#"
+ if [ -f $DISTR_BASE/source/nsswitch/libnss_winbind.so ] ; then
+ echo f none /usr/lib/libnss_winbind.so=source/nsswitch/libnss_winbind.so 0755 root other
+ echo s none /usr/lib/libnss_winbind.so.1=/usr/lib/libnss_winbind.so 0755 root other
+ echo s none /usr/lib/libnss_winbind.so.2=/usr/lib/libnss_winbind.so 0755 root other
+ echo s none /usr/lib/nss_winbind.so.1=/usr/lib/libnss_winbind.so 0755 root other
+ echo s none /usr/lib/nss_winbind.so.2=/usr/lib/libnss_winbind.so 0755 root other
+ fi
+
+ # add the .dat codepages
+ echo "#\n# Codepages \n#"
+ for file in $DISTR_BASE/source/codepages/*.dat ; do
+ bfile=`basename $file`
+ echo f none /usr/local/samba/lib/$bfile=source/codepages/$bfile
+ done
+
+ # Add the manpages
+ echo "#\n# man pages \n#"
+ echo d none /usr ? ? ?
+ echo d none /usr/share ? ? ?
+ echo d none /usr/share/man ? ? ?
+
+ # Create directories for man page sections if nonexistent
+ cd $DISTR_BASE/docs/manpages
+ for i in 1 2 3 4 5 6 7 8 9
+ do
+ manpages=`ls *.$i 2>/dev/null`
+ if [ $? -eq 0 ]
+ then
+ echo d none /usr/share/man/man$i ? ? ?
+ for manpage in $manpages
+ do
+ echo f none /usr/share/man/man${i}/${manpage}=docs/manpages/$manpage 0644 root other
+ done
+ fi
+ done
+
+ echo "#\n# HTML documentation \n#"
+ cd $DISTR_BASE
+ list=`find docs/htmldocs -type d | grep -v "/CVS$"`
+ for docdir in $list
+ do
+ if [ -d $docdir ]; then
+ echo d none samba/$docdir 0755 root other
+ fi
+ done
+
+ list=`find docs/htmldocs -type f | grep -v /CVS/`
+ for htmldoc in $list
+ do
+ if [ -f $htmldoc ]; then
+ echo f none samba/$htmldoc=$htmldoc 0644 root other
+ fi
+ done
+
+ # Create a symbolic link to the Samba book in docs/ for beginners
+ echo 's none samba/docs/samba_book=htmldocs/using_samba'
+
+ echo "#\n# SWAT \n#"
+ cd $DISTR_BASE
+ list=`find swat -type d | grep -v "/CVS$"`
+ for i in $list
+ do
+ echo "d none samba/$i 0755 root other"
+ done
+ list=`find swat -type f | grep -v /CVS/`
+ for i in $list
+ do
+ echo "f none samba/$i=$i 0644 root other"
+ done
+ # add the .msg files for SWAT
+ echo "#\n# msg files \n#"
+ for file in $DISTR_BASE/source/po/*.msg ; do
+ bfile=`basename $file`
+ echo f none /usr/local/samba/lib/$bfile=source/po/$bfile
+ done
+
+ echo "#\n# HTML documentation for SWAT\n#"
+ cd $DISTR_BASE/docs/htmldocs
+ for htmldoc in *
+ do
+ if [ -f $htmldoc ]; then
+ echo f none samba/swat/help/$htmldoc=docs/htmldocs/$htmldoc 0644 root other
+ fi
+ done
+
+ echo "#\n# Using Samba Book files for SWAT\n#"
+ cd $DISTR_BASE/docs/htmldocs
+
+# set up a symbolic link instead of duplicating the book tree
+ echo 's none samba/swat/using_samba=../docs/htmldocs/using_samba'
+
+}
+
+if [ $# = 0 ]
+then
+ # Try to guess the distribution base..
+ CURR_DIR=`pwd`
+ DISTR_BASE=`echo $CURR_DIR | sed 's|\(.*\)/packaging.*|\1|'`
+ echo "Assuming Samba distribution is rooted at $DISTR_BASE.."
+else
+ DISTR_BASE=$1
+fi
+
+#
+if [ ! -d $DISTR_BASE ]; then
+ echo "Source build directory $DISTR_BASE does not exist."
+ exit 1
+fi
+
+# Set up the prototype file from prototype.master
+if [ -f prototype ]; then
+ rm prototype
+fi
+
+# Setup version from version.h
+VERSION=PVERSION
+sed -e "s|__VERSION__|$VERSION|" -e "s|__ARCH__|`uname -p`|" -e "s|__BASEDIR__|$INSTALL_BASE|g" pkginfo.master >pkginfo
+
+sed -e "s|__BASEDIR__|$INSTALL_BASE|g" inetd.conf.master >inetd.conf
+sed -e "s|__BASEDIR__|$INSTALL_BASE|g" samba.server.master >samba.server
+
+cp prototype.master prototype
+
+# Add the dynamic part to the prototype file
+(add_dynamic_entries >> prototype)
+
+# Create the package
+pkgmk -o -d /tmp -b $DISTR_BASE -f prototype
+if [ $? = 0 ]
+then
+ pkgtrans /tmp samba.pkg samba
+fi
+echo The samba package is in /tmp
diff --git a/packaging/Solaris/pkg-specs/pkginfo b/packaging/Solaris/pkg-specs/pkginfo
new file mode 100644
index 00000000000..d24ecaefe81
--- /dev/null
+++ b/packaging/Solaris/pkg-specs/pkginfo
@@ -0,0 +1,12 @@
+PKG=samba
+NAME=SMB based file/printer sharing
+ARCH=sparc
+VERSION=3.0.0beta3
+CATEGORY=system
+VENDOR=Samba Group
+DESC=File and printer sharing for NT workstations
+HOTLINE=Please contact your local UNIX support group
+EMAIL=samba@samba.org
+CLASSES=none
+BASEDIR=/usr/local
+INTONLY=1
diff --git a/packaging/Solaris/pkginfo.master b/packaging/Solaris/pkginfo.master
new file mode 100644
index 00000000000..33e7cdb471d
--- /dev/null
+++ b/packaging/Solaris/pkginfo.master
@@ -0,0 +1,12 @@
+PKG=samba
+NAME=SMB based file/printer sharing
+ARCH=__ARCH__
+VERSION=__VERSION__
+CATEGORY=system
+VENDOR=Samba Team
+DESC=File and printer sharing for Windows workstations
+HOTLINE=Please contact your local UNIX support group
+EMAIL=samba@samba.org
+CLASSES=none
+BASEDIR=__BASEDIR__
+INTONLY=1
diff --git a/packaging/Solaris/postinstall b/packaging/Solaris/postinstall
new file mode 100644
index 00000000000..0b7f40a85d0
--- /dev/null
+++ b/packaging/Solaris/postinstall
@@ -0,0 +1,21 @@
+cat <<EOF
+___________________________________________________________________________
+
+INSTALLATION COMPLETE.
+
+All files comprising the Samba Server have been installed.
+
+You can configure Samba by creating a configuration file at
+${BASEDIR}/samba/lib/smb.conf. For details on configuration,
+refer to the Samba man pages under ${PKG_INSTALL_ROOT}/usr/share/man
+and the documentation at ${BASEDIR}/samba/docs.
+
+BEGINNERS:
+Beginners can also refer to the excellent "Using Samba" book published
+by O'Reilly and Associates and officially supported by the Samba Team.
+This book is supplied with this package and can be accessed at
+${BASEDIR}/samba/docs/samba_book/index.html
+___________________________________________________________________________
+
+EOF
+
diff --git a/packaging/Solaris/preremove b/packaging/Solaris/preremove
new file mode 100644
index 00000000000..28e8d75c298
--- /dev/null
+++ b/packaging/Solaris/preremove
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+# If this is a local deinstall, stop samba
+if [ -z "$PKG_INSTALL_ROOT" ]
+then
+ SMBD=`ps -e -o pid,comm | grep smbd | awk '{print $1}'`
+ NMBD=`ps -e -o pid,comm | grep nmbd | awk '{print $1}'`
+ [ ! -z "$SMBD" ] && kill $SMBD
+ [ ! -z "$NMBD" ] && kill $NMBD
+ sleep 2
+fi
+
diff --git a/packaging/Solaris/prototype.master b/packaging/Solaris/prototype.master
new file mode 100644
index 00000000000..6c5aa3e28ab
--- /dev/null
+++ b/packaging/Solaris/prototype.master
@@ -0,0 +1,54 @@
+#
+# The static master prototype file for the Samba package.
+# For files that can't be dynamically added to the prototype file at
+# package build time
+#
+# Information files.
+#
+i pkginfo=./pkginfo
+i copyright=./copyright
+i request=./request
+i preremove=./preremove
+i postinstall=./postinstall
+i i.swat=./i.swat
+i r.swat=./r.swat
+#
+# Stuff that goes into the system areas of the filesystem.
+#
+d none /etc ? ? ?
+d initscript /etc/init.d ? ? ?
+f initscript /etc/init.d/samba.server=packaging/Solaris/samba.server 0744 root sys
+d initscript /etc/rc3.d ? ? ?
+s initscript /etc/rc3.d/S99samba.server=../init.d/samba.server
+#
+# Stuff to set up SWAT
+#
+d swat /etc/inet ? ? ?
+e swat /etc/inet/services=packaging/Solaris/services ? ? ?
+e swat /etc/inet/inetd.conf=packaging/Solaris/inetd.conf ? ? ?
+#
+# Create the samba subtree. (Usually /usr/local/samba )
+#
+d none samba 0755 root other
+d none samba/var 0755 root other
+d none samba/bin 0755 root other
+d none samba/lib 0755 root other
+d none samba/docs 0755 root other
+#
+# Stuff that goes into lib
+#
+d none samba/lib/charset 0755 root other
+f none samba/lib/smb.conf.example=examples/smb.conf.default 0644 root other
+d none samba/lib/regeditscripts 0755 root other
+f none samba/lib/regeditscripts/NT4_PlainPassword.reg=docs/Registry/NT4_PlainPassword.reg 0444 root other
+f none samba/lib/regeditscripts/Win95_PlainPassword.reg=docs/Registry/Win95_PlainPassword.reg 0444 root other
+f none samba/lib/regeditscripts/Win98_PlainPassword.reg=docs/Registry/Win98_PlainPassword.reg 0444 root other
+f none samba/lib/regeditscripts/WinME_PlainPassword.reg=docs/Registry/WinME_PlainPassword.reg 0444 root other
+f none samba/lib/regeditscripts/Win2000_PlainPassword.reg=docs/Registry/Win2000_PlainPassword.reg 0444 root other
+f none samba/lib/regeditscripts/WinXP_PlainPassword.reg=docs/Registry/WinXP_PlainPassword.reg 0444 root other
+#
+# Random files
+f none samba/docs/Samba-HOWTO-Collection.pdf=docs/Samba-HOWTO-Collection.pdf 0644 root other
+#
+# Static part of prototype file ends.
+#
diff --git a/packaging/Solaris/r.swat b/packaging/Solaris/r.swat
new file mode 100644
index 00000000000..11c776646da
--- /dev/null
+++ b/packaging/Solaris/r.swat
@@ -0,0 +1,16 @@
+while read dest
+do
+ sed -e '/^swat.*swat$/d' $dest >/tmp/$$swat || exit 2
+ # Use cp;rm; instead of mv because $dest might be a symlink
+ cp -f /tmp/$$swat $dest || exit 2
+ rm -f /tmp/$$swat
+done
+
+if [ "$1" = ENDOFCLASS ]
+then
+ if [ -z "$PKG_INSTALL_ROOT" ]
+ then
+ kill -HUP `ps -e -o pid,comm | grep inetd | awk '{print $1}'`
+ fi
+fi
+
diff --git a/packaging/Solaris/request b/packaging/Solaris/request
new file mode 100644
index 00000000000..59cdd0ab22a
--- /dev/null
+++ b/packaging/Solaris/request
@@ -0,0 +1,17 @@
+trap 'exit 3' 15
+
+VALSTR=/usr/sadm/bin/valstr
+
+resp=`ckyorn -d y -p "Do you wish to have Samba start whenever the system boots up? (default:y) " -Q`
+$VALSTR -r "^[yY]" $resp
+[ $? -eq 0 ] && CLASSES="$CLASSES initscript"
+
+resp=`ckyorn -d y -p "Do you wish to set up the Samba Web Admin Tool (SWAT)? (default:y) " -Q`
+$VALSTR -r "^[yY]" $resp
+[ $? -eq 0 ] && CLASSES="$CLASSES swat"
+
+cat >$1 <<!
+CLASSES=$CLASSES
+!
+exit 0
+
diff --git a/packaging/Solaris/samba.server.master b/packaging/Solaris/samba.server.master
new file mode 100755
index 00000000000..d8bea2421c3
--- /dev/null
+++ b/packaging/Solaris/samba.server.master
@@ -0,0 +1,48 @@
+#!/bin/sh
+#ident "@(#)samba.server 1.0 96/06/19 TK" /* SVr4.0 1.1.13.1*/
+#
+# Please send info on modifications to knuutila@cs.utu.fi
+#
+# This file should have uid root, gid sys and chmod 744
+#
+if [ ! -d /usr/bin ]
+then # /usr not mounted
+ exit
+fi
+
+killproc() { # kill the named process(es)
+ pid=`/usr/bin/ps -e |
+ /usr/bin/grep -w $1 |
+ /usr/bin/sed -e 's/^ *//' -e 's/ .*//'`
+ [ "$pid" != "" ] && kill $pid
+}
+
+# Start/stop processes required for samba server
+
+case "$1" in
+
+'start')
+#
+# Edit these lines to suit your installation (paths, workgroup, host)
+#
+ BASE=__BASEDIR__/samba
+ $BASE/bin/smbd -D -s$BASE/lib/smb.conf
+ $BASE/bin/nmbd -D -s$BASE/lib/smb.conf
+ ;;
+'stop')
+ killproc nmbd
+ killproc smbd
+ ;;
+
+'restart')
+ killproc nmbd
+ killproc smbd
+ BASE=__BASEDIR__/samba
+ $BASE/bin/smbd -D -s$BASE/lib/smb.conf
+ $BASE/bin/nmbd -D -l$BASE/var/log -s$BASE/lib/smb.conf
+ ;;
+
+*)
+ echo "Usage: /etc/init.d/samba.server { start | stop | restart }"
+ ;;
+esac
diff --git a/packaging/Solaris/services b/packaging/Solaris/services
new file mode 100644
index 00000000000..fc691200c8d
--- /dev/null
+++ b/packaging/Solaris/services
@@ -0,0 +1 @@
+swat 901/tcp # Samba Web Admin Tool - swat
diff --git a/packaging/SuSE/README b/packaging/SuSE/README
new file mode 100644
index 00000000000..5d0af9944aa
--- /dev/null
+++ b/packaging/SuSE/README
@@ -0,0 +1,18 @@
+Date: March 29, 2003
+
+Note: The current packaging files are NOT officially supported files.
+---------------------------------------------------------------------
+
+While the SPEC file shows who the original author was, these files imply no warranty of
+fitness what so ever. These files are NOT official SuSE files and are NOT supported by
+them. If you have ANY problems with the use of these files then please email jht@samba.org
+and NOT SuSE support.
+
+
+These files may be used to build Samba-3.0 packages for SuSE Linux 8.1 and/or for
+UnitedLinux 1.0 systems.
+
+Note2: You most likely will need to update to heimdal-0.5.1 or later if you intend to
+use any Kerberos functionality.
+
+- John T.
diff --git a/packaging/SuSE/samba-3.0.0.files.tar.bz2 b/packaging/SuSE/samba-3.0.0.files.tar.bz2
new file mode 100644
index 00000000000..1e8fc9baf0c
--- /dev/null
+++ b/packaging/SuSE/samba-3.0.0.files.tar.bz2
Binary files differ
diff --git a/packaging/SuSE/samba-mutual-auth.diff b/packaging/SuSE/samba-mutual-auth.diff
new file mode 100644
index 00000000000..865f91682a3
--- /dev/null
+++ b/packaging/SuSE/samba-mutual-auth.diff
@@ -0,0 +1,247 @@
+--- source/configure.in 22 Feb 2003 12:19:18 -0000 1.409
++++ source/configure.in 24 Feb 2003 06:04:25 -0000
+@@ -627,6 +627,15 @@
+ fi
+
+ ############################################
++# support for using Kerberos keytab instead of secrets database
++
++AC_ARG_ENABLE(keytab,
++[ --enable-keytab Turn on support for Kerberos keytabs in lieu of secrets DB (default=no)],
++ [if eval "test x$enable_keytab = xyes"; then
++ AC_DEFINE(USE_KEYTAB,1,[Use Kerberos keytab])
++ 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
+--- source/passdb/secrets.c 1 Feb 2003 04:39:15 -0000 1.54
++++ source/passdb/secrets.c 24 Feb 2003 06:04:26 -0000
+@@ -221,6 +221,72 @@
+ return True;
+ }
+
++#ifdef USE_KEYTAB
++/************************************************************************
++ Read local secret from the keytab
++************************************************************************/
++
++static BOOL secrets_fetch_keytab_password(uint8 ret_pwd[16], time_t *pass_last_set_time)
++{
++ char spn[MAXHOSTNAMELEN + 2], *p;
++ krb5_context context;
++ krb5_error_code ret;
++ krb5_principal princ;
++ krb5_keyblock *key;
++
++ ret = krb5_init_context(&context);
++ if (ret) {
++ DEBUG(1, ("secrets_fetch_keytab_password: failed to initialize Kerberos context\n"));
++ return False;
++ }
++
++ spn[sizeof(spn) - 1] = '\0';
++ if (gethostname(spn, sizeof(spn) - 2) < 0) {
++ DEBUG(1, ("secrets_fetch_keytab_password: could not determine local hostname\n"));
++ krb5_free_context(context);
++ return False;
++ }
++
++ for (p = spn; *p && *p != '.'; p++)
++ *p = toupper(*p);
++ *p++ = '$';
++ *p = '\0';
++
++ ret = krb5_parse_name(context, spn, &princ);
++ if (ret) {
++ DEBUG(1, ("secrets_fetch_keytab_password: failed to parse name %s\n", spn));
++ krb5_free_context(context);
++ return False;
++ }
++
++#ifdef ENCTYPE_ARCFOUR_HMAC
++ ret = krb5_kt_read_service_key(context, NULL, princ, 0, ENCTYPE_ARCFOUR_HMAC, &key);
++#elif defined(HAVE_ENCTYPE_ARCFOUR_HMAC_MD5)
++ ret = krb5_kt_read_service_key(context, NULL, princ, 0, ENCTYPE_ARCFOUR_HMAC_MD5, &key);
++#else
++#error ENCTYPE_ARCFOUR_HMAC or ENCTYPE_ARCFOUR_HMAC_MD5 required for keytab secret storage
++#endif
++ if (ret) {
++ DEBUG(1, ("secrets_fetch_keytab_password: failed to read secret for %s\n", spn));
++ krb5_free_context(context);
++ return False;
++ }
++ if (key->keyvalue.length != 16) {
++ DEBUG(1, ("secrets_fetch_keytab_password: key is incorrect length\n"));
++ krb5_free_context(context);
++ return False;
++ }
++
++ memcpy(ret_pwd, key->keyvalue.data, key->keyvalue.length);
++ time(pass_last_set_time); /* XXX */
++
++ krb5_free_keyblock(context, key);
++ krb5_free_context(context);
++
++ return True;
++}
++#endif /* USE_KEYTAB */
++
+ /************************************************************************
+ Routine to get the trust account password for a domain.
+ The user of this function must have locked the trust password file using
+@@ -243,6 +309,12 @@
+ pass_last_set_time = 0;
+ return True;
+ }
++
++#ifdef USE_KEYTAB
++ if (is_myworkgroup(domain)) {
++ return secrets_fetch_keytab_password(ret_pwd, pass_last_set_time);
++ }
++#endif /* USE_KEYTAB */
+
+ if (!(pass = secrets_fetch(trust_keystr(domain), &size))) {
+ DEBUG(5, ("secrets_fetch failed!\n"));
+
+--- source/libsmb/clikrb5.c 2003-07-02 00:32:55.000000000 +0200
++++ source/libsmb/clikrb5.c 2003-07-02 00:37:22.000000000 +0200
+@@ -316,11 +316,13 @@
+ krb5_enctype enc_types[] = {
+ #ifdef ENCTYPE_ARCFOUR_HMAC
+ ENCTYPE_ARCFOUR_HMAC,
++#elif defined(HAVE_ENCTYPE_ARCFOUR_HMAC_MD5)
++ ENCTYPE_ARCFOUR_HMAC_MD5,
+ #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",
+@@ -367,24 +369,26 @@
+
+ BOOL get_krb5_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 defined(ENCTYPE_ARCFOUR_HMAC) || defined(HAVE_ENCTYPE_ARCFOUR_HMAC_MD5)
+ if (krb5_auth_con_getremotesubkey(context, auth_context, &skey) == 0 && skey != NULL) {
+ if (KRB5_KEY_TYPE(skey) ==
++# ifdef ENCTYPE_ARCFOUR_HMAC
+ ENCTYPE_ARCFOUR_HMAC
++# else
++ ENCTYPE_ARCFOUR_HMAC_MD5
++# endif /* 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 */
++#endif /* ENCTYPE_ARCFOUR_HMAC || HAVE_ENCTYPE_ARCFOUR_HMAC_MD5 */
+
+ return ret;
+ }
+@@ -395,5 +399,12 @@
+ DEBUG(0,("NO KERBEROS SUPPORT\n"));
+ return data_blob(NULL, 0);
+ }
++BOOL krb5_get_smb_session_key(krb5_context context, krb5_auth_context ac, uint8 session_key[16])
++ {
++ DEBUG(0,("NO KERBEROS SUPPORT\n"));
++ memset(session_key, 0, 16);
++ return False;
++ }
++ //#endif
+
+ #endif
+--- source/libads/kerberos_verify.c 2003-06-28 23:40:55.000000000 +0200
++++ source/libads/kerberos_verify.c 2003-07-02 00:50:13.000000000 +0200
+@@ -38,7 +38,9 @@
+ krb5_keytab keytab = NULL;
+ krb5_data packet;
+ krb5_ticket *tkt = NULL;
+- int ret, i;
++ int ret;
++#ifndef USE_KEYTAB
++ int i;
+ krb5_keyblock * key;
+ krb5_principal host_princ;
+ char *host_princ_s;
+@@ -46,8 +48,10 @@
+ char *password_s;
+ krb5_data password;
+ krb5_enctype *enctypes = NULL;
++#endif /* USE_KEYTAB */
+ BOOL auth_ok = False;
+
++#ifndef USE_KEYTAB
+ if (!secrets_init()) {
+ DEBUG(1,("secrets_init failed\n"));
+ return NT_STATUS_LOGON_FAILURE;
+@@ -61,6 +65,7 @@
+
+ password.data = password_s;
+ password.length = strlen(password_s);
++#endif /* USE_KEYTAB */
+
+ ret = krb5_init_context(&context);
+ if (ret) {
+@@ -82,7 +87,16 @@
+ DEBUG(1,("krb5_auth_con_init failed (%s)\n", error_message(ret)));
+ return NT_STATUS_LOGON_FAILURE;
+ }
++#ifdef USE_KEYTAB
++ packet.length = ticket->length;
++ packet.data = (krb5_pointer)ticket->data;
+
++ if (!(ret = krb5_rd_req(context, &auth_context, &packet,
++ NULL, keytab, NULL, &tkt))) {
++ auth_ok = True;
++ }
++
++#else
+ fstrcpy(myname, global_myname());
+ strlower(myname);
+ asprintf(&host_princ_s, "HOST/%s@%s", myname, lp_realm());
+@@ -121,6 +135,9 @@
+ }
+ }
+
++ SAFE_FREE(key);
++#endif /* USE_KEYTAB */
++
+ if (!auth_ok) {
+ DEBUG(3,("krb5_rd_req with auth failed (%s)\n",
+ error_message(ret)));
+--- source/Makefile.in 2003-07-01 23:35:49.000000000 +0200
++++ source/Makefile.in 2003-07-02 01:20:09.000000000 +0200
+@@ -806,7 +806,7 @@
+
+ bin/pdbedit@EXEEXT@: $(PDBEDIT_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+- @$(CC) $(FLAGS) -o $@ $(IDMAP_LIBS) $(PDBEDIT_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) @POPTLIBS@ $(PASSDBLIBS)
++ @$(CC) $(FLAGS) -o $@ $(IDMAP_LIBS) $(PDBEDIT_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) @POPTLIBS@ $(PASSDBLIBS) $(KRB5LIBS)
+
+ bin/samtest@EXEEXT@: $(SAMTEST_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+@@ -1062,7 +1062,7 @@
+
+ bin/wbinfo@EXEEXT@: $(WBINFO_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+- @$(LINK) -o $@ $(WBINFO_OBJ) $(LIBS) @POPTLIBS@
++ @$(LINK) -o $@ $(WBINFO_OBJ) $(LIBS) @POPTLIBS@ $(KRB5LIBS)
+
+ bin/ntlm_auth@EXEEXT@: $(NTLM_AUTH_OBJ) $(PARAM_OBJ) $(LIB_OBJ) \
+ $(UBIQX_OBJ) @BUILD_POPT@ bin/.dummy
diff --git a/packaging/SuSE/samba-vscan-0.3.2b.tar.bz2 b/packaging/SuSE/samba-vscan-0.3.2b.tar.bz2
new file mode 100755
index 00000000000..2680bed82f0
--- /dev/null
+++ b/packaging/SuSE/samba-vscan-0.3.2b.tar.bz2
Binary files differ
diff --git a/packaging/SuSE/samba3-3.0.0-Makefiles-heimdal.diff b/packaging/SuSE/samba3-3.0.0-Makefiles-heimdal.diff
new file mode 100644
index 00000000000..13da47e7409
--- /dev/null
+++ b/packaging/SuSE/samba3-3.0.0-Makefiles-heimdal.diff
@@ -0,0 +1,22 @@
+--- examples/pdb/Makefile Thu Sep 5 02:11:41 2002
++++ examples/pdb/Makefile Thu Sep 5 02:11:59 2002
+@@ -8,7 +8,7 @@
+ SAMBA_INCL = ../../source/include
+ UBIQX_SRC = ../../source/ubiqx
+ SMBWR_SRC = ../../source/smbwrapper
+-CFLAGS = -I$(SAMBA_SRC) -I$(SAMBA_INCL) -I$(UBIQX_SRC) -I$(SMBWR_SRC) -Wall -g
++CFLAGS = -I$(SAMBA_SRC) -I$(SAMBA_INCL) -I$(UBIQX_SRC) -I$(SMBWR_SRC) -Wall -g -I/usr/include/heimdal
+ PDB_OBJS = pdb_test.so
+
+ # Default target
+--- examples/VFS/Makefile.in 2003-06-04 15:13:41.000000000 +0200
++++ examples/VFS/Makefile.in 2003-06-04 22:07:03.000000000 +0200
+@@ -7,7 +7,7 @@
+ SAMBA_SOURCE = @SAMBA_SOURCE@
+ SHLIBEXT = @SHLIBEXT@
+ OBJEXT = @OBJEXT@
+-FLAGS = $(CFLAGS) -Iinclude -I$(SAMBA_SOURCE)/include -I$(SAMBA_SOURCE)/ubiqx -I$(SAMBA_SOURCE)/smbwrapper -I. $(CPPFLAGS) -I$(SAMBA_SOURCE)
++FLAGS = $(CFLAGS) -Iinclude -I$(SAMBA_SOURCE)/include -I$(SAMBA_SOURCE)/ubiqx -I$(SAMBA_SOURCE)/smbwrapper -I. $(CPPFLAGS) -I$(SAMBA_SOURCE) -I/usr/include/heimdal
+
+
+ prefix = @prefix@
diff --git a/packaging/SuSE/samba3-3.0.0-heimdal-06.diff b/packaging/SuSE/samba3-3.0.0-heimdal-06.diff
new file mode 100644
index 00000000000..87dd3e1824c
--- /dev/null
+++ b/packaging/SuSE/samba3-3.0.0-heimdal-06.diff
@@ -0,0 +1,14 @@
+--- source/include/ads.h 2003-06-10 08:51:03.000000000 +0200
++++ source/include/ads.h 2003-06-22 23:14:24.000000000 +0200
+@@ -215,9 +215,11 @@
+ #define ENCTYPE_ARCFOUR_HMAC ENCTYPE_ARCFOUR_HMAC_MD5
+ #endif
+
++#if 0
+ /* The older versions of heimdal that don't have this
+ define don't seem to use it anyway. I'm told they
+ always use a subkey */
+ #ifndef AP_OPTS_USE_SUBKEY
+ #define AP_OPTS_USE_SUBKEY 0
+ #endif
++#endif
diff --git a/packaging/SuSE/samba3-3.0.0-pdb.diff b/packaging/SuSE/samba3-3.0.0-pdb.diff
new file mode 100644
index 00000000000..0c811b57c52
--- /dev/null
+++ b/packaging/SuSE/samba3-3.0.0-pdb.diff
@@ -0,0 +1,13 @@
+--- examples/pdb/pdb_test.c 26 Sep 2002 18:37:54 -0000 1.1.2.3
++++ examples/pdb/pdb_test.c 22 Apr 2003 20:06:31 -0000
+@@ -142,8 +142,6 @@
+ int init_module(void);
+
+ int init_module() {
+- if(smb_register_passdb("testsam", testsam_init, PASSDB_INTERFACE_VERSION))
+- return 0;
+-
+- return 1;
++ smb_register_passdb(PASSDB_INTERFACE_VERSION, "testsam", testsam_init);
++ return True;
+ }
diff --git a/packaging/SuSE/samba3-Makefile.diff b/packaging/SuSE/samba3-Makefile.diff
new file mode 100644
index 00000000000..bc1ad142d9f
--- /dev/null
+++ b/packaging/SuSE/samba3-Makefile.diff
@@ -0,0 +1,16 @@
+--- source/Makefile.in 2003-04-23 10:43:06.000000000 +0200
++++ source/Makefile.in 2003-04-23 10:45:39.000000000 +0200
+@@ -673,6 +673,13 @@
+ @echo "Using FLAGS = $(FLAGS)"
+ @echo " FLAGS32 = $(FLAGS32)"
+ @echo " LIBS = $(LIBS)"
++ @echo " TERMLIBS = $(TERMLIBS)"
++ @echo " PRINTLIBS = $(PRINTLIBS)"
++ @echo " AUTHLIBS = $(AUTHLIBS)"
++ @echo " ACLLIBS = $(ACLLIBS)"
++ @echo " PASSDBLIBS = $(PASSDBLIBS)"
++ @echo " ADSLIBS = $(ADSLIBS)"
++ @echo " KRB5LIBS = $(KRB5_LIBS)"
+ @echo " LDSHFLAGS = $(LDSHFLAGS)"
+ @echo " LDFLAGS = $(LDFLAGS)"
+
diff --git a/packaging/SuSE/samba3-com_err.diff b/packaging/SuSE/samba3-com_err.diff
new file mode 100644
index 00000000000..c5d04cebe19
--- /dev/null
+++ b/packaging/SuSE/samba3-com_err.diff
@@ -0,0 +1,60 @@
+--- source/libads/kerberos.c 23 Oct 2002 00:02:26 -0000 1.18
++++ source/libads/kerberos.c 1 Jul 2003 21:30:17 -0000
+@@ -126,6 +126,7 @@
+ return KRB5_LIBOS_CANTREADPWD;
+ }
+
++ initialize_krb5_error_table();
+ ret = kerberos_kinit_password(s, ads->auth.password, ads->auth.time_offset);
+
+ if (ret) {
+--- source/libads/kerberos_verify.c 6 Jun 2003 14:53:22 -0000 1.10.2.1
++++ source/libads/kerberos_verify.c 1 Jul 2003 21:30:17 -0000
+@@ -62,6 +62,7 @@
+ password.data = password_s;
+ password.length = strlen(password_s);
+
++ initialize_krb5_error_table();
+ ret = krb5_init_context(&context);
+ if (ret) {
+ DEBUG(1,("krb5_init_context failed (%s)\n", error_message(ret)));
+--- source/libads/krb5_setpw.c 6 Jun 2003 14:53:22 -0000 1.16.2.1
++++ source/libads/krb5_setpw.c 1 Jul 2003 21:30:17 -0000
+@@ -470,6 +470,7 @@
+ krb5_creds creds, *credsp;
+ krb5_ccache ccache;
+
++ initialize_krb5_error_table();
+ ret = krb5_init_context(&context);
+ if (ret) {
+ DEBUG(1,("Failed to init krb5 context (%s)\n", error_message(ret)));
+@@ -584,6 +585,7 @@
+ krb5_creds creds;
+ char *chpw_princ = NULL, *password;
+
++ initialize_krb5_error_table();
+ ret = krb5_init_context(&context);
+ if (ret) {
+ DEBUG(1,("Failed to init krb5 context (%s)\n", error_message(ret)));
+--- source/libsmb/clikrb5.c 1 Jul 2003 14:40:37 -0000 1.36.2.2
++++ source/libsmb/clikrb5.c 1 Jul 2003 21:30:20 -0000
+@@ -320,7 +320,8 @@
+ ENCTYPE_DES_CBC_MD5,
+ ENCTYPE_DES_CBC_CRC,
+ ENCTYPE_NULL};
+-
++
++ initialize_krb5_error_table();
+ retval = krb5_init_context(&context);
+ if (retval) {
+ DEBUG(1,("krb5_init_context failed (%s)\n",
+--- source/utils/net_lookup.c 1 Jul 2003 14:40:47 -0000 1.8.2.1
++++ source/utils/net_lookup.c 1 Jul 2003 21:30:24 -0000
+@@ -177,6 +177,7 @@
+ krb5_data realm;
+ char **realms;
+
++ initialize_krb5_error_table();
+ rc = krb5_init_context(&ctx);
+ if (rc) {
+ DEBUG(1,("krb5_init_context failed (%s)\n",
diff --git a/packaging/SuSE/samba3-net_ads_password.diff b/packaging/SuSE/samba3-net_ads_password.diff
new file mode 100644
index 00000000000..cc800fb7bfd
--- /dev/null
+++ b/packaging/SuSE/samba3-net_ads_password.diff
@@ -0,0 +1,58 @@
+Index: source/utils/net_ads.c
+===================================================================
+RCS file: /cvsroot/samba/source/utils/net_ads.c,v
+retrieving revision 1.37.2.22
+diff -u -r1.37.2.22 net_ads.c
+--- source/utils/net_ads.c 10 Jun 2003 04:15:55 -0000 1.37.2.22
++++ source/utils/net_ads.c 20 Jun 2003 19:59:36 -0000
+@@ -44,9 +44,9 @@
+ "\n\tdump the machine account details to stdout\n"
+ "\nnet ads lookup"\
+ "\n\tperform a CLDAP search on the server\n"
+-"\nnet ads password <username@realm> -Uadmin_username@realm%%admin_pass"\
++"\nnet ads password <username@realm> <password> -Uadmin_username@realm%%admin_pass"\
+ "\n\tchange a user's password using an admin account"\
+-"\n\t(note: use realm in UPPERCASE)\n"\
++"\n\t(note: use realm in UPPERCASE, prompts if password is obmitted)\n"\
+ "\nnet ads changetrustpw"\
+ "\n\tchange the trust account password of this machine in the AD tree\n"\
+ "\nnet ads printer [info | publish | remove] <printername> <servername>"\
+@@ -909,7 +909,7 @@
+ }
+
+
+- if (argc != 1) {
++ if (argc < 1) {
+ d_printf("ERROR: You must say which username to change password for\n");
+ return -1;
+ }
+@@ -941,22 +941,24 @@
+ return -1;
+ }
+
+- asprintf(&prompt, "Enter new password for %s:", user);
+-
+- new_password = getpass(prompt);
++ if (argv[1]) {
++ new_password = (char *)argv[1];
++ } else {
++ asprintf(&prompt, "Enter new password for %s:", user);
++ new_password = getpass(prompt);
++ free(prompt);
++ }
+
+ ret = kerberos_set_password(ads->auth.kdc_server, auth_principal,
+ auth_password, user, new_password, ads->auth.time_offset);
+ if (!ADS_ERR_OK(ret)) {
+ d_printf("Password change failed :-( ...\n");
+ ads_destroy(&ads);
+- free(prompt);
+ return -1;
+ }
+
+ d_printf("Password change for %s completed.\n", user);
+ ads_destroy(&ads);
+- free(prompt);
+
+ return 0;
+ }
diff --git a/packaging/SuSE/samba3-smbwrapper.diff b/packaging/SuSE/samba3-smbwrapper.diff
new file mode 100644
index 00000000000..0f7b391de0d
--- /dev/null
+++ b/packaging/SuSE/samba3-smbwrapper.diff
@@ -0,0 +1,11 @@
+--- source/smbwrapper/smbsh.c.orig 2003-05-04 19:47:39.000000000 +0200
++++ source/smbwrapper/smbsh.c 2003-05-04 19:47:47.000000000 +0200
+@@ -36,7 +36,7 @@
+ int main(int argc, char *argv[])
+ {
+ char *p, *u;
+- const char *libd = dyn_BINDIR;
++ const char *libd = dyn_LIBDIR;
+ pstring line, wd;
+ int opt;
+ extern char *optarg;
diff --git a/packaging/SuSE/samba3-vscan.diff b/packaging/SuSE/samba3-vscan.diff
new file mode 100644
index 00000000000..330b470af2c
--- /dev/null
+++ b/packaging/SuSE/samba3-vscan.diff
@@ -0,0 +1,269 @@
+--- examples/VFS/samba-vscan-0.3.2b/fprot/Makefile 2003-01-14 00:42:15.000000000 +0100
++++ examples/VFS/samba-vscan-0.3.2b/fprot/Makefile 2003-04-09 20:21:37.000000000 +0200
+@@ -14,7 +14,7 @@
+ SMBWR_SRC = ../../../../source/smbwrapper
+ SMBVS_INCL = ../include
+ SMBVS_GLB = ../global
+-CFLAGS = -I$(SAMBA_SRC) -I$(SAMBA_INCL) -I$(UBIQX_SRC) -I$(SMBWR_SRC) -I$(SMBVS_INCL) -Wall -g -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -fPIC
++CFLAGS = -I$(SAMBA_SRC) -I$(SAMBA_INCL) -I$(UBIQX_SRC) -I$(SMBWR_SRC) -I$(SMBVS_INCL) -Wall -g -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -fPIC -I/usr/include/heimdal
+ VFS_OBJS = vscan-fprotd.so
+ SOURCES = $(SMBVS_GLB)/vscan-functions.c $(SMBVS_GLB)/vscan-message.c $(SMBVS_GLB)/vscan-quarantine.c $(SMBVS_GLB)/vscan-fileaccesslog.c vscan-fprotd.c vscan-fprotd_core.c vscan-fprotd.h vscan-fprotd_core.h
+ OBJS = vscan-functions.lo vscan-message.lo vscan-quarantine.lo vscan-fileaccesslog.lo vscan-fprotd.lo vscan-fprotd_core.lo
+--- examples/VFS/samba-vscan-0.3.2b/fprot/vscan-fprotd.c 2003-02-21 21:37:44.000000000 +0100
++++ examples/VFS/samba-vscan-0.3.2b/fprot/vscan-fprotd.c 2003-04-09 20:25:25.000000000 +0200
+@@ -432,14 +432,14 @@
+ rc = vscan_do_infected_file_action(&default_vfs_ops, conn, filepath, quarantine_dir, quarantine_prefix, infected_file_action);
+
+ /* add/update file. mark file as infected! */
+- lrufiles_add(filepath, stat_buf.st_mtime, TRUE);
++ lrufiles_add(filepath, stat_buf.st_mtime, True);
+
+ /* virus found, deny acces */
+ errno = EACCES;
+ return -1;
+ } else if ( retval == 0 ) {
+ /* file is clean, add to lrufiles */
+- lrufiles_add(filepath, stat_buf.st_mtime, FALSE);
++ lrufiles_add(filepath, stat_buf.st_mtime, False);
+ }
+ }
+
+--- examples/VFS/samba-vscan-0.3.2b/fprot/vscan-fprotd_core.c 2003-01-25 18:40:57.000000000 +0100
++++ examples/VFS/samba-vscan-0.3.2b/fprot/vscan-fprotd_core.c 2003-04-09 20:23:31.000000000 +0200
+@@ -110,7 +110,7 @@
+ pstring fprotdCommand; /* the command line to be send to daemon */
+ char *str;
+ FILE *fpin, *fpout;
+- bool received_data = FALSE; /* indicates, if any response from deamon was received */
++ bool received_data = False; /* indicates, if any response from deamon was received */
+
+ /* open stream sockets */
+ fpin = fdopen(sockfd, "r");
+@@ -159,7 +159,7 @@
+
+ while ( (fgets(recvline, MAXLINE, fpin)) != NULL ) {
+
+- received_data = TRUE;
++ received_data = True;
+
+ /* ignore the HTTP response header, remove any leading
+ white spaces */
+--- examples/VFS/samba-vscan-0.3.2b/icap/Makefile 2003-01-30 00:53:02.000000000 +0100
++++ examples/VFS/samba-vscan-0.3.2b/icap/Makefile 2003-04-09 20:21:37.000000000 +0200
+@@ -15,7 +15,7 @@
+ SMBWR_SRC = ../../../../source/smbwrapper
+ SMBVS_INCL = ../include
+ SMBVS_GLB = ../global
+-CFLAGS = -I$(SAMBA_SRC) -I$(SAMBA_INCL) -I$(UBIQX_SRC) -I$(SMBWR_SRC) -I$(SMBVS_INCL) -Wall -g -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -fPIC
++CFLAGS = -I$(SAMBA_SRC) -I$(SAMBA_INCL) -I$(UBIQX_SRC) -I$(SMBWR_SRC) -I$(SMBVS_INCL) -Wall -g -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -fPIC -I/usr/include/heimdal
+ VFS_OBJS = vscan-icap.so
+ SOURCES = $(SMBVS_GLB)/vscan-functions.c $(SMBVS_GLB)/vscan-message.c $(SMBVS_GLB)/vscan-quarantine.c $(SMBVS_GLB)/vscan-fileaccesslog.c vscan-icap.c vscan-icap_core.c vscan-icap.h vscan-icap_core.h
+ OBJS = vscan-functions.lo vscan-message.lo vscan-quarantine.lo vscan-fileaccesslog.lo vscan-icap.lo vscan-icap_core.lo
+--- examples/VFS/samba-vscan-0.3.2b/icap/vscan-icap.c 2003-02-21 21:37:50.000000000 +0100
++++ examples/VFS/samba-vscan-0.3.2b/icap/vscan-icap.c 2003-04-09 20:32:20.000000000 +0200
+@@ -413,14 +413,14 @@
+ rc = vscan_do_infected_file_action(&default_vfs_ops, conn, filepath, quarantine_dir, quarantine_prefix, infected_file_action);
+
+ /* add/update file. mark file as infected! */
+- lrufiles_add(filepath, stat_buf.st_mtime, TRUE);
++ lrufiles_add(filepath, stat_buf.st_mtime, True);
+
+ /* virus found, deny acces */
+ errno = EACCES;
+ return -1;
+ } else if ( retval == 0 ) {
+ /* file is clean, add to lrufiles */
+- lrufiles_add(filepath, stat_buf.st_mtime, FALSE);
++ lrufiles_add(filepath, stat_buf.st_mtime, False);
+ }
+ }
+
+--- examples/VFS/samba-vscan-0.3.2b/icap/vscan-icap_core.c 2003-01-15 00:19:18.000000000 +0100
++++ examples/VFS/samba-vscan-0.3.2b/icap/vscan-icap_core.c 2003-04-09 20:30:56.000000000 +0200
+@@ -114,8 +114,8 @@
+ char buf[BUFLEN];
+ char recvline[MAXLINE + 1];
+ char *str;
+- bool first_line = FALSE; /* first line we've received? */
+- bool infected = FALSE; /* an infected found? */
++ bool first_line = False; /* first line we've received? */
++ bool infected = False; /* an infected found? */
+
+
+ /* get file length */
+@@ -213,7 +213,7 @@
+ /* set line buffering */
+ setvbuf(fpin, (char *)NULL, _IOLBF, 0);
+
+- first_line = TRUE;
++ first_line = True;
+ while ( (fgets(recvline, MAXLINE, fpin)) != NULL ) {
+ str = recvline;
+ if ( first_line ) {
+@@ -226,7 +226,7 @@
+ return(0);
+ }
+ else if ( strncmp("403", str, 3) == 0 ) {
+- infected = TRUE;
++ infected = True;
+ } else {
+ if ( verbose_file_logging )
+ vscan_syslog("ERROR: file %s not found, not readable or an error occured", scan_file);
+@@ -241,7 +241,7 @@
+ return(-1);
+ }
+
+- first_line = FALSE;
++ first_line = False;
+ }
+ if ( infected ) {
+ if ( strncmp("X-Infection-Found", str, 17) == 0 ) {
+--- examples/VFS/samba-vscan-0.3.2b/include/vscan-global.h 2002-11-25 16:48:10.000000000 +0100
++++ examples/VFS/samba-vscan-0.3.2b/include/vscan-global.h 2003-04-09 20:21:37.000000000 +0200
+@@ -93,7 +93,7 @@
+ */
+
+ #ifndef SAMBA_VERSION_MAJOR
+-# define SAMBA_VERSION_MAJOR 2
++# define SAMBA_VERSION_MAJOR 3
+ #endif
+
+ #ifndef SAMBA_VERSION_MINOR
+--- examples/VFS/samba-vscan-0.3.2b/kaspersky/Makefile 2003-02-20 15:41:32.000000000 +0100
++++ examples/VFS/samba-vscan-0.3.2b/kaspersky/Makefile 2003-04-09 20:21:37.000000000 +0200
+@@ -23,9 +23,9 @@
+ VFS_OBJS = vscan-kavp.so
+
+ ifdef USE_DEBUG
+-CFLAGS = -I$(SAMBA_SRC) -I$(SAMBA_INCL) -I$(UBIQX_SRC) -I$(SMBWR_SRC) -I$(SMBVS_INCL) -Wall -g -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -fPIC
++CFLAGS = -I$(SAMBA_SRC) -I$(SAMBA_INCL) -I$(UBIQX_SRC) -I$(SMBWR_SRC) -I$(SMBVS_INCL) -Wall -g -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -fPIC -I/usr/include/heimdal
+ else
+-CFLAGS = -I$(SAMBA_SRC) -I$(SAMBA_INCL) -I$(UBIQX_SRC) -I$(SMBWR_SRC) -I$(SMBVS_INCL) -Wall -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -fPIC
++CFLAGS = -I$(SAMBA_SRC) -I$(SAMBA_INCL) -I$(UBIQX_SRC) -I$(SMBWR_SRC) -I$(SMBVS_INCL) -Wall -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -fPIC -I/usr/include/heimdal
+ endif
+
+ ifndef USE_KAVPSHAREDLIB
+--- examples/VFS/samba-vscan-0.3.2b/mks/Makefile 2003-01-19 18:09:53.000000000 +0100
++++ examples/VFS/samba-vscan-0.3.2b/mks/Makefile 2003-04-09 20:21:37.000000000 +0200
+@@ -16,10 +16,10 @@
+ SMBWR_SRC = ../../../../source/smbwrapper
+ SMBVS_INCL = ../include
+ SMBVS_GLB = ../global
+-CFLAGS = -I$(SAMBA_SRC) -I$(SAMBA_INCL) -I$(UBIQX_SRC) -I$(SMBWR_SRC) -I$(SMBVS_INCL) -Wall -g -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -fPIC
++CFLAGS = -I$(SAMBA_SRC) -I$(SAMBA_INCL) -I$(UBIQX_SRC) -I$(SMBWR_SRC) -I$(SMBVS_INCL) -Wall -g -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -fPIC -I/usr/include/heimdal
+
+ ifdef USE_INCLMKSDLIB
+-CFLAGS=-I$(SAMBA_SRC) -I$(SAMBA_INCL) -I$(UBIQX_SRC) -I$(SMBWR_SRC) -I$(SMBVS_INCL) -Wall -g -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_USE_INCL_MKSD_LIB=1 -fPIC
++CFLAGS=-I$(SAMBA_SRC) -I$(SAMBA_INCL) -I$(UBIQX_SRC) -I$(SMBWR_SRC) -I$(SMBVS_INCL) -Wall -g -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_USE_INCL_MKSD_LIB=1 -fPIC -I/usr/include/heimdal
+ endif
+
+ VFS_OBJS = vscan-mksd.so
+--- examples/VFS/samba-vscan-0.3.2b/mks/vscan-mksd.c 2003-02-21 21:37:50.000000000 +0100
++++ examples/VFS/samba-vscan-0.3.2b/mks/vscan-mksd.c 2003-04-09 20:38:16.000000000 +0200
+@@ -393,14 +393,14 @@
+ rc = vscan_do_infected_file_action(&default_vfs_ops, conn, filepath, quarantine_dir, quarantine_prefix, infected_file_action);
+
+ /* add/update file. mark file as infected! */
+- lrufiles_add(filepath, stat_buf.st_mtime, TRUE);
++ lrufiles_add(filepath, stat_buf.st_mtime, True);
+
+ /* virus found, deny acces */
+ errno = EACCES;
+ return -1;
+ } else if ( retval == 0 ) {
+ /* file is clean, add to lrufiles */
+- lrufiles_add(filepath, stat_buf.st_mtime, FALSE);
++ lrufiles_add(filepath, stat_buf.st_mtime, False);
+ }
+ }
+
+--- examples/VFS/samba-vscan-0.3.2b/openantivirus/Makefile 2003-01-30 00:53:08.000000000 +0100
++++ examples/VFS/samba-vscan-0.3.2b/openantivirus/Makefile 2003-04-09 20:21:37.000000000 +0200
+@@ -15,7 +15,7 @@
+ SMBWR_SRC = ../../../../source/smbwrapper
+ SMBVS_INCL = ../include
+ SMBVS_GLB = ../global
+-CFLAGS = -I$(SAMBA_SRC) -I$(SAMBA_INCL) -I$(UBIQX_SRC) -I$(SMBWR_SRC) -I$(SMBVS_INCL) -Wall -g -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -fPIC
++CFLAGS = -I$(SAMBA_SRC) -I$(SAMBA_INCL) -I$(UBIQX_SRC) -I$(SMBWR_SRC) -I$(SMBVS_INCL) -Wall -g -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -fPIC -I/usr/include/heimdal
+ VFS_OBJS = vscan-oav.so
+ SOURCES = $(SMBVS_GLB)/vscan-functions.c $(SMBVS_GLB)/vscan-message.c $(SMBVS_GLB)/vscan-quarantine.c $(SMBVS_GLB)/vscan-fileaccesslog.c vscan-oav.c vscan-oav_core.c vscan-oav.h vscan-oav_core.h
+ OBJS = vscan-functions.lo vscan-message.lo vscan-quarantine.lo vscan-fileaccesslog.lo vscan-oav.lo vscan-oav_core.lo
+--- examples/VFS/samba-vscan-0.3.2b/openantivirus/vscan-oav.c 2003-02-21 21:37:51.000000000 +0100
++++ examples/VFS/samba-vscan-0.3.2b/openantivirus/vscan-oav.c 2003-04-09 20:40:53.000000000 +0200
+@@ -417,14 +417,14 @@
+ rc = vscan_do_infected_file_action(&default_vfs_ops, conn, filepath, quarantine_dir, quarantine_prefix, infected_file_action);
+
+ /* add/update file. mark file as infected! */
+- lrufiles_add(filepath, stat_buf.st_mtime, TRUE);
++ lrufiles_add(filepath, stat_buf.st_mtime, True);
+
+ /* virus found, deny acces */
+ errno = EACCES;
+ return -1;
+ } else if ( retval == 0 ) {
+ /* file is clean, add to lrufiles */
+- lrufiles_add(filepath, stat_buf.st_mtime, FALSE);
++ lrufiles_add(filepath, stat_buf.st_mtime, False);
+ }
+ }
+
+--- examples/VFS/samba-vscan-0.3.2b/sophos/Makefile 2003-01-30 00:53:08.000000000 +0100
++++ examples/VFS/samba-vscan-0.3.2b/sophos/Makefile 2003-04-09 20:21:37.000000000 +0200
+@@ -15,7 +15,7 @@
+ SMBWR_SRC = ../../../../source/smbwrapper
+ SMBVS_INCL = ../include
+ SMBVS_GLB = ../global
+-CFLAGS = -I$(SAMBA_SRC) -I$(SAMBA_INCL) -I$(UBIQX_SRC) -I$(SMBWR_SRC) -I$(SMBVS_INCL) -Wall -g -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -fPIC
++CFLAGS = -I$(SAMBA_SRC) -I$(SAMBA_INCL) -I$(UBIQX_SRC) -I$(SMBWR_SRC) -I$(SMBVS_INCL) -Wall -g -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -fPIC -I/usr/include/heimdal
+ VFS_OBJS = vscan-sophos.so
+ SOURCES = $(SMBVS_GLB)/vscan-functions.c $(SMBVS_GLB)/vscan-message.c $(SMBVS_GLB)/vscan-quarantine.c $(SMBVS_GLB)/vscan-fileaccesslog.c vscan-sophos.c vscan-sophos_core.c vscan-sophos.h vscan-sophos_core.h
+ OBJS = vscan-functions.lo vscan-message.lo vscan-quarantine.lo vscan-fileaccesslog.lo vscan-sophos.lo vscan-sophos_core.lo
+--- examples/VFS/samba-vscan-0.3.2b/sophos/vscan-sophos.c 2003-02-21 21:37:51.000000000 +0100
++++ examples/VFS/samba-vscan-0.3.2b/sophos/vscan-sophos.c 2003-04-09 20:43:11.000000000 +0200
+@@ -399,14 +399,14 @@
+ rc = vscan_do_infected_file_action(&default_vfs_ops, conn, filepath, quarantine_dir, quarantine_prefix, infected_file_action);
+
+ /* add/update file. mark file as infected! */
+- lrufiles_add(filepath, stat_buf.st_mtime, TRUE);
++ lrufiles_add(filepath, stat_buf.st_mtime, True);
+
+ /* deny access */
+ errno = EACCES;
+ return -1;
+ } else if ( retval == 0 ) {
+ /* file is clean, add to lrufiles */
+- lrufiles_add(filepath, stat_buf.st_mtime, FALSE);
++ lrufiles_add(filepath, stat_buf.st_mtime, False);
+ }
+ }
+
+--- examples/VFS/samba-vscan-0.3.2b/trend/Makefile 2003-01-30 01:03:38.000000000 +0100
++++ examples/VFS/samba-vscan-0.3.2b/trend/Makefile 2003-04-09 20:21:37.000000000 +0200
+@@ -15,7 +15,7 @@
+ SMBWR_SRC = ../../../../source/smbwrapper
+ SMBVS_INCL = ../include
+ SMBVS_GLB = ../global
+-CFLAGS = -I$(SAMBA_SRC) -I$(SAMBA_INCL) -I$(UBIQX_SRC) -I$(SMBWR_SRC) -I$(SMBVS_INCL) -Wall -g -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -fPIC
++CFLAGS = -I$(SAMBA_SRC) -I$(SAMBA_INCL) -I$(UBIQX_SRC) -I$(SMBWR_SRC) -I$(SMBVS_INCL) -Wall -g -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -fPIC -I/usr/include/heimdal
+ VFS_OBJS = vscan-trend.so
+ SOURCES = $(SMBVS_GLB)/vscan-functions.c $(SMBVS_GLB)/vscan-message.c $(SMBVS_GLB)/vscan-quarantine.c $(SMBVS_GLB)/vscan-fileaccesslog.c vscan-trend.c vscan-trend_core.c vscan-trend.h vscan-trend_core.h
+ OBJS = vscan-functions.lo vscan-message.lo vscan-quarantine.lo vscan-fileaccesslog.lo vscan-trend.lo vscan-trend_core.lo
+--- examples/VFS/samba-vscan-0.3.2b/trend/vscan-trend.c 2003-02-21 21:37:52.000000000 +0100
++++ examples/VFS/samba-vscan-0.3.2b/trend/vscan-trend.c 2003-04-09 20:46:07.000000000 +0200
+@@ -409,14 +409,14 @@
+ rc = vscan_do_infected_file_action(&default_vfs_ops, conn, filepath, quarantine_dir, quarantine_prefix, infected_file_action);
+
+ /* add/update file. mark file as infected! */
+- lrufiles_add(filepath, stat_buf.st_mtime, TRUE);
++ lrufiles_add(filepath, stat_buf.st_mtime, True);
+
+ /* deny access */
+ errno = EACCES;
+ return -1;
+ } else if ( retval == 0 ) {
+ /* file is clean, add to lrufiles */
+- lrufiles_add(filepath, stat_buf.st_mtime, FALSE);
++ lrufiles_add(filepath, stat_buf.st_mtime, False);
+ }
+ }
+
diff --git a/packaging/SuSE/samba3.spec b/packaging/SuSE/samba3.spec
new file mode 100644
index 00000000000..37d8a4d36f8
--- /dev/null
+++ b/packaging/SuSE/samba3.spec
@@ -0,0 +1,766 @@
+#
+# spec file for package samba3 (Version 3.0.0rc1cvs)
+#
+# Copyright (c) 2003 SuSE Linux AG, Nuernberg, Germany.
+# This file and all modifications and additions to the pristine
+# package are under the same license as the package itself.
+#
+# Please submit bugfixes or comments via http://www.suse.de/feedback/
+#
+# Note: The Samba3 tarball should be called: samba3-3.0.0.tar.bz2
+#
+
+# neededforbuild XFree86-libs autoconf automake cups-devel cups-libs dialog docbook-utils docbook-xsl-stylesheets docbook_4 ed freetype2 ghostscript-fonts-std ghostscript-library ghostscript-x11 glib heimdal heimdal-devel heimdal-lib iso_ent libacl libacl-devel libattr libattr-devel libgimpprint libpng libtiff libxml2 libxml2-devel libxslt mysql-devel mysql-shared openldap2 openldap2-client openldap2-devel openssl openssl-devel popt popt-devel python python-devel readline readline-devel te_etex te_latex te_pdf tetex xmlcharent
+# usedforbuild aaa_base acl attr bash bind9-utils bison coreutils cpio cpp cvs cyrus-sasl2 db devs diffutils e2fsprogs file filesystem fillup findutils flex gawk gdbm-devel glibc glibc-devel glibc-locale gpm grep groff gzip info insserv kbd less libacl libattr libgcc libstdc++ libxcrypt m4 make man mktemp modutils ncurses ncurses-devel net-tools netcfg pam pam-devel pam-modules patch permissions ps rcs readline sed sendmail shadow strace syslogd sysvinit tar texinfo timezone unzip util-linux vim zlib zlib-devel XFree86-libs autoconf automake binutils bzip2 cracklib cups-devel cups-libs dialog docbook-utils docbook-xsl-stylesheets docbook_4 ed freetype2 gcc gdbm gettext ghostscript-fonts-std ghostscript-library ghostscript-x11 glib heimdal heimdal-devel heimdal-lib iso_ent libacl-devel libattr-devel libgimpprint libpng libtiff libtool libxml2 libxml2-devel libxslt mysql-devel mysql-shared openldap2 openldap2-client openldap2-devel openssl openssl-devel perl popt popt-devel python python-devel readline-devel rpm te_ams te_etex te_latex te_pdf tetex xmlcharent
+
+Name: samba3
+Vendor: Samba Team
+License: GPL
+Group: Productivity/Networking/Samba
+Url: http://www.samba.org
+Provides: samba smbfs samba3
+Requires: samba3-client
+Obsoletes: samba-classic samba-ldap
+Autoreqprov: on
+%define krb_heimdal_05 0
+%define new_heimdal /opt/heimdal
+%define new_sasl /opt/sasl
+%define new_openldap /opt/openldap
+%define new_glibc 0
+Version: 3.0.0
+Release: %(date +%%j)
+%define head 0
+%define samba_ver 3.0.0
+%define samba_release 0
+%define ul_version 0
+%define suse_ver 820
+%define python_ver python2.2
+%if %{suse_ver} > 810
+%define new_glibc 1
+%endif
+%if %{suse_ver} > 821
+%define python_ver python2.3
+%endif
+%define make_cifsvfs 1
+%define make_devel 0
+%define make_doc 0
+%define make_python 1
+%define make_shared_mod 0
+%define make_smbwrap 1
+# vscan has not yet updated to the new vfs-api
+%define make_vscan 0
+%define make_wrepld 1
+%define use_keytab 0
+Summary: samba3
+Source: %{name}-%{version}.tar.bz2
+Source10: %{name}-%{version}.files.tar.bz2
+Source50: samba-vscan-%{vscan_ver}.tar.bz2
+Patch1: %{name}-%{version}-Makefiles-heimdal.diff
+Patch2: samba-mutual-auth.diff
+Patch29: %{name}-com_err.diff
+Patch30: %{name}-%{version}-heimdal-06.diff
+Patch31: %{name}-%{version}-pdb.diff
+Patch32: %{name}-net_ads_password.diff
+Patch33: %{name}-Makefile.diff
+Patch34: %{name}-smbwrapper.diff
+Patch51: %{name}-vscan.diff
+BuildRoot: %{_tmppath}/%{name}-%{version}-build
+%define DOCDIR %{_defaultdocdir}/%{name}
+%define DOCBOOKDIR %{_defaultdocdir}/%{name}/docbook
+%define SWATDIR %{_datadir}/samba/swat
+%define vscan_ver 0.3.2b
+%define vscan_modules fprot icap mks openantivirus sophos trend
+#not pdb_nisplussam
+%define pdb_modules pdb_xml,pdb_mysql,pdb_ldap,pdb_smbpasswd,pdb_tdbsam,pdb_unix,pdb_guest,pdb_nisplussam
+%define rpc_modules rpc_lsa,rpc_samr,rpc_reg,rpc_wks,rpc_net,rpc_dfs,rpc_srv,rpc_spoolss
+%define auth_modules auth_rhosts,auth_sam,auth_unix,auth_winbind,auth_server,auth_domain,auth_builtin
+%define vfs_modules vfs_recycle,vfs_audit,vfs_extd_audit,vfs_netatalk,vfs_fake_perms
+%define idmap_modules idmap_winbind,idmap_ldap,idmap_tdb
+%define charset_modules charset_weird
+%package client
+Summary: samba3-client
+Autoreqprov: on
+Requires: cups-libs
+Obsoletes: smbclnt samba-classic-client samba-ldap-client
+Provides: samba-client samba3-client
+Group: Productivity/Networking/Samba
+%package winbind
+Requires: samba-client samba
+Summary: samba3-winbind
+Autoreqprov: on
+Group: Productivity/Networking/Samba
+%package utils
+Summary: samba3-utils
+Autoreqprov: on
+Group: Productivity/Networking/Samba
+%package doc
+Summary: samba3-doc
+Autoreqprov: on
+Group: Productivity/Networking/Samba
+%package docbook
+Summary: samba3-docbook
+Autoreqprov: on
+Group: Productivity/Networking/Samba
+%package pdb
+Summary: samba3-pdb
+Autoreqprov: on
+Group: Productivity/Networking/Samba
+%if %{make_cifsvfs}
+%package cifsmount
+Summary: samba3-cifsmount
+Autoreqprov: on
+Group: Productivity/Networking/Samba
+Url: http://us1.samba.org/samba/Linux_CIFS_client.html
+%endif
+%if %{make_vscan}
+%package vscan
+Summary: samba3-vscan
+Autoreqprov: on
+Group: Productivity/Networking/Samba
+Version: 0.3.2a
+Release: 0
+Url: http://www.openantivirus.org/
+%endif
+%if %{make_wrepld}
+%package wrepld
+Summary: samba3-wrepld
+Autoreqprov: on
+Group: Productivity/Networking/Samba
+%endif
+%if %{make_python}
+%package python
+Summary: samba3-python
+Autoreqprov: on
+Group: Productivity/Networking/Samba
+%endif
+%package -n libsmbclient
+Summary: Samba client library
+Autoreqprov: on
+Group: System/Libraries
+%package -n libsmbclient-devel
+Summary: Libraries and header files to develop programs with smbclient support
+Autoreqprov: on
+Group: Development/Libraries/C and C++
+%prep
+[ $RPM_BUILD_ROOT = "/" ] && (echo "your buildroot is /" && exit 0) || rm -rf $RPM_BUILD_ROOT
+mkdir $RPM_BUILD_ROOT
+%setup -n %{name}-%{samba_ver}
+%setup -T -D -a 50
+cp -ar samba-vscan-%{vscan_ver} examples/VFS/
+# untar my configs
+%setup -T -D -a 10
+###########
+### PATCHES
+###########
+# Makefiles-heimdal.diff
+%patch1
+%if %{use_keytab}
+# luke howards keytab-patch
+%patch2
+%endif
+# some com_err fixes
+%patch29
+%if %{suse_ver} > 821
+%patch30
+%endif
+# vscan patch
+%patch51
+# net ads password
+%patch32
+# temp Makefile (show more libs)
+%patch33
+# temp pdb-test.c
+%patch31
+# smbwrapper should use LIBDIR not BINDIR
+%patch34
+#find . -name CVS -print | xargs rm -rf
+#find . -name ".cvsignore" -print | xargs rm -rf
+find . -name "*.gd" -print | xargs rm -rvf
+find . -name "*.orig" -print | xargs rm -rvf
+%if %{ul_version} >= 1
+ echo '#define VERSION "%samba_ver-UL"' > source/include/version.h
+%else
+ echo '#define VERSION "%samba_ver-SuSE"' > source/include/version.h
+%endif
+
+%build %{name}-%{samba_ver}
+%{?suse_update_config:%{suse_update_config -f}}
+cd source
+./autogen.sh
+export CFLAGS="$RPM_OPT_FLAGS -Wall -O -D_GNU_SOURCE -D_LARGEFILE64_SOURCE"
+# debugging symbols
+%if %{make_devel}
+export CFLAGS="$RPM_OPT_FLAGS -g -Wall -O -D_GNU_SOURCE -D_LARGEFILE64_SOURCE"
+%endif
+%if %{krb_heimdal_05}
+export CFLAGS="$CFLAGS -I./include -I%{new_heimdal}/include "
+export CFLAGS="$CFLAGS -I%{new_openldap}/include "
+export CFLAGS="$CFLAGS -I%{new_sasl}/include "
+export LDFLAGS="$LDFLAGS -Wl,-rpath %{new_heimdal}/lib"
+export LDFLAGS="$LDFLAGS -Wl,-rpath %{new_openldap}/lib"
+export LDFLAGS="$LDFLAGS -Wl,-rpath %{new_sasl}/lib"
+%endif
+%ifarch ppc64
+export CFLAGS="$CFLAGS -mminimal-toc"
+%endif
+CONF_OPTS="\
+ --enable-cups \
+ --libdir=/usr/lib/samba \
+ --localstatedir=/var/lib/samba \
+ --mandir=%{_mandir} \
+ --prefix=/usr \
+ --sbindir=/usr/sbin \
+ --sysconfdir=/etc/samba \
+ --with-acl-support \
+ --with-automount \
+ --with-configdir=/etc/samba \
+ --with-lockdir=/var/lib/samba \
+ --with-logfilebase=/var/log/samba \
+ --with-msdfs \
+ --with-pam \
+ --with-pam_smbpass \
+ --with-piddir=/var/run/samba \
+ --with-privatedir=/etc/samba \
+ --with-quotas \
+ --with-smbmount \
+ --with-swatdir=/usr/share/samba/swat \
+ --with-syslog \
+ --with-utmp \
+ --with-vfs \
+ --with-winbind \
+ --with-tdbsam \
+ --with-expsam=xml,mysql \
+ --with-profiling-data \
+%if %{use_keytab}
+ --enable-keytab \
+%endif
+%if %{make_smbwrap}
+ --with-smbwrapper \
+%endif
+%if %{make_python}
+ --with-python=%{python_ver} \
+%endif
+%if %{make_shared_mod}
+ --with-shared-modules=%{pdb_modules},%{rpc_modules} \
+%endif
+%if %{make_devel}
+ --enable-developer \
+ --enable-krb5developer \
+%endif
+"
+# --with-nisplus-home \
+# make sure we have a chance to find the krb5-config-tool
+export PATH="$PATH:/usr/lib/heimdal/bin"
+./configure $CONF_OPTS
+make \
+ all \
+ torture \
+ nsswitch/libnss_wins.so \
+ debug2html \
+ libsmbclient \
+ everything \
+ bin/editreg
+# everything = nsswitch smbwrapper smbtorture debug2html smbfilter nsswitch/libnss_wins.so
+make modules
+make -C tdb tdbdump tdbtest tdbtool tdbtorture
+make talloctort
+%if %{make_wrepld}
+make bin/wrepld
+%endif
+%if %{make_doc}
+pushd `pwd`
+cd ../docs/docbook
+autoconf -f
+./configure
+# gracefully ignore errors...
+make -i manpages html html-single pdf htmlfaq htmlman
+# ps is not necessary, txt neither
+# everything = manpages ps pdf html-single html htmlman txt htmlfaq
+popd
+%endif
+# make examples in VFS,PDB
+pushd `pwd`
+cd ../examples/VFS/
+sh -x autogen.sh
+./configure
+popd
+EXAMPLEDIRS="pdb VFS"
+for i in $EXAMPLEDIRS; do make -C ../examples/$i; done
+%if %{make_vscan}
+export USE_KAVPSHAREDLIB=0
+export USE_INCLMKSDLIB=1
+for module in %{vscan_modules}; do
+ make -C ../examples/VFS/samba-vscan-%{vscan_ver}/${module};
+done
+%endif
+%if %{make_python}
+make python_ext
+%endif
+%if %{make_cifsvfs}
+cd client
+export CFLAGS="$RPM_OPT_FLAGS -Wall -O -D_GNU_SOURCE -D_LARGEFILE64_SOURCE"
+gcc mount.cifs.c -o mount.cifs
+cd ..
+%endif
+
+%install
+mkdir -p \
+ $RPM_BUILD_ROOT/%{DOCDIR} \
+ $RPM_BUILD_ROOT/%{DOCDIR}-vscan \
+ $RPM_BUILD_ROOT/%{DOCDIR}/docbook \
+ $RPM_BUILD_ROOT/etc/{pam.d,init.d,samba} \
+ $RPM_BUILD_ROOT/lib/security \
+ $RPM_BUILD_ROOT/sbin \
+ $RPM_BUILD_ROOT/usr/include \
+ $RPM_BUILD_ROOT/usr/lib/%{python_ver}/lib-dynload \
+ $RPM_BUILD_ROOT/usr/lib/samba/{vfs,pdb,vscan,rpc,auth,charset,idmap} \
+ $RPM_BUILD_ROOT/usr/share/{man,samba/swat} \
+ $RPM_BUILD_ROOT/usr/{bin,sbin} \
+ $RPM_BUILD_ROOT/var/adm \
+ $RPM_BUILD_ROOT/var/lib/samba/{netlogon,drivers/{W32X86,WIN40,W32ALPHA,W32MIPS,W32PPC},profiles} \
+ $RPM_BUILD_ROOT/var/log/samba \
+ $RPM_BUILD_ROOT/var/run/samba \
+ $RPM_BUILD_ROOT/var/spool/samba
+cd source/
+make install \
+ LIBDIR=$RPM_BUILD_ROOT/usr/lib/samba \
+ LOGFILEBASE=$RPM_BUILD_ROOT/var/log/samba \
+ CONFIGFILE=$RPM_BUILD_ROOT/etc/samba/smb.conf \
+ LMHOSTSFILE=$RPM_BUILD_ROOT/etc/samba/lmhosts \
+ SWATDIR=$RPM_BUILD_ROOT/usr/share/samba/swat \
+ SBINDIR=$RPM_BUILD_ROOT/usr/sbin \
+ LOCKDIR=$RPM_BUILD_ROOT/var/lib/samba \
+ DRIVERFILE=$RPM_BUILD_ROOT/etc/samba/printers.def \
+ BINDIR=$RPM_BUILD_ROOT/usr/bin \
+ SMB_PASSWD_FILE=$RPM_BUILD_ROOT/etc/samba/smbpasswd \
+ MANDIR=$RPM_BUILD_ROOT/usr/share/man
+make installmodules \
+ LIBDIR=$RPM_BUILD_ROOT/usr/lib/samba
+cd ..
+# utility scripts
+%if %{head}
+scripts="creategroup cvslog.pl scancvslog.pl"
+%else
+scripts="scancvslog.pl"
+%endif
+mkdir -p examples/scripts
+for i in $scripts; do
+ cp -a source/script/$i examples/scripts/
+done
+# configuration files
+%if %{ul_version} >= 1
+ SUFFIX="UnitedLinux"
+%else
+ SUFFIX="SuSE"
+%endif
+cat smb.conf.vendor | egrep -v '(^$$|^#)' > smb.conf
+mv smb.conf.vendor examples/smb.conf.${SUFFIX}
+install -m 644 smb.conf* $RPM_BUILD_ROOT/etc/samba/
+install -m 644 lmhosts $RPM_BUILD_ROOT/etc/samba/
+install -m 644 smbusers $RPM_BUILD_ROOT/etc/samba/
+install -m 600 smbpasswd -o root -g root $RPM_BUILD_ROOT/etc/samba/
+install -m 600 smbfstab -o root -g root $RPM_BUILD_ROOT/etc/samba/
+# pam
+install -m 644 samba.pamd $RPM_BUILD_ROOT/etc/pam.d/samba
+# sambamount
+ln -sf /usr/bin/smbmount $RPM_BUILD_ROOT/sbin/mount.smbfs
+#cifsmount
+%if %{make_cifsvfs}
+install -m755 source/client/mount.cifs $RPM_BUILD_ROOT/sbin
+%endif
+# start scripts
+install rc.smb $RPM_BUILD_ROOT/etc/init.d/smb
+ln -sf ../../etc/init.d/smb $RPM_BUILD_ROOT/usr/sbin/rcsmb
+install rc.nmb $RPM_BUILD_ROOT/etc/init.d/nmb
+ln -sf ../../etc/init.d/nmb $RPM_BUILD_ROOT/usr/sbin/rcnmb
+install rc.smbfs $RPM_BUILD_ROOT/etc/init.d/smbfs
+ln -sf ../../etc/init.d/smbfs $RPM_BUILD_ROOT/usr/sbin/rcsmbfs
+install rc.winbind $RPM_BUILD_ROOT/etc/init.d/winbind
+ln -sf ../../etc/init.d/winbind $RPM_BUILD_ROOT/usr/sbin/rcwinbind
+%if %{make_wrepld}
+install rc.wrepl $RPM_BUILD_ROOT/etc/init.d/wrepl
+ln -sf ../../etc/init.d/wrepl $RPM_BUILD_ROOT/usr/sbin/rcwrepl
+cp -a source/bin/wrepld $RPM_BUILD_ROOT/usr/sbin/
+%endif
+# libnss_wins.so
+cp source/nsswitch/libnss_wins.so $RPM_BUILD_ROOT/lib/libnss_wins.so.2
+ln -sf /lib/libnss_wins.so.2 $RPM_BUILD_ROOT/lib/libnss_wins.so
+# winbind stuff
+cp -a source/nsswitch/pam_winbind.so $RPM_BUILD_ROOT/lib/security/
+cp -a source/nsswitch/libnss_winbind.so $RPM_BUILD_ROOT/lib/libnss_winbind.so.2
+cp -a source/bin/winbindd $RPM_BUILD_ROOT/usr/sbin/
+ln -s /lib/libnss_winbind.so.2 $RPM_BUILD_ROOT/lib/libnss_winbind.so
+# pam_smbpass
+cp -a source/bin/pam_smbpass.so $RPM_BUILD_ROOT/lib/security/
+# smbfilter
+cp -a source/bin/smbfilter $RPM_BUILD_ROOT/usr/bin/
+# editreg
+cp -a source/bin/editreg $RPM_BUILD_ROOT/usr/bin/
+# install libsmbclient
+install -m0755 source/bin/libsmbclient.a $RPM_BUILD_ROOT/%{_libdir}
+install -m0755 source/bin/libsmbclient.so $RPM_BUILD_ROOT/%{_libdir}/libsmbclient.so.0
+ln -s /usr/lib/libsmbclient.so.0 $RPM_BUILD_ROOT/%{_libdir}/libsmbclient.so
+install -m0644 source/include/libsmbclient.h $RPM_BUILD_ROOT/%{_includedir}
+# install nsswitch-headers (for squid, etc.)
+mkdir -p $RPM_BUILD_ROOT/%{_includedir}/samba/nsswitch
+cp source/nsswitch/*.h $RPM_BUILD_ROOT/%{_includedir}/samba/nsswitch/
+# install smbtorture and other test-programs
+install -m0755 source/bin/smbtorture $RPM_BUILD_ROOT/usr/bin/
+install -m0755 source/bin/talloctort $RPM_BUILD_ROOT/usr/bin/
+install -m0755 source/bin/{msgtest,masktest,locktest*} $RPM_BUILD_ROOT/usr/bin/
+install -m0755 source/bin/{vfstest,nsstest} $RPM_BUILD_ROOT/usr/bin/
+# install tdb tools
+install -m0755 source/tdb/{tdbdump,tdbtest,tdbtool,tdbtorture} $RPM_BUILD_ROOT/usr/bin/
+# install VFS-modules
+install -m0755 examples/VFS/*.so $RPM_BUILD_ROOT/%{_libdir}/samba/vfs/
+# install PDB-modules
+install -m0755 examples/pdb/pdb_test.so $RPM_BUILD_ROOT/%{_libdir}/samba/pdb/
+%if %{make_vscan}
+# install VSCAN-vfs-modules
+install -m0755 examples/VFS/samba-vscan-%{vscan_ver}/*/*.so $RPM_BUILD_ROOT/%{_libdir}/samba/vscan/
+%endif
+# make examples clean
+VFS="$RPM_BUILD_DIR/%{name}-%{samba_ver}/examples/VFS"
+VSCAN="$VFS/samba-vscan-%{vscan_ver}"
+PDB="$RPM_BUILD_DIR/%{name}-%{samba_ver}/examples/pdb"
+dirs="$PDB $SAM $VFS"
+(for i in $dirs; do make -C $i clean; done)
+%if %{make_vscan}
+(for i in %{vscan_modules}; do make -C $VSCAN/$i clean; done)
+%endif
+# install python
+%if %{make_python}
+cp -a source/build/lib.*/samba $RPM_BUILD_ROOT/usr/lib/%{python_ver}/lib-dynload/
+find source/python -name CVS -print | xargs rm -rf
+find source/python -name ".cvsignore" -print | xargs rm -rf
+%endif
+# whats this ?
+install -m0755 source/bin/debug2html $RPM_BUILD_ROOT/usr/bin/
+# install smbwrapper
+%if %{make_smbwrap}
+install -m0755 source/bin/smbwrapper.so $RPM_BUILD_ROOT/%{_libdir}/samba/
+install -m0755 source/bin/smbsh $RPM_BUILD_ROOT/usr/bin/
+%endif
+##############
+# cleanup docs
+##############
+#chmod 644 `find docs examples -type f`
+#chmod 755 `find docs examples -type d`
+#find . -name CVS -print | xargs rm -rf
+#find . -name ".cvsignore" -print | xargs rm -rf
+mv COPYING Manifest README Read-Manifest-Now Roadmap WHATSNEW.txt $RPM_BUILD_ROOT/%{DOCDIR}/
+cp source/msdfs/README $RPM_BUILD_ROOT/%{DOCDIR}/README.msdfs
+cp source/smbwrapper/README $RPM_BUILD_ROOT/%{DOCDIR}/README.smbwrapper
+%if %{ul_version} >= 1
+ SUFFIX="UnitedLinux"
+%else
+ SUFFIX="SuSE"
+%endif
+cp README.vendor ${RPM_BUILD_ROOT}/%{DOCDIR}/README.${SUFFIX}
+# pam_smbpass is missing
+cp -a source/pam_smbpass/samples examples/pam_smbpass/
+cp -a source/pam_smbpass/{CHANGELOG,INSTALL,README,TODO} examples/pam_smbpass/
+# prepare docbook package
+cp -a docs/docbook/* $RPM_BUILD_ROOT/%{DOCBOOKDIR}
+#make -C $RPM_BUILD_ROOT/%{DOCBOOKDIR} clean
+rm -rf $RPM_BUILD_ROOT/%{DOCBOOKDIR}/autom4te.cache
+rm -rf $RPM_BUILD_ROOT/%{DOCBOOKDIR}/config.*
+# this is empty
+rm -rf docs/yodldocs
+rm -rf examples/VFS/samba-vscan-%{vscan_ver}
+# zip manpages at least
+gzip -f docs/manpages/*.[1-9]
+cp -a docs/* $RPM_BUILD_ROOT/%{DOCDIR}
+cp -a examples/ $RPM_BUILD_ROOT/%{DOCDIR}
+# save space...
+rm -r $RPM_BUILD_ROOT/%{SWATDIR}/using_samba
+ln -s %{DOCDIR}/htmldocs/using_samba $RPM_BUILD_ROOT/%{SWATDIR}
+# hm...
+cp $RPM_BUILD_ROOT/%{SWATDIR}/help/welcome.html $RPM_BUILD_ROOT/%{DOCDIR}/htmldocs/
+rm -r $RPM_BUILD_ROOT/%{SWATDIR}/help
+ln -s %{DOCDIR}/htmldocs $RPM_BUILD_ROOT/%{SWATDIR}/help
+# remove cvs
+find $RPM_BUILD_ROOT/%{DOCDIR} -name CVS -print | xargs rm -rf
+find $RPM_BUILD_ROOT/%{DOCDIR} -name ".cvsignore" -print | xargs rm -rf
+# finally build a file-list
+for file in $( find ${RPM_BUILD_ROOT}%{DOCDIR} -maxdepth 1); do
+ # exclude %{DOCDIR} and docbook
+ case "${file#${RPM_BUILD_ROOT}}" in
+ %{DOCDIR}|%{DOCDIR}/docbook) continue ;;
+ esac
+ echo "%doc ${file#${RPM_BUILD_ROOT}}" >> ${RPM_BUILD_DIR}/%{name}-%{samba_ver}/filelist-doc
+done
+
+%post
+%{fillup_and_insserv smb}
+mkdir -p $RPM_BUILD_ROOT/var/adm/notify/messages
+cat << EOF > var/adm/notify/messages/samba-notify
+Achtung!
+This is %{name}-%{samba_ver}. Please do not run on production systems.
+You have been warned.
+EOF
+
+%post client
+%{fillup_and_insserv -fpy smbfs}
+%{fillup_only -ans samba client}
+
+%post winbind
+%{fillup_and_insserv winbind}
+
+%postun
+%{insserv_cleanup}
+
+%postun client
+%{insserv_cleanup}
+
+%postun winbind
+%{insserv_cleanup}
+
+%clean
+#make -C source realclean
+
+%files
+#/usr/bin/addtosmbpass
+#/usr/bin/convert_smbpasswd
+%dir /etc/samba
+%dir /usr/lib/samba
+%config /etc/init.d/nmb
+%config /etc/init.d/smb
+%config /etc/pam.d/samba
+%config(noreplace) /etc/samba/smbpasswd
+%config(noreplace) /etc/samba/smbusers
+%doc %{_mandir}/man1/smbcontrol.1.gz
+%doc %{_mandir}/man1/smbstatus.1.gz
+%doc %{_mandir}/man1/testparm.1.gz
+%doc %{_mandir}/man1/testprns.1.gz
+%doc %{_mandir}/man5/smbpasswd.5.gz
+%doc %{_mandir}/man7/samba.7.gz
+%doc %{_mandir}/man8/nmbd.8.gz
+%doc %{_mandir}/man8/pdbedit.8.gz
+%doc %{_mandir}/man8/smbd.8.gz
+%doc %{_mandir}/man8/smbpasswd.8.gz
+%doc %{_mandir}/man8/swat.8.gz
+%doc %{_mandir}/man8/tdbbackup.8.gz
+%{_includedir}/samba
+/lib/security/pam_smbpass.so
+/usr/bin/pdbedit
+/usr/bin/smbcontrol
+/usr/bin/smbpasswd
+/usr/bin/smbstatus
+/usr/bin/tdbbackup
+/usr/bin/tdbdump
+/usr/bin/tdbtest
+/usr/bin/tdbtool
+/usr/bin/testparm
+/usr/bin/testprns
+/usr/lib/samba/rpc
+/usr/lib/samba/vfs
+/usr/sbin/nmbd
+/usr/sbin/rcnmb
+/usr/sbin/rcsmb
+/usr/sbin/smbd
+/usr/sbin/swat
+/usr/share/samba
+/var/lib/samba
+/var/log/samba
+/var/run/samba
+/var/spool/samba
+
+%files client
+%config /etc/init.d/smbfs
+%config(noreplace) /etc/samba/lmhosts
+%config(noreplace) /etc/samba/smb.conf
+%config(noreplace) /etc/samba/smbfstab
+%dir /etc/samba
+%dir /usr/lib/samba
+%doc %{_mandir}/man1/editreg.1.gz
+%doc %{_mandir}/man1/findsmb.1.gz
+%doc %{_mandir}/man1/nmblookup.1.gz
+%doc %{_mandir}/man1/profiles.1.gz
+%doc %{_mandir}/man1/rpcclient.1.gz
+%doc %{_mandir}/man1/smbcacls.1.gz
+%doc %{_mandir}/man1/smbclient.1.gz
+%doc %{_mandir}/man1/smbcquotas.1.gz
+%doc %{_mandir}/man1/smbtar.1.gz
+%doc %{_mandir}/man1/smbtree.1.gz
+%doc %{_mandir}/man5/lmhosts.5.gz
+%doc %{_mandir}/man5/smb.conf.5.gz
+%doc %{_mandir}/man7/Samba.7.gz
+%doc %{_mandir}/man8/net.8.gz
+%doc %{_mandir}/man8/smbmnt.8.gz
+%doc %{_mandir}/man8/smbmount.8.gz
+%doc %{_mandir}/man8/smbspool.8.gz
+%doc %{_mandir}/man8/smbumount.8.gz
+/sbin/mount.smbfs
+/usr/bin/editreg
+/usr/bin/findsmb
+/usr/bin/net
+/usr/bin/nmblookup
+/usr/bin/profiles
+/usr/bin/rpcclient
+/usr/bin/smbcacls
+/usr/bin/smbclient
+/usr/bin/smbcquotas
+/usr/bin/smbfilter
+/usr/bin/smbmnt
+/usr/bin/smbmount
+/usr/bin/smbspool
+/usr/bin/smbtar
+/usr/bin/smbtree
+/usr/bin/smbumount
+/usr/lib/samba/lowcase.dat
+/usr/lib/samba/upcase.dat
+/usr/lib/samba/valid.dat
+/usr/sbin/rcsmbfs
+%if %{make_smbwrap}
+/usr/bin/smbsh
+%doc %{_mandir}/man1/smbsh.1.gz
+/usr/lib/samba/smbwrapper.so
+%endif
+
+%files winbind
+%config /etc/init.d/winbind
+%config(noreplace) /etc/samba/smb.conf
+%dir /etc/samba
+%doc %{_mandir}/man1/wbinfo.1.gz
+%doc %{_mandir}/man8/winbindd.8.gz
+%doc %{_mandir}/man1/ntlm_auth.1.gz
+/lib/libnss_winbind.so*
+/lib/libnss_wins.so*
+/lib/security/pam_winbind.so
+/usr/bin/ntlm_auth
+/usr/bin/wbinfo
+/usr/sbin/rcwinbind
+/usr/sbin/winbindd
+
+%files utils
+%doc %{_mandir}/man1/vfstest.1.gz
+/usr/bin/debug2html
+/usr/bin/locktest
+/usr/bin/locktest2
+/usr/bin/masktest
+/usr/bin/msgtest
+/usr/bin/nsstest
+/usr/bin/smbtorture
+/usr/bin/talloctort
+/usr/bin/tdbtorture
+/usr/bin/vfstest
+
+%files doc -f filelist-doc
+%dir /usr/share/doc/packages/samba3
+
+%files docbook
+%docdir %{DOCBOOKDIR}
+%{DOCBOOKDIR}
+%dir /usr/share/doc/packages/samba3
+
+%files pdb
+/usr/lib/samba/pdb
+%doc examples/pdb/{Makefile,README,pdb_test.c}
+%doc examples/pdb/{mysql/mysql.dump,mysql/smb.conf}
+%if %{make_cifsvfs}
+
+%files cifsmount
+/sbin/mount.cifs
+%endif
+%if %{make_wrepld}
+
+%files wrepld
+%config /etc/init.d/wrepl
+/usr/sbin/rcwrepl
+/usr/sbin/wrepld
+%endif
+%if %{make_vscan}
+
+%files vscan
+/usr/lib/samba/vscan
+%doc samba-vscan-%{vscan_ver}/{AUTHORS,COPYING,ChangeLog,FAQ,NEWS,README,TODO}
+%endif
+%if %{make_python}
+
+%files python
+%doc source/python/README
+%doc source/python/examples
+%doc source/python/gprinterdata
+%doc source/python/gtdbtool
+%doc source/python/gtkdictbrowser.py
+/usr/lib/%{python_ver}/lib-dynload/samba
+%endif
+
+%files -n libsmbclient
+%{_libdir}/libsmbclient.so.*
+
+%files -n libsmbclient-devel
+%{_includedir}/libsmbclient.h
+%{_libdir}/libsmbclient.a
+%{_libdir}/libsmbclient.so
+
+%description
+samba3
+
+
+%description client
+samba3-client
+
+
+%description winbind
+samba3-winbind
+
+
+%description utils
+samba3-utils
+
+
+%description doc
+samba3-doc
+
+
+%description docbook
+samba3-docbook
+
+
+%description pdb
+samba3-pdb
+
+%if %{make_cifsvfs}
+
+%description cifsmount
+samba3-cifsmount
+
+%endif
+%if %{make_vscan}
+
+%description vscan
+samba3-vscan
+
+%endif
+%if %{make_python}
+
+%description python
+samba3-python
+
+%endif
+%if %{make_wrepld}
+
+%description wrepld
+samba3-wrepld
+
+%endif
+
+%description -n libsmbclient
+This package includes the libsmbclient library.
+
+Authors:
+--------
+ The Samba Team <samba@samba.org>
+
+
+%description -n libsmbclient-devel
+This package contains static libraries and header files needed to develop
+programs which make use of the smbclient programming interface.
+
+Authors:
+--------
+ The Samba Team <samba@samba.org>
+
+
diff --git a/packaging/bin/update-pkginfo b/packaging/bin/update-pkginfo
new file mode 100755
index 00000000000..47f3c3c306b
--- /dev/null
+++ b/packaging/bin/update-pkginfo
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+VERSION=$1
+RELEASE=$2
+
+if [ $# -ne 2 ]; then
+ echo Usage: update-pkginfo VERSION RELEASE
+ exit 1
+fi
+
+for f in `du -a | awk '{print $2}' | grep \.tmpl$`; do
+ f2=`echo $f | sed s/.tmpl//g`
+ echo $f2
+ sed -e s/PVERSION/$VERSION/g -e s/PRELEASE/$RELEASE/g < $f > $f2
+done
+
diff --git a/pcp/Install b/pcp/Install
new file mode 100755
index 00000000000..c2087fc01e3
--- /dev/null
+++ b/pcp/Install
@@ -0,0 +1,64 @@
+#! /bin/sh
+#
+# Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write the Free Software Foundation, Inc., 59
+# Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+# Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+# Mountain View, CA 94043, or:
+#
+# http://www.sgi.com
+#
+# For further information regarding this notice, see:
+#
+# http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+#
+# Install the samba PMDA and/or PMNS
+#
+
+# source the PCP configuration environment variables
+. /etc/pcp.env
+
+# Get the common procedures and variable assignments
+#
+. $PCP_SHARE_DIR/lib/pmdaproc.sh
+
+# The name of the PMDA
+#
+iam=samba
+
+# override interactive dialog from pmdaproc.sh
+#
+__choose_mode()
+{
+ echo "Installing the \"$iam\" Performance Metrics Domain Agent (PMDA) ..."
+ echo
+}
+
+# Using libpcp_pmda.so.2 and PMDA_INTERFACE_2
+#
+pmda_interface=2
+
+# Do it
+#
+pmdaSetup
+pmdaInstall
+
+exit 0
diff --git a/pcp/Makefile b/pcp/Makefile
new file mode 100644
index 00000000000..e4b5fb75064
--- /dev/null
+++ b/pcp/Makefile
@@ -0,0 +1,74 @@
+#!make
+#
+# Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write the Free Software Foundation, Inc., 59
+# Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+# Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+# Mountain View, CA 94043, or:
+#
+# http://www.sgi.com
+#
+# For further information regarding this notice, see:
+#
+# http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+#
+
+SHELL = sh
+
+include /etc/pcp.conf
+
+IAM = samba
+CFILES = $(IAM).c
+
+LIBTARGET = pmda_$(IAM).so
+CMDTARGET = pmda$(IAM)
+TARGETS = $(LIBTARGET) $(CMDTARGET)
+
+DEBUG = -DDEBUG
+CFLAGS = $(DEBUG)
+LDOPTS =
+LDLIBS = -lpcp_pmda -lpcp
+DSOOPTS = -shared
+LDIRT = profile.h metrics.h so_locations *.log help.dir help.pag *.pmda_$(IAM).so
+
+PROFILEHEADER = ../source/include/smbprofile.h
+
+INSTALL = install
+CC = cc
+
+default: $(TARGETS)
+
+install: default
+
+$(CMDTARGET): profile.h metrics.h $(CFILES)
+ $(CC) $(CFLAGS) $(CFILES) $(LDOPTS) $(LDLIBS) -o $@
+
+$(LIBTARGET): profile.h metrics.h $(CFILES)
+ $(CC) $(CFLAGS) $(DSOOPTS) $(LDOPTS) $(CFILES) $(LDLIBS) -o $@
+
+metrics.h: profile.h mkheader.pl
+ ./mkheader.pl
+
+profile.h: $(PROFILEHEADER)
+ ln -s $(PROFILEHEADER) $@
+
+clobber clean:
+ rm -f $(LDIRT) $(TARGETS)
diff --git a/pcp/README b/pcp/README
new file mode 100644
index 00000000000..97d8125a53e
--- /dev/null
+++ b/pcp/README
@@ -0,0 +1,94 @@
+samba PMDA
+===========
+
+This PMDA is a sample that illustrates how a simple samba monitor
+PMDA might be constructed, using a shared memory segment to transfer
+information about transaction activity from the smb daemon.
+
+Note:
+ This PMDA may be remade from source and hence requires
+ a C compiler and Perl to be installed.
+
+ Uses of make(1) may fail (without removing or clobbering files)
+ if the C compiler cannot be found. This is most likely to
+ happen when running the PMDA ./Install script.
+
+ The only remedial action is to install the C compiler, or
+ hand-craft changes to the Makefile.
+
+Metrics
+=======
+
+The file ./help contains descriptions for all of the metrics exported
+by this PMDA.
+
+Once the PMDA has been installed, the following command will list all
+the available metrics and their explanatory "help" text:
+
+ $ pminfo -fT samba
+
+Installation
+============
+
+ + # mkdir /var/pcp/pmdas/samba
+ + # cp * /var/pcp/pmdas/samba
+ + # cp ../source/include/profile.h /var/pcp/pmdas/samba
+ + # cd /var/pcp/pmdas/samba
+
+ + Check that there is no clash in the Performance Metrics Domain
+ defined in ./domain.h and the other PMDAs currently in use
+ (/etc/pmcd.conf). If there is, edit ./domain.h to choose another
+ domain number.
+
+ + If you are not installing on an IRIX system, edit samba.c and
+ comment out the
+
+ #define IRIX 1
+
+ + Then simply use
+
+ # ./Install
+
+ + Alternatively, to install just the Performance Metrics Name Space
+ for the samba metrics on the local system, but not the samba PMDA
+ (presumably because the local system is running PCP 1.x and you
+ wish to connect to a remote system where PCP 2.0 and the samba PMDA
+ is running), make sure the Performance Metrics Domain defined in
+ ./domain.h matches the domain chosen for the samba PMDA on the
+ remote system (check the second field in the corresponding line of
+ the pmcd.conf file on the remote system - located in /etc on IRIX
+ and /var/pcp/config/pmcd on Linux), then
+
+ # ./Install -N
+
+De-installation
+===============
+
+ + Simply use
+
+ # cd /var/pcp/pmdas/samba
+ # ./Remove
+
+ + If you also want to remove the sources use
+
+ # cd /
+ # rm -rf /var/pcp/pmdas/samba
+
+Making something happen
+=======================
+
+The application "smbd" updates the shared memory segment to add
+profile information about smbd. By default updating is disabled.
+To start updating of the shared memory segment you need to run the
+smbcontrol command to turn on profiling for one or more smbd processes
+(see the man page for smbcontrol).
+
+
+
+Troubleshooting
+===============
+
+ + After installing or restarting the agent, the PMCD log file
+ (pmcd.log) and the PMDA log file (samba.log) should be checked
+ for any warnings or errors. These logs are located in
+ /var/log/pcp/pmcd on Linux and /var/adm/pcplog on IRIX.
diff --git a/pcp/Remove b/pcp/Remove
new file mode 100755
index 00000000000..3f7434d2553
--- /dev/null
+++ b/pcp/Remove
@@ -0,0 +1,52 @@
+#! /bin/sh
+#
+# Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write the Free Software Foundation, Inc., 59
+# Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+# Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+# Mountain View, CA 94043, or:
+#
+# http://www.sgi.com
+#
+# For further information regarding this notice, see:
+#
+# http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+#
+# Remove the samba PMDA
+#
+
+# source the PCP configuration environment variables
+. /etc/pcp.env
+
+# Get the common procedures and variable assignments
+#
+. $PCP_SHARE_DIR/lib/pmdaproc.sh
+
+# The name of the PMDA
+#
+iam=samba
+
+# Do it
+#
+pmdaSetup
+pmdaRemove
+
+exit 0
diff --git a/pcp/domain.h b/pcp/domain.h
new file mode 100644
index 00000000000..3cf0bc6ce97
--- /dev/null
+++ b/pcp/domain.h
@@ -0,0 +1,4 @@
+/*
+ * built from /var/pcp/pmns/stdpmid
+ */
+#define SAMBA 123
diff --git a/pcp/help b/pcp/help
new file mode 100644
index 00000000000..dc3b02f03bd
--- /dev/null
+++ b/pcp/help
@@ -0,0 +1,77 @@
+#
+# samba PMDA help file in the ASCII format
+#
+# lines beginning with a # are ignored
+# lines beginning @ introduce a new entry of the form
+# @ metric_name oneline-text
+# help test goes
+# here over multiple lines
+# ...
+#
+# the metric_name is decoded against the default PMNS -- as a special case,
+# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an
+# instance domain identification, and the text describes the instance domain
+#
+# blank lines before the @ line are ignored
+#
+
+@ SAMBA.0 Count and Time Instance Domain
+Contains count and time information for system calls and smb transactions.
+Counts shows the number of times each transaction was called. Times
+indicates the time spent in each transaction.
+
+@ SAMBA.1 Byte Instance Domain
+This domain contains instances for the number of bytes transferred
+using the read and write system call.
+
+@ samba.counts Count of calls to this function
+
+@ samba.times Time required to complete call
+
+@ samba.bytes Number of bytes processed by this call
+
+@ samba.smbd.smb_count Count of SMB packets processed
+
+@ samba.smbd.uid_changes Count of times effective uid changed
+
+@ samba.statcache.lookups Number of lookups in stat cache
+
+@ samba.statcache.misses Number of times stat cache lookup missed
+
+@ samba.statcache.hits Number of times stat cache lookup hit
+
+@ samba.writecache.num_caches Number of write caches available
+
+@ samba.writecache.allocated_caches Number of write caches allocated
+
+@ samba.writecache.read_hits Number of times read request found in write cache
+
+@ samba.writecache.total_writes Number of writes to write cache
+
+@ samba.writecache.init_writes Number of initial writes to write cache
+
+@ samba.writecache.abutted_writes
+
+@ samba.writecache.perfect_writes
+
+@ samba.writecache.direct_writes
+
+@ samba.writecache.non_oplock_writes
+
+@ samba.writecache.seek_flush
+
+@ samba.writecache.read_flush
+
+@ samba.writecache.write_flush
+
+@ samba.writecache.readraw_flush
+
+@ samba.writecache.oplock_rel_flush
+
+@ samba.writecache.close_flush
+
+@ samba.writecache.sync_flush
+
+@ samba.writecache.size_change_flush
+
+@ samba.bytes cumulative number of bytes read or written
diff --git a/pcp/mkheader.pl b/pcp/mkheader.pl
new file mode 100755
index 00000000000..ad069c544a8
--- /dev/null
+++ b/pcp/mkheader.pl
@@ -0,0 +1,63 @@
+#!/usr/bin/perl
+
+
+open(PROFILE,"profile.h") || die "Unable to open profile.h\n";
+@profile = <PROFILE>;
+close PROFILE;
+
+open(METRICS,"> metrics.h") || die "Unable to open metrics.h for output\n";
+
+print METRICS "#define COUNT_TIME_INDOM 0\n";
+print METRICS "#define BYTE_INDOM 1\n\n";
+print METRICS "#define FIELD_OFF(x) (unsigned)\&(((struct profile_stats *)NULL)->x)\n\n";
+print METRICS "typedef struct {\n";
+print METRICS "\tchar *name;\n";
+print METRICS "\tunsigned offset;\n";
+print METRICS "} samba_instance;\n\n";
+
+@instnames = grep(/unsigned .*_time;/,@profile);
+foreach $instnames (@instnames) {
+ chomp $instnames;
+ $instnames =~ s/^.*unsigned (.*)_time.*$/$1/;
+}
+
+print METRICS "static samba_instance samba_counts[] = {";
+$first = 1;
+foreach $1 (@instnames) {
+ if ($first == 1) {
+ $first = 0;
+ print METRICS "\n";
+ } else {
+ print METRICS ",\n";
+ }
+ print METRICS "\t{\"$1\", FIELD_OFF($1_count)}";
+}
+print METRICS "\n};\n\n";
+print METRICS "static samba_instance samba_times[] = {";
+$first = 1;
+foreach $1 (@instnames) {
+ if ($first == 1) {
+ $first = 0;
+ print METRICS "\n";
+ } else {
+ print METRICS ",\n";
+ }
+ print METRICS "\t{\"$1\", FIELD_OFF($1_time)}";
+}
+print METRICS "\n};\n\n";
+print METRICS "static samba_instance samba_bytes[] = {";
+@instnames = grep(/unsigned .*_bytes;/,@profile);
+$first = 1;
+foreach $_ (@instnames) {
+ if ($first == 1) {
+ $first = 0;
+ print METRICS "\n";
+ } else {
+ print METRICS ",\n";
+ }
+ /^.*unsigned (.*)_bytes.*$/;
+ print METRICS "\t{\"$1\", FIELD_OFF($1_bytes)}";
+}
+print METRICS "\n};\n";
+
+close METRICS
diff --git a/pcp/pmns b/pcp/pmns
new file mode 100644
index 00000000000..1f3dd3934ee
--- /dev/null
+++ b/pcp/pmns
@@ -0,0 +1,45 @@
+/*
+ * Metrics for samba PMDA
+ *
+ */
+
+samba {
+ smbd
+ statcache
+ writecache
+ counts SAMBA:3:0
+ times SAMBA:4:0
+ bytes SAMBA:5:0
+}
+
+samba.smbd {
+ smb_count SAMBA:0:0
+ uid_changes SAMBA:0:1
+}
+
+samba.statcache {
+ lookups SAMBA:1:0
+ misses SAMBA:1:1
+ hits SAMBA:1:2
+}
+
+samba.writecache {
+ num_caches SAMBA:2:0
+ allocated_caches SAMBA:2:1
+ read_hits SAMBA:2:2
+ total_writes SAMBA:2:3
+ init_writes SAMBA:2:4
+ abutted_writes SAMBA:2:5
+ perfect_writes SAMBA:2:6
+ direct_writes SAMBA:2:7
+ non_oplock_writes SAMBA:2:8
+ seek_flush SAMBA:2:9
+ read_flush SAMBA:2:10
+ write_flush SAMBA:2:11
+ readraw_flush SAMBA:2:12
+ oplock_rel_flush SAMBA:2:13
+ close_flush SAMBA:2:14
+ sync_flush SAMBA:2:15
+ size_change_flush SAMBA:2:16
+}
+
diff --git a/pcp/root b/pcp/root
new file mode 100644
index 00000000000..d5137bc7ddd
--- /dev/null
+++ b/pcp/root
@@ -0,0 +1,10 @@
+/*
+ * fake "root" for validating the local PMNS subtree
+ */
+
+#include "/var/pcp/pmns/stdpmid"
+
+root { samba }
+
+#include "pmns"
+
diff --git a/pcp/samba.c b/pcp/samba.c
new file mode 100644
index 00000000000..07a6ce2e169
--- /dev/null
+++ b/pcp/samba.c
@@ -0,0 +1,390 @@
+/*
+ * Samba, configurable PMDA
+ *
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+typedef int BOOL;
+
+#define IRIX 1
+
+#include <stdio.h>
+#include <sys/shm.h>
+#include <pcp/pmapi.h>
+#ifdef IRIX
+#include <pcp/impl.h>
+#endif
+#include <pcp/pmda.h>
+#include "domain.h"
+#include "profile.h"
+#include "metrics.h"
+
+static pmdaInstid *counttime = NULL;
+static pmdaInstid *bytes = NULL;
+
+/*
+ * List of instance domains
+ */
+
+static pmdaIndom indomtab[] = {
+ {COUNT_TIME_INDOM,0,NULL},
+ {BYTE_INDOM,0,NULL}
+};
+/*
+ * all metrics supported in this PMDA - one table entry for each
+ */
+
+static pmdaMetric metrictab[] = {
+/* smbd.smb_count */
+ { NULL, { PMDA_PMID(0,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+ { 0,0,1,0,0,PM_COUNT_ONE} }, },
+/* smbd.uid_changes */
+ { NULL, { PMDA_PMID(0,1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+ { 0,0,1,0,0,PM_COUNT_ONE} }, },
+/* statcache.lookups */
+ { NULL, { PMDA_PMID(1,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+ { 0,0,1,0,0,PM_COUNT_ONE} }, },
+/* statcache.misses */
+ { NULL, { PMDA_PMID(1,1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+ { 0,0,1,0,0,PM_COUNT_ONE} }, },
+/* statcache.hits */
+ { NULL, { PMDA_PMID(1,2), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+ { 0,0,1,0,0,PM_COUNT_ONE} }, },
+/* writecache.num_caches */
+ { NULL, { PMDA_PMID(2,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT,
+ { 0,0,1,0,0,PM_COUNT_ONE} }, },
+/* writecache.allocated_caches */
+ { NULL, { PMDA_PMID(2,1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT,
+ { 0,0,1,0,0,PM_COUNT_ONE} }, },
+/* writecache.read_hits */
+ { NULL, { PMDA_PMID(2,2), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+ { 0,0,1,0,0,PM_COUNT_ONE} }, },
+/* writecache.total_writes */
+ { NULL, { PMDA_PMID(2,3), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+ { 0,0,1,0,0,PM_COUNT_ONE} }, },
+/* writecache.init_writes */
+ { NULL, { PMDA_PMID(2,4), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+ { 0,0,1,0,0,PM_COUNT_ONE} }, },
+/* writecache.abutted_writes */
+ { NULL, { PMDA_PMID(2,5), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+ { 0,0,1,0,0,PM_COUNT_ONE} }, },
+/* writecache.perfect_writes */
+ { NULL, { PMDA_PMID(2,6), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+ { 0,0,1,0,0,PM_COUNT_ONE} }, },
+/* writecache.direct_writes */
+ { NULL, { PMDA_PMID(2,7), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+ { 0,0,1,0,0,PM_COUNT_ONE} }, },
+/* writecache.non_oplock_writes */
+ { NULL, { PMDA_PMID(2,8), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+ { 0,0,1,0,0,PM_COUNT_ONE} }, },
+/* writecache.seek_flush */
+ { NULL, { PMDA_PMID(2,9), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+ { 0,0,1,0,0,PM_COUNT_ONE} }, },
+/* writecache.read_flush */
+ { NULL, { PMDA_PMID(2,10), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+ { 0,0,1,0,0,PM_COUNT_ONE} }, },
+/* writecache.write_flush */
+ { NULL, { PMDA_PMID(2,11), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+ { 0,0,1,0,0,PM_COUNT_ONE} }, },
+/* writecache.readraw_flush */
+ { NULL, { PMDA_PMID(2,12), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+ { 0,0,1,0,0,PM_COUNT_ONE} }, },
+/* writecache.oplock_rel_flush */
+ { NULL, { PMDA_PMID(2,13), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+ { 0,0,1,0,0,PM_COUNT_ONE} }, },
+/* writecache.close_flush */
+ { NULL, { PMDA_PMID(2,14), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+ { 0,0,1,0,0,PM_COUNT_ONE} }, },
+/* writecache.sync_flush */
+ { NULL, { PMDA_PMID(2,15), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+ { 0,0,1,0,0,PM_COUNT_ONE} }, },
+/* writecache.size_change_flush */
+ { NULL, { PMDA_PMID(2,16), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+ { 0,0,1,0,0,PM_COUNT_ONE} }, },
+/* counts instance domain */
+ { NULL, { PMDA_PMID(3,0), PM_TYPE_U32, COUNT_TIME_INDOM, PM_SEM_COUNTER,
+ { 0,0,1,0,0,PM_COUNT_ONE} }, },
+/* times instance domain */
+ { NULL, { PMDA_PMID(4,0), PM_TYPE_U32, COUNT_TIME_INDOM, PM_SEM_COUNTER,
+ { 0,1,0,0,PM_TIME_USEC,0} }, },
+/* bytes instance domain */
+ { NULL, { PMDA_PMID(5,0), PM_TYPE_U32, BYTE_INDOM, PM_SEM_COUNTER,
+ { 1,0,0,PM_SPACE_BYTE,0,0} }, }
+
+};
+
+extern int errno;
+struct profile_stats *stats;
+struct profile_header *shmheader;
+int shmid = -1;
+
+
+int
+samba_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom)
+{
+ __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid);
+
+
+ if (inst != PM_IN_NULL && mdesc->m_desc.indom == PM_INDOM_NULL)
+ return PM_ERR_INST;
+
+ if (idp->cluster == 0) {
+ switch (idp->item) {
+ case 0: /* smbd.smb_count */
+ atom->ul = stats->smb_count;
+ break;
+ case 1: /* smb.uid_changes */
+ atom->ul = stats->uid_changes;
+ break;
+ default:
+ return PM_ERR_PMID;
+ }
+ }
+ else if (idp->cluster == 1) { /* statcache */
+ switch (idp->item) {
+ case 0: /* statcache.lookups */
+ atom->ul = stats->statcache_lookups;
+ break;
+ case 1: /* statcache.misses */
+ atom->ul = stats->statcache_misses;
+ break;
+ case 2: /* statcache.hits */
+ atom->ul = stats->statcache_hits;
+ break;
+ default:
+ return PM_ERR_PMID;
+ }
+ }
+ else if (idp->cluster == 2) { /* writecache */
+ switch (idp->item) {
+ case 0: /* writecache.num_caches */
+ atom->ul = stats->writecache_num_write_caches;
+ break;
+ case 1: /* writecache.allocated_caches */
+ atom->ul = stats->writecache_allocated_write_caches;
+ break;
+ case 2: /* writecache.read_hits */
+ atom->ul = stats->writecache_read_hits;
+ break;
+ case 3: /* writecache.total_writes */
+ atom->ul = stats->writecache_total_writes;
+ break;
+ case 4: /* writecache.init_writes */
+ atom->ul = stats->writecache_init_writes;
+ break;
+ case 5: /* writecache.abutted_writes */
+ atom->ul = stats->writecache_abutted_writes;
+ break;
+ case 6: /* writecache.perfect_writes */
+ atom->ul = stats->writecache_num_perfect_writes;
+ break;
+ case 7: /* writecache.direct_writes */
+ atom->ul = stats->writecache_direct_writes;
+ break;
+ case 8: /* writecache.non_oplock_writes */
+ atom->ul = stats->writecache_non_oplock_writes;
+ break;
+ case 9: /* writecache.seek_flush */
+ atom->ul = stats->writecache_flushed_writes[SEEK_FLUSH];
+ break;
+ case 10: /* writecache.read_flush */
+ atom->ul = stats->writecache_flushed_writes[READ_FLUSH];
+ break;
+ case 11: /* writecache.write_flush */
+ atom->ul = stats->writecache_flushed_writes[WRITE_FLUSH];
+ break;
+ case 12: /* writecache.readraw_flush */
+ atom->ul = stats->writecache_flushed_writes[READRAW_FLUSH];
+ break;
+ case 13: /* writecache.oplock_rel_flush */
+ atom->ul = stats->writecache_flushed_writes[OPLOCK_RELEASE_FLUSH];
+ break;
+ case 14: /* writecache.close_flush */
+ atom->ul = stats->writecache_flushed_writes[CLOSE_FLUSH];
+ break;
+ case 15: /* writecache.sync_flush */
+ atom->ul = stats->writecache_flushed_writes[SYNC_FLUSH];
+ break;
+ case 16: /* writecache.size_change_flush */
+ atom->ul = stats->writecache_flushed_writes[SIZECHANGE_FLUSH];
+ break;
+ default:
+ return PM_ERR_PMID;
+ }
+ }
+ else if (idp->cluster == 3) { /* counts */
+ if (idp->item == 0) {
+ if (inst < indomtab[COUNT_TIME_INDOM].it_numinst) {
+ unsigned *p;
+
+ p = (unsigned *)((unsigned)stats + samba_counts[inst].offset);
+ atom->ul = *p;
+ }
+ else
+ return PM_ERR_INST;
+ }
+ else
+ return PM_ERR_PMID;
+ }
+ else if (idp->cluster == 4) { /* times */
+ if (idp->item == 0) {
+ if (inst < indomtab[COUNT_TIME_INDOM].it_numinst) {
+ unsigned *p;
+
+ p = (unsigned *)((unsigned)stats + samba_times[inst].offset);
+ atom->ul = *p;
+ }
+ else
+ return PM_ERR_INST;
+ }
+ else
+ return PM_ERR_PMID;
+ }
+ else if (idp->cluster == 5) { /* bytes */
+ if (idp->item == 0) {
+ if (inst < indomtab[BYTE_INDOM].it_numinst) {
+ unsigned *p;
+
+ p = (unsigned *)((unsigned)stats + samba_bytes[inst].offset);
+ atom->ul = *p;
+ }
+ else
+ return PM_ERR_INST;
+ }
+ else
+ return PM_ERR_PMID;
+ }
+ else
+ return PM_ERR_PMID;
+ return 0;
+}
+
+
+void
+samba_init(pmdaInterface *dp)
+{
+ int inst_count, i;
+
+ if (dp->status != 0)
+ return;
+
+ if ((shmid = shmget(PROF_SHMEM_KEY, 0, 0)) == -1) {
+ fprintf(stderr, "shmid: %s\n", strerror(errno));
+ fprintf(stderr, "samba not compiled with profile support or not running\n");
+ exit(1);
+ }
+ shmheader = (struct profile_header *)shmat(shmid, NULL, SHM_RDONLY);
+ if ((int)shmheader == -1) {
+ fprintf(stderr, "shmat: %s\n", strerror(errno));
+ exit(1);
+ }
+
+/*
+ * Initialize lists of instances
+ */
+
+ inst_count = sizeof(samba_counts)/sizeof(samba_counts[0]);
+ counttime = (pmdaInstid *)malloc(inst_count * sizeof(pmdaInstid));
+ if (counttime == NULL) {
+ __pmNoMem("count&time",inst_count * sizeof(pmdaInstid),PM_FATAL_ERR);
+ /* NOTREACHED*/
+ }
+ for (i = 0; i < inst_count; i++) {
+ counttime[i].i_inst = i;
+ counttime[i].i_name = samba_counts[i].name;
+ }
+ indomtab[COUNT_TIME_INDOM].it_numinst = inst_count;
+ indomtab[COUNT_TIME_INDOM].it_set = counttime;
+
+ inst_count = sizeof(samba_bytes)/sizeof(samba_bytes[0]);
+ bytes = (pmdaInstid *)malloc(inst_count * sizeof(pmdaInstid));
+ if (bytes == NULL) {
+ __pmNoMem("bytes",inst_count * sizeof(pmdaInstid),PM_FATAL_ERR);
+ /* NOTREACHED*/
+ }
+ for (i = 0; i < inst_count; i++) {
+ bytes[i].i_inst = i;
+ bytes[i].i_name = samba_bytes[i].name;
+ }
+ indomtab[BYTE_INDOM].it_numinst = inst_count;
+ indomtab[BYTE_INDOM].it_set = bytes;
+
+
+ pmdaSetFetchCallBack(dp, samba_fetchCallBack);
+ pmdaInit(dp, indomtab, sizeof(indomtab)/sizeof(indomtab[0]),
+ metrictab, sizeof(metrictab)/sizeof(metrictab[0]));
+
+ /* validate the data */
+ if (!shmheader) /* not mapped yet */
+ fprintf(stderr, "samba_init: shmem not mapped\n");
+ else if (shmheader->prof_shm_magic != PROF_SHM_MAGIC)
+ fprintf(stderr, "samba_init: bad magic\n");
+ else if (shmheader->prof_shm_version != PROF_SHM_VERSION)
+ fprintf(stderr, "samba_init: bad version %X\n",
+ shmheader->prof_shm_version);
+ else {
+ stats = &shmheader->stats;
+ return; /* looks OK */
+ }
+ exit(1);
+}
+
+
+int
+main(int argc, char **argv)
+{
+ int err = 0;
+ char *p;
+ pmdaInterface dispatch;
+
+ for (p = pmProgname = argv[0]; *p; p++)
+ if (*p == '/') pmProgname = p+1;
+
+ pmdaDaemon(&dispatch, PMDA_INTERFACE_2, pmProgname, SAMBA,
+ "samba.log", "/var/pcp/pmdas/samba/help");
+
+ if (pmdaGetOpt(argc, argv, "D:d:l:?", &dispatch, &err) != EOF) {
+ fprintf(stderr, "Usage: %s [options]\n\n\
+Options:\n\
+ -d domain use domain (numeric) for metrics domain of PMDA\n\
+ -l logfile write log into logfile rather than using default log name\n",
+ pmProgname);
+ exit(1);
+ }
+
+ pmdaOpenLog(&dispatch);
+ samba_init(&dispatch);
+ pmdaConnect(&dispatch);
+ pmdaMain(&dispatch);
+
+ exit(0);
+ /*NOTREACHED*/
+}
diff --git a/source/.cvsignore b/source/.cvsignore
new file mode 100644
index 00000000000..31dd2c3a7a9
--- /dev/null
+++ b/source/.cvsignore
@@ -0,0 +1,31 @@
+*.po
+*.po32
+.headers.stamp
+.inslog2
+.ix*
+.proto.check
+.proto.stamp
+autom4te.cache
+autom4te-2.53.cache
+ID
+Makefile
+bin
+build
+config.cache
+config.log
+config.status
+configure.tridge
+cvs.log
+diffs
+dmalloc.log
+dmallog.log
+dox
+libtool
+so_locations
+tca.log
+testdir
+testtmp
+trace.out
+typescript*
+configure
+smbadduser
diff --git a/source/.dmallocrc b/source/.dmallocrc
new file mode 100644
index 00000000000..5e5c45e1244
--- /dev/null
+++ b/source/.dmallocrc
@@ -0,0 +1,2 @@
+samba allow-free-null, log-stats, log-non-free, log-trans, \
+ check-fence, check-heap, check-lists, error-abort \ No newline at end of file
diff --git a/source/Doxyfile b/source/Doxyfile
new file mode 100644
index 00000000000..c1040781d84
--- /dev/null
+++ b/source/Doxyfile
@@ -0,0 +1,176 @@
+# Doxyfile 0.1
+
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+PROJECT_NAME = Samba
+PROJECT_NUMBER = HEAD
+
+# NOTE: By default, Doxygen writes into the dox/ subdirectory of the
+# invocation directory. If you want to put it somewhere else, for
+# example, to write straight into a webserver directory, then override
+# this variable in a configuration concatenated to this one: Doxygen
+# doesn't mind variables being redefined.
+
+OUTPUT_DIRECTORY = dox
+
+OUTPUT_LANGUAGE = English
+EXTRACT_ALL = YES
+EXTRACT_PRIVATE = YES
+EXTRACT_STATIC = YES
+HIDE_UNDOC_MEMBERS = NO
+HIDE_UNDOC_CLASSES = NO
+BRIEF_MEMBER_DESC = YES
+REPEAT_BRIEF = YES
+ALWAYS_DETAILED_SEC = NO
+FULL_PATH_NAMES = YES
+STRIP_FROM_PATH = $(PWD)/
+INTERNAL_DOCS = YES
+CLASS_DIAGRAMS = YES
+SOURCE_BROWSER = YES
+INLINE_SOURCES = YES
+STRIP_CODE_COMMENTS = NO
+CASE_SENSE_NAMES = YES
+SHORT_NAMES = NO
+HIDE_SCOPE_NAMES = YES
+VERBATIM_HEADERS = YES
+SHOW_INCLUDE_FILES = YES
+JAVADOC_AUTOBRIEF = YES
+INHERIT_DOCS = YES
+INLINE_INFO = YES
+SORT_MEMBER_DOCS = NO
+DISTRIBUTE_GROUP_DOC = NO
+TAB_SIZE = 8
+GENERATE_TODOLIST = YES
+GENERATE_TESTLIST = YES
+GENERATE_BUGLIST = YES
+ALIASES =
+ENABLED_SECTIONS =
+MAX_INITIALIZER_LINES = 30
+OPTIMIZE_OUTPUT_FOR_C = YES
+SHOW_USED_FILES = YES
+REFERENCED_BY_RELATION = YES
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET = YES
+WARNINGS = NO
+WARN_IF_UNDOCUMENTED = NO
+WARN_FORMAT = "$file:$line: $text"
+WARN_LOGFILE =
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT = .
+FILE_PATTERNS = *.c \
+ *.h \
+ *.idl
+RECURSIVE = YES
+EXCLUDE = include/includes.h \
+ include/proto.h
+EXCLUDE_PATTERNS =
+EXAMPLE_PATH =
+EXAMPLE_PATTERNS =
+IMAGE_PATH =
+INPUT_FILTER =
+FILTER_SOURCE_FILES = NO
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX = YES
+COLS_IN_ALPHA_INDEX = 1
+IGNORE_PREFIX =
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML = YES
+HTML_OUTPUT = .
+HTML_HEADER =
+HTML_FOOTER =
+HTML_STYLESHEET =
+HTML_ALIGN_MEMBERS = YES
+GENERATE_HTMLHELP = NO
+GENERATE_CHI = NO
+BINARY_TOC = NO
+TOC_EXPAND = NO
+DISABLE_INDEX = NO
+ENUM_VALUES_PER_LINE = 3
+GENERATE_TREEVIEW = NO
+TREEVIEW_WIDTH = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX = NO
+LATEX_OUTPUT = latex
+COMPACT_LATEX = NO
+PAPER_TYPE = a4wide
+EXTRA_PACKAGES =
+LATEX_HEADER =
+PDF_HYPERLINKS = YES
+USE_PDFLATEX = YES
+LATEX_BATCHMODE = YES
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF = NO
+RTF_OUTPUT = rtf
+COMPACT_RTF = NO
+RTF_HYPERLINKS = NO
+RTF_STYLESHEET_FILE =
+RTF_EXTENSIONS_FILE =
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN = NO
+MAN_OUTPUT = man
+MAN_EXTENSION = .3
+MAN_LINKS = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML = NO
+#---------------------------------------------------------------------------
+# configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING = NO
+MACRO_EXPANSION = NO
+EXPAND_ONLY_PREDEF = NO
+SEARCH_INCLUDES = YES
+INCLUDE_PATH =
+INCLUDE_FILE_PATTERNS =
+PREDEFINED =
+EXPAND_AS_DEFINED =
+SKIP_FUNCTION_MACROS = YES
+#---------------------------------------------------------------------------
+# configuration::additions related to external references
+#---------------------------------------------------------------------------
+TAGFILES =
+GENERATE_TAGFILE =
+ALLEXTERNALS = NO
+PERL_PATH = /usr/bin/perl
+#---------------------------------------------------------------------------
+# configuration options related to the dot tool
+#---------------------------------------------------------------------------
+HAVE_DOT = NO
+CLASS_GRAPH = YES
+COLLABORATION_GRAPH = YES
+TEMPLATE_RELATIONS = YES
+INCLUDE_GRAPH = YES
+INCLUDED_BY_GRAPH = YES
+GRAPHICAL_HIERARCHY = YES
+DOT_PATH =
+DOTFILE_DIRS =
+MAX_DOT_GRAPH_WIDTH = 1024
+MAX_DOT_GRAPH_HEIGHT = 1024
+GENERATE_LEGEND = YES
+DOT_CLEANUP = YES
+#---------------------------------------------------------------------------
+# configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+SEARCHENGINE = NO
+CGI_NAME = search.cgi
+CGI_URL =
+DOC_URL =
+DOC_ABSPATH =
+BIN_ABSPATH = /usr/local/bin/
+EXT_DOC_PATHS =
diff --git a/source/Makefile.in b/source/Makefile.in
new file mode 100644
index 00000000000..145905332ad
--- /dev/null
+++ b/source/Makefile.in
@@ -0,0 +1,1526 @@
+#########################################################################
+# Makefile.in for Samba - rewritten for autoconf support
+# Copyright Andrew Tridgell 1992-1998
+# Copyright (C) 2001 by Martin Pool <mbp@samba.org>
+# Copyright Andrew Bartlett 2002
+# Copyright (C) 2003 Jim McDonough <jmcd@us.ibm.com>
+# Copyright (C) 2002-2003 Jelmer Vernooij <jelmer@samba.org>
+###########################################################################
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+
+LIBS=@LIBS@
+CC=@CC@
+SHLD=@SHLD@
+CFLAGS=@CFLAGS@
+CPPFLAGS=@CPPFLAGS@
+EXEEXT=@EXEEXT@
+LDFLAGS=@LDFLAGS@
+AR=@AR@
+LDSHFLAGS=@LDSHFLAGS@ @LDFLAGS@
+WINBIND_NSS_LDSHFLAGS=@WINBIND_NSS_LDSHFLAGS@ @LDFLAGS@
+AWK=@AWK@
+DYNEXP=@DYNEXP@
+PYTHON=@PYTHON@
+PERL=@PERL@
+
+TERMLDFLAGS=@TERMLDFLAGS@
+TERMLIBS=@TERMLIBS@
+PRINT_LIBS=@PRINT_LIBS@
+AUTH_LIBS=@AUTH_LIBS@
+ACL_LIBS=@ACL_LIBS@
+PASSDB_LIBS=@PASSDB_LIBS@
+IDMAP_LIBS=@IDMAP_LIBS@
+KRB5LIBS=@KRB5_LIBS@
+LDAP_LIBS=@LDAP_LIBS@
+
+LINK=$(CC) $(FLAGS) $(LDFLAGS)
+
+INSTALLCMD=@INSTALL@
+INSTALLCLIENTCMD_SH=@INSTALLCLIENTCMD_SH@
+INSTALLCLIENTCMD_A=@INSTALLCLIENTCMD_A@
+
+VPATH=@srcdir@
+srcdir=@abs_srcdir@
+builddir=@abs_builddir@
+SHELL=/bin/sh
+DESTDIR=/
+
+# XXX: Perhaps this should be @SHELL@ instead -- apparently autoconf
+# will search for a POSIX-compliant shell, and that might not be
+# /bin/sh on some platforms. I guess it's not a big problem -- mbp
+
+# See the autoconf manual "Installation Directory Variables" for a
+# discussion of the subtle use of these variables.
+
+BASEDIR= @prefix@
+BINDIR = @bindir@
+# sbindir is mapped to bindir when compiling SAMBA in 2.0.x compatibility mode.
+SBINDIR = @sbindir@
+LIBDIR = @libdir@
+VFSLIBDIR = $(LIBDIR)/vfs
+PDBLIBDIR = $(LIBDIR)/pdb
+RPCLIBDIR = $(LIBDIR)/rpc
+IDMAPLIBDIR = $(LIBDIR)/idmap
+CHARSETLIBDIR = $(LIBDIR)/charset
+AUTHLIBDIR = $(LIBDIR)/auth
+CONFIGLIBDIR = $(LIBDIR)/config
+CONFIGDIR = @configdir@
+VARDIR = @localstatedir@
+MANDIR = @mandir@
+DATADIR = @datadir@
+
+# The permissions to give the executables
+INSTALLPERMS = 0755
+
+# set these to where to find various files
+# These can be overridden by command line switches (see smbd(8))
+# or in smb.conf (see smb.conf(5))
+LOGFILEBASE = @logfilebase@
+CONFIGFILE = $(CONFIGDIR)/smb.conf
+LMHOSTSFILE = $(CONFIGDIR)/lmhosts
+
+# This is where smbpasswd et al go
+PRIVATEDIR = @privatedir@
+
+SMB_PASSWD_FILE = $(PRIVATEDIR)/smbpasswd
+PRIVATE_DIR = $(PRIVATEDIR)
+
+# This is where SWAT images and help files go
+SWATDIR = @swatdir@
+
+# the directory where lock files go
+LOCKDIR = @lockdir@
+
+# the directory where pid files go
+PIDDIR = @piddir@
+# man pages language(s)
+man_langs = "@manlangs@"
+
+LIBSMBCLIENT=bin/libsmbclient.a @LIBSMBCLIENT_SHARED@
+LIBSMBCLIENT_MAJOR=0
+LIBSMBCLIENT_MINOR=1
+
+
+FLAGS1 = $(CFLAGS) @FLAGS1@ -Iinclude -I$(srcdir)/include -I$(srcdir)/ubiqx -I$(srcdir)/smbwrapper -I. $(CPPFLAGS) -I$(srcdir)
+FLAGS2 =
+FLAGS3 =
+FLAGS4 =
+FLAGS5 = $(FLAGS1) $(FLAGS2) $(FLAGS3) $(FLAGS4)
+FLAGS = $(ISA) $(FLAGS5)
+
+PASSWD_FLAGS = -DSMB_PASSWD_FILE=\"$(SMB_PASSWD_FILE)\" -DPRIVATE_DIR=\"$(PRIVATE_DIR)\"
+PATH_FLAGS1 = -DCONFIGFILE=\"$(CONFIGFILE)\" -DSBINDIR=\"$(SBINDIR)\"
+PATH_FLAGS2 = $(PATH_FLAGS1) -DBINDIR=\"$(BINDIR)\" -DDRIVERFILE=\"$(DRIVERFILE)\"
+PATH_FLAGS3 = $(PATH_FLAGS2) -DLMHOSTSFILE=\"$(LMHOSTSFILE)\"
+PATH_FLAGS4 = $(PATH_FLAGS3) -DSWATDIR=\"$(SWATDIR)\" -DLOCKDIR=\"$(LOCKDIR)\" -DPIDDIR=\"$(PIDDIR)\"
+PATH_FLAGS5 = $(PATH_FLAGS4) -DLIBDIR=\"$(LIBDIR)\" \
+ -DLOGFILEBASE=\"$(LOGFILEBASE)\" -DSHLIBEXT=\"@SHLIBEXT@\"
+PATH_FLAGS6 = $(PATH_FLAGS5) -DCONFIGDIR=\"$(CONFIGDIR)\"
+PATH_FLAGS = $(PATH_FLAGS6) $(PASSWD_FLAGS)
+
+# Note that all executable programs now provide for an optional executable suffix.
+
+SBIN_PROGS = bin/smbd@EXEEXT@ bin/nmbd@EXEEXT@ bin/swat@EXEEXT@ @EXTRA_SBIN_PROGS@
+
+BIN_PROGS1 = bin/smbclient@EXEEXT@ bin/net@EXEEXT@ bin/smbspool@EXEEXT@ \
+ bin/testparm@EXEEXT@ bin/testprns@EXEEXT@ bin/smbstatus@EXEEXT@
+BIN_PROGS2 = bin/smbcontrol@EXEEXT@ bin/smbtree@EXEEXT@ bin/tdbbackup@EXEEXT@ \
+ bin/nmblookup@EXEEXT@ bin/pdbedit@EXEEXT@ bin/tdbdump@EXEEXT@
+BIN_PROGS3 = bin/smbpasswd@EXEEXT@ bin/rpcclient@EXEEXT@ bin/smbcacls@EXEEXT@ \
+ bin/profiles@EXEEXT@ bin/ntlm_auth@EXEEXT@ \
+ bin/smbcquotas@EXEEXT@
+
+# editreg removed from standard build until it is portable. It needs a major rewrite to
+# achieve this (tridge)
+# bin/editreg@EXEEXT@
+
+TORTURE_PROGS = bin/smbtorture@EXEEXT@ bin/msgtest@EXEEXT@ \
+ bin/masktest@EXEEXT@ bin/locktest@EXEEXT@ \
+ bin/locktest2@EXEEXT@ bin/nsstest@EXEEXT@ bin/vfstest@EXEEXT@
+
+BIN_PROGS = $(BIN_PROGS1) $(BIN_PROGS2) $(BIN_PROGS3) @EXTRA_BIN_PROGS@
+
+EVERYTHING_PROGS = bin/debug2html@EXEEXT@ bin/smbfilter@EXEEXT@ bin/talloctort@EXEEXT@ \
+ bin/log2pcap@EXEEXT@
+
+SHLIBS = @SHLIB_PROGS@ @LIBSMBCLIENT@
+
+SCRIPTS = $(srcdir)/script/smbtar $(builddir)/script/findsmb
+
+VFS_MODULES = @VFS_MODULES@
+PDB_MODULES = @PDB_MODULES@
+RPC_MODULES = @RPC_MODULES@
+IDMAP_MODULES = @IDMAP_MODULES@
+CHARSET_MODULES = @CHARSET_MODULES@
+AUTH_MODULES = @AUTH_MODULES@
+CONFIG_MODULES = @CONFIG_MODULES@
+MODULES = $(VFS_MODULES) $(PDB_MODULES) $(RPC_MODULES) $(IDMAP_MODULES) $(CHARSET_MODULES) $(AUTH_MODULES) $(CONFIG_MODULES)
+
+######################################################################
+# object file lists
+######################################################################
+
+TDBBASE_OBJ = tdb/tdb.o tdb/spinlock.o
+TDB_OBJ = $(TDBBASE_OBJ) tdb/tdbutil.o tdb/tdbback.o
+
+SMBLDAP_OBJ = @SMBLDAP@ @SMBLDAPUTIL@
+
+LIB_OBJ = lib/version.o lib/charcnv.o lib/debug.o lib/fault.o \
+ lib/getsmbpass.o lib/interface.o lib/md4.o \
+ lib/interfaces.o lib/pidfile.o lib/replace.o lib/replace1.o \
+ lib/signal.o lib/system.o lib/sendfile.o lib/time.o \
+ lib/ufc.o lib/genrand.o lib/username.o \
+ lib/util_getent.o lib/util_pw.o lib/access.o lib/smbrun.o \
+ lib/bitmap.o lib/crc32.o lib/snprintf.o lib/dprintf.o \
+ lib/xfile.o lib/wins_srv.o \
+ lib/util_str.o lib/clobber.o lib/util_sid.o lib/util_uuid.o \
+ lib/util_unistr.o lib/util_file.o lib/data_blob.o \
+ lib/util.o lib/util_sock.o lib/sock_exec.o lib/util_sec.o \
+ lib/talloc.o lib/hash.o lib/substitute.o lib/fsusage.o \
+ lib/ms_fnmatch.o lib/select.o lib/messages.o \
+ lib/tallocmsg.o lib/dmallocmsg.o libsmb/smb_signing.o \
+ lib/md5.o lib/hmacmd5.o lib/iconv.o \
+ nsswitch/wb_client.o nsswitch/wb_common.o \
+ lib/pam_errors.o intl/lang_tdb.o lib/account_pol.o \
+ lib/adt_tree.o lib/gencache.o $(TDB_OBJ) \
+ lib/module.o lib/ldap_escape.o @CHARSET_STATIC@ \
+ lib/privileges.o lib/secdesc.o lib/secace.o lib/secacl.o \
+ lib/genparser.o lib/genparser_samba.o
+
+LIB_SMBD_OBJ = lib/system_smbd.o lib/util_smbd.o
+
+LIB_NONSMBD_OBJ = $(LIB_OBJ) lib/dummysmbd.o
+
+READLINE_OBJ = lib/readline.o
+
+# Also depends on $(SECRETS_OBJ) $(LIBSAMBA_OBJ)
+# Be sure to include them into your application
+POPT_LIB_OBJ = lib/popt_common.o
+
+UBIQX_OBJ = ubiqx/ubi_BinTree.o ubiqx/ubi_Cache.o ubiqx/ubi_SplayTree.o \
+ ubiqx/ubi_dLinkList.o ubiqx/ubi_sLinkList.o
+
+PARAM_OBJ = dynconfig.o param/loadparm.o param/params.o param/modconf.o
+
+KRBCLIENT_OBJ = libads/kerberos.o libads/ads_status.o
+
+LIBADS_OBJ = libads/ldap.o libads/ldap_printer.o libads/sasl.o \
+ libads/krb5_setpw.o libads/ldap_user.o \
+ libads/ads_struct.o \
+ libads/disp_sec.o libads/ads_utils.o libads/ldap_utils.o \
+ libads/ads_ldap.o libads/authdata.o
+
+LIBADS_SERVER_OBJ = libads/util.o libads/kerberos_verify.o
+
+SECRETS_OBJ = passdb/secrets.o passdb/machine_sid.o
+
+LIBNMB_OBJ = libsmb/unexpected.o libsmb/namecache.o libsmb/nmblib.o \
+ libsmb/namequery.o libsmb/conncache.o
+
+LIBSAMBA_OBJ = libsmb/nterr.o libsmb/smbdes.o libsmb/smbencrypt.o libsmb/ntlm_check.o \
+ libsmb/ntlmssp.o libsmb/ntlmssp_parse.o libsmb/ntlmssp_sign.o
+
+LIBSMB_OBJ = libsmb/clientgen.o libsmb/cliconnect.o libsmb/clifile.o \
+ libsmb/clikrb5.o libsmb/clispnego.o libsmb/asn1.o \
+ libsmb/clirap.o libsmb/clierror.o libsmb/climessage.o \
+ libsmb/clireadwrite.o libsmb/clilist.o libsmb/cliprint.o \
+ libsmb/clitrans.o libsmb/clisecdesc.o libsmb/clidgram.o \
+ libsmb/clistr.o lib/util_seaccess.o \
+ libsmb/cliquota.o libsmb/clifsinfo.o \
+ libsmb/smberr.o libsmb/credentials.o libsmb/pwd_cache.o \
+ libsmb/clioplock.o libsmb/errormap.o libsmb/clirap2.o \
+ libsmb/doserr.o \
+ $(RPC_PARSE_OBJ1) $(LIBSAMBA_OBJ) $(LIBNMB_OBJ)
+
+LIBMSRPC_OBJ = rpc_client/cli_lsarpc.o rpc_client/cli_samr.o \
+ rpc_client/cli_netlogon.o rpc_client/cli_srvsvc.o \
+ rpc_client/cli_wkssvc.o rpc_client/cli_dfs.o \
+ rpc_client/cli_reg.o rpc_client/cli_pipe.o \
+ rpc_client/cli_spoolss.o rpc_client/cli_spoolss_notify.o \
+ rpc_client/cli_ds.o rpc_client/cli_echo.o \
+ rpc_client/cli_shutdown.o rpc_client/cli_epmapper.o
+
+REGOBJS_OBJ = registry/reg_objects.o
+REGISTRY_OBJ = registry/reg_frontend.o registry/reg_cachehook.o registry/reg_printing.o \
+ registry/reg_db.o
+
+RPC_LSA_OBJ = rpc_server/srv_lsa.o rpc_server/srv_lsa_nt.o
+
+RPC_NETLOG_OBJ = rpc_server/srv_netlog.o rpc_server/srv_netlog_nt.o
+
+RPC_SAMR_OBJ = rpc_server/srv_samr.o rpc_server/srv_samr_nt.o \
+ rpc_server/srv_samr_util.o
+
+RPC_REG_OBJ = rpc_server/srv_reg.o rpc_server/srv_reg_nt.o
+
+RPC_LSA_DS_OBJ = rpc_server/srv_lsa_ds.o rpc_server/srv_lsa_ds_nt.o
+
+RPC_SVC_OBJ = rpc_server/srv_srvsvc.o rpc_server/srv_srvsvc_nt.o
+
+RPC_WKS_OBJ = rpc_server/srv_wkssvc.o rpc_server/srv_wkssvc_nt.o
+
+RPC_DFS_OBJ = rpc_server/srv_dfs.o rpc_server/srv_dfs_nt.o
+RPC_SPOOLSS_OBJ = rpc_server/srv_spoolss.o rpc_server/srv_spoolss_nt.o
+
+RPC_PIPE_OBJ = rpc_server/srv_pipe_hnd.o rpc_server/srv_util.o \
+ rpc_server/srv_pipe.o rpc_server/srv_lsa_hnd.o
+
+RPC_ECHO_OBJ = rpc_server/srv_echo.o rpc_server/srv_echo_nt.o
+
+RPC_EPMAPPER_OBJ = rpc_server/srv_epmapper.o rpc_server/srv_epmapper_nt.o
+
+RPC_SERVER_OBJ = @RPC_STATIC@ $(RPC_PIPE_OBJ)
+
+# this includes only the low level parse code, not stuff
+# that requires knowledge of security contexts
+RPC_PARSE_OBJ1 = rpc_parse/parse_prs.o rpc_parse/parse_sec.o \
+ rpc_parse/parse_misc.o
+
+RPC_PARSE_OBJ = rpc_parse/parse_lsa.o rpc_parse/parse_net.o \
+ rpc_parse/parse_reg.o rpc_parse/parse_rpc.o \
+ rpc_parse/parse_samr.o rpc_parse/parse_srv.o \
+ rpc_parse/parse_wks.o rpc_parse/parse_ds.o \
+ rpc_parse/parse_spoolss.o rpc_parse/parse_dfs.o \
+ rpc_parse/parse_echo.o rpc_parse/parse_shutdown.o \
+ rpc_parse/parse_epmapper.o $(REGOBJS_OBJ)
+
+
+RPC_CLIENT_OBJ = rpc_client/cli_pipe.o
+
+LOCKING_OBJ = locking/locking.o locking/brlock.o locking/posix.o smbd/tdbutil.o
+
+GUMS_OBJ = sam/gums.o sam/gums_api.o sam/gums_helper.o @GUMS_STATIC@
+
+PASSDB_GET_SET_OBJ = passdb/pdb_get_set.o
+
+PASSDB_OBJ = $(PASSDB_GET_SET_OBJ) passdb/passdb.o passdb/pdb_interface.o \
+ passdb/util_sam_sid.o passdb/pdb_compat.o \
+ passdb/privileges.o passdb/lookup_sid.o \
+ passdb/login_cache.o @PDB_STATIC@ passdb/pdb_sql.o
+
+XML_OBJ = passdb/pdb_xml.o
+MYSQL_OBJ = passdb/pdb_mysql.o
+PGSQL_OBJ = passdb/pdb_pgsql.o
+DEVEL_HELP_WEIRD_OBJ = modules/weird.o
+CP850_OBJ = modules/CP850.o
+CP437_OBJ = modules/CP437.o
+CHARSET_MACOSXFS_OBJ = modules/charset_macosxfs.o
+
+GROUPDB_OBJ = groupdb/mapping.o
+
+PROFILE_OBJ = profile/profile.o
+PROFILES_OBJ = utils/profiles.o
+EDITREG_OBJ = utils/editreg.o
+
+OPLOCK_OBJ = smbd/oplock.o smbd/oplock_irix.o smbd/oplock_linux.o
+
+NOTIFY_OBJ = smbd/notify.o smbd/notify_hash.o smbd/notify_kernel.o
+
+VFS_AUDIT_OBJ = modules/vfs_audit.o
+VFS_EXTD_AUDIT_OBJ = modules/vfs_extd_audit.o
+VFS_FAKE_PERMS_OBJ = modules/vfs_fake_perms.o
+VFS_RECYCLE_OBJ = modules/vfs_recycle.o
+VFS_NETATALK_OBJ = modules/vfs_netatalk.o
+VFS_DEFAULT_QUOTA_OBJ = modules/vfs_default_quota.o
+VFS_READONLY_OBJ = modules/vfs_readonly.o modules/getdate.o
+VFS_CAP_OBJ = modules/vfs_cap.o
+VFS_EXPAND_MSDFS_OBJ = modules/vfs_expand_msdfs.o
+
+PLAINTEXT_AUTH_OBJ = auth/pampass.o auth/pass_check.o
+
+SLCACHE_OBJ = libsmb/samlogon_cache.o
+
+DCUTIL_OBJ = libsmb/namequery_dc.o libsmb/trustdom_cache.o libsmb/trusts_util.o
+
+AUTH_BUILTIN_OBJ = auth/auth_builtin.o
+AUTH_DOMAIN_OBJ = auth/auth_domain.o
+AUTH_SAM_OBJ = auth/auth_sam.o
+AUTH_RHOSTS_OBJ = auth/auth_rhosts.o
+AUTH_SERVER_OBJ = auth/auth_server.o
+AUTH_UNIX_OBJ = auth/auth_unix.o
+AUTH_WINBIND_OBJ = auth/auth_winbind.o
+
+AUTH_OBJ = auth/auth.o @AUTH_STATIC@ auth/auth_util.o auth/auth_compat.o \
+ auth/auth_ntlmssp.o \
+ $(PLAINTEXT_AUTH_OBJ) $(SLCACHE_OBJ) $(DCUTIL_OBJ)
+
+MANGLE_OBJ = smbd/mangle.o smbd/mangle_hash.o smbd/mangle_map.o smbd/mangle_hash2.o
+
+CONFIG_LDAP_OBJ = param/config_ldap.o
+
+SMBD_OBJ_MAIN = smbd/server.o
+
+BUILDOPT_OBJ = smbd/build_options.o
+
+SMBD_OBJ_SRV = smbd/files.o smbd/chgpasswd.o smbd/connection.o \
+ smbd/utmp.o smbd/session.o \
+ smbd/dfree.o smbd/dir.o smbd/password.o smbd/conn.o smbd/fileio.o \
+ smbd/ipc.o smbd/lanman.o smbd/negprot.o \
+ smbd/message.o smbd/nttrans.o smbd/pipes.o \
+ smbd/reply.o smbd/sesssetup.o smbd/trans2.o smbd/uid.o \
+ smbd/dosmode.o smbd/filename.o smbd/open.o smbd/close.o \
+ smbd/blocking.o smbd/sec_ctx.o smbd/srvstr.o \
+ smbd/vfs.o smbd/vfs-wrap.o smbd/statcache.o \
+ smbd/posix_acls.o lib/sysacls.o lib/server_mutex.o \
+ smbd/process.o smbd/service.o smbd/error.o \
+ printing/printfsp.o lib/sysquotas.o lib/sysquotas_linux.o \
+ lib/sysquotas_xfs.o lib/sysquotas_4A.o \
+ smbd/change_trust_pw.o smbd/fake_file.o \
+ smbd/quotas.o smbd/ntquotas.o lib/afs.o \
+ $(MANGLE_OBJ) @VFS_STATIC@
+
+SMBD_OBJ_BASE = $(PARAM_OBJ) $(SMBD_OBJ_SRV) $(MSDFS_OBJ) $(LIBSMB_OBJ) \
+ $(RPC_SERVER_OBJ) $(RPC_PARSE_OBJ) $(SECRETS_OBJ) \
+ $(LOCKING_OBJ) $(PASSDB_OBJ) $(PRINTING_OBJ) $(PROFILE_OBJ) \
+ $(LIB_OBJ) $(PRINTBACKEND_OBJ) $(OPLOCK_OBJ) \
+ $(NOTIFY_OBJ) $(GROUPDB_OBJ) $(AUTH_OBJ) \
+ $(LIBMSRPC_OBJ) \
+ $(LIBADS_OBJ) $(KRBCLIENT_OBJ) $(LIBADS_SERVER_OBJ) \
+ $(LIB_SMBD_OBJ) $(REGISTRY_OBJ) $(POPT_LIB_OBJ) \
+ $(UBIQX_OBJ) $(BUILDOPT_OBJ) $(SMBLDAP_OBJ)
+
+PRINTING_OBJ = printing/pcap.o printing/print_svid.o \
+ printing/print_cups.o printing/print_generic.o \
+ printing/lpq_parse.o printing/load.o
+
+PRINTBACKEND_OBJ = printing/printing.o printing/nt_printing.o printing/notify.o \
+ printing/printing_db.o
+
+MSDFS_OBJ = msdfs/msdfs.o
+
+SMBD_OBJ = $(SMBD_OBJ_BASE) $(SMBD_OBJ_MAIN)
+NMBD_OBJ1 = nmbd/asyncdns.o nmbd/nmbd.o nmbd/nmbd_become_dmb.o \
+ nmbd/nmbd_become_lmb.o nmbd/nmbd_browserdb.o \
+ nmbd/nmbd_browsesync.o nmbd/nmbd_elections.o \
+ nmbd/nmbd_incomingdgrams.o nmbd/nmbd_incomingrequests.o \
+ nmbd/nmbd_lmhosts.o nmbd/nmbd_logonnames.o nmbd/nmbd_mynames.o \
+ nmbd/nmbd_namelistdb.o nmbd/nmbd_namequery.o \
+ nmbd/nmbd_nameregister.o nmbd/nmbd_namerelease.o \
+ nmbd/nmbd_nodestatus.o nmbd/nmbd_packets.o \
+ nmbd/nmbd_processlogon.o nmbd/nmbd_responserecordsdb.o \
+ nmbd/nmbd_sendannounce.o nmbd/nmbd_serverlistdb.o \
+ nmbd/nmbd_subnetdb.o nmbd/nmbd_winsproxy.o nmbd/nmbd_winsserver.o \
+ nmbd/nmbd_workgroupdb.o nmbd/nmbd_synclists.o
+
+NMBD_OBJ = $(NMBD_OBJ1) $(PARAM_OBJ) $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) $(UBIQX_OBJ) \
+ $(PROFILE_OBJ) $(LIB_NONSMBD_OBJ) $(SECRETS_OBJ) $(POPT_LIB_OBJ)
+
+WREPL_OBJ1 = wrepld/server.o wrepld/process.o wrepld/parser.o wrepld/socket.o \
+ wrepld/partners.o
+
+WREPL_OBJ = $(WREPL_OBJ1) $(PARAM_OBJ) $(UBIQX_OBJ) \
+ $(PROFILE_OBJ) $(LIB_NONSMBD_OBJ) $(POPT_LIB_OBJ) $(SECRETS_OBJ) \
+ $(LIBSAMBA_OBJ)
+
+SWAT_OBJ1 = web/cgi.o web/diagnose.o web/startstop.o web/statuspage.o \
+ web/swat.o web/neg_lang.o
+
+SWAT_OBJ = $(SWAT_OBJ1) $(PARAM_OBJ) $(PRINTING_OBJ) $(LIBSMB_OBJ) \
+ $(LOCKING_OBJ) $(PASSDB_OBJ) $(SECRETS_OBJ) $(KRBCLIENT_OBJ) \
+ $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ) $(GROUPDB_OBJ) $(PLAINTEXT_AUTH_OBJ) \
+ $(POPT_LIB_OBJ) $(SMBLDAP_OBJ) $(RPC_PARSE_OBJ) $(LIBMSRPC_OBJ) \
+ libsmb/passchange.o lib/dummyroot.o
+
+SMBSH_OBJ = smbwrapper/smbsh.o smbwrapper/shared.o \
+ $(PARAM_OBJ) $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ)
+
+STATUS_OBJ = utils/status.o $(LOCKING_OBJ) $(PARAM_OBJ) \
+ $(UBIQX_OBJ) $(PROFILE_OBJ) $(LIB_NONSMBD_OBJ) $(POPT_LIB_OBJ) \
+ $(SECRETS_OBJ) $(LIBSAMBA_OBJ) lib/dummyroot.o libsmb/errormap.o
+
+
+SMBCONTROL_OBJ = utils/smbcontrol.o $(LOCKING_OBJ) $(PARAM_OBJ) \
+ $(UBIQX_OBJ) $(PROFILE_OBJ) $(LIB_NONSMBD_OBJ) $(POPT_LIB_OBJ) \
+ $(SECRETS_OBJ) $(LIBSAMBA_OBJ) \
+ printing/notify.o printing/printing_db.o lib/dummyroot.o libsmb/errormap.o
+
+SMBTREE_OBJ = utils/smbtree.o $(PARAM_OBJ) \
+ $(UBIQX_OBJ) $(PROFILE_OBJ) $(LIB_NONSMBD_OBJ) $(LIBSMB_OBJ) \
+ $(KRBCLIENT_OBJ) $(POPT_LIB_OBJ) $(SECRETS_OBJ)
+
+TESTPARM_OBJ = utils/testparm.o \
+ $(PARAM_OBJ) $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ) $(POPT_LIB_OBJ) \
+ $(SECRETS_OBJ) $(LIBSAMBA_OBJ)
+
+TESTPRNS_OBJ = utils/testprns.o $(PARAM_OBJ) $(PRINTING_OBJ) $(UBIQX_OBJ) \
+ $(LIB_NONSMBD_OBJ)
+
+SMBPASSWD_OBJ = utils/smbpasswd.o libsmb/passchange.o $(PARAM_OBJ) $(SECRETS_OBJ) \
+ $(LIBSMB_OBJ) $(PASSDB_OBJ) $(GROUPDB_OBJ)\
+ $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ) $(KRBCLIENT_OBJ) \
+ $(SMBLDAP_OBJ) $(RPC_PARSE_OBJ) $(LIBMSRPC_OBJ) lib/dummyroot.o
+
+PDBEDIT_OBJ = utils/pdbedit.o $(PARAM_OBJ) $(PASSDB_OBJ) $(LIBSMB_OBJ) $(LIBMSRPC_OBJ) \
+ $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ) $(GROUPDB_OBJ) $(SECRETS_OBJ) \
+ $(POPT_LIB_OBJ) $(SMBLDAP_OBJ) $(KRBCLIENT_OBJ) $(RPC_PARSE_OBJ) lib/dummyroot.o
+
+SMBGET_OBJ = utils/smbget.o $(POPT_LIB_OBJ) $(LIBSMBCLIENT_OBJ) $(SECRETS_OBJ)
+
+RPCCLIENT_OBJ1 = rpcclient/rpcclient.o rpcclient/cmd_lsarpc.o \
+ rpcclient/cmd_samr.o rpcclient/cmd_spoolss.o \
+ rpcclient/cmd_netlogon.o rpcclient/cmd_srvsvc.o \
+ rpcclient/cmd_dfs.o rpcclient/cmd_reg.o \
+ rpcclient/display_sec.o rpcclient/cmd_ds.o \
+ rpcclient/cmd_echo.o rpcclient/cmd_shutdown.o \
+ rpcclient/cmd_epmapper.o
+
+RPCCLIENT_OBJ = $(RPCCLIENT_OBJ1) \
+ $(PARAM_OBJ) $(LIBSMB_OBJ) $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ) \
+ $(RPC_PARSE_OBJ) $(PASSDB_OBJ) $(LIBMSRPC_OBJ) \
+ $(READLINE_OBJ) $(GROUPDB_OBJ) $(KRBCLIENT_OBJ) \
+ $(LIBADS_OBJ) $(SECRETS_OBJ) $(POPT_LIB_OBJ) \
+ $(SMBLDAP_OBJ) $(DCUTIL_OBJ) lib/dummyroot.o
+
+PAM_WINBIND_PICOBJ = nsswitch/pam_winbind.@PICSUFFIX@ \
+ nsswitch/wb_common.@PICSUFFIX@ lib/replace1.@PICSUFFIX@ \
+ lib/snprintf.@PICSUFFIX@
+
+SMBW_OBJ1 = smbwrapper/smbw.o \
+ smbwrapper/smbw_dir.o smbwrapper/smbw_stat.o \
+ smbwrapper/realcalls.o smbwrapper/shared.o \
+ smbwrapper/smbw_cache.o
+
+SMBW_OBJ = $(SMBW_OBJ1) $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) $(PARAM_OBJ) \
+ $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ)
+
+SMBWRAPPER_OBJ1 = smbwrapper/wrapped.o
+
+SMBWRAPPER_OBJ = $(SMBW_OBJ) $(SMBWRAPPER_OBJ1)
+
+LIBSMBCLIENT_OBJ = libsmb/libsmbclient.o libsmb/libsmb_compat.o \
+ libsmb/libsmb_cache.o \
+ $(PARAM_OBJ) $(LIB_NONSMBD_OBJ) \
+ $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) $(UBIQX_OBJ) \
+ $(LIBMSRPC_OBJ) $(RPC_PARSE_OBJ) $(PASSDB_GET_SET_OBJ)
+
+# This shared library is intended for linking with unit test programs
+# to test Samba internals. It's called libbigballofmud.so to
+# discourage casual usage.
+
+LIBBIGBALLOFMUD_MAJOR = 0
+
+LIBBIGBALLOFMUD_OBJ = $(PARAM_OBJ) $(LIB_NONSMBD_OBJ) $(UBIQX_OBJ) $(SECRETS_OBJ) \
+ $(LIBSMB_OBJ) $(LIBMSRPC_OBJ) $(RPC_PARSE_OBJ) $(PASSDB_OBJ) \
+ $(GROUPDB_OBJ) $(KRBCLIENT_OBJ) $(SMBLDAP_OBJ) lib/dummyroot.o
+
+LIBBIGBALLOFMUD_PICOBJS = $(LIBBIGBALLOFMUD_OBJ:.o=.@PICSUFFIX@)
+
+CLIENT_OBJ1 = client/client.o client/clitar.o
+
+CLIENT_OBJ = $(CLIENT_OBJ1) $(PARAM_OBJ) $(LIBSMB_OBJ) $(UBIQX_OBJ) \
+ $(LIB_NONSMBD_OBJ) $(KRBCLIENT_OBJ) \
+ $(READLINE_OBJ) $(POPT_LIB_OBJ) $(SECRETS_OBJ)
+
+NET_OBJ1 = utils/net.o utils/net_ads.o utils/net_ads_cldap.o utils/net_help.o \
+ utils/net_rap.o utils/net_rpc.o utils/net_rpc_samsync.o \
+ utils/net_rpc_join.o utils/net_time.o utils/net_lookup.o \
+ utils/net_cache.o utils/net_groupmap.o utils/net_idmap.o \
+ utils/net_status.o utils/net_privileges.o
+
+NET_OBJ = $(NET_OBJ1) $(PARAM_OBJ) $(SECRETS_OBJ) $(LIBSMB_OBJ) \
+ $(RPC_PARSE_OBJ) $(PASSDB_OBJ) $(GROUPDB_OBJ) \
+ $(KRBCLIENT_OBJ) $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ) \
+ $(LIBMSRPC_OBJ) $(IDMAP_OBJ) \
+ $(LIBADS_OBJ) $(LIBADS_SERVER_OBJ) $(POPT_LIB_OBJ) \
+ $(SMBLDAP_OBJ) $(DCUTIL_OBJ) lib/dummyroot.o lib/server_mutex.o lib/afs.o
+
+CUPS_OBJ = client/smbspool.o $(PARAM_OBJ) $(LIBSMB_OBJ) $(UBIQX_OBJ) \
+ $(LIB_NONSMBD_OBJ) $(KRBCLIENT_OBJ) $(SECRETS_OBJ)
+
+MOUNT_OBJ = client/smbmount.o \
+ $(PARAM_OBJ) $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ)
+
+MNT_OBJ = client/smbmnt.o lib/version.o lib/snprintf.o
+
+UMOUNT_OBJ = client/smbumount.o
+
+NMBLOOKUP_OBJ = utils/nmblookup.o $(PARAM_OBJ) $(UBIQX_OBJ) $(LIBNMB_OBJ) \
+ $(LIB_NONSMBD_OBJ) $(POPT_LIB_OBJ) $(SECRETS_OBJ) $(LIBSAMBA_OBJ)
+
+SMBTORTURE_OBJ1 = torture/torture.o torture/nbio.o torture/scanner.o torture/utable.o \
+ torture/denytest.o torture/mangle_test.o
+
+SMBTORTURE_OBJ = $(SMBTORTURE_OBJ1) $(PARAM_OBJ) \
+ $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ)
+
+MASKTEST_OBJ = torture/masktest.o $(PARAM_OBJ) $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) \
+ $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ)
+
+MSGTEST_OBJ = torture/msgtest.o $(PARAM_OBJ) $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) \
+ $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ)
+
+LOCKTEST_OBJ = torture/locktest.o $(PARAM_OBJ) $(LOCKING_OBJ) $(KRBCLIENT_OBJ) \
+ $(LIBSMB_OBJ) $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ) lib/dummyroot.o
+
+NSSTEST_OBJ = torture/nsstest.o $(PARAM_OBJ) $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) \
+ $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ)
+
+VFSTEST_OBJ = torture/cmd_vfs.o torture/vfstest.o $(SMBD_OBJ_BASE) $(READLINE_OBJ)
+
+SMBICONV_OBJ = $(PARAM_OBJ) torture/smbiconv.o $(LIB_NONSMBD_OBJ) $(UBIQX_OBJ) $(POPT_LIB_OBJ) $(SECRETS_OBJ) $(LIBSAMBA_OBJ)
+
+LOG2PCAP_OBJ = utils/log2pcaphex.o
+
+LOCKTEST2_OBJ = torture/locktest2.o $(PARAM_OBJ) $(LOCKING_OBJ) $(LIBSMB_OBJ) \
+ $(KRBCLIENT_OBJ) $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ) lib/dummyroot.o
+
+SMBCACLS_OBJ = utils/smbcacls.o $(PARAM_OBJ) $(LIBSMB_OBJ) \
+ $(KRBCLIENT_OBJ) $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ) $(RPC_PARSE_OBJ) \
+ $(PASSDB_GET_SET_OBJ) $(LIBMSRPC_OBJ) $(SECRETS_OBJ) \
+ $(POPT_LIB_OBJ) $(DCUTIL_OBJ) $(LIBADS_OBJ)
+
+SMBCQUOTAS_OBJ = utils/smbcquotas.o $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) \
+ $(PARAM_OBJ) \
+ $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ) $(RPC_PARSE_OBJ) $(PASSDB_GET_SET_OBJ) \
+ $(LIBMSRPC_OBJ) $(SECRETS_OBJ) $(POPT_LIB_OBJ)
+
+TALLOCTORT_OBJ = lib/talloctort.o $(PARAM_OBJ) $(LIB_NONSMBD_OBJ) $(UBIQX_OBJ)
+
+RPCTORTURE_OBJ = torture/rpctorture.o \
+ rpcclient/display.o \
+ rpcclient/cmd_lsarpc.o \
+ rpcclient/cmd_wkssvc.o \
+ rpcclient/cmd_samr.o \
+ rpcclient/cmd_srvsvc.o \
+ rpcclient/cmd_netlogon.o \
+ $(PARAM_OBJ) $(LIBSMB_OBJ) $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ) $(KRBCLIENT_OBJ) \
+ $(RPC_CLIENT_OBJ) $(RPC_PARSE_OBJ) $(PASSDB_GET_SET_OBJ)
+
+DEBUG2HTML_OBJ = utils/debug2html.o ubiqx/debugparse.o
+
+SMBFILTER_OBJ = utils/smbfilter.o $(PARAM_OBJ) $(LIBSMB_OBJ) \
+ $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ) $(KRBCLIENT_OBJ)
+
+PROTO_OBJ = $(SMBD_OBJ_MAIN) \
+ $(SMBD_OBJ_SRV) $(NMBD_OBJ1) $(SWAT_OBJ1) $(LIB_OBJ) $(LIBSMB_OBJ) \
+ $(SMBW_OBJ1) $(SMBWRAPPER_OBJ1) $(SMBTORTURE_OBJ1) $(RPCCLIENT_OBJ1) \
+ $(LIBMSRPC_OBJ) $(RPC_CLIENT_OBJ) \
+ $(RPC_PIPE_OBJ) $(RPC_PARSE_OBJ) $(KRBCLIENT_OBJ) \
+ $(AUTH_OBJ) $(PARAM_OBJ) $(LOCKING_OBJ) $(SECRETS_OBJ) \
+ $(PRINTING_OBJ) $(PRINTBACKEND_OBJ) $(OPLOCK_OBJ) $(NOTIFY_OBJ) \
+ $(PASSDB_OBJ) $(GROUPDB_OBJ) $(MSDFS_OBJ) \
+ $(READLINE_OBJ) $(PROFILE_OBJ) $(LIBADS_OBJ) $(LIBADS_SERVER_OBJ) \
+ $(LIB_SMBD_OBJ) $(AUTH_SAM_OBJ) $(REGISTRY_OBJ) $(POPT_LIB_OBJ) \
+ $(RPC_LSA_OBJ) $(RPC_NETLOG_OBJ) $(RPC_SAMR_OBJ) $(RPC_REG_OBJ) \
+ $(RPC_LSA_DS_OBJ) $(RPC_SVC_OBJ) $(RPC_WKS_OBJ) $(RPC_DFS_OBJ) \
+ $(RPC_SPOOLSS_OBJ) $(RPC_ECHO_OBJ) $(RPC_EPMAPPER_OBJ) \
+ $(SMBLDAP_OBJ) $(IDMAP_OBJ) libsmb/spnego.o libsmb/passchange.o
+
+WINBIND_WINS_NSS_OBJ = nsswitch/wins.o $(PARAM_OBJ) $(UBIQX_OBJ) \
+ $(LIBSMB_OBJ) $(LIB_NONSMBD_OBJ) $(NSSWINS_OBJ) $(KRBCLIENT_OBJ)
+
+WINBIND_WINS_NSS_PICOBJS = $(WINBIND_WINS_NSS_OBJ:.o=.@PICSUFFIX@)
+
+PICOBJS = $(SMBWRAPPER_OBJ:.o=.@PICSUFFIX@)
+LIBSMBCLIENT_PICOBJS = $(LIBSMBCLIENT_OBJ:.o=.@PICSUFFIX@)
+
+PAM_SMBPASS_OBJ_0 = pam_smbpass/pam_smb_auth.o pam_smbpass/pam_smb_passwd.o \
+ pam_smbpass/pam_smb_acct.o pam_smbpass/support.o \
+ lib/dummyroot.o $(PARAM_OBJ) $(LIB_NONSMBD_OBJ) $(PASSDB_OBJ) $(GROUPDB_OBJ) \
+ $(SECRETS_OBJ) $(UBIQX_OBJ) $(SMBLDAP_OBJ) $(LIBSAMBA_OBJ)
+
+PAM_SMBPASS_PICOOBJ = $(PAM_SMBPASS_OBJ_0:.o=.@PICSUFFIX@)
+
+IDMAP_OBJ = sam/idmap.o sam/idmap_util.o @IDMAP_STATIC@
+
+WINBINDD_OBJ1 = \
+ nsswitch/winbindd.o \
+ nsswitch/winbindd_user.o \
+ nsswitch/winbindd_group.o \
+ nsswitch/winbindd_util.o \
+ nsswitch/winbindd_cache.o \
+ nsswitch/winbindd_pam.o \
+ nsswitch/winbindd_sid.o \
+ nsswitch/winbindd_misc.o \
+ nsswitch/winbindd_cm.o \
+ nsswitch/winbindd_wins.o \
+ nsswitch/winbindd_rpc.o \
+ nsswitch/winbindd_ads.o \
+ nsswitch/winbindd_passdb.o \
+ nsswitch/winbindd_dual.o \
+ nsswitch/winbindd_acct.o
+
+WINBINDD_OBJ = \
+ $(WINBINDD_OBJ1) $(PASSDB_OBJ) $(GROUPDB_OBJ) \
+ $(PARAM_OBJ) $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ) \
+ $(LIBSMB_OBJ) $(LIBMSRPC_OBJ) $(RPC_PARSE_OBJ) \
+ $(PROFILE_OBJ) $(SLCACHE_OBJ) $(SMBLDAP_OBJ) \
+ $(SECRETS_OBJ) $(LIBADS_OBJ) $(KRBCLIENT_OBJ) $(POPT_LIB_OBJ) \
+ $(DCUTIL_OBJ) $(IDMAP_OBJ) lib/dummyroot.o lib/afs.o
+
+WBINFO_OBJ = nsswitch/wbinfo.o $(LIBSAMBA_OBJ) $(PARAM_OBJ) $(LIB_NONSMBD_OBJ) \
+ $(UBIQX_OBJ) $(SECRETS_OBJ) $(POPT_LIB_OBJ) lib/afs.o
+
+WINBIND_NSS_OBJ = nsswitch/wb_common.o lib/replace1.o @WINBIND_NSS_EXTRA_OBJS@
+
+WINBIND_NSS_PICOBJS = $(WINBIND_NSS_OBJ:.o=.@PICSUFFIX@) lib/snprintf.@PICSUFFIX@
+
+POPT_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \
+ popt/popthelp.o popt/poptparse.o
+
+TDBBACKUP_OBJ = tdb/tdbbackup.o tdb/tdbback.o lib/snprintf.o $(TDBBASE_OBJ)
+
+TDBDUMP_OBJ = tdb/tdbdump.o $(TDBBASE_OBJ)
+
+NTLM_AUTH_OBJ = utils/ntlm_auth.o $(LIBSAMBA_OBJ) $(POPT_LIB_OBJ) \
+ libsmb/asn1.o libsmb/spnego.o libsmb/clikrb5.o libads/kerberos.o \
+ libads/kerberos_verify.o $(SECRETS_OBJ) lib/server_mutex.o \
+ libads/authdata.o rpc_parse/parse_prs.o rpc_parse/parse_misc.o \
+ libsmb/doserr.o
+
+######################################################################
+# now the rules...
+######################################################################
+all : SHOWFLAGS proto_exists $(SBIN_PROGS) $(BIN_PROGS) $(SHLIBS) \
+ $(MODULES) @EXTRA_ALL_TARGETS@
+
+pam_smbpass : SHOWFLAGS bin/pam_smbpass.@SHLIBEXT@
+
+smbwrapper : SHOWFLAGS @SMBWRAPPER@
+
+torture : SHOWFLAGS $(TORTURE_PROGS)
+
+smbtorture : SHOWFLAGS bin/smbtorture@EXEEXT@
+
+masktest : SHOWFLAGS bin/masktest@EXEEXT@
+
+msgtest : SHOWFLAGS bin/msgtest@EXEEXT@
+
+locktest : SHOWFLAGS bin/locktest@EXEEXT@
+
+smbcacls : SHOWFLAGS bin/smbcacls@EXEEXT@
+
+smbcquotas : SHOWFLAGS bin/smbcquotas@EXEEXT@
+
+locktest2 : SHOWFLAGS bin/locktest2@EXEEXT@
+
+rpctorture : SHOWFLAGS bin/rpctorture@EXEEXT@
+
+debug2html : SHOWFLAGS bin/debug2html@EXEEXT@
+
+smbfilter : SHOWFLAGS bin/smbfilter@EXEEXT@
+
+talloctort : SHOWFLAGS bin/talloctort@EXEEXT@
+
+nsswitch : SHOWFLAGS bin/winbindd@EXEEXT@ bin/wbinfo@EXEEXT@ @WINBIND_NSS@ \
+ @WINBIND_WINS_NSS@ nsswitch/pam_winbind.@SHLIBEXT@
+
+wins : SHOWFLAGS @WINBIND_WINS_NSS@
+
+modules: SHOWFLAGS proto_exists $(MODULES)
+
+everything: all libsmbclient debug2html smbfilter talloctort modules torture \
+ $(EVERYTHING_PROGS)
+
+.SUFFIXES:
+.SUFFIXES: .c .o .@PICSUFFIX@ .lo
+
+SHOWFLAGS:
+ @echo "Using FLAGS = $(FLAGS)"
+ @echo " LIBS = $(LIBS)"
+ @echo " LDSHFLAGS = $(LDSHFLAGS)"
+ @echo " LDFLAGS = $(LDFLAGS)"
+
+MAKEDIR = || exec false; \
+ if test -d "$$dir"; then :; else \
+ echo mkdir "$$dir"; \
+ mkdir -p "$$dir" >/dev/null 2>&1 || \
+ test -d "$$dir" || \
+ mkdir "$$dir" || \
+ exec false; fi || exec false
+
+.c.o:
+ @if (: >> $@ || : > $@) >/dev/null 2>&1; then rm -f $@; else \
+ dir=`echo $@ | sed 's,/[^/]*$$,,;s,^$$,.,'` $(MAKEDIR); fi
+ @echo Compiling $*.c
+ @$(CC) -I. -I$(srcdir) $(FLAGS) -c $< \
+ -o $@
+@BROKEN_CC@ -mv `echo $@ | sed 's%^.*/%%g'` $@
+
+# this adds support for precompiled headers. To use it, install a snapshot
+# of gcc-3.4 and run 'make pch' before you do the main build.
+pch:
+ rm -f $(srcdir)/include/includes.h.gch
+ $(CC) -I. -I$(srcdir) $(FLAGS) -c $(srcdir)/include/includes.h -o $(srcdir)/include/includes.h.gch
+
+# These dependencies are only approximately correct: we want to make
+# sure Samba's paths are updated if ./configure is re-run. Really it
+# would be nice if "make prefix=/opt/samba all" also rebuilt things,
+# but since we also require "make install prefix=/opt/samba" *not* to
+# rebuild it's a bit hard.
+
+dynconfig.o: dynconfig.c Makefile
+ @echo Compiling $*.c
+ @$(CC) $(FLAGS) $(PATH_FLAGS) -c $< -o $@
+
+dynconfig.@PICSUFFIX@: dynconfig.c Makefile
+ @if (: >> $@ || : > $@) >/dev/null 2>&1; then rm -f $@; else \
+ dir=`echo $@ | sed 's,/[^/]*$$,,;s,^$$,.,'` $(MAKEDIR); fi
+ @echo Compiling $*.c with @PICFLAGS@
+ @$(CC) -I. -I$(srcdir) $(FLAGS) $(PATH_FLAGS) @PICFLAGS@ -c $< -o $*.@PICSUFFIX@
+@BROKEN_CC@ -mv `echo $@ | sed -e 's%^.*/%%g' -e 's%\.@PICSUFFIX@$$%.o%'` $@
+
+lib/version.o: lib/version.c include/version.h
+ @echo Compiling $*.c
+ @$(CC) -I. -I$(srcdir) $(FLAGS) $(PATH_FLAGS) -c $< -o $@
+
+lib/version.@PICSUFFIX@: lib/version.c include/version.h
+ @if (: >> $@ || : > $@) >/dev/null 2>&1; then rm -f $@; else \
+ dir=`echo $@ | sed 's,/[^/]*$$,,;s,^$$,.,'` $(MAKEDIR); fi
+ @echo Compiling $*.c with @PICFLAGS@
+ @$(CC) -I. -I$(srcdir) $(FLAGS) $(PATH_FLAGS) @PICFLAGS@ -c $< -o $*.@PICSUFFIX@
+@BROKEN_CC@ -mv `echo $@ | sed -e 's%^.*/%%g' -e 's%\.@PICSUFFIX@$$%.o%'` $@
+
+smbd/build_options.o: smbd/build_options.c Makefile include/config.h include/build_env.h include/proto.h
+ @echo Compiling $*.c
+ @$(CC) $(FLAGS) $(PATH_FLAGS) -c $< -o $@
+
+smbd/build_options.c: include/config.h.in script/mkbuildoptions.awk
+ @echo Generating $@
+ @dir=smbd $(MAKEDIR) && $(AWK) -f $(srcdir)/script/mkbuildoptions.awk > $(builddir)/smbd/build_options.c < $(srcdir)/include/config.h.in
+
+.c.@PICSUFFIX@:
+ @if (: >> $@ || : > $@) >/dev/null 2>&1; then rm -f $@; else \
+ dir=`echo $@ | sed 's,/[^/]*$$,,;s,^$$,.,'` $(MAKEDIR); fi
+ @echo Compiling $*.c with @PICFLAGS@
+ @$(CC) -I. -I$(srcdir) $(FLAGS) @PICFLAGS@ -c $< -o $*.@PICSUFFIX@
+@BROKEN_CC@ -mv `echo $@ | sed -e 's%^.*/%%g' -e 's%\.@PICSUFFIX@$$%.o%'` $@
+
+bin/.dummy:
+ @if (: >> $@ || : > $@) >/dev/null 2>&1; then :; else \
+ dir=bin $(MAKEDIR); fi
+ @: >> $@ || : > $@ # what a fancy emoticon!
+
+bin/smbd@EXEEXT@: $(SMBD_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SMBD_OBJ) $(LDFLAGS) $(LDAP_LIBS) \
+ $(KRB5LIBS) $(DYNEXP) $(PRINT_LIBS) $(AUTH_LIBS) \
+ $(ACL_LIBS) $(PASSDB_LIBS) $(LIBS) @POPTLIBS@
+
+bin/nmbd@EXEEXT@: $(NMBD_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(NMBD_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) @POPTLIBS@ $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/wrepld@EXEEXT@: $(WREPL_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(WREPL_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) @POPTLIBS@
+
+bin/swat@EXEEXT@: $(SWAT_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SWAT_OBJ) $(LDFLAGS) $(DYNEXP) $(PRINT_LIBS) \
+ $(AUTH_LIBS) $(LIBS) $(PASSDB_LIBS) @POPTLIBS@ $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/rpcclient@EXEEXT@: $(RPCCLIENT_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(LDFLAGS) $(PASSDB_LIBS) $(RPCCLIENT_OBJ) \
+ $(DYNEXP) $(TERMLDFLAGS) $(TERMLIBS) $(LIBS) @POPTLIBS@ \
+ $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/smbclient@EXEEXT@: $(CLIENT_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(CLIENT_OBJ) $(LDFLAGS) $(DYNEXP) $(TERMLDFLAGS) $(TERMLIBS) $(LIBS) @POPTLIBS@ $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/net@EXEEXT@: $(NET_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(NET_OBJ) $(DYNEXP) $(LDFLAGS) $(LIBS) @POPTLIBS@ $(KRB5LIBS) $(LDAP_LIBS) $(PASSDB_LIBS)
+
+bin/profiles@EXEEXT@: $(PROFILES_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(PROFILES_OBJ) $(LDFLAGS) $(LIBS) @POPTLIBS@
+
+bin/editreg@EXEEXT@: $(EDITREG_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(EDITREG_OBJ) $(LDFLAGS) $(LIBS) @POPTLIBS@
+
+bin/smbspool@EXEEXT@: $(CUPS_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(CUPS_OBJ) $(LDFLAGS) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/smbmount@EXEEXT@: $(MOUNT_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(MOUNT_OBJ) $(LDFLAGS) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/smbmnt@EXEEXT@: $(MNT_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(MNT_OBJ) $(LDFLAGS)
+
+bin/smbumount@EXEEXT@: $(UMOUNT_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(UMOUNT_OBJ) $(LDFLAGS)
+
+bin/testparm@EXEEXT@: $(TESTPARM_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(TESTPARM_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) @POPTLIBS@
+
+bin/testprns@EXEEXT@: $(TESTPRNS_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(TESTPRNS_OBJ) $(LDFLAGS) $(DYNEXP) $(PRINT_LIBS) $(LIBS)
+
+bin/smbstatus@EXEEXT@: $(STATUS_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(STATUS_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) \
+ @POPTLIBS@
+
+bin/smbcontrol@EXEEXT@: $(SMBCONTROL_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) -DUSING_SMBCONTROL $(FLAGS) -o $@ $(SMBCONTROL_OBJ) $(DYNEXP) \
+ $(LDFLAGS) $(LIBS) \
+ @POPTLIBS@
+
+bin/smbtree@EXEEXT@: $(SMBTREE_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SMBTREE_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) @POPTLIBS@ $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/smbpasswd@EXEEXT@: $(SMBPASSWD_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SMBPASSWD_OBJ) $(LDFLAGS) $(PASSDB_LIBS) \
+ $(DYNEXP) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/pdbedit@EXEEXT@: $(PDBEDIT_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(PDBEDIT_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) @POPTLIBS@ $(PASSDB_LIBS) $(LDAP_LIBS) $(KRB5LIBS)
+
+bin/smbget@EXEEXT@: $(SMBGET_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SMBGET_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) @POPTLIBS@ $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/samtest@EXEEXT@: $(SAMTEST_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SAMTEST_OBJ) $(LDFLAGS) $(TERMLDFLAGS) $(TERMLIBS) $(DYNEXP) $(LIBS) @POPTLIBS@ $(PASSDB_LIBS) $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/nmblookup@EXEEXT@: $(NMBLOOKUP_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(NMBLOOKUP_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) @POPTLIBS@ $(LDAP_LIBS)
+
+bin/smbtorture@EXEEXT@: $(SMBTORTURE_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SMBTORTURE_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS) $(SECRETS_OBJ)
+
+bin/talloctort@EXEEXT@: $(TALLOCTORT_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(TALLOCTORT_OBJ) $(LDFLAGS) $(LIBS)
+
+bin/masktest@EXEEXT@: $(MASKTEST_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(MASKTEST_OBJ) $(LDFLAGS) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS) $(SECRETS_OBJ)
+
+bin/msgtest@EXEEXT@: $(MSGTEST_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(MSGTEST_OBJ) $(LDFLAGS) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/smbcacls@EXEEXT@: $(SMBCACLS_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SMBCACLS_OBJ) $(DYNEXP) $(LDFLAGS) $(LIBS) @POPTLIBS@ $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/smbcquotas@EXEEXT@: $(SMBCQUOTAS_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SMBCQUOTAS_OBJ) $(DYNEXP) $(LDFLAGS) $(LIBS) @POPTLIBS@ $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/locktest@EXEEXT@: $(LOCKTEST_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(LOCKTEST_OBJ) $(LDFLAGS) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/nsstest@EXEEXT@: $(NSSTEST_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(NSSTEST_OBJ) $(LDFLAGS) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/vfstest@EXEEXT@: $(VFSTEST_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(VFSTEST_OBJ) $(LDFLAGS) $(TERMLDFLAGS) $(TERMLIBS) $(DYNEXP) $(PRINT_LIBS) $(AUTH_LIBS) $(ACL_LIBS) $(LIBS) @POPTLIBS@ $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/smbiconv@EXEEXT@: $(SMBICONV_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SMBICONV_OBJ) $(LDFLAGS) $(TERMLDFLAGS) $(TERMLIBS) $(DYNEXP) $(LIBS) @POPTLIBS@
+
+bin/log2pcap@EXEEXT@: $(LOG2PCAP_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(LOG2PCAP_OBJ) $(LDFLAGS) @POPTLIBS@ $(LIBS)
+
+bin/locktest2@EXEEXT@: $(LOCKTEST2_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(LOCKTEST2_OBJ) $(LDFLAGS) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/rpctorture@EXEEXT@: $(RPCTORTURE_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(RPCTORTURE_OBJ) $(DYNEXP) $(LDFLAGS) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/debug2html@EXEEXT@: $(DEBUG2HTML_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(DEBUG2HTML_OBJ) $(LDFLAGS) $(LIBS)
+
+bin/smbfilter@EXEEXT@: $(SMBFILTER_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SMBFILTER_OBJ) $(LDFLAGS) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/smbw_sample@EXEEXT@: $(SMBW_OBJ) utils/smbw_sample.o bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SMBW_OBJ) utils/smbw_sample.o $(LDFLAGS) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/smbsh@EXEEXT@: $(SMBSH_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SMBSH_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS)
+
+bin/smbwrapper.@SHLIBEXT@: $(PICOBJS) bin/.dummy
+ @echo Linking shared library $@
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(PICOBJS) $(LIBS) \
+ $(KRB5LIBS) $(LDAP_LIBS) \
+ @SONAMEFLAG@`basename $@`
+
+bin/libsmbclient.@SHLIBEXT@: $(LIBSMBCLIENT_PICOBJS)
+ @echo Linking libsmbclient shared library $@
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(LIBSMBCLIENT_PICOBJS) $(LDFLAGS) $(DYNEXP) $(LIBS) \
+ $(KRB5LIBS) $(LDAP_LIBS) \
+ @SONAMEFLAG@`basename $@`.$(LIBSMBCLIENT_MAJOR)
+
+bin/libsmbclient.a: $(LIBSMBCLIENT_PICOBJS)
+ @echo Linking libsmbclient non-shared library $@
+ @-$(AR) -rc $@ $(LIBSMBCLIENT_PICOBJS)
+
+# This is probably wrong for anything other than the GNU linker.
+bin/libbigballofmud.@SHLIBEXT@: $(LIBBIGBALLOFMUD_PICOBJS)
+ @echo Linking bigballofmud shared library $@
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(LIBBIGBALLOFMUD_PICOBJS) $(LIBS) \
+ $(PASSDB_LIBS) $(IDMAP_LIBS) $(KRB5LIBS) $(LDAP_LIBS) \
+ @SONAMEFLAG@`basename $@`.$(LIBBIGBALLOFMUD_MAJOR)
+ ln -snf libbigballofmud.so bin/libbigballofmud.so.0
+
+# It would be nice to build a static bigballofmud too, but when I try
+# I get linker errors about dl_open and similar things. I'm not sure if
+# it can be fixed or if they just can't be called from a static
+# library.
+
+libsmbclient: $(LIBSMBCLIENT)
+
+bin/librpc_lsarpc.@SHLIBEXT@: $(RPC_LSA_OBJ)
+ @echo "Linking $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_LSA_OBJ) -lc \
+ @SONAMEFLAG@`basename $@`
+
+bin/librpc_samr.@SHLIBEXT@: $(RPC_SAMR_OBJ)
+ @echo "Linking $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_SAMR_OBJ) -lc \
+ @SONAMEFLAG@`basename $@`
+
+bin/librpc_srvsvc.@SHLIBEXT@: $(RPC_SVC_OBJ)
+ @echo "Linking $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_SVC_OBJ) -lc \
+ @SONAMEFLAG@`basename $@`
+
+bin/librpc_wkssvc.@SHLIBEXT@: $(RPC_WKS_OBJ)
+ @echo "Linking $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_WKS_OBJ) -lc \
+ @SONAMEFLAG@`basename $@`
+
+bin/librpc_NETLOGON.@SHLIBEXT@: $(RPC_NETLOG_OBJ)
+ @echo "Linking $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_NETLOG_OBJ) -lc \
+ @SONAMEFLAG@`basename $@`
+
+bin/librpc_winreg.@SHLIBEXT@: $(RPC_REG_OBJ)
+ @echo "Linking $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_REG_OBJ) -lc \
+ @SONAMEFLAG@`basename $@`
+
+bin/librpc_lsa_ds.@SHLIBEXT@: $(RPC_LSA_DS_OBJ)
+ @echo "Linking $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_LSA_DS_OBJ) -lc \
+ @SONAMEFLAG@`basename $@`
+
+bin/librpc_spoolss.@SHLIBEXT@: $(RPC_SPOOLSS_OBJ)
+ @echo "Linking $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_SPOOLSS_OBJ) -lc \
+ @SONAMEFLAG@`basename $@`
+
+bin/librpc_netdfs.@SHLIBEXT@: $(RPC_DFS_OBJ)
+ @echo "Linking $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_DFS_OBJ) -lc \
+ @SONAMEFLAG@`basename $@`
+
+bin/librpc_echo.@SHLIBEXT@: $(RPC_ECHO_OBJ)
+ @echo "Linking $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_ECHO_OBJ) -lc \
+ @SONAMEFLAG@`basename $@`
+
+bin/librpc_epmapper.@SHLIBEXT@: $(RPC_EPMAPPER_OBJ)
+ @echo "Linking $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_EPMAPPER_OBJ) -lc \
+ @SONAMEFLAG@`basename $@`
+
+bin/winbindd@EXEEXT@: $(WINBINDD_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo "Linking $@"
+ @$(LINK) -o $@ $(WINBINDD_OBJ) $(DYNEXP) $(LIBS) @POPTLIBS@ $(KRB5LIBS) $(LDAP_LIBS) $(PASSDB_LIBS)
+
+# Please don't add .o files to libnss_winbind, libnss_wins, or the pam_winbind
+# libraries. Add to the appropriate PICOBJ variable instead.
+
+@WINBIND_NSS@: $(WINBIND_NSS_PICOBJS)
+ @echo "Linking $@"
+ @$(SHLD) $(WINBIND_NSS_LDSHFLAGS) -o $@ $(WINBIND_NSS_PICOBJS) \
+ @WINBIND_NSS_EXTRA_LIBS@ @SONAMEFLAG@`basename $@`
+
+@WINBIND_WINS_NSS@: $(WINBIND_WINS_NSS_PICOBJS)
+ @echo "Linking $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(WINBIND_WINS_NSS_PICOBJS) \
+ $(LDAP_LIBS) $(KRB5LIBS) -lc \
+ @SONAMEFLAG@`basename $@`
+
+nsswitch/pam_winbind.@SHLIBEXT@: $(PAM_WINBIND_PICOBJ) bin/.dummy
+ @echo "Linking $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(PAM_WINBIND_PICOBJ) \
+ @SONAMEFLAG@`basename $@` -lpam
+
+bin/rhosts.@SHLIBEXT@: $(AUTH_RHOSTS_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(AUTH_RHOSTS_OBJ:.o=.@PICSUFFIX@) @SONAMEFLAG@`basename $@`
+
+bin/builtin.@SHLIBEXT@: $(AUTH_BUILTIN_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(AUTH_BUILTIN_OBJ:.o=.@PICSUFFIX@) @SONAMEFLAG@`basename $@`
+
+bin/domain.@SHLIBEXT@: $(AUTH_DOMAIN_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(AUTH_DOMAIN_OBJ:.o=.@PICSUFFIX@) @SONAMEFLAG@`basename $@`
+
+bin/smbserver.@SHLIBEXT@: $(AUTH_SERVER_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(AUTH_SERVER_OBJ:.o=.@PICSUFFIX@) @SONAMEFLAG@`basename $@`
+
+bin/winbind.@SHLIBEXT@: $(AUTH_WINBIND_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(AUTH_WINBIND_OBJ:.o=.@PICSUFFIX@) @SONAMEFLAG@`basename $@`
+
+bin/unix.@SHLIBEXT@: $(AUTH_UNIX_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(AUTH_UNIX_OBJ:.o=.@PICSUFFIX@) @SONAMEFLAG@`basename $@`
+
+bin/sam.@SHLIBEXT@: $(AUTH_SAM_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(AUTH_SAM_OBJ:.o=.@PICSUFFIX@) @SONAMEFLAG@`basename $@`
+
+bin/mysql.@SHLIBEXT@: $(MYSQL_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(MYSQL_OBJ:.o=.@PICSUFFIX@) @MYSQL_LIBS@ \
+ @SONAMEFLAG@`basename $@`
+
+bin/pgsql.@SHLIBEXT@: $(PGSQL_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(PGSQL_OBJ:.o=.@PICSUFFIX@) @PGSQL_LIBS@ \
+ @SONAMEFLAG@`basename $@`
+
+bin/ldapsam.@SHLIBEXT@: passdb/pdb_ldap.@PICSUFFIX@
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) $(LDAP_LIBS) -o $@ passdb/pdb_ldap.@PICSUFFIX@ \
+ @SONAMEFLAG@`basename $@`
+
+bin/tdbsam.@SHLIBEXT@: passdb/pdb_tdb.@PICSUFFIX@
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ passdb/pdb_tdb.@PICSUFFIX@ \
+ @SONAMEFLAG@`basename $@`
+
+bin/smbpasswd.@SHLIBEXT@: passdb/pdb_smbpasswd.@PICSUFFIX@
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ passdb/pdb_smbpasswd.@PICSUFFIX@ \
+ @SONAMEFLAG@`basename $@`
+
+bin/weird.@SHLIBEXT@: $(DEVEL_HELP_WEIRD_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(DEVEL_HELP_WEIRD_OBJ:.o=.@PICSUFFIX@) \
+ @SONAMEFLAG@`basename $@`
+
+bin/CP850.@SHLIBEXT@: $(CP850_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(CP850_OBJ:.o=.@PICSUFFIX@) \
+ @SONAMEFLAG@`basename $@`
+
+bin/CP437.@SHLIBEXT@: $(CP437_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(CP437_OBJ:.o=.@PICSUFFIX@) \
+ @SONAMEFLAG@`basename $@`
+
+bin/macosxfs.@SHLIBEXT@: $(CHARSET_MACOSXFS_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(CHARSET_MACOSXFS_OBJ:.o=.@PICSUFFIX@) \
+ -framework CoreFoundation @SONAMEFLAG@`basename $@`
+
+bin/xml.@SHLIBEXT@: $(XML_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(XML_OBJ:.o=.@PICSUFFIX@) @XML_LIBS@ \
+ @SONAMEFLAG@`basename $@`
+
+bin/audit.@SHLIBEXT@: $(VFS_AUDIT_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_AUDIT_OBJ:.o=.@PICSUFFIX@) \
+ @SONAMEFLAG@`basename $@`
+
+bin/extd_audit.@SHLIBEXT@: $(VFS_EXTD_AUDIT_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_EXTD_AUDIT_OBJ:.o=.@PICSUFFIX@) \
+ @SONAMEFLAG@`basename $@`
+
+bin/recycle.@SHLIBEXT@: $(VFS_RECYCLE_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_RECYCLE_OBJ:.o=.@PICSUFFIX@) \
+ @SONAMEFLAG@`basename $@`
+
+bin/netatalk.@SHLIBEXT@: $(VFS_NETATALK_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_NETATALK_OBJ:.o=.@PICSUFFIX@) \
+ @SONAMEFLAG@`basename $@`
+
+bin/fake_perms.@SHLIBEXT@: $(VFS_FAKE_PERMS_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_FAKE_PERMS_OBJ:.o=.@PICSUFFIX@) \
+ @SONAMEFLAG@`basename $@`
+
+bin/default_quota.@SHLIBEXT@: $(VFS_DEFAULT_QUOTA_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_DEFAULT_QUOTA_OBJ:.o=.@PICSUFFIX@) \
+ @SONAMEFLAG@`basename $@`
+
+bin/readonly.@SHLIBEXT@: $(VFS_READONLY_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_READONLY_OBJ:.o=.@PICSUFFIX@) \
+ @SONAMEFLAG@`basename $@`
+
+bin/cap.@SHLIBEXT@: $(VFS_CAP_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_CAP_OBJ:.o=.@PICSUFFIX@) \
+ @SONAMEFLAG@`basename $@`
+
+bin/expand_msdfs.@SHLIBEXT@: $(VFS_EXPAND_MSDFS_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_EXPAND_MSDFS_OBJ:.o=.@PICSUFFIX@) \
+ @SONAMEFLAG@`basename $@`
+
+bin/config_ldap.@SHLIBEXT@: $(CONFIG_LDAP_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(CONFIG_LDAP_OBJ:.o=.@PICSUFFIX@) \
+ @SMBLDAP@ @LDAP_LIBS@ @SONAMEFLAG@`basename $@`
+
+bin/wbinfo@EXEEXT@: $(WBINFO_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(LINK) -o $@ $(WBINFO_OBJ) $(LIBS) @POPTLIBS@ -lcrypto
+
+bin/ntlm_auth@EXEEXT@: $(NTLM_AUTH_OBJ) $(PARAM_OBJ) $(LIB_NONSMBD_OBJ) \
+ $(UBIQX_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(LINK) -o $@ $(NTLM_AUTH_OBJ) $(PARAM_OBJ) $(LIB_NONSMBD_OBJ) \
+ $(UBIQX_OBJ) $(LIBS) @POPTLIBS@ $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/pam_smbpass.@SHLIBEXT@: $(PAM_SMBPASS_PICOOBJ)
+ @echo "Linking shared library $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(PAM_SMBPASS_PICOOBJ) -lpam $(DYNEXP) $(LIBS) -lc $(LDAP_LIBS) $(KRB5LIBS)
+
+bin/libmsrpc.a: $(LIBMSRPC_PICOBJ)
+ @-$(AR) -rc $@ $(LIBMSRPC_PICOBJ)
+
+bin/tdbbackup@EXEEXT@: $(TDBBACKUP_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(TDBBACKUP_OBJ)
+
+bin/tdbdump@EXEEXT@: $(TDBDUMP_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(TDBDUMP_OBJ)
+
+bin/t_strcmp@EXEEXT@: bin/libbigballofmud.@SHLIBEXT@ torture/t_strcmp.o
+ $(CC) $(FLAGS) -o $@ $(LIBS) torture/t_strcmp.o -L ./bin -lbigballofmud
+
+bin/t_strstr@EXEEXT@: bin/libbigballofmud.@SHLIBEXT@ torture/t_strstr.o
+ $(CC) $(FLAGS) -o $@ $(LIBS) torture/t_strstr.o -L ./bin -lbigballofmud
+
+bin/t_stringoverflow@EXEEXT@: bin/libbigballofmud.@SHLIBEXT@ torture/t_stringoverflow.o
+ $(CC) $(FLAGS) -o $@ torture/t_stringoverflow.o -L./bin -lbigballofmud
+
+bin/t_doschar@EXEEXT@: bin/libbigballofmud.@SHLIBEXT@ torture/t_doschar.o
+ $(CC) $(FLAGS) -o $@ $(LIBS) torture/t_doschar.o -L ./bin -lbigballofmud
+bin/t_push_ucs2@EXEEXT@: bin/libbigballofmud.@SHLIBEXT@ torture/t_push_ucs2.o
+ $(CC) $(FLAGS) -o $@ $(LIBS) torture/t_push_ucs2.o -L ./bin -lbigballofmud
+
+bin/t_snprintf@EXEEXT@: lib/snprintf.c
+ $(CC) $(FLAGS) -o $@ -DTEST_SNPRINTF lib/snprintf.c -lm
+install: installbin installman installscripts installdat installswat installmodules @INSTALLCLIENT@
+
+install-everything: install installmodules
+
+# DESTDIR is used here to prevent packagers wasting their time
+# duplicating the Makefile. Remove it and you will have the privelege
+# of package each samba release for muliple versions of multiple
+# distributions and operating systems, or at least supplying patches
+# to all the packaging files required for this, prior to committing
+# the removal of DESTDIR. Do not remove it even though you think it
+# is not used
+
+installdirs:
+ @$(SHELL) $(srcdir)/script/installdirs.sh $(DESTDIR)$(BASEDIR) $(DESTDIR)$(BINDIR) $(DESTDIR)$(SBINDIR) $(DESTDIR)$(LIBDIR) $(DESTDIR)$(VARDIR) $(DESTDIR)$(PRIVATEDIR) $(DESTDIR)$(PIDDIR) $(DESTDIR)$(LOCKDIR) $(DESTDIR)$(MANDIR)
+
+installservers: all installdirs
+ @$(SHELL) $(srcdir)/script/installbin.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(SBINDIR) $(DESTDIR)$(LIBDIR) $(DESTDIR)$(VARDIR) $(SBIN_PROGS)
+
+installbin: all installdirs
+ @$(SHELL) $(srcdir)/script/installbin.sh $(INSTALLPERMS) $(DESTDIR) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(SBINDIR) $(DESTDIR)$(LIBDIR) $(DESTDIR)$(VARDIR) $(SBIN_PROGS)
+ @$(SHELL) $(srcdir)/script/installbin.sh $(INSTALLPERMS) $(DESTDIR) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(BINDIR) $(DESTDIR)$(LIBDIR) $(DESTDIR)$(VARDIR) $(BIN_PROGS)
+
+
+# Some symlinks are required for the 'probing' of modules.
+# This mechanism should go at some point..
+installmodules: modules installdirs
+ @$(SHELL) $(srcdir)/script/installmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(VFSLIBDIR) $(VFS_MODULES)
+ @$(SHELL) $(srcdir)/script/installmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(PDBLIBDIR) $(PDB_MODULES)
+ @$(SHELL) $(srcdir)/script/installmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(RPCLIBDIR) $(RPC_MODULES)
+ @$(SHELL) $(srcdir)/script/installmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(IDMAPLIBDIR) $(IDMAP_MODULES)
+ @$(SHELL) $(srcdir)/script/installmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(CHARSETLIBDIR) $(CHARSET_MODULES)
+ @$(SHELL) $(srcdir)/script/installmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(AUTHLIBDIR) $(AUTH_MODULES)
+ @$(SHELL) $(srcdir)/script/linkmodules.sh $(DESTDIR)$(PDBLIBDIR) ldapsam.@SHLIBEXT@ ldapsam_compat.@SHLIBEXT@
+ @$(SHELL) $(srcdir)/script/linkmodules.sh $(DESTDIR)$(AUTHLIBDIR) rhosts.@SHLIBEXT@ hostsequiv.@SHLIBEXT@
+ @$(SHELL) $(srcdir)/script/linkmodules.sh $(DESTDIR)$(AUTHLIBDIR) sam.@SHLIBEXT@ sam_ignoredomain.@SHLIBEXT@
+ @$(SHELL) $(srcdir)/script/linkmodules.sh $(DESTDIR)$(AUTHLIBDIR) domain.@SHLIBEXT@ trustdomain.@SHLIBEXT@ ntdomain.@SHLIBEXT@
+ @$(SHELL) $(srcdir)/script/linkmodules.sh $(DESTDIR)$(AUTHLIBDIR) builtin.@SHLIBEXT@ guest.@SHLIBEXT@ fixed_challenge.@SHLIBEXT@ name_to_ntstatus.@SHLIBEXT@
+
+installscripts: installdirs
+ @$(SHELL) $(srcdir)/script/installscripts.sh $(INSTALLPERMS) $(DESTDIR)$(BINDIR) $(SCRIPTS)
+
+installdat: installdirs
+ @$(SHELL) $(srcdir)/script/installdat.sh $(DESTDIR)$(LIBDIR) $(srcdir)
+
+installmsg: installdirs
+ @$(SHELL) $(srcdir)/script/installmsg.sh $(DESTDIR)$(LIBDIR) $(srcdir)
+
+installswat: installdirs installmsg
+ @$(SHELL) $(srcdir)/script/installswat.sh $(DESTDIR)$(SWATDIR) $(srcdir)
+
+installclientlib: installdirs libsmbclient
+ @$(SHELL) $(srcdir)/script/installdirs.sh $(DESTDIR)${prefix}/lib
+ -$(INSTALLCLIENTCMD_SH) bin/libsmbclient.@SHLIBEXT@ $(DESTDIR)${prefix}/lib
+ -$(INSTALLCLIENTCMD_A) bin/libsmbclient.a $(DESTDIR)${prefix}/lib
+ @$(SHELL) $(srcdir)/script/installdirs.sh $(DESTDIR)${prefix}/include
+ -$(INSTALLCMD) $(srcdir)/include/libsmbclient.h $(DESTDIR)${prefix}/include
+
+# Python extensions
+
+PYTHON_OBJS = $(PARAM_OBJ) $(LIB_NONSMBD_OBJ) $(LIBSMB_OBJ) $(RPC_PARSE_OBJ) \
+ $(UBIQX_OBJ) $(LIBMSRPC_OBJ) $(PASSDB_OBJ) $(GROUPDB_OBJ) \
+ $(SECRETS_OBJ) $(KRBCLIENT_OBJ) $(SMBLDAP_OBJ) lib/dummyroot.o
+
+PYTHON_PICOBJS = $(PYTHON_OBJS:.o=.@PICSUFFIX@)
+
+python_ext: $(PYTHON_PICOBJS)
+ @if test -z "$(PYTHON)"; then \
+ echo Use the option --with-python to configure python; \
+ exit 1; \
+ fi
+ PYTHON_OBJS="$(PYTHON_PICOBJS)" \
+ PYTHON_CFLAGS="$(CFLAGS) $(CPPFLAGS) $(FLAGS)" \
+ LIBS="$(LDFLAGS) $(LIBS) $(PASSDB_LIBS) $(IDMAP_LIBS) $(KRB5LIBS) $(LDAP_LIBS)" \
+ $(PYTHON) python/setup.py build
+
+python_install: $(PYTHON_PICOBJS)
+ @if test -z "$(PYTHON)"; then \
+ echo Use the option --with-python to configure python; \
+ exit 1; \
+ fi
+ PYTHON_OBJS="$(PYTHON_PICOBJS)" \
+ PYTHON_CFLAGS="$(CFLAGS) $(CPPFLAGS)" \
+ LIBS="$(LDFLAGS) $(LIBS)" \
+ $(PYTHON) python/setup.py install
+
+python_clean:
+ @-if test -n "$(PYTHON)"; then $(PYTHON) python/setup.py clean; fi
+
+# revert to the previously installed version
+revert:
+ @$(SHELL) $(srcdir)/script/revert.sh $(SBINDIR) $(SBIN_PROGS)
+ @$(SHELL) $(srcdir)/script/revert.sh $(BINDIR) $(BIN_PROGS) $(SCRIPTS)
+
+installman: installdirs
+ @$(SHELL) $(srcdir)/script/installman.sh $(DESTDIR)$(MANDIR) $(srcdir) $(man_langs) "@ROFF@"
+
+.PHONY: showlayout
+
+showlayout:
+ @echo "Samba will be installed into:"
+ @echo " basedir: $(BASEDIR)"
+ @echo " bindir: $(BINDIR)"
+ @echo " sbindir: $(SBINDIR)"
+ @echo " libdir: $(LIBDIR)"
+ @echo " vardir: $(VARDIR)"
+ @echo " mandir: $(MANDIR)"
+ @echo " privatedir: $(PRIVATE_DIR)"
+ @echo " configdir: $(CONFIGDIR)"
+ @echo " lockdir: $(LOCKDIR)"
+ @echo " piddir: $(PIDDIR)"
+ @echo " swatdir: $(SWATDIR)"
+
+
+uninstall: uninstallman uninstallbin uninstallscripts uninstallmodules
+
+uninstallman:
+ @$(SHELL) $(srcdir)/script/uninstallman.sh $(DESTDIR)$(MANDIR) $(srcdir) $(man_langs)
+
+uninstallbin:
+ @$(SHELL) $(srcdir)/script/uninstallbin.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(SBINDIR) $(DESTDIR)$(LIBDIR) $(DESTDIR)$(VARDIR) $(DESTDIR)$(SBIN_PROGS)
+ @$(SHELL) $(srcdir)/script/uninstallbin.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(BINDIR) $(DESTDIR)$(LIBDIR) $(DESTDIR)$(VARDIR) $(DESTDIR)$(BIN_PROGS)
+
+uninstallmodules:
+ @$(SHELL) $(srcdir)/script/uninstallmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(VFSLIBDIR) $(DESTDIR)$(VFS_MODULES)
+ @$(SHELL) $(srcdir)/script/uninstallmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(PDBLIBDIR) $(DESTDIR)$(PDB_MODULES)
+ @$(SHELL) $(srcdir)/script/uninstallmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(RPCLIBDIR) $(DESTDIR)$(RPC_MODULES)
+ @$(SHELL) $(srcdir)/script/uninstallmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(CHARSETLIBDIR) $(DESTDIR)$(CHARSET_MODULES)
+ @$(SHELL) $(srcdir)/script/uninstallmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(AUTHLIBDIR) $(DESTDIR)$(AUTH_MODULES)
+
+uninstallscripts:
+ @$(SHELL) $(srcdir)/script/uninstallscripts.sh $(INSTALLPERMS) $(DESTDIR)$(BINDIR) $(SCRIPTS)
+
+# Toplevel clean files
+TOPFILES=dynconfig.o dynconfig.@PICSUFFIX@
+
+clean: delheaders python_clean
+ -rm -f core */*~ *~ */*.o */*.@PICSUFFIX@ */*.@SHLIBEXT@ \
+ $(TOPFILES) $(BIN_PROGS) $(SBIN_PROGS) $(MODULES) $(TORTURE_PROGS) \
+ $(LIBSMBCLIENT) $(EVERYTHING_PROGS) .headers.stamp
+
+# Making this target will just make sure that the prototype files
+# exist, not necessarily that they are up to date. Since they're
+# removed by "make clean" this will always be run when you do anything
+# afterwards.
+proto_exists: include/proto.h include/wrepld_proto.h include/build_env.h \
+ nsswitch/winbindd_proto.h web/swat_proto.h \
+ client/client_proto.h utils/net_proto.h smbd/build_options.c
+
+delheaders:
+ @echo Removing prototype headers
+ @rm -f include/proto.h include/build_env.h include/wrepld_proto.h \
+ nsswitch/winbindd_proto.h web/swat_proto.h \
+ client/client_proto.h utils/net_proto.h \
+ smbd/build_options.c
+
+MKPROTO_SH = $(srcdir)/script/mkproto.sh
+
+include/proto.h: smbd/build_options.c
+ @echo Building include/proto.h
+ @cd $(srcdir) && $(SHELL) $(MKPROTO_SH) $(AWK) \
+ -h _PROTO_H_ $(builddir)/include/proto.h \
+ $(PROTO_OBJ)
+
+include/build_env.h: script/build_env.sh
+ @echo Building include/build_env.h
+ @$(SHELL) $(srcdir)/script/build_env.sh $(srcdir) $(builddir) $(CC) \
+ > $(builddir)/include/build_env.h
+
+include/wrepld_proto.h:
+ @echo Building include/wrepld_proto.h
+ @cd $(srcdir) && $(SHELL) $(MKPROTO_SH) $(AWK) \
+ -h _WREPLD_PROTO_H_ $(builddir)/include/wrepld_proto.h \
+ $(WREPL_OBJ1)
+
+nsswitch/winbindd_proto.h:
+ @cd $(srcdir) && $(SHELL) $(MKPROTO_SH) $(AWK) \
+ -h _WINBINDD_PROTO_H_ $(builddir)/nsswitch/winbindd_proto.h \
+ $(WINBINDD_OBJ1)
+
+web/swat_proto.h:
+ @cd $(srcdir) && $(SHELL) $(MKPROTO_SH) $(AWK) \
+ -h _SWAT_PROTO_H_ $(builddir)/web/swat_proto.h \
+ $(SWAT_OBJ1)
+
+client/client_proto.h:
+ @cd $(srcdir) && $(SHELL) $(MKPROTO_SH) $(AWK) \
+ -h _CLIENT_PROTO_H_ $(builddir)/client/client_proto.h \
+ $(CLIENT_OBJ1)
+
+utils/net_proto.h:
+ @cd $(srcdir) && $(SHELL) $(MKPROTO_SH) $(AWK) \
+ -h _NET_PROTO_H_ $(builddir)/utils/net_proto.h \
+ $(NET_OBJ1)
+
+include/tdbsam2_parse_info.h:
+ @if test -n "$(PERL)"; then \
+ cd $(srcdir) && @PERL@ -w script/genstruct.pl \
+ -o include/tdbsam2_parse_info.h $(CC) -E -O2 -g \
+ include/gums.h; \
+ else \
+ echo Unable to build $@, continuing; \
+ fi
+
+# "make headers" or "make proto" calls a subshell because we need to
+# make sure these commands are executed in sequence even for a
+# parallel make.
+headers:
+ $(MAKE) delheaders; \
+ $(MAKE) smbd/build_options.c; \
+ $(MAKE) include/proto.h; \
+ $(MAKE) include/build_env.h; \
+ $(MAKE) include/wrepld_proto.h; \
+ $(MAKE) nsswitch/winbindd_proto.h; \
+ $(MAKE) web/swat_proto.h; \
+ $(MAKE) client/client_proto.h; \
+ $(MAKE) utils/net_proto.h;
+
+prebuiltheaders:
+ $(MAKE) include/tdbsam2_parse_info.h
+
+genparse: prebuiltheaders
+
+proto: headers
+
+.PHONY: headers proto
+
+etags:
+ etags `find $(srcdir) -name "*.[ch]" | grep -v /CVS/`
+
+ctags:
+ ctags `find $(srcdir) -name "*.[ch]" | grep -v /CVS/`
+
+realclean: clean delheaders
+ -rm -f config.log bin/.dummy script/findsmb
+
+distclean: realclean
+ -rm -f include/stamp-h
+ -rm -f include/config.h Makefile
+ -rm -f config.status config.cache so_locations
+ -rm -rf .deps TAGS
+
+realdistclean: distclean
+ -rm -f include/config.h.in
+ -rm -f include/version.h
+ -rm -f configure
+
+# this target is really just for my use. It only works on a limited
+# range of machines and is used to produce a list of potentially
+# dead (ie. unused) functions in the code. (tridge)
+finddead:
+ nm */*.o |grep 'U ' | awk '{print $$2}' | sort -u > nmused.txt
+ nm */*.o |grep 'T ' | awk '{print $$3}' | sort -u > nmfns.txt
+ comm -13 nmused.txt nmfns.txt
+
+
+# when configure.in is updated, reconfigure
+$(srcdir)/configure: $(srcdir)/configure.in
+ @echo "WARNING: you need to rerun ./autogen.sh"
+
+config.status: $(srcdir)/configure
+ @echo "WARNING: you need to run ./configure"
+
+Makefile: $(srcdir)/Makefile.in config.status
+ @echo "WARNING: you need to run ./config.status"
+
+######################################################################
+# Samba Testing Framework
+
+# FIXME: LD_LIBRARY_PATH is not portable, but in the absence of
+# libtool I don't know a better way to do it. Perhaps we should fix
+# libbigballofmud to link statically?
+
+check: check-programs
+ LD_LIBRARY_PATH="`pwd`/bin:$$LD_LIBRARY_PATH" \
+ PATH="`pwd`/bin:$$PATH" \
+ python stf/standardcheck.py; \
+ if test -n "$(PYTHON)"; then \
+ python stf/pythoncheck.py; \
+ fi
+
+# These are called by the test suite and need to be built before
+# running it. For the time being we don't build all of BIN_PROGS,
+# because they're not all needed.
+check-programs: bin/t_strcmp bin/t_strstr bin/t_push_ucs2 bin/smbcontrol bin/t_snprintf
diff --git a/source/VERSION b/source/VERSION
new file mode 100644
index 00000000000..d7f386ab42b
--- /dev/null
+++ b/source/VERSION
@@ -0,0 +1,123 @@
+########################################################
+# SAMBA Version #
+# #
+# script/mkversion.sh #
+# will use this file to create #
+# include/version.h #
+# #
+########################################################
+
+########################################################
+# This are the main SAMBA version numbers #
+# #
+# <MAJOR>.<MINOR>.<RELEASE> #
+# #
+# e.g. SAMBA_VERSION_MAJOR=3 #
+# SAMBA_VERSION_MINOR=0 #
+# SAMBA_VERSION_RELEASE=0 #
+# -> "3.0.0" #
+########################################################
+SAMBA_VERSION_MAJOR=3
+SAMBA_VERSION_MINOR=1
+SAMBA_VERSION_RELEASE=0
+
+########################################################
+# If a official release has a serious bug #
+# a security release will have 'a' sufffix #
+# #
+# so SAMBA's version will be #
+# <MAJOR>.<MINOR>.<RELEASE><REVISION> #
+# #
+# e.g. SAMBA_VERSION_PRE_RELEASE=a #
+# -> "2.2.8a" #
+########################################################
+SAMBA_VERSION_REVISION=
+
+########################################################
+# For 'pre' releases the version will be #
+# #
+# <MAJOR>.<MINOR>.<RELEASE>pre<PRE_RELEASE> #
+# #
+# e.g. SAMBA_VERSION_PRE_RELEASE=1 #
+# -> "2.2.9pre1" #
+########################################################
+SAMBA_VERSION_PRE_RELEASE=
+
+########################################################
+# For 'rc' releases the version will be #
+# #
+# <MAJOR>.<MINOR>.<RELEASE>rc<RC_RELEASE> #
+# #
+# e.g. SAMBA_VERSION_RC_RELEASE=1 #
+# -> "3.0.0rc1" #
+########################################################
+SAMBA_VERSION_RC_RELEASE=
+
+########################################################
+# For 'beta' releases the version will be #
+# #
+# <MAJOR>.<MINOR>.<RELEASE>beta<BETA_RELEASE> #
+# #
+# e.g. SAMBA_VERSION_BETA_RELEASE=3 #
+# -> "3.0.0beta3" #
+########################################################
+SAMBA_VERSION_BETA_RELEASE=
+
+########################################################
+# For 'alpha' releases the version will be #
+# #
+# <MAJOR>.<MINOR>.<RELEASE>alpha<ALPHA_RELEASE> #
+# #
+# e.g. SAMBA_VERSION_ALPHA_RELEASE=1 #
+# -> "4.0.0alpha1" #
+########################################################
+SAMBA_VERSION_ALPHA_RELEASE=1
+
+########################################################
+# For 'test' releases the version will be #
+# #
+# <MAJOR>.<MINOR>.<RELEASE>test<TEST_RELEASE> #
+# #
+# e.g. SAMBA_VERSION_TEST_RELEASE=1 #
+# -> "4.0.0test1" #
+########################################################
+SAMBA_VERSION_TEST_RELEASE=
+
+########################################################
+# To mark CVS snapshots this should be set to 'yes' #
+# in the development BRANCH, and set to 'no' only in #
+# the SAMBA_X_X_RELEASE BRANCH #
+# #
+# <MAJOR>.<MINOR>.<RELEASE>[...]cvs #
+# #
+# e.g. SAMBA_VERSION_IS_CVS_SNAPSHOT=yes #
+# -> "CVS 3.0.0rc2" #
+########################################################
+SAMBA_VERSION_IS_CVS_SNAPSHOT=yes
+
+########################################################
+# This can be set by vendors if they want... #
+# This can be a string constant or a function which #
+# returns a string (const char *) #
+# #
+# <MAJOR>.<MINOR>.<RELEASE>[...]-<VENDOR_SUFFIX> #
+# #
+# Note the '-' is automaticaly added #
+# #
+# e.g. SAMBA_VERSION_VENDOR_SUFFIX="VendorVersion" #
+# -> "CVS 3.0.0rc2-VendorVersion" #
+# #
+# Note: If you want to use a function, #
+# then patch lib/version.c and add this function #
+# there, because the symbol must be available in #
+# binary. #
+# #
+# const char *vendor_version(void) #
+# { #
+# return "VendorVersion"; #
+# } #
+# #
+# e.g. SAMBA_VERSION_VENDOR_SUFFIX=vendor_version() #
+# -> "CVS 3.0.0rc2-VendorVersion" #
+########################################################
+SAMBA_VERSION_VENDOR_SUFFIX=
diff --git a/source/aclocal.m4 b/source/aclocal.m4
new file mode 100644
index 00000000000..6de11953b44
--- /dev/null
+++ b/source/aclocal.m4
@@ -0,0 +1,775 @@
+dnl AC_VALIDATE_CACHE_SYSTEM_TYPE[(cmd)]
+dnl if the cache file is inconsistent with the current host,
+dnl target and build system types, execute CMD or print a default
+dnl error message.
+AC_DEFUN(AC_VALIDATE_CACHE_SYSTEM_TYPE, [
+ AC_REQUIRE([AC_CANONICAL_SYSTEM])
+ AC_MSG_CHECKING([config.cache system type])
+ if { test x"${ac_cv_host_system_type+set}" = x"set" &&
+ test x"$ac_cv_host_system_type" != x"$host"; } ||
+ { test x"${ac_cv_build_system_type+set}" = x"set" &&
+ test x"$ac_cv_build_system_type" != x"$build"; } ||
+ { test x"${ac_cv_target_system_type+set}" = x"set" &&
+ test x"$ac_cv_target_system_type" != x"$target"; }; then
+ AC_MSG_RESULT([different])
+ ifelse($#, 1, [$1],
+ [AC_MSG_ERROR(["you must remove config.cache and restart configure"])])
+ else
+ AC_MSG_RESULT([same])
+ fi
+ ac_cv_host_system_type="$host"
+ ac_cv_build_system_type="$build"
+ ac_cv_target_system_type="$target"
+])
+
+
+dnl test whether dirent has a d_off member
+AC_DEFUN(AC_DIRENT_D_OFF,
+[AC_CACHE_CHECK([for d_off in dirent], ac_cv_dirent_d_off,
+[AC_TRY_COMPILE([
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>], [struct dirent d; d.d_off;],
+ac_cv_dirent_d_off=yes, ac_cv_dirent_d_off=no)])
+if test $ac_cv_dirent_d_off = yes; then
+ AC_DEFINE(HAVE_DIRENT_D_OFF,1,[Whether dirent has a d_off member])
+fi
+])
+
+dnl Mark specified module as shared
+dnl SMB_MODULE(name,static_files,shared_files,subsystem,whatif-static,whatif-shared)
+AC_DEFUN(SMB_MODULE,
+[
+ AC_MSG_CHECKING([how to build $1])
+ if test "$[MODULE_][$1]"; then
+ DEST=$[MODULE_][$1]
+ elif test "$[MODULE_]translit([$4], [A-Z], [a-z])" -a "$[MODULE_DEFAULT_][$1]"; then
+ DEST=$[MODULE_]translit([$4], [A-Z], [a-z])
+ else
+ DEST=$[MODULE_DEFAULT_][$1]
+ fi
+
+ if test x"$DEST" = xSHARED; then
+ AC_DEFINE([$1][_init], [init_module], [Whether to build $1 as shared module])
+ $4_MODULES="$$4_MODULES $3"
+ AC_MSG_RESULT([shared])
+ [$6]
+ string_shared_modules="$string_shared_modules $1"
+ elif test x"$DEST" = xSTATIC; then
+ [init_static_modules_]translit([$4], [A-Z], [a-z])="$[init_static_modules_]translit([$4], [A-Z], [a-z]) $1_init();"
+ string_static_modules="$string_static_modules $1"
+ $4_STATIC="$$4_STATIC $2"
+ AC_SUBST($4_STATIC)
+ [$5]
+ AC_MSG_RESULT([static])
+ else
+ string_ignored_modules="$string_ignored_modules $1"
+ AC_MSG_RESULT([not])
+ fi
+])
+
+AC_DEFUN(SMB_SUBSYSTEM,
+[
+ AC_SUBST($1_STATIC)
+ AC_SUBST($1_MODULES)
+ AC_DEFINE_UNQUOTED([static_init_]translit([$1], [A-Z], [a-z]), [{$init_static_modules_]translit([$1], [A-Z], [a-z])[}], [Static init functions])
+ ifelse([$2], , :, [rm -f $2])
+])
+
+dnl AC_PROG_CC_FLAG(flag)
+AC_DEFUN(AC_PROG_CC_FLAG,
+[AC_CACHE_CHECK(whether ${CC-cc} accepts -$1, ac_cv_prog_cc_$1,
+[echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -$1 -c conftest.c 2>&1`"; then
+ ac_cv_prog_cc_$1=yes
+else
+ ac_cv_prog_cc_$1=no
+fi
+rm -f conftest*
+])])
+
+dnl see if a declaration exists for a function or variable
+dnl defines HAVE_function_DECL if it exists
+dnl AC_HAVE_DECL(var, includes)
+AC_DEFUN(AC_HAVE_DECL,
+[
+ AC_CACHE_CHECK([for $1 declaration],ac_cv_have_$1_decl,[
+ AC_TRY_COMPILE([$2],[int i = (int)$1],
+ ac_cv_have_$1_decl=yes,ac_cv_have_$1_decl=no)])
+ if test x"$ac_cv_have_$1_decl" = x"yes"; then
+ AC_DEFINE([HAVE_]translit([$1], [a-z], [A-Z])[_DECL],1,[Whether $1() is available])
+ fi
+])
+
+
+dnl check for a function in a library, but don't
+dnl keep adding the same library to the LIBS variable.
+dnl AC_LIBTESTFUNC(lib,func)
+AC_DEFUN(AC_LIBTESTFUNC,
+[case "$LIBS" in
+ *-l$1*) AC_CHECK_FUNCS($2) ;;
+ *) AC_CHECK_LIB($1, $2)
+ AC_CHECK_FUNCS($2)
+ ;;
+ esac
+])
+
+# AC_CHECK_LIB_EXT(LIBRARY, [EXT_LIBS], [FUNCTION],
+# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND],
+# [ADD-ACTION-IF-FOUND],[OTHER-LIBRARIES])
+# ------------------------------------------------------
+#
+# Use a cache variable name containing both the library and function name,
+# because the test really is for library $1 defining function $3, not
+# just for library $1. Separate tests with the same $1 and different $3s
+# may have different results.
+#
+# Note that using directly AS_VAR_PUSHDEF([ac_Lib], [ac_cv_lib_$1_$3])
+# is asking for trouble, since AC_CHECK_LIB($lib, fun) would give
+# ac_cv_lib_$lib_fun, which is definitely not what was meant. Hence
+# the AS_LITERAL_IF indirection.
+#
+# FIXME: This macro is extremely suspicious. It DEFINEs unconditionally,
+# whatever the FUNCTION, in addition to not being a *S macro. Note
+# that the cache does depend upon the function we are looking for.
+#
+# It is on purpose we used `ac_check_lib_ext_save_LIBS' and not just
+# `ac_save_LIBS': there are many macros which don't want to see `LIBS'
+# changed but still want to use AC_CHECK_LIB_EXT, so they save `LIBS'.
+# And ``ac_save_LIBS' is too tempting a name, so let's leave them some
+# freedom.
+AC_DEFUN([AC_CHECK_LIB_EXT],
+[
+AH_CHECK_LIB_EXT([$1])
+ac_check_lib_ext_save_LIBS=$LIBS
+LIBS="-l$1 $$2 $7 $LIBS"
+AS_LITERAL_IF([$1],
+ [AS_VAR_PUSHDEF([ac_Lib_ext], [ac_cv_lib_ext_$1])],
+ [AS_VAR_PUSHDEF([ac_Lib_ext], [ac_cv_lib_ext_$1''])])dnl
+
+m4_ifval([$3],
+ [
+ AH_CHECK_FUNC_EXT([$3])
+ AS_LITERAL_IF([$1],
+ [AS_VAR_PUSHDEF([ac_Lib_func], [ac_cv_lib_ext_$1_$3])],
+ [AS_VAR_PUSHDEF([ac_Lib_func], [ac_cv_lib_ext_$1''_$3])])dnl
+ AC_CACHE_CHECK([for $3 in -l$1], ac_Lib_func,
+ [AC_TRY_LINK_FUNC($3,
+ [AS_VAR_SET(ac_Lib_func, yes);
+ AS_VAR_SET(ac_Lib_ext, yes)],
+ [AS_VAR_SET(ac_Lib_func, no);
+ AS_VAR_SET(ac_Lib_ext, no)])
+ ])
+ AS_IF([test AS_VAR_GET(ac_Lib_func) = yes],
+ [AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_$3))])dnl
+ AS_VAR_POPDEF([ac_Lib_func])dnl
+ ],[
+ AC_CACHE_CHECK([for -l$1], ac_Lib_ext,
+ [AC_TRY_LINK_FUNC([main],
+ [AS_VAR_SET(ac_Lib_ext, yes)],
+ [AS_VAR_SET(ac_Lib_ext, no)])
+ ])
+ ])
+LIBS=$ac_check_lib_ext_save_LIBS
+
+AS_IF([test AS_VAR_GET(ac_Lib_ext) = yes],
+ [m4_default([$4],
+ [AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_LIB$1))
+ case "$$2" in
+ *-l$1*)
+ ;;
+ *)
+ $2="-l$1 $$2"
+ ;;
+ esac])
+ [$6]
+ ],
+ [$5])dnl
+AS_VAR_POPDEF([ac_Lib_ext])dnl
+])# AC_CHECK_LIB_EXT
+
+# AH_CHECK_LIB_EXT(LIBNAME)
+# ---------------------
+m4_define([AH_CHECK_LIB_EXT],
+[AH_TEMPLATE(AS_TR_CPP(HAVE_LIB$1),
+ [Define to 1 if you have the `]$1[' library (-l]$1[).])])
+
+# AC_CHECK_FUNCS_EXT(FUNCTION, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+# -----------------------------------------------------------------
+dnl check for a function in a $LIBS and $OTHER_LIBS libraries variable.
+dnl AC_CHECK_FUNC_EXT(func,OTHER_LIBS,IF-TRUE,IF-FALSE)
+AC_DEFUN([AC_CHECK_FUNC_EXT],
+[
+ AH_CHECK_FUNC_EXT($1)
+ ac_check_func_ext_save_LIBS=$LIBS
+ LIBS="$2 $LIBS"
+ AS_VAR_PUSHDEF([ac_var], [ac_cv_func_ext_$1])dnl
+ AC_CACHE_CHECK([for $1], ac_var,
+ [AC_LINK_IFELSE([AC_LANG_FUNC_LINK_TRY([$1])],
+ [AS_VAR_SET(ac_var, yes)],
+ [AS_VAR_SET(ac_var, no)])])
+ LIBS=$ac_check_func_ext_save_LIBS
+ AS_IF([test AS_VAR_GET(ac_var) = yes],
+ [AC_DEFINE_UNQUOTED(AS_TR_CPP([HAVE_$1])) $3],
+ [$4])dnl
+AS_VAR_POPDEF([ac_var])dnl
+])# AC_CHECK_FUNC
+
+# AH_CHECK_FUNC_EXT(FUNCNAME)
+# ---------------------
+m4_define([AH_CHECK_FUNC_EXT],
+[AH_TEMPLATE(AS_TR_CPP(HAVE_$1),
+ [Define to 1 if you have the `]$1[' function.])])
+
+dnl Define an AC_DEFINE with ifndef guard.
+dnl AC_N_DEFINE(VARIABLE [, VALUE])
+define(AC_N_DEFINE,
+[cat >> confdefs.h <<\EOF
+[#ifndef] $1
+[#define] $1 ifelse($#, 2, [$2], $#, 3, [$2], 1)
+[#endif]
+EOF
+])
+
+dnl Add an #include
+dnl AC_ADD_INCLUDE(VARIABLE)
+define(AC_ADD_INCLUDE,
+[cat >> confdefs.h <<\EOF
+[#include] $1
+EOF
+])
+
+dnl Copied from libtool.m4
+AC_DEFUN(AC_PROG_LD_GNU,
+[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], ac_cv_prog_gnu_ld,
+[# I'd rather use --version here, but apparently some GNU ld's only accept -v.
+if $LD -v 2>&1 </dev/null | egrep '(GNU|with BFD)' 1>&5; then
+ ac_cv_prog_gnu_ld=yes
+else
+ ac_cv_prog_gnu_ld=no
+fi])
+])
+
+# Configure paths for LIBXML2
+# Toshio Kuratomi 2001-04-21
+# Adapted from:
+# Configure paths for GLIB
+# Owen Taylor 97-11-3
+
+dnl AM_PATH_XML2([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
+dnl Test for XML, and define XML_CFLAGS and XML_LIBS
+dnl
+AC_DEFUN(AM_PATH_XML2,[
+AC_ARG_WITH(xml-prefix,
+ [ --with-xml-prefix=PFX Prefix where libxml is installed (optional)],
+ xml_config_prefix="$withval", xml_config_prefix="")
+AC_ARG_WITH(xml-exec-prefix,
+ [ --with-xml-exec-prefix=PFX Exec prefix where libxml is installed (optional)],
+ xml_config_exec_prefix="$withval", xml_config_exec_prefix="")
+AC_ARG_ENABLE(xmltest,
+ [ --disable-xmltest Do not try to compile and run a test LIBXML program],,
+ enable_xmltest=yes)
+
+ if test x$xml_config_exec_prefix != x ; then
+ xml_config_args="$xml_config_args --exec-prefix=$xml_config_exec_prefix"
+ if test x${XML2_CONFIG+set} != xset ; then
+ XML2_CONFIG=$xml_config_exec_prefix/bin/xml2-config
+ fi
+ fi
+ if test x$xml_config_prefix != x ; then
+ xml_config_args="$xml_config_args --prefix=$xml_config_prefix"
+ if test x${XML2_CONFIG+set} != xset ; then
+ XML2_CONFIG=$xml_config_prefix/bin/xml2-config
+ fi
+ fi
+
+ AC_PATH_PROG(XML2_CONFIG, xml2-config, no)
+ min_xml_version=ifelse([$1], ,2.0.0,[$1])
+ AC_MSG_CHECKING(for libxml - version >= $min_xml_version)
+ no_xml=""
+ if test "$XML2_CONFIG" = "no" ; then
+ no_xml=yes
+ else
+ XML_CFLAGS=`$XML2_CONFIG $xml_config_args --cflags`
+ XML_LIBS=`$XML2_CONFIG $xml_config_args --libs`
+ xml_config_major_version=`$XML2_CONFIG $xml_config_args --version | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+ xml_config_minor_version=`$XML2_CONFIG $xml_config_args --version | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+ xml_config_micro_version=`$XML2_CONFIG $xml_config_args --version | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+ if test "x$enable_xmltest" = "xyes" ; then
+ ac_save_CFLAGS="$CFLAGS"
+ ac_save_LIBS="$LIBS"
+ CFLAGS="$CFLAGS $XML_CFLAGS"
+ LIBS="$XML_LIBS $LIBS"
+dnl
+dnl Now check if the installed libxml is sufficiently new.
+dnl (Also sanity checks the results of xml2-config to some extent)
+dnl
+ rm -f conf.xmltest
+ AC_TRY_RUN([
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <libxml/xmlversion.h>
+
+int
+main()
+{
+ int xml_major_version, xml_minor_version, xml_micro_version;
+ int major, minor, micro;
+ char *tmp_version;
+
+ system("touch conf.xmltest");
+
+ /* Capture xml2-config output via autoconf/configure variables */
+ /* HP/UX 9 (%@#!) writes to sscanf strings */
+ tmp_version = (char *)strdup("$min_xml_version");
+ if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
+ printf("%s, bad version string from xml2-config\n", "$min_xml_version");
+ exit(1);
+ }
+ free(tmp_version);
+
+ /* Capture the version information from the header files */
+ tmp_version = (char *)strdup(LIBXML_DOTTED_VERSION);
+ if (sscanf(tmp_version, "%d.%d.%d", &xml_major_version, &xml_minor_version, &xml_micro_version) != 3) {
+ printf("%s, bad version string from libxml includes\n", "LIBXML_DOTTED_VERSION");
+ exit(1);
+ }
+ free(tmp_version);
+
+ /* Compare xml2-config output to the libxml headers */
+ if ((xml_major_version != $xml_config_major_version) ||
+ (xml_minor_version != $xml_config_minor_version) ||
+ (xml_micro_version != $xml_config_micro_version))
+ {
+ printf("*** libxml header files (version %d.%d.%d) do not match\n",
+ xml_major_version, xml_minor_version, xml_micro_version);
+ printf("*** xml2-config (version %d.%d.%d)\n",
+ $xml_config_major_version, $xml_config_minor_version, $xml_config_micro_version);
+ return 1;
+ }
+/* Compare the headers to the library to make sure we match */
+ /* Less than ideal -- doesn't provide us with return value feedback,
+ * only exits if there's a serious mismatch between header and library.
+ */
+ LIBXML_TEST_VERSION;
+
+ /* Test that the library is greater than our minimum version */
+ if ((xml_major_version > major) ||
+ ((xml_major_version == major) && (xml_minor_version > minor)) ||
+ ((xml_major_version == major) && (xml_minor_version == minor) &&
+ (xml_micro_version >= micro)))
+ {
+ return 0;
+ }
+ else
+ {
+ printf("\n*** An old version of libxml (%d.%d.%d) was found.\n",
+ xml_major_version, xml_minor_version, xml_micro_version);
+ printf("*** You need a version of libxml newer than %d.%d.%d. The latest version of\n",
+ major, minor, micro);
+ printf("*** libxml is always available from ftp://ftp.xmlsoft.org.\n");
+ printf("***\n");
+ printf("*** If you have already installed a sufficiently new version, this error\n");
+ printf("*** probably means that the wrong copy of the xml2-config shell script is\n");
+ printf("*** being found. The easiest way to fix this is to remove the old version\n");
+ printf("*** of LIBXML, but you can also set the XML2_CONFIG environment to point to the\n");
+ printf("*** correct copy of xml2-config. (In this case, you will have to\n");
+ printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
+ printf("*** so that the correct libraries are found at run-time))\n");
+ }
+ return 1;
+}
+],, no_xml=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
+ CFLAGS="$ac_save_CFLAGS"
+ LIBS="$ac_save_LIBS"
+ fi
+ fi
+
+ if test "x$no_xml" = x ; then
+ AC_MSG_RESULT(yes (version $xml_config_major_version.$xml_config_minor_version.$xml_config_micro_version))
+ ifelse([$2], , :, [$2])
+ else
+ AC_MSG_RESULT(no)
+ if test "$XML2_CONFIG" = "no" ; then
+ echo "*** The xml2-config script installed by LIBXML could not be found"
+ echo "*** If libxml was installed in PREFIX, make sure PREFIX/bin is in"
+ echo "*** your path, or set the XML2_CONFIG environment variable to the"
+ echo "*** full path to xml2-config."
+ else
+ if test -f conf.xmltest ; then
+ :
+ else
+ echo "*** Could not run libxml test program, checking why..."
+ CFLAGS="$CFLAGS $XML_CFLAGS"
+ LIBS="$LIBS $XML_LIBS"
+ AC_TRY_LINK([
+#include <libxml/xmlversion.h>
+#include <stdio.h>
+], [ LIBXML_TEST_VERSION; return 0;],
+ [ echo "*** The test program compiled, but did not run. This usually means"
+ echo "*** that the run-time linker is not finding LIBXML or finding the wrong"
+ echo "*** version of LIBXML. If it is not finding LIBXML, you'll need to set your"
+ echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+ echo "*** to the installed location Also, make sure you have run ldconfig if that"
+ echo "*** is required on your system"
+ echo "***"
+ echo "*** If you have an old version installed, it is best to remove it, although"
+ echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ],
+ [ echo "*** The test program failed to compile or link. See the file config.log for the"
+ echo "*** exact error that occured. This usually means LIBXML was incorrectly installed"
+ echo "*** or that you have moved LIBXML since it was installed. In the latter case, you"
+ echo "*** may want to edit the xml2-config script: $XML2_CONFIG" ])
+ CFLAGS="$ac_save_CFLAGS"
+ LIBS="$ac_save_LIBS"
+ fi
+ fi
+
+ XML_CFLAGS=""
+ XML_LIBS=""
+ ifelse([$3], , :, [$3])
+ fi
+ AC_SUBST(XML_CFLAGS)
+ AC_SUBST(XML_LIBS)
+ rm -f conf.xmltest
+])
+
+# =========================================================================
+# AM_PATH_MYSQL : MySQL library
+
+dnl AM_PATH_MYSQL([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
+dnl Test for MYSQL, and define MYSQL_CFLAGS and MYSQL_LIBS
+dnl
+AC_DEFUN(AM_PATH_MYSQL,
+[dnl
+dnl Get the cflags and libraries from the mysql_config script
+dnl
+AC_ARG_WITH(mysql-prefix,[ --with-mysql-prefix=PFX Prefix where MYSQL is installed (optional)],
+ mysql_prefix="$withval", mysql_prefix="")
+AC_ARG_WITH(mysql-exec-prefix,[ --with-mysql-exec-prefix=PFX Exec prefix where MYSQL is installed (optional)],
+ mysql_exec_prefix="$withval", mysql_exec_prefix="")
+
+ if test x$mysql_exec_prefix != x ; then
+ mysql_args="$mysql_args --exec-prefix=$mysql_exec_prefix"
+ if test x${MYSQL_CONFIG+set} != xset ; then
+ MYSQL_CONFIG=$mysql_exec_prefix/bin/mysql_config
+ fi
+ fi
+ if test x$mysql_prefix != x ; then
+ mysql_args="$mysql_args --prefix=$mysql_prefix"
+ if test x${MYSQL_CONFIG+set} != xset ; then
+ MYSQL_CONFIG=$mysql_prefix/bin/mysql_config
+ fi
+ fi
+
+ AC_REQUIRE([AC_CANONICAL_TARGET])
+ AC_PATH_PROG(MYSQL_CONFIG, mysql_config, no)
+ AC_MSG_CHECKING(for MYSQL)
+ no_mysql=""
+ if test "$MYSQL_CONFIG" = "no" ; then
+ MYSQL_CFLAGS=""
+ MYSQL_LIBS=""
+ AC_MSG_RESULT(no)
+ ifelse([$2], , :, [$2])
+ else
+ MYSQL_CFLAGS=`$MYSQL_CONFIG $mysqlconf_args --cflags | sed -e "s/'//g"`
+ MYSQL_LIBS=`$MYSQL_CONFIG $mysqlconf_args --libs | sed -e "s/'//g"`
+ AC_MSG_RESULT(yes)
+ ifelse([$1], , :, [$1])
+ fi
+ AC_SUBST(MYSQL_CFLAGS)
+ AC_SUBST(MYSQL_LIBS)
+])
+
+# =========================================================================
+# AM_PATH_PGSQL : pgSQL library
+
+dnl AM_PATH_PGSQL([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
+dnl Test for PGSQL, and define PGSQL_CFLAGS and PGSQL_LIBS
+dnl
+AC_DEFUN(AM_PATH_PGSQL,
+[dnl
+dnl Get the cflags and libraries from the pg_config script
+dnl
+AC_ARG_WITH(pgsql-prefix,[ --with-pgsql-prefix=PFX Prefix where PostgreSQL is installed (optional)],
+ pgsql_prefix="$withval", pgsql_prefix="")
+AC_ARG_WITH(pgsql-exec-prefix,[ --with-pgsql-exec-prefix=PFX Exec prefix where PostgreSQL is installed (optional)],
+ pgsql_exec_prefix="$withval", pgsql_exec_prefix="")
+
+ if test x$pgsql_exec_prefix != x ; then
+ if test x${PGSQL_CONFIG+set} != xset ; then
+ PGSQL_CONFIG=$pgsql_exec_prefix/bin/pg_config
+ fi
+ fi
+ if test x$pgsql_prefix != x ; then
+ if test x${PGSQL_CONFIG+set} != xset ; then
+ PGSQL_CONFIG=$pgsql_prefix/bin/pg_config
+ fi
+ fi
+
+ AC_REQUIRE([AC_CANONICAL_TARGET])
+ AC_PATH_PROG(PGSQL_CONFIG, pg_config, no, [$PATH:/usr/lib/postgresql/bin])
+ AC_MSG_CHECKING(for PGSQL)
+ no_pgsql=""
+ if test "$PGSQL_CONFIG" = "no" ; then
+ PGSQL_CFLAGS=""
+ PGSQL_LIBS=""
+ AC_MSG_RESULT(no)
+ ifelse([$2], , :, [$2])
+ else
+ PGSQL_CFLAGS=-I`$PGSQL_CONFIG --includedir`
+ PGSQL_LIBS="-lpq -L`$PGSQL_CONFIG --libdir`"
+ AC_MSG_RESULT(yes)
+ ifelse([$1], , :, [$1])
+ fi
+ AC_SUBST(PGSQL_CFLAGS)
+ AC_SUBST(PGSQL_LIBS)
+])
+
+dnl Removes -I/usr/include/? from given variable
+AC_DEFUN(CFLAGS_REMOVE_USR_INCLUDE,[
+ ac_new_flags=""
+ for i in [$]$1; do
+ case [$]i in
+ -I/usr/include|-I/usr/include/) ;;
+ *) ac_new_flags="[$]ac_new_flags [$]i" ;;
+ esac
+ done
+ $1=[$]ac_new_flags
+])
+
+dnl Removes -L/usr/lib/? from given variable
+AC_DEFUN(LIB_REMOVE_USR_LIB,[
+ ac_new_flags=""
+ for i in [$]$1; do
+ case [$]i in
+ -L/usr/lib|-L/usr/lib/) ;;
+ *) ac_new_flags="[$]ac_new_flags [$]i" ;;
+ esac
+ done
+ $1=[$]ac_new_flags
+])
+
+dnl From Bruno Haible.
+
+AC_DEFUN(jm_ICONV,
+[
+ dnl Some systems have iconv in libc, some have it in libiconv (OSF/1 and
+ dnl those with the standalone portable libiconv installed).
+ AC_MSG_CHECKING(for iconv in $1)
+ jm_cv_func_iconv="no"
+ jm_cv_lib_iconv=""
+ jm_cv_giconv=no
+ jm_save_LIBS="$LIBS"
+ LIBS="$LIBS -lbiconv"
+ AC_TRY_LINK([#include <stdlib.h>
+#include <biconv.h>],
+ [iconv_t cd = iconv_open("","");
+ iconv(cd,NULL,NULL,NULL,NULL);
+ iconv_close(cd);],
+ jm_cv_func_iconv=yes
+ jm_cv_biconv=yes
+ jm_cv_include="biconv.h"
+ jm_cv_lib_iconv="biconv")
+ LIBS="$jm_save_LIBS"
+
+ dnl Check for include in funny place but no lib needed
+ if test "$jm_cv_func_iconv" != yes; then
+ AC_TRY_LINK([#include <stdlib.h>
+#include <giconv.h>],
+ [iconv_t cd = iconv_open("","");
+ iconv(cd,NULL,NULL,NULL,NULL);
+ iconv_close(cd);],
+ jm_cv_func_iconv=yes
+ jm_cv_include="giconv.h"
+ jm_cv_giconv="yes"
+ jm_cv_lib_iconv="")
+
+ dnl Standard iconv.h include, lib in glibc or libc ...
+ if test "$jm_cv_func_iconv" != yes; then
+ AC_TRY_LINK([#include <stdlib.h>
+#include <iconv.h>],
+ [iconv_t cd = iconv_open("","");
+ iconv(cd,NULL,NULL,NULL,NULL);
+ iconv_close(cd);],
+ jm_cv_include="iconv.h"
+ jm_cv_func_iconv=yes
+ jm_cv_lib_iconv="")
+
+ if test "$jm_cv_lib_iconv" != yes; then
+ jm_save_LIBS="$LIBS"
+ LIBS="$LIBS -lgiconv"
+ AC_TRY_LINK([#include <stdlib.h>
+#include <giconv.h>],
+ [iconv_t cd = iconv_open("","");
+ iconv(cd,NULL,NULL,NULL,NULL);
+ iconv_close(cd);],
+ jm_cv_lib_iconv=yes
+ jm_cv_func_iconv=yes
+ jm_cv_include="giconv.h"
+ jm_cv_giconv=yes
+ jm_cv_lib_iconv="giconv")
+
+ LIBS="$jm_save_LIBS"
+
+ if test "$jm_cv_func_iconv" != yes; then
+ jm_save_LIBS="$LIBS"
+ LIBS="$LIBS -liconv"
+ AC_TRY_LINK([#include <stdlib.h>
+#include <iconv.h>],
+ [iconv_t cd = iconv_open("","");
+ iconv(cd,NULL,NULL,NULL,NULL);
+ iconv_close(cd);],
+ jm_cv_include="iconv.h"
+ jm_cv_func_iconv=yes
+ jm_cv_lib_iconv="iconv")
+ LIBS="$jm_save_LIBS"
+ fi
+ fi
+ fi
+ fi
+ if test "$jm_cv_func_iconv" = yes; then
+ if test "$jm_cv_giconv" = yes; then
+ AC_DEFINE(HAVE_GICONV, 1, [What header to include for iconv() function: giconv.h])
+ AC_MSG_RESULT(yes)
+ ICONV_FOUND=yes
+ else
+ if test "$jm_cv_biconv" = yes; then
+ AC_DEFINE(HAVE_BICONV, 1, [What header to include for iconv() function: biconv.h])
+ AC_MSG_RESULT(yes)
+ ICONV_FOUND=yes
+ else
+ AC_DEFINE(HAVE_ICONV, 1, [What header to include for iconv() function: iconv.h])
+ AC_MSG_RESULT(yes)
+ ICONV_FOUND=yes
+ fi
+ fi
+ else
+ AC_MSG_RESULT(no)
+ fi
+])
+
+AC_DEFUN(rjs_CHARSET,[
+ dnl Find out if we can convert from $1 to UCS2-LE
+ AC_MSG_CHECKING([can we convert from $1 to UCS2-LE?])
+ AC_TRY_RUN([
+#include <$jm_cv_include>
+main(){
+ iconv_t cd = iconv_open("$1", "UCS-2LE");
+ if (cd == 0 || cd == (iconv_t)-1) {
+ return -1;
+ }
+ return 0;
+}
+ ],ICONV_CHARSET=$1,ICONV_CHARSET=no,ICONV_CHARSET=cross)
+ AC_MSG_RESULT($ICONV_CHARSET)
+])
+
+dnl CFLAGS_ADD_DIR(CFLAGS, $INCDIR)
+dnl This function doesn't add -I/usr/include into CFLAGS
+AC_DEFUN(CFLAGS_ADD_DIR,[
+if test "$2" != "/usr/include" ; then
+ $1="$$1 -I$2"
+fi
+])
+
+dnl LIB_ADD_DIR(LDFLAGS, $LIBDIR)
+dnl This function doesn't add -L/usr/lib into LDFLAGS
+AC_DEFUN(LIB_ADD_DIR,[
+if test "$2" != "/usr/lib" ; then
+ $1="$$1 -L$2"
+fi
+])
+
+dnl AC_ENABLE_SHARED - implement the --enable-shared flag
+dnl Usage: AC_ENABLE_SHARED[(DEFAULT)]
+dnl Where DEFAULT is either `yes' or `no'. If omitted, it defaults to
+dnl `yes'.
+AC_DEFUN([AC_ENABLE_SHARED],
+[define([AC_ENABLE_SHARED_DEFAULT], ifelse($1, no, no, yes))dnl
+AC_ARG_ENABLE(shared,
+changequote(<<, >>)dnl
+<< --enable-shared[=PKGS] build shared libraries [default=>>AC_ENABLE_SHARED_DEFAULT],
+changequote([, ])dnl
+[p=${PACKAGE-default}
+case $enableval in
+yes) enable_shared=yes ;;
+no) enable_shared=no ;;
+*)
+ enable_shared=no
+ # Look at the argument we got. We use all the common list separators.
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:,"
+ for pkg in $enableval; do
+ if test "X$pkg" = "X$p"; then
+ enable_shared=yes
+ fi
+
+ done
+ IFS="$ac_save_ifs"
+ ;;
+esac],
+enable_shared=AC_ENABLE_SHARED_DEFAULT)dnl
+])
+
+dnl AC_ENABLE_STATIC - implement the --enable-static flag
+dnl Usage: AC_ENABLE_STATIC[(DEFAULT)]
+dnl Where DEFAULT is either `yes' or `no'. If omitted, it defaults to
+dnl `yes'.
+AC_DEFUN([AC_ENABLE_STATIC],
+[define([AC_ENABLE_STATIC_DEFAULT], ifelse($1, no, no, yes))dnl
+AC_ARG_ENABLE(static,
+changequote(<<, >>)dnl
+<< --enable-static[=PKGS] build static libraries [default=>>AC_ENABLE_STATIC_DEFAULT],
+changequote([, ])dnl
+[p=${PACKAGE-default}
+case $enableval in
+yes) enable_static=yes ;;
+no) enable_static=no ;;
+*)
+ enable_static=no
+ # Look at the argument we got. We use all the common list separators.
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:,"
+ for pkg in $enableval; do
+ if test "X$pkg" = "X$p"; then
+ enable_static=yes
+ fi
+ done
+ IFS="$ac_save_ifs"
+ ;;
+esac],
+enable_static=AC_ENABLE_STATIC_DEFAULT)dnl
+])
+
+dnl AC_DISABLE_STATIC - set the default static flag to --disable-static
+AC_DEFUN([AC_DISABLE_STATIC],
+[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl
+AC_ENABLE_STATIC(no)])
+
+dnl AC_TRY_RUN_STRICT(PROGRAM,CFLAGS,CPPFLAGS,LDFLAGS,
+dnl [ACTION-IF-TRUE],[ACTION-IF-FALSE],
+dnl [ACTION-IF-CROSS-COMPILING = RUNTIME-ERROR])
+AC_DEFUN( [AC_TRY_RUN_STRICT],
+[
+ old_CFLAGS="$CFLAGS";
+ CFLAGS="$2";
+ export CFLAGS;
+ old_CPPFLAGS="$CPPFLAGS";
+ CPPFLAGS="$3";
+ export CPPFLAGS;
+ old_LDFLAGS="$LDFLAGS";
+ LDFLAGS="$4";
+ export LDFLAGS;
+ AC_TRY_RUN([$1],[$5],[$6],[$7]);
+ CFLAGS="$old_CFLAGS";
+ old_CFLAGS="";
+ export CFLAGS;
+ CPPFLAGS="$old_CPPFLAGS";
+ old_CPPFLAGS="";
+ export CPPFLAGS;
+ LDFLAGS="$old_LDFLAGS";
+ old_LDFLAGS="";
+ export LDFLAGS;
+])
diff --git a/source/aparser/Makefile b/source/aparser/Makefile
new file mode 100644
index 00000000000..953743b234d
--- /dev/null
+++ b/source/aparser/Makefile
@@ -0,0 +1,17 @@
+CFLAGS=-Wall -g
+CC=gcc
+
+OBJ = vluke.o parser.o
+AWKPROGS=dump.awk harness.awk header.awk parsefn.awk main.awk parsetree.awk template.awk util.awk
+
+all: test.h vluke
+
+test.h : $(AWKPROGS)
+ igawk -f main.awk srvsvc.struct
+
+vluke: test.h $(OBJ)
+ $(CC) $(CFLAGS) -o vluke $(OBJ)
+
+clean:
+ rm -f *.o test.h prs_*.[ch]
+
diff --git a/source/aparser/build b/source/aparser/build
new file mode 100755
index 00000000000..4cdf2901f80
--- /dev/null
+++ b/source/aparser/build
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+file=$1
+
+if ! igawk -f main.awk $file; then
+ echo parse failed;
+ exit 1;
+fi
+
+echo compiling vluke
+gcc -Wall -g -o vluke parser.c vluke.c util.c
+echo done.
+
diff --git a/source/aparser/cifs.struct b/source/aparser/cifs.struct
new file mode 100644
index 00000000000..202f0d7e619
--- /dev/null
+++ b/source/aparser/cifs.struct
@@ -0,0 +1,1029 @@
+module cifs
+
+option autoalign False
+
+
+#define BOOL uint32
+#define UCHAR uint8
+#define WCHAR uint16
+#define USHORT uint16
+#define LONG uint32
+#define ULONG uint32
+#define DWORD uint32
+#define SMB_TIME uint16
+#define SMB_DATE uint16
+
+typedef struct {
+ ULONG low;
+ LONG high;
+} TIME;
+
+typedef struct {
+ ULONG low;
+ ULONG high;
+} hyper;
+
+typedef struct {
+ uint8 cmd;
+ uint8 reserved;
+ uint16 offset;
+} ANDX_INFO;
+
+
+typedef struct {
+ uint8 tag2;
+ STRING protocol;
+} BUF2;
+
+typedef struct {
+ uint16 bcount;
+ BUF2 protocol[*];
+} Q_NEGPROT_0;
+
+typedef struct {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 0 Q_NEGPROT_0 q0;
+ }
+} Q_NEGPROT;
+
+typedef struct {
+ USHORT DialectIndex; /* Index of selected dialect */
+ USHORT SecurityMode; /* Security mode: */
+ /* bit 0: 0 = share, 1 = user */
+ /* bit 1: 1 = use challenge/response */
+ /* authentication */
+ USHORT MaxBufferSize; /* Max transmit buffer size (>= 1024) */
+ USHORT MaxMpxCount; /* Max pending multiplexed requests */
+ USHORT MaxNumberVcs; /* Max VCs between client and server */
+ USHORT RawMode; /* Raw modes supported: */
+ /* bit 0: 1 = Read Raw supported */
+ /* bit 1: 1 = Write Raw supported */
+ ULONG SessionKey; /* Unique token identifying this session */
+ SMB_TIME ServerTime; /* Current time at server */
+ SMB_DATE ServerDate; /* Current date at server */
+ USHORT ServerTimeZone; /* Current time zone at server */
+ USHORT ChallengeLength; /* Length of Challenge; MBZ if not LM2.1
+ /* dialect or later */
+ USHORT Reserved; /* MBZ */
+ USHORT ByteCount; /* Count of data bytes */
+ UCHAR Challenge[ChallengeLength]; /* The challenge */
+ STRING PrimaryDomain; /* The server's primary domain */
+
+} R_NEGPROT_12;
+
+typedef struct {
+ USHORT DialectIndex; /*Index of selected dialect */
+ UCHAR SecurityMode; /*Security mode: */
+ /* bit 0: 0 = share, 1 = user */
+ /* bit 1: 1 = use challenge/response */
+ /* authentication */
+ /* bit 2: 1 = Security Signatures (SMB integrity */
+ /* check) enabled */
+ /* bit 3: 1 = Security Signatures (SMB integrity */
+ /* check) required */
+ USHORT MaxMpxCount; /*Max pending outstanding requests */
+ USHORT MaxNumberVcs; /*Max VCs between client and server */
+ ULONG MaxBufferSize; /*Max transmit buffer size */
+ ULONG MaxRawSize; /*Maximum raw buffer size */
+ ULONG SessionKey; /*Unique token identifying this session */
+ ULONG Capabilities; /*Server capabilities */
+ ULONG SystemTimeLow; /*System (UTC) time of the server (low). */
+ ULONG SystemTimeHigh; /*System (UTC) time of the server (high). */
+ USHORT ServerTimeZone;/*Time zone of server (minutes from UTC) */
+ UCHAR SecurityBlobLength;/*Length of SecurityBlob */
+
+ USHORT bcount; /*Count of data bytes */
+ /*UCHAR GUID[16]; A globally unique identifier assigned to the */
+ /* server; present only when */
+ /* CAP_EXTENDED_SECURITY is on in the */
+ /* Capabilities field. */
+ UCHAR SecurityBlob[SecurityBlobLength]; /*Opaque Security Blob associated with the */
+ /* security package if CAP_EXTENDED_SECURITY is */
+ /* on in the Capabilities field; else challenge */
+ /* for CIFS challenge/response authentication. */
+ STRING OemDomainName[+]; /*The name of the domain (in OEM chars); not */
+ /* present if CAP_EXTENDED_SECURITY is on in the */
+ /* Capabilities field */
+} R_NEGPROT_17;
+
+
+typedef struct {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 17 R_NEGPROT_17 r17;
+ }
+} R_NEGPROT;
+
+typedef struct {
+ uint8 wcount;
+ uint16 vwv[wcount];
+ uint16 bcount;
+ uint8 none[bcount];
+} Q_TDIS;
+
+typedef struct {
+ uint8 wcount;
+ uint16 vwv[wcount];
+ uint16 bcount;
+ uint8 none[bcount];
+} R_TDIS;
+
+typedef struct {
+ ANDX_INFO andx;
+ uint16 bcount;
+ uint8 none[bcount];
+} R_ULOGOFF_ANDX_2;
+
+typedef struct {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 2 R_ULOGOFF_ANDX_2 q2;
+ }
+} R_ULOGOFF_ANDX;
+
+typedef struct {
+ ANDX_INFO andx;
+ uint16 bcount;
+ uint8 none[bcount];
+} Q_ULOGOFF_ANDX_2;
+
+typedef struct {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 2 Q_ULOGOFF_ANDX_2 q2;
+ }
+} Q_ULOGOFF_ANDX;
+
+typedef struct {
+ ANDX_INFO andx;
+ uint16 bufsize;
+ uint16 max_mpx;
+ uint16 vc;
+ ULONG sess_key;
+ uint16 pwlen;
+ ULONG reserved;
+
+ uint16 bcount;
+ uint8 password[pwlen];
+ STRING domain;
+ STRING os;
+ STRING server;
+
+} Q_SESSION_SETUP_ANDX_10;
+
+typedef struct {
+ ANDX_INFO andx;
+ uint16 bufsize;
+ uint16 max_mpx;
+ uint16 vc;
+ ULONG sess_key;
+ uint16 pwlen;
+ uint16 upwlen;
+ ULONG capabilities;
+ ULONG reserved;
+
+ uint16 bcount;
+ uint8 password[pwlen];
+ uint8 upassword[upwlen];
+ STRING user;
+ STRING domain;
+ STRING os;
+ STRING server;
+
+} Q_SESSION_SETUP_ANDX_13;
+
+typedef struct _Q_SESSION_SETUP_ANDX {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 10 Q_SESSION_SETUP_ANDX_10 q10;
+ case 13 Q_SESSION_SETUP_ANDX_13 q13;
+ }
+} Q_SESSION_SETUP_ANDX;
+
+typedef struct {
+ ANDX_INFO andx;
+ uint16 vwv2;
+ uint16 passlen;
+ uint16 bcount;
+ uint8 password[passlen];
+ STRING path;
+ STRING device;
+} Q_TCON_ANDX_4;
+
+typedef struct _Q_TCON_ANDX {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 4 Q_TCON_ANDX_4 q4;
+ }
+} Q_TCON_ANDX;
+
+typedef struct {
+ ANDX_INFO andx;
+ uint16 vwv2;
+ uint16 bcount;
+ STRING share;
+} R_TCON_ANDX_3;
+
+typedef struct _R_TCON_ANDX {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 3 R_TCON_ANDX_3 q3;
+ }
+} R_TCON_ANDX;
+
+typedef struct {
+ ANDX_INFO andx;
+ uint16 action;
+
+ uint16 count;
+ STRING os;
+ STRING server;
+ STRING domain;
+} R_SESSION_SETUP_ANDX_10;
+
+typedef struct _R_SESSION_SETUP_ANDX {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 3 R_SESSION_SETUP_ANDX_10 r3;
+ }
+} R_SESSION_SETUP_ANDX;
+
+
+typedef struct _R_CLOSE {
+ uint8 wcount;
+ uint16 count;
+ uint8 none[count];
+
+} R_CLOSE;
+
+typedef struct _Q_CLOSE {
+ uint8 wcount;
+ uint16 fnum;
+ uint32 vwv1;
+
+ uint16 count;
+ uint8 none[count];
+
+} Q_CLOSE;
+
+typedef struct {
+ uint16 dsize;
+ uint16 bsizehi;
+ uint16 bsizelo;
+ uint16 avail;
+ uint16 vwv4;
+
+ uint16 bcount;
+ uint8 none[bcount];
+
+} R_DSKATTR_5;
+
+typedef struct {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 5 R_DSKATTR_5 r5;
+ }
+} R_DSKATTR;
+
+typedef struct {
+ uint16 count;
+ uint8 none[count];
+
+} Q_DSKATTR_0;
+
+typedef struct _Q_DSKATTR {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 0 Q_DSKATTR_0 q1;
+ }
+
+} Q_DSKATTR;
+
+typedef struct {
+ ANDX_INFO andx;
+
+ uint16 bcount;
+ uint8 none[bcount];
+
+} R_LOCKING_2;
+
+typedef struct {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 2 R_LOCKING_2 r2;
+ }
+} R_LOCKING_ANDX;
+
+/* XXXX must do a switch on bit 0x10 to do large locks XXXX */
+/* LockType Flag Name Value Description */
+
+#define LOCKING_ANDX_SHARED_LOCK 0x01 /* Read-only lock */
+#define LOCKING_ANDX_OPLOCK_RELEASE 0x02 /* Oplock break notification */
+#define LOCKING_ANDX_CHANGE_LOCKTYPE 0x04 /* Change lock type */
+#define LOCKING_ANDX_CANCEL_LOCK 0x08 /* Cancel outstanding request */
+#define LOCKING_ANDX_LARGE_FILES 0x10 /* Large file locking format */
+
+typedef struct {
+ USHORT Pid; /* PID of process "owning" lock */
+ ULONG Offset; /* Offset to bytes to [un]lock */
+ ULONG Length; /* Number of bytes to [un]lock */
+} LOCKING_ANDX_RANGE_SHORT;
+
+typedef struct {
+ USHORT Pid; /* PID of process "owning" lock */
+ .align4 0;
+ ULONG OffsetHigh; /* Offset to bytes to [un]lock (high) */
+ ULONG OffsetLow; /* Offset to bytes to [un]lock (low) */
+ ULONG LengthHigh; /* Number of bytes to [un]lock (high) */
+ ULONG LengthLow; /* Number of bytes to [un]lock (low) */
+
+} LOCKING_ANDX_RANGE_LARGE;
+
+/* typedef struct { */
+ /* union ctr[LockType&0x10] { */
+ /* case 0 LOCKING_ANDX_RANGE_SHORT ls; */
+ /* case 0x10 LOCKING_ANDX_RANGE_LARGE ll; */
+ /* } */
+/* } LOCKING_ANDX_RANGE; */
+
+typedef struct {
+ ANDX_INFO andx;
+
+ USHORT Fid; /* File handle */
+ UCHAR LockType; /* See LockType table below */
+ UCHAR OplockLevel; /* The new oplock level */
+ ULONG Timeout; /* Milliseconds to wait for unlock */
+ USHORT NumberOfUnlocks; /* Num. unlock range structs following */
+ USHORT NumberOfLocks; /* Num. lock range structs following */
+
+ USHORT ByteCount; /* Count of data bytes */
+ LOCKING_ANDX_RANGE_SHORT Unlocks[NumberOfUnlocks]; /* Unlock ranges */
+ LOCKING_ANDX_RANGE_SHORT Locks[NumberOfLocks]; /* Lock ranges */
+
+} Q_LOCKING_8;
+
+typedef struct _Q_LOCKING {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 8 Q_LOCKING_8 q8;
+ }
+
+} Q_LOCKING_ANDX;
+
+
+typedef struct {
+ uint16 bcount;
+ uint8 protocols[bcount];
+
+} R_UNLINK_0;
+
+typedef struct {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 0 R_UNLINK_0 r0;
+ }
+} R_UNLINK;
+
+typedef struct {
+ uint16 dirtype;
+
+ uint16 count;
+ uint8 fname[count];
+
+} Q_UNLINK_1;
+
+typedef struct _Q_UNLINK {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 1 Q_UNLINK_1 q1;
+ }
+
+} Q_UNLINK;
+
+typedef struct _R_OPEN_ANDX{
+ uint8 wcount;
+ ANDX_INFO andx;
+ uint16 fnum;
+ uint16 fmode;
+ uint32 mtime;
+ uint32 size;
+ uint16 rmode;
+ uint16 vwv9;
+ uint16 vwv10;
+ uint16 smb_action;
+ uint16 vwv12;
+ uint16 vwv13;
+ uint16 vwv14;
+
+ uint16 count;
+ uint8 none[count];
+
+} R_OPEN_ANDX;
+
+typedef struct _Q_OPEN_ANDX{
+ uint8 wcount;
+ ANDX_INFO andx;
+ uint16 fnum;
+ uint16 fmode;
+ uint32 mtime;
+ uint32 size;
+ uint16 rmode;
+ uint16 vwv9;
+ uint16 vwv10;
+ uint16 smb_action;
+ uint16 vwv12;
+ uint16 vwv13;
+ uint16 vwv14;
+
+ uint16 count;
+ uint8 fname[count];
+
+} Q_OPEN_ANDX;
+
+typedef struct _R_READ_ANDX {
+ uint8 wcount;
+ ANDX_INFO andx;
+ uint16 vwv2;
+ uint16 vwv3;
+ uint16 vwv4;
+ uint16 nread;
+ uint16 offset;
+ uint16 vwv7;
+ uint16 vwv8;
+ uint16 vwv9;
+ uint16 vwv10;
+ uint16 vwv11;
+
+ uint16 count;
+ uint8 data[count];
+
+} R_READ_ANDX;
+
+typedef struct _Q_READ_ANDX_10 {
+ ANDX_INFO andx;
+ uint16 fnum;
+ uint32 startpos;
+ uint16 smb_maxcnt;
+ uint16 smb_mincnt;
+ uint16 vwv7;
+ uint16 vwv8;
+ uint16 vwv9;
+
+ uint16 count;
+ uint8 none[count];
+
+} Q_READ_ANDX_10;
+
+typedef struct _Q_READ_ANDX_12 {
+ ANDX_INFO andx;
+ uint16 fnum;
+ uint32 startpos;
+ uint16 smb_maxcnt;
+ uint16 smb_mincnt;
+ uint16 vwv7;
+ uint16 vwv8;
+ uint16 vwv9;
+ uint32 startposhi;
+
+ uint16 count;
+ uint8 none[count];
+
+} Q_READ_ANDX_12;
+
+typedef struct _Q_READ_ANDX {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 10 Q_READ_ANDX_10 q10;
+ case 12 Q_READ_ANDX_12 q12;
+ }
+} Q_READ_ANDX;
+
+typedef struct _R_WRITE_ANDX {
+ uint8 wcount;
+ ANDX_INFO andx;
+ uint16 nwritten;
+ uint16 vwv3;
+ uint16 vwv4;
+ uint16 vwv5;
+
+ uint16 count;
+ uint8 none[count];
+
+} R_WRITE_ANDX;
+
+typedef struct _Q_WRITE_ANDX_12 {
+ ANDX_INFO andx;
+ uint16 fnum;
+ uint32 startpos;
+ uint16 vwv5;
+ uint16 vwv6;
+ uint16 write_through;
+ uint16 vwv8;
+ uint16 vwv9;
+ uint16 numtowrite;
+ uint16 smb_doff;
+
+ uint16 count;
+ uint8 data[count];
+
+} Q_WRITE_ANDX_12;
+
+typedef struct _Q_WRITE_ANDX_14 {
+ ANDX_INFO andx;
+ uint16 fnum;
+ uint32 startpos;
+ uint16 vwv5;
+ uint16 vwv6;
+ uint16 write_through;
+ uint16 vwv8;
+ uint16 vwv9;
+ uint16 numtowrite;
+ uint16 smb_doff;
+ uint32 startposhi;
+
+ uint16 count;
+ uint8 data[count];
+
+} Q_WRITE_ANDX_14;
+
+typedef struct _Q_WRITE_ANDX {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 12 Q_WRITE_ANDX_12 q12;
+ case 14 Q_WRITE_ANDX_14 q14;
+ }
+} Q_WRITE_ANDX;
+
+
+
+typedef struct _Q_NTTRANS_19 {
+ UCHAR MaxSetupCount; /* Max setup words to return */
+ USHORT Reserved;
+ ULONG TotalParameterCount; /* Total parameter bytes being sent */
+ ULONG TotalDataCount; /* Total data bytes being sent */
+ ULONG MaxParameterCount; /* Max parameter bytes to return */
+ ULONG MaxDataCount; /* Max data bytes to return */
+ ULONG ParameterCount; /* Parameter bytes sent this buffer */
+ ULONG ParameterOffset; /* Offset (from header start) to */
+ /* Parameters */
+ ULONG DataCount; /* Data bytes sent this buffer */
+ ULONG DataOffset; /* Offset (from header start) to data */
+ UCHAR SetupCount; /* Count of setup words */
+ USHORT Function; /* The transaction function code */
+ UCHAR Buffer[1];
+ USHORT Setup[SetupCount]; /* Setup words */
+ USHORT ByteCount; /* Count of data bytes */
+ .align4 0;
+ UCHAR Parameters[ParameterCount];/* Parameter bytes */
+ .align4 0;
+ UCHAR Data[DataCount]; /* Data bytes */
+
+} Q_NTTRANS_19;
+
+typedef struct _Q_NTTRANS {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 19 Q_NTTRANS_19 q19;
+ }
+} Q_NTTRANS;
+
+typedef struct _R_NTTRANS_18 {
+ UCHAR Reserved[3];
+ ULONG TotalParameterCount; /* Total parameter bytes being sent */
+ ULONG TotalDataCount; /* Total data bytes being sent */
+ ULONG ParameterCount; /* Parameter bytes sent this buffer */
+ ULONG ParameterOffset; /* Offset (from header start) to */
+ /* Parameters */
+ ULONG ParameterDisplacement; /* Specifies the offset from the start */
+ /* of the overall parameter block to */
+ /* the parameter bytes that are */
+ /* contained in this message */
+ ULONG DataCount; /* Data bytes sent this buffer */
+ ULONG DataOffset; /* Offset (from header start) to data */
+ ULONG DataDisplacement; /* Specifies the offset from the start */
+ /* of the overall data block to the */
+ /* data bytes that are contained in */
+ /* this message. */
+ UCHAR SetupCount; /* Count of setup words */
+ USHORT Setup[SetupCount]; /* Setup words */
+ USHORT ByteCount; /* Count of data bytes */
+ .align4 0;
+ UCHAR Parameters[ParameterCount]; /* Parameter bytes */
+ .align4 0;
+ UCHAR Data[DataCount]; /* Data bytes */
+} R_NTTRANS_18;
+
+typedef struct _R_NTTRANS {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 18 R_NTTRANS_18 q18;
+ }
+ .align4 2;
+} R_NTTRANS;
+
+/*Setup[0] Transaction2 Value Description */
+/*Subcommand Code */
+/*=============================== ===== ============================= */
+
+#define TRANS2_OPEN2 0x00 /* Create file with extended attributes */
+#define TRANS2_FIND_FIRST2 0x01 /* Begin search for files */
+#define TRANS2_FIND_NEXT2 0x02 /* Resume search for files */
+#define TRANS2_QUERY_FS_INFO 0x03 /* Get file system information
+#define TRANS2_RESERVED4 0x04 /* Reserved */
+#define TRANS2_QUERY_PATH_INFO 0x05 /* Get information about a named file or directory */
+#define TRANS2_SET_PATH_INFO 0x06 /* Set information about a named file or directory */
+#define TRANS2_QUERY_FILE_INFO 0x07 /* Get information about a handle */
+#define TRANS2_SET_FILE_INFO 0x08 /* Set information by handle */
+#define TRANS2_FSCTL 0x09 /* Not implemented by NT server */
+#define TRANS2_IOCTL2 0x0A /* Not implemented by NT server */
+#define TRANS2_FIND_NOTIFY_FIRST 0x0B /* Not implemented by NT server */
+#define TRANS2_FIND_NOTIFY_NEXT 0x0C /* Not implemented by NT server */
+#define TRANS2_CREATE_DIRECTORY 0x0D /* Create directory with extended attributes */
+#define TRANS2_SESSION_SETUP 0x0E /* Session setup with extended security information */
+#define TRANS2_GET_DFS_REFERRAL 0x10 /* Get a DFS referral */
+#define TRANS2_REPORT_DFS_INCONSISTENCY 0x11 /* Report a DFS knowledge inconsistency */
+
+typedef struct {
+ USHORT InformationLevel; /* Level of information requested */
+} TRANS2_QUERY_FS_INFO_STRUCT;
+
+#define SMB_INFO_STANDARD 1
+#define SMB_INFO_QUERY_EA_SIZE 2
+#define SMB_SET_FILE_BASIC_INFO 0x101
+#define SMB_SET_FILE_DISPOSITION_INFO 0x102
+#define SMB_SET_FILE_ALLOCATION_INFO 0x103
+#define SMB_SET_FILE_END_OF_FILE_INFO 0x104
+
+
+typedef struct {
+ hyper CreationTime;
+ hyper LastAccessTime;
+ hyper LastWriteTime;
+ hyper ChangeTime;
+ USHORT Attributes;
+ .align4 0;
+} SMB_QUERY_FILE_BASIC_INFO_STRUCT;
+
+
+typedef struct {
+ ULONG fs_atr;
+ LONG max_len_filename;
+ ULONG length;
+ uint8 fs[length];
+ .align4 2;
+} SMB_QUERY_FS_ATTRIBUTE_INFO_STRUCT;
+
+#define FILE_CASE_SENSITIVE_SEARCH 0x00000001
+#define FILE_CASE_PRESERVED_NAMES 0x00000002
+#define FILE_PRSISTENT_ACLS 0x00000004
+#define FILE_FILE_COMPRESSION 0x00000008
+#define FILE_VOLUME_QUOTAS 0x00000010
+#define FILE_DEVICE_IS_MOUNTED 0x00000020
+#define FILE_VOLUME_IS_COMPRESSED 0x00008000
+
+typedef struct {
+ USHORT Fid;
+ USHORT InformationLevel;
+ USHORT Reserved;
+ .align4 0;
+
+ union ctr[InformationLevel] {
+ case 0x101 SMB_QUERY_FILE_BASIC_INFO_STRUCT t101;
+ }
+
+} TRANS2_SET_FILE_INFO_STRUCT;
+
+typedef struct {
+ USHORT InformationLevel; /* Level of information requested */
+ ULONG Reserved; /* Must be zero */
+ STRING FileName; /* File or directory name */
+} TRANS2_QUERY_PATH_INFO_STRUCT;
+
+typedef struct {
+ USHORT SearchAttributes;
+ USHORT SearchCount;
+ USHORT Flags;
+ USHORT InformationLevel;
+ ULONG SearchStorageType;
+ STRING FileName;
+} TRANS2_FIND_FIRST2_STRUCT;
+
+typedef struct _Q_TRANS2_15 {
+ USHORT TotalParameterCount; /* Total parameter bytes being sent */
+ USHORT TotalDataCount; /* Total data bytes being sent */
+ USHORT MaxParameterCount; /* Max parameter bytes to return */
+ USHORT MaxDataCount; /* Max data bytes to return */
+ UCHAR MaxSetupCount; /* Max setup words to return */
+ UCHAR Reserved;
+ USHORT Flags; /* Additional information: */
+ /* bit 0 - also disconnect TID in TID */
+ ULONG Timeout;
+ USHORT Reserved2;
+ USHORT ParameterCount; /* Parameter bytes sent this buffer */
+ USHORT ParameterOffset; /* Offset (from header start) to */
+ /* Parameters */
+ USHORT DataCount; /* Data bytes sent this buffer */
+ USHORT DataOffset; /* Offset (from header start) to data */
+ UCHAR SetupCount; /* Count of setup words */
+ UCHAR Reserved3; /* Reserved (pad above to word) */
+ USHORT Setup[SetupCount]; /* Setup words (# = SetupWordCount) */
+ USHORT ByteCount; /* Count of data bytes */
+ .align4 0;
+ union ctr[Setup[0]] {
+ case 1 TRANS2_FIND_FIRST2_STRUCT t1;
+ case 3 TRANS2_QUERY_FS_INFO_STRUCT t3;
+ case 5 TRANS2_QUERY_PATH_INFO_STRUCT t5;
+ case 8 TRANS2_SET_FILE_INFO_STRUCT t8;
+ }
+
+} Q_TRANS2_15;
+
+typedef struct _Q_TRANS2 {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 15 Q_TRANS2_15 q15;
+ }
+} Q_TRANS2;
+
+typedef struct {
+ ULONG NextEntryOffset;
+ ULONG FileIndex;
+ hyper CreationTime;
+ hyper LastAccessTime;
+ hyper LastWriteTime;
+ hyper ChangeTime;
+ hyper EndOfFile;
+ hyper AllocationSize;
+ ULONG ExtFileAttributes;
+ ULONG FileNameLength;
+ ULONG EaSize;
+ UCHAR ShortNameLength;
+ UCHAR Reserved;
+ uint8 ShortName[24];
+ UCHAR FileName[FileNameLength];
+ .align4 2;
+} SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
+
+typedef struct {
+ .align2 0;
+} R_TRANS2_D0;
+
+typedef struct {
+ .align4 2;
+} R_TRANS2_P0;
+
+typedef struct {
+ USHORT Reserved;
+} R_TRANS2_P2;
+
+typedef struct {
+ USHORT Sid; /* Search handle */
+ USHORT SearchCount; /* Number of entries returned */
+ USHORT EndOfSearch; /* Was last entry returned? */
+ USHORT EaErrorOffset; /* Offset into EA list if EA error */
+ USHORT LastNameOffset; /* Offset into data to file name of last */
+ /* entry, if server needs it to resume */
+ /* search; else 0 */
+ .align4 2;
+ SMB_FIND_FILE_BOTH_DIRECTORY_INFO i104[SearchCount];
+} R_TRANS2_FIND_FIRST2_STRUCT;
+
+typedef struct {
+ SMB_QUERY_FILE_BASIC_INFO_STRUCT i101;
+ .align4 2;
+} R_TRANS2_FILE_BASIC_STRUCT;
+
+typedef struct _R_TRANS2_10 {
+ USHORT TotalParameterCount;/* Total parameter bytes being sent */
+ USHORT TotalDataCount; /* Total data bytes being sent */
+ USHORT Reserved2;
+ USHORT ParameterCount; /* Parameter bytes sent this buffer */
+ USHORT ParameterOffset; /* Offset (from header start) to */
+ /* Parameters */
+ USHORT ParameterDisplacement; /* Specifies the offset from the start */
+ /* of the overall parameter block to */
+ /* the parameter bytes that are */
+ /* contained in this message */
+ USHORT DataCount; /* Data bytes sent this buffer */
+ USHORT DataOffset; /* Offset (from header start) to data */
+ USHORT DataDisplacement; /* Specifies the offset from the start */
+ /* of the overall data block to the */
+ /* data bytes that are contained in */
+ /* this message. */
+ UCHAR SetupCount; /* Count of setup words */
+ UCHAR Reserved3; /* Reserved (pad above to word) */
+ USHORT Setup[SetupCount]; /* Setup words */
+ USHORT ByteCount; /* Count of data bytes */
+ .align4 2;
+ union pctr[ParameterCount] {
+ case 0 R_TRANS2_P0 p0;
+ case 2 R_TRANS2_P2 p2;
+ case 10 R_TRANS2_FIND_FIRST2_STRUCT r10;
+ }
+ union dctr[DataCount] {
+ case 0 R_TRANS2_D0 d0;
+ case 0x24 R_TRANS2_FILE_BASIC_STRUCT r24;
+ case 0x14 SMB_QUERY_FS_ATTRIBUTE_INFO_STRUCT r14;
+ }
+} R_TRANS2_10;
+
+typedef struct {
+ USHORT ByteCount; /* Count of data bytes */
+} R_TRANS2_0;
+
+typedef struct _R_TRANS2 {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 0 R_TRANS2_0 q0;
+ case 10 R_TRANS2_10 q10;
+ }
+} R_TRANS2;
+
+typedef struct _Q_TRANS_16 {
+ USHORT TotalParameterCount; /* Total parameter bytes being sent */
+ USHORT TotalDataCount; /* Total data bytes being sent */
+ USHORT MaxParameterCount; /* Max parameter bytes to return */
+ USHORT MaxDataCount; /* Max data bytes to return */
+ UCHAR MaxSetupCount; /* Max setup words to return */
+ UCHAR Reserved;
+ USHORT Flags; /* Additional information: */
+ /* bit 0 - also disconnect TID in TID */
+ ULONG Timeout;
+ USHORT Reserved2;
+ USHORT ParameterCount; /* Parameter bytes sent this buffer */
+ USHORT ParameterOffset; /* Offset (from header start) to */
+ /* Parameters */
+ USHORT DataCount; /* Data bytes sent this buffer */
+ USHORT DataOffset; /* Offset (from header start) to data */
+ UCHAR SetupCount; /* Count of setup words */
+ UCHAR Reserved3; /* Reserved (pad above to word) */
+ USHORT Setup[SetupCount]; /* Setup words (# = SetupWordCount) */
+ USHORT ByteCount; /* Count of data bytes */
+ STRING Name; /* Must be NULL */
+ .align4 0;
+ UCHAR Parameters[ParameterCount];/* Parameter bytes (# = ParameterCount) */
+ .align4 0;
+ UCHAR Data[DataCount]; /* Data bytes (# = DataCount) */
+
+} Q_TRANS_16;
+
+typedef struct _Q_TRANS {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 16 Q_TRANS_16 q16;
+ }
+} Q_TRANS;
+
+typedef struct _R_TRANS_10 {
+ USHORT TotalParameterCount;/* Total parameter bytes being sent */
+ USHORT TotalDataCount; /* Total data bytes being sent */
+ USHORT Reserved2;
+ USHORT ParameterCount; /* Parameter bytes sent this buffer */
+ USHORT ParameterOffset; /* Offset (from header start) to */
+ /* Parameters */
+ USHORT ParameterDisplacement; /* Specifies the offset from the start */
+ /* of the overall parameter block to */
+ /* the parameter bytes that are */
+ /* contained in this message */
+ USHORT DataCount; /* Data bytes sent this buffer */
+ USHORT DataOffset; /* Offset (from header start) to data */
+ USHORT DataDisplacement; /* Specifies the offset from the start */
+ /* of the overall data block to the */
+ /* data bytes that are contained in */
+ /* this message. */
+ UCHAR SetupCount; /* Count of setup words */
+ UCHAR Reserved3; /* Reserved (pad above to word) */
+ USHORT Setup[SetupCount]; /* Setup words */
+ USHORT ByteCount; /* Count of data bytes */
+ .align4 0;
+ UCHAR Parameters[ParameterCount];/* Parameter bytes */
+ .align4 0;
+ UCHAR Data[DataCount]; /* Data bytes */
+} R_TRANS_10;
+
+typedef struct _R_TRANS {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 10 R_TRANS_10 q10;
+ }
+} R_TRANS;
+
+typedef struct _Q_NT_CREATE_ANDX_24 {
+ ANDX_INFO andx;
+ uint8 reserved;
+ uint16 name_len;
+ ULONG flags;
+ ULONG rootfid;
+ ULONG access;
+ hyper allocsize;
+ ULONG attribs;
+ ULONG sharing;
+ ULONG creat_disp;
+ ULONG creat_options;
+ ULONG impersonation;
+ uint8 sec_flags;
+
+ uint16 count;
+ uint8 name[name_len];
+
+} Q_NTCREATE_ANDX_24;
+
+typedef struct _Q_NTCREATE_ANDX{
+ uint8 wcount;
+ union ctr[wcount] {
+ case 24 Q_NTCREATE_ANDX_24 q24;
+ }
+} Q_NTCREATE_ANDX;
+
+typedef struct {
+ ANDX_INFO andx;
+ uint8 oplock_level;
+ uint16 fid;
+ ULONG action;
+ TIME create_time;
+ TIME access_time;
+ TIME write_time;
+ TIME change_time;
+ ULONG ext_attribs;
+ hyper allocsize;
+ hyper size;
+ uint16 type;
+ uint16 state;
+ uint8 directory;
+
+ uint16 count;
+ uint8 none[count];
+
+} R_NTCREATE_ANDX_34;
+
+typedef struct _R_NTCREATE_ANDX{
+ uint8 wcount;
+ union ctr[wcount] {
+ case 34 R_NTCREATE_ANDX_34 q34;
+ }
+} R_NTCREATE_ANDX;
+
+typedef struct {
+ ULONG smbhdr;
+ uint8 com;
+ uint8 rcls;
+ uint8 reh;
+ uint16 err;
+ uint8 flg;
+ uint16 flg2;
+ uint16 reserved;
+ uint8 SecuritySignature[8];
+ uint16 pad;
+ uint16 tid;
+ uint16 pid;
+ uint16 uid;
+ uint16 mid;
+} SMB_HDR;
+
+typedef struct _R_SMB {
+ ULONG nbhdr;
+ SMB_HDR hdr;
+ union ctr[hdr.com] {
+ case 4 R_CLOSE r4;
+ case 6 R_UNLINK r6;
+ case 36 R_LOCKING_ANDX r36;
+ case 37 R_TRANS r37;
+ case 45 R_OPEN_ANDX r45;
+ case 46 R_READ_ANDX r46;
+ case 47 R_WRITE_ANDX r47;
+ case 50 R_TRANS2 q50;
+ case 113 R_TDIS r113;
+ case 114 R_NEGPROT r114;
+ case 115 R_SESSION_SETUP_ANDX r115;
+ case 116 R_ULOGOFF_ANDX r116;
+ case 117 R_TCON_ANDX r117;
+ case 128 R_DSKATTR r128;
+ case 160 R_NTTRANS r160;
+ case 162 R_NTCREATE_ANDX r162;
+ }
+} R_SMB;
+
+typedef struct _Q_SMB {
+ ULONG nbhdr;
+ SMB_HDR hdr;
+ union ctr[hdr.com] {
+ case 4 Q_CLOSE q4;
+ case 6 Q_UNLINK q6;
+ case 36 Q_LOCKING_ANDX q36;
+ case 37 Q_TRANS q37;
+ case 45 Q_OPEN_ANDX q45;
+ case 46 Q_READ_ANDX q46;
+ case 47 Q_WRITE_ANDX q47;
+ case 50 Q_TRANS2 q50;
+ case 113 Q_TDIS q113;
+ case 114 Q_NEGPROT q114;
+ case 115 Q_SESSION_SETUP_ANDX q115;
+ case 116 Q_ULOGOFF_ANDX q116;
+ case 117 Q_TCON_ANDX q117;
+ case 128 Q_DSKATTR q128;
+ case 160 Q_NTTRANS q160;
+ case 162 Q_NTCREATE_ANDX q162;
+ }
+} Q_SMB;
+
diff --git a/source/aparser/dump.awk b/source/aparser/dump.awk
new file mode 100644
index 00000000000..11bfb107e46
--- /dev/null
+++ b/source/aparser/dump.awk
@@ -0,0 +1,70 @@
+# dump the current parse tree
+
+
+function element_string(elnum,
+ LOCAL, elem)
+{
+ elem = elements[elnum, "elem"];
+ if (elements[elnum, "ptr"]=="1") elem="*"elem;
+ if (elements[elnum, "array_len"]!="")
+ elem=elem"["elements[elnum, "array_len"]"]";
+ if (elements[elnum, "switch"]!="")
+ elem=elem"["elements[elnum, "switch"]"]";
+ return elem;
+}
+
+function dump_element(f, elnum,
+ LOCAL, elem, type)
+{
+ type = elements[elnum, "type"];
+ case = elements[elnum, "case"];
+ elem = element_string(elnum);
+ if (case != "") {
+ xprintf(f,"\t\tcase %d %s %s;\n", case, type, elem);
+ } else {
+ xprintf(f,"\t%s %s;\n", type, elem);
+ }
+}
+
+function dump_union(f, elnum,
+ LOCAL, i)
+{
+ xprintf(f,"\tunion %s {\n", element_string(elnum));
+ for (i=0;i<unions[elnum, "num_elems"];i++) {
+ dump_element(f, unions[elnum, i]);
+ }
+ xprintf(f,"\t}\n");
+}
+
+function dump_elem(f, struct_num, elem_num,
+ LOCAL, enum)
+{
+ elnum = structs[struct_num, elem_num];
+
+ if (elements[elnum, "type"] == "union") {
+ dump_union(f, elnum);
+ } else {
+ dump_element(f, elnum);
+ }
+}
+
+function dump_structs(f, NIL,
+ LOCAL, i, j)
+{
+ xprintf(f,"/* dump of parsed structures */\n\n\n");
+
+ for (i=0;i < num_options;i++) {
+ xprintf(f,"option %s %s\n", options[i, "name"], options[i, "value"]);
+ }
+ xprintf(f,"\n\n");
+
+ for (i=0;i < num_structs;i++) {
+ xprintf(f,"/* structure %d */\n", i);
+ xprintf(f,"struct %s {\n", structs[i, "name"]);
+ for (j=0;j<structs[i, "num_elems"];j++) {
+ dump_elem(f, i, j);
+ }
+ xprintf(f,"};\n\n");
+ }
+ xprintf(f,"/* end dump */\n\n");
+}
diff --git a/source/aparser/harness.awk b/source/aparser/harness.awk
new file mode 100644
index 00000000000..6c4c35c40f2
--- /dev/null
+++ b/source/aparser/harness.awk
@@ -0,0 +1,17 @@
+function produce_harness(f,
+ LOCAL, v, struct_num, i)
+{
+ struct_num=structs[test];
+
+ v["MODULE"]=module;
+
+ print_template(f, "harness_start.tpl", v);
+
+ for (i=0;i<num_structs;i++) {
+ v["TEST"] = structs[i, "name"];
+ print_template(f, "harness.tpl", v);
+ }
+
+ print_template(f, "harness_end.tpl", v);
+}
+
diff --git a/source/aparser/header.awk b/source/aparser/header.awk
new file mode 100644
index 00000000000..ba7117436b8
--- /dev/null
+++ b/source/aparser/header.awk
@@ -0,0 +1,80 @@
+# produce a header file for a parsed struct file
+
+function header_elstring(elnum,
+ LOCAL, elem)
+{
+ array_len = elements[elnum, "array_len"];
+ elem=elements[elnum, "elem"];
+ if (elements[elnum, "ptr"]=="1") elem="*"elem;
+ if (array_len!="") {
+ if (is_constant(array_len) == 1) {
+ elem=elem"["array_len"]";
+ } else {
+ elem="*"elem;
+ }
+ }
+ return elem;
+}
+
+function header_element(f, elnum,
+ LOCAL, type)
+{
+ type=elements[elnum, "type"];
+ if (substr(type,1,1) == ".") return;
+ xprintf(f,"\t%s %s;\n", type, header_elstring(elnum));
+}
+
+function header_union(f, elnum,
+ LOCAL, i)
+{
+ xprintf(f,"\tunion {\n");
+ for (i=0;i<unions[elnum, "num_elems"];i++) {
+ header_element(f, unions[elnum, i]);
+ }
+ xprintf(f,"\t} %s;\n", header_elstring(elnum));
+}
+
+function header_elem(f, elnum)
+{
+
+ if (elements[elnum, "type"] == "union") {
+ header_union(f, elnum);
+ } else {
+ header_element(f, elnum);
+ }
+}
+
+function header_struct(f, struct_num,
+ LOCAL, i)
+{
+ xprintf(f,"/* structure %s */\n",
+ structs[struct_num, "name"]);
+ xprintf(f,"typedef struct {\n");
+ for (i=0;i < structs[struct_num, "num_elems"];i++) {
+ header_elem(f, structs[struct_num, i]);
+ }
+ xprintf(f,"} %s;\n\n\n", structs[struct_num, "name"]);
+}
+
+
+function produce_headers(f, NIL,
+ LOCAL, i)
+{
+ xprintf(f,"/* auto-generated headers for %s */\n\n\n", module);
+ xprintf(f,"#ifndef _%s_\n", module);
+ xprintf(f,"#define _%s_\n", module);
+
+ xprintf(f,"\n\n");
+ for (i=0;i < num_options;i++) {
+ xprintf(f,"#define OPTION_%s %s\n",
+ options[i, "name"], options[i, "value"]);
+ }
+ xprintf(f,"\n\n");
+
+ for (i=0;i < num_structs;i++) {
+ header_struct(f, i);
+ }
+ xprintf(f,"/* end auto-generated headers */\n\n");
+ xprintf(f,"#endif /* _%s_ */\n", module);
+}
+
diff --git a/source/aparser/main.awk b/source/aparser/main.awk
new file mode 100644
index 00000000000..4969f2217a2
--- /dev/null
+++ b/source/aparser/main.awk
@@ -0,0 +1,25 @@
+# the main program
+
+@include dump.awk
+@include header.awk
+@include util.awk
+@include template.awk
+#@include parsefn.awk
+@include parserel.awk
+@include harness.awk
+@include parsetree.awk
+@include token.awk
+
+END {
+ dump_structs("dump.out");
+ printf("Producing headers...\n");
+ produce_headers("prs_"module".h");
+# printf("Producing parsers...\n");
+# produce_parsers("prs_"module".c", "mod_"module".c");
+ printf("Producing relative parsers...\n");
+ produce_relative("prs_"module".c");
+ printf("Producing harness...\n");
+ produce_harness("test.h");
+ printf("Done.\n");
+ exit 0;
+}
diff --git a/source/aparser/parsefn.awk b/source/aparser/parsefn.awk
new file mode 100644
index 00000000000..2bebd765e66
--- /dev/null
+++ b/source/aparser/parsefn.awk
@@ -0,0 +1,271 @@
+# build parse functions for a parsed struct file
+
+function elem_name(v, elem)
+{
+ return v["UNION"]elem;
+}
+
+function parse_array(f, v, elnum, flags,
+ LOCAL, type, elem, array_len)
+{
+ type = elements[elnum, "type"];
+ elem = elements[elnum, "elem"];
+ array_len = elements[elnum, "array_len"];
+ v["ELEM"] = elem_name(v, elem);
+ v["TYPE"] = type;
+ v["FLAGS"] = flags;
+ v["ARRAY_LEN"] = array_len;
+
+ if (array_len=="+") {
+ print_template(f,"prs_array_optional.tpl", v);
+ return;
+ }
+
+ if (array_len=="*") {
+ print_template(f,"prs_array_remainder.tpl", v);
+ return;
+ }
+
+ if (type == "wchar" || type == "uint16") {
+ if (match(array_len,"[0-9]") == 1) {
+ print_template(f, "prs_wstring_fixed.tpl", v);
+ } else {
+ print_template(f, "prs_wstring.tpl", v);
+ }
+ } else if (type == "uint8") {
+ if (match(array_len,"[0-9]") == 1) {
+ print_template(f, "prs_uint8s_fixed.tpl", v);
+ } else {
+ print_template(f, "prs_uint8s.tpl", v);
+ }
+ } else {
+ print_template(f, "prs_array.tpl", v);
+ }
+}
+
+
+function parse_element(f, v, elnum, flags,
+ LOCAL, type, elem)
+{
+ if (elements[elnum,"nowire"] != "") {
+ return;
+ }
+ type = elements[elnum, "type"];
+ if (substr(type,1,1) == ".") return;
+ elem = elements[elnum, "elem"];
+ if (elements[elnum,"ptr"] == "") {
+ v["PTR"] = "\\&";
+ } else {
+ v["PTR"] = " ";
+ }
+ v["ELEM"] = elem_name(v, elem);
+ v["TYPE"] = type;
+ v["FLAGS"] = flags;
+ print_template(f, "prs_element.tpl", v);
+}
+
+function parse_union(f, v, elnum, flags,
+ LOCAL, i)
+{
+ v["UNION"] = elements[elnum, "elem"];
+ v["SWITCH"] = elements[elnum, "switch"];
+
+ if (elements[elnum, "ptr"] == "1") {
+ v["UNION"] = v["UNION"]"->";
+ } else {
+ v["UNION"] = v["UNION"]".";
+ }
+
+ print_template(f, "union_start.tpl", v);
+ for (i=0;i<unions[elnum, "num_elems"];i++) {
+ v["CASE"] = elements[unions[elnum, i], "case"];
+ print_template(f, "prs_case.tpl", v);
+ if (elements[elnum, "ptr"] == "1") {
+ parse_scalars(f, v, unions[elnum, i], "PARSE_SCALARS");
+ parse_buffers(f, v, unions[elnum, i], "PARSE_BUFFERS");
+ } else {
+ if (flags == "PARSE_SCALARS") {
+ parse_scalars(f, v, unions[elnum, i], flags);
+ } else {
+ parse_buffers(f, v, unions[elnum, i], flags);
+ }
+ }
+ print_template(f, "prs_break.tpl", v);
+ }
+ v["UNION"] = "";
+
+ print_template(f, "union_end.tpl", v);
+}
+
+function parse_scalar(f, v, elnum, flags)
+{
+ if (elements[elnum, "type"] == "union") {
+ parse_union(f, v, elnum, flags);
+ } else if (elements[elnum, "array_len"]!="") {
+ parse_array(f, v, elnum, flags);
+ } else {
+ parse_element(f, v, elnum, flags);
+ }
+}
+
+function parse_align2(f, v, elnum, flags,
+ LOCAL, elem)
+{
+ elem = elements[elnum, "elem"];
+ v["OFFSET"] = elem_name(v, elem);
+ print_template(f, "prs_align2.tpl", v);
+}
+
+function parse_align4(f, v, elnum, flags,
+ LOCAL, elem)
+{
+ elem = elements[elnum, "elem"];
+ v["OFFSET"] = elem_name(v, elem);
+ print_template(f, "prs_align4.tpl", v);
+}
+
+function parse_pointer(f, v, elnum, flags,
+ LOCAL, elem)
+{
+ elem = elements[elnum, "elem"];
+ v["ELEM"] = elem_name(v, elem);
+ v["FLAGS"] = flags;
+ print_template(f, "prs_pointer.tpl", v);
+}
+
+function parse_scalar_fn(m, v, elnum,
+ LOCAL, elem, type)
+{
+ elem = elements[elnum, "elem"];
+ type = elements[elnum, "type"]
+ xprintf(m, "%s %s", type, elem_name(v, elem));
+ if (type == "union") {
+ } else if (elements[elnum, "array_len"]!="") {
+ } else {
+ }
+}
+
+function parse_pointer_fn(f, v, elnum, flags,
+ LOCAL, elem)
+{
+ elem = elements[elnum, "elem"];
+ v["ELEM"] = elem_name(v, elem);
+ v["FLAGS"] = flags;
+ xprintf(m, "%s\n", v["ELEM"]);
+}
+
+function parse_scalars_fn(m, v, elnum, flags)
+{
+ if (elements[elnum, "type"] == ".align2") {
+ }
+ else if (elements[elnum, "type"] == ".align4") {
+ }
+ else if (elements[elnum, "ptr"] == "1") {
+ parse_pointer_fn(m, v, elnum, flags);
+ } else {
+ parse_scalar_fn(m, v, elnum, flags);
+ }
+}
+
+function parse_scalars(f, v, elnum, flags)
+{
+ if (elements[elnum, "type"] == ".align2") {
+ parse_align2(f, v, elnum, flags);
+ }
+ else if (elements[elnum, "type"] == ".align4") {
+ parse_align4(f, v, elnum, flags);
+ }
+ else if (elements[elnum, "ptr"] == "1") {
+ parse_pointer(f, v, elnum, flags);
+ } else {
+ parse_scalar(f, v, elnum, flags);
+ }
+}
+
+function parse_buffers(f, v, elnum, flags,
+ LOCAL, elem, type)
+{
+ elem = elements[elnum, "elem"];
+ type = elements[elnum, "type"];
+ v["ELEM"] = elem_name(v, elem);
+ if (elements[elnum, "type"] == ".align2") {
+ }
+ else if (elements[elnum, "type"] == ".align4") {
+ } else if (elements[elnum, "ptr"] == "1") {
+ print_template(f, "ifptr_start.tpl", v);
+ parse_scalar(f, v, elnum, "PARSE_SCALARS|PARSE_BUFFERS");
+ print_template(f, "ifptr_end.tpl", v);
+ } else {
+ parse_scalar(f, v, elnum, flags);
+ }
+}
+
+function struct_parser(f, m, v, struct_num,
+ LOCAL, i, n1, f1, num_elems)
+{
+ f1 = -1;
+ num_elems = structs[struct_num, "num_elems"];
+ v["STRUCTNAME"] = structs[struct_num, "name"];
+ v["FUNCNAME"] = "io_" v["STRUCTNAME"];
+ print_template(f, "fn_start.tpl", v);
+
+ for (n1=0;n1<num_elems;n1++) {
+ if (elements[structs[struct_num, n1], "type"] == ".trailer") {
+ f1 = n1;
+ break;
+ }
+ }
+
+ # first all the structure pointers, scalars and arrays
+ for (i=0;i<n1;i++) {
+ parse_scalars(f, v, structs[struct_num, i], "PARSE_SCALARS");
+ }
+
+ print_template(f, "fn_mid.tpl", v);
+
+ # now the buffers
+ for (i=0;i<n1;i++) {
+ parse_buffers(f, v, structs[struct_num, i], "PARSE_BUFFERS");
+ }
+
+ # and any trailers
+ for (i=n1;i<num_elems;i++) {
+ parse_scalars(f, v, structs[struct_num, i], "PARSE_SCALARS");
+ parse_buffers(f, v, structs[struct_num, i], "PARSE_BUFFERS");
+ }
+
+ if (i > 0) {
+ print_template(f, "fn_end.tpl", v);
+ }
+ else {
+ print_template(f, "fn_end0.tpl", v);
+ }
+
+ if (f1 == -1)
+ return;
+
+ xprintf(m, "void fn_%s(\n", v["STRUCTNAME"]);
+
+ for (i=f1+1;i<num_elems;i++) {
+ parse_scalars_fn(m, v, structs[struct_num, i]);
+ if (i != num_elems-1)
+ xprintf(m, ", \n");
+
+ }
+
+ xprintf(m, ")\n{\n}\n");
+}
+
+function produce_parsers(f, m,
+ LOCAL, v, i)
+{
+ v["MODULE"]=module;
+
+ print_template(f, "module_start.tpl", v);
+
+ for (i=0;i < num_structs;i++) {
+ struct_parser(f, m, v, i);
+ }
+
+ print_template(f, "module_end.tpl", v);
+}
diff --git a/source/aparser/parser.c b/source/aparser/parser.c
new file mode 100644
index 00000000000..0c7153e1fb7
--- /dev/null
+++ b/source/aparser/parser.c
@@ -0,0 +1,471 @@
+#include "parser.h"
+
+/*******************************************************************
+ Attempt, if needed, to grow a data buffer.
+ Also depends on the data stream mode (io).
+ ********************************************************************/
+
+BOOL io_grow(io_struct *ps, uint32 extra_space)
+{
+ uint32 new_size;
+ char *new_data;
+
+ ps->grow_size = MAX(ps->grow_size, ps->data_offset + extra_space);
+
+ if(ps->data_offset + extra_space <= ps->buffer_size)
+ return True;
+
+ /*
+ * We cannot grow the buffer if we're not reading
+ * into the io_struct, or if we don't own the memory.
+ */
+
+ if(UNMARSHALLING(ps) || !ps->is_dynamic) {
+ DEBUG(0,("io_grow: Buffer overflow - unable to expand buffer by %u bytes.\n",
+ (unsigned int)extra_space));
+ return False;
+ }
+
+ /*
+ * Decide how much extra space we really need.
+ */
+
+ extra_space -= (ps->buffer_size - ps->data_offset);
+ if(ps->buffer_size == 0) {
+ new_size = extra_space;
+
+ if((new_data = malloc(new_size)) == NULL) {
+ DEBUG(0,("io_grow: Malloc failure for size %u.\n", (unsigned int)new_size));
+ return False;
+ }
+ memset(new_data, '\0', new_size );
+ } else {
+ /*
+ * If the current buffer size is bigger than the space needed, just
+ * double it, else add extra_space.
+ */
+ new_size = MAX(ps->buffer_size*2, ps->buffer_size + extra_space);
+
+ if ((new_data = Realloc(ps->data_p, new_size)) == NULL) {
+ DEBUG(0,("io_grow: Realloc failure for size %u.\n",
+ (unsigned int)new_size));
+ return False;
+ }
+ }
+ ps->buffer_size = new_size;
+ ps->data_p = new_data;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Ensure we can read/write to a given offset.
+ ********************************************************************/
+
+char *io_mem_get(io_struct *ps, uint32 extra_size)
+{
+ if(UNMARSHALLING(ps)) {
+ /*
+ * If reading, ensure that we can read the requested size item.
+ */
+ if (ps->data_offset + extra_size > ps->buffer_size) {
+ DEBUG(0,("io_mem_get: reading data of size %u would overrun buffer.\n",
+ (unsigned int)extra_size ));
+ return NULL;
+ }
+ } else {
+ /*
+ * Writing - grow the buffer if needed.
+ */
+ if(!io_grow(ps, extra_size))
+ return False;
+ }
+ return &ps->data_p[ps->data_offset];
+}
+
+/*******************************************************************
+ Initialise a parse structure - malloc the data if requested.
+ ********************************************************************/
+
+BOOL io_init(io_struct *ps, uint32 size, BOOL io)
+{
+ ZERO_STRUCTP(ps);
+ ps->io = io;
+ ps->bigendian_data = False;
+ ps->is_dynamic = False;
+ ps->data_offset = 0;
+ ps->buffer_size = 0;
+ ps->data_p = NULL;
+
+ if (size != 0) {
+ ps->buffer_size = size;
+ if((ps->data_p = (char *)malloc((size_t)size)) == NULL) {
+ DEBUG(0,("io_init: malloc fail for %u bytes.\n", (unsigned int)size));
+ return False;
+ }
+ ps->is_dynamic = True; /* We own this memory. */
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ debug output for parsing info.
+
+ XXXX side-effect of this function is to increase the debug depth XXXX
+
+ ********************************************************************/
+void io_debug(io_struct *ps, int depth, char *desc, char *fn_name)
+{
+ DEBUG(5+depth, ("%s%06x %s %s\n", tab_depth(depth), ps->data_offset, fn_name, desc));
+}
+
+/*******************************************************************
+ Align a the data_len to a multiple of align bytes - filling with
+ zeros.
+ ********************************************************************/
+
+BOOL io_align2(io_struct *ps, int offset)
+{
+ uint32 mod = (ps->data_offset + offset) & (2-1);
+
+ if (mod != 0) {
+ uint32 extra_space = (2 - mod);
+ if(!io_grow(ps, extra_space))
+ return False;
+ memset(&ps->data_p[ps->data_offset], '\0', (size_t)extra_space);
+ ps->data_offset += extra_space;
+ }
+
+ return True;
+}
+
+BOOL io_align4(io_struct *ps, int offset)
+{
+ uint32 mod = (ps->data_offset + offset) & (4-1);
+
+ if (mod != 0) {
+ uint32 extra_space = (4 - mod);
+ if(!io_grow(ps, extra_space))
+ return False;
+ memset(&ps->data_p[ps->data_offset], '\0', (size_t)extra_space);
+ ps->data_offset += extra_space;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Align a the data_len to a multiple of align bytes - filling with
+ zeros.
+ ********************************************************************/
+
+BOOL io_align(io_struct *ps, int align)
+{
+ uint32 mod;
+
+ if (!ps->autoalign) return True;
+
+ mod = ps->data_offset & (align-1);
+
+ if (align != 0 && mod != 0) {
+ uint32 extra_space = (align - mod);
+ if(!io_grow(ps, extra_space))
+ return False;
+ memset(&ps->data_p[ps->data_offset], '\0', (size_t)extra_space);
+ ps->data_offset += extra_space;
+ }
+
+ return True;
+}
+
+
+/*******************************************************************
+ read from a socket into memory.
+ ********************************************************************/
+BOOL io_read(io_struct *ps, int fd, size_t len, int timeout)
+{
+ BOOL ok;
+ size_t prev_size = ps->buffer_size;
+ if (!io_grow(ps, len))
+ {
+ return False;
+ }
+
+ if (timeout > 0)
+ {
+ ok = (read(fd, &ps->data_p[prev_size], len) == len);
+ }
+ else
+ {
+ ok = (read(fd, &ps->data_p[prev_size], len) == len);
+ }
+ return ok;
+}
+
+
+/*******************************************************************
+ do IO on a uint32.
+ ********************************************************************/
+BOOL io_uint32(char *name, io_struct *ps, int depth, uint32 *data32, unsigned flags)
+{
+ char *q;
+
+ if (!(flags & PARSE_SCALARS)) return True;
+
+ if (!io_align(ps, 4)) return False;
+
+ q = io_mem_get(ps, sizeof(uint32));
+ if (q == NULL) return False;
+
+ DBG_RW_IVAL(name, depth, ps->data_offset, ps->io, ps->bigendian_data, q, *data32)
+ ps->data_offset += sizeof(uint32);
+
+ return True;
+}
+
+/*******************************************************************
+ do IO on a uint16.
+ ********************************************************************/
+BOOL io_uint16(char *name, io_struct *ps, int depth, uint16 *data16, unsigned flags)
+{
+ char *q;
+
+ if (!(flags & PARSE_SCALARS)) return True;
+
+ if (!io_align(ps, 2)) return False;
+
+ q = io_mem_get(ps, sizeof(uint16));
+ if (q == NULL) return False;
+
+ DBG_RW_SVAL(name, depth, ps->data_offset, ps->io, ps->bigendian_data, q, *data16)
+ ps->data_offset += sizeof(uint16);
+
+ return True;
+}
+
+/*******************************************************************
+ do IO on a uint8.
+ ********************************************************************/
+BOOL io_uint8(char *name, io_struct *ps, int depth, uint8 *data8, unsigned flags)
+{
+ char *q;
+
+ if (!(flags & PARSE_SCALARS)) return True;
+
+ q = io_mem_get(ps, sizeof(uint8));
+ if (q == NULL) return False;
+
+ DBG_RW_IVAL(name, depth, ps->data_offset, ps->io, ps->bigendian_data, q, *data8)
+ ps->data_offset += sizeof(uint8);
+
+ return True;
+}
+
+/*******************************************************************
+ do IO on a pointer
+ ********************************************************************/
+BOOL io_pointer(char *desc, io_struct *ps, int depth, void **p, unsigned flags)
+{
+ uint32 v;
+
+ if (!(flags & PARSE_SCALARS)) return True;
+
+ v = (*p) ? 0xdeadbeef : 0;
+ if (!io_uint32(desc, ps, depth, &v, flags)) return False;
+ *p = (void *) (v ? 0xdeadbeef : 0);
+ return True;
+}
+
+/*******************************************************************
+ Stream a null-terminated string.
+ ********************************************************************/
+BOOL io_SMBSTR(char *name, io_struct *ps, int depth, char **str, unsigned flags)
+{
+ char *q;
+ uint8 *start;
+ int i;
+ size_t len;
+ int start_offset = ps->data_offset;
+
+ if (!(flags & PARSE_SCALARS)) return True;
+
+ if (UNMARSHALLING(ps)) {
+ *str = io_mem_get(ps, 0);
+ if (*str == NULL)
+ return False;
+ len = strlen(*str);
+ ps->data_offset += len + 1;
+ }
+ else
+ {
+ len = strlen(*str)+1;
+ start = (uint8*)q;
+
+ for(i = 0; i < len; i++) {
+ q = io_mem_get(ps, 1);
+ if (q == NULL)
+ return False;
+
+ RW_CVAL(ps->io, q, (*str)[i],0);
+ ps->data_offset++;
+ }
+ }
+
+ DEBUG(5,("%s%04x %s: %s\n", tab_depth(depth),
+ start_offset, name, *str));
+ return True;
+}
+
+/******************************************************************
+ do IO on a byte array
+ ********************************************************************/
+BOOL io_uint8s(char *name, io_struct *ps, int depth, uint8 **data8s, int len, unsigned flags)
+{
+ char *q;
+ size_t num_bytes = len * sizeof(uint8);
+
+ if (!(flags & PARSE_SCALARS)) return True;
+
+ q = io_mem_get(ps, num_bytes);
+ if (q == NULL) return False;
+
+ if (MARSHALLING(ps))
+ {
+ DBG_RW_PCVAL(True, name, depth, ps->data_offset, ps->io, q, *data8s, len)
+ }
+ else
+ {
+ *data8s = q;
+ dump_data(depth+5, *data8s, num_bytes);
+ }
+ ps->data_offset += num_bytes;
+
+ return True;
+}
+/******************************************************************
+ do IO on a fixed-size byte array
+ ********************************************************************/
+BOOL io_uint8s_fixed(char *name, io_struct *ps, int depth, uint8 *data8s, int len, unsigned flags)
+{
+ char *q;
+ size_t num_bytes = len * sizeof(uint8);
+
+ if (!(flags & PARSE_SCALARS)) return True;
+
+ q = io_mem_get(ps, num_bytes);
+ if (q == NULL) return False;
+
+ DBG_RW_PCVAL(True, name, depth, ps->data_offset, ps->io, q, data8s, len)
+ ps->data_offset += num_bytes;
+
+ return True;
+}
+
+
+/******************************************************************
+ do IO on an io (eh?? :)
+ ********************************************************************/
+BOOL io_io_struct(char *name, io_struct *ps, int depth, io_struct *io, unsigned flags)
+{
+ char *q;
+ uint16 len;
+
+ if (!(flags & PARSE_SCALARS)) return True;
+
+ q = io_mem_get(ps, sizeof(uint16));
+ if (q == NULL) return False;
+
+ /* length first */
+ if (MARSHALLING(ps))
+ {
+ len = io->data_offset;
+ }
+ if (!io_uint16("len", ps, depth+1, &len, flags))
+ {
+ return False;
+ }
+ if (UNMARSHALLING(ps))
+ {
+ if (!io_init(io, len, UNMARSHALL))
+ {
+ return False;
+ }
+ }
+
+ /* now data */
+ q = io_mem_get(ps, len * sizeof(uint8));
+ if (q == NULL) return False;
+
+ if (MARSHALLING(ps))
+ {
+ DBG_RW_PCVAL(False, name, depth+1, ps->data_offset, ps->io, q, io->data_p, len)
+ }
+ else
+ {
+ io->data_p = q;
+ dump_data(depth+5, q, len);
+ }
+ ps->data_offset += len;
+
+ return True;
+}
+
+/******************************************************************
+ do IO on a unicode array
+ ********************************************************************/
+BOOL io_wstring(char *name, io_struct *ps, int depth, uint16 *data16s, int len, unsigned flags)
+{
+ char *q;
+
+ if (!(flags & PARSE_SCALARS)) return True;
+
+ if (!io_align(ps, 2)) return False;
+
+ q = io_mem_get(ps, len * sizeof(uint16));
+ if (q == NULL) return False;
+
+ DBG_RW_PSVAL(True, name, depth, ps->data_offset, ps->io, ps->bigendian_data, q, data16s, len)
+ ps->data_offset += (len * sizeof(uint16));
+
+ return True;
+}
+
+
+/******************************************************************
+allocate some memory for a parse structure
+ ********************************************************************/
+void io_free(io_struct *ps)
+{
+ if (ps->is_dynamic && ps->data_p)
+ {
+ free(ps->data_p);
+ ps->data_p = NULL;
+ }
+}
+
+/******************************************************************
+allocate some memory for a parse structure
+ ********************************************************************/
+BOOL io_alloc(char *name, io_struct *ps, void **ptr, unsigned size)
+{
+ (*ptr) = (void *)malloc(size);
+ if (*ptr) return True;
+ return False;
+}
+
+/******************************************************************
+realloc some memory for a parse structure
+ ********************************************************************/
+BOOL io_realloc(char *name, io_struct *ps, void **ptr, unsigned size)
+{
+ BOOL ret = True;
+ void *tp;
+
+ tp = (void *)Realloc(*ptr, size);
+ if (tp) *ptr = tp;
+ else ret = False;
+ return ret;
+}
+
diff --git a/source/aparser/parser.h b/source/aparser/parser.h
new file mode 100644
index 00000000000..319aeb5d138
--- /dev/null
+++ b/source/aparser/parser.h
@@ -0,0 +1,103 @@
+#include <ctype.h>
+#include <stdio.h>
+#include <malloc.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "../include/byteorder.h"
+
+#define PARSE_SCALARS (1<<0)
+#define PARSE_BUFFERS (1<<1)
+
+#ifndef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+#ifndef MAX
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#endif
+
+#define DEBUG(lvl, str) printf str;
+#define DEBUGADD(lvl, str) printf str;
+
+#define MARSHALL 0
+#define UNMARSHALL 1
+
+#define MARSHALLING(ps) (!(ps)->io)
+#define UNMARSHALLING(ps) ((ps)->io)
+
+typedef int BOOL;
+typedef unsigned char uint8;
+typedef unsigned char uchar;
+typedef unsigned short uint16;
+typedef unsigned short wchar;
+typedef unsigned uint32;
+typedef char *SMBSTR;
+
+/* a null terminated unicode string */
+typedef uint16 ZUSTRING;
+
+#ifndef _PSTRING
+
+#define PSTRING_LEN 1024
+#define FSTRING_LEN 128
+
+typedef char pstring[PSTRING_LEN];
+typedef char fstring[FSTRING_LEN];
+
+#define _PSTRING
+
+#endif
+#define False 0
+#define True 1
+
+/* zero a structure given a pointer to the structure */
+#define ZERO_STRUCTP(x) do { if ((x) != NULL) memset((char *)(x), 0, sizeof(*(x))); } while(0)
+
+#define MAX_UNISTRLEN 256
+#define MAX_STRINGLEN 256
+#define MAX_BUFFERLEN 512
+
+typedef struct _io_struct
+{
+ BOOL io; /* parsing in or out of data stream */
+ /*
+ * If the (incoming) data is big-endian. On output we are
+ * always little-endian.
+ */
+ BOOL bigendian_data;
+ BOOL is_dynamic; /* Do we own this memory or not ? */
+ BOOL autoalign; /* should we auto-align all elements? */
+ uint32 data_offset; /* Current working offset into data. */
+ uint32 buffer_size; /* Current size of the buffer. */
+ uint32 grow_size; /* size requested via io_grow() calls */
+ char *data_p; /* The buffer itself. */
+} io_struct;
+
+
+char *io_mem_get(io_struct *ps, uint32 extra_size);
+BOOL io_init(io_struct *ps, uint32 size, BOOL io);
+void io_debug(io_struct *ps, int depth, char *desc, char *fn_name);
+BOOL io_align(io_struct *ps, int align);
+BOOL io_align4(io_struct *ps, int align);
+BOOL io_align2(io_struct *ps, int align);
+BOOL io_read(io_struct *ps, int fd, size_t len, int timeout);
+void dump_data(int level,char *buf1,int len);
+BOOL io_alloc(char *name, io_struct *ps, void **ptr, unsigned size);
+BOOL io_uint32(char *name, io_struct *ps, int depth, uint32 *data32, unsigned flags);
+BOOL io_uint16(char *name, io_struct *ps, int depth, uint16 *data16, unsigned flags);
+BOOL io_uint8(char *name, io_struct *ps, int depth, uint8 *data8, unsigned flags);
+BOOL io_pointer(char *desc, io_struct *ps, int depth, void **p, unsigned flags);
+BOOL io_SMBSTR(char *name, io_struct *ps, int depth, char **str, unsigned flags);
+BOOL io_io_struct(char *name, io_struct *ps, int depth, io_struct *io, unsigned flags);
+BOOL io_wstring(char *name, io_struct *ps, int depth, uint16 *data16s, int len, unsigned flags);
+BOOL io_uint8s_fixed(char *name, io_struct *ps, int depth, uint8 *data8s, int len, unsigned flags);
+BOOL io_uint8s(char *name, io_struct *ps, int depth, uint8 **data8s, int len, unsigned flags);
+
+char *tab_depth(int depth);
+void *Realloc(void *p,size_t size);
+void dump_data(int level,char *buf1,int len);
+void print_asc(int level, uchar const *buf, int len);
+BOOL io_ZUSTRING(char *name, io_struct *ps, int depth, uint16 **ustr, unsigned flags);
+size_t strlen_w(void *src);
+
diff --git a/source/aparser/parserel.awk b/source/aparser/parserel.awk
new file mode 100644
index 00000000000..6d80f0607e4
--- /dev/null
+++ b/source/aparser/parserel.awk
@@ -0,0 +1,213 @@
+# build parse functions for a parsed struct file
+
+function elem_name(v, elem)
+{
+ return v["UNION"]elem;
+}
+
+function parse_array(f, v, elnum, flags,
+ LOCAL, type, elem, array_len)
+{
+ type = elements[elnum, "type"];
+ elem = elements[elnum, "elem"];
+ array_len = elements[elnum, "array_len"];
+ v["ELEM"] = elem_name(v, elem);
+ v["TYPE"] = type;
+ v["FLAGS"] = flags;
+ v["ARRAY_LEN"] = array_len;
+
+ if (array_len=="+") {
+ print_template(f,"prs_array_optional.tpl", v);
+ return;
+ }
+
+ if (array_len=="&") {
+ print_template(f,"prs_array_null.tpl", v);
+ return;
+ }
+
+ if (array_len=="*") {
+ print_template(f,"prs_array_remainder.tpl", v);
+ return;
+ }
+
+ if (type == "wchar" || type == "uint16") {
+ if (match(array_len,"[0-9]") == 1) {
+ print_template(f, "prs_wstring_fixed.tpl", v);
+ } else {
+ print_template(f, "prs_wstring.tpl", v);
+ }
+ } else if (type == "uint8") {
+ if (match(array_len,"[0-9]") == 1) {
+ print_template(f, "prs_uint8s_fixed.tpl", v);
+ } else {
+ print_template(f, "prs_uint8s.tpl", v);
+ }
+ } else {
+ print_template(f, "prs_array.tpl", v);
+ }
+}
+
+
+function parse_element(f, v, elnum, flags,
+ LOCAL, type, elem)
+{
+ if (elements[elnum,"nowire"] != "") {
+ return;
+ }
+ type = elements[elnum, "type"];
+ if (substr(type,1,1) == ".") return;
+ elem = elements[elnum, "elem"];
+ if (elements[elnum,"ptr"] == "") {
+ v["PTR"] = "\\&";
+ } else {
+ v["PTR"] = " ";
+ }
+ v["ELEM"] = elem_name(v, elem);
+ v["TYPE"] = type;
+ v["FLAGS"] = flags;
+ print_template(f, "prs_element.tpl", v);
+}
+
+function parse_union(f, v, elnum, flags,
+ LOCAL, i)
+{
+ v["UNION"] = elements[elnum, "elem"];
+ v["SWITCH"] = elements[elnum, "switch"];
+
+ if (elements[elnum, "ptr"] == "1") {
+ v["UNION"] = v["UNION"]"->";
+ } else {
+ v["UNION"] = v["UNION"]".";
+ }
+
+ print_template(f, "union_start.tpl", v);
+ for (i=0;i<unions[elnum, "num_elems"];i++) {
+ v["CASE"] = elements[unions[elnum, i], "case"];
+ print_template(f, "prs_case.tpl", v);
+ if (elements[elnum, "ptr"] == "1") {
+ parse_scalars(f, v, unions[elnum, i], "PARSE_SCALARS");
+ parse_buffers(f, v, unions[elnum, i], "PARSE_BUFFERS");
+ } else {
+ if (flags == "PARSE_SCALARS") {
+ parse_scalars(f, v, unions[elnum, i], flags);
+ } else {
+ parse_buffers(f, v, unions[elnum, i], flags);
+ }
+ }
+ print_template(f, "prs_break.tpl", v);
+ }
+ v["UNION"] = "";
+
+ print_template(f, "union_end.tpl", v);
+}
+
+function parse_scalar(f, v, elnum, flags)
+{
+ if (elements[elnum, "type"] == "union") {
+ parse_union(f, v, elnum, flags);
+ } else if (elements[elnum, "array_len"]!="") {
+ parse_array(f, v, elnum, flags);
+ } else {
+ parse_element(f, v, elnum, flags);
+ }
+}
+
+function parse_pointer(f, v, elnum, flags,
+ LOCAL, elem)
+{
+ elem = elements[elnum, "elem"];
+ v["ELEM"] = elem_name(v, elem);
+ v["FLAGS"] = flags;
+ print_template(f, "prs_pointer.tpl", v);
+}
+
+function parse_scalars(f, v, elnum, flags)
+{
+ if (elements[elnum, "ptr"] == "1") {
+ parse_pointer(f, v, elnum, flags);
+ } else {
+ parse_scalar(f, v, elnum, flags);
+ }
+}
+
+function parse_buffers(f, v, elnum, flags,
+ LOCAL, elem, type)
+{
+ elem = elements[elnum, "elem"];
+ type = elements[elnum, "type"];
+ v["ELEM"] = elem_name(v, elem);
+ if (elements[elnum, "ptr"] == "1") {
+ print_template(f, "ifptr_start.tpl", v);
+ parse_scalar(f, v, elnum, "PARSE_SCALARS|PARSE_BUFFERS");
+ print_template(f, "ifptr_end.tpl", v);
+ } else {
+ parse_scalar(f, v, elnum, flags);
+ }
+}
+
+function struct_immediate(f, v, struct_num,
+ LOCAL, i, n1, num_elems)
+{
+ num_elems = structs[struct_num, "num_elems"];
+ v["STRUCTNAME"] = structs[struct_num, "name"];
+ v["FUNCNAME"] = "io_" v["STRUCTNAME"];
+
+ print_template(f, "fn_i_start.tpl", v);
+
+ for (i=0;i<num_elems;i++) {
+ parse_scalars(f, v, structs[struct_num, i], "PARSE_SCALARS");
+ parse_buffers(f, v, structs[struct_num, i], "PARSE_BUFFERS");
+ }
+
+ print_template(f, "fn_i_end.tpl", v);
+}
+
+
+function struct_recursive(f, v, struct_num,
+ LOCAL, i, n1, num_elems)
+{
+ num_elems = structs[struct_num, "num_elems"];
+ v["STRUCTNAME"] = structs[struct_num, "name"];
+ v["FUNCNAME"] = "io_" v["STRUCTNAME"];
+
+ print_template(f, "fn_start.tpl", v);
+
+# first all the structure pointers, scalars and arrays
+ for (i=0;i<num_elems;i++) {
+ parse_scalars(f, v, structs[struct_num, i], "PARSE_SCALARS");
+ }
+
+ print_template(f, "fn_mid.tpl", v);
+
+# now the buffers
+ for (i=0;i<num_elems;i++) {
+ parse_buffers(f, v, structs[struct_num, i], "PARSE_BUFFERS");
+ }
+
+ print_template(f, "fn_end.tpl", v);
+}
+
+function struct_parser(f, v, struct_num,
+ LOCAL, i, n1, num_elems)
+{
+ if (structs[struct_num, "recurse"] == "True") {
+ struct_recursive(f, v, struct_num);
+ } else {
+ struct_immediate(f, v, struct_num);
+ }
+}
+
+function produce_relative(f,
+ LOCAL, v, i)
+{
+ v["MODULE"]=module;
+
+ print_template(f, "module_start.tpl", v);
+
+ for (i=0;i < num_structs;i++) {
+ struct_parser(f, v, i);
+ }
+
+ print_template(f, "module_end.tpl", v);
+}
diff --git a/source/aparser/parsetree.awk b/source/aparser/parsetree.awk
new file mode 100644
index 00000000000..80587a01116
--- /dev/null
+++ b/source/aparser/parsetree.awk
@@ -0,0 +1,224 @@
+# build the parse tree for a struct file
+
+function find_structure(name,
+ LOCAL, i)
+{
+ for (i=0;i<num_structs;i++) {
+ if (structs[i, "name"] == name) return i;
+ }
+ return "-1";
+}
+
+function start_module(name)
+{
+ module=name;
+ num_structs=0;
+ num_elements=0;
+ num_unions=0;
+ num_tests=0;
+ num_options=0;
+}
+
+function set_option(name, value)
+{
+ options[name] = value;
+ options[num_options, "name"] = name;
+ options[num_options, "value"] = value;
+ num_options++;
+}
+
+function parse_define(def1, def2,
+ LOCAL, type, i)
+{
+ defines[def1]=def2;
+}
+
+function start_struct(name)
+{
+ current_struct=num_structs;
+ structs[name]=current_struct;
+ structs[current_struct, "name"]=name;
+ structs[current_struct, "num_elems"]=0;
+ structs[current_struct, "num_unions"]=0;
+ structs[current_struct, "recurse"] = options["recurse"];
+}
+
+function end_struct(name)
+{
+ if (name!="") structs[num_structs, "name"]=name;
+ printf("struct %s with %d elements\n",
+ structs[num_structs, "name"],
+ structs[num_structs, "num_elems"]);
+ num_structs++;
+ current_struct="";
+}
+
+function add_element(type, elem, case,
+ LOCAL, elem_num, i, v)
+{
+ while (defines[type]!="") {
+ type=defines[type];
+ }
+ elem_num=num_elements;
+
+ if (substr(elem, 1, 1) == ".") {
+ elem=substr(elem, 2);
+ elements[elem_num, "nowire"]=1;
+ }
+
+ if (substr(elem, 1, 1) == "*") {
+ elem=substr(elem, 2);
+ elements[elem_num, "ptr"]=1;
+ }
+
+ i=match(elem,"[[]");
+ if (i != 0) {
+ v = substr(elem, i+1, length(elem)-i-1);
+ elem=substr(elem, 1, i-1);
+ if (type=="union") {
+ elements[elem_num, "switch"] = v;
+ } else {
+ elements[elem_num, "array_len"] = v;
+ }
+ }
+
+ elements[elem_num, "type"] = type;
+ elements[elem_num, "elem"] = elem;
+ elements[elem_num, "case"] = case;
+
+ num_elements++;
+ return elem_num;
+}
+
+function add_struct_elem(type, elem, case,
+ LOCAL, elem_num)
+{
+ elem_num=structs[current_struct, "num_elems"];
+ structs[current_struct, elem_num] = add_element(type, elem, case);
+ structs[current_struct, "num_elems"]++;
+ return structs[current_struct, elem_num];
+}
+
+function start_union(elem)
+{
+ current_union = add_struct_elem("union", elem);
+ unions[current_union, "num_elems"] = 0;
+}
+
+function start_union_notencap(switch)
+{
+ add_struct_elem("uint32", "switch_"switch);
+ start_union("UNKNOWN[switch_"switch"]");
+}
+
+function start_union_encap(struct, type, switch, union)
+{
+ start_struct(struct);
+ add_struct_elem(type, switch);
+ add_struct_elem(type, "switch_"switch);
+ start_union(union"[switch_"switch"]");
+ encap_union="1";
+}
+
+function parse_case(case, type, elem,
+ LOCAL, elem_num)
+{
+ split(case, a, "[:]");
+ case = a[1];
+ elem_num = unions[current_union, "num_elems"];
+ unions[current_union, elem_num] = add_element(type, elem, case);
+ unions[current_union, "num_elems"]++;
+}
+
+function end_union(name)
+{
+ if (name!="") {
+ elements[current_union, "elem"] = name;
+ }
+ current_union="";
+ if (encap_union=="1") {
+ end_struct(name);
+ encap_union="0";
+ }
+}
+
+function delete_element(struct, elnum,
+ LOCAL, i)
+{
+ for (i=elnum;i<structs[struct,"num_elems"]-1;i++) {
+ structs[struct, i] = structs[struct, i+1];
+ }
+ structs[struct, "num_elems"]--;
+}
+
+function copy_struct(from, to,
+ LOCAL, i)
+{
+ for (i=0;i<structs[from,"num_elems"];i++) {
+ structs[to, i] = structs[from, i];
+ }
+ structs[to, "name"] = structs[from, "name"];
+ structs[to, "num_elems"] = structs[from, "num_elems"];
+ structs[to, "num_unions"] = structs[from, "num_unions"];
+}
+
+function add_sizeis_array(count, type, elem)
+{
+ copy_struct(current_struct, current_struct+1);
+ elem=substr(elem,2);
+ start_struct("array_"current_struct"_"elem);
+ add_struct_elem("uint32", count);
+ add_struct_elem(type, elem"["count"]");
+ end_struct("");
+ current_struct=num_structs;
+ add_struct_elem("array_"current_struct-1"_"elem, "*"elem"_ptr");
+}
+
+
+function start_function(type, fname)
+{
+ start_struct(fname);
+ structs[current_struct, "recurse"] = "False";
+}
+
+function end_function(LOCAL, i)
+{
+ copy_struct(num_structs, num_structs+1);
+ structs[num_structs, "name"] = "Q_"structs[num_structs, "name"];
+ for (i=0;i<structs[num_structs, "num_elems"];i++) {
+ if (match(elements[structs[num_structs, i], "properties"], "in") == 0) {
+ delete_element(num_structs, i);
+ i--;
+ }
+ }
+ end_struct();
+ current_struct=num_structs;
+ structs[num_structs, "name"] = "R_"structs[num_structs, "name"];
+ for (i=0;i<structs[num_structs, "num_elems"];i++) {
+ if (match(elements[structs[num_structs, i], "properties"], "out") == 0) {
+ delete_element(num_structs, i);
+ i--;
+ }
+ }
+ if (return_result!="void")
+ add_function_param("[out]", return_result, "status");
+ end_struct();
+}
+
+function add_function_param(properties, type, elem,
+ LOCAL, elnum, len)
+{
+ len=length(type);
+ if (substr(type, len) == "*") {
+ type=substr(type, 1, len-1);
+ elem="*"elem;
+ }
+ if (substr(elem,1,1) == "*" &&
+ (match(properties,"in") == 0 ||
+ find_structure(type) != "-1")) {
+ elem=substr(elem, 2);
+ }
+ elnum = add_struct_elem(type, elem);
+ elements[elnum, "properties"] = properties;
+}
+
diff --git a/source/aparser/spool.struct b/source/aparser/spool.struct
new file mode 100644
index 00000000000..1563ba5be07
--- /dev/null
+++ b/source/aparser/spool.struct
@@ -0,0 +1,90 @@
+module spool
+
+struct BUFFER5 {
+ uint32 buf_len;
+ uint16 buffer[buf_len];
+};
+
+struct BUFFERP {
+ uint32 buf_len;
+ BUFFER5 *buf;
+};
+
+struct UNISTR2 {
+ uint32 max_len;
+ uint32 undoc;
+ uint32 str_len;
+ uint16 buffer[str_len];
+};
+
+struct LPWSTR {
+ UNISTR2 *str;
+};
+
+struct VERSION {
+ uint32 version;
+ uint32 build;
+ uint32 osversion;
+};
+
+struct NTTIME {
+ uint32 low;
+ uint32 high;
+};
+
+struct DWORD {
+ uint32 x;
+};
+
+struct PRINTER_DRIVER_INFO_LEVEL_3 {
+ DWORD cversion;
+ LPWSTR name;
+ LPWSTR environment;
+ LPWSTR driverpath;
+ LPWSTR datafile;
+ LPWSTR configfile;
+ LPWSTR helpfile;
+ LPWSTR monitorname;
+ LPWSTR defaultdatatype;
+ BUFFERP dependentfiles;
+};
+
+struct PRINTER_DRIVER_INFO_LEVEL_6 {
+ DWORD dummy1;
+ DWORD version;
+ LPWSTR name;
+ LPWSTR environment;
+ LPWSTR driverpath;
+ LPWSTR datafile;
+ LPWSTR configfile;
+ LPWSTR helpfile;
+ LPWSTR monitorname;
+ LPWSTR defaultdatatype;
+ BUFFERP dependentfiles;
+ BUFFERP previousnames;
+ NTTIME driverdate;
+ VERSION driverversion;
+ LPWSTR mfgname;
+ LPWSTR oemurl;
+ LPWSTR hardwareid;
+ LPWSTR provider;
+};
+
+
+struct PRINTER_DRIVER_INFO {
+ uint32 level;
+ union *info[level] {
+ case 3 PRINTER_DRIVER_INFO_LEVEL_3 info_3;
+ case 6 PRINTER_DRIVER_INFO_LEVEL_6 info_6;
+ }
+};
+
+
+struct R_GETPRINTERDATA {
+ uint32 type;
+ uint32 size;
+ uint8 *data;
+ uint32 needed;
+ uint32 status;
+};
+
diff --git a/source/aparser/spool_io_printer_driver_info_level_3.prs b/source/aparser/spool_io_printer_driver_info_level_3.prs
new file mode 100644
index 00000000000..baa32ef7c7b
--- /dev/null
+++ b/source/aparser/spool_io_printer_driver_info_level_3.prs
Binary files differ
diff --git a/source/aparser/spool_io_printer_driver_info_level_6.prs b/source/aparser/spool_io_printer_driver_info_level_6.prs
new file mode 100644
index 00000000000..3c5a47e7a3c
--- /dev/null
+++ b/source/aparser/spool_io_printer_driver_info_level_6.prs
Binary files differ
diff --git a/source/aparser/srvsvc.struct b/source/aparser/srvsvc.struct
new file mode 100644
index 00000000000..aa40c8f15e4
--- /dev/null
+++ b/source/aparser/srvsvc.struct
@@ -0,0 +1,184 @@
+module srvsvc
+
+typedef uint32 LONG;
+typedef uint32 *ENUM_HND;
+
+typedef struct _UNISTR2 {
+ uint32 max_len;
+ uint32 undoc;
+ uint32 str_len;
+ wchar buffer[str_len];
+} UNISTR2;
+
+typedef UNISTR2 *LPWSTR;
+
+/* function 8 */
+struct CONN_INFO_0 {
+ uint32 id; /* connection id. */
+};
+
+struct CONN_INFO_1 {
+ uint32 id;
+ uint32 type;
+ uint32 num_opens;
+ uint32 num_users;
+ uint32 open_time;
+ LPWSTR usr_name;
+ LPWSTR net_name;
+};
+
+struct CONN_ENUM_CTR {
+ uint32 level;
+ uint32 level2;
+ uint32 num_entries;
+ uint32 num_entries2;
+ union *info[level] {
+ case 0 CONN_INFO_0 info0[num_entries];
+ case 1 CONN_INFO_1 info1[num_entries];
+ }
+};
+
+struct SRV_R_NET_CONN_ENUM {
+ .trailer;
+ CONN_ENUM_CTR ctr;
+ uint32 num_entries;
+ ENUM_HND handle;
+ uint32 status2;
+};
+
+struct SRV_Q_NET_CONN_ENUM {
+ .trailer;
+ LPWSTR dest_srv;
+ LPWSTR qual_srv;
+ uint32 level;
+ uint32 level2;
+ CONN_ENUM_CTR *ctr;
+ uint32 max_len;
+ ENUM_HND handle;
+};
+
+/* function 9 */
+struct FILE_INFO_3 {
+ uint32 id; /* file index */
+ uint32 perms; /* file permissions. don't know what format */
+ uint32 num_locks; /* file locks */
+ LPWSTR path_name; /* file name */
+ LPWSTR user_name; /* file owner */
+};
+
+struct SRV_FILE_INFO_CTR {
+ uint32 level;
+ uint32 num_entries;
+ uint32 dummy;
+ union *file[level] {
+ case 3 FILE_INFO_3 info3[num_entries];
+ }
+};
+
+struct SRV_Q_NET_FILE_ENUM {
+ .trailer;
+ LPWSTR srv_name;
+ LPWSTR qual_name;
+ uint32 dummy;
+ uint32 level;
+ SRV_FILE_INFO_CTR ctr;
+ uint32 *status;
+ uint32 preferred_len;
+ ENUM_HND enum_hnd;
+};
+
+
+struct SRV_R_NET_FILE_ENUM {
+ .trailer;
+ uint32 level;
+ uint32 dummy;
+ SRV_FILE_INFO_CTR *ctr;
+ uint32 total_entries; /* total number of files */
+ ENUM_HND enum_hnd;
+ uint32 status; /* return status */
+};
+
+
+/* function 15 */
+struct SRV_SHARE_INFO_1 {
+ LPWSTR uni_netname;
+ uint32 type;
+ LPWSTR uni_remark;
+};
+
+struct SRV_SHARE_INFO_2 {
+ LPWSTR uni_netname;
+ uint32 type;
+ LPWSTR uni_remark;
+ uint32 perms;
+ uint32 max_uses;
+ uint32 num_uses;
+ LPWSTR path;
+ LPWSTR passwd;
+};
+
+struct SRV_R_NET_SHARE_ENUM {
+ uint32 level;
+ uint32 level2;
+ uint32 *ret_count;
+ uint32 num_entries;
+ union *info[level] {
+ case 1 SRV_SHARE_INFO_1 info1[num_entries];
+ case 2 SRV_SHARE_INFO_2 info2[num_entries];
+ }
+ .trailer;
+ uint32 count;
+ ENUM_HND handle;
+ uint32 status;
+};
+
+
+
+/* function 21 */
+struct SERVER_INFO_100 {
+ uint32 dwPlatformID;
+ LPWSTR pszName;
+};
+
+struct SERVER_INFO_101 {
+ uint32 dwPlatformID;
+ LPWSTR pszName;
+ uint32 dwVerMajor;
+ uint32 dwVerMinor;
+ uint32 dwType;
+ LPWSTR pszComment;
+};
+
+struct SERVER_INFO_102 {
+ uint32 dwPlatformID;
+ LPWSTR pszName;
+ uint32 dwVerMajor;
+ uint32 dwVerMinor;
+ uint32 dwType;
+ LPWSTR pszComment;
+ uint32 dwUsers;
+ uint32 lDisc;
+ uint32 bHidden;
+ uint32 dwAnnounce;
+ uint32 dwAnnDelta;
+ uint32 dwLicenses;
+ LPWSTR pszUserPath;
+};
+
+struct SRV_R_NET_SERVER_INFO {
+ .trailer;
+ uint32 level;
+ union *info[level] {
+ case 100 SERVER_INFO_100 sv100;
+ case 101 SERVER_INFO_101 sv101;
+ case 102 SERVER_INFO_102 sv102;
+ }
+ uint32 status;
+};
+
+struct SRV_Q_NET_SERVER_INFO {
+ .trailer;
+ LPWSTR server;
+ uint32 level;
+};
+
diff --git a/source/aparser/srvsvc2.struct b/source/aparser/srvsvc2.struct
new file mode 100644
index 00000000000..362d121e378
--- /dev/null
+++ b/source/aparser/srvsvc2.struct
@@ -0,0 +1,655 @@
+module srvsvc
+
+option autoalign True
+option relative False
+option recurse True
+option foo blah
+
+#define BOOL uint32
+#define LONG uint32
+#define DWORD uint32
+#define STATUS uint32
+
+typedef struct _UNISTR2 {
+ uint32 max_len;
+ uint32 undoc;
+ uint32 str_len;
+ wchar buffer[str_len];
+} UNISTR2;
+
+struct LPWSTR {
+ UNISTR2 *str;
+};
+
+
+
+ /* -- CHARACTER DEVICE INFORMATION -- */
+
+ typedef struct _CHARDEV_INFO_0 {
+ LPWSTR pszName;
+ } CHARDEV_INFO_0;
+
+ typedef struct _CHARDEV_INFO_1 {
+ LPWSTR pszName;
+ DWORD dwStatus;
+ LPWSTR pszUser;
+ DWORD dwTime;
+ } CHARDEV_INFO_1;
+
+ typedef union _CHARDEV_INFO switch (DWORD dwLevel) ctr {
+ case 1: CHARDEV_INFO_0 *ci0;
+ case 2: CHARDEV_INFO_1 *ci1;
+ } CHARDEV_INFO;
+
+ typedef struct _CHARDEV_ENUM_0 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] CHARDEV_INFO_0 *ci0;
+ } CHARDEV_ENUM_0;
+
+ typedef struct _CHARDEV_ENUM_1 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] CHARDEV_INFO_1 *ci1;
+ } CHARDEV_ENUM_1;
+
+ typedef struct _CHARDEV_ENUM {
+ DWORD dwLevel;
+ [switch_is(dwLevel)] union {
+ [case(0)] CHARDEV_ENUM_0 *ce0;
+ [case(1)] CHARDEV_ENUM_1 *ce1;
+ } ctr;
+ } CHARDEV_ENUM;
+
+ STATUS NetrCharDevEnum( /* Function 0x00 */
+ [in,unique] LPWSTR pszServer,
+ [in,out] CHARDEV_ENUM* pCharDevEnum,
+ [in] DWORD dwMaxLen,
+ [out] DWORD* dwEntries,
+ [in,out] DWORD* hResume
+ );
+
+ STATUS NetrCharDevGetInfo( /* Function 0x01 */
+ [in,unique] LPWSTR pszServer,
+ [in,ref] LPWSTR pszDevice,
+ [in] DWORD dwLevel,
+ [out] CHARDEV_INFO* pCharDevInfo
+ );
+
+ STATUS NetrCharDevControl( /* Function 0x02 */
+ [in,unique] LPWSTR pszServer,
+ [in,ref] LPWSTR pszDevice,
+ [in] DWORD dwOpcode
+ );
+
+ /* -- CHARACTER DEVICE QUEUE INFORMATION -- */
+
+ typedef struct _CHARDEVQ_INFO_0 {
+ LPWSTR pszName;
+ } CHARDEVQ_INFO_0;
+
+ typedef struct _CHARDEVQ_INFO_1 {
+ LPWSTR pszName;
+ DWORD dwPriority;
+ LPWSTR pszDevices;
+ DWORD dwNumUsers;
+ DWORD dwNumAhead;
+ } CHARDEVQ_INFO_1;
+
+ typedef union _CHARDEVQ_INFO switch (DWORD dwLevel) ctr {
+ case 1: CHARDEVQ_INFO_0 *ci0;
+ case 2: CHARDEVQ_INFO_1 *ci1;
+ } CHARDEVQ_INFO;
+
+ typedef struct _CHARDEVQ_ENUM_0 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] CHARDEVQ_INFO_0 *ci0;
+ } CHARDEVQ_ENUM_0;
+
+ typedef struct _CHARDEVQ_ENUM_1 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] CHARDEVQ_INFO_1 *ci1;
+ } CHARDEVQ_ENUM_1;
+
+ typedef struct _CHARDEVQ_ENUM {
+ DWORD dwLevel;
+ [switch_is(dwLevel)] union {
+ [case(0)] CHARDEVQ_ENUM_0 *ce0;
+ [case(1)] CHARDEVQ_ENUM_1 *ce1;
+ } ctr;
+ } CHARDEVQ_ENUM;
+
+ STATUS NetrCharDevQEnum( /* Function 0x03 */
+ [in,unique] LPWSTR pszServer,
+ [in,unique] LPWSTR pszUser,
+ [in,out] CHARDEVQ_ENUM* pCharDevQEnum,
+ [in] DWORD dwMaxLen,
+ [out] DWORD* dwEntries,
+ [in,out] DWORD* hResume
+ );
+
+ STATUS NetrCharDevQGetInfo( /* Function 0x04 */
+ [in,unique] LPWSTR pszServer,
+ [in,ref] LPWSTR pszQueue,
+ [in,ref] LPWSTR pszUser,
+ [in] DWORD dwLevel,
+ [out] CHARDEVQ_INFO* pCharDevQInfo
+ );
+
+ STATUS NetrCharDevQSetInfo( /* Function 0x05 */
+ [in,unique] LPWSTR pszServer,
+ [in,ref] LPWSTR pszQueue,
+ [in] DWORD dwLevel,
+ [in] CHARDEVQ_INFO* pCharDevQInfo,
+ [in,out] DWORD* dwParmError
+ );
+
+ STATUS NetrCharDevQPurge( /* Function 0x06 */
+ [in,unique] LPWSTR pszServer,
+ [in,ref] LPWSTR pszQueue
+ );
+
+ STATUS NetrCharDevQPurgeSelf( /* Function 0x07 */
+ [in,unique] LPWSTR pszServer,
+ [in,ref] LPWSTR pszQueue,
+ [in,ref] LPWSTR pszComputer
+ );
+
+ /* -- CONNECTION INFORMATION -- */
+
+ typedef struct _CONNECTION_INFO_0 {
+ DWORD dwConnID;
+ } CONNECTION_INFO_0;
+
+ typedef struct _CONNECTION_INFO_1 {
+ DWORD dwConnID;
+ DWORD dwType;
+ DWORD dwNumOpens;
+ DWORD dwNumUsers;
+ DWORD dwTime;
+ LPWSTR pszUser;
+ LPWSTR pszShare;
+ } CONNECTION_INFO_1;
+
+ typedef struct _CONNECTION_ENUM_0 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] CONNECTION_INFO_0 *ci0;
+ } CONNECTION_ENUM_0;
+
+ typedef struct _CONNECTION_ENUM_1 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] CONNECTION_INFO_1 *ci1;
+ } CONNECTION_ENUM_1;
+
+ typedef struct _CONNECTION_ENUM {
+ DWORD dwLevel;
+ [switch_is(dwLevel)] union {
+ [case(0)] CONNECTION_ENUM_0 *ce0;
+ [case(1)] CONNECTION_ENUM_1 *ce1;
+ } ctr;
+ } CONNECTION_ENUM;
+
+ STATUS NetrConnectionEnum( /* Function 0x08 */
+ [in,unique] LPWSTR pszServer,
+ [in,unique] LPWSTR pszClient,
+ [in,out] CONNECTION_ENUM* pConnectionEnum,
+ [in] DWORD dwMaxLen,
+ [out] DWORD* dwEntries,
+ [in,out] DWORD* hResume
+ );
+
+ /* -- FILE INFORMATION -- */
+
+ typedef struct _FILE_INFO_2 {
+ DWORD dwFileID;
+ } FILE_INFO_2;
+
+ typedef struct _FILE_INFO_3 {
+ DWORD dwFileID;
+ DWORD dwPermissions;
+ DWORD dwNumLocks;
+ LPWSTR pszPath;
+ LPWSTR pszUser;
+ } FILE_INFO_3;
+
+ typedef union _FILE_INFO switch (DWORD dwLevel) ctr {
+ case 2: FILE_INFO_2 *fi2;
+ case 3: FILE_INFO_3 *fi3;
+ } FILE_INFO;
+
+ typedef struct _FILE_ENUM_2 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] FILE_INFO_2 *fi2;
+ } FILE_ENUM_2;
+
+ typedef struct _FILE_ENUM_3 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] FILE_INFO_3 *fi3;
+ } FILE_ENUM_3;
+
+ typedef struct _FILE_ENUM {
+ DWORD dwLevel;
+ [switch_is(dwLevel)] union {
+ [case(2)] FILE_ENUM_2 *fe2;
+ [case(3)] FILE_ENUM_3 *fe3;
+ } ctr;
+ } FILE_ENUM;
+
+ STATUS NetrFileEnum( /* Function 0x09 */
+ [in,unique] LPWSTR pszServer,
+ [in,unique] LPWSTR pszBasePath,
+ [in,unique] LPWSTR pszUser,
+ [in,out] FILE_ENUM* pFileEnum,
+ [in] DWORD dwMaxLen,
+ [out] DWORD* dwEntries,
+ [in,out] DWORD* hResume
+ );
+
+ STATUS NetrFileGetInfo( /* Function 0x0A */
+ [in,unique] LPWSTR pszServer,
+ [in] DWORD dwFileID,
+ [in] DWORD dwLevel,
+ [out] FILE_INFO* pFileInfo
+ );
+
+ STATUS NetrFileClose( /* Function 0x0B */
+ [in,unique] LPWSTR pszServer,
+ [in] DWORD dwFileID
+ );
+
+ /* -- SESSION INFORMATION -- */
+
+ typedef struct _SESSION_INFO_0 {
+ LPWSTR pszClient;
+ } SESSION_INFO_0;
+
+ typedef struct _SESSION_INFO_1 {
+ LPWSTR pszClient;
+ LPWSTR pszUser;
+ DWORD dwOpens;
+ DWORD dwTime;
+ DWORD dwIdleTime;
+ DWORD dwUserFlags;
+ } SESSION_INFO_1;
+
+ typedef struct _SESSION_INFO_2 {
+ LPWSTR pszClient;
+ LPWSTR pszUser;
+ DWORD dwOpens;
+ DWORD dwTime;
+ DWORD dwIdleTime;
+ DWORD dwUserFlags;
+ LPWSTR pszClientType;
+ } SESSION_INFO_2;
+
+ typedef struct _SESSION_ENUM_0 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] SESSION_INFO_0 *si0;
+ } SESSION_ENUM_0;
+
+ typedef struct _SESSION_ENUM_1 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] SESSION_INFO_1 *si1;
+ } SESSION_ENUM_1;
+
+ typedef struct _SESSION_ENUM_2 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] SESSION_INFO_2 *si2;
+ } SESSION_ENUM_2;
+
+ typedef struct _SESSION_ENUM {
+ DWORD dwLevel;
+ [switch_is(dwLevel)] union {
+ [case(0)] SESSION_ENUM_0 *se0;
+ [case(1)] SESSION_ENUM_1 *se1;
+ [case(2)] SESSION_ENUM_2 *se2;
+ } ctr;
+ } SESSION_ENUM;
+
+ STATUS NetrSessionEnum( /* Function 0x0C */
+ [in,unique] LPWSTR pszServer,
+ [in,unique] LPWSTR pszClient,
+ [in,unique] LPWSTR pszUser,
+ [in,out] SESSION_ENUM* pFileEnum,
+ [in] DWORD dwMaxLen,
+ [out] DWORD* dwEntries,
+ [in,out] DWORD* hResume
+ );
+
+ STATUS NetrSessionDel( /* Function 0x0D */
+ [in,unique] LPWSTR pszServer,
+ [in,ref] LPWSTR pszClient,
+ [in,ref] LPWSTR pszUser
+ );
+
+ /* -- SHARE INFORMATION -- */
+
+ typedef struct _SHARE_INFO_0 {
+ LPWSTR pszName;
+ } SHARE_INFO_0;
+
+ typedef struct _SHARE_INFO_1 {
+ LPWSTR pszName;
+ DWORD dwType;
+ LPWSTR pszComment;
+ } SHARE_INFO_1;
+
+ typedef struct _SHARE_INFO_2 {
+ LPWSTR pszName;
+ DWORD dwType;
+ LPWSTR pszComment;
+ DWORD dwPermissions;
+ DWORD dwMaxUses;
+ DWORD dwCurrentUses;
+ LPWSTR pszPath;
+ LPWSTR pszPasswd;
+ } SHARE_INFO_2;
+
+ typedef union _SHARE_INFO switch (DWORD dwLevel) ctr {
+ case 0: SHARE_INFO_0 *si0;
+ case 1: SHARE_INFO_1 *si1;
+ case 2: SHARE_INFO_2 *si2;
+ } SHARE_INFO;
+
+ typedef struct _SHARE_ENUM_0 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] SHARE_INFO_0 *si0;
+ } SHARE_ENUM_0;
+
+ typedef struct _SHARE_ENUM_1 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] SHARE_INFO_1 *si1;
+ } SHARE_ENUM_1;
+
+ typedef struct _SHARE_ENUM_2 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] SHARE_INFO_2 *si2;
+ } SHARE_ENUM_2;
+
+ typedef struct _SHARE_ENUM {
+ DWORD dwLevel;
+ [switch_is(dwLevel)] union {
+ [case(0)] SHARE_ENUM_0 *se0;
+ [case(1)] SHARE_ENUM_1 *se1;
+ [case(2)] SHARE_ENUM_2 *se2;
+ } ctr;
+ } SHARE_ENUM;
+
+ STATUS NetrShareAdd( /* Function 0x0E */
+ [in,unique] LPWSTR pszServer,
+ [in] DWORD dwLevel,
+ [out] SHARE_INFO* pShareInfo,
+ [in,out] DWORD* dwParmError
+ );
+
+ STATUS NetrShareEnum( /* Function 0x0F */
+ [in,unique] LPWSTR pszServer,
+ [in,out] SHARE_ENUM* pShareEnum,
+ [in] DWORD dwMaxLen,
+ [out] DWORD* dwEntries,
+ [in,out] DWORD* hResume
+ );
+
+ STATUS NetrShareGetInfo( /* Function 0x10 */
+ [in,unique] LPWSTR pszServer,
+ [in,ref] LPWSTR pszShare,
+ [in] DWORD dwLevel,
+ [out] SHARE_INFO* pShareInfo
+ );
+
+ STATUS NetrShareSetInfo( /* Function 0x11 */
+ [in,unique] LPWSTR pszServer,
+ [in,ref] LPWSTR pszShare,
+ [in] DWORD dwLevel,
+ [in] SHARE_INFO* pShareInfo,
+ [in] DWORD dwReserved
+ );
+
+ STATUS NetrShareDel( /* Function 0x12 */
+ [in,unique] LPWSTR pszServer,
+ [in,ref] LPWSTR pszShare,
+ [in] DWORD dwReserved
+ );
+
+ STATUS NetrShareDelSticky( /* Function 0x13 */
+ [in,unique] LPWSTR pszServer,
+ [in,ref] LPWSTR pszShare,
+ [in] DWORD dwReserved
+ );
+
+ STATUS NetrShareCheck( /* Function 0x14 */
+ [in,unique] LPWSTR pszServer,
+ [in,ref] LPWSTR pszDevice,
+ [out] DWORD* dwType
+ );
+
+ /* --- SERVER INFORMATION --- */
+
+ typedef struct _SERVER_INFO_100 {
+ DWORD dwPlatformID;
+ LPWSTR pszName;
+ } SERVER_INFO_100;
+
+ typedef struct _SERVER_INFO_101 {
+ DWORD dwPlatformID;
+ LPWSTR pszName;
+ DWORD dwVerMajor;
+ DWORD dwVerMinor;
+ DWORD dwType;
+ LPWSTR pszComment;
+ } SERVER_INFO_101;
+
+ typedef struct _SERVER_INFO_102 {
+ DWORD dwPlatformID;
+ LPWSTR pszName;
+ DWORD dwVerMajor;
+ DWORD dwVerMinor;
+ DWORD dwType;
+ LPWSTR pszComment;
+ DWORD dwUsers;
+ LONG lDisc;
+ BOOL bHidden;
+ DWORD dwAnnounce;
+ DWORD dwAnnDelta;
+ DWORD dwLicenses;
+ LPWSTR pszUserPath;
+ } SERVER_INFO_102;
+
+ typedef union _SERVER_INFO switch (DWORD dwLevel) ctr {
+ case 100: SERVER_INFO_100 *sv100;
+ case 101: SERVER_INFO_101 *sv101;
+ case 102: SERVER_INFO_102 *sv102;
+ } SERVER_INFO;
+
+ STATUS NetrServerGetInfo( /* Function 0x15 */
+ [in,unique] LPWSTR pszServerName,
+ [in] DWORD dwLevel,
+ [out] SERVER_INFO* pServerInfo
+ );
+
+ STATUS NetrServerSetInfo( /* Function 0x16 */
+ [in,unique] LPWSTR pszServerName,
+ [in] DWORD dwLevel,
+ [in] SERVER_INFO* pServerInfo,
+ [in] DWORD dwReserved
+ );
+
+ typedef struct _DISK_INFO {
+ LPWSTR pszName;
+ } DISK_INFO;
+
+ typedef struct _DISK_ENUM {
+ DWORD dwEntries;
+ [size_is(dwEntries)] DISK_INFO *di;
+ } DISK_ENUM;
+
+ STATUS NetrServerDiskEnum( /* Function 0x17 */
+ [in,unique] LPWSTR pszServer,
+ [in] DWORD dwLevel,
+ [in,out] DISK_ENUM* pDiskEnum,
+ [in] DWORD dwMaxLen,
+ [out] DWORD* dwEntries,
+ [in,out] DWORD* hResume
+ );
+
+ typedef struct _STAT_SERVER {
+ DWORD dwStart;
+ DWORD dwFOpens;
+ DWORD dwDevOpens;
+ DWORD dwJobsQueued;
+ DWORD dwSOpens;
+ DWORD dwSTimedOut;
+ DWORD dwSErrors;
+ DWORD dwPWErrors;
+ DWORD dwPermErrors;
+ DWORD dwSysErrors;
+ DWORD dwBytesSentLow;
+ DWORD dwBytesSentHigh;
+ DWORD dwBytesRcvdLow;
+ DWORD dwBytesRcvdHigh;
+ DWORD dwAVResponse;
+ DWORD dwReqBufNeed;
+ DWORD dwBigBufNeed;
+ } STAT_SERVER;
+
+ STATUS NetrServerStatisticsGet( /* Function 0x18 */
+ [in,unique] LPWSTR pszServer,
+ [in] DWORD dwLevel,
+ [in] DWORD dwOptions,
+ [out] STAT_SERVER* pStatServer
+ );
+
+ typedef struct _TRANSPORT_INFO_0 {
+ LPWSTR pszName;
+ } TRANSPORT_INFO_0;
+
+ typedef union _TRANSPORT_INFO switch (DWORD dwLevel) ctr {
+ case 0: TRANSPORT_INFO_0 *ti0;
+ } TRANSPORT_INFO;
+
+ typedef struct _TRANSPORT_ENUM_0 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] TRANSPORT_INFO_0 *ti0;
+ } TRANSPORT_ENUM_0;
+
+ typedef struct _TRANSPORT_ENUM {
+ DWORD dwLevel;
+ [switch_is(dwLevel)] union {
+ [case(0)] TRANSPORT_ENUM_0 *te0;
+ } ctr;
+ } TRANSPORT_ENUM;
+
+ STATUS NetrServerTransportAdd( /* Function 0x19 */
+ [in,unique] LPWSTR pszServer,
+ [in] DWORD dwLevel,
+ [out] TRANSPORT_INFO* pTransportInfo
+ );
+
+ STATUS NetrServerTransportEnum( /* Function 0x1a */
+ [in,unique] LPWSTR pszServer,
+ [in,out] TRANSPORT_ENUM* pTransportEnum,
+ [in] DWORD dwMaxLen,
+ [out] DWORD* dwEntries,
+ [in,out] DWORD* hResume
+ );
+
+ STATUS NetrServerTransportDel( /* Function 0x1b */
+ [in,unique] LPWSTR pszServer,
+ [in] DWORD dwLevel,
+ [out] TRANSPORT_INFO* pTransportInfo
+ );
+
+ typedef struct _TIME_OF_DAY {
+ DWORD dwElapsedTime;
+ DWORD dwMsecs;
+ DWORD dwHours;
+ DWORD dwMins;
+ DWORD dwSecs;
+ DWORD dwHunds;
+ LONG lTimeZone;
+ DWORD dwInterval;
+ DWORD dwDay;
+ DWORD dwMonth;
+ DWORD dwYear;
+ DWORD dwWeekday;
+ } TIME_OF_DAY;
+
+ STATUS NetrRemoteTOD( /* Function 0x1c */
+ [in,unique] LPWSTR pszServer,
+ [out] TIME_OF_DAY* pTOD
+ );
+
+ STATUS NetrServerSetServiceBits( /* Function 0x1d */
+ [in,unique] LPWSTR pszServer,
+ [in] DWORD hServiceStatus, /* ?? */
+ [in] DWORD dwServiceBits,
+ [in] BOOL bSetBitsOn,
+ [in] BOOL bUpdateImmediately
+ );
+
+ /* --- PATH INFORMATION --- */
+
+ STATUS NetprPathType( /* Function 0x1e */
+ void /* Not known */
+ );
+
+ STATUS NetprPathCanonicalize( /* Function 0x1f */
+ void /* Not known */
+ );
+
+ STATUS NetprPathCompare( /* Function 0x20 */
+ void /* Not known */
+ );
+
+ STATUS NetprNameValidate( /* Function 0x21 */
+ void /* Not known */
+ );
+
+ STATUS NetprNameCanonicalize( /* Function 0x22 */
+ void /* Not known */
+ );
+
+ STATUS NetprNameCompare( /* Function 0x23 */
+ void /* Not known */
+ );
+
+ /* --- LATER ADDITIONS --- */
+
+ STATUS NetrShareEnumSticky( /* Function 0x24 */
+ [in,unique] LPWSTR pszServer,
+ [in,out] SHARE_ENUM* pShareEnum,
+ [in] DWORD dwMaxLen,
+ [out] DWORD* dwEntries,
+ [in,out] DWORD* hResume
+ );
+
+ STATUS NetrShareDelStart( /* Function 0x25 */
+ [in,unique] LPWSTR pszServer,
+ [in,ref] LPWSTR pszShare,
+ [in] DWORD dwReserved /* ? */
+ );
+
+ STATUS NetrShareDelCommit( /* Function 0x26 */
+ [in,unique] LPWSTR pszServer
+ );
+
+ STATUS NetrpGetFileSecurity( /* Function 0x27 */
+ void /* Not known */
+ );
+
+ STATUS NetrpSetFileSecurity( /* Function 0x28 */
+ void /* Not known */
+ );
+
+ STATUS NetrServerTransportAddEx( /* Function 0x29 */
+ [in,unique] LPWSTR pszServer,
+ [in] DWORD dwLevel,
+ [out] TRANSPORT_INFO* pTransportInfo
+ );
+
+ STATUS NetrServerSetServiceBitsEx( /* Function 0x30 */
+ [in,unique] LPWSTR pszServer,
+ [in] DWORD hServiceStatus, /* ?? */
+ [in] DWORD dwServiceBits,
+ [in] BOOL bSetBitsOn,
+ [in] BOOL bUpdateImmediately
+ );
+
diff --git a/source/aparser/template.awk b/source/aparser/template.awk
new file mode 100644
index 00000000000..20157008354
--- /dev/null
+++ b/source/aparser/template.awk
@@ -0,0 +1,18 @@
+# template file handling
+
+function print_template(f, tplname, v,
+ LOCAL, i, pat, line)
+{
+ tplname="templates/"tplname;
+ if (numlines(tplname) <= 0) fatal("no template "tplname);
+ while ((getline line < tplname) > 0) {
+ while ((i = match(line,"@[a-zA-Z_]*@")) != 0) {
+ pat=substr(line,i+1,RLENGTH-2);
+ if (v[pat] == "") fatal("no value for "pat" in "tplname);
+ gsub("@"pat"@", v[pat], line);
+ }
+
+ xprintf(f, "%s\n", line);
+ }
+ close(tplname);
+}
diff --git a/source/aparser/templates/fn_end.tpl b/source/aparser/templates/fn_end.tpl
new file mode 100644
index 00000000000..2275ec4e33c
--- /dev/null
+++ b/source/aparser/templates/fn_end.tpl
@@ -0,0 +1,13 @@
+
+end:
+ /* the parse is OK */
+ return True;
+
+fail:
+ if (UNMARSHALLING(ps)) {
+ ZERO_STRUCTP(il);
+ }
+ return False;
+} /* @FUNCNAME@ */
+
+
diff --git a/source/aparser/templates/fn_end0.tpl b/source/aparser/templates/fn_end0.tpl
new file mode 100644
index 00000000000..6e49a10f538
--- /dev/null
+++ b/source/aparser/templates/fn_end0.tpl
@@ -0,0 +1,8 @@
+
+end:
+ /* the parse is OK */
+ return True;
+
+} /* @FUNCNAME@ */
+
+
diff --git a/source/aparser/templates/fn_i_end.tpl b/source/aparser/templates/fn_i_end.tpl
new file mode 100644
index 00000000000..9de61decb38
--- /dev/null
+++ b/source/aparser/templates/fn_i_end.tpl
@@ -0,0 +1,12 @@
+
+ /* the parse is OK */
+ return True;
+
+fail:
+ if (UNMARSHALLING(ps)) {
+ ZERO_STRUCTP(il);
+ }
+ return False;
+} /* @FUNCNAME@ */
+
+
diff --git a/source/aparser/templates/fn_i_start.tpl b/source/aparser/templates/fn_i_start.tpl
new file mode 100644
index 00000000000..3979d78e7db
--- /dev/null
+++ b/source/aparser/templates/fn_i_start.tpl
@@ -0,0 +1,15 @@
+/*******************************************************************
+parse a @STRUCTNAME@ structure
+********************************************************************/
+BOOL @FUNCNAME@(char *desc, io_struct *ps, int depth,
+ @STRUCTNAME@ *il, unsigned flags)
+{
+ io_debug(ps, depth, desc, "@FUNCNAME@");
+ depth++;
+
+#if 0
+ if (UNMARSHALLING(ps)) {
+ ZERO_STRUCTP(il);
+ }
+#endif
+ /* parse the scalars */
diff --git a/source/aparser/templates/fn_mid.tpl b/source/aparser/templates/fn_mid.tpl
new file mode 100644
index 00000000000..b81de92a5bd
--- /dev/null
+++ b/source/aparser/templates/fn_mid.tpl
@@ -0,0 +1,6 @@
+
+buffers:
+ if (!(flags & PARSE_BUFFERS)) goto end;
+
+ /* now parse the buffers */
+
diff --git a/source/aparser/templates/fn_start.tpl b/source/aparser/templates/fn_start.tpl
new file mode 100644
index 00000000000..a5d58767a6c
--- /dev/null
+++ b/source/aparser/templates/fn_start.tpl
@@ -0,0 +1,17 @@
+/*******************************************************************
+parse a @STRUCTNAME@ structure
+********************************************************************/
+BOOL @FUNCNAME@(char *desc, io_struct *ps, int depth,
+ @STRUCTNAME@ *il, unsigned flags)
+{
+ io_debug(ps, depth, desc, "@FUNCNAME@");
+ depth++;
+
+ if (!(flags & PARSE_SCALARS)) goto buffers;
+
+#if 0
+ if (UNMARSHALLING(ps)) {
+ ZERO_STRUCTP(il);
+ }
+#endif
+ /* parse the scalars */
diff --git a/source/aparser/templates/harness.tpl b/source/aparser/templates/harness.tpl
new file mode 100644
index 00000000000..27c33c0adc1
--- /dev/null
+++ b/source/aparser/templates/harness.tpl
@@ -0,0 +1,5 @@
+
+ if (strcmp(test,"@TEST@")==0) {
+ @TEST@ il;
+ ret = io_@TEST@("@TEST@", ps, 0, &il, flags);
+ } else
diff --git a/source/aparser/templates/harness_end.tpl b/source/aparser/templates/harness_end.tpl
new file mode 100644
index 00000000000..1e15faec167
--- /dev/null
+++ b/source/aparser/templates/harness_end.tpl
@@ -0,0 +1,7 @@
+ {
+ printf("structure %s not found\n", test);
+ ret = False;
+ }
+
+ return ret;
+}
diff --git a/source/aparser/templates/harness_start.tpl b/source/aparser/templates/harness_start.tpl
new file mode 100644
index 00000000000..beba6fc12de
--- /dev/null
+++ b/source/aparser/templates/harness_start.tpl
@@ -0,0 +1,7 @@
+#include "prs_@MODULE@.c"
+
+static BOOL run_test(char *test, io_struct *ps, int flags)
+{
+ BOOL ret;
+
+
diff --git a/source/aparser/templates/ifptr_end.tpl b/source/aparser/templates/ifptr_end.tpl
new file mode 100644
index 00000000000..990635cf453
--- /dev/null
+++ b/source/aparser/templates/ifptr_end.tpl
@@ -0,0 +1 @@
+ }
diff --git a/source/aparser/templates/ifptr_start.tpl b/source/aparser/templates/ifptr_start.tpl
new file mode 100644
index 00000000000..228b84bac93
--- /dev/null
+++ b/source/aparser/templates/ifptr_start.tpl
@@ -0,0 +1,2 @@
+ if (il->@ELEM@) {
+ if (!io_alloc("@ELEM@", ps, (void **)&il->@ELEM@, sizeof(*(il->@ELEM@)))) goto fail;
diff --git a/source/aparser/templates/module_end.tpl b/source/aparser/templates/module_end.tpl
new file mode 100644
index 00000000000..661f7edb95d
--- /dev/null
+++ b/source/aparser/templates/module_end.tpl
@@ -0,0 +1,3 @@
+
+
+/* end auto-generated structure parsers for @MODULE@ */
diff --git a/source/aparser/templates/module_start.tpl b/source/aparser/templates/module_start.tpl
new file mode 100644
index 00000000000..ac6a3c9d98d
--- /dev/null
+++ b/source/aparser/templates/module_start.tpl
@@ -0,0 +1,5 @@
+/* auto-generated structure parsers for @MODULE@
+ generated by aparser
+*/
+#include "prs_@MODULE@.h"
+
diff --git a/source/aparser/templates/prs_.align.tpl b/source/aparser/templates/prs_.align.tpl
new file mode 100644
index 00000000000..25816a23b34
--- /dev/null
+++ b/source/aparser/templates/prs_.align.tpl
@@ -0,0 +1 @@
+ if(!io_align(ps)) goto fail;
diff --git a/source/aparser/templates/prs_align2.tpl b/source/aparser/templates/prs_align2.tpl
new file mode 100644
index 00000000000..54c569b547b
--- /dev/null
+++ b/source/aparser/templates/prs_align2.tpl
@@ -0,0 +1 @@
+ if (!io_align2(ps, @OFFSET@)) goto fail;
diff --git a/source/aparser/templates/prs_align4.tpl b/source/aparser/templates/prs_align4.tpl
new file mode 100644
index 00000000000..702fab13243
--- /dev/null
+++ b/source/aparser/templates/prs_align4.tpl
@@ -0,0 +1 @@
+ if (!io_align4(ps, @OFFSET@)) goto fail;
diff --git a/source/aparser/templates/prs_array.tpl b/source/aparser/templates/prs_array.tpl
new file mode 100644
index 00000000000..4bd6a26c99c
--- /dev/null
+++ b/source/aparser/templates/prs_array.tpl
@@ -0,0 +1,8 @@
+ if ((@FLAGS@ & PARSE_SCALARS) &&
+ !io_alloc("@ELEM@", ps, (void **)&il->@ELEM@, sizeof(*(il->@ELEM@))*(il->@ARRAY_LEN@))) goto fail;
+ {
+ int i;
+ for (i=0;i<il->@ARRAY_LEN@;i++) {
+ if (!io_@TYPE@("@ELEM@...", ps, depth+1, &il->@ELEM@[i], @FLAGS@)) goto fail;
+ }
+ }
diff --git a/source/aparser/templates/prs_array_optional.tpl b/source/aparser/templates/prs_array_optional.tpl
new file mode 100644
index 00000000000..38bd32861f7
--- /dev/null
+++ b/source/aparser/templates/prs_array_optional.tpl
@@ -0,0 +1,5 @@
+ if ((MARSHALLING(ps) && il->@ELEM@) ||
+ ps->data_offset < ps->buffer_size) {
+ if (!io_alloc("@ELEM@", ps, (void **)&il->@ELEM@, sizeof(*(il->@ELEM@)))) goto fail;
+ if (!io_@TYPE@("@ELEM@...", ps, depth+1, il->@ELEM@, @FLAGS@)) goto fail;
+ }
diff --git a/source/aparser/templates/prs_array_remainder.tpl b/source/aparser/templates/prs_array_remainder.tpl
new file mode 100644
index 00000000000..c8b1e2ab5af
--- /dev/null
+++ b/source/aparser/templates/prs_array_remainder.tpl
@@ -0,0 +1,17 @@
+ if (UNMARSHALLING(ps))
+ {
+ int i;
+ for (i=0;ps->data_offset < ps->buffer_size;i++) {
+ if (!io_alloc("@ELEM@", ps, (void **)&il->@ELEM@, sizeof(*(il->@ELEM@))*(i+1))) goto fail;
+ if (!io_@TYPE@("@ELEM@...", ps, depth+1, &il->@ELEM@[i], @FLAGS@)) goto fail;
+ }
+ }
+ else
+ {
+ int i = -1;
+ /* HACK ALERT! */
+ do {
+ i++;
+ if (!io_@TYPE@("@ELEM@...", ps, depth+1, &il->@ELEM@[i], @FLAGS@)) goto fail;
+ } while (il->@ELEM@[i].tag2 != 0);
+ }
diff --git a/source/aparser/templates/prs_break.tpl b/source/aparser/templates/prs_break.tpl
new file mode 100644
index 00000000000..eb540f7be84
--- /dev/null
+++ b/source/aparser/templates/prs_break.tpl
@@ -0,0 +1 @@
+ break;
diff --git a/source/aparser/templates/prs_case.tpl b/source/aparser/templates/prs_case.tpl
new file mode 100644
index 00000000000..06c1bd3ae6e
--- /dev/null
+++ b/source/aparser/templates/prs_case.tpl
@@ -0,0 +1 @@
+ case @CASE@:
diff --git a/source/aparser/templates/prs_case_end.tpl b/source/aparser/templates/prs_case_end.tpl
new file mode 100644
index 00000000000..eb540f7be84
--- /dev/null
+++ b/source/aparser/templates/prs_case_end.tpl
@@ -0,0 +1 @@
+ break;
diff --git a/source/aparser/templates/prs_element.tpl b/source/aparser/templates/prs_element.tpl
new file mode 100644
index 00000000000..e8bf5180cec
--- /dev/null
+++ b/source/aparser/templates/prs_element.tpl
@@ -0,0 +1 @@
+ if (!io_@TYPE@("@ELEM@", ps, depth+1, @PTR@il->@ELEM@, @FLAGS@)) goto fail;
diff --git a/source/aparser/templates/prs_pointer.tpl b/source/aparser/templates/prs_pointer.tpl
new file mode 100644
index 00000000000..4ebcf19d834
--- /dev/null
+++ b/source/aparser/templates/prs_pointer.tpl
@@ -0,0 +1,2 @@
+ if (!io_pointer("@ELEM@_ptr", ps, depth+1,
+ (void **)&il->@ELEM@, @FLAGS@)) goto fail;
diff --git a/source/aparser/templates/prs_struct.tpl b/source/aparser/templates/prs_struct.tpl
new file mode 100644
index 00000000000..ab8246db8e7
--- /dev/null
+++ b/source/aparser/templates/prs_struct.tpl
@@ -0,0 +1 @@
+ if (!@MODULE@_io_@TYPE@("@ELEM@", &il->@ELEM@, ps, depth+1)) goto fail;
diff --git a/source/aparser/templates/prs_struct_alloc.tpl b/source/aparser/templates/prs_struct_alloc.tpl
new file mode 100644
index 00000000000..9eae5c92fca
--- /dev/null
+++ b/source/aparser/templates/prs_struct_alloc.tpl
@@ -0,0 +1 @@
+ if (!@MODULE@_io_@TYPE@_alloc("@ELEM@", &il->@ELEM@, ps, depth+1)) goto fail;
diff --git a/source/aparser/templates/prs_uint16.tpl b/source/aparser/templates/prs_uint16.tpl
new file mode 100644
index 00000000000..b40d6d4216f
--- /dev/null
+++ b/source/aparser/templates/prs_uint16.tpl
@@ -0,0 +1 @@
+ if (!io_uint16("@ELEM@", ps, depth+1, &il->@ELEM@)) goto fail;
diff --git a/source/aparser/templates/prs_uint32.tpl b/source/aparser/templates/prs_uint32.tpl
new file mode 100644
index 00000000000..eb76715d28b
--- /dev/null
+++ b/source/aparser/templates/prs_uint32.tpl
@@ -0,0 +1 @@
+ if (!io_uint32("@ELEM@", ps, depth+1, &il->@ELEM@)) goto fail;
diff --git a/source/aparser/templates/prs_uint8s.tpl b/source/aparser/templates/prs_uint8s.tpl
new file mode 100644
index 00000000000..967162213f4
--- /dev/null
+++ b/source/aparser/templates/prs_uint8s.tpl
@@ -0,0 +1,2 @@
+ if (!io_alloc("@ELEM@", ps, (void **)&il->@ELEM@, sizeof(*(il->@ELEM@))*(il->@ARRAY_LEN@))) goto fail;
+ if (!io_uint8s("@ELEM@", ps, depth+1, &il->@ELEM@, il->@ARRAY_LEN@, @FLAGS@)) goto fail;
diff --git a/source/aparser/templates/prs_uint8s_fixed.tpl b/source/aparser/templates/prs_uint8s_fixed.tpl
new file mode 100644
index 00000000000..26597f419f3
--- /dev/null
+++ b/source/aparser/templates/prs_uint8s_fixed.tpl
@@ -0,0 +1 @@
+ if (!io_uint8s_fixed("@ELEM@", ps, depth+1, il->@ELEM@, @ARRAY_LEN@, @FLAGS@)) goto fail;
diff --git a/source/aparser/templates/prs_wstring.tpl b/source/aparser/templates/prs_wstring.tpl
new file mode 100644
index 00000000000..4de46f093c8
--- /dev/null
+++ b/source/aparser/templates/prs_wstring.tpl
@@ -0,0 +1,2 @@
+ if (!io_alloc("@ELEM@", ps, (void **)&il->@ELEM@, sizeof(*(il->@ELEM@))*(il->@ARRAY_LEN@))) goto fail;
+ if (!io_wstring("@ELEM@", ps, depth+1, il->@ELEM@, il->@ARRAY_LEN@, @FLAGS@)) goto fail;
diff --git a/source/aparser/templates/prs_wstring_fixed.tpl b/source/aparser/templates/prs_wstring_fixed.tpl
new file mode 100644
index 00000000000..e33f7c3d5d2
--- /dev/null
+++ b/source/aparser/templates/prs_wstring_fixed.tpl
@@ -0,0 +1,2 @@
+ if (!io_alloc("@ELEM@", ps, (void **)&il->@ELEM@, sizeof(*(il->@ELEM@))*(@ARRAY_LEN@))) goto fail;
+ if (!io_wstring("@ELEM@", ps, depth+1, il->@ELEM@, @ARRAY_LEN@, @FLAGS@)) goto fail;
diff --git a/source/aparser/templates/union_end.tpl b/source/aparser/templates/union_end.tpl
new file mode 100644
index 00000000000..511adbcf602
--- /dev/null
+++ b/source/aparser/templates/union_end.tpl
@@ -0,0 +1,5 @@
+ default:
+ DEBUG(5,("No handler for case %d in @FUNCNAME@\n",
+ (int)il->@SWITCH@));
+ goto fail;
+ }
diff --git a/source/aparser/templates/union_start.tpl b/source/aparser/templates/union_start.tpl
new file mode 100644
index 00000000000..aa052be6972
--- /dev/null
+++ b/source/aparser/templates/union_start.tpl
@@ -0,0 +1 @@
+ switch (il->@SWITCH@) {
diff --git a/source/aparser/token.awk b/source/aparser/token.awk
new file mode 100644
index 00000000000..fb2982f0777
--- /dev/null
+++ b/source/aparser/token.awk
@@ -0,0 +1,180 @@
+# tokenise the input file
+
+function parse_error(msg) {
+ printf("PARSE ERROR: %s\nLine "NR" : "$0"\n", msg);
+ exit 1;
+}
+
+# ignore multi-line C comments.
+{
+ if (t = index($0, "/*")) {
+ if (t > 1)
+ tmp = substr($0, 1, t - 1)
+ else
+ tmp = ""
+ u = index(substr($0, t + 2), "*/")
+ while (u == 0) {
+ getline
+ t = -1
+ u = index($0, "*/")
+ }
+ if (u <= length($0) - 2)
+ $0 = tmp substr($0, t + u + 3)
+ else
+ $0 = tmp
+ }
+}
+
+# ignore blank lines
+/^[ \t]*$/ {
+ next;
+}
+
+/^\#define.*/ {
+ split($0,a,"[ \t;]*");
+ parse_define(a[2], a[3]);
+ next;
+}
+
+# ignore comments
+/^[ \t]*\#/ {
+ next;
+}
+
+/^[ \t]*module/ {
+ {if (module!="") parse_error("you can only specify one module name");}
+ start_module($2);
+ next;
+}
+
+{if (module=="") parse_error("you must specify the module name first");}
+
+/^[ \t]*option/ {
+ set_option($2, $3);
+ next;
+}
+
+/^[ \t]*typedef struct.*\{/ {
+ {if (current_struct!="") parse_error("you cannot have nested structures");}
+ start_struct($3);
+ next;
+}
+
+/^[ \t]*struct.*\{/ {
+ {if (current_struct!="") parse_error("you cannot have nested structures");}
+ start_struct($2);
+ next;
+}
+
+/^[ \t]*typedef union.*\{/ {
+ {if (current_struct!="") parse_error("this cannot appear inside a structure");}
+ split($0,a,"[ \t;()]*");
+ start_union_encap(a[4], a[6], a[7], a[8]);
+ next;
+}
+
+/^[ \t]*void.*\(/ {
+ {if (current_struct!="") parse_error("you cannot have nested structures");}
+ split($0,a,"[ \t;()]*");
+ start_function(a[2], a[3]);
+ return_result="void";
+ next;
+}
+
+/^[ \t]*STATUS.*\(|^[ \t]*void.*\(/ {
+ {if (current_struct!="") parse_error("you cannot have nested structures");}
+ split($0,a,"[ \t;()]*");
+ start_function(a[2], a[3]);
+ return_result="STATUS";
+ next;
+}
+
+{if (current_struct=="") parse_error("this must appear inside a structure");}
+
+/^[ \t]*union.*\{/ {
+ {if (current_union!="") parse_error("you cannot have nested unions");}
+ start_union($2);
+ next;
+}
+
+/^[ \t]*\[switch_is.*union.*\{/ {
+ {if (current_union!="") parse_error("you cannot have nested unions");}
+ split($0,a,"[ \t;()]*");
+ start_union_notencap(a[3]);
+ next;
+}
+
+/^[ \t]*case.*;/ {
+ {if (current_union=="") parse_error("this must appear inide a union");}
+ split($0,a,"[ \t;]*");
+ parse_case(a[3],a[4],a[5]);
+ next;
+}
+
+/^[ \t]*\[case(.*)\].*;/ {
+ {if (current_union=="") parse_error("this must appear inide a union");}
+ split($0,a,"[ \t;()[\]]*");
+ parse_case(a[6],a[8],a[9]);
+ next;
+}
+
+/^[ \t]*\}$/ {
+ {if (current_union=="") parse_error("this must appear inside a union");}
+ end_union("");
+ next;
+}
+
+/^[ \t]*\} .*;/ {
+ if (current_union!="") {
+ split($2,a,"[ \t;]*");
+ end_union(a[1]);
+ next;
+ }
+}
+
+{if (current_union!="") parse_error("this cannot appear inside a union");}
+
+/^[ \t]*\};/ {
+ end_struct("");
+ next;
+}
+
+/^[ \t]*\} .*;/ {
+ split($2,a,"[ \t;]*");
+ end_struct(a[1]);
+ next;
+}
+
+/^[ \t]*\);/ {
+ end_function();
+ return_result="";
+ next;
+}
+
+/^.*size_is.*\*.*;/ {
+ split($0,a,"[ \t;()]*");
+ add_sizeis_array(a[3], a[5], a[6]);
+ next;
+}
+
+/^.*;/ {
+ split($0,a,"[ \t;]*");
+ add_struct_elem(a[2], a[3]);
+ next;
+}
+
+/^[\t ]*void/ {
+ next;
+}
+
+/^[ \t]*\[.*\].*/ {
+ split($0,a,"[ \t;]*");
+ split(a[4], b, "[,]");
+ add_function_param(a[2], a[3], b[1]);
+ next;
+}
+
+{
+ parse_error("Unknown construct.");
+}
+
diff --git a/source/aparser/util.awk b/source/aparser/util.awk
new file mode 100644
index 00000000000..6c5594da688
--- /dev/null
+++ b/source/aparser/util.awk
@@ -0,0 +1,39 @@
+function isaptr(elem)
+{
+ if (substr(elem, 1, 1) == "*") {
+ return 1;
+ }
+ return 0;
+}
+
+function noptr(elem)
+{
+ if (!isaptr(elem)) return elem;
+ return substr(elem, 2);
+}
+
+function xprintf(f, fmt, v1, v2, v3, v4, v5, v6, v7)
+{
+ printf(fmt, v1, v2, v3, v4, v5, v6) > f;
+}
+
+function fatal(why)
+{
+ printf("FATAL: %s\n", why);
+ exit 1;
+}
+
+function numlines(fname,
+ LOCAL, line, count)
+{
+ count=0;
+ while ((getline line < fname) > 0) count++;
+ close(fname);
+ return count;
+}
+
+# return 1 if the string is a constant
+function is_constant(s)
+{
+ return match(s,"^[0-9]+$");
+}
diff --git a/source/aparser/util.c b/source/aparser/util.c
new file mode 100644
index 00000000000..ffa84dbfabc
--- /dev/null
+++ b/source/aparser/util.c
@@ -0,0 +1,112 @@
+#include "parser.h"
+
+
+/*******************************************************************
+ Count the number of characters (not bytes) in a unicode string.
+********************************************************************/
+size_t strlen_w(void *src)
+{
+ size_t len;
+
+ for (len = 0; SVAL(src, len*2); len++) ;
+
+ return len;
+}
+
+/****************************************************************************
+expand a pointer to be a particular size
+****************************************************************************/
+void *Realloc(void *p,size_t size)
+{
+ void *ret=NULL;
+
+ if (size == 0) {
+ if (p) free(p);
+ DEBUG(5,("Realloc asked for 0 bytes\n"));
+ return NULL;
+ }
+
+ if (!p)
+ ret = (void *)malloc(size);
+ else
+ ret = (void *)realloc(p,size);
+
+ if (!ret)
+ DEBUG(0,("Memory allocation error: failed to expand to %d bytes\n",(int)size));
+
+ return(ret);
+}
+
+
+char *tab_depth(int depth)
+{
+ static pstring spaces;
+ memset(spaces, ' ', depth * 4);
+ spaces[depth * 4] = 0;
+ return spaces;
+}
+
+void print_asc(int level, uchar const *buf, int len)
+{
+ int i;
+ for (i = 0; i < len; i++)
+ {
+ DEBUGADD(level, ("%c", isprint(buf[i]) ? buf[i] : '.'));
+ }
+}
+
+void dump_data(int level, char *buf1, int len)
+{
+ uchar const *buf = (uchar const *)buf1;
+ int i = 0;
+ if (buf == NULL)
+ {
+ DEBUG(level, ("dump_data: NULL, len=%d\n", len));
+ return;
+ }
+ if (len < 0)
+ return;
+ if (len == 0)
+ {
+ DEBUG(level, ("\n"));
+ return;
+ }
+
+ DEBUG(level, ("[%03X] ", i));
+ for (i = 0; i < len;)
+ {
+ DEBUGADD(level, ("%02X ", (int)buf[i]));
+ i++;
+ if (i % 8 == 0)
+ DEBUGADD(level, (" "));
+ if (i % 16 == 0)
+ {
+ print_asc(level, &buf[i - 16], 8);
+ DEBUGADD(level, (" "));
+ print_asc(level, &buf[i - 8], 8);
+ DEBUGADD(level, ("\n"));
+ if (i < len)
+ DEBUGADD(level, ("[%03X] ", i));
+ }
+ }
+
+ if (i % 16 != 0) /* finish off a non-16-char-length row */
+ {
+ int n;
+
+ n = 16 - (i % 16);
+ DEBUGADD(level, (" "));
+ if (n > 8)
+ DEBUGADD(level, (" "));
+ while (n--)
+ DEBUGADD(level, (" "));
+
+ n = MIN(8, i % 16);
+ print_asc(level, &buf[i - (i % 16)], n);
+ DEBUGADD(level, (" "));
+ n = (i % 16) - n;
+ if (n > 0)
+ print_asc(level, &buf[i - n], n);
+ DEBUGADD(level, ("\n"));
+ }
+}
diff --git a/source/aparser/vluke.c b/source/aparser/vluke.c
new file mode 100644
index 00000000000..d3868f2753e
--- /dev/null
+++ b/source/aparser/vluke.c
@@ -0,0 +1,41 @@
+#include "parser.h"
+#include "test.h"
+
+int main(int argc, char *argv[])
+{
+ BOOL ret;
+ char *fname, *test;
+ int fd;
+ struct stat st;
+ io_struct ps;
+
+ if (argc < 3) {
+ printf("usage: vluke <structure> <file>\n");
+ exit(1);
+ }
+
+ test = argv[1];
+ fname = argv[2];
+
+ fd = open(fname,O_RDONLY);
+ if (fd == -1) {
+ perror(fname);
+ exit(1);
+ }
+ fstat(fd, &st);
+
+ io_init(&ps, 0, MARSHALL);
+ ps.is_dynamic=True;
+ io_read(&ps, fd, st.st_size, 0);
+ ps.data_offset = 0;
+ ps.buffer_size = ps.grow_size;
+ ps.io = UNMARSHALL;
+ ps.autoalign = OPTION_autoalign;
+ ret = run_test(test, &ps, PARSE_SCALARS|PARSE_BUFFERS);
+ printf("\nret=%s\n", ret?"OK":"Bad");
+ printf("Trailer is %d bytes\n\n", ps.grow_size - ps.data_offset);
+ if (ps.grow_size - ps.data_offset > 0) {
+ dump_data(0, ps.data_p + ps.data_offset, ps.grow_size - ps.data_offset);
+ }
+ return !ret;
+}
diff --git a/source/auth/auth.c b/source/auth/auth.c
new file mode 100644
index 00000000000..1b49699fbca
--- /dev/null
+++ b/source/auth/auth.c
@@ -0,0 +1,534 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett 2001-2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+static struct auth_init_function_entry *backends = NULL;
+
+static struct auth_init_function_entry *auth_find_backend_entry(const char *name);
+
+NTSTATUS smb_register_auth(int version, const char *name, auth_init_function init)
+{
+ struct auth_init_function_entry *entry = backends;
+
+ if (version != AUTH_INTERFACE_VERSION) {
+ DEBUG(0,("Can't register auth_method!\n"
+ "You tried to register an auth module with AUTH_INTERFACE_VERSION %d, while this version of samba uses %d\n",
+ version,AUTH_INTERFACE_VERSION));
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ if (!name || !init) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(5,("Attempting to register auth backend %s\n", name));
+
+ if (auth_find_backend_entry(name)) {
+ DEBUG(0,("There already is an auth method registered with the name %s!\n", name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ entry = smb_xmalloc(sizeof(struct auth_init_function_entry));
+ entry->name = smb_xstrdup(name);
+ entry->init = init;
+
+ DLIST_ADD(backends, entry);
+ DEBUG(5,("Successfully added auth method '%s'\n", name));
+ return NT_STATUS_OK;
+}
+
+static struct auth_init_function_entry *auth_find_backend_entry(const char *name)
+{
+ struct auth_init_function_entry *entry = backends;
+
+ while(entry) {
+ if (strcmp(entry->name, name)==0) return entry;
+ entry = entry->next;
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Try to get a challenge out of the various authentication modules.
+ Returns a const char of length 8 bytes.
+****************************************************************************/
+
+static const uint8 *get_ntlm_challenge(struct auth_context *auth_context)
+{
+ DATA_BLOB challenge = data_blob(NULL, 0);
+ const char *challenge_set_by = NULL;
+ auth_methods *auth_method;
+ TALLOC_CTX *mem_ctx;
+
+ if (auth_context->challenge.length) {
+ DEBUG(5, ("get_ntlm_challenge (auth subsystem): returning previous challenge by module %s (normal)\n",
+ auth_context->challenge_set_by));
+ return auth_context->challenge.data;
+ }
+
+ auth_context->challenge_may_be_modified = False;
+
+ for (auth_method = auth_context->auth_method_list; auth_method; auth_method = auth_method->next) {
+ if (auth_method->get_chal == NULL) {
+ DEBUG(5, ("auth_get_challenge: module %s did not want to specify a challenge\n", auth_method->name));
+ continue;
+ }
+
+ DEBUG(5, ("auth_get_challenge: getting challenge from module %s\n", auth_method->name));
+ if (challenge_set_by != NULL) {
+ DEBUG(1, ("auth_get_challenge: CONFIGURATION ERROR: authentication method %s has already specified a challenge. Challenge by %s ignored.\n",
+ challenge_set_by, auth_method->name));
+ continue;
+ }
+
+ mem_ctx = talloc_init("auth_get_challenge for module %s", auth_method->name);
+ if (!mem_ctx) {
+ smb_panic("talloc_init() failed!");
+ }
+
+ challenge = auth_method->get_chal(auth_context, &auth_method->private_data, mem_ctx);
+ if (!challenge.length) {
+ DEBUG(3, ("auth_get_challenge: getting challenge from authentication method %s FAILED.\n",
+ auth_method->name));
+ } else {
+ DEBUG(5, ("auth_get_challenge: sucessfully got challenge from module %s\n", auth_method->name));
+ auth_context->challenge = challenge;
+ challenge_set_by = auth_method->name;
+ auth_context->challenge_set_method = auth_method;
+ }
+ talloc_destroy(mem_ctx);
+ }
+
+ if (!challenge_set_by) {
+ uchar chal[8];
+
+ generate_random_buffer(chal, sizeof(chal), False);
+ auth_context->challenge = data_blob_talloc(auth_context->mem_ctx,
+ chal, sizeof(chal));
+
+ challenge_set_by = "random";
+ auth_context->challenge_may_be_modified = True;
+ }
+
+ DEBUG(5, ("auth_context challenge created by %s\n", challenge_set_by));
+ DEBUG(5, ("challenge is: \n"));
+ dump_data(5, (const char *)auth_context->challenge.data, auth_context->challenge.length);
+
+ SMB_ASSERT(auth_context->challenge.length == 8);
+
+ auth_context->challenge_set_by=challenge_set_by;
+
+ return auth_context->challenge.data;
+}
+
+
+/**
+ * Check user is in correct domain (if required)
+ *
+ * @param user Only used to fill in the debug message
+ *
+ * @param domain The domain to be verified
+ *
+ * @return True if the user can connect with that domain,
+ * False otherwise.
+**/
+
+static BOOL check_domain_match(const char *user, const char *domain)
+{
+ /*
+ * If we aren't serving to trusted domains, we must make sure that
+ * the validation request comes from an account in the same domain
+ * as the Samba server
+ */
+
+ if (!lp_allow_trusted_domains() &&
+ !(strequal("", domain) ||
+ strequal(lp_workgroup(), domain) ||
+ is_myname(domain))) {
+ DEBUG(1, ("check_domain_match: Attempt to connect as user %s from domain %s denied.\n", user, domain));
+ return False;
+ } else {
+ return True;
+ }
+}
+
+/**
+ * Check a user's Plaintext, LM or NTLM password.
+ *
+ * Check a user's password, as given in the user_info struct and return various
+ * interesting details in the server_info struct.
+ *
+ * This function does NOT need to be in a become_root()/unbecome_root() pair
+ * as it makes the calls itself when needed.
+ *
+ * The return value takes precedence over the contents of the server_info
+ * struct. When the return is other than NT_STATUS_OK the contents
+ * of that structure is undefined.
+ *
+ * @param user_info Contains the user supplied components, including the passwords.
+ * Must be created with make_user_info() or one of its wrappers.
+ *
+ * @param auth_context Supplies the challenges and some other data.
+ * Must be created with make_auth_context(), and the challenges should be
+ * filled in, either at creation or by calling the challenge geneation
+ * function auth_get_challenge().
+ *
+ * @param server_info If successful, contains information about the authentication,
+ * including a SAM_ACCOUNT struct describing the user.
+ *
+ * @return An NTSTATUS with NT_STATUS_OK or an appropriate error.
+ *
+ **/
+
+static NTSTATUS check_ntlm_password(const struct auth_context *auth_context,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **server_info)
+{
+ /* if all the modules say 'not for me' this is reasonable */
+ NTSTATUS nt_status = NT_STATUS_NO_SUCH_USER;
+ const char *unix_username;
+ auth_methods *auth_method;
+ TALLOC_CTX *mem_ctx;
+
+ if (!user_info || !auth_context || !server_info)
+ return NT_STATUS_LOGON_FAILURE;
+
+ DEBUG(3, ("check_ntlm_password: Checking password for unmapped user [%s]\\[%s]@[%s] with the new password interface\n",
+ user_info->client_domain.str, user_info->smb_name.str, user_info->wksta_name.str));
+
+ DEBUG(3, ("check_ntlm_password: mapped user is: [%s]\\[%s]@[%s]\n",
+ user_info->domain.str, user_info->internal_username.str, user_info->wksta_name.str));
+
+ if (auth_context->challenge.length != 8) {
+ DEBUG(0, ("check_ntlm_password: Invalid challenge stored for this auth context - cannot continue\n"));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ if (auth_context->challenge_set_by)
+ DEBUG(10, ("check_ntlm_password: auth_context challenge created by %s\n",
+ auth_context->challenge_set_by));
+
+ DEBUG(10, ("challenge is: \n"));
+ dump_data(5, (const char *)auth_context->challenge.data, auth_context->challenge.length);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("user_info has passwords of length %d and %d\n",
+ user_info->lm_resp.length, user_info->nt_resp.length));
+ DEBUG(100, ("lm:\n"));
+ dump_data(100, user_info->lm_resp.data, user_info->lm_resp.length);
+ DEBUG(100, ("nt:\n"));
+ dump_data(100, user_info->nt_resp.data, user_info->nt_resp.length);
+#endif
+
+ /* This needs to be sorted: If it doesn't match, what should we do? */
+ if (!check_domain_match(user_info->smb_name.str, user_info->domain.str))
+ return NT_STATUS_LOGON_FAILURE;
+
+ for (auth_method = auth_context->auth_method_list;auth_method; auth_method = auth_method->next) {
+ NTSTATUS result;
+
+ mem_ctx = talloc_init("%s authentication for user %s\\%s", auth_method->name,
+ user_info->domain.str, user_info->smb_name.str);
+
+ result = auth_method->auth(auth_context, auth_method->private_data, mem_ctx, user_info, server_info);
+
+ /* check if the module did anything */
+ if ( NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_NOT_IMPLEMENTED) ) {
+ DEBUG(10,("check_ntlm_password: %s had nothing to say\n", auth_method->name));
+ talloc_destroy(mem_ctx);
+ continue;
+ }
+
+ nt_status = result;
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(3, ("check_ntlm_password: %s authentication for user [%s] succeeded\n",
+ auth_method->name, user_info->smb_name.str));
+ } else {
+ DEBUG(5, ("check_ntlm_password: %s authentication for user [%s] FAILED with error %s\n",
+ auth_method->name, user_info->smb_name.str, nt_errstr(nt_status)));
+ }
+
+ talloc_destroy(mem_ctx);
+
+ if ( NT_STATUS_IS_OK(nt_status))
+ {
+ break;
+ }
+ }
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ unix_username = (*server_info)->unix_name;
+ if (!(*server_info)->guest) {
+ /* We might not be root if we are an RPC call */
+ become_root();
+ nt_status = smb_pam_accountcheck(unix_username);
+ unbecome_root();
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(5, ("check_ntlm_password: PAM Account for user [%s] succeeded\n",
+ unix_username));
+ } else {
+ DEBUG(3, ("check_ntlm_password: PAM Account for user [%s] FAILED with error %s\n",
+ unix_username, nt_errstr(nt_status)));
+ }
+ }
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ DEBUG((*server_info)->guest ? 5 : 2,
+ ("check_ntlm_password: %sauthentication for user [%s] -> [%s] -> [%s] succeeded\n",
+ (*server_info)->guest ? "guest " : "",
+ user_info->smb_name.str,
+ user_info->internal_username.str,
+ unix_username));
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(2, ("check_ntlm_password: Authentication for user [%s] -> [%s] FAILED with error %s\n",
+ user_info->smb_name.str, user_info->internal_username.str,
+ nt_errstr(nt_status)));
+ ZERO_STRUCTP(server_info);
+ }
+ return nt_status;
+}
+
+/***************************************************************************
+ Clear out a auth_context, and destroy the attached TALLOC_CTX
+***************************************************************************/
+
+static void free_auth_context(struct auth_context **auth_context)
+{
+ auth_methods *auth_method;
+
+ if (*auth_context) {
+ /* Free private data of context's authentication methods */
+ for (auth_method = (*auth_context)->auth_method_list; auth_method; auth_method = auth_method->next) {
+ if (auth_method->free_private_data) {
+ auth_method->free_private_data (&auth_method->private_data);
+ auth_method->private_data = NULL;
+ }
+ }
+
+ talloc_destroy((*auth_context)->mem_ctx);
+ *auth_context = NULL;
+ }
+}
+
+/***************************************************************************
+ Make a auth_info struct
+***************************************************************************/
+
+static NTSTATUS make_auth_context(struct auth_context **auth_context)
+{
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_init("authentication context");
+
+ *auth_context = talloc(mem_ctx, sizeof(**auth_context));
+ if (!*auth_context) {
+ DEBUG(0,("make_auth_context: talloc failed!\n"));
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ZERO_STRUCTP(*auth_context);
+
+ (*auth_context)->mem_ctx = mem_ctx;
+ (*auth_context)->check_ntlm_password = check_ntlm_password;
+ (*auth_context)->get_ntlm_challenge = get_ntlm_challenge;
+ (*auth_context)->free = free_auth_context;
+
+ return NT_STATUS_OK;
+}
+
+BOOL load_auth_module(struct auth_context *auth_context,
+ const char *module, auth_methods **ret)
+{
+ static BOOL initialised_static_modules = False;
+
+ struct auth_init_function_entry *entry;
+ char *module_name = smb_xstrdup(module);
+ char *module_params = NULL;
+ char *p;
+ BOOL good = False;
+
+ /* Initialise static modules if not done so yet */
+ if(!initialised_static_modules) {
+ static_init_auth;
+ initialised_static_modules = True;
+ }
+
+ DEBUG(5,("load_auth_module: Attempting to find an auth method to match %s\n",
+ module));
+
+ p = strchr(module_name, ':');
+ if (p) {
+ *p = 0;
+ module_params = p+1;
+ trim_char(module_params, ' ', ' ');
+ }
+
+ trim_char(module_name, ' ', ' ');
+
+ entry = auth_find_backend_entry(module_name);
+
+ if (entry == NULL) {
+ if (NT_STATUS_IS_OK(smb_probe_module("auth", module_name))) {
+ entry = auth_find_backend_entry(module_name);
+ }
+ }
+
+ if (entry != NULL) {
+ if (!NT_STATUS_IS_OK(entry->init(auth_context, module_params, ret))) {
+ DEBUG(0,("load_auth_module: auth method %s did not correctly init\n",
+ module_name));
+ } else {
+ DEBUG(5,("load_auth_module: auth method %s has a valid init\n",
+ module_name));
+ good = True;
+ }
+ } else {
+ DEBUG(0,("load_auth_module: can't find auth method %s!\n", module_name));
+ }
+
+ SAFE_FREE(module_name);
+ return good;
+}
+
+/***************************************************************************
+ Make a auth_info struct for the auth subsystem
+***************************************************************************/
+
+static NTSTATUS make_auth_context_text_list(struct auth_context **auth_context, char **text_list)
+{
+ auth_methods *list = NULL;
+ auth_methods *t = NULL;
+ auth_methods *tmp;
+ NTSTATUS nt_status;
+
+ if (!text_list) {
+ DEBUG(2,("make_auth_context_text_list: No auth method list!?\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = make_auth_context(auth_context)))
+ return nt_status;
+
+ for (;*text_list; text_list++) {
+ if (load_auth_module(*auth_context, *text_list, &t)) {
+ DLIST_ADD_END(list, t, tmp);
+ }
+ }
+
+ (*auth_context)->auth_method_list = list;
+
+ return nt_status;
+}
+
+/***************************************************************************
+ Make a auth_context struct for the auth subsystem
+***************************************************************************/
+
+NTSTATUS make_auth_context_subsystem(struct auth_context **auth_context)
+{
+ char **auth_method_list = NULL;
+ NTSTATUS nt_status;
+
+ if (lp_auth_methods() && !str_list_copy(&auth_method_list, lp_auth_methods())) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (auth_method_list == NULL) {
+ switch (lp_security())
+ {
+ case SEC_DOMAIN:
+ DEBUG(5,("Making default auth method list for security=domain\n"));
+ auth_method_list = str_list_make("guest sam winbind:ntdomain", NULL);
+ break;
+ case SEC_SERVER:
+ DEBUG(5,("Making default auth method list for security=server\n"));
+ auth_method_list = str_list_make("guest sam smbserver", NULL);
+ break;
+ case SEC_USER:
+ if (lp_encrypted_passwords()) {
+ if ((lp_server_role() == ROLE_DOMAIN_PDC) || (lp_server_role() == ROLE_DOMAIN_BDC)) {
+ DEBUG(5,("Making default auth method list for DC, security=user, encrypt passwords = yes\n"));
+ auth_method_list = str_list_make("guest sam winbind:trustdomain", NULL);
+ } else {
+ DEBUG(5,("Making default auth method list for standalone security=user, encrypt passwords = yes\n"));
+ auth_method_list = str_list_make("guest sam", NULL);
+ }
+ } else {
+ DEBUG(5,("Making default auth method list for security=user, encrypt passwords = no\n"));
+ auth_method_list = str_list_make("guest unix", NULL);
+ }
+ break;
+ case SEC_SHARE:
+ if (lp_encrypted_passwords()) {
+ DEBUG(5,("Making default auth method list for security=share, encrypt passwords = yes\n"));
+ auth_method_list = str_list_make("guest sam", NULL);
+ } else {
+ DEBUG(5,("Making default auth method list for security=share, encrypt passwords = no\n"));
+ auth_method_list = str_list_make("guest unix", NULL);
+ }
+ break;
+ case SEC_ADS:
+ DEBUG(5,("Making default auth method list for security=ADS\n"));
+ auth_method_list = str_list_make("guest sam winbind:ntdomain", NULL);
+ break;
+ default:
+ DEBUG(5,("Unknown auth method!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ } else {
+ DEBUG(5,("Using specified auth order\n"));
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = make_auth_context_text_list(auth_context, auth_method_list))) {
+ str_list_free(&auth_method_list);
+ return nt_status;
+ }
+
+ str_list_free(&auth_method_list);
+ return nt_status;
+}
+
+/***************************************************************************
+ Make a auth_info struct with a fixed challenge
+***************************************************************************/
+
+NTSTATUS make_auth_context_fixed(struct auth_context **auth_context, uchar chal[8])
+{
+ NTSTATUS nt_status;
+ if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(auth_context))) {
+ return nt_status;
+ }
+
+ (*auth_context)->challenge = data_blob_talloc((*auth_context)->mem_ctx, chal, 8);
+ (*auth_context)->challenge_set_by = "fixed";
+ return nt_status;
+}
+
+
diff --git a/source/auth/auth_builtin.c b/source/auth/auth_builtin.c
new file mode 100644
index 00000000000..96c2221652e
--- /dev/null
+++ b/source/auth/auth_builtin.c
@@ -0,0 +1,175 @@
+/*
+ Unix SMB/CIFS implementation.
+ Generic authentication types
+ Copyright (C) Andrew Bartlett 2001-2002
+ Copyright (C) Jelmer Vernooij 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/**
+ * Return a guest logon for guest users (username = "")
+ *
+ * Typically used as the first module in the auth chain, this allows
+ * guest logons to be dealt with in one place. Non-guest logons 'fail'
+ * and pass onto the next module.
+ **/
+
+static NTSTATUS check_guest_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ /* mark this as 'not for me' */
+ NTSTATUS nt_status = NT_STATUS_NOT_IMPLEMENTED;
+
+ if (!(user_info->internal_username.str
+ && *user_info->internal_username.str)) {
+ nt_status = make_server_info_guest(server_info);
+ }
+
+ return nt_status;
+}
+
+/* Guest modules initialisation */
+
+static NTSTATUS auth_init_guest(struct auth_context *auth_context, const char *options, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method))
+ return NT_STATUS_NO_MEMORY;
+
+ (*auth_method)->auth = check_guest_security;
+ (*auth_method)->name = "guest";
+ return NT_STATUS_OK;
+}
+
+#ifdef DEVELOPER
+/**
+ * Return an error based on username
+ *
+ * This function allows the testing of obsure errors, as well as the generation
+ * of NT_STATUS -> DOS error mapping tables.
+ *
+ * This module is of no value to end-users.
+ *
+ * The password is ignored.
+ *
+ * @return An NTSTATUS value based on the username
+ **/
+
+static NTSTATUS check_name_to_ntstatus_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ NTSTATUS nt_status;
+ fstring user;
+ long error_num;
+ fstrcpy(user, user_info->smb_name.str);
+
+ if (strnequal("NT_STATUS", user, strlen("NT_STATUS"))) {
+ strupper_m(user);
+ return nt_status_string_to_code(user);
+ }
+
+ strlower_m(user);
+ error_num = strtoul(user, NULL, 16);
+
+ DEBUG(5,("check_name_to_ntstatus_security: Error for user %s was %lx\n", user, error_num));
+
+ nt_status = NT_STATUS(error_num);
+
+ return nt_status;
+}
+
+/** Module initialisation function */
+
+static NTSTATUS auth_init_name_to_ntstatus(struct auth_context *auth_context, const char *param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method))
+ return NT_STATUS_NO_MEMORY;
+
+ (*auth_method)->auth = check_name_to_ntstatus_security;
+ (*auth_method)->name = "name_to_ntstatus";
+ return NT_STATUS_OK;
+}
+
+/**
+ * Return a 'fixed' challenge instead of a variable one.
+ *
+ * The idea of this function is to make packet snifs consistant
+ * with a fixed challenge, so as to aid debugging.
+ *
+ * This module is of no value to end-users.
+ *
+ * This module does not actually authenticate the user, but
+ * just pretenteds to need a specified challenge.
+ * This module removes *all* security from the challenge-response system
+ *
+ * @return NT_STATUS_UNSUCCESSFUL
+ **/
+
+static NTSTATUS check_fixed_challenge_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************************
+ Get the challenge out of a password server.
+****************************************************************************/
+
+static DATA_BLOB auth_get_fixed_challenge(const struct auth_context *auth_context,
+ void **my_private_data,
+ TALLOC_CTX *mem_ctx)
+{
+ const char *challenge = "I am a teapot";
+ return data_blob(challenge, 8);
+}
+
+
+/** Module initailisation function */
+
+static NTSTATUS auth_init_fixed_challenge(struct auth_context *auth_context, const char *param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method))
+ return NT_STATUS_NO_MEMORY;
+
+ (*auth_method)->auth = check_fixed_challenge_security;
+ (*auth_method)->get_chal = auth_get_fixed_challenge;
+ (*auth_method)->name = "fixed_challenge";
+ return NT_STATUS_OK;
+}
+#endif /* DEVELOPER */
+
+NTSTATUS auth_builtin_init(void)
+{
+ smb_register_auth(AUTH_INTERFACE_VERSION, "guest", auth_init_guest);
+#ifdef DEVELOPER
+ smb_register_auth(AUTH_INTERFACE_VERSION, "fixed_challenge", auth_init_fixed_challenge);
+ smb_register_auth(AUTH_INTERFACE_VERSION, "name_to_ntstatus", auth_init_name_to_ntstatus);
+#endif
+ return NT_STATUS_OK;
+}
diff --git a/source/auth/auth_compat.c b/source/auth/auth_compat.c
new file mode 100644
index 00000000000..a70f1e98b72
--- /dev/null
+++ b/source/auth/auth_compat.c
@@ -0,0 +1,122 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett 2001-2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/****************************************************************************
+ COMPATIBILITY INTERFACES:
+ ***************************************************************************/
+
+/****************************************************************************
+check if a username/password is OK assuming the password is a 24 byte
+SMB hash
+return True if the password is correct, False otherwise
+****************************************************************************/
+
+NTSTATUS check_plaintext_password(const char *smb_name, DATA_BLOB plaintext_password, auth_serversupplied_info **server_info)
+{
+ struct auth_context *plaintext_auth_context = NULL;
+ auth_usersupplied_info *user_info = NULL;
+ const uint8 *chal;
+ NTSTATUS nt_status;
+ if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(&plaintext_auth_context))) {
+ return nt_status;
+ }
+
+ chal = plaintext_auth_context->get_ntlm_challenge(plaintext_auth_context);
+
+ if (!make_user_info_for_reply(&user_info,
+ smb_name, lp_workgroup(), chal,
+ plaintext_password)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nt_status = plaintext_auth_context->check_ntlm_password(plaintext_auth_context,
+ user_info, server_info);
+
+ (plaintext_auth_context->free)(&plaintext_auth_context);
+ free_user_info(&user_info);
+ return nt_status;
+}
+
+static NTSTATUS pass_check_smb(const char *smb_name,
+ const char *domain,
+ DATA_BLOB lm_pwd,
+ DATA_BLOB nt_pwd,
+ DATA_BLOB plaintext_password,
+ BOOL encrypted)
+
+{
+ NTSTATUS nt_status;
+ extern struct auth_context *negprot_global_auth_context;
+ auth_serversupplied_info *server_info = NULL;
+ if (encrypted) {
+ auth_usersupplied_info *user_info = NULL;
+ make_user_info_for_reply_enc(&user_info, smb_name,
+ domain,
+ lm_pwd,
+ nt_pwd);
+ nt_status = negprot_global_auth_context->check_ntlm_password(negprot_global_auth_context,
+ user_info, &server_info);
+ free_user_info(&user_info);
+ } else {
+ nt_status = check_plaintext_password(smb_name, plaintext_password, &server_info);
+ }
+ free_server_info(&server_info);
+ return nt_status;
+}
+
+/****************************************************************************
+check if a username/password pair is ok via the auth subsystem.
+return True if the password is correct, False otherwise
+****************************************************************************/
+BOOL password_ok(char *smb_name, DATA_BLOB password_blob)
+{
+
+ DATA_BLOB null_password = data_blob(NULL, 0);
+ extern BOOL global_encrypted_passwords_negotiated;
+ BOOL encrypted = (global_encrypted_passwords_negotiated && password_blob.length == 24);
+
+ if (encrypted) {
+ /*
+ * The password could be either NTLM or plain LM. Try NTLM first,
+ * but fall-through as required.
+ * NTLMv2 makes no sense here.
+ */
+ if (NT_STATUS_IS_OK(pass_check_smb(smb_name, lp_workgroup(), null_password, password_blob, null_password, encrypted))) {
+ return True;
+ }
+
+ if (NT_STATUS_IS_OK(pass_check_smb(smb_name, lp_workgroup(), password_blob, null_password, null_password, encrypted))) {
+ return True;
+ }
+ } else {
+ if (NT_STATUS_IS_OK(pass_check_smb(smb_name, lp_workgroup(), null_password, null_password, password_blob, encrypted))) {
+ return True;
+ }
+ }
+
+ return False;
+}
+
+
diff --git a/source/auth/auth_domain.c b/source/auth/auth_domain.c
new file mode 100644
index 00000000000..fdff0b52f96
--- /dev/null
+++ b/source/auth/auth_domain.c
@@ -0,0 +1,424 @@
+/*
+ Unix SMB/CIFS implementation.
+ Authenticate against a remote domain
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Andrew Bartlett 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+extern BOOL global_machine_password_needs_changing;
+
+/**
+ * Connect to a remote server for (inter)domain security authenticaion.
+ *
+ * @param cli the cli to return containing the active connection
+ * @param server either a machine name or text IP address to
+ * connect to.
+ * @param setup_creds_as domain account to setup credentials as
+ * @param sec_chan a switch value to distinguish between domain
+ * member and interdomain authentication
+ * @param trust_passwd the trust password to establish the
+ * credentials with.
+ *
+ **/
+
+static NTSTATUS connect_to_domain_password_server(struct cli_state **cli,
+ const char *domain, const char *dc_name,
+ struct in_addr dc_ip,
+ const char *setup_creds_as,
+ uint16 sec_chan,
+ const unsigned char *trust_passwd,
+ BOOL *retry)
+{
+ NTSTATUS result;
+
+ /* TODO: Send a SAMLOGON request to determine whether this is a valid
+ logonserver. We can avoid a 30-second timeout if the DC is down
+ if the SAMLOGON request fails as it is only over UDP. */
+
+ /* we use a mutex to prevent two connections at once - when a
+ Win2k PDC get two connections where one hasn't completed a
+ session setup yet it will send a TCP reset to the first
+ connection (tridge) */
+
+ /*
+ * With NT4.x DC's *all* authentication must be serialized to avoid
+ * ACCESS_DENIED errors if 2 auths are done from the same machine. JRA.
+ */
+
+ if (!grab_server_mutex(dc_name))
+ return NT_STATUS_NO_LOGON_SERVERS;
+
+ /* Attempt connection */
+ *retry = True;
+ result = cli_full_connection(cli, global_myname(), dc_name, &dc_ip, 0,
+ "IPC$", "IPC", "", "", "", 0, Undefined, retry);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ /* map to something more useful */
+ if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
+ result = NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ release_server_mutex();
+ return result;
+ }
+
+ /*
+ * We now have an anonymous connection to IPC$ on the domain password server.
+ */
+
+ /*
+ * Even if the connect succeeds we need to setup the netlogon
+ * pipe here. We do this as we may just have changed the domain
+ * account password on the PDC and yet we may be talking to
+ * a BDC that doesn't have this replicated yet. In this case
+ * a successful connect to a DC needs to take the netlogon connect
+ * into account also. This patch from "Bjart Kvarme" <bjart.kvarme@usit.uio.no>.
+ */
+
+ if(cli_nt_session_open(*cli, PI_NETLOGON) == False) {
+ DEBUG(0,("connect_to_domain_password_server: unable to open the domain client session to \
+machine %s. Error was : %s.\n", dc_name, cli_errstr(*cli)));
+ cli_nt_session_close(*cli);
+ cli_ulogoff(*cli);
+ cli_shutdown(*cli);
+ release_server_mutex();
+ return NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ fstr_sprintf((*cli)->mach_acct, "%s$", setup_creds_as);
+
+ /* This must be the remote domain (not ours) for schannel */
+
+ fstrcpy( (*cli)->domain, domain );
+
+ result = cli_nt_establish_netlogon(*cli, sec_chan, trust_passwd);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0,("connect_to_domain_password_server: unable to setup the NETLOGON credentials to machine \
+%s. Error was : %s.\n", dc_name, nt_errstr(result)));
+ cli_nt_session_close(*cli);
+ cli_ulogoff(*cli);
+ cli_shutdown(*cli);
+ release_server_mutex();
+ return result;
+ }
+
+ /* We exit here with the mutex *locked*. JRA */
+
+ return NT_STATUS_OK;
+}
+
+/***********************************************************************
+ Do the same as security=server, but using NT Domain calls and a session
+ key from the machine password. If the server parameter is specified
+ use it, otherwise figure out a server from the 'password server' param.
+************************************************************************/
+
+static NTSTATUS domain_client_validate(TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ const char *domain,
+ uchar chal[8],
+ auth_serversupplied_info **server_info,
+ const char *dc_name, struct in_addr dc_ip,
+ const char *setup_creds_as,
+ uint16 sec_chan,
+ unsigned char trust_passwd[16],
+ time_t last_change_time)
+{
+ NET_USER_INFO_3 info3;
+ struct cli_state *cli = NULL;
+ NTSTATUS nt_status = NT_STATUS_NO_LOGON_SERVERS;
+ int i;
+ BOOL retry = True;
+
+ /*
+ * At this point, smb_apasswd points to the lanman response to
+ * the challenge in local_challenge, and smb_ntpasswd points to
+ * the NT response to the challenge in local_challenge. Ship
+ * these over the secure channel to a domain controller and
+ * see if they were valid.
+ */
+
+ /* rety loop for robustness */
+
+ for (i = 0; !NT_STATUS_IS_OK(nt_status) && retry && (i < 3); i++) {
+ nt_status = connect_to_domain_password_server(&cli, domain, dc_name,
+ dc_ip, setup_creds_as, sec_chan, trust_passwd, &retry);
+ }
+
+ if ( !NT_STATUS_IS_OK(nt_status) ) {
+ DEBUG(0,("domain_client_validate: Domain password server not available.\n"));
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
+ return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
+ }
+ return nt_status;
+ }
+
+ ZERO_STRUCT(info3);
+
+ /*
+ * If this call succeeds, we now have lots of info about the user
+ * in the info3 structure.
+ */
+
+ nt_status = cli_netlogon_sam_network_logon(cli, mem_ctx,
+ NULL, user_info->smb_name.str, user_info->domain.str,
+ user_info->wksta_name.str, chal, user_info->lm_resp,
+ user_info->nt_resp, &info3);
+
+ /* let go as soon as possible so we avoid any potential deadlocks
+ with winbind lookup up users or groups */
+
+ release_server_mutex();
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0,("domain_client_validate: unable to validate password "
+ "for user %s in domain %s to Domain controller %s. "
+ "Error was %s.\n", user_info->smb_name.str,
+ user_info->domain.str, cli->srv_name_slash,
+ nt_errstr(nt_status)));
+
+ /* map to something more useful */
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
+ nt_status = NT_STATUS_NO_LOGON_SERVERS;
+ }
+ } else {
+ nt_status = make_server_info_info3(mem_ctx, user_info->internal_username.str,
+ user_info->smb_name.str, domain, server_info, &info3);
+ netsamlogon_cache_store( mem_ctx, &info3 );
+ }
+
+#if 0
+ /*
+ * We don't actually need to do this - plus it fails currently with
+ * NT_STATUS_INVALID_INFO_CLASS - we need to know *exactly* what to
+ * send here. JRA.
+ */
+
+ if (NT_STATUS_IS_OK(status)) {
+ if(cli_nt_logoff(&cli, &ctr) == False) {
+ DEBUG(0,("domain_client_validate: unable to log off user %s in domain \
+%s to Domain controller %s. Error was %s.\n", user, domain, dc_name, cli_errstr(&cli)));
+ nt_status = NT_STATUS_LOGON_FAILURE;
+ }
+ }
+#endif /* 0 */
+
+ /* Note - once the cli stream is shutdown the mem_ctx used
+ to allocate the other_sids and gids structures has been deleted - so
+ these pointers are no longer valid..... */
+
+ cli_nt_session_close(cli);
+ cli_ulogoff(cli);
+ cli_shutdown(cli);
+ return nt_status;
+}
+
+/****************************************************************************
+ Check for a valid username and password in security=domain mode.
+****************************************************************************/
+
+static NTSTATUS check_ntdomain_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
+ unsigned char trust_passwd[16];
+ time_t last_change_time;
+ const char *domain = lp_workgroup();
+ uint32 sec_channel_type = 0;
+ fstring dc_name;
+ struct in_addr dc_ip;
+
+ if ( lp_server_role() != ROLE_DOMAIN_MEMBER ) {
+ DEBUG(0,("check_ntdomain_security: Configuration error! Cannot use "
+ "ntdomain auth method when not a member of a domain.\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!user_info || !server_info || !auth_context) {
+ DEBUG(1,("check_ntdomain_security: Critical variables not present. Failing.\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /*
+ * Check that the requested domain is not our own machine name.
+ * If it is, we should never check the PDC here, we use our own local
+ * password file.
+ */
+
+ if(strequal(get_global_sam_name(), user_info->domain.str)) {
+ DEBUG(3,("check_ntdomain_security: Requested domain was for this machine.\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ /*
+ * Get the machine account password for our primary domain
+ * No need to become_root() as secrets_init() is done at startup.
+ */
+
+ if (!secrets_fetch_trust_account_password(domain, trust_passwd, &last_change_time, &sec_channel_type))
+ {
+ DEBUG(0, ("check_ntdomain_security: could not fetch trust account password for domain '%s'\n", domain));
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+
+ /* Test if machine password has expired and needs to be changed */
+ if (lp_machine_password_timeout()) {
+ if (last_change_time > 0 &&
+ time(NULL) > (last_change_time +
+ lp_machine_password_timeout())) {
+ global_machine_password_needs_changing = True;
+ }
+ }
+
+ /* we need our DC to send the net_sam_logon() request to */
+
+ if ( !get_dc_name(domain, NULL, dc_name, &dc_ip) ) {
+ DEBUG(5,("check_ntdomain_security: unable to locate a DC for domain %s\n",
+ user_info->domain.str));
+ return NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ nt_status = domain_client_validate(mem_ctx, user_info, domain,
+ (uchar *)auth_context->challenge.data, server_info, dc_name, dc_ip,
+ global_myname(), sec_channel_type,trust_passwd, last_change_time);
+
+ return nt_status;
+}
+
+/* module initialisation */
+static NTSTATUS auth_init_ntdomain(struct auth_context *auth_context, const char* param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*auth_method)->name = "ntdomain";
+ (*auth_method)->auth = check_ntdomain_security;
+ return NT_STATUS_OK;
+}
+
+
+/****************************************************************************
+ Check for a valid username and password in a trusted domain
+****************************************************************************/
+
+static NTSTATUS check_trustdomain_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
+ unsigned char trust_md4_password[16];
+ char *trust_password;
+ time_t last_change_time;
+ DOM_SID sid;
+ fstring dc_name;
+ struct in_addr dc_ip;
+
+ if (!user_info || !server_info || !auth_context) {
+ DEBUG(1,("check_trustdomain_security: Critical variables not present. Failing.\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /*
+ * Check that the requested domain is not our own machine name or domain name.
+ */
+
+ if( strequal(get_global_sam_name(), user_info->domain.str)) {
+ DEBUG(3,("check_trustdomain_security: Requested domain [%s] was for this machine.\n",
+ user_info->domain.str));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ /* No point is bothering if this is not a trusted domain.
+ This return makes "map to guest = bad user" work again.
+ The logic is that if we know nothing about the domain, that
+ user is known to us and does not exist */
+
+ if ( !is_trusted_domain( user_info->domain.str ) )
+ return NT_STATUS_NOT_IMPLEMENTED;
+
+ /*
+ * Get the trusted account password for the trusted domain
+ * No need to become_root() as secrets_init() is done at startup.
+ */
+
+ if (!secrets_fetch_trusted_domain_password(user_info->domain.str, &trust_password, &sid, &last_change_time))
+ {
+ DEBUG(0, ("check_trustdomain_security: could not fetch trust account password for domain %s\n", user_info->domain.str));
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("Trust password for domain %s is %s\n", user_info->domain.str, trust_password));
+#endif
+ E_md4hash(trust_password, trust_md4_password);
+ SAFE_FREE(trust_password);
+
+#if 0
+ /* Test if machine password is expired and need to be changed */
+ if (time(NULL) > last_change_time + lp_machine_password_timeout())
+ {
+ global_machine_password_needs_changing = True;
+ }
+#endif
+
+ /* use get_dc_name() for consistency even through we know that it will be
+ a netbios name */
+
+ if ( !get_dc_name(user_info->domain.str, NULL, dc_name, &dc_ip) ) {
+ DEBUG(5,("check_trustdomain_security: unable to locate a DC for domain %s\n",
+ user_info->domain.str));
+ return NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ nt_status = domain_client_validate(mem_ctx, user_info, user_info->domain.str,
+ (uchar *)auth_context->challenge.data, server_info, dc_name, dc_ip,
+ lp_workgroup(), SEC_CHAN_DOMAIN, trust_md4_password, last_change_time);
+
+ return nt_status;
+}
+
+/* module initialisation */
+static NTSTATUS auth_init_trustdomain(struct auth_context *auth_context, const char* param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*auth_method)->name = "trustdomain";
+ (*auth_method)->auth = check_trustdomain_security;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_domain_init(void)
+{
+ smb_register_auth(AUTH_INTERFACE_VERSION, "trustdomain", auth_init_trustdomain);
+ smb_register_auth(AUTH_INTERFACE_VERSION, "ntdomain", auth_init_ntdomain);
+ return NT_STATUS_OK;
+}
diff --git a/source/auth/auth_ntlmssp.c b/source/auth/auth_ntlmssp.c
new file mode 100644
index 00000000000..a5ce101e5e7
--- /dev/null
+++ b/source/auth/auth_ntlmssp.c
@@ -0,0 +1,200 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ handle NLTMSSP, server side
+
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Andrew Bartlett 2001-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/**
+ * Return the challenge as determined by the authentication subsystem
+ * @return an 8 byte random challenge
+ */
+
+static const uint8 *auth_ntlmssp_get_challenge(const struct ntlmssp_state *ntlmssp_state)
+{
+ AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context;
+ return auth_ntlmssp_state->auth_context->get_ntlm_challenge(auth_ntlmssp_state->auth_context);
+}
+
+/**
+ * Some authentication methods 'fix' the challenge, so we may not be able to set it
+ *
+ * @return If the effective challenge used by the auth subsystem may be modified
+ */
+static BOOL auth_ntlmssp_may_set_challenge(const struct ntlmssp_state *ntlmssp_state)
+{
+ AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context;
+ struct auth_context *auth_context = auth_ntlmssp_state->auth_context;
+
+ return auth_context->challenge_may_be_modified;
+}
+
+/**
+ * NTLM2 authentication modifies the effective challange,
+ * @param challenge The new challenge value
+ */
+static NTSTATUS auth_ntlmssp_set_challenge(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *challenge)
+{
+ AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context;
+ struct auth_context *auth_context = auth_ntlmssp_state->auth_context;
+
+ SMB_ASSERT(challenge->length == 8);
+
+ auth_context->challenge = data_blob_talloc(auth_context->mem_ctx,
+ challenge->data, challenge->length);
+
+ auth_context->challenge_set_by = "NTLMSSP callback (NTLM2)";
+
+ DEBUG(5, ("auth_context challenge set by %s\n", auth_context->challenge_set_by));
+ DEBUG(5, ("challenge is: \n"));
+ dump_data(5, (const char *)auth_context->challenge.data, auth_context->challenge.length);
+ return NT_STATUS_OK;
+}
+
+/**
+ * Check the password on an NTLMSSP login.
+ *
+ * Return the session keys used on the connection.
+ */
+
+static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *nt_session_key, DATA_BLOB *lm_session_key)
+{
+ AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context;
+ uint32 auth_flags = AUTH_FLAG_NONE;
+ auth_usersupplied_info *user_info = NULL;
+ DATA_BLOB plaintext_password = data_blob(NULL, 0);
+ NTSTATUS nt_status;
+
+ if (auth_ntlmssp_state->ntlmssp_state->lm_resp.length) {
+ auth_flags |= AUTH_FLAG_LM_RESP;
+ }
+
+ if (auth_ntlmssp_state->ntlmssp_state->nt_resp.length == 24) {
+ auth_flags |= AUTH_FLAG_NTLM_RESP;
+ } else if (auth_ntlmssp_state->ntlmssp_state->nt_resp.length > 24) {
+ auth_flags |= AUTH_FLAG_NTLMv2_RESP;
+ }
+
+ /* the client has given us its machine name (which we otherwise would not get on port 445).
+ we need to possibly reload smb.conf if smb.conf includes depend on the machine name */
+
+ set_remote_machine_name(auth_ntlmssp_state->ntlmssp_state->workstation, True);
+
+ /* setup the string used by %U */
+ /* sub_set_smb_name checks for weird internally */
+ sub_set_smb_name(auth_ntlmssp_state->ntlmssp_state->user);
+
+ reload_services(True);
+
+ nt_status = make_user_info_map(&user_info,
+ auth_ntlmssp_state->ntlmssp_state->user,
+ auth_ntlmssp_state->ntlmssp_state->domain,
+ auth_ntlmssp_state->ntlmssp_state->workstation,
+ auth_ntlmssp_state->ntlmssp_state->lm_resp,
+ auth_ntlmssp_state->ntlmssp_state->nt_resp,
+ plaintext_password,
+ auth_flags, True);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ nt_status = auth_ntlmssp_state->auth_context->check_ntlm_password(auth_ntlmssp_state->auth_context,
+ user_info, &auth_ntlmssp_state->server_info);
+
+ free_user_info(&user_info);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ if (auth_ntlmssp_state->server_info->nt_session_key.length) {
+ DEBUG(10, ("Got NT session key of length %u\n", auth_ntlmssp_state->server_info->nt_session_key.length));
+ *nt_session_key = data_blob_talloc(auth_ntlmssp_state->mem_ctx,
+ auth_ntlmssp_state->server_info->nt_session_key.data,
+ auth_ntlmssp_state->server_info->nt_session_key.length);
+ }
+ if (auth_ntlmssp_state->server_info->lm_session_key.length) {
+ DEBUG(10, ("Got LM session key of length %u\n", auth_ntlmssp_state->server_info->lm_session_key.length));
+ *lm_session_key = data_blob_talloc(auth_ntlmssp_state->mem_ctx,
+ auth_ntlmssp_state->server_info->lm_session_key.data,
+ auth_ntlmssp_state->server_info->lm_session_key.length);
+ }
+ return nt_status;
+}
+
+NTSTATUS auth_ntlmssp_start(AUTH_NTLMSSP_STATE **auth_ntlmssp_state)
+{
+ NTSTATUS nt_status;
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_init("AUTH NTLMSSP context");
+
+ *auth_ntlmssp_state = talloc_zero(mem_ctx, sizeof(**auth_ntlmssp_state));
+ if (!*auth_ntlmssp_state) {
+ DEBUG(0,("auth_ntlmssp_start: talloc failed!\n"));
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCTP(*auth_ntlmssp_state);
+
+ (*auth_ntlmssp_state)->mem_ctx = mem_ctx;
+
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_server_start(&(*auth_ntlmssp_state)->ntlmssp_state))) {
+ return nt_status;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(&(*auth_ntlmssp_state)->auth_context))) {
+ return nt_status;
+ }
+
+ (*auth_ntlmssp_state)->ntlmssp_state->auth_context = (*auth_ntlmssp_state);
+ (*auth_ntlmssp_state)->ntlmssp_state->get_challenge = auth_ntlmssp_get_challenge;
+ (*auth_ntlmssp_state)->ntlmssp_state->may_set_challenge = auth_ntlmssp_may_set_challenge;
+ (*auth_ntlmssp_state)->ntlmssp_state->set_challenge = auth_ntlmssp_set_challenge;
+ (*auth_ntlmssp_state)->ntlmssp_state->check_password = auth_ntlmssp_check_password;
+ (*auth_ntlmssp_state)->ntlmssp_state->server_role = lp_server_role();
+
+ return NT_STATUS_OK;
+}
+
+void auth_ntlmssp_end(AUTH_NTLMSSP_STATE **auth_ntlmssp_state)
+{
+ TALLOC_CTX *mem_ctx = (*auth_ntlmssp_state)->mem_ctx;
+
+ if ((*auth_ntlmssp_state)->ntlmssp_state) {
+ ntlmssp_end(&(*auth_ntlmssp_state)->ntlmssp_state);
+ }
+ if ((*auth_ntlmssp_state)->auth_context) {
+ ((*auth_ntlmssp_state)->auth_context->free)(&(*auth_ntlmssp_state)->auth_context);
+ }
+ if ((*auth_ntlmssp_state)->server_info) {
+ free_server_info(&(*auth_ntlmssp_state)->server_info);
+ }
+ talloc_destroy(mem_ctx);
+ *auth_ntlmssp_state = NULL;
+}
+
+NTSTATUS auth_ntlmssp_update(AUTH_NTLMSSP_STATE *auth_ntlmssp_state,
+ const DATA_BLOB request, DATA_BLOB *reply)
+{
+ return ntlmssp_update(auth_ntlmssp_state->ntlmssp_state, request, reply);
+}
diff --git a/source/auth/auth_rhosts.c b/source/auth/auth_rhosts.c
new file mode 100644
index 00000000000..b295df9328f
--- /dev/null
+++ b/source/auth/auth_rhosts.c
@@ -0,0 +1,257 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main SMB reply routines
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/****************************************************************************
+ Read the a hosts.equiv or .rhosts file and check if it
+ allows this user from this machine.
+****************************************************************************/
+
+static BOOL check_user_equiv(const char *user, const char *remote, const char *equiv_file)
+{
+ int plus_allowed = 1;
+ char *file_host;
+ char *file_user;
+ char **lines = file_lines_load(equiv_file, NULL);
+ int i;
+
+ DEBUG(5, ("check_user_equiv %s %s %s\n", user, remote, equiv_file));
+ if (! lines) return False;
+ for (i=0; lines[i]; i++) {
+ char *buf = lines[i];
+ trim_char(buf,' ',' ');
+
+ if (buf[0] != '#' && buf[0] != '\n')
+ {
+ BOOL is_group = False;
+ int plus = 1;
+ char *bp = buf;
+ if (strcmp(buf, "NO_PLUS\n") == 0)
+ {
+ DEBUG(6, ("check_user_equiv NO_PLUS\n"));
+ plus_allowed = 0;
+ }
+ else {
+ if (buf[0] == '+')
+ {
+ bp++;
+ if (*bp == '\n' && plus_allowed)
+ {
+ /* a bare plus means everbody allowed */
+ DEBUG(6, ("check_user_equiv everybody allowed\n"));
+ file_lines_free(lines);
+ return True;
+ }
+ }
+ else if (buf[0] == '-')
+ {
+ bp++;
+ plus = 0;
+ }
+ if (*bp == '@')
+ {
+ is_group = True;
+ bp++;
+ }
+ file_host = strtok(bp, " \t\n");
+ file_user = strtok(NULL, " \t\n");
+ DEBUG(7, ("check_user_equiv %s %s\n", file_host ? file_host : "(null)",
+ file_user ? file_user : "(null)" ));
+ if (file_host && *file_host)
+ {
+ BOOL host_ok = False;
+
+#if defined(HAVE_NETGROUP) && defined(HAVE_YP_GET_DEFAULT_DOMAIN)
+ if (is_group)
+ {
+ static char *mydomain = NULL;
+ if (!mydomain)
+ yp_get_default_domain(&mydomain);
+ if (mydomain && innetgr(file_host,remote,user,mydomain))
+ host_ok = True;
+ }
+#else
+ if (is_group)
+ {
+ DEBUG(1,("Netgroups not configured\n"));
+ continue;
+ }
+#endif
+
+ /* is it this host */
+ /* the fact that remote has come from a call of gethostbyaddr
+ * means that it may have the fully qualified domain name
+ * so we could look up the file version to get it into
+ * a canonical form, but I would rather just type it
+ * in full in the equiv file
+ */
+ if (!host_ok && !is_group && strequal(remote, file_host))
+ host_ok = True;
+
+ if (!host_ok)
+ continue;
+
+ /* is it this user */
+ if (file_user == 0 || strequal(user, file_user))
+ {
+ DEBUG(5, ("check_user_equiv matched %s%s %s\n",
+ (plus ? "+" : "-"), file_host,
+ (file_user ? file_user : "")));
+ file_lines_free(lines);
+ return (plus ? True : False);
+ }
+ }
+ }
+ }
+ }
+ file_lines_free(lines);
+ return False;
+}
+
+/****************************************************************************
+check for a possible hosts equiv or rhosts entry for the user
+****************************************************************************/
+
+static BOOL check_hosts_equiv(SAM_ACCOUNT *account)
+{
+ uid_t uid;
+ char *fname = NULL;
+
+ fname = lp_hosts_equiv();
+ if (!NT_STATUS_IS_OK(sid_to_uid(pdb_get_user_sid(account), &uid)))
+ return False;
+
+ /* note: don't allow hosts.equiv on root */
+ if (fname && *fname && uid != 0) {
+ if (check_user_equiv(pdb_get_username(account),client_name(),fname))
+ return True;
+ }
+
+ return False;
+}
+
+
+/****************************************************************************
+ Check for a valid .rhosts/hosts.equiv entry for this user
+****************************************************************************/
+
+static NTSTATUS check_hostsequiv_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ NTSTATUS nt_status;
+ SAM_ACCOUNT *account = NULL;
+ if (!NT_STATUS_IS_OK(nt_status =
+ auth_get_sam_account(user_info->internal_username.str,
+ &account))) {
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER))
+ nt_status = NT_STATUS_NOT_IMPLEMENTED;
+ return nt_status;
+ }
+
+ if (check_hosts_equiv(account)) {
+ nt_status = make_server_info_sam(server_info, account);
+ } else {
+ pdb_free_sam(&account);
+ nt_status = NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return nt_status;
+}
+
+/* module initialisation */
+static NTSTATUS auth_init_hostsequiv(struct auth_context *auth_context, const char* param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*auth_method)->auth = check_hostsequiv_security;
+ (*auth_method)->name = "hostsequiv";
+ return NT_STATUS_OK;
+}
+
+
+/****************************************************************************
+ Check for a valid .rhosts/hosts.equiv entry for this user
+****************************************************************************/
+
+static NTSTATUS check_rhosts_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ NTSTATUS nt_status;
+ SAM_ACCOUNT *account = NULL;
+ pstring rhostsfile;
+ const char *home;
+
+ if (!NT_STATUS_IS_OK(nt_status =
+ auth_get_sam_account(user_info->internal_username.str,
+ &account))) {
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER))
+ nt_status = NT_STATUS_NOT_IMPLEMENTED;
+ return nt_status;
+ }
+
+ home = pdb_get_unix_homedir(account);
+
+ if (home) {
+ slprintf(rhostsfile, sizeof(rhostsfile)-1, "%s/.rhosts", home);
+ become_root();
+ if (check_user_equiv(pdb_get_username(account),client_name(),rhostsfile)) {
+ nt_status = make_server_info_sam(server_info, account);
+ } else {
+ pdb_free_sam(&account);
+ }
+ unbecome_root();
+ } else {
+ pdb_free_sam(&account);
+ nt_status = NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return nt_status;
+}
+
+/* module initialisation */
+static NTSTATUS auth_init_rhosts(struct auth_context *auth_context, const char *param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*auth_method)->auth = check_rhosts_security;
+ (*auth_method)->name = "rhosts";
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_rhosts_init(void)
+{
+ smb_register_auth(AUTH_INTERFACE_VERSION, "rhosts", auth_init_rhosts);
+ smb_register_auth(AUTH_INTERFACE_VERSION, "hostsequiv", auth_init_hostsequiv);
+ return NT_STATUS_OK;
+}
diff --git a/source/auth/auth_sam.c b/source/auth/auth_sam.c
new file mode 100644
index 00000000000..b35cc78d6b2
--- /dev/null
+++ b/source/auth/auth_sam.c
@@ -0,0 +1,343 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Andrew Bartlett 2001-2003
+ Copyright (C) Gerald Carter 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/****************************************************************************
+ Do a specific test for an smb password being correct, given a smb_password and
+ the lanman and NT responses.
+****************************************************************************/
+
+static NTSTATUS sam_password_ok(const struct auth_context *auth_context,
+ TALLOC_CTX *mem_ctx,
+ SAM_ACCOUNT *sampass,
+ const auth_usersupplied_info *user_info,
+ DATA_BLOB *user_sess_key,
+ DATA_BLOB *lm_sess_key)
+{
+ uint16 acct_ctrl;
+ const uint8 *lm_pw, *nt_pw;
+ const char *username = pdb_get_username(sampass);
+
+ acct_ctrl = pdb_get_acct_ctrl(sampass);
+ if (acct_ctrl & ACB_PWNOTREQ) {
+ if (lp_null_passwords()) {
+ DEBUG(3,("Account for user '%s' has no password and null passwords are allowed.\n", username));
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("Account for user '%s' has no password and null passwords are NOT allowed.\n", username));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ }
+
+ lm_pw = pdb_get_lanman_passwd(sampass);
+ nt_pw = pdb_get_nt_passwd(sampass);
+
+ return ntlm_password_check(mem_ctx, &auth_context->challenge,
+ &user_info->lm_resp, &user_info->nt_resp,
+ username,
+ user_info->smb_name.str,
+ user_info->client_domain.str,
+ lm_pw, nt_pw, user_sess_key, lm_sess_key);
+}
+
+
+/****************************************************************************
+ Do a specific test for a SAM_ACCOUNT being vaild for this connection
+ (ie not disabled, expired and the like).
+****************************************************************************/
+
+static NTSTATUS sam_account_ok(TALLOC_CTX *mem_ctx,
+ SAM_ACCOUNT *sampass,
+ const auth_usersupplied_info *user_info)
+{
+ uint16 acct_ctrl = pdb_get_acct_ctrl(sampass);
+ char *workstation_list;
+ time_t kickoff_time;
+
+ DEBUG(4,("sam_account_ok: Checking SMB password for user %s\n",pdb_get_username(sampass)));
+
+ /* Quit if the account was disabled. */
+ if (acct_ctrl & ACB_DISABLED) {
+ DEBUG(1,("sam_account_ok: Account for user '%s' was disabled.\n", pdb_get_username(sampass)));
+ return NT_STATUS_ACCOUNT_DISABLED;
+ }
+
+ /* Quit if the account was locked out. */
+ if (acct_ctrl & ACB_AUTOLOCK) {
+ DEBUG(1,("sam_account_ok: Account for user %s was locked out.\n", pdb_get_username(sampass)));
+ return NT_STATUS_ACCOUNT_LOCKED_OUT;
+ }
+
+ /* Test account expire time */
+
+ kickoff_time = pdb_get_kickoff_time(sampass);
+ if (kickoff_time != 0 && time(NULL) > kickoff_time) {
+ DEBUG(1,("sam_account_ok: Account for user '%s' has expired.\n", pdb_get_username(sampass)));
+ DEBUG(3,("sam_account_ok: Account expired at '%ld' unix time.\n", (long)kickoff_time));
+ return NT_STATUS_ACCOUNT_EXPIRED;
+ }
+
+ if (!(pdb_get_acct_ctrl(sampass) & ACB_PWNOEXP)) {
+ time_t must_change_time = pdb_get_pass_must_change_time(sampass);
+ time_t last_set_time = pdb_get_pass_last_set_time(sampass);
+
+ /* check for immediate expiry "must change at next logon" */
+ if (must_change_time == 0 && last_set_time != 0) {
+ DEBUG(1,("sam_account_ok: Account for user '%s' password must change!.\n", pdb_get_username(sampass)));
+ return NT_STATUS_PASSWORD_MUST_CHANGE;
+ }
+
+ /* check for expired password */
+ if (must_change_time < time(NULL) && must_change_time != 0) {
+ DEBUG(1,("sam_account_ok: Account for user '%s' password expired!.\n", pdb_get_username(sampass)));
+ DEBUG(1,("sam_account_ok: Password expired at '%s' (%ld) unix time.\n", http_timestring(must_change_time), (long)must_change_time));
+ return NT_STATUS_PASSWORD_EXPIRED;
+ }
+ }
+
+ /* Test workstation. Workstation list is comma separated. */
+
+ workstation_list = talloc_strdup(mem_ctx, pdb_get_workstations(sampass));
+ if (!workstation_list)
+ return NT_STATUS_NO_MEMORY;
+
+ if (*workstation_list) {
+ BOOL invalid_ws = True;
+ const char *s = workstation_list;
+
+ fstring tok;
+
+ while (next_token(&s, tok, ",", sizeof(tok))) {
+ DEBUG(10,("sam_account_ok: checking for workstation match %s and %s (len=%d)\n",
+ tok, user_info->wksta_name.str, user_info->wksta_name.len));
+ if(strequal(tok, user_info->wksta_name.str)) {
+ invalid_ws = False;
+ break;
+ }
+ }
+
+ if (invalid_ws)
+ return NT_STATUS_INVALID_WORKSTATION;
+ }
+
+ if (acct_ctrl & ACB_DOMTRUST) {
+ DEBUG(2,("sam_account_ok: Domain trust account %s denied by server\n", pdb_get_username(sampass)));
+ return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
+ }
+
+ if (acct_ctrl & ACB_SVRTRUST) {
+ DEBUG(2,("sam_account_ok: Server trust account %s denied by server\n", pdb_get_username(sampass)));
+ return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
+ }
+
+ if (acct_ctrl & ACB_WSTRUST) {
+ DEBUG(4,("sam_account_ok: Wksta trust account %s denied by server\n", pdb_get_username(sampass)));
+ return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+check if a username/password is OK assuming the password is a 24 byte
+SMB hash supplied in the user_info structure
+return an NT_STATUS constant.
+****************************************************************************/
+
+static NTSTATUS check_sam_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ SAM_ACCOUNT *sampass=NULL;
+ BOOL ret;
+ NTSTATUS nt_status;
+ DATA_BLOB user_sess_key = data_blob(NULL, 0);
+ DATA_BLOB lm_sess_key = data_blob(NULL, 0);
+ BOOL updated_autolock = False, updated_badpw = False;
+
+ if (!user_info || !auth_context) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Can't use the talloc version here, because the returned struct gets
+ kept on the server_info */
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam(&sampass))) {
+ return nt_status;
+ }
+
+ /* get the account information */
+
+ become_root();
+ ret = pdb_getsampwnam(sampass, user_info->internal_username.str);
+ unbecome_root();
+
+ if (ret == False) {
+ DEBUG(3,("check_sam_security: Couldn't find user '%s' in passdb file.\n", user_info->internal_username.str));
+ pdb_free_sam(&sampass);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ /* see if autolock flag needs to be updated */
+ if (pdb_get_acct_ctrl(sampass) & ACB_NORMAL)
+ pdb_update_autolock_flag(sampass, &updated_autolock);
+ /* Quit if the account was locked out. */
+ if (pdb_get_acct_ctrl(sampass) & ACB_AUTOLOCK) {
+ DEBUG(3,("check_sam_security: Account for user %s was locked out.\n", pdb_get_username(sampass)));
+ return NT_STATUS_ACCOUNT_LOCKED_OUT;
+ }
+
+ nt_status = sam_password_ok(auth_context, mem_ctx, sampass,
+ user_info, &user_sess_key, &lm_sess_key);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ if (NT_STATUS_EQUAL(nt_status,NT_STATUS_WRONG_PASSWORD) &&
+ pdb_get_acct_ctrl(sampass) &ACB_NORMAL) {
+ pdb_increment_bad_password_count(sampass);
+ updated_badpw = True;
+ } else {
+ pdb_update_bad_password_count(sampass,
+ &updated_badpw);
+ }
+ if (updated_autolock || updated_badpw){
+ become_root();
+ if(!pdb_update_sam_account(sampass))
+ DEBUG(1, ("Failed to modify entry.\n"));
+ unbecome_root();
+ }
+ pdb_free_sam(&sampass);
+ return nt_status;
+ }
+
+ if ((pdb_get_acct_ctrl(sampass) & ACB_NORMAL) &&
+ (pdb_get_bad_password_count(sampass) > 0)){
+ pdb_set_bad_password_count(sampass, 0, PDB_CHANGED);
+ pdb_set_bad_password_time(sampass, 0, PDB_CHANGED);
+ updated_badpw = True;
+ }
+
+ if (updated_autolock || updated_badpw){
+ become_root();
+ if(!pdb_update_sam_account(sampass))
+ DEBUG(1, ("Failed to modify entry.\n"));
+ unbecome_root();
+ }
+
+ nt_status = sam_account_ok(mem_ctx, sampass, user_info);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ pdb_free_sam(&sampass);
+ return nt_status;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = make_server_info_sam(server_info, sampass))) {
+ DEBUG(0,("check_sam_security: make_server_info_sam() failed with '%s'\n", nt_errstr(nt_status)));
+ return nt_status;
+ }
+
+ (*server_info)->nt_session_key = user_sess_key;
+ (*server_info)->lm_session_key = lm_sess_key;
+
+ return nt_status;
+}
+
+/* module initialisation */
+static NTSTATUS auth_init_sam_ignoredomain(struct auth_context *auth_context, const char *param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*auth_method)->auth = check_sam_security;
+ (*auth_method)->name = "sam_ignoredomain";
+ return NT_STATUS_OK;
+}
+
+
+/****************************************************************************
+Check SAM security (above) but with a few extra checks.
+****************************************************************************/
+
+static NTSTATUS check_samstrict_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ BOOL is_local_name, is_my_domain;
+
+ if (!user_info || !auth_context) {
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ is_local_name = is_myname(user_info->domain.str);
+ is_my_domain = strequal(user_info->domain.str, lp_workgroup());
+
+ /* check whether or not we service this domain/workgroup name */
+
+ switch ( lp_server_role() ) {
+ case ROLE_STANDALONE:
+ case ROLE_DOMAIN_MEMBER:
+ if ( !is_local_name ) {
+ DEBUG(6,("check_samstrict_security: %s is not one of my local names (%s)\n",
+ user_info->domain.str, (lp_server_role() == ROLE_DOMAIN_MEMBER
+ ? "ROLE_DOMAIN_MEMBER" : "ROLE_STANDALONE") ));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ case ROLE_DOMAIN_PDC:
+ case ROLE_DOMAIN_BDC:
+ if ( !is_local_name && !is_my_domain ) {
+ DEBUG(6,("check_samstrict_security: %s is not one of my local names or domain name (DC)\n",
+ user_info->domain.str));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ default: /* name is ok */
+ break;
+ }
+
+ return check_sam_security(auth_context, my_private_data, mem_ctx, user_info, server_info);
+}
+
+/* module initialisation */
+static NTSTATUS auth_init_sam(struct auth_context *auth_context, const char *param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*auth_method)->auth = check_samstrict_security;
+ (*auth_method)->name = "sam";
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_sam_init(void)
+{
+ smb_register_auth(AUTH_INTERFACE_VERSION, "sam", auth_init_sam);
+ smb_register_auth(AUTH_INTERFACE_VERSION, "sam_ignoredomain", auth_init_sam_ignoredomain);
+ return NT_STATUS_OK;
+}
diff --git a/source/auth/auth_server.c b/source/auth/auth_server.c
new file mode 100644
index 00000000000..bc611ec229b
--- /dev/null
+++ b/source/auth/auth_server.c
@@ -0,0 +1,417 @@
+/*
+ Unix SMB/CIFS implementation.
+ Authenticate to a remote server
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Andrew Bartlett 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+extern userdom_struct current_user_info;
+
+/****************************************************************************
+ Support for server level security.
+****************************************************************************/
+
+static struct cli_state *server_cryptkey(TALLOC_CTX *mem_ctx)
+{
+ struct cli_state *cli = NULL;
+ fstring desthost;
+ struct in_addr dest_ip;
+ const char *p;
+ char *pserver;
+ BOOL connected_ok = False;
+
+ if (!(cli = cli_initialise(cli)))
+ return NULL;
+
+ /* security = server just can't function with spnego */
+ cli->use_spnego = False;
+
+ pserver = talloc_strdup(mem_ctx, lp_passwordserver());
+ p = pserver;
+
+ while(next_token( &p, desthost, LIST_SEP, sizeof(desthost))) {
+ standard_sub_basic(current_user_info.smb_name, desthost, sizeof(desthost));
+ strupper_m(desthost);
+
+ if(!resolve_name( desthost, &dest_ip, 0x20)) {
+ DEBUG(1,("server_cryptkey: Can't resolve address for %s\n",desthost));
+ continue;
+ }
+
+ if (ismyip(dest_ip)) {
+ DEBUG(1,("Password server loop - disabling password server %s\n",desthost));
+ continue;
+ }
+
+ /* we use a mutex to prevent two connections at once - when a
+ Win2k PDC get two connections where one hasn't completed a
+ session setup yet it will send a TCP reset to the first
+ connection (tridge) */
+
+ if (!grab_server_mutex(desthost)) {
+ return NULL;
+ }
+
+ if (cli_connect(cli, desthost, &dest_ip)) {
+ DEBUG(3,("connected to password server %s\n",desthost));
+ connected_ok = True;
+ break;
+ }
+ }
+
+ if (!connected_ok) {
+ release_server_mutex();
+ DEBUG(0,("password server not available\n"));
+ cli_shutdown(cli);
+ return NULL;
+ }
+
+ if (!attempt_netbios_session_request(cli, global_myname(),
+ desthost, &dest_ip)) {
+ release_server_mutex();
+ DEBUG(1,("password server fails session request\n"));
+ cli_shutdown(cli);
+ return NULL;
+ }
+
+ if (strequal(desthost,myhostname())) {
+ exit_server("Password server loop!");
+ }
+
+ DEBUG(3,("got session\n"));
+
+ if (!cli_negprot(cli)) {
+ DEBUG(1,("%s rejected the negprot\n",desthost));
+ release_server_mutex();
+ cli_shutdown(cli);
+ return NULL;
+ }
+
+ if (cli->protocol < PROTOCOL_LANMAN2 ||
+ !(cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL)) {
+ DEBUG(1,("%s isn't in user level security mode\n",desthost));
+ release_server_mutex();
+ cli_shutdown(cli);
+ return NULL;
+ }
+
+ /* Get the first session setup done quickly, to avoid silly
+ Win2k bugs. (The next connection to the server will kill
+ this one...
+ */
+
+ if (!cli_session_setup(cli, "", "", 0, "", 0,
+ "")) {
+ DEBUG(0,("%s rejected the initial session setup (%s)\n",
+ desthost, cli_errstr(cli)));
+ release_server_mutex();
+ cli_shutdown(cli);
+ return NULL;
+ }
+
+ release_server_mutex();
+
+ DEBUG(3,("password server OK\n"));
+
+ return cli;
+}
+
+/****************************************************************************
+ Clean up our allocated cli.
+****************************************************************************/
+
+static void free_server_private_data(void **private_data_pointer)
+{
+ struct cli_state **cli = (struct cli_state **)private_data_pointer;
+ if (*cli && (*cli)->initialised) {
+ DEBUG(10, ("Shutting down smbserver connection\n"));
+ cli_shutdown(*cli);
+ }
+ *private_data_pointer = NULL;
+}
+
+/****************************************************************************
+ Send a 'keepalive' packet down the cli pipe.
+****************************************************************************/
+
+static void send_server_keepalive(void **private_data_pointer)
+{
+ /* also send a keepalive to the password server if its still
+ connected */
+ if (private_data_pointer) {
+ struct cli_state *cli = (struct cli_state *)(*private_data_pointer);
+ if (cli && cli->initialised) {
+ if (!send_keepalive(cli->fd)) {
+ DEBUG( 2, ( "send_server_keepalive: password server keepalive failed.\n"));
+ cli_shutdown(cli);
+ *private_data_pointer = NULL;
+ }
+ }
+ }
+}
+
+/****************************************************************************
+ Get the challenge out of a password server.
+****************************************************************************/
+
+static DATA_BLOB auth_get_challenge_server(const struct auth_context *auth_context,
+ void **my_private_data,
+ TALLOC_CTX *mem_ctx)
+{
+ struct cli_state *cli = server_cryptkey(mem_ctx);
+
+ if (cli) {
+ DEBUG(3,("using password server validation\n"));
+
+ if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
+ /* We can't work with unencrypted password servers
+ unless 'encrypt passwords = no' */
+ DEBUG(5,("make_auth_info_server: Server is unencrypted, no challenge available..\n"));
+
+ /* However, it is still a perfectly fine connection
+ to pass that unencrypted password over */
+ *my_private_data = (void *)cli;
+ return data_blob(NULL, 0);
+
+ } else if (cli->secblob.length < 8) {
+ /* We can't do much if we don't get a full challenge */
+ DEBUG(2,("make_auth_info_server: Didn't receive a full challenge from server\n"));
+ cli_shutdown(cli);
+ return data_blob(NULL, 0);
+ }
+
+ *my_private_data = (void *)cli;
+
+ /* The return must be allocated on the caller's mem_ctx, as our own will be
+ destoyed just after the call. */
+ return data_blob_talloc(auth_context->mem_ctx, cli->secblob.data,8);
+ } else {
+ return data_blob(NULL, 0);
+ }
+}
+
+
+/****************************************************************************
+ Check for a valid username and password in security=server mode.
+ - Validate a password with the password server.
+****************************************************************************/
+
+static NTSTATUS check_smbserver_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ struct cli_state *cli;
+ static unsigned char badpass[24];
+ static fstring baduser;
+ static BOOL tested_password_server = False;
+ static BOOL bad_password_server = False;
+ NTSTATUS nt_status = NT_STATUS_NOT_IMPLEMENTED;
+ BOOL locally_made_cli = False;
+
+ /*
+ * Check that the requested domain is not our own machine name.
+ * If it is, we should never check the PDC here, we use our own local
+ * password file.
+ */
+
+ if(is_myname(user_info->domain.str)) {
+ DEBUG(3,("check_smbserver_security: Requested domain was for this machine.\n"));
+ return nt_status;
+ }
+
+ cli = my_private_data;
+
+ if (cli) {
+ } else {
+ cli = server_cryptkey(mem_ctx);
+ locally_made_cli = True;
+ }
+
+ if (!cli || !cli->initialised) {
+ DEBUG(1,("password server is not connected (cli not initilised)\n"));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
+ if (user_info->encrypted) {
+ DEBUG(1,("password server %s is plaintext, but we are encrypted. This just can't work :-(\n", cli->desthost));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ } else {
+ if (memcmp(cli->secblob.data, auth_context->challenge.data, 8) != 0) {
+ DEBUG(1,("the challenge that the password server (%s) supplied us is not the one we gave our client. This just can't work :-(\n", cli->desthost));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ }
+
+ if(badpass[0] == 0)
+ memset(badpass, 0x1f, sizeof(badpass));
+
+ if((user_info->nt_resp.length == sizeof(badpass)) &&
+ !memcmp(badpass, user_info->nt_resp.data, sizeof(badpass))) {
+ /*
+ * Very unlikely, our random bad password is the same as the users
+ * password.
+ */
+ memset(badpass, badpass[0]+1, sizeof(badpass));
+ }
+
+ if(baduser[0] == 0) {
+ fstrcpy(baduser, INVALID_USER_PREFIX);
+ fstrcat(baduser, global_myname());
+ }
+
+ /*
+ * Attempt a session setup with a totally incorrect password.
+ * If this succeeds with the guest bit *NOT* set then the password
+ * server is broken and is not correctly setting the guest bit. We
+ * need to detect this as some versions of NT4.x are broken. JRA.
+ */
+
+ /* I sure as hell hope that there aren't servers out there that take
+ * NTLMv2 and have this bug, as we don't test for that...
+ * - abartlet@samba.org
+ */
+
+ if ((!tested_password_server) && (lp_paranoid_server_security())) {
+ if (cli_session_setup(cli, baduser, (char *)badpass, sizeof(badpass),
+ (char *)badpass, sizeof(badpass), user_info->domain.str)) {
+
+ /*
+ * We connected to the password server so we
+ * can say we've tested it.
+ */
+ tested_password_server = True;
+
+ if ((SVAL(cli->inbuf,smb_vwv2) & 1) == 0) {
+ DEBUG(0,("server_validate: password server %s allows users as non-guest \
+with a bad password.\n", cli->desthost));
+ DEBUG(0,("server_validate: This is broken (and insecure) behaviour. Please do not \
+use this machine as the password server.\n"));
+ cli_ulogoff(cli);
+
+ /*
+ * Password server has the bug.
+ */
+ bad_password_server = True;
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ cli_ulogoff(cli);
+ }
+ } else {
+
+ /*
+ * We have already tested the password server.
+ * Fail immediately if it has the bug.
+ */
+
+ if(bad_password_server) {
+ DEBUG(0,("server_validate: [1] password server %s allows users as non-guest \
+with a bad password.\n", cli->desthost));
+ DEBUG(0,("server_validate: [1] This is broken (and insecure) behaviour. Please do not \
+use this machine as the password server.\n"));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ }
+
+ /*
+ * Now we know the password server will correctly set the guest bit, or is
+ * not guest enabled, we can try with the real password.
+ */
+
+ if (!user_info->encrypted) {
+ /* Plaintext available */
+ if (!cli_session_setup(cli, user_info->smb_name.str,
+ (char *)user_info->plaintext_password.data,
+ user_info->plaintext_password.length,
+ NULL, 0,
+ user_info->domain.str)) {
+ DEBUG(1,("password server %s rejected the password\n", cli->desthost));
+ /* Make this cli_nt_error() when the conversion is in */
+ nt_status = cli_nt_error(cli);
+ } else {
+ nt_status = NT_STATUS_OK;
+ }
+ } else {
+ if (!cli_session_setup(cli, user_info->smb_name.str,
+ (char *)user_info->lm_resp.data,
+ user_info->lm_resp.length,
+ (char *)user_info->nt_resp.data,
+ user_info->nt_resp.length,
+ user_info->domain.str)) {
+ DEBUG(1,("password server %s rejected the password\n", cli->desthost));
+ /* Make this cli_nt_error() when the conversion is in */
+ nt_status = cli_nt_error(cli);
+ } else {
+ nt_status = NT_STATUS_OK;
+ }
+ }
+
+ /* if logged in as guest then reject */
+ if ((SVAL(cli->inbuf,smb_vwv2) & 1) != 0) {
+ DEBUG(1,("password server %s gave us guest only\n", cli->desthost));
+ nt_status = NT_STATUS_LOGON_FAILURE;
+ }
+
+ cli_ulogoff(cli);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ fstring real_username;
+ struct passwd *pass;
+
+ if ( (pass = smb_getpwnam( user_info->internal_username.str,
+ real_username, True )) != NULL )
+ {
+ nt_status = make_server_info_pw(server_info, pass->pw_name, pass);
+ }
+ else
+ {
+ nt_status = NT_STATUS_NO_SUCH_USER;
+ }
+ }
+
+ if (locally_made_cli) {
+ cli_shutdown(cli);
+ }
+
+ return(nt_status);
+}
+
+static NTSTATUS auth_init_smbserver(struct auth_context *auth_context, const char* param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ (*auth_method)->name = "smbserver";
+ (*auth_method)->auth = check_smbserver_security;
+ (*auth_method)->get_chal = auth_get_challenge_server;
+ (*auth_method)->send_keepalive = send_server_keepalive;
+ (*auth_method)->free_private_data = free_server_private_data;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_server_init(void)
+{
+ return smb_register_auth(AUTH_INTERFACE_VERSION, "smbserver", auth_init_smbserver);
+}
diff --git a/source/auth/auth_unix.c b/source/auth/auth_unix.c
new file mode 100644
index 00000000000..f744cba0c43
--- /dev/null
+++ b/source/auth/auth_unix.c
@@ -0,0 +1,136 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/**
+ * update the encrypted smbpasswd file from the plaintext username and password
+ *
+ * this ugly hack needs to die, but not quite yet, I think people still use it...
+ **/
+static BOOL update_smbpassword_file(const char *user, const char *password)
+{
+ SAM_ACCOUNT *sampass = NULL;
+ BOOL ret;
+
+ pdb_init_sam(&sampass);
+
+ become_root();
+ ret = pdb_getsampwnam(sampass, user);
+ unbecome_root();
+
+ if(ret == False) {
+ DEBUG(0,("pdb_getsampwnam returned NULL\n"));
+ pdb_free_sam(&sampass);
+ return False;
+ }
+
+ /*
+ * Remove the account disabled flag - we are updating the
+ * users password from a login.
+ */
+ if (!pdb_set_acct_ctrl(sampass, pdb_get_acct_ctrl(sampass) & ~ACB_DISABLED, PDB_CHANGED)) {
+ pdb_free_sam(&sampass);
+ return False;
+ }
+
+ if (!pdb_set_plaintext_passwd (sampass, password)) {
+ pdb_free_sam(&sampass);
+ return False;
+ }
+
+ /* Now write it into the file. */
+ become_root();
+
+ ret = pdb_update_sam_account (sampass);
+
+ unbecome_root();
+
+ if (ret) {
+ DEBUG(3,("pdb_update_sam_account returned %d\n",ret));
+ }
+
+ pdb_free_sam(&sampass);
+ return ret;
+}
+
+
+/** Check a plaintext username/password
+ *
+ * Cannot deal with an encrupted password in any manner whatsoever,
+ * unless the account has a null password.
+ **/
+
+static NTSTATUS check_unix_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ NTSTATUS nt_status;
+ struct passwd *pass = NULL;
+
+ become_root();
+ pass = Get_Pwnam(user_info->internal_username.str);
+
+
+ /** @todo This call assumes a ASCII password, no charset transformation is
+ done. We may need to revisit this **/
+ nt_status = pass_check(pass,
+ pass ? pass->pw_name : user_info->internal_username.str,
+ (char *)user_info->plaintext_password.data,
+ user_info->plaintext_password.length-1,
+ lp_update_encrypted() ?
+ update_smbpassword_file : NULL,
+ True);
+
+ unbecome_root();
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ if (pass) {
+ make_server_info_pw(server_info, pass->pw_name, pass);
+ } else {
+ /* we need to do somthing more useful here */
+ nt_status = NT_STATUS_NO_SUCH_USER;
+ }
+ }
+
+ return nt_status;
+}
+
+/* module initialisation */
+static NTSTATUS auth_init_unix(struct auth_context *auth_context, const char* param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*auth_method)->name = "unix";
+ (*auth_method)->auth = check_unix_security;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_unix_init(void)
+{
+ return smb_register_auth(AUTH_INTERFACE_VERSION, "unix", auth_init_unix);
+}
diff --git a/source/auth/auth_util.c b/source/auth/auth_util.c
new file mode 100644
index 00000000000..a823991bcad
--- /dev/null
+++ b/source/auth/auth_util.c
@@ -0,0 +1,1500 @@
+/*
+ Unix SMB/CIFS implementation.
+ Authentication utility functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Andrew Bartlett 2001
+ Copyright (C) Jeremy Allison 2000-2001
+ Copyright (C) Rafal Szczesniak 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+extern DOM_SID global_sid_World;
+extern DOM_SID global_sid_Network;
+extern DOM_SID global_sid_Builtin_Guests;
+extern DOM_SID global_sid_Authenticated_Users;
+
+
+/****************************************************************************
+ Create a UNIX user on demand.
+****************************************************************************/
+
+static int smb_create_user(const char *domain, const char *unix_username, const char *homedir)
+{
+ pstring add_script;
+ int ret;
+
+ pstrcpy(add_script, lp_adduser_script());
+ if (! *add_script)
+ return -1;
+ all_string_sub(add_script, "%u", unix_username, sizeof(pstring));
+ if (domain)
+ all_string_sub(add_script, "%D", domain, sizeof(pstring));
+ if (homedir)
+ all_string_sub(add_script, "%H", homedir, sizeof(pstring));
+ ret = smbrun(add_script,NULL);
+ DEBUG(3,("smb_create_user: Running the command `%s' gave %d\n",add_script,ret));
+ return ret;
+}
+
+/****************************************************************************
+ Add and Delete UNIX users on demand, based on NTSTATUS codes.
+ We don't care about RID's here so ignore.
+****************************************************************************/
+
+void auth_add_user_script(const char *domain, const char *username)
+{
+ uint32 rid;
+ /*
+ * User validated ok against Domain controller.
+ * If the admin wants us to try and create a UNIX
+ * user on the fly, do so.
+ */
+
+ if ( *lp_adduser_script() )
+ smb_create_user(domain, username, NULL);
+ else {
+ DEBUG(10,("auth_add_user_script: no 'add user script'. Asking winbindd\n"));
+
+ /* should never get here is we a re a domain member running winbindd
+ However, a host set for 'security = server' might run winbindd for
+ account allocation */
+
+ if ( !winbind_create_user(username, NULL) ) {
+ DEBUG(5,("auth_add_user_script: winbindd_create_user() failed\n"));
+ rid = 0;
+ }
+ }
+}
+
+/****************************************************************************
+ Create a SAM_ACCOUNT - either by looking in the pdb, or by faking it up from
+ unix info.
+****************************************************************************/
+
+NTSTATUS auth_get_sam_account(const char *user, SAM_ACCOUNT **account)
+{
+ BOOL pdb_ret;
+ NTSTATUS nt_status;
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam(account))) {
+ return nt_status;
+ }
+
+ become_root();
+ pdb_ret = pdb_getsampwnam(*account, user);
+ unbecome_root();
+
+ if (!pdb_ret) {
+
+ struct passwd *pass = Get_Pwnam(user);
+ if (!pass)
+ return NT_STATUS_NO_SUCH_USER;
+
+ if (!NT_STATUS_IS_OK(nt_status = pdb_fill_sam_pw(*account, pass))) {
+ return nt_status;
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Create an auth_usersupplied_data structure
+****************************************************************************/
+
+static NTSTATUS make_user_info(auth_usersupplied_info **user_info,
+ const char *smb_name,
+ const char *internal_username,
+ const char *client_domain,
+ const char *domain,
+ const char *wksta_name,
+ DATA_BLOB lm_pwd, DATA_BLOB nt_pwd,
+ DATA_BLOB plaintext,
+ uint32 auth_flags, BOOL encrypted)
+{
+
+ DEBUG(5,("attempting to make a user_info for %s (%s)\n", internal_username, smb_name));
+
+ *user_info = malloc(sizeof(**user_info));
+ if (!user_info) {
+ DEBUG(0,("malloc failed for user_info (size %lu)\n", (unsigned long)sizeof(*user_info)));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCTP(*user_info);
+
+ DEBUG(5,("making strings for %s's user_info struct\n", internal_username));
+
+ (*user_info)->smb_name.str = strdup(smb_name);
+ if ((*user_info)->smb_name.str) {
+ (*user_info)->smb_name.len = strlen(smb_name);
+ } else {
+ free_user_info(user_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*user_info)->internal_username.str = strdup(internal_username);
+ if ((*user_info)->internal_username.str) {
+ (*user_info)->internal_username.len = strlen(internal_username);
+ } else {
+ free_user_info(user_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*user_info)->domain.str = strdup(domain);
+ if ((*user_info)->domain.str) {
+ (*user_info)->domain.len = strlen(domain);
+ } else {
+ free_user_info(user_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*user_info)->client_domain.str = strdup(client_domain);
+ if ((*user_info)->client_domain.str) {
+ (*user_info)->client_domain.len = strlen(client_domain);
+ } else {
+ free_user_info(user_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*user_info)->wksta_name.str = strdup(wksta_name);
+ if ((*user_info)->wksta_name.str) {
+ (*user_info)->wksta_name.len = strlen(wksta_name);
+ } else {
+ free_user_info(user_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DEBUG(5,("making blobs for %s's user_info struct\n", internal_username));
+
+ (*user_info)->lm_resp = data_blob(lm_pwd.data, lm_pwd.length);
+ (*user_info)->nt_resp = data_blob(nt_pwd.data, nt_pwd.length);
+ (*user_info)->plaintext_password = data_blob(plaintext.data, plaintext.length);
+
+ (*user_info)->encrypted = encrypted;
+ (*user_info)->auth_flags = auth_flags;
+
+ DEBUG(10,("made an %sencrypted user_info for %s (%s)\n", encrypted ? "":"un" , internal_username, smb_name));
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Create an auth_usersupplied_data structure after appropriate mapping.
+****************************************************************************/
+
+NTSTATUS make_user_info_map(auth_usersupplied_info **user_info,
+ const char *smb_name,
+ const char *client_domain,
+ const char *wksta_name,
+ DATA_BLOB lm_pwd, DATA_BLOB nt_pwd,
+ DATA_BLOB plaintext,
+ uint32 ntlmssp_flags, BOOL encrypted)
+{
+ const char *domain;
+ fstring internal_username;
+ fstrcpy(internal_username, smb_name);
+ map_username(internal_username);
+
+ DEBUG(5, ("make_user_info_map: Mapping user [%s]\\[%s] from workstation [%s]\n",
+ client_domain, smb_name, wksta_name));
+
+ /* don't allow "" as a domain, fixes a Win9X bug
+ where it doens't supply a domain for logon script
+ 'net use' commands. */
+
+ if ( *client_domain )
+ domain = client_domain;
+ else
+ domain = lp_workgroup();
+
+ /* do what win2k does. Always map unknown domains to our own
+ and let the "passdb backend" handle unknown users. */
+
+ if ( !is_trusted_domain(domain) && !strequal(domain, get_global_sam_name()) )
+ domain = get_default_sam_name();
+
+ /* we know that it is a trusted domain (and we are allowing them) or it is our domain */
+
+ return make_user_info(user_info, smb_name, internal_username,
+ client_domain, domain, wksta_name, lm_pwd, nt_pwd,
+ plaintext, ntlmssp_flags, encrypted);
+}
+
+/****************************************************************************
+ Create an auth_usersupplied_data, making the DATA_BLOBs here.
+ Decrypt and encrypt the passwords.
+****************************************************************************/
+
+BOOL make_user_info_netlogon_network(auth_usersupplied_info **user_info,
+ const char *smb_name,
+ const char *client_domain,
+ const char *wksta_name,
+ const uchar *lm_network_pwd, int lm_pwd_len,
+ const uchar *nt_network_pwd, int nt_pwd_len)
+{
+ BOOL ret;
+ NTSTATUS nt_status;
+ DATA_BLOB lm_blob = data_blob(lm_network_pwd, lm_pwd_len);
+ DATA_BLOB nt_blob = data_blob(nt_network_pwd, nt_pwd_len);
+ DATA_BLOB plaintext_blob = data_blob(NULL, 0);
+ uint32 auth_flags = AUTH_FLAG_NONE;
+
+ if (lm_pwd_len)
+ auth_flags |= AUTH_FLAG_LM_RESP;
+ if (nt_pwd_len == 24) {
+ auth_flags |= AUTH_FLAG_NTLM_RESP;
+ } else if (nt_pwd_len != 0) {
+ auth_flags |= AUTH_FLAG_NTLMv2_RESP;
+ }
+
+ nt_status = make_user_info_map(user_info,
+ smb_name, client_domain,
+ wksta_name,
+ lm_blob, nt_blob,
+ plaintext_blob,
+ auth_flags, True);
+
+ ret = NT_STATUS_IS_OK(nt_status) ? True : False;
+
+ data_blob_free(&lm_blob);
+ data_blob_free(&nt_blob);
+ return ret;
+}
+
+/****************************************************************************
+ Create an auth_usersupplied_data, making the DATA_BLOBs here.
+ Decrypt and encrypt the passwords.
+****************************************************************************/
+
+BOOL make_user_info_netlogon_interactive(auth_usersupplied_info **user_info,
+ const char *smb_name,
+ const char *client_domain,
+ const char *wksta_name,
+ const uchar chal[8],
+ const uchar lm_interactive_pwd[16],
+ const uchar nt_interactive_pwd[16],
+ const uchar *dc_sess_key)
+{
+ char lm_pwd[16];
+ char nt_pwd[16];
+ unsigned char local_lm_response[24];
+ unsigned char local_nt_response[24];
+ unsigned char key[16];
+ uint32 auth_flags = AUTH_FLAG_NONE;
+
+ ZERO_STRUCT(key);
+ memcpy(key, dc_sess_key, 8);
+
+ if (lm_interactive_pwd) memcpy(lm_pwd, lm_interactive_pwd, sizeof(lm_pwd));
+ if (nt_interactive_pwd) memcpy(nt_pwd, nt_interactive_pwd, sizeof(nt_pwd));
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("key:"));
+ dump_data(100, (char *)key, sizeof(key));
+
+ DEBUG(100,("lm owf password:"));
+ dump_data(100, lm_pwd, sizeof(lm_pwd));
+
+ DEBUG(100,("nt owf password:"));
+ dump_data(100, nt_pwd, sizeof(nt_pwd));
+#endif
+
+ SamOEMhash((uchar *)lm_pwd, key, sizeof(lm_pwd));
+ SamOEMhash((uchar *)nt_pwd, key, sizeof(nt_pwd));
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("decrypt of lm owf password:"));
+ dump_data(100, lm_pwd, sizeof(lm_pwd));
+
+ DEBUG(100,("decrypt of nt owf password:"));
+ dump_data(100, nt_pwd, sizeof(nt_pwd));
+#endif
+
+ SMBOWFencrypt((const unsigned char *)lm_pwd, chal, local_lm_response);
+ SMBOWFencrypt((const unsigned char *)nt_pwd, chal, local_nt_response);
+
+ /* Password info paranoia */
+ ZERO_STRUCT(lm_pwd);
+ ZERO_STRUCT(nt_pwd);
+ ZERO_STRUCT(key);
+
+ {
+ BOOL ret;
+ NTSTATUS nt_status;
+ DATA_BLOB local_lm_blob = data_blob(local_lm_response, sizeof(local_lm_response));
+ DATA_BLOB local_nt_blob = data_blob(local_nt_response, sizeof(local_nt_response));
+ DATA_BLOB plaintext_blob = data_blob(NULL, 0);
+
+ if (lm_interactive_pwd)
+ auth_flags |= AUTH_FLAG_LM_RESP;
+ if (nt_interactive_pwd)
+ auth_flags |= AUTH_FLAG_NTLM_RESP;
+
+ nt_status = make_user_info_map(user_info,
+ smb_name, client_domain,
+ wksta_name,
+ local_lm_blob,
+ local_nt_blob,
+ plaintext_blob,
+ auth_flags, True);
+
+ ret = NT_STATUS_IS_OK(nt_status) ? True : False;
+ data_blob_free(&local_lm_blob);
+ data_blob_free(&local_nt_blob);
+ return ret;
+ }
+}
+
+
+/****************************************************************************
+ Create an auth_usersupplied_data structure
+****************************************************************************/
+
+BOOL make_user_info_for_reply(auth_usersupplied_info **user_info,
+ const char *smb_name,
+ const char *client_domain,
+ const uint8 chal[8],
+ DATA_BLOB plaintext_password)
+{
+
+ DATA_BLOB local_lm_blob;
+ DATA_BLOB local_nt_blob;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ uint32 auth_flags = AUTH_FLAG_NONE;
+
+ /*
+ * Not encrypted - do so.
+ */
+
+ DEBUG(5,("make_user_info_for_reply: User passwords not in encrypted format.\n"));
+
+ if (plaintext_password.data) {
+ unsigned char local_lm_response[24];
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(10,("Unencrypted password (len %d):\n",plaintext_password.length));
+ dump_data(100, plaintext_password.data, plaintext_password.length);
+#endif
+
+ SMBencrypt( (const char *)plaintext_password.data, (const uchar*)chal, local_lm_response);
+ local_lm_blob = data_blob(local_lm_response, 24);
+
+ /* We can't do an NT hash here, as the password needs to be
+ case insensitive */
+ local_nt_blob = data_blob(NULL, 0);
+
+ auth_flags = (AUTH_FLAG_PLAINTEXT | AUTH_FLAG_LM_RESP);
+ } else {
+ local_lm_blob = data_blob(NULL, 0);
+ local_nt_blob = data_blob(NULL, 0);
+ }
+
+ ret = make_user_info_map(user_info, smb_name,
+ client_domain,
+ get_remote_machine_name(),
+ local_lm_blob,
+ local_nt_blob,
+ plaintext_password,
+ auth_flags, False);
+
+ data_blob_free(&local_lm_blob);
+ return NT_STATUS_IS_OK(ret) ? True : False;
+}
+
+/****************************************************************************
+ Create an auth_usersupplied_data structure
+****************************************************************************/
+
+NTSTATUS make_user_info_for_reply_enc(auth_usersupplied_info **user_info,
+ const char *smb_name,
+ const char *client_domain,
+ DATA_BLOB lm_resp, DATA_BLOB nt_resp)
+{
+ uint32 auth_flags = AUTH_FLAG_NONE;
+
+ DATA_BLOB no_plaintext_blob = data_blob(NULL, 0);
+
+ if (lm_resp.length == 24) {
+ auth_flags |= AUTH_FLAG_LM_RESP;
+ }
+ if (nt_resp.length == 0) {
+ } else if (nt_resp.length == 24) {
+ auth_flags |= AUTH_FLAG_NTLM_RESP;
+ } else {
+ auth_flags |= AUTH_FLAG_NTLMv2_RESP;
+ }
+
+ return make_user_info_map(user_info, smb_name,
+ client_domain,
+ get_remote_machine_name(),
+ lm_resp,
+ nt_resp,
+ no_plaintext_blob,
+ auth_flags, True);
+}
+
+/****************************************************************************
+ Create a guest user_info blob, for anonymous authenticaion.
+****************************************************************************/
+
+BOOL make_user_info_guest(auth_usersupplied_info **user_info)
+{
+ DATA_BLOB lm_blob = data_blob(NULL, 0);
+ DATA_BLOB nt_blob = data_blob(NULL, 0);
+ DATA_BLOB plaintext_blob = data_blob(NULL, 0);
+ uint32 auth_flags = AUTH_FLAG_NONE;
+ NTSTATUS nt_status;
+
+ nt_status = make_user_info(user_info,
+ "","",
+ "","",
+ "",
+ nt_blob, lm_blob,
+ plaintext_blob,
+ auth_flags, True);
+
+ return NT_STATUS_IS_OK(nt_status) ? True : False;
+}
+
+/****************************************************************************
+ prints a NT_USER_TOKEN to debug output.
+****************************************************************************/
+
+void debug_nt_user_token(int dbg_class, int dbg_lev, NT_USER_TOKEN *token)
+{
+ fstring sid_str;
+ size_t i;
+
+ if (!token) {
+ DEBUGC(dbg_class, dbg_lev, ("NT user token: (NULL)\n"));
+ return;
+ }
+
+ DEBUGC(dbg_class, dbg_lev, ("NT user token of user %s\n",
+ sid_to_string(sid_str, &token->user_sids[0]) ));
+ DEBUGADDC(dbg_class, dbg_lev, ("contains %lu SIDs\n", (unsigned long)token->num_sids));
+ for (i = 0; i < token->num_sids; i++)
+ DEBUGADDC(dbg_class, dbg_lev, ("SID[%3lu]: %s\n", (unsigned long)i,
+ sid_to_string(sid_str, &token->user_sids[i])));
+}
+
+/****************************************************************************
+ prints a UNIX 'token' to debug output.
+****************************************************************************/
+
+void debug_unix_user_token(int dbg_class, int dbg_lev, uid_t uid, gid_t gid, int n_groups, gid_t *groups)
+{
+ int i;
+ DEBUGC(dbg_class, dbg_lev, ("UNIX token of user %ld\n", (long int)uid));
+
+ DEBUGADDC(dbg_class, dbg_lev, ("Primary group is %ld and contains %i supplementary groups\n", (long int)gid, n_groups));
+ for (i = 0; i < n_groups; i++)
+ DEBUGADDC(dbg_class, dbg_lev, ("Group[%3i]: %ld\n", i,
+ (long int)groups[i]));
+}
+
+/****************************************************************************
+ Create the SID list for this user.
+****************************************************************************/
+
+static NTSTATUS create_nt_user_token(const DOM_SID *user_sid, const DOM_SID *group_sid,
+ int n_groupSIDs, DOM_SID *groupSIDs,
+ BOOL is_guest, NT_USER_TOKEN **token)
+{
+ NTSTATUS nt_status = NT_STATUS_OK;
+ NT_USER_TOKEN *ptoken;
+ int i;
+ int sid_ndx;
+
+ if ((ptoken = malloc( sizeof(NT_USER_TOKEN) ) ) == NULL) {
+ DEBUG(0, ("create_nt_token: Out of memory allocating token\n"));
+ nt_status = NT_STATUS_NO_MEMORY;
+ return nt_status;
+ }
+
+ ZERO_STRUCTP(ptoken);
+
+ ptoken->num_sids = n_groupSIDs + 5;
+
+ if ((ptoken->user_sids = (DOM_SID *)malloc( sizeof(DOM_SID) * ptoken->num_sids )) == NULL) {
+ DEBUG(0, ("create_nt_token: Out of memory allocating SIDs\n"));
+ nt_status = NT_STATUS_NO_MEMORY;
+ return nt_status;
+ }
+
+ memset((char*)ptoken->user_sids,0,sizeof(DOM_SID) * ptoken->num_sids);
+
+ /*
+ * Note - user SID *MUST* be first in token !
+ * se_access_check depends on this.
+ *
+ * Primary group SID is second in token. Convention.
+ */
+
+ sid_copy(&ptoken->user_sids[PRIMARY_USER_SID_INDEX], user_sid);
+ if (group_sid)
+ sid_copy(&ptoken->user_sids[PRIMARY_GROUP_SID_INDEX], group_sid);
+
+ /*
+ * Finally add the "standard" SIDs.
+ * The only difference between guest and "anonymous" (which we
+ * don't really support) is the addition of Authenticated_Users.
+ */
+
+ sid_copy(&ptoken->user_sids[2], &global_sid_World);
+ sid_copy(&ptoken->user_sids[3], &global_sid_Network);
+
+ if (is_guest)
+ sid_copy(&ptoken->user_sids[4], &global_sid_Builtin_Guests);
+ else
+ sid_copy(&ptoken->user_sids[4], &global_sid_Authenticated_Users);
+
+ sid_ndx = 5; /* next available spot */
+
+ for (i = 0; i < n_groupSIDs; i++) {
+ size_t check_sid_idx;
+ for (check_sid_idx = 1; check_sid_idx < ptoken->num_sids; check_sid_idx++) {
+ if (sid_equal(&ptoken->user_sids[check_sid_idx],
+ &groupSIDs[i])) {
+ break;
+ }
+ }
+
+ if (check_sid_idx >= ptoken->num_sids) /* Not found already */ {
+ sid_copy(&ptoken->user_sids[sid_ndx++], &groupSIDs[i]);
+ } else {
+ ptoken->num_sids--;
+ }
+ }
+
+ debug_nt_user_token(DBGC_AUTH, 10, ptoken);
+
+ *token = ptoken;
+
+ return nt_status;
+}
+
+/****************************************************************************
+ Create the SID list for this user.
+****************************************************************************/
+
+NT_USER_TOKEN *create_nt_token(uid_t uid, gid_t gid, int ngroups, gid_t *groups, BOOL is_guest)
+{
+ DOM_SID user_sid;
+ DOM_SID group_sid;
+ DOM_SID *group_sids;
+ NT_USER_TOKEN *token;
+ int i;
+
+ if (!NT_STATUS_IS_OK(uid_to_sid(&user_sid, uid))) {
+ return NULL;
+ }
+ if (!NT_STATUS_IS_OK(gid_to_sid(&group_sid, gid))) {
+ return NULL;
+ }
+
+ group_sids = malloc(sizeof(DOM_SID) * ngroups);
+ if (!group_sids) {
+ DEBUG(0, ("create_nt_token: malloc() failed for DOM_SID list!\n"));
+ return NULL;
+ }
+
+ for (i = 0; i < ngroups; i++) {
+ if (!NT_STATUS_IS_OK(gid_to_sid(&(group_sids)[i], (groups)[i]))) {
+ DEBUG(1, ("create_nt_token: failed to convert gid %ld to a sid!\n", (long int)groups[i]));
+ SAFE_FREE(group_sids);
+ return NULL;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(create_nt_user_token(&user_sid, &group_sid,
+ ngroups, group_sids, is_guest, &token))) {
+ SAFE_FREE(group_sids);
+ return NULL;
+ }
+
+ SAFE_FREE(group_sids);
+
+ return token;
+}
+
+/******************************************************************************
+ * this function returns the groups (SIDs) of the local SAM the user is in.
+ * If this samba server is a DC of the domain the user belongs to, it returns
+ * both domain groups and local / builtin groups. If the user is in a trusted
+ * domain, or samba is a member server of a domain, then this function returns
+ * local and builtin groups the user is a member of.
+ *
+ * currently this is a hack, as there is no sam implementation that is capable
+ * of groups.
+ *
+ * NOTE!! This function will fail if you pass in a winbind user without
+ * the domain --jerry
+ ******************************************************************************/
+
+static NTSTATUS get_user_groups(const char *username, uid_t uid, gid_t gid,
+ int *n_groups, DOM_SID **groups, gid_t **unix_groups)
+{
+ int n_unix_groups;
+ int i;
+
+ *n_groups = 0;
+ *groups = NULL;
+
+ /* Try winbind first */
+
+ if ( strchr(username, *lp_winbind_separator()) ) {
+ n_unix_groups = winbind_getgroups( username, unix_groups );
+
+ DEBUG(10,("get_user_groups: winbind_getgroups(%s): result = %s\n", username,
+ n_unix_groups == -1 ? "FAIL" : "SUCCESS"));
+
+ if ( n_unix_groups == -1 )
+ return NT_STATUS_NO_SUCH_USER; /* what should this return value be? */
+ }
+ else {
+ /* fallback to getgrouplist() */
+
+ n_unix_groups = groups_max();
+
+ if ((*unix_groups = malloc( sizeof(gid_t) * n_unix_groups ) ) == NULL) {
+ DEBUG(0, ("get_user_groups: Out of memory allocating unix group list\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (sys_getgrouplist(username, gid, *unix_groups, &n_unix_groups) == -1) {
+
+ gid_t *groups_tmp;
+
+ groups_tmp = Realloc(*unix_groups, sizeof(gid_t) * n_unix_groups);
+
+ if (!groups_tmp) {
+ SAFE_FREE(*unix_groups);
+ return NT_STATUS_NO_MEMORY;
+ }
+ *unix_groups = groups_tmp;
+
+ if (sys_getgrouplist(username, gid, *unix_groups, &n_unix_groups) == -1) {
+ DEBUG(0, ("get_user_groups: failed to get the unix group list\n"));
+ SAFE_FREE(*unix_groups);
+ return NT_STATUS_NO_SUCH_USER; /* what should this return value be? */
+ }
+ }
+ }
+
+ debug_unix_user_token(DBGC_CLASS, 5, uid, gid, n_unix_groups, *unix_groups);
+
+ /* now setup the space for storing the SIDS */
+
+ if (n_unix_groups > 0) {
+
+ *groups = malloc(sizeof(DOM_SID) * n_unix_groups);
+
+ if (!*groups) {
+ DEBUG(0, ("get_user_group: malloc() failed for DOM_SID list!\n"));
+ SAFE_FREE(*unix_groups);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ *n_groups = n_unix_groups;
+
+ for (i = 0; i < *n_groups; i++) {
+ if (!NT_STATUS_IS_OK(gid_to_sid(&(*groups)[i], (*unix_groups)[i]))) {
+ DEBUG(1, ("get_user_groups: failed to convert gid %ld to a sid!\n",
+ (long int)(*unix_groups)[i+1]));
+ SAFE_FREE(*groups);
+ SAFE_FREE(*unix_groups);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Make a user_info struct
+***************************************************************************/
+
+static NTSTATUS make_server_info(auth_serversupplied_info **server_info)
+{
+ *server_info = malloc(sizeof(**server_info));
+ if (!*server_info) {
+ DEBUG(0,("make_server_info: malloc failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ ZERO_STRUCTP(*server_info);
+
+ /* Initialise the uid and gid values to something non-zero
+ which may save us from giving away root access if there
+ is a bug in allocating these fields. */
+
+ (*server_info)->uid = -1;
+ (*server_info)->gid = -1;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+Fill a server_info struct from a SAM_ACCOUNT with their groups
+***************************************************************************/
+
+static NTSTATUS add_user_groups(auth_serversupplied_info **server_info,
+ const char * unix_username,
+ SAM_ACCOUNT *sampass,
+ uid_t uid, gid_t gid)
+{
+ NTSTATUS nt_status;
+ const DOM_SID *user_sid = pdb_get_user_sid(sampass);
+ const DOM_SID *group_sid = pdb_get_group_sid(sampass);
+ int n_groupSIDs = 0;
+ DOM_SID *groupSIDs = NULL;
+ gid_t *unix_groups = NULL;
+ NT_USER_TOKEN *token;
+ BOOL is_guest;
+ uint32 rid;
+
+ nt_status = get_user_groups(unix_username, uid, gid,
+ &n_groupSIDs, &groupSIDs, &unix_groups);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(4,("get_user_groups_from_local_sam failed\n"));
+ free_server_info(server_info);
+ return nt_status;
+ }
+
+ is_guest = (sid_peek_rid(user_sid, &rid) && rid == DOMAIN_USER_RID_GUEST);
+
+ if (!NT_STATUS_IS_OK(nt_status = create_nt_user_token(user_sid, group_sid,
+ n_groupSIDs, groupSIDs, is_guest,
+ &token)))
+ {
+ DEBUG(4,("create_nt_user_token failed\n"));
+ SAFE_FREE(groupSIDs);
+ SAFE_FREE(unix_groups);
+ free_server_info(server_info);
+ return nt_status;
+ }
+
+ SAFE_FREE(groupSIDs);
+
+ (*server_info)->n_groups = n_groupSIDs;
+ (*server_info)->groups = unix_groups;
+ (*server_info)->ptok = token;
+
+ return nt_status;
+}
+
+/***************************************************************************
+Fill a server_info struct from a SAM_ACCOUNT with its privileges
+***************************************************************************/
+
+static NTSTATUS add_privileges(auth_serversupplied_info **server_info)
+{
+ PRIVILEGE_SET *privs = NULL;
+
+ init_privilege(&privs);
+ if (!pdb_get_privilege_set((*server_info)->ptok->user_sids, (*server_info)->ptok->num_sids, privs))
+ DEBUG(1, ("Could not add privileges\n"));
+
+ (*server_info)->privs = privs;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Make (and fill) a user_info struct from a SAM_ACCOUNT
+***************************************************************************/
+
+NTSTATUS make_server_info_sam(auth_serversupplied_info **server_info,
+ SAM_ACCOUNT *sampass)
+{
+ NTSTATUS nt_status;
+ struct passwd *pwd;
+
+ if (!NT_STATUS_IS_OK(nt_status = make_server_info(server_info)))
+ return nt_status;
+
+ (*server_info)->sam_account = sampass;
+
+ if ( !(pwd = getpwnam_alloc(pdb_get_username(sampass))) ) {
+ DEBUG(1, ("User %s in passdb, but getpwnam() fails!\n",
+ pdb_get_username(sampass)));
+ free_server_info(server_info);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+ (*server_info)->unix_name = smb_xstrdup(pwd->pw_name);
+ (*server_info)->gid = pwd->pw_gid;
+ (*server_info)->uid = pwd->pw_uid;
+
+ passwd_free(&pwd);
+
+ if (!NT_STATUS_IS_OK(nt_status = add_user_groups(server_info, pdb_get_username(sampass),
+ sampass,
+ (*server_info)->uid,
+ (*server_info)->gid)))
+ {
+ free_server_info(server_info);
+ return nt_status;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = add_privileges(server_info))) {
+ free_server_info(server_info);
+ return nt_status;
+ }
+
+ (*server_info)->sam_fill_level = SAM_FILL_ALL;
+ DEBUG(5,("make_server_info_sam: made server info for user %s -> %s\n",
+ pdb_get_username(sampass),
+ (*server_info)->unix_name));
+
+ return nt_status;
+}
+
+/***************************************************************************
+ Make (and fill) a user_info struct from a 'struct passwd' by conversion
+ to a SAM_ACCOUNT
+***************************************************************************/
+
+NTSTATUS make_server_info_pw(auth_serversupplied_info **server_info,
+ char *unix_username,
+ struct passwd *pwd)
+{
+ NTSTATUS nt_status;
+ SAM_ACCOUNT *sampass = NULL;
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam_pw(&sampass, pwd))) {
+ return nt_status;
+ }
+ if (!NT_STATUS_IS_OK(nt_status = make_server_info(server_info))) {
+ return nt_status;
+ }
+
+ (*server_info)->sam_account = sampass;
+
+ if (!NT_STATUS_IS_OK(nt_status = add_user_groups(server_info, unix_username,
+ sampass, pwd->pw_uid, pwd->pw_gid)))
+ {
+ return nt_status;
+ }
+
+ (*server_info)->unix_name = smb_xstrdup(unix_username);
+
+ (*server_info)->sam_fill_level = SAM_FILL_ALL;
+ (*server_info)->uid = pwd->pw_uid;
+ (*server_info)->gid = pwd->pw_gid;
+ return nt_status;
+}
+
+/***************************************************************************
+ Make (and fill) a user_info struct for a guest login.
+***************************************************************************/
+
+NTSTATUS make_server_info_guest(auth_serversupplied_info **server_info)
+{
+ NTSTATUS nt_status;
+ SAM_ACCOUNT *sampass = NULL;
+ DOM_SID guest_sid;
+
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam(&sampass))) {
+ return nt_status;
+ }
+
+ sid_copy(&guest_sid, get_global_sam_sid());
+ sid_append_rid(&guest_sid, DOMAIN_USER_RID_GUEST);
+
+ become_root();
+ if (!pdb_getsampwsid(sampass, &guest_sid)) {
+ unbecome_root();
+ return NT_STATUS_NO_SUCH_USER;
+ }
+ unbecome_root();
+
+ nt_status = make_server_info_sam(server_info, sampass);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ static const char zeros[16];
+ (*server_info)->guest = True;
+
+ /* annoying, but the Guest really does have a session key,
+ and it is all zeros! */
+ (*server_info)->nt_session_key = data_blob(zeros, sizeof(zeros));
+ (*server_info)->lm_session_key = data_blob(zeros, sizeof(zeros));
+ }
+
+ return nt_status;
+}
+
+/***************************************************************************
+ Purely internal function for make_server_info_info3
+ Fill the sam account from getpwnam
+***************************************************************************/
+static NTSTATUS fill_sam_account(TALLOC_CTX *mem_ctx,
+ const char *domain,
+ const char *username,
+ char **found_username,
+ uid_t *uid, gid_t *gid,
+ SAM_ACCOUNT **sam_account)
+{
+ fstring dom_user;
+ fstring real_username;
+ struct passwd *passwd;
+
+ fstr_sprintf(dom_user, "%s%s%s", domain, lp_winbind_separator(),
+ username);
+
+ /* get the passwd struct but don't create the user if he/she
+ does not exist. We were explicitly called from a following
+ a winbindd authentication request so we should assume that
+ nss_winbindd is working */
+
+ if ( !(passwd = smb_getpwnam( dom_user, real_username, True )) )
+ return NT_STATUS_NO_SUCH_USER;
+
+ *uid = passwd->pw_uid;
+ *gid = passwd->pw_gid;
+
+ /* This is pointless -- there is no suport for differing
+ unix and windows names. Make sure to always store the
+ one we actually looked up and succeeded. Have I mentioned
+ why I hate the 'winbind use default domain' parameter?
+ --jerry */
+
+ *found_username = talloc_strdup( mem_ctx, real_username );
+
+ DEBUG(5,("fill_sam_account: located username was [%s]\n",
+ *found_username));
+
+ return pdb_init_sam_pw(sam_account, passwd);
+}
+
+/****************************************************************************
+ Wrapper to allow the getpwnam() call to strip the domain name and
+ try again in case a local UNIX user is already there. Also run through
+ the username if we fallback to the username only.
+ ****************************************************************************/
+
+struct passwd *smb_getpwnam( char *domuser, fstring save_username, BOOL create )
+{
+ struct passwd *pw = NULL;
+ char *p;
+ fstring mapped_username;
+ fstring strip_username;
+
+ /* we only save a copy of the username it has been mangled
+ by winbindd use default domain */
+
+ save_username[0] = '\0';
+
+ /* save a local copy of the username and run it through the
+ username map */
+
+ fstrcpy( mapped_username, domuser );
+ map_username( mapped_username );
+
+ p = strchr_m( mapped_username, *lp_winbind_separator() );
+
+ /* code for a DOMAIN\user string */
+
+ if ( p ) {
+ pw = Get_Pwnam( domuser );
+ if ( pw ) {
+ /* make sure we get the case of the username correct */
+ /* work around 'winbind use default domain = yes' */
+
+ if ( !strchr_m( pw->pw_name, *lp_winbind_separator() ) ) {
+ char *domain;
+
+ domain = mapped_username;
+ *p = '\0';
+ fstr_sprintf(save_username, "%s%c%s", domain, *lp_winbind_separator(), pw->pw_name);
+ }
+ else
+ fstrcpy( save_username, pw->pw_name );
+
+ /* whew -- done! */
+ return pw;
+ }
+
+ /* setup for lookup of just the username */
+ /* remember that p and mapped_username are overlapping memory */
+
+ p++;
+ fstrcpy( strip_username, p );
+ fstrcpy( mapped_username, strip_username );
+ }
+
+ /* just lookup a plain username */
+
+ pw = Get_Pwnam(mapped_username);
+
+ /* Create local user if requested. */
+
+ if ( !pw && create ) {
+ /* Don't add a machine account. */
+ if (mapped_username[strlen(mapped_username)-1] == '$')
+ return NULL;
+
+ auth_add_user_script(NULL, mapped_username);
+ pw = Get_Pwnam(mapped_username);
+ }
+
+ /* one last check for a valid passwd struct */
+
+ if ( pw )
+ fstrcpy( save_username, pw->pw_name );
+
+ return pw;
+}
+
+/***************************************************************************
+ Make a server_info struct from the info3 returned by a domain logon
+***************************************************************************/
+
+NTSTATUS make_server_info_info3(TALLOC_CTX *mem_ctx,
+ const char *internal_username,
+ const char *sent_nt_username,
+ const char *domain,
+ auth_serversupplied_info **server_info,
+ NET_USER_INFO_3 *info3)
+{
+ static const char zeros[16];
+
+ NTSTATUS nt_status = NT_STATUS_OK;
+ char *found_username;
+ const char *nt_domain;
+ const char *nt_username;
+
+ SAM_ACCOUNT *sam_account = NULL;
+ DOM_SID user_sid;
+ DOM_SID group_sid;
+
+ uid_t uid;
+ gid_t gid;
+
+ int n_lgroupSIDs;
+ DOM_SID *lgroupSIDs = NULL;
+
+ gid_t *unix_groups = NULL;
+ NT_USER_TOKEN *token;
+
+ DOM_SID *all_group_SIDs;
+ size_t i;
+
+ /*
+ Here is where we should check the list of
+ trusted domains, and verify that the SID
+ matches.
+ */
+
+ sid_copy(&user_sid, &info3->dom_sid.sid);
+ if (!sid_append_rid(&user_sid, info3->user_rid)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ sid_copy(&group_sid, &info3->dom_sid.sid);
+ if (!sid_append_rid(&group_sid, info3->group_rid)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!(nt_username = unistr2_tdup(mem_ctx, &(info3->uni_user_name)))) {
+ /* If the server didn't give us one, just use the one we sent them */
+ nt_username = sent_nt_username;
+ }
+
+ if (!(nt_domain = unistr2_tdup(mem_ctx, &(info3->uni_logon_dom)))) {
+ /* If the server didn't give us one, just use the one we sent them */
+ nt_domain = domain;
+ }
+
+ /* try to fill the SAM account.. If getpwnam() fails, then try the
+ add user script (2.2.x behavior) */
+
+ nt_status = fill_sam_account(mem_ctx, nt_domain, internal_username,
+ &found_username, &uid, &gid, &sam_account);
+
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER)) {
+ DEBUG(3,("User %s does not exist, trying to add it\n",
+ internal_username));
+ auth_add_user_script(nt_domain, internal_username);
+ nt_status = fill_sam_account(mem_ctx, nt_domain,
+ internal_username, &found_username,
+ &uid, &gid, &sam_account);
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("make_server_info_info3: pdb_init_sam failed!\n"));
+ return nt_status;
+ }
+
+ if (!pdb_set_nt_username(sam_account, nt_username, PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_username(sam_account, nt_username, PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_domain(sam_account, nt_domain, PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_user_sid(sam_account, &user_sid, PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!pdb_set_group_sid(sam_account, &group_sid, PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!pdb_set_fullname(sam_account, unistr2_static(&(info3->uni_full_name)),
+ PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_logon_script(sam_account, unistr2_static(&(info3->uni_logon_script)), PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_profile_path(sam_account, unistr2_static(&(info3->uni_profile_path)), PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_homedir(sam_account, unistr2_static(&(info3->uni_home_dir)), PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_dir_drive(sam_account, unistr2_static(&(info3->uni_dir_drive)), PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = make_server_info(server_info))) {
+ DEBUG(4, ("make_server_info failed!\n"));
+ pdb_free_sam(&sam_account);
+ return nt_status;
+ }
+
+ /* save this here to _net_sam_logon() doesn't fail (it assumes a
+ valid SAM_ACCOUNT) */
+
+ (*server_info)->sam_account = sam_account;
+
+ (*server_info)->unix_name = smb_xstrdup(found_username);
+
+ /* Fill in the unix info we found on the way */
+
+ (*server_info)->sam_fill_level = SAM_FILL_ALL;
+ (*server_info)->uid = uid;
+ (*server_info)->gid = gid;
+
+ /* Store the user group information in the server_info
+ returned to the caller. */
+
+ nt_status = get_user_groups((*server_info)->unix_name,
+ uid, gid, &n_lgroupSIDs, &lgroupSIDs, &unix_groups);
+
+ if ( !NT_STATUS_IS_OK(nt_status) ) {
+ DEBUG(4,("get_user_groups failed\n"));
+ return nt_status;
+ }
+
+ (*server_info)->groups = unix_groups;
+ (*server_info)->n_groups = n_lgroupSIDs;
+
+ /* Create a 'combined' list of all SIDs we might want in the SD */
+
+ all_group_SIDs = malloc(sizeof(DOM_SID) * (info3->num_groups2 +info3->num_other_sids));
+
+ if (!all_group_SIDs) {
+ DEBUG(0, ("malloc() failed for DOM_SID list!\n"));
+ SAFE_FREE(lgroupSIDs);
+ free_server_info(server_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+#if 0 /* JERRY -- no such thing as local groups in current code */
+ /* Copy the 'local' sids */
+ memcpy(all_group_SIDs, lgroupSIDs, sizeof(DOM_SID) * n_lgroupSIDs);
+ SAFE_FREE(lgroupSIDs);
+#endif
+
+ /* and create (by appending rids) the 'domain' sids */
+
+ for (i = 0; i < info3->num_groups2; i++) {
+
+ sid_copy(&all_group_SIDs[i], &(info3->dom_sid.sid));
+
+ if (!sid_append_rid(&all_group_SIDs[i], info3->gids[i].g_rid)) {
+
+ nt_status = NT_STATUS_INVALID_PARAMETER;
+
+ DEBUG(3,("could not append additional group rid 0x%x\n",
+ info3->gids[i].g_rid));
+
+ SAFE_FREE(lgroupSIDs);
+ free_server_info(server_info);
+
+ return nt_status;
+
+ }
+ }
+
+ /* Copy 'other' sids. We need to do sid filtering here to
+ prevent possible elevation of privileges. See:
+
+ http://www.microsoft.com/windows2000/techinfo/administration/security/sidfilter.asp
+ */
+
+ for (i = 0; i < info3->num_other_sids; i++) {
+ sid_copy(&all_group_SIDs[info3->num_groups2 + i],
+ &info3->other_sids[i].sid);
+ }
+
+ /* Where are the 'global' sids... */
+
+ /* can the user be guest? if yes, where is it stored? */
+
+ nt_status = create_nt_user_token(&user_sid, &group_sid,
+ info3->num_groups2 + info3->num_other_sids,
+ all_group_SIDs, False, &token);
+
+ if ( !NT_STATUS_IS_OK(nt_status) ) {
+ DEBUG(4,("create_nt_user_token failed\n"));
+ SAFE_FREE(all_group_SIDs);
+ free_server_info(server_info);
+ return nt_status;
+ }
+
+ (*server_info)->ptok = token;
+
+ SAFE_FREE(all_group_SIDs);
+
+ /* ensure we are never given NULL session keys */
+
+ if (memcmp(info3->user_sess_key, zeros, sizeof(zeros)) == 0) {
+ (*server_info)->nt_session_key = data_blob(NULL, 0);
+ } else {
+ (*server_info)->nt_session_key = data_blob(info3->user_sess_key, sizeof(info3->user_sess_key));
+ }
+
+ if (memcmp(info3->padding, zeros, sizeof(zeros)) == 0) {
+ (*server_info)->lm_session_key = data_blob(NULL, 0);
+ } else {
+ (*server_info)->lm_session_key = data_blob(info3->padding, 16);
+ }
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Free a user_info struct
+***************************************************************************/
+
+void free_user_info(auth_usersupplied_info **user_info)
+{
+ DEBUG(5,("attempting to free (and zero) a user_info structure\n"));
+ if (*user_info != NULL) {
+ if ((*user_info)->smb_name.str) {
+ DEBUG(10,("structure was created for %s\n", (*user_info)->smb_name.str));
+ }
+ SAFE_FREE((*user_info)->smb_name.str);
+ SAFE_FREE((*user_info)->internal_username.str);
+ SAFE_FREE((*user_info)->client_domain.str);
+ SAFE_FREE((*user_info)->domain.str);
+ SAFE_FREE((*user_info)->wksta_name.str);
+ data_blob_free(&(*user_info)->lm_resp);
+ data_blob_free(&(*user_info)->nt_resp);
+ SAFE_FREE((*user_info)->interactive_password);
+ data_blob_clear_free(&(*user_info)->plaintext_password);
+ ZERO_STRUCT(**user_info);
+ }
+ SAFE_FREE(*user_info);
+}
+
+/***************************************************************************
+ Clear out a server_info struct that has been allocated
+***************************************************************************/
+
+void free_server_info(auth_serversupplied_info **server_info)
+{
+ DEBUG(5,("attempting to free (and zero) a server_info structure\n"));
+ if (*server_info != NULL) {
+ pdb_free_sam(&(*server_info)->sam_account);
+
+ /* call pam_end here, unless we know we are keeping it */
+ delete_nt_token( &(*server_info)->ptok );
+ SAFE_FREE((*server_info)->groups);
+ SAFE_FREE((*server_info)->unix_name);
+ data_blob_free(&(*server_info)->lm_session_key);
+ data_blob_free(&(*server_info)->nt_session_key);
+ ZERO_STRUCT(**server_info);
+ }
+ SAFE_FREE(*server_info);
+}
+
+/***************************************************************************
+ Make an auth_methods struct
+***************************************************************************/
+
+BOOL make_auth_methods(struct auth_context *auth_context, auth_methods **auth_method)
+{
+ if (!auth_context) {
+ smb_panic("no auth_context supplied to make_auth_methods()!\n");
+ }
+
+ if (!auth_method) {
+ smb_panic("make_auth_methods: pointer to auth_method pointer is NULL!\n");
+ }
+
+ *auth_method = talloc(auth_context->mem_ctx, sizeof(**auth_method));
+ if (!*auth_method) {
+ DEBUG(0,("make_auth_method: malloc failed!\n"));
+ return False;
+ }
+ ZERO_STRUCTP(*auth_method);
+
+ return True;
+}
+
+/****************************************************************************
+ Delete a SID token.
+****************************************************************************/
+
+void delete_nt_token(NT_USER_TOKEN **pptoken)
+{
+ if (*pptoken) {
+ NT_USER_TOKEN *ptoken = *pptoken;
+ SAFE_FREE( ptoken->user_sids );
+ ZERO_STRUCTP(ptoken);
+ }
+ SAFE_FREE(*pptoken);
+}
+
+/****************************************************************************
+ Duplicate a SID token.
+****************************************************************************/
+
+NT_USER_TOKEN *dup_nt_token(NT_USER_TOKEN *ptoken)
+{
+ NT_USER_TOKEN *token;
+
+ if (!ptoken)
+ return NULL;
+
+ if ((token = (NT_USER_TOKEN *)malloc( sizeof(NT_USER_TOKEN) ) ) == NULL)
+ return NULL;
+
+ ZERO_STRUCTP(token);
+
+ if ((token->user_sids = (DOM_SID *)memdup( ptoken->user_sids, sizeof(DOM_SID) * ptoken->num_sids )) == NULL) {
+ SAFE_FREE(token);
+ return NULL;
+ }
+
+ token->num_sids = ptoken->num_sids;
+
+ return token;
+}
+
+/**
+ * Squash an NT_STATUS in line with security requirements.
+ * In an attempt to avoid giving the whole game away when users
+ * are authenticating, NT replaces both NT_STATUS_NO_SUCH_USER and
+ * NT_STATUS_WRONG_PASSWORD with NT_STATUS_LOGON_FAILURE in certain situations
+ * (session setups in particular).
+ *
+ * @param nt_status NTSTATUS input for squashing.
+ * @return the 'squashed' nt_status
+ **/
+
+NTSTATUS nt_status_squash(NTSTATUS nt_status)
+{
+ if NT_STATUS_IS_OK(nt_status) {
+ return nt_status;
+ } else if NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER) {
+ /* Match WinXP and don't give the game away */
+ return NT_STATUS_LOGON_FAILURE;
+
+ } else if NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD) {
+ /* Match WinXP and don't give the game away */
+ return NT_STATUS_LOGON_FAILURE;
+ } else {
+ return nt_status;
+ }
+}
+
+
+/**
+ * Verify whether or not given domain is trusted.
+ *
+ * @param domain_name name of the domain to be verified
+ * @return true if domain is one of the trusted once or
+ * false if otherwise
+ **/
+
+BOOL is_trusted_domain(const char* dom_name)
+{
+ DOM_SID trustdom_sid;
+ char *pass = NULL;
+ time_t lct;
+ BOOL ret;
+
+ /* no trusted domains for a standalone server */
+
+ if ( lp_server_role() == ROLE_STANDALONE )
+ return False;
+
+ /* if we are a DC, then check for a direct trust relationships */
+
+ if (lp_server_role() == ROLE_DOMAIN_BDC || lp_server_role() == ROLE_DOMAIN_PDC) {
+ become_root();
+ ret = secrets_fetch_trusted_domain_password(dom_name, &pass, &trustdom_sid, &lct);
+ unbecome_root();
+ SAFE_FREE(pass);
+ if (ret)
+ return True;
+ }
+ else {
+ /* if winbindd is not up and we are a domain member) then we need to update the
+ trustdom_cache ourselves */
+
+ if ( !winbind_ping() )
+ update_trustdom_cache();
+ }
+
+ /* now the trustdom cache should be available a DC could still
+ * have a transitive trust so fall back to the cache of trusted
+ * domains (like a domain member would use */
+
+ if ( trustdom_cache_fetch(dom_name, &trustdom_sid) ) {
+ return True;
+ }
+
+ return False;
+}
+
diff --git a/source/auth/auth_winbind.c b/source/auth/auth_winbind.c
new file mode 100644
index 00000000000..0e2820313e3
--- /dev/null
+++ b/source/auth/auth_winbind.c
@@ -0,0 +1,171 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind authentication mechnism
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Andrew Bartlett 2001 - 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+static NTSTATUS get_info3_from_ndr(TALLOC_CTX *mem_ctx, struct winbindd_response *response, NET_USER_INFO_3 *info3)
+{
+ uint8 *info3_ndr;
+ size_t len = response->length - sizeof(struct winbindd_response);
+ prs_struct ps;
+ if (len > 0) {
+ info3_ndr = response->extra_data;
+ if (!prs_init(&ps, len, mem_ctx, UNMARSHALL)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ prs_copy_data_in(&ps, (char *)info3_ndr, len);
+ prs_set_offset(&ps,0);
+ if (!net_io_user_info3("", info3, &ps, 1, 3)) {
+ DEBUG(2, ("get_info3_from_ndr: could not parse info3 struct!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ prs_mem_free(&ps);
+
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(2, ("get_info3_from_ndr: No info3 struct found!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+}
+
+/* Authenticate a user with a challenge/response */
+
+static NTSTATUS check_winbind_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ NTSTATUS nt_status;
+ NET_USER_INFO_3 info3;
+
+ if (!user_info) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!auth_context) {
+ DEBUG(3,("Password for user %s cannot be checked because we have no auth_info to get the challenge from.\n",
+ user_info->internal_username.str));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (strequal(user_info->domain.str, get_global_sam_name())) {
+ DEBUG(3,("check_winbind_security: Not using winbind, requested domain [%s] was for this SAM.\n",
+ user_info->domain.str));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.flags = WBFLAG_PAM_INFO3_NDR;
+
+ push_utf8_fstring(request.data.auth_crap.user,
+ user_info->smb_name.str);
+ push_utf8_fstring(request.data.auth_crap.domain,
+ user_info->domain.str);
+ push_utf8_fstring(request.data.auth_crap.workstation,
+ user_info->wksta_name.str);
+
+ memcpy(request.data.auth_crap.chal, auth_context->challenge.data, sizeof(request.data.auth_crap.chal));
+
+ request.data.auth_crap.lm_resp_len = MIN(user_info->lm_resp.length,
+ sizeof(request.data.auth_crap.lm_resp));
+ request.data.auth_crap.nt_resp_len = MIN(user_info->nt_resp.length,
+ sizeof(request.data.auth_crap.nt_resp));
+
+ memcpy(request.data.auth_crap.lm_resp, user_info->lm_resp.data,
+ request.data.auth_crap.lm_resp_len);
+ memcpy(request.data.auth_crap.nt_resp, user_info->nt_resp.data,
+ request.data.auth_crap.nt_resp_len);
+
+ /* we are contacting the privileged pipe */
+ become_root();
+ result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response);
+ unbecome_root();
+
+ if ( result == NSS_STATUS_UNAVAIL ) {
+ struct auth_methods *auth_method = my_private_data;
+
+ if ( auth_method )
+ return auth_method->auth(auth_context, auth_method->private_data,
+ mem_ctx, user_info, server_info);
+ else
+ /* log an error since this should not happen */
+ DEBUG(0,("check_winbind_security: ERROR! my_private_data == NULL!\n"));
+ }
+
+ nt_status = NT_STATUS(response.data.auth.nt_status);
+
+ if (result == NSS_STATUS_SUCCESS && response.extra_data) {
+ if (NT_STATUS_IS_OK(nt_status)) {
+
+ if (NT_STATUS_IS_OK(nt_status = get_info3_from_ndr(mem_ctx, &response, &info3)))
+ {
+ nt_status = make_server_info_info3(mem_ctx,
+ user_info->internal_username.str,
+ user_info->smb_name.str, user_info->domain.str,
+ server_info, &info3);
+ }
+
+ }
+ } else if (NT_STATUS_IS_OK(nt_status)) {
+ nt_status = NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ return nt_status;
+}
+
+/* module initialisation */
+static NTSTATUS auth_init_winbind(struct auth_context *auth_context, const char *param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*auth_method)->name = "winbind";
+ (*auth_method)->auth = check_winbind_security;
+
+ if (param && *param) {
+ /* we load the 'fallback' module - if winbind isn't here, call this
+ module */
+ if (!load_auth_module(auth_context, param, (auth_methods **)&(*auth_method)->private_data)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ }
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_winbind_init(void)
+{
+ return smb_register_auth(AUTH_INTERFACE_VERSION, "winbind", auth_init_winbind);
+}
diff --git a/source/auth/pampass.c b/source/auth/pampass.c
new file mode 100644
index 00000000000..3239686a20d
--- /dev/null
+++ b/source/auth/pampass.c
@@ -0,0 +1,875 @@
+/*
+ Unix SMB/CIFS implementation.
+ PAM Password checking
+ Copyright (C) Andrew Tridgell 1992-2001
+ Copyright (C) John H Terpsta 1999-2001
+ Copyright (C) Andrew Bartlett 2001
+ Copyright (C) Jeremy Allison 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ * This module provides PAM based functions for validation of
+ * username/password pairs, account managment, session and access control.
+ * Note: SMB password checking is done in smbpass.c
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+#ifdef WITH_PAM
+
+/*******************************************************************
+ * Handle PAM authentication
+ * - Access, Authentication, Session, Password
+ * Note: See PAM Documentation and refer to local system PAM implementation
+ * which determines what actions/limitations/allowances become affected.
+ *********************************************************************/
+
+#include <security/pam_appl.h>
+
+/*
+ * Structure used to communicate between the conversation function
+ * and the server_login/change password functions.
+ */
+
+struct smb_pam_userdata {
+ const char *PAM_username;
+ const char *PAM_password;
+ const char *PAM_newpassword;
+};
+
+typedef int (*smb_pam_conv_fn)(int, const struct pam_message **, struct pam_response **, void *appdata_ptr);
+
+/*
+ * Macros to help make life easy
+ */
+#define COPY_STRING(s) (s) ? strdup(s) : NULL
+
+/*******************************************************************
+ PAM error handler.
+ *********************************************************************/
+
+static BOOL smb_pam_error_handler(pam_handle_t *pamh, int pam_error, const char *msg, int dbglvl)
+{
+
+ if( pam_error != PAM_SUCCESS) {
+ DEBUG(dbglvl, ("smb_pam_error_handler: PAM: %s : %s\n",
+ msg, pam_strerror(pamh, pam_error)));
+
+ return False;
+ }
+ return True;
+}
+
+/*******************************************************************
+ This function is a sanity check, to make sure that we NEVER report
+ failure as sucess.
+*********************************************************************/
+
+static BOOL smb_pam_nt_status_error_handler(pam_handle_t *pamh, int pam_error,
+ const char *msg, int dbglvl,
+ NTSTATUS *nt_status)
+{
+ *nt_status = pam_to_nt_status(pam_error);
+
+ if (smb_pam_error_handler(pamh, pam_error, msg, dbglvl))
+ return True;
+
+ if (NT_STATUS_IS_OK(*nt_status)) {
+ /* Complain LOUDLY */
+ DEBUG(0, ("smb_pam_nt_status_error_handler: PAM: BUG: PAM and NT_STATUS \
+error MISMATCH, forcing to NT_STATUS_LOGON_FAILURE"));
+ *nt_status = NT_STATUS_LOGON_FAILURE;
+ }
+ return False;
+}
+
+/*
+ * PAM conversation function
+ * Here we assume (for now, at least) that echo on means login name, and
+ * echo off means password.
+ */
+
+static int smb_pam_conv(int num_msg,
+ const struct pam_message **msg,
+ struct pam_response **resp,
+ void *appdata_ptr)
+{
+ int replies = 0;
+ struct pam_response *reply = NULL;
+ struct smb_pam_userdata *udp = (struct smb_pam_userdata *)appdata_ptr;
+
+ *resp = NULL;
+
+ if (num_msg <= 0)
+ return PAM_CONV_ERR;
+
+ /*
+ * Apparantly HPUX has a buggy PAM that doesn't support the
+ * appdata_ptr. Fail if this is the case. JRA.
+ */
+
+ if (udp == NULL) {
+ DEBUG(0,("smb_pam_conv: PAM on this system is broken - appdata_ptr == NULL !\n"));
+ return PAM_CONV_ERR;
+ }
+
+ reply = malloc(sizeof(struct pam_response) * num_msg);
+ if (!reply)
+ return PAM_CONV_ERR;
+
+ memset(reply, '\0', sizeof(struct pam_response) * num_msg);
+
+ for (replies = 0; replies < num_msg; replies++) {
+ switch (msg[replies]->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies].resp = COPY_STRING(udp->PAM_username);
+ /* PAM frees resp */
+ break;
+
+ case PAM_PROMPT_ECHO_OFF:
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies].resp = COPY_STRING(udp->PAM_password);
+ /* PAM frees resp */
+ break;
+
+ case PAM_TEXT_INFO:
+ /* fall through */
+
+ case PAM_ERROR_MSG:
+ /* ignore it... */
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies].resp = NULL;
+ break;
+
+ default:
+ /* Must be an error of some sort... */
+ SAFE_FREE(reply);
+ return PAM_CONV_ERR;
+ }
+ }
+ if (reply)
+ *resp = reply;
+ return PAM_SUCCESS;
+}
+
+/*
+ * PAM password change conversation function
+ * Here we assume (for now, at least) that echo on means login name, and
+ * echo off means password.
+ */
+
+static void special_char_sub(char *buf)
+{
+ all_string_sub(buf, "\\n", "", 0);
+ all_string_sub(buf, "\\r", "", 0);
+ all_string_sub(buf, "\\s", " ", 0);
+ all_string_sub(buf, "\\t", "\t", 0);
+}
+
+static void pwd_sub(char *buf, const char *username, const char *oldpass, const char *newpass)
+{
+ fstring_sub(buf, "%u", username);
+ all_string_sub(buf, "%o", oldpass, sizeof(fstring));
+ all_string_sub(buf, "%n", newpass, sizeof(fstring));
+}
+
+
+struct chat_struct {
+ struct chat_struct *next, *prev;
+ fstring prompt;
+ fstring reply;
+};
+
+/**************************************************************
+ Create a linked list containing chat data.
+***************************************************************/
+
+static struct chat_struct *make_pw_chat(char *p)
+{
+ fstring prompt;
+ fstring reply;
+ struct chat_struct *list = NULL;
+ struct chat_struct *t;
+ struct chat_struct *tmp;
+
+ while (1) {
+ t = (struct chat_struct *)malloc(sizeof(*t));
+ if (!t) {
+ DEBUG(0,("make_pw_chat: malloc failed!\n"));
+ return NULL;
+ }
+
+ ZERO_STRUCTP(t);
+
+ DLIST_ADD_END(list, t, tmp);
+
+ if (!next_token(&p, prompt, NULL, sizeof(fstring)))
+ break;
+
+ if (strequal(prompt,"."))
+ fstrcpy(prompt,"*");
+
+ special_char_sub(prompt);
+ fstrcpy(t->prompt, prompt);
+ strlower_m(t->prompt);
+ trim_char(t->prompt, ' ', ' ');
+
+ if (!next_token(&p, reply, NULL, sizeof(fstring)))
+ break;
+
+ if (strequal(reply,"."))
+ fstrcpy(reply,"");
+
+ special_char_sub(reply);
+ fstrcpy(t->reply, reply);
+ strlower_m(t->reply);
+ trim_char(t->reply, ' ', ' ');
+
+ }
+ return list;
+}
+
+static void free_pw_chat(struct chat_struct *list)
+{
+ while (list) {
+ struct chat_struct *old_head = list;
+ DLIST_REMOVE(list, list);
+ SAFE_FREE(old_head);
+ }
+}
+
+static int smb_pam_passchange_conv(int num_msg,
+ const struct pam_message **msg,
+ struct pam_response **resp,
+ void *appdata_ptr)
+{
+ int replies = 0;
+ struct pam_response *reply = NULL;
+ fstring current_prompt;
+ fstring current_reply;
+ struct smb_pam_userdata *udp = (struct smb_pam_userdata *)appdata_ptr;
+ struct chat_struct *pw_chat= make_pw_chat(lp_passwd_chat());
+ struct chat_struct *t;
+ BOOL found;
+ *resp = NULL;
+
+ DEBUG(10,("smb_pam_passchange_conv: starting converstation for %d messages\n", num_msg));
+
+ if (num_msg <= 0)
+ return PAM_CONV_ERR;
+
+ if (pw_chat == NULL)
+ return PAM_CONV_ERR;
+
+ /*
+ * Apparantly HPUX has a buggy PAM that doesn't support the
+ * appdata_ptr. Fail if this is the case. JRA.
+ */
+
+ if (udp == NULL) {
+ DEBUG(0,("smb_pam_passchange_conv: PAM on this system is broken - appdata_ptr == NULL !\n"));
+ free_pw_chat(pw_chat);
+ return PAM_CONV_ERR;
+ }
+
+ reply = malloc(sizeof(struct pam_response) * num_msg);
+ if (!reply) {
+ DEBUG(0,("smb_pam_passchange_conv: malloc for reply failed!\n"));
+ free_pw_chat(pw_chat);
+ return PAM_CONV_ERR;
+ }
+
+ for (replies = 0; replies < num_msg; replies++) {
+ found = False;
+ DEBUG(10,("smb_pam_passchange_conv: Processing message %d\n", replies));
+ switch (msg[replies]->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+ DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: PAM said: %s\n", msg[replies]->msg));
+ fstrcpy(current_prompt, msg[replies]->msg);
+ trim_char(current_prompt, ' ', ' ');
+ for (t=pw_chat; t; t=t->next) {
+
+ DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: trying to match |%s| to |%s|\n",
+ t->prompt, current_prompt ));
+
+ if (unix_wild_match(t->prompt, current_prompt) == 0) {
+ fstrcpy(current_reply, t->reply);
+ DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: We sent: %s\n", current_reply));
+ pwd_sub(current_reply, udp->PAM_username, udp->PAM_password, udp->PAM_newpassword);
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: We actualy sent: %s\n", current_reply));
+#endif
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies].resp = COPY_STRING(current_reply);
+ found = True;
+ break;
+ }
+ }
+ /* PAM frees resp */
+ if (!found) {
+ DEBUG(3,("smb_pam_passchange_conv: Could not find reply for PAM prompt: %s\n",msg[replies]->msg));
+ free_pw_chat(pw_chat);
+ SAFE_FREE(reply);
+ return PAM_CONV_ERR;
+ }
+ break;
+
+ case PAM_PROMPT_ECHO_OFF:
+ DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: PAM said: %s\n", msg[replies]->msg));
+ fstrcpy(current_prompt, msg[replies]->msg);
+ trim_char(current_prompt, ' ', ' ');
+ for (t=pw_chat; t; t=t->next) {
+
+ DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: trying to match |%s| to |%s|\n",
+ t->prompt, current_prompt ));
+
+ if (unix_wild_match(t->prompt, current_prompt) == 0) {
+ fstrcpy(current_reply, t->reply);
+ DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: We sent: %s\n", current_reply));
+ pwd_sub(current_reply, udp->PAM_username, udp->PAM_password, udp->PAM_newpassword);
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies].resp = COPY_STRING(current_reply);
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: We actualy sent: %s\n", current_reply));
+#endif
+ found = True;
+ break;
+ }
+ }
+ /* PAM frees resp */
+
+ if (!found) {
+ DEBUG(3,("smb_pam_passchange_conv: Could not find reply for PAM prompt: %s\n",msg[replies]->msg));
+ free_pw_chat(pw_chat);
+ SAFE_FREE(reply);
+ return PAM_CONV_ERR;
+ }
+ break;
+
+ case PAM_TEXT_INFO:
+ /* fall through */
+
+ case PAM_ERROR_MSG:
+ /* ignore it... */
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies].resp = NULL;
+ break;
+
+ default:
+ /* Must be an error of some sort... */
+ free_pw_chat(pw_chat);
+ SAFE_FREE(reply);
+ return PAM_CONV_ERR;
+ }
+ }
+
+ free_pw_chat(pw_chat);
+ if (reply)
+ *resp = reply;
+ return PAM_SUCCESS;
+}
+
+/***************************************************************************
+ Free up a malloced pam_conv struct.
+****************************************************************************/
+
+static void smb_free_pam_conv(struct pam_conv *pconv)
+{
+ if (pconv)
+ SAFE_FREE(pconv->appdata_ptr);
+
+ SAFE_FREE(pconv);
+}
+
+/***************************************************************************
+ Allocate a pam_conv struct.
+****************************************************************************/
+
+static struct pam_conv *smb_setup_pam_conv(smb_pam_conv_fn smb_pam_conv_fnptr, const char *user,
+ const char *passwd, const char *newpass)
+{
+ struct pam_conv *pconv = (struct pam_conv *)malloc(sizeof(struct pam_conv));
+ struct smb_pam_userdata *udp = (struct smb_pam_userdata *)malloc(sizeof(struct smb_pam_userdata));
+
+ if (pconv == NULL || udp == NULL) {
+ SAFE_FREE(pconv);
+ SAFE_FREE(udp);
+ return NULL;
+ }
+
+ udp->PAM_username = user;
+ udp->PAM_password = passwd;
+ udp->PAM_newpassword = newpass;
+
+ pconv->conv = smb_pam_conv_fnptr;
+ pconv->appdata_ptr = (void *)udp;
+ return pconv;
+}
+
+/*
+ * PAM Closing out cleanup handler
+ */
+
+static BOOL smb_pam_end(pam_handle_t *pamh, struct pam_conv *smb_pam_conv_ptr)
+{
+ int pam_error;
+
+ smb_free_pam_conv(smb_pam_conv_ptr);
+
+ if( pamh != NULL ) {
+ pam_error = pam_end(pamh, 0);
+ if(smb_pam_error_handler(pamh, pam_error, "End Cleanup Failed", 2) == True) {
+ DEBUG(4, ("smb_pam_end: PAM: PAM_END OK.\n"));
+ return True;
+ }
+ }
+ DEBUG(2,("smb_pam_end: PAM: not initialised"));
+ return False;
+}
+
+/*
+ * Start PAM authentication for specified account
+ */
+
+static BOOL smb_pam_start(pam_handle_t **pamh, const char *user, const char *rhost, struct pam_conv *pconv)
+{
+ int pam_error;
+ const char *our_rhost;
+
+ *pamh = (pam_handle_t *)NULL;
+
+ DEBUG(4,("smb_pam_start: PAM: Init user: %s\n", user));
+
+ pam_error = pam_start("samba", user, pconv, pamh);
+ if( !smb_pam_error_handler(*pamh, pam_error, "Init Failed", 0)) {
+ *pamh = (pam_handle_t *)NULL;
+ return False;
+ }
+
+ if (rhost == NULL) {
+ our_rhost = client_name();
+ if (strequal(rhost,"UNKNOWN"))
+ our_rhost = client_addr();
+ } else {
+ our_rhost = rhost;
+ }
+
+#ifdef PAM_RHOST
+ DEBUG(4,("smb_pam_start: PAM: setting rhost to: %s\n", our_rhost));
+ pam_error = pam_set_item(*pamh, PAM_RHOST, our_rhost);
+ if(!smb_pam_error_handler(*pamh, pam_error, "set rhost failed", 0)) {
+ smb_pam_end(*pamh, pconv);
+ *pamh = (pam_handle_t *)NULL;
+ return False;
+ }
+#endif
+#ifdef PAM_TTY
+ DEBUG(4,("smb_pam_start: PAM: setting tty\n"));
+ pam_error = pam_set_item(*pamh, PAM_TTY, "samba");
+ if (!smb_pam_error_handler(*pamh, pam_error, "set tty failed", 0)) {
+ smb_pam_end(*pamh, pconv);
+ *pamh = (pam_handle_t *)NULL;
+ return False;
+ }
+#endif
+ DEBUG(4,("smb_pam_start: PAM: Init passed for user: %s\n", user));
+ return True;
+}
+
+/*
+ * PAM Authentication Handler
+ */
+static NTSTATUS smb_pam_auth(pam_handle_t *pamh, const char *user)
+{
+ int pam_error;
+ NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
+
+ /*
+ * To enable debugging set in /etc/pam.d/samba:
+ * auth required /lib/security/pam_pwdb.so nullok shadow audit
+ */
+
+ DEBUG(4,("smb_pam_auth: PAM: Authenticate User: %s\n", user));
+ pam_error = pam_authenticate(pamh, PAM_SILENT | lp_null_passwords() ? 0 : PAM_DISALLOW_NULL_AUTHTOK);
+ switch( pam_error ){
+ case PAM_AUTH_ERR:
+ DEBUG(2, ("smb_pam_auth: PAM: Athentication Error for user %s\n", user));
+ break;
+ case PAM_CRED_INSUFFICIENT:
+ DEBUG(2, ("smb_pam_auth: PAM: Insufficient Credentials for user %s\n", user));
+ break;
+ case PAM_AUTHINFO_UNAVAIL:
+ DEBUG(2, ("smb_pam_auth: PAM: Authentication Information Unavailable for user %s\n", user));
+ break;
+ case PAM_USER_UNKNOWN:
+ DEBUG(2, ("smb_pam_auth: PAM: Username %s NOT known to Authentication system\n", user));
+ break;
+ case PAM_MAXTRIES:
+ DEBUG(2, ("smb_pam_auth: PAM: One or more authentication modules reports user limit for user %s exceeeded\n", user));
+ break;
+ case PAM_ABORT:
+ DEBUG(0, ("smb_pam_auth: PAM: One or more PAM modules failed to load for user %s\n", user));
+ break;
+ case PAM_SUCCESS:
+ DEBUG(4, ("smb_pam_auth: PAM: User %s Authenticated OK\n", user));
+ break;
+ default:
+ DEBUG(0, ("smb_pam_auth: PAM: UNKNOWN ERROR while authenticating user %s\n", user));
+ break;
+ }
+
+ smb_pam_nt_status_error_handler(pamh, pam_error, "Authentication Failure", 2, &nt_status);
+ return nt_status;
+}
+
+/*
+ * PAM Account Handler
+ */
+static NTSTATUS smb_pam_account(pam_handle_t *pamh, const char * user)
+{
+ int pam_error;
+ NTSTATUS nt_status = NT_STATUS_ACCOUNT_DISABLED;
+
+ DEBUG(4,("smb_pam_account: PAM: Account Management for User: %s\n", user));
+ pam_error = pam_acct_mgmt(pamh, PAM_SILENT); /* Is user account enabled? */
+ switch( pam_error ) {
+ case PAM_AUTHTOK_EXPIRED:
+ DEBUG(2, ("smb_pam_account: PAM: User %s is valid but password is expired\n", user));
+ break;
+ case PAM_ACCT_EXPIRED:
+ DEBUG(2, ("smb_pam_account: PAM: User %s no longer permitted to access system\n", user));
+ break;
+ case PAM_AUTH_ERR:
+ DEBUG(2, ("smb_pam_account: PAM: There was an authentication error for user %s\n", user));
+ break;
+ case PAM_PERM_DENIED:
+ DEBUG(0, ("smb_pam_account: PAM: User %s is NOT permitted to access system at this time\n", user));
+ break;
+ case PAM_USER_UNKNOWN:
+ DEBUG(0, ("smb_pam_account: PAM: User \"%s\" is NOT known to account management\n", user));
+ break;
+ case PAM_SUCCESS:
+ DEBUG(4, ("smb_pam_account: PAM: Account OK for User: %s\n", user));
+ break;
+ default:
+ DEBUG(0, ("smb_pam_account: PAM: UNKNOWN PAM ERROR (%d) during Account Management for User: %s\n", pam_error, user));
+ break;
+ }
+
+ smb_pam_nt_status_error_handler(pamh, pam_error, "Account Check Failed", 2, &nt_status);
+ return nt_status;
+}
+
+/*
+ * PAM Credential Setting
+ */
+
+static NTSTATUS smb_pam_setcred(pam_handle_t *pamh, const char * user)
+{
+ int pam_error;
+ NTSTATUS nt_status = NT_STATUS_NO_TOKEN;
+
+ /*
+ * This will allow samba to aquire a kerberos token. And, when
+ * exporting an AFS cell, be able to /write/ to this cell.
+ */
+
+ DEBUG(4,("PAM: Account Management SetCredentials for User: %s\n", user));
+ pam_error = pam_setcred(pamh, (PAM_ESTABLISH_CRED|PAM_SILENT));
+ switch( pam_error ) {
+ case PAM_CRED_UNAVAIL:
+ DEBUG(0, ("smb_pam_setcred: PAM: Credentials not found for user:%s\n", user ));
+ break;
+ case PAM_CRED_EXPIRED:
+ DEBUG(0, ("smb_pam_setcred: PAM: Credentials for user: \"%s\" EXPIRED!\n", user ));
+ break;
+ case PAM_USER_UNKNOWN:
+ DEBUG(0, ("smb_pam_setcred: PAM: User: \"%s\" is NOT known so can not set credentials!\n", user ));
+ break;
+ case PAM_CRED_ERR:
+ DEBUG(0, ("smb_pam_setcred: PAM: Unknown setcredentials error - unable to set credentials for %s\n", user ));
+ break;
+ case PAM_SUCCESS:
+ DEBUG(4, ("smb_pam_setcred: PAM: SetCredentials OK for User: %s\n", user));
+ break;
+ default:
+ DEBUG(0, ("smb_pam_setcred: PAM: UNKNOWN PAM ERROR (%d) during SetCredentials for User: %s\n", pam_error, user));
+ break;
+ }
+
+ smb_pam_nt_status_error_handler(pamh, pam_error, "Set Credential Failure", 2, &nt_status);
+ return nt_status;
+}
+
+/*
+ * PAM Internal Session Handler
+ */
+static BOOL smb_internal_pam_session(pam_handle_t *pamh, const char *user, const char *tty, BOOL flag)
+{
+ int pam_error;
+
+#ifdef PAM_TTY
+ DEBUG(4,("smb_internal_pam_session: PAM: tty set to: %s\n", tty));
+ pam_error = pam_set_item(pamh, PAM_TTY, tty);
+ if (!smb_pam_error_handler(pamh, pam_error, "set tty failed", 0))
+ return False;
+#endif
+
+ if (flag) {
+ pam_error = pam_open_session(pamh, PAM_SILENT);
+ if (!smb_pam_error_handler(pamh, pam_error, "session setup failed", 0))
+ return False;
+ } else {
+ pam_setcred(pamh, (PAM_DELETE_CRED|PAM_SILENT)); /* We don't care if this fails */
+ pam_error = pam_close_session(pamh, PAM_SILENT); /* This will probably pick up the error anyway */
+ if (!smb_pam_error_handler(pamh, pam_error, "session close failed", 0))
+ return False;
+ }
+ return (True);
+}
+
+/*
+ * Internal PAM Password Changer.
+ */
+
+static BOOL smb_pam_chauthtok(pam_handle_t *pamh, const char * user)
+{
+ int pam_error;
+
+ DEBUG(4,("smb_pam_chauthtok: PAM: Password Change for User: %s\n", user));
+
+ pam_error = pam_chauthtok(pamh, PAM_SILENT); /* Change Password */
+
+ switch( pam_error ) {
+ case PAM_AUTHTOK_ERR:
+ DEBUG(2, ("PAM: unable to obtain the new authentication token - is password to weak?\n"));
+ break;
+
+ /* This doesn't seem to be defined on Solaris. JRA */
+#ifdef PAM_AUTHTOK_RECOVER_ERR
+ case PAM_AUTHTOK_RECOVER_ERR:
+ DEBUG(2, ("PAM: unable to obtain the old authentication token - was the old password wrong?.\n"));
+ break;
+#endif
+
+ case PAM_AUTHTOK_LOCK_BUSY:
+ DEBUG(2, ("PAM: unable to change the authentication token since it is currently locked.\n"));
+ break;
+ case PAM_AUTHTOK_DISABLE_AGING:
+ DEBUG(2, ("PAM: Authentication token aging has been disabled.\n"));
+ break;
+ case PAM_PERM_DENIED:
+ DEBUG(0, ("PAM: Permission denied.\n"));
+ break;
+ case PAM_TRY_AGAIN:
+ DEBUG(0, ("PAM: Could not update all authentication token(s). No authentication tokens were updated.\n"));
+ break;
+ case PAM_USER_UNKNOWN:
+ DEBUG(0, ("PAM: User not known to PAM\n"));
+ break;
+ case PAM_SUCCESS:
+ DEBUG(4, ("PAM: Account OK for User: %s\n", user));
+ break;
+ default:
+ DEBUG(0, ("PAM: UNKNOWN PAM ERROR (%d) for User: %s\n", pam_error, user));
+ }
+
+ if(!smb_pam_error_handler(pamh, pam_error, "Password Change Failed", 2)) {
+ return False;
+ }
+
+ /* If this point is reached, the password has changed. */
+ return True;
+}
+
+/*
+ * PAM Externally accessible Session handler
+ */
+
+BOOL smb_pam_claim_session(char *user, char *tty, char *rhost)
+{
+ pam_handle_t *pamh = NULL;
+ struct pam_conv *pconv = NULL;
+
+ /* Ignore PAM if told to. */
+
+ if (!lp_obey_pam_restrictions())
+ return True;
+
+ if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, NULL, NULL)) == NULL)
+ return False;
+
+ if (!smb_pam_start(&pamh, user, rhost, pconv))
+ return False;
+
+ if (!smb_internal_pam_session(pamh, user, tty, True)) {
+ smb_pam_end(pamh, pconv);
+ return False;
+ }
+
+ return smb_pam_end(pamh, pconv);
+}
+
+/*
+ * PAM Externally accessible Session handler
+ */
+
+BOOL smb_pam_close_session(char *user, char *tty, char *rhost)
+{
+ pam_handle_t *pamh = NULL;
+ struct pam_conv *pconv = NULL;
+
+ /* Ignore PAM if told to. */
+
+ if (!lp_obey_pam_restrictions())
+ return True;
+
+ if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, NULL, NULL)) == NULL)
+ return False;
+
+ if (!smb_pam_start(&pamh, user, rhost, pconv))
+ return False;
+
+ if (!smb_internal_pam_session(pamh, user, tty, False)) {
+ smb_pam_end(pamh, pconv);
+ return False;
+ }
+
+ return smb_pam_end(pamh, pconv);
+}
+
+/*
+ * PAM Externally accessible Account handler
+ */
+
+NTSTATUS smb_pam_accountcheck(const char * user)
+{
+ NTSTATUS nt_status = NT_STATUS_ACCOUNT_DISABLED;
+ pam_handle_t *pamh = NULL;
+ struct pam_conv *pconv = NULL;
+
+ /* Ignore PAM if told to. */
+
+ if (!lp_obey_pam_restrictions())
+ return NT_STATUS_OK;
+
+ if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, NULL, NULL)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if (!smb_pam_start(&pamh, user, NULL, pconv))
+ return NT_STATUS_ACCOUNT_DISABLED;
+
+ if (!NT_STATUS_IS_OK(nt_status = smb_pam_account(pamh, user)))
+ DEBUG(0, ("smb_pam_accountcheck: PAM: Account Validation Failed - Rejecting User %s!\n", user));
+
+ smb_pam_end(pamh, pconv);
+ return nt_status;
+}
+
+/*
+ * PAM Password Validation Suite
+ */
+
+NTSTATUS smb_pam_passcheck(const char * user, const char * password)
+{
+ pam_handle_t *pamh = NULL;
+ NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
+ struct pam_conv *pconv = NULL;
+
+ /*
+ * Note we can't ignore PAM here as this is the only
+ * way of doing auths on plaintext passwords when
+ * compiled --with-pam.
+ */
+
+ if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, password, NULL)) == NULL)
+ return NT_STATUS_LOGON_FAILURE;
+
+ if (!smb_pam_start(&pamh, user, NULL, pconv))
+ return NT_STATUS_LOGON_FAILURE;
+
+ if (!NT_STATUS_IS_OK(nt_status = smb_pam_auth(pamh, user))) {
+ DEBUG(0, ("smb_pam_passcheck: PAM: smb_pam_auth failed - Rejecting User %s !\n", user));
+ smb_pam_end(pamh, pconv);
+ return nt_status;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = smb_pam_account(pamh, user))) {
+ DEBUG(0, ("smb_pam_passcheck: PAM: smb_pam_account failed - Rejecting User %s !\n", user));
+ smb_pam_end(pamh, pconv);
+ return nt_status;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = smb_pam_setcred(pamh, user))) {
+ DEBUG(0, ("smb_pam_passcheck: PAM: smb_pam_setcred failed - Rejecting User %s !\n", user));
+ smb_pam_end(pamh, pconv);
+ return nt_status;
+ }
+
+ smb_pam_end(pamh, pconv);
+ return nt_status;
+}
+
+/*
+ * PAM Password Change Suite
+ */
+
+BOOL smb_pam_passchange(const char * user, const char * oldpassword, const char * newpassword)
+{
+ /* Appropriate quantities of root should be obtained BEFORE calling this function */
+ struct pam_conv *pconv = NULL;
+ pam_handle_t *pamh = NULL;
+
+ if ((pconv = smb_setup_pam_conv(smb_pam_passchange_conv, user, oldpassword, newpassword)) == NULL)
+ return False;
+
+ if(!smb_pam_start(&pamh, user, NULL, pconv))
+ return False;
+
+ if (!smb_pam_chauthtok(pamh, user)) {
+ DEBUG(0, ("smb_pam_passchange: PAM: Password Change Failed for user %s!\n", user));
+ smb_pam_end(pamh, pconv);
+ return False;
+ }
+
+ return smb_pam_end(pamh, pconv);
+}
+
+#else
+
+/* If PAM not used, no PAM restrictions on accounts. */
+NTSTATUS smb_pam_accountcheck(const char * user)
+{
+ return NT_STATUS_OK;
+}
+
+/* If PAM not used, also no PAM restrictions on sessions. */
+BOOL smb_pam_claim_session(char *user, char *tty, char *rhost)
+{
+ return True;
+}
+
+/* If PAM not used, also no PAM restrictions on sessions. */
+BOOL smb_pam_close_session(char *in_user, char *tty, char *rhost)
+{
+ return True;
+}
+#endif /* WITH_PAM */
diff --git a/source/auth/pass_check.c b/source/auth/pass_check.c
new file mode 100644
index 00000000000..1ac8c1815a6
--- /dev/null
+++ b/source/auth/pass_check.c
@@ -0,0 +1,783 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password checking
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* this module is for checking a username/password against a system
+ password database. The SMB encrypted password support is elsewhere */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/* these are kept here to keep the string_combinations function simple */
+static fstring this_user;
+#if !defined(WITH_PAM)
+static fstring this_salt;
+static fstring this_crypted;
+#endif
+
+#ifdef WITH_AFS
+
+#include <afs/stds.h>
+#include <afs/kautils.h>
+
+/*******************************************************************
+check on AFS authentication
+********************************************************************/
+static BOOL afs_auth(char *user, char *password)
+{
+ long password_expires = 0;
+ char *reason;
+
+ /* For versions of AFS prior to 3.3, this routine has few arguments, */
+ /* but since I can't find the old documentation... :-) */
+ setpag();
+ if (ka_UserAuthenticateGeneral
+ (KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG, user, (char *)0, /* instance */
+ (char *)0, /* cell */
+ password, 0, /* lifetime, default */
+ &password_expires, /*days 'til it expires */
+ 0, /* spare 2 */
+ &reason) == 0)
+ {
+ return (True);
+ }
+ DEBUG(1,
+ ("AFS authentication for \"%s\" failed (%s)\n", user, reason));
+ return (False);
+}
+#endif
+
+
+#ifdef WITH_DFS
+
+#include <dce/dce_error.h>
+#include <dce/sec_login.h>
+
+/*****************************************************************
+ This new version of the DFS_AUTH code was donated by Karsten Muuss
+ <muuss@or.uni-bonn.de>. It fixes the following problems with the
+ old code :
+
+ - Server credentials may expire
+ - Client credential cache files have wrong owner
+ - purge_context() function is called with invalid argument
+
+ This new code was modified to ensure that on exit the uid/gid is
+ still root, and the original directory is restored. JRA.
+******************************************************************/
+
+sec_login_handle_t my_dce_sec_context;
+int dcelogin_atmost_once = 0;
+
+/*******************************************************************
+check on a DCE/DFS authentication
+********************************************************************/
+static BOOL dfs_auth(char *user, char *password)
+{
+ error_status_t err;
+ int err2;
+ int prterr;
+ signed32 expire_time, current_time;
+ boolean32 password_reset;
+ struct passwd *pw;
+ sec_passwd_rec_t passwd_rec;
+ sec_login_auth_src_t auth_src = sec_login_auth_src_network;
+ unsigned char dce_errstr[dce_c_error_string_len];
+ gid_t egid;
+
+ if (dcelogin_atmost_once)
+ return (False);
+
+#ifdef HAVE_CRYPT
+ /*
+ * We only go for a DCE login context if the given password
+ * matches that stored in the local password file..
+ * Assumes local passwd file is kept in sync w/ DCE RGY!
+ */
+
+ if (strcmp((char *)crypt(password, this_salt), this_crypted))
+ {
+ return (False);
+ }
+#endif
+
+ sec_login_get_current_context(&my_dce_sec_context, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
+
+ return (False);
+ }
+
+ sec_login_certify_identity(my_dce_sec_context, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
+
+ return (False);
+ }
+
+ sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
+
+ return (False);
+ }
+
+ time(&current_time);
+
+ if (expire_time < (current_time + 60))
+ {
+ struct passwd *pw;
+ sec_passwd_rec_t *key;
+
+ sec_login_get_pwent(my_dce_sec_context,
+ (sec_login_passwd_t *) & pw, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
+
+ return (False);
+ }
+
+ sec_login_refresh_identity(my_dce_sec_context, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't refresh identity. %s\n",
+ dce_errstr));
+
+ return (False);
+ }
+
+ sec_key_mgmt_get_key(rpc_c_authn_dce_secret, NULL,
+ (unsigned char *)pw->pw_name,
+ sec_c_key_version_none,
+ (void **)&key, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't get key for %s. %s\n",
+ pw->pw_name, dce_errstr));
+
+ return (False);
+ }
+
+ sec_login_valid_and_cert_ident(my_dce_sec_context, key,
+ &password_reset, &auth_src,
+ &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0,
+ ("DCE can't validate and certify identity for %s. %s\n",
+ pw->pw_name, dce_errstr));
+ }
+
+ sec_key_mgmt_free_key(key, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't free key.\n", dce_errstr));
+ }
+ }
+
+ if (sec_login_setup_identity((unsigned char *)user,
+ sec_login_no_flags,
+ &my_dce_sec_context, &err) == 0)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
+ user, dce_errstr));
+ return (False);
+ }
+
+ sec_login_get_pwent(my_dce_sec_context,
+ (sec_login_passwd_t *) & pw, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
+
+ return (False);
+ }
+
+ sec_login_purge_context(&my_dce_sec_context, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't purge context. %s\n", dce_errstr));
+
+ return (False);
+ }
+
+ /*
+ * NB. I'd like to change these to call something like change_to_user()
+ * instead but currently we don't have a connection
+ * context to become the correct user. This is already
+ * fairly platform specific code however, so I think
+ * this should be ok. I have added code to go
+ * back to being root on error though. JRA.
+ */
+
+ egid = getegid();
+
+ set_effective_gid(pw->pw_gid);
+ set_effective_uid(pw->pw_uid);
+
+ if (sec_login_setup_identity((unsigned char *)user,
+ sec_login_no_flags,
+ &my_dce_sec_context, &err) == 0)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
+ user, dce_errstr));
+ goto err;
+ }
+
+ sec_login_get_pwent(my_dce_sec_context,
+ (sec_login_passwd_t *) & pw, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
+ goto err;
+ }
+
+ passwd_rec.version_number = sec_passwd_c_version_none;
+ passwd_rec.pepper = NULL;
+ passwd_rec.key.key_type = sec_passwd_plain;
+ passwd_rec.key.tagged_union.plain = (idl_char *) password;
+
+ sec_login_validate_identity(my_dce_sec_context,
+ &passwd_rec, &password_reset,
+ &auth_src, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0,
+ ("DCE Identity Validation failed for principal %s: %s\n",
+ user, dce_errstr));
+ goto err;
+ }
+
+ sec_login_certify_identity(my_dce_sec_context, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE certify identity failed: %s\n", dce_errstr));
+ goto err;
+ }
+
+ if (auth_src != sec_login_auth_src_network)
+ {
+ DEBUG(0, ("DCE context has no network credentials.\n"));
+ }
+
+ sec_login_set_context(my_dce_sec_context, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0,
+ ("DCE login failed for principal %s, cant set context: %s\n",
+ user, dce_errstr));
+
+ sec_login_purge_context(&my_dce_sec_context, &err);
+ goto err;
+ }
+
+ sec_login_get_pwent(my_dce_sec_context,
+ (sec_login_passwd_t *) & pw, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
+ goto err;
+ }
+
+ DEBUG(0, ("DCE login succeeded for principal %s on pid %d\n",
+ user, sys_getpid()));
+
+ DEBUG(3, ("DCE principal: %s\n"
+ " uid: %d\n"
+ " gid: %d\n",
+ pw->pw_name, pw->pw_uid, pw->pw_gid));
+ DEBUG(3, (" info: %s\n"
+ " dir: %s\n"
+ " shell: %s\n",
+ pw->pw_gecos, pw->pw_dir, pw->pw_shell));
+
+ sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
+ goto err;
+ }
+
+ set_effective_uid(0);
+ set_effective_gid(0);
+
+ DEBUG(0,
+ ("DCE context expires: %s", asctime(localtime(&expire_time))));
+
+ dcelogin_atmost_once = 1;
+ return (True);
+
+ err:
+
+ /* Go back to root, JRA. */
+ set_effective_uid(0);
+ set_effective_gid(egid);
+ return (False);
+}
+
+void dfs_unlogin(void)
+{
+ error_status_t err;
+ int err2;
+ unsigned char dce_errstr[dce_c_error_string_len];
+
+ sec_login_purge_context(&my_dce_sec_context, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0,
+ ("DCE purge login context failed for server instance %d: %s\n",
+ sys_getpid(), dce_errstr));
+ }
+}
+#endif
+
+#ifdef LINUX_BIGCRYPT
+/****************************************************************************
+an enhanced crypt for Linux to handle password longer than 8 characters
+****************************************************************************/
+static int linux_bigcrypt(char *password, char *salt1, char *crypted)
+{
+#define LINUX_PASSWORD_SEG_CHARS 8
+ char salt[3];
+ int i;
+
+ StrnCpy(salt, salt1, 2);
+ crypted += 2;
+
+ for (i = strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
+ char *p = crypt(password, salt) + 2;
+ if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
+ return (0);
+ password += LINUX_PASSWORD_SEG_CHARS;
+ crypted += strlen(p);
+ }
+
+ return (1);
+}
+#endif
+
+#ifdef OSF1_ENH_SEC
+/****************************************************************************
+an enhanced crypt for OSF1
+****************************************************************************/
+static char *osf1_bigcrypt(char *password, char *salt1)
+{
+ static char result[AUTH_MAX_PASSWD_LENGTH] = "";
+ char *p1;
+ char *p2 = password;
+ char salt[3];
+ int i;
+ int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
+ if (strlen(password) % AUTH_CLEARTEXT_SEG_CHARS)
+ parts++;
+
+ StrnCpy(salt, salt1, 2);
+ StrnCpy(result, salt1, 2);
+ result[2] = '\0';
+
+ for (i = 0; i < parts; i++) {
+ p1 = crypt(p2, salt);
+ strncat(result, p1 + 2,
+ AUTH_MAX_PASSWD_LENGTH - strlen(p1 + 2) - 1);
+ StrnCpy(salt, &result[2 + i * AUTH_CIPHERTEXT_SEG_CHARS], 2);
+ p2 += AUTH_CLEARTEXT_SEG_CHARS;
+ }
+
+ return (result);
+}
+#endif
+
+
+/****************************************************************************
+apply a function to upper/lower case combinations
+of a string and return true if one of them returns true.
+try all combinations with N uppercase letters.
+offset is the first char to try and change (start with 0)
+it assumes the string starts lowercased
+****************************************************************************/
+static NTSTATUS string_combinations2(char *s, int offset, NTSTATUS (*fn) (const char *),
+ int N)
+{
+ int len = strlen(s);
+ int i;
+ NTSTATUS nt_status;
+
+#ifdef PASSWORD_LENGTH
+ len = MIN(len, PASSWORD_LENGTH);
+#endif
+
+ if (N <= 0 || offset >= len)
+ return (fn(s));
+
+ for (i = offset; i < (len - (N - 1)); i++) {
+ char c = s[i];
+ if (!islower(c))
+ continue;
+ s[i] = toupper(c);
+ if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, i + 1, fn, N - 1),NT_STATUS_WRONG_PASSWORD)) {
+ return (nt_status);
+ }
+ s[i] = c;
+ }
+ return (NT_STATUS_WRONG_PASSWORD);
+}
+
+/****************************************************************************
+apply a function to upper/lower case combinations
+of a string and return true if one of them returns true.
+try all combinations with up to N uppercase letters.
+offset is the first char to try and change (start with 0)
+it assumes the string starts lowercased
+****************************************************************************/
+static NTSTATUS string_combinations(char *s, NTSTATUS (*fn) (const char *), int N)
+{
+ int n;
+ NTSTATUS nt_status;
+ for (n = 1; n <= N; n++)
+ if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, 0, fn, n), NT_STATUS_WRONG_PASSWORD))
+ return nt_status;
+ return NT_STATUS_WRONG_PASSWORD;
+}
+
+
+/****************************************************************************
+core of password checking routine
+****************************************************************************/
+static NTSTATUS password_check(const char *password)
+{
+#ifdef WITH_PAM
+ return smb_pam_passcheck(this_user, password);
+#else
+
+ BOOL ret;
+
+#ifdef WITH_AFS
+ if (afs_auth(this_user, password))
+ return NT_STATUS_OK;
+#endif /* WITH_AFS */
+
+#ifdef WITH_DFS
+ if (dfs_auth(this_user, password))
+ return NT_STATUS_OK;
+#endif /* WITH_DFS */
+
+#ifdef OSF1_ENH_SEC
+
+ ret = (strcmp(osf1_bigcrypt(password, this_salt),
+ this_crypted) == 0);
+ if (!ret) {
+ DEBUG(2,
+ ("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
+ ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
+ }
+ if (ret) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+#endif /* OSF1_ENH_SEC */
+
+#ifdef ULTRIX_AUTH
+ ret = (strcmp((char *)crypt16(password, this_salt), this_crypted) == 0);
+ if (ret) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+#endif /* ULTRIX_AUTH */
+
+#ifdef LINUX_BIGCRYPT
+ ret = (linux_bigcrypt(password, this_salt, this_crypted));
+ if (ret) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+#endif /* LINUX_BIGCRYPT */
+
+#if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
+
+ /*
+ * Some systems have bigcrypt in the C library but might not
+ * actually use it for the password hashes (HPUX 10.20) is
+ * a noteable example. So we try bigcrypt first, followed
+ * by crypt.
+ */
+
+ if (strcmp(bigcrypt(password, this_salt), this_crypted) == 0)
+ return NT_STATUS_OK;
+ else
+ ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
+ if (ret) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+#else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
+
+#ifdef HAVE_BIGCRYPT
+ ret = (strcmp(bigcrypt(password, this_salt), this_crypted) == 0);
+ if (ret) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+#endif /* HAVE_BIGCRYPT */
+
+#ifndef HAVE_CRYPT
+ DEBUG(1, ("Warning - no crypt available\n"));
+ return NT_STATUS_LOGON_FAILURE;
+#else /* HAVE_CRYPT */
+ ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
+ if (ret) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+#endif /* HAVE_CRYPT */
+#endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
+#endif /* WITH_PAM */
+}
+
+
+
+/****************************************************************************
+CHECK if a username/password is OK
+the function pointer fn() points to a function to call when a successful
+match is found and is used to update the encrypted password file
+return NT_STATUS_OK on correct match, appropriate error otherwise
+****************************************************************************/
+
+NTSTATUS pass_check(const struct passwd *pass, const char *user, const char *password,
+ int pwlen, BOOL (*fn) (const char *, const char *), BOOL run_cracker)
+{
+ pstring pass2;
+ int level = lp_passwordlevel();
+
+ NTSTATUS nt_status;
+
+#if DEBUG_PASSWORD
+ DEBUG(100, ("checking user=[%s] pass=[%s]\n", user, password));
+#endif
+
+ if (!password)
+ return NT_STATUS_LOGON_FAILURE;
+
+ if (((!*password) || (!pwlen)) && !lp_null_passwords())
+ return NT_STATUS_LOGON_FAILURE;
+
+#if defined(WITH_PAM)
+
+ /*
+ * If we're using PAM we want to short-circuit all the
+ * checks below and dive straight into the PAM code.
+ */
+
+ fstrcpy(this_user, user);
+
+ DEBUG(4, ("pass_check: Checking (PAM) password for user %s (l=%d)\n", user, pwlen));
+
+#else /* Not using PAM */
+
+ DEBUG(4, ("pass_check: Checking password for user %s (l=%d)\n", user, pwlen));
+
+ if (!pass) {
+ DEBUG(3, ("Couldn't find user %s\n", user));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+
+ /* Copy into global for the convenience of looping code */
+ /* Also the place to keep the 'password' no matter what
+ crazy struct it started in... */
+ fstrcpy(this_crypted, pass->pw_passwd);
+ fstrcpy(this_salt, pass->pw_passwd);
+
+#ifdef HAVE_GETSPNAM
+ {
+ struct spwd *spass;
+
+ /* many shadow systems require you to be root to get
+ the password, in most cases this should already be
+ the case when this function is called, except
+ perhaps for IPC password changing requests */
+
+ spass = getspnam(pass->pw_name);
+ if (spass && spass->sp_pwdp) {
+ fstrcpy(this_crypted, spass->sp_pwdp);
+ fstrcpy(this_salt, spass->sp_pwdp);
+ }
+ }
+#elif defined(IA_UINFO)
+ {
+ /* Need to get password with SVR4.2's ia_ functions
+ instead of get{sp,pw}ent functions. Required by
+ UnixWare 2.x, tested on version
+ 2.1. (tangent@cyberport.com) */
+ uinfo_t uinfo;
+ if (ia_openinfo(pass->pw_name, &uinfo) != -1)
+ ia_get_logpwd(uinfo, &(pass->pw_passwd));
+ }
+#endif
+
+#ifdef HAVE_GETPRPWNAM
+ {
+ struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
+ if (pr_pw && pr_pw->ufld.fd_encrypt)
+ fstrcpy(this_crypted, pr_pw->ufld.fd_encrypt);
+ }
+#endif
+
+#ifdef HAVE_GETPWANAM
+ {
+ struct passwd_adjunct *pwret;
+ pwret = getpwanam(s);
+ if (pwret && pwret->pwa_passwd)
+ fstrcpy(this_crypted, pwret->pwa_passwd);
+ }
+#endif
+
+#ifdef OSF1_ENH_SEC
+ {
+ struct pr_passwd *mypasswd;
+ DEBUG(5, ("Checking password for user %s in OSF1_ENH_SEC\n",
+ user));
+ mypasswd = getprpwnam(user);
+ if (mypasswd) {
+ fstrcpy(this_user, mypasswd->ufld.fd_name);
+ fstrcpy(this_crypted, mypasswd->ufld.fd_encrypt);
+ } else {
+ DEBUG(5,
+ ("OSF1_ENH_SEC: No entry for user %s in protected database !\n",
+ user));
+ }
+ }
+#endif
+
+#ifdef ULTRIX_AUTH
+ {
+ AUTHORIZATION *ap = getauthuid(pass->pw_uid);
+ if (ap) {
+ fstrcpy(this_crypted, ap->a_password);
+ endauthent();
+ }
+ }
+#endif
+
+#if defined(HAVE_TRUNCATED_SALT)
+ /* crypt on some platforms (HPUX in particular)
+ won't work with more than 2 salt characters. */
+ this_salt[2] = 0;
+#endif
+
+ if (!*this_crypted) {
+ if (!lp_null_passwords()) {
+ DEBUG(2, ("Disallowing %s with null password\n",
+ this_user));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ if (!*password) {
+ DEBUG(3,
+ ("Allowing access to %s with null password\n",
+ this_user));
+ return NT_STATUS_OK;
+ }
+ }
+
+#endif /* defined(WITH_PAM) */
+
+ /* try it as it came to us */
+ nt_status = password_check(password);
+ if NT_STATUS_IS_OK(nt_status) {
+ if (fn) {
+ fn(user, password);
+ }
+ return (nt_status);
+ } else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
+ /* No point continuing if its not the password thats to blame (ie PAM disabled). */
+ return (nt_status);
+ }
+
+ if (!run_cracker) {
+ return (nt_status);
+ }
+
+ /* if the password was given to us with mixed case then we don't
+ * need to proceed as we know it hasn't been case modified by the
+ * client */
+ if (strhasupper(password) && strhaslower(password)) {
+ return nt_status;
+ }
+
+ /* make a copy of it */
+ pstrcpy(pass2, password);
+
+ /* try all lowercase if it's currently all uppercase */
+ if (strhasupper(pass2)) {
+ strlower_m(pass2);
+ if NT_STATUS_IS_OK(nt_status = password_check(pass2)) {
+ if (fn)
+ fn(user, pass2);
+ return (nt_status);
+ }
+ }
+
+ /* give up? */
+ if (level < 1) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* last chance - all combinations of up to level chars upper! */
+ strlower_m(pass2);
+
+ if (NT_STATUS_IS_OK(nt_status = string_combinations(pass2, password_check, level))) {
+ if (fn)
+ fn(user, pass2);
+ return nt_status;
+ }
+
+ return NT_STATUS_WRONG_PASSWORD;
+}
diff --git a/source/autogen.sh b/source/autogen.sh
new file mode 100755
index 00000000000..6042b82cdce
--- /dev/null
+++ b/source/autogen.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+
+# Run this script to build samba from CVS.
+
+## insert all possible names (only works with
+## autoconf 2.x
+TESTAUTOHEADER="autoheader autoheader-2.53"
+TESTAUTOCONF="autoconf autoconf-2.53"
+
+AUTOHEADERFOUND="0"
+AUTOCONFFOUND="0"
+
+
+##
+## Look for autoheader
+##
+for i in $TESTAUTOHEADER; do
+ if which $i > /dev/null 2>&1; then
+ if [ `$i --version | head -n 1 | cut -d. -f 2` -ge 53 ]; then
+ AUTOHEADER=$i
+ AUTOHEADERFOUND="1"
+ break
+ fi
+ fi
+done
+
+##
+## Look for autoconf
+##
+
+for i in $TESTAUTOCONF; do
+ if which $i > /dev/null 2>&1; then
+ if [ `$i --version | head -n 1 | cut -d. -f 2` -ge 53 ]; then
+ AUTOCONF=$i
+ AUTOCONFFOUND="1"
+ break
+ fi
+ fi
+done
+
+
+##
+## do we have it?
+##
+if [ "$AUTOCONFFOUND" = "0" -o "$AUTOHEADERFOUND" = "0" ]; then
+ echo "$0: need autoconf 2.53 or later to build samba from CVS" >&2
+ exit 1
+fi
+
+
+
+echo "$0: running $AUTOHEADER"
+$AUTOHEADER || exit 1
+
+echo "$0: running $AUTOCONF"
+$AUTOCONF || exit 1
+
+echo "$0: running script/mkversion.sh"
+./script/mkversion.sh || exit 1
+
+rm -rf autom4te.cache autom4te-2.53.cache
+
+echo "Now run ./configure and then make."
+exit 0
+
diff --git a/source/bin/.cvsignore b/source/bin/.cvsignore
new file mode 100644
index 00000000000..3144075134e
--- /dev/null
+++ b/source/bin/.cvsignore
@@ -0,0 +1,56 @@
+debug2html
+.dummy
+editreg
+.libs
+locktest
+locktest2
+log2pcap
+make_printerdef
+make_smbcodepage
+make_unicodemap
+masktest
+msgtest
+net
+nmbd
+nmblookup
+nsstest
+ntlm_auth
+pdbedit
+profiles
+rpcclient
+samsync
+samtest
+smbcacls
+smbcacls
+smbclient
+smbcontrol
+smbcquotas
+smbd
+smbfilter
+smbget
+smbgroupedit
+smbiconv
+smbmnt
+smbmount
+smbpasswd
+smbsh
+smbspool
+smbstatus
+smbtorture
+smbtree
+smbumount
+*.so
+swat
+talloctort
+tdbbackup
+tdbdump
+testparm
+testprns
+t_push_ucs2
+t_snprintf
+t_strcmp
+t_stringoverflow
+vfstest
+wbinfo
+winbindd
+wrepld
diff --git a/source/change-log b/source/change-log
new file mode 100644
index 00000000000..1f7798b541f
--- /dev/null
+++ b/source/change-log
@@ -0,0 +1,1878 @@
+SUPERCEDED Change Log for Samba
+^^^^^^^^^^
+
+Unless otherwise attributed, all changes were made by
+Andrew.Tridgell@anu.edu.au. All bugs to samba-bugs@samba.org.
+
+NOTE: THIS LOG IS IN CHRONOLOGICAL ORDER
+
+NOTE: From now on the cvs.log file will be used to give a complete log of
+changes to samba. This change-log is now obsolete.
+
+1.5.00 announced to mailing list
+
+1.5.01 1/12/93
+ - configuration through makefile only
+ - fixed silly bug that made the client not accept dir's from
+ the server
+ - tested and updated include files for ultrix, aix and solaris
+ - several things fixed thanks to pierson@ketje.enet.dec.com
+ who provided invaluable help and advice.
+
+1.5.02 1/12/93
+ - added username option to services file so connection
+ as non guest from lanmanager is possible
+ - made server abort when it can't read/write on a socket
+ - added logging to client
+
+1.5.03 2/12/93
+ - printing now works
+ - fixed a minor bug to do with hidden and system attributes
+
+1.5.04 2/12/93
+ - added reduce_name() call to fill in security hole.
+ - cleanup up debug stuff a little
+
+1.5.05 2/12/93
+ - fixed bug in reduce_name that affects services with base paths
+ that have a soft link in them.
+
+1.5.06 3/12/93
+ - used the reserved server field in the search status to hold the
+ directory pointer. This allows lots of directories to be open
+ at once by clients without stuffing things up.
+ - preserved all the client reserved bytes in the search status
+ in case they actually use them. Hopefully this will fix the annoying
+ empty directory dir bug. (it does)
+
+1.5.07 3/12/93
+ - fixed silly bug that caused volume ids to appear twice
+ - fixed a wrote-too-few bug in smb_send()
+
+1.5.08 3/12/93
+ - did the SMBsearch properly. It can now handle recursive searches.
+ In order to keep the required dir info I encode the dirptr and
+ the current dir offset (from telldir) into 5 bytes by using a table
+ on the last 7 bits of the first byte. The first bit is always on
+ as this byte must by != 0
+ This is all put in the "server reserved" search field.
+
+1.5.09 5/12/93
+ - added a prototype nameserver. It's broken but can at least interpret
+ incoming packets.
+ - minor fixes to the server and client
+
+
+1.5.10 5/12/93
+ - fixed silly unsigned/signed char bug that made dosshell noot see all files
+ - added nmbd to Makefile
+
+1.5.11 6/12/93
+ - made the volume label appear as the service name, rather than "Remote"
+ - made the nmbd actually work (a little) for lanman for dos
+
+1.5.12 7/12/93
+ - fixed broadcasting in the nameserver
+ - the smbd now correctly sets the pid and uid
+ - nmbd now seems to work enough to satisfy the MS client.
+
+
+1.5.13 7/12/93
+ - fixed a silly bug that truncated filenames
+ - added -B option to nameserver to specify bcast address
+ - added -R option to nameserver to prevent name registering
+ - fixed minor read() bug. Does this fix the "cmp" bug?
+
+1.5.14 8/12/93
+ - fixed a bug in send_login() in the client. Thanks to
+ tim.hudson@gslmail.mincom.oz.au for pointing this out.
+ - changed name_mangle() to pad to minimum of 32 bytes with spaces
+ - changed the returned buffer size in reply_connect() to not
+ count the 4 byte length field. This fixes the "can execute" bug
+ and the "comp" bug
+ - once again re-wrote the directory pointer handling code.
+ now "tree" works correctly
+
+1.5.15 9/12/93
+ - fixed name mangle bug introduced in 1.5.14 which stopped
+ nameserver from working
+
+1.5.16 9/12/93
+ - arrgh. another silly bug in name_mangle() causes the client to die.
+
+
+1.5.17 13/12/93
+ - some cosmetic cleanups to the code
+ - changed make_connection not to lower case the password (thanks
+ to bryan@alex.com)
+ - fixed accept() bug not initialising in_addrlen (thanks to
+ bogstad@cs.jhu.edu)
+ - fixed cd bug in client.c (thanks to joergs@toppoint.de)
+ - lots of fixes to the nameserver to read_socket and
+ associated routines. It should now correctly reply to the originating
+ address and use the correct broadcast.
+ (thanks to troyer@saifr00.ateng.az.honeywell.com)
+ - SVR4 patches from mark@scot1.ucsalf.ac.uk
+ - changed the default BUFFER_SIZE to 0xFFFF
+
+1.5.18 15/12/93
+ - minor fix to reply_printqueue() to zero data buffer array.
+ - added print command to client.
+ - fixed minor bug in cmd_put() in client where a handle could
+ be closed without being previously opened.
+ - minor cleanups to the client
+ - minor solaris fixes from lonnie@itg.ti.com
+ - SYSV, shadow password and dfree() fixes from mark@scot1.ucsalf.ac.uk
+ - fixed reply_delete() to not delete read-only files
+ - fixed infinite loop in reply_delete on "del ."
+ Thanks to mark@scot1.ucsalf.ac.uk for pointing this out.
+ - posix mode definitions and changes from mark@scot1.ucsalf.ac.uk
+
+
+1.5.19 18/12/93
+ - another very minor fix to dfree().
+ - minor change to SVR4 makefile entry from rossw@march.co.uk
+ - changed reply_open not to open directories, this fixes the
+ "copy .." bug pointed out by mark@scot1.ucsalf.ac.uk
+ - changed dos_mode() so it doesn't return hidden and system info
+ on directories.
+ - changed get_dir_entry() not to descend into proc/self under linux
+ control this with the DONT_DESCEND define in includes.h
+ - changed smb_setlen() to add in the SMB id. (thanks
+ to troyer@saifr00.ateng.az.honeywell.com)
+ - fixed minor bug in reply_dir() so it won't return a ACCESS_DENIED
+ when searching a directory that is unreadable
+ - removed second stat() from get_dir_entry() (speed up)
+ - made null searches close the dirptr (fixes big filesystem problem)
+ - fixed clean_name for cd .. (from magnus@axiom.se)
+
+
+1.5.20 28/12/93
+ - added debug statement in case of SMBcreate with volid set (leefi@microsoft.com)
+ - fixed a bug in dptr_close() so it sets the next_key to a better
+ value, this fixes a annoying dir bug
+ - LOTS of changes from jeremy@netcom.com (Jeremy Allison). This
+ makes it possible to at least connect to a NT server with the client
+ and also fixes up much of the socket/process code. This also includes
+ stuff for compiling on a sun386
+ - got the client working with the Syntax server (a commercial
+ smb-based server). This required a few minor changes so the xmit
+ sizes were negotiated properly.
+ - added support for OSF1, tested on a DEC3000/400 alpha.
+ - fixed the ifconf support under ultrix
+
+1.5.21 31/12/93
+ - minor cosmetic change to reduce_name()
+ - changes for HPUX from ppk@atk.tpo.fi (Pasi Kaara)
+ - minor fix to nameserver
+ - revamped configuration file format. It now takes a Windows-style
+ (.INI style) configuration file. See the file services for
+ full details of the format. New files: loadparm.c, loadparm.h,
+ params.c, params.h, testparm.c. Several changes to smb.h, local.h,
+ server.c, Makefile. The services structure is no longer visible
+ to the rest of the system. (Karl Auer)
+ - added ability to specify a print command on a per service basis
+ and globally via the configuration file. Also allows guest account
+ to be specified in the configuration file. Made appropriate changes
+ to server.c so that these data items are obtained from the config
+ module rather than from hardcoded strings (though the hardcoded
+ strings are still the source of the defaults). (Karl Auer)
+ - renamed old-style configuration file to services.old (Karl Auer)
+ - changed README to reflect new configuration details. (Karl Auer)
+ - removed an item from the bugs wishlist (now supplied!) (Karl Auer)
+ - protected smb.h against multiple compilation. (Karl Auer)
+ - protected local.h against multiple compilation. (Karl Auer)
+ - made config stuff do dynamic allocation
+ - added "homes" capability
+ - added create_mask to each service in config
+
+1.5.22 3/1/94
+ - added "root dir" option for extra security
+ - added -n option to client (useful for OS/2)
+ - changed operation of -n to nameserver to be more useful
+ - patches from Jeremy Allison (jeremy@netcom.com)
+ fixing bug in set_message(), fixing up wait3() for SYSV,
+ making cd check the path in the client, allowing fetching to stdin
+ in client, and enhancing prompt in client to include directory.
+ - made the -D become_daemon() actually detach from the tty. This
+ may need tuning for different flavors of unix.
+ - added "dont descend" option to each service to prevent infinite
+ loops on recursive filesystems.
+ - updated README to add "running as a daemon" and a simple
+ smb.conf file.
+ - HP/UX fixes from ppk@atk.tpo.fi
+ - made lock calls only if opened with write enabled, as pointed out
+ by gadams@ddrive.demon.co.uk
+
+1.5.23 4/1/94
+ - minor fix to logging of data in receive_smb(). It used to
+ miss the last 4 bytes of packets.
+ - added the pid,uid and mid fields to the negotiation phase of
+ the client.
+ - made client able to print from stdin
+ - added password on command line for client
+ - created a sample printcap input filter "smbprint"
+ - several fixes to client to work with OS/2
+ - added mput, mget, prompt and lcd to client
+
+1.5.24 5/1/94
+ - a resend of 1.5.23 as I managed to not include the new
+ prompt, mput and mget code.
+
+1.5.25 7/1/94
+ - change -B on nameserver so it can override the broadcast address
+ - minor changes to printing in client so OS/2 server can handle it.
+ - fixed reply_access() where OK was not being initialised
+ - added "max xmit" to global parameters.
+ - changed create to open with O_RDWR instead of O_WRONLY
+ - added printmode command to client
+ - made help return extra help on a specified command in client
+ - fixed return code in chkpath
+ - added "recurse" and "lowercase" options to client
+ - fixed some error codes from server
+ - added -I option to client
+ - fix for become_daemon() for HPUX from ppk@atk.tpo.fi
+ - added "hosts allow" and "hosts deny" to server
+ - added keepalives to server
+ - added "access" feature to testparam
+ - NetBSD patches from sreiz@aie.nl
+
+1.5.26 8/1/94
+ - changed semantics of hosts access code to do more sensible defaults
+ when either of "hosts allow" or "hosts deny" is blank
+ - added the SO_KEEPALIVE option to configurations of sockets in the
+ server
+ - made some of the SVAL fns into macros to keep fussy compilers from
+ complaining
+ - fixed several null pointer bugs in check_access(). These bugs
+ made 1.5.25 unuseable for many people.
+ - fixed null pointer reference of lp_dontdescend()
+ - reload services file after each new connection.
+
+1.5.27 11/1/94
+ - fixed opening mode for reply_open() in server
+ - patches from Jeremy Allison (jeremy@netcom.com) to support the
+ "core+" protocol. The patches also inclued some other features, such
+ as a new read_with_timeout() call (used by SMBreadbraw), and auto
+ detection of the need to create a socket.
+ - changed the default KEEPALIVE value to 0, as it caused
+ problems with Lanmanager.
+ - added tar capability to client when getting files
+ - altered unix_mode() to return x bits for directories
+ - fixed bug in trim_string()
+
+1.5.28 12/1/94
+ - cleaned up the debug levels a little so debug level 1 is a practical
+ level for general use
+ - fixed a bug in add_a_service() where a freed pointer was referenced. Thanks
+ to bryan@alex.com for finding the bug.
+ - fixed bug in time structure handling in server and client. Thanks to
+ bryan@alex.com for pointing out the bug.
+
+
+1.5.29 15/1/94
+ - fixed a silly bug in reply_open(). Thanks to
+ jeremy@netcom.com for pointing this out.
+ - fixed debug levels in client to be more sensible
+ - added raw read to client
+ - added -B option to client
+ - fixed several bugs in the client, mostly to do with the tar option
+ - added -E option to client
+
+1.5.30 16/1/94
+ - added lots of prototypes so compilers don't complain
+ - fixed minor bug in reply_rename() (thanks to ppk@atk.tpo.fi)
+ - added more support for LANMAN1.0 protocol.
+ - added SESSION SETUP AND X call
+ - added READ AND X call
+ - added TREE CONNECT AND X call
+ - added support for setbuffer for HPUX (thanks to ppk@atk.tpo.fi)
+
+1.5.31 29/1/94
+ - added support for user level security in smbclient eg:
+ smbclient "\\SERVER\SHARE" -U USERNAME%PASSWORD
+ - added error message decode as per SMB File Sharing
+ protocol extensions. (thanks to merik@blackadder.dsh.oz.au)
+ - added selection masks to smbclient that recurse down directory
+ tree. eg: mget *.* with recurse and mask *.c on will retrieve all
+ *.c files in the tree.
+ - patches for FreeBSD from kuku@acds.physik.rwth-aachen.de
+ - changed reduce_name() to trim ./ from front of strings and / from
+ back
+ - fixed a nasty bug in trim_string().
+ - numerous small changes to lots of stuff that I didn't
+ document while I was doing them. Sorry :-(
+ - slightly updated sockspy
+
+ - The following was done by Karl Auer (Karl.Auer@anu.edu.au)
+ - added processing in configuration file of a [printers] section. Allows
+ connection to any printer specified in /etc/printcap (or the file
+ specified in the global parameter 'printcap name').
+ - added full processing of 'available' flag to configuration file. A
+ service can now be 'turned off' by specifying 'available = no'. Of
+ dubious utility.
+ - added 'printcap =' parameter to [global] section in the configuration
+ file. This allows the normal /etc/printcap to be bypassed when
+ checking printer names for dynamic printer connections via [printers].
+ - added 'printer name =' parameters to both the [global] section and
+ services sections of the configuration file. This allows the printer
+ name only to be set, without having to specify an entire print
+ command.
+ - added some synonyms: 'writable' and 'write ok' have the opposite sense
+ to 'read only'. 'public' may be used instead of 'guest ok'. 'printer'
+ may be used instead of 'printer name'. 'printable' is the same as
+ 'print ok'. 'root' may be used instead of 'root dir' or 'root
+ directory'.
+ - added lots more detail to the sample configuration file to take
+ account of the above.
+ - many minor fixes to internal documentation in the configuration
+ sources.
+ - also - Man pages!
+
+
+1.5.32 3/2/94
+ - addition of smbd, smbclient and testparm man pages
+ from Karl Auer
+ - zombie process fix from lendecke@namu01.gwdg.de
+ - added capability to nmbd to serve names available
+ via gethostbyname().
+
+1.5.33 3/2/94
+ - fixed up getting of netmask so it works on more unix variants
+ - added -N option to nmbd
+ - changed GMT diff calculation. need to check it's right for
+ lots of OSes
+ - fixed a bug in read_and_X() and chain_reply() chaining now
+ seems to work correctly
+
+1.5.34 4/2/94
+ - fixed bug in client that meant it couldn't get/put files from WfWg
+ - fixed a bug in the server that caused lpr to return -1 under sunos
+ - fixed a few errors in the hosts allow section of the
+ smb.conf.5 manual page and added examples
+
+1.5.35 6/2/1994
+ - minor bugfix in reduce_name().
+ - changed width of "size" in client during a dir
+ - patches for NEXT (among other things) from lendecke@namu01.gwdg.de
+ - added -a switch to server, and made default action to append
+ to log file
+ - added deadtime options to [global] section for timing out
+ dead connections to the smbd.
+ - HPUX changes from Pasi.Kaara@atk.tpo.fi
+ - made use of unsigned char more consistent
+ - changed the way of getting the default username and host in the
+ client
+ - made LANMAN1 default to on in the client, off in server.
+ Use -DLANMAN1=1 to make it on in both.
+ - lots of casts and cleanups for various operating systems
+ - changes to the Makefile from Karl to auto-instal the man pages
+ - added a short history of the project to the distribution
+
+1.5.36 15/2/94
+ - fixed minor bug in Debug() (thanks to Pasi.Kaara@atk.tpo.fi)
+ - fixed bug in server.c so -a wasn't accepted.
+ - minor fixes to the client
+ - added hosts file to name server (-H option)
+ - added -G option for groups to nameserver
+ - cleanups and additions from Jeremy Allison, taking us
+ closer to LANMAN1.0. In particular the locking code was cleaned up
+ considerably.
+
+1.5.37 16/2/94
+ - fixed bug introduced in 1.5.36 which disabled SMBcreate
+
+1.5.38 18/2/94
+ - fixed get_broadcast() for ultrix (fix from iversen@dsfys1.fi.uib.no)
+ - added automatic group registration
+ - fixed bug in registration code
+ - made nmbd work better with WfWg, and probably others
+ - updated the man pages to include the new nmbd options.
+ - minor updates to the README
+ - fixed double log_out() in send_packet().
+ - fixed bug in smbclient so that "dir" didn't work correctly
+ with pathworks
+ - possibly fixed bug in server that led to "abort retry ignore" from
+ pathworks client when doing a "dir".
+ - changed behaviour of smbclient login slightly, to try a
+ blank password in SMBtcon if the right password fails, and a
+ session setup has succeeded. Some clients seem to use a blank
+ one if a session setup has succeeded.
+ - ISC patches from imb@asstdc.scgt.oz.au
+ - the client now tries to do name registration using a unicast.
+ Let me know if this helps anyone.
+ - tried to add a "contributed" line to each OS in the Makefile.
+
+1.5.39 18/2/94
+ - fixed silly C code that only worked with some compilers
+ - fixed another silly bug in nameserv.c that caused it to seg fault
+
+1.5.40 21/2/94
+ - removed the from (IP) message so people don't worry about 0.0.0.0,
+ it's redundant anyway.
+ - changed the client so the crypt key isn't printed
+ - changed the structure of switch_message() to use a list of functions.
+ This improves the debug info.
+ - made SMBopen ignore supplied attribute as per X/Open spec
+ - made SMBopen fail if file doesn't exist in all cases. Let me know
+ if this breaks something. It is implied in the X/Open spec. This
+ fixes the pkzip bug.
+ - added dptr_demote() to replace dptr_close() to try and fix
+ pathworks dir bug. This has the potential disadvantage of
+ leaving lots of open file descriptors.
+ - changed mask_match to disallow two .s in a name
+
+1.5.41 2/3/94
+ - added "dfree command" global option to smbd to support an
+ external "disk free" executable (typically a script). This gets
+ around the problem of getting disk free info reliably on lots
+ of systems.
+ - added ffirst and fclose to client
+ - simple SYSVR4 patch from mark@scot1.ucsalf.ac.uk
+ - added better uid/gid reporting for debugging purposes
+ - several changes to the logon procedure for the client, so hopefully
+ it will connect correctly to a wider range of servers.
+ - server should no longer crash if it can't open the debug
+ file (thanks to MGK@newton.npl.co.uk)
+ - added the THANKS file.
+
+1.5.42 6/3/94
+ - lots of changes from Jeremy Allison, implementing more of
+ the LANMAN1.0 protocol, and fixing a few bugs.
+ - fixed delete bug, so hopefully wildcards are correct now
+ - pcap changes from Martin Kiff so non-aliased printers in
+ /etc/printcap are recognised
+ - wrote announce file ready for 1.6
+ - re-wrote browse code in client (still doesn't work)
+ - updates to man-pages from Karl Auer
+ - made raw packet dumps mode 0600 and only if -dA is given
+ - changed socket code to use utility functions in util.c
+
+1.6.00 17/3/94
+ - made server always return to original directory (rather than /)
+ - fixed bug in params.c that caused a seg fault if no parms in a
+ section
+ - minor clean ups for clean compile under solaris
+ - solaris fix for running from inetd from Karl Auer
+ - fixes for dfree() under solaris
+ - minor changes that might help BSDI
+ - changes to the Makefile, manual-pages and sample config file from
+ Karl Auer
+ - fixed dfree for Ultrix
+
+1.6.01 19/3/94
+ - fixed setatr bug that allowed directories to be unusable
+
+1.6.02 27/3/94
+ - added timestamps to connection message in log
+ - added idle timeout of 10 minutes to name server
+ - made HAVE_SYSCONF==0 the default in includes.h
+ - made the client not register by default
+ - ISC patches from imb@asstdc.scgt.oz.au
+ - GetWd() cache code from Martin Kiff
+ - rewrote the locking code in terms of fcntl() calls.
+ - fixed "can't delete directory" bug
+ - added code to close old dirptrs for duplicate searches
+ - removed exchange_uids() and the access() call and replaced them.
+
+1.6.03 28/3/94
+ - tried to clean up the time handling a little (local vs gmt time)
+ - added debug level global to server config
+ - added protocol level global to server config
+ - added SMBecho command to server
+ - included Karl Auers SMBGuide in the distribution.
+
+1.6.04 31/3/94
+ - fixed time zeroing bug in smb_close and smb_setatr
+ - re-wrote the username/password handling to be more flexible
+ - added "guest only" service setting to smb.conf
+ - updated man pages for new username/password handling
+ - fixed parse bug in reply_tconX
+ - improved error return code from tcon
+ - several changes to fix printing from WfWg
+
+1.6.05 2/4/94
+ - changed the name of the whole package to Samba
+ - removed SMBexit call from client to stop exiting error message
+ - added interpret_addr() call to replace inet_addr() so
+ a hostname can be used whenever a IP is required
+
+1.6.06 8/4/94
+ - added random tid choice to reduce problem of clients not
+ detecting a server disconnection.
+ - made client not report spurious time from CORE or COREPLUS server.
+ - minor HPUX fix from gunjkoa@dep.sa.gov.au
+ - turned off GETWD_CACHE until we track down a minor bug in it
+
+1.6.07: 10/4/94
+ - added helpful error messages to connection failure in client.
+ - fixed problem with mput in client
+ - changed server to allow guest-only sesssetup messages with any
+ password. Control this with GUEST_SESSION_SETUP in local.h.
+ - minor change to session setup handling in make_connection()
+ - added check for right number of \s in the client.
+ - made the server not exit on last close if the deadtime is != 0
+ - added malloc and realloc wrappers. enable them with -DWRAP_MALLOC=1
+ - if smbd is started with a debug level of 10 or greater it creates
+ a log file ending in the process number
+
+1.6.08: 18/4/94
+ - updated the THANKS file
+ - changes from marcel@fanout.et.tudelft.nl (Marcel Mol) for AMPM
+ times and error report on connect().
+ - made the get_myname() routine discard any part after the first '.'
+ - added a wrapper for free from Martin Kiff
+ - added simpleminded code to handle trapdoor uid systems (untested)
+ - added Martin Kiffs "paranoid" getwd code.
+ - added default MAXPATHLEN if undefined of 1024
+ - made get_broadcast() continue to get netmask if it can't get
+ broadcast (suggestion from Hannu Martikk)
+ - replaced fchmod() calls with chmod() to satisfy some unixes
+
+
+
+1.6.09: 4/5/94
+ - changed perror() calls to strerror() in server.c
+ - fix for dfree on OSF1 from
+ Maximilian Errath (errath@balu.kfunigraz.ac.at)
+ - fixed server time reporting for protocol >= LANMAN1
+ - fixed TimeDiff() for machines without TIMEZONE or TIMELOCAL
+ (thanks to Vesa S{rkel{ <vesku@rankki.kcl.fi>)
+ - added SYSV defs to AIX and HPUX to fix "memory" problem
+ (actually a signal problem).
+ - added version to client banner in log file
+ - Ultrix patches from Vesa S{rkel{ <vesku@rankki.kcl.fi>
+ - added ! command to client for executing shell commands
+ - fixed ERRnofids bug in server
+ - fixed name_equal bug
+ (thanks to cjkiick@flinx.b11.ingr.com (Chris Kiick))
+ - wrapped gethostbyname() with Get_Hostbyname() to prevent
+ case sensitive problems on name lookups
+ - limit printer tmp filename to 14 chars
+ (from Paul Thomas Mahoney <ptm@xact1.xact.com>)
+ - added ability to understand 64 bit file times
+ (thanks to davidb@ndl.co.uk (David Boreham))
+ - added Gwt_Pwnam() wrapper to cover server case-sensitivity
+ problems (suggestion from J.M.OConnor@massey.ac.nz (John O'Connor))
+ - changed the setuid() calls to try and work for more systems
+ without breaking the ones it currently works for
+ - added version number to usage()
+ (suggestion from peter@prospect.anprod.csiro.au)
+ - added "security=" option for share or user level security
+ - allowed multiple usernames in "user=" field
+ - changed display method for recursive dorectory listings
+ - switched client to use long filenames where supported
+ - added speed reporting to client transfers
+ - several NT fixes to server from jra@vantive.com (Jeremy Allison)
+ - ISC fixes from ptm@xact.demon.co.uk (Paul Mahoney)
+ - fix to README from grif@cs.ucr.edu (Michael A. Griffith)
+ - default netmask and broadcast from Ian A Young <iay@threel.co.uk>
+ - changed default of is_locked() on fcntl() error.
+ - fixed bug in read_with_timeout() that could cause a runaway
+ smbd process.
+ - fixed findnext bug for long filenames in client
+ - changed default protocol level to LANMAN1
+ - change default reported security level to SHARE.
+ - changed password_ok() so that if pwdauth() fails it tries
+ with standard crypt.
+ - added "translate" command to the client to do CR/LF translation
+ for printing, and add a form feed at the end.
+ (thanks to mh2620@sarek.sbc.com (Mark A. Horstman ) )
+ - added "locking=yes/no" toggle for each service
+ - SCO unix patches from Heinz Mauelshagen (mauelsha@ez.da.telekom.de)
+
+1.6.10: 7/5/94
+ - fixed important bug in readbraw/writebraw
+ - added -A option to client
+ - fixed delete bug on long filenames (untested). Thanks to
+ Stefan Wessels <SWESSELS@dos-lan.cs.up.ac.za>
+ - neatened up the byte swapping code
+
+1.6.11: 3/6/94
+ - fixed bug in client in receive_trans2_response() that caused
+ some strange behaviour with LANMAN2.
+ - fixed some offset/alignment problems with lockingX (thanks to
+ Jeremy Allison)
+ - allow locking on O_RDONLY files. Thanks to Martin N Dey <mnd@netmgrs.co.uk>
+ - fixed del bug in client thanks to paulzn@olivetti.nl (Paul van der Zwan)
+ - fixed multiple user= bug thanks to MDGrosen@spectron.COM (Mark Grosen)
+ - added translate ability for all files. Thanks to mh2620@sarek.sbc.com (Mark A. Horstman )
+ - mask out negative lock offsets. Thanks to bgm@atml.co.uk (Barry G Merrick)
+ - more attempts to get the structure alignment better for some machines
+ - cleaned up the machine dependencies a little
+ - ISC fixes from Paul Thomas Mahoney <ptm@xact1.xact.com>
+ - enabled printing with a SMBclose and SMBwrite for NT
+ thanks to jkf@frisky.Franz.COM (Sean Foderaro)
+ - SGI changes from Michael Chua <lpc@solomon.technet.sg>
+ - CLIX patches from cjkiick@ingr.com
+ - NEXT2 and NEXT3_0 patches from Brad Greer (brad@cac.washington.edu)
+ - BSDI changes from tomh@metrics.com (Tom Haapanen)
+ - SCO patches from John Owens (john@micros.com)
+ - fix psz bug in pcap.c (thanks to Karl Auer)
+ - added widelinks option (global and per service). Suggestion from
+ Karl Auer. Defaults to True.
+ - made locking able to be global or local (default is give by global)
+ - added check_name() to dir listings
+ - added "packet size" option to globals. default to 32767. This
+ "fixes" a WfWg bug (thanks to Karl Auer)
+ - fixes for getattrE and setattrE and minor fix in util.c from Jeremy Allison.
+ - Karl updated the man pages o be current
+ - disabled writebraw and readbraw until a possible bug can be investigated further
+
+1.7.00: 14/7/94
+ - added session_users list, to overcome problem of missing usernames in SMBTconX.
+ - added term support to the client
+ - added "default service"
+ - fork for print so user is not root
+ - added name mangling to 8.3 (rudimentary)
+ - fixed bug in in_group()
+ - changed to use gid in place of egid
+ - fixed client connection to OS/2 (1.3 + lanman2.2) and long filenames
+ - added patches from mcochran@wellfeet.com (Marc Cochran)
+ these implement scope ids and fix some udp bugs. It means
+ the -L option to nmbd now works.
+ - made nmbd respond to incoming port rather than only 137
+ - made wide links refuse .. components
+ - fixed "dir foo." bug to stop it showing "foo.???"
+ - improved name mangling (added stack)
+ - added valid FNUM check to most calls
+ - fixed important do_put bug in the client
+ - added magic scripts to the server
+ - re-enabled getwd_cache code
+ - added optional agressive password checking
+ - removed dptr_closepath from SMBsearch to try and stop "dos for loop"
+ bug
+ - DGUX patches from ross@augie.insci.com (ross andrus)
+ - updated the README and THANKS file.
+ - added node status request to -L option of nmbd
+ - stripped trailing spaces in mask_match() (thanks to mike hench hench@cae.uwm.edu)
+ - added COREPLUS style print queue reporting and "lpq command"
+ in globals.
+ - cleaned up date handling and fixed byte order dependancy on dates
+ in SMBgetattrE.
+ - cleaned up the password handling and added "password level" with
+ the possability of checking all case combinations up to N upper
+ case chars.
+ - changed to use recvfrom only on udp ports (fixed read raw!)
+ - added TCB password support for SCO (thanks to lance@fox.com)
+ - updated README, THANKS and announce files.
+ - fixed timezone reporting to be signed (thanks to noses@oink.rhein.de)
+ - disabled max packet as it could cause problems with WfWg (no longer
+ needed now readraw is "fixed")
+ - changed from creat() to open() in mktemp and mknew.
+ - changed umask handling
+ - sped up nmbd by making it cache names
+ - changed idle timeout on nmbd to 2 mins
+ - Netbsd changes from noses@oink.rhein.de
+ - released alpha2
+ - added name timeout to nmbd
+ - changed bind port retry in nmbd
+ - added Limitations sections to README
+ - fixed two . in is_83()
+ - fixed compilations warnings in util.c (thanks to njw@cpsg.com.au)
+ - made [homes] honour multiple user list
+ - fixed mask match bug introduced in alpha1
+ - added "mangled stack" option for stack size
+ - added mangled stack promotion
+ - released alpha3
+ - netbsd-1.0 fix for statfs().
+ - added null_string to util.c to reduce memory usage
+ - changed the way directory structures are put together
+ - added smbrun for system() requests
+ - changed maxmux to 0 in hope of avoiding mpx commands problem
+ - fixed zero response length for session keepalives
+ - removed called name from session users list
+ - added F_RDLCK support to try and handle locks on readonly files
+ - made directory creation honour the lowercase flag in client (thanks
+ to charlie@edina.demon.co.uk)
+ - made checksum for mangling independant of extension if extension is
+ lowercase
+ - added ability to rename files with different extension, preserving
+ root name
+ - released alpha4
+ - better command line error checking in client
+ - changed all debug statements to new format
+ - fixed delete error code reporting
+ - released alpha5
+ - added mangled name support to wildcard delete in server
+ - fixed mask bug in SMBsearch
+ - cleaned up prototypes
+ - released alpha6
+ - fixed important bug in session_setup which made WfWg freeze
+ (maxmux was 0 - this bug was introduced in alpha4)
+ - released alpha7
+ - two printing bug fixes thanks to bgm@atml.co.uk (Barry G Merrick)
+ - uid fix to smbrun (thanks to larry@witch.mitra.com)
+ - man page updates from Karl Auer
+ - FAQ file from Karl Auer
+ - released alpha8
+ - fixed read-only flag in dos_mode() for non writeable services
+ - fixed error code reporting in open() and openX().
+ - minor secureware fix from (thanks to lance@fox.com)
+ - released alpha9
+ - casting cleanups for memcpy().
+ - cleaned up error code names to be more consistant
+
+1.7.01: 17/7/94
+ - minor man page fix from baeder@cadence.com (Scott Baeder)
+ - changed usage() error message in client
+ - made nmbd not exit if can't register own name
+ - made nmbd only register if running as a daemon
+ - fixed stdout problem in smbrun by closing stdin/stdout/stderr
+ - minor fix to lmhosts parsing
+
+
+1.7.02: 20/7/94
+ - made nmbd not call get_broadcast if both -B and -N are used (thanks
+ to Chris Woodrow <Chris.Woodrow@actrix.gen.nz>)
+ - disabled GETWD_CACHE again
+ - fixed INCLUDES list in Makefile to add version.h (thanks to
+ jimw@PE-Nelson.COM (Jim Watt))
+ - made checkname do a become user if it hasn't already done so.
+ - added consistancy check to become_user().
+ - removed mask extension expansion from SMBsearch
+ - small change to chkpth
+ - fix to snum select for lpq status (thanks to Rafi Sadowsky
+ rafi@tavor.openu.ac.il)
+ - changed daemon to is_daemon for NetBSD (thanks to noses@oink.rhein.de)
+ - removed STAFS3 stuff for NETBSD_1_0
+
+
+1.7.03: 29/7/94
+ - updated docs for new distribution structure
+ - made getatr return 0 size for directories (thanks to Bernd Esser
+ esser@pib1.physik.uni-bonn.de)
+ - added valid dos filename checks from Stefan Wessels
+ (swessels@cs.up.ac.za)
+ - added trimming of . in hostnames to -S mode of nmbd
+ - removed become_user() and OPEN_CNUM calls. Now make them
+ in switch_message instead which simplifies a lot of code.
+ - added GETFNUM macro to make chain_fnum more consistant and
+ reliable.
+ - added flags to protocol structures to simplify CAN_WRITE and AS_USER
+ checking
+ - added getwd cache boolean option to globals
+ - added fclose() to lpq status routine thanks to
+ dgb900@durras.anu.edu.au (David Baldwin)
+ - added "only user" option, to limit connection usernames to those
+ in the user= line
+ - changed to badpath from badfile in chkpath despite specs (following
+ what WfWg does). This fixes "file not found" error in copy command.
+ Thanks to rwa@aber.ac.uk for pointing out the bug
+ - changes for apollo from Stephen C. Steel <steve@qv3donald.LeidenUniv.nl>
+ - more changes for Apollo from jmi@csd.cri.dk (John Mills)
+ - released alpha release
+ - added FTRUNCATE_CAN_EXTEND=0 as default to fix problem with word6.
+ Possibly not needed on many OSes? Thanks to Charlie Hussey
+ charlie@edina.demon.co.uk
+ - started adding max connections code
+ - much improved group handling contributed by
+ Ian Heath (ih@ecs.soton.ac.uk)
+
+1.7.04: 29/7/94
+ - fixed one line bug in SMBopenX that got error code wrong.
+
+1.7.05: 2/8/94
+ - added UNIXERROR() macro to get error code from unix errno.
+ - fixed lpq status for MSTCPB3
+ - added @ option for user= line to lookup groups in group file
+ - added become_user optimisation and process timeout (thanks to
+ Jeanette Pauline Middelink (middelin@calvin.iaf.nl)
+ - added malloc optimisation in readbraw
+ - released alpha
+ - patches for OSF1 enhanced security from Udo Linauer <ul@eacpc4.tuwien.ac.at>
+ - made level 2 a more useful debug level (less guff)
+ - added "max connections" and "lock dir" options to allow
+ limiting of the number of connections to a service at one time.
+ - released alpha2
+ - updated man pages
+ - released alpha3
+ - added read prediction code for better read performance
+ - released alpha4
+ - minor tuning to receive_smb()
+ - changed the order of mangled stack checking
+ - bug fix in read_predict().
+ - released alpha5
+ - minor search optimisation
+ - fixed keep alive bug in writebraw and in readbraw in the client
+ - released alpha6
+ - disabled writeraw by default pending a bug fix
+ - added profiling code (off by default)
+ - minor delete tuning
+
+
+1.7.06: 4/8/94
+ - OSF1 crypt fix thanks to Udo Linauer <ul@eacpc4.tuwien.ac.at>
+ - ifdef'd EDQUOT in case you don't have it (thanks to Paul Blackman <ictinus@Lake.canberra.edu.au>)
+ - tidied up UNIXERROR stuff to work on more systems.
+ - made Makefile more sophisticated and added "make revert"
+
+1.7.07: 4/8/94
+ - fixed one line fatal bug in receive_smb. Thanks to bruce@pixar.com
+
+1.7.08: 2/9/94
+ - initgroups call for SCO from lance@fox.com
+ - code cleanups from cap@isac.hces.com (Simon Casady)
+ - use full pathname in print command construction
+ - ISC includes fix from Martin Tomes <mt00@ecl.etherm.co.uk>
+ - added GID_TYPE define to cope with ultrix. Thanks to
+ brad@cac.washington.edu
+ - added umask call to main in server
+ - fixed several minor problems with the max connections
+ code. Thanks to lehmann@klizix.mpi-stuttgart.mpg.de (Arno Lehmann).
+ - fixed filetime in writeclose. Thanks to Andreas Bahrdt
+ <100321.2431@compuserve.com>
+ - df fix for large disks from Andreas Bahrdt
+ - getpwanam support from horn@mickey.jsc.nasa.gov
+ - clean name change from Bernd Esser
+ <be@syli30.physik.uni-bonn.de>
+ - released alpha1
+ - more locking changes to fix Excel problem
+ - released alpha3
+ - another minor locking change
+ - smarter masking in the locking code. Excel now apparently works.
+ - minor FAQ updates
+ - changed max connections refusal error to access denied.
+ - added queue command to client to show the print queue
+ - changed some print queue reporting stuff
+
+1.8.0: 14/10/94
+ - added international chars to valid_dos_char(). Thanks
+ to Daniel.Grandjean@dgr.epfl.ch
+ - volume label fix
+ - released alpha1
+ - important off by 4 fix in the server
+ - readbraw size adaption in the client
+ - released alpha2
+ - wait3 cast for NeXt fixed. Thanks to dbrandon@politics.tamu.edu.
+ - man page fix for max xmit. Thanks to mmoore@wexford (Mike Moore)
+ - is_8_3() fixes from Jochen Roderburg <Roderburg@rrz.Uni-Koeln.DE>
+ - list_match() fix from jkf@soton.ac.uk
+ - statfs3 fix for BSDI from dan@supra.com
+ - changed file open/close/read in server in preparation for mmap()
+ based IO.
+ - added mmap() support for reading files in the server. Optional
+ at compile time. Thanks to suggestion from Roger Binns <rogerb@x.co.uk>
+ - mmap bug fixes
+ - added __SAMBA__ name in nmbd
+ - major changes for support of lanman2 and long filenames from
+ Jeremy Allison (jeremy@netcom.com)
+ - lseek optimisation. Thanks to Linus Torvalds.
+ - released alpha4
+ - date patches for lanman2 from Jeremy Allison
+ - added protocol aliases to handle WfWg (untested)
+ - allow for zero params or data in reply_trans2
+ - small lanman2 patches from jeremy
+ - more prototype additions for clean compilation
+ - postscript patches from tim@fsg.com
+ - more lanman2 patches from Jeremy
+ - added null ioctl support
+ - kanji patches from fujita@ainix.isac.co.jp
+ - released alpha6
+ - disallowed null password access (thanks to Birger Kraegelin krg@iitb.fhg.de)
+ - Makefile fix for ultrix from andrew@d2bsys.demon.co.uk (Andrew Stirling)
+ - added per-service mangled names
+ - totally re-vamped loadparm.c
+ - added "mangling char" parameter
+ - released alpha7
+ - added "default case = lower|upper" service option
+ - change mangling char to a service parameter
+ - ultrix enhanced security patch from steven@gopher.dosli.govt.nz
+ - more changes to loadparm.c
+ - printer name always set in [printers]
+ - string_free() fix thanks to jef_iwaniw@pts.mot.com
+ - changed group handling to be faster and work for large numbers
+ of groups
+ - added dynamic gid_t type determination
+ - released alpha8
+ - fixed become_user() problem for services with invalid
+ directories
+ - added "invalid users" list on per service basis
+ - fixed pointer problems in alpha8 (thanks to murnaghant@a1uproar.yuppy.rdgmts.MTS.dec.com)
+ - fixed some date setting problems
+ - trans2 fixes from jeremy to stop infinite directory listings of
+ long filenames
+ - "standard input" lpq patch from root@tlspu.demon.co.uk (Adrian Hungate)
+ - changed password checking to check session list and validated ids
+ before user list
+ - split off password functions into password.c
+ - added hosts equiv and rhosts code (thanks to Tim Murnaghan <murnaghant@a1uproar.yuppy.hhl.MTS.dec.com>)
+ - released alpha11
+ - added "newer" command to the client
+ - attempt at aix trapdoor uid workaround
+ - released alpha12
+ - minor trans2 bugfix
+ - added ufc crypt (fast crypt) support. Thanks to suggestion from
+ forrest d whitcher <fw@world.std.com>
+ - socket() fix for getting bcast and netmask thanks to
+ Brian.Onn@Canada.Sun.COM
+ - added beginnings of IPC and named pipe support in the server
+ - changed file structure a bit, creating reply.c
+ - finished print queue support for lanman1
+ - changed default protocol to LANMAN2
+ - released alpha13
+ - logged IPC connects at a higher debug level
+ - added netgroup support to hosts equiv search
+ - disallowed root access though hosts.equiv (thanks to Colin.Dean@Smallworld.co.uk)
+ - kanji and password handling fixes from fujita@ainix.isac.co.jp
+ - several bug fixes for lanman and other things from
+ esser@pib1.physik.uni-bonn.de (Bernd Esser)
+ - updated man pages, README and announce files.
+ - released 1.8.00alpha1
+ - reply_close() time change fix from Andreas Bahrdt <100321.2431@compuserve.com>
+ - added valid users list to compliment invalid users list.
+ - aix fixes from tomc@osi.curtin.edu.au (Tom Crawley)
+ - changed testparm output format
+ - support for getting time from the server (nearly untested)
+ - fixed device type error for wild device ????
+ - fixed groups problem when in 0 groups
+ - more IPC fixups
+ - added support for "net view \\server" command to list
+ available services (like browsing)
+ - released 1.8.00alpha2
+ - changed port choice for nmbd -L
+ - added -L option to client to view share list on a host
+ - bug fixes for NetShareEnum code
+ - added "server string" option
+ - changed default print file name to include remote machine name.
+ - added hooks for browsing in nmbd
+ - added browsing to nmbd
+ - freebsd fixed from Steve Sims SimsS@Infi.Net
+ - got rid of tell()
+ - added subnet browsing with the S option in lmhosts
+ - made smbd prime nmbd with a 1 byte dgram
+ - added REUSADDR to open_socket_in() thanks to peter@ifm.liu.se
+
+
+1.8.01: 18/10/94
+
+ - auto add group "LANGROUP" if no group specified in nmbd
+ - made nmbd more responsive at startup
+ - lots of cleanups and consistancy checks
+ - added -C option to nmbd to set "machine comment".
+ - fixed postscript option
+ - force print_file in print_open()
+ - restructured the browsing a little
+ - casesignames fix for lanman-dos
+ - auto-load home directory from session setup
+ - changed to StrnCpy() for safety
+ - fixed "out of file descriptors" bug in the client (a WfWg bug?)
+
+
+1.8.02: 22/10/94
+ - fixed uppercase username problem
+ - added "hide dot files" option
+ - changed auto debug log in nmbd
+ - added LMHOSTS to Makefile
+ - added M flag in lmhosts to specify own netbios name
+ - added "load printers" option to auto-load all printers
+ - substitution of %p in lpq command
+ - substitution of %h and %v in server string and -C option of
+ nmbd
+ - string substitions substitute all occurances of a pattern
+ - added casesignames global option
+ - fix for man pages thanks to David Gardiner <dgardine@cssip.edu.au>
+ - changed debug options a bit
+ - added default for lpq command and lpr command
+ - changed default shell path to /bin/sh
+ - forced lpq under api to run as root - should speed things up
+ - added "group" option to force group of a connection
+ - added "read list" and "write list" options
+ - added max mux option - seems to fix NT browsing?
+ - added "mangled map" option thanks to
+ Martin.Tomes@uk.co.eurotherm.controls
+ - separated mangling functions into mangle.c
+ - allowed all dos chars in mangled names
+ - apollo changes from Helmut Buchsbaum <buc@eze22.siemens.co.at>
+ - password changing code from Bob Nance <Bob.Nance@niehs.nih.gov>
+ it doesn't quite work yet, but it's a start (disabled by default)
+
+
+1.8.03: 25/10/94
+ - made auto loaded services browsable as per default service
+ so you can hide homes but keep home directories.
+ - changed check_name() to handle "direct to network" printing
+ - auto 3 minute deadtime if all connections are closed. This
+ prevents restart when polling the print queue.
+ - fix for newer command in client from Rich-Hoesly@uai.com
+ - changed connection recording method
+ - added the program smbstatus
+ - changed timeout mechanism
+ - "null passwords" option from Pim Zandbergen <pim@cti-software.nl>
+ - made new files with casesignames=False set their case to the default
+ case.
+ - fixed problem of uppercasing first letter of printers in printcap
+ - debug level fixes in trans2 from jimw@PE-Nelson.COM (Jim Watt)
+ - made null printer default to lp
+
+1.8.04: 27/10/94
+ - added OS2.txt from riiber@oslonett.no
+ - another "auto services" fix. A silly strtok() bug :-(
+ - fixed the status locking and max connections (broken in 1.8.03)
+ - released alpha1
+ - added gets_slash so lines can be continued in smb.conf and
+ lmhosts
+ - browse list bugfix
+ - default to "load printers=yes"
+ - rewrote pcap.c
+ - intergraph bugfix from tarjeij@ulrik.uio.no
+ - changed properties flags in nmbd (to fix NT print browsing)
+ - allowed very long lines in printcap parsing.
+
+1.8.05: 28/10/94
+ - lanman2 fix from Jeremy
+
+1.9.00: 22/1/95
+ - only add home if not already there.
+ - added ulogoffX support
+ - PTR_DIFF() cleanups
+ - fixed a bug that caused STATUS..LCK to grow very large
+ - changed mangling to handle names ending in . a little better
+ - added "strip dot" option
+ - SGI and setgroups() fix from bill@sg25.npt.nuwc.navy.mil
+ - fixed password preservation in password_ok() (again?)
+ - unink fix from emer@vssad.enet.dec.com (Joel S. Emer)
+ - changed username part of spool filename to max 10 chars (from 6)
+ - magic script fix from beverly@datacube.com (Beverly Brown)
+ - reply_special() fix from Peter Brouwer <pb@apd.dec.com>
+ - stopped nmbd from listening on 138. It didn't seem to help much.
+ - clix fixes from ttj@sknsws61.sjo.statkart.no
+ - fixed select behaviour under Linux
+ - man page fix from Robin Cutshaw <robin@intercore.com>
+ - ISC block size fix from ralf@rbsoft.sdata.de (Ralf Beck)
+ - ISC fixes from Martin.Tomes@controls.eurotherm.co.uk
+ - attrib bit fix in smbclient (pointed out by Rich-Hoesly@uai.com)
+ - japanese extensions from fujita@ainix.isac.co.jp (Takashi
+ Fujita) and ouki@gssm.otuska.tsukuba.ac.jp.
+ - SCO patches from Stephen.Rothwell@pd.necisa.oz.au
+ - changed the system commands to redirect stderr
+ - changed default printername to service name for all print ops
+ - added ability to delete print queue entries
+ - added warning if you try to print without -P in smbclient
+ - INTERACTIVE patches from cardinal@settimo.italtel.it
+ - patch to handle spaces in group names from GJC@vax1.village.com
+ (GEORGE J. CARRETTE)
+ - lockingX fix from stefank@esi.COM.AU (Stefan Kjellberg)
+ - some fairly radical changes to filename handling. We can now
+ handle mixed case filenames properly
+ - released alpha2
+ - added sysv printing support and improved bsd support
+ - changed the user that does print queues and lprm jobs
+ - return code support in the client from doylen@nbslib.isc-br.com (Doyle Nickless)
+ - added "strict locking" option. Defaults to no.
+
+ - added -I switch to nmbd
+ - fixed DEV bug thanks to Dirk.DeWachter@rug.ac.be
+ - use pw_encrypt() for shadow passords in Linux (from begemot@begemot.iko.kharkov.ua (Dmitry Gorodchanin))
+ - disabled read prediction by default
+ - added varient handling code to ipc.c for printQ and printDel.
+ - released alpha5
+ - AUX patches from root@dolphin.csudh.edu
+ - struct timeval fix from gkb1@york.ac.uk
+ - patches to merge ISC and INTERACTIVE from pim@cti-software.nl
+ - changed to "printing ="
+ - fixed problem with long print queues.
+ - fixed node status request in nmbd to go to non bcast
+ - made default path in services /tmp if not specified
+ - added %u in passwd program
+ - fixed up the password changing code for Linux
+ - no guest sess setup when user level security
+ - changed timeouts to kill dirptrs so cdroms can be unmounted
+ - added auto-reload of smb.conf if changed
+ - added SIGHUP to reload the config files
+ - added -M option to nmbd to search for a master browser
+ - added support for continue bit in trans2findnext
+ - changed to dynamic strings in some more structures
+ - changed default deadtime to 30 minutes
+ - cleaned up the memory swapping code a bit
+ - updated the man pages somewhat
+ - added %m and %u in the "path=" of services
+ - released alpha6
+ - simple testing and fixups for solaris, sunos, aix, ultrix and
+ osf/1 (this is all I have access to).
+ - fixed chdir bug
+ - added hashing to cnum selection
+ - released alpha7
+ - fixed printing bug
+ - reduced chance of "hung" smbd with dead client
+ - fixed do_match() bug (recently introduced)
+ - released alpha8
+ - nameserver fix from W.J.M.vGeest@et.tudelft.nl (W.J.M. van Geest)
+ - rewrote readbraw to try and overlap reads with writes
+ - client optimisations
+ - rewrote getwd cache and enabled it by default
+ - added partial smb packet reads (hopefully faster writes)
+ - added log file and log level options (with subs)
+ - added "read size" option
+ - tried setting some more socket options
+ - can use subs in "config file=" and will auto-reload
+ - added "include" options, with some subs
+ - finally got print manager working with NT
+ - auto-respond in nmbd to non-broadcast (auto WINS server, no -A
+ needed)
+ - released alpha10
+ - auto-delet unused services when reloading
+ - fixed auto-deletion
+ - fixed long names in printing
+ - fixed double loading of services file
+ - added printer file name support
+ - reformatted man pages for better www conversion
+ - renamed to 1.9.00.
+ - added support for RNetServerGetInfo and NetWkstaGetInfo API's
+ - updated the docs a bit
+ - released alpha1
+ - added -M -
+ - changed nmbd announce interval to 10 mins in outgoing packets
+ - hopefully fixed idle timeout reconnects
+ - strupper all command lines in nmbd
+ - added %a substitution for "remote architecture"
+ - added "Samba" protocol (same as lanman2)
+ - added "security = SERVER"
+ - released alpha2
+ - lowercase password fix
+ - fixed connect path length bug (thanks to JOHN YTSENG
+ <jtseng@cory.EECS.Berkeley.EDU>)
+ - added subs on "password server".
+ - fixed printing filename bug from smbclient
+ - disk quotas and hpux printing support from Dirk.DeWachter@rug.ac.be
+ - Makefile patches from pappinm@ayr_srv2.nth.dpi.qld.gov.au
+ - AFS patches from Mike Allard (mgrmja@nextwork.rose-hulman.edu)
+ - fixed grp name = server name problem
+ - man page updates from Charlie Brady (charlieb@budge.apana.org.au)
+ - fixed file search bug by adding "finished" flag
+ - added "max log size". Suggestion from Mark Hastings <mark.hastings@gain.com>
+ - released alpha3
+ - changed the read/write routines to handle partial read/writes
+ - released alpha4
+ - changed "guest account" to per-service
+ - changed so "guest ok" allows access to the guest account,
+ not the "user=" line
+ - changed default readsize to 2048
+ - try bind to 137 in nmbd if possible
+ - added server lookup to -L option in smbclient (gets list of servers)
+ - added -M switch to smbclient for sending winpopup messages
+ - released alpha5
+ - FAQ updates from Paul Blackman ictinus@lake.canberra.edu.au
+
+1.9.01: 23/1/95
+ - changed comment in print Q info to service rather than server comment
+ - fixed smbclient -L to NT when in user level security mode
+ - hopefully finally fixed NT print manager problems
+ - added informative messages during smbclient -M
+ - added node status replies to nmbd
+ - changed the lock offset fixup calculation to be more friendly
+ to dumb lockd daemons.
+ - added sigbus and sigsegv handlers to catch any silly errors and
+ print a message
+ - added message receipt to smbd and "message command =" option
+
+1.9.02: 25/1/95
+ - added argv/argc mangling for people who start the server the
+ wrong way.
+ - some man page updates
+ - added "revalidate" option
+ - added hosts allow/deny access check to messaging access
+ - added timeouts in the client
+ - added check for existance of smbrun binary
+ - man page updates from Colin.Dean@Smallworld.co.uk
+ - freebsd patches from dfr@render.com
+ - added mask sanity check in SMBsearch
+ - added more useful substitutions (%S, %P, %I and %T)
+ - added "exec =" option to execute commands on each connection
+
+1.9.03: 13/3/95
+ - added "socket options" option
+ - close base fd's (0,1 and 2)
+ - use dup(0) for inetd operation
+ - better detection of is_daemon
+ - hopefully finally fixed silly put bug that gave the wrong
+ date on files.
+ - fixed segv in readbraw bug
+ - added improved checing for invalid (or null) print file name
+ - several patches from ad@papyrus.hamburg.com (Andreas Degert)
+ - fixed slow logout bug in smbclient
+ - fixed automounter problems
+ - added subs on lock dir
+ - BSDI patch from John.Terpstra@Aquasoft.com.au
+ - added separate nmb and smb logfile entries in the Makefile
+ - fixed return code error in open calls
+ - added simple status display of printer in lpq parsing
+ - rewrote the directory handling to avoid seekdir (added dir.c)
+ - added uid=65535 check (thanks to grant@gear.torque.net)
+ - enhanced transfer_file() to add header (used in readbraw)
+ - reply_special bugfix from ferret@pc8871.seqeb.gov.au
+ - added HAVE_PATHCONF
+ - RiscIX patches from Jim Barry <jim@ilp.com> and
+ Charles Gay-Jones <charlie@ilp.com>
+ - CLIX patches from ttj@sknsws61.sjo.statkart.no
+ - fixed aix lpq parser from kvintus@acd.com
+ - added substitutions to "include="
+ - M88K_S3 patches from tonyb@plaza.ds.adp.com (Tony D. Birnseth)
+ - fixed mangled stack problem
+ - added code to handle broken readdir() setups on solaris
+ - initgroups() fix from jarit@to.icl.fi
+ - dgux dfree fix from listwork@cloud9.net
+ - dnix support from Peter Olsson <pol@leissner.se>
+ - getgrgid() patch from tpg@bailey.com (Tom Gall)
+ - Makefile patch from obrien@Sea.Legent.com (David O'Brien)
+ - password changing fixes from Dirk.DeWachter@rug.ac.be
+ - minor man page updates
+ - tried to enhance the read prediction code a little bit
+
+1.9.04: 16/3/95
+ - a bit better handling of global include lists
+ - fixed GSTRING bug in loadparm.c (affected "socket options =")
+ - fixed broken lpq parsing code (recent bug).
+ Thanks to Dirk.DeWachter@rug.ac.be
+
+1.9.05: 20/3/95
+ - improved mget in client to take multiple arguments and default
+ to *.*
+ - socket option fixes for both nmbd and smbd
+ - changed the byteorder handling scheme to be more portable (and
+ faster)
+ - lint cleanups from kast@kcs.planet.net (Robert Kast)
+ - added crude segv, sigbus and sighup recovery to nmbd
+ - rewrote lanman2_match to be closer to NT and WfWg behaviour
+ - Cray support from velo@sesun3.epfl.ch (Martin Ouwehand)
+ - "admin users" patch from Tim Leamy <tcleamy@ucdavis.edu>
+ - released alpha1
+ - added samba.7 man page
+ - no chdir when doing non AS_USER protocols
+ - become_guest() returns true in trapdoor uid system
+ - added more sophisticated segv/sigbus reporting (Linux only)
+ - released alpha2
+ - minor code cleanups (output of -Wall)
+ - smbprint fix from James Dryfoos <dryfoos@ll.mit.edu>
+ - improved testparm a little
+ - updated INSTALL.txt a little
+
+
+1.9.06: 21/3/95
+ - added %S substitution to users, valid users and invalid
+ users. This is useful for [homes].
+ - split off printing routines into printing.c and more dir
+ commands into dir.c
+ - postexec patch from jpm@gin.Mens.DE (Jan-Piet Mens)
+ - smbstatus updates from jpm@gin.Mens.DE (Jan-Piet Mens)
+ - reload sighup after use
+ - fixed name ptr offset bug
+ - added %f in print commands
+ - fixed byte ordering in nmbd which caused browsing to fail in
+ 1.9.05
+
+1.9.07: 22/3/95
+ - important directory listing fix
+ - allowed path= in [homes] section
+ - printer status patches from Dirk.DeWachter@rug.ac.be
+
+1.9.08: 24/3/95
+ - fixed . and .. in root dir for lanman2
+ - better default comment in [homes]
+ - added time stamping to directory entries
+ - check directory access at connection time
+ - rlimit code from loebach@homer.atria.com (Thomas M. Loebach)
+ - fixed home dir default comment
+ - totally rewrote dptr handling to overcome a persistant bug
+ - added [globals] as well as [global]
+
+1.9.09: 30/3/95
+ - fixed static string bug in nmbd
+ - better null password handling
+ - split CFLAGS in Makefile
+ - fixed typo in smbclient messaging
+ - made home dir not inherit path from [global]
+ - standard input printing patch from xiao@ic.ac.uk
+ - added O_CREAT to all print opens (bug in Win95)
+ - use /proc for process_exists under Linux and solaris
+ - fixed another segv problem in readbraw
+ - fixed volume label problem
+ - lots of changes to try and support the NT1 protocol
+ - released alpha1
+ - fixed session setup bug with NT in NT1 protocol
+ - released alpha2
+ - fixed "get" bug in smbclient that affected NT3.5
+ - added SO_KEEPALIVE as a default socket option in smbd
+ - changed some error codes to match those that NT 3.5 produces
+ - updated trans2 with some new calls for Win95 and WinNT (better
+ long file support)
+ - released alpha3
+ - fixed "nmbd -D -b" timeouts
+ - added IS_LONG_NAME flag to getattr in NT1
+ - added the NT qfileinfo trans2 commands
+ - merged qpathinfo with qfileinfo
+ - changed idling technique to try and be more friendly to
+ clients
+ - merged setfileinfo with setpathinfo and updated them with the NT fns
+ - improved read prediction a lot
+ - added read prediction to readbraw
+ - improved fault reporting (last packet dump)
+
+1.9.10: 30/3/95
+ - fixed read prediction+readbraw bug for read/write files
+
+1.9.11: 9/4/95
+ - fixed trans2 qpathinfo bug
+ - fixed bug with % in service name when doing print queue requests
+ - default readsize now 16K
+ - minor read prediction changes
+ - fixed status initialisation in print queue reporting
+ - fixed const compile problem for hpux
+ - minor SMBread fix from Volker Lendecke <lendecke@namu01.gwdg.de>
+ - removed space after -P in print commands (for fussy systems)
+ - disabled level2 of setfilepathinfo
+ - changed to a single read dir model, saving all dir names in
+ the Dir structure
+ - disabled NT protocols in the client due to reported problems
+ - fixed QUERY_FS_VOLUME_INFO which caused Win95 to hang on drive
+ properties
+ - minor lseek bug fix
+ - fixed up keepalives
+ - new timezone handling code (hopefully better!)
+ from steve@qv3pluto.LeidenUniv.nl
+ - BSDI interface patch from jrb@csi.compuserve.com
+ - gettimeofday changes from Roger Binns <rogerb@x.co.uk>
+ - added smbrun option
+ - added "root preexec" and "root postexec" options
+
+1.9.12: 12/4/95
+ - hopefully fixed some recently introduced NT problems
+ - fixed a unlink error code problem
+ - minor testparm fix
+ - fixed silly error messages about comments in config files
+ - added "valid chars" option for other languages
+
+1.9.13: 28/4/95
+ - patches from David O'Brien (obrien@Sea.Legent.com) improving the
+ netgroup suport, and adding the "map archive" option, as well as
+ other minor cleanups.
+ - tried to add info level 3 and 4 support for OS/2
+ - default deadtime set to 0 as in docs
+ - cleaned up the trans2 code a little
+ - cleaned up the Makefile a little
+ - added charset.c and charset.h
+ - expanded "valid chars" option to handle case mapping
+ - lots of changes to try and get timezones right
+ - released alpha1
+ - win95 fixups
+ - released alpha2
+ - added %H substitution (gives home directory)
+ - nameserv.c cleanups and minor bug fixes
+ - redid the browse hook logic
+ - fixed daylight saving time offset for logfile messages
+ - added name cacheing to nmbd
+ - added send counts to node status in nmbd
+ - added STRICT_TIMEZONES compile time option (very computationally
+ expensive)
+ - removed the partial read code
+ - cleaned up the permission checking a lot
+ - added share modes (DENY_READ, DENY_WRITE, DENY_ALL, DENY_NONE,
+ DENY_FCB and DENY_DOS)
+ - added "share modes" option
+ - cleaned up the file open calls
+ - released alpha4
+ - fixed important one line bug in open_file()
+ - trans2 client fix from lendecke@namu01.gwdg.de
+ - netgroup patche from David O'Brien (obrien@Sea.Legent.com)
+ - case sensitive fix from lenneis@statrix2.wu-wien.ac.at (Joerg Lenneis)
+ - got long filenames working from Win95 dos prompt
+ - added "workgroup=" option
+ - added "username map" option including multiple maps, group maps etc
+ - fixed password server for NT1 protocol and made it more robust
+ - changed unix_mode() to add IWUSR to read-only directories. This
+ is much closer to what clients expect.
+ - added preservation of unused permission bits when a chmod() is
+ called from a client.
+ - made static those fns that could be
+ - fixed typo in access.c (thanks to Andrew J Cole
+ <A.J.Cole@cbl.leeds.ac.uk>)
+ - added %d substitution for process id
+ (thanks to lenneis@statrix2.wu-wien.ac.at (Joerg Lenneis))
+ - changed share error code to ERRbadshare
+ - added locked files list to smbstatus if share modes is enabled
+ - changed DENY_DOS to allow read by other tasks
+ - added shared_pending checks to server
+ - preserverd all possible permission bits during a chmod, and
+ fixed a trans2 chmod bug
+ - open /dev/null to use up first 3 fds, in an attempt to stop rogue
+ library routines from causing havoc
+ - fixed NT username problem when in server security
+ - added "force user" and "force group" options
+ - cleaned up some of the IPC calls a bit
+ - added writeraw to the client and cleaned up write raw in the server
+ - osf1 big-crypt bugfix from Udo Linauer <ul@eacpc4.tuwien.ac.at>
+ - hopefully better disk-full checking
+ - next uid bugfix from patrick@graphics.cornell.edu
+ - changed share modes so lock directory doesn't need to be world
+ writeable
+ - enabled write-raw by default
+ - added server_info() in client
+ - added level checks in some ipc calls
+ - added defines for the important timeouts in local.h
+ - added print queue deletion to smbclient (untested)
+ - removed the sysconf() calls
+ - optimised writebraw a bit
+ - fixed some file deletion problems
+ - added total_data check for extended attribs in trans2 (for OS/2)
+ - fixed broadcast reply bug in nmbd
+ - added careful core dumping code
+ - added faster password level searches (suggestion
+ by lydick@cvpsun104.csc.ti.com (Dan Lydick))
+
+
+1.9.14: 22/9/95
+ - fixed up level 3 and 4 trans2 requests for OS/2
+ - minor optimisations in a few places
+ - cleaned up the closing of low fds a bit
+ - added SO_REUSEADDR to socket as a daemon
+ - override aDIR bit for directories in dos_chmod()
+ - SGI5 fixes from ymd@biosym.com (Yuri Diomin)
+ - bsize sanity check and removed sunos force to 1k
+ - force the create mode to be at least 0700
+ - SCO and freebsd include changes from Peter Olsson
+ <pol@leissner.se>
+ - check with FQDN in access.c (thanks to Arne Ansper <arne@ioc.ee>)
+ - default broadcast for dnix from Peter Olsson <pol@leissner.se>
+ - solaris patches from Ronald Guilmette <rfg@segfault.us.com>
+ - added EXDEV handling
+ - small AFS Makefile patch from mgrlhc@nextwork.rose-hulman.edu
+ - hopefully fixed the Win95 dates to work in other than my
+ timezone
+ - attempted alignment fixups (to speed up memcpy)
+ - added some DCE/DFS support (thanks to Jim Doyle <doyle@oec.com>)
+ - added fix so that root doesn't have special privilages to open
+ readonly files for writing (but admin users do). This fixes the MS
+ office install problem.
+ - fixed trans2 response bug in client
+ - got dual names working for NT
+ - enabled lock_and_read in NT protocol
+ - added %L macro for "local machine"
+ - changed dfree reporting to use "sectors per unit"
+ - fixed "not enough memory" bug in MS print manger by limiting
+ share name length in share enum.
+ - "short preserve case" option from Rabin Ezra (rabin@acm.org)
+ - added archive option to client
+ - changed openX in client to be able to open hidden and system files
+ - added "sync always" option
+ - rewrote writebmpx and readbmpx
+ - added auto string_sub_basic to all loadparm strings
+ - lots of nmbd fixups (add registration, refresh etc)
+ - released alpha1
+ - added smbtar patches from Ricky Poulten (poultenr@logica.co.uk)
+ - added a lpq cache and the "lpq cache time" option
+ - released alpha 2
+ - sun includes fix from Kimmo Suominen <kim@deshaw.com>
+ - change nmbd -L lookup type to workstation from server
+ - added min print space option
+ - added user and group names to smbstatus (thanks to
+ davide.migliavacca@inferentia.it)
+ - fixed %f in print command bug (thanks to huver@amgraf.com)
+ - added wildcard support to SMBmv
+ - misc patches from David Elm (delm@hookup.net)
+ - changed default of "share modes" to yes
+ - changed default of "status" to yes
+ - aix qconfig parsing from Jean-Pierre.Boulard@univ-rennes1.fr
+ - more long_date fixups
+ - added wildcards to nmbd
+ - extensive changes to ipc.c and miscellaneous other changes
+ from ad@papyrus.hamburg.com (Andreas Degert). Should especially
+ help OS/2 users
+ - added name release to nmbd
+ - relesed alpha4
+ - fixed "SOLARIS" to SUNOS5 in Makefile
+ - several minor fixups to get it to compile on aix, osf1, ultrix,
+ solaris and sunos
+ - released alpha5
+ - minor bug fixes and cleanups in ipc.c
+ - fixed "only user" bug
+ - changed lpq to report guest queue entries as sesssetup_user to
+ allow for deletion by windows
+ - released alpha6
+ - added __SAMBA__ as type 0 in nmbd (was type 20)
+ - fixed null print job bug
+ - added 8 char warnings to testparm and smbclient
+ - changed to 8 char limit for names in pcap.c
+ - added linked list of config files to detect all date changes
+ that require a reload
+ - simplified pcap guessing heuristics
+ - added space trimming to the name mapping
+ - updated Get_Pwnam to add allow_change field for username mapping
+ - fixed MemMove bug (thanks to mass@tanner.com (Massimo
+ Sivilotti))
+ - released alpha7
+ - rewrote MemMove to be a little more efficient
+ - ipc va_arg bug fix from djg@tas.com (Dave Gesswein)
+ - added check for illegal chars in long filenames
+ - fixed name cache init bug in nmbd
+ - Convex patches from Victor Balashov <balashov@cv.jinr.dubna.su>
+ - timestring() bugfix from staale@spacetec.no
+ - changed %H to give path of forced user if one is set
+ - added quoting to smbclient to allow spaces in filenames
+ - convex and other patches from Ulrich Hahn
+ <ulrich.hahn@zdv.uni-tuebingen.de>
+ - released alpha8
+ - fixed rename directory bug
+ - nmbd wins fix from Maximilian Errath <errath@balu.kfunigraz.ac.at>
+ - client and AFS changes + password.c reorganisation + "more" and
+ "pwd" commands in client from Todd j. Derr (tjd@smi.med.pitt.edu)
+ - fixed several nmbd bugs
+ - released alpha9
+ - fixed another "cd" bug in smbclient
+ - password encryption from Jeremy Allison
+ - added "passwd chat" option and chat interpretation code
+ - added "smb passwd file" option
+ - released alpha10
+ - cleaned up chgpasswd.c a little
+ - portability changes to the encryption handling code
+ - added password encryption to smbclient
+ - fixed a share level security encryption bug
+ - added "ENCRYPTION.txt" document
+ - released alpha11
+ - added code to detect a password server loop
+ - fixed typo in chkpath in client.c that broken cd (again)
+ - LINUX_BIGCRYPT from marsj@ida.liu.se
+ - AFS password fixup from jbushey@primenet.com (Jeffrey G. Bushey)
+ - iso/8859-1 charcnv patches from Dan.Oscarsson@malmo.trab.se
+ - strtok/user_in_list fix from roderich@nodebonn.muc.bmw.de
+ - NETGROUP patches from J.W.Schilperoort@research.ptt.nl
+ - trim_string patch from J.W.Schilperoort@research.ptt.nl
+ - fixed problem with files with no extension getting mixed up
+ - ipc bugfix for print job deletion from Rainer Leberle <rleberle@auspex.de>
+ - released alpha12
+ - pwlen fix in NETGROUP from Andrew J Cole <A.J.Cole@cbl.leeds.ac.uk>
+ - lots of uid and encryption changes from Jeremy Allison. WinDD
+ should now work.
+ - released alpha13
+ - fixed max_xmit bug in client
+ - select fix in server (fixed critical drive errors under ISC)
+ - released alpha14
+ - wildcard fix from Jeremy
+ - changes to make IPC code more robust
+ - small select loop change to reduce cleaning of share files
+ - vtp, altos and mktime patches from Christian A. Lademann
+ <cal@zls.com>
+ - EEXIST bugfix in server.c
+ - changed mangled map to apply in all cases
+ - released alpha15
+ - fixed fcb open permissions (should mean apps know when a file is
+ read only)
+ - released alpha16
+ - client help formatting fix and docs fix from Peter Jones
+ <thanatos@drealm.org>
+ - added a directory cache
+ - use /proc whenever possible for pid detection
+ - TCSANOW fix in getsmbpasswd from roderich@nodebonn.muc.bmw.de
+ - fixed default printing mode for sysv systems
+ - make client always expand mask
+ - more minor IPC fixups
+ - pyramid makefile entry from jeffrey@itm.org
+ - client fixups for passlen, maxvcs and session redirect from
+ Charles Hoch <hoch@hplcgh.hpl.hp.com>
+ - finally fixed important IPC bug (varargs bug with int16)
+ - quota patches from Dirk.DeWachter@rug.ac.be
+ - print queue cache changes (per service) and print queue priority
+ additions from Dirk.DeWachter@rug.ac.be
+ - new japanese patches (incomplete) from
+ fujita@ainix.isac.co.jp (Takashi Fujita)
+ - moved a lot more functions into system.c via wrappers
+ - changed a lot of the connection refused error codes to be more
+ informative (or at least different)
+ - released alpha17
+ - changed error return code from cannor chdir() in make_connection
+ - fixed realloc() bug in printing.c
+ - fixed invalid username bug in sesssetupX
+ - released alpha18
+ - made default service change name to asked for service (idea
+ from Ian McEwan <ijm@doc.ic.ac.uk>)
+ - fixed "guest only" bug
+ - sambatar patches from Ricky
+ - printing.c patches from Dirk.DeWachter@rug.ac.be
+ - rewrote become_user()
+ - sunos5 patch from Niels.Baggesen@uni-c.dk
+ - more japanese extensions patches from fujita@ainix.isac.co.jp
+ - released alpha20
+ - added force_user to conn struct
+
+
+1.9.15: 14/11/95
+ - removed bcast override from workgroup announce in nmbd
+ - aix patch, added NO_SYSMOUNTH, from
+ lionel leston <102624.346@compuserve.com>
+ - quick fix in lp_string() to try and stop some core dumps
+ - added uid cache in connections structure
+ to make user level security faster
+ - changed dos_mode() to show read-only on read-only shares only if
+ user w bit not set
+ - added check to stop exit_server() looping
+ - core dump fix in string_sub()
+ - fix client bug for long dirs in NT1 mode.
+ Thanks to Erwin Authried (erwin@ws1.atv.tuwien.ac.at)
+ - switched to a safer (but probably slower) readbraw implementation
+ - released p1
+ - readbraw fix from Stefaan.Eeckels@eunet.lu
+ - fixed groups bug when user is in 1 group
+ - fixed NT1 dir bug
+ - changed default protocol in client to NT1
+ - changed trans2 to not return both names in long listing if long
+ name is 8.3
+ - made stat of "" return RONLY if not writeable drive
+ - wrapped strcpy() to stop nulls propogating (hack)
+ - made rename and unlink look at share locks on file
+ - clitar memory leak fix from jjm@jjm.com
+ - added -p option to smbstatus to list smbd processes
+ - added rename to the client
+ - released p2
+ - fixed SMBmv for case where the destination exists
+ - man page patch from michal@ellpspace.math.ualberta.ca (Michal Jaegermann)
+ - once again redid the time handling, but finally explained what
+ is going on, this is written up in TIME.txt. The "kludge-GMT" used
+ by NT is a bastard and led to a lot of the confusion
+ - kanji patch from fujita@ainix.isac.co.jp (Takashi Fujita)
+ - is08859-1 patches from eauth@mail.cso.co.at
+ - starting rewriting nmbd, new nmbd is nmbd2, old one still around
+ for time being
+ - released p3
+ - rewrote more of nmbd2 to use new structures
+ - CLIX patches from Jason.J.Faultless@bechtel.btx400.co.uk
+ - DirCacheFlush() bugfix from Michael Joosten
+ <joost@ori.cadlab.de>. This bug explains a lot of the crashes.
+ - fixed a bug in ChDir() that caused reversion to / in some
+ situations
+ - ipc fix from Magnus Hyllander <mhy@os.se>
+ - released p4
+ - smbpasswd fix from Jeremy
+ - compilation fixes from Magnus Hyllander <mhy@os.se>
+ - added NetServerEnum to ipc.c (needed for master browser stuff)
+ - Makefile fix from Gunther Mayer <gmayer@physik.uni-kl.de>
+ - cleanups for clean compile on several OSes
+ - added browse mastering code
+ - started integration with smb.conf for nmbd2
+ - released p5
+ - fixed death_time (should be t+ttl*3)
+ - fixed non-removal of dead servers
+ - added smbstatus -u patch from oskarh@spornet.is (Oskar Hannesson)
+ - NETGROUP fix from J.W.Schilperoort@research.kpn.com
+ - select and NO_SETGROUPS patches from lennylim@netcom.com (Lenny
+ Lim)
+ - added LINKS_READ_ONLY define in dos_mode() for LM/X
+ compatability
+ - "dir a.c" bug fixed thanks to roderich@nodebonn.muc.bmw.de
+ (Roderich Schupp)
+ - job cancel fix in client from peo@mtek.chalmers.se
+ - changed nmbd2 to nmbd
+ - fixed "dir a*" under trans2 lookups
+ - added StrnCaseCmp()
+ - updated docs a bit for new browsing stuff
+ - updated INSTALL.txt
+ - hopefully fixed server level security with WfWg
+
+1.9.15 (patches):
+ - major/minor fix for solaris from Jeroen Schipper
+ <Jeroen.Schipper@let.ruu.nl>
+ - fixed critical bug in directory listings
+ - released p1
+ - fixed one of the causes of "out of memory" while browsing
+ - fixed manpage install script (Paul Blackman)
+ - added DNS failures to name cache
+ - fixed writebmpx bug (affects OS/2)
+ - misc OS/2 fixes, mostly for EA handling
+ - added SMBcopy
+ - added "max ttl" option
+ - arch detection patch from Bas Laarhoven <bas@vimec.nl>
+ - released p2
+ - another OS/2 fix - the level 4 getpathinfo for EAs
+ - added "alternate permissions" option
+ - changed client to parse destination names into name + domain
+ - fixed problem with PrimaryGroup and lmhosts loading
+ - added domain master ability to nmbd
+ - added "domain master" option
+ - added "domain controller" option and code
+ - pwd fix to client from Erik Devriendt (de@te6.siemens.be)
+ - fixed problem in smbmv that led to ar not working in mks
+ - added transs2
+ - released p3
+ - updated email addresses
+ - fix for innetgr from Olaf Seibert (rhialto@polder.ubc.kun.nl)
+ - client translate fix from bandc@dircon.co.uk
+ - netbsd bcast fix from from Olaf Seibert (rhialto@polder.ubc.kun.nl)
+ - syslog code from Alex Nash <alex@fa.tca.com>
+ - strip dot fix from Arne Ansper <arne@ioc.ee>
+ - added addtosmbpass + man page from
+ michal@ellpspace.math.ualberta.ca (Michal Jaegermann)
+ - pcap fix for AIX from Jon Christiansen <jchristi@sctcorp.com>
+ - fixed servertype bug in remote announcements
+ - fixed up illegal name checks (should also be faster)
+ - kanji patches from fujita@ainix.isac.co.jp (Takashi Fujita)
+ - fixed bug handling non-encrypted passwords
+ - released p4
+ - fixed makefile for addtosmbpass
+ - DCE/DFS fixes from John Brezak (brezak@ch.hp.com)
+ - client patch for partial command matching from Andrew Wiseman
+ <bandc@dircon.co.uk>
+ - made is_8_3() handle full paths
+ - rewrote open_file_shared() with help from Charles Hoch
+ <hoch@hplcgh.hpl.hp.com>
+ - changed syslog to handle interactive programs
+ - fixed syslog problem with full path in argv[0]
+ - illegal name fixup for kanji from fujita@ainix.isac.co.jp
+ - fixed server level security to allow fallback to encryption
+ - changed reply_read() and reply_lockread() to ignore clients
+ smb_bufsize in order to handle broken lanman clients
+ - fixed NT wildcard problem with old style programs
+ - man page patches from "John M. Sellens"
+ <jmsellen@watdragon.uwaterloo.ca>
+ - partially documented the "character set" option
+ - changed default for MAXDIR to 64
+ - changed default DPTR idle time to 120
+ - released p5
+ - QNX patches from eldo@invisa.satlink.net (Eldo Loguzzo)
+ - made nmbd use the "max log size" option and changed log handling
+ code a bit
+ - sunos patches, remote protocol (%R) addition and arch detection
+ changes to stop compiler warning from Timothy Hunt <tim@fsg.com>
+ - fixed become_user() bug that led to incorrect permissions in
+ some situations.
+ - released p6
+ - is_8_3() fix from Charles Hoch <hoch@hplcgh.hpl.hp.com>
+ - nmblib bugfix from gmk@mhcnet.att.com (George Kull)
+ - aix pcap fix from Jon Christiansen <jchristi@sctcorp.com>
+ - added explicit sig_pipe() in server.c
+ - added domain logins option (not fully implemented)
+ - added HAVE_GMTOFF code
+ - got rid of PM_MAXLINE
+ - minor client fix from goggi@eflir (Garðar Georg Nielsen)
+ - added SIGCLD_IGNORE for HPUX (from Tor Lillqvist
+ <tml@hemuli.tte.vtt.fi>)
+ - OSF/1 lpq patch from scooter@GENE.COM (Scooter Morris)
+ - NeXT patches from pmarcos@next.com (Paul Marcos)
+ - dstdiff patch to stop infinite loop from Erwin Authried (eauth@cso.co.at)
+ - password server option can now take a list of password servers
+ - patches to let samba run on OS/2 from Jason Rumney <jasonr@pec.co.nz>
+ - added domain logon and logon script suport
+ - SCO openserver 5 patches from Scott Michel <scottm@intime.intime.com>
+ - Makefile changes from Marty Leisner <leisner@sdsp.mc.xerox.com>
+ - chgpasswd changes from Roman Dumych <roman@nyxis.unibase.com>
+ for SVR4
+ - GUEST_SESSSETUP change from David.Chappell@mail.cc.trincoll.edu
+ - released p7
+ - moved SO_REUSEADDR before bind() (thanks to Thomas Bellman
+ <tbe@ivab.se>)
+ - added more flexible GUEST_SESSSETUP to local.h and restored
+ pre-p7 behaviour as default
+ - released p8
+
+1.9.16:
+ - Makefile fix from Marty Leisner <leisner@sdsp.mc.xerox.com>
+ - added %g and %G substitutions
+ - changed IDLE_CLOSED_TIMEOUT to 60
+ - fixed the "admin user" status in domain logons
+ - hpux 10 "trusted security" patches from David-Michael Lincke
+ (dlincke@sgcl1.unisg.ch)
+ - added nmb lookups to client from Adrian Hill <Adrian.Hill@softimage.co.uk>
+ - svr4 pause/resume printing patch from Brendan O'Dea (bod@tyndall.com.au)
+ - fixed master announcement thanks to Luke Leighton <rah14@dial.pipex.com>
+ - changed srcdir usage in Makefile to be friendly to more systems
+ - NT4 alignment patches from Jeremy Allison (jra@vantive.com)
+ - updated share mode code for new spec
+ - minor client bugfix (for smbclient '\\\')
+ - fix for level 260 when magling disabled. From Martin Tomes
+ <Martin.Tomes@ecl.etherm.co.uk>
+ - SMBtranss2 fix for OS/2 from Jeremy Allison
+ - profiles fixup from Timm Wetzel <twetzel@cage.mpibpc.gwdg.de>
+ - man page updates from Dirk.DeWachter@rug.ac.be
+ - nmbsync fix from Andy Whitcroft <andy@soi.city.ac.uk>
+ - Lynx patches from Manfred Woelfel <woelfel@hpesco1.fzk.de>
+ - new smbtar stuff from Ricky
+ - changed to share mode DENY_NONE for tar
+ - fixed -D option of smbclient when in tar mode
+ - added aARCH to open modes
+ - added code to cope with select/read errors
+ - fixed blank browse entries after smb.conf reread
+ - integrated new browse stuff from Luke into ipc.c
+ - added workgroup list to smbclient -L
+ - improved archive attribute handling in close_file() and
+ write_file()
+ - smbtar fixes from Martin.Kraemer@mch.sni.de
+ - Linux quota patch from xeno@mix.hsv.no
+ - try to work around NT passlen2 problem in session setup
+ - released alpha1
+
+NOTE: From now on the cvs.log file will be used to give a complete log of
+changes to samba. This change-log is now obsolete.
+
+
+==========
+todo:
+
+
+64 bit longs and IP addresses may give problems with unsigned longs?
+
+set archive bit whenever file is modified??
+
+fix man page dates
+
+reply only to own workgroup in server enum
+
+patch to compile with g++ and possibly solaris c++
+
+nmbd needs to keep browse list uptodate by talking to the master if it loses
+an election as others may still think its a valid backup and use it to get
+lists.
+
+leftover lock files can end up belonging to non-smbd processes after a reboot.
+
+hosts allow in nmbd
+
+hosts allow cache
+
+add password command in smbclient
+
+drag long filename to samba under os/2 gives short name
+
+document max ttl option
+
+dup/close 0 for getopt?
+
+implement SMBmove ??
+
+add option to print more info about locked files (full path, share name
+etc)
+
+very slow listing CD, perhaps because of order of stat and readdir or add
+masking to opendir?
+
+protocol drop back in client to avoid openX etc.
+
+handle exported fat drives to a long filename capable client
+
+add check for existance of lpq commands etc (use stat?)
+
+get rid of the silly +4 and -4 by removing NBT stuff
+
+write-only shares
+
+document cnvchar stuff
+
+allow smbd to serve user and group lists to win95
+
+document homes behaviour with WinDD
+
+add "hide file = *.o" "hide dir = .Foo*" "show file = xx*" type options.
+
+ALLOW_PASSWORD_CHANGE only compiles/works on some systems
+
+weird foooooooo/open.exe bug on NT
+
+%a detection can't detect Win95 versus WinNT
+
+reverse mangled maps, so (*.html *.htm) works for new files.
+
+install problems with w95. could be some sort of race?
+
+more efficient Files[] structure to handle thousands of open files
+
+lpd stuff:
+ Tony Aiuto (tony@ics.com)
+
+make max disk size local
+
diff --git a/source/client/.cvsignore b/source/client/.cvsignore
new file mode 100644
index 00000000000..49a52f7616d
--- /dev/null
+++ b/source/client/.cvsignore
@@ -0,0 +1 @@
+client_proto.h \ No newline at end of file
diff --git a/source/client/client.c b/source/client/client.c
new file mode 100644
index 00000000000..1da35fcc439
--- /dev/null
+++ b/source/client/client.c
@@ -0,0 +1,3085 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Simo Sorce 2001-2002
+ Copyright (C) Jelmer Vernooij 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+#include "../client/client_proto.h"
+#ifndef REGISTER
+#define REGISTER 0
+#endif
+
+struct cli_state *cli;
+extern BOOL in_client;
+static int port = 0;
+pstring cur_dir = "\\";
+static pstring cd_path = "";
+static pstring service;
+static pstring desthost;
+static pstring username;
+static pstring password;
+static BOOL use_kerberos;
+static BOOL got_pass;
+static BOOL grepable=False;
+static char *cmdstr = NULL;
+
+static int io_bufsize = 64512;
+
+static int name_type = 0x20;
+static int max_protocol = PROTOCOL_NT1;
+
+static int process_tok(pstring tok);
+static int cmd_help(void);
+
+/* 30 second timeout on most commands */
+#define CLIENT_TIMEOUT (30*1000)
+#define SHORT_TIMEOUT (5*1000)
+
+/* value for unused fid field in trans2 secondary request */
+#define FID_UNUSED (0xFFFF)
+
+time_t newer_than = 0;
+static int archive_level = 0;
+
+static BOOL translation = False;
+
+static BOOL have_ip;
+
+/* clitar bits insert */
+extern int blocksize;
+extern BOOL tar_inc;
+extern BOOL tar_reset;
+/* clitar bits end */
+
+
+static BOOL prompt = True;
+
+static int printmode = 1;
+
+static BOOL recurse = False;
+BOOL lowercase = False;
+
+static struct in_addr dest_ip;
+
+#define SEPARATORS " \t\n\r"
+
+static BOOL abort_mget = True;
+
+static pstring fileselection = "";
+
+extern file_info def_finfo;
+
+/* timing globals */
+SMB_BIG_UINT get_total_size = 0;
+unsigned int get_total_time_ms = 0;
+static SMB_BIG_UINT put_total_size = 0;
+static unsigned int put_total_time_ms = 0;
+
+/* totals globals */
+static double dir_total;
+
+#define USENMB
+
+/* some forward declarations */
+static struct cli_state *do_connect(const char *server, const char *share);
+
+/****************************************************************************
+ Write to a local file with CR/LF->LF translation if appropriate. Return the
+ number taken from the buffer. This may not equal the number written.
+****************************************************************************/
+
+static int writefile(int f, char *b, int n)
+{
+ int i;
+
+ if (!translation) {
+ return write(f,b,n);
+ }
+
+ i = 0;
+ while (i < n) {
+ if (*b == '\r' && (i<(n-1)) && *(b+1) == '\n') {
+ b++;i++;
+ }
+ if (write(f, b, 1) != 1) {
+ break;
+ }
+ b++;
+ i++;
+ }
+
+ return(i);
+}
+
+/****************************************************************************
+ Read from a file with LF->CR/LF translation if appropriate. Return the
+ number read. read approx n bytes.
+****************************************************************************/
+
+static int readfile(char *b, int n, XFILE *f)
+{
+ int i;
+ int c;
+
+ if (!translation)
+ return x_fread(b,1,n,f);
+
+ i = 0;
+ while (i < (n - 1) && (i < BUFFER_SIZE)) {
+ if ((c = x_getc(f)) == EOF) {
+ break;
+ }
+
+ if (c == '\n') { /* change all LFs to CR/LF */
+ b[i++] = '\r';
+ }
+
+ b[i++] = c;
+ }
+
+ return(i);
+}
+
+/****************************************************************************
+ Send a message.
+****************************************************************************/
+
+static void send_message(void)
+{
+ int total_len = 0;
+ int grp_id;
+
+ if (!cli_message_start(cli, desthost, username, &grp_id)) {
+ d_printf("message start: %s\n", cli_errstr(cli));
+ return;
+ }
+
+
+ d_printf("Connected. Type your message, ending it with a Control-D\n");
+
+ while (!feof(stdin) && total_len < 1600) {
+ int maxlen = MIN(1600 - total_len,127);
+ pstring msg;
+ int l=0;
+ int c;
+
+ ZERO_ARRAY(msg);
+
+ for (l=0;l<maxlen && (c=fgetc(stdin))!=EOF;l++) {
+ if (c == '\n')
+ msg[l++] = '\r';
+ msg[l] = c;
+ }
+
+ if (!cli_message_text(cli, msg, l, grp_id)) {
+ d_printf("SMBsendtxt failed (%s)\n",cli_errstr(cli));
+ return;
+ }
+
+ total_len += l;
+ }
+
+ if (total_len >= 1600)
+ d_printf("the message was truncated to 1600 bytes\n");
+ else
+ d_printf("sent %d bytes\n",total_len);
+
+ if (!cli_message_end(cli, grp_id)) {
+ d_printf("SMBsendend failed (%s)\n",cli_errstr(cli));
+ return;
+ }
+}
+
+/****************************************************************************
+ Check the space on a device.
+****************************************************************************/
+
+static int do_dskattr(void)
+{
+ int total, bsize, avail;
+
+ if (!cli_dskattr(cli, &bsize, &total, &avail)) {
+ d_printf("Error in dskattr: %s\n",cli_errstr(cli));
+ return 1;
+ }
+
+ d_printf("\n\t\t%d blocks of size %d. %d blocks available\n",
+ total, bsize, avail);
+
+ return 0;
+}
+
+/****************************************************************************
+ Show cd/pwd.
+****************************************************************************/
+
+static int cmd_pwd(void)
+{
+ d_printf("Current directory is %s",service);
+ d_printf("%s\n",cur_dir);
+ return 0;
+}
+
+/****************************************************************************
+ Change directory - inner section.
+****************************************************************************/
+
+static int do_cd(char *newdir)
+{
+ char *p = newdir;
+ pstring saved_dir;
+ pstring dname;
+
+ dos_format(newdir);
+
+ /* Save the current directory in case the
+ new directory is invalid */
+ pstrcpy(saved_dir, cur_dir);
+ if (*p == '\\')
+ pstrcpy(cur_dir,p);
+ else
+ pstrcat(cur_dir,p);
+ if (*(cur_dir+strlen(cur_dir)-1) != '\\') {
+ pstrcat(cur_dir, "\\");
+ }
+ dos_clean_name(cur_dir);
+ pstrcpy(dname,cur_dir);
+ pstrcat(cur_dir,"\\");
+ dos_clean_name(cur_dir);
+
+ if (!strequal(cur_dir,"\\")) {
+ if (!cli_chkpath(cli, dname)) {
+ d_printf("cd %s: %s\n", dname, cli_errstr(cli));
+ pstrcpy(cur_dir,saved_dir);
+ }
+ }
+
+ pstrcpy(cd_path,cur_dir);
+
+ return 0;
+}
+
+/****************************************************************************
+ Change directory.
+****************************************************************************/
+
+static int cmd_cd(void)
+{
+ pstring buf;
+ int rc = 0;
+
+ if (next_token_nr(NULL,buf,NULL,sizeof(buf)))
+ rc = do_cd(buf);
+ else
+ d_printf("Current directory is %s\n",cur_dir);
+
+ return rc;
+}
+
+/*******************************************************************
+ Decide if a file should be operated on.
+********************************************************************/
+
+static BOOL do_this_one(file_info *finfo)
+{
+ if (finfo->mode & aDIR)
+ return(True);
+
+ if (*fileselection &&
+ !mask_match(finfo->name,fileselection,False)) {
+ DEBUG(3,("mask_match %s failed\n", finfo->name));
+ return False;
+ }
+
+ if (newer_than && finfo->mtime < newer_than) {
+ DEBUG(3,("newer_than %s failed\n", finfo->name));
+ return(False);
+ }
+
+ if ((archive_level==1 || archive_level==2) && !(finfo->mode & aARCH)) {
+ DEBUG(3,("archive %s failed\n", finfo->name));
+ return(False);
+ }
+
+ return(True);
+}
+
+/****************************************************************************
+ Display info about a file.
+****************************************************************************/
+
+static void display_finfo(file_info *finfo)
+{
+ if (do_this_one(finfo)) {
+ time_t t = finfo->mtime; /* the time is assumed to be passed as GMT */
+ d_printf(" %-30s%7.7s %8.0f %s",
+ finfo->name,
+ attrib_string(finfo->mode),
+ (double)finfo->size,
+ asctime(LocalTime(&t)));
+ dir_total += finfo->size;
+ }
+}
+
+/****************************************************************************
+ Accumulate size of a file.
+****************************************************************************/
+
+static void do_du(file_info *finfo)
+{
+ if (do_this_one(finfo)) {
+ dir_total += finfo->size;
+ }
+}
+
+static BOOL do_list_recurse;
+static BOOL do_list_dirs;
+static char *do_list_queue = 0;
+static long do_list_queue_size = 0;
+static long do_list_queue_start = 0;
+static long do_list_queue_end = 0;
+static void (*do_list_fn)(file_info *);
+
+/****************************************************************************
+ Functions for do_list_queue.
+****************************************************************************/
+
+/*
+ * The do_list_queue is a NUL-separated list of strings stored in a
+ * char*. Since this is a FIFO, we keep track of the beginning and
+ * ending locations of the data in the queue. When we overflow, we
+ * double the size of the char*. When the start of the data passes
+ * the midpoint, we move everything back. This is logically more
+ * complex than a linked list, but easier from a memory management
+ * angle. In any memory error condition, do_list_queue is reset.
+ * Functions check to ensure that do_list_queue is non-NULL before
+ * accessing it.
+ */
+
+static void reset_do_list_queue(void)
+{
+ SAFE_FREE(do_list_queue);
+ do_list_queue_size = 0;
+ do_list_queue_start = 0;
+ do_list_queue_end = 0;
+}
+
+static void init_do_list_queue(void)
+{
+ reset_do_list_queue();
+ do_list_queue_size = 1024;
+ do_list_queue = malloc(do_list_queue_size);
+ if (do_list_queue == 0) {
+ d_printf("malloc fail for size %d\n",
+ (int)do_list_queue_size);
+ reset_do_list_queue();
+ } else {
+ memset(do_list_queue, 0, do_list_queue_size);
+ }
+}
+
+static void adjust_do_list_queue(void)
+{
+ /*
+ * If the starting point of the queue is more than half way through,
+ * move everything toward the beginning.
+ */
+ if (do_list_queue && (do_list_queue_start == do_list_queue_end)) {
+ DEBUG(4,("do_list_queue is empty\n"));
+ do_list_queue_start = do_list_queue_end = 0;
+ *do_list_queue = '\0';
+ } else if (do_list_queue_start > (do_list_queue_size / 2)) {
+ DEBUG(4,("sliding do_list_queue backward\n"));
+ memmove(do_list_queue,
+ do_list_queue + do_list_queue_start,
+ do_list_queue_end - do_list_queue_start);
+ do_list_queue_end -= do_list_queue_start;
+ do_list_queue_start = 0;
+ }
+}
+
+static void add_to_do_list_queue(const char* entry)
+{
+ char *dlq;
+ long new_end = do_list_queue_end + ((long)strlen(entry)) + 1;
+ while (new_end > do_list_queue_size) {
+ do_list_queue_size *= 2;
+ DEBUG(4,("enlarging do_list_queue to %d\n",
+ (int)do_list_queue_size));
+ dlq = Realloc(do_list_queue, do_list_queue_size);
+ if (! dlq) {
+ d_printf("failure enlarging do_list_queue to %d bytes\n",
+ (int)do_list_queue_size);
+ reset_do_list_queue();
+ } else {
+ do_list_queue = dlq;
+ memset(do_list_queue + do_list_queue_size / 2,
+ 0, do_list_queue_size / 2);
+ }
+ }
+ if (do_list_queue) {
+ safe_strcpy_base(do_list_queue + do_list_queue_end,
+ entry, do_list_queue, do_list_queue_size);
+ do_list_queue_end = new_end;
+ DEBUG(4,("added %s to do_list_queue (start=%d, end=%d)\n",
+ entry, (int)do_list_queue_start, (int)do_list_queue_end));
+ }
+}
+
+static char *do_list_queue_head(void)
+{
+ return do_list_queue + do_list_queue_start;
+}
+
+static void remove_do_list_queue_head(void)
+{
+ if (do_list_queue_end > do_list_queue_start) {
+ do_list_queue_start += strlen(do_list_queue_head()) + 1;
+ adjust_do_list_queue();
+ DEBUG(4,("removed head of do_list_queue (start=%d, end=%d)\n",
+ (int)do_list_queue_start, (int)do_list_queue_end));
+ }
+}
+
+static int do_list_queue_empty(void)
+{
+ return (! (do_list_queue && *do_list_queue));
+}
+
+/****************************************************************************
+ A helper for do_list.
+****************************************************************************/
+
+static void do_list_helper(file_info *f, const char *mask, void *state)
+{
+ if (f->mode & aDIR) {
+ if (do_list_dirs && do_this_one(f)) {
+ do_list_fn(f);
+ }
+ if (do_list_recurse &&
+ !strequal(f->name,".") &&
+ !strequal(f->name,"..")) {
+ pstring mask2;
+ char *p;
+
+ if (!f->name[0]) {
+ d_printf("Empty dir name returned. Possible server misconfiguration.\n");
+ return;
+ }
+
+ pstrcpy(mask2, mask);
+ p = strrchr_m(mask2,'\\');
+ if (!p)
+ return;
+ p[1] = 0;
+ pstrcat(mask2, f->name);
+ pstrcat(mask2,"\\*");
+ add_to_do_list_queue(mask2);
+ }
+ return;
+ }
+
+ if (do_this_one(f)) {
+ do_list_fn(f);
+ }
+}
+
+/****************************************************************************
+ A wrapper around cli_list that adds recursion.
+****************************************************************************/
+
+void do_list(const char *mask,uint16 attribute,void (*fn)(file_info *),BOOL rec, BOOL dirs)
+{
+ static int in_do_list = 0;
+
+ if (in_do_list && rec) {
+ fprintf(stderr, "INTERNAL ERROR: do_list called recursively when the recursive flag is true\n");
+ exit(1);
+ }
+
+ in_do_list = 1;
+
+ do_list_recurse = rec;
+ do_list_dirs = dirs;
+ do_list_fn = fn;
+
+ if (rec) {
+ init_do_list_queue();
+ add_to_do_list_queue(mask);
+
+ while (! do_list_queue_empty()) {
+ /*
+ * Need to copy head so that it doesn't become
+ * invalid inside the call to cli_list. This
+ * would happen if the list were expanded
+ * during the call.
+ * Fix from E. Jay Berkenbilt (ejb@ql.org)
+ */
+ pstring head;
+ pstrcpy(head, do_list_queue_head());
+ cli_list(cli, head, attribute, do_list_helper, NULL);
+ remove_do_list_queue_head();
+ if ((! do_list_queue_empty()) && (fn == display_finfo)) {
+ char* next_file = do_list_queue_head();
+ char* save_ch = 0;
+ if ((strlen(next_file) >= 2) &&
+ (next_file[strlen(next_file) - 1] == '*') &&
+ (next_file[strlen(next_file) - 2] == '\\')) {
+ save_ch = next_file +
+ strlen(next_file) - 2;
+ *save_ch = '\0';
+ }
+ d_printf("\n%s\n",next_file);
+ if (save_ch) {
+ *save_ch = '\\';
+ }
+ }
+ }
+ } else {
+ if (cli_list(cli, mask, attribute, do_list_helper, NULL) == -1) {
+ d_printf("%s listing %s\n", cli_errstr(cli), mask);
+ }
+ }
+
+ in_do_list = 0;
+ reset_do_list_queue();
+}
+
+/****************************************************************************
+ Get a directory listing.
+****************************************************************************/
+
+static int cmd_dir(void)
+{
+ uint16 attribute = aDIR | aSYSTEM | aHIDDEN;
+ pstring mask;
+ pstring buf;
+ char *p=buf;
+ int rc;
+
+ dir_total = 0;
+ if (strcmp(cur_dir, "\\") != 0) {
+ pstrcpy(mask,cur_dir);
+ if(mask[strlen(mask)-1]!='\\')
+ pstrcat(mask,"\\");
+ } else {
+ *mask = '\0';
+ }
+
+ if (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ dos_format(p);
+ if (*p == '\\')
+ pstrcpy(mask,p);
+ else
+ pstrcat(mask,p);
+ } else {
+ pstrcat(mask,"*");
+ }
+
+ do_list(mask, attribute, display_finfo, recurse, True);
+
+ rc = do_dskattr();
+
+ DEBUG(3, ("Total bytes listed: %.0f\n", dir_total));
+
+ return rc;
+}
+
+/****************************************************************************
+ Get a directory listing.
+****************************************************************************/
+
+static int cmd_du(void)
+{
+ uint16 attribute = aDIR | aSYSTEM | aHIDDEN;
+ pstring mask;
+ pstring buf;
+ char *p=buf;
+ int rc;
+
+ dir_total = 0;
+ pstrcpy(mask,cur_dir);
+ if(mask[strlen(mask)-1]!='\\')
+ pstrcat(mask,"\\");
+
+ if (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ dos_format(p);
+ if (*p == '\\')
+ pstrcpy(mask,p);
+ else
+ pstrcat(mask,p);
+ } else {
+ pstrcat(mask,"*");
+ }
+
+ do_list(mask, attribute, do_du, recurse, True);
+
+ rc = do_dskattr();
+
+ d_printf("Total number of bytes: %.0f\n", dir_total);
+
+ return rc;
+}
+
+/****************************************************************************
+ Get a file from rname to lname
+****************************************************************************/
+
+static int do_get(char *rname, char *lname, BOOL reget)
+{
+ int handle = 0, fnum;
+ BOOL newhandle = False;
+ char *data;
+ struct timeval tp_start;
+ int read_size = io_bufsize;
+ uint16 attr;
+ size_t size;
+ off_t start = 0;
+ off_t nread = 0;
+ int rc = 0;
+
+ GetTimeOfDay(&tp_start);
+
+ if (lowercase) {
+ strlower_m(lname);
+ }
+
+ fnum = cli_open(cli, rname, O_RDONLY, DENY_NONE);
+
+ if (fnum == -1) {
+ d_printf("%s opening remote file %s\n",cli_errstr(cli),rname);
+ return 1;
+ }
+
+ if(!strcmp(lname,"-")) {
+ handle = fileno(stdout);
+ } else {
+ if (reget) {
+ handle = sys_open(lname, O_WRONLY|O_CREAT, 0644);
+ if (handle >= 0) {
+ start = sys_lseek(handle, 0, SEEK_END);
+ if (start == -1) {
+ d_printf("Error seeking local file\n");
+ return 1;
+ }
+ }
+ } else {
+ handle = sys_open(lname, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ }
+ newhandle = True;
+ }
+ if (handle < 0) {
+ d_printf("Error opening local file %s\n",lname);
+ return 1;
+ }
+
+
+ if (!cli_qfileinfo(cli, fnum,
+ &attr, &size, NULL, NULL, NULL, NULL, NULL) &&
+ !cli_getattrE(cli, fnum,
+ &attr, &size, NULL, NULL, NULL)) {
+ d_printf("getattrib: %s\n",cli_errstr(cli));
+ return 1;
+ }
+
+ DEBUG(2,("getting file %s of size %.0f as %s ",
+ rname, (double)size, lname));
+
+ if(!(data = (char *)malloc(read_size))) {
+ d_printf("malloc fail for size %d\n", read_size);
+ cli_close(cli, fnum);
+ return 1;
+ }
+
+ while (1) {
+ int n = cli_read(cli, fnum, data, nread + start, read_size);
+
+ if (n <= 0)
+ break;
+
+ if (writefile(handle,data, n) != n) {
+ d_printf("Error writing local file\n");
+ rc = 1;
+ break;
+ }
+
+ nread += n;
+ }
+
+ if (nread + start < size) {
+ DEBUG (0, ("Short read when getting file %s. Only got %ld bytes.\n",
+ rname, (long)nread));
+
+ rc = 1;
+ }
+
+ SAFE_FREE(data);
+
+ if (!cli_close(cli, fnum)) {
+ d_printf("Error %s closing remote file\n",cli_errstr(cli));
+ rc = 1;
+ }
+
+ if (newhandle) {
+ close(handle);
+ }
+
+ if (archive_level >= 2 && (attr & aARCH)) {
+ cli_setatr(cli, rname, attr & ~(uint16)aARCH, 0);
+ }
+
+ {
+ struct timeval tp_end;
+ int this_time;
+
+ GetTimeOfDay(&tp_end);
+ this_time =
+ (tp_end.tv_sec - tp_start.tv_sec)*1000 +
+ (tp_end.tv_usec - tp_start.tv_usec)/1000;
+ get_total_time_ms += this_time;
+ get_total_size += nread;
+
+ DEBUG(2,("(%3.1f kb/s) (average %3.1f kb/s)\n",
+ nread / (1.024*this_time + 1.0e-4),
+ get_total_size / (1.024*get_total_time_ms)));
+ }
+
+ return rc;
+}
+
+/****************************************************************************
+ Get a file.
+****************************************************************************/
+
+static int cmd_get(void)
+{
+ pstring lname;
+ pstring rname;
+ char *p;
+
+ pstrcpy(rname,cur_dir);
+ pstrcat(rname,"\\");
+
+ p = rname + strlen(rname);
+
+ if (!next_token_nr(NULL,p,NULL,sizeof(rname)-strlen(rname))) {
+ d_printf("get <filename>\n");
+ return 1;
+ }
+ pstrcpy(lname,p);
+ dos_clean_name(rname);
+
+ next_token_nr(NULL,lname,NULL,sizeof(lname));
+
+ return do_get(rname, lname, False);
+}
+
+/****************************************************************************
+ Do an mget operation on one file.
+****************************************************************************/
+
+static void do_mget(file_info *finfo)
+{
+ pstring rname;
+ pstring quest;
+ pstring saved_curdir;
+ pstring mget_mask;
+
+ if (strequal(finfo->name,".") || strequal(finfo->name,".."))
+ return;
+
+ if (abort_mget) {
+ d_printf("mget aborted\n");
+ return;
+ }
+
+ if (finfo->mode & aDIR)
+ slprintf(quest,sizeof(pstring)-1,
+ "Get directory %s? ",finfo->name);
+ else
+ slprintf(quest,sizeof(pstring)-1,
+ "Get file %s? ",finfo->name);
+
+ if (prompt && !yesno(quest))
+ return;
+
+ if (!(finfo->mode & aDIR)) {
+ pstrcpy(rname,cur_dir);
+ pstrcat(rname,finfo->name);
+ do_get(rname, finfo->name, False);
+ return;
+ }
+
+ /* handle directories */
+ pstrcpy(saved_curdir,cur_dir);
+
+ pstrcat(cur_dir,finfo->name);
+ pstrcat(cur_dir,"\\");
+
+ unix_format(finfo->name);
+ if (lowercase)
+ strlower_m(finfo->name);
+
+ if (!directory_exist(finfo->name,NULL) &&
+ mkdir(finfo->name,0777) != 0) {
+ d_printf("failed to create directory %s\n",finfo->name);
+ pstrcpy(cur_dir,saved_curdir);
+ return;
+ }
+
+ if (chdir(finfo->name) != 0) {
+ d_printf("failed to chdir to directory %s\n",finfo->name);
+ pstrcpy(cur_dir,saved_curdir);
+ return;
+ }
+
+ pstrcpy(mget_mask,cur_dir);
+ pstrcat(mget_mask,"*");
+
+ do_list(mget_mask, aSYSTEM | aHIDDEN | aDIR,do_mget,False, True);
+ chdir("..");
+ pstrcpy(cur_dir,saved_curdir);
+}
+
+/****************************************************************************
+ View the file using the pager.
+****************************************************************************/
+
+static int cmd_more(void)
+{
+ pstring rname,lname,pager_cmd;
+ char *pager;
+ int fd;
+ int rc = 0;
+
+ pstrcpy(rname,cur_dir);
+ pstrcat(rname,"\\");
+
+ slprintf(lname,sizeof(lname)-1, "%s/smbmore.XXXXXX",tmpdir());
+ fd = smb_mkstemp(lname);
+ if (fd == -1) {
+ d_printf("failed to create temporary file for more\n");
+ return 1;
+ }
+ close(fd);
+
+ if (!next_token_nr(NULL,rname+strlen(rname),NULL,sizeof(rname)-strlen(rname))) {
+ d_printf("more <filename>\n");
+ unlink(lname);
+ return 1;
+ }
+ dos_clean_name(rname);
+
+ rc = do_get(rname, lname, False);
+
+ pager=getenv("PAGER");
+
+ slprintf(pager_cmd,sizeof(pager_cmd)-1,
+ "%s %s",(pager? pager:PAGER), lname);
+ system(pager_cmd);
+ unlink(lname);
+
+ return rc;
+}
+
+/****************************************************************************
+ Do a mget command.
+****************************************************************************/
+
+static int cmd_mget(void)
+{
+ uint16 attribute = aSYSTEM | aHIDDEN;
+ pstring mget_mask;
+ pstring buf;
+ char *p=buf;
+
+ *mget_mask = 0;
+
+ if (recurse)
+ attribute |= aDIR;
+
+ abort_mget = False;
+
+ while (next_token_nr(NULL,p,NULL,sizeof(buf))) {
+ pstrcpy(mget_mask,cur_dir);
+ if(mget_mask[strlen(mget_mask)-1]!='\\')
+ pstrcat(mget_mask,"\\");
+
+ if (*p == '\\')
+ pstrcpy(mget_mask,p);
+ else
+ pstrcat(mget_mask,p);
+ do_list(mget_mask, attribute,do_mget,False,True);
+ }
+
+ if (!*mget_mask) {
+ pstrcpy(mget_mask,cur_dir);
+ if(mget_mask[strlen(mget_mask)-1]!='\\')
+ pstrcat(mget_mask,"\\");
+ pstrcat(mget_mask,"*");
+ do_list(mget_mask, attribute,do_mget,False,True);
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ Make a directory of name "name".
+****************************************************************************/
+
+static BOOL do_mkdir(char *name)
+{
+ if (!cli_mkdir(cli, name)) {
+ d_printf("%s making remote directory %s\n",
+ cli_errstr(cli),name);
+ return(False);
+ }
+
+ return(True);
+}
+
+/****************************************************************************
+ Show 8.3 name of a file.
+****************************************************************************/
+
+static BOOL do_altname(char *name)
+{
+ pstring altname;
+ if (!NT_STATUS_IS_OK(cli_qpathinfo_alt_name(cli, name, altname))) {
+ d_printf("%s getting alt name for %s\n",
+ cli_errstr(cli),name);
+ return(False);
+ }
+ d_printf("%s\n", altname);
+
+ return(True);
+}
+
+/****************************************************************************
+ Exit client.
+****************************************************************************/
+
+static int cmd_quit(void)
+{
+ cli_shutdown(cli);
+ exit(0);
+ /* NOTREACHED */
+ return 0;
+}
+
+/****************************************************************************
+ Make a directory.
+****************************************************************************/
+
+static int cmd_mkdir(void)
+{
+ pstring mask;
+ pstring buf;
+ char *p=buf;
+
+ pstrcpy(mask,cur_dir);
+
+ if (!next_token_nr(NULL,p,NULL,sizeof(buf))) {
+ if (!recurse)
+ d_printf("mkdir <dirname>\n");
+ return 1;
+ }
+ pstrcat(mask,p);
+
+ if (recurse) {
+ pstring ddir;
+ pstring ddir2;
+ *ddir2 = 0;
+
+ pstrcpy(ddir,mask);
+ trim_char(ddir,'.','\0');
+ p = strtok(ddir,"/\\");
+ while (p) {
+ pstrcat(ddir2,p);
+ if (!cli_chkpath(cli, ddir2)) {
+ do_mkdir(ddir2);
+ }
+ pstrcat(ddir2,"\\");
+ p = strtok(NULL,"/\\");
+ }
+ } else {
+ do_mkdir(mask);
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ Show alt name.
+****************************************************************************/
+
+static int cmd_altname(void)
+{
+ pstring name;
+ pstring buf;
+ char *p=buf;
+
+ pstrcpy(name,cur_dir);
+
+ if (!next_token_nr(NULL,p,NULL,sizeof(buf))) {
+ d_printf("altname <file>\n");
+ return 1;
+ }
+ pstrcat(name,p);
+
+ do_altname(name);
+
+ return 0;
+}
+
+/****************************************************************************
+ Put a single file.
+****************************************************************************/
+
+static int do_put(char *rname, char *lname, BOOL reput)
+{
+ int fnum;
+ XFILE *f;
+ size_t start = 0;
+ off_t nread = 0;
+ char *buf = NULL;
+ int maxwrite = io_bufsize;
+ int rc = 0;
+
+ struct timeval tp_start;
+ GetTimeOfDay(&tp_start);
+
+ if (reput) {
+ fnum = cli_open(cli, rname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum >= 0) {
+ if (!cli_qfileinfo(cli, fnum, NULL, &start, NULL, NULL, NULL, NULL, NULL) &&
+ !cli_getattrE(cli, fnum, NULL, &start, NULL, NULL, NULL)) {
+ d_printf("getattrib: %s\n",cli_errstr(cli));
+ return 1;
+ }
+ }
+ } else {
+ fnum = cli_open(cli, rname, O_RDWR|O_CREAT|O_TRUNC, DENY_NONE);
+ }
+
+ if (fnum == -1) {
+ d_printf("%s opening remote file %s\n",cli_errstr(cli),rname);
+ return 1;
+ }
+
+ /* allow files to be piped into smbclient
+ jdblair 24.jun.98
+
+ Note that in this case this function will exit(0) rather
+ than returning. */
+ if (!strcmp(lname, "-")) {
+ f = x_stdin;
+ /* size of file is not known */
+ } else {
+ f = x_fopen(lname,O_RDONLY, 0);
+ if (f && reput) {
+ if (x_tseek(f, start, SEEK_SET) == -1) {
+ d_printf("Error seeking local file\n");
+ return 1;
+ }
+ }
+ }
+
+ if (!f) {
+ d_printf("Error opening local file %s\n",lname);
+ return 1;
+ }
+
+ DEBUG(1,("putting file %s as %s ",lname,
+ rname));
+
+ buf = (char *)malloc(maxwrite);
+ if (!buf) {
+ d_printf("ERROR: Not enough memory!\n");
+ return 1;
+ }
+ while (!x_feof(f)) {
+ int n = maxwrite;
+ int ret;
+
+ if ((n = readfile(buf,n,f)) < 1) {
+ if((n == 0) && x_feof(f))
+ break; /* Empty local file. */
+
+ d_printf("Error reading local file: %s\n", strerror(errno));
+ rc = 1;
+ break;
+ }
+
+ ret = cli_write(cli, fnum, 0, buf, nread + start, n);
+
+ if (n != ret) {
+ d_printf("Error writing file: %s\n", cli_errstr(cli));
+ rc = 1;
+ break;
+ }
+
+ nread += n;
+ }
+
+ if (!cli_close(cli, fnum)) {
+ d_printf("%s closing remote file %s\n",cli_errstr(cli),rname);
+ x_fclose(f);
+ SAFE_FREE(buf);
+ return 1;
+ }
+
+
+ if (f != x_stdin) {
+ x_fclose(f);
+ }
+
+ SAFE_FREE(buf);
+
+ {
+ struct timeval tp_end;
+ int this_time;
+
+ GetTimeOfDay(&tp_end);
+ this_time =
+ (tp_end.tv_sec - tp_start.tv_sec)*1000 +
+ (tp_end.tv_usec - tp_start.tv_usec)/1000;
+ put_total_time_ms += this_time;
+ put_total_size += nread;
+
+ DEBUG(1,("(%3.1f kb/s) (average %3.1f kb/s)\n",
+ nread / (1.024*this_time + 1.0e-4),
+ put_total_size / (1.024*put_total_time_ms)));
+ }
+
+ if (f == x_stdin) {
+ cli_shutdown(cli);
+ exit(0);
+ }
+
+ return rc;
+}
+
+/****************************************************************************
+ Put a file.
+****************************************************************************/
+
+static int cmd_put(void)
+{
+ pstring lname;
+ pstring rname;
+ pstring buf;
+ char *p=buf;
+
+ pstrcpy(rname,cur_dir);
+ pstrcat(rname,"\\");
+
+ if (!next_token_nr(NULL,p,NULL,sizeof(buf))) {
+ d_printf("put <filename>\n");
+ return 1;
+ }
+ pstrcpy(lname,p);
+
+ if (next_token_nr(NULL,p,NULL,sizeof(buf)))
+ pstrcat(rname,p);
+ else
+ pstrcat(rname,lname);
+
+ dos_clean_name(rname);
+
+ {
+ SMB_STRUCT_STAT st;
+ /* allow '-' to represent stdin
+ jdblair, 24.jun.98 */
+ if (!file_exist(lname,&st) &&
+ (strcmp(lname,"-"))) {
+ d_printf("%s does not exist\n",lname);
+ return 1;
+ }
+ }
+
+ return do_put(rname, lname, False);
+}
+
+/*************************************
+ File list structure.
+*************************************/
+
+static struct file_list {
+ struct file_list *prev, *next;
+ char *file_path;
+ BOOL isdir;
+} *file_list;
+
+/****************************************************************************
+ Free a file_list structure.
+****************************************************************************/
+
+static void free_file_list (struct file_list * list)
+{
+ struct file_list *tmp;
+
+ while (list) {
+ tmp = list;
+ DLIST_REMOVE(list, list);
+ SAFE_FREE(tmp->file_path);
+ SAFE_FREE(tmp);
+ }
+}
+
+/****************************************************************************
+ Seek in a directory/file list until you get something that doesn't start with
+ the specified name.
+****************************************************************************/
+
+static BOOL seek_list(struct file_list *list, char *name)
+{
+ while (list) {
+ trim_string(list->file_path,"./","\n");
+ if (strncmp(list->file_path, name, strlen(name)) != 0) {
+ return(True);
+ }
+ list = list->next;
+ }
+
+ return(False);
+}
+
+/****************************************************************************
+ Set the file selection mask.
+****************************************************************************/
+
+static int cmd_select(void)
+{
+ pstrcpy(fileselection,"");
+ next_token_nr(NULL,fileselection,NULL,sizeof(fileselection));
+
+ return 0;
+}
+
+/****************************************************************************
+ Recursive file matching function act as find
+ match must be always set to True when calling this function
+****************************************************************************/
+
+static int file_find(struct file_list **list, const char *directory,
+ const char *expression, BOOL match)
+{
+ DIR *dir;
+ struct file_list *entry;
+ struct stat statbuf;
+ int ret;
+ char *path;
+ BOOL isdir;
+ const char *dname;
+
+ dir = opendir(directory);
+ if (!dir)
+ return -1;
+
+ while ((dname = readdirname(dir))) {
+ if (!strcmp("..", dname))
+ continue;
+ if (!strcmp(".", dname))
+ continue;
+
+ if (asprintf(&path, "%s/%s", directory, dname) <= 0) {
+ continue;
+ }
+
+ isdir = False;
+ if (!match || !gen_fnmatch(expression, dname)) {
+ if (recurse) {
+ ret = stat(path, &statbuf);
+ if (ret == 0) {
+ if (S_ISDIR(statbuf.st_mode)) {
+ isdir = True;
+ ret = file_find(list, path, expression, False);
+ }
+ } else {
+ d_printf("file_find: cannot stat file %s\n", path);
+ }
+
+ if (ret == -1) {
+ SAFE_FREE(path);
+ closedir(dir);
+ return -1;
+ }
+ }
+ entry = (struct file_list *) malloc(sizeof (struct file_list));
+ if (!entry) {
+ d_printf("Out of memory in file_find\n");
+ closedir(dir);
+ return -1;
+ }
+ entry->file_path = path;
+ entry->isdir = isdir;
+ DLIST_ADD(*list, entry);
+ } else {
+ SAFE_FREE(path);
+ }
+ }
+
+ closedir(dir);
+ return 0;
+}
+
+/****************************************************************************
+ mput some files.
+****************************************************************************/
+
+static int cmd_mput(void)
+{
+ pstring buf;
+ char *p=buf;
+
+ while (next_token_nr(NULL,p,NULL,sizeof(buf))) {
+ int ret;
+ struct file_list *temp_list;
+ char *quest, *lname, *rname;
+
+ file_list = NULL;
+
+ ret = file_find(&file_list, ".", p, True);
+ if (ret) {
+ free_file_list(file_list);
+ continue;
+ }
+
+ quest = NULL;
+ lname = NULL;
+ rname = NULL;
+
+ for (temp_list = file_list; temp_list;
+ temp_list = temp_list->next) {
+
+ SAFE_FREE(lname);
+ if (asprintf(&lname, "%s/", temp_list->file_path) <= 0)
+ continue;
+ trim_string(lname, "./", "/");
+
+ /* check if it's a directory */
+ if (temp_list->isdir) {
+ /* if (!recurse) continue; */
+
+ SAFE_FREE(quest);
+ if (asprintf(&quest, "Put directory %s? ", lname) < 0) break;
+ if (prompt && !yesno(quest)) { /* No */
+ /* Skip the directory */
+ lname[strlen(lname)-1] = '/';
+ if (!seek_list(temp_list, lname))
+ break;
+ } else { /* Yes */
+ SAFE_FREE(rname);
+ if(asprintf(&rname, "%s%s", cur_dir, lname) < 0) break;
+ dos_format(rname);
+ if (!cli_chkpath(cli, rname) &&
+ !do_mkdir(rname)) {
+ DEBUG (0, ("Unable to make dir, skipping..."));
+ /* Skip the directory */
+ lname[strlen(lname)-1] = '/';
+ if (!seek_list(temp_list, lname))
+ break;
+ }
+ }
+ continue;
+ } else {
+ SAFE_FREE(quest);
+ if (asprintf(&quest,"Put file %s? ", lname) < 0) break;
+ if (prompt && !yesno(quest)) /* No */
+ continue;
+
+ /* Yes */
+ SAFE_FREE(rname);
+ if (asprintf(&rname, "%s%s", cur_dir, lname) < 0) break;
+ }
+
+ dos_format(rname);
+
+ do_put(rname, lname, False);
+ }
+ free_file_list(file_list);
+ SAFE_FREE(quest);
+ SAFE_FREE(lname);
+ SAFE_FREE(rname);
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ Cancel a print job.
+****************************************************************************/
+
+static int do_cancel(int job)
+{
+ if (cli_printjob_del(cli, job)) {
+ d_printf("Job %d cancelled\n",job);
+ return 0;
+ } else {
+ d_printf("Error cancelling job %d : %s\n",job,cli_errstr(cli));
+ return 1;
+ }
+}
+
+/****************************************************************************
+ Cancel a print job.
+****************************************************************************/
+
+static int cmd_cancel(void)
+{
+ pstring buf;
+ int job;
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ d_printf("cancel <jobid> ...\n");
+ return 1;
+ }
+ do {
+ job = atoi(buf);
+ do_cancel(job);
+ } while (next_token_nr(NULL,buf,NULL,sizeof(buf)));
+
+ return 0;
+}
+
+/****************************************************************************
+ Print a file.
+****************************************************************************/
+
+static int cmd_print(void)
+{
+ pstring lname;
+ pstring rname;
+ char *p;
+
+ if (!next_token_nr(NULL,lname,NULL, sizeof(lname))) {
+ d_printf("print <filename>\n");
+ return 1;
+ }
+
+ pstrcpy(rname,lname);
+ p = strrchr_m(rname,'/');
+ if (p) {
+ slprintf(rname, sizeof(rname)-1, "%s-%d", p+1, (int)sys_getpid());
+ }
+
+ if (strequal(lname,"-")) {
+ slprintf(rname, sizeof(rname)-1, "stdin-%d", (int)sys_getpid());
+ }
+
+ return do_put(rname, lname, False);
+}
+
+/****************************************************************************
+ Show a print queue entry.
+****************************************************************************/
+
+static void queue_fn(struct print_job_info *p)
+{
+ d_printf("%-6d %-9d %s\n", (int)p->id, (int)p->size, p->name);
+}
+
+/****************************************************************************
+ Show a print queue.
+****************************************************************************/
+
+static int cmd_queue(void)
+{
+ cli_print_queue(cli, queue_fn);
+
+ return 0;
+}
+
+/****************************************************************************
+ Delete some files.
+****************************************************************************/
+
+static void do_del(file_info *finfo)
+{
+ pstring mask;
+
+ pstrcpy(mask,cur_dir);
+ pstrcat(mask,finfo->name);
+
+ if (finfo->mode & aDIR)
+ return;
+
+ if (!cli_unlink(cli, mask)) {
+ d_printf("%s deleting remote file %s\n",cli_errstr(cli),mask);
+ }
+}
+
+/****************************************************************************
+ Delete some files.
+****************************************************************************/
+
+static int cmd_del(void)
+{
+ pstring mask;
+ pstring buf;
+ uint16 attribute = aSYSTEM | aHIDDEN;
+
+ if (recurse)
+ attribute |= aDIR;
+
+ pstrcpy(mask,cur_dir);
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ d_printf("del <filename>\n");
+ return 1;
+ }
+ pstrcat(mask,buf);
+
+ do_list(mask, attribute,do_del,False,False);
+
+ return 0;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static int cmd_open(void)
+{
+ pstring mask;
+ pstring buf;
+
+ pstrcpy(mask,cur_dir);
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ d_printf("open <filename>\n");
+ return 1;
+ }
+ pstrcat(mask,buf);
+
+ cli_open(cli, mask, O_RDWR, DENY_ALL);
+
+ return 0;
+}
+
+
+/****************************************************************************
+ Remove a directory.
+****************************************************************************/
+
+static int cmd_rmdir(void)
+{
+ pstring mask;
+ pstring buf;
+
+ pstrcpy(mask,cur_dir);
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ d_printf("rmdir <dirname>\n");
+ return 1;
+ }
+ pstrcat(mask,buf);
+
+ if (!cli_rmdir(cli, mask)) {
+ d_printf("%s removing remote directory file %s\n",
+ cli_errstr(cli),mask);
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ UNIX hardlink.
+****************************************************************************/
+
+static int cmd_link(void)
+{
+ pstring src,dest;
+ pstring buf,buf2;
+
+ if (!SERVER_HAS_UNIX_CIFS(cli)) {
+ d_printf("Server doesn't support UNIX CIFS calls.\n");
+ return 1;
+ }
+
+ pstrcpy(src,cur_dir);
+ pstrcpy(dest,cur_dir);
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf)) ||
+ !next_token_nr(NULL,buf2,NULL, sizeof(buf2))) {
+ d_printf("link <src> <dest>\n");
+ return 1;
+ }
+
+ pstrcat(src,buf);
+ pstrcat(dest,buf2);
+
+ if (!cli_unix_hardlink(cli, src, dest)) {
+ d_printf("%s linking files (%s -> %s)\n", cli_errstr(cli), src, dest);
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ UNIX symlink.
+****************************************************************************/
+
+static int cmd_symlink(void)
+{
+ pstring src,dest;
+ pstring buf,buf2;
+
+ if (!SERVER_HAS_UNIX_CIFS(cli)) {
+ d_printf("Server doesn't support UNIX CIFS calls.\n");
+ return 1;
+ }
+
+ pstrcpy(src,cur_dir);
+ pstrcpy(dest,cur_dir);
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf)) ||
+ !next_token_nr(NULL,buf2,NULL, sizeof(buf2))) {
+ d_printf("symlink <src> <dest>\n");
+ return 1;
+ }
+
+ pstrcat(src,buf);
+ pstrcat(dest,buf2);
+
+ if (!cli_unix_symlink(cli, src, dest)) {
+ d_printf("%s symlinking files (%s -> %s)\n",
+ cli_errstr(cli), src, dest);
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ UNIX chmod.
+****************************************************************************/
+
+static int cmd_chmod(void)
+{
+ pstring src;
+ mode_t mode;
+ pstring buf, buf2;
+
+ if (!SERVER_HAS_UNIX_CIFS(cli)) {
+ d_printf("Server doesn't support UNIX CIFS calls.\n");
+ return 1;
+ }
+
+ pstrcpy(src,cur_dir);
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf)) ||
+ !next_token_nr(NULL,buf2,NULL, sizeof(buf2))) {
+ d_printf("chmod mode file\n");
+ return 1;
+ }
+
+ mode = (mode_t)strtol(buf, NULL, 8);
+ pstrcat(src,buf2);
+
+ if (!cli_unix_chmod(cli, src, mode)) {
+ d_printf("%s chmod file %s 0%o\n",
+ cli_errstr(cli), src, (unsigned int)mode);
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ UNIX chown.
+****************************************************************************/
+
+static int cmd_chown(void)
+{
+ pstring src;
+ uid_t uid;
+ gid_t gid;
+ pstring buf, buf2, buf3;
+
+ if (!SERVER_HAS_UNIX_CIFS(cli)) {
+ d_printf("Server doesn't support UNIX CIFS calls.\n");
+ return 1;
+ }
+
+ pstrcpy(src,cur_dir);
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf)) ||
+ !next_token_nr(NULL,buf2,NULL, sizeof(buf2)) ||
+ !next_token_nr(NULL,buf3,NULL, sizeof(buf3))) {
+ d_printf("chown uid gid file\n");
+ return 1;
+ }
+
+ uid = (uid_t)atoi(buf);
+ gid = (gid_t)atoi(buf2);
+ pstrcat(src,buf3);
+
+ if (!cli_unix_chown(cli, src, uid, gid)) {
+ d_printf("%s chown file %s uid=%d, gid=%d\n",
+ cli_errstr(cli), src, (int)uid, (int)gid);
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ Rename some file.
+****************************************************************************/
+
+static int cmd_rename(void)
+{
+ pstring src,dest;
+ pstring buf,buf2;
+
+ pstrcpy(src,cur_dir);
+ pstrcpy(dest,cur_dir);
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf)) ||
+ !next_token_nr(NULL,buf2,NULL, sizeof(buf2))) {
+ d_printf("rename <src> <dest>\n");
+ return 1;
+ }
+
+ pstrcat(src,buf);
+ pstrcat(dest,buf2);
+
+ if (!cli_rename(cli, src, dest)) {
+ d_printf("%s renaming files\n",cli_errstr(cli));
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ Hard link files using the NT call.
+****************************************************************************/
+
+static int cmd_hardlink(void)
+{
+ pstring src,dest;
+ pstring buf,buf2;
+
+ pstrcpy(src,cur_dir);
+ pstrcpy(dest,cur_dir);
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf)) ||
+ !next_token_nr(NULL,buf2,NULL, sizeof(buf2))) {
+ d_printf("hardlink <src> <dest>\n");
+ return 1;
+ }
+
+ pstrcat(src,buf);
+ pstrcat(dest,buf2);
+
+ if (!cli_nt_hardlink(cli, src, dest)) {
+ d_printf("%s doing an NT hard link of files\n",cli_errstr(cli));
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ Toggle the prompt flag.
+****************************************************************************/
+
+static int cmd_prompt(void)
+{
+ prompt = !prompt;
+ DEBUG(2,("prompting is now %s\n",prompt?"on":"off"));
+
+ return 1;
+}
+
+/****************************************************************************
+ Set the newer than time.
+****************************************************************************/
+
+static int cmd_newer(void)
+{
+ pstring buf;
+ BOOL ok;
+ SMB_STRUCT_STAT sbuf;
+
+ ok = next_token_nr(NULL,buf,NULL,sizeof(buf));
+ if (ok && (sys_stat(buf,&sbuf) == 0)) {
+ newer_than = sbuf.st_mtime;
+ DEBUG(1,("Getting files newer than %s",
+ asctime(LocalTime(&newer_than))));
+ } else {
+ newer_than = 0;
+ }
+
+ if (ok && newer_than == 0) {
+ d_printf("Error setting newer-than time\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ Set the archive level.
+****************************************************************************/
+
+static int cmd_archive(void)
+{
+ pstring buf;
+
+ if (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ archive_level = atoi(buf);
+ } else
+ d_printf("Archive level is %d\n",archive_level);
+
+ return 0;
+}
+
+/****************************************************************************
+ Toggle the lowercaseflag.
+****************************************************************************/
+
+static int cmd_lowercase(void)
+{
+ lowercase = !lowercase;
+ DEBUG(2,("filename lowercasing is now %s\n",lowercase?"on":"off"));
+
+ return 0;
+}
+
+/****************************************************************************
+ Toggle the recurse flag.
+****************************************************************************/
+
+static int cmd_recurse(void)
+{
+ recurse = !recurse;
+ DEBUG(2,("directory recursion is now %s\n",recurse?"on":"off"));
+
+ return 0;
+}
+
+/****************************************************************************
+ Toggle the translate flag.
+****************************************************************************/
+
+static int cmd_translate(void)
+{
+ translation = !translation;
+ DEBUG(2,("CR/LF<->LF and print text translation now %s\n",
+ translation?"on":"off"));
+
+ return 0;
+}
+
+/****************************************************************************
+ Do a printmode command.
+****************************************************************************/
+
+static int cmd_printmode(void)
+{
+ fstring buf;
+ fstring mode;
+
+ if (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ if (strequal(buf,"text")) {
+ printmode = 0;
+ } else {
+ if (strequal(buf,"graphics"))
+ printmode = 1;
+ else
+ printmode = atoi(buf);
+ }
+ }
+
+ switch(printmode) {
+ case 0:
+ fstrcpy(mode,"text");
+ break;
+ case 1:
+ fstrcpy(mode,"graphics");
+ break;
+ default:
+ slprintf(mode,sizeof(mode)-1,"%d",printmode);
+ break;
+ }
+
+ DEBUG(2,("the printmode is now %s\n",mode));
+
+ return 0;
+}
+
+/****************************************************************************
+ Do the lcd command.
+ ****************************************************************************/
+
+static int cmd_lcd(void)
+{
+ pstring buf;
+ pstring d;
+
+ if (next_token_nr(NULL,buf,NULL,sizeof(buf)))
+ chdir(buf);
+ DEBUG(2,("the local directory is now %s\n",sys_getwd(d)));
+
+ return 0;
+}
+
+/****************************************************************************
+ Get a file restarting at end of local file.
+ ****************************************************************************/
+
+static int cmd_reget(void)
+{
+ pstring local_name;
+ pstring remote_name;
+ char *p;
+
+ pstrcpy(remote_name, cur_dir);
+ pstrcat(remote_name, "\\");
+
+ p = remote_name + strlen(remote_name);
+
+ if (!next_token_nr(NULL, p, NULL, sizeof(remote_name) - strlen(remote_name))) {
+ d_printf("reget <filename>\n");
+ return 1;
+ }
+ pstrcpy(local_name, p);
+ dos_clean_name(remote_name);
+
+ next_token_nr(NULL, local_name, NULL, sizeof(local_name));
+
+ return do_get(remote_name, local_name, True);
+}
+
+/****************************************************************************
+ Put a file restarting at end of local file.
+ ****************************************************************************/
+
+static int cmd_reput(void)
+{
+ pstring local_name;
+ pstring remote_name;
+ pstring buf;
+ char *p = buf;
+ SMB_STRUCT_STAT st;
+
+ pstrcpy(remote_name, cur_dir);
+ pstrcat(remote_name, "\\");
+
+ if (!next_token_nr(NULL, p, NULL, sizeof(buf))) {
+ d_printf("reput <filename>\n");
+ return 1;
+ }
+ pstrcpy(local_name, p);
+
+ if (!file_exist(local_name, &st)) {
+ d_printf("%s does not exist\n", local_name);
+ return 1;
+ }
+
+ if (next_token_nr(NULL, p, NULL, sizeof(buf)))
+ pstrcat(remote_name, p);
+ else
+ pstrcat(remote_name, local_name);
+
+ dos_clean_name(remote_name);
+
+ return do_put(remote_name, local_name, True);
+}
+
+/****************************************************************************
+ List a share name.
+ ****************************************************************************/
+
+static void browse_fn(const char *name, uint32 m,
+ const char *comment, void *state)
+{
+ fstring typestr;
+
+ *typestr=0;
+
+ switch (m)
+ {
+ case STYPE_DISKTREE:
+ fstrcpy(typestr,"Disk"); break;
+ case STYPE_PRINTQ:
+ fstrcpy(typestr,"Printer"); break;
+ case STYPE_DEVICE:
+ fstrcpy(typestr,"Device"); break;
+ case STYPE_IPC:
+ fstrcpy(typestr,"IPC"); break;
+ }
+ /* FIXME: If the remote machine returns non-ascii characters
+ in any of these fields, they can corrupt the output. We
+ should remove them. */
+ if (!grepable) {
+ d_printf("\t%-15s %-10.10s%s\n",
+ name,typestr,comment);
+ } else {
+ d_printf ("%s|%s|%s\n",typestr,name,comment);
+ }
+}
+
+/****************************************************************************
+ Try and browse available connections on a host.
+****************************************************************************/
+
+static BOOL browse_host(BOOL sort)
+{
+ int ret;
+ if (!grepable) {
+ d_printf("\n\tSharename Type Comment\n");
+ d_printf("\t--------- ---- -------\n");
+ }
+
+ if((ret = cli_RNetShareEnum(cli, browse_fn, NULL)) == -1)
+ d_printf("Error returning browse list: %s\n", cli_errstr(cli));
+
+ return (ret != -1);
+}
+
+/****************************************************************************
+ List a server name.
+****************************************************************************/
+
+static void server_fn(const char *name, uint32 m,
+ const char *comment, void *state)
+{
+
+ if (!grepable){
+ d_printf("\t%-16s %s\n", name, comment);
+ } else {
+ d_printf("%s|%s|%s\n",(char *)state, name, comment);
+ }
+}
+
+/****************************************************************************
+ Try and browse available connections on a host.
+****************************************************************************/
+
+static BOOL list_servers(const char *wk_grp)
+{
+ if (!cli->server_domain)
+ return False;
+
+ if (!grepable) {
+ d_printf("\n\tServer Comment\n");
+ d_printf("\t--------- -------\n");
+ };
+ cli_NetServerEnum(cli, cli->server_domain, SV_TYPE_ALL, server_fn,
+ "Server");
+
+ if (!grepable) {
+ d_printf("\n\tWorkgroup Master\n");
+ d_printf("\t--------- -------\n");
+ };
+
+ cli_NetServerEnum(cli, cli->server_domain, SV_TYPE_DOMAIN_ENUM,
+ server_fn, "Workgroup");
+ return True;
+}
+
+/****************************************************************************
+ Print or set current VUID
+****************************************************************************/
+
+static int cmd_vuid(void)
+{
+ fstring buf;
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ d_printf("Current VUID is %d\n", cli->vuid);
+ return 0;
+ }
+
+ cli->vuid = atoi(buf);
+ return 0;
+}
+
+/****************************************************************************
+ Setup a new VUID, by issuing a session setup
+****************************************************************************/
+
+static int cmd_logon(void)
+{
+ pstring l_username, l_password;
+ pstring buf,buf2;
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ d_printf("logon <username> [<password>]\n");
+ return 0;
+ }
+
+ pstrcpy(l_username, buf);
+
+ if (!next_token_nr(NULL,buf2,NULL,sizeof(buf))) {
+ char *pass = getpass("Password: ");
+ if (pass) {
+ pstrcpy(l_password, pass);
+ got_pass = 1;
+ }
+ } else {
+ pstrcpy(l_password, buf2);
+ }
+
+ if (!cli_session_setup(cli, l_username,
+ l_password, strlen(l_password),
+ l_password, strlen(l_password),
+ lp_workgroup())) {
+ d_printf("session setup failed: %s\n", cli_errstr(cli));
+ return -1;
+ }
+
+ d_printf("Current VUID is %d\n", cli->vuid);
+ return 0;
+}
+
+/* Some constants for completing filename arguments */
+
+#define COMPL_NONE 0 /* No completions */
+#define COMPL_REMOTE 1 /* Complete remote filename */
+#define COMPL_LOCAL 2 /* Complete local filename */
+
+/* This defines the commands supported by this client.
+ * NOTE: The "!" must be the last one in the list because it's fn pointer
+ * field is NULL, and NULL in that field is used in process_tok()
+ * (below) to indicate the end of the list. crh
+ */
+static struct
+{
+ const char *name;
+ int (*fn)(void);
+ const char *description;
+ char compl_args[2]; /* Completion argument info */
+} commands[] = {
+ {"?",cmd_help,"[command] give help on a command",{COMPL_NONE,COMPL_NONE}},
+ {"altname",cmd_altname,"<file> show alt name",{COMPL_NONE,COMPL_NONE}},
+ {"archive",cmd_archive,"<level>\n0=ignore archive bit\n1=only get archive files\n2=only get archive files and reset archive bit\n3=get all files and reset archive bit",{COMPL_NONE,COMPL_NONE}},
+ {"blocksize",cmd_block,"blocksize <number> (default 20)",{COMPL_NONE,COMPL_NONE}},
+ {"cancel",cmd_cancel,"<jobid> cancel a print queue entry",{COMPL_NONE,COMPL_NONE}},
+ {"cd",cmd_cd,"[directory] change/report the remote directory",{COMPL_REMOTE,COMPL_NONE}},
+ {"chmod",cmd_chmod,"<src> <mode> chmod a file using UNIX permission",{COMPL_REMOTE,COMPL_REMOTE}},
+ {"chown",cmd_chown,"<src> <uid> <gid> chown a file using UNIX uids and gids",{COMPL_REMOTE,COMPL_REMOTE}},
+ {"del",cmd_del,"<mask> delete all matching files",{COMPL_REMOTE,COMPL_NONE}},
+ {"dir",cmd_dir,"<mask> list the contents of the current directory",{COMPL_REMOTE,COMPL_NONE}},
+ {"du",cmd_du,"<mask> computes the total size of the current directory",{COMPL_REMOTE,COMPL_NONE}},
+ {"exit",cmd_quit,"logoff the server",{COMPL_NONE,COMPL_NONE}},
+ {"get",cmd_get,"<remote name> [local name] get a file",{COMPL_REMOTE,COMPL_LOCAL}},
+ {"hardlink",cmd_hardlink,"<src> <dest> create a Windows hard link",{COMPL_REMOTE,COMPL_REMOTE}},
+ {"help",cmd_help,"[command] give help on a command",{COMPL_NONE,COMPL_NONE}},
+ {"history",cmd_history,"displays the command history",{COMPL_NONE,COMPL_NONE}},
+ {"lcd",cmd_lcd,"[directory] change/report the local current working directory",{COMPL_LOCAL,COMPL_NONE}},
+ {"link",cmd_link,"<src> <dest> create a UNIX hard link",{COMPL_REMOTE,COMPL_REMOTE}},
+ {"lowercase",cmd_lowercase,"toggle lowercasing of filenames for get",{COMPL_NONE,COMPL_NONE}},
+ {"ls",cmd_dir,"<mask> list the contents of the current directory",{COMPL_REMOTE,COMPL_NONE}},
+ {"mask",cmd_select,"<mask> mask all filenames against this",{COMPL_REMOTE,COMPL_NONE}},
+ {"md",cmd_mkdir,"<directory> make a directory",{COMPL_NONE,COMPL_NONE}},
+ {"mget",cmd_mget,"<mask> get all the matching files",{COMPL_REMOTE,COMPL_NONE}},
+ {"mkdir",cmd_mkdir,"<directory> make a directory",{COMPL_NONE,COMPL_NONE}},
+ {"more",cmd_more,"<remote name> view a remote file with your pager",{COMPL_REMOTE,COMPL_NONE}},
+ {"mput",cmd_mput,"<mask> put all matching files",{COMPL_REMOTE,COMPL_NONE}},
+ {"newer",cmd_newer,"<file> only mget files newer than the specified local file",{COMPL_LOCAL,COMPL_NONE}},
+ {"open",cmd_open,"<mask> open a file",{COMPL_REMOTE,COMPL_NONE}},
+ {"print",cmd_print,"<file name> print a file",{COMPL_NONE,COMPL_NONE}},
+ {"printmode",cmd_printmode,"<graphics or text> set the print mode",{COMPL_NONE,COMPL_NONE}},
+ {"prompt",cmd_prompt,"toggle prompting for filenames for mget and mput",{COMPL_NONE,COMPL_NONE}},
+ {"put",cmd_put,"<local name> [remote name] put a file",{COMPL_LOCAL,COMPL_REMOTE}},
+ {"pwd",cmd_pwd,"show current remote directory (same as 'cd' with no args)",{COMPL_NONE,COMPL_NONE}},
+ {"q",cmd_quit,"logoff the server",{COMPL_NONE,COMPL_NONE}},
+ {"queue",cmd_queue,"show the print queue",{COMPL_NONE,COMPL_NONE}},
+ {"quit",cmd_quit,"logoff the server",{COMPL_NONE,COMPL_NONE}},
+ {"rd",cmd_rmdir,"<directory> remove a directory",{COMPL_NONE,COMPL_NONE}},
+ {"recurse",cmd_recurse,"toggle directory recursion for mget and mput",{COMPL_NONE,COMPL_NONE}},
+ {"reget",cmd_reget,"<remote name> [local name] get a file restarting at end of local file",{COMPL_REMOTE,COMPL_LOCAL}},
+ {"rename",cmd_rename,"<src> <dest> rename some files",{COMPL_REMOTE,COMPL_REMOTE}},
+ {"reput",cmd_reput,"<local name> [remote name] put a file restarting at end of remote file",{COMPL_LOCAL,COMPL_REMOTE}},
+ {"rm",cmd_del,"<mask> delete all matching files",{COMPL_REMOTE,COMPL_NONE}},
+ {"rmdir",cmd_rmdir,"<directory> remove a directory",{COMPL_NONE,COMPL_NONE}},
+ {"setmode",cmd_setmode,"filename <setmode string> change modes of file",{COMPL_REMOTE,COMPL_NONE}},
+ {"symlink",cmd_symlink,"<src> <dest> create a UNIX symlink",{COMPL_REMOTE,COMPL_REMOTE}},
+ {"tar",cmd_tar,"tar <c|x>[IXFqbgNan] current directory to/from <file name>",{COMPL_NONE,COMPL_NONE}},
+ {"tarmode",cmd_tarmode,"<full|inc|reset|noreset> tar's behaviour towards archive bits",{COMPL_NONE,COMPL_NONE}},
+ {"translate",cmd_translate,"toggle text translation for printing",{COMPL_NONE,COMPL_NONE}},
+ {"vuid",cmd_vuid,"change current vuid",{COMPL_NONE,COMPL_NONE}},
+ {"logon",cmd_logon,"establish new logon",{COMPL_NONE,COMPL_NONE}},
+
+ /* Yes, this must be here, see crh's comment above. */
+ {"!",NULL,"run a shell command on the local system",{COMPL_NONE,COMPL_NONE}},
+ {NULL,NULL,NULL,{COMPL_NONE,COMPL_NONE}}
+};
+
+/*******************************************************************
+ Lookup a command string in the list of commands, including
+ abbreviations.
+******************************************************************/
+
+static int process_tok(pstring tok)
+{
+ int i = 0, matches = 0;
+ int cmd=0;
+ int tok_len = strlen(tok);
+
+ while (commands[i].fn != NULL) {
+ if (strequal(commands[i].name,tok)) {
+ matches = 1;
+ cmd = i;
+ break;
+ } else if (strnequal(commands[i].name, tok, tok_len)) {
+ matches++;
+ cmd = i;
+ }
+ i++;
+ }
+
+ if (matches == 0)
+ return(-1);
+ else if (matches == 1)
+ return(cmd);
+ else
+ return(-2);
+}
+
+/****************************************************************************
+ Help.
+****************************************************************************/
+
+static int cmd_help(void)
+{
+ int i=0,j;
+ pstring buf;
+
+ if (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ if ((i = process_tok(buf)) >= 0)
+ d_printf("HELP %s:\n\t%s\n\n",commands[i].name,commands[i].description);
+ } else {
+ while (commands[i].description) {
+ for (j=0; commands[i].description && (j<5); j++) {
+ d_printf("%-15s",commands[i].name);
+ i++;
+ }
+ d_printf("\n");
+ }
+ }
+ return 0;
+}
+
+/****************************************************************************
+ Process a -c command string.
+****************************************************************************/
+
+static int process_command_string(char *cmd)
+{
+ pstring line;
+ const char *ptr;
+ int rc = 0;
+
+ /* establish the connection if not already */
+
+ if (!cli) {
+ cli = do_connect(desthost, service);
+ if (!cli)
+ return 0;
+ }
+
+ while (cmd[0] != '\0') {
+ char *p;
+ pstring tok;
+ int i;
+
+ if ((p = strchr_m(cmd, ';')) == 0) {
+ strncpy(line, cmd, 999);
+ line[1000] = '\0';
+ cmd += strlen(cmd);
+ } else {
+ if (p - cmd > 999)
+ p = cmd + 999;
+ strncpy(line, cmd, p - cmd);
+ line[p - cmd] = '\0';
+ cmd = p + 1;
+ }
+
+ /* and get the first part of the command */
+ ptr = line;
+ if (!next_token_nr(&ptr,tok,NULL,sizeof(tok))) continue;
+
+ if ((i = process_tok(tok)) >= 0) {
+ rc = commands[i].fn();
+ } else if (i == -2) {
+ d_printf("%s: command abbreviation ambiguous\n",tok);
+ } else {
+ d_printf("%s: command not found\n",tok);
+ }
+ }
+
+ return rc;
+}
+
+#define MAX_COMPLETIONS 100
+
+typedef struct {
+ pstring dirmask;
+ char **matches;
+ int count, samelen;
+ const char *text;
+ int len;
+} completion_remote_t;
+
+static void completion_remote_filter(file_info *f, const char *mask, void *state)
+{
+ completion_remote_t *info = (completion_remote_t *)state;
+
+ if ((info->count < MAX_COMPLETIONS - 1) && (strncmp(info->text, f->name, info->len) == 0) && (strcmp(f->name, ".") != 0) && (strcmp(f->name, "..") != 0)) {
+ if ((info->dirmask[0] == 0) && !(f->mode & aDIR))
+ info->matches[info->count] = strdup(f->name);
+ else {
+ pstring tmp;
+
+ if (info->dirmask[0] != 0)
+ pstrcpy(tmp, info->dirmask);
+ else
+ tmp[0] = 0;
+ pstrcat(tmp, f->name);
+ if (f->mode & aDIR)
+ pstrcat(tmp, "/");
+ info->matches[info->count] = strdup(tmp);
+ }
+ if (info->matches[info->count] == NULL)
+ return;
+ if (f->mode & aDIR)
+ smb_readline_ca_char(0);
+
+ if (info->count == 1)
+ info->samelen = strlen(info->matches[info->count]);
+ else
+ while (strncmp(info->matches[info->count], info->matches[info->count-1], info->samelen) != 0)
+ info->samelen--;
+ info->count++;
+ }
+}
+
+static char **remote_completion(const char *text, int len)
+{
+ pstring dirmask;
+ int i;
+ completion_remote_t info = { "", NULL, 1, 0, NULL, 0 };
+
+ /* can't have non-static intialisation on Sun CC, so do it
+ at run time here */
+ info.samelen = len;
+ info.text = text;
+ info.len = len;
+
+ if (len >= PATH_MAX)
+ return(NULL);
+
+ info.matches = (char **)malloc(sizeof(info.matches[0])*MAX_COMPLETIONS);
+ if (!info.matches) return NULL;
+ info.matches[0] = NULL;
+
+ for (i = len-1; i >= 0; i--)
+ if ((text[i] == '/') || (text[i] == '\\'))
+ break;
+ info.text = text+i+1;
+ info.samelen = info.len = len-i-1;
+
+ if (i > 0) {
+ strncpy(info.dirmask, text, i+1);
+ info.dirmask[i+1] = 0;
+ pstr_sprintf(dirmask, "%s%*s*", cur_dir, i-1, text);
+ } else
+ pstr_sprintf(dirmask, "%s*", cur_dir);
+
+ if (cli_list(cli, dirmask, aDIR | aSYSTEM | aHIDDEN, completion_remote_filter, &info) < 0)
+ goto cleanup;
+
+ if (info.count == 2)
+ info.matches[0] = strdup(info.matches[1]);
+ else {
+ info.matches[0] = malloc(info.samelen+1);
+ if (!info.matches[0])
+ goto cleanup;
+ strncpy(info.matches[0], info.matches[1], info.samelen);
+ info.matches[0][info.samelen] = 0;
+ }
+ info.matches[info.count] = NULL;
+ return info.matches;
+
+cleanup:
+ for (i = 0; i < info.count; i++)
+ free(info.matches[i]);
+ free(info.matches);
+ return NULL;
+}
+
+static char **completion_fn(const char *text, int start, int end)
+{
+ smb_readline_ca_char(' ');
+
+ if (start) {
+ const char *buf, *sp;
+ int i;
+ char compl_type;
+
+ buf = smb_readline_get_line_buffer();
+ if (buf == NULL)
+ return NULL;
+
+ sp = strchr(buf, ' ');
+ if (sp == NULL)
+ return NULL;
+
+ for (i = 0; commands[i].name; i++)
+ if ((strncmp(commands[i].name, text, sp - buf) == 0) && (commands[i].name[sp - buf] == 0))
+ break;
+ if (commands[i].name == NULL)
+ return NULL;
+
+ while (*sp == ' ')
+ sp++;
+
+ if (sp == (buf + start))
+ compl_type = commands[i].compl_args[0];
+ else
+ compl_type = commands[i].compl_args[1];
+
+ if (compl_type == COMPL_REMOTE)
+ return remote_completion(text, end - start);
+ else /* fall back to local filename completion */
+ return NULL;
+ } else {
+ char **matches;
+ int i, len, samelen, count=1;
+
+ matches = (char **)malloc(sizeof(matches[0])*MAX_COMPLETIONS);
+ if (!matches) return NULL;
+ matches[0] = NULL;
+
+ len = strlen(text);
+ for (i=0;commands[i].fn && count < MAX_COMPLETIONS-1;i++) {
+ if (strncmp(text, commands[i].name, len) == 0) {
+ matches[count] = strdup(commands[i].name);
+ if (!matches[count])
+ goto cleanup;
+ if (count == 1)
+ samelen = strlen(matches[count]);
+ else
+ while (strncmp(matches[count], matches[count-1], samelen) != 0)
+ samelen--;
+ count++;
+ }
+ }
+
+ switch (count) {
+ case 0: /* should never happen */
+ case 1:
+ goto cleanup;
+ case 2:
+ matches[0] = strdup(matches[1]);
+ break;
+ default:
+ matches[0] = malloc(samelen+1);
+ if (!matches[0])
+ goto cleanup;
+ strncpy(matches[0], matches[1], samelen);
+ matches[0][samelen] = 0;
+ }
+ matches[count] = NULL;
+ return matches;
+
+cleanup:
+ for (i = 0; i < count; i++)
+ free(matches[i]);
+
+ free(matches);
+ return NULL;
+ }
+}
+
+/****************************************************************************
+ Make sure we swallow keepalives during idle time.
+****************************************************************************/
+
+static void readline_callback(void)
+{
+ fd_set fds;
+ struct timeval timeout;
+ static time_t last_t;
+ time_t t;
+
+ t = time(NULL);
+
+ if (t - last_t < 5)
+ return;
+
+ last_t = t;
+
+ again:
+
+ if (cli->fd == -1)
+ return;
+
+ FD_ZERO(&fds);
+ FD_SET(cli->fd,&fds);
+
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+ sys_select_intr(cli->fd+1,&fds,NULL,NULL,&timeout);
+
+ /* We deliberately use receive_smb instead of
+ client_receive_smb as we want to receive
+ session keepalives and then drop them here.
+ */
+ if (FD_ISSET(cli->fd,&fds)) {
+ receive_smb(cli->fd,cli->inbuf,0);
+ goto again;
+ }
+
+ cli_chkpath(cli, "\\");
+}
+
+/****************************************************************************
+ Process commands on stdin.
+****************************************************************************/
+
+static void process_stdin(void)
+{
+ const char *ptr;
+
+ while (1) {
+ pstring tok;
+ pstring the_prompt;
+ char *cline;
+ pstring line;
+ int i;
+
+ /* display a prompt */
+ slprintf(the_prompt, sizeof(the_prompt)-1, "smb: %s> ", cur_dir);
+ cline = smb_readline(the_prompt, readline_callback, completion_fn);
+
+ if (!cline) break;
+
+ pstrcpy(line, cline);
+
+ /* special case - first char is ! */
+ if (*line == '!') {
+ system(line + 1);
+ continue;
+ }
+
+ /* and get the first part of the command */
+ ptr = line;
+ if (!next_token_nr(&ptr,tok,NULL,sizeof(tok))) continue;
+
+ if ((i = process_tok(tok)) >= 0) {
+ commands[i].fn();
+ } else if (i == -2) {
+ d_printf("%s: command abbreviation ambiguous\n",tok);
+ } else {
+ d_printf("%s: command not found\n",tok);
+ }
+ }
+}
+
+/*****************************************************
+ Return a connection to a server.
+*******************************************************/
+
+static struct cli_state *do_connect(const char *server, const char *share)
+{
+ struct cli_state *c;
+ struct nmb_name called, calling;
+ const char *server_n;
+ struct in_addr ip;
+ pstring servicename;
+ char *sharename;
+
+ /* make a copy so we don't modify the global string 'service' */
+ pstrcpy(servicename, share);
+ sharename = servicename;
+ if (*sharename == '\\') {
+ server = sharename+2;
+ sharename = strchr_m(server,'\\');
+ if (!sharename) return NULL;
+ *sharename = 0;
+ sharename++;
+ }
+
+ server_n = server;
+
+ zero_ip(&ip);
+
+ make_nmb_name(&calling, global_myname(), 0x0);
+ make_nmb_name(&called , server, name_type);
+
+ again:
+ zero_ip(&ip);
+ if (have_ip) ip = dest_ip;
+
+ /* have to open a new connection */
+ if (!(c=cli_initialise(NULL)) || (cli_set_port(c, port) != port) ||
+ !cli_connect(c, server_n, &ip)) {
+ d_printf("Connection to %s failed\n", server_n);
+ return NULL;
+ }
+
+ c->protocol = max_protocol;
+ c->use_kerberos = use_kerberos;
+ cli_setup_signing_state(c, cmdline_auth_info.signing_state);
+
+
+ if (!cli_session_request(c, &calling, &called)) {
+ char *p;
+ d_printf("session request to %s failed (%s)\n",
+ called.name, cli_errstr(c));
+ cli_shutdown(c);
+ if ((p=strchr_m(called.name, '.'))) {
+ *p = 0;
+ goto again;
+ }
+ if (strcmp(called.name, "*SMBSERVER")) {
+ make_nmb_name(&called , "*SMBSERVER", 0x20);
+ goto again;
+ }
+ return NULL;
+ }
+
+ DEBUG(4,(" session request ok\n"));
+
+ if (!cli_negprot(c)) {
+ d_printf("protocol negotiation failed\n");
+ cli_shutdown(c);
+ return NULL;
+ }
+
+ if (!got_pass) {
+ char *pass = getpass("Password: ");
+ if (pass) {
+ pstrcpy(password, pass);
+ got_pass = 1;
+ }
+ }
+
+ if (!cli_session_setup(c, username,
+ password, strlen(password),
+ password, strlen(password),
+ lp_workgroup())) {
+ /* if a password was not supplied then try again with a null username */
+ if (password[0] || !username[0] || use_kerberos ||
+ !cli_session_setup(c, "", "", 0, "", 0, lp_workgroup())) {
+ d_printf("session setup failed: %s\n", cli_errstr(c));
+ if (NT_STATUS_V(cli_nt_error(c)) ==
+ NT_STATUS_V(NT_STATUS_MORE_PROCESSING_REQUIRED))
+ d_printf("did you forget to run kinit?\n");
+ cli_shutdown(c);
+ return NULL;
+ }
+ d_printf("Anonymous login successful\n");
+ }
+
+ if (*c->server_domain) {
+ DEBUG(1,("Domain=[%s] OS=[%s] Server=[%s]\n",
+ c->server_domain,c->server_os,c->server_type));
+ } else if (*c->server_os || *c->server_type){
+ DEBUG(1,("OS=[%s] Server=[%s]\n",
+ c->server_os,c->server_type));
+ }
+ DEBUG(4,(" session setup ok\n"));
+
+ if (!cli_send_tconX(c, sharename, "?????",
+ password, strlen(password)+1)) {
+ d_printf("tree connect failed: %s\n", cli_errstr(c));
+ cli_shutdown(c);
+ return NULL;
+ }
+
+ DEBUG(4,(" tconx ok\n"));
+
+ return c;
+}
+
+/****************************************************************************
+ Process commands from the client.
+****************************************************************************/
+
+static int process(char *base_directory)
+{
+ int rc = 0;
+
+ cli = do_connect(desthost, service);
+ if (!cli) {
+ return 1;
+ }
+
+ if (*base_directory) do_cd(base_directory);
+
+ if (cmdstr) {
+ rc = process_command_string(cmdstr);
+ } else {
+ process_stdin();
+ }
+
+ cli_shutdown(cli);
+ return rc;
+}
+
+/****************************************************************************
+ Handle a -L query.
+****************************************************************************/
+
+static int do_host_query(char *query_host)
+{
+ cli = do_connect(query_host, "IPC$");
+ if (!cli)
+ return 1;
+
+ browse_host(True);
+
+ if (port != 139) {
+
+ /* Workgroups simply don't make sense over anything
+ else but port 139... */
+
+ cli_shutdown(cli);
+ port = 139;
+ cli = do_connect(query_host, "IPC$");
+ }
+
+ if (cli == NULL) {
+ d_printf("NetBIOS over TCP disabled -- no workgroup available\n");
+ return 1;
+ }
+
+ list_servers(lp_workgroup());
+
+ cli_shutdown(cli);
+
+ return(0);
+}
+
+
+/****************************************************************************
+ Handle a tar operation.
+****************************************************************************/
+
+static int do_tar_op(char *base_directory)
+{
+ int ret;
+
+ /* do we already have a connection? */
+ if (!cli) {
+ cli = do_connect(desthost, service);
+ if (!cli)
+ return 1;
+ }
+
+ recurse=True;
+
+ if (*base_directory) do_cd(base_directory);
+
+ ret=process_tar();
+
+ cli_shutdown(cli);
+
+ return(ret);
+}
+
+/****************************************************************************
+ Handle a message operation.
+****************************************************************************/
+
+static int do_message_op(void)
+{
+ struct in_addr ip;
+ struct nmb_name called, calling;
+ fstring server_name;
+ char name_type_hex[10];
+
+ make_nmb_name(&calling, global_myname(), 0x0);
+ make_nmb_name(&called , desthost, name_type);
+
+ fstrcpy(server_name, desthost);
+ snprintf(name_type_hex, sizeof(name_type_hex), "#%X", name_type);
+ fstrcat(server_name, name_type_hex);
+
+ zero_ip(&ip);
+ if (have_ip) ip = dest_ip;
+
+ if (!(cli=cli_initialise(NULL)) || (cli_set_port(cli, port) != port) ||
+ !cli_connect(cli, server_name, &ip)) {
+ d_printf("Connection to %s failed\n", desthost);
+ return 1;
+ }
+
+ if (!cli_session_request(cli, &calling, &called)) {
+ d_printf("session request failed\n");
+ cli_shutdown(cli);
+ return 1;
+ }
+
+ send_message();
+ cli_shutdown(cli);
+
+ return 0;
+}
+
+
+/****************************************************************************
+ main program
+****************************************************************************/
+
+ int main(int argc,char *argv[])
+{
+ extern BOOL AllowDebugChange;
+ extern BOOL override_logfile;
+ pstring base_directory;
+ int opt;
+ pstring query_host;
+ BOOL message = False;
+ extern char tar_type;
+ pstring term_code;
+ static const char *new_name_resolve_order = NULL;
+ poptContext pc;
+ char *p;
+ int rc = 0;
+ fstring new_workgroup;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+
+ { "name-resolve", 'R', POPT_ARG_STRING, &new_name_resolve_order, 'R', "Use these name resolution services only", "NAME-RESOLVE-ORDER" },
+ { "message", 'M', POPT_ARG_STRING, NULL, 'M', "Send message", "HOST" },
+ { "ip-address", 'I', POPT_ARG_STRING, NULL, 'I', "Use this IP to connect to", "IP" },
+ { "stderr", 'E', POPT_ARG_NONE, NULL, 'E', "Write messages to stderr instead of stdout" },
+ { "list", 'L', POPT_ARG_STRING, NULL, 'L', "Get a list of shares available on a host", "HOST" },
+ { "terminal", 't', POPT_ARG_STRING, NULL, 't', "Terminal I/O code {sjis|euc|jis7|jis8|junet|hex}", "CODE" },
+ { "max-protocol", 'm', POPT_ARG_STRING, NULL, 'm', "Set the max protocol level", "LEVEL" },
+ { "tar", 'T', POPT_ARG_STRING, NULL, 'T', "Command line tar", "<c|x>IXFqgbNan" },
+ { "directory", 'D', POPT_ARG_STRING, NULL, 'D', "Start from directory", "DIR" },
+ { "command", 'c', POPT_ARG_STRING, &cmdstr, 'c', "Execute semicolon separated commands" },
+ { "send-buffer", 'b', POPT_ARG_INT, &io_bufsize, 'b', "Changes the transmit/send buffer", "BYTES" },
+ { "port", 'p', POPT_ARG_INT, &port, 'p', "Port to connect to", "PORT" },
+ { "grepable", 'g', POPT_ARG_NONE, NULL, 'g', "Produce grepable output" },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ POPT_COMMON_CREDENTIALS
+ POPT_TABLEEND
+ };
+
+
+#ifdef KANJI
+ pstrcpy(term_code, KANJI);
+#else /* KANJI */
+ *term_code = 0;
+#endif /* KANJI */
+
+ *query_host = 0;
+ *base_directory = 0;
+
+ /* initialize the workgroup name so we can determine whether or
+ not it was set by a command line option */
+
+ set_global_myworkgroup( "" );
+
+ /* set default debug level to 0 regardless of what smb.conf sets */
+ setup_logging( "smbclient", True );
+ DEBUGLEVEL_CLASS[DBGC_ALL] = 1;
+ dbf = x_stderr;
+ x_setbuf( x_stderr, NULL );
+
+ pc = poptGetContext("smbclient", argc, (const char **) argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+ poptSetOtherOptionHelp(pc, "service <password>");
+
+ in_client = True; /* Make sure that we tell lp_load we are */
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'M':
+ /* Messages are sent to NetBIOS name type 0x3
+ * (Messenger Service). Make sure we default
+ * to port 139 instead of port 445. srl,crh
+ */
+ name_type = 0x03;
+ pstrcpy(desthost,poptGetOptArg(pc));
+ if( 0 == port )
+ port = 139;
+ message = True;
+ break;
+ case 'I':
+ {
+ dest_ip = *interpret_addr2(poptGetOptArg(pc));
+ if (is_zero_ip(dest_ip))
+ exit(1);
+ have_ip = True;
+ }
+ break;
+ case 'E':
+ dbf = x_stderr;
+ display_set_stderr();
+ break;
+
+ case 'L':
+ pstrcpy(query_host, poptGetOptArg(pc));
+ break;
+ case 't':
+ pstrcpy(term_code, poptGetOptArg(pc));
+ break;
+ case 'm':
+ max_protocol = interpret_protocol(poptGetOptArg(pc), max_protocol);
+ break;
+ case 'T':
+ /* We must use old option processing for this. Find the
+ * position of the -T option in the raw argv[]. */
+ {
+ int i, optnum;
+ for (i = 1; i < argc; i++) {
+ if (strncmp("-T", argv[i],2)==0)
+ break;
+ }
+ i++;
+ if (!(optnum = tar_parseargs(argc, argv, poptGetOptArg(pc), i))) {
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ /* Now we must eat (optnum - i) options - they have
+ * been processed by tar_parseargs().
+ */
+ optnum -= i;
+ for (i = 0; i < optnum; i++)
+ poptGetOptArg(pc);
+ }
+ break;
+ case 'D':
+ pstrcpy(base_directory,poptGetOptArg(pc));
+ break;
+ case 'g':
+ grepable=True;
+ break;
+ }
+ }
+
+ poptGetArg(pc);
+
+ /*
+ * Don't load debug level from smb.conf. It should be
+ * set by cmdline arg or remain default (0)
+ */
+ AllowDebugChange = False;
+
+ /* save the workgroup...
+
+ FIXME!! do we need to do this for other options as well
+ (or maybe a generic way to keep lp_load() from overwriting
+ everything)? */
+
+ fstrcpy( new_workgroup, lp_workgroup() );
+
+ if ( override_logfile )
+ setup_logging( lp_logfile(), False );
+
+ if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
+ fprintf(stderr, "%s: Can't load %s - run testparm to debug it\n",
+ argv[0], dyn_CONFIGFILE);
+ }
+
+ load_interfaces();
+
+ if ( strlen(new_workgroup) != 0 )
+ set_global_myworkgroup( new_workgroup );
+
+ if(poptPeekArg(pc)) {
+ pstrcpy(service,poptGetArg(pc));
+ /* Convert any '/' characters in the service name to '\' characters */
+ string_replace(service, '/','\\');
+
+ if (count_chars(service,'\\') < 3) {
+ d_printf("\n%s: Not enough '\\' characters in service\n",service);
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+
+ if (poptPeekArg(pc) && !cmdline_auth_info.got_pass) {
+ cmdline_auth_info.got_pass = True;
+ pstrcpy(cmdline_auth_info.password,poptGetArg(pc));
+ }
+
+ init_names();
+
+ if(new_name_resolve_order)
+ lp_set_name_resolve_order(new_name_resolve_order);
+
+ if (!tar_type && !*query_host && !*service && !message) {
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+
+ poptFreeContext(pc);
+
+ pstrcpy(username, cmdline_auth_info.username);
+ pstrcpy(password, cmdline_auth_info.password);
+
+ use_kerberos = cmdline_auth_info.use_kerberos;
+ got_pass = cmdline_auth_info.got_pass;
+
+ DEBUG(3,("Client started (version %s).\n", SAMBA_VERSION_STRING));
+
+ if (tar_type) {
+ if (cmdstr)
+ process_command_string(cmdstr);
+ return do_tar_op(base_directory);
+ }
+
+ if (*query_host) {
+ char *qhost = query_host;
+ char *slash;
+
+ while (*qhost == '\\' || *qhost == '/')
+ qhost++;
+
+ if ((slash = strchr_m(qhost, '/'))
+ || (slash = strchr_m(qhost, '\\'))) {
+ *slash = 0;
+ }
+
+ if ((p=strchr_m(qhost, '#'))) {
+ *p = 0;
+ p++;
+ sscanf(p, "%x", &name_type);
+ }
+
+ return do_host_query(qhost);
+ }
+
+ if (message) {
+ return do_message_op();
+ }
+
+ if (process(base_directory)) {
+ return 1;
+ }
+
+ return rc;
+}
diff --git a/source/client/clitar.c b/source/client/clitar.c
new file mode 100644
index 00000000000..e43b3e4cc50
--- /dev/null
+++ b/source/client/clitar.c
@@ -0,0 +1,1807 @@
+/*
+ Unix SMB/CIFS implementation.
+ Tar Extensions
+ Copyright (C) Ricky Poulten 1995-1998
+ Copyright (C) Richard Sharpe 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+/* The following changes developed by Richard Sharpe for Canon Information
+ Systems Research Australia (CISRA)
+
+ 1. Restore can now restore files with long file names
+ 2. Save now saves directory information so that we can restore
+ directory creation times
+ 3. tar now accepts both UNIX path names and DOS path names. I prefer
+ those lovely /'s to those UGLY \'s :-)
+ 4. the files to exclude can be specified as a regular expression by adding
+ an r flag to the other tar flags. Eg:
+
+ -TcrX file.tar "*.(obj|exe)"
+
+ will skip all .obj and .exe files
+*/
+
+
+#include "includes.h"
+#include "clitar.h"
+#include "../client/client_proto.h"
+
+static int clipfind(char **aret, int ret, char *tok);
+
+typedef struct file_info_struct file_info2;
+
+struct file_info_struct {
+ SMB_BIG_UINT size;
+ uint16 mode;
+ uid_t uid;
+ gid_t gid;
+ /* These times are normally kept in GMT */
+ time_t mtime;
+ time_t atime;
+ time_t ctime;
+ char *name; /* This is dynamically allocate */
+
+ file_info2 *next, *prev; /* Used in the stack ... */
+};
+
+typedef struct {
+ file_info2 *top;
+ int items;
+} stack;
+
+#define SEPARATORS " \t\n\r"
+extern struct cli_state *cli;
+
+/* These defines are for the do_setrattr routine, to indicate
+ * setting and reseting of file attributes in the function call */
+#define ATTRSET 1
+#define ATTRRESET 0
+
+static uint16 attribute = aDIR | aSYSTEM | aHIDDEN;
+
+#ifndef CLIENT_TIMEOUT
+#define CLIENT_TIMEOUT (30*1000)
+#endif
+
+static char *tarbuf, *buffer_p;
+static int tp, ntarf, tbufsiz;
+static double ttarf;
+/* Incremental mode */
+static BOOL tar_inc=False;
+/* Reset archive bit */
+static BOOL tar_reset=False;
+/* Include / exclude mode (true=include, false=exclude) */
+static BOOL tar_excl=True;
+/* use regular expressions for search on file names */
+static BOOL tar_re_search=False;
+#ifdef HAVE_REGEX_H
+regex_t *preg;
+#endif
+/* Do not dump anything, just calculate sizes */
+static BOOL dry_run=False;
+/* Dump files with System attribute */
+static BOOL tar_system=True;
+/* Dump files with Hidden attribute */
+static BOOL tar_hidden=True;
+/* Be noisy - make a catalogue */
+static BOOL tar_noisy=True;
+static BOOL tar_real_noisy=False; /* Don't want to be really noisy by default */
+
+char tar_type='\0';
+static char **cliplist=NULL;
+static int clipn=0;
+static BOOL must_free_cliplist = False;
+
+extern file_info def_finfo;
+extern BOOL lowercase;
+extern uint16 cnum;
+extern BOOL readbraw_supported;
+extern int max_xmit;
+extern pstring cur_dir;
+extern int get_total_time_ms;
+extern int get_total_size;
+
+static int blocksize=20;
+static int tarhandle;
+
+static void writetarheader(int f, const char *aname, SMB_BIG_UINT size, time_t mtime,
+ const char *amode, unsigned char ftype);
+static void do_atar(char *rname,char *lname,file_info *finfo1);
+static void do_tar(file_info *finfo);
+static void oct_it(SMB_BIG_UINT value, int ndgs, char *p);
+static void fixtarname(char *tptr, const char *fp, size_t l);
+static int dotarbuf(int f, char *b, int n);
+static void dozerobuf(int f, int n);
+static void dotareof(int f);
+static void initarbuf(void);
+
+/* restore functions */
+static long readtarheader(union hblock *hb, file_info2 *finfo, char *prefix);
+static long unoct(char *p, int ndgs);
+static void do_tarput(void);
+static void unfixtarname(char *tptr, char *fp, int l, BOOL first);
+
+/*
+ * tar specific utitlities
+ */
+
+/*******************************************************************
+Create a string of size size+1 (for the null)
+*******************************************************************/
+
+static char *string_create_s(int size)
+{
+ char *tmp;
+
+ tmp = (char *)malloc(size+1);
+
+ if (tmp == NULL) {
+ DEBUG(0, ("Out of memory in string_create_s\n"));
+ }
+
+ return(tmp);
+}
+
+/****************************************************************************
+Write a tar header to buffer
+****************************************************************************/
+
+static void writetarheader(int f, const char *aname, SMB_BIG_UINT size, time_t mtime,
+ const char *amode, unsigned char ftype)
+{
+ union hblock hb;
+ int i, chk, l;
+ char *jp;
+
+ DEBUG(5, ("WriteTarHdr, Type = %c, Size= %.0f, Name = %s\n", ftype, (double)size, aname));
+
+ memset(hb.dummy, 0, sizeof(hb.dummy));
+
+ l=strlen(aname);
+ /* We will be prepending a '.' in fixtarheader so use +2 to
+ * take care of the . and terminating zero. JRA.
+ */
+ if (l+2 >= NAMSIZ) {
+ /* write a GNU tar style long header */
+ char *b;
+ b = (char *)malloc(l+TBLOCK+100);
+ if (!b) {
+ DEBUG(0,("out of memory\n"));
+ exit(1);
+ }
+ writetarheader(f, "/./@LongLink", l+2, 0, " 0 \0", 'L');
+ memset(b, 0, l+TBLOCK+100);
+ fixtarname(b, aname, l+2);
+ i = strlen(b)+1;
+ DEBUG(5, ("File name in tar file: %s, size=%d, \n", b, (int)strlen(b)));
+ dotarbuf(f, b, TBLOCK*(((i-1)/TBLOCK)+1));
+ SAFE_FREE(b);
+ }
+
+ fixtarname(hb.dbuf.name, aname, (l+2 >= NAMSIZ) ? NAMSIZ : l + 2);
+
+ if (lowercase)
+ strlower_m(hb.dbuf.name);
+
+ /* write out a "standard" tar format header */
+
+ hb.dbuf.name[NAMSIZ-1]='\0';
+ safe_strcpy(hb.dbuf.mode, amode, sizeof(hb.dbuf.mode)-1);
+ oct_it((SMB_BIG_UINT)0, 8, hb.dbuf.uid);
+ oct_it((SMB_BIG_UINT)0, 8, hb.dbuf.gid);
+ oct_it((SMB_BIG_UINT) size, 13, hb.dbuf.size);
+ if (size > (SMB_BIG_UINT)077777777777LL) {
+
+ /* This is a non-POSIX compatible extention to store files
+ greater than 8GB. */
+
+ memset(hb.dbuf.size, 0, 4);
+ hb.dbuf.size[0]=128;
+ for (i = 8, jp=(char*)&size; i; i--)
+ hb.dbuf.size[i+3] = *(jp++);
+ }
+ oct_it((SMB_BIG_UINT) mtime, 13, hb.dbuf.mtime);
+ memcpy(hb.dbuf.chksum, " ", sizeof(hb.dbuf.chksum));
+ memset(hb.dbuf.linkname, 0, NAMSIZ);
+ hb.dbuf.linkflag=ftype;
+
+ for (chk=0, i=sizeof(hb.dummy), jp=hb.dummy; --i>=0;)
+ chk+=(0xFF & *jp++);
+
+ oct_it((SMB_BIG_UINT) chk, 8, hb.dbuf.chksum);
+ hb.dbuf.chksum[6] = '\0';
+
+ (void) dotarbuf(f, hb.dummy, sizeof(hb.dummy));
+}
+
+/****************************************************************************
+Read a tar header into a hblock structure, and validate
+***************************************************************************/
+
+static long readtarheader(union hblock *hb, file_info2 *finfo, char *prefix)
+{
+ long chk, fchk;
+ int i;
+ char *jp;
+
+ /*
+ * read in a "standard" tar format header - we're not that interested
+ * in that many fields, though
+ */
+
+ /* check the checksum */
+ for (chk=0, i=sizeof(hb->dummy), jp=hb->dummy; --i>=0;)
+ chk+=(0xFF & *jp++);
+
+ if (chk == 0)
+ return chk;
+
+ /* compensate for blanks in chksum header */
+ for (i=sizeof(hb->dbuf.chksum), jp=hb->dbuf.chksum; --i>=0;)
+ chk-=(0xFF & *jp++);
+
+ chk += ' ' * sizeof(hb->dbuf.chksum);
+
+ fchk=unoct(hb->dbuf.chksum, sizeof(hb->dbuf.chksum));
+
+ DEBUG(5, ("checksum totals chk=%ld fchk=%ld chksum=%s\n",
+ chk, fchk, hb->dbuf.chksum));
+
+ if (fchk != chk) {
+ DEBUG(0, ("checksums don't match %ld %ld\n", fchk, chk));
+ dump_data(5, (char *)hb - TBLOCK, TBLOCK *3);
+ return -1;
+ }
+
+ if ((finfo->name = string_create_s(strlen(prefix) + strlen(hb -> dbuf.name) + 3)) == NULL) {
+ DEBUG(0, ("Out of space creating file_info2 for %s\n", hb -> dbuf.name));
+ return(-1);
+ }
+
+ safe_strcpy(finfo->name, prefix, strlen(prefix) + strlen(hb -> dbuf.name) + 3);
+
+ /* use l + 1 to do the null too; do prefix - prefcnt to zap leading slash */
+ unfixtarname(finfo->name + strlen(prefix), hb->dbuf.name,
+ strlen(hb->dbuf.name) + 1, True);
+
+ /* can't handle some links at present */
+ if ((hb->dbuf.linkflag != '0') && (hb -> dbuf.linkflag != '5')) {
+ if (hb->dbuf.linkflag == 0) {
+ DEBUG(6, ("Warning: NULL link flag (gnu tar archive ?) %s\n",
+ finfo->name));
+ } else {
+ if (hb -> dbuf.linkflag == 'L') { /* We have a longlink */
+ /* Do nothing here at the moment. do_tarput will handle this
+ as long as the longlink gets back to it, as it has to advance
+ the buffer pointer, etc */
+ } else {
+ DEBUG(0, ("this tar file appears to contain some kind \
+of link other than a GNUtar Longlink - ignoring\n"));
+ return -2;
+ }
+ }
+ }
+
+ if ((unoct(hb->dbuf.mode, sizeof(hb->dbuf.mode)) & S_IFDIR) ||
+ (*(finfo->name+strlen(finfo->name)-1) == '\\')) {
+ finfo->mode=aDIR;
+ } else {
+ finfo->mode=0; /* we don't care about mode at the moment, we'll
+ * just make it a regular file */
+ }
+
+ /*
+ * Bug fix by richard@sj.co.uk
+ *
+ * REC: restore times correctly (as does tar)
+ * We only get the modification time of the file; set the creation time
+ * from the mod. time, and the access time to current time
+ */
+ finfo->mtime = finfo->ctime = strtol(hb->dbuf.mtime, NULL, 8);
+ finfo->atime = time(NULL);
+ finfo->size = unoct(hb->dbuf.size, sizeof(hb->dbuf.size));
+
+ return True;
+}
+
+/****************************************************************************
+Write out the tar buffer to tape or wherever
+****************************************************************************/
+
+static int dotarbuf(int f, char *b, int n)
+{
+ int fail=1, writ=n;
+
+ if (dry_run) {
+ return writ;
+ }
+ /* This routine and the next one should be the only ones that do write()s */
+ if (tp + n >= tbufsiz) {
+ int diff;
+
+ diff=tbufsiz-tp;
+ memcpy(tarbuf + tp, b, diff);
+ fail=fail && (1+write(f, tarbuf, tbufsiz));
+ n-=diff;
+ b+=diff;
+ tp=0;
+
+ while (n >= tbufsiz) {
+ fail=fail && (1 + write(f, b, tbufsiz));
+ n-=tbufsiz;
+ b+=tbufsiz;
+ }
+ }
+
+ if (n>0) {
+ memcpy(tarbuf+tp, b, n);
+ tp+=n;
+ }
+
+ return(fail ? writ : 0);
+}
+
+/****************************************************************************
+Write zeros to buffer / tape
+****************************************************************************/
+
+static void dozerobuf(int f, int n)
+{
+ /* short routine just to write out n zeros to buffer -
+ * used to round files to nearest block
+ * and to do tar EOFs */
+
+ if (dry_run)
+ return;
+
+ if (n+tp >= tbufsiz) {
+ memset(tarbuf+tp, 0, tbufsiz-tp);
+ write(f, tarbuf, tbufsiz);
+ memset(tarbuf, 0, (tp+=n-tbufsiz));
+ } else {
+ memset(tarbuf+tp, 0, n);
+ tp+=n;
+ }
+}
+
+/****************************************************************************
+Malloc tape buffer
+****************************************************************************/
+
+static void initarbuf(void)
+{
+ /* initialize tar buffer */
+ tbufsiz=blocksize*TBLOCK;
+ tarbuf=malloc(tbufsiz); /* FIXME: We might not get the buffer */
+
+ /* reset tar buffer pointer and tar file counter and total dumped */
+ tp=0; ntarf=0; ttarf=0;
+}
+
+/****************************************************************************
+Write two zero blocks at end of file
+****************************************************************************/
+
+static void dotareof(int f)
+{
+ SMB_STRUCT_STAT stbuf;
+ /* Two zero blocks at end of file, write out full buffer */
+
+ if (dry_run)
+ return;
+
+ (void) dozerobuf(f, TBLOCK);
+ (void) dozerobuf(f, TBLOCK);
+
+ if (sys_fstat(f, &stbuf) == -1) {
+ DEBUG(0, ("Couldn't stat file handle\n"));
+ return;
+ }
+
+ /* Could be a pipe, in which case S_ISREG should fail,
+ * and we should write out at full size */
+ if (tp > 0)
+ write(f, tarbuf, S_ISREG(stbuf.st_mode) ? tp : tbufsiz);
+}
+
+/****************************************************************************
+(Un)mangle DOS pathname, make nonabsolute
+****************************************************************************/
+
+static void fixtarname(char *tptr, const char *fp, size_t l)
+{
+ /* add a '.' to start of file name, convert from ugly dos \'s in path
+ * to lovely unix /'s :-} */
+ *tptr++='.';
+ l--;
+
+ StrnCpy(tptr, fp, l-1);
+ string_replace(tptr, '\\', '/');
+}
+
+/****************************************************************************
+Convert from decimal to octal string
+****************************************************************************/
+
+static void oct_it (SMB_BIG_UINT value, int ndgs, char *p)
+{
+ /* Converts long to octal string, pads with leading zeros */
+
+ /* skip final null, but do final space */
+ --ndgs;
+ p[--ndgs] = ' ';
+
+ /* Loop does at least one digit */
+ do {
+ p[--ndgs] = '0' + (char) (value & 7);
+ value >>= 3;
+ } while (ndgs > 0 && value != 0);
+
+ /* Do leading zeros */
+ while (ndgs > 0)
+ p[--ndgs] = '0';
+}
+
+/****************************************************************************
+Convert from octal string to long
+***************************************************************************/
+
+static long unoct(char *p, int ndgs)
+{
+ long value=0;
+ /* Converts octal string to long, ignoring any non-digit */
+
+ while (--ndgs) {
+ if (isdigit((int)*p))
+ value = (value << 3) | (long) (*p - '0');
+
+ p++;
+ }
+
+ return value;
+}
+
+/****************************************************************************
+Compare two strings in a slash insensitive way, allowing s1 to match s2
+if s1 is an "initial" string (up to directory marker). Thus, if s2 is
+a file in any subdirectory of s1, declare a match.
+***************************************************************************/
+
+static int strslashcmp(char *s1, char *s2)
+{
+ char *s1_0=s1;
+
+ while(*s1 && *s2 && (*s1 == *s2 || tolower(*s1) == tolower(*s2) ||
+ (*s1 == '\\' && *s2=='/') || (*s1 == '/' && *s2=='\\'))) {
+ s1++; s2++;
+ }
+
+ /* if s1 has a trailing slash, it compared equal, so s1 is an "initial"
+ string of s2.
+ */
+ if (!*s1 && s1 != s1_0 && (*(s1-1) == '/' || *(s1-1) == '\\'))
+ return 0;
+
+ /* ignore trailing slash on s1 */
+ if (!*s2 && (*s1 == '/' || *s1 == '\\') && !*(s1+1))
+ return 0;
+
+ /* check for s1 is an "initial" string of s2 */
+ if ((*s2 == '/' || *s2 == '\\') && !*s1)
+ return 0;
+
+ return *s1-*s2;
+}
+
+/****************************************************************************
+Ensure a remote path exists (make if necessary)
+***************************************************************************/
+
+static BOOL ensurepath(char *fname)
+{
+ /* *must* be called with buffer ready malloc'ed */
+ /* ensures path exists */
+
+ char *partpath, *ffname;
+ char *p=fname, *basehack;
+
+ DEBUG(5, ( "Ensurepath called with: %s\n", fname));
+
+ partpath = string_create_s(strlen(fname));
+ ffname = string_create_s(strlen(fname));
+
+ if ((partpath == NULL) || (ffname == NULL)){
+ DEBUG(0, ("Out of memory in ensurepath: %s\n", fname));
+ return(False);
+ }
+
+ *partpath = 0;
+
+ /* fname copied to ffname so can strtok */
+
+ safe_strcpy(ffname, fname, strlen(fname));
+
+ /* do a `basename' on ffname, so don't try and make file name directory */
+ if ((basehack=strrchr_m(ffname, '\\')) == NULL)
+ return True;
+ else
+ *basehack='\0';
+
+ p=strtok(ffname, "\\");
+
+ while (p) {
+ safe_strcat(partpath, p, strlen(fname) + 1);
+
+ if (!cli_chkpath(cli, partpath)) {
+ if (!cli_mkdir(cli, partpath)) {
+ DEBUG(0, ("Error mkdirhiering\n"));
+ return False;
+ } else {
+ DEBUG(3, ("mkdirhiering %s\n", partpath));
+ }
+ }
+
+ safe_strcat(partpath, "\\", strlen(fname) + 1);
+ p = strtok(NULL,"/\\");
+ }
+
+ return True;
+}
+
+static int padit(char *buf, int bufsize, int padsize)
+{
+ int berr= 0;
+ int bytestowrite;
+
+ DEBUG(5, ("Padding with %d zeros\n", padsize));
+ memset(buf, 0, bufsize);
+ while( !berr && padsize > 0 ) {
+ bytestowrite= MIN(bufsize, padsize);
+ berr = dotarbuf(tarhandle, buf, bytestowrite) != bytestowrite;
+ padsize -= bytestowrite;
+ }
+
+ return berr;
+}
+
+static void do_setrattr(char *name, uint16 attr, int set)
+{
+ uint16 oldattr;
+
+ if (!cli_getatr(cli, name, &oldattr, NULL, NULL)) return;
+
+ if (set == ATTRSET) {
+ attr |= oldattr;
+ } else {
+ attr = oldattr & ~attr;
+ }
+
+ if (!cli_setatr(cli, name, attr, 0)) {
+ DEBUG(1,("setatr failed: %s\n", cli_errstr(cli)));
+ }
+}
+
+/****************************************************************************
+append one remote file to the tar file
+***************************************************************************/
+
+static void do_atar(char *rname,char *lname,file_info *finfo1)
+{
+ int fnum;
+ SMB_BIG_UINT nread=0;
+ char ftype;
+ file_info2 finfo;
+ BOOL close_done = False;
+ BOOL shallitime=True;
+ char data[65520];
+ int read_size = 65520;
+ int datalen=0;
+
+ struct timeval tp_start;
+
+ GetTimeOfDay(&tp_start);
+
+ ftype = '0'; /* An ordinary file ... */
+
+ if (finfo1) {
+ finfo.size = finfo1 -> size;
+ finfo.mode = finfo1 -> mode;
+ finfo.uid = finfo1 -> uid;
+ finfo.gid = finfo1 -> gid;
+ finfo.mtime = finfo1 -> mtime;
+ finfo.atime = finfo1 -> atime;
+ finfo.ctime = finfo1 -> ctime;
+ finfo.name = finfo1 -> name;
+ } else {
+ finfo.size = def_finfo.size;
+ finfo.mode = def_finfo.mode;
+ finfo.uid = def_finfo.uid;
+ finfo.gid = def_finfo.gid;
+ finfo.mtime = def_finfo.mtime;
+ finfo.atime = def_finfo.atime;
+ finfo.ctime = def_finfo.ctime;
+ finfo.name = def_finfo.name;
+ }
+
+ if (dry_run) {
+ DEBUG(3,("skipping file %s of size %12.0f bytes\n", finfo.name,
+ (double)finfo.size));
+ shallitime=0;
+ ttarf+=finfo.size + TBLOCK - (finfo.size % TBLOCK);
+ ntarf++;
+ return;
+ }
+
+ fnum = cli_open(cli, rname, O_RDONLY, DENY_NONE);
+
+ dos_clean_name(rname);
+
+ if (fnum == -1) {
+ DEBUG(0,("%s opening remote file %s (%s)\n",
+ cli_errstr(cli),rname, cur_dir));
+ return;
+ }
+
+ finfo.name = string_create_s(strlen(rname));
+ if (finfo.name == NULL) {
+ DEBUG(0, ("Unable to allocate space for finfo.name in do_atar\n"));
+ return;
+ }
+
+ safe_strcpy(finfo.name,rname, strlen(rname));
+ if (!finfo1) {
+ if (!cli_getattrE(cli, fnum, &finfo.mode, &finfo.size, NULL, &finfo.atime, &finfo.mtime)) {
+ DEBUG(0, ("getattrE: %s\n", cli_errstr(cli)));
+ return;
+ }
+ finfo.ctime = finfo.mtime;
+ }
+
+ DEBUG(3,("file %s attrib 0x%X\n",finfo.name,finfo.mode));
+
+ if (tar_inc && !(finfo.mode & aARCH)) {
+ DEBUG(4, ("skipping %s - archive bit not set\n", finfo.name));
+ shallitime=0;
+ } else if (!tar_system && (finfo.mode & aSYSTEM)) {
+ DEBUG(4, ("skipping %s - system bit is set\n", finfo.name));
+ shallitime=0;
+ } else if (!tar_hidden && (finfo.mode & aHIDDEN)) {
+ DEBUG(4, ("skipping %s - hidden bit is set\n", finfo.name));
+ shallitime=0;
+ } else {
+ DEBUG(3,("getting file %s of size %.0f bytes as a tar file %s",
+ finfo.name, (double)finfo.size, lname));
+
+ /* write a tar header, don't bother with mode - just set to 100644 */
+ writetarheader(tarhandle, rname, finfo.size, finfo.mtime, "100644 \0", ftype);
+
+ while (nread < finfo.size && !close_done) {
+
+ DEBUG(3,("nread=%.0f\n",(double)nread));
+
+ datalen = cli_read(cli, fnum, data, nread, read_size);
+
+ if (datalen == -1) {
+ DEBUG(0,("Error reading file %s : %s\n", rname, cli_errstr(cli)));
+ break;
+ }
+
+ nread += datalen;
+
+ /* if file size has increased since we made file size query, truncate
+ read so tar header for this file will be correct.
+ */
+
+ if (nread > finfo.size) {
+ datalen -= nread - finfo.size;
+ DEBUG(0,("File size change - truncating %s to %.0f bytes\n",
+ finfo.name, (double)finfo.size));
+ }
+
+ /* add received bits of file to buffer - dotarbuf will
+ * write out in 512 byte intervals */
+
+ if (dotarbuf(tarhandle,data,datalen) != datalen) {
+ DEBUG(0,("Error writing to tar file - %s\n", strerror(errno)));
+ break;
+ }
+
+ if (datalen == 0) {
+ DEBUG(0,("Error reading file %s. Got 0 bytes\n", rname));
+ break;
+ }
+
+ datalen=0;
+ }
+
+ /* pad tar file with zero's if we couldn't get entire file */
+ if (nread < finfo.size) {
+ DEBUG(0, ("Didn't get entire file. size=%.0f, nread=%d\n",
+ (double)finfo.size, (int)nread));
+ if (padit(data, sizeof(data), finfo.size - nread))
+ DEBUG(0,("Error writing tar file - %s\n", strerror(errno)));
+ }
+
+ /* round tar file to nearest block */
+ if (finfo.size % TBLOCK)
+ dozerobuf(tarhandle, TBLOCK - (finfo.size % TBLOCK));
+
+ ttarf+=finfo.size + TBLOCK - (finfo.size % TBLOCK);
+ ntarf++;
+ }
+
+ cli_close(cli, fnum);
+
+ if (shallitime) {
+ struct timeval tp_end;
+ int this_time;
+
+ /* if shallitime is true then we didn't skip */
+ if (tar_reset && !dry_run)
+ (void) do_setrattr(finfo.name, aARCH, ATTRRESET);
+
+ GetTimeOfDay(&tp_end);
+ this_time = (tp_end.tv_sec - tp_start.tv_sec)*1000 + (tp_end.tv_usec - tp_start.tv_usec)/1000;
+ get_total_time_ms += this_time;
+ get_total_size += finfo.size;
+
+ if (tar_noisy) {
+ DEBUG(0, ("%12.0f (%7.1f kb/s) %s\n",
+ (double)finfo.size, finfo.size / MAX(0.001, (1.024*this_time)),
+ finfo.name));
+ }
+
+ /* Thanks to Carel-Jan Engel (ease@mail.wirehub.nl) for this one */
+ DEBUG(3,("(%g kb/s) (average %g kb/s)\n",
+ finfo.size / MAX(0.001, (1.024*this_time)),
+ get_total_size / MAX(0.001, (1.024*get_total_time_ms))));
+ }
+}
+
+/****************************************************************************
+Append single file to tar file (or not)
+***************************************************************************/
+
+static void do_tar(file_info *finfo)
+{
+ pstring rname;
+
+ if (strequal(finfo->name,"..") || strequal(finfo->name,"."))
+ return;
+
+ /* Is it on the exclude list ? */
+ if (!tar_excl && clipn) {
+ pstring exclaim;
+
+ DEBUG(5, ("Excl: strlen(cur_dir) = %d\n", (int)strlen(cur_dir)));
+
+ pstrcpy(exclaim, cur_dir);
+ *(exclaim+strlen(exclaim)-1)='\0';
+
+ pstrcat(exclaim, "\\");
+ pstrcat(exclaim, finfo->name);
+
+ DEBUG(5, ("...tar_re_search: %d\n", tar_re_search));
+
+ if ((!tar_re_search && clipfind(cliplist, clipn, exclaim)) ||
+#ifdef HAVE_REGEX_H
+ (tar_re_search && !regexec(preg, exclaim, 0, NULL, 0))) {
+#else
+ (tar_re_search && mask_match_list(exclaim, cliplist, clipn, True))) {
+#endif
+ DEBUG(3,("Skipping file %s\n", exclaim));
+ return;
+ }
+ }
+
+ if (finfo->mode & aDIR) {
+ pstring saved_curdir;
+ pstring mtar_mask;
+
+ pstrcpy(saved_curdir, cur_dir);
+
+ DEBUG(5, ("Sizeof(cur_dir)=%d, strlen(cur_dir)=%d, \
+strlen(finfo->name)=%d\nname=%s,cur_dir=%s\n",
+ (int)sizeof(cur_dir), (int)strlen(cur_dir),
+ (int)strlen(finfo->name), finfo->name, cur_dir));
+
+ pstrcat(cur_dir,finfo->name);
+ pstrcat(cur_dir,"\\");
+
+ DEBUG(5, ("Writing a dir, Name = %s\n", cur_dir));
+
+ /* write a tar directory, don't bother with mode - just set it to
+ * 40755 */
+ writetarheader(tarhandle, cur_dir, 0, finfo->mtime, "040755 \0", '5');
+ if (tar_noisy) {
+ DEBUG(0,(" directory %s\n", cur_dir));
+ }
+ ntarf++; /* Make sure we have a file on there */
+ pstrcpy(mtar_mask,cur_dir);
+ pstrcat(mtar_mask,"*");
+ DEBUG(5, ("Doing list with mtar_mask: %s\n", mtar_mask));
+ do_list(mtar_mask, attribute, do_tar, False, True);
+ pstrcpy(cur_dir,saved_curdir);
+ } else {
+ pstrcpy(rname,cur_dir);
+ pstrcat(rname,finfo->name);
+ do_atar(rname,finfo->name,finfo);
+ }
+}
+
+/****************************************************************************
+Convert from UNIX to DOS file names
+***************************************************************************/
+
+static void unfixtarname(char *tptr, char *fp, int l, BOOL first)
+{
+ /* remove '.' from start of file name, convert from unix /'s to
+ * dos \'s in path. Kill any absolute path names. But only if first!
+ */
+
+ DEBUG(5, ("firstb=%lX, secondb=%lX, len=%i\n", (long)tptr, (long)fp, l));
+
+ if (first) {
+ if (*fp == '.') {
+ fp++;
+ l--;
+ }
+ if (*fp == '\\' || *fp == '/') {
+ fp++;
+ l--;
+ }
+ }
+
+ safe_strcpy(tptr, fp, l);
+ string_replace(tptr, '/', '\\');
+}
+
+/****************************************************************************
+Move to the next block in the buffer, which may mean read in another set of
+blocks. FIXME, we should allow more than one block to be skipped.
+****************************************************************************/
+
+static int next_block(char *ltarbuf, char **bufferp, int bufsiz)
+{
+ int bufread, total = 0;
+
+ DEBUG(5, ("Advancing to next block: %0lx\n", (unsigned long)*bufferp));
+ *bufferp += TBLOCK;
+ total = TBLOCK;
+
+ if (*bufferp >= (ltarbuf + bufsiz)) {
+
+ DEBUG(5, ("Reading more data into ltarbuf ...\n"));
+
+ /*
+ * Bugfix from Bob Boehmer <boehmer@worldnet.att.net>
+ * Fixes bug where read can return short if coming from
+ * a pipe.
+ */
+
+ bufread = read(tarhandle, ltarbuf, bufsiz);
+ total = bufread;
+
+ while (total < bufsiz) {
+ if (bufread < 0) { /* An error, return false */
+ return (total > 0 ? -2 : bufread);
+ }
+ if (bufread == 0) {
+ if (total <= 0) {
+ return -2;
+ }
+ break;
+ }
+ bufread = read(tarhandle, &ltarbuf[total], bufsiz - total);
+ total += bufread;
+ }
+
+ DEBUG(5, ("Total bytes read ... %i\n", total));
+
+ *bufferp = ltarbuf;
+ }
+
+ return(total);
+}
+
+/* Skip a file, even if it includes a long file name? */
+static int skip_file(int skipsize)
+{
+ int dsize = skipsize;
+
+ DEBUG(5, ("Skiping file. Size = %i\n", skipsize));
+
+ /* FIXME, we should skip more than one block at a time */
+
+ while (dsize > 0) {
+ if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) {
+ DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
+ return(False);
+ }
+ dsize -= TBLOCK;
+ }
+
+ return(True);
+}
+
+/*************************************************************
+ Get a file from the tar file and store it.
+ When this is called, tarbuf already contains the first
+ file block. This is a bit broken & needs fixing.
+**************************************************************/
+
+static int get_file(file_info2 finfo)
+{
+ int fnum = -1, pos = 0, dsize = 0, bpos = 0;
+ SMB_BIG_UINT rsize = 0;
+
+ DEBUG(5, ("get_file: file: %s, size %.0f\n", finfo.name, (double)finfo.size));
+
+ if (ensurepath(finfo.name) &&
+ (fnum=cli_open(cli, finfo.name, O_RDWR|O_CREAT|O_TRUNC, DENY_NONE)) == -1) {
+ DEBUG(0, ("abandoning restore\n"));
+ return(False);
+ }
+
+ /* read the blocks from the tar file and write to the remote file */
+
+ rsize = finfo.size; /* This is how much to write */
+
+ while (rsize > 0) {
+
+ /* We can only write up to the end of the buffer */
+ dsize = MIN(tbufsiz - (buffer_p - tarbuf) - bpos, 65520); /* Calculate the size to write */
+ dsize = MIN(dsize, rsize); /* Should be only what is left */
+ DEBUG(5, ("writing %i bytes, bpos = %i ...\n", dsize, bpos));
+
+ if (cli_write(cli, fnum, 0, buffer_p + bpos, pos, dsize) != dsize) {
+ DEBUG(0, ("Error writing remote file\n"));
+ return 0;
+ }
+
+ rsize -= dsize;
+ pos += dsize;
+
+ /* Now figure out how much to move in the buffer */
+
+ /* FIXME, we should skip more than one block at a time */
+
+ /* First, skip any initial part of the part written that is left over */
+ /* from the end of the first TBLOCK */
+
+ if ((bpos) && ((bpos + dsize) >= TBLOCK)) {
+ dsize -= (TBLOCK - bpos); /* Get rid of the end of the first block */
+ bpos = 0;
+
+ if (next_block(tarbuf, &buffer_p, tbufsiz) <=0) { /* and skip the block */
+ DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
+ return False;
+ }
+ }
+
+ /*
+ * Bugfix from Bob Boehmer <boehmer@worldnet.att.net>.
+ * If the file being extracted is an exact multiple of
+ * TBLOCK bytes then we don't want to extract the next
+ * block from the tarfile here, as it will be done in
+ * the caller of get_file().
+ */
+
+ while (((rsize != 0) && (dsize >= TBLOCK)) ||
+ ((rsize == 0) && (dsize > TBLOCK))) {
+
+ if (next_block(tarbuf, &buffer_p, tbufsiz) <=0) {
+ DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
+ return False;
+ }
+
+ dsize -= TBLOCK;
+ }
+ bpos = dsize;
+ }
+
+ /* Now close the file ... */
+
+ if (!cli_close(cli, fnum)) {
+ DEBUG(0, ("Error closing remote file\n"));
+ return(False);
+ }
+
+ /* Now we update the creation date ... */
+ DEBUG(5, ("Updating creation date on %s\n", finfo.name));
+
+ if (!cli_setatr(cli, finfo.name, finfo.mode, finfo.mtime)) {
+ if (tar_real_noisy) {
+ DEBUG(0, ("Could not set time on file: %s\n", finfo.name));
+ /*return(False); */ /* Ignore, as Win95 does not allow changes */
+ }
+ }
+
+ ntarf++;
+ DEBUG(0, ("restore tar file %s of size %.0f bytes\n", finfo.name, (double)finfo.size));
+ return(True);
+}
+
+/* Create a directory. We just ensure that the path exists and return as there
+ is no file associated with a directory
+*/
+static int get_dir(file_info2 finfo)
+{
+ DEBUG(0, ("restore directory %s\n", finfo.name));
+
+ if (!ensurepath(finfo.name)) {
+ DEBUG(0, ("Problems creating directory\n"));
+ return(False);
+ }
+ ntarf++;
+ return(True);
+}
+
+/* Get a file with a long file name ... first file has file name, next file
+ has the data. We only want the long file name, as the loop in do_tarput
+ will deal with the rest.
+*/
+static char *get_longfilename(file_info2 finfo)
+{
+ /* finfo.size here is the length of the filename as written by the "/./@LongLink" name
+ * header call. */
+ int namesize = finfo.size + strlen(cur_dir) + 2;
+ char *longname = malloc(namesize);
+ int offset = 0, left = finfo.size;
+ BOOL first = True;
+
+ DEBUG(5, ("Restoring a long file name: %s\n", finfo.name));
+ DEBUG(5, ("Len = %.0f\n", (double)finfo.size));
+
+ if (longname == NULL) {
+ DEBUG(0, ("could not allocate buffer of size %d for longname\n", namesize));
+ return(NULL);
+ }
+
+ /* First, add cur_dir to the long file name */
+
+ if (strlen(cur_dir) > 0) {
+ strncpy(longname, cur_dir, namesize);
+ offset = strlen(cur_dir);
+ }
+
+ /* Loop through the blocks picking up the name */
+
+ while (left > 0) {
+ if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) {
+ DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
+ return(NULL);
+ }
+
+ unfixtarname(longname + offset, buffer_p, MIN(TBLOCK, finfo.size), first--);
+ DEBUG(5, ("UnfixedName: %s, buffer: %s\n", longname, buffer_p));
+
+ offset += TBLOCK;
+ left -= TBLOCK;
+ }
+
+ return(longname);
+}
+
+static void do_tarput(void)
+{
+ file_info2 finfo;
+ struct timeval tp_start;
+ char *longfilename = NULL, linkflag;
+ int skip = False;
+
+ GetTimeOfDay(&tp_start);
+ DEBUG(5, ("RJS do_tarput called ...\n"));
+
+ buffer_p = tarbuf + tbufsiz; /* init this to force first read */
+
+ /* Now read through those files ... */
+ while (True) {
+ /* Get us to the next block, or the first block first time around */
+ if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) {
+ DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
+ return;
+ }
+
+ DEBUG(5, ("Reading the next header ...\n"));
+
+ switch (readtarheader((union hblock *) buffer_p, &finfo, cur_dir)) {
+ case -2: /* Hmm, not good, but not fatal */
+ DEBUG(0, ("Skipping %s...\n", finfo.name));
+ if ((next_block(tarbuf, &buffer_p, tbufsiz) <= 0) && !skip_file(finfo.size)) {
+ DEBUG(0, ("Short file, bailing out...\n"));
+ return;
+ }
+ break;
+
+ case -1:
+ DEBUG(0, ("abandoning restore, -1 from read tar header\n"));
+ return;
+
+ case 0: /* chksum is zero - looks like an EOF */
+ DEBUG(0, ("tar: restored %d files and directories\n", ntarf));
+ return; /* Hmmm, bad here ... */
+
+ default:
+ /* No action */
+ break;
+ }
+
+ /* Now, do we have a long file name? */
+ if (longfilename != NULL) {
+ SAFE_FREE(finfo.name); /* Free the space already allocated */
+ finfo.name = longfilename;
+ longfilename = NULL;
+ }
+
+ /* Well, now we have a header, process the file ... */
+ /* Should we skip the file? We have the long name as well here */
+ skip = clipn && ((!tar_re_search && clipfind(cliplist, clipn, finfo.name) ^ tar_excl) ||
+#ifdef HAVE_REGEX_H
+ (tar_re_search && !regexec(preg, finfo.name, 0, NULL, 0)));
+#else
+ (tar_re_search && mask_match_list(finfo.name, cliplist, clipn, True)));
+#endif
+
+ DEBUG(5, ("Skip = %i, cliplist=%s, file=%s\n", skip, (cliplist?cliplist[0]:NULL), finfo.name));
+ if (skip) {
+ skip_file(finfo.size);
+ continue;
+ }
+
+ /* We only get this far if we should process the file */
+ linkflag = ((union hblock *)buffer_p) -> dbuf.linkflag;
+ switch (linkflag) {
+ case '0': /* Should use symbolic names--FIXME */
+ /*
+ * Skip to the next block first, so we can get the file, FIXME, should
+ * be in get_file ...
+ * The 'finfo.size != 0' fix is from Bob Boehmer <boehmer@worldnet.att.net>
+ * Fixes bug where file size in tarfile is zero.
+ */
+ if ((finfo.size != 0) && next_block(tarbuf, &buffer_p, tbufsiz) <=0) {
+ DEBUG(0, ("Short file, bailing out...\n"));
+ return;
+ }
+ if (!get_file(finfo)) {
+ DEBUG(0, ("Abandoning restore\n"));
+ return;
+ }
+ break;
+ case '5':
+ if (!get_dir(finfo)) {
+ DEBUG(0, ("Abandoning restore \n"));
+ return;
+ }
+ break;
+ case 'L':
+ longfilename = get_longfilename(finfo);
+ if (!longfilename) {
+ DEBUG(0, ("abandoning restore\n"));
+ return;
+ }
+ DEBUG(5, ("Long file name: %s\n", longfilename));
+ break;
+
+ default:
+ skip_file(finfo.size); /* Don't handle these yet */
+ break;
+ }
+ }
+}
+
+/*
+ * samba interactive commands
+ */
+
+/****************************************************************************
+Blocksize command
+***************************************************************************/
+
+int cmd_block(void)
+{
+ fstring buf;
+ int block;
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ DEBUG(0, ("blocksize <n>\n"));
+ return 1;
+ }
+
+ block=atoi(buf);
+ if (block < 0 || block > 65535) {
+ DEBUG(0, ("blocksize out of range"));
+ return 1;
+ }
+
+ blocksize=block;
+ DEBUG(2,("blocksize is now %d\n", blocksize));
+
+ return 0;
+}
+
+/****************************************************************************
+command to set incremental / reset mode
+***************************************************************************/
+
+int cmd_tarmode(void)
+{
+ fstring buf;
+
+ while (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ if (strequal(buf, "full"))
+ tar_inc=False;
+ else if (strequal(buf, "inc"))
+ tar_inc=True;
+ else if (strequal(buf, "reset"))
+ tar_reset=True;
+ else if (strequal(buf, "noreset"))
+ tar_reset=False;
+ else if (strequal(buf, "system"))
+ tar_system=True;
+ else if (strequal(buf, "nosystem"))
+ tar_system=False;
+ else if (strequal(buf, "hidden"))
+ tar_hidden=True;
+ else if (strequal(buf, "nohidden"))
+ tar_hidden=False;
+ else if (strequal(buf, "verbose") || strequal(buf, "noquiet"))
+ tar_noisy=True;
+ else if (strequal(buf, "quiet") || strequal(buf, "noverbose"))
+ tar_noisy=False;
+ else
+ DEBUG(0, ("tarmode: unrecognised option %s\n", buf));
+ }
+
+ DEBUG(0, ("tarmode is now %s, %s, %s, %s, %s\n",
+ tar_inc ? "incremental" : "full",
+ tar_system ? "system" : "nosystem",
+ tar_hidden ? "hidden" : "nohidden",
+ tar_reset ? "reset" : "noreset",
+ tar_noisy ? "verbose" : "quiet"));
+ return 0;
+}
+
+/****************************************************************************
+Feeble attrib command
+***************************************************************************/
+
+int cmd_setmode(void)
+{
+ char *q;
+ fstring buf;
+ pstring fname;
+ uint16 attra[2];
+ int direct=1;
+
+ attra[0] = attra[1] = 0;
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ DEBUG(0, ("setmode <filename> <[+|-]rsha>\n"));
+ return 1;
+ }
+
+ pstrcpy(fname, cur_dir);
+ pstrcat(fname, buf);
+
+ while (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ q=buf;
+
+ while(*q) {
+ switch (*q++) {
+ case '+':
+ direct=1;
+ break;
+ case '-':
+ direct=0;
+ break;
+ case 'r':
+ attra[direct]|=aRONLY;
+ break;
+ case 'h':
+ attra[direct]|=aHIDDEN;
+ break;
+ case 's':
+ attra[direct]|=aSYSTEM;
+ break;
+ case 'a':
+ attra[direct]|=aARCH;
+ break;
+ default:
+ DEBUG(0, ("setmode <filename> <perm=[+|-]rsha>\n"));
+ return 1;
+ }
+ }
+ }
+
+ if (attra[ATTRSET]==0 && attra[ATTRRESET]==0) {
+ DEBUG(0, ("setmode <filename> <[+|-]rsha>\n"));
+ return 1;
+ }
+
+ DEBUG(2, ("\nperm set %d %d\n", attra[ATTRSET], attra[ATTRRESET]));
+ do_setrattr(fname, attra[ATTRSET], ATTRSET);
+ do_setrattr(fname, attra[ATTRRESET], ATTRRESET);
+ return 0;
+}
+
+/****************************************************************************
+Principal command for creating / extracting
+***************************************************************************/
+
+int cmd_tar(void)
+{
+ fstring buf;
+ char **argl;
+ int argcl;
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ DEBUG(0,("tar <c|x>[IXbgan] <filename>\n"));
+ return 1;
+ }
+
+ argl=toktocliplist(&argcl, NULL);
+ if (!tar_parseargs(argcl, argl, buf, 0))
+ return 1;
+
+ process_tar();
+ SAFE_FREE(argl);
+ return 0;
+}
+
+/****************************************************************************
+Command line (option) version
+***************************************************************************/
+
+int process_tar(void)
+{
+ initarbuf();
+ switch(tar_type) {
+ case 'x':
+
+#if 0
+ do_tarput2();
+#else
+ do_tarput();
+#endif
+ SAFE_FREE(tarbuf);
+ close(tarhandle);
+ break;
+ case 'r':
+ case 'c':
+ if (clipn && tar_excl) {
+ int i;
+ pstring tarmac;
+
+ for (i=0; i<clipn; i++) {
+ DEBUG(5,("arg %d = %s\n", i, cliplist[i]));
+
+ if (*(cliplist[i]+strlen(cliplist[i])-1)=='\\') {
+ *(cliplist[i]+strlen(cliplist[i])-1)='\0';
+ }
+
+ if (strrchr_m(cliplist[i], '\\')) {
+ pstring saved_dir;
+
+ pstrcpy(saved_dir, cur_dir);
+
+ if (*cliplist[i]=='\\') {
+ pstrcpy(tarmac, cliplist[i]);
+ } else {
+ pstrcpy(tarmac, cur_dir);
+ pstrcat(tarmac, cliplist[i]);
+ }
+ pstrcpy(cur_dir, tarmac);
+ *(strrchr_m(cur_dir, '\\')+1)='\0';
+
+ DEBUG(5, ("process_tar, do_list with tarmac: %s\n", tarmac));
+ do_list(tarmac,attribute,do_tar, False, True);
+ pstrcpy(cur_dir,saved_dir);
+ } else {
+ pstrcpy(tarmac, cur_dir);
+ pstrcat(tarmac, cliplist[i]);
+ DEBUG(5, ("process_tar, do_list with tarmac: %s\n", tarmac));
+ do_list(tarmac,attribute,do_tar, False, True);
+ }
+ }
+ } else {
+ pstring mask;
+ pstrcpy(mask,cur_dir);
+ DEBUG(5, ("process_tar, do_list with mask: %s\n", mask));
+ pstrcat(mask,"\\*");
+ do_list(mask,attribute,do_tar,False, True);
+ }
+
+ if (ntarf)
+ dotareof(tarhandle);
+ close(tarhandle);
+ SAFE_FREE(tarbuf);
+
+ DEBUG(0, ("tar: dumped %d files and directories\n", ntarf));
+ DEBUG(0, ("Total bytes written: %.0f\n", (double)ttarf));
+ break;
+ }
+
+ if (must_free_cliplist) {
+ int i;
+ for (i = 0; i < clipn; ++i) {
+ SAFE_FREE(cliplist[i]);
+ }
+ SAFE_FREE(cliplist);
+ cliplist = NULL;
+ clipn = 0;
+ must_free_cliplist = False;
+ }
+ return(0);
+}
+
+/****************************************************************************
+Find a token (filename) in a clip list
+***************************************************************************/
+
+static int clipfind(char **aret, int ret, char *tok)
+{
+ if (aret==NULL)
+ return 0;
+
+ /* ignore leading slashes or dots in token */
+ while(strchr_m("/\\.", *tok))
+ tok++;
+
+ while(ret--) {
+ char *pkey=*aret++;
+
+ /* ignore leading slashes or dots in list */
+ while(strchr_m("/\\.", *pkey))
+ pkey++;
+
+ if (!strslashcmp(pkey, tok))
+ return 1;
+ }
+ return 0;
+}
+
+/****************************************************************************
+Read list of files to include from the file and initialize cliplist
+accordingly.
+***************************************************************************/
+
+static int read_inclusion_file(char *filename)
+{
+ XFILE *inclusion = NULL;
+ char buf[PATH_MAX + 1];
+ char *inclusion_buffer = NULL;
+ int inclusion_buffer_size = 0;
+ int inclusion_buffer_sofar = 0;
+ char *p;
+ char *tmpstr;
+ int i;
+ int error = 0;
+
+ clipn = 0;
+ buf[PATH_MAX] = '\0'; /* guarantee null-termination */
+ if ((inclusion = x_fopen(filename, O_RDONLY, 0)) == NULL) {
+ /* XXX It would be better to include a reason for failure, but without
+ * autoconf, it's hard to use strerror, sys_errlist, etc.
+ */
+ DEBUG(0,("Unable to open inclusion file %s\n", filename));
+ return 0;
+ }
+
+ while ((! error) && (x_fgets(buf, sizeof(buf)-1, inclusion))) {
+ if (inclusion_buffer == NULL) {
+ inclusion_buffer_size = 1024;
+ if ((inclusion_buffer = malloc(inclusion_buffer_size)) == NULL) {
+ DEBUG(0,("failure allocating buffer to read inclusion file\n"));
+ error = 1;
+ break;
+ }
+ }
+
+ if (buf[strlen(buf)-1] == '\n') {
+ buf[strlen(buf)-1] = '\0';
+ }
+
+ if ((strlen(buf) + 1 + inclusion_buffer_sofar) >= inclusion_buffer_size) {
+ char *ib;
+ inclusion_buffer_size *= 2;
+ ib = Realloc(inclusion_buffer,inclusion_buffer_size);
+ if (! ib) {
+ DEBUG(0,("failure enlarging inclusion buffer to %d bytes\n",
+ inclusion_buffer_size));
+ error = 1;
+ break;
+ } else {
+ inclusion_buffer = ib;
+ }
+ }
+
+ safe_strcpy(inclusion_buffer + inclusion_buffer_sofar, buf, inclusion_buffer_size - inclusion_buffer_sofar);
+ inclusion_buffer_sofar += strlen(buf) + 1;
+ clipn++;
+ }
+ x_fclose(inclusion);
+
+ if (! error) {
+ /* Allocate an array of clipn + 1 char*'s for cliplist */
+ cliplist = malloc((clipn + 1) * sizeof(char *));
+ if (cliplist == NULL) {
+ DEBUG(0,("failure allocating memory for cliplist\n"));
+ error = 1;
+ } else {
+ cliplist[clipn] = NULL;
+ p = inclusion_buffer;
+ for (i = 0; (! error) && (i < clipn); i++) {
+ /* set current item to NULL so array will be null-terminated even if
+ * malloc fails below. */
+ cliplist[i] = NULL;
+ if ((tmpstr = (char *)malloc(strlen(p)+1)) == NULL) {
+ DEBUG(0, ("Could not allocate space for a cliplist item, # %i\n", i));
+ error = 1;
+ } else {
+ unfixtarname(tmpstr, p, strlen(p) + 1, True);
+ cliplist[i] = tmpstr;
+ if ((p = strchr_m(p, '\000')) == NULL) {
+ DEBUG(0,("INTERNAL ERROR: inclusion_buffer is of unexpected contents.\n"));
+ abort();
+ }
+ }
+ ++p;
+ }
+ must_free_cliplist = True;
+ }
+ }
+
+ SAFE_FREE(inclusion_buffer);
+ if (error) {
+ if (cliplist) {
+ char **pp;
+ /* We know cliplist is always null-terminated */
+ for (pp = cliplist; *pp; ++pp) {
+ SAFE_FREE(*pp);
+ }
+ SAFE_FREE(cliplist);
+ cliplist = NULL;
+ must_free_cliplist = False;
+ }
+ return 0;
+ }
+
+ /* cliplist and its elements are freed at the end of process_tar. */
+ return 1;
+}
+
+/****************************************************************************
+Parse tar arguments. Sets tar_type, tar_excl, etc.
+***************************************************************************/
+
+int tar_parseargs(int argc, char *argv[], const char *Optarg, int Optind)
+{
+ int newOptind = Optind;
+ char tar_clipfl='\0';
+
+ /* Reset back to defaults - could be from interactive version
+ * reset mode and archive mode left as they are though
+ */
+ tar_type='\0';
+ tar_excl=True;
+ dry_run=False;
+
+ while (*Optarg) {
+ switch(*Optarg++) {
+ case 'c':
+ tar_type='c';
+ break;
+ case 'x':
+ if (tar_type=='c') {
+ printf("Tar must be followed by only one of c or x.\n");
+ return 0;
+ }
+ tar_type='x';
+ break;
+ case 'b':
+ if (Optind>=argc || !(blocksize=atoi(argv[Optind]))) {
+ DEBUG(0,("Option b must be followed by valid blocksize\n"));
+ return 0;
+ } else {
+ Optind++;
+ newOptind++;
+ }
+ break;
+ case 'g':
+ tar_inc=True;
+ break;
+ case 'N':
+ if (Optind>=argc) {
+ DEBUG(0,("Option N must be followed by valid file name\n"));
+ return 0;
+ } else {
+ SMB_STRUCT_STAT stbuf;
+ extern time_t newer_than;
+
+ if (sys_stat(argv[Optind], &stbuf) == 0) {
+ newer_than = stbuf.st_mtime;
+ DEBUG(1,("Getting files newer than %s",
+ asctime(LocalTime(&newer_than))));
+ newOptind++;
+ Optind++;
+ } else {
+ DEBUG(0,("Error setting newer-than time\n"));
+ return 0;
+ }
+ }
+ break;
+ case 'a':
+ tar_reset=True;
+ break;
+ case 'q':
+ tar_noisy=False;
+ break;
+ case 'I':
+ if (tar_clipfl) {
+ DEBUG(0,("Only one of I,X,F must be specified\n"));
+ return 0;
+ }
+ tar_clipfl='I';
+ break;
+ case 'X':
+ if (tar_clipfl) {
+ DEBUG(0,("Only one of I,X,F must be specified\n"));
+ return 0;
+ }
+ tar_clipfl='X';
+ break;
+ case 'F':
+ if (tar_clipfl) {
+ DEBUG(0,("Only one of I,X,F must be specified\n"));
+ return 0;
+ }
+ tar_clipfl='F';
+ break;
+ case 'r':
+ DEBUG(0, ("tar_re_search set\n"));
+ tar_re_search = True;
+ break;
+ case 'n':
+ if (tar_type == 'c') {
+ DEBUG(0, ("dry_run set\n"));
+ dry_run = True;
+ } else {
+ DEBUG(0, ("n is only meaningful when creating a tar-file\n"));
+ return 0;
+ }
+ break;
+ default:
+ DEBUG(0,("Unknown tar option\n"));
+ return 0;
+ }
+ }
+
+ if (!tar_type) {
+ printf("Option T must be followed by one of c or x.\n");
+ return 0;
+ }
+
+ /* tar_excl is true if cliplist lists files to be included.
+ * Both 'I' and 'F' mean include. */
+ tar_excl=tar_clipfl!='X';
+
+ if (tar_clipfl=='F') {
+ if (argc-Optind-1 != 1) {
+ DEBUG(0,("Option F must be followed by exactly one filename.\n"));
+ return 0;
+ }
+ newOptind++;
+ Optind++;
+ if (! read_inclusion_file(argv[Optind])) {
+ return 0;
+ }
+ } else if (Optind+1<argc && !tar_re_search) { /* For backwards compatibility */
+ char *tmpstr;
+ char **tmplist;
+ int clipcount;
+
+ cliplist=argv+Optind+1;
+ clipn=argc-Optind-1;
+ clipcount = clipn;
+
+ if ((tmplist=malloc(clipn*sizeof(char *))) == NULL) {
+ DEBUG(0, ("Could not allocate space to process cliplist, count = %i\n", clipn));
+ return 0;
+ }
+
+ for (clipcount = 0; clipcount < clipn; clipcount++) {
+
+ DEBUG(5, ("Processing an item, %s\n", cliplist[clipcount]));
+
+ if ((tmpstr = (char *)malloc(strlen(cliplist[clipcount])+1)) == NULL) {
+ DEBUG(0, ("Could not allocate space for a cliplist item, # %i\n", clipcount));
+ return 0;
+ }
+
+ unfixtarname(tmpstr, cliplist[clipcount], strlen(cliplist[clipcount]) + 1, True);
+ tmplist[clipcount] = tmpstr;
+ DEBUG(5, ("Processed an item, %s\n", tmpstr));
+
+ DEBUG(5, ("Cliplist is: %s\n", cliplist[0]));
+ }
+
+ cliplist = tmplist;
+ must_free_cliplist = True;
+
+ newOptind += clipn;
+ }
+
+ if (Optind+1<argc && tar_re_search) { /* Doing regular expression seaches */
+#ifdef HAVE_REGEX_H
+ int errcode;
+
+ if ((preg = (regex_t *)malloc(65536)) == NULL) {
+
+ DEBUG(0, ("Could not allocate buffer for regular expression search\n"));
+ return;
+ }
+
+ if (errcode = regcomp(preg, argv[Optind + 1], REG_EXTENDED)) {
+ char errstr[1024];
+ size_t errlen;
+
+ errlen = regerror(errcode, preg, errstr, sizeof(errstr) - 1);
+ DEBUG(0, ("Could not compile pattern buffer for re search: %s\n%s\n", argv[Optind + 1], errstr));
+ return;
+ }
+#endif
+
+ clipn=argc-Optind-1;
+ cliplist=argv+Optind+1;
+ newOptind += clipn;
+ }
+
+ if (Optind>=argc || !strcmp(argv[Optind], "-")) {
+ /* Sets tar handle to either 0 or 1, as appropriate */
+ tarhandle=(tar_type=='c');
+ /*
+ * Make sure that dbf points to stderr if we are using stdout for
+ * tar output
+ */
+ if (tarhandle == 1) {
+ dbf = x_stderr;
+ }
+ if (!argv[Optind]) {
+ DEBUG(0,("Must specify tar filename\n"));
+ return 0;
+ }
+ if (!strcmp(argv[Optind], "-")) {
+ newOptind++;
+ }
+
+ } else {
+ if (tar_type=='c' && (dry_run || strcmp(argv[Optind], "/dev/null")==0)) {
+ if (!dry_run) {
+ DEBUG(0,("Output is /dev/null, assuming dry_run\n"));
+ dry_run = True;
+ }
+ tarhandle=-1;
+ } else if ((tar_type=='x' && (tarhandle = sys_open(argv[Optind], O_RDONLY, 0)) == -1)
+ || (tar_type=='c' && (tarhandle=sys_creat(argv[Optind], 0644)) < 0)) {
+ DEBUG(0,("Error opening local file %s - %s\n", argv[Optind], strerror(errno)));
+ return(0);
+ }
+ newOptind++;
+ }
+
+ return newOptind;
+}
diff --git a/source/client/mount.cifs.c b/source/client/mount.cifs.c
new file mode 100755
index 00000000000..8c23cc22123
--- /dev/null
+++ b/source/client/mount.cifs.c
@@ -0,0 +1,825 @@
+/*
+ Mount helper utility for Linux CIFS VFS (virtual filesystem) client
+ Copyright (C) 2003 Steve French (sfrench@us.ibm.com)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <getopt.h>
+#include <errno.h>
+#include <netdb.h>
+#include <string.h>
+#include <mntent.h>
+#include <fcntl.h>
+
+#define MOUNT_CIFS_VERSION_MAJOR "1"
+#define MOUNT_CIFS_VERSION_MINOR "0"
+
+#ifndef MOUNT_CIFS_VENDOR_SUFFIX
+#define MOUNT_CIFS_VENDOR_SUFFIX ""
+#endif
+
+char * thisprogram;
+int verboseflag = 0;
+static int got_password = 0;
+static int got_user = 0;
+static int got_domain = 0;
+static int got_ip = 0;
+static int got_unc = 0;
+static int got_uid = 0;
+static int got_gid = 0;
+static char * user_name = NULL;
+char * mountpassword = NULL;
+
+
+/* BB finish BB
+
+ cifs_umount
+ open nofollow - avoid symlink exposure?
+ get owner of dir see if matches self or if root
+ call system(umount argv) etc.
+
+BB end finish BB */
+
+static void mount_cifs_usage(void)
+{
+ printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
+ printf("\nMount the remote target, specified as a UNC name,");
+ printf(" to a local directory.\n");
+ if(mountpassword) {
+ memset(mountpassword,0,64);
+ free(mountpassword);
+ }
+ exit(1);
+}
+
+/* caller frees username if necessary */
+static char * getusername(void) {
+ char *username = NULL;
+ struct passwd *password = getpwuid(getuid());
+
+ if (password) {
+ username = password->pw_name;
+ }
+ return username;
+}
+
+char * parse_cifs_url(char * unc_name)
+{
+ printf("\ncifs url %s\n",unc_name);
+ return NULL;
+}
+
+static int open_cred_file(char * file_name)
+{
+ char * line_buf;
+ char * temp_val;
+ FILE * fs;
+ int i, length;
+ fs = fopen(file_name,"r");
+ if(fs == NULL)
+ return errno;
+ line_buf = malloc(4096);
+ if(line_buf == NULL)
+ return -ENOMEM;
+
+ while(fgets(line_buf,4096,fs)) {
+ /* parse line from credential file */
+
+ /* eat leading white space */
+ for(i=0;i<4096;i++) {
+ if(line_buf[i] == '\0')
+ break;
+ else if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
+ break;
+ line_buf++;
+ }
+
+ if (strncasecmp("username",line_buf,8) == 0) {
+ temp_val = strchr(line_buf + i,'=');
+ if(temp_val) {
+ /* go past equals sign */
+ temp_val++;
+ length = strlen(temp_val);
+ if(length > 4086) {
+ printf("cifs.mount failed due to malformed username in credentials file");
+ memset(line_buf,0,4096);
+ if(mountpassword) {
+ memset(mountpassword,0,64);
+ }
+ exit(1);
+ } else {
+ got_user = 1;
+ user_name = calloc(1 + length,1);
+ /* BB adding free of user_name string before exit,
+ not really necessary but would be cleaner */
+ strncpy(user_name,temp_val, length);
+ }
+ }
+ } else if (strncasecmp("password",line_buf,8) == 0) {
+ temp_val = strchr(line_buf+i,'=');
+ if(temp_val) {
+ /* go past equals sign */
+ temp_val++;
+ length = strlen(temp_val);
+ if(length > 64) {
+ printf("cifs.mount failed: password in credentials file too long\n");
+ memset(line_buf,0, 4096);
+ if(mountpassword) {
+ memset(mountpassword,0,64);
+ }
+ exit(1);
+ } else {
+ if(mountpassword == NULL) {
+ mountpassword = calloc(65,1);
+ }
+ if(mountpassword) {
+ strncpy(mountpassword,temp_val,64);
+ got_password = 1;
+ }
+ }
+ }
+ }
+ }
+ fclose(fs);
+ if(line_buf) {
+ memset(line_buf,0,4096);
+ free(line_buf);
+ }
+ return 0;
+}
+
+static int get_password_from_file(int file_descript, char * filename)
+{
+ int rc = 0;
+ int i;
+ char c;
+
+ if(mountpassword == NULL)
+ mountpassword = calloc(65,1);
+ else
+ memset(mountpassword, 0, 64);
+
+ if(filename != NULL) {
+ file_descript = open(filename, O_RDONLY);
+ if(file_descript < 0) {
+ printf("cifs.mount failed. %s attempting to open password file %s\n",
+ strerror(errno),filename);
+ exit(1);
+ }
+ }
+ /* else file already open and fd provided */
+
+ for(i=0;i<64;i++) {
+ rc = read(file_descript,&c,1);
+ if(rc < 0) {
+ printf("cifs.mount failed. Error %s reading password file\n",strerror(errno));
+ memset(mountpassword,0,64);
+ if(filename != NULL)
+ close(file_descript);
+ exit(1);
+ } else if(rc == 0) {
+ if(mountpassword[0] == 0) {
+ if(verboseflag)
+ printf("\nWarning: null password used since cifs password file empty");
+ }
+ break;
+ } else /* read valid character */ {
+ if((c == 0) || (c == '\n')) {
+ break;
+ } else
+ mountpassword[i] = c;
+ }
+ }
+ if((i == 64) && (verboseflag)) {
+ printf("\nWarning: password longer than 64 characters specified in cifs password file");
+ }
+ got_password = 1;
+ if(filename != NULL) {
+ close(file_descript);
+ }
+
+ return rc;
+}
+
+static int parse_options(char * options)
+{
+ char * data;
+ char * percent_char = 0;
+ char * value = 0;
+ char * next_keyword = 0;
+ int rc = 0;
+
+ if (!options)
+ return 1;
+ else
+ data = options;
+
+ if(verboseflag)
+ printf("\n parsing options: %s", options);
+
+/* while ((data = strsep(&options, ",")) != NULL) { */
+ while(data != NULL) {
+ /* check if ends with trailing comma */
+ if(*data == 0)
+ break;
+
+ /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
+ /* data = next keyword */
+ /* value = next value ie stuff after equal sign */
+
+ next_keyword = strchr(data,',');
+
+ /* temporarily null terminate end of keyword=value pair */
+ if(next_keyword)
+ *next_keyword = 0;
+
+ /* if (!*data)
+ continue; */
+
+ /* temporarily null terminate keyword to make keyword and value distinct */
+ if ((value = strchr(data, '=')) != NULL) {
+ *value = '\0';
+ value++;
+ }
+
+ if (strncmp(data, "user", 4) == 0) {
+ if (!value || !*value) {
+ printf("invalid or missing username\n");
+ return 1; /* needs_arg; */
+ }
+ if (strnlen(value, 260) < 260) {
+ got_user=1;
+ percent_char = strchr(value,'%');
+ if(percent_char) {
+ *percent_char = ',';
+ if(mountpassword == NULL)
+ mountpassword = calloc(65,1);
+ if(mountpassword) {
+ if(got_password)
+ printf("\ncifs.mount warning - password specified twice\n");
+ got_password = 1;
+ percent_char++;
+ strncpy(mountpassword, percent_char,64);
+ /* remove password from username */
+ while(*percent_char != 0) {
+ *percent_char = ',';
+ percent_char++;
+ }
+ }
+ }
+ } else {
+ printf("username too long\n");
+ return 1;
+ }
+ } else if (strncmp(data, "pass", 4) == 0) {
+ if (!value || !*value) {
+ if(got_password) {
+ printf("\npassword specified twice, ignoring second\n");
+ } else
+ got_password = 1;
+ } else if (strnlen(value, 17) < 17) {
+ if(got_password)
+ printf("\ncifs.mount warning - password specified twice\n");
+ got_password = 1;
+ } else {
+ printf("password too long\n");
+ return 1;
+ }
+ } else if (strncmp(data, "ip", 2) == 0) {
+ if (!value || !*value) {
+ printf("target ip address argument missing");
+ } else if (strnlen(value, 35) < 35) {
+ got_ip = 1;
+ } else {
+ printf("ip address too long\n");
+ return 1;
+ }
+ } else if ((strncmp(data, "unc", 3) == 0)
+ || (strncmp(data, "target", 6) == 0)
+ || (strncmp(data, "path", 4) == 0)) {
+ if (!value || !*value) {
+ printf("invalid path to network resource\n");
+ return 1; /* needs_arg; */
+ } else if(strnlen(value,5) < 5) {
+ printf("UNC name too short");
+ }
+
+ if (strnlen(value, 300) < 300) {
+ got_unc = 1;
+ if (strncmp(value, "//", 2) == 0) {
+ if(got_unc)
+ printf("unc name specified twice, ignoring second\n");
+ else
+ got_unc = 1;
+ } else if (strncmp(value, "\\\\", 2) != 0) {
+ printf("UNC Path does not begin with // or \\\\ \n");
+ return 1;
+ } else {
+ if(got_unc)
+ printf("unc name specified twice, ignoring second\n");
+ else
+ got_unc = 1;
+ }
+ } else {
+ printf("CIFS: UNC name too long\n");
+ return 1;
+ }
+ } else if ((strncmp(data, "domain", 3) == 0)
+ || (strncmp(data, "workgroup", 5) == 0)) {
+ if (!value || !*value) {
+ printf("CIFS: invalid domain name\n");
+ return 1; /* needs_arg; */
+ }
+ if (strnlen(value, 65) < 65) {
+ got_domain = 1;
+ } else {
+ printf("domain name too long\n");
+ return 1;
+ }
+ } else if (strncmp(data, "cred", 4) == 0) {
+ if (value && *value) {
+ rc = open_cred_file(value);
+ if(rc) {
+ printf("error %d opening credential file %s",rc, value);
+ return 1;
+ }
+ } else {
+ printf("invalid credential file name specified\n");
+ return 1;
+ }
+ } else if (strncmp(data, "uid", 3) == 0) {
+ if (value && *value) {
+ got_uid = 1;
+ }
+ } else if (strncmp(data, "gid", 3) == 0) {
+ if (value && *value) {
+ got_gid = 1;
+ }
+ /* fmask and dmask synonyms for people used to smbfs syntax */
+ } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
+ if (!value || !*value) {
+ printf ("Option '%s' requires a numerical argument\n", data);
+ return 1;
+ }
+
+ if (value[0] != '0') {
+ printf ("WARNING: '%s' not expressed in octal.\n", data);
+ }
+
+ if (strcmp (data, "fmask") == 0) {
+ printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
+ data = "file_mode";
+ }
+ } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
+ if (!value || !*value) {
+ printf ("Option '%s' requires a numerical argument\n", data);
+ return 1;
+ }
+
+ if (value[0] != '0') {
+ printf ("WARNING: '%s' not expressed in octal.\n", data);
+ }
+
+ if (strcmp (data, "dmask") == 0) {
+ printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
+ data = "dir_mode";
+ }
+ } /* else if (strnicmp(data, "port", 4) == 0) {
+ if (value && *value) {
+ vol->port =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "rsize", 5) == 0) {
+ if (value && *value) {
+ vol->rsize =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "wsize", 5) == 0) {
+ if (value && *value) {
+ vol->wsize =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "version", 3) == 0) {
+
+ } else if (strnicmp(data, "rw", 2) == 0) {
+
+ } else
+ printf("CIFS: Unknown mount option %s\n",data); */
+
+ /* move to next option */
+ data = next_keyword+1;
+
+ /* put overwritten equals sign back */
+ if(value) {
+ value--;
+ *value = '=';
+ }
+
+ /* put previous overwritten comma back */
+ if(next_keyword)
+ *next_keyword = ',';
+ else
+ data = 0;
+
+ }
+ return 0;
+}
+
+/* Note that caller frees the returned buffer if necessary */
+char * parse_server(char * unc_name)
+{
+ int length = strnlen(unc_name,1024);
+ char * share;
+ char * ipaddress_string = NULL;
+ struct hostent * host_entry;
+ struct in_addr server_ipaddr;
+ int rc;
+
+ if(length > 1023) {
+ printf("mount error: UNC name too long");
+ return 0;
+ }
+ if (strncasecmp("cifs://",unc_name,7) == 0)
+ return parse_cifs_url(unc_name+7);
+ if (strncasecmp("smb://",unc_name,6) == 0) {
+ return parse_cifs_url(unc_name+6);
+ }
+
+ if(length < 3) {
+ /* BB add code to find DFS root here */
+ printf("\nMounting the DFS root for domain not implemented yet");
+ return 0;
+ } else {
+ if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
+ printf("mount error: improperly formatted UNC name.");
+ printf(" %s does not begin with \\\\ or //\n",unc_name);
+ return 0;
+ } else {
+ unc_name[0] = '\\';
+ unc_name[0] = '/';
+ unc_name[1] = '/';
+ unc_name += 2;
+ if ((share = strchr(unc_name, '/')) ||
+ (share = strchr(unc_name,'\\'))) {
+ *share = 0; /* temporarily terminate the string */
+ share += 1;
+ host_entry = gethostbyname(unc_name);
+ *(share - 1) = '/'; /* put the slash back */
+/* rc = getipnodebyname(unc_name, AF_INET, AT_ADDRCONFIG ,&rc);*/
+ if(host_entry == NULL) {
+ printf("mount error: could not find target server. TCP name %s not found ", unc_name);
+ printf(" rc = %d\n",rc);
+ return 0;
+ }
+ else {
+ /* BB should we pass an alternate version of the share name as Unicode */
+ /* BB what about ipv6? BB */
+ /* BB add retries with alternate servers in list */
+
+ memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
+
+ ipaddress_string = inet_ntoa(server_ipaddr);
+ if(ipaddress_string == NULL) {
+ printf("mount error: could not get valid ip address for target server\n");
+ return 0;
+ }
+ return ipaddress_string;
+ }
+ } else {
+ /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
+ printf("Mounting the DFS root for a particular server not implemented yet\n");
+ return 0;
+ }
+ }
+ }
+}
+
+static struct option longopts[] = {
+ { "all", 0, 0, 'a' },
+ { "help", 0, 0, 'h' },
+ { "read-only", 0, 0, 'r' },
+ { "ro", 0, 0, 'r' },
+ { "verbose", 0, 0, 'v' },
+ { "version", 0, 0, 'V' },
+ { "read-write", 0, 0, 'w' },
+ { "rw", 0, 0, 'w' },
+ { "options", 1, 0, 'o' },
+ { "types", 1, 0, 't' },
+ { "rsize",1, 0, 'R' },
+ { "wsize",1, 0, 'W' },
+ { "uid", 1, 0, '1'},
+ { "gid", 1, 0, '2'},
+ { "uuid",1,0,'U' },
+ { "user",1,0,'u'},
+ { "username",1,0,'u'},
+ { "dom",1,0,'d'},
+ { "domain",1,0,'d'},
+ { "password",1,0,'p'},
+ { "pass",1,0,'p'},
+ { "credentials",1,0,'c'},
+ { "port",1,0,'P'},
+ { NULL, 0, 0, 0 }
+};
+
+int main(int argc, char ** argv)
+{
+ int c;
+ int flags = MS_MANDLOCK | MS_MGC_VAL;
+ char * orgoptions = NULL;
+ char * share_name = NULL;
+ char * domain_name = NULL;
+ char * ipaddr = NULL;
+ char * uuid = NULL;
+ char * mountpoint;
+ char * options;
+ char * temp;
+ int rc;
+ int rsize = 0;
+ int wsize = 0;
+ int nomtab = 0;
+ int uid = 0;
+ int gid = 0;
+ int optlen = 0;
+ int orgoptlen = 0;
+ struct stat statbuf;
+ struct utsname sysinfo;
+ struct mntent mountent;
+ FILE * pmntfile;
+
+ /* setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE); */
+
+ if(argc && argv) {
+ thisprogram = argv[0];
+ }
+ if(thisprogram == NULL)
+ thisprogram = "mount.cifs";
+
+ uname(&sysinfo);
+ /* BB add workstation name and domain and pass down */
+
+/* #ifdef _GNU_SOURCE
+ printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
+#endif */
+
+ share_name = argv[1];
+ mountpoint = argv[2];
+
+ /* add sharename in opts string as unc= parm */
+
+ while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsU:vVwt:",
+ longopts, NULL)) != -1) {
+ switch (c) {
+/* No code to do the following options yet */
+/* case 'l':
+ list_with_volumelabel = 1;
+ break;
+ case 'L':
+ volumelabel = optarg;
+ break; */
+/* case 'a':
+ ++mount_all;
+ break; */
+
+ case '?':
+ case 'h': /* help */
+ mount_cifs_usage ();
+ exit(1);
+ case 'n':
+ ++nomtab;
+ break;
+ case 'o':
+ orgoptions = strdup(optarg);
+ break;
+ case 'r': /* mount readonly */
+ flags |= MS_RDONLY;
+ break;
+ case 'U':
+ uuid = optarg;
+ break;
+ case 'v':
+ ++verboseflag;
+ break;
+ case 'V':
+ printf ("mount.cifs version: %s.%s%s\n",
+ MOUNT_CIFS_VERSION_MAJOR,
+ MOUNT_CIFS_VERSION_MINOR,
+ MOUNT_CIFS_VENDOR_SUFFIX);
+ if(mountpassword) {
+ memset(mountpassword,0,64);
+ }
+ exit (0);
+ case 'w':
+ flags &= ~MS_RDONLY;
+ break;
+ case 'R':
+ rsize = atoi(optarg) ;
+ break;
+ case 'W':
+ wsize = atoi(optarg);
+ break;
+ case '1':
+ uid = atoi(optarg);
+ break;
+ case '2':
+ gid = atoi(optarg);
+ break;
+ case 'u':
+ got_user = 1;
+ user_name = optarg;
+ break;
+ case 'd':
+ domain_name = optarg;
+ break;
+ case 'p':
+ if(mountpassword == NULL)
+ mountpassword = calloc(65,1);
+ if(mountpassword) {
+ got_password = 1;
+ strncpy(mountpassword,optarg,64);
+ }
+ break;
+ case 't':
+ break;
+ default:
+ printf("unknown mount option %c\n",c);
+ mount_cifs_usage();
+ exit(1);
+ }
+ }
+
+ if(argc < 3)
+ mount_cifs_usage();
+
+ if (getenv("PASSWD")) {
+ if(mountpassword == NULL)
+ mountpassword = calloc(65,1);
+ if(mountpassword) {
+ strncpy(mountpassword,getenv("PASSWD"),64);
+ got_password = 1;
+ }
+ } else if (getenv("PASSWD_FD")) {
+ get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
+ } else if (getenv("PASSWD_FILE")) {
+ get_password_from_file(0, getenv("PASSWD_FILE"));
+ }
+
+ ipaddr = parse_server(share_name);
+
+ if (orgoptions && parse_options(orgoptions))
+ return 1;
+
+ /* BB save off path and pop after mount returns? */
+ /* BB canonicalize the path in argv[1]? */
+
+ if(chdir(mountpoint)) {
+ printf("mount error: can not change directory into mount target %s\n",mountpoint);
+ }
+
+ if(stat (mountpoint, &statbuf)) {
+ printf("mount error: mount point %s does not exist\n",mountpoint);
+ return -1;
+ }
+
+ if (S_ISDIR(statbuf.st_mode) == 0) {
+ printf("mount error: mount point %s is not a directory\n",mountpoint);
+ return -1;
+ }
+
+ if((getuid() != 0) && (geteuid() == 0)) {
+ if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
+ printf("setuid mount allowed\n");
+ } else {
+ printf("mount error: permission denied or not superuser and cifs.mount not installed SUID\n");
+ return -1;
+ }
+ }
+
+ if(got_user == 0)
+ user_name = getusername();
+
+ if(got_password == 0) {
+ mountpassword = getpass("Password: "); /* BB obsolete */
+ got_password = 1;
+ }
+ /* FIXME launch daemon (handles dfs name resolution and credential change)
+ remember to clear parms and overwrite password field before launching */
+ if(orgoptions) {
+ optlen = strlen(orgoptions);
+ orgoptlen = optlen;
+ } else
+ optlen = 0;
+ if(share_name)
+ optlen += strlen(share_name) + 4;
+ if(user_name)
+ optlen += strlen(user_name) + 6;
+ if(ipaddr)
+ optlen += strlen(ipaddr) + 4;
+ if(mountpassword)
+ optlen += strlen(mountpassword) + 6;
+ options = malloc(optlen + 10);
+
+ options[0] = 0;
+ strncat(options,"unc=",4);
+ strcat(options,share_name);
+ /* scan backwards and reverse direction of slash */
+ temp = strrchr(options, '/');
+ if(temp > options + 6)
+ *temp = '\\';
+ if(ipaddr) {
+ strncat(options,",ip=",4);
+ strcat(options,ipaddr);
+ }
+ if(user_name) {
+ strncat(options,",user=",6);
+ strcat(options,user_name);
+ }
+ if(mountpassword) {
+ strncat(options,",pass=",6);
+ strcat(options,mountpassword);
+ }
+ strncat(options,",ver=",5);
+ strcat(options,MOUNT_CIFS_VERSION_MAJOR);
+
+ if(orgoptions) {
+ strcat(options,",");
+ strcat(options,orgoptions);
+ }
+ if(verboseflag)
+ printf("\ncifs.mount kernel mount options %s \n",options);
+ if(mount(share_name, mountpoint, "cifs", flags, options)) {
+ /* remember to kill daemon on error */
+ switch (errno) {
+ case 0:
+ printf("mount failed but no error number set\n");
+ break;
+ case ENODEV:
+ printf("mount error: cifs filesystem not supported by the system\n");
+ break;
+ default:
+ printf("mount error %d = %s\n",errno,strerror(errno));
+ }
+ printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
+ if(mountpassword) {
+ memset(mountpassword,0,64);
+ }
+ return -1;
+ } else {
+ pmntfile = setmntent(MOUNTED, "a+");
+ if(pmntfile) {
+ mountent.mnt_fsname = share_name;
+ mountent.mnt_dir = mountpoint;
+ mountent.mnt_type = "cifs";
+ mountent.mnt_opts = "";
+ mountent.mnt_freq = 0;
+ mountent.mnt_passno = 0;
+ rc = addmntent(pmntfile,&mountent);
+ endmntent(pmntfile);
+ } else {
+ printf("could not update mount table\n");
+ }
+ }
+ if(mountpassword) {
+ memset(mountpassword,0,64);
+ free(mountpassword);
+ }
+
+ if(options) {
+ memset(options,0,optlen);
+ free(options);
+ }
+
+ if(orgoptions) {
+ memset(orgoptions,0,orgoptlen);
+ free(orgoptions);
+ }
+ return 0;
+}
+
diff --git a/source/client/smbmnt.c b/source/client/smbmnt.c
new file mode 100644
index 00000000000..753a31c3139
--- /dev/null
+++ b/source/client/smbmnt.c
@@ -0,0 +1,306 @@
+/*
+ * smbmnt.c
+ *
+ * Copyright (C) 1995-1998 by Paal-Kr. Engstad and Volker Lendecke
+ * extensively modified by Tridge
+ *
+ */
+
+#include "includes.h"
+
+#include <mntent.h>
+#include <sys/utsname.h>
+
+#include <asm/types.h>
+#include <asm/posix_types.h>
+#include <linux/smb.h>
+#include <linux/smb_mount.h>
+#include <asm/unistd.h>
+
+#ifndef MS_MGC_VAL
+/* This may look strange but MS_MGC_VAL is what we are looking for and
+ is what we need from <linux/fs.h> under libc systems and is
+ provided in standard includes on glibc systems. So... We
+ switch on what we need... */
+#include <linux/fs.h>
+#endif
+
+static uid_t mount_uid;
+static gid_t mount_gid;
+static int mount_ro;
+static unsigned mount_fmask;
+static unsigned mount_dmask;
+static int user_mount;
+static char *options;
+
+static void
+help(void)
+{
+ printf("\n");
+ printf("Usage: smbmnt mount-point [options]\n");
+ printf("Version %s\n\n",SAMBA_VERSION_STRING);
+ printf("-s share share name on server\n"
+ "-r mount read-only\n"
+ "-u uid mount as uid\n"
+ "-g gid mount as gid\n"
+ "-f mask permission mask for files\n"
+ "-d mask permission mask for directories\n"
+ "-o options name=value, list of options\n"
+ "-h print this help text\n");
+}
+
+static int
+parse_args(int argc, char *argv[], struct smb_mount_data *data, char **share)
+{
+ int opt;
+
+ while ((opt = getopt (argc, argv, "s:u:g:rf:d:o:")) != EOF)
+ {
+ switch (opt)
+ {
+ case 's':
+ *share = optarg;
+ break;
+ case 'u':
+ if (!user_mount) {
+ mount_uid = strtol(optarg, NULL, 0);
+ }
+ break;
+ case 'g':
+ if (!user_mount) {
+ mount_gid = strtol(optarg, NULL, 0);
+ }
+ break;
+ case 'r':
+ mount_ro = 1;
+ break;
+ case 'f':
+ mount_fmask = strtol(optarg, NULL, 8);
+ break;
+ case 'd':
+ mount_dmask = strtol(optarg, NULL, 8);
+ break;
+ case 'o':
+ options = optarg;
+ break;
+ default:
+ return -1;
+ }
+ }
+ return 0;
+
+}
+
+static char *
+fullpath(const char *p)
+{
+ char path[PATH_MAX+1];
+
+ if (strlen(p) > PATH_MAX) {
+ return NULL;
+ }
+
+ if (realpath(p, path) == NULL) {
+ fprintf(stderr,"Failed to find real path for mount point\n");
+ exit(1);
+ }
+ return strdup(path);
+}
+
+/* Check whether user is allowed to mount on the specified mount point. If it's
+ OK then we change into that directory - this prevents race conditions */
+static int mount_ok(char *mount_point)
+{
+ struct stat st;
+
+ if (chdir(mount_point) != 0) {
+ return -1;
+ }
+
+ if (stat(".", &st) != 0) {
+ return -1;
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ errno = ENOTDIR;
+ return -1;
+ }
+
+ if ((getuid() != 0) &&
+ ((getuid() != st.st_uid) ||
+ ((st.st_mode & S_IRWXU) != S_IRWXU))) {
+ errno = EPERM;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Tries to mount using the appropriate format. For 2.2 the struct,
+ for 2.4 the ascii version. */
+static int
+do_mount(char *share_name, unsigned int flags, struct smb_mount_data *data)
+{
+ pstring opts;
+ struct utsname uts;
+ char *release, *major, *minor;
+ char *data1, *data2;
+
+ uname(&uts);
+ release = uts.release;
+ major = strtok(release, ".");
+ minor = strtok(NULL, ".");
+ if (major && minor && atoi(major) == 2 && atoi(minor) < 4) {
+ /* < 2.4, assume struct */
+ data1 = (char *) data;
+ data2 = opts;
+ } else {
+ /* >= 2.4, assume ascii but fall back on struct */
+ data1 = opts;
+ data2 = (char *) data;
+ }
+
+ slprintf(opts, sizeof(opts)-1,
+ "version=7,uid=%d,gid=%d,file_mode=0%o,dir_mode=0%o,%s",
+ data->uid, data->gid, data->file_mode, data->dir_mode,options);
+ if (mount(share_name, ".", "smbfs", flags, data1) == 0)
+ return 0;
+ return mount(share_name, ".", "smbfs", flags, data2);
+}
+
+ int main(int argc, char *argv[])
+{
+ char *mount_point, *share_name = NULL;
+ FILE *mtab;
+ int fd;
+ unsigned int flags;
+ struct smb_mount_data data;
+ struct mntent ment;
+
+ memset(&data, 0, sizeof(struct smb_mount_data));
+
+ if (argc < 2) {
+ help();
+ exit(1);
+ }
+
+ if (argv[1][0] == '-') {
+ help();
+ exit(1);
+ }
+
+ if (getuid() != 0) {
+ user_mount = 1;
+ }
+
+ if (geteuid() != 0) {
+ fprintf(stderr, "smbmnt must be installed suid root for direct user mounts (%d,%d)\n", getuid(), geteuid());
+ exit(1);
+ }
+
+ mount_uid = getuid();
+ mount_gid = getgid();
+ mount_fmask = umask(0);
+ umask(mount_fmask);
+ mount_fmask = ~mount_fmask;
+
+ mount_point = fullpath(argv[1]);
+
+ argv += 1;
+ argc -= 1;
+
+ if (mount_ok(mount_point) != 0) {
+ fprintf(stderr, "cannot mount on %s: %s\n",
+ mount_point, strerror(errno));
+ exit(1);
+ }
+
+ data.version = SMB_MOUNT_VERSION;
+
+ /* getuid() gives us the real uid, who may umount the fs */
+ data.mounted_uid = getuid();
+
+ if (parse_args(argc, argv, &data, &share_name) != 0) {
+ help();
+ return -1;
+ }
+
+ data.uid = mount_uid;
+ data.gid = mount_gid;
+ data.file_mode = (S_IRWXU|S_IRWXG|S_IRWXO) & mount_fmask;
+ data.dir_mode = (S_IRWXU|S_IRWXG|S_IRWXO) & mount_dmask;
+
+ if (mount_dmask == 0) {
+ data.dir_mode = data.file_mode;
+ if ((data.dir_mode & S_IRUSR) != 0)
+ data.dir_mode |= S_IXUSR;
+ if ((data.dir_mode & S_IRGRP) != 0)
+ data.dir_mode |= S_IXGRP;
+ if ((data.dir_mode & S_IROTH) != 0)
+ data.dir_mode |= S_IXOTH;
+ }
+
+ flags = MS_MGC_VAL | MS_NOSUID | MS_NODEV;
+
+ if (mount_ro) flags |= MS_RDONLY;
+
+ if (do_mount(share_name, flags, &data) < 0) {
+ switch (errno) {
+ case ENODEV:
+ fprintf(stderr, "ERROR: smbfs filesystem not supported by the kernel\n");
+ break;
+ default:
+ perror("mount error");
+ }
+ fprintf(stderr, "Please refer to the smbmnt(8) manual page\n");
+ return -1;
+ }
+
+ ment.mnt_fsname = share_name ? share_name : "none";
+ ment.mnt_dir = mount_point;
+ ment.mnt_type = "smbfs";
+ ment.mnt_opts = "";
+ ment.mnt_freq = 0;
+ ment.mnt_passno= 0;
+
+ mount_point = ment.mnt_dir;
+
+ if (mount_point == NULL)
+ {
+ fprintf(stderr, "Mount point too long\n");
+ return -1;
+ }
+
+ if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1)
+ {
+ fprintf(stderr, "Can't get "MOUNTED"~ lock file");
+ return 1;
+ }
+ close(fd);
+
+ if ((mtab = setmntent(MOUNTED, "a+")) == NULL)
+ {
+ fprintf(stderr, "Can't open " MOUNTED);
+ return 1;
+ }
+
+ if (addmntent(mtab, &ment) == 1)
+ {
+ fprintf(stderr, "Can't write mount entry");
+ return 1;
+ }
+ if (fchmod(fileno(mtab), 0644) == -1)
+ {
+ fprintf(stderr, "Can't set perms on "MOUNTED);
+ return 1;
+ }
+ endmntent(mtab);
+
+ if (unlink(MOUNTED"~") == -1)
+ {
+ fprintf(stderr, "Can't remove "MOUNTED"~");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/source/client/smbmount.c b/source/client/smbmount.c
new file mode 100644
index 00000000000..6e8d9f5bca7
--- /dev/null
+++ b/source/client/smbmount.c
@@ -0,0 +1,940 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMBFS mount program
+ Copyright (C) Andrew Tridgell 1999
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+#include <mntent.h>
+#include <asm/types.h>
+#include <linux/smb_fs.h>
+
+extern BOOL in_client;
+extern pstring user_socket_options;
+
+static pstring credentials;
+static pstring my_netbios_name;
+static pstring password;
+static pstring username;
+static pstring workgroup;
+static pstring mpoint;
+static pstring service;
+static pstring options;
+
+static struct in_addr dest_ip;
+static BOOL have_ip;
+static int smb_port = 0;
+static BOOL got_user;
+static BOOL got_pass;
+static uid_t mount_uid;
+static gid_t mount_gid;
+static int mount_ro;
+static unsigned mount_fmask;
+static unsigned mount_dmask;
+static BOOL use_kerberos;
+/* TODO: Add code to detect smbfs version in kernel */
+static BOOL status32_smbfs = False;
+static BOOL smbfs_has_unicode = False;
+static BOOL smbfs_has_lfs = False;
+
+static void usage(void);
+
+static void exit_parent(int sig)
+{
+ /* parent simply exits when child says go... */
+ exit(0);
+}
+
+static void daemonize(void)
+{
+ int j, status;
+ pid_t child_pid;
+
+ signal( SIGTERM, exit_parent );
+
+ if ((child_pid = sys_fork()) < 0) {
+ DEBUG(0,("could not fork\n"));
+ }
+
+ if (child_pid > 0) {
+ while( 1 ) {
+ j = waitpid( child_pid, &status, 0 );
+ if( j < 0 ) {
+ if( EINTR == errno ) {
+ continue;
+ }
+ status = errno;
+ }
+ break;
+ }
+
+ /* If we get here - the child exited with some error status */
+ if (WIFSIGNALED(status))
+ exit(128 + WTERMSIG(status));
+ else
+ exit(WEXITSTATUS(status));
+ }
+
+ signal( SIGTERM, SIG_DFL );
+ chdir("/");
+}
+
+static void close_our_files(int client_fd)
+{
+ int i;
+ struct rlimit limits;
+
+ getrlimit(RLIMIT_NOFILE,&limits);
+ for (i = 0; i< limits.rlim_max; i++) {
+ if (i == client_fd)
+ continue;
+ close(i);
+ }
+}
+
+static void usr1_handler(int x)
+{
+ return;
+}
+
+
+/*****************************************************
+return a connection to a server
+*******************************************************/
+static struct cli_state *do_connection(char *the_service)
+{
+ struct cli_state *c;
+ struct nmb_name called, calling;
+ char *server_n;
+ struct in_addr ip;
+ pstring server;
+ char *share;
+
+ if (the_service[0] != '\\' || the_service[1] != '\\') {
+ usage();
+ exit(1);
+ }
+
+ pstrcpy(server, the_service+2);
+ share = strchr_m(server,'\\');
+ if (!share) {
+ usage();
+ exit(1);
+ }
+ *share = 0;
+ share++;
+
+ server_n = server;
+
+ make_nmb_name(&calling, my_netbios_name, 0x0);
+ make_nmb_name(&called , server, 0x20);
+
+ again:
+ zero_ip(&ip);
+ if (have_ip) ip = dest_ip;
+
+ /* have to open a new connection */
+ if (!(c=cli_initialise(NULL)) || (cli_set_port(c, smb_port) != smb_port) ||
+ !cli_connect(c, server_n, &ip)) {
+ DEBUG(0,("%d: Connection to %s failed\n", sys_getpid(), server_n));
+ if (c) {
+ cli_shutdown(c);
+ }
+ return NULL;
+ }
+
+ /* SPNEGO doesn't work till we get NTSTATUS error support */
+ /* But it is REQUIRED for kerberos authentication */
+ if(!use_kerberos) c->use_spnego = False;
+
+ /* The kernel doesn't yet know how to sign it's packets */
+ c->sign_info.allow_smb_signing = False;
+
+ /* Use kerberos authentication if specified */
+ c->use_kerberos = use_kerberos;
+
+ if (!cli_session_request(c, &calling, &called)) {
+ char *p;
+ DEBUG(0,("%d: session request to %s failed (%s)\n",
+ sys_getpid(), called.name, cli_errstr(c)));
+ cli_shutdown(c);
+ if ((p=strchr_m(called.name, '.'))) {
+ *p = 0;
+ goto again;
+ }
+ if (strcmp(called.name, "*SMBSERVER")) {
+ make_nmb_name(&called , "*SMBSERVER", 0x20);
+ goto again;
+ }
+ return NULL;
+ }
+
+ DEBUG(4,("%d: session request ok\n", sys_getpid()));
+
+ if (!cli_negprot(c)) {
+ DEBUG(0,("%d: protocol negotiation failed\n", sys_getpid()));
+ cli_shutdown(c);
+ return NULL;
+ }
+
+ if (!got_pass) {
+ char *pass = getpass("Password: ");
+ if (pass) {
+ pstrcpy(password, pass);
+ }
+ }
+
+ /* This should be right for current smbfs. Future versions will support
+ large files as well as unicode and oplocks. */
+ c->capabilities &= ~(CAP_NT_SMBS | CAP_NT_FIND | CAP_LEVEL_II_OPLOCKS);
+ if (!smbfs_has_lfs)
+ c->capabilities &= ~CAP_LARGE_FILES;
+ if (!smbfs_has_unicode)
+ c->capabilities &= ~CAP_UNICODE;
+ if (!status32_smbfs) {
+ c->capabilities &= ~CAP_STATUS32;
+ c->force_dos_errors = True;
+ }
+
+ if (!cli_session_setup(c, username,
+ password, strlen(password),
+ password, strlen(password),
+ workgroup)) {
+ /* if a password was not supplied then try again with a
+ null username */
+ if (password[0] || !username[0] ||
+ !cli_session_setup(c, "", "", 0, "", 0, workgroup)) {
+ DEBUG(0,("%d: session setup failed: %s\n",
+ sys_getpid(), cli_errstr(c)));
+ cli_shutdown(c);
+ return NULL;
+ }
+ DEBUG(0,("Anonymous login successful\n"));
+ }
+
+ DEBUG(4,("%d: session setup ok\n", sys_getpid()));
+
+ if (!cli_send_tconX(c, share, "?????",
+ password, strlen(password)+1)) {
+ DEBUG(0,("%d: tree connect failed: %s\n",
+ sys_getpid(), cli_errstr(c)));
+ cli_shutdown(c);
+ return NULL;
+ }
+
+ DEBUG(4,("%d: tconx ok\n", sys_getpid()));
+
+ got_pass = True;
+
+ return c;
+}
+
+
+/****************************************************************************
+unmount smbfs (this is a bailout routine to clean up if a reconnect fails)
+ Code blatently stolen from smbumount.c
+ -mhw-
+****************************************************************************/
+static void smb_umount(char *mount_point)
+{
+ int fd;
+ struct mntent *mnt;
+ FILE* mtab;
+ FILE* new_mtab;
+
+ /* Programmers Note:
+ This routine only gets called to the scene of a disaster
+ to shoot the survivors... A connection that was working
+ has now apparently failed. We have an active mount point
+ (presumably) that we need to dump. If we get errors along
+ the way - make some noise, but we are already turning out
+ the lights to exit anyways...
+ */
+ if (umount(mount_point) != 0) {
+ DEBUG(0,("%d: Could not umount %s: %s\n",
+ sys_getpid(), mount_point, strerror(errno)));
+ return;
+ }
+
+ if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) {
+ DEBUG(0,("%d: Can't get "MOUNTED"~ lock file", sys_getpid()));
+ return;
+ }
+
+ close(fd);
+
+ if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
+ DEBUG(0,("%d: Can't open " MOUNTED ": %s\n",
+ sys_getpid(), strerror(errno)));
+ return;
+ }
+
+#define MOUNTED_TMP MOUNTED".tmp"
+
+ if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) {
+ DEBUG(0,("%d: Can't open " MOUNTED_TMP ": %s\n",
+ sys_getpid(), strerror(errno)));
+ endmntent(mtab);
+ return;
+ }
+
+ while ((mnt = getmntent(mtab)) != NULL) {
+ if (strcmp(mnt->mnt_dir, mount_point) != 0) {
+ addmntent(new_mtab, mnt);
+ }
+ }
+
+ endmntent(mtab);
+
+ if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
+ DEBUG(0,("%d: Error changing mode of %s: %s\n",
+ sys_getpid(), MOUNTED_TMP, strerror(errno)));
+ return;
+ }
+
+ endmntent(new_mtab);
+
+ if (rename(MOUNTED_TMP, MOUNTED) < 0) {
+ DEBUG(0,("%d: Cannot rename %s to %s: %s\n",
+ sys_getpid(), MOUNTED, MOUNTED_TMP, strerror(errno)));
+ return;
+ }
+
+ if (unlink(MOUNTED"~") == -1) {
+ DEBUG(0,("%d: Can't remove "MOUNTED"~", sys_getpid()));
+ return;
+ }
+}
+
+
+/*
+ * Call the smbfs ioctl to install a connection socket,
+ * then wait for a signal to reconnect. Note that we do
+ * not exit after open_sockets() or send_login() errors,
+ * as the smbfs mount would then have no way to recover.
+ */
+static void send_fs_socket(char *the_service, char *mount_point, struct cli_state *c)
+{
+ int fd, closed = 0, res = 1;
+ pid_t parentpid = getppid();
+ struct smb_conn_opt conn_options;
+
+ memset(&conn_options, 0, sizeof(conn_options));
+
+ while (1) {
+ if ((fd = open(mount_point, O_RDONLY)) < 0) {
+ DEBUG(0,("mount.smbfs[%d]: can't open %s\n",
+ sys_getpid(), mount_point));
+ break;
+ }
+
+ conn_options.fd = c->fd;
+ conn_options.protocol = c->protocol;
+ conn_options.case_handling = SMB_CASE_DEFAULT;
+ conn_options.max_xmit = c->max_xmit;
+ conn_options.server_uid = c->vuid;
+ conn_options.tid = c->cnum;
+ conn_options.secmode = c->sec_mode;
+ conn_options.rawmode = 0;
+ conn_options.sesskey = c->sesskey;
+ conn_options.maxraw = 0;
+ conn_options.capabilities = c->capabilities;
+ conn_options.serverzone = c->serverzone/60;
+
+ res = ioctl(fd, SMB_IOC_NEWCONN, &conn_options);
+ if (res != 0) {
+ DEBUG(0,("mount.smbfs[%d]: ioctl failed, res=%d\n",
+ sys_getpid(), res));
+ close(fd);
+ break;
+ }
+
+ if (parentpid) {
+ /* Ok... We are going to kill the parent. Now
+ is the time to break the process group... */
+ setsid();
+ /* Send a signal to the parent to terminate */
+ kill(parentpid, SIGTERM);
+ parentpid = 0;
+ }
+
+ close(fd);
+
+ /* This looks wierd but we are only closing the userspace
+ side, the connection has already been passed to smbfs and
+ it has increased the usage count on the socket.
+
+ If we don't do this we will "leak" sockets and memory on
+ each reconnection we have to make. */
+ cli_shutdown(c);
+ c = NULL;
+
+ if (!closed) {
+ /* redirect stdout & stderr since we can't know that
+ the library functions we use are using DEBUG. */
+ if ( (fd = open("/dev/null", O_WRONLY)) < 0)
+ DEBUG(2,("mount.smbfs: can't open /dev/null\n"));
+ close_our_files(fd);
+ if (fd >= 0) {
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ close(fd);
+ }
+
+ /* here we are no longer interactive */
+ set_remote_machine_name("smbmount", False); /* sneaky ... */
+ setup_logging("mount.smbfs", False);
+ reopen_logs();
+ DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", the_service, sys_getpid()));
+
+ closed = 1;
+ }
+
+ /* Wait for a signal from smbfs ... but don't continue
+ until we actually get a new connection. */
+ while (!c) {
+ CatchSignal(SIGUSR1, &usr1_handler);
+ pause();
+ DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", sys_getpid()));
+ c = do_connection(the_service);
+ }
+ }
+
+ smb_umount(mount_point);
+ DEBUG(2,("mount.smbfs[%d]: exit\n", sys_getpid()));
+ exit(1);
+}
+
+
+/**
+ * Mount a smbfs
+ **/
+static void init_mount(void)
+{
+ char mount_point[PATH_MAX+1];
+ pstring tmp;
+ pstring svc2;
+ struct cli_state *c;
+ char *args[20];
+ int i, status;
+
+ if (realpath(mpoint, mount_point) == NULL) {
+ fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
+ return;
+ }
+
+
+ c = do_connection(service);
+ if (!c) {
+ fprintf(stderr,"SMB connection failed\n");
+ exit(1);
+ }
+
+ /*
+ Set up to return as a daemon child and wait in the parent
+ until the child say it's ready...
+ */
+ daemonize();
+
+ pstrcpy(svc2, service);
+ string_replace(svc2, '\\','/');
+ string_replace(svc2, ' ','_');
+
+ memset(args, 0, sizeof(args[0])*20);
+
+ i=0;
+ args[i++] = "smbmnt";
+
+ args[i++] = mount_point;
+ args[i++] = "-s";
+ args[i++] = svc2;
+
+ if (mount_ro) {
+ args[i++] = "-r";
+ }
+ if (mount_uid) {
+ slprintf(tmp, sizeof(tmp)-1, "%d", mount_uid);
+ args[i++] = "-u";
+ args[i++] = smb_xstrdup(tmp);
+ }
+ if (mount_gid) {
+ slprintf(tmp, sizeof(tmp)-1, "%d", mount_gid);
+ args[i++] = "-g";
+ args[i++] = smb_xstrdup(tmp);
+ }
+ if (mount_fmask) {
+ slprintf(tmp, sizeof(tmp)-1, "0%o", mount_fmask);
+ args[i++] = "-f";
+ args[i++] = smb_xstrdup(tmp);
+ }
+ if (mount_dmask) {
+ slprintf(tmp, sizeof(tmp)-1, "0%o", mount_dmask);
+ args[i++] = "-d";
+ args[i++] = smb_xstrdup(tmp);
+ }
+ if (options) {
+ args[i++] = "-o";
+ args[i++] = options;
+ }
+
+ if (sys_fork() == 0) {
+ char *smbmnt_path;
+
+ asprintf(&smbmnt_path, "%s/smbmnt", dyn_BINDIR);
+
+ if (file_exist(smbmnt_path, NULL)) {
+ execv(smbmnt_path, args);
+ fprintf(stderr,
+ "smbfs/init_mount: execv of %s failed. Error was %s.",
+ smbmnt_path, strerror(errno));
+ } else {
+ execvp("smbmnt", args);
+ fprintf(stderr,
+ "smbfs/init_mount: execv of %s failed. Error was %s.",
+ "smbmnt", strerror(errno));
+ }
+ free(smbmnt_path);
+ exit(1);
+ }
+
+ if (waitpid(-1, &status, 0) == -1) {
+ fprintf(stderr,"waitpid failed: Error was %s", strerror(errno) );
+ /* FIXME: do some proper error handling */
+ exit(1);
+ }
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
+ fprintf(stderr,"smbmnt failed: %d\n", WEXITSTATUS(status));
+ /* FIXME: do some proper error handling */
+ exit(1);
+ } else if (WIFSIGNALED(status)) {
+ fprintf(stderr, "smbmnt killed by signal %d\n", WTERMSIG(status));
+ exit(1);
+ }
+
+ /* Ok... This is the rubicon for that mount point... At any point
+ after this, if the connections fail and can not be reconstructed
+ for any reason, we will have to unmount the mount point. There
+ is no exit from the next call...
+ */
+ send_fs_socket(service, mount_point, c);
+}
+
+
+/****************************************************************************
+get a password from a a file or file descriptor
+exit on failure (from smbclient, move to libsmb or shared .c file?)
+****************************************************************************/
+static void get_password_file(void)
+{
+ int fd = -1;
+ char *p;
+ BOOL close_it = False;
+ pstring spec;
+ char pass[128];
+
+ if ((p = getenv("PASSWD_FD")) != NULL) {
+ pstrcpy(spec, "descriptor ");
+ pstrcat(spec, p);
+ sscanf(p, "%d", &fd);
+ close_it = False;
+ } else if ((p = getenv("PASSWD_FILE")) != NULL) {
+ fd = sys_open(p, O_RDONLY, 0);
+ pstrcpy(spec, p);
+ if (fd < 0) {
+ fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
+ spec, strerror(errno));
+ exit(1);
+ }
+ close_it = True;
+ }
+
+ for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
+ p && p - pass < sizeof(pass);) {
+ switch (read(fd, p, 1)) {
+ case 1:
+ if (*p != '\n' && *p != '\0') {
+ *++p = '\0'; /* advance p, and null-terminate pass */
+ break;
+ }
+ case 0:
+ if (p - pass) {
+ *p = '\0'; /* null-terminate it, just in case... */
+ p = NULL; /* then force the loop condition to become false */
+ break;
+ } else {
+ fprintf(stderr, "Error reading password from file %s: %s\n",
+ spec, "empty password\n");
+ exit(1);
+ }
+
+ default:
+ fprintf(stderr, "Error reading password from file %s: %s\n",
+ spec, strerror(errno));
+ exit(1);
+ }
+ }
+ pstrcpy(password, pass);
+ if (close_it)
+ close(fd);
+}
+
+/****************************************************************************
+get username and password from a credentials file
+exit on failure (from smbclient, move to libsmb or shared .c file?)
+****************************************************************************/
+static void read_credentials_file(char *filename)
+{
+ FILE *auth;
+ fstring buf;
+ uint16 len = 0;
+ char *ptr, *val, *param;
+
+ if ((auth=sys_fopen(filename, "r")) == NULL)
+ {
+ /* fail if we can't open the credentials file */
+ DEBUG(0,("ERROR: Unable to open credentials file!\n"));
+ exit (-1);
+ }
+
+ while (!feof(auth))
+ {
+ /* get a line from the file */
+ if (!fgets (buf, sizeof(buf), auth))
+ continue;
+ len = strlen(buf);
+
+ if ((len) && (buf[len-1]=='\n'))
+ {
+ buf[len-1] = '\0';
+ len--;
+ }
+ if (len == 0)
+ continue;
+
+ /* break up the line into parameter & value.
+ will need to eat a little whitespace possibly */
+ param = buf;
+ if (!(ptr = strchr (buf, '=')))
+ continue;
+ val = ptr+1;
+ *ptr = '\0';
+
+ /* eat leading white space */
+ while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
+ val++;
+
+ if (strwicmp("password", param) == 0)
+ {
+ pstrcpy(password, val);
+ got_pass = True;
+ }
+ else if (strwicmp("username", param) == 0) {
+ pstrcpy(username, val);
+ }
+
+ memset(buf, 0, sizeof(buf));
+ }
+ fclose(auth);
+}
+
+
+/****************************************************************************
+usage on the program
+****************************************************************************/
+static void usage(void)
+{
+ printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
+
+ printf("Version %s\n\n",SAMBA_VERSION_STRING);
+
+ printf(
+"Options:\n\
+ username=<arg> SMB username\n\
+ password=<arg> SMB password\n\
+ credentials=<filename> file with username/password\n\
+ krb use kerberos (active directory)\n\
+ netbiosname=<arg> source NetBIOS name\n\
+ uid=<arg> mount uid or username\n\
+ gid=<arg> mount gid or groupname\n\
+ port=<arg> remote SMB port number\n\
+ fmask=<arg> file umask\n\
+ dmask=<arg> directory umask\n\
+ debug=<arg> debug level\n\
+ ip=<arg> destination host or IP address\n\
+ workgroup=<arg> workgroup on destination\n\
+ sockopt=<arg> TCP socket options\n\
+ scope=<arg> NetBIOS scope\n\
+ iocharset=<arg> Linux charset (iso8859-1, utf8)\n\
+ codepage=<arg> server codepage (cp850)\n\
+ unicode use unicode when communicating with server\n\
+ lfs large file system support\n\
+ ttl=<arg> dircache time to live\n\
+ guest don't prompt for a password\n\
+ ro mount read-only\n\
+ rw mount read-write\n\
+\n\
+This command is designed to be run from within /bin/mount by giving\n\
+the option '-t smbfs'. For example:\n\
+ mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test\n\
+");
+}
+
+
+/****************************************************************************
+ Argument parsing for mount.smbfs interface
+ mount will call us like this:
+ mount.smbfs device mountpoint -o <options>
+
+ <options> is never empty, containing at least rw or ro
+ ****************************************************************************/
+static void parse_mount_smb(int argc, char **argv)
+{
+ int opt;
+ char *opts;
+ char *opteq;
+ extern char *optarg;
+ int val;
+ char *p;
+
+ /* FIXME: This function can silently fail if the arguments are
+ * not in the expected order.
+
+ > The arguments syntax of smbmount 2.2.3a (smbfs of Debian stable)
+ > requires that one gives "-o" before further options like username=...
+ > . Without -o, the username=.. setting is *silently* ignored. I've
+ > spent about an hour trying to find out why I couldn't log in now..
+
+ */
+
+
+ if (argc < 2 || argv[1][0] == '-') {
+ usage();
+ exit(1);
+ }
+
+ pstrcpy(service, argv[1]);
+ pstrcpy(mpoint, argv[2]);
+
+ /* Convert any '/' characters in the service name to
+ '\' characters */
+ string_replace(service, '/','\\');
+ argc -= 2;
+ argv += 2;
+
+ opt = getopt(argc, argv, "o:");
+ if(opt != 'o') {
+ return;
+ }
+
+ options[0] = 0;
+ p = options;
+
+ /*
+ * option parsing from nfsmount.c (util-linux-2.9u)
+ */
+ for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
+ DEBUG(3, ("opts: %s\n", opts));
+ if ((opteq = strchr_m(opts, '='))) {
+ val = atoi(opteq + 1);
+ *opteq = '\0';
+
+ if (!strcmp(opts, "username") ||
+ !strcmp(opts, "logon")) {
+ char *lp;
+ got_user = True;
+ pstrcpy(username,opteq+1);
+ if ((lp=strchr_m(username,'%'))) {
+ *lp = 0;
+ pstrcpy(password,lp+1);
+ got_pass = True;
+ memset(strchr_m(opteq+1,'%')+1,'X',strlen(password));
+ }
+ if ((lp=strchr_m(username,'/'))) {
+ *lp = 0;
+ pstrcpy(workgroup,lp+1);
+ }
+ } else if(!strcmp(opts, "passwd") ||
+ !strcmp(opts, "password")) {
+ pstrcpy(password,opteq+1);
+ got_pass = True;
+ memset(opteq+1,'X',strlen(password));
+ } else if(!strcmp(opts, "credentials")) {
+ pstrcpy(credentials,opteq+1);
+ } else if(!strcmp(opts, "netbiosname")) {
+ pstrcpy(my_netbios_name,opteq+1);
+ } else if(!strcmp(opts, "uid")) {
+ mount_uid = nametouid(opteq+1);
+ } else if(!strcmp(opts, "gid")) {
+ mount_gid = nametogid(opteq+1);
+ } else if(!strcmp(opts, "port")) {
+ smb_port = val;
+ } else if(!strcmp(opts, "fmask")) {
+ mount_fmask = strtol(opteq+1, NULL, 8);
+ } else if(!strcmp(opts, "dmask")) {
+ mount_dmask = strtol(opteq+1, NULL, 8);
+ } else if(!strcmp(opts, "debug")) {
+ DEBUGLEVEL = val;
+ } else if(!strcmp(opts, "ip")) {
+ dest_ip = *interpret_addr2(opteq+1);
+ if (is_zero_ip(dest_ip)) {
+ fprintf(stderr,"Can't resolve address %s\n", opteq+1);
+ exit(1);
+ }
+ have_ip = True;
+ } else if(!strcmp(opts, "workgroup")) {
+ pstrcpy(workgroup,opteq+1);
+ } else if(!strcmp(opts, "sockopt")) {
+ pstrcpy(user_socket_options,opteq+1);
+ } else if(!strcmp(opts, "scope")) {
+ set_global_scope(opteq+1);
+ } else {
+ slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
+ p += strlen(p);
+ }
+ } else {
+ val = 1;
+ if(!strcmp(opts, "nocaps")) {
+ fprintf(stderr, "Unhandled option: %s\n", opteq+1);
+ exit(1);
+ } else if(!strcmp(opts, "guest")) {
+ *password = '\0';
+ got_pass = True;
+ } else if(!strcmp(opts, "krb")) {
+#ifdef HAVE_KRB5
+
+ use_kerberos = True;
+ if(!status32_smbfs)
+ fprintf(stderr, "Warning: kerberos support will only work for samba servers\n");
+#else
+ fprintf(stderr,"No kerberos support compiled in\n");
+ exit(1);
+#endif
+ } else if(!strcmp(opts, "rw")) {
+ mount_ro = 0;
+ } else if(!strcmp(opts, "ro")) {
+ mount_ro = 1;
+ } else if(!strcmp(opts, "unicode")) {
+ smbfs_has_unicode = True;
+ } else if(!strcmp(opts, "lfs")) {
+ smbfs_has_lfs = True;
+ } else {
+ strncpy(p, opts, sizeof(pstring) - (p - options) - 1);
+ p += strlen(opts);
+ *p++ = ',';
+ *p = 0;
+ }
+ }
+ }
+
+ if (!*service) {
+ usage();
+ exit(1);
+ }
+
+ if (p != options) {
+ *(p-1) = 0; /* remove trailing , */
+ DEBUG(3,("passthrough options '%s'\n", options));
+ }
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+ extern char *optarg;
+ extern int optind;
+ char *p;
+
+ DEBUGLEVEL = 1;
+
+ /* here we are interactive, even if run from autofs */
+ setup_logging("mount.smbfs",True);
+
+#if 0 /* JRA - Urban says not needed ? */
+ /* CLI_FORCE_ASCII=false makes smbmount negotiate unicode. The default
+ is to not announce any unicode capabilities as current smbfs does
+ not support it. */
+ p = getenv("CLI_FORCE_ASCII");
+ if (p && !strcmp(p, "false"))
+ unsetenv("CLI_FORCE_ASCII");
+ else
+ setenv("CLI_FORCE_ASCII", "true", 1);
+#endif
+
+ in_client = True; /* Make sure that we tell lp_load we are */
+
+ if (getenv("USER")) {
+ pstrcpy(username,getenv("USER"));
+
+ if ((p=strchr_m(username,'%'))) {
+ *p = 0;
+ pstrcpy(password,p+1);
+ got_pass = True;
+ memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password));
+ }
+ strupper_m(username);
+ }
+
+ if (getenv("PASSWD")) {
+ pstrcpy(password,getenv("PASSWD"));
+ got_pass = True;
+ }
+
+ if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
+ get_password_file();
+ got_pass = True;
+ }
+
+ if (*username == 0 && getenv("LOGNAME")) {
+ pstrcpy(username,getenv("LOGNAME"));
+ }
+
+ if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
+ fprintf(stderr, "Can't load %s - run testparm to debug it\n",
+ dyn_CONFIGFILE);
+ }
+
+ parse_mount_smb(argc, argv);
+
+ if (use_kerberos && !got_user) {
+ got_pass = True;
+ }
+
+ if (*credentials != 0) {
+ read_credentials_file(credentials);
+ }
+
+ DEBUG(3,("mount.smbfs started (version %s)\n", SAMBA_VERSION_STRING));
+
+ if (*workgroup == 0) {
+ pstrcpy(workgroup,lp_workgroup());
+ }
+
+ load_interfaces();
+ if (!*my_netbios_name) {
+ pstrcpy(my_netbios_name, myhostname());
+ }
+ strupper_m(my_netbios_name);
+
+ init_mount();
+ return 0;
+}
diff --git a/source/client/smbspool.c b/source/client/smbspool.c
new file mode 100644
index 00000000000..5daefec5a55
--- /dev/null
+++ b/source/client/smbspool.c
@@ -0,0 +1,363 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB backend for the Common UNIX Printing System ("CUPS")
+ Copyright 1999 by Easy Software Products
+ Copyright Andrew Tridgell 1994-1998
+ Copyright Andrew Bartlett 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+/*
+ * Globals...
+ */
+
+extern BOOL in_client; /* Boolean for client library */
+
+
+/*
+ * Local functions...
+ */
+
+static void list_devices(void);
+static struct cli_state *smb_connect(const char *, const char *, const char *, const char *, const char *);
+static int smb_print(struct cli_state *, char *, FILE *);
+
+
+/*
+ * 'main()' - Main entry for SMB backend.
+ */
+
+ int /* O - Exit status */
+ main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int i; /* Looping var */
+ int copies; /* Number of copies */
+ char uri[1024], /* URI */
+ *sep, /* Pointer to separator */
+ *password; /* Password */
+ const char *username, /* Username */
+ *server, /* Server name */
+ *printer; /* Printer name */
+ const char *workgroup; /* Workgroup */
+ FILE *fp; /* File to print */
+ int status=0; /* Status of LPD job */
+ struct cli_state *cli; /* SMB interface */
+
+ /* we expect the URI in argv[0]. Detect the case where it is in argv[1] and cope */
+ if (argc > 2 && strncmp(argv[0],"smb://", 6) && !strncmp(argv[1],"smb://", 6)) {
+ argv++;
+ argc--;
+ }
+
+ if (argc == 1)
+ {
+ /*
+ * NEW! In CUPS 1.1 the backends are run with no arguments to list the
+ * available devices. These can be devices served by this backend
+ * or any other backends (i.e. you can have an SNMP backend that
+ * is only used to enumerate the available network printers... :)
+ */
+
+ list_devices();
+ return (0);
+ }
+
+ if (argc < 6 || argc > 7)
+ {
+ fprintf(stderr, "Usage: %s [DEVICE_URI] job-id user title copies options [file]\n",
+ argv[0]);
+ fputs(" The DEVICE_URI environment variable can also contain the\n", stderr);
+ fputs(" destination printer:\n", stderr);
+ fputs("\n", stderr);
+ fputs(" smb://[username:password@][workgroup/]server/printer\n", stderr);
+ return (1);
+ }
+
+ /*
+ * If we have 7 arguments, print the file named on the command-line.
+ * Otherwise, print data from stdin...
+ */
+
+ if (argc == 6)
+ {
+ /*
+ * Print from Copy stdin to a temporary file...
+ */
+
+ fp = stdin;
+ copies = 1;
+ }
+ else if ((fp = fopen(argv[6], "rb")) == NULL)
+ {
+ perror("ERROR: Unable to open print file");
+ return (1);
+ }
+ else
+ copies = atoi(argv[4]);
+
+ /*
+ * Find the URI...
+ */
+
+ if (strncmp(argv[0], "smb://", 6) == 0)
+ strncpy(uri, argv[0], sizeof(uri) - 1);
+ else if (getenv("DEVICE_URI") != NULL)
+ strncpy(uri, getenv("DEVICE_URI"), sizeof(uri) - 1);
+ else
+ {
+ fputs("ERROR: No device URI found in argv[0] or DEVICE_URI environment variable!\n", stderr);
+ return (1);
+ }
+
+ uri[sizeof(uri) - 1] = '\0';
+
+ /*
+ * Extract the destination from the URI...
+ */
+
+ if ((sep = strrchr_m(uri, '@')) != NULL)
+ {
+ username = uri + 6;
+ *sep++ = '\0';
+
+ server = sep;
+
+ /*
+ * Extract password as needed...
+ */
+
+ if ((password = strchr_m(username, ':')) != NULL)
+ *password++ = '\0';
+ else
+ password = "";
+ }
+ else
+ {
+ username = "";
+ password = "";
+ server = uri + 6;
+ }
+
+ if ((sep = strchr_m(server, '/')) == NULL)
+ {
+ fputs("ERROR: Bad URI - need printer name!\n", stderr);
+ return (1);
+ }
+
+ *sep++ = '\0';
+ printer = sep;
+
+ if ((sep = strchr_m(printer, '/')) != NULL)
+ {
+ /*
+ * Convert to smb://[username:password@]workgroup/server/printer...
+ */
+
+ *sep++ = '\0';
+
+ workgroup = server;
+ server = printer;
+ printer = sep;
+ }
+ else
+ workgroup = NULL;
+
+ /*
+ * Setup the SAMBA server state...
+ */
+
+ setup_logging("smbspool", True);
+
+ in_client = True; /* Make sure that we tell lp_load we are */
+
+ if (!lp_load(dyn_CONFIGFILE, True, False, False))
+ {
+ fprintf(stderr, "ERROR: Can't load %s - run testparm to debug it\n", dyn_CONFIGFILE);
+ return (1);
+ }
+
+ if (workgroup == NULL)
+ workgroup = lp_workgroup();
+
+ load_interfaces();
+
+ do
+ {
+ if ((cli = smb_connect(workgroup, server, printer, username, password)) == NULL)
+ {
+ if (getenv("CLASS") == NULL)
+ {
+ fprintf(stderr, "ERROR: Unable to connect to SAMBA host, will retry in 60 seconds...");
+ sleep (60);
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: Unable to connect to SAMBA host, trying next printer...");
+ return (1);
+ }
+ }
+ }
+ while (cli == NULL);
+
+ /*
+ * Now that we are connected to the server, ignore SIGTERM so that we
+ * can finish out any page data the driver sends (e.g. to eject the
+ * current page... Only ignore SIGTERM if we are printing data from
+ * stdin (otherwise you can't cancel raw jobs...)
+ */
+
+ if (argc < 7)
+ CatchSignal(SIGTERM, SIG_IGN);
+
+ /*
+ * Queue the job...
+ */
+
+ for (i = 0; i < copies; i ++)
+ if ((status = smb_print(cli, argv[3] /* title */, fp)) != 0)
+ break;
+
+ cli_shutdown(cli);
+
+ /*
+ * Return the queue status...
+ */
+
+ return (status);
+}
+
+
+/*
+ * 'list_devices()' - List the available printers seen on the network...
+ */
+
+static void
+list_devices(void)
+{
+ /*
+ * Eventually, search the local workgroup for available hosts and printers.
+ */
+
+ puts("network smb \"Unknown\" \"Windows Printer via SAMBA\"");
+}
+
+
+/*
+ * 'smb_connect()' - Return a connection to a server.
+ */
+
+static struct cli_state * /* O - SMB connection */
+smb_connect(const char *workgroup, /* I - Workgroup */
+ const char *server, /* I - Server */
+ const char *share, /* I - Printer */
+ const char *username, /* I - Username */
+ const char *password) /* I - Password */
+{
+ struct cli_state *c; /* New connection */
+ pstring myname; /* Client name */
+ NTSTATUS nt_status;
+
+ /*
+ * Get the names and addresses of the client and server...
+ */
+
+ get_myname(myname);
+
+ nt_status = cli_full_connection(&c, myname, server, NULL, 0, share, "?????",
+ username, workgroup, password, 0, Undefined, NULL);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ fprintf(stderr, "ERROR: Connection failed with error %s\n", nt_errstr(nt_status));
+ return NULL;
+ }
+
+ /*
+ * Return the new connection...
+ */
+
+ return (c);
+}
+
+
+/*
+ * 'smb_print()' - Queue a job for printing using the SMB protocol.
+ */
+
+static int /* O - 0 = success, non-0 = failure */
+smb_print(struct cli_state *cli, /* I - SMB connection */
+ char *title, /* I - Title/job name */
+ FILE *fp) /* I - File to print */
+{
+ int fnum; /* File number */
+ int nbytes, /* Number of bytes read */
+ tbytes; /* Total bytes read */
+ char buffer[8192], /* Buffer for copy */
+ *ptr; /* Pointer into tile */
+
+
+ /*
+ * Sanitize the title...
+ */
+
+ for (ptr = title; *ptr; ptr ++)
+ if (!isalnum((int)*ptr) && !isspace((int)*ptr))
+ *ptr = '_';
+
+ /*
+ * Open the printer device...
+ */
+
+ if ((fnum = cli_open(cli, title, O_RDWR | O_CREAT | O_TRUNC, DENY_NONE)) == -1)
+ {
+ fprintf(stderr, "ERROR: %s opening remote file %s\n",
+ cli_errstr(cli), title);
+ return (1);
+ }
+
+ /*
+ * Copy the file to the printer...
+ */
+
+ if (fp != stdin)
+ rewind(fp);
+
+ tbytes = 0;
+
+ while ((nbytes = fread(buffer, 1, sizeof(buffer), fp)) > 0)
+ {
+ if (cli_write(cli, fnum, 0, buffer, tbytes, nbytes) != nbytes)
+ {
+ fprintf(stderr, "ERROR: Error writing file: %s\n", cli_errstr(cli));
+ break;
+ }
+
+ tbytes += nbytes;
+ }
+
+ if (!cli_close(cli, fnum))
+ {
+ fprintf(stderr, "ERROR: %s closing remote file %s\n",
+ cli_errstr(cli), title);
+ return (1);
+ }
+ else
+ return (0);
+}
diff --git a/source/client/smbumount.c b/source/client/smbumount.c
new file mode 100644
index 00000000000..9ea3083a6f9
--- /dev/null
+++ b/source/client/smbumount.c
@@ -0,0 +1,186 @@
+/*
+ * smbumount.c
+ *
+ * Copyright (C) 1995-1998 by Volker Lendecke
+ *
+ */
+
+#include "includes.h"
+
+#include <mntent.h>
+
+#include <asm/types.h>
+#include <asm/posix_types.h>
+#include <linux/smb.h>
+#include <linux/smb_mount.h>
+#include <linux/smb_fs.h>
+
+/* This is a (hopefully) temporary hack due to the fact that
+ sizeof( uid_t ) != sizeof( __kernel_uid_t ) under glibc.
+ This may change in the future and smb.h may get fixed in the
+ future. In the mean time, it's ugly hack time - get over it.
+*/
+#undef SMB_IOC_GETMOUNTUID
+#define SMB_IOC_GETMOUNTUID _IOR('u', 1, __kernel_uid_t)
+
+#ifndef O_NOFOLLOW
+#define O_NOFOLLOW 0400000
+#endif
+
+static void
+usage(void)
+{
+ printf("usage: smbumount mountpoint\n");
+}
+
+static int
+umount_ok(const char *mount_point)
+{
+ /* we set O_NOFOLLOW to prevent users playing games with symlinks to
+ umount filesystems they don't own */
+ int fid = open(mount_point, O_RDONLY|O_NOFOLLOW, 0);
+ __kernel_uid_t mount_uid;
+
+ if (fid == -1) {
+ fprintf(stderr, "Could not open %s: %s\n",
+ mount_point, strerror(errno));
+ return -1;
+ }
+
+ if (ioctl(fid, SMB_IOC_GETMOUNTUID, &mount_uid) != 0) {
+ fprintf(stderr, "%s probably not smb-filesystem\n",
+ mount_point);
+ return -1;
+ }
+
+ if ((getuid() != 0)
+ && (mount_uid != getuid())) {
+ fprintf(stderr, "You are not allowed to umount %s\n",
+ mount_point);
+ return -1;
+ }
+
+ close(fid);
+ return 0;
+}
+
+/* Make a canonical pathname from PATH. Returns a freshly malloced string.
+ It is up the *caller* to ensure that the PATH is sensible. i.e.
+ canonicalize ("/dev/fd0/.") returns "/dev/fd0" even though ``/dev/fd0/.''
+ is not a legal pathname for ``/dev/fd0'' Anything we cannot parse
+ we return unmodified. */
+static char *
+canonicalize (char *path)
+{
+ char *canonical = malloc (PATH_MAX + 1);
+
+ if (!canonical) {
+ fprintf(stderr, "Error! Not enough memory!\n");
+ return NULL;
+ }
+
+ if (strlen(path) > PATH_MAX) {
+ fprintf(stderr, "Mount point string too long\n");
+ return NULL;
+ }
+
+ if (path == NULL)
+ return NULL;
+
+ if (realpath (path, canonical))
+ return canonical;
+
+ strncpy (canonical, path, PATH_MAX);
+ canonical[PATH_MAX] = '\0';
+ return canonical;
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ int fd;
+ char* mount_point;
+ struct mntent *mnt;
+ FILE* mtab;
+ FILE* new_mtab;
+
+ if (argc != 2) {
+ usage();
+ exit(1);
+ }
+
+ if (geteuid() != 0) {
+ fprintf(stderr, "smbumount must be installed suid root\n");
+ exit(1);
+ }
+
+ mount_point = canonicalize(argv[1]);
+
+ if (mount_point == NULL)
+ {
+ exit(1);
+ }
+
+ if (umount_ok(mount_point) != 0) {
+ exit(1);
+ }
+
+ if (umount(mount_point) != 0) {
+ fprintf(stderr, "Could not umount %s: %s\n",
+ mount_point, strerror(errno));
+ exit(1);
+ }
+
+ if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1)
+ {
+ fprintf(stderr, "Can't get "MOUNTED"~ lock file");
+ return 1;
+ }
+ close(fd);
+
+ if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
+ fprintf(stderr, "Can't open " MOUNTED ": %s\n",
+ strerror(errno));
+ return 1;
+ }
+
+#define MOUNTED_TMP MOUNTED".tmp"
+
+ if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) {
+ fprintf(stderr, "Can't open " MOUNTED_TMP ": %s\n",
+ strerror(errno));
+ endmntent(mtab);
+ return 1;
+ }
+
+ while ((mnt = getmntent(mtab)) != NULL) {
+ if (strcmp(mnt->mnt_dir, mount_point) != 0) {
+ addmntent(new_mtab, mnt);
+ }
+ }
+
+ endmntent(mtab);
+
+ if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
+ fprintf(stderr, "Error changing mode of %s: %s\n",
+ MOUNTED_TMP, strerror(errno));
+ exit(1);
+ }
+
+ endmntent(new_mtab);
+
+ if (rename(MOUNTED_TMP, MOUNTED) < 0) {
+ fprintf(stderr, "Cannot rename %s to %s: %s\n",
+ MOUNTED, MOUNTED_TMP, strerror(errno));
+ exit(1);
+ }
+
+ if (unlink(MOUNTED"~") == -1)
+ {
+ fprintf(stderr, "Can't remove "MOUNTED"~");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/source/client/tree.c b/source/client/tree.c
new file mode 100644
index 00000000000..97ad7742e31
--- /dev/null
+++ b/source/client/tree.c
@@ -0,0 +1,811 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client GTK+ tree-based application
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2001
+ Copyright (C) John Terpstra 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* example-gtk+ application, ripped off from the gtk+ tree.c sample */
+
+#include <stdio.h>
+#include <errno.h>
+#include <gtk/gtk.h>
+#include "libsmbclient.h"
+
+static GtkWidget *clist;
+
+struct tree_data {
+
+ guint32 type; /* Type of tree item, an SMBC_TYPE */
+ char name[256]; /* May need to change this later */
+
+};
+
+static void tree_error_message(gchar *message) {
+
+ GtkWidget *dialog, *label, *okay_button;
+
+ /* Create the widgets */
+
+ dialog = gtk_dialog_new();
+ gtk_window_set_modal(GTK_WINDOW(dialog), True);
+ label = gtk_label_new (message);
+ okay_button = gtk_button_new_with_label("Okay");
+
+ /* Ensure that the dialog box is destroyed when the user clicks ok. */
+
+ gtk_signal_connect_object (GTK_OBJECT (okay_button), "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy), dialog);
+ gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->action_area),
+ okay_button);
+
+ /* Add the label, and show everything we've added to the dialog. */
+
+ gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),
+ label);
+ gtk_widget_show_all (dialog);
+}
+
+/*
+ * We are given a widget, and we want to retrieve its URL so we
+ * can do a directory listing.
+ *
+ * We walk back up the tree, picking up pieces until we hit a server or
+ * workgroup type and return a path from there
+ */
+
+static pstring path_string;
+
+char *get_path(GtkWidget *item)
+{
+ GtkWidget *p = item;
+ struct tree_data *pd;
+ char *comps[1024]; /* We keep pointers to the components here */
+ int i = 0, j, level,type;
+
+ /* Walk back up the tree, getting the private data */
+
+ level = GTK_TREE(item->parent)->level;
+
+ /* Pick up this item's component info */
+
+ pd = (struct tree_data *)gtk_object_get_user_data(GTK_OBJECT(item));
+
+ comps[i++] = pd->name;
+ type = pd->type;
+
+ while (level > 0 && type != SMBC_SERVER && type != SMBC_WORKGROUP) {
+
+ /* Find the parent and extract the data etc ... */
+
+ p = GTK_WIDGET(p->parent);
+ p = GTK_WIDGET(GTK_TREE(p)->tree_owner);
+
+ pd = (struct tree_data *)gtk_object_get_user_data(GTK_OBJECT(p));
+
+ level = GTK_TREE(item->parent)->level;
+
+ comps[i++] = pd->name;
+ type = pd->type;
+
+ }
+
+ /*
+ * Got a list of comps now, should check that we did not hit a workgroup
+ * when we got other things as well ... Later
+ *
+ * Now, build the path
+ */
+
+ pstrcpy( path_string, "smb:/" );
+
+ for (j = i - 1; j >= 0; j--) {
+
+ strncat(path_string, "/", sizeof(path_string) - strlen(path_string));
+ strncat(path_string, comps[j], sizeof(path_string) - strlen(path_string));
+
+ }
+
+ fprintf(stdout, "Path string = %s\n", path_string);
+
+ return path_string;
+
+}
+
+struct tree_data *make_tree_data(guint32 type, const char *name)
+{
+ struct tree_data *p = (struct tree_data *)malloc(sizeof(struct tree_data));
+
+ if (p) {
+
+ p->type = type;
+ strncpy(p->name, name, sizeof(p->name));
+
+ }
+
+ return p;
+
+}
+
+/* Note that this is called every time the user clicks on an item,
+ whether it is already selected or not. */
+static void cb_select_child (GtkWidget *root_tree, GtkWidget *child,
+ GtkWidget *subtree)
+{
+ gint dh, err, dirlen;
+ char dirbuf[512];
+ struct smbc_dirent *dirp;
+ struct stat st1;
+ pstring path, path1;
+
+ g_print ("select_child called for root tree %p, subtree %p, child %p\n",
+ root_tree, subtree, child);
+
+ /* Now, figure out what it is, and display it in the clist ... */
+
+ gtk_clist_clear(GTK_CLIST(clist)); /* Clear the CLIST */
+
+ /* Now, get the private data for the subtree */
+
+ strncpy(path, get_path(child), 1024);
+
+ if ((dh = smbc_opendir(path)) < 0) { /* Handle error */
+
+ g_print("cb_select_child: Could not open dir %s, %s\n", path,
+ strerror(errno));
+
+ gtk_main_quit();
+
+ return;
+
+ }
+
+ while ((err = smbc_getdents(dh, (struct smbc_dirent *)dirbuf,
+ sizeof(dirbuf))) != 0) {
+
+ if (err < 0) {
+
+ g_print("cb_select_child: Could not read dir %s, %s\n", path,
+ strerror(errno));
+
+ gtk_main_quit();
+
+ return;
+
+ }
+
+ dirp = (struct smbc_dirent *)dirbuf;
+
+ while (err > 0) {
+ gchar col1[128], col2[128], col3[128], col4[128];
+ gchar *rowdata[4] = {col1, col2, col3, col4};
+
+ dirlen = dirp->dirlen;
+
+ /* Format each of the items ... */
+
+ strncpy(col1, dirp->name, 128);
+
+ col2[0] = col3[0] = col4[0] = (char)0;
+
+ switch (dirp->smbc_type) {
+
+ case SMBC_WORKGROUP:
+
+ break;
+
+ case SMBC_SERVER:
+
+ strncpy(col2, (dirp->comment?dirp->comment:""), 128);
+
+ break;
+
+ case SMBC_FILE_SHARE:
+
+ strncpy(col2, (dirp->comment?dirp->comment:""), 128);
+
+ break;
+
+ case SMBC_PRINTER_SHARE:
+
+ strncpy(col2, (dirp->comment?dirp->comment:""), 128);
+ break;
+
+ case SMBC_COMMS_SHARE:
+
+ break;
+
+ case SMBC_IPC_SHARE:
+
+ break;
+
+ case SMBC_DIR:
+ case SMBC_FILE:
+
+ /* Get stats on the file/dir and see what we have */
+
+ if ((strcmp(dirp->name, ".") != 0) &&
+ (strcmp(dirp->name, "..") != 0)) {
+
+ strncpy(path1, path, sizeof(path1));
+ strncat(path1, "/", sizeof(path) - strlen(path));
+ strncat(path1, dirp->name, sizeof(path) - strlen(path));
+
+ if (smbc_stat(path1, &st1) < 0) {
+
+ if (errno != EBUSY) {
+
+ g_print("cb_select_child: Could not stat file %s, %s\n", path1,
+ strerror(errno));
+
+ gtk_main_quit();
+
+ return;
+
+ }
+ else {
+
+ strncpy(col2, "Device or resource busy", sizeof(col2));
+
+ }
+ }
+ else {
+ /* Now format each of the relevant things ... */
+
+ snprintf(col2, sizeof(col2), "%c%c%c%c%c%c%c%c%c(%0X)",
+ (st1.st_mode&S_IRUSR?'r':'-'),
+ (st1.st_mode&S_IWUSR?'w':'-'),
+ (st1.st_mode&S_IXUSR?'x':'-'),
+ (st1.st_mode&S_IRGRP?'r':'-'),
+ (st1.st_mode&S_IWGRP?'w':'-'),
+ (st1.st_mode&S_IXGRP?'x':'-'),
+ (st1.st_mode&S_IROTH?'r':'-'),
+ (st1.st_mode&S_IWOTH?'w':'-'),
+ (st1.st_mode&S_IXOTH?'x':'-'),
+ st1.st_mode);
+ snprintf(col3, sizeof(col3), "%u", st1.st_size);
+ snprintf(col4, sizeof(col4), "%s", ctime(&st1.st_mtime));
+ }
+ }
+
+ break;
+
+ default:
+
+ break;
+ }
+
+ gtk_clist_append(GTK_CLIST(clist), rowdata);
+
+ (char *)dirp += dirlen;
+ err -= dirlen;
+
+ }
+
+ }
+
+}
+
+/* Note that this is never called */
+static void cb_unselect_child( GtkWidget *root_tree,
+ GtkWidget *child,
+ GtkWidget *subtree )
+{
+ g_print ("unselect_child called for root tree %p, subtree %p, child %p\n",
+ root_tree, subtree, child);
+}
+
+/* for all the GtkItem:: and GtkTreeItem:: signals */
+static void cb_itemsignal( GtkWidget *item,
+ gchar *signame )
+{
+ GtkWidget *real_tree, *aitem, *subtree;
+ gchar *name;
+ GtkLabel *label;
+ gint dh, err, dirlen, level;
+ char dirbuf[512];
+ struct smbc_dirent *dirp;
+
+ label = GTK_LABEL (GTK_BIN (item)->child);
+ /* Get the text of the label */
+ gtk_label_get (label, &name);
+
+ level = GTK_TREE(item->parent)->level;
+
+ /* Get the level of the tree which the item is in */
+ g_print ("%s called for item %s->%p, level %d\n", signame, name,
+ item, GTK_TREE (item->parent)->level);
+
+ real_tree = GTK_TREE_ITEM_SUBTREE(item); /* Get the subtree */
+
+ if (strncmp(signame, "expand", 6) == 0) { /* Expand called */
+ char server[128];
+
+ if ((dh = smbc_opendir(get_path(item))) < 0) { /* Handle error */
+ gchar errmsg[256];
+
+ g_print("cb_itemsignal: Could not open dir %s, %s\n", get_path(item),
+ strerror(errno));
+
+ slprintf(errmsg, sizeof(errmsg), "cb_itemsignal: Could not open dir %s, %s\n", get_path(item), strerror(errno));
+
+ tree_error_message(errmsg);
+
+ /* gtk_main_quit();*/
+
+ return;
+
+ }
+
+ while ((err = smbc_getdents(dh, (struct smbc_dirent *)dirbuf,
+ sizeof(dirbuf))) != 0) {
+
+ if (err < 0) { /* An error, report it */
+ gchar errmsg[256];
+
+ g_print("cb_itemsignal: Could not read dir smbc://, %s\n",
+ strerror(errno));
+
+ slprintf(errmsg, sizeof(errmsg), "cb_itemsignal: Could not read dir smbc://, %s\n", strerror(errno));
+
+ tree_error_message(errmsg);
+
+ /* gtk_main_quit();*/
+
+ return;
+
+ }
+
+ dirp = (struct smbc_dirent *)dirbuf;
+
+ while (err > 0) {
+ struct tree_data *my_data;
+
+ dirlen = dirp->dirlen;
+
+ my_data = make_tree_data(dirp->smbc_type, dirp->name);
+
+ if (!my_data) {
+
+ g_print("Could not allocate space for tree_data: %s\n",
+ dirp->name);
+
+ gtk_main_quit();
+ return;
+
+ }
+
+ aitem = gtk_tree_item_new_with_label(dirp->name);
+
+ /* Connect all GtkItem:: and GtkTreeItem:: signals */
+ gtk_signal_connect (GTK_OBJECT(aitem), "select",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "select");
+ gtk_signal_connect (GTK_OBJECT(aitem), "deselect",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "deselect");
+ gtk_signal_connect (GTK_OBJECT(aitem), "toggle",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "toggle");
+ gtk_signal_connect (GTK_OBJECT(aitem), "expand",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "expand");
+ gtk_signal_connect (GTK_OBJECT(aitem), "collapse",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "collapse");
+ /* Add it to the parent tree */
+ gtk_tree_append (GTK_TREE(real_tree), aitem);
+
+ gtk_widget_show (aitem);
+
+ gtk_object_set_user_data(GTK_OBJECT(aitem), (gpointer)my_data);
+
+ fprintf(stdout, "Added: %s, len: %u\n", dirp->name, dirlen);
+
+ if (dirp->smbc_type != SMBC_FILE &&
+ dirp->smbc_type != SMBC_IPC_SHARE &&
+ (strcmp(dirp->name, ".") != 0) &&
+ (strcmp(dirp->name, "..") !=0)){
+
+ subtree = gtk_tree_new();
+ gtk_tree_item_set_subtree(GTK_TREE_ITEM(aitem), subtree);
+
+ gtk_signal_connect(GTK_OBJECT(subtree), "select_child",
+ GTK_SIGNAL_FUNC(cb_select_child), real_tree);
+ gtk_signal_connect(GTK_OBJECT(subtree), "unselect_child",
+ GTK_SIGNAL_FUNC(cb_unselect_child), real_tree);
+
+ }
+
+ (char *)dirp += dirlen;
+ err -= dirlen;
+
+ }
+
+ }
+
+ smbc_closedir(dh);
+
+ }
+ else if (strncmp(signame, "collapse", 8) == 0) {
+ GtkWidget *subtree = gtk_tree_new();
+
+ gtk_tree_remove_items(GTK_TREE(real_tree), GTK_TREE(real_tree)->children);
+
+ gtk_tree_item_set_subtree(GTK_TREE_ITEM(item), subtree);
+
+ gtk_signal_connect (GTK_OBJECT(subtree), "select_child",
+ GTK_SIGNAL_FUNC(cb_select_child), real_tree);
+ gtk_signal_connect (GTK_OBJECT(subtree), "unselect_child",
+ GTK_SIGNAL_FUNC(cb_unselect_child), real_tree);
+
+ }
+
+}
+
+static void cb_selection_changed( GtkWidget *tree )
+{
+ GList *i;
+
+ g_print ("selection_change called for tree %p\n", tree);
+ g_print ("selected objects are:\n");
+
+ i = GTK_TREE_SELECTION(tree);
+ while (i){
+ gchar *name;
+ GtkLabel *label;
+ GtkWidget *item;
+
+ /* Get a GtkWidget pointer from the list node */
+ item = GTK_WIDGET (i->data);
+ label = GTK_LABEL (GTK_BIN (item)->child);
+ gtk_label_get (label, &name);
+ g_print ("\t%s on level %d\n", name, GTK_TREE
+ (item->parent)->level);
+ i = i->next;
+ }
+}
+
+/*
+ * Expand or collapse the whole network ...
+ */
+static void cb_wholenet(GtkWidget *item, gchar *signame)
+{
+ GtkWidget *real_tree, *aitem, *subtree;
+ gchar *name;
+ GtkLabel *label;
+ gint dh, err, dirlen;
+ char dirbuf[512];
+ struct smbc_dirent *dirp;
+
+ label = GTK_LABEL (GTK_BIN (item)->child);
+ gtk_label_get (label, &name);
+ g_print ("%s called for item %s->%p, level %d\n", signame, name,
+ item, GTK_TREE (item->parent)->level);
+
+ real_tree = GTK_TREE_ITEM_SUBTREE(item); /* Get the subtree */
+
+ if (strncmp(signame, "expand", 6) == 0) { /* Expand called */
+
+ if ((dh = smbc_opendir("smb://")) < 0) { /* Handle error */
+
+ g_print("cb_wholenet: Could not open dir smbc://, %s\n",
+ strerror(errno));
+
+ gtk_main_quit();
+
+ return;
+
+ }
+
+ while ((err = smbc_getdents(dh, (struct smbc_dirent *)dirbuf,
+ sizeof(dirbuf))) != 0) {
+
+ if (err < 0) { /* An error, report it */
+
+ g_print("cb_wholenet: Could not read dir smbc://, %s\n",
+ strerror(errno));
+
+ gtk_main_quit();
+
+ return;
+
+ }
+
+ dirp = (struct smbc_dirent *)dirbuf;
+
+ while (err > 0) {
+ struct tree_data *my_data;
+
+ dirlen = dirp->dirlen;
+
+ my_data = make_tree_data(dirp->smbc_type, dirp->name);
+
+ aitem = gtk_tree_item_new_with_label(dirp->name);
+
+ /* Connect all GtkItem:: and GtkTreeItem:: signals */
+ gtk_signal_connect (GTK_OBJECT(aitem), "select",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "select");
+ gtk_signal_connect (GTK_OBJECT(aitem), "deselect",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "deselect");
+ gtk_signal_connect (GTK_OBJECT(aitem), "toggle",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "toggle");
+ gtk_signal_connect (GTK_OBJECT(aitem), "expand",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "expand");
+ gtk_signal_connect (GTK_OBJECT(aitem), "collapse",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "collapse");
+
+ gtk_tree_append (GTK_TREE(real_tree), aitem);
+ /* Show it - this can be done at any time */
+ gtk_widget_show (aitem);
+
+ gtk_object_set_user_data(GTK_OBJECT(aitem), (gpointer)my_data);
+
+ fprintf(stdout, "Added: %s, len: %u\n", dirp->name, dirlen);
+
+ subtree = gtk_tree_new();
+
+ gtk_tree_item_set_subtree(GTK_TREE_ITEM(aitem), subtree);
+
+ gtk_signal_connect(GTK_OBJECT(subtree), "select_child",
+ GTK_SIGNAL_FUNC(cb_select_child), real_tree);
+ gtk_signal_connect(GTK_OBJECT(subtree), "unselect_child",
+ GTK_SIGNAL_FUNC(cb_unselect_child), real_tree);
+
+ (char *)dirp += dirlen;
+ err -= dirlen;
+
+ }
+
+ }
+
+ smbc_closedir(dh);
+
+ }
+ else { /* Must be collapse ... FIXME ... */
+ GtkWidget *subtree = gtk_tree_new();
+
+ gtk_tree_remove_items(GTK_TREE(real_tree), GTK_TREE(real_tree)->children);
+
+ gtk_tree_item_set_subtree(GTK_TREE_ITEM(item), subtree);
+
+ gtk_signal_connect (GTK_OBJECT(subtree), "select_child",
+ GTK_SIGNAL_FUNC(cb_select_child), real_tree);
+ gtk_signal_connect (GTK_OBJECT(subtree), "unselect_child",
+ GTK_SIGNAL_FUNC(cb_unselect_child), real_tree);
+
+
+ }
+
+}
+
+/* Should put up a dialog box to ask the user for username and password */
+
+static void
+auth_fn(const char *server, const char *share,
+ char *workgroup, int wgmaxlen, char *username, int unmaxlen,
+ char *password, int pwmaxlen)
+{
+
+ strncpy(username, "test", unmaxlen);
+ strncpy(password, "test", pwmaxlen);
+
+}
+
+static char *col_titles[] = {
+ "Name", "Attributes", "Size", "Modification Date",
+};
+
+int main( int argc,
+ char *argv[] )
+{
+ GtkWidget *window, *scrolled_win, *scrolled_win2, *tree;
+ GtkWidget *subtree, *item, *main_hbox, *r_pane, *l_pane;
+ gint err, dh;
+ gint i;
+ char dirbuf[512];
+ struct smbc_dirent *dirp;
+
+ gtk_init (&argc, &argv);
+
+ /* Init the smbclient library */
+
+ err = smbc_init(auth_fn, 10);
+
+ /* Print an error response ... */
+
+ if (err < 0) {
+
+ fprintf(stderr, "smbc_init returned %s (%i)\nDo you have a ~/.smb/smb.conf file?\n", strerror(errno), errno);
+ exit(1);
+
+ }
+
+ /* a generic toplevel window */
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_name(window, "main browser window");
+ gtk_signal_connect (GTK_OBJECT(window), "delete_event",
+ GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
+ gtk_window_set_title(GTK_WINDOW(window), "The Linux Windows Network Browser");
+ gtk_widget_set_usize(GTK_WIDGET(window), 750, -1);
+ gtk_container_set_border_width (GTK_CONTAINER(window), 5);
+
+ gtk_widget_show (window);
+
+ /* A container for the two panes ... */
+
+ main_hbox = gtk_hbox_new(FALSE, 1);
+ gtk_container_border_width(GTK_CONTAINER(main_hbox), 1);
+ gtk_container_add(GTK_CONTAINER(window), main_hbox);
+
+ gtk_widget_show(main_hbox);
+
+ l_pane = gtk_hpaned_new();
+ gtk_paned_gutter_size(GTK_PANED(l_pane), (GTK_PANED(l_pane))->handle_size);
+ r_pane = gtk_hpaned_new();
+ gtk_paned_gutter_size(GTK_PANED(r_pane), (GTK_PANED(r_pane))->handle_size);
+ gtk_container_add(GTK_CONTAINER(main_hbox), l_pane);
+ gtk_widget_show(l_pane);
+
+ /* A generic scrolled window */
+ scrolled_win = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_widget_set_usize (scrolled_win, 150, 200);
+ gtk_container_add (GTK_CONTAINER(l_pane), scrolled_win);
+ gtk_widget_show (scrolled_win);
+
+ /* Another generic scrolled window */
+ scrolled_win2 = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win2),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_widget_set_usize (scrolled_win2, 150, 200);
+ gtk_paned_add2 (GTK_PANED(l_pane), scrolled_win2);
+ gtk_widget_show (scrolled_win2);
+
+ /* Create the root tree */
+ tree = gtk_tree_new();
+ g_print ("root tree is %p\n", tree);
+ /* connect all GtkTree:: signals */
+ gtk_signal_connect (GTK_OBJECT(tree), "select_child",
+ GTK_SIGNAL_FUNC(cb_select_child), tree);
+ gtk_signal_connect (GTK_OBJECT(tree), "unselect_child",
+ GTK_SIGNAL_FUNC(cb_unselect_child), tree);
+ gtk_signal_connect (GTK_OBJECT(tree), "selection_changed",
+ GTK_SIGNAL_FUNC(cb_selection_changed), tree);
+ /* Add it to the scrolled window */
+ gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(scrolled_win),
+ tree);
+ /* Set the selection mode */
+ gtk_tree_set_selection_mode (GTK_TREE(tree),
+ GTK_SELECTION_MULTIPLE);
+ /* Show it */
+ gtk_widget_show (tree);
+
+ /* Now, create a clist and attach it to the second pane */
+
+ clist = gtk_clist_new_with_titles(4, col_titles);
+
+ gtk_container_add (GTK_CONTAINER(scrolled_win2), clist);
+
+ gtk_widget_show(clist);
+
+ /* Now, build the top level display ... */
+
+ if ((dh = smbc_opendir("smb:///")) < 0) {
+
+ fprintf(stderr, "Could not list default workgroup: smb:///: %s\n",
+ strerror(errno));
+
+ exit(1);
+
+ }
+
+ /* Create a tree item for Whole Network */
+
+ item = gtk_tree_item_new_with_label ("Whole Network");
+ /* Connect all GtkItem:: and GtkTreeItem:: signals */
+ gtk_signal_connect (GTK_OBJECT(item), "select",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "select");
+ gtk_signal_connect (GTK_OBJECT(item), "deselect",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "deselect");
+ gtk_signal_connect (GTK_OBJECT(item), "toggle",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "toggle");
+ gtk_signal_connect (GTK_OBJECT(item), "expand",
+ GTK_SIGNAL_FUNC(cb_wholenet), "expand");
+ gtk_signal_connect (GTK_OBJECT(item), "collapse",
+ GTK_SIGNAL_FUNC(cb_wholenet), "collapse");
+ /* Add it to the parent tree */
+ gtk_tree_append (GTK_TREE(tree), item);
+ /* Show it - this can be done at any time */
+ gtk_widget_show (item);
+
+ subtree = gtk_tree_new(); /* A subtree for Whole Network */
+
+ gtk_tree_item_set_subtree(GTK_TREE_ITEM(item), subtree);
+
+ gtk_signal_connect (GTK_OBJECT(subtree), "select_child",
+ GTK_SIGNAL_FUNC(cb_select_child), tree);
+ gtk_signal_connect (GTK_OBJECT(subtree), "unselect_child",
+ GTK_SIGNAL_FUNC(cb_unselect_child), tree);
+
+ /* Now, get the items in smb:/// and add them to the tree */
+
+ dirp = (struct smbc_dirent *)dirbuf;
+
+ while ((err = smbc_getdents(dh, (struct smbc_dirent *)dirbuf,
+ sizeof(dirbuf))) != 0) {
+
+ if (err < 0) { /* Handle the error */
+
+ fprintf(stderr, "Could not read directory for smbc:///: %s\n",
+ strerror(errno));
+
+ exit(1);
+
+ }
+
+ fprintf(stdout, "Dir len: %u\n", err);
+
+ while (err > 0) { /* Extract each entry and make a sub-tree */
+ struct tree_data *my_data;
+ int dirlen = dirp->dirlen;
+
+ my_data = make_tree_data(dirp->smbc_type, dirp->name);
+
+ item = gtk_tree_item_new_with_label(dirp->name);
+ /* Connect all GtkItem:: and GtkTreeItem:: signals */
+ gtk_signal_connect (GTK_OBJECT(item), "select",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "select");
+ gtk_signal_connect (GTK_OBJECT(item), "deselect",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "deselect");
+ gtk_signal_connect (GTK_OBJECT(item), "toggle",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "toggle");
+ gtk_signal_connect (GTK_OBJECT(item), "expand",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "expand");
+ gtk_signal_connect (GTK_OBJECT(item), "collapse",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "collapse");
+ /* Add it to the parent tree */
+ gtk_tree_append (GTK_TREE(tree), item);
+ /* Show it - this can be done at any time */
+ gtk_widget_show (item);
+
+ gtk_object_set_user_data(GTK_OBJECT(item), (gpointer)my_data);
+
+ fprintf(stdout, "Added: %s, len: %u\n", dirp->name, dirlen);
+
+ subtree = gtk_tree_new();
+
+ gtk_tree_item_set_subtree(GTK_TREE_ITEM(item), subtree);
+
+ gtk_signal_connect (GTK_OBJECT(subtree), "select_child",
+ GTK_SIGNAL_FUNC(cb_select_child), tree);
+ gtk_signal_connect (GTK_OBJECT(subtree), "unselect_child",
+ GTK_SIGNAL_FUNC(cb_unselect_child), tree);
+
+ (char *)dirp += dirlen;
+ err -= dirlen;
+
+ }
+
+ }
+
+ smbc_closedir(dh); /* FIXME, check for error :-) */
+
+ /* Show the window and loop endlessly */
+ gtk_main();
+ return 0;
+}
+/* example-end */
diff --git a/source/codepages/.cvsignore b/source/codepages/.cvsignore
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/source/codepages/.cvsignore
diff --git a/source/codepages/lowcase.dat b/source/codepages/lowcase.dat
new file mode 100644
index 00000000000..62b6e2e952b
--- /dev/null
+++ b/source/codepages/lowcase.dat
Binary files differ
diff --git a/source/codepages/upcase.dat b/source/codepages/upcase.dat
new file mode 100644
index 00000000000..bb6f9beb4e3
--- /dev/null
+++ b/source/codepages/upcase.dat
Binary files differ
diff --git a/source/codepages/valid.dat b/source/codepages/valid.dat
new file mode 100644
index 00000000000..78c14b33f0f
--- /dev/null
+++ b/source/codepages/valid.dat
Binary files differ
diff --git a/source/config.guess b/source/config.guess
new file mode 100755
index 00000000000..78f6b92cd30
--- /dev/null
+++ b/source/config.guess
@@ -0,0 +1,1409 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+timestamp='2003-01-10'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Per Bothner <per@bothner.com>.
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub. If it succeeds, it prints the system name on stdout, and
+# exits with 0. Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit 0 ;;
+ --version | -v )
+ echo "$version" ; exit 0 ;;
+ --help | --h* | -h )
+ echo "$usage"; exit 0 ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+ * )
+ break ;;
+ esac
+done
+
+if test $# != 0; then
+ echo "$me: too many arguments$help" >&2
+ exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,) echo "int x;" > $dummy.c ;
+ for c in cc gcc c89 c99 ; do
+ if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+ CC_FOR_BUILD="$c"; break ;
+ fi ;
+ done ;
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found ;
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+esac ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ *:NetBSD:*:*)
+ # NetBSD (nbsd) targets should (where applicable) match one or
+ # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+ # compatibility and a consistent mechanism for selecting the
+ # object file format.
+ #
+ # Note: NetBSD doesn't particularly care about the vendor
+ # portion of the name. We always set it to "unknown".
+ sysctl="sysctl -n hw.machine_arch"
+ UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+ /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+ case "${UNAME_MACHINE_ARCH}" in
+ armeb) machine=armeb-unknown ;;
+ arm*) machine=arm-unknown ;;
+ sh3el) machine=shl-unknown ;;
+ sh3eb) machine=sh-unknown ;;
+ *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+ esac
+ # The Operating System including object format, if it has switched
+ # to ELF recently, or will in the future.
+ case "${UNAME_MACHINE_ARCH}" in
+ arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+ eval $set_cc_for_build
+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep __ELF__ >/dev/null
+ then
+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+ # Return netbsd for either. FIX?
+ os=netbsd
+ else
+ os=netbsdelf
+ fi
+ ;;
+ *)
+ os=netbsd
+ ;;
+ esac
+ # The OS release
+ # Debian GNU/NetBSD machines have a different userland, and
+ # thus, need a distinct triplet. However, they do not need
+ # kernel version information, so it can be replaced with a
+ # suitable tag, in the style of linux-gnu.
+ case "${UNAME_VERSION}" in
+ Debian*)
+ release='-gnu'
+ ;;
+ *)
+ release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ ;;
+ esac
+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+ # contains redundant information, the shorter form:
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "${machine}-${os}${release}"
+ exit 0 ;;
+ amiga:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ arc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ hp300:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mac68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ macppc:OpenBSD:*:*)
+ echo powerpc-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme88k:OpenBSD:*:*)
+ echo m88k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvmeppc:OpenBSD:*:*)
+ echo powerpc-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ pmax:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sgi:OpenBSD:*:*)
+ echo mipseb-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sun3:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ wgrisc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:OpenBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:MicroBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-microbsd${UNAME_RELEASE}
+ exit 0 ;;
+ alpha:OSF1:*:*)
+ if test $UNAME_RELEASE = "V4.0"; then
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ fi
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ eval $set_cc_for_build
+ cat <<EOF >$dummy.s
+ .data
+\$Lformat:
+ .byte 37,100,45,37,120,10,0 # "%d-%x\n"
+
+ .text
+ .globl main
+ .align 4
+ .ent main
+main:
+ .frame \$30,16,\$26,0
+ ldgp \$29,0(\$27)
+ .prologue 1
+ .long 0x47e03d80 # implver \$0
+ lda \$2,-1
+ .long 0x47e20c21 # amask \$2,\$1
+ lda \$16,\$Lformat
+ mov \$0,\$17
+ not \$1,\$18
+ jsr \$26,printf
+ ldgp \$29,0(\$26)
+ mov 0,\$16
+ jsr \$26,exit
+ .end main
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.s 2>/dev/null
+ if test "$?" = 0 ; then
+ case `$dummy` in
+ 0-0)
+ UNAME_MACHINE="alpha"
+ ;;
+ 1-0)
+ UNAME_MACHINE="alphaev5"
+ ;;
+ 1-1)
+ UNAME_MACHINE="alphaev56"
+ ;;
+ 1-101)
+ UNAME_MACHINE="alphapca56"
+ ;;
+ 2-303)
+ UNAME_MACHINE="alphaev6"
+ ;;
+ 2-307)
+ UNAME_MACHINE="alphaev67"
+ ;;
+ 2-1307)
+ UNAME_MACHINE="alphaev68"
+ ;;
+ 3-1307)
+ UNAME_MACHINE="alphaev7"
+ ;;
+ esac
+ fi
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ exit 0 ;;
+ Alpha\ *:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # Should we change UNAME_MACHINE based on the output of uname instead
+ # of the specific Alpha model?
+ echo alpha-pc-interix
+ exit 0 ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit 0 ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-unknown-sysv4
+ exit 0;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-amigaos
+ exit 0 ;;
+ *:[Mm]orph[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-morphos
+ exit 0 ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit 0 ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit 0;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit 0;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit 0 ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit 0 ;;
+ DRS?6000:UNIX_SV:4.2*:7*)
+ case `/usr/bin/uname -p` in
+ sparc) echo sparc-icl-nx7 && exit 0 ;;
+ esac ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ i86pc:SunOS:5.*:*)
+ echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ exit 0 ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ ;;
+ sun4)
+ echo sparc-sun-sunos${UNAME_RELEASE}
+ ;;
+ esac
+ exit 0 ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit 0 ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit 0 ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit 0 ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit 0 ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix${UNAME_RELEASE}
+ exit 0 ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c \
+ && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+ && exit 0
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit 0 ;;
+ Motorola:PowerMAX_OS:*:*)
+ echo powerpc-motorola-powermax
+ exit 0 ;;
+ Motorola:*:4.3:PL8-*)
+ echo powerpc-harris-powermax
+ exit 0 ;;
+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+ echo powerpc-harris-powermax
+ exit 0 ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit 0 ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit 0 ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit 0 ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+ then
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+ [ ${TARGET_BINARY_INTERFACE}x = x ]
+ then
+ echo m88k-dg-dgux${UNAME_RELEASE}
+ else
+ echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ fi
+ else
+ echo i586-dg-dgux${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit 0 ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit 0 ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit 0 ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit 0 ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i*86:AIX:*:*)
+ echo i386-ibm-aix
+ exit 0 ;;
+ ia64:AIX:*:*)
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+ exit 0 ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
+ echo rs6000-ibm-aix3.2.5
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit 0 ;;
+ *:AIX:*:[45])
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ exit 0 ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit 0 ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit 0 ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit 0 ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit 0 ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit 0 ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit 0 ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit 0 ;;
+ 9000/[34678]??:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ case "${UNAME_MACHINE}" in
+ 9000/31? ) HP_ARCH=m68000 ;;
+ 9000/[34]?? ) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ if [ -x /usr/bin/getconf ]; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case "${sc_cpu_version}" in
+ 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "${sc_kernel_bits}" in
+ 32) HP_ARCH="hppa2.0n" ;;
+ 64) HP_ARCH="hppa2.0w" ;;
+ '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
+ esac ;;
+ esac
+ fi
+ if [ "${HP_ARCH}" = "" ]; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+
+ #define _HPUX_SOURCE
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+ test -z "$HP_ARCH" && HP_ARCH=hppa
+ fi ;;
+ esac
+ if [ ${HP_ARCH} = "hppa2.0w" ]
+ then
+ # avoid double evaluation of $set_cc_for_build
+ test -n "$CC_FOR_BUILD" || eval $set_cc_for_build
+ if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null
+ then
+ HP_ARCH="hppa2.0w"
+ else
+ HP_ARCH="hppa64"
+ fi
+ fi
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ia64-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ 3050*:HI-UX:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
+ echo unknown-hitachi-hiuxwe2
+ exit 0 ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit 0 ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit 0 ;;
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit 0 ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit 0 ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit 0 ;;
+ i*86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo ${UNAME_MACHINE}-unknown-osf1mk
+ else
+ echo ${UNAME_MACHINE}-unknown-osf1
+ fi
+ exit 0 ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit 0 ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+ -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*T3E:*:*:*)
+ echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*SV1:*:*:*)
+ echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ *:UNICOS/mp:*:*)
+ echo nv1-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit 0 ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:FreeBSD:*:*)
+ # Determine whether the default compiler uses glibc.
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <features.h>
+ #if __GLIBC__ >= 2
+ LIBC=gnu
+ #else
+ LIBC=
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
+ echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`${LIBC:+-$LIBC}
+ exit 0 ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit 0 ;;
+ i*:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit 0 ;;
+ i*:PW*:*)
+ echo ${UNAME_MACHINE}-pc-pw32
+ exit 0 ;;
+ x86:Interix*:3*)
+ echo i586-pc-interix3
+ exit 0 ;;
+ [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+ echo i${UNAME_MACHINE}-pc-mks
+ exit 0 ;;
+ i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+ # UNAME_MACHINE based on the output of uname instead of i386?
+ echo i586-pc-interix
+ exit 0 ;;
+ i*:UWIN*:*)
+ echo ${UNAME_MACHINE}-pc-uwin
+ exit 0 ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-unknown-cygwin
+ exit 0 ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ *:GNU:*:*)
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit 0 ;;
+ i*86:Minix:*:*)
+ echo ${UNAME_MACHINE}-pc-minix
+ exit 0 ;;
+ arm*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ ia64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ m68*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ mips:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips
+ #undef mipsel
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mipsel
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+ test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
+ ;;
+ mips64:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips64
+ #undef mips64el
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mips64el
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips64
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+ test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
+ ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-gnu
+ exit 0 ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-gnu
+ exit 0 ;;
+ alpha:Linux:*:*)
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+ if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+ exit 0 ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+ PA7*) echo hppa1.1-unknown-linux-gnu ;;
+ PA8*) echo hppa2.0-unknown-linux-gnu ;;
+ *) echo hppa-unknown-linux-gnu ;;
+ esac
+ exit 0 ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-gnu
+ exit 0 ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo ${UNAME_MACHINE}-ibm-linux
+ exit 0 ;;
+ sh*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ x86_64:Linux:*:*)
+ echo x86_64-unknown-linux-gnu
+ exit 0 ;;
+ i*86:Linux:*:*)
+ # The BFD linker knows what the default object file format is, so
+ # first see if it will tell us. cd to the root directory to prevent
+ # problems with other programs or directories called `ld' in the path.
+ # Set LC_ALL=C to ensure ld outputs messages in English.
+ ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
+ | sed -ne '/supported targets:/!d
+ s/[ ][ ]*/ /g
+ s/.*supported targets: *//
+ s/ .*//
+ p'`
+ case "$ld_supported_targets" in
+ elf32-i386)
+ TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
+ ;;
+ a.out-i386-linux)
+ echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+ exit 0 ;;
+ coff-i386)
+ echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+ exit 0 ;;
+ "")
+ # Either a pre-BFD a.out linker (linux-gnuoldld) or
+ # one that does not give us useful --help.
+ echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
+ exit 0 ;;
+ esac
+ # Determine whether the default compiler is a.out or elf
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <features.h>
+ #ifdef __ELF__
+ # ifdef __GLIBC__
+ # if __GLIBC__ >= 2
+ LIBC=gnu
+ # else
+ LIBC=gnulibc1
+ # endif
+ # else
+ LIBC=gnulibc1
+ # endif
+ #else
+ #ifdef __INTEL_COMPILER
+ LIBC=gnu
+ #else
+ LIBC=gnuaout
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
+ test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0
+ test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0
+ ;;
+ i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+ # earlier versions are messed up and put the nodename in both
+ # sysname and nodename.
+ echo i386-sequent-sysv4
+ exit 0 ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ exit 0 ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ echo ${UNAME_MACHINE}-pc-os2-emx
+ exit 0 ;;
+ i*86:XTS-300:*:STOP)
+ echo ${UNAME_MACHINE}-unknown-stop
+ exit 0 ;;
+ i*86:atheos:*:*)
+ echo ${UNAME_MACHINE}-unknown-atheos
+ exit 0 ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ i*86:*DOS:*:*)
+ echo ${UNAME_MACHINE}-pc-msdosdjgpp
+ exit 0 ;;
+ i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+ UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+ fi
+ exit 0 ;;
+ i*86:*:5:[78]*)
+ case `/bin/uname -X | grep "^Machine"` in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+ esac
+ echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+ exit 0 ;;
+ i*86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ else
+ echo ${UNAME_MACHINE}-pc-sysv32
+ fi
+ exit 0 ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i386.
+ echo i386-pc-msdosdjgpp
+ exit 0 ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit 0 ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit 0 ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ fi
+ exit 0 ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit 0 ;;
+ mc68k:UNIX:SYSTEM5:3.51m)
+ echo m68k-convergent-sysv
+ exit 0 ;;
+ M680?0:D-NIX:5.3:*)
+ echo m68k-diab-dnix
+ exit 0 ;;
+ M68*:*:R3V[567]*:*)
+ test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+ 3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4.3${OS_REL} && exit 0
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4 && exit 0 ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ echo m68k-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit 0 ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ rs6000:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+ echo powerpc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv${UNAME_RELEASE}
+ exit 0 ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo ${UNAME_MACHINE}-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit 0 ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit 0 ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit 0 ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit 0 ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo hppa1.1-stratus-vos
+ exit 0 ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit 0 ;;
+ news*:NEWS-OS:6*:*)
+ echo mips-sony-newsos6
+ exit 0 ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+ echo mips-unknown-sysv${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit 0 ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit 0 ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit 0 ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ SX-6:SUPER-UX:*:*)
+ echo sx6-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:Rhapsody:*:*)
+ echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:Darwin:*:*)
+ case `uname -p` in
+ *86) UNAME_PROCESSOR=i686 ;;
+ powerpc) UNAME_PROCESSOR=powerpc ;;
+ esac
+ echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+ exit 0 ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+ UNAME_PROCESSOR=`uname -p`
+ if test "$UNAME_PROCESSOR" = "x86"; then
+ UNAME_PROCESSOR=i386
+ UNAME_MACHINE=pc
+ fi
+ echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+ exit 0 ;;
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit 0 ;;
+ NSR-[DGKLNPTVW]:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk${UNAME_RELEASE}
+ exit 0 ;;
+ *:NonStop-UX:*:*)
+ echo mips-compaq-nonstopux
+ exit 0 ;;
+ BS2000:POSIX*:*:*)
+ echo bs2000-siemens-sysv
+ exit 0 ;;
+ DS/*:UNIX_System_V:*:*)
+ echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+ exit 0 ;;
+ *:Plan9:*:*)
+ # "uname -m" is not consistent, so use $cputype instead. 386
+ # is converted to i386 for consistency with other x86
+ # operating systems.
+ if test "$cputype" = "386"; then
+ UNAME_MACHINE=i386
+ else
+ UNAME_MACHINE="$cputype"
+ fi
+ echo ${UNAME_MACHINE}-unknown-plan9
+ exit 0 ;;
+ *:TOPS-10:*:*)
+ echo pdp10-unknown-tops10
+ exit 0 ;;
+ *:TENEX:*:*)
+ echo pdp10-unknown-tenex
+ exit 0 ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ echo pdp10-dec-tops20
+ exit 0 ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ echo pdp10-xkl-tops20
+ exit 0 ;;
+ *:TOPS-20:*:*)
+ echo pdp10-unknown-tops20
+ exit 0 ;;
+ *:ITS:*:*)
+ echo pdp10-unknown-its
+ exit 0 ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+ printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+ printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+# include <sys/param.h>
+# if defined (BSD)
+# if BSD == 43
+ printf ("vax-dec-bsd4.3\n"); exit (0);
+# else
+# if BSD == 199006
+ printf ("vax-dec-bsd4.3reno\n"); exit (0);
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# endif
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# else
+ printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+ case `getsysinfo -f cpu_type` in
+ c1*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ c34*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ c38*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ c4*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+ ftp://ftp.gnu.org/pub/gnu/config/
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo = `(hostinfo) 2>/dev/null`
+/bin/universe = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/source/config.sub b/source/config.sub
new file mode 100755
index 00000000000..2476310dff3
--- /dev/null
+++ b/source/config.sub
@@ -0,0 +1,1421 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+# Free Software Foundation, Inc.
+
+timestamp='2001-12-03'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine. It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+ $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit 0 ;;
+ --version | -v )
+ echo "$version" ; exit 0 ;;
+ --help | --h* | -h )
+ echo "$usage"; exit 0 ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help"
+ exit 1 ;;
+
+ *local*)
+ # First pass through any local machine types.
+ echo $1
+ exit 0;;
+
+ * )
+ break ;;
+ esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+ exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+ exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+ nto-qnx* | linux-gnu* | storm-chaos* | os2-emx* | windows32-*)
+ os=-$maybe_os
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
+ *)
+ basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+ if [ $basic_machine != $1 ]
+ then os=`echo $1 | sed 's/.*-/-/'`
+ else os=; fi
+ ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work. We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+ -sun*os*)
+ # Prevent following clause from handling this invalid input.
+ ;;
+ -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+ -apple | -axis)
+ os=
+ basic_machine=$1
+ ;;
+ -sim | -cisco | -oki | -wec | -winbond)
+ os=
+ basic_machine=$1
+ ;;
+ -scout)
+ ;;
+ -wrs)
+ os=-vxworks
+ basic_machine=$1
+ ;;
+ -chorusos*)
+ os=-chorusos
+ basic_machine=$1
+ ;;
+ -chorusrdb)
+ os=-chorusrdb
+ basic_machine=$1
+ ;;
+ -hiux*)
+ os=-hiuxwe2
+ ;;
+ -sco5)
+ os=-sco3.2v5
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco4)
+ os=-sco3.2v4
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2v[4-9]*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco*)
+ os=-sco3.2v2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -udk*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -isc)
+ os=-isc2.2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -clix*)
+ basic_machine=clipper-intergraph
+ ;;
+ -isc*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -lynx*)
+ os=-lynxos
+ ;;
+ -ptx*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+ ;;
+ -windowsnt*)
+ os=`echo $os | sed -e 's/windowsnt/winnt/'`
+ ;;
+ -psos*)
+ os=-psos
+ ;;
+ -mint | -mint[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+ # Recognize the basic CPU types without company name.
+ # Some are omitted here because they have special meanings below.
+ 1750a | 580 \
+ | a29k \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
+ | c4x | clipper \
+ | d10v | d30v | dsp16xx \
+ | fr30 \
+ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | i370 | i860 | i960 | ia64 \
+ | m32r | m68000 | m68k | m88k | mcore \
+ | mips16 | mips64 | mips64el | mips64orion | mips64orionel \
+ | mips64vr4100 | mips64vr4100el | mips64vr4300 \
+ | mips64vr4300el | mips64vr5000 | mips64vr5000el \
+ | mipsbe | mipseb | mipsel | mipsle | mipstx39 | mipstx39el \
+ | mipsisa32 \
+ | mn10200 | mn10300 \
+ | ns16k | ns32k \
+ | openrisc \
+ | pdp10 | pdp11 | pj | pjl \
+ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+ | pyramid \
+ | sh | sh[34] | sh[34]eb | shbe | shle \
+ | sparc | sparc64 | sparclet | sparclite | sparcv9 | sparcv9b \
+ | strongarm \
+ | tahoe | thumb | tic80 | tron \
+ | v850 | v850e \
+ | we32k \
+ | x86 | xscale | xstormy16 | xtensa \
+ | z8k)
+ basic_machine=$basic_machine-unknown
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12)
+ # Motorola 68HC11/12.
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+ ;;
+
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i*86 | x86_64)
+ basic_machine=$basic_machine-pc
+ ;;
+ # Object if more than one company name word.
+ *-*-*)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+ # Recognize the basic CPU types with company name.
+ 580-* \
+ | a29k-* \
+ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+ | alphapca5[67]-* | arc-* \
+ | arm-* | armbe-* | armle-* | armv*-* \
+ | avr-* \
+ | bs2000-* \
+ | c[123]* | c30-* | [cjt]90-* | c54x-* \
+ | clipper-* | cray2-* | cydra-* \
+ | d10v-* | d30v-* \
+ | elxsi-* \
+ | f30[01]-* | f700-* | fr30-* | fx80-* \
+ | h8300-* | h8500-* \
+ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+ | i*86-* | i860-* | i960-* | ia64-* \
+ | m32r-* \
+ | m68000-* | m680[01234]0-* | m68360-* | m683?2-* | m68k-* \
+ | m88110-* | m88k-* | mcore-* \
+ | mips-* | mips16-* | mips64-* | mips64el-* | mips64orion-* \
+ | mips64orionel-* | mips64vr4100-* | mips64vr4100el-* \
+ | mips64vr4300-* | mips64vr4300el-* | mipsbe-* | mipseb-* \
+ | mipsle-* | mipsel-* | mipstx39-* | mipstx39el-* \
+ | none-* | np1-* | ns16k-* | ns32k-* \
+ | orion-* \
+ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+ | pyramid-* \
+ | romp-* | rs6000-* \
+ | sh-* | sh[34]-* | sh[34]eb-* | shbe-* | shle-* \
+ | sparc-* | sparc64-* | sparc86x-* | sparclite-* \
+ | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* \
+ | t3e-* | tahoe-* | thumb-* | tic30-* | tic54x-* | tic80-* | tron-* \
+ | v850-* | v850e-* | vax-* \
+ | we32k-* \
+ | x86-* | x86_64-* | xmp-* | xps100-* | xscale-* | xstormy16-* \
+ | xtensa-* \
+ | ymp-* \
+ | z8k-*)
+ ;;
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 386bsd)
+ basic_machine=i386-unknown
+ os=-bsd
+ ;;
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ basic_machine=m68000-att
+ ;;
+ 3b*)
+ basic_machine=we32k-att
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=-scout
+ ;;
+ alliant | fx80)
+ basic_machine=fx80-alliant
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=-bsd
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=-sysv
+ ;;
+ amiga | amiga-*)
+ basic_machine=m68k-unknown
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-unknown
+ os=-amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-unknown
+ os=-sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=-sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ os=-bsd
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=-aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=-dynix
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=-bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=-bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=-bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=-bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=-bsd
+ ;;
+ cray | ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ cray2)
+ basic_machine=cray2-cray
+ os=-unicos
+ ;;
+ [cjt]90)
+ basic_machine=${basic_machine}-cray
+ os=-unicos
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ cris | cris-* | etrax*)
+ basic_machine=cris-axis
+ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ basic_machine=m68k-motorola
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=-sysv3
+ ;;
+ dpx20 | dpx20-*)
+ basic_machine=rs6000-bull
+ os=-bosx
+ ;;
+ dpx2* | dpx2*-bull)
+ basic_machine=m68k-bull
+ os=-sysv3
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=-ebmon
+ ;;
+ elxsi)
+ basic_machine=elxsi-elxsi
+ os=-bsd
+ ;;
+ encore | umax | mmax)
+ basic_machine=ns32k-encore
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ os=-ose
+ ;;
+ fx2800)
+ basic_machine=i860-alliant
+ ;;
+ genix)
+ basic_machine=ns32k-ns
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=-sysv
+ ;;
+ go32)
+ basic_machine=i386-pc
+ os=-go32
+ ;;
+ h3050r* | hiux*)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=-hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ os=-xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ os=-hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=-sysv3
+ ;;
+ hp300-*)
+ basic_machine=m68k-hp
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=-bsd
+ ;;
+ hp300hpux)
+ basic_machine=m68k-hp
+ os=-hpux
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ basic_machine=m68000-hp
+ ;;
+ hp9k3[2-9][0-9])
+ basic_machine=m68k-hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hppa-next)
+ os=-nextstep3
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ os=-osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ os=-proelf
+ ;;
+ i370-ibm* | ibm*)
+ basic_machine=i370-ibm
+ ;;
+# I'm not sure what "Sysv32" means. Should this be sysv3.2?
+ i*86v32)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv32
+ ;;
+ i*86v4*)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv4
+ ;;
+ i*86v)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv
+ ;;
+ i*86sol2)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-solaris2
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ os=-mach
+ ;;
+ i386-vsta | vsta)
+ basic_machine=i386-unknown
+ os=-vsta
+ ;;
+ iris | iris4d)
+ basic_machine=mips-sgi
+ case $os in
+ -irix*)
+ ;;
+ *)
+ os=-irix4
+ ;;
+ esac
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=-sysv
+ ;;
+ m88k-omron*)
+ basic_machine=m88k-omron
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=-sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+ mingw32)
+ basic_machine=i386-pc
+ os=-mingw32
+ ;;
+ miniframe)
+ basic_machine=m68000-convergent
+ ;;
+ *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+ mipsel*-linux*)
+ basic_machine=mipsel-unknown
+ os=-linux-gnu
+ ;;
+ mips*-linux*)
+ basic_machine=mips-unknown
+ os=-linux-gnu
+ ;;
+ mips3*-*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+ ;;
+ mips3*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+ ;;
+ mmix*)
+ basic_machine=mmix-knuth
+ os=-mmixware
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ msdos)
+ basic_machine=i386-pc
+ os=-msdos
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=-sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-unknown
+ os=-netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ os=-linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=-newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=-newsos
+ ;;
+ news-3600 | risc-news)
+ basic_machine=mips-sony
+ os=-newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ os=-sysv
+ ;;
+ next | m*-next )
+ basic_machine=m68k-next
+ case $os in
+ -nextstep* )
+ ;;
+ -ns2*)
+ os=-nextstep2
+ ;;
+ *)
+ os=-nextstep3
+ ;;
+ esac
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=-cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=-cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=-nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ os=-mon960
+ ;;
+ nonstopux)
+ basic_machine=mips-compaq
+ os=-nonstopux
+ ;;
+ np1)
+ basic_machine=np1-gould
+ ;;
+ nsr-tandem)
+ basic_machine=nsr-tandem
+ ;;
+ op50n-* | op60c-*)
+ basic_machine=hppa1.1-oki
+ os=-proelf
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ os=-ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ os=-os68k
+ ;;
+ pa-hitachi)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=-osf
+ ;;
+ pbd)
+ basic_machine=sparc-tti
+ ;;
+ pbb)
+ basic_machine=m68k-tti
+ ;;
+ pc532 | pc532-*)
+ basic_machine=ns32k-pc532
+ ;;
+ pentium | p5 | k5 | k6 | nexgen | viac3)
+ basic_machine=i586-pc
+ ;;
+ pentiumpro | p6 | 6x86 | athlon)
+ basic_machine=i686-pc
+ ;;
+ pentiumii | pentium2)
+ basic_machine=i686-pc
+ ;;
+ pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+ basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumpro-* | p6-* | 6x86-* | athlon-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumii-* | pentium2-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pn)
+ basic_machine=pn-gould
+ ;;
+ power) basic_machine=power-ibm
+ ;;
+ ppc) basic_machine=powerpc-unknown
+ ;;
+ ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle | ppc-le | powerpc-little)
+ basic_machine=powerpcle-unknown
+ ;;
+ ppcle-* | powerpclittle-*)
+ basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64) basic_machine=powerpc64-unknown
+ ;;
+ ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+ basic_machine=powerpc64le-unknown
+ ;;
+ ppc64le-* | powerpc64little-*)
+ basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ps2)
+ basic_machine=i386-ibm
+ ;;
+ pw32)
+ basic_machine=i586-unknown
+ os=-pw32
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ rm[46]00)
+ basic_machine=mips-siemens
+ ;;
+ rtpc | rtpc-*)
+ basic_machine=romp-ibm
+ ;;
+ s390 | s390-*)
+ basic_machine=s390-ibm
+ ;;
+ s390x | s390x-*)
+ basic_machine=s390x-ibm
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ ;;
+ sh)
+ basic_machine=sh-hitachi
+ os=-hms
+ ;;
+ sparclite-wrs | simso-wrs)
+ basic_machine=sparclite-wrs
+ os=-vxworks
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=-sysv2
+ ;;
+ spur)
+ basic_machine=spur-unknown
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ os=-sysv4
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=-sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=-sunos4
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=-sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=-sunos4
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=-sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=-sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=-solaris2
+ ;;
+ sun3 | sun3-*)
+ basic_machine=m68k-sun
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ ;;
+ sv1)
+ basic_machine=sv1-cray
+ os=-unicos
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=-dynix
+ ;;
+ t3e)
+ basic_machine=t3e-cray
+ os=-unicos
+ ;;
+ tic54x | c54x*)
+ basic_machine=tic54x-unknown
+ os=-coff
+ ;;
+ tx39)
+ basic_machine=mipstx39-unknown
+ ;;
+ tx39el)
+ basic_machine=mipstx39el-unknown
+ ;;
+ tower | tower-32)
+ basic_machine=m68k-ncr
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=-sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ os=-none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=-sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=-vms
+ ;;
+ vpp*|vx|vx-*)
+ basic_machine=f301-fujitsu
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=-vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=-vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=-vxworks
+ ;;
+ w65*)
+ basic_machine=w65-wdc
+ os=-none
+ ;;
+ w89k-*)
+ basic_machine=hppa1.1-winbond
+ os=-proelf
+ ;;
+ windows32)
+ basic_machine=i386-pc
+ os=-windows32-msvcrt
+ ;;
+ xmp)
+ basic_machine=xmp-cray
+ os=-unicos
+ ;;
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
+ z8k-*-coff)
+ basic_machine=z8k-unknown
+ os=-sim
+ ;;
+ none)
+ basic_machine=none-none
+ os=-none
+ ;;
+
+# Here we handle the default manufacturer of certain CPU types. It is in
+# some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ basic_machine=hppa1.1-winbond
+ ;;
+ op50n)
+ basic_machine=hppa1.1-oki
+ ;;
+ op60c)
+ basic_machine=hppa1.1-oki
+ ;;
+ mips)
+ if [ x$os = x-linux-gnu ]; then
+ basic_machine=mips-unknown
+ else
+ basic_machine=mips-mips
+ fi
+ ;;
+ romp)
+ basic_machine=romp-ibm
+ ;;
+ rs6000)
+ basic_machine=rs6000-ibm
+ ;;
+ vax)
+ basic_machine=vax-dec
+ ;;
+ pdp10)
+ # there are many clones, so DEC is not a safe bet
+ basic_machine=pdp10-unknown
+ ;;
+ pdp11)
+ basic_machine=pdp11-dec
+ ;;
+ we32k)
+ basic_machine=we32k-att
+ ;;
+ sh3 | sh4 | sh3eb | sh4eb)
+ basic_machine=sh-unknown
+ ;;
+ sparc | sparcv9 | sparcv9b)
+ basic_machine=sparc-sun
+ ;;
+ cydra)
+ basic_machine=cydra-cydrome
+ ;;
+ orion)
+ basic_machine=orion-highlevel
+ ;;
+ orion105)
+ basic_machine=clipper-highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ basic_machine=m68k-apple
+ ;;
+ pmac | pmac-mpw)
+ basic_machine=powerpc-apple
+ ;;
+ c4x*)
+ basic_machine=c4x-none
+ os=-coff
+ ;;
+ *-unknown)
+ # Make sure to match an already-canonicalized machine name.
+ ;;
+ *)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+ *-digital*)
+ basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+ ;;
+ *-commodore*)
+ basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+ # First match some system type aliases
+ # that might get confused with valid system types.
+ # -solaris* is a basic system type, with this one exception.
+ -solaris1 | -solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ -solaris)
+ os=-solaris2
+ ;;
+ -svr4*)
+ os=-sysv4
+ ;;
+ -unixware*)
+ os=-sysv4.2uw
+ ;;
+ -gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # First accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST END IN A *, to match a version number.
+ # -sysv* is not here because it comes later, after sysvr4.
+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+ | -aos* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \
+ | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -chorusos* | -chorusrdb* \
+ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -interix* | -uwin* | -rhapsody* | -darwin* | -opened* \
+ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -qnx*)
+ case $basic_machine in
+ x86-* | i*86-*)
+ ;;
+ *)
+ os=-nto$os
+ ;;
+ esac
+ ;;
+ -nto*)
+ os=-nto-qnx
+ ;;
+ -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+ | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
+ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+ ;;
+ -mac*)
+ os=`echo $os | sed -e 's|mac|macos|'`
+ ;;
+ -linux*)
+ os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ ;;
+ -sunos5*)
+ os=`echo $os | sed -e 's|sunos5|solaris2|'`
+ ;;
+ -sunos6*)
+ os=`echo $os | sed -e 's|sunos6|solaris3|'`
+ ;;
+ -opened*)
+ os=-openedition
+ ;;
+ -wince*)
+ os=-wince
+ ;;
+ -osfrose*)
+ os=-osfrose
+ ;;
+ -osf*)
+ os=-osf
+ ;;
+ -utek*)
+ os=-bsd
+ ;;
+ -dynix*)
+ os=-bsd
+ ;;
+ -acis*)
+ os=-aos
+ ;;
+ -atheos*)
+ os=-atheos
+ ;;
+ -386bsd)
+ os=-bsd
+ ;;
+ -ctix* | -uts*)
+ os=-sysv
+ ;;
+ -ns2 )
+ os=-nextstep2
+ ;;
+ -nsk*)
+ os=-nsk
+ ;;
+ # Preserve the version number of sinix5.
+ -sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ -sinix*)
+ os=-sysv4
+ ;;
+ -triton*)
+ os=-sysv3
+ ;;
+ -oss*)
+ os=-sysv3
+ ;;
+ -svr4)
+ os=-sysv4
+ ;;
+ -svr3)
+ os=-sysv3
+ ;;
+ -sysvr4)
+ os=-sysv4
+ ;;
+ # This must come after -sysvr4.
+ -sysv*)
+ ;;
+ -ose*)
+ os=-ose
+ ;;
+ -es1800*)
+ os=-ose
+ ;;
+ -xenix)
+ os=-xenix
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ os=-mint
+ ;;
+ -none)
+ ;;
+ *)
+ # Get rid of the `-' at the beginning of $os.
+ os=`echo $os | sed 's/[^-]*-//'`
+ echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+ *-acorn)
+ os=-riscix1.2
+ ;;
+ arm*-rebel)
+ os=-linux
+ ;;
+ arm*-semi)
+ os=-aout
+ ;;
+ pdp10-*)
+ os=-tops20
+ ;;
+ pdp11-*)
+ os=-none
+ ;;
+ *-dec | vax-*)
+ os=-ultrix4.2
+ ;;
+ m68*-apollo)
+ os=-domain
+ ;;
+ i386-sun)
+ os=-sunos4.0.2
+ ;;
+ m68000-sun)
+ os=-sunos3
+ # This also exists in the configure program, but was not the
+ # default.
+ # os=-sunos4
+ ;;
+ m68*-cisco)
+ os=-aout
+ ;;
+ mips*-cisco)
+ os=-elf
+ ;;
+ mips*-*)
+ os=-elf
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=-sysv3
+ ;;
+ sparc-* | *-sun)
+ os=-sunos4.1.1
+ ;;
+ *-be)
+ os=-beos
+ ;;
+ *-ibm)
+ os=-aix
+ ;;
+ *-wec)
+ os=-proelf
+ ;;
+ *-winbond)
+ os=-proelf
+ ;;
+ *-oki)
+ os=-proelf
+ ;;
+ *-hp)
+ os=-hpux
+ ;;
+ *-hitachi)
+ os=-hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=-sysv
+ ;;
+ *-cbm)
+ os=-amigaos
+ ;;
+ *-dg)
+ os=-dgux
+ ;;
+ *-dolphin)
+ os=-sysv3
+ ;;
+ m68k-ccur)
+ os=-rtu
+ ;;
+ m88k-omron*)
+ os=-luna
+ ;;
+ *-next )
+ os=-nextstep
+ ;;
+ *-sequent)
+ os=-ptx
+ ;;
+ *-crds)
+ os=-unos
+ ;;
+ *-ns)
+ os=-genix
+ ;;
+ i370-*)
+ os=-mvs
+ ;;
+ *-next)
+ os=-nextstep3
+ ;;
+ *-gould)
+ os=-sysv
+ ;;
+ *-highlevel)
+ os=-bsd
+ ;;
+ *-encore)
+ os=-bsd
+ ;;
+ *-sgi)
+ os=-irix
+ ;;
+ *-siemens)
+ os=-sysv4
+ ;;
+ *-masscomp)
+ os=-rtu
+ ;;
+ f30[01]-fujitsu | f700-fujitsu)
+ os=-uxpv
+ ;;
+ *-rom68k)
+ os=-coff
+ ;;
+ *-*bug)
+ os=-coff
+ ;;
+ *-apple)
+ os=-macos
+ ;;
+ *-atari*)
+ os=-mint
+ ;;
+ *)
+ os=-none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+ *-unknown)
+ case $os in
+ -riscix*)
+ vendor=acorn
+ ;;
+ -sunos*)
+ vendor=sun
+ ;;
+ -aix*)
+ vendor=ibm
+ ;;
+ -beos*)
+ vendor=be
+ ;;
+ -hpux*)
+ vendor=hp
+ ;;
+ -mpeix*)
+ vendor=hp
+ ;;
+ -hiux*)
+ vendor=hitachi
+ ;;
+ -unos*)
+ vendor=crds
+ ;;
+ -dgux*)
+ vendor=dg
+ ;;
+ -luna*)
+ vendor=omron
+ ;;
+ -genix*)
+ vendor=ns
+ ;;
+ -mvs* | -opened*)
+ vendor=ibm
+ ;;
+ -ptx*)
+ vendor=sequent
+ ;;
+ -vxsim* | -vxworks*)
+ vendor=wrs
+ ;;
+ -aux*)
+ vendor=apple
+ ;;
+ -hms*)
+ vendor=hitachi
+ ;;
+ -mpw* | -macos*)
+ vendor=apple
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ vendor=atari
+ ;;
+ -vos*)
+ vendor=stratus
+ ;;
+ esac
+ basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+ ;;
+esac
+
+echo $basic_machine$os
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/source/configure.developer b/source/configure.developer
new file mode 100755
index 00000000000..a7074acfd3a
--- /dev/null
+++ b/source/configure.developer
@@ -0,0 +1,2 @@
+#!/bin/sh
+`dirname $0`/configure --enable-developer "$@"
diff --git a/source/configure.in b/source/configure.in
new file mode 100644
index 00000000000..f546069e08a
--- /dev/null
+++ b/source/configure.in
@@ -0,0 +1,4442 @@
+dnl Process this file with autoconf to produce a configure script.
+
+dnl We must use autotools 2.53 or above
+AC_PREREQ(2.53)
+AC_INIT(include/includes.h)
+AC_CONFIG_HEADER(include/config.h)
+
+AC_DISABLE_STATIC
+AC_ENABLE_SHARED
+
+#################################################
+# Directory handling stuff to support both the
+# legacy SAMBA directories and FHS compliant
+# ones...
+AC_PREFIX_DEFAULT(/usr/local/samba)
+
+lockdir="\${VARDIR}/locks"
+piddir="\${VARDIR}/locks"
+mandir="\${prefix}/man"
+logfilebase="\${VARDIR}"
+privatedir="\${prefix}/private"
+libdir="\${prefix}/lib"
+configdir="\${LIBDIR}"
+swatdir="\${prefix}/swat"
+
+AC_ARG_WITH(fhs,
+[ --with-fhs Use FHS-compliant paths (default=no)],
+[ case "$withval" in
+ yes)
+ lockdir="\${VARDIR}/lib/samba"
+ piddir="\${VARDIR}/run"
+ mandir="\${prefix}/share/man"
+ logfilebase="\${VARDIR}/log/samba"
+ privatedir="\${CONFIGDIR}/private"
+ libdir="\${prefix}/lib/samba"
+ configdir="${sysconfdir}/samba"
+ swatdir="\${DATADIR}/samba/swat"
+ ;;
+ esac])
+
+#################################################
+# set private directory location
+AC_ARG_WITH(privatedir,
+[ --with-privatedir=DIR Where to put smbpasswd ($ac_default_prefix/private)],
+[ case "$withval" in
+ yes|no)
+ #
+ # Just in case anybody calls it without argument
+ #
+ AC_MSG_WARN([--with-privatedir called without argument - will use default])
+ ;;
+ * )
+ privatedir="$withval"
+ ;;
+ esac])
+
+#################################################
+# set lock directory location
+AC_ARG_WITH(lockdir,
+[ --with-lockdir=DIR Where to put lock files ($ac_default_prefix/var/locks)],
+[ case "$withval" in
+ yes|no)
+ #
+ # Just in case anybody calls it without argument
+ #
+ AC_MSG_WARN([--with-lockdir called without argument - will use default])
+ ;;
+ * )
+ lockdir="$withval"
+ ;;
+ esac])
+
+#################################################
+# set pid directory location
+AC_ARG_WITH(piddir,
+[ --with-piddir=DIR Where to put pid files ($ac_default_prefix/var/locks)],
+[ case "$withval" in
+ yes|no)
+ #
+ # Just in case anybody calls it without argument
+ #
+ AC_MSG_WARN([--with-piddir called without argument - will use default])
+ ;;
+ * )
+ piddir="$withval"
+ ;;
+ esac])
+
+#################################################
+# set SWAT directory location
+AC_ARG_WITH(swatdir,
+[ --with-swatdir=DIR Where to put SWAT files ($ac_default_prefix/swat)],
+[ case "$withval" in
+ yes|no)
+ #
+ # Just in case anybody does it
+ #
+ AC_MSG_WARN([--with-swatdir called without argument - will use default])
+ ;;
+ * )
+ swatdir="$withval"
+ ;;
+ esac])
+
+#################################################
+# set configuration directory location
+AC_ARG_WITH(configdir,
+[ --with-configdir=DIR Where to put configuration files ($libdir)],
+[ case "$withval" in
+ yes|no)
+ #
+ # Just in case anybody does it
+ #
+ AC_MSG_WARN([--with-configdir called without argument - will use default])
+ ;;
+ * )
+ configdir="$withval"
+ ;;
+ esac])
+
+#################################################
+# set log directory location
+AC_ARG_WITH(logfilebase,
+[ --with-logfilebase=DIR Where to put log files ($VARDIR)],
+[ case "$withval" in
+ yes|no)
+ #
+ # Just in case anybody does it
+ #
+ AC_MSG_WARN([--with-logfilebase called without argument - will use default])
+ ;;
+ * )
+ logfilebase="$withval"
+ ;;
+ esac])
+
+#################################################
+# set lib directory location
+AC_ARG_WITH(libdir,
+[ --with-libdir=DIR Where to put libdir ($libdir)],
+[ case "$withval" in
+ yes|no)
+ #
+ # Just in case anybody does it
+ #
+ AC_MSG_WARN([--with-libdir without argument - will use default])
+ ;;
+ * )
+ libdir="$withval"
+ ;;
+ esac])
+
+#################################################
+# set lib directory location
+AC_ARG_WITH(mandir,
+[ --with-mandir=DIR Where to put man pages ($mandir)],
+[ case "$withval" in
+ yes|no)
+ #
+ # Just in case anybody does it
+ #
+ AC_MSG_WARN([--with-mandir without argument - will use default])
+ ;;
+ * )
+ mandir="$withval"
+ ;;
+ esac])
+
+AC_ARG_WITH(cfenc,
+[ --with-cfenc=HEADERDIR Use internal CoreFoundation encoding API
+ for optimization (Mac OS X/Darwin only)],
+[
+# May be in source $withval/CoreFoundation/StringEncodings.subproj.
+# Should have been in framework $withval/CoreFoundation.framework/Headers.
+for d in \
+ $withval/CoreFoundation/StringEncodings.subproj \
+ $withval/StringEncodings.subproj \
+ $withval/CoreFoundation.framework/Headers \
+ $withval/Headers \
+ $withval
+do
+ if test -r $d/CFStringEncodingConverter.h; then
+ ln -sfh $d include/CoreFoundation
+ fi
+done
+])
+
+AC_SUBST(configdir)
+AC_SUBST(lockdir)
+AC_SUBST(piddir)
+AC_SUBST(logfilebase)
+AC_SUBST(privatedir)
+AC_SUBST(swatdir)
+AC_SUBST(bindir)
+AC_SUBST(sbindir)
+
+dnl Unique-to-Samba variables we'll be playing with.
+AC_SUBST(SHELL)
+AC_SUBST(LDSHFLAGS)
+AC_SUBST(SONAMEFLAG)
+AC_SUBST(SHLD)
+AC_SUBST(HOST_OS)
+AC_SUBST(PICFLAGS)
+AC_SUBST(PICSUFFIX)
+AC_SUBST(SHLIBEXT)
+AC_SUBST(INSTALLCLIENT)
+AC_SUBST(INSTALLCLIENTCMD_SH)
+AC_SUBST(INSTALLCLIENTCMD_A)
+AC_SUBST(LIBSMBCLIENT_SHARED)
+AC_SUBST(LIBSMBCLIENT)
+AC_SUBST(PRINT_LIBS)
+AC_SUBST(AUTH_LIBS)
+AC_SUBST(ACL_LIBS)
+AC_SUBST(PASSDB_LIBS)
+AC_SUBST(IDMAP_LIBS)
+AC_SUBST(KRB5_LIBS)
+AC_SUBST(LDAP_LIBS)
+AC_SUBST(SHLIB_PROGS)
+AC_SUBST(SMBWRAPPER)
+AC_SUBST(EXTRA_BIN_PROGS)
+AC_SUBST(EXTRA_SBIN_PROGS)
+AC_SUBST(EXTRA_ALL_TARGETS)
+AC_SUBST(CONFIG_LIBS)
+
+AC_ARG_ENABLE(debug,
+[ --enable-debug Turn on compiler debugging information (default=no)],
+ [if eval "test x$enable_debug = xyes"; then
+ CFLAGS="${CFLAGS} -g"
+ fi])
+
+AC_ARG_ENABLE(developer, [ --enable-developer Turn on developer warnings and debugging (default=no)],
+ [if eval "test x$enable_developer = xyes"; then
+ developer=yes
+ CFLAGS="${CFLAGS} -gstabs -Wall -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings -DDEBUG_PASSWORD -DDEVELOPER"
+ fi])
+
+AC_ARG_ENABLE(krb5developer, [ --enable-krb5developer Turn on developer warnings and debugging, except -Wstrict-prototypes (default=no)],
+ [if eval "test x$enable_krb5developer = xyes"; then
+ developer=yes
+ CFLAGS="${CFLAGS} -gstabs -Wall -Wshadow -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings -DDEBUG_PASSWORD -DDEVELOPER"
+ fi])
+
+AC_ARG_ENABLE(dmalloc, [ --enable-dmalloc Enable heap debugging [default=no]])
+
+if test "x$enable_dmalloc" = xyes
+then
+ AC_DEFINE(ENABLE_DMALLOC, 1, [Define to turn on dmalloc debugging])
+ AC_DEFINE(DMALLOC_FUNC_CHECK, 1,
+ [Define to check invariants around some common functions])
+ LIBS="$LIBS -ldmalloc"
+fi
+
+dnl Checks for programs.
+
+##
+## for some reason this macro resets the CFLAGS
+## so save and restore
+##
+OLD_CFLAGS=${CFLAGS}
+AC_PROG_CC
+CFLAGS=${OLD_CFLAGS}
+
+OLD_CFLAGS=${CFLAGS}
+AC_PROG_CPP
+CFLAGS=${OLD_CFLAGS}
+
+AC_PROG_INSTALL
+AC_PROG_AWK
+AC_PATH_PROG(PERL, perl)
+
+AC_CHECK_TOOL(AR, ar)
+
+# compile with optimization and without debugging by default, but
+# allow people to set their own preference.
+if test "x$CFLAGS" = x
+then
+ CFLAGS="-O ${CFLAGS}"
+fi
+
+dnl Check if we use GNU ld
+LD=ld
+AC_PROG_LD_GNU
+
+dnl needed before AC_TRY_COMPILE
+AC_ISC_POSIX
+
+dnl look for executable suffix
+AC_EXEEXT
+
+dnl Check if C compiler understands -c and -o at the same time
+AC_PROG_CC_C_O
+if eval "test \"`echo '$ac_cv_prog_cc_'${ac_cc}_c_o`\" = no"; then
+ BROKEN_CC=
+else
+ BROKEN_CC=#
+fi
+AC_SUBST(BROKEN_CC)
+
+dnl Check if the C compiler understands -Werror
+AC_CACHE_CHECK([that the C compiler understands -Werror],samba_cv_HAVE_Werror, [
+ AC_TRY_RUN_STRICT([
+ int main(void)
+ {
+ return 0;
+ }],[-Werror],[$CPPFLAGS],[$LDFLAGS],
+ samba_cv_HAVE_Werror=yes,samba_cv_HAVE_Werror=no,samba_cv_HAVE_Werror=cross)])
+if test x"$samba_cv_HAVE_Werror" = x"yes"; then
+ Werror_FLAGS="-Werror"
+else
+dnl Check if the C compiler understands -w2
+AC_CACHE_CHECK([that the C compiler understands -w2],samba_cv_HAVE_w2, [
+ AC_TRY_RUN_STRICT([
+ int main(void)
+ {
+ return 0;
+ }],[-w2],[$CPPFLAGS],[$LDFLAGS],
+ samba_cv_HAVE_w2=yes,samba_cv_HAVE_w2=no,samba_cv_HAVE_w2=cross)])
+if test x"$samba_cv_HAVE_w2" = x"yes"; then
+ Werror_FLAGS="-w2"
+fi
+fi
+
+dnl Check if the C compiler understands volatile (it should, being ANSI).
+AC_CACHE_CHECK([that the C compiler understands volatile],samba_cv_volatile, [
+ AC_TRY_COMPILE([#include <sys/types.h>],[volatile int i = 0],
+ samba_cv_volatile=yes,samba_cv_volatile=no)])
+if test x"$samba_cv_volatile" = x"yes"; then
+ AC_DEFINE(HAVE_VOLATILE, 1, [Whether the C compiler understands volatile])
+fi
+
+UNAME_S=`(uname -s) 2>/dev/null` || UNAME_S="unknown"
+AC_MSG_CHECKING(uname -s)
+AC_MSG_RESULT(${UNAME_S})
+
+UNAME_R=`(uname -r) 2>/dev/null` || UNAME_R="unknown"
+AC_MSG_CHECKING(uname -r)
+AC_MSG_RESULT(${UNAME_R})
+
+UNAME_M=`(uname -m) 2>/dev/null` || UNAME_M="unknown"
+AC_MSG_CHECKING(uname -m)
+AC_MSG_RESULT(${UNAME_M})
+
+UNAME_P=`(uname -p) 2>/dev/null` || UNAME_P="unknown"
+AC_MSG_CHECKING(uname -p)
+AC_MSG_RESULT(${UNAME_P})
+
+AC_CANONICAL_SYSTEM
+
+dnl Add #include for broken IRIX header files
+ case "$host_os" in
+ *irix6*) AC_ADD_INCLUDE(<standards.h>)
+ ;;
+esac
+
+AC_VALIDATE_CACHE_SYSTEM_TYPE
+
+DYNEXP=
+
+dnl Add modules that have to be built by default here
+dnl These have to be built static:
+default_static_modules="pdb_smbpasswd pdb_tdbsam rpc_lsa rpc_samr rpc_reg rpc_lsa_ds rpc_wks rpc_net rpc_dfs rpc_srv rpc_spoolss rpc_epmapper auth_rhosts auth_sam auth_unix auth_winbind auth_server auth_domain auth_builtin"
+
+dnl These are preferably build shared, and static if dlopen() is not available
+default_shared_modules="vfs_recycle vfs_audit vfs_extd_audit vfs_netatalk vfs_fake_perms vfs_default_quota vfs_readonly vfs_cap vfs_expand_msdfs charset_CP850 charset_CP437"
+
+if test "x$developer" = xyes; then
+ default_static_modules="$default_static_modules rpc_echo"
+ default_shared_modules="$default_shared_modules charset_weird"
+fi
+
+#
+# Config CPPFLAG settings for strange OS's that must be set
+# before other tests.
+#
+case "$host_os" in
+# Try to work out if this is the native HPUX compiler that uses the -Ae flag.
+ *hpux*)
+
+ AC_PROG_CC_FLAG(Ae)
+ # mmap on HPUX is completely broken...
+ AC_DEFINE(MMAP_BLACKLIST, 1, [Whether MMAP is broken])
+ if test $ac_cv_prog_cc_Ae = yes; then
+ CPPFLAGS="$CPPFLAGS -Ae"
+ fi
+#
+# Defines needed for HPUX support.
+# HPUX has bigcrypt but (sometimes?) doesn't use it for
+# password hashing - hence the USE_BOTH_CRYPT_CALLS define.
+#
+ case `uname -r` in
+ *9*|*10*)
+ CPPFLAGS="$CPPFLAGS -D_HPUX_SOURCE -D_POSIX_SOURCE -D_ALIGNMENT_REQUIRED=1 -D_MAX_ALIGNMENT=4 -DMAX_POSITIVE_LOCK_OFFSET=0x1ffffffffffLL"
+ AC_DEFINE(USE_BOTH_CRYPT_CALLS, 1, [Whether to use both of HPUX' crypt calls])
+ AC_DEFINE(_HPUX_SOURCE, 1, [Whether to use HPUX extensions])
+ AC_DEFINE(_POSIX_SOURCE, 1, [Whether to use POSIX compatible functions])
+ AC_DEFINE(_ALIGNMENT_REQUIRED,1,[Required alignment])
+ AC_DEFINE(_MAX_ALIGNMENT,4,[Maximum alignment])
+ ;;
+ *11*)
+ CPPFLAGS="$CPPFLAGS -D_HPUX_SOURCE -D_POSIX_SOURCE -D_LARGEFILE64_SOURCE -D_ALIGNMENT_REQUIRED=1 -D_MAX_ALIGNMENT=4 -DMAX_POSITIVE_LOCK_OFFSET=0x1ffffffffffLL"
+ AC_DEFINE(USE_BOTH_CRYPT_CALLS, 1, [Whether to use both of HPUX' crypt calls])
+ AC_DEFINE(_HPUX_SOURCE, 1, [Whether to use HPUX extensions])
+ AC_DEFINE(_POSIX_SOURCE, 1, [Whether to use POSIX compatible functions])
+ AC_DEFINE(_LARGEFILE64_SOURCE, 1, [Whether to use large file support])
+ AC_DEFINE(_ALIGNMENT_REQUIRED, 1, [Required alignment])
+ AC_DEFINE(_MAX_ALIGNMENT, 4, [Maximum alignment])
+ ;;
+ esac
+ DYNEXP="-Wl,-E"
+ ;;
+
+#
+# CRAY Unicos has broken const handling
+ *unicos*)
+ AC_MSG_RESULT([disabling const])
+ CPPFLAGS="$CPPFLAGS -Dconst="
+ ;;
+
+#
+# AIX4.x doesn't even admit to having large
+# files *at all* unless the -D_LARGE_FILE or -D_LARGE_FILE_API flags are set.
+#
+ *aix4*)
+ AC_MSG_RESULT([enabling large file support])
+ CPPFLAGS="$CPPFLAGS -D_LARGE_FILES"
+ AC_DEFINE(_LARGE_FILES, 1, [Whether to enable large file support])
+ ;;
+#
+# Defines needed for Solaris 2.6/2.7 aka 7.0 to make it admit
+# to the existance of large files..
+# Note that -D_LARGEFILE64_SOURCE is different from the Sun
+# recommendations on large file support, however it makes the
+# compile work using gcc 2.7 and 2.8, whereas using the Sun
+# recommendation makes the compile fail on gcc2.7. JRA.
+#
+# Solaris uses SYSV printing. Make sure to set that here. --jerry
+#
+ *solaris*)
+ AC_DEFINE(SYSV, 1, [Whether to enable System V compatibility])
+ case `uname -r` in
+ 5.0*|5.1*|5.2*|5.3*|5.5*)
+ AC_MSG_RESULT([no large file support])
+ ;;
+ 5.*)
+ AC_MSG_RESULT([enabling large file support])
+ if test "$ac_cv_prog_gcc" = yes; then
+ ${CC-cc} -v >conftest.c 2>&1
+ ac_cv_gcc_compiler_version_number=`grep 'gcc version' conftest.c`
+ rm -fr conftest.c
+ case "$ac_cv_gcc_compiler_version_number" in
+ *"gcc version 2.6"*|*"gcc version 2.7"*)
+ CPPFLAGS="$CPPFLAGS -D_LARGEFILE64_SOURCE"
+ AC_DEFINE(_LARGEFILE64_SOURCE, 1, [Whether to enable large file support])
+ ;;
+ *)
+ CPPFLAGS="$CPPFLAGS -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64"
+ AC_DEFINE(_LARGEFILE64_SOURCE, 1, [Whether to enable large file support])
+ AC_DEFINE(_FILE_OFFSET_BITS, 64, [File offset bits])
+ ;;
+ esac
+ else
+ CPPFLAGS="$CPPFLAGS -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64"
+ AC_DEFINE(_LARGEFILE64_SOURCE, 1, [Whether to enable large file support])
+ AC_DEFINE(_FILE_OFFSET_BITS, 64, [File offset bits])
+ fi
+ ;;
+ esac
+ ;;
+#
+# IRIX uses SYSV printing. Make sure to set that here
+#
+ *irix*)
+ AC_DEFINE(SYSV, 1, [Whether to enable System V compatibility])
+ ;;
+ *freebsd*)
+ AC_DEFINE(FREEBSD, 1, [Whether the host os is FreeBSD])
+ ;;
+#
+# VOS may need to have POSIX support and System V compatibility enabled.
+#
+ *vos*)
+ case "$CPPFLAGS" in
+ *-D_POSIX_C_SOURCE*)
+ ;;
+ *)
+ CPPFLAGS="$CPPFLAGS -D_POSIX_C_SOURCE=199506L"
+ AC_DEFINE(_POSIX_C_SOURCE, 199506L, [Whether to enable POSIX support])
+ ;;
+ esac
+ case "$CPPFLAGS" in
+ *-D_SYSV*|*-D_SVID_SOURCE*)
+ ;;
+ *)
+ CPPFLAGS="$CPPFLAGS -D_SYSV"
+ AC_DEFINE(_SYSV, 1, [Whether to enable System V compatibility])
+ esac
+ ;;
+#
+# Tests needed for SINIX large file support.
+#
+ *sysv4*)
+ if test $host = mips-sni-sysv4 ; then
+ AC_MSG_CHECKING([for LFS support])
+ old_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="-D_LARGEFILE64_SOURCE $CPPFLAGS"
+ AC_TRY_RUN([
+#include <unistd.h>
+main () {
+#if _LFS64_LARGEFILE == 1
+exit(0);
+#else
+exit(1);
+#endif
+}], [SINIX_LFS_SUPPORT=yes], [SINIX_LFS_SUPPORT=no], [SINIX_LFS_SUPPORT=cross])
+ CPPFLAGS="$old_CPPFLAGS"
+ if test x$SINIX_LFS_SUPPORT = xyes ; then
+ CPPFLAGS="-D_LARGEFILE64_SOURCE $CPPFLAGS"
+ AC_DEFINE(_LARGEFILE64_SOURCE, 1, [Whether to enable large file support])
+ CFLAGS="`getconf LFS64_CFLAGS` $CFLAGS"
+ LDFLAGS="`getconf LFS64_LDFLAGS` $LDFLAGS"
+ LIBS="`getconf LFS64_LIBS` $LIBS"
+ fi
+ AC_MSG_RESULT([$SINIX_LFS_SUPPORT])
+ fi
+ ;;
+
+# Tests for linux LFS support. Need kernel 2.4 and glibc2.2 or greater support.
+#
+ *linux*)
+ AC_MSG_CHECKING([for LFS support])
+ old_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="-D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE $CPPFLAGS"
+ AC_TRY_RUN([
+#include <unistd.h>
+#include <sys/utsname.h>
+#include <string.h>
+#include <stdlib.h>
+main() {
+#if _LFS64_LARGEFILE == 1
+ struct utsname uts;
+ char *release;
+ int major, minor;
+
+ /* Ensure this is glibc 2.2 or higher */
+#if defined(__GLIBC__) && defined(__GLIBC_MINOR__)
+ int libc_major = __GLIBC__;
+ int libc_minor = __GLIBC_MINOR__;
+
+ if (libc_major < 2)
+ exit(1);
+ if (libc_minor < 2)
+ exit(1);
+#endif
+
+ /* Ensure this is kernel 2.4 or higher */
+
+ uname(&uts);
+ release = strdup(uts.release);
+ major = atoi(strsep(&release, "."));
+ minor = atoi(strsep(&release, "."));
+
+ if (major > 2 || (major == 2 && minor > 3))
+ exit(0);
+ exit(1);
+#else
+ exit(1);
+#endif
+}
+], [LINUX_LFS_SUPPORT=yes], [LINUX_LFS_SUPPORT=no], [LINUX_LFS_SUPPORT=cross])
+ CPPFLAGS="$old_CPPFLAGS"
+ if test x$LINUX_LFS_SUPPORT = xyes ; then
+ CPPFLAGS="-D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE $CPPFLAGS"
+ AC_DEFINE(_LARGEFILE64_SOURCE, 1, [Whether to enable large file support])
+ AC_DEFINE(_FILE_OFFSET_BITS, 64, [File offset bits])
+ AC_DEFINE(_GNU_SOURCE, 1, [Whether to use GNU libc extensions])
+ fi
+ AC_MSG_RESULT([$LINUX_LFS_SUPPORT])
+ ;;
+
+#
+# MacOS X is the *only* system that uses compose character in utf8. This
+# is so horribly broken....
+#
+ *darwin*)
+ AC_DEFINE(BROKEN_UNICODE_COMPOSE_CHARACTERS, 1, [Does this system use unicode compose characters])
+# Add Fink directories for various packages, like dlcompat.
+# Note: iconv does that explicitly below, but other packages
+# don't.
+ CPPFLAGS="$CPPFLAGS -I/sw/include"
+ LDFLAGS="$LDFLAGS -L/sw/lib"
+
+# If we have dlsym_prepend_underscore (from Fink's dlcompat),
+# use that instead of plain dlsym.
+
+ AC_CHECK_LIB(dl,dlopen)
+ AC_CHECK_FUNCS(dlsym_prepend_underscore,[CPPFLAGS="$CPPFLAGS -Ddlsym=dlsym_prepend_underscore"])
+
+#Add a system specific charset module.
+
+ default_shared_modules="$default_shared_modules charset_macosxfs"
+ old_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="-Iinclude $CPPFLAGS"
+ AC_CHECK_HEADERS([CoreFoundation/CFStringEncodingConverter.h], [], [AC_CHECK_HEADERS([CFStringEncodingConverter.h])])
+ CPPFLAGS="$old_CPPFLAGS"
+ ;;
+ *hurd*)
+ AC_MSG_CHECKING([for LFS support])
+ old_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="-D_LARGEFILE64_SOURCE -D_GNU_SOURCE $CPPFLAGS"
+ AC_TRY_RUN([
+#include <unistd.h>
+main () {
+#if _LFS64_LARGEFILE == 1
+exit(0);
+#else
+exit(1);
+#endif
+}], [GLIBC_LFS_SUPPORT=yes], [GLIBC_LFS_SUPPORT=no], [GLIBC_LFS_SUPPORT=cross])
+ CPPFLAGS="$old_CPPFLAGS"
+ if test x$GLIBC_LFS_SUPPORT = xyes ; then
+ CPPFLAGS="-D_LARGEFILE64_SOURCE -D_GNU_SOURCE $CPPFLAGS"
+ AC_DEFINE(_LARGEFILE64_SOURCE, 1, [Whether to enable large file support])
+ AC_DEFINE(_GNU_SOURCE, 1, [Whether to use GNU libc extensions])
+ fi
+ AC_MSG_RESULT([$GLIBC_LFS_SUPPORT])
+ ;;
+
+esac
+
+AC_INLINE
+AC_HEADER_STDC
+AC_HEADER_DIRENT
+AC_HEADER_TIME
+AC_HEADER_SYS_WAIT
+AC_CHECK_HEADERS(arpa/inet.h sys/fcntl.h sys/select.h fcntl.h sys/time.h sys/unistd.h)
+AC_CHECK_HEADERS(unistd.h utime.h grp.h sys/id.h limits.h memory.h net/if.h)
+AC_CHECK_HEADERS(rpc/rpc.h rpcsvc/nis.h rpcsvc/yp_prot.h rpcsvc/ypclnt.h)
+AC_CHECK_HEADERS(sys/param.h ctype.h sys/wait.h sys/resource.h sys/ioctl.h sys/ipc.h sys/mode.h)
+AC_CHECK_HEADERS(sys/mman.h sys/filio.h sys/priv.h sys/shm.h string.h strings.h stdlib.h sys/socket.h)
+AC_CHECK_HEADERS(sys/mount.h sys/vfs.h sys/fs/s5param.h sys/filsys.h termios.h termio.h)
+AC_CHECK_HEADERS(sys/termio.h sys/statfs.h sys/dustat.h sys/statvfs.h stdarg.h sys/sockio.h)
+AC_CHECK_HEADERS(security/pam_modules.h security/_pam_macros.h dlfcn.h)
+AC_CHECK_HEADERS(sys/syslog.h syslog.h execinfo.h)
+AC_CHECK_HEADERS(langinfo.h locale.h)
+
+# In valgrind 1.0.x, it's just valgrind.h. In 1.9.x+ there's a
+# subdirectory of headers.
+AC_CHECK_HEADERS(valgrind.h valgrind/valgrind.h valgrind/memcheck.h)
+
+#
+# HPUX has a bug in that including shadow.h causes a re-definition of MAXINT.
+# This causes configure to fail to detect it. Check for shadow separately on HPUX.
+#
+case "$host_os" in
+ *hpux*)
+ AC_TRY_COMPILE([#include <shadow.h>],[struct spwd testme],
+ ac_cv_header_shadow_h=yes,ac_cv_header_shadow_h=no)
+ if test x"$ac_cv_header_shadow_h" = x"yes"; then
+ AC_DEFINE(HAVE_SHADOW_H,1,[Whether we have shadow.h])
+ fi
+ ;;
+esac
+AC_CHECK_HEADERS(shadow.h netinet/ip.h netinet/tcp.h netinet/in_systm.h netinet/in_ip.h)
+AC_CHECK_HEADERS(nss.h nss_common.h nsswitch.h ns_api.h sys/security.h security/pam_appl.h security/pam_modules.h)
+AC_CHECK_HEADERS(stropts.h poll.h)
+AC_CHECK_HEADERS(sys/capability.h syscall.h sys/syscall.h)
+AC_CHECK_HEADERS(sys/acl.h sys/attributes.h attr/xattr.h sys/xattr.h sys/cdefs.h glob.h)
+
+# For experimental utmp support (lastlog on some BSD-like systems)
+AC_CHECK_HEADERS(utmp.h utmpx.h lastlog.h)
+
+AC_CHECK_SIZEOF(int,cross)
+AC_CHECK_SIZEOF(long,cross)
+AC_CHECK_SIZEOF(short,cross)
+
+AC_C_CONST
+AC_C_INLINE
+AC_C_BIGENDIAN
+AC_C_CHAR_UNSIGNED
+
+AC_TYPE_SIGNAL
+AC_TYPE_UID_T
+AC_TYPE_MODE_T
+AC_TYPE_OFF_T
+AC_TYPE_SIZE_T
+AC_TYPE_PID_T
+AC_STRUCT_ST_RDEV
+AC_DIRENT_D_OFF
+AC_CHECK_TYPE(ino_t,unsigned)
+AC_CHECK_TYPE(loff_t,off_t)
+AC_CHECK_TYPE(offset_t,loff_t)
+AC_CHECK_TYPE(ssize_t, int)
+AC_CHECK_TYPE(wchar_t, unsigned short)
+
+############################################
+# for cups support we need libcups, and a handful of header files
+
+AC_ARG_ENABLE(cups,
+[ --enable-cups Turn on CUPS support (default=auto)])
+
+if test x$enable_cups != xno; then
+ AC_PATH_PROG(CUPS_CONFIG, cups-config)
+
+ if test "x$CUPS_CONFIG" != x; then
+ AC_DEFINE(HAVE_CUPS,1,[Whether we have CUPS])
+ CFLAGS="$CFLAGS `$CUPS_CONFIG --cflags`"
+ LDFLAGS="$LDFLAGS `$CUPS_CONFIG --ldflags`"
+ PRINT_LIBS="$PRINT_LIBS `$CUPS_CONFIG --libs`"
+ fi
+fi
+
+############################################
+# we need dlopen/dlclose/dlsym/dlerror for PAM, the password database plugins and the plugin loading code
+AC_SEARCH_LIBS(dlopen, [dl])
+# dlopen/dlclose/dlsym/dlerror will be checked again later and defines will be set then
+
+############################################
+# check if the compiler can do immediate structures
+AC_CACHE_CHECK([for immediate structures],samba_cv_immediate_structures, [
+ AC_TRY_COMPILE([
+#include <stdio.h>],
+[
+ typedef struct {unsigned x;} FOOBAR;
+ #define X_FOOBAR(x) ((FOOBAR) { x })
+ #define FOO_ONE X_FOOBAR(1)
+ FOOBAR f = FOO_ONE;
+ static struct {
+ FOOBAR y;
+ } f2[] = {
+ {FOO_ONE}
+ };
+],
+ samba_cv_immediate_structures=yes,samba_cv_immediate_structures=no)])
+if test x"$samba_cv_immediate_structures" = x"yes"; then
+ AC_DEFINE(HAVE_IMMEDIATE_STRUCTURES,1,[Whether the compiler supports immediate structures])
+fi
+
+############################################
+# check if the compiler can do immediate structures
+AC_CACHE_CHECK([if the compiler will optimize out function calls],samba_cv_optimize_out_funcation_calls, [
+ AC_TRY_LINK([
+#include <stdio.h>],
+[
+ if (0) {
+ this_function_does_not_exist();
+ } else {
+ return 1;
+ }
+
+],
+ samba_cv_optimize_out_funcation_calls=yes,samba_cv_optimize_out_funcation_calls=no)])
+if test x"$samba_cv_optimize_out_funcation_calls" = x"yes"; then
+ AC_DEFINE(HAVE_COMPILER_WILL_OPTIMIZE_OUT_FNS,1,[Whether the compiler will optimize out function calls])
+fi
+
+############################################
+# check for unix domain sockets
+AC_CACHE_CHECK([for unix domain sockets],samba_cv_unixsocket, [
+ AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/socket.h>
+#include <sys/un.h>],
+[
+ struct sockaddr_un sunaddr;
+ sunaddr.sun_family = AF_UNIX;
+],
+ samba_cv_unixsocket=yes,samba_cv_unixsocket=no)])
+if test x"$samba_cv_unixsocket" = x"yes"; then
+ AC_DEFINE(HAVE_UNIXSOCKET,1,[If we need to build with unixscoket support])
+fi
+
+
+AC_CACHE_CHECK([for socklen_t type],samba_cv_socklen_t, [
+ AC_TRY_COMPILE([
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+#include <sys/socket.h>],[socklen_t i = 0],
+ samba_cv_socklen_t=yes,samba_cv_socklen_t=no)])
+if test x"$samba_cv_socklen_t" = x"yes"; then
+ AC_DEFINE(HAVE_SOCKLEN_T_TYPE,1,[Whether we have the variable type socklen_t])
+fi
+
+AC_CACHE_CHECK([for sig_atomic_t type],samba_cv_sig_atomic_t, [
+ AC_TRY_COMPILE([
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+#include <signal.h>],[sig_atomic_t i = 0],
+ samba_cv_sig_atomic_t=yes,samba_cv_sig_atomic_t=no)])
+if test x"$samba_cv_sig_atomic_t" = x"yes"; then
+ AC_DEFINE(HAVE_SIG_ATOMIC_T_TYPE,1,[Whether we have the atomic_t variable type])
+fi
+
+# stupid headers have the functions but no declaration. grrrr.
+AC_HAVE_DECL(errno, [#include <errno.h>])
+AC_HAVE_DECL(setresuid, [#include <unistd.h>])
+AC_HAVE_DECL(setresgid, [#include <unistd.h>])
+AC_HAVE_DECL(asprintf, [#include <stdio.h>])
+AC_HAVE_DECL(vasprintf, [#include <stdio.h>])
+AC_HAVE_DECL(vsnprintf, [#include <stdio.h>])
+AC_HAVE_DECL(snprintf, [#include <stdio.h>])
+
+# and glibc has setresuid under linux but the function does
+# nothing until kernel 2.1.44! very dumb.
+AC_CACHE_CHECK([for real setresuid],samba_cv_have_setresuid,[
+ AC_TRY_RUN([#include <errno.h>
+main() { setresuid(1,1,1); setresuid(2,2,2); exit(errno==EPERM?0:1);}],
+ samba_cv_have_setresuid=yes,samba_cv_have_setresuid=no,samba_cv_have_setresuid=cross)])
+if test x"$samba_cv_have_setresuid" = x"yes"; then
+ AC_DEFINE(HAVE_SETRESUID,1,[Whether the system has setresuid])
+fi
+
+# Do the same check for setresguid...
+#
+AC_CACHE_CHECK([for real setresgid],samba_cv_have_setresgid,[
+ AC_TRY_RUN([#include <unistd.h>
+#include <errno.h>
+main() { errno = 0; setresgid(1,1,1); exit(errno != 0 ? (errno==EPERM ? 0 : 1) : 0);}],
+ samba_cv_have_setresgid=yes,samba_cv_have_setresgid=no,samba_cv_have_setresgid=cross)])
+if test x"$samba_cv_have_setresgid" = x"yes"; then
+ AC_DEFINE(HAVE_SETRESGID,1,[Whether the system has setresgid])
+fi
+
+AC_FUNC_MEMCMP
+
+###############################################
+# Readline included by default unless explicitly asked not to
+test "${with_readline+set}" != "set" && with_readline=yes
+
+# test for where we get readline() from
+AC_MSG_CHECKING(whether to use readline)
+AC_ARG_WITH(readline,
+[ --with-readline[=DIR] Look for readline include/libs in DIR (default=auto) ],
+[ case "$with_readline" in
+ yes)
+ AC_MSG_RESULT(yes)
+
+ AC_CHECK_HEADERS(readline.h history.h readline/readline.h)
+ AC_CHECK_HEADERS(readline/history.h)
+
+ AC_CHECK_HEADERS(readline.h readline/readline.h,[
+ for termlib in ncurses curses termcap terminfo termlib tinfo; do
+ AC_CHECK_LIB(${termlib}, tgetent, [TERMLIBS="-l${termlib}"; break])
+ done
+ AC_CHECK_LIB(readline, rl_callback_handler_install,
+ [TERMLIBS="-lreadline $TERMLIBS"
+ AC_DEFINE(HAVE_LIBREADLINE,1,[Whether the system has readline])
+ break], [TERMLIBS=], $TERMLIBS)])
+ ;;
+ no)
+ AC_MSG_RESULT(no)
+ ;;
+ *)
+ AC_MSG_RESULT(yes)
+
+ # Needed for AC_CHECK_HEADERS and AC_CHECK_LIB to look at
+ # alternate readline path
+ _ldflags=${LDFLAGS}
+ _cppflags=${CPPFLAGS}
+
+ # Add additional search path
+ LDFLAGS="-L$with_readline/lib $LDFLAGS"
+ CPPFLAGS="-I$with_readline/include $CPPFLAGS"
+
+ AC_CHECK_HEADERS(readline.h history.h readline/readline.h)
+ AC_CHECK_HEADERS(readline/history.h)
+
+ AC_CHECK_HEADERS(readline.h readline/readline.h,[
+ for termlib in ncurses curses termcap terminfo termlib; do
+ AC_CHECK_LIB(${termlib}, tgetent, [TERMLIBS="-l${termlib}"; break])
+ done
+ AC_CHECK_LIB(readline, rl_callback_handler_install,
+ [TERMLDFLAGS="-L$with_readline/lib"
+ TERMCPPFLAGS="-I$with_readline/include"
+ CPPFLAGS="-I$with_readline/include $CPPFLAGS"
+ TERMLIBS="-lreadline $TERMLIBS"
+ AC_DEFINE(HAVE_LIBREADLINE,1,[Whether the system has readline])
+ break], [TERMLIBS= CPPFLAGS=$_cppflags], $TERMLIBS)])
+
+ LDFLAGS=$_ldflags
+ ;;
+ esac],
+ AC_MSG_RESULT(no)
+)
+AC_SUBST(TERMLIBS)
+AC_SUBST(TERMLDFLAGS)
+
+# The readline API changed slightly from readline3 to readline4, so
+# code will generate warnings on one of them unless we have a few
+# special cases.
+AC_CHECK_LIB(readline, rl_completion_matches,
+ [AC_DEFINE(HAVE_NEW_LIBREADLINE, 1,
+ [Do we have rl_completion_matches?])],
+ [],
+ [$TERMLIBS])
+
+# The following test taken from the cvs sources
+# If we can't find connect, try looking in -lsocket, -lnsl, and -linet.
+# The Irix 5 libc.so has connect and gethostbyname, but Irix 5 also has
+# libsocket.so which has a bad implementation of gethostbyname (it
+# only looks in /etc/hosts), so we only look for -lsocket if we need
+# it.
+AC_CHECK_FUNCS(connect)
+if test x"$ac_cv_func_connect" = x"no"; then
+ case "$LIBS" in
+ *-lnsl*) ;;
+ *) AC_CHECK_LIB(nsl_s, printf) ;;
+ esac
+ case "$LIBS" in
+ *-lnsl*) ;;
+ *) AC_CHECK_LIB(nsl, printf) ;;
+ esac
+ case "$LIBS" in
+ *-lsocket*) ;;
+ *) AC_CHECK_LIB(socket, connect) ;;
+ esac
+ case "$LIBS" in
+ *-linet*) ;;
+ *) AC_CHECK_LIB(inet, connect) ;;
+ esac
+ dnl We can't just call AC_CHECK_FUNCS(connect) here, because the value
+ dnl has been cached.
+ if test x"$ac_cv_lib_socket_connect" = x"yes" ||
+ test x"$ac_cv_lib_inet_connect" = x"yes"; then
+ # ac_cv_func_connect=yes
+ # don't! it would cause AC_CHECK_FUNC to succeed next time configure is run
+ AC_DEFINE(HAVE_CONNECT,1,[Whether the system has connect()])
+ fi
+fi
+
+###############################################
+# test for where we get yp_get_default_domain() from
+AC_SEARCH_LIBS(yp_get_default_domain, [nsl])
+AC_CHECK_FUNCS(yp_get_default_domain)
+
+# Check if we have execl, if not we need to compile smbrun.
+AC_CHECK_FUNCS(execl)
+if test x"$ac_cv_func_execl" = x"no"; then
+ EXTRA_BIN_PROGS="$EXTRA_BIN_PROGS bin/smbrun\$(EXEEXT)"
+fi
+
+AC_CHECK_FUNCS(dlopen dlclose dlsym dlerror waitpid getcwd strdup strndup strnlen strtoul strerror chown fchown chmod fchmod chroot link mknod mknod64)
+AC_CHECK_FUNCS(fstat strchr utime utimes getrlimit fsync bzero memset strlcpy strlcat setpgid)
+AC_CHECK_FUNCS(memmove vsnprintf snprintf asprintf vasprintf setsid glob strpbrk pipe crypt16 getauthuid)
+AC_CHECK_FUNCS(strftime sigprocmask sigblock sigaction sigset innetgr setnetgrent getnetgrent endnetgrent)
+AC_CHECK_FUNCS(initgroups select poll rdchk getgrnam getgrent pathconf realpath)
+AC_CHECK_FUNCS(setpriv setgidx setuidx setgroups sysconf mktime rename ftruncate stat64 fstat64)
+AC_CHECK_FUNCS(lstat64 fopen64 atexit grantpt dup2 lseek64 ftruncate64 readdir64)
+AC_CHECK_FUNCS(fseek64 fseeko64 ftell64 ftello64 setluid getpwanam setlinebuf)
+AC_CHECK_FUNCS(srandom random srand rand setenv usleep strcasecmp fcvt fcvtl symlink readlink)
+AC_CHECK_FUNCS(syslog vsyslog timegm)
+AC_CHECK_FUNCS(setlocale nl_langinfo)
+# setbuffer, shmget, shm_open are needed for smbtorture
+AC_CHECK_FUNCS(setbuffer shmget shm_open backtrace_symbols)
+AC_CHECK_HEADERS(libexc.h)
+AC_CHECK_LIB(exc, trace_back_stack)
+
+# syscall() is needed for smbwrapper.
+AC_CHECK_FUNCS(syscall)
+
+AC_CHECK_FUNCS(_dup _dup2 _opendir _readdir _seekdir _telldir _closedir)
+AC_CHECK_FUNCS(__dup __dup2 __opendir __readdir __seekdir __telldir __closedir)
+AC_CHECK_FUNCS(__getcwd _getcwd)
+AC_CHECK_FUNCS(__xstat __fxstat __lxstat)
+AC_CHECK_FUNCS(_stat _lstat _fstat __stat __lstat __fstat)
+AC_CHECK_FUNCS(_acl __acl _facl __facl _open __open _chdir __chdir)
+AC_CHECK_FUNCS(_close __close _fchdir __fchdir _fcntl __fcntl)
+AC_CHECK_FUNCS(getdents _getdents __getdents _lseek __lseek _read __read)
+AC_CHECK_FUNCS(getdirentries _write __write _fork __fork)
+AC_CHECK_FUNCS(_stat64 __stat64 _fstat64 __fstat64 _lstat64 __lstat64)
+AC_CHECK_FUNCS(__sys_llseek llseek _llseek __llseek readdir64 _readdir64 __readdir64)
+AC_CHECK_FUNCS(pread _pread __pread pread64 _pread64 __pread64)
+AC_CHECK_FUNCS(pwrite _pwrite __pwrite pwrite64 _pwrite64 __pwrite64)
+AC_CHECK_FUNCS(open64 _open64 __open64 creat64)
+
+#
+#
+#
+case "$host_os" in
+ *linux*)
+ # glibc <= 2.3.2 has a broken getgrouplist
+ AC_TRY_RUN([
+#include <unistd.h>
+#include <sys/utsname.h>
+main() {
+ /* glibc up to 2.3 has a broken getgrouplist */
+#if defined(__GLIBC__) && defined(__GLIBC_MINOR__)
+ int libc_major = __GLIBC__;
+ int libc_minor = __GLIBC_MINOR__;
+
+ if (libc_major < 2)
+ exit(1);
+ if ((libc_major == 2) && (libc_minor <= 3))
+ exit(1);
+#endif
+ exit(0);
+}
+], [linux_getgrouplist_ok=yes], [linux_getgrouplist_ok=no])
+ if test x"$linux_getgrouplist_ok" = x"yes"; then
+ AC_DEFINE(HAVE_GETGROUPLIST, 1, [Have good getgrouplist])
+ fi
+ ;;
+ *)
+ AC_CHECK_FUNCS(getgrouplist)
+ ;;
+esac
+
+#
+# stat64 family may need <sys/stat.h> on some systems, notably ReliantUNIX
+#
+
+if test x$ac_cv_func_stat64 = xno ; then
+ AC_MSG_CHECKING([for stat64 in <sys/stat.h>])
+ AC_TRY_LINK([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <sys/stat.h>
+], [struct stat64 st64; exit(stat64(".",&st64));], [ac_cv_func_stat64=yes])
+ AC_MSG_RESULT([$ac_cv_func_stat64])
+ if test x$ac_cv_func_stat64 = xyes ; then
+ AC_DEFINE(HAVE_STAT64,1,[Whether stat64() is available])
+ fi
+fi
+
+if test x$ac_cv_func_lstat64 = xno ; then
+ AC_MSG_CHECKING([for lstat64 in <sys/stat.h>])
+ AC_TRY_LINK([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <sys/stat.h>
+], [struct stat64 st64; exit(lstat64(".",&st64));], [ac_cv_func_lstat64=yes])
+ AC_MSG_RESULT([$ac_cv_func_lstat64])
+ if test x$ac_cv_func_lstat64 = xyes ; then
+ AC_DEFINE(HAVE_LSTAT64,[Whether lstat64() is available])
+ fi
+fi
+
+if test x$ac_cv_func_fstat64 = xno ; then
+ AC_MSG_CHECKING([for fstat64 in <sys/stat.h>])
+ AC_TRY_LINK([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <sys/stat.h>
+], [struct stat64 st64; exit(fstat64(0,&st64));], [ac_cv_func_fstat64=yes])
+ AC_MSG_RESULT([$ac_cv_func_fstat64])
+ if test x$ac_cv_func_fstat64 = xyes ; then
+ AC_DEFINE(HAVE_FSTAT64,1,[Whether fstat64() is available])
+ fi
+fi
+
+#####################################
+# we might need the resolv library on some systems
+AC_CHECK_LIB(resolv, dn_expand)
+
+#
+# Check for the functions putprpwnam, set_auth_parameters,
+# getspnam, bigcrypt and getprpwnam in -lsec and -lsecurity
+# Needed for OSF1 and HPUX.
+#
+
+AC_LIBTESTFUNC(security, putprpwnam)
+AC_LIBTESTFUNC(sec, putprpwnam)
+
+AC_LIBTESTFUNC(security, set_auth_parameters)
+AC_LIBTESTFUNC(sec, set_auth_parameters)
+
+# UnixWare 7.x has its getspnam in -lgen
+AC_LIBTESTFUNC(gen, getspnam)
+
+AC_LIBTESTFUNC(security, getspnam)
+AC_LIBTESTFUNC(sec, getspnam)
+
+AC_LIBTESTFUNC(security, bigcrypt)
+AC_LIBTESTFUNC(sec, bigcrypt)
+
+AC_LIBTESTFUNC(security, getprpwnam)
+AC_LIBTESTFUNC(sec, getprpwnam)
+
+############################################
+# Check if we have libattr
+AC_SEARCH_LIBS(getxattr, [attr])
+AC_CHECK_FUNCS(getxattr lgetxattr fgetxattr listxattr llistxattr)
+AC_CHECK_FUNCS(flistxattr removexattr lremovexattr fremovexattr)
+AC_CHECK_FUNCS(setxattr lsetxattr fsetxattr)
+AC_CHECK_FUNCS(attr_get attr_list attr_set attr_remove)
+AC_CHECK_FUNCS(attr_getf attr_listf attr_setf attr_removef)
+
+# Assume non-shared by default and override below
+BLDSHARED="false"
+
+# these are the defaults, good for lots of systems
+HOST_OS="$host_os"
+LDSHFLAGS="-shared"
+SONAMEFLAG="#"
+SHLD="\${CC} \${CFLAGS}"
+PICFLAGS=""
+PICSUFFIX="po"
+SHLIBEXT="so"
+
+if test "$enable_shared" = "yes"; then
+ # this bit needs to be modified for each OS that is suported by
+ # smbwrapper. You need to specify how to created a shared library and
+ # how to compile C code to produce PIC object files
+
+ AC_MSG_CHECKING([ability to build shared libraries])
+
+ # and these are for particular systems
+ case "$host_os" in
+ *linux*) AC_DEFINE(LINUX,1,[Whether the host os is linux])
+ BLDSHARED="true"
+ LDSHFLAGS="-shared"
+ DYNEXP="-Wl,--export-dynamic"
+ PICFLAGS="-fPIC"
+ SONAMEFLAG="-Wl,-soname="
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+ ;;
+ *solaris*) AC_DEFINE(SUNOS5,1,[Whether the host os is solaris])
+ BLDSHARED="true"
+ LDSHFLAGS="-G"
+ SONAMEFLAG="-h "
+ if test "${GCC}" = "yes"; then
+ PICFLAGS="-fPIC"
+ if test "${ac_cv_prog_gnu_ld}" = "yes"; then
+ DYNEXP="-Wl,-E"
+ fi
+ else
+ PICFLAGS="-KPIC"
+ ## ${CFLAGS} added for building 64-bit shared
+ ## libs using Sun's Compiler
+ LDSHFLAGS="-G \${CFLAGS}"
+ PICSUFFIX="po.o"
+ fi
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512,[The size of a block])
+ AC_DEFINE(BROKEN_GETGRNAM,1,[Does getgrnam work correctly])
+ ;;
+ *sunos*) AC_DEFINE(SUNOS4,1,[Whether the host os is sunos4])
+ BLDSHARED="true"
+ LDSHFLAGS="-G"
+ SONAMEFLAG="-Wl,-h,"
+ PICFLAGS="-KPIC" # Is this correct for SunOS
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+ AC_DEFINE(BROKEN_GETGRNAM,1,[Does getgrnam work correctly])
+ ;;
+ *netbsd* | *freebsd*) BLDSHARED="true"
+ LDSHFLAGS="-shared"
+ DYNEXP="-Wl,--export-dynamic"
+ SONAMEFLAG="-Wl,-soname,"
+ PICFLAGS="-fPIC -DPIC"
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512,[The size of a block])
+ AC_DEFINE(BROKEN_GETGRNAM,1,[Does getgrnam work correctly])
+ ;;
+ *openbsd*) BLDSHARED="true"
+ LDSHFLAGS="-shared"
+ DYNEXP="-Wl,-Bdynamic"
+ SONAMEFLAG="-Wl,-soname,"
+ PICFLAGS="-fPIC"
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512,[The size of a block])
+ AC_DEFINE(BROKEN_GETGRNAM,1,[Does getgrnam work correctly])
+ ;;
+ *irix*) AC_DEFINE(IRIX,1,[Whether the host os is irix])
+ case "$host_os" in
+ *irix6*) AC_DEFINE(IRIX6,1,[Whether the host os is irix6])
+ ;;
+ esac
+ BLDSHARED="true"
+ LDSHFLAGS="-set_version sgi1.0 -shared"
+ SONAMEFLAG="-soname "
+ SHLD="\${LD}"
+ if test "${GCC}" = "yes"; then
+ PICFLAGS="-fPIC"
+ else
+ PICFLAGS="-KPIC"
+ fi
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512,[The size of a block])
+ ;;
+ *aix*) AC_DEFINE(AIX,1,[Whether the host os is aix])
+ BLDSHARED="true"
+ LDSHFLAGS="-Wl,-bexpall,-bM:SRE,-bnoentry,-berok"
+ DYNEXP="-Wl,-brtl,-bexpall"
+ PICFLAGS="-O2"
+ if test "${GCC}" != "yes"; then
+ ## for funky AIX compiler using strncpy()
+ CFLAGS="$CFLAGS -D_LINUX_SOURCE_COMPAT -qmaxmem=32000"
+ fi
+
+ AC_DEFINE(STAT_ST_BLOCKSIZE,DEV_BSIZE,[The size of a block])
+ ;;
+ *hpux*) AC_DEFINE(HPUX,1,[Whether the host os is HPUX])
+ SHLIBEXT="sl"
+ # Use special PIC flags for the native HP-UX compiler.
+ if test $ac_cv_prog_cc_Ae = yes; then
+ BLDSHARED="true"
+ SHLD="/usr/bin/ld"
+ LDSHFLAGS="-B symbolic -b -z"
+ SONAMEFLAG="+h "
+ PICFLAGS="+z"
+ elif test "${GCC}" = "yes"; then
+ PICFLAGS="-fPIC"
+ fi
+ DYNEXP="-Wl,-E"
+ AC_DEFINE(STAT_ST_BLOCKSIZE,8192,[The size of a block])
+ AC_DEFINE(POSIX_ACL_NEEDS_MASK,1,[Does a POSIX ACL need a mask element])
+ ;;
+ *qnx*) AC_DEFINE(QNX,1,[Whether the host os is qnx])
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+ ;;
+ *osf*) AC_DEFINE(OSF1,1,[Whether the host os is osf1])
+ BLDSHARED="true"
+ LDSHFLAGS="-shared"
+ SONAMEFLAG="-Wl,-soname,"
+ PICFLAGS="-fPIC"
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+ AC_DEFINE(BROKEN_GETGRNAM,1,[Does getgrnam work correctly])
+ ;;
+ *sco*) AC_DEFINE(SCO,1,[Whether the host os is sco unix])
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+ ;;
+ *unixware*) AC_DEFINE(UNIXWARE,1,[Whether the host os is unixware])
+ BLDSHARED="true"
+ LDSHFLAGS="-shared"
+ SONAMEFLAG="-Wl,-soname,"
+ PICFLAGS="-KPIC"
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+ ;;
+ *next2*) AC_DEFINE(NEXT2,1,[Whether the host os is NeXT v2])
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+ ;;
+ *dgux*) AC_CHECK_PROG( ROFF, groff, [groff -etpsR -Tascii -man])
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+ ;;
+ *sysv4*) AC_DEFINE(SYSV,1,[Whether this is a system V system])
+ case "$host" in
+ *-univel-*) if [ test "$GCC" != yes ]; then
+ AC_DEFINE(HAVE_MEMSET,1,[Whether memset() is available])
+ fi
+ LDSHFLAGS="-G"
+ DYNEXP="-Bexport"
+ ;;
+ *mips-sni-sysv4*) AC_DEFINE(RELIANTUNIX,1,[Whether the host os is reliantunix]);;
+ esac
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+ ;;
+
+ *sysv5*) AC_DEFINE(SYSV,1,[Whether this is a system V system])
+ if [ test "$GCC" != yes ]; then
+ AC_DEFINE(HAVE_MEMSET,1,[Whether memset() is available])
+ fi
+ LDSHFLAGS="-G"
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+ ;;
+ *vos*) AC_DEFINE(STAT_ST_BLOCKSIZE,4096)
+ BLDSHARED="false"
+ LDSHFLAGS=""
+ ;;
+
+ *darwin*) AC_DEFINE(DARWINOS,1,[Whether the host os is Darwin/MacOSX])
+ BLDSHARED="true"
+ LDSHFLAGS="-bundle -flat_namespace -undefined suppress"
+ SHLIBEXT="dylib"
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+ ;;
+
+ *)
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+ ;;
+ esac
+ AC_SUBST(DYNEXP)
+ AC_MSG_RESULT($BLDSHARED)
+ AC_MSG_CHECKING([linker flags for shared libraries])
+ AC_MSG_RESULT([$LDSHFLAGS])
+ AC_MSG_CHECKING([compiler flags for position-independent code])
+ AC_MSG_RESULT([$PICFLAGS])
+fi
+
+#######################################################
+# test whether building a shared library actually works
+if test $BLDSHARED = true; then
+AC_CACHE_CHECK([whether building shared libraries actually works],
+ [ac_cv_shlib_works],[
+ ac_cv_shlib_works=no
+ # try building a trivial shared library
+ $CC $CPPFLAGS $CFLAGS $PICFLAGS -c -o \
+ shlib.$PICSUFFIX ${srcdir-.}/tests/shlib.c && \
+ $CC $CPPFLAGS $CFLAGS `eval echo $LDSHFLAGS` -o "shlib.$SHLIBEXT" \
+ shlib.$PICSUFFIX && \
+ ac_cv_shlib_works=yes
+ rm -f "shlib.$SHLIBEXT" shlib.$PICSUFFIX
+])
+if test $ac_cv_shlib_works = no; then
+ BLDSHARED=false
+fi
+fi
+
+################
+
+AC_CACHE_CHECK([for long long],samba_cv_have_longlong,[
+AC_TRY_RUN([#include <stdio.h>
+main() { long long x = 1000000; x *= x; exit(((x/1000000) == 1000000)? 0: 1); }],
+samba_cv_have_longlong=yes,samba_cv_have_longlong=no,samba_cv_have_longlong=cross)])
+if test x"$samba_cv_have_longlong" = x"yes"; then
+ AC_DEFINE(HAVE_LONGLONG,1,[Whether the host supports long long's])
+fi
+
+#
+# Check if the compiler supports the LL prefix on long long integers.
+# AIX needs this.
+
+AC_CACHE_CHECK([for LL suffix on long long integers],samba_cv_compiler_supports_ll, [
+ AC_TRY_COMPILE([#include <stdio.h>],[long long i = 0x8000000000LL],
+ samba_cv_compiler_supports_ll=yes,samba_cv_compiler_supports_ll=no)])
+if test x"$samba_cv_compiler_supports_ll" = x"yes"; then
+ AC_DEFINE(COMPILER_SUPPORTS_LL,1,[Whether the compiler supports the LL prefix on long long integers])
+fi
+
+
+AC_CACHE_CHECK([for 64 bit off_t],samba_cv_SIZEOF_OFF_T,[
+AC_TRY_RUN([#include <stdio.h>
+#include <sys/stat.h>
+main() { exit((sizeof(off_t) == 8) ? 0 : 1); }],
+samba_cv_SIZEOF_OFF_T=yes,samba_cv_SIZEOF_OFF_T=no,samba_cv_SIZEOF_OFF_T=cross)])
+if test x"$samba_cv_SIZEOF_OFF_T" = x"yes"; then
+ AC_DEFINE(SIZEOF_OFF_T,8,[The size of the 'off_t' type])
+fi
+
+AC_CACHE_CHECK([for off64_t],samba_cv_HAVE_OFF64_T,[
+AC_TRY_RUN([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include <sys/stat.h>
+main() { struct stat64 st; off64_t s; if (sizeof(off_t) == sizeof(off64_t)) exit(1); exit((lstat64("/dev/null", &st)==0)?0:1); }],
+samba_cv_HAVE_OFF64_T=yes,samba_cv_HAVE_OFF64_T=no,samba_cv_HAVE_OFF64_T=cross)])
+if test x"$samba_cv_HAVE_OFF64_T" = x"yes"; then
+ AC_DEFINE(HAVE_OFF64_T,1,[Whether off64_t is available])
+fi
+
+AC_CACHE_CHECK([for 64 bit ino_t],samba_cv_SIZEOF_INO_T,[
+AC_TRY_RUN([#include <stdio.h>
+#include <sys/stat.h>
+main() { exit((sizeof(ino_t) == 8) ? 0 : 1); }],
+samba_cv_SIZEOF_INO_T=yes,samba_cv_SIZEOF_INO_T=no,samba_cv_SIZEOF_INO_T=cross)])
+if test x"$samba_cv_SIZEOF_INO_T" = x"yes"; then
+ AC_DEFINE(SIZEOF_INO_T,8,[The size of the 'ino_t' type])
+fi
+
+AC_CACHE_CHECK([for ino64_t],samba_cv_HAVE_INO64_T,[
+AC_TRY_RUN([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include <sys/stat.h>
+main() { struct stat64 st; ino64_t s; if (sizeof(ino_t) == sizeof(ino64_t)) exit(1); exit((lstat64("/dev/null", &st)==0)?0:1); }],
+samba_cv_HAVE_INO64_T=yes,samba_cv_HAVE_INO64_T=no,samba_cv_HAVE_INO64_T=cross)])
+if test x"$samba_cv_HAVE_INO64_T" = x"yes"; then
+ AC_DEFINE(HAVE_INO64_T,1,[Whether the 'ino64_t' type is available])
+fi
+
+AC_CACHE_CHECK([for dev64_t],samba_cv_HAVE_DEV64_T,[
+AC_TRY_RUN([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include <sys/stat.h>
+main() { struct stat64 st; dev64_t s; if (sizeof(dev_t) == sizeof(dev64_t)) exit(1); exit((lstat64("/dev/null", &st)==0)?0:1); }],
+samba_cv_HAVE_DEV64_T=yes,samba_cv_HAVE_DEV64_T=no,samba_cv_HAVE_DEV64_T=cross)])
+if test x"$samba_cv_HAVE_DEV64_T" = x"yes"; then
+ AC_DEFINE(HAVE_DEV64_T,1,[Whether the 'dev64_t' type is available])
+fi
+
+AC_CACHE_CHECK([for struct dirent64],samba_cv_HAVE_STRUCT_DIRENT64,[
+AC_TRY_COMPILE([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+#include <dirent.h>],
+[struct dirent64 de;],
+samba_cv_HAVE_STRUCT_DIRENT64=yes,samba_cv_HAVE_STRUCT_DIRENT64=no)])
+if test x"$samba_cv_HAVE_STRUCT_DIRENT64" = x"yes" && test x"$ac_cv_func_readdir64" = x"yes"; then
+ AC_DEFINE(HAVE_STRUCT_DIRENT64,1,[Whether the 'dirent64' struct is available])
+fi
+
+AC_CACHE_CHECK([for major macro],samba_cv_HAVE_DEVICE_MAJOR_FN,[
+AC_TRY_RUN([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+main() { dev_t dev; int i = major(dev); return 0; }],
+samba_cv_HAVE_DEVICE_MAJOR_FN=yes,samba_cv_HAVE_DEVICE_MAJOR_FN=no,samba_cv_HAVE_DEVICE_MAJOR_FN=cross)])
+if test x"$samba_cv_HAVE_DEVICE_MAJOR_FN" = x"yes"; then
+ AC_DEFINE(HAVE_DEVICE_MAJOR_FN,1,[Whether the major macro for dev_t is available])
+fi
+
+AC_CACHE_CHECK([for minor macro],samba_cv_HAVE_DEVICE_MINOR_FN,[
+AC_TRY_RUN([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+main() { dev_t dev; int i = minor(dev); return 0; }],
+samba_cv_HAVE_DEVICE_MINOR_FN=yes,samba_cv_HAVE_DEVICE_MINOR_FN=no,samba_cv_HAVE_DEVICE_MINOR_FN=cross)])
+if test x"$samba_cv_HAVE_DEVICE_MINOR_FN" = x"yes"; then
+ AC_DEFINE(HAVE_DEVICE_MINOR_FN,1,[Whether the minor macro for dev_t is available])
+fi
+
+AC_CACHE_CHECK([for unsigned char],samba_cv_HAVE_UNSIGNED_CHAR,[
+AC_TRY_RUN([#include <stdio.h>
+main() { char c; c=250; exit((c > 0)?0:1); }],
+samba_cv_HAVE_UNSIGNED_CHAR=yes,samba_cv_HAVE_UNSIGNED_CHAR=no,samba_cv_HAVE_UNSIGNED_CHAR=cross)])
+if test x"$samba_cv_HAVE_UNSIGNED_CHAR" = x"yes"; then
+ AC_DEFINE(HAVE_UNSIGNED_CHAR,1,[Whether the 'unsigned char' type is available])
+fi
+
+AC_CACHE_CHECK([for sin_len in sock],samba_cv_HAVE_SOCK_SIN_LEN,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>],
+[struct sockaddr_in sock; sock.sin_len = sizeof(sock);],
+samba_cv_HAVE_SOCK_SIN_LEN=yes,samba_cv_HAVE_SOCK_SIN_LEN=no)])
+if test x"$samba_cv_HAVE_SOCK_SIN_LEN" = x"yes"; then
+ AC_DEFINE(HAVE_SOCK_SIN_LEN,1,[Whether the sockaddr_in struct has a sin_len property])
+fi
+
+AC_CACHE_CHECK([whether seekdir returns void],samba_cv_SEEKDIR_RETURNS_VOID,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <dirent.h>
+void seekdir(DIR *d, long loc) { return; }],[return 0;],
+samba_cv_SEEKDIR_RETURNS_VOID=yes,samba_cv_SEEKDIR_RETURNS_VOID=no)])
+if test x"$samba_cv_SEEKDIR_RETURNS_VOID" = x"yes"; then
+ AC_DEFINE(SEEKDIR_RETURNS_VOID,1,[Whether seekdir returns void])
+fi
+
+AC_CACHE_CHECK([for __FUNCTION__ macro],samba_cv_HAVE_FUNCTION_MACRO,[
+AC_TRY_COMPILE([#include <stdio.h>], [printf("%s\n", __FUNCTION__);],
+samba_cv_HAVE_FUNCTION_MACRO=yes,samba_cv_HAVE_FUNCTION_MACRO=no)])
+if test x"$samba_cv_HAVE_FUNCTION_MACRO" = x"yes"; then
+ AC_DEFINE(HAVE_FUNCTION_MACRO,1,[Whether there is a __FUNCTION__ macro])
+fi
+
+AC_CACHE_CHECK([if gettimeofday takes tz argument],samba_cv_HAVE_GETTIMEOFDAY_TZ,[
+AC_TRY_RUN([
+#include <sys/time.h>
+#include <unistd.h>
+main() { struct timeval tv; exit(gettimeofday(&tv, NULL));}],
+ samba_cv_HAVE_GETTIMEOFDAY_TZ=yes,samba_cv_HAVE_GETTIMEOFDAY_TZ=no,samba_cv_HAVE_GETTIMEOFDAY_TZ=cross)])
+if test x"$samba_cv_HAVE_GETTIMEOFDAY_TZ" = x"yes"; then
+ AC_DEFINE(HAVE_GETTIMEOFDAY_TZ,1,[Whether gettimeofday() is available])
+fi
+
+AC_CACHE_CHECK([for va_copy],samba_cv_HAVE_VA_COPY,[
+AC_TRY_LINK([#include <stdarg.h>
+va_list ap1,ap2;], [va_copy(ap1,ap2);],
+samba_cv_HAVE_VA_COPY=yes,
+samba_cv_HAVE_VA_COPY=no)])
+if test x"$samba_cv_HAVE_VA_COPY" = x"yes"; then
+ AC_DEFINE(HAVE_VA_COPY,1,[Whether va_copy() is available])
+else
+ AC_CACHE_CHECK([for __va_copy],samba_cv_HAVE___VA_COPY,[
+ AC_TRY_LINK([#include <stdarg.h>
+ va_list ap1,ap2;], [__va_copy(ap1,ap2);],
+ samba_cv_HAVE___VA_COPY=yes,
+ samba_cv_HAVE___VA_COPY=no)])
+ if test x"$samba_cv_HAVE___VA_COPY" = x"yes"; then
+ AC_DEFINE(HAVE___VA_COPY,1,[Whether __va_copy() is available])
+ fi
+fi
+
+AC_CACHE_CHECK([for C99 vsnprintf],samba_cv_HAVE_C99_VSNPRINTF,[
+AC_TRY_RUN([
+#include <sys/types.h>
+#include <stdarg.h>
+void foo(const char *format, ...) {
+ va_list ap;
+ int len;
+ char buf[5];
+
+ va_start(ap, format);
+ len = vsnprintf(buf, 0, format, ap);
+ va_end(ap);
+ if (len != 5) exit(1);
+
+ va_start(ap, format);
+ len = vsnprintf(0, 0, format, ap);
+ va_end(ap);
+ if (len != 5) exit(1);
+
+ if (snprintf(buf, 3, "hello") != 5 || strcmp(buf, "he") != 0) exit(1);
+
+ exit(0);
+}
+main() { foo("hello"); }
+],
+samba_cv_HAVE_C99_VSNPRINTF=yes,samba_cv_HAVE_C99_VSNPRINTF=no,samba_cv_HAVE_C99_VSNPRINTF=cross)])
+if test x"$samba_cv_HAVE_C99_VSNPRINTF" = x"yes"; then
+ AC_DEFINE(HAVE_C99_VSNPRINTF,1,[Whether there is a C99 compliant vsnprintf])
+fi
+
+AC_CACHE_CHECK([for broken readdir],samba_cv_HAVE_BROKEN_READDIR,[
+AC_TRY_RUN([#include <sys/types.h>
+#include <dirent.h>
+main() { struct dirent *di; DIR *d = opendir("."); di = readdir(d);
+if (di && di->d_name[-2] == '.' && di->d_name[-1] == 0 &&
+di->d_name[0] == 0) exit(0); exit(1);} ],
+samba_cv_HAVE_BROKEN_READDIR=yes,samba_cv_HAVE_BROKEN_READDIR=no,samba_cv_HAVE_BROKEN_READDIR=cross)])
+if test x"$samba_cv_HAVE_BROKEN_READDIR" = x"yes"; then
+ AC_DEFINE(HAVE_BROKEN_READDIR,1,[Whether readdir() is broken])
+fi
+
+AC_CACHE_CHECK([for utimbuf],samba_cv_HAVE_UTIMBUF,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utime.h>],
+[struct utimbuf tbuf; tbuf.actime = 0; tbuf.modtime = 1; exit(utime("foo.c",&tbuf));],
+samba_cv_HAVE_UTIMBUF=yes,samba_cv_HAVE_UTIMBUF=no,samba_cv_HAVE_UTIMBUF=cross)])
+if test x"$samba_cv_HAVE_UTIMBUF" = x"yes"; then
+ AC_DEFINE(HAVE_UTIMBUF,1,[Whether struct utimbuf is available])
+fi
+
+##############
+# Check utmp details, but only if our OS offers utmp.h
+if test x"$ac_cv_header_utmp_h" = x"yes"; then
+dnl utmp and utmpx come in many flavours
+dnl We need to check for many of them
+dnl But we don't need to do each and every one, because our code uses
+dnl mostly just the utmp (not utmpx) fields.
+
+AC_CHECK_FUNCS(pututline pututxline updwtmp updwtmpx getutmpx)
+
+AC_CACHE_CHECK([for ut_name in utmp],samba_cv_HAVE_UT_UT_NAME,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut; ut.ut_name[0] = 'a';],
+samba_cv_HAVE_UT_UT_NAME=yes,samba_cv_HAVE_UT_UT_NAME=no,samba_cv_HAVE_UT_UT_NAME=cross)])
+if test x"$samba_cv_HAVE_UT_UT_NAME" = x"yes"; then
+ AC_DEFINE(HAVE_UT_UT_NAME,1,[Whether the utmp struct has a property ut_name])
+fi
+
+AC_CACHE_CHECK([for ut_user in utmp],samba_cv_HAVE_UT_UT_USER,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut; ut.ut_user[0] = 'a';],
+samba_cv_HAVE_UT_UT_USER=yes,samba_cv_HAVE_UT_UT_USER=no,samba_cv_HAVE_UT_UT_USER=cross)])
+if test x"$samba_cv_HAVE_UT_UT_USER" = x"yes"; then
+ AC_DEFINE(HAVE_UT_UT_USER,1,[Whether the utmp struct has a property ut_user])
+fi
+
+AC_CACHE_CHECK([for ut_id in utmp],samba_cv_HAVE_UT_UT_ID,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut; ut.ut_id[0] = 'a';],
+samba_cv_HAVE_UT_UT_ID=yes,samba_cv_HAVE_UT_UT_ID=no,samba_cv_HAVE_UT_UT_ID=cross)])
+if test x"$samba_cv_HAVE_UT_UT_ID" = x"yes"; then
+ AC_DEFINE(HAVE_UT_UT_ID,1,[Whether the utmp struct has a property ut_id])
+fi
+
+AC_CACHE_CHECK([for ut_host in utmp],samba_cv_HAVE_UT_UT_HOST,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut; ut.ut_host[0] = 'a';],
+samba_cv_HAVE_UT_UT_HOST=yes,samba_cv_HAVE_UT_UT_HOST=no,samba_cv_HAVE_UT_UT_HOST=cross)])
+if test x"$samba_cv_HAVE_UT_UT_HOST" = x"yes"; then
+ AC_DEFINE(HAVE_UT_UT_HOST,1,[Whether the utmp struct has a property ut_host])
+fi
+
+AC_CACHE_CHECK([for ut_time in utmp],samba_cv_HAVE_UT_UT_TIME,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut; time_t t; ut.ut_time = t;],
+samba_cv_HAVE_UT_UT_TIME=yes,samba_cv_HAVE_UT_UT_TIME=no,samba_cv_HAVE_UT_UT_TIME=cross)])
+if test x"$samba_cv_HAVE_UT_UT_TIME" = x"yes"; then
+ AC_DEFINE(HAVE_UT_UT_TIME,1,[Whether the utmp struct has a property ut_time])
+fi
+
+AC_CACHE_CHECK([for ut_tv in utmp],samba_cv_HAVE_UT_UT_TV,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut; struct timeval tv; ut.ut_tv = tv;],
+samba_cv_HAVE_UT_UT_TV=yes,samba_cv_HAVE_UT_UT_TV=no,samba_cv_HAVE_UT_UT_TV=cross)])
+if test x"$samba_cv_HAVE_UT_UT_TV" = x"yes"; then
+ AC_DEFINE(HAVE_UT_UT_TV,1,[Whether the utmp struct has a property ut_tv])
+fi
+
+AC_CACHE_CHECK([for ut_type in utmp],samba_cv_HAVE_UT_UT_TYPE,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut; ut.ut_type = 0;],
+samba_cv_HAVE_UT_UT_TYPE=yes,samba_cv_HAVE_UT_UT_TYPE=no,samba_cv_HAVE_UT_UT_TYPE=cross)])
+if test x"$samba_cv_HAVE_UT_UT_TYPE" = x"yes"; then
+ AC_DEFINE(HAVE_UT_UT_TYPE,1,[Whether the utmp struct has a property ut_type])
+fi
+
+AC_CACHE_CHECK([for ut_pid in utmp],samba_cv_HAVE_UT_UT_PID,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut; ut.ut_pid = 0;],
+samba_cv_HAVE_UT_UT_PID=yes,samba_cv_HAVE_UT_UT_PID=no,samba_cv_HAVE_UT_UT_PID=cross)])
+if test x"$samba_cv_HAVE_UT_UT_PID" = x"yes"; then
+ AC_DEFINE(HAVE_UT_UT_PID,1,[Whether the utmp struct has a property ut_pid])
+fi
+
+AC_CACHE_CHECK([for ut_exit in utmp],samba_cv_HAVE_UT_UT_EXIT,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut; ut.ut_exit.e_exit = 0;],
+samba_cv_HAVE_UT_UT_EXIT=yes,samba_cv_HAVE_UT_UT_EXIT=no,samba_cv_HAVE_UT_UT_EXIT=cross)])
+if test x"$samba_cv_HAVE_UT_UT_EXIT" = x"yes"; then
+ AC_DEFINE(HAVE_UT_UT_EXIT,1,[Whether the utmp struct has a property ut_exit])
+fi
+
+AC_CACHE_CHECK([for ut_addr in utmp],samba_cv_HAVE_UT_UT_ADDR,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut; ut.ut_addr = 0;],
+samba_cv_HAVE_UT_UT_ADDR=yes,samba_cv_HAVE_UT_UT_ADDR=no,samba_cv_HAVE_UT_UT_ADDR=cross)])
+if test x"$samba_cv_HAVE_UT_UT_ADDR" = x"yes"; then
+ AC_DEFINE(HAVE_UT_UT_ADDR,1,[Whether the utmp struct has a property ut_addr])
+fi
+
+if test x$ac_cv_func_pututline = xyes ; then
+ AC_CACHE_CHECK([whether pututline returns pointer],samba_cv_PUTUTLINE_RETURNS_UTMP,[
+ AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+ [struct utmp utarg; struct utmp *utreturn; utreturn = pututline(&utarg);],
+ samba_cv_PUTUTLINE_RETURNS_UTMP=yes,samba_cv_PUTUTLINE_RETURNS_UTMP=no)])
+ if test x"$samba_cv_PUTUTLINE_RETURNS_UTMP" = x"yes"; then
+ AC_DEFINE(PUTUTLINE_RETURNS_UTMP,1,[Whether pututline returns pointer])
+ fi
+fi
+
+AC_CACHE_CHECK([for ut_syslen in utmpx],samba_cv_HAVE_UX_UT_SYSLEN,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmpx.h>],
+[struct utmpx ux; ux.ut_syslen = 0;],
+samba_cv_HAVE_UX_UT_SYSLEN=yes,samba_cv_HAVE_UX_UT_SYSLEN=no,samba_cv_HAVE_UX_UT_SYSLEN=cross)])
+if test x"$samba_cv_HAVE_UX_UT_SYSLEN" = x"yes"; then
+ AC_DEFINE(HAVE_UX_UT_SYSLEN,1,[Whether the utmpx struct has a property ut_syslen])
+fi
+
+fi
+# end utmp details
+
+
+ICONV_LOCATION=standard
+LOOK_DIRS="/usr /usr/local /sw"
+AC_ARG_WITH(libiconv,
+[ --with-libiconv=BASEDIR Use libiconv in BASEDIR/lib and BASEDIR/include (default=auto) ],
+[
+ if test "$withval" = "no" ; then
+ AC_MSG_ERROR([argument to --with-libiconv must be a directory])
+ else
+ if test "$withval" != "yes" ; then
+ ICONV_PATH_SPEC=yes
+ LOOK_DIRS="$withval"
+ fi
+ fi
+])
+
+ICONV_FOUND="no"
+for i in $LOOK_DIRS ; do
+ save_LIBS=$LIBS
+ save_LDFLAGS=$LDFLAGS
+ save_CPPFLAGS=$CPPFLAGS
+ CPPFLAGS="$CPPFLAGS -I$i/include"
+dnl This is here to handle -withval stuff for --with-libiconv
+dnl Perhaps we should always add a -L
+ LDFLAGS="$LDFLAGS -L$i/lib"
+ LIBS=
+ export LDFLAGS LIBS CPPFLAGS
+dnl Try to find iconv(3)
+ jm_ICONV($i)
+
+ if test "$ICONV_FOUND" = yes; then
+ LDFLAGS=$save_LDFLAGS
+ LIB_ADD_DIR(LDFLAGS, "$i/lib")
+ CFLAGS_ADD_DIR(CPPFLAGS, "$i/include")
+ LIBS="$save_LIBS"
+ ICONV_LOCATION=$i
+ export LDFLAGS LIBS CPPFLAGS
+dnl Now, check for a working iconv ... we want to do it here because
+dnl there might be a working iconv further down the list of LOOK_DIRS
+
+ ############
+ # check for iconv in libc
+ ic_save_LIBS="$LIBS"
+ if test x"$ICONV_PATH_SPEC" = "xyes" ; then
+ LIBS="$LIBS -L$ICONV_LOCATION/lib"
+ fi
+ if test x"$jm_cv_lib_iconv" != x; then
+ LIBS="$LIBS -l$jm_cv_lib_iconv"
+ fi
+dnl AC_CACHE_CHECK([for working iconv],samba_cv_HAVE_NATIVE_ICONV,[
+ default_dos_charset=no
+ default_display_charset=no
+ default_unix_charset=no
+
+ # check for default dos charset name
+ for j in CP850 IBM850 ; do
+ rjs_CHARSET($j)
+ if test x"$ICONV_CHARSET" = x"$j"; then
+ default_dos_charset="\"$j\""
+ break
+ fi
+ done
+ # check for default display charset name
+ for j in ASCII 646 ; do
+ rjs_CHARSET($j)
+ if test x"$ICONV_CHARSET" = x"$j"; then
+ default_display_charset="\"$j\""
+ break
+ fi
+ done
+ # check for default unix charset name
+ for j in UTF-8 UTF8 ; do
+ rjs_CHARSET($j)
+ if test x"$ICONV_CHARSET" = x"$j"; then
+ default_unix_charset="\"$j\""
+ break
+ fi
+ done
+
+ if test "$default_dos_charset" != "no" -a \
+ "$default_dos_charset" != "cross" -a \
+ "$default_display_charset" != "no" -a \
+ "$default_display_charset" != "cross" -a \
+ "$default_unix_charset" != "no" -a \
+ "$default_unix_charset" != "cross"
+ then
+ samba_cv_HAVE_NATIVE_ICONV=yes
+ else if test "$default_dos_charset" = "cross" -o \
+ "$default_display_charset" = "cross" -o \
+ "$default_unix_charset" = "cross"
+ then
+ samba_cv_HAVE_NATIVE_ICONV=cross
+ else
+ samba_cv_HAVE_NATIVE_ICONV=no
+ fi
+ fi
+dnl ])
+
+ LIBS="$ic_save_LIBS"
+ if test x"$samba_cv_HAVE_NATIVE_ICONV" = x"yes"; then
+ CPPFLAGS=$save_CPPFLAGS
+ LDFLAGS=$save_LDFLAGS
+ LIBS=$save_LIBS
+ if test x"$jm_cv_lib_iconv" != x; then
+ LIBS="$LIBS -l$jm_cv_lib_iconv"
+ fi
+ dnl Add the flags we need to CPPFLAGS and LDFLAGS
+ CFLAGS_ADD_DIR(CPPFLAGS, "$i/include")
+ LIB_ADD_DIR(LDFLAGS, "$i/lib")
+ export CPPFLAGS
+ AC_DEFINE(HAVE_NATIVE_ICONV,1,[Whether to use native iconv])
+ AC_DEFINE_UNQUOTED(DEFAULT_DOS_CHARSET,$default_dos_charset,[Default dos charset name])
+ AC_DEFINE_UNQUOTED(DEFAULT_DISPLAY_CHARSET,$default_display_charset,[Default display charset name])
+ AC_DEFINE_UNQUOTED(DEFAULT_UNIX_CHARSET,$default_unix_charset,[Default unix charset name])
+ break
+ fi
+dnl We didn't find a working iconv, so keep going
+ fi
+dnl We only need to clean these up here for the next pass through the loop
+ CPPFLAGS=$save_CPPFLAGS
+ LDFLAGS=$save_LDFLAGS
+ LIBS=$save_LIBS
+ export LDFLAGS LIBS CPPFLAGS
+done
+
+
+if test x"$ICONV_FOUND" = x"no" -o x"$samba_cv_HAVE_NATIVE_ICONV" != x"yes" ; then
+ AC_MSG_WARN([Sufficient support for iconv function was not found.
+ Install libiconv from http://freshmeat.net/projects/libiconv/ for better charset compatibility!])
+ AC_DEFINE_UNQUOTED(DEFAULT_DOS_CHARSET,"ASCII",[Default dos charset name])
+ AC_DEFINE_UNQUOTED(DEFAULT_DISPLAY_CHARSET,"ASCII",[Default display charset name])
+ AC_DEFINE_UNQUOTED(DEFAULT_UNIX_CHARSET,"UTF8",[Default unix charset name])
+fi
+
+
+AC_CACHE_CHECK([for Linux kernel oplocks],samba_cv_HAVE_KERNEL_OPLOCKS_LINUX,[
+AC_TRY_RUN([
+#include <sys/types.h>
+#include <fcntl.h>
+#ifndef F_GETLEASE
+#define F_GETLEASE 1025
+#endif
+main() {
+ int fd = open("/dev/null", O_RDONLY);
+ return fcntl(fd, F_GETLEASE, 0) == -1;
+}
+],
+samba_cv_HAVE_KERNEL_OPLOCKS_LINUX=yes,samba_cv_HAVE_KERNEL_OPLOCKS_LINUX=no,samba_cv_HAVE_KERNEL_OPLOCKS_LINUX=cross)])
+if test x"$samba_cv_HAVE_KERNEL_OPLOCKS_LINUX" = x"yes"; then
+ AC_DEFINE(HAVE_KERNEL_OPLOCKS_LINUX,1,[Whether to use linux kernel oplocks])
+fi
+
+AC_CACHE_CHECK([for kernel change notify support],samba_cv_HAVE_KERNEL_CHANGE_NOTIFY,[
+AC_TRY_RUN([
+#include <sys/types.h>
+#include <fcntl.h>
+#include <signal.h>
+#ifndef F_NOTIFY
+#define F_NOTIFY 1026
+#endif
+main() {
+ exit(fcntl(open("/tmp", O_RDONLY), F_NOTIFY, 0) == -1 ? 1 : 0);
+}
+],
+samba_cv_HAVE_KERNEL_CHANGE_NOTIFY=yes,samba_cv_HAVE_KERNEL_CHANGE_NOTIFY=no,samba_cv_HAVE_KERNEL_CHANGE_NOTIFY=cross)])
+if test x"$samba_cv_HAVE_KERNEL_CHANGE_NOTIFY" = x"yes"; then
+ AC_DEFINE(HAVE_KERNEL_CHANGE_NOTIFY,1,[Whether kernel notifies changes])
+fi
+
+AC_CACHE_CHECK([for kernel share modes],samba_cv_HAVE_KERNEL_SHARE_MODES,[
+AC_TRY_RUN([
+#include <sys/types.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/file.h>
+#ifndef LOCK_MAND
+#define LOCK_MAND 32
+#define LOCK_READ 64
+#endif
+main() {
+ exit(flock(open("/dev/null", O_RDWR), LOCK_MAND|LOCK_READ) != 0);
+}
+],
+samba_cv_HAVE_KERNEL_SHARE_MODES=yes,samba_cv_HAVE_KERNEL_SHARE_MODES=no,samba_cv_HAVE_KERNEL_SHARE_MODES=cross)])
+if test x"$samba_cv_HAVE_KERNEL_SHARE_MODES" = x"yes"; then
+ AC_DEFINE(HAVE_KERNEL_SHARE_MODES,1,[Whether the kernel supports share modes])
+fi
+
+
+
+
+AC_CACHE_CHECK([for IRIX kernel oplock type definitions],samba_cv_HAVE_KERNEL_OPLOCKS_IRIX,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <fcntl.h>],
+[oplock_stat_t t; t.os_state = OP_REVOKE; t.os_dev = 1; t.os_ino = 1;],
+samba_cv_HAVE_KERNEL_OPLOCKS_IRIX=yes,samba_cv_HAVE_KERNEL_OPLOCKS_IRIX=no)])
+if test x"$samba_cv_HAVE_KERNEL_OPLOCKS_IRIX" = x"yes"; then
+ AC_DEFINE(HAVE_KERNEL_OPLOCKS_IRIX,1,[Whether IRIX kernel oplock type definitions are available])
+fi
+
+AC_CACHE_CHECK([for irix specific capabilities],samba_cv_HAVE_IRIX_SPECIFIC_CAPABILITIES,[
+AC_TRY_RUN([#include <sys/types.h>
+#include <sys/capability.h>
+main() {
+ cap_t cap;
+ if ((cap = cap_get_proc()) == NULL)
+ exit(1);
+ cap->cap_effective |= CAP_NETWORK_MGT;
+ cap->cap_inheritable |= CAP_NETWORK_MGT;
+ cap_set_proc(cap);
+ exit(0);
+}
+],
+samba_cv_HAVE_IRIX_SPECIFIC_CAPABILITIES=yes,samba_cv_HAVE_IRIX_SPECIFIC_CAPABILITIES=no,samba_cv_HAVE_IRIX_SPECIFIC_CAPABILITIES=cross)])
+if test x"$samba_cv_HAVE_IRIX_SPECIFIC_CAPABILITIES" = x"yes"; then
+ AC_DEFINE(HAVE_IRIX_SPECIFIC_CAPABILITIES,1,[Whether IRIX specific capabilities are available])
+fi
+
+#
+# Check for int16, uint16, int32 and uint32 in rpc/types.h included from rpc/rpc.h
+# This is *really* broken but some systems (DEC OSF1) do this.... JRA.
+#
+
+AC_CACHE_CHECK([for int16 typedef included by rpc/rpc.h],samba_cv_HAVE_INT16_FROM_RPC_RPC_H,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#if defined(HAVE_RPC_RPC_H)
+#include <rpc/rpc.h>
+#endif],
+[int16 testvar;],
+samba_cv_HAVE_INT16_FROM_RPC_RPC_H=yes,samba_cv_HAVE_INT16_FROM_RPC_RPC_H=no)])
+if test x"$samba_cv_HAVE_INT16_FROM_RPC_RPC_H" = x"yes"; then
+ AC_DEFINE(HAVE_INT16_FROM_RPC_RPC_H,1,[Whether int16 typedef is included by rpc/rpc.h])
+fi
+
+AC_CACHE_CHECK([for uint16 typedef included by rpc/rpc.h],samba_cv_HAVE_UINT16_FROM_RPC_RPC_H,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#if defined(HAVE_RPC_RPC_H)
+#include <rpc/rpc.h>
+#endif],
+[uint16 testvar;],
+samba_cv_HAVE_UINT16_FROM_RPC_RPC_H=yes,samba_cv_HAVE_UINT16_FROM_RPC_RPC_H=no)])
+if test x"$samba_cv_HAVE_UINT16_FROM_RPC_RPC_H" = x"yes"; then
+ AC_DEFINE(HAVE_UINT16_FROM_RPC_RPC_H,1,[Whether uint16 typedef is included by rpc/rpc.h])
+fi
+
+AC_CACHE_CHECK([for int32 typedef included by rpc/rpc.h],samba_cv_HAVE_INT32_FROM_RPC_RPC_H,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#if defined(HAVE_RPC_RPC_H)
+#include <rpc/rpc.h>
+#endif],
+[int32 testvar;],
+samba_cv_HAVE_INT32_FROM_RPC_RPC_H=yes,samba_cv_HAVE_INT32_FROM_RPC_RPC_H=no)])
+if test x"$samba_cv_HAVE_INT32_FROM_RPC_RPC_H" = x"yes"; then
+ AC_DEFINE(HAVE_INT32_FROM_RPC_RPC_H,1,[Whether int32 typedef is included by rpc/rpc.h])
+fi
+
+AC_CACHE_CHECK([for uint32 typedef included by rpc/rpc.h],samba_cv_HAVE_UINT32_FROM_RPC_RPC_H,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#if defined(HAVE_RPC_RPC_H)
+#include <rpc/rpc.h>
+#endif],
+[uint32 testvar;],
+samba_cv_HAVE_UINT32_FROM_RPC_RPC_H=yes,samba_cv_HAVE_UINT32_FROM_RPC_RPC_H=no)])
+if test x"$samba_cv_HAVE_UINT32_FROM_RPC_RPC_H" = x"yes"; then
+ AC_DEFINE(HAVE_UINT32_FROM_RPC_RPC_H,1,[Whether uint32 typedef is included by rpc/rpc.h])
+fi
+
+dnl
+dnl Some systems (SCO) have a problem including
+dnl <prot.h> and <rpc/rpc.h> due to AUTH_ERROR being defined
+dnl as a #define in <prot.h> and as part of an enum
+dnl in <rpc/rpc.h>.
+dnl
+
+AC_CACHE_CHECK([for conflicting AUTH_ERROR define in rpc/rpc.h],samba_cv_HAVE_RPC_AUTH_ERROR_CONFLICT,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#ifdef HAVE_SYS_SECURITY_H
+#include <sys/security.h>
+#include <prot.h>
+#endif /* HAVE_SYS_SECURITY_H */
+#if defined(HAVE_RPC_RPC_H)
+#include <rpc/rpc.h>
+#endif],
+[int testvar;],
+samba_cv_HAVE_RPC_AUTH_ERROR_CONFLICT=no,samba_cv_HAVE_RPC_AUTH_ERROR_CONFLICT=yes)])
+if test x"$samba_cv_HAVE_RPC_AUTH_ERROR_CONFLICT" = x"yes"; then
+ AC_DEFINE(HAVE_RPC_AUTH_ERROR_CONFLICT,1,[Whether there is a conflicting AUTH_ERROR define in rpc/rpc.h])
+fi
+
+AC_MSG_CHECKING([for test routines])
+AC_TRY_RUN([#include "${srcdir-.}/tests/trivial.c"],
+ AC_MSG_RESULT(yes),
+ AC_MSG_ERROR([cant find test code. Aborting config]),
+ AC_MSG_WARN([cannot run when cross-compiling]))
+
+AC_CACHE_CHECK([for ftruncate extend],samba_cv_HAVE_FTRUNCATE_EXTEND,[
+AC_TRY_RUN([#include "${srcdir-.}/tests/ftruncate.c"],
+ samba_cv_HAVE_FTRUNCATE_EXTEND=yes,samba_cv_HAVE_FTRUNCATE_EXTEND=no,samba_cv_HAVE_FTRUNCATE_EXTEND=cross)])
+if test x"$samba_cv_HAVE_FTRUNCATE_EXTEND" = x"yes"; then
+ AC_DEFINE(HAVE_FTRUNCATE_EXTEND,1,[Truncate extend])
+fi
+
+AC_CACHE_CHECK([for AF_LOCAL socket support], samba_cv_HAVE_WORKING_AF_LOCAL, [
+AC_TRY_RUN([#include "${srcdir-.}/tests/unixsock.c"],
+ samba_cv_HAVE_WORKING_AF_LOCAL=yes,
+ samba_cv_HAVE_WORKING_AF_LOCAL=no,
+ samba_cv_HAVE_WORKING_AF_LOCAL=cross)])
+if test x"$samba_cv_HAVE_WORKING_AF_LOCAL" != xno
+then
+ AC_DEFINE(HAVE_WORKING_AF_LOCAL, 1, [Define if you have working AF_LOCAL sockets])
+fi
+
+AC_CACHE_CHECK([for broken getgroups],samba_cv_HAVE_BROKEN_GETGROUPS,[
+AC_TRY_RUN([#include "${srcdir-.}/tests/getgroups.c"],
+ samba_cv_HAVE_BROKEN_GETGROUPS=yes,samba_cv_HAVE_BROKEN_GETGROUPS=no,samba_cv_HAVE_BROKEN_GETGROUPS=cross)])
+if test x"$samba_cv_HAVE_BROKEN_GETGROUPS" = x"yes"; then
+ AC_DEFINE(HAVE_BROKEN_GETGROUPS,1,[Whether getgroups is broken])
+fi
+
+AC_CACHE_CHECK([whether getpass should be replaced],samba_cv_REPLACE_GETPASS,[
+SAVE_CPPFLAGS="$CPPFLAGS"
+CPPFLAGS="$CPPFLAGS -I${srcdir-.}/ -I${srcdir-.}/include -I${srcdir-.}/ubiqx -I${srcdir-.}/popt -I${srcdir-.}/smbwrapper"
+AC_TRY_COMPILE([
+#define REPLACE_GETPASS 1
+#define NO_PROTO_H 1
+#define NO_CONFIG_H 1
+#define main dont_declare_main
+#include "${srcdir-.}/lib/getsmbpass.c"
+#undef main
+],[],samba_cv_REPLACE_GETPASS=yes,samba_cv_REPLACE_GETPASS=no)
+CPPFLAGS="$SAVE_CPPFLAGS"
+])
+if test x"$samba_cv_REPLACE_GETPASS" = x"yes"; then
+ AC_DEFINE(REPLACE_GETPASS,1,[Whether getpass should be replaced])
+fi
+
+AC_CACHE_CHECK([for broken inet_ntoa],samba_cv_REPLACE_INET_NTOA,[
+AC_TRY_RUN([
+#include <stdio.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+main() { struct in_addr ip; ip.s_addr = 0x12345678;
+if (strcmp(inet_ntoa(ip),"18.52.86.120") &&
+ strcmp(inet_ntoa(ip),"120.86.52.18")) { exit(0); }
+exit(1);}],
+ samba_cv_REPLACE_INET_NTOA=yes,samba_cv_REPLACE_INET_NTOA=no,samba_cv_REPLACE_INET_NTOA=cross)])
+if test x"$samba_cv_REPLACE_INET_NTOA" = x"yes"; then
+ AC_DEFINE(REPLACE_INET_NTOA,1,[Whether inet_ntoa should be replaced])
+fi
+
+AC_CACHE_CHECK([for secure mkstemp],samba_cv_HAVE_SECURE_MKSTEMP,[
+AC_TRY_RUN([#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+main() {
+ struct stat st;
+ char tpl[20]="/tmp/test.XXXXXX";
+ int fd = mkstemp(tpl);
+ if (fd == -1) exit(1);
+ unlink(tpl);
+ if (fstat(fd, &st) != 0) exit(1);
+ if ((st.st_mode & 0777) != 0600) exit(1);
+ exit(0);
+}],
+samba_cv_HAVE_SECURE_MKSTEMP=yes,
+samba_cv_HAVE_SECURE_MKSTEMP=no,
+samba_cv_HAVE_SECURE_MKSTEMP=cross)])
+if test x"$samba_cv_HAVE_SECURE_MKSTEMP" = x"yes"; then
+ AC_DEFINE(HAVE_SECURE_MKSTEMP,1,[Whether mkstemp is secure])
+fi
+
+AC_CACHE_CHECK([for sysconf(_SC_NGROUPS_MAX)],samba_cv_SYSCONF_SC_NGROUPS_MAX,[
+AC_TRY_RUN([#include <unistd.h>
+main() { exit(sysconf(_SC_NGROUPS_MAX) == -1 ? 1 : 0); }],
+samba_cv_SYSCONF_SC_NGROUPS_MAX=yes,samba_cv_SYSCONF_SC_NGROUPS_MAX=no,samba_cv_SYSCONF_SC_NGROUPS_MAX=cross)])
+if test x"$samba_cv_SYSCONF_SC_NGROUPS_MAX" = x"yes"; then
+ AC_DEFINE(SYSCONF_SC_NGROUPS_MAX,1,[Whether sysconf(_SC_NGROUPS_MAX) is available])
+fi
+
+AC_CACHE_CHECK([for sysconf(_SC_NPROC_ONLN)],samba_cv_SYSCONF_SC_NPROC_ONLN,[
+AC_TRY_RUN([#include <unistd.h>
+main() { exit(sysconf(_SC_NPROC_ONLN) == -1 ? 1 : 0); }],
+samba_cv_SYSCONF_SC_NPROC_ONLN=yes,samba_cv_SYSCONF_SC_NPROC_ONLN=no,samba_cv_SYSCONF_SC_NPROC_ONLN=cross)])
+if test x"$samba_cv_SYSCONF_SC_NPROC_ONLN" = x"yes"; then
+ AC_DEFINE(SYSCONF_SC_NPROC_ONLN,1,[Whether sysconf(_SC_NPROC_ONLN) is available])
+fi
+
+AC_CACHE_CHECK([for root],samba_cv_HAVE_ROOT,[
+AC_TRY_RUN([main() { exit(getuid() != 0); }],
+ samba_cv_HAVE_ROOT=yes,samba_cv_HAVE_ROOT=no,samba_cv_HAVE_ROOT=cross)])
+if test x"$samba_cv_HAVE_ROOT" = x"yes"; then
+ AC_DEFINE(HAVE_ROOT,1,[Whether current user is root])
+else
+ AC_MSG_WARN(running as non-root will disable some tests)
+fi
+
+##################
+# look for a method of finding the list of network interfaces
+iface=no;
+AC_CACHE_CHECK([for iface AIX],samba_cv_HAVE_IFACE_AIX,[
+AC_TRY_RUN([
+#define HAVE_IFACE_AIX 1
+#define AUTOCONF_TEST 1
+#include "confdefs.h"
+#include "${srcdir-.}/lib/interfaces.c"],
+ samba_cv_HAVE_IFACE_AIX=yes,samba_cv_HAVE_IFACE_AIX=no,samba_cv_HAVE_IFACE_AIX=cross)])
+if test x"$samba_cv_HAVE_IFACE_AIX" = x"yes"; then
+ iface=yes;AC_DEFINE(HAVE_IFACE_AIX,1,[Whether iface AIX is available])
+fi
+
+if test $iface = no; then
+AC_CACHE_CHECK([for iface ifconf],samba_cv_HAVE_IFACE_IFCONF,[
+AC_TRY_RUN([
+#define HAVE_IFACE_IFCONF 1
+#define AUTOCONF_TEST 1
+#include "confdefs.h"
+#include "${srcdir-.}/lib/interfaces.c"],
+ samba_cv_HAVE_IFACE_IFCONF=yes,samba_cv_HAVE_IFACE_IFCONF=no,samba_cv_HAVE_IFACE_IFCONF=cross)])
+if test x"$samba_cv_HAVE_IFACE_IFCONF" = x"yes"; then
+ iface=yes;AC_DEFINE(HAVE_IFACE_IFCONF,1,[Whether iface ifconf is available])
+fi
+fi
+
+if test $iface = no; then
+AC_CACHE_CHECK([for iface ifreq],samba_cv_HAVE_IFACE_IFREQ,[
+AC_TRY_RUN([
+#define HAVE_IFACE_IFREQ 1
+#define AUTOCONF_TEST 1
+#include "confdefs.h"
+#include "${srcdir-.}/lib/interfaces.c"],
+ samba_cv_HAVE_IFACE_IFREQ=yes,samba_cv_HAVE_IFACE_IFREQ=no,samba_cv_HAVE_IFACE_IFREQ=cross)])
+if test x"$samba_cv_HAVE_IFACE_IFREQ" = x"yes"; then
+ iface=yes;AC_DEFINE(HAVE_IFACE_IFREQ,1,[Whether iface ifreq is available])
+fi
+fi
+
+
+################################################
+# look for a method of setting the effective uid
+seteuid=no;
+if test $seteuid = no; then
+AC_CACHE_CHECK([for setresuid],samba_cv_USE_SETRESUID,[
+AC_TRY_RUN([
+#define AUTOCONF_TEST 1
+#define USE_SETRESUID 1
+#include "confdefs.h"
+#include "${srcdir-.}/lib/util_sec.c"],
+ samba_cv_USE_SETRESUID=yes,samba_cv_USE_SETRESUID=no,samba_cv_USE_SETRESUID=cross)])
+if test x"$samba_cv_USE_SETRESUID" = x"yes"; then
+ seteuid=yes;AC_DEFINE(USE_SETRESUID,1,[Whether setresuid() is available])
+fi
+fi
+
+
+if test $seteuid = no; then
+AC_CACHE_CHECK([for setreuid],samba_cv_USE_SETREUID,[
+AC_TRY_RUN([
+#define AUTOCONF_TEST 1
+#define USE_SETREUID 1
+#include "confdefs.h"
+#include "${srcdir-.}/lib/util_sec.c"],
+ samba_cv_USE_SETREUID=yes,samba_cv_USE_SETREUID=no,samba_cv_USE_SETREUID=cross)])
+if test x"$samba_cv_USE_SETREUID" = x"yes"; then
+ seteuid=yes;AC_DEFINE(USE_SETREUID,1,[Whether setreuid() is available])
+fi
+fi
+
+if test $seteuid = no; then
+AC_CACHE_CHECK([for seteuid],samba_cv_USE_SETEUID,[
+AC_TRY_RUN([
+#define AUTOCONF_TEST 1
+#define USE_SETEUID 1
+#include "confdefs.h"
+#include "${srcdir-.}/lib/util_sec.c"],
+ samba_cv_USE_SETEUID=yes,samba_cv_USE_SETEUID=no,samba_cv_USE_SETEUID=cross)])
+if test x"$samba_cv_USE_SETEUID" = x"yes"; then
+ seteuid=yes;AC_DEFINE(USE_SETEUID,1,[Whether seteuid() is available])
+fi
+fi
+
+if test $seteuid = no; then
+AC_CACHE_CHECK([for setuidx],samba_cv_USE_SETUIDX,[
+AC_TRY_RUN([
+#define AUTOCONF_TEST 1
+#define USE_SETUIDX 1
+#include "confdefs.h"
+#include "${srcdir-.}/lib/util_sec.c"],
+ samba_cv_USE_SETUIDX=yes,samba_cv_USE_SETUIDX=no,samba_cv_USE_SETUIDX=cross)])
+if test x"$samba_cv_USE_SETUIDX" = x"yes"; then
+ seteuid=yes;AC_DEFINE(USE_SETUIDX,1,[Whether setuidx() is available])
+fi
+fi
+
+
+AC_CACHE_CHECK([for working mmap],samba_cv_HAVE_MMAP,[
+AC_TRY_RUN([#include "${srcdir-.}/tests/shared_mmap.c"],
+ samba_cv_HAVE_MMAP=yes,samba_cv_HAVE_MMAP=no,samba_cv_HAVE_MMAP=cross)])
+if test x"$samba_cv_HAVE_MMAP" = x"yes"; then
+ AC_DEFINE(HAVE_MMAP,1,[Whether mmap works])
+fi
+
+AC_CACHE_CHECK([for ftruncate needs root],samba_cv_FTRUNCATE_NEEDS_ROOT,[
+AC_TRY_RUN([#include "${srcdir-.}/tests/ftruncroot.c"],
+ samba_cv_FTRUNCATE_NEEDS_ROOT=yes,samba_cv_FTRUNCATE_NEEDS_ROOT=no,samba_cv_FTRUNCATE_NEEDS_ROOT=cross)])
+if test x"$samba_cv_FTRUNCATE_NEEDS_ROOT" = x"yes"; then
+ AC_DEFINE(FTRUNCATE_NEEDS_ROOT,1,[Whether ftruncate() needs root])
+fi
+
+AC_CACHE_CHECK([for fcntl locking],samba_cv_HAVE_FCNTL_LOCK,[
+AC_TRY_RUN([#include "${srcdir-.}/tests/fcntl_lock.c"],
+ samba_cv_HAVE_FCNTL_LOCK=yes,samba_cv_HAVE_FCNTL_LOCK=no,samba_cv_HAVE_FCNTL_LOCK=cross)])
+if test x"$samba_cv_HAVE_FCNTL_LOCK" = x"yes"; then
+ AC_DEFINE(HAVE_FCNTL_LOCK,1,[Whether fcntl locking is available])
+fi
+
+AC_CACHE_CHECK([for broken (glibc2.1/x86) 64 bit fcntl locking],samba_cv_HAVE_BROKEN_FCNTL64_LOCKS,[
+AC_TRY_RUN([#include "${srcdir-.}/tests/fcntl_lock64.c"],
+ samba_cv_HAVE_BROKEN_FCNTL64_LOCKS=yes,samba_cv_HAVE_BROKEN_FCNTL64_LOCKS=no,samba_cv_HAVE_BROKEN_FCNTL64_LOCKS=cross)])
+if test x"$samba_cv_HAVE_BROKEN_FCNTL64_LOCKS" = x"yes"; then
+ AC_DEFINE(HAVE_BROKEN_FCNTL64_LOCKS,1,[Whether fcntl64 locks are broken])
+
+else
+
+dnl
+dnl Don't check for 64 bit fcntl locking if we know that the
+dnl glibc2.1 broken check has succeeded.
+dnl
+
+ AC_CACHE_CHECK([for 64 bit fcntl locking],samba_cv_HAVE_STRUCT_FLOCK64,[
+ AC_TRY_RUN([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+main() { struct flock64 fl64;
+#if defined(F_SETLKW64) && defined(F_SETLK64) && defined(F_GETLK64)
+exit(0);
+#else
+exit(1);
+#endif
+}],
+ samba_cv_HAVE_STRUCT_FLOCK64=yes,samba_cv_HAVE_STRUCT_FLOCK64=no,samba_cv_HAVE_STRUCT_FLOCK64=cross)])
+
+ if test x"$samba_cv_HAVE_STRUCT_FLOCK64" = x"yes"; then
+ AC_DEFINE(HAVE_STRUCT_FLOCK64,1,[Whether the flock64 struct is available])
+ fi
+fi
+
+AC_CACHE_CHECK([for st_blocks in struct stat],samba_cv_HAVE_STAT_ST_BLOCKS,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>],
+[struct stat st; st.st_blocks = 0;],
+samba_cv_HAVE_STAT_ST_BLOCKS=yes,samba_cv_HAVE_STAT_ST_BLOCKS=no,samba_cv_HAVE_STAT_ST_BLOCKS=cross)])
+if test x"$samba_cv_HAVE_STAT_ST_BLOCKS" = x"yes"; then
+ AC_DEFINE(HAVE_STAT_ST_BLOCKS,1,[Whether the stat struct has a st_block property])
+fi
+
+AC_CACHE_CHECK([for st_blksize in struct stat],samba_cv_HAVE_STAT_ST_BLKSIZE,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>],
+[struct stat st; st.st_blksize = 0;],
+samba_cv_HAVE_STAT_ST_BLKSIZE=yes,samba_cv_HAVE_STAT_ST_BLKSIZE=no,samba_cv_HAVE_STAT_ST_BLKSIZE=cross)])
+if test x"$samba_cv_HAVE_STAT_ST_BLKSIZE" = x"yes"; then
+ AC_DEFINE(HAVE_STAT_ST_BLKSIZE,1,[Whether the stat struct has a st_blksize property])
+fi
+
+case "$host_os" in
+*linux*)
+AC_CACHE_CHECK([for broken RedHat 7.2 system header files],samba_cv_BROKEN_REDHAT_7_SYSTEM_HEADERS,[
+AC_TRY_COMPILE([
+#ifdef HAVE_SYS_VFS_H
+#include <sys/vfs.h>
+#endif
+#ifdef HAVE_SYS_CAPABILITY_H
+#include <sys/capability.h>
+#endif
+],[int i;],
+ samba_cv_BROKEN_REDHAT_7_SYSTEM_HEADERS=no,samba_cv_BROKEN_REDHAT_7_SYSTEM_HEADERS=yes)])
+if test x"$samba_cv_BROKEN_REDHAT_7_SYSTEM_HEADERS" = x"yes"; then
+ AC_DEFINE(BROKEN_REDHAT_7_SYSTEM_HEADERS,1,[Broken RedHat 7.2 system header files])
+fi
+;;
+esac
+
+AC_CACHE_CHECK([for broken nisplus include files],samba_cv_BROKEN_NISPLUS_INCLUDE_FILES,[
+AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <sys/acl.h>
+#if defined(HAVE_RPCSVC_NIS_H)
+#include <rpcsvc/nis.h>
+#endif],
+[int i;],
+samba_cv_BROKEN_NISPLUS_INCLUDE_FILES=no,samba_cv_BROKEN_NISPLUS_INCLUDE_FILES=yes)])
+if test x"$samba_cv_BROKEN_NISPLUS_INCLUDE_FILES" = x"yes"; then
+ AC_DEFINE(BROKEN_NISPLUS_INCLUDE_FILES,1,[Whether the nisplus include files are broken])
+fi
+
+
+#################################################
+# check for smbwrapper support
+AC_MSG_CHECKING(whether to use smbwrapper)
+AC_ARG_WITH(smbwrapper,
+[ --with-smbwrapper Include SMB wrapper support (default=no) ],
+[ case "$withval" in
+ yes)
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(WITH_SMBWRAPPER,1,[Whether to include smbwrapper support])
+ WRAPPROG="bin/smbsh\$(EXEEXT)"
+ WRAP="bin/smbwrapper.$SHLIBEXT"
+
+# Conditions under which smbwrapper should not be built.
+
+ if test x$PICFLAGS = x; then
+ echo No support for PIC code - disabling smbwrapper and smbsh
+ WRAPPROG=""
+ WRAP=""
+ elif test x$ac_cv_func_syscall = xno; then
+ AC_MSG_RESULT([No syscall() -- disabling smbwrapper and smbsh])
+ WRAPPROG=""
+ WRAP=""
+ fi
+ EXTRA_ALL_TARGETS="$EXTRA_ALL_TARGETS $WRAPPROG $WRAP"
+ SMBWRAPPER="$WRAPPROG $WRAP"
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+ AC_MSG_RESULT(no)
+)
+
+#################################################
+# check for AFS clear-text auth support
+samba_cv_WITH_AFS=no
+AC_MSG_CHECKING(whether to use AFS clear-text auth)
+AC_ARG_WITH(afs,
+[ --with-afs Include AFS clear-text auth support (default=no) ],
+[ case "$withval" in
+ yes|auto)
+ AC_MSG_RESULT($withval)
+ samba_cv_WITH_AFS=$withval
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+ AC_MSG_RESULT(no)
+)
+
+####################################################
+# check for Linux-specific AFS fake-kaserver support
+samba_cv_WITH_FAKE_KASERVER=no
+AC_MSG_CHECKING(whether to use AFS fake-kaserver)
+AC_ARG_WITH(fake-kaserver,
+[ --with-fake-kaserver Include AFS fake-kaserver support (default=no) ],
+[ case "$withval" in
+ yes|auto)
+ AC_MSG_RESULT($withval)
+ samba_cv_WITH_FAKE_KASERVER=$withval
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+ AC_MSG_RESULT(no)
+)
+
+#################################################
+# decide whether we can support WITH_AFS and / or WITH_FAKE_KASERVER
+if test x"$samba_cv_WITH_AFS" != x"no" ||
+ test x"$samba_cv_WITH_FAKE_KASERVER" != x"no"; then
+
+ # see if this box has the afs-headers in /usr/include/afs
+ AC_MSG_CHECKING(for /usr/include/afs)
+ if test -d /usr/include/afs; then
+ CFLAGS="$CFLAGS -I/usr/include/afs"
+ CPPFLAGS="$CPPFLAGS -I/usr/include/afs"
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_RESULT(no)
+ fi
+
+ # check for afs.h
+ have_afs_headers=no
+ AC_CHECK_HEADERS(afs.h afs/afs.h)
+ if test x"$ac_cv_header_afs_h" = x"no" && test x"$ac_cv_header_afs_afs_h" = x"no"; then
+ if test x"$samba_cv_WITH_FAKE_KASERVER" = x"auto" ||
+ test x"$samba_cv_WITH_AFS" = x"auto"; then
+ AC_MSG_WARN([AFS cannot be supported without afs.h])
+ else
+ AC_MSG_ERROR([AFS cannot be supported without afs.h])
+ fi
+ else
+ have_afs_headers=yes
+ fi
+fi
+
+if test x"$samba_cv_WITH_FAKE_KASERVER" != x"no" && test x"$have_afs_headers" == x"yes"; then
+ AC_DEFINE(WITH_FAKE_KASERVER,1,[Whether to include AFS fake-kaserver support])
+fi
+
+if test x"$samba_cv_WITH_AFS" != x"no" && test x"$have_afs_headers" = x"yes"; then
+ AC_DEFINE(WITH_AFS,1,[Whether to include AFS clear-text auth support])
+fi
+
+#################################################
+# check for the DFS clear-text auth system
+AC_MSG_CHECKING(whether to use DFS clear-text auth)
+AC_ARG_WITH(dfs,
+[ --with-dce-dfs Include DCE/DFS clear-text auth support (default=no)],
+[ case "$withval" in
+ yes)
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(WITH_DFS,1,[Whether to include DFS support])
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+ AC_MSG_RESULT(no)
+)
+
+########################################################
+# Compile with LDAP support?
+
+with_ldap_support=auto
+AC_MSG_CHECKING([for LDAP support])
+
+AC_ARG_WITH(ldap,
+[ --with-ldap LDAP support (default yes)],
+[ case "$withval" in
+ yes|no)
+ with_ldap_support=$withval
+ ;;
+ esac ])
+
+AC_MSG_RESULT($with_ldap_support)
+
+SMBLDAP=""
+AC_SUBST(SMBLDAP)
+SMBLDAPUTIL=""
+AC_SUBST(SMBLDAPUTIL)
+if test x"$with_ldap_support" != x"no"; then
+
+ ##################################################################
+ # first test for ldap.h and lber.h
+ # (ldap.h is required for this test)
+ AC_CHECK_HEADERS(ldap.h lber.h)
+
+ if test x"$ac_cv_header_ldap_h" != x"yes"; then
+ if test x"$with_ldap_support" = x"yes"; then
+ AC_MSG_ERROR(ldap.h is needed for LDAP support)
+ else
+ AC_MSG_WARN(ldap.h is needed for LDAP support)
+ fi
+
+ with_ldap_support=no
+ fi
+fi
+
+if test x"$with_ldap_support" != x"no"; then
+ ac_save_LIBS=$LIBS
+
+ ##################################################################
+ # we might need the lber lib on some systems. To avoid link errors
+ # this test must be before the libldap test
+ AC_CHECK_LIB_EXT(lber, LDAP_LIBS, ber_scanf)
+
+ ########################################################
+ # now see if we can find the ldap libs in standard paths
+ AC_CHECK_LIB_EXT(ldap, LDAP_LIBS, ldap_init)
+
+ AC_CHECK_FUNC_EXT(ldap_domain2hostlist,$LDAP_LIBS)
+
+ ########################################################
+ # If we have LDAP, does it's rebind procedure take 2 or 3 arguments?
+ # Check found in pam_ldap 145.
+ AC_CHECK_FUNC_EXT(ldap_set_rebind_proc,$LDAP_LIBS)
+
+ LIBS="$LIBS $LDAP_LIBS"
+ AC_CACHE_CHECK(whether ldap_set_rebind_proc takes 3 arguments, smb_ldap_cv_ldap_set_rebind_proc, [
+ AC_TRY_COMPILE([
+ #include <lber.h>
+ #include <ldap.h>],
+ [ldap_set_rebind_proc(0, 0, 0);],
+ [smb_ldap_cv_ldap_set_rebind_proc=3],
+ [smb_ldap_cv_ldap_set_rebind_proc=2]
+ )
+ ])
+
+ AC_DEFINE_UNQUOTED(LDAP_SET_REBIND_PROC_ARGS, $smb_ldap_cv_ldap_set_rebind_proc, [Number of arguments to ldap_set_rebind_proc])
+
+ AC_CHECK_FUNC_EXT(ldap_initialize,$LDAP_LIBS)
+
+ if test x"$ac_cv_lib_ext_ldap_ldap_init" = x"yes" -a x"$ac_cv_func_ext_ldap_domain2hostlist" = x"yes"; then
+ AC_DEFINE(HAVE_LDAP,1,[Whether ldap is available])
+ default_static_modules="$default_static_modules pdb_ldap idmap_ldap";
+ default_shared_modules="$default_shared_modules config_ldap";
+ SMBLDAP="lib/smbldap.o"
+ SMBLDAPUTIL="lib/smbldap_util.o"
+ with_ldap_support=yes
+ AC_MSG_CHECKING(whether LDAP support is used)
+ AC_MSG_RESULT(yes)
+ else
+ if test x"$with_ldap_support" = x"yes"; then
+ AC_MSG_ERROR(libldap is needed for LDAP support)
+ else
+ AC_MSG_WARN(libldap is needed for LDAP support)
+ fi
+
+ LDAP_LIBS=""
+ with_ldap_support=no
+ fi
+ LIBS=$ac_save_LIBS
+fi
+
+
+#################################################
+# active directory support
+
+with_ads_support=auto
+AC_MSG_CHECKING([for Active Directory and krb5 support])
+
+AC_ARG_WITH(ads,
+[ --with-ads Active Directory support (default auto)],
+[ case "$withval" in
+ yes|no)
+ with_ads_support="$withval"
+ ;;
+ esac ])
+
+AC_MSG_RESULT($with_ads_support)
+
+FOUND_KRB5=no
+KRB5_LIBS=""
+
+if test x"$with_ldap_support" != x"yes"; then
+ if test x"$with_ads_support" = x"yes"; then
+ AC_MSG_ERROR(Active Directory Support requires LDAP support)
+ elif test x"$with_ads_support" != x"no"; then
+ AC_MSG_WARN(Active Directory Support requires LDAP support)
+ fi
+ with_ads_support=no
+fi
+
+if test x"$with_ads_support" != x"no"; then
+
+ # Do no harm to the values of CFLAGS and LIBS while testing for
+ # Kerberos support.
+
+ #################################################
+ # check for krb5-config from recent MIT and Heimdal kerberos 5
+ AC_PATH_PROG(KRB5_CONFIG, krb5-config)
+ AC_MSG_CHECKING(for working krb5-config)
+ if test -x "$KRB5_CONFIG"; then
+ ac_save_CFLAGS=$CFLAGS
+ CFLAGS="";export CFLAGS
+ ac_save_LDFLAGS=$LDFLAGS
+ LDFLAGS="";export LDFLAGS
+ KRB5_LIBS="`$KRB5_CONFIG --libs gssapi`"
+ KRB5_CFLAGS="`$KRB5_CONFIG --cflags | sed s/@INCLUDE_des@//`"
+ KRB5_CPPFLAGS="`$KRB5_CONFIG --cflags | sed s/@INCLUDE_des@//`"
+ CFLAGS=$ac_save_CFLAGS;export CFLAGS
+ LDFLAGS=$ac_save_LDFLAGS;export LDFLAGS
+ FOUND_KRB5=yes
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_RESULT(no. Fallback to previous krb5 detection strategy)
+ fi
+
+ if test x$FOUND_KRB5 = x"no"; then
+ #################################################
+ # check for location of Kerberos 5 install
+ AC_MSG_CHECKING(for kerberos 5 install path)
+ AC_ARG_WITH(krb5,
+ [ --with-krb5=base-dir Locate Kerberos 5 support (default=/usr)],
+ [ case "$withval" in
+ no)
+ AC_MSG_RESULT(no krb5-path given)
+ ;;
+ yes)
+ AC_MSG_RESULT(/usr)
+ FOUND_KRB5=yes
+ ;;
+ *)
+ AC_MSG_RESULT($withval)
+ KRB5_CFLAGS="-I$withval/include"
+ KRB5_CPPFLAGS="-I$withval/include"
+ KRB5_LDFLAGS="-L$withval/lib"
+ FOUND_KRB5=yes
+ ;;
+ esac ],
+ AC_MSG_RESULT(no krb5-path given)
+ )
+ fi
+
+ if test x$FOUND_KRB5 = x"no"; then
+ #################################################
+ # see if this box has the SuSE location for the heimdal krb implementation
+ AC_MSG_CHECKING(for /usr/include/heimdal)
+ if test -d /usr/include/heimdal; then
+ if test -f /usr/lib/heimdal/lib/libkrb5.a; then
+ KRB5_CFLAGS="-I/usr/include/heimdal"
+ KRB5_CPPFLAGS="-I/usr/include/heimdal"
+ KRB5_LDFLAGS="-L/usr/lib/heimdal/lib"
+ AC_MSG_RESULT(yes)
+ else
+ KRB5_CFLAGS="-I/usr/include/heimdal"
+ KRB5_CPPFLAGS="-I/usr/include/heimdal"
+ AC_MSG_RESULT(yes)
+ fi
+ else
+ AC_MSG_RESULT(no)
+ fi
+ fi
+
+ if test x$FOUND_KRB5 = x"no"; then
+ #################################################
+ # see if this box has the RedHat location for kerberos
+ AC_MSG_CHECKING(for /usr/kerberos)
+ if test -d /usr/kerberos -a -f /usr/kerberos/lib/libkrb5.a; then
+ KRB5_LDFLAGS="-L/usr/kerberos/lib"
+ KRB5_CFLAGS="-I/usr/kerberos/include"
+ KRB5_CPPFLAGS="-I/usr/kerberos/include"
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_RESULT(no)
+ fi
+ fi
+
+ ac_save_CFLAGS=$CFLAGS
+ ac_save_CPPFLAGS=$CPPFLAGS
+ ac_save_LDFLAGS=$LDFLAGS
+
+ CFLAGS="$CFLAGS $KRB5_CFLAGS"
+ CPPFLAGS="$CPPFLAGS $KRB5_CPPFLAGS"
+ LDFLAGS="$LDFLAGS $KRB5_LDFLAGS"
+
+ KRB5_LIBS="$KRB5_LDFLAGS $KRB5_LIBS"
+
+ # now check for krb5.h. Some systems have the libraries without the headers!
+ # note that this check is done here to allow for different kerberos
+ # include paths
+ AC_CHECK_HEADERS(krb5.h)
+
+ if test x"$ac_cv_header_krb5_h" = x"no"; then
+
+ # Give a warning if AD support was not explicitly requested,
+ # i.e with_ads_support = auto, otherwise die with an error.
+
+ if test x"$with_ads_support" = x"yes"; then
+ AC_MSG_ERROR([Active Directory cannot be supported without krb5.h])
+ else
+ AC_MSG_WARN([Active Directory cannot be supported without krb5.h])
+ fi
+
+ # Turn off AD support and restore CFLAGS and LIBS variables
+
+ with_ads_support="no"
+
+ CFLAGS=$ac_save_CFLAGS
+ CPPFLAGS=$ac_save_CPPFLAGS
+ LDFLAGS=$ac_save_LDFLAGS
+ fi
+fi
+
+# Now we have determined whether we really want ADS support
+
+if test x"$with_ads_support" != x"no"; then
+ ac_save_LIBS=$LIBS
+
+ # now check for gssapi headers. This is also done here to allow for
+ # different kerberos include paths
+ AC_CHECK_HEADERS(gssapi.h gssapi/gssapi_generic.h gssapi/gssapi.h com_err.h)
+
+ ##################################################################
+ # we might need the k5crypto and com_err libraries on some systems
+ AC_CHECK_LIB_EXT(com_err, KRB5_LIBS, _et_list)
+ AC_CHECK_LIB_EXT(k5crypto, KRB5_LIBS, krb5_encrypt_data)
+
+ # Heimdal checks.
+ AC_CHECK_LIB_EXT(crypto, KRB5_LIBS, des_set_key)
+ AC_CHECK_LIB_EXT(asn1, KRB5_LIBS, copy_Authenticator)
+ AC_CHECK_LIB_EXT(roken, KRB5_LIBS, roken_getaddrinfo_hostspec)
+
+ # Heimdal checks. On static Heimdal gssapi must be linked before krb5.
+ AC_CHECK_LIB_EXT(gssapi, KRB5_LIBS, gss_display_status,[],[],
+ AC_DEFINE(HAVE_GSSAPI,1,[Whether GSSAPI is available]))
+
+ ########################################################
+ # now see if we can find the krb5 libs in standard paths
+ # or as specified above
+ AC_CHECK_LIB_EXT(krb5, KRB5_LIBS, krb5_mk_req_extended)
+
+ ########################################################
+ # now see if we can find the gssapi libs in standard paths
+ AC_CHECK_LIB_EXT(gssapi_krb5, KRB5_LIBS,gss_display_status,[],[],
+ AC_DEFINE(HAVE_GSSAPI,1,[Whether GSSAPI is available]))
+
+ AC_CHECK_FUNC_EXT(krb5_set_real_time, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_set_default_in_tkt_etypes, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_set_default_tgs_ktypes, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_principal2salt, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_use_enctype, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_string_to_key, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_get_pw_salt, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_string_to_key_salt, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_auth_con_setkey, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_auth_con_setuseruserkey, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_locate_kdc, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_get_permitted_enctypes, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_get_default_in_tkt_etypes, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_free_ktypes, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_free_data_contents, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_principal_get_comp_string, $KRB5_LIBS)
+
+ LIBS="$LIBS $KRB5_LIBS"
+
+ AC_CACHE_CHECK([for addrtype in krb5_address],
+ samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS,[
+ AC_TRY_COMPILE([#include <krb5.h>],
+ [krb5_address kaddr; kaddr.addrtype = ADDRTYPE_INET;],
+ samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS=yes,
+ samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS=no)])
+
+ if test x"$samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS" = x"yes"; then
+ AC_DEFINE(HAVE_ADDRTYPE_IN_KRB5_ADDRESS,1,
+ [Whether the krb5_address struct has a addrtype property])
+ fi
+
+ AC_CACHE_CHECK([for addr_type in krb5_address],
+ samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS,[
+ AC_TRY_COMPILE([#include <krb5.h>],
+ [krb5_address kaddr; kaddr.addr_type = KRB5_ADDRESS_INET;],
+ samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS=yes,
+ samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS=no)])
+
+ if test x"$samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS" = x"yes"; then
+ AC_DEFINE(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS,1,
+ [Whether the krb5_address struct has a addr_type property])
+ fi
+
+ AC_CACHE_CHECK([for enc_part2 in krb5_ticket],
+ samba_cv_HAVE_KRB5_TKT_ENC_PART2,
+ [AC_TRY_COMPILE([#include <krb5.h>],
+ [krb5_ticket tkt; tkt.enc_part2->authorization_data[0]->contents = NULL;],
+ samba_cv_HAVE_KRB5_TKT_ENC_PART2=yes,samba_cv_HAVE_KRB5_TKT_ENC_PART2=no)])
+
+ if test x"$samba_cv_HAVE_KRB5_TKT_ENC_PART2" = x"yes"; then
+ AC_DEFINE(HAVE_KRB5_TKT_ENC_PART2,1,
+ [Whether the krb5_ticket struct has a enc_part2 property])
+ fi
+
+ AC_CACHE_CHECK([for keyvalue in krb5_keyblock],
+ samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE,[
+ AC_TRY_COMPILE([#include <krb5.h>],
+ [krb5_keyblock key; key.keyvalue.data = NULL;],
+ samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE=yes,
+ samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE=no)])
+
+ if test x"$samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE" = x"yes"; then
+ AC_DEFINE(HAVE_KRB5_KEYBLOCK_KEYVALUE,1,
+ [Whether the krb5_keyblock struct has a keyvalue property])
+ fi
+
+ AC_CACHE_CHECK([for ENCTYPE_ARCFOUR_HMAC_MD5],
+ samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5,[
+ AC_TRY_COMPILE([#include <krb5.h>],
+ [krb5_enctype enctype; enctype = ENCTYPE_ARCFOUR_HMAC_MD5;],
+ samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5=yes,
+ samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5=no)])
+ AC_CACHE_CHECK([for KEYTYPE_ARCFOUR_56],
+ samba_cv_HAVE_KEYTYPE_ARCFOUR_56,[
+ AC_TRY_COMPILE([#include <krb5.h>],
+ [krb5_keytype keytype; keytype = KEYTYPE_ARCFOUR_56;],
+ samba_cv_HAVE_KEYTYPE_ARCFOUR_56=yes,
+ samba_cv_HAVE_KEYTYPE_ARCFOUR_56=no)])
+# Heimdals with KEYTYPE_ARCFOUR but not KEYTYPE_ARCFOUR_56 are broken
+# w.r.t. arcfour and windows, so we must not enable it here
+ if test x"$samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5" = x"yes" -a\
+ x"$samba_cv_HAVE_KEYTYPE_ARCFOUR_56" = x"yes"; then
+ AC_DEFINE(HAVE_ENCTYPE_ARCFOUR_HMAC_MD5,1,
+ [Whether the ENCTYPE_ARCFOUR_HMAC_MD5 key type is available])
+ fi
+
+ AC_CACHE_CHECK([for AP_OPTS_USE_SUBKEY],
+ samba_cv_HAVE_AP_OPTS_USE_SUBKEY,[
+ AC_TRY_COMPILE([#include <krb5.h>],
+ [krb5_flags ap_options; ap_options = AP_OPTS_USE_SUBKEY;],
+ samba_cv_HAVE_AP_OPTS_USE_SUBKEY=yes,
+ samba_cv_HAVE_AP_OPTS_USE_SUBKEY=no)])
+
+ if test x"$samba_cv_HAVE_AP_OPTS_USE_SUBKEY" = x"yes"; then
+ AC_DEFINE(HAVE_AP_OPTS_USE_SUBKEY,1,
+ [Whether the AP_OPTS_USE_SUBKEY ap option is available])
+ fi
+
+ AC_CACHE_CHECK([for the krb5_princ_component macro],
+ samba_cv_HAVE_KRB5_PRINC_COMPONENT,[
+ AC_TRY_LINK([#include <krb5.h>],
+ [const krb5_data *pkdata; krb5_context context; krb5_principal principal; pkdata = krb5_princ_component(context, principal, 0);],
+ samba_cv_HAVE_KRB5_PRINC_COMPONENT=yes,
+ samba_cv_HAVE_KRB5_PRINC_COMPONENT=no)])
+
+ if test x"$samba_cv_HAVE_KRB5_PRINC_COMPONENT" = x"yes"; then
+ AC_DEFINE(HAVE_KRB5_PRINC_COMPONENT,1,
+ [Whether krb5_princ_component is available])
+ fi
+
+ AC_CACHE_CHECK([for key in krb5_keytab_entry],
+ samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY,[
+ AC_TRY_COMPILE([#include <krb5.h>],
+ [krb5_keytab_entry entry; krb5_keyblock e; entry.key = e;],
+ samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY=yes,
+ samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY=no)])
+
+ if test x"$samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY" = x"yes"; then
+ AC_DEFINE(HAVE_KRB5_KEYTAB_ENTRY_KEY,1,
+ [Whether krb5_keytab_entry has key member])
+ fi
+
+ AC_CACHE_CHECK([for keyblock in krb5_keytab_entry],
+ samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK,[
+ AC_TRY_COMPILE([#include <krb5.h>],
+ [krb5_keytab_entry entry; entry.keyblock.keytype = 0;],
+ samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK=yes,
+ samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK=no)])
+
+ if test x"$samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK" = x"yes"; then
+ AC_DEFINE(HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK,1,
+ [Whether krb5_keytab_entry has keyblock member])
+ fi
+
+ if test x"$ac_cv_lib_ext_krb5_krb5_mk_req_extended" = x"yes"; then
+ AC_DEFINE(HAVE_KRB5,1,[Whether to have KRB5 support])
+ AC_DEFINE(WITH_ADS,1,[Whether to include Active Directory support])
+ AC_MSG_CHECKING(whether Active Directory and krb5 support is used)
+ AC_MSG_RESULT(yes)
+ else
+ if test x"$with_ads_support" = x"yes"; then
+ AC_MSG_ERROR(libkrb5 is needed for Active Directory support)
+ else
+ AC_MSG_WARN(libkrb5 is needed for Active Directory support)
+ fi
+ KRB5_LIBS=""
+ with_ads_support=no
+ fi
+ LIBS="$ac_save_LIBS"
+fi
+
+########################################################
+# Compile experimental passdb backends?
+# (pdb_xml, pdb_mysql, pdb_pgsql)
+AC_MSG_CHECKING(whether to build experimental passdb libraries)
+AC_ARG_WITH(expsam,
+[ --with-expsam=<list> Include experimental passdb libraries (default=no)]
+[ Valid choices include (comma separated list): ]
+[ xml, mysql & pgsql],
+[ expsam_pdb_modules=`echo "$withval" | sed 's/,/ /g'`
+ if test "z$expsam_pdb_modules" = "zyes"; then
+ expsam_pdb_modules="xml mysql pgsql"
+ fi
+ AC_MSG_RESULT($expsam_pdb_modules)
+ for i in $expsam_pdb_modules
+ do
+ case "$i" in
+ xml|all|yes)
+ ## pdb_xml
+ AM_PATH_XML2([2.0.0],[default_shared_modules="$default_shared_modules pdb_xml"],[AC_MSG_ERROR([Can't find XML libraries while XML support is requested])])
+ CFLAGS="$CFLAGS $XML_CFLAGS"
+ ;;
+ mysql|all|yes)
+ ## pdb_mysql
+ AM_PATH_MYSQL([default_shared_modules="$default_shared_modules pdb_mysql"],[AC_MSG_ERROR([Can't find MySQL libraries while MySQL support is requested])])
+ CFLAGS="$CFLAGS $MYSQL_CFLAGS"
+ ;;
+ pgsql|all|yes)
+ ## pdb_pgsql
+ AM_PATH_PGSQL([default_shared_modules="$default_shared_modules pdb_pgsql"],[])
+ CFLAGS="$CFLAGS $PGSQL_CFLAGS"
+ ;;
+ no)
+ ;;
+ *)
+ echo "Unknown module name \"$i\"! Exiting..."
+ exit 1
+ ;;
+ esac
+ done ],
+ AC_MSG_RESULT(no)
+)
+
+#################################################
+# check for automount support
+AC_MSG_CHECKING(whether to use automount)
+AC_ARG_WITH(automount,
+[ --with-automount Include automount support (default=no)],
+[ case "$withval" in
+ yes)
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(WITH_AUTOMOUNT,1,[Whether to include automount support])
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+ AC_MSG_RESULT(no)
+)
+
+#################################################
+# check for smbmount support
+AC_MSG_CHECKING(whether to use smbmount)
+AC_ARG_WITH(smbmount,
+[ --with-smbmount Include smbmount (Linux only) support (default=no)],
+[ case "$withval" in
+ yes)
+ case "$host_os" in
+ *linux*)
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(WITH_SMBMOUNT,1,[Whether to build smbmount])
+ EXTRA_BIN_PROGS="$EXTRA_BIN_PROGS bin/smbmount bin/smbmnt bin/smbumount"
+ ;;
+ *)
+ AC_MSG_ERROR(not on a linux system!)
+ ;;
+ esac
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+ AC_MSG_RESULT(no)
+)
+
+
+#################################################
+# check for a PAM clear-text auth, accounts, password and session support
+with_pam_for_crypt=no
+AC_MSG_CHECKING(whether to use PAM)
+AC_ARG_WITH(pam,
+[ --with-pam Include PAM support (default=no)],
+[ case "$withval" in
+ yes)
+ AC_MSG_RESULT(yes)
+ if test x"$ac_cv_header_security_pam_appl_h" = x"no"; then
+ if test x"$ac_cv_header_security_pam_modules_h" = x"no"; then
+ if test x"$ac_cv_header_security__pam_macros_h" = x"no"; then
+ AC_MSG_ERROR(--with-pam specified but no PAM headers found)
+ fi
+ fi
+ fi
+ AC_DEFINE(WITH_PAM,1,[Whether to include PAM support])
+ AUTH_LIBS="$AUTH_LIBS -lpam"
+ with_pam_for_crypt=yes
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+ AC_MSG_RESULT(no)
+)
+
+# we can't build a pam module if we don't have pam.
+AC_CHECK_LIB(pam, pam_get_data, [AC_DEFINE(HAVE_LIBPAM,1,[Whether libpam is available])])
+
+#################################################
+# check for pam_smbpass support
+AC_MSG_CHECKING(whether to use pam_smbpass)
+AC_ARG_WITH(pam_smbpass,
+[ --with-pam_smbpass Build PAM module for authenticating against passdb backends (default=no)],
+[ case "$withval" in
+ yes)
+ AC_MSG_RESULT(yes)
+
+ # Conditions under which pam_smbpass should not be built.
+
+ if test x$PICFLAGS = x; then
+ AC_MSG_ERROR([No support for PIC code])
+ elif test x"$ac_cv_header_security_pam_appl_h" = x"no"; then
+ AC_MSG_ERROR([No security/pam_appl.h found])
+ elif test x$ac_cv_lib_pam_pam_get_data = xno; then
+ AC_MSG_ERROR([No libpam found])
+ else
+ AUTH_LIBS="$AUTH_LIBS -lpam"
+ SHLIB_PROGS="$SHLIB_PROGS bin/pam_smbpass.$SHLIBEXT"
+ fi
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+ AC_MSG_RESULT(no)
+)
+
+
+###############################################
+# test for where we get crypt() from
+AC_SEARCH_LIBS(crypt, [crypt],
+ [test "$ac_cv_search_crypt" = "none required" || AUTH_LIBS="-lcrypt $AUTH_LIBS"
+ AC_DEFINE(HAVE_CRYPT,1,[Whether the system has the crypt() function])])
+
+##
+## moved after the check for -lcrypt in order to
+## ensure that the necessary libraries are included
+## check checking for truncated salt. Wrapped by the
+## $with_pam_for_crypt variable as above --jerry
+##
+if test $with_pam_for_crypt = no; then
+AC_CACHE_CHECK([for a crypt that needs truncated salt],samba_cv_HAVE_TRUNCATED_SALT,[
+crypt_LIBS="$LIBS"
+LIBS="$AUTH_LIBS $LIBS"
+AC_TRY_RUN([#include "${srcdir-.}/tests/crypttest.c"],
+ samba_cv_HAVE_TRUNCATED_SALT=no,samba_cv_HAVE_TRUNCATED_SALT=yes,samba_cv_HAVE_TRUNCATED_SALT=cross)
+LIBS="$crypt_LIBS"])
+if test x"$samba_cv_HAVE_TRUNCATED_SALT" = x"yes"; then
+ AC_DEFINE(HAVE_TRUNCATED_SALT,1,[Whether crypt needs truncated salt])
+fi
+fi
+
+
+dictpath="/usr/lib/cracklib_dict"
+with_cracklib=yes
+###############################################
+# test for where we get FaciestCheck from
+AC_MSG_CHECKING(where to use cracklib from (default=$dictpath))
+AC_ARG_WITH(cracklib,
+[ --with-cracklib[=DIR] Look for cracklib dictionary in this location ],
+[ case "$withval" in
+ yes)
+ AC_MSG_RESULT(${dictpath})
+ ;;
+ no)
+ AC_MSG_RESULT(no)
+ dictpath=""
+ ;;
+ *)
+ dictpath="$withval"
+ AC_MSG_RESULT(${dictpath})
+ ;;
+ esac ],
+ dictpath=""
+ AC_MSG_RESULT(no)
+)
+
+if test x$dictpath != x""; then
+ AC_SEARCH_LIBS(FascistCheck, [crack],
+ [test "$ac_cv_search_crack" = "none required" || samba_cv_found_crack="yes"
+ AC_DEFINE(HAVE_CRACK,1,[Whether the system has the FaciestCheck function from cracklib])])
+
+ crack_saved_libs=$LIBS;
+
+ if test x$samba_cv_found_crack=x"yes"; then
+ AC_SEARCH_LIBS(CRACKLIB_DICTPATH, [crypt],
+ AC_DEFINE(HAVE_CRACKLIB_DICTPATH, 1, [Whether we have given a CRACKLIB_DICTPATH in our headers])
+ )
+
+ AC_DEFINE_UNQUOTED(SAMBA_CRACKLIB_DICTPATH, "$dictpath", [Where the cracklib dictionay is])
+ AC_MSG_CHECKING(Whether we have a working cracklib)
+ AC_TRY_RUN([
+ #include "${srcdir-.}/tests/crack.c"],
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_WORKING_CRACKLIB,1,[Whether we have a working cracklib])
+ AUTH_LIBS="-lcrack $AUTH_LIBS",
+
+ AC_MSG_RESULT(no)
+ AC_MSG_WARN(cracklib exists - but does not function correctly),
+
+ AC_MSG_RESULT(no)
+ AC_MSG_WARN(cannot test-run when cross-compiling)
+ )
+ else
+ AC_MSG_CHECKING(Whether we have cracklib)
+ AC_MSG_RESULT(no)
+ fi
+ LIBS=$crack_saved_libs
+fi
+
+########################################################################################
+##
+## TESTS FOR SAM BACKENDS. KEEP THESE GROUPED TOGETHER
+##
+########################################################################################
+
+#################################################
+# check for a LDAP password database configuration backwards compatibility
+AC_MSG_CHECKING(whether to use LDAP SAM 2.2 compatible configuration)
+AC_ARG_WITH(ldapsam,
+[ --with-ldapsam Include LDAP SAM 2.2 compatible configuration (default=no)],
+[ case "$withval" in
+ yes)
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(WITH_LDAP_SAMCONFIG,1,[Whether to include 2.2 compatible LDAP SAM configuration])
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+ AC_MSG_RESULT(no)
+)
+
+########################################################################################
+##
+## END OF TESTS FOR SAM BACKENDS.
+##
+########################################################################################
+
+#################################################
+# check for a NISPLUS_HOME support
+AC_MSG_CHECKING(whether to use NISPLUS_HOME)
+AC_ARG_WITH(nisplus-home,
+[ --with-nisplus-home Include NISPLUS_HOME support (default=no)],
+[ case "$withval" in
+ yes)
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(WITH_NISPLUS_HOME,1,[Whether to include nisplus_home support])
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+ AC_MSG_RESULT(no)
+)
+
+#################################################
+# check for syslog logging
+AC_MSG_CHECKING(whether to use syslog logging)
+AC_ARG_WITH(syslog,
+[ --with-syslog Include experimental SYSLOG support (default=no)],
+[ case "$withval" in
+ yes)
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(WITH_SYSLOG,1,[Whether to include experimental syslog support])
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+ AC_MSG_RESULT(no)
+)
+
+#################################################
+# check for a shared memory profiling support
+AC_MSG_CHECKING(whether to use profiling)
+AC_ARG_WITH(profiling-data,
+[ --with-profiling-data Include gathering source code profile information (default=no)],
+[ case "$withval" in
+ yes)
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(WITH_PROFILE,1,[Whether to use profiling])
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+ AC_MSG_RESULT(no)
+)
+
+
+#################################################
+# check for experimental disk-quotas support
+
+samba_cv_WITH_QUOTAS=auto
+samba_cv_TRY_QUOTAS=no
+samba_cv_RUN_QUOTA_TESTS=auto
+samba_cv_WITH_SYS_QUOTAS=auto
+samba_cv_TRY_SYS_QUOTAS=no
+samba_cv_SYSQUOTA_FOUND=no;
+
+AC_MSG_CHECKING(whether to try disk-quotas support)
+AC_ARG_WITH(quotas,
+[ --with-quotas Include disk-quota support (default=no)],
+[ case "$withval" in
+ yes)
+ AC_MSG_RESULT(yes)
+ samba_cv_WITH_QUOTAS=yes
+ samba_cv_TRY_QUOTAS=yes
+ samba_cv_RUN_QUOTA_TESTS=yes
+ #set sys quotas to auto in this case
+ samba_cv_TRY_SYS_QUOTAS=auto
+ ;;
+ auto)
+ AC_MSG_RESULT(auto)
+ samba_cv_WITH_QUOTAS=auto
+ samba_cv_TRY_QUOTAS=auto
+ samba_cv_RUN_QUOTA_TESTS=auto
+ #set sys quotas to auto in this case
+ samba_cv_TRY_SYS_QUOTAS=auto
+ ;;
+ no)
+ AC_MSG_RESULT(no)
+ samba_cv_WITH_QUOTAS=no
+ samba_cv_TRY_QUOTAS=no
+ samba_cv_RUN_QUOTA_TESTS=no
+ ;;
+ *)
+ AC_MSG_RESULT(${samba_cv_TRY_QUOTAS})
+ ;;
+ esac ],
+ AC_MSG_RESULT(${samba_cv_TRY_QUOTAS})
+)
+
+AC_MSG_CHECKING(whether to try the new lib/sysquotas.c interface)
+AC_ARG_WITH(sys-quotas,
+[ --with-sys-quotas Include lib/sysquotas.c support (default=auto)],
+[ case "$withval" in
+ yes)
+ AC_MSG_RESULT(yes)
+ samba_cv_WITH_SYS_QUOTAS=yes
+ samba_cv_TRY_SYS_QUOTAS=yes
+ samba_cv_RUN_QUOTA_TESTS=yes
+ ;;
+ auto)
+ AC_MSG_RESULT(auto)
+ samba_cv_WITH_SYS_QUOTAS=auto
+ samba_cv_TRY_SYS_QUOTAS=auto
+ samba_cv_RUN_QUOTA_TESTS=auto
+ ;;
+ no)
+ AC_MSG_RESULT(no)
+ samba_cv_WITH_SYS_QUOTAS=no
+ samba_cv_TRY_SYS_QUOTAS=no
+ ;;
+ *)
+ AC_MSG_RESULT(${samba_cv_TRY_SYS_QUOTAS})
+ ;;
+ esac ],
+ AC_MSG_RESULT(${samba_cv_TRY_SYS_QUOTAS})
+)
+
+if test x"$samba_cv_TRY_SYS_QUOTAS" = x"auto"; then
+AC_MSG_CHECKING(whether to try the lib/sysquotas.c interface on ${host_os})
+ case "$host_os" in
+ *linux*)
+ AC_MSG_RESULT(yes)
+ samba_cv_TRY_SYS_QUOTAS=yes
+ samba_cv_RUN_QUOTA_TESTS=yes
+ samba_cv_SYSQUOTA_FOUND=yes
+ AC_DEFINE(HAVE_QUOTACTL_LINUX,1,[Whether Linux quota support is available])
+ samba_cv_sysquotas_file="lib/sysquotas_linux.c"
+ AC_DEFINE(HAVE_LINUX_XFS_QUOTAS,1,[Whether Linux xfs quota support is available])
+ samba_cv_found_xfs_header=yes
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ samba_cv_TRY_SYS_QUOTAS=no
+ ;;
+ esac
+fi
+
+#############################################
+# only check for quota stuff if --with-quotas
+if test x"$samba_cv_RUN_QUOTA_TESTS" != x"no"; then
+
+# some broken header files need this
+AC_CHECK_HEADER(asm/types.h,[
+ AC_DEFINE(HAVE_ASM_TYPES_H,1,[check for <asm/types.h>])
+ AC_ADD_INCLUDE(<asm/types.h>)
+ ])
+
+# For quotas on Veritas VxFS filesystems
+AC_CHECK_HEADERS(sys/fs/vx_quota.h)
+
+# For sys/quota.h and linux/quota.h
+AC_CHECK_HEADERS(sys/quota.h)
+
+if test x"$samba_cv_found_xfs_header" != x"yes"; then
+# if we have xfs quota support <sys/quota.h> (IRIX) we should use it
+AC_CACHE_CHECK([for XFS QUOTA in <sys/quota.h>],samba_cv_HAVE_SYS_QUOTA_XFS, [
+AC_TRY_COMPILE([
+#include "confdefs.h"
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_ASM_TYPES_H
+#include <asm/types.h>
+#endif
+#include <sys/quota.h>
+],[int i = Q_XGETQUOTA;],
+samba_cv_HAVE_SYS_QUOTA_XFS=yes,samba_cv_HAVE_SYS_QUOTA_XFS=no)])
+if test "$samba_cv_HAVE_SYS_QUOTA_XFS"x = "yes"x; then
+ samba_cv_found_xfs_header=yes
+fi
+fi
+
+# if we have struct dqblk .dqb_fsoftlimit instead of .dqb_isoftlimit on IRIX
+AC_CACHE_CHECK([if struct dqblk has .dqb_fsoftlimit],samba_cv_HAVE_DQB_FSOFTLIMIT, [
+AC_TRY_COMPILE([
+#include "confdefs.h"
+#ifdef HAVE_SYS_QUOTA_H
+#include <sys/quota.h>
+#endif
+],[
+struct dqblk D;
+D.dqb_fsoftlimit = 0;],
+samba_cv_HAVE_DQB_FSOFTLIMIT=yes,samba_cv_HAVE_DQB_FSOFTLIMIT=no)])
+if test "$samba_cv_HAVE_DQB_FSOFTLIMIT"x = "yes"x; then
+ AC_DEFINE(HAVE_DQB_FSOFTLIMIT,1,[struct dqblk .dqb_fsoftlimit])
+fi
+
+##################
+# look for a working quota system
+
+if test x"$samba_cv_SYSQUOTA_FOUND" != x"yes"; then
+AC_CACHE_CHECK([for long quotactl(int cmd, char *special, qid_t id, caddr_t addr)],samba_cv_HAVE_QUOTACTL_4A,[
+AC_TRY_RUN_STRICT([
+#define HAVE_QUOTACTL_4A 1
+#define AUTOCONF_TEST 1
+#include "confdefs.h"
+#include "${srcdir-.}/tests/sysquotas.c"],[$Werror_FLAGS],[$CPPFLAGS],[$LDFLAGS],
+ samba_cv_HAVE_QUOTACTL_4A=yes,samba_cv_HAVE_QUOTACTL_4A=no,samba_cv_HAVE_QUOTACTL_4A=cross)])
+if test x"$samba_cv_HAVE_QUOTACTL_4A" = x"yes"; then
+ samba_cv_SYSQUOTA_FOUND=yes;
+ AC_DEFINE(HAVE_QUOTACTL_4A,1,[Whether long quotactl(int cmd, char *special, qid_t id, caddr_t addr) is available])
+ samba_cv_sysquotas_file="lib/sysquotas_4A.c"
+fi
+fi
+
+if test x"$samba_cv_SYSQUOTA_FOUND" != x"yes"; then
+AC_CACHE_CHECK([for int quotactl(const char *path, int cmd, int id, char *addr)],samba_cv_HAVE_QUOTACTL_4B,[
+AC_TRY_RUN_STRICT([
+#define HAVE_QUOTACTL_4B 1
+#define AUTOCONF_TEST 1
+#include "confdefs.h"
+#include "${srcdir-.}/tests/sysquotas.c"],[$Werror_FLAGS],[$CPPFLAGS],[$LDFLAGS],
+ samba_cv_HAVE_QUOTACTL_4B=yes,samba_cv_HAVE_QUOTACTL_4B=no,samba_cv_HAVE_QUOTACTL_4B=cross)])
+if test x"$samba_cv_HAVE_QUOTACTL_4B" = x"yes"; then
+ echo "int quotactl(const char *path, int cmd, int id, char *addr) is not reworked for the new sys_quota api"
+ samba_cv_SYSQUOTA_FOUND=yes;
+ AC_DEFINE(HAVE_QUOTACTL_4B,1,[Whether int quotactl(const char *path, int cmd, int id, char *addr) is available])
+ samba_cv_sysquotas_file="lib/sysquotas_4B.c"
+fi
+fi
+
+if test x"$samba_cv_SYSQUOTA_FOUND" != x"yes"; then
+AC_CACHE_CHECK([for CRAY int quotactl (char *spec, int request, char *arg)],samba_cv_HAVE_QUOTACTL_3,[
+AC_TRY_RUN_STRICT([
+#define HAVE_QUOTACTL_3 1
+#define AUTOCONF_TEST 1
+#include "confdefs.h"
+#include "${srcdir-.}/tests/sysquotas.c"],[$Werror_FLAGS],[$CPPFLAGS],[$LDFLAGS],
+ samba_cv_HAVE_QUOTACTL_3=yes,samba_cv_HAVE_QUOTACTL_3=no,samba_cv_HAVE_QUOTACTL_3=cross)])
+if test x"$samba_cv_HAVE_QUOTACTL_3" = x"yes"; then
+ echo "CRAY int quotactl (char *spec, int request, char *arg) is NOT reworked for the sys_quota api"
+ samba_cv_SYSQUOTA_FOUND=yes;
+ AC_DEFINE(HAVE_QUOTACTL_3,1,[Whether CRAY int quotactl (char *spec, int request, char *arg); is available])
+ samba_cv_sysquotas_file="lib/sysquotas_3.c"
+fi
+fi
+
+#################################################
+# check for mntent.h and struct mntent
+AC_CHECK_HEADERS(mntent.h)
+#################################################
+# check for setmntent,getmntent,endmntent
+AC_CHECK_FUNCS(setmntent getmntent endmntent)
+
+#################################################
+# check for devnm.h and struct mntent
+AC_CHECK_HEADERS(devnm.h)
+#################################################
+# check for devnm
+AC_CHECK_FUNCS(devnm)
+
+if test x"$samba_cv_WITH_SYS_QUOTAS" = x"yes"; then
+ if test x"$samba_cv_SYSQUOTA_FOUND" != x"yes"; then
+ # if --with-sys-quotas=yes then build it
+ # you have can use the get/set quota command smb.conf
+ # options then
+ samba_cv_SYSQUOTA_FOUND=auto
+ fi
+ if test x"$samba_cv_TRY_SYS_QUOTAS" != x"yes"; then
+ # if --with-sys-quotas=yes then build it
+ # you have can use the get/set quota command smb.conf
+ # options then
+ samba_cv_TRY_SYS_QUOTAS=auto
+ fi
+fi
+
+if test x"$samba_cv_SYSQUOTA_FOUND" != x"no"; then
+AC_CACHE_CHECK([whether the sys_quota interface works],samba_cv_SYSQUOTA_WORKS,[
+SAVE_CPPFLAGS="$CPPFLAGS"
+CPPFLAGS="$CPPFLAGS -I${srcdir-.}/ -I. -I${srcdir-.}/include -I${srcdir-.}/ubiqx -I${srcdir-.}/popt -I${srcdir-.}/smbwrapper -I${srcdir-.}/nsswitch"
+AC_TRY_COMPILE([
+#include "confdefs.h"
+#define NO_PROTO_H 1
+#define NO_CONFIG_H 1
+#define HAVE_SYS_QUOTAS 1
+#include "${srcdir-.}/${samba_cv_sysquotas_file}"
+#include "${srcdir-.}/lib/sysquotas.c"
+],[],samba_cv_SYSQUOTA_WORKS=yes,samba_cv_SYSQUOTA_WORKS=no)
+CPPFLAGS="$SAVE_CPPFLAGS"
+])
+if test x"$samba_cv_SYSQUOTA_WORKS" = x"yes"; then
+AC_MSG_CHECKING(whether to use the new lib/sysquotas.c interface)
+ if test x"$samba_cv_TRY_SYS_QUOTAS" != x"no"; then
+ AC_DEFINE(WITH_QUOTAS,1,[Whether to use disk quota support])
+ AC_DEFINE(HAVE_SYS_QUOTAS,1,[Whether the new lib/sysquotas.c interface can be used])
+ samba_cv_WE_USE_SYS_QUOTAS=yes
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_RESULT(no)
+ fi
+fi
+fi
+
+if test x"$samba_cv_SYSQUOTA_FOUND" != x"no" -a x"$samba_cv_found_xfs_header" = x"yes"; then
+AC_CACHE_CHECK([whether the sys_quota interface works with XFS],samba_cv_SYSQUOTA_WORKS_XFS,[
+SAVE_CPPFLAGS="$CPPFLAGS"
+CPPFLAGS="$CPPFLAGS -I${srcdir-.}/ -I. -I${srcdir-.}/include -I${srcdir-.}/ubiqx -I${srcdir-.}/popt -I${srcdir-.}/smbwrapper -I${srcdir-.}/nsswitch"
+AC_TRY_COMPILE([
+#include "confdefs.h"
+#define NO_PROTO_H 1
+#define NO_CONFIG_H 1
+#define HAVE_SYS_QUOTAS 1
+#define HAVE_XFS_QUOTAS 1
+#include "${srcdir-.}/lib/sysquotas_xfs.c"
+],[],samba_cv_SYSQUOTA_WORKS_XFS=yes,samba_cv_SYSQUOTA_WORKS_XFS=no)
+CPPFLAGS="$SAVE_CPPFLAGS"
+])
+if test x"$samba_cv_SYSQUOTA_WORKS_XFS" = x"yes"; then
+ if test x"$samba_cv_WE_USE_SYS_QUOTAS" = x"yes"; then
+ AC_DEFINE(HAVE_XFS_QUOTAS,1,[Whether xfs quota support is available])
+ fi
+fi
+fi
+
+AC_CACHE_CHECK([whether the old quota support works],samba_cv_QUOTA_WORKS,[
+SAVE_CPPFLAGS="$CPPFLAGS"
+CPPFLAGS="$CPPFLAGS -I${srcdir-.}/ -I. -I${srcdir-.}/include -I${srcdir-.}/ubiqx -I${srcdir-.}/popt -I${srcdir-.}/smbwrapper -I${srcdir-.}/nsswitch"
+AC_TRY_COMPILE([
+#include "confdefs.h"
+#define NO_PROTO_H 1
+#define NO_CONFIG_H 1
+#include "${srcdir-.}/smbd/quotas.c"
+],[],samba_cv_QUOTA_WORKS=yes,samba_cv_QUOTA_WORKS=no)
+CPPFLAGS="$SAVE_CPPFLAGS"
+])
+if test x"$samba_cv_QUOTA_WORKS" = x"yes"; then
+AC_MSG_CHECKING(whether to use the old quota support)
+ if test x"$samba_cv_WE_USE_SYS_QUOTAS" != x"yes"; then
+ if test x"$samba_cv_TRY_QUOTAS" != x"no"; then
+ AC_DEFINE(WITH_QUOTAS,1,[Whether to use disk quota support])
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_RESULT(no)
+ fi
+ else
+ AC_MSG_RESULT(no)
+ fi
+fi
+
+####################
+# End of quota check samba_cv_RUN_QUOTA_TESTS
+fi
+
+#################################################
+# check for experimental utmp accounting
+
+AC_MSG_CHECKING(whether to support utmp accounting)
+WITH_UTMP=yes
+AC_ARG_WITH(utmp,
+[ --with-utmp Include utmp accounting (default, if supported by OS)],
+[ case "$withval" in
+ no)
+ WITH_UTMP=no
+ ;;
+ *)
+ WITH_UTMP=yes
+ ;;
+ esac ],
+)
+
+# utmp requires utmp.h
+# Note similar check earlier, when checking utmp details.
+
+if test x"$WITH_UTMP" = x"yes" -a x"$ac_cv_header_utmp_h" = x"no"; then
+ utmp_no_reason=", no utmp.h on $host_os"
+ WITH_UTMP=no
+fi
+
+# Display test results
+
+if test x"$WITH_UTMP" = x"yes"; then
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(WITH_UTMP,1,[Whether to include experimental utmp accounting])
+else
+ AC_MSG_RESULT(no$utmp_no_reason)
+fi
+
+#################################################
+# choose native language(s) of man pages
+AC_MSG_CHECKING(chosen man pages' language(s))
+AC_ARG_WITH(manpages-langs,
+[ --with-manpages-langs={en,ja,pl} Choose man pages' language(s). (en)],
+[ case "$withval" in
+ yes|no)
+ AC_MSG_WARN(--with-manpages-langs called without argument - will use default)
+ manlangs="en"
+ ;;
+ *)
+ manlangs="$withval"
+ ;;
+ esac
+
+ AC_MSG_RESULT($manlangs)
+ manlangs=`echo $manlangs | sed "s/,/ /g"` # replacing commas with spaces to produce a list
+ AC_SUBST(manlangs)],
+
+ [manlangs="en"
+ AC_MSG_RESULT($manlangs)
+ AC_SUBST(manlangs)]
+)
+
+#################################################
+# should we build libsmbclient?
+
+INSTALLCLIENTCMD_SH=:
+INSTALLCLIENTCMD_A=:
+INSTALLCLIENT=
+LIBSMBCLIENT_SHARED=
+LIBSMBCLIENT=
+AC_MSG_CHECKING(whether to build the libsmbclient shared library)
+AC_ARG_WITH(libsmbclient,
+[ --with-libsmbclient Build the libsmbclient shared library (default=yes if shared libs supported)],
+[ case "$withval" in
+ no)
+ AC_MSG_RESULT(no)
+ ;;
+ *)
+ if test $BLDSHARED = true; then
+ INSTALLCLIENTCMD_SH="\$(INSTALLCMD)"
+ ## build the static version of libsmbclient as well
+ INSTALLCLIENTCMD_A="\$(INSTALLCMD)"
+ LIBSMBCLIENT_SHARED=bin/libsmbclient.$SHLIBEXT
+ LIBSMBCLIENT=libsmbclient
+ AC_MSG_RESULT(yes)
+ else
+ enable_static=yes
+ AC_MSG_RESULT(no shared library support -- will supply static library)
+ fi
+ if test $enable_static = yes; then
+ INSTALLCLIENTCMD_A="\$(INSTALLCMD)"
+ LIBSMBCLIENT=libsmbclient
+ fi
+ INSTALLCLIENT=installclientlib
+ ;;
+ esac ],
+[
+# if unspecified, default is to built it iff possible.
+ if test $BLDSHARED = true; then
+ INSTALLCLIENTCMD_SH="\$(INSTALLCMD)"
+ LIBSMBCLIENT_SHARED=bin/libsmbclient.$SHLIBEXT
+ LIBSMBCLIENT=libsmbclient
+ AC_MSG_RESULT(yes)
+ else
+ enable_static=yes
+ AC_MSG_RESULT(no shared library support -- will supply static library)
+ fi
+ if test $enable_static = yes; then
+ INSTALLCLIENTCMD_A="\$(INSTALLCMD)"
+ LIBSMBCLIENT=libsmbclient
+ fi]
+ INSTALLCLIENT=installclientlib
+)
+
+
+#################################################
+# these tests are taken from the GNU fileutils package
+AC_CHECKING(how to get filesystem space usage)
+space=no
+
+# Test for statvfs64.
+if test $space = no; then
+ # SVR4
+ AC_CACHE_CHECK([statvfs64 function (SVR4)], fu_cv_sys_stat_statvfs64,
+ [AC_TRY_RUN([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+#include <sys/statvfs.h>
+ main ()
+ {
+ struct statvfs64 fsd;
+ exit (statvfs64 (".", &fsd));
+ }],
+ fu_cv_sys_stat_statvfs64=yes,
+ fu_cv_sys_stat_statvfs64=no,
+ fu_cv_sys_stat_statvfs64=cross)])
+ if test $fu_cv_sys_stat_statvfs64 = yes; then
+ space=yes
+ AC_DEFINE(STAT_STATVFS64,1,[Whether statvfs64() is available])
+ fi
+fi
+
+# Perform only the link test since it seems there are no variants of the
+# statvfs function. This check is more than just AC_CHECK_FUNCS(statvfs)
+# because that got a false positive on SCO OSR5. Adding the declaration
+# of a `struct statvfs' causes this test to fail (as it should) on such
+# systems. That system is reported to work fine with STAT_STATFS4 which
+# is what it gets when this test fails.
+if test $space = no; then
+ # SVR4
+ AC_CACHE_CHECK([statvfs function (SVR4)], fu_cv_sys_stat_statvfs,
+ [AC_TRY_LINK([#include <sys/types.h>
+#include <sys/statvfs.h>],
+ [struct statvfs fsd; statvfs (0, &fsd);],
+ fu_cv_sys_stat_statvfs=yes,
+ fu_cv_sys_stat_statvfs=no)])
+ if test $fu_cv_sys_stat_statvfs = yes; then
+ space=yes
+ AC_DEFINE(STAT_STATVFS,1,[Whether statvfs() is available])
+ fi
+fi
+
+if test $space = no; then
+ # DEC Alpha running OSF/1
+ AC_MSG_CHECKING([for 3-argument statfs function (DEC OSF/1)])
+ AC_CACHE_VAL(fu_cv_sys_stat_statfs3_osf1,
+ [AC_TRY_RUN([
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+ main ()
+ {
+ struct statfs fsd;
+ fsd.f_fsize = 0;
+ exit (statfs (".", &fsd, sizeof (struct statfs)));
+ }],
+ fu_cv_sys_stat_statfs3_osf1=yes,
+ fu_cv_sys_stat_statfs3_osf1=no,
+ fu_cv_sys_stat_statfs3_osf1=no)])
+ AC_MSG_RESULT($fu_cv_sys_stat_statfs3_osf1)
+ if test $fu_cv_sys_stat_statfs3_osf1 = yes; then
+ space=yes
+ AC_DEFINE(STAT_STATFS3_OSF1,1,[Whether statfs requires 3 arguments])
+ fi
+fi
+
+if test $space = no; then
+# AIX
+ AC_MSG_CHECKING([for two-argument statfs with statfs.bsize dnl
+member (AIX, 4.3BSD)])
+ AC_CACHE_VAL(fu_cv_sys_stat_statfs2_bsize,
+ [AC_TRY_RUN([
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+#ifdef HAVE_SYS_VFS_H
+#include <sys/vfs.h>
+#endif
+ main ()
+ {
+ struct statfs fsd;
+ fsd.f_bsize = 0;
+ exit (statfs (".", &fsd));
+ }],
+ fu_cv_sys_stat_statfs2_bsize=yes,
+ fu_cv_sys_stat_statfs2_bsize=no,
+ fu_cv_sys_stat_statfs2_bsize=no)])
+ AC_MSG_RESULT($fu_cv_sys_stat_statfs2_bsize)
+ if test $fu_cv_sys_stat_statfs2_bsize = yes; then
+ space=yes
+ AC_DEFINE(STAT_STATFS2_BSIZE,1,[Whether statfs requires two arguments and struct statfs has bsize property])
+ fi
+fi
+
+if test $space = no; then
+# SVR3
+ AC_MSG_CHECKING([for four-argument statfs (AIX-3.2.5, SVR3)])
+ AC_CACHE_VAL(fu_cv_sys_stat_statfs4,
+ [AC_TRY_RUN([#include <sys/types.h>
+#include <sys/statfs.h>
+ main ()
+ {
+ struct statfs fsd;
+ exit (statfs (".", &fsd, sizeof fsd, 0));
+ }],
+ fu_cv_sys_stat_statfs4=yes,
+ fu_cv_sys_stat_statfs4=no,
+ fu_cv_sys_stat_statfs4=no)])
+ AC_MSG_RESULT($fu_cv_sys_stat_statfs4)
+ if test $fu_cv_sys_stat_statfs4 = yes; then
+ space=yes
+ AC_DEFINE(STAT_STATFS4,1,[Whether statfs requires 4 arguments])
+ fi
+fi
+
+if test $space = no; then
+# 4.4BSD and NetBSD
+ AC_MSG_CHECKING([for two-argument statfs with statfs.fsize dnl
+member (4.4BSD and NetBSD)])
+ AC_CACHE_VAL(fu_cv_sys_stat_statfs2_fsize,
+ [AC_TRY_RUN([#include <sys/types.h>
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+ main ()
+ {
+ struct statfs fsd;
+ fsd.f_fsize = 0;
+ exit (statfs (".", &fsd));
+ }],
+ fu_cv_sys_stat_statfs2_fsize=yes,
+ fu_cv_sys_stat_statfs2_fsize=no,
+ fu_cv_sys_stat_statfs2_fsize=no)])
+ AC_MSG_RESULT($fu_cv_sys_stat_statfs2_fsize)
+ if test $fu_cv_sys_stat_statfs2_fsize = yes; then
+ space=yes
+ AC_DEFINE(STAT_STATFS2_FSIZE,1,[Whether statfs requires 2 arguments and struct statfs has fsize])
+ fi
+fi
+
+if test $space = no; then
+ # Ultrix
+ AC_MSG_CHECKING([for two-argument statfs with struct fs_data (Ultrix)])
+ AC_CACHE_VAL(fu_cv_sys_stat_fs_data,
+ [AC_TRY_RUN([#include <sys/types.h>
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+#ifdef HAVE_SYS_FS_TYPES_H
+#include <sys/fs_types.h>
+#endif
+ main ()
+ {
+ struct fs_data fsd;
+ /* Ultrix's statfs returns 1 for success,
+ 0 for not mounted, -1 for failure. */
+ exit (statfs (".", &fsd) != 1);
+ }],
+ fu_cv_sys_stat_fs_data=yes,
+ fu_cv_sys_stat_fs_data=no,
+ fu_cv_sys_stat_fs_data=no)])
+ AC_MSG_RESULT($fu_cv_sys_stat_fs_data)
+ if test $fu_cv_sys_stat_fs_data = yes; then
+ space=yes
+ AC_DEFINE(STAT_STATFS2_FS_DATA,1,[Whether statfs requires 2 arguments and struct fs_data is available])
+ fi
+fi
+
+#
+# As a gating factor for large file support, in order to
+# use <4GB files we must have the following minimal support
+# available.
+# long long, and a 64 bit off_t or off64_t.
+# If we don't have all of these then disable large
+# file support.
+#
+AC_MSG_CHECKING([if large file support can be enabled])
+AC_TRY_COMPILE([
+#if defined(HAVE_LONGLONG) && (defined(HAVE_OFF64_T) || (defined(SIZEOF_OFF_T) && (SIZEOF_OFF_T == 8)))
+#include <sys/types.h>
+#else
+__COMPILE_ERROR_
+#endif
+],
+[int i],
+samba_cv_HAVE_EXPLICIT_LARGEFILE_SUPPORT=yes,samba_cv_HAVE_EXPLICIT_LARGEFILE_SUPPORT=no)
+if test x"$samba_cv_HAVE_EXPLICIT_LARGEFILE_SUPPORT" = x"yes"; then
+ AC_DEFINE(HAVE_EXPLICIT_LARGEFILE_SUPPORT,1,[Whether large file support can be enabled])
+fi
+AC_MSG_RESULT([$samba_cv_HAVE_EXPLICIT_LARGEFILE_SUPPORT])
+
+AC_ARG_WITH(spinlocks,
+[ --with-spinlocks Use spin locks instead of fcntl locks (default=no) ])
+if test "x$with_spinlocks" = "xyes"; then
+ AC_DEFINE(USE_SPINLOCKS,1,[Whether to use spin locks instead of fcntl locks])
+
+ case "$host_cpu" in
+ sparc)
+ AC_DEFINE(SPARC_SPINLOCKS,1,[Whether to use sparc spinlocks])
+ ;;
+
+ i386|i486|i586|i686)
+ AC_DEFINE(INTEL_SPINLOCKS,1,[Whether to use intel spinlocks])
+ ;;
+
+ mips)
+ AC_DEFINE(MIPS_SPINLOCKS,1,[Whether to use mips spinlocks])
+ ;;
+
+ powerpc)
+ AC_DEFINE(POWERPC_SPINLOCKS,1,[Whether to use powerpc spinlocks])
+ ;;
+ esac
+fi
+
+#################################################
+# check for ACL support
+
+AC_MSG_CHECKING(whether to support ACLs)
+AC_ARG_WITH(acl-support,
+[ --with-acl-support Include ACL support (default=no)],
+[ case "$withval" in
+ yes)
+
+ case "$host_os" in
+ *sysv5*)
+ AC_MSG_RESULT(Using UnixWare ACLs)
+ AC_DEFINE(HAVE_UNIXWARE_ACLS,1,[Whether UnixWare ACLs are available])
+ ;;
+ *solaris*)
+ AC_MSG_RESULT(Using solaris ACLs)
+ AC_DEFINE(HAVE_SOLARIS_ACLS,1,[Whether solaris ACLs are available])
+ ;;
+ *hpux*)
+ AC_MSG_RESULT(Using HPUX ACLs)
+ AC_DEFINE(HAVE_HPUX_ACLS,1,[Whether HPUX ACLs are available])
+ ;;
+ *irix*)
+ AC_MSG_RESULT(Using IRIX ACLs)
+ AC_DEFINE(HAVE_IRIX_ACLS,1,[Whether IRIX ACLs are available])
+ ;;
+ *aix*)
+ AC_MSG_RESULT(Using AIX ACLs)
+ AC_DEFINE(HAVE_AIX_ACLS,1,[Whether AIX ACLs are available])
+ ;;
+ *osf*)
+ AC_MSG_RESULT(Using Tru64 ACLs)
+ AC_DEFINE(HAVE_TRU64_ACLS,1,[Whether Tru64 ACLs are available])
+ ACL_LIBS="$ACL_LIBS -lpacl"
+ ;;
+ *freebsd5*)
+ AC_MSG_RESULT(Using FreeBSD posix ACLs)
+ AC_DEFINE(HAVE_POSIX_ACLS,1,[Whether FreeBSD POSIX ACLs are available])
+ AC_DEFINE(HAVE_ACL_GET_PERM_NP,1,[Whether acl_get_perm_np() is available])
+ ;;
+ *linux*)
+ AC_CHECK_LIB(attr,getxattr,[ACL_LIBS="$ACL_LIBS -lattr"])
+ AC_CHECK_LIB(acl,acl_get_file,[ACL_LIBS="$ACL_LIBS -lacl"])
+ AC_CACHE_CHECK([for ACL support],samba_cv_HAVE_POSIX_ACLS,[
+ acl_LIBS=$LIBS
+ LIBS="$LIBS -lacl"
+ AC_TRY_LINK([#include <sys/types.h>
+#include <sys/acl.h>],
+[ acl_t acl; int entry_id; acl_entry_t *entry_p; return acl_get_entry( acl, entry_id, entry_p);],
+samba_cv_HAVE_POSIX_ACLS=yes,samba_cv_HAVE_POSIX_ACLS=no)
+ LIBS=$acl_LIBS])
+ if test x"$samba_cv_HAVE_POSIX_ACLS" = x"yes"; then
+ AC_MSG_RESULT(Using posix ACLs)
+ AC_DEFINE(HAVE_POSIX_ACLS,1,[Whether POSIX ACLs are available])
+ AC_CACHE_CHECK([for acl_get_perm_np],samba_cv_HAVE_ACL_GET_PERM_NP,[
+ acl_LIBS=$LIBS
+ LIBS="$LIBS -lacl"
+ AC_TRY_LINK([#include <sys/types.h>
+#include <sys/acl.h>],
+[ acl_permset_t permset_d; acl_perm_t perm; return acl_get_perm_np( permset_d, perm);],
+samba_cv_HAVE_ACL_GET_PERM_NP=yes,samba_cv_HAVE_ACL_GET_PERM_NP=no)
+ LIBS=$acl_LIBS])
+ if test x"$samba_cv_HAVE_ACL_GET_PERM_NP" = x"yes"; then
+ AC_DEFINE(HAVE_ACL_GET_PERM_NP,1,[Whether acl_get_perm_np() is available])
+ fi
+ fi
+ ;;
+ *)
+ AC_CHECK_LIB(acl,acl_get_file,[ACL_LIBS="$ACL_LIBS -lacl"])
+ AC_CACHE_CHECK([for ACL support],samba_cv_HAVE_POSIX_ACLS,[
+ acl_LIBS=$LIBS
+ LIBS="$LIBS -lacl"
+ AC_TRY_LINK([#include <sys/types.h>
+#include <sys/acl.h>],
+[ acl_t acl; int entry_id; acl_entry_t *entry_p; return acl_get_entry( acl, entry_id, entry_p);],
+samba_cv_HAVE_POSIX_ACLS=yes,samba_cv_HAVE_POSIX_ACLS=no)
+ LIBS=$acl_LIBS])
+ if test x"$samba_cv_HAVE_POSIX_ACLS" = x"yes"; then
+ AC_MSG_RESULT(Using posix ACLs)
+ AC_DEFINE(HAVE_POSIX_ACLS,1,[Whether POSIX ACLs are available])
+ AC_CACHE_CHECK([for acl_get_perm_np],samba_cv_HAVE_ACL_GET_PERM_NP,[
+ acl_LIBS=$LIBS
+ LIBS="$LIBS -lacl"
+ AC_TRY_LINK([#include <sys/types.h>
+#include <sys/acl.h>],
+[ acl_permset_t permset_d; acl_perm_t perm; return acl_get_perm_np( permset_d, perm);],
+samba_cv_HAVE_ACL_GET_PERM_NP=yes,samba_cv_HAVE_ACL_GET_PERM_NP=no)
+ LIBS=$acl_LIBS])
+ if test x"$samba_cv_HAVE_ACL_GET_PERM_NP" = x"yes"; then
+ AC_DEFINE(HAVE_ACL_GET_PERM_NP,1,[Whether acl_get_perm_np() is available])
+ fi
+ fi
+ ;;
+ esac
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ AC_DEFINE(HAVE_NO_ACLS,1,[Whether no ACLs support is available])
+ ;;
+ esac ],
+ AC_DEFINE(HAVE_NO_ACLS,1,[Whether no ACLs support should be built in])
+ AC_MSG_RESULT(no)
+)
+
+#################################################
+# check for sendfile support
+
+with_sendfile_support=yes
+AC_MSG_CHECKING(whether to check to support sendfile)
+AC_ARG_WITH(sendfile-support,
+[ --with-sendfile-support Check for sendfile support (default=yes)],
+[ case "$withval" in
+ yes)
+
+ AC_MSG_RESULT(yes);
+
+ case "$host_os" in
+ *linux*)
+ AC_CACHE_CHECK([for linux sendfile64 support],samba_cv_HAVE_SENDFILE64,[
+ AC_TRY_LINK([#include <sys/sendfile.h>],
+[\
+int tofd, fromfd;
+off64_t offset;
+size_t total;
+ssize_t nwritten = sendfile64(tofd, fromfd, &offset, total);
+],
+samba_cv_HAVE_SENDFILE64=yes,samba_cv_HAVE_SENDFILE64=no)])
+
+ AC_CACHE_CHECK([for linux sendfile support],samba_cv_HAVE_SENDFILE,[
+ AC_TRY_LINK([#include <sys/sendfile.h>],
+[\
+int tofd, fromfd;
+off_t offset;
+size_t total;
+ssize_t nwritten = sendfile(tofd, fromfd, &offset, total);
+],
+samba_cv_HAVE_SENDFILE=yes,samba_cv_HAVE_SENDFILE=no)])
+
+# Try and cope with broken Linux sendfile....
+ AC_CACHE_CHECK([for broken linux sendfile support],samba_cv_HAVE_BROKEN_LINUX_SENDFILE,[
+ AC_TRY_LINK([\
+#if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64)
+#undef _FILE_OFFSET_BITS
+#endif
+#include <sys/sendfile.h>],
+[\
+int tofd, fromfd;
+off_t offset;
+size_t total;
+ssize_t nwritten = sendfile(tofd, fromfd, &offset, total);
+],
+samba_cv_HAVE_BROKEN_LINUX_SENDFILE=yes,samba_cv_HAVE_BROKEN_LINUX_SENDFILE=no)])
+
+ if test x"$samba_cv_HAVE_SENDFILE64" = x"yes"; then
+ AC_DEFINE(HAVE_SENDFILE64,1,[Whether 64-bit sendfile() is available])
+ AC_DEFINE(LINUX_SENDFILE_API,1,[Whether linux sendfile() API is available])
+ AC_DEFINE(WITH_SENDFILE,1,[Whether sendfile() should be used])
+ elif test x"$samba_cv_HAVE_SENDFILE" = x"yes"; then
+ AC_DEFINE(HAVE_SENDFILE,1,[Whether sendfile() is available])
+ AC_DEFINE(LINUX_SENDFILE_API,1,[Whether linux sendfile() API is available])
+ AC_DEFINE(WITH_SENDFILE,1,[Whether sendfile() should be used])
+ elif test x"$samba_cv_HAVE_BROKEN_LINUX_SENDFILE" = x"yes"; then
+ AC_DEFINE(LINUX_BROKEN_SENDFILE_API,1,[Whether (linux) sendfile() is broken])
+ AC_DEFINE(WITH_SENDFILE,1,[Whether sendfile should be used])
+ else
+ AC_MSG_RESULT(no);
+ fi
+
+ ;;
+ *freebsd*)
+ AC_CACHE_CHECK([for freebsd sendfile support],samba_cv_HAVE_SENDFILE,[
+ AC_TRY_LINK([\
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/uio.h>],
+[\
+ int fromfd, tofd, ret, total=0;
+ off_t offset, nwritten;
+ struct sf_hdtr hdr;
+ struct iovec hdtrl;
+ hdr.headers = &hdtrl;
+ hdr.hdr_cnt = 1;
+ hdr.trailers = NULL;
+ hdr.trl_cnt = 0;
+ hdtrl.iov_base = NULL;
+ hdtrl.iov_len = 0;
+ ret = sendfile(fromfd, tofd, offset, total, &hdr, &nwritten, 0);
+],
+samba_cv_HAVE_SENDFILE=yes,samba_cv_HAVE_SENDFILE=no)])
+
+ if test x"$samba_cv_HAVE_SENDFILE" = x"yes"; then
+ AC_DEFINE(HAVE_SENDFILE,1,[Whether sendfile() support is available])
+ AC_DEFINE(FREEBSD_SENDFILE_API,1,[Whether the FreeBSD sendfile() API is available])
+ AC_DEFINE(WITH_SENDFILE,1,[Whether sendfile() support should be included])
+ else
+ AC_MSG_RESULT(no);
+ fi
+ ;;
+
+ *hpux*)
+ AC_CACHE_CHECK([for hpux sendfile64 support],samba_cv_HAVE_SENDFILE64,[
+ AC_TRY_LINK([\
+#include <sys/socket.h>
+#include <sys/uio.h>],
+[\
+ int fromfd, tofd;
+ size_t total=0;
+ struct iovec hdtrl[2];
+ ssize_t nwritten;
+ off64_t offset;
+
+ hdtrl[0].iov_base = 0;
+ hdtrl[0].iov_len = 0;
+
+ nwritten = sendfile64(tofd, fromfd, offset, total, &hdtrl[0], 0);
+],
+samba_cv_HAVE_SENDFILE64=yes,samba_cv_HAVE_SENDFILE64=no)])
+ if test x"$samba_cv_HAVE_SENDFILE64" = x"yes"; then
+ AC_DEFINE(HAVE_SENDFILE64,1,[Whether sendfile64() is available])
+ AC_DEFINE(HPUX_SENDFILE_API,1,[Whether the hpux sendfile() API is available])
+ AC_DEFINE(WITH_SENDFILE,1,[Whether sendfile() support should be included])
+ else
+ AC_MSG_RESULT(no);
+ fi
+
+ AC_CACHE_CHECK([for hpux sendfile support],samba_cv_HAVE_SENDFILE,[
+ AC_TRY_LINK([\
+#include <sys/socket.h>
+#include <sys/uio.h>],
+[\
+ int fromfd, tofd;
+ size_t total=0;
+ struct iovec hdtrl[2];
+ ssize_t nwritten;
+ off_t offset;
+
+ hdtrl[0].iov_base = 0;
+ hdtrl[0].iov_len = 0;
+
+ nwritten = sendfile(tofd, fromfd, offset, total, &hdtrl[0], 0);
+],
+samba_cv_HAVE_SENDFILE=yes,samba_cv_HAVE_SENDFILE=no)])
+ if test x"$samba_cv_HAVE_SENDFILE" = x"yes"; then
+ AC_DEFINE(HAVE_SENDFILE,1,[Whether sendfile() is available])
+ AC_DEFINE(HPUX_SENDFILE_API,1,[Whether the hpux sendfile() API is available])
+ AC_DEFINE(WITH_SENDFILE,1,[Whether sendfile() support should be included])
+ else
+ AC_MSG_RESULT(no);
+ fi
+ ;;
+
+ *solaris*)
+ AC_CHECK_LIB(sendfile,sendfilev)
+ AC_CACHE_CHECK([for solaris sendfilev64 support],samba_cv_HAVE_SENDFILEV64,[
+ AC_TRY_LINK([\
+#include <sys/sendfile.h>],
+[\
+ int sfvcnt;
+ size_t xferred;
+ struct sendfilevec vec[2];
+ ssize_t nwritten;
+ int tofd;
+
+ sfvcnt = 2;
+
+ vec[0].sfv_fd = SFV_FD_SELF;
+ vec[0].sfv_flag = 0;
+ vec[0].sfv_off = 0;
+ vec[0].sfv_len = 0;
+
+ vec[1].sfv_fd = 0;
+ vec[1].sfv_flag = 0;
+ vec[1].sfv_off = 0;
+ vec[1].sfv_len = 0;
+ nwritten = sendfilev64(tofd, vec, sfvcnt, &xferred);
+],
+samba_cv_HAVE_SENDFILEV64=yes,samba_cv_HAVE_SENDFILEV64=no)])
+
+ if test x"$samba_cv_HAVE_SENDFILEV64" = x"yes"; then
+ AC_DEFINE(HAVE_SENDFILEV64,1,[Whether sendfilev64() is available])
+ AC_DEFINE(SOLARIS_SENDFILE_API,1,[Whether the soloris sendfile() API is available])
+ AC_DEFINE(WITH_SENDFILE,1,[Whether sendfile() support should be included])
+ else
+ AC_MSG_RESULT(no);
+ fi
+
+ AC_CACHE_CHECK([for solaris sendfilev support],samba_cv_HAVE_SENDFILEV,[
+ AC_TRY_LINK([\
+#include <sys/sendfile.h>],
+[\
+ int sfvcnt;
+ size_t xferred;
+ struct sendfilevec vec[2];
+ ssize_t nwritten;
+ int tofd;
+
+ sfvcnt = 2;
+
+ vec[0].sfv_fd = SFV_FD_SELF;
+ vec[0].sfv_flag = 0;
+ vec[0].sfv_off = 0;
+ vec[0].sfv_len = 0;
+
+ vec[1].sfv_fd = 0;
+ vec[1].sfv_flag = 0;
+ vec[1].sfv_off = 0;
+ vec[1].sfv_len = 0;
+ nwritten = sendfilev(tofd, vec, sfvcnt, &xferred);
+],
+samba_cv_HAVE_SENDFILEV=yes,samba_cv_HAVE_SENDFILEV=no)])
+
+ if test x"$samba_cv_HAVE_SENDFILEV" = x"yes"; then
+ AC_DEFINE(HAVE_SENDFILEV,1,[Whether sendfilev() is available])
+ AC_DEFINE(SOLARIS_SENDFILE_API,1,[Whether the solaris sendfile() API is available])
+ AC_DEFINE(WITH_SENDFILE,1,[Whether to include sendfile() support])
+ else
+ AC_MSG_RESULT(no);
+ fi
+ ;;
+
+ *)
+ ;;
+ esac
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+ AC_MSG_RESULT(yes)
+)
+
+
+#################################################
+# Check whether winbind is supported on this platform. If so we need to
+# build and install client programs, sbin programs and shared libraries
+
+AC_MSG_CHECKING(whether to build winbind)
+
+# Initially, the value of $host_os decides whether winbind is supported
+
+HAVE_WINBIND=yes
+
+# Define the winbind shared library name and any specific linker flags
+# it needs to be built with.
+
+WINBIND_NSS="nsswitch/libnss_winbind.$SHLIBEXT"
+WINBIND_WINS_NSS="nsswitch/libnss_wins.$SHLIBEXT"
+WINBIND_NSS_LDSHFLAGS=$LDSHFLAGS
+
+case "$host_os" in
+ *linux*)
+ WINBIND_NSS_EXTRA_OBJS="nsswitch/winbind_nss_linux.o"
+ ;;
+ *freebsd5*)
+ # FreeBSD winbind client is implemented as a wrapper around
+ # the Linux version.
+ WINBIND_NSS_EXTRA_OBJS="nsswitch/winbind_nss_freebsd.o \
+ nsswitch/winbind_nss_linux.o"
+ ;;
+ *irix*)
+ # IRIX has differently named shared libraries
+ WINBIND_NSS_EXTRA_OBJS="nsswitch/winbind_nss_irix.o"
+ WINBIND_NSS="nsswitch/libns_winbind.$SHLIBEXT"
+ WINBIND_WINS_NSS="nsswitch/libns_wins.$SHLIBEXT"
+ ;;
+ *solaris*)
+ # Solaris winbind client is implemented as a wrapper around
+ # the Linux version.
+ WINBIND_NSS_EXTRA_OBJS="nsswitch/winbind_nss_solaris.o \
+ nsswitch/winbind_nss_linux.o"
+ WINBIND_NSS_EXTRA_LIBS="-lsocket"
+ ;;
+ *hpux11*)
+ WINBIND_NSS_EXTRA_OBJS="nsswitch/winbind_nss_solaris.o"
+ ;;
+ *aix*)
+ # AIX has even differently named shared libraries. No
+ # WINS support has been implemented yet.
+ WINBIND_NSS_EXTRA_OBJS="nsswitch/winbind_nss_aix.o"
+ WINBIND_NSS_LDSHFLAGS="-Wl,-bexpall,-bM:SRE,-ewb_aix_init"
+ WINBIND_NSS="nsswitch/WINBIND"
+ WINBIND_WINS_NSS=""
+ ;;
+ *)
+ HAVE_WINBIND=no
+ winbind_no_reason=", unsupported on $host_os"
+ ;;
+esac
+
+AC_SUBST(WINBIND_NSS)
+AC_SUBST(WINBIND_WINS_NSS)
+AC_SUBST(WINBIND_NSS_LDSHFLAGS)
+AC_SUBST(WINBIND_NSS_EXTRA_OBJS)
+AC_SUBST(WINBIND_NSS_EXTRA_LIBS)
+
+# Check the setting of --with-winbindd
+
+AC_ARG_WITH(winbind,
+[ --with-winbind Build winbind (default, if supported by OS)],
+[
+ case "$withval" in
+ yes)
+ HAVE_WINBIND=yes
+ ;;
+ no)
+ HAVE_WINBIND=no
+ winbind_reason=""
+ ;;
+ esac ],
+)
+
+# We need unix domain sockets for winbind
+
+if test x"$HAVE_WINBIND" = x"yes"; then
+ if test x"$samba_cv_unixsocket" = x"no"; then
+ winbind_no_reason=", no unix domain socket support on $host_os"
+ HAVE_WINBIND=no
+ fi
+fi
+
+# Display test results
+
+if test x"$HAVE_WINBIND" = x"yes"; then
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(WITH_WINBIND,1,[Whether to build winbind])
+
+ EXTRA_BIN_PROGS="$EXTRA_BIN_PROGS bin/wbinfo\$(EXEEXT)"
+ EXTRA_SBIN_PROGS="$EXTRA_SBIN_PROGS bin/winbindd\$(EXEEXT)"
+ if test x"$BLDSHARED" = x"true"; then
+ SHLIB_PROGS="$SHLIB_PROGS $WINBIND_NSS $WINBIND_WINS_NSS"
+
+ if test x"$with_pam" = x"yes"; then
+ SHLIB_PROGS="$SHLIB_PROGS nsswitch/pam_winbind.$SHLIBEXT"
+ fi
+ fi
+else
+ AC_MSG_RESULT(no$winbind_no_reason)
+fi
+
+# Solaris has some extra fields in struct passwd that need to be
+# initialised otherwise nscd crashes.
+
+AC_CHECK_MEMBER(struct passwd.pw_comment,
+ AC_DEFINE(HAVE_PASSWD_PW_COMMENT, 1, [Defined if struct passwd has pw_comment field]),,
+ [#include <pwd.h>])
+
+AC_CHECK_MEMBER(struct passwd.pw_age,
+ AC_DEFINE(HAVE_PASSWD_PW_AGE, 1, [Defined if struct passwd has pw_age field]),,
+ [#include <pwd.h>])
+
+#################################################
+# Check to see if we should use the included popt
+
+AC_ARG_WITH(included-popt,
+[ --with-included-popt use bundled popt library, not from system],
+[
+ case "$withval" in
+ yes)
+ INCLUDED_POPT=yes
+ ;;
+ no)
+ INCLUDED_POPT=no
+ ;;
+ esac ],
+)
+if test x"$INCLUDED_POPT" != x"yes"; then
+ AC_CHECK_LIB(popt, poptGetContext,
+ INCLUDED_POPT=no, INCLUDED_POPT=yes)
+fi
+
+AC_MSG_CHECKING(whether to use included popt)
+if test x"$INCLUDED_POPT" = x"yes"; then
+ AC_MSG_RESULT(yes)
+ BUILD_POPT='$(POPT_OBJS)'
+ POPTLIBS='$(POPT_OBJS)'
+ FLAGS1="-I$srcdir/popt"
+else
+ AC_MSG_RESULT(no)
+ BUILD_POPT=""
+ POPTLIBS="-lpopt"
+fi
+AC_SUBST(BUILD_POPT)
+AC_SUBST(POPTLIBS)
+AC_SUBST(FLAGS1)
+
+#################################################
+# Check if the user wants Python
+
+# At the moment, you can use this to set which Python binary to link
+# against. (Libraries built for Python2.2 can't be used by 2.1,
+# though they can coexist in different directories.) In the future
+# this might make the Python stuff be built by default.
+
+# Defaulting python breaks the clean target if python isn't installed
+
+PYTHON=
+
+AC_ARG_WITH(python,
+[ --with-python=PYTHONNAME build Python libraries],
+[ case "${withval-python}" in
+ yes)
+ PYTHON=python
+ EXTRA_ALL_TARGETS="$EXTRA_ALL_TARGETS python_ext"
+ ;;
+ no)
+ PYTHON=
+ ;;
+ *)
+ PYTHON=${withval-python}
+ ;;
+ esac ])
+AC_SUBST(PYTHON)
+
+for i in `echo $default_static_modules | sed -e's/,/ /g'`
+do
+ eval MODULE_DEFAULT_$i=STATIC
+done
+
+for i in `echo $default_shared_modules | sed -e's/,/ /g'`
+do
+ dnl Fall back to static if dlopen() is not available
+ eval MODULE_DEFAULT_$i=STATIC
+
+ if test x"$ac_cv_func_dlopen" = xyes; then
+ eval MODULE_DEFAULT_$i=SHARED
+ fi
+done
+
+dnl Always built these modules static
+MODULE_pdb_guest=STATIC
+MODULE_rpc_spoolss=STATIC
+MODULE_rpc_srv=STATIC
+MODULE_idmap_tdb=STATIC
+MODULE_gums_tdbsam2=STATIC
+
+AC_ARG_WITH(static-modules,
+[ --with-static-modules=MODULES Comma-seperated list of names of modules to statically link in],
+[ if test $withval; then
+ for i in `echo $withval | sed -e's/,/ /g'`
+ do
+ eval MODULE_$i=STATIC
+ done
+fi ])
+
+AC_ARG_WITH(shared-modules,
+[ --with-shared-modules=MODULES Comma-seperated list of names of modules to build shared],
+[ if test $withval; then
+ for i in `echo $withval | sed -e's/,/ /g'`
+ do
+ eval MODULE_$i=SHARED
+ done
+fi ])
+
+###########################################################################
+## contributed pdb_modules
+
+SMB_MODULE(pdb_xml, passdb/pdb_xml.o, "bin/xml.$SHLIBEXT", PDB,
+ [ PASSDB_LIBS="$PASSDB_LIBS $XML_LIBS" ] )
+SMB_MODULE(pdb_mysql, passdb/pdb_mysql.o, "bin/mysql.$SHLIBEXT", PDB,
+ [ PASSDB_LIBS="$PASSDB_LIBS $MYSQL_LIBS" ] )
+SMB_MODULE(pdb_pgsql, passdb/pdb_pgsql.o, "bin/pgsql.$SHLIBEXT", PDB,
+ [ PASSDB_LIBS="$PASSDB_LIBS $PGSQL_LIBS" ] )
+
+## end of contributed pdb_modules
+###########################################################################
+
+SMB_MODULE(pdb_ldap, passdb/pdb_ldap.o, "bin/ldapsam.$SHLIBEXT", PDB,
+ [ PASSDB_LIBS="$PASSDB_LIBS $LDAP_LIBS" ] )
+SMB_MODULE(pdb_smbpasswd, passdb/pdb_smbpasswd.o, "bin/smbpasswd.$SHLIBEXT", PDB)
+SMB_MODULE(pdb_tdbsam, passdb/pdb_tdb.o, "bin/tdbsam.$SHLIBEXT", PDB)
+SMB_MODULE(pdb_guest, passdb/pdb_guest.o, "bin/guest.$SHLIBEXT", PDB)
+SMB_MODULE(pdb_gums, [passdb/pdb_gums.o \$(GUMS_OBJ)], "bin/gums.$SHLIBEXT", PDB)
+SMB_SUBSYSTEM(PDB,passdb/pdb_interface.o)
+
+SMB_MODULE(gums_tdbsam2, sam/gums_tdbsam2.o, "bin/tdbsam2.$SHLIBEXT", GUMS)
+SMB_SUBSYSTEM(GUMS)
+
+SMB_MODULE(rpc_lsa, \$(RPC_LSA_OBJ), "bin/librpc_lsarpc.$SHLIBEXT", RPC)
+SMB_MODULE(rpc_reg, \$(RPC_REG_OBJ), "bin/librpc_winreg.$SHLIBEXT", RPC)
+SMB_MODULE(rpc_lsa_ds, \$(RPC_LSA_DS_OBJ), "bin/librpc_lsa_ds.$SHLIBEXT", RPC)
+SMB_MODULE(rpc_wks, \$(RPC_WKS_OBJ), "bin/librpc_wkssvc.$SHLIBEXT", RPC)
+SMB_MODULE(rpc_net, \$(RPC_NETLOG_OBJ), "bin/librpc_NETLOGON.$SHLIBEXT", RPC)
+SMB_MODULE(rpc_dfs, \$(RPC_DFS_OBJ), "bin/librpc_netdfs.$SHLIBEXT", RPC)
+SMB_MODULE(rpc_srv, \$(RPC_SVC_OBJ), "bin/librpc_srvsvc.$SHLIBEXT", RPC)
+SMB_MODULE(rpc_spoolss, \$(RPC_SPOOLSS_OBJ), "bin/librpc_spoolss.$SHLIBEXT", RPC)
+SMB_MODULE(rpc_samr, \$(RPC_SAMR_OBJ), "bin/librpc_samr.$SHLIBEXT", RPC)
+SMB_MODULE(rpc_echo, \$(RPC_ECHO_OBJ), "bin/librpc_echo.$SHLIBEXT", RPC)
+SMB_MODULE(rpc_epmapper, \$(RPC_EPMAPPER_OBJ), "bin/librpc_epmapper.$SHLIBEXT",
+ RPC)
+SMB_SUBSYSTEM(RPC,smbd/server.o)
+
+SMB_MODULE(idmap_ldap, sam/idmap_ldap.o, "bin/idmap_ldap.$SHLIBEXT", IDMAP)
+SMB_MODULE(idmap_tdb, sam/idmap_tdb.o, "bin/idmap_tdb.$SHLIBEXT", IDMAP)
+SMB_SUBSYSTEM(IDMAP,sam/idmap.o)
+
+SMB_MODULE(charset_weird, modules/weird.o, "bin/weird.$SHLIBEXT", CHARSET)
+SMB_MODULE(charset_CP850, modules/CP850.o, "bin/CP850.$SHLIBEXT", CHARSET)
+SMB_MODULE(charset_CP437, modules/CP437.o, "bin/CP437.$SHLIBEXT", CHARSET)
+SMB_MODULE(charset_macosxfs, modules/charset_macosxfs.o,"bin/macosxfs.$SHLIBEXT", CHARSET)
+SMB_SUBSYSTEM(CHARSET,lib/iconv.o)
+
+SMB_MODULE(auth_rhosts, \$(AUTH_RHOSTS_OBJ), "bin/rhosts.$SHLIBEXT", AUTH)
+SMB_MODULE(auth_sam, \$(AUTH_SAM_OBJ), "bin/sam.$SHLIBEXT", AUTH)
+SMB_MODULE(auth_unix, \$(AUTH_UNIX_OBJ), "bin/unix.$SHLIBEXT", AUTH)
+SMB_MODULE(auth_winbind, \$(AUTH_WINBIND_OBJ), "bin/winbind.$SHLIBEXT", AUTH)
+SMB_MODULE(auth_server, \$(AUTH_SERVER_OBJ), "bin/smbserver.$SHLIBEXT", AUTH)
+SMB_MODULE(auth_domain, \$(AUTH_DOMAIN_OBJ), "bin/domain.$SHLIBEXT", AUTH)
+SMB_MODULE(auth_builtin, \$(AUTH_BUILTIN_OBJ), "bin/builtin.$SHLIBEXT", AUTH)
+SMB_SUBSYSTEM(AUTH,auth/auth.o)
+
+SMB_MODULE(vfs_recycle, \$(VFS_RECYCLE_OBJ), "bin/recycle.$SHLIBEXT", VFS)
+SMB_MODULE(vfs_audit, \$(VFS_AUDIT_OBJ), "bin/audit.$SHLIBEXT", VFS)
+SMB_MODULE(vfs_extd_audit, \$(VFS_EXTD_AUDIT_OBJ), "bin/extd_audit.$SHLIBEXT", VFS)
+SMB_MODULE(vfs_netatalk, \$(VFS_NETATALK_OBJ), "bin/netatalk.$SHLIBEXT", VFS)
+SMB_MODULE(vfs_fake_perms, \$(VFS_FAKE_PERMS_OBJ), "bin/fake_perms.$SHLIBEXT", VFS)
+SMB_MODULE(vfs_default_quota, \$(VFS_DEFAULT_QUOTA_OBJ), "bin/default_quota.$SHLIBEXT", VFS)
+SMB_MODULE(vfs_readonly, \$(VFS_READONLY_OBJ), "bin/readonly.$SHLIBEXT", VFS)
+SMB_MODULE(vfs_cap, \$(VFS_CAP_OBJ), "bin/cap.$SHLIBEXT", VFS)
+SMB_MODULE(vfs_expand_msdfs, \$(VFS_EXPAND_MSDFS_OBJ), "bin/expand_msdfs.$SHLIBEXT", VFS)
+SMB_SUBSYSTEM(VFS,smbd/vfs.o)
+
+SMB_MODULE(config_ldap, param/config_ldap.o, "bin/config_ldap.$SHLIBEXT", CONFIG, [ CONFIG_LIBS="$CONFIG_LIBS $LDAP_LIBS" "$SMBLDAP" ])
+SMB_SUBSYSTEM(CONFIG, param/modconf.o)
+
+AC_DEFINE_UNQUOTED(STRING_STATIC_MODULES, "$string_static_modules", [String list of builtin modules])
+
+#################################################
+# do extra things if we are running insure
+
+if test "${ac_cv_prog_CC}" = "insure"; then
+ CPPFLAGS="$CPPFLAGS -D__INSURE__"
+fi
+
+#################################################
+# Display summary of libraries detected
+
+AC_MSG_RESULT([Using libraries:])
+AC_MSG_RESULT([ LIBS = $LIBS])
+if test x"$with_ads_support" != x"no"; then
+ AC_MSG_RESULT([ KRB5_LIBS = $KRB5_LIBS])
+fi
+if test x"$with_ldap_support" != x"no"; then
+ AC_MSG_RESULT([ LDAP_LIBS = $LDAP_LIBS])
+fi
+AC_MSG_RESULT([ AUTH_LIBS = $AUTH_LIBS])
+
+#################################################
+# final configure stuff
+
+AC_MSG_CHECKING([configure summary])
+AC_TRY_RUN([#include "${srcdir-.}/tests/summary.c"],
+ AC_MSG_RESULT(yes),
+ AC_MSG_ERROR([summary failure. Aborting config]); exit 1;,
+ AC_MSG_WARN([cannot run when cross-compiling]))
+
+builddir=`pwd`
+AC_SUBST(builddir)
+
+dnl Remove -L/usr/lib/? from LDFLAGS and LIBS
+LIB_REMOVE_USR_LIB(LDFLAGS)
+LIB_REMOVE_USR_LIB(LIBS)
+
+dnl Remove -I/usr/include/? from CFLAGS and CPPFLAGS
+CFLAGS_REMOVE_USR_INCLUDE(CFLAGS)
+CFLAGS_REMOVE_USR_INCLUDE(CPPFLAGS)
+
+AC_OUTPUT(include/stamp-h Makefile script/findsmb smbadduser script/gen-8bit-gap.sh)
+
+#################################################
+# Print very concise instructions on building/use
+if test "x$enable_dmalloc" = xyes
+then
+ AC_MSG_RESULT([Note: The dmalloc debug library will be included. To turn it on use])
+ AC_MSG_RESULT([ \$ eval \`dmalloc samba\`.])
+fi
diff --git a/source/dynconfig.c b/source/dynconfig.c
new file mode 100644
index 00000000000..34c716926cc
--- /dev/null
+++ b/source/dynconfig.c
@@ -0,0 +1,72 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) 2001 by Martin Pool <mbp@samba.org>
+ Copyright (C) 2003 by Jim McDonough <jmcd@us.ibm.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/**
+ * @file dynconfig.c
+ *
+ * @brief Global configurations, initialized to configured defaults.
+ *
+ * This file should be the only file that depends on path
+ * configuration (--prefix, etc), so that if ./configure is re-run,
+ * all programs will be appropriately updated. Everything else in
+ * Samba should import extern variables from here, rather than relying
+ * on preprocessor macros.
+ *
+ * Eventually some of these may become even more variable, so that
+ * they can for example consistently be set across the whole of Samba
+ * by command-line parameters, config file entries, or environment
+ * variables.
+ *
+ * @todo Perhaps eventually these should be merged into the parameter
+ * table? There's kind of a chicken-and-egg situation there...
+ **/
+
+char const *dyn_SBINDIR = SBINDIR,
+ *dyn_BINDIR = BINDIR,
+ *dyn_SWATDIR = SWATDIR;
+
+pstring dyn_CONFIGFILE = CONFIGFILE; /**< Location of smb.conf file. **/
+
+/** Log file directory. **/
+pstring dyn_LOGFILEBASE = LOGFILEBASE;
+
+/** Statically configured LanMan hosts. **/
+pstring dyn_LMHOSTSFILE = LMHOSTSFILE;
+
+/**
+ * @brief Samba library directory.
+ *
+ * @sa lib_path() to get the path to a file inside the LIBDIR.
+ **/
+pstring dyn_LIBDIR = LIBDIR;
+const fstring dyn_SHLIBEXT = SHLIBEXT;
+
+/**
+ * @brief Directory holding lock files.
+ *
+ * Not writable, but used to set a default in the parameter table.
+ **/
+const pstring dyn_LOCKDIR = LOCKDIR;
+const pstring dyn_PIDDIR = PIDDIR;
+
+const pstring dyn_SMB_PASSWD_FILE = SMB_PASSWD_FILE;
+const pstring dyn_PRIVATE_DIR = PRIVATE_DIR;
diff --git a/source/groupdb/.cvsignore b/source/groupdb/.cvsignore
new file mode 100644
index 00000000000..5f2a5c4cf75
--- /dev/null
+++ b/source/groupdb/.cvsignore
@@ -0,0 +1,2 @@
+*.po
+*.po32
diff --git a/source/groupdb/mapping.c b/source/groupdb/mapping.c
new file mode 100644
index 00000000000..d476f5cac13
--- /dev/null
+++ b/source/groupdb/mapping.c
@@ -0,0 +1,1438 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-2000,
+ * Copyright (C) Jean François Micouleau 1998-2001.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+static TDB_CONTEXT *tdb; /* used for driver files */
+
+#define DATABASE_VERSION_V1 1 /* native byte format. */
+#define DATABASE_VERSION_V2 2 /* le format. */
+
+#define GROUP_PREFIX "UNIXGROUP/"
+
+/* Alias memberships are stored reverse, as memberships. The performance
+ * critical operation is to determine the aliases a SID is member of, not
+ * listing alias members. So we store a list of alias SIDs a SID is member of
+ * hanging of the member as key.
+ */
+#define MEMBEROF_PREFIX "MEMBEROF/"
+
+/****************************************************************************
+dump the mapping group mapping to a text file
+****************************************************************************/
+char *decode_sid_name_use(fstring group, enum SID_NAME_USE name_use)
+{
+ static fstring group_type;
+
+ switch(name_use) {
+ case SID_NAME_USER:
+ fstrcpy(group_type,"User");
+ break;
+ case SID_NAME_DOM_GRP:
+ fstrcpy(group_type,"Domain group");
+ break;
+ case SID_NAME_DOMAIN:
+ fstrcpy(group_type,"Domain");
+ break;
+ case SID_NAME_ALIAS:
+ fstrcpy(group_type,"Local group");
+ break;
+ case SID_NAME_WKN_GRP:
+ fstrcpy(group_type,"Builtin group");
+ break;
+ case SID_NAME_DELETED:
+ fstrcpy(group_type,"Deleted");
+ break;
+ case SID_NAME_INVALID:
+ fstrcpy(group_type,"Invalid");
+ break;
+ case SID_NAME_UNKNOWN:
+ default:
+ fstrcpy(group_type,"Unknown type");
+ break;
+ }
+
+ fstrcpy(group, group_type);
+ return group_type;
+}
+
+/****************************************************************************
+initialise first time the mapping list - called from init_group_mapping()
+****************************************************************************/
+static BOOL default_group_mapping(void)
+{
+ DOM_SID sid_admins;
+ DOM_SID sid_users;
+ DOM_SID sid_guests;
+ fstring str_admins;
+ fstring str_users;
+ fstring str_guests;
+
+ /* Add the Wellknown groups */
+
+ add_initial_entry(-1, "S-1-5-32-544", SID_NAME_WKN_GRP, "Administrators", "");
+ add_initial_entry(-1, "S-1-5-32-545", SID_NAME_WKN_GRP, "Users", "");
+ add_initial_entry(-1, "S-1-5-32-546", SID_NAME_WKN_GRP, "Guests", "");
+ add_initial_entry(-1, "S-1-5-32-547", SID_NAME_WKN_GRP, "Power Users", "");
+ add_initial_entry(-1, "S-1-5-32-548", SID_NAME_WKN_GRP, "Account Operators", "");
+ add_initial_entry(-1, "S-1-5-32-549", SID_NAME_WKN_GRP, "System Operators", "");
+ add_initial_entry(-1, "S-1-5-32-550", SID_NAME_WKN_GRP, "Print Operators", "");
+ add_initial_entry(-1, "S-1-5-32-551", SID_NAME_WKN_GRP, "Backup Operators", "");
+ add_initial_entry(-1, "S-1-5-32-552", SID_NAME_WKN_GRP, "Replicators", "");
+
+ /* Add the defaults domain groups */
+
+ sid_copy(&sid_admins, get_global_sam_sid());
+ sid_append_rid(&sid_admins, DOMAIN_GROUP_RID_ADMINS);
+ sid_to_string(str_admins, &sid_admins);
+ add_initial_entry(-1, str_admins, SID_NAME_DOM_GRP, "Domain Admins", "");
+
+ sid_copy(&sid_users, get_global_sam_sid());
+ sid_append_rid(&sid_users, DOMAIN_GROUP_RID_USERS);
+ sid_to_string(str_users, &sid_users);
+ add_initial_entry(-1, str_users, SID_NAME_DOM_GRP, "Domain Users", "");
+
+ sid_copy(&sid_guests, get_global_sam_sid());
+ sid_append_rid(&sid_guests, DOMAIN_GROUP_RID_GUESTS);
+ sid_to_string(str_guests, &sid_guests);
+ add_initial_entry(-1, str_guests, SID_NAME_DOM_GRP, "Domain Guests", "");
+
+ return True;
+}
+
+/****************************************************************************
+ Open the group mapping tdb.
+****************************************************************************/
+
+static BOOL init_group_mapping(void)
+{
+ static pid_t local_pid;
+ const char *vstring = "INFO/version";
+ int32 vers_id;
+
+ if (tdb && local_pid == sys_getpid())
+ return True;
+ tdb = tdb_open_log(lock_path("group_mapping.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+ if (!tdb) {
+ DEBUG(0,("Failed to open group mapping database\n"));
+ return False;
+ }
+
+ local_pid = sys_getpid();
+
+ /* handle a Samba upgrade */
+ tdb_lock_bystring(tdb, vstring, 0);
+
+ /* Cope with byte-reversed older versions of the db. */
+ vers_id = tdb_fetch_int32(tdb, vstring);
+ if ((vers_id == DATABASE_VERSION_V1) || (IREV(vers_id) == DATABASE_VERSION_V1)) {
+ /* Written on a bigendian machine with old fetch_int code. Save as le. */
+ tdb_store_int32(tdb, vstring, DATABASE_VERSION_V2);
+ vers_id = DATABASE_VERSION_V2;
+ }
+
+ if (vers_id != DATABASE_VERSION_V2) {
+ tdb_traverse(tdb, tdb_traverse_delete_fn, NULL);
+ tdb_store_int32(tdb, vstring, DATABASE_VERSION_V2);
+ }
+
+ tdb_unlock_bystring(tdb, vstring);
+
+ /* write a list of default groups */
+ if(!default_group_mapping())
+ return False;
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+static BOOL add_mapping_entry(GROUP_MAP *map, int flag)
+{
+ TDB_DATA kbuf, dbuf;
+ pstring key, buf;
+ fstring string_sid="";
+ int len;
+
+ if(!init_group_mapping()) {
+ DEBUG(0,("failed to initialize group mapping\n"));
+ return(False);
+ }
+
+ sid_to_string(string_sid, &map->sid);
+
+ len = tdb_pack(buf, sizeof(buf), "ddff",
+ map->gid, map->sid_name_use, map->nt_name, map->comment);
+
+ if (len > sizeof(buf))
+ return False;
+
+ slprintf(key, sizeof(key), "%s%s", GROUP_PREFIX, string_sid);
+
+ kbuf.dsize = strlen(key)+1;
+ kbuf.dptr = key;
+ dbuf.dsize = len;
+ dbuf.dptr = buf;
+ if (tdb_store(tdb, kbuf, dbuf, flag) != 0) return False;
+
+ return True;
+}
+
+/****************************************************************************
+initialise first time the mapping list
+****************************************************************************/
+BOOL add_initial_entry(gid_t gid, const char *sid, enum SID_NAME_USE sid_name_use, const char *nt_name, const char *comment)
+{
+ GROUP_MAP map;
+
+ if(!init_group_mapping()) {
+ DEBUG(0,("failed to initialize group mapping\n"));
+ return(False);
+ }
+
+ map.gid=gid;
+ if (!string_to_sid(&map.sid, sid)) {
+ DEBUG(0, ("string_to_sid failed: %s", sid));
+ return False;
+ }
+
+ map.sid_name_use=sid_name_use;
+ fstrcpy(map.nt_name, nt_name);
+ fstrcpy(map.comment, comment);
+
+ return pdb_add_group_mapping_entry(&map);
+}
+
+/****************************************************************************
+ Return the sid and the type of the unix group.
+****************************************************************************/
+
+static BOOL get_group_map_from_sid(DOM_SID sid, GROUP_MAP *map)
+{
+ TDB_DATA kbuf, dbuf;
+ pstring key;
+ fstring string_sid;
+ int ret = 0;
+
+ if(!init_group_mapping()) {
+ DEBUG(0,("failed to initialize group mapping\n"));
+ return(False);
+ }
+
+ /* the key is the SID, retrieving is direct */
+
+ sid_to_string(string_sid, &sid);
+ slprintf(key, sizeof(key), "%s%s", GROUP_PREFIX, string_sid);
+
+ kbuf.dptr = key;
+ kbuf.dsize = strlen(key)+1;
+
+ dbuf = tdb_fetch(tdb, kbuf);
+ if (!dbuf.dptr)
+ return False;
+
+ ret = tdb_unpack(dbuf.dptr, dbuf.dsize, "ddff",
+ &map->gid, &map->sid_name_use, &map->nt_name, &map->comment);
+
+ SAFE_FREE(dbuf.dptr);
+
+ if ( ret == -1 ) {
+ DEBUG(3,("get_group_map_from_sid: tdb_unpack failure\n"));
+ return False;
+ }
+
+ sid_copy(&map->sid, &sid);
+
+ return True;
+}
+
+/****************************************************************************
+ Return the sid and the type of the unix group.
+****************************************************************************/
+
+static BOOL get_group_map_from_gid(gid_t gid, GROUP_MAP *map)
+{
+ TDB_DATA kbuf, dbuf, newkey;
+ fstring string_sid;
+ int ret;
+
+ if(!init_group_mapping()) {
+ DEBUG(0,("failed to initialize group mapping\n"));
+ return(False);
+ }
+
+ /* we need to enumerate the TDB to find the GID */
+
+ for (kbuf = tdb_firstkey(tdb);
+ kbuf.dptr;
+ newkey = tdb_nextkey(tdb, kbuf), safe_free(kbuf.dptr), kbuf=newkey) {
+
+ if (strncmp(kbuf.dptr, GROUP_PREFIX, strlen(GROUP_PREFIX)) != 0) continue;
+
+ dbuf = tdb_fetch(tdb, kbuf);
+ if (!dbuf.dptr)
+ continue;
+
+ fstrcpy(string_sid, kbuf.dptr+strlen(GROUP_PREFIX));
+
+ string_to_sid(&map->sid, string_sid);
+
+ ret = tdb_unpack(dbuf.dptr, dbuf.dsize, "ddff",
+ &map->gid, &map->sid_name_use, &map->nt_name, &map->comment);
+
+ SAFE_FREE(dbuf.dptr);
+
+ if ( ret == -1 ) {
+ DEBUG(3,("get_group_map_from_gid: tdb_unpack failure\n"));
+ return False;
+ }
+
+ if (gid==map->gid) {
+ SAFE_FREE(kbuf.dptr);
+ return True;
+ }
+ }
+
+ return False;
+}
+
+/****************************************************************************
+ Return the sid and the type of the unix group.
+****************************************************************************/
+
+static BOOL get_group_map_from_ntname(const char *name, GROUP_MAP *map)
+{
+ TDB_DATA kbuf, dbuf, newkey;
+ fstring string_sid;
+ int ret;
+
+ if(!init_group_mapping()) {
+ DEBUG(0,("get_group_map_from_ntname:failed to initialize group mapping\n"));
+ return(False);
+ }
+
+ /* we need to enumerate the TDB to find the name */
+
+ for (kbuf = tdb_firstkey(tdb);
+ kbuf.dptr;
+ newkey = tdb_nextkey(tdb, kbuf), safe_free(kbuf.dptr), kbuf=newkey) {
+
+ if (strncmp(kbuf.dptr, GROUP_PREFIX, strlen(GROUP_PREFIX)) != 0) continue;
+
+ dbuf = tdb_fetch(tdb, kbuf);
+ if (!dbuf.dptr)
+ continue;
+
+ fstrcpy(string_sid, kbuf.dptr+strlen(GROUP_PREFIX));
+
+ string_to_sid(&map->sid, string_sid);
+
+ ret = tdb_unpack(dbuf.dptr, dbuf.dsize, "ddff",
+ &map->gid, &map->sid_name_use, &map->nt_name, &map->comment);
+
+ SAFE_FREE(dbuf.dptr);
+
+ if ( ret == -1 ) {
+ DEBUG(3,("get_group_map_from_ntname: tdb_unpack failure\n"));
+ return False;
+ }
+
+ if (StrCaseCmp(name, map->nt_name)==0) {
+ SAFE_FREE(kbuf.dptr);
+ return True;
+ }
+ }
+
+ return False;
+}
+
+/****************************************************************************
+ Remove a group mapping entry.
+****************************************************************************/
+
+static BOOL group_map_remove(const DOM_SID *sid)
+{
+ TDB_DATA kbuf, dbuf;
+ pstring key;
+ fstring string_sid;
+
+ if(!init_group_mapping()) {
+ DEBUG(0,("failed to initialize group mapping\n"));
+ return(False);
+ }
+
+ /* the key is the SID, retrieving is direct */
+
+ sid_to_string(string_sid, sid);
+ slprintf(key, sizeof(key), "%s%s", GROUP_PREFIX, string_sid);
+
+ kbuf.dptr = key;
+ kbuf.dsize = strlen(key)+1;
+
+ dbuf = tdb_fetch(tdb, kbuf);
+ if (!dbuf.dptr)
+ return False;
+
+ SAFE_FREE(dbuf.dptr);
+
+ if(tdb_delete(tdb, kbuf) != TDB_SUCCESS)
+ return False;
+
+ return True;
+}
+
+/****************************************************************************
+ Enumerate the group mapping.
+****************************************************************************/
+
+static BOOL enum_group_mapping(enum SID_NAME_USE sid_name_use, GROUP_MAP **rmap,
+ int *num_entries, BOOL unix_only)
+{
+ TDB_DATA kbuf, dbuf, newkey;
+ fstring string_sid;
+ fstring group_type;
+ GROUP_MAP map;
+ GROUP_MAP *mapt;
+ int ret;
+ int entries=0;
+
+ if(!init_group_mapping()) {
+ DEBUG(0,("failed to initialize group mapping\n"));
+ return(False);
+ }
+
+ *num_entries=0;
+ *rmap=NULL;
+
+ for (kbuf = tdb_firstkey(tdb);
+ kbuf.dptr;
+ newkey = tdb_nextkey(tdb, kbuf), safe_free(kbuf.dptr), kbuf=newkey) {
+
+ if (strncmp(kbuf.dptr, GROUP_PREFIX, strlen(GROUP_PREFIX)) != 0)
+ continue;
+
+ dbuf = tdb_fetch(tdb, kbuf);
+ if (!dbuf.dptr)
+ continue;
+
+ fstrcpy(string_sid, kbuf.dptr+strlen(GROUP_PREFIX));
+
+ ret = tdb_unpack(dbuf.dptr, dbuf.dsize, "ddff",
+ &map.gid, &map.sid_name_use, &map.nt_name, &map.comment);
+
+ SAFE_FREE(dbuf.dptr);
+
+ if ( ret == -1 ) {
+ DEBUG(3,("enum_group_mapping: tdb_unpack failure\n"));
+ continue;
+ }
+
+ /* list only the type or everything if UNKNOWN */
+ if (sid_name_use!=SID_NAME_UNKNOWN && sid_name_use!=map.sid_name_use) {
+ DEBUG(11,("enum_group_mapping: group %s is not of the requested type\n", map.nt_name));
+ continue;
+ }
+
+ if (unix_only==ENUM_ONLY_MAPPED && map.gid==-1) {
+ DEBUG(11,("enum_group_mapping: group %s is non mapped\n", map.nt_name));
+ continue;
+ }
+
+ string_to_sid(&map.sid, string_sid);
+
+ decode_sid_name_use(group_type, map.sid_name_use);
+ DEBUG(11,("enum_group_mapping: returning group %s of type %s\n", map.nt_name ,group_type));
+
+ mapt=(GROUP_MAP *)Realloc((*rmap), (entries+1)*sizeof(GROUP_MAP));
+ if (!mapt) {
+ DEBUG(0,("enum_group_mapping: Unable to enlarge group map!\n"));
+ SAFE_FREE(*rmap);
+ return False;
+ }
+ else
+ (*rmap) = mapt;
+
+ mapt[entries].gid = map.gid;
+ sid_copy( &mapt[entries].sid, &map.sid);
+ mapt[entries].sid_name_use = map.sid_name_use;
+ fstrcpy(mapt[entries].nt_name, map.nt_name);
+ fstrcpy(mapt[entries].comment, map.comment);
+
+ entries++;
+
+ }
+
+ *num_entries=entries;
+
+ return True;
+}
+
+/* This operation happens on session setup, so it should better be fast. We
+ * store a list of aliases a SID is member of hanging off MEMBEROF/SID. */
+
+static NTSTATUS alias_memberships(const DOM_SID *sid, DOM_SID **sids, int *num)
+{
+ fstring key, string_sid;
+ TDB_DATA kbuf, dbuf;
+ const char *p;
+
+ *num = 0;
+ *sids = NULL;
+
+ if (!init_group_mapping()) {
+ DEBUG(0,("failed to initialize group mapping\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ sid_to_string(string_sid, sid);
+ slprintf(key, sizeof(key), "%s%s", MEMBEROF_PREFIX, string_sid);
+
+ kbuf.dsize = strlen(key)+1;
+ kbuf.dptr = key;
+
+ dbuf = tdb_fetch(tdb, kbuf);
+
+ if (dbuf.dptr == NULL) {
+ return NT_STATUS_OK;
+ }
+
+ p = dbuf.dptr;
+
+ while (next_token(&p, string_sid, " ", sizeof(string_sid))) {
+
+ DOM_SID alias;
+
+ if (!string_to_sid(&alias, string_sid))
+ continue;
+
+ add_sid_to_array(&alias, sids, num);
+
+ if (sids == NULL)
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ SAFE_FREE(dbuf.dptr);
+ return NT_STATUS_OK;
+}
+
+static BOOL is_aliasmem(const DOM_SID *alias, const DOM_SID *member)
+{
+ DOM_SID *sids;
+ int i, num;
+
+ /* This feels the wrong way round, but the on-disk data structure
+ * dictates it this way. */
+ if (!NT_STATUS_IS_OK(alias_memberships(member, &sids, &num)))
+ return False;
+
+ for (i=0; i<num; i++) {
+ if (sid_compare(alias, &sids[i]) == 0) {
+ SAFE_FREE(sids);
+ return True;
+ }
+ }
+ SAFE_FREE(sids);
+ return False;
+}
+
+static NTSTATUS add_aliasmem(const DOM_SID *alias, const DOM_SID *member)
+{
+ GROUP_MAP map;
+ TDB_DATA kbuf, dbuf;
+ pstring key;
+ fstring string_sid;
+ char *new_memberstring;
+ int result;
+
+ if(!init_group_mapping()) {
+ DEBUG(0,("failed to initialize group mapping\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (!get_group_map_from_sid(*alias, &map))
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ if ( (map.sid_name_use != SID_NAME_ALIAS) &&
+ (map.sid_name_use != SID_NAME_WKN_GRP) )
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ if (is_aliasmem(alias, member))
+ return NT_STATUS_MEMBER_IN_ALIAS;
+
+ sid_to_string(string_sid, member);
+ slprintf(key, sizeof(key), "%s%s", MEMBEROF_PREFIX, string_sid);
+
+ kbuf.dsize = strlen(key)+1;
+ kbuf.dptr = key;
+
+ dbuf = tdb_fetch(tdb, kbuf);
+
+ sid_to_string(string_sid, alias);
+
+ if (dbuf.dptr != NULL) {
+ asprintf(&new_memberstring, "%s %s", (char *)(dbuf.dptr),
+ string_sid);
+ } else {
+ new_memberstring = strdup(string_sid);
+ }
+
+ if (new_memberstring == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ SAFE_FREE(dbuf.dptr);
+ dbuf.dsize = strlen(new_memberstring)+1;
+ dbuf.dptr = new_memberstring;
+
+ result = tdb_store(tdb, kbuf, dbuf, 0);
+
+ SAFE_FREE(new_memberstring);
+
+ return (result == 0 ? NT_STATUS_OK : NT_STATUS_ACCESS_DENIED);
+}
+
+struct aliasmem_closure {
+ const DOM_SID *alias;
+ DOM_SID **sids;
+ int *num;
+};
+
+static int collect_aliasmem(TDB_CONTEXT *tdb_ctx, TDB_DATA key, TDB_DATA data,
+ void *state)
+{
+ struct aliasmem_closure *closure = (struct aliasmem_closure *)state;
+ const char *p;
+ fstring alias_string;
+
+ if (strncmp(key.dptr, MEMBEROF_PREFIX,
+ strlen(MEMBEROF_PREFIX)) != 0)
+ return 0;
+
+ p = data.dptr;
+
+ while (next_token(&p, alias_string, " ", sizeof(alias_string))) {
+
+ DOM_SID alias, member;
+ const char *member_string;
+
+
+ if (!string_to_sid(&alias, alias_string))
+ continue;
+
+ if (sid_compare(closure->alias, &alias) != 0)
+ continue;
+
+ /* Ok, we found the alias we're looking for in the membership
+ * list currently scanned. The key represents the alias
+ * member. Add that. */
+
+ member_string = strchr(key.dptr, '/');
+
+ /* Above we tested for MEMBEROF_PREFIX which includes the
+ * slash. */
+
+ SMB_ASSERT(member_string != NULL);
+ member_string += 1;
+
+ if (!string_to_sid(&member, member_string))
+ continue;
+
+ add_sid_to_array(&member, closure->sids, closure->num);
+ }
+
+ return 0;
+}
+
+static NTSTATUS enum_aliasmem(const DOM_SID *alias, DOM_SID **sids, int *num)
+{
+ GROUP_MAP map;
+ struct aliasmem_closure closure;
+
+ if(!init_group_mapping()) {
+ DEBUG(0,("failed to initialize group mapping\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (!get_group_map_from_sid(*alias, &map))
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ if ( (map.sid_name_use != SID_NAME_ALIAS) &&
+ (map.sid_name_use != SID_NAME_WKN_GRP) )
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ *sids = NULL;
+ *num = 0;
+
+ closure.alias = alias;
+ closure.sids = sids;
+ closure.num = num;
+
+ tdb_traverse(tdb, collect_aliasmem, &closure);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS del_aliasmem(const DOM_SID *alias, const DOM_SID *member)
+{
+ NTSTATUS result;
+ DOM_SID *sids;
+ int i, num;
+ BOOL found = False;
+ char *member_string;
+ TDB_DATA kbuf, dbuf;
+ pstring key;
+ fstring sid_string;
+
+ result = alias_memberships(member, &sids, &num);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ for (i=0; i<num; i++) {
+ if (sid_compare(&sids[i], alias) == 0) {
+ found = True;
+ break;
+ }
+ }
+
+ if (!found) {
+ SAFE_FREE(sids);
+ return NT_STATUS_MEMBER_NOT_IN_ALIAS;
+ }
+
+ if (i < num)
+ sids[i] = sids[num-1];
+
+ num -= 1;
+
+ sid_to_string(sid_string, member);
+ slprintf(key, sizeof(key), "%s%s", MEMBEROF_PREFIX, sid_string);
+
+ kbuf.dsize = strlen(key)+1;
+ kbuf.dptr = key;
+
+ if (num == 0)
+ return tdb_delete(tdb, kbuf) == 0 ?
+ NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+
+ member_string = strdup("");
+
+ if (member_string == NULL) {
+ SAFE_FREE(sids);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<num; i++) {
+ char *s = member_string;
+
+ sid_to_string(sid_string, &sids[i]);
+ asprintf(&member_string, "%s %s", s, sid_string);
+
+ SAFE_FREE(s);
+ if (member_string == NULL) {
+ SAFE_FREE(sids);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ dbuf.dsize = strlen(member_string)+1;
+ dbuf.dptr = member_string;
+
+ result = tdb_store(tdb, kbuf, dbuf, 0) == 0 ?
+ NT_STATUS_OK : NT_STATUS_ACCESS_DENIED;
+
+ SAFE_FREE(sids);
+ SAFE_FREE(member_string);
+
+ return result;
+}
+
+/*
+ *
+ * High level functions
+ * better to use them than the lower ones.
+ *
+ * we are checking if the group is in the mapping file
+ * and if the group is an existing unix group
+ *
+ */
+
+/* get a domain group from it's SID */
+
+BOOL get_domain_group_from_sid(DOM_SID sid, GROUP_MAP *map)
+{
+ struct group *grp;
+ BOOL ret;
+
+ if(!init_group_mapping()) {
+ DEBUG(0,("failed to initialize group mapping\n"));
+ return(False);
+ }
+
+ DEBUG(10, ("get_domain_group_from_sid\n"));
+
+ /* if the group is NOT in the database, it CAN NOT be a domain group */
+
+ become_root();
+ ret = pdb_getgrsid(map, sid);
+ unbecome_root();
+
+ if ( !ret )
+ return False;
+
+ DEBUG(10, ("get_domain_group_from_sid: SID found in the TDB\n"));
+
+ /* if it's not a domain group, continue */
+ if (map->sid_name_use!=SID_NAME_DOM_GRP) {
+ return False;
+ }
+
+ DEBUG(10, ("get_domain_group_from_sid: SID is a domain group\n"));
+
+ if (map->gid==-1) {
+ return False;
+ }
+
+ DEBUG(10, ("get_domain_group_from_sid: SID is mapped to gid:%lu\n",(unsigned long)map->gid));
+
+ grp = getgrgid(map->gid);
+ if ( !grp ) {
+ DEBUG(10, ("get_domain_group_from_sid: gid DOESN'T exist in UNIX security\n"));
+ return False;
+ }
+
+ DEBUG(10, ("get_domain_group_from_sid: gid exists in UNIX security\n"));
+
+ return True;
+}
+
+
+/* get a local (alias) group from it's SID */
+
+BOOL get_local_group_from_sid(DOM_SID *sid, GROUP_MAP *map)
+{
+ BOOL ret;
+
+ if(!init_group_mapping()) {
+ DEBUG(0,("failed to initialize group mapping\n"));
+ return(False);
+ }
+
+ /* The group is in the mapping table */
+ become_root();
+ ret = pdb_getgrsid(map, *sid);
+ unbecome_root();
+
+ if ( !ret )
+ return False;
+
+ if ( ( (map->sid_name_use != SID_NAME_ALIAS) &&
+ (map->sid_name_use != SID_NAME_WKN_GRP) )
+ || (map->gid == -1)
+ || (getgrgid(map->gid) == NULL) )
+ {
+ return False;
+ }
+
+#if 1 /* JERRY */
+ /* local groups only exist in the group mapping DB so this
+ is not necessary */
+
+ else {
+ /* the group isn't in the mapping table.
+ * make one based on the unix information */
+ uint32 alias_rid;
+ struct group *grp;
+
+ sid_peek_rid(sid, &alias_rid);
+ map->gid=pdb_group_rid_to_gid(alias_rid);
+
+ grp = getgrgid(map->gid);
+ if ( !grp ) {
+ DEBUG(3,("get_local_group_from_sid: No unix group for [%ul]\n", map->gid));
+ return False;
+ }
+
+ map->sid_name_use=SID_NAME_ALIAS;
+
+ fstrcpy(map->nt_name, grp->gr_name);
+ fstrcpy(map->comment, "Local Unix Group");
+
+ sid_copy(&map->sid, sid);
+ }
+#endif
+
+ return True;
+}
+
+/* get a builtin group from it's SID */
+
+BOOL get_builtin_group_from_sid(DOM_SID *sid, GROUP_MAP *map)
+{
+ struct group *grp;
+ BOOL ret;
+
+
+ if(!init_group_mapping()) {
+ DEBUG(0,("failed to initialize group mapping\n"));
+ return(False);
+ }
+
+ become_root();
+ ret = pdb_getgrsid(map, *sid);
+ unbecome_root();
+
+ if ( !ret )
+ return False;
+
+ if (map->sid_name_use!=SID_NAME_WKN_GRP) {
+ return False;
+ }
+
+ if (map->gid==-1) {
+ return False;
+ }
+
+ if ( (grp=getgrgid(map->gid)) == NULL) {
+ return False;
+ }
+
+ return True;
+}
+
+
+
+/****************************************************************************
+Returns a GROUP_MAP struct based on the gid.
+****************************************************************************/
+BOOL get_group_from_gid(gid_t gid, GROUP_MAP *map)
+{
+ struct group *grp;
+ BOOL ret;
+
+ if(!init_group_mapping()) {
+ DEBUG(0,("failed to initialize group mapping\n"));
+ return(False);
+ }
+
+ if ( (grp=getgrgid(gid)) == NULL)
+ return False;
+
+ /*
+ * make a group map from scratch if doesn't exist.
+ */
+
+ become_root();
+ ret = pdb_getgrgid(map, gid);
+ unbecome_root();
+
+ if ( !ret ) {
+ map->gid=gid;
+ map->sid_name_use=SID_NAME_ALIAS;
+
+ /* interim solution until we have a last RID allocated */
+
+ sid_copy(&map->sid, get_global_sam_sid());
+ sid_append_rid(&map->sid, pdb_gid_to_group_rid(gid));
+
+ fstrcpy(map->nt_name, grp->gr_name);
+ fstrcpy(map->comment, "Local Unix Group");
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Create a UNIX group on demand.
+****************************************************************************/
+
+int smb_create_group(char *unix_group, gid_t *new_gid)
+{
+ pstring add_script;
+ int ret = -1;
+ int fd = 0;
+
+ *new_gid = 0;
+
+ /* defer to scripts */
+
+ if ( *lp_addgroup_script() ) {
+ pstrcpy(add_script, lp_addgroup_script());
+ pstring_sub(add_script, "%g", unix_group);
+ ret = smbrun(add_script, (new_gid!=NULL) ? &fd : NULL);
+ DEBUG(3,("smb_create_group: Running the command `%s' gave %d\n",add_script,ret));
+ if (ret != 0)
+ return ret;
+
+ if (fd != 0) {
+ fstring output;
+
+ *new_gid = 0;
+ if (read(fd, output, sizeof(output)) > 0) {
+ *new_gid = (gid_t)strtoul(output, NULL, 10);
+ }
+
+ close(fd);
+ }
+
+ } else if ( winbind_create_group( unix_group, NULL ) ) {
+
+ DEBUG(3,("smb_create_group: winbindd created the group (%s)\n",
+ unix_group));
+ ret = 0;
+ }
+
+ if (*new_gid == 0) {
+ struct group *grp = getgrnam(unix_group);
+
+ if (grp != NULL)
+ *new_gid = grp->gr_gid;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ Delete a UNIX group on demand.
+****************************************************************************/
+
+int smb_delete_group(char *unix_group)
+{
+ pstring del_script;
+ int ret;
+
+ /* defer to scripts */
+
+ if ( *lp_delgroup_script() ) {
+ pstrcpy(del_script, lp_delgroup_script());
+ pstring_sub(del_script, "%g", unix_group);
+ ret = smbrun(del_script,NULL);
+ DEBUG(3,("smb_delete_group: Running the command `%s' gave %d\n",del_script,ret));
+ return ret;
+ }
+
+ if ( winbind_delete_group( unix_group ) ) {
+ DEBUG(3,("smb_delete_group: winbindd deleted the group (%s)\n",
+ unix_group));
+ return 0;
+ }
+
+ return -1;
+}
+
+/****************************************************************************
+ Set a user's primary UNIX group.
+****************************************************************************/
+int smb_set_primary_group(const char *unix_group, const char* unix_user)
+{
+ pstring add_script;
+ int ret;
+
+ /* defer to scripts */
+
+ if ( *lp_setprimarygroup_script() ) {
+ pstrcpy(add_script, lp_setprimarygroup_script());
+ all_string_sub(add_script, "%g", unix_group, sizeof(add_script));
+ all_string_sub(add_script, "%u", unix_user, sizeof(add_script));
+ ret = smbrun(add_script,NULL);
+ DEBUG(3,("smb_set_primary_group: "
+ "Running the command `%s' gave %d\n",add_script,ret));
+ return ret;
+ }
+
+ /* Try winbindd */
+
+ if ( winbind_set_user_primary_group( unix_user, unix_group ) ) {
+ DEBUG(3,("smb_delete_group: winbindd set the group (%s) as the primary group for user (%s)\n",
+ unix_group, unix_user));
+ return 0;
+ }
+
+ return -1;
+}
+
+/****************************************************************************
+ Add a user to a UNIX group.
+****************************************************************************/
+
+int smb_add_user_group(char *unix_group, char *unix_user)
+{
+ pstring add_script;
+ int ret;
+
+ /* defer to scripts */
+
+ if ( *lp_addusertogroup_script() ) {
+ pstrcpy(add_script, lp_addusertogroup_script());
+ pstring_sub(add_script, "%g", unix_group);
+ pstring_sub(add_script, "%u", unix_user);
+ ret = smbrun(add_script,NULL);
+ DEBUG(3,("smb_add_user_group: Running the command `%s' gave %d\n",add_script,ret));
+ return ret;
+ }
+
+ /* Try winbindd */
+
+ if ( winbind_add_user_to_group( unix_user, unix_group ) ) {
+ DEBUG(3,("smb_delete_group: winbindd added user (%s) to the group (%s)\n",
+ unix_user, unix_group));
+ return -1;
+ }
+
+ return -1;
+}
+
+/****************************************************************************
+ Delete a user from a UNIX group
+****************************************************************************/
+
+int smb_delete_user_group(const char *unix_group, const char *unix_user)
+{
+ pstring del_script;
+ int ret;
+
+ /* defer to scripts */
+
+ if ( *lp_deluserfromgroup_script() ) {
+ pstrcpy(del_script, lp_deluserfromgroup_script());
+ pstring_sub(del_script, "%g", unix_group);
+ pstring_sub(del_script, "%u", unix_user);
+ ret = smbrun(del_script,NULL);
+ DEBUG(3,("smb_delete_user_group: Running the command `%s' gave %d\n",del_script,ret));
+ return ret;
+ }
+
+ /* Try winbindd */
+
+ if ( winbind_remove_user_from_group( unix_user, unix_group ) ) {
+ DEBUG(3,("smb_delete_group: winbindd removed user (%s) from the group (%s)\n",
+ unix_user, unix_group));
+ return 0;
+ }
+
+ return -1;
+}
+
+
+NTSTATUS pdb_default_getgrsid(struct pdb_methods *methods, GROUP_MAP *map,
+ DOM_SID sid)
+{
+ return get_group_map_from_sid(sid, map) ?
+ NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_default_getgrgid(struct pdb_methods *methods, GROUP_MAP *map,
+ gid_t gid)
+{
+ return get_group_map_from_gid(gid, map) ?
+ NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_default_getgrnam(struct pdb_methods *methods, GROUP_MAP *map,
+ const char *name)
+{
+ return get_group_map_from_ntname(name, map) ?
+ NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_default_add_group_mapping_entry(struct pdb_methods *methods,
+ GROUP_MAP *map)
+{
+ return add_mapping_entry(map, TDB_INSERT) ?
+ NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_default_update_group_mapping_entry(struct pdb_methods *methods,
+ GROUP_MAP *map)
+{
+ return add_mapping_entry(map, TDB_REPLACE) ?
+ NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_default_delete_group_mapping_entry(struct pdb_methods *methods,
+ DOM_SID sid)
+{
+ return group_map_remove(&sid) ?
+ NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_default_enum_group_mapping(struct pdb_methods *methods,
+ enum SID_NAME_USE sid_name_use,
+ GROUP_MAP **rmap, int *num_entries,
+ BOOL unix_only)
+{
+ return enum_group_mapping(sid_name_use, rmap, num_entries, unix_only) ?
+ NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_default_find_alias(struct pdb_methods *methods,
+ const char *name, DOM_SID *sid)
+{
+ GROUP_MAP map;
+
+ if (!pdb_getgrnam(&map, name))
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ if ((map.sid_name_use != SID_NAME_WKN_GRP) &&
+ (map.sid_name_use != SID_NAME_ALIAS))
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ sid_copy(sid, &map.sid);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_default_create_alias(struct pdb_methods *methods,
+ const char *name, uint32 *rid)
+{
+ DOM_SID sid;
+ enum SID_NAME_USE type;
+ uint32 new_rid;
+ gid_t gid;
+
+ GROUP_MAP map;
+
+ if (lookup_name(get_global_sam_name(), name, &sid, &type))
+ return NT_STATUS_ALIAS_EXISTS;
+
+ if (!winbind_allocate_rid(&new_rid))
+ return NT_STATUS_ACCESS_DENIED;
+
+ sid_copy(&sid, get_global_sam_sid());
+ sid_append_rid(&sid, new_rid);
+
+ /* Here we allocate the gid */
+ if (!winbind_sid_to_gid(&gid, &sid)) {
+ DEBUG(0, ("Could not get gid for new RID\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ map.gid = gid;
+ sid_copy(&map.sid, &sid);
+ map.sid_name_use = SID_NAME_ALIAS;
+ fstrcpy(map.nt_name, name);
+ fstrcpy(map.comment, "");
+
+ if (!pdb_add_group_mapping_entry(&map)) {
+ DEBUG(0, ("Could not add group mapping entry for alias %s\n",
+ name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ *rid = new_rid;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_default_delete_alias(struct pdb_methods *methods,
+ const DOM_SID *sid)
+{
+ return pdb_delete_group_mapping_entry(*sid) ?
+ NT_STATUS_OK : NT_STATUS_ACCESS_DENIED;
+}
+
+NTSTATUS pdb_default_enum_aliases(struct pdb_methods *methods,
+ const DOM_SID *sid,
+ uint32 start_idx, uint32 max_entries,
+ uint32 *num_aliases,
+ struct acct_info **info)
+{
+ extern DOM_SID global_sid_Builtin;
+
+ GROUP_MAP *map;
+ int i, num_maps;
+ enum SID_NAME_USE type = SID_NAME_UNKNOWN;
+
+ if (sid_compare(sid, get_global_sam_sid()) == 0)
+ type = SID_NAME_ALIAS;
+
+ if (sid_compare(sid, &global_sid_Builtin) == 0)
+ type = SID_NAME_WKN_GRP;
+
+ if (!pdb_enum_group_mapping(type, &map, &num_maps, False) ||
+ (num_maps == 0)) {
+ *num_aliases = 0;
+ *info = NULL;
+ goto done;
+ }
+
+ if (start_idx > num_maps) {
+ *num_aliases = 0;
+ *info = NULL;
+ goto done;
+ }
+
+ *num_aliases = num_maps - start_idx;
+
+ if (*num_aliases > max_entries)
+ *num_aliases = max_entries;
+
+ *info = malloc(sizeof(struct acct_info) * (*num_aliases));
+
+ for (i=0; i<*num_aliases; i++) {
+ fstrcpy((*info)[i].acct_name, map[i+start_idx].nt_name);
+ fstrcpy((*info)[i].acct_desc, map[i+start_idx].comment);
+ sid_peek_rid(&map[i].sid, &(*info)[i+start_idx].rid);
+ }
+
+ done:
+ SAFE_FREE(map);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_default_get_aliasinfo(struct pdb_methods *methods,
+ const DOM_SID *sid,
+ struct acct_info *info)
+{
+ GROUP_MAP map;
+
+ if (!pdb_getgrsid(&map, *sid))
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ fstrcpy(info->acct_name, map.nt_name);
+ fstrcpy(info->acct_desc, map.comment);
+ sid_peek_rid(&map.sid, &info->rid);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_default_set_aliasinfo(struct pdb_methods *methods,
+ const DOM_SID *sid,
+ struct acct_info *info)
+{
+ GROUP_MAP map;
+
+ if (!pdb_getgrsid(&map, *sid))
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ fstrcpy(map.comment, info->acct_desc);
+
+ if (!pdb_update_group_mapping_entry(&map))
+ return NT_STATUS_ACCESS_DENIED;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_default_add_aliasmem(struct pdb_methods *methods,
+ const DOM_SID *alias, const DOM_SID *member)
+{
+ return add_aliasmem(alias, member);
+}
+
+NTSTATUS pdb_default_del_aliasmem(struct pdb_methods *methods,
+ const DOM_SID *alias, const DOM_SID *member)
+{
+ return del_aliasmem(alias, member);
+}
+
+NTSTATUS pdb_default_enum_aliasmem(struct pdb_methods *methods,
+ const DOM_SID *alias, DOM_SID **members,
+ int *num_members)
+{
+ return enum_aliasmem(alias, members, num_members);
+}
+
+NTSTATUS pdb_default_alias_memberships(struct pdb_methods *methods,
+ const DOM_SID *sid,
+ DOM_SID **aliases, int *num)
+{
+ return alias_memberships(sid, aliases, num);
+}
+
+/**********************************************************************
+ no ops for passdb backends that don't implement group mapping
+ *********************************************************************/
+
+NTSTATUS pdb_nop_getgrsid(struct pdb_methods *methods, GROUP_MAP *map,
+ DOM_SID sid)
+{
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_nop_getgrgid(struct pdb_methods *methods, GROUP_MAP *map,
+ gid_t gid)
+{
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_nop_getgrnam(struct pdb_methods *methods, GROUP_MAP *map,
+ const char *name)
+{
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_nop_add_group_mapping_entry(struct pdb_methods *methods,
+ GROUP_MAP *map)
+{
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_nop_update_group_mapping_entry(struct pdb_methods *methods,
+ GROUP_MAP *map)
+{
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_nop_delete_group_mapping_entry(struct pdb_methods *methods,
+ DOM_SID sid)
+{
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_nop_enum_group_mapping(struct pdb_methods *methods,
+ enum SID_NAME_USE sid_name_use,
+ GROUP_MAP **rmap, int *num_entries,
+ BOOL unix_only)
+{
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/****************************************************************************
+ These need to be redirected through pdb_interface.c
+****************************************************************************/
+BOOL pdb_get_dom_grp_info(const DOM_SID *sid, struct acct_info *info)
+{
+ GROUP_MAP map;
+ BOOL res;
+
+ become_root();
+ res = get_domain_group_from_sid(*sid, &map);
+ unbecome_root();
+
+ if (!res)
+ return False;
+
+ fstrcpy(info->acct_name, map.nt_name);
+ fstrcpy(info->acct_desc, map.comment);
+ sid_peek_rid(sid, &info->rid);
+ return True;
+}
+
+BOOL pdb_set_dom_grp_info(const DOM_SID *sid, const struct acct_info *info)
+{
+ GROUP_MAP map;
+
+ if (!get_domain_group_from_sid(*sid, &map))
+ return False;
+
+ fstrcpy(map.nt_name, info->acct_name);
+ fstrcpy(map.comment, info->acct_desc);
+
+ return pdb_update_group_mapping_entry(&map);
+}
+
+
diff --git a/source/include/.cvsignore b/source/include/.cvsignore
new file mode 100644
index 00000000000..7dff121f143
--- /dev/null
+++ b/source/include/.cvsignore
@@ -0,0 +1,7 @@
+build_env.h
+config.h
+stamp-h
+proto.h
+wrepld_proto.h
+config.h.in
+version.h
diff --git a/source/include/MacExtensions.h b/source/include/MacExtensions.h
new file mode 100644
index 00000000000..d09370ed9f7
--- /dev/null
+++ b/source/include/MacExtensions.h
@@ -0,0 +1,246 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) John H Terpstra 1996-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+ Copyright (C) Paul Ashton 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef _MAC_EXTENSIONS_H
+#define _MAC_EXTENSIONS_H
+
+/* Folder that holds the stream info */
+#define STREAM_FOLDER ".streams"
+#define STREAM_FOLDER_SLASH ".streams/"
+
+/* Common Streams Names*/
+#define DefaultStreamTestLen 6
+#define DefaultStreamTest ":$DATA"
+#define AFPDATA_STREAM "::$DATA"
+#define AFPINFO_STREAM ":AFP_AfpInfo:$DATA"
+#define AFPRESOURCE_STREAM ":AFP_Resource:$DATA"
+#define AFPCOMMENTS_STREAM ":Comments:$DATA"
+#define AFPDESKTOP_STREAM ":AFP_DeskTop:$DATA"
+#define AFPIDINDEX_STREAM ":AFP_IdIndex:$DATA"
+
+/*
+** NT's AFP_AfpInfo stream structure
+*/
+#define APF_INFO_SIZE 0x3c
+#define AFP_Signature 0x41465000
+#define AFP_Version 0x00000100
+#define AFP_BackupTime 0x00000080
+#define AFP_FinderSize 32
+/*
+** Orginal AFP_AfpInfo stream used by NT
+** We needed a way to store the create date so SAMBA
+** AFP_AfpInfo adds for bytes to this structrure
+** and call's it _SambaAfpInfo
+*/
+typedef struct _AfpInfo
+{
+ uint32 afpi_Signature; /* Must be *(PDWORD)"AFP" */
+ uint32 afpi_Version; /* Must be 0x00010000 */
+ uint32 afpi_Reserved1;
+ uint32 afpi_BackupTime; /* Backup time for the file/dir */
+ unsigned char afpi_FinderInfo[AFP_FinderSize]; /* Finder Info (32 bytes) */
+ unsigned char afpi_ProDosInfo[6]; /* ProDos Info (6 bytes) # */
+ unsigned char afpi_Reserved2[6];
+} AfpInfo;
+
+typedef struct _SambaAfpInfo
+{
+ AfpInfo afp;
+ unsigned long createtime;
+} SambaAfpInfo;
+
+/*
+** On SAMBA this structrue is followed by 4 bytes that store the create
+** date of the file or folder asociated with it.
+*/
+
+/*
+** These extentions are only supported with the NT LM 0.12 Dialect. These extentions
+** will be process on a share by share bases.
+*/
+
+/*
+** Trans2_Query_FS_Information Call is used by the MacCIFS extentions for three reasons.
+** First to see if the remote server share supports the basic Macintosh CIFS extentions.
+** Second to return some basic need information about the share to the Macintosh.
+** Third to see if this share support any other Macintosh extentions.
+**
+** We will be using infromation levels that are betwwen 0x300 and 0x399 for all Macintosh
+** extentions calls. The first of these will be the SMB_MAC_QUERY_FS_INFO level which
+** will allow the server to return the MacQueryFSInfo structure. All fields are Little
+** Endian unless other wise specified.
+*/
+#define SMB_MAC_QUERY_FS_INFO 0x301
+
+
+
+/*
+** The server will return folder access control in the Trans2_Find_First2
+** and Trans2_Find_Next2 message described later in this document.
+*/
+#define SUPPORT_MAC_ACCESS_CNTRL 0x0010
+/*
+** The server supports setting/getting comments using the mechanism in this
+** document instead of using the NTFS format described in the Introduction.
+*/
+#define SUPPORT_MAC_GETSETCOMMENTS 0x0020
+/*
+** The Server supports setting and getting Macintosh desktop database information
+** using the mechanism in this document.
+*/
+#define SUPPORT_MAC_DESKTOPDB_CALLS 0x0040
+/*
+** The server will return a unique id for files and directories in the
+** Trans2_Find_First2 and Trans2_Find_Next2 message described later in this document.
+*/
+#define SUPPORT_MAC_UNIQUE_IDS 0x0080
+/*
+** The server will return this flag telling the client that the server does
+** not support streams or the Macintosh extensions. The rest of this message
+** will be ignored by the client.
+*/
+#define NO_STREAMS_OR_MAC_SUPPORT 0x0100
+
+/*
+** We will be adding a new info level to the Trans2_Find_First2 and Trans2_Find_Next2.
+** This info level will be SMB_MAC_FIND_BOTH_HFS_INFO and will support the server
+** return additional information need by the Macintosh. All fields are Little
+** Endian unless other wise specified.
+*/
+
+#define SMB_MAC_FIND_BOTH_HFS_INFO 0x302
+
+enum {
+ ownerRead = 0x0400,
+ ownerWrite = 0x0200,
+ ownerSearch = 0x0100,
+ groupRead = 0x0040,
+ groupWrite = 0x0020,
+ groupSearch = 0x0010,
+ otherRead = 0x0004,
+ otherWrite = 0x0002,
+ otherSearch = 0x0001,
+ Owner = 0x0800
+};
+
+
+/*
+** We will be adding a new info level to the Trans2_Set_Path_Information.
+** This info level will be SMB_MAC_SET_FINDER_INFO and will support the client
+** setting information on the server need by the Macintosh. All fields are Little
+** Endian unless other wise specified.
+*/
+
+#define SMB_MAC_SET_FINDER_INFO 0x303
+
+enum {
+ SetCreateDate = 0x01, /* If this is set then set the create date of the file/folder */
+ SetModDate = 0x02, /* If this is set then set the modify date of the file/folder */
+ SetFLAttrib = 0x04, /* If this is set then set the Macintosh lock bit of the file/folder */
+ FndrInfo1 = 0x08, /* If this is set then set the first 16 bytes of finder info */
+ FndrInfo2 = 0x10, /* If this is set then set the second 16 bytes of finder info */
+ SetHidden = 0x20 /* We are either setting or unsetting the hidden bit */
+};
+
+
+/*
+** We will be adding some new info level to the Trans2_Set_Path_Information and Trans2_Query_Path_Information.
+** These info levels will allow the client to add, get, and remove desktop inforamtion from the
+** server. How the server stores this information is up to them.
+*/
+
+/*
+** We need to be able to store an application name and its creator in a database. We send a
+** Trans2_Set_Path_Information call with the full path of the application in the path field.
+** We will send an info level that represents adding an application name and creator to the database.
+** We will pass the File Creator in the data message.
+**
+** The server should just respond with no error or an error.
+*/
+#define SMB_MAC_DT_ADD_APPL 0x304
+
+/*
+** We need to be able to remove an application name and its creator from a database. We send a
+** Trans2_Set_Path_Information call with the full path of the application in the path field.
+** We will send an info level that represents removing an application name and creator from the database.
+** We will pass the File Creator in the data message.
+**
+** The server should just respond with no error or an error.
+*/
+#define SMB_MAC_DT_REMOVE_APPL 0x305
+
+
+/*
+** We need to be able to get an application name and its creator from a database. We send a
+** Trans2_Query_Path_Information call in which the name field is just ignore.
+** We will send an info level that represents getting an application name with a structure that
+** contains the File Creator and index. Were index has the following meaning.
+** Index = 0; Get the application path from the database with the most current date.
+** Index > 0; Use the index to find the application path from the database.
+** e.g. index of 5 means get the fifth entry of this application name in the database.
+** if not entry return an error.
+**
+** The server returns with a structure that contains the full path to the appication and
+** its creator's date.
+*/
+#define SMB_MAC_DT_GET_APPL 0x306
+
+
+/*
+** We need to be able to get an icon from a database. We send a Trans2_Query_Path_Information call in
+** which the path name is ignore. We will send an info level that represents getting an icon with a structure
+** that contains the Requested size of the icon, the Icon type, File Creator, and File Type.
+**
+** The server returns with a structure that contains the actual size of the icon
+** (must be less than requested length) and the icon bit map.
+*/
+#define SMB_MAC_DT_GET_ICON 0x307
+
+
+/*
+** We need to be able to get an icon from a database. We send a Trans2_Query_Path_Information call in
+** which the path name is ignore. We will send an info level that represents getting an icon with a structure
+** that contains the index and File Creator. The index allows the client to make repeated calls to the server
+** gathering all icon stored by this file creator.
+**
+**
+** The server returns with a structure that contains the actual size of the icon
+** (must be less than requested length) and the icon bit map, File Type, and Icon Type.
+*/
+#define SMB_MAC_DT_GET_ICON_INFO 0x308
+
+
+
+/*
+** We need to be able to add an icon to a database. We send a Trans2_Set_Path_Information call in
+** which the path name is ignore. We will send an info level that represents setting an icon with a structure
+** that contains the icon data, icon size, icon type, the file type, and file creator.
+**
+**
+** The server returns only that the call was succesfull or not.
+*/
+#define SMB_MAC_DT_ADD_ICON 0x309
+
+#endif /* _MAC_EXTENSIONS_H */
+
+/* _MAC_EXTENSIONS_H */
+
diff --git a/source/include/ads.h b/source/include/ads.h
new file mode 100644
index 00000000000..4daa65e796d
--- /dev/null
+++ b/source/include/ads.h
@@ -0,0 +1,226 @@
+/*
+ header for ads (active directory) library routines
+
+ basically this is a wrapper around ldap
+*/
+
+typedef struct {
+ void *ld; /* the active ldap structure */
+ struct in_addr ldap_ip; /* the ip of the active connection, if any */
+ time_t last_attempt; /* last attempt to reconnect */
+ int ldap_port;
+
+ int is_mine; /* do I own this structure's memory? */
+
+ /* info needed to find the server */
+ struct {
+ char *realm;
+ char *workgroup;
+ char *ldap_server;
+ char *ldap_uri;
+ int foreign; /* set to 1 if connecting to a foreign realm */
+ } server;
+
+ /* info needed to authenticate */
+ struct {
+ char *realm;
+ char *password;
+ char *user_name;
+ char *kdc_server;
+ unsigned flags;
+ int time_offset;
+ time_t expire;
+ } auth;
+
+ /* info derived from the servers config */
+ struct {
+ char *realm;
+ char *bind_path;
+ char *ldap_server_name;
+ time_t current_time;
+ } config;
+} ADS_STRUCT;
+
+/* there are 5 possible types of errors the ads subsystem can produce */
+enum ads_error_type {ENUM_ADS_ERROR_KRB5, ENUM_ADS_ERROR_GSS,
+ ENUM_ADS_ERROR_LDAP, ENUM_ADS_ERROR_SYSTEM, ENUM_ADS_ERROR_NT};
+
+typedef struct {
+ enum ads_error_type error_type;
+ union err_state{
+ int rc;
+ NTSTATUS nt_status;
+ } err;
+ /* For error_type = ENUM_ADS_ERROR_GSS minor_status describe GSS API error */
+ /* Where rc represents major_status of GSS API error */
+ int minor_status;
+} ADS_STATUS;
+
+#ifdef HAVE_ADS
+typedef LDAPMod **ADS_MODLIST;
+#else
+typedef void **ADS_MODLIST;
+#endif
+
+/* macros to simplify error returning */
+#define ADS_ERROR(rc) ADS_ERROR_LDAP(rc)
+#define ADS_ERROR_LDAP(rc) ads_build_error(ENUM_ADS_ERROR_LDAP, rc, 0)
+#define ADS_ERROR_SYSTEM(rc) ads_build_error(ENUM_ADS_ERROR_SYSTEM, rc?rc:EINVAL, 0)
+#define ADS_ERROR_KRB5(rc) ads_build_error(ENUM_ADS_ERROR_KRB5, rc, 0)
+#define ADS_ERROR_GSS(rc, minor) ads_build_error(ENUM_ADS_ERROR_GSS, rc, minor)
+#define ADS_ERROR_NT(rc) ads_build_nt_error(ENUM_ADS_ERROR_NT,rc)
+
+#define ADS_ERR_OK(status) ((status.error_type == ENUM_ADS_ERROR_NT) ? NT_STATUS_IS_OK(status.err.nt_status):(status.err.rc == 0))
+#define ADS_SUCCESS ADS_ERROR(0)
+
+/* time between reconnect attempts */
+#define ADS_RECONNECT_TIME 5
+
+/* timeout on searches */
+#define ADS_SEARCH_TIMEOUT 10
+
+/* ldap control oids */
+#define ADS_PAGE_CTL_OID "1.2.840.113556.1.4.319"
+#define ADS_NO_REFERRALS_OID "1.2.840.113556.1.4.1339"
+#define ADS_SERVER_SORT_OID "1.2.840.113556.1.4.473"
+#define ADS_PERMIT_MODIFY_OID "1.2.840.113556.1.4.1413"
+
+/* UserFlags for userAccountControl */
+#define UF_SCRIPT 0x00000001
+#define UF_ACCOUNTDISABLE 0x00000002
+#define UF_UNUSED_1 0x00000004
+#define UF_HOMEDIR_REQUIRED 0x00000008
+
+#define UF_LOCKOUT 0x00000010
+#define UF_PASSWD_NOTREQD 0x00000020
+#define UF_PASSWD_CANT_CHANGE 0x00000040
+#define UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED 0x00000080
+
+#define UF_TEMP_DUPLICATE_ACCOUNT 0x00000100
+#define UF_NORMAL_ACCOUNT 0x00000200
+#define UF_UNUSED_2 0x00000400
+#define UF_INTERDOMAIN_TRUST_ACCOUNT 0x00000800
+
+#define UF_WORKSTATION_TRUST_ACCOUNT 0x00001000
+#define UF_SERVER_TRUST_ACCOUNT 0x00002000
+#define UF_UNUSED_3 0x00004000
+#define UF_UNUSED_4 0x00008000
+
+#define UF_DONT_EXPIRE_PASSWD 0x00010000
+#define UF_MNS_LOGON_ACCOUNT 0x00020000
+#define UF_SMARTCARD_REQUIRED 0x00040000
+#define UF_TRUSTED_FOR_DELEGATION 0x00080000
+
+#define UF_NOT_DELEGATED 0x00100000
+#define UF_USE_DES_KEY_ONLY 0x00200000
+#define UF_DONT_REQUIRE_PREAUTH 0x00400000
+#define UF_UNUSED_5 0x00800000
+
+#define UF_UNUSED_6 0x01000000
+#define UF_UNUSED_7 0x02000000
+#define UF_UNUSED_8 0x04000000
+#define UF_UNUSED_9 0x08000000
+
+#define UF_UNUSED_10 0x10000000
+#define UF_UNUSED_11 0x20000000
+#define UF_UNUSED_12 0x40000000
+#define UF_UNUSED_13 0x80000000
+
+#define UF_MACHINE_ACCOUNT_MASK (\
+ UF_INTERDOMAIN_TRUST_ACCOUNT |\
+ UF_WORKSTATION_TRUST_ACCOUNT |\
+ UF_SERVER_TRUST_ACCOUNT \
+ )
+
+#define UF_ACCOUNT_TYPE_MASK (\
+ UF_TEMP_DUPLICATE_ACCOUNT |\
+ UF_NORMAL_ACCOUNT |\
+ UF_INTERDOMAIN_TRUST_ACCOUNT |\
+ UF_WORKSTATION_TRUST_ACCOUNT |\
+ UF_SERVER_TRUST_ACCOUNT \
+ )
+
+#define UF_SETTABLE_BITS (\
+ UF_SCRIPT |\
+ UF_ACCOUNTDISABLE |\
+ UF_HOMEDIR_REQUIRED |\
+ UF_LOCKOUT |\
+ UF_PASSWD_NOTREQD |\
+ UF_PASSWD_CANT_CHANGE |\
+ UF_ACCOUNT_TYPE_MASK | \
+ UF_DONT_EXPIRE_PASSWD | \
+ UF_MNS_LOGON_ACCOUNT |\
+ UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED |\
+ UF_SMARTCARD_REQUIRED |\
+ UF_TRUSTED_FOR_DELEGATION |\
+ UF_NOT_DELEGATED |\
+ UF_USE_DES_KEY_ONLY |\
+ UF_DONT_REQUIRE_PREAUTH \
+ )
+
+/* sAMAccountType */
+#define ATYPE_NORMAL_ACCOUNT 0x30000000 /* 805306368 */
+#define ATYPE_WORKSTATION_TRUST 0x30000001 /* 805306369 */
+#define ATYPE_INTERDOMAIN_TRUST 0x30000002 /* 805306370 */
+#define ATYPE_SECURITY_GLOBAL_GROUP 0x10000000 /* 268435456 */
+#define ATYPE_DISTRIBUTION_GLOBAL_GROUP 0x10000001 /* 268435457 */
+#define ATYPE_DISTRIBUTION_UNIVERSAL_GROUP ATYPE_DISTRIBUTION_GLOBAL_GROUP
+#define ATYPE_SECURITY_LOCAL_GROUP 0x20000000 /* 536870912 */
+#define ATYPE_DISTRIBUTION_LOCAL_GROUP 0x20000001 /* 536870913 */
+
+#define ATYPE_ACCOUNT ATYPE_NORMAL_ACCOUNT /* 0x30000000 805306368 */
+#define ATYPE_GLOBAL_GROUP ATYPE_SECURITY_GLOBAL_GROUP /* 0x10000000 268435456 */
+#define ATYPE_LOCAL_GROUP ATYPE_SECURITY_LOCAL_GROUP /* 0x20000000 536870912 */
+
+/* groupType */
+#define GTYPE_SECURITY_BUILTIN_LOCAL_GROUP 0x80000005 /* -2147483643 */
+#define GTYPE_SECURITY_DOMAIN_LOCAL_GROUP 0x80000004 /* -2147483644 */
+#define GTYPE_SECURITY_GLOBAL_GROUP 0x80000002 /* -2147483646 */
+#define GTYPE_DISTRIBUTION_GLOBAL_GROUP 0x00000002 /* 2 */
+#define GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP 0x00000004 /* 4 */
+#define GTYPE_DISTRIBUTION_UNIVERSAL_GROUP 0x00000008 /* 8 */
+
+/* Mailslot or cldap getdcname response flags */
+#define ADS_PDC 0x00000001 /* DC is PDC */
+#define ADS_GC 0x00000004 /* DC is a GC of forest */
+#define ADS_LDAP 0x00000008 /* DC is an LDAP server */
+#define ADS_DS 0x00000010 /* DC supports DS */
+#define ADS_KDC 0x00000020 /* DC is running KDC */
+#define ADS_TIMESERV 0x00000040 /* DC is running time services */
+#define ADS_CLOSEST 0x00000080 /* DC is closest to client */
+#define ADS_WRITABLE 0x00000100 /* DC has writable DS */
+#define ADS_GOOD_TIMESERV 0x00000200 /* DC has hardware clock
+ (and running time) */
+#define ADS_NDNC 0x00000400 /* DomainName is non-domain NC serviced
+ by LDAP server */
+#define ADS_PINGS 0x0000FFFF /* Ping response */
+#define ADS_DNS_CONTROLLER 0x20000000 /* DomainControllerName is a DNS name*/
+#define ADS_DNS_DOMAIN 0x40000000 /* DomainName is a DNS name */
+#define ADS_DNS_FOREST 0x80000000 /* DnsForestName is a DNS name */
+
+/* DomainCntrollerAddressType */
+#define ADS_INET_ADDRESS 0x00000001
+#define ADS_NETBIOS_ADDRESS 0x00000002
+
+
+/* ads auth control flags */
+#define ADS_AUTH_DISABLE_KERBEROS 0x01
+#define ADS_AUTH_NO_BIND 0x02
+#define ADS_AUTH_ANON_BIND 0x04
+#define ADS_AUTH_SIMPLE_BIND 0x08
+#define ADS_AUTH_ALLOW_NTLMSSP 0x10
+
+/* Kerberos environment variable names */
+#define KRB5_ENV_CCNAME "KRB5CCNAME"
+
+/* Heimdal uses a slightly different name */
+#if defined(HAVE_ENCTYPE_ARCFOUR_HMAC_MD5)
+#define ENCTYPE_ARCFOUR_HMAC ENCTYPE_ARCFOUR_HMAC_MD5
+#endif
+
+/* The older versions of heimdal that don't have this
+ define don't seem to use it anyway. I'm told they
+ always use a subkey */
+#ifndef HAVE_AP_OPTS_USE_SUBKEY
+#define AP_OPTS_USE_SUBKEY 0
+#endif
diff --git a/source/include/adt_tree.h b/source/include/adt_tree.h
new file mode 100644
index 00000000000..12e2ea5cc53
--- /dev/null
+++ b/source/include/adt_tree.h
@@ -0,0 +1,38 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Generic Abstract Data Types
+ * Copyright (C) Gerald Carter 2002.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef ADT_TREE_H
+#define ADT_TREE_H
+
+typedef struct _tree_node {
+ struct _tree_node *parent;
+ struct _tree_node **children;
+ int num_children;
+ char *key;
+ void *data_p;
+} TREE_NODE;
+
+typedef struct _tree_root {
+ TREE_NODE *root;
+ int (*compare)(void* x, void *y);
+ void (*free_func)(void *p);
+} SORTED_TREE;
+
+#endif
diff --git a/source/include/asn_1.h b/source/include/asn_1.h
new file mode 100644
index 00000000000..796c8bb7404
--- /dev/null
+++ b/source/include/asn_1.h
@@ -0,0 +1,69 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple ASN1 code
+ Copyright (C) Andrew Tridgell 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _ASN_1_H
+#define _ASN_1_H
+
+struct nesting {
+ off_t start;
+ size_t taglen; /* for parsing */
+ struct nesting *next;
+};
+
+typedef struct {
+ uint8 *data;
+ size_t length;
+ off_t ofs;
+ struct nesting *nesting;
+ BOOL has_error;
+} ASN1_DATA;
+
+
+#define ASN1_APPLICATION(x) ((x)+0x60)
+#define ASN1_SEQUENCE(x) ((x)+0x30)
+#define ASN1_CONTEXT(x) ((x)+0xa0)
+#define ASN1_GENERAL_STRING 0x1b
+#define ASN1_OCTET_STRING 0x4
+#define ASN1_OID 0x6
+#define ASN1_BOOLEAN 0x1
+#define ASN1_INTEGER 0x2
+#define ASN1_ENUMERATED 0xa
+#define ASN1_SET 0x31
+
+#define ASN1_MAX_OIDS 20
+
+/* some well known object IDs */
+#define OID_SPNEGO "1 3 6 1 5 5 2"
+#define OID_NTLMSSP "1 3 6 1 4 1 311 2 2 10"
+#define OID_KERBEROS5_OLD "1 2 840 48018 1 2 2"
+#define OID_KERBEROS5 "1 2 840 113554 1 2 2"
+
+#define SPNEGO_NEG_RESULT_ACCEPT 0
+#define SPNEGO_NEG_RESULT_INCOMPLETE 1
+#define SPNEGO_NEG_RESULT_REJECT 2
+
+/* not really ASN.1, but RFC 1964 */
+#define TOK_ID_KRB_AP_REQ (uchar*)"\x01\x00"
+#define TOK_ID_KRB_AP_REP (uchar*)"\x02\x00"
+#define TOK_ID_KRB_ERROR (uchar*)"\x03\x00"
+#define TOK_ID_GSS_GETMIC (uchar*)"\x01\x01"
+#define TOK_ID_GSS_WRAP (uchar*)"\x02\x01"
+
+#endif /* _ASN_1_H */
diff --git a/source/include/auth.h b/source/include/auth.h
new file mode 100644
index 00000000000..27cdc1e3f5f
--- /dev/null
+++ b/source/include/auth.h
@@ -0,0 +1,172 @@
+#ifndef _SMBAUTH_H_
+#define _SMBAUTH_H_
+/*
+ Unix SMB/CIFS implementation.
+ Standardised Authentication types
+ Copyright (C) Andrew Bartlett 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* AUTH_STR - string */
+typedef struct normal_string
+{
+ int len;
+ char *str;
+} AUTH_STR;
+
+/* AUTH_UNISTR - unicode string or buffer */
+typedef struct unicode_string
+{
+ int len;
+ uchar *unistr;
+} AUTH_UNISTR;
+
+typedef struct interactive_password
+{
+ OWF_INFO lm_owf; /* LM OWF Password */
+ OWF_INFO nt_owf; /* NT OWF Password */
+} auth_interactive_password;
+
+#define AUTH_FLAG_NONE 0x000000
+#define AUTH_FLAG_PLAINTEXT 0x000001
+#define AUTH_FLAG_LM_RESP 0x000002
+#define AUTH_FLAG_NTLM_RESP 0x000004
+#define AUTH_FLAG_NTLMv2_RESP 0x000008
+
+typedef struct auth_usersupplied_info
+{
+
+ DATA_BLOB lm_resp;
+ DATA_BLOB nt_resp;
+ auth_interactive_password * interactive_password;
+ DATA_BLOB plaintext_password;
+
+ BOOL encrypted;
+
+ uint32 auth_flags;
+
+ AUTH_STR client_domain; /* domain name string */
+ AUTH_STR domain; /* domain name after mapping */
+ AUTH_STR internal_username; /* username after mapping */
+ AUTH_STR smb_name; /* username before mapping */
+ AUTH_STR wksta_name; /* workstation name (netbios calling name) unicode string */
+
+} auth_usersupplied_info;
+
+#define SAM_FILL_NAME 0x01
+#define SAM_FILL_INFO3 0x02
+#define SAM_FILL_SAM 0x04
+#define SAM_FILL_UNIX 0x08
+#define SAM_FILL_ALL (SAM_FILL_NAME | SAM_FILL_INFO3 | SAM_FILL_SAM | SAM_FILL_UNIX)
+
+typedef struct auth_serversupplied_info
+{
+ BOOL guest;
+
+ uid_t uid;
+ gid_t gid;
+
+ /* This groups info is needed for when we become_user() for this uid */
+ int n_groups;
+ gid_t *groups;
+
+ /* NT group information taken from the info3 structure */
+
+ NT_USER_TOKEN *ptok;
+ PRIVILEGE_SET *privs;
+
+ DATA_BLOB nt_session_key;
+ DATA_BLOB lm_session_key;
+
+ uint32 sam_fill_level; /* How far is this structure filled? */
+
+ SAM_ACCOUNT *sam_account;
+
+ void *pam_handle;
+
+ char *unix_name;
+
+} auth_serversupplied_info;
+
+struct auth_context {
+ DATA_BLOB challenge;
+
+ /* Who set this up in the first place? */
+ const char *challenge_set_by;
+
+ BOOL challenge_may_be_modified;
+
+ struct auth_methods *challenge_set_method;
+ /* What order are the various methods in? Try to stop it changing under us */
+ struct auth_methods *auth_method_list;
+
+ TALLOC_CTX *mem_ctx;
+ const uint8 *(*get_ntlm_challenge)(struct auth_context *auth_context);
+ NTSTATUS (*check_ntlm_password)(const struct auth_context *auth_context,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **server_info);
+ NTSTATUS (*nt_status_squash)(NTSTATUS nt_status);
+ void (*free)(struct auth_context **auth_context);
+};
+
+typedef struct auth_methods
+{
+ struct auth_methods *prev, *next;
+ const char *name; /* What name got this module */
+
+ NTSTATUS (*auth)(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info);
+
+ DATA_BLOB (*get_chal)(const struct auth_context *auth_context,
+ void **my_private_data,
+ TALLOC_CTX *mem_ctx);
+
+ /* Used to keep tabs on things like the cli for SMB server authentication */
+ void *private_data;
+
+ /* Function to clean up the above arbitary structure */
+ void (*free_private_data)(void **private_data);
+
+ /* Function to send a keepalive message on the above structure */
+ void (*send_keepalive)(void **private_data);
+
+} auth_methods;
+
+typedef NTSTATUS (*auth_init_function)(struct auth_context *, const char *, struct auth_methods **);
+
+struct auth_init_function_entry {
+ const char *name;
+ /* Function to create a member of the authmethods list */
+
+ auth_init_function init;
+
+ struct auth_init_function_entry *prev, *next;
+};
+
+typedef struct auth_ntlmssp_state
+{
+ TALLOC_CTX *mem_ctx;
+ struct auth_context *auth_context;
+ struct auth_serversupplied_info *server_info;
+ struct ntlmssp_state *ntlmssp_state;
+} AUTH_NTLMSSP_STATE;
+
+#define AUTH_INTERFACE_VERSION 1
+
+#endif /* _SMBAUTH_H_ */
diff --git a/source/include/authdata.h b/source/include/authdata.h
new file mode 100644
index 00000000000..9d80745fb06
--- /dev/null
+++ b/source/include/authdata.h
@@ -0,0 +1,152 @@
+/*
+ Unix SMB/CIFS implementation.
+ Kerberos authorization data
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _AUTHDATA_H
+#define _AUTHDATA_H
+
+#include "rpc_misc.h"
+
+#define PAC_TYPE_LOGON_INFO 1
+#define PAC_TYPE_SERVER_CHECKSUM 6
+#define PAC_TYPE_PRIVSVR_CHECKSUM 7
+#define PAC_TYPE_UNKNOWN_10 10
+
+typedef struct unknown_type_10 {
+ NTTIME unknown_time;
+ uint16 len;
+ uint16 *username; /* might not be null terminated, so not UNISTR */
+} UNKNOWN_TYPE_10;
+
+typedef struct pac_signature_data {
+ uint32 type;
+ uint8 *signature;
+} PAC_SIGNATURE_DATA;
+
+typedef struct group_membership {
+ uint32 rid;
+ uint32 attrs;
+} GROUP_MEMBERSHIP;
+
+typedef struct group_membership_array {
+ uint32 count;
+ GROUP_MEMBERSHIP *group_membership;
+} GROUP_MEMBERSHIP_ARRAY;
+
+typedef struct krb_sid_and_attrs {
+ uint32 sid_ptr;
+ uint32 attrs;
+ DOM_SID2 *sid;
+} KRB_SID_AND_ATTRS;
+
+typedef struct krb_sid_and_attr_array {
+ uint32 count;
+ KRB_SID_AND_ATTRS *krb_sid_and_attrs;
+} KRB_SID_AND_ATTR_ARRAY;
+
+
+/* This is awfully similar to a samr_user_info_23, but not identical.
+ Many of the field names have been swiped from there, because it is
+ so similar that they are likely the same, but many have been verified.
+ Some are in a different order, though... */
+typedef struct pac_logon_info {
+ NTTIME logon_time; /* logon time */
+ NTTIME logoff_time; /* logoff time */
+ NTTIME kickoff_time; /* kickoff time */
+ NTTIME pass_last_set_time; /* password last set time */
+ NTTIME pass_can_change_time; /* password can change time */
+ NTTIME pass_must_change_time; /* password must change time */
+
+ UNIHDR hdr_user_name; /* user name unicode string header */
+ UNIHDR hdr_full_name; /* user's full name unicode string header */
+ UNIHDR hdr_logon_script; /* these last 4 appear to be in a different */
+ UNIHDR hdr_profile_path; /* order than in the info23 */
+ UNIHDR hdr_home_dir;
+ UNIHDR hdr_dir_drive;
+
+ uint16 logon_count; /* number of times user has logged onto domain */
+ uint16 reserved12;
+
+ uint32 user_rid;
+ uint32 group_rid;
+ uint32 group_count;
+ uint32 group_membership_ptr;
+ uint32 user_flags;
+
+ uint32 reserved13[4];
+ UNIHDR hdr_dom_controller;
+ UNIHDR hdr_dom_name;
+
+ uint32 ptr_dom_sid;
+
+ uint32 reserved16[2];
+ uint32 reserved17; /* looks like it may be acb_info */
+ uint32 reserved18[7];
+
+ uint32 sid_count;
+ uint32 ptr_extra_sids;
+
+ uint32 ptr_res_group_dom_sid;
+ uint32 res_group_count;
+ uint32 ptr_res_groups;
+
+ UNISTR2 uni_user_name; /* user name unicode string header */
+ UNISTR2 uni_full_name; /* user's full name unicode string header */
+ UNISTR2 uni_logon_script; /* these last 4 appear to be in a different*/
+ UNISTR2 uni_profile_path; /* order than in the info23 */
+ UNISTR2 uni_home_dir;
+ UNISTR2 uni_dir_drive;
+ UNISTR2 uni_dom_controller;
+ UNISTR2 uni_dom_name;
+ DOM_SID2 dom_sid;
+ GROUP_MEMBERSHIP_ARRAY groups;
+ KRB_SID_AND_ATTR_ARRAY extra_sids;
+ DOM_SID2 res_group_dom_sid;
+ GROUP_MEMBERSHIP_ARRAY res_groups;
+
+} PAC_LOGON_INFO;
+
+typedef struct pac_info_ctr
+{
+ union
+ {
+ PAC_LOGON_INFO *logon_info;
+ PAC_SIGNATURE_DATA *srv_cksum;
+ PAC_SIGNATURE_DATA *privsrv_cksum;
+ UNKNOWN_TYPE_10 *type_10;
+ } pac;
+} PAC_INFO_CTR;
+
+typedef struct pac_info_hdr {
+ uint32 type;
+ uint32 size;
+ uint32 offset;
+ uint32 offsethi;
+ PAC_INFO_CTR *ctr;
+} PAC_INFO_HDR;
+
+typedef struct pac_data {
+ uint32 num_buffers;
+ uint32 version;
+ PAC_INFO_HDR *pac_info_hdr_ptr;
+} PAC_DATA;
+
+
+#endif
diff --git a/source/include/byteorder.h b/source/include/byteorder.h
new file mode 100644
index 00000000000..0eef5573066
--- /dev/null
+++ b/source/include/byteorder.h
@@ -0,0 +1,171 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB Byte handling
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _BYTEORDER_H
+#define _BYTEORDER_H
+
+/*
+ This file implements macros for machine independent short and
+ int manipulation
+
+Here is a description of this file that I emailed to the samba list once:
+
+> I am confused about the way that byteorder.h works in Samba. I have
+> looked at it, and I would have thought that you might make a distinction
+> between LE and BE machines, but you only seem to distinguish between 386
+> and all other architectures.
+>
+> Can you give me a clue?
+
+sure.
+
+The distinction between 386 and other architectures is only there as
+an optimisation. You can take it out completely and it will make no
+difference. The routines (macros) in byteorder.h are totally byteorder
+independent. The 386 optimsation just takes advantage of the fact that
+the x86 processors don't care about alignment, so we don't have to
+align ints on int boundaries etc. If there are other processors out
+there that aren't alignment sensitive then you could also define
+CAREFUL_ALIGNMENT=0 on those processors as well.
+
+Ok, now to the macros themselves. I'll take a simple example, say we
+want to extract a 2 byte integer from a SMB packet and put it into a
+type called uint16 that is in the local machines byte order, and you
+want to do it with only the assumption that uint16 is _at_least_ 16
+bits long (this last condition is very important for architectures
+that don't have any int types that are 2 bytes long)
+
+You do this:
+
+#define CVAL(buf,pos) (((unsigned char *)(buf))[pos])
+#define PVAL(buf,pos) ((unsigned)CVAL(buf,pos))
+#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
+
+then to extract a uint16 value at offset 25 in a buffer you do this:
+
+char *buffer = foo_bar();
+uint16 xx = SVAL(buffer,25);
+
+We are using the byteoder independence of the ANSI C bitshifts to do
+the work. A good optimising compiler should turn this into efficient
+code, especially if it happens to have the right byteorder :-)
+
+I know these macros can be made a bit tidier by removing some of the
+casts, but you need to look at byteorder.h as a whole to see the
+reasoning behind them. byteorder.h defines the following macros:
+
+SVAL(buf,pos) - extract a 2 byte SMB value
+IVAL(buf,pos) - extract a 4 byte SMB value
+SVALS(buf,pos) signed version of SVAL()
+IVALS(buf,pos) signed version of IVAL()
+
+SSVAL(buf,pos,val) - put a 2 byte SMB value into a buffer
+SIVAL(buf,pos,val) - put a 4 byte SMB value into a buffer
+SSVALS(buf,pos,val) - signed version of SSVAL()
+SIVALS(buf,pos,val) - signed version of SIVAL()
+
+RSVAL(buf,pos) - like SVAL() but for NMB byte ordering
+RSVALS(buf,pos) - like SVALS() but for NMB byte ordering
+RIVAL(buf,pos) - like IVAL() but for NMB byte ordering
+RIVALS(buf,pos) - like IVALS() but for NMB byte ordering
+RSSVAL(buf,pos,val) - like SSVAL() but for NMB ordering
+RSIVAL(buf,pos,val) - like SIVAL() but for NMB ordering
+RSIVALS(buf,pos,val) - like SIVALS() but for NMB ordering
+
+it also defines lots of intermediate macros, just ignore those :-)
+
+*/
+
+#undef CAREFUL_ALIGNMENT
+
+/* we know that the 386 can handle misalignment and has the "right"
+ byteorder */
+#ifdef __i386__
+#define CAREFUL_ALIGNMENT 0
+#endif
+
+#ifndef CAREFUL_ALIGNMENT
+#define CAREFUL_ALIGNMENT 1
+#endif
+
+#define CVAL(buf,pos) ((unsigned)(((const unsigned char *)(buf))[pos]))
+#define CVAL_NC(buf,pos) (((unsigned char *)(buf))[pos]) /* Non-const version of CVAL */
+#define PVAL(buf,pos) (CVAL(buf,pos))
+#define SCVAL(buf,pos,val) (CVAL_NC(buf,pos) = (val))
+
+
+#if CAREFUL_ALIGNMENT
+
+#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
+#define IVAL(buf,pos) (SVAL(buf,pos)|SVAL(buf,(pos)+2)<<16)
+#define SSVALX(buf,pos,val) (CVAL_NC(buf,pos)=(unsigned char)((val)&0xFF),CVAL_NC(buf,pos+1)=(unsigned char)((val)>>8))
+#define SIVALX(buf,pos,val) (SSVALX(buf,pos,val&0xFFFF),SSVALX(buf,pos+2,val>>16))
+#define SVALS(buf,pos) ((int16)SVAL(buf,pos))
+#define IVALS(buf,pos) ((int32)IVAL(buf,pos))
+#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((uint16)(val)))
+#define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32)(val)))
+#define SSVALS(buf,pos,val) SSVALX((buf),(pos),((int16)(val)))
+#define SIVALS(buf,pos,val) SIVALX((buf),(pos),((int32)(val)))
+
+#else /* CAREFUL_ALIGNMENT */
+
+/* this handles things for architectures like the 386 that can handle
+ alignment errors */
+/*
+ WARNING: This section is dependent on the length of int16 and int32
+ being correct
+*/
+
+/* get single value from an SMB buffer */
+#define SVAL(buf,pos) (*(const uint16 *)((const char *)(buf) + (pos)))
+#define SVAL_NC(buf,pos) (*(uint16 *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define IVAL(buf,pos) (*(const uint32 *)((const char *)(buf) + (pos)))
+#define IVAL_NC(buf,pos) (*(uint32 *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define SVALS(buf,pos) (*(const int16 *)((const char *)(buf) + (pos)))
+#define SVALS_NC(buf,pos) (*(int16 *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define IVALS(buf,pos) (*(const int32 *)((const char *)(buf) + (pos)))
+#define IVALS_NC(buf,pos) (*(int32 *)((char *)(buf) + (pos))) /* Non const version of above. */
+
+/* store single value in an SMB buffer */
+#define SSVAL(buf,pos,val) SVAL_NC(buf,pos)=((uint16)(val))
+#define SIVAL(buf,pos,val) IVAL_NC(buf,pos)=((uint32)(val))
+#define SSVALS(buf,pos,val) SVALS_NC(buf,pos)=((int16)(val))
+#define SIVALS(buf,pos,val) IVALS_NC(buf,pos)=((int32)(val))
+
+#endif /* CAREFUL_ALIGNMENT */
+
+/* now the reverse routines - these are used in nmb packets (mostly) */
+#define SREV(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF))
+#define IREV(x) ((SREV(x)<<16) | (SREV((x)>>16)))
+
+#define RSVAL(buf,pos) SREV(SVAL(buf,pos))
+#define RSVALS(buf,pos) SREV(SVALS(buf,pos))
+#define RIVAL(buf,pos) IREV(IVAL(buf,pos))
+#define RIVALS(buf,pos) IREV(IVALS(buf,pos))
+#define RSSVAL(buf,pos,val) SSVAL(buf,pos,SREV(val))
+#define RSSVALS(buf,pos,val) SSVALS(buf,pos,SREV(val))
+#define RSIVAL(buf,pos,val) SIVAL(buf,pos,IREV(val))
+#define RSIVALS(buf,pos,val) SIVALS(buf,pos,IREV(val))
+
+/* Alignment macros. */
+#define ALIGN4(p,base) ((p) + ((4 - (PTR_DIFF((p), (base)) & 3)) & 3))
+#define ALIGN2(p,base) ((p) + ((2 - (PTR_DIFF((p), (base)) & 1)) & 1))
+
+#endif /* _BYTEORDER_H */
diff --git a/source/include/charset.h b/source/include/charset.h
new file mode 100644
index 00000000000..7a9b12ef55d
--- /dev/null
+++ b/source/include/charset.h
@@ -0,0 +1,127 @@
+/*
+ Unix SMB/CIFS implementation.
+ charset defines
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Jelmer Vernooij 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* this defines the charset types used in samba */
+typedef enum {CH_UCS2=0, CH_UNIX=1, CH_DISPLAY=2, CH_DOS=3, CH_UTF8=4} charset_t;
+
+#define NUM_CHARSETS 5
+
+/*
+ * for each charset we have a function that pushes from that charset to a ucs2
+ * buffer, and a function that pulls from ucs2 buffer to that charset.
+ * */
+
+struct charset_functions {
+ const char *name;
+ size_t (*pull)(void *, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft);
+ size_t (*push)(void *, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft);
+ struct charset_functions *prev, *next;
+};
+
+/*
+ * This is auxiliary struct used by source/script/gen-8-bit-gap.sh script
+ * during generation of an encoding table for charset module
+ * */
+
+struct charset_gap_table {
+ uint16 start;
+ uint16 end;
+ int32 idx;
+};
+
+/*
+ * Define stub for charset module which implements 8-bit encoding with gaps.
+ * Encoding tables for such module should be produced from glibc's CHARMAPs
+ * using script source/script/gen-8bit-gap.sh
+ * CHARSETNAME is CAPITALIZED charset name
+ *
+ * */
+#define SMB_GENERATE_CHARSET_MODULE_8_BIT_GAP(CHARSETNAME) \
+static size_t CHARSETNAME ## _push(void *cd, char **inbuf, size_t *inbytesleft, \
+ char **outbuf, size_t *outbytesleft) \
+{ \
+ while (*inbytesleft >= 2 && *outbytesleft >= 1) { \
+ int i; \
+ int done = 0; \
+ \
+ uint16 ch = SVAL(*inbuf,0); \
+ \
+ for (i=0; from_idx[i].start != 0xffff; i++) { \
+ if ((from_idx[i].start <= ch) && (from_idx[i].end >= ch)) { \
+ ((unsigned char*)(*outbuf))[0] = from_ucs2[from_idx[i].idx+ch]; \
+ (*inbytesleft) -= 2; \
+ (*outbytesleft) -= 1; \
+ (*inbuf) += 2; \
+ (*outbuf) += 1; \
+ done = 1; \
+ break; \
+ } \
+ } \
+ if (!done) { \
+ errno = EINVAL; \
+ return -1; \
+ } \
+ \
+ } \
+ \
+ if (*inbytesleft == 1) { \
+ errno = EINVAL; \
+ return -1; \
+ } \
+ \
+ if (*inbytesleft > 1) { \
+ errno = E2BIG; \
+ return -1; \
+ } \
+ \
+ return 0; \
+} \
+ \
+static size_t CHARSETNAME ## _pull(void *cd, char **inbuf, size_t *inbytesleft, \
+ char **outbuf, size_t *outbytesleft) \
+{ \
+ while (*inbytesleft >= 1 && *outbytesleft >= 2) { \
+ *(uint16*)(*outbuf) = to_ucs2[((unsigned char*)(*inbuf))[0]]; \
+ (*inbytesleft) -= 1; \
+ (*outbytesleft) -= 2; \
+ (*inbuf) += 1; \
+ (*outbuf) += 2; \
+ } \
+ \
+ if (*inbytesleft > 0) { \
+ errno = E2BIG; \
+ return -1; \
+ } \
+ \
+ return 0; \
+} \
+ \
+struct charset_functions CHARSETNAME ## _functions = \
+ {#CHARSETNAME, CHARSETNAME ## _pull, CHARSETNAME ## _push}; \
+ \
+NTSTATUS charset_ ## CHARSETNAME ## _init(void) \
+{ \
+ return smb_register_charset(& CHARSETNAME ## _functions); \
+} \
+
+
diff --git a/source/include/client.h b/source/include/client.h
new file mode 100644
index 00000000000..968b73f0b41
--- /dev/null
+++ b/source/include/client.h
@@ -0,0 +1,169 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+ Copyright (C) Jeremy Allison 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _CLIENT_H
+#define _CLIENT_H
+
+/* the client asks for a smaller buffer to save ram and also to get more
+ overlap on the wire. This size gives us a nice read/write size, which
+ will be a multiple of the page size on almost any system */
+#define CLI_BUFFER_SIZE (0xFFFF)
+
+
+/*
+ * These definitions depend on smb.h
+ */
+
+typedef struct file_info
+{
+ SMB_BIG_UINT size;
+ uint16 mode;
+ uid_t uid;
+ gid_t gid;
+ /* these times are normally kept in GMT */
+ time_t mtime;
+ time_t atime;
+ time_t ctime;
+ pstring name;
+ char short_name[13*3]; /* the *3 is to cope with multi-byte */
+} file_info;
+
+struct print_job_info
+{
+ uint16 id;
+ uint16 priority;
+ size_t size;
+ fstring user;
+ fstring name;
+ time_t t;
+};
+
+struct cli_state {
+ int port;
+ int fd;
+ int smb_rw_error; /* Copy of last read or write error. */
+ uint16 cnum;
+ uint16 pid;
+ uint16 mid;
+ uint16 vuid;
+ int protocol;
+ int sec_mode;
+ int rap_error;
+ int privileges;
+
+ fstring desthost;
+ fstring user_name;
+ fstring domain;
+
+ /*
+ * The following strings are the
+ * ones returned by the server if
+ * the protocol > NT1.
+ */
+ fstring server_type;
+ fstring server_os;
+ fstring server_domain;
+
+ fstring share;
+ fstring dev;
+ struct nmb_name called;
+ struct nmb_name calling;
+ fstring full_dest_host_name;
+ struct in_addr dest_ip;
+
+ struct pwd_info pwd;
+ DATA_BLOB secblob; /* cryptkey or negTokenInit */
+ uint32 sesskey;
+ int serverzone;
+ uint32 servertime;
+ int readbraw_supported;
+ int writebraw_supported;
+ int timeout; /* in milliseconds. */
+ size_t max_xmit;
+ size_t max_mux;
+ char *outbuf;
+ char *inbuf;
+ unsigned int bufsize;
+ int initialised;
+ int win95;
+ uint32 capabilities;
+
+ TALLOC_CTX *mem_ctx;
+
+ smb_sign_info sign_info;
+
+ /* the session key for this CLI, outside
+ any per-pipe authenticaion */
+ DATA_BLOB user_session_key;
+
+ /*
+ * Only used in NT domain calls.
+ */
+
+ int pipe_idx; /* Index (into list of known pipes)
+ of the pipe we're talking to,
+ if any */
+
+ uint16 nt_pipe_fnum; /* Pipe handle. */
+
+ /* Secure pipe parameters */
+ int pipe_auth_flags;
+
+ uint16 saved_netlogon_pipe_fnum; /* The "first" pipe to get
+ the session key for the
+ schannel. */
+ struct netsec_auth_struct auth_info;
+
+ NTLMSSP_STATE *ntlmssp_pipe_state;
+
+ unsigned char sess_key[16]; /* Current session key. */
+ DOM_CRED clnt_cred; /* Client credential. */
+ fstring mach_acct; /* MYNAME$. */
+ fstring srv_name_slash; /* \\remote server. */
+ fstring clnt_name_slash; /* \\local client. */
+ uint16 max_xmit_frag;
+ uint16 max_recv_frag;
+
+ BOOL use_kerberos;
+ BOOL use_spnego;
+
+ BOOL use_oplocks; /* should we use oplocks? */
+ BOOL use_level_II_oplocks; /* should we use level II oplocks? */
+
+ /* a oplock break request handler */
+ BOOL (*oplock_handler)(struct cli_state *cli, int fnum, unsigned char level);
+
+ BOOL force_dos_errors;
+
+ /* was this structure allocated by cli_initialise? If so, then
+ free in cli_shutdown() */
+ BOOL allocated;
+
+ /* Name of the pipe we're talking to, if any */
+ fstring pipe_name;
+};
+
+#define CLI_FULL_CONNECTION_DONT_SPNEGO 0x0001
+#define CLI_FULL_CONNECTION_USE_KERBEROS 0x0002
+#define CLI_FULL_CONNECTION_ANNONYMOUS_FALLBACK 0x0004
+
+#endif /* _CLIENT_H */
diff --git a/source/include/clitar.h b/source/include/clitar.h
new file mode 100644
index 00000000000..b7731172d61
--- /dev/null
+++ b/source/include/clitar.h
@@ -0,0 +1,41 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * clitar file format
+ * Copyright (C) Andrew Tridgell 2000
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 675
+ * Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _CLITAR_H
+#define _CLITAR_H
+
+#define TBLOCK 512
+#define NAMSIZ 100
+union hblock {
+ char dummy[TBLOCK];
+ struct header {
+ char name[NAMSIZ];
+ char mode[8];
+ char uid[8];
+ char gid[8];
+ char size[12];
+ char mtime[12];
+ char chksum[8];
+ char linkflag;
+ char linkname[NAMSIZ];
+ } dbuf;
+};
+
+#endif /* _CLITAR_H */
diff --git a/source/include/debug.h b/source/include/debug.h
new file mode 100644
index 00000000000..55f07d8a18e
--- /dev/null
+++ b/source/include/debug.h
@@ -0,0 +1,201 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB debug stuff
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) John H Terpstra 1996-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+ Copyright (C) Paul Ashton 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _DEBUG_H
+#define _DEBUG_H
+
+/* -------------------------------------------------------------------------- **
+ * Debugging code. See also debug.c
+ */
+
+/* mkproto.awk has trouble with ifdef'd function definitions (it ignores
+ * the #ifdef directive and will read both definitions, thus creating two
+ * diffferent prototype declarations), so we must do these by hand.
+ */
+/* I know the __attribute__ stuff is ugly, but it does ensure we get the
+ arguemnts to DEBUG() right. We have got them wrong too often in the
+ past.
+ The PRINTFLIKE comment does the equivalent for SGI MIPSPro.
+ */
+/* PRINTFLIKE1 */
+int Debug1( const char *, ... ) PRINTF_ATTRIBUTE(1,2);
+/* PRINTFLIKE1 */
+BOOL dbgtext( const char *, ... ) PRINTF_ATTRIBUTE(1,2);
+BOOL dbghdr( int level, const char *file, const char *func, int line );
+
+extern XFILE *dbf;
+extern pstring debugf;
+
+/* If we have these macros, we can add additional info to the header. */
+
+#ifdef HAVE_FUNCTION_MACRO
+#define FUNCTION_MACRO (__FUNCTION__)
+#else
+#define FUNCTION_MACRO ("")
+#endif
+
+/*
+ * Redefine DEBUGLEVEL because so we don't have to change every source file
+ * that *unnecessarily* references it. Source files neeed not extern reference
+ * DEBUGLEVEL, as it's extern in includes.h (which all source files include).
+ * Eventually, all these references should be removed, and all references to
+ * DEBUGLEVEL should be references to DEBUGLEVEL_CLASS[DBGC_ALL]. This could
+ * still be through a macro still called DEBUGLEVEL. This cannot be done now
+ * because some references would expand incorrectly.
+ */
+#define DEBUGLEVEL *debug_level
+extern int DEBUGLEVEL;
+
+/*
+ * Define all new debug classes here. A class is represented by an entry in
+ * the DEBUGLEVEL_CLASS array. Index zero of this arrray is equivalent to the
+ * old DEBUGLEVEL. Any source file that does NOT add the following lines:
+ *
+ * #undef DBGC_CLASS
+ * #define DBGC_CLASS DBGC_<your class name here>
+ *
+ * at the start of the file (after #include "includes.h") will default to
+ * using index zero, so it will behaive just like it always has.
+ */
+#define DBGC_ALL 0 /* index equivalent to DEBUGLEVEL */
+
+#define DBGC_TDB 1
+#define DBGC_PRINTDRIVERS 2
+#define DBGC_LANMAN 3
+#define DBGC_SMB 4
+#define DBGC_RPC_PARSE 5
+#define DBGC_RPC_SRV 6
+#define DBGC_RPC_CLI 7
+#define DBGC_PASSDB 8
+#define DBGC_SAM 9
+#define DBGC_AUTH 10
+#define DBGC_WINBIND 11
+#define DBGC_VFS 12
+#define DBGC_IDMAP 13
+#define DBGC_QUOTA 14
+
+/* So you can define DBGC_CLASS before including debug.h */
+#ifndef DBGC_CLASS
+#define DBGC_CLASS 0 /* override as shown above */
+#endif
+
+extern int *DEBUGLEVEL_CLASS;
+extern BOOL *DEBUGLEVEL_CLASS_ISSET;
+
+/* Debugging macros
+ *
+ * DEBUGLVL()
+ * If the 'file specific' debug class level >= level OR the system-wide
+ * DEBUGLEVEL (synomym for DEBUGLEVEL_CLASS[ DBGC_ALL ]) >= level then
+ * generate a header using the default macros for file, line, and
+ * function name. Returns True if the debug level was <= DEBUGLEVEL.
+ *
+ * Example: if( DEBUGLVL( 2 ) ) dbgtext( "Some text.\n" );
+ *
+ * DEBUGLVLC()
+ * If the 'macro specified' debug class level >= level OR the system-wide
+ * DEBUGLEVEL (synomym for DEBUGLEVEL_CLASS[ DBGC_ALL ]) >= level then
+ * generate a header using the default macros for file, line, and
+ * function name. Returns True if the debug level was <= DEBUGLEVEL.
+ *
+ * Example: if( DEBUGLVLC( DBGC_TDB, 2 ) ) dbgtext( "Some text.\n" );
+ *
+ * DEBUG()
+ * If the 'file specific' debug class level >= level OR the system-wide
+ * DEBUGLEVEL (synomym for DEBUGLEVEL_CLASS[ DBGC_ALL ]) >= level then
+ * generate a header using the default macros for file, line, and
+ * function name. Each call to DEBUG() generates a new header *unless* the
+ * previous debug output was unterminated (i.e. no '\n').
+ * See debug.c:dbghdr() for more info.
+ *
+ * Example: DEBUG( 2, ("Some text and a value %d.\n", value) );
+ *
+ * DEBUGC()
+ * If the 'macro specified' debug class level >= level OR the system-wide
+ * DEBUGLEVEL (synomym for DEBUGLEVEL_CLASS[ DBGC_ALL ]) >= level then
+ * generate a header using the default macros for file, line, and
+ * function name. Each call to DEBUG() generates a new header *unless* the
+ * previous debug output was unterminated (i.e. no '\n').
+ * See debug.c:dbghdr() for more info.
+ *
+ * Example: DEBUGC( DBGC_TDB, 2, ("Some text and a value %d.\n", value) );
+ *
+ * DEBUGADD(), DEBUGADDC()
+ * Same as DEBUG() and DEBUGC() except the text is appended to the previous
+ * DEBUG(), DEBUGC(), DEBUGADD(), DEBUGADDC() with out another interviening
+ * header.
+ *
+ * Example: DEBUGADD( 2, ("Some text and a value %d.\n", value) );
+ * DEBUGADDC( DBGC_TDB, 2, ("Some text and a value %d.\n", value) );
+ *
+ * Note: If the debug class has not be redeined (see above) then the optimizer
+ * will remove the extra conditional test.
+ */
+
+#define DEBUGLVL( level ) \
+ ( ((level) <= MAX_DEBUG_LEVEL) && \
+ ((DEBUGLEVEL_CLASS[ DBGC_CLASS ] >= (level))|| \
+ (!DEBUGLEVEL_CLASS_ISSET[ DBGC_CLASS ] && \
+ DEBUGLEVEL_CLASS[ DBGC_ALL ] >= (level)) ) \
+ && dbghdr( level, __FILE__, FUNCTION_MACRO, (__LINE__) ) )
+
+
+#define DEBUGLVLC( dbgc_class, level ) \
+ ( ((level) <= MAX_DEBUG_LEVEL) && \
+ ((DEBUGLEVEL_CLASS[ dbgc_class ] >= (level))|| \
+ (!DEBUGLEVEL_CLASS_ISSET[ dbgc_class ] && \
+ DEBUGLEVEL_CLASS[ DBGC_ALL ] >= (level)) ) \
+ && dbghdr( level, __FILE__, FUNCTION_MACRO, (__LINE__) ) )
+
+
+#define DEBUG( level, body ) \
+ (void)( ((level) <= MAX_DEBUG_LEVEL) && \
+ ((DEBUGLEVEL_CLASS[ DBGC_CLASS ] >= (level))|| \
+ (!DEBUGLEVEL_CLASS_ISSET[ DBGC_CLASS ] && \
+ DEBUGLEVEL_CLASS[ DBGC_ALL ] >= (level)) ) \
+ && (dbghdr( level, __FILE__, FUNCTION_MACRO, (__LINE__) )) \
+ && (dbgtext body) )
+
+#define DEBUGC( dbgc_class, level, body ) \
+ (void)( ((level) <= MAX_DEBUG_LEVEL) && \
+ ((DEBUGLEVEL_CLASS[ dbgc_class ] >= (level))|| \
+ (!DEBUGLEVEL_CLASS_ISSET[ dbgc_class ] && \
+ DEBUGLEVEL_CLASS[ DBGC_ALL ] >= (level)) ) \
+ && (dbghdr( level, __FILE__, FUNCTION_MACRO, (__LINE__) )) \
+ && (dbgtext body) )
+
+#define DEBUGADD( level, body ) \
+ (void)( ((level) <= MAX_DEBUG_LEVEL) && \
+ ((DEBUGLEVEL_CLASS[ DBGC_CLASS ] >= (level))|| \
+ (!DEBUGLEVEL_CLASS_ISSET[ DBGC_CLASS ] && \
+ DEBUGLEVEL_CLASS[ DBGC_ALL ] >= (level)) ) \
+ && (dbgtext body) )
+
+#define DEBUGADDC( dbgc_class, level, body ) \
+ (void)( ((level) <= MAX_DEBUG_LEVEL) && \
+ ((DEBUGLEVEL_CLASS[ dbgc_class ] >= (level))|| \
+ (!DEBUGLEVEL_CLASS_ISSET[ dbgc_class ] && \
+ DEBUGLEVEL_CLASS[ DBGC_ALL ] >= (level)) ) \
+ && (dbgtext body) )
+
+#endif
diff --git a/source/include/dlinklist.h b/source/include/dlinklist.h
new file mode 100644
index 00000000000..794aea75766
--- /dev/null
+++ b/source/include/dlinklist.h
@@ -0,0 +1,78 @@
+/*
+ Unix SMB/CIFS implementation.
+ some simple double linked list macros
+ Copyright (C) Andrew Tridgell 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* To use these macros you must have a structure containing a next and
+ prev pointer */
+
+
+/* hook into the front of the list */
+#define DLIST_ADD(list, p) \
+{ \
+ if (!(list)) { \
+ (list) = (p); \
+ (p)->next = (p)->prev = NULL; \
+ } else { \
+ (list)->prev = (p); \
+ (p)->next = (list); \
+ (p)->prev = NULL; \
+ (list) = (p); \
+ }\
+}
+
+/* remove an element from a list - element doesn't have to be in list. */
+#define DLIST_REMOVE(list, p) \
+{ \
+ if ((p) == (list)) { \
+ (list) = (p)->next; \
+ if (list) (list)->prev = NULL; \
+ } else { \
+ if ((p)->prev) (p)->prev->next = (p)->next; \
+ if ((p)->next) (p)->next->prev = (p)->prev; \
+ } \
+ if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \
+}
+
+/* promote an element to the top of the list */
+#define DLIST_PROMOTE(list, p) \
+{ \
+ DLIST_REMOVE(list, p) \
+ DLIST_ADD(list, p) \
+}
+
+/* hook into the end of the list - needs a tmp pointer */
+#define DLIST_ADD_END(list, p, tmp) \
+{ \
+ if (!(list)) { \
+ (list) = (p); \
+ (p)->next = (p)->prev = NULL; \
+ } else { \
+ for ((tmp) = (list); (tmp)->next; (tmp) = (tmp)->next) ; \
+ (tmp)->next = (p); \
+ (p)->next = NULL; \
+ (p)->prev = (tmp); \
+ } \
+}
+
+/* demote an element to the top of the list, needs a tmp pointer */
+#define DLIST_DEMOTE(list, p, tmp) \
+{ \
+ DLIST_REMOVE(list, p) \
+ DLIST_ADD_END(list, p, tmp) \
+}
diff --git a/source/include/doserr.h b/source/include/doserr.h
new file mode 100644
index 00000000000..576aeda2bf7
--- /dev/null
+++ b/source/include/doserr.h
@@ -0,0 +1,233 @@
+/*
+ Unix SMB/CIFS implementation.
+ DOS error code constants
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) John H Terpstra 1996-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Paul Ashton 1998-2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _DOSERR_H
+#define _DOSERR_H
+
+/* Error classes */
+
+#define ERRDOS 0x01 /* Error is from the core DOS operating system set. */
+#define ERRSRV 0x02 /* Error is generated by the server network file manager.*/
+#define ERRHRD 0x03 /* Error is an hardware error. */
+#define ERRCMD 0xFF /* Command was not in the "SMB" format. */
+
+/* SMB X/Open error codes for the ERRDOS error class */
+#define ERRsuccess 0 /* No error */
+#define ERRbadfunc 1 /* Invalid function (or system call) */
+#define ERRbadfile 2 /* File not found (pathname error) */
+#define ERRbadpath 3 /* Directory not found */
+#define ERRnofids 4 /* Too many open files */
+#define ERRnoaccess 5 /* Access denied */
+#define ERRbadfid 6 /* Invalid fid */
+#define ERRbadmcb 7 /* Memory control blocks destroyed. */
+#define ERRnomem 8 /* Out of memory */
+#define ERRbadmem 9 /* Invalid memory block address */
+#define ERRbadenv 10 /* Invalid environment */
+#define ERRbadaccess 12 /* Invalid open mode */
+#define ERRbaddata 13 /* Invalid data (only from ioctl call) */
+#define ERRres 14 /* reserved */
+#define ERRbaddrive 15 /* Invalid drive */
+#define ERRremcd 16 /* Attempt to delete current directory */
+#define ERRdiffdevice 17 /* rename/move across different filesystems */
+#define ERRnofiles 18 /* no more files found in file search */
+#define ERRgeneral 31 /* General failure */
+#define ERRbadshare 32 /* Share mode on file conflict with open mode */
+#define ERRlock 33 /* Lock request conflicts with existing lock */
+#define ERRunsup 50 /* Request unsupported, returned by Win 95, RJS 20Jun98 */
+#define ERRnetnamedel 64 /* Network name deleted or not available */
+#define ERRnosuchshare 67 /* You specified an invalid share name */
+#define ERRfilexists 80 /* File in operation already exists */
+#define ERRinvalidparam 87
+#define ERRcannotopen 110 /* Cannot open the file specified */
+#define ERRinsufficientbuffer 122
+#define ERRinvalidname 123 /* Invalid name */
+#define ERRunknownlevel 124
+#define ERRnotlocked 158 /* This region is not locked by this locking context. */
+#define ERRrename 183
+#define ERRbadpipe 230 /* Named pipe invalid */
+#define ERRpipebusy 231 /* All instances of pipe are busy */
+#define ERRpipeclosing 232 /* named pipe close in progress */
+#define ERRnotconnected 233 /* No process on other end of named pipe */
+#define ERRmoredata 234 /* More data to be returned */
+#define ERRnomoreitems 259
+#define ERRbaddirectory 267 /* Invalid directory name in a path. */
+#define ERReasnotsupported 282 /* Extended attributes */
+#define ERRlogonfailure 1326 /* Unknown username or bad password */
+#define ERRbuftoosmall 2123
+#define ERRunknownipc 2142
+#define ERRnosuchprintjob 2151
+#define ERRinvgroup 2455
+
+/* here's a special one from observing NT */
+#define ERRnoipc 66 /* don't support ipc */
+
+/* These errors seem to be only returned by the NT printer driver system */
+#define ERRdriveralreadyinstalled 1795 /* ERROR_PRINTER_DRIVER_ALREADY_INSTALLED */
+#define ERRunknownprinterport 1796 /* ERROR_UNKNOWN_PORT */
+#define ERRunknownprinterdriver 1797 /* ERROR_UNKNOWN_PRINTER_DRIVER */
+#define ERRunknownprintprocessor 1798 /* ERROR_UNKNOWN_PRINTPROCESSOR */
+#define ERRinvalidseparatorfile 1799 /* ERROR_INVALID_SEPARATOR_FILE */
+#define ERRinvalidjobpriority 1800 /* ERROR_INVALID_PRIORITY */
+#define ERRinvalidprintername 1801 /* ERROR_INVALID_PRINTER_NAME */
+#define ERRprinteralreadyexists 1802 /* ERROR_PRINTER_ALREADY_EXISTS */
+#define ERRinvalidprintercommand 1803 /* ERROR_INVALID_PRINTER_COMMAND */
+#define ERRinvaliddatatype 1804 /* ERROR_INVALID_DATATYPE */
+#define ERRinvalidenvironment 1805 /* ERROR_INVALID_ENVIRONMENT */
+
+#define ERRunknownprintmonitor 3000 /* ERROR_UNKNOWN_PRINT_MONITOR */
+#define ERRprinterdriverinuse 3001 /* ERROR_PRINTER_DRIVER_IN_USE */
+#define ERRspoolfilenotfound 3002 /* ERROR_SPOOL_FILE_NOT_FOUND */
+#define ERRnostartdoc 3003 /* ERROR_SPL_NO_STARTDOC */
+#define ERRnoaddjob 3004 /* ERROR_SPL_NO_ADDJOB */
+#define ERRprintprocessoralreadyinstalled 3005 /* ERROR_PRINT_PROCESSOR_ALREADY_INSTALLED */
+#define ERRprintmonitoralreadyinstalled 3006 /* ERROR_PRINT_MONITOR_ALREADY_INSTALLED */
+#define ERRinvalidprintmonitor 3007 /* ERROR_INVALID_PRINT_MONITOR */
+#define ERRprintmonitorinuse 3008 /* ERROR_PRINT_MONITOR_IN_USE */
+#define ERRprinterhasjobsqueued 3009 /* ERROR_PRINTER_HAS_JOBS_QUEUED */
+
+/* Error codes for the ERRSRV class */
+
+#define ERRerror 1 /* Non specific error code */
+#define ERRbadpw 2 /* Bad password */
+#define ERRbadtype 3 /* reserved */
+#define ERRaccess 4 /* No permissions to do the requested operation */
+#define ERRinvnid 5 /* tid invalid */
+#define ERRinvnetname 6 /* Invalid servername */
+#define ERRinvdevice 7 /* Invalid device */
+#define ERRqfull 49 /* Print queue full */
+#define ERRqtoobig 50 /* Queued item too big */
+#define ERRinvpfid 52 /* Invalid print file in smb_fid */
+#define ERRsmbcmd 64 /* Unrecognised command */
+#define ERRsrverror 65 /* smb server internal error */
+#define ERRfilespecs 67 /* fid and pathname invalid combination */
+#define ERRbadlink 68 /* reserved */
+#define ERRbadpermits 69 /* Access specified for a file is not valid */
+#define ERRbadpid 70 /* reserved */
+#define ERRsetattrmode 71 /* attribute mode invalid */
+#define ERRpaused 81 /* Message server paused */
+#define ERRmsgoff 82 /* Not receiving messages */
+#define ERRnoroom 83 /* No room for message */
+#define ERRrmuns 87 /* too many remote usernames */
+#define ERRtimeout 88 /* operation timed out */
+#define ERRnoresource 89 /* No resources currently available for request. */
+#define ERRtoomanyuids 90 /* too many userids */
+#define ERRbaduid 91 /* bad userid */
+#define ERRuseMPX 250 /* temporarily unable to use raw mode, use MPX mode */
+#define ERRuseSTD 251 /* temporarily unable to use raw mode, use standard mode */
+#define ERRcontMPX 252 /* resume MPX mode */
+#define ERRbadPW /* reserved */
+#define ERRnosupport 0xFFFF
+#define ERRunknownsmb 22 /* from NT 3.5 response */
+
+/* Error codes for the ERRHRD class */
+
+#define ERRnowrite 19 /* read only media */
+#define ERRbadunit 20 /* Unknown device */
+#define ERRnotready 21 /* Drive not ready */
+#define ERRbadcmd 22 /* Unknown command */
+#define ERRdata 23 /* Data (CRC) error */
+#define ERRbadreq 24 /* Bad request structure length */
+#define ERRseek 25
+#define ERRbadmedia 26
+#define ERRbadsector 27
+#define ERRnopaper 28
+#define ERRwrite 29 /* write fault */
+#define ERRread 30 /* read fault */
+#define ERRgeneral 31 /* General hardware failure */
+#define ERRwrongdisk 34
+#define ERRFCBunavail 35
+#define ERRsharebufexc 36 /* share buffer exceeded */
+#define ERRdiskfull 39
+
+
+/* these are win32 error codes. There are only a few places where
+ these matter for Samba, primarily in the NT printing code */
+#define WERR_OK W_ERROR(0)
+#define WERR_BADFUNC W_ERROR(1)
+#define WERR_BADFILE W_ERROR(2)
+#define WERR_ACCESS_DENIED W_ERROR(5)
+#define WERR_BADFID W_ERROR(6)
+#define WERR_NOMEM W_ERROR(8)
+#define WERR_GENERAL_FAILURE W_ERROR(31)
+#define WERR_NOT_SUPPORTED W_ERROR(50)
+#define WERR_PRINTQ_FULL W_ERROR(61)
+#define WERR_NO_SPOOL_SPACE W_ERROR(62)
+#define WERR_NO_SUCH_SHARE W_ERROR(67)
+#define WERR_ALREADY_EXISTS W_ERROR(80)
+#define WERR_BAD_PASSWORD W_ERROR(86)
+#define WERR_INVALID_PARAM W_ERROR(87)
+#define WERR_INSUFFICIENT_BUFFER W_ERROR(122)
+#define WERR_INVALID_NAME W_ERROR(123)
+#define WERR_UNKNOWN_LEVEL W_ERROR(124)
+#define WERR_OBJECT_PATH_INVALID W_ERROR(161)
+#define WERR_NO_MORE_ITEMS W_ERROR(259)
+#define WERR_MORE_DATA W_ERROR(234)
+#define WERR_INVALID_OWNER W_ERROR(1307)
+#define WERR_CAN_NOT_COMPLETE W_ERROR(1003)
+#define WERR_INVALID_SECURITY_DESCRIPTOR W_ERROR(1338)
+#define WERR_SERVER_UNAVAILABLE W_ERROR(1722)
+#define WERR_INVALID_FORM_NAME W_ERROR(1902)
+#define WERR_INVALID_FORM_SIZE W_ERROR(1903)
+#define WERR_BUF_TOO_SMALL W_ERROR(2123)
+#define WERR_JOB_NOT_FOUND W_ERROR(2151)
+#define WERR_DEST_NOT_FOUND W_ERROR(2152)
+#define WERR_NOT_LOCAL_DOMAIN W_ERROR(2320)
+#define WERR_STATUS_MORE_ENTRIES W_ERROR(0x0105)
+
+#define WERR_PRINTER_DRIVER_ALREADY_INSTALLED W_ERROR(ERRdriveralreadyinstalled)
+#define WERR_UNKNOWN_PORT W_ERROR(ERRunknownprinterport)
+#define WERR_UNKNOWN_PRINTER_DRIVER W_ERROR(ERRunknownprinterdriver)
+#define WERR_UNKNOWN_PRINTPROCESSOR W_ERROR(ERRunknownprintprocessor)
+#define WERR_INVALID_SEPARATOR_FILE W_ERROR(ERRinvalidseparatorfile)
+#define WERR_INVALID_PRIORITY W_ERROR(ERRinvalidjobpriority)
+#define WERR_INVALID_PRINTER_NAME W_ERROR(ERRinvalidprintername)
+#define WERR_PRINTER_ALREADY_EXISTS W_ERROR(ERRprinteralreadyexists)
+#define WERR_INVALID_PRINTER_COMMAND W_ERROR(ERRinvalidprintercommand)
+#define WERR_INVALID_DATATYPE W_ERROR(ERRinvaliddatatype)
+#define WERR_INVALID_ENVIRONMENT W_ERROR(ERRinvalidenvironment)
+
+#define WERR_UNKNOWN_PRINT_MONITOR W_ERROR(ERRunknownprintmonitor)
+#define WERR_PRINTER_DRIVER_IN_USE W_ERROR(ERRprinterdriverinuse)
+#define WERR_SPOOL_FILE_NOT_FOUND W_ERROR(ERRspoolfilenotfound)
+#define WERR_SPL_NO_STARTDOC W_ERROR(ERRnostartdoc)
+#define WERR_SPL_NO_ADDJOB W_ERROR(ERRnoaddjob)
+#define WERR_PRINT_PROCESSOR_ALREADY_INSTALLED W_ERROR(ERRprintprocessoralreadyinstalled)
+#define WERR_PRINT_MONITOR_ALREADY_INSTALLED W_ERROR(ERRprintmonitoralreadyinstalled)
+#define WERR_INVALID_PRINT_MONITOR W_ERROR(ERRinvalidprintmonitor)
+#define WERR_PRINT_MONITOR_IN_USE W_ERROR(ERRprintmonitorinuse)
+#define WERR_PRINTER_HAS_JOBS_QUEUED W_ERROR(ERRprinterhasjobsqueued)
+
+
+/* DFS errors */
+
+#ifndef NERR_BASE
+#define NERR_BASE (2100)
+#endif
+
+#define WERR_DFS_NO_SUCH_VOL W_ERROR(NERR_BASE+562)
+#define WERR_DFS_NO_SUCH_SHARE W_ERROR(NERR_BASE+565)
+#define WERR_DFS_NO_SUCH_SERVER W_ERROR(NERR_BASE+573)
+#define WERR_DFS_INTERNAL_ERROR W_ERROR(NERR_BASE+590)
+#define WERR_DFS_CANT_CREATE_JUNCT W_ERROR(NERR_BASE+569)
+
+#endif /* _DOSERR_H */
diff --git a/source/include/dynconfig.h b/source/include/dynconfig.h
new file mode 100644
index 00000000000..a74d77e41f7
--- /dev/null
+++ b/source/include/dynconfig.h
@@ -0,0 +1,38 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) 2001 by Martin Pool <mbp@samba.org>
+ Copyright (C) 2003 by Jim McDonough <jmcd@us.ibm.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/**
+ * @file dynconfig.h
+ *
+ * @brief Exported global configurations.
+ **/
+
+extern char const *dyn_SBINDIR,
+ *dyn_BINDIR,
+ *dyn_SWATDIR;
+
+extern pstring dyn_CONFIGFILE;
+extern pstring dyn_LOGFILEBASE, dyn_LMHOSTSFILE;
+extern pstring dyn_LIBDIR;
+extern const fstring dyn_SHLIBEXT;
+extern const pstring dyn_LOCKDIR;
+extern const pstring dyn_PIDDIR;
+extern const pstring dyn_SMB_PASSWD_FILE;
+extern const pstring dyn_PRIVATE_DIR;
diff --git a/source/include/fake_file.h b/source/include/fake_file.h
new file mode 100644
index 00000000000..3fe60072e9e
--- /dev/null
+++ b/source/include/fake_file.h
@@ -0,0 +1,46 @@
+/*
+ Unix SMB/CIFS implementation.
+ FAKE FILE suppport, for faking up special files windows want access to
+ Copyright (C) Stefan (metze) Metzmacher 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _FAKE_FILE_H
+#define _FAKE_FILE_H
+
+enum FAKE_FILE_TYPE {
+ FAKE_FILE_TYPE_NONE = 0,
+ FAKE_FILE_TYPE_QUOTA
+};
+
+#define FAKE_FILE_NAME_QUOTA "\\$Extend\\$Quota:$Q:$INDEX_ALLOCATION"
+
+typedef struct _FAKE_FILE_HANDLE {
+ enum FAKE_FILE_TYPE type;
+ TALLOC_CTX *mem_ctx;
+ void *pd; /* for private data */
+ void (*free_pd)(void **pd); /* free private_data */
+} FAKE_FILE_HANDLE;
+
+typedef struct _FAKE_FILE {
+ const char *name;
+ enum FAKE_FILE_TYPE type;
+ void *(*init_pd)(TALLOC_CTX *men_ctx);
+ void (*free_pd)(void **pd);
+} FAKE_FILE;
+
+
+#endif /* _FAKE_FILE_H */
diff --git a/source/include/genparser.h b/source/include/genparser.h
new file mode 100644
index 00000000000..f28cd78249d
--- /dev/null
+++ b/source/include/genparser.h
@@ -0,0 +1,78 @@
+/*
+ Copyright (C) Andrew Tridgell <genstruct@tridgell.net> 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _GENPARSER_H
+#define _GENPARSER_H
+
+/* these macros are needed for genstruct auto-parsers */
+#ifndef GENSTRUCT
+#define GENSTRUCT
+#define _LEN(x)
+#define _NULLTERM
+#endif
+
+/*
+ automatic marshalling/unmarshalling system for C structures
+*/
+
+/* flag to mark a fixed size array as actually being null terminated */
+#define FLAG_NULLTERM 1
+#define FLAG_ALWAYS 2
+
+struct enum_struct {
+ const char *name;
+ unsigned value;
+};
+
+/* intermediate dumps are stored in one of these */
+struct parse_string {
+ unsigned allocated;
+ unsigned length;
+ char *s;
+};
+
+typedef int (*gen_dump_fn)(TALLOC_CTX *, struct parse_string *, const char *ptr, unsigned indent);
+typedef int (*gen_parse_fn)(TALLOC_CTX *, char *ptr, const char *str);
+
+/* genstruct.pl generates arrays of these */
+struct parse_struct {
+ const char *name;
+ unsigned ptr_count;
+ unsigned size;
+ unsigned offset;
+ unsigned array_len;
+ const char *dynamic_len;
+ unsigned flags;
+ gen_dump_fn dump_fn;
+ gen_parse_fn parse_fn;
+};
+
+#define DUMP_PARSE_DECL(type) \
+ int gen_dump_ ## type(TALLOC_CTX *, struct parse_string *, const char *, unsigned); \
+ int gen_parse_ ## type(TALLOC_CTX *, char *, const char *);
+
+DUMP_PARSE_DECL(char)
+DUMP_PARSE_DECL(int)
+DUMP_PARSE_DECL(unsigned)
+DUMP_PARSE_DECL(double)
+DUMP_PARSE_DECL(float)
+
+#define gen_dump_unsigned_char gen_dump_char
+#define gen_parse_unsigned_char gen_parse_char
+
+#endif /* _GENPARSER_H */
diff --git a/source/include/genparser_samba.h b/source/include/genparser_samba.h
new file mode 100644
index 00000000000..213d51da876
--- /dev/null
+++ b/source/include/genparser_samba.h
@@ -0,0 +1,63 @@
+/*
+ Copyright (C) Simo Sorce <idra@samba.org> 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _GENPARSER_SAMBA_H
+#define _GENPARSER_SAMBA_H
+
+const struct parse_struct pinfo_security_ace_info[] = {
+{"type", 0, sizeof(uint8), offsetof(struct security_ace_info, type), 0, NULL, 0, gen_dump_uint8, gen_parse_uint8},
+{"flags", 0, sizeof(uint8), offsetof(struct security_ace_info, flags), 0, NULL, 0, gen_dump_uint8, gen_parse_uint8},
+{"size", 0, sizeof(uint16), offsetof(struct security_ace_info, size), 0, NULL, 0, gen_dump_uint16, gen_parse_uint16},
+{"info", 0, sizeof(char), offsetof(struct security_ace_info, info), 0, NULL, 0, gen_dump_SEC_ACCESS, gen_parse_SEC_ACCESS},
+{"obj_flags", 0, sizeof(uint32), offsetof(struct security_ace_info, obj_flags), 0, NULL, 0, gen_dump_uint32, gen_parse_uint32},
+{"obj_guid", 0, sizeof(char), offsetof(struct security_ace_info, obj_guid), 0, NULL, 0, gen_dump_GUID, gen_parse_GUID},
+{"inh_guid", 0, sizeof(char), offsetof(struct security_ace_info, inh_guid), 0, NULL, 0, gen_dump_GUID, gen_parse_GUID},
+{"trustee", 0, sizeof(char), offsetof(struct security_ace_info, trustee), 0, NULL, 0, gen_dump_DOM_SID, gen_parse_DOM_SID},
+{NULL, 0, 0, 0, 0, NULL, 0, NULL, NULL}};
+
+const struct parse_struct pinfo_security_acl_info[] = {
+{"revision", 0, sizeof(uint16), offsetof(struct security_acl_info, revision), 0, NULL, 0, gen_dump_uint16, gen_parse_uint16},
+{"size", 0, sizeof(uint16), offsetof(struct security_acl_info, size), 0, NULL, 0, gen_dump_uint16, gen_parse_uint16},
+{"num_aces", 0, sizeof(uint32), offsetof(struct security_acl_info, num_aces), 0, NULL, 0, gen_dump_uint32, gen_parse_uint32},
+{"ace", 1, sizeof(struct security_ace_info), offsetof(struct security_acl_info, ace), 0, "size", 0, gen_dump_SEC_ACE, gen_parse_SEC_ACE},
+{NULL, 0, 0, 0, 0, NULL, 0, NULL, NULL}};
+
+const struct parse_struct pinfo_security_descriptor_info[] = {
+{"revision", 0, sizeof(uint16), offsetof(struct security_descriptor_info, revision), 0, NULL, 0, gen_dump_uint16, gen_parse_uint16},
+{"type", 0, sizeof(uint16), offsetof(struct security_descriptor_info, type), 0, NULL, 0, gen_dump_uint16, gen_parse_uint16},
+{"off_owner_sid", 0, sizeof(uint32), offsetof(struct security_descriptor_info, off_owner_sid), 0, NULL, 0, gen_dump_uint32, gen_parse_uint32},
+{"off_grp_sid", 0, sizeof(uint32), offsetof(struct security_descriptor_info, off_grp_sid), 0, NULL, 0, gen_dump_uint32, gen_parse_uint32},
+{"off_sacl", 0, sizeof(uint32), offsetof(struct security_descriptor_info, off_sacl), 0, NULL, 0, gen_dump_uint32, gen_parse_uint32},
+{"off_dacl", 0, sizeof(uint32), offsetof(struct security_descriptor_info, off_dacl), 0, NULL, 0, gen_dump_uint32, gen_parse_uint32},
+{"dacl", 1, sizeof(struct security_acl_info), offsetof(struct security_descriptor_info, dacl), 0, NULL, 0, gen_dump_SEC_ACL, gen_parse_SEC_ACL},
+{"sacl", 1, sizeof(struct security_acl_info), offsetof(struct security_descriptor_info, sacl), 0, NULL, 0, gen_dump_SEC_ACL, gen_parse_SEC_ACL},
+{"owner_sid", 1, sizeof(char), offsetof(struct security_descriptor_info, owner_sid), 0, NULL, 0, gen_dump_DOM_SID, gen_parse_DOM_SID},
+{"grp_sid", 1, sizeof(char), offsetof(struct security_descriptor_info, grp_sid), 0, NULL, 0, gen_dump_DOM_SID, gen_parse_DOM_SID},
+{NULL, 0, 0, 0, 0, NULL, 0, NULL, NULL}};
+
+const struct parse_struct pinfo_luid_attr_info[] = {
+{"attr", 0, sizeof(uint32), offsetof(struct LUID_ATTR, attr), 0, NULL, 0, gen_dump_uint32, gen_parse_uint32},
+{"luid", 1, sizeof(LUID), offsetof(struct LUID_ATTR, luid), 0, NULL, 0, gen_dump_LUID, gen_parse_LUID},
+{NULL, 0, 0, 0, 0, NULL, 0, NULL, NULL}};
+
+const struct parse_struct pinfo_data_blob_info[] = {
+{"length", 0, sizeof(int), offsetof(DATA_BLOB, length), 0, NULL, 0, gen_dump_int, gen_parse_int},
+{"data", 1, sizeof(char), offsetof(DATA_BLOB, data), 0, "length", 0, gen_dump_char, gen_parse_char},
+{NULL, 0, 0, 0, 0, NULL, 0, NULL, NULL}};
+
+#endif /* _GENPARSER_SAMBA_H */
diff --git a/source/include/gums.h b/source/include/gums.h
new file mode 100644
index 00000000000..d16a839bc4b
--- /dev/null
+++ b/source/include/gums.h
@@ -0,0 +1,272 @@
+/*
+ Unix SMB/CIFS implementation.
+ GUMS structures
+ Copyright (C) Simo Sorce 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _GUMS_H
+#define _GUMS_H
+
+#define GUMS_VERSION_MAJOR 0
+#define GUMS_VERSION_MINOR 1
+#define GUMS_OBJECT_VERSION 1
+#define GUMS_PRIVILEGE_VERSION 1
+#define GUMS_INTERFACE_VERSION 1
+
+#define GUMS_OBJ_DOMAIN 0x10
+#define GUMS_OBJ_NORMAL_USER 0x20
+#define GUMS_OBJ_GROUP 0x30
+#define GUMS_OBJ_ALIAS 0x31
+
+/* define value types */
+#define GUMS_SET_PRIMARY_GROUP 0x1
+#define GUMS_SET_SEC_DESC 0x2
+
+#define GUMS_SET_NAME 0x10
+#define GUMS_SET_DESCRIPTION 0x11
+#define GUMS_SET_FULL_NAME 0x12
+
+/* user specific type values */
+#define GUMS_SET_LOGON_TIME 0x20
+#define GUMS_SET_LOGOFF_TIME 0x21
+#define GUMS_SET_KICKOFF_TIME 0x23
+#define GUMS_SET_PASS_LAST_SET_TIME 0x24
+#define GUMS_SET_PASS_CAN_CHANGE_TIME 0x25
+#define GUMS_SET_PASS_MUST_CHANGE_TIME 0x26
+
+
+#define GUMS_SET_HOME_DIRECTORY 0x31
+#define GUMS_SET_DRIVE 0x32
+#define GUMS_SET_LOGON_SCRIPT 0x33
+#define GUMS_SET_PROFILE_PATH 0x34
+#define GUMS_SET_WORKSTATIONS 0x35
+#define GUMS_SET_UNKNOWN_STRING 0x36
+#define GUMS_SET_MUNGED_DIAL 0x37
+
+#define GUMS_SET_LM_PASSWORD 0x40
+#define GUMS_SET_NT_PASSWORD 0x41
+#define GUMS_SET_PLAINTEXT_PASSWORD 0x42
+#define GUMS_SET_UNKNOWN_3 0x43
+#define GUMS_SET_LOGON_DIVS 0x44
+#define GUMS_SET_HOURS_LEN 0x45
+#define GUMS_SET_HOURS 0x46
+#define GUMS_SET_BAD_PASSWORD_COUNT 0x47
+#define GUMS_SET_LOGON_COUNT 0x48
+#define GUMS_SET_UNKNOWN_6 0x49
+
+#define GUMS_SET_MUST_CHANGE_PASS 0x50
+#define GUMS_SET_CANNOT_CHANGE_PASS 0x51
+#define GUMS_SET_PASS_NEVER_EXPIRE 0x52
+#define GUMS_SET_ACCOUNT_DISABLED 0x53
+#define GUMS_SET_ACCOUNT_LOCKOUT 0x54
+
+/*group specific type values */
+#define GUMS_ADD_SID_LIST 0x60
+#define GUMS_DEL_SID_LIST 0x61
+#define GUMS_SET_SID_LIST 0x62
+
+GENSTRUCT struct gums_user
+{
+ DOM_SID *group_sid; /* Primary Group SID */
+
+ NTTIME logon_time; /* logon time */
+ NTTIME logoff_time; /* logoff time */
+ NTTIME kickoff_time; /* kickoff time */
+ NTTIME pass_last_set_time; /* password last set time */
+ NTTIME pass_can_change_time; /* password can change time */
+ NTTIME pass_must_change_time; /* password must change time */
+
+ char *full_name; _NULLTERM /* user's full name string */
+ char *home_dir; _NULLTERM /* home directory string */
+ char *dir_drive; _NULLTERM /* home directory drive string */
+ char *logon_script; _NULLTERM /* logon script string */
+ char *profile_path; _NULLTERM /* profile path string */
+ char *workstations; _NULLTERM /* login from workstations string */
+ char *unknown_str; _NULLTERM /* don't know what this is, yet. */
+ char *munged_dial; _NULLTERM /* munged path name and dial-back tel number */
+
+ DATA_BLOB lm_pw; /* .data is Null if no password */
+ DATA_BLOB nt_pw; /* .data is Null if no password */
+
+ uint16 acct_ctrl; /* account type & status flags */
+ uint16 logon_divs; /* 168 - number of hours in a week */
+ uint32 hours_len; /* normally 21 bytes */
+ uint8 *hours; _LEN(hours_len) /* normally 21 bytes (depends on hours_len) */
+
+ uint16 bad_password_count; /* 0 */
+ uint16 logon_count; /* 0 */
+ uint32 unknown_3; /* 0x00ff ffff */
+ uint32 unknown_6; /* 0x0000 04ec */
+
+};
+
+GENSTRUCT struct gums_group
+{
+ uint32 count; /* Number of SIDs */
+ DOM_SID *members; _LEN(count) /* SID array */
+
+};
+
+GENSTRUCT struct gums_domain
+{
+ uint32 next_rid;
+
+};
+
+GENSTRUCT struct gums_object
+{
+ TALLOC_CTX *mem_ctx;
+
+ uint32 type; /* Object Type */
+ uint32 version; /* Object Version */
+ uint32 seq_num; /* Object Sequence Number */
+
+ SEC_DESC *sec_desc; /* Security Descriptor */
+
+ DOM_SID *sid; /* Object Sid */
+ char *name; _NULLTERM /* Object Name - it should be in DOMAIN\NAME format */
+ char *description; _NULLTERM /* Object Description */
+
+ struct gums_user *user;
+ struct gums_group *group;
+ struct gums_domain *domain;
+
+};
+
+GENSTRUCT struct gums_privilege
+{
+ TALLOC_CTX *mem_ctx;
+
+ uint32 version; /* Object Version */
+ uint32 seq_num; /* Object Sequence Number */
+
+ char *name; _NULLTERM /* Object Name */
+ char *description; _NULLTERM /* Object Description */
+
+ LUID_ATTR *privilege; /* Privilege Type */
+
+ uint32 count;
+ DOM_SID *members; _LEN(count)
+
+};
+
+typedef struct gums_user GUMS_USER;
+typedef struct gums_group GUMS_GROUP;
+typedef struct gums_domain GUMS_DOMAIN;
+typedef struct gums_object GUMS_OBJECT;
+typedef struct gums_privilege GUMS_PRIVILEGE;
+
+typedef struct gums_data_set
+{
+ int type; /* GUMS_SET_xxx */
+ void *data;
+
+} GUMS_DATA_SET;
+
+typedef struct gums_commit_set
+{
+ TALLOC_CTX *mem_ctx;
+
+ uint32 type; /* Object type */
+ DOM_SID sid; /* Object Sid */
+ uint32 count; /* number of changes */
+ GUMS_DATA_SET *data;
+
+} GUMS_COMMIT_SET;
+
+typedef struct gums_priv_commit_set
+{
+ TALLOC_CTX *mem_ctx;
+
+ uint32 type; /* Object type */
+ char *name; /* Object Sid */
+ uint32 count; /* number of changes */
+ GUMS_DATA_SET *data;
+
+} GUMS_PRIV_COMMIT_SET;
+
+
+typedef struct gums_functions
+{
+ /* module data */
+ TALLOC_CTX *mem_ctx;
+ char *name;
+ void *private_data;
+ void (*free_private_data)(void **);
+
+ /* Generic object functions */
+
+ NTSTATUS (*get_domain_sid) (DOM_SID *sid, const char* name);
+ NTSTATUS (*set_domain_sid) (const DOM_SID *sid);
+
+ NTSTATUS (*get_sequence_number) (void);
+
+ NTSTATUS (*new_object) (DOM_SID *sid, const char *name, const int obj_type);
+ NTSTATUS (*delete_object) (const DOM_SID *sid);
+
+ NTSTATUS (*get_object_from_sid) (GUMS_OBJECT **object, const DOM_SID *sid, const int obj_type);
+ NTSTATUS (*get_object_from_name) (GUMS_OBJECT **object, const char *domain, const char *name, const int obj_type);
+ /* This function is used to get the list of all objects changed since b_time, it is
+ used to support PDC<->BDC synchronization */
+ NTSTATUS (*get_updated_objects) (GUMS_OBJECT **objects, const NTTIME base_time);
+
+ NTSTATUS (*enumerate_objects_start) (void **handle, const DOM_SID *sid, const int obj_type);
+ NTSTATUS (*enumerate_objects_get_next) (GUMS_OBJECT **object, void *handle);
+ NTSTATUS (*enumerate_objects_stop) (void *handle);
+
+ /* This function MUST be used ONLY by PDC<->BDC replication code or recovery tools.
+ Never use this function to update an object in the database, use set_object_values() */
+ NTSTATUS (*set_object) (GUMS_OBJECT *object);
+
+ /* set object values function */
+ NTSTATUS (*set_object_values) (DOM_SID *sid, uint32 count, GUMS_DATA_SET *data_set);
+
+ /* Group related functions */
+ NTSTATUS (*add_members_to_group) (const DOM_SID *group, const DOM_SID **members);
+ NTSTATUS (*delete_members_from_group) (const DOM_SID *group, const DOM_SID **members);
+ NTSTATUS (*enumerate_group_members) (DOM_SID **members, const DOM_SID *sid, const int type);
+
+ NTSTATUS (*get_sid_groups) (DOM_SID **groups, const DOM_SID *sid);
+
+ NTSTATUS (*lock_sid) (const DOM_SID *sid);
+ NTSTATUS (*unlock_sid) (const DOM_SID *sid);
+
+ /* privileges related functions */
+
+ NTSTATUS (*get_privilege) (GUMS_OBJECT **object, const char *name);
+ NTSTATUS (*add_members_to_privilege) (const char *name, const DOM_SID **members);
+ NTSTATUS (*delete_members_from_privilege) (const char *name, const DOM_SID **members);
+ NTSTATUS (*enumerate_privilege_members) (const char *name, DOM_SID **members);
+ NTSTATUS (*get_sid_privileges) (const DOM_SID *sid, const char **privs);
+
+ /* warning!: set_privilege will overwrite a prior existing privilege if such exist */
+ NTSTATUS (*set_privilege) (GUMS_PRIVILEGE *priv);
+
+} GUMS_FUNCTIONS;
+
+typedef NTSTATUS (*gums_init_function)(
+ struct gums_functions *,
+ const char *);
+
+struct gums_init_function_entry {
+
+ const char *name;
+ gums_init_function init_fn;
+ struct gums_init_function_entry *prev, *next;
+};
+
+#endif /* _GUMS_H */
diff --git a/source/include/hash.h b/source/include/hash.h
new file mode 100644
index 00000000000..40cc8b7cab3
--- /dev/null
+++ b/source/include/hash.h
@@ -0,0 +1,74 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Ying Chen 2000.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _HASH_H_
+#define _HASH_H_
+
+#define MAX_HASH_TABLE_SIZE 16384
+#define HASH_TABLE_INCREMENT 2
+
+typedef int (*compare_function)(char *, char *);
+typedef int (*hash_function)(int, char *);
+
+/*
+ * lru_link: links the node to the LRU list.
+ * hash_elem: the pointer to the element that is tied onto the link.
+ */
+typedef struct lru_node {
+ ubi_dlNode lru_link;
+ void *hash_elem;
+} lru_node;
+
+/*
+ * bucket_link: link the hash element to the bucket chain that it belongs to.
+ * lru_link: this element ties the hash element to the lru list.
+ * bucket: a pointer to the hash bucket that this element belongs to.
+ * value: a pointer to the hash element content. It can be anything.
+ * key: stores the string key. The hash_element is always allocated with
+ * more memory space than the structure shown below to accomodate the space
+ * used for the whole string. But the memory is always appended at the
+ * end of the structure, so keep "key" at the end of the structure.
+ * Don't move it.
+ */
+typedef struct hash_element {
+ ubi_dlNode bucket_link;
+ lru_node lru_link;
+ ubi_dlList *bucket;
+ void *value;
+ char key[1];
+} hash_element;
+
+/*
+ * buckets: a list of buckets, implemented as a dLinkList.
+ * lru_chain: the lru list of all the hash elements.
+ * num_elements: the # of elements in the hash table.
+ * size: the hash table size.
+ * comp_func: the compare function used during hash key comparisons.
+ */
+
+typedef struct hash_table {
+ ubi_dlList *buckets;
+ ubi_dlList lru_chain;
+ unsigned num_elements;
+ unsigned size;
+ compare_function comp_func;
+} hash_table;
+
+#endif /* _HASH_H_ */
diff --git a/source/include/hmacmd5.h b/source/include/hmacmd5.h
new file mode 100644
index 00000000000..6b53a6fd074
--- /dev/null
+++ b/source/include/hmacmd5.h
@@ -0,0 +1,32 @@
+/*
+ Unix SMB/CIFS implementation.
+ Interface header: Scheduler service
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1999
+ Copyright (C) Andrew Tridgell 1992-1999
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _HMAC_MD5_H
+
+typedef struct
+{
+ struct MD5Context ctx;
+ uchar k_ipad[65];
+ uchar k_opad[65];
+
+} HMACMD5Context;
+
+#endif /* _HMAC_MD5_H */
diff --git a/source/include/idmap.h b/source/include/idmap.h
new file mode 100644
index 00000000000..20b1015285e
--- /dev/null
+++ b/source/include/idmap.h
@@ -0,0 +1,57 @@
+#ifndef _IDMAP_H_
+#define _IDMAP_H_
+/*
+ Unix SMB/CIFS implementation.
+
+ Idmap headers
+
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+ Copyright (C) Simo Sorce 2003
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#define SMB_IDMAP_INTERFACE_VERSION 2
+
+
+#define ID_EMPTY 0x00
+#define ID_USERID 0x01
+#define ID_GROUPID 0x02
+#define ID_OTHER 0x04
+
+#define ID_TYPEMASK 0x0f
+
+#define ID_QUERY_ONLY 0x10
+
+/* Filled out by IDMAP backends */
+struct idmap_methods {
+
+ /* Called when backend is first loaded */
+ NTSTATUS (*init)( char *params );
+
+ NTSTATUS (*allocate_rid)(uint32 *rid, int rid_type);
+ NTSTATUS (*allocate_id)(unid_t *id, int id_type);
+ NTSTATUS (*get_sid_from_id)(DOM_SID *sid, unid_t id, int id_type);
+ NTSTATUS (*get_id_from_sid)(unid_t *id, int *id_type, const DOM_SID *sid);
+ NTSTATUS (*set_mapping)(const DOM_SID *sid, unid_t id, int id_type);
+
+ /* Called when backend is unloaded */
+ NTSTATUS (*close)(void);
+
+ /* Called to dump backend status */
+ void (*status)(void);
+};
+#endif /* _IDMAP_H_ */
diff --git a/source/include/includes.h b/source/include/includes.h
new file mode 100644
index 00000000000..dd93c813d3d
--- /dev/null
+++ b/source/include/includes.h
@@ -0,0 +1,1333 @@
+#ifndef _INCLUDES_H
+#define _INCLUDES_H
+/*
+ Unix SMB/CIFS implementation.
+ Machine customisation and include handling
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) 2002 by Martin Pool <mbp@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef NO_CONFIG_H /* for some tests */
+#include "config.h"
+#endif
+
+#include "local.h"
+
+#ifdef AIX
+#define DEFAULT_PRINTING PRINT_AIX
+#define PRINTCAP_NAME "/etc/qconfig"
+#endif
+
+#ifdef HPUX
+#define DEFAULT_PRINTING PRINT_HPUX
+#endif
+
+#ifdef QNX
+#define DEFAULT_PRINTING PRINT_QNX
+#endif
+
+#ifdef SUNOS4
+/* on SUNOS4 termios.h conflicts with sys/ioctl.h */
+#undef HAVE_TERMIOS_H
+#endif
+
+#ifdef __GNUC__
+/** Use gcc attribute to check printf fns. a1 is the 1-based index of
+ * the parameter containing the format, and a2 the index of the first
+ * argument. **/
+#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2)))
+#else
+#define PRINTF_ATTRIBUTE(a1, a2)
+#endif
+
+#ifdef __GNUC__
+/** gcc attribute used on function parameters so that it does not emit
+ * warnings about them being unused. **/
+# define UNUSED(param) param __attribute__ ((unused))
+#else
+# define UNUSED(param) param
+/** Feel free to add definitions for other compilers here. */
+#endif
+
+#ifdef RELIANTUNIX
+/*
+ * <unistd.h> has to be included before any other to get
+ * large file support on Reliant UNIX. Yes, it's broken :-).
+ */
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#endif /* RELIANTUNIX */
+
+#include <sys/types.h>
+
+#ifdef TIME_WITH_SYS_TIME
+#include <sys/time.h>
+#include <time.h>
+#else
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+#endif
+
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_UNIXSOCKET
+#include <sys/un.h>
+#endif
+
+#ifdef HAVE_SYS_SYSCALL_H
+#include <sys/syscall.h>
+#elif HAVE_SYSCALL_H
+#include <syscall.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#else
+#ifdef HAVE_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+#endif
+
+#include <sys/stat.h>
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+
+#include <signal.h>
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#ifdef HAVE_SYS_PRIV_H
+#include <sys/priv.h>
+#endif
+#ifdef HAVE_SYS_ID_H
+#include <sys/id.h>
+#endif
+
+#include <errno.h>
+
+#ifdef HAVE_UTIME_H
+#include <utime.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#ifdef HAVE_SYS_MODE_H
+/* apparently AIX needs this for S_ISLNK */
+#ifndef S_ISLNK
+#include <sys/mode.h>
+#endif
+#endif
+
+#ifdef HAVE_GLOB_H
+#include <glob.h>
+#endif
+
+#include <pwd.h>
+
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#else
+#ifdef HAVE_SYS_SYSLOG_H
+#include <sys/syslog.h>
+#endif
+#endif
+
+#include <sys/file.h>
+
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
+/*
+ * The next three defines are needed to access the IPTOS_* options
+ * on some systems.
+ */
+
+#ifdef HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_IP_H
+#include <netinet/in_ip.h>
+#endif
+
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif
+
+#if defined(HAVE_TERMIOS_H)
+/* POSIX terminal handling. */
+#include <termios.h>
+#elif defined(HAVE_TERMIO_H)
+/* Older SYSV terminal handling - don't use if we can avoid it. */
+#include <termio.h>
+#elif defined(HAVE_SYS_TERMIO_H)
+/* Older SYSV terminal handling - don't use if we can avoid it. */
+#include <sys/termio.h>
+#endif
+
+#if HAVE_DIRENT_H
+# include <dirent.h>
+# define NAMLEN(dirent) strlen((dirent)->d_name)
+#else
+# define dirent direct
+# define NAMLEN(dirent) (dirent)->d_namlen
+# if HAVE_SYS_NDIR_H
+# include <sys/ndir.h>
+# endif
+# if HAVE_SYS_DIR_H
+# include <sys/dir.h>
+# endif
+# if HAVE_NDIR_H
+# include <ndir.h>
+# endif
+#endif
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+
+
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+
+#ifdef HAVE_SYS_VFS_H
+#include <sys/vfs.h>
+#endif
+
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#endif
+
+#ifdef HAVE_SYS_FS_S5PARAM_H
+#include <sys/fs/s5param.h>
+#endif
+
+#if defined (HAVE_SYS_FILSYS_H) && !defined (_CRAY)
+#include <sys/filsys.h>
+#endif
+
+#ifdef HAVE_SYS_STATFS_H
+# include <sys/statfs.h>
+#endif
+
+#ifdef HAVE_DUSTAT_H
+#include <sys/dustat.h>
+#endif
+
+#ifdef HAVE_SYS_STATVFS_H
+#include <sys/statvfs.h>
+#endif
+
+#ifdef HAVE_SHADOW_H
+#include <shadow.h>
+#endif
+
+#ifdef HAVE_GETPWANAM
+#include <sys/label.h>
+#include <sys/audit.h>
+#include <pwdadj.h>
+#endif
+
+#ifdef HAVE_SYS_SECURITY_H
+#include <sys/security.h>
+#include <prot.h>
+#define PASSWORD_LENGTH 16
+#endif /* HAVE_SYS_SECURITY_H */
+
+#ifdef HAVE_STROPTS_H
+#include <stropts.h>
+#endif
+
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif
+
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
+
+#ifdef HAVE_SYS_CAPABILITY_H
+
+#if defined(BROKEN_REDHAT_7_SYSTEM_HEADERS) && !defined(_I386_STATFS_H) && !defined(_PPC_STATFS_H)
+#define _I386_STATFS_H
+#define _PPC_STATFS_H
+#define BROKEN_REDHAT_7_STATFS_WORKAROUND
+#endif
+
+#include <sys/capability.h>
+
+#ifdef BROKEN_REDHAT_7_STATFS_WORKAROUND
+#undef _I386_STATFS_H
+#undef _PPC_STATFS_H
+#undef BROKEN_REDHAT_7_STATFS_WORKAROUND
+#endif
+
+#endif
+
+#if defined(HAVE_RPC_RPC_H)
+/*
+ * Check for AUTH_ERROR define conflict with rpc/rpc.h in prot.h.
+ */
+#if defined(HAVE_SYS_SECURITY_H) && defined(HAVE_RPC_AUTH_ERROR_CONFLICT)
+#undef AUTH_ERROR
+#endif
+#include <rpc/rpc.h>
+#endif
+
+#if defined(HAVE_YP_GET_DEFAULT_DOMAIN) && defined(HAVE_SETNETGRENT) && defined(HAVE_ENDNETGRENT) && defined(HAVE_GETNETGRENT)
+#define HAVE_NETGROUP 1
+#endif
+
+#if defined (HAVE_NETGROUP)
+#if defined(HAVE_RPCSVC_YP_PROT_H)
+#include <rpcsvc/yp_prot.h>
+#endif
+#if defined(HAVE_RPCSVC_YPCLNT_H)
+#include <rpcsvc/ypclnt.h>
+#endif
+#endif /* HAVE_NETGROUP */
+
+#if defined(HAVE_SYS_IPC_H)
+#include <sys/ipc.h>
+#endif /* HAVE_SYS_IPC_H */
+
+#if defined(HAVE_SYS_SHM_H)
+#include <sys/shm.h>
+#endif /* HAVE_SYS_SHM_H */
+
+#ifdef HAVE_NATIVE_ICONV
+#ifdef HAVE_ICONV
+#include <iconv.h>
+#endif
+#ifdef HAVE_GICONV
+#include <giconv.h>
+#endif
+#ifdef HAVE_BICONV
+#include <biconv.h>
+#endif
+#endif
+
+#if HAVE_KRB5_H
+#include <krb5.h>
+#else
+#undef HAVE_KRB5
+#endif
+
+#if HAVE_LBER_H
+#include <lber.h>
+#endif
+
+#if HAVE_LDAP_H
+#include <ldap.h>
+#else
+#undef HAVE_LDAP
+#endif
+
+#if HAVE_GSSAPI_H
+#include <gssapi.h>
+#endif
+
+#if HAVE_GSSAPI_GSSAPI_H
+#include <gssapi/gssapi.h>
+#endif
+
+#if HAVE_GSSAPI_GSSAPI_GENERIC_H
+#include <gssapi/gssapi_generic.h>
+#endif
+
+#if HAVE_COM_ERR_H
+#include <com_err.h>
+#endif
+
+#if HAVE_SYS_ATTRIBUTES_H
+#include <sys/attributes.h>
+#endif
+
+/* mutually exclusive (SuSE 8.2) */
+#if HAVE_ATTR_XATTR_H
+#include <attr/xattr.h>
+#elif HAVE_SYS_XATTR_H
+#include <sys/xattr.h>
+#endif
+
+#if HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#if HAVE_LANGINFO_H
+#include <langinfo.h>
+#endif
+
+/* Special macros that are no-ops except when run under Valgrind on
+ * x86. They've moved a little bit from valgrind 1.0.4 to 1.9.4 */
+#if HAVE_VALGRIND_MEMCHECK_H
+ /* memcheck.h includes valgrind.h */
+#include <valgrind/memcheck.h>
+#elif HAVE_VALGRIND_H
+#include <valgrind.h>
+#endif
+
+/* If we have --enable-developer and the valgrind header is present,
+ * then we're OK to use it. Set a macro so this logic can be done only
+ * once. */
+#if defined(DEVELOPER) && (HAVE_VALGRIND_H || HAVE_VALGRIND_VALGRIND_H)
+#define VALGRIND
+#endif
+
+
+/* we support ADS if we want it and have krb5 and ldap libs */
+#if defined(WITH_ADS) && defined(HAVE_KRB5) && defined(HAVE_LDAP)
+#define HAVE_ADS
+#endif
+
+/*
+ * Define VOLATILE if needed.
+ */
+
+#if defined(HAVE_VOLATILE)
+#define VOLATILE volatile
+#else
+#define VOLATILE
+#endif
+
+/*
+ * Define additional missing types
+ */
+#if defined(HAVE_SIG_ATOMIC_T_TYPE) && defined(AIX)
+typedef sig_atomic_t SIG_ATOMIC_T;
+#elif defined(HAVE_SIG_ATOMIC_T_TYPE) && !defined(AIX)
+typedef sig_atomic_t VOLATILE SIG_ATOMIC_T;
+#else
+typedef int VOLATILE SIG_ATOMIC_T;
+#endif
+
+#ifndef HAVE_SOCKLEN_T_TYPE
+typedef int socklen_t;
+#endif
+
+
+#ifndef uchar
+#define uchar unsigned char
+#endif
+
+#ifdef HAVE_UNSIGNED_CHAR
+#define schar signed char
+#else
+#define schar char
+#endif
+
+/*
+ Samba needs type definitions for int16, int32, uint16 and uint32.
+
+ Normally these are signed and unsigned 16 and 32 bit integers, but
+ they actually only need to be at least 16 and 32 bits
+ respectively. Thus if your word size is 8 bytes just defining them
+ as signed and unsigned int will work.
+*/
+
+#ifndef uint8
+#define uint8 unsigned char
+#endif
+
+#if !defined(int16) && !defined(HAVE_INT16_FROM_RPC_RPC_H)
+#if (SIZEOF_SHORT == 4)
+#define int16 __ERROR___CANNOT_DETERMINE_TYPE_FOR_INT16;
+#else /* SIZEOF_SHORT != 4 */
+#define int16 short
+#endif /* SIZEOF_SHORT != 4 */
+#endif
+
+/*
+ * Note we duplicate the size tests in the unsigned
+ * case as int16 may be a typedef from rpc/rpc.h
+ */
+
+#if !defined(uint16) && !defined(HAVE_UINT16_FROM_RPC_RPC_H)
+#if (SIZEOF_SHORT == 4)
+#define uint16 __ERROR___CANNOT_DETERMINE_TYPE_FOR_INT16;
+#else /* SIZEOF_SHORT != 4 */
+#define uint16 unsigned short
+#endif /* SIZEOF_SHORT != 4 */
+#endif
+
+#if !defined(int32) && !defined(HAVE_INT32_FROM_RPC_RPC_H)
+#if (SIZEOF_INT == 4)
+#define int32 int
+#elif (SIZEOF_LONG == 4)
+#define int32 long
+#elif (SIZEOF_SHORT == 4)
+#define int32 short
+#else
+/* uggh - no 32 bit type?? probably a CRAY. just hope this works ... */
+#define int32 int
+#endif
+#endif
+
+/*
+ * Note we duplicate the size tests in the unsigned
+ * case as int32 may be a typedef from rpc/rpc.h
+ */
+
+#if !defined(uint32) && !defined(HAVE_UINT32_FROM_RPC_RPC_H)
+#if (SIZEOF_INT == 4)
+#define uint32 unsigned int
+#elif (SIZEOF_LONG == 4)
+#define uint32 unsigned long
+#elif (SIZEOF_SHORT == 4)
+#define uint32 unsigned short
+#else
+/* uggh - no 32 bit type?? probably a CRAY. just hope this works ... */
+#define uint32 unsigned
+#endif
+#endif
+
+/*
+ * Types for devices, inodes and offsets.
+ */
+
+#ifndef SMB_DEV_T
+# if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_DEV64_T)
+# define SMB_DEV_T dev64_t
+# else
+# define SMB_DEV_T dev_t
+# endif
+#endif
+
+/*
+ * Setup the correctly sized inode type.
+ */
+
+#ifndef SMB_INO_T
+# if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_INO64_T)
+# define SMB_INO_T ino64_t
+# else
+# define SMB_INO_T ino_t
+# endif
+#endif
+
+#ifndef LARGE_SMB_INO_T
+# if (defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_INO64_T)) || (defined(SIZEOF_INO_T) && (SIZEOF_INO_T == 8))
+# define LARGE_SMB_INO_T 1
+# endif
+#endif
+
+#ifdef LARGE_SMB_INO_T
+#define SINO_T(p, ofs, v) (SIVAL(p,ofs,(v)&0xFFFFFFFF), SIVAL(p,(ofs)+4,(v)>>32))
+#else
+#define SINO_T(p, ofs, v) (SIVAL(p,ofs,v),SIVAL(p,(ofs)+4,0))
+#endif
+
+#ifndef SMB_OFF_T
+# if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T)
+# define SMB_OFF_T off64_t
+# else
+# define SMB_OFF_T off_t
+# endif
+#endif
+
+#if defined(HAVE_LONGLONG)
+#define SMB_BIG_UINT unsigned long long
+#define SMB_BIG_INT long long
+#define SBIG_UINT(p, ofs, v) (SIVAL(p,ofs,(v)&0xFFFFFFFF), SIVAL(p,(ofs)+4,(v)>>32))
+#else
+#define SMB_BIG_UINT unsigned long
+#define SMB_BIG_INT long
+#define SBIG_UINT(p, ofs, v) (SIVAL(p,ofs,v),SIVAL(p,(ofs)+4,0))
+#endif
+
+#define SMB_BIG_UINT_BITS (sizeof(SMB_BIG_UINT)*8)
+
+/* this should really be a 64 bit type if possible */
+#define br_off SMB_BIG_UINT
+
+#define SMB_OFF_T_BITS (sizeof(SMB_OFF_T)*8)
+
+/*
+ * Set the define that tells us if we can do 64 bit
+ * NT SMB calls.
+ */
+
+#ifndef LARGE_SMB_OFF_T
+# if (defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T)) || (defined(SIZEOF_OFF_T) && (SIZEOF_OFF_T == 8))
+# define LARGE_SMB_OFF_T 1
+# endif
+#endif
+
+#ifdef LARGE_SMB_OFF_T
+#define SOFF_T(p, ofs, v) (SIVAL(p,ofs,(v)&0xFFFFFFFF), SIVAL(p,(ofs)+4,(v)>>32))
+#define SOFF_T_R(p, ofs, v) (SIVAL(p,(ofs)+4,(v)&0xFFFFFFFF), SIVAL(p,ofs,(v)>>32))
+#define IVAL_TO_SMB_OFF_T(buf,off) ((SMB_OFF_T)(( ((SMB_BIG_UINT)(IVAL((buf),(off)))) & ((SMB_BIG_UINT)0xFFFFFFFF) )))
+#define IVAL2_TO_SMB_BIG_UINT(buf,off) ( (((SMB_BIG_UINT)(IVAL((buf),(off)))) & ((SMB_BIG_UINT)0xFFFFFFFF)) | \
+ (( ((SMB_BIG_UINT)(IVAL((buf),(off+4)))) & ((SMB_BIG_UINT)0xFFFFFFFF) ) << 32 ) )
+#else
+#define SOFF_T(p, ofs, v) (SIVAL(p,ofs,v),SIVAL(p,(ofs)+4,0))
+#define SOFF_T_R(p, ofs, v) (SIVAL(p,(ofs)+4,v),SIVAL(p,ofs,0))
+#define IVAL_TO_SMB_OFF_T(buf,off) ((SMB_OFF_T)(( ((uint32)(IVAL((buf),(off)))) & 0xFFFFFFFF )))
+#define IVAL2_TO_SMB_BIG_UINT(buf,off) ( (((SMB_BIG_UINT)(IVAL((buf),(off)))) & ((SMB_BIG_UINT)0xFFFFFFFF)) | \
+ (( ((SMB_BIG_UINT)(IVAL((buf),(off+4)))) & ((SMB_BIG_UINT)0xFFFFFFFF) ) << 32 ) )
+#endif
+
+/*
+ * Type for stat structure.
+ */
+
+#ifndef SMB_STRUCT_STAT
+# if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_STAT64) && defined(HAVE_OFF64_T)
+# define SMB_STRUCT_STAT struct stat64
+# else
+# define SMB_STRUCT_STAT struct stat
+# endif
+#endif
+
+/*
+ * Type for dirent structure.
+ */
+
+#ifndef SMB_STRUCT_DIRENT
+# if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_STRUCT_DIRENT64)
+# define SMB_STRUCT_DIRENT struct dirent64
+# else
+# define SMB_STRUCT_DIRENT struct dirent
+# endif
+#endif
+
+/*
+ * Defines for 64 bit fcntl locks.
+ */
+
+#ifndef SMB_STRUCT_FLOCK
+# if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_STRUCT_FLOCK64) && defined(HAVE_OFF64_T)
+# define SMB_STRUCT_FLOCK struct flock64
+# else
+# define SMB_STRUCT_FLOCK struct flock
+# endif
+#endif
+
+#ifndef SMB_F_SETLKW
+# if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_STRUCT_FLOCK64) && defined(HAVE_OFF64_T)
+# define SMB_F_SETLKW F_SETLKW64
+# else
+# define SMB_F_SETLKW F_SETLKW
+# endif
+#endif
+
+#ifndef SMB_F_SETLK
+# if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_STRUCT_FLOCK64) && defined(HAVE_OFF64_T)
+# define SMB_F_SETLK F_SETLK64
+# else
+# define SMB_F_SETLK F_SETLK
+# endif
+#endif
+
+#ifndef SMB_F_GETLK
+# if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_STRUCT_FLOCK64) && defined(HAVE_OFF64_T)
+# define SMB_F_GETLK F_GETLK64
+# else
+# define SMB_F_GETLK F_GETLK
+# endif
+#endif
+
+#ifndef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+#ifndef MAX
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#endif
+
+#ifndef HAVE_STRERROR
+extern char *sys_errlist[];
+#define strerror(i) sys_errlist[i]
+#endif
+
+#ifndef HAVE_ERRNO_DECL
+extern int errno;
+#endif
+
+#ifdef HAVE_BROKEN_GETGROUPS
+#define GID_T int
+#else
+#define GID_T gid_t
+#endif
+
+#ifndef NGROUPS_MAX
+#define NGROUPS_MAX 32 /* Guess... */
+#endif
+
+/* Our own pstrings and fstrings */
+#include "pstring.h"
+
+/* Lists, trees, caching, database... */
+#include "xfile.h"
+#include "intl.h"
+#include "ubi_sLinkList.h"
+#include "ubi_dLinkList.h"
+#include "dlinklist.h"
+#include "../tdb/tdb.h"
+#include "../tdb/spinlock.h"
+#include "../tdb/tdbutil.h"
+#include "talloc.h"
+#include "nt_status.h"
+#include "ads.h"
+#include "interfaces.h"
+#include "hash.h"
+#include "trans2.h"
+#include "nterr.h"
+#include "ntioctl.h"
+#include "messages.h"
+#include "charset.h"
+#include "dynconfig.h"
+#include "adt_tree.h"
+
+#include "util_getent.h"
+
+#ifndef UBI_BINTREE_H
+#include "ubi_Cache.h"
+#endif /* UBI_BINTREE_H */
+
+#include "debugparse.h"
+
+#include "version.h"
+
+#include "privileges.h"
+
+#include "smb.h"
+
+#include "nameserv.h"
+
+#include "secrets.h"
+
+#include "byteorder.h"
+
+#include "rpc_creds.h"
+
+#include "mapping.h"
+
+#include "passdb.h"
+
+#include "ntdomain.h"
+
+#include "rpc_misc.h"
+
+#include "rpc_secdes.h"
+
+#include "genparser.h"
+
+#include "gums.h"
+
+#include "nt_printing.h"
+
+#include "msdfs.h"
+
+#include "smbprofile.h"
+
+#include "rap.h"
+
+#include "md5.h"
+#include "hmacmd5.h"
+
+#include "ntlmssp.h"
+
+#include "auth.h"
+
+#include "idmap.h"
+
+#include "client.h"
+
+#include "smbw.h"
+
+#include "session.h"
+
+#include "asn_1.h"
+
+#include "popt.h"
+
+#include "mangle.h"
+
+#include "module.h"
+
+#include "nsswitch/winbind_client.h"
+
+#include "spnego.h"
+
+/*
+ * Type for wide character dirent structure.
+ * Only d_name is defined by POSIX.
+ */
+
+typedef struct smb_wdirent {
+ wpstring d_name;
+} SMB_STRUCT_WDIRENT;
+
+/*
+ * Type for wide character passwd structure.
+ */
+
+typedef struct smb_wpasswd {
+ wfstring pw_name;
+ char *pw_passwd;
+ uid_t pw_uid;
+ gid_t pw_gid;
+ wpstring pw_gecos;
+ wpstring pw_dir;
+ wpstring pw_shell;
+} SMB_STRUCT_WPASSWD;
+
+/* used in net.c */
+struct functable {
+ const char *funcname;
+ int (*fn)(int argc, const char **argv);
+};
+
+
+/* Defines for wisXXX functions. */
+#define UNI_UPPER 0x1
+#define UNI_LOWER 0x2
+#define UNI_DIGIT 0x4
+#define UNI_XDIGIT 0x8
+#define UNI_SPACE 0x10
+
+#include "nsswitch/winbind_nss.h"
+
+/* forward declaration from printing.h to get around
+ header file dependencies */
+
+struct printjob;
+
+struct smb_ldap_privates;
+
+/* forward declarations from smbldap.c */
+
+#include "smbldap.h"
+#include "modconf.h"
+
+/***** automatically generated prototypes *****/
+#ifndef NO_PROTO_H
+#include "proto.h"
+#endif
+
+/* String routines */
+
+#include "srvstr.h"
+#include "safe_string.h"
+
+#ifdef __COMPAR_FN_T
+#define QSORT_CAST (__compar_fn_t)
+#endif
+
+#ifndef QSORT_CAST
+#define QSORT_CAST (int (*)(const void *, const void *))
+#endif
+
+#ifndef DEFAULT_PRINTING
+#ifdef HAVE_CUPS
+#define DEFAULT_PRINTING PRINT_CUPS
+#define PRINTCAP_NAME "cups"
+#elif defined(SYSV)
+#define DEFAULT_PRINTING PRINT_SYSV
+#define PRINTCAP_NAME "lpstat"
+#else
+#define DEFAULT_PRINTING PRINT_BSD
+#define PRINTCAP_NAME "/etc/printcap"
+#endif
+#endif
+
+#ifndef PRINTCAP_NAME
+#define PRINTCAP_NAME "/etc/printcap"
+#endif
+
+#ifndef SIGCLD
+#define SIGCLD SIGCHLD
+#endif
+
+#ifndef SIGRTMIN
+#define SIGRTMIN 32
+#endif
+
+#ifndef MAP_FILE
+#define MAP_FILE 0
+#endif
+
+#if defined(HAVE_PUTPRPWNAM) && defined(AUTH_CLEARTEXT_SEG_CHARS)
+#define OSF1_ENH_SEC 1
+#endif
+
+#ifndef ALLOW_CHANGE_PASSWORD
+#if (defined(HAVE_TERMIOS_H) && defined(HAVE_DUP2) && defined(HAVE_SETSID))
+#define ALLOW_CHANGE_PASSWORD 1
+#endif
+#endif
+
+/* what is the longest significant password available on your system?
+ Knowing this speeds up password searches a lot */
+#ifndef PASSWORD_LENGTH
+#define PASSWORD_LENGTH 8
+#endif
+
+#ifdef REPLACE_INET_NTOA
+#define inet_ntoa rep_inet_ntoa
+#endif
+
+#ifndef HAVE_PIPE
+#define SYNC_DNS 1
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+#ifndef INADDR_LOOPBACK
+#define INADDR_LOOPBACK 0x7f000001
+#endif
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+#ifndef HAVE_CRYPT
+#define crypt ufc_crypt
+#endif
+
+#ifndef O_ACCMODE
+#define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR)
+#endif
+
+#if defined(HAVE_CRYPT16) && defined(HAVE_GETAUTHUID)
+#define ULTRIX_AUTH 1
+#endif
+
+#ifndef HAVE_STRDUP
+char *strdup(const char *s);
+#endif
+
+#ifndef HAVE_STRNDUP
+char *strndup(const char *s, size_t size);
+#endif
+
+#ifndef HAVE_MEMMOVE
+void *memmove(void *dest,const void *src,int size);
+#endif
+
+#ifndef HAVE_INITGROUPS
+int initgroups(char *name,gid_t id);
+#endif
+
+#ifndef HAVE_RENAME
+int rename(const char *zfrom, const char *zto);
+#endif
+
+#ifndef HAVE_MKTIME
+time_t mktime(struct tm *t);
+#endif
+
+#ifndef HAVE_STRLCPY
+size_t strlcpy(char *d, const char *s, size_t bufsize);
+#endif
+
+#ifndef HAVE_STRLCAT
+size_t strlcat(char *d, const char *s, size_t bufsize);
+#endif
+
+#ifndef HAVE_FTRUNCATE
+int ftruncate(int f,long l);
+#endif
+
+#ifndef HAVE_STRNDUP
+char *strndup(const char *s, size_t n);
+#endif
+
+#ifndef HAVE_STRNLEN
+size_t strnlen(const char *s, size_t n);
+#endif
+
+#ifndef HAVE_STRTOUL
+unsigned long strtoul(const char *nptr, char **endptr, int base);
+#endif
+
+#ifndef HAVE_SETENV
+int setenv(const char *name, const char *value, int overwrite);
+#endif
+
+#if (defined(USE_SETRESUID) && !defined(HAVE_SETRESUID_DECL))
+/* stupid glibc */
+int setresuid(uid_t ruid, uid_t euid, uid_t suid);
+#endif
+#if (defined(USE_SETRESUID) && !defined(HAVE_SETRESGID_DECL))
+int setresgid(gid_t rgid, gid_t egid, gid_t sgid);
+#endif
+#ifndef HAVE_VASPRINTF_DECL
+int vasprintf(char **ptr, const char *format, va_list ap);
+#endif
+
+#ifdef REPLACE_GETPASS
+#define getpass(prompt) getsmbpass((prompt))
+#endif
+
+/*
+ * Some older systems seem not to have MAXHOSTNAMELEN
+ * defined.
+ */
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 254
+#endif
+
+/* yuck, I'd like a better way of doing this */
+#define DIRP_SIZE (256 + 32)
+
+/*
+ * glibc on linux doesn't seem to have MSG_WAITALL
+ * defined. I think the kernel has it though..
+ */
+
+#ifndef MSG_WAITALL
+#define MSG_WAITALL 0
+#endif
+
+/* default socket options. Dave Miller thinks we should default to TCP_NODELAY
+ given the socket IO pattern that Samba uses */
+#ifdef TCP_NODELAY
+#define DEFAULT_SOCKET_OPTIONS "TCP_NODELAY"
+#else
+#define DEFAULT_SOCKET_OPTIONS ""
+#endif
+
+/* Load header file for dynamic linking stuff */
+
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+/* dmalloc -- free heap debugger (dmalloc.org). This should be near
+ * the *bottom* of include files so as not to conflict. */
+#ifdef ENABLE_DMALLOC
+# include <dmalloc.h>
+#endif
+
+
+/* Some POSIX definitions for those without */
+
+#ifndef S_IFDIR
+#define S_IFDIR 0x4000
+#endif
+#ifndef S_ISDIR
+#define S_ISDIR(mode) ((mode & 0xF000) == S_IFDIR)
+#endif
+#ifndef S_IRWXU
+#define S_IRWXU 00700 /* read, write, execute: owner */
+#endif
+#ifndef S_IRUSR
+#define S_IRUSR 00400 /* read permission: owner */
+#endif
+#ifndef S_IWUSR
+#define S_IWUSR 00200 /* write permission: owner */
+#endif
+#ifndef S_IXUSR
+#define S_IXUSR 00100 /* execute permission: owner */
+#endif
+#ifndef S_IRWXG
+#define S_IRWXG 00070 /* read, write, execute: group */
+#endif
+#ifndef S_IRGRP
+#define S_IRGRP 00040 /* read permission: group */
+#endif
+#ifndef S_IWGRP
+#define S_IWGRP 00020 /* write permission: group */
+#endif
+#ifndef S_IXGRP
+#define S_IXGRP 00010 /* execute permission: group */
+#endif
+#ifndef S_IRWXO
+#define S_IRWXO 00007 /* read, write, execute: other */
+#endif
+#ifndef S_IROTH
+#define S_IROTH 00004 /* read permission: other */
+#endif
+#ifndef S_IWOTH
+#define S_IWOTH 00002 /* write permission: other */
+#endif
+#ifndef S_IXOTH
+#define S_IXOTH 00001 /* execute permission: other */
+#endif
+
+/* For sys_adminlog(). */
+#ifndef LOG_EMERG
+#define LOG_EMERG 0 /* system is unusable */
+#endif
+
+#ifndef LOG_ALERT
+#define LOG_ALERT 1 /* action must be taken immediately */
+#endif
+
+#ifndef LOG_CRIT
+#define LOG_CRIT 2 /* critical conditions */
+#endif
+
+#ifndef LOG_ERR
+#define LOG_ERR 3 /* error conditions */
+#endif
+
+#ifndef LOG_WARNING
+#define LOG_WARNING 4 /* warning conditions */
+#endif
+
+#ifndef LOG_NOTICE
+#define LOG_NOTICE 5 /* normal but significant condition */
+#endif
+
+#ifndef LOG_INFO
+#define LOG_INFO 6 /* informational */
+#endif
+
+#ifndef LOG_DEBUG
+#define LOG_DEBUG 7 /* debug-level messages */
+#endif
+
+/* NetBSD doesn't have these */
+#ifndef SHM_R
+#define SHM_R 0400
+#endif
+
+#ifndef SHM_W
+#define SHM_W 0200
+#endif
+
+#if HAVE_KERNEL_SHARE_MODES
+#ifndef LOCK_MAND
+#define LOCK_MAND 32 /* This is a mandatory flock */
+#define LOCK_READ 64 /* ... Which allows concurrent read operations */
+#define LOCK_WRITE 128 /* ... Which allows concurrent write operations */
+#define LOCK_RW 192 /* ... Which allows concurrent read & write ops */
+#endif
+#endif
+
+extern int DEBUGLEVEL;
+
+#define MAX_SEC_CTX_DEPTH 8 /* Maximum number of security contexts */
+
+
+#ifdef GLIBC_HACK_FCNTL64
+/* this is a gross hack. 64 bit locking is completely screwed up on
+ i386 Linux in glibc 2.1.95 (which ships with RedHat 7.0). This hack
+ "fixes" the problem with the current 2.4.0test kernels
+*/
+#define fcntl fcntl64
+#undef F_SETLKW
+#undef F_SETLK
+#define F_SETLK 13
+#define F_SETLKW 14
+#endif
+
+
+/* Needed for sys_dlopen/sys_dlsym/sys_dlclose */
+#ifndef RTLD_GLOBAL
+#define RTLD_GLOBAL 0
+#endif
+
+#ifndef RTLD_LAZY
+#define RTLD_LAZY 0
+#endif
+
+#ifndef RTLD_NOW
+#define RTLD_NOW 0
+#endif
+
+/* needed for some systems without iconv. Doesn't really matter
+ what error code we use */
+#ifndef EILSEQ
+#define EILSEQ EIO
+#endif
+
+/* add varargs prototypes with printf checking */
+int fdprintf(int , const char *, ...) PRINTF_ATTRIBUTE(2,3);
+int d_printf(const char *, ...) PRINTF_ATTRIBUTE(1,2);
+int d_fprintf(FILE *f, const char *, ...) PRINTF_ATTRIBUTE(2,3);
+#ifndef HAVE_SNPRINTF_DECL
+int snprintf(char *,size_t ,const char *, ...) PRINTF_ATTRIBUTE(3,4);
+#endif
+#ifndef HAVE_ASPRINTF_DECL
+int asprintf(char **,const char *, ...) PRINTF_ATTRIBUTE(2,3);
+#endif
+
+/* Fix prototype problem with non-C99 compliant snprintf implementations, esp
+ HPUX 11. Don't change the sense of this #if statement. Read the comments
+ in lib/snprint.c if you think you need to. See also bugzilla bug 174. */
+
+#if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
+#define snprintf smb_snprintf
+#endif
+
+void sys_adminlog(int priority, const char *format_str, ...) PRINTF_ATTRIBUTE(2,3);
+
+int pstr_sprintf(pstring s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+int fstr_sprintf(fstring s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+
+int d_vfprintf(FILE *f, const char *format, va_list ap) PRINTF_ATTRIBUTE(2,0);
+
+int smb_xvasprintf(char **ptr, const char *format, va_list ap) PRINTF_ATTRIBUTE(2,0);
+
+/* we used to use these fns, but now we have good replacements
+ for snprintf and vsnprintf */
+#define slprintf snprintf
+#define vslprintf vsnprintf
+
+
+/* we need to use __va_copy() on some platforms */
+#ifdef HAVE_VA_COPY
+#define VA_COPY(dest, src) va_copy(dest, src)
+#else
+#ifdef HAVE___VA_COPY
+#define VA_COPY(dest, src) __va_copy(dest, src)
+#else
+#define VA_COPY(dest, src) (dest) = (src)
+#endif
+#endif
+
+#ifndef HAVE_TIMEGM
+time_t timegm(struct tm *tm);
+#endif
+
+/*
+ * Veritas File System. Often in addition to native.
+ * Quotas different.
+ */
+#if defined(HAVE_SYS_FS_VX_QUOTA_H)
+#define VXFS_QUOTA
+#endif
+
+#if defined(HAVE_KRB5)
+
+#ifndef HAVE_KRB5_SET_REAL_TIME
+krb5_error_code krb5_set_real_time(krb5_context context, int32_t seconds, int32_t microseconds);
+#endif
+
+#ifndef HAVE_KRB5_SET_DEFAULT_TGS_KTYPES
+krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc);
+#endif
+
+#if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY)
+krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context, krb5_auth_context auth_context, krb5_keyblock *keyblock);
+#endif
+
+/* Samba wrapper function for krb5 functionality. */
+void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr);
+int create_kerberos_key_from_string(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype);
+void get_auth_data_from_tkt(DATA_BLOB *auth_data, krb5_ticket *tkt);
+krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt);
+krb5_error_code krb5_locate_kdc(krb5_context ctx, const krb5_data *realm, struct sockaddr **addr_pp, int *naddrs, int get_masters);
+krb5_error_code get_kerberos_allowed_etypes(krb5_context context, krb5_enctype **enctypes);
+void free_kerberos_etypes(krb5_context context, krb5_enctype *enctypes);
+BOOL get_krb5_smb_session_key(krb5_context context, krb5_auth_context auth_context, DATA_BLOB *session_key, BOOL remote);
+#endif /* HAVE_KRB5 */
+
+/* TRUE and FALSE are part of the C99 standard and gcc, but
+ unfortunately many vendor compilers don't support them. Use True
+ and False instead. */
+
+#ifdef TRUE
+#undef TRUE
+#endif
+#define TRUE __ERROR__XX__DONT_USE_TRUE
+
+#ifdef FALSE
+#undef FALSE
+#endif
+#define FALSE __ERROR__XX__DONT_USE_FALSE
+
+/* If we have blacklisted mmap() try to avoid using it accidentally by
+ undefining the HAVE_MMAP symbol. */
+
+#ifdef MMAP_BLACKLIST
+#undef HAVE_MMAP
+#endif
+
+#endif /* _INCLUDES_H */
diff --git a/source/include/interfaces.h b/source/include/interfaces.h
new file mode 100644
index 00000000000..3b786f1ebcb
--- /dev/null
+++ b/source/include/interfaces.h
@@ -0,0 +1,12 @@
+/*
+ This structure is used by lib/interfaces.c to return the list of network
+ interfaces on the machine
+*/
+
+#define MAX_INTERFACES 128
+
+struct iface_struct {
+ char name[16];
+ struct in_addr ip;
+ struct in_addr netmask;
+};
diff --git a/source/include/intl.h b/source/include/intl.h
new file mode 100644
index 00000000000..01fa3bad976
--- /dev/null
+++ b/source/include/intl.h
@@ -0,0 +1,25 @@
+/*
+ Unix SMB/CIFS implementation.
+ internationalisation headers
+ Copyright (C) Andrew Tridgell 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+/* ideally we would have a static mapping, but that precludes
+ dynamic loading. This is a reasonable compromise */
+#define _(x) lang_msg_rotate(x)
+#define N_(x) (x)
diff --git a/source/include/libsmb_internal.h b/source/include/libsmb_internal.h
new file mode 100644
index 00000000000..21fe47d4b29
--- /dev/null
+++ b/source/include/libsmb_internal.h
@@ -0,0 +1,67 @@
+#ifndef _LIBSMB_INTERNAL_H_
+#define _LIBSMB_INTERNAL_H_
+
+#define SMBC_MAX_NAME 1023
+#define SMBC_FILE_MODE (S_IFREG | 0444)
+#define SMBC_DIR_MODE (S_IFDIR | 0555)
+
+
+#include "../include/libsmbclient.h"
+
+
+struct _SMBCSRV {
+ struct cli_state cli;
+ dev_t dev;
+ BOOL no_pathinfo2;
+ int server_fd;
+
+ SMBCSRV *next, *prev;
+
+};
+
+/*
+ * Keep directory entries in a list
+ */
+struct smbc_dir_list {
+ struct smbc_dir_list *next;
+ struct smbc_dirent *dirent;
+};
+
+
+/*
+ * Structure for open file management
+ */
+struct _SMBCFILE {
+ int cli_fd;
+ char *fname;
+ off_t offset;
+ struct _SMBCSRV *srv;
+ BOOL file;
+ struct smbc_dir_list *dir_list, *dir_end, *dir_next;
+ int dir_type, dir_error;
+
+ SMBCFILE *next, *prev;
+};
+
+
+struct smbc_internal_data {
+
+ /** INTERNAL: is this handle initialized ?
+ */
+ int _initialized;
+
+ /** INTERNAL: dirent pointer location
+ */
+ char _dirent[512];
+
+ /** INTERNAL: server connection list
+ */
+ SMBCSRV * _servers;
+
+ /** INTERNAL: open file/dir list
+ */
+ SMBCFILE * _files;
+};
+
+
+#endif
diff --git a/source/include/libsmbclient.h b/source/include/libsmbclient.h
new file mode 100644
index 00000000000..68c4a053d1b
--- /dev/null
+++ b/source/include/libsmbclient.h
@@ -0,0 +1,1947 @@
+/*=====================================================================
+ Unix SMB/Netbios implementation.
+ SMB client library API definitions
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000
+ Copyright (C) John Terpsra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 2003
+
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ =====================================================================*/
+
+#ifndef SMBCLIENT_H_INCLUDED
+#define SMBCLIENT_H_INCLUDED
+
+/*-------------------------------------------------------------------*/
+/* The following are special comments to instruct DOXYGEN (automated
+ * documentation tool:
+*/
+/** \defgroup libsmbclient
+*/
+/** \defgroup structure Data Structures Type and Constants
+* \ingroup libsmbclient
+* Data structures, types, and constants
+*/
+/** \defgroup callback Callback function types
+* \ingroup libsmbclient
+* Callback functions
+*/
+/** \defgroup file File Functions
+* \ingroup libsmbclient
+* Functions used to access individual file contents
+*/
+/** \defgroup directory Directory Functions
+* \ingroup libsmbclient
+* Functions used to access directory entries
+*/
+/** \defgroup attribute Attributes Functions
+* \ingroup libsmbclient
+* Functions used to view or change file and directory attributes
+*/
+/** \defgroup print Print Functions
+* \ingroup libsmbclient
+* Functions used to access printing functionality
+*/
+/** \defgroup misc Miscellaneous Functions
+* \ingroup libsmbclient
+* Functions that don't fit in to other categories
+*/
+/*-------------------------------------------------------------------*/
+
+/* Make sure we have the following includes for now ... */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <utime.h>
+
+#define SMBC_BASE_FD 10000 /* smallest file descriptor returned */
+
+#define SMBC_WORKGROUP 1
+#define SMBC_SERVER 2
+#define SMBC_FILE_SHARE 3
+#define SMBC_PRINTER_SHARE 4
+#define SMBC_COMMS_SHARE 5
+#define SMBC_IPC_SHARE 6
+#define SMBC_DIR 7
+#define SMBC_FILE 8
+#define SMBC_LINK 9
+
+/**@ingroup structure
+ * Structure that represents a directory entry.
+ *
+ */
+struct smbc_dirent
+{
+ /** Type of entity.
+ SMBC_WORKGROUP=1,
+ SMBC_SERVER=2,
+ SMBC_FILE_SHARE=3,
+ SMBC_PRINTER_SHARE=4,
+ SMBC_COMMS_SHARE=5,
+ SMBC_IPC_SHARE=6,
+ SMBC_DIR=7,
+ SMBC_FILE=8,
+ SMBC_LINK=9,*/
+ unsigned int smbc_type;
+
+ /** Length of this smbc_dirent in bytes
+ */
+ unsigned int dirlen;
+ /** The length of the comment string in bytes (includes null
+ * terminator)
+ */
+ unsigned int commentlen;
+ /** Points to the null terminated comment string
+ */
+ char *comment;
+ /** The length of the name string in bytes (includes null
+ * terminator)
+ */
+ unsigned int namelen;
+ /** Points to the null terminated name string
+ */
+ char name[1];
+};
+
+/*
+ * Flags for smbc_setxattr()
+ * Specify a bitwise OR of these, or 0 to add or replace as necessary
+ */
+#define SMBC_XATTR_FLAG_CREATE 0x1 /* fail if attr already exists */
+#define SMBC_XATTR_FLAG_REPLACE 0x2 /* fail if attr does not exist */
+
+
+#ifndef ENOATTR
+# define ENOATTR ENOENT /* No such attribute */
+#endif
+
+
+
+
+/**@ingroup structure
+ * Structure that represents a print job.
+ *
+ */
+#ifndef _CLIENT_H
+struct print_job_info
+{
+ /** numeric ID of the print job
+ */
+ unsigned short id;
+
+ /** represents print job priority (lower numbers mean higher priority)
+ */
+ unsigned short priority;
+
+ /** Size of the print job
+ */
+ size_t size;
+
+ /** Name of the user that owns the print job
+ */
+ char user[128];
+
+ /** Name of the print job. This will have no name if an anonymous print
+ * file was opened. Ie smb://server/printer
+ */
+ char name[128];
+
+ /** Time the print job was spooled
+ */
+ time_t t;
+};
+#endif /* _CLIENT_H */
+
+
+/**@ingroup structure
+ * Server handle
+ */
+typedef struct _SMBCSRV SMBCSRV;
+
+/**@ingroup structure
+ * File or directory handle
+ */
+typedef struct _SMBCFILE SMBCFILE;
+
+/**@ingroup structure
+ * File or directory handle
+ */
+typedef struct _SMBCCTX SMBCCTX;
+
+
+
+
+
+/**@ingroup callback
+ * Authentication callback function type.
+ *
+ * Type for the the authentication function called by the library to
+ * obtain authentication credentals
+ *
+ * @param srv Server being authenticated to
+ *
+ * @param shr Share being authenticated to
+ *
+ * @param wg Pointer to buffer containing a "hint" for the
+ * workgroup to be authenticated. Should be filled in
+ * with the correct workgroup if the hint is wrong.
+ *
+ * @param wglen The size of the workgroup buffer in bytes
+ *
+ * @param un Pointer to buffer containing a "hint" for the
+ * user name to be use for authentication. Should be
+ * filled in with the correct workgroup if the hint is
+ * wrong.
+ *
+ * @param unlen The size of the username buffer in bytes
+ *
+ * @param pw Pointer to buffer containing to which password
+ * copied
+ *
+ * @param pwlen The size of the password buffer in bytes
+ *
+ */
+typedef void (*smbc_get_auth_data_fn)(const char *srv,
+ const char *shr,
+ char *wg, int wglen,
+ char *un, int unlen,
+ char *pw, int pwlen);
+
+
+/**@ingroup callback
+ * Print job info callback function type.
+ *
+ * @param i pointer to print job information structure
+ *
+ */
+typedef void (*smbc_list_print_job_fn)(struct print_job_info *i);
+
+
+/**@ingroup callback
+ * Check if a server is still good
+ *
+ * @param c pointer to smb context
+ *
+ * @param srv pointer to server to check
+ *
+ * @return 0 when connection is good. 1 on error.
+ *
+ */
+typedef int (*smbc_check_server_fn)(SMBCCTX * c, SMBCSRV *srv);
+
+/**@ingroup callback
+ * Remove a server if unused
+ *
+ * @param c pointer to smb context
+ *
+ * @param srv pointer to server to remove
+ *
+ * @return 0 on success. 1 on failure.
+ *
+ */
+typedef int (*smbc_remove_unused_server_fn)(SMBCCTX * c, SMBCSRV *srv);
+
+
+/**@ingroup callback
+ * Add a server to the cache system
+ *
+ * @param c pointer to smb context
+ *
+ * @param srv pointer to server to add
+ *
+ * @param server server name
+ *
+ * @param share share name
+ *
+ * @param workgroup workgroup used to connect
+ *
+ * @param username username used to connect
+ *
+ * @return 0 on success. 1 on failure.
+ *
+ */
+typedef int (*smbc_add_cached_srv_fn) (SMBCCTX * c, SMBCSRV *srv,
+ const char * server, const char * share,
+ const char * workgroup, const char * username);
+
+/**@ingroup callback
+ * Look up a server in the cache system
+ *
+ * @param c pointer to smb context
+ *
+ * @param server server name to match
+ *
+ * @param share share name to match
+ *
+ * @param workgroup workgroup to match
+ *
+ * @param username username to match
+ *
+ * @return pointer to SMBCSRV on success. NULL on failure.
+ *
+ */
+typedef SMBCSRV * (*smbc_get_cached_srv_fn) (SMBCCTX * c, const char * server,
+ const char * share, const char * workgroup,
+ const char * username);
+
+/**@ingroup callback
+ * Check if a server is still good
+ *
+ * @param c pointer to smb context
+ *
+ * @param srv pointer to server to remove
+ *
+ * @return 0 when found and removed. 1 on failure.
+ *
+ */
+typedef int (*smbc_remove_cached_srv_fn)(SMBCCTX * c, SMBCSRV *srv);
+
+
+/**@ingroup callback
+ * Try to remove all servers from the cache system and disconnect
+ *
+ * @param c pointer to smb context
+ *
+ * @return 0 when found and removed. 1 on failure.
+ *
+ */
+typedef int (*smbc_purge_cached_fn) (SMBCCTX * c);
+
+
+
+
+/**@ingroup structure
+ * Structure that contains a client context information
+ * This structure is know as SMBCCTX
+ */
+struct _SMBCCTX {
+ /** debug level
+ */
+ int debug;
+
+ /** netbios name used for making connections
+ */
+ char * netbios_name;
+
+ /** workgroup name used for making connections
+ */
+ char * workgroup;
+
+ /** username used for making connections
+ */
+ char * user;
+
+ /** timeout used for waiting on connections / response data (in milliseconds)
+ */
+ int timeout;
+
+ /** callable functions for files:
+ * For usage and return values see the smbc_* functions
+ */
+ SMBCFILE * (*open) (SMBCCTX *c, const char *fname, int flags, mode_t mode);
+ SMBCFILE * (*creat) (SMBCCTX *c, const char *path, mode_t mode);
+ ssize_t (*read) (SMBCCTX *c, SMBCFILE *file, void *buf, size_t count);
+ ssize_t (*write) (SMBCCTX *c, SMBCFILE *file, void *buf, size_t count);
+ int (*unlink) (SMBCCTX *c, const char *fname);
+ int (*rename) (SMBCCTX *ocontext, const char *oname,
+ SMBCCTX *ncontext, const char *nname);
+ off_t (*lseek) (SMBCCTX *c, SMBCFILE * file, off_t offset, int whence);
+ int (*stat) (SMBCCTX *c, const char *fname, struct stat *st);
+ int (*fstat) (SMBCCTX *c, SMBCFILE *file, struct stat *st);
+ int (*close) (SMBCCTX *c, SMBCFILE *file);
+
+ /** callable functions for dirs
+ */
+ SMBCFILE * (*opendir) (SMBCCTX *c, const char *fname);
+ int (*closedir)(SMBCCTX *c, SMBCFILE *dir);
+ struct smbc_dirent * (*readdir)(SMBCCTX *c, SMBCFILE *dir);
+ int (*getdents)(SMBCCTX *c, SMBCFILE *dir,
+ struct smbc_dirent *dirp, int count);
+ int (*mkdir) (SMBCCTX *c, const char *fname, mode_t mode);
+ int (*rmdir) (SMBCCTX *c, const char *fname);
+ off_t (*telldir) (SMBCCTX *c, SMBCFILE *dir);
+ int (*lseekdir)(SMBCCTX *c, SMBCFILE *dir, off_t offset);
+ int (*fstatdir)(SMBCCTX *c, SMBCFILE *dir, struct stat *st);
+ int (*chmod)(SMBCCTX *c, const char *fname, mode_t mode);
+ int (*utimes)(SMBCCTX *c,
+ const char *fname, struct timeval *tbuf);
+ int (*setxattr)(SMBCCTX *context,
+ const char *fname,
+ const char *name,
+ const void *value,
+ size_t size,
+ int flags);
+ int (*getxattr)(SMBCCTX *context,
+ const char *fname,
+ const char *name,
+ const void *value,
+ size_t size);
+ int (*removexattr)(SMBCCTX *context,
+ const char *fname,
+ const char *name);
+ int (*listxattr)(SMBCCTX *context,
+ const char *fname,
+ char *list,
+ size_t size);
+
+ /** callable functions for printing
+ */
+ int (*print_file)(SMBCCTX *c_file, const char *fname,
+ SMBCCTX *c_print, const char *printq);
+ SMBCFILE * (*open_print_job)(SMBCCTX *c, const char *fname);
+ int (*list_print_jobs)(SMBCCTX *c, const char *fname, smbc_list_print_job_fn fn);
+ int (*unlink_print_job)(SMBCCTX *c, const char *fname, int id);
+
+
+ /** Callbacks
+ * These callbacks _always_ have to be initialized because they will not be checked
+ * at dereference for increased speed.
+ */
+ struct _smbc_callbacks {
+ /** authentication function callback: called upon auth requests
+ */
+ smbc_get_auth_data_fn auth_fn;
+
+ /** check if a server is still good
+ */
+ smbc_check_server_fn check_server_fn;
+
+ /** remove a server if unused
+ */
+ smbc_remove_unused_server_fn remove_unused_server_fn;
+
+ /** Cache subsystem
+ * For an example cache system see samba/source/libsmb/libsmb_cache.c
+ * Cache subsystem functions follow.
+ */
+
+ /** server cache addition
+ */
+ smbc_add_cached_srv_fn add_cached_srv_fn;
+
+ /** server cache lookup
+ */
+ smbc_get_cached_srv_fn get_cached_srv_fn;
+
+ /** server cache removal
+ */
+ smbc_remove_cached_srv_fn remove_cached_srv_fn;
+
+ /** server cache purging, try to remove all cached servers (disconnect)
+ */
+ smbc_purge_cached_fn purge_cached_fn;
+ } callbacks;
+
+
+ /** Space to store private data of the server cache.
+ */
+ struct smbc_server_cache * server_cache;
+
+ /** INTERNAL DATA
+ * do _NOT_ touch this from your program !
+ */
+ struct smbc_internal_data * internal;
+
+};
+
+
+/**@ingroup misc
+ * Create a new SBMCCTX (a context).
+ *
+ * Must be called before the context is passed to smbc_context_init()
+ *
+ * @return The given SMBCCTX pointer on success, NULL on error with errno set:
+ * - ENOMEM Out of memory
+ *
+ * @see smbc_free_context(), smbc_init_context()
+ *
+ * @note Do not forget to smbc_init_context() the returned SMBCCTX pointer !
+ */
+SMBCCTX * smbc_new_context(void);
+
+/**@ingroup misc
+ * Delete a SBMCCTX (a context) acquired from smbc_new_context().
+ *
+ * The context will be deleted if possible.
+ *
+ * @param context A pointer to a SMBCCTX obtained from smbc_new_context()
+ *
+ * @param shutdown_ctx If 1, all connections and files will be closed even if they are busy.
+ *
+ *
+ * @return Returns 0 on succes. Returns 1 on failure with errno set:
+ * - EBUSY Server connections are still used, Files are open or cache
+ * could not be purged
+ * - EBADF context == NULL
+ *
+ * @see smbc_new_context()
+ *
+ * @note It is advised to clean up all the contexts with shutdown_ctx set to 1
+ * just before exit()'ing. When shutdown_ctx is 0, this function can be
+ * use in periodical cleanup functions for example.
+ */
+int smbc_free_context(SMBCCTX * context, int shutdown_ctx);
+
+
+/**@ingroup misc
+ * Initialize a SBMCCTX (a context).
+ *
+ * Must be called before using any SMBCCTX API function
+ *
+ * @param context A pointer to a SMBCCTX obtained from smbc_new_context()
+ *
+ * @return A pointer to the given SMBCCTX on success, NULL on error with errno set:
+ * - EBADF NULL context given
+ * - ENOMEM Out of memory
+ * - ENOENT The smb.conf file would not load
+ *
+ * @see smbc_new_context()
+ *
+ * @note my_context = smbc_init_context(smbc_new_context()) is perfectly safe,
+ * but it might leak memory on smbc_context_init() failure. Avoid this.
+ * You'll have to call smbc_free_context() yourself on failure.
+ */
+
+SMBCCTX * smbc_init_context(SMBCCTX * context);
+
+/**@ingroup misc
+ * Initialize the samba client library.
+ *
+ * Must be called before using any of the smbclient API function
+ *
+ * @param fn The function that will be called to obtaion
+ * authentication credentials.
+ *
+ * @param debug Allows caller to set the debug level. Can be
+ * changed in smb.conf file. Allows caller to set
+ * debugging if no smb.conf.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - ENOMEM Out of memory
+ * - ENOENT The smb.conf file would not load
+ *
+ */
+
+int smbc_init(smbc_get_auth_data_fn fn, int debug);
+
+/**@ingroup misc
+ * Set or retrieve the compatibility library's context pointer
+ *
+ * @param context New context to use, or NULL. If a new context is provided,
+ * it must have allocated with smbc_new_context() and
+ * initialized with smbc_init_context(), followed, optionally,
+ * by some manual changes to some of the non-internal fields.
+ *
+ * @return The old context.
+ *
+ * @see smbc_new_context(), smbc_init_context(), smbc_init()
+ *
+ * @note This function may be called prior to smbc_init() to force
+ * use of the next context without any internal calls to
+ * smbc_new_context() or smbc_init_context(). It may also
+ * be called after smbc_init() has already called those two
+ * functions, to replace the existing context with a new one.
+ * Care should be taken, in this latter case, to ensure that
+ * the server cache and any data allocated by the
+ * authentication functions have been freed, if necessary.
+ */
+
+SMBCCTX * smbc_set_context(SMBCCTX * new_context);
+
+/**@ingroup file
+ * Open a file on an SMB server.
+ *
+ * @param furl The smb url of the file to be opened.
+ *
+ * @param flags Is one of O_RDONLY, O_WRONLY or O_RDWR which
+ * request opening the file read-only,write-only
+ * or read/write. flags may also be bitwise-or'd with
+ * one or more of the following:
+ * O_CREAT - If the file does not exist it will be
+ * created.
+ * O_EXCL - When used with O_CREAT, if the file
+ * already exists it is an error and the open will
+ * fail.
+ * O_TRUNC - If the file already exists it will be
+ * truncated.
+ * O_APPEND The file is opened in append mode
+ *
+ * @param mode mode specifies the permissions to use if a new
+ * file is created. It is modified by the
+ * process's umask in the usual way: the permissions
+ * of the created file are (mode & ~umask)
+ *
+ * Not currently use, but there for future use.
+ * We will map this to SYSTEM, HIDDEN, etc bits
+ * that reverses the mapping that smbc_fstat does.
+ *
+ * @return Valid file handle, < 0 on error with errno set:
+ * - ENOMEM Out of memory
+ * - EINVAL if an invalid parameter passed, like no
+ * file, or smbc_init not called.
+ * - EEXIST pathname already exists and O_CREAT and
+ * O_EXCL were used.
+ * - EISDIR pathname refers to a directory and
+ * the access requested involved writing.
+ * - EACCES The requested access to the file is not
+ * allowed
+ * - ENODEV The requested share does not exist
+ * - ENOTDIR A file on the path is not a directory
+ * - ENOENT A directory component in pathname does
+ * not exist.
+ *
+ * @see smbc_creat()
+ *
+ * @note This call uses an underlying routine that may create
+ * a new connection to the server specified in the URL.
+ * If the credentials supplied in the URL, or via the
+ * auth_fn in the smbc_init call, fail, this call will
+ * try again with an empty username and password. This
+ * often gets mapped to the guest account on some machines.
+ */
+
+int smbc_open(const char *furl, int flags, mode_t mode);
+
+/**@ingroup file
+ * Create a file on an SMB server.
+ *
+ * Same as calling smbc_open() with flags = O_CREAT|O_WRONLY|O_TRUNC
+ *
+ * @param furl The smb url of the file to be created
+ *
+ * @param mode mode specifies the permissions to use if a new
+ * file is created. It is modified by the
+ * process's umask in the usual way: the permissions
+ * of the created file are (mode & ~umask)
+ *
+ * NOTE, the above is not true. We are dealing with
+ * an SMB server, which has no concept of a umask!
+ *
+ * @return Valid file handle, < 0 on error with errno set:
+ * - ENOMEM Out of memory
+ * - EINVAL if an invalid parameter passed, like no
+ * file, or smbc_init not called.
+ * - EEXIST pathname already exists and O_CREAT and
+ * O_EXCL were used.
+ * - EISDIR pathname refers to a directory and
+ * the access requested involved writing.
+ * - EACCES The requested access to the file is not
+ * allowed
+ * - ENOENT A directory component in pathname does
+ * not exist.
+ * - ENODEV The requested share does not exist.
+ * @see smbc_open()
+ *
+ */
+
+int smbc_creat(const char *furl, mode_t mode);
+
+/**@ingroup file
+ * Read from a file using an opened file handle.
+ *
+ * @param fd Open file handle from smbc_open() or smbc_creat()
+ *
+ * @param buf Pointer to buffer to recieve read data
+ *
+ * @param bufsize Size of buf in bytes
+ *
+ * @return Number of bytes read, < 0 on error with errno set:
+ * - EISDIR fd refers to a directory
+ * - EBADF fd is not a valid file descriptor or
+ * is not open for reading.
+ * - EINVAL fd is attached to an object which is
+ * unsuitable for reading, or no buffer passed or
+ * smbc_init not called.
+ *
+ * @see smbc_open(), smbc_write()
+ *
+ */
+ssize_t smbc_read(int fd, void *buf, size_t bufsize);
+
+
+/**@ingroup file
+ * Write to a file using an opened file handle.
+ *
+ * @param fd Open file handle from smbc_open() or smbc_creat()
+ *
+ * @param buf Pointer to buffer to recieve read data
+ *
+ * @param bufsize Size of buf in bytes
+ *
+ * @return Number of bytes written, < 0 on error with errno set:
+ * - EISDIR fd refers to a directory.
+ * - EBADF fd is not a valid file descriptor or
+ * is not open for reading.
+ * - EINVAL fd is attached to an object which is
+ * unsuitable for reading, or no buffer passed or
+ * smbc_init not called.
+ *
+ * @see smbc_open(), smbc_read()
+ *
+ */
+ssize_t smbc_write(int fd, void *buf, size_t bufsize);
+
+
+/**@ingroup file
+ * Seek to a specific location in a file.
+ *
+ * @param fd Open file handle from smbc_open() or smbc_creat()
+ *
+ * @param offset Offset in bytes from whence
+ *
+ * @param whence A location in the file:
+ * - SEEK_SET The offset is set to offset bytes from
+ * the beginning of the file
+ * - SEEK_CUR The offset is set to current location
+ * plus offset bytes.
+ * - SEEK_END The offset is set to the size of the
+ * file plus offset bytes.
+ *
+ * @return Upon successful completion, lseek returns the
+ * resulting offset location as measured in bytes
+ * from the beginning of the file. Otherwise, a value
+ * of (off_t)-1 is returned and errno is set to
+ * indicate the error:
+ * - EBADF Fildes is not an open file descriptor.
+ * - EINVAL Whence is not a proper value or smbc_init
+ * not called.
+ *
+ * @todo Are all the whence values really supported?
+ *
+ * @todo Are errno values complete and correct?
+ */
+off_t smbc_lseek(int fd, off_t offset, int whence);
+
+
+/**@ingroup file
+ * Close an open file handle.
+ *
+ * @param fd The file handle to close
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EBADF fd isn't a valid open file descriptor
+ * - EINVAL smbc_init() failed or has not been called
+ *
+ * @see smbc_open(), smbc_creat()
+ */
+int smbc_close(int fd);
+
+
+/**@ingroup directory
+ * Unlink (delete) a file or directory.
+ *
+ * @param furl The smb url of the file to delete
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EACCES or EPERM Write access to the directory
+ * containing pathname is not allowed or one
+ * of the directories in pathname did not allow
+ * search (execute) permission
+ * - ENOENT A directory component in pathname does
+ * not exist
+ * - EINVAL NULL was passed in the file param or
+ * smbc_init not called.
+ * - EACCES You do not have access to the file
+ * - ENOMEM Insufficient kernel memory was available
+ *
+ * @see smbc_rmdir()s
+ *
+ * @todo Are errno values complete and correct?
+ */
+int smbc_unlink(const char *furl);
+
+
+/**@ingroup directory
+ * Rename or move a file or directory.
+ *
+ * @param ourl The original smb url (source url) of file or
+ * directory to be moved
+ *
+ * @param nurl The new smb url (destination url) of the file
+ * or directory after the move. Currently nurl must
+ * be on the same share as ourl.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EISDIR nurl is an existing directory, but ourl is
+ * not a directory.
+ * - EEXIST nurl is a non-empty directory,
+ * i.e., contains entries other than "." and ".."
+ * - EINVAL The new url contained a path prefix
+ * of the old, or, more generally, an attempt was
+ * made to make a directory a subdirectory of itself
+ * or smbc_init not called.
+ * - ENOTDIR A component used as a directory in ourl
+ * or nurl path is not, in fact, a directory. Or,
+ * ourl is a directory, and newpath exists but is not
+ * a directory.
+ * - EACCES or EPERM Write access to the directory
+ * containing ourl or nurl is not allowed for the
+ * process's effective uid, or one of the
+ * directories in ourl or nurl did not allow search
+ * (execute) permission, or ourl was a directory
+ * and did not allow write permission.
+ * - ENOENT A directory component in ourl or nurl
+ * does not exist.
+ * - EXDEV Rename across shares not supported.
+ * - ENOMEM Insufficient kernel memory was available.
+ * - EEXIST The target file, nurl, already exists.
+ *
+ *
+ * @todo Are we going to support copying when urls are not on the same
+ * share? I say no... NOTE. I agree for the moment.
+ *
+ */
+int smbc_rename(const char *ourl, const char *nurl);
+
+
+/**@ingroup directory
+ * Open a directory used to obtain directory entries.
+ *
+ * @param durl The smb url of the directory to open
+ *
+ * @return Valid directory handle. < 0 on error with errno set:
+ * - EACCES Permission denied.
+ * - EINVAL A NULL file/URL was passed, or the URL would
+ * not parse, or was of incorrect form or smbc_init not
+ * called.
+ * - ENOENT durl does not exist, or name is an
+ * - ENOMEM Insufficient memory to complete the
+ * operation.
+ * - ENOTDIR name is not a directory.
+ * - EPERM the workgroup could not be found.
+ * - ENODEV the workgroup or server could not be found.
+ *
+ * @see smbc_getdents(), smbc_readdir(), smbc_closedir()
+ *
+ */
+int smbc_opendir(const char *durl);
+
+
+/**@ingroup directory
+ * Close a directory handle opened by smbc_opendir().
+ *
+ * @param dh Directory handle to close
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EBADF dh is an invalid directory handle
+ *
+ * @see smbc_opendir()
+ */
+int smbc_closedir(int dh);
+
+
+/**@ingroup directory
+ * Get multiple directory entries.
+ *
+ * smbc_getdents() reads as many dirent structures from the an open
+ * directory handle into a specified memory area as will fit.
+ *
+ * @param dh Valid directory as returned by smbc_opendir()
+ *
+ * @param dirp pointer to buffer that will receive the directory
+ * entries.
+ *
+ * @param count The size of the dirp buffer in bytes
+ *
+ * @returns If any dirents returned, return will indicate the
+ * total size. If there were no more dirents available,
+ * 0 is returned. < 0 indicates an error.
+ * - EBADF Invalid directory handle
+ * - EINVAL Result buffer is too small or smbc_init
+ * not called.
+ * - ENOENT No such directory.
+ * @see , smbc_dirent, smbc_readdir(), smbc_open()
+ *
+ * @todo Are errno values complete and correct?
+ *
+ * @todo Add example code so people know how to parse buffers.
+ */
+int smbc_getdents(unsigned int dh, struct smbc_dirent *dirp, int count);
+
+
+/**@ingroup directory
+ * Get a single directory entry.
+ *
+ * @param dh Valid directory as returned by smbc_opendir()
+ *
+ * @return A pointer to a smbc_dirent structure, or NULL if an
+ * error occurs or end-of-directory is reached:
+ * - EBADF Invalid directory handle
+ * - EINVAL smbc_init() failed or has not been called
+ *
+ * @see smbc_dirent, smbc_getdents(), smbc_open()
+ */
+struct smbc_dirent* smbc_readdir(unsigned int dh);
+
+
+/**@ingroup directory
+ * Get the current directory offset.
+ *
+ * smbc_telldir() may be used in conjunction with smbc_readdir() and
+ * smbc_lseekdir().
+ *
+ * @param dh Valid directory as returned by smbc_opendir()
+ *
+ * @return The current location in the directory stream or -1
+ * if an error occur. The current location is not
+ * an offset. Becuase of the implementation, it is a
+ * handle that allows the library to find the entry
+ * later.
+ * - EBADF dh is not a valid directory handle
+ * - EINVAL smbc_init() failed or has not been called
+ * - ENOTDIR if dh is not a directory
+ *
+ * @see smbc_readdir()
+ *
+ */
+off_t smbc_telldir(int dh);
+
+
+/**@ingroup directory
+ * lseek on directories.
+ *
+ * smbc_lseekdir() may be used in conjunction with smbc_readdir() and
+ * smbc_telldir(). (rewind by smbc_lseekdir(fd, NULL))
+ *
+ * @param fd Valid directory as returned by smbc_opendir()
+ *
+ * @param offset The offset (as returned by smbc_telldir). Can be
+ * NULL, in which case we will rewind
+ *
+ * @return 0 on success, -1 on failure
+ * - EBADF dh is not a valid directory handle
+ * - ENOTDIR if dh is not a directory
+ * - EINVAL offset did not refer to a valid dirent or
+ * smbc_init not called.
+ *
+ * @see smbc_telldir()
+ *
+ *
+ * @todo In what does the reture and errno values mean?
+ */
+int smbc_lseekdir(int fd, off_t offset);
+
+/**@ingroup directory
+ * Create a directory.
+ *
+ * @param durl The url of the directory to create
+ *
+ * @param mode Specifies the permissions to use. It is modified
+ * by the process's umask in the usual way: the
+ * permissions of the created file are (mode & ~umask).
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EEXIST directory url already exists
+ * - EACCES The parent directory does not allow write
+ * permission to the process, or one of the directories
+ * - ENOENT A directory component in pathname does not
+ * exist.
+ * - EINVAL NULL durl passed or smbc_init not called.
+ * - ENOMEM Insufficient memory was available.
+ *
+ * @see smbc_rmdir()
+ *
+ */
+int smbc_mkdir(const char *durl, mode_t mode);
+
+
+/**@ingroup directory
+ * Remove a directory.
+ *
+ * @param durl The smb url of the directory to remove
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EACCES or EPERM Write access to the directory
+ * containing pathname was not allowed.
+ * - EINVAL durl is NULL or smbc_init not called.
+ * - ENOENT A directory component in pathname does not
+ * exist.
+ * - ENOTEMPTY directory contains entries.
+ * - ENOMEM Insufficient kernel memory was available.
+ *
+ * @see smbc_mkdir(), smbc_unlink()
+ *
+ * @todo Are errno values complete and correct?
+ */
+int smbc_rmdir(const char *durl);
+
+
+/**@ingroup attribute
+ * Get information about a file or directory.
+ *
+ * @param url The smb url to get information for
+ *
+ * @param st pointer to a buffer that will be filled with
+ * standard Unix struct stat information.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - ENOENT A component of the path file_name does not
+ * exist.
+ * - EINVAL a NULL url was passed or smbc_init not called.
+ * - EACCES Permission denied.
+ * - ENOMEM Out of memory
+ * - ENOTDIR The target dir, url, is not a directory.
+ *
+ * @see Unix stat()
+ *
+ */
+int smbc_stat(const char *url, struct stat *st);
+
+
+/**@ingroup attribute
+ * Get file information via an file descriptor.
+ *
+ * @param fd Open file handle from smbc_open() or smbc_creat()
+ *
+ * @param st pointer to a buffer that will be filled with
+ * standard Unix struct stat information.
+ *
+ * @return EBADF filedes is bad.
+ * - EACCES Permission denied.
+ * - EBADF fd is not a valid file descriptor
+ * - EINVAL Problems occurred in the underlying routines
+ * or smbc_init not called.
+ * - ENOMEM Out of memory
+ *
+ * @see smbc_stat(), Unix stat()
+ *
+ */
+int smbc_fstat(int fd, struct stat *st);
+
+
+/**@ingroup attribue
+ * Change the ownership of a file or directory.
+ *
+ * @param url The smb url of the file or directory to change
+ * ownership of.
+ *
+ * @param owner I have no idea?
+ *
+ * @param group I have not idea?
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EPERM The effective UID does not match the owner
+ * of the file, and is not zero; or the owner or group
+ * were specified incorrectly.
+ * - ENOENT The file does not exist.
+ * - ENOMEM Insufficient was available.
+ * - ENOENT file or directory does not exist
+ *
+ * @todo Are we actually going to be able to implement this function
+ *
+ * @todo How do we abstract owner and group uid and gid?
+ *
+ */
+int smbc_chown(const char *url, uid_t owner, gid_t group);
+
+
+/**@ingroup attribute
+ * Change the permissions of a file.
+ *
+ * @param url The smb url of the file or directory to change
+ * permissions of
+ *
+ * @param mode The permissions to set:
+ * - Put good explaination of permissions here!
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EPERM The effective UID does not match the owner
+ * of the file, and is not zero
+ * - ENOENT The file does not exist.
+ * - ENOMEM Insufficient was available.
+ * - ENOENT file or directory does not exist
+ *
+ * @todo Actually implement this fuction?
+ *
+ * @todo Are errno values complete and correct?
+ */
+int smbc_chmod(const char *url, mode_t mode);
+
+/**@ingroup attribute
+ * Change the last modification time on a file
+ *
+ * @param url The smb url of the file or directory to change
+ * the modification time of
+ *
+ * @param tbuf A timeval structure which contains the desired
+ * modification time. NOTE: Only the tv_sec field is
+ * used. The tv_usec (microseconds) portion is ignored.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * - EPERM Permission was denied.
+ *
+ */
+int smbc_utimes(const char *url, struct timeval *tbuf);
+
+#ifdef HAVE_UTIME_H
+/**@ingroup attribute
+ * Change the last modification time on a file
+ *
+ * @param url The smb url of the file or directory to change
+ * the modification time of
+ *
+ * @param utbuf A utimebuf structure which contains the desired
+ * modification time. NOTE: Although the structure contains
+ * an access time as well, the access time value is ignored.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * - ENOMEM No memory was available for internal needs
+ * - EPERM Permission was denied.
+ *
+ */
+int smbc_utime(const char *fname, struct utimbuf *utbuf);
+#endif
+
+/**@ingroup attribute
+ * Set extended attributes for a file. This is used for modifying a file's
+ * security descriptor (i.e. owner, group, and access control list)
+ *
+ * @param url The smb url of the file or directory to set extended
+ * attributes for.
+ *
+ * @param name The name of an attribute to be changed. Names are of
+ * one of the following forms:
+ *
+ * system.nt_sec_desc.<attribute name>
+ * system.nt_sec_desc.*
+ * system.nt_sec_desc.*+
+ *
+ * where <attribute name> is one of:
+ *
+ * revision
+ * owner
+ * owner+
+ * group
+ * group+
+ * acl:<name or sid>
+ * acl+:<name or sid>
+ *
+ * In the forms "system.nt_sec_desc.*" and
+ * "system.nt_sec_desc.*+", the asterisk and plus signs are
+ * literal, i.e. the string is provided exactly as shown, and
+ * the value parameter should contain a complete security
+ * descriptor with name:value pairs separated by tabs,
+ * commas, or newlines (not spaces!).
+ *
+ * The plus sign ('+') indicates that SIDs should be mapped
+ * to names. Without the plus sign, SIDs are not mapped;
+ * rather they are simply converted to a string format.
+ *
+ * @param value The value to be assigned to the specified attribute name.
+ * This buffer should contain only the attribute value if the
+ * name was of the "system.nt_sec_desc.<attribute_name>"
+ * form. If the name was of the "system.nt_sec_desc.*" form
+ * then a complete security descriptor, with name:value pairs
+ * separated by tabs, commas, or newlines (not spaces!),
+ * should be provided in this value buffer. A complete
+ * security descriptor will contain one or more entries
+ * selected from the following:
+ *
+ * REVISION:<revision number>
+ * OWNER:<sid or name>
+ * GROUP:<sid or name>
+ * ACL:<sid or name>:<type>/<flags>/<mask>
+ *
+ * The revision of the ACL specifies the internal Windows NT
+ * ACL revision for the security descriptor. If not specified
+ * it defaults to 1. Using values other than 1 may cause
+ * strange behaviour.
+ *
+ * The owner and group specify the owner and group sids for
+ * the object. If the attribute name (either '*+' with a
+ * complete security descriptor, or individual 'owner+' or
+ * 'group+' attribute names) ended with a plus sign, the
+ * specified name is resolved to a SID value, using the
+ * server on which the file or directory resides. Otherwise,
+ * the value should be provided in SID-printable format as
+ * S-1-x-y-z, and is used directly. The <sid or name>
+ * associated with the ACL: attribute should be provided
+ * similarly.
+ *
+ * @param size The number of the bytes of data in the value buffer
+ *
+ * @param flags A bit-wise OR of zero or more of the following:
+ * SMBC_XATTR_FLAG_CREATE -
+ * fail if the named attribute already exists
+ * SMBC_XATTR_FLAG_REPLACE -
+ * fail if the attribute does not already exist
+ *
+ * If neither flag is specified, the specified attributes
+ * will be added or replace existing attributes of the same
+ * name, as necessary.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * or one of the parameters is not of a correct
+ * form
+ * - ENOMEM No memory was available for internal needs
+ * - EEXIST If the attribute already exists and the flag
+ * SMBC_XATTR_FLAG_CREAT was specified
+ * - ENOATTR If the attribute does not exist and the flag
+ * SMBC_XATTR_FLAG_REPLACE was specified
+ * - EPERM Permission was denied.
+ * - ENOTSUP The referenced file system does not support
+ * extended attributes
+ *
+ * @note Attribute names are compared in a case-insensitive
+ * fashion. All of the following are equivalent, although
+ * the all-lower-case name is the preferred format:
+ * system.nt_sec_desc.owner
+ * SYSTEM.NT_SEC_DESC.OWNER
+ * sYsTeM.nt_sEc_desc.owNER
+ *
+ */
+int smbc_setxattr(const char *url,
+ const char *name,
+ const void *value,
+ size_t size,
+ int flags);
+
+
+/**@ingroup attribute
+ * Set extended attributes for a file. This is used for modifying a file's
+ * security descriptor (i.e. owner, group, and access control list). The
+ * POSIX function which this maps to would act on a symbolic link rather than
+ * acting on what the symbolic link points to, but with no symbolic links in
+ * SMB file systems, this function is functionally identical to
+ * smbc_setxattr().
+ *
+ * @param url The smb url of the file or directory to set extended
+ * attributes for.
+ *
+ * @param name The name of an attribute to be changed. Names are of
+ * one of the following forms:
+ *
+ * system.nt_sec_desc.<attribute name>
+ * system.nt_sec_desc.*
+ * system.nt_sec_desc.*+
+ *
+ * where <attribute name> is one of:
+ *
+ * revision
+ * owner
+ * owner+
+ * group
+ * group+
+ * acl:<name or sid>
+ * acl+:<name or sid>
+ *
+ * In the forms "system.nt_sec_desc.*" and
+ * "system.nt_sec_desc.*+", the asterisk and plus signs are
+ * literal, i.e. the string is provided exactly as shown, and
+ * the value parameter should contain a complete security
+ * descriptor with name:value pairs separated by tabs,
+ * commas, or newlines (not spaces!).
+ *
+ * The plus sign ('+') indicates that SIDs should be mapped
+ * to names. Without the plus sign, SIDs are not mapped;
+ * rather they are simply converted to a string format.
+ *
+ * @param value The value to be assigned to the specified attribute name.
+ * This buffer should contain only the attribute value if the
+ * name was of the "system.nt_sec_desc.<attribute_name>"
+ * form. If the name was of the "system.nt_sec_desc.*" form
+ * then a complete security descriptor, with name:value pairs
+ * separated by tabs, commas, or newlines (not spaces!),
+ * should be provided in this value buffer. A complete
+ * security descriptor will contain one or more entries
+ * selected from the following:
+ *
+ * REVISION:<revision number>
+ * OWNER:<sid or name>
+ * GROUP:<sid or name>
+ * ACL:<sid or name>:<type>/<flags>/<mask>
+ *
+ * The revision of the ACL specifies the internal Windows NT
+ * ACL revision for the security descriptor. If not specified
+ * it defaults to 1. Using values other than 1 may cause
+ * strange behaviour.
+ *
+ * The owner and group specify the owner and group sids for
+ * the object. If the attribute name (either '*+' with a
+ * complete security descriptor, or individual 'owner+' or
+ * 'group+' attribute names) ended with a plus sign, the
+ * specified name is resolved to a SID value, using the
+ * server on which the file or directory resides. Otherwise,
+ * the value should be provided in SID-printable format as
+ * S-1-x-y-z, and is used directly. The <sid or name>
+ * associated with the ACL: attribute should be provided
+ * similarly.
+ *
+ * @param size The number of the bytes of data in the value buffer
+ *
+ * @param flags A bit-wise OR of zero or more of the following:
+ * SMBC_XATTR_FLAG_CREATE -
+ * fail if the named attribute already exists
+ * SMBC_XATTR_FLAG_REPLACE -
+ * fail if the attribute does not already exist
+ *
+ * If neither flag is specified, the specified attributes
+ * will be added or replace existing attributes of the same
+ * name, as necessary.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * or one of the parameters is not of a correct
+ * form
+ * - ENOMEM No memory was available for internal needs
+ * - EEXIST If the attribute already exists and the flag
+ * SMBC_XATTR_FLAG_CREAT was specified
+ * - ENOATTR If the attribute does not exist and the flag
+ * SMBC_XATTR_FLAG_REPLACE was specified
+ * - EPERM Permission was denied.
+ * - ENOTSUP The referenced file system does not support
+ * extended attributes
+ *
+ * @note Attribute names are compared in a case-insensitive
+ * fashion. All of the following are equivalent, although
+ * the all-lower-case name is the preferred format:
+ * system.nt_sec_desc.owner
+ * SYSTEM.NT_SEC_DESC.OWNER
+ * sYsTeM.nt_sEc_desc.owNER
+ *
+ */
+int smbc_lsetxattr(const char *url,
+ const char *name,
+ const void *value,
+ size_t size,
+ int flags);
+
+
+/**@ingroup attribute
+ * Set extended attributes for a file. This is used for modifying a file's
+ * security descriptor (i.e. owner, group, and access control list)
+ *
+ * @param fd A file descriptor associated with an open file (as
+ * previously returned by smbc_open(), to get extended
+ * attributes for.
+ *
+ * @param name The name of an attribute to be changed. Names are of
+ * one of the following forms:
+ *
+ * system.nt_sec_desc.<attribute name>
+ * system.nt_sec_desc.*
+ * system.nt_sec_desc.*+
+ *
+ * where <attribute name> is one of:
+ *
+ * revision
+ * owner
+ * owner+
+ * group
+ * group+
+ * acl:<name or sid>
+ * acl+:<name or sid>
+ *
+ * In the forms "system.nt_sec_desc.*" and
+ * "system.nt_sec_desc.*+", the asterisk and plus signs are
+ * literal, i.e. the string is provided exactly as shown, and
+ * the value parameter should contain a complete security
+ * descriptor with name:value pairs separated by tabs,
+ * commas, or newlines (not spaces!).
+ *
+ * The plus sign ('+') indicates that SIDs should be mapped
+ * to names. Without the plus sign, SIDs are not mapped;
+ * rather they are simply converted to a string format.
+ *
+ * @param value The value to be assigned to the specified attribute name.
+ * This buffer should contain only the attribute value if the
+ * name was of the "system.nt_sec_desc.<attribute_name>"
+ * form. If the name was of the "system.nt_sec_desc.*" form
+ * then a complete security descriptor, with name:value pairs
+ * separated by tabs, commas, or newlines (not spaces!),
+ * should be provided in this value buffer. A complete
+ * security descriptor will contain one or more entries
+ * selected from the following:
+ *
+ * REVISION:<revision number>
+ * OWNER:<sid or name>
+ * GROUP:<sid or name>
+ * ACL:<sid or name>:<type>/<flags>/<mask>
+ *
+ * The revision of the ACL specifies the internal Windows NT
+ * ACL revision for the security descriptor. If not specified
+ * it defaults to 1. Using values other than 1 may cause
+ * strange behaviour.
+ *
+ * The owner and group specify the owner and group sids for
+ * the object. If the attribute name (either '*+' with a
+ * complete security descriptor, or individual 'owner+' or
+ * 'group+' attribute names) ended with a plus sign, the
+ * specified name is resolved to a SID value, using the
+ * server on which the file or directory resides. Otherwise,
+ * the value should be provided in SID-printable format as
+ * S-1-x-y-z, and is used directly. The <sid or name>
+ * associated with the ACL: attribute should be provided
+ * similarly.
+ *
+ * @param size The number of the bytes of data in the value buffer
+ *
+ * @param flags A bit-wise OR of zero or more of the following:
+ * SMBC_XATTR_FLAG_CREATE -
+ * fail if the named attribute already exists
+ * SMBC_XATTR_FLAG_REPLACE -
+ * fail if the attribute does not already exist
+ *
+ * If neither flag is specified, the specified attributes
+ * will be added or replace existing attributes of the same
+ * name, as necessary.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * or one of the parameters is not of a correct
+ * form
+ * - ENOMEM No memory was available for internal needs
+ * - EEXIST If the attribute already exists and the flag
+ * SMBC_XATTR_FLAG_CREAT was specified
+ * - ENOATTR If the attribute does not exist and the flag
+ * SMBC_XATTR_FLAG_REPLACE was specified
+ * - EPERM Permission was denied.
+ * - ENOTSUP The referenced file system does not support
+ * extended attributes
+ *
+ * @note Attribute names are compared in a case-insensitive
+ * fashion. All of the following are equivalent, although
+ * the all-lower-case name is the preferred format:
+ * system.nt_sec_desc.owner
+ * SYSTEM.NT_SEC_DESC.OWNER
+ * sYsTeM.nt_sEc_desc.owNER
+ *
+ */
+int smbc_fsetxattr(int fd,
+ const char *name,
+ const void *value,
+ size_t size,
+ int flags);
+
+
+/**@ingroup attribute
+ * Get extended attributes for a file.
+ *
+ * @param url The smb url of the file or directory to get extended
+ * attributes for.
+ *
+ * @param name The name of an attribute to be retrieved. Names are of
+ * one of the following forms:
+ *
+ * system.nt_sec_desc.<attribute name>
+ * system.nt_sec_desc.*
+ * system.nt_sec_desc.*+
+ *
+ * where <attribute name> is one of:
+ *
+ * revision
+ * owner
+ * owner+
+ * group
+ * group+
+ * acl:<name or sid>
+ * acl+:<name or sid>
+ *
+ * In the forms "system.nt_sec_desc.*" and
+ * "system.nt_sec_desc.*+", the asterisk and plus signs are
+ * literal, i.e. the string is provided exactly as shown, and
+ * the value parameter will return a complete security
+ * descriptor with name:value pairs separated by tabs,
+ * commas, or newlines (not spaces!).
+ *
+ * The plus sign ('+') indicates that SIDs should be mapped
+ * to names. Without the plus sign, SIDs are not mapped;
+ * rather they are simply converted to a string format.
+ *
+ * @param value A pointer to a buffer in which the value of the specified
+ * attribute will be placed (unless size is zero).
+ *
+ * @param size The size of the buffer pointed to by value. This parameter
+ * may also be zero, in which case the size of the buffer
+ * required to hold the attribute value will be returned,
+ * but nothing will be placed into the value buffer.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * or one of the parameters is not of a correct
+ * form
+ * - ENOMEM No memory was available for internal needs
+ * - EEXIST If the attribute already exists and the flag
+ * SMBC_XATTR_FLAG_CREAT was specified
+ * - ENOATTR If the attribute does not exist and the flag
+ * SMBC_XATTR_FLAG_REPLACE was specified
+ * - EPERM Permission was denied.
+ * - ENOTSUP The referenced file system does not support
+ * extended attributes
+ *
+ */
+int smbc_getxattr(const char *url,
+ const char *name,
+ const void *value,
+ size_t size);
+
+
+/**@ingroup attribute
+ * Get extended attributes for a file. The POSIX function which this maps to
+ * would act on a symbolic link rather than acting on what the symbolic link
+ * points to, but with no symbolic links in SMB file systems, this function
+ * is functionally identical to smbc_getxattr().
+ *
+ * @param url The smb url of the file or directory to get extended
+ * attributes for.
+ *
+ * @param name The name of an attribute to be retrieved. Names are of
+ * one of the following forms:
+ *
+ * system.nt_sec_desc.<attribute name>
+ * system.nt_sec_desc.*
+ * system.nt_sec_desc.*+
+ *
+ * where <attribute name> is one of:
+ *
+ * revision
+ * owner
+ * owner+
+ * group
+ * group+
+ * acl:<name or sid>
+ * acl+:<name or sid>
+ *
+ * In the forms "system.nt_sec_desc.*" and
+ * "system.nt_sec_desc.*+", the asterisk and plus signs are
+ * literal, i.e. the string is provided exactly as shown, and
+ * the value parameter will return a complete security
+ * descriptor with name:value pairs separated by tabs,
+ * commas, or newlines (not spaces!).
+ *
+ * The plus sign ('+') indicates that SIDs should be mapped
+ * to names. Without the plus sign, SIDs are not mapped;
+ * rather they are simply converted to a string format.
+ *
+ * @param value A pointer to a buffer in which the value of the specified
+ * attribute will be placed (unless size is zero).
+ *
+ * @param size The size of the buffer pointed to by value. This parameter
+ * may also be zero, in which case the size of the buffer
+ * required to hold the attribute value will be returned,
+ * but nothing will be placed into the value buffer.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * or one of the parameters is not of a correct
+ * form
+ * - ENOMEM No memory was available for internal needs
+ * - EEXIST If the attribute already exists and the flag
+ * SMBC_XATTR_FLAG_CREAT was specified
+ * - ENOATTR If the attribute does not exist and the flag
+ * SMBC_XATTR_FLAG_REPLACE was specified
+ * - EPERM Permission was denied.
+ * - ENOTSUP The referenced file system does not support
+ * extended attributes
+ *
+ */
+int smbc_lgetxattr(const char *url,
+ const char *name,
+ const void *value,
+ size_t size);
+
+
+/**@ingroup attribute
+ * Get extended attributes for a file.
+ *
+ * @param fd A file descriptor associated with an open file (as
+ * previously returned by smbc_open(), to get extended
+ * attributes for.
+ *
+ * @param name The name of an attribute to be retrieved. Names are of
+ * one of the following forms:
+ *
+ * system.nt_sec_desc.<attribute name>
+ * system.nt_sec_desc.*
+ * system.nt_sec_desc.*+
+ *
+ * where <attribute name> is one of:
+ *
+ * revision
+ * owner
+ * owner+
+ * group
+ * group+
+ * acl:<name or sid>
+ * acl+:<name or sid>
+ *
+ * In the forms "system.nt_sec_desc.*" and
+ * "system.nt_sec_desc.*+", the asterisk and plus signs are
+ * literal, i.e. the string is provided exactly as shown, and
+ * the value parameter will return a complete security
+ * descriptor with name:value pairs separated by tabs,
+ * commas, or newlines (not spaces!).
+ *
+ * The plus sign ('+') indicates that SIDs should be mapped
+ * to names. Without the plus sign, SIDs are not mapped;
+ * rather they are simply converted to a string format.
+ *
+ * @param value A pointer to a buffer in which the value of the specified
+ * attribute will be placed (unless size is zero).
+ *
+ * @param size The size of the buffer pointed to by value. This parameter
+ * may also be zero, in which case the size of the buffer
+ * required to hold the attribute value will be returned,
+ * but nothing will be placed into the value buffer.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * or one of the parameters is not of a correct
+ * form
+ * - ENOMEM No memory was available for internal needs
+ * - EEXIST If the attribute already exists and the flag
+ * SMBC_XATTR_FLAG_CREAT was specified
+ * - ENOATTR If the attribute does not exist and the flag
+ * SMBC_XATTR_FLAG_REPLACE was specified
+ * - EPERM Permission was denied.
+ * - ENOTSUP The referenced file system does not support
+ * extended attributes
+ *
+ */
+int smbc_fgetxattr(int fd,
+ const char *name,
+ const void *value,
+ size_t size);
+
+
+/**@ingroup attribute
+ * Remove extended attributes for a file. This is used for modifying a file's
+ * security descriptor (i.e. owner, group, and access control list)
+ *
+ * @param url The smb url of the file or directory to remove the extended
+ * attributes for.
+ *
+ * @param name The name of an attribute to be removed. Names are of
+ * one of the following forms:
+ *
+ * system.nt_sec_desc.<attribute name>
+ * system.nt_sec_desc.*
+ * system.nt_sec_desc.*+
+ *
+ * where <attribute name> is one of:
+ *
+ * revision
+ * owner
+ * owner+
+ * group
+ * group+
+ * acl:<name or sid>
+ * acl+:<name or sid>
+ *
+ * In the forms "system.nt_sec_desc.*" and
+ * "system.nt_sec_desc.*+", the asterisk and plus signs are
+ * literal, i.e. the string is provided exactly as shown, and
+ * the value parameter will return a complete security
+ * descriptor with name:value pairs separated by tabs,
+ * commas, or newlines (not spaces!).
+ *
+ * The plus sign ('+') indicates that SIDs should be mapped
+ * to names. Without the plus sign, SIDs are not mapped;
+ * rather they are simply converted to a string format.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * - ENOMEM No memory was available for internal needs
+ * - EPERM Permission was denied.
+ * - ENOTSUP The referenced file system does not support
+ * extended attributes
+ *
+ */
+int smbc_removexattr(const char *url,
+ const char *name);
+
+
+/**@ingroup attribute
+ * Remove extended attributes for a file. This is used for modifying a file's
+ * security descriptor (i.e. owner, group, and access control list) The POSIX
+ * function which this maps to would act on a symbolic link rather than acting
+ * on what the symbolic link points to, but with no symbolic links in SMB file
+ * systems, this function is functionally identical to smbc_removexattr().
+ *
+ * @param url The smb url of the file or directory to remove the extended
+ * attributes for.
+ *
+ * @param name The name of an attribute to be removed. Names are of
+ * one of the following forms:
+ *
+ * system.nt_sec_desc.<attribute name>
+ * system.nt_sec_desc.*
+ * system.nt_sec_desc.*+
+ *
+ * where <attribute name> is one of:
+ *
+ * revision
+ * owner
+ * owner+
+ * group
+ * group+
+ * acl:<name or sid>
+ * acl+:<name or sid>
+ *
+ * In the forms "system.nt_sec_desc.*" and
+ * "system.nt_sec_desc.*+", the asterisk and plus signs are
+ * literal, i.e. the string is provided exactly as shown, and
+ * the value parameter will return a complete security
+ * descriptor with name:value pairs separated by tabs,
+ * commas, or newlines (not spaces!).
+ *
+ * The plus sign ('+') indicates that SIDs should be mapped
+ * to names. Without the plus sign, SIDs are not mapped;
+ * rather they are simply converted to a string format.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * - ENOMEM No memory was available for internal needs
+ * - EPERM Permission was denied.
+ * - ENOTSUP The referenced file system does not support
+ * extended attributes
+ *
+ */
+int smbc_lremovexattr(const char *url,
+ const char *name);
+
+
+/**@ingroup attribute
+ * Remove extended attributes for a file. This is used for modifying a file's
+ * security descriptor (i.e. owner, group, and access control list)
+ *
+ * @param fd A file descriptor associated with an open file (as
+ * previously returned by smbc_open(), to get extended
+ * attributes for.
+ *
+ * @param name The name of an attribute to be removed. Names are of
+ * one of the following forms:
+ *
+ * system.nt_sec_desc.<attribute name>
+ * system.nt_sec_desc.*
+ * system.nt_sec_desc.*+
+ *
+ * where <attribute name> is one of:
+ *
+ * revision
+ * owner
+ * owner+
+ * group
+ * group+
+ * acl:<name or sid>
+ * acl+:<name or sid>
+ *
+ * In the forms "system.nt_sec_desc.*" and
+ * "system.nt_sec_desc.*+", the asterisk and plus signs are
+ * literal, i.e. the string is provided exactly as shown, and
+ * the value parameter will return a complete security
+ * descriptor with name:value pairs separated by tabs,
+ * commas, or newlines (not spaces!).
+ *
+ * The plus sign ('+') indicates that SIDs should be mapped
+ * to names. Without the plus sign, SIDs are not mapped;
+ * rather they are simply converted to a string format.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * - ENOMEM No memory was available for internal needs
+ * - EPERM Permission was denied.
+ * - ENOTSUP The referenced file system does not support
+ * extended attributes
+ *
+ */
+int smbc_fremovexattr(int fd,
+ const char *name);
+
+
+/**@ingroup attribute
+ * List the supported extended attribute names associated with a file
+ *
+ * @param url The smb url of the file or directory to list the extended
+ * attributes for.
+ *
+ * @param list A pointer to a buffer in which the list of attributes for
+ * the specified file or directory will be placed (unless
+ * size is zero).
+ *
+ * @param size The size of the buffer pointed to by list. This parameter
+ * may also be zero, in which case the size of the buffer
+ * required to hold all of the attribute names will be
+ * returned, but nothing will be placed into the list buffer.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * - ENOMEM No memory was available for internal needs
+ * - EPERM Permission was denied.
+ * - ENOTSUP The referenced file system does not support
+ * extended attributes
+ *
+ * @note This function always returns all attribute names supported
+ * by NT file systems, regardless of wether the referenced
+ * file system supports extended attributes (e.g. a Windows
+ * 2000 machine supports extended attributes if NTFS is used,
+ * but not if FAT is used, and Windows 98 doesn't support
+ * extended attributes at all. Whether this is a feature or
+ * a bug is yet to be decided.
+ */
+int smbc_listxattr(const char *url,
+ char *list,
+ size_t size);
+
+/**@ingroup attribute
+ * List the supported extended attribute names associated with a file The
+ * POSIX function which this maps to would act on a symbolic link rather than
+ * acting on what the symbolic link points to, but with no symbolic links in
+ * SMB file systems, this function is functionally identical to
+ * smbc_listxattr().
+ *
+ * @param url The smb url of the file or directory to list the extended
+ * attributes for.
+ *
+ * @param list A pointer to a buffer in which the list of attributes for
+ * the specified file or directory will be placed (unless
+ * size is zero).
+ *
+ * @param size The size of the buffer pointed to by list. This parameter
+ * may also be zero, in which case the size of the buffer
+ * required to hold all of the attribute names will be
+ * returned, but nothing will be placed into the list buffer.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * - ENOMEM No memory was available for internal needs
+ * - EPERM Permission was denied.
+ * - ENOTSUP The referenced file system does not support
+ * extended attributes
+ *
+ * @note This function always returns all attribute names supported
+ * by NT file systems, regardless of wether the referenced
+ * file system supports extended attributes (e.g. a Windows
+ * 2000 machine supports extended attributes if NTFS is used,
+ * but not if FAT is used, and Windows 98 doesn't support
+ * extended attributes at all. Whether this is a feature or
+ * a bug is yet to be decided.
+ */
+int smbc_llistxattr(const char *url,
+ char *list,
+ size_t size);
+
+/**@ingroup attribute
+ * List the supported extended attribute names associated with a file
+ *
+ * @param fd A file descriptor associated with an open file (as
+ * previously returned by smbc_open(), to get extended
+ * attributes for.
+ *
+ * @param list A pointer to a buffer in which the list of attributes for
+ * the specified file or directory will be placed (unless
+ * size is zero).
+ *
+ * @param size The size of the buffer pointed to by list. This parameter
+ * may also be zero, in which case the size of the buffer
+ * required to hold all of the attribute names will be
+ * returned, but nothing will be placed into the list buffer.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * - ENOMEM No memory was available for internal needs
+ * - EPERM Permission was denied.
+ * - ENOTSUP The referenced file system does not support
+ * extended attributes
+ *
+ * @note This function always returns all attribute names supported
+ * by NT file systems, regardless of wether the referenced
+ * file system supports extended attributes (e.g. a Windows
+ * 2000 machine supports extended attributes if NTFS is used,
+ * but not if FAT is used, and Windows 98 doesn't support
+ * extended attributes at all. Whether this is a feature or
+ * a bug is yet to be decided.
+ */
+int smbc_flistxattr(int fd,
+ char *list,
+ size_t size);
+
+/**@ingroup print
+ * Print a file given the name in fname. It would be a URL ...
+ *
+ * @param fname The URL of a file on a remote SMB server that the
+ * caller wants printed
+ *
+ * @param printq The URL of the print share to print the file to.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ *
+ * - EINVAL fname or printq was NULL or smbc_init not
+ * not called.
+ * and errors returned by smbc_open
+ *
+ */
+int smbc_print_file(const char *fname, const char *printq);
+
+/**@ingroup print
+ * Open a print file that can be written to by other calls. This simply
+ * does an smbc_open call after checking if there is a file name on the
+ * URI. If not, a temporary name is added ...
+ *
+ * @param fname The URL of the print share to print to?
+ *
+ * @returns A file handle for the print file if successful.
+ * Returns -1 if an error ocurred and errno has the values
+ * - EINVAL fname was NULL or smbc_init not called.
+ * - all errors returned by smbc_open
+ *
+ */
+int smbc_open_print_job(const char *fname);
+
+/**@ingroup print
+ * List the print jobs on a print share, for the moment, pass a callback
+ *
+ * @param purl The url of the print share to list the jobs of
+ *
+ * @param fn Callback function the receives printjob info
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL fname was NULL or smbc_init not called
+ * - EACCES ???
+ */
+int smbc_list_print_jobs(const char *purl, smbc_list_print_job_fn fn);
+
+/**@ingroup print
+ * Delete a print job
+ *
+ * @param purl Url of the print share
+ *
+ * @param id The id of the job to delete
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL fname was NULL or smbc_init not called
+ *
+ * @todo what errno values are possible here?
+ */
+int smbc_unlink_print_job(const char *purl, int id);
+
+/**@ingroup callback
+ * Remove a server from the cached server list it's unused.
+ *
+ * @param context pointer to smb context
+ *
+ * @param srv pointer to server to remove
+ *
+ * @return On success, 0 is returned. 1 is returned if the server could not
+ * be removed. Also useable outside libsmbclient.
+ */
+int smbc_remove_unused_server(SMBCCTX * context, SMBCSRV * srv);
+
+#endif /* SMBCLIENT_H_INCLUDED */
diff --git a/source/include/local.h b/source/include/local.h
new file mode 100644
index 00000000000..540365047a2
--- /dev/null
+++ b/source/include/local.h
@@ -0,0 +1,233 @@
+/* Copyright (C) 1995-1998 Samba-Team */
+/* Copyright (C) 1998 John H Terpstra <jht@aquasoft.com.au> */
+
+/* local definitions for file server */
+#ifndef _LOCAL_H
+#define _LOCAL_H
+
+/* The default workgroup - usually overridden in smb.conf */
+#ifndef WORKGROUP
+#define WORKGROUP "WORKGROUP"
+#endif
+
+/* the maximum debug level to compile into the code. This assumes a good
+ optimising compiler that can remove unused code
+ for embedded or low-memory systems set this to a value like 2 to get
+ only important messages. This gives *much* smaller binaries
+*/
+#ifndef MAX_DEBUG_LEVEL
+#define MAX_DEBUG_LEVEL 1000
+#endif
+
+/* This defines the section name in the configuration file that will contain */
+/* global parameters - that is, parameters relating to the whole server, not */
+/* just services. This name is then reserved, and may not be used as a */
+/* a service name. It will default to "global" if not defined here. */
+#define GLOBAL_NAME "global"
+#define GLOBAL_NAME2 "globals"
+
+/* This defines the section name in the configuration file that will
+ refer to the special "homes" service */
+#define HOMES_NAME "homes"
+
+/* This defines the section name in the configuration file that will
+ refer to the special "printers" service */
+#define PRINTERS_NAME "printers"
+
+/* Yves Gaige <yvesg@hptnodur.grenoble.hp.com> requested this set this */
+/* to a maximum of 8 if old smb clients break because of long printer names. */
+#define MAXPRINTERLEN 15
+
+/* max number of directories open at once */
+/* note that with the new directory code this no longer requires a
+ file handle per directory, but large numbers do use more memory */
+#define MAX_OPEN_DIRECTORIES 256
+
+/* max number of directory handles */
+/* As this now uses the bitmap code this can be
+ quite large. */
+#define MAX_DIRECTORY_HANDLES 2048
+
+/* maximum number of file caches per smbd */
+#define MAX_WRITE_CACHES 10
+
+/* define what facility to use for syslog */
+#ifndef SYSLOG_FACILITY
+#define SYSLOG_FACILITY LOG_DAEMON
+#endif
+
+/*
+ * Default number of maximum open files per smbd. This is
+ * also limited by the maximum available file descriptors
+ * per process and can also be set in smb.conf as "max open files"
+ * in the [global] section.
+ */
+
+#ifndef MAX_OPEN_FILES
+#define MAX_OPEN_FILES 10000
+#endif
+
+#define WORDMAX 0xFFFF
+
+/* the maximum password length before we declare a likely attack */
+#define MAX_PASS_LEN 200
+
+/* separators for lists */
+#define LIST_SEP " \t,;\n\r"
+
+/* wchar separators for lists */
+#define LIST_SEP_W wchar_list_sep
+
+/* this is where browse lists are kept in the lock dir */
+#define SERVER_LIST "browse.dat"
+
+/* shall filenames with illegal chars in them get mangled in long
+ filename listings? */
+#define MANGLE_LONG_FILENAMES
+
+/* define this if you want to stop spoofing with .. and soft links
+ NOTE: This also slows down the server considerably */
+#define REDUCE_PATHS
+
+/* the size of the directory cache */
+#define DIRCACHESIZE 20
+
+/* what default type of filesystem do we want this to show up as in a
+ NT file manager window? */
+#define FSTYPE_STRING "NTFS"
+
+/* the default guest account - normally set in the Makefile or smb.conf */
+#ifndef GUEST_ACCOUNT
+#define GUEST_ACCOUNT "nobody"
+#endif
+
+/* user to test password server with as invalid in security=server mode. */
+#ifndef INVALID_USER_PREFIX
+#define INVALID_USER_PREFIX "sambatest"
+#endif
+
+/* the default pager to use for the client "more" command. Users can
+ override this with the PAGER environment variable */
+#ifndef PAGER
+#define PAGER "more"
+#endif
+
+/* the size of the uid cache used to reduce valid user checks */
+#define VUID_CACHE_SIZE 32
+
+/* the following control timings of various actions. Don't change
+ them unless you know what you are doing. These are all in seconds */
+#define DEFAULT_SMBD_TIMEOUT (60*60*24*7)
+#define SMBD_RELOAD_CHECK (180)
+#define IDLE_CLOSED_TIMEOUT (60)
+#define DPTR_IDLE_TIMEOUT (120)
+#define SMBD_SELECT_TIMEOUT (60)
+#define NMBD_SELECT_LOOP (10)
+#define BROWSE_INTERVAL (60)
+#define REGISTRATION_INTERVAL (10*60)
+#define NMBD_INETD_TIMEOUT (120)
+#define NMBD_MAX_TTL (24*60*60)
+#define LPQ_LOCK_TIMEOUT (5)
+#define NMBD_INTERFACES_RELOAD (120)
+#define NMBD_UNEXPECTED_TIMEOUT (15)
+
+/* the following are in milliseconds */
+#define LOCK_RETRY_TIMEOUT (100)
+
+/* do you want to dump core (carefully!) when an internal error is
+ encountered? Samba will be careful to make the core file only
+ accessible to root */
+#define DUMP_CORE 1
+
+/* shall we support browse requests via a FIFO to nmbd? */
+#define ENABLE_FIFO 1
+
+/* how long (in miliseconds) to wait for a socket connect to happen */
+#define LONG_CONNECT_TIMEOUT 30000
+#define SHORT_CONNECT_TIMEOUT 5000
+
+/* the default netbios keepalive timeout */
+#define DEFAULT_KEEPALIVE 300
+
+/* the directory to sit in when idle */
+/* #define IDLE_DIR "/" */
+
+/* Timout (in seconds) to wait for an oplock break
+ message to return from the client. */
+
+#define OPLOCK_BREAK_TIMEOUT 30
+
+/* Timout (in seconds) to add to the oplock break timeout
+ to wait for the smbd to smbd message to return. */
+
+#define OPLOCK_BREAK_TIMEOUT_FUDGEFACTOR 2
+
+/* the read preciction code has been disabled until some problems with
+ it are worked out */
+#define USE_READ_PREDICTION 0
+
+/*
+ * Default passwd chat script.
+ */
+
+#define DEFAULT_PASSWD_CHAT "*new*password* %n\\n *new*password* %n\\n *changed*"
+
+/* Minimum length of allowed password when changing UNIX password. */
+#define MINPASSWDLENGTH 5
+
+/* maximum ID number used for session control. This cannot be larger
+ than 62*62 for the current code */
+#define MAX_SESSION_ID 3000
+
+/* For the benifit of PAM and the 'session exec' scripts, we fake up a terminal
+ name. This can be in one of two forms: The first for systems not using
+ utmp (and therefore not constrained as to length or the need for a number
+ < 3000 or so) and the second for systems with this 'well behaved terminal
+ like name' constraint.
+*/
+
+#ifndef SESSION_TEMPLATE
+/* Paramaters are 'pid' and 'vuid' */
+#define SESSION_TEMPLATE "smb/%lu/%d"
+#endif
+
+#ifndef SESSION_UTMP_TEMPLATE
+#define SESSION_UTMP_TEMPLATE "smb/%d"
+#endif
+
+/* the maximum age in seconds of a password. Should be a lp_ parameter */
+#define MAX_PASSWORD_AGE (21*24*60*60)
+
+/* Allocation roundup. */
+#define SMB_ROUNDUP_ALLOCATION_SIZE 0x100000
+
+/* shall we deny oplocks to clients that get timeouts? */
+#define FASCIST_OPLOCK_BACKOFF 1
+
+/* this enables the "rabbit pellet" fix for SMBwritebraw */
+#define RABBIT_PELLET_FIX 1
+
+/* Max number of jobs per print queue. */
+#define PRINT_MAX_JOBID 10000
+
+/* Max number of open RPC pipes. */
+#define MAX_OPEN_PIPES 2048
+
+/* Tuning for server auth mutex. */
+#define CLI_AUTH_TIMEOUT 5000 /* In milli-seconds. */
+#define NUM_CLI_AUTH_CONNECT_RETRIES 3
+/* Number in seconds to wait for the mutex. This must be less than 30 seconds. */
+#define SERVER_MUTEX_WAIT_TIME ( ((NUM_CLI_AUTH_CONNECT_RETRIES) * ((CLI_AUTH_TIMEOUT)/1000)) + 5)
+/* Number in seconds for winbindd to wait for the mutex. Make this 2 * smbd wait time. */
+#define WINBIND_SERVER_MUTEX_WAIT_TIME (( ((NUM_CLI_AUTH_CONNECT_RETRIES) * ((CLI_AUTH_TIMEOUT)/1000)) + 5)*2)
+
+/* Max number of simultaneous winbindd socket connections. */
+#define WINBINDD_MAX_SIMULTANEOUS_CLIENTS 200
+
+/* Buffer size to use when printing backtraces */
+#define BACKTRACE_STACK_SIZE 64
+
+/* size of listen() backlog in smbd */
+#define SMBD_LISTEN_BACKLOG 50
+
+#endif
diff --git a/source/include/mangle.h b/source/include/mangle.h
new file mode 100644
index 00000000000..769278d828b
--- /dev/null
+++ b/source/include/mangle.h
@@ -0,0 +1,14 @@
+#ifndef _MANGLE_H_
+#define _MANGLE_H_
+/*
+ header for 8.3 name mangling interface
+*/
+
+struct mangle_fns {
+ BOOL (*is_mangled)(const char *s);
+ BOOL (*is_8_3)(const char *fname, BOOL check_case, BOOL allow_wildcards);
+ void (*reset)(void);
+ BOOL (*check_cache)(char *s);
+ void (*name_map)(char *OutName, BOOL need83, BOOL cache83);
+};
+#endif /* _MANGLE_H_ */
diff --git a/source/include/mapping.h b/source/include/mapping.h
new file mode 100644
index 00000000000..fdaa2b04532
--- /dev/null
+++ b/source/include/mapping.h
@@ -0,0 +1,33 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-2000,
+ * Copyright (C) Jean François Micouleau 1998-2001.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define ENUM_ONLY_MAPPED True
+#define ENUM_ALL_MAPPED False
+
+typedef struct _GROUP_MAP {
+ struct pdb_methods *methods;
+ gid_t gid;
+ DOM_SID sid;
+ enum SID_NAME_USE sid_name_use;
+ fstring nt_name;
+ fstring comment;
+} GROUP_MAP;
+
diff --git a/source/include/md5.h b/source/include/md5.h
new file mode 100644
index 00000000000..6665171e7c5
--- /dev/null
+++ b/source/include/md5.h
@@ -0,0 +1,24 @@
+#ifndef MD5_H
+#define MD5_H
+#ifndef HEADER_MD5_H
+/* Try to avoid clashes with OpenSSL */
+#define HEADER_MD5_H
+#endif
+
+struct MD5Context {
+ uint32 buf[4];
+ uint32 bits[2];
+ unsigned char in[64];
+};
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, unsigned char const *buf,
+ unsigned len);
+void MD5Final(unsigned char digest[16], struct MD5Context *context);
+
+/*
+ * This is needed to make RSAREF happy on some MS-DOS compilers.
+ */
+typedef struct MD5Context MD5_CTX;
+
+#endif /* !MD5_H */
diff --git a/source/include/messages.h b/source/include/messages.h
new file mode 100644
index 00000000000..37e9372cdaa
--- /dev/null
+++ b/source/include/messages.h
@@ -0,0 +1,71 @@
+/*
+ Unix SMB/CIFS implementation.
+ messages.c header
+ Copyright (C) Andrew Tridgell 2000
+ Copyright (C) 2001, 2002 by Martin Pool
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _MESSAGES_H_
+#define _MESSAGES_H_
+
+/* general messages */
+#define MSG_DEBUG 1
+#define MSG_PING 2
+#define MSG_PONG 3
+#define MSG_PROFILE 4
+#define MSG_REQ_DEBUGLEVEL 5
+#define MSG_DEBUGLEVEL 6
+#define MSG_REQ_PROFILELEVEL 7
+#define MSG_PROFILELEVEL 8
+#define MSG_REQ_POOL_USAGE 9
+#define MSG_POOL_USAGE 10
+
+/* If dmalloc is included, set a steady-state mark */
+#define MSG_REQ_DMALLOC_MARK 11
+
+/* If dmalloc is included, dump to the dmalloc log a description of
+ * what has changed since the last MARK */
+#define MSG_REQ_DMALLOC_LOG_CHANGED 12
+
+#define MSG_SHUTDOWN 13
+
+/* nmbd messages */
+#define MSG_FORCE_ELECTION 1001
+#define MSG_WINS_NEW_ENTRY 1002
+
+/* printing messages */
+/* #define MSG_PRINTER_NOTIFY 2001*/ /* Obsolete */
+#define MSG_PRINTER_DRVUPGRADE 2002
+#define MSG_PRINTER_NOTIFY2 2003
+#define MSG_PRINTERDATA_INIT_RESET 2004
+
+/* smbd messages */
+#define MSG_SMB_CONF_UPDATED 3001
+#define MSG_SMB_FORCE_TDIS 3002
+#define MSG_SMB_SAM_SYNC 3003
+#define MSG_SMB_SAM_REPL 3004
+#define MSG_SMB_UNLOCK 3005
+
+/* Flags to classify messages - used in message_send_all() */
+/* Sender will filter by flag. */
+
+#define FLAG_MSG_GENERAL 0x0001
+#define FLAG_MSG_SMBD 0x0002
+#define FLAG_MSG_NMBD 0x0004
+#define FLAG_MSG_PRINTING 0x0008
+
+#endif
diff --git a/source/include/modconf.h b/source/include/modconf.h
new file mode 100644
index 00000000000..f5cc5ef4889
--- /dev/null
+++ b/source/include/modconf.h
@@ -0,0 +1,34 @@
+#ifndef _MODCONF_H_
+#define _MODCONF_H_
+/*
+ Unix SMB/CIFS implementation.
+
+ ModConf headers
+
+ Copyright (C) Simo Sorce 2003
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#define SAMBA_CONFIG_INTERFACE_VERSION 1
+
+/* Filled out by config backends */
+struct config_functions {
+ NTSTATUS (*init)(char *params);
+ NTSTATUS (*load)(BOOL (*sfunc)(const char *),BOOL (*pfunc)(const char *, const char *));
+ NTSTATUS (*close)(void);
+};
+#endif /* _MODCONF_H_ */
diff --git a/source/include/module.h b/source/include/module.h
new file mode 100644
index 00000000000..c41310c7f75
--- /dev/null
+++ b/source/include/module.h
@@ -0,0 +1,38 @@
+/*
+ Unix SMB/CIFS implementation.
+ Handling of idle/exit events
+ Copyright (C) Stefan (metze) Metzmacher 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _MODULE_H
+#define _MODULE_H
+
+/* Module support */
+typedef NTSTATUS (init_module_function) (void);
+
+
+typedef int smb_event_id_t;
+#define SMB_EVENT_ID_INVALID (-1)
+
+#define SMB_IDLE_EVENT_DEFAULT_INTERVAL 180
+#define SMB_IDLE_EVENT_MIN_INTERVAL 30
+
+typedef void (smb_idle_event_fn)(void **data,time_t *interval,time_t now);
+
+typedef void (smb_exit_event_fn)(void **data);
+
+#endif /* _MODULE_H */
diff --git a/source/include/msdfs.h b/source/include/msdfs.h
new file mode 100644
index 00000000000..32aa7ecef25
--- /dev/null
+++ b/source/include/msdfs.h
@@ -0,0 +1,85 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ MSDfs services for Samba
+ Copyright (C) Shirish Kalele 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef _MSDFS_H
+#define _MSDFS_H
+
+#define REFERRAL_TTL 600
+
+/* Flags used in trans2 Get Referral reply */
+#define DFSREF_REFERRAL_SERVER 0x1
+#define DFSREF_STORAGE_SERVER 0x2
+
+/* Referral sizes */
+#define VERSION2_REFERRAL_SIZE 0x16
+#define VERSION3_REFERRAL_SIZE 0x22
+#define REFERRAL_HEADER_SIZE 0x08
+
+/* Maximum number of referrals for each Dfs volume */
+#define MAX_REFERRAL_COUNT 256
+
+struct referral
+{
+ pstring alternate_path; /* contains the path referred */
+ uint32 proximity;
+ uint32 ttl; /* how long should client cache referral */
+};
+
+struct junction_map
+{
+ pstring service_name;
+ pstring volume_name;
+ int referral_count;
+ struct referral* referral_list;
+};
+
+struct dfs_path
+{
+ pstring hostname;
+ pstring servicename;
+ pstring reqpath;
+};
+
+#define RESOLVE_DFSPATH(name, conn, inbuf, outbuf) \
+{ if ((SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES) && \
+ lp_host_msdfs() && lp_msdfs_root(SNUM(conn)) && \
+ dfs_redirect(name,conn,False)) \
+ return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED, \
+ ERRSRV, ERRbadpath);; }
+
+#define RESOLVE_FINDFIRST_DFSPATH(name, conn, inbuf, outbuf) \
+{ if ( (SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES) || \
+ ((get_remote_arch() == RA_WIN95) && lp_msdfs_root(SNUM(conn))) ) \
+ if (lp_host_msdfs() && dfs_redirect(name,conn,True)) \
+ return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED, \
+ ERRSRV, ERRbadpath);; }
+
+
+#define init_dfsroot(conn, inbuf, outbuf) \
+{ if (lp_msdfs_root(SNUM(conn)) && lp_host_msdfs()) { \
+ DEBUG(2,("Serving %s as a Dfs root\n", \
+ lp_servicename(SNUM(conn)) )); \
+ SSVAL(outbuf, smb_vwv2, SMB_SHARE_IN_DFS \
+ | SVAL(outbuf, smb_vwv2)); \
+} }
+
+#endif /* _MSDFS_H */
diff --git a/source/include/nameserv.h b/source/include/nameserv.h
new file mode 100644
index 00000000000..ec3d56c06b7
--- /dev/null
+++ b/source/include/nameserv.h
@@ -0,0 +1,627 @@
+#ifndef _NAMESERV_H_
+#define _NAMESERV_H_
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios header - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#define INFO_VERSION "INFO/version"
+#define INFO_COUNT "INFO/num_entries"
+#define INFO_ID_HIGH "INFO/id_high"
+#define INFO_ID_LOW "INFO/id_low"
+#define ENTRY_PREFIX "ENTRY/"
+
+#define PERMANENT_TTL 0
+
+/* NTAS uses 2, NT uses 1, WfWg uses 0 */
+#define MAINTAIN_LIST 2
+#define ELECTION_VERSION 1
+
+#define MAX_DGRAM_SIZE (576) /* tcp/ip datagram limit is 576 bytes */
+#define MIN_DGRAM_SIZE 12
+
+/*********************************************************
+ Types of reply packet.
+**********************************************************/
+
+enum netbios_reply_type_code { NMB_QUERY, NMB_STATUS, NMB_REG, NMB_REG_REFRESH,
+ NMB_REL, NMB_WAIT_ACK, NMB_MULTIHOMED_REG,
+ WINS_REG, WINS_QUERY };
+
+/* From rfc1002, 4.2.1.2 */
+/* Question types. */
+#define QUESTION_TYPE_NB_QUERY 0x20
+#define QUESTION_TYPE_NB_STATUS 0x21
+
+/* Question class */
+#define QUESTION_CLASS_IN 0x1
+
+/* Opcode definitions */
+#define NMB_NAME_QUERY_OPCODE 0x0
+#define NMB_NAME_REG_OPCODE 0x05 /* see rfc1002.txt 4.2.2,3,5,6,7,8 */
+#define NMB_NAME_RELEASE_OPCODE 0x06 /* see rfc1002.txt 4.2.9,10,11 */
+#define NMB_WACK_OPCODE 0x07 /* see rfc1002.txt 4.2.16 */
+/* Ambiguity in rfc1002 about which of these is correct. */
+/* WinNT uses 8 by default but can be made to use 9. */
+#define NMB_NAME_REFRESH_OPCODE_8 0x08 /* see rfc1002.txt 4.2.4 */
+#define NMB_NAME_REFRESH_OPCODE_9 0x09 /* see rfc1002.txt 4.2.4 */
+#define NMB_NAME_MULTIHOMED_REG_OPCODE 0x0F /* Invented by Microsoft. */
+
+/* XXXX what about all the other types?? 0x1, 0x2, 0x3, 0x4, 0x8? */
+
+/* Resource record types. rfc1002 4.2.1.3 */
+#define RR_TYPE_A 0x1
+#define RR_TYPE_NS 0x2
+#define RR_TYPE_NULL 0xA
+#define RR_TYPE_NB 0x20
+#define RR_TYPE_NBSTAT 0x21
+
+/* Resource record class. */
+#define RR_CLASS_IN 0x1
+
+/* NetBIOS flags */
+#define NB_GROUP 0x80
+#define NB_PERM 0x02
+#define NB_ACTIVE 0x04
+#define NB_CONFL 0x08
+#define NB_DEREG 0x10
+#define NB_BFLAG 0x00 /* Broadcast node type. */
+#define NB_PFLAG 0x20 /* Point-to-point node type. */
+#define NB_MFLAG 0x40 /* Mixed bcast & p-p node type. */
+#define NB_HFLAG 0x60 /* Microsoft 'hybrid' node type. */
+#define NB_NODETYPEMASK 0x60
+/* Mask applied to outgoing NetBIOS flags. */
+#define NB_FLGMSK 0xE0
+
+/* The wins flags. Looks like the nbflags ! */
+#define WINS_UNIQUE 0x00 /* Unique record */
+#define WINS_NGROUP 0x01 /* Normal Group eg: 1B */
+#define WINS_SGROUP 0x02 /* Special Group eg: 1C */
+#define WINS_MHOMED 0x03 /* MultiHomed */
+
+#define WINS_ACTIVE 0x00 /* active record */
+#define WINS_RELEASED 0x04 /* released record */
+#define WINS_TOMBSTONED 0x08 /* tombstoned record */
+#define WINS_DELETED 0x0C /* deleted record */
+
+#define WINS_STATE_MASK 0x0C
+
+#define WINS_LOCAL 0x00 /* local record */
+#define WINS_REMOTE 0x10 /* remote record */
+
+#define WINS_BNODE 0x00 /* Broadcast node */
+#define WINS_PNODE 0x20 /* PtP node */
+#define WINS_MNODE 0x40 /* Mixed node */
+#define WINS_HNODE 0x60 /* Hybrid node */
+
+#define WINS_NONSTATIC 0x00 /* dynamic record */
+#define WINS_STATIC 0x80 /* static record */
+
+#define WINS_STATE_ACTIVE(p) (((p)->data.wins_flags & WINS_STATE_MASK) == WINS_ACTIVE)
+
+
+/* NetBIOS flag identifier. */
+#define NAME_GROUP(p) ((p)->data.nb_flags & NB_GROUP)
+#define NAME_BFLAG(p) (((p)->data.nb_flags & NB_NODETYPEMASK) == NB_BFLAG)
+#define NAME_PFLAG(p) (((p)->data.nb_flags & NB_NODETYPEMASK) == NB_PFLAG)
+#define NAME_MFLAG(p) (((p)->data.nb_flags & NB_NODETYPEMASK) == NB_MFLAG)
+#define NAME_HFLAG(p) (((p)->data.nb_flags & NB_NODETYPEMASK) == NB_HFLAG)
+
+/* Samba name state for a name in a namelist. */
+#define NAME_IS_ACTIVE(p) ((p)->data.nb_flags & NB_ACTIVE)
+#define NAME_IN_CONFLICT(p) ((p)->data.nb_flags & NB_CONFL)
+#define NAME_IS_DEREGISTERING(p) ((p)->data.nb_flags & NB_DEREG)
+
+/* Error codes for NetBIOS requests. */
+#define FMT_ERR 0x1 /* Packet format error. */
+#define SRV_ERR 0x2 /* Internal server error. */
+#define NAM_ERR 0x3 /* Name does not exist. */
+#define IMP_ERR 0x4 /* Request not implemented. */
+#define RFS_ERR 0x5 /* Request refused. */
+#define ACT_ERR 0x6 /* Active error - name owned by another host. */
+#define CFT_ERR 0x7 /* Name in conflict error. */
+
+#define REFRESH_TIME (15*60)
+#define NAME_POLL_REFRESH_TIME (5*60)
+#define NAME_POLL_INTERVAL 15
+
+/* Workgroup state identifiers. */
+#define AM_POTENTIAL_MASTER_BROWSER(work) ((work)->mst_state == MST_POTENTIAL)
+#define AM_LOCAL_MASTER_BROWSER(work) ((work)->mst_state == MST_BROWSER)
+#define AM_DOMAIN_MASTER_BROWSER(work) ((work)->dom_state == DOMAIN_MST)
+#define AM_DOMAIN_MEMBER(work) ((work)->log_state == LOGON_SRV)
+
+/* Microsoft browser NetBIOS name. */
+#define MSBROWSE "\001\002__MSBROWSE__\002"
+
+/* Mail slots. */
+#define BROWSE_MAILSLOT "\\MAILSLOT\\BROWSE"
+#define NET_LOGON_MAILSLOT "\\MAILSLOT\\NET\\NETLOGON"
+#define NT_LOGON_MAILSLOT "\\MAILSLOT\\NET\\NTLOGON"
+#define LANMAN_MAILSLOT "\\MAILSLOT\\LANMAN"
+
+/* Samba definitions for find_name_on_subnet(). */
+#define FIND_ANY_NAME 0
+#define FIND_SELF_NAME 1
+
+/*
+ * The different name types that can be in namelists.
+ *
+ * SELF_NAME should only be on the broadcast and unicast subnets.
+ * LMHOSTS_NAME should only be in the remote_broadcast_subnet.
+ * REGISTER_NAME, DNS_NAME, DNSFAIL_NAME should only be in the wins_server_subnet.
+ * WINS_PROXY_NAME should only be on the broadcast subnets.
+ * PERMANENT_NAME can be on all subnets except remote_broadcast_subnet.
+ *
+ */
+
+enum name_source {LMHOSTS_NAME, REGISTER_NAME, SELF_NAME, DNS_NAME,
+ DNSFAIL_NAME, PERMANENT_NAME, WINS_PROXY_NAME};
+enum node_type {B_NODE=0, P_NODE=1, M_NODE=2, NBDD_NODE=3};
+enum packet_type {NMB_PACKET, DGRAM_PACKET};
+
+enum master_state {
+ MST_NONE,
+ MST_POTENTIAL,
+ MST_BACKUP,
+ MST_MSB,
+ MST_BROWSER,
+ MST_UNBECOMING_MASTER
+};
+
+enum domain_state {
+ DOMAIN_NONE,
+ DOMAIN_WAIT,
+ DOMAIN_MST
+};
+
+enum logon_state {
+ LOGON_NONE,
+ LOGON_WAIT,
+ LOGON_SRV
+};
+
+struct subnet_record;
+
+struct nmb_data {
+ uint16 nb_flags; /* Netbios flags. */
+ int num_ips; /* Number of ip entries. */
+ struct in_addr *ip; /* The ip list for this name. */
+
+ enum name_source source; /* Where the name came from. */
+
+ time_t death_time; /* The time the record must be removed (do not remove if 0). */
+ time_t refresh_time; /* The time the record should be refreshed. */
+
+ SMB_BIG_UINT id; /* unique id */
+ struct in_addr wins_ip; /* the adress of the wins server this record comes from */
+
+ int wins_flags; /* similar to the netbios flags but different ! */
+};
+
+/* This structure represents an entry in a local netbios name list. */
+struct name_record {
+ ubi_trNode node[1];
+ struct subnet_record *subnet;
+ struct nmb_name name; /* The netbios name. */
+ struct nmb_data data; /* The netbios data. */
+};
+
+/* Browser cache for synchronising browse lists. */
+struct browse_cache_record {
+ ubi_dlNode node[1];
+ unstring lmb_name;
+ unstring work_group;
+ struct in_addr ip;
+ time_t sync_time;
+ time_t death_time; /* The time the record must be removed. */
+};
+
+/* This is used to hold the list of servers in my domain, and is
+ contained within lists of domains. */
+
+struct server_record {
+ struct server_record *next;
+ struct server_record *prev;
+
+ struct subnet_record *subnet;
+
+ struct server_info_struct serv;
+ time_t death_time;
+};
+
+/* A workgroup structure. It contains a list of servers. */
+struct work_record {
+ struct work_record *next;
+ struct work_record *prev;
+
+ struct subnet_record *subnet;
+
+ struct server_record *serverlist;
+
+ /* Stage of development from non-local-master up to local-master browser. */
+ enum master_state mst_state;
+
+ /* Stage of development from non-domain-master to domain-master browser. */
+ enum domain_state dom_state;
+
+ /* Stage of development from non-logon-server to logon server. */
+ enum logon_state log_state;
+
+ /* Work group info. */
+ unstring work_group;
+ int token; /* Used when communicating with backup browsers. */
+ unstring local_master_browser_name; /* Current local master browser. */
+
+ /* Announce info. */
+ time_t lastannounce_time;
+ int announce_interval;
+ BOOL needannounce;
+
+ /* Timeout time for this workgroup. 0 means permanent. */
+ time_t death_time;
+
+ /* Election info */
+ BOOL RunningElection;
+ BOOL needelection;
+ int ElectionCount;
+ uint32 ElectionCriterion;
+
+ /* Domain master browser info. Used for efficient syncs. */
+ struct nmb_name dmb_name;
+ struct in_addr dmb_addr;
+};
+
+/* typedefs needed to define copy & free functions for userdata. */
+struct userdata_struct;
+
+typedef struct userdata_struct * (*userdata_copy_fn)(struct userdata_struct *);
+typedef void (*userdata_free_fn)(struct userdata_struct *);
+
+/* Structure to define any userdata passed around. */
+
+struct userdata_struct {
+ userdata_copy_fn copy_fn;
+ userdata_free_fn free_fn;
+ unsigned int userdata_len;
+ char data[16]; /* 16 is to ensure alignment/padding on all systems */
+};
+
+struct response_record;
+struct packet_struct;
+struct res_rec;
+
+/* typedef to define the function called when this response packet comes in. */
+typedef void (*response_function)(struct subnet_record *, struct response_record *,
+ struct packet_struct *);
+
+/* typedef to define the function called when this response record times out. */
+typedef void (*timeout_response_function)(struct subnet_record *,
+ struct response_record *);
+
+/* typedef to define the function called when the request that caused this
+ response record to be created is successful. */
+typedef void (*success_function)(struct subnet_record *, struct userdata_struct *, ...);
+
+/* typedef to define the function called when the request that caused this
+ response record to be created is unsuccessful. */
+typedef void (*fail_function)(struct subnet_record *, struct response_record *, ...);
+
+/* List of typedefs for success and fail functions of the different query
+ types. Used to catch any compile time prototype errors. */
+
+typedef void (*register_name_success_function)( struct subnet_record *,
+ struct userdata_struct *,
+ struct nmb_name *,
+ uint16,
+ int,
+ struct in_addr);
+typedef void (*register_name_fail_function)( struct subnet_record *,
+ struct response_record *,
+ struct nmb_name *);
+
+typedef void (*release_name_success_function)( struct subnet_record *,
+ struct userdata_struct *,
+ struct nmb_name *,
+ struct in_addr);
+typedef void (*release_name_fail_function)( struct subnet_record *,
+ struct response_record *,
+ struct nmb_name *);
+
+typedef void (*refresh_name_success_function)( struct subnet_record *,
+ struct userdata_struct *,
+ struct nmb_name *,
+ uint16,
+ int,
+ struct in_addr);
+typedef void (*refresh_name_fail_function)( struct subnet_record *,
+ struct response_record *,
+ struct nmb_name *);
+
+typedef void (*query_name_success_function)( struct subnet_record *,
+ struct userdata_struct *,
+ struct nmb_name *,
+ struct in_addr,
+ struct res_rec *answers);
+
+typedef void (*query_name_fail_function)( struct subnet_record *,
+ struct response_record *,
+ struct nmb_name *,
+ int);
+
+typedef void (*node_status_success_function)( struct subnet_record *,
+ struct userdata_struct *,
+ struct res_rec *,
+ struct in_addr);
+typedef void (*node_status_fail_function)( struct subnet_record *,
+ struct response_record *);
+
+/* Initiated name queries are recorded in this list to track any responses. */
+
+struct response_record {
+ struct response_record *next;
+ struct response_record *prev;
+
+ uint16 response_id;
+
+ /* Callbacks for packets received or not. */
+ response_function resp_fn;
+ timeout_response_function timeout_fn;
+
+ /* Callbacks for the request succeeding or not. */
+ success_function success_fn;
+ fail_function fail_fn;
+
+ struct packet_struct *packet;
+
+ struct userdata_struct *userdata;
+
+ int num_msgs;
+
+ time_t repeat_time;
+ time_t repeat_interval;
+ int repeat_count;
+
+ /* Recursion protection. */
+ BOOL in_expiration_processing;
+};
+
+/* A subnet structure. It contains a list of workgroups and netbios names. */
+
+/*
+ B nodes will have their own, totally separate subnet record, with their
+ own netbios name set. These do NOT interact with other subnet records'
+ netbios names.
+*/
+
+enum subnet_type {
+ NORMAL_SUBNET = 0, /* Subnet listed in interfaces list. */
+ UNICAST_SUBNET = 1, /* Subnet for unicast packets. */
+ REMOTE_BROADCAST_SUBNET = 2, /* Subnet for remote broadcasts. */
+ WINS_SERVER_SUBNET = 3 /* Only created if we are a WINS server. */
+};
+
+struct subnet_record {
+ struct subnet_record *next;
+ struct subnet_record *prev;
+
+ char *subnet_name; /* For Debug identification. */
+ enum subnet_type type; /* To catagorize the subnet. */
+
+ struct work_record *workgrouplist; /* List of workgroups. */
+ ubi_trRoot namelist[1]; /* List of netbios names. */
+ struct response_record *responselist; /* List of responses expected. */
+
+ BOOL namelist_changed;
+ BOOL work_changed;
+
+ struct in_addr bcast_ip;
+ struct in_addr mask_ip;
+ struct in_addr myip;
+ int nmb_sock; /* socket to listen for unicast 137. */
+ int dgram_sock; /* socket to listen for unicast 138. */
+};
+
+/* A resource record. */
+struct res_rec {
+ struct nmb_name rr_name;
+ int rr_type;
+ int rr_class;
+ int ttl;
+ int rdlength;
+ char rdata[MAX_DGRAM_SIZE];
+};
+
+/* Define these so we can pass info back to caller of name_query */
+#define NM_FLAGS_RS 0x80 /* Response. Cheat */
+#define NM_FLAGS_AA 0x40 /* Authoritative */
+#define NM_FLAGS_TC 0x20 /* Truncated */
+#define NM_FLAGS_RD 0x10 /* Recursion Desired */
+#define NM_FLAGS_RA 0x08 /* Recursion Available */
+#define NM_FLAGS_B 0x01 /* Broadcast */
+
+/* An nmb packet. */
+struct nmb_packet {
+ struct {
+ int name_trn_id;
+ int opcode;
+ BOOL response;
+ struct {
+ BOOL bcast;
+ BOOL recursion_available;
+ BOOL recursion_desired;
+ BOOL trunc;
+ BOOL authoritative;
+ } nm_flags;
+ int rcode;
+ int qdcount;
+ int ancount;
+ int nscount;
+ int arcount;
+ } header;
+
+ struct {
+ struct nmb_name question_name;
+ int question_type;
+ int question_class;
+ } question;
+
+ struct res_rec *answers;
+ struct res_rec *nsrecs;
+ struct res_rec *additional;
+};
+
+/* msg_type field options - from rfc1002. */
+
+#define DGRAM_UNIQUE 0x10
+#define DGRAM_GROUP 0x11
+#define DGRAM_BROADCAST 0x12
+#define DGRAM_ERROR 0x13
+#define DGRAM_QUERY_REQUEST 0x14
+#define DGRAM_POSITIVE_QUERY_RESPONSE 0x15
+#define DGRAM_NEGATIVE_QUERT_RESPONSE 0x16
+
+/* A datagram - this normally contains SMB data in the data[] array. */
+
+struct dgram_packet {
+ struct {
+ int msg_type;
+ struct {
+ enum node_type node_type;
+ BOOL first;
+ BOOL more;
+ } flags;
+ int dgm_id;
+ struct in_addr source_ip;
+ int source_port;
+ int dgm_length;
+ int packet_offset;
+ } header;
+ struct nmb_name source_name;
+ struct nmb_name dest_name;
+ int datasize;
+ char data[MAX_DGRAM_SIZE];
+};
+
+/* Define a structure used to queue packets. This will be a linked
+ list of nmb packets. */
+
+struct packet_struct
+{
+ struct packet_struct *next;
+ struct packet_struct *prev;
+ BOOL locked;
+ struct in_addr ip;
+ int port;
+ int fd;
+ time_t timestamp;
+ enum packet_type packet_type;
+ union {
+ struct nmb_packet nmb;
+ struct dgram_packet dgram;
+ } packet;
+};
+
+/* NETLOGON opcodes */
+
+#define QUERYFORPDC 7 /* Query for PDC. */
+#define SAM_UAS_CHANGE 10 /* Announce change to UAS or SAM. */
+#define QUERYFORPDC_R 12 /* Response to Query for PDC. */
+#define SAMLOGON 18
+#define SAMLOGON_R 19
+#define SAMLOGON_UNK_R 21
+#define SAMLOGON_AD_UNK_R 23
+#define SAMLOGON_AD_R 25
+
+/* Ids for netbios packet types. */
+
+#define ANN_HostAnnouncement 1
+#define ANN_AnnouncementRequest 2
+#define ANN_Election 8
+#define ANN_GetBackupListReq 9
+#define ANN_GetBackupListResp 10
+#define ANN_BecomeBackup 11
+#define ANN_DomainAnnouncement 12
+#define ANN_MasterAnnouncement 13
+#define ANN_ResetBrowserState 14
+#define ANN_LocalMasterAnnouncement 15
+
+
+/* Broadcast packet announcement intervals, in minutes. */
+
+/* Attempt to add domain logon and domain master names. */
+#define CHECK_TIME_ADD_DOM_NAMES 5
+
+/* Search for master browsers of workgroups samba knows about,
+ except default. */
+#define CHECK_TIME_MST_BROWSE 5
+
+/* Request backup browser announcements from other servers. */
+#define CHECK_TIME_ANNOUNCE_BACKUP 15
+
+/* Request host announcements from other servers: min and max of interval. */
+#define CHECK_TIME_MIN_HOST_ANNCE 3
+#define CHECK_TIME_MAX_HOST_ANNCE 12
+
+/* Announce as master to WINS server and any Primary Domain Controllers. */
+#define CHECK_TIME_MST_ANNOUNCE 15
+
+/* Time between syncs from domain master browser to local master browsers. */
+#define CHECK_TIME_DMB_TO_LMB_SYNC 15
+
+/* Do all remote announcements this often. */
+#define REMOTE_ANNOUNCE_INTERVAL 180
+
+/* what is the maximum period between name refreshes. Note that this only
+ affects non-permanent self names (in seconds) */
+#define MAX_REFRESH_TIME (60*20)
+
+/* The Extinction interval: 4 days, time a node will stay in released state */
+#define EXTINCTION_INTERVAL (4*24*60*60)
+
+/* The Extinction time-out: 1 day, time a node will stay in deleted state */
+#define EXTINCTION_TIMEOUT (24*60*60)
+
+/* Macro's to enumerate subnets either with or without
+ the UNICAST subnet. */
+
+extern struct subnet_record *subnetlist;
+extern struct subnet_record *unicast_subnet;
+extern struct subnet_record *wins_server_subnet;
+extern struct subnet_record *remote_broadcast_subnet;
+
+#define FIRST_SUBNET subnetlist
+#define NEXT_SUBNET_EXCLUDING_UNICAST(x) ((x)->next)
+#define NEXT_SUBNET_INCLUDING_UNICAST(x) (get_next_subnet_maybe_unicast((x)))
+
+/* wins replication record used between nmbd and wrepld */
+typedef struct _WINS_RECORD {
+ char name[17];
+ char type;
+ int nb_flags;
+ int wins_flags;
+ SMB_BIG_UINT id;
+ int num_ips;
+ struct in_addr ip[25];
+ struct in_addr wins_ip;
+} WINS_RECORD;
+
+/* To be removed. */
+enum state_type { TEST };
+#endif /* _NAMESERV_H_ */
diff --git a/source/include/nt_printing.h b/source/include/nt_printing.h
new file mode 100644
index 00000000000..762b1c69170
--- /dev/null
+++ b/source/include/nt_printing.h
@@ -0,0 +1,484 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-2000,
+ Copyright (C) Jean Francois Micouleau 1998-2000.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef NT_PRINTING_H_
+#define NT_PRINTING_H_
+
+#define ORIENTATION 0x00000001L
+#define PAPERSIZE 0x00000002L
+#define PAPERLENGTH 0x00000004L
+#define PAPERWIDTH 0x00000008L
+#define SCALE 0x00000010L
+#define COPIES 0x00000100L
+#define DEFAULTSOURCE 0x00000200L
+#define PRINTQUALITY 0x00000400L
+#define COLOR 0x00000800L
+#define DUPLEX 0x00001000L
+#define YRESOLUTION 0x00002000L
+#define TTOPTION 0x00004000L
+#define COLLATE 0x00008000L
+#define FORMNAME 0x00010000L
+#define LOGPIXELS 0x00020000L
+#define BITSPERPEL 0x00040000L
+#define PELSWIDTH 0x00080000L
+#define PELSHEIGHT 0x00100000L
+#define DISPLAYFLAGS 0x00200000L
+#define DISPLAYFREQUENCY 0x00400000L
+#define PANNINGWIDTH 0x00800000L
+#define PANNINGHEIGHT 0x01000000L
+
+#define ORIENT_PORTRAIT 1
+#define ORIENT_LANDSCAPE 2
+
+#define PAPER_FIRST PAPER_LETTER
+#define PAPER_LETTER 1 /* Letter 8 1/2 x 11 in */
+#define PAPER_LETTERSMALL 2 /* Letter Small 8 1/2 x 11 in */
+#define PAPER_TABLOID 3 /* Tabloid 11 x 17 in */
+#define PAPER_LEDGER 4 /* Ledger 17 x 11 in */
+#define PAPER_LEGAL 5 /* Legal 8 1/2 x 14 in */
+#define PAPER_STATEMENT 6 /* Statement 5 1/2 x 8 1/2 in */
+#define PAPER_EXECUTIVE 7 /* Executive 7 1/4 x 10 1/2 in */
+#define PAPER_A3 8 /* A3 297 x 420 mm */
+#define PAPER_A4 9 /* A4 210 x 297 mm */
+#define PAPER_A4SMALL 10 /* A4 Small 210 x 297 mm */
+#define PAPER_A5 11 /* A5 148 x 210 mm */
+#define PAPER_B4 12 /* B4 (JIS) 250 x 354 */
+#define PAPER_B5 13 /* B5 (JIS) 182 x 257 mm */
+#define PAPER_FOLIO 14 /* Folio 8 1/2 x 13 in */
+#define PAPER_QUARTO 15 /* Quarto 215 x 275 mm */
+#define PAPER_10X14 16 /* 10x14 in */
+#define PAPER_11X17 17 /* 11x17 in */
+#define PAPER_NOTE 18 /* Note 8 1/2 x 11 in */
+#define PAPER_ENV_9 19 /* Envelope #9 3 7/8 x 8 7/8 */
+#define PAPER_ENV_10 20 /* Envelope #10 4 1/8 x 9 1/2 */
+#define PAPER_ENV_11 21 /* Envelope #11 4 1/2 x 10 3/8 */
+#define PAPER_ENV_12 22 /* Envelope #12 4 \276 x 11 */
+#define PAPER_ENV_14 23 /* Envelope #14 5 x 11 1/2 */
+#define PAPER_CSHEET 24 /* C size sheet */
+#define PAPER_DSHEET 25 /* D size sheet */
+#define PAPER_ESHEET 26 /* E size sheet */
+#define PAPER_ENV_DL 27 /* Envelope DL 110 x 220mm */
+#define PAPER_ENV_C5 28 /* Envelope C5 162 x 229 mm */
+#define PAPER_ENV_C3 29 /* Envelope C3 324 x 458 mm */
+#define PAPER_ENV_C4 30 /* Envelope C4 229 x 324 mm */
+#define PAPER_ENV_C6 31 /* Envelope C6 114 x 162 mm */
+#define PAPER_ENV_C65 32 /* Envelope C65 114 x 229 mm */
+#define PAPER_ENV_B4 33 /* Envelope B4 250 x 353 mm */
+#define PAPER_ENV_B5 34 /* Envelope B5 176 x 250 mm */
+#define PAPER_ENV_B6 35 /* Envelope B6 176 x 125 mm */
+#define PAPER_ENV_ITALY 36 /* Envelope 110 x 230 mm */
+#define PAPER_ENV_MONARCH 37 /* Envelope Monarch 3.875 x 7.5 in */
+#define PAPER_ENV_PERSONAL 38 /* 6 3/4 Envelope 3 5/8 x 6 1/2 in */
+#define PAPER_FANFOLD_US 39 /* US Std Fanfold 14 7/8 x 11 in */
+#define PAPER_FANFOLD_STD_GERMAN 40 /* German Std Fanfold 8 1/2 x 12 in */
+#define PAPER_FANFOLD_LGL_GERMAN 41 /* German Legal Fanfold 8 1/2 x 13 in */
+
+#define PAPER_LAST PAPER_FANFOLD_LGL_GERMAN
+#define PAPER_USER 256
+
+#define BIN_FIRST BIN_UPPER
+#define BIN_UPPER 1
+#define BIN_ONLYONE 1
+#define BIN_LOWER 2
+#define BIN_MIDDLE 3
+#define BIN_MANUAL 4
+#define BIN_ENVELOPE 5
+#define BIN_ENVMANUAL 6
+#define BIN_AUTO 7
+#define BIN_TRACTOR 8
+#define BIN_SMALLFMT 9
+#define BIN_LARGEFMT 10
+#define BIN_LARGECAPACITY 11
+#define BIN_CASSETTE 14
+#define BIN_FORMSOURCE 15
+#define BIN_LAST BIN_FORMSOURCE
+
+#define BIN_USER 256 /* device specific bins start here */
+
+#define RES_DRAFT (-1)
+#define RES_LOW (-2)
+#define RES_MEDIUM (-3)
+#define RES_HIGH (-4)
+
+#define COLOR_MONOCHROME 1
+#define COLOR_COLOR 2
+
+#define DUP_SIMPLEX 1
+#define DUP_VERTICAL 2
+#define DUP_HORIZONTAL 3
+
+#define TT_BITMAP 1 /* print TT fonts as graphics */
+#define TT_DOWNLOAD 2 /* download TT fonts as soft fonts */
+#define TT_SUBDEV 3 /* substitute device fonts for TT fonts */
+
+#define COLLATE_FALSE 0
+#define COLLATE_TRUE 1
+
+typedef struct nt_printer_driver_info_level_3
+{
+ uint32 cversion;
+
+ fstring name;
+ fstring environment;
+ fstring driverpath;
+ fstring datafile;
+ fstring configfile;
+ fstring helpfile;
+ fstring monitorname;
+ fstring defaultdatatype;
+ fstring *dependentfiles;
+} NT_PRINTER_DRIVER_INFO_LEVEL_3;
+
+/* SPOOL_PRINTER_DRIVER_INFO_LEVEL_6 structure */
+typedef struct {
+ uint32 version;
+ fstring name;
+ fstring environment;
+ fstring driverpath;
+ fstring datafile;
+ fstring configfile;
+ fstring helpfile;
+ fstring monitorname;
+ fstring defaultdatatype;
+ fstring mfgname;
+ fstring oemurl;
+ fstring hardwareid;
+ fstring provider;
+ fstring *dependentfiles;
+ fstring *previousnames;
+} NT_PRINTER_DRIVER_INFO_LEVEL_6;
+
+
+typedef struct nt_printer_driver_info_level
+{
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 *info_3;
+ NT_PRINTER_DRIVER_INFO_LEVEL_6 *info_6;
+} NT_PRINTER_DRIVER_INFO_LEVEL;
+
+/* predefined registry key names for printer data */
+
+#define SPOOL_PRINTERDATA_KEY "PrinterDriverData"
+#define SPOOL_DSSPOOLER_KEY "DsSpooler"
+#define SPOOL_DSDRIVER_KEY "DsDriver"
+#define SPOOL_DSUSER_KEY "DsUser"
+#define SPOOL_PNPDATA_KEY "PnPData"
+#define SPOOL_OID_KEY "OID"
+
+/* predefined value names for printer data */
+#define SPOOL_REG_ASSETNUMBER "assetNumber"
+#define SPOOL_REG_BYTESPERMINUTE "bytesPerMinute"
+#define SPOOL_REG_DEFAULTPRIORITY "defaultPriority"
+#define SPOOL_REG_DESCRIPTION "description"
+#define SPOOL_REG_DRIVERNAME "driverName"
+#define SPOOL_REG_DRIVERVERSION "driverVersion"
+#define SPOOL_REG_FLAGS "flags"
+#define SPOOL_REG_LOCATION "location"
+#define SPOOL_REG_OPERATINGSYSTEM "operatingSystem"
+#define SPOOL_REG_OPERATINGSYSTEMHOTFIX "operatingSystemHotfix"
+#define SPOOL_REG_OPERATINGSYSTEMSERVICEPACK "operatingSystemServicePack"
+#define SPOOL_REG_OPERATINGSYSTEMVERSION "operatingSystemVersion"
+#define SPOOL_REG_PORTNAME "portName"
+#define SPOOL_REG_PRINTATTRIBUTES "printAttributes"
+#define SPOOL_REG_PRINTBINNAMES "printBinNames"
+#define SPOOL_REG_PRINTCOLLATE "printCollate"
+#define SPOOL_REG_PRINTCOLOR "printColor"
+#define SPOOL_REG_PRINTDUPLEXSUPPORTED "printDuplexSupported"
+#define SPOOL_REG_PRINTENDTIME "printEndTime"
+#define SPOOL_REG_PRINTERNAME "printerName"
+#define SPOOL_REG_PRINTFORMNAME "printFormName"
+#define SPOOL_REG_PRINTKEEPPRINTEDJOBS "printKeepPrintedJobs"
+#define SPOOL_REG_PRINTLANGUAGE "printLanguage"
+#define SPOOL_REG_PRINTMACADDRESS "printMACAddress"
+#define SPOOL_REG_PRINTMAXCOPIES "printMaxCopies"
+#define SPOOL_REG_PRINTMAXRESOLUTIONSUPPORTED "printMaxResolutionSupported"
+#define SPOOL_REG_PRINTMAXXEXTENT "printMaxXExtent"
+#define SPOOL_REG_PRINTMAXYEXTENT "printMaxYExtent"
+#define SPOOL_REG_PRINTMEDIAREADY "printMediaReady"
+#define SPOOL_REG_PRINTMEDIASUPPORTED "printMediaSupported"
+#define SPOOL_REG_PRINTMEMORY "printMemory"
+#define SPOOL_REG_PRINTMINXEXTENT "printMinXExtent"
+#define SPOOL_REG_PRINTMINYEXTENT "printMinYExtent"
+#define SPOOL_REG_PRINTNETWORKADDRESS "printNetworkAddress"
+#define SPOOL_REG_PRINTNOTIFY "printNotify"
+#define SPOOL_REG_PRINTNUMBERUP "printNumberUp"
+#define SPOOL_REG_PRINTORIENTATIONSSUPPORTED "printOrientationsSupported"
+#define SPOOL_REG_PRINTOWNER "printOwner"
+#define SPOOL_REG_PRINTPAGESPERMINUTE "printPagesPerMinute"
+#define SPOOL_REG_PRINTRATE "printRate"
+#define SPOOL_REG_PRINTRATEUNIT "printRateUnit"
+#define SPOOL_REG_PRINTSEPARATORFILE "printSeparatorFile"
+#define SPOOL_REG_PRINTSHARENAME "printShareName"
+#define SPOOL_REG_PRINTSPOOLING "printSpooling"
+#define SPOOL_REGVAL_PRINTWHILESPOOLING "PrintWhileSpooling"
+#define SPOOL_REGVAL_PRINTAFTERSPOOLED "PrintAfterSpooled"
+#define SPOOL_REGVAL_PRINTDIRECT "PrintDirect"
+#define SPOOL_REG_PRINTSTAPLINGSUPPORTED "printStaplingSupported"
+#define SPOOL_REG_PRINTSTARTTIME "printStartTime"
+#define SPOOL_REG_PRINTSTATUS "printStatus"
+#define SPOOL_REG_PRIORITY "priority"
+#define SPOOL_REG_SERVERNAME "serverName"
+#define SPOOL_REG_SHORTSERVERNAME "shortServerName"
+#define SPOOL_REG_UNCNAME "uNCName"
+#define SPOOL_REG_URL "url"
+#define SPOOL_REG_VERSIONNUMBER "versionNumber"
+
+/* container for a single registry key */
+
+typedef struct {
+ char *name;
+ REGVAL_CTR values;
+} NT_PRINTER_KEY;
+
+/* container for all printer data */
+
+typedef struct {
+ int num_keys;
+ NT_PRINTER_KEY *keys;
+} NT_PRINTER_DATA;
+
+#define MAXDEVICENAME 32
+
+typedef struct ntdevicemode
+{
+ fstring devicename;
+ fstring formname;
+
+ uint16 specversion;
+ uint16 driverversion;
+ uint16 size;
+ uint16 driverextra;
+ uint16 orientation;
+ uint16 papersize;
+ uint16 paperlength;
+ uint16 paperwidth;
+ uint16 scale;
+ uint16 copies;
+ uint16 defaultsource;
+ uint16 printquality;
+ uint16 color;
+ uint16 duplex;
+ uint16 yresolution;
+ uint16 ttoption;
+ uint16 collate;
+ uint16 logpixels;
+
+ uint32 fields;
+ uint32 bitsperpel;
+ uint32 pelswidth;
+ uint32 pelsheight;
+ uint32 displayflags;
+ uint32 displayfrequency;
+ uint32 icmmethod;
+ uint32 icmintent;
+ uint32 mediatype;
+ uint32 dithertype;
+ uint32 reserved1;
+ uint32 reserved2;
+ uint32 panningwidth;
+ uint32 panningheight;
+ uint8 *private;
+} NT_DEVICEMODE;
+
+typedef struct nt_printer_info_level_2
+{
+ uint32 attributes;
+ uint32 priority;
+ uint32 default_priority;
+ uint32 starttime;
+ uint32 untiltime;
+ uint32 status;
+ uint32 cjobs;
+ uint32 averageppm;
+ fstring servername;
+ fstring printername;
+ fstring sharename;
+ fstring portname;
+ fstring drivername;
+ pstring comment;
+ fstring location;
+ NT_DEVICEMODE *devmode;
+ fstring sepfile;
+ fstring printprocessor;
+ fstring datatype;
+ fstring parameters;
+ NT_PRINTER_DATA data;
+ SEC_DESC_BUF *secdesc_buf;
+ uint32 changeid;
+ uint32 c_setprinter;
+ uint32 setuptime;
+} NT_PRINTER_INFO_LEVEL_2;
+
+typedef struct nt_printer_info_level
+{
+ NT_PRINTER_INFO_LEVEL_2 *info_2;
+} NT_PRINTER_INFO_LEVEL;
+
+typedef struct
+{
+ fstring name;
+ uint32 flag;
+ uint32 width;
+ uint32 length;
+ uint32 left;
+ uint32 top;
+ uint32 right;
+ uint32 bottom;
+} nt_forms_struct;
+
+/*
+typedef struct _form
+{
+ uint32 flags;
+ uint32 name_ptr;
+ uint32 size_x;
+ uint32 size_y;
+ uint32 left;
+ uint32 top;
+ uint32 right;
+ uint32 bottom;
+ UNISTR2 name;
+} FORM;
+*/
+
+#ifndef SAMBA_PRINTER_PORT_NAME
+#define SAMBA_PRINTER_PORT_NAME "Samba Printer Port"
+#endif
+
+/* DOS header format */
+#define DOS_HEADER_SIZE 64
+#define DOS_HEADER_MAGIC_OFFSET 0
+#define DOS_HEADER_MAGIC 0x5A4D
+#define DOS_HEADER_LFANEW_OFFSET 60
+
+/* New Executable format (Win or OS/2 1.x segmented) */
+#define NE_HEADER_SIZE 64
+#define NE_HEADER_SIGNATURE_OFFSET 0
+#define NE_HEADER_SIGNATURE 0x454E
+#define NE_HEADER_TARGET_OS_OFFSET 54
+#define NE_HEADER_TARGOS_WIN 0x02
+#define NE_HEADER_MINOR_VER_OFFSET 62
+#define NE_HEADER_MAJOR_VER_OFFSET 63
+
+/* Portable Executable format */
+#define PE_HEADER_SIZE 248
+#define PE_HEADER_SIGNATURE_OFFSET 0
+#define PE_HEADER_SIGNATURE 0x00004550
+#define PE_HEADER_MACHINE_OFFSET 4
+#define PE_HEADER_MACHINE_I386 0x14c
+#define PE_HEADER_NUMBER_OF_SECTIONS 6
+#define PE_HEADER_MAJOR_OS_VER_OFFSET 64
+#define PE_HEADER_MINOR_OS_VER_OFFSET 66
+#define PE_HEADER_MAJOR_IMG_VER_OFFSET 68
+#define PE_HEADER_MINOR_IMG_VER_OFFSET 70
+#define PE_HEADER_MAJOR_SS_VER_OFFSET 72
+#define PE_HEADER_MINOR_SS_VER_OFFSET 74
+#define PE_HEADER_SECT_HEADER_SIZE 40
+#define PE_HEADER_SECT_NAME_OFFSET 0
+#define PE_HEADER_SECT_SIZE_DATA_OFFSET 16
+#define PE_HEADER_SECT_PTR_DATA_OFFSET 20
+
+/* Microsoft file version format */
+#define VS_SIGNATURE "VS_VERSION_INFO"
+#define VS_MAGIC_VALUE 0xfeef04bd
+#define VS_MAJOR_OFFSET 8
+#define VS_MINOR_OFFSET 12
+#define VS_VERSION_INFO_UNICODE_SIZE (sizeof(VS_SIGNATURE)*2+4+VS_MINOR_OFFSET+4) /* not true size! */
+#define VS_VERSION_INFO_SIZE (sizeof(VS_SIGNATURE)+4+VS_MINOR_OFFSET+4) /* not true size! */
+#define VS_NE_BUF_SIZE 4096 /* Must be > 2*VS_VERSION_INFO_SIZE */
+
+/* Notify spoolss clients that something has changed. The
+ notification data is either stored in two uint32 values or a
+ variable length array. */
+
+#define SPOOLSS_NOTIFY_MSG_UNIX_JOBID 0x0001 /* Job id is unix */
+
+typedef struct spoolss_notify_msg {
+ fstring printer; /* Name of printer notified */
+ uint32 type; /* Printer or job notify */
+ uint32 field; /* Notify field changed */
+ uint32 id; /* Job id */
+ uint32 len; /* Length of data, 0 for two uint32 value */
+ uint32 flags;
+ union {
+ uint32 value[2];
+ char *data;
+ } notify;
+} SPOOLSS_NOTIFY_MSG;
+
+typedef struct {
+ fstring printername;
+ uint32 num_msgs;
+ SPOOLSS_NOTIFY_MSG *msgs;
+} SPOOLSS_NOTIFY_MSG_GROUP;
+
+typedef struct {
+ TALLOC_CTX *ctx;
+ uint32 num_groups;
+ SPOOLSS_NOTIFY_MSG_GROUP *msg_groups;
+} SPOOLSS_NOTIFY_MSG_CTR;
+
+#define PRINTER_HANDLE_IS_PRINTER 0
+#define PRINTER_HANDLE_IS_PRINTSERVER 1
+
+/* structure to store the printer handles */
+/* and a reference to what it's pointing to */
+/* and the notify info asked about */
+/* that's the central struct */
+typedef struct _Printer{
+ struct _Printer *prev, *next;
+ BOOL document_started;
+ BOOL page_started;
+ uint32 jobid; /* jobid in printing backend */
+ BOOL printer_type;
+ TALLOC_CTX *ctx;
+ union {
+ fstring handlename;
+ fstring printerservername;
+ } dev;
+ uint32 type;
+ uint32 access_granted;
+ struct {
+ uint32 flags;
+ uint32 options;
+ fstring localmachine;
+ uint32 printerlocal;
+ SPOOL_NOTIFY_OPTION *option;
+ POLICY_HND client_hnd;
+ BOOL client_connected;
+ uint32 change;
+ /* are we in a FindNextPrinterChangeNotify() call? */
+ BOOL fnpcn;
+ } notify;
+ struct {
+ fstring machine;
+ fstring user;
+ } client;
+
+ /* devmode sent in the OpenPrinter() call */
+ NT_DEVICEMODE *nt_devmode;
+
+ /* cache the printer info */
+ NT_PRINTER_INFO_LEVEL *printer_info;
+
+} Printer_entry;
+
+#endif /* NT_PRINTING_H_ */
diff --git a/source/include/nt_status.h b/source/include/nt_status.h
new file mode 100644
index 00000000000..9747f73eb18
--- /dev/null
+++ b/source/include/nt_status.h
@@ -0,0 +1,63 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup, plus a whole lot more.
+
+ Copyright (C) Andrew Tridgell 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _NT_STATUS_H
+#define _NT_STATUS_H
+
+/* The Splint code analysis tool doesn't like immediate structures. */
+
+#ifdef _SPLINT_ /* http://www.splint.org */
+#undef HAVE_IMMEDIATE_STRUCTURES
+#endif
+
+/* the following rather strange looking definitions of NTSTATUS and WERROR
+ and there in order to catch common coding errors where different error types
+ are mixed up. This is especially important as we slowly convert Samba
+ from using BOOL for internal functions
+*/
+
+#if defined(HAVE_IMMEDIATE_STRUCTURES)
+typedef struct {uint32 v;} NTSTATUS;
+#define NT_STATUS(x) ((NTSTATUS) { x })
+#define NT_STATUS_V(x) ((x).v)
+#else
+typedef uint32 NTSTATUS;
+#define NT_STATUS(x) (x)
+#define NT_STATUS_V(x) (x)
+#endif
+
+#if defined(HAVE_IMMEDIATE_STRUCTURES)
+typedef struct {uint32 v;} WERROR;
+#define W_ERROR(x) ((WERROR) { x })
+#define W_ERROR_V(x) ((x).v)
+#else
+typedef uint32 WERROR;
+#define W_ERROR(x) (x)
+#define W_ERROR_V(x) (x)
+#endif
+
+#define NT_STATUS_IS_OK(x) (NT_STATUS_V(x) == 0)
+#define NT_STATUS_IS_ERR(x) ((NT_STATUS_V(x) & 0xc0000000) == 0xc0000000)
+#define NT_STATUS_EQUAL(x,y) (NT_STATUS_V(x) == NT_STATUS_V(y))
+#define W_ERROR_IS_OK(x) (W_ERROR_V(x) == 0)
+#define W_ERROR_EQUAL(x,y) (W_ERROR_V(x) == W_ERROR_V(y))
+
+#endif
diff --git a/source/include/ntdomain.h b/source/include/ntdomain.h
new file mode 100644
index 00000000000..4e6795a85d5
--- /dev/null
+++ b/source/include/ntdomain.h
@@ -0,0 +1,417 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+ Copyright (C) Paul Ashton 1997
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _NT_DOMAIN_H /* _NT_DOMAIN_H */
+#define _NT_DOMAIN_H
+
+struct uuid
+{
+ uint32 time_low;
+ uint16 time_mid;
+ uint16 time_hi_and_version;
+ uint8 clock_seq[2];
+ uint8 node[6];
+};
+#define UUID_SIZE 16
+
+#define UUID_FLAT_SIZE 16
+typedef struct uuid_flat
+{
+ uint8 info[UUID_FLAT_SIZE];
+} UUID_FLAT;
+
+/* dce/rpc support */
+#include "rpc_dce.h"
+
+/* miscellaneous structures / defines */
+#include "rpc_misc.h"
+
+#include "rpc_creds.h"
+
+#include "talloc.h"
+
+/*
+ * A bunch of stuff that was put into smb.h
+ * in the NTDOM branch - it didn't belong there.
+ */
+
+typedef struct _prs_struct
+{
+ BOOL io; /* parsing in or out of data stream */
+ /*
+ * If the (incoming) data is big-endian. On output we are
+ * always little-endian.
+ */
+ BOOL bigendian_data;
+ uint8 align; /* data alignment */
+ BOOL is_dynamic; /* Do we own this memory or not ? */
+ uint32 data_offset; /* Current working offset into data. */
+ uint32 buffer_size; /* Current allocated size of the buffer. */
+ uint32 grow_size; /* size requested via prs_grow() calls */
+ char *data_p; /* The buffer itself. */
+ TALLOC_CTX *mem_ctx; /* When unmarshalling, use this.... */
+} prs_struct;
+
+/*
+ * Defines for io member of prs_struct.
+ */
+
+#define MARSHALL 0
+#define UNMARSHALL 1
+
+#define MARSHALLING(ps) (!(ps)->io)
+#define UNMARSHALLING(ps) ((ps)->io)
+
+#define RPC_BIG_ENDIAN 1
+#define RPC_LITTLE_ENDIAN 0
+
+#define RPC_PARSE_ALIGN 4
+
+typedef struct _output_data {
+ /*
+ * Raw RPC output data. This does not include RPC headers or footers.
+ */
+ prs_struct rdata;
+
+ /* The amount of data sent from the current rdata struct. */
+ uint32 data_sent_length;
+
+ /*
+ * The current PDU being returned. This inclues
+ * headers, data and authentication footer.
+ */
+ unsigned char current_pdu[MAX_PDU_FRAG_LEN];
+
+ /* The amount of data in the current_pdu buffer. */
+ uint32 current_pdu_len;
+
+ /* The amount of data sent from the current PDU. */
+ uint32 current_pdu_sent;
+} output_data;
+
+typedef struct _input_data {
+ /*
+ * This is the current incoming pdu. The data here
+ * is collected via multiple writes until a complete
+ * pdu is seen, then the data is copied into the in_data
+ * structure. The maximum size of this is 0x1630 (MAX_PDU_FRAG_LEN).
+ */
+ unsigned char current_in_pdu[MAX_PDU_FRAG_LEN];
+
+ /*
+ * The amount of data needed to complete the in_pdu.
+ * If this is zero, then we are at the start of a new
+ * pdu.
+ */
+ uint32 pdu_needed_len;
+
+ /*
+ * The amount of data received so far in the in_pdu.
+ * If this is zero, then we are at the start of a new
+ * pdu.
+ */
+ uint32 pdu_received_len;
+
+ /*
+ * This is the collection of input data with all
+ * the rpc headers and auth footers removed.
+ * The maximum length of this (1Mb) is strictly enforced.
+ */
+ prs_struct data;
+} input_data;
+
+/*
+ * Handle database - stored per pipe.
+ */
+
+struct policy
+{
+ struct policy *next, *prev;
+
+ POLICY_HND pol_hnd;
+
+ void *data_ptr;
+ void (*free_fn)(void *);
+
+};
+
+struct handle_list {
+ struct policy *Policy; /* List of policies. */
+ size_t count; /* Current number of handles. */
+ size_t pipe_ref_count; /* Number of pipe handles referring to this list. */
+};
+
+/* Domain controller authentication protocol info */
+struct dcinfo
+{
+ DOM_CHAL clnt_chal; /* Initial challenge received from client */
+ DOM_CHAL srv_chal; /* Initial server challenge */
+ DOM_CRED clnt_cred; /* Last client credential */
+ DOM_CRED srv_cred; /* Last server credential */
+
+ uchar sess_key[8]; /* Session key */
+ uchar md4pw[16]; /* md4(machine password) */
+
+ fstring mach_acct; /* Machine name we've authenticated. */
+
+ fstring remote_machine; /* Machine name we've authenticated. */
+
+ BOOL challenge_sent;
+ BOOL got_session_key;
+ BOOL authenticated;
+
+};
+
+typedef struct pipe_rpc_fns {
+
+ struct pipe_rpc_fns *next, *prev;
+
+ /* RPC function table associated with the current rpc_bind (associated by context) */
+
+ struct api_struct *cmds;
+ int n_cmds;
+ uint32 context_id;
+
+} PIPE_RPC_FNS;
+
+/*
+ * DCE/RPC-specific samba-internal-specific handling of data on
+ * NamedPipes.
+ */
+
+typedef struct pipes_struct
+{
+ struct pipes_struct *next, *prev;
+
+ connection_struct *conn;
+ uint16 vuid; /* points to the unauthenticated user that opened this pipe. */
+
+ fstring name;
+ fstring pipe_srv_name;
+
+ /* linked list of rpc dispatch tables associated
+ with the open rpc contexts */
+
+ PIPE_RPC_FNS *contexts;
+
+ RPC_HDR hdr; /* Incoming RPC header. */
+ RPC_HDR_REQ hdr_req; /* Incoming request header. */
+
+ uint32 ntlmssp_chal_flags; /* Client challenge flags. */
+ BOOL ntlmssp_auth_requested; /* If the client wanted authenticated rpc. */
+ BOOL ntlmssp_auth_validated; /* If the client *got* authenticated rpc. */
+ unsigned char challenge[8];
+ unsigned char ntlmssp_hash[258];
+ uint32 ntlmssp_seq_num;
+ struct dcinfo dc; /* Keeps the creds data. */
+
+ /* Hmm. In my understanding the authentication happens
+ implicitly later, so there are no two stages for
+ schannel. */
+
+ BOOL netsec_auth_validated;
+ struct netsec_auth_struct netsec_auth;
+
+ /*
+ * Windows user info.
+ */
+ fstring user_name;
+ fstring domain;
+ fstring wks;
+
+ /*
+ * Unix user name and credentials.
+ */
+
+ fstring pipe_user_name;
+ struct current_user pipe_user;
+
+ DATA_BLOB session_key;
+
+ /*
+ * Set to true when an RPC bind has been done on this pipe.
+ */
+
+ BOOL pipe_bound;
+
+ /*
+ * Set to true when we should return fault PDU's for everything.
+ */
+
+ BOOL fault_state;
+
+ /*
+ * Set to true when we should return fault PDU's for a bad handle.
+ */
+
+ BOOL bad_handle_fault_state;
+
+ /*
+ * Set to RPC_BIG_ENDIAN when dealing with big-endian PDU's
+ */
+
+ BOOL endian;
+
+ /*
+ * Struct to deal with multiple pdu inputs.
+ */
+
+ input_data in_data;
+
+ /*
+ * Struct to deal with multiple pdu outputs.
+ */
+
+ output_data out_data;
+
+ /* talloc context to use when allocating memory on this pipe. */
+ TALLOC_CTX *mem_ctx;
+
+ /* handle database to use on this pipe. */
+ struct handle_list *pipe_handles;
+
+} pipes_struct;
+
+typedef struct smb_np_struct
+{
+ struct smb_np_struct *next, *prev;
+ int pnum;
+ connection_struct *conn;
+ uint16 vuid; /* points to the unauthenticated user that opened this pipe. */
+ BOOL open; /* open connection */
+ uint16 device_state;
+ uint16 priority;
+ fstring name;
+
+ /* When replying to an SMBtrans, this is the maximum amount of
+ data that can be sent in the initial reply. */
+ int max_trans_reply;
+
+ /*
+ * NamedPipe state information.
+ *
+ * (e.g. typecast a np_struct, above).
+ */
+ void *np_state;
+
+ /*
+ * NamedPipe functions, to be called to perform
+ * Named Pipe transactions on request from an
+ * SMB client.
+ */
+
+ /* call to create a named pipe connection.
+ * returns: state information representing the connection.
+ * is stored in np_state, above.
+ */
+ void * (*namedpipe_create)(char *pipe_name,
+ connection_struct *conn, uint16 vuid);
+
+ /* call to perform a write / read namedpipe transaction.
+ * TransactNamedPipe is weird: it returns whether there
+ * is more data outstanding to be read, and the
+ * caller is expected to take note and follow up with
+ * read requests.
+ */
+ ssize_t (*namedpipe_transact)(void *np_state,
+ char *data, int len,
+ char *rdata, int rlen,
+ BOOL *pipe_outstanding);
+
+ /* call to perform a write namedpipe operation
+ */
+ ssize_t (*namedpipe_write)(void * np_state,
+ char *data, size_t n);
+
+ /* call to perform a read namedpipe operation.
+ *
+ * NOTE: the only reason that the pipe_outstanding
+ * argument is here is because samba does not use
+ * the namedpipe_transact function yet: instead,
+ * it performs the same as what namedpipe_transact
+ * does - a write, followed by a read.
+ *
+ * when samba is modified to use namedpipe_transact,
+ * the pipe_outstanding argument may be removed.
+ */
+ ssize_t (*namedpipe_read)(void * np_state,
+ char *data, size_t max_len,
+ BOOL *pipe_outstanding);
+
+ /* call to close a namedpipe.
+ * function is expected to perform all cleanups
+ * necessary, free all memory etc.
+ *
+ * returns True if cleanup was successful (not that
+ * we particularly care).
+ */
+ BOOL (*namedpipe_close)(void * np_state);
+
+} smb_np_struct;
+
+struct api_struct
+{
+ const char *name;
+ uint8 opnum;
+ BOOL (*fn) (pipes_struct *);
+};
+
+typedef struct
+{
+ uint32 rid;
+ const char *name;
+
+} rid_name;
+
+/*
+ * higher order functions for use with msrpc client code
+ */
+
+#define PRINT_INFO_FN(fn)\
+ void (*fn)(const char*, uint32, uint32, void *const *const)
+#define JOB_INFO_FN(fn)\
+ void (*fn)(const char*, const char*, uint32, uint32, void *const *const)
+
+/* end higher order functions */
+
+
+/* security descriptor structures */
+#include "rpc_secdes.h"
+
+/* pac */
+#include "authdata.h"
+
+/* different dce/rpc pipes */
+#include "rpc_lsa.h"
+#include "rpc_netlogon.h"
+#include "rpc_reg.h"
+#include "rpc_samr.h"
+#include "rpc_srvsvc.h"
+#include "rpc_wkssvc.h"
+#include "rpc_spoolss.h"
+#include "rpc_dfs.h"
+#include "rpc_ds.h"
+#include "rpc_echo.h"
+#include "rpc_epmapper.h"
+#include "rpc_shutdown.h"
+
+#endif /* _NT_DOMAIN_H */
diff --git a/source/include/nterr.h b/source/include/nterr.h
new file mode 100644
index 00000000000..19c70cffcc9
--- /dev/null
+++ b/source/include/nterr.h
@@ -0,0 +1,564 @@
+/*
+ Unix SMB/CIFS implementation.
+ NT error code constants
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) John H Terpstra 1996-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Paul Ashton 1998-2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _NTERR_H
+#define _NTERR_H
+
+/* Win32 Status codes. */
+
+#define STATUS_BUFFER_OVERFLOW NT_STATUS(0x80000005)
+#define NT_STATUS_NO_MORE_ENTRIES NT_STATUS(0x8000001a)
+
+#define STATUS_MORE_ENTRIES NT_STATUS(0x0105)
+#define STATUS_SOME_UNMAPPED NT_STATUS(0x0107)
+#define ERROR_INVALID_PARAMETER NT_STATUS(0x0057)
+#define ERROR_INSUFFICIENT_BUFFER NT_STATUS(0x007a)
+#define STATUS_NOTIFY_ENUM_DIR NT_STATUS(0x010c)
+#define ERROR_INVALID_DATATYPE NT_STATUS(0x070c)
+
+/* Win32 Error codes extracted using a loop in smbclient then printing a
+ netmon sniff to a file. */
+
+/*
+ --------------
+ / \
+ / REST \
+ / IN \
+ / PEACE \
+ / \
+ | NT_STATUS_NOPROBLEMO |
+ | |
+ | |
+ | 4 September |
+ | |
+ | 2001 |
+ *| * * * | *
+ _________)/\\_//(\/(/\)/\//\/\///|_)_______
+*/
+
+#define NT_STATUS_OK NT_STATUS(0x0000)
+#define NT_STATUS_UNSUCCESSFUL NT_STATUS(0xC0000000 | 0x0001)
+#define NT_STATUS_NOT_IMPLEMENTED NT_STATUS(0xC0000000 | 0x0002)
+#define NT_STATUS_INVALID_INFO_CLASS NT_STATUS(0xC0000000 | 0x0003)
+#define NT_STATUS_INFO_LENGTH_MISMATCH NT_STATUS(0xC0000000 | 0x0004)
+#define NT_STATUS_ACCESS_VIOLATION NT_STATUS(0xC0000000 | 0x0005)
+#define NT_STATUS_IN_PAGE_ERROR NT_STATUS(0xC0000000 | 0x0006)
+#define NT_STATUS_PAGEFILE_QUOTA NT_STATUS(0xC0000000 | 0x0007)
+#define NT_STATUS_INVALID_HANDLE NT_STATUS(0xC0000000 | 0x0008)
+#define NT_STATUS_BAD_INITIAL_STACK NT_STATUS(0xC0000000 | 0x0009)
+#define NT_STATUS_BAD_INITIAL_PC NT_STATUS(0xC0000000 | 0x000a)
+#define NT_STATUS_INVALID_CID NT_STATUS(0xC0000000 | 0x000b)
+#define NT_STATUS_TIMER_NOT_CANCELED NT_STATUS(0xC0000000 | 0x000c)
+#define NT_STATUS_INVALID_PARAMETER NT_STATUS(0xC0000000 | 0x000d)
+#define NT_STATUS_NO_SUCH_DEVICE NT_STATUS(0xC0000000 | 0x000e)
+#define NT_STATUS_NO_SUCH_FILE NT_STATUS(0xC0000000 | 0x000f)
+#define NT_STATUS_INVALID_DEVICE_REQUEST NT_STATUS(0xC0000000 | 0x0010)
+#define NT_STATUS_END_OF_FILE NT_STATUS(0xC0000000 | 0x0011)
+#define NT_STATUS_WRONG_VOLUME NT_STATUS(0xC0000000 | 0x0012)
+#define NT_STATUS_NO_MEDIA_IN_DEVICE NT_STATUS(0xC0000000 | 0x0013)
+#define NT_STATUS_UNRECOGNIZED_MEDIA NT_STATUS(0xC0000000 | 0x0014)
+#define NT_STATUS_NONEXISTENT_SECTOR NT_STATUS(0xC0000000 | 0x0015)
+#define NT_STATUS_MORE_PROCESSING_REQUIRED NT_STATUS(0xC0000000 | 0x0016)
+#define NT_STATUS_NO_MEMORY NT_STATUS(0xC0000000 | 0x0017)
+#define NT_STATUS_CONFLICTING_ADDRESSES NT_STATUS(0xC0000000 | 0x0018)
+#define NT_STATUS_NOT_MAPPED_VIEW NT_STATUS(0xC0000000 | 0x0019)
+#define NT_STATUS_UNABLE_TO_FREE_VM NT_STATUS(0xC0000000 | 0x001a)
+#define NT_STATUS_UNABLE_TO_DELETE_SECTION NT_STATUS(0xC0000000 | 0x001b)
+#define NT_STATUS_INVALID_SYSTEM_SERVICE NT_STATUS(0xC0000000 | 0x001c)
+#define NT_STATUS_ILLEGAL_INSTRUCTION NT_STATUS(0xC0000000 | 0x001d)
+#define NT_STATUS_INVALID_LOCK_SEQUENCE NT_STATUS(0xC0000000 | 0x001e)
+#define NT_STATUS_INVALID_VIEW_SIZE NT_STATUS(0xC0000000 | 0x001f)
+#define NT_STATUS_INVALID_FILE_FOR_SECTION NT_STATUS(0xC0000000 | 0x0020)
+#define NT_STATUS_ALREADY_COMMITTED NT_STATUS(0xC0000000 | 0x0021)
+#define NT_STATUS_ACCESS_DENIED NT_STATUS(0xC0000000 | 0x0022)
+#define NT_STATUS_BUFFER_TOO_SMALL NT_STATUS(0xC0000000 | 0x0023)
+#define NT_STATUS_OBJECT_TYPE_MISMATCH NT_STATUS(0xC0000000 | 0x0024)
+#define NT_STATUS_NONCONTINUABLE_EXCEPTION NT_STATUS(0xC0000000 | 0x0025)
+#define NT_STATUS_INVALID_DISPOSITION NT_STATUS(0xC0000000 | 0x0026)
+#define NT_STATUS_UNWIND NT_STATUS(0xC0000000 | 0x0027)
+#define NT_STATUS_BAD_STACK NT_STATUS(0xC0000000 | 0x0028)
+#define NT_STATUS_INVALID_UNWIND_TARGET NT_STATUS(0xC0000000 | 0x0029)
+#define NT_STATUS_NOT_LOCKED NT_STATUS(0xC0000000 | 0x002a)
+#define NT_STATUS_PARITY_ERROR NT_STATUS(0xC0000000 | 0x002b)
+#define NT_STATUS_UNABLE_TO_DECOMMIT_VM NT_STATUS(0xC0000000 | 0x002c)
+#define NT_STATUS_NOT_COMMITTED NT_STATUS(0xC0000000 | 0x002d)
+#define NT_STATUS_INVALID_PORT_ATTRIBUTES NT_STATUS(0xC0000000 | 0x002e)
+#define NT_STATUS_PORT_MESSAGE_TOO_LONG NT_STATUS(0xC0000000 | 0x002f)
+#define NT_STATUS_INVALID_PARAMETER_MIX NT_STATUS(0xC0000000 | 0x0030)
+#define NT_STATUS_INVALID_QUOTA_LOWER NT_STATUS(0xC0000000 | 0x0031)
+#define NT_STATUS_DISK_CORRUPT_ERROR NT_STATUS(0xC0000000 | 0x0032)
+#define NT_STATUS_OBJECT_NAME_INVALID NT_STATUS(0xC0000000 | 0x0033)
+#define NT_STATUS_OBJECT_NAME_NOT_FOUND NT_STATUS(0xC0000000 | 0x0034)
+#define NT_STATUS_OBJECT_NAME_COLLISION NT_STATUS(0xC0000000 | 0x0035)
+#define NT_STATUS_HANDLE_NOT_WAITABLE NT_STATUS(0xC0000000 | 0x0036)
+#define NT_STATUS_PORT_DISCONNECTED NT_STATUS(0xC0000000 | 0x0037)
+#define NT_STATUS_DEVICE_ALREADY_ATTACHED NT_STATUS(0xC0000000 | 0x0038)
+#define NT_STATUS_OBJECT_PATH_INVALID NT_STATUS(0xC0000000 | 0x0039)
+#define NT_STATUS_OBJECT_PATH_NOT_FOUND NT_STATUS(0xC0000000 | 0x003a)
+#define NT_STATUS_OBJECT_PATH_SYNTAX_BAD NT_STATUS(0xC0000000 | 0x003b)
+#define NT_STATUS_DATA_OVERRUN NT_STATUS(0xC0000000 | 0x003c)
+#define NT_STATUS_DATA_LATE_ERROR NT_STATUS(0xC0000000 | 0x003d)
+#define NT_STATUS_DATA_ERROR NT_STATUS(0xC0000000 | 0x003e)
+#define NT_STATUS_CRC_ERROR NT_STATUS(0xC0000000 | 0x003f)
+#define NT_STATUS_SECTION_TOO_BIG NT_STATUS(0xC0000000 | 0x0040)
+#define NT_STATUS_PORT_CONNECTION_REFUSED NT_STATUS(0xC0000000 | 0x0041)
+#define NT_STATUS_INVALID_PORT_HANDLE NT_STATUS(0xC0000000 | 0x0042)
+#define NT_STATUS_SHARING_VIOLATION NT_STATUS(0xC0000000 | 0x0043)
+#define NT_STATUS_QUOTA_EXCEEDED NT_STATUS(0xC0000000 | 0x0044)
+#define NT_STATUS_INVALID_PAGE_PROTECTION NT_STATUS(0xC0000000 | 0x0045)
+#define NT_STATUS_MUTANT_NOT_OWNED NT_STATUS(0xC0000000 | 0x0046)
+#define NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED NT_STATUS(0xC0000000 | 0x0047)
+#define NT_STATUS_PORT_ALREADY_SET NT_STATUS(0xC0000000 | 0x0048)
+#define NT_STATUS_SECTION_NOT_IMAGE NT_STATUS(0xC0000000 | 0x0049)
+#define NT_STATUS_SUSPEND_COUNT_EXCEEDED NT_STATUS(0xC0000000 | 0x004a)
+#define NT_STATUS_THREAD_IS_TERMINATING NT_STATUS(0xC0000000 | 0x004b)
+#define NT_STATUS_BAD_WORKING_SET_LIMIT NT_STATUS(0xC0000000 | 0x004c)
+#define NT_STATUS_INCOMPATIBLE_FILE_MAP NT_STATUS(0xC0000000 | 0x004d)
+#define NT_STATUS_SECTION_PROTECTION NT_STATUS(0xC0000000 | 0x004e)
+#define NT_STATUS_EAS_NOT_SUPPORTED NT_STATUS(0xC0000000 | 0x004f)
+#define NT_STATUS_EA_TOO_LARGE NT_STATUS(0xC0000000 | 0x0050)
+#define NT_STATUS_NONEXISTENT_EA_ENTRY NT_STATUS(0xC0000000 | 0x0051)
+#define NT_STATUS_NO_EAS_ON_FILE NT_STATUS(0xC0000000 | 0x0052)
+#define NT_STATUS_EA_CORRUPT_ERROR NT_STATUS(0xC0000000 | 0x0053)
+#define NT_STATUS_FILE_LOCK_CONFLICT NT_STATUS(0xC0000000 | 0x0054)
+#define NT_STATUS_LOCK_NOT_GRANTED NT_STATUS(0xC0000000 | 0x0055)
+#define NT_STATUS_DELETE_PENDING NT_STATUS(0xC0000000 | 0x0056)
+#define NT_STATUS_CTL_FILE_NOT_SUPPORTED NT_STATUS(0xC0000000 | 0x0057)
+#define NT_STATUS_UNKNOWN_REVISION NT_STATUS(0xC0000000 | 0x0058)
+#define NT_STATUS_REVISION_MISMATCH NT_STATUS(0xC0000000 | 0x0059)
+#define NT_STATUS_INVALID_OWNER NT_STATUS(0xC0000000 | 0x005a)
+#define NT_STATUS_INVALID_PRIMARY_GROUP NT_STATUS(0xC0000000 | 0x005b)
+#define NT_STATUS_NO_IMPERSONATION_TOKEN NT_STATUS(0xC0000000 | 0x005c)
+#define NT_STATUS_CANT_DISABLE_MANDATORY NT_STATUS(0xC0000000 | 0x005d)
+#define NT_STATUS_NO_LOGON_SERVERS NT_STATUS(0xC0000000 | 0x005e)
+#define NT_STATUS_NO_SUCH_LOGON_SESSION NT_STATUS(0xC0000000 | 0x005f)
+#define NT_STATUS_NO_SUCH_PRIVILEGE NT_STATUS(0xC0000000 | 0x0060)
+#define NT_STATUS_PRIVILEGE_NOT_HELD NT_STATUS(0xC0000000 | 0x0061)
+#define NT_STATUS_INVALID_ACCOUNT_NAME NT_STATUS(0xC0000000 | 0x0062)
+#define NT_STATUS_USER_EXISTS NT_STATUS(0xC0000000 | 0x0063)
+#define NT_STATUS_NO_SUCH_USER NT_STATUS(0xC0000000 | 0x0064)
+#define NT_STATUS_GROUP_EXISTS NT_STATUS(0xC0000000 | 0x0065)
+#define NT_STATUS_NO_SUCH_GROUP NT_STATUS(0xC0000000 | 0x0066)
+#define NT_STATUS_MEMBER_IN_GROUP NT_STATUS(0xC0000000 | 0x0067)
+#define NT_STATUS_MEMBER_NOT_IN_GROUP NT_STATUS(0xC0000000 | 0x0068)
+#define NT_STATUS_LAST_ADMIN NT_STATUS(0xC0000000 | 0x0069)
+#define NT_STATUS_WRONG_PASSWORD NT_STATUS(0xC0000000 | 0x006a)
+#define NT_STATUS_ILL_FORMED_PASSWORD NT_STATUS(0xC0000000 | 0x006b)
+#define NT_STATUS_PASSWORD_RESTRICTION NT_STATUS(0xC0000000 | 0x006c)
+#define NT_STATUS_LOGON_FAILURE NT_STATUS(0xC0000000 | 0x006d)
+#define NT_STATUS_ACCOUNT_RESTRICTION NT_STATUS(0xC0000000 | 0x006e)
+#define NT_STATUS_INVALID_LOGON_HOURS NT_STATUS(0xC0000000 | 0x006f)
+#define NT_STATUS_INVALID_WORKSTATION NT_STATUS(0xC0000000 | 0x0070)
+#define NT_STATUS_PASSWORD_EXPIRED NT_STATUS(0xC0000000 | 0x0071)
+#define NT_STATUS_ACCOUNT_DISABLED NT_STATUS(0xC0000000 | 0x0072)
+#define NT_STATUS_NONE_MAPPED NT_STATUS(0xC0000000 | 0x0073)
+#define NT_STATUS_TOO_MANY_LUIDS_REQUESTED NT_STATUS(0xC0000000 | 0x0074)
+#define NT_STATUS_LUIDS_EXHAUSTED NT_STATUS(0xC0000000 | 0x0075)
+#define NT_STATUS_INVALID_SUB_AUTHORITY NT_STATUS(0xC0000000 | 0x0076)
+#define NT_STATUS_INVALID_ACL NT_STATUS(0xC0000000 | 0x0077)
+#define NT_STATUS_INVALID_SID NT_STATUS(0xC0000000 | 0x0078)
+#define NT_STATUS_INVALID_SECURITY_DESCR NT_STATUS(0xC0000000 | 0x0079)
+#define NT_STATUS_PROCEDURE_NOT_FOUND NT_STATUS(0xC0000000 | 0x007a)
+#define NT_STATUS_INVALID_IMAGE_FORMAT NT_STATUS(0xC0000000 | 0x007b)
+#define NT_STATUS_NO_TOKEN NT_STATUS(0xC0000000 | 0x007c)
+#define NT_STATUS_BAD_INHERITANCE_ACL NT_STATUS(0xC0000000 | 0x007d)
+#define NT_STATUS_RANGE_NOT_LOCKED NT_STATUS(0xC0000000 | 0x007e)
+#define NT_STATUS_DISK_FULL NT_STATUS(0xC0000000 | 0x007f)
+#define NT_STATUS_SERVER_DISABLED NT_STATUS(0xC0000000 | 0x0080)
+#define NT_STATUS_SERVER_NOT_DISABLED NT_STATUS(0xC0000000 | 0x0081)
+#define NT_STATUS_TOO_MANY_GUIDS_REQUESTED NT_STATUS(0xC0000000 | 0x0082)
+#define NT_STATUS_GUIDS_EXHAUSTED NT_STATUS(0xC0000000 | 0x0083)
+#define NT_STATUS_INVALID_ID_AUTHORITY NT_STATUS(0xC0000000 | 0x0084)
+#define NT_STATUS_AGENTS_EXHAUSTED NT_STATUS(0xC0000000 | 0x0085)
+#define NT_STATUS_INVALID_VOLUME_LABEL NT_STATUS(0xC0000000 | 0x0086)
+#define NT_STATUS_SECTION_NOT_EXTENDED NT_STATUS(0xC0000000 | 0x0087)
+#define NT_STATUS_NOT_MAPPED_DATA NT_STATUS(0xC0000000 | 0x0088)
+#define NT_STATUS_RESOURCE_DATA_NOT_FOUND NT_STATUS(0xC0000000 | 0x0089)
+#define NT_STATUS_RESOURCE_TYPE_NOT_FOUND NT_STATUS(0xC0000000 | 0x008a)
+#define NT_STATUS_RESOURCE_NAME_NOT_FOUND NT_STATUS(0xC0000000 | 0x008b)
+#define NT_STATUS_ARRAY_BOUNDS_EXCEEDED NT_STATUS(0xC0000000 | 0x008c)
+#define NT_STATUS_FLOAT_DENORMAL_OPERAND NT_STATUS(0xC0000000 | 0x008d)
+#define NT_STATUS_FLOAT_DIVIDE_BY_ZERO NT_STATUS(0xC0000000 | 0x008e)
+#define NT_STATUS_FLOAT_INEXACT_RESULT NT_STATUS(0xC0000000 | 0x008f)
+#define NT_STATUS_FLOAT_INVALID_OPERATION NT_STATUS(0xC0000000 | 0x0090)
+#define NT_STATUS_FLOAT_OVERFLOW NT_STATUS(0xC0000000 | 0x0091)
+#define NT_STATUS_FLOAT_STACK_CHECK NT_STATUS(0xC0000000 | 0x0092)
+#define NT_STATUS_FLOAT_UNDERFLOW NT_STATUS(0xC0000000 | 0x0093)
+#define NT_STATUS_INTEGER_DIVIDE_BY_ZERO NT_STATUS(0xC0000000 | 0x0094)
+#define NT_STATUS_INTEGER_OVERFLOW NT_STATUS(0xC0000000 | 0x0095)
+#define NT_STATUS_PRIVILEGED_INSTRUCTION NT_STATUS(0xC0000000 | 0x0096)
+#define NT_STATUS_TOO_MANY_PAGING_FILES NT_STATUS(0xC0000000 | 0x0097)
+#define NT_STATUS_FILE_INVALID NT_STATUS(0xC0000000 | 0x0098)
+#define NT_STATUS_ALLOTTED_SPACE_EXCEEDED NT_STATUS(0xC0000000 | 0x0099)
+#define NT_STATUS_INSUFFICIENT_RESOURCES NT_STATUS(0xC0000000 | 0x009a)
+#define NT_STATUS_DFS_EXIT_PATH_FOUND NT_STATUS(0xC0000000 | 0x009b)
+#define NT_STATUS_DEVICE_DATA_ERROR NT_STATUS(0xC0000000 | 0x009c)
+#define NT_STATUS_DEVICE_NOT_CONNECTED NT_STATUS(0xC0000000 | 0x009d)
+#define NT_STATUS_DEVICE_POWER_FAILURE NT_STATUS(0xC0000000 | 0x009e)
+#define NT_STATUS_FREE_VM_NOT_AT_BASE NT_STATUS(0xC0000000 | 0x009f)
+#define NT_STATUS_MEMORY_NOT_ALLOCATED NT_STATUS(0xC0000000 | 0x00a0)
+#define NT_STATUS_WORKING_SET_QUOTA NT_STATUS(0xC0000000 | 0x00a1)
+#define NT_STATUS_MEDIA_WRITE_PROTECTED NT_STATUS(0xC0000000 | 0x00a2)
+#define NT_STATUS_DEVICE_NOT_READY NT_STATUS(0xC0000000 | 0x00a3)
+#define NT_STATUS_INVALID_GROUP_ATTRIBUTES NT_STATUS(0xC0000000 | 0x00a4)
+#define NT_STATUS_BAD_IMPERSONATION_LEVEL NT_STATUS(0xC0000000 | 0x00a5)
+#define NT_STATUS_CANT_OPEN_ANONYMOUS NT_STATUS(0xC0000000 | 0x00a6)
+#define NT_STATUS_BAD_VALIDATION_CLASS NT_STATUS(0xC0000000 | 0x00a7)
+#define NT_STATUS_BAD_TOKEN_TYPE NT_STATUS(0xC0000000 | 0x00a8)
+#define NT_STATUS_BAD_MASTER_BOOT_RECORD NT_STATUS(0xC0000000 | 0x00a9)
+#define NT_STATUS_INSTRUCTION_MISALIGNMENT NT_STATUS(0xC0000000 | 0x00aa)
+#define NT_STATUS_INSTANCE_NOT_AVAILABLE NT_STATUS(0xC0000000 | 0x00ab)
+#define NT_STATUS_PIPE_NOT_AVAILABLE NT_STATUS(0xC0000000 | 0x00ac)
+#define NT_STATUS_INVALID_PIPE_STATE NT_STATUS(0xC0000000 | 0x00ad)
+#define NT_STATUS_PIPE_BUSY NT_STATUS(0xC0000000 | 0x00ae)
+#define NT_STATUS_ILLEGAL_FUNCTION NT_STATUS(0xC0000000 | 0x00af)
+#define NT_STATUS_PIPE_DISCONNECTED NT_STATUS(0xC0000000 | 0x00b0)
+#define NT_STATUS_PIPE_CLOSING NT_STATUS(0xC0000000 | 0x00b1)
+#define NT_STATUS_PIPE_CONNECTED NT_STATUS(0xC0000000 | 0x00b2)
+#define NT_STATUS_PIPE_LISTENING NT_STATUS(0xC0000000 | 0x00b3)
+#define NT_STATUS_INVALID_READ_MODE NT_STATUS(0xC0000000 | 0x00b4)
+#define NT_STATUS_IO_TIMEOUT NT_STATUS(0xC0000000 | 0x00b5)
+#define NT_STATUS_FILE_FORCED_CLOSED NT_STATUS(0xC0000000 | 0x00b6)
+#define NT_STATUS_PROFILING_NOT_STARTED NT_STATUS(0xC0000000 | 0x00b7)
+#define NT_STATUS_PROFILING_NOT_STOPPED NT_STATUS(0xC0000000 | 0x00b8)
+#define NT_STATUS_COULD_NOT_INTERPRET NT_STATUS(0xC0000000 | 0x00b9)
+#define NT_STATUS_FILE_IS_A_DIRECTORY NT_STATUS(0xC0000000 | 0x00ba)
+#define NT_STATUS_NOT_SUPPORTED NT_STATUS(0xC0000000 | 0x00bb)
+#define NT_STATUS_REMOTE_NOT_LISTENING NT_STATUS(0xC0000000 | 0x00bc)
+#define NT_STATUS_DUPLICATE_NAME NT_STATUS(0xC0000000 | 0x00bd)
+#define NT_STATUS_BAD_NETWORK_PATH NT_STATUS(0xC0000000 | 0x00be)
+#define NT_STATUS_NETWORK_BUSY NT_STATUS(0xC0000000 | 0x00bf)
+#define NT_STATUS_DEVICE_DOES_NOT_EXIST NT_STATUS(0xC0000000 | 0x00c0)
+#define NT_STATUS_TOO_MANY_COMMANDS NT_STATUS(0xC0000000 | 0x00c1)
+#define NT_STATUS_ADAPTER_HARDWARE_ERROR NT_STATUS(0xC0000000 | 0x00c2)
+#define NT_STATUS_INVALID_NETWORK_RESPONSE NT_STATUS(0xC0000000 | 0x00c3)
+#define NT_STATUS_UNEXPECTED_NETWORK_ERROR NT_STATUS(0xC0000000 | 0x00c4)
+#define NT_STATUS_BAD_REMOTE_ADAPTER NT_STATUS(0xC0000000 | 0x00c5)
+#define NT_STATUS_PRINT_QUEUE_FULL NT_STATUS(0xC0000000 | 0x00c6)
+#define NT_STATUS_NO_SPOOL_SPACE NT_STATUS(0xC0000000 | 0x00c7)
+#define NT_STATUS_PRINT_CANCELLED NT_STATUS(0xC0000000 | 0x00c8)
+#define NT_STATUS_NETWORK_NAME_DELETED NT_STATUS(0xC0000000 | 0x00c9)
+#define NT_STATUS_NETWORK_ACCESS_DENIED NT_STATUS(0xC0000000 | 0x00ca)
+#define NT_STATUS_BAD_DEVICE_TYPE NT_STATUS(0xC0000000 | 0x00cb)
+#define NT_STATUS_BAD_NETWORK_NAME NT_STATUS(0xC0000000 | 0x00cc)
+#define NT_STATUS_TOO_MANY_NAMES NT_STATUS(0xC0000000 | 0x00cd)
+#define NT_STATUS_TOO_MANY_SESSIONS NT_STATUS(0xC0000000 | 0x00ce)
+#define NT_STATUS_SHARING_PAUSED NT_STATUS(0xC0000000 | 0x00cf)
+#define NT_STATUS_REQUEST_NOT_ACCEPTED NT_STATUS(0xC0000000 | 0x00d0)
+#define NT_STATUS_REDIRECTOR_PAUSED NT_STATUS(0xC0000000 | 0x00d1)
+#define NT_STATUS_NET_WRITE_FAULT NT_STATUS(0xC0000000 | 0x00d2)
+#define NT_STATUS_PROFILING_AT_LIMIT NT_STATUS(0xC0000000 | 0x00d3)
+#define NT_STATUS_NOT_SAME_DEVICE NT_STATUS(0xC0000000 | 0x00d4)
+#define NT_STATUS_FILE_RENAMED NT_STATUS(0xC0000000 | 0x00d5)
+#define NT_STATUS_VIRTUAL_CIRCUIT_CLOSED NT_STATUS(0xC0000000 | 0x00d6)
+#define NT_STATUS_NO_SECURITY_ON_OBJECT NT_STATUS(0xC0000000 | 0x00d7)
+#define NT_STATUS_CANT_WAIT NT_STATUS(0xC0000000 | 0x00d8)
+#define NT_STATUS_PIPE_EMPTY NT_STATUS(0xC0000000 | 0x00d9)
+#define NT_STATUS_CANT_ACCESS_DOMAIN_INFO NT_STATUS(0xC0000000 | 0x00da)
+#define NT_STATUS_CANT_TERMINATE_SELF NT_STATUS(0xC0000000 | 0x00db)
+#define NT_STATUS_INVALID_SERVER_STATE NT_STATUS(0xC0000000 | 0x00dc)
+#define NT_STATUS_INVALID_DOMAIN_STATE NT_STATUS(0xC0000000 | 0x00dd)
+#define NT_STATUS_INVALID_DOMAIN_ROLE NT_STATUS(0xC0000000 | 0x00de)
+#define NT_STATUS_NO_SUCH_DOMAIN NT_STATUS(0xC0000000 | 0x00df)
+#define NT_STATUS_DOMAIN_EXISTS NT_STATUS(0xC0000000 | 0x00e0)
+#define NT_STATUS_DOMAIN_LIMIT_EXCEEDED NT_STATUS(0xC0000000 | 0x00e1)
+#define NT_STATUS_OPLOCK_NOT_GRANTED NT_STATUS(0xC0000000 | 0x00e2)
+#define NT_STATUS_INVALID_OPLOCK_PROTOCOL NT_STATUS(0xC0000000 | 0x00e3)
+#define NT_STATUS_INTERNAL_DB_CORRUPTION NT_STATUS(0xC0000000 | 0x00e4)
+#define NT_STATUS_INTERNAL_ERROR NT_STATUS(0xC0000000 | 0x00e5)
+#define NT_STATUS_GENERIC_NOT_MAPPED NT_STATUS(0xC0000000 | 0x00e6)
+#define NT_STATUS_BAD_DESCRIPTOR_FORMAT NT_STATUS(0xC0000000 | 0x00e7)
+#define NT_STATUS_INVALID_USER_BUFFER NT_STATUS(0xC0000000 | 0x00e8)
+#define NT_STATUS_UNEXPECTED_IO_ERROR NT_STATUS(0xC0000000 | 0x00e9)
+#define NT_STATUS_UNEXPECTED_MM_CREATE_ERR NT_STATUS(0xC0000000 | 0x00ea)
+#define NT_STATUS_UNEXPECTED_MM_MAP_ERROR NT_STATUS(0xC0000000 | 0x00eb)
+#define NT_STATUS_UNEXPECTED_MM_EXTEND_ERR NT_STATUS(0xC0000000 | 0x00ec)
+#define NT_STATUS_NOT_LOGON_PROCESS NT_STATUS(0xC0000000 | 0x00ed)
+#define NT_STATUS_LOGON_SESSION_EXISTS NT_STATUS(0xC0000000 | 0x00ee)
+#define NT_STATUS_INVALID_PARAMETER_1 NT_STATUS(0xC0000000 | 0x00ef)
+#define NT_STATUS_INVALID_PARAMETER_2 NT_STATUS(0xC0000000 | 0x00f0)
+#define NT_STATUS_INVALID_PARAMETER_3 NT_STATUS(0xC0000000 | 0x00f1)
+#define NT_STATUS_INVALID_PARAMETER_4 NT_STATUS(0xC0000000 | 0x00f2)
+#define NT_STATUS_INVALID_PARAMETER_5 NT_STATUS(0xC0000000 | 0x00f3)
+#define NT_STATUS_INVALID_PARAMETER_6 NT_STATUS(0xC0000000 | 0x00f4)
+#define NT_STATUS_INVALID_PARAMETER_7 NT_STATUS(0xC0000000 | 0x00f5)
+#define NT_STATUS_INVALID_PARAMETER_8 NT_STATUS(0xC0000000 | 0x00f6)
+#define NT_STATUS_INVALID_PARAMETER_9 NT_STATUS(0xC0000000 | 0x00f7)
+#define NT_STATUS_INVALID_PARAMETER_10 NT_STATUS(0xC0000000 | 0x00f8)
+#define NT_STATUS_INVALID_PARAMETER_11 NT_STATUS(0xC0000000 | 0x00f9)
+#define NT_STATUS_INVALID_PARAMETER_12 NT_STATUS(0xC0000000 | 0x00fa)
+#define NT_STATUS_REDIRECTOR_NOT_STARTED NT_STATUS(0xC0000000 | 0x00fb)
+#define NT_STATUS_REDIRECTOR_STARTED NT_STATUS(0xC0000000 | 0x00fc)
+#define NT_STATUS_STACK_OVERFLOW NT_STATUS(0xC0000000 | 0x00fd)
+#define NT_STATUS_NO_SUCH_PACKAGE NT_STATUS(0xC0000000 | 0x00fe)
+#define NT_STATUS_BAD_FUNCTION_TABLE NT_STATUS(0xC0000000 | 0x00ff)
+#define NT_STATUS_DIRECTORY_NOT_EMPTY NT_STATUS(0xC0000000 | 0x0101)
+#define NT_STATUS_FILE_CORRUPT_ERROR NT_STATUS(0xC0000000 | 0x0102)
+#define NT_STATUS_NOT_A_DIRECTORY NT_STATUS(0xC0000000 | 0x0103)
+#define NT_STATUS_BAD_LOGON_SESSION_STATE NT_STATUS(0xC0000000 | 0x0104)
+#define NT_STATUS_LOGON_SESSION_COLLISION NT_STATUS(0xC0000000 | 0x0105)
+#define NT_STATUS_NAME_TOO_LONG NT_STATUS(0xC0000000 | 0x0106)
+#define NT_STATUS_FILES_OPEN NT_STATUS(0xC0000000 | 0x0107)
+#define NT_STATUS_CONNECTION_IN_USE NT_STATUS(0xC0000000 | 0x0108)
+#define NT_STATUS_MESSAGE_NOT_FOUND NT_STATUS(0xC0000000 | 0x0109)
+#define NT_STATUS_PROCESS_IS_TERMINATING NT_STATUS(0xC0000000 | 0x010a)
+#define NT_STATUS_INVALID_LOGON_TYPE NT_STATUS(0xC0000000 | 0x010b)
+#define NT_STATUS_NO_GUID_TRANSLATION NT_STATUS(0xC0000000 | 0x010c)
+#define NT_STATUS_CANNOT_IMPERSONATE NT_STATUS(0xC0000000 | 0x010d)
+#define NT_STATUS_IMAGE_ALREADY_LOADED NT_STATUS(0xC0000000 | 0x010e)
+#define NT_STATUS_ABIOS_NOT_PRESENT NT_STATUS(0xC0000000 | 0x010f)
+#define NT_STATUS_ABIOS_LID_NOT_EXIST NT_STATUS(0xC0000000 | 0x0110)
+#define NT_STATUS_ABIOS_LID_ALREADY_OWNED NT_STATUS(0xC0000000 | 0x0111)
+#define NT_STATUS_ABIOS_NOT_LID_OWNER NT_STATUS(0xC0000000 | 0x0112)
+#define NT_STATUS_ABIOS_INVALID_COMMAND NT_STATUS(0xC0000000 | 0x0113)
+#define NT_STATUS_ABIOS_INVALID_LID NT_STATUS(0xC0000000 | 0x0114)
+#define NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE NT_STATUS(0xC0000000 | 0x0115)
+#define NT_STATUS_ABIOS_INVALID_SELECTOR NT_STATUS(0xC0000000 | 0x0116)
+#define NT_STATUS_NO_LDT NT_STATUS(0xC0000000 | 0x0117)
+#define NT_STATUS_INVALID_LDT_SIZE NT_STATUS(0xC0000000 | 0x0118)
+#define NT_STATUS_INVALID_LDT_OFFSET NT_STATUS(0xC0000000 | 0x0119)
+#define NT_STATUS_INVALID_LDT_DESCRIPTOR NT_STATUS(0xC0000000 | 0x011a)
+#define NT_STATUS_INVALID_IMAGE_NE_FORMAT NT_STATUS(0xC0000000 | 0x011b)
+#define NT_STATUS_RXACT_INVALID_STATE NT_STATUS(0xC0000000 | 0x011c)
+#define NT_STATUS_RXACT_COMMIT_FAILURE NT_STATUS(0xC0000000 | 0x011d)
+#define NT_STATUS_MAPPED_FILE_SIZE_ZERO NT_STATUS(0xC0000000 | 0x011e)
+#define NT_STATUS_TOO_MANY_OPENED_FILES NT_STATUS(0xC0000000 | 0x011f)
+#define NT_STATUS_CANCELLED NT_STATUS(0xC0000000 | 0x0120)
+#define NT_STATUS_CANNOT_DELETE NT_STATUS(0xC0000000 | 0x0121)
+#define NT_STATUS_INVALID_COMPUTER_NAME NT_STATUS(0xC0000000 | 0x0122)
+#define NT_STATUS_FILE_DELETED NT_STATUS(0xC0000000 | 0x0123)
+#define NT_STATUS_SPECIAL_ACCOUNT NT_STATUS(0xC0000000 | 0x0124)
+#define NT_STATUS_SPECIAL_GROUP NT_STATUS(0xC0000000 | 0x0125)
+#define NT_STATUS_SPECIAL_USER NT_STATUS(0xC0000000 | 0x0126)
+#define NT_STATUS_MEMBERS_PRIMARY_GROUP NT_STATUS(0xC0000000 | 0x0127)
+#define NT_STATUS_FILE_CLOSED NT_STATUS(0xC0000000 | 0x0128)
+#define NT_STATUS_TOO_MANY_THREADS NT_STATUS(0xC0000000 | 0x0129)
+#define NT_STATUS_THREAD_NOT_IN_PROCESS NT_STATUS(0xC0000000 | 0x012a)
+#define NT_STATUS_TOKEN_ALREADY_IN_USE NT_STATUS(0xC0000000 | 0x012b)
+#define NT_STATUS_PAGEFILE_QUOTA_EXCEEDED NT_STATUS(0xC0000000 | 0x012c)
+#define NT_STATUS_COMMITMENT_LIMIT NT_STATUS(0xC0000000 | 0x012d)
+#define NT_STATUS_INVALID_IMAGE_LE_FORMAT NT_STATUS(0xC0000000 | 0x012e)
+#define NT_STATUS_INVALID_IMAGE_NOT_MZ NT_STATUS(0xC0000000 | 0x012f)
+#define NT_STATUS_INVALID_IMAGE_PROTECT NT_STATUS(0xC0000000 | 0x0130)
+#define NT_STATUS_INVALID_IMAGE_WIN_16 NT_STATUS(0xC0000000 | 0x0131)
+#define NT_STATUS_LOGON_SERVER_CONFLICT NT_STATUS(0xC0000000 | 0x0132)
+#define NT_STATUS_TIME_DIFFERENCE_AT_DC NT_STATUS(0xC0000000 | 0x0133)
+#define NT_STATUS_SYNCHRONIZATION_REQUIRED NT_STATUS(0xC0000000 | 0x0134)
+#define NT_STATUS_DLL_NOT_FOUND NT_STATUS(0xC0000000 | 0x0135)
+#define NT_STATUS_OPEN_FAILED NT_STATUS(0xC0000000 | 0x0136)
+#define NT_STATUS_IO_PRIVILEGE_FAILED NT_STATUS(0xC0000000 | 0x0137)
+#define NT_STATUS_ORDINAL_NOT_FOUND NT_STATUS(0xC0000000 | 0x0138)
+#define NT_STATUS_ENTRYPOINT_NOT_FOUND NT_STATUS(0xC0000000 | 0x0139)
+#define NT_STATUS_CONTROL_C_EXIT NT_STATUS(0xC0000000 | 0x013a)
+#define NT_STATUS_LOCAL_DISCONNECT NT_STATUS(0xC0000000 | 0x013b)
+#define NT_STATUS_REMOTE_DISCONNECT NT_STATUS(0xC0000000 | 0x013c)
+#define NT_STATUS_REMOTE_RESOURCES NT_STATUS(0xC0000000 | 0x013d)
+#define NT_STATUS_LINK_FAILED NT_STATUS(0xC0000000 | 0x013e)
+#define NT_STATUS_LINK_TIMEOUT NT_STATUS(0xC0000000 | 0x013f)
+#define NT_STATUS_INVALID_CONNECTION NT_STATUS(0xC0000000 | 0x0140)
+#define NT_STATUS_INVALID_ADDRESS NT_STATUS(0xC0000000 | 0x0141)
+#define NT_STATUS_DLL_INIT_FAILED NT_STATUS(0xC0000000 | 0x0142)
+#define NT_STATUS_MISSING_SYSTEMFILE NT_STATUS(0xC0000000 | 0x0143)
+#define NT_STATUS_UNHANDLED_EXCEPTION NT_STATUS(0xC0000000 | 0x0144)
+#define NT_STATUS_APP_INIT_FAILURE NT_STATUS(0xC0000000 | 0x0145)
+#define NT_STATUS_PAGEFILE_CREATE_FAILED NT_STATUS(0xC0000000 | 0x0146)
+#define NT_STATUS_NO_PAGEFILE NT_STATUS(0xC0000000 | 0x0147)
+#define NT_STATUS_INVALID_LEVEL NT_STATUS(0xC0000000 | 0x0148)
+#define NT_STATUS_WRONG_PASSWORD_CORE NT_STATUS(0xC0000000 | 0x0149)
+#define NT_STATUS_ILLEGAL_FLOAT_CONTEXT NT_STATUS(0xC0000000 | 0x014a)
+#define NT_STATUS_PIPE_BROKEN NT_STATUS(0xC0000000 | 0x014b)
+#define NT_STATUS_REGISTRY_CORRUPT NT_STATUS(0xC0000000 | 0x014c)
+#define NT_STATUS_REGISTRY_IO_FAILED NT_STATUS(0xC0000000 | 0x014d)
+#define NT_STATUS_NO_EVENT_PAIR NT_STATUS(0xC0000000 | 0x014e)
+#define NT_STATUS_UNRECOGNIZED_VOLUME NT_STATUS(0xC0000000 | 0x014f)
+#define NT_STATUS_SERIAL_NO_DEVICE_INITED NT_STATUS(0xC0000000 | 0x0150)
+#define NT_STATUS_NO_SUCH_ALIAS NT_STATUS(0xC0000000 | 0x0151)
+#define NT_STATUS_MEMBER_NOT_IN_ALIAS NT_STATUS(0xC0000000 | 0x0152)
+#define NT_STATUS_MEMBER_IN_ALIAS NT_STATUS(0xC0000000 | 0x0153)
+#define NT_STATUS_ALIAS_EXISTS NT_STATUS(0xC0000000 | 0x0154)
+#define NT_STATUS_LOGON_NOT_GRANTED NT_STATUS(0xC0000000 | 0x0155)
+#define NT_STATUS_TOO_MANY_SECRETS NT_STATUS(0xC0000000 | 0x0156)
+#define NT_STATUS_SECRET_TOO_LONG NT_STATUS(0xC0000000 | 0x0157)
+#define NT_STATUS_INTERNAL_DB_ERROR NT_STATUS(0xC0000000 | 0x0158)
+#define NT_STATUS_FULLSCREEN_MODE NT_STATUS(0xC0000000 | 0x0159)
+#define NT_STATUS_TOO_MANY_CONTEXT_IDS NT_STATUS(0xC0000000 | 0x015a)
+#define NT_STATUS_LOGON_TYPE_NOT_GRANTED NT_STATUS(0xC0000000 | 0x015b)
+#define NT_STATUS_NOT_REGISTRY_FILE NT_STATUS(0xC0000000 | 0x015c)
+#define NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED NT_STATUS(0xC0000000 | 0x015d)
+#define NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR NT_STATUS(0xC0000000 | 0x015e)
+#define NT_STATUS_FT_MISSING_MEMBER NT_STATUS(0xC0000000 | 0x015f)
+#define NT_STATUS_ILL_FORMED_SERVICE_ENTRY NT_STATUS(0xC0000000 | 0x0160)
+#define NT_STATUS_ILLEGAL_CHARACTER NT_STATUS(0xC0000000 | 0x0161)
+#define NT_STATUS_UNMAPPABLE_CHARACTER NT_STATUS(0xC0000000 | 0x0162)
+#define NT_STATUS_UNDEFINED_CHARACTER NT_STATUS(0xC0000000 | 0x0163)
+#define NT_STATUS_FLOPPY_VOLUME NT_STATUS(0xC0000000 | 0x0164)
+#define NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND NT_STATUS(0xC0000000 | 0x0165)
+#define NT_STATUS_FLOPPY_WRONG_CYLINDER NT_STATUS(0xC0000000 | 0x0166)
+#define NT_STATUS_FLOPPY_UNKNOWN_ERROR NT_STATUS(0xC0000000 | 0x0167)
+#define NT_STATUS_FLOPPY_BAD_REGISTERS NT_STATUS(0xC0000000 | 0x0168)
+#define NT_STATUS_DISK_RECALIBRATE_FAILED NT_STATUS(0xC0000000 | 0x0169)
+#define NT_STATUS_DISK_OPERATION_FAILED NT_STATUS(0xC0000000 | 0x016a)
+#define NT_STATUS_DISK_RESET_FAILED NT_STATUS(0xC0000000 | 0x016b)
+#define NT_STATUS_SHARED_IRQ_BUSY NT_STATUS(0xC0000000 | 0x016c)
+#define NT_STATUS_FT_ORPHANING NT_STATUS(0xC0000000 | 0x016d)
+#define NT_STATUS_PARTITION_FAILURE NT_STATUS(0xC0000000 | 0x0172)
+#define NT_STATUS_INVALID_BLOCK_LENGTH NT_STATUS(0xC0000000 | 0x0173)
+#define NT_STATUS_DEVICE_NOT_PARTITIONED NT_STATUS(0xC0000000 | 0x0174)
+#define NT_STATUS_UNABLE_TO_LOCK_MEDIA NT_STATUS(0xC0000000 | 0x0175)
+#define NT_STATUS_UNABLE_TO_UNLOAD_MEDIA NT_STATUS(0xC0000000 | 0x0176)
+#define NT_STATUS_EOM_OVERFLOW NT_STATUS(0xC0000000 | 0x0177)
+#define NT_STATUS_NO_MEDIA NT_STATUS(0xC0000000 | 0x0178)
+#define NT_STATUS_NO_SUCH_MEMBER NT_STATUS(0xC0000000 | 0x017a)
+#define NT_STATUS_INVALID_MEMBER NT_STATUS(0xC0000000 | 0x017b)
+#define NT_STATUS_KEY_DELETED NT_STATUS(0xC0000000 | 0x017c)
+#define NT_STATUS_NO_LOG_SPACE NT_STATUS(0xC0000000 | 0x017d)
+#define NT_STATUS_TOO_MANY_SIDS NT_STATUS(0xC0000000 | 0x017e)
+#define NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED NT_STATUS(0xC0000000 | 0x017f)
+#define NT_STATUS_KEY_HAS_CHILDREN NT_STATUS(0xC0000000 | 0x0180)
+#define NT_STATUS_CHILD_MUST_BE_VOLATILE NT_STATUS(0xC0000000 | 0x0181)
+#define NT_STATUS_DEVICE_CONFIGURATION_ERROR NT_STATUS(0xC0000000 | 0x0182)
+#define NT_STATUS_DRIVER_INTERNAL_ERROR NT_STATUS(0xC0000000 | 0x0183)
+#define NT_STATUS_INVALID_DEVICE_STATE NT_STATUS(0xC0000000 | 0x0184)
+#define NT_STATUS_IO_DEVICE_ERROR NT_STATUS(0xC0000000 | 0x0185)
+#define NT_STATUS_DEVICE_PROTOCOL_ERROR NT_STATUS(0xC0000000 | 0x0186)
+#define NT_STATUS_BACKUP_CONTROLLER NT_STATUS(0xC0000000 | 0x0187)
+#define NT_STATUS_LOG_FILE_FULL NT_STATUS(0xC0000000 | 0x0188)
+#define NT_STATUS_TOO_LATE NT_STATUS(0xC0000000 | 0x0189)
+#define NT_STATUS_NO_TRUST_LSA_SECRET NT_STATUS(0xC0000000 | 0x018a)
+#define NT_STATUS_NO_TRUST_SAM_ACCOUNT NT_STATUS(0xC0000000 | 0x018b)
+#define NT_STATUS_TRUSTED_DOMAIN_FAILURE NT_STATUS(0xC0000000 | 0x018c)
+#define NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE NT_STATUS(0xC0000000 | 0x018d)
+#define NT_STATUS_EVENTLOG_FILE_CORRUPT NT_STATUS(0xC0000000 | 0x018e)
+#define NT_STATUS_EVENTLOG_CANT_START NT_STATUS(0xC0000000 | 0x018f)
+#define NT_STATUS_TRUST_FAILURE NT_STATUS(0xC0000000 | 0x0190)
+#define NT_STATUS_MUTANT_LIMIT_EXCEEDED NT_STATUS(0xC0000000 | 0x0191)
+#define NT_STATUS_NETLOGON_NOT_STARTED NT_STATUS(0xC0000000 | 0x0192)
+#define NT_STATUS_ACCOUNT_EXPIRED NT_STATUS(0xC0000000 | 0x0193)
+#define NT_STATUS_POSSIBLE_DEADLOCK NT_STATUS(0xC0000000 | 0x0194)
+#define NT_STATUS_NETWORK_CREDENTIAL_CONFLICT NT_STATUS(0xC0000000 | 0x0195)
+#define NT_STATUS_REMOTE_SESSION_LIMIT NT_STATUS(0xC0000000 | 0x0196)
+#define NT_STATUS_EVENTLOG_FILE_CHANGED NT_STATUS(0xC0000000 | 0x0197)
+#define NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT NT_STATUS(0xC0000000 | 0x0198)
+#define NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT NT_STATUS(0xC0000000 | 0x0199)
+#define NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT NT_STATUS(0xC0000000 | 0x019a)
+#define NT_STATUS_DOMAIN_TRUST_INCONSISTENT NT_STATUS(0xC0000000 | 0x019b)
+#define NT_STATUS_FS_DRIVER_REQUIRED NT_STATUS(0xC0000000 | 0x019c)
+#define NT_STATUS_NO_USER_SESSION_KEY NT_STATUS(0xC0000000 | 0x0202)
+#define NT_STATUS_USER_SESSION_DELETED NT_STATUS(0xC0000000 | 0x0203)
+#define NT_STATUS_RESOURCE_LANG_NOT_FOUND NT_STATUS(0xC0000000 | 0x0204)
+#define NT_STATUS_INSUFF_SERVER_RESOURCES NT_STATUS(0xC0000000 | 0x0205)
+#define NT_STATUS_INVALID_BUFFER_SIZE NT_STATUS(0xC0000000 | 0x0206)
+#define NT_STATUS_INVALID_ADDRESS_COMPONENT NT_STATUS(0xC0000000 | 0x0207)
+#define NT_STATUS_INVALID_ADDRESS_WILDCARD NT_STATUS(0xC0000000 | 0x0208)
+#define NT_STATUS_TOO_MANY_ADDRESSES NT_STATUS(0xC0000000 | 0x0209)
+#define NT_STATUS_ADDRESS_ALREADY_EXISTS NT_STATUS(0xC0000000 | 0x020a)
+#define NT_STATUS_ADDRESS_CLOSED NT_STATUS(0xC0000000 | 0x020b)
+#define NT_STATUS_CONNECTION_DISCONNECTED NT_STATUS(0xC0000000 | 0x020c)
+#define NT_STATUS_CONNECTION_RESET NT_STATUS(0xC0000000 | 0x020d)
+#define NT_STATUS_TOO_MANY_NODES NT_STATUS(0xC0000000 | 0x020e)
+#define NT_STATUS_TRANSACTION_ABORTED NT_STATUS(0xC0000000 | 0x020f)
+#define NT_STATUS_TRANSACTION_TIMED_OUT NT_STATUS(0xC0000000 | 0x0210)
+#define NT_STATUS_TRANSACTION_NO_RELEASE NT_STATUS(0xC0000000 | 0x0211)
+#define NT_STATUS_TRANSACTION_NO_MATCH NT_STATUS(0xC0000000 | 0x0212)
+#define NT_STATUS_TRANSACTION_RESPONDED NT_STATUS(0xC0000000 | 0x0213)
+#define NT_STATUS_TRANSACTION_INVALID_ID NT_STATUS(0xC0000000 | 0x0214)
+#define NT_STATUS_TRANSACTION_INVALID_TYPE NT_STATUS(0xC0000000 | 0x0215)
+#define NT_STATUS_NOT_SERVER_SESSION NT_STATUS(0xC0000000 | 0x0216)
+#define NT_STATUS_NOT_CLIENT_SESSION NT_STATUS(0xC0000000 | 0x0217)
+#define NT_STATUS_CANNOT_LOAD_REGISTRY_FILE NT_STATUS(0xC0000000 | 0x0218)
+#define NT_STATUS_DEBUG_ATTACH_FAILED NT_STATUS(0xC0000000 | 0x0219)
+#define NT_STATUS_SYSTEM_PROCESS_TERMINATED NT_STATUS(0xC0000000 | 0x021a)
+#define NT_STATUS_DATA_NOT_ACCEPTED NT_STATUS(0xC0000000 | 0x021b)
+#define NT_STATUS_NO_BROWSER_SERVERS_FOUND NT_STATUS(0xC0000000 | 0x021c)
+#define NT_STATUS_VDM_HARD_ERROR NT_STATUS(0xC0000000 | 0x021d)
+#define NT_STATUS_DRIVER_CANCEL_TIMEOUT NT_STATUS(0xC0000000 | 0x021e)
+#define NT_STATUS_REPLY_MESSAGE_MISMATCH NT_STATUS(0xC0000000 | 0x021f)
+#define NT_STATUS_MAPPED_ALIGNMENT NT_STATUS(0xC0000000 | 0x0220)
+#define NT_STATUS_IMAGE_CHECKSUM_MISMATCH NT_STATUS(0xC0000000 | 0x0221)
+#define NT_STATUS_LOST_WRITEBEHIND_DATA NT_STATUS(0xC0000000 | 0x0222)
+#define NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID NT_STATUS(0xC0000000 | 0x0223)
+#define NT_STATUS_PASSWORD_MUST_CHANGE NT_STATUS(0xC0000000 | 0x0224)
+#define NT_STATUS_NOT_FOUND NT_STATUS(0xC0000000 | 0x0225)
+#define NT_STATUS_NOT_TINY_STREAM NT_STATUS(0xC0000000 | 0x0226)
+#define NT_STATUS_RECOVERY_FAILURE NT_STATUS(0xC0000000 | 0x0227)
+#define NT_STATUS_STACK_OVERFLOW_READ NT_STATUS(0xC0000000 | 0x0228)
+#define NT_STATUS_FAIL_CHECK NT_STATUS(0xC0000000 | 0x0229)
+#define NT_STATUS_DUPLICATE_OBJECTID NT_STATUS(0xC0000000 | 0x022a)
+#define NT_STATUS_OBJECTID_EXISTS NT_STATUS(0xC0000000 | 0x022b)
+#define NT_STATUS_CONVERT_TO_LARGE NT_STATUS(0xC0000000 | 0x022c)
+#define NT_STATUS_RETRY NT_STATUS(0xC0000000 | 0x022d)
+#define NT_STATUS_FOUND_OUT_OF_SCOPE NT_STATUS(0xC0000000 | 0x022e)
+#define NT_STATUS_ALLOCATE_BUCKET NT_STATUS(0xC0000000 | 0x022f)
+#define NT_STATUS_PROPSET_NOT_FOUND NT_STATUS(0xC0000000 | 0x0230)
+#define NT_STATUS_MARSHALL_OVERFLOW NT_STATUS(0xC0000000 | 0x0231)
+#define NT_STATUS_INVALID_VARIANT NT_STATUS(0xC0000000 | 0x0232)
+#define NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND NT_STATUS(0xC0000000 | 0x0233)
+#define NT_STATUS_ACCOUNT_LOCKED_OUT NT_STATUS(0xC0000000 | 0x0234)
+#define NT_STATUS_HANDLE_NOT_CLOSABLE NT_STATUS(0xC0000000 | 0x0235)
+#define NT_STATUS_CONNECTION_REFUSED NT_STATUS(0xC0000000 | 0x0236)
+#define NT_STATUS_GRACEFUL_DISCONNECT NT_STATUS(0xC0000000 | 0x0237)
+#define NT_STATUS_ADDRESS_ALREADY_ASSOCIATED NT_STATUS(0xC0000000 | 0x0238)
+#define NT_STATUS_ADDRESS_NOT_ASSOCIATED NT_STATUS(0xC0000000 | 0x0239)
+#define NT_STATUS_CONNECTION_INVALID NT_STATUS(0xC0000000 | 0x023a)
+#define NT_STATUS_CONNECTION_ACTIVE NT_STATUS(0xC0000000 | 0x023b)
+#define NT_STATUS_NETWORK_UNREACHABLE NT_STATUS(0xC0000000 | 0x023c)
+#define NT_STATUS_HOST_UNREACHABLE NT_STATUS(0xC0000000 | 0x023d)
+#define NT_STATUS_PROTOCOL_UNREACHABLE NT_STATUS(0xC0000000 | 0x023e)
+#define NT_STATUS_PORT_UNREACHABLE NT_STATUS(0xC0000000 | 0x023f)
+#define NT_STATUS_REQUEST_ABORTED NT_STATUS(0xC0000000 | 0x0240)
+#define NT_STATUS_CONNECTION_ABORTED NT_STATUS(0xC0000000 | 0x0241)
+#define NT_STATUS_BAD_COMPRESSION_BUFFER NT_STATUS(0xC0000000 | 0x0242)
+#define NT_STATUS_USER_MAPPED_FILE NT_STATUS(0xC0000000 | 0x0243)
+#define NT_STATUS_AUDIT_FAILED NT_STATUS(0xC0000000 | 0x0244)
+#define NT_STATUS_TIMER_RESOLUTION_NOT_SET NT_STATUS(0xC0000000 | 0x0245)
+#define NT_STATUS_CONNECTION_COUNT_LIMIT NT_STATUS(0xC0000000 | 0x0246)
+#define NT_STATUS_LOGIN_TIME_RESTRICTION NT_STATUS(0xC0000000 | 0x0247)
+#define NT_STATUS_LOGIN_WKSTA_RESTRICTION NT_STATUS(0xC0000000 | 0x0248)
+#define NT_STATUS_IMAGE_MP_UP_MISMATCH NT_STATUS(0xC0000000 | 0x0249)
+#define NT_STATUS_INSUFFICIENT_LOGON_INFO NT_STATUS(0xC0000000 | 0x0250)
+#define NT_STATUS_BAD_DLL_ENTRYPOINT NT_STATUS(0xC0000000 | 0x0251)
+#define NT_STATUS_BAD_SERVICE_ENTRYPOINT NT_STATUS(0xC0000000 | 0x0252)
+#define NT_STATUS_LPC_REPLY_LOST NT_STATUS(0xC0000000 | 0x0253)
+#define NT_STATUS_IP_ADDRESS_CONFLICT1 NT_STATUS(0xC0000000 | 0x0254)
+#define NT_STATUS_IP_ADDRESS_CONFLICT2 NT_STATUS(0xC0000000 | 0x0255)
+#define NT_STATUS_REGISTRY_QUOTA_LIMIT NT_STATUS(0xC0000000 | 0x0256)
+#define NT_STATUS_PATH_NOT_COVERED NT_STATUS(0xC0000000 | 0x0257)
+#define NT_STATUS_NO_CALLBACK_ACTIVE NT_STATUS(0xC0000000 | 0x0258)
+#define NT_STATUS_LICENSE_QUOTA_EXCEEDED NT_STATUS(0xC0000000 | 0x0259)
+#define NT_STATUS_PWD_TOO_SHORT NT_STATUS(0xC0000000 | 0x025a)
+#define NT_STATUS_PWD_TOO_RECENT NT_STATUS(0xC0000000 | 0x025b)
+#define NT_STATUS_PWD_HISTORY_CONFLICT NT_STATUS(0xC0000000 | 0x025c)
+#define NT_STATUS_PLUGPLAY_NO_DEVICE NT_STATUS(0xC0000000 | 0x025e)
+#define NT_STATUS_UNSUPPORTED_COMPRESSION NT_STATUS(0xC0000000 | 0x025f)
+#define NT_STATUS_INVALID_HW_PROFILE NT_STATUS(0xC0000000 | 0x0260)
+#define NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH NT_STATUS(0xC0000000 | 0x0261)
+#define NT_STATUS_DRIVER_ORDINAL_NOT_FOUND NT_STATUS(0xC0000000 | 0x0262)
+#define NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND NT_STATUS(0xC0000000 | 0x0263)
+#define NT_STATUS_RESOURCE_NOT_OWNED NT_STATUS(0xC0000000 | 0x0264)
+#define NT_STATUS_TOO_MANY_LINKS NT_STATUS(0xC0000000 | 0x0265)
+#define NT_STATUS_QUOTA_LIST_INCONSISTENT NT_STATUS(0xC0000000 | 0x0266)
+#define NT_STATUS_FILE_IS_OFFLINE NT_STATUS(0xC0000000 | 0x0267)
+#define NT_STATUS_NOT_A_REPARSE_POINT NT_STATUS(0xC0000000 | 0x0275)
+#define NT_STATUS_NO_SUCH_JOB NT_STATUS(0xC0000000 | 0xEDE) /* scheduler */
+
+#endif /* _NTERR_H */
diff --git a/source/include/ntioctl.h b/source/include/ntioctl.h
new file mode 100644
index 00000000000..9814c88e5e5
--- /dev/null
+++ b/source/include/ntioctl.h
@@ -0,0 +1,87 @@
+/*
+ Unix SMB/CIFS implementation.
+ NT ioctl code constants
+ Copyright (C) Andrew Tridgell 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ I'm guessing we will need to support a bunch of these eventually. For now
+ we only need the sparse flag
+*/
+
+#ifndef _NTIOCTL_H
+#define _NTIOCTL_H
+
+/* IOCTL information */
+/* List of ioctl function codes that look to be of interest to remote clients like this. */
+/* Need to do some experimentation to make sure they all work remotely. */
+/* Some of the following such as the encryption/compression ones would be */
+/* invoked from tools via a specialized hook into the VFS rather than via the */
+/* standard vfs entry points */
+#define FSCTL_REQUEST_OPLOCK_LEVEL_1 0x00090000
+#define FSCTL_REQUEST_OPLOCK_LEVEL_2 0x00090004
+#define FSCTL_REQUEST_BATCH_OPLOCK 0x00090008
+#define FSCTL_LOCK_VOLUME 0x00090018
+#define FSCTL_UNLOCK_VOLUME 0x0009001C
+#define FSCTL_GET_COMPRESSION 0x0009003C
+#define FSCTL_SET_COMPRESSION 0x0009C040
+#define FSCTL_REQUEST_FILTER_OPLOCK 0x0009008C
+#define FSCTL_FIND_FILES_BY_SID 0x0009008F
+#define FSCTL_FILESYS_GET_STATISTICS 0x00090090
+#define FSCTL_SET_REPARSE_POINT 0x000900A4
+#define FSCTL_GET_REPARSE_POINT 0x000900A8
+#define FSCTL_DELETE_REPARSE_POINT 0x000900AC
+#define FSCTL_0x000900C0 0x000900C0
+#define FSCTL_SET_SPARSE 0x000900C4
+#define FSCTL_SET_ZERO_DATA 0x000900C8
+#define FSCTL_SET_ENCRYPTION 0x000900D7
+#define FSCTL_ENCRYPTION_FSCTL_IO 0x000900DB
+#define FSCTL_WRITE_RAW_ENCRYPTED 0x000900DF
+#define FSCTL_READ_RAW_ENCRYPTED 0x000900E3
+#define FSCTL_SIS_COPYFILE 0x00090100
+#define FSCTL_SIS_LINK_FILES 0x0009C104
+
+#define FSCTL_GET_SHADOW_COPY_DATA 0x00144064 /* KJC -- Shadow Copy information */
+
+#if 0
+#define FSCTL_SECURITY_ID_CHECK
+#define FSCTL_DISMOUNT_VOLUME
+#define FSCTL_GET_NTFS_FILE_RECORD
+#define FSCTL_ALLOW_EXTENDED_DASD_IO
+#define FSCTL_RECALL_FILE
+#define FSCTL_QUERY_ALLOCATED_RANGES
+
+#endif
+
+#define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003
+#define IO_REPARSE_TAG_HSM 0xC0000004
+#define IO_REPARSE_TAG_SIS 0x80000007
+
+
+/* For FSCTL_GET_SHADOW_COPY_DATA ...*/
+typedef char SHADOW_COPY_LABEL[25];
+
+typedef struct shadow_copy_data {
+ TALLOC_CTX *mem_ctx;
+ /* Total number of shadow volumes currently mounted */
+ uint32 num_volumes;
+ /* Concatenated list of labels */
+ SHADOW_COPY_LABEL *labels;
+} SHADOW_COPY_DATA;
+
+
+#endif /* _NTIOCTL_H */
diff --git a/source/include/ntlmssp.h b/source/include/ntlmssp.h
new file mode 100644
index 00000000000..24ac7967615
--- /dev/null
+++ b/source/include/ntlmssp.h
@@ -0,0 +1,169 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+ Copyright (C) Paul Ashton 1997
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* NTLMSSP mode */
+enum NTLMSSP_ROLE
+{
+ NTLMSSP_SERVER,
+ NTLMSSP_CLIENT
+};
+
+/* NTLMSSP message types */
+enum NTLM_MESSAGE_TYPE
+{
+ NTLMSSP_INITIAL = 0 /* samba internal state */,
+ NTLMSSP_NEGOTIATE = 1,
+ NTLMSSP_CHALLENGE = 2,
+ NTLMSSP_AUTH = 3,
+ NTLMSSP_UNKNOWN = 4
+};
+
+/* NTLMSSP negotiation flags */
+#define NTLMSSP_NEGOTIATE_UNICODE 0x00000001
+#define NTLMSSP_NEGOTIATE_OEM 0x00000002
+#define NTLMSSP_REQUEST_TARGET 0x00000004
+#define NTLMSSP_NEGOTIATE_SIGN 0x00000010 /* Message integrity */
+#define NTLMSSP_NEGOTIATE_SEAL 0x00000020 /* Message confidentiality */
+#define NTLMSSP_NEGOTIATE_DATAGRAM_STYLE 0x00000040
+#define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080
+#define NTLMSSP_NEGOTIATE_NETWARE 0x00000100
+#define NTLMSSP_NEGOTIATE_NTLM 0x00000200
+#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x00001000
+#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x00002000
+#define NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL 0x00004000
+#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000
+#define NTLMSSP_TARGET_TYPE_DOMAIN 0x10000
+#define NTLMSSP_TARGET_TYPE_SERVER 0x20000
+#define NTLMSSP_CHAL_INIT_RESPONSE 0x00010000
+
+#define NTLMSSP_CHAL_ACCEPT_RESPONSE 0x00020000
+#define NTLMSSP_CHAL_NON_NT_SESSION_KEY 0x00040000
+#define NTLMSSP_NEGOTIATE_NTLM2 0x00080000
+#define NTLMSSP_CHAL_TARGET_INFO 0x00800000
+#define NTLMSSP_NEGOTIATE_128 0x20000000 /* 128-bit encryption */
+#define NTLMSSP_NEGOTIATE_KEY_EXCH 0x40000000
+#define NTLMSSP_NEGOTIATE_080000000 0x80000000
+
+#define NTLMSSP_NAME_TYPE_SERVER 0x01
+#define NTLMSSP_NAME_TYPE_DOMAIN 0x02
+#define NTLMSSP_NAME_TYPE_SERVER_DNS 0x03
+#define NTLMSSP_NAME_TYPE_DOMAIN_DNS 0x04
+
+typedef struct ntlmssp_state
+{
+ TALLOC_CTX *mem_ctx;
+ unsigned int ref_count;
+ enum NTLMSSP_ROLE role;
+ enum server_types server_role;
+ uint32 expected_state;
+
+ BOOL unicode;
+ BOOL use_ntlmv2;
+ char *user;
+ char *domain;
+ char *workstation;
+ char *password;
+ char *server_domain;
+
+ DATA_BLOB internal_chal; /* Random challenge as supplied to the client for NTLM authentication */
+
+ DATA_BLOB chal; /* Random challenge as input into the actual NTLM (or NTLM2) authentication */
+ DATA_BLOB lm_resp;
+ DATA_BLOB nt_resp;
+ DATA_BLOB session_key;
+
+ uint32 neg_flags; /* the current state of negotiation with the NTLMSSP partner */
+
+ void *auth_context;
+
+ /**
+ * Callback to get the 'challenge' used for NTLM authentication.
+ *
+ * @param ntlmssp_state This structure
+ * @return 8 bytes of challnege data, determined by the server to be the challenge for NTLM authentication
+ *
+ */
+ const uint8 *(*get_challenge)(const struct ntlmssp_state *ntlmssp_state);
+
+ /**
+ * Callback to find if the challenge used by NTLM authentication may be modified
+ *
+ * The NTLM2 authentication scheme modifies the effective challenge, but this is not compatiable with the
+ * current 'security=server' implementation..
+ *
+ * @param ntlmssp_state This structure
+ * @return Can the challenge be set to arbitary values?
+ *
+ */
+ BOOL (*may_set_challenge)(const struct ntlmssp_state *ntlmssp_state);
+
+ /**
+ * Callback to set the 'challenge' used for NTLM authentication.
+ *
+ * The callback may use the void *auth_context to store state information, but the same value is always available
+ * from the DATA_BLOB chal on this structure.
+ *
+ * @param ntlmssp_state This structure
+ * @param challange 8 bytes of data, agreed by the client and server to be the effective challenge for NTLM2 authentication
+ *
+ */
+ NTSTATUS (*set_challenge)(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *challenge);
+
+ /**
+ * Callback to check the user's password.
+ *
+ * The callback must reads the feilds of this structure for the information it needs on the user
+ * @param ntlmssp_state This structure
+ * @param nt_session_key If an NT session key is returned by the authentication process, return it here
+ * @param lm_session_key If an LM session key is returned by the authentication process, return it here
+ *
+ */
+ NTSTATUS (*check_password)(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *nt_session_key, DATA_BLOB *lm_session_key);
+
+ const char *(*get_global_myname)(void);
+ const char *(*get_domain)(void);
+
+ /* SMB Signing */
+
+ uint32 ntlmssp_seq_num;
+
+ /* ntlmv2 */
+ unsigned char send_sign_const[16];
+ unsigned char send_seal_const[16];
+ unsigned char recv_sign_const[16];
+ unsigned char recv_seal_const[16];
+
+ unsigned char send_sign_hash[258];
+ unsigned char send_seal_hash[258];
+ unsigned char recv_sign_hash[258];
+ unsigned char recv_seal_hash[258];
+
+ /* ntlmv1 */
+ unsigned char ntlmssp_hash[258];
+
+ /* it turns out that we don't always get the
+ response in at the time we want to process it.
+ Store it here, until we need it */
+ DATA_BLOB stored_response;
+
+} NTLMSSP_STATE;
+
diff --git a/source/include/ntquotas.h b/source/include/ntquotas.h
new file mode 100644
index 00000000000..dac1173770b
--- /dev/null
+++ b/source/include/ntquotas.h
@@ -0,0 +1,97 @@
+/*
+ Unix SMB/CIFS implementation.
+ NT QUOTA code constants
+ Copyright (C) Stefan (metze) Metzmacher 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _NTQUOTAS_H
+#define _NTQUOTAS_H
+
+/*
+ * details for Quota Flags:
+ *
+ * 0x20 Log Limit: log if the user exceeds his Hard Quota
+ * 0x10 Log Warn: log if the user exceeds his Soft Quota
+ * 0x02 Deny Disk: deny disk access when the user exceeds his Hard Quota
+ * 0x01 Enable Quotas: enable quota for this fs
+ *
+ */
+
+#define QUOTAS_ENABLED 0x0001
+#define QUOTAS_DENY_DISK 0x0002
+#define QUOTAS_LOG_VIOLATIONS 0x0004
+#define CONTENT_INDEX_DISABLED 0x0008
+#define QUOTAS_LOG_THRESHOLD 0x0010
+#define QUOTAS_LOG_LIMIT 0x0020
+#define LOG_VOLUME_THRESHOLD 0x0040
+#define LOG_VOLUME_LIMIT 0x0080
+#define QUOTAS_INCOMPLETE 0x0100
+#define QUOTAS_REBUILDING 0x0200
+#define QUOTAS_0400 0x0400
+#define QUOTAS_0800 0x0800
+#define QUOTAS_1000 0x1000
+#define QUOTAS_2000 0x2000
+#define QUOTAS_4000 0x4000
+#define QUOTAS_8000 0x8000
+
+#define SMB_NTQUOTAS_NO_LIMIT ((SMB_BIG_UINT)(-1))
+#define SMB_NTQUOTAS_NO_ENTRY ((SMB_BIG_UINT)(-2))
+#define SMB_NTQUOTAS_NO_SPACE ((SMB_BIG_UINT)(0))
+#define SMB_NTQUOTAS_1_B (SMB_BIG_UINT)0x0000000000000001
+#define SMB_NTQUOTAS_1KB (SMB_BIG_UINT)0x0000000000000400
+#define SMB_NTQUOTAS_1MB (SMB_BIG_UINT)0x0000000000100000
+#define SMB_NTQUOTAS_1GB (SMB_BIG_UINT)0x0000000040000000
+#define SMB_NTQUOTAS_1TB (SMB_BIG_UINT)0x0000010000000000
+#define SMB_NTQUOTAS_1PB (SMB_BIG_UINT)0x0004000000000000
+#define SMB_NTQUOTAS_1EB (SMB_BIG_UINT)0x1000000000000000
+
+enum SMB_QUOTA_TYPE {
+ SMB_INVALID_QUOTA_TYPE = -1,
+ SMB_USER_FS_QUOTA_TYPE = 1,
+ SMB_USER_QUOTA_TYPE = 2,
+ SMB_GROUP_FS_QUOTA_TYPE = 3,/* not used yet */
+ SMB_GROUP_QUOTA_TYPE = 4 /* not in use yet, maybe for disk_free queries */
+};
+
+typedef struct _SMB_NTQUOTA_STRUCT {
+ enum SMB_QUOTA_TYPE qtype;
+ SMB_BIG_UINT usedspace;
+ SMB_BIG_UINT softlim;
+ SMB_BIG_UINT hardlim;
+ uint32 qflags;
+ DOM_SID sid;
+} SMB_NTQUOTA_STRUCT;
+
+typedef struct _SMB_NTQUOTA_LIST {
+ struct _SMB_NTQUOTA_LIST *prev,*next;
+ TALLOC_CTX *mem_ctx;
+ uid_t uid;
+ SMB_NTQUOTA_STRUCT *quotas;
+} SMB_NTQUOTA_LIST;
+
+typedef struct _SMB_NTQUOTA_HANDLE {
+ BOOL valid;
+ SMB_NTQUOTA_LIST *quota_list;
+ SMB_NTQUOTA_LIST *tmp_list;
+} SMB_NTQUOTA_HANDLE;
+
+#define CHECK_NTQUOTA_HANDLE_OK(fsp,conn) (FNUM_OK(fsp,conn) &&\
+ (fsp)->fake_file_handle &&\
+ ((fsp)->fake_file_handle->type == FAKE_FILE_TYPE_QUOTA) &&\
+ (fsp)->fake_file_handle->pd)
+
+#endif /*_NTQUOTAS_H */
diff --git a/source/include/passdb.h b/source/include/passdb.h
new file mode 100644
index 00000000000..baf0e23a20c
--- /dev/null
+++ b/source/include/passdb.h
@@ -0,0 +1,573 @@
+/*
+ Unix SMB/CIFS implementation.
+ passdb structures and parameters
+ Copyright (C) Gerald Carter 2001
+ Copyright (C) Luke Kenneth Casson Leighton 1998 - 2000
+ Copyright (C) Andrew Bartlett 2002
+ Copyright (C) Simo Sorce 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _PASSDB_H
+#define _PASSDB_H
+
+
+/*
+ * fields_present flags meanings
+ * same names as found in samba4 idl files
+ */
+
+#define ACCT_USERNAME 0x00000001
+#define ACCT_FULL_NAME 0x00000002
+#define ACCT_RID 0x00000004
+#define ACCT_PRIMARY_GID 0x00000008
+#define ACCT_ADMIN_DESC 0x00000010
+#define ACCT_DESCRIPTION 0x00000020
+#define ACCT_HOME_DIR 0x00000040
+#define ACCT_HOME_DRIVE 0x00000080
+#define ACCT_LOGON_SCRIPT 0x00000100
+#define ACCT_PROFILE 0x00000200
+#define ACCT_WORKSTATIONS 0x00000400
+#define ACCT_LAST_LOGON 0x00000800
+#define ACCT_LAST_LOGOFF 0x00001000
+#define ACCT_LOGON_HOURS 0x00002000
+#define ACCT_BAD_PWD_COUNT 0x00004000
+#define ACCT_NUM_LOGONS 0x00008000
+#define ACCT_ALLOW_PWD_CHANGE 0x00010000
+#define ACCT_FORCE_PWD_CHANGE 0x00020000
+#define ACCT_LAST_PWD_CHANGE 0x00040000
+#define ACCT_EXPIRY 0x00080000
+#define ACCT_FLAGS 0x00100000
+#define ACCT_CALLBACK 0x00200000
+#define ACCT_COUNTRY_CODE 0x00400000
+#define ACCT_CODE_PAGE 0x00800000
+#define ACCT_NT_PWD_SET 0x01000000
+#define ACCT_LM_PWD_SET 0x02000000
+#define ACCT_PRIVATEDATA 0x04000000
+#define ACCT_EXPIRED_FLAG 0x08000000
+#define ACCT_SEC_DESC 0x10000000
+#define ACCT_OWF_PWD 0x20000000
+
+/*
+ * bit flags representing initialized fields in SAM_ACCOUNT
+ */
+enum pdb_elements {
+ PDB_UNINIT,
+ PDB_SMBHOME,
+ PDB_PROFILE,
+ PDB_DRIVE,
+ PDB_LOGONSCRIPT,
+ PDB_LOGONTIME,
+ PDB_LOGOFFTIME,
+ PDB_KICKOFFTIME,
+ PDB_BAD_PASSWORD_TIME,
+ PDB_CANCHANGETIME,
+ PDB_MUSTCHANGETIME,
+ PDB_PLAINTEXT_PW,
+ PDB_USERNAME,
+ PDB_FULLNAME,
+ PDB_DOMAIN,
+ PDB_NTUSERNAME,
+ PDB_HOURSLEN,
+ PDB_LOGONDIVS,
+ PDB_USERSID,
+ PDB_GROUPSID,
+ PDB_ACCTCTRL,
+ PDB_PASSLASTSET,
+ PDB_UNIXHOMEDIR,
+ PDB_ACCTDESC,
+ PDB_WORKSTATIONS,
+ PDB_UNKNOWNSTR,
+ PDB_MUNGEDDIAL,
+ PDB_HOURS,
+ PDB_FIELDS_PRESENT,
+ PDB_BAD_PASSWORD_COUNT,
+ PDB_LOGON_COUNT,
+ PDB_UNKNOWN6,
+ PDB_LMPASSWD,
+ PDB_NTPASSWD,
+ PDB_BACKEND_PRIVATE_DATA,
+
+ /* this must be the last element */
+ PDB_COUNT
+};
+
+enum pdb_group_elements {
+ PDB_GROUP_NAME,
+ PDB_GROUP_SID,
+ PDB_GROUP_SID_NAME_USE,
+ PDB_GROUP_MEMBERS,
+
+ /* this must be the last element */
+ PDB_GROUP_COUNT
+};
+
+enum pdb_trust_passwd_elements {
+ PDB_TRUST_PASS,
+ PDB_TRUST_SID,
+ PDB_TRUST_NAME,
+ PDB_TRUST_MODTIME,
+ PDB_TRUST_FLAGS,
+
+ PDB_TRUST_COUNT
+};
+
+enum pdb_value_state {
+ PDB_DEFAULT=0,
+ PDB_SET,
+ PDB_CHANGED
+};
+
+#define IS_SAM_SET(x, flag) (pdb_get_init_flags(x, flag) == PDB_SET)
+#define IS_SAM_CHANGED(x, flag) (pdb_get_init_flags(x, flag) == PDB_CHANGED)
+#define IS_SAM_DEFAULT(x, flag) (pdb_get_init_flags(x, flag) == PDB_DEFAULT)
+
+/* cache for bad password lockout data, to be used on replicated SAMs */
+typedef struct logon_cache_struct
+{
+ time_t entry_timestamp;
+ uint16 acct_ctrl;
+ uint16 bad_password_count;
+ time_t bad_password_time;
+} LOGIN_CACHE;
+
+typedef struct sam_passwd
+{
+ TALLOC_CTX *mem_ctx;
+
+ void (*free_fn)(struct sam_passwd **);
+
+ struct pdb_methods *methods;
+
+ struct user_data {
+ /* initialization flags */
+ struct bitmap *change_flags;
+ struct bitmap *set_flags;
+
+ time_t logon_time; /* logon time */
+ time_t logoff_time; /* logoff time */
+ time_t kickoff_time; /* kickoff time */
+ time_t bad_password_time; /* last bad password entered */
+ time_t pass_last_set_time; /* password last set time */
+ time_t pass_can_change_time; /* password can change time */
+ time_t pass_must_change_time; /* password must change time */
+
+ const char * username; /* UNIX username string */
+ const char * domain; /* Windows Domain name */
+ const char * nt_username; /* Windows username string */
+ const char * full_name; /* user's full name string */
+ const char * unix_home_dir; /* UNIX home directory string */
+ const char * home_dir; /* home directory string */
+ const char * dir_drive; /* home directory drive string */
+ const char * logon_script; /* logon script string */
+ const char * profile_path; /* profile path string */
+ const char * acct_desc ; /* user description string */
+ const char * workstations; /* login from workstations string */
+ const char * unknown_str ; /* don't know what this is, yet. */
+ const char * munged_dial ; /* munged path name and dial-back tel number */
+
+ DOM_SID user_sid; /* Primary User SID */
+ DOM_SID group_sid; /* Primary Group SID */
+
+ DATA_BLOB lm_pw; /* .data is Null if no password */
+ DATA_BLOB nt_pw; /* .data is Null if no password */
+ char* plaintext_pw; /* is Null if not available */
+
+ uint16 acct_ctrl; /* account info (ACB_xxxx bit-mask) */
+ uint32 fields_present; /* 0x00ff ffff */
+
+ uint16 logon_divs; /* 168 - number of hours in a week */
+ uint32 hours_len; /* normally 21 bytes */
+ uint8 hours[MAX_HOURS_LEN];
+
+ /* Was unknown_5. */
+ uint16 bad_password_count;
+ uint16 logon_count;
+
+ uint32 unknown_6; /* 0x0000 04ec */
+ /* a tag for who added the private methods */
+ const struct pdb_methods *backend_private_methods;
+ void *backend_private_data;
+ void (*backend_private_data_free_fn)(void **);
+ } private;
+
+ /* Lets see if the remaining code can get the hint that you
+ are meant to use the pdb_...() functions. */
+
+} SAM_ACCOUNT;
+
+typedef struct sam_group {
+ TALLOC_CTX *mem_ctx;
+
+ void (*free_fn)(struct sam_group **);
+
+ struct pdb_methods *methods;
+
+ struct group_data {
+ /* initialization flags */
+ struct bitmap *change_flags;
+ struct bitmap *set_flags;
+
+ const char *name; /* Windows group name string */
+
+ DOM_SID sid; /* Group SID */
+ enum SID_NAME_USE sid_name_use; /* Group type */
+
+ uint32 mem_num; /* Number of member SIDs */
+ DOM_SID *members; /* SID array */
+ } private;
+
+} SAM_GROUP;
+
+
+typedef struct _GROUP_INFO {
+ struct pdb_methods *methods;
+ DOM_SID sid;
+ enum SID_NAME_USE sid_name_use;
+ fstring nt_name;
+ fstring comment;
+} GROUP_INFO;
+
+struct acct_info
+{
+ fstring acct_name; /* account name */
+ fstring acct_desc; /* account name */
+ uint32 rid; /* domain-relative RID */
+};
+
+typedef struct sam_trust_passwd {
+ TALLOC_CTX *mem_ctx;
+
+ void (*free_fn)(struct sam_trust_passwd **);
+
+ struct pdb_methods *methods;
+
+ struct trust_passwd_data {
+ uint16 flags; /* flags */
+ size_t uni_name_len; /* unicode name length */
+ smb_ucs2_t uni_name[32]; /* unicode domain name */
+ fstring pass; /* trust password */
+ time_t mod_time; /* last change time */
+ DOM_SID domain_sid; /* trusted domain sid */
+ } private;
+
+} SAM_TRUST_PASSWD;
+
+
+
+/*****************************************************************
+ Functions to be implemented by the new (v2) passdb API
+****************************************************************/
+
+/*
+ * This next constant specifies the version number of the PASSDB interface
+ * this SAMBA will load. Increment this if *ANY* changes are made to the interface.
+ */
+
+#define PASSDB_INTERFACE_VERSION 7
+
+typedef struct pdb_context
+{
+ struct pdb_methods *pdb_methods;
+ struct pdb_methods *pwent_methods;
+
+ /* These functions are wrappers for the functions listed above.
+ They may do extra things like re-reading a SAM_ACCOUNT on update */
+
+ NTSTATUS (*pdb_setsampwent)(struct pdb_context *, BOOL update);
+
+ void (*pdb_endsampwent)(struct pdb_context *);
+
+ NTSTATUS (*pdb_getsampwent)(struct pdb_context *, SAM_ACCOUNT *user);
+
+ NTSTATUS (*pdb_getsampwnam)(struct pdb_context *, SAM_ACCOUNT *sam_acct, const char *username);
+
+ NTSTATUS (*pdb_getsampwsid)(struct pdb_context *, SAM_ACCOUNT *sam_acct, const DOM_SID *sid);
+
+ NTSTATUS (*pdb_add_sam_account)(struct pdb_context *, SAM_ACCOUNT *sampass);
+
+ NTSTATUS (*pdb_update_sam_account)(struct pdb_context *, SAM_ACCOUNT *sampass);
+
+ NTSTATUS (*pdb_delete_sam_account)(struct pdb_context *, SAM_ACCOUNT *username);
+
+ /* group mapping functions: to be removed */
+
+ NTSTATUS (*pdb_getgrsid)(struct pdb_context *context, GROUP_MAP *map, DOM_SID sid);
+
+ NTSTATUS (*pdb_getgrgid)(struct pdb_context *context, GROUP_MAP *map, gid_t gid);
+
+ NTSTATUS (*pdb_getgrnam)(struct pdb_context *context, GROUP_MAP *map, const char *name);
+
+ NTSTATUS (*pdb_add_group_mapping_entry)(struct pdb_context *context,
+ GROUP_MAP *map);
+
+ NTSTATUS (*pdb_update_group_mapping_entry)(struct pdb_context *context,
+ GROUP_MAP *map);
+
+ NTSTATUS (*pdb_delete_group_mapping_entry)(struct pdb_context *context,
+ DOM_SID sid);
+
+ NTSTATUS (*pdb_enum_group_mapping)(struct pdb_context *context,
+ enum SID_NAME_USE sid_name_use,
+ GROUP_MAP **rmap, int *num_entries,
+ BOOL unix_only);
+
+ NTSTATUS (*pdb_find_alias)(struct pdb_context *context,
+ const char *name, DOM_SID *sid);
+
+ NTSTATUS (*pdb_create_alias)(struct pdb_context *context,
+ const char *name, uint32 *rid);
+
+ NTSTATUS (*pdb_delete_alias)(struct pdb_context *context,
+ const DOM_SID *sid);
+
+ NTSTATUS (*pdb_enum_aliases)(struct pdb_context *context,
+ const DOM_SID *domain_sid,
+ uint32 start_idx, uint32 num_entries,
+ uint32 *num_aliases,
+ struct acct_info **aliases);
+
+ NTSTATUS (*pdb_get_aliasinfo)(struct pdb_context *context,
+ const DOM_SID *sid,
+ struct acct_info *info);
+
+ NTSTATUS (*pdb_set_aliasinfo)(struct pdb_context *context,
+ const DOM_SID *sid,
+ struct acct_info *info);
+
+ NTSTATUS (*pdb_add_aliasmem)(struct pdb_context *context,
+ const DOM_SID *alias,
+ const DOM_SID *member);
+
+ NTSTATUS (*pdb_del_aliasmem)(struct pdb_context *context,
+ const DOM_SID *alias,
+ const DOM_SID *member);
+
+ NTSTATUS (*pdb_enum_aliasmem)(struct pdb_context *context,
+ const DOM_SID *alias,
+ DOM_SID **members, int *num_members);
+
+ NTSTATUS (*pdb_enum_alias_memberships)(struct pdb_context *context,
+ const DOM_SID *alias,
+ DOM_SID **aliases,
+ int *num);
+
+ /* group functions */
+
+ NTSTATUS (*pdb_get_group_info_by_sid)(struct pdb_context *context, GROUP_INFO *info, const DOM_SID *group);
+
+ NTSTATUS (*pdb_get_group_list)(struct pdb_context *context, GROUP_INFO **info, const enum SID_NAME_USE sid_name_use, int *num_groups);
+
+ NTSTATUS (*pdb_get_group_sids)(struct pdb_context *context, const DOM_SID *group, DOM_SID **members, int *num_members);
+
+ NTSTATUS (*pdb_add_group)(struct pdb_context *context, const SAM_GROUP *group);
+
+ NTSTATUS (*pdb_update_group)(struct pdb_context *context, const SAM_GROUP *group);
+
+ NTSTATUS (*pdb_delete_group)(struct pdb_context *context, const DOM_SID *group);
+
+ NTSTATUS (*pdb_add_sid_to_group)(struct pdb_context *context, const DOM_SID *group, const DOM_SID *member);
+
+ NTSTATUS (*pdb_remove_sid_from_group)(struct pdb_context *context, const DOM_SID *group, const DOM_SID *member);
+
+ NTSTATUS (*pdb_get_group_info_by_name)(struct pdb_context *context, GROUP_INFO *info, const char *name);
+
+ NTSTATUS (*pdb_get_group_info_by_nt_name)(struct pdb_context *context, GROUP_INFO *info, const char *nt_name);
+
+ NTSTATUS (*pdb_get_group_uids)(struct pdb_context *context, const DOM_SID *group, uid_t **members, int *num_members);
+
+ /* trust password functions */
+
+ NTSTATUS (*pdb_settrustpwent)(struct pdb_context *context);
+
+ NTSTATUS (*pdb_gettrustpwent)(struct pdb_context *context, SAM_TRUST_PASSWD *trust);
+
+ NTSTATUS (*pdb_gettrustpwnam)(struct pdb_context *context, SAM_TRUST_PASSWD *trust, const char *dom_name);
+
+ NTSTATUS (*pdb_gettrustpwsid)(struct pdb_context *context, SAM_TRUST_PASSWD *trust, const DOM_SID *sid);
+
+ NTSTATUS (*pdb_add_trust_passwd)(struct pdb_context *context, SAM_TRUST_PASSWD* trust);
+
+ NTSTATUS (*pdb_update_trust_passwd)(struct pdb_context *context, SAM_TRUST_PASSWD* trust);
+
+ NTSTATUS (*pdb_delete_trust_passwd)(struct pdb_context *context, SAM_TRUST_PASSWD* trust);
+
+ /* privileges functions */
+
+ NTSTATUS (*pdb_add_sid_to_privilege)(struct pdb_context *context, const char *priv_name, const DOM_SID *sid);
+
+ NTSTATUS (*pdb_remove_sid_from_privilege)(struct pdb_context *context, const char *priv_name, const DOM_SID *sid);
+
+ NTSTATUS (*pdb_get_privilege_set)(struct pdb_context *context, DOM_SID *user_sids, int num_sids, PRIVILEGE_SET *privs);
+
+ NTSTATUS (*pdb_get_privilege_entry)(struct pdb_context *context, const char *privname, char **sid_list);
+
+ void (*free_fn)(struct pdb_context **);
+
+ TALLOC_CTX *mem_ctx;
+
+} PDB_CONTEXT;
+
+typedef struct pdb_methods
+{
+ const char *name; /* What name got this module */
+ struct pdb_context *parent;
+
+ /* Use macros from dlinklist.h on these two */
+ struct pdb_methods *next;
+ struct pdb_methods *prev;
+
+ NTSTATUS (*setsampwent)(struct pdb_methods *, BOOL update);
+
+ void (*endsampwent)(struct pdb_methods *);
+
+ NTSTATUS (*getsampwent)(struct pdb_methods *, SAM_ACCOUNT *user);
+
+ NTSTATUS (*getsampwnam)(struct pdb_methods *, SAM_ACCOUNT *sam_acct, const char *username);
+
+ NTSTATUS (*getsampwsid)(struct pdb_methods *, SAM_ACCOUNT *sam_acct, const DOM_SID *sid);
+
+ NTSTATUS (*add_sam_account)(struct pdb_methods *, SAM_ACCOUNT *sampass);
+
+ NTSTATUS (*update_sam_account)(struct pdb_methods *, SAM_ACCOUNT *sampass);
+
+ NTSTATUS (*delete_sam_account)(struct pdb_methods *, SAM_ACCOUNT *username);
+
+ /* group mapping functions: to be removed */
+
+ NTSTATUS (*getgrsid)(struct pdb_methods *methods, GROUP_MAP *map, DOM_SID sid);
+
+ NTSTATUS (*getgrgid)(struct pdb_methods *methods, GROUP_MAP *map, gid_t gid);
+
+ NTSTATUS (*getgrnam)(struct pdb_methods *methods, GROUP_MAP *map, const char *name);
+
+ NTSTATUS (*add_group_mapping_entry)(struct pdb_methods *methods,
+ GROUP_MAP *map);
+
+ NTSTATUS (*update_group_mapping_entry)(struct pdb_methods *methods,
+ GROUP_MAP *map);
+
+ NTSTATUS (*delete_group_mapping_entry)(struct pdb_methods *methods,
+ DOM_SID sid);
+
+ NTSTATUS (*enum_group_mapping)(struct pdb_methods *methods,
+ enum SID_NAME_USE sid_name_use,
+ GROUP_MAP **rmap, int *num_entries,
+ BOOL unix_only);
+
+ NTSTATUS (*find_alias)(struct pdb_methods *methods,
+ const char *name, DOM_SID *sid);
+
+ NTSTATUS (*create_alias)(struct pdb_methods *methods,
+ const char *name, uint32 *rid);
+
+ NTSTATUS (*delete_alias)(struct pdb_methods *methods,
+ const DOM_SID *sid);
+
+ NTSTATUS (*enum_aliases)(struct pdb_methods *methods,
+ const DOM_SID *domain_sid,
+ uint32 start_idx, uint32 max_entries,
+ uint32 *num_aliases, struct acct_info **info);
+
+ NTSTATUS (*get_aliasinfo)(struct pdb_methods *methods,
+ const DOM_SID *sid,
+ struct acct_info *info);
+
+ NTSTATUS (*set_aliasinfo)(struct pdb_methods *methods,
+ const DOM_SID *sid,
+ struct acct_info *info);
+
+ NTSTATUS (*add_aliasmem)(struct pdb_methods *methods,
+ const DOM_SID *alias, const DOM_SID *member);
+ NTSTATUS (*del_aliasmem)(struct pdb_methods *methods,
+ const DOM_SID *alias, const DOM_SID *member);
+ NTSTATUS (*enum_aliasmem)(struct pdb_methods *methods,
+ const DOM_SID *alias, DOM_SID **members,
+ int *num_members);
+ NTSTATUS (*enum_alias_memberships)(struct pdb_methods *methods,
+ const DOM_SID *sid,
+ DOM_SID **aliases, int *num);
+
+ /* group functions */
+
+ NTSTATUS (*get_group_info_by_sid)(struct pdb_methods *methods, GROUP_INFO *info, const DOM_SID *group);
+
+ NTSTATUS (*get_group_list)(struct pdb_methods *methods, GROUP_INFO **info, const enum SID_NAME_USE sid_name_use, int *num_groups);
+
+ NTSTATUS (*get_group_sids)(struct pdb_methods *methods, const DOM_SID *group, DOM_SID **members, int *num_members);
+
+ NTSTATUS (*add_group)(struct pdb_methods *methods, const SAM_GROUP *group);
+
+ NTSTATUS (*update_group)(struct pdb_methods *methods, const SAM_GROUP *group);
+
+ NTSTATUS (*delete_group)(struct pdb_methods *methods, const DOM_SID *group);
+
+ NTSTATUS (*add_sid_to_group)(struct pdb_methods *methods, const DOM_SID *group, const DOM_SID *member);
+
+ NTSTATUS (*remove_sid_from_group)(struct pdb_methods *methods, const DOM_SID *group, const DOM_SID *member);
+
+ NTSTATUS (*get_group_info_by_name)(struct pdb_methods *methods, GROUP_INFO *info, const char *name);
+
+ NTSTATUS (*get_group_info_by_nt_name)(struct pdb_methods *methods, GROUP_INFO *info, const char *nt_name);
+
+ NTSTATUS (*get_group_uids)(struct pdb_methods *methods, const DOM_SID *group, uid_t **members, int *num_members);
+
+ void *private_data; /* Private data of some kind */
+
+ void (*free_private_data)(void **);
+
+ /* trust password functions */
+
+ NTSTATUS (*settrustpwent)(struct pdb_methods *methods);
+
+ NTSTATUS (*gettrustpwent)(struct pdb_methods *methods, SAM_TRUST_PASSWD *trust);
+
+ NTSTATUS (*gettrustpwnam)(struct pdb_methods *methods, SAM_TRUST_PASSWD *trust, const char *name);
+
+ NTSTATUS (*gettrustpwsid)(struct pdb_methods *methods, SAM_TRUST_PASSWD *trust, const DOM_SID *sid);
+
+ NTSTATUS (*add_trust_passwd)(struct pdb_methods *methods, const SAM_TRUST_PASSWD* trust);
+
+ NTSTATUS (*update_trust_passwd)(struct pdb_methods *methods, const SAM_TRUST_PASSWD* trust);
+
+ NTSTATUS (*delete_trust_passwd)(struct pdb_methods *methods, const SAM_TRUST_PASSWD* trust);
+
+ /* privileges functions */
+
+ NTSTATUS (*add_sid_to_privilege)(struct pdb_methods *methods, const char *priv_name, const DOM_SID *sid);
+
+ NTSTATUS (*remove_sid_from_privilege)(struct pdb_methods *methods, const char *priv_name, const DOM_SID *sid);
+
+ NTSTATUS (*get_privilege_set)(struct pdb_methods *methods, DOM_SID *user_sids, int num_sids, PRIVILEGE_SET *privs);
+
+ NTSTATUS (*get_privilege_entry)(struct pdb_methods *methods, const char *privname, char **sid_list);
+
+} PDB_METHODS;
+
+typedef NTSTATUS (*pdb_init_function)(struct pdb_context *,
+ struct pdb_methods **,
+ const char *);
+
+struct pdb_init_function_entry {
+ const char *name;
+ /* Function to create a member of the pdb_methods list */
+ pdb_init_function init;
+ struct pdb_init_function_entry *prev, *next;
+};
+
+enum sql_search_field { SQL_SEARCH_NONE = 0, SQL_SEARCH_USER_SID = 1, SQL_SEARCH_USER_NAME = 2};
+
+#endif /* _PASSDB_H */
diff --git a/source/include/popt_common.h b/source/include/popt_common.h
new file mode 100644
index 00000000000..6db30fbc0ac
--- /dev/null
+++ b/source/include/popt_common.h
@@ -0,0 +1,49 @@
+/*
+ Unix SMB/CIFS implementation.
+ Common popt arguments
+ Copyright (C) Jelmer Vernooij 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _POPT_COMMON_H
+#define _POPT_COMMON_H
+
+/* Common popt structures */
+extern struct poptOption popt_common_samba[];
+extern struct poptOption popt_common_connection[];
+extern struct poptOption popt_common_version[];
+extern struct poptOption popt_common_credentials[];
+
+#ifndef POPT_TABLEEND
+#define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL }
+#endif
+
+#define POPT_COMMON_SAMBA { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_samba, 0, "Common samba options:", NULL },
+#define POPT_COMMON_CONNECTION { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_connection, 0, "Connection options:", NULL },
+#define POPT_COMMON_VERSION { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_version, 0, "Common samba options:", NULL },
+#define POPT_COMMON_CREDENTIALS { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_credentials, 0, "Authentication options:", NULL },
+
+struct user_auth_info {
+ pstring username;
+ pstring password;
+ BOOL got_pass;
+ BOOL use_kerberos;
+ int signing_state;
+};
+
+extern struct user_auth_info cmdline_auth_info;
+
+#endif /* _POPT_COMMON_H */
diff --git a/source/include/printing.h b/source/include/printing.h
new file mode 100644
index 00000000000..bf7c61b251e
--- /dev/null
+++ b/source/include/printing.h
@@ -0,0 +1,91 @@
+#ifndef PRINTING_H_
+#define PRINTING_H_
+
+/*
+ Unix SMB/CIFS implementation.
+ printing definitions
+ Copyright (C) Andrew Tridgell 1992-2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ This file defines the low-level printing system interfaces used by the
+ SAMBA printing subsystem.
+*/
+
+/* Information for print jobs */
+struct printjob {
+ pid_t pid; /* which process launched the job */
+ int sysjob; /* the system (lp) job number */
+ int fd; /* file descriptor of open file if open */
+ time_t starttime; /* when the job started spooling */
+ int status; /* the status of this job */
+ size_t size; /* the size of the job so far */
+ int page_count; /* then number of pages so far */
+ BOOL spooled; /* has it been sent to the spooler yet? */
+ BOOL smbjob; /* set if the job is a SMB job */
+ fstring filename; /* the filename used to spool the file */
+ fstring jobname; /* the job name given to us by the client */
+ fstring user; /* the user who started the job */
+ fstring queuename; /* service number of printer for this job */
+ NT_DEVICEMODE *nt_devmode;
+};
+
+/* Information for print interfaces */
+struct printif
+{
+ int (*queue_get)(int snum, print_queue_struct **q,
+ print_status_struct *status);
+ int (*queue_pause)(int snum);
+ int (*queue_resume)(int snum);
+ int (*job_delete)(int snum, struct printjob *pjob);
+ int (*job_pause)(int snum, struct printjob *pjob);
+ int (*job_resume)(int snum, struct printjob *pjob);
+ int (*job_submit)(int snum, struct printjob *pjob);
+};
+
+extern struct printif generic_printif;
+
+#ifdef HAVE_CUPS
+extern struct printif cups_printif;
+#endif /* HAVE_CUPS */
+
+/* PRINT_MAX_JOBID is now defined in local.h */
+#define UNIX_JOB_START PRINT_MAX_JOBID
+#define NEXT_JOBID(j) ((j+1) % PRINT_MAX_JOBID > 0 ? (j+1) % PRINT_MAX_JOBID : 1)
+
+#define MAX_CACHE_VALID_TIME 3600
+
+#define PRINT_SPOOL_PREFIX "smbprn."
+#define PRINT_DATABASE_VERSION 5
+
+/* There can be this many printing tdb's open, plus any locked ones. */
+#define MAX_PRINT_DBS_OPEN 1
+
+struct tdb_print_db {
+ struct tdb_print_db *next, *prev;
+ TDB_CONTEXT *tdb;
+ int ref_count;
+ fstring printer_name;
+};
+
+/*
+ * Used for print notify
+ */
+
+#define NOTIFY_PID_LIST_KEY "NOTIFY_PID_LIST"
+
+#endif /* PRINTING_H_ */
diff --git a/source/include/privileges.h b/source/include/privileges.h
new file mode 100644
index 00000000000..289afa234ec
--- /dev/null
+++ b/source/include/privileges.h
@@ -0,0 +1,99 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+ Copyright (C) Paul Ashton 1997
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef PRIVILEGES_H
+#define PRIVILEGES_H
+
+#define PRIV_ALL_INDEX 30
+
+#define SE_NONE 0
+#define SE_ASSIGN_PRIMARY_TOKEN 1
+#define SE_CREATE_TOKEN 2
+#define SE_LOCK_MEMORY 3
+#define SE_INCREASE_QUOTA 4
+#define SE_UNSOLICITED_INPUT 5
+#define SE_MACHINE_ACCOUNT 6
+#define SE_TCB 7
+#define SE_SECURITY 8
+#define SE_TAKE_OWNERSHIP 9
+#define SE_LOAD_DRIVER 10
+#define SE_SYSTEM_PROFILE 11
+#define SE_SYSTEM_TIME 12
+#define SE_PROF_SINGLE_PROCESS 13
+#define SE_INC_BASE_PRIORITY 14
+#define SE_CREATE_PAGEFILE 15
+#define SE_CREATE_PERMANENT 16
+#define SE_BACKUP 17
+#define SE_RESTORE 18
+#define SE_SHUTDOWN 19
+#define SE_DEBUG 20
+#define SE_AUDIT 21
+#define SE_SYSTEM_ENVIRONMENT 22
+#define SE_CHANGE_NOTIFY 23
+#define SE_REMOTE_SHUTDOWN 24
+#define SE_UNDOCK 25
+#define SE_SYNC_AGENT 26
+#define SE_ENABLE_DELEGATION 27
+#define SE_PRINT_OPERATOR 28
+#define SE_ADD_USERS 29
+#define SE_ALL_PRIVS 0xffff
+
+#define PR_NONE 0x0000
+#define PR_LOG_ON_LOCALLY 0x0001
+#define PR_ACCESS_FROM_NETWORK 0x0002
+#define PR_LOG_ON_BATCH_JOB 0x0004
+#define PR_LOG_ON_SERVICE 0x0010
+
+#ifndef _BOOL
+typedef int BOOL;
+#define _BOOL /* So we don't typedef BOOL again in vfs.h */
+#endif
+
+typedef struct LUID
+{
+ uint32 low;
+ uint32 high;
+} LUID;
+
+typedef struct LUID_ATTR
+{
+ LUID luid;
+ uint32 attr;
+} LUID_ATTR;
+
+typedef struct privilege_set
+{
+ TALLOC_CTX *mem_ctx;
+ BOOL ext_ctx;
+ uint32 count;
+ uint32 control;
+ LUID_ATTR *set;
+} PRIVILEGE_SET;
+
+typedef struct _PRIVS {
+ uint32 se_priv;
+ const char *priv;
+ const char *description;
+} PRIVS;
+
+
+#endif /* PRIVILEGES_H */
diff --git a/source/include/pstring.h b/source/include/pstring.h
new file mode 100644
index 00000000000..92870e4cae5
--- /dev/null
+++ b/source/include/pstring.h
@@ -0,0 +1,36 @@
+/*
+ samba -- Unix SMB/CIFS implementation.
+ Safe standardized string types
+
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) John H Terpstra 1996-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Paul Ashton 1998-2000
+ Copyright (C) Martin Pool 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _PSTRING
+
+#define PSTRING_LEN 1024
+#define FSTRING_LEN 256
+
+typedef char pstring[PSTRING_LEN];
+typedef char fstring[FSTRING_LEN];
+
+#define _PSTRING
+
+#endif /* ndef _PSTRING */
diff --git a/source/include/rap.h b/source/include/rap.h
new file mode 100755
index 00000000000..993dfa7e335
--- /dev/null
+++ b/source/include/rap.h
@@ -0,0 +1,507 @@
+/*
+ Samba Unix/Linux SMB client library
+ RAP (SMB Remote Procedure Calls) defines and structures
+ Copyright (C) Steve French 2001 (sfrench@us.ibm.com)
+ Copyright (C) Jim McDonough 2001 (jmcd@us.ibm.com)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _RAP_H_
+#define _RAP_H_
+
+/*****************************************************/
+/* */
+/* Additional RAP functionality */
+/* */
+/* RAP is the original SMB RPC, documented */
+/* by Microsoft and X/Open in the 1990s and */
+/* supported by most SMB/CIFS servers although */
+/* it is unlikely that any one implementation */
+/* supports all RAP command codes since some */
+/* are quite obsolete and a few are specific */
+/* to a particular network operating system */
+/* */
+/* Although it has largely been replaced */
+/* for complex remote admistration and management */
+/* (of servers) by the relatively newer */
+/* DCE/RPC based remote API (which better handles */
+/* large >64K data structures), there are many */
+/* important administrative and resource location */
+/* tasks and user tasks (e.g. password change) */
+/* that are performed via RAP. */
+/* */
+/* Although a few of the RAP calls are implemented */
+/* in the Samba client library already (clirap.c) */
+/* the new ones are in clirap2.c for easy patching */
+/* and integration and a corresponding header */
+/* file, rap.h, has been created. */
+/* */
+/* This is based on data from the CIFS spec */
+/* and the LAN Server and LAN Manager */
+/* Programming Reference books and published */
+/* RAP document and CIFS forum postings and */
+/* lots of trial and error. Additional */
+/* background information is available from the */
+/* X/Open reference book in their PC Interworking */
+/* series "IPC for SMB" and also from the */
+/* interoperability documentation in */
+/* ftp://ftp.microsoft.com/developr/drg/cifs */
+/* */
+/* Function names changed from API_ (as they are */
+/* in the CIFS specification to RAP_ in order */
+/* to avoid confusion with other API calls */
+/* sent via DCE RPC */
+/* */
+/*****************************************************/
+
+/*****************************************************/
+/* */
+/* Although without pound defines (of this header) */
+/* cifsrap.c already includes support for: */
+/* */
+/* WshareEnum (API number 0, level 1) */
+/* NetServerEnum2 (API num 104, level 1) */
+/* WWkstaUserLogon (132) */
+/* SamOEMchgPasswordUser2_P (214) */
+/* */
+/* and cifsprint.c already includes support for: */
+/* */
+/* WPrintJobEnum (API num 76, level 2) */
+/* WPrintJobDel (API num 81) */
+/* */
+/*****************************************************/
+
+#define RAP_WshareEnum 0
+#define RAP_WshareGetInfo 1
+#define RAP_WshareSetInfo 2
+#define RAP_WshareAdd 3
+#define RAP_WshareDel 4
+#define RAP_NetShareCheck 5
+#define RAP_WsessionEnum 6
+#define RAP_WsessionGetInfo 7
+#define RAP_WsessionDel 8
+#define RAP_WconnectionEnum 9
+#define RAP_WfileEnum 10
+#define RAP_WfileGetInfo 11
+#define RAP_WfileClose 12
+#define RAP_WserverGetInfo 13
+#define RAP_WserverSetInfo 14
+#define RAP_WserverDiskEnum 15
+#define RAP_WserverAdminCommand 16
+#define RAP_NetAuditOpen 17
+#define RAP_WauditClear 18
+#define RAP_NetErrorLogOpen 19
+#define RAP_WerrorLogClear 20
+#define RAP_NetCharDevEnum 21
+#define RAP_NetCharDevGetInfo 22
+#define RAP_WCharDevControl 23
+#define RAP_NetCharDevQEnum 24
+#define RAP_NetCharDevQGetInfo 25
+#define RAP_WCharDevQSetInfo 26
+#define RAP_WCharDevQPurge 27
+#define RAP_WCharDevQPurgeSelf 28
+#define RAP_WMessageNameEnum 29
+#define RAP_WMessageNameGetInfo 30
+#define RAP_WMessageNameAdd 31
+#define RAP_WMessageNameDel 32
+#define RAP_WMessageNameFwd 33
+#define RAP_WMessageNameUnFwd 34
+#define RAP_WMessageBufferSend 35
+#define RAP_WMessageFileSend 36
+#define RAP_WMessageLogFileSet 37
+#define RAP_WMessageLogFileGet 38
+#define RAP_WServiceEnum 39
+#define RAP_WServiceInstall 40
+#define RAP_WServiceControl 41
+#define RAP_WAccessEnum 42
+#define RAP_WAccessGetInfo 43
+#define RAP_WAccessSetInfo 44
+#define RAP_WAccessAdd 45
+#define RAP_WAccessDel 46
+#define RAP_WGroupEnum 47
+#define RAP_WGroupAdd 48
+#define RAP_WGroupDel 49
+#define RAP_WGroupAddUser 50
+#define RAP_WGroupDelUser 51
+#define RAP_WGroupGetUsers 52
+#define RAP_WUserEnum 53
+#define RAP_WUserAdd 54
+#define RAP_WUserDel 55
+#define RAP_WUserGetInfo 56
+#define RAP_WUserSetInfo 57
+#define RAP_WUserPasswordSet 58
+#define RAP_WUserGetGroups 59
+#define RAP_WWkstaSetUID 62
+#define RAP_WWkstaGetInfo 63
+#define RAP_WWkstaSetInfo 64
+#define RAP_WUseEnum 65
+#define RAP_WUseAdd 66
+#define RAP_WUseDel 67
+#define RAP_WUseGetInfo 68
+#define RAP_WPrintQEnum 69
+#define RAP_WPrintQGetInfo 70
+#define RAP_WPrintQSetInfo 71
+#define RAP_WPrintQAdd 72
+#define RAP_WPrintQDel 73
+#define RAP_WPrintQPause 74
+#define RAP_WPrintQContinue 75
+#define RAP_WPrintJobEnum 76
+#define RAP_WPrintJobGetInfo 77
+#define RAP_WPrintJobSetInfo_OLD 78
+#define RAP_WPrintJobDel 81
+#define RAP_WPrintJobPause 82
+#define RAP_WPrintJobContinue 83
+#define RAP_WPrintDestEnum 84
+#define RAP_WPrintDestGetInfo 85
+#define RAP_WPrintDestControl 86
+#define RAP_WProfileSave 87
+#define RAP_WProfileLoad 88
+#define RAP_WStatisticsGet 89
+#define RAP_WStatisticsClear 90
+#define RAP_NetRemoteTOD 91
+#define RAP_WNetBiosEnum 92
+#define RAP_WNetBiosGetInfo 93
+#define RAP_NetServerEnum 94
+#define RAP_I_NetServerEnum 95
+#define RAP_WServiceGetInfo 96
+#define RAP_WPrintQPurge 103
+#define RAP_NetServerEnum2 104
+#define RAP_WAccessGetUserPerms 105
+#define RAP_WGroupGetInfo 106
+#define RAP_WGroupSetInfo 107
+#define RAP_WGroupSetUsers 108
+#define RAP_WUserSetGroups 109
+#define RAP_WUserModalsGet 110
+#define RAP_WUserModalsSet 111
+#define RAP_WFileEnum2 112
+#define RAP_WUserAdd2 113
+#define RAP_WUserSetInfo2 114
+#define RAP_WUserPasswordSet2 115
+#define RAP_I_NetServerEnum2 116
+#define RAP_WConfigGet2 117
+#define RAP_WConfigGetAll2 118
+#define RAP_WGetDCName 119
+#define RAP_NetHandleGetInfo 120
+#define RAP_NetHandleSetInfo 121
+#define RAP_WStatisticsGet2 122
+#define RAP_WBuildGetInfo 123
+#define RAP_WFileGetInfo2 124
+#define RAP_WFileClose2 125
+#define RAP_WNetServerReqChallenge 126
+#define RAP_WNetServerAuthenticate 127
+#define RAP_WNetServerPasswordSet 128
+#define RAP_WNetAccountDeltas 129
+#define RAP_WNetAccountSync 130
+#define RAP_WUserEnum2 131
+#define RAP_WWkstaUserLogon 132
+#define RAP_WWkstaUserLogoff 133
+#define RAP_WLogonEnum 134
+#define RAP_WErrorLogRead 135
+#define RAP_NetPathType 136
+#define RAP_NetPathCanonicalize 137
+#define RAP_NetPathCompare 138
+#define RAP_NetNameValidate 139
+#define RAP_NetNameCanonicalize 140
+#define RAP_NetNameCompare 141
+#define RAP_WAuditRead 142
+#define RAP_WPrintDestAdd 143
+#define RAP_WPrintDestSetInfo 144
+#define RAP_WPrintDestDel 145
+#define RAP_WUserValidate2 146
+#define RAP_WPrintJobSetInfo 147
+#define RAP_TI_NetServerDiskEnum 148
+#define RAP_TI_NetServerDiskGetInfo 149
+#define RAP_TI_FTVerifyMirror 150
+#define RAP_TI_FTAbortVerify 151
+#define RAP_TI_FTGetInfo 152
+#define RAP_TI_FTSetInfo 153
+#define RAP_TI_FTLockDisk 154
+#define RAP_TI_FTFixError 155
+#define RAP_TI_FTAbortFix 156
+#define RAP_TI_FTDiagnoseError 157
+#define RAP_TI_FTGetDriveStats 158
+#define RAP_TI_FTErrorGetInfo 160
+#define RAP_NetAccessCheck 163
+#define RAP_NetAlertRaise 164
+#define RAP_NetAlertStart 165
+#define RAP_NetAlertStop 166
+#define RAP_NetAuditWrite 167
+#define RAP_NetIRemoteAPI 168
+#define RAP_NetServiceStatus 169
+#define RAP_NetServerRegister 170
+#define RAP_NetServerDeregister 171
+#define RAP_NetSessionEntryMake 172
+#define RAP_NetSessionEntryClear 173
+#define RAP_NetSessionEntryGetInfo 174
+#define RAP_NetSessionEntrySetInfo 175
+#define RAP_NetConnectionEntryMake 176
+#define RAP_NetConnectionEntryClear 177
+#define RAP_NetConnectionEntrySetInfo 178
+#define RAP_NetConnectionEntryGetInfo 179
+#define RAP_NetFileEntryMake 180
+#define RAP_NetFileEntryClear 181
+#define RAP_NetFileEntrySetInfo 182
+#define RAP_NetFileEntryGetInfo 183
+#define RAP_AltSrvMessageBufferSend 184
+#define RAP_AltSrvMessageFileSend 185
+#define RAP_wI_NetRplWkstaEnum 186
+#define RAP_wI_NetRplWkstaGetInfo 187
+#define RAP_wI_NetRplWkstaSetInfo 188
+#define RAP_wI_NetRplWkstaAdd 189
+#define RAP_wI_NetRplWkstaDel 190
+#define RAP_wI_NetRplProfileEnum 191
+#define RAP_wI_NetRplProfileGetInfo 192
+#define RAP_wI_NetRplProfileSetInfo 193
+#define RAP_wI_NetRplProfileAdd 194
+#define RAP_wI_NetRplProfileDel 195
+#define RAP_wI_NetRplProfileClone 196
+#define RAP_wI_NetRplBaseProfileEnum 197
+#define RAP_WIServerSetInfo 201
+#define RAP_WPrintDriverEnum 205
+#define RAP_WPrintQProcessorEnum 206
+#define RAP_WPrintPortEnum 207
+#define RAP_WNetWriteUpdateLog 208
+#define RAP_WNetAccountUpdate 209
+#define RAP_WNetAccountConfirmUpdate 210
+#define RAP_WConfigSet 211
+#define RAP_WAccountsReplicate 212
+#define RAP_SamOEMChgPasswordUser2_P 214
+#define RAP_NetServerEnum3 215
+#define RAP_WprintDriverGetInfo 250
+#define RAP_WprintDriverSetInfo 251
+#define RAP_WaliasAdd 252
+#define RAP_WaliasDel 253
+#define RAP_WaliasGetInfo 254
+#define RAP_WaliasSetInfo 255
+#define RAP_WaliasEnum 256
+#define RAP_WuserGetLogonAsn 257
+#define RAP_WuserSetLogonAsn 258
+#define RAP_WuserGetAppSel 259
+#define RAP_WuserSetAppSel 260
+#define RAP_WappAdd 261
+#define RAP_WappDel 262
+#define RAP_WappGetInfo 263
+#define RAP_WappSetInfo 264
+#define RAP_WappEnum 265
+#define RAP_WUserDCDBInit 266
+#define RAP_WDASDAdd 267
+#define RAP_WDASDDel 268
+#define RAP_WDASDGetInfo 269
+#define RAP_WDASDSetInfo 270
+#define RAP_WDASDEnum 271
+#define RAP_WDASDCheck 272
+#define RAP_WDASDCtl 273
+#define RAP_WuserRemoteLogonCheck 274
+#define RAP_WUserPasswordSet3 275
+#define RAP_WCreateRIPLMachine 276
+#define RAP_WDeleteRIPLMachine 277
+#define RAP_WGetRIPLMachineInfo 278
+#define RAP_WSetRIPLMachineInfo 279
+#define RAP_WEnumRIPLMachine 280
+#define RAP_I_ShareAdd 281
+#define RAP_AliasEnum 282
+#define RAP_WaccessApply 283
+#define RAP_WPrt16Query 284
+#define RAP_WPrt16Set 285
+#define RAP_WUserDel100 286
+#define RAP_WUserRemoteLogonCheck2 287
+#define RAP_WRemoteTODSet 294
+#define RAP_WprintJobMoveAll 295
+#define RAP_W16AppParmAdd 296
+#define RAP_W16AppParmDel 297
+#define RAP_W16AppParmGet 298
+#define RAP_W16AppParmSet 299
+#define RAP_W16RIPLMachineCreate 300
+#define RAP_W16RIPLMachineGetInfo 301
+#define RAP_W16RIPLMachineSetInfo 302
+#define RAP_W16RIPLMachineEnum 303
+#define RAP_W16RIPLMachineListParmEnum 304
+#define RAP_W16RIPLMachClassGetInfo 305
+#define RAP_W16RIPLMachClassEnum 306
+#define RAP_W16RIPLMachClassCreate 307
+#define RAP_W16RIPLMachClassSetInfo 308
+#define RAP_W16RIPLMachClassDelete 309
+#define RAP_W16RIPLMachClassLPEnum 310
+#define RAP_W16RIPLMachineDelete 311
+#define RAP_W16WSLevelGetInfo 312
+#define RAP_WserverNameAdd 313
+#define RAP_WserverNameDel 314
+#define RAP_WserverNameEnum 315
+#define RAP_I_WDASDEnum 316
+#define RAP_WDASDEnumTerminate 317
+#define RAP_WDASDSetInfo2 318
+#define MAX_API 318
+
+
+/* Parameter description strings for RAP calls */
+/* Names are defined name for RAP call with _REQ */
+/* appended to end. */
+
+#define RAP_WFileEnum2_REQ "zzWrLehb8g8"
+#define RAP_WFileGetInfo2_REQ "DWrLh"
+#define RAP_WFileClose2_REQ "D"
+
+#define RAP_NetGroupEnum_REQ "WrLeh"
+#define RAP_NetGroupAdd_REQ "WsT"
+#define RAP_NetGroupDel_REQ "z"
+#define RAP_NetGroupAddUser_REQ "zz"
+#define RAP_NetGroupDelUser_REQ "zz"
+#define RAP_NetGroupGetUsers_REQ "zWrLeh"
+#define RAP_NetGroupSetUsers_REQ "zWsTW"
+
+#define RAP_NetUserAdd2_REQ "WsTWW"
+#define RAP_NetUserEnum_REQ "WrLeh"
+#define RAP_NetUserEnum2_REQ "WrLDieh"
+#define RAP_NetUserGetGroups_REQ "zWrLeh"
+#define RAP_NetUserSetGroups_REQ "zWsTW"
+#define RAP_NetUserPasswordSet_REQ "zb16b16w"
+#define RAP_NetUserPasswordSet2_REQ "zb16b16WW"
+#define RAP_SAMOEMChgPasswordUser2_REQ "B516B16"
+#define RAP_NetUserValidate2_REQ "Wb62WWrLhWW"
+
+#define RAP_NetServerEnum2_REQ "WrLehDz"
+#define RAP_WserverGetInfo_REQ "WrLh"
+#define RAP_NetWkstatGetInfo "WrLh"
+
+#define RAP_WShareAdd_REQ "WsT"
+#define RAP_WShareEnum_REQ "WrLeh"
+#define RAP_WShareDel_REQ "zW"
+#define RAP_WWkstaGetInfo_REQ "WrLh"
+
+#define RAP_NetPrintQEnum_REQ "WrLeh"
+#define RAP_NetPrintQGetInfo_REQ "zWrLh"
+
+#define RAP_NetServerAdminCommand_REQ "zhrLeh"
+#define RAP_NetServiceEnum_REQ "WrLeh"
+#define RAP_NetServiceControl_REQ "zWWrL"
+#define RAP_NetServiceInstall_REQ "zF88sg88T"
+#define RAP_NetServiceGetInfo_REQ "zWrLh"
+#define RAP_NetSessionEnum_REQ "WrLeh"
+#define RAP_NetSessionGetInfo_REQ "zWrLh"
+#define RAP_NetSessionDel_REQ "zW"
+
+#define RAP_NetConnectionEnum_REQ "zWrLeh"
+
+#define RAP_NetWkstaUserLogoff_REQ "zzWb38WrLh"
+
+/* Description strings for returned data in RAP calls */
+/* I use all caps here in part to avoid accidental */
+/* name collisions */
+
+#define RAP_FILE_INFO_L2 "D"
+#define RAP_FILE_INFO_L3 "DWWzz"
+
+#define RAP_GROUP_INFO_L0 "B21"
+#define RAP_GROUP_INFO_L1 "B21Bz"
+#define RAP_GROUP_USERS_INFO_0 "B21"
+#define RAP_GROUP_USERS_INFO_1 "B21BN"
+
+#define RAP_USER_INFO_L0 "B21"
+#define RAP_USER_INFO_L1 "B21BB16DWzzWz"
+
+#define RAP_SERVER_INFO_L0 "B16"
+#define RAP_SERVER_INFO_L1 "B16BBDz"
+#define RAP_SERVER_INFO_L2 "B16BBDzDDDWWzWWWWWWWB21BzWWWWWWWWWWWWWWWWWWWWWWz"
+#define RAP_SERVER_INFO_L3 "B16BBDzDDDWWzWWWWWWWB21BzWWWWWWWWWWWWWWWWWWWWWWzDWz"
+#define RAP_SERVICE_INFO_L0 "B16"
+#define RAP_SERVICE_INFO_L2 "B16WDWB64"
+#define RAP_SHARE_INFO_L0 "B13"
+#define RAP_SHARE_INFO_L1 "B13BWz"
+#define RAP_SHARE_INFO_L2 "B13BWzWWWzB9B"
+
+#define RAP_PRINTQ_INFO_L2 "B13BWWWzzzzzWN"
+#define RAP_SMB_PRINT_JOB_L1 "WB21BB16B10zWWzDDz"
+
+#define RAP_SESSION_INFO_L2 "zzWWWDDDz"
+#define RAP_CONNECTION_INFO_L1 "WWWWDzz"
+
+#define RAP_USER_LOGOFF_INFO_L1 "WDW"
+
+#define RAP_WKSTA_INFO_L1 "WDzzzzBBDWDWWWWWWWWWWWWWWWWWWWzzWzzW"
+#define RAP_WKSTA_INFO_L10 "zzzBBzz"
+
+/* BB explicit packing would help in structs below */
+
+/* sizes of fixed-length fields, including null terminator */
+#define RAP_GROUPNAME_LEN 21
+#define RAP_USERNAME_LEN 21
+#define RAP_SHARENAME_LEN 13
+#define RAP_UPASSWD_LEN 16 /* user password */
+#define RAP_SPASSWD_LEN 9 /* share password */
+#define RAP_MACHNAME_LEN 16
+#define RAP_SRVCNAME_LEN 16
+#define RAP_SRVCCMNT_LEN 64
+#define RAP_DATATYPE_LEN 10
+
+
+typedef struct rap_group_info_1
+{
+ char group_name[RAP_GROUPNAME_LEN];
+ char reserved1;
+ char * comment;
+} RAP_GROUP_INFO_1;
+
+typedef struct rap_user_info_1
+{
+ char user_name[RAP_USERNAME_LEN];
+ char reserved1;
+ char passwrd[RAP_UPASSWD_LEN];
+ uint32 pwage;
+ uint16 priv;
+ char * home_dir;
+ char * comment;
+ uint16 userflags;
+ char * logon_script;
+} RAP_USER_INFO_1;
+
+typedef struct rap_service_info_2
+{
+ char service_name[RAP_SRVCNAME_LEN];
+ uint16 status;
+ uint32 installcode;
+ uint16 process_num;
+ char * comment;
+} RAP_SERVICE_INFO_2;
+
+
+typedef struct rap_share_info_0
+{
+ char share_name[RAP_SHARENAME_LEN];
+} RAP_SHARE_INFO_0;
+
+typedef struct rap_share_info_1
+{
+ char share_name[RAP_SHARENAME_LEN];
+ char reserved1;
+ uint16 share_type;
+ char * comment;
+} RAP_SHARE_INFO_1;
+
+typedef struct rap_share_info_2
+{
+ char share_name[RAP_SHARENAME_LEN];
+ char reserved1;
+ uint16 share_type;
+ char * comment;
+ uint16 perms;
+ uint16 maximum_users;
+ uint16 active_users;
+ char * path;
+ char password[RAP_SPASSWD_LEN];
+ char reserved2;
+} RAP_SHARE_INFO_2;
+
+#endif /* _RAP_H_ */
diff --git a/source/include/rpc_brs.h b/source/include/rpc_brs.h
new file mode 100644
index 00000000000..cd0928d470f
--- /dev/null
+++ b/source/include/rpc_brs.h
@@ -0,0 +1,80 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-1999
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1999
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _RPC_BRS_H /* _RPC_BRS_H */
+#define _RPC_BRS_H
+
+
+/* brssvc pipe */
+#define BRS_QUERY_INFO 0x02
+
+
+/* BRS_Q_QUERY_INFO - probably a capabilities request */
+typedef struct q_brs_query_info_info
+{
+ uint32 ptr_srv_name; /* pointer (to server name?) */
+ UNISTR2 uni_srv_name; /* unicode server name starting with '\\' */
+
+ uint16 switch_value1; /* info level 100 (0x64) */
+ /* align */
+ uint16 switch_value2; /* info level 100 (0x64) */
+
+ uint32 ptr;
+ uint32 pad1;
+ uint32 pad2;
+
+} BRS_Q_QUERY_INFO;
+
+
+/* BRS_INFO_100 - level 100 info */
+typedef struct brs_info_100_info
+{
+ uint32 pad1;
+ uint32 ptr2;
+ uint32 pad2;
+ uint32 pad3;
+
+} BRS_INFO_100;
+
+
+/* BRS_R_QUERY_INFO - probably a capabilities request */
+typedef struct r_brs_query_info_info
+{
+ uint16 switch_value1; /* 100 (0x64) - switch value */
+ /* align */
+ uint16 switch_value2; /* info level 100 (0x64) */
+
+ /* for now, only level 100 is supported. this should be an enum container */
+ uint32 ptr_1; /* pointer 1 */
+
+ union
+ {
+ BRS_INFO_100 *brs100; /* browser info level 100 */
+ void *id;
+
+ } info;
+
+ NTSTATUS status; /* return status */
+
+} BRS_R_QUERY_INFO;
+
+#endif /* _RPC_BRS_H */
+
diff --git a/source/include/rpc_client.h b/source/include/rpc_client.h
new file mode 100644
index 00000000000..bce9ec7f273
--- /dev/null
+++ b/source/include/rpc_client.h
@@ -0,0 +1,28 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Elrond 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _RPC_CLIENT_H
+#define _RPC_CLIENT_H
+
+#if 0 /* JERRY */
+#include "rpc_client_proto.h"
+#endif
+
+#endif /* _RPC_CLIENT_H */
diff --git a/source/include/rpc_creds.h b/source/include/rpc_creds.h
new file mode 100644
index 00000000000..3022b172899
--- /dev/null
+++ b/source/include/rpc_creds.h
@@ -0,0 +1,96 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-1999
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1999
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+#ifndef _RPC_CREDS_H /* _RPC_CREDS_H */
+#define _RPC_CREDS_H
+
+typedef struct ntuser_creds
+{
+ fstring user_name;
+ fstring domain;
+ struct pwd_info pwd;
+
+ uint32 ntlmssp_flags;
+
+} CREDS_NT;
+
+typedef struct unixuser_creds
+{
+ fstring user_name;
+ fstring requested_name;
+ fstring real_name;
+ BOOL guest;
+
+} CREDS_UNIX;
+
+typedef struct unixsec_creds
+{
+ uint32 uid;
+ uint32 gid;
+ int num_grps;
+ uint32 *grps;
+
+} CREDS_UNIX_SEC;
+
+typedef struct ntsec_creds
+{
+ DOM_SID sid;
+ uint32 num_grps;
+ uint32 *grp_rids;
+
+} CREDS_NT_SEC;
+
+typedef struct user_creds
+{
+ BOOL reuse;
+
+ uint32 ptr_ntc;
+ uint32 ptr_uxc;
+ uint32 ptr_nts;
+ uint32 ptr_uxs;
+ uint32 ptr_ssk;
+
+ CREDS_NT ntc;
+ CREDS_UNIX uxc;
+
+ CREDS_NT_SEC nts;
+ CREDS_UNIX_SEC uxs;
+
+ uchar usr_sess_key[16];
+
+} CREDS_HYBRID;
+
+typedef struct cred_command
+{
+ uint16 version;
+ uint16 command;
+ uint32 pid; /* unique process id */
+
+ fstring name;
+
+ uint32 ptr_creds;
+ CREDS_HYBRID *cred;
+
+} CREDS_CMD;
+
+#endif /* _RPC_CREDS_H */
+
diff --git a/source/include/rpc_dce.h b/source/include/rpc_dce.h
new file mode 100644
index 00000000000..8266fc861f1
--- /dev/null
+++ b/source/include/rpc_dce.h
@@ -0,0 +1,361 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+ Copyright (C) Paul Ashton 1997
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _DCE_RPC_H /* _DCE_RPC_H */
+#define _DCE_RPC_H
+
+#include "rpc_misc.h" /* this only pulls in STRHDR */
+
+
+/* DCE/RPC packet types */
+
+enum RPC_PKT_TYPE
+{
+ RPC_REQUEST = 0x00,
+ RPC_RESPONSE = 0x02,
+ RPC_FAULT = 0x03,
+ RPC_BIND = 0x0B,
+ RPC_BINDACK = 0x0C,
+ RPC_BINDNACK = 0x0D,
+ RPC_ALTCONT = 0x0E,
+ RPC_ALTCONTRESP = 0x0F,
+ RPC_BINDRESP = 0x10 /* not the real name! this is undocumented! */
+};
+
+/* DCE/RPC flags */
+#define RPC_FLG_FIRST 0x01
+#define RPC_FLG_LAST 0x02
+#define RPC_FLG_NOCALL 0x20
+
+#define SMBD_NTLMSSP_NEG_FLAGS 0x000082b1 /* ALWAYS_SIGN|NEG_NTLM|NEG_LM|NEG_SEAL|NEG_SIGN|NEG_UNICODE */
+
+/* NTLMSSP signature version */
+#define NTLMSSP_SIGN_VERSION 0x01
+
+/* NTLMSSP auth type */
+#define NTLMSSP_AUTH_TYPE 0xa
+
+/* DCE-RPC standard identifiers to indicate
+ signing or sealing of an RPC pipe */
+#define RPC_PIPE_AUTH_SIGN_LEVEL 0x5
+#define RPC_PIPE_AUTH_SEAL_LEVEL 0x6
+
+/* Netlogon schannel auth type and level */
+#define NETSEC_AUTH_TYPE 0x44
+#define NETSEC_SIGN_SIGNATURE { 0x77, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 }
+#define NETSEC_SEAL_SIGNATURE { 0x77, 0x00, 0x7a, 0x00, 0xff, 0xff, 0x00, 0x00 }
+#define RPC_AUTH_NETSEC_CHK_LEN 0x20
+
+/* The 7 here seems to be required to get Win2k not to downgrade us
+ to NT4. Actually, anything other than 1ff would seem to do... */
+#define NETLOGON_NEG_AUTH2_FLAGS 0x000701ff
+
+#define NETLOGON_NEG_SCHANNEL 0x40000000
+
+enum netsec_direction
+{
+ SENDER_IS_INITIATOR,
+ SENDER_IS_ACCEPTOR
+};
+
+/* Internal Flags to indicate what type of authentication on the pipe */
+#define AUTH_PIPE_SIGN 0x0001
+#define AUTH_PIPE_SEAL 0x0002
+#define AUTH_PIPE_NTLMSSP 0x0004
+#define AUTH_PIPE_NETSEC 0x0008
+
+/* Maximum PDU fragment size. */
+/* #define MAX_PDU_FRAG_LEN 0x1630 this is what wnt sets */
+#define MAX_PDU_FRAG_LEN 0x10b8 /* this is what w2k sets */
+
+/* RPC_IFACE */
+typedef struct rpc_iface_info
+{
+ struct uuid uuid; /* 16 bytes of rpc interface identification */
+ uint32 version; /* the interface version number */
+
+} RPC_IFACE;
+
+#define RPC_IFACE_LEN (UUID_SIZE + 4)
+
+struct pipe_id_info
+{
+ /* the names appear not to matter: the syntaxes _do_ matter */
+
+ const char *client_pipe;
+ RPC_IFACE abstr_syntax; /* this one is the abstract syntax id */
+
+ const char *server_pipe; /* this one is the secondary syntax name */
+ RPC_IFACE trans_syntax; /* this one is the primary syntax id */
+};
+
+/* RPC_HDR - dce rpc header */
+typedef struct rpc_hdr_info
+{
+ uint8 major; /* 5 - RPC major version */
+ uint8 minor; /* 0 - RPC minor version */
+ uint8 pkt_type; /* RPC_PKT_TYPE - RPC response packet */
+ uint8 flags; /* DCE/RPC flags */
+ uint8 pack_type[4]; /* 0x1000 0000 - little-endian packed data representation */
+ uint16 frag_len; /* fragment length - data size (bytes) inc header and tail. */
+ uint16 auth_len; /* 0 - authentication length */
+ uint32 call_id; /* call identifier. matches 12th uint32 of incoming RPC data. */
+
+} RPC_HDR;
+
+#define RPC_HEADER_LEN 16
+
+/* RPC_HDR_REQ - ms request rpc header */
+typedef struct rpc_hdr_req_info
+{
+ uint32 alloc_hint; /* allocation hint - data size (bytes) minus header and tail. */
+ uint16 context_id; /* presentation context identifier */
+ uint16 opnum; /* opnum */
+
+} RPC_HDR_REQ;
+
+#define RPC_HDR_REQ_LEN 8
+
+/* RPC_HDR_RESP - ms response rpc header */
+typedef struct rpc_hdr_resp_info
+{
+ uint32 alloc_hint; /* allocation hint - data size (bytes) minus header and tail. */
+ uint16 context_id; /* 0 - presentation context identifier */
+ uint8 cancel_count; /* 0 - cancel count */
+ uint8 reserved; /* 0 - reserved. */
+
+} RPC_HDR_RESP;
+
+#define RPC_HDR_RESP_LEN 8
+
+/* RPC_HDR_FAULT - fault rpc header */
+typedef struct rpc_hdr_fault_info
+{
+ NTSTATUS status;
+ uint32 reserved; /* 0x0000 0000 */
+} RPC_HDR_FAULT;
+
+#define RPC_HDR_FAULT_LEN 8
+
+/* this seems to be the same string name depending on the name of the pipe,
+ * but is more likely to be linked to the interface name
+ * "srvsvc", "\\PIPE\\ntsvcs"
+ * "samr", "\\PIPE\\lsass"
+ * "wkssvc", "\\PIPE\\wksvcs"
+ * "NETLOGON", "\\PIPE\\NETLOGON"
+ */
+/* RPC_ADDR_STR */
+typedef struct rpc_addr_info
+{
+ uint16 len; /* length of the string including null terminator */
+ fstring str; /* the string above in single byte, null terminated form */
+
+} RPC_ADDR_STR;
+
+/* RPC_HDR_BBA */
+typedef struct rpc_hdr_bba_info
+{
+ uint16 max_tsize; /* maximum transmission fragment size (0x1630) */
+ uint16 max_rsize; /* max receive fragment size (0x1630) */
+ uint32 assoc_gid; /* associated group id (0x0) */
+
+} RPC_HDR_BBA;
+
+#define RPC_HDR_BBA_LEN 8
+
+/* RPC_HDR_AUTHA */
+typedef struct rpc_hdr_autha_info
+{
+ uint16 max_tsize; /* maximum transmission fragment size (0x1630) */
+ uint16 max_rsize; /* max receive fragment size (0x1630) */
+
+ uint8 auth_type; /* 0x0a */
+ uint8 auth_level; /* 0x06 */
+ uint8 stub_type_len; /* don't know */
+ uint8 padding; /* padding */
+
+ uint32 unknown; /* 0x0014a0c0 */
+
+} RPC_HDR_AUTHA;
+
+#define RPC_HDR_AUTHA_LEN 12
+
+/* RPC_HDR_AUTH */
+typedef struct rpc_hdr_auth_info
+{
+ uint8 auth_type; /* 0x0a */
+ uint8 auth_level; /* 0x06 */
+ uint8 padding;
+ uint8 reserved; /* padding */
+
+ uint32 auth_context; /* pointer */
+
+} RPC_HDR_AUTH;
+
+#define RPC_HDR_AUTH_LEN 8
+
+/* this is TEMPORARILY coded up as a specific structure */
+/* this structure comes after the bind request */
+/* RPC_AUTH_NETSEC_NEG */
+typedef struct rpc_auth_netsec_neg_info
+{
+ uint32 type1; /* Always zero ? */
+ uint32 type2; /* Types 0x3 and 0x13 seen. Check AcquireSecurityContext() docs.... */
+ fstring domain; /* calling workstations's domain */
+ fstring myname; /* calling workstation's name */
+} RPC_AUTH_NETSEC_NEG;
+
+/* attached to the end of encrypted rpc requests and responses */
+/* RPC_AUTH_NETSEC_CHK */
+typedef struct rpc_auth_netsec_chk_info
+{
+ uint8 sig [8]; /* 77 00 7a 00 ff ff 00 00 */
+ uint8 packet_digest[8]; /* checksum over the packet, MD5'ed with session key */
+ uint8 seq_num[8]; /* verifier, seq num */
+ uint8 confounder[8]; /* random 8-byte nonce */
+} RPC_AUTH_NETSEC_CHK;
+
+struct netsec_auth_struct
+{
+ uchar sess_key[16];
+ uint32 seq_num;
+ int auth_flags;
+};
+
+/* RPC_BIND_REQ - ms req bind */
+typedef struct rpc_bind_req_info
+{
+ RPC_HDR_BBA bba;
+
+ uint32 num_elements; /* the number of elements (0x1) */
+ uint16 context_id; /* presentation context identifier (0x0) */
+ uint8 num_syntaxes; /* the number of syntaxes (has always been 1?)(0x1) */
+
+ RPC_IFACE abstract; /* num and vers. of interface client is using */
+ RPC_IFACE transfer; /* num and vers. of interface to use for replies */
+
+} RPC_HDR_RB;
+
+/*
+ * The following length is 8 bytes RPC_HDR_BBA_LEN, 8 bytes internals
+ * (with 3 bytes padding), + 2 x RPC_IFACE_LEN bytes for RPC_IFACE structs.
+ */
+
+#define RPC_HDR_RB_LEN (RPC_HDR_BBA_LEN + 8 + (2*RPC_IFACE_LEN))
+
+/* RPC_RESULTS - can only cope with one reason, right now... */
+typedef struct rpc_results_info
+{
+/* uint8[] # 4-byte alignment padding, against SMB header */
+
+ uint8 num_results; /* the number of results (0x01) */
+
+/* uint8[] # 4-byte alignment padding, against SMB header */
+
+ uint16 result; /* result (0x00 = accept) */
+ uint16 reason; /* reason (0x00 = no reason specified) */
+
+} RPC_RESULTS;
+
+/* RPC_HDR_BA */
+typedef struct rpc_hdr_ba_info
+{
+ RPC_HDR_BBA bba;
+
+ RPC_ADDR_STR addr ; /* the secondary address string, as described earlier */
+ RPC_RESULTS res ; /* results and reasons */
+ RPC_IFACE transfer; /* the transfer syntax from the request */
+
+} RPC_HDR_BA;
+
+/* RPC_AUTH_VERIFIER */
+typedef struct rpc_auth_verif_info
+{
+ fstring signature; /* "NTLMSSP".. Ok, not quite anymore */
+ uint32 msg_type; /* NTLMSSP_MESSAGE_TYPE (1,2,3) and 5 for schannel */
+
+} RPC_AUTH_VERIFIER;
+
+/* this is TEMPORARILY coded up as a specific structure */
+/* this structure comes after the bind request */
+/* RPC_AUTH_NTLMSSP_NEG */
+typedef struct rpc_auth_ntlmssp_neg_info
+{
+ uint32 neg_flgs; /* 0x0000 b2b3 */
+
+ STRHDR hdr_myname; /* offset is against START of this structure */
+ STRHDR hdr_domain; /* offset is against START of this structure */
+
+ fstring myname; /* calling workstation's name */
+ fstring domain; /* calling workstations's domain */
+
+} RPC_AUTH_NTLMSSP_NEG;
+
+/* this is TEMPORARILY coded up as a specific structure */
+/* this structure comes after the bind acknowledgement */
+/* RPC_AUTH_NTLMSSP_CHAL */
+typedef struct rpc_auth_ntlmssp_chal_info
+{
+ uint32 unknown_1; /* 0x0000 0000 */
+ uint32 unknown_2; /* 0x0000 0028 */
+ uint32 neg_flags; /* 0x0000 82b1 */
+
+ uint8 challenge[8]; /* ntlm challenge */
+ uint8 reserved [8]; /* zeros */
+
+} RPC_AUTH_NTLMSSP_CHAL;
+
+
+/* RPC_AUTH_NTLMSSP_RESP */
+typedef struct rpc_auth_ntlmssp_resp_info
+{
+ STRHDR hdr_lm_resp; /* 24 byte response */
+ STRHDR hdr_nt_resp; /* 24 byte response */
+ STRHDR hdr_domain;
+ STRHDR hdr_usr;
+ STRHDR hdr_wks;
+ STRHDR hdr_sess_key; /* NULL unless negotiated */
+ uint32 neg_flags; /* 0x0000 82b1 */
+
+ fstring sess_key;
+ fstring wks;
+ fstring user;
+ fstring domain;
+ fstring nt_resp;
+ fstring lm_resp;
+
+} RPC_AUTH_NTLMSSP_RESP;
+
+/* attached to the end of encrypted rpc requests and responses */
+/* RPC_AUTH_NTLMSSP_CHK */
+typedef struct rpc_auth_ntlmssp_chk_info
+{
+ uint32 ver; /* 0x0000 0001 */
+ uint32 reserved;
+ uint32 crc32; /* checksum using 0xEDB8 8320 as a polynomial */
+ uint32 seq_num;
+
+} RPC_AUTH_NTLMSSP_CHK;
+
+#define RPC_AUTH_NTLMSSP_CHK_LEN 16
+
+#endif /* _DCE_RPC_H */
diff --git a/source/include/rpc_dfs.h b/source/include/rpc_dfs.h
new file mode 100644
index 00000000000..39316a5d541
--- /dev/null
+++ b/source/include/rpc_dfs.h
@@ -0,0 +1,197 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba parameters and setup
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996 - 2000
+ Copyright (C) Shirish Kalele 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _RPC_DFS_H
+#define _RPC_DFS_H
+
+/* NETDFS pipe: calls */
+#define DFS_EXIST 0x00
+#define DFS_ADD 0x01
+#define DFS_REMOVE 0x02
+#define DFS_GET_INFO 0x04
+#define DFS_ENUM 0x05
+
+/* dfsadd flags */
+#define DFSFLAG_ADD_VOLUME 0x00000001
+#define DFSFLAG_RESTORE_VOLUME 0x00000002
+
+typedef struct dfs_q_dfs_exist
+{
+ uint32 dummy;
+}
+DFS_Q_DFS_EXIST;
+
+/* status == 1 if dfs exists. */
+typedef struct dfs_r_dfs_exist
+{
+ uint32 status; /* Not a WERROR or NTSTATUS code */
+}
+DFS_R_DFS_EXIST;
+
+typedef struct dfs_q_dfs_add
+{
+ uint32 ptr_DfsEntryPath;
+ UNISTR2 DfsEntryPath;
+ uint32 ptr_ServerName;
+ UNISTR2 ServerName;
+ uint32 ptr_ShareName;
+ UNISTR2 ShareName;
+ uint32 ptr_Comment;
+ UNISTR2 Comment;
+ uint32 Flags;
+}
+DFS_Q_DFS_ADD;
+
+typedef struct dfs_r_dfs_add
+{
+ WERROR status;
+}
+DFS_R_DFS_ADD;
+
+/********************************************/
+typedef struct dfs_q_dfs_remove
+{
+ UNISTR2 DfsEntryPath;
+ uint32 ptr_ServerName;
+ UNISTR2 ServerName;
+ uint32 ptr_ShareName;
+ UNISTR2 ShareName;
+}
+DFS_Q_DFS_REMOVE;
+
+typedef struct dfs_r_dfs_remove
+{
+ WERROR status;
+}
+DFS_R_DFS_REMOVE;
+
+/********************************************/
+typedef struct dfs_info_1
+{
+ uint32 ptr_entrypath;
+ UNISTR2 entrypath;
+}
+DFS_INFO_1;
+
+typedef struct dfs_info_2
+{
+ uint32 ptr_entrypath;
+ UNISTR2 entrypath;
+ uint32 ptr_comment;
+ UNISTR2 comment;
+ uint32 state;
+ uint32 num_storages;
+}
+DFS_INFO_2;
+
+typedef struct dfs_storage_info
+{
+ uint32 state;
+ uint32 ptr_servername;
+ UNISTR2 servername;
+ uint32 ptr_sharename;
+ UNISTR2 sharename;
+}
+DFS_STORAGE_INFO;
+
+typedef struct dfs_info_3
+{
+ uint32 ptr_entrypath;
+ UNISTR2 entrypath;
+ uint32 ptr_comment;
+ UNISTR2 comment;
+ uint32 state;
+ uint32 num_storages;
+ uint32 ptr_storages;
+ uint32 num_storage_infos;
+ DFS_STORAGE_INFO* storages;
+}
+DFS_INFO_3;
+
+typedef struct dfs_info_ctr
+{
+
+ uint32 switch_value;
+ uint32 num_entries;
+ uint32 ptr_dfs_ctr; /* pointer to dfs info union */
+ union
+ {
+ DFS_INFO_1 *info1;
+ DFS_INFO_2 *info2;
+ DFS_INFO_3 *info3;
+ } dfs;
+}
+DFS_INFO_CTR;
+
+typedef struct dfs_q_dfs_get_info
+{
+ UNISTR2 uni_path;
+
+ uint32 ptr_server;
+ UNISTR2 uni_server;
+
+ uint32 ptr_share;
+ UNISTR2 uni_share;
+
+ uint32 level;
+}
+DFS_Q_DFS_GET_INFO;
+
+typedef struct dfs_r_dfs_get_info
+{
+ uint32 level;
+ uint32 ptr_ctr;
+ DFS_INFO_CTR ctr;
+ WERROR status;
+}
+DFS_R_DFS_GET_INFO;
+
+typedef struct dfs_q_dfs_enum
+{
+ uint32 level;
+ uint32 maxpreflen;
+ uint32 ptr_buffer;
+ uint32 level2;
+ uint32 ptr_num_entries;
+ uint32 num_entries;
+ uint32 ptr_num_entries2;
+ uint32 num_entries2;
+ ENUM_HND reshnd;
+}
+DFS_Q_DFS_ENUM;
+
+typedef struct dfs_r_dfs_enum
+{
+ DFS_INFO_CTR *ctr;
+ uint32 ptr_buffer;
+ uint32 level;
+ uint32 level2;
+ uint32 ptr_num_entries;
+ uint32 num_entries;
+ uint32 ptr_num_entries2;
+ uint32 num_entries2;
+ ENUM_HND reshnd;
+ WERROR status;
+}
+DFS_R_DFS_ENUM;
+
+#endif
diff --git a/source/include/rpc_ds.h b/source/include/rpc_ds.h
new file mode 100644
index 00000000000..e06918730a4
--- /dev/null
+++ b/source/include/rpc_ds.h
@@ -0,0 +1,171 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Gerald Carter 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _RPC_DS_H /* _RPC_LSA_H */
+#define _RPC_DS_H
+
+#include "rpc_misc.h"
+
+
+/* Opcodes available on PIPE_LSARPC_DS */
+
+#define DS_GETPRIMDOMINFO 0x00
+#define DS_NOP 0xFF /* no op -- placeholder */
+
+/* Opcodes available on PIPE_NETLOGON */
+
+#define DS_ENUM_DOM_TRUSTS 0x28
+
+
+/* macros for RPC's */
+
+/* DSROLE_PRIMARY_DOMAIN_INFO_BASIC */
+
+/* flags */
+
+#define DSROLE_PRIMARY_DS_RUNNING 0x00000001
+#define DSROLE_PRIMARY_DS_MIXED_MODE 0x00000002
+#define DSROLE_UPGRADE_IN_PROGRESS 0x00000004
+#define DSROLE_PRIMARY_DOMAIN_GUID_PRESENT 0x01000000
+
+/* machine role */
+
+#define DSROLE_STANDALONE_SRV 2
+#define DSROLE_DOMAIN_MEMBER_SRV 3
+#define DSROLE_BDC 4
+#define DSROLE_PDC 5
+
+
+typedef struct
+{
+ uint16 machine_role;
+ uint16 unknown; /* 0x6173 -- maybe just alignment? */
+
+ uint32 flags;
+
+ uint32 netbios_ptr;
+ uint32 dnsname_ptr;
+ uint32 forestname_ptr;
+
+ struct uuid domain_guid;
+
+ UNISTR2 netbios_domain;
+
+ UNISTR2 dns_domain; /* our dns domain */
+ UNISTR2 forest_domain; /* root domain of the forest to which we belong */
+} DSROLE_PRIMARY_DOMAIN_INFO_BASIC;
+
+typedef struct
+{
+ DSROLE_PRIMARY_DOMAIN_INFO_BASIC *basic;
+} DS_DOMINFO_CTR;
+
+/* info levels for ds_getprimdominfo() */
+
+#define DsRolePrimaryDomainInfoBasic 1
+
+
+/* DS_Q_GETPRIMDOMINFO - DsGetPrimaryDomainInformation() request */
+typedef struct
+{
+ uint16 level;
+} DS_Q_GETPRIMDOMINFO;
+
+/* DS_R_GETPRIMDOMINFO - DsGetPrimaryDomainInformation() response */
+typedef struct
+{
+ uint32 ptr;
+
+ uint16 level;
+ uint16 unknown0; /* 0x455c -- maybe just alignment? */
+
+ DS_DOMINFO_CTR info;
+
+ NTSTATUS status;
+} DS_R_GETPRIMDOMINFO;
+
+typedef struct {
+ /* static portion of structure */
+ uint32 netbios_ptr;
+ uint32 dns_ptr;
+ uint32 flags;
+ uint32 parent_index;
+ uint32 trust_type;
+ uint32 trust_attributes;
+ uint32 sid_ptr;
+ struct uuid guid;
+
+ UNISTR2 netbios_domain;
+ UNISTR2 dns_domain;
+ DOM_SID2 sid;
+
+} DS_DOMAIN_TRUSTS;
+
+struct ds_domain_trust {
+ /* static portion of structure */
+ uint32 flags;
+ uint32 parent_index;
+ uint32 trust_type;
+ uint32 trust_attributes;
+ struct uuid guid;
+
+ DOM_SID sid;
+ char *netbios_domain;
+ char *dns_domain;
+};
+
+typedef struct {
+
+ uint32 ptr;
+ uint32 max_count;
+ DS_DOMAIN_TRUSTS *trusts;
+
+} DS_DOMAIN_TRUSTS_CTR;
+
+#define DS_DOMAIN_IN_FOREST 0x0001 /* domains in the forest to which
+ we belong; even different domain trees */
+#define DS_DOMAIN_DIRECT_OUTBOUND 0x0002 /* trusted domains */
+#define DS_DOMAIN_TREE_ROOT 0x0004 /* root of our forest; also available in
+ DsRoleGetPrimaryDomainInfo() */
+#define DS_DOMAIN_PRIMARY 0x0008 /* our domain */
+#define DS_DOMAIN_NATIVE_MODE 0x0010 /* native mode AD servers */
+#define DS_DOMAIN_DIRECT_INBOUND 0x0020 /* trusting domains */
+
+/* DS_Q_ENUM_DOM_TRUSTS - DsEnumerateDomainTrusts() request */
+typedef struct
+{
+ uint32 server_ptr;
+ UNISTR2 server;
+ uint32 flags;
+
+} DS_Q_ENUM_DOM_TRUSTS;
+
+/* DS_R_ENUM_DOM_TRUSTS - DsEnumerateDomainTrusts() response */
+typedef struct
+{
+ uint32 num_domains;
+ DS_DOMAIN_TRUSTS_CTR domains;
+
+ NTSTATUS status;
+
+} DS_R_ENUM_DOM_TRUSTS;
+
+
+#endif /* _RPC_DS_H */
diff --git a/source/include/rpc_echo.h b/source/include/rpc_echo.h
new file mode 100644
index 00000000000..6b4ea6abfba
--- /dev/null
+++ b/source/include/rpc_echo.h
@@ -0,0 +1,75 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Samba rpcecho definitions.
+
+ Copyright (C) Tim Potter 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _RPC_ECHO_H
+#define _RPC_ECHO_H
+
+#define ECHO_ADD_ONE 0x00
+#define ECHO_DATA 0x01
+#define ECHO_SINK_DATA 0x02
+#define ECHO_SOURCE_DATA 0x03
+
+typedef struct echo_q_add_one
+{
+ uint32 request;
+} ECHO_Q_ADD_ONE;
+
+typedef struct echo_r_add_one
+{
+ uint32 response;
+} ECHO_R_ADD_ONE;
+
+typedef struct echo_q_echo_data
+{
+ uint32 size;
+ char *data;
+} ECHO_Q_ECHO_DATA;
+
+typedef struct echo_r_echo_data
+{
+ uint32 size;
+ char *data;
+} ECHO_R_ECHO_DATA;
+
+typedef struct echo_q_source_data
+{
+ uint32 size;
+} ECHO_Q_SOURCE_DATA;
+
+typedef struct echo_r_source_data
+{
+ uint32 size;
+ char *data;
+} ECHO_R_SOURCE_DATA;
+
+typedef struct echo_q_sink_data
+{
+ uint32 size;
+ char *data;
+} ECHO_Q_SINK_DATA;
+
+typedef struct echo_r_sink_data
+{
+ int dummy; /* unused */
+} ECHO_R_SINK_DATA;
+
+#endif
diff --git a/source/include/rpc_epmapper.h b/source/include/rpc_epmapper.h
new file mode 100644
index 00000000000..bbca6ac1f28
--- /dev/null
+++ b/source/include/rpc_epmapper.h
@@ -0,0 +1,118 @@
+/*
+ Unix SMB/CIFS implementation.
+ Endpoint mapper data definitions
+ Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define EPM_HANDLE_LEN 20
+
+/* ordinal for the mapping interface */
+
+#define EPM_MAP_PIPE_NAME 0x03
+
+/* some of the different connection protocols and their IDs from Windows */
+
+#define EPM_FLOOR_UUID 0x0d /* floor contains UUID */
+#define EPM_FLOOR_RPC 0x0b /* tower is for connection-oriented rpc */
+#define EPM_FLOOR_TCP 0x07 /* floor contains tcp port number */
+#define EPM_FLOOR_IP 0x09 /* floor contains IP address */
+#define EPM_FLOOR_NMPIPES 0x0f /* floor contains remote named pipe name */
+#define EPM_FLOOR_LRPC 0x10 /* floor contains local named pipe name */
+#define EPM_FLOOR_NETBIOS 0x11 /* floor contains netbios address */
+#define EPM_FLOOR_NETBEUI 0x12 /* floor contains netbeui address */
+#define EPM_FLOOR_SOCKET 0x20
+
+#define EPM_PIPE_NM "epmapper"
+
+#define MAX_TOWERS 1
+
+typedef struct
+{
+ uint8 data[EPM_HANDLE_LEN];
+} EPM_HANDLE;
+
+typedef struct
+{
+ struct {
+ uint16 length;
+ uint8 protocol;
+ struct {
+ struct uuid uuid;
+ uint16 version;
+ } uuid;
+ } lhs;
+ struct {
+ uint16 length;
+ uint16 unknown;
+ struct {
+ uint16 port;
+ } tcp;
+ struct {
+ uint8 addr[4];
+ } ip;
+ char string[MAXHOSTNAMELEN+3]; /* hostname + \\ + null term */
+ } rhs;
+} EPM_FLOOR;
+
+typedef struct
+{
+ uint32 max_length;
+ uint32 length;
+ uint16 num_floors;
+ EPM_FLOOR *floors;
+ uint8 unknown;
+} EPM_TOWER;
+
+typedef struct
+{
+ EPM_HANDLE handle;
+ uint32 tower_ref_id;
+ EPM_TOWER *tower;
+ EPM_HANDLE term_handle; /* in/out */
+ uint32 max_towers;
+} EPM_Q_MAP;
+
+typedef struct
+{
+ uint32 max_count;
+ uint32 offset;
+ uint32 count;
+ uint32 *tower_ref_ids;
+ EPM_TOWER *towers;
+} EPM_TOWER_ARRAY;
+
+typedef struct
+{
+ EPM_HANDLE handle;
+ uint32 num_results;
+ EPM_TOWER_ARRAY *results;
+ uint32 status;
+} EPM_R_MAP;
+
+
+/* port mapping entries to be read */
+
+typedef struct _mapper_entries{
+ uint8 protocol ;
+ RPC_IFACE uuid_info ; /* needs to be zeroed if no specific uuid */
+ uint16 port ;
+ char pipe_name[40] ;
+ char srv_name[20] ;
+ uint8 srv_port[4] ;
+ char func_name[16][16]; /* array of up to 16 functions available */
+} mapper_entries;
+
diff --git a/source/include/rpc_lsa.h b/source/include/rpc_lsa.h
new file mode 100644
index 00000000000..29a9cd7306b
--- /dev/null
+++ b/source/include/rpc_lsa.h
@@ -0,0 +1,744 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+ Copyright (C) Paul Ashton 1997
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _RPC_LSA_H /* _RPC_LSA_H */
+#define _RPC_LSA_H
+
+#include "rpc_misc.h"
+
+/* Opcodes available on PIPE_LSARPC */
+
+#if 0 /* UNIMPLEMENTED */
+
+#define LSA_LOOKUPSIDS2 0x39
+
+#endif
+
+#define LSA_CLOSE 0x00
+#define LSA_DELETE 0x01
+#define LSA_ENUM_PRIVS 0x02
+#define LSA_QUERYSECOBJ 0x03
+#define LSA_SETSECOBJ 0x04
+#define LSA_CHANGEPASSWORD 0x05
+#define LSA_OPENPOLICY 0x06
+#define LSA_QUERYINFOPOLICY 0x07
+#define LSA_SETINFOPOLICY 0x08
+#define LSA_CLEARAUDITLOG 0x09
+#define LSA_CREATEACCOUNT 0x0a
+#define LSA_ENUM_ACCOUNTS 0x0b
+#define LSA_CREATETRUSTDOM 0x0c
+#define LSA_ENUMTRUSTDOM 0x0d
+#define LSA_LOOKUPNAMES 0x0e
+#define LSA_LOOKUPSIDS 0x0f
+#define LSA_CREATESECRET 0x10
+#define LSA_OPENACCOUNT 0x11
+#define LSA_ENUMPRIVSACCOUNT 0x12
+#define LSA_ADDPRIVS 0x13
+#define LSA_REMOVEPRIVS 0x14
+#define LSA_GETQUOTAS 0x15
+#define LSA_SETQUOTAS 0x16
+#define LSA_GETSYSTEMACCOUNT 0x17
+#define LSA_SETSYSTEMACCOUNT 0x18
+#define LSA_OPENTRUSTDOM 0x19
+#define LSA_QUERYTRUSTDOM 0x1a
+#define LSA_SETINFOTRUSTDOM 0x1b
+#define LSA_OPENSECRET 0x1c
+#define LSA_SETSECRET 0x1d
+#define LSA_QUERYSECRET 0x1e
+#define LSA_LOOKUPPRIVVALUE 0x1f
+#define LSA_LOOKUPPRIVNAME 0x20
+#define LSA_PRIV_GET_DISPNAME 0x21
+#define LSA_DELETEOBJECT 0x22
+#define LSA_ENUMACCTWITHRIGHT 0x23
+#define LSA_ENUMACCTRIGHTS 0x24
+#define LSA_ADDACCTRIGHTS 0x25
+#define LSA_REMOVEACCTRIGHTS 0x26
+#define LSA_QUERYTRUSTDOMINFO 0x27
+#define LSA_SETTRUSTDOMINFO 0x28
+#define LSA_DELETETRUSTDOM 0x29
+#define LSA_STOREPRIVDATA 0x2a
+#define LSA_RETRPRIVDATA 0x2b
+#define LSA_OPENPOLICY2 0x2c
+#define LSA_UNK_GET_CONNUSER 0x2d /* LsaGetConnectedCredentials ? */
+#define LSA_QUERYINFO2 0x2e
+
+/* XXXX these are here to get a compile! */
+#define LSA_LOOKUPRIDS 0xFD
+
+/* DOM_QUERY - info class 3 and 5 LSA Query response */
+typedef struct dom_query_info
+{
+ uint16 uni_dom_max_len; /* domain name string length * 2 */
+ uint16 uni_dom_str_len; /* domain name string length * 2 */
+ uint32 buffer_dom_name; /* undocumented domain name string buffer pointer */
+ uint32 buffer_dom_sid; /* undocumented domain SID string buffer pointer */
+ UNISTR2 uni_domain_name; /* domain name (unicode string) */
+ DOM_SID2 dom_sid; /* domain SID */
+
+} DOM_QUERY;
+
+/* level 5 is same as level 3. */
+typedef DOM_QUERY DOM_QUERY_3;
+typedef DOM_QUERY DOM_QUERY_5;
+
+/* level 2 is auditing settings */
+typedef struct dom_query_2
+{
+ uint32 auditing_enabled;
+ uint32 count1; /* usualy 7, at least on nt4sp4 */
+ uint32 count2; /* the same */
+ uint32 *auditsettings;
+} DOM_QUERY_2;
+
+/* level 6 is server role information */
+typedef struct dom_query_6
+{
+ uint16 server_role; /* 2=backup, 3=primary */
+} DOM_QUERY_6;
+
+typedef struct seq_qos_info
+{
+ uint32 len; /* 12 */
+ uint16 sec_imp_level; /* 0x02 - impersonation level */
+ uint8 sec_ctxt_mode; /* 0x01 - context tracking mode */
+ uint8 effective_only; /* 0x00 - effective only */
+
+} LSA_SEC_QOS;
+
+typedef struct obj_attr_info
+{
+ uint32 len; /* 0x18 - length (in bytes) inc. the length field. */
+ uint32 ptr_root_dir; /* 0 - root directory (pointer) */
+ uint32 ptr_obj_name; /* 0 - object name (pointer) */
+ uint32 attributes; /* 0 - attributes (undocumented) */
+ uint32 ptr_sec_desc; /* 0 - security descriptior (pointer) */
+ uint32 ptr_sec_qos; /* security quality of service */
+ LSA_SEC_QOS *sec_qos;
+
+} LSA_OBJ_ATTR;
+
+/* LSA_Q_OPEN_POL - LSA Query Open Policy */
+typedef struct lsa_q_open_pol_info
+{
+ uint32 ptr; /* undocumented buffer pointer */
+ uint16 system_name; /* 0x5c - system name */
+ LSA_OBJ_ATTR attr ; /* object attributes */
+
+ uint32 des_access; /* desired access attributes */
+
+} LSA_Q_OPEN_POL;
+
+/* LSA_R_OPEN_POL - response to LSA Open Policy */
+typedef struct lsa_r_open_pol_info
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status; /* return code */
+
+} LSA_R_OPEN_POL;
+
+/* LSA_Q_OPEN_POL2 - LSA Query Open Policy */
+typedef struct lsa_q_open_pol2_info
+{
+ uint32 ptr; /* undocumented buffer pointer */
+ UNISTR2 uni_server_name; /* server name, starting with two '\'s */
+ LSA_OBJ_ATTR attr ; /* object attributes */
+
+ uint32 des_access; /* desired access attributes */
+
+} LSA_Q_OPEN_POL2;
+
+/* LSA_R_OPEN_POL2 - response to LSA Open Policy */
+typedef struct lsa_r_open_pol2_info
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status; /* return code */
+
+} LSA_R_OPEN_POL2;
+
+
+#define POLICY_VIEW_LOCAL_INFORMATION 0x00000001
+#define POLICY_VIEW_AUDIT_INFORMATION 0x00000002
+#define POLICY_GET_PRIVATE_INFORMATION 0x00000004
+#define POLICY_TRUST_ADMIN 0x00000008
+#define POLICY_CREATE_ACCOUNT 0x00000010
+#define POLICY_CREATE_SECRET 0x00000020
+#define POLICY_CREATE_PRIVILEGE 0x00000040
+#define POLICY_SET_DEFAULT_QUOTA_LIMITS 0x00000080
+#define POLICY_SET_AUDIT_REQUIREMENTS 0x00000100
+#define POLICY_AUDIT_LOG_ADMIN 0x00000200
+#define POLICY_SERVER_ADMIN 0x00000400
+#define POLICY_LOOKUP_NAMES 0x00000800
+
+#define POLICY_ALL_ACCESS ( STANDARD_RIGHTS_REQUIRED_ACCESS |\
+ POLICY_VIEW_LOCAL_INFORMATION |\
+ POLICY_VIEW_AUDIT_INFORMATION |\
+ POLICY_GET_PRIVATE_INFORMATION |\
+ POLICY_TRUST_ADMIN |\
+ POLICY_CREATE_ACCOUNT |\
+ POLICY_CREATE_SECRET |\
+ POLICY_CREATE_PRIVILEGE |\
+ POLICY_SET_DEFAULT_QUOTA_LIMITS |\
+ POLICY_SET_AUDIT_REQUIREMENTS |\
+ POLICY_AUDIT_LOG_ADMIN |\
+ POLICY_SERVER_ADMIN |\
+ POLICY_LOOKUP_NAMES )
+
+
+#define POLICY_READ ( STANDARD_RIGHTS_READ_ACCESS |\
+ POLICY_VIEW_AUDIT_INFORMATION |\
+ POLICY_GET_PRIVATE_INFORMATION)
+
+#define POLICY_WRITE ( STANDARD_RIGHTS_WRITE_ACCESS |\
+ POLICY_TRUST_ADMIN |\
+ POLICY_CREATE_ACCOUNT |\
+ POLICY_CREATE_SECRET |\
+ POLICY_CREATE_PRIVILEGE |\
+ POLICY_SET_DEFAULT_QUOTA_LIMITS |\
+ POLICY_SET_AUDIT_REQUIREMENTS |\
+ POLICY_AUDIT_LOG_ADMIN |\
+ POLICY_SERVER_ADMIN)
+
+#define POLICY_EXECUTE ( STANDARD_RIGHTS_EXECUTE_ACCESS |\
+ POLICY_VIEW_LOCAL_INFORMATION |\
+ POLICY_LOOKUP_NAMES )
+
+/* LSA_Q_QUERY_SEC_OBJ - LSA query security */
+typedef struct lsa_query_sec_obj_info
+{
+ POLICY_HND pol; /* policy handle */
+ uint32 sec_info;
+
+} LSA_Q_QUERY_SEC_OBJ;
+
+/* LSA_R_QUERY_SEC_OBJ - probably an open */
+typedef struct r_lsa_query_sec_obj_info
+{
+ uint32 ptr;
+ SEC_DESC_BUF *buf;
+
+ NTSTATUS status; /* return status */
+
+} LSA_R_QUERY_SEC_OBJ;
+
+/* LSA_Q_QUERY_INFO - LSA query info policy */
+typedef struct lsa_query_info
+{
+ POLICY_HND pol; /* policy handle */
+ uint16 info_class; /* info class */
+
+} LSA_Q_QUERY_INFO;
+
+/* LSA_INFO_UNION */
+typedef union lsa_info_union
+{
+ DOM_QUERY_2 id2;
+ DOM_QUERY_3 id3;
+ DOM_QUERY_5 id5;
+ DOM_QUERY_6 id6;
+} LSA_INFO_UNION;
+
+/* LSA_R_QUERY_INFO - response to LSA query info policy */
+typedef struct lsa_r_query_info
+{
+ uint32 undoc_buffer; /* undocumented buffer pointer */
+ uint16 info_class; /* info class (same as info class in request) */
+
+ LSA_INFO_UNION dom;
+
+ NTSTATUS status; /* return code */
+
+} LSA_R_QUERY_INFO;
+
+/* LSA_DNS_DOM_INFO - DNS domain info - info class 12*/
+typedef struct lsa_dns_dom_info
+{
+ UNIHDR hdr_nb_dom_name; /* netbios domain name */
+ UNIHDR hdr_dns_dom_name;
+ UNIHDR hdr_forest_name;
+
+ struct uuid dom_guid; /* domain GUID */
+
+ UNISTR2 uni_nb_dom_name;
+ UNISTR2 uni_dns_dom_name;
+ UNISTR2 uni_forest_name;
+
+ uint32 ptr_dom_sid;
+ DOM_SID2 dom_sid; /* domain SID */
+} LSA_DNS_DOM_INFO;
+
+typedef union lsa_info2_union
+{
+ LSA_DNS_DOM_INFO dns_dom_info;
+} LSA_INFO2_UNION;
+
+/* LSA_Q_QUERY_INFO2 - LSA query info */
+typedef struct lsa_q_query_info2
+{
+ POLICY_HND pol; /* policy handle */
+ uint16 info_class; /* info class */
+} LSA_Q_QUERY_INFO2;
+
+typedef struct lsa_r_query_info2
+{
+ uint32 ptr; /* pointer to info struct */
+ uint16 info_class;
+ LSA_INFO2_UNION info; /* so far the only one */
+ NTSTATUS status;
+} LSA_R_QUERY_INFO2;
+
+/* LSA_Q_ENUM_TRUST_DOM - LSA enumerate trusted domains */
+typedef struct lsa_enum_trust_dom_info
+{
+ POLICY_HND pol; /* policy handle */
+ uint32 enum_context; /* enumeration context handle */
+ uint32 preferred_len; /* preferred maximum length */
+
+} LSA_Q_ENUM_TRUST_DOM;
+
+/* LSA_R_ENUM_TRUST_DOM - response to LSA enumerate trusted domains */
+typedef struct lsa_r_enum_trust_dom_info
+{
+ uint32 enum_context; /* enumeration context handle */
+ uint32 num_domains; /* number of domains */
+ uint32 ptr_enum_domains; /* buffer pointer to num domains */
+
+ /* this lot is only added if ptr_enum_domains is non-NULL */
+ uint32 num_domains2; /* number of domains */
+ UNIHDR2 *hdr_domain_name;
+ UNISTR2 *uni_domain_name;
+ DOM_SID2 *domain_sid;
+
+ NTSTATUS status; /* return code */
+
+} LSA_R_ENUM_TRUST_DOM;
+
+/* LSA_Q_CLOSE */
+typedef struct lsa_q_close_info
+{
+ POLICY_HND pol; /* policy handle */
+
+} LSA_Q_CLOSE;
+
+/* LSA_R_CLOSE */
+typedef struct lsa_r_close_info
+{
+ POLICY_HND pol; /* policy handle. should be all zeros. */
+
+ NTSTATUS status; /* return code */
+
+} LSA_R_CLOSE;
+
+
+#define MAX_REF_DOMAINS 32
+
+/* DOM_TRUST_HDR */
+typedef struct dom_trust_hdr
+{
+ UNIHDR hdr_dom_name; /* referenced domain unicode string headers */
+ uint32 ptr_dom_sid;
+
+} DOM_TRUST_HDR;
+
+/* DOM_TRUST_INFO */
+typedef struct dom_trust_info
+{
+ UNISTR2 uni_dom_name; /* domain name unicode string */
+ DOM_SID2 ref_dom ; /* referenced domain SID */
+
+} DOM_TRUST_INFO;
+
+/* DOM_R_REF */
+typedef struct dom_ref_info
+{
+ uint32 num_ref_doms_1; /* num referenced domains */
+ uint32 ptr_ref_dom; /* pointer to referenced domains */
+ uint32 max_entries; /* 32 - max number of entries */
+ uint32 num_ref_doms_2; /* num referenced domains */
+
+ DOM_TRUST_HDR hdr_ref_dom[MAX_REF_DOMAINS]; /* referenced domains */
+ DOM_TRUST_INFO ref_dom [MAX_REF_DOMAINS]; /* referenced domains */
+
+} DOM_R_REF;
+
+/* the domain_idx points to a SID associated with the name */
+
+/* LSA_TRANS_NAME - translated name */
+typedef struct lsa_trans_name_info
+{
+ uint16 sid_name_use; /* value is 5 for a well-known group; 2 for a domain group; 1 for a user... */
+ UNIHDR hdr_name;
+ uint32 domain_idx; /* index into DOM_R_REF array of SIDs */
+
+} LSA_TRANS_NAME;
+
+/* This number purly arbitary - just to prevent a client from requesting large amounts of memory */
+#define MAX_LOOKUP_SIDS 256
+
+/* LSA_TRANS_NAME_ENUM - LSA Translated Name Enumeration container */
+typedef struct lsa_trans_name_enum_info
+{
+ uint32 num_entries;
+ uint32 ptr_trans_names;
+ uint32 num_entries2;
+
+ LSA_TRANS_NAME *name; /* translated names */
+ UNISTR2 *uni_name;
+
+} LSA_TRANS_NAME_ENUM;
+
+/* LSA_SID_ENUM - LSA SID enumeration container */
+typedef struct lsa_sid_enum_info
+{
+ uint32 num_entries;
+ uint32 ptr_sid_enum;
+ uint32 num_entries2;
+
+ uint32 *ptr_sid; /* domain SID pointers to be looked up. */
+ DOM_SID2 *sid; /* domain SIDs to be looked up. */
+
+} LSA_SID_ENUM;
+
+/* LSA_Q_LOOKUP_SIDS - LSA Lookup SIDs */
+typedef struct lsa_q_lookup_sids
+{
+ POLICY_HND pol; /* policy handle */
+ LSA_SID_ENUM sids;
+ LSA_TRANS_NAME_ENUM names;
+ LOOKUP_LEVEL level;
+ uint32 mapped_count;
+
+} LSA_Q_LOOKUP_SIDS;
+
+/* LSA_R_LOOKUP_SIDS - response to LSA Lookup SIDs */
+typedef struct lsa_r_lookup_sids
+{
+ uint32 ptr_dom_ref;
+ DOM_R_REF *dom_ref; /* domain reference info */
+
+ LSA_TRANS_NAME_ENUM *names;
+ uint32 mapped_count;
+
+ NTSTATUS status; /* return code */
+
+} LSA_R_LOOKUP_SIDS;
+
+/* LSA_Q_LOOKUP_NAMES - LSA Lookup NAMEs */
+typedef struct lsa_q_lookup_names
+{
+ POLICY_HND pol; /* policy handle */
+ uint32 num_entries;
+ uint32 num_entries2;
+ UNIHDR *hdr_name; /* name buffer pointers */
+ UNISTR2 *uni_name; /* names to be looked up */
+
+ uint32 num_trans_entries;
+ uint32 ptr_trans_sids; /* undocumented domain SID buffer pointer */
+ uint32 lookup_level;
+ uint32 mapped_count;
+
+} LSA_Q_LOOKUP_NAMES;
+
+/* LSA_R_LOOKUP_NAMES - response to LSA Lookup NAMEs by name */
+typedef struct lsa_r_lookup_names
+{
+ uint32 ptr_dom_ref;
+ DOM_R_REF *dom_ref; /* domain reference info */
+
+ uint32 num_entries;
+ uint32 ptr_entries;
+ uint32 num_entries2;
+ DOM_RID2 *dom_rid; /* domain RIDs being looked up */
+
+ uint32 mapped_count;
+
+ NTSTATUS status; /* return code */
+} LSA_R_LOOKUP_NAMES;
+
+/* This is probably a policy handle but at the moment we
+ never read it - so use a dummy struct. */
+
+typedef struct lsa_q_open_secret
+{
+ uint32 dummy;
+} LSA_Q_OPEN_SECRET;
+
+/* We always return "not found" at present - so just marshal the minimum. */
+
+typedef struct lsa_r_open_secret
+{
+ uint32 dummy1;
+ uint32 dummy2;
+ uint32 dummy3;
+ uint32 dummy4;
+ NTSTATUS status;
+} LSA_R_OPEN_SECRET;
+
+typedef struct lsa_enum_priv_entry
+{
+ UNIHDR hdr_name;
+ uint32 luid_low;
+ uint32 luid_high;
+ UNISTR2 name;
+
+} LSA_PRIV_ENTRY;
+
+/* LSA_Q_ENUM_PRIVS - LSA enum privileges */
+typedef struct lsa_q_enum_privs
+{
+ POLICY_HND pol; /* policy handle */
+ uint32 enum_context;
+ uint32 pref_max_length;
+} LSA_Q_ENUM_PRIVS;
+
+typedef struct lsa_r_enum_privs
+{
+ uint32 enum_context;
+ uint32 count;
+ uint32 ptr;
+ uint32 count1;
+
+ LSA_PRIV_ENTRY *privs;
+
+ NTSTATUS status;
+} LSA_R_ENUM_PRIVS;
+
+/* LSA_Q_ENUM_ACCT_RIGHTS - LSA enum account rights */
+typedef struct
+{
+ POLICY_HND pol; /* policy handle */
+ DOM_SID2 sid;
+} LSA_Q_ENUM_ACCT_RIGHTS;
+
+/* LSA_R_ENUM_ACCT_RIGHTS - LSA enum account rights */
+typedef struct
+{
+ uint32 count;
+ UNISTR2_ARRAY rights;
+ NTSTATUS status;
+} LSA_R_ENUM_ACCT_RIGHTS;
+
+
+/* LSA_Q_ADD_ACCT_RIGHTS - LSA add account rights */
+typedef struct
+{
+ POLICY_HND pol; /* policy handle */
+ DOM_SID2 sid;
+ UNISTR2_ARRAY rights;
+ uint32 count;
+} LSA_Q_ADD_ACCT_RIGHTS;
+
+/* LSA_R_ADD_ACCT_RIGHTS - LSA add account rights */
+typedef struct
+{
+ NTSTATUS status;
+} LSA_R_ADD_ACCT_RIGHTS;
+
+
+/* LSA_Q_REMOVE_ACCT_RIGHTS - LSA remove account rights */
+typedef struct
+{
+ POLICY_HND pol; /* policy handle */
+ DOM_SID2 sid;
+ uint32 removeall;
+ UNISTR2_ARRAY rights;
+ uint32 count;
+} LSA_Q_REMOVE_ACCT_RIGHTS;
+
+/* LSA_R_REMOVE_ACCT_RIGHTS - LSA remove account rights */
+typedef struct
+{
+ NTSTATUS status;
+} LSA_R_REMOVE_ACCT_RIGHTS;
+
+
+/* LSA_Q_PRIV_GET_DISPNAME - LSA get privilege display name */
+typedef struct lsa_q_priv_get_dispname
+{
+ POLICY_HND pol; /* policy handle */
+ UNIHDR hdr_name;
+ UNISTR2 name;
+ uint16 lang_id;
+ uint16 lang_id_sys;
+} LSA_Q_PRIV_GET_DISPNAME;
+
+typedef struct lsa_r_priv_get_dispname
+{
+ uint32 ptr_info;
+ UNIHDR hdr_desc;
+ UNISTR2 desc;
+ /* Don't align ! */
+ uint16 lang_id;
+ /* align */
+ NTSTATUS status;
+} LSA_R_PRIV_GET_DISPNAME;
+
+/* LSA_Q_ENUM_ACCOUNTS */
+typedef struct lsa_q_enum_accounts
+{
+ POLICY_HND pol; /* policy handle */
+ uint32 enum_context;
+ uint32 pref_max_length;
+} LSA_Q_ENUM_ACCOUNTS;
+
+/* LSA_R_ENUM_ACCOUNTS */
+typedef struct lsa_r_enum_accounts
+{
+ uint32 enum_context;
+ LSA_SID_ENUM sids;
+ NTSTATUS status;
+} LSA_R_ENUM_ACCOUNTS;
+
+/* LSA_Q_UNK_GET_CONNUSER - gets username\domain of connected user
+ called when "Take Ownership" is clicked -SK */
+typedef struct lsa_q_unk_get_connuser
+{
+ uint32 ptr_srvname;
+ UNISTR2 uni2_srvname;
+ uint32 unk1; /* 3 unknown uint32's are seen right after uni2_srvname */
+ uint32 unk2; /* unk2 appears to be a ptr, unk1 = unk3 = 0 usually */
+ uint32 unk3;
+} LSA_Q_UNK_GET_CONNUSER;
+
+/* LSA_R_UNK_GET_CONNUSER */
+typedef struct lsa_r_unk_get_connuser
+{
+ uint32 ptr_user_name;
+ UNIHDR hdr_user_name;
+ UNISTR2 uni2_user_name;
+
+ uint32 unk1;
+
+ uint32 ptr_dom_name;
+ UNIHDR hdr_dom_name;
+ UNISTR2 uni2_dom_name;
+
+ NTSTATUS status;
+} LSA_R_UNK_GET_CONNUSER;
+
+
+typedef struct lsa_q_createaccount
+{
+ POLICY_HND pol; /* policy handle */
+ DOM_SID2 sid;
+ uint32 access; /* access */
+} LSA_Q_CREATEACCOUNT;
+
+typedef struct lsa_r_createaccount
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status;
+} LSA_R_CREATEACCOUNT;
+
+
+typedef struct lsa_q_openaccount
+{
+ POLICY_HND pol; /* policy handle */
+ DOM_SID2 sid;
+ uint32 access; /* desired access */
+} LSA_Q_OPENACCOUNT;
+
+typedef struct lsa_r_openaccount
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status;
+} LSA_R_OPENACCOUNT;
+
+typedef struct lsa_q_enumprivsaccount
+{
+ POLICY_HND pol; /* policy handle */
+} LSA_Q_ENUMPRIVSACCOUNT;
+
+typedef struct lsa_r_enumprivsaccount
+{
+ uint32 ptr;
+ uint32 count;
+ PRIVILEGE_SET *set;
+ NTSTATUS status;
+} LSA_R_ENUMPRIVSACCOUNT;
+
+typedef struct lsa_q_getsystemaccount
+{
+ POLICY_HND pol; /* policy handle */
+} LSA_Q_GETSYSTEMACCOUNT;
+
+typedef struct lsa_r_getsystemaccount
+{
+ uint32 access;
+ NTSTATUS status;
+} LSA_R_GETSYSTEMACCOUNT;
+
+
+typedef struct lsa_q_setsystemaccount
+{
+ POLICY_HND pol; /* policy handle */
+ uint32 access;
+} LSA_Q_SETSYSTEMACCOUNT;
+
+typedef struct lsa_r_setsystemaccount
+{
+ NTSTATUS status;
+} LSA_R_SETSYSTEMACCOUNT;
+
+
+typedef struct lsa_q_lookupprivvalue
+{
+ POLICY_HND pol; /* policy handle */
+ UNIHDR hdr_right;
+ UNISTR2 uni2_right;
+} LSA_Q_LOOKUPPRIVVALUE;
+
+typedef struct lsa_r_lookupprivvalue
+{
+ LUID luid;
+ NTSTATUS status;
+} LSA_R_LOOKUPPRIVVALUE;
+
+
+typedef struct lsa_q_addprivs
+{
+ POLICY_HND pol; /* policy handle */
+ uint32 count;
+ PRIVILEGE_SET *set;
+} LSA_Q_ADDPRIVS;
+
+typedef struct lsa_r_addprivs
+{
+ NTSTATUS status;
+} LSA_R_ADDPRIVS;
+
+
+typedef struct lsa_q_removeprivs
+{
+ POLICY_HND pol; /* policy handle */
+ uint32 allrights;
+ uint32 ptr;
+ uint32 count;
+ PRIVILEGE_SET *set;
+} LSA_Q_REMOVEPRIVS;
+
+typedef struct lsa_r_removeprivs
+{
+ NTSTATUS status;
+} LSA_R_REMOVEPRIVS;
+
+
+#endif /* _RPC_LSA_H */
diff --git a/source/include/rpc_misc.h b/source/include/rpc_misc.h
new file mode 100644
index 00000000000..0c6eee3650a
--- /dev/null
+++ b/source/include/rpc_misc.h
@@ -0,0 +1,408 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+ Copyright (C) Paul Ashton 1997
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "ntdomain.h"
+#include "rpc_dce.h"
+
+#ifndef _RPC_MISC_H /* _RPC_MISC_H */
+#define _RPC_MISC_H
+
+#define SMB_RPC_INTERFACE_VERSION 1
+
+/* well-known RIDs - Relative IDs */
+
+/* RIDs - Well-known users ... */
+#define DOMAIN_USER_RID_ADMIN (0x000001F4L)
+#define DOMAIN_USER_RID_GUEST (0x000001F5L)
+#define DOMAIN_USER_RID_KRBTGT (0x000001F6L)
+
+/* RIDs - well-known groups ... */
+#define DOMAIN_GROUP_RID_ADMINS (0x00000200L)
+#define DOMAIN_GROUP_RID_USERS (0x00000201L)
+#define DOMAIN_GROUP_RID_GUESTS (0x00000202L)
+#define DOMAIN_GROUP_RID_COMPUTERS (0x00000203L)
+
+#define DOMAIN_GROUP_RID_CONTROLLERS (0x00000204L)
+#define DOMAIN_GROUP_RID_CERT_ADMINS (0x00000205L)
+#define DOMAIN_GROUP_RID_SCHEMA_ADMINS (0x00000206L)
+#define DOMAIN_GROUP_RID_ENTERPRISE_ADMINS (0x00000207L)
+
+/* is the following the right number? I bet it is --simo
+#define DOMAIN_GROUP_RID_POLICY_ADMINS (0x00000208L)
+*/
+
+/* RIDs - well-known aliases ... */
+#define BUILTIN_ALIAS_RID_ADMINS (0x00000220L)
+#define BUILTIN_ALIAS_RID_USERS (0x00000221L)
+#define BUILTIN_ALIAS_RID_GUESTS (0x00000222L)
+#define BUILTIN_ALIAS_RID_POWER_USERS (0x00000223L)
+
+#define BUILTIN_ALIAS_RID_ACCOUNT_OPS (0x00000224L)
+#define BUILTIN_ALIAS_RID_SYSTEM_OPS (0x00000225L)
+#define BUILTIN_ALIAS_RID_PRINT_OPS (0x00000226L)
+#define BUILTIN_ALIAS_RID_BACKUP_OPS (0x00000227L)
+
+#define BUILTIN_ALIAS_RID_REPLICATOR (0x00000228L)
+#define BUILTIN_ALIAS_RID_RAS_SERVERS (0x00000229L)
+#define BUILTIN_ALIAS_RID_PRE_2K_ACCESS (0x0000022aL)
+
+/*
+ * Masks for mappings between unix uid and gid types and
+ * NT RIDS.
+ */
+
+
+#define BASE_RID (0x000003E8L)
+
+/* Take the bottom bit. */
+#define RID_TYPE_MASK 1
+#define RID_MULTIPLIER 2
+
+/* The two common types. */
+#define USER_RID_TYPE 0
+#define GROUP_RID_TYPE 1
+
+/* ENUM_HND */
+typedef struct enum_hnd_info
+{
+ uint32 ptr_hnd; /* pointer to enumeration handle */
+ uint32 handle; /* enumeration handle */
+} ENUM_HND;
+
+/* LOOKUP_LEVEL - switch value */
+typedef struct lookup_level_info
+{
+ uint16 value;
+} LOOKUP_LEVEL;
+
+/* DOM_SID2 - security id */
+typedef struct sid_info_2
+{
+ uint32 num_auths; /* length, bytes, including length of len :-) */
+ DOM_SID sid;
+} DOM_SID2;
+
+/* STRHDR - string header */
+typedef struct header_info
+{
+ uint16 str_str_len;
+ uint16 str_max_len;
+ uint32 buffer; /* non-zero */
+} STRHDR;
+
+/* UNIHDR - unicode string header */
+typedef struct unihdr_info
+{
+ uint16 uni_str_len;
+ uint16 uni_max_len;
+ uint32 buffer; /* usually has a value of 4 */
+} UNIHDR;
+
+/* UNIHDR2 - unicode string header and undocumented buffer */
+typedef struct unihdr2_info
+{
+ UNIHDR unihdr;
+ uint32 buffer; /* 32 bit buffer pointer */
+} UNIHDR2;
+
+/* clueless as to what maximum length should be */
+#define MAX_UNISTRLEN 256
+#define MAX_STRINGLEN 256
+#define MAX_BUFFERLEN 512
+
+/* UNISTR - unicode string size and buffer */
+typedef struct unistr_info
+{
+ /* unicode characters. ***MUST*** be little-endian. ***MUST*** be null-terminated */
+ uint16 *buffer;
+} UNISTR;
+
+/* BUFHDR - buffer header */
+typedef struct bufhdr_info
+{
+ uint32 buf_max_len;
+ uint32 buf_len;
+} BUFHDR;
+
+/* BUFFER2 - unicode string, size (in uint8 ascii chars) and buffer */
+/* pathetic. some stupid team of \PIPE\winreg writers got the concept */
+/* of a unicode string different from the other \PIPE\ writers */
+typedef struct buffer2_info
+{
+ uint32 buf_max_len;
+ uint32 offset;
+ uint32 buf_len;
+ /* unicode characters. ***MUST*** be little-endian. **NOT** necessarily null-terminated */
+ uint16 *buffer;
+} BUFFER2;
+
+/* BUFFER3 */
+typedef struct buffer3_info
+{
+ uint32 buf_max_len;
+ uint8 *buffer; /* Data */
+ uint32 buf_len;
+} BUFFER3;
+
+/* BUFFER5 */
+typedef struct buffer5_info
+{
+ uint32 buf_len;
+ uint16 *buffer; /* data */
+} BUFFER5;
+
+/* UNISTR2 - unicode string size (in uint16 unicode chars) and buffer */
+typedef struct unistr2_info
+{
+ uint32 uni_max_len;
+ uint32 offset;
+ uint32 uni_str_len;
+ /* unicode characters. ***MUST*** be little-endian.
+ **must** be null-terminated and the uni_str_len should include
+ the NULL character */
+ uint16 *buffer;
+} UNISTR2;
+
+/* STRING2 - string size (in uint8 chars) and buffer */
+typedef struct string2_info
+{
+ uint32 str_max_len;
+ uint32 offset;
+ uint32 str_str_len;
+ uint8 *buffer; /* uint8 characters. **NOT** necessarily null-terminated */
+} STRING2;
+
+/* UNISTR3 - XXXX not sure about this structure */
+typedef struct unistr3_info
+{
+ uint32 uni_str_len;
+ UNISTR str;
+
+} UNISTR3;
+
+/* an element in a unicode string array */
+typedef struct
+{
+ uint16 length;
+ uint16 size;
+ uint32 ref_id;
+ UNISTR2 string;
+} UNISTR2_ARRAY_EL;
+
+/* an array of unicode strings */
+typedef struct
+{
+ uint32 ref_id;
+ uint32 count;
+ UNISTR2_ARRAY_EL *strings;
+} UNISTR2_ARRAY;
+
+
+/* an element in a sid array */
+typedef struct
+{
+ uint32 ref_id;
+ DOM_SID2 sid;
+} SID_ARRAY_EL;
+
+/* an array of sids */
+typedef struct
+{
+ uint32 ref_id;
+ uint32 count;
+ SID_ARRAY_EL *sids;
+} SID_ARRAY;
+
+/* DOM_RID2 - domain RID structure for ntlsa pipe */
+typedef struct domrid2_info
+{
+ uint8 type; /* value is SID_NAME_USE enum */
+ uint32 rid;
+ uint32 rid_idx; /* referenced domain index */
+
+} DOM_RID2;
+
+/* DOM_RID3 - domain RID structure for samr pipe */
+typedef struct domrid3_info
+{
+ uint32 rid; /* domain-relative (to a SID) id */
+ uint32 type1; /* value is 0x1 */
+ uint32 ptr_type; /* undocumented pointer */
+ uint32 type2; /* value is 0x1 */
+ uint32 unk; /* value is 0x2 */
+
+} DOM_RID3;
+
+/* DOM_RID4 - rid + user attributes */
+typedef struct domrid4_info
+{
+ uint32 unknown;
+ uint16 attr;
+ uint32 rid; /* user RID */
+} DOM_RID4;
+
+/* DOM_CLNT_SRV - client / server names */
+typedef struct clnt_srv_info
+{
+ uint32 undoc_buffer; /* undocumented 32 bit buffer pointer */
+ UNISTR2 uni_logon_srv; /* logon server name */
+ uint32 undoc_buffer2; /* undocumented 32 bit buffer pointer */
+ UNISTR2 uni_comp_name; /* client machine name */
+} DOM_CLNT_SRV;
+
+/* DOM_LOG_INFO - login info */
+typedef struct log_info
+{
+ uint32 undoc_buffer; /* undocumented 32 bit buffer pointer */
+ UNISTR2 uni_logon_srv; /* logon server name */
+ UNISTR2 uni_acct_name; /* account name */
+ uint16 sec_chan; /* secure channel type */
+ UNISTR2 uni_comp_name; /* client machine name */
+} DOM_LOG_INFO;
+
+/* DOM_CHAL - challenge info */
+typedef struct chal_info
+{
+ uchar data[8]; /* credentials */
+} DOM_CHAL;
+
+/* DOM_CREDs - timestamped client or server credentials */
+typedef struct cred_info
+{
+ DOM_CHAL challenge; /* credentials */
+ UTIME timestamp; /* credential time-stamp */
+} DOM_CRED;
+
+/* DOM_CLNT_INFO - client info */
+typedef struct clnt_info
+{
+ DOM_LOG_INFO login;
+ DOM_CRED cred;
+} DOM_CLNT_INFO;
+
+/* DOM_CLNT_INFO2 - client info */
+typedef struct clnt_info2
+{
+ DOM_CLNT_SRV login;
+ uint32 ptr_cred;
+ DOM_CRED cred;
+} DOM_CLNT_INFO2;
+
+/* DOM_LOGON_ID - logon id */
+typedef struct logon_info
+{
+ uint32 low;
+ uint32 high;
+} DOM_LOGON_ID;
+
+/* OWF INFO */
+typedef struct owf_info
+{
+ uint8 data[16];
+} OWF_INFO;
+
+
+/* DOM_GID - group id + user attributes */
+typedef struct gid_info
+{
+ uint32 g_rid; /* a group RID */
+ uint32 attr;
+} DOM_GID;
+
+/* POLICY_HND */
+typedef struct lsa_policy_info
+{
+ uint32 data1;
+ uint32 data2;
+ uint16 data3;
+ uint16 data4;
+ uint8 data5[8];
+#ifdef __INSURE__
+
+ /* To prevent the leakage of policy handles mallocate a bit of
+ memory when a policy handle is created and free it when the
+ handle is closed. This should cause Insure to flag an error
+ when policy handles are overwritten or fall out of scope without
+ being freed. */
+
+ char *marker;
+#endif
+} POLICY_HND;
+
+/*
+ * A client connection's state, pipe name,
+ * user credentials, etc...
+ */
+typedef struct _cli_auth_fns cli_auth_fns;
+struct user_creds;
+struct cli_connection {
+
+ char *srv_name;
+ char *pipe_name;
+ struct user_creds usr_creds;
+
+ struct cli_state *pCli_state;
+
+ cli_auth_fns *auth;
+
+ void *auth_info;
+ void *auth_creds;
+};
+
+
+/*
+ * Associate a POLICY_HND with a cli_connection
+ */
+typedef struct rpc_hnd_node {
+
+ POLICY_HND hnd;
+ struct cli_connection *cli;
+
+} RPC_HND_NODE;
+
+typedef struct uint64_s
+{
+ uint32 low;
+ uint32 high;
+} UINT64_S;
+
+/* BUFHDR2 - another buffer header, with info level */
+typedef struct bufhdr2_info
+{
+ uint32 info_level;
+ uint32 length; /* uint8 chars */
+ uint32 buffer;
+
+}
+BUFHDR2;
+
+/* BUFFER4 - simple length and buffer */
+typedef struct buffer4_info
+{
+ uint32 buf_len;
+ uint8 buffer[MAX_BUFFERLEN];
+
+}
+BUFFER4;
+
+enum unistr2_term_codes { UNI_FLAGS_NONE = 0, UNI_STR_TERMINATE = 1, UNI_MAXLEN_TERMINATE = 2, UNI_BROKEN_NON_NULL = 3 };
+#endif /* _RPC_MISC_H */
diff --git a/source/include/rpc_netlogon.h b/source/include/rpc_netlogon.h
new file mode 100644
index 00000000000..a5b93b0238a
--- /dev/null
+++ b/source/include/rpc_netlogon.h
@@ -0,0 +1,930 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+ Copyright (C) Paul Ashton 1997
+ Copyright (C) Jean François Micouleau 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _RPC_NETLOGON_H /* _RPC_NETLOGON_H */
+#define _RPC_NETLOGON_H
+
+
+/* NETLOGON pipe */
+#define NET_SAMLOGON 0x02
+#define NET_SAMLOGOFF 0x03
+#define NET_REQCHAL 0x04
+#define NET_AUTH 0x05
+#define NET_SRVPWSET 0x06
+#define NET_SAM_DELTAS 0x07
+#define NET_LOGON_CTRL 0x0c
+#define NET_GETDCNAME 0x0d
+#define NET_AUTH2 0x0f
+#define NET_LOGON_CTRL2 0x0e
+#define NET_SAM_SYNC 0x10
+#define NET_TRUST_DOM_LIST 0x13
+#define NET_AUTH3 0x1a
+
+/* Secure Channel types. used in NetrServerAuthenticate negotiation */
+#define SEC_CHAN_WKSTA 2
+#define SEC_CHAN_DOMAIN 4
+#define SEC_CHAN_BDC 6
+
+/* Returned delta types */
+#define SAM_DELTA_DOMAIN_INFO 0x01
+#define SAM_DELTA_GROUP_INFO 0x02
+#define SAM_DELTA_RENAME_GROUP 0x04
+#define SAM_DELTA_ACCOUNT_INFO 0x05
+#define SAM_DELTA_RENAME_USER 0x07
+#define SAM_DELTA_GROUP_MEM 0x08
+#define SAM_DELTA_ALIAS_INFO 0x09
+#define SAM_DELTA_RENAME_ALIAS 0x0b
+#define SAM_DELTA_ALIAS_MEM 0x0c
+#define SAM_DELTA_POLICY_INFO 0x0d
+#define SAM_DELTA_TRUST_DOMS 0x0e
+#define SAM_DELTA_PRIVS_INFO 0x10 /* DT_DELTA_ACCOUNTS */
+#define SAM_DELTA_SECRET_INFO 0x12
+#define SAM_DELTA_DELETE_GROUP 0x14
+#define SAM_DELTA_DELETE_USER 0x15
+#define SAM_DELTA_MODIFIED_COUNT 0x16
+
+/* SAM database types */
+#define SAM_DATABASE_DOMAIN 0x00 /* Domain users and groups */
+#define SAM_DATABASE_BUILTIN 0x01 /* BUILTIN users and groups */
+#define SAM_DATABASE_PRIVS 0x02 /* Privileges */
+
+#if 0
+/* I think this is correct - it's what gets parsed on the wire. JRA. */
+/* NET_USER_INFO_2 */
+typedef struct net_user_info_2
+{
+ uint32 ptr_user_info;
+
+ NTTIME logon_time; /* logon time */
+ NTTIME logoff_time; /* logoff time */
+ NTTIME kickoff_time; /* kickoff time */
+ NTTIME pass_last_set_time; /* password last set time */
+ NTTIME pass_can_change_time; /* password can change time */
+ NTTIME pass_must_change_time; /* password must change time */
+
+ UNIHDR hdr_user_name; /* username unicode string header */
+ UNIHDR hdr_full_name; /* user's full name unicode string header */
+ UNIHDR hdr_logon_script; /* logon script unicode string header */
+ UNIHDR hdr_profile_path; /* profile path unicode string header */
+ UNIHDR hdr_home_dir; /* home directory unicode string header */
+ UNIHDR hdr_dir_drive; /* home directory drive unicode string header */
+
+ uint16 logon_count; /* logon count */
+ uint16 bad_pw_count; /* bad password count */
+
+ uint32 user_id; /* User ID */
+ uint32 group_id; /* Group ID */
+ uint32 num_groups; /* num groups */
+ uint32 buffer_groups; /* undocumented buffer pointer to groups. */
+ uint32 user_flgs; /* user flags */
+
+ uint8 user_sess_key[16]; /* unused user session key */
+
+ UNIHDR hdr_logon_srv; /* logon server unicode string header */
+ UNIHDR hdr_logon_dom; /* logon domain unicode string header */
+
+ uint32 buffer_dom_id; /* undocumented logon domain id pointer */
+ uint8 padding[40]; /* unused padding bytes. expansion room */
+
+ UNISTR2 uni_user_name; /* username unicode string */
+ UNISTR2 uni_full_name; /* user's full name unicode string */
+ UNISTR2 uni_logon_script; /* logon script unicode string */
+ UNISTR2 uni_profile_path; /* profile path unicode string */
+ UNISTR2 uni_home_dir; /* home directory unicode string */
+ UNISTR2 uni_dir_drive; /* home directory drive unicode string */
+
+ uint32 num_groups2; /* num groups */
+ DOM_GID *gids; /* group info */
+
+ UNISTR2 uni_logon_srv; /* logon server unicode string */
+ UNISTR2 uni_logon_dom; /* logon domain unicode string */
+
+ DOM_SID2 dom_sid; /* domain SID */
+
+ uint32 num_other_groups; /* other groups */
+ DOM_GID *other_gids; /* group info */
+ DOM_SID2 *other_sids; /* undocumented - domain SIDs */
+
+} NET_USER_INFO_2;
+#endif
+
+/* NET_USER_INFO_3 */
+typedef struct net_user_info_3
+{
+ uint32 ptr_user_info;
+
+ NTTIME logon_time; /* logon time */
+ NTTIME logoff_time; /* logoff time */
+ NTTIME kickoff_time; /* kickoff time */
+ NTTIME pass_last_set_time; /* password last set time */
+ NTTIME pass_can_change_time; /* password can change time */
+ NTTIME pass_must_change_time; /* password must change time */
+
+ UNIHDR hdr_user_name; /* username unicode string header */
+ UNIHDR hdr_full_name; /* user's full name unicode string header */
+ UNIHDR hdr_logon_script; /* logon script unicode string header */
+ UNIHDR hdr_profile_path; /* profile path unicode string header */
+ UNIHDR hdr_home_dir; /* home directory unicode string header */
+ UNIHDR hdr_dir_drive; /* home directory drive unicode string header */
+
+ uint16 logon_count; /* logon count */
+ uint16 bad_pw_count; /* bad password count */
+
+ uint32 user_rid; /* User RID */
+ uint32 group_rid; /* Group RID */
+
+ uint32 num_groups; /* num groups */
+ uint32 buffer_groups; /* undocumented buffer pointer to groups. */
+ uint32 user_flgs; /* user flags */
+
+ uint8 user_sess_key[16]; /* user session key */
+
+ UNIHDR hdr_logon_srv; /* logon server unicode string header */
+ UNIHDR hdr_logon_dom; /* logon domain unicode string header */
+
+ uint32 buffer_dom_id; /* undocumented logon domain id pointer */
+ uint8 padding[40]; /* unused padding bytes. expansion room */
+
+ uint32 num_other_sids; /* number of foreign/trusted domain sids */
+ uint32 buffer_other_sids;
+
+ UNISTR2 uni_user_name; /* username unicode string */
+ UNISTR2 uni_full_name; /* user's full name unicode string */
+ UNISTR2 uni_logon_script; /* logon script unicode string */
+ UNISTR2 uni_profile_path; /* profile path unicode string */
+ UNISTR2 uni_home_dir; /* home directory unicode string */
+ UNISTR2 uni_dir_drive; /* home directory drive unicode string */
+
+ uint32 num_groups2; /* num groups */
+ DOM_GID *gids; /* group info */
+
+ UNISTR2 uni_logon_srv; /* logon server unicode string */
+ UNISTR2 uni_logon_dom; /* logon domain unicode string */
+
+ DOM_SID2 dom_sid; /* domain SID */
+
+ uint32 num_other_groups; /* other groups */
+ DOM_GID *other_gids; /* group info */
+ DOM_SID2 *other_sids; /* foreign/trusted domain SIDs */
+
+} NET_USER_INFO_3;
+
+
+/* NETLOGON_INFO_1 - pdc status info, i presume */
+typedef struct netlogon_1_info
+{
+ uint32 flags; /* 0x0 - undocumented */
+ uint32 pdc_status; /* 0x0 - undocumented */
+
+} NETLOGON_INFO_1;
+
+/* NETLOGON_INFO_2 - pdc status info, plus trusted domain info */
+typedef struct netlogon_2_info
+{
+ uint32 flags; /* 0x0 - undocumented */
+ uint32 pdc_status; /* 0x0 - undocumented */
+ uint32 ptr_trusted_dc_name; /* pointer to trusted domain controller name */
+ uint32 tc_status; /* 0x051f - ERROR_NO_LOGON_SERVERS */
+ UNISTR2 uni_trusted_dc_name; /* unicode string - trusted dc name */
+
+} NETLOGON_INFO_2;
+
+/* NETLOGON_INFO_3 - logon status info, i presume */
+typedef struct netlogon_3_info
+{
+ uint32 flags; /* 0x0 - undocumented */
+ uint32 logon_attempts; /* number of logon attempts */
+ uint32 reserved_1; /* 0x0 - undocumented */
+ uint32 reserved_2; /* 0x0 - undocumented */
+ uint32 reserved_3; /* 0x0 - undocumented */
+ uint32 reserved_4; /* 0x0 - undocumented */
+ uint32 reserved_5; /* 0x0 - undocumented */
+
+} NETLOGON_INFO_3;
+
+/********************************************************
+ Logon Control Query
+
+ This is generated by a nltest /bdc_query:DOMAIN
+
+ query_level 0x1, function_code 0x1
+
+ ********************************************************/
+
+/* NET_Q_LOGON_CTRL - LSA Netr Logon Control */
+
+typedef struct net_q_logon_ctrl_info
+{
+ uint32 ptr;
+ UNISTR2 uni_server_name;
+ uint32 function_code;
+ uint32 query_level;
+} NET_Q_LOGON_CTRL;
+
+/* NET_R_LOGON_CTRL - LSA Netr Logon Control */
+
+typedef struct net_r_logon_ctrl_info
+{
+ uint32 switch_value;
+ uint32 ptr;
+
+ union {
+ NETLOGON_INFO_1 info1;
+ } logon;
+
+ NTSTATUS status;
+} NET_R_LOGON_CTRL;
+
+/********************************************************
+ Logon Control2 Query
+
+ query_level 0x1 - pdc status
+ query_level 0x3 - number of logon attempts.
+
+ ********************************************************/
+
+/* NET_Q_LOGON_CTRL2 - LSA Netr Logon Control 2 */
+typedef struct net_q_logon_ctrl2_info
+{
+ uint32 ptr; /* undocumented buffer pointer */
+ UNISTR2 uni_server_name; /* server name, starting with two '\'s */
+
+ uint32 function_code; /* 0x1 */
+ uint32 query_level; /* 0x1, 0x3 */
+ uint32 switch_value; /* 0x1 */
+
+} NET_Q_LOGON_CTRL2;
+
+/*******************************************************
+ Logon Control Response
+
+ switch_value is same as query_level in request
+ *******************************************************/
+
+/* NET_R_LOGON_CTRL2 - response to LSA Logon Control2 */
+typedef struct net_r_logon_ctrl2_info
+{
+ uint32 switch_value; /* 0x1, 0x3 */
+ uint32 ptr;
+
+ union
+ {
+ NETLOGON_INFO_1 info1;
+ NETLOGON_INFO_2 info2;
+ NETLOGON_INFO_3 info3;
+
+ } logon;
+
+ NTSTATUS status; /* return code */
+
+} NET_R_LOGON_CTRL2;
+
+/* NET_Q_GETDCNAME - Ask a DC for a trusted DC name */
+
+typedef struct net_q_getdcname
+{
+ uint32 ptr_logon_server;
+ UNISTR2 uni_logon_server;
+ uint32 ptr_domainname;
+ UNISTR2 uni_domainname;
+} NET_Q_GETDCNAME;
+
+/* NET_R_GETDCNAME - Ask a DC for a trusted DC name */
+
+typedef struct net_r_getdcname
+{
+ uint32 ptr_dcname;
+ UNISTR2 uni_dcname;
+ NTSTATUS status;
+} NET_R_GETDCNAME;
+
+/* NET_Q_TRUST_DOM_LIST - LSA Query Trusted Domains */
+typedef struct net_q_trust_dom_info
+{
+ uint32 ptr; /* undocumented buffer pointer */
+ UNISTR2 uni_server_name; /* server name, starting with two '\'s */
+
+} NET_Q_TRUST_DOM_LIST;
+
+#define MAX_TRUST_DOMS 1
+
+/* NET_R_TRUST_DOM_LIST - response to LSA Trusted Domains */
+typedef struct net_r_trust_dom_info
+{
+ UNISTR2 uni_trust_dom_name[MAX_TRUST_DOMS];
+
+ NTSTATUS status; /* return code */
+
+} NET_R_TRUST_DOM_LIST;
+
+
+/* NEG_FLAGS */
+typedef struct neg_flags_info
+{
+ uint32 neg_flags; /* negotiated flags */
+
+} NEG_FLAGS;
+
+
+/* NET_Q_REQ_CHAL */
+typedef struct net_q_req_chal_info
+{
+ uint32 undoc_buffer; /* undocumented buffer pointer */
+ UNISTR2 uni_logon_srv; /* logon server unicode string */
+ UNISTR2 uni_logon_clnt; /* logon client unicode string */
+ DOM_CHAL clnt_chal; /* client challenge */
+
+} NET_Q_REQ_CHAL;
+
+
+/* NET_R_REQ_CHAL */
+typedef struct net_r_req_chal_info
+{
+ DOM_CHAL srv_chal; /* server challenge */
+ NTSTATUS status; /* return code */
+} NET_R_REQ_CHAL;
+
+/* NET_Q_AUTH */
+typedef struct net_q_auth_info
+{
+ DOM_LOG_INFO clnt_id; /* client identification info */
+ DOM_CHAL clnt_chal; /* client-calculated credentials */
+} NET_Q_AUTH;
+
+/* NET_R_AUTH */
+typedef struct net_r_auth_info
+{
+ DOM_CHAL srv_chal; /* server-calculated credentials */
+ NTSTATUS status; /* return code */
+} NET_R_AUTH;
+
+/* NET_Q_AUTH_2 */
+typedef struct net_q_auth2_info
+{
+ DOM_LOG_INFO clnt_id; /* client identification info */
+ DOM_CHAL clnt_chal; /* client-calculated credentials */
+
+ NEG_FLAGS clnt_flgs; /* usually 0x0000 01ff */
+
+} NET_Q_AUTH_2;
+
+
+/* NET_R_AUTH_2 */
+typedef struct net_r_auth2_info
+{
+ DOM_CHAL srv_chal; /* server-calculated credentials */
+ NEG_FLAGS srv_flgs; /* usually 0x0000 01ff */
+ NTSTATUS status; /* return code */
+} NET_R_AUTH_2;
+
+/* NET_Q_AUTH_3 */
+typedef struct net_q_auth3_info
+{
+ DOM_LOG_INFO clnt_id; /* client identification info */
+ DOM_CHAL clnt_chal; /* client-calculated credentials */
+ NEG_FLAGS clnt_flgs; /* usually 0x6007 ffff */
+} NET_Q_AUTH_3;
+
+/* NET_R_AUTH_3 */
+typedef struct net_r_auth3_info
+{
+ DOM_CHAL srv_chal; /* server-calculated credentials */
+ NEG_FLAGS srv_flgs; /* usually 0x6007 ffff */
+ uint32 unknown; /* 0x0000045b */
+ NTSTATUS status; /* return code */
+} NET_R_AUTH_3;
+
+
+/* NET_Q_SRV_PWSET */
+typedef struct net_q_srv_pwset_info
+{
+ DOM_CLNT_INFO clnt_id; /* client identification/authentication info */
+ uint8 pwd[16]; /* new password - undocumented. */
+
+} NET_Q_SRV_PWSET;
+
+/* NET_R_SRV_PWSET */
+typedef struct net_r_srv_pwset_info
+{
+ DOM_CRED srv_cred; /* server-calculated credentials */
+
+ NTSTATUS status; /* return code */
+
+} NET_R_SRV_PWSET;
+
+/* NET_ID_INFO_2 */
+typedef struct net_network_info_2
+{
+ uint32 ptr_id_info2; /* pointer to id_info_2 */
+ UNIHDR hdr_domain_name; /* domain name unicode header */
+ uint32 param_ctrl; /* param control (0x2) */
+ DOM_LOGON_ID logon_id; /* logon ID */
+ UNIHDR hdr_user_name; /* user name unicode header */
+ UNIHDR hdr_wksta_name; /* workstation name unicode header */
+ uint8 lm_chal[8]; /* lan manager 8 byte challenge */
+ STRHDR hdr_nt_chal_resp; /* nt challenge response */
+ STRHDR hdr_lm_chal_resp; /* lm challenge response */
+
+ UNISTR2 uni_domain_name; /* domain name unicode string */
+ UNISTR2 uni_user_name; /* user name unicode string */
+ UNISTR2 uni_wksta_name; /* workgroup name unicode string */
+ STRING2 nt_chal_resp; /* nt challenge response */
+ STRING2 lm_chal_resp; /* lm challenge response */
+
+} NET_ID_INFO_2;
+
+/* NET_ID_INFO_1 */
+typedef struct id_info_1
+{
+ uint32 ptr_id_info1; /* pointer to id_info_1 */
+ UNIHDR hdr_domain_name; /* domain name unicode header */
+ uint32 param_ctrl; /* param control */
+ DOM_LOGON_ID logon_id; /* logon ID */
+ UNIHDR hdr_user_name; /* user name unicode header */
+ UNIHDR hdr_wksta_name; /* workstation name unicode header */
+ OWF_INFO lm_owf; /* LM OWF Password */
+ OWF_INFO nt_owf; /* NT OWF Password */
+ UNISTR2 uni_domain_name; /* domain name unicode string */
+ UNISTR2 uni_user_name; /* user name unicode string */
+ UNISTR2 uni_wksta_name; /* workgroup name unicode string */
+
+} NET_ID_INFO_1;
+
+#define INTERACTIVE_LOGON_TYPE 1
+#define NET_LOGON_TYPE 2
+
+/* NET_ID_INFO_CTR */
+typedef struct net_id_info_ctr_info
+{
+ uint16 switch_value;
+
+ union
+ {
+ NET_ID_INFO_1 id1; /* auth-level 1 - interactive user login */
+ NET_ID_INFO_2 id2; /* auth-level 2 - workstation referred login */
+
+ } auth;
+
+} NET_ID_INFO_CTR;
+
+/* SAM_INFO - sam logon/off id structure */
+typedef struct sam_info
+{
+ DOM_CLNT_INFO2 client;
+ uint32 ptr_rtn_cred; /* pointer to return credentials */
+ DOM_CRED rtn_cred; /* return credentials */
+ uint16 logon_level;
+ NET_ID_INFO_CTR *ctr;
+
+} DOM_SAM_INFO;
+
+/* NET_Q_SAM_LOGON */
+typedef struct net_q_sam_logon_info
+{
+ DOM_SAM_INFO sam_id;
+ uint16 validation_level;
+
+} NET_Q_SAM_LOGON;
+
+/* NET_R_SAM_LOGON */
+typedef struct net_r_sam_logon_info
+{
+ uint32 buffer_creds; /* undocumented buffer pointer */
+ DOM_CRED srv_creds; /* server credentials. server time stamp appears to be ignored. */
+
+ uint16 switch_value; /* 3 - indicates type of USER INFO */
+ NET_USER_INFO_3 *user;
+
+ uint32 auth_resp; /* 1 - Authoritative response; 0 - Non-Auth? */
+
+ NTSTATUS status; /* return code */
+
+} NET_R_SAM_LOGON;
+
+
+/* NET_Q_SAM_LOGOFF */
+typedef struct net_q_sam_logoff_info
+{
+ DOM_SAM_INFO sam_id;
+
+} NET_Q_SAM_LOGOFF;
+
+/* NET_R_SAM_LOGOFF */
+typedef struct net_r_sam_logoff_info
+{
+ uint32 buffer_creds; /* undocumented buffer pointer */
+ DOM_CRED srv_creds; /* server credentials. server time stamp appears to be ignored. */
+
+ NTSTATUS status; /* return code */
+
+} NET_R_SAM_LOGOFF;
+
+/* NET_Q_SAM_SYNC */
+typedef struct net_q_sam_sync_info
+{
+ UNISTR2 uni_srv_name; /* \\PDC */
+ UNISTR2 uni_cli_name; /* BDC */
+ DOM_CRED cli_creds;
+ DOM_CRED ret_creds;
+
+ uint32 database_id;
+ uint32 restart_state;
+ uint32 sync_context;
+
+ uint32 max_size; /* preferred maximum length */
+
+} NET_Q_SAM_SYNC;
+
+/* SAM_DELTA_HDR */
+typedef struct sam_delta_hdr_info
+{
+ uint16 type; /* type of structure attached */
+ uint16 type2;
+ uint32 target_rid;
+
+ uint32 type3;
+ uint32 ptr_delta;
+
+} SAM_DELTA_HDR;
+
+/* SAM_DOMAIN_INFO (0x1) */
+typedef struct sam_domain_info_info
+{
+ UNIHDR hdr_dom_name;
+ UNIHDR hdr_oem_info;
+
+ UINT64_S force_logoff;
+ uint16 min_pwd_len;
+ uint16 pwd_history_len;
+ UINT64_S max_pwd_age;
+ UINT64_S min_pwd_age;
+ UINT64_S dom_mod_count;
+ NTTIME creation_time;
+
+ BUFHDR2 hdr_sec_desc; /* security descriptor */
+ UNIHDR hdr_unknown;
+ uint8 reserved[40];
+
+ UNISTR2 uni_dom_name;
+ UNISTR2 buf_oem_info; /* never seen */
+
+ BUFFER4 buf_sec_desc;
+ UNISTR2 buf_unknown;
+
+} SAM_DOMAIN_INFO;
+
+/* SAM_GROUP_INFO (0x2) */
+typedef struct sam_group_info_info
+{
+ UNIHDR hdr_grp_name;
+ DOM_GID gid;
+ UNIHDR hdr_grp_desc;
+ BUFHDR2 hdr_sec_desc; /* security descriptor */
+ uint8 reserved[48];
+
+ UNISTR2 uni_grp_name;
+ UNISTR2 uni_grp_desc;
+ BUFFER4 buf_sec_desc;
+
+} SAM_GROUP_INFO;
+
+/* SAM_PWD */
+typedef struct sam_passwd_info
+{
+ /* this structure probably contains password history */
+ /* this is probably a count of lm/nt pairs */
+ uint32 unk_0; /* 0x0000 0002 */
+
+ UNIHDR hdr_lm_pwd;
+ uint8 buf_lm_pwd[16];
+
+ UNIHDR hdr_nt_pwd;
+ uint8 buf_nt_pwd[16];
+
+ UNIHDR hdr_empty_lm;
+ UNIHDR hdr_empty_nt;
+
+} SAM_PWD;
+
+/* SAM_ACCOUNT_INFO (0x5) */
+typedef struct sam_account_info_info
+{
+ UNIHDR hdr_acct_name;
+ UNIHDR hdr_full_name;
+
+ uint32 user_rid;
+ uint32 group_rid;
+
+ UNIHDR hdr_home_dir;
+ UNIHDR hdr_dir_drive;
+ UNIHDR hdr_logon_script;
+ UNIHDR hdr_acct_desc;
+ UNIHDR hdr_workstations;
+
+ NTTIME logon_time;
+ NTTIME logoff_time;
+
+ uint32 logon_divs; /* 0xA8 */
+ uint32 ptr_logon_hrs;
+
+ uint16 bad_pwd_count;
+ uint16 logon_count;
+ NTTIME pwd_last_set_time;
+ NTTIME acct_expiry_time;
+
+ uint32 acb_info;
+ uint8 nt_pwd[16];
+ uint8 lm_pwd[16];
+ uint8 nt_pwd_present;
+ uint8 lm_pwd_present;
+ uint8 pwd_expired;
+
+ UNIHDR hdr_comment;
+ UNIHDR hdr_parameters;
+ uint16 country;
+ uint16 codepage;
+
+ BUFHDR2 hdr_sec_desc; /* security descriptor */
+
+ UNIHDR hdr_profile;
+ UNIHDR hdr_reserved[3]; /* space for more strings */
+ uint32 dw_reserved[4]; /* space for more data - first two seem to
+ be an NTTIME */
+
+ UNISTR2 uni_acct_name;
+ UNISTR2 uni_full_name;
+ UNISTR2 uni_home_dir;
+ UNISTR2 uni_dir_drive;
+ UNISTR2 uni_logon_script;
+ UNISTR2 uni_acct_desc;
+ UNISTR2 uni_workstations;
+
+ uint32 unknown1; /* 0x4EC */
+ uint32 unknown2; /* 0 */
+
+ BUFFER4 buf_logon_hrs;
+ UNISTR2 uni_comment;
+ UNISTR2 uni_parameters;
+ SAM_PWD pass;
+ BUFFER4 buf_sec_desc;
+ UNISTR2 uni_profile;
+
+} SAM_ACCOUNT_INFO;
+
+/* SAM_GROUP_MEM_INFO (0x8) */
+typedef struct sam_group_mem_info_info
+{
+ uint32 ptr_rids;
+ uint32 ptr_attribs;
+ uint32 num_members;
+ uint8 unknown[16];
+
+ uint32 num_members2;
+ uint32 *rids;
+
+ uint32 num_members3;
+ uint32 *attribs;
+
+} SAM_GROUP_MEM_INFO;
+
+/* SAM_ALIAS_INFO (0x9) */
+typedef struct sam_alias_info_info
+{
+ UNIHDR hdr_als_name;
+ uint32 als_rid;
+ BUFHDR2 hdr_sec_desc; /* security descriptor */
+ UNIHDR hdr_als_desc;
+ uint8 reserved[40];
+
+ UNISTR2 uni_als_name;
+ BUFFER4 buf_sec_desc;
+ UNISTR2 uni_als_desc;
+
+} SAM_ALIAS_INFO;
+
+/* SAM_ALIAS_MEM_INFO (0xC) */
+typedef struct sam_alias_mem_info_info
+{
+ uint32 num_members;
+ uint32 ptr_members;
+ uint8 unknown[16];
+
+ uint32 num_sids;
+ uint32 *ptr_sids;
+ DOM_SID2 *sids;
+
+} SAM_ALIAS_MEM_INFO;
+
+
+/* SAM_DELTA_POLICY (0x0D) */
+typedef struct
+{
+ uint32 max_log_size; /* 0x5000 */
+ UINT64_S audit_retention_period; /* 0 */
+ uint32 auditing_mode; /* 0 */
+ uint32 num_events;
+ uint32 ptr_events;
+ UNIHDR hdr_dom_name;
+ uint32 sid_ptr;
+
+ uint32 paged_pool_limit; /* 0x02000000 */
+ uint32 non_paged_pool_limit; /* 0x00100000 */
+ uint32 min_workset_size; /* 0x00010000 */
+ uint32 max_workset_size; /* 0x0f000000 */
+ uint32 page_file_limit; /* 0 */
+ UINT64_S time_limit; /* 0 */
+ NTTIME modify_time; /* 0x3c*/
+ NTTIME create_time; /* a7080110 */
+ BUFHDR2 hdr_sec_desc;
+
+ uint32 num_event_audit_options;
+ uint32 event_audit_option;
+
+ UNISTR2 domain_name;
+ DOM_SID2 domain_sid;
+
+ BUFFER4 buf_sec_desc;
+} SAM_DELTA_POLICY;
+
+/* SAM_DELTA_TRUST_DOMS */
+typedef struct
+{
+ uint32 buf_size;
+ SEC_DESC *sec_desc;
+ DOM_SID2 sid;
+ UNIHDR hdr_domain;
+
+ uint32 unknown0;
+ uint32 unknown1;
+ uint32 unknown2;
+
+ uint32 buf_size2;
+ uint32 ptr;
+
+ uint32 unknown3;
+ UNISTR2 domain;
+
+} SAM_DELTA_TRUSTDOMS;
+
+/* SAM_DELTA_PRIVS (0x10) */
+typedef struct
+{
+ DOM_SID2 sid;
+
+ uint32 priv_count;
+ uint32 priv_control;
+
+ uint32 priv_attr_ptr;
+ uint32 priv_name_ptr;
+
+ uint32 paged_pool_limit; /* 0x02000000 */
+ uint32 non_paged_pool_limit; /* 0x00100000 */
+ uint32 min_workset_size; /* 0x00010000 */
+ uint32 max_workset_size; /* 0x0f000000 */
+ uint32 page_file_limit; /* 0 */
+ UINT64_S time_limit; /* 0 */
+ uint32 system_flags; /* 1 */
+ BUFHDR2 hdr_sec_desc;
+
+ uint32 buf_size2;
+
+ uint32 attribute_count;
+ uint32 *attributes;
+
+ uint32 privlist_count;
+ UNIHDR *hdr_privslist;
+ UNISTR2 *uni_privslist;
+
+ BUFFER4 buf_sec_desc;
+} SAM_DELTA_PRIVS;
+
+/* SAM_DELTA_SECRET */
+typedef struct
+{
+ uint32 buf_size;
+ SEC_DESC *sec_desc;
+ UNISTR2 secret;
+
+ uint32 count1;
+ uint32 count2;
+ uint32 ptr;
+ NTTIME time1;
+ uint32 count3;
+ uint32 count4;
+ uint32 ptr2;
+ NTTIME time2;
+ uint32 unknow1;
+
+ uint32 buf_size2;
+ uint32 ptr3;
+ uint32 unknow2; /* 0x0 12 times */
+
+ uint32 chal_len;
+ uint32 reserved1; /* 0 */
+ uint32 chal_len2;
+ uint8 chal[16];
+
+ uint32 key_len;
+ uint32 reserved2; /* 0 */
+ uint32 key_len2;
+ uint8 key[8];
+
+ uint32 buf_size3;
+ SEC_DESC *sec_desc2;
+
+} SAM_DELTA_SECRET;
+
+/* SAM_DELTA_MOD_COUNT (0x16) */
+typedef struct
+{
+ uint32 seqnum;
+ uint32 dom_mod_count_ptr;
+ UINT64_S dom_mod_count; /* domain mod count at last sync */
+} SAM_DELTA_MOD_COUNT;
+
+typedef union sam_delta_ctr_info
+{
+ SAM_DOMAIN_INFO domain_info ;
+ SAM_GROUP_INFO group_info ;
+ SAM_ACCOUNT_INFO account_info;
+ SAM_GROUP_MEM_INFO grp_mem_info;
+ SAM_ALIAS_INFO alias_info ;
+ SAM_ALIAS_MEM_INFO als_mem_info;
+ SAM_DELTA_POLICY policy_info;
+ SAM_DELTA_PRIVS privs_info;
+ SAM_DELTA_MOD_COUNT mod_count;
+ SAM_DELTA_TRUSTDOMS trustdoms_info;
+ SAM_DELTA_SECRET secret_info;
+} SAM_DELTA_CTR;
+
+/* NET_R_SAM_SYNC */
+typedef struct net_r_sam_sync_info
+{
+ DOM_CRED srv_creds;
+
+ uint32 sync_context;
+
+ uint32 ptr_deltas;
+ uint32 num_deltas;
+ uint32 ptr_deltas2;
+ uint32 num_deltas2;
+
+ SAM_DELTA_HDR *hdr_deltas;
+ SAM_DELTA_CTR *deltas;
+
+ NTSTATUS status;
+} NET_R_SAM_SYNC;
+
+/* NET_Q_SAM_DELTAS */
+typedef struct net_q_sam_deltas_info
+{
+ UNISTR2 uni_srv_name;
+ UNISTR2 uni_cli_name;
+ DOM_CRED cli_creds;
+ DOM_CRED ret_creds;
+
+ uint32 database_id;
+ UINT64_S dom_mod_count; /* domain mod count at last sync */
+
+ uint32 max_size; /* preferred maximum length */
+
+} NET_Q_SAM_DELTAS;
+
+/* NET_R_SAM_DELTAS */
+typedef struct net_r_sam_deltas_info
+{
+ DOM_CRED srv_creds;
+
+ UINT64_S dom_mod_count; /* new domain mod count */
+
+ uint32 ptr_deltas;
+ uint32 num_deltas;
+ uint32 num_deltas2;
+
+ SAM_DELTA_HDR *hdr_deltas;
+ SAM_DELTA_CTR *deltas;
+
+ NTSTATUS status;
+} NET_R_SAM_DELTAS;
+
+#endif /* _RPC_NETLOGON_H */
diff --git a/source/include/rpc_parse.h b/source/include/rpc_parse.h
new file mode 100644
index 00000000000..73fbcb2b1be
--- /dev/null
+++ b/source/include/rpc_parse.h
@@ -0,0 +1,30 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Elrond 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _RPC_PARSE_H
+#define _RPC_PARSE_H
+
+/* different dce/rpc pipes */
+#include "rpc_reg.h"
+#include "rpc_brs.h"
+
+#endif /* _RPC_PARSE_H */
diff --git a/source/include/rpc_reg.h b/source/include/rpc_reg.h
new file mode 100644
index 00000000000..46ec88283df
--- /dev/null
+++ b/source/include/rpc_reg.h
@@ -0,0 +1,644 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-1997.
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1997.
+ Copyright (C) Paul Ashton 1997.
+ Copyright (C) Gerald Carter 2002.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _RPC_REG_H /* _RPC_REG_H */
+#define _RPC_REG_H
+
+
+/* winreg pipe defines
+ NOT IMPLEMENTED !!
+#define _REG_UNK_01 0x01
+#define _REG_UNK_03 0x03
+#define REG_CREATE_KEY 0x06
+#define REG_DELETE_KEY 0x07
+#define REG_DELETE_VALUE 0x08
+#define REG_FLUSH_KEY 0x0b
+#define REG_GET_KEY_SEC 0x0c
+#define _REG_UNK_0D 0x0d
+#define _REG_UNK_0E 0x0e
+#define _REG_UNK_12 0x12
+#define _REG_UNK_13 0x13
+#define REG_SET_KEY_SEC 0x15
+#define REG_CREATE_VALUE 0x16
+#define _REG_UNK_17 0x17
+*/
+
+/* Implemented */
+#define REG_OPEN_HKCR 0x00
+#define REG_OPEN_HKLM 0x02
+#define REG_OPEN_HKU 0x04
+#define REG_CLOSE 0x05
+#define REG_ENUM_KEY 0x09
+#define REG_ENUM_VALUE 0x0a
+#define REG_OPEN_ENTRY 0x0f
+#define REG_QUERY_KEY 0x10
+#define REG_INFO 0x11
+#define REG_SHUTDOWN 0x18
+#define REG_ABORT_SHUTDOWN 0x19
+#define REG_SAVE_KEY 0x14 /* no idea what the real name is */
+#define REG_UNKNOWN_1A 0x1a
+
+
+#define HKEY_CLASSES_ROOT 0x80000000
+#define HKEY_CURRENT_USER 0x80000001
+#define HKEY_LOCAL_MACHINE 0x80000002
+#define HKEY_USERS 0x80000003
+
+#define KEY_HKLM "HKLM"
+#define KEY_HKU "HKU"
+#define KEY_HKCR "HKCR"
+#define KEY_PRINTING "HKLM\\SYSTEM\\CurrentControlSet\\Control\\Print"
+#define KEY_TREE_ROOT ""
+
+/* Registry data types */
+
+#define REG_NONE 0
+#define REG_SZ 1
+#define REG_EXPAND_SZ 2
+#define REG_BINARY 3
+#define REG_DWORD 4
+#define REG_DWORD_LE 4 /* DWORD, little endian */
+#define REG_DWORD_BE 5 /* DWORD, big endian */
+#define REG_LINK 6
+#define REG_MULTI_SZ 7
+#define REG_RESOURCE_LIST 8
+#define REG_FULL_RESOURCE_DESCRIPTOR 9
+#define REG_RESOURCE_REQUIREMENTS_LIST 10
+
+/* structure to contain registry values */
+
+typedef struct {
+ fstring valuename;
+ uint16 type;
+ uint32 size; /* in bytes */
+ uint8 *data_p;
+} REGISTRY_VALUE;
+
+/* container for regostry values */
+
+typedef struct {
+ TALLOC_CTX *ctx;
+ uint32 num_values;
+ REGISTRY_VALUE **values;
+} REGVAL_CTR;
+
+/* container for registry subkey names */
+
+typedef struct {
+ TALLOC_CTX *ctx;
+ uint32 num_subkeys;
+ char **subkeys;
+} REGSUBKEY_CTR;
+
+
+/*
+ * container for function pointers to enumeration routines
+ * for vitural registry view
+ */
+
+typedef struct {
+ /* functions for enumerating subkeys and values */
+ int (*subkey_fn)( char *key, REGSUBKEY_CTR *subkeys);
+ int (*value_fn) ( char *key, REGVAL_CTR *val );
+ BOOL (*store_subkeys_fn)( char *key, REGSUBKEY_CTR *subkeys );
+ BOOL (*store_values_fn)( char *key, REGVAL_CTR *val );
+} REGISTRY_OPS;
+
+typedef struct {
+ const char *keyname; /* full path to name of key */
+ REGISTRY_OPS *ops; /* registry function hooks */
+} REGISTRY_HOOK;
+
+
+
+/* structure to store the registry handles */
+
+typedef struct _RegistryKey {
+
+ struct _RegistryKey *prev, *next;
+
+ POLICY_HND hnd;
+ pstring name; /* full name of registry key */
+ REGISTRY_HOOK *hook;
+
+} REGISTRY_KEY;
+
+
+/* REG_Q_OPEN_HKCR */
+typedef struct q_reg_open_hkcr_info
+{
+ uint32 ptr;
+ uint16 unknown_0; /* 0x5428 - 16 bit unknown */
+ uint16 unknown_1; /* random. changes */
+ uint32 level; /* 0x0000 0002 - 32 bit unknown */
+
+} REG_Q_OPEN_HKCR ;
+
+/* REG_R_OPEN_HKCR */
+typedef struct r_reg_open_hkcr_info
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status; /* return status */
+
+} REG_R_OPEN_HKCR;
+
+
+/* REG_Q_OPEN_HKLM */
+typedef struct q_reg_open_hklm_info
+{
+ uint32 ptr;
+ uint16 unknown_0; /* 0xE084 - 16 bit unknown */
+ uint16 unknown_1; /* random. changes */
+ uint32 access_mask;
+
+}
+REG_Q_OPEN_HKLM;
+
+/* REG_R_OPEN_HKLM */
+typedef struct r_reg_open_hklm_info
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status; /* return status */
+
+}
+REG_R_OPEN_HKLM;
+
+
+/* REG_Q_OPEN_HKU */
+typedef struct q_reg_open_hku_info
+{
+ uint32 ptr;
+ uint16 unknown_0;
+ uint16 unknown_1;
+ uint32 access_mask;
+
+} REG_Q_OPEN_HKU;
+
+/* REG_R_OPEN_HKU */
+typedef struct r_reg_open_hku_info
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status; /* return status */
+
+} REG_R_OPEN_HKU;
+
+
+/* REG_Q_FLUSH_KEY */
+typedef struct q_reg_open_flush_key_info
+{
+ POLICY_HND pol; /* policy handle */
+
+} REG_Q_FLUSH_KEY;
+
+/* REG_R_FLUSH_KEY */
+typedef struct r_reg_open_flush_key_info
+{
+ NTSTATUS status; /* return status */
+
+} REG_R_FLUSH_KEY;
+
+
+/* REG_Q_SET_KEY_SEC */
+typedef struct q_reg_set_key_sec_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ uint32 sec_info; /* xxxx_SECURITY_INFORMATION */
+
+ uint32 ptr; /* pointer */
+ BUFHDR hdr_sec; /* header for security data */
+ SEC_DESC_BUF *data; /* security data */
+
+} REG_Q_SET_KEY_SEC;
+
+/* REG_R_SET_KEY_SEC */
+typedef struct r_reg_set_key_sec_info
+{
+ NTSTATUS status;
+
+} REG_R_SET_KEY_SEC;
+
+
+/* REG_Q_GET_KEY_SEC */
+typedef struct q_reg_get_key_sec_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ uint32 sec_info; /* xxxx_SECURITY_INFORMATION */
+
+ uint32 ptr; /* pointer */
+ BUFHDR hdr_sec; /* header for security data */
+ SEC_DESC_BUF *data; /* security data */
+
+} REG_Q_GET_KEY_SEC;
+
+/* REG_R_GET_KEY_SEC */
+typedef struct r_reg_get_key_sec_info
+{
+ uint32 sec_info; /* xxxx_SECURITY_INFORMATION */
+
+ uint32 ptr; /* pointer */
+ BUFHDR hdr_sec; /* header for security data */
+ SEC_DESC_BUF *data; /* security data */
+
+ NTSTATUS status;
+
+} REG_R_GET_KEY_SEC;
+
+/* REG_Q_CREATE_VALUE */
+typedef struct q_reg_create_value_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ UNIHDR hdr_name; /* name of value */
+ UNISTR2 uni_name;
+
+ uint32 type; /* 1 = UNISTR, 3 = BYTES, 4 = DWORD, 7 = MULTI_UNISTR */
+
+ BUFFER3 *buf_value; /* value, in byte buffer */
+
+} REG_Q_CREATE_VALUE;
+
+/* REG_R_CREATE_VALUE */
+typedef struct r_reg_create_value_info
+{
+ NTSTATUS status; /* return status */
+
+} REG_R_CREATE_VALUE;
+
+/* REG_Q_ENUM_VALUE */
+typedef struct q_reg_query_value_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ uint32 val_index; /* index */
+
+ UNIHDR hdr_name; /* name of value */
+ UNISTR2 uni_name;
+
+ uint32 ptr_type; /* pointer */
+ uint32 type; /* 1 = UNISTR, 3 = BYTES, 4 = DWORD, 7 = MULTI_UNISTR */
+
+ uint32 ptr_value; /* pointer */
+ BUFFER2 buf_value; /* value, in byte buffer */
+
+ uint32 ptr1; /* pointer */
+ uint32 len_value1; /* */
+
+ uint32 ptr2; /* pointer */
+ uint32 len_value2; /* */
+
+
+} REG_Q_ENUM_VALUE;
+
+/* REG_R_ENUM_VALUE */
+typedef struct r_reg_enum_value_info
+{
+ UNIHDR hdr_name; /* name of value */
+ UNISTR2 uni_name;
+
+ uint32 ptr_type; /* pointer */
+ uint32 type; /* 1 = UNISTR, 3 = BYTES, 4 = DWORD, 7 = MULTI_UNISTR */
+
+ uint32 ptr_value; /* pointer */
+ BUFFER2 buf_value; /* value, in byte buffer */
+
+ uint32 ptr1; /* pointer */
+ uint32 len_value1; /* */
+
+ uint32 ptr2; /* pointer */
+ uint32 len_value2; /* */
+
+ NTSTATUS status; /* return status */
+
+} REG_R_ENUM_VALUE;
+
+/* REG_Q_CREATE_KEY */
+typedef struct q_reg_create_key_info
+{
+ POLICY_HND pnt_pol; /* parent key policy handle */
+
+ UNIHDR hdr_name;
+ UNISTR2 uni_name;
+
+ UNIHDR hdr_class;
+ UNISTR2 uni_class;
+
+ uint32 reserved; /* 0x0000 0000 */
+ SEC_ACCESS sam_access; /* access rights flags, see rpc_secdes.h */
+
+ uint32 ptr1;
+ uint32 sec_info; /* xxxx_SECURITY_INFORMATION */
+
+ uint32 ptr2; /* pointer */
+ BUFHDR hdr_sec; /* header for security data */
+ uint32 ptr3; /* pointer */
+ SEC_DESC_BUF *data;
+
+ uint32 unknown_2; /* 0x0000 0000 */
+
+} REG_Q_CREATE_KEY;
+
+/* REG_R_CREATE_KEY */
+typedef struct r_reg_create_key_info
+{
+ POLICY_HND key_pol; /* policy handle */
+ uint32 unknown; /* 0x0000 0000 */
+
+ NTSTATUS status; /* return status */
+
+} REG_R_CREATE_KEY;
+
+/* REG_Q_DELETE_KEY */
+typedef struct q_reg_delete_key_info
+{
+ POLICY_HND pnt_pol; /* parent key policy handle */
+
+ UNIHDR hdr_name;
+ UNISTR2 uni_name;
+} REG_Q_DELETE_KEY;
+
+/* REG_R_DELETE_KEY */
+typedef struct r_reg_delete_key_info
+{
+ POLICY_HND key_pol; /* policy handle */
+
+ NTSTATUS status; /* return status */
+
+} REG_R_DELETE_KEY;
+
+/* REG_Q_DELETE_VALUE */
+typedef struct q_reg_delete_val_info
+{
+ POLICY_HND pnt_pol; /* parent key policy handle */
+
+ UNIHDR hdr_name;
+ UNISTR2 uni_name;
+
+} REG_Q_DELETE_VALUE;
+
+/* REG_R_DELETE_VALUE */
+typedef struct r_reg_delete_val_info
+{
+ POLICY_HND key_pol; /* policy handle */
+
+ NTSTATUS status; /* return status */
+
+} REG_R_DELETE_VALUE;
+
+/* REG_Q_QUERY_KEY */
+typedef struct q_reg_query_info
+{
+ POLICY_HND pol; /* policy handle */
+ UNIHDR hdr_class;
+ UNISTR2 uni_class;
+
+} REG_Q_QUERY_KEY;
+
+/* REG_R_QUERY_KEY */
+typedef struct r_reg_query_key_info
+{
+ UNIHDR hdr_class;
+ UNISTR2 uni_class;
+
+ uint32 num_subkeys;
+ uint32 max_subkeylen;
+ uint32 reserved; /* 0x0000 0000 - according to MSDN (max_subkeysize?) */
+ uint32 num_values;
+ uint32 max_valnamelen;
+ uint32 max_valbufsize;
+ uint32 sec_desc; /* 0x0000 0078 */
+ NTTIME mod_time; /* modified time */
+
+ NTSTATUS status; /* return status */
+
+} REG_R_QUERY_KEY;
+
+
+/* REG_Q_UNKNOWN_1A */
+typedef struct q_reg_unk_1a_info
+{
+ POLICY_HND pol; /* policy handle */
+
+} REG_Q_UNKNOWN_1A;
+
+/* REG_R_UNKNOWN_1A */
+typedef struct r_reg_unk_1a_info
+{
+ uint32 unknown; /* 0x0500 0000 */
+ NTSTATUS status; /* return status */
+
+} REG_R_UNKNOWN_1A;
+
+
+/* REG_Q_UNKNOWN_1A */
+typedef struct q_reg_unknown_14
+{
+ POLICY_HND pol; /* policy handle */
+
+ UNIHDR hdr_file; /* unicode product type header */
+ UNISTR2 uni_file; /* local filename to save key as from regedt32.exe */
+ /* e.g. "c:\temp\test.dat" */
+
+ uint32 unknown; /* 0x0000 0000 */
+
+} REG_Q_SAVE_KEY;
+
+
+/* REG_R_UNKNOWN_1A */
+typedef struct r_reg_unknown_14
+{
+ NTSTATUS status; /* return status */
+
+} REG_R_SAVE_KEY;
+
+
+
+/* REG_Q_CLOSE */
+typedef struct reg_q_close_info
+{
+ POLICY_HND pol; /* policy handle */
+
+} REG_Q_CLOSE;
+
+/* REG_R_CLOSE */
+typedef struct reg_r_close_info
+{
+ POLICY_HND pol; /* policy handle. should be all zeros. */
+
+ NTSTATUS status; /* return code */
+
+} REG_R_CLOSE;
+
+
+/* REG_Q_ENUM_KEY */
+typedef struct q_reg_enum_value_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ uint32 key_index;
+
+ uint16 key_name_len; /* 0x0000 */
+ uint16 unknown_1; /* 0x0414 */
+
+ uint32 ptr1; /* pointer */
+ uint32 unknown_2; /* 0x0000 020A */
+ uint8 pad1[8]; /* padding - zeros */
+
+ uint32 ptr2; /* pointer */
+ uint8 pad2[8]; /* padding - zeros */
+
+ uint32 ptr3; /* pointer */
+ NTTIME time; /* current time? */
+
+} REG_Q_ENUM_KEY;
+
+/* REG_R_ENUM_KEY */
+typedef struct r_reg_enum_key_info
+{
+ uint16 key_name_len; /* number of bytes in key name */
+ uint16 unknown_1; /* 0x0414 - matches with query unknown_1 */
+
+ uint32 ptr1; /* pointer */
+ uint32 unknown_2; /* 0x0000 020A */
+ uint32 unknown_3; /* 0x0000 0000 */
+
+ UNISTR3 key_name;
+
+ uint32 ptr2; /* pointer */
+ uint8 pad2[8]; /* padding - zeros */
+
+ uint32 ptr3; /* pointer */
+ NTTIME time; /* current time? */
+
+ NTSTATUS status; /* return status */
+
+} REG_R_ENUM_KEY;
+
+
+/* REG_Q_INFO */
+typedef struct q_reg_info_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ UNIHDR hdr_type; /* unicode product type header */
+ UNISTR2 uni_type; /* unicode product type - "ProductType" */
+
+ uint32 ptr_reserved; /* pointer */
+
+ uint32 ptr_buf; /* the next three fields follow if ptr_buf != 0 */
+ uint32 ptr_bufsize;
+ uint32 bufsize;
+ uint32 buf_unk;
+
+ uint32 unk1;
+ uint32 ptr_buflen;
+ uint32 buflen;
+
+ uint32 ptr_buflen2;
+ uint32 buflen2;
+
+} REG_Q_INFO;
+
+/* REG_R_INFO */
+typedef struct r_reg_info_info
+{
+ uint32 ptr_type; /* key type pointer */
+ uint32 type; /* key datatype */
+
+ uint32 ptr_uni_val; /* key value pointer */
+ BUFFER2 uni_val; /* key value */
+
+ uint32 ptr_max_len;
+ uint32 buf_max_len;
+
+ uint32 ptr_len;
+ uint32 buf_len;
+
+ NTSTATUS status; /* return status */
+
+} REG_R_INFO;
+
+
+/* REG_Q_OPEN_ENTRY */
+typedef struct q_reg_open_entry_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ UNIHDR hdr_name; /* unicode registry string header */
+ UNISTR2 uni_name; /* unicode registry string name */
+
+ uint32 unknown_0; /* 32 bit unknown - 0x0000 0000 */
+ uint32 access_desired;
+
+} REG_Q_OPEN_ENTRY;
+
+
+
+/* REG_R_OPEN_ENTRY */
+typedef struct r_reg_open_entry_info
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status; /* return status */
+
+} REG_R_OPEN_ENTRY;
+
+/* REG_Q_SHUTDOWN */
+typedef struct q_reg_shutdown_info
+{
+ uint32 ptr_0;
+ uint32 ptr_1;
+ uint32 ptr_2;
+ UNIHDR hdr_msg; /* shutdown message */
+ UNISTR2 uni_msg; /* seconds */
+ uint32 timeout; /* seconds */
+ uint8 force; /* boolean: force shutdown */
+ uint8 reboot; /* boolean: reboot on shutdown */
+
+} REG_Q_SHUTDOWN;
+
+/* REG_R_SHUTDOWN */
+typedef struct r_reg_shutdown_info
+{
+ NTSTATUS status; /* return status */
+
+} REG_R_SHUTDOWN;
+
+/* REG_Q_ABORT_SHUTDOWN */
+typedef struct q_reg_abort_shutdown_info
+{
+ uint32 ptr_server;
+ uint16 server;
+
+} REG_Q_ABORT_SHUTDOWN;
+
+/* REG_R_ABORT_SHUTDOWN */
+typedef struct r_reg_abort_shutdown_info
+{
+ NTSTATUS status; /* return status */
+
+} REG_R_ABORT_SHUTDOWN;
+
+
+#endif /* _RPC_REG_H */
+
diff --git a/source/include/rpc_samr.h b/source/include/rpc_samr.h
new file mode 100644
index 00000000000..111e62c3553
--- /dev/null
+++ b/source/include/rpc_samr.h
@@ -0,0 +1,1862 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Paul Ashton 1997-2000
+ Copyright (C) Jean François Micouleau 1998-2001
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
+
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _RPC_SAMR_H /* _RPC_SAMR_H */
+#define _RPC_SAMR_H
+
+#include "rpc_misc.h"
+
+/*******************************************************************
+ the following information comes from a QuickView on samsrv.dll,
+ and gives an idea of exactly what is needed:
+
+x SamrAddMemberToAlias
+x SamrAddMemberToGroup
+SamrAddMultipleMembersToAlias
+x SamrChangePasswordUser
+x SamrCloseHandle
+x SamrConnect
+x SamrCreateAliasInDomain
+x SamrCreateGroupInDomain
+x SamrCreateUserInDomain
+? SamrDeleteAlias
+SamrDeleteGroup
+x SamrDeleteUser
+x SamrEnumerateAliasesInDomain
+SamrEnumerateDomainsInSamServer
+x SamrEnumerateGroupsInDomain
+x SamrEnumerateUsersInDomain
+SamrGetUserDomainPasswordInformation
+SamrLookupDomainInSamServer
+? SamrLookupIdsInDomain
+x SamrLookupNamesInDomain
+x SamrOpenAlias
+x SamrOpenDomain
+x SamrOpenGroup
+x SamrOpenUser
+x SamrQueryDisplayInformation
+x SamrQueryInformationAlias
+SamrQueryInformationDomain
+? SamrQueryInformationUser
+x SamrQuerySecurityObject
+SamrRemoveMemberFromAlias
+SamrRemoveMemberFromForiegnDomain
+SamrRemoveMemberFromGroup
+SamrRemoveMultipleMembersFromAlias
+x SamrSetInformationAlias
+SamrSetInformationDomain
+x SamrSetInformationGroup
+x SamrSetInformationUser
+SamrSetMemberAttributesOfGroup
+SamrSetSecurityObject
+SamrShutdownSamServer
+SamrTestPrivateFunctionsDomain
+SamrTestPrivateFunctionsUser
+
+********************************************************************/
+
+#define SAMR_CONNECT_ANON 0x00
+#define SAMR_CLOSE_HND 0x01
+#define SAMR_SET_SEC_OBJECT 0x02
+#define SAMR_QUERY_SEC_OBJECT 0x03
+
+#define SAMR_UNKNOWN_4 0x04 /* profile info? */
+#define SAMR_LOOKUP_DOMAIN 0x05
+#define SAMR_ENUM_DOMAINS 0x06
+#define SAMR_OPEN_DOMAIN 0x07
+#define SAMR_QUERY_DOMAIN_INFO 0x08
+#define SAMR_SET_DOMAIN_INFO 0x09
+
+#define SAMR_CREATE_DOM_GROUP 0x0a
+#define SAMR_ENUM_DOM_GROUPS 0x0b
+#define SAMR_ENUM_DOM_USERS 0x0d
+#define SAMR_CREATE_DOM_ALIAS 0x0e
+#define SAMR_ENUM_DOM_ALIASES 0x0f
+#define SAMR_QUERY_USERALIASES 0x10
+
+#define SAMR_LOOKUP_NAMES 0x11
+#define SAMR_LOOKUP_RIDS 0x12
+
+#define SAMR_OPEN_GROUP 0x13
+#define SAMR_QUERY_GROUPINFO 0x14
+#define SAMR_SET_GROUPINFO 0x15
+#define SAMR_ADD_GROUPMEM 0x16
+#define SAMR_DELETE_DOM_GROUP 0x17
+#define SAMR_DEL_GROUPMEM 0x18
+#define SAMR_QUERY_GROUPMEM 0x19
+#define SAMR_UNKNOWN_1A 0x1a
+
+#define SAMR_OPEN_ALIAS 0x1b
+#define SAMR_QUERY_ALIASINFO 0x1c
+#define SAMR_SET_ALIASINFO 0x1d
+#define SAMR_DELETE_DOM_ALIAS 0x1e
+#define SAMR_ADD_ALIASMEM 0x1f
+#define SAMR_DEL_ALIASMEM 0x20
+#define SAMR_QUERY_ALIASMEM 0x21
+
+#define SAMR_OPEN_USER 0x22
+#define SAMR_DELETE_DOM_USER 0x23
+#define SAMR_QUERY_USERINFO 0x24
+#define SAMR_SET_USERINFO2 0x25
+#define SAMR_QUERY_USERGROUPS 0x27
+
+#define SAMR_QUERY_DISPINFO 0x28
+#define SAMR_UNKNOWN_29 0x29
+#define SAMR_UNKNOWN_2a 0x2a
+#define SAMR_UNKNOWN_2b 0x2b
+#define SAMR_GET_USRDOM_PWINFO 0x2c
+#define SAMR_REMOVE_SID_FOREIGN_DOMAIN 0x2d
+#define SAMR_UNKNOWN_2E 0x2e /* looks like an alias for SAMR_QUERY_DOMAIN_INFO */
+#define SAMR_UNKNOWN_2f 0x2f
+#define SAMR_QUERY_DISPINFO3 0x30 /* Alias for SAMR_QUERY_DISPINFO
+ with info level 3 */
+#define SAMR_UNKNOWN_31 0x31
+#define SAMR_CREATE_USER 0x32
+#define SAMR_QUERY_DISPINFO4 0x33 /* Alias for SAMR_QUERY_DISPINFO
+ with info level 4 */
+#define SAMR_ADDMULTI_ALIASMEM 0x34
+
+#define SAMR_UNKNOWN_35 0x35
+#define SAMR_UNKNOWN_36 0x36
+#define SAMR_CHGPASSWD_USER 0x37
+#define SAMR_GET_DOM_PWINFO 0x38
+#define SAMR_CONNECT 0x39
+#define SAMR_SET_USERINFO 0x3A
+#define SAMR_CONNECT4 0x3E
+
+typedef struct logon_hours_info
+{
+ uint32 len; /* normally 21 bytes */
+ uint8 hours[32];
+
+} LOGON_HRS;
+
+/* SAM_USER_INFO_23 */
+typedef struct sam_user_info_23
+{
+ /* TIMES MAY NOT IN RIGHT ORDER!!!! */
+ NTTIME logon_time; /* logon time */
+ NTTIME logoff_time; /* logoff time */
+ NTTIME kickoff_time; /* kickoff time */
+ NTTIME pass_last_set_time; /* password last set time */
+ NTTIME pass_can_change_time; /* password can change time */
+ NTTIME pass_must_change_time; /* password must change time */
+
+ UNIHDR hdr_user_name; /* NULL - user name unicode string header */
+ UNIHDR hdr_full_name; /* user's full name unicode string header */
+ UNIHDR hdr_home_dir; /* home directory unicode string header */
+ UNIHDR hdr_dir_drive; /* home drive unicode string header */
+ UNIHDR hdr_logon_script; /* logon script unicode string header */
+ UNIHDR hdr_profile_path; /* profile path unicode string header */
+ UNIHDR hdr_acct_desc ; /* user description */
+ UNIHDR hdr_workstations; /* comma-separated workstations user can log in from */
+ UNIHDR hdr_unknown_str ; /* don't know what this is, yet. */
+ UNIHDR hdr_munged_dial ; /* munged path name and dial-back tel number */
+
+ uint8 lm_pwd[16]; /* lm user passwords */
+ uint8 nt_pwd[16]; /* nt user passwords */
+
+ uint32 user_rid; /* Primary User ID */
+ uint32 group_rid; /* Primary Group ID */
+
+ uint32 acb_info; /* account info (ACB_xxxx bit-mask) */
+
+ uint32 fields_present; /* 0x09f8 27fa */
+
+ uint16 logon_divs; /* 0x0000 00a8 which is 168 which is num hrs in a week */
+ /* uint8 pad[2] */
+ uint32 ptr_logon_hrs; /* pointer to logon hours */
+
+ /* Was unknown_5. */
+ uint16 bad_password_count;
+ uint16 logon_count;
+
+ uint8 padding1[6];
+
+ uint8 passmustchange; /* 0x00 must change = 0x01 */
+
+ uint8 padding2;
+
+ uint8 pass[516];
+
+ UNISTR2 uni_user_name; /* NULL - username unicode string */
+ UNISTR2 uni_full_name; /* user's full name unicode string */
+ UNISTR2 uni_home_dir; /* home directory unicode string */
+ UNISTR2 uni_dir_drive; /* home directory drive unicode string */
+ UNISTR2 uni_logon_script; /* logon script unicode string */
+ UNISTR2 uni_profile_path; /* profile path unicode string */
+ UNISTR2 uni_acct_desc ; /* user description unicode string */
+ UNISTR2 uni_workstations; /* login from workstations unicode string */
+ UNISTR2 uni_unknown_str ; /* don't know what this is, yet. */
+ UNISTR2 uni_munged_dial ; /* munged path name and dial-back tel no */
+
+ uint32 unknown_6; /* 0x0000 04ec */
+ uint32 padding4;
+
+ LOGON_HRS logon_hrs;
+
+} SAM_USER_INFO_23;
+
+/* SAM_USER_INFO_24 */
+typedef struct sam_user_info_24
+{
+ uint8 pass[516];
+ uint16 pw_len;
+} SAM_USER_INFO_24;
+
+/*
+ * NB. This structure is *definately* incorrect. It's my best guess
+ * currently for W2K SP2. The password field is encrypted in a different
+ * way than normal... And there are definately other problems. JRA.
+ */
+
+/* SAM_USER_INFO_25 */
+typedef struct sam_user_info_25
+{
+ /* TIMES MAY NOT IN RIGHT ORDER!!!! */
+ NTTIME logon_time; /* logon time */
+ NTTIME logoff_time; /* logoff time */
+ NTTIME kickoff_time; /* kickoff time */
+ NTTIME pass_last_set_time; /* password last set time */
+ NTTIME pass_can_change_time; /* password can change time */
+ NTTIME pass_must_change_time; /* password must change time */
+
+ UNIHDR hdr_user_name; /* NULL - user name unicode string header */
+ UNIHDR hdr_full_name; /* user's full name unicode string header */
+ UNIHDR hdr_home_dir; /* home directory unicode string header */
+ UNIHDR hdr_dir_drive; /* home drive unicode string header */
+ UNIHDR hdr_logon_script; /* logon script unicode string header */
+ UNIHDR hdr_profile_path; /* profile path unicode string header */
+ UNIHDR hdr_acct_desc ; /* user description */
+ UNIHDR hdr_workstations; /* comma-separated workstations user can log in from */
+ UNIHDR hdr_unknown_str ; /* don't know what this is, yet. */
+ UNIHDR hdr_munged_dial ; /* munged path name and dial-back tel number */
+
+ uint8 lm_pwd[16]; /* lm user passwords */
+ uint8 nt_pwd[16]; /* nt user passwords */
+
+ uint32 user_rid; /* Primary User ID */
+ uint32 group_rid; /* Primary Group ID */
+
+ uint32 acb_info; /* account info (ACB_xxxx bit-mask) */
+
+ uint32 unknown_6[6];
+
+ uint8 pass[532];
+
+ UNISTR2 uni_user_name; /* NULL - username unicode string */
+ UNISTR2 uni_full_name; /* user's full name unicode string */
+ UNISTR2 uni_home_dir; /* home directory unicode string */
+ UNISTR2 uni_dir_drive; /* home directory drive unicode string */
+ UNISTR2 uni_logon_script; /* logon script unicode string */
+ UNISTR2 uni_profile_path; /* profile path unicode string */
+ UNISTR2 uni_acct_desc ; /* user description unicode string */
+ UNISTR2 uni_workstations; /* login from workstations unicode string */
+ UNISTR2 uni_unknown_str ; /* don't know what this is, yet. */
+ UNISTR2 uni_munged_dial ; /* munged path name and dial-back tel no */
+} SAM_USER_INFO_25;
+
+
+/* SAM_USER_INFO_21 */
+typedef struct sam_user_info_21
+{
+ NTTIME logon_time; /* logon time */
+ NTTIME logoff_time; /* logoff time */
+ NTTIME kickoff_time; /* kickoff time */
+ NTTIME pass_last_set_time; /* password last set time */
+ NTTIME pass_can_change_time; /* password can change time */
+ NTTIME pass_must_change_time; /* password must change time */
+
+ UNIHDR hdr_user_name; /* username unicode string header */
+ UNIHDR hdr_full_name; /* user's full name unicode string header */
+ UNIHDR hdr_home_dir; /* home directory unicode string header */
+ UNIHDR hdr_dir_drive; /* home drive unicode string header */
+ UNIHDR hdr_logon_script; /* logon script unicode string header */
+ UNIHDR hdr_profile_path; /* profile path unicode string header */
+ UNIHDR hdr_acct_desc ; /* user description */
+ UNIHDR hdr_workstations; /* comma-separated workstations user can log in from */
+ UNIHDR hdr_unknown_str ; /* don't know what this is, yet. */
+ UNIHDR hdr_munged_dial ; /* munged path name and dial-back tel number */
+
+ uint8 lm_pwd[16]; /* lm user passwords */
+ uint8 nt_pwd[16]; /* nt user passwords */
+
+ uint32 user_rid; /* Primary User ID */
+ uint32 group_rid; /* Primary Group ID */
+
+ uint32 acb_info; /* account info (ACB_xxxx bit-mask) */
+
+ /* Was unknown_3 */
+ uint32 fields_present; /* 0x00ff ffff */
+
+ uint16 logon_divs; /* 0x0000 00a8 which is 168 which is num hrs in a week */
+ /* uint8 pad[2] */
+ uint32 ptr_logon_hrs; /* unknown pointer */
+
+ /* Was unknown_5. */
+ uint16 bad_password_count;
+ uint16 logon_count;
+
+ uint8 padding1[6];
+
+ uint8 passmustchange; /* 0x00 must change = 0x01 */
+
+ uint8 padding2;
+
+ UNISTR2 uni_user_name; /* username unicode string */
+ UNISTR2 uni_full_name; /* user's full name unicode string */
+ UNISTR2 uni_home_dir; /* home directory unicode string */
+ UNISTR2 uni_dir_drive; /* home directory drive unicode string */
+ UNISTR2 uni_logon_script; /* logon script unicode string */
+ UNISTR2 uni_profile_path; /* profile path unicode string */
+ UNISTR2 uni_acct_desc ; /* user description unicode string */
+ UNISTR2 uni_workstations; /* login from workstations unicode string */
+ UNISTR2 uni_unknown_str ; /* don't know what this is, yet. */
+ UNISTR2 uni_munged_dial ; /* munged path name and dial-back tel number */
+
+ uint32 unknown_6; /* 0x0000 04ec */
+ uint32 padding4;
+
+ LOGON_HRS logon_hrs;
+
+} SAM_USER_INFO_21;
+
+#define PASS_MUST_CHANGE_AT_NEXT_LOGON 0x01
+#define PASS_DONT_CHANGE_AT_NEXT_LOGON 0x00
+
+/* SAM_USER_INFO_20 */
+typedef struct sam_user_info_20
+{
+ UNIHDR hdr_munged_dial ; /* munged path name and dial-back tel number */
+
+ UNISTR2 uni_munged_dial ; /* munged path name and dial-back tel number */
+
+} SAM_USER_INFO_20;
+
+/* SAM_USER_INFO_12 */
+typedef struct sam_user_info_12
+{
+ uint8 lm_pwd[16]; /* lm user passwords */
+ uint8 nt_pwd[16]; /* nt user passwords */
+
+ uint8 lm_pwd_active;
+ uint8 nt_pwd_active;
+
+} SAM_USER_INFO_12;
+
+/* SAM_USER_INFO_11 */
+typedef struct sam_user_info_11
+{
+ uint8 padding_0[16]; /* 0 - padding 16 bytes */
+ NTTIME expiry; /* expiry time or something? */
+ uint8 padding_1[24]; /* 0 - padding 24 bytes */
+
+ UNIHDR hdr_mach_acct; /* unicode header for machine account */
+ uint32 padding_2; /* 0 - padding 4 bytes */
+
+ uint32 ptr_1; /* pointer */
+ uint8 padding_3[32]; /* 0 - padding 32 bytes */
+ uint32 padding_4; /* 0 - padding 4 bytes */
+
+ uint32 ptr_2; /* pointer */
+ uint32 padding_5; /* 0 - padding 4 bytes */
+
+ uint32 ptr_3; /* pointer */
+ uint8 padding_6[32]; /* 0 - padding 32 bytes */
+
+ uint32 rid_user; /* user RID */
+ uint32 rid_group; /* group RID */
+
+ uint16 acct_ctrl; /* 0080 - ACB_XXXX */
+ uint16 unknown_3; /* 16 bit padding */
+
+ uint16 unknown_4; /* 0x003f - 16 bit unknown */
+ uint16 unknown_5; /* 0x003c - 16 bit unknown */
+
+ uint8 padding_7[16]; /* 0 - padding 16 bytes */
+ uint32 padding_8; /* 0 - padding 4 bytes */
+
+ UNISTR2 uni_mach_acct; /* unicode string for machine account */
+
+ uint8 padding_9[48]; /* 0 - padding 48 bytes */
+
+} SAM_USER_INFO_11;
+
+
+/* SAM_USER_INFO_10 */
+typedef struct sam_user_info_10
+{
+ uint32 acb_info;
+
+} SAM_USER_INFO_10;
+
+
+
+/* SAMR_Q_CLOSE_HND - probably a policy handle close */
+typedef struct q_samr_close_hnd_info
+{
+ POLICY_HND pol; /* policy handle */
+
+} SAMR_Q_CLOSE_HND;
+
+
+/* SAMR_R_CLOSE_HND - probably a policy handle close */
+typedef struct r_samr_close_hnd_info
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status; /* return status */
+
+} SAMR_R_CLOSE_HND;
+
+
+/****************************************************************************
+SAMR_Q_GET_USRDOM_PWINFO - a "set user info" occurs just after this
+*****************************************************************************/
+
+/* SAMR_Q_GET_USRDOM_PWINFO */
+typedef struct q_samr_usrdom_pwinfo_info
+{
+ POLICY_HND user_pol; /* policy handle */
+
+} SAMR_Q_GET_USRDOM_PWINFO;
+
+
+/****************************************************************************
+SAMR_R_GET_USRDOM_PWINFO - a "set user info" occurs just after this
+*****************************************************************************/
+
+/* SAMR_R_GET_USRDOM_PWINFO */
+typedef struct r_samr_usrdom_pwinfo_info
+{
+ uint16 unknown_0; /* 0000 */
+ uint16 unknown_1; /* 0x0016 or 0x0015 */
+ uint32 unknown_2; /* 0x0000 0000 */
+ NTSTATUS status;
+
+} SAMR_R_GET_USRDOM_PWINFO;
+
+/****************************************************************************
+SAMR_Q_SET_SEC_OBJ - info level 4.
+*****************************************************************************/
+
+/* SAMR_Q_SET_SEC_OBJ - */
+typedef struct q_samr_set_sec_obj_info
+{
+ POLICY_HND pol; /* policy handle */
+ uint32 sec_info; /* xxxx_SECURITY_INFORMATION 0x0000 0004 */
+ SEC_DESC_BUF *buf;
+
+} SAMR_Q_SET_SEC_OBJ;
+
+/* SAMR_R_SET_SEC_OBJ - */
+typedef struct r_samr_set_sec_obj_info
+{
+ NTSTATUS status; /* return status */
+
+} SAMR_R_SET_SEC_OBJ;
+
+
+/****************************************************************************
+SAMR_Q_QUERY_SEC_OBJ - info level 4. returns SIDs.
+*****************************************************************************/
+
+/* SAMR_Q_QUERY_SEC_OBJ - probably get domain info... */
+typedef struct q_samr_query_sec_obj_info
+{
+ POLICY_HND user_pol; /* policy handle */
+ uint32 sec_info; /* xxxx_SECURITY_INFORMATION 0x0000 0004 */
+
+} SAMR_Q_QUERY_SEC_OBJ;
+
+/* SAMR_R_QUERY_SEC_OBJ - probably an open */
+typedef struct r_samr_query_sec_obj_info
+{
+ uint32 ptr;
+ SEC_DESC_BUF *buf;
+
+ NTSTATUS status; /* return status */
+
+} SAMR_R_QUERY_SEC_OBJ;
+
+
+/****************************************************************************
+SAMR_Q_QUERY_DOMAIN_INFO - probably a query on domain group info.
+*****************************************************************************/
+
+/* SAMR_Q_QUERY_DOMAIN_INFO - */
+typedef struct q_samr_query_domain_info
+{
+ POLICY_HND domain_pol; /* policy handle */
+ uint16 switch_value; /* 0x0002, 0x0001 */
+
+} SAMR_Q_QUERY_DOMAIN_INFO;
+
+typedef struct sam_unknown_info_3_info
+{
+ NTTIME logout;
+ /* 0x8000 0000 */ /* DON'T forcibly disconnect remote users from server when logon hours expire*/
+
+ /* 0x0000 0000 */ /* forcibly disconnect remote users from server when logon hours expire*/
+
+} SAM_UNK_INFO_3;
+
+typedef struct sam_unknown_info_6_info
+{
+ uint32 unknown_0; /* 0x0000 0000 */
+
+ uint32 ptr_0; /* pointer to unknown structure */
+ uint8 padding[12]; /* 12 bytes zeros */
+
+} SAM_UNK_INFO_6;
+
+typedef struct sam_unknown_info_7_info
+{
+ uint16 unknown_0; /* 0x0003 */
+
+} SAM_UNK_INFO_7;
+
+typedef struct sam_unknown_info_12_inf
+{
+ NTTIME duration;
+ NTTIME reset_count;
+ uint16 bad_attempt_lockout;
+
+} SAM_UNK_INFO_12;
+
+typedef struct sam_unknown_info_5_inf
+{
+ UNIHDR hdr_server; /* server name unicode header */
+ UNISTR2 uni_server; /* server name unicode string */
+
+} SAM_UNK_INFO_5;
+
+typedef struct sam_unknown_info_2_inf
+{
+ uint32 unknown_0; /* 0x0000 0000 */
+ uint32 unknown_1; /* 0x8000 0000 */
+ uint32 unknown_2; /* 0x0000 0000 */
+
+ uint32 ptr_0; /* pointer to unknown structure */
+ UNIHDR hdr_domain; /* domain name unicode header */
+ UNIHDR hdr_server; /* server name unicode header */
+
+ /* put all the data in here, at the moment, including what the above
+ pointer is referring to
+ */
+
+ uint32 seq_num; /* some sort of incrementing sequence number? */
+ uint32 unknown_3; /* 0x0000 0000 */
+
+ uint32 unknown_4; /* 0x0000 0001 */
+ uint32 unknown_5; /* 0x0000 0003 */
+ uint32 unknown_6; /* 0x0000 0001 */
+ uint32 num_domain_usrs; /* number of users in domain */
+ uint32 num_domain_grps; /* number of domain groups in domain */
+ uint32 num_local_grps; /* number of local groups in domain */
+
+ uint8 padding[12]; /* 12 bytes zeros */
+
+ UNISTR2 uni_domain; /* domain name unicode string */
+ UNISTR2 uni_server; /* server name unicode string */
+
+} SAM_UNK_INFO_2;
+
+typedef struct sam_unknown_info_1_inf
+{
+ uint16 min_length_password;
+ uint16 password_history;
+ uint32 flag;
+ NTTIME expire;
+ NTTIME min_passwordage;
+
+} SAM_UNK_INFO_1;
+
+
+typedef struct sam_unknown_ctr_info
+{
+ union
+ {
+ SAM_UNK_INFO_1 inf1;
+ SAM_UNK_INFO_2 inf2;
+ SAM_UNK_INFO_3 inf3;
+ SAM_UNK_INFO_5 inf5;
+ SAM_UNK_INFO_6 inf6;
+ SAM_UNK_INFO_7 inf7;
+ SAM_UNK_INFO_12 inf12;
+
+ } info;
+
+} SAM_UNK_CTR;
+
+
+/* SAMR_R_QUERY_DOMAIN_INFO - */
+typedef struct r_samr_query_domain_info
+{
+ uint32 ptr_0;
+ uint16 switch_value; /* same as in query */
+
+ SAM_UNK_CTR *ctr;
+
+ NTSTATUS status; /* return status */
+
+} SAMR_R_QUERY_DOMAIN_INFO;
+
+
+/* SAMR_Q_LOOKUP_DOMAIN - obtain SID for a local domain */
+typedef struct q_samr_lookup_domain_info
+{
+ POLICY_HND connect_pol;
+
+ UNIHDR hdr_domain;
+ UNISTR2 uni_domain;
+
+} SAMR_Q_LOOKUP_DOMAIN;
+
+
+/* SAMR_R_LOOKUP_DOMAIN */
+typedef struct r_samr_lookup_domain_info
+{
+ uint32 ptr_sid;
+ DOM_SID2 dom_sid;
+
+ NTSTATUS status;
+
+} SAMR_R_LOOKUP_DOMAIN;
+
+
+/****************************************************************************
+SAMR_Q_OPEN_DOMAIN - unknown_0 values seen associated with SIDs:
+
+0x0000 03f1 and a specific domain sid - S-1-5-21-44c01ca6-797e5c3d-33f83fd0
+0x0000 0200 and a specific domain sid - S-1-5-21-44c01ca6-797e5c3d-33f83fd0
+*****************************************************************************/
+
+/* SAMR_Q_OPEN_DOMAIN */
+typedef struct q_samr_open_domain_info
+{
+ POLICY_HND pol; /* policy handle */
+ uint32 flags; /* 0x2000 0000; 0x0000 0211; 0x0000 0280; 0x0000 0200 - flags? */
+ DOM_SID2 dom_sid; /* domain SID */
+
+} SAMR_Q_OPEN_DOMAIN;
+
+
+/* SAMR_R_OPEN_DOMAIN - probably an open */
+typedef struct r_samr_open_domain_info
+{
+ POLICY_HND domain_pol; /* policy handle associated with the SID */
+ NTSTATUS status; /* return status */
+
+} SAMR_R_OPEN_DOMAIN;
+
+#define MAX_SAM_ENTRIES_W2K 0x400
+#define MAX_SAM_ENTRIES_W95 50
+/* The following should be the greater of the preceeding two. */
+#define MAX_SAM_ENTRIES MAX_SAM_ENTRIES_W2K
+
+typedef struct samr_entry_info
+{
+ uint32 rid;
+ UNIHDR hdr_name;
+
+} SAM_ENTRY;
+
+
+/* SAMR_Q_ENUM_DOMAINS - SAM rids and names */
+typedef struct q_samr_enum_domains_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ uint32 start_idx; /* enumeration handle */
+ uint32 max_size; /* 0x0000 ffff */
+
+} SAMR_Q_ENUM_DOMAINS;
+
+/* SAMR_R_ENUM_DOMAINS - SAM rids and Domain names */
+typedef struct r_samr_enum_domains_info
+{
+ uint32 next_idx; /* next starting index required for enum */
+ uint32 ptr_entries1;
+
+ uint32 num_entries2;
+ uint32 ptr_entries2;
+
+ uint32 num_entries3;
+
+ SAM_ENTRY *sam;
+ UNISTR2 *uni_dom_name;
+
+ uint32 num_entries4;
+
+ NTSTATUS status;
+
+} SAMR_R_ENUM_DOMAINS;
+
+/* SAMR_Q_ENUM_DOM_USERS - SAM rids and names */
+typedef struct q_samr_enum_dom_users_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ uint32 start_idx; /* number of values (0 indicates unlimited?) */
+ uint16 acb_mask; /* 0x0000 indicates all */
+ uint16 unknown_1; /* 0x0000 */
+
+ uint32 max_size; /* 0x0000 ffff */
+
+} SAMR_Q_ENUM_DOM_USERS;
+
+
+/* SAMR_R_ENUM_DOM_USERS - SAM rids and names */
+typedef struct r_samr_enum_dom_users_info
+{
+ uint32 next_idx; /* next starting index required for enum */
+ uint32 ptr_entries1;
+
+ uint32 num_entries2;
+ uint32 ptr_entries2;
+
+ uint32 num_entries3;
+
+ SAM_ENTRY *sam;
+ UNISTR2 *uni_acct_name;
+
+ uint32 num_entries4;
+
+ NTSTATUS status;
+
+} SAMR_R_ENUM_DOM_USERS;
+
+
+/* SAMR_Q_ENUM_DOM_GROUPS - SAM rids and names */
+typedef struct q_samr_enum_dom_groups_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ /* this is possibly an enumeration context handle... */
+ uint32 start_idx; /* 0x0000 0000 */
+
+ uint32 max_size; /* 0x0000 ffff */
+
+} SAMR_Q_ENUM_DOM_GROUPS;
+
+
+/* SAMR_R_ENUM_DOM_GROUPS - SAM rids and names */
+typedef struct r_samr_enum_dom_groups_info
+{
+ uint32 next_idx;
+ uint32 ptr_entries1;
+
+ uint32 num_entries2;
+ uint32 ptr_entries2;
+
+ uint32 num_entries3;
+
+ SAM_ENTRY *sam;
+ UNISTR2 *uni_grp_name;
+
+ uint32 num_entries4;
+
+ NTSTATUS status;
+
+} SAMR_R_ENUM_DOM_GROUPS;
+
+
+/* SAMR_Q_ENUM_DOM_ALIASES - SAM rids and names */
+typedef struct q_samr_enum_dom_aliases_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ /* this is possibly an enumeration context handle... */
+ uint32 start_idx; /* 0x0000 0000 */
+
+ uint32 max_size; /* 0x0000 ffff */
+
+} SAMR_Q_ENUM_DOM_ALIASES;
+
+
+/* SAMR_R_ENUM_DOM_ALIASES - SAM rids and names */
+typedef struct r_samr_enum_dom_aliases_info
+{
+ uint32 next_idx;
+ uint32 ptr_entries1;
+
+ uint32 num_entries2;
+ uint32 ptr_entries2;
+
+ uint32 num_entries3;
+
+ SAM_ENTRY *sam;
+ UNISTR2 *uni_grp_name;
+
+ uint32 num_entries4;
+
+ NTSTATUS status;
+
+} SAMR_R_ENUM_DOM_ALIASES;
+
+
+/* -- Level 1 Display Info - User Information -- */
+
+typedef struct samr_entry_info1
+{
+ uint32 user_idx;
+
+ uint32 rid_user;
+ uint16 acb_info;
+
+ UNIHDR hdr_acct_name;
+ UNIHDR hdr_user_name;
+ UNIHDR hdr_user_desc;
+
+} SAM_ENTRY1;
+
+typedef struct samr_str_entry_info1
+{
+ UNISTR2 uni_acct_name;
+ UNISTR2 uni_full_name;
+ UNISTR2 uni_acct_desc;
+
+} SAM_STR1;
+
+typedef struct sam_entry_info_1
+{
+ SAM_ENTRY1 *sam;
+ SAM_STR1 *str;
+
+} SAM_DISPINFO_1;
+
+
+/* -- Level 2 Display Info - Trust Account Information -- */
+
+typedef struct samr_entry_info2
+{
+ uint32 user_idx;
+
+ uint32 rid_user;
+ uint16 acb_info;
+
+ UNIHDR hdr_srv_name;
+ UNIHDR hdr_srv_desc;
+
+} SAM_ENTRY2;
+
+typedef struct samr_str_entry_info2
+{
+ UNISTR2 uni_srv_name;
+ UNISTR2 uni_srv_desc;
+
+} SAM_STR2;
+
+typedef struct sam_entry_info_2
+{
+ SAM_ENTRY2 *sam;
+ SAM_STR2 *str;
+
+} SAM_DISPINFO_2;
+
+
+/* -- Level 3 Display Info - Domain Group Information -- */
+
+typedef struct samr_entry_info3
+{
+ uint32 grp_idx;
+
+ uint32 rid_grp;
+ uint32 attr; /* SE_GROUP_xxx, usually 7 */
+
+ UNIHDR hdr_grp_name;
+ UNIHDR hdr_grp_desc;
+
+} SAM_ENTRY3;
+
+typedef struct samr_str_entry_info3
+{
+ UNISTR2 uni_grp_name;
+ UNISTR2 uni_grp_desc;
+
+} SAM_STR3;
+
+typedef struct sam_entry_info_3
+{
+ SAM_ENTRY3 *sam;
+ SAM_STR3 *str;
+
+} SAM_DISPINFO_3;
+
+
+/* -- Level 4 Display Info - User List (ASCII) -- */
+
+typedef struct samr_entry_info4
+{
+ uint32 user_idx;
+ STRHDR hdr_acct_name;
+
+} SAM_ENTRY4;
+
+typedef struct samr_str_entry_info4
+{
+ STRING2 acct_name;
+
+} SAM_STR4;
+
+typedef struct sam_entry_info_4
+{
+ SAM_ENTRY4 *sam;
+ SAM_STR4 *str;
+
+} SAM_DISPINFO_4;
+
+
+/* -- Level 5 Display Info - Group List (ASCII) -- */
+
+typedef struct samr_entry_info5
+{
+ uint32 grp_idx;
+ STRHDR hdr_grp_name;
+
+} SAM_ENTRY5;
+
+typedef struct samr_str_entry_info5
+{
+ STRING2 grp_name;
+
+} SAM_STR5;
+
+typedef struct sam_entry_info_5
+{
+ SAM_ENTRY5 *sam;
+ SAM_STR5 *str;
+
+} SAM_DISPINFO_5;
+
+
+typedef struct sam_dispinfo_ctr_info
+{
+ union
+ {
+ SAM_DISPINFO_1 *info1; /* users/names/descriptions */
+ SAM_DISPINFO_2 *info2; /* trust accounts */
+ SAM_DISPINFO_3 *info3; /* domain groups/descriptions */
+ SAM_DISPINFO_4 *info4; /* user list (ASCII) - used by Win95 */
+ SAM_DISPINFO_5 *info5; /* group list (ASCII) */
+ void *info; /* allows assignment without typecasting, */
+
+ } sam;
+
+} SAM_DISPINFO_CTR;
+
+
+/* SAMR_Q_QUERY_DISPINFO - SAM rids, names and descriptions */
+typedef struct q_samr_query_disp_info
+{
+ POLICY_HND domain_pol;
+
+ uint16 switch_level; /* see SAM_DISPINFO_CTR above */
+ /* align */
+
+ uint32 start_idx; /* start enumeration index */
+ uint32 max_entries; /* maximum number of entries to return */
+ uint32 max_size; /* recommended data size; if exceeded server
+ should return STATUS_MORE_ENTRIES */
+
+} SAMR_Q_QUERY_DISPINFO;
+
+
+/* SAMR_R_QUERY_DISPINFO */
+typedef struct r_samr_query_dispinfo_info
+{
+ uint32 total_size; /* total data size for all matching entries
+ (0 = uncalculated) */
+ uint32 data_size; /* actual data size returned = size of SAM_ENTRY
+ structures + total length of strings */
+
+ uint16 switch_level; /* see SAM_DISPINFO_CTR above */
+ /* align */
+
+ uint32 num_entries; /* number of entries returned */
+ uint32 ptr_entries;
+ uint32 num_entries2;
+
+ SAM_DISPINFO_CTR *ctr;
+
+ NTSTATUS status;
+
+} SAMR_R_QUERY_DISPINFO;
+
+
+/* SAMR_Q_DELETE_DOM_GROUP - delete domain group */
+typedef struct q_samr_delete_dom_group_info
+{
+ POLICY_HND group_pol; /* policy handle */
+
+} SAMR_Q_DELETE_DOM_GROUP;
+
+
+/* SAMR_R_DELETE_DOM_GROUP - delete domain group */
+typedef struct r_samr_delete_dom_group_info
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status; /* return status */
+
+} SAMR_R_DELETE_DOM_GROUP;
+
+
+/* SAMR_Q_CREATE_DOM_GROUP - SAM create group */
+typedef struct q_samr_create_dom_group_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ UNIHDR hdr_acct_desc;
+ UNISTR2 uni_acct_desc;
+
+ uint32 access_mask;
+
+} SAMR_Q_CREATE_DOM_GROUP;
+
+/* SAMR_R_CREATE_DOM_GROUP - SAM create group */
+typedef struct r_samr_create_dom_group_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ uint32 rid;
+ NTSTATUS status;
+
+} SAMR_R_CREATE_DOM_GROUP;
+
+/* SAMR_Q_QUERY_GROUPINFO - SAM Group Info */
+typedef struct q_samr_query_group_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ uint16 switch_level; /* 0x0001 seen */
+
+} SAMR_Q_QUERY_GROUPINFO;
+
+typedef struct samr_group_info1
+{
+ UNIHDR hdr_acct_name;
+
+ uint32 unknown_1; /* 0x0000 0003 - number of group members? */
+ uint32 num_members; /* 0x0000 0001 - number of group members? */
+
+ UNIHDR hdr_acct_desc;
+
+ UNISTR2 uni_acct_name;
+ UNISTR2 uni_acct_desc;
+
+} GROUP_INFO1;
+
+typedef struct samr_group_info3
+{
+ uint32 unknown_1; /* 0x0000 0003 - number of group members? */
+
+} GROUP_INFO3;
+
+typedef struct samr_group_info4
+{
+ uint16 level;
+ UNIHDR hdr_acct_desc;
+ UNISTR2 uni_acct_desc;
+
+} GROUP_INFO4;
+
+/* GROUP_INFO_CTR */
+typedef struct group_info_ctr
+{
+ uint16 switch_value1;
+
+ union
+ {
+ GROUP_INFO1 info1;
+ GROUP_INFO3 info3;
+ GROUP_INFO4 info4;
+
+ } group;
+
+} GROUP_INFO_CTR;
+
+/* SAMR_R_QUERY_GROUPINFO - SAM Group Info */
+typedef struct r_samr_query_groupinfo_info
+{
+ uint32 ptr;
+ GROUP_INFO_CTR *ctr;
+
+ NTSTATUS status;
+
+} SAMR_R_QUERY_GROUPINFO;
+
+
+/* SAMR_Q_SET_GROUPINFO - SAM Group Info */
+typedef struct q_samr_set_group_info
+{
+ POLICY_HND pol; /* policy handle */
+ GROUP_INFO_CTR *ctr;
+
+} SAMR_Q_SET_GROUPINFO;
+
+/* SAMR_R_SET_GROUPINFO - SAM Group Info */
+typedef struct r_samr_set_group_info
+{
+ NTSTATUS status;
+
+} SAMR_R_SET_GROUPINFO;
+
+
+/* SAMR_Q_DELETE_DOM_ALIAS - delete domain alias */
+typedef struct q_samr_delete_dom_alias_info
+{
+ POLICY_HND alias_pol; /* policy handle */
+
+} SAMR_Q_DELETE_DOM_ALIAS;
+
+
+/* SAMR_R_DELETE_DOM_ALIAS - delete domain alias */
+typedef struct r_samr_delete_dom_alias_info
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status; /* return status */
+
+} SAMR_R_DELETE_DOM_ALIAS;
+
+
+/* SAMR_Q_CREATE_DOM_ALIAS - SAM create alias */
+typedef struct q_samr_create_dom_alias_info
+{
+ POLICY_HND dom_pol; /* policy handle */
+
+ UNIHDR hdr_acct_desc;
+ UNISTR2 uni_acct_desc;
+
+ uint32 access_mask; /* 0x001f000f */
+
+} SAMR_Q_CREATE_DOM_ALIAS;
+
+/* SAMR_R_CREATE_DOM_ALIAS - SAM create alias */
+typedef struct r_samr_create_dom_alias_info
+{
+ POLICY_HND alias_pol; /* policy handle */
+
+ uint32 rid;
+ NTSTATUS status;
+
+} SAMR_R_CREATE_DOM_ALIAS;
+
+/* SAMR_Q_QUERY_ALIASINFO - SAM Alias Info */
+typedef struct q_samr_query_alias_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ uint16 switch_level; /* 0x0003 seen */
+
+} SAMR_Q_QUERY_ALIASINFO;
+
+typedef struct samr_alias_info1
+{
+ UNIHDR hdr_acct_name;
+ UNIHDR hdr_acct_desc;
+ uint32 num_member;
+ UNISTR2 uni_acct_name;
+ UNISTR2 uni_acct_desc;
+
+} ALIAS_INFO1;
+
+typedef struct samr_alias_info3
+{
+ UNIHDR hdr_acct_desc;
+ UNISTR2 uni_acct_desc;
+
+} ALIAS_INFO3;
+
+/* ALIAS_INFO_CTR */
+typedef struct alias_info_ctr
+{
+ uint16 switch_value1;
+ uint16 switch_value2;
+
+ union
+ {
+ ALIAS_INFO1 info1;
+ ALIAS_INFO3 info3;
+
+ } alias;
+
+} ALIAS_INFO_CTR;
+
+/* SAMR_R_QUERY_ALIASINFO - SAM alias info */
+typedef struct r_samr_query_aliasinfo_info
+{
+ uint32 ptr;
+ ALIAS_INFO_CTR ctr;
+
+ NTSTATUS status;
+
+} SAMR_R_QUERY_ALIASINFO;
+
+
+/* SAMR_Q_SET_ALIASINFO - SAM Alias Info */
+typedef struct q_samr_set_alias_info
+{
+ POLICY_HND alias_pol; /* policy handle */
+ ALIAS_INFO_CTR ctr;
+
+} SAMR_Q_SET_ALIASINFO;
+
+/* SAMR_R_SET_ALIASINFO - SAM alias info */
+typedef struct r_samr_set_aliasinfo_info
+{
+ NTSTATUS status;
+
+} SAMR_R_SET_ALIASINFO;
+
+
+/* SAMR_Q_QUERY_USERGROUPS - */
+typedef struct q_samr_query_usergroup_info
+{
+ POLICY_HND pol; /* policy handle associated with unknown id */
+
+} SAMR_Q_QUERY_USERGROUPS;
+
+/* SAMR_R_QUERY_USERGROUPS - probably a get sam info */
+typedef struct r_samr_query_usergroup_info
+{
+ uint32 ptr_0; /* pointer */
+ uint32 num_entries; /* number of RID groups */
+ uint32 ptr_1; /* pointer */
+ uint32 num_entries2; /* number of RID groups */
+
+ DOM_GID *gid; /* group info */
+
+ NTSTATUS status; /* return status */
+
+} SAMR_R_QUERY_USERGROUPS;
+
+/* SAM_USERINFO_CTR - sam user info */
+typedef struct sam_userinfo_ctr_info
+{
+ uint16 switch_value;
+
+ union
+ {
+ SAM_USER_INFO_10 *id10; /* auth-level 0x10 */
+ SAM_USER_INFO_11 *id11; /* auth-level 0x11 */
+ SAM_USER_INFO_12 *id12; /* auth-level 0x12 */
+ SAM_USER_INFO_20 *id20; /* auth-level 20 */
+ SAM_USER_INFO_21 *id21; /* auth-level 21 */
+ SAM_USER_INFO_23 *id23; /* auth-level 0x17 */
+ SAM_USER_INFO_24 *id24; /* auth-level 0x18 */
+ SAM_USER_INFO_25 *id25; /* auth-level 0x19 */
+ void* id; /* to make typecasting easy */
+
+ } info;
+
+} SAM_USERINFO_CTR;
+
+
+/* SAMR_Q_SET_USERINFO2 - set sam info */
+typedef struct q_samr_set_user_info2
+{
+ POLICY_HND pol; /* policy handle associated with user */
+ uint16 switch_value; /* 0x0010 */
+
+ SAM_USERINFO_CTR *ctr;
+
+} SAMR_Q_SET_USERINFO2;
+
+/* SAMR_R_SET_USERINFO2 - set sam info */
+typedef struct r_samr_set_user_info2
+{
+ NTSTATUS status; /* return status */
+
+} SAMR_R_SET_USERINFO2;
+
+/* SAMR_Q_SET_USERINFO - set sam info */
+typedef struct q_samr_set_user_info
+{
+ POLICY_HND pol; /* policy handle associated with user */
+ uint16 switch_value;
+ SAM_USERINFO_CTR *ctr;
+
+} SAMR_Q_SET_USERINFO;
+
+/* SAMR_R_SET_USERINFO - set sam info */
+typedef struct r_samr_set_user_info
+{
+ NTSTATUS status; /* return status */
+
+} SAMR_R_SET_USERINFO;
+
+
+/* SAMR_Q_QUERY_USERINFO - probably a get sam info */
+typedef struct q_samr_query_user_info
+{
+ POLICY_HND pol; /* policy handle associated with unknown id */
+ uint16 switch_value; /* 0x0015, 0x0011 or 0x0010 - 16 bit unknown */
+
+} SAMR_Q_QUERY_USERINFO;
+
+/* SAMR_R_QUERY_USERINFO - probably a get sam info */
+typedef struct r_samr_query_user_info
+{
+ uint32 ptr; /* pointer */
+ SAM_USERINFO_CTR *ctr;
+
+ NTSTATUS status; /* return status */
+
+} SAMR_R_QUERY_USERINFO;
+
+
+/****************************************************************************
+SAMR_Q_QUERY_USERALIASES - do a conversion from name to RID.
+
+the policy handle allocated by an "samr open secret" call is associated
+with a SID. this policy handle is what is queried here, *not* the SID
+itself. the response to the lookup rids is relative to this SID.
+*****************************************************************************/
+/* SAMR_Q_QUERY_USERALIASES */
+typedef struct q_samr_query_useraliases_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ uint32 num_sids1; /* number of rids being looked up */
+ uint32 ptr; /* buffer pointer */
+ uint32 num_sids2; /* number of rids being looked up */
+
+ uint32 *ptr_sid; /* pointers to sids to be looked up */
+ DOM_SID2 *sid ; /* sids to be looked up. */
+
+} SAMR_Q_QUERY_USERALIASES;
+
+
+/* SAMR_R_QUERY_USERALIASES */
+typedef struct r_samr_query_useraliases_info
+{
+ uint32 num_entries;
+ uint32 ptr; /* undocumented buffer pointer */
+
+ uint32 num_entries2;
+ uint32 *rid; /* domain RIDs being looked up */
+
+ NTSTATUS status; /* return code */
+
+} SAMR_R_QUERY_USERALIASES;
+
+
+/****************************************************************************
+SAMR_Q_LOOKUP_NAMES - do a conversion from Names to RIDs+types.
+*****************************************************************************/
+/* SAMR_Q_LOOKUP_NAMES */
+typedef struct q_samr_lookup_names_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ uint32 num_names1; /* number of names being looked up */
+ uint32 flags; /* 0x0000 03e8 - unknown */
+ uint32 ptr; /* 0x0000 0000 - 32 bit unknown */
+ uint32 num_names2; /* number of names being looked up */
+
+ UNIHDR *hdr_name; /* unicode account name header */
+ UNISTR2 *uni_name; /* unicode account name string */
+
+} SAMR_Q_LOOKUP_NAMES;
+
+
+/* SAMR_R_LOOKUP_NAMES */
+typedef struct r_samr_lookup_names_info
+{
+ uint32 num_rids1; /* number of aliases being looked up */
+ uint32 ptr_rids; /* pointer to aliases */
+ uint32 num_rids2; /* number of aliases being looked up */
+
+ uint32 *rids; /* rids */
+
+ uint32 num_types1; /* number of users in aliases being looked up */
+ uint32 ptr_types; /* pointer to users in aliases */
+ uint32 num_types2; /* number of users in aliases being looked up */
+
+ uint32 *types; /* SID_ENUM type */
+
+ NTSTATUS status; /* return code */
+
+} SAMR_R_LOOKUP_NAMES;
+
+
+/****************************************************************************
+SAMR_Q_LOOKUP_RIDS - do a conversion from RID groups to something.
+
+called to resolve domain RID groups.
+*****************************************************************************/
+/* SAMR_Q_LOOKUP_RIDS */
+typedef struct q_samr_lookup_rids_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ uint32 num_rids1; /* number of rids being looked up */
+ uint32 flags; /* 0x0000 03e8 - unknown */
+ uint32 ptr; /* 0x0000 0000 - 32 bit unknown */
+ uint32 num_rids2; /* number of rids being looked up */
+
+ uint32 *rid; /* domain RIDs being looked up */
+
+} SAMR_Q_LOOKUP_RIDS;
+
+
+/****************************************************************************
+SAMR_R_LOOKUP_RIDS - do a conversion from group RID to names
+
+*****************************************************************************/
+/* SAMR_R_LOOKUP_RIDS */
+typedef struct r_samr_lookup_rids_info
+{
+ uint32 num_names1; /* number of aliases being looked up */
+ uint32 ptr_names; /* pointer to aliases */
+ uint32 num_names2; /* number of aliases being looked up */
+
+ UNIHDR *hdr_name; /* unicode account name header */
+ UNISTR2 *uni_name; /* unicode account name string */
+
+ uint32 num_types1; /* number of users in aliases being looked up */
+ uint32 ptr_types; /* pointer to users in aliases */
+ uint32 num_types2; /* number of users in aliases being looked up */
+
+ uint32 *type; /* SID_ENUM type */
+
+ NTSTATUS status;
+
+} SAMR_R_LOOKUP_RIDS;
+
+
+/* SAMR_Q_OPEN_USER - probably an open */
+typedef struct q_samr_open_user_info
+{
+ POLICY_HND domain_pol; /* policy handle */
+ uint32 access_mask; /* 32 bit unknown - 0x02011b */
+ uint32 user_rid; /* user RID */
+
+} SAMR_Q_OPEN_USER;
+
+
+/* SAMR_R_OPEN_USER - probably an open */
+typedef struct r_samr_open_user_info
+{
+ POLICY_HND user_pol; /* policy handle associated with unknown id */
+ NTSTATUS status; /* return status */
+
+} SAMR_R_OPEN_USER;
+
+
+/* SAMR_Q_CREATE_USER - probably a create */
+typedef struct q_samr_create_user_info
+{
+ POLICY_HND domain_pol; /* policy handle */
+
+ UNIHDR hdr_name; /* unicode account name header */
+ UNISTR2 uni_name; /* unicode account name */
+
+ uint32 acb_info; /* account control info */
+ uint32 access_mask; /* 0xe005 00b0 */
+
+} SAMR_Q_CREATE_USER;
+
+
+/* SAMR_R_CREATE_USER - probably a create */
+typedef struct r_samr_create_user_info
+{
+ POLICY_HND user_pol; /* policy handle associated with user */
+
+ uint32 access_granted;
+ uint32 user_rid; /* user RID */
+ NTSTATUS status; /* return status */
+
+} SAMR_R_CREATE_USER;
+
+
+/* SAMR_Q_DELETE_DOM_USER - delete domain user */
+typedef struct q_samr_delete_dom_user_info
+{
+ POLICY_HND user_pol; /* policy handle */
+
+} SAMR_Q_DELETE_DOM_USER;
+
+
+/* SAMR_R_DELETE_DOM_USER - delete domain user */
+typedef struct r_samr_delete_dom_user_info
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status; /* return status */
+
+} SAMR_R_DELETE_DOM_USER;
+
+
+/* SAMR_Q_QUERY_GROUPMEM - query group members */
+typedef struct q_samr_query_groupmem_info
+{
+ POLICY_HND group_pol; /* policy handle */
+
+} SAMR_Q_QUERY_GROUPMEM;
+
+
+/* SAMR_R_QUERY_GROUPMEM - query group members */
+typedef struct r_samr_query_groupmem_info
+{
+ uint32 ptr;
+ uint32 num_entries;
+
+ uint32 ptr_rids;
+ uint32 ptr_attrs;
+
+ uint32 num_rids;
+ uint32 *rid;
+
+ uint32 num_attrs;
+ uint32 *attr;
+
+ NTSTATUS status;
+
+} SAMR_R_QUERY_GROUPMEM;
+
+
+/* SAMR_Q_DEL_GROUPMEM - probably an del group member */
+typedef struct q_samr_del_group_mem_info
+{
+ POLICY_HND pol; /* policy handle */
+ uint32 rid; /* rid */
+
+} SAMR_Q_DEL_GROUPMEM;
+
+
+/* SAMR_R_DEL_GROUPMEM - probably an del group member */
+typedef struct r_samr_del_group_mem_info
+{
+ NTSTATUS status; /* return status */
+
+} SAMR_R_DEL_GROUPMEM;
+
+
+/* SAMR_Q_ADD_GROUPMEM - probably an add group member */
+typedef struct q_samr_add_group_mem_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ uint32 rid; /* rid */
+ uint32 unknown; /* 0x0000 0005 */
+
+} SAMR_Q_ADD_GROUPMEM;
+
+
+/* SAMR_R_ADD_GROUPMEM - probably an add group member */
+typedef struct r_samr_add_group_mem_info
+{
+ NTSTATUS status; /* return status */
+
+} SAMR_R_ADD_GROUPMEM;
+
+
+/* SAMR_Q_OPEN_GROUP - probably an open */
+typedef struct q_samr_open_group_info
+{
+ POLICY_HND domain_pol; /* policy handle */
+ uint32 access_mask; /* 0x0000 0001, 0x0000 0003, 0x0000 001f */
+ uint32 rid_group; /* rid */
+
+} SAMR_Q_OPEN_GROUP;
+
+
+/* SAMR_R_OPEN_GROUP - probably an open */
+typedef struct r_samr_open_group_info
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status; /* return status */
+
+} SAMR_R_OPEN_GROUP;
+
+
+/* SAMR_Q_QUERY_ALIASMEM - query alias members */
+typedef struct q_samr_query_aliasmem_info
+{
+ POLICY_HND alias_pol; /* policy handle */
+
+} SAMR_Q_QUERY_ALIASMEM;
+
+
+/* SAMR_R_QUERY_ALIASMEM - query alias members */
+typedef struct r_samr_query_aliasmem_info
+{
+ uint32 num_sids;
+ uint32 ptr;
+ uint32 num_sids1;
+
+ DOM_SID2 *sid;
+
+ NTSTATUS status;
+
+} SAMR_R_QUERY_ALIASMEM;
+
+
+/* SAMR_Q_ADD_ALIASMEM - add alias member */
+typedef struct q_samr_add_alias_mem_info
+{
+ POLICY_HND alias_pol; /* policy handle */
+
+ DOM_SID2 sid; /* member sid to be added to the alias */
+
+} SAMR_Q_ADD_ALIASMEM;
+
+
+/* SAMR_R_ADD_ALIASMEM - add alias member */
+typedef struct r_samr_add_alias_mem_info
+{
+ NTSTATUS status; /* return status */
+
+} SAMR_R_ADD_ALIASMEM;
+
+
+/* SAMR_Q_DEL_ALIASMEM - add an add alias member */
+typedef struct q_samr_del_alias_mem_info
+{
+ POLICY_HND alias_pol; /* policy handle */
+
+ DOM_SID2 sid; /* member sid to be added to alias */
+
+} SAMR_Q_DEL_ALIASMEM;
+
+
+/* SAMR_R_DEL_ALIASMEM - delete alias member */
+typedef struct r_samr_del_alias_mem_info
+{
+ NTSTATUS status; /* return status */
+
+} SAMR_R_DEL_ALIASMEM;
+
+
+
+/* SAMR_Q_OPEN_ALIAS - probably an open */
+typedef struct q_samr_open_alias_info
+{
+ POLICY_HND dom_pol;
+
+ uint32 access_mask;
+ uint32 rid_alias;
+
+} SAMR_Q_OPEN_ALIAS;
+
+
+/* SAMR_R_OPEN_ALIAS - probably an open */
+typedef struct r_samr_open_alias_info
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status; /* return status */
+
+} SAMR_R_OPEN_ALIAS;
+
+
+/* SAMR_Q_CONNECT_ANON - probably an open */
+typedef struct q_samr_connect_anon_info
+{
+ uint32 ptr; /* ptr? */
+ uint16 unknown_0; /* 0x005c */
+ uint16 unknown_1; /* 0x0001 */
+ uint32 access_mask;
+
+} SAMR_Q_CONNECT_ANON;
+
+/* SAMR_R_CONNECT_ANON - probably an open */
+typedef struct r_samr_connect_anon_info
+{
+ POLICY_HND connect_pol; /* policy handle */
+ NTSTATUS status; /* return status */
+
+} SAMR_R_CONNECT_ANON;
+
+/* SAMR_Q_CONNECT - probably an open */
+typedef struct q_samr_connect_info
+{
+ uint32 ptr_srv_name; /* pointer (to server name?) */
+ UNISTR2 uni_srv_name; /* unicode server name starting with '\\' */
+
+ uint32 access_mask;
+
+} SAMR_Q_CONNECT;
+
+
+/* SAMR_R_CONNECT - probably an open */
+typedef struct r_samr_connect_info
+{
+ POLICY_HND connect_pol; /* policy handle */
+ NTSTATUS status; /* return status */
+
+} SAMR_R_CONNECT;
+
+/* SAMR_Q_CONNECT4 */
+typedef struct q_samr_connect4_info
+{
+ uint32 ptr_srv_name; /* pointer to server name */
+ UNISTR2 uni_srv_name;
+
+ uint32 unk_0; /* possible server name type, 1 for IP num, 2 for name */
+ uint32 access_mask;
+} SAMR_Q_CONNECT4;
+
+/* SAMR_R_CONNECT4 - same format as connect */
+typedef struct r_samr_connect_info SAMR_R_CONNECT4;
+
+/* SAMR_Q_GET_DOM_PWINFO */
+typedef struct q_samr_get_dom_pwinfo
+{
+ uint32 ptr;
+ UNIHDR hdr_srv_name;
+ UNISTR2 uni_srv_name;
+
+} SAMR_Q_GET_DOM_PWINFO;
+
+/* SAMR_R_GET_DOM_PWINFO */
+typedef struct r_samr_get_dom_pwinfo
+{
+ /*
+ * Previously this was 3 uint16's. However, after some tests
+ * it appears that the data len for the signing needs to be 16.
+ * Not sure how 3 unit16's ever worked since the length always
+ * turned out to 12. 3 uint32's + NT_STATUS == 16 bytes. Tested
+ * using NT and 2k. --jerry
+ */
+ uint32 unk_0;
+ uint32 unk_1;
+ uint32 unk_2;
+ NTSTATUS status;
+
+} SAMR_R_GET_DOM_PWINFO;
+
+/* SAMR_ENC_PASSWD */
+typedef struct enc_passwd_info
+{
+ uint32 ptr;
+ uint8 pass[516];
+
+} SAMR_ENC_PASSWD;
+
+/* SAMR_ENC_HASH */
+typedef struct enc_hash_info
+{
+ uint32 ptr;
+ uint8 hash[16];
+
+} SAMR_ENC_HASH;
+
+/* SAMR_Q_CHGPASSWD_USER */
+typedef struct q_samr_chgpasswd_user_info
+{
+ uint32 ptr_0;
+
+ UNIHDR hdr_dest_host; /* server name unicode header */
+ UNISTR2 uni_dest_host; /* server name unicode string */
+
+ UNIHDR hdr_user_name; /* username unicode string header */
+ UNISTR2 uni_user_name; /* username unicode string */
+
+ SAMR_ENC_PASSWD nt_newpass;
+ SAMR_ENC_HASH nt_oldhash;
+
+ uint32 unknown; /* 0x0000 0001 */
+
+ SAMR_ENC_PASSWD lm_newpass;
+ SAMR_ENC_HASH lm_oldhash;
+
+} SAMR_Q_CHGPASSWD_USER;
+
+/* SAMR_R_CHGPASSWD_USER */
+typedef struct r_samr_chgpasswd_user_info
+{
+ NTSTATUS status; /* 0 == OK, C000006A (NT_STATUS_WRONG_PASSWORD) */
+
+} SAMR_R_CHGPASSWD_USER;
+
+
+/* SAMR_Q_REMOVE_SID_FOREIGN_DOMAIN */
+typedef struct q_samr_remove_sid_foreign_domain_info
+{
+ POLICY_HND dom_pol; /* policy handle */
+ DOM_SID2 sid; /* SID */
+
+} SAMR_Q_REMOVE_SID_FOREIGN_DOMAIN;
+
+
+/* SAMR_R_REMOVE_SID_FOREIGN_DOMAIN */
+typedef struct r_samr_remove_sid_foreign_domain_info
+{
+ NTSTATUS status; /* return status */
+
+} SAMR_R_REMOVE_SID_FOREIGN_DOMAIN;
+
+
+
+/* these are from the old rpc_samr.h - they are needed while the merge
+ is still going on */
+#define MAX_SAM_SIDS 15
+
+/* DOM_SID3 - security id */
+typedef struct sid_info_3
+{
+ uint16 len; /* length, bytes, including length of len :-) */
+ /* uint8 pad[2]; */
+
+ DOM_SID sid;
+
+} DOM_SID3;
+
+/* SAMR_Q_UNKNOWN_2E */
+typedef struct q_samr_unknown_2e_info
+{
+ POLICY_HND domain_pol; /* policy handle */
+ uint16 switch_value;
+
+} SAMR_Q_UNKNOWN_2E;
+
+/* SAMR_R_UNKNOWN_2E */
+typedef struct r_samr_unknown_2e_info
+{
+ uint32 ptr_0;
+ uint16 switch_value;
+ SAM_UNK_CTR *ctr;
+ NTSTATUS status; /* return status */
+
+} SAMR_R_UNKNOWN_2E;
+
+/* SAMR_Q_SET_DOMAIN_INFO */
+typedef struct q_samr_set_domain_info
+{
+ POLICY_HND domain_pol; /* policy handle */
+ uint16 switch_value0;
+ uint16 switch_value;
+ SAM_UNK_CTR *ctr;
+
+} SAMR_Q_SET_DOMAIN_INFO;
+
+/* SAMR_R_SET_DOMAIN_INFO */
+typedef struct r_samr_set_domain_info
+{
+ NTSTATUS status; /* return status */
+
+} SAMR_R_SET_DOMAIN_INFO;
+
+#endif /* _RPC_SAMR_H */
diff --git a/source/include/rpc_secdes.h b/source/include/rpc_secdes.h
new file mode 100644
index 00000000000..56145ac024c
--- /dev/null
+++ b/source/include/rpc_secdes.h
@@ -0,0 +1,469 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _RPC_SECDES_H /* _RPC_SECDES_H */
+#define _RPC_SECDES_H
+
+#define SEC_RIGHTS_QUERY_VALUE 0x00000001
+#define SEC_RIGHTS_SET_VALUE 0x00000002
+#define SEC_RIGHTS_CREATE_SUBKEY 0x00000004
+#define SEC_RIGHTS_ENUM_SUBKEYS 0x00000008
+#define SEC_RIGHTS_NOTIFY 0x00000010
+#define SEC_RIGHTS_CREATE_LINK 0x00000020
+#define SEC_RIGHTS_READ 0x00020019
+#define SEC_RIGHTS_FULL_CONTROL 0x000f003f
+#define SEC_RIGHTS_MAXIMUM_ALLOWED 0x02000000
+
+/* for ADS */
+#define SEC_RIGHTS_LIST_CONTENTS 0x4
+#define SEC_RIGHTS_LIST_OBJECT 0x80
+#define SEC_RIGHTS_READ_ALL_PROP 0x10
+#define SEC_RIGHTS_READ_PERMS 0x20000
+#define SEC_RIGHTS_WRITE_ALL_VALID 0x8
+#define SEC_RIGHTS_WRITE_ALL_PROP 0x20
+#define SEC_RIGHTS_MODIFY_OWNER 0x80000
+#define SEC_RIGHTS_MODIFY_PERMS 0x40000
+#define SEC_RIGHTS_CREATE_CHILD 0x1
+#define SEC_RIGHTS_DELETE_CHILD 0x2
+#define SEC_RIGHTS_DELETE_SUBTREE 0x40
+#define SEC_RIGHTS_DELETE 0x10000 /* advanced/special/object/delete */
+#define SEC_RIGHTS_EXTENDED 0x100 /* change/reset password, receive/send as*/
+#define SEC_RIGHTS_CHANGE_PASSWD SEC_RIGHTS_EXTENDED
+#define SEC_RIGHTS_RESET_PASSWD SEC_RIGHTS_EXTENDED
+#define SEC_RIGHTS_FULL_CTRL 0xf01ff
+
+#define SEC_ACE_OBJECT_PRESENT 0x00000001 /* thanks for Jim McDonough <jmcd@us.ibm.com> */
+#define SEC_ACE_OBJECT_INHERITED_PRESENT 0x00000002
+
+#define SEC_ACE_FLAG_OBJECT_INHERIT 0x1
+#define SEC_ACE_FLAG_CONTAINER_INHERIT 0x2
+#define SEC_ACE_FLAG_NO_PROPAGATE_INHERIT 0x4
+#define SEC_ACE_FLAG_INHERIT_ONLY 0x8
+#define SEC_ACE_FLAG_INHERITED_ACE 0x10 /* New for Windows 2000 */
+#define SEC_ACE_FLAG_VALID_INHERIT 0xf
+#define SEC_ACE_FLAG_SUCCESSFUL_ACCESS 0x40
+#define SEC_ACE_FLAG_FAILED_ACCESS 0x80
+
+#define SEC_ACE_TYPE_ACCESS_ALLOWED 0x0
+#define SEC_ACE_TYPE_ACCESS_DENIED 0x1
+#define SEC_ACE_TYPE_SYSTEM_AUDIT 0x2
+#define SEC_ACE_TYPE_SYSTEM_ALARM 0x3
+#define SEC_ACE_TYPE_ALLOWED_COMPOUND 0x4
+#define SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT 0x5
+#define SEC_ACE_TYPE_ACCESS_DENIED_OBJECT 0x6
+#define SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT 0x7
+#define SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT 0x8
+
+#define SEC_DESC_OWNER_DEFAULTED 0x0001
+#define SEC_DESC_GROUP_DEFAULTED 0x0002
+#define SEC_DESC_DACL_PRESENT 0x0004
+#define SEC_DESC_DACL_DEFAULTED 0x0008
+#define SEC_DESC_SACL_PRESENT 0x0010
+#define SEC_DESC_SACL_DEFAULTED 0x0020
+#define SEC_DESC_DACL_TRUSTED 0x0040
+#define SEC_DESC_SERVER_SECURITY 0x0080
+/*
+ * New Windows 2000 bits.
+ */
+#define SE_DESC_DACL_AUTO_INHERIT_REQ 0x0100
+#define SE_DESC_SACL_AUTO_INHERIT_REQ 0x0200
+#define SE_DESC_DACL_AUTO_INHERITED 0x0400
+#define SE_DESC_SACL_AUTO_INHERITED 0x0800
+#define SE_DESC_DACL_PROTECTED 0x1000
+#define SE_DESC_SACL_PROTECTED 0x2000
+
+/* Don't know what this means. */
+#define SEC_DESC_RM_CONTROL_VALID 0x4000
+
+#define SEC_DESC_SELF_RELATIVE 0x8000
+
+/* security information */
+#define OWNER_SECURITY_INFORMATION 0x00000001
+#define GROUP_SECURITY_INFORMATION 0x00000002
+#define DACL_SECURITY_INFORMATION 0x00000004
+#define SACL_SECURITY_INFORMATION 0x00000008
+/* Extra W2K flags. */
+#define UNPROTECTED_SACL_SECURITY_INFORMATION 0x10000000
+#define UNPROTECTED_DACL_SECURITY_INFORMATION 0x20000000
+#define PROTECTED_SACL_SECURITY_INFORMATION 0x40000000
+#define PROTECTED_DACL_SECURITY_INFORMATION 0x80000000
+
+#define ALL_SECURITY_INFORMATION (OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|\
+ DACL_SECURITY_INFORMATION|SACL_SECURITY_INFORMATION|\
+ UNPROTECTED_SACL_SECURITY_INFORMATION|\
+ UNPROTECTED_DACL_SECURITY_INFORMATION|\
+ PROTECTED_SACL_SECURITY_INFORMATION|\
+ PROTECTED_DACL_SECURITY_INFORMATION)
+
+/* SEC_ACCESS */
+typedef struct security_info_info
+{
+ uint32 mask;
+
+} SEC_ACCESS;
+
+/* SEC_ACE */
+typedef struct security_ace_info
+{
+ uint8 type; /* xxxx_xxxx_ACE_TYPE - e.g allowed / denied etc */
+ uint8 flags; /* xxxx_INHERIT_xxxx - e.g OBJECT_INHERIT_ACE */
+ uint16 size;
+
+ SEC_ACCESS info;
+
+ /* this stuff may be present when type is XXXX_TYPE_XXXX_OBJECT */
+ uint32 obj_flags; /* xxxx_ACE_OBJECT_xxxx e.g present/inherited present etc */
+ struct uuid obj_guid; /* object GUID */
+ struct uuid inh_guid; /* inherited object GUID */
+ /* eof object stuff */
+
+ DOM_SID trustee;
+
+} SEC_ACE;
+#define SEC_ACE_HEADER_SIZE (2 * sizeof(uint8) + sizeof(uint16) + sizeof(uint32))
+
+#ifndef ACL_REVISION
+#define ACL_REVISION 0x3
+#endif
+
+#ifndef NT4_ACL_REVISION
+#define NT4_ACL_REVISION 0x2
+#endif
+
+#ifndef _SEC_ACL
+/* SEC_ACL */
+typedef struct security_acl_info
+{
+ uint16 revision; /* 0x0003 */
+ uint16 size; /* size in bytes of the entire ACL structure */
+ uint32 num_aces; /* number of Access Control Entries */
+
+ SEC_ACE *ace;
+
+} SEC_ACL;
+#define SEC_ACL_HEADER_SIZE (2 * sizeof(uint16) + sizeof(uint32))
+#define _SEC_ACL
+#endif
+
+#ifndef SEC_DESC_REVISION
+#define SEC_DESC_REVISION 0x1
+#endif
+
+#ifndef _SEC_DESC
+/* SEC_DESC */
+typedef struct security_descriptor_info
+{
+ uint16 revision; /* 0x0001 */
+ uint16 type; /* SEC_DESC_xxxx flags */
+
+ uint32 off_owner_sid; /* offset to owner sid */
+ uint32 off_grp_sid ; /* offset to group sid */
+ uint32 off_sacl ; /* offset to system list of permissions */
+ uint32 off_dacl ; /* offset to list of permissions */
+
+ SEC_ACL *dacl; /* user ACL */
+ SEC_ACL *sacl; /* system ACL */
+ DOM_SID *owner_sid;
+ DOM_SID *grp_sid;
+
+} SEC_DESC;
+#define SEC_DESC_HEADER_SIZE (2 * sizeof(uint16) + 4 * sizeof(uint32))
+#define _SEC_DESC
+#endif
+
+#ifndef _SEC_DESC_BUF
+/* SEC_DESC_BUF */
+typedef struct sec_desc_buf_info
+{
+ uint32 max_len;
+ uint32 ptr;
+ uint32 len;
+
+ SEC_DESC *sec;
+
+} SEC_DESC_BUF;
+#define _SEC_DESC_BUF
+#endif
+
+/* A type to describe the mapping of generic access rights to object
+ specific access rights. */
+
+typedef struct generic_mapping {
+ uint32 generic_read;
+ uint32 generic_write;
+ uint32 generic_execute;
+ uint32 generic_all;
+} GENERIC_MAPPING;
+
+typedef struct standard_mapping {
+ uint32 std_read;
+ uint32 std_write;
+ uint32 std_execute;
+ uint32 std_all;
+} STANDARD_MAPPING;
+
+
+/* Security Access Masks Rights */
+
+#define SPECIFIC_RIGHTS_MASK 0x0000FFFF
+#define STANDARD_RIGHTS_MASK 0x00FF0000
+#define GENERIC_RIGHTS_MASK 0xF0000000
+
+#define SEC_RIGHT_SYSTEM_SECURITY 0x01000000
+#define SEC_RIGHT_MAXIMUM_ALLOWED 0x02000000
+
+/* Generic access rights */
+
+#define GENERIC_RIGHT_ALL_ACCESS 0x10000000
+#define GENERIC_RIGHT_EXECUTE_ACCESS 0x20000000
+#define GENERIC_RIGHT_WRITE_ACCESS 0x40000000
+#define GENERIC_RIGHT_READ_ACCESS 0x80000000
+
+/* Standard access rights. */
+
+#define STD_RIGHT_DELETE_ACCESS 0x00010000
+#define STD_RIGHT_READ_CONTROL_ACCESS 0x00020000
+#define STD_RIGHT_WRITE_DAC_ACCESS 0x00040000
+#define STD_RIGHT_WRITE_OWNER_ACCESS 0x00080000
+#define STD_RIGHT_SYNCHRONIZE_ACCESS 0x00100000
+
+#define STD_RIGHT_ALL_ACCESS 0x001F0000
+
+/* Combinations of standard masks. */
+#define STANDARD_RIGHTS_ALL_ACCESS STD_RIGHT_ALL_ACCESS /* 0x001f0000 */
+#define STANDARD_RIGHTS_EXECUTE_ACCESS STD_RIGHT_READ_CONTROL_ACCESS /* 0x00020000 */
+#define STANDARD_RIGHTS_READ_ACCESS STD_RIGHT_READ_CONTROL_ACCESS /* 0x00020000 */
+#define STANDARD_RIGHTS_WRITE_ACCESS STD_RIGHT_READ_CONTROL_ACCESS /* 0x00020000 */
+#define STANDARD_RIGHTS_REQUIRED_ACCESS \
+ (STD_RIGHT_DELETE_ACCESS | \
+ STD_RIGHT_READ_CONTROL_ACCESS | \
+ STD_RIGHT_WRITE_DAC_ACCESS | \
+ STD_RIGHT_WRITE_OWNER_ACCESS) /* 0x000f0000 */
+
+/* File Object specific access rights */
+
+#define SA_RIGHT_FILE_READ_DATA 0x00000001
+#define SA_RIGHT_FILE_WRITE_DATA 0x00000002
+#define SA_RIGHT_FILE_APPEND_DATA 0x00000004
+#define SA_RIGHT_FILE_READ_EA 0x00000008
+#define SA_RIGHT_FILE_WRITE_EA 0x00000010
+#define SA_RIGHT_FILE_EXECUTE 0x00000020
+#define SA_RIGHT_FILE_DELETE_CHILD 0x00000040
+#define SA_RIGHT_FILE_READ_ATTRIBUTES 0x00000080
+#define SA_RIGHT_FILE_WRITE_ATTRIBUTES 0x00000100
+
+#define SA_RIGHT_FILE_ALL_ACCESS 0x000001FF
+
+#define GENERIC_RIGHTS_FILE_ALL_ACCESS \
+ (STANDARD_RIGHTS_REQUIRED_ACCESS| \
+ STD_RIGHT_SYNCHRONIZE_ACCESS | \
+ SA_RIGHT_FILE_ALL_ACCESS)
+
+#define GENERIC_RIGHTS_FILE_READ \
+ (STANDARD_RIGHTS_READ_ACCESS | \
+ STD_RIGHT_SYNCHRONIZE_ACCESS | \
+ SA_RIGHT_FILE_READ_DATA | \
+ SA_RIGHT_FILE_READ_ATTRIBUTES | \
+ SA_RIGHT_FILE_READ_EA)
+
+#define GENERIC_RIGHTS_FILE_WRITE \
+ (STANDARD_RIGHTS_WRITE_ACCESS | \
+ STD_RIGHT_SYNCHRONIZE_ACCESS | \
+ SA_RIGHT_FILE_WRITE_DATA | \
+ SA_RIGHT_FILE_WRITE_ATTRIBUTES | \
+ SA_RIGHT_FILE_WRITE_EA | \
+ SA_RIGHT_FILE_APPEND_DATA)
+
+#define GENERIC_RIGHTS_FILE_EXECUTE \
+ (STANDARD_RIGHTS_EXECUTE_ACCESS | \
+ SA_RIGHT_FILE_READ_ATTRIBUTES | \
+ SA_RIGHT_FILE_EXECUTE)
+
+
+/* SAM server specific access rights */
+
+#define SA_RIGHT_SAM_CONNECT_SERVER 0x00000001
+#define SA_RIGHT_SAM_SHUTDOWN_SERVER 0x00000002
+#define SA_RIGHT_SAM_INITIALISE_SERVER 0x00000004
+#define SA_RIGHT_SAM_CREATE_DOMAIN 0x00000008
+#define SA_RIGHT_SAM_ENUM_DOMAINS 0x00000010
+#define SA_RIGHT_SAM_OPEN_DOMAIN 0x00000020
+
+#define SA_RIGHT_SAM_ALL_ACCESS 0x0000003F
+
+#define GENERIC_RIGHTS_SAM_ALL_ACCESS \
+ (STANDARD_RIGHTS_REQUIRED_ACCESS| \
+ SA_RIGHT_SAM_ALL_ACCESS)
+
+#define GENERIC_RIGHTS_SAM_READ \
+ (STANDARD_RIGHTS_READ_ACCESS | \
+ SA_RIGHT_SAM_ENUM_DOMAINS)
+
+#define GENERIC_RIGHTS_SAM_WRITE \
+ (STANDARD_RIGHTS_WRITE_ACCESS | \
+ SA_RIGHT_SAM_CREATE_DOMAIN | \
+ SA_RIGHT_SAM_INITIALISE_SERVER | \
+ SA_RIGHT_SAM_SHUTDOWN_SERVER)
+
+#define GENERIC_RIGHTS_SAM_EXECUTE \
+ (STANDARD_RIGHTS_EXECUTE_ACCESS | \
+ SA_RIGHT_SAM_OPEN_DOMAIN | \
+ SA_RIGHT_SAM_CONNECT_SERVER)
+
+
+/* Domain Object specific access rights */
+
+#define SA_RIGHT_DOMAIN_LOOKUP_INFO_1 0x00000001
+#define SA_RIGHT_DOMAIN_SET_INFO_1 0x00000002
+#define SA_RIGHT_DOMAIN_LOOKUP_INFO_2 0x00000004
+#define SA_RIGHT_DOMAIN_SET_INFO_2 0x00000008
+#define SA_RIGHT_DOMAIN_CREATE_USER 0x00000010
+#define SA_RIGHT_DOMAIN_CREATE_GROUP 0x00000020
+#define SA_RIGHT_DOMAIN_CREATE_ALIAS 0x00000040
+#define SA_RIGHT_DOMAIN_LOOKUP_ALIAS_BY_MEM 0x00000080
+#define SA_RIGHT_DOMAIN_ENUM_ACCOUNTS 0x00000100
+#define SA_RIGHT_DOMAIN_OPEN_ACCOUNT 0x00000200
+#define SA_RIGHT_DOMAIN_SET_INFO_3 0x00000400
+
+#define SA_RIGHT_DOMAIN_ALL_ACCESS 0x000007FF
+
+#define GENERIC_RIGHTS_DOMAIN_ALL_ACCESS \
+ (STANDARD_RIGHTS_REQUIRED_ACCESS| \
+ SA_RIGHT_DOMAIN_ALL_ACCESS)
+
+#define GENERIC_RIGHTS_DOMAIN_READ \
+ (STANDARD_RIGHTS_READ_ACCESS | \
+ SA_RIGHT_DOMAIN_LOOKUP_ALIAS_BY_MEM | \
+ SA_RIGHT_DOMAIN_LOOKUP_INFO_2)
+
+#define GENERIC_RIGHTS_DOMAIN_WRITE \
+ (STANDARD_RIGHTS_WRITE_ACCESS | \
+ SA_RIGHT_DOMAIN_SET_INFO_3 | \
+ SA_RIGHT_DOMAIN_CREATE_ALIAS | \
+ SA_RIGHT_DOMAIN_CREATE_GROUP | \
+ SA_RIGHT_DOMAIN_CREATE_USER | \
+ SA_RIGHT_DOMAIN_SET_INFO_2 | \
+ SA_RIGHT_DOMAIN_SET_INFO_1)
+
+#define GENERIC_RIGHTS_DOMAIN_EXECUTE \
+ (STANDARD_RIGHTS_EXECUTE_ACCESS | \
+ SA_RIGHT_DOMAIN_OPEN_ACCOUNT | \
+ SA_RIGHT_DOMAIN_ENUM_ACCOUNTS | \
+ SA_RIGHT_DOMAIN_LOOKUP_INFO_1)
+
+
+/* User Object specific access rights */
+
+#define SA_RIGHT_USER_GET_NAME_ETC 0x00000001
+#define SA_RIGHT_USER_GET_LOCALE 0x00000002
+#define SA_RIGHT_USER_SET_LOC_COM 0x00000004
+#define SA_RIGHT_USER_GET_LOGONINFO 0x00000008
+#define SA_RIGHT_USER_ACCT_FLAGS_EXPIRY 0x00000010
+#define SA_RIGHT_USER_SET_ATTRIBUTES 0x00000020
+#define SA_RIGHT_USER_CHANGE_PASSWORD 0x00000040
+#define SA_RIGHT_USER_SET_PASSWORD 0x00000080
+#define SA_RIGHT_USER_GET_GROUPS 0x00000100
+#define SA_RIGHT_USER_READ_GROUP_MEM 0x00000200
+#define SA_RIGHT_USER_CHANGE_GROUP_MEM 0x00000400
+
+#define SA_RIGHT_USER_ALL_ACCESS 0x000007FF
+
+#define GENERIC_RIGHTS_USER_ALL_ACCESS \
+ (STANDARD_RIGHTS_REQUIRED_ACCESS| \
+ SA_RIGHT_USER_ALL_ACCESS) /* 0x000f07ff */
+
+#define GENERIC_RIGHTS_USER_READ \
+ (STANDARD_RIGHTS_READ_ACCESS | \
+ SA_RIGHT_USER_READ_GROUP_MEM | \
+ SA_RIGHT_USER_GET_GROUPS | \
+ SA_RIGHT_USER_ACCT_FLAGS_EXPIRY | \
+ SA_RIGHT_USER_GET_LOGONINFO | \
+ SA_RIGHT_USER_GET_LOCALE) /* 0x0002031a */
+
+#define GENERIC_RIGHTS_USER_WRITE \
+ (STANDARD_RIGHTS_WRITE_ACCESS | \
+ SA_RIGHT_USER_CHANGE_PASSWORD | \
+ SA_RIGHT_USER_SET_LOC_COM) /* 0x00020044 */
+
+#define GENERIC_RIGHTS_USER_EXECUTE \
+ (STANDARD_RIGHTS_EXECUTE_ACCESS | \
+ SA_RIGHT_USER_CHANGE_PASSWORD | \
+ SA_RIGHT_USER_GET_NAME_ETC ) /* 0x00020041 */
+
+
+/* Group Object specific access rights */
+
+#define SA_RIGHT_GROUP_LOOKUP_INFO 0x00000001
+#define SA_RIGHT_GROUP_SET_INFO 0x00000002
+#define SA_RIGHT_GROUP_ADD_MEMBER 0x00000004
+#define SA_RIGHT_GROUP_REMOVE_MEMBER 0x00000008
+#define SA_RIGHT_GROUP_GET_MEMBERS 0x00000010
+
+#define SA_RIGHT_GROUP_ALL_ACCESS 0x0000001F
+
+#define GENERIC_RIGHTS_GROUP_ALL_ACCESS \
+ (STANDARD_RIGHTS_REQUIRED_ACCESS| \
+ SA_RIGHT_GROUP_ALL_ACCESS) /* 0x000f001f */
+
+#define GENERIC_RIGHTS_GROUP_READ \
+ (STANDARD_RIGHTS_READ_ACCESS | \
+ SA_RIGHT_GROUP_GET_MEMBERS) /* 0x00020010 */
+
+#define GENERIC_RIGHTS_GROUP_WRITE \
+ (STANDARD_RIGHTS_WRITE_ACCESS | \
+ SA_RIGHT_GROUP_REMOVE_MEMBER | \
+ SA_RIGHT_GROUP_ADD_MEMBER | \
+ SA_RIGHT_GROUP_SET_INFO ) /* 0x0002000e */
+
+#define GENERIC_RIGHTS_GROUP_EXECUTE \
+ (STANDARD_RIGHTS_EXECUTE_ACCESS | \
+ SA_RIGHT_GROUP_LOOKUP_INFO) /* 0x00020001 */
+
+
+/* Alias Object specific access rights */
+
+#define SA_RIGHT_ALIAS_ADD_MEMBER 0x00000001
+#define SA_RIGHT_ALIAS_REMOVE_MEMBER 0x00000002
+#define SA_RIGHT_ALIAS_GET_MEMBERS 0x00000004
+#define SA_RIGHT_ALIAS_LOOKUP_INFO 0x00000008
+#define SA_RIGHT_ALIAS_SET_INFO 0x00000010
+
+#define SA_RIGHT_ALIAS_ALL_ACCESS 0x0000001F
+
+#define GENERIC_RIGHTS_ALIAS_ALL_ACCESS \
+ (STANDARD_RIGHTS_REQUIRED_ACCESS| \
+ SA_RIGHT_ALIAS_ALL_ACCESS) /* 0x000f001f */
+
+#define GENERIC_RIGHTS_ALIAS_READ \
+ (STANDARD_RIGHTS_READ_ACCESS | \
+ SA_RIGHT_ALIAS_GET_MEMBERS ) /* 0x00020004 */
+
+#define GENERIC_RIGHTS_ALIAS_WRITE \
+ (STANDARD_RIGHTS_WRITE_ACCESS | \
+ SA_RIGHT_ALIAS_REMOVE_MEMBER | \
+ SA_RIGHT_ALIAS_ADD_MEMBER | \
+ SA_RIGHT_ALIAS_SET_INFO ) /* 0x00020013 */
+
+#define GENERIC_RIGHTS_ALIAS_EXECUTE \
+ (STANDARD_RIGHTS_EXECUTE_ACCESS | \
+ SA_RIGHT_ALIAS_LOOKUP_INFO ) /* 0x00020008 */
+
+#endif /* _RPC_SECDES_H */
diff --git a/source/include/rpc_shutdown.h b/source/include/rpc_shutdown.h
new file mode 100644
index 00000000000..b8e50b835f5
--- /dev/null
+++ b/source/include/rpc_shutdown.h
@@ -0,0 +1,70 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _RPC_SHUTDOWN_H /* _RPC_SHUTDOWN_H */
+#define _RPC_SHUTDOWN_H
+
+
+/* Implemented */
+#define SHUTDOWN_INIT 0x00
+#define SHUTDOWN_ABORT 0x01
+/* NOT IMPLEMENTED
+#define SHUTDOWN_INIT_EX 0x02
+*/
+
+/* SHUTDOWN_Q_INIT */
+typedef struct q_shutodwn_init_info
+{
+ uint32 ptr_server;
+ uint16 server;
+ uint32 ptr_msg;
+ UNIHDR hdr_msg; /* shutdown message */
+ UNISTR2 uni_msg; /* seconds */
+ uint32 timeout; /* seconds */
+ uint8 force; /* boolean: force shutdown */
+ uint8 reboot; /* boolean: reboot on shutdown */
+
+} SHUTDOWN_Q_INIT;
+
+/* SHUTDOWN_R_INIT */
+typedef struct r_shutdown_init_info
+{
+ NTSTATUS status; /* return status */
+
+} SHUTDOWN_R_INIT;
+
+/* SHUTDOWN_Q_ABORT */
+typedef struct q_shutdown_abort_info
+{
+ uint32 ptr_server;
+ uint16 server;
+
+} SHUTDOWN_Q_ABORT;
+
+/* SHUTDOWN_R_ABORT */
+typedef struct r_shutdown_abort_info
+{
+ NTSTATUS status; /* return status */
+
+} SHUTDOWN_R_ABORT;
+
+
+#endif /* _RPC_SHUTDOWN_H */
+
diff --git a/source/include/rpc_spoolss.h b/source/include/rpc_spoolss.h
new file mode 100755
index 00000000000..d9fc0c6a6ab
--- /dev/null
+++ b/source/include/rpc_spoolss.h
@@ -0,0 +1,2234 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-2000,
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+ Copyright (C) Jean Francois Micouleau 1998-2000.
+ Copyright (C) Gerald Carter 2001-2002.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _RPC_SPOOLSS_H /* _RPC_SPOOLSS_H */
+#define _RPC_SPOOLSS_H
+
+/* spoolss pipe: this are the calls which are not implemented ...
+#define SPOOLSS_GETPRINTERDRIVER 0x0b
+#define SPOOLSS_READPRINTER 0x16
+#define SPOOLSS_WAITFORPRINTERCHANGE 0x1c
+#define SPOOLSS_ADDPORT 0x25
+#define SPOOLSS_CONFIGUREPORT 0x26
+#define SPOOLSS_DELETEPORT 0x27
+#define SPOOLSS_CREATEPRINTERIC 0x28
+#define SPOOLSS_PLAYGDISCRIPTONPRINTERIC 0x29
+#define SPOOLSS_DELETEPRINTERIC 0x2a
+#define SPOOLSS_ADDPRINTERCONNECTION 0x2b
+#define SPOOLSS_DELETEPRINTERCONNECTION 0x2c
+#define SPOOLSS_PRINTERMESSAGEBOX 0x2d
+#define SPOOLSS_ADDMONITOR 0x2e
+#define SPOOLSS_DELETEMONITOR 0x2f
+#define SPOOLSS_DELETEPRINTPROCESSOR 0x30
+#define SPOOLSS_ADDPRINTPROVIDOR 0x31
+#define SPOOLSS_DELETEPRINTPROVIDOR 0x32
+#define SPOOLSS_FINDFIRSTPRINTERCHANGENOTIFICATION 0x36
+#define SPOOLSS_FINDNEXTPRINTERCHANGENOTIFICATION 0x37
+#define SPOOLSS_ROUTERFINDFIRSTPRINTERNOTIFICATIONOLD 0x39
+#define SPOOLSS_ADDPORTEX 0x3d
+#define SPOOLSS_REMOTEFINDFIRSTPRINTERCHANGENOTIFICATION0x3e
+#define SPOOLSS_SPOOLERINIT 0x3f
+#define SPOOLSS_RESETPRINTEREX 0x40
+*/
+
+/* those are implemented */
+#define SPOOLSS_ENUMPRINTERS 0x00
+#define SPOOLSS_OPENPRINTER 0x01
+#define SPOOLSS_SETJOB 0x02
+#define SPOOLSS_GETJOB 0x03
+#define SPOOLSS_ENUMJOBS 0x04
+#define SPOOLSS_ADDPRINTER 0x05
+#define SPOOLSS_DELETEPRINTER 0x06
+#define SPOOLSS_SETPRINTER 0x07
+#define SPOOLSS_GETPRINTER 0x08
+#define SPOOLSS_ADDPRINTERDRIVER 0x09
+#define SPOOLSS_ENUMPRINTERDRIVERS 0x0a
+#define SPOOLSS_GETPRINTERDRIVERDIRECTORY 0x0c
+#define SPOOLSS_DELETEPRINTERDRIVER 0x0d
+#define SPOOLSS_ADDPRINTPROCESSOR 0x0e
+#define SPOOLSS_ENUMPRINTPROCESSORS 0x0f
+#define SPOOLSS_GETPRINTPROCESSORDIRECTORY 0x10
+#define SPOOLSS_STARTDOCPRINTER 0x11
+#define SPOOLSS_STARTPAGEPRINTER 0x12
+#define SPOOLSS_WRITEPRINTER 0x13
+#define SPOOLSS_ENDPAGEPRINTER 0x14
+#define SPOOLSS_ABORTPRINTER 0x15
+#define SPOOLSS_ENDDOCPRINTER 0x17
+#define SPOOLSS_ADDJOB 0x18
+#define SPOOLSS_SCHEDULEJOB 0x19
+#define SPOOLSS_GETPRINTERDATA 0x1a
+#define SPOOLSS_SETPRINTERDATA 0x1b
+#define SPOOLSS_CLOSEPRINTER 0x1d
+#define SPOOLSS_ADDFORM 0x1e
+#define SPOOLSS_DELETEFORM 0x1f
+#define SPOOLSS_GETFORM 0x20
+#define SPOOLSS_SETFORM 0x21
+#define SPOOLSS_ENUMFORMS 0x22
+#define SPOOLSS_ENUMPORTS 0x23
+#define SPOOLSS_ENUMMONITORS 0x24
+#define SPOOLSS_ENUMPRINTPROCDATATYPES 0x33
+#define SPOOLSS_RESETPRINTER 0x34
+#define SPOOLSS_GETPRINTERDRIVER2 0x35
+#define SPOOLSS_FCPN 0x38 /* FindClosePrinterNotify */
+#define SPOOLSS_REPLYOPENPRINTER 0x3a
+#define SPOOLSS_ROUTERREPLYPRINTER 0x3b
+#define SPOOLSS_REPLYCLOSEPRINTER 0x3c
+#define SPOOLSS_RFFPCNEX 0x41 /* RemoteFindFirstPrinterChangeNotifyEx */
+#define SPOOLSS_RRPCN 0x42 /* RouteRefreshPrinterChangeNotification */
+#define SPOOLSS_RFNPCNEX 0x43 /* RemoteFindNextPrinterChangeNotifyEx */
+#define SPOOLSS_OPENPRINTEREX 0x45
+#define SPOOLSS_ADDPRINTEREX 0x46
+#define SPOOLSS_ENUMPRINTERDATA 0x48
+#define SPOOLSS_DELETEPRINTERDATA 0x49
+#define SPOOLSS_SETPRINTERDATAEX 0x4d
+#define SPOOLSS_GETPRINTERDATAEX 0x4e
+#define SPOOLSS_ENUMPRINTERDATAEX 0x4f
+#define SPOOLSS_ENUMPRINTERKEY 0x50
+#define SPOOLSS_DELETEPRINTERDATAEX 0x51
+#define SPOOLSS_DELETEPRINTERKEY 0x52
+#define SPOOLSS_DELETEPRINTERDRIVEREX 0x54
+#define SPOOLSS_ADDPRINTERDRIVEREX 0x59
+
+
+#define PRINTER_CONTROL_UNPAUSE 0x00000000
+#define PRINTER_CONTROL_PAUSE 0x00000001
+#define PRINTER_CONTROL_RESUME 0x00000002
+#define PRINTER_CONTROL_PURGE 0x00000003
+#define PRINTER_CONTROL_SET_STATUS 0x00000004
+
+#define PRINTER_STATUS_OK 0x00000000
+#define PRINTER_STATUS_PAUSED 0x00000001
+#define PRINTER_STATUS_ERROR 0x00000002
+#define PRINTER_STATUS_PENDING_DELETION 0x00000004
+#define PRINTER_STATUS_PAPER_JAM 0x00000008
+
+#define PRINTER_STATUS_PAPER_OUT 0x00000010
+#define PRINTER_STATUS_MANUAL_FEED 0x00000020
+#define PRINTER_STATUS_PAPER_PROBLEM 0x00000040
+#define PRINTER_STATUS_OFFLINE 0x00000080
+
+#define PRINTER_STATUS_IO_ACTIVE 0x00000100
+#define PRINTER_STATUS_BUSY 0x00000200
+#define PRINTER_STATUS_PRINTING 0x00000400
+#define PRINTER_STATUS_OUTPUT_BIN_FULL 0x00000800
+
+#define PRINTER_STATUS_NOT_AVAILABLE 0x00001000
+#define PRINTER_STATUS_WAITING 0x00002000
+#define PRINTER_STATUS_PROCESSING 0x00004000
+#define PRINTER_STATUS_INITIALIZING 0x00008000
+
+#define PRINTER_STATUS_WARMING_UP 0x00010000
+#define PRINTER_STATUS_TONER_LOW 0x00020000
+#define PRINTER_STATUS_NO_TONER 0x00040000
+#define PRINTER_STATUS_PAGE_PUNT 0x00080000
+
+#define PRINTER_STATUS_USER_INTERVENTION 0x00100000
+#define PRINTER_STATUS_OUT_OF_MEMORY 0x00200000
+#define PRINTER_STATUS_DOOR_OPEN 0x00400000
+#define PRINTER_STATUS_SERVER_UNKNOWN 0x00800000
+
+#define PRINTER_STATUS_POWER_SAVE 0x01000000
+
+#define SERVER_ACCESS_ADMINISTER 0x00000001
+#define SERVER_ACCESS_ENUMERATE 0x00000002
+#define PRINTER_ACCESS_ADMINISTER 0x00000004
+#define PRINTER_ACCESS_USE 0x00000008
+#define JOB_ACCESS_ADMINISTER 0x00000010
+
+/* JOB status codes. */
+
+#define JOB_STATUS_QUEUED 0x0000
+#define JOB_STATUS_PAUSED 0x0001
+#define JOB_STATUS_ERROR 0x0002
+#define JOB_STATUS_DELETING 0x0004
+#define JOB_STATUS_SPOOLING 0x0008
+#define JOB_STATUS_PRINTING 0x0010
+#define JOB_STATUS_OFFLINE 0x0020
+#define JOB_STATUS_PAPEROUT 0x0040
+#define JOB_STATUS_PRINTED 0x0080
+#define JOB_STATUS_DELETED 0x0100
+#define JOB_STATUS_BLOCKED 0x0200
+#define JOB_STATUS_USER_INTERVENTION 0x0400
+
+/* ACE masks for the various print permissions */
+
+#define PRINTER_ACE_FULL_CONTROL GENERIC_ALL_ACCESS
+#define PRINTER_ACE_MANAGE_DOCUMENTS READ_CONTROL_ACCESS
+#define PRINTER_ACE_PRINT \
+ (GENERIC_READ_ACCESS | GENERIC_WRITE_ACCESS | GENERIC_EXECUTE_ACCESS)
+
+/* Access rights for print servers */
+#define SERVER_ALL_ACCESS STANDARD_RIGHTS_REQUIRED_ACCESS|SERVER_ACCESS_ADMINISTER|SERVER_ACCESS_ENUMERATE
+#define SERVER_READ STANDARD_RIGHTS_READ_ACCESS|SERVER_ACCESS_ENUMERATE
+#define SERVER_WRITE STANDARD_RIGHTS_WRITE_ACCESS|SERVER_ACCESS_ADMINISTER|SERVER_ACCESS_ENUMERATE
+#define SERVER_EXECUTE STANDARD_RIGHTS_EXECUTE_ACCESS|SERVER_ACCESS_ENUMERATE
+
+/* Access rights for printers */
+#define PRINTER_ALL_ACCESS STANDARD_RIGHTS_REQUIRED_ACCESS|PRINTER_ACCESS_ADMINISTER|PRINTER_ACCESS_USE
+#define PRINTER_READ STANDARD_RIGHTS_READ_ACCESS|PRINTER_ACCESS_USE
+#define PRINTER_WRITE STANDARD_RIGHTS_WRITE_ACCESS|PRINTER_ACCESS_USE
+#define PRINTER_EXECUTE STANDARD_RIGHTS_EXECUTE_ACCESS|PRINTER_ACCESS_USE
+
+/* Access rights for jobs */
+#define JOB_ALL_ACCESS STANDARD_RIGHTS_REQUIRED_ACCESS|JOB_ACCESS_ADMINISTER
+#define JOB_READ STANDARD_RIGHTS_READ_ACCESS|JOB_ACCESS_ADMINISTER
+#define JOB_WRITE STANDARD_RIGHTS_WRITE_ACCESS|JOB_ACCESS_ADMINISTER
+#define JOB_EXECUTE STANDARD_RIGHTS_EXECUTE_ACCESS|JOB_ACCESS_ADMINISTER
+
+/* Notify field types */
+
+#define NOTIFY_ONE_VALUE 1 /* Notify data is stored in value1 */
+#define NOTIFY_TWO_VALUE 2 /* Notify data is stored in value2 */
+#define NOTIFY_POINTER 3 /* Data is a pointer to a buffer */
+#define NOTIFY_STRING 4 /* Data is a pointer to a buffer w/length */
+#define NOTIFY_SECDESC 5 /* Data is a security descriptor */
+
+#define PRINTER_NOTIFY_TYPE 0x00
+#define JOB_NOTIFY_TYPE 0x01
+
+#define MAX_PRINTER_NOTIFY 26
+#define MAX_JOB_NOTIFY 24
+
+#define MAX_NOTIFY_TYPE_FOR_NOW 26
+
+#define PRINTER_NOTIFY_SERVER_NAME 0x00
+#define PRINTER_NOTIFY_PRINTER_NAME 0x01
+#define PRINTER_NOTIFY_SHARE_NAME 0x02
+#define PRINTER_NOTIFY_PORT_NAME 0x03
+#define PRINTER_NOTIFY_DRIVER_NAME 0x04
+#define PRINTER_NOTIFY_COMMENT 0x05
+#define PRINTER_NOTIFY_LOCATION 0x06
+#define PRINTER_NOTIFY_DEVMODE 0x07
+#define PRINTER_NOTIFY_SEPFILE 0x08
+#define PRINTER_NOTIFY_PRINT_PROCESSOR 0x09
+#define PRINTER_NOTIFY_PARAMETERS 0x0A
+#define PRINTER_NOTIFY_DATATYPE 0x0B
+#define PRINTER_NOTIFY_SECURITY_DESCRIPTOR 0x0C
+#define PRINTER_NOTIFY_ATTRIBUTES 0x0D
+#define PRINTER_NOTIFY_PRIORITY 0x0E
+#define PRINTER_NOTIFY_DEFAULT_PRIORITY 0x0F
+#define PRINTER_NOTIFY_START_TIME 0x10
+#define PRINTER_NOTIFY_UNTIL_TIME 0x11
+#define PRINTER_NOTIFY_STATUS 0x12
+#define PRINTER_NOTIFY_STATUS_STRING 0x13
+#define PRINTER_NOTIFY_CJOBS 0x14
+#define PRINTER_NOTIFY_AVERAGE_PPM 0x15
+#define PRINTER_NOTIFY_TOTAL_PAGES 0x16
+#define PRINTER_NOTIFY_PAGES_PRINTED 0x17
+#define PRINTER_NOTIFY_TOTAL_BYTES 0x18
+#define PRINTER_NOTIFY_BYTES_PRINTED 0x19
+
+#define JOB_NOTIFY_PRINTER_NAME 0x00
+#define JOB_NOTIFY_MACHINE_NAME 0x01
+#define JOB_NOTIFY_PORT_NAME 0x02
+#define JOB_NOTIFY_USER_NAME 0x03
+#define JOB_NOTIFY_NOTIFY_NAME 0x04
+#define JOB_NOTIFY_DATATYPE 0x05
+#define JOB_NOTIFY_PRINT_PROCESSOR 0x06
+#define JOB_NOTIFY_PARAMETERS 0x07
+#define JOB_NOTIFY_DRIVER_NAME 0x08
+#define JOB_NOTIFY_DEVMODE 0x09
+#define JOB_NOTIFY_STATUS 0x0A
+#define JOB_NOTIFY_STATUS_STRING 0x0B
+#define JOB_NOTIFY_SECURITY_DESCRIPTOR 0x0C
+#define JOB_NOTIFY_DOCUMENT 0x0D
+#define JOB_NOTIFY_PRIORITY 0x0E
+#define JOB_NOTIFY_POSITION 0x0F
+#define JOB_NOTIFY_SUBMITTED 0x10
+#define JOB_NOTIFY_START_TIME 0x11
+#define JOB_NOTIFY_UNTIL_TIME 0x12
+#define JOB_NOTIFY_TIME 0x13
+#define JOB_NOTIFY_TOTAL_PAGES 0x14
+#define JOB_NOTIFY_PAGES_PRINTED 0x15
+#define JOB_NOTIFY_TOTAL_BYTES 0x16
+#define JOB_NOTIFY_BYTES_PRINTED 0x17
+
+#define PRINTER_NOTIFY_OPTIONS_REFRESH 0x01
+
+#define PRINTER_CHANGE_ADD_PRINTER 0x00000001
+#define PRINTER_CHANGE_SET_PRINTER 0x00000002
+#define PRINTER_CHANGE_DELETE_PRINTER 0x00000004
+#define PRINTER_CHANGE_FAILED_CONNECTION_PRINTER 0x00000008
+#define PRINTER_CHANGE_PRINTER (PRINTER_CHANGE_ADD_PRINTER | \
+ PRINTER_CHANGE_SET_PRINTER | \
+ PRINTER_CHANGE_DELETE_PRINTER | \
+ PRINTER_CHANGE_FAILED_CONNECTION_PRINTER )
+
+#define PRINTER_CHANGE_ADD_JOB 0x00000100
+#define PRINTER_CHANGE_SET_JOB 0x00000200
+#define PRINTER_CHANGE_DELETE_JOB 0x00000400
+#define PRINTER_CHANGE_WRITE_JOB 0x00000800
+#define PRINTER_CHANGE_JOB (PRINTER_CHANGE_ADD_JOB | \
+ PRINTER_CHANGE_SET_JOB | \
+ PRINTER_CHANGE_DELETE_JOB | \
+ PRINTER_CHANGE_WRITE_JOB )
+
+#define PRINTER_CHANGE_ADD_FORM 0x00010000
+#define PRINTER_CHANGE_SET_FORM 0x00020000
+#define PRINTER_CHANGE_DELETE_FORM 0x00040000
+#define PRINTER_CHANGE_FORM (PRINTER_CHANGE_ADD_FORM | \
+ PRINTER_CHANGE_SET_FORM | \
+ PRINTER_CHANGE_DELETE_FORM )
+
+#define PRINTER_CHANGE_ADD_PORT 0x00100000
+#define PRINTER_CHANGE_CONFIGURE_PORT 0x00200000
+#define PRINTER_CHANGE_DELETE_PORT 0x00400000
+#define PRINTER_CHANGE_PORT (PRINTER_CHANGE_ADD_PORT | \
+ PRINTER_CHANGE_CONFIGURE_PORT | \
+ PRINTER_CHANGE_DELETE_PORT )
+
+#define PRINTER_CHANGE_ADD_PRINT_PROCESSOR 0x01000000
+#define PRINTER_CHANGE_DELETE_PRINT_PROCESSOR 0x04000000
+#define PRINTER_CHANGE_PRINT_PROCESSOR (PRINTER_CHANGE_ADD_PRINT_PROCESSOR | \
+ PRINTER_CHANGE_DELETE_PRINT_PROCESSOR )
+
+#define PRINTER_CHANGE_ADD_PRINTER_DRIVER 0x10000000
+#define PRINTER_CHANGE_SET_PRINTER_DRIVER 0x20000000
+#define PRINTER_CHANGE_DELETE_PRINTER_DRIVER 0x40000000
+#define PRINTER_CHANGE_PRINTER_DRIVER (PRINTER_CHANGE_ADD_PRINTER_DRIVER | \
+ PRINTER_CHANGE_SET_PRINTER_DRIVER | \
+ PRINTER_CHANGE_DELETE_PRINTER_DRIVER )
+
+#define PRINTER_CHANGE_TIMEOUT 0x80000000
+#define PRINTER_CHANGE_ALL (PRINTER_CHANGE_JOB | \
+ PRINTER_CHANGE_FORM | \
+ PRINTER_CHANGE_PORT | \
+ PRINTER_CHANGE_PRINT_PROCESSOR | \
+ PRINTER_CHANGE_PRINTER_DRIVER )
+
+#define PRINTER_NOTIFY_INFO_DISCARDED 0x1
+
+/*
+ * Set of macros for flagging what changed in the PRINTER_INFO_2 struct
+ * when sending messages to other smbd's
+ */
+#define PRINTER_MESSAGE_NULL 0x00000000
+#define PRINTER_MESSAGE_DRIVER 0x00000001
+#define PRINTER_MESSAGE_COMMENT 0x00000002
+#define PRINTER_MESSAGE_PRINTERNAME 0x00000004
+#define PRINTER_MESSAGE_LOCATION 0x00000008
+#define PRINTER_MESSAGE_DEVMODE 0x00000010 /* not curently supported */
+#define PRINTER_MESSAGE_SEPFILE 0x00000020
+#define PRINTER_MESSAGE_PRINTPROC 0x00000040
+#define PRINTER_MESSAGE_PARAMS 0x00000080
+#define PRINTER_MESSAGE_DATATYPE 0x00000100
+#define PRINTER_MESSAGE_SECDESC 0x00000200
+#define PRINTER_MESSAGE_CJOBS 0x00000400
+#define PRINTER_MESSAGE_PORT 0x00000800
+#define PRINTER_MESSAGE_SHARENAME 0x00001000
+#define PRINTER_MESSAGE_ATTRIBUTES 0x00002000
+
+typedef struct printer_message_info {
+ uint32 low; /* PRINTER_CHANGE_XXX */
+ uint32 high; /* PRINTER_CHANGE_XXX */
+ fstring printer_name;
+ uint32 flags; /* PRINTER_MESSAGE_XXX */
+}
+PRINTER_MESSAGE_INFO;
+
+/*
+ * The printer attributes.
+ * I #defined all of them (grabbed form MSDN)
+ * I'm only using:
+ * ( SHARED | NETWORK | RAW_ONLY )
+ * RAW_ONLY _MUST_ be present otherwise NT will send an EMF file
+ */
+
+#define PRINTER_ATTRIBUTE_QUEUED 0x00000001
+#define PRINTER_ATTRIBUTE_DIRECT 0x00000002
+#define PRINTER_ATTRIBUTE_DEFAULT 0x00000004
+#define PRINTER_ATTRIBUTE_SHARED 0x00000008
+
+#define PRINTER_ATTRIBUTE_NETWORK 0x00000010
+#define PRINTER_ATTRIBUTE_HIDDEN 0x00000020
+#define PRINTER_ATTRIBUTE_LOCAL 0x00000040
+#define PRINTER_ATTRIBUTE_ENABLE_DEVQ 0x00000080
+
+#define PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS 0x00000100
+#define PRINTER_ATTRIBUTE_DO_COMPLETE_FIRST 0x00000200
+#define PRINTER_ATTRIBUTE_WORK_OFFLINE 0x00000400
+#define PRINTER_ATTRIBUTE_ENABLE_BIDI 0x00000800
+
+#define PRINTER_ATTRIBUTE_RAW_ONLY 0x00001000
+#define PRINTER_ATTRIBUTE_PUBLISHED 0x00002000
+
+#define PRINTER_ATTRIBUTE_SAMBA (PRINTER_ATTRIBUTE_RAW_ONLY|\
+ PRINTER_ATTRIBUTE_SHARED|\
+ PRINTER_ATTRIBUTE_LOCAL)
+
+#define NO_PRIORITY 0
+#define MAX_PRIORITY 99
+#define MIN_PRIORITY 1
+#define DEF_PRIORITY 1
+
+/* the flags of the query */
+#define PRINTER_ENUM_DEFAULT 0x00000001
+#define PRINTER_ENUM_LOCAL 0x00000002
+#define PRINTER_ENUM_CONNECTIONS 0x00000004
+#define PRINTER_ENUM_FAVORITE 0x00000004
+#define PRINTER_ENUM_NAME 0x00000008
+#define PRINTER_ENUM_REMOTE 0x00000010
+#define PRINTER_ENUM_SHARED 0x00000020
+#define PRINTER_ENUM_NETWORK 0x00000040
+
+/* the flags of each printers */
+#define PRINTER_ENUM_UNKNOWN_8 0x00000008
+#define PRINTER_ENUM_EXPAND 0x00004000
+#define PRINTER_ENUM_CONTAINER 0x00008000
+#define PRINTER_ENUM_ICONMASK 0x00ff0000
+#define PRINTER_ENUM_ICON1 0x00010000
+#define PRINTER_ENUM_ICON2 0x00020000
+#define PRINTER_ENUM_ICON3 0x00040000
+#define PRINTER_ENUM_ICON4 0x00080000
+#define PRINTER_ENUM_ICON5 0x00100000
+#define PRINTER_ENUM_ICON6 0x00200000
+#define PRINTER_ENUM_ICON7 0x00400000
+#define PRINTER_ENUM_ICON8 0x00800000
+
+/* FLAGS for SPOOLSS_DELETEPRINTERDRIVEREX */
+
+#define DPD_DELETE_UNUSED_FILES 0x00000001
+#define DPD_DELETE_SPECIFIC_VERSION 0x00000002
+#define DPD_DELETE_ALL_FILES 0x00000004
+
+#define DRIVER_ANY_VERSION 0xffffffff
+#define DRIVER_MAX_VERSION 4
+
+/* FLAGS for SPOOLSS_ADDPRINTERDRIVEREX */
+
+#define APD_STRICT_UPGRADE 0x00000001
+#define APD_STRICT_DOWNGRADE 0x00000002
+#define APD_COPY_ALL_FILES 0x00000004
+#define APD_COPY_NEW_FILES 0x00000008
+
+
+/* this struct is undocumented */
+/* thanks to the ddk ... */
+typedef struct spool_user_1
+{
+ uint32 size; /* length of user_name & client_name + 2? */
+ uint32 client_name_ptr;
+ uint32 user_name_ptr;
+ uint32 build;
+ uint32 major;
+ uint32 minor;
+ uint32 processor;
+ UNISTR2 client_name;
+ UNISTR2 user_name;
+}
+SPOOL_USER_1;
+
+typedef struct spool_user_ctr_info
+{
+ uint32 level;
+ uint32 ptr;
+ SPOOL_USER_1 user1;
+}
+SPOOL_USER_CTR;
+
+/*
+ * various bits in the DEVICEMODE.fields member
+ */
+
+#define DEVMODE_ORIENTATION 0x00000001
+#define DEVMODE_PAPERSIZE 0x00000002
+#define DEVMODE_PAPERLENGTH 0x00000004
+#define DEVMODE_PAPERWIDTH 0x00000008
+#define DEVMODE_SCALE 0x00000010
+#define DEVMODE_POSITION 0x00000020
+#define DEVMODE_NUP 0x00000040
+#define DEVMODE_COPIES 0x00000100
+#define DEVMODE_DEFAULTSOURCE 0x00000200
+#define DEVMODE_PRINTQUALITY 0x00000400
+#define DEVMODE_COLOR 0x00000800
+#define DEVMODE_DUPLEX 0x00001000
+#define DEVMODE_YRESOLUTION 0x00002000
+#define DEVMODE_TTOPTION 0x00004000
+#define DEVMODE_COLLATE 0x00008000
+#define DEVMODE_FORMNAME 0x00010000
+#define DEVMODE_LOGPIXELS 0x00020000
+#define DEVMODE_BITSPERPEL 0x00040000
+#define DEVMODE_PELSWIDTH 0x00080000
+#define DEVMODE_PELSHEIGHT 0x00100000
+#define DEVMODE_DISPLAYFLAGS 0x00200000
+#define DEVMODE_DISPLAYFREQUENCY 0x00400000
+#define DEVMODE_ICMMETHOD 0x00800000
+#define DEVMODE_ICMINTENT 0x01000000
+#define DEVMODE_MEDIATYPE 0x02000000
+#define DEVMODE_DITHERTYPE 0x04000000
+#define DEVMODE_PANNINGWIDTH 0x08000000
+#define DEVMODE_PANNINGHEIGHT 0x10000000
+
+
+/*
+ * Devicemode structure
+ */
+
+typedef struct devicemode
+{
+ UNISTR devicename;
+ uint16 specversion;
+ uint16 driverversion;
+ uint16 size;
+ uint16 driverextra;
+ uint32 fields;
+ uint16 orientation;
+ uint16 papersize;
+ uint16 paperlength;
+ uint16 paperwidth;
+ uint16 scale;
+ uint16 copies;
+ uint16 defaultsource;
+ uint16 printquality;
+ uint16 color;
+ uint16 duplex;
+ uint16 yresolution;
+ uint16 ttoption;
+ uint16 collate;
+ UNISTR formname;
+ uint16 logpixels;
+ uint32 bitsperpel;
+ uint32 pelswidth;
+ uint32 pelsheight;
+ uint32 displayflags;
+ uint32 displayfrequency;
+ uint32 icmmethod;
+ uint32 icmintent;
+ uint32 mediatype;
+ uint32 dithertype;
+ uint32 reserved1;
+ uint32 reserved2;
+ uint32 panningwidth;
+ uint32 panningheight;
+ uint8 *private;
+}
+DEVICEMODE;
+
+typedef struct _devmode_cont
+{
+ uint32 size;
+ uint32 devmode_ptr;
+ DEVICEMODE *devmode;
+}
+DEVMODE_CTR;
+
+typedef struct _printer_default
+{
+ uint32 datatype_ptr;
+ UNISTR2 datatype;
+ DEVMODE_CTR devmode_cont;
+ uint32 access_required;
+}
+PRINTER_DEFAULT;
+
+/* SPOOL_Q_OPEN_PRINTER request to open a printer */
+typedef struct spool_q_open_printer
+{
+ uint32 printername_ptr;
+ UNISTR2 printername;
+ PRINTER_DEFAULT printer_default;
+}
+SPOOL_Q_OPEN_PRINTER;
+
+/* SPOOL_R_OPEN_PRINTER reply to an open printer */
+typedef struct spool_r_open_printer
+{
+ POLICY_HND handle; /* handle used along all transactions (20*uint8) */
+ WERROR status;
+}
+SPOOL_R_OPEN_PRINTER;
+
+/* SPOOL_Q_OPEN_PRINTER_EX request to open a printer */
+typedef struct spool_q_open_printer_ex
+{
+ uint32 printername_ptr;
+ UNISTR2 printername;
+ PRINTER_DEFAULT printer_default;
+ uint32 user_switch;
+ SPOOL_USER_CTR user_ctr;
+}
+SPOOL_Q_OPEN_PRINTER_EX;
+
+/* SPOOL_R_OPEN_PRINTER_EX reply to an open printer */
+typedef struct spool_r_open_printer_ex
+{
+ POLICY_HND handle; /* handle used along all transactions (20*uint8) */
+ WERROR status;
+}
+SPOOL_R_OPEN_PRINTER_EX;
+
+typedef struct spool_notify_option_type
+{
+ uint16 type;
+ uint16 reserved0;
+ uint32 reserved1;
+ uint32 reserved2;
+ uint32 count;
+ uint32 fields_ptr;
+ uint32 count2;
+ uint16 fields[MAX_NOTIFY_TYPE_FOR_NOW];
+}
+SPOOL_NOTIFY_OPTION_TYPE;
+
+typedef struct spool_notify_option_type_ctr
+{
+ uint32 count;
+ SPOOL_NOTIFY_OPTION_TYPE *type;
+}
+SPOOL_NOTIFY_OPTION_TYPE_CTR;
+
+
+
+typedef struct s_header_type
+{
+ uint32 type;
+ union
+ {
+ uint32 value;
+ UNISTR string;
+ }
+ data;
+}
+HEADER_TYPE;
+
+typedef struct new_buffer
+{
+ uint32 ptr;
+ uint32 size;
+ prs_struct prs;
+ uint32 struct_start;
+ uint32 string_at_end;
+}
+NEW_BUFFER;
+
+typedef struct spool_q_getprinterdata
+{
+ POLICY_HND handle;
+ UNISTR2 valuename;
+ uint32 size;
+}
+SPOOL_Q_GETPRINTERDATA;
+
+typedef struct spool_r_getprinterdata
+{
+ uint32 type;
+ uint32 size;
+ uint8 *data;
+ uint32 needed;
+ WERROR status;
+}
+SPOOL_R_GETPRINTERDATA;
+
+typedef struct spool_q_deleteprinterdata
+{
+ POLICY_HND handle;
+ UNISTR2 valuename;
+}
+SPOOL_Q_DELETEPRINTERDATA;
+
+typedef struct spool_r_deleteprinterdata
+{
+ WERROR status;
+}
+SPOOL_R_DELETEPRINTERDATA;
+
+typedef struct spool_q_closeprinter
+{
+ POLICY_HND handle;
+}
+SPOOL_Q_CLOSEPRINTER;
+
+typedef struct spool_r_closeprinter
+{
+ POLICY_HND handle;
+ WERROR status;
+}
+SPOOL_R_CLOSEPRINTER;
+
+typedef struct spool_q_startpageprinter
+{
+ POLICY_HND handle;
+}
+SPOOL_Q_STARTPAGEPRINTER;
+
+typedef struct spool_r_startpageprinter
+{
+ WERROR status;
+}
+SPOOL_R_STARTPAGEPRINTER;
+
+typedef struct spool_q_endpageprinter
+{
+ POLICY_HND handle;
+}
+SPOOL_Q_ENDPAGEPRINTER;
+
+typedef struct spool_r_endpageprinter
+{
+ WERROR status;
+}
+SPOOL_R_ENDPAGEPRINTER;
+
+
+typedef struct spool_q_deleteprinterdriver
+{
+ uint32 server_ptr;
+ UNISTR2 server;
+ UNISTR2 arch;
+ UNISTR2 driver;
+}
+SPOOL_Q_DELETEPRINTERDRIVER;
+
+typedef struct spool_r_deleteprinterdriver
+{
+ WERROR status;
+}
+SPOOL_R_DELETEPRINTERDRIVER;
+
+typedef struct spool_q_deleteprinterdriverex
+{
+ uint32 server_ptr;
+ UNISTR2 server;
+ UNISTR2 arch;
+ UNISTR2 driver;
+ uint32 delete_flags;
+ uint32 version;
+}
+SPOOL_Q_DELETEPRINTERDRIVEREX;
+
+typedef struct spool_r_deleteprinterdriverex
+{
+ WERROR status;
+}
+SPOOL_R_DELETEPRINTERDRIVEREX;
+
+
+typedef struct spool_doc_info_1
+{
+ uint32 p_docname;
+ uint32 p_outputfile;
+ uint32 p_datatype;
+ UNISTR2 docname;
+ UNISTR2 outputfile;
+ UNISTR2 datatype;
+}
+DOC_INFO_1;
+
+typedef struct spool_doc_info
+{
+ uint32 switch_value;
+ DOC_INFO_1 doc_info_1;
+}
+DOC_INFO;
+
+typedef struct spool_doc_info_container
+{
+ uint32 level;
+ DOC_INFO docinfo;
+}
+DOC_INFO_CONTAINER;
+
+typedef struct spool_q_startdocprinter
+{
+ POLICY_HND handle;
+ DOC_INFO_CONTAINER doc_info_container;
+}
+SPOOL_Q_STARTDOCPRINTER;
+
+typedef struct spool_r_startdocprinter
+{
+ uint32 jobid;
+ WERROR status;
+}
+SPOOL_R_STARTDOCPRINTER;
+
+typedef struct spool_q_enddocprinter
+{
+ POLICY_HND handle;
+}
+SPOOL_Q_ENDDOCPRINTER;
+
+typedef struct spool_r_enddocprinter
+{
+ WERROR status;
+}
+SPOOL_R_ENDDOCPRINTER;
+
+typedef struct spool_q_writeprinter
+{
+ POLICY_HND handle;
+ uint32 buffer_size;
+ uint8 *buffer;
+ uint32 buffer_size2;
+}
+SPOOL_Q_WRITEPRINTER;
+
+typedef struct spool_r_writeprinter
+{
+ uint32 buffer_written;
+ WERROR status;
+}
+SPOOL_R_WRITEPRINTER;
+
+typedef struct spool_notify_option
+{
+ uint32 version;
+ uint32 flags;
+ uint32 count;
+ uint32 option_type_ptr;
+ SPOOL_NOTIFY_OPTION_TYPE_CTR ctr;
+}
+SPOOL_NOTIFY_OPTION;
+
+typedef struct spool_notify_info_data
+{
+ uint16 type;
+ uint16 field;
+ uint32 reserved;
+ uint32 id;
+ union {
+ uint32 value[2];
+ struct {
+ uint32 length;
+ uint16 *string;
+ } data;
+ struct {
+ uint32 size;
+ SEC_DESC *desc;
+ } sd;
+ }
+ notify_data;
+ uint32 size;
+ BOOL enc_type;
+} SPOOL_NOTIFY_INFO_DATA;
+
+typedef struct spool_notify_info
+{
+ uint32 version;
+ uint32 flags;
+ uint32 count;
+ SPOOL_NOTIFY_INFO_DATA *data;
+}
+SPOOL_NOTIFY_INFO;
+
+/* If the struct name looks obscure, yes it is ! */
+/* RemoteFindFirstPrinterChangeNotificationEx query struct */
+typedef struct spoolss_q_rffpcnex
+{
+ POLICY_HND handle;
+ uint32 flags;
+ uint32 options;
+ uint32 localmachine_ptr;
+ UNISTR2 localmachine;
+ uint32 printerlocal;
+ uint32 option_ptr;
+ SPOOL_NOTIFY_OPTION *option;
+}
+SPOOL_Q_RFFPCNEX;
+
+typedef struct spool_r_rffpcnex
+{
+ WERROR status;
+}
+SPOOL_R_RFFPCNEX;
+
+/* Remote Find Next Printer Change Notify Ex */
+typedef struct spool_q_rfnpcnex
+{
+ POLICY_HND handle;
+ uint32 change;
+ uint32 option_ptr;
+ SPOOL_NOTIFY_OPTION *option;
+}
+SPOOL_Q_RFNPCNEX;
+
+typedef struct spool_r_rfnpcnex
+{
+ uint32 info_ptr;
+ SPOOL_NOTIFY_INFO info;
+ WERROR status;
+}
+SPOOL_R_RFNPCNEX;
+
+/* Find Close Printer Notify */
+typedef struct spool_q_fcpn
+{
+ POLICY_HND handle;
+}
+SPOOL_Q_FCPN;
+
+typedef struct spool_r_fcpn
+{
+ WERROR status;
+}
+SPOOL_R_FCPN;
+
+
+typedef struct printer_info_0
+{
+ UNISTR printername;
+ UNISTR servername;
+ uint32 cjobs;
+ uint32 total_jobs;
+ uint32 total_bytes;
+
+ uint16 year;
+ uint16 month;
+ uint16 dayofweek;
+ uint16 day;
+ uint16 hour;
+ uint16 minute;
+ uint16 second;
+ uint16 milliseconds;
+
+ uint32 global_counter;
+ uint32 total_pages;
+
+ uint16 major_version;
+ uint16 build_version;
+
+ uint32 unknown7;
+ uint32 unknown8;
+ uint32 unknown9;
+ uint32 session_counter;
+ uint32 unknown11;
+ uint32 printer_errors;
+ uint32 unknown13;
+ uint32 unknown14;
+ uint32 unknown15;
+ uint32 unknown16;
+ uint32 change_id;
+ uint32 unknown18;
+ uint32 status;
+ uint32 unknown20;
+ uint32 c_setprinter;
+
+ uint16 unknown22;
+ uint16 unknown23;
+ uint16 unknown24;
+ uint16 unknown25;
+ uint16 unknown26;
+ uint16 unknown27;
+ uint16 unknown28;
+ uint16 unknown29;
+} PRINTER_INFO_0;
+
+typedef struct printer_info_1
+{
+ uint32 flags;
+ UNISTR description;
+ UNISTR name;
+ UNISTR comment;
+}
+PRINTER_INFO_1;
+
+typedef struct printer_info_2
+{
+ UNISTR servername;
+ UNISTR printername;
+ UNISTR sharename;
+ UNISTR portname;
+ UNISTR drivername;
+ UNISTR comment;
+ UNISTR location;
+ DEVICEMODE *devmode;
+ UNISTR sepfile;
+ UNISTR printprocessor;
+ UNISTR datatype;
+ UNISTR parameters;
+ SEC_DESC *secdesc;
+ uint32 attributes;
+ uint32 priority;
+ uint32 defaultpriority;
+ uint32 starttime;
+ uint32 untiltime;
+ uint32 status;
+ uint32 cjobs;
+ uint32 averageppm;
+}
+PRINTER_INFO_2;
+
+typedef struct printer_info_3
+{
+ uint32 flags;
+ SEC_DESC *secdesc;
+}
+PRINTER_INFO_3;
+
+typedef struct printer_info_4
+{
+ UNISTR printername;
+ UNISTR servername;
+ uint32 attributes;
+}
+PRINTER_INFO_4;
+
+typedef struct printer_info_5
+{
+ UNISTR printername;
+ UNISTR portname;
+ uint32 attributes;
+ uint32 device_not_selected_timeout;
+ uint32 transmission_retry_timeout;
+}
+PRINTER_INFO_5;
+
+#define SPOOL_DS_PUBLISH 1
+#define SPOOL_DS_UPDATE 2
+#define SPOOL_DS_UNPUBLISH 4
+#define SPOOL_DS_PENDING 0x80000000
+
+typedef struct printer_info_7
+{
+ UNISTR guid; /* text form of printer guid */
+ uint32 action;
+}
+PRINTER_INFO_7;
+
+typedef struct spool_q_enumprinters
+{
+ uint32 flags;
+ uint32 servername_ptr;
+ UNISTR2 servername;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_ENUMPRINTERS;
+
+typedef struct printer_info_ctr_info
+{
+ PRINTER_INFO_0 *printers_0;
+ PRINTER_INFO_1 *printers_1;
+ PRINTER_INFO_2 *printers_2;
+ PRINTER_INFO_3 *printers_3;
+ PRINTER_INFO_4 *printers_4;
+ PRINTER_INFO_5 *printers_5;
+}
+PRINTER_INFO_CTR;
+
+typedef struct spool_r_enumprinters
+{
+ NEW_BUFFER *buffer;
+ uint32 needed; /* bytes needed */
+ uint32 returned; /* number of printers */
+ WERROR status;
+}
+SPOOL_R_ENUMPRINTERS;
+
+
+typedef struct spool_q_getprinter
+{
+ POLICY_HND handle;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_GETPRINTER;
+
+typedef struct printer_info_info
+{
+ union
+ {
+ PRINTER_INFO_0 *info0;
+ PRINTER_INFO_1 *info1;
+ PRINTER_INFO_2 *info2;
+ void *info;
+ } printer;
+} PRINTER_INFO;
+
+typedef struct spool_r_getprinter
+{
+ NEW_BUFFER *buffer;
+ uint32 needed;
+ WERROR status;
+} SPOOL_R_GETPRINTER;
+
+typedef struct driver_info_1
+{
+ UNISTR name;
+} DRIVER_INFO_1;
+
+typedef struct driver_info_2
+{
+ uint32 version;
+ UNISTR name;
+ UNISTR architecture;
+ UNISTR driverpath;
+ UNISTR datafile;
+ UNISTR configfile;
+} DRIVER_INFO_2;
+
+typedef struct driver_info_3
+{
+ uint32 version;
+ UNISTR name;
+ UNISTR architecture;
+ UNISTR driverpath;
+ UNISTR datafile;
+ UNISTR configfile;
+ UNISTR helpfile;
+ uint16 *dependentfiles;
+ UNISTR monitorname;
+ UNISTR defaultdatatype;
+}
+DRIVER_INFO_3;
+
+typedef struct driver_info_6
+{
+ uint32 version;
+ UNISTR name;
+ UNISTR architecture;
+ UNISTR driverpath;
+ UNISTR datafile;
+ UNISTR configfile;
+ UNISTR helpfile;
+ uint16 *dependentfiles;
+ UNISTR monitorname;
+ UNISTR defaultdatatype;
+ uint16* previousdrivernames;
+ NTTIME driver_date;
+ uint32 padding;
+ uint32 driver_version_low;
+ uint32 driver_version_high;
+ UNISTR mfgname;
+ UNISTR oem_url;
+ UNISTR hardware_id;
+ UNISTR provider;
+}
+DRIVER_INFO_6;
+
+typedef struct driver_info_info
+{
+ DRIVER_INFO_1 *info1;
+ DRIVER_INFO_2 *info2;
+ DRIVER_INFO_3 *info3;
+ DRIVER_INFO_6 *info6;
+}
+PRINTER_DRIVER_CTR;
+
+typedef struct spool_q_getprinterdriver2
+{
+ POLICY_HND handle;
+ uint32 architecture_ptr;
+ UNISTR2 architecture;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+ uint32 clientmajorversion;
+ uint32 clientminorversion;
+}
+SPOOL_Q_GETPRINTERDRIVER2;
+
+typedef struct spool_r_getprinterdriver2
+{
+ NEW_BUFFER *buffer;
+ uint32 needed;
+ uint32 servermajorversion;
+ uint32 serverminorversion;
+ WERROR status;
+}
+SPOOL_R_GETPRINTERDRIVER2;
+
+
+typedef struct add_jobinfo_1
+{
+ UNISTR path;
+ uint32 job_number;
+}
+ADD_JOBINFO_1;
+
+
+typedef struct spool_q_addjob
+{
+ POLICY_HND handle;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_ADDJOB;
+
+typedef struct spool_r_addjob
+{
+ NEW_BUFFER *buffer;
+ uint32 needed;
+ WERROR status;
+}
+SPOOL_R_ADDJOB;
+
+/*
+ * I'm really wondering how many different time formats
+ * I will have to cope with
+ *
+ * JFM, 09/13/98 In a mad mood ;-(
+*/
+typedef struct systemtime
+{
+ uint16 year;
+ uint16 month;
+ uint16 dayofweek;
+ uint16 day;
+ uint16 hour;
+ uint16 minute;
+ uint16 second;
+ uint16 milliseconds;
+}
+SYSTEMTIME;
+
+typedef struct s_job_info_1
+{
+ uint32 jobid;
+ UNISTR printername;
+ UNISTR machinename;
+ UNISTR username;
+ UNISTR document;
+ UNISTR datatype;
+ UNISTR text_status;
+ uint32 status;
+ uint32 priority;
+ uint32 position;
+ uint32 totalpages;
+ uint32 pagesprinted;
+ SYSTEMTIME submitted;
+}
+JOB_INFO_1;
+
+typedef struct s_job_info_2
+{
+ uint32 jobid;
+ UNISTR printername;
+ UNISTR machinename;
+ UNISTR username;
+ UNISTR document;
+ UNISTR notifyname;
+ UNISTR datatype;
+ UNISTR printprocessor;
+ UNISTR parameters;
+ UNISTR drivername;
+ DEVICEMODE *devmode;
+ UNISTR text_status;
+/* SEC_DESC sec_desc;*/
+ uint32 status;
+ uint32 priority;
+ uint32 position;
+ uint32 starttime;
+ uint32 untiltime;
+ uint32 totalpages;
+ uint32 size;
+ SYSTEMTIME submitted;
+ uint32 timeelapsed;
+ uint32 pagesprinted;
+}
+JOB_INFO_2;
+
+typedef struct spool_q_enumjobs
+{
+ POLICY_HND handle;
+ uint32 firstjob;
+ uint32 numofjobs;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_ENUMJOBS;
+
+typedef struct job_info_ctr_info
+{
+ union
+ {
+ JOB_INFO_1 *job_info_1;
+ JOB_INFO_2 *job_info_2;
+ void *info;
+ } job;
+
+} JOB_INFO_CTR;
+
+typedef struct spool_r_enumjobs
+{
+ NEW_BUFFER *buffer;
+ uint32 needed;
+ uint32 returned;
+ WERROR status;
+}
+SPOOL_R_ENUMJOBS;
+
+typedef struct spool_q_schedulejob
+{
+ POLICY_HND handle;
+ uint32 jobid;
+}
+SPOOL_Q_SCHEDULEJOB;
+
+typedef struct spool_r_schedulejob
+{
+ WERROR status;
+}
+SPOOL_R_SCHEDULEJOB;
+
+typedef struct s_port_info_1
+{
+ UNISTR port_name;
+}
+PORT_INFO_1;
+
+typedef struct s_port_info_2
+{
+ UNISTR port_name;
+ UNISTR monitor_name;
+ UNISTR description;
+ uint32 port_type;
+ uint32 reserved;
+}
+PORT_INFO_2;
+
+/* Port Type bits */
+#define PORT_TYPE_WRITE 0x0001
+#define PORT_TYPE_READ 0x0002
+#define PORT_TYPE_REDIRECTED 0x0004
+#define PORT_TYPE_NET_ATTACHED 0x0008
+
+typedef struct spool_q_enumports
+{
+ uint32 name_ptr;
+ UNISTR2 name;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_ENUMPORTS;
+
+typedef struct port_info_ctr_info
+{
+ union
+ {
+ PORT_INFO_1 *info_1;
+ PORT_INFO_2 *info_2;
+ }
+ port;
+
+}
+PORT_INFO_CTR;
+
+typedef struct spool_r_enumports
+{
+ NEW_BUFFER *buffer;
+ uint32 needed; /* bytes needed */
+ uint32 returned; /* number of printers */
+ WERROR status;
+}
+SPOOL_R_ENUMPORTS;
+
+#define JOB_CONTROL_PAUSE 1
+#define JOB_CONTROL_RESUME 2
+#define JOB_CONTROL_CANCEL 3
+#define JOB_CONTROL_RESTART 4
+#define JOB_CONTROL_DELETE 5
+
+typedef struct job_info_info
+{
+ union
+ {
+ JOB_INFO_1 job_info_1;
+ JOB_INFO_2 job_info_2;
+ }
+ job;
+
+}
+JOB_INFO;
+
+typedef struct spool_q_setjob
+{
+ POLICY_HND handle;
+ uint32 jobid;
+ uint32 level;
+ JOB_INFO ctr;
+ uint32 command;
+
+}
+SPOOL_Q_SETJOB;
+
+typedef struct spool_r_setjob
+{
+ WERROR status;
+
+}
+SPOOL_R_SETJOB;
+
+typedef struct spool_q_enumprinterdrivers
+{
+ uint32 name_ptr;
+ UNISTR2 name;
+ uint32 environment_ptr;
+ UNISTR2 environment;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_ENUMPRINTERDRIVERS;
+
+typedef struct spool_r_enumprinterdrivers
+{
+ NEW_BUFFER *buffer;
+ uint32 needed;
+ uint32 returned;
+ WERROR status;
+}
+SPOOL_R_ENUMPRINTERDRIVERS;
+
+#define FORM_USER 0
+#define FORM_BUILTIN 1
+#define FORM_PRINTER 2
+
+typedef struct spool_form_1
+{
+ uint32 flag;
+ UNISTR name;
+ uint32 width;
+ uint32 length;
+ uint32 left;
+ uint32 top;
+ uint32 right;
+ uint32 bottom;
+}
+FORM_1;
+
+typedef struct spool_q_enumforms
+{
+ POLICY_HND handle;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_ENUMFORMS;
+
+typedef struct spool_r_enumforms
+{
+ NEW_BUFFER *buffer;
+ uint32 needed;
+ uint32 numofforms;
+ WERROR status;
+}
+SPOOL_R_ENUMFORMS;
+
+typedef struct spool_q_getform
+{
+ POLICY_HND handle;
+ UNISTR2 formname;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_GETFORM;
+
+typedef struct spool_r_getform
+{
+ NEW_BUFFER *buffer;
+ uint32 needed;
+ WERROR status;
+}
+SPOOL_R_GETFORM;
+
+typedef struct spool_printer_info_level_1
+{
+ uint32 flags;
+ uint32 description_ptr;
+ uint32 name_ptr;
+ uint32 comment_ptr;
+ UNISTR2 description;
+ UNISTR2 name;
+ UNISTR2 comment;
+} SPOOL_PRINTER_INFO_LEVEL_1;
+
+typedef struct spool_printer_info_level_2
+{
+ uint32 servername_ptr;
+ uint32 printername_ptr;
+ uint32 sharename_ptr;
+ uint32 portname_ptr;
+ uint32 drivername_ptr;
+ uint32 comment_ptr;
+ uint32 location_ptr;
+ uint32 devmode_ptr;
+ uint32 sepfile_ptr;
+ uint32 printprocessor_ptr;
+ uint32 datatype_ptr;
+ uint32 parameters_ptr;
+ uint32 secdesc_ptr;
+ uint32 attributes;
+ uint32 priority;
+ uint32 default_priority;
+ uint32 starttime;
+ uint32 untiltime;
+ uint32 status;
+ uint32 cjobs;
+ uint32 averageppm;
+ UNISTR2 servername;
+ UNISTR2 printername;
+ UNISTR2 sharename;
+ UNISTR2 portname;
+ UNISTR2 drivername;
+ UNISTR2 comment;
+ UNISTR2 location;
+ UNISTR2 sepfile;
+ UNISTR2 printprocessor;
+ UNISTR2 datatype;
+ UNISTR2 parameters;
+}
+SPOOL_PRINTER_INFO_LEVEL_2;
+
+typedef struct spool_printer_info_level_3
+{
+ uint32 secdesc_ptr;
+}
+SPOOL_PRINTER_INFO_LEVEL_3;
+
+typedef struct spool_printer_info_level_7
+{
+ uint32 guid_ptr;
+ uint32 action;
+ UNISTR2 guid;
+}
+SPOOL_PRINTER_INFO_LEVEL_7;
+
+typedef struct spool_printer_info_level
+{
+ uint32 level;
+ uint32 info_ptr;
+ SPOOL_PRINTER_INFO_LEVEL_1 *info_1;
+ SPOOL_PRINTER_INFO_LEVEL_2 *info_2;
+ SPOOL_PRINTER_INFO_LEVEL_3 *info_3;
+ SPOOL_PRINTER_INFO_LEVEL_7 *info_7;
+}
+SPOOL_PRINTER_INFO_LEVEL;
+
+typedef struct spool_printer_driver_info_level_3
+{
+ uint32 cversion;
+ uint32 name_ptr;
+ uint32 environment_ptr;
+ uint32 driverpath_ptr;
+ uint32 datafile_ptr;
+ uint32 configfile_ptr;
+ uint32 helpfile_ptr;
+ uint32 monitorname_ptr;
+ uint32 defaultdatatype_ptr;
+ uint32 dependentfilessize;
+ uint32 dependentfiles_ptr;
+
+ UNISTR2 name;
+ UNISTR2 environment;
+ UNISTR2 driverpath;
+ UNISTR2 datafile;
+ UNISTR2 configfile;
+ UNISTR2 helpfile;
+ UNISTR2 monitorname;
+ UNISTR2 defaultdatatype;
+ BUFFER5 dependentfiles;
+
+}
+SPOOL_PRINTER_DRIVER_INFO_LEVEL_3;
+
+/* SPOOL_PRINTER_DRIVER_INFO_LEVEL_6 structure */
+typedef struct {
+ uint32 version;
+ uint32 name_ptr;
+ uint32 environment_ptr;
+ uint32 driverpath_ptr;
+ uint32 datafile_ptr;
+ uint32 configfile_ptr;
+ uint32 helpfile_ptr;
+ uint32 monitorname_ptr;
+ uint32 defaultdatatype_ptr;
+ uint32 dependentfiles_len;
+ uint32 dependentfiles_ptr;
+ uint32 previousnames_len;
+ uint32 previousnames_ptr;
+ NTTIME driverdate;
+ UINT64_S driverversion;
+ uint32 dummy4;
+ uint32 mfgname_ptr;
+ uint32 oemurl_ptr;
+ uint32 hardwareid_ptr;
+ uint32 provider_ptr;
+ UNISTR2 name;
+ UNISTR2 environment;
+ UNISTR2 driverpath;
+ UNISTR2 datafile;
+ UNISTR2 configfile;
+ UNISTR2 helpfile;
+ UNISTR2 monitorname;
+ UNISTR2 defaultdatatype;
+ BUFFER5 dependentfiles;
+ BUFFER5 previousnames;
+ UNISTR2 mfgname;
+ UNISTR2 oemurl;
+ UNISTR2 hardwareid;
+ UNISTR2 provider;
+} SPOOL_PRINTER_DRIVER_INFO_LEVEL_6;
+
+
+typedef struct spool_printer_driver_info_level
+{
+ uint32 level;
+ uint32 ptr;
+ SPOOL_PRINTER_DRIVER_INFO_LEVEL_3 *info_3;
+ SPOOL_PRINTER_DRIVER_INFO_LEVEL_6 *info_6;
+}
+SPOOL_PRINTER_DRIVER_INFO_LEVEL;
+
+
+/* this struct is undocumented */
+/* thanks to the ddk ... */
+typedef struct spool_user_level_1
+{
+ uint32 size;
+ uint32 client_name_ptr;
+ uint32 user_name_ptr;
+ uint32 build;
+ uint32 major;
+ uint32 minor;
+ uint32 processor;
+ UNISTR2 client_name;
+ UNISTR2 user_name;
+}
+SPOOL_USER_LEVEL_1;
+
+typedef struct spool_user_level
+{
+ SPOOL_USER_LEVEL_1 *user_level_1;
+}
+SPOOL_USER_LEVEL;
+
+typedef struct spool_q_setprinter
+{
+ POLICY_HND handle;
+ uint32 level;
+ SPOOL_PRINTER_INFO_LEVEL info;
+ SEC_DESC_BUF *secdesc_ctr;
+ DEVMODE_CTR devmode_ctr;
+
+ uint32 command;
+
+}
+SPOOL_Q_SETPRINTER;
+
+typedef struct spool_r_setprinter
+{
+ WERROR status;
+}
+SPOOL_R_SETPRINTER;
+
+typedef struct spool_q_addprinter
+{
+ UNISTR2 server_name;
+ uint32 level;
+ SPOOL_PRINTER_INFO_LEVEL info;
+ DEVMODE_CTR devmode_ctr;
+ SEC_DESC_BUF *secdesc_ctr;
+ uint32 user_level;
+ SPOOL_USER_LEVEL user;
+}
+SPOOL_Q_ADDPRINTER;
+
+typedef struct spool_r_addprinter
+{
+ WERROR status;
+}
+SPOOL_R_ADDPRINTER;
+
+typedef struct spool_q_deleteprinter
+{
+ POLICY_HND handle;
+}
+SPOOL_Q_DELETEPRINTER;
+
+typedef struct spool_r_deleteprinter
+{
+ POLICY_HND handle;
+ WERROR status;
+}
+SPOOL_R_DELETEPRINTER;
+
+typedef struct spool_q_abortprinter
+{
+ POLICY_HND handle;
+}
+SPOOL_Q_ABORTPRINTER;
+
+typedef struct spool_r_abortprinter
+{
+ WERROR status;
+}
+SPOOL_R_ABORTPRINTER;
+
+
+typedef struct spool_q_addprinterex
+{
+ uint32 server_name_ptr;
+ UNISTR2 server_name;
+ uint32 level;
+ SPOOL_PRINTER_INFO_LEVEL info;
+ DEVMODE_CTR devmode_ctr;
+ SEC_DESC_BUF *secdesc_ctr;
+ uint32 user_switch;
+ SPOOL_USER_CTR user_ctr;
+}
+SPOOL_Q_ADDPRINTEREX;
+
+typedef struct spool_r_addprinterex
+{
+ POLICY_HND handle;
+ WERROR status;
+}
+SPOOL_R_ADDPRINTEREX;
+
+
+typedef struct spool_q_addprinterdriver
+{
+ uint32 server_name_ptr;
+ UNISTR2 server_name;
+ uint32 level;
+ SPOOL_PRINTER_DRIVER_INFO_LEVEL info;
+}
+SPOOL_Q_ADDPRINTERDRIVER;
+
+typedef struct spool_r_addprinterdriver
+{
+ WERROR status;
+}
+SPOOL_R_ADDPRINTERDRIVER;
+
+typedef struct spool_q_addprinterdriverex
+{
+ uint32 server_name_ptr;
+ UNISTR2 server_name;
+ uint32 level;
+ SPOOL_PRINTER_DRIVER_INFO_LEVEL info;
+ uint32 copy_flags;
+}
+SPOOL_Q_ADDPRINTERDRIVEREX;
+
+typedef struct spool_r_addprinterdriverex
+{
+ WERROR status;
+}
+SPOOL_R_ADDPRINTERDRIVEREX;
+
+
+typedef struct driver_directory_1
+{
+ UNISTR name;
+}
+DRIVER_DIRECTORY_1;
+
+typedef struct driver_info_ctr_info
+{
+ DRIVER_DIRECTORY_1 *info1;
+}
+DRIVER_DIRECTORY_CTR;
+
+typedef struct spool_q_getprinterdriverdirectory
+{
+ uint32 name_ptr;
+ UNISTR2 name;
+ uint32 environment_ptr;
+ UNISTR2 environment;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_GETPRINTERDRIVERDIR;
+
+typedef struct spool_r_getprinterdriverdirectory
+{
+ NEW_BUFFER *buffer;
+ uint32 needed;
+ WERROR status;
+}
+SPOOL_R_GETPRINTERDRIVERDIR;
+
+typedef struct spool_q_addprintprocessor
+{
+ uint32 server_ptr;
+ UNISTR2 server;
+ UNISTR2 environment;
+ UNISTR2 path;
+ UNISTR2 name;
+}
+SPOOL_Q_ADDPRINTPROCESSOR;
+
+typedef struct spool_r_addprintprocessor
+{
+ WERROR status;
+}
+SPOOL_R_ADDPRINTPROCESSOR;
+
+
+typedef struct spool_q_enumprintprocessors
+{
+ uint32 name_ptr;
+ UNISTR2 name;
+ uint32 environment_ptr;
+ UNISTR2 environment;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_ENUMPRINTPROCESSORS;
+
+typedef struct printprocessor_1
+{
+ UNISTR name;
+}
+PRINTPROCESSOR_1;
+
+typedef struct spool_r_enumprintprocessors
+{
+ NEW_BUFFER *buffer;
+ uint32 needed;
+ uint32 returned;
+ WERROR status;
+}
+SPOOL_R_ENUMPRINTPROCESSORS;
+
+typedef struct spool_q_enumprintprocdatatypes
+{
+ uint32 name_ptr;
+ UNISTR2 name;
+ uint32 processor_ptr;
+ UNISTR2 processor;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_ENUMPRINTPROCDATATYPES;
+
+typedef struct ppdatatype_1
+{
+ UNISTR name;
+}
+PRINTPROCDATATYPE_1;
+
+typedef struct spool_r_enumprintprocdatatypes
+{
+ NEW_BUFFER *buffer;
+ uint32 needed;
+ uint32 returned;
+ WERROR status;
+}
+SPOOL_R_ENUMPRINTPROCDATATYPES;
+
+typedef struct printmonitor_1
+{
+ UNISTR name;
+}
+PRINTMONITOR_1;
+
+typedef struct printmonitor_2
+{
+ UNISTR name;
+ UNISTR environment;
+ UNISTR dll_name;
+}
+PRINTMONITOR_2;
+
+typedef struct spool_q_enumprintmonitors
+{
+ uint32 name_ptr;
+ UNISTR2 name;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_ENUMPRINTMONITORS;
+
+typedef struct spool_r_enumprintmonitors
+{
+ NEW_BUFFER *buffer;
+ uint32 needed;
+ uint32 returned;
+ WERROR status;
+}
+SPOOL_R_ENUMPRINTMONITORS;
+
+
+typedef struct spool_q_enumprinterdata
+{
+ POLICY_HND handle;
+ uint32 index;
+ uint32 valuesize;
+ uint32 datasize;
+}
+SPOOL_Q_ENUMPRINTERDATA;
+
+typedef struct spool_r_enumprinterdata
+{
+ uint32 valuesize;
+ uint16 *value;
+ uint32 realvaluesize;
+ uint32 type;
+ uint32 datasize;
+ uint8 *data;
+ uint32 realdatasize;
+ WERROR status;
+}
+SPOOL_R_ENUMPRINTERDATA;
+
+typedef struct spool_q_setprinterdata
+{
+ POLICY_HND handle;
+ UNISTR2 value;
+ uint32 type;
+ uint32 max_len;
+ uint8 *data;
+ uint32 real_len;
+ uint32 numeric_data;
+}
+SPOOL_Q_SETPRINTERDATA;
+
+typedef struct spool_r_setprinterdata
+{
+ WERROR status;
+}
+SPOOL_R_SETPRINTERDATA;
+
+typedef struct spool_q_resetprinter
+{
+ POLICY_HND handle;
+ uint32 datatype_ptr;
+ UNISTR2 datatype;
+ DEVMODE_CTR devmode_ctr;
+
+} SPOOL_Q_RESETPRINTER;
+
+typedef struct spool_r_resetprinter
+{
+ WERROR status;
+}
+SPOOL_R_RESETPRINTER;
+
+
+
+typedef struct _form
+{
+ uint32 flags;
+ uint32 name_ptr;
+ uint32 size_x;
+ uint32 size_y;
+ uint32 left;
+ uint32 top;
+ uint32 right;
+ uint32 bottom;
+ UNISTR2 name;
+}
+FORM;
+
+typedef struct spool_q_addform
+{
+ POLICY_HND handle;
+ uint32 level;
+ uint32 level2; /* This should really be part of the FORM structure */
+ FORM form;
+}
+SPOOL_Q_ADDFORM;
+
+typedef struct spool_r_addform
+{
+ WERROR status;
+}
+SPOOL_R_ADDFORM;
+
+typedef struct spool_q_setform
+{
+ POLICY_HND handle;
+ UNISTR2 name;
+ uint32 level;
+ uint32 level2;
+ FORM form;
+}
+SPOOL_Q_SETFORM;
+
+typedef struct spool_r_setform
+{
+ WERROR status;
+}
+SPOOL_R_SETFORM;
+
+typedef struct spool_q_deleteform
+{
+ POLICY_HND handle;
+ UNISTR2 name;
+}
+SPOOL_Q_DELETEFORM;
+
+typedef struct spool_r_deleteform
+{
+ WERROR status;
+}
+SPOOL_R_DELETEFORM;
+
+typedef struct spool_q_getjob
+{
+ POLICY_HND handle;
+ uint32 jobid;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_GETJOB;
+
+typedef struct pjob_info_info
+{
+ union
+ {
+ JOB_INFO_1 *job_info_1;
+ JOB_INFO_2 *job_info_2;
+ void *info;
+ }
+ job;
+
+}
+PJOB_INFO;
+
+typedef struct spool_r_getjob
+{
+ NEW_BUFFER *buffer;
+ uint32 needed;
+ WERROR status;
+}
+SPOOL_R_GETJOB;
+
+typedef struct spool_q_replyopenprinter
+{
+ UNISTR2 string;
+ uint32 printer;
+ uint32 type;
+ uint32 unknown0;
+ uint32 unknown1;
+}
+SPOOL_Q_REPLYOPENPRINTER;
+
+typedef struct spool_r_replyopenprinter
+{
+ POLICY_HND handle;
+ WERROR status;
+}
+SPOOL_R_REPLYOPENPRINTER;
+
+typedef struct spool_q_routerreplyprinter
+{
+ POLICY_HND handle;
+ uint32 condition;
+ uint32 unknown1; /* 0x00000001 */
+ uint32 change_id;
+ uint8 unknown2[5]; /* 0x0000000001 */
+}
+SPOOL_Q_ROUTERREPLYPRINTER;
+
+typedef struct spool_r_routerreplyprinter
+{
+ WERROR status;
+}
+SPOOL_R_ROUTERREPLYPRINTER;
+
+typedef struct spool_q_replycloseprinter
+{
+ POLICY_HND handle;
+}
+SPOOL_Q_REPLYCLOSEPRINTER;
+
+typedef struct spool_r_replycloseprinter
+{
+ POLICY_HND handle;
+ WERROR status;
+}
+SPOOL_R_REPLYCLOSEPRINTER;
+
+typedef struct spool_q_rrpcn
+{
+ POLICY_HND handle;
+ uint32 change_low;
+ uint32 change_high;
+ uint32 unknown0;
+ uint32 unknown1;
+ uint32 info_ptr;
+ SPOOL_NOTIFY_INFO info;
+}
+SPOOL_Q_REPLY_RRPCN;
+
+typedef struct spool_r_rrpcn
+{
+ uint32 unknown0;
+ WERROR status;
+}
+SPOOL_R_REPLY_RRPCN;
+
+typedef struct spool_q_getprinterdataex
+{
+ POLICY_HND handle;
+ UNISTR2 keyname;
+ UNISTR2 valuename;
+ uint32 size;
+}
+SPOOL_Q_GETPRINTERDATAEX;
+
+typedef struct spool_r_getprinterdataex
+{
+ uint32 type;
+ uint32 size;
+ uint8 *data;
+ uint32 needed;
+ WERROR status;
+}
+SPOOL_R_GETPRINTERDATAEX;
+
+typedef struct spool_q_setprinterdataex
+{
+ POLICY_HND handle;
+ UNISTR2 key;
+ UNISTR2 value;
+ uint32 type;
+ uint32 max_len;
+ uint8 *data;
+ uint32 real_len;
+ uint32 numeric_data;
+}
+SPOOL_Q_SETPRINTERDATAEX;
+
+typedef struct spool_r_setprinterdataex
+{
+ WERROR status;
+}
+SPOOL_R_SETPRINTERDATAEX;
+
+
+typedef struct spool_q_deleteprinterdataex
+{
+ POLICY_HND handle;
+ UNISTR2 keyname;
+ UNISTR2 valuename;
+}
+SPOOL_Q_DELETEPRINTERDATAEX;
+
+typedef struct spool_r_deleteprinterdataex
+{
+ WERROR status;
+}
+SPOOL_R_DELETEPRINTERDATAEX;
+
+
+typedef struct spool_q_enumprinterkey
+{
+ POLICY_HND handle;
+ UNISTR2 key;
+ uint32 size;
+}
+SPOOL_Q_ENUMPRINTERKEY;
+
+typedef struct spool_r_enumprinterkey
+{
+ BUFFER5 keys;
+ uint32 needed; /* in bytes */
+ WERROR status;
+}
+SPOOL_R_ENUMPRINTERKEY;
+
+typedef struct spool_q_deleteprinterkey
+{
+ POLICY_HND handle;
+ UNISTR2 keyname;
+}
+SPOOL_Q_DELETEPRINTERKEY;
+
+typedef struct spool_r_deleteprinterkey
+{
+ WERROR status;
+}
+SPOOL_R_DELETEPRINTERKEY;
+
+typedef struct printer_enum_values
+{
+ UNISTR valuename;
+ uint32 value_len;
+ uint32 type;
+ uint8 *data;
+ uint32 data_len;
+
+}
+PRINTER_ENUM_VALUES;
+
+typedef struct printer_enum_values_ctr
+{
+ uint32 size;
+ uint32 size_of_array;
+ PRINTER_ENUM_VALUES *values;
+}
+PRINTER_ENUM_VALUES_CTR;
+
+typedef struct spool_q_enumprinterdataex
+{
+ POLICY_HND handle;
+ UNISTR2 key;
+ uint32 size;
+}
+SPOOL_Q_ENUMPRINTERDATAEX;
+
+typedef struct spool_r_enumprinterdataex
+{
+ PRINTER_ENUM_VALUES_CTR ctr;
+ uint32 needed;
+ uint32 returned;
+ WERROR status;
+}
+SPOOL_R_ENUMPRINTERDATAEX;
+
+typedef struct printprocessor_directory_1
+{
+ UNISTR name;
+}
+PRINTPROCESSOR_DIRECTORY_1;
+
+typedef struct spool_q_getprintprocessordirectory
+{
+ UNISTR2 name;
+ UNISTR2 environment;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_GETPRINTPROCESSORDIRECTORY;
+
+typedef struct spool_r_getprintprocessordirectory
+{
+ NEW_BUFFER *buffer;
+ uint32 needed;
+ WERROR status;
+}
+SPOOL_R_GETPRINTPROCESSORDIRECTORY;
+
+#define PRINTER_DRIVER_VERSION 2
+#define PRINTER_DRIVER_ARCHITECTURE "Windows NT x86"
+
+#endif /* _RPC_SPOOLSS_H */
+
diff --git a/source/include/rpc_srvsvc.h b/source/include/rpc_srvsvc.h
new file mode 100644
index 00000000000..5ebb77a8c21
--- /dev/null
+++ b/source/include/rpc_srvsvc.h
@@ -0,0 +1,954 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+ Copyright (C) Paul Ashton 1997
+ Copyright (C) Nigel Williams 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _RPC_SRVSVC_H /* _RPC_SRVSVC_H */
+#define _RPC_SRVSVC_H
+
+/* srvsvc pipe */
+#define SRV_NET_CONN_ENUM 0x08
+#define SRV_NET_FILE_ENUM 0x09
+#define SRV_NET_FILE_CLOSE 0x0b
+#define SRV_NET_SESS_ENUM 0x0c
+#define SRV_NET_SHARE_ADD 0x0e
+#define SRV_NET_SHARE_ENUM_ALL 0x0f
+#define SRV_NET_SHARE_GET_INFO 0x10
+#define SRV_NET_SHARE_SET_INFO 0x11
+#define SRV_NET_SHARE_DEL 0x12
+#define SRV_NET_SHARE_DEL_STICKY 0x13
+#define SRV_NET_SRV_GET_INFO 0x15
+#define SRV_NET_SRV_SET_INFO 0x16
+#define SRV_NET_DISK_ENUM 0x17
+#define SRV_NET_REMOTE_TOD 0x1c
+#define SRV_NET_NAME_VALIDATE 0x21
+#define SRV_NET_SHARE_ENUM 0x24
+#define SRV_NET_FILE_QUERY_SECDESC 0x27
+#define SRV_NET_FILE_SET_SECDESC 0x28
+
+#define MAX_SERVER_DISK_ENTRIES 15
+
+typedef struct disk_info {
+ uint32 unknown;
+ UNISTR3 disk_name;
+} DISK_INFO;
+
+typedef struct disk_enum_container {
+ uint32 level;
+ uint32 entries_read;
+ uint32 unknown;
+ uint32 disk_info_ptr;
+ DISK_INFO *disk_info;
+} DISK_ENUM_CONTAINER;
+
+typedef struct net_srv_disk_enum {
+ uint32 ptr_srv_name; /* pointer (to server name?) */
+ UNISTR2 uni_srv_name; /* server name */
+
+ DISK_ENUM_CONTAINER disk_enum_ctr;
+
+ uint32 preferred_len; /* preferred maximum length (0xffff ffff) */
+ uint32 total_entries; /* total number of entries */
+ ENUM_HND enum_hnd;
+ WERROR status; /* return status */
+} SRV_Q_NET_DISK_ENUM, SRV_R_NET_DISK_ENUM;
+
+typedef struct net_name_validate {
+ uint32 ptr_srv_name;
+ UNISTR2 uni_srv_name;
+ UNISTR2 uni_name; /*name to validate*/
+ uint32 type;
+ uint32 flags;
+ WERROR status;
+} SRV_Q_NET_NAME_VALIDATE, SRV_R_NET_NAME_VALIDATE;
+
+/* SESS_INFO_0 (pointers to level 0 session info strings) */
+typedef struct ptr_sess_info0
+{
+ uint32 ptr_name; /* pointer to name. */
+
+} SESS_INFO_0;
+
+/* SESS_INFO_0_STR (level 0 session info strings) */
+typedef struct str_sess_info0
+{
+ UNISTR2 uni_name; /* unicode string of name */
+
+} SESS_INFO_0_STR;
+
+/* oops - this is going to take up a *massive* amount of stack. */
+/* the UNISTR2s already have 1024 uint16 chars in them... */
+#define MAX_SESS_ENTRIES 32
+
+/* SRV_SESS_INFO_0 */
+typedef struct srv_sess_info_0_info
+{
+ uint32 num_entries_read; /* EntriesRead */
+ uint32 ptr_sess_info; /* Buffer */
+ uint32 num_entries_read2; /* EntriesRead */
+
+ SESS_INFO_0 info_0 [MAX_SESS_ENTRIES]; /* session entry pointers */
+ SESS_INFO_0_STR info_0_str[MAX_SESS_ENTRIES]; /* session entry strings */
+
+} SRV_SESS_INFO_0;
+
+/* SESS_INFO_1 (pointers to level 1 session info strings) */
+typedef struct ptr_sess_info1
+{
+ uint32 ptr_name; /* pointer to name. */
+ uint32 ptr_user; /* pointer to user name. */
+
+ uint32 num_opens;
+ uint32 open_time;
+ uint32 idle_time;
+ uint32 user_flags;
+
+} SESS_INFO_1;
+
+/* SESS_INFO_1_STR (level 1 session info strings) */
+typedef struct str_sess_info1
+{
+ UNISTR2 uni_name; /* unicode string of name */
+ UNISTR2 uni_user; /* unicode string of user */
+
+} SESS_INFO_1_STR;
+
+/* SRV_SESS_INFO_1 */
+typedef struct srv_sess_info_1_info
+{
+ uint32 num_entries_read; /* EntriesRead */
+ uint32 ptr_sess_info; /* Buffer */
+ uint32 num_entries_read2; /* EntriesRead */
+
+ SESS_INFO_1 info_1 [MAX_SESS_ENTRIES]; /* session entry pointers */
+ SESS_INFO_1_STR info_1_str[MAX_SESS_ENTRIES]; /* session entry strings */
+
+} SRV_SESS_INFO_1;
+
+/* SRV_SESS_INFO_CTR */
+typedef struct srv_sess_info_ctr_info
+{
+ uint32 switch_value; /* switch value */
+ uint32 ptr_sess_ctr; /* pointer to sess info union */
+ union
+ {
+ SRV_SESS_INFO_0 info0; /* session info level 0 */
+ SRV_SESS_INFO_1 info1; /* session info level 1 */
+
+ } sess;
+
+} SRV_SESS_INFO_CTR;
+
+
+/* SRV_Q_NET_SESS_ENUM */
+typedef struct q_net_sess_enum_info
+{
+ uint32 ptr_srv_name; /* pointer (to server name?) */
+ UNISTR2 uni_srv_name; /* server name */
+
+ uint32 ptr_qual_name; /* pointer (to qualifier name) */
+ UNISTR2 uni_qual_name; /* qualifier name "\\qualifier" */
+
+ uint32 ptr_user_name; /* pointer (to user name */
+ UNISTR2 uni_user_name; /* user name */
+
+ uint32 sess_level; /* session level */
+
+ SRV_SESS_INFO_CTR *ctr;
+
+ uint32 preferred_len; /* preferred maximum length (0xffff ffff) */
+ ENUM_HND enum_hnd;
+
+} SRV_Q_NET_SESS_ENUM;
+
+/* SRV_R_NET_SESS_ENUM */
+typedef struct r_net_sess_enum_info
+{
+ uint32 sess_level; /* share level */
+
+ SRV_SESS_INFO_CTR *ctr;
+
+ uint32 total_entries; /* total number of entries */
+ ENUM_HND enum_hnd;
+
+ WERROR status; /* return status */
+
+} SRV_R_NET_SESS_ENUM;
+
+/* CONN_INFO_0 (pointers to level 0 connection info strings) */
+typedef struct ptr_conn_info0
+{
+ uint32 id; /* connection id. */
+
+} CONN_INFO_0;
+
+/* oops - this is going to take up a *massive* amount of stack. */
+/* the UNISTR2s already have 1024 uint16 chars in them... */
+#define MAX_CONN_ENTRIES 32
+
+/* SRV_CONN_INFO_0 */
+typedef struct srv_conn_info_0_info
+{
+ uint32 num_entries_read; /* EntriesRead */
+ uint32 ptr_conn_info; /* Buffer */
+ uint32 num_entries_read2; /* EntriesRead */
+
+ CONN_INFO_0 info_0 [MAX_CONN_ENTRIES]; /* connection entry pointers */
+
+} SRV_CONN_INFO_0;
+
+/* CONN_INFO_1 (pointers to level 1 connection info strings) */
+typedef struct ptr_conn_info1
+{
+ uint32 id; /* connection id */
+ uint32 type; /* 0x3 */
+ uint32 num_opens;
+ uint32 num_users;
+ uint32 open_time;
+
+ uint32 ptr_usr_name; /* pointer to user name. */
+ uint32 ptr_net_name; /* pointer to network name (e.g IPC$). */
+
+} CONN_INFO_1;
+
+/* CONN_INFO_1_STR (level 1 connection info strings) */
+typedef struct str_conn_info1
+{
+ UNISTR2 uni_usr_name; /* unicode string of user */
+ UNISTR2 uni_net_name; /* unicode string of name */
+
+} CONN_INFO_1_STR;
+
+/* SRV_CONN_INFO_1 */
+typedef struct srv_conn_info_1_info
+{
+ uint32 num_entries_read; /* EntriesRead */
+ uint32 ptr_conn_info; /* Buffer */
+ uint32 num_entries_read2; /* EntriesRead */
+
+ CONN_INFO_1 info_1 [MAX_CONN_ENTRIES]; /* connection entry pointers */
+ CONN_INFO_1_STR info_1_str[MAX_CONN_ENTRIES]; /* connection entry strings */
+
+} SRV_CONN_INFO_1;
+
+/* SRV_CONN_INFO_CTR */
+typedef struct srv_conn_info_ctr_info
+{
+ uint32 switch_value; /* switch value */
+ uint32 ptr_conn_ctr; /* pointer to conn info union */
+ union
+ {
+ SRV_CONN_INFO_0 info0; /* connection info level 0 */
+ SRV_CONN_INFO_1 info1; /* connection info level 1 */
+
+ } conn;
+
+} SRV_CONN_INFO_CTR;
+
+
+/* SRV_Q_NET_CONN_ENUM */
+typedef struct q_net_conn_enum_info
+{
+ uint32 ptr_srv_name; /* pointer (to server name) */
+ UNISTR2 uni_srv_name; /* server name "\\server" */
+
+ uint32 ptr_qual_name; /* pointer (to qualifier name) */
+ UNISTR2 uni_qual_name; /* qualifier name "\\qualifier" */
+
+ uint32 conn_level; /* connection level */
+
+ SRV_CONN_INFO_CTR *ctr;
+
+ uint32 preferred_len; /* preferred maximum length (0xffff ffff) */
+ ENUM_HND enum_hnd;
+
+} SRV_Q_NET_CONN_ENUM;
+
+/* SRV_R_NET_CONN_ENUM */
+typedef struct r_net_conn_enum_info
+{
+ uint32 conn_level; /* share level */
+
+ SRV_CONN_INFO_CTR *ctr;
+
+ uint32 total_entries; /* total number of entries */
+ ENUM_HND enum_hnd;
+
+ WERROR status; /* return status */
+
+} SRV_R_NET_CONN_ENUM;
+
+/* SH_INFO_0 */
+typedef struct ptr_share_info0
+{
+ uint32 ptr_netname; /* pointer to net name. */
+} SH_INFO_0;
+
+/* SH_INFO_0_STR (level 0 share info strings) */
+typedef struct str_share_info0
+{
+ SH_INFO_0 *ptrs;
+
+ UNISTR2 uni_netname; /* unicode string of net name */
+
+} SH_INFO_0_STR;
+
+/* SRV_SHARE_INFO_0 */
+typedef struct share_info_0_info
+{
+ SH_INFO_0 info_0;
+ SH_INFO_0_STR info_0_str;
+
+} SRV_SHARE_INFO_0;
+
+/* SH_INFO_1 (pointers to level 1 share info strings) */
+typedef struct ptr_share_info1
+{
+ uint32 ptr_netname; /* pointer to net name. */
+ uint32 type; /* ipc, print, disk ... */
+ uint32 ptr_remark; /* pointer to comment. */
+
+} SH_INFO_1;
+
+/* SH_INFO_1_STR (level 1 share info strings) */
+typedef struct str_share_info1
+{
+ SH_INFO_1 *ptrs;
+
+ UNISTR2 uni_netname; /* unicode string of net name */
+ UNISTR2 uni_remark; /* unicode string of comment */
+
+} SH_INFO_1_STR;
+
+/* SRV_SHARE_INFO_1 */
+typedef struct share_info_1_info
+{
+ SH_INFO_1 info_1;
+ SH_INFO_1_STR info_1_str;
+
+} SRV_SHARE_INFO_1;
+
+/* SH_INFO_2 (pointers to level 2 share info strings) */
+typedef struct ptr_share_info2
+{
+ uint32 ptr_netname; /* pointer to net name. */
+ uint32 type; /* ipc, print, disk ... */
+ uint32 ptr_remark; /* pointer to comment. */
+ uint32 perms; /* permissions */
+ uint32 max_uses; /* maximum uses */
+ uint32 num_uses; /* current uses */
+ uint32 ptr_path; /* pointer to path name */
+ uint32 ptr_passwd; /* pointer to password */
+
+} SH_INFO_2;
+
+/* SH_INFO_2_STR (level 2 share info strings) */
+typedef struct str_share_info2
+{
+ SH_INFO_2 *ptrs;
+
+ UNISTR2 uni_netname; /* unicode string of net name (e.g NETLOGON) */
+ UNISTR2 uni_remark; /* unicode string of comment (e.g "Logon server share") */
+ UNISTR2 uni_path; /* unicode string of local path (e.g c:\winnt\system32\repl\import\scripts) */
+ UNISTR2 uni_passwd; /* unicode string of password - presumably for share level security (e.g NULL) */
+
+} SH_INFO_2_STR;
+
+/* SRV_SHARE_INFO_2 */
+typedef struct share_info_2_info
+{
+ SH_INFO_2 info_2;
+ SH_INFO_2_STR info_2_str;
+
+} SRV_SHARE_INFO_2;
+
+typedef struct ptr_share_info501
+{
+ uint32 ptr_netname; /* pointer to net name */
+ uint32 type; /* ipc, print, disk */
+ uint32 ptr_remark; /* pointer to comment */
+ uint32 csc_policy; /* client-side offline caching policy << 4 */
+} SH_INFO_501;
+
+typedef struct str_share_info501
+{
+ UNISTR2 uni_netname; /* unicode string of net name */
+ UNISTR2 uni_remark; /* unicode string of comment */
+} SH_INFO_501_STR;
+
+/* SRV_SHARE_INFO_501 */
+typedef struct share_info_501_info
+{
+ SH_INFO_501 info_501;
+ SH_INFO_501_STR info_501_str;
+} SRV_SHARE_INFO_501;
+
+/* SH_INFO_502 (pointers to level 502 share info strings) */
+typedef struct ptr_share_info502
+{
+ uint32 ptr_netname; /* pointer to net name. */
+ uint32 type; /* ipc, print, disk ... */
+ uint32 ptr_remark; /* pointer to comment. */
+ uint32 perms; /* permissions */
+ uint32 max_uses; /* maximum uses */
+ uint32 num_uses; /* current uses */
+ uint32 ptr_path; /* pointer to path name */
+ uint32 ptr_passwd; /* pointer to password */
+ uint32 reserved; /* this holds the space taken by the sd in the rpc packet */
+ uint32 reserved_offset; /* required for _post operation when marshalling */
+ uint32 sd_size; /* size of security descriptor */
+ uint32 ptr_sd; /* pointer to security descriptor */
+
+} SH_INFO_502;
+
+/* SH_INFO_502_STR (level 502 share info strings) */
+typedef struct str_share_info502
+{
+ SH_INFO_502 *ptrs;
+
+ UNISTR2 uni_netname; /* unicode string of net name (e.g NETLOGON) */
+ UNISTR2 uni_remark; /* unicode string of comment (e.g "Logon server share") */
+ UNISTR2 uni_path; /* unicode string of local path (e.g c:\winnt\system32\repl\import\scripts) */
+ UNISTR2 uni_passwd; /* unicode string of password - presumably for share level security (e.g NULL) */
+
+ uint32 reserved;
+ uint32 sd_size;
+ SEC_DESC *sd;
+
+} SH_INFO_502_STR;
+
+/* SRV_SHARE_INFO_502 */
+typedef struct share_info_502_info
+{
+ SH_INFO_502 info_502;
+ SH_INFO_502_STR info_502_str;
+
+} SRV_SHARE_INFO_502;
+
+typedef struct ptr_share_info1004
+{
+ uint32 ptr_remark;
+
+} SH_INFO_1004;
+
+typedef struct str_share_info1004
+{
+ SH_INFO_1004 *ptrs;
+
+ UNISTR2 uni_remark;
+
+} SH_INFO_1004_STR;
+
+typedef struct ptr_info_1004_info
+{
+ SH_INFO_1004 info_1004;
+ SH_INFO_1004_STR info_1004_str;
+} SRV_SHARE_INFO_1004;
+
+#define SHARE_1005_IN_DFS 0x00000001
+#define SHARE_1005_DFS_ROOT 0x00000002
+/* use the CSC policy mask and shift to match up with the smb.conf parm */
+#define SHARE_1005_CSC_POLICY_MASK 0x00000030
+#define SHARE_1005_CSC_POLICY_SHIFT 4
+
+typedef struct share_info_1005_info
+{
+ uint32 share_info_flags;
+} SRV_SHARE_INFO_1005;
+
+typedef struct share_info_1006_info
+{
+ uint32 max_uses;
+} SRV_SHARE_INFO_1006;
+
+typedef struct ptr_share_info1007
+{
+ uint32 flags;
+ uint32 ptr_AlternateDirectoryName;
+
+} SH_INFO_1007;
+
+typedef struct str_share_info1007
+{
+ SH_INFO_1007 *ptrs;
+
+ UNISTR2 uni_AlternateDirectoryName;
+
+} SH_INFO_1007_STR;
+
+typedef struct ptr_info_1007_info
+{
+ SH_INFO_1007 info_1007;
+ SH_INFO_1007_STR info_1007_str;
+} SRV_SHARE_INFO_1007;
+
+/* SRV_SHARE_INFO_1501 */
+typedef struct share_info_1501_info
+{
+ SEC_DESC_BUF *sdb;
+} SRV_SHARE_INFO_1501;
+
+/* SRV_SHARE_INFO_CTR */
+typedef struct srv_share_info_ctr_info
+{
+ uint32 info_level;
+ uint32 switch_value;
+ uint32 ptr_share_info;
+
+ uint32 num_entries;
+ uint32 ptr_entries;
+ uint32 num_entries2;
+
+ union {
+ SRV_SHARE_INFO_0 *info0;
+ SRV_SHARE_INFO_1 *info1; /* share info level 1 */
+ SRV_SHARE_INFO_2 *info2; /* share info level 2 */
+ SRV_SHARE_INFO_501 *info501; /* share info level 501 */
+ SRV_SHARE_INFO_502 *info502; /* share info level 502 */
+ SRV_SHARE_INFO_1004 *info1004;
+ SRV_SHARE_INFO_1005 *info1005;
+ SRV_SHARE_INFO_1006 *info1006;
+ SRV_SHARE_INFO_1007 *info1007;
+ SRV_SHARE_INFO_1501 *info1501;
+ void *info;
+
+ } share;
+
+} SRV_SHARE_INFO_CTR;
+
+/* SRV_Q_NET_SHARE_ENUM */
+typedef struct q_net_share_enum_info
+{
+ uint32 ptr_srv_name; /* pointer (to server name?) */
+ UNISTR2 uni_srv_name; /* server name */
+
+ SRV_SHARE_INFO_CTR ctr; /* share info container */
+
+ uint32 preferred_len; /* preferred maximum length (0xffff ffff) */
+
+ ENUM_HND enum_hnd;
+
+} SRV_Q_NET_SHARE_ENUM;
+
+
+/* SRV_R_NET_SHARE_ENUM */
+typedef struct r_net_share_enum_info
+{
+ SRV_SHARE_INFO_CTR ctr; /* share info container */
+
+ uint32 total_entries; /* total number of entries */
+ ENUM_HND enum_hnd;
+
+ WERROR status; /* return status */
+
+} SRV_R_NET_SHARE_ENUM;
+
+
+/* SRV_Q_NET_SHARE_GET_INFO */
+typedef struct q_net_share_get_info_info
+{
+ uint32 ptr_srv_name;
+ UNISTR2 uni_srv_name;
+
+ UNISTR2 uni_share_name;
+ uint32 info_level;
+
+} SRV_Q_NET_SHARE_GET_INFO;
+
+/* SRV_SHARE_INFO */
+typedef struct srv_share_info {
+ uint32 switch_value;
+ uint32 ptr_share_ctr;
+
+ union {
+ SRV_SHARE_INFO_0 info0;
+ SRV_SHARE_INFO_1 info1;
+ SRV_SHARE_INFO_2 info2;
+ SRV_SHARE_INFO_501 info501;
+ SRV_SHARE_INFO_502 info502;
+ SRV_SHARE_INFO_1004 info1004;
+ SRV_SHARE_INFO_1005 info1005;
+ SRV_SHARE_INFO_1006 info1006;
+ SRV_SHARE_INFO_1007 info1007;
+ SRV_SHARE_INFO_1501 info1501;
+ } share;
+} SRV_SHARE_INFO;
+
+/* SRV_R_NET_SHARE_GET_INFO */
+typedef struct r_net_share_get_info_info
+{
+ SRV_SHARE_INFO info;
+ WERROR status;
+
+} SRV_R_NET_SHARE_GET_INFO;
+
+/* SRV_Q_NET_SHARE_SET_INFO */
+typedef struct q_net_share_set_info_info
+{
+ uint32 ptr_srv_name;
+ UNISTR2 uni_srv_name;
+
+ UNISTR2 uni_share_name;
+ uint32 info_level;
+
+ SRV_SHARE_INFO info;
+
+ uint32 ptr_parm_error;
+ uint32 parm_error;
+
+} SRV_Q_NET_SHARE_SET_INFO;
+
+/* SRV_R_NET_SHARE_SET_INFO */
+typedef struct r_net_share_set_info
+{
+ uint32 ptr_parm_error;
+ uint32 parm_error;
+
+ WERROR status; /* return status */
+
+} SRV_R_NET_SHARE_SET_INFO;
+
+/* SRV_Q_NET_SHARE_ADD */
+typedef struct q_net_share_add
+{
+ uint32 ptr_srv_name;
+ UNISTR2 uni_srv_name;
+
+ uint32 info_level;
+
+ SRV_SHARE_INFO info;
+
+ uint32 ptr_err_index; /* pointer to error index */
+ uint32 err_index; /* index in info to field in error */
+
+} SRV_Q_NET_SHARE_ADD;
+
+/* SRV_R_NET_SHARE_ADD */
+typedef struct r_net_share_add
+{
+
+ uint32 ptr_parm_error;
+ uint32 parm_error;
+
+ WERROR status; /* return status */
+
+} SRV_R_NET_SHARE_ADD;
+
+/* SRV_Q_NET_SHARE_DEL */
+typedef struct q_net_share_del
+{
+ uint32 ptr_srv_name;
+ UNISTR2 uni_srv_name;
+ UNISTR2 uni_share_name;
+ uint32 reserved;
+
+} SRV_Q_NET_SHARE_DEL;
+
+/* SRV_R_NET_SHARE_DEL */
+typedef struct r_net_share_del
+{
+ WERROR status; /* return status */
+
+} SRV_R_NET_SHARE_DEL;
+
+/* FILE_INFO_3 (level 3 file info strings) */
+typedef struct file_info3_info
+{
+ uint32 id; /* file index */
+ uint32 perms; /* file permissions. don't know what format */
+ uint32 num_locks; /* file locks */
+ uint32 ptr_path_name; /* file name */
+ uint32 ptr_user_name; /* file owner */
+
+} FILE_INFO_3;
+
+/* FILE_INFO_3_STR (level 3 file info strings) */
+typedef struct str_file_info3_info
+{
+ UNISTR2 uni_path_name; /* unicode string of file name */
+ UNISTR2 uni_user_name; /* unicode string of file owner. */
+
+} FILE_INFO_3_STR;
+
+/* SRV_FILE_INFO_3 */
+typedef struct srv_file_info_3
+{
+ uint32 num_entries_read; /* EntriesRead */
+ uint32 ptr_file_info; /* Buffer */
+
+ uint32 num_entries_read2; /* EntriesRead */
+ FILE_INFO_3 info_3; /* file entry details */
+ FILE_INFO_3_STR info_3_str; /* file entry strings */
+} SRV_FILE_INFO_3;
+
+/* SRV_FILE_INFO_CTR */
+typedef struct srv_file_info_3_info
+{
+ uint32 switch_value; /* switch value */
+ uint32 ptr_file_info; /* pointer to file info union */
+
+ uint32 num_entries;
+ uint32 ptr_entries;
+ uint32 num_entries2;
+ union
+ {
+ SRV_FILE_INFO_3 *info3;
+ } file;
+
+} SRV_FILE_INFO_CTR;
+
+
+/* SRV_Q_NET_FILE_ENUM */
+typedef struct q_net_file_enum_info
+{
+ uint32 ptr_srv_name; /* pointer (to server name?) */
+ UNISTR2 uni_srv_name; /* server name */
+
+ uint32 ptr_qual_name; /* pointer (to qualifier name) */
+ UNISTR2 uni_qual_name; /* qualifier name "\\qualifier" */
+
+ uint32 ptr_user_name; /* pointer (to user name) */
+ UNISTR2 uni_user_name; /* user name */
+
+ uint32 file_level; /* file level */
+
+ SRV_FILE_INFO_CTR ctr;
+
+ uint32 preferred_len; /* preferred maximum length (0xffff ffff) */
+ ENUM_HND enum_hnd;
+
+} SRV_Q_NET_FILE_ENUM;
+
+
+/* SRV_R_NET_FILE_ENUM */
+typedef struct r_net_file_enum_info
+{
+ uint32 file_level; /* file level */
+
+ SRV_FILE_INFO_CTR ctr;
+
+ uint32 total_entries; /* total number of files */
+ ENUM_HND enum_hnd;
+
+ WERROR status; /* return status */
+
+} SRV_R_NET_FILE_ENUM;
+
+/* SRV_Q_NET_FILE_CLOSE */
+typedef struct q_net_file_close
+{
+ uint32 ptr_srv_name; /* pointer to server name */
+ UNISTR2 uni_srv_name; /* server name */
+
+ uint32 file_id;
+} SRV_Q_NET_FILE_CLOSE;
+
+/* SRV_R_NET_FILE_CLOSE */
+typedef struct r_net_file_close
+{
+ WERROR status; /* return status */
+} SRV_R_NET_FILE_CLOSE;
+
+/* SRV_INFO_100 */
+typedef struct srv_info_100_info
+{
+ uint32 platform_id; /* 0x500 */
+ uint32 ptr_name; /* pointer to server name */
+
+ UNISTR2 uni_name; /* server name "server" */
+
+} SRV_INFO_100;
+
+/* SRV_INFO_101 */
+typedef struct srv_info_101_info
+{
+ uint32 platform_id; /* 0x500 */
+ uint32 ptr_name; /* pointer to server name */
+ uint32 ver_major; /* 0x4 */
+ uint32 ver_minor; /* 0x2 */
+ uint32 srv_type; /* browse etc type */
+ uint32 ptr_comment; /* pointer to server comment */
+
+ UNISTR2 uni_name; /* server name "server" */
+ UNISTR2 uni_comment; /* server comment "samba x.x.x blah" */
+
+} SRV_INFO_101;
+
+/* SRV_INFO_102 */
+typedef struct srv_info_102_info
+{
+ uint32 platform_id; /* 0x500 */
+ uint32 ptr_name; /* pointer to server name */
+ uint32 ver_major; /* 0x4 */
+ uint32 ver_minor; /* 0x2 */
+ uint32 srv_type; /* browse etc type */
+ uint32 ptr_comment; /* pointer to server comment */
+ uint32 users; /* 0xffff ffff*/
+ uint32 disc; /* 0xf */
+ uint32 hidden; /* 0x0 */
+ uint32 announce; /* 240 */
+ uint32 ann_delta; /* 3000 */
+ uint32 licenses; /* 0 */
+ uint32 ptr_usr_path; /* pointer to user path */
+
+ UNISTR2 uni_name; /* server name "server" */
+ UNISTR2 uni_comment; /* server comment "samba x.x.x blah" */
+ UNISTR2 uni_usr_path; /* "c:\" (eh?) */
+
+} SRV_INFO_102;
+
+
+/* SRV_INFO_CTR */
+typedef struct srv_info_ctr_info
+{
+ uint32 switch_value; /* switch value */
+ uint32 ptr_srv_ctr; /* pointer to server info */
+ union
+ {
+ SRV_INFO_102 sv102; /* server info level 102 */
+ SRV_INFO_101 sv101; /* server info level 101 */
+ SRV_INFO_100 sv100; /* server info level 100 */
+
+ } srv;
+
+} SRV_INFO_CTR;
+
+/* SRV_Q_NET_SRV_GET_INFO */
+typedef struct q_net_srv_get_info
+{
+ uint32 ptr_srv_name;
+ UNISTR2 uni_srv_name; /* "\\server" */
+ uint32 switch_value;
+
+} SRV_Q_NET_SRV_GET_INFO;
+
+/* SRV_R_NET_SRV_GET_INFO */
+typedef struct r_net_srv_get_info
+{
+ SRV_INFO_CTR *ctr;
+
+ WERROR status; /* return status */
+
+} SRV_R_NET_SRV_GET_INFO;
+
+/* SRV_Q_NET_SRV_SET_INFO */
+typedef struct q_net_srv_set_info
+{
+ uint32 ptr_srv_name;
+ UNISTR2 uni_srv_name; /* "\\server" */
+ uint32 switch_value;
+
+ SRV_INFO_CTR *ctr;
+
+} SRV_Q_NET_SRV_SET_INFO;
+
+
+/* SRV_R_NET_SRV_SET_INFO */
+typedef struct r_net_srv_set_info
+{
+ uint32 switch_value; /* switch value */
+
+ WERROR status; /* return status */
+
+} SRV_R_NET_SRV_SET_INFO;
+
+/* SRV_Q_NET_REMOTE_TOD */
+typedef struct q_net_remote_tod
+{
+ uint32 ptr_srv_name;
+ UNISTR2 uni_srv_name; /* "\\server" */
+
+} SRV_Q_NET_REMOTE_TOD;
+
+/* TIME_OF_DAY_INFO */
+typedef struct time_of_day_info
+{
+ uint32 elapsedt;
+ uint32 msecs;
+ uint32 hours;
+ uint32 mins;
+ uint32 secs;
+ uint32 hunds;
+ uint32 zone;
+ uint32 tintervals;
+ uint32 day;
+ uint32 month;
+ uint32 year;
+ uint32 weekday;
+
+} TIME_OF_DAY_INFO;
+
+/* SRV_R_NET_REMOTE_TOD */
+typedef struct r_net_remote_tod
+{
+ uint32 ptr_srv_tod; /* pointer to TOD */
+ TIME_OF_DAY_INFO *tod;
+
+ WERROR status; /* return status */
+
+} SRV_R_NET_REMOTE_TOD;
+
+/* SRV_Q_NET_FILE_QUERY_SECDESC */
+typedef struct q_net_file_query_secdesc
+{
+ uint32 ptr_srv_name;
+ UNISTR2 uni_srv_name;
+ uint32 ptr_qual_name;
+ UNISTR2 uni_qual_name;
+ UNISTR2 uni_file_name;
+ uint32 unknown1;
+ uint32 unknown2;
+ uint32 unknown3;
+} SRV_Q_NET_FILE_QUERY_SECDESC;
+
+/* SRV_R_NET_FILE_QUERY_SECDESC */
+typedef struct r_net_file_query_secdesc
+{
+ uint32 ptr_response;
+ uint32 size_response;
+ uint32 ptr_secdesc;
+ uint32 size_secdesc;
+ SEC_DESC *sec_desc;
+ WERROR status;
+} SRV_R_NET_FILE_QUERY_SECDESC;
+
+/* SRV_Q_NET_FILE_SET_SECDESC */
+typedef struct q_net_file_set_secdesc
+{
+ uint32 ptr_srv_name;
+ UNISTR2 uni_srv_name;
+ uint32 ptr_qual_name;
+ UNISTR2 uni_qual_name;
+ UNISTR2 uni_file_name;
+ uint32 sec_info;
+ uint32 size_set;
+ uint32 ptr_secdesc;
+ uint32 size_secdesc;
+ SEC_DESC *sec_desc;
+} SRV_Q_NET_FILE_SET_SECDESC;
+
+/* SRV_R_NET_FILE_SET_SECDESC */
+typedef struct r_net_file_set_secdesc
+{
+ WERROR status;
+} SRV_R_NET_FILE_SET_SECDESC;
+
+#endif /* _RPC_SRVSVC_H */
diff --git a/source/include/rpc_wkssvc.h b/source/include/rpc_wkssvc.h
new file mode 100644
index 00000000000..adc37c255b2
--- /dev/null
+++ b/source/include/rpc_wkssvc.h
@@ -0,0 +1,72 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+ Copyright (C) Paul Ashton 1997
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _RPC_WKS_H /* _RPC_WKS_H */
+#define _RPC_WKS_H
+
+
+/* wkssvc pipe */
+#define WKS_QUERY_INFO 0x00
+
+
+/* WKS_Q_QUERY_INFO - probably a capabilities request */
+typedef struct q_wks_query_info_info
+{
+ uint32 ptr_srv_name; /* pointer (to server name?) */
+ UNISTR2 uni_srv_name; /* unicode server name starting with '\\' */
+
+ uint16 switch_value; /* info level 100 (0x64) */
+
+} WKS_Q_QUERY_INFO;
+
+
+/* WKS_INFO_100 - level 100 info */
+typedef struct wks_info_100_info
+{
+ uint32 platform_id; /* 0x0000 01f4 - unknown */
+ uint32 ptr_compname; /* pointer to server name */
+ uint32 ptr_lan_grp ; /* pointer to domain name */
+ uint32 ver_major; /* 4 - unknown */
+ uint32 ver_minor; /* 0 - unknown */
+
+ UNISTR2 uni_compname; /* unicode server name */
+ UNISTR2 uni_lan_grp ; /* unicode domain name */
+
+} WKS_INFO_100;
+
+
+/* WKS_R_QUERY_INFO - probably a capabilities request */
+typedef struct r_wks_query_info_info
+{
+ uint16 switch_value; /* 100 (0x64) - switch value */
+
+ /* for now, only level 100 is supported. this should be an enum container */
+ uint32 ptr_1; /* pointer 1 */
+ WKS_INFO_100 *wks100; /* workstation info level 100 */
+
+ NTSTATUS status; /* return status */
+
+} WKS_R_QUERY_INFO;
+
+
+#endif /* _RPC_WKS_H */
+
diff --git a/source/include/safe_string.h b/source/include/safe_string.h
new file mode 100644
index 00000000000..b22c5efcc99
--- /dev/null
+++ b/source/include/safe_string.h
@@ -0,0 +1,228 @@
+/*
+ Unix SMB/CIFS implementation.
+ Safe string handling routines.
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _SAFE_STRING_H
+#define _SAFE_STRING_H
+
+#ifndef _SPLINT_ /* http://www.splint.org */
+
+/* Some macros to ensure people don't use buffer overflow vulnerable string
+ functions. */
+
+#ifdef bcopy
+#undef bcopy
+#endif /* bcopy */
+#define bcopy(src,dest,size) __ERROR__XX__NEVER_USE_BCOPY___;
+
+#ifdef strcpy
+#undef strcpy
+#endif /* strcpy */
+#define strcpy(dest,src) __ERROR__XX__NEVER_USE_STRCPY___;
+
+#ifdef strcat
+#undef strcat
+#endif /* strcat */
+#define strcat(dest,src) __ERROR__XX__NEVER_USE_STRCAT___;
+
+#ifdef sprintf
+#undef sprintf
+#endif /* sprintf */
+#define sprintf __ERROR__XX__NEVER_USE_SPRINTF__;
+
+/*
+ * strcasecmp/strncasecmp aren't an error, but it means you're not thinking about
+ * multibyte. Don't use them. JRA.
+ */
+#ifdef strcasecmp
+#undef strcasecmp
+#endif
+#define strcasecmp __ERROR__XX__NEVER_USE_STRCASECMP__;
+
+#ifdef strncasecmp
+#undef strncasecmp
+#endif
+#define strncasecmp __ERROR__XX__NEVER_USE_STRCASECMP__;
+
+#endif /* !_SPLINT_ */
+
+#ifdef DEVELOPER
+#define SAFE_STRING_FUNCTION_NAME FUNCTION_MACRO
+#define SAFE_STRING_LINE __LINE__
+#else
+#define SAFE_STRING_FUNCTION_NAME ("")
+#define SAFE_STRING_LINE (0)
+#endif
+
+/* We need a number of different prototypes for our
+ non-existant fuctions */
+char * __unsafe_string_function_usage_here__(void);
+
+size_t __unsafe_string_function_usage_here_size_t__(void);
+
+size_t __unsafe_string_function_usage_here_char__(void);
+
+#ifdef HAVE_COMPILER_WILL_OPTIMIZE_OUT_FNS
+
+/* if the compiler will optimize out function calls, then use this to tell if we are
+ have the correct types (this works only where sizeof() returns the size of the buffer, not
+ the size of the pointer). */
+
+#define CHECK_STRING_SIZE(d, len) (sizeof(d) != (len) && sizeof(d) != sizeof(char *))
+
+#define fstrterminate(d) (CHECK_STRING_SIZE(d, sizeof(fstring)) \
+ ? __unsafe_string_function_usage_here_char__() \
+ : (((d)[sizeof(fstring)-1]) = '\0'))
+#define pstrterminate(d) (CHECK_STRING_SIZE(d, sizeof(pstring)) \
+ ? __unsafe_string_function_usage_here_char__() \
+ : (((d)[sizeof(pstring)-1]) = '\0'))
+
+#define wpstrcpy(d,s) ((sizeof(d) != sizeof(wpstring) && sizeof(d) != sizeof(smb_ucs2_t *)) \
+ ? __unsafe_string_function_usage_here__() \
+ : safe_strcpy_w((d),(s),sizeof(wpstring)))
+#define wpstrcat(d,s) ((sizeof(d) != sizeof(wpstring) && sizeof(d) != sizeof(smb_ucs2_t *)) \
+ ? __unsafe_string_function_usage_here__() \
+ : safe_strcat_w((d),(s),sizeof(wpstring)))
+#define wfstrcpy(d,s) ((sizeof(d) != sizeof(wfstring) && sizeof(d) != sizeof(smb_ucs2_t *)) \
+ ? __unsafe_string_function_usage_here__() \
+ : safe_strcpy_w((d),(s),sizeof(wfstring)))
+#define wfstrcat(d,s) ((sizeof(d) != sizeof(wfstring) && sizeof(d) != sizeof(smb_ucs2_t *)) \
+ ? __unsafe_string_function_usage_here__() \
+ : safe_strcat_w((d),(s),sizeof(wfstring)))
+
+#define push_pstring_base(dest, src, pstring_base) \
+ (CHECK_STRING_SIZE(pstring_base, sizeof(pstring)) \
+ ? __unsafe_string_function_usage_here_size_t__() \
+ : push_ascii(dest, src, sizeof(pstring)-PTR_DIFF(dest,pstring_base)-1, STR_TERMINATE))
+
+#else /* HAVE_COMPILER_WILL_OPTIMIZE_OUT_FNS */
+
+#define fstrterminate(d) (((d)[sizeof(fstring)-1]) = '\0')
+#define pstrterminate(d) (((d)[sizeof(pstring)-1]) = '\0')
+
+#define wpstrcpy(d,s) safe_strcpy_w((d),(s),sizeof(wpstring))
+#define wpstrcat(d,s) safe_strcat_w((d),(s),sizeof(wpstring))
+#define wfstrcpy(d,s) safe_strcpy_w((d),(s),sizeof(wfstring))
+#define wfstrcat(d,s) safe_strcat_w((d),(s),sizeof(wfstring))
+
+#define push_pstring_base(dest, src, pstring_base) \
+ push_ascii(dest, src, sizeof(pstring)-PTR_DIFF(dest,pstring_base)-1, STR_TERMINATE)
+
+#endif /* HAVE_COMPILER_WILL_OPTIMIZE_OUT_FNS */
+
+#define safe_strcpy_base(dest, src, base, size) \
+ safe_strcpy(dest, src, size-PTR_DIFF(dest,base)-1)
+
+/* String copy functions - macro hell below adds 'type checking' (limited,
+ but the best we can do in C) and may tag with function name/number to
+ record the last 'clobber region' on that string */
+
+#define pstrcpy(d,s) safe_strcpy((d), (s),sizeof(pstring)-1)
+#define pstrcat(d,s) safe_strcat((d), (s),sizeof(pstring)-1)
+#define fstrcpy(d,s) safe_strcpy((d),(s),sizeof(fstring)-1)
+#define fstrcat(d,s) safe_strcat((d),(s),sizeof(fstring)-1)
+#define nstrcpy(d,s) safe_strcpy((d), (s),sizeof(nstring)-1)
+#define unstrcpy(d,s) safe_strcpy((d), (s),sizeof(unstring)-1)
+
+/* the addition of the DEVELOPER checks in safe_strcpy means we must
+ * update a lot of code. To make this a little easier here are some
+ * functions that provide the lengths with less pain */
+#define pstrcpy_base(dest, src, pstring_base) \
+ safe_strcpy(dest, src, sizeof(pstring)-PTR_DIFF(dest,pstring_base)-1)
+
+
+/* Inside the _fn variants of these is a call to clobber_region(), -
+ * which might destroy the stack on a buggy function. We help the
+ * debugging process by putting the function and line who last caused
+ * a clobbering into a static buffer. If the program crashes at
+ * address 0xf1f1f1f1 then this function is probably, but not
+ * necessarily, to blame. */
+
+/* overmalloc_safe_strcpy: DEPRECATED! Used when you know the
+ * destination buffer is longer than maxlength, but you don't know how
+ * long. This is not a good situation, because we can't do the normal
+ * sanity checks. Don't use in new code! */
+
+#define overmalloc_safe_strcpy(dest,src,maxlength) safe_strcpy_fn(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE,dest,src,maxlength)
+#define safe_strcpy(dest,src,maxlength) safe_strcpy_fn2(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE,dest,src,maxlength)
+#define safe_strcat(dest,src,maxlength) safe_strcat_fn2(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE,dest,src,maxlength)
+#define push_string(base_ptr, dest, src, dest_len, flags) push_string_fn2(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, base_ptr, dest, src, dest_len, flags)
+#define pull_string(base_ptr, dest, src, dest_len, src_len, flags) pull_string_fn2(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, base_ptr, dest, src, dest_len, src_len, flags)
+#define clistr_push(cli, dest, src, dest_len, flags) clistr_push_fn2(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, cli, dest, src, dest_len, flags)
+#define clistr_pull(cli, dest, src, dest_len, src_len, flags) clistr_pull_fn2(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, cli, dest, src, dest_len, src_len, flags)
+#define srvstr_push(base_ptr, dest, src, dest_len, flags) srvstr_push_fn2(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, base_ptr, dest, src, dest_len, flags)
+
+#define alpha_strcpy(dest,src,other_safe_chars,maxlength) alpha_strcpy_fn(SAFE_STRING_FUNCTION_NAME,SAFE_STRING_LINE,dest,src,other_safe_chars,maxlength)
+#define StrnCpy(dest,src,n) StrnCpy_fn(SAFE_STRING_FUNCTION_NAME,SAFE_STRING_LINE,dest,src,n)
+
+#ifdef HAVE_COMPILER_WILL_OPTIMIZE_OUT_FNS
+
+/* if the compiler will optimize out function calls, then use this to tell if we are
+ have the correct types (this works only where sizeof() returns the size of the buffer, not
+ the size of the pointer). */
+
+#define safe_strcpy_fn2(fn_name, fn_line, d, s, max_len) \
+ (CHECK_STRING_SIZE(d, max_len+1) \
+ ? __unsafe_string_function_usage_here__() \
+ : safe_strcpy_fn(fn_name, fn_line, (d), (s), (max_len)))
+
+#define safe_strcat_fn2(fn_name, fn_line, d, s, max_len) \
+ (CHECK_STRING_SIZE(d, max_len+1) \
+ ? __unsafe_string_function_usage_here__() \
+ : safe_strcat_fn(fn_name, fn_line, (d), (s), (max_len)))
+
+#define push_string_fn2(fn_name, fn_line, base_ptr, dest, src, dest_len, flags) \
+ (CHECK_STRING_SIZE(dest, dest_len) \
+ ? __unsafe_string_function_usage_here_size_t__() \
+ : push_string_fn(fn_name, fn_line, base_ptr, dest, src, dest_len, flags))
+
+#define pull_string_fn2(fn_name, fn_line, base_ptr, dest, src, dest_len, src_len, flags) \
+ (CHECK_STRING_SIZE(dest, dest_len) \
+ ? __unsafe_string_function_usage_here_size_t__() \
+ : pull_string_fn(fn_name, fn_line, base_ptr, dest, src, dest_len, src_len, flags))
+
+#define clistr_push_fn2(fn_name, fn_line, cli, dest, src, dest_len, flags) \
+ (CHECK_STRING_SIZE(dest, dest_len) \
+ ? __unsafe_string_function_usage_here_size_t__() \
+ : clistr_push_fn(fn_name, fn_line, cli, dest, src, dest_len, flags))
+
+#define clistr_pull_fn2(fn_name, fn_line, cli, dest, src, dest_len, srclen, flags) \
+ (CHECK_STRING_SIZE(dest, dest_len) \
+ ? __unsafe_string_function_usage_here_size_t__() \
+ : clistr_pull_fn(fn_name, fn_line, cli, dest, src, dest_len, srclen, flags))
+
+#define srvstr_push_fn2(fn_name, fn_line, base_ptr, dest, src, dest_len, flags) \
+ (CHECK_STRING_SIZE(dest, dest_len) \
+ ? __unsafe_string_function_usage_here_size_t__() \
+ : srvstr_push_fn(fn_name, fn_line, base_ptr, dest, src, dest_len, flags))
+
+#else
+
+#define safe_strcpy_fn2 safe_strcpy_fn
+#define safe_strcat_fn2 safe_strcat_fn
+#define push_string_fn2 push_string_fn
+#define pull_string_fn2 pull_string_fn
+#define clistr_push_fn2 clistr_push_fn
+#define clistr_pull_fn2 clistr_pull_fn
+#define srvstr_push_fn2 srvstr_push_fn
+
+#endif
+
+#endif
diff --git a/source/include/samba_linux_quota.h b/source/include/samba_linux_quota.h
new file mode 100644
index 00000000000..02b3e5169b1
--- /dev/null
+++ b/source/include/samba_linux_quota.h
@@ -0,0 +1,336 @@
+#ifndef _SAMBA_LINUX_QUOTA_H_
+#define _SAMBA_LINUX_QUOTA_H_
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) Andrew Tridgell 1994-2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ This file is needed because Quota support on Linux has
+ been broken since Linus kernel 2.4.x. It will only get
+ better (and this file be removed) when all the distributions
+ ship a glibc with a working quota.h file. This is very
+ bad. JRA.
+
+ Original file came from Christoph Hellwig <hch@infradead.org>.
+ Massaged into one nasty include file (to stop us having to
+ add multiple files into Samba just for Linux braindamage)
+ by JRA.
+*/
+
+#undef QUOTABLOCK_SIZE
+
+#ifndef _QUOTAIO_LINUX_V1
+#define _QUOTAIO_LINUX_V1
+
+/*
+ * Headerfile for old quotafile format
+ */
+
+#include <sys/types.h>
+
+#define V1_DQBLK_SIZE_BITS 10
+#define V1_DQBLK_SIZE (1 << V1_DQBLK_SIZE_BITS) /* Size of one quota block in bytes in old format */
+
+#define V1_DQOFF(__id) ((loff_t) ((__id) * sizeof(struct v1_disk_dqblk)))
+
+/* Structure of quota on disk */
+struct v1_disk_dqblk {
+ u_int32_t dqb_bhardlimit; /* absolute limit on disk blks alloc */
+ u_int32_t dqb_bsoftlimit; /* preferred limit on disk blks */
+ u_int32_t dqb_curblocks; /* current block count */
+ u_int32_t dqb_ihardlimit; /* maximum # allocated inodes */
+ u_int32_t dqb_isoftlimit; /* preferred limit on inodes */
+ u_int32_t dqb_curinodes; /* current # allocated inodes */
+ time_t dqb_btime; /* time limit for excessive disk use */
+ time_t dqb_itime; /* time limit for excessive files */
+} __attribute__ ((packed));
+
+/* Structure used for communication with kernel */
+struct v1_kern_dqblk {
+ u_int32_t dqb_bhardlimit; /* absolute limit on disk blks alloc */
+ u_int32_t dqb_bsoftlimit; /* preferred limit on disk blks */
+ u_int32_t dqb_curblocks; /* current block count */
+ u_int32_t dqb_ihardlimit; /* maximum # allocated inodes */
+ u_int32_t dqb_isoftlimit; /* preferred inode limit */
+ u_int32_t dqb_curinodes; /* current # allocated inodes */
+ time_t dqb_btime; /* time limit for excessive disk use */
+ time_t dqb_itime; /* time limit for excessive files */
+};
+
+struct v1_dqstats {
+ u_int32_t lookups;
+ u_int32_t drops;
+ u_int32_t reads;
+ u_int32_t writes;
+ u_int32_t cache_hits;
+ u_int32_t allocated_dquots;
+ u_int32_t free_dquots;
+ u_int32_t syncs;
+};
+
+#ifndef Q_V1_GETQUOTA
+#define Q_V1_GETQUOTA 0x300
+#endif
+#ifndef Q_V1_SETQUOTA
+#define Q_V1_SETQUOTA 0x400
+#endif
+
+#endif /* _QUOTAIO_LINUX_V1 */
+
+/*
+ *
+ * Header file for disk format of new quotafile format
+ *
+ */
+
+#ifndef _QUOTAIO_LINUX_V2
+#define _QUOTAIO_LINUX_V2
+
+#include <sys/types.h>
+
+#ifndef _QUOTA_LINUX
+#define _QUOTA_LINUX
+
+#include <sys/types.h>
+
+typedef u_int32_t qid_t; /* Type in which we store ids in memory */
+typedef u_int64_t qsize_t; /* Type in which we store size limitations */
+
+#define MAXQUOTAS 2
+#define USRQUOTA 0 /* element used for user quotas */
+#define GRPQUOTA 1 /* element used for group quotas */
+
+/*
+ * Definitions for the default names of the quotas files.
+ */
+#define INITQFNAMES { \
+ "user", /* USRQUOTA */ \
+ "group", /* GRPQUOTA */ \
+ "undefined", \
+}
+
+/*
+ * Definitions of magics and versions of current quota files
+ */
+#define INITQMAGICS {\
+ 0xd9c01f11, /* USRQUOTA */\
+ 0xd9c01927 /* GRPQUOTA */\
+}
+
+/* Size of blocks in which are counted size limits in generic utility parts */
+#define QUOTABLOCK_BITS 10
+#define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS)
+
+/* Conversion routines from and to quota blocks */
+#define qb2kb(x) ((x) << (QUOTABLOCK_BITS-10))
+#define kb2qb(x) ((x) >> (QUOTABLOCK_BITS-10))
+#define toqb(x) (((x) + QUOTABLOCK_SIZE - 1) >> QUOTABLOCK_BITS)
+
+/*
+ * Command definitions for the 'quotactl' system call.
+ * The commands are broken into a main command defined below
+ * and a subcommand that is used to convey the type of
+ * quota that is being manipulated (see above).
+ */
+#define SUBCMDMASK 0x00ff
+#define SUBCMDSHIFT 8
+#define QCMD(cmd, type) (((cmd) << SUBCMDSHIFT) | ((type) & SUBCMDMASK))
+
+#define Q_6_5_QUOTAON 0x0100 /* enable quotas */
+#define Q_6_5_QUOTAOFF 0x0200 /* disable quotas */
+#define Q_6_5_SYNC 0x0600 /* sync disk copy of a filesystems quotas */
+
+#define Q_SYNC 0x800001 /* sync disk copy of a filesystems quotas */
+#define Q_QUOTAON 0x800002 /* turn quotas on */
+#define Q_QUOTAOFF 0x800003 /* turn quotas off */
+#define Q_GETFMT 0x800004 /* get quota format used on given filesystem */
+#define Q_GETINFO 0x800005 /* get information about quota files */
+#define Q_SETINFO 0x800006 /* set information about quota files */
+#define Q_GETQUOTA 0x800007 /* get user quota structure */
+#define Q_SETQUOTA 0x800008 /* set user quota structure */
+
+/*
+ * Quota structure used for communication with userspace via quotactl
+ * Following flags are used to specify which fields are valid
+ */
+#define QIF_BLIMITS 1
+#define QIF_SPACE 2
+#define QIF_ILIMITS 4
+#define QIF_INODES 8
+#define QIF_BTIME 16
+#define QIF_ITIME 32
+#define QIF_LIMITS (QIF_BLIMITS | QIF_ILIMITS)
+#define QIF_USAGE (QIF_SPACE | QIF_INODES)
+#define QIF_TIMES (QIF_BTIME | QIF_ITIME)
+#define QIF_ALL (QIF_LIMITS | QIF_USAGE | QIF_TIMES)
+
+struct if_dqblk {
+ u_int64_t dqb_bhardlimit;
+ u_int64_t dqb_bsoftlimit;
+ u_int64_t dqb_curspace;
+ u_int64_t dqb_ihardlimit;
+ u_int64_t dqb_isoftlimit;
+ u_int64_t dqb_curinodes;
+ u_int64_t dqb_btime;
+ u_int64_t dqb_itime;
+ u_int32_t dqb_valid;
+};
+
+/*
+ * Structure used for setting quota information about file via quotactl
+ * Following flags are used to specify which fields are valid
+ */
+#define IIF_BGRACE 1
+#define IIF_IGRACE 2
+#define IIF_FLAGS 4
+#define IIF_ALL (IIF_BGRACE | IIF_IGRACE | IIF_FLAGS)
+
+struct if_dqinfo {
+ u_int64_t dqi_bgrace;
+ u_int64_t dqi_igrace;
+ u_int32_t dqi_flags;
+ u_int32_t dqi_valid;
+};
+
+/* Quota format identifiers */
+#define QFMT_VFS_OLD 1
+#define QFMT_VFS_V0 2
+
+/* Flags supported by kernel */
+#define V1_DQF_RSQUASH 1
+
+/* Ioctl for getting quota size */
+#include <sys/ioctl.h>
+#ifndef FIOQSIZE
+ #if defined(__alpha__) || defined(__powerpc__) || defined(__sh__) || defined(__sparc__) || defined(__sparc64__)
+ #define FIOQSIZE _IOR('f', 128, loff_t)
+ #elif defined(__arm__) || defined(__mc68000__) || defined(__s390__)
+ #define FIOQSIZE 0x545E
+ #elif defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__ia64__) || defined(__parisc__) || defined(__cris__) || defined(__hppa__)
+ #define FIOQSIZE 0x5460
+ #elif defined(__mips__) || defined(__mips64__)
+ #define FIOQSIZE 0x6667
+ #endif
+#endif
+
+long quotactl __P((int, const char *, qid_t, caddr_t));
+
+#endif /* _QUOTA_LINUX */
+
+#define V2_DQINFOOFF sizeof(struct v2_disk_dqheader) /* Offset of info header in file */
+#define V2_DQBLKSIZE_BITS 10
+#define V2_DQBLKSIZE (1 << V2_DQBLKSIZE_BITS) /* Size of block with quota structures */
+#define V2_DQTREEOFF 1 /* Offset of tree in file in blOcks */
+#define V2_DQTREEDEPTH 4 /* Depth of quota tree */
+#define V2_DQSTRINBLK ((V2_DQBLKSIZE - sizeof(struct v2_disk_dqdbheader)) / sizeof(struct v2_disk_dqblk)) /* Number of entries in one blocks */
+#define V2_GETIDINDEX(id, depth) (((id) >> ((V2_DQTREEDEPTH-(depth)-1)*8)) & 0xff)
+#define V2_GETENTRIES(buf) ((struct v2_disk_dqblk *)(((char *)(buf)) + sizeof(struct v2_disk_dqdbheader)))
+#define INIT_V2_VERSIONS { 0, 0}
+
+struct v2_disk_dqheader {
+ u_int32_t dqh_magic; /* Magic number identifying file */
+ u_int32_t dqh_version; /* File version */
+} __attribute__ ((packed));
+
+/* Flags for version specific files */
+#define V2_DQF_MASK 0x0000 /* Mask for all valid ondisk flags */
+
+/* Header with type and version specific information */
+struct v2_disk_dqinfo {
+ u_int32_t dqi_bgrace; /* Time before block soft limit becomes hard limit */
+ u_int32_t dqi_igrace; /* Time before inode soft limit becomes hard limit */
+ u_int32_t dqi_flags; /* Flags for quotafile (DQF_*) */
+ u_int32_t dqi_blocks; /* Number of blocks in file */
+ u_int32_t dqi_free_blk; /* Number of first free block in the list */
+ u_int32_t dqi_free_entry; /* Number of block with at least one free entry */
+} __attribute__ ((packed));
+
+/*
+ * Structure of header of block with quota structures. It is padded to 16 bytes so
+ * there will be space for exactly 18 quota-entries in a block
+ */
+struct v2_disk_dqdbheader {
+ u_int32_t dqdh_next_free; /* Number of next block with free entry */
+ u_int32_t dqdh_prev_free; /* Number of previous block with free entry */
+ u_int16_t dqdh_entries; /* Number of valid entries in block */
+ u_int16_t dqdh_pad1;
+ u_int32_t dqdh_pad2;
+} __attribute__ ((packed));
+
+/* Structure of quota for one user on disk */
+struct v2_disk_dqblk {
+ u_int32_t dqb_id; /* id this quota applies to */
+ u_int32_t dqb_ihardlimit; /* absolute limit on allocated inodes */
+ u_int32_t dqb_isoftlimit; /* preferred inode limit */
+ u_int32_t dqb_curinodes; /* current # allocated inodes */
+ u_int32_t dqb_bhardlimit; /* absolute limit on disk space (in QUOTABLOCK_SIZE) */
+ u_int32_t dqb_bsoftlimit; /* preferred limit on disk space (in QUOTABLOCK_SIZE) */
+ u_int64_t dqb_curspace; /* current space occupied (in bytes) */
+ u_int64_t dqb_btime; /* time limit for excessive disk use */
+ u_int64_t dqb_itime; /* time limit for excessive inode use */
+} __attribute__ ((packed));
+
+/* Structure of quota for communication with kernel */
+struct v2_kern_dqblk {
+ unsigned int dqb_ihardlimit;
+ unsigned int dqb_isoftlimit;
+ unsigned int dqb_curinodes;
+ unsigned int dqb_bhardlimit;
+ unsigned int dqb_bsoftlimit;
+ qsize_t dqb_curspace;
+ time_t dqb_btime;
+ time_t dqb_itime;
+};
+
+/* Structure of quotafile info for communication with kernel */
+struct v2_kern_dqinfo {
+ unsigned int dqi_bgrace;
+ unsigned int dqi_igrace;
+ unsigned int dqi_flags;
+ unsigned int dqi_blocks;
+ unsigned int dqi_free_blk;
+ unsigned int dqi_free_entry;
+};
+
+/* Structure with gathered statistics from kernel */
+struct v2_dqstats {
+ u_int32_t lookups;
+ u_int32_t drops;
+ u_int32_t reads;
+ u_int32_t writes;
+ u_int32_t cache_hits;
+ u_int32_t allocated_dquots;
+ u_int32_t free_dquots;
+ u_int32_t syncs;
+ u_int32_t version;
+};
+
+#ifndef Q_V2_GETQUOTA
+#define Q_V2_GETQUOTA 0x0D00
+#endif
+#ifndef Q_V2_SETQUOTA
+#define Q_V2_SETQUOTA 0x0E00
+#endif
+
+#endif /* _QUOTAIO_LINUX_V2 */
+
+#ifndef QUOTABLOCK_SIZE
+#define QUOTABLOCK_SIZE 1024
+#endif
+
+#endif /* _SAMBA_LINUX_QUOTA_H_ */
diff --git a/source/include/samba_xfs_quota.h b/source/include/samba_xfs_quota.h
new file mode 100644
index 00000000000..1db435064a6
--- /dev/null
+++ b/source/include/samba_xfs_quota.h
@@ -0,0 +1,165 @@
+#ifndef _SAMBA_LINUX_XFS_H_
+#define _SAMBA_LINUX_XFS_H_
+
+#ifndef _QUOTAIO_LINUX_XFS
+#define _QUOTAIO_LINUX_XFS
+
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <linux/types.h>
+
+#define XQM_CMD(cmd) ( ('X'<<8)+(cmd) )
+#define IS_XQM_CMD(cmd) ( ((int)(cmd)>>8) == 'X' )
+
+/*
+ * Disk quota - quotactl(2) commands for XFS Quota Manager (XQM).
+ */
+#define Q_XQUOTAON XQM_CMD(0x1) /* enable quota accounting/enforcement */
+#define Q_XQUOTAOFF XQM_CMD(0x2) /* disable quota accounting/enforcement */
+#define Q_XGETQUOTA XQM_CMD(0x3) /* get disk limits & usage */
+#define Q_XSETQLIM XQM_CMD(0x4) /* set disk limits only */
+#define Q_XGETQSTAT XQM_CMD(0x5) /* returns fs_quota_stat_t struct */
+#define Q_XQUOTARM XQM_CMD(0x6) /* free quota files' space */
+
+/*
+ * fs_disk_quota structure:
+ *
+ * This contains the current quota information regarding a user/proj/group.
+ * It is 64-bit aligned, and all the blk units are in BBs (Basic Blocks) of
+ * 512 bytes.
+ */
+#define FS_DQUOT_VERSION 1 /* fs_disk_quota.d_version */
+typedef struct fs_disk_quota {
+ u_int8_t d_version; /* version of this structure */
+ u_int8_t d_flags; /* XFS_{USER,PROJ,GROUP}_QUOTA */
+ u_int16_t d_fieldmask; /* field specifier */
+ u_int32_t d_id; /* user, project, or group ID */
+ u_int64_t d_blk_hardlimit; /* absolute limit on disk blks */
+ u_int64_t d_blk_softlimit; /* preferred limit on disk blks */
+ u_int64_t d_ino_hardlimit; /* maximum # allocated inodes */
+ u_int64_t d_ino_softlimit; /* preferred inode limit */
+ u_int64_t d_bcount; /* # disk blocks owned by the user */
+ u_int64_t d_icount; /* # inodes owned by the user */
+ int32_t d_itimer; /* zero if within inode limits */
+ /* if not, we refuse service */
+ int32_t d_btimer; /* similar to above; for disk blocks */
+ u_int16_t d_iwarns; /* # warnings issued wrt num inodes */
+ u_int16_t d_bwarns; /* # warnings issued wrt disk blocks */
+ int32_t d_padding2; /* padding2 - for future use */
+ u_int64_t d_rtb_hardlimit; /* absolute limit on realtime blks */
+ u_int64_t d_rtb_softlimit; /* preferred limit on RT disk blks */
+ u_int64_t d_rtbcount; /* # realtime blocks owned */
+ int32_t d_rtbtimer; /* similar to above; for RT disk blks */
+ u_int16_t d_rtbwarns; /* # warnings issued wrt RT disk blks */
+ int16_t d_padding3; /* padding3 - for future use */
+ char d_padding4[8]; /* yet more padding */
+} fs_disk_quota_t;
+
+/*
+ * These fields are sent to Q_XSETQLIM to specify fields that need to change.
+ */
+#define FS_DQ_ISOFT (1<<0)
+#define FS_DQ_IHARD (1<<1)
+#define FS_DQ_BSOFT (1<<2)
+#define FS_DQ_BHARD (1<<3)
+#define FS_DQ_RTBSOFT (1<<4)
+#define FS_DQ_RTBHARD (1<<5)
+#define FS_DQ_LIMIT_MASK (FS_DQ_ISOFT | FS_DQ_IHARD | FS_DQ_BSOFT | \
+ FS_DQ_BHARD | FS_DQ_RTBSOFT | FS_DQ_RTBHARD)
+/*
+ * These timers can only be set in super user's dquot. For others, timers are
+ * automatically started and stopped. Superusers timer values set the limits
+ * for the rest. In case these values are zero, the DQ_{F,B}TIMELIMIT values
+ * defined below are used.
+ * These values also apply only to the d_fieldmask field for Q_XSETQLIM.
+ */
+#define FS_DQ_BTIMER (1<<6)
+#define FS_DQ_ITIMER (1<<7)
+#define FS_DQ_RTBTIMER (1<<8)
+#define FS_DQ_TIMER_MASK (FS_DQ_BTIMER | FS_DQ_ITIMER | FS_DQ_RTBTIMER)
+
+/*
+ * The following constants define the default amount of time given a user
+ * before the soft limits are treated as hard limits (usually resulting
+ * in an allocation failure). These may be modified by the quotactl(2)
+ * system call with the Q_XSETQLIM command.
+ */
+#define DQ_FTIMELIMIT (7 * 24*60*60) /* 1 week */
+#define DQ_BTIMELIMIT (7 * 24*60*60) /* 1 week */
+
+/*
+ * Various flags related to quotactl(2). Only relevant to XFS filesystems.
+ */
+#define XFS_QUOTA_UDQ_ACCT (1<<0) /* user quota accounting */
+#define XFS_QUOTA_UDQ_ENFD (1<<1) /* user quota limits enforcement */
+#define XFS_QUOTA_GDQ_ACCT (1<<2) /* group quota accounting */
+#define XFS_QUOTA_GDQ_ENFD (1<<3) /* group quota limits enforcement */
+
+#define XFS_USER_QUOTA (1<<0) /* user quota type */
+#define XFS_PROJ_QUOTA (1<<1) /* (IRIX) project quota type */
+#define XFS_GROUP_QUOTA (1<<2) /* group quota type */
+
+/*
+ * fs_quota_stat is the struct returned in Q_XGETQSTAT for a given file system.
+ * Provides a centralized way to get meta infomation about the quota subsystem.
+ * eg. space taken up for user and group quotas, number of dquots currently
+ * incore.
+ */
+#define FS_QSTAT_VERSION 1 /* fs_quota_stat.qs_version */
+
+/*
+ * Some basic infomation about 'quota files'.
+ */
+typedef struct fs_qfilestat {
+ u_int64_t qfs_ino; /* inode number */
+ u_int64_t qfs_nblks; /* number of BBs 512-byte-blks */
+ u_int32_t qfs_nextents; /* number of extents */
+} fs_qfilestat_t;
+
+typedef struct fs_quota_stat {
+ u_int8_t qs_version; /* version number for future changes */
+ u_int16_t qs_flags; /* XFS_QUOTA_{U,P,G}DQ_{ACCT,ENFD} */
+ u_int8_t qs_pad; /* unused */
+ fs_qfilestat_t qs_uquota; /* user quota storage information */
+ fs_qfilestat_t qs_gquota; /* group quota storage information */
+ u_int32_t qs_incoredqs; /* number of dquots incore */
+ int32_t qs_btimelimit; /* limit for blks timer */
+ int32_t qs_itimelimit; /* limit for inodes timer */
+ int32_t qs_rtbtimelimit; /* limit for rt blks timer */
+ u_int16_t qs_bwarnlimit; /* limit for num warnings */
+ u_int16_t qs_iwarnlimit; /* limit for num warnings */
+} fs_quota_stat_t;
+
+#endif /* _QUOTAIO_LINUX_XFS */
+
+#endif /* _SAMBA_LINUX_XFS_H_ */
diff --git a/source/include/secrets.h b/source/include/secrets.h
new file mode 100644
index 00000000000..8c393940586
--- /dev/null
+++ b/source/include/secrets.h
@@ -0,0 +1,105 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * secrets.tdb file format info
+ * Copyright (C) Andrew Tridgell 2000
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 675
+ * Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _SECRETS_H
+#define _SECRETS_H
+
+/* the first one is for the hashed password (NT4 style) the latter
+ for plaintext (ADS)
+*/
+#define SECRETS_MACHINE_ACCT_PASS "SECRETS/$MACHINE.ACC"
+#define SECRETS_MACHINE_PASSWORD "SECRETS/MACHINE_PASSWORD"
+#define SECRETS_MACHINE_LAST_CHANGE_TIME "SECRETS/MACHINE_LAST_CHANGE_TIME"
+#define SECRETS_MACHINE_SEC_CHANNEL_TYPE "SECRETS/MACHINE_SEC_CHANNEL_TYPE"
+#define SECRETS_MACHINE_TRUST_ACCOUNT_NAME "SECRETS/SECRETS_MACHINE_TRUST_ACCOUNT_NAME"
+/* this one is for storing trusted domain account password */
+#define SECRETS_DOMTRUST_ACCT_PASS "SECRETS/$DOMTRUST.ACC"
+
+/* The domain sid and our sid are stored here even though they aren't
+ really secret. */
+#define SECRETS_DOMAIN_SID "SECRETS/SID"
+#define SECRETS_SAM_SID "SAM/SID"
+
+/* The domain GUID and server GUID (NOT the same) are also not secret */
+#define SECRETS_DOMAIN_GUID "SECRETS/DOMGUID"
+#define SECRETS_SERVER_GUID "SECRETS/GUID"
+
+#define SECRETS_LDAP_BIND_PW "SECRETS/LDAP_BIND_PW"
+
+/* Authenticated user info is stored in secrets.tdb under these keys */
+
+#define SECRETS_AUTH_USER "SECRETS/AUTH_USER"
+#define SECRETS_AUTH_DOMAIN "SECRETS/AUTH_DOMAIN"
+#define SECRETS_AUTH_PASSWORD "SECRETS/AUTH_PASSWORD"
+
+/* Trust password type flags */
+#define PASS_MACHINE_TRUST_NT 0x0001
+#define PASS_SERVER_TRUST_NT 0x0002
+#define PASS_DOMAIN_TRUST_NT 0x0004
+#define PASS_MACHINE_TRUST_ADS 0x0008
+#define PASS_DOMAIN_TRUST_ADS 0x0010
+
+/* structure for storing machine account password
+ (ie. when samba server is member of a domain */
+struct machine_acct_pass {
+ uint8 hash[16];
+ time_t mod_time;
+};
+
+/*
+ * storage structure for trusted domain
+ */
+typedef struct trusted_dom_pass {
+ size_t uni_name_len;
+ smb_ucs2_t uni_name[32]; /* unicode domain name */
+ size_t pass_len;
+ fstring pass; /* trust relationship's password */
+ time_t mod_time;
+ DOM_SID domain_sid; /* remote domain's sid */
+} TRUSTED_DOM_PASS;
+
+/*
+ * trusted domain entry/entries returned by secrets_get_trusted_domains
+ * (used in _lsa_enum_trust_dom call)
+ */
+typedef struct trustdom {
+ smb_ucs2_t *name;
+ DOM_SID sid;
+} TRUSTDOM;
+
+/*
+ * Format of an OpenAFS keyfile
+ */
+
+#define SECRETS_AFS_MAXKEYS 8
+
+struct afs_key {
+ uint32 kvno;
+ char key[8];
+};
+
+struct afs_keyfile {
+ uint32 nkeys;
+ struct afs_key entry[SECRETS_AFS_MAXKEYS];
+};
+
+#define SECRETS_AFS_KEYFILE "SECRETS/AFS_KEYFILE"
+
+#endif /* _SECRETS_H */
diff --git a/source/include/session.h b/source/include/session.h
new file mode 100644
index 00000000000..f613afee09a
--- /dev/null
+++ b/source/include/session.h
@@ -0,0 +1,40 @@
+/*
+ Unix SMB/CIFS implementation.
+ session handling for recording currently vailid vuids
+ Copyright (C) tridge@samba.org 2001
+ Copyright (C) Andew Bartlett <abartlet@samba.org> 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* a "session" is claimed when we do a SessionSetupX operation
+ and is yielded when the corresponding vuid is destroyed.
+
+ sessions are used to populate utmp and PAM session structures
+*/
+
+struct sessionid {
+ uid_t uid;
+ gid_t gid;
+ fstring username;
+ fstring hostname;
+ fstring netbios_name;
+ fstring remote_machine;
+ fstring id_str;
+ uint32 id_num;
+ uint32 pid;
+ fstring ip_addr;
+};
+
diff --git a/source/include/smb.h b/source/include/smb.h
new file mode 100644
index 00000000000..6de50c8afa1
--- /dev/null
+++ b/source/include/smb.h
@@ -0,0 +1,1679 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup, plus a whole lot more.
+
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) John H Terpstra 1996-2002
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Paul Ashton 1998-2000
+ Copyright (C) Simo Sorce 2001-2002
+ Copyright (C) Martin Pool 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _SMB_H
+#define _SMB_H
+
+#if defined(LARGE_SMB_OFF_T)
+#define BUFFER_SIZE (128*1024)
+#else /* no large readwrite possible */
+#define BUFFER_SIZE (0xFFFF)
+#endif
+
+#define SAFETY_MARGIN 1024
+#define LARGE_WRITEX_HDR_SIZE 65
+
+#define NMB_PORT 137
+#define DGRAM_PORT 138
+#define SMB_PORT1 445
+#define SMB_PORT2 139
+#define SMB_PORTS "445 139"
+
+#define Undefined (-1)
+#define False (0)
+#define True (1)
+#define Auto (2)
+#define Required (3)
+
+#ifndef _BOOL
+typedef int BOOL;
+#define _BOOL /* So we don't typedef BOOL again in vfs.h */
+#endif
+
+/* limiting size of ipc replies */
+#define REALLOC(ptr,size) Realloc(ptr,MAX((size),4*1024))
+
+#define SIZEOFWORD 2
+
+#ifndef DEF_CREATE_MASK
+#define DEF_CREATE_MASK (0755)
+#endif
+
+/* string manipulation flags - see clistr.c and srvstr.c */
+#define STR_TERMINATE 1
+#define STR_UPPER 2
+#define STR_ASCII 4
+#define STR_UNICODE 8
+#define STR_NOALIGN 16
+#define STR_TERMINATE_ASCII 128
+
+/* how long to wait for secondary SMB packets (milli-seconds) */
+#define SMB_SECONDARY_WAIT (60*1000)
+
+/* Debugging stuff */
+#include "debug.h"
+
+/* this defines the error codes that receive_smb can put in smb_read_error */
+#define READ_TIMEOUT 1
+#define READ_EOF 2
+#define READ_ERROR 3
+#define WRITE_ERROR 4 /* This error code can go into the client smb_rw_error. */
+#define READ_BAD_SIG 5
+
+#define DIR_STRUCT_SIZE 43
+
+/* these define the attribute byte as seen by DOS */
+#define aRONLY (1L<<0) /* 0x01 */
+#define aHIDDEN (1L<<1) /* 0x02 */
+#define aSYSTEM (1L<<2) /* 0x04 */
+#define aVOLID (1L<<3) /* 0x08 */
+#define aDIR (1L<<4) /* 0x10 */
+#define aARCH (1L<<5) /* 0x20 */
+
+/* deny modes */
+#define DENY_DOS 0
+#define DENY_ALL 1
+#define DENY_WRITE 2
+#define DENY_READ 3
+#define DENY_NONE 4
+#define DENY_FCB 7
+
+/* open modes */
+#define DOS_OPEN_RDONLY 0
+#define DOS_OPEN_WRONLY 1
+#define DOS_OPEN_RDWR 2
+#define DOS_OPEN_FCB 0xF
+
+/* define shifts and masks for share and open modes. */
+#define OPEN_MODE_MASK 0xF
+#define SHARE_MODE_SHIFT 4
+#define SHARE_MODE_MASK 0x7
+#define GET_OPEN_MODE(x) ((x) & OPEN_MODE_MASK)
+#define SET_OPEN_MODE(x) ((x) & OPEN_MODE_MASK)
+#define GET_DENY_MODE(x) (((x)>>SHARE_MODE_SHIFT) & SHARE_MODE_MASK)
+#define SET_DENY_MODE(x) (((x) & SHARE_MODE_MASK) <<SHARE_MODE_SHIFT)
+
+/* Sync on open file (not sure if used anymore... ?) */
+#define FILE_SYNC_OPENMODE (1<<14)
+#define GET_FILE_SYNC_OPENMODE(x) (((x) & FILE_SYNC_OPENMODE) ? True : False)
+
+/* allow delete on open file mode (used by NT SMB's). */
+#define ALLOW_SHARE_DELETE (1<<15)
+#define GET_ALLOW_SHARE_DELETE(x) (((x) & ALLOW_SHARE_DELETE) ? True : False)
+#define SET_ALLOW_SHARE_DELETE(x) ((x) ? ALLOW_SHARE_DELETE : 0)
+
+/* delete on close flag (used by NT SMB's). */
+#define DELETE_ON_CLOSE_FLAG (1<<16)
+#define GET_DELETE_ON_CLOSE_FLAG(x) (((x) & DELETE_ON_CLOSE_FLAG) ? True : False)
+#define SET_DELETE_ON_CLOSE_FLAG(x) ((x) ? DELETE_ON_CLOSE_FLAG : 0)
+
+/* open disposition values */
+#define FILE_EXISTS_FAIL 0
+#define FILE_EXISTS_OPEN 1
+#define FILE_EXISTS_TRUNCATE 2
+
+/* mask for open disposition. */
+#define FILE_OPEN_MASK 0x3
+
+#define GET_FILE_OPEN_DISPOSITION(x) ((x) & FILE_OPEN_MASK)
+#define SET_FILE_OPEN_DISPOSITION(x) ((x) & FILE_OPEN_MASK)
+
+/* The above can be OR'ed with... */
+#define FILE_CREATE_IF_NOT_EXIST 0x10
+#define FILE_FAIL_IF_NOT_EXIST 0
+
+#define GET_FILE_CREATE_DISPOSITION(x) ((x) & (FILE_CREATE_IF_NOT_EXIST|FILE_FAIL_IF_NOT_EXIST))
+
+/* share types */
+#define STYPE_DISKTREE 0 /* Disk drive */
+#define STYPE_PRINTQ 1 /* Spooler queue */
+#define STYPE_DEVICE 2 /* Serial device */
+#define STYPE_IPC 3 /* Interprocess communication (IPC) */
+#define STYPE_HIDDEN 0x80000000 /* share is a hidden one (ends with $) */
+
+#include "doserr.h"
+
+typedef union unid_t {
+ uid_t uid;
+ gid_t gid;
+} unid_t;
+
+/*
+ * SMB UCS2 (16-bit unicode) internal type.
+ */
+
+typedef uint16 smb_ucs2_t;
+
+/* ucs2 string types. */
+typedef smb_ucs2_t wpstring[PSTRING_LEN];
+typedef smb_ucs2_t wfstring[FSTRING_LEN];
+
+#ifdef WORDS_BIGENDIAN
+#define UCS2_SHIFT 8
+#else
+#define UCS2_SHIFT 0
+#endif
+
+/* turn a 7 bit character into a ucs2 character */
+#define UCS2_CHAR(c) ((c) << UCS2_SHIFT)
+
+/* pipe string names */
+#define PIPE_LANMAN "\\PIPE\\LANMAN"
+#define PIPE_SRVSVC "\\PIPE\\srvsvc"
+#define PIPE_SAMR "\\PIPE\\samr"
+#define PIPE_WINREG "\\PIPE\\winreg"
+#define PIPE_WKSSVC "\\PIPE\\wkssvc"
+#define PIPE_NETLOGON "\\PIPE\\NETLOGON"
+#define PIPE_NTLSA "\\PIPE\\ntlsa"
+#define PIPE_NTSVCS "\\PIPE\\ntsvcs"
+#define PIPE_LSASS "\\PIPE\\lsass"
+#define PIPE_LSARPC "\\PIPE\\lsarpc"
+#define PIPE_SPOOLSS "\\PIPE\\spoolss"
+#define PIPE_NETDFS "\\PIPE\\netdfs"
+#define PIPE_ECHO "\\PIPE\\rpcecho"
+#define PIPE_SHUTDOWN "\\PIPE\\initshutdown"
+#define PIPE_EPM "\\PIPE\\epmapper"
+
+#define PIPE_NETLOGON_PLAIN "\\NETLOGON"
+
+#define PI_LSARPC 0
+#define PI_LSARPC_DS 1
+#define PI_SAMR 2
+#define PI_NETLOGON 3
+#define PI_SRVSVC 4
+#define PI_WKSSVC 5
+#define PI_WINREG 6
+#define PI_SPOOLSS 7
+#define PI_NETDFS 8
+#define PI_ECHO 9
+#define PI_SHUTDOWN 10
+#define PI_EPM 11
+#define PI_MAX_PIPES 12
+
+/* 64 bit time (100usec) since ????? - cifs6.txt, section 3.5, page 30 */
+typedef struct nttime_info
+{
+ uint32 low;
+ uint32 high;
+} NTTIME;
+
+
+/* Allowable account control bits */
+#define ACB_DISABLED 0x0001 /* 1 = User account disabled */
+#define ACB_HOMDIRREQ 0x0002 /* 1 = Home directory required */
+#define ACB_PWNOTREQ 0x0004 /* 1 = User password not required */
+#define ACB_TEMPDUP 0x0008 /* 1 = Temporary duplicate account */
+#define ACB_NORMAL 0x0010 /* 1 = Normal user account */
+#define ACB_MNS 0x0020 /* 1 = MNS logon user account */
+#define ACB_DOMTRUST 0x0040 /* 1 = Interdomain trust account */
+#define ACB_WSTRUST 0x0080 /* 1 = Workstation trust account */
+#define ACB_SVRTRUST 0x0100 /* 1 = Server trust account */
+#define ACB_PWNOEXP 0x0200 /* 1 = User password does not expire */
+#define ACB_AUTOLOCK 0x0400 /* 1 = Account auto locked */
+
+#define MAX_HOURS_LEN 32
+
+/*
+ * window during which we must talk to the PDC to avoid
+ * sam sync delays; expressed in seconds (15 minutes is the
+ * default period for SAM replication under Windows NT 4.0
+ */
+#define SAM_SYNC_WINDOW 900
+
+
+#ifndef MAXSUBAUTHS
+#define MAXSUBAUTHS 15 /* max sub authorities in a SID */
+#endif
+
+#define SID_MAX_SIZE ((size_t)(8+(MAXSUBAUTHS*4)))
+
+/* SID Types */
+enum SID_NAME_USE
+{
+ SID_NAME_USE_NONE = 0,
+ SID_NAME_USER = 1, /* user */
+ SID_NAME_DOM_GRP, /* domain group */
+ SID_NAME_DOMAIN, /* domain sid */
+ SID_NAME_ALIAS, /* local group */
+ SID_NAME_WKN_GRP, /* well-known group */
+ SID_NAME_DELETED, /* deleted account: needed for c2 rating */
+ SID_NAME_INVALID, /* invalid account */
+ SID_NAME_UNKNOWN, /* unknown sid type */
+ SID_NAME_COMPUTER /* sid for a computer */
+};
+
+/**
+ * @brief Security Identifier
+ *
+ * @sa http://msdn.microsoft.com/library/default.asp?url=/library/en-us/security/accctrl_38yn.asp
+ **/
+typedef struct sid_info
+{
+ uint8 sid_rev_num; /**< SID revision number */
+ uint8 num_auths; /**< Number of sub-authorities */
+ uint8 id_auth[6]; /**< Identifier Authority */
+ /*
+ * Pointer to sub-authorities.
+ *
+ * @note The values in these uint32's are in *native* byteorder, not
+ * neccessarily little-endian...... JRA.
+ */
+ uint32 sub_auths[MAXSUBAUTHS];
+
+} DOM_SID;
+
+/*
+ * The complete list of SIDS belonging to this user.
+ * Created when a vuid is registered.
+ * The definition of the user_sids array is as follows :
+ *
+ * token->user_sids[0] = primary user SID.
+ * token->user_sids[1] = primary group SID.
+ * token->user_sids[2..num_sids] = supplementary group SIDS.
+ */
+
+#define PRIMARY_USER_SID_INDEX 0
+#define PRIMARY_GROUP_SID_INDEX 1
+
+typedef struct _nt_user_token {
+ size_t num_sids;
+ DOM_SID *user_sids;
+} NT_USER_TOKEN;
+
+/*** query a local group, get a list of these: shows who is in that group ***/
+
+/* local group member info */
+typedef struct local_grp_member_info
+{
+ DOM_SID sid ; /* matches with name */
+ uint8 sid_use; /* usr=1 grp=2 dom=3 alias=4 wkng=5 del=6 inv=7 unk=8 */
+ fstring name ; /* matches with sid: must be of the form "DOMAIN\account" */
+
+} LOCAL_GRP_MEMBER;
+
+/* enumerate these to get list of local groups */
+
+/* local group info */
+typedef struct local_grp_info
+{
+ fstring name;
+ fstring comment;
+
+} LOCAL_GRP;
+
+/*** enumerate these to get list of domain groups ***/
+
+/* domain group member info */
+typedef struct domain_grp_info
+{
+ fstring name;
+ fstring comment;
+ uint32 rid; /* group rid */
+ uint8 attr; /* attributes forced to be set to 0x7: SE_GROUP_xxx */
+
+} DOMAIN_GRP;
+
+/*** query a domain group, get a list of these: shows who is in that group ***/
+
+/* domain group info */
+typedef struct domain_grp_member_info
+{
+ fstring name;
+ uint8 attr; /* attributes forced to be set to 0x7: SE_GROUP_xxx */
+
+} DOMAIN_GRP_MEMBER;
+
+/* 32 bit time (sec) since 01jan1970 - cifs6.txt, section 3.5, page 30 */
+typedef struct time_info
+{
+ uint32 time;
+} UTIME;
+
+/* Structure used when SMBwritebmpx is active */
+typedef struct
+{
+ size_t wr_total_written; /* So we know when to discard this */
+ int32 wr_timeout;
+ int32 wr_errclass;
+ int32 wr_error; /* Cached errors */
+ BOOL wr_mode; /* write through mode) */
+ BOOL wr_discard; /* discard all further data */
+} write_bmpx_struct;
+
+typedef struct write_cache
+{
+ SMB_OFF_T file_size;
+ SMB_OFF_T offset;
+ size_t alloc_size;
+ size_t data_size;
+ char *data;
+} write_cache;
+
+typedef struct
+{
+ smb_ucs2_t *origname;
+ smb_ucs2_t *filename;
+ SMB_STRUCT_STAT *statinfo;
+} smb_filename;
+
+#include "fake_file.h"
+
+typedef struct files_struct
+{
+ struct files_struct *next, *prev;
+ int fnum;
+ struct connection_struct *conn;
+ int fd;
+ uint16 rap_print_jobid;
+ SMB_DEV_T dev;
+ SMB_INO_T inode;
+ BOOL delete_on_close;
+ SMB_OFF_T pos;
+ SMB_BIG_UINT size;
+ SMB_BIG_UINT initial_allocation_size; /* Faked up initial allocation on disk. */
+ SMB_BIG_UINT position_information;
+ mode_t mode;
+ uint16 file_pid;
+ uint16 vuid;
+ write_bmpx_struct *wbmpx_ptr;
+ write_cache *wcp;
+ struct timeval open_time;
+ int share_mode;
+ uint32 desired_access;
+ time_t pending_modtime;
+ int oplock_type;
+ int sent_oplock_break;
+ unsigned long file_id;
+ BOOL can_lock;
+ BOOL can_read;
+ BOOL can_write;
+ BOOL print_file;
+ BOOL modified;
+ BOOL is_directory;
+ BOOL is_stat;
+ BOOL directory_delete_on_close;
+ char *fsp_name;
+ FAKE_FILE_HANDLE *fake_file_handle;
+} files_struct;
+
+#include "ntquotas.h"
+#include "sysquotas.h"
+
+/* used to hold an arbitrary blob of data */
+typedef struct data_blob
+{
+ uint8 *data;
+ size_t length;
+ void (*free)(struct data_blob *data_blob);
+} DATA_BLOB;
+
+/*
+ * Structure used to keep directory state information around.
+ * Used in NT change-notify code.
+ */
+
+typedef struct
+{
+ time_t modify_time;
+ time_t status_time;
+} dir_status_struct;
+
+struct vuid_cache_entry
+{
+ uint16 vuid;
+ BOOL read_only;
+ BOOL admin_user;
+};
+
+struct vuid_cache
+{
+ unsigned int entries;
+ struct vuid_cache_entry array[VUID_CACHE_SIZE];
+};
+
+typedef struct
+{
+ char *name;
+ BOOL is_wild;
+} name_compare_entry;
+
+/* Include VFS stuff */
+
+#include "smb_acls.h"
+#include "vfs.h"
+
+typedef struct connection_struct
+{
+ struct connection_struct *next, *prev;
+ TALLOC_CTX *mem_ctx;
+ unsigned cnum; /* an index passed over the wire */
+ int service;
+ BOOL force_user;
+ BOOL force_group;
+ struct vuid_cache vuid_cache;
+ void *dirptr;
+ BOOL printer;
+ BOOL ipc;
+ BOOL read_only; /* Attributes for the current user of the share. */
+ BOOL admin_user; /* Attributes for the current user of the share. */
+ char *dirpath;
+ char *connectpath;
+ char *origpath;
+
+ struct vfs_ops vfs; /* Filesystem operations */
+ struct vfs_ops vfs_opaque; /* OPAQUE Filesystem operations */
+ struct vfs_handle_struct *vfs_handles; /* for the new plugins */
+
+ char *user; /* name of user who *opened* this connection */
+ uid_t uid; /* uid of user who *opened* this connection */
+ gid_t gid; /* gid of user who *opened* this connection */
+ char client_address[18]; /* String version of client IP address. */
+
+ uint16 vuid; /* vuid of user who *opened* this connection, or UID_FIELD_INVALID */
+
+ /* following groups stuff added by ih */
+
+ /* This groups info is valid for the user that *opened* the connection */
+ int ngroups;
+ gid_t *groups;
+ NT_USER_TOKEN *nt_user_token;
+ PRIVILEGE_SET *privs;
+
+ time_t lastused;
+ BOOL used;
+ int num_files_open;
+ name_compare_entry *hide_list; /* Per-share list of files to return as hidden. */
+ name_compare_entry *veto_list; /* Per-share list of files to veto (never show). */
+ name_compare_entry *veto_oplock_list; /* Per-share list of files to refuse oplocks on. */
+
+} connection_struct;
+
+struct current_user
+{
+ connection_struct *conn;
+ uint16 vuid;
+ uid_t uid;
+ gid_t gid;
+ int ngroups;
+ gid_t *groups;
+ NT_USER_TOKEN *nt_user_token;
+ PRIVILEGE_SET *privs;
+};
+
+/* Defines for the sent_oplock_break field above. */
+#define NO_BREAK_SENT 0
+#define EXCLUSIVE_BREAK_SENT 1
+#define LEVEL_II_BREAK_SENT 2
+
+typedef struct {
+ fstring smb_name; /* user name from the client */
+ fstring unix_name; /* unix user name of a validated user */
+ fstring full_name; /* to store full name (such as "Joe Bloggs") from gecos field of password file */
+ fstring domain; /* domain that the client specified */
+} userdom_struct;
+
+/* Extra fields above "LPQ_PRINTING" are used to map extra NT status codes. */
+
+enum {LPQ_QUEUED=0,LPQ_PAUSED,LPQ_SPOOLING,LPQ_PRINTING,LPQ_ERROR,LPQ_DELETING,
+ LPQ_OFFLINE,LPQ_PAPEROUT,LPQ_PRINTED,LPQ_DELETED,LPQ_BLOCKED,LPQ_USER_INTERVENTION};
+
+typedef struct _print_queue_struct
+{
+ int job; /* normally the SMB jobid -- see note in
+ printing.c:traverse_fn_delete() */
+ int size;
+ int page_count;
+ int status;
+ int priority;
+ time_t time;
+ fstring fs_user;
+ fstring fs_file;
+} print_queue_struct;
+
+enum {LPSTAT_OK, LPSTAT_STOPPED, LPSTAT_ERROR};
+
+typedef struct
+{
+ fstring message;
+ int qcount;
+ int status;
+} print_status_struct;
+
+/* used for server information: client, nameserv and ipc */
+struct server_info_struct
+{
+ fstring name;
+ uint32 type;
+ fstring comment;
+ fstring domain; /* used ONLY in ipc.c NOT namework.c */
+ BOOL server_added; /* used ONLY in ipc.c NOT namework.c */
+};
+
+
+/* used for network interfaces */
+struct interface
+{
+ struct interface *next, *prev;
+ struct in_addr ip;
+ struct in_addr bcast;
+ struct in_addr nmask;
+};
+
+/* struct returned by get_share_modes */
+typedef struct {
+ pid_t pid;
+ uint16 op_port;
+ uint16 op_type;
+ int share_mode;
+ uint32 desired_access;
+ struct timeval time;
+ SMB_DEV_T dev;
+ SMB_INO_T inode;
+ unsigned long share_file_id;
+} share_mode_entry;
+
+
+#define SHAREMODE_FN_CAST() \
+ void (*)(share_mode_entry *, char*)
+
+#define SHAREMODE_FN(fn) \
+ void (*fn)(share_mode_entry *, char*)
+
+#define NT_HASH_LEN 16
+#define LM_HASH_LEN 16
+
+/*
+ * Flags for account policy.
+ */
+#define AP_MIN_PASSWORD_LEN 1
+#define AP_PASSWORD_HISTORY 2
+#define AP_USER_MUST_LOGON_TO_CHG_PASS 3
+#define AP_MAX_PASSWORD_AGE 4
+#define AP_MIN_PASSWORD_AGE 5
+#define AP_LOCK_ACCOUNT_DURATION 6
+#define AP_RESET_COUNT_TIME 7
+#define AP_BAD_ATTEMPT_LOCKOUT 8
+#define AP_TIME_TO_LOGOUT 9
+
+
+/*
+ * Flags for local user manipulation.
+ */
+
+#define LOCAL_ADD_USER 0x1
+#define LOCAL_DELETE_USER 0x2
+#define LOCAL_DISABLE_USER 0x4
+#define LOCAL_ENABLE_USER 0x8
+#define LOCAL_TRUST_ACCOUNT 0x10
+#define LOCAL_SET_NO_PASSWORD 0x20
+#define LOCAL_SET_PASSWORD 0x40
+#define LOCAL_SET_LDAP_ADMIN_PW 0x80
+#define LOCAL_INTERDOM_ACCOUNT 0x100
+#define LOCAL_AM_ROOT 0x200 /* Act as root */
+
+/* key and data in the connections database - used in smbstatus and smbd */
+struct connections_key {
+ pid_t pid;
+ int cnum;
+ fstring name;
+};
+
+struct connections_data {
+ int magic;
+ pid_t pid;
+ int cnum;
+ uid_t uid;
+ gid_t gid;
+ char name[24];
+ char addr[24];
+ char machine[FSTRING_LEN];
+ time_t start;
+ uint32 bcast_msg_flags;
+};
+
+
+/* key and data records in the tdb locking database */
+struct locking_key {
+ SMB_DEV_T dev;
+ SMB_INO_T inode;
+};
+
+struct locking_data {
+ union {
+ int num_share_mode_entries;
+ share_mode_entry dummy; /* Needed for alignment. */
+ } u;
+ /* the following two entries are implicit
+ share_mode_entry modes[num_share_mode_entries];
+ char file_name[];
+ */
+};
+
+
+/* the following are used by loadparm for option lists */
+typedef enum
+{
+ P_BOOL,P_BOOLREV,P_CHAR,P_INTEGER,P_OCTAL,P_LIST,
+ P_STRING,P_USTRING,P_GSTRING,P_UGSTRING,P_ENUM,P_SEP
+} parm_type;
+
+typedef enum
+{
+ P_LOCAL,P_GLOBAL,P_SEPARATOR,P_NONE
+} parm_class;
+
+/* passed to br lock code */
+enum brl_type {READ_LOCK, WRITE_LOCK, PENDING_LOCK};
+
+struct enum_list {
+ int value;
+ const char *name;
+};
+
+#define BRLOCK_FN_CAST() \
+ void (*)(SMB_DEV_T dev, SMB_INO_T ino, int pid, \
+ enum brl_type lock_type, \
+ br_off start, br_off size)
+#define BRLOCK_FN(fn) \
+ void (*fn)(SMB_DEV_T dev, SMB_INO_T ino, int pid, \
+ enum brl_type lock_type, \
+ br_off start, br_off size)
+struct parm_struct
+{
+ const char *label;
+ parm_type type;
+ parm_class class;
+ void *ptr;
+ BOOL (*special)(const char *, char **);
+ const struct enum_list *enum_list;
+ unsigned flags;
+ union {
+ BOOL bvalue;
+ int ivalue;
+ char *svalue;
+ char cvalue;
+ char **lvalue;
+ } def;
+};
+
+struct bitmap {
+ uint32 *b;
+ unsigned int n;
+};
+
+/* The following flags are used in SWAT */
+#define FLAG_BASIC 0x0001 /* Display only in BASIC view */
+#define FLAG_SHARE 0x0002 /* file sharing options */
+#define FLAG_PRINT 0x0004 /* printing options */
+#define FLAG_GLOBAL 0x0008 /* local options that should be globally settable in SWAT */
+#define FLAG_WIZARD 0x0010 /* Parameters that the wizard will operate on */
+#define FLAG_ADVANCED 0x0020 /* Parameters that will be visible in advanced view */
+#define FLAG_DEVELOPER 0x0040 /* No longer used */
+#define FLAG_DEPRECATED 0x1000 /* options that should no longer be used */
+#define FLAG_HIDE 0x2000 /* options that should be hidden in SWAT */
+#define FLAG_DOS_STRING 0x4000 /* convert from UNIX to DOS codepage when reading this string. */
+
+#ifndef LOCKING_VERSION
+#define LOCKING_VERSION 4
+#endif /* LOCKING_VERSION */
+
+
+/* the basic packet size, assuming no words or bytes */
+#define smb_size 39
+
+/* offsets into message for common items */
+#define smb_com 8
+#define smb_rcls 9
+#define smb_reh 10
+#define smb_err 11
+#define smb_flg 13
+#define smb_flg2 14
+#define smb_pidhigh 16
+#define smb_ss_field 18
+#define smb_tid 28
+#define smb_pid 30
+#define smb_uid 32
+#define smb_mid 34
+#define smb_wct 36
+#define smb_vwv 37
+#define smb_vwv0 37
+#define smb_vwv1 39
+#define smb_vwv2 41
+#define smb_vwv3 43
+#define smb_vwv4 45
+#define smb_vwv5 47
+#define smb_vwv6 49
+#define smb_vwv7 51
+#define smb_vwv8 53
+#define smb_vwv9 55
+#define smb_vwv10 57
+#define smb_vwv11 59
+#define smb_vwv12 61
+#define smb_vwv13 63
+#define smb_vwv14 65
+#define smb_vwv15 67
+#define smb_vwv16 69
+#define smb_vwv17 71
+
+/* flag defines. CIFS spec 3.1.1 */
+#define FLAG_SUPPORT_LOCKREAD 0x01
+#define FLAG_CLIENT_BUF_AVAIL 0x02
+#define FLAG_RESERVED 0x04
+#define FLAG_CASELESS_PATHNAMES 0x08
+#define FLAG_CANONICAL_PATHNAMES 0x10
+#define FLAG_REQUEST_OPLOCK 0x20
+#define FLAG_REQUEST_BATCH_OPLOCK 0x40
+#define FLAG_REPLY 0x80
+
+/* the complete */
+#define SMBmkdir 0x00 /* create directory */
+#define SMBrmdir 0x01 /* delete directory */
+#define SMBopen 0x02 /* open file */
+#define SMBcreate 0x03 /* create file */
+#define SMBclose 0x04 /* close file */
+#define SMBflush 0x05 /* flush file */
+#define SMBunlink 0x06 /* delete file */
+#define SMBmv 0x07 /* rename file */
+#define SMBgetatr 0x08 /* get file attributes */
+#define SMBsetatr 0x09 /* set file attributes */
+#define SMBread 0x0A /* read from file */
+#define SMBwrite 0x0B /* write to file */
+#define SMBlock 0x0C /* lock byte range */
+#define SMBunlock 0x0D /* unlock byte range */
+#define SMBctemp 0x0E /* create temporary file */
+#define SMBmknew 0x0F /* make new file */
+#define SMBchkpth 0x10 /* check directory path */
+#define SMBexit 0x11 /* process exit */
+#define SMBlseek 0x12 /* seek */
+#define SMBtcon 0x70 /* tree connect */
+#define SMBtconX 0x75 /* tree connect and X*/
+#define SMBtdis 0x71 /* tree disconnect */
+#define SMBnegprot 0x72 /* negotiate protocol */
+#define SMBdskattr 0x80 /* get disk attributes */
+#define SMBsearch 0x81 /* search directory */
+#define SMBsplopen 0xC0 /* open print spool file */
+#define SMBsplwr 0xC1 /* write to print spool file */
+#define SMBsplclose 0xC2 /* close print spool file */
+#define SMBsplretq 0xC3 /* return print queue */
+#define SMBsends 0xD0 /* send single block message */
+#define SMBsendb 0xD1 /* send broadcast message */
+#define SMBfwdname 0xD2 /* forward user name */
+#define SMBcancelf 0xD3 /* cancel forward */
+#define SMBgetmac 0xD4 /* get machine name */
+#define SMBsendstrt 0xD5 /* send start of multi-block message */
+#define SMBsendend 0xD6 /* send end of multi-block message */
+#define SMBsendtxt 0xD7 /* send text of multi-block message */
+
+/* Core+ protocol */
+#define SMBlockread 0x13 /* Lock a range and read */
+#define SMBwriteunlock 0x14 /* Unlock a range then write */
+#define SMBreadbraw 0x1a /* read a block of data with no smb header */
+#define SMBwritebraw 0x1d /* write a block of data with no smb header */
+#define SMBwritec 0x20 /* secondary write request */
+#define SMBwriteclose 0x2c /* write a file then close it */
+
+/* dos extended protocol */
+#define SMBreadBraw 0x1A /* read block raw */
+#define SMBreadBmpx 0x1B /* read block multiplexed */
+#define SMBreadBs 0x1C /* read block (secondary response) */
+#define SMBwriteBraw 0x1D /* write block raw */
+#define SMBwriteBmpx 0x1E /* write block multiplexed */
+#define SMBwriteBs 0x1F /* write block (secondary request) */
+#define SMBwriteC 0x20 /* write complete response */
+#define SMBsetattrE 0x22 /* set file attributes expanded */
+#define SMBgetattrE 0x23 /* get file attributes expanded */
+#define SMBlockingX 0x24 /* lock/unlock byte ranges and X */
+#define SMBtrans 0x25 /* transaction - name, bytes in/out */
+#define SMBtranss 0x26 /* transaction (secondary request/response) */
+#define SMBioctl 0x27 /* IOCTL */
+#define SMBioctls 0x28 /* IOCTL (secondary request/response) */
+#define SMBcopy 0x29 /* copy */
+#define SMBmove 0x2A /* move */
+#define SMBecho 0x2B /* echo */
+#define SMBopenX 0x2D /* open and X */
+#define SMBreadX 0x2E /* read and X */
+#define SMBwriteX 0x2F /* write and X */
+#define SMBsesssetupX 0x73 /* Session Set Up & X (including User Logon) */
+#define SMBffirst 0x82 /* find first */
+#define SMBfunique 0x83 /* find unique */
+#define SMBfclose 0x84 /* find close */
+#define SMBkeepalive 0x85 /* keepalive */
+#define SMBinvalid 0xFE /* invalid command */
+
+/* Extended 2.0 protocol */
+#define SMBtrans2 0x32 /* TRANS2 protocol set */
+#define SMBtranss2 0x33 /* TRANS2 protocol set, secondary command */
+#define SMBfindclose 0x34 /* Terminate a TRANSACT2_FINDFIRST */
+#define SMBfindnclose 0x35 /* Terminate a TRANSACT2_FINDNOTIFYFIRST */
+#define SMBulogoffX 0x74 /* user logoff */
+
+/* NT SMB extensions. */
+#define SMBnttrans 0xA0 /* NT transact */
+#define SMBnttranss 0xA1 /* NT transact secondary */
+#define SMBntcreateX 0xA2 /* NT create and X */
+#define SMBntcancel 0xA4 /* NT cancel */
+#define SMBntrename 0xA5 /* NT rename */
+
+/* These are the trans subcommands */
+#define TRANSACT_SETNAMEDPIPEHANDLESTATE 0x01
+#define TRANSACT_DCERPCCMD 0x26
+#define TRANSACT_WAITNAMEDPIPEHANDLESTATE 0x53
+
+/* These are the TRANS2 sub commands */
+#define TRANSACT2_OPEN 0x00
+#define TRANSACT2_FINDFIRST 0x01
+#define TRANSACT2_FINDNEXT 0x02
+#define TRANSACT2_QFSINFO 0x03
+#define TRANSACT2_SETFSINFO 0x04
+#define TRANSACT2_QPATHINFO 0x05
+#define TRANSACT2_SETPATHINFO 0x06
+#define TRANSACT2_QFILEINFO 0x07
+#define TRANSACT2_SETFILEINFO 0x08
+#define TRANSACT2_FSCTL 0x09
+#define TRANSACT2_IOCTL 0x0A
+#define TRANSACT2_FINDNOTIFYFIRST 0x0B
+#define TRANSACT2_FINDNOTIFYNEXT 0x0C
+#define TRANSACT2_MKDIR 0x0D
+#define TRANSACT2_SESSION_SETUP 0x0E
+#define TRANSACT2_GET_DFS_REFERRAL 0x10
+#define TRANSACT2_REPORT_DFS_INCONSISTANCY 0x11
+
+/* These are the NT transact sub commands. */
+#define NT_TRANSACT_CREATE 1
+#define NT_TRANSACT_IOCTL 2
+#define NT_TRANSACT_SET_SECURITY_DESC 3
+#define NT_TRANSACT_NOTIFY_CHANGE 4
+#define NT_TRANSACT_RENAME 5
+#define NT_TRANSACT_QUERY_SECURITY_DESC 6
+#define NT_TRANSACT_GET_USER_QUOTA 7
+#define NT_TRANSACT_SET_USER_QUOTA 8
+
+/* These are the NT transact_get_user_quota sub commands */
+#define TRANSACT_GET_USER_QUOTA_LIST_CONTINUE 0x0000
+#define TRANSACT_GET_USER_QUOTA_LIST_START 0x0100
+#define TRANSACT_GET_USER_QUOTA_FOR_SID 0x0101
+
+/* Relevant IOCTL codes */
+#define IOCTL_QUERY_JOB_INFO 0x530060
+
+/* these are the trans2 sub fields for primary requests */
+#define smb_tpscnt smb_vwv0
+#define smb_tdscnt smb_vwv1
+#define smb_mprcnt smb_vwv2
+#define smb_mdrcnt smb_vwv3
+#define smb_msrcnt smb_vwv4
+#define smb_flags smb_vwv5
+#define smb_timeout smb_vwv6
+#define smb_pscnt smb_vwv9
+#define smb_psoff smb_vwv10
+#define smb_dscnt smb_vwv11
+#define smb_dsoff smb_vwv12
+#define smb_suwcnt smb_vwv13
+#define smb_setup smb_vwv14
+#define smb_setup0 smb_setup
+#define smb_setup1 (smb_setup+2)
+#define smb_setup2 (smb_setup+4)
+
+/* these are for the secondary requests */
+#define smb_spscnt smb_vwv2
+#define smb_spsoff smb_vwv3
+#define smb_spsdisp smb_vwv4
+#define smb_sdscnt smb_vwv5
+#define smb_sdsoff smb_vwv6
+#define smb_sdsdisp smb_vwv7
+#define smb_sfid smb_vwv8
+
+/* and these for responses */
+#define smb_tprcnt smb_vwv0
+#define smb_tdrcnt smb_vwv1
+#define smb_prcnt smb_vwv3
+#define smb_proff smb_vwv4
+#define smb_prdisp smb_vwv5
+#define smb_drcnt smb_vwv6
+#define smb_droff smb_vwv7
+#define smb_drdisp smb_vwv8
+
+/* these are for the NT trans primary request. */
+#define smb_nt_MaxSetupCount smb_vwv0
+#define smb_nt_Flags (smb_vwv0 + 1)
+#define smb_nt_TotalParameterCount (smb_vwv0 + 3)
+#define smb_nt_TotalDataCount (smb_vwv0 + 7)
+#define smb_nt_MaxParameterCount (smb_vwv0 + 11)
+#define smb_nt_MaxDataCount (smb_vwv0 + 15)
+#define smb_nt_ParameterCount (smb_vwv0 + 19)
+#define smb_nt_ParameterOffset (smb_vwv0 + 23)
+#define smb_nt_DataCount (smb_vwv0 + 27)
+#define smb_nt_DataOffset (smb_vwv0 + 31)
+#define smb_nt_SetupCount (smb_vwv0 + 35)
+#define smb_nt_Function (smb_vwv0 + 36)
+#define smb_nt_SetupStart (smb_vwv0 + 38)
+
+/* these are for the NT trans secondary request. */
+#define smb_nts_TotalParameterCount (smb_vwv0 + 3)
+#define smb_nts_TotalDataCount (smb_vwv0 + 7)
+#define smb_nts_ParameterCount (smb_vwv0 + 11)
+#define smb_nts_ParameterOffset (smb_vwv0 + 15)
+#define smb_nts_ParameterDisplacement (smb_vwv0 + 19)
+#define smb_nts_DataCount (smb_vwv0 + 23)
+#define smb_nts_DataOffset (smb_vwv0 + 27)
+#define smb_nts_DataDisplacement (smb_vwv0 + 31)
+
+/* these are for the NT trans reply. */
+#define smb_ntr_TotalParameterCount (smb_vwv0 + 3)
+#define smb_ntr_TotalDataCount (smb_vwv0 + 7)
+#define smb_ntr_ParameterCount (smb_vwv0 + 11)
+#define smb_ntr_ParameterOffset (smb_vwv0 + 15)
+#define smb_ntr_ParameterDisplacement (smb_vwv0 + 19)
+#define smb_ntr_DataCount (smb_vwv0 + 23)
+#define smb_ntr_DataOffset (smb_vwv0 + 27)
+#define smb_ntr_DataDisplacement (smb_vwv0 + 31)
+
+/* these are for the NT create_and_X */
+#define smb_ntcreate_NameLength (smb_vwv0 + 5)
+#define smb_ntcreate_Flags (smb_vwv0 + 7)
+#define smb_ntcreate_RootDirectoryFid (smb_vwv0 + 11)
+#define smb_ntcreate_DesiredAccess (smb_vwv0 + 15)
+#define smb_ntcreate_AllocationSize (smb_vwv0 + 19)
+#define smb_ntcreate_FileAttributes (smb_vwv0 + 27)
+#define smb_ntcreate_ShareAccess (smb_vwv0 + 31)
+#define smb_ntcreate_CreateDisposition (smb_vwv0 + 35)
+#define smb_ntcreate_CreateOptions (smb_vwv0 + 39)
+#define smb_ntcreate_ImpersonationLevel (smb_vwv0 + 43)
+#define smb_ntcreate_SecurityFlags (smb_vwv0 + 47)
+
+/* this is used on a TConX. I'm not sure the name is very helpful though */
+#define SMB_SUPPORT_SEARCH_BITS 0x0001
+#define SMB_SHARE_IN_DFS 0x0002
+
+/* Named pipe write mode flags. Used in writeX calls. */
+#define PIPE_RAW_MODE 0x4
+#define PIPE_START_MESSAGE 0x8
+
+/* File Specific access rights */
+#define FILE_READ_DATA 0x00000001
+#define FILE_WRITE_DATA 0x00000002
+#define FILE_APPEND_DATA 0x00000004
+#define FILE_READ_EA 0x00000008 /* File and directory */
+#define FILE_WRITE_EA 0x00000010 /* File and directory */
+#define FILE_EXECUTE 0x00000020
+#define FILE_DELETE_CHILD 0x00000040
+#define FILE_READ_ATTRIBUTES 0x00000080
+#define FILE_WRITE_ATTRIBUTES 0x00000100
+
+#define FILE_ALL_ACCESS 0x000001FF
+
+/* Directory specific access rights */
+#define FILE_LIST_DIRECTORY 0x00000001
+#define FILE_ADD_FILE 0x00000002
+#define FILE_ADD_SUBDIRECTORY 0x00000004
+#define FILE_TRAVERSE 0x00000020
+#define FILE_DELETE_CHILD 0x00000040
+
+/* the desired access to use when opening a pipe */
+#define DESIRED_ACCESS_PIPE 0x2019f
+
+/* Generic access masks & rights. */
+#define DELETE_ACCESS (1L<<16) /* 0x00010000 */
+#define READ_CONTROL_ACCESS (1L<<17) /* 0x00020000 */
+#define WRITE_DAC_ACCESS (1L<<18) /* 0x00040000 */
+#define WRITE_OWNER_ACCESS (1L<<19) /* 0x00080000 */
+#define SYNCHRONIZE_ACCESS (1L<<20) /* 0x00100000 */
+
+#define SYSTEM_SECURITY_ACCESS (1L<<24) /* 0x01000000 */
+#define MAXIMUM_ALLOWED_ACCESS (1L<<25) /* 0x02000000 */
+#define GENERIC_ALL_ACCESS (1<<28) /* 0x10000000 */
+#define GENERIC_EXECUTE_ACCESS (1<<29) /* 0x20000000 */
+#define GENERIC_WRITE_ACCESS (1<<30) /* 0x40000000 */
+#define GENERIC_READ_ACCESS (((unsigned)1)<<31) /* 0x80000000 */
+
+/* Mapping of generic access rights for files to specific rights. */
+
+#define FILE_GENERIC_ALL (STANDARD_RIGHTS_REQUIRED_ACCESS| SYNCHRONIZE_ACCESS|FILE_ALL_ACCESS)
+
+#define FILE_GENERIC_READ (STANDARD_RIGHTS_READ_ACCESS|FILE_READ_DATA|FILE_READ_ATTRIBUTES|\
+ FILE_READ_EA|SYNCHRONIZE_ACCESS)
+
+#define FILE_GENERIC_WRITE (STANDARD_RIGHTS_WRITE_ACCESS|FILE_WRITE_DATA|FILE_WRITE_ATTRIBUTES|\
+ FILE_WRITE_EA|FILE_APPEND_DATA|SYNCHRONIZE_ACCESS)
+
+#define FILE_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE_ACCESS|FILE_READ_ATTRIBUTES|\
+ FILE_EXECUTE|SYNCHRONIZE_ACCESS)
+
+/* Mapping of access rights to UNIX perms. */
+#define UNIX_ACCESS_RWX FILE_GENERIC_ALL
+#define UNIX_ACCESS_R FILE_GENERIC_READ
+#define UNIX_ACCESS_W FILE_GENERIC_WRITE
+#define UNIX_ACCESS_X FILE_GENERIC_EXECUTE
+
+#if 0
+/*
+ * This is the old mapping we used to use. To get W2KSP2 profiles
+ * working we need to map to the canonical file perms.
+ */
+#define UNIX_ACCESS_RWX (UNIX_ACCESS_R|UNIX_ACCESS_W|UNIX_ACCESS_X)
+#define UNIX_ACCESS_R (READ_CONTROL_ACCESS|SYNCHRONIZE_ACCESS|\
+ FILE_READ_ATTRIBUTES|FILE_READ_EA|FILE_READ_DATA)
+#define UNIX_ACCESS_W (READ_CONTROL_ACCESS|SYNCHRONIZE_ACCESS|\
+ FILE_WRITE_ATTRIBUTES|FILE_WRITE_EA|\
+ FILE_APPEND_DATA|FILE_WRITE_DATA)
+#define UNIX_ACCESS_X (READ_CONTROL_ACCESS|SYNCHRONIZE_ACCESS|\
+ FILE_EXECUTE|FILE_READ_ATTRIBUTES)
+#endif
+
+#define UNIX_ACCESS_NONE (WRITE_OWNER_ACCESS)
+
+/* Flags field. */
+#define REQUEST_OPLOCK 2
+#define REQUEST_BATCH_OPLOCK 4
+#define OPEN_DIRECTORY 8
+#define EXTENDED_RESPONSE_REQUIRED 0x10
+
+/* ShareAccess field. */
+#define FILE_SHARE_NONE 0 /* Cannot be used in bitmask. */
+#define FILE_SHARE_READ 1
+#define FILE_SHARE_WRITE 2
+#define FILE_SHARE_DELETE 4
+
+/* FileAttributesField */
+#define FILE_ATTRIBUTE_READONLY 0x001L
+#define FILE_ATTRIBUTE_HIDDEN 0x002L
+#define FILE_ATTRIBUTE_SYSTEM 0x004L
+#define FILE_ATTRIBUTE_DIRECTORY 0x010L
+#define FILE_ATTRIBUTE_ARCHIVE 0x020L
+#define FILE_ATTRIBUTE_NORMAL 0x080L
+#define FILE_ATTRIBUTE_TEMPORARY 0x100L
+#define FILE_ATTRIBUTE_SPARSE 0x200L
+#define FILE_ATTRIBUTE_COMPRESSED 0x800L
+#define FILE_ATTRIBUTE_NONINDEXED 0x2000L
+#define SAMBA_ATTRIBUTES_MASK 0x7F
+
+/* Flags - combined with attributes. */
+#define FILE_FLAG_WRITE_THROUGH 0x80000000L
+#define FILE_FLAG_NO_BUFFERING 0x20000000L
+#define FILE_FLAG_RANDOM_ACCESS 0x10000000L
+#define FILE_FLAG_SEQUENTIAL_SCAN 0x08000000L
+#define FILE_FLAG_DELETE_ON_CLOSE 0x04000000L
+#define FILE_FLAG_BACKUP_SEMANTICS 0x02000000L
+#define FILE_FLAG_POSIX_SEMANTICS 0x01000000L
+
+/* CreateDisposition field. */
+#define FILE_SUPERSEDE 0
+#define FILE_OPEN 1
+#define FILE_CREATE 2
+#define FILE_OPEN_IF 3
+#define FILE_OVERWRITE 4
+#define FILE_OVERWRITE_IF 5
+
+/* CreateOptions field. */
+#define FILE_DIRECTORY_FILE 0x0001
+#define FILE_WRITE_THROUGH 0x0002
+#define FILE_SEQUENTIAL_ONLY 0x0004
+#define FILE_NON_DIRECTORY_FILE 0x0040
+#define FILE_NO_EA_KNOWLEDGE 0x0200
+#define FILE_EIGHT_DOT_THREE_ONLY 0x0400
+#define FILE_RANDOM_ACCESS 0x0800
+#define FILE_DELETE_ON_CLOSE 0x1000
+#define FILE_OPEN_BY_FILE_ID 0x2000
+
+/* Responses when opening a file. */
+#define FILE_WAS_SUPERSEDED 0
+#define FILE_WAS_OPENED 1
+#define FILE_WAS_CREATED 2
+#define FILE_WAS_OVERWRITTEN 3
+
+/* File type flags */
+#define FILE_TYPE_DISK 0
+#define FILE_TYPE_BYTE_MODE_PIPE 1
+#define FILE_TYPE_MESSAGE_MODE_PIPE 2
+#define FILE_TYPE_PRINTER 3
+#define FILE_TYPE_COMM_DEVICE 4
+#define FILE_TYPE_UNKNOWN 0xFFFF
+
+/* Flag for NT transact rename call. */
+#define RENAME_REPLACE_IF_EXISTS 1
+
+/* flags for SMBntrename call (from Samba4) */
+#define RENAME_FLAG_MOVE_CLUSTER_INFORMATION 0x102 /* ???? */
+#define RENAME_FLAG_HARD_LINK 0x103
+#define RENAME_FLAG_RENAME 0x104
+#define RENAME_FLAG_COPY 0x105
+
+/* Filesystem Attributes. */
+#define FILE_CASE_SENSITIVE_SEARCH 0x00000001
+#define FILE_CASE_PRESERVED_NAMES 0x00000002
+#define FILE_UNICODE_ON_DISK 0x00000004
+/* According to cifs9f, this is 4, not 8 */
+/* Acconding to testing, this actually sets the security attribute! */
+#define FILE_PERSISTENT_ACLS 0x00000008
+#define FILE_FILE_COMPRESSION 0x00000010
+#define FILE_VOLUME_QUOTAS 0x00000020
+#define FILE_SUPPORTS_SPARSE_FILES 0x00000040
+#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080
+#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100
+#define FS_LFN_APIS 0x00004000
+#define FILE_VOLUME_IS_COMPRESSED 0x00008000
+#define FILE_SUPPORTS_OBJECT_IDS 0x00010000
+#define FILE_SUPPORTS_ENCRYPTION 0x00020000
+#define FILE_NAMED_STREAMS 0x00040000
+#define FILE_READ_ONLY_VOLUME 0x00080000
+
+/* ChangeNotify flags. */
+#define FILE_NOTIFY_CHANGE_FILE 0x001
+#define FILE_NOTIFY_CHANGE_DIR_NAME 0x002
+#define FILE_NOTIFY_CHANGE_ATTRIBUTES 0x004
+#define FILE_NOTIFY_CHANGE_SIZE 0x008
+#define FILE_NOTIFY_CHANGE_LAST_WRITE 0x010
+#define FILE_NOTIFY_CHANGE_LAST_ACCESS 0x020
+#define FILE_NOTIFY_CHANGE_CREATION 0x040
+#define FILE_NOTIFY_CHANGE_EA 0x080
+#define FILE_NOTIFY_CHANGE_SECURITY 0x100
+#define FILE_NOTIFY_CHANGE_FILE_NAME 0x200
+
+/* where to find the base of the SMB packet proper */
+#define smb_base(buf) (((char *)(buf))+4)
+
+/* we don't allow server strings to be longer than 48 characters as
+ otherwise NT will not honour the announce packets */
+#define MAX_SERVER_STRING_LENGTH 48
+
+
+#define SMB_SUCCESS 0 /* The request was successful. */
+
+#ifdef WITH_DFS
+void dfs_unlogin(void);
+extern int dcelogin_atmost_once;
+#endif
+
+#ifdef NOSTRDUP
+char *strdup(char *s);
+#endif
+
+#ifndef SIGNAL_CAST
+#define SIGNAL_CAST (RETSIGTYPE (*)(int))
+#endif
+
+#ifndef SELECT_CAST
+#define SELECT_CAST
+#endif
+
+/* these are used in NetServerEnum to choose what to receive */
+#define SV_TYPE_WORKSTATION 0x00000001
+#define SV_TYPE_SERVER 0x00000002
+#define SV_TYPE_SQLSERVER 0x00000004
+#define SV_TYPE_DOMAIN_CTRL 0x00000008
+#define SV_TYPE_DOMAIN_BAKCTRL 0x00000010
+#define SV_TYPE_TIME_SOURCE 0x00000020
+#define SV_TYPE_AFP 0x00000040
+#define SV_TYPE_NOVELL 0x00000080
+#define SV_TYPE_DOMAIN_MEMBER 0x00000100
+#define SV_TYPE_PRINTQ_SERVER 0x00000200
+#define SV_TYPE_DIALIN_SERVER 0x00000400
+#define SV_TYPE_SERVER_UNIX 0x00000800
+#define SV_TYPE_NT 0x00001000
+#define SV_TYPE_WFW 0x00002000
+#define SV_TYPE_SERVER_MFPN 0x00004000
+#define SV_TYPE_SERVER_NT 0x00008000
+#define SV_TYPE_POTENTIAL_BROWSER 0x00010000
+#define SV_TYPE_BACKUP_BROWSER 0x00020000
+#define SV_TYPE_MASTER_BROWSER 0x00040000
+#define SV_TYPE_DOMAIN_MASTER 0x00080000
+#define SV_TYPE_SERVER_OSF 0x00100000
+#define SV_TYPE_SERVER_VMS 0x00200000
+#define SV_TYPE_WIN95_PLUS 0x00400000
+#define SV_TYPE_DFS_SERVER 0x00800000
+#define SV_TYPE_ALTERNATE_XPORT 0x20000000
+#define SV_TYPE_LOCAL_LIST_ONLY 0x40000000
+#define SV_TYPE_DOMAIN_ENUM 0x80000000
+#define SV_TYPE_ALL 0xFFFFFFFF
+
+/* This was set by JHT in liaison with Jeremy Allison early 1997
+ * History:
+ * Version 4.0 - never made public
+ * Version 4.10 - New to 1.9.16p2, lost in space 1.9.16p3 to 1.9.16p9
+ * - Reappeared in 1.9.16p11 with fixed smbd services
+ * Version 4.20 - To indicate that nmbd and browsing now works better
+ * Version 4.50 - Set at release of samba-2.2.0 by JHT
+ *
+ * Note: In the presence of NT4.X do not set above 4.9
+ * Setting this above 4.9 can have undesired side-effects.
+ * This may change again in Samba-3.0 after further testing. JHT
+ */
+
+#define DEFAULT_MAJOR_VERSION 0x04
+#define DEFAULT_MINOR_VERSION 0x09
+
+/* Browser Election Values */
+#define BROWSER_ELECTION_VERSION 0x010f
+#define BROWSER_CONSTANT 0xaa55
+
+/* Sercurity mode bits. */
+#define NEGOTIATE_SECURITY_USER_LEVEL 0x01
+#define NEGOTIATE_SECURITY_CHALLENGE_RESPONSE 0x02
+#define NEGOTIATE_SECURITY_SIGNATURES_ENABLED 0x04
+#define NEGOTIATE_SECURITY_SIGNATURES_REQUIRED 0x08
+
+/* NT Flags2 bits - cifs6.txt section 3.1.2 */
+
+#define FLAGS2_LONG_PATH_COMPONENTS 0x0001
+#define FLAGS2_EXTENDED_ATTRIBUTES 0x0002
+#define FLAGS2_SMB_SECURITY_SIGNATURES 0x0004
+#define FLAGS2_IS_LONG_NAME 0x0040
+#define FLAGS2_EXTENDED_SECURITY 0x0800
+#define FLAGS2_DFS_PATHNAMES 0x1000
+#define FLAGS2_READ_PERMIT_NO_EXECUTE 0x2000
+#define FLAGS2_32_BIT_ERROR_CODES 0x4000
+#define FLAGS2_UNICODE_STRINGS 0x8000
+
+#define FLAGS2_WIN2K_SIGNATURE 0xC852 /* Hack alert ! For now... JRA. */
+
+/* Capabilities. see ftp.microsoft.com/developr/drg/cifs/cifs/cifs4.txt */
+
+#define CAP_RAW_MODE 0x0001
+#define CAP_MPX_MODE 0x0002
+#define CAP_UNICODE 0x0004
+#define CAP_LARGE_FILES 0x0008
+#define CAP_NT_SMBS 0x0010
+#define CAP_RPC_REMOTE_APIS 0x0020
+#define CAP_STATUS32 0x0040
+#define CAP_LEVEL_II_OPLOCKS 0x0080
+#define CAP_LOCK_AND_READ 0x0100
+#define CAP_NT_FIND 0x0200
+#define CAP_DFS 0x1000
+#define CAP_W2K_SMBS 0x2000
+#define CAP_LARGE_READX 0x4000
+#define CAP_LARGE_WRITEX 0x8000
+#define CAP_UNIX 0x800000 /* Capabilities for UNIX extensions. Created by HP. */
+#define CAP_EXTENDED_SECURITY 0x80000000
+
+/* protocol types. It assumes that higher protocols include lower protocols
+ as subsets */
+enum protocol_types {PROTOCOL_NONE,PROTOCOL_CORE,PROTOCOL_COREPLUS,PROTOCOL_LANMAN1,PROTOCOL_LANMAN2,PROTOCOL_NT1};
+
+/* security levels */
+enum security_types {SEC_SHARE,SEC_USER,SEC_SERVER,SEC_DOMAIN,SEC_ADS};
+
+/* server roles */
+enum server_types
+{
+ ROLE_STANDALONE,
+ ROLE_DOMAIN_MEMBER,
+ ROLE_DOMAIN_BDC,
+ ROLE_DOMAIN_PDC
+};
+
+/* printing types */
+enum printing_types {PRINT_BSD,PRINT_SYSV,PRINT_AIX,PRINT_HPUX,
+ PRINT_QNX,PRINT_PLP,PRINT_LPRNG,PRINT_SOFTQ,
+ PRINT_CUPS,PRINT_LPRNT,PRINT_LPROS2
+#ifdef DEVELOPER
+,PRINT_TEST,PRINT_VLP
+#endif /* DEVELOPER */
+};
+
+/* LDAP schema types */
+enum schema_types {SCHEMA_COMPAT, SCHEMA_AD, SCHEMA_SAMBA};
+
+/* LDAP SSL options */
+enum ldap_ssl_types {LDAP_SSL_ON, LDAP_SSL_OFF, LDAP_SSL_START_TLS};
+
+/* LDAP PASSWD SYNC methods */
+enum ldap_passwd_sync_types {LDAP_PASSWD_SYNC_ON, LDAP_PASSWD_SYNC_OFF, LDAP_PASSWD_SYNC_ONLY};
+
+/* Remote architectures we know about. */
+enum remote_arch_types {RA_UNKNOWN, RA_WFWG, RA_OS2, RA_WIN95, RA_WINNT, RA_WIN2K, RA_WINXP, RA_WIN2K3, RA_SAMBA};
+
+/* case handling */
+enum case_handling {CASE_LOWER,CASE_UPPER};
+
+/*
+ * Global value meaing that the smb_uid field should be
+ * ingored (in share level security and protocol level == CORE)
+ */
+
+#define UID_FIELD_INVALID 0
+#define VUID_OFFSET 100 /* Amount to bias returned vuid numbers */
+
+/*
+ * Size of buffer to use when moving files across filesystems.
+ */
+#define COPYBUF_SIZE (8*1024)
+
+/*
+ * Values used to override error codes.
+ */
+extern int unix_ERR_class;
+extern int unix_ERR_code;
+extern NTSTATUS unix_ERR_ntstatus;
+
+/*
+ * Used in chaining code.
+ */
+extern int chain_size;
+
+/*
+ * Map the Core and Extended Oplock requesst bits down
+ * to common bits (EXCLUSIVE_OPLOCK & BATCH_OPLOCK).
+ */
+
+/*
+ * Core protocol.
+ */
+#define CORE_OPLOCK_REQUEST(inbuf) \
+ ((CVAL(inbuf,smb_flg)&(FLAG_REQUEST_OPLOCK|FLAG_REQUEST_BATCH_OPLOCK))>>5)
+
+/*
+ * Extended protocol.
+ */
+#define EXTENDED_OPLOCK_REQUEST(inbuf) ((SVAL(inbuf,smb_vwv2)&((1<<1)|(1<<2)))>>1)
+
+/* Lock types. */
+#define LOCKING_ANDX_SHARED_LOCK 0x1
+#define LOCKING_ANDX_OPLOCK_RELEASE 0x2
+#define LOCKING_ANDX_CHANGE_LOCKTYPE 0x4
+#define LOCKING_ANDX_CANCEL_LOCK 0x8
+#define LOCKING_ANDX_LARGE_FILES 0x10
+
+/* Oplock levels */
+#define OPLOCKLEVEL_NONE 0
+#define OPLOCKLEVEL_II 1
+
+/*
+ * Bits we test with.
+ */
+
+#define NO_OPLOCK 0
+#define EXCLUSIVE_OPLOCK 1
+#define BATCH_OPLOCK 2
+#define LEVEL_II_OPLOCK 4
+
+#define EXCLUSIVE_OPLOCK_TYPE(lck) ((lck) & (EXCLUSIVE_OPLOCK|BATCH_OPLOCK))
+#define BATCH_OPLOCK_TYPE(lck) ((lck) & BATCH_OPLOCK)
+#define LEVEL_II_OPLOCK_TYPE(lck) ((lck) & LEVEL_II_OPLOCK)
+
+#define CORE_OPLOCK_GRANTED (1<<5)
+#define EXTENDED_OPLOCK_GRANTED (1<<15)
+
+/*
+ * Return values for oplock types.
+ */
+
+#define NO_OPLOCK_RETURN 0
+#define EXCLUSIVE_OPLOCK_RETURN 1
+#define BATCH_OPLOCK_RETURN 2
+#define LEVEL_II_OPLOCK_RETURN 3
+
+/*
+ * Loopback command offsets.
+ */
+
+#define OPBRK_CMD_LEN_OFFSET 0
+#define OPBRK_CMD_PORT_OFFSET 4
+#define OPBRK_CMD_HEADER_LEN 6
+
+#define OPBRK_MESSAGE_CMD_OFFSET 0
+
+/*
+ * Oplock break command code to send over the udp socket.
+ * The same message is sent for both exlusive and level II breaks.
+ *
+ * The form of this is :
+ *
+ * 0 2 2+pid 2+pid+dev 2+pid+dev+ino
+ * +----+--------+-------+--------+---------+
+ * | cmd| pid | dev | inode | fileid |
+ * +----+--------+-------+--------+---------+
+ */
+
+#define OPLOCK_BREAK_PID_OFFSET 2
+#define OPLOCK_BREAK_DEV_OFFSET (OPLOCK_BREAK_PID_OFFSET + sizeof(pid_t))
+#define OPLOCK_BREAK_INODE_OFFSET (OPLOCK_BREAK_DEV_OFFSET + sizeof(SMB_DEV_T))
+#define OPLOCK_BREAK_FILEID_OFFSET (OPLOCK_BREAK_INODE_OFFSET + sizeof(SMB_INO_T))
+#define OPLOCK_BREAK_MSG_LEN (OPLOCK_BREAK_FILEID_OFFSET + sizeof(unsigned long))
+
+/* Message types */
+#define OPLOCK_BREAK_CMD 0x1
+#define KERNEL_OPLOCK_BREAK_CMD 0x2
+#define LEVEL_II_OPLOCK_BREAK_CMD 0x3
+#define ASYNC_LEVEL_II_OPLOCK_BREAK_CMD 0x4
+
+/*
+ * Capabilities abstracted for different systems.
+ */
+
+#define KERNEL_OPLOCK_CAPABILITY 0x1
+
+/*
+ * Oplock break command code sent via the kernel interface (if it exists).
+ *
+ * Form of this is :
+ *
+ * 0 2 2+devsize 2+devsize+inodesize
+ * +----+--------+--------+----------+
+ * | cmd| dev | inode | fileid |
+ * +----+--------+--------+----------+
+ */
+#define KERNEL_OPLOCK_BREAK_DEV_OFFSET 2
+#define KERNEL_OPLOCK_BREAK_INODE_OFFSET (KERNEL_OPLOCK_BREAK_DEV_OFFSET + sizeof(SMB_DEV_T))
+#define KERNEL_OPLOCK_BREAK_FILEID_OFFSET (KERNEL_OPLOCK_BREAK_INODE_OFFSET + sizeof(SMB_INO_T))
+#define KERNEL_OPLOCK_BREAK_MSG_LEN (KERNEL_OPLOCK_BREAK_FILEID_OFFSET + sizeof(unsigned long))
+
+
+/* if a kernel does support oplocks then a structure of the following
+ typee is used to describe how to interact with the kernel */
+struct kernel_oplocks {
+ BOOL (*receive_message)(fd_set *fds, char *buffer, int buffer_len);
+ BOOL (*set_oplock)(files_struct *fsp, int oplock_type);
+ void (*release_oplock)(files_struct *fsp);
+ BOOL (*parse_message)(char *msg_start, int msg_len, SMB_INO_T *inode, SMB_DEV_T *dev, unsigned long *file_id);
+ BOOL (*msg_waiting)(fd_set *fds);
+ int notification_fd;
+};
+
+
+#define CMD_REPLY 0x8000
+
+/* this structure defines the functions for doing change notify in
+ various implementations */
+struct cnotify_fns {
+ void * (*register_notify)(connection_struct *conn, char *path, uint32 flags);
+ BOOL (*check_notify)(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *data, time_t t);
+ void (*remove_notify)(void *data);
+ int select_time;
+};
+
+
+
+#include "smb_macros.h"
+
+#define MAX_NETBIOSNAME_LEN 16
+/* DOS character, NetBIOS namestring. Type used on the wire. */
+typedef char nstring[MAX_NETBIOSNAME_LEN];
+/* Unix character, NetBIOS namestring. Type used to manipulate name in nmbd. */
+typedef char unstring[MAX_NETBIOSNAME_LEN*4];
+
+/* A netbios name structure. */
+struct nmb_name {
+ nstring name;
+ char scope[64];
+ unsigned int name_type;
+};
+
+
+/* A netbios node status array element. */
+struct node_status {
+ nstring name;
+ unsigned char type;
+ unsigned char flags;
+};
+
+struct pwd_info
+{
+ BOOL null_pwd;
+ BOOL cleartext;
+
+ fstring password;
+
+};
+
+typedef struct user_struct
+{
+ struct user_struct *next, *prev;
+ uint16 vuid; /* Tag for this entry. */
+ uid_t uid; /* uid of a validated user */
+ gid_t gid; /* gid of a validated user */
+
+ userdom_struct user;
+ char *homedir;
+ char *unix_homedir;
+ char *logon_script;
+
+ BOOL guest;
+
+ /* following groups stuff added by ih */
+ /* This groups info is needed for when we become_user() for this uid */
+ int n_groups;
+ gid_t *groups;
+
+ NT_USER_TOKEN *nt_user_token;
+ PRIVILEGE_SET *privs;
+
+ DATA_BLOB session_key;
+
+ char *session_keystr; /* used by utmp and pam session code.
+ TDB key string */
+ int homes_snum;
+
+ struct auth_serversupplied_info *server_info;
+
+} user_struct;
+
+
+struct unix_error_map {
+ int unix_error;
+ int dos_class;
+ int dos_code;
+ NTSTATUS nt_error;
+};
+
+/*
+#include "ntdomain.h"
+
+#include "client.h"
+*/
+
+/*
+ * Size of new password account encoding string. This is enough space to
+ * hold 11 ACB characters, plus the surrounding [] and a terminating null.
+ * Do not change unless you are adding new ACB bits!
+ */
+
+#define NEW_PW_FORMAT_SPACE_PADDED_LEN 14
+
+/*
+ Do you want session setups at user level security with a invalid
+ password to be rejected or allowed in as guest? WinNT rejects them
+ but it can be a pain as it means "net view" needs to use a password
+
+ You have 3 choices in the setting of map_to_guest:
+
+ "NEVER_MAP_TO_GUEST" means session setups with an invalid password
+ are rejected. This is the default.
+
+ "MAP_TO_GUEST_ON_BAD_USER" means session setups with an invalid password
+ are rejected, unless the username does not exist, in which case it
+ is treated as a guest login
+
+ "MAP_TO_GUEST_ON_BAD_PASSWORD" means session setups with an invalid password
+ are treated as a guest login
+
+ Note that map_to_guest only has an effect in user or server
+ level security.
+*/
+
+#define NEVER_MAP_TO_GUEST 0
+#define MAP_TO_GUEST_ON_BAD_USER 1
+#define MAP_TO_GUEST_ON_BAD_PASSWORD 2
+
+#define SAFE_NETBIOS_CHARS ". -_"
+
+/* generic iconv conversion structure */
+typedef struct {
+ size_t (*direct)(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft);
+ size_t (*pull)(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft);
+ size_t (*push)(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft);
+ void *cd_direct, *cd_pull, *cd_push;
+ char *from_name, *to_name;
+} *smb_iconv_t;
+
+/* The maximum length of a trust account password.
+ Used when we randomly create it, 15 char passwords
+ exceed NT4's max password length */
+
+#define DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH 14
+
+#include "popt_common.h"
+
+#define PORT_NONE 0
+#ifndef LDAP_PORT
+#define LDAP_PORT 389
+#endif
+
+/* used by the IP comparison function */
+struct ip_service {
+ struct in_addr ip;
+ unsigned port;
+};
+
+/* Used by the SMB signing functions. */
+
+typedef struct smb_sign_info {
+ void (*sign_outgoing_message)(char *outbuf, struct smb_sign_info *si);
+ BOOL (*check_incoming_message)(char *inbuf, struct smb_sign_info *si, BOOL expected_ok);
+ void (*free_signing_context)(struct smb_sign_info *si);
+ void *signing_context;
+
+ BOOL negotiated_smb_signing;
+ BOOL allow_smb_signing;
+ BOOL doing_signing;
+ BOOL mandatory_signing;
+ BOOL seen_valid; /* Have I ever seen a validly signed packet? */
+} smb_sign_info;
+
+struct ea_struct {
+ uint8 flags;
+ char *name;
+ DATA_BLOB value;
+};
+
+/* EA names used internally in Samba. KEEP UP TO DATE with prohibited_ea_names in trans2.c !. */
+#define SAMBA_POSIX_INHERITANCE_EA_NAME "user.SAMBA_PAI"
+/* EA to use for DOS attributes */
+#define SAMBA_XATTR_DOS_ATTRIB "user.DOSATTRIB"
+
+#endif /* _SMB_H */
diff --git a/source/include/smb_acls.h b/source/include/smb_acls.h
new file mode 100644
index 00000000000..2bde6caeda1
--- /dev/null
+++ b/source/include/smb_acls.h
@@ -0,0 +1,275 @@
+/*
+ Unix SMB/CIFS implementation.
+ Portable SMB ACL interface
+ Copyright (C) Jeremy Allison 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _SMB_ACLS_H
+#define _SMB_ACLS_H
+#if defined(HAVE_POSIX_ACLS)
+
+/* This is an identity mapping (just remove the SMB_). */
+
+#define SMB_ACL_TAG_T acl_tag_t
+#define SMB_ACL_TYPE_T acl_type_t
+#define SMB_ACL_PERMSET_T acl_permset_t
+#define SMB_ACL_PERM_T acl_perm_t
+#define SMB_ACL_READ ACL_READ
+#define SMB_ACL_WRITE ACL_WRITE
+#define SMB_ACL_EXECUTE ACL_EXECUTE
+
+/* Types of ACLs. */
+#define SMB_ACL_USER ACL_USER
+#define SMB_ACL_USER_OBJ ACL_USER_OBJ
+#define SMB_ACL_GROUP ACL_GROUP
+#define SMB_ACL_GROUP_OBJ ACL_GROUP_OBJ
+#define SMB_ACL_OTHER ACL_OTHER
+#define SMB_ACL_MASK ACL_MASK
+
+#define SMB_ACL_T acl_t
+
+#define SMB_ACL_ENTRY_T acl_entry_t
+
+#define SMB_ACL_FIRST_ENTRY ACL_FIRST_ENTRY
+#define SMB_ACL_NEXT_ENTRY ACL_NEXT_ENTRY
+
+#define SMB_ACL_TYPE_ACCESS ACL_TYPE_ACCESS
+#define SMB_ACL_TYPE_DEFAULT ACL_TYPE_DEFAULT
+
+#elif defined(HAVE_TRU64_ACLS)
+
+/* This is for DEC/Compaq Tru64 UNIX */
+
+#define SMB_ACL_TAG_T acl_tag_t
+#define SMB_ACL_TYPE_T acl_type_t
+#define SMB_ACL_PERMSET_T acl_permset_t
+#define SMB_ACL_PERM_T acl_perm_t
+#define SMB_ACL_READ ACL_READ
+#define SMB_ACL_WRITE ACL_WRITE
+#define SMB_ACL_EXECUTE ACL_EXECUTE
+
+/* Types of ACLs. */
+#define SMB_ACL_USER ACL_USER
+#define SMB_ACL_USER_OBJ ACL_USER_OBJ
+#define SMB_ACL_GROUP ACL_GROUP
+#define SMB_ACL_GROUP_OBJ ACL_GROUP_OBJ
+#define SMB_ACL_OTHER ACL_OTHER
+#define SMB_ACL_MASK ACL_MASK
+
+#define SMB_ACL_T acl_t
+
+#define SMB_ACL_ENTRY_T acl_entry_t
+
+#define SMB_ACL_FIRST_ENTRY 0
+#define SMB_ACL_NEXT_ENTRY 1
+
+#define SMB_ACL_TYPE_ACCESS ACL_TYPE_ACCESS
+#define SMB_ACL_TYPE_DEFAULT ACL_TYPE_DEFAULT
+
+#elif defined(HAVE_UNIXWARE_ACLS) || defined(HAVE_SOLARIS_ACLS)
+/*
+ * Donated by Michael Davidson <md@sco.COM> for UnixWare / OpenUNIX.
+ * Modified by Toomas Soome <tsoome@ut.ee> for Solaris.
+ */
+
+/* SVR4.2 ES/MP ACLs */
+typedef int SMB_ACL_TAG_T;
+typedef int SMB_ACL_TYPE_T;
+typedef ushort *SMB_ACL_PERMSET_T;
+typedef ushort SMB_ACL_PERM_T;
+#define SMB_ACL_READ 4
+#define SMB_ACL_WRITE 2
+#define SMB_ACL_EXECUTE 1
+
+/* Types of ACLs. */
+#define SMB_ACL_USER USER
+#define SMB_ACL_USER_OBJ USER_OBJ
+#define SMB_ACL_GROUP GROUP
+#define SMB_ACL_GROUP_OBJ GROUP_OBJ
+#define SMB_ACL_OTHER OTHER_OBJ
+#define SMB_ACL_MASK CLASS_OBJ
+
+typedef struct SMB_ACL_T {
+ int size;
+ int count;
+ int next;
+ struct acl acl[1];
+} *SMB_ACL_T;
+
+typedef struct acl *SMB_ACL_ENTRY_T;
+
+#define SMB_ACL_FIRST_ENTRY 0
+#define SMB_ACL_NEXT_ENTRY 1
+
+#define SMB_ACL_TYPE_ACCESS 0
+#define SMB_ACL_TYPE_DEFAULT 1
+
+#elif defined(HAVE_HPUX_ACLS)
+
+/*
+ * Based on the Solaris & UnixWare code.
+ */
+
+#undef GROUP
+#include <sys/aclv.h>
+
+/* SVR4.2 ES/MP ACLs */
+typedef int SMB_ACL_TAG_T;
+typedef int SMB_ACL_TYPE_T;
+typedef ushort *SMB_ACL_PERMSET_T;
+typedef ushort SMB_ACL_PERM_T;
+#define SMB_ACL_READ 4
+#define SMB_ACL_WRITE 2
+#define SMB_ACL_EXECUTE 1
+
+/* Types of ACLs. */
+#define SMB_ACL_USER USER
+#define SMB_ACL_USER_OBJ USER_OBJ
+#define SMB_ACL_GROUP GROUP
+#define SMB_ACL_GROUP_OBJ GROUP_OBJ
+#define SMB_ACL_OTHER OTHER_OBJ
+#define SMB_ACL_MASK CLASS_OBJ
+
+typedef struct SMB_ACL_T {
+ int size;
+ int count;
+ int next;
+ struct acl acl[1];
+} *SMB_ACL_T;
+
+typedef struct acl *SMB_ACL_ENTRY_T;
+
+#define SMB_ACL_FIRST_ENTRY 0
+#define SMB_ACL_NEXT_ENTRY 1
+
+#define SMB_ACL_TYPE_ACCESS 0
+#define SMB_ACL_TYPE_DEFAULT 1
+
+#elif defined(HAVE_IRIX_ACLS)
+
+#define SMB_ACL_TAG_T acl_tag_t
+#define SMB_ACL_TYPE_T acl_type_t
+#define SMB_ACL_PERMSET_T acl_permset_t
+#define SMB_ACL_PERM_T acl_perm_t
+#define SMB_ACL_READ ACL_READ
+#define SMB_ACL_WRITE ACL_WRITE
+#define SMB_ACL_EXECUTE ACL_EXECUTE
+
+/* Types of ACLs. */
+#define SMB_ACL_USER ACL_USER
+#define SMB_ACL_USER_OBJ ACL_USER_OBJ
+#define SMB_ACL_GROUP ACL_GROUP
+#define SMB_ACL_GROUP_OBJ ACL_GROUP_OBJ
+#define SMB_ACL_OTHER ACL_OTHER_OBJ
+#define SMB_ACL_MASK ACL_MASK
+
+typedef struct SMB_ACL_T {
+ int next;
+ BOOL freeaclp;
+ struct acl *aclp;
+} *SMB_ACL_T;
+
+#define SMB_ACL_ENTRY_T acl_entry_t
+
+#define SMB_ACL_FIRST_ENTRY 0
+#define SMB_ACL_NEXT_ENTRY 1
+
+#define SMB_ACL_TYPE_ACCESS ACL_TYPE_ACCESS
+#define SMB_ACL_TYPE_DEFAULT ACL_TYPE_DEFAULT
+
+#elif defined(HAVE_AIX_ACLS)
+
+/* Donated by Medha Date, mdate@austin.ibm.com, for IBM */
+
+#include <acl.h>
+
+typedef uint *SMB_ACL_PERMSET_T;
+
+struct acl_entry_link{
+ struct acl_entry_link *prevp;
+ struct new_acl_entry *entryp;
+ struct acl_entry_link *nextp;
+ int count;
+};
+
+struct new_acl_entry{
+ unsigned short ace_len;
+ unsigned short ace_type;
+ unsigned int ace_access;
+ struct ace_id ace_id[1];
+};
+
+#define SMB_ACL_ENTRY_T struct new_acl_entry*
+#define SMB_ACL_T struct acl_entry_link*
+
+#define SMB_ACL_TAG_T unsigned short
+#define SMB_ACL_TYPE_T int
+#define SMB_ACL_PERM_T uint
+#define SMB_ACL_READ S_IRUSR
+#define SMB_ACL_WRITE S_IWUSR
+#define SMB_ACL_EXECUTE S_IXUSR
+
+/* Types of ACLs. */
+#define SMB_ACL_USER ACEID_USER
+#define SMB_ACL_USER_OBJ 3
+#define SMB_ACL_GROUP ACEID_GROUP
+#define SMB_ACL_GROUP_OBJ 4
+#define SMB_ACL_OTHER 5
+#define SMB_ACL_MASK 6
+
+
+#define SMB_ACL_FIRST_ENTRY 1
+#define SMB_ACL_NEXT_ENTRY 2
+
+#define SMB_ACL_TYPE_ACCESS 0
+#define SMB_ACL_TYPE_DEFAULT 1
+
+#else /* No ACLs. */
+
+/* No ACLS - fake it. */
+#define SMB_ACL_TAG_T int
+#define SMB_ACL_TYPE_T int
+#define SMB_ACL_PERMSET_T mode_t
+#define SMB_ACL_PERM_T mode_t
+#define SMB_ACL_READ S_IRUSR
+#define SMB_ACL_WRITE S_IWUSR
+#define SMB_ACL_EXECUTE S_IXUSR
+
+/* Types of ACLs. */
+#define SMB_ACL_USER 0
+#define SMB_ACL_USER_OBJ 1
+#define SMB_ACL_GROUP 2
+#define SMB_ACL_GROUP_OBJ 3
+#define SMB_ACL_OTHER 4
+#define SMB_ACL_MASK 5
+
+typedef struct SMB_ACL_T {
+ int dummy;
+} *SMB_ACL_T;
+
+typedef struct SMB_ACL_ENTRY_T {
+ int dummy;
+} *SMB_ACL_ENTRY_T;
+
+#define SMB_ACL_FIRST_ENTRY 0
+#define SMB_ACL_NEXT_ENTRY 1
+
+#define SMB_ACL_TYPE_ACCESS 0
+#define SMB_ACL_TYPE_DEFAULT 1
+
+#endif /* No ACLs. */
+#endif /* _SMB_ACLS_H */
diff --git a/source/include/smb_macros.h b/source/include/smb_macros.h
new file mode 100644
index 00000000000..c3bdba30b15
--- /dev/null
+++ b/source/include/smb_macros.h
@@ -0,0 +1,264 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-1999
+ Copyright (C) John H Terpstra 1996-1999
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1999
+ Copyright (C) Paul Ashton 1998 - 1999
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _SMB_MACROS_H
+#define _SMB_MACROS_H
+
+/* Misc bit macros */
+#define BOOLSTR(b) ((b) ? "Yes" : "No")
+#define BITSETB(ptr,bit) ((((char *)ptr)[0] & (1<<(bit)))!=0)
+#define BITSETW(ptr,bit) ((SVAL(ptr,0) & (1<<(bit)))!=0)
+
+/* for readability... */
+#define IS_DOS_READONLY(test_mode) (((test_mode) & aRONLY) != 0)
+#define IS_DOS_DIR(test_mode) (((test_mode) & aDIR) != 0)
+#define IS_DOS_ARCHIVE(test_mode) (((test_mode) & aARCH) != 0)
+#define IS_DOS_SYSTEM(test_mode) (((test_mode) & aSYSTEM) != 0)
+#define IS_DOS_HIDDEN(test_mode) (((test_mode) & aHIDDEN) != 0)
+
+#ifndef SAFE_FREE /* Oh no this is also defined in tdb.h */
+
+/**
+ * Free memory if the pointer and zero the pointer.
+ *
+ * @note You are explicitly allowed to pass NULL pointers -- they will
+ * always be ignored.
+ **/
+#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
+#endif
+
+/* zero a structure */
+#define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x))
+
+/* zero a structure given a pointer to the structure */
+#define ZERO_STRUCTP(x) do { if ((x) != NULL) memset((char *)(x), 0, sizeof(*(x))); } while(0)
+
+/* zero a structure given a pointer to the structure - no zero check */
+#define ZERO_STRUCTPN(x) memset((char *)(x), 0, sizeof(*(x)))
+
+/* zero an array - note that sizeof(array) must work - ie. it must not be a
+ pointer */
+#define ZERO_ARRAY(x) memset((char *)(x), 0, sizeof(x))
+
+/* pointer difference macro */
+#define PTR_DIFF(p1,p2) ((ptrdiff_t)(((const char *)(p1)) - (const char *)(p2)))
+
+/* work out how many elements there are in a static array */
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+
+/* assert macros */
+#define SMB_ASSERT(b) ((b)?(void)0: \
+ (DEBUG(0,("PANIC: assert failed at %s(%d)\n", \
+ __FILE__, __LINE__)), smb_panic("assert failed")))
+#define SMB_ASSERT_ARRAY(a,n) SMB_ASSERT((sizeof(a)/sizeof((a)[0])) >= (n))
+
+/* these are useful macros for checking validity of handles */
+#define OPEN_FSP(fsp) ((fsp) && !(fsp)->is_directory)
+#define OPEN_CONN(conn) ((conn) && (conn)->open)
+#define IS_IPC(conn) ((conn) && (conn)->ipc)
+#define IS_PRINT(conn) ((conn) && (conn)->printer)
+#define FSP_BELONGS_CONN(fsp,conn) do {\
+ extern struct current_user current_user;\
+ if (!((fsp) && (conn) && ((conn)==(fsp)->conn) && (current_user.vuid==(fsp)->vuid))) \
+ return(ERROR_DOS(ERRDOS,ERRbadfid));\
+ } while(0)
+
+#define FNUM_OK(fsp,c) (OPEN_FSP(fsp) && (c)==(fsp)->conn && current_user.vuid==(fsp)->vuid)
+
+#define CHECK_FSP(fsp,conn) do {\
+ extern struct current_user current_user;\
+ if (!FNUM_OK(fsp,conn)) \
+ return(ERROR_DOS(ERRDOS,ERRbadfid)); \
+ else if((fsp)->fd == -1) \
+ return(ERROR_DOS(ERRDOS,ERRbadaccess));\
+ } while(0)
+
+#define CHECK_READ(fsp) if (!(fsp)->can_read) \
+ return(ERROR_DOS(ERRDOS,ERRbadaccess))
+#define CHECK_WRITE(fsp) if (!(fsp)->can_write) \
+ return(ERROR_DOS(ERRDOS,ERRbadaccess))
+
+#define CHECK_ERROR(fsp) if (HAS_CACHED_ERROR(fsp)) \
+ return(CACHED_ERROR(fsp))
+
+#define ERROR_WAS_LOCK_DENIED(status) (NT_STATUS_EQUAL((status), NT_STATUS_LOCK_NOT_GRANTED) || \
+ NT_STATUS_EQUAL((status), NT_STATUS_FILE_LOCK_CONFLICT) )
+
+/* the service number for the [globals] defaults */
+#define GLOBAL_SECTION_SNUM (-1)
+/* translates a connection number into a service number */
+#define SNUM(conn) ((conn)?(conn)->service:GLOBAL_SECTION_SNUM)
+
+
+/* access various service details */
+#define SERVICE(snum) (lp_servicename(snum))
+#define PRINTERNAME(snum) (lp_printername(snum))
+#define CAN_WRITE(conn) (!conn->read_only)
+#define VALID_SNUM(snum) (lp_snum_ok(snum))
+#define GUEST_OK(snum) (VALID_SNUM(snum) && lp_guest_ok(snum))
+#define GUEST_ONLY(snum) (VALID_SNUM(snum) && lp_guest_only(snum))
+#define CAN_SETDIR(snum) (!lp_no_set_dir(snum))
+#define CAN_PRINT(conn) ((conn) && lp_print_ok((conn)->service))
+#define MAP_HIDDEN(conn) ((conn) && lp_map_hidden((conn)->service))
+#define MAP_SYSTEM(conn) ((conn) && lp_map_system((conn)->service))
+#define MAP_ARCHIVE(conn) ((conn) && lp_map_archive((conn)->service))
+#define IS_HIDDEN_PATH(conn,path) ((conn) && is_in_path((path),(conn)->hide_list))
+#define IS_VETO_PATH(conn,path) ((conn) && is_in_path((path),(conn)->veto_list))
+#define IS_VETO_OPLOCK_PATH(conn,path) ((conn) && is_in_path((path),(conn)->veto_oplock_list))
+
+/*
+ * Used by the stat cache code to check if a returned
+ * stat structure is valid.
+ */
+
+#define VALID_STAT(st) ((st).st_nlink != 0)
+#define VALID_STAT_OF_DIR(st) (VALID_STAT(st) && S_ISDIR((st).st_mode))
+
+#ifndef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+#ifndef MAX
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#endif
+
+#ifndef ABS
+#define ABS(a) ((a)>0?(a):(-(a)))
+#endif
+
+/* Macros to get at offsets within smb_lkrng and smb_unlkrng
+ structures. We cannot define these as actual structures
+ due to possible differences in structure packing
+ on different machines/compilers. */
+
+#define SMB_LPID_OFFSET(indx) (10 * (indx))
+#define SMB_LKOFF_OFFSET(indx) ( 2 + (10 * (indx)))
+#define SMB_LKLEN_OFFSET(indx) ( 6 + (10 * (indx)))
+#define SMB_LARGE_LPID_OFFSET(indx) (20 * (indx))
+#define SMB_LARGE_LKOFF_OFFSET_HIGH(indx) (4 + (20 * (indx)))
+#define SMB_LARGE_LKOFF_OFFSET_LOW(indx) (8 + (20 * (indx)))
+#define SMB_LARGE_LKLEN_OFFSET_HIGH(indx) (12 + (20 * (indx)))
+#define SMB_LARGE_LKLEN_OFFSET_LOW(indx) (16 + (20 * (indx)))
+
+/* Macro to cache an error in a write_bmpx_struct */
+#define CACHE_ERROR(w,c,e) ((w)->wr_errclass = (c), (w)->wr_error = (e), \
+ w->wr_discard = True, -1)
+/* Macro to test if an error has been cached for this fnum */
+#define HAS_CACHED_ERROR(fsp) ((fsp)->wbmpx_ptr && \
+ (fsp)->wbmpx_ptr->wr_discard)
+/* Macro to turn the cached error into an error packet */
+#define CACHED_ERROR(fsp) cached_error_packet(outbuf,fsp,__LINE__,__FILE__)
+
+/* these are the datagram types */
+#define DGRAM_DIRECT_UNIQUE 0x10
+
+#define ERROR_DOS(class,code) error_packet(outbuf,NT_STATUS_OK,class,code,__LINE__,__FILE__)
+#define ERROR_NT(status) error_packet(outbuf,status,0,0,__LINE__,__FILE__)
+#define ERROR_BOTH(status,class,code) error_packet(outbuf,status,class,code,__LINE__,__FILE__)
+
+/* this is how errors are generated */
+#define UNIXERROR(defclass,deferror) unix_error_packet(outbuf,defclass,deferror,__LINE__,__FILE__)
+
+#define SMB_ROUNDUP(x,r) ( ((x)%(r)) ? ( (((x)+(r))/(r))*(r) ) : (x))
+
+/* Extra macros added by Ying Chen at IBM - speed increase by inlining. */
+#define smb_buf(buf) (((char *)(buf)) + smb_size + CVAL(buf,smb_wct)*2)
+#define smb_buflen(buf) (SVAL(buf,smb_vwv0 + (int)CVAL(buf, smb_wct)*2))
+
+/* the remaining number of bytes in smb buffer 'buf' from pointer 'p'. */
+#define smb_bufrem(buf, p) (smb_buflen(buf)-PTR_DIFF(p, smb_buf(buf)))
+
+/* Note that chain_size must be available as an extern int to this macro. */
+#define smb_offset(p,buf) (PTR_DIFF(p,buf+4) + chain_size)
+
+#define smb_len(buf) (PVAL(buf,3)|(PVAL(buf,2)<<8)|((PVAL(buf,1)&1)<<16))
+#define _smb_setlen(buf,len) do { buf[0] = 0; buf[1] = (len&0x10000)>>16; \
+ buf[2] = (len&0xFF00)>>8; buf[3] = len&0xFF; } while (0)
+
+/*******************************************************************
+find the difference in milliseconds between two struct timeval
+values
+********************************************************************/
+
+#define TvalDiff(tvalold,tvalnew) \
+ (((tvalnew)->tv_sec - (tvalold)->tv_sec)*1000 + \
+ ((int)(tvalnew)->tv_usec - (int)(tvalold)->tv_usec)/1000)
+
+/****************************************************************************
+true if two IP addresses are equal
+****************************************************************************/
+
+#define ip_equal(ip1,ip2) ((ip1).s_addr == (ip2).s_addr)
+#define ip_service_equal(ip1,ip2) ( ((ip1).ip.s_addr == (ip2).ip.s_addr) && ((ip1).port == (ip2).port) )
+
+/*****************************************************************
+ splits out the last subkey of a key
+ *****************************************************************/
+
+#define reg_get_subkey(full_keyname, key_name, subkey_name) \
+ split_at_last_component(full_keyname, key_name, '\\', subkey_name)
+
+/****************************************************************************
+ Used by dptr_zero.
+****************************************************************************/
+
+#define DPTR_MASK ((uint32)(((uint32)1)<<31))
+
+/****************************************************************************
+ Return True if the offset is at zero.
+****************************************************************************/
+
+#define dptr_zero(buf) ((IVAL(buf,1)&~DPTR_MASK) == 0)
+
+/*******************************************************************
+copy an IP address from one buffer to another
+********************************************************************/
+
+#define putip(dest,src) memcpy(dest,src,4)
+
+/*******************************************************************
+ Return True if a server has CIFS UNIX capabilities.
+********************************************************************/
+
+#define SERVER_HAS_UNIX_CIFS(c) ((c)->capabilities & CAP_UNIX)
+
+/****************************************************************************
+ Make a filename into unix format.
+****************************************************************************/
+
+#define IS_DIRECTORY_SEP(c) ((c) == '\\' || (c) == '/')
+#define unix_format(fname) string_replace(fname,'\\','/')
+#define unix_format_w(fname) string_replace_w(fname, UCS2_CHAR('\\'), UCS2_CHAR('/'))
+
+/****************************************************************************
+ Make a file into DOS format.
+****************************************************************************/
+
+#define dos_format(fname) string_replace(fname,'/','\\')
+
+/*****************************************************************************
+ Check to see if we are a DO for this domain
+*****************************************************************************/
+
+#define IS_DC (lp_server_role()==ROLE_DOMAIN_PDC || lp_server_role()==ROLE_DOMAIN_BDC)
+
+#endif /* _SMB_MACROS_H */
diff --git a/source/include/smbldap.h b/source/include/smbldap.h
new file mode 100644
index 00000000000..119479f218d
--- /dev/null
+++ b/source/include/smbldap.h
@@ -0,0 +1,159 @@
+/*
+ Unix SMB/CIFS mplementation.
+ LDAP protocol helper functions for SAMBA
+ Copyright (C) Gerald Carter 2001-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef _SMBLDAP_H
+#define _SMBLDAP_H
+
+#ifdef HAVE_LDAP
+
+/* specify schema versions between 2.2. and 3.0 */
+
+#define SCHEMAVER_SAMBAACCOUNT 1
+#define SCHEMAVER_SAMBASAMACCOUNT 2
+
+/* objectclass names */
+
+#define LDAP_OBJ_SAMBASAMACCOUNT "sambaSamAccount"
+#define LDAP_OBJ_SAMBAACCOUNT "sambaAccount"
+#define LDAP_OBJ_GROUPMAP "sambaGroupMapping"
+#define LDAP_OBJ_DOMINFO "sambaDomain"
+#define LDAP_OBJ_IDPOOL "sambaUnixIdPool"
+#define LDAP_OBJ_IDMAP_ENTRY "sambaIdmapEntry"
+#define LDAP_OBJ_SID_ENTRY "sambaSidEntry"
+#define LDAP_OBJ_PRIVILEGE "sambaPrivilege"
+
+#define LDAP_OBJ_ACCOUNT "account"
+#define LDAP_OBJ_POSIXACCOUNT "posixAccount"
+#define LDAP_OBJ_POSIXGROUP "posixGroup"
+#define LDAP_OBJ_OU "organizationalUnit"
+
+/* some generic attributes that get reused a lot */
+
+#define LDAP_ATTRIBUTE_SID "sambaSID"
+#define LDAP_ATTRIBUTE_UIDNUMBER "uidNumber"
+#define LDAP_ATTRIBUTE_GIDNUMBER "gidNumber"
+#define LDAP_ATTRIBUTE_SID_LIST "sambaSIDList"
+
+/* attribute map table indexes */
+
+#define LDAP_ATTR_LIST_END 0
+#define LDAP_ATTR_UID 1
+#define LDAP_ATTR_UIDNUMBER 2
+#define LDAP_ATTR_GIDNUMBER 3
+#define LDAP_ATTR_UNIX_HOME 4
+#define LDAP_ATTR_PWD_LAST_SET 5
+#define LDAP_ATTR_PWD_CAN_CHANGE 6
+#define LDAP_ATTR_PWD_MUST_CHANGE 7
+#define LDAP_ATTR_LOGON_TIME 8
+#define LDAP_ATTR_LOGOFF_TIME 9
+#define LDAP_ATTR_KICKOFF_TIME 10
+#define LDAP_ATTR_CN 11
+#define LDAP_ATTR_DISPLAY_NAME 12
+#define LDAP_ATTR_HOME_PATH 13
+#define LDAP_ATTR_LOGON_SCRIPT 14
+#define LDAP_ATTR_PROFILE_PATH 15
+#define LDAP_ATTR_DESC 16
+#define LDAP_ATTR_USER_WKS 17
+#define LDAP_ATTR_USER_SID 18
+#define LDAP_ATTR_USER_RID 18
+#define LDAP_ATTR_PRIMARY_GROUP_SID 19
+#define LDAP_ATTR_PRIMARY_GROUP_RID 20
+#define LDAP_ATTR_LMPW 21
+#define LDAP_ATTR_NTPW 22
+#define LDAP_ATTR_DOMAIN 23
+#define LDAP_ATTR_OBJCLASS 24
+#define LDAP_ATTR_ACB_INFO 25
+#define LDAP_ATTR_NEXT_USERRID 26
+#define LDAP_ATTR_NEXT_GROUPRID 27
+#define LDAP_ATTR_DOM_SID 28
+#define LDAP_ATTR_HOME_DRIVE 29
+#define LDAP_ATTR_GROUP_SID 30
+#define LDAP_ATTR_GROUP_TYPE 31
+#define LDAP_ATTR_SID 32
+#define LDAP_ATTR_ALGORITHMIC_RID_BASE 33
+#define LDAP_ATTR_NEXT_RID 34
+#define LDAP_ATTR_BAD_PASSWORD_COUNT 35
+#define LDAP_ATTR_LOGON_COUNT 36
+#define LDAP_ATTR_MUNGED_DIAL 37
+#define LDAP_ATTR_BAD_PASSWORD_TIME 38
+#define LDAP_ATTR_MOD_TIMESTAMP 39
+#define LDAP_ATTR_SID_LIST 40
+
+typedef struct _attrib_map_entry {
+ int attrib;
+ const char *name;
+} ATTRIB_MAP_ENTRY;
+
+
+/* structures */
+
+extern ATTRIB_MAP_ENTRY attrib_map_v22[];
+extern ATTRIB_MAP_ENTRY attrib_map_v30[];
+extern ATTRIB_MAP_ENTRY dominfo_attr_list[];
+extern ATTRIB_MAP_ENTRY groupmap_attr_list[];
+extern ATTRIB_MAP_ENTRY privilege_attr_list[];
+extern ATTRIB_MAP_ENTRY groupmap_attr_list_to_delete[];
+extern ATTRIB_MAP_ENTRY idpool_attr_list[];
+extern ATTRIB_MAP_ENTRY sidmap_attr_list[];
+
+/* Function declarations -- not included in proto.h so we don't
+ have to worry about LDAP structure types */
+
+const char* get_attr_key2string( ATTRIB_MAP_ENTRY table[], int key );
+char** get_attr_list( ATTRIB_MAP_ENTRY table[] );
+void free_attr_list( char **list );
+void smbldap_set_mod (LDAPMod *** modlist, int modop, const char *attribute, const char *value);
+void smbldap_make_mod(LDAP *ldap_struct, LDAPMessage *existing,
+ LDAPMod ***mods,
+ const char *attribute, const char *newval);
+BOOL smbldap_get_single_attribute (LDAP * ldap_struct, LDAPMessage * entry,
+ const char *attribute, char *value,
+ int max_len);
+BOOL smbldap_get_single_pstring (LDAP * ldap_struct, LDAPMessage * entry,
+ const char *attribute, pstring value);
+
+/**
+ * Struct to keep the state for all the ldap stuff
+ *
+ */
+
+struct smbldap_state {
+ LDAP *ldap_struct;
+ time_t last_ping;
+ /* retrive-once info */
+ const char *uri;
+ char *bind_dn;
+ char *bind_secret;
+
+ unsigned int num_failures;
+
+ time_t last_use;
+ smb_event_id_t event_id;
+
+ struct timeval last_rebind;
+};
+
+#endif /* HAVE_LDAP */
+
+struct smbldap_state;
+
+#endif /* _SMBLDAP_H */
+
diff --git a/source/include/smbprofile.h b/source/include/smbprofile.h
new file mode 100644
index 00000000000..e494faf7da6
--- /dev/null
+++ b/source/include/smbprofile.h
@@ -0,0 +1,485 @@
+#ifndef _PROFILE_H_
+#define _PROFILE_H_
+/*
+ Unix SMB/CIFS implementation.
+ store smbd profiling information in shared memory
+ Copyright (C) Andrew Tridgell 1999
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/*
+ * Reasons for cache flush.
+ */
+
+#define NUM_FLUSH_REASONS 8 /* Keep this in sync with the enum below. */
+enum flush_reason_enum { SEEK_FLUSH, READ_FLUSH, WRITE_FLUSH, READRAW_FLUSH,
+ OPLOCK_RELEASE_FLUSH, CLOSE_FLUSH, SYNC_FLUSH, SIZECHANGE_FLUSH };
+
+/* this file defines the profile structure in the profile shared
+ memory area */
+
+#define PROF_SHMEM_KEY ((key_t)0x07021999)
+#define PROF_SHM_MAGIC 0x6349985
+#define PROF_SHM_VERSION 9
+
+/* time values in the following structure are in microseconds */
+
+struct profile_stats {
+/* general counters */
+ unsigned smb_count; /* how many SMB packets we have processed */
+ unsigned uid_changes; /* how many times we change our effective uid */
+/* system call counters */
+ unsigned syscall_opendir_count;
+ unsigned syscall_opendir_time;
+ unsigned syscall_readdir_count;
+ unsigned syscall_readdir_time;
+ unsigned syscall_mkdir_count;
+ unsigned syscall_mkdir_time;
+ unsigned syscall_rmdir_count;
+ unsigned syscall_rmdir_time;
+ unsigned syscall_closedir_count;
+ unsigned syscall_closedir_time;
+ unsigned syscall_open_count;
+ unsigned syscall_open_time;
+ unsigned syscall_close_count;
+ unsigned syscall_close_time;
+ unsigned syscall_read_count;
+ unsigned syscall_read_time;
+ unsigned syscall_read_bytes; /* bytes read with read syscall */
+ unsigned syscall_pread_count;
+ unsigned syscall_pread_time;
+ unsigned syscall_pread_bytes; /* bytes read with pread syscall */
+ unsigned syscall_write_count;
+ unsigned syscall_write_time;
+ unsigned syscall_write_bytes; /* bytes written with write syscall */
+ unsigned syscall_pwrite_count;
+ unsigned syscall_pwrite_time;
+ unsigned syscall_pwrite_bytes; /* bytes written with pwrite syscall */
+ unsigned syscall_lseek_count;
+ unsigned syscall_lseek_time;
+ unsigned syscall_sendfile_count;
+ unsigned syscall_sendfile_time;
+ unsigned syscall_sendfile_bytes; /* bytes read with sendfile syscall */
+ unsigned syscall_rename_count;
+ unsigned syscall_rename_time;
+ unsigned syscall_fsync_count;
+ unsigned syscall_fsync_time;
+ unsigned syscall_stat_count;
+ unsigned syscall_stat_time;
+ unsigned syscall_fstat_count;
+ unsigned syscall_fstat_time;
+ unsigned syscall_lstat_count;
+ unsigned syscall_lstat_time;
+ unsigned syscall_unlink_count;
+ unsigned syscall_unlink_time;
+ unsigned syscall_chmod_count;
+ unsigned syscall_chmod_time;
+ unsigned syscall_fchmod_count;
+ unsigned syscall_fchmod_time;
+ unsigned syscall_chown_count;
+ unsigned syscall_chown_time;
+ unsigned syscall_fchown_count;
+ unsigned syscall_fchown_time;
+ unsigned syscall_chdir_count;
+ unsigned syscall_chdir_time;
+ unsigned syscall_getwd_count;
+ unsigned syscall_getwd_time;
+ unsigned syscall_utime_count;
+ unsigned syscall_utime_time;
+ unsigned syscall_ftruncate_count;
+ unsigned syscall_ftruncate_time;
+ unsigned syscall_fcntl_lock_count;
+ unsigned syscall_fcntl_lock_time;
+ unsigned syscall_readlink_count;
+ unsigned syscall_readlink_time;
+ unsigned syscall_symlink_count;
+ unsigned syscall_symlink_time;
+ unsigned syscall_link_count;
+ unsigned syscall_link_time;
+ unsigned syscall_mknod_count;
+ unsigned syscall_mknod_time;
+ unsigned syscall_realpath_count;
+ unsigned syscall_realpath_time;
+ unsigned syscall_get_quota_count;
+ unsigned syscall_get_quota_time;
+ unsigned syscall_set_quota_count;
+ unsigned syscall_set_quota_time;
+/* stat cache counters */
+ unsigned statcache_lookups;
+ unsigned statcache_misses;
+ unsigned statcache_hits;
+/* write cache counters */
+ unsigned writecache_read_hits;
+ unsigned writecache_abutted_writes;
+ unsigned writecache_total_writes;
+ unsigned writecache_non_oplock_writes;
+ unsigned writecache_direct_writes;
+ unsigned writecache_init_writes;
+ unsigned writecache_flushed_writes[NUM_FLUSH_REASONS];
+ unsigned writecache_num_perfect_writes;
+ unsigned writecache_num_write_caches;
+ unsigned writecache_allocated_write_caches;
+/* counters for individual SMB types */
+ unsigned SMBmkdir_count; /* create directory */
+ unsigned SMBmkdir_time;
+ unsigned SMBrmdir_count; /* delete directory */
+ unsigned SMBrmdir_time;
+ unsigned SMBopen_count; /* open file */
+ unsigned SMBopen_time;
+ unsigned SMBcreate_count; /* create file */
+ unsigned SMBcreate_time;
+ unsigned SMBclose_count; /* close file */
+ unsigned SMBclose_time;
+ unsigned SMBflush_count; /* flush file */
+ unsigned SMBflush_time;
+ unsigned SMBunlink_count; /* delete file */
+ unsigned SMBunlink_time;
+ unsigned SMBmv_count; /* rename file */
+ unsigned SMBmv_time;
+ unsigned SMBgetatr_count; /* get file attributes */
+ unsigned SMBgetatr_time;
+ unsigned SMBsetatr_count; /* set file attributes */
+ unsigned SMBsetatr_time;
+ unsigned SMBread_count; /* read from file */
+ unsigned SMBread_time;
+ unsigned SMBwrite_count; /* write to file */
+ unsigned SMBwrite_time;
+ unsigned SMBlock_count; /* lock byte range */
+ unsigned SMBlock_time;
+ unsigned SMBunlock_count; /* unlock byte range */
+ unsigned SMBunlock_time;
+ unsigned SMBctemp_count; /* create temporary file */
+ unsigned SMBctemp_time;
+ /* SMBmknew stats are currently combined with SMBcreate */
+ unsigned SMBmknew_count; /* make new file */
+ unsigned SMBmknew_time;
+ unsigned SMBchkpth_count; /* check directory path */
+ unsigned SMBchkpth_time;
+ unsigned SMBexit_count; /* process exit */
+ unsigned SMBexit_time;
+ unsigned SMBlseek_count; /* seek */
+ unsigned SMBlseek_time;
+ unsigned SMBlockread_count; /* Lock a range and read */
+ unsigned SMBlockread_time;
+ unsigned SMBwriteunlock_count; /* Unlock a range then write */
+ unsigned SMBwriteunlock_time;
+ unsigned SMBreadbraw_count; /* read a block of data with no smb header */
+ unsigned SMBreadbraw_time;
+ unsigned SMBreadBmpx_count; /* read block multiplexed */
+ unsigned SMBreadBmpx_time;
+ unsigned SMBreadBs_count; /* read block (secondary response) */
+ unsigned SMBreadBs_time;
+ unsigned SMBwritebraw_count; /* write a block of data with no smb header */
+ unsigned SMBwritebraw_time;
+ unsigned SMBwriteBmpx_count; /* write block multiplexed */
+ unsigned SMBwriteBmpx_time;
+ unsigned SMBwriteBs_count; /* write block (secondary request) */
+ unsigned SMBwriteBs_time;
+ unsigned SMBwritec_count; /* secondary write request */
+ unsigned SMBwritec_time;
+ unsigned SMBsetattrE_count; /* set file attributes expanded */
+ unsigned SMBsetattrE_time;
+ unsigned SMBgetattrE_count; /* get file attributes expanded */
+ unsigned SMBgetattrE_time;
+ unsigned SMBlockingX_count; /* lock/unlock byte ranges and X */
+ unsigned SMBlockingX_time;
+ unsigned SMBtrans_count; /* transaction - name, bytes in/out */
+ unsigned SMBtrans_time;
+ unsigned SMBtranss_count; /* transaction (secondary request/response) */
+ unsigned SMBtranss_time;
+ unsigned SMBioctl_count; /* IOCTL */
+ unsigned SMBioctl_time;
+ unsigned SMBioctls_count; /* IOCTL (secondary request/response) */
+ unsigned SMBioctls_time;
+ unsigned SMBcopy_count; /* copy */
+ unsigned SMBcopy_time;
+ unsigned SMBmove_count; /* move */
+ unsigned SMBmove_time;
+ unsigned SMBecho_count; /* echo */
+ unsigned SMBecho_time;
+ unsigned SMBwriteclose_count; /* write a file then close it */
+ unsigned SMBwriteclose_time;
+ unsigned SMBopenX_count; /* open and X */
+ unsigned SMBopenX_time;
+ unsigned SMBreadX_count; /* read and X */
+ unsigned SMBreadX_time;
+ unsigned SMBwriteX_count; /* write and X */
+ unsigned SMBwriteX_time;
+ unsigned SMBtrans2_count; /* TRANS2 protocol set */
+ unsigned SMBtrans2_time;
+ unsigned SMBtranss2_count; /* TRANS2 protocol set, secondary command */
+ unsigned SMBtranss2_time;
+ unsigned SMBfindclose_count; /* Terminate a TRANSACT2_FINDFIRST */
+ unsigned SMBfindclose_time;
+ unsigned SMBfindnclose_count; /* Terminate a TRANSACT2_FINDNOTIFYFIRST */
+ unsigned SMBfindnclose_time;
+ unsigned SMBtcon_count; /* tree connect */
+ unsigned SMBtcon_time;
+ unsigned SMBtdis_count; /* tree disconnect */
+ unsigned SMBtdis_time;
+ unsigned SMBnegprot_count; /* negotiate protocol */
+ unsigned SMBnegprot_time;
+ unsigned SMBsesssetupX_count; /* Session Set Up & X (including User Logon) */
+ unsigned SMBsesssetupX_time;
+ unsigned SMBulogoffX_count; /* user logoff */
+ unsigned SMBulogoffX_time;
+ unsigned SMBtconX_count; /* tree connect and X*/
+ unsigned SMBtconX_time;
+ unsigned SMBdskattr_count; /* get disk attributes */
+ unsigned SMBdskattr_time;
+ unsigned SMBsearch_count; /* search directory */
+ unsigned SMBsearch_time;
+ /* SBMffirst stats combined with SMBsearch */
+ unsigned SMBffirst_count; /* find first */
+ unsigned SMBffirst_time;
+ /* SBMfunique stats combined with SMBsearch */
+ unsigned SMBfunique_count; /* find unique */
+ unsigned SMBfunique_time;
+ unsigned SMBfclose_count; /* find close */
+ unsigned SMBfclose_time;
+ unsigned SMBnttrans_count; /* NT transact */
+ unsigned SMBnttrans_time;
+ unsigned SMBnttranss_count; /* NT transact secondary */
+ unsigned SMBnttranss_time;
+ unsigned SMBntcreateX_count; /* NT create and X */
+ unsigned SMBntcreateX_time;
+ unsigned SMBntcancel_count; /* NT cancel */
+ unsigned SMBntcancel_time;
+ unsigned SMBntrename_count; /* NT rename file */
+ unsigned SMBntrename_time;
+ unsigned SMBsplopen_count; /* open print spool file */
+ unsigned SMBsplopen_time;
+ unsigned SMBsplwr_count; /* write to print spool file */
+ unsigned SMBsplwr_time;
+ unsigned SMBsplclose_count; /* close print spool file */
+ unsigned SMBsplclose_time;
+ unsigned SMBsplretq_count; /* return print queue */
+ unsigned SMBsplretq_time;
+ unsigned SMBsends_count; /* send single block message */
+ unsigned SMBsends_time;
+ unsigned SMBsendb_count; /* send broadcast message */
+ unsigned SMBsendb_time;
+ unsigned SMBfwdname_count; /* forward user name */
+ unsigned SMBfwdname_time;
+ unsigned SMBcancelf_count; /* cancel forward */
+ unsigned SMBcancelf_time;
+ unsigned SMBgetmac_count; /* get machine name */
+ unsigned SMBgetmac_time;
+ unsigned SMBsendstrt_count; /* send start of multi-block message */
+ unsigned SMBsendstrt_time;
+ unsigned SMBsendend_count; /* send end of multi-block message */
+ unsigned SMBsendend_time;
+ unsigned SMBsendtxt_count; /* send text of multi-block message */
+ unsigned SMBsendtxt_time;
+ unsigned SMBinvalid_count; /* invalid command */
+ unsigned SMBinvalid_time;
+/* Pathworks setdir command */
+ unsigned pathworks_setdir_count;
+ unsigned pathworks_setdir_time;
+/* These are the TRANS2 sub commands */
+ unsigned Trans2_open_count;
+ unsigned Trans2_open_time;
+ unsigned Trans2_findfirst_count;
+ unsigned Trans2_findfirst_time;
+ unsigned Trans2_findnext_count;
+ unsigned Trans2_findnext_time;
+ unsigned Trans2_qfsinfo_count;
+ unsigned Trans2_qfsinfo_time;
+ unsigned Trans2_setfsinfo_count;
+ unsigned Trans2_setfsinfo_time;
+ unsigned Trans2_qpathinfo_count;
+ unsigned Trans2_qpathinfo_time;
+ unsigned Trans2_setpathinfo_count;
+ unsigned Trans2_setpathinfo_time;
+ unsigned Trans2_qfileinfo_count;
+ unsigned Trans2_qfileinfo_time;
+ unsigned Trans2_setfileinfo_count;
+ unsigned Trans2_setfileinfo_time;
+ unsigned Trans2_fsctl_count;
+ unsigned Trans2_fsctl_time;
+ unsigned Trans2_ioctl_count;
+ unsigned Trans2_ioctl_time;
+ unsigned Trans2_findnotifyfirst_count;
+ unsigned Trans2_findnotifyfirst_time;
+ unsigned Trans2_findnotifynext_count;
+ unsigned Trans2_findnotifynext_time;
+ unsigned Trans2_mkdir_count;
+ unsigned Trans2_mkdir_time;
+ unsigned Trans2_session_setup_count;
+ unsigned Trans2_session_setup_time;
+ unsigned Trans2_get_dfs_referral_count;
+ unsigned Trans2_get_dfs_referral_time;
+ unsigned Trans2_report_dfs_inconsistancy_count;
+ unsigned Trans2_report_dfs_inconsistancy_time;
+/* These are the NT transact sub commands. */
+ unsigned NT_transact_create_count;
+ unsigned NT_transact_create_time;
+ unsigned NT_transact_ioctl_count;
+ unsigned NT_transact_ioctl_time;
+ unsigned NT_transact_set_security_desc_count;
+ unsigned NT_transact_set_security_desc_time;
+ unsigned NT_transact_notify_change_count;
+ unsigned NT_transact_notify_change_time;
+ unsigned NT_transact_rename_count;
+ unsigned NT_transact_rename_time;
+ unsigned NT_transact_query_security_desc_count;
+ unsigned NT_transact_query_security_desc_time;
+ unsigned NT_transact_get_user_quota_count;
+ unsigned NT_transact_get_user_quota_time;
+ unsigned NT_transact_set_user_quota_count;
+ unsigned NT_transact_set_user_quota_time;
+/* These are ACL manipulation calls */
+ unsigned get_nt_acl_count;
+ unsigned get_nt_acl_time;
+ unsigned fget_nt_acl_count;
+ unsigned fget_nt_acl_time;
+ unsigned set_nt_acl_count;
+ unsigned set_nt_acl_time;
+ unsigned fset_nt_acl_count;
+ unsigned fset_nt_acl_time;
+ unsigned chmod_acl_count;
+ unsigned chmod_acl_time;
+ unsigned fchmod_acl_count;
+ unsigned fchmod_acl_time;
+/* These are nmbd stats */
+ unsigned name_release_count;
+ unsigned name_release_time;
+ unsigned name_refresh_count;
+ unsigned name_refresh_time;
+ unsigned name_registration_count;
+ unsigned name_registration_time;
+ unsigned node_status_count;
+ unsigned node_status_time;
+ unsigned name_query_count;
+ unsigned name_query_time;
+ unsigned host_announce_count;
+ unsigned host_announce_time;
+ unsigned workgroup_announce_count;
+ unsigned workgroup_announce_time;
+ unsigned local_master_announce_count;
+ unsigned local_master_announce_time;
+ unsigned master_browser_announce_count;
+ unsigned master_browser_announce_time;
+ unsigned lm_host_announce_count;
+ unsigned lm_host_announce_time;
+ unsigned get_backup_list_count;
+ unsigned get_backup_list_time;
+ unsigned reset_browser_count;
+ unsigned reset_browser_time;
+ unsigned announce_request_count;
+ unsigned announce_request_time;
+ unsigned lm_announce_request_count;
+ unsigned lm_announce_request_time;
+ unsigned domain_logon_count;
+ unsigned domain_logon_time;
+ unsigned sync_browse_lists_count;
+ unsigned sync_browse_lists_time;
+ unsigned run_elections_count;
+ unsigned run_elections_time;
+ unsigned election_count;
+ unsigned election_time;
+};
+
+struct profile_header {
+ int prof_shm_magic;
+ int prof_shm_version;
+ struct profile_stats stats;
+};
+
+extern struct profile_header *profile_h;
+extern struct profile_stats *profile_p;
+extern struct timeval profile_starttime;
+extern struct timeval profile_endtime;
+extern struct timeval profile_starttime_nested;
+extern struct timeval profile_endtime_nested;
+extern BOOL do_profile_flag;
+extern BOOL do_profile_times;
+
+/* these are helper macros - do not call them directly in the code
+ * use the DO_PROFILE_* START_PROFILE and END_PROFILE ones
+ * below which test for the profile flage first
+ */
+#define INC_PROFILE_COUNT(x) profile_p->x++
+#define DEC_PROFILE_COUNT(x) profile_p->x--
+#define ADD_PROFILE_COUNT(x,y) profile_p->x += (y)
+#define PROFILE_TIME \
+ ((profile_endtime.tv_sec - profile_starttime.tv_sec) *1000000 + \
+ ((int)profile_endtime.tv_usec - (int)profile_starttime.tv_usec))
+#define PROFILE_TIME_NESTED \
+ ((profile_endtime_nested.tv_sec - profile_starttime_nested.tv_sec) *1000000 + \
+ ((int)profile_endtime_nested.tv_usec - (int)profile_starttime_nested.tv_usec))
+
+#ifdef WITH_PROFILE
+#define DO_PROFILE_INC(x) \
+ if (do_profile_flag) { \
+ INC_PROFILE_COUNT(x); \
+ }
+#define DO_PROFILE_DEC(x) \
+ if (do_profile_flag) { \
+ DEC_PROFILE_COUNT(x); \
+ }
+#define DO_PROFILE_DEC_INC(x,y) \
+ if (do_profile_flag) { \
+ DEC_PROFILE_COUNT(x); \
+ INC_PROFILE_COUNT(y); \
+ }
+#define DO_PROFILE_ADD(x,n) \
+ if (do_profile_flag) { \
+ ADD_PROFILE_COUNT(x,n); \
+ }
+#define START_PROFILE(x) \
+ if (do_profile_flag) { \
+ if (do_profile_times) \
+ GetTimeOfDay(&profile_starttime); \
+ INC_PROFILE_COUNT(x##_count); \
+ }
+#define START_PROFILE_NESTED(x) \
+ if (do_profile_flag) { \
+ if (do_profile_times) \
+ GetTimeOfDay(&profile_starttime_nested); \
+ INC_PROFILE_COUNT(x##_count); \
+ }
+#define START_PROFILE_BYTES(x,n) \
+ if (do_profile_flag) { \
+ if (do_profile_times) \
+ GetTimeOfDay(&profile_starttime); \
+ INC_PROFILE_COUNT(x##_count); \
+ ADD_PROFILE_COUNT(x##_bytes,n); \
+ }
+#define END_PROFILE(x) \
+ if (do_profile_times) { \
+ GetTimeOfDay(&profile_endtime); \
+ ADD_PROFILE_COUNT(x##_time,PROFILE_TIME); \
+ }
+#define END_PROFILE_NESTED(x) \
+ if (do_profile_times) { \
+ GetTimeOfDay(&profile_endtime_nested); \
+ ADD_PROFILE_COUNT(x##_time,PROFILE_TIME_NESTED); \
+ }
+#else
+#define DO_PROFILE_INC(x)
+#define DO_PROFILE_DEC(x)
+#define DO_PROFILE_DEC_INC(x,y)
+#define DO_PROFILE_ADD(x,n)
+#define START_PROFILE(x)
+#define START_PROFILE_NESTED(x)
+#define START_PROFILE_BYTES(x,n)
+#define END_PROFILE(x)
+#define END_PROFILE_NESTED(x)
+#endif
+
+#endif
diff --git a/source/include/spnego.h b/source/include/spnego.h
new file mode 100644
index 00000000000..b6492ee3c8a
--- /dev/null
+++ b/source/include/spnego.h
@@ -0,0 +1,65 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ RFC2478 Compliant SPNEGO implementation
+
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef SAMBA_SPNEGO_H
+#define SAMBA_SPNEGO_H
+
+#define SPNEGO_DELEG_FLAG 0x01
+#define SPNEGO_MUTUAL_FLAG 0x02
+#define SPNEGO_REPLAY_FLAG 0x04
+#define SPNEGO_SEQUENCE_FLAG 0x08
+#define SPNEGO_ANON_FLAG 0x10
+#define SPNEGO_CONF_FLAG 0x20
+#define SPNEGO_INTEG_FLAG 0x40
+#define SPNEGO_REQ_FLAG 0x80
+
+#define SPNEGO_NEG_TOKEN_INIT 0
+#define SPNEGO_NEG_TOKEN_TARG 1
+
+typedef enum _spnego_negResult {
+ SPNEGO_ACCEPT_COMPLETED = 0,
+ SPNEGO_ACCEPT_INCOMPLETE = 1,
+ SPNEGO_REJECT = 2
+} negResult_t;
+
+typedef struct spnego_negTokenInit {
+ const char **mechTypes;
+ int reqFlags;
+ DATA_BLOB mechToken;
+ DATA_BLOB mechListMIC;
+} negTokenInit_t;
+
+typedef struct spnego_negTokenTarg {
+ uint8 negResult;
+ const char *supportedMech;
+ DATA_BLOB responseToken;
+ DATA_BLOB mechListMIC;
+} negTokenTarg_t;
+
+typedef struct spnego_spnego {
+ int type;
+ negTokenInit_t negTokenInit;
+ negTokenTarg_t negTokenTarg;
+} SPNEGO_DATA;
+
+#endif
diff --git a/source/include/srvstr.h b/source/include/srvstr.h
new file mode 100644
index 00000000000..04db59cf012
--- /dev/null
+++ b/source/include/srvstr.h
@@ -0,0 +1,33 @@
+/*
+ Unix SMB/CIFS implementation.
+ server specific string routines
+ Copyright (C) Andrew Tridgell 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#define srvstr_pull(base_ptr, dest, src, dest_len, src_len, flags) \
+ pull_string(base_ptr, dest, src, dest_len, src_len, flags)
+
+/* pull a string from the smb_buf part of a packet. In this case the
+ string can either be null terminated or it can be terminated by the
+ end of the smbbuf area
+*/
+
+#define srvstr_pull_buf(inbuf, dest, src, dest_len, flags) \
+ pull_string(inbuf, dest, src, dest_len, smb_bufrem(inbuf, src), flags)
+
diff --git a/source/include/stamp-h.in b/source/include/stamp-h.in
new file mode 100644
index 00000000000..c9061b3ad3d
--- /dev/null
+++ b/source/include/stamp-h.in
@@ -0,0 +1 @@
+Sun Jul 18 20:32:29 UTC 1999
diff --git a/source/include/sysquotas.h b/source/include/sysquotas.h
new file mode 100644
index 00000000000..bfb9466b392
--- /dev/null
+++ b/source/include/sysquotas.h
@@ -0,0 +1,76 @@
+/*
+ Unix SMB/CIFS implementation.
+ SYS QUOTA code constants
+ Copyright (C) Stefan (metze) Metzmacher 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _SYSQUOTAS_H
+#define _SYSQUOTAS_H
+
+#ifdef HAVE_SYS_QUOTAS
+
+#if defined(HAVE_MNTENT_H)&&defined(HAVE_SETMNTENT)&&defined(HAVE_GETMNTENT)&&defined(HAVE_ENDMNTENT)
+#include <mntent.h>
+#define HAVE_MNTENT 1
+/*#endif defined(HAVE_MNTENT_H)&&defined(HAVE_SETMNTENT)&&defined(HAVE_GETMNTENT)&&defined(HAVE_ENDMNTENT) */
+#elif defined(HAVE_DEVNM_H)&&defined(HAVE_DEVNM)
+#include <devnm.h>
+#endif /* defined(HAVE_DEVNM_H)&&defined(HAVE_DEVNM) */
+
+#endif /* HAVE_SYS_QUOTAS */
+
+
+/**************************************************
+ Some stuff for the sys_quota api.
+ **************************************************/
+
+#define SMB_QUOTAS_NO_LIMIT ((SMB_BIG_UINT)(0))
+#define SMB_QUOTAS_NO_SPACE ((SMB_BIG_UINT)(1))
+
+#define SMB_QUOTAS_SET_NO_LIMIT(dp) \
+{\
+ (dp)->softlimit = SMB_QUOTAS_NO_LIMIT;\
+ (dp)->hardlimit = SMB_QUOTAS_NO_LIMIT;\
+ (dp)->isoftlimit = SMB_QUOTAS_NO_LIMIT;\
+ (dp)->ihardlimit = SMB_QUOTAS_NO_LIMIT;\
+}
+
+#define SMB_QUOTAS_SET_NO_SPACE(dp) \
+{\
+ (dp)->softlimit = SMB_QUOTAS_NO_SPACE;\
+ (dp)->hardlimit = SMB_QUOTAS_NO_SPACE;\
+ (dp)->isoftlimit = SMB_QUOTAS_NO_SPACE;\
+ (dp)->ihardlimit = SMB_QUOTAS_NO_SPACE;\
+}
+
+typedef struct _SMB_DISK_QUOTA {
+ enum SMB_QUOTA_TYPE qtype;
+ SMB_BIG_UINT bsize;
+ SMB_BIG_UINT hardlimit; /* In bsize units. */
+ SMB_BIG_UINT softlimit; /* In bsize units. */
+ SMB_BIG_UINT curblocks; /* In bsize units. */
+ SMB_BIG_UINT ihardlimit; /* inode hard limit. */
+ SMB_BIG_UINT isoftlimit; /* inode soft limit. */
+ SMB_BIG_UINT curinodes; /* Current used inodes. */
+ uint32 qflags;
+} SMB_DISK_QUOTA;
+
+#ifndef QUOTABLOCK_SIZE
+#define QUOTABLOCK_SIZE 1024
+#endif
+
+#endif /*_SYSQUOTAS_H */
diff --git a/source/include/talloc.h b/source/include/talloc.h
new file mode 100644
index 00000000000..433b52ec954
--- /dev/null
+++ b/source/include/talloc.h
@@ -0,0 +1,72 @@
+#ifndef _TALLOC_H_
+#define _TALLOC_H_
+/*
+ Unix SMB/CIFS implementation.
+ Samba temporary memory allocation functions
+ Copyright (C) Andrew Tridgell 2000
+ Copyright (C) 2001 by Martin Pool <mbp@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/**
+ * @ingroup talloc
+ * @{
+ * @sa talloc.c
+ */
+
+/**
+ * talloc allocation pool. All allocated blocks can be freed in one go.
+ **/
+
+struct talloc_chunk {
+ struct talloc_chunk *next;
+ size_t size;
+ void *ptr;
+};
+
+struct talloc_ctx {
+ struct talloc_chunk *list;
+ size_t total_alloc_size;
+
+ /** The name recorded for this pool, if any. Should describe
+ * the purpose for which it was allocated. The string is
+ * allocated within the pool. **/
+ char *name;
+
+ /** Pointer to the next allocate talloc pool, so that we can
+ * summarize all talloc memory usage. **/
+ struct talloc_ctx *next_ctx;
+};
+
+typedef struct talloc_ctx TALLOC_CTX;
+
+TALLOC_CTX *talloc_init(char const *fmt, ...) PRINTF_ATTRIBUTE(1, 2);
+
+char *talloc_vasprintf(TALLOC_CTX *t, const char *fmt, va_list ap)
+ PRINTF_ATTRIBUTE(2, 0);
+
+char *talloc_asprintf(TALLOC_CTX *t, const char *fmt, ...)
+ PRINTF_ATTRIBUTE(2, 3);
+
+char *talloc_vasprintf_append(TALLOC_CTX *t, char *, const char *, va_list ap)
+ PRINTF_ATTRIBUTE(3, 0);
+
+char *talloc_asprintf_append(TALLOC_CTX *t, char *, const char *, ...)
+ PRINTF_ATTRIBUTE(3, 4);
+
+/** @} */
+
+#endif /* ndef _TALLOC_H_ */
diff --git a/source/include/tdbsam2_parse_info.h b/source/include/tdbsam2_parse_info.h
new file mode 100644
index 00000000000..35eeaeb2d2c
--- /dev/null
+++ b/source/include/tdbsam2_parse_info.h
@@ -0,0 +1,2 @@
+/* This is an automatically generated file - DO NOT EDIT! */
+
diff --git a/source/include/trans2.h b/source/include/trans2.h
new file mode 100644
index 00000000000..168e6477210
--- /dev/null
+++ b/source/include/trans2.h
@@ -0,0 +1,455 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB transaction2 handling
+ Copyright (C) Jeremy Allison 1994-2002.
+
+ Extensively modified by Andrew Tridgell, 1995
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _TRANS2_H_
+#define _TRANS2_H_
+
+/* Define the structures needed for the trans2 calls. */
+
+/*******************************************************
+ For DosFindFirst/DosFindNext - level 1
+
+MAXFILENAMELEN = 255;
+FDATE == uint16
+FTIME == uint16
+ULONG == uint32
+USHORT == uint16
+
+typedef struct _FILEFINDBUF {
+Byte offset Type name description
+-------------+-------+-------------------+--------------
+0 FDATE fdateCreation;
+2 FTIME ftimeCreation;
+4 FDATE fdateLastAccess;
+6 FTIME ftimeLastAccess;
+8 FDATE fdateLastWrite;
+10 FTIME ftimeLastWrite;
+12 ULONG cbFile file length in bytes
+16 ULONG cbFileAlloc size of file allocation unit
+20 USHORT attrFile
+22 UCHAR cchName length of name to follow (not including zero)
+23 UCHAR achName[MAXFILENAMELEN]; Null terminated name
+} FILEFINDBUF;
+*********************************************************/
+
+#define l1_fdateCreation 0
+#define l1_fdateLastAccess 4
+#define l1_fdateLastWrite 8
+#define l1_cbFile 12
+#define l1_cbFileAlloc 16
+#define l1_attrFile 20
+#define l1_cchName 22
+#define l1_achName 23
+
+/**********************************************************
+For DosFindFirst/DosFindNext - level 2
+
+typedef struct _FILEFINDBUF2 {
+Byte offset Type name description
+-------------+-------+-------------------+--------------
+0 FDATE fdateCreation;
+2 FTIME ftimeCreation;
+4 FDATE fdateLastAccess;
+6 FTIME ftimeLastAccess;
+8 FDATE fdateLastWrite;
+10 FTIME ftimeLastWrite;
+12 ULONG cbFile file length in bytes
+16 ULONG cbFileAlloc size of file allocation unit
+20 USHORT attrFile
+22 ULONG cbList Extended attribute list (always 0)
+26 UCHAR cchName length of name to follow (not including zero)
+27 UCHAR achName[MAXFILENAMELEN]; Null terminated name
+} FILEFINDBUF2;
+*************************************************************/
+
+#define l2_fdateCreation 0
+#define l2_fdateLastAccess 4
+#define l2_fdateLastWrite 8
+#define l2_cbFile 12
+#define l2_cbFileAlloc 16
+#define l2_attrFile 20
+#define l2_cbList 22
+#define l2_cchName 26
+#define l2_achName 27
+
+
+/**********************************************************
+For DosFindFirst/DosFindNext - level 260
+
+typedef struct _FILEFINDBUF260 {
+Byte offset Type name description
+-------------+-------+-------------------+--------------
+0 ULONG NextEntryOffset;
+4 ULONG FileIndex;
+8 LARGE_INTEGER CreationTime;
+16 LARGE_INTEGER LastAccessTime;
+24 LARGE_INTEGER LastWriteTime;
+32 LARGE_INTEGER ChangeTime;
+40 LARGE_INTEGER EndOfFile;
+48 LARGE_INTEGER AllocationSize;
+56 ULONG FileAttributes;
+60 ULONG FileNameLength;
+64 ULONG EaSize;
+68 CHAR ShortNameLength;
+70 UNICODE ShortName[12];
+94 UNICODE FileName[];
+*************************************************************/
+
+#define l260_achName 94
+
+
+/**********************************************************
+For DosQueryPathInfo/DosQueryFileInfo/DosSetPathInfo/
+DosSetFileInfo - level 1
+
+typedef struct _FILESTATUS {
+Byte offset Type name description
+-------------+-------+-------------------+--------------
+0 FDATE fdateCreation;
+2 FTIME ftimeCreation;
+4 FDATE fdateLastAccess;
+6 FTIME ftimeLastAccess;
+8 FDATE fdateLastWrite;
+10 FTIME ftimeLastWrite;
+12 ULONG cbFile file length in bytes
+16 ULONG cbFileAlloc size of file allocation unit
+20 USHORT attrFile
+} FILESTATUS;
+*************************************************************/
+
+/* Use the l1_ defines from DosFindFirst */
+
+/**********************************************************
+For DosQueryPathInfo/DosQueryFileInfo/DosSetPathInfo/
+DosSetFileInfo - level 2
+
+typedef struct _FILESTATUS2 {
+Byte offset Type name description
+-------------+-------+-------------------+--------------
+0 FDATE fdateCreation;
+2 FTIME ftimeCreation;
+4 FDATE fdateLastAccess;
+6 FTIME ftimeLastAccess;
+8 FDATE fdateLastWrite;
+10 FTIME ftimeLastWrite;
+12 ULONG cbFile file length in bytes
+16 ULONG cbFileAlloc size of file allocation unit
+20 USHORT attrFile
+22 ULONG cbList Length of EA's (0)
+} FILESTATUS2;
+*************************************************************/
+
+/* Use the l2_ #defines from DosFindFirst */
+
+/**********************************************************
+For DosQFSInfo/DosSetFSInfo - level 1
+
+typedef struct _FSALLOCATE {
+Byte offset Type name description
+-------------+-------+-------------------+--------------
+0 ULONG idFileSystem id of file system
+4 ULONG cSectorUnit number of sectors per allocation unit
+8 ULONG cUnit number of allocation units
+12 ULONG cUnitAvail Available allocation units
+16 USHORT cbSector bytes per sector
+} FSALLOCATE;
+*************************************************************/
+
+#define l1_idFileSystem 0
+#define l1_cSectorUnit 4
+#define l1_cUnit 8
+#define l1_cUnitAvail 12
+#define l1_cbSector 16
+
+/**********************************************************
+For DosQFSInfo/DosSetFSInfo - level 2
+
+typedef struct _FSINFO {
+Byte offset Type name description
+-------------+-------+-------------------+--------------
+0 FDATE vol_fdateCreation
+2 FTIME vol_ftimeCreation
+4 UCHAR vol_cch length of volume name (excluding NULL)
+5 UCHAR vol_szVolLabel[12] volume name
+} FSINFO;
+*************************************************************/
+
+#define SMB_INFO_STANDARD 1 /* FILESTATUS3 struct */
+#define SMB_INFO_SET_EA 2 /* EAOP2 struct, only valid on set not query */
+#define SMB_INFO_QUERY_EA_SIZE 2 /* FILESTATUS4 struct, only valid on query not set */
+#define SMB_INFO_QUERY_EAS_FROM_LIST 3 /* only valid on query not set */
+#define SMB_INFO_QUERY_ALL_EAS 4 /* only valid on query not set */
+#define SMB_INFO_IS_NAME_VALID 6
+#define SMB_INFO_STANDARD_LONG 11 /* similar to level 1, ie struct FileStatus3 */
+#define SMB_QUERY_EA_SIZE_LONG 12 /* similar to level 2, ie struct FileStatus4 */
+#define SMB_QUERY_FS_LABEL_INFO 0x101
+#define SMB_QUERY_FS_VOLUME_INFO 0x102
+#define SMB_QUERY_FS_SIZE_INFO 0x103
+#define SMB_QUERY_FS_DEVICE_INFO 0x104
+#define SMB_QUERY_FS_ATTRIBUTE_INFO 0x105
+#if 0
+#define SMB_QUERY_FS_QUOTA_INFO
+#endif
+
+#define l2_vol_fdateCreation 0
+#define l2_vol_cch 4
+#define l2_vol_szVolLabel 5
+
+
+#define SMB_QUERY_FILE_BASIC_INFO 0x101
+#define SMB_QUERY_FILE_STANDARD_INFO 0x102
+#define SMB_QUERY_FILE_EA_INFO 0x103
+#define SMB_QUERY_FILE_NAME_INFO 0x104
+#define SMB_QUERY_FILE_ALLOCATION_INFO 0x105
+#define SMB_QUERY_FILE_END_OF_FILEINFO 0x106
+#define SMB_QUERY_FILE_ALL_INFO 0x107
+#define SMB_QUERY_FILE_ALT_NAME_INFO 0x108
+#define SMB_QUERY_FILE_STREAM_INFO 0x109
+#define SMB_QUERY_COMPRESSION_INFO 0x10b
+
+#define SMB_FIND_FILE_DIRECTORY_INFO 0x101
+#define SMB_FIND_FILE_FULL_DIRECTORY_INFO 0x102
+#define SMB_FIND_FILE_NAMES_INFO 0x103
+#define SMB_FIND_FILE_BOTH_DIRECTORY_INFO 0x104
+#define SMB_FIND_FILE_LEVEL_261 0x105
+#define SMB_FIND_FILE_LEVEL_262 0x106
+
+#define SMB_SET_FILE_BASIC_INFO 0x101
+#define SMB_SET_FILE_DISPOSITION_INFO 0x102
+#define SMB_SET_FILE_ALLOCATION_INFO 0x103
+#define SMB_SET_FILE_END_OF_FILE_INFO 0x104
+
+/* Query FS info. */
+#define SMB_INFO_ALLOCATION 1
+#define SMB_INFO_VOLUME 2
+
+/*
+ * Thursby MAC extensions....
+ */
+
+/*
+ * MAC CIFS Extensions have the range 0x300 - 0x2FF reserved.
+ * Supposedly Microsoft have agreed to this.
+ */
+
+#define MIN_MAC_INFO_LEVEL 0x300
+#define MAX_MAC_INFO_LEVEL 0x3FF
+
+#define SMB_MAC_QUERY_FS_INFO 0x301
+
+#define DIRLEN_GUESS (45+MAX(l1_achName,l2_achName))
+
+/*
+ * DeviceType and Characteristics returned in a
+ * SMB_QUERY_FS_DEVICE_INFO call.
+ */
+
+#define DEVICETYPE_CD_ROM 0x2
+#define DEVICETYPE_CD_ROM_FILE_SYSTEM 0x3
+#define DEVICETYPE_DISK 0x7
+#define DEVICETYPE_DISK_FILE_SYSTEM 0x8
+#define DEVICETYPE_FILE_SYSTEM 0x9
+
+/* Characteristics. */
+#define TYPE_REMOVABLE_MEDIA 0x1
+#define TYPE_READ_ONLY_DEVICE 0x2
+#define TYPE_FLOPPY 0x4
+#define TYPE_WORM 0x8
+#define TYPE_REMOTE 0x10
+#define TYPE_MOUNTED 0x20
+#define TYPE_VIRTUAL 0x40
+
+/* NT passthrough levels... */
+
+#define SMB_FILE_DIRECTORY_INFORMATION 1001
+#define SMB_FILE_FULL_DIRECTORY_INFORMATION 1002
+#define SMB_FILE_BOTH_DIRECTORY_INFORMATION 1003
+#define SMB_FILE_BASIC_INFORMATION 1004
+#define SMB_FILE_STANDARD_INFORMATION 1005
+#define SMB_FILE_INTERNAL_INFORMATION 1006
+#define SMB_FILE_EA_INFORMATION 1007
+#define SMB_FILE_ACCESS_INFORMATION 1008
+#define SMB_FILE_NAME_INFORMATION 1009
+#define SMB_FILE_RENAME_INFORMATION 1010
+#define SMB_FILE_LINK_INFORMATION 1011
+#define SMB_FILE_NAMES_INFORMATION 1012
+#define SMB_FILE_DISPOSITION_INFORMATION 1013
+#define SMB_FILE_POSITION_INFORMATION 1014
+#define SMB_FILE_FULL_EA_INFORMATION 1015
+#define SMB_FILE_MODE_INFORMATION 1016
+#define SMB_FILE_ALIGNMENT_INFORMATION 1017
+#define SMB_FILE_ALL_INFORMATION 1018
+#define SMB_FILE_ALLOCATION_INFORMATION 1019
+#define SMB_FILE_END_OF_FILE_INFORMATION 1020
+#define SMB_FILE_ALTERNATE_NAME_INFORMATION 1021
+#define SMB_FILE_STREAM_INFORMATION 1022
+#define SMB_FILE_PIPE_INFORMATION 1023
+#define SMB_FILE_PIPE_LOCAL_INFORMATION 1024
+#define SMB_FILE_PIPE_REMOTE_INFORMATION 1025
+#define SMB_FILE_MAILSLOT_QUERY_INFORMATION 1026
+#define SMB_FILE_MAILSLOT_SET_INFORMATION 1027
+#define SMB_FILE_COMPRESSION_INFORMATION 1028
+#define SMB_FILE_OBJECTID_INFORMATION 1029
+#define SMB_FILE_COMPLETION_INFORMATION 1030
+#define SMB_FILE_MOVE_CLUSTER_INFORMATION 1031
+#define SMB_FILE_QUOTA_INFORMATION 1032
+#define SMB_FILE_REPARSEPOINT_INFORMATION 1033
+#define SMB_FILE_NETWORK_OPEN_INFORMATION 1034
+#define SMB_FILE_ATTRIBUTE_TAG_INFORMATION 1035
+#define SMB_FILE_TRACKING_INFORMATION 1036
+#define SMB_FILE_MAXIMUM_INFORMATION 1037
+
+/* NT passthough levels for qfsinfo. */
+
+#define SMB_FS_VOLUME_INFORMATION 1001
+#define SMB_FS_LABEL_INFORMATION 1002
+#define SMB_FS_SIZE_INFORMATION 1003
+#define SMB_FS_DEVICE_INFORMATION 1004
+#define SMB_FS_ATTRIBUTE_INFORMATION 1005
+#define SMB_FS_QUOTA_INFORMATION 1006
+#define SMB_FS_FULL_SIZE_INFORMATION 1007
+#define SMB_FS_OBJECTID_INFORMATION 1008
+
+/* UNIX CIFS Extensions - created by HP */
+/*
+ * UNIX CIFS Extensions have the range 0x200 - 0x2FF reserved.
+ * Supposedly Microsoft have agreed to this.
+ */
+
+#define MIN_UNIX_INFO_LEVEL 0x200
+#define MAX_UNIX_INFO_LEVEL 0x2FF
+
+#define INFO_LEVEL_IS_UNIX(level) (((level) >= MIN_UNIX_INFO_LEVEL) && ((level) <= MAX_UNIX_INFO_LEVEL))
+
+#define SMB_QUERY_FILE_UNIX_BASIC 0x200 /* UNIX File Info*/
+#define SMB_SET_FILE_UNIX_BASIC 0x200
+
+#define SMB_MODE_NO_CHANGE 0xFFFFFFFF /* file mode value which */
+ /* means "don't change it" */
+#define SMB_UID_NO_CHANGE 0xFFFFFFFF
+#define SMB_GID_NO_CHANGE 0xFFFFFFFF
+
+#define SMB_SIZE_NO_CHANGE_LO 0xFFFFFFFF
+#define SMB_SIZE_NO_CHANGE_HI 0xFFFFFFFF
+
+#define SMB_TIME_NO_CHANGE_LO 0xFFFFFFFF
+#define SMB_TIME_NO_CHANGE_HI 0xFFFFFFFF
+
+/*
+Offset Size Name
+0 LARGE_INTEGER EndOfFile File size
+8 LARGE_INTEGER Blocks Number of bytes used on disk (st_blocks).
+16 LARGE_INTEGER CreationTime Creation time
+24 LARGE_INTEGER LastAccessTime Last access time
+32 LARGE_INTEGER LastModificationTime Last modification time
+40 LARGE_INTEGER Uid Numeric user id for the owner
+48 LARGE_INTEGER Gid Numeric group id of owner
+56 ULONG Type Enumeration specifying the pathname type:
+ 0 -- File
+ 1 -- Directory
+ 2 -- Symbolic link
+ 3 -- Character device
+ 4 -- Block device
+ 5 -- FIFO (named pipe)
+ 6 -- Unix domain socket
+
+60 LARGE_INTEGER devmajor Major device number if type is device
+68 LARGE_INTEGER devminor Minor device number if type is device
+76 LARGE_INTEGER uniqueid This is a server-assigned unique id for the file. The client
+ will typically map this onto an inode number. The scope of
+ uniqueness is the share.
+84 LARGE_INTEGER permissions Standard UNIX file permissions - see below.
+92 LARGE_INTEGER nlinks The number of directory entries that map to this entry
+ (number of hard links)
+
+100 - end.
+*/
+
+/* UNIX filetype mappings. */
+
+#define UNIX_TYPE_FILE 0
+#define UNIX_TYPE_DIR 1
+#define UNIX_TYPE_SYMLINK 2
+#define UNIX_TYPE_CHARDEV 3
+#define UNIX_TYPE_BLKDEV 4
+#define UNIX_TYPE_FIFO 5
+#define UNIX_TYPE_SOCKET 6
+#define UNIX_TYPE_UNKNOWN 0xFFFFFFFF
+
+/*
+ * Oh this is fun. "Standard UNIX permissions" has no
+ * meaning in POSIX. We need to define the mapping onto
+ * and off the wire as this was not done in the original HP
+ * spec. JRA.
+ */
+
+#define UNIX_X_OTH 0000001
+#define UNIX_W_OTH 0000002
+#define UNIX_R_OTH 0000004
+#define UNIX_X_GRP 0000010
+#define UNIX_W_GRP 0000020
+#define UNIX_R_GRP 0000040
+#define UNIX_X_USR 0000100
+#define UNIX_W_USR 0000200
+#define UNIX_R_USR 0000400
+#define UNIX_STICKY 0001000
+#define UNIX_SET_GID 0002000
+#define UNIX_SET_UID 0004000
+
+/* Masks for the above */
+#define UNIX_OTH_MASK 0000007
+#define UNIX_GRP_MASK 0000070
+#define UNIX_USR_MASK 0000700
+#define UNIX_PERM_MASK 0000777
+#define UNIX_EXTRA_MASK 0007000
+#define UNIX_ALL_MASK 0007777
+
+#define SMB_QUERY_FILE_UNIX_LINK 0x201
+#define SMB_SET_FILE_UNIX_LINK 0x201
+#define SMB_SET_FILE_UNIX_HLINK 0x203
+
+#define SMB_FIND_FILE_UNIX 0x202
+
+/*
+ Info level for QVOLINFO - returns version of CIFS UNIX extensions, plus
+ 64-bits worth of capability fun :-).
+*/
+
+#define SMB_QUERY_CIFS_UNIX_INFO 0x200
+
+/* Returns the following.
+
+ UINT16 major version number
+ UINT16 minor version number
+ LARGE_INTEGER capability bitfield
+
+*/
+
+#define CIFS_UNIX_MAJOR_VERSION 1
+#define CIFS_UNIX_MINOR_VERSION 0
+
+#define CIFS_UNIX_FCNTL_LOCKS_CAP 0x1
+#define CIFS_UNIX_POSIX_ACLS_CAP 0x2
+
+/* ... more as we think of them :-). */
+
+#endif
diff --git a/source/include/util_getent.h b/source/include/util_getent.h
new file mode 100644
index 00000000000..b67758ba230
--- /dev/null
+++ b/source/include/util_getent.h
@@ -0,0 +1,61 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Simo Sorce 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _UTIL_GETENT_H
+#define _UTIL_GETENT_H
+
+/* Element for a single linked list of group entries */
+/* Replace the use of struct group in some cases */
+/* Used by getgrent_list() */
+
+struct sys_grent {
+ char *gr_name;
+ char *gr_passwd;
+ gid_t gr_gid;
+ char **gr_mem;
+ struct sys_grent *next;
+};
+
+/* Element for a single linked list of passwd entries */
+/* Replace the use of struct passwd in some cases */
+/* Used by getpwent_list() */
+
+struct sys_pwent {
+ char *pw_name;
+ char *pw_passwd;
+ uid_t pw_uid;
+ gid_t pw_gid;
+ char *pw_gecos;
+ char *pw_dir;
+ char *pw_shell;
+ struct sys_pwent *next;
+};
+
+/* Element for a single linked list of user names in a group. */
+/* Used to return group lists that may span multiple lines in
+ /etc/group file. */
+/* Used by get_users_in_group() */
+
+struct sys_userlist {
+ struct sys_userlist *next, *prev;
+ char *unix_name;
+};
+
+#endif /* _UTIL_GETENT_H */
diff --git a/source/include/vfs.h b/source/include/vfs.h
new file mode 100644
index 00000000000..8caf64fd99d
--- /dev/null
+++ b/source/include/vfs.h
@@ -0,0 +1,489 @@
+/*
+ Unix SMB/CIFS implementation.
+ VFS structures and parameters
+ Copyright (C) Jeremy Allison 1999-2003
+ Copyright (C) Tim Potter 1999
+ Copyright (C) Alexander Bokovoy 2002
+ Copyright (C) Stefan (metze) Metzmacher 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ This work was sponsored by Optifacio Software Services, Inc.
+*/
+
+#ifndef _VFS_H
+#define _VFS_H
+
+/* Avoid conflict with an AIX include file */
+
+#ifdef vfs_ops
+#undef vfs_ops
+#endif
+
+/*
+ * As we're now (thanks Andrew ! :-) using file_structs and connection
+ * structs in the vfs - then anyone writing a vfs must include includes.h...
+ */
+
+/*
+ * This next constant specifies the version number of the VFS interface
+ * this smbd will load. Increment this if *ANY* changes are made to the
+ * vfs_ops below. JRA.
+ */
+
+/* Changed to version 2 for CIFS UNIX extensions (mknod and link added). JRA. */
+/* Changed to version 3 for POSIX acl extensions. JRA. */
+/* Changed to version 4 for cascaded VFS interface. Alexander Bokovoy. */
+/* Changed to version 5 for sendfile addition. JRA. */
+/* Changed to version 6 for the new module system, fixed cascading and quota functions. --metze */
+/* Changed to version 7 to include the get_nt_acl info parameter. JRA. */
+/* Changed to version 8 includes EA calls. JRA. */
+/* Changed to version 9 to include the get_shadow_data call. --metze */
+/* Changed to version 10 to include pread/pwrite calls. */
+#define SMB_VFS_INTERFACE_VERSION 10
+
+
+/* to bug old modules witch are trying to compile with the old functions */
+#define vfs_init __ERROR_please_port_this_module_to_SMB_VFS_INTERFACE_VERSION_8_donot_use_vfs_init_anymore(void) { __ERROR_please_port_this_module_to_SMB_VFS_INTERFACE_VERSION_8_donot_use_vfs_init_anymore };
+#define lp_parm_string __ERROR_please_port_lp_parm_string_to_lp_parm_const_string_or_lp_parm_talloc_string { \
+ __ERROR_please_port_lp_parm_string_to_lp_parm_const_string_or_lp_parm_talloc_string };
+#define lp_vfs_options __ERROR_please_donot_use_lp_vfs_options_anymore_use_lp_parm_xxxx_functions_instead { \
+ __ERROR_please_donot_use_lp_vfs_options_anymore_use_lp_parm_xxxx_functions_instead };
+
+/*
+ All intercepted VFS operations must be declared as static functions inside module source
+ in order to keep smbd namespace unpolluted. See source of audit, extd_audit, fake_perms and recycle
+ example VFS modules for more details.
+*/
+
+/* VFS operations structure */
+
+struct vfs_handle_struct;
+struct connection_struct;
+struct files_struct;
+struct security_descriptor_info;
+
+/*
+ Available VFS operations. These values must be in sync with vfs_ops struct
+ (struct vfs_fn_pointers and struct vfs_handle_pointers inside of struct vfs_ops).
+ In particular, if new operations are added to vfs_ops, appropriate constants
+ should be added to vfs_op_type so that order of them kept same as in vfs_ops.
+*/
+
+typedef enum _vfs_op_type {
+ SMB_VFS_OP_NOOP = -1,
+
+ /* Disk operations */
+
+ SMB_VFS_OP_CONNECT = 0,
+ SMB_VFS_OP_DISCONNECT,
+ SMB_VFS_OP_DISK_FREE,
+ SMB_VFS_OP_GET_QUOTA,
+ SMB_VFS_OP_SET_QUOTA,
+ SMB_VFS_OP_GET_SHADOW_COPY_DATA,
+
+
+ /* Directory operations */
+
+ SMB_VFS_OP_OPENDIR,
+ SMB_VFS_OP_READDIR,
+ SMB_VFS_OP_MKDIR,
+ SMB_VFS_OP_RMDIR,
+ SMB_VFS_OP_CLOSEDIR,
+
+ /* File operations */
+
+ SMB_VFS_OP_OPEN,
+ SMB_VFS_OP_CLOSE,
+ SMB_VFS_OP_READ,
+ SMB_VFS_OP_PREAD,
+ SMB_VFS_OP_WRITE,
+ SMB_VFS_OP_PWRITE,
+ SMB_VFS_OP_LSEEK,
+ SMB_VFS_OP_SENDFILE,
+ SMB_VFS_OP_RENAME,
+ SMB_VFS_OP_FSYNC,
+ SMB_VFS_OP_STAT,
+ SMB_VFS_OP_FSTAT,
+ SMB_VFS_OP_LSTAT,
+ SMB_VFS_OP_UNLINK,
+ SMB_VFS_OP_CHMOD,
+ SMB_VFS_OP_FCHMOD,
+ SMB_VFS_OP_CHOWN,
+ SMB_VFS_OP_FCHOWN,
+ SMB_VFS_OP_CHDIR,
+ SMB_VFS_OP_GETWD,
+ SMB_VFS_OP_UTIME,
+ SMB_VFS_OP_FTRUNCATE,
+ SMB_VFS_OP_LOCK,
+ SMB_VFS_OP_SYMLINK,
+ SMB_VFS_OP_READLINK,
+ SMB_VFS_OP_LINK,
+ SMB_VFS_OP_MKNOD,
+ SMB_VFS_OP_REALPATH,
+
+ /* NT ACL operations. */
+
+ SMB_VFS_OP_FGET_NT_ACL,
+ SMB_VFS_OP_GET_NT_ACL,
+ SMB_VFS_OP_FSET_NT_ACL,
+ SMB_VFS_OP_SET_NT_ACL,
+
+ /* POSIX ACL operations. */
+
+ SMB_VFS_OP_CHMOD_ACL,
+ SMB_VFS_OP_FCHMOD_ACL,
+
+ SMB_VFS_OP_SYS_ACL_GET_ENTRY,
+ SMB_VFS_OP_SYS_ACL_GET_TAG_TYPE,
+ SMB_VFS_OP_SYS_ACL_GET_PERMSET,
+ SMB_VFS_OP_SYS_ACL_GET_QUALIFIER,
+ SMB_VFS_OP_SYS_ACL_GET_FILE,
+ SMB_VFS_OP_SYS_ACL_GET_FD,
+ SMB_VFS_OP_SYS_ACL_CLEAR_PERMS,
+ SMB_VFS_OP_SYS_ACL_ADD_PERM,
+ SMB_VFS_OP_SYS_ACL_TO_TEXT,
+ SMB_VFS_OP_SYS_ACL_INIT,
+ SMB_VFS_OP_SYS_ACL_CREATE_ENTRY,
+ SMB_VFS_OP_SYS_ACL_SET_TAG_TYPE,
+ SMB_VFS_OP_SYS_ACL_SET_QUALIFIER,
+ SMB_VFS_OP_SYS_ACL_SET_PERMSET,
+ SMB_VFS_OP_SYS_ACL_VALID,
+ SMB_VFS_OP_SYS_ACL_SET_FILE,
+ SMB_VFS_OP_SYS_ACL_SET_FD,
+ SMB_VFS_OP_SYS_ACL_DELETE_DEF_FILE,
+ SMB_VFS_OP_SYS_ACL_GET_PERM,
+ SMB_VFS_OP_SYS_ACL_FREE_TEXT,
+ SMB_VFS_OP_SYS_ACL_FREE_ACL,
+ SMB_VFS_OP_SYS_ACL_FREE_QUALIFIER,
+
+ /* EA operations. */
+ SMB_VFS_OP_GETXATTR,
+ SMB_VFS_OP_LGETXATTR,
+ SMB_VFS_OP_FGETXATTR,
+ SMB_VFS_OP_LISTXATTR,
+ SMB_VFS_OP_LLISTXATTR,
+ SMB_VFS_OP_FLISTXATTR,
+ SMB_VFS_OP_REMOVEXATTR,
+ SMB_VFS_OP_LREMOVEXATTR,
+ SMB_VFS_OP_FREMOVEXATTR,
+ SMB_VFS_OP_SETXATTR,
+ SMB_VFS_OP_LSETXATTR,
+ SMB_VFS_OP_FSETXATTR,
+
+ /* This should always be last enum value */
+
+ SMB_VFS_OP_LAST
+} vfs_op_type;
+
+/*
+ Please keep vfs_op_type, struct vfs_fn_pointers and struct vfs_handles_pointers in sync.
+*/
+struct vfs_ops {
+ struct vfs_fn_pointers {
+ /* Disk operations */
+
+ int (*connect)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *service, const char *user);
+ void (*disconnect)(struct vfs_handle_struct *handle, struct connection_struct *conn);
+ SMB_BIG_UINT (*disk_free)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, BOOL small_query, SMB_BIG_UINT *bsize,
+ SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize);
+ int (*get_quota)(struct vfs_handle_struct *handle, struct connection_struct *conn, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *qt);
+ int (*set_quota)(struct vfs_handle_struct *handle, struct connection_struct *conn, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *qt);
+ int (*get_shadow_copy_data)(struct vfs_handle_struct *handle, struct files_struct *fsp, SHADOW_COPY_DATA *shadow_copy_data, BOOL labels);
+
+ /* Directory operations */
+
+ DIR *(*opendir)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *fname);
+ struct dirent *(*readdir)(struct vfs_handle_struct *handle, struct connection_struct *conn, DIR *dirp);
+ int (*mkdir)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, mode_t mode);
+ int (*rmdir)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path);
+ int (*closedir)(struct vfs_handle_struct *handle, struct connection_struct *conn, DIR *dir);
+
+ /* File operations */
+
+ int (*open)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *fname, int flags, mode_t mode);
+ int (*close)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd);
+ ssize_t (*read)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, void *data, size_t n);
+ ssize_t (*pread)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, void *data, size_t n, SMB_OFF_T offset);
+ ssize_t (*write)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, const void *data, size_t n);
+ ssize_t (*pwrite)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, const void *data, size_t n, SMB_OFF_T offset);
+ SMB_OFF_T (*lseek)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, SMB_OFF_T offset, int whence);
+ ssize_t (*sendfile)(struct vfs_handle_struct *handle, int tofd, files_struct *fsp, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count);
+ int (*rename)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *old, const char *new);
+ int (*fsync)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd);
+ int (*stat)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *fname, SMB_STRUCT_STAT *sbuf);
+ int (*fstat)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, SMB_STRUCT_STAT *sbuf);
+ int (*lstat)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf);
+ int (*unlink)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path);
+ int (*chmod)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, mode_t mode);
+ int (*fchmod)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, mode_t mode);
+ int (*chown)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, uid_t uid, gid_t gid);
+ int (*fchown)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, uid_t uid, gid_t gid);
+ int (*chdir)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path);
+ char *(*getwd)(struct vfs_handle_struct *handle, struct connection_struct *conn, char *buf);
+ int (*utime)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, struct utimbuf *times);
+ int (*ftruncate)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, SMB_OFF_T offset);
+ BOOL (*lock)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, int op, SMB_OFF_T offset, SMB_OFF_T count, int type);
+ int (*symlink)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *oldpath, const char *newpath);
+ int (*readlink)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, char *buf, size_t bufsiz);
+ int (*link)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *oldpath, const char *newpath);
+ int (*mknod)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, mode_t mode, SMB_DEV_T dev);
+ char *(*realpath)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, char *resolved_path);
+
+ /* NT ACL operations. */
+
+ size_t (*fget_nt_acl)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, uint32 security_info, struct security_descriptor_info **ppdesc);
+ size_t (*get_nt_acl)(struct vfs_handle_struct *handle, struct files_struct *fsp, const char *name, uint32 security_info, struct security_descriptor_info **ppdesc);
+ BOOL (*fset_nt_acl)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, uint32 security_info_sent, struct security_descriptor_info *psd);
+ BOOL (*set_nt_acl)(struct vfs_handle_struct *handle, struct files_struct *fsp, const char *name, uint32 security_info_sent, struct security_descriptor_info *psd);
+
+ /* POSIX ACL operations. */
+
+ int (*chmod_acl)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *name, mode_t mode);
+ int (*fchmod_acl)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, mode_t mode);
+
+ int (*sys_acl_get_entry)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_T theacl, int entry_id, SMB_ACL_ENTRY_T *entry_p);
+ int (*sys_acl_get_tag_type)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p);
+ int (*sys_acl_get_permset)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p);
+ void * (*sys_acl_get_qualifier)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_ENTRY_T entry_d);
+ SMB_ACL_T (*sys_acl_get_file)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path_p, SMB_ACL_TYPE_T type);
+ SMB_ACL_T (*sys_acl_get_fd)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd);
+ int (*sys_acl_clear_perms)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_PERMSET_T permset);
+ int (*sys_acl_add_perm)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm);
+ char * (*sys_acl_to_text)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_T theacl, ssize_t *plen);
+ SMB_ACL_T (*sys_acl_init)(struct vfs_handle_struct *handle, struct connection_struct *conn, int count);
+ int (*sys_acl_create_entry)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry);
+ int (*sys_acl_set_tag_type)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype);
+ int (*sys_acl_set_qualifier)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_ENTRY_T entry, void *qual);
+ int (*sys_acl_set_permset)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset);
+ int (*sys_acl_valid)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_T theacl );
+ int (*sys_acl_set_file)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl);
+ int (*sys_acl_set_fd)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, SMB_ACL_T theacl);
+ int (*sys_acl_delete_def_file)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path);
+ int (*sys_acl_get_perm)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm);
+ int (*sys_acl_free_text)(struct vfs_handle_struct *handle, struct connection_struct *conn, char *text);
+ int (*sys_acl_free_acl)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_T posix_acl);
+ int (*sys_acl_free_qualifier)(struct vfs_handle_struct *handle, struct connection_struct *conn, void *qualifier, SMB_ACL_TAG_T tagtype);
+
+ /* EA operations. */
+ ssize_t (*getxattr)(struct vfs_handle_struct *handle,struct connection_struct *conn,const char *path, const char *name, void *value, size_t size);
+ ssize_t (*lgetxattr)(struct vfs_handle_struct *handle,struct connection_struct *conn,const char *path, const char *name, void *value, size_t size);
+ ssize_t (*fgetxattr)(struct vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name, void *value, size_t size);
+ ssize_t (*listxattr)(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, char *list, size_t size);
+ ssize_t (*llistxattr)(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, char *list, size_t size);
+ ssize_t (*flistxattr)(struct vfs_handle_struct *handle, struct files_struct *fsp,int fd, char *list, size_t size);
+ int (*removexattr)(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name);
+ int (*lremovexattr)(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name);
+ int (*fremovexattr)(struct vfs_handle_struct *handle, struct files_struct *fsp,int filedes, const char *name);
+ int (*setxattr)(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, const void *value, size_t size, int flags);
+ int (*lsetxattr)(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, const void *value, size_t size, int flags);
+ int (*fsetxattr)(struct vfs_handle_struct *handle, struct files_struct *fsp,int filedes, const char *name, const void *value, size_t size, int flags);
+
+ } ops;
+
+ struct vfs_handles_pointers {
+ /* Disk operations */
+
+ struct vfs_handle_struct *connect;
+ struct vfs_handle_struct *disconnect;
+ struct vfs_handle_struct *disk_free;
+ struct vfs_handle_struct *get_quota;
+ struct vfs_handle_struct *set_quota;
+ struct vfs_handle_struct *get_shadow_copy_data;
+
+ /* Directory operations */
+
+ struct vfs_handle_struct *opendir;
+ struct vfs_handle_struct *readdir;
+ struct vfs_handle_struct *mkdir;
+ struct vfs_handle_struct *rmdir;
+ struct vfs_handle_struct *closedir;
+
+ /* File operations */
+
+ struct vfs_handle_struct *open;
+ struct vfs_handle_struct *close;
+ struct vfs_handle_struct *read;
+ struct vfs_handle_struct *pread;
+ struct vfs_handle_struct *write;
+ struct vfs_handle_struct *pwrite;
+ struct vfs_handle_struct *lseek;
+ struct vfs_handle_struct *sendfile;
+ struct vfs_handle_struct *rename;
+ struct vfs_handle_struct *fsync;
+ struct vfs_handle_struct *stat;
+ struct vfs_handle_struct *fstat;
+ struct vfs_handle_struct *lstat;
+ struct vfs_handle_struct *unlink;
+ struct vfs_handle_struct *chmod;
+ struct vfs_handle_struct *fchmod;
+ struct vfs_handle_struct *chown;
+ struct vfs_handle_struct *fchown;
+ struct vfs_handle_struct *chdir;
+ struct vfs_handle_struct *getwd;
+ struct vfs_handle_struct *utime;
+ struct vfs_handle_struct *ftruncate;
+ struct vfs_handle_struct *lock;
+ struct vfs_handle_struct *symlink;
+ struct vfs_handle_struct *readlink;
+ struct vfs_handle_struct *link;
+ struct vfs_handle_struct *mknod;
+ struct vfs_handle_struct *realpath;
+
+ /* NT ACL operations. */
+
+ struct vfs_handle_struct *fget_nt_acl;
+ struct vfs_handle_struct *get_nt_acl;
+ struct vfs_handle_struct *fset_nt_acl;
+ struct vfs_handle_struct *set_nt_acl;
+
+ /* POSIX ACL operations. */
+
+ struct vfs_handle_struct *chmod_acl;
+ struct vfs_handle_struct *fchmod_acl;
+
+ struct vfs_handle_struct *sys_acl_get_entry;
+ struct vfs_handle_struct *sys_acl_get_tag_type;
+ struct vfs_handle_struct *sys_acl_get_permset;
+ struct vfs_handle_struct *sys_acl_get_qualifier;
+ struct vfs_handle_struct *sys_acl_get_file;
+ struct vfs_handle_struct *sys_acl_get_fd;
+ struct vfs_handle_struct *sys_acl_clear_perms;
+ struct vfs_handle_struct *sys_acl_add_perm;
+ struct vfs_handle_struct *sys_acl_to_text;
+ struct vfs_handle_struct *sys_acl_init;
+ struct vfs_handle_struct *sys_acl_create_entry;
+ struct vfs_handle_struct *sys_acl_set_tag_type;
+ struct vfs_handle_struct *sys_acl_set_qualifier;
+ struct vfs_handle_struct *sys_acl_set_permset;
+ struct vfs_handle_struct *sys_acl_valid;
+ struct vfs_handle_struct *sys_acl_set_file;
+ struct vfs_handle_struct *sys_acl_set_fd;
+ struct vfs_handle_struct *sys_acl_delete_def_file;
+ struct vfs_handle_struct *sys_acl_get_perm;
+ struct vfs_handle_struct *sys_acl_free_text;
+ struct vfs_handle_struct *sys_acl_free_acl;
+ struct vfs_handle_struct *sys_acl_free_qualifier;
+
+ /* EA operations. */
+ struct vfs_handle_struct *getxattr;
+ struct vfs_handle_struct *lgetxattr;
+ struct vfs_handle_struct *fgetxattr;
+ struct vfs_handle_struct *listxattr;
+ struct vfs_handle_struct *llistxattr;
+ struct vfs_handle_struct *flistxattr;
+ struct vfs_handle_struct *removexattr;
+ struct vfs_handle_struct *lremovexattr;
+ struct vfs_handle_struct *fremovexattr;
+ struct vfs_handle_struct *setxattr;
+ struct vfs_handle_struct *lsetxattr;
+ struct vfs_handle_struct *fsetxattr;
+
+ } handles;
+};
+
+/*
+ Possible VFS operation layers (per-operation)
+
+ These values are used by VFS subsystem when building vfs_ops for connection
+ from multiple VFS modules. Internally, Samba differentiates only opaque and
+ transparent layers at this process. Other types are used for providing better
+ diagnosing facilities.
+
+ Most modules will provide transparent layers. Opaque layer is for modules
+ which implement actual file system calls (like DB-based VFS). For example,
+ default POSIX VFS which is built in into Samba is an opaque VFS module.
+
+ Other layer types (audit, splitter, scanner) were designed to provide different
+ degree of transparency and for diagnosing VFS module behaviour.
+
+ Each module can implement several layers at the same time provided that only
+ one layer is used per each operation.
+
+*/
+
+typedef enum _vfs_op_layer {
+ SMB_VFS_LAYER_NOOP = -1, /* - For using in VFS module to indicate end of array */
+ /* of operations description */
+ SMB_VFS_LAYER_OPAQUE = 0, /* - Final level, does not call anything beyond itself */
+ SMB_VFS_LAYER_TRANSPARENT, /* - Normal operation, calls underlying layer after */
+ /* possibly changing passed data */
+ SMB_VFS_LAYER_LOGGER, /* - Logs data, calls underlying layer, logging may not */
+ /* use Samba VFS */
+ SMB_VFS_LAYER_SPLITTER, /* - Splits operation, calls underlying layer _and_ own facility, */
+ /* then combines result */
+ SMB_VFS_LAYER_SCANNER /* - Checks data and possibly initiates additional */
+ /* file activity like logging to files _inside_ samba VFS */
+} vfs_op_layer;
+
+/*
+ VFS operation description. Each VFS module registers an array of vfs_op_tuple to VFS subsystem,
+ which describes all operations this module is willing to intercept.
+ VFS subsystem initializes then the conn->vfs_ops and conn->vfs_opaque_ops structs
+ using this information.
+*/
+
+typedef struct _vfs_op_tuple {
+ void* op;
+ vfs_op_type type;
+ vfs_op_layer layer;
+} vfs_op_tuple;
+
+
+typedef struct vfs_handle_struct {
+ struct vfs_handle_struct *next, *prev;
+ const char *param;
+ struct vfs_ops vfs_next;
+ struct connection_struct *conn;
+ void *data;
+ void (*free_data)(void **data);
+} vfs_handle_struct;
+
+
+#define SMB_VFS_HANDLE_GET_DATA(handle, datap, type, ret) { \
+ if (!(handle)||((datap=(type *)(handle)->data)==NULL)) { \
+ DEBUG(0,("%s() failed to get vfs_handle->data!\n",FUNCTION_MACRO)); \
+ ret; \
+ } \
+}
+
+#define SMB_VFS_HANDLE_SET_DATA(handle, datap, free_fn, type, ret) { \
+ if (!(handle)) { \
+ DEBUG(0,("%s() failed to set handle->data!\n",FUNCTION_MACRO)); \
+ ret; \
+ } else { \
+ if ((handle)->free_data) { \
+ (handle)->free_data(&(handle)->data); \
+ } \
+ (handle)->data = (void *)datap; \
+ (handle)->free_data = free_fn; \
+ } \
+}
+
+#define SMB_VFS_HANDLE_FREE_DATA(handle) { \
+ if ((handle) && (handle)->free_data) { \
+ (handle)->free_data(&(handle)->data); \
+ } \
+}
+
+/* Check whether module-specific data handle was already allocated or not */
+#define SMB_VFS_HANDLE_TEST_DATA(handle) ( !(handle) || !(handle)->data ? False : True )
+
+#define SMB_VFS_OP(x) ((void *) x)
+
+
+#include "vfs_macros.h"
+
+#endif /* _VFS_H */
diff --git a/source/include/vfs_macros.h b/source/include/vfs_macros.h
new file mode 100644
index 00000000000..1ec1c5a7789
--- /dev/null
+++ b/source/include/vfs_macros.h
@@ -0,0 +1,318 @@
+/*
+ Unix SMB/CIFS implementation.
+ VFS wrapper macros
+ Copyright (C) Stefan (metze) Metzmacher 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _VFS_MACROS_H
+#define _VFS_MACROS_H
+
+/*******************************************************************
+ Don't access conn->vfs.ops.* directly!!!
+ Use this macros!
+ (Fixes should go also into the vfs_opaque_* and vfs_next_* macros!)
+********************************************************************/
+
+/* Disk operations */
+#define SMB_VFS_CONNECT(conn, service, user) ((conn)->vfs.ops.connect((conn)->vfs.handles.connect, (conn), (service), (user)))
+#define SMB_VFS_DISCONNECT(conn) ((conn)->vfs.ops.disconnect((conn)->vfs.handles.disconnect, (conn)))
+#define SMB_VFS_DISK_FREE(conn, path, small_query, bsize, dfree ,dsize) ((conn)->vfs.ops.disk_free((conn)->vfs.handles.disk_free, (conn), (path), (small_query), (bsize), (dfree), (dsize)))
+#define SMB_VFS_GET_QUOTA(conn, qtype, id, qt) ((conn)->vfs.ops.get_quota((conn)->vfs.handles.get_quota, (conn), (qtype), (id), (qt)))
+#define SMB_VFS_SET_QUOTA(conn, qtype, id, qt) ((conn)->vfs.ops.set_quota((conn)->vfs.handles.set_quota, (conn), (qtype), (id), (qt)))
+#define SMB_VFS_GET_SHADOW_COPY_DATA(fsp,shadow_copy_data,labels) ((fsp)->conn->vfs.ops.get_shadow_copy_data((fsp)->conn->vfs.handles.get_shadow_copy_data,(fsp),(shadow_copy_data),(labels)))
+
+/* Directory operations */
+#define SMB_VFS_OPENDIR(conn, fname) ((conn)->vfs.ops.opendir((conn)->vfs.handles.opendir, (conn), (fname)))
+#define SMB_VFS_READDIR(conn, dirp) ((conn)->vfs.ops.readdir((conn)->vfs.handles.readdir, (conn), (dirp)))
+#define SMB_VFS_MKDIR(conn, path, mode) ((conn)->vfs.ops.mkdir((conn)->vfs.handles.mkdir,(conn), (path), (mode)))
+#define SMB_VFS_RMDIR(conn, path) ((conn)->vfs.ops.rmdir((conn)->vfs.handles.rmdir, (conn), (path)))
+#define SMB_VFS_CLOSEDIR(conn, dir) ((conn)->vfs.ops.closedir((conn)->vfs.handles.closedir, (conn), dir))
+
+/* File operations */
+#define SMB_VFS_OPEN(conn, fname, flags, mode) ((conn)->vfs.ops.open((conn)->vfs.handles.open, (conn), (fname), (flags), (mode)))
+#define SMB_VFS_CLOSE(fsp, fd) ((fsp)->conn->vfs.ops.close((fsp)->conn->vfs.handles.close, (fsp), (fd)))
+#define SMB_VFS_READ(fsp, fd, data, n) ((fsp)->conn->vfs.ops.read((fsp)->conn->vfs.handles.read, (fsp), (fd), (data), (n)))
+#define SMB_VFS_PREAD(fsp, fd, data, n, off) ((fsp)->conn->vfs.ops.pread((fsp)->conn->vfs.handles.pread, (fsp), (fd), (data), (n), (off)))
+#define SMB_VFS_WRITE(fsp, fd, data, n) ((fsp)->conn->vfs.ops.write((fsp)->conn->vfs.handles.write, (fsp), (fd), (data), (n)))
+#define SMB_VFS_PWRITE(fsp, fd, data, n, off) ((fsp)->conn->vfs.ops.pwrite((fsp)->conn->vfs.handles.pwrite, (fsp), (fd), (data), (n), (off)))
+#define SMB_VFS_LSEEK(fsp, fd, offset, whence) ((fsp)->conn->vfs.ops.lseek((fsp)->conn->vfs.handles.lseek, (fsp), (fd), (offset), (whence)))
+#define SMB_VFS_SENDFILE(tofd, fsp, fromfd, header, offset, count) ((fsp)->conn->vfs.ops.sendfile((fsp)->conn->vfs.handles.sendfile, (tofd), (fsp), (fromfd), (header), (offset), (count)))
+#define SMB_VFS_RENAME(conn, old, new) ((conn)->vfs.ops.rename((conn)->vfs.handles.rename, (conn), (old), (new)))
+#define SMB_VFS_FSYNC(fsp, fd) ((fsp)->conn->vfs.ops.fsync((fsp)->conn->vfs.handles.fsync, (fsp), (fd)))
+#define SMB_VFS_STAT(conn, fname, sbuf) ((conn)->vfs.ops.stat((conn)->vfs.handles.stat, (conn), (fname), (sbuf)))
+#define SMB_VFS_FSTAT(fsp, fd, sbuf) ((fsp)->conn->vfs.ops.fstat((fsp)->conn->vfs.handles.fstat, (fsp) ,(fd) ,(sbuf)))
+#define SMB_VFS_LSTAT(conn, path, sbuf) ((conn)->vfs.ops.lstat((conn)->vfs.handles.lstat, (conn), (path), (sbuf)))
+#define SMB_VFS_UNLINK(conn, path) ((conn)->vfs.ops.unlink((conn)->vfs.handles.unlink, (conn), (path)))
+#define SMB_VFS_CHMOD(conn, path, mode) ((conn)->vfs.ops.chmod((conn)->vfs.handles.chmod, (conn), (path), (mode)))
+#define SMB_VFS_FCHMOD(fsp, fd, mode) ((fsp)->conn->vfs.ops.fchmod((fsp)->conn->vfs.handles.fchmod, (fsp), (fd), (mode)))
+#define SMB_VFS_CHOWN(conn, path, uid, gid) ((conn)->vfs.ops.chown((conn)->vfs.handles.chown, (conn), (path), (uid), (gid)))
+#define SMB_VFS_FCHOWN(fsp, fd, uid, gid) ((fsp)->conn->vfs.ops.fchown((fsp)->conn->vfs.handles.fchown, (fsp), (fd), (uid), (gid)))
+#define SMB_VFS_CHDIR(conn, path) ((conn)->vfs.ops.chdir((conn)->vfs.handles.chdir, (conn), (path)))
+#define SMB_VFS_GETWD(conn, buf) ((conn)->vfs.ops.getwd((conn)->vfs.handles.getwd, (conn), (buf)))
+#define SMB_VFS_UTIME(conn, path, times) ((conn)->vfs.ops.utime((conn)->vfs.handles.utime, (conn), (path), (times)))
+#define SMB_VFS_FTRUNCATE(fsp, fd, offset) ((fsp)->conn->vfs.ops.ftruncate((fsp)->conn->vfs.handles.ftruncate, (fsp), (fd), (offset)))
+#define SMB_VFS_LOCK(fsp, fd, op, offset, count, type) ((fsp)->conn->vfs.ops.lock((fsp)->conn->vfs.handles.lock, (fsp), (fd) ,(op), (offset), (count), (type)))
+#define SMB_VFS_SYMLINK(conn, oldpath, newpath) ((conn)->vfs.ops.symlink((conn)->vfs.handles.symlink, (conn), (oldpath), (newpath)))
+#define SMB_VFS_READLINK(conn, path, buf, bufsiz) ((conn)->vfs.ops.readlink((conn)->vfs.handles.readlink, (conn), (path), (buf), (bufsiz)))
+#define SMB_VFS_LINK(conn, oldpath, newpath) ((conn)->vfs.ops.link((conn)->vfs.handles.link, (conn), (oldpath), (newpath)))
+#define SMB_VFS_MKNOD(conn, path, mode, dev) ((conn)->vfs.ops.mknod((conn)->vfs.handles.mknod, (conn), (path), (mode), (dev)))
+#define SMB_VFS_REALPATH(conn, path, resolved_path) ((conn)->vfs.ops.realpath((conn)->vfs.handles.realpath, (conn), (path), (resolved_path)))
+
+/* NT ACL operations. */
+#define SMB_VFS_FGET_NT_ACL(fsp, fd, security_info, ppdesc) ((fsp)->conn->vfs.ops.fget_nt_acl((fsp)->conn->vfs.handles.fget_nt_acl, (fsp), (fd), (security_info), (ppdesc)))
+#define SMB_VFS_GET_NT_ACL(fsp, name, security_info, ppdesc) ((fsp)->conn->vfs.ops.get_nt_acl((fsp)->conn->vfs.handles.get_nt_acl, (fsp), (name), (security_info), (ppdesc)))
+#define SMB_VFS_FSET_NT_ACL(fsp, fd, security_info_sent, psd) ((fsp)->conn->vfs.ops.fset_nt_acl((fsp)->conn->vfs.handles.fset_nt_acl, (fsp), (fd), (security_info_sent), (psd)))
+#define SMB_VFS_SET_NT_ACL(fsp, name, security_info_sent, psd) ((fsp)->conn->vfs.ops.set_nt_acl((fsp)->conn->vfs.handles.set_nt_acl, (fsp), (name), (security_info_sent), (psd)))
+
+/* POSIX ACL operations. */
+#define SMB_VFS_CHMOD_ACL(conn, name, mode) ((conn)->vfs.ops.chmod_acl((conn)->vfs.handles.chmod_acl, (conn), (name), (mode)))
+#define SMB_VFS_FCHMOD_ACL(fsp, fd, mode) ((fsp)->conn->vfs.ops.fchmod_acl((fsp)->conn->vfs.handles.chmod_acl, (fsp), (fd), (mode)))
+
+#define SMB_VFS_SYS_ACL_GET_ENTRY(conn, theacl, entry_id, entry_p) ((conn)->vfs.ops.sys_acl_get_entry((conn)->vfs.handles.sys_acl_get_entry, (conn), (theacl), (entry_id), (entry_p)))
+#define SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry_d, tag_type_p) ((conn)->vfs.ops.sys_acl_get_tag_type((conn)->vfs.handles.sys_acl_get_tag_type, (conn), (entry_d), (tag_type_p)))
+#define SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry_d, permset_p) ((conn)->vfs.ops.sys_acl_get_permset((conn)->vfs.handles.sys_acl_get_permset, (conn), (entry_d), (permset_p)))
+#define SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry_d) ((conn)->vfs.ops.sys_acl_get_qualifier((conn)->vfs.handles.sys_acl_get_qualifier, (conn), (entry_d)))
+#define SMB_VFS_SYS_ACL_GET_FILE(conn, path_p, type) ((conn)->vfs.ops.sys_acl_get_file((conn)->vfs.handles.sys_acl_get_file, (conn), (path_p), (type)))
+#define SMB_VFS_SYS_ACL_GET_FD(fsp, fd) ((fsp)->conn->vfs.ops.sys_acl_get_fd((fsp)->conn->vfs.handles.sys_acl_get_fd, (fsp), (fd)))
+#define SMB_VFS_SYS_ACL_CLEAR_PERMS(conn, permset) ((conn)->vfs.ops.sys_acl_clear_perms((conn)->vfs.handles.sys_acl_clear_perms, (conn), (permset)))
+#define SMB_VFS_SYS_ACL_ADD_PERM(conn, permset, perm) ((conn)->vfs.ops.sys_acl_add_perm((conn)->vfs.handles.sys_acl_add_perm, (conn), (permset), (perm)))
+#define SMB_VFS_SYS_ACL_TO_TEXT(conn, theacl, plen) ((conn)->vfs.ops.sys_acl_to_text((conn)->vfs.handles.sys_acl_to_text, (conn), (theacl), (plen)))
+#define SMB_VFS_SYS_ACL_INIT(conn, count) ((conn)->vfs.ops.sys_acl_init((conn)->vfs.handles.sys_acl_init, (conn), (count)))
+#define SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, pacl, pentry) ((conn)->vfs.ops.sys_acl_create_entry((conn)->vfs.handles.sys_acl_create_entry, (conn), (pacl), (pentry)))
+#define SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, entry, tagtype) ((conn)->vfs.ops.sys_acl_set_tag_type((conn)->vfs.handles.sys_acl_set_tag_type, (conn), (entry), (tagtype)))
+#define SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, entry, qual) ((conn)->vfs.ops.sys_acl_set_qualifier((conn)->vfs.handles.sys_acl_set_qualifier, (conn), (entry), (qual)))
+#define SMB_VFS_SYS_ACL_SET_PERMSET(conn, entry, permset) ((conn)->vfs.ops.sys_acl_set_permset((conn)->vfs.handles.sys_acl_set_permset, (conn), (entry), (permset)))
+#define SMB_VFS_SYS_ACL_VALID(conn, theacl) ((conn)->vfs.ops.sys_acl_valid((conn)->vfs.handles.sys_acl_valid, (conn), (theacl)))
+#define SMB_VFS_SYS_ACL_SET_FILE(conn, name, acltype, theacl) ((conn)->vfs.ops.sys_acl_set_file((conn)->vfs.handles.sys_acl_set_file, (conn), (name), (acltype), (theacl)))
+#define SMB_VFS_SYS_ACL_SET_FD(fsp, fd, theacl) ((fsp)->conn->vfs.ops.sys_acl_set_fd((fsp)->conn->vfs.handles.sys_acl_set_fd, (fsp), (fd), (theacl)))
+#define SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, path) ((conn)->vfs.ops.sys_acl_delete_def_file((conn)->vfs.handles.sys_acl_delete_def_file, (conn), (path)))
+#define SMB_VFS_SYS_ACL_GET_PERM(conn, permset, perm) ((conn)->vfs.ops.sys_acl_get_perm((conn)->vfs.handles.sys_acl_get_perm, (conn), (permset), (perm)))
+#define SMB_VFS_SYS_ACL_FREE_TEXT(conn, text) ((conn)->vfs.ops.sys_acl_free_text((conn)->vfs.handles.sys_acl_free_text, (conn), (text)))
+#define SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl) ((conn)->vfs.ops.sys_acl_free_acl((conn)->vfs.handles.sys_acl_free_acl, (conn), (posix_acl)))
+#define SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, qualifier, tagtype) ((conn)->vfs.ops.sys_acl_free_qualifier((conn)->vfs.handles.sys_acl_free_qualifier, (conn), (qualifier), (tagtype)))
+
+/* EA operations. */
+#define SMB_VFS_GETXATTR(conn,path,name,value,size) ((conn)->vfs.ops.getxattr((conn)->vfs.handles.getxattr,(conn),(path),(name),(value),(size)))
+#define SMB_VFS_LGETXATTR(conn,path,name,value,size) ((conn)->vfs.ops.lgetxattr((conn)->vfs.handles.lgetxattr,(conn),(path),(name),(value),(size)))
+#define SMB_VFS_FGETXATTR(fsp,fd,name,value,size) ((fsp)->conn->vfs.ops.fgetxattr((fsp)->conn->vfs.handles.fgetxattr,(fsp),(fd),(name),(value),(size)))
+#define SMB_VFS_LISTXATTR(conn,path,list,size) ((conn)->vfs.ops.listxattr((conn)->vfs.handles.listxattr,(conn),(path),(list),(size)))
+#define SMB_VFS_LLISTXATTR(conn,path,list,size) ((conn)->vfs.ops.llistxattr((conn)->vfs.handles.llistxattr,(conn),(path),(list),(size)))
+#define SMB_VFS_FLISTXATTR(fsp,fd,list,size) ((fsp)->conn->vfs.ops.flistxattr((fsp)->conn->vfs.handles.flistxattr,(fsp),(fd),(list),(size)))
+#define SMB_VFS_REMOVEXATTR(conn,path,name) ((conn)->vfs.ops.removexattr((conn)->vfs.handles.removexattr,(conn),(path),(name)))
+#define SMB_VFS_LREMOVEXATTR(conn,path,name) ((conn)->vfs.ops.lremovexattr((conn)->vfs.handles.lremovexattr,(conn),(path),(name)))
+#define SMB_VFS_FREMOVEXATTR(fsp,fd,name) ((fsp)->conn->vfs.ops.fremovexattr((fsp)->conn->vfs.handles.fremovexattr,(fsp),(fd),(name)))
+#define SMB_VFS_SETXATTR(conn,path,name,value,size,flags) ((conn)->vfs.ops.setxattr((conn)->vfs.handles.setxattr,(conn),(path),(name),(value),(size),(flags)))
+#define SMB_VFS_LSETXATTR(conn,path,name,value,size,flags) ((conn)->vfs.ops.lsetxattr((conn)->vfs.handles.lsetxattr,(conn),(path),(name),(value),(size),(flags)))
+#define SMB_VFS_FSETXATTR(fsp,fd,name,value,size,flags) ((fsp)->conn->vfs.ops.fsetxattr((fsp)->conn->vfs.handles.fsetxattr,(fsp),(fd),(name),(value),(size),(flags)))
+
+/*******************************************************************
+ Don't access conn->vfs_opaque.ops directly!!!
+ Use this macros!
+ (Fixes should also go into the vfs_* and vfs_next_* macros!)
+********************************************************************/
+
+/* Disk operations */
+#define SMB_VFS_OPAQUE_CONNECT(conn, service, user) ((conn)->vfs_opaque.ops.connect((conn)->vfs_opaque.handles.connect, (conn), (service), (user)))
+#define SMB_VFS_OPAQUE_DISCONNECT(conn) ((conn)->vfs_opaque.ops.disconnect((conn)->vfs_opaque.handles.disconnect, (conn)))
+#define SMB_VFS_OPAQUE_DISK_FREE(conn, path, small_query, bsize, dfree ,dsize) ((conn)->vfs_opaque.ops.disk_free((conn)->vfs_opaque.handles.disk_free, (conn), (path), (small_query), (bsize), (dfree), (dsize)))
+#define SMB_VFS_OPAQUE_GET_QUOTA(conn, qtype, id, qt) ((conn)->vfs_opaque.ops.get_quota((conn)->vfs_opaque.handles.get_quota, (conn), (qtype), (id), (qt)))
+#define SMB_VFS_OPAQUE_SET_QUOTA(conn, qtype, id, qt) ((conn)->vfs_opaque.ops.set_quota((conn)->vfs_opaque.handles.set_quota, (conn), (qtype), (id), (qt)))
+#define SMB_VFS_OPAQUE_GET_SHADOW_COPY_DATA(fsp,shadow_copy_data,labels) ((fsp)->conn->vfs_opaque.ops.get_shadow_copy_data((fsp)->conn->vfs_opaque.handles.get_shadow_copy_data,(fsp),(shadow_copy_data),(labels)))
+
+/* Directory operations */
+#define SMB_VFS_OPAQUE_OPENDIR(conn, fname) ((conn)->vfs_opaque.ops.opendir((conn)->vfs_opaque.handles.opendir, (conn), (fname)))
+#define SMB_VFS_OPAQUE_READDIR(conn, dirp) ((conn)->vfs_opaque.ops.readdir((conn)->vfs_opaque.handles.readdir, (conn), (dirp)))
+#define SMB_VFS_OPAQUE_MKDIR(conn, path, mode) ((conn)->vfs_opaque.ops.mkdir((conn)->vfs_opaque.handles.mkdir,(conn), (path), (mode)))
+#define SMB_VFS_OPAQUE_RMDIR(conn, path) ((conn)->vfs_opaque.ops.rmdir((conn)->vfs_opaque.handles.rmdir, (conn), (path)))
+#define SMB_VFS_OPAQUE_CLOSEDIR(conn, dir) ((conn)->vfs_opaque.ops.closedir((conn)->vfs_opaque.handles.closedir, (conn), dir))
+
+/* File operations */
+#define SMB_VFS_OPAQUE_OPEN(conn, fname, flags, mode) ((conn)->vfs_opaque.ops.open((conn)->vfs_opaque.handles.open, (conn), (fname), (flags), (mode)))
+#define SMB_VFS_OPAQUE_CLOSE(fsp, fd) ((fsp)->conn->vfs_opaque.ops.close((fsp)->conn->vfs_opaque.handles.close, (fsp), (fd)))
+#define SMB_VFS_OPAQUE_READ(fsp, fd, data, n) ((fsp)->conn->vfs_opaque.ops.read((fsp)->conn->vfs_opaque.handles.read, (fsp), (fd), (data), (n)))
+#define SMB_VFS_OPAQUE_PREAD(fsp, fd, data, n, off) ((fsp)->conn->vfs_opaque.ops.pread((fsp)->conn->vfs_opaque.handles.pread, (fsp), (fd), (data), (n), (off)))
+#define SMB_VFS_OPAQUE_WRITE(fsp, fd, data, n) ((fsp)->conn->vfs_opaque.ops.write((fsp)->conn->vfs_opaque.handles.write, (fsp), (fd), (data), (n)))
+#define SMB_VFS_OPAQUE_PWRITE(fsp, fd, data, n, off) ((fsp)->conn->vfs_opaque.ops.pwrite((fsp)->conn->vfs_opaque.handles.pwrite, (fsp), (fd), (data), (n), (off)))
+#define SMB_VFS_OPAQUE_LSEEK(fsp, fd, offset, whence) ((fsp)->conn->vfs_opaque.ops.lseek((fsp)->conn->vfs_opaque.handles.lseek, (fsp), (fd), (offset), (whence)))
+#define SMB_VFS_OPAQUE_SENDFILE(tofd, fsp, fromfd, header, offset, count) ((fsp)->conn->vfs_opaque.ops.sendfile((fsp)->conn->vfs_opaque.handles.sendfile, (tofd), (fsp), (fromfd), (header), (offset), (count)))
+#define SMB_VFS_OPAQUE_RENAME(conn, old, new) ((conn)->vfs_opaque.ops.rename((conn)->vfs_opaque.handles.rename, (conn), (old), (new)))
+#define SMB_VFS_OPAQUE_FSYNC(fsp, fd) ((fsp)->conn->vfs_opaque.ops.fsync((fsp)->conn->vfs_opaque.handles.fsync, (fsp), (fd)))
+#define SMB_VFS_OPAQUE_STAT(conn, fname, sbuf) ((conn)->vfs_opaque.ops.stat((conn)->vfs_opaque.handles.stat, (conn), (fname), (sbuf)))
+#define SMB_VFS_OPAQUE_FSTAT(fsp, fd, sbuf) ((fsp)->conn->vfs_opaque.ops.fstat((fsp)->conn->vfs_opaque.handles.fstat, (fsp) ,(fd) ,(sbuf)))
+#define SMB_VFS_OPAQUE_LSTAT(conn, path, sbuf) ((conn)->vfs_opaque.ops.lstat((conn)->vfs_opaque.handles.lstat, (conn), (path), (sbuf)))
+#define SMB_VFS_OPAQUE_UNLINK(conn, path) ((conn)->vfs_opaque.ops.unlink((conn)->vfs_opaque.handles.unlink, (conn), (path)))
+#define SMB_VFS_OPAQUE_CHMOD(conn, path, mode) ((conn)->vfs_opaque.ops.chmod((conn)->vfs_opaque.handles.chmod, (conn), (path), (mode)))
+#define SMB_VFS_OPAQUE_FCHMOD(fsp, fd, mode) ((fsp)->conn->vfs_opaque.ops.fchmod((fsp)->conn->vfs_opaque.handles.fchmod, (fsp), (fd), (mode)))
+#define SMB_VFS_OPAQUE_CHOWN(conn, path, uid, gid) ((conn)->vfs_opaque.ops.chown((conn)->vfs_opaque.handles.chown, (conn), (path), (uid), (gid)))
+#define SMB_VFS_OPAQUE_FCHOWN(fsp, fd, uid, gid) ((fsp)->conn->vfs_opaque.ops.fchown((fsp)->conn->vfs_opaque.handles.fchown, (fsp), (fd), (uid), (gid)))
+#define SMB_VFS_OPAQUE_CHDIR(conn, path) ((conn)->vfs_opaque.ops.chdir((conn)->vfs_opaque.handles.chdir, (conn), (path)))
+#define SMB_VFS_OPAQUE_GETWD(conn, buf) ((conn)->vfs_opaque.ops.getwd((conn)->vfs_opaque.handles.getwd, (conn), (buf)))
+#define SMB_VFS_OPAQUE_UTIME(conn, path, times) ((conn)->vfs_opaque.ops.utime((conn)->vfs_opaque.handles.utime, (conn), (path), (times)))
+#define SMB_VFS_OPAQUE_FTRUNCATE(fsp, fd, offset) ((fsp)->conn->vfs_opaque.ops.ftruncate((fsp)->conn->vfs_opaque.handles.ftruncate, (fsp), (fd), (offset)))
+#define SMB_VFS_OPAQUE_LOCK(fsp, fd, op, offset, count, type) ((fsp)->conn->vfs_opaque.ops.lock((fsp)->conn->vfs_opaque.handles.lock, (fsp), (fd) ,(op), (offset), (count), (type)))
+#define SMB_VFS_OPAQUE_SYMLINK(conn, oldpath, newpath) ((conn)->vfs_opaque.ops.symlink((conn)->vfs_opaque.handles.symlink, (conn), (oldpath), (newpath)))
+#define SMB_VFS_OPAQUE_READLINK(conn, path, buf, bufsiz) ((conn)->vfs_opaque.ops.readlink((conn)->vfs_opaque.handles.readlink, (conn), (path), (buf), (bufsiz)))
+#define SMB_VFS_OPAQUE_LINK(conn, oldpath, newpath) ((conn)->vfs_opaque.ops.link((conn)->vfs_opaque.handles.link, (conn), (oldpath), (newpath)))
+#define SMB_VFS_OPAQUE_MKNOD(conn, path, mode, dev) ((conn)->vfs_opaque.ops.mknod((conn)->vfs_opaque.handles.mknod, (conn), (path), (mode), (dev)))
+#define SMB_VFS_OPAQUE_REALPATH(conn, path, resolved_path) ((conn)->vfs_opaque.ops.realpath((conn)->vfs_opaque.handles.realpath, (conn), (path), (resolved_path)))
+
+/* NT ACL operations. */
+#define SMB_VFS_OPAQUE_FGET_NT_ACL(fsp, fd, security_info, ppdesc) ((fsp)->conn->vfs_opaque.ops.fget_nt_acl((fsp)->conn->vfs_opaque.handles.fget_nt_acl, (fsp), (fd), (security_info), (ppdesc)))
+#define SMB_VFS_OPAQUE_GET_NT_ACL(fsp, name, security_info, ppdesc) ((fsp)->conn->vfs_opaque.ops.get_nt_acl((fsp)->conn->vfs_opaque.handles.get_nt_acl, (fsp), (name), (security_info), (ppdesc)))
+#define SMB_VFS_OPAQUE_FSET_NT_ACL(fsp, fd, security_info_sent, psd) ((fsp)->conn->vfs_opaque.ops.fset_nt_acl((fsp)->conn->vfs_opaque.handles.fset_nt_acl, (fsp), (fd), (security_info_sent), (psd)))
+#define SMB_VFS_OPAQUE_SET_NT_ACL(fsp, name, security_info_sent, psd) ((fsp)->conn->vfs_opaque.ops.set_nt_acl((fsp)->conn->vfs_opaque.handles.set_nt_acl, (fsp), (name), (security_info_sent), (psd)))
+
+/* POSIX ACL operations. */
+#define SMB_VFS_OPAQUE_CHMOD_ACL(conn, name, mode) ((conn)->vfs_opaque.ops.chmod_acl((conn)->vfs_opaque.handles.chmod_acl, (conn), (name), (mode)))
+#define SMB_VFS_OPAQUE_FCHMOD_ACL(fsp, fd, mode) ((fsp)->conn->vfs_opaque.ops.fchmod_acl((fsp)->conn->vfs_opaque.handles.chmod_acl, (fsp), (fd), (mode)))
+
+#define SMB_VFS_OPAQUE_SYS_ACL_GET_ENTRY(conn, theacl, entry_id, entry_p) ((conn)->vfs_opaque.ops.sys_acl_get_entry((conn)->vfs_opaque.handles.sys_acl_get_entry, (conn), (theacl), (entry_id), (entry_p)))
+#define SMB_VFS_OPAQUE_SYS_ACL_GET_TAG_TYPE(conn, entry_d, tag_type_p) ((conn)->vfs_opaque.ops.sys_acl_get_tag_type((conn)->vfs_opaque.handles.sys_acl_get_tag_type, (conn), (entry_d), (tag_type_p)))
+#define SMB_VFS_OPAQUE_SYS_ACL_GET_PERMSET(conn, entry_d, permset_p) ((conn)->vfs_opaque.ops.sys_acl_get_permset((conn)->vfs_opaque.handles.sys_acl_get_permset, (conn), (entry_d), (permset_p)))
+#define SMB_VFS_OPAQUE_SYS_ACL_GET_QUALIFIER(conn, entry_d) ((conn)->vfs_opaque.ops.sys_acl_get_qualifier((conn)->vfs_opaque.handles.sys_acl_get_qualifier, (conn), (entry_d)))
+#define SMB_VFS_OPAQUE_SYS_ACL_GET_FILE(conn, path_p, type) ((conn)->vfs_opaque.ops.sys_acl_get_file((conn)->vfs_opaque.handles.sys_acl_get_file, (conn), (path_p), (type)))
+#define SMB_VFS_OPAQUE_SYS_ACL_GET_FD(fsp, fd) ((fsp)->conn->vfs_opaque.ops.sys_acl_get_fd((fsp)->conn->vfs_opaque.handles.sys_acl_get_fd, (fsp), (fd)))
+#define SMB_VFS_OPAQUE_SYS_ACL_CLEAR_PERMS(conn, permset) ((conn)->vfs_opaque.ops.sys_acl_clear_perms((conn)->vfs_opaque.handles.sys_acl_clear_perms, (conn), (permset)))
+#define SMB_VFS_OPAQUE_SYS_ACL_ADD_PERM(conn, permset, perm) ((conn)->vfs_opaque.ops.sys_acl_add_perm((conn)->vfs_opaque.handles.sys_acl_add_perm, (conn), (permset), (perm)))
+#define SMB_VFS_OPAQUE_SYS_ACL_TO_TEXT(conn, theacl, plen) ((conn)->vfs_opaque.ops.sys_acl_to_text((conn)->vfs_opaque.handles.sys_acl_to_text, (conn), (theacl), (plen)))
+#define SMB_VFS_OPAQUE_SYS_ACL_INIT(conn, count) ((conn)->vfs_opaque.ops.sys_acl_init((conn)->vfs_opaque.handles.sys_acl_init, (conn), (count)))
+#define SMB_VFS_OPAQUE_SYS_ACL_CREATE_ENTRY(conn, pacl, pentry) ((conn)->vfs_opaque.ops.sys_acl_create_entry((conn)->vfs_opaque.handles.sys_acl_create_entry, (conn), (pacl), (pentry)))
+#define SMB_VFS_OPAQUE_SYS_ACL_SET_TAG_TYPE(conn, entry, tagtype) ((conn)->vfs_opaque.ops.sys_acl_set_tag_type((conn)->vfs_opaque.handles.sys_acl_set_tag_type, (conn), (entry), (tagtype)))
+#define SMB_VFS_OPAQUE_SYS_ACL_SET_QUALIFIER(conn, entry, qual) ((conn)->vfs_opaque.ops.sys_acl_set_qualifier((conn)->vfs_opaque.handles.sys_acl_set_qualifier, (conn), (entry), (qual)))
+#define SMB_VFS_OPAQUE_SYS_ACL_SET_PERMSET(conn, entry, permset) ((conn)->vfs_opaque.ops.sys_acl_set_permset((conn)->vfs_opaque.handles.sys_acl_set_permset, (conn), (entry), (permset)))
+#define SMB_VFS_OPAQUE_SYS_ACL_VALID(conn, theacl) ((conn)->vfs_opaque.ops.sys_acl_valid((conn)->vfs_opaque.handles.sys_acl_valid, (conn), (theacl)))
+#define SMB_VFS_OPAQUE_SYS_ACL_SET_FILE(conn, name, acltype, theacl) ((conn)->vfs_opaque.ops.sys_acl_set_file((conn)->vfs_opaque.handles.sys_acl_set_file, (conn), (name), (acltype), (theacl)))
+#define SMB_VFS_OPAQUE_SYS_ACL_SET_FD(fsp, fd, theacl) ((fsp)->conn->vfs_opaque.ops.sys_acl_set_fd((fsp)->conn->vfs_opaque.handles.sys_acl_set_fd, (fsp), (fd), (theacl)))
+#define SMB_VFS_OPAQUE_SYS_ACL_DELETE_DEF_FILE(conn, path) ((conn)->vfs_opaque.ops.sys_acl_delete_def_file((conn)->vfs_opaque.handles.sys_acl_delete_def_file, (conn), (path)))
+#define SMB_VFS_OPAQUE_SYS_ACL_GET_PERM(conn, permset, perm) ((conn)->vfs_opaque.ops.sys_acl_get_perm((conn)->vfs_opaque.handles.sys_acl_get_perm, (conn), (permset), (perm)))
+#define SMB_VFS_OPAQUE_SYS_ACL_FREE_TEXT(conn, text) ((conn)->vfs_opaque.ops.sys_acl_free_text((conn)->vfs_opaque.handles.sys_acl_free_text, (conn), (text)))
+#define SMB_VFS_OPAQUE_SYS_ACL_FREE_ACL(conn, posix_acl) ((conn)->vfs_opaque.ops.sys_acl_free_acl((conn)->vfs_opaque.handles.sys_acl_free_acl, (conn), (posix_acl)))
+#define SMB_VFS_OPAQUE_SYS_ACL_FREE_QUALIFIER(conn, qualifier, tagtype) ((conn)->vfs_opaque.ops.sys_acl_free_qualifier((conn)->vfs_opaque.handles.sys_acl_free_qualifier, (conn), (qualifier), (tagtype)))
+
+/* EA operations. */
+#define SMB_VFS_OPAQUE_GETXATTR(conn,path,name,value,size) ((conn)->vfs_opaque.ops.getxattr((conn)->vfs_opaque.handles.getxattr,(conn),(path),(name),(value),(size)))
+#define SMB_VFS_OPAQUE_LGETXATTR(conn,path,name,value,size) ((conn)->vfs_opaque.ops.lgetxattr((conn)->vfs_opaque.handles.lgetxattr,(conn),(path),(name),(value),(size)))
+#define SMB_VFS_OPAQUE_FGETXATTR(fsp,fd,name,value,size) ((fsp)->conn->vfs_opaque.ops.fgetxattr((fsp)->conn->vfs_opaque.handles.fgetxattr,(fsp),(fd),(name),(value),(size)))
+#define SMB_VFS_OPAQUE_LISTXATTR(conn,path,list,size) ((conn)->vfs_opaque.ops.listxattr((conn)->vfs_opaque.handles.listxattr,(conn),(path),(list),(size)))
+#define SMB_VFS_OPAQUE_LLISTXATTR(conn,path,list,size) ((conn)->vfs_opaque.ops.llistxattr((conn)->vfs_opaque.handles.llistxattr,(conn),(path),(list),(size)))
+#define SMB_VFS_OPAQUE_FLISTXATTR(fsp,fd,list,size) ((fsp)->conn->vfs_opaque.ops.flistxattr((fsp)->conn->vfs_opaque.handles.flistxattr,(fsp),(fd),(list),(size)))
+#define SMB_VFS_OPAQUE_REMOVEXATTR(conn,path,name) ((conn)->vfs_opaque.ops.removexattr((conn)->vfs_opaque.handles.removexattr,(conn),(path),(name)))
+#define SMB_VFS_OPAQUE_LREMOVEXATTR(conn,path,name) ((conn)->vfs_opaque.ops.lremovexattr((conn)->vfs_opaque.handles.lremovexattr,(conn),(path),(name)))
+#define SMB_VFS_OPAQUE_FREMOVEXATTR(fsp,fd,name) ((fsp)->conn->vfs_opaque.ops.fremovexattr((fsp)->conn->vfs_opaque.handles.fremovexattr,(fsp),(fd),(name)))
+#define SMB_VFS_OPAQUE_SETXATTR(conn,path,name,value,size,flags) ((conn)->vfs_opaque.ops.setxattr((conn)->vfs_opaque.handles.setxattr,(conn),(path),(name),(value),(size),(flags)))
+#define SMB_VFS_OPAQUE_LSETXATTR(conn,path,name,value,size,flags) ((conn)->vfs_opaque.ops.lsetxattr((conn)->vfs_opaque.handles.lsetxattr,(conn),(path),(name),(value),(size),(flags)))
+#define SMB_VFS_OPAQUE_FSETXATTR(fsp,fd,name,value,size,flags) ((fsp)->conn->vfs_opaque.ops.fsetxattr((fsp)->conn->vfs_opaque.handles.fsetxattr,(fsp),(fd),(name),(value),(size),(flags)))
+
+/*******************************************************************
+ Don't access handle->vfs_next.ops.* directly!!!
+ Use this macros!
+ (Fixes should go also into the vfs_* and vfs_opaque_* macros!)
+********************************************************************/
+
+/* Disk operations */
+#define SMB_VFS_NEXT_CONNECT(handle, conn, service, user) ((handle)->vfs_next.ops.connect((handle)->vfs_next.handles.connect, (conn), (service), (user)))
+#define SMB_VFS_NEXT_DISCONNECT(handle, conn) ((handle)->vfs_next.ops.disconnect((handle)->vfs_next.handles.disconnect, (conn)))
+#define SMB_VFS_NEXT_DISK_FREE(handle, conn, path, small_query, bsize, dfree ,dsize) ((handle)->vfs_next.ops.disk_free((handle)->vfs_next.handles.disk_free, (conn), (path), (small_query), (bsize), (dfree), (dsize)))
+#define SMB_VFS_NEXT_GET_QUOTA(handle, conn, qtype, id, qt) ((handle)->vfs_next.ops.get_quota((handle)->vfs_next.handles.get_quota, (conn), (qtype), (id), (qt)))
+#define SMB_VFS_NEXT_SET_QUOTA(handle, conn, qtype, id, qt) ((handle)->vfs_next.ops.set_quota((handle)->vfs_next.handles.set_quota, (conn), (qtype), (id), (qt)))
+#define SMB_VFS_NEXT_GET_SHADOW_COPY_DATA(handle, fsp, shadow_copy_data ,labels) ((handle)->vfs_next.ops.get_shadow_copy_data((handle)->vfs_next.handles.get_shadow_copy_data,(fsp),(shadow_copy_data),(labels)))
+
+/* Directory operations */
+#define SMB_VFS_NEXT_OPENDIR(handle, conn, fname) ((handle)->vfs_next.ops.opendir((handle)->vfs_next.handles.opendir, (conn), (fname)))
+#define SMB_VFS_NEXT_READDIR(handle, conn, dirp) ((handle)->vfs_next.ops.readdir((handle)->vfs_next.handles.readdir, (conn), (dirp)))
+#define SMB_VFS_NEXT_MKDIR(handle, conn, path, mode) ((handle)->vfs_next.ops.mkdir((handle)->vfs_next.handles.mkdir,(conn), (path), (mode)))
+#define SMB_VFS_NEXT_RMDIR(handle, conn, path) ((handle)->vfs_next.ops.rmdir((handle)->vfs_next.handles.rmdir, (conn), (path)))
+#define SMB_VFS_NEXT_CLOSEDIR(handle, conn, dir) ((handle)->vfs_next.ops.closedir((handle)->vfs_next.handles.closedir, (conn), dir))
+
+/* File operations */
+#define SMB_VFS_NEXT_OPEN(handle, conn, fname, flags, mode) ((handle)->vfs_next.ops.open((handle)->vfs_next.handles.open, (conn), (fname), (flags), (mode)))
+#define SMB_VFS_NEXT_CLOSE(handle, fsp, fd) ((handle)->vfs_next.ops.close((handle)->vfs_next.handles.close, (fsp), (fd)))
+#define SMB_VFS_NEXT_READ(handle, fsp, fd, data, n) ((handle)->vfs_next.ops.read((handle)->vfs_next.handles.read, (fsp), (fd), (data), (n)))
+#define SMB_VFS_NEXT_PREAD(handle, fsp, fd, data, n, off) ((handle)->vfs_next.ops.pread((handle)->vfs_next.handles.pread, (fsp), (fd), (data), (n), (off)))
+#define SMB_VFS_NEXT_WRITE(handle, fsp, fd, data, n) ((handle)->vfs_next.ops.write((handle)->vfs_next.handles.write, (fsp), (fd), (data), (n)))
+#define SMB_VFS_NEXT_PWRITE(handle, fsp, fd, data, n, off) ((handle)->vfs_next.ops.pwrite((handle)->vfs_next.handles.pwrite, (fsp), (fd), (data), (n), (off)))
+#define SMB_VFS_NEXT_LSEEK(handle, fsp, fd, offset, whence) ((handle)->vfs_next.ops.lseek((handle)->vfs_next.handles.lseek, (fsp), (fd), (offset), (whence)))
+#define SMB_VFS_NEXT_SENDFILE(handle, tofd, fsp, fromfd, header, offset, count) ((handle)->vfs_next.ops.sendfile((handle)->vfs_next.handles.sendfile, (tofd), (fsp), (fromfd), (header), (offset), (count)))
+#define SMB_VFS_NEXT_RENAME(handle, conn, old, new) ((handle)->vfs_next.ops.rename((handle)->vfs_next.handles.rename, (conn), (old), (new)))
+#define SMB_VFS_NEXT_FSYNC(handle, fsp, fd) ((handle)->vfs_next.ops.fsync((handle)->vfs_next.handles.fsync, (fsp), (fd)))
+#define SMB_VFS_NEXT_STAT(handle, conn, fname, sbuf) ((handle)->vfs_next.ops.stat((handle)->vfs_next.handles.stat, (conn), (fname), (sbuf)))
+#define SMB_VFS_NEXT_FSTAT(handle, fsp, fd, sbuf) ((handle)->vfs_next.ops.fstat((handle)->vfs_next.handles.fstat, (fsp) ,(fd) ,(sbuf)))
+#define SMB_VFS_NEXT_LSTAT(handle, conn, path, sbuf) ((handle)->vfs_next.ops.lstat((handle)->vfs_next.handles.lstat, (conn), (path), (sbuf)))
+#define SMB_VFS_NEXT_UNLINK(handle, conn, path) ((handle)->vfs_next.ops.unlink((handle)->vfs_next.handles.unlink, (conn), (path)))
+#define SMB_VFS_NEXT_CHMOD(handle, conn, path, mode) ((handle)->vfs_next.ops.chmod((handle)->vfs_next.handles.chmod, (conn), (path), (mode)))
+#define SMB_VFS_NEXT_FCHMOD(handle, fsp, fd, mode) ((handle)->vfs_next.ops.fchmod((handle)->vfs_next.handles.fchmod, (fsp), (fd), (mode)))
+#define SMB_VFS_NEXT_CHOWN(handle, conn, path, uid, gid) ((handle)->vfs_next.ops.chown((handle)->vfs_next.handles.chown, (conn), (path), (uid), (gid)))
+#define SMB_VFS_NEXT_FCHOWN(handle, fsp, fd, uid, gid) ((handle)->vfs_next.ops.fchown((handle)->vfs_next.handles.fchown, (fsp), (fd), (uid), (gid)))
+#define SMB_VFS_NEXT_CHDIR(handle, conn, path) ((handle)->vfs_next.ops.chdir((handle)->vfs_next.handles.chdir, (conn), (path)))
+#define SMB_VFS_NEXT_GETWD(handle, conn, buf) ((handle)->vfs_next.ops.getwd((handle)->vfs_next.handles.getwd, (conn), (buf)))
+#define SMB_VFS_NEXT_UTIME(handle, conn, path, times) ((handle)->vfs_next.ops.utime((handle)->vfs_next.handles.utime, (conn), (path), (times)))
+#define SMB_VFS_NEXT_FTRUNCATE(handle, fsp, fd, offset) ((handle)->vfs_next.ops.ftruncate((handle)->vfs_next.handles.ftruncate, (fsp), (fd), (offset)))
+#define SMB_VFS_NEXT_LOCK(handle, fsp, fd, op, offset, count, type) ((handle)->vfs_next.ops.lock((handle)->vfs_next.handles.lock, (fsp), (fd) ,(op), (offset), (count), (type)))
+#define SMB_VFS_NEXT_SYMLINK(handle, conn, oldpath, newpath) ((handle)->vfs_next.ops.symlink((handle)->vfs_next.handles.symlink, (conn), (oldpath), (newpath)))
+#define SMB_VFS_NEXT_READLINK(handle, conn, path, buf, bufsiz) ((handle)->vfs_next.ops.readlink((handle)->vfs_next.handles.readlink, (conn), (path), (buf), (bufsiz)))
+#define SMB_VFS_NEXT_LINK(handle, conn, oldpath, newpath) ((handle)->vfs_next.ops.link((handle)->vfs_next.handles.link, (conn), (oldpath), (newpath)))
+#define SMB_VFS_NEXT_MKNOD(handle, conn, path, mode, dev) ((handle)->vfs_next.ops.mknod((handle)->vfs_next.handles.mknod, (conn), (path), (mode), (dev)))
+#define SMB_VFS_NEXT_REALPATH(handle, conn, path, resolved_path) ((handle)->vfs_next.ops.realpath((handle)->vfs_next.handles.realpath, (conn), (path), (resolved_path)))
+
+/* NT ACL operations. */
+#define SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, fd, security_info, ppdesc) ((handle)->vfs_next.ops.fget_nt_acl((handle)->vfs_next.handles.fget_nt_acl, (fsp), (fd), (security_info), (ppdesc)))
+#define SMB_VFS_NEXT_GET_NT_ACL(handle, fsp, name, security_info, ppdesc) ((handle)->vfs_next.ops.get_nt_acl((handle)->vfs_next.handles.get_nt_acl, (fsp), (name), (security_info), (ppdesc)))
+#define SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, fd, security_info_sent, psd) ((handle)->vfs_next.ops.fset_nt_acl((handle)->vfs_next.handles.fset_nt_acl, (fsp), (fd), (security_info_sent), (psd)))
+#define SMB_VFS_NEXT_SET_NT_ACL(handle, fsp, name, security_info_sent, psd) ((handle)->vfs_next.ops.set_nt_acl((handle)->vfs_next.handles.set_nt_acl, (fsp), (name), (security_info_sent), (psd)))
+
+/* POSIX ACL operations. */
+#define SMB_VFS_NEXT_CHMOD_ACL(handle, conn, name, mode) ((handle)->vfs_next.ops.chmod_acl((handle)->vfs_next.handles.chmod_acl, (conn), (name), (mode)))
+#define SMB_VFS_NEXT_FCHMOD_ACL(handle, fsp, fd, mode) ((handle)->vfs_next.ops.fchmod_acl((handle)->vfs_next.handles.chmod_acl, (fsp), (fd), (mode)))
+
+#define SMB_VFS_NEXT_SYS_ACL_GET_ENTRY(handle, conn, theacl, entry_id, entry_p) ((handle)->vfs_next.ops.sys_acl_get_entry((handle)->vfs_next.handles.sys_acl_get_entry, (conn), (theacl), (entry_id), (entry_p)))
+#define SMB_VFS_NEXT_SYS_ACL_GET_TAG_TYPE(handle, conn, entry_d, tag_type_p) ((handle)->vfs_next.ops.sys_acl_get_tag_type((handle)->vfs_next.handles.sys_acl_get_tag_type, (conn), (entry_d), (tag_type_p)))
+#define SMB_VFS_NEXT_SYS_ACL_GET_PERMSET(handle, conn, entry_d, permset_p) ((handle)->vfs_next.ops.sys_acl_get_permset((handle)->vfs_next.handles.sys_acl_get_permset, (conn), (entry_d), (permset_p)))
+#define SMB_VFS_NEXT_SYS_ACL_GET_QUALIFIER(handle, conn, entry_d) ((handle)->vfs_next.ops.sys_acl_get_qualifier((handle)->vfs_next.handles.sys_acl_get_qualifier, (conn), (entry_d)))
+#define SMB_VFS_NEXT_SYS_ACL_GET_FILE(handle, conn, path_p, type) ((handle)->vfs_next.ops.sys_acl_get_file((handle)->vfs_next.handles.sys_acl_get_file, (conn), (path_p), (type)))
+#define SMB_VFS_NEXT_SYS_ACL_GET_FD(handle, fsp, fd) ((handle)->vfs_next.ops.sys_acl_get_fd((handle)->vfs_next.handles.sys_acl_get_fd, (fsp), (fd)))
+#define SMB_VFS_NEXT_SYS_ACL_CLEAR_PERMS(handle, conn, permset) ((handle)->vfs_next.ops.sys_acl_clear_perms((handle)->vfs_next.handles.sys_acl_clear_perms, (conn), (permset)))
+#define SMB_VFS_NEXT_SYS_ACL_ADD_PERM(handle, conn, permset, perm) ((handle)->vfs_next.ops.sys_acl_add_perm((handle)->vfs_next.handles.sys_acl_add_perm, (conn), (permset), (perm)))
+#define SMB_VFS_NEXT_SYS_ACL_TO_TEXT(handle, conn, theacl, plen) ((handle)->vfs_next.ops.sys_acl_to_text((handle)->vfs_next.handles.sys_acl_to_text, (conn), (theacl), (plen)))
+#define SMB_VFS_NEXT_SYS_ACL_INIT(handle, conn, count) ((handle)->vfs_next.ops.sys_acl_init((handle)->vfs_next.handles.sys_acl_init, (conn), (count)))
+#define SMB_VFS_NEXT_SYS_ACL_CREATE_ENTRY(handle, conn, pacl, pentry) ((handle)->vfs_next.ops.sys_acl_create_entry((handle)->vfs_next.handles.sys_acl_create_entry, (conn), (pacl), (pentry)))
+#define SMB_VFS_NEXT_SYS_ACL_SET_TAG_TYPE(handle, conn, entry, tagtype) ((handle)->vfs_next.ops.sys_acl_set_tag_type((handle)->vfs_next.handles.sys_acl_set_tag_type, (conn), (entry), (tagtype)))
+#define SMB_VFS_NEXT_SYS_ACL_SET_QUALIFIER(handle, conn, entry, qual) ((handle)->vfs_next.ops.sys_acl_set_qualifier((handle)->vfs_next.handles.sys_acl_set_qualifier, (conn), (entry), (qual)))
+#define SMB_VFS_NEXT_SYS_ACL_SET_PERMSET(handle, conn, entry, permset) ((handle)->vfs_next.ops.sys_acl_set_permset((handle)->vfs_next.handles.sys_acl_set_permset, (conn), (entry), (permset)))
+#define SMB_VFS_NEXT_SYS_ACL_VALID(handle, conn, theacl) ((handle)->vfs_next.ops.sys_acl_valid((handle)->vfs_next.handles.sys_acl_valid, (conn), (theacl)))
+#define SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, conn, name, acltype, theacl) ((handle)->vfs_next.ops.sys_acl_set_file((handle)->vfs_next.handles.sys_acl_set_file, (conn), (name), (acltype), (theacl)))
+#define SMB_VFS_NEXT_SYS_ACL_SET_FD(handle, fsp, fd, theacl) ((handle)->vfs_next.ops.sys_acl_set_fd((handle)->vfs_next.handles.sys_acl_set_fd, (fsp), (fd), (theacl)))
+#define SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(handle, conn, path) ((handle)->vfs_next.ops.sys_acl_delete_def_file((handle)->vfs_next.handles.sys_acl_delete_def_file, (conn), (path)))
+#define SMB_VFS_NEXT_SYS_ACL_GET_PERM(handle, conn, permset, perm) ((handle)->vfs_next.ops.sys_acl_get_perm((handle)->vfs_next.handles.sys_acl_get_perm, (conn), (permset), (perm)))
+#define SMB_VFS_NEXT_SYS_ACL_FREE_TEXT(handle, conn, text) ((handle)->vfs_next.ops.sys_acl_free_text((handle)->vfs_next.handles.sys_acl_free_text, (conn), (text)))
+#define SMB_VFS_NEXT_SYS_ACL_FREE_ACL(handle, conn, posix_acl) ((handle)->vfs_next.ops.sys_acl_free_acl((handle)->vfs_next.handles.sys_acl_free_acl, (conn), (posix_acl)))
+#define SMB_VFS_NEXT_SYS_ACL_FREE_QUALIFIER(handle, conn, qualifier, tagtype) ((handle)->vfs_next.ops.sys_acl_free_qualifier((handle)->vfs_next.handles.sys_acl_free_qualifier, (conn), (qualifier), (tagtype)))
+
+/* EA operations. */
+#define SMB_VFS_NEXT_GETXATTR(handle,conn,path,name,value,size) ((handle)->vfs_next.ops.getxattr((handle)->vfs_next.handles.getxattr,(conn),(path),(name),(value),(size)))
+#define SMB_VFS_NEXT_LGETXATTR(handle,conn,path,name,value,size) ((handle)->vfs_next.ops.lgetxattr((handle)->vfs_next.handles.lgetxattr,(conn),(path),(name),(value),(size)))
+#define SMB_VFS_NEXT_FGETXATTR(handle,fsp,fd,name,value,size) ((handle)->vfs_next.ops.fgetxattr((handle)->vfs_next.handles.fgetxattr,(fsp),(fd),(name),(value),(size)))
+#define SMB_VFS_NEXT_LISTXATTR(handle,conn,path,list,size) ((handle)->vfs_next.ops.listxattr((handle)->vfs_next.handles.listxattr,(conn),(path),(list),(size)))
+#define SMB_VFS_NEXT_LLISTXATTR(handle,conn,path,list,size) ((handle)->vfs_next.ops.llistxattr((handle)->vfs_next.handles.llistxattr,(conn),(path),(list),(size)))
+#define SMB_VFS_NEXT_FLISTXATTR(handle,fsp,fd,list,size) ((handle)->vfs_next.ops.flistxattr((handle)->vfs_next.handles.flistxattr,(fsp),(fd),(list),(size)))
+#define SMB_VFS_NEXT_REMOVEXATTR(handle,conn,path,name) ((handle)->vfs_next.ops.removexattr((handle)->vfs_next.handles.removexattr,(conn),(path),(name)))
+#define SMB_VFS_NEXT_LREMOVEXATTR(handle,conn,path,name) ((handle)->vfs_next.ops.lremovexattr((handle)->vfs_next.handles.lremovexattr,(conn),(path),(name)))
+#define SMB_VFS_NEXT_FREMOVEXATTR(handle,fsp,fd,name) ((handle)->vfs_next.ops.fremovexattr((handle)->vfs_next.handles.fremovexattr,(fsp),(fd),(name)))
+#define SMB_VFS_NEXT_SETXATTR(handle,conn,path,name,value,size,flags) ((handle)->vfs_next.ops.setxattr((handle)->vfs_next.handles.setxattr,(conn),(path),(name),(value),(size),(flags)))
+#define SMB_VFS_NEXT_LSETXATTR(handle,conn,path,name,value,size,flags) ((handle)->vfs_next.ops.lsetxattr((handle)->vfs_next.handles.lsetxattr,(conn),(path),(name),(value),(size),(flags)))
+#define SMB_VFS_NEXT_FSETXATTR(handle,fsp,fd,name,value,size,flags) ((handle)->vfs_next.ops.fsetxattr((handle)->vfs_next.handles.fsetxattr,(fsp),(fd),(name),(value),(size),(flags)))
+
+#endif /* _VFS_MACROS_H */
diff --git a/source/include/xfile.h b/source/include/xfile.h
new file mode 100644
index 00000000000..89fa9d1e118
--- /dev/null
+++ b/source/include/xfile.h
@@ -0,0 +1,49 @@
+/*
+ Unix SMB/CIFS implementation.
+ stdio replacement
+ Copyright (C) Andrew Tridgell 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _XFILE_H_
+#define _XFILE_H_
+/*
+ see xfile.c for explanations
+*/
+
+typedef struct {
+ int fd;
+ char *buf;
+ char *next;
+ int bufsize;
+ int bufused;
+ int open_flags;
+ int buftype;
+ int flags;
+} XFILE;
+
+extern XFILE *x_stdin, *x_stdout, *x_stderr;
+
+/* buffering type */
+#define X_IOFBF 0
+#define X_IOLBF 1
+#define X_IONBF 2
+
+#define x_getc(f) x_fgetc(f)
+
+int x_vfprintf(XFILE *f, const char *format, va_list ap) PRINTF_ATTRIBUTE(2, 0);
+int x_fprintf(XFILE *f, const char *format, ...) PRINTF_ATTRIBUTE(2, 3);
+#endif /* _XFILE_H_ */
diff --git a/source/install-sh b/source/install-sh
new file mode 100755
index 00000000000..58719246f04
--- /dev/null
+++ b/source/install-sh
@@ -0,0 +1,238 @@
+#! /bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/source/intl/.cvsignore b/source/intl/.cvsignore
new file mode 100644
index 00000000000..5f2a5c4cf75
--- /dev/null
+++ b/source/intl/.cvsignore
@@ -0,0 +1,2 @@
+*.po
+*.po32
diff --git a/source/intl/lang_tdb.c b/source/intl/lang_tdb.c
new file mode 100644
index 00000000000..fe2ad5b2fce
--- /dev/null
+++ b/source/intl/lang_tdb.c
@@ -0,0 +1,269 @@
+/*
+ Unix SMB/CIFS implementation.
+ tdb based replacement for gettext
+ Copyright (C) Andrew Tridgell 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static TDB_CONTEXT *tdb;
+
+/* the currently selected language */
+static char *current_lang;
+
+
+/* load a msg file into the tdb */
+static BOOL load_msg(const char *msg_file)
+{
+ char **lines;
+ int num_lines, i;
+ char *msgid, *msgstr;
+ TDB_DATA key, data;
+
+ lines = file_lines_load(msg_file, &num_lines);
+
+ if (!lines) {
+ return False;
+ }
+
+ if (tdb_lockall(tdb) != 0) return False;
+
+ /* wipe the db */
+ tdb_traverse(tdb, tdb_traverse_delete_fn, NULL);
+
+ msgid = NULL;
+
+ for (i=0;i<num_lines;i++) {
+ if (strncmp(lines[i], "msgid \"", 7) == 0) {
+ msgid = lines[i] + 7;
+ }
+ if (msgid && strncmp(lines[i], "msgstr \"", 8) == 0) {
+ msgstr = lines[i] + 8;
+ trim_char(msgid, '\0', '\"');
+ trim_char(msgstr, '\0', '\"');
+ if (*msgstr == 0) {
+ msgstr = msgid;
+ }
+ all_string_sub(msgid, "\\n", "\n", 0);
+ all_string_sub(msgstr, "\\n", "\n", 0);
+ key.dptr = msgid;
+ key.dsize = strlen(msgid)+1;
+ data.dptr = msgstr;
+ data.dsize = strlen(msgstr)+1;
+ tdb_store(tdb, key, data, 0);
+ msgid = NULL;
+ }
+ }
+
+ file_lines_free(lines);
+ tdb_unlockall(tdb);
+
+ return True;
+}
+
+
+/* work out what language to use from locale variables */
+static const char *get_lang(void)
+{
+ const char *vars[] = {"LANGUAGE", "LC_ALL", "LC_LANG", "LANG", NULL};
+ int i;
+ char *p;
+
+ for (i=0; vars[i]; i++) {
+ if ((p = getenv(vars[i]))) {
+ return p;
+ }
+ }
+
+ return NULL;
+}
+
+/* initialise the message translation subsystem. If the "lang" argument
+ is NULL then get the language from the normal environment variables */
+BOOL lang_tdb_init(const char *lang)
+{
+ char *path = NULL;
+ char *msg_path = NULL;
+ struct stat st;
+ static int initialised;
+ time_t loadtime;
+ BOOL result = False;
+
+ /* we only want to init once per process, unless given
+ an override */
+ if (initialised && !lang)
+ return True;
+
+ if (initialised) {
+ /* we are re-initialising, free up any old init */
+ if (tdb) {
+ tdb_close(tdb);
+ tdb = NULL;
+ }
+ SAFE_FREE(current_lang);
+ }
+
+ initialised = 1;
+
+ if (!lang) {
+ /* no lang given, use environment */
+ lang = get_lang();
+ }
+
+ /* if no lang then we don't translate */
+ if (!lang)
+ return True;
+
+ asprintf(&msg_path, "%s.msg", lib_path((const char *)lang));
+ if (stat(msg_path, &st) != 0) {
+ /* the msg file isn't available */
+ DEBUG(10, ("lang_tdb_init: %s: %s\n", msg_path,
+ strerror(errno)));
+ goto done;
+ }
+
+ asprintf(&path, "%s%s.tdb", lock_path("lang_"), lang);
+
+ DEBUG(10, ("lang_tdb_init: loading %s\n", path));
+
+ tdb = tdb_open_log(path, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0644);
+ if (!tdb) {
+ tdb = tdb_open_log(path, 0, TDB_DEFAULT, O_RDONLY, 0);
+ if (!tdb) {
+ DEBUG(10, ("lang_tdb_init: %s: %s\n", path,
+ strerror(errno)));
+ goto done;
+ }
+ current_lang = strdup(lang);
+ result = True;
+ goto done;
+ }
+
+ loadtime = tdb_fetch_int32(tdb, "/LOADTIME/");
+
+ if (loadtime == -1 || loadtime < st.st_mtime) {
+ load_msg(msg_path);
+ tdb_store_int32(tdb, "/LOADTIME/", (int)time(NULL));
+ }
+
+ current_lang = strdup(lang);
+ result = True;
+
+ done:
+ SAFE_FREE(msg_path);
+ SAFE_FREE(path);
+
+ return result;
+}
+
+/* translate a msgid to a message string in the current language
+ returns a string that must be freed by calling lang_msg_free()
+*/
+const char *lang_msg(const char *msgid)
+{
+ TDB_DATA key, data;
+ const char *p;
+ char *q, *msgid_quoted;
+ int count;
+
+ lang_tdb_init(NULL);
+
+ if (!tdb) return msgid;
+
+ /* Due to the way quotes in msgids are escaped in the msg file we
+ must replace " with \" before doing a lookup in the tdb. */
+
+ count = 0;
+
+ for(p = msgid; *p; p++) {
+ if (*p == '\"')
+ count++;
+ }
+
+ if (!(msgid_quoted = malloc(strlen(msgid) + count + 1)))
+ return msgid;
+
+ /* string_sub() is unsuitable here as it replaces some punctuation
+ chars with underscores. */
+
+ for(p = msgid, q = msgid_quoted; *p; p++) {
+ if (*p == '\"') {
+ *q = '\\';
+ q++;
+ }
+ *q = *p;
+ q++;
+ }
+
+ *q = 0;
+
+ key.dptr = (char *)msgid_quoted;
+ key.dsize = strlen(msgid_quoted)+1;
+
+ data = tdb_fetch(tdb, key);
+
+ free(msgid_quoted);
+
+ /* if the message isn't found then we still need to return a pointer
+ that can be freed. Pity. */
+ if (!data.dptr)
+ return strdup(msgid);
+
+ return (const char *)data.dptr;
+}
+
+
+/* free up a string from lang_msg() */
+void lang_msg_free(const char *msgstr)
+{
+ if (!tdb) return;
+ free((void *)msgstr);
+}
+
+
+/*
+ when the _() translation macro is used there is no obvious place to free
+ the resulting string and there is no easy way to give a static pointer.
+ All we can do is rotate between some static buffers and hope a single d_printf()
+ doesn't have more calls to _() than the number of buffers
+*/
+const char *lang_msg_rotate(const char *msgid)
+{
+#define NUM_LANG_BUFS 16
+ char *msgstr;
+ static pstring bufs[NUM_LANG_BUFS];
+ static int next;
+
+ msgstr = (char *)lang_msg(msgid);
+ if (!msgstr) return msgid;
+
+ pstrcpy(bufs[next], msgstr);
+ msgstr = bufs[next];
+
+ next = (next+1) % NUM_LANG_BUFS;
+
+ return msgstr;
+}
+
+
+/*
+ return the current language - needed for language file mappings
+*/
+char *lang_tdb_current(void)
+{
+ return current_lang;
+}
diff --git a/source/intl/linux-msg.sed b/source/intl/linux-msg.sed
new file mode 100644
index 00000000000..5918e720a9a
--- /dev/null
+++ b/source/intl/linux-msg.sed
@@ -0,0 +1,100 @@
+# po2msg.sed - Convert Uniforum style .po file to Linux style .msg file
+# Copyright (C) 1995 Free Software Foundation, Inc.
+# Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+#
+# The first directive in the .msg should be the definition of the
+# message set number. We use always set number 1.
+#
+1 {
+ i\
+$set 1 # Automatically created by po2msg.sed
+ h
+ s/.*/0/
+ x
+}
+#
+# Mitch's old catalog format does not allow comments.
+#
+# We copy the original message as a comment into the .msg file.
+#
+/^msgid/ {
+ s/msgid[ ]*"//
+#
+# This does not work now with the new format.
+# /"$/! {
+# s/\\$//
+# s/$/ ... (more lines following)"/
+# }
+ x
+# The following nice solution is by
+# Bruno <Haible@ma2s2.mathematik.uni-karlsruhe.de>
+ td
+# Increment a decimal number in pattern space.
+# First hide trailing `9' digits.
+ :d
+ s/9\(_*\)$/_\1/
+ td
+# Assure at least one digit is available.
+ s/^\(_*\)$/0\1/
+# Increment the last digit.
+ s/8\(_*\)$/9\1/
+ s/7\(_*\)$/8\1/
+ s/6\(_*\)$/7\1/
+ s/5\(_*\)$/6\1/
+ s/4\(_*\)$/5\1/
+ s/3\(_*\)$/4\1/
+ s/2\(_*\)$/3\1/
+ s/1\(_*\)$/2\1/
+ s/0\(_*\)$/1\1/
+# Convert the hidden `9' digits to `0's.
+ s/_/0/g
+ x
+ G
+ s/\(.*\)"\n\([0-9]*\)/$ #\2 Original Message:(\1)/p
+}
+#
+# The .msg file contains, other then the .po file, only the translations
+# but each given a unique ID. Starting from 1 and incrementing by 1 for
+# each message we assign them to the messages.
+# It is important that the .po file used to generate the cat-id-tbl.c file
+# (with po-to-tbl) is the same as the one used here. (At least the order
+# of declarations must not be changed.)
+#
+/^msgstr/ {
+ s/msgstr[ ]*"\(.*\)"/# \1/
+# Clear substitution flag.
+ tb
+# Append the next line.
+ :b
+ N
+# Look whether second part is continuation line.
+ s/\(.*\n\)"\(.*\)"/\1\2/
+# Yes, then branch.
+ ta
+ P
+ D
+# Note that D includes a jump to the start!!
+# We found a continuation line. But before printing insert '\'.
+ :a
+ s/\(.*\)\(\n.*\)/\1\\\2/
+ P
+# We cannot use D here.
+ s/.*\n\(.*\)/\1/
+ tb
+}
+d
diff --git a/source/lib/.cvsignore b/source/lib/.cvsignore
new file mode 100644
index 00000000000..07da2225c72
--- /dev/null
+++ b/source/lib/.cvsignore
@@ -0,0 +1,3 @@
+*.po
+*.po32
+
diff --git a/source/lib/access.c b/source/lib/access.c
new file mode 100644
index 00000000000..f03f5daf333
--- /dev/null
+++ b/source/lib/access.c
@@ -0,0 +1,333 @@
+/*
+ This module is an adaption of code from the tcpd-1.4 package written
+ by Wietse Venema, Eindhoven University of Technology, The Netherlands.
+
+ The code is used here with permission.
+
+ The code has been considerably changed from the original. Bug reports
+ should be sent to samba@samba.org
+*/
+
+#include "includes.h"
+
+#define FAIL (-1)
+
+#define ALLONES ((uint32)0xFFFFFFFF)
+
+/* masked_match - match address against netnumber/netmask */
+static BOOL masked_match(const char *tok, const char *slash, const char *s)
+{
+ uint32 net;
+ uint32 mask;
+ uint32 addr;
+ fstring tok_cpy;
+
+ if ((addr = interpret_addr(s)) == INADDR_NONE)
+ return (False);
+
+ fstrcpy(tok_cpy, tok);
+ tok_cpy[PTR_DIFF(slash,tok)] = '\0';
+ net = interpret_addr(tok_cpy);
+ tok_cpy[PTR_DIFF(slash,tok)] = '/';
+
+ if (strlen(slash + 1) > 2) {
+ mask = interpret_addr(slash + 1);
+ } else {
+ mask = (uint32)((ALLONES >> atoi(slash + 1)) ^ ALLONES);
+ /* convert to network byte order */
+ mask = htonl(mask);
+ }
+
+ if (net == INADDR_NONE || mask == INADDR_NONE) {
+ DEBUG(0,("access: bad net/mask access control: %s\n", tok));
+ return (False);
+ }
+
+ return ((addr & mask) == (net & mask));
+}
+
+/* string_match - match string against token */
+static BOOL string_match(const char *tok,const char *s, char *invalid_char)
+{
+ size_t tok_len;
+ size_t str_len;
+ const char *cut;
+
+ *invalid_char = '\0';
+
+ /* Return True if a token has the magic value "ALL". Return
+ * FAIL if the token is "FAIL". If the token starts with a "."
+ * (domain name), return True if it matches the last fields of
+ * the string. If the token has the magic value "LOCAL",
+ * return True if the string does not contain a "."
+ * character. If the token ends on a "." (network number),
+ * return True if it matches the first fields of the
+ * string. If the token begins with a "@" (netgroup name),
+ * return True if the string is a (host) member of the
+ * netgroup. Return True if the token fully matches the
+ * string. If the token is a netnumber/netmask pair, return
+ * True if the address is a member of the specified subnet.
+ */
+
+ if (tok[0] == '.') { /* domain: match last fields */
+ if ((str_len = strlen(s)) > (tok_len = strlen(tok))
+ && strequal(tok, s + str_len - tok_len))
+ return (True);
+ } else if (tok[0] == '@') { /* netgroup: look it up */
+#ifdef HAVE_NETGROUP
+ static char *mydomain = NULL;
+ char *hostname = NULL;
+ BOOL netgroup_ok = False;
+
+ if (!mydomain)
+ yp_get_default_domain(&mydomain);
+
+ if (!mydomain) {
+ DEBUG(0,("Unable to get default yp domain.\n"));
+ return False;
+ }
+ if (!(hostname = strdup(s))) {
+ DEBUG(1,("out of memory for strdup!\n"));
+ return False;
+ }
+
+ netgroup_ok = innetgr(tok + 1, hostname, (char *) 0, mydomain);
+
+ DEBUG(5,("looking for %s of domain %s in netgroup %s gave %s\n",
+ hostname,
+ mydomain,
+ tok+1,
+ BOOLSTR(netgroup_ok)));
+
+ SAFE_FREE(hostname);
+
+ if (netgroup_ok)
+ return(True);
+#else
+ DEBUG(0,("access: netgroup support is not configured\n"));
+ return (False);
+#endif
+ } else if (strequal(tok, "ALL")) { /* all: match any */
+ return (True);
+ } else if (strequal(tok, "FAIL")) { /* fail: match any */
+ return (FAIL);
+ } else if (strequal(tok, "LOCAL")) { /* local: no dots */
+ if (strchr_m(s, '.') == 0 && !strequal(s, "unknown"))
+ return (True);
+ } else if (strequal(tok, s)) { /* match host name or address */
+ return (True);
+ } else if (tok[(tok_len = strlen(tok)) - 1] == '.') { /* network */
+ if (strncmp(tok, s, tok_len) == 0)
+ return (True);
+ } else if ((cut = strchr_m(tok, '/')) != 0) { /* netnumber/netmask */
+ if (isdigit((int)s[0]) && masked_match(tok, cut, s))
+ return (True);
+ } else if (strchr_m(tok, '*') != 0) {
+ *invalid_char = '*';
+ } else if (strchr_m(tok, '?') != 0) {
+ *invalid_char = '?';
+ }
+ return (False);
+}
+
+/* client_match - match host name and address against token */
+static BOOL client_match(const char *tok, const char *item)
+{
+ const char **client = (const char **)item;
+ BOOL match;
+ char invalid_char = '\0';
+
+ /*
+ * Try to match the address first. If that fails, try to match the host
+ * name if available.
+ */
+
+ if ((match = string_match(tok, client[1], &invalid_char)) == 0) {
+ if(invalid_char)
+ DEBUG(0,("client_match: address match failing due to invalid character '%c' found in \
+token '%s' in an allow/deny hosts line.\n", invalid_char, tok ));
+
+ if (client[0][0] != 0)
+ match = string_match(tok, client[0], &invalid_char);
+
+ if(invalid_char)
+ DEBUG(0,("client_match: address match failing due to invalid character '%c' found in \
+token '%s' in an allow/deny hosts line.\n", invalid_char, tok ));
+ }
+
+ return (match);
+}
+
+/* list_match - match an item against a list of tokens with exceptions */
+static BOOL list_match(const char **list,const char *item,
+ BOOL (*match_fn)(const char *, const char *))
+{
+ BOOL match = False;
+
+ if (!list)
+ return False;
+
+ /*
+ * Process tokens one at a time. We have exhausted all possible matches
+ * when we reach an "EXCEPT" token or the end of the list. If we do find
+ * a match, look for an "EXCEPT" list and recurse to determine whether
+ * the match is affected by any exceptions.
+ */
+
+ for (; *list ; list++) {
+ if (strequal(*list, "EXCEPT")) /* EXCEPT: give up */
+ break;
+ if ((match = (*match_fn) (*list, item))) /* True or FAIL */
+ break;
+ }
+ /* Process exceptions to True or FAIL matches. */
+
+ if (match != False) {
+ while (*list && !strequal(*list, "EXCEPT"))
+ list++;
+
+ for (; *list; list++) {
+ if ((*match_fn) (*list, item)) /* Exception Found */
+ return False;
+ }
+ }
+
+ return (match);
+}
+
+/* return true if access should be allowed */
+static BOOL allow_access_internal(const char **deny_list,const char **allow_list,
+ const char *cname, const char *caddr)
+{
+ const char *client[2];
+
+ client[0] = cname;
+ client[1] = caddr;
+
+ /* if it is loopback then always allow unless specifically denied */
+ if (strcmp(caddr, "127.0.0.1") == 0) {
+ /*
+ * If 127.0.0.1 matches both allow and deny then allow.
+ * Patch from Steve Langasek vorlon@netexpress.net.
+ */
+ if (deny_list &&
+ list_match(deny_list,(const char *)client,client_match) &&
+ (!allow_list ||
+ !list_match(allow_list,(const char *)client, client_match))) {
+ return False;
+ }
+ return True;
+ }
+
+ /* if theres no deny list and no allow list then allow access */
+ if ((!deny_list || *deny_list == 0) &&
+ (!allow_list || *allow_list == 0)) {
+ return(True);
+ }
+
+ /* if there is an allow list but no deny list then allow only hosts
+ on the allow list */
+ if (!deny_list || *deny_list == 0)
+ return(list_match(allow_list,(const char *)client,client_match));
+
+ /* if theres a deny list but no allow list then allow
+ all hosts not on the deny list */
+ if (!allow_list || *allow_list == 0)
+ return(!list_match(deny_list,(const char *)client,client_match));
+
+ /* if there are both types of list then allow all hosts on the
+ allow list */
+ if (list_match(allow_list,(const char *)client,client_match))
+ return (True);
+
+ /* if there are both types of list and it's not on the allow then
+ allow it if its not on the deny */
+ if (list_match(deny_list,(const char *)client,client_match))
+ return (False);
+
+ return (True);
+}
+
+/* return true if access should be allowed */
+BOOL allow_access(const char **deny_list, const char **allow_list,
+ const char *cname, const char *caddr)
+{
+ BOOL ret;
+ char *nc_cname = smb_xstrdup(cname);
+ char *nc_caddr = smb_xstrdup(caddr);
+
+ ret = allow_access_internal(deny_list, allow_list, nc_cname, nc_caddr);
+
+ SAFE_FREE(nc_cname);
+ SAFE_FREE(nc_caddr);
+ return ret;
+}
+
+/* return true if the char* contains ip addrs only. Used to avoid
+gethostbyaddr() calls */
+
+static BOOL only_ipaddrs_in_list(const char** list)
+{
+ BOOL only_ip = True;
+
+ if (!list)
+ return True;
+
+ for (; *list ; list++) {
+ /* factor out the special strings */
+ if (strequal(*list, "ALL") || strequal(*list, "FAIL") ||
+ strequal(*list, "EXCEPT")) {
+ continue;
+ }
+
+ if (!is_ipaddress(*list)) {
+ /*
+ * if we failed, make sure that it was not because the token
+ * was a network/netmask pair. Only network/netmask pairs
+ * have a '/' in them
+ */
+ if ((strchr_m(*list, '/')) == NULL) {
+ only_ip = False;
+ DEBUG(3,("only_ipaddrs_in_list: list has non-ip address (%s)\n", *list));
+ break;
+ }
+ }
+ }
+
+ return only_ip;
+}
+
+/* return true if access should be allowed to a service for a socket */
+BOOL check_access(int sock, const char **allow_list, const char **deny_list)
+{
+ BOOL ret = False;
+ BOOL only_ip = False;
+
+ if ((!deny_list || *deny_list==0) && (!allow_list || *allow_list==0))
+ ret = True;
+
+ if (!ret) {
+ /* bypass gethostbyaddr() calls if the lists only contain IP addrs */
+ if (only_ipaddrs_in_list(allow_list) && only_ipaddrs_in_list(deny_list)) {
+ only_ip = True;
+ DEBUG (3, ("check_access: no hostnames in host allow/deny list.\n"));
+ ret = allow_access(deny_list,allow_list, "", get_peer_addr(sock));
+ } else {
+ DEBUG (3, ("check_access: hostnames in host allow/deny list.\n"));
+ ret = allow_access(deny_list,allow_list, get_peer_name(sock,True),
+ get_peer_addr(sock));
+ }
+
+ if (ret) {
+ DEBUG(2,("Allowed connection from %s (%s)\n",
+ only_ip ? "" : get_peer_name(sock,True),
+ get_peer_addr(sock)));
+ } else {
+ DEBUG(0,("Denied connection from %s (%s)\n",
+ only_ip ? "" : get_peer_name(sock,True),
+ get_peer_addr(sock)));
+ }
+ }
+
+ return(ret);
+}
diff --git a/source/lib/account_pol.c b/source/lib/account_pol.c
new file mode 100644
index 00000000000..c2c63493a6f
--- /dev/null
+++ b/source/lib/account_pol.c
@@ -0,0 +1,162 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * account policy storage
+ * Copyright (C) Jean François Micouleau 1998-2001.
+ * Copyright (C) Andrew Bartlett 2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+static TDB_CONTEXT *tdb; /* used for driver files */
+
+#define DATABASE_VERSION 1
+
+/****************************************************************************
+ Open the account policy tdb.
+****************************************************************************/
+
+BOOL init_account_policy(void)
+{
+ static pid_t local_pid;
+ const char *vstring = "INFO/version";
+ uint32 version;
+
+ if (tdb && local_pid == sys_getpid())
+ return True;
+ tdb = tdb_open_log(lock_path("account_policy.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+ if (!tdb) {
+ DEBUG(0,("Failed to open account policy database\n"));
+ return False;
+ }
+
+ local_pid = sys_getpid();
+
+ /* handle a Samba upgrade */
+ tdb_lock_bystring(tdb, vstring,0);
+ if (!tdb_fetch_uint32(tdb, vstring, &version) || version != DATABASE_VERSION) {
+ tdb_traverse(tdb, tdb_traverse_delete_fn, NULL);
+ tdb_store_uint32(tdb, vstring, DATABASE_VERSION);
+
+ account_policy_set(AP_MIN_PASSWORD_LEN, MINPASSWDLENGTH); /* 5 chars minimum */
+ account_policy_set(AP_PASSWORD_HISTORY, 0); /* don't keep any old password */
+ account_policy_set(AP_USER_MUST_LOGON_TO_CHG_PASS, 0); /* don't force user to logon */
+ account_policy_set(AP_MAX_PASSWORD_AGE, (uint32)-1); /* don't expire */
+ account_policy_set(AP_MIN_PASSWORD_AGE, 0); /* 0 days */
+ account_policy_set(AP_LOCK_ACCOUNT_DURATION, 30); /* lockout for 30 minutes */
+ account_policy_set(AP_RESET_COUNT_TIME, 30); /* reset after 30 minutes */
+ account_policy_set(AP_BAD_ATTEMPT_LOCKOUT, 0); /* don't lockout */
+ account_policy_set(AP_TIME_TO_LOGOUT, -1); /* don't force logout */
+ }
+ tdb_unlock_bystring(tdb, vstring);
+
+ return True;
+}
+
+static const struct {
+ int field;
+ const char *string;
+} account_policy_names[] = {
+ {AP_MIN_PASSWORD_LEN, "min password length"},
+ {AP_PASSWORD_HISTORY, "password history"},
+ {AP_USER_MUST_LOGON_TO_CHG_PASS, "user must logon to change password"},
+ {AP_MAX_PASSWORD_AGE, "maximum password age"},
+ {AP_MIN_PASSWORD_AGE,"minimum password age"},
+ {AP_LOCK_ACCOUNT_DURATION, "lockout duration"},
+ {AP_RESET_COUNT_TIME, "reset count minutes"},
+ {AP_BAD_ATTEMPT_LOCKOUT, "bad lockout attempt"},
+ {AP_TIME_TO_LOGOUT, "disconnect time"},
+ {0, NULL}
+};
+
+/****************************************************************************
+Get the account policy name as a string from its #define'ed number
+****************************************************************************/
+
+static const char *decode_account_policy_name(int field)
+{
+ int i;
+ for (i=0; account_policy_names[i].string; i++) {
+ if (field == account_policy_names[i].field)
+ return account_policy_names[i].string;
+ }
+ return NULL;
+
+}
+
+/****************************************************************************
+Get the account policy name as a string from its #define'ed number
+****************************************************************************/
+
+int account_policy_name_to_fieldnum(const char *name)
+{
+ int i;
+ for (i=0; account_policy_names[i].string; i++) {
+ if (strcmp(name, account_policy_names[i].string) == 0)
+ return account_policy_names[i].field;
+ }
+ return 0;
+
+}
+
+
+/****************************************************************************
+****************************************************************************/
+BOOL account_policy_get(int field, uint32 *value)
+{
+ fstring name;
+
+ if(!init_account_policy())return False;
+
+ *value = 0;
+
+ fstrcpy(name, decode_account_policy_name(field));
+ if (!*name) {
+ DEBUG(1, ("account_policy_get: Field %d is not a valid account policy type! Cannot get, returning 0.\n", field));
+ return False;
+ }
+ if (!tdb_fetch_uint32(tdb, name, value)) {
+ DEBUG(1, ("account_policy_get: tdb_fetch_uint32 failed for efild %d (%s), returning 0", field, name));
+ return False;
+ }
+ DEBUG(10,("account_policy_get: %s:%d\n", name, *value));
+ return True;
+}
+
+
+/****************************************************************************
+****************************************************************************/
+BOOL account_policy_set(int field, uint32 value)
+{
+ fstring name;
+
+ if(!init_account_policy())return False;
+
+ fstrcpy(name, decode_account_policy_name(field));
+ if (!*name) {
+ DEBUG(1, ("Field %d is not a valid account policy type! Cannot set.\n", field));
+ return False;
+ }
+
+ if (!tdb_store_uint32(tdb, name, value)) {
+ DEBUG(1, ("tdb_store_uint32 failed for field %d (%s) on value %u", field, name, value));
+ return False;
+ }
+
+ DEBUG(10,("account_policy_set: %s:%d\n", name, value));
+
+ return True;
+}
+
diff --git a/source/lib/adt_tree.c b/source/lib/adt_tree.c
new file mode 100644
index 00000000000..bd857e205ac
--- /dev/null
+++ b/source/lib/adt_tree.c
@@ -0,0 +1,464 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Generic Abstract Data Types
+ * Copyright (C) Gerald Carter 2002.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+
+/**************************************************************************
+ Initialize the tree's root. The cmp_fn is a callback function used
+ for comparision of two children
+ *************************************************************************/
+
+static BOOL trim_tree_keypath( char *path, char **base, char **new_path )
+{
+ char *p;
+
+ *new_path = *base = NULL;
+
+ if ( !path )
+ return False;
+
+ *base = path;
+
+ p = strchr( path, '/' );
+
+ if ( p ) {
+ *p = '\0';
+ *new_path = p+1;
+ }
+
+ return True;
+}
+
+
+/**************************************************************************
+ Initialize the tree's root. The cmp_fn is a callback function used
+ for comparision of two children
+ *************************************************************************/
+
+SORTED_TREE* sorted_tree_init( void *data_p,
+ int (cmp_fn)(void*, void*),
+ void (free_fn)(void*) )
+{
+ SORTED_TREE *tree = NULL;
+
+ if ( !(tree = (SORTED_TREE*)malloc( sizeof(SORTED_TREE) )) )
+ return NULL;
+
+ ZERO_STRUCTP( tree );
+
+ tree->compare = cmp_fn;
+ tree->free_func = free_fn;
+
+ if ( !(tree->root = (TREE_NODE*)malloc( sizeof(TREE_NODE) )) ) {
+ SAFE_FREE( tree );
+ return NULL;
+ }
+
+ ZERO_STRUCTP( tree->root );
+ tree->root->data_p = data_p;
+
+ return tree;
+}
+
+
+/**************************************************************************
+ Delete a tree and free all allocated memory
+ *************************************************************************/
+
+static void sorted_tree_destroy_children( TREE_NODE *root )
+{
+ int i;
+
+ if ( !root )
+ return;
+
+ for ( i=0; i<root->num_children; i++ )
+ {
+ sorted_tree_destroy_children( root->children[i] );
+ }
+
+ SAFE_FREE( root->children );
+ SAFE_FREE( root->key );
+
+ return;
+}
+
+/**************************************************************************
+ Delete a tree and free all allocated memory
+ *************************************************************************/
+
+void sorted_tree_destroy( SORTED_TREE *tree )
+{
+ if ( tree->root )
+ sorted_tree_destroy_children( tree->root );
+
+ if ( tree->free_func )
+ tree->free_func( tree->root );
+
+ SAFE_FREE( tree );
+}
+
+/**************************************************************************
+ Find the next child given a key string
+ *************************************************************************/
+
+static TREE_NODE* sorted_tree_birth_child( TREE_NODE *node, char* key )
+{
+ TREE_NODE *infant = NULL;
+ TREE_NODE **siblings;
+ int i;
+
+ if ( !(infant = (TREE_NODE*)malloc( sizeof(TREE_NODE) )) )
+ return NULL;
+
+ ZERO_STRUCTP( infant );
+
+ infant->key = strdup( key );
+ infant->parent = node;
+
+ siblings = Realloc( node->children, sizeof(TREE_NODE*)*(node->num_children+1) );
+
+ if ( siblings )
+ node->children = siblings;
+
+ node->num_children++;
+
+ /* first child */
+
+ if ( node->num_children == 1 ) {
+ DEBUG(11,("sorted_tree_birth_child: First child of node [%s]! [%s]\n",
+ node->key ? node->key : "NULL", infant->key ));
+ node->children[0] = infant;
+ }
+ else
+ {
+ /*
+ * multiple siblings .... (at least 2 children)
+ *
+ * work from the end of the list forward
+ * The last child is not set at this point
+ * Insert the new infanct in ascending order
+ * from left to right
+ */
+
+ for ( i = node->num_children-1; i>=1; i-- )
+ {
+ DEBUG(11,("sorted_tree_birth_child: Looking for crib; infant -> [%s], child -> [%s]\n",
+ infant->key, node->children[i-1]->key));
+
+ /* the strings should never match assuming that we
+ have called sorted_tree_find_child() first */
+
+ if ( StrCaseCmp( infant->key, node->children[i-1]->key ) > 0 ) {
+ DEBUG(11,("sorted_tree_birth_child: storing infant in i == [%d]\n",
+ i));
+ node->children[i] = infant;
+ break;
+ }
+
+ /* bump everything towards the end on slot */
+
+ node->children[i] = node->children[i-1];
+ }
+
+ DEBUG(11,("sorted_tree_birth_child: Exiting loop (i == [%d])\n", i ));
+
+ /* if we haven't found the correct slot yet, the child
+ will be first in the list */
+
+ if ( i == 0 )
+ node->children[0] = infant;
+ }
+
+ return infant;
+}
+
+/**************************************************************************
+ Find the next child given a key string
+ *************************************************************************/
+
+static TREE_NODE* sorted_tree_find_child( TREE_NODE *node, char* key )
+{
+ TREE_NODE *next = NULL;
+ int i, result;
+
+ if ( !node ) {
+ DEBUG(0,("sorted_tree_find_child: NULL node passed into function!\n"));
+ return NULL;
+ }
+
+ if ( !key ) {
+ DEBUG(0,("sorted_tree_find_child: NULL key string passed into function!\n"));
+ return NULL;
+ }
+
+ for ( i=0; i<node->num_children; i++ )
+ {
+ DEBUG(11,("sorted_tree_find_child: child key => [%s]\n",
+ node->children[i]->key));
+
+ result = StrCaseCmp( node->children[i]->key, key );
+
+ if ( result == 0 )
+ next = node->children[i];
+
+ /* if result > 0 then we've gone to far because
+ the list of children is sorted by key name
+ If result == 0, then we have a match */
+
+ if ( result > 0 )
+ break;
+ }
+
+ DEBUG(11,("sorted_tree_find_child: %s [%s]\n",
+ next ? "Found" : "Did not find", key ));
+
+ return next;
+}
+
+/**************************************************************************
+ Add a new node into the tree given a key path and a blob of data
+ *************************************************************************/
+
+BOOL sorted_tree_add( SORTED_TREE *tree, const char *path, void *data_p )
+{
+ char *str, *base, *path2;
+ TREE_NODE *current, *next;
+ BOOL ret = True;
+
+ DEBUG(8,("sorted_tree_add: Enter\n"));
+
+ if ( !path || *path != '/' ) {
+ DEBUG(0,("sorted_tree_add: Attempt to add a node with a bad path [%s]\n",
+ path ? path : "NULL" ));
+ return False;
+ }
+
+ if ( !tree ) {
+ DEBUG(0,("sorted_tree_add: Attempt to add a node to an uninitialized tree!\n"));
+ return False;
+ }
+
+ /* move past the first '/' */
+
+ path++;
+ path2 = strdup( path );
+ if ( !path2 ) {
+ DEBUG(0,("sorted_tree_add: strdup() failed on string [%s]!?!?!\n", path));
+ return False;
+ }
+
+
+ /*
+ * this works sort of like a 'mkdir -p' call, possibly
+ * creating an entire path to the new node at once
+ * The path should be of the form /<key1>/<key2>/...
+ */
+
+ base = path2;
+ str = path2;
+ current = tree->root;
+
+ do {
+ /* break off the remaining part of the path */
+
+ str = strchr( str, '/' );
+ if ( str )
+ *str = '\0';
+
+ /* iterate to the next child--birth it if necessary */
+
+ next = sorted_tree_find_child( current, base );
+ if ( !next ) {
+ next = sorted_tree_birth_child( current, base );
+ if ( !next ) {
+ DEBUG(0,("sorted_tree_add: Failed to create new child!\n"));
+ ret = False;
+ goto done;
+ }
+ }
+ current = next;
+
+ /* setup the next part of the path */
+
+ base = str;
+ if ( base ) {
+ *base = '/';
+ base++;
+ str = base;
+ }
+
+ } while ( base != NULL );
+
+ current->data_p = data_p;
+
+ DEBUG(10,("sorted_tree_add: Successfully added node [%s] to tree\n",
+ path ));
+
+ DEBUG(8,("sorted_tree_add: Exit\n"));
+
+done:
+ SAFE_FREE( path2 );
+ return ret;
+}
+
+
+/**************************************************************************
+ Recursive routine to print out all children of a TREE_NODE
+ *************************************************************************/
+
+static void sorted_tree_print_children( TREE_NODE *node, int debug, const char *path )
+{
+ int i;
+ int num_children;
+ pstring path2;
+
+ if ( !node )
+ return;
+
+
+ if ( node->key )
+ DEBUG(debug,("%s: [%s] (%s)\n", path ? path : "NULL", node->key,
+ node->data_p ? "data" : "NULL" ));
+
+ *path2 = '\0';
+ if ( path )
+ pstrcpy( path2, path );
+ pstrcat( path2, node->key ? node->key : "NULL" );
+ pstrcat( path2, "/" );
+
+ num_children = node->num_children;
+ for ( i=0; i<num_children; i++ )
+ sorted_tree_print_children( node->children[i], debug, path2 );
+
+
+}
+
+/**************************************************************************
+ Dump the kys for a tree to the log file
+ *************************************************************************/
+
+void sorted_tree_print_keys( SORTED_TREE *tree, int debug )
+{
+ int i;
+ int num_children = tree->root->num_children;
+
+ if ( tree->root->key )
+ DEBUG(debug,("ROOT/: [%s] (%s)\n", tree->root->key,
+ tree->root->data_p ? "data" : "NULL" ));
+
+ for ( i=0; i<num_children; i++ ) {
+ sorted_tree_print_children( tree->root->children[i], debug,
+ tree->root->key ? tree->root->key : "ROOT/" );
+ }
+
+}
+
+/**************************************************************************
+ return the data_p for for the node in tree matching the key string
+ The key string is the full path. We must break it apart and walk
+ the tree
+ *************************************************************************/
+
+void* sorted_tree_find( SORTED_TREE *tree, char *key )
+{
+ char *keystr, *base, *str, *p;
+ TREE_NODE *current;
+ void *result = NULL;
+
+ DEBUG(10,("sorted_tree_find: Enter [%s]\n", key ? key : "NULL" ));
+
+ /* sanity checks first */
+
+ if ( !key ) {
+ DEBUG(0,("sorted_tree_find: Attempt to search tree using NULL search string!\n"));
+ return NULL;
+ }
+
+ if ( !tree ) {
+ DEBUG(0,("sorted_tree_find: Attempt to search an uninitialized tree using string [%s]!\n",
+ key ? key : "NULL" ));
+ return NULL;
+ }
+
+ if ( !tree->root )
+ return NULL;
+
+ /* make a copy to play with */
+
+ if ( *key == '/' )
+ keystr = strdup( key+1 );
+ else
+ keystr = strdup( key );
+
+ if ( !keystr ) {
+ DEBUG(0,("sorted_tree_find: strdup() failed on string [%s]!?!?!\n", key));
+ return NULL;
+ }
+
+ /* start breaking the path apart */
+
+ p = keystr;
+ current = tree->root;
+
+ if ( tree->root->data_p )
+ result = tree->root->data_p;
+
+ do
+ {
+ /* break off the remaining part of the path */
+
+ trim_tree_keypath( p, &base, &str );
+
+ DEBUG(11,("sorted_tree_find: [loop] base => [%s], new_path => [%s]\n",
+ base, str));
+
+ /* iterate to the next child */
+
+ current = sorted_tree_find_child( current, base );
+
+ /*
+ * the idea is that the data_p for a parent should
+ * be inherited by all children, but allow it to be
+ * overridden farther down
+ */
+
+ if ( current && current->data_p )
+ result = current->data_p;
+
+ /* reset the path pointer 'p' to the remaining part of the key string */
+
+ p = str;
+
+ } while ( str && current );
+
+ /* result should be the data_p from the lowest match node in the tree */
+ if ( result )
+ DEBUG(11,("sorted_tree_find: Found data_p!\n"));
+
+ SAFE_FREE( keystr );
+
+ DEBUG(10,("sorted_tree_find: Exit\n"));
+
+ return result;
+}
+
+
diff --git a/source/lib/afs.c b/source/lib/afs.c
new file mode 100644
index 00000000000..ce972ec27b7
--- /dev/null
+++ b/source/lib/afs.c
@@ -0,0 +1,486 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Generate AFS tickets
+ * Copyright (C) Volker Lendecke 2003
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+#ifdef WITH_FAKE_KASERVER
+
+#include <afs/stds.h>
+#include <afs/afs.h>
+#include <afs/auth.h>
+#include <afs/venus.h>
+#include <asm/unistd.h>
+#include <openssl/des.h>
+
+_syscall5(int, afs_syscall, int, subcall,
+ char *, path,
+ int, cmd,
+ char *, cmarg,
+ int, follow);
+
+struct ClearToken {
+ uint32 AuthHandle;
+ char HandShakeKey[8];
+ uint32 ViceId;
+ uint32 BeginTimestamp;
+ uint32 EndTimestamp;
+};
+
+static char *afs_encode_token(const char *cell, const DATA_BLOB ticket,
+ const struct ClearToken *ct)
+{
+ char *base64_ticket;
+ char *result;
+
+ DATA_BLOB key = data_blob(ct->HandShakeKey, 8);
+ char *base64_key;
+
+ base64_ticket = base64_encode_data_blob(ticket);
+ if (base64_ticket == NULL)
+ return NULL;
+
+ base64_key = base64_encode_data_blob(key);
+ if (base64_key == NULL) {
+ free(base64_ticket);
+ return NULL;
+ }
+
+ asprintf(&result, "%s\n%u\n%s\n%u\n%u\n%u\n%s\n", cell,
+ ct->AuthHandle, base64_key, ct->ViceId, ct->BeginTimestamp,
+ ct->EndTimestamp, base64_ticket);
+
+ DEBUG(10, ("Got ticket string:\n%s\n", result));
+
+ free(base64_ticket);
+ free(base64_key);
+
+ return result;
+}
+
+static BOOL afs_decode_token(const char *string, char **cell,
+ DATA_BLOB *ticket, struct ClearToken *ct)
+{
+ DATA_BLOB blob;
+ struct ClearToken result_ct;
+
+ char *s = strdup(string);
+
+ char *t;
+
+ if ((t = strtok(s, "\n")) == NULL) {
+ DEBUG(10, ("strtok failed\n"));
+ return False;
+ }
+
+ *cell = strdup(t);
+
+ if ((t = strtok(NULL, "\n")) == NULL) {
+ DEBUG(10, ("strtok failed\n"));
+ return False;
+ }
+
+ if (sscanf(t, "%u", &result_ct.AuthHandle) != 1) {
+ DEBUG(10, ("sscanf AuthHandle failed\n"));
+ return False;
+ }
+
+ if ((t = strtok(NULL, "\n")) == NULL) {
+ DEBUG(10, ("strtok failed\n"));
+ return False;
+ }
+
+ blob = base64_decode_data_blob(t);
+
+ if ( (blob.data == NULL) ||
+ (blob.length != sizeof(result_ct.HandShakeKey) )) {
+ DEBUG(10, ("invalid key: %x/%d\n", (uint32)blob.data,
+ blob.length));
+ return False;
+ }
+
+ memcpy(result_ct.HandShakeKey, blob.data, blob.length);
+
+ data_blob_free(&blob);
+
+ if ((t = strtok(NULL, "\n")) == NULL) {
+ DEBUG(10, ("strtok failed\n"));
+ return False;
+ }
+
+ if (sscanf(t, "%u", &result_ct.ViceId) != 1) {
+ DEBUG(10, ("sscanf ViceId failed\n"));
+ return False;
+ }
+
+ if ((t = strtok(NULL, "\n")) == NULL) {
+ DEBUG(10, ("strtok failed\n"));
+ return False;
+ }
+
+ if (sscanf(t, "%u", &result_ct.BeginTimestamp) != 1) {
+ DEBUG(10, ("sscanf BeginTimestamp failed\n"));
+ return False;
+ }
+
+ if ((t = strtok(NULL, "\n")) == NULL) {
+ DEBUG(10, ("strtok failed\n"));
+ return False;
+ }
+
+ if (sscanf(t, "%u", &result_ct.EndTimestamp) != 1) {
+ DEBUG(10, ("sscanf EndTimestamp failed\n"));
+ return False;
+ }
+
+ if ((t = strtok(NULL, "\n")) == NULL) {
+ DEBUG(10, ("strtok failed\n"));
+ return False;
+ }
+
+ blob = base64_decode_data_blob(t);
+
+ if (blob.data == NULL) {
+ DEBUG(10, ("Could not get ticket\n"));
+ return False;
+ }
+
+ *ticket = blob;
+ *ct = result_ct;
+
+ return True;
+}
+
+/*
+ Put an AFS token into the Kernel so that it can authenticate against
+ the AFS server. This assumes correct local uid settings.
+
+ This is currently highly Linux and OpenAFS-specific. The correct API
+ call for this would be ktc_SetToken. But to do that we would have to
+ import a REALLY big bunch of libraries which I would currently like
+ to avoid.
+*/
+
+static BOOL afs_settoken(const char *cell,
+ const struct ClearToken *ctok,
+ DATA_BLOB ticket)
+{
+ int ret;
+ struct {
+ char *in, *out;
+ uint16 in_size, out_size;
+ } iob;
+
+ char buf[1024];
+ char *p = buf;
+ int tmp;
+
+ memcpy(p, &ticket.length, sizeof(uint32));
+ p += sizeof(uint32);
+ memcpy(p, ticket.data, ticket.length);
+ p += ticket.length;
+
+ tmp = sizeof(struct ClearToken);
+ memcpy(p, &tmp, sizeof(uint32));
+ p += sizeof(uint32);
+ memcpy(p, ctok, tmp);
+ p += tmp;
+
+ tmp = 0;
+
+ memcpy(p, &tmp, sizeof(uint32));
+ p += sizeof(uint32);
+
+ tmp = strlen(cell);
+ if (tmp >= MAXKTCREALMLEN) {
+ DEBUG(1, ("Realm too long\n"));
+ return False;
+ }
+
+ strncpy(p, cell, tmp);
+ p += tmp;
+ *p = 0;
+ p +=1;
+
+ iob.in = buf;
+ iob.in_size = PTR_DIFF(p,buf);
+ iob.out = buf;
+ iob.out_size = sizeof(buf);
+
+#if 0
+ file_save("/tmp/ioctlbuf", iob.in, iob.in_size);
+#endif
+
+ ret = afs_syscall(AFSCALL_PIOCTL, 0, VIOCSETTOK, (char *)&iob, 0);
+
+ DEBUG(10, ("afs VIOCSETTOK returned %d\n", ret));
+ return (ret == 0);
+}
+
+BOOL afs_settoken_str(const char *token_string)
+{
+ DATA_BLOB ticket;
+ struct ClearToken ct;
+ BOOL result;
+ char *cell;
+
+ if (!afs_decode_token(token_string, &cell, &ticket, &ct))
+ return False;
+
+ if (geteuid() != 0)
+ ct.ViceId = getuid();
+
+ result = afs_settoken(cell, &ct, ticket);
+
+ SAFE_FREE(cell);
+ data_blob_free(&ticket);
+
+ return result;
+ }
+
+/* Create a ClearToken and an encrypted ticket. ClearToken has not yet the
+ * ViceId set, this should be set by the caller. */
+
+static BOOL afs_createtoken(const char *username, const char *cell,
+ DATA_BLOB *ticket, struct ClearToken *ct)
+{
+ fstring clear_ticket;
+ char *p = clear_ticket;
+ uint32 len;
+ uint32 now;
+
+ struct afs_key key;
+ des_key_schedule key_schedule;
+
+ if (!secrets_init())
+ return False;
+
+ if (!secrets_fetch_afs_key(cell, &key)) {
+ DEBUG(1, ("Could not fetch AFS service key\n"));
+ return False;
+ }
+
+ ct->AuthHandle = key.kvno;
+
+ /* Build the ticket. This is going to be encrypted, so in our
+ way we fill in ct while we still have the unencrypted
+ form. */
+
+ p = clear_ticket;
+
+ /* The byte-order */
+ *p = 1;
+ p += 1;
+
+ /* "Alice", the client username */
+ strncpy(p, username, sizeof(clear_ticket)-PTR_DIFF(p,clear_ticket)-1);
+ p += strlen(p)+1;
+ strncpy(p, "", sizeof(clear_ticket)-PTR_DIFF(p,clear_ticket)-1);
+ p += strlen(p)+1;
+ strncpy(p, cell, sizeof(clear_ticket)-PTR_DIFF(p,clear_ticket)-1);
+ p += strlen(p)+1;
+
+ /* Alice's network layer address. At least Openafs-1.2.10
+ ignores this, so we fill in a dummy value here. */
+ SIVAL(p, 0, 0);
+ p += 4;
+
+ /* We need to create a session key */
+ generate_random_buffer(p, 8, False);
+
+ /* Our client code needs the the key in the clear, it does not
+ know the server-key ... */
+ memcpy(ct->HandShakeKey, p, 8);
+
+ p += 8;
+
+ /* Ticket lifetime. We fake everything here, so go as long as
+ possible. This is in 5-minute intervals, so 255 is 21 hours
+ and 15 minutes.*/
+ *p = 255;
+ p += 1;
+
+ /* Ticket creation time */
+ now = time(NULL);
+ SIVAL(p, 0, now);
+ ct->BeginTimestamp = now;
+
+ ct->EndTimestamp = now + (255*60*5);
+ if (((ct->EndTimestamp - ct->BeginTimestamp) & 1) == 1) {
+ ct->BeginTimestamp += 1; /* Lifetime must be even */
+ }
+ p += 4;
+
+ /* And here comes Bob's name and instance, in this case the
+ AFS server. */
+ strncpy(p, "afs", sizeof(clear_ticket)-PTR_DIFF(p,clear_ticket)-1);
+ p += strlen(p)+1;
+ strncpy(p, "", sizeof(clear_ticket)-PTR_DIFF(p,clear_ticket)-1);
+ p += strlen(p)+1;
+
+ /* And zero-pad to a multiple of 8 bytes */
+ len = PTR_DIFF(p, clear_ticket);
+ if (len & 7) {
+ uint32 extra_space = 8-(len & 7);
+ memset(p, 0, extra_space);
+ p+=extra_space;
+ }
+ len = PTR_DIFF(p, clear_ticket);
+
+ des_key_sched((const_des_cblock *)key.key, key_schedule);
+ des_pcbc_encrypt(clear_ticket, clear_ticket,
+ len, key_schedule, (C_Block *)key.key, 1);
+
+ ZERO_STRUCT(key);
+
+ *ticket = data_blob(clear_ticket, len);
+
+ return True;
+}
+
+char *afs_createtoken_str(const char *username, const char *cell)
+{
+ DATA_BLOB ticket;
+ struct ClearToken ct;
+ char *result;
+
+ if (!afs_createtoken(username, cell, &ticket, &ct))
+ return NULL;
+
+ result = afs_encode_token(cell, ticket, &ct);
+
+ data_blob_free(&ticket);
+
+ return result;
+}
+
+/*
+ This routine takes a radical approach completely bypassing the
+ Kerberos idea of security and using AFS simply as an intelligent
+ file backend. Samba has persuaded itself somehow that the user is
+ actually correctly identified and then we create a ticket that the
+ AFS server hopefully accepts using its KeyFile that the admin has
+ kindly stored to our secrets.tdb.
+
+ Thanks to the book "Network Security -- PRIVATE Communication in a
+ PUBLIC World" by Charlie Kaufman, Radia Perlman and Mike Speciner
+ Kerberos 4 tickets are not really hard to construct.
+
+ For the comments "Alice" is the User to be auth'ed, and "Bob" is the
+ AFS server. */
+
+BOOL afs_login(connection_struct *conn)
+{
+ DATA_BLOB ticket;
+ pstring afs_username;
+ char *cell;
+ BOOL result;
+
+ struct ClearToken ct;
+
+ pstrcpy(afs_username, lp_afs_username_map());
+ standard_sub_conn(conn, afs_username, sizeof(afs_username));
+
+ /* The pts command always generates completely lower-case user
+ * names. */
+ strlower_m(afs_username);
+
+ cell = strchr(afs_username, '@');
+
+ if (cell == NULL) {
+ DEBUG(1, ("AFS username doesn't contain a @, "
+ "could not find cell\n"));
+ return False;
+ }
+
+ *cell = '\0';
+ cell += 1;
+
+ DEBUG(10, ("Trying to log into AFS for user %s@%s\n",
+ afs_username, cell));
+
+ if (!afs_createtoken(afs_username, cell, &ticket, &ct))
+ return False;
+
+ /* For which Unix-UID do we want to set the token? */
+ ct.ViceId = getuid();
+
+ {
+ char *str, *new_cell;
+ DATA_BLOB test_ticket;
+ struct ClearToken test_ct;
+
+ hex_encode(ct.HandShakeKey, sizeof(ct.HandShakeKey), &str);
+ DEBUG(10, ("Key: %s\n", str));
+ free(str);
+
+ str = afs_encode_token(cell, ticket, &ct);
+
+ if (!afs_decode_token(str, &new_cell, &test_ticket,
+ &test_ct)) {
+ DEBUG(0, ("Could not decode token"));
+ goto decode_failed;
+ }
+
+ if (strcmp(cell, new_cell) != 0) {
+ DEBUG(0, ("cell changed\n"));
+ }
+
+ if ((ticket.length != test_ticket.length) ||
+ (memcmp(ticket.data, test_ticket.data,
+ ticket.length) != 0)) {
+ DEBUG(0, ("Ticket changed\n"));
+ }
+
+ if (memcmp(&ct, &test_ct, sizeof(ct)) != 0) {
+ DEBUG(0, ("ClearToken changed\n"));
+ }
+
+ data_blob_free(&test_ticket);
+
+ decode_failed:
+ SAFE_FREE(str);
+ SAFE_FREE(new_cell);
+ }
+
+ result = afs_settoken(cell, &ct, ticket);
+
+ data_blob_free(&ticket);
+
+ return result;
+}
+
+#else
+
+BOOL afs_login(connection_struct *conn)
+{
+ return True;
+}
+
+BOOL afs_settoken_str(const char *token_string)
+{
+ return False;
+}
+
+char *afs_createtoken_str(const char *username, const char *cell)
+{
+ return False;
+}
+
+#endif /* WITH_FAKE_KASERVER */
diff --git a/source/lib/bitmap.c b/source/lib/bitmap.c
new file mode 100644
index 00000000000..3fa20cdd112
--- /dev/null
+++ b/source/lib/bitmap.c
@@ -0,0 +1,177 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple bitmap functions
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* these functions provide a simple way to allocate integers from a
+ pool without repetition */
+
+/****************************************************************************
+allocate a bitmap of the specified size
+****************************************************************************/
+struct bitmap *bitmap_allocate(int n)
+{
+ struct bitmap *bm;
+
+ bm = (struct bitmap *)malloc(sizeof(*bm));
+
+ if (!bm) return NULL;
+
+ bm->n = n;
+ bm->b = (uint32 *)malloc(sizeof(bm->b[0])*(n+31)/32);
+ if (!bm->b) {
+ SAFE_FREE(bm);
+ return NULL;
+ }
+
+ memset(bm->b, 0, sizeof(bm->b[0])*(n+31)/32);
+
+ return bm;
+}
+
+/****************************************************************************
+free a bitmap.
+****************************************************************************/
+
+void bitmap_free(struct bitmap *bm)
+{
+ if (!bm)
+ return;
+
+ SAFE_FREE(bm->b);
+ SAFE_FREE(bm);
+}
+
+/****************************************************************************
+talloc a bitmap
+****************************************************************************/
+struct bitmap *bitmap_talloc(TALLOC_CTX *mem_ctx, int n)
+{
+ struct bitmap *bm;
+
+ if (!mem_ctx) return NULL;
+
+ bm = (struct bitmap *)talloc(mem_ctx, sizeof(*bm));
+
+ if (!bm) return NULL;
+
+ bm->n = n;
+ bm->b = (uint32 *)talloc(mem_ctx, sizeof(bm->b[0])*(n+31)/32);
+ if (!bm->b) {
+ return NULL;
+ }
+
+ memset(bm->b, 0, sizeof(bm->b[0])*(n+31)/32);
+
+ return bm;
+}
+
+/****************************************************************************
+copy as much of the source bitmap as will fit in the destination bitmap.
+****************************************************************************/
+
+int bitmap_copy(struct bitmap * const dst, const struct bitmap * const src)
+{
+ int count = MIN(dst->n, src->n);
+
+ SMB_ASSERT(dst->b != src->b);
+ memcpy(dst->b, src->b, sizeof(dst->b[0])*(count+31)/32);
+
+ return count;
+}
+
+/****************************************************************************
+set a bit in a bitmap
+****************************************************************************/
+BOOL bitmap_set(struct bitmap *bm, unsigned i)
+{
+ if (i >= bm->n) {
+ DEBUG(0,("Setting invalid bitmap entry %d (of %d)\n",
+ i, bm->n));
+ return False;
+ }
+ bm->b[i/32] |= (1<<(i%32));
+ return True;
+}
+
+/****************************************************************************
+clear a bit in a bitmap
+****************************************************************************/
+BOOL bitmap_clear(struct bitmap *bm, unsigned i)
+{
+ if (i >= bm->n) {
+ DEBUG(0,("clearing invalid bitmap entry %d (of %d)\n",
+ i, bm->n));
+ return False;
+ }
+ bm->b[i/32] &= ~(1<<(i%32));
+ return True;
+}
+
+/****************************************************************************
+query a bit in a bitmap
+****************************************************************************/
+BOOL bitmap_query(struct bitmap *bm, unsigned i)
+{
+ if (i >= bm->n) return False;
+ if (bm->b[i/32] & (1<<(i%32))) {
+ return True;
+ }
+ return False;
+}
+
+/****************************************************************************
+find a zero bit in a bitmap starting at the specified offset, with
+wraparound
+****************************************************************************/
+int bitmap_find(struct bitmap *bm, unsigned ofs)
+{
+ unsigned int i, j;
+
+ if (ofs > bm->n) ofs = 0;
+
+ i = ofs;
+ while (i < bm->n) {
+ if (~(bm->b[i/32])) {
+ j = i;
+ do {
+ if (!bitmap_query(bm, j)) return j;
+ j++;
+ } while (j & 31 && j < bm->n);
+ }
+ i += 32;
+ i &= ~31;
+ }
+
+ i = 0;
+ while (i < ofs) {
+ if (~(bm->b[i/32])) {
+ j = i;
+ do {
+ if (!bitmap_query(bm, j)) return j;
+ j++;
+ } while (j & 31 && j < bm->n);
+ }
+ i += 32;
+ i &= ~31;
+ }
+
+ return -1;
+}
diff --git a/source/lib/charcnv.c b/source/lib/charcnv.c
new file mode 100644
index 00000000000..b9791931a35
--- /dev/null
+++ b/source/lib/charcnv.c
@@ -0,0 +1,1339 @@
+/*
+ Unix SMB/CIFS implementation.
+ Character set conversion Extensions
+ Copyright (C) Igor Vergeichik <iverg@mail.ru> 2001
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Simo Sorce 2001
+ Copyright (C) Martin Pool 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+#include "includes.h"
+
+/**
+ * @file
+ *
+ * @brief Character-set conversion routines built on our iconv.
+ *
+ * @note Samba's internal character set (at least in the 3.0 series)
+ * is always the same as the one for the Unix filesystem. It is
+ * <b>not</b> necessarily UTF-8 and may be different on machines that
+ * need i18n filenames to be compatible with Unix software. It does
+ * have to be a superset of ASCII. All multibyte sequences must start
+ * with a byte with the high bit set.
+ *
+ * @sa lib/iconv.c
+ */
+
+
+static smb_iconv_t conv_handles[NUM_CHARSETS][NUM_CHARSETS];
+static BOOL conv_silent; /* Should we do a debug if the conversion fails ? */
+
+/**
+ * Return the name of a charset to give to iconv().
+ **/
+static const char *charset_name(charset_t ch)
+{
+ const char *ret = NULL;
+
+ if (ch == CH_UCS2) ret = "UCS-2LE";
+ else if (ch == CH_UNIX) ret = lp_unix_charset();
+ else if (ch == CH_DOS) ret = lp_dos_charset();
+ else if (ch == CH_DISPLAY) ret = lp_display_charset();
+ else if (ch == CH_UTF8) ret = "UTF8";
+
+#if defined(HAVE_NL_LANGINFO) && defined(CODESET)
+ if (ret && !strcmp(ret, "LOCALE")) {
+ const char *ln = NULL;
+
+#ifdef HAVE_SETLOCALE
+ setlocale(LC_ALL, "");
+#endif
+ ln = nl_langinfo(CODESET);
+ if (ln) {
+ /* Check whether the charset name is supported
+ by iconv */
+ smb_iconv_t handle = smb_iconv_open(ln,"UCS-2LE");
+ if (handle == (smb_iconv_t) -1) {
+ DEBUG(5,("Locale charset '%s' unsupported, using ASCII instead\n", ln));
+ ln = NULL;
+ } else {
+ DEBUG(5,("Substituting charset '%s' for LOCALE\n", ln));
+ smb_iconv_close(handle);
+ }
+ }
+ ret = ln;
+ }
+#endif
+
+ if (!ret || !*ret) ret = "ASCII";
+ return ret;
+}
+
+void lazy_initialize_conv(void)
+{
+ static int initialized = False;
+
+ if (!initialized) {
+ initialized = True;
+ load_case_tables();
+ init_iconv();
+ }
+}
+
+/**
+ * Initialize iconv conversion descriptors.
+ *
+ * This is called the first time it is needed, and also called again
+ * every time the configuration is reloaded, because the charset or
+ * codepage might have changed.
+ **/
+void init_iconv(void)
+{
+ int c1, c2;
+ BOOL did_reload = False;
+
+ /* so that charset_name() works we need to get the UNIX<->UCS2 going
+ first */
+ if (!conv_handles[CH_UNIX][CH_UCS2])
+ conv_handles[CH_UNIX][CH_UCS2] = smb_iconv_open("UCS-2LE", "ASCII");
+
+ if (!conv_handles[CH_UCS2][CH_UNIX])
+ conv_handles[CH_UCS2][CH_UNIX] = smb_iconv_open("ASCII", "UCS-2LE");
+
+ for (c1=0;c1<NUM_CHARSETS;c1++) {
+ for (c2=0;c2<NUM_CHARSETS;c2++) {
+ const char *n1 = charset_name((charset_t)c1);
+ const char *n2 = charset_name((charset_t)c2);
+ if (conv_handles[c1][c2] &&
+ strcmp(n1, conv_handles[c1][c2]->from_name) == 0 &&
+ strcmp(n2, conv_handles[c1][c2]->to_name) == 0)
+ continue;
+
+ did_reload = True;
+
+ if (conv_handles[c1][c2])
+ smb_iconv_close(conv_handles[c1][c2]);
+
+ conv_handles[c1][c2] = smb_iconv_open(n2,n1);
+ if (conv_handles[c1][c2] == (smb_iconv_t)-1) {
+ DEBUG(0,("init_iconv: Conversion from %s to %s not supported\n",
+ charset_name((charset_t)c1), charset_name((charset_t)c2)));
+ if (c1 != CH_UCS2) {
+ n1 = "ASCII";
+ }
+ if (c2 != CH_UCS2) {
+ n2 = "ASCII";
+ }
+ DEBUG(0,("init_iconv: Attempting to replace with conversion from %s to %s\n",
+ n1, n2 ));
+ conv_handles[c1][c2] = smb_iconv_open(n2,n1);
+ if (!conv_handles[c1][c2]) {
+ DEBUG(0,("init_iconv: Conversion from %s to %s failed", n1, n2));
+ smb_panic("init_iconv: conv_handle initialization failed.");
+ }
+ }
+ }
+ }
+
+ if (did_reload) {
+ /* XXX: Does this really get called every time the dos
+ * codepage changes? */
+ /* XXX: Is the did_reload test too strict? */
+ conv_silent = True;
+ init_doschar_table();
+ init_valid_table();
+ conv_silent = False;
+ }
+}
+
+/**
+ * Convert string from one encoding to another, making error checking etc
+ * Slow path version - uses (slow) iconv.
+ *
+ * @param src pointer to source string (multibyte or singlebyte)
+ * @param srclen length of the source string in bytes
+ * @param dest pointer to destination string (multibyte or singlebyte)
+ * @param destlen maximal length allowed for string
+ * @param allow_bad_conv determines if a "best effort" conversion is acceptable (never returns errors)
+ * @returns the number of bytes occupied in the destination
+ *
+ * Ensure the srclen contains the terminating zero.
+ *
+ **/
+
+static size_t convert_string_internal(charset_t from, charset_t to,
+ void const *src, size_t srclen,
+ void *dest, size_t destlen, BOOL allow_bad_conv)
+{
+ size_t i_len, o_len;
+ size_t retval;
+ const char* inbuf = (const char*)src;
+ char* outbuf = (char*)dest;
+ smb_iconv_t descriptor;
+
+ lazy_initialize_conv();
+
+ descriptor = conv_handles[from][to];
+
+ if (srclen == (size_t)-1) {
+ if (from == CH_UCS2) {
+ srclen = (strlen_w((const smb_ucs2_t *)src)+1) * 2;
+ } else {
+ srclen = strlen((const char *)src)+1;
+ }
+ }
+
+
+ if (descriptor == (smb_iconv_t)-1 || descriptor == (smb_iconv_t)0) {
+ if (!conv_silent)
+ DEBUG(0,("convert_string_internal: Conversion not supported.\n"));
+ return (size_t)-1;
+ }
+
+ i_len=srclen;
+ o_len=destlen;
+
+ again:
+
+ retval = smb_iconv(descriptor, (char **)&inbuf, &i_len, &outbuf, &o_len);
+ if(retval==(size_t)-1) {
+ const char *reason="unknown error";
+ switch(errno) {
+ case EINVAL:
+ reason="Incomplete multibyte sequence";
+ if (!conv_silent)
+ DEBUG(3,("convert_string_internal: Conversion error: %s(%s)\n",reason,inbuf));
+ if (allow_bad_conv)
+ goto use_as_is;
+ break;
+ case E2BIG:
+ reason="No more room";
+ if (!conv_silent)
+ DEBUG(3, ("convert_string_internal: Required %lu, available %lu\n",
+ (unsigned long)srclen, (unsigned long)destlen));
+ /* we are not sure we need srclen bytes,
+ may be more, may be less.
+ We only know we need more than destlen
+ bytes ---simo */
+ break;
+ case EILSEQ:
+ reason="Illegal multibyte sequence";
+ if (!conv_silent)
+ DEBUG(3,("convert_string_internal: Conversion error: %s(%s)\n",reason,inbuf));
+ if (allow_bad_conv)
+ goto use_as_is;
+ break;
+ default:
+ if (!conv_silent)
+ DEBUG(0,("convert_string_internal: Conversion error: %s(%s)\n",reason,inbuf));
+ break;
+ }
+ /* smb_panic(reason); */
+ }
+ return destlen-o_len;
+
+ use_as_is:
+
+ /*
+ * Conversion not supported. This is actually an error, but there are so
+ * many misconfigured iconv systems and smb.conf's out there we can't just
+ * fail. Do a very bad conversion instead.... JRA.
+ */
+
+ {
+ if (o_len == 0 || i_len == 0)
+ return destlen - o_len;
+
+ if (from == CH_UCS2 && to != CH_UCS2) {
+ /* Can't convert from ucs2 to multibyte. Just truncate this char to ascii. */
+ if (i_len < 2)
+ return destlen - o_len;
+ if (i_len >= 2) {
+ *outbuf = inbuf[0];
+
+ outbuf++;
+ o_len--;
+
+ inbuf += 2;
+ i_len -= 2;
+ }
+
+ if (o_len == 0 || i_len == 0)
+ return destlen - o_len;
+
+ /* Keep trying with the next char... */
+ goto again;
+
+ } else if (from != CH_UCS2 && to == CH_UCS2) {
+ /* Can't convert to ucs2 - just widen by adding zero. */
+ if (o_len < 2)
+ return destlen - o_len;
+
+ outbuf[0] = inbuf[0];
+ outbuf[1] = '\0';
+
+ inbuf++;
+ i_len--;
+
+ outbuf += 2;
+ o_len -= 2;
+
+ if (o_len == 0 || i_len == 0)
+ return destlen - o_len;
+
+ /* Keep trying with the next char... */
+ goto again;
+
+ } else if (from != CH_UCS2 && to != CH_UCS2) {
+ /* Failed multibyte to multibyte. Just copy 1 char and
+ try again. */
+ outbuf[0] = inbuf[0];
+
+ inbuf++;
+ i_len--;
+
+ outbuf++;
+ o_len--;
+
+ if (o_len == 0 || i_len == 0)
+ return destlen - o_len;
+
+ /* Keep trying with the next char... */
+ goto again;
+
+ } else {
+ /* Keep compiler happy.... */
+ return destlen - o_len;
+ }
+ }
+}
+
+/**
+ * Convert string from one encoding to another, making error checking etc
+ * Fast path version - handles ASCII first.
+ *
+ * @param src pointer to source string (multibyte or singlebyte)
+ * @param srclen length of the source string in bytes, or -1 for nul terminated.
+ * @param dest pointer to destination string (multibyte or singlebyte)
+ * @param destlen maximal length allowed for string - *NEVER* -1.
+ * @param allow_bad_conv determines if a "best effort" conversion is acceptable (never returns errors)
+ * @returns the number of bytes occupied in the destination
+ *
+ * Ensure the srclen contains the terminating zero.
+ *
+ * This function has been hand-tuned to provide a fast path.
+ * Don't change unless you really know what you are doing. JRA.
+ **/
+
+size_t convert_string(charset_t from, charset_t to,
+ void const *src, size_t srclen,
+ void *dest, size_t destlen, BOOL allow_bad_conv)
+{
+ /*
+ * NB. We deliberately don't do a strlen here if srclen == -1.
+ * This is very expensive over millions of calls and is taken
+ * care of in the slow path in convert_string_internal. JRA.
+ */
+
+#ifdef DEVELOPER
+ SMB_ASSERT(destlen != (size_t)-1);
+#endif
+
+ if (srclen == 0)
+ return 0;
+
+ if (from != CH_UCS2 && to != CH_UCS2) {
+ const unsigned char *p = (const unsigned char *)src;
+ unsigned char *q = (unsigned char *)dest;
+ size_t slen = srclen;
+ size_t dlen = destlen;
+ unsigned char lastp;
+ size_t retval = 0;
+
+ /* If all characters are ascii, fast path here. */
+ while (slen && dlen) {
+ if ((lastp = *p) <= 0x7f) {
+ *q++ = *p++;
+ if (slen != (size_t)-1) {
+ slen--;
+ }
+ dlen--;
+ retval++;
+ if (!lastp)
+ break;
+ } else {
+#ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS
+ goto general_case;
+#else
+ return retval + convert_string_internal(from, to, p, slen, q, dlen, allow_bad_conv);
+#endif
+ }
+ }
+ return retval;
+ } else if (from == CH_UCS2 && to != CH_UCS2) {
+ const unsigned char *p = (const unsigned char *)src;
+ unsigned char *q = (unsigned char *)dest;
+ size_t retval = 0;
+ size_t slen = srclen;
+ size_t dlen = destlen;
+ unsigned char lastp;
+
+ /* If all characters are ascii, fast path here. */
+ while (((slen == (size_t)-1) || (slen >= 2)) && dlen) {
+ if (((lastp = *p) <= 0x7f) && (p[1] == 0)) {
+ *q++ = *p;
+ if (slen != (size_t)-1) {
+ slen -= 2;
+ }
+ p += 2;
+ dlen--;
+ retval++;
+ if (!lastp)
+ break;
+ } else {
+#ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS
+ goto general_case;
+#else
+ return retval + convert_string_internal(from, to, p, slen, q, dlen, allow_bad_conv);
+#endif
+ }
+ }
+ return retval;
+ } else if (from != CH_UCS2 && to == CH_UCS2) {
+ const unsigned char *p = (const unsigned char *)src;
+ unsigned char *q = (unsigned char *)dest;
+ size_t retval = 0;
+ size_t slen = srclen;
+ size_t dlen = destlen;
+ unsigned char lastp;
+
+ /* If all characters are ascii, fast path here. */
+ while (slen && (dlen >= 2)) {
+ if ((lastp = *p) <= 0x7F) {
+ *q++ = *p++;
+ *q++ = '\0';
+ if (slen != (size_t)-1) {
+ slen--;
+ }
+ dlen -= 2;
+ retval += 2;
+ if (!lastp)
+ break;
+ } else {
+#ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS
+ goto general_case;
+#else
+ return retval + convert_string_internal(from, to, p, slen, q, dlen, allow_bad_conv);
+#endif
+ }
+ }
+ return retval;
+ }
+
+#ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS
+ general_case:
+#endif
+ return convert_string_internal(from, to, src, srclen, dest, destlen, allow_bad_conv);
+}
+
+/**
+ * Convert between character sets, allocating a new buffer for the result.
+ *
+ * @param ctx TALLOC_CTX to use to allocate with. If NULL use malloc.
+ * @param srclen length of source buffer.
+ * @param dest always set at least to NULL
+ * @note -1 is not accepted for srclen.
+ *
+ * @returns Size in bytes of the converted string; or -1 in case of error.
+ *
+ * Ensure the srclen contains the terminating zero.
+ *
+ * I hate the goto's in this function. It's embarressing.....
+ * There has to be a cleaner way to do this. JRA.
+ **/
+
+size_t convert_string_allocate(TALLOC_CTX *ctx, charset_t from, charset_t to,
+ void const *src, size_t srclen, void **dest, BOOL allow_bad_conv)
+{
+ size_t i_len, o_len, destlen = MAX(srclen, 512);
+ size_t retval;
+ const char *inbuf = (const char *)src;
+ char *outbuf = NULL, *ob = NULL;
+ smb_iconv_t descriptor;
+
+ *dest = NULL;
+
+ if (src == NULL || srclen == (size_t)-1)
+ return (size_t)-1;
+ if (srclen == 0)
+ return 0;
+
+ lazy_initialize_conv();
+
+ descriptor = conv_handles[from][to];
+
+ if (descriptor == (smb_iconv_t)-1 || descriptor == (smb_iconv_t)0) {
+ if (!conv_silent)
+ DEBUG(0,("convert_string_allocate: Conversion not supported.\n"));
+ return (size_t)-1;
+ }
+
+ convert:
+
+ if ((destlen*2) < destlen) {
+ /* wrapped ! abort. */
+ if (!conv_silent)
+ DEBUG(0, ("convert_string_allocate: destlen wrapped !\n"));
+ if (!ctx)
+ SAFE_FREE(outbuf);
+ return (size_t)-1;
+ } else {
+ destlen = destlen * 2;
+ }
+
+ if (ctx)
+ ob = (char *)talloc_realloc(ctx, ob, destlen);
+ else
+ ob = (char *)Realloc(ob, destlen);
+
+ if (!ob) {
+ DEBUG(0, ("convert_string_allocate: realloc failed!\n"));
+ if (!ctx)
+ SAFE_FREE(outbuf);
+ return (size_t)-1;
+ } else {
+ outbuf = ob;
+ }
+ i_len = srclen;
+ o_len = destlen;
+
+ again:
+
+ retval = smb_iconv(descriptor,
+ (char **)&inbuf, &i_len,
+ &outbuf, &o_len);
+ if(retval == (size_t)-1) {
+ const char *reason="unknown error";
+ switch(errno) {
+ case EINVAL:
+ reason="Incomplete multibyte sequence";
+ if (!conv_silent)
+ DEBUG(3,("convert_string_allocate: Conversion error: %s(%s)\n",reason,inbuf));
+ if (allow_bad_conv)
+ goto use_as_is;
+ break;
+ case E2BIG:
+ goto convert;
+ case EILSEQ:
+ reason="Illegal multibyte sequence";
+ if (!conv_silent)
+ DEBUG(3,("convert_string_allocate: Conversion error: %s(%s)\n",reason,inbuf));
+ if (allow_bad_conv)
+ goto use_as_is;
+ break;
+ }
+ if (!conv_silent)
+ DEBUG(0,("Conversion error: %s(%s)\n",reason,inbuf));
+ /* smb_panic(reason); */
+ return (size_t)-1;
+ }
+
+ out:
+
+ destlen = destlen - o_len;
+ if (ctx)
+ *dest = (char *)talloc_realloc(ctx,ob,destlen);
+ else
+ *dest = (char *)Realloc(ob,destlen);
+ if (destlen && !*dest) {
+ DEBUG(0, ("convert_string_allocate: out of memory!\n"));
+ if (!ctx)
+ SAFE_FREE(ob);
+ return (size_t)-1;
+ }
+
+ return destlen;
+
+ use_as_is:
+
+ /*
+ * Conversion not supported. This is actually an error, but there are so
+ * many misconfigured iconv systems and smb.conf's out there we can't just
+ * fail. Do a very bad conversion instead.... JRA.
+ */
+
+ {
+ if (o_len == 0 || i_len == 0)
+ goto out;
+
+ if (from == CH_UCS2 && to != CH_UCS2) {
+ /* Can't convert from ucs2 to multibyte. Just truncate this char to ascii. */
+ if (i_len < 2)
+ goto out;
+
+ if (i_len >= 2) {
+ *outbuf = inbuf[0];
+
+ outbuf++;
+ o_len--;
+
+ inbuf += 2;
+ i_len -= 2;
+ }
+
+ if (o_len == 0 || i_len == 0)
+ goto out;
+
+ /* Keep trying with the next char... */
+ goto again;
+
+ } else if (from != CH_UCS2 && to == CH_UCS2) {
+ /* Can't convert to ucs2 - just widen by adding zero. */
+ if (o_len < 2)
+ goto out;
+
+ outbuf[0] = inbuf[0];
+ outbuf[1] = '\0';
+
+ inbuf++;
+ i_len--;
+
+ outbuf += 2;
+ o_len -= 2;
+
+ if (o_len == 0 || i_len == 0)
+ goto out;
+
+ /* Keep trying with the next char... */
+ goto again;
+
+ } else if (from != CH_UCS2 && to != CH_UCS2) {
+ /* Failed multibyte to multibyte. Just copy 1 char and
+ try again. */
+ outbuf[0] = inbuf[0];
+
+ inbuf++;
+ i_len--;
+
+ outbuf++;
+ o_len--;
+
+ if (o_len == 0 || i_len == 0)
+ goto out;
+
+ /* Keep trying with the next char... */
+ goto again;
+
+ } else {
+ /* Keep compiler happy.... */
+ goto out;
+ }
+ }
+}
+
+/**
+ * Convert between character sets, allocating a new buffer using talloc for the result.
+ *
+ * @param srclen length of source buffer.
+ * @param dest always set at least to NULL
+ * @note -1 is not accepted for srclen.
+ *
+ * @returns Size in bytes of the converted string; or -1 in case of error.
+ **/
+static size_t convert_string_talloc(TALLOC_CTX *ctx, charset_t from, charset_t to,
+ void const *src, size_t srclen, void **dest, BOOL allow_bad_conv)
+{
+ size_t dest_len;
+
+ *dest = NULL;
+ dest_len=convert_string_allocate(ctx, from, to, src, srclen, dest, allow_bad_conv);
+ if (dest_len == (size_t)-1)
+ return (size_t)-1;
+ if (*dest == NULL)
+ return (size_t)-1;
+ return dest_len;
+}
+
+size_t unix_strupper(const char *src, size_t srclen, char *dest, size_t destlen)
+{
+ size_t size;
+ smb_ucs2_t *buffer;
+
+ size = push_ucs2_allocate(&buffer, src);
+ if (size == (size_t)-1) {
+ smb_panic("failed to create UCS2 buffer");
+ }
+ if (!strupper_w(buffer) && (dest == src)) {
+ free(buffer);
+ return srclen;
+ }
+
+ size = convert_string(CH_UCS2, CH_UNIX, buffer, size, dest, destlen, True);
+ free(buffer);
+ return size;
+}
+
+/**
+ strdup() a unix string to upper case.
+ Max size is pstring.
+**/
+
+char *strdup_upper(const char *s)
+{
+ pstring out_buffer;
+ const unsigned char *p = (const unsigned char *)s;
+ unsigned char *q = (unsigned char *)out_buffer;
+
+ /* this is quite a common operation, so we want it to be
+ fast. We optimise for the ascii case, knowing that all our
+ supported multi-byte character sets are ascii-compatible
+ (ie. they match for the first 128 chars) */
+
+ while (1) {
+ if (*p & 0x80)
+ break;
+ *q++ = toupper(*p);
+ if (!*p)
+ break;
+ p++;
+ if (p - ( const unsigned char *)s >= sizeof(pstring))
+ break;
+ }
+
+ if (*p) {
+ /* MB case. */
+ size_t size;
+ wpstring buffer;
+ size = convert_string(CH_UNIX, CH_UCS2, s, -1, buffer, sizeof(buffer), True);
+ if (size == (size_t)-1) {
+ return NULL;
+ }
+
+ strupper_w(buffer);
+
+ size = convert_string(CH_UCS2, CH_UNIX, buffer, -1, out_buffer, sizeof(out_buffer), True);
+ if (size == (size_t)-1) {
+ return NULL;
+ }
+ }
+
+ return strdup(out_buffer);
+}
+
+size_t unix_strlower(const char *src, size_t srclen, char *dest, size_t destlen)
+{
+ size_t size;
+ smb_ucs2_t *buffer = NULL;
+
+ size = convert_string_allocate(NULL, CH_UNIX, CH_UCS2, src, srclen,
+ (void **) &buffer, True);
+ if (size == (size_t)-1 || !buffer) {
+ smb_panic("failed to create UCS2 buffer");
+ }
+ if (!strlower_w(buffer) && (dest == src)) {
+ SAFE_FREE(buffer);
+ return srclen;
+ }
+ size = convert_string(CH_UCS2, CH_UNIX, buffer, size, dest, destlen, True);
+ SAFE_FREE(buffer);
+ return size;
+}
+
+/**
+ strdup() a unix string to lower case.
+**/
+
+char *strdup_lower(const char *s)
+{
+ size_t size;
+ smb_ucs2_t *buffer = NULL;
+ char *out_buffer;
+
+ size = push_ucs2_allocate(&buffer, s);
+ if (size == -1 || !buffer) {
+ return NULL;
+ }
+
+ strlower_w(buffer);
+
+ size = pull_ucs2_allocate(&out_buffer, buffer);
+ SAFE_FREE(buffer);
+
+ if (size == (size_t)-1) {
+ return NULL;
+ }
+
+ return out_buffer;
+}
+
+static size_t ucs2_align(const void *base_ptr, const void *p, int flags)
+{
+ if (flags & (STR_NOALIGN|STR_ASCII))
+ return 0;
+ return PTR_DIFF(p, base_ptr) & 1;
+}
+
+
+/**
+ * Copy a string from a char* unix src to a dos codepage string destination.
+ *
+ * @return the number of bytes occupied by the string in the destination.
+ *
+ * @param flags can include
+ * <dl>
+ * <dt>STR_TERMINATE</dt> <dd>means include the null termination</dd>
+ * <dt>STR_UPPER</dt> <dd>means uppercase in the destination</dd>
+ * </dl>
+ *
+ * @param dest_len the maximum length in bytes allowed in the
+ * destination. If @p dest_len is -1 then no maximum is used.
+ **/
+size_t push_ascii(void *dest, const char *src, size_t dest_len, int flags)
+{
+ size_t src_len = strlen(src);
+ pstring tmpbuf;
+
+ /* treat a pstring as "unlimited" length */
+ if (dest_len == (size_t)-1)
+ dest_len = sizeof(pstring);
+
+ if (flags & STR_UPPER) {
+ pstrcpy(tmpbuf, src);
+ strupper_m(tmpbuf);
+ src = tmpbuf;
+ }
+
+ if (flags & (STR_TERMINATE | STR_TERMINATE_ASCII))
+ src_len++;
+
+ return convert_string(CH_UNIX, CH_DOS, src, src_len, dest, dest_len, True);
+}
+
+size_t push_ascii_fstring(void *dest, const char *src)
+{
+ return push_ascii(dest, src, sizeof(fstring), STR_TERMINATE);
+}
+
+size_t push_ascii_pstring(void *dest, const char *src)
+{
+ return push_ascii(dest, src, sizeof(pstring), STR_TERMINATE);
+}
+
+/********************************************************************
+ Push an nstring - ensure null terminated. Written by
+ moriyama@miraclelinux.com (MORIYAMA Masayuki).
+********************************************************************/
+
+size_t push_ascii_nstring(void *dest, const char *src)
+{
+ size_t i, buffer_len, dest_len;
+ smb_ucs2_t *buffer;
+
+ conv_silent = True;
+ buffer_len = push_ucs2_allocate(&buffer, src);
+ if (buffer_len == (size_t)-1) {
+ smb_panic("failed to create UCS2 buffer");
+ }
+
+ /* We're using buffer_len below to count ucs2 characters, not bytes. */
+ buffer_len /= sizeof(smb_ucs2_t);
+
+ dest_len = 0;
+ for (i = 0; buffer[i] != 0 && (i < buffer_len); i++) {
+ unsigned char mb[10];
+ /* Convert one smb_ucs2_t character at a time. */
+ size_t mb_len = convert_string(CH_UCS2, CH_DOS, buffer+i, sizeof(smb_ucs2_t), mb, sizeof(mb), False);
+ if ((mb_len != (size_t)-1) && (dest_len + mb_len <= MAX_NETBIOSNAME_LEN - 1)) {
+ memcpy((char *)dest + dest_len, mb, mb_len);
+ dest_len += mb_len;
+ } else {
+ errno = E2BIG;
+ break;
+ }
+ }
+ ((char *)dest)[dest_len] = '\0';
+
+ SAFE_FREE(buffer);
+ conv_silent = False;
+ return dest_len;
+}
+
+/**
+ * Copy a string from a dos codepage source to a unix char* destination.
+ *
+ * The resulting string in "dest" is always null terminated.
+ *
+ * @param flags can have:
+ * <dl>
+ * <dt>STR_TERMINATE</dt>
+ * <dd>STR_TERMINATE means the string in @p src
+ * is null terminated, and src_len is ignored.</dd>
+ * </dl>
+ *
+ * @param src_len is the length of the source area in bytes.
+ * @returns the number of bytes occupied by the string in @p src.
+ **/
+size_t pull_ascii(char *dest, const void *src, size_t dest_len, size_t src_len, int flags)
+{
+ size_t ret;
+
+ if (dest_len == (size_t)-1)
+ dest_len = sizeof(pstring);
+
+ if (flags & STR_TERMINATE) {
+ if (src_len == (size_t)-1) {
+ src_len = strlen(src) + 1;
+ } else {
+ size_t len = strnlen(src, src_len);
+ if (len < src_len)
+ len++;
+ src_len = len;
+ }
+ }
+
+ ret = convert_string(CH_DOS, CH_UNIX, src, src_len, dest, dest_len, True);
+ if (ret == (size_t)-1) {
+ dest_len = 0;
+ }
+
+ if (dest_len)
+ dest[MIN(ret, dest_len-1)] = 0;
+ else
+ dest[0] = 0;
+
+ return src_len;
+}
+
+size_t pull_ascii_pstring(char *dest, const void *src)
+{
+ return pull_ascii(dest, src, sizeof(pstring), -1, STR_TERMINATE);
+}
+
+size_t pull_ascii_fstring(char *dest, const void *src)
+{
+ return pull_ascii(dest, src, sizeof(fstring), -1, STR_TERMINATE);
+}
+
+/* When pulling an nstring it can expand into a larger size (dos cp -> utf8). Cope with this. */
+
+size_t pull_ascii_nstring(char *dest, size_t dest_len, const void *src)
+{
+ return pull_ascii(dest, src, dest_len, sizeof(nstring), STR_TERMINATE);
+}
+
+/**
+ * Copy a string from a char* src to a unicode destination.
+ *
+ * @returns the number of bytes occupied by the string in the destination.
+ *
+ * @param flags can have:
+ *
+ * <dl>
+ * <dt>STR_TERMINATE <dd>means include the null termination.
+ * <dt>STR_UPPER <dd>means uppercase in the destination.
+ * <dt>STR_NOALIGN <dd>means don't do alignment.
+ * </dl>
+ *
+ * @param dest_len is the maximum length allowed in the
+ * destination. If dest_len is -1 then no maxiumum is used.
+ **/
+
+size_t push_ucs2(const void *base_ptr, void *dest, const char *src, size_t dest_len, int flags)
+{
+ size_t len=0;
+ size_t src_len;
+ size_t ret;
+
+ /* treat a pstring as "unlimited" length */
+ if (dest_len == (size_t)-1)
+ dest_len = sizeof(pstring);
+
+ if (flags & STR_TERMINATE)
+ src_len = (size_t)-1;
+ else
+ src_len = strlen(src);
+
+ if (ucs2_align(base_ptr, dest, flags)) {
+ *(char *)dest = 0;
+ dest = (void *)((char *)dest + 1);
+ if (dest_len)
+ dest_len--;
+ len++;
+ }
+
+ /* ucs2 is always a multiple of 2 bytes */
+ dest_len &= ~1;
+
+ ret = convert_string(CH_UNIX, CH_UCS2, src, src_len, dest, dest_len, True);
+ if (ret == (size_t)-1) {
+ return 0;
+ }
+
+ len += ret;
+
+ if (flags & STR_UPPER) {
+ smb_ucs2_t *dest_ucs2 = dest;
+ size_t i;
+ for (i = 0; i < (dest_len / 2) && dest_ucs2[i]; i++) {
+ smb_ucs2_t v = toupper_w(dest_ucs2[i]);
+ if (v != dest_ucs2[i]) {
+ dest_ucs2[i] = v;
+ }
+ }
+ }
+
+ return len;
+}
+
+
+/**
+ * Copy a string from a unix char* src to a UCS2 destination,
+ * allocating a buffer using talloc().
+ *
+ * @param dest always set at least to NULL
+ *
+ * @returns The number of bytes occupied by the string in the destination
+ * or -1 in case of error.
+ **/
+size_t push_ucs2_talloc(TALLOC_CTX *ctx, smb_ucs2_t **dest, const char *src)
+{
+ size_t src_len = strlen(src)+1;
+
+ *dest = NULL;
+ return convert_string_talloc(ctx, CH_UNIX, CH_UCS2, src, src_len, (void **)dest, True);
+}
+
+
+/**
+ * Copy a string from a unix char* src to a UCS2 destination, allocating a buffer
+ *
+ * @param dest always set at least to NULL
+ *
+ * @returns The number of bytes occupied by the string in the destination
+ * or -1 in case of error.
+ **/
+
+size_t push_ucs2_allocate(smb_ucs2_t **dest, const char *src)
+{
+ size_t src_len = strlen(src)+1;
+
+ *dest = NULL;
+ return convert_string_allocate(NULL, CH_UNIX, CH_UCS2, src, src_len, (void **)dest, True);
+}
+
+/**
+ Copy a string from a char* src to a UTF-8 destination.
+ Return the number of bytes occupied by the string in the destination
+ Flags can have:
+ STR_TERMINATE means include the null termination
+ STR_UPPER means uppercase in the destination
+ dest_len is the maximum length allowed in the destination. If dest_len
+ is -1 then no maxiumum is used.
+**/
+
+static size_t push_utf8(void *dest, const char *src, size_t dest_len, int flags)
+{
+ size_t src_len = strlen(src);
+ pstring tmpbuf;
+
+ /* treat a pstring as "unlimited" length */
+ if (dest_len == (size_t)-1)
+ dest_len = sizeof(pstring);
+
+ if (flags & STR_UPPER) {
+ pstrcpy(tmpbuf, src);
+ strupper_m(tmpbuf);
+ src = tmpbuf;
+ }
+
+ if (flags & STR_TERMINATE)
+ src_len++;
+
+ return convert_string(CH_UNIX, CH_UTF8, src, src_len, dest, dest_len, True);
+}
+
+size_t push_utf8_fstring(void *dest, const char *src)
+{
+ return push_utf8(dest, src, sizeof(fstring), STR_TERMINATE);
+}
+
+/**
+ * Copy a string from a unix char* src to a UTF-8 destination, allocating a buffer using talloc
+ *
+ * @param dest always set at least to NULL
+ *
+ * @returns The number of bytes occupied by the string in the destination
+ **/
+
+size_t push_utf8_talloc(TALLOC_CTX *ctx, char **dest, const char *src)
+{
+ size_t src_len = strlen(src)+1;
+
+ *dest = NULL;
+ return convert_string_talloc(ctx, CH_UNIX, CH_UTF8, src, src_len, (void**)dest, True);
+}
+
+/**
+ * Copy a string from a unix char* src to a UTF-8 destination, allocating a buffer
+ *
+ * @param dest always set at least to NULL
+ *
+ * @returns The number of bytes occupied by the string in the destination
+ **/
+
+size_t push_utf8_allocate(char **dest, const char *src)
+{
+ size_t src_len = strlen(src)+1;
+
+ *dest = NULL;
+ return convert_string_allocate(NULL, CH_UNIX, CH_UTF8, src, src_len, (void **)dest, True);
+}
+
+/**
+ Copy a string from a ucs2 source to a unix char* destination.
+ Flags can have:
+ STR_TERMINATE means the string in src is null terminated.
+ STR_NOALIGN means don't try to align.
+ if STR_TERMINATE is set then src_len is ignored if it is -1.
+ src_len is the length of the source area in bytes
+ Return the number of bytes occupied by the string in src.
+ The resulting string in "dest" is always null terminated.
+**/
+
+size_t pull_ucs2(const void *base_ptr, char *dest, const void *src, size_t dest_len, size_t src_len, int flags)
+{
+ size_t ret;
+
+ if (dest_len == (size_t)-1)
+ dest_len = sizeof(pstring);
+
+ if (ucs2_align(base_ptr, src, flags)) {
+ src = (const void *)((const char *)src + 1);
+ if (src_len != (size_t)-1)
+ src_len--;
+ }
+
+ if (flags & STR_TERMINATE) {
+ /* src_len -1 is the default for null terminated strings. */
+ if (src_len != (size_t)-1) {
+ size_t len = strnlen_w(src, src_len/2);
+ if (len < src_len/2)
+ len++;
+ src_len = len*2;
+ }
+ }
+
+ /* ucs2 is always a multiple of 2 bytes */
+ if (src_len != (size_t)-1)
+ src_len &= ~1;
+
+ ret = convert_string(CH_UCS2, CH_UNIX, src, src_len, dest, dest_len, True);
+ if (ret == (size_t)-1) {
+ return 0;
+ }
+
+ if (src_len == (size_t)-1)
+ src_len = ret*2;
+
+ if (dest_len)
+ dest[MIN(ret, dest_len-1)] = 0;
+ else
+ dest[0] = 0;
+
+ return src_len;
+}
+
+size_t pull_ucs2_pstring(char *dest, const void *src)
+{
+ return pull_ucs2(NULL, dest, src, sizeof(pstring), -1, STR_TERMINATE);
+}
+
+size_t pull_ucs2_fstring(char *dest, const void *src)
+{
+ return pull_ucs2(NULL, dest, src, sizeof(fstring), -1, STR_TERMINATE);
+}
+
+/**
+ * Copy a string from a UCS2 src to a unix char * destination, allocating a buffer using talloc
+ *
+ * @param dest always set at least to NULL
+ *
+ * @returns The number of bytes occupied by the string in the destination
+ **/
+
+size_t pull_ucs2_talloc(TALLOC_CTX *ctx, char **dest, const smb_ucs2_t *src)
+{
+ size_t src_len = (strlen_w(src)+1) * sizeof(smb_ucs2_t);
+ *dest = NULL;
+ return convert_string_talloc(ctx, CH_UCS2, CH_UNIX, src, src_len, (void **)dest, True);
+}
+
+/**
+ * Copy a string from a UCS2 src to a unix char * destination, allocating a buffer
+ *
+ * @param dest always set at least to NULL
+ *
+ * @returns The number of bytes occupied by the string in the destination
+ **/
+
+size_t pull_ucs2_allocate(char **dest, const smb_ucs2_t *src)
+{
+ size_t src_len = (strlen_w(src)+1) * sizeof(smb_ucs2_t);
+ *dest = NULL;
+ return convert_string_allocate(NULL, CH_UCS2, CH_UNIX, src, src_len, (void **)dest, True);
+}
+
+/**
+ * Copy a string from a UTF-8 src to a unix char * destination, allocating a buffer using talloc
+ *
+ * @param dest always set at least to NULL
+ *
+ * @returns The number of bytes occupied by the string in the destination
+ **/
+
+size_t pull_utf8_talloc(TALLOC_CTX *ctx, char **dest, const char *src)
+{
+ size_t src_len = strlen(src)+1;
+ *dest = NULL;
+ return convert_string_talloc(ctx, CH_UTF8, CH_UNIX, src, src_len, (void **)dest, True);
+}
+
+/**
+ * Copy a string from a UTF-8 src to a unix char * destination, allocating a buffer
+ *
+ * @param dest always set at least to NULL
+ *
+ * @returns The number of bytes occupied by the string in the destination
+ **/
+
+size_t pull_utf8_allocate(char **dest, const char *src)
+{
+ size_t src_len = strlen(src)+1;
+ *dest = NULL;
+ return convert_string_allocate(NULL, CH_UTF8, CH_UNIX, src, src_len, (void **)dest, True);
+}
+
+/**
+ Copy a string from a char* src to a unicode or ascii
+ dos codepage destination choosing unicode or ascii based on the
+ flags in the SMB buffer starting at base_ptr.
+ Return the number of bytes occupied by the string in the destination.
+ flags can have:
+ STR_TERMINATE means include the null termination.
+ STR_UPPER means uppercase in the destination.
+ STR_ASCII use ascii even with unicode packet.
+ STR_NOALIGN means don't do alignment.
+ dest_len is the maximum length allowed in the destination. If dest_len
+ is -1 then no maxiumum is used.
+**/
+
+size_t push_string_fn(const char *function, unsigned int line, const void *base_ptr, void *dest, const char *src, size_t dest_len, int flags)
+{
+#ifdef DEVELOPER
+ /* We really need to zero fill here, not clobber
+ * region, as we want to ensure that valgrind thinks
+ * all of the outgoing buffer has been written to
+ * so a send() or write() won't trap an error.
+ * JRA.
+ */
+#if 0
+ if (dest_len != (size_t)-1)
+ clobber_region(function, line, dest, dest_len);
+#else
+ if (dest_len != (size_t)-1)
+ memset(dest, '\0', dest_len);
+#endif
+#endif
+
+ if (!(flags & STR_ASCII) && \
+ ((flags & STR_UNICODE || \
+ (SVAL(base_ptr, smb_flg2) & FLAGS2_UNICODE_STRINGS)))) {
+ return push_ucs2(base_ptr, dest, src, dest_len, flags);
+ }
+ return push_ascii(dest, src, dest_len, flags);
+}
+
+
+/**
+ Copy a string from a unicode or ascii source (depending on
+ the packet flags) to a char* destination.
+ Flags can have:
+ STR_TERMINATE means the string in src is null terminated.
+ STR_UNICODE means to force as unicode.
+ STR_ASCII use ascii even with unicode packet.
+ STR_NOALIGN means don't do alignment.
+ if STR_TERMINATE is set then src_len is ignored is it is -1
+ src_len is the length of the source area in bytes.
+ Return the number of bytes occupied by the string in src.
+ The resulting string in "dest" is always null terminated.
+**/
+
+size_t pull_string_fn(const char *function, unsigned int line, const void *base_ptr, char *dest, const void *src, size_t dest_len, size_t src_len, int flags)
+{
+#ifdef DEVELOPER
+ if (dest_len != (size_t)-1)
+ clobber_region(function, line, dest, dest_len);
+#endif
+
+ if (!(flags & STR_ASCII) && \
+ ((flags & STR_UNICODE || \
+ (SVAL(base_ptr, smb_flg2) & FLAGS2_UNICODE_STRINGS)))) {
+ return pull_ucs2(base_ptr, dest, src, dest_len, src_len, flags);
+ }
+ return pull_ascii(dest, src, dest_len, src_len, flags);
+}
+
+size_t align_string(const void *base_ptr, const char *p, int flags)
+{
+ if (!(flags & STR_ASCII) && \
+ ((flags & STR_UNICODE || \
+ (SVAL(base_ptr, smb_flg2) & FLAGS2_UNICODE_STRINGS)))) {
+ return ucs2_align(base_ptr, p, flags);
+ }
+ return 0;
+}
+
+/****************************************************************
+ Calculate the size (in bytes) of the next multibyte character in
+ our internal character set. Note that p must be pointing to a
+ valid mb char, not within one.
+****************************************************************/
+
+size_t next_mb_char_size(const char *s)
+{
+ size_t i;
+
+ if (!(*s & 0x80))
+ return 1; /* ascii. */
+
+ conv_silent = True;
+ for ( i = 1; i <=4; i++ ) {
+ smb_ucs2_t uc;
+ if (convert_string(CH_UNIX, CH_UCS2, s, i, &uc, 2, False) == 2) {
+#if 0 /* JRATEST */
+ DEBUG(10,("next_mb_char_size: size %u at string %s\n",
+ (unsigned int)i, s));
+#endif
+ conv_silent = False;
+ return i;
+ }
+ }
+ /* We're hosed - we don't know how big this is... */
+ DEBUG(10,("next_mb_char_size: unknown size at string %s\n", s));
+ conv_silent = False;
+ return 1;
+}
diff --git a/source/lib/clobber.c b/source/lib/clobber.c
new file mode 100644
index 00000000000..fb3a0dc2815
--- /dev/null
+++ b/source/lib/clobber.c
@@ -0,0 +1,60 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Martin Pool 2003
+ Copyright (C) Andrew Bartlett 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#ifdef DEVELOPER
+const char *global_clobber_region_function;
+unsigned int global_clobber_region_line;
+#endif
+
+/**
+ * In developer builds, clobber a region of memory.
+ *
+ * If we think a string buffer is longer than it really is, this ought
+ * to make the failure obvious, by segfaulting (if in the heap) or by
+ * killing the return address (on the stack), or by trapping under a
+ * memory debugger.
+ *
+ * This is meant to catch possible string overflows, even if the
+ * actual string copied is not big enough to cause an overflow.
+ *
+ * In addition, under Valgrind the buffer is marked as uninitialized.
+ **/
+void clobber_region(const char *fn, unsigned int line, char *dest, size_t len)
+{
+#ifdef DEVELOPER
+ global_clobber_region_function = fn;
+ global_clobber_region_line = line;
+
+ /* F1 is odd and 0xf1f1f1f1 shouldn't be a valid pointer */
+ memset(dest, 0xF1, len);
+#ifdef VALGRIND
+ /* Even though we just wrote to this, from the application's
+ * point of view it is not initialized.
+ *
+ * (This is not redundant with the clobbering above. The
+ * marking might not actually take effect if we're not running
+ * under valgrind.) */
+ VALGRIND_MAKE_WRITABLE(dest, len);
+#endif /* VALGRIND */
+#endif /* DEVELOPER */
+}
diff --git a/source/lib/crc32.c b/source/lib/crc32.c
new file mode 100644
index 00000000000..da3aeaa901d
--- /dev/null
+++ b/source/lib/crc32.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright Francesco Ferrara, 1998 <francesco@aerra.it>
+ *
+ * Used by kind permission, 14th October 1998. http://www.aerre.it/francesco
+ *
+ *
+ */
+
+#include "includes.h"
+
+static const unsigned long CRCTable[256] =
+{
+ 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,
+ 0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,
+ 0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,0x1DB71064,0x6AB020F2,
+ 0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,
+ 0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,
+ 0xFA0F3D63,0x8D080DF5,0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,
+ 0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,
+ 0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
+ 0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,
+ 0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,
+ 0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,0x76DC4190,0x01DB7106,
+ 0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,
+ 0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,
+ 0x91646C97,0xE6635C01,0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,
+ 0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,
+ 0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
+ 0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,
+ 0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,
+ 0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,0x5005713C,0x270241AA,
+ 0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,
+ 0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,
+ 0xB7BD5C3B,0xC0BA6CAD,0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,
+ 0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,
+ 0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
+ 0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,
+ 0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,
+ 0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,0xD6D6A3E8,0xA1D1937E,
+ 0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,
+ 0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,
+ 0x316E8EEF,0x4669BE79,0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,
+ 0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,
+ 0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
+ 0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,
+ 0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,
+ 0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,0x86D3D2D4,0xF1D4E242,
+ 0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,
+ 0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,
+ 0x616BFFD3,0x166CCF45,0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,
+ 0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,
+ 0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
+ 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,
+ 0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,
+ 0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D
+};
+
+uint32 crc32_calc_buffer( const char *buffer, uint32 count)
+{
+ uint32 crc=0xffffffff, i;
+ for(i=0;i<count;i++)
+ crc = (crc>>8) ^ CRCTable[(buffer[i] ^ crc) & 0xff];
+ crc^=0xffffffff;
+ DEBUG(10,("crc32_calc_buffer: %x\n", crc));
+ dump_data(100, buffer, count);
+ return crc;
+}
diff --git a/source/lib/data_blob.c b/source/lib/data_blob.c
new file mode 100644
index 00000000000..83afc591a15
--- /dev/null
+++ b/source/lib/data_blob.c
@@ -0,0 +1,115 @@
+/*
+ Unix SMB/CIFS implementation.
+ Easy management of byte-length data
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Andrew Bartlett 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*******************************************************************
+ free() a data blob
+*******************************************************************/
+static void free_data_blob(DATA_BLOB *d)
+{
+ if ((d) && (d->free)) {
+ SAFE_FREE(d->data);
+ }
+}
+
+/*******************************************************************
+ construct a data blob, must be freed with data_blob_free()
+ you can pass NULL for p and get a blank data blob
+*******************************************************************/
+DATA_BLOB data_blob(const void *p, size_t length)
+{
+ DATA_BLOB ret;
+
+ if (!length) {
+ ZERO_STRUCT(ret);
+ return ret;
+ }
+
+ if (p) {
+ ret.data = smb_xmemdup(p, length);
+ } else {
+ ret.data = smb_xmalloc(length);
+ }
+ ret.length = length;
+ ret.free = free_data_blob;
+ return ret;
+}
+
+/*******************************************************************
+ construct a data blob, using supplied TALLOC_CTX
+*******************************************************************/
+DATA_BLOB data_blob_talloc(TALLOC_CTX *mem_ctx, const void *p, size_t length)
+{
+ DATA_BLOB ret;
+
+ if (!length) {
+ ZERO_STRUCT(ret);
+ return ret;
+ }
+
+ if (p) {
+ ret.data = talloc_memdup(mem_ctx, p, length);
+ if (ret.data == NULL)
+ smb_panic("data_blob_talloc: talloc_memdup failed.\n");
+ } else {
+ ret.data = talloc(mem_ctx, length);
+ if (ret.data == NULL)
+ smb_panic("data_blob_talloc: talloc failed.\n");
+ }
+
+ ret.length = length;
+ ret.free = NULL;
+ return ret;
+}
+
+/*******************************************************************
+free a data blob
+*******************************************************************/
+void data_blob_free(DATA_BLOB *d)
+{
+ if (d) {
+ if (d->free) {
+ (d->free)(d);
+ }
+ d->length = 0;
+ }
+}
+
+/*******************************************************************
+clear a DATA_BLOB's contents
+*******************************************************************/
+static void data_blob_clear(DATA_BLOB *d)
+{
+ if (d->data) {
+ memset(d->data, 0, d->length);
+ }
+}
+
+/*******************************************************************
+free a data blob and clear its contents
+*******************************************************************/
+void data_blob_clear_free(DATA_BLOB *d)
+{
+ data_blob_clear(d);
+ data_blob_free(d);
+}
+
diff --git a/source/lib/debug.c b/source/lib/debug.c
new file mode 100644
index 00000000000..1a926053bb0
--- /dev/null
+++ b/source/lib/debug.c
@@ -0,0 +1,994 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Elrond 2002
+ Copyright (C) Simo Sorce 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* -------------------------------------------------------------------------- **
+ * Defines...
+ *
+ * FORMAT_BUFR_MAX - Index of the last byte of the format buffer;
+ * format_bufr[FORMAT_BUFR_MAX] should always be reserved
+ * for a terminating null byte.
+ */
+
+#define FORMAT_BUFR_MAX ( sizeof( format_bufr ) - 1 )
+
+/* -------------------------------------------------------------------------- **
+ * This module implements Samba's debugging utility.
+ *
+ * The syntax of a debugging log file is represented as:
+ *
+ * <debugfile> :== { <debugmsg> }
+ *
+ * <debugmsg> :== <debughdr> '\n' <debugtext>
+ *
+ * <debughdr> :== '[' TIME ',' LEVEL ']' [ [FILENAME ':'] [FUNCTION '()'] ]
+ *
+ * <debugtext> :== { <debugline> }
+ *
+ * <debugline> :== TEXT '\n'
+ *
+ * TEXT is a string of characters excluding the newline character.
+ * LEVEL is the DEBUG level of the message (an integer in the range 0..10).
+ * TIME is a timestamp.
+ * FILENAME is the name of the file from which the debug message was generated.
+ * FUNCTION is the function from which the debug message was generated.
+ *
+ * Basically, what that all means is:
+ *
+ * - A debugging log file is made up of debug messages.
+ *
+ * - Each debug message is made up of a header and text. The header is
+ * separated from the text by a newline.
+ *
+ * - The header begins with the timestamp and debug level of the message
+ * enclosed in brackets. The filename and function from which the
+ * message was generated may follow. The filename is terminated by a
+ * colon, and the function name is terminated by parenthesis.
+ *
+ * - The message text is made up of zero or more lines, each terminated by
+ * a newline.
+ */
+
+/* -------------------------------------------------------------------------- **
+ * External variables.
+ *
+ * dbf - Global debug file handle.
+ * debugf - Debug file name.
+ * DEBUGLEVEL - System-wide debug message limit. Messages with message-
+ * levels higher than DEBUGLEVEL will not be processed.
+ */
+
+XFILE *dbf = NULL;
+pstring debugf = "";
+BOOL debug_warn_unknown_class = True;
+BOOL debug_auto_add_unknown_class = True;
+BOOL AllowDebugChange = True;
+
+/*
+ used to check if the user specified a
+ logfile on the command line
+*/
+BOOL override_logfile;
+
+
+/*
+ * This is to allow assignment to DEBUGLEVEL before the debug
+ * system has been initialised.
+ */
+static int debug_all_class_hack = 1;
+static BOOL debug_all_class_isset_hack = True;
+
+static int debug_num_classes = 0;
+int *DEBUGLEVEL_CLASS = &debug_all_class_hack;
+BOOL *DEBUGLEVEL_CLASS_ISSET = &debug_all_class_isset_hack;
+
+/* DEBUGLEVEL is #defined to *debug_level */
+int DEBUGLEVEL = &debug_all_class_hack;
+
+
+/* -------------------------------------------------------------------------- **
+ * Internal variables.
+ *
+ * stdout_logging - Default False, if set to True then dbf will be set to
+ * stdout and debug output will go to dbf only, and not
+ * to syslog. Set in setup_logging() and read in Debug1().
+ *
+ * debug_count - Number of debug messages that have been output.
+ * Used to check log size.
+ *
+ * syslog_level - Internal copy of the message debug level. Written by
+ * dbghdr() and read by Debug1().
+ *
+ * format_bufr - Used to format debug messages. The dbgtext() function
+ * prints debug messages to a string, and then passes the
+ * string to format_debug_text(), which uses format_bufr
+ * to build the formatted output.
+ *
+ * format_pos - Marks the first free byte of the format_bufr.
+ *
+ *
+ * log_overflow - When this variable is True, never attempt to check the
+ * size of the log. This is a hack, so that we can write
+ * a message using DEBUG, from open_logs() when we
+ * are unable to open a new log file for some reason.
+ */
+
+static BOOL stdout_logging = False;
+static int debug_count = 0;
+#ifdef WITH_SYSLOG
+static int syslog_level = 0;
+#endif
+static pstring format_bufr = { '\0' };
+static size_t format_pos = 0;
+static BOOL log_overflow = False;
+
+/*
+ * Define all the debug class selection names here. Names *MUST NOT* contain
+ * white space. There must be one name for each DBGC_<class name>, and they
+ * must be in the table in the order of DBGC_<class name>..
+ */
+static const char *default_classname_table[] = {
+ "all", /* DBGC_ALL; index refs traditional DEBUGLEVEL */
+ "tdb", /* DBGC_TDB */
+ "printdrivers", /* DBGC_PRINTDRIVERS */
+ "lanman", /* DBGC_LANMAN */
+ "smb", /* DBGC_SMB */
+ "rpc_parse", /* DBGC_RPC_PARSE */
+ "rpc_srv", /* DBGC_RPC_SRV */
+ "rpc_cli", /* DBGC_RPC_CLI */
+ "passdb", /* DBGC_PASSDB */
+ "sam", /* DBGC_SAM */
+ "auth", /* DBGC_AUTH */
+ "winbind", /* DBGC_WINBIND */
+ "vfs", /* DBGC_VFS */
+ "idmap", /* DBGC_IDMAP */
+ "quota", /* DBGC_QUOTA */
+ NULL
+};
+
+static char **classname_table = NULL;
+
+
+/* -------------------------------------------------------------------------- **
+ * Functions...
+ */
+
+
+/****************************************************************************
+utility lists registered debug class names's
+****************************************************************************/
+
+#define MAX_CLASS_NAME_SIZE 1024
+
+static char *debug_list_class_names_and_levels(void)
+{
+ int i, dim;
+ char **list;
+ char *buf = NULL;
+ char *b;
+ BOOL err = False;
+
+ if (DEBUGLEVEL_CLASS == &debug_all_class_hack)
+ return NULL;
+
+ list = calloc(debug_num_classes + 1, sizeof(char *));
+ if (!list)
+ return NULL;
+
+ /* prepare strings */
+ for (i = 0, dim = 0; i < debug_num_classes; i++) {
+ int l = asprintf(&list[i],
+ "%s:%d ",
+ classname_table[i],
+ DEBUGLEVEL_CLASS_ISSET[i]?DEBUGLEVEL_CLASS[i]:DEBUGLEVEL);
+ if (l < 0 || l > MAX_CLASS_NAME_SIZE) {
+ err = True;
+ goto done;
+ }
+ dim += l;
+ }
+
+ /* create single string list */
+ b = buf = malloc(dim);
+ if (!buf) {
+ err = True;
+ goto done;
+ }
+ for (i = 0; i < debug_num_classes; i++) {
+ int l = strlen(list[i]);
+ strncpy(b, list[i], l);
+ b = b + l;
+ }
+ b[-1] = '\0';
+
+done:
+ /* free strings list */
+ for (i = 0; i < debug_num_classes; i++)
+ if (list[i]) free(list[i]);
+ free(list);
+
+ if (err) {
+ if (buf)
+ free(buf);
+ return NULL;
+ } else {
+ return buf;
+ }
+}
+
+/****************************************************************************
+utility access to debug class names's
+****************************************************************************/
+const char *debug_classname_from_index(int ndx)
+{
+ if (ndx < 0 || ndx >= debug_num_classes)
+ return NULL;
+ else
+ return classname_table[ndx];
+}
+
+/****************************************************************************
+utility to translate names to debug class index's (internal version)
+****************************************************************************/
+static int debug_lookup_classname_int(const char* classname)
+{
+ int i;
+
+ if (!classname) return -1;
+
+ for (i=0; i < debug_num_classes; i++) {
+ if (strcmp(classname, classname_table[i])==0)
+ return i;
+ }
+ return -1;
+}
+
+/****************************************************************************
+Add a new debug class to the system
+****************************************************************************/
+int debug_add_class(const char *classname)
+{
+ int ndx;
+ void *new_ptr;
+
+ if (!classname)
+ return -1;
+
+ /* check the init has yet been called */
+ debug_init();
+
+ ndx = debug_lookup_classname_int(classname);
+ if (ndx >= 0)
+ return ndx;
+ ndx = debug_num_classes;
+
+ new_ptr = DEBUGLEVEL_CLASS;
+ if (DEBUGLEVEL_CLASS == &debug_all_class_hack)
+ {
+ /* Initial loading... */
+ new_ptr = NULL;
+ }
+ new_ptr = Realloc(new_ptr,
+ sizeof(int) * (debug_num_classes + 1));
+ if (!new_ptr)
+ return -1;
+ DEBUGLEVEL_CLASS = new_ptr;
+ DEBUGLEVEL_CLASS[ndx] = 0;
+
+ /* debug_level is the pointer used for the DEBUGLEVEL-thingy */
+ if (ndx==0)
+ {
+ /* Transfer the initial level from debug_all_class_hack */
+ DEBUGLEVEL_CLASS[ndx] = DEBUGLEVEL;
+ }
+ debug_level = DEBUGLEVEL_CLASS;
+
+ new_ptr = DEBUGLEVEL_CLASS_ISSET;
+ if (new_ptr == &debug_all_class_isset_hack)
+ {
+ new_ptr = NULL;
+ }
+ new_ptr = Realloc(new_ptr,
+ sizeof(BOOL) * (debug_num_classes + 1));
+ if (!new_ptr)
+ return -1;
+ DEBUGLEVEL_CLASS_ISSET = new_ptr;
+ DEBUGLEVEL_CLASS_ISSET[ndx] = False;
+
+ new_ptr = Realloc(classname_table,
+ sizeof(char *) * (debug_num_classes + 1));
+ if (!new_ptr)
+ return -1;
+ classname_table = new_ptr;
+
+ classname_table[ndx] = strdup(classname);
+ if (! classname_table[ndx])
+ return -1;
+
+ debug_num_classes++;
+
+ return ndx;
+}
+
+/****************************************************************************
+utility to translate names to debug class index's (public version)
+****************************************************************************/
+int debug_lookup_classname(const char *classname)
+{
+ int ndx;
+
+ if (!classname || !*classname) return -1;
+
+ ndx = debug_lookup_classname_int(classname);
+
+ if (ndx != -1)
+ return ndx;
+
+ if (debug_warn_unknown_class)
+ {
+ DEBUG(0, ("debug_lookup_classname(%s): Unknown class\n",
+ classname));
+ }
+ if (debug_auto_add_unknown_class)
+ {
+ return debug_add_class(classname);
+ }
+ return -1;
+}
+
+
+/****************************************************************************
+dump the current registered debug levels
+****************************************************************************/
+static void debug_dump_status(int level)
+{
+ int q;
+
+ DEBUG(level, ("INFO: Current debug levels:\n"));
+ for (q = 0; q < debug_num_classes; q++)
+ {
+ DEBUGADD(level, (" %s: %s/%d\n",
+ classname_table[q],
+ (DEBUGLEVEL_CLASS_ISSET[q]
+ ? "True" : "False"),
+ DEBUGLEVEL_CLASS[q]));
+ }
+}
+
+/****************************************************************************
+parse the debug levels from smbcontrol. Example debug level parameter:
+ printdrivers:7
+****************************************************************************/
+static BOOL debug_parse_params(char **params)
+{
+ int i, ndx;
+ char *class_name;
+ char *class_level;
+
+ if (!params)
+ return False;
+
+ /* Allow DBGC_ALL to be specified w/o requiring its class name e.g."10"
+ * v.s. "all:10", this is the traditional way to set DEBUGLEVEL
+ */
+ if (isdigit((int)params[0][0])) {
+ DEBUGLEVEL_CLASS[DBGC_ALL] = atoi(params[0]);
+ DEBUGLEVEL_CLASS_ISSET[DBGC_ALL] = True;
+ i = 1; /* start processing at the next params */
+ }
+ else
+ i = 0; /* DBGC_ALL not specified OR class name was included */
+
+ /* Fill in new debug class levels */
+ for (; i < debug_num_classes && params[i]; i++) {
+ if ((class_name=strtok(params[i],":")) &&
+ (class_level=strtok(NULL, "\0")) &&
+ ((ndx = debug_lookup_classname(class_name)) != -1)) {
+ DEBUGLEVEL_CLASS[ndx] = atoi(class_level);
+ DEBUGLEVEL_CLASS_ISSET[ndx] = True;
+ } else {
+ DEBUG(0,("debug_parse_params: unrecognized debug class name or format [%s]\n", params[i]));
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/****************************************************************************
+parse the debug levels from smb.conf. Example debug level string:
+ 3 tdb:5 printdrivers:7
+Note: the 1st param has no "name:" preceeding it.
+****************************************************************************/
+BOOL debug_parse_levels(const char *params_str)
+{
+ char **params;
+
+ /* Just in case */
+ debug_init();
+
+ if (AllowDebugChange == False)
+ return True;
+
+ params = str_list_make(params_str, NULL);
+
+ if (debug_parse_params(params))
+ {
+ debug_dump_status(5);
+ str_list_free(&params);
+ return True;
+ } else {
+ str_list_free(&params);
+ return False;
+ }
+}
+
+/****************************************************************************
+receive a "set debug level" message
+****************************************************************************/
+static void debug_message(int msg_type, pid_t src, void *buf, size_t len)
+{
+ const char *params_str = buf;
+
+ /* Check, it's a proper string! */
+ if (params_str[len-1] != '\0')
+ {
+ DEBUG(1, ("Invalid debug message from pid %u to pid %u\n",
+ (unsigned int)src, (unsigned int)getpid()));
+ return;
+ }
+
+ DEBUG(3, ("INFO: Remote set of debug to `%s' (pid %u from pid %u)\n",
+ params_str, (unsigned int)getpid(), (unsigned int)src));
+
+ debug_parse_levels(params_str);
+}
+
+
+/****************************************************************************
+send a "set debug level" message
+****************************************************************************/
+void debug_message_send(pid_t pid, const char *params_str)
+{
+ if (!params_str)
+ return;
+ message_send_pid(pid, MSG_DEBUG, params_str, strlen(params_str) + 1,
+ False);
+}
+
+/****************************************************************************
+ Return current debug level.
+****************************************************************************/
+
+static void debuglevel_message(int msg_type, pid_t src, void *buf, size_t len)
+{
+ char *message = debug_list_class_names_and_levels();
+
+ DEBUG(1,("INFO: Received REQ_DEBUGLEVEL message from PID %u\n",(unsigned int)src));
+ message_send_pid(src, MSG_DEBUGLEVEL, message, strlen(message) + 1, True);
+
+ SAFE_FREE(message);
+}
+
+/****************************************************************************
+Init debugging (one time stuff)
+****************************************************************************/
+void debug_init(void)
+{
+ static BOOL initialised = False;
+ const char **p;
+
+ if (initialised)
+ return;
+
+ initialised = True;
+
+ message_register(MSG_DEBUG, debug_message);
+ message_register(MSG_REQ_DEBUGLEVEL, debuglevel_message);
+
+ for(p = default_classname_table; *p; p++)
+ {
+ debug_add_class(*p);
+ }
+}
+
+
+/* ************************************************************************** **
+ * get ready for syslog stuff
+ * ************************************************************************** **
+ */
+void setup_logging(const char *pname, BOOL interactive)
+{
+ debug_init();
+
+ /* reset to allow multiple setup calls, going from interactive to
+ non-interactive */
+ stdout_logging = False;
+ dbf = NULL;
+
+ if (interactive) {
+ stdout_logging = True;
+ dbf = x_stdout;
+ x_setbuf( x_stdout, NULL );
+ }
+#ifdef WITH_SYSLOG
+ else {
+ const char *p = strrchr_m( pname,'/' );
+ if (p)
+ pname = p + 1;
+#ifdef LOG_DAEMON
+ openlog( pname, LOG_PID, SYSLOG_FACILITY );
+#else
+ /* for old systems that have no facility codes. */
+ openlog( pname, LOG_PID );
+#endif
+ }
+#endif
+} /* setup_logging */
+
+/* ************************************************************************** **
+ * reopen the log files
+ * note that we now do this unconditionally
+ * We attempt to open the new debug fp before closing the old. This means
+ * if we run out of fd's we just keep using the old fd rather than aborting.
+ * Fix from dgibson@linuxcare.com.
+ * ************************************************************************** **
+ */
+
+BOOL reopen_logs( void )
+{
+ pstring fname;
+ mode_t oldumask;
+ XFILE *new_dbf = NULL;
+ XFILE *old_dbf = NULL;
+ BOOL ret = True;
+
+ if (stdout_logging)
+ return True;
+
+ oldumask = umask( 022 );
+
+ pstrcpy(fname, debugf );
+
+ if (lp_loaded()) {
+ char *logfname;
+
+ logfname = lp_logfile();
+ if (*logfname)
+ pstrcpy(fname, logfname);
+ }
+
+ pstrcpy( debugf, fname );
+ new_dbf = x_fopen( debugf, O_WRONLY|O_APPEND|O_CREAT, 0644);
+
+ if (!new_dbf) {
+ log_overflow = True;
+ DEBUG(0, ("Unable to open new log file %s: %s\n", debugf, strerror(errno)));
+ log_overflow = False;
+ if (dbf)
+ x_fflush(dbf);
+ ret = False;
+ } else {
+ x_setbuf(new_dbf, NULL);
+ old_dbf = dbf;
+ dbf = new_dbf;
+ if (old_dbf)
+ (void) x_fclose(old_dbf);
+ }
+
+ /* Fix from klausr@ITAP.Physik.Uni-Stuttgart.De
+ * to fix problem where smbd's that generate less
+ * than 100 messages keep growing the log.
+ */
+ force_check_log_size();
+ (void)umask(oldumask);
+
+ /* Take over stderr to catch ouput into logs */
+ if (dbf && sys_dup2(x_fileno(dbf), 2) == -1) {
+ close_low_fds(True); /* Close stderr too, if dup2 can't point it
+ at the logfile */
+ }
+
+ return ret;
+}
+
+/* ************************************************************************** **
+ * Force a check of the log size.
+ * ************************************************************************** **
+ */
+void force_check_log_size( void )
+{
+ debug_count = 100;
+}
+
+/***************************************************************************
+ Check to see if there is any need to check if the logfile has grown too big.
+**************************************************************************/
+
+BOOL need_to_check_log_size( void )
+{
+ int maxlog;
+
+ if( debug_count < 100 )
+ return( False );
+
+ maxlog = lp_max_log_size() * 1024;
+ if( !dbf || maxlog <= 0 ) {
+ debug_count = 0;
+ return(False);
+ }
+ return( True );
+}
+
+/* ************************************************************************** **
+ * Check to see if the log has grown to be too big.
+ * ************************************************************************** **
+ */
+
+void check_log_size( void )
+{
+ int maxlog;
+ SMB_STRUCT_STAT st;
+
+ /*
+ * We need to be root to check/change log-file, skip this and let the main
+ * loop check do a new check as root.
+ */
+
+ if( geteuid() != 0 )
+ return;
+
+ if(log_overflow || !need_to_check_log_size() )
+ return;
+
+ maxlog = lp_max_log_size() * 1024;
+
+ if( sys_fstat( x_fileno( dbf ), &st ) == 0 && st.st_size > maxlog ) {
+ (void)reopen_logs();
+ if( dbf && get_file_size( debugf ) > maxlog ) {
+ pstring name;
+
+ slprintf( name, sizeof(name)-1, "%s.old", debugf );
+ (void)rename( debugf, name );
+
+ if (!reopen_logs()) {
+ /* We failed to reopen a log - continue using the old name. */
+ (void)rename(name, debugf);
+ }
+ }
+ }
+
+ /*
+ * Here's where we need to panic if dbf == NULL..
+ */
+
+ if(dbf == NULL) {
+ /* This code should only be reached in very strange
+ * circumstances. If we merely fail to open the new log we
+ * should stick with the old one. ergo this should only be
+ * reached when opening the logs for the first time: at
+ * startup or when the log level is increased from zero.
+ * -dwg 6 June 2000
+ */
+ dbf = x_fopen( "/dev/console", O_WRONLY, 0);
+ if(dbf) {
+ DEBUG(0,("check_log_size: open of debug file %s failed - using console.\n",
+ debugf ));
+ } else {
+ /*
+ * We cannot continue without a debug file handle.
+ */
+ abort();
+ }
+ }
+ debug_count = 0;
+} /* check_log_size */
+
+/* ************************************************************************** **
+ * Write an debug message on the debugfile.
+ * This is called by dbghdr() and format_debug_text().
+ * ************************************************************************** **
+ */
+ int Debug1( const char *format_str, ... )
+{
+ va_list ap;
+ int old_errno = errno;
+
+ debug_count++;
+
+ if( stdout_logging )
+ {
+ va_start( ap, format_str );
+ if(dbf)
+ (void)x_vfprintf( dbf, format_str, ap );
+ va_end( ap );
+ errno = old_errno;
+ return( 0 );
+ }
+
+#ifdef WITH_SYSLOG
+ if( !lp_syslog_only() )
+#endif
+ {
+ if( !dbf )
+ {
+ mode_t oldumask = umask( 022 );
+
+ dbf = x_fopen( debugf, O_WRONLY|O_APPEND|O_CREAT, 0644 );
+ (void)umask( oldumask );
+ if( dbf )
+ {
+ x_setbuf( dbf, NULL );
+ }
+ else
+ {
+ errno = old_errno;
+ return(0);
+ }
+ }
+ }
+
+#ifdef WITH_SYSLOG
+ if( syslog_level < lp_syslog() )
+ {
+ /* map debug levels to syslog() priorities
+ * note that not all DEBUG(0, ...) calls are
+ * necessarily errors
+ */
+ static int priority_map[] = {
+ LOG_ERR, /* 0 */
+ LOG_WARNING, /* 1 */
+ LOG_NOTICE, /* 2 */
+ LOG_INFO, /* 3 */
+ };
+ int priority;
+ pstring msgbuf;
+
+ if( syslog_level >= ( sizeof(priority_map) / sizeof(priority_map[0]) )
+ || syslog_level < 0)
+ priority = LOG_DEBUG;
+ else
+ priority = priority_map[syslog_level];
+
+ va_start( ap, format_str );
+ vslprintf( msgbuf, sizeof(msgbuf)-1, format_str, ap );
+ va_end( ap );
+
+ msgbuf[255] = '\0';
+ syslog( priority, "%s", msgbuf );
+ }
+#endif
+
+ check_log_size();
+
+#ifdef WITH_SYSLOG
+ if( !lp_syslog_only() )
+#endif
+ {
+ va_start( ap, format_str );
+ if(dbf)
+ (void)x_vfprintf( dbf, format_str, ap );
+ va_end( ap );
+ if(dbf)
+ (void)x_fflush( dbf );
+ }
+
+ errno = old_errno;
+
+ return( 0 );
+ } /* Debug1 */
+
+
+/* ************************************************************************** **
+ * Print the buffer content via Debug1(), then reset the buffer.
+ *
+ * Input: none
+ * Output: none
+ *
+ * ************************************************************************** **
+ */
+static void bufr_print( void )
+ {
+ format_bufr[format_pos] = '\0';
+ (void)Debug1( "%s", format_bufr );
+ format_pos = 0;
+ } /* bufr_print */
+
+/* ************************************************************************** **
+ * Format the debug message text.
+ *
+ * Input: msg - Text to be added to the "current" debug message text.
+ *
+ * Output: none.
+ *
+ * Notes: The purpose of this is two-fold. First, each call to syslog()
+ * (used by Debug1(), see above) generates a new line of syslog
+ * output. This is fixed by storing the partial lines until the
+ * newline character is encountered. Second, printing the debug
+ * message lines when a newline is encountered allows us to add
+ * spaces, thus indenting the body of the message and making it
+ * more readable.
+ *
+ * ************************************************************************** **
+ */
+static void format_debug_text( char *msg )
+ {
+ size_t i;
+ BOOL timestamp = (!stdout_logging && (lp_timestamp_logs() ||
+ !(lp_loaded())));
+
+ for( i = 0; msg[i]; i++ )
+ {
+ /* Indent two spaces at each new line. */
+ if(timestamp && 0 == format_pos)
+ {
+ format_bufr[0] = format_bufr[1] = ' ';
+ format_pos = 2;
+ }
+
+ /* If there's room, copy the character to the format buffer. */
+ if( format_pos < FORMAT_BUFR_MAX )
+ format_bufr[format_pos++] = msg[i];
+
+ /* If a newline is encountered, print & restart. */
+ if( '\n' == msg[i] )
+ bufr_print();
+
+ /* If the buffer is full dump it out, reset it, and put out a line
+ * continuation indicator.
+ */
+ if( format_pos >= FORMAT_BUFR_MAX )
+ {
+ bufr_print();
+ (void)Debug1( " +>\n" );
+ }
+ }
+
+ /* Just to be safe... */
+ format_bufr[format_pos] = '\0';
+ } /* format_debug_text */
+
+/* ************************************************************************** **
+ * Flush debug output, including the format buffer content.
+ *
+ * Input: none
+ * Output: none
+ *
+ * ************************************************************************** **
+ */
+void dbgflush( void )
+ {
+ bufr_print();
+ if(dbf)
+ (void)x_fflush( dbf );
+ } /* dbgflush */
+
+/* ************************************************************************** **
+ * Print a Debug Header.
+ *
+ * Input: level - Debug level of the message (not the system-wide debug
+ * level. )
+ * file - Pointer to a string containing the name of the file
+ * from which this function was called, or an empty string
+ * if the __FILE__ macro is not implemented.
+ * func - Pointer to a string containing the name of the function
+ * from which this function was called, or an empty string
+ * if the __FUNCTION__ macro is not implemented.
+ * line - line number of the call to dbghdr, assuming __LINE__
+ * works.
+ *
+ * Output: Always True. This makes it easy to fudge a call to dbghdr()
+ * in a macro, since the function can be called as part of a test.
+ * Eg: ( (level <= DEBUGLEVEL) && (dbghdr(level,"",line)) )
+ *
+ * Notes: This function takes care of setting syslog_level.
+ *
+ * ************************************************************************** **
+ */
+
+BOOL dbghdr( int level, const char *file, const char *func, int line )
+{
+ /* Ensure we don't lose any real errno value. */
+ int old_errno = errno;
+
+ if( format_pos ) {
+ /* This is a fudge. If there is stuff sitting in the format_bufr, then
+ * the *right* thing to do is to call
+ * format_debug_text( "\n" );
+ * to write the remainder, and then proceed with the new header.
+ * Unfortunately, there are several places in the code at which
+ * the DEBUG() macro is used to build partial lines. That in mind,
+ * we'll work under the assumption that an incomplete line indicates
+ * that a new header is *not* desired.
+ */
+ return( True );
+ }
+
+#ifdef WITH_SYSLOG
+ /* Set syslog_level. */
+ syslog_level = level;
+#endif
+
+ /* Don't print a header if we're logging to stdout. */
+ if( stdout_logging )
+ return( True );
+
+ /* Print the header if timestamps are turned on. If parameters are
+ * not yet loaded, then default to timestamps on.
+ */
+ if( lp_timestamp_logs() || !(lp_loaded()) ) {
+ char header_str[200];
+
+ header_str[0] = '\0';
+
+ if( lp_debug_pid())
+ slprintf(header_str,sizeof(header_str)-1,", pid=%u",(unsigned int)sys_getpid());
+
+ if( lp_debug_uid()) {
+ size_t hs_len = strlen(header_str);
+ slprintf(header_str + hs_len,
+ sizeof(header_str) - 1 - hs_len,
+ ", effective(%u, %u), real(%u, %u)",
+ (unsigned int)geteuid(), (unsigned int)getegid(),
+ (unsigned int)getuid(), (unsigned int)getgid());
+ }
+
+ /* Print it all out at once to prevent split syslog output. */
+ (void)Debug1( "[%s, %d%s] %s:%s(%d)\n",
+ timestring(lp_debug_hires_timestamp()), level,
+ header_str, file, func, line );
+ }
+
+ errno = old_errno;
+ return( True );
+}
+
+/* ************************************************************************** **
+ * Add text to the body of the "current" debug message via the format buffer.
+ *
+ * Input: format_str - Format string, as used in printf(), et. al.
+ * ... - Variable argument list.
+ *
+ * ..or.. va_alist - Old style variable parameter list starting point.
+ *
+ * Output: Always True. See dbghdr() for more info, though this is not
+ * likely to be used in the same way.
+ *
+ * ************************************************************************** **
+ */
+ BOOL dbgtext( const char *format_str, ... )
+ {
+ va_list ap;
+ pstring msgbuf;
+
+ va_start( ap, format_str );
+ vslprintf( msgbuf, sizeof(msgbuf)-1, format_str, ap );
+ va_end( ap );
+
+ format_debug_text( msgbuf );
+
+ return( True );
+ } /* dbgtext */
+
+
+/* ************************************************************************** */
diff --git a/source/lib/dmallocmsg.c b/source/lib/dmallocmsg.c
new file mode 100644
index 00000000000..a83ed518d7c
--- /dev/null
+++ b/source/lib/dmallocmsg.c
@@ -0,0 +1,72 @@
+/*
+ samba -- Unix SMB/CIFS implementation.
+ Copyright (C) 2002 by Martin Pool
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/**
+ * @file dmallocmsg.c
+ *
+ * Glue code to cause dmalloc info to come out when we receive a prod
+ * over samba messaging.
+ **/
+
+#ifdef ENABLE_DMALLOC
+static unsigned long our_dm_mark = 0;
+#endif /* ENABLE_DMALLOC */
+
+
+/**
+ * Respond to a POOL_USAGE message by sending back string form of memory
+ * usage stats.
+ **/
+static void msg_req_dmalloc_mark(int UNUSED(msg_type), pid_t UNUSED(src_pid),
+ void *UNUSED(buf), size_t UNUSED(len))
+{
+#ifdef ENABLE_DMALLOC
+ our_dm_mark = dmalloc_mark();
+ DEBUG(2,("Got MSG_REQ_DMALLOC_MARK: mark set\n"));
+#else
+ DEBUG(2,("Got MSG_REQ_DMALLOC_MARK but dmalloc not in this process\n"));
+#endif
+}
+
+
+
+static void msg_req_dmalloc_log_changed(int UNUSED(msg_type),
+ pid_t UNUSED(src_pid),
+ void *UNUSED(buf), size_t UNUSED(len))
+{
+#ifdef ENABLE_DMALLOC
+ dmalloc_log_changed(our_dm_mark, True, True, True);
+ DEBUG(2,("Got MSG_REQ_DMALLOC_LOG_CHANGED: done\n"));
+#else
+ DEBUG(2,("Got MSG_REQ_DMALLOC_LOG_CHANGED but dmalloc not in this process\n"));
+#endif
+}
+
+
+/**
+ * Register handler for MSG_REQ_POOL_USAGE
+ **/
+void register_dmalloc_msgs(void)
+{
+ message_register(MSG_REQ_DMALLOC_MARK, msg_req_dmalloc_mark);
+ message_register(MSG_REQ_DMALLOC_LOG_CHANGED, msg_req_dmalloc_log_changed);
+ DEBUG(2, ("Registered MSG_REQ_DMALLOC_MARK and LOG_CHANGED\n"));
+}
diff --git a/source/lib/dprintf.c b/source/lib/dprintf.c
new file mode 100644
index 00000000000..c62a1f41d10
--- /dev/null
+++ b/source/lib/dprintf.c
@@ -0,0 +1,113 @@
+/*
+ Unix SMB/CIFS implementation.
+ display print functions
+ Copyright (C) Andrew Tridgell 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+/*
+ this module provides functions for printing internal strings in the "display charset"
+ This charset may be quite different from the chosen unix charset
+
+ Eventually these functions will need to take care of column count constraints
+
+ The d_ prefix on print functions in Samba refers to the display character set
+ conversion
+*/
+
+#include "includes.h"
+
+ int d_vfprintf(FILE *f, const char *format, va_list ap)
+{
+ char *p, *p2;
+ int ret, maxlen, clen;
+ const char *msgstr;
+ va_list ap2;
+
+ /* do any message translations */
+ msgstr = lang_msg(format);
+ if (!msgstr) return -1;
+
+ VA_COPY(ap2, ap);
+
+ ret = vasprintf(&p, msgstr, ap2);
+
+ lang_msg_free(msgstr);
+
+ if (ret <= 0) return ret;
+
+ /* now we have the string in unix format, convert it to the display
+ charset, but beware of it growing */
+ maxlen = ret*2;
+again:
+ p2 = malloc(maxlen);
+ if (!p2) {
+ SAFE_FREE(p);
+ return -1;
+ }
+ clen = convert_string(CH_UNIX, CH_DISPLAY, p, ret, p2, maxlen, True);
+
+ if (clen >= maxlen) {
+ /* it didn't fit - try a larger buffer */
+ maxlen *= 2;
+ SAFE_FREE(p2);
+ goto again;
+ }
+
+ /* good, its converted OK */
+ SAFE_FREE(p);
+ ret = fwrite(p2, 1, clen, f);
+ SAFE_FREE(p2);
+
+ return ret;
+}
+
+
+ int d_fprintf(FILE *f, const char *format, ...)
+{
+ int ret;
+ va_list ap;
+
+ va_start(ap, format);
+ ret = d_vfprintf(f, format, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+static FILE *outfile;
+
+ int d_printf(const char *format, ...)
+{
+ int ret;
+ va_list ap;
+
+ if (!outfile) outfile = stdout;
+
+ va_start(ap, format);
+ ret = d_vfprintf(outfile, format, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+/* interactive programs need a way of tell d_*() to write to stderr instead
+ of stdout */
+void display_set_stderr(void)
+{
+ outfile = stderr;
+}
diff --git a/source/lib/dummyroot.c b/source/lib/dummyroot.c
new file mode 100644
index 00000000000..c8465cb791a
--- /dev/null
+++ b/source/lib/dummyroot.c
@@ -0,0 +1,33 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ Copyright (C) Tim Potter 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* Stupid dummy functions required due to the horrible dependency mess
+ in Samba. */
+
+void become_root(void)
+{
+ return;
+}
+
+void unbecome_root(void)
+{
+ return;
+}
diff --git a/source/lib/dummysmbd.c b/source/lib/dummysmbd.c
new file mode 100644
index 00000000000..17bc3217743
--- /dev/null
+++ b/source/lib/dummysmbd.c
@@ -0,0 +1,29 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ Copyright (C) Gerald (Jerry) Carter 2004.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* Stupid dummy functions required due to the horrible dependency mess
+ in Samba. */
+
+void decrement_smbd_process_count( void )
+{
+ return;
+}
+
diff --git a/source/lib/fault.c b/source/lib/fault.c
new file mode 100644
index 00000000000..d8364ff2257
--- /dev/null
+++ b/source/lib/fault.c
@@ -0,0 +1,83 @@
+/*
+ Unix SMB/CIFS implementation.
+ Critical Fault handling
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static void (*cont_fn)(void *);
+
+/*******************************************************************
+report a fault
+********************************************************************/
+static void fault_report(int sig)
+{
+ static int counter;
+
+ if (counter) _exit(1);
+
+ counter++;
+
+ DEBUG(0,("===============================================================\n"));
+ DEBUG(0,("INTERNAL ERROR: Signal %d in pid %d (%s)",sig,(int)sys_getpid(),SAMBA_VERSION_STRING));
+ DEBUG(0,("\nPlease read the appendix Bugs of the Samba HOWTO collection\n"));
+ DEBUG(0,("===============================================================\n"));
+
+ smb_panic("internal error");
+
+ if (cont_fn) {
+ cont_fn(NULL);
+#ifdef SIGSEGV
+ CatchSignal(SIGSEGV,SIGNAL_CAST SIG_DFL);
+#endif
+#ifdef SIGBUS
+ CatchSignal(SIGBUS,SIGNAL_CAST SIG_DFL);
+#endif
+#ifdef SIGABRT
+ CatchSignal(SIGABRT,SIGNAL_CAST SIG_DFL);
+#endif
+ return; /* this should cause a core dump */
+ }
+ exit(1);
+}
+
+/****************************************************************************
+catch serious errors
+****************************************************************************/
+static void sig_fault(int sig)
+{
+ fault_report(sig);
+}
+
+/*******************************************************************
+setup our fault handlers
+********************************************************************/
+void fault_setup(void (*fn)(void *))
+{
+ cont_fn = fn;
+
+#ifdef SIGSEGV
+ CatchSignal(SIGSEGV,SIGNAL_CAST sig_fault);
+#endif
+#ifdef SIGBUS
+ CatchSignal(SIGBUS,SIGNAL_CAST sig_fault);
+#endif
+#ifdef SIGABRT
+ CatchSignal(SIGABRT,SIGNAL_CAST sig_fault);
+#endif
+}
diff --git a/source/lib/fsusage.c b/source/lib/fsusage.c
new file mode 100644
index 00000000000..bb7cff06453
--- /dev/null
+++ b/source/lib/fsusage.c
@@ -0,0 +1,148 @@
+/*
+ Unix SMB/CIFS implementation.
+ functions to calculate the free disk space
+ Copyright (C) Andrew Tridgell 1998-2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+
+/* Return the number of TOSIZE-byte blocks used by
+ BLOCKS FROMSIZE-byte blocks, rounding away from zero.
+*/
+static SMB_BIG_UINT adjust_blocks(SMB_BIG_UINT blocks, SMB_BIG_UINT fromsize, SMB_BIG_UINT tosize)
+{
+ if (fromsize == tosize) /* e.g., from 512 to 512 */
+ return blocks;
+ else if (fromsize > tosize) /* e.g., from 2048 to 512 */
+ return blocks * (fromsize / tosize);
+ else /* e.g., from 256 to 512 */
+ return (blocks + 1) / (tosize / fromsize);
+}
+
+/* this does all of the system specific guff to get the free disk space.
+ It is derived from code in the GNU fileutils package, but has been
+ considerably mangled for use here
+
+ results are returned in *dfree and *dsize, in 512 byte units
+*/
+int sys_fsusage(const char *path, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+#ifdef STAT_STATFS3_OSF1
+#define CONVERT_BLOCKS(B) adjust_blocks ((SMB_BIG_UINT)(B), (SMB_BIG_UINT)fsd.f_fsize, (SMB_BIG_UINT)512)
+ struct statfs fsd;
+
+ if (statfs (path, &fsd, sizeof (struct statfs)) != 0)
+ return -1;
+#endif /* STAT_STATFS3_OSF1 */
+
+#ifdef STAT_STATFS2_FS_DATA /* Ultrix */
+#define CONVERT_BLOCKS(B) adjust_blocks ((SMB_BIG_UINT)(B), (SMB_BIG_UINT)1024, (SMB_BIG_UINT)512)
+ struct fs_data fsd;
+
+ if (statfs (path, &fsd) != 1)
+ return -1;
+
+ (*dsize) = CONVERT_BLOCKS (fsd.fd_req.btot);
+ (*dfree) = CONVERT_BLOCKS (fsd.fd_req.bfreen);
+#endif /* STAT_STATFS2_FS_DATA */
+
+#ifdef STAT_STATFS2_BSIZE /* 4.3BSD, SunOS 4, HP-UX, AIX */
+#define CONVERT_BLOCKS(B) adjust_blocks ((SMB_BIG_UINT)(B), (SMB_BIG_UINT)fsd.f_bsize, (SMB_BIG_UINT)512)
+ struct statfs fsd;
+
+ if (statfs (path, &fsd) < 0)
+ return -1;
+
+#ifdef STATFS_TRUNCATES_BLOCK_COUNTS
+ /* In SunOS 4.1.2, 4.1.3, and 4.1.3_U1, the block counts in the
+ struct statfs are truncated to 2GB. These conditions detect that
+ truncation, presumably without botching the 4.1.1 case, in which
+ the values are not truncated. The correct counts are stored in
+ undocumented spare fields. */
+ if (fsd.f_blocks == 0x1fffff && fsd.f_spare[0] > 0) {
+ fsd.f_blocks = fsd.f_spare[0];
+ fsd.f_bfree = fsd.f_spare[1];
+ fsd.f_bavail = fsd.f_spare[2];
+ }
+#endif /* STATFS_TRUNCATES_BLOCK_COUNTS */
+#endif /* STAT_STATFS2_BSIZE */
+
+
+#ifdef STAT_STATFS2_FSIZE /* 4.4BSD */
+#define CONVERT_BLOCKS(B) adjust_blocks ((SMB_BIG_UINT)(B), (SMB_BIG_UINT)fsd.f_fsize, (SMB_BIG_UINT)512)
+
+ struct statfs fsd;
+
+ if (statfs (path, &fsd) < 0)
+ return -1;
+#endif /* STAT_STATFS2_FSIZE */
+
+#ifdef STAT_STATFS4 /* SVR3, Dynix, Irix, AIX */
+# if _AIX || defined(_CRAY)
+# define CONVERT_BLOCKS(B) adjust_blocks ((SMB_BIG_UINT)(B), (SMB_BIG_UINT)fsd.f_bsize, (SMB_BIG_UINT)512)
+# ifdef _CRAY
+# define f_bavail f_bfree
+# endif
+# else
+# define CONVERT_BLOCKS(B) ((SMB_BIG_UINT)B)
+# ifndef _SEQUENT_ /* _SEQUENT_ is DYNIX/ptx */
+# ifndef DOLPHIN /* DOLPHIN 3.8.alfa/7.18 has f_bavail */
+# define f_bavail f_bfree
+# endif
+# endif
+# endif
+
+ struct statfs fsd;
+
+ if (statfs (path, &fsd, sizeof fsd, 0) < 0)
+ return -1;
+ /* Empirically, the block counts on most SVR3 and SVR3-derived
+ systems seem to always be in terms of 512-byte blocks,
+ no matter what value f_bsize has. */
+
+#endif /* STAT_STATFS4 */
+
+#if defined(STAT_STATVFS) || defined(STAT_STATVFS64) /* SVR4 */
+# define CONVERT_BLOCKS(B) \
+ adjust_blocks ((SMB_BIG_UINT)(B), fsd.f_frsize ? (SMB_BIG_UINT)fsd.f_frsize : (SMB_BIG_UINT)fsd.f_bsize, (SMB_BIG_UINT)512)
+
+#ifdef STAT_STATVFS64
+ struct statvfs64 fsd;
+ if (statvfs64(path, &fsd) < 0) return -1;
+#else
+ struct statvfs fsd;
+ if (statvfs(path, &fsd) < 0) return -1;
+#endif
+
+ /* f_frsize isn't guaranteed to be supported. */
+
+#endif /* STAT_STATVFS */
+
+#ifndef CONVERT_BLOCKS
+ /* we don't have any dfree code! */
+ return -1;
+#else
+#if !defined(STAT_STATFS2_FS_DATA)
+ /* !Ultrix */
+ (*dsize) = CONVERT_BLOCKS (fsd.f_blocks);
+ (*dfree) = CONVERT_BLOCKS (fsd.f_bavail);
+#endif /* not STAT_STATFS2_FS_DATA */
+#endif
+
+ return 0;
+}
diff --git a/source/lib/gencache.c b/source/lib/gencache.c
new file mode 100644
index 00000000000..39e727c24fa
--- /dev/null
+++ b/source/lib/gencache.c
@@ -0,0 +1,379 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Generic, persistent and shared between processes cache mechanism for use
+ by various parts of the Samba code
+
+ Copyright (C) Rafal Szczesniak 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_TDB
+
+#define TIMEOUT_LEN 12
+#define CACHE_DATA_FMT "%12u/%s"
+
+static TDB_CONTEXT *cache;
+
+/**
+ * @file gencache.c
+ * @brief Generic, persistent and shared between processes cache mechanism
+ * for use by various parts of the Samba code
+ *
+ **/
+
+
+/**
+ * Cache initialisation function. Opens cache tdb file or creates
+ * it if does not exist.
+ *
+ * @return true on successful initialisation of the cache or
+ * false on failure
+ **/
+
+BOOL gencache_init(void)
+{
+ char* cache_fname = NULL;
+
+ /* skip file open if it's already opened */
+ if (cache) return True;
+
+ asprintf(&cache_fname, "%s/%s", lp_lockdir(), "gencache.tdb");
+ if (cache_fname)
+ DEBUG(5, ("Opening cache file at %s\n", cache_fname));
+ else {
+ DEBUG(0, ("Filename allocation failed.\n"));
+ return False;
+ }
+
+ cache = tdb_open_log(cache_fname, 0, TDB_DEFAULT,
+ O_RDWR|O_CREAT, 0644);
+
+ SAFE_FREE(cache_fname);
+ if (!cache) {
+ DEBUG(5, ("Attempt to open gencache.tdb has failed.\n"));
+ return False;
+ }
+ return True;
+}
+
+
+/**
+ * Cache shutdown function. Closes opened cache tdb file.
+ *
+ * @return true on successful closing the cache or
+ * false on failure during cache shutdown
+ **/
+
+BOOL gencache_shutdown(void)
+{
+ /* tdb_close routine returns -1 on error */
+ if (!cache) return False;
+ DEBUG(5, ("Closing cache file\n"));
+ return tdb_close(cache) != -1;
+}
+
+
+/**
+ * Set an entry in the cache file. If there's no such
+ * one, then add it.
+ *
+ * @param keystr string that represents a key of this entry
+ * @param value text representation value being cached
+ * @param timeout time when the value is expired
+ *
+ * @retval true when entry is successfuly stored
+ * @retval false on failure
+ **/
+
+BOOL gencache_set(const char *keystr, const char *value, time_t timeout)
+{
+ int ret;
+ TDB_DATA keybuf, databuf;
+ char* valstr = NULL;
+
+ /* fail completely if get null pointers passed */
+ SMB_ASSERT(keystr && value);
+
+ if (!gencache_init()) return False;
+
+ asprintf(&valstr, CACHE_DATA_FMT, (int)timeout, value);
+ if (!valstr)
+ return False;
+
+ keybuf.dptr = strdup(keystr);
+ keybuf.dsize = strlen(keystr)+1;
+ databuf.dptr = strdup(valstr);
+ databuf.dsize = strlen(valstr)+1;
+ DEBUG(10, ("Adding cache entry with key = %s; value = %s and timeout ="
+ " %s (%d seconds %s)\n", keybuf.dptr, value,ctime(&timeout),
+ (int)(timeout - time(NULL)),
+ timeout > time(NULL) ? "ahead" : "in the past"));
+
+ ret = tdb_store(cache, keybuf, databuf, 0);
+ SAFE_FREE(valstr);
+ SAFE_FREE(keybuf.dptr);
+ SAFE_FREE(databuf.dptr);
+
+ return ret == 0;
+}
+
+
+/**
+ * Set existing entry to the cache file.
+ *
+ * @param keystr string that represents a key of this entry
+ * @param valstr text representation value being cached
+ * @param timeout time when the value is expired
+ *
+ * @retval true when entry is successfuly set
+ * @retval false on failure
+ **/
+
+BOOL gencache_set_only(const char *keystr, const char *valstr, time_t timeout)
+{
+ int ret = -1;
+ TDB_DATA keybuf, databuf;
+ char *old_valstr, *datastr;
+ time_t old_timeout;
+
+ /* fail completely if get null pointers passed */
+ SMB_ASSERT(keystr && valstr);
+
+ if (!gencache_init()) return False;
+
+ /*
+ * Check whether entry exists in the cache
+ * Don't verify gencache_get exit code, since the entry may be expired
+ */
+ gencache_get(keystr, &old_valstr, &old_timeout);
+
+ if (!(old_valstr && old_timeout)) return False;
+
+ DEBUG(10, ("Setting cache entry with key = %s; old value = %s and old timeout \
+ = %s\n", keystr, old_valstr, ctime(&old_timeout)));
+
+ asprintf(&datastr, CACHE_DATA_FMT, (int)timeout, valstr);
+ keybuf.dptr = strdup(keystr);
+ keybuf.dsize = strlen(keystr)+1;
+ databuf.dptr = strdup(datastr);
+ databuf.dsize = strlen(datastr)+1;
+ DEBUGADD(10, ("New value = %s, new timeout = %s (%d seconds %s)", valstr,
+ ctime(&timeout), (int)(timeout - time(NULL)),
+ timeout > time(NULL) ? "ahead" : "in the past"));
+
+
+ ret = tdb_store(cache, keybuf, databuf, TDB_REPLACE);
+
+ SAFE_FREE(datastr);
+ SAFE_FREE(old_valstr);
+ SAFE_FREE(keybuf.dptr);
+ SAFE_FREE(databuf.dptr);
+
+ return ret == 0;
+}
+
+
+/**
+ * Delete one entry from the cache file.
+ *
+ * @param keystr string that represents a key of this entry
+ *
+ * @retval true upon successful deletion
+ * @retval false in case of failure
+ **/
+
+BOOL gencache_del(const char *keystr)
+{
+ int ret;
+ TDB_DATA keybuf;
+
+ /* fail completely if get null pointers passed */
+ SMB_ASSERT(keystr);
+
+ if (!gencache_init()) return False;
+
+ keybuf.dptr = strdup(keystr);
+ keybuf.dsize = strlen(keystr)+1;
+ DEBUG(10, ("Deleting cache entry (key = %s)\n", keystr));
+ ret = tdb_delete(cache, keybuf);
+
+ SAFE_FREE(keybuf.dptr);
+ return ret == 0;
+}
+
+
+/**
+ * Get existing entry from the cache file.
+ *
+ * @param keystr string that represents a key of this entry
+ * @param valstr buffer that is allocated and filled with the entry value
+ * buffer's disposing must be done outside
+ * @param timeout pointer to a time_t that is filled with entry's
+ * timeout
+ *
+ * @retval true when entry is successfuly fetched
+ * @retval False for failure
+ **/
+
+BOOL gencache_get(const char *keystr, char **valstr, time_t *timeout)
+{
+ TDB_DATA keybuf, databuf;
+
+ /* fail completely if get null pointers passed */
+ SMB_ASSERT(keystr);
+
+ if (!gencache_init())
+ return False;
+
+ keybuf.dptr = strdup(keystr);
+ keybuf.dsize = strlen(keystr)+1;
+ databuf = tdb_fetch(cache, keybuf);
+ SAFE_FREE(keybuf.dptr);
+
+ if (databuf.dptr && databuf.dsize > TIMEOUT_LEN) {
+ char* entry_buf = strndup(databuf.dptr, databuf.dsize);
+ char *v;
+ time_t t;
+
+ v = (char*)malloc(sizeof(char) *
+ (databuf.dsize - TIMEOUT_LEN));
+
+ SAFE_FREE(databuf.dptr);
+ sscanf(entry_buf, CACHE_DATA_FMT, (int*)&t, v);
+ SAFE_FREE(entry_buf);
+
+ DEBUG(10, ("Returning %s cache entry: key = %s, value = %s, "
+ "timeout = %s\n", t > time(NULL) ? "valid" :
+ "expired", keystr, v, ctime(&t)));
+
+ if (valstr)
+ *valstr = v;
+ else
+ SAFE_FREE(v);
+
+ if (timeout)
+ *timeout = t;
+
+ return t > time(NULL);
+
+ } else {
+ SAFE_FREE(databuf.dptr);
+
+ if (valstr)
+ *valstr = NULL;
+
+ if (timeout)
+ timeout = NULL;
+
+ DEBUG(10, ("Cache entry with key = %s couldn't be found\n",
+ keystr));
+
+ return False;
+ }
+}
+
+
+/**
+ * Iterate through all entries which key matches to specified pattern
+ *
+ * @param fn pointer to the function that will be supplied with each single
+ * matching cache entry (key, value and timeout) as an arguments
+ * @param data void pointer to an arbitrary data that is passed directly to the fn
+ * function on each call
+ * @param keystr_pattern pattern the existing entries' keys are matched to
+ *
+ **/
+
+void gencache_iterate(void (*fn)(const char* key, const char *value, time_t timeout, void* dptr),
+ void* data, const char* keystr_pattern)
+{
+ TDB_LIST_NODE *node, *first_node;
+ TDB_DATA databuf;
+ char *keystr = NULL, *valstr = NULL, *entry = NULL;
+ time_t timeout = 0;
+
+ /* fail completely if get null pointers passed */
+ SMB_ASSERT(fn && keystr_pattern);
+
+ if (!gencache_init()) return;
+
+ DEBUG(5, ("Searching cache keys with pattern %s\n", keystr_pattern));
+ node = tdb_search_keys(cache, keystr_pattern);
+ first_node = node;
+
+ while (node) {
+ /* ensure null termination of the key string */
+ keystr = strndup(node->node_key.dptr, node->node_key.dsize);
+
+ /*
+ * We don't use gencache_get function, because we need to iterate through
+ * all of the entries. Validity verification is up to fn routine.
+ */
+ databuf = tdb_fetch(cache, node->node_key);
+ if (!databuf.dptr || databuf.dsize <= TIMEOUT_LEN) {
+ SAFE_FREE(databuf.dptr);
+ SAFE_FREE(keystr);
+ node = node->next;
+ continue;
+ }
+ entry = strndup(databuf.dptr, databuf.dsize);
+ SAFE_FREE(databuf.dptr);
+ valstr = (char*)malloc(sizeof(char) * (databuf.dsize - TIMEOUT_LEN));
+ sscanf(entry, CACHE_DATA_FMT, (int*)(&timeout), valstr);
+
+ DEBUG(10, ("Calling function with arguments (key = %s, value = %s, timeout = %s)\n",
+ keystr, valstr, ctime(&timeout)));
+ fn(keystr, valstr, timeout, data);
+
+ SAFE_FREE(valstr);
+ SAFE_FREE(entry);
+ SAFE_FREE(keystr);
+ node = node->next;
+ }
+
+ tdb_search_list_free(first_node);
+}
+
+/********************************************************************
+ lock a key
+********************************************************************/
+
+int gencache_lock_entry( const char *key )
+{
+ if (!gencache_init())
+ return -1;
+
+ return tdb_lock_bystring(cache, key, 0);
+}
+
+/********************************************************************
+ unlock a key
+********************************************************************/
+
+void gencache_unlock_entry( const char *key )
+{
+ if (!gencache_init())
+ return;
+
+ tdb_unlock_bystring(cache, key);
+ return;
+}
+
+
diff --git a/source/lib/genparser.c b/source/lib/genparser.c
new file mode 100644
index 00000000000..7476b5d0aff
--- /dev/null
+++ b/source/lib/genparser.c
@@ -0,0 +1,783 @@
+/*
+ Copyright (C) Andrew Tridgell <genstruct@tridgell.net> 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ automatic marshalling/unmarshalling system for C structures
+*/
+
+#include "includes.h"
+
+/* see if a range of memory is all zero. Used to prevent dumping of zero elements */
+static int all_zero(const char *ptr, unsigned size)
+{
+ int i;
+ if (!ptr) return 1;
+ for (i=0;i<size;i++) {
+ if (ptr[i]) return 0;
+ }
+ return 1;
+}
+
+/* encode a buffer of bytes into a escaped string */
+static char *encode_bytes(TALLOC_CTX *mem_ctx, const char *ptr, unsigned len)
+{
+ const char *hexdig = "0123456789abcdef";
+ char *ret, *p;
+ unsigned i;
+ ret = talloc(mem_ctx, len*3 + 1); /* worst case size */
+ if (!ret) return NULL;
+ for (p=ret,i=0;i<len;i++) {
+ if (isalnum(ptr[i]) || isspace(ptr[i]) ||
+ (ispunct(ptr[i]) && !strchr("\\{}", ptr[i]))) {
+ *p++ = ptr[i];
+ } else {
+ unsigned char c = *(unsigned char *)(ptr+i);
+ if (c == 0 && all_zero(ptr+i, len-i)) break;
+ p[0] = '\\';
+ p[1] = hexdig[c>>4];
+ p[2] = hexdig[c&0xF];
+ p += 3;
+ }
+ }
+
+ *p = 0;
+
+ return ret;
+}
+
+/* decode an escaped string from encode_bytes() into a buffer */
+static char *decode_bytes(TALLOC_CTX *mem_ctx, const char *s, unsigned *len)
+{
+ char *ret, *p;
+ unsigned i;
+ int slen = strlen(s) + 1;
+
+ ret = talloc(mem_ctx, slen); /* worst case length */
+ if (!ret)
+ return NULL;
+ memset(ret, 0, slen);
+
+ if (*s == '{') s++;
+
+ for (p=ret,i=0;s[i];i++) {
+ if (s[i] == '}') {
+ break;
+ } else if (s[i] == '\\') {
+ unsigned v;
+ if (sscanf(&s[i+1], "%02x", &v) != 1 || v > 255) {
+ return NULL;
+ }
+ *(unsigned char *)p = v;
+ p++;
+ i += 2;
+ } else {
+ *p++ = s[i];
+ }
+ }
+ *p = 0;
+
+ (*len) = (unsigned)(p - ret);
+
+ return ret;
+}
+
+/* the add*() functions deal with adding things to a struct
+ parse_string */
+
+/* allocate more space if needed */
+static int addgen_alloc(TALLOC_CTX *mem_ctx, struct parse_string *p, int n)
+{
+ if (p->length + n <= p->allocated) return 0;
+ p->allocated = p->length + n + 200;
+ p->s = talloc_realloc(mem_ctx, p->s, p->allocated);
+ if (!p->s) {
+ errno = ENOMEM;
+ return -1;
+ }
+ return 0;
+}
+
+/* add a character to the buffer */
+static int addchar(TALLOC_CTX *mem_ctx, struct parse_string *p, char c)
+{
+ if (addgen_alloc(mem_ctx, p, 2) != 0) {
+ return -1;
+ }
+ p->s[p->length++] = c;
+ p->s[p->length] = 0;
+ return 0;
+}
+
+/* add a string to the buffer */
+int addstr(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *s)
+{
+ int len = strlen(s);
+ if (addgen_alloc(mem_ctx, p, len+1) != 0) {
+ return -1;
+ }
+ memcpy(p->s + p->length, s, len+1);
+ p->length += len;
+ return 0;
+}
+
+/* add a string to the buffer with a tab prefix */
+static int addtabbed(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *s, unsigned indent)
+{
+ int len = strlen(s);
+ if (addgen_alloc(mem_ctx, p, indent+len+1) != 0) {
+ return -1;
+ }
+ while (indent--) {
+ p->s[p->length++] = '\t';
+ }
+ memcpy(p->s + p->length, s, len+1);
+ p->length += len;
+ return 0;
+}
+
+/* note! this can only be used for results up to 60 chars wide! */
+int addshort(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *fmt, ...)
+{
+ char buf[60];
+ int n;
+ va_list ap;
+ va_start(ap, fmt);
+ n = vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ if (addgen_alloc(mem_ctx, p, n + 1) != 0) {
+ return -1;
+ }
+ if (n != 0) {
+ memcpy(p->s + p->length, buf, n);
+ }
+ p->length += n;
+ p->s[p->length] = 0;
+ return 0;
+}
+
+/*
+ this is here to make it easier for people to write dump functions
+ for their own types
+ */
+int gen_addgen(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *fmt, ...)
+{
+ char *buf = NULL;
+ int n;
+ va_list ap;
+ va_start(ap, fmt);
+ n = vasprintf(&buf, fmt, ap);
+ va_end(ap);
+ if (addgen_alloc(mem_ctx, p, n + 1) != 0) {
+ if (buf) free(buf);
+ return -1;
+ }
+ if (n != 0) {
+ memcpy(p->s + p->length, buf, n);
+ }
+ p->length += n;
+ p->s[p->length] = 0;
+ if (buf) free(buf);
+ return 0;
+}
+
+/* dump a enumerated type */
+int gen_dump_enum(TALLOC_CTX *mem_ctx,
+ const struct enum_struct *einfo,
+ struct parse_string *p,
+ const char *ptr,
+ unsigned indent)
+{
+ unsigned v = *(unsigned *)ptr;
+ int i;
+ for (i=0;einfo[i].name;i++) {
+ if (v == einfo[i].value) {
+ addstr(mem_ctx, p, einfo[i].name);
+ return 0;
+ }
+ }
+ /* hmm, maybe we should just fail? */
+ return gen_dump_unsigned(mem_ctx, p, ptr, indent);
+}
+
+/* dump a single non-array element, hanlding struct and enum */
+static int gen_dump_one(TALLOC_CTX *mem_ctx,
+ struct parse_string *p,
+ const struct parse_struct *pinfo,
+ const char *ptr,
+ unsigned indent)
+{
+ if (pinfo->dump_fn == gen_dump_char && pinfo->ptr_count == 1) {
+ char *s = encode_bytes(mem_ctx, ptr, strlen(ptr));
+ if (addchar(mem_ctx, p,'{') ||
+ addstr(mem_ctx, p, s) ||
+ addstr(mem_ctx, p, "}")) {
+ return -1;
+ }
+ return 0;
+ }
+
+ return pinfo->dump_fn(mem_ctx, p, ptr, indent);
+}
+
+/* handle dumping of an array of arbitrary type */
+static int gen_dump_array(TALLOC_CTX *mem_ctx,
+ struct parse_string *p,
+ const struct parse_struct *pinfo,
+ const char *ptr,
+ int array_len,
+ int indent)
+{
+ int i, count=0;
+
+ /* special handling of fixed length strings */
+ if (array_len != 0 &&
+ pinfo->ptr_count == 0 &&
+ pinfo->dump_fn == gen_dump_char) {
+ char *s = encode_bytes(mem_ctx, ptr, array_len);
+ if (!s) return -1;
+ if (addtabbed(mem_ctx, p, pinfo->name, indent) ||
+ addstr(mem_ctx, p, " = {") ||
+ addstr(mem_ctx, p, s) ||
+ addstr(mem_ctx, p, "}\n")) {
+ return -1;
+ }
+ return 0;
+ }
+
+ for (i=0;i<array_len;i++) {
+ const char *p2 = ptr;
+ unsigned size = pinfo->size;
+
+ /* generic pointer dereference */
+ if (pinfo->ptr_count) {
+ p2 = *(const char **)ptr;
+ size = sizeof(void *);
+ }
+
+ if ((count || pinfo->ptr_count) &&
+ !(pinfo->flags & FLAG_ALWAYS) &&
+ all_zero(ptr, size)) {
+ ptr += size;
+ continue;
+ }
+ if (count == 0) {
+ if (addtabbed(mem_ctx, p, pinfo->name, indent) ||
+ addshort(mem_ctx, p, " = %u:", i)) {
+ return -1;
+ }
+ } else {
+ if (addshort(mem_ctx, p, ", %u:", i) != 0) {
+ return -1;
+ }
+ }
+ if (gen_dump_one(mem_ctx, p, pinfo, p2, indent) != 0) {
+ return -1;
+ }
+ ptr += size;
+ count++;
+ }
+ if (count) {
+ return addstr(mem_ctx, p, "\n");
+ }
+ return 0;
+}
+
+/* find a variable by name in a loaded structure and return its value
+ as an integer. Used to support dynamic arrays */
+static int find_var(const struct parse_struct *pinfo,
+ const char *data,
+ const char *var)
+{
+ int i;
+ const char *ptr;
+
+ /* this allows for constant lengths */
+ if (isdigit(*var)) {
+ return atoi(var);
+ }
+
+ for (i=0;pinfo[i].name;i++) {
+ if (strcmp(pinfo[i].name, var) == 0) break;
+ }
+ if (!pinfo[i].name) return -1;
+
+ ptr = data + pinfo[i].offset;
+
+ switch (pinfo[i].size) {
+ case sizeof(int):
+ return *(int *)ptr;
+ case sizeof(char):
+ return *(char *)ptr;
+ }
+
+ return -1;
+}
+
+
+int gen_dump_struct(TALLOC_CTX *mem_ctx,
+ const struct parse_struct *pinfo,
+ struct parse_string *p,
+ const char *ptr,
+ unsigned indent)
+{
+ char *s = gen_dump(mem_ctx, pinfo, ptr, indent+1);
+ if (!s) return -1;
+ if (addstr(mem_ctx, p, "{\n") ||
+ addstr(mem_ctx, p, s) ||
+ addtabbed(mem_ctx, p, "}", indent)) {
+ return -1;
+ }
+ return 0;
+}
+
+static int gen_dump_string(TALLOC_CTX *mem_ctx,
+ struct parse_string *p,
+ const struct parse_struct *pinfo,
+ const char *data,
+ unsigned indent)
+{
+ const char *ptr = *(char **)data;
+ char *s = encode_bytes(mem_ctx, ptr, strlen(ptr));
+ if (addtabbed(mem_ctx, p, pinfo->name, indent) ||
+ addstr(mem_ctx, p, " = ") ||
+ addchar(mem_ctx, p, '{') ||
+ addstr(mem_ctx, p, s) ||
+ addstr(mem_ctx, p, "}\n")) {
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ find the length of a nullterm array
+*/
+static int len_nullterm(const char *ptr, int size, int array_len)
+{
+ int len;
+
+ if (size == 1) {
+ len = strnlen(ptr, array_len);
+ } else {
+ for (len=0; len < array_len; len++) {
+ if (all_zero(ptr+len*size, size)) break;
+ }
+ }
+
+ if (len == 0) len = 1;
+
+ return len;
+}
+
+
+/* the generic dump routine. Scans the parse information for this structure
+ and processes it recursively */
+char *gen_dump(TALLOC_CTX *mem_ctx,
+ const struct parse_struct *pinfo,
+ const char *data,
+ unsigned indent)
+{
+ struct parse_string p;
+ int i;
+
+ p.length = 0;
+ p.allocated = 0;
+ p.s = NULL;
+
+ if (addstr(mem_ctx, &p, "") != 0) {
+ return NULL;
+ }
+
+ for (i=0;pinfo[i].name;i++) {
+ const char *ptr = data + pinfo[i].offset;
+ unsigned size = pinfo[i].size;
+
+ if (pinfo[i].ptr_count) {
+ size = sizeof(void *);
+ }
+
+ /* special handling for array types */
+ if (pinfo[i].array_len) {
+ unsigned len = pinfo[i].array_len;
+ if (pinfo[i].flags & FLAG_NULLTERM) {
+ len = len_nullterm(ptr, size, len);
+ }
+ if (gen_dump_array(mem_ctx, &p, &pinfo[i], ptr,
+ len, indent)) {
+ goto failed;
+ }
+ continue;
+ }
+
+ /* and dynamically sized arrays */
+ if (pinfo[i].dynamic_len) {
+ int len = find_var(pinfo, data, pinfo[i].dynamic_len);
+ struct parse_struct p2 = pinfo[i];
+ if (len < 0) {
+ goto failed;
+ }
+ if (len > 0) {
+ if (pinfo[i].flags & FLAG_NULLTERM) {
+ len = len_nullterm(*(char **)ptr,
+ pinfo[i].size, len);
+ }
+ p2.ptr_count--;
+ p2.dynamic_len = NULL;
+ if (gen_dump_array(mem_ctx, &p, &p2,
+ *(char **)ptr,
+ len, indent) != 0) {
+ goto failed;
+ }
+ }
+ continue;
+ }
+
+ /* don't dump zero elements */
+ if (!(pinfo[i].flags & FLAG_ALWAYS) && all_zero(ptr, size)) continue;
+
+ /* assume char* is a null terminated string */
+ if (pinfo[i].size == 1 && pinfo[i].ptr_count == 1 &&
+ pinfo[i].dump_fn == gen_dump_char) {
+ if (gen_dump_string(mem_ctx, &p, &pinfo[i], ptr, indent) != 0) {
+ goto failed;
+ }
+ continue;
+ }
+
+ /* generic pointer dereference */
+ if (pinfo[i].ptr_count) {
+ ptr = *(const char **)ptr;
+ }
+
+ if (addtabbed(mem_ctx, &p, pinfo[i].name, indent) ||
+ addstr(mem_ctx, &p, " = ") ||
+ gen_dump_one(mem_ctx, &p, &pinfo[i], ptr, indent) ||
+ addstr(mem_ctx, &p, "\n")) {
+ goto failed;
+ }
+ }
+ return p.s;
+
+failed:
+ return NULL;
+}
+
+/* search for a character in a string, skipping over sections within
+ matching braces */
+static char *match_braces(char *s, char c)
+{
+ int depth = 0;
+ while (*s) {
+ switch (*s) {
+ case '}':
+ depth--;
+ break;
+ case '{':
+ depth++;
+ break;
+ }
+ if (depth == 0 && *s == c) {
+ return s;
+ }
+ s++;
+ }
+ return s;
+}
+
+/* parse routine for enumerated types */
+int gen_parse_enum(TALLOC_CTX *mem_ctx,
+ const struct enum_struct *einfo,
+ char *ptr,
+ const char *str)
+{
+ unsigned v;
+ int i;
+
+ if (isdigit(*str)) {
+ if (sscanf(str, "%u", &v) != 1) {
+ errno = EINVAL;
+ return -1;
+ }
+ *(unsigned *)ptr = v;
+ return 0;
+ }
+
+ for (i=0;einfo[i].name;i++) {
+ if (strcmp(einfo[i].name, str) == 0) {
+ *(unsigned *)ptr = einfo[i].value;
+ return 0;
+ }
+ }
+
+ /* unknown enum value?? */
+ return -1;
+}
+
+
+/* parse all base types */
+static int gen_parse_base(TALLOC_CTX *mem_ctx,
+ const struct parse_struct *pinfo,
+ char *ptr,
+ const char *str)
+{
+ if (pinfo->parse_fn == gen_parse_char && pinfo->ptr_count==1) {
+ unsigned len;
+ char *s = decode_bytes(mem_ctx, str, &len);
+ if (!s) return -1;
+ *(char **)ptr = s;
+ return 0;
+ }
+
+ if (pinfo->ptr_count) {
+ unsigned size = pinfo->ptr_count>1?sizeof(void *):pinfo->size;
+ struct parse_struct p2 = *pinfo;
+ *(void **)ptr = talloc(mem_ctx, size);
+ if (! *(void **)ptr) {
+ return -1;
+ }
+ memset(*(void **)ptr, 0, size);
+ ptr = *(char **)ptr;
+ p2.ptr_count--;
+ return gen_parse_base(mem_ctx, &p2, ptr, str);
+ }
+
+ return pinfo->parse_fn(mem_ctx, ptr, str);
+}
+
+/* parse a generic array */
+static int gen_parse_array(TALLOC_CTX *mem_ctx,
+ const struct parse_struct *pinfo,
+ char *ptr,
+ const char *str,
+ int array_len)
+{
+ char *p, *p2;
+ unsigned size = pinfo->size;
+
+ /* special handling of fixed length strings */
+ if (array_len != 0 &&
+ pinfo->ptr_count == 0 &&
+ pinfo->dump_fn == gen_dump_char) {
+ unsigned len = 0;
+ char *s = decode_bytes(mem_ctx, str, &len);
+ if (!s || (len > array_len)) return -1;
+ memset(ptr, 0, array_len);
+ memcpy(ptr, s, len);
+ return 0;
+ }
+
+ if (pinfo->ptr_count) {
+ size = sizeof(void *);
+ }
+
+ while (*str) {
+ unsigned idx;
+ int done;
+
+ idx = atoi(str);
+ p = strchr(str,':');
+ if (!p) break;
+ p++;
+ p2 = match_braces(p, ',');
+ done = (*p2 != ',');
+ *p2 = 0;
+
+ if (*p == '{') {
+ p++;
+ p[strlen(p)-1] = 0;
+ }
+
+ if (gen_parse_base(mem_ctx, pinfo, ptr + idx*size, p) != 0) {
+ return -1;
+ }
+
+ if (done) break;
+ str = p2+1;
+ }
+
+ return 0;
+}
+
+/* parse one element, hanlding dynamic and static arrays */
+static int gen_parse_one(TALLOC_CTX *mem_ctx,
+ const struct parse_struct *pinfo,
+ const char *name,
+ char *data,
+ const char *str)
+{
+ int i;
+ for (i=0;pinfo[i].name;i++) {
+ if (strcmp(pinfo[i].name, name) == 0) {
+ break;
+ }
+ }
+ if (pinfo[i].name == NULL) {
+ return 0;
+ }
+
+ if (pinfo[i].array_len) {
+ return gen_parse_array(mem_ctx, &pinfo[i],
+ data+pinfo[i].offset,
+ str, pinfo[i].array_len);
+ }
+
+ if (pinfo[i].dynamic_len) {
+ int len = find_var(pinfo, data, pinfo[i].dynamic_len);
+ if (len < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (len > 0) {
+ struct parse_struct p2 = pinfo[i];
+ char *ptr;
+ unsigned size = pinfo[i].ptr_count>1?sizeof(void*):pinfo[i].size;
+ ptr = talloc(mem_ctx, len*size);
+ if (!ptr) {
+ errno = ENOMEM;
+ return -1;
+ }
+ memset(ptr, 0, len*size);
+ *((char **)(data + pinfo[i].offset)) = ptr;
+ p2.ptr_count--;
+ p2.dynamic_len = NULL;
+ return gen_parse_array(mem_ctx, &p2, ptr, str, len);
+ }
+ return 0;
+ }
+
+ return gen_parse_base(mem_ctx, &pinfo[i], data + pinfo[i].offset, str);
+}
+
+int gen_parse_struct(TALLOC_CTX * mem_ctx, const struct parse_struct *pinfo, char *ptr, const char *str)
+{
+ return gen_parse(mem_ctx, pinfo, ptr, str);
+}
+
+/* the main parse routine */
+int gen_parse(TALLOC_CTX *mem_ctx, const struct parse_struct *pinfo, char *data, const char *s)
+{
+ char *str, *s0;
+
+ s0 = talloc_strdup(mem_ctx, s);
+ str = s0;
+
+ while (*str) {
+ char *p;
+ char *name;
+ char *value;
+
+ /* skip leading whitespace */
+ while (isspace(*str)) str++;
+
+ p = strchr(str, '=');
+ if (!p) break;
+ value = p+1;
+ while (p > str && isspace(*(p-1))) {
+ p--;
+ }
+
+ *p = 0;
+ name = str;
+
+ while (isspace(*value)) value++;
+
+ if (*value == '{') {
+ str = match_braces(value, '}');
+ value++;
+ } else {
+ str = match_braces(value, '\n');
+ }
+
+ *str++ = 0;
+
+ if (gen_parse_one(mem_ctx, pinfo, name, data, value) != 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+
+/* for convenience supply some standard dumpers and parsers here */
+
+int gen_parse_char(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ *(unsigned char *)ptr = atoi(str);
+ return 0;
+}
+
+int gen_parse_int(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ *(int *)ptr = atoi(str);
+ return 0;
+}
+
+int gen_parse_unsigned(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ *(unsigned *)ptr = strtoul(str, NULL, 10);
+ return 0;
+}
+
+int gen_parse_time_t(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ *(time_t *)ptr = strtoul(str, NULL, 10);
+ return 0;
+}
+
+int gen_parse_double(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ *(double *)ptr = atof(str);
+ return 0;
+}
+
+int gen_parse_float(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ *(float *)ptr = atof(str);
+ return 0;
+}
+
+int gen_dump_char(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return addshort(mem_ctx, p, "%u", *(unsigned char *)(ptr));
+}
+
+int gen_dump_int(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return addshort(mem_ctx, p, "%d", *(int *)(ptr));
+}
+
+int gen_dump_unsigned(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return addshort(mem_ctx, p, "%u", *(unsigned *)(ptr));
+}
+
+int gen_dump_time_t(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return addshort(mem_ctx, p, "%u", *(time_t *)(ptr));
+}
+
+int gen_dump_double(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return addshort(mem_ctx, p, "%lg", *(double *)(ptr));
+}
+
+int gen_dump_float(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return addshort(mem_ctx, p, "%g", *(float *)(ptr));
+}
diff --git a/source/lib/genparser_samba.c b/source/lib/genparser_samba.c
new file mode 100644
index 00000000000..8f469a46d6a
--- /dev/null
+++ b/source/lib/genparser_samba.c
@@ -0,0 +1,218 @@
+/*
+ Copyright (C) Andrew Tridgell <genstruct@tridgell.net> 2002
+ Copyright (C) Simo Sorce <idra@samba.org> 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "genparser_samba.h"
+
+/* PARSE functions */
+
+int gen_parse_uint8(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ *(uint8 *)ptr = atoi(str);
+ return 0;
+}
+
+int gen_parse_uint16(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ *(uint16 *)ptr = atoi(str);
+ return 0;
+}
+
+int gen_parse_uint32(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ *(uint32 *)ptr = strtoul(str, NULL, 10);
+ return 0;
+}
+
+int gen_parse_NTTIME(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ if(sscanf(str, "%u,%u", &(((NTTIME *)(ptr))->high), &(((NTTIME *)(ptr))->low)) != 2) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+int gen_parse_DOM_SID(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ if(!string_to_sid((DOM_SID *)ptr, str)) return -1;
+ return 0;
+}
+
+int gen_parse_SEC_ACCESS(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ ((SEC_ACCESS *)ptr)->mask = strtoul(str, NULL, 10);
+ return 0;
+}
+
+int gen_parse_GUID(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ int info[UUID_FLAT_SIZE];
+ int i;
+ char *sc;
+ char *p;
+ char *m;
+
+ m = strdup(str);
+ if (!m) return -1;
+ sc = m;
+
+ memset(info, 0, sizeof(info));
+ for (i = 0; i < UUID_FLAT_SIZE; i++) {
+ p = strchr(sc, ',');
+ if (p != NULL) p = '\0';
+ info[i] = atoi(sc);
+ if (p != NULL) sc = p + 1;
+ }
+ free(m);
+
+ for (i = 0; i < UUID_FLAT_SIZE; i++) {
+ ((UUID_FLAT *)ptr)->info[i] = info[i];
+ }
+
+ return 0;
+}
+
+int gen_parse_SEC_ACE(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ return gen_parse_struct(mem_ctx, pinfo_security_ace_info, ptr, str);
+}
+
+int gen_parse_SEC_ACL(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ return gen_parse_struct(mem_ctx, pinfo_security_acl_info, ptr, str);
+}
+
+int gen_parse_SEC_DESC(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ return gen_parse_struct(mem_ctx, pinfo_security_descriptor_info, ptr, str);
+}
+
+int gen_parse_LUID_ATTR(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ return gen_parse_struct(mem_ctx, pinfo_luid_attr_info, ptr, str);
+}
+
+int gen_parse_LUID(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ if(sscanf(str, "%u,%u", &(((LUID *)(ptr))->high), &(((LUID *)(ptr))->low)) != 2) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+int gen_parse_DATA_BLOB(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ return gen_parse_struct(mem_ctx, pinfo_data_blob_info, ptr, str);
+}
+
+int gen_parse_TALLOC_CTX(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ (TALLOC_CTX *)ptr = NULL;
+ return 0;
+}
+
+/* DUMP functions */
+
+int gen_dump_uint8(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return addshort(mem_ctx, p, "%u", *(uint8 *)(ptr));
+}
+
+int gen_dump_uint16(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return addshort(mem_ctx, p, "%u", *(uint16 *)(ptr));
+}
+
+int gen_dump_uint32(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return addshort(mem_ctx, p, "%u", *(uint32 *)(ptr));
+}
+
+int gen_dump_NTTIME(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ uint32 low, high;
+
+ high = ((NTTIME *)(ptr))->high;
+ low = ((NTTIME *)(ptr))->low;
+ return addshort(mem_ctx, p, "%u,%u", high, low);
+}
+
+int gen_dump_DOM_SID(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ fstring sidstr;
+
+ sid_to_string(sidstr, (DOM_SID *)ptr);
+ return addstr(mem_ctx, p, sidstr);
+}
+
+int gen_dump_SEC_ACCESS(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return addshort(mem_ctx, p, "%u", ((SEC_ACCESS *)ptr)->mask);
+}
+
+int gen_dump_GUID(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ int i, r;
+
+ for (i = 0; i < (UUID_FLAT_SIZE - 1); i++) {
+ if (!(r = addshort(mem_ctx, p, "%d,", ((UUID_FLAT *)ptr)->info[i]))) return r;
+ }
+ return addshort(mem_ctx, p, "%d", ((UUID_FLAT *)ptr)->info[i]);
+}
+
+int gen_dump_SEC_ACE(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return gen_dump_struct(mem_ctx, pinfo_security_ace_info, p, ptr, indent);
+}
+
+int gen_dump_SEC_ACL(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return gen_dump_struct(mem_ctx, pinfo_security_acl_info, p, ptr, indent);
+}
+
+int gen_dump_SEC_DESC(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return gen_dump_struct(mem_ctx, pinfo_security_descriptor_info, p, ptr, indent);
+}
+
+int gen_dump_LUID_ATTR(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return gen_dump_struct(mem_ctx, pinfo_luid_attr_info, p, ptr, indent);
+}
+
+int gen_dump_LUID(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ uint32 low, high;
+
+ high = ((LUID *)(ptr))->high;
+ low = ((LUID *)(ptr))->low;
+ return addshort(mem_ctx, p, "%u,%u", high, low);
+}
+
+int gen_dump_DATA_BLOB(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return gen_dump_struct(mem_ctx, pinfo_data_blob_info, p, ptr, indent);
+}
+
+int gen_dump_TALLOC_CTX(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return addshort(mem_ctx, p, "TALLOC_CTX");
+}
diff --git a/source/lib/genrand.c b/source/lib/genrand.c
new file mode 100644
index 00000000000..bc9f21c6403
--- /dev/null
+++ b/source/lib/genrand.c
@@ -0,0 +1,267 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Functions to create reasonable random numbers for crypto use.
+
+ Copyright (C) Jeremy Allison 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static unsigned char hash[258];
+static uint32 counter;
+static unsigned char *reseed_data;
+static size_t reseed_data_size;
+
+/****************************************************************
+ Copy any user given reseed data.
+*****************************************************************/
+
+void set_rand_reseed_data(unsigned char *data, size_t len)
+{
+ SAFE_FREE(reseed_data);
+ reseed_data_size = 0;
+
+ reseed_data = (unsigned char *)memdup(data, len);
+ if (reseed_data)
+ reseed_data_size = len;
+}
+
+/****************************************************************
+ Setup the seed.
+*****************************************************************/
+
+static void seed_random_stream(unsigned char *seedval, size_t seedlen)
+{
+ unsigned char j = 0;
+ size_t ind;
+
+ for (ind = 0; ind < 256; ind++)
+ hash[ind] = (unsigned char)ind;
+
+ for( ind = 0; ind < 256; ind++) {
+ unsigned char tc;
+
+ j += (hash[ind] + seedval[ind%seedlen]);
+
+ tc = hash[ind];
+ hash[ind] = hash[j];
+ hash[j] = tc;
+ }
+
+ hash[256] = 0;
+ hash[257] = 0;
+}
+
+/****************************************************************
+ Get datasize bytes worth of random data.
+*****************************************************************/
+
+static void get_random_stream(unsigned char *data, size_t datasize)
+{
+ unsigned char index_i = hash[256];
+ unsigned char index_j = hash[257];
+ size_t ind;
+
+ for( ind = 0; ind < datasize; ind++) {
+ unsigned char tc;
+ unsigned char t;
+
+ index_i++;
+ index_j += hash[index_i];
+
+ tc = hash[index_i];
+ hash[index_i] = hash[index_j];
+ hash[index_j] = tc;
+
+ t = hash[index_i] + hash[index_j];
+ data[ind] = hash[t];
+ }
+
+ hash[256] = index_i;
+ hash[257] = index_j;
+}
+
+/****************************************************************
+ Get a 16 byte hash from the contents of a file.
+ Note that the hash is not initialised.
+*****************************************************************/
+
+static void do_filehash(const char *fname, unsigned char *the_hash)
+{
+ unsigned char buf[1011]; /* deliberate weird size */
+ unsigned char tmp_md4[16];
+ int fd, n;
+
+ fd = sys_open(fname,O_RDONLY,0);
+ if (fd == -1)
+ return;
+
+ while ((n = read(fd, (char *)buf, sizeof(buf))) > 0) {
+ mdfour(tmp_md4, buf, n);
+ for (n=0;n<16;n++)
+ the_hash[n] ^= tmp_md4[n];
+ }
+ close(fd);
+}
+
+/**************************************************************
+ Try and get a good random number seed. Try a number of
+ different factors. Firstly, try /dev/urandom - use if exists.
+
+ We use /dev/urandom as a read of /dev/random can block if
+ the entropy pool dries up. This leads clients to timeout
+ or be very slow on connect.
+
+ If we can't use /dev/urandom then seed the stream random generator
+ above...
+**************************************************************/
+
+static int do_reseed(BOOL use_fd, int fd)
+{
+ unsigned char seed_inbuf[40];
+ uint32 v1, v2; struct timeval tval; pid_t mypid;
+ struct passwd *pw;
+
+ if (use_fd) {
+ if (fd != -1)
+ return fd;
+
+ fd = sys_open( "/dev/urandom", O_RDONLY,0);
+ if(fd >= 0)
+ return fd;
+ }
+
+ /* Add in some secret file contents */
+
+ do_filehash("/etc/shadow", &seed_inbuf[0]);
+ do_filehash(lp_smb_passwd_file(), &seed_inbuf[16]);
+
+ /*
+ * Add in the root encrypted password.
+ * On any system where security is taken
+ * seriously this will be secret.
+ */
+
+ pw = getpwnam_alloc("root");
+ if (pw && pw->pw_passwd) {
+ size_t i;
+ unsigned char md4_tmp[16];
+ mdfour(md4_tmp, (unsigned char *)pw->pw_passwd, strlen(pw->pw_passwd));
+ for (i=0;i<16;i++)
+ seed_inbuf[8+i] ^= md4_tmp[i];
+ passwd_free(&pw);
+ }
+
+ /*
+ * Add the counter, time of day, and pid.
+ */
+
+ GetTimeOfDay(&tval);
+ mypid = sys_getpid();
+ v1 = (counter++) + mypid + tval.tv_sec;
+ v2 = (counter++) * mypid + tval.tv_usec;
+
+ SIVAL(seed_inbuf, 32, v1 ^ IVAL(seed_inbuf, 32));
+ SIVAL(seed_inbuf, 36, v2 ^ IVAL(seed_inbuf, 36));
+
+ /*
+ * Add any user-given reseed data.
+ */
+
+ if (reseed_data) {
+ size_t i;
+ for (i = 0; i < sizeof(seed_inbuf); i++)
+ seed_inbuf[i] ^= reseed_data[i % reseed_data_size];
+ }
+
+ seed_random_stream(seed_inbuf, sizeof(seed_inbuf));
+
+ return -1;
+}
+
+/*******************************************************************
+ Interface to the (hopefully) good crypto random number generator.
+********************************************************************/
+
+void generate_random_buffer( unsigned char *out, int len, BOOL do_reseed_now)
+{
+ static BOOL done_reseed = False;
+ static int urand_fd = -1;
+ unsigned char md4_buf[64];
+ unsigned char tmp_buf[16];
+ unsigned char *p;
+
+ if(!done_reseed || do_reseed_now) {
+ urand_fd = do_reseed(True, urand_fd);
+ done_reseed = True;
+ }
+
+ if (urand_fd != -1 && len > 0) {
+
+ if (read(urand_fd, out, len) == len)
+ return; /* len bytes of random data read from urandom. */
+
+ /* Read of urand error, drop back to non urand method. */
+ close(urand_fd);
+ urand_fd = -1;
+ do_reseed(False, -1);
+ done_reseed = True;
+ }
+
+ /*
+ * Generate random numbers in chunks of 64 bytes,
+ * then md4 them & copy to the output buffer.
+ * This way the raw state of the stream is never externally
+ * seen.
+ */
+
+ p = out;
+ while(len > 0) {
+ int copy_len = len > 16 ? 16 : len;
+
+ get_random_stream(md4_buf, sizeof(md4_buf));
+ mdfour(tmp_buf, md4_buf, sizeof(md4_buf));
+ memcpy(p, tmp_buf, copy_len);
+ p += copy_len;
+ len -= copy_len;
+ }
+}
+
+/*******************************************************************
+ Use the random number generator to generate a random string.
+********************************************************************/
+
+static char c_list[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,";
+
+char *generate_random_str(size_t len)
+{
+ static unsigned char retstr[256];
+ size_t i;
+
+ memset(retstr, '\0', sizeof(retstr));
+
+ if (len > sizeof(retstr)-1)
+ len = sizeof(retstr) -1;
+ generate_random_buffer( retstr, len, False);
+ for (i = 0; i < len; i++)
+ retstr[i] = c_list[ retstr[i] % (sizeof(c_list)-1) ];
+
+ retstr[i] = '\0';
+
+ return (char *)retstr;
+}
diff --git a/source/lib/getsmbpass.c b/source/lib/getsmbpass.c
new file mode 100644
index 00000000000..df5e0359aa2
--- /dev/null
+++ b/source/lib/getsmbpass.c
@@ -0,0 +1,181 @@
+/* Copyright (C) 1992-1998 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+/* Modified to use with samba by Jeremy Allison, 8th July 1995. */
+
+#include "includes.h"
+
+#ifdef REPLACE_GETPASS
+
+#ifdef SYSV_TERMIO
+
+/* SYSTEM V TERMIO HANDLING */
+
+static struct termio t;
+
+#define ECHO_IS_ON(t) ((t).c_lflag & ECHO)
+#define TURN_ECHO_OFF(t) ((t).c_lflag &= ~ECHO)
+#define TURN_ECHO_ON(t) ((t).c_lflag |= ECHO)
+
+#ifndef TCSAFLUSH
+#define TCSAFLUSH 1
+#endif
+
+#ifndef TCSANOW
+#define TCSANOW 0
+#endif
+
+static int tcgetattr(int fd, struct termio *t)
+{
+ return ioctl(fd, TCGETA, t);
+}
+
+static int tcsetattr(int fd, int flags, struct termio *t)
+{
+ if(flags & TCSAFLUSH)
+ ioctl(fd, TCFLSH, TCIOFLUSH);
+ return ioctl(fd, TCSETS, t);
+}
+
+#elif !defined(TCSAFLUSH)
+
+/* BSD TERMIO HANDLING */
+
+static struct sgttyb t;
+
+#define ECHO_IS_ON(t) ((t).sg_flags & ECHO)
+#define TURN_ECHO_OFF(t) ((t).sg_flags &= ~ECHO)
+#define TURN_ECHO_ON(t) ((t).sg_flags |= ECHO)
+
+#define TCSAFLUSH 1
+#define TCSANOW 0
+
+static int tcgetattr(int fd, struct sgttyb *t)
+{
+ return ioctl(fd, TIOCGETP, (char *)t);
+}
+
+static int tcsetattr(int fd, int flags, struct sgttyb *t)
+{
+ return ioctl(fd, TIOCSETP, (char *)t);
+}
+
+#else /* POSIX TERMIO HANDLING */
+#define ECHO_IS_ON(t) ((t).c_lflag & ECHO)
+#define TURN_ECHO_OFF(t) ((t).c_lflag &= ~ECHO)
+#define TURN_ECHO_ON(t) ((t).c_lflag |= ECHO)
+
+static struct termios t;
+#endif /* SYSV_TERMIO */
+
+static SIG_ATOMIC_T gotintr;
+static int in_fd = -1;
+
+/***************************************************************
+ Signal function to tell us were ^C'ed.
+****************************************************************/
+
+static void gotintr_sig(void)
+{
+ gotintr = 1;
+ if (in_fd != -1)
+ close(in_fd); /* Safe way to force a return. */
+ in_fd = -1;
+}
+
+char *getsmbpass(const char *prompt)
+{
+ FILE *in, *out;
+ int echo_off;
+ static char buf[256];
+ static size_t bufsize = sizeof(buf);
+ size_t nread;
+
+ /* Catch problematic signals */
+ CatchSignal(SIGINT, SIGNAL_CAST gotintr_sig);
+
+ /* Try to write to and read from the terminal if we can.
+ If we can't open the terminal, use stderr and stdin. */
+
+ in = fopen ("/dev/tty", "w+");
+ if (in == NULL) {
+ in = stdin;
+ out = stderr;
+ } else {
+ out = in;
+ }
+
+ setvbuf(in, NULL, _IONBF, 0);
+
+ /* Turn echoing off if it is on now. */
+
+ if (tcgetattr (fileno (in), &t) == 0) {
+ if (ECHO_IS_ON(t)) {
+ TURN_ECHO_OFF(t);
+ echo_off = tcsetattr (fileno (in), TCSAFLUSH, &t) == 0;
+ TURN_ECHO_ON(t);
+ } else {
+ echo_off = 0;
+ }
+ } else {
+ echo_off = 0;
+ }
+
+ /* Write the prompt. */
+ fputs(prompt, out);
+ fflush(out);
+
+ /* Read the password. */
+ buf[0] = 0;
+ if (!gotintr) {
+ in_fd = fileno(in);
+ fgets(buf, bufsize, in);
+ }
+ nread = strlen(buf);
+ if (buf[nread - 1] == '\n')
+ buf[nread - 1] = '\0';
+
+ /* Restore echoing. */
+ if (echo_off) {
+ if (gotintr && in_fd == -1)
+ in = fopen ("/dev/tty", "w+");
+ if (in != NULL)
+ tcsetattr (fileno (in), TCSANOW, &t);
+ }
+
+ fprintf(out, "\n");
+ fflush(out);
+
+ if (in != stdin) /* We opened the terminal; now close it. */
+ fclose(in);
+
+ /* Catch problematic signals */
+ CatchSignal(SIGINT, SIGNAL_CAST SIG_DFL);
+
+ if (gotintr) {
+ printf("Interupted by signal.\n");
+ fflush(stdout);
+ exit(1);
+ }
+ return buf;
+}
+
+#else
+ void getsmbpasswd_dummy(void);
+ void getsmbpasswd_dummy(void) {;}
+#endif
diff --git a/source/lib/hash.c b/source/lib/hash.c
new file mode 100644
index 00000000000..18b6534dec2
--- /dev/null
+++ b/source/lib/hash.c
@@ -0,0 +1,316 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Ying Chen 2000.
+ Copyright (C) Jeremy Allison 2000.
+ - added some defensive programming.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ * NB. We may end up replacing this functionality in a future 2.x
+ * release to reduce the number of hashing/lookup methods we support. JRA.
+ */
+
+#include "includes.h"
+
+static BOOL enlarge_hash_table(hash_table *table);
+static unsigned primes[] =
+ {17, 37, 67, 131, 257, 521, 1031, 2053, 4099, 8209, 16411};
+
+/****************************************************************************
+ * This function initializes the hash table.
+ * This hash function hashes on string keys.
+ * This number of hash buckets is always rounded up to a power of
+ * 2 first, then to a prime number that is large than the power of two.
+ * Input:
+ * table -- the hash table pointer.
+ * num_buckets -- the number of buckets to be allocated. This
+ * hash function can dynamically increase its size when the
+ * the hash table size becomes small. There is a MAX hash table
+ * size defined in hash.h.
+ * compare_func -- the function pointer to a comparison function
+ * used by the hash key comparison.
+ ****************************************************************************
+ */
+
+BOOL hash_table_init(hash_table *table, unsigned num_buckets, compare_function compare_func)
+{
+ unsigned i;
+ ubi_dlList *bucket;
+
+ table->num_elements = 0;
+ table->size = 2;
+ table->comp_func = compare_func;
+ while (table->size < num_buckets)
+ table->size <<= 1;
+ for (i = 0; i < ARRAY_SIZE(primes); i++) {
+ if (primes[i] > table->size) {
+ table->size = primes[i];
+ break;
+ }
+ }
+
+ DEBUG(5, ("Hash size = %d.\n", table->size));
+
+ if(!(table->buckets = (ubi_dlList *) malloc(sizeof(ubi_dlList) * table->size))) {
+ DEBUG(0,("hash_table_init: malloc fail !\n"));
+ return False;
+ }
+ ubi_dlInitList(&(table->lru_chain));
+ for (i=0, bucket = table->buckets; i < table->size; i++, bucket++)
+ ubi_dlInitList(bucket);
+
+ return True;
+}
+
+/*
+ **************************************************************
+ * Compute a hash value based on a string key value.
+ * Make the string key into an array of int's if possible.
+ * For the last few chars that cannot be int'ed, use char instead.
+ * The function returns the bucket index number for the hashed
+ * key.
+ * JRA. Use a djb-algorithm hash for speed.
+ **************************************************************
+ */
+
+static int string_hash(int hash_size, const char *key)
+{
+ u32 n = 0;
+ const char *p;
+ for (p = key; *p != '\0'; p++) {
+ n = ((n << 5) + n) ^ (u32)(*p);
+ }
+ return (n % hash_size);
+}
+
+/* *************************************************************************
+ * Search the hash table for the entry in the hash chain.
+ * The function returns the pointer to the
+ * element found in the chain or NULL if none is found.
+ * If the element is found, the element is also moved to
+ * the head of the LRU list.
+ *
+ * Input:
+ * table -- The hash table where the element is stored in.
+ * hash_chain -- The pointer to the bucket that stores the
+ * element to be found.
+ * key -- The hash key to be found.
+ ***************************************************************************
+ */
+
+static hash_element *hash_chain_find(hash_table *table, ubi_dlList *hash_chain, char *key)
+{
+ hash_element *hash_elem;
+ ubi_dlNodePtr lru_item;
+ unsigned int i = 0;
+
+ for (hash_elem = (hash_element *)(ubi_dlFirst(hash_chain)); i < hash_chain->count;
+ i++, hash_elem = (hash_element *)(ubi_dlNext(hash_elem))) {
+ if ((table->comp_func)(hash_elem->key, key) == 0) {
+ /* Move to the head of the lru List. */
+ lru_item = ubi_dlRemove(&(table->lru_chain), &(hash_elem->lru_link.lru_link));
+ ubi_dlAddHead(&(table->lru_chain), lru_item);
+ return(hash_elem);
+ }
+ }
+ return ((hash_element *) NULL);
+}
+
+/* ***************************************************************************
+ *
+ * Lookup a hash table for an element with key.
+ * The function returns a pointer to the hash element.
+ * If no element is found, the function returns NULL.
+ *
+ * Input:
+ * table -- The hash table to be searched on.
+ * key -- The key to be found.
+ *****************************************************************************
+ */
+
+hash_element *hash_lookup(hash_table *table, char *key)
+{
+ return (hash_chain_find(table, &table->buckets[string_hash(table->size, key)], key));
+}
+
+/* ***************************************************************
+ *
+ * This function first checks if an element with key "key"
+ * exists in the hash table. If so, the function moves the
+ * element to the front of the LRU list. Otherwise, a new
+ * hash element corresponding to "value" and "key" is allocated
+ * and inserted into the hash table. The new elements are
+ * always inserted in the LRU order to the LRU list as well.
+ *
+ * Input:
+ * table -- The hash table to be inserted in.
+ * value -- The content of the element to be inserted.
+ * key -- The key of the new element to be inserted.
+ *
+ ****************************************************************
+ */
+
+hash_element *hash_insert(hash_table *table, char *value, char *key)
+{
+ hash_element *hash_elem;
+ ubi_dlNodePtr lru_item;
+ ubi_dlList *bucket;
+ size_t string_length;
+
+ /*
+ * If the hash table size has not reached the MAX_HASH_TABLE_SIZE,
+ * the hash table may be enlarged if the current hash table is full.
+ * If the hash table size has reached the MAX_HASH_TABLE_SIZE,
+ * use LRU to remove the oldest element from the hash table.
+ */
+
+ if ((table->num_elements >= table->size) &&
+ (table->num_elements < MAX_HASH_TABLE_SIZE)) {
+ if(!enlarge_hash_table(table))
+ return (hash_element *)NULL;
+ table->num_elements += 1;
+ } else if (table->num_elements >= MAX_HASH_TABLE_SIZE) {
+ /* Do an LRU replacement. */
+ lru_item = ubi_dlLast(&(table->lru_chain));
+ hash_elem = (hash_element *)(((lru_node *)lru_item)->hash_elem);
+ bucket = hash_elem->bucket;
+ ubi_dlRemThis(&(table->lru_chain), &(hash_elem->lru_link.lru_link));
+ ubi_dlRemThis(bucket, (ubi_dlNodePtr)hash_elem);
+ SAFE_FREE(hash_elem->value);
+ SAFE_FREE(hash_elem);
+ } else {
+ table->num_elements += 1;
+ }
+
+ bucket = &table->buckets[string_hash(table->size, key)];
+
+ /* Since we only have 1-byte for the key string, we need to
+ * allocate extra space in the hash_element to store the entire key
+ * string.
+ */
+
+ string_length = strlen(key);
+ if(!(hash_elem = (hash_element *) malloc(sizeof(hash_element) + string_length))) {
+ DEBUG(0,("hash_insert: malloc fail !\n"));
+ return (hash_element *)NULL;
+ }
+
+ safe_strcpy((char *) hash_elem->key, key, string_length);
+
+ hash_elem->value = (char *)value;
+ hash_elem->bucket = bucket;
+ /* Insert in front of the lru list and the bucket list. */
+ ubi_dlAddHead(bucket, hash_elem);
+ hash_elem->lru_link.hash_elem = hash_elem;
+ ubi_dlAddHead(&(table->lru_chain), &(hash_elem->lru_link.lru_link));
+
+ return(hash_elem);
+}
+
+/* **************************************************************************
+ *
+ * Remove a hash element from the hash table. The hash element is
+ * removed from both the LRU list and the hash bucket chain.
+ *
+ * Input:
+ * table -- the hash table to be manipulated on.
+ * hash_elem -- the element to be removed.
+ **************************************************************************
+ */
+
+void hash_remove(hash_table *table, hash_element *hash_elem)
+{
+ if (hash_elem) {
+ ubi_dlRemove(&(table->lru_chain), &(hash_elem->lru_link.lru_link));
+ ubi_dlRemove(hash_elem->bucket, (ubi_dlNodePtr) hash_elem);
+ SAFE_FREE(hash_elem->value);
+ SAFE_FREE(hash_elem);
+ table->num_elements--;
+ }
+}
+
+/* ******************************************************************
+ * Increase the hash table size if it is too small.
+ * The hash table size is increased by the HASH_TABLE_INCREMENT
+ * ratio.
+ * Input:
+ * table -- the hash table to be enlarged.
+ ******************************************************************
+ */
+
+static BOOL enlarge_hash_table(hash_table *table)
+{
+ hash_element *hash_elem;
+ int size, hash_value;
+ ubi_dlList *buckets;
+ ubi_dlList *old_bucket;
+ ubi_dlList *bucket;
+ ubi_dlList lru_chain;
+
+ buckets = table->buckets;
+ lru_chain = table->lru_chain;
+ size = table->size;
+
+ /* Reinitialize the hash table. */
+ if(!hash_table_init(table, table->size * HASH_TABLE_INCREMENT, table->comp_func))
+ return False;
+
+ for (old_bucket = buckets; size > 0; size--, old_bucket++) {
+ while (old_bucket->count != 0) {
+ hash_elem = (hash_element *) ubi_dlRemHead(old_bucket);
+ ubi_dlRemove(&lru_chain, &(hash_elem->lru_link.lru_link));
+ hash_value = string_hash(table->size, (char *) hash_elem->key);
+ bucket = &(table->buckets[hash_value]);
+ ubi_dlAddHead(bucket, hash_elem);
+ ubi_dlAddHead(&(table->lru_chain), &(hash_elem->lru_link.lru_link));
+ hash_elem->bucket = bucket;
+ hash_elem->lru_link.hash_elem = hash_elem;
+ table->num_elements++;
+ }
+ }
+ SAFE_FREE(buckets);
+
+ return True;
+}
+
+/* **********************************************************************
+ *
+ * Remove everything from a hash table and free up the memory it
+ * occupies.
+ * Input:
+ * table -- the hash table to be cleared.
+ *
+ *************************************************************************
+ */
+
+void hash_clear(hash_table *table)
+{
+ unsigned int i;
+ ubi_dlList *bucket = table->buckets;
+ hash_element *hash_elem;
+ for (i = 0; i < table->size; bucket++, i++) {
+ while (bucket->count != 0) {
+ hash_elem = (hash_element *) ubi_dlRemHead(bucket);
+ SAFE_FREE(hash_elem->value);
+ SAFE_FREE(hash_elem);
+ }
+ }
+ table->size = 0;
+ SAFE_FREE(table->buckets);
+ table->buckets = NULL;
+}
diff --git a/source/lib/hmacmd5.c b/source/lib/hmacmd5.c
new file mode 100644
index 00000000000..f436fd30c0e
--- /dev/null
+++ b/source/lib/hmacmd5.c
@@ -0,0 +1,134 @@
+/*
+ Unix SMB/CIFS implementation.
+ HMAC MD5 code for use in NTLMv2
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Andrew Tridgell 1992-2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* taken direct from rfc2104 implementation and modified for suitable use
+ * for ntlmv2.
+ */
+
+#include "includes.h"
+
+/***********************************************************************
+ the rfc 2104 version of hmac_md5 initialisation.
+***********************************************************************/
+void hmac_md5_init_rfc2104(uchar* key, int key_len, HMACMD5Context *ctx)
+{
+ int i;
+
+ /* if key is longer than 64 bytes reset it to key=MD5(key) */
+ if (key_len > 64)
+ {
+ uchar tk[16];
+ struct MD5Context tctx;
+
+ MD5Init(&tctx);
+ MD5Update(&tctx, key, key_len);
+ MD5Final(tk, &tctx);
+
+ key = tk;
+ key_len = 16;
+ }
+
+ /* start out by storing key in pads */
+ ZERO_STRUCT(ctx->k_ipad);
+ ZERO_STRUCT(ctx->k_opad);
+ memcpy( ctx->k_ipad, key, key_len);
+ memcpy( ctx->k_opad, key, key_len);
+
+ /* XOR key with ipad and opad values */
+ for (i=0; i<64; i++)
+ {
+ ctx->k_ipad[i] ^= 0x36;
+ ctx->k_opad[i] ^= 0x5c;
+ }
+
+ MD5Init(&ctx->ctx);
+ MD5Update(&ctx->ctx, ctx->k_ipad, 64);
+}
+
+/***********************************************************************
+ the microsoft version of hmac_md5 initialisation.
+***********************************************************************/
+void hmac_md5_init_limK_to_64(const uchar* key, int key_len,
+ HMACMD5Context *ctx)
+{
+ int i;
+
+ /* if key is longer than 64 bytes truncate it */
+ if (key_len > 64)
+ {
+ key_len = 64;
+ }
+
+ /* start out by storing key in pads */
+ ZERO_STRUCT(ctx->k_ipad);
+ ZERO_STRUCT(ctx->k_opad);
+ memcpy( ctx->k_ipad, key, key_len);
+ memcpy( ctx->k_opad, key, key_len);
+
+ /* XOR key with ipad and opad values */
+ for (i=0; i<64; i++) {
+ ctx->k_ipad[i] ^= 0x36;
+ ctx->k_opad[i] ^= 0x5c;
+ }
+
+ MD5Init(&ctx->ctx);
+ MD5Update(&ctx->ctx, ctx->k_ipad, 64);
+}
+
+/***********************************************************************
+ update hmac_md5 "inner" buffer
+***********************************************************************/
+void hmac_md5_update(const uchar* text, int text_len, HMACMD5Context *ctx)
+{
+ MD5Update(&ctx->ctx, text, text_len); /* then text of datagram */
+}
+
+/***********************************************************************
+ finish off hmac_md5 "inner" buffer and generate outer one.
+***********************************************************************/
+void hmac_md5_final(uchar *digest, HMACMD5Context *ctx)
+
+{
+ struct MD5Context ctx_o;
+
+ MD5Final(digest, &ctx->ctx);
+
+ MD5Init(&ctx_o);
+ MD5Update(&ctx_o, ctx->k_opad, 64);
+ MD5Update(&ctx_o, digest, 16);
+ MD5Final(digest, &ctx_o);
+}
+
+/***********************************************************
+ single function to calculate an HMAC MD5 digest from data.
+ use the microsoft hmacmd5 init method because the key is 16 bytes.
+************************************************************/
+void hmac_md5( uchar key[16], uchar* data, int data_len, uchar* digest)
+{
+ HMACMD5Context ctx;
+ hmac_md5_init_limK_to_64(key, 16, &ctx);
+ if (data_len != 0)
+ {
+ hmac_md5_update(data, data_len, &ctx);
+ }
+ hmac_md5_final(digest, &ctx);
+}
+
diff --git a/source/lib/iconv.c b/source/lib/iconv.c
new file mode 100644
index 00000000000..7df73192f24
--- /dev/null
+++ b/source/lib/iconv.c
@@ -0,0 +1,591 @@
+/*
+ Unix SMB/CIFS implementation.
+ minimal iconv implementation
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Jelmer Vernooij 2002,2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*
+ * We have to use strcasecmp here as the character conversions
+ * haven't been initialised yet. JRA.
+ */
+
+#undef strcasecmp
+
+/**
+ * @file
+ *
+ * @brief Samba wrapper/stub for iconv character set conversion.
+ *
+ * iconv is the XPG2 interface for converting between character
+ * encodings. This file provides a Samba wrapper around it, and also
+ * a simple reimplementation that is used if the system does not
+ * implement iconv.
+ *
+ * Samba only works with encodings that are supersets of ASCII: ascii
+ * characters like whitespace can be tested for directly, multibyte
+ * sequences start with a byte with the high bit set, and strings are
+ * terminated by a nul byte.
+ *
+ * Note that the only function provided by iconv is conversion between
+ * characters. It doesn't directly support operations like
+ * uppercasing or comparison. We have to convert to UCS-2 and compare
+ * there.
+ *
+ * @sa Samba Developers Guide
+ **/
+
+static size_t ascii_pull(void *,char **, size_t *, char **, size_t *);
+static size_t ascii_push(void *,char **, size_t *, char **, size_t *);
+static size_t latin1_push(void *,char **, size_t *, char **, size_t *);
+static size_t utf8_pull(void *,char **, size_t *, char **, size_t *);
+static size_t utf8_push(void *,char **, size_t *, char **, size_t *);
+static size_t ucs2hex_pull(void *,char **, size_t *, char **, size_t *);
+static size_t ucs2hex_push(void *,char **, size_t *, char **, size_t *);
+static size_t iconv_copy(void *,char **, size_t *, char **, size_t *);
+
+static struct charset_functions builtin_functions[] = {
+ {"UCS-2LE", iconv_copy, iconv_copy},
+ {"UTF8", utf8_pull, utf8_push},
+ {"ASCII", ascii_pull, ascii_push},
+ {"646", ascii_pull, ascii_push},
+ {"ISO-8859-1", ascii_pull, latin1_push},
+ {"UCS2-HEX", ucs2hex_pull, ucs2hex_push},
+ {NULL, NULL, NULL}
+};
+
+static struct charset_functions *charsets = NULL;
+
+static struct charset_functions *find_charset_functions(const char *name)
+{
+ struct charset_functions *c = charsets;
+
+ while(c) {
+ if (strcasecmp(name, c->name) == 0) {
+ return c;
+ }
+ c = c->next;
+ }
+
+ return NULL;
+}
+
+NTSTATUS smb_register_charset(struct charset_functions *funcs)
+{
+ if (!funcs) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(5, ("Attempting to register new charset %s\n", funcs->name));
+ /* Check whether we already have this charset... */
+ if (find_charset_functions(funcs->name)) {
+ DEBUG(0, ("Duplicate charset %s, not registering\n", funcs->name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ funcs->next = funcs->prev = NULL;
+ DEBUG(5, ("Registered charset %s\n", funcs->name));
+ DLIST_ADD(charsets, funcs);
+ return NT_STATUS_OK;
+}
+
+static void lazy_initialize_iconv(void)
+{
+ static BOOL initialized;
+ int i;
+
+ if (!initialized) {
+ initialized = True;
+ for(i = 0; builtin_functions[i].name; i++)
+ smb_register_charset(&builtin_functions[i]);
+ static_init_charset;
+ }
+}
+
+/* if there was an error then reset the internal state,
+ this ensures that we don't have a shift state remaining for
+ character sets like SJIS */
+static size_t sys_iconv(void *cd,
+ char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+#ifdef HAVE_NATIVE_ICONV
+ size_t ret = iconv((iconv_t)cd,
+ inbuf, inbytesleft,
+ outbuf, outbytesleft);
+ if (ret == (size_t)-1) iconv(cd, NULL, NULL, NULL, NULL);
+ return ret;
+#else
+ errno = EINVAL;
+ return -1;
+#endif
+}
+
+/**
+ * This is a simple portable iconv() implementaion.
+ *
+ * It only knows about a very small number of character sets - just
+ * enough that Samba works on systems that don't have iconv.
+ **/
+size_t smb_iconv(smb_iconv_t cd,
+ char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+ char cvtbuf[2048];
+ char *bufp = cvtbuf;
+ size_t bufsize;
+
+ /* in many cases we can go direct */
+ if (cd->direct) {
+ return cd->direct(cd->cd_direct,
+ (char **)inbuf, inbytesleft, outbuf, outbytesleft);
+ }
+
+
+ /* otherwise we have to do it chunks at a time */
+ while (*inbytesleft > 0) {
+ bufp = cvtbuf;
+ bufsize = sizeof(cvtbuf);
+
+ if (cd->pull(cd->cd_pull,
+ (char **)inbuf, inbytesleft, &bufp, &bufsize) == -1
+ && errno != E2BIG) return -1;
+
+ bufp = cvtbuf;
+ bufsize = sizeof(cvtbuf) - bufsize;
+
+ if (cd->push(cd->cd_push,
+ &bufp, &bufsize,
+ outbuf, outbytesleft) == -1) return -1;
+ }
+
+ return 0;
+}
+
+/*
+ simple iconv_open() wrapper
+ */
+smb_iconv_t smb_iconv_open(const char *tocode, const char *fromcode)
+{
+ smb_iconv_t ret;
+ struct charset_functions *from, *to;
+
+ lazy_initialize_iconv();
+ from = charsets;
+ to = charsets;
+
+ ret = (smb_iconv_t)malloc(sizeof(*ret));
+ if (!ret) {
+ errno = ENOMEM;
+ return (smb_iconv_t)-1;
+ }
+ memset(ret, 0, sizeof(*ret));
+
+ ret->from_name = strdup(fromcode);
+ ret->to_name = strdup(tocode);
+
+ /* check for the simplest null conversion */
+ if (strcasecmp(fromcode, tocode) == 0) {
+ ret->direct = iconv_copy;
+ return ret;
+ }
+
+ /* check if we have a builtin function for this conversion */
+ from = find_charset_functions(fromcode);
+ if(from)ret->pull = from->pull;
+
+ to = find_charset_functions(tocode);
+ if(to)ret->push = to->push;
+
+ /* check if we can use iconv for this conversion */
+#ifdef HAVE_NATIVE_ICONV
+ if (!ret->pull) {
+ ret->cd_pull = iconv_open("UCS-2LE", fromcode);
+ if (ret->cd_pull != (iconv_t)-1)
+ ret->pull = sys_iconv;
+ }
+
+ if (!ret->push) {
+ ret->cd_push = iconv_open(tocode, "UCS-2LE");
+ if (ret->cd_push != (iconv_t)-1)
+ ret->push = sys_iconv;
+ }
+#endif
+
+ /* check if there is a module available that can do this conversion */
+ if (!ret->pull && NT_STATUS_IS_OK(smb_probe_module("charset", fromcode))) {
+ if(!(from = find_charset_functions(fromcode)))
+ DEBUG(0, ("Module %s doesn't provide charset %s!\n", fromcode, fromcode));
+ else
+ ret->pull = from->pull;
+ }
+
+ if (!ret->push && NT_STATUS_IS_OK(smb_probe_module("charset", tocode))) {
+ if(!(to = find_charset_functions(tocode)))
+ DEBUG(0, ("Module %s doesn't provide charset %s!\n", tocode, tocode));
+ else
+ ret->push = to->push;
+ }
+
+ if (!ret->push || !ret->pull) {
+ SAFE_FREE(ret->from_name);
+ SAFE_FREE(ret->to_name);
+ SAFE_FREE(ret);
+ errno = EINVAL;
+ return (smb_iconv_t)-1;
+ }
+
+ /* check for conversion to/from ucs2 */
+ if (strcasecmp(fromcode, "UCS-2LE") == 0 && to) {
+ ret->direct = to->push;
+ ret->push = ret->pull = NULL;
+ return ret;
+ }
+
+ if (strcasecmp(tocode, "UCS-2LE") == 0 && from) {
+ ret->direct = from->pull;
+ ret->push = ret->pull = NULL;
+ return ret;
+ }
+
+ /* Check if we can do the conversion direct */
+#ifdef HAVE_NATIVE_ICONV
+ if (strcasecmp(fromcode, "UCS-2LE") == 0) {
+ ret->direct = sys_iconv;
+ ret->cd_direct = ret->cd_push;
+ ret->cd_push = NULL;
+ return ret;
+ }
+ if (strcasecmp(tocode, "UCS-2LE") == 0) {
+ ret->direct = sys_iconv;
+ ret->cd_direct = ret->cd_pull;
+ ret->cd_pull = NULL;
+ return ret;
+ }
+#endif
+
+ return ret;
+}
+
+/*
+ simple iconv_close() wrapper
+*/
+int smb_iconv_close (smb_iconv_t cd)
+{
+#ifdef HAVE_NATIVE_ICONV
+ if (cd->cd_direct) iconv_close((iconv_t)cd->cd_direct);
+ if (cd->cd_pull) iconv_close((iconv_t)cd->cd_pull);
+ if (cd->cd_push) iconv_close((iconv_t)cd->cd_push);
+#endif
+
+ SAFE_FREE(cd->from_name);
+ SAFE_FREE(cd->to_name);
+
+ memset(cd, 0, sizeof(*cd));
+ SAFE_FREE(cd);
+ return 0;
+}
+
+
+/**********************************************************************
+ the following functions implement the builtin character sets in Samba
+ and also the "test" character sets that are designed to test
+ multi-byte character set support for english users
+***********************************************************************/
+
+static size_t ascii_pull(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+ while (*inbytesleft >= 1 && *outbytesleft >= 2) {
+ (*outbuf)[0] = (*inbuf)[0];
+ (*outbuf)[1] = 0;
+ (*inbytesleft) -= 1;
+ (*outbytesleft) -= 2;
+ (*inbuf) += 1;
+ (*outbuf) += 2;
+ }
+
+ if (*inbytesleft > 0) {
+ errno = E2BIG;
+ return -1;
+ }
+
+ return 0;
+}
+
+static size_t ascii_push(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+ int ir_count=0;
+
+ while (*inbytesleft >= 2 && *outbytesleft >= 1) {
+ (*outbuf)[0] = (*inbuf)[0] & 0x7F;
+ if ((*inbuf)[1]) ir_count++;
+ (*inbytesleft) -= 2;
+ (*outbytesleft) -= 1;
+ (*inbuf) += 2;
+ (*outbuf) += 1;
+ }
+
+ if (*inbytesleft == 1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (*inbytesleft > 1) {
+ errno = E2BIG;
+ return -1;
+ }
+
+ return ir_count;
+}
+
+static size_t latin1_push(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+ int ir_count=0;
+
+ while (*inbytesleft >= 2 && *outbytesleft >= 1) {
+ (*outbuf)[0] = (*inbuf)[0];
+ if ((*inbuf)[1]) ir_count++;
+ (*inbytesleft) -= 2;
+ (*outbytesleft) -= 1;
+ (*inbuf) += 2;
+ (*outbuf) += 1;
+ }
+
+ if (*inbytesleft == 1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (*inbytesleft > 1) {
+ errno = E2BIG;
+ return -1;
+ }
+
+ return ir_count;
+}
+
+static size_t ucs2hex_pull(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+ while (*inbytesleft >= 1 && *outbytesleft >= 2) {
+ unsigned v;
+
+ if ((*inbuf)[0] != '@') {
+ /* seven bit ascii case */
+ (*outbuf)[0] = (*inbuf)[0];
+ (*outbuf)[1] = 0;
+ (*inbytesleft) -= 1;
+ (*outbytesleft) -= 2;
+ (*inbuf) += 1;
+ (*outbuf) += 2;
+ continue;
+ }
+ /* it's a hex character */
+ if (*inbytesleft < 5) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (sscanf(&(*inbuf)[1], "%04x", &v) != 1) {
+ errno = EILSEQ;
+ return -1;
+ }
+
+ (*outbuf)[0] = v&0xff;
+ (*outbuf)[1] = v>>8;
+ (*inbytesleft) -= 5;
+ (*outbytesleft) -= 2;
+ (*inbuf) += 5;
+ (*outbuf) += 2;
+ }
+
+ if (*inbytesleft > 0) {
+ errno = E2BIG;
+ return -1;
+ }
+
+ return 0;
+}
+
+static size_t ucs2hex_push(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+ while (*inbytesleft >= 2 && *outbytesleft >= 1) {
+ char buf[6];
+
+ if ((*inbuf)[1] == 0 &&
+ ((*inbuf)[0] & 0x80) == 0 &&
+ (*inbuf)[0] != '@') {
+ (*outbuf)[0] = (*inbuf)[0];
+ (*inbytesleft) -= 2;
+ (*outbytesleft) -= 1;
+ (*inbuf) += 2;
+ (*outbuf) += 1;
+ continue;
+ }
+ if (*outbytesleft < 5) {
+ errno = E2BIG;
+ return -1;
+ }
+ snprintf(buf, 6, "@%04x", SVAL(*inbuf, 0));
+ memcpy(*outbuf, buf, 5);
+ (*inbytesleft) -= 2;
+ (*outbytesleft) -= 5;
+ (*inbuf) += 2;
+ (*outbuf) += 5;
+ }
+
+ if (*inbytesleft == 1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (*inbytesleft > 1) {
+ errno = E2BIG;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static size_t iconv_copy(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+ int n;
+
+ n = MIN(*inbytesleft, *outbytesleft);
+
+ memmove(*outbuf, *inbuf, n);
+
+ (*inbytesleft) -= n;
+ (*outbytesleft) -= n;
+ (*inbuf) += n;
+ (*outbuf) += n;
+
+ if (*inbytesleft > 0) {
+ errno = E2BIG;
+ return -1;
+ }
+
+ return 0;
+}
+
+static size_t utf8_pull(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+ while (*inbytesleft >= 1 && *outbytesleft >= 2) {
+ unsigned char *c = (unsigned char *)*inbuf;
+ unsigned char *uc = (unsigned char *)*outbuf;
+ int len = 1;
+
+ if ((c[0] & 0x80) == 0) {
+ uc[0] = c[0];
+ uc[1] = 0;
+ } else if ((c[0] & 0xf0) == 0xe0) {
+ if (*inbytesleft < 3) {
+ DEBUG(0,("short utf8 char\n"));
+ goto badseq;
+ }
+ uc[1] = ((c[0]&0xF)<<4) | ((c[1]>>2)&0xF);
+ uc[0] = (c[1]<<6) | (c[2]&0x3f);
+ len = 3;
+ } else if ((c[0] & 0xe0) == 0xc0) {
+ if (*inbytesleft < 2) {
+ DEBUG(0,("short utf8 char\n"));
+ goto badseq;
+ }
+ uc[1] = (c[0]>>2) & 0x7;
+ uc[0] = (c[0]<<6) | (c[1]&0x3f);
+ len = 2;
+ }
+
+ (*inbuf) += len;
+ (*inbytesleft) -= len;
+ (*outbytesleft) -= 2;
+ (*outbuf) += 2;
+ }
+
+ if (*inbytesleft > 0) {
+ errno = E2BIG;
+ return -1;
+ }
+
+ return 0;
+
+badseq:
+ errno = EINVAL;
+ return -1;
+}
+
+static size_t utf8_push(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+ while (*inbytesleft >= 2 && *outbytesleft >= 1) {
+ unsigned char *c = (unsigned char *)*outbuf;
+ unsigned char *uc = (unsigned char *)*inbuf;
+ int len=1;
+
+ if (uc[1] & 0xf8) {
+ if (*outbytesleft < 3) {
+ DEBUG(0,("short utf8 write\n"));
+ goto toobig;
+ }
+ c[0] = 0xe0 | (uc[1]>>4);
+ c[1] = 0x80 | ((uc[1]&0xF)<<2) | (uc[0]>>6);
+ c[2] = 0x80 | (uc[0]&0x3f);
+ len = 3;
+ } else if (uc[1] | (uc[0] & 0x80)) {
+ if (*outbytesleft < 2) {
+ DEBUG(0,("short utf8 write\n"));
+ goto toobig;
+ }
+ c[0] = 0xc0 | (uc[1]<<2) | (uc[0]>>6);
+ c[1] = 0x80 | (uc[0]&0x3f);
+ len = 2;
+ } else {
+ c[0] = uc[0];
+ }
+
+
+ (*inbytesleft) -= 2;
+ (*outbytesleft) -= len;
+ (*inbuf) += 2;
+ (*outbuf) += len;
+ }
+
+ if (*inbytesleft == 1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (*inbytesleft > 1) {
+ errno = E2BIG;
+ return -1;
+ }
+
+ return 0;
+
+toobig:
+ errno = E2BIG;
+ return -1;
+}
diff --git a/source/lib/interface.c b/source/lib/interface.c
new file mode 100644
index 00000000000..4d8010e31bc
--- /dev/null
+++ b/source/lib/interface.c
@@ -0,0 +1,338 @@
+/*
+ Unix SMB/CIFS implementation.
+ multiple interface handling
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static struct iface_struct *probed_ifaces;
+static int total_probed;
+
+struct in_addr allones_ip;
+struct in_addr loopback_ip;
+
+static struct interface *local_interfaces;
+
+#define ALLONES ((uint32)0xFFFFFFFF)
+#define MKBCADDR(_IP, _NM) ((_IP & _NM) | (_NM ^ ALLONES))
+#define MKNETADDR(_IP, _NM) (_IP & _NM)
+
+/****************************************************************************
+Try and find an interface that matches an ip. If we cannot, return NULL
+ **************************************************************************/
+static struct interface *iface_find(struct in_addr ip, BOOL CheckMask)
+{
+ struct interface *i;
+ if (is_zero_ip(ip)) return local_interfaces;
+
+ for (i=local_interfaces;i;i=i->next)
+ if (CheckMask) {
+ if (same_net(i->ip,ip,i->nmask)) return i;
+ } else if ((i->ip).s_addr == ip.s_addr) return i;
+
+ return NULL;
+}
+
+
+/****************************************************************************
+add an interface to the linked list of interfaces
+****************************************************************************/
+static void add_interface(struct in_addr ip, struct in_addr nmask)
+{
+ struct interface *iface;
+ if (iface_find(ip, False)) {
+ DEBUG(3,("not adding duplicate interface %s\n",inet_ntoa(ip)));
+ return;
+ }
+
+ if (ip_equal(nmask, allones_ip)) {
+ DEBUG(3,("not adding non-broadcast interface %s\n",inet_ntoa(ip)));
+ return;
+ }
+
+ iface = (struct interface *)malloc(sizeof(*iface));
+ if (!iface) return;
+
+ ZERO_STRUCTPN(iface);
+
+ iface->ip = ip;
+ iface->nmask = nmask;
+ iface->bcast.s_addr = MKBCADDR(iface->ip.s_addr, iface->nmask.s_addr);
+
+ DLIST_ADD(local_interfaces, iface);
+
+ DEBUG(2,("added interface ip=%s ",inet_ntoa(iface->ip)));
+ DEBUG(2,("bcast=%s ",inet_ntoa(iface->bcast)));
+ DEBUG(2,("nmask=%s\n",inet_ntoa(iface->nmask)));
+}
+
+
+
+/****************************************************************************
+interpret a single element from a interfaces= config line
+
+This handles the following different forms:
+
+1) wildcard interface name
+2) DNS name
+3) IP/masklen
+4) ip/mask
+5) bcast/mask
+****************************************************************************/
+static void interpret_interface(const char *token)
+{
+ struct in_addr ip, nmask;
+ char *p;
+ int i, added=0;
+
+ zero_ip(&ip);
+ zero_ip(&nmask);
+
+ /* first check if it is an interface name */
+ for (i=0;i<total_probed;i++) {
+ if (gen_fnmatch(token, probed_ifaces[i].name) == 0) {
+ add_interface(probed_ifaces[i].ip,
+ probed_ifaces[i].netmask);
+ added = 1;
+ }
+ }
+ if (added) return;
+
+ /* maybe it is a DNS name */
+ p = strchr_m(token,'/');
+ if (!p) {
+ ip = *interpret_addr2(token);
+ for (i=0;i<total_probed;i++) {
+ if (ip.s_addr == probed_ifaces[i].ip.s_addr &&
+ !ip_equal(allones_ip, probed_ifaces[i].netmask)) {
+ add_interface(probed_ifaces[i].ip,
+ probed_ifaces[i].netmask);
+ return;
+ }
+ }
+ DEBUG(2,("can't determine netmask for %s\n", token));
+ return;
+ }
+
+ /* parse it into an IP address/netmasklength pair */
+ *p++ = 0;
+
+ ip = *interpret_addr2(token);
+
+ if (strlen(p) > 2) {
+ nmask = *interpret_addr2(p);
+ } else {
+ nmask.s_addr = htonl(((ALLONES >> atoi(p)) ^ ALLONES));
+ }
+
+ /* maybe the first component was a broadcast address */
+ if (ip.s_addr == MKBCADDR(ip.s_addr, nmask.s_addr) ||
+ ip.s_addr == MKNETADDR(ip.s_addr, nmask.s_addr)) {
+ for (i=0;i<total_probed;i++) {
+ if (same_net(ip, probed_ifaces[i].ip, nmask)) {
+ add_interface(probed_ifaces[i].ip, nmask);
+ return;
+ }
+ }
+ DEBUG(2,("Can't determine ip for broadcast address %s\n", token));
+ return;
+ }
+
+ add_interface(ip, nmask);
+}
+
+
+/****************************************************************************
+load the list of network interfaces
+****************************************************************************/
+void load_interfaces(void)
+{
+ const char **ptr;
+ int i;
+ struct iface_struct ifaces[MAX_INTERFACES];
+
+ ptr = lp_interfaces();
+
+ allones_ip = *interpret_addr2("255.255.255.255");
+ loopback_ip = *interpret_addr2("127.0.0.1");
+
+ SAFE_FREE(probed_ifaces);
+
+ /* dump the current interfaces if any */
+ while (local_interfaces) {
+ struct interface *iface = local_interfaces;
+ DLIST_REMOVE(local_interfaces, local_interfaces);
+ ZERO_STRUCTPN(iface);
+ SAFE_FREE(iface);
+ }
+
+ /* probe the kernel for interfaces */
+ total_probed = get_interfaces(ifaces, MAX_INTERFACES);
+
+ if (total_probed > 0) {
+ probed_ifaces = memdup(ifaces, sizeof(ifaces[0])*total_probed);
+ }
+
+ /* if we don't have a interfaces line then use all broadcast capable
+ interfaces except loopback */
+ if (!ptr || !*ptr || !**ptr) {
+ if (total_probed <= 0) {
+ DEBUG(0,("ERROR: Could not determine network interfaces, you must use a interfaces config line\n"));
+ exit(1);
+ }
+ for (i=0;i<total_probed;i++) {
+ if (probed_ifaces[i].netmask.s_addr != allones_ip.s_addr &&
+ probed_ifaces[i].ip.s_addr != loopback_ip.s_addr) {
+ add_interface(probed_ifaces[i].ip,
+ probed_ifaces[i].netmask);
+ }
+ }
+ return;
+ }
+
+ if (ptr) {
+ while (*ptr) {
+ interpret_interface(*ptr);
+ ptr++;
+ }
+ }
+
+ if (!local_interfaces) {
+ DEBUG(0,("WARNING: no network interfaces found\n"));
+ }
+}
+
+
+/****************************************************************************
+return True if the list of probed interfaces has changed
+****************************************************************************/
+BOOL interfaces_changed(void)
+{
+ int n;
+ struct iface_struct ifaces[MAX_INTERFACES];
+
+ n = get_interfaces(ifaces, MAX_INTERFACES);
+
+ if ((n > 0 )&& (n != total_probed ||
+ memcmp(ifaces, probed_ifaces, sizeof(ifaces[0])*n))) {
+ return True;
+ }
+
+ return False;
+}
+
+
+/****************************************************************************
+ check if an IP is one of mine
+ **************************************************************************/
+BOOL ismyip(struct in_addr ip)
+{
+ struct interface *i;
+ for (i=local_interfaces;i;i=i->next)
+ if (ip_equal(i->ip,ip)) return True;
+ return False;
+}
+
+/****************************************************************************
+ check if a packet is from a local (known) net
+ **************************************************************************/
+BOOL is_local_net(struct in_addr from)
+{
+ struct interface *i;
+ for (i=local_interfaces;i;i=i->next) {
+ if((from.s_addr & i->nmask.s_addr) ==
+ (i->ip.s_addr & i->nmask.s_addr))
+ return True;
+ }
+ return False;
+}
+
+/****************************************************************************
+ how many interfaces do we have
+ **************************************************************************/
+int iface_count(void)
+{
+ int ret = 0;
+ struct interface *i;
+
+ for (i=local_interfaces;i;i=i->next)
+ ret++;
+ return ret;
+}
+
+/****************************************************************************
+ return the Nth interface
+ **************************************************************************/
+struct interface *get_interface(int n)
+{
+ struct interface *i;
+
+ for (i=local_interfaces;i && n;i=i->next)
+ n--;
+
+ if (i) return i;
+ return NULL;
+}
+
+/****************************************************************************
+ return IP of the Nth interface
+ **************************************************************************/
+struct in_addr *iface_n_ip(int n)
+{
+ struct interface *i;
+
+ for (i=local_interfaces;i && n;i=i->next)
+ n--;
+
+ if (i) return &i->ip;
+ return NULL;
+}
+
+/****************************************************************************
+ return bcast of the Nth interface
+ **************************************************************************/
+struct in_addr *iface_n_bcast(int n)
+{
+ struct interface *i;
+
+ for (i=local_interfaces;i && n;i=i->next)
+ n--;
+
+ if (i) return &i->bcast;
+ return NULL;
+}
+
+
+/* these 3 functions return the ip/bcast/nmask for the interface
+ most appropriate for the given ip address. If they can't find
+ an appropriate interface they return the requested field of the
+ first known interface. */
+
+struct in_addr *iface_ip(struct in_addr ip)
+{
+ struct interface *i = iface_find(ip, True);
+ return(i ? &i->ip : &local_interfaces->ip);
+}
+
+/*
+ return True if a IP is directly reachable on one of our interfaces
+*/
+BOOL iface_local(struct in_addr ip)
+{
+ return iface_find(ip, True) ? True : False;
+}
diff --git a/source/lib/interfaces.c b/source/lib/interfaces.c
new file mode 100644
index 00000000000..96f4b4cd94f
--- /dev/null
+++ b/source/lib/interfaces.c
@@ -0,0 +1,407 @@
+/*
+ Unix SMB/CIFS implementation.
+ return a list of network interfaces
+ Copyright (C) Andrew Tridgell 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+/* working out the interfaces for a OS is an incredibly non-portable
+ thing. We have several possible implementations below, and autoconf
+ tries each of them to see what works
+
+ Note that this file does _not_ include includes.h. That is so this code
+ can be called directly from the autoconf tests. That also means
+ this code cannot use any of the normal Samba debug stuff or defines.
+ This is standalone code.
+
+*/
+
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <net/if.h>
+
+#ifdef AUTOCONF_TEST
+struct iface_struct {
+ char name[16];
+ struct in_addr ip;
+ struct in_addr netmask;
+};
+#else
+#include "config.h"
+#include "interfaces.h"
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifndef SIOCGIFCONF
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#ifdef __COMPAR_FN_T
+#define QSORT_CAST (__compar_fn_t)
+#endif
+
+#ifndef QSORT_CAST
+#define QSORT_CAST (int (*)(const void *, const void *))
+#endif
+
+#if HAVE_IFACE_IFCONF
+
+/* this works for Linux 2.2, Solaris 2.5, SunOS4, HPUX 10.20, OSF1
+ V4.0, Ultrix 4.4, SCO Unix 3.2, IRIX 6.4 and FreeBSD 3.2.
+
+ It probably also works on any BSD style system. */
+
+/****************************************************************************
+ get the netmask address for a local interface
+****************************************************************************/
+static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
+{
+ struct ifconf ifc;
+ char buff[8192];
+ int fd, i, n;
+ struct ifreq *ifr=NULL;
+ int total = 0;
+ struct in_addr ipaddr;
+ struct in_addr nmask;
+ char *iname;
+
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+ return -1;
+ }
+
+ ifc.ifc_len = sizeof(buff);
+ ifc.ifc_buf = buff;
+
+ if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
+ close(fd);
+ return -1;
+ }
+
+ ifr = ifc.ifc_req;
+
+ n = ifc.ifc_len / sizeof(struct ifreq);
+
+ /* Loop through interfaces, looking for given IP address */
+ for (i=n-1;i>=0 && total < max_interfaces;i--) {
+ if (ioctl(fd, SIOCGIFADDR, &ifr[i]) != 0) {
+ continue;
+ }
+
+ iname = ifr[i].ifr_name;
+ ipaddr = (*(struct sockaddr_in *)&ifr[i].ifr_addr).sin_addr;
+
+ if (ioctl(fd, SIOCGIFFLAGS, &ifr[i]) != 0) {
+ continue;
+ }
+
+ if (!(ifr[i].ifr_flags & IFF_UP)) {
+ continue;
+ }
+
+ if (ioctl(fd, SIOCGIFNETMASK, &ifr[i]) != 0) {
+ continue;
+ }
+
+ nmask = ((struct sockaddr_in *)&ifr[i].ifr_addr)->sin_addr;
+
+ strncpy(ifaces[total].name, iname, sizeof(ifaces[total].name)-1);
+ ifaces[total].name[sizeof(ifaces[total].name)-1] = 0;
+ ifaces[total].ip = ipaddr;
+ ifaces[total].netmask = nmask;
+ total++;
+ }
+
+ close(fd);
+
+ return total;
+}
+
+#elif HAVE_IFACE_IFREQ
+
+#ifndef I_STR
+#include <sys/stropts.h>
+#endif
+
+/****************************************************************************
+this should cover most of the streams based systems
+Thanks to Andrej.Borsenkow@mow.siemens.ru for several ideas in this code
+****************************************************************************/
+static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
+{
+ struct ifreq ifreq;
+ struct strioctl strioctl;
+ char buff[8192];
+ int fd, i, n;
+ struct ifreq *ifr=NULL;
+ int total = 0;
+ struct in_addr ipaddr;
+ struct in_addr nmask;
+ char *iname;
+
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+ return -1;
+ }
+
+ strioctl.ic_cmd = SIOCGIFCONF;
+ strioctl.ic_dp = buff;
+ strioctl.ic_len = sizeof(buff);
+ if (ioctl(fd, I_STR, &strioctl) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ /* we can ignore the possible sizeof(int) here as the resulting
+ number of interface structures won't change */
+ n = strioctl.ic_len / sizeof(struct ifreq);
+
+ /* we will assume that the kernel returns the length as an int
+ at the start of the buffer if the offered size is a
+ multiple of the structure size plus an int */
+ if (n*sizeof(struct ifreq) + sizeof(int) == strioctl.ic_len) {
+ ifr = (struct ifreq *)(buff + sizeof(int));
+ } else {
+ ifr = (struct ifreq *)buff;
+ }
+
+ /* Loop through interfaces */
+
+ for (i = 0; i<n && total < max_interfaces; i++) {
+ ifreq = ifr[i];
+
+ strioctl.ic_cmd = SIOCGIFFLAGS;
+ strioctl.ic_dp = (char *)&ifreq;
+ strioctl.ic_len = sizeof(struct ifreq);
+ if (ioctl(fd, I_STR, &strioctl) != 0) {
+ continue;
+ }
+
+ if (!(ifreq.ifr_flags & IFF_UP)) {
+ continue;
+ }
+
+ strioctl.ic_cmd = SIOCGIFADDR;
+ strioctl.ic_dp = (char *)&ifreq;
+ strioctl.ic_len = sizeof(struct ifreq);
+ if (ioctl(fd, I_STR, &strioctl) != 0) {
+ continue;
+ }
+
+ ipaddr = (*(struct sockaddr_in *) &ifreq.ifr_addr).sin_addr;
+ iname = ifreq.ifr_name;
+
+ strioctl.ic_cmd = SIOCGIFNETMASK;
+ strioctl.ic_dp = (char *)&ifreq;
+ strioctl.ic_len = sizeof(struct ifreq);
+ if (ioctl(fd, I_STR, &strioctl) != 0) {
+ continue;
+ }
+
+ nmask = ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr;
+
+ strncpy(ifaces[total].name, iname, sizeof(ifaces[total].name)-1);
+ ifaces[total].name[sizeof(ifaces[total].name)-1] = 0;
+ ifaces[total].ip = ipaddr;
+ ifaces[total].netmask = nmask;
+
+ total++;
+ }
+
+ close(fd);
+
+ return total;
+}
+
+#elif HAVE_IFACE_AIX
+
+/****************************************************************************
+this one is for AIX (tested on 4.2)
+****************************************************************************/
+static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
+{
+ char buff[8192];
+ int fd, i;
+ struct ifconf ifc;
+ struct ifreq *ifr=NULL;
+ struct in_addr ipaddr;
+ struct in_addr nmask;
+ char *iname;
+ int total = 0;
+
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+ return -1;
+ }
+
+
+ ifc.ifc_len = sizeof(buff);
+ ifc.ifc_buf = buff;
+
+ if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
+ close(fd);
+ return -1;
+ }
+
+ ifr = ifc.ifc_req;
+
+ /* Loop through interfaces */
+ i = ifc.ifc_len;
+
+ while (i > 0 && total < max_interfaces) {
+ unsigned inc;
+
+ inc = ifr->ifr_addr.sa_len;
+
+ if (ioctl(fd, SIOCGIFADDR, ifr) != 0) {
+ goto next;
+ }
+
+ ipaddr = (*(struct sockaddr_in *) &ifr->ifr_addr).sin_addr;
+ iname = ifr->ifr_name;
+
+ if (ioctl(fd, SIOCGIFFLAGS, ifr) != 0) {
+ goto next;
+ }
+
+ if (!(ifr->ifr_flags & IFF_UP)) {
+ goto next;
+ }
+
+ if (ioctl(fd, SIOCGIFNETMASK, ifr) != 0) {
+ goto next;
+ }
+
+ nmask = ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
+
+ strncpy(ifaces[total].name, iname, sizeof(ifaces[total].name)-1);
+ ifaces[total].name[sizeof(ifaces[total].name)-1] = 0;
+ ifaces[total].ip = ipaddr;
+ ifaces[total].netmask = nmask;
+
+ total++;
+
+ next:
+ /*
+ * Patch from Archie Cobbs (archie@whistle.com). The
+ * addresses in the SIOCGIFCONF interface list have a
+ * minimum size. Usually this doesn't matter, but if
+ * your machine has tunnel interfaces, etc. that have
+ * a zero length "link address", this does matter. */
+
+ if (inc < sizeof(ifr->ifr_addr))
+ inc = sizeof(ifr->ifr_addr);
+ inc += IFNAMSIZ;
+
+ ifr = (struct ifreq*) (((char*) ifr) + inc);
+ i -= inc;
+ }
+
+
+ close(fd);
+ return total;
+}
+
+#else /* a dummy version */
+static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
+{
+ return -1;
+}
+#endif
+
+
+static int iface_comp(struct iface_struct *i1, struct iface_struct *i2)
+{
+ int r;
+ r = strcmp(i1->name, i2->name);
+ if (r) return r;
+ r = ntohl(i1->ip.s_addr) - ntohl(i2->ip.s_addr);
+ if (r) return r;
+ r = ntohl(i1->netmask.s_addr) - ntohl(i2->netmask.s_addr);
+ return r;
+}
+
+/* this wrapper is used to remove duplicates from the interface list generated
+ above */
+int get_interfaces(struct iface_struct *ifaces, int max_interfaces)
+{
+ int total, i, j;
+
+ total = _get_interfaces(ifaces, max_interfaces);
+ if (total <= 0) return total;
+
+ /* now we need to remove duplicates */
+ qsort(ifaces, total, sizeof(ifaces[0]), QSORT_CAST iface_comp);
+
+ for (i=1;i<total;) {
+ if (iface_comp(&ifaces[i-1], &ifaces[i]) == 0) {
+ for (j=i-1;j<total-1;j++) {
+ ifaces[j] = ifaces[j+1];
+ }
+ total--;
+ } else {
+ i++;
+ }
+ }
+
+ return total;
+}
+
+
+#ifdef AUTOCONF_TEST
+/* this is the autoconf driver to test get_interfaces() */
+
+#define MAX_INTERFACES 128
+
+ int main()
+{
+ struct iface_struct ifaces[MAX_INTERFACES];
+ int total = get_interfaces(ifaces, MAX_INTERFACES);
+ int i;
+
+ printf("got %d interfaces:\n", total);
+ if (total <= 0) exit(1);
+
+ for (i=0;i<total;i++) {
+ printf("%-10s ", ifaces[i].name);
+ printf("IP=%s ", inet_ntoa(ifaces[i].ip));
+ printf("NETMASK=%s\n", inet_ntoa(ifaces[i].netmask));
+ }
+ return 0;
+}
+#endif
diff --git a/source/lib/ldap_escape.c b/source/lib/ldap_escape.c
new file mode 100644
index 00000000000..9e88b4999cc
--- /dev/null
+++ b/source/lib/ldap_escape.c
@@ -0,0 +1,90 @@
+/*
+ Unix SMB/CIFS implementation.
+ ldap filter argument escaping
+
+ Copyright (C) 1998, 1999, 2000 Luke Howard <lukeh@padl.com>,
+ Copyright (C) 2003 Andrew Bartlett <abartlet@samba.org>
+
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/**
+ * Escape a parameter to an LDAP filter string, so they cannot contain
+ * embeded ( ) * or \ chars which may cause it not to parse correctly.
+ *
+ * @param s The input string
+ *
+ * @return A string allocated with malloc(), containing the escaped string,
+ * and to be free()ed by the caller.
+ **/
+
+char *escape_ldap_string_alloc(const char *s)
+{
+ size_t len = strlen(s)+1;
+ char *output = malloc(len);
+ char *output_tmp;
+ const char *sub;
+ int i = 0;
+ char *p = output;
+
+ while (*s)
+ {
+ switch (*s)
+ {
+ case '*':
+ sub = "\\2a";
+ break;
+ case '(':
+ sub = "\\28";
+ break;
+ case ')':
+ sub = "\\29";
+ break;
+ case '\\':
+ sub = "\\5c";
+ break;
+ default:
+ sub = NULL;
+ break;
+ }
+
+ if (sub) {
+ len = len + 3;
+ output_tmp = realloc(output, len);
+ if (!output_tmp) {
+ SAFE_FREE(output);
+ return NULL;
+ }
+ output = output_tmp;
+
+ p = &output[i];
+ strncpy (p, sub, 3);
+ p += 3;
+ i += 3;
+
+ } else {
+ *p = *s;
+ p++;
+ i++;
+ }
+ s++;
+ }
+
+ *p = '\0';
+ return output;
+}
diff --git a/source/lib/md4.c b/source/lib/md4.c
new file mode 100644
index 00000000000..6803b7e8831
--- /dev/null
+++ b/source/lib/md4.c
@@ -0,0 +1,169 @@
+/*
+ Unix SMB/CIFS implementation.
+ a implementation of MD4 designed for use in the SMB authentication protocol
+ Copyright (C) Andrew Tridgell 1997-1998.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* NOTE: This code makes no attempt to be fast!
+
+ It assumes that a int is at least 32 bits long
+*/
+
+static uint32 A, B, C, D;
+
+static uint32 F(uint32 X, uint32 Y, uint32 Z)
+{
+ return (X&Y) | ((~X)&Z);
+}
+
+static uint32 G(uint32 X, uint32 Y, uint32 Z)
+{
+ return (X&Y) | (X&Z) | (Y&Z);
+}
+
+static uint32 H(uint32 X, uint32 Y, uint32 Z)
+{
+ return X^Y^Z;
+}
+
+static uint32 lshift(uint32 x, int s)
+{
+ x &= 0xFFFFFFFF;
+ return ((x<<s)&0xFFFFFFFF) | (x>>(32-s));
+}
+
+#define ROUND1(a,b,c,d,k,s) a = lshift(a + F(b,c,d) + X[k], s)
+#define ROUND2(a,b,c,d,k,s) a = lshift(a + G(b,c,d) + X[k] + (uint32)0x5A827999,s)
+#define ROUND3(a,b,c,d,k,s) a = lshift(a + H(b,c,d) + X[k] + (uint32)0x6ED9EBA1,s)
+
+/* this applies md4 to 64 byte chunks */
+static void mdfour64(uint32 *M)
+{
+ int j;
+ uint32 AA, BB, CC, DD;
+ uint32 X[16];
+
+ for (j=0;j<16;j++)
+ X[j] = M[j];
+
+ AA = A; BB = B; CC = C; DD = D;
+
+ ROUND1(A,B,C,D, 0, 3); ROUND1(D,A,B,C, 1, 7);
+ ROUND1(C,D,A,B, 2, 11); ROUND1(B,C,D,A, 3, 19);
+ ROUND1(A,B,C,D, 4, 3); ROUND1(D,A,B,C, 5, 7);
+ ROUND1(C,D,A,B, 6, 11); ROUND1(B,C,D,A, 7, 19);
+ ROUND1(A,B,C,D, 8, 3); ROUND1(D,A,B,C, 9, 7);
+ ROUND1(C,D,A,B, 10, 11); ROUND1(B,C,D,A, 11, 19);
+ ROUND1(A,B,C,D, 12, 3); ROUND1(D,A,B,C, 13, 7);
+ ROUND1(C,D,A,B, 14, 11); ROUND1(B,C,D,A, 15, 19);
+
+ ROUND2(A,B,C,D, 0, 3); ROUND2(D,A,B,C, 4, 5);
+ ROUND2(C,D,A,B, 8, 9); ROUND2(B,C,D,A, 12, 13);
+ ROUND2(A,B,C,D, 1, 3); ROUND2(D,A,B,C, 5, 5);
+ ROUND2(C,D,A,B, 9, 9); ROUND2(B,C,D,A, 13, 13);
+ ROUND2(A,B,C,D, 2, 3); ROUND2(D,A,B,C, 6, 5);
+ ROUND2(C,D,A,B, 10, 9); ROUND2(B,C,D,A, 14, 13);
+ ROUND2(A,B,C,D, 3, 3); ROUND2(D,A,B,C, 7, 5);
+ ROUND2(C,D,A,B, 11, 9); ROUND2(B,C,D,A, 15, 13);
+
+ ROUND3(A,B,C,D, 0, 3); ROUND3(D,A,B,C, 8, 9);
+ ROUND3(C,D,A,B, 4, 11); ROUND3(B,C,D,A, 12, 15);
+ ROUND3(A,B,C,D, 2, 3); ROUND3(D,A,B,C, 10, 9);
+ ROUND3(C,D,A,B, 6, 11); ROUND3(B,C,D,A, 14, 15);
+ ROUND3(A,B,C,D, 1, 3); ROUND3(D,A,B,C, 9, 9);
+ ROUND3(C,D,A,B, 5, 11); ROUND3(B,C,D,A, 13, 15);
+ ROUND3(A,B,C,D, 3, 3); ROUND3(D,A,B,C, 11, 9);
+ ROUND3(C,D,A,B, 7, 11); ROUND3(B,C,D,A, 15, 15);
+
+ A += AA; B += BB; C += CC; D += DD;
+
+ A &= 0xFFFFFFFF; B &= 0xFFFFFFFF;
+ C &= 0xFFFFFFFF; D &= 0xFFFFFFFF;
+
+ for (j=0;j<16;j++)
+ X[j] = 0;
+}
+
+static void copy64(uint32 *M, const unsigned char *in)
+{
+ int i;
+
+ for (i=0;i<16;i++)
+ M[i] = (in[i*4+3]<<24) | (in[i*4+2]<<16) |
+ (in[i*4+1]<<8) | (in[i*4+0]<<0);
+}
+
+static void copy4(unsigned char *out, uint32 x)
+{
+ out[0] = x&0xFF;
+ out[1] = (x>>8)&0xFF;
+ out[2] = (x>>16)&0xFF;
+ out[3] = (x>>24)&0xFF;
+}
+
+/* produce a md4 message digest from data of length n bytes */
+void mdfour(unsigned char *out, const unsigned char *in, int n)
+{
+ unsigned char buf[128];
+ uint32 M[16];
+ uint32 b = n * 8;
+ int i;
+
+ A = 0x67452301;
+ B = 0xefcdab89;
+ C = 0x98badcfe;
+ D = 0x10325476;
+
+ while (n > 64) {
+ copy64(M, in);
+ mdfour64(M);
+ in += 64;
+ n -= 64;
+ }
+
+ for (i=0;i<128;i++)
+ buf[i] = 0;
+ memcpy(buf, in, n);
+ buf[n] = 0x80;
+
+ if (n <= 55) {
+ copy4(buf+56, b);
+ copy64(M, buf);
+ mdfour64(M);
+ } else {
+ copy4(buf+120, b);
+ copy64(M, buf);
+ mdfour64(M);
+ copy64(M, buf+64);
+ mdfour64(M);
+ }
+
+ for (i=0;i<128;i++)
+ buf[i] = 0;
+ copy64(M, buf);
+
+ copy4(out, A);
+ copy4(out+4, B);
+ copy4(out+8, C);
+ copy4(out+12, D);
+
+ A = B = C = D = 0;
+}
+
+
diff --git a/source/lib/md5.c b/source/lib/md5.c
new file mode 100644
index 00000000000..2121b170479
--- /dev/null
+++ b/source/lib/md5.c
@@ -0,0 +1,247 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+/* This code slightly modified to fit into Samba by
+ abartlet@samba.org Jun 2001 */
+
+#include "includes.h"
+
+#include "md5.h"
+
+static void MD5Transform(uint32 buf[4], uint32 const in[16]);
+
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void byteReverse(unsigned char *buf, unsigned longs)
+{
+ uint32 t;
+ do {
+ t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+ ((unsigned) buf[1] << 8 | buf[0]);
+ *(uint32 *) buf = t;
+ buf += 4;
+ } while (--longs);
+}
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+ register uint32 t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((uint32) len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ unsigned char *p = (unsigned char *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ memmove(p, buf, len);
+ return;
+ }
+ memmove(p, buf, t);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32 *) ctx->in);
+ buf += t;
+ len -= t;
+ }
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memmove(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32 *) ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memmove(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+ unsigned int count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32 *) ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count - 8);
+ }
+ byteReverse(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ((uint32 *) ctx->in)[14] = ctx->bits[0];
+ ((uint32 *) ctx->in)[15] = ctx->bits[1];
+
+ MD5Transform(ctx->buf, (uint32 *) ctx->in);
+ byteReverse((unsigned char *) ctx->buf, 4);
+ memmove(digest, ctx->buf, 16);
+ memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
+}
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void MD5Transform(uint32 buf[4], uint32 const in[16])
+{
+ register uint32 a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
diff --git a/source/lib/messages.c b/source/lib/messages.c
new file mode 100644
index 00000000000..8706ede7065
--- /dev/null
+++ b/source/lib/messages.c
@@ -0,0 +1,596 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba internal messaging functions
+ Copyright (C) Andrew Tridgell 2000
+ Copyright (C) 2001 by Martin Pool
+ Copyright (C) 2002 by Jeremy Allison
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/**
+ @defgroup messages Internal messaging framework
+ @{
+ @file messages.c
+
+ @brief Module for internal messaging between Samba daemons.
+
+ The idea is that if a part of Samba wants to do communication with
+ another Samba process then it will do a message_register() of a
+ dispatch function, and use message_send_pid() to send messages to
+ that process.
+
+ The dispatch function is given the pid of the sender, and it can
+ use that to reply by message_send_pid(). See ping_message() for a
+ simple example.
+
+ @caution Dispatch functions must be able to cope with incoming
+ messages on an *odd* byte boundary.
+
+ This system doesn't have any inherent size limitations but is not
+ very efficient for large messages or when messages are sent in very
+ quick succession.
+
+*/
+
+#include "includes.h"
+
+/* the locking database handle */
+static TDB_CONTEXT *tdb;
+static int received_signal;
+
+/* change the message version with any incompatible changes in the protocol */
+#define MESSAGE_VERSION 1
+
+struct message_rec {
+ int msg_version;
+ int msg_type;
+ pid_t dest;
+ pid_t src;
+ size_t len;
+};
+
+/* we have a linked list of dispatch handlers */
+static struct dispatch_fns {
+ struct dispatch_fns *next, *prev;
+ int msg_type;
+ void (*fn)(int msg_type, pid_t pid, void *buf, size_t len);
+} *dispatch_fns;
+
+/****************************************************************************
+ Notifications come in as signals.
+****************************************************************************/
+
+static void sig_usr1(void)
+{
+ received_signal = 1;
+ sys_select_signal();
+}
+
+/****************************************************************************
+ A useful function for testing the message system.
+****************************************************************************/
+
+static void ping_message(int msg_type, pid_t src, void *buf, size_t len)
+{
+ const char *msg = buf ? buf : "none";
+ DEBUG(1,("INFO: Received PING message from PID %u [%s]\n",(unsigned int)src, msg));
+ message_send_pid(src, MSG_PONG, buf, len, True);
+}
+
+/****************************************************************************
+ Initialise the messaging functions.
+****************************************************************************/
+
+BOOL message_init(void)
+{
+ if (tdb) return True;
+
+ tdb = tdb_open_log(lock_path("messages.tdb"),
+ 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT,
+ O_RDWR|O_CREAT,0600);
+
+ if (!tdb) {
+ DEBUG(0,("ERROR: Failed to initialise messages database\n"));
+ return False;
+ }
+
+ CatchSignal(SIGUSR1, SIGNAL_CAST sig_usr1);
+
+ message_register(MSG_PING, ping_message);
+
+ /* Register some debugging related messages */
+
+ register_msg_pool_usage();
+ register_dmalloc_msgs();
+
+ return True;
+}
+
+/*******************************************************************
+ Form a static tdb key from a pid.
+******************************************************************/
+
+static TDB_DATA message_key_pid(pid_t pid)
+{
+ static char key[20];
+ TDB_DATA kbuf;
+
+ slprintf(key, sizeof(key)-1, "PID/%d", (int)pid);
+
+ kbuf.dptr = (char *)key;
+ kbuf.dsize = strlen(key)+1;
+ return kbuf;
+}
+
+/****************************************************************************
+ Notify a process that it has a message. If the process doesn't exist
+ then delete its record in the database.
+****************************************************************************/
+
+static BOOL message_notify(pid_t pid)
+{
+ /*
+ * Doing kill with a non-positive pid causes messages to be
+ * sent to places we don't want.
+ */
+
+ SMB_ASSERT(pid > 0);
+
+ if (kill(pid, SIGUSR1) == -1) {
+ if (errno == ESRCH) {
+ DEBUG(2,("pid %d doesn't exist - deleting messages record\n", (int)pid));
+ tdb_delete(tdb, message_key_pid(pid));
+ } else {
+ DEBUG(2,("message to process %d failed - %s\n", (int)pid, strerror(errno)));
+ }
+ return False;
+ }
+ return True;
+}
+
+/****************************************************************************
+ Send a message to a particular pid.
+****************************************************************************/
+
+static BOOL message_send_pid_internal(pid_t pid, int msg_type, const void *buf, size_t len,
+ BOOL duplicates_allowed, unsigned int timeout)
+{
+ TDB_DATA kbuf;
+ TDB_DATA dbuf;
+ TDB_DATA old_dbuf;
+ struct message_rec rec;
+ char *ptr;
+ struct message_rec prec;
+
+ /*
+ * Doing kill with a non-positive pid causes messages to be
+ * sent to places we don't want.
+ */
+
+ SMB_ASSERT(pid > 0);
+
+ rec.msg_version = MESSAGE_VERSION;
+ rec.msg_type = msg_type;
+ rec.dest = pid;
+ rec.src = sys_getpid();
+ rec.len = len;
+
+ kbuf = message_key_pid(pid);
+
+ dbuf.dptr = (void *)malloc(len + sizeof(rec));
+ if (!dbuf.dptr)
+ return False;
+
+ memcpy(dbuf.dptr, &rec, sizeof(rec));
+ if (len > 0)
+ memcpy((void *)((char*)dbuf.dptr+sizeof(rec)), buf, len);
+
+ dbuf.dsize = len + sizeof(rec);
+
+ if (duplicates_allowed) {
+
+ /* If duplicates are allowed we can just append the message and return. */
+
+ /* lock the record for the destination */
+ if (timeout) {
+ if (tdb_chainlock_with_timeout(tdb, kbuf, timeout) == -1) {
+ DEBUG(0,("message_send_pid_internal: failed to get chainlock with timeout %ul.\n", timeout));
+ return False;
+ }
+ } else {
+ if (tdb_chainlock(tdb, kbuf) == -1) {
+ DEBUG(0,("message_send_pid_internal: failed to get chainlock.\n"));
+ return False;
+ }
+ }
+ tdb_append(tdb, kbuf, dbuf);
+ tdb_chainunlock(tdb, kbuf);
+
+ SAFE_FREE(dbuf.dptr);
+ errno = 0; /* paranoia */
+ return message_notify(pid);
+ }
+
+ /* lock the record for the destination */
+ if (timeout) {
+ if (tdb_chainlock_with_timeout(tdb, kbuf, timeout) == -1) {
+ DEBUG(0,("message_send_pid_internal: failed to get chainlock with timeout %ul.\n", timeout));
+ return False;
+ }
+ } else {
+ if (tdb_chainlock(tdb, kbuf) == -1) {
+ DEBUG(0,("message_send_pid_internal: failed to get chainlock.\n"));
+ return False;
+ }
+ }
+
+ old_dbuf = tdb_fetch(tdb, kbuf);
+
+ if (!old_dbuf.dptr) {
+ /* its a new record */
+
+ tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
+ tdb_chainunlock(tdb, kbuf);
+
+ SAFE_FREE(dbuf.dptr);
+ errno = 0; /* paranoia */
+ return message_notify(pid);
+ }
+
+ /* Not a new record. Check for duplicates. */
+
+ for(ptr = (char *)old_dbuf.dptr; ptr < old_dbuf.dptr + old_dbuf.dsize; ) {
+ /*
+ * First check if the message header matches, then, if it's a non-zero
+ * sized message, check if the data matches. If so it's a duplicate and
+ * we can discard it. JRA.
+ */
+
+ if (!memcmp(ptr, &rec, sizeof(rec))) {
+ if (!len || (len && !memcmp( ptr + sizeof(rec), buf, len))) {
+ tdb_chainunlock(tdb, kbuf);
+ DEBUG(10,("message_send_pid_internal: discarding duplicate message.\n"));
+ SAFE_FREE(dbuf.dptr);
+ SAFE_FREE(old_dbuf.dptr);
+ return True;
+ }
+ }
+ memcpy(&prec, ptr, sizeof(prec));
+ ptr += sizeof(rec) + prec.len;
+ }
+
+ /* we're adding to an existing entry */
+
+ tdb_append(tdb, kbuf, dbuf);
+ tdb_chainunlock(tdb, kbuf);
+
+ SAFE_FREE(old_dbuf.dptr);
+ SAFE_FREE(dbuf.dptr);
+
+ errno = 0; /* paranoia */
+ return message_notify(pid);
+}
+
+/****************************************************************************
+ Send a message to a particular pid - no timeout.
+****************************************************************************/
+
+BOOL message_send_pid(pid_t pid, int msg_type, const void *buf, size_t len, BOOL duplicates_allowed)
+{
+ return message_send_pid_internal(pid, msg_type, buf, len, duplicates_allowed, 0);
+}
+
+/****************************************************************************
+ Send a message to a particular pid, with timeout in seconds.
+****************************************************************************/
+
+BOOL message_send_pid_with_timeout(pid_t pid, int msg_type, const void *buf, size_t len,
+ BOOL duplicates_allowed, unsigned int timeout)
+{
+ return message_send_pid_internal(pid, msg_type, buf, len, duplicates_allowed, timeout);
+}
+
+/****************************************************************************
+ Count the messages pending for a particular pid. Expensive....
+****************************************************************************/
+
+unsigned int messages_pending_for_pid(pid_t pid)
+{
+ TDB_DATA kbuf;
+ TDB_DATA dbuf;
+ char *buf;
+ unsigned int message_count = 0;
+
+ kbuf = message_key_pid(sys_getpid());
+
+ dbuf = tdb_fetch(tdb, kbuf);
+ if (dbuf.dptr == NULL || dbuf.dsize == 0) {
+ SAFE_FREE(dbuf.dptr);
+ return 0;
+ }
+
+ for (buf = dbuf.dptr; dbuf.dsize > sizeof(struct message_rec);) {
+ struct message_rec rec;
+ memcpy(&rec, buf, sizeof(rec));
+ buf += (sizeof(rec) + rec.len);
+ dbuf.dsize -= (sizeof(rec) + rec.len);
+ message_count++;
+ }
+
+ SAFE_FREE(dbuf.dptr);
+ return message_count;
+}
+
+/****************************************************************************
+ Retrieve all messages for the current process.
+****************************************************************************/
+
+static BOOL retrieve_all_messages(char **msgs_buf, size_t *total_len)
+{
+ TDB_DATA kbuf;
+ TDB_DATA dbuf;
+ TDB_DATA null_dbuf;
+
+ ZERO_STRUCT(null_dbuf);
+
+ *msgs_buf = NULL;
+ *total_len = 0;
+
+ kbuf = message_key_pid(sys_getpid());
+
+ if (tdb_chainlock(tdb, kbuf) == -1)
+ return False;
+
+ dbuf = tdb_fetch(tdb, kbuf);
+ /*
+ * Replace with an empty record to keep the allocated
+ * space in the tdb.
+ */
+ tdb_store(tdb, kbuf, null_dbuf, TDB_REPLACE);
+ tdb_chainunlock(tdb, kbuf);
+
+ if (dbuf.dptr == NULL || dbuf.dsize == 0) {
+ SAFE_FREE(dbuf.dptr);
+ return False;
+ }
+
+ *msgs_buf = dbuf.dptr;
+ *total_len = dbuf.dsize;
+
+ return True;
+}
+
+/****************************************************************************
+ Parse out the next message for the current process.
+****************************************************************************/
+
+static BOOL message_recv(char *msgs_buf, size_t total_len, int *msg_type, pid_t *src, char **buf, size_t *len)
+{
+ struct message_rec rec;
+ char *ret_buf = *buf;
+
+ *buf = NULL;
+ *len = 0;
+
+ if (total_len - (ret_buf - msgs_buf) < sizeof(rec))
+ return False;
+
+ memcpy(&rec, ret_buf, sizeof(rec));
+ ret_buf += sizeof(rec);
+
+ if (rec.msg_version != MESSAGE_VERSION) {
+ DEBUG(0,("message version %d received (expected %d)\n", rec.msg_version, MESSAGE_VERSION));
+ return False;
+ }
+
+ if (rec.len > 0) {
+ if (total_len - (ret_buf - msgs_buf) < rec.len)
+ return False;
+ }
+
+ *len = rec.len;
+ *msg_type = rec.msg_type;
+ *src = rec.src;
+ *buf = ret_buf;
+
+ return True;
+}
+
+/****************************************************************************
+ Receive and dispatch any messages pending for this process.
+ Notice that all dispatch handlers for a particular msg_type get called,
+ so you can register multiple handlers for a message.
+ *NOTE*: Dispatch functions must be able to cope with incoming
+ messages on an *odd* byte boundary.
+****************************************************************************/
+
+void message_dispatch(void)
+{
+ int msg_type;
+ pid_t src;
+ char *buf;
+ char *msgs_buf;
+ size_t len, total_len;
+ struct dispatch_fns *dfn;
+ int n_handled;
+
+ if (!received_signal)
+ return;
+
+ DEBUG(10,("message_dispatch: received_signal = %d\n", received_signal));
+
+ received_signal = 0;
+
+ if (!retrieve_all_messages(&msgs_buf, &total_len))
+ return;
+
+ for (buf = msgs_buf; message_recv(msgs_buf, total_len, &msg_type, &src, &buf, &len); buf += len) {
+ DEBUG(10,("message_dispatch: received msg_type=%d src_pid=%u\n",
+ msg_type, (unsigned int) src));
+ n_handled = 0;
+ for (dfn = dispatch_fns; dfn; dfn = dfn->next) {
+ if (dfn->msg_type == msg_type) {
+ DEBUG(10,("message_dispatch: processing message of type %d.\n", msg_type));
+ dfn->fn(msg_type, src, len ? (void *)buf : NULL, len);
+ n_handled++;
+ }
+ }
+ if (!n_handled) {
+ DEBUG(5,("message_dispatch: warning: no handlers registed for "
+ "msg_type %d in pid %u\n",
+ msg_type, (unsigned int)sys_getpid()));
+ }
+ }
+ SAFE_FREE(msgs_buf);
+}
+
+/****************************************************************************
+ Register a dispatch function for a particular message type.
+ *NOTE*: Dispatch functions must be able to cope with incoming
+ messages on an *odd* byte boundary.
+****************************************************************************/
+
+void message_register(int msg_type,
+ void (*fn)(int msg_type, pid_t pid, void *buf, size_t len))
+{
+ struct dispatch_fns *dfn;
+
+ dfn = (struct dispatch_fns *)malloc(sizeof(*dfn));
+
+ if (dfn != NULL) {
+
+ ZERO_STRUCTPN(dfn);
+
+ dfn->msg_type = msg_type;
+ dfn->fn = fn;
+
+ DLIST_ADD(dispatch_fns, dfn);
+ }
+ else {
+
+ DEBUG(0,("message_register: Not enough memory. malloc failed!\n"));
+ }
+}
+
+/****************************************************************************
+ De-register the function for a particular message type.
+****************************************************************************/
+
+void message_deregister(int msg_type)
+{
+ struct dispatch_fns *dfn, *next;
+
+ for (dfn = dispatch_fns; dfn; dfn = next) {
+ next = dfn->next;
+ if (dfn->msg_type == msg_type) {
+ DLIST_REMOVE(dispatch_fns, dfn);
+ SAFE_FREE(dfn);
+ }
+ }
+}
+
+struct msg_all {
+ int msg_type;
+ uint32 msg_flag;
+ const void *buf;
+ size_t len;
+ BOOL duplicates;
+ int n_sent;
+};
+
+/****************************************************************************
+ Send one of the messages for the broadcast.
+****************************************************************************/
+
+static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
+{
+ struct connections_data crec;
+ struct msg_all *msg_all = (struct msg_all *)state;
+
+ if (dbuf.dsize != sizeof(crec))
+ return 0;
+
+ memcpy(&crec, dbuf.dptr, sizeof(crec));
+
+ if (crec.cnum != -1)
+ return 0;
+
+ /* Don't send if the receiver hasn't registered an interest. */
+
+ if(!(crec.bcast_msg_flags & msg_all->msg_flag))
+ return 0;
+
+ /* If the msg send fails because the pid was not found (i.e. smbd died),
+ * the msg has already been deleted from the messages.tdb.*/
+
+ if (!message_send_pid(crec.pid, msg_all->msg_type,
+ msg_all->buf, msg_all->len,
+ msg_all->duplicates)) {
+
+ /* If the pid was not found delete the entry from connections.tdb */
+
+ if (errno == ESRCH) {
+ DEBUG(2,("pid %u doesn't exist - deleting connections %d [%s]\n",
+ (unsigned int)crec.pid, crec.cnum, crec.name));
+ tdb_delete(the_tdb, kbuf);
+ }
+ }
+ msg_all->n_sent++;
+ return 0;
+}
+
+/**
+ * Send a message to all smbd processes.
+ *
+ * It isn't very efficient, but should be OK for the sorts of
+ * applications that use it. When we need efficient broadcast we can add
+ * it.
+ *
+ * @param n_sent Set to the number of messages sent. This should be
+ * equal to the number of processes, but be careful for races.
+ *
+ * @retval True for success.
+ **/
+BOOL message_send_all(TDB_CONTEXT *conn_tdb, int msg_type,
+ const void *buf, size_t len,
+ BOOL duplicates_allowed,
+ int *n_sent)
+{
+ struct msg_all msg_all;
+
+ msg_all.msg_type = msg_type;
+ if (msg_type < 1000)
+ msg_all.msg_flag = FLAG_MSG_GENERAL;
+ else if (msg_type > 1000 && msg_type < 2000)
+ msg_all.msg_flag = FLAG_MSG_NMBD;
+ else if (msg_type > 2000 && msg_type < 3000)
+ msg_all.msg_flag = FLAG_MSG_PRINTING;
+ else if (msg_type > 3000 && msg_type < 4000)
+ msg_all.msg_flag = FLAG_MSG_SMBD;
+ else
+ return False;
+
+ msg_all.buf = buf;
+ msg_all.len = len;
+ msg_all.duplicates = duplicates_allowed;
+ msg_all.n_sent = 0;
+
+ tdb_traverse(conn_tdb, traverse_fn, &msg_all);
+ if (n_sent)
+ *n_sent = msg_all.n_sent;
+ return True;
+}
+/** @} **/
diff --git a/source/lib/module.c b/source/lib/module.c
new file mode 100644
index 00000000000..2abe918ef44
--- /dev/null
+++ b/source/lib/module.c
@@ -0,0 +1,304 @@
+/*
+ Unix SMB/CIFS implementation.
+ module loading system
+
+ Copyright (C) Jelmer Vernooij 2002-2003
+ Copyright (C) Stefan (metze) Metzmacher 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#ifdef HAVE_DLOPEN
+
+/* Load a dynamic module. Only log a level 0 error if we are not checking
+ for the existence of a module (probling). */
+
+static NTSTATUS do_smb_load_module(const char *module_name, BOOL is_probe)
+{
+ void *handle;
+ init_module_function *init;
+ NTSTATUS status;
+ const char *error;
+
+ /* Always try to use LAZY symbol resolving; if the plugin has
+ * backwards compatibility, there might be symbols in the
+ * plugin referencing to old (removed) functions
+ */
+ handle = sys_dlopen(module_name, RTLD_LAZY);
+
+ if(!handle) {
+ int level = is_probe ? 3 : 0;
+ error = sys_dlerror();
+ DEBUG(level, ("Error loading module '%s': %s\n", module_name, error ? error : ""));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ init = (init_module_function *)sys_dlsym(handle, "init_module");
+
+ /* we must check sys_dlerror() to determine if it worked, because
+ sys_dlsym() can validly return NULL */
+ error = sys_dlerror();
+ if (error) {
+ DEBUG(0, ("Error trying to resolve symbol 'init_module' in %s: %s\n",
+ module_name, error));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ status = init();
+
+ DEBUG(2, ("Module '%s' loaded\n", module_name));
+
+ return status;
+}
+
+NTSTATUS smb_load_module(const char *module_name)
+{
+ return do_smb_load_module(module_name, False);
+}
+
+/* Load all modules in list and return number of
+ * modules that has been successfully loaded */
+int smb_load_modules(const char **modules)
+{
+ int i;
+ int success = 0;
+
+ for(i = 0; modules[i]; i++){
+ if(NT_STATUS_IS_OK(smb_load_module(modules[i]))) {
+ success++;
+ }
+ }
+
+ DEBUG(2, ("%d modules successfully loaded\n", success));
+
+ return success;
+}
+
+NTSTATUS smb_probe_module(const char *subsystem, const char *module)
+{
+ pstring full_path;
+
+ /* Check for absolute path */
+
+ /* if we make any 'samba multibyte string'
+ calls here, we break
+ for loading string modules */
+
+ DEBUG(5, ("Probing module '%s'\n", module));
+
+ if (module[0] == '/')
+ return do_smb_load_module(module, True);
+
+ pstrcpy(full_path, lib_path(subsystem));
+ pstrcat(full_path, "/");
+ pstrcat(full_path, module);
+ pstrcat(full_path, ".");
+ pstrcat(full_path, shlib_ext());
+
+ DEBUG(5, ("Probing module '%s': Trying to load from %s\n", module, full_path));
+
+ return do_smb_load_module(full_path, True);
+}
+
+#else /* HAVE_DLOPEN */
+
+NTSTATUS smb_load_module(const char *module_name)
+{
+ DEBUG(0,("This samba executable has not been built with plugin support\n"));
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+int smb_load_modules(const char **modules)
+{
+ DEBUG(0,("This samba executable has not been built with plugin support\n"));
+ return -1;
+}
+
+NTSTATUS smb_probe_module(const char *subsystem, const char *module)
+{
+ DEBUG(0,("This samba executable has not been built with plugin support, not probing\n"));
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+#endif /* HAVE_DLOPEN */
+
+void init_modules(void)
+{
+ /* FIXME: This can cause undefined symbol errors :
+ * smb_register_vfs() isn't available in nmbd, for example */
+ if(lp_preload_modules())
+ smb_load_modules(lp_preload_modules());
+}
+
+
+/***************************************************************************
+ * This Function registers a idle event
+ *
+ * the registered funtions are run periodically
+ * and maybe shutdown idle connections (e.g. to an LDAP server)
+ ***************************************************************************/
+static smb_event_id_t smb_idle_event_id = 1;
+
+struct smb_idle_list_ent {
+ struct smb_idle_list_ent *prev,*next;
+ smb_event_id_t id;
+ smb_idle_event_fn *fn;
+ void *data;
+ time_t interval;
+ time_t lastrun;
+};
+
+static struct smb_idle_list_ent *smb_idle_event_list = NULL;
+
+smb_event_id_t smb_register_idle_event(smb_idle_event_fn *fn, void *data, time_t interval)
+{
+ struct smb_idle_list_ent *event;
+
+ if (!fn) {
+ return SMB_EVENT_ID_INVALID;
+ }
+
+ event = (struct smb_idle_list_ent *)malloc(sizeof(struct smb_idle_list_ent));
+ if (!event) {
+ DEBUG(0,("malloc() failed!\n"));
+ return SMB_EVENT_ID_INVALID;
+ }
+ event->fn = fn;
+ event->data = data;
+ event->interval = interval;
+ event->lastrun = 0;
+ event->id = smb_idle_event_id++;
+
+ DLIST_ADD(smb_idle_event_list,event);
+
+ return event->id;
+}
+
+BOOL smb_unregister_idle_event(smb_event_id_t id)
+{
+ struct smb_idle_list_ent *event = smb_idle_event_list;
+
+ while(event) {
+ if (event->id == id) {
+ DLIST_REMOVE(smb_idle_event_list,event);
+ SAFE_FREE(event);
+ return True;
+ }
+ event = event->next;
+ }
+
+ return False;
+}
+
+void smb_run_idle_events(time_t now)
+{
+ struct smb_idle_list_ent *event = smb_idle_event_list;
+
+ while (event) {
+ struct smb_idle_list_ent *next = event->next;
+ time_t interval;
+
+ if (event->interval <= 0) {
+ interval = SMB_IDLE_EVENT_DEFAULT_INTERVAL;
+ } else if (event->interval >= SMB_IDLE_EVENT_MIN_INTERVAL) {
+ interval = event->interval;
+ } else {
+ interval = SMB_IDLE_EVENT_MIN_INTERVAL;
+ }
+ if (now >(event->lastrun+interval)) {
+ event->lastrun = now;
+ event->fn(&event->data,&event->interval,now);
+ }
+ event = next;
+ }
+
+ return;
+}
+
+/***************************************************************************
+ * This Function registers a exit event
+ *
+ * the registered functions are run on exit()
+ * and maybe shutdown idle connections (e.g. to an LDAP server)
+ ***************************************************************************/
+
+struct smb_exit_list_ent {
+ struct smb_exit_list_ent *prev,*next;
+ smb_event_id_t id;
+ smb_exit_event_fn *fn;
+ void *data;
+};
+
+static struct smb_exit_list_ent *smb_exit_event_list = NULL;
+
+smb_event_id_t smb_register_exit_event(smb_exit_event_fn *fn, void *data)
+{
+ struct smb_exit_list_ent *event;
+ static smb_event_id_t smb_exit_event_id = 1;
+
+ if (!fn) {
+ return SMB_EVENT_ID_INVALID;
+ }
+
+ event = (struct smb_exit_list_ent *)malloc(sizeof(struct smb_exit_list_ent));
+ if (!event) {
+ DEBUG(0,("malloc() failed!\n"));
+ return SMB_EVENT_ID_INVALID;
+ }
+ event->fn = fn;
+ event->data = data;
+ event->id = smb_exit_event_id++;
+
+ DLIST_ADD(smb_exit_event_list,event);
+
+ return event->id;
+}
+
+BOOL smb_unregister_exit_event(smb_event_id_t id)
+{
+ struct smb_exit_list_ent *event = smb_exit_event_list;
+
+ while(event) {
+ if (event->id == id) {
+ DLIST_REMOVE(smb_exit_event_list,event);
+ SAFE_FREE(event);
+ return True;
+ }
+ event = event->next;
+ }
+
+ return False;
+}
+
+void smb_run_exit_events(void)
+{
+ struct smb_exit_list_ent *event = smb_exit_event_list;
+ struct smb_exit_list_ent *tmp = NULL;
+
+ while (event) {
+ event->fn(&event->data);
+ tmp = event;
+ event = event->next;
+ /* exit event should only run one time :-)*/
+ SAFE_FREE(tmp);
+ }
+
+ /* the list is empty now...*/
+ smb_exit_event_list = NULL;
+
+ return;
+}
diff --git a/source/lib/ms_fnmatch.c b/source/lib/ms_fnmatch.c
new file mode 100644
index 00000000000..24232c3b523
--- /dev/null
+++ b/source/lib/ms_fnmatch.c
@@ -0,0 +1,249 @@
+/*
+ Unix SMB/CIFS implementation.
+ filename matching routine
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ This module was originally based on fnmatch.c copyright by the Free
+ Software Foundation. It bears little resemblence to that code now
+*/
+
+
+#if FNMATCH_TEST
+#include <stdio.h>
+#include <stdlib.h>
+#else
+#include "includes.h"
+#endif
+
+/*
+ bugger. we need a separate wildcard routine for older versions
+ of the protocol. This is not yet perfect, but its a lot
+ better than what we had */
+static int ms_fnmatch_lanman_core(const smb_ucs2_t *pattern,
+ const smb_ucs2_t *string,
+ BOOL case_sensitive)
+{
+ const smb_ucs2_t *p = pattern, *n = string;
+ smb_ucs2_t c;
+
+ if (strcmp_wa(p, "?")==0 && strcmp_wa(n, ".")) goto match;
+
+ while ((c = *p++)) {
+ switch (c) {
+ case UCS2_CHAR('.'):
+ if (! *n) goto next;
+ if (*n != UCS2_CHAR('.')) goto nomatch;
+ n++;
+ break;
+
+ case UCS2_CHAR('?'):
+ if (! *n) goto next;
+ if ((*n == UCS2_CHAR('.') &&
+ n[1] != UCS2_CHAR('.')) || ! *n)
+ goto next;
+ n++;
+ break;
+
+ case UCS2_CHAR('>'):
+ if (! *n) goto next;
+ if (n[0] == UCS2_CHAR('.')) {
+ if (! n[1] && ms_fnmatch_lanman_core(p, n+1, case_sensitive) == 0) goto match;
+ if (ms_fnmatch_lanman_core(p, n, case_sensitive) == 0) goto match;
+ goto nomatch;
+ }
+ n++;
+ break;
+
+ case UCS2_CHAR('*'):
+ if (! *n) goto next;
+ if (! *p) goto match;
+ for (; *n; n++) {
+ if (ms_fnmatch_lanman_core(p, n, case_sensitive) == 0) goto match;
+ }
+ break;
+
+ case UCS2_CHAR('<'):
+ for (; *n; n++) {
+ if (ms_fnmatch_lanman_core(p, n, case_sensitive) == 0) goto match;
+ if (*n == UCS2_CHAR('.') &&
+ !strchr_w(n+1,UCS2_CHAR('.'))) {
+ n++;
+ break;
+ }
+ }
+ break;
+
+ case UCS2_CHAR('"'):
+ if (*n == 0 && ms_fnmatch_lanman_core(p, n, case_sensitive) == 0) goto match;
+ if (*n != UCS2_CHAR('.')) goto nomatch;
+ n++;
+ break;
+
+ default:
+ if (case_sensitive) {
+ if (c != *n) goto nomatch;
+ } else {
+ if (tolower_w(c) != tolower_w(*n)) goto nomatch;
+ }
+ n++;
+ }
+ }
+
+ if (! *n) goto match;
+
+ nomatch:
+ /*
+ if (verbose) printf("NOMATCH pattern=[%s] string=[%s]\n", pattern, string);
+ */
+ return -1;
+
+next:
+ if (ms_fnmatch_lanman_core(p, n, case_sensitive) == 0) goto match;
+ goto nomatch;
+
+ match:
+ /*
+ if (verbose) printf("MATCH pattern=[%s] string=[%s]\n", pattern, string);
+ */
+ return 0;
+}
+
+static int ms_fnmatch_lanman1(const smb_ucs2_t *pattern,
+ const smb_ucs2_t *string, BOOL case_sensitive)
+{
+ if (!strpbrk_wa(pattern, "?*<>\"")) {
+ smb_ucs2_t s[] = {UCS2_CHAR('.'), 0};
+ if (strcmp_wa(string,"..") == 0) string = s;
+ return strcasecmp_w(pattern, string);
+ }
+
+ if (strcmp_wa(string,"..") == 0 || strcmp_wa(string,".") == 0) {
+ smb_ucs2_t dot[] = {UCS2_CHAR('.'), 0};
+ smb_ucs2_t dotdot[] = {UCS2_CHAR('.'), UCS2_CHAR('.'), 0};
+ return ms_fnmatch_lanman_core(pattern, dotdot, case_sensitive) &&
+ ms_fnmatch_lanman_core(pattern, dot, case_sensitive);
+ }
+
+ return ms_fnmatch_lanman_core(pattern, string, case_sensitive);
+}
+
+
+/* the following function was derived using the masktest utility -
+ after years of effort we finally have a perfect MS wildcard
+ matching routine!
+
+ NOTE: this matches only filenames with no directory component
+
+ Returns 0 on match, -1 on fail.
+*/
+static int ms_fnmatch_w(const smb_ucs2_t *pattern, const smb_ucs2_t *string,
+ int protocol, BOOL case_sensitive)
+{
+ const smb_ucs2_t *p = pattern, *n = string;
+ smb_ucs2_t c;
+
+ if (protocol <= PROTOCOL_LANMAN2) {
+ return ms_fnmatch_lanman1(pattern, string, case_sensitive);
+ }
+
+ while ((c = *p++)) {
+ switch (c) {
+ case UCS2_CHAR('?'):
+ if (! *n) return -1;
+ n++;
+ break;
+
+ case UCS2_CHAR('>'):
+ if (n[0] == UCS2_CHAR('.')) {
+ if (! n[1] && ms_fnmatch_w(p, n+1, protocol, case_sensitive) == 0) return 0;
+ if (ms_fnmatch_w(p, n, protocol, case_sensitive) == 0) return 0;
+ return -1;
+ }
+ if (! *n) return ms_fnmatch_w(p, n, protocol, case_sensitive);
+ n++;
+ break;
+
+ case UCS2_CHAR('*'):
+ for (; *n; n++) {
+ if (ms_fnmatch_w(p, n, protocol, case_sensitive) == 0) return 0;
+ }
+ break;
+
+ case UCS2_CHAR('<'):
+ for (; *n; n++) {
+ if (ms_fnmatch_w(p, n, protocol, case_sensitive) == 0) return 0;
+ if (*n == UCS2_CHAR('.') && !strchr_wa(n+1,'.')) {
+ n++;
+ break;
+ }
+ }
+ break;
+
+ case UCS2_CHAR('"'):
+ if (*n == 0 && ms_fnmatch_w(p, n, protocol, case_sensitive) == 0) return 0;
+ if (*n != UCS2_CHAR('.')) return -1;
+ n++;
+ break;
+
+ default:
+ if (case_sensitive) {
+ if (c != *n) return -1;
+ } else {
+ if (tolower_w(c) != tolower_w(*n)) return -1;
+ }
+ n++;
+ }
+ }
+
+ if (! *n) return 0;
+
+ return -1;
+}
+
+int ms_fnmatch(const char *pattern, const char *string, int protocol,
+ BOOL case_senstive)
+{
+ wpstring buffer_pattern, buffer_string;
+ int ret;
+ size_t size;
+
+ size = push_ucs2(NULL, buffer_pattern, pattern, sizeof(buffer_pattern), STR_TERMINATE);
+ if (size == (size_t)-1) {
+ return -1;
+ /* Not quite the right answer, but finding the right one
+ under this failure case is expensive, and it's pretty close */
+ }
+
+ size = push_ucs2(NULL, buffer_string, string, sizeof(buffer_string), STR_TERMINATE);
+ if (size == (size_t)-1) {
+ return -1;
+ /* Not quite the right answer, but finding the right one
+ under this failure case is expensive, and it's pretty close */
+ }
+
+ ret = ms_fnmatch_w(buffer_pattern, buffer_string, protocol, case_senstive);
+ DEBUG(10,("ms_fnmatch(%s,%s) -> %d\n", pattern, string, ret));
+
+ return ret;
+}
+
+/* a generic fnmatch function - uses for non-CIFS pattern matching */
+int gen_fnmatch(const char *pattern, const char *string)
+{
+ return ms_fnmatch(pattern, string, PROTOCOL_NT1, True);
+}
diff --git a/source/lib/pam_errors.c b/source/lib/pam_errors.c
new file mode 100644
index 00000000000..925441fb1d4
--- /dev/null
+++ b/source/lib/pam_errors.c
@@ -0,0 +1,126 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * PAM error mapping functions
+ * Copyright (C) Andrew Bartlett 2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+#ifdef WITH_PAM
+#include <security/pam_appl.h>
+
+#if defined(PAM_AUTHTOK_RECOVERY_ERR) && !defined(PAM_AUTHTOK_RECOVER_ERR)
+#define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_RECOVERY_ERR
+#endif
+
+/* PAM -> NT_STATUS map */
+static const struct {
+ int pam_code;
+ NTSTATUS ntstatus;
+} pam_to_nt_status_map[] = {
+ {PAM_OPEN_ERR, NT_STATUS_UNSUCCESSFUL},
+ {PAM_SYMBOL_ERR, NT_STATUS_UNSUCCESSFUL},
+ {PAM_SERVICE_ERR, NT_STATUS_UNSUCCESSFUL},
+ {PAM_SYSTEM_ERR, NT_STATUS_UNSUCCESSFUL},
+ {PAM_BUF_ERR, NT_STATUS_UNSUCCESSFUL},
+ {PAM_PERM_DENIED, NT_STATUS_ACCESS_DENIED},
+ {PAM_AUTH_ERR, NT_STATUS_WRONG_PASSWORD},
+ {PAM_CRED_INSUFFICIENT, NT_STATUS_INSUFFICIENT_LOGON_INFO}, /* FIXME: Is this correct? */
+ {PAM_AUTHINFO_UNAVAIL, NT_STATUS_LOGON_FAILURE},
+ {PAM_USER_UNKNOWN, NT_STATUS_NO_SUCH_USER},
+ {PAM_MAXTRIES, NT_STATUS_REMOTE_SESSION_LIMIT}, /* FIXME: Is this correct? */
+ {PAM_NEW_AUTHTOK_REQD, NT_STATUS_PASSWORD_MUST_CHANGE},
+ {PAM_ACCT_EXPIRED, NT_STATUS_ACCOUNT_EXPIRED},
+ {PAM_SESSION_ERR, NT_STATUS_INSUFFICIENT_RESOURCES},
+ {PAM_CRED_UNAVAIL, NT_STATUS_NO_TOKEN}, /* FIXME: Is this correct? */
+ {PAM_CRED_EXPIRED, NT_STATUS_PASSWORD_EXPIRED}, /* FIXME: Is this correct? */
+ {PAM_CRED_ERR, NT_STATUS_UNSUCCESSFUL},
+ {PAM_AUTHTOK_ERR, NT_STATUS_UNSUCCESSFUL},
+#ifdef PAM_AUTHTOK_RECOVER_ERR
+ {PAM_AUTHTOK_RECOVER_ERR, NT_STATUS_UNSUCCESSFUL},
+#endif
+ {PAM_AUTHTOK_EXPIRED, NT_STATUS_PASSWORD_EXPIRED},
+ {PAM_SUCCESS, NT_STATUS_OK}
+};
+
+/* NT_STATUS -> PAM map */
+static const struct {
+ NTSTATUS ntstatus;
+ int pam_code;
+} nt_status_to_pam_map[] = {
+ {NT_STATUS_UNSUCCESSFUL, PAM_SYSTEM_ERR},
+ {NT_STATUS_NO_SUCH_USER, PAM_USER_UNKNOWN},
+ {NT_STATUS_WRONG_PASSWORD, PAM_AUTH_ERR},
+ {NT_STATUS_LOGON_FAILURE, PAM_AUTH_ERR},
+ {NT_STATUS_ACCOUNT_EXPIRED, PAM_ACCT_EXPIRED},
+ {NT_STATUS_PASSWORD_EXPIRED, PAM_AUTHTOK_EXPIRED},
+ {NT_STATUS_PASSWORD_MUST_CHANGE, PAM_NEW_AUTHTOK_REQD},
+ {NT_STATUS_OK, PAM_SUCCESS}
+};
+
+/*****************************************************************************
+convert a PAM error to a NT status32 code
+ *****************************************************************************/
+NTSTATUS pam_to_nt_status(int pam_error)
+{
+ int i;
+ if (pam_error == 0) return NT_STATUS_OK;
+
+ for (i=0; NT_STATUS_V(pam_to_nt_status_map[i].ntstatus); i++) {
+ if (pam_error == pam_to_nt_status_map[i].pam_code)
+ return pam_to_nt_status_map[i].ntstatus;
+ }
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/*****************************************************************************
+convert an NT status32 code to a PAM error
+ *****************************************************************************/
+int nt_status_to_pam(NTSTATUS nt_status)
+{
+ int i;
+ if NT_STATUS_IS_OK(nt_status) return PAM_SUCCESS;
+
+ for (i=0; NT_STATUS_V(nt_status_to_pam_map[i].ntstatus); i++) {
+ if (NT_STATUS_EQUAL(nt_status,nt_status_to_pam_map[i].ntstatus))
+ return nt_status_to_pam_map[i].pam_code;
+ }
+ return PAM_SYSTEM_ERR;
+}
+
+#else
+
+/*****************************************************************************
+convert a PAM error to a NT status32 code
+ *****************************************************************************/
+NTSTATUS pam_to_nt_status(int pam_error)
+{
+ if (pam_error == 0) return NT_STATUS_OK;
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/*****************************************************************************
+convert an NT status32 code to a PAM error
+ *****************************************************************************/
+int nt_status_to_pam(NTSTATUS nt_status)
+{
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_OK)) return 0;
+ return 4; /* PAM_SYSTEM_ERR */
+}
+
+#endif
+
diff --git a/source/lib/pidfile.c b/source/lib/pidfile.c
new file mode 100644
index 00000000000..1a462bf1287
--- /dev/null
+++ b/source/lib/pidfile.c
@@ -0,0 +1,109 @@
+/* this code is broken - there is a race condition with the unlink (tridge) */
+
+/*
+ Unix SMB/CIFS implementation.
+ pidfile handling
+ Copyright (C) Andrew Tridgell 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#ifndef O_NONBLOCK
+#define O_NONBLOCK
+#endif
+
+/* return the pid in a pidfile. return 0 if the process (or pidfile)
+ does not exist */
+pid_t pidfile_pid(const char *name)
+{
+ int fd;
+ char pidstr[20];
+ unsigned ret;
+ pstring pidFile;
+
+ slprintf(pidFile, sizeof(pidFile)-1, "%s/%s.pid", lp_piddir(), name);
+
+ fd = sys_open(pidFile, O_NONBLOCK | O_RDONLY, 0644);
+ if (fd == -1) {
+ return 0;
+ }
+
+ ZERO_ARRAY(pidstr);
+
+ if (read(fd, pidstr, sizeof(pidstr)-1) <= 0) {
+ goto noproc;
+ }
+
+ ret = atoi(pidstr);
+
+ if (!process_exists((pid_t)ret)) {
+ goto noproc;
+ }
+
+ if (fcntl_lock(fd,SMB_F_SETLK,0,1,F_RDLCK)) {
+ /* we could get the lock - it can't be a Samba process */
+ goto noproc;
+ }
+
+ close(fd);
+ return (pid_t)ret;
+
+ noproc:
+ close(fd);
+ unlink(pidFile);
+ return 0;
+}
+
+/* create a pid file in the pid directory. open it and leave it locked */
+void pidfile_create(const char *name)
+{
+ int fd;
+ char buf[20];
+ pstring pidFile;
+ pid_t pid;
+
+ slprintf(pidFile, sizeof(pidFile)-1, "%s/%s.pid", lp_piddir(), name);
+
+ pid = pidfile_pid(name);
+ if (pid != 0) {
+ DEBUG(0,("ERROR: %s is already running. File %s exists and process id %d is running.\n",
+ name, pidFile, (int)pid));
+ exit(1);
+ }
+
+ fd = sys_open(pidFile, O_NONBLOCK | O_CREAT | O_WRONLY | O_EXCL, 0644);
+ if (fd == -1) {
+ DEBUG(0,("ERROR: can't open %s: Error was %s\n", pidFile,
+ strerror(errno)));
+ exit(1);
+ }
+
+ if (fcntl_lock(fd,SMB_F_SETLK,0,1,F_WRLCK)==False) {
+ DEBUG(0,("ERROR: %s : fcntl lock of file %s failed. Error was %s\n",
+ name, pidFile, strerror(errno)));
+ exit(1);
+ }
+
+ memset(buf, 0, sizeof(buf));
+ slprintf(buf, sizeof(buf) - 1, "%u\n", (unsigned int) sys_getpid());
+ if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) {
+ DEBUG(0,("ERROR: can't write to file %s: %s\n",
+ pidFile, strerror(errno)));
+ exit(1);
+ }
+ /* Leave pid file open & locked for the duration... */
+}
diff --git a/source/lib/popt_common.c b/source/lib/popt_common.c
new file mode 100644
index 00000000000..6c35213d43a
--- /dev/null
+++ b/source/lib/popt_common.c
@@ -0,0 +1,394 @@
+/*
+ Unix SMB/CIFS implementation.
+ Common popt routines
+
+ Copyright (C) Tim Potter 2001,2002
+ Copyright (C) Jelmer Vernooij 2002,2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* Handle command line options:
+ * -d,--debuglevel
+ * -s,--configfile
+ * -O,--socket-options
+ * -V,--version
+ * -l,--log-base
+ * -n,--netbios-name
+ * -W,--workgroup
+ * -i,--scope
+ */
+
+extern pstring user_socket_options;
+extern BOOL AllowDebugChange;
+extern BOOL override_logfile;
+
+struct user_auth_info cmdline_auth_info;
+
+static void popt_common_callback(poptContext con,
+ enum poptCallbackReason reason,
+ const struct poptOption *opt,
+ const char *arg, const void *data)
+{
+ pstring logfile;
+ const char *pname;
+
+ /* Find out basename of current program */
+ pname = strrchr_m(poptGetInvocationName(con),'/');
+
+ if (!pname)
+ pname = poptGetInvocationName(con);
+ else
+ pname++;
+
+ if (reason == POPT_CALLBACK_REASON_PRE) {
+ pstr_sprintf(logfile, "%s/log.%s", dyn_LOGFILEBASE, pname);
+ lp_set_logfile(logfile);
+ return;
+ }
+
+ switch(opt->val) {
+ case 'd':
+ if (arg) {
+ debug_parse_levels(arg);
+ AllowDebugChange = False;
+ }
+ break;
+
+ case 'V':
+ printf( "Version %s\n", SAMBA_VERSION_STRING);
+ exit(0);
+ break;
+
+ case 'O':
+ if (arg) {
+ pstrcpy(user_socket_options,arg);
+ }
+ break;
+
+ case 's':
+ if (arg) {
+ pstrcpy(dyn_CONFIGFILE, arg);
+ }
+ break;
+
+ case 'n':
+ if (arg) {
+ set_global_myname(arg);
+ }
+ break;
+
+ case 'l':
+ if (arg) {
+ pstr_sprintf(logfile, "%s/log.%s", arg, pname);
+ lp_set_logfile(logfile);
+ override_logfile = True;
+ }
+ break;
+
+ case 'i':
+ if (arg) {
+ set_global_scope(arg);
+ }
+ break;
+
+ case 'W':
+ if (arg) {
+ set_global_myworkgroup(arg);
+ }
+ break;
+ }
+}
+
+struct poptOption popt_common_connection[] = {
+ { NULL, 0, POPT_ARG_CALLBACK, popt_common_callback },
+ { "socket-options", 'O', POPT_ARG_STRING, NULL, 'O', "socket options to use",
+ "SOCKETOPTIONS" },
+ { "netbiosname", 'n', POPT_ARG_STRING, NULL, 'n', "Primary netbios name", "NETBIOSNAME" },
+ { "workgroup", 'W', POPT_ARG_STRING, NULL, 'W', "Set the workgroup name", "WORKGROUP" },
+ { "scope", 'i', POPT_ARG_STRING, NULL, 'i', "Use this Netbios scope", "SCOPE" },
+
+ POPT_TABLEEND
+};
+
+struct poptOption popt_common_samba[] = {
+ { NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_PRE, popt_common_callback },
+ { "debuglevel", 'd', POPT_ARG_STRING, NULL, 'd', "Set debug level", "DEBUGLEVEL" },
+ { "configfile", 's', POPT_ARG_STRING, NULL, 's', "Use alternative configuration file", "CONFIGFILE" },
+ { "log-basename", 'l', POPT_ARG_STRING, NULL, 'l', "Basename for log/debug files", "LOGFILEBASE" },
+ { "version", 'V', POPT_ARG_NONE, NULL, 'V', "Print version" },
+ POPT_TABLEEND
+};
+
+struct poptOption popt_common_version[] = {
+ { NULL, 0, POPT_ARG_CALLBACK, popt_common_callback },
+ { "version", 'V', POPT_ARG_NONE, NULL, 'V', "Print version" },
+ POPT_TABLEEND
+};
+
+
+
+/****************************************************************************
+ * get a password from a a file or file descriptor
+ * exit on failure
+ * ****************************************************************************/
+static void get_password_file(struct user_auth_info *a)
+{
+ int fd = -1;
+ char *p;
+ BOOL close_it = False;
+ pstring spec;
+ char pass[128];
+
+ if ((p = getenv("PASSWD_FD")) != NULL) {
+ pstrcpy(spec, "descriptor ");
+ pstrcat(spec, p);
+ sscanf(p, "%d", &fd);
+ close_it = False;
+ } else if ((p = getenv("PASSWD_FILE")) != NULL) {
+ fd = sys_open(p, O_RDONLY, 0);
+ pstrcpy(spec, p);
+ if (fd < 0) {
+ fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
+ spec, strerror(errno));
+ exit(1);
+ }
+ close_it = True;
+ }
+
+ for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
+ p && p - pass < sizeof(pass);) {
+ switch (read(fd, p, 1)) {
+ case 1:
+ if (*p != '\n' && *p != '\0') {
+ *++p = '\0'; /* advance p, and null-terminate pass */
+ break;
+ }
+ case 0:
+ if (p - pass) {
+ *p = '\0'; /* null-terminate it, just in case... */
+ p = NULL; /* then force the loop condition to become false */
+ break;
+ } else {
+ fprintf(stderr, "Error reading password from file %s: %s\n",
+ spec, "empty password\n");
+ exit(1);
+ }
+
+ default:
+ fprintf(stderr, "Error reading password from file %s: %s\n",
+ spec, strerror(errno));
+ exit(1);
+ }
+ }
+ pstrcpy(a->password, pass);
+ if (close_it)
+ close(fd);
+}
+
+static void get_credentials_file(const char *file, struct user_auth_info *info)
+{
+ XFILE *auth;
+ fstring buf;
+ uint16 len = 0;
+ char *ptr, *val, *param;
+
+ if ((auth=x_fopen(file, O_RDONLY, 0)) == NULL)
+ {
+ /* fail if we can't open the credentials file */
+ d_printf("ERROR: Unable to open credentials file!\n");
+ exit(-1);
+ }
+
+ while (!x_feof(auth))
+ {
+ /* get a line from the file */
+ if (!x_fgets(buf, sizeof(buf), auth))
+ continue;
+ len = strlen(buf);
+
+ if ((len) && (buf[len-1]=='\n'))
+ {
+ buf[len-1] = '\0';
+ len--;
+ }
+ if (len == 0)
+ continue;
+
+ /* break up the line into parameter & value.
+ * will need to eat a little whitespace possibly */
+ param = buf;
+ if (!(ptr = strchr_m (buf, '=')))
+ continue;
+
+ val = ptr+1;
+ *ptr = '\0';
+
+ /* eat leading white space */
+ while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
+ val++;
+
+ if (strwicmp("password", param) == 0)
+ {
+ pstrcpy(info->password, val);
+ info->got_pass = True;
+ }
+ else if (strwicmp("username", param) == 0)
+ pstrcpy(info->username, val);
+ else if (strwicmp("domain", param) == 0)
+ set_global_myworkgroup(val);
+ memset(buf, 0, sizeof(buf));
+ }
+ x_fclose(auth);
+}
+
+/* Handle command line options:
+ * -U,--user
+ * -A,--authentication-file
+ * -k,--use-kerberos
+ * -N,--no-pass
+ * -S,--signing
+ * -P --machine-pass
+ */
+
+
+static void popt_common_credentials_callback(poptContext con,
+ enum poptCallbackReason reason,
+ const struct poptOption *opt,
+ const char *arg, const void *data)
+{
+ char *p;
+
+ if (reason == POPT_CALLBACK_REASON_PRE) {
+ cmdline_auth_info.use_kerberos = False;
+ cmdline_auth_info.got_pass = False;
+ cmdline_auth_info.signing_state = Undefined;
+ pstrcpy(cmdline_auth_info.username, "GUEST");
+
+ if (getenv("LOGNAME"))pstrcpy(cmdline_auth_info.username,getenv("LOGNAME"));
+
+ if (getenv("USER")) {
+ pstrcpy(cmdline_auth_info.username,getenv("USER"));
+
+ if ((p = strchr_m(cmdline_auth_info.username,'%'))) {
+ *p = 0;
+ pstrcpy(cmdline_auth_info.password,p+1);
+ cmdline_auth_info.got_pass = True;
+ memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(cmdline_auth_info.password));
+ }
+ }
+
+ if (getenv("PASSWD")) {
+ pstrcpy(cmdline_auth_info.password,getenv("PASSWD"));
+ cmdline_auth_info.got_pass = True;
+ }
+
+ if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
+ get_password_file(&cmdline_auth_info);
+ cmdline_auth_info.got_pass = True;
+ }
+
+ return;
+ }
+
+ switch(opt->val) {
+ case 'U':
+ {
+ char *lp;
+
+ pstrcpy(cmdline_auth_info.username,arg);
+ if ((lp=strchr_m(cmdline_auth_info.username,'%'))) {
+ *lp = 0;
+ pstrcpy(cmdline_auth_info.password,lp+1);
+ cmdline_auth_info.got_pass = True;
+ memset(strchr_m(arg,'%')+1,'X',strlen(cmdline_auth_info.password));
+ }
+ }
+ break;
+
+ case 'A':
+ get_credentials_file(arg, &cmdline_auth_info);
+ break;
+
+ case 'k':
+#ifndef HAVE_KRB5
+ d_printf("No kerberos support compiled in\n");
+ exit(1);
+#else
+ cmdline_auth_info.use_kerberos = True;
+ cmdline_auth_info.got_pass = True;
+#endif
+ break;
+
+ case 'S':
+ {
+ cmdline_auth_info.signing_state = -1;
+ if (strequal(arg, "off") || strequal(arg, "no") || strequal(arg, "false"))
+ cmdline_auth_info.signing_state = False;
+ else if (strequal(arg, "on") || strequal(arg, "yes") || strequal(arg, "true") ||
+ strequal(arg, "auto") )
+ cmdline_auth_info.signing_state = True;
+ else if (strequal(arg, "force") || strequal(arg, "required") || strequal(arg, "forced"))
+ cmdline_auth_info.signing_state = Required;
+ else {
+ fprintf(stderr, "Unknown signing option %s\n", arg );
+ exit(1);
+ }
+ }
+ break;
+ case 'P':
+ {
+ char *opt_password = NULL;
+ /* it is very useful to be able to make ads queries as the
+ machine account for testing purposes and for domain leave */
+
+ if (!secrets_init()) {
+ d_printf("ERROR: Unable to open secrets database\n");
+ exit(1);
+ }
+
+ opt_password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
+
+ if (!opt_password) {
+ d_printf("ERROR: Unable to fetch machine password\n");
+ exit(1);
+ }
+ pstr_sprintf(cmdline_auth_info.username, "%s$",
+ global_myname());
+ pstrcpy(cmdline_auth_info.password,opt_password);
+ SAFE_FREE(opt_password);
+
+ /* machine accounts only work with kerberos */
+ cmdline_auth_info.use_kerberos = True;
+ cmdline_auth_info.got_pass = True;
+ }
+ break;
+ }
+}
+
+
+
+struct poptOption popt_common_credentials[] = {
+ { NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_PRE, popt_common_credentials_callback },
+ { "user", 'U', POPT_ARG_STRING, NULL, 'U', "Set the network username", "USERNAME" },
+ { "no-pass", 'N', POPT_ARG_NONE, &cmdline_auth_info.got_pass, 0, "Don't ask for a password" },
+ { "kerberos", 'k', POPT_ARG_NONE, &cmdline_auth_info.use_kerberos, 'k', "Use kerberos (active directory) authentication" },
+ { "authentication-file", 'A', POPT_ARG_STRING, NULL, 'A', "Get the credentials from a file", "FILE" },
+ { "signing", 'S', POPT_ARG_STRING, NULL, 'S', "Set the client signing state", "on|off|required" },
+ {"machine-pass", 'P', POPT_ARG_NONE, NULL, 'P', "Use stored machine account password" },
+ POPT_TABLEEND
+};
diff --git a/source/lib/privileges.c b/source/lib/privileges.c
new file mode 100644
index 00000000000..abbaf112d34
--- /dev/null
+++ b/source/lib/privileges.c
@@ -0,0 +1,442 @@
+/*
+ Unix SMB/CIFS implementation.
+ Privileges handling functions
+ Copyright (C) Jean François Micouleau 1998-2001
+ Copyright (C) Simo Sorce 2002-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* defines */
+
+#define ALLOC_CHECK(ptr, err, label, str) do { if ((ptr) == NULL) { DEBUG(0, ("%s: out of memory!\n", str)); err = NT_STATUS_NO_MEMORY; goto label; } } while(0)
+#define NTSTATUS_CHECK(err, label, str1, str2) do { if (!NT_STATUS_IS_OK(err)) { DEBUG(0, ("%s: %s failed!\n", str1, str2)); } } while(0)
+
+
+PRIVS privs[] = {
+ {SE_NONE, "no_privs", "No privilege"}, /* this one MUST be first */
+ {SE_CREATE_TOKEN, "SeCreateTokenPrivilege", "Create Token"},
+ {SE_ASSIGN_PRIMARY_TOKEN, "SeAssignPrimaryTokenPrivilege", "Assign Primary Token"},
+ {SE_LOCK_MEMORY, "SeLockMemoryPrivilege", "Lock Memory"},
+ {SE_INCREASE_QUOTA, "SeIncreaseQuotaPrivilege", "Increase Quota"},
+ {SE_UNSOLICITED_INPUT, "SeUnsolicitedInputPrivilege", "Unsolicited Input"},
+ {SE_MACHINE_ACCOUNT, "SeMachineAccountPrivilege", "Can add Machine Accounts to the Domain"},
+ {SE_TCB, "SeTcbPrivilege", "TCB"},
+ {SE_SECURITY, "SeSecurityPrivilege", "Security Privilege"},
+ {SE_TAKE_OWNERSHIP, "SeTakeOwnershipPrivilege", "Take Ownership Privilege"},
+ {SE_LOAD_DRIVER, "SeLocalDriverPrivilege", "Local Driver Privilege"},
+ {SE_SYSTEM_PROFILE, "SeSystemProfilePrivilege", "System Profile Privilege"},
+ {SE_SYSTEM_TIME, "SeSystemtimePrivilege", "System Time"},
+ {SE_PROF_SINGLE_PROCESS, "SeProfileSingleProcessPrivilege", "Profile Single Process Privilege"},
+ {SE_INC_BASE_PRIORITY, "SeIncreaseBasePriorityPrivilege", "Increase Base Priority Privilege"},
+ {SE_CREATE_PAGEFILE, "SeCreatePagefilePrivilege", "Create Pagefile Privilege"},
+ {SE_CREATE_PERMANENT, "SeCreatePermanentPrivilege", "Create Permanent"},
+ {SE_BACKUP, "SeBackupPrivilege", "Backup Privilege"},
+ {SE_RESTORE, "SeRestorePrivilege", "Restore Privilege"},
+ {SE_SHUTDOWN, "SeShutdownPrivilege", "Shutdown Privilege"},
+ {SE_DEBUG, "SeDebugPrivilege", "Debug Privilege"},
+ {SE_AUDIT, "SeAuditPrivilege", "Audit"},
+ {SE_SYSTEM_ENVIRONMENT, "SeSystemEnvironmentPrivilege", "System Environment Privilege"},
+ {SE_CHANGE_NOTIFY, "SeChangeNotifyPrivilege", "Change Notify"},
+ {SE_REMOTE_SHUTDOWN, "SeRemoteShutdownPrivilege", "Remote Shutdown Privilege"},
+ {SE_UNDOCK, "SeUndockPrivilege", "Undock"},
+ {SE_SYNC_AGENT, "SeSynchronizationAgentPrivilege", "Synchronization Agent"},
+ {SE_ENABLE_DELEGATION, "SeEnableDelegationPrivilege", "Enable Delegation"},
+ {SE_PRINT_OPERATOR, "SePrintOperatorPrivilege", "Printer Operator"},
+ {SE_ADD_USERS, "SeAddUsersPrivilege", "Add Users"},
+ {SE_ALL_PRIVS, "SeAllPrivileges", "All Privileges"}
+};
+
+
+
+/****************************************************************************
+ Check if a user is a mapped group.
+
+ This function will check if the group SID is mapped onto a
+ system managed gid or onto a winbind manged sid.
+ In the first case it will be threated like a mapped group
+ and the backend should take the member list with a getgrgid
+ and ignore any user that have been possibly set into the group
+ object.
+
+ In the second case, the group is a fully SAM managed group
+ served back to the system through winbind. In this case the
+ members of a Local group are "unrolled" to cope with the fact
+ that unix cannot contain groups inside groups.
+ The backend MUST never call any getgr* / getpw* function or
+ loops with winbind may happen.
+ ****************************************************************************/
+
+#if 0
+NTSTATUS is_mapped_group(BOOL *mapped, const DOM_SID *sid)
+{
+ NTSTATUS result;
+ gid_t id;
+
+ /* look if mapping exist, do not make idmap alloc an uid if SID is not found */
+ result = idmap_get_gid_from_sid(&id, sid, False);
+ if (NT_STATUS_IS_OK(result)) {
+ *mapped = gid_is_in_winbind_range(id);
+ } else {
+ *mapped = False;
+ }
+
+ return result;
+}
+#endif
+
+/****************************************************************************
+ duplicate alloc luid_attr
+ ****************************************************************************/
+NTSTATUS dupalloc_luid_attr(TALLOC_CTX *mem_ctx, LUID_ATTR **new_la, LUID_ATTR *old_la, int count)
+{
+ NTSTATUS ret;
+ int i;
+
+ /* don't crash if the source pointer is NULL (since we don't
+ do priviledges now anyways) */
+
+ if ( !old_la )
+ return NT_STATUS_OK;
+
+ *new_la = (LUID_ATTR *)talloc(mem_ctx, count*sizeof(LUID_ATTR));
+ ALLOC_CHECK(new_la, ret, done, "dupalloc_luid_attr");
+
+ for (i=0; i<count; i++) {
+ (*new_la)[i].luid.high = old_la[i].luid.high;
+ (*new_la)[i].luid.low = old_la[i].luid.low;
+ (*new_la)[i].attr = old_la[i].attr;
+ }
+
+ ret = NT_STATUS_OK;
+
+done:
+ return ret;
+}
+
+/****************************************************************************
+ initialise a privilege list
+ ****************************************************************************/
+NTSTATUS init_privilege(PRIVILEGE_SET **priv_set)
+{
+ NTSTATUS ret;
+ TALLOC_CTX *mem_ctx = talloc_init("privilege set");
+ ALLOC_CHECK(mem_ctx, ret, done, "init_privilege");
+
+ *priv_set = talloc_zero(mem_ctx, sizeof(PRIVILEGE_SET));
+ ALLOC_CHECK(*priv_set, ret, done, "init_privilege");
+
+ (*priv_set)->mem_ctx = mem_ctx;
+
+ ret = NT_STATUS_OK;
+
+done:
+ return ret;
+}
+
+NTSTATUS init_priv_with_ctx(TALLOC_CTX *mem_ctx, PRIVILEGE_SET **priv_set)
+{
+ NTSTATUS ret;
+
+ *priv_set = talloc_zero(mem_ctx, sizeof(PRIVILEGE_SET));
+ ALLOC_CHECK(*priv_set, ret, done, "init_privilege");
+
+ (*priv_set)->mem_ctx = mem_ctx;
+ (*priv_set)->ext_ctx = True;
+
+ ret = NT_STATUS_OK;
+
+done:
+ return ret;
+}
+
+void reset_privilege(PRIVILEGE_SET *priv_set)
+{
+ priv_set->count = 0;
+ priv_set->control = 0;
+ priv_set->set = NULL;
+}
+
+void destroy_privilege(PRIVILEGE_SET **priv_set)
+{
+ if (priv_set == NULL || *priv_set == NULL)
+ return;
+
+ reset_privilege(*priv_set);
+ if (!((*priv_set)->ext_ctx))
+ /* mem_ctx is local, destroy it */
+ talloc_destroy((*priv_set)->mem_ctx);
+ *priv_set = NULL;
+}
+
+/****************************************************************************
+ add a privilege to a privilege array
+ ****************************************************************************/
+NTSTATUS add_privilege(PRIVILEGE_SET *priv_set, LUID_ATTR set)
+{
+ NTSTATUS ret;
+ LUID_ATTR *new_set;
+
+ /* check if the privilege is not already in the list */
+ if (NT_STATUS_IS_OK(check_priv_in_privilege(priv_set, set)))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ /* we can allocate memory to add the new privilege */
+
+ new_set = (LUID_ATTR *)talloc_realloc(priv_set->mem_ctx, priv_set->set, (priv_set->count + 1) * (sizeof(LUID_ATTR)));
+ ALLOC_CHECK(new_set, ret, done, "add_privilege");
+
+ new_set[priv_set->count].luid.high = set.luid.high;
+ new_set[priv_set->count].luid.low = set.luid.low;
+ new_set[priv_set->count].attr = set.attr;
+
+ priv_set->count++;
+ priv_set->set = new_set;
+
+ ret = NT_STATUS_OK;
+
+done:
+ return ret;
+}
+
+NTSTATUS add_privilege_by_name(PRIVILEGE_SET *priv_set, const char *name)
+{
+ int e;
+
+ for (e = 0; privs[e].se_priv != SE_ALL_PRIVS; e++) {
+ if (StrCaseCmp(privs[e].priv, name) == 0) {
+ LUID_ATTR la;
+
+ la.attr = 0;
+ la.luid.high = 0;
+ la.luid.low = privs[e].se_priv;
+
+ return add_privilege(priv_set, la);
+ }
+ }
+
+ DEBUG(1, ("add_privilege_by_name: No Such Privilege Found (%s)\n", name));
+
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/****************************************************************************
+ add all the privileges to a privilege array
+ ****************************************************************************/
+NTSTATUS add_all_privilege(PRIVILEGE_SET *priv_set)
+{
+ NTSTATUS result = NT_STATUS_OK;
+ LUID_ATTR set;
+
+ set.attr = 0;
+ set.luid.high = 0;
+
+ /* TODO: set a proper list of privileges */
+ set.luid.low = SE_ADD_USERS;
+ result = add_privilege(priv_set, set);
+ NTSTATUS_CHECK(result, done, "add_all_privilege", "add_privilege");
+
+ set.luid.low = SE_MACHINE_ACCOUNT;
+ result = add_privilege(priv_set, set);
+ NTSTATUS_CHECK(result, done, "add_all_privilege", "add_privilege");
+
+ set.luid.low = SE_PRINT_OPERATOR;
+ result = add_privilege(priv_set, set);
+ NTSTATUS_CHECK(result, done, "add_all_privilege", "add_privilege");
+
+ return result;
+}
+
+/****************************************************************************
+ check if the privilege list is empty
+ ****************************************************************************/
+NTSTATUS check_empty_privilege(PRIVILEGE_SET *priv_set)
+{
+ if (!priv_set)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (priv_set->count == 0)
+ return NT_STATUS_OK;
+
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/****************************************************************************
+ check if the privilege is in the privilege list
+ ****************************************************************************/
+NTSTATUS check_priv_in_privilege(PRIVILEGE_SET *priv_set, LUID_ATTR set)
+{
+ int i;
+
+ if (!priv_set)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ /* if the list is empty, obviously we can't have it */
+ if (NT_STATUS_IS_OK(check_empty_privilege(priv_set)))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ for (i = 0; i < priv_set->count; i++) {
+ LUID_ATTR *cur_set;
+
+ cur_set = &priv_set->set[i];
+ /* check only the low and high part. Checking the attr field has no meaning */
+ if ( (cur_set->luid.low == set.luid.low) &&
+ (cur_set->luid.high == set.luid.high) ) {
+ return NT_STATUS_OK;
+ }
+ }
+
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/****************************************************************************
+ remove a privilege from a privilege array
+ ****************************************************************************/
+NTSTATUS remove_privilege(PRIVILEGE_SET *priv_set, LUID_ATTR set)
+{
+ NTSTATUS ret;
+ LUID_ATTR *new_set;
+ LUID_ATTR *old_set;
+ int i,j;
+
+ if (!priv_set)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ /* check if the privilege is in the list */
+ if (!NT_STATUS_IS_OK(check_priv_in_privilege(priv_set, set)))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ /* special case if it's the only privilege in the list */
+ if (priv_set->count == 1) {
+ reset_privilege(priv_set);
+ return NT_STATUS_OK;
+ }
+
+ /*
+ * the privilege is there, create a new list,
+ * and copy the other privileges
+ */
+
+ old_set = priv_set->set;
+
+ new_set = (LUID_ATTR *)talloc(priv_set->mem_ctx, (priv_set->count - 1) * (sizeof(LUID_ATTR)));
+ ALLOC_CHECK(new_set, ret, done, "remove_privilege");
+
+ for (i=0, j=0; i < priv_set->count; i++) {
+ if ( (old_set[i].luid.low == set.luid.low) &&
+ (old_set[i].luid.high == set.luid.high) ) {
+ continue;
+ }
+
+ new_set[j].luid.low = old_set[i].luid.low;
+ new_set[j].luid.high = old_set[i].luid.high;
+ new_set[j].attr = old_set[i].attr;
+
+ j++;
+ }
+
+ if (j != priv_set->count - 1) {
+ DEBUG(0,("remove_privilege: mismatch ! difference is not -1\n"));
+ DEBUGADD(0,("old count:%d, new count:%d\n", priv_set->count, j));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ /* ok everything is fine */
+
+ priv_set->count--;
+ priv_set->set = new_set;
+
+ ret = NT_STATUS_OK;
+
+done:
+ return ret;
+}
+
+/****************************************************************************
+ duplicates a privilege array
+ the new privilege set must be passed inited
+ (use init_privilege or init_priv_with_ctx)
+ ****************************************************************************/
+NTSTATUS dup_priv_set(PRIVILEGE_SET *new_priv_set, PRIVILEGE_SET *priv_set)
+{
+ NTSTATUS ret;
+ LUID_ATTR *new_set;
+ LUID_ATTR *old_set;
+ int i;
+
+ if (new_priv_set == NULL || priv_set == NULL)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ /* special case if there are no privileges in the list */
+ if (priv_set->count == 0) {
+ return NT_STATUS_OK;
+ }
+
+ /*
+ * create a new list,
+ * and copy the other privileges
+ */
+
+ old_set = priv_set->set;
+
+ new_set = (LUID_ATTR *)talloc(new_priv_set->mem_ctx, (priv_set->count) * (sizeof(LUID_ATTR)));
+ ALLOC_CHECK(new_set, ret, done, "dup_priv_set");
+
+ for (i=0; i < priv_set->count; i++) {
+
+ new_set[i].luid.low = old_set[i].luid.low;
+ new_set[i].luid.high = old_set[i].luid.high;
+ new_set[i].attr = old_set[i].attr;
+ }
+
+ new_priv_set->count = priv_set->count;
+ new_priv_set->control = priv_set->control;
+ new_priv_set->set = new_set;
+
+ ret = NT_STATUS_OK;
+
+done:
+ return ret;
+}
+
+
+NTSTATUS user_has_privilege(struct current_user *user, uint32 privilege)
+{
+ LUID_ATTR set;
+
+ set.attr = 0;
+ set.luid.high = 0;
+ set.luid.low = privilege;
+
+ return check_priv_in_privilege(user->privs, set);
+}
+
+BOOL luid_to_privilege_name(const LUID *set, fstring name)
+{
+ int i;
+
+ if (set->high != 0)
+ return False;
+
+ for (i=1; i<PRIV_ALL_INDEX-1; i++) {
+ if (set->low == privs[i].se_priv) {
+ fstrcpy(name, privs[i].priv);
+ return True;
+ }
+ }
+ return False;
+}
diff --git a/source/lib/readline.c b/source/lib/readline.c
new file mode 100644
index 00000000000..78b99fd7fb0
--- /dev/null
+++ b/source/lib/readline.c
@@ -0,0 +1,161 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba readline wrapper implementation
+ Copyright (C) Simo Sorce 2001
+ Copyright (C) Andrew Tridgell 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#ifdef HAVE_LIBREADLINE
+# ifdef HAVE_READLINE_READLINE_H
+# include <readline/readline.h>
+# ifdef HAVE_READLINE_HISTORY_H
+# include <readline/history.h>
+# endif
+# else
+# ifdef HAVE_READLINE_H
+# include <readline.h>
+# ifdef HAVE_HISTORY_H
+# include <history.h>
+# endif
+# else
+# undef HAVE_LIBREADLINE
+# endif
+# endif
+#endif
+
+#ifdef HAVE_NEW_LIBREADLINE
+# define RL_COMPLETION_CAST (rl_completion_func_t *)
+#else
+/* This type is missing from libreadline<4.0 (approximately) */
+# define RL_COMPLETION_CAST
+#endif /* HAVE_NEW_LIBREADLINE */
+
+/****************************************************************************
+ Display the prompt and wait for input. Call callback() regularly
+****************************************************************************/
+
+static char *smb_readline_replacement(char *prompt, void (*callback)(void),
+ char **(completion_fn)(const char *text, int start, int end))
+{
+ fd_set fds;
+ static pstring line;
+ struct timeval timeout;
+ int fd = x_fileno(x_stdin);
+ char *ret;
+
+ x_fprintf(dbf, "%s", prompt);
+ x_fflush(dbf);
+
+ while (1) {
+ timeout.tv_sec = 5;
+ timeout.tv_usec = 0;
+
+ FD_ZERO(&fds);
+ FD_SET(fd,&fds);
+
+ if (sys_select_intr(fd+1,&fds,NULL,NULL,&timeout) == 1) {
+ ret = x_fgets(line, sizeof(line), x_stdin);
+ return ret;
+ }
+ if (callback)
+ callback();
+ }
+}
+
+/****************************************************************************
+ Display the prompt and wait for input. Call callback() regularly.
+****************************************************************************/
+
+char *smb_readline(char *prompt, void (*callback)(void),
+ char **(completion_fn)(const char *text, int start, int end))
+{
+#if HAVE_LIBREADLINE
+ if (isatty(x_fileno(x_stdin))) {
+ char *ret;
+
+ /* Aargh! Readline does bizzare things with the terminal width
+ that mucks up expect(1). Set CLI_NO_READLINE in the environment
+ to force readline not to be used. */
+
+ if (getenv("CLI_NO_READLINE"))
+ return smb_readline_replacement(prompt, callback, completion_fn);
+
+ if (completion_fn) {
+ /* The callback prototype has changed slightly between
+ different versions of Readline, so the same function
+ works in all of them to date, but we get compiler
+ warnings in some. */
+ rl_attempted_completion_function = RL_COMPLETION_CAST completion_fn;
+ }
+
+ if (callback)
+ rl_event_hook = (Function *)callback;
+ ret = readline(prompt);
+ if (ret && *ret)
+ add_history(ret);
+ return ret;
+ } else
+#endif
+ return smb_readline_replacement(prompt, callback, completion_fn);
+}
+
+/****************************************************************************
+ * return line buffer text
+ ****************************************************************************/
+const char *smb_readline_get_line_buffer(void)
+{
+#if defined(HAVE_LIBREADLINE)
+ return rl_line_buffer;
+#else
+ return NULL;
+#endif
+}
+
+
+/****************************************************************************
+ * set completion append character
+ ***************************************************************************/
+void smb_readline_ca_char(char c)
+{
+#if defined(HAVE_LIBREADLINE)
+ rl_completion_append_character = c;
+#endif
+}
+
+/****************************************************************************
+history
+****************************************************************************/
+int cmd_history(void)
+{
+#if defined(HAVE_LIBREADLINE)
+ HIST_ENTRY **hlist;
+ int i;
+
+ hlist = history_list();
+
+ for (i = 0; hlist && hlist[i]; i++) {
+ DEBUG(0, ("%d: %s\n", i, hlist[i]->line));
+ }
+#else
+ DEBUG(0,("no history without readline support\n"));
+#endif
+
+ return 0;
+}
+
diff --git a/source/lib/replace.c b/source/lib/replace.c
new file mode 100644
index 00000000000..fe1cfc04eb1
--- /dev/null
+++ b/source/lib/replace.c
@@ -0,0 +1,452 @@
+/*
+ Unix SMB/CIFS implementation.
+ replacement routines for broken systems
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+ void replace_dummy(void);
+ void replace_dummy(void) {}
+
+#ifndef HAVE_FTRUNCATE
+ /*******************************************************************
+ftruncate for operating systems that don't have it
+********************************************************************/
+ int ftruncate(int f,SMB_OFF_T l)
+{
+ struct flock fl;
+
+ fl.l_whence = 0;
+ fl.l_len = 0;
+ fl.l_start = l;
+ fl.l_type = F_WRLCK;
+ return fcntl(f, F_FREESP, &fl);
+}
+#endif /* HAVE_FTRUNCATE */
+
+
+#ifndef HAVE_STRLCPY
+/* like strncpy but does not 0 fill the buffer and always null
+ terminates. bufsize is the size of the destination buffer */
+ size_t strlcpy(char *d, const char *s, size_t bufsize)
+{
+ size_t len = strlen(s);
+ size_t ret = len;
+ if (bufsize <= 0) return 0;
+ if (len >= bufsize) len = bufsize-1;
+ memcpy(d, s, len);
+ d[len] = 0;
+ return ret;
+}
+#endif
+
+#ifndef HAVE_STRLCAT
+/* like strncat but does not 0 fill the buffer and always null
+ terminates. bufsize is the length of the buffer, which should
+ be one more than the maximum resulting string length */
+ size_t strlcat(char *d, const char *s, size_t bufsize)
+{
+ size_t len1 = strlen(d);
+ size_t len2 = strlen(s);
+ size_t ret = len1 + len2;
+
+ if (len1 >= bufsize) {
+ return 0;
+ }
+ if (len1+len2 >= bufsize) {
+ len2 = bufsize - (len1+1);
+ }
+ if (len2 > 0) {
+ memcpy(d+len1, s, len2);
+ d[len1+len2] = 0;
+ }
+ return ret;
+}
+#endif
+
+#ifndef HAVE_MKTIME
+/*******************************************************************
+a mktime() replacement for those who don't have it - contributed by
+C.A. Lademann <cal@zls.com>
+Corrections by richard.kettlewell@kewill.com
+********************************************************************/
+
+#define MINUTE 60
+#define HOUR 60*MINUTE
+#define DAY 24*HOUR
+#define YEAR 365*DAY
+ time_t mktime(struct tm *t)
+{
+ struct tm *u;
+ time_t epoch = 0;
+ int n;
+ int mon [] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ y, m, i;
+
+ if(t->tm_year < 70)
+ return((time_t)-1);
+
+ n = t->tm_year + 1900 - 1;
+ epoch = (t->tm_year - 70) * YEAR +
+ ((n / 4 - n / 100 + n / 400) - (1969 / 4 - 1969 / 100 + 1969 / 400)) * DAY;
+
+ y = t->tm_year + 1900;
+ m = 0;
+
+ for(i = 0; i < t->tm_mon; i++) {
+ epoch += mon [m] * DAY;
+ if(m == 1 && y % 4 == 0 && (y % 100 != 0 || y % 400 == 0))
+ epoch += DAY;
+
+ if(++m > 11) {
+ m = 0;
+ y++;
+ }
+ }
+
+ epoch += (t->tm_mday - 1) * DAY;
+ epoch += t->tm_hour * HOUR + t->tm_min * MINUTE + t->tm_sec;
+
+ if((u = localtime(&epoch)) != NULL) {
+ t->tm_sec = u->tm_sec;
+ t->tm_min = u->tm_min;
+ t->tm_hour = u->tm_hour;
+ t->tm_mday = u->tm_mday;
+ t->tm_mon = u->tm_mon;
+ t->tm_year = u->tm_year;
+ t->tm_wday = u->tm_wday;
+ t->tm_yday = u->tm_yday;
+ t->tm_isdst = u->tm_isdst;
+ }
+
+ return(epoch);
+}
+#endif /* !HAVE_MKTIME */
+
+
+
+#ifndef HAVE_RENAME
+/* Rename a file. (from libiberty in GNU binutils) */
+ int rename(const char *zfrom, const char *zto)
+{
+ if (link (zfrom, zto) < 0)
+ {
+ if (errno != EEXIST)
+ return -1;
+ if (unlink (zto) < 0
+ || link (zfrom, zto) < 0)
+ return -1;
+ }
+ return unlink (zfrom);
+}
+#endif /* HAVE_RENAME */
+
+
+#ifndef HAVE_INNETGR
+#if defined(HAVE_SETNETGRENT) && defined(HAVE_GETNETGRENT) && defined(HAVE_ENDNETGRENT)
+/*
+ * Search for a match in a netgroup. This replaces it on broken systems.
+ */
+ int innetgr(const char *group,const char *host,const char *user,const char *dom)
+{
+ char *hst, *usr, *dm;
+
+ setnetgrent(group);
+ while (getnetgrent(&hst, &usr, &dm)) {
+ if (((host == 0) || (hst == 0) || !strcmp(host, hst)) &&
+ ((user == 0) || (usr == 0) || !strcmp(user, usr)) &&
+ ((dom == 0) || (dm == 0) || !strcmp(dom, dm))) {
+ endnetgrent();
+ return (1);
+ }
+ }
+ endnetgrent();
+ return (0);
+}
+#endif /* HAVE_SETNETGRENT HAVE_GETNETGRENT HAVE_ENDNETGRENT */
+#endif /* HAVE_INNETGR */
+
+
+
+#ifndef HAVE_INITGROUPS
+/****************************************************************************
+ some systems don't have an initgroups call
+****************************************************************************/
+ int initgroups(char *name,gid_t id)
+{
+#ifndef HAVE_SETGROUPS
+ static int done;
+ if (!done) {
+ DEBUG(1,("WARNING: running without setgroups\n"));
+ done=1;
+ }
+ /* yikes! no SETGROUPS or INITGROUPS? how can this work? */
+ return(0);
+#else /* HAVE_SETGROUPS */
+ gid_t *grouplst = NULL;
+ int max_gr = groups_max();
+ int ret;
+ int i,j;
+ struct group *g;
+ char *gr;
+
+ if((grouplst = (gid_t *)malloc(sizeof(gid_t) * max_gr)) == NULL) {
+ DEBUG(0,("initgroups: malloc fail !\n"));
+ return -1;
+ }
+
+ grouplst[0] = id;
+ i = 1;
+ while (i < max_gr && ((g = (struct group *)getgrent()) != (struct group *)NULL)) {
+ if (g->gr_gid == id)
+ continue;
+ j = 0;
+ gr = g->gr_mem[0];
+ while (gr && (*gr != (char)NULL)) {
+ if (strcmp(name,gr) == 0) {
+ grouplst[i] = g->gr_gid;
+ i++;
+ gr = (char *)NULL;
+ break;
+ }
+ gr = g->gr_mem[++j];
+ }
+ }
+ endgrent();
+ ret = sys_setgroups(i,grouplst);
+ SAFE_FREE(grouplst);
+ return ret;
+#endif /* HAVE_SETGROUPS */
+}
+#endif /* HAVE_INITGROUPS */
+
+
+#if (defined(SecureWare) && defined(SCO))
+/* This is needed due to needing the nap() function but we don't want
+ to include the Xenix libraries since that will break other things...
+ BTW: system call # 0x0c28 is the same as calling nap() */
+ long nap(long milliseconds) {
+ return syscall(0x0c28, milliseconds);
+ }
+#endif
+
+
+#ifndef HAVE_MEMMOVE
+/*******************************************************************
+safely copies memory, ensuring no overlap problems.
+this is only used if the machine does not have it's own memmove().
+this is not the fastest algorithm in town, but it will do for our
+needs.
+********************************************************************/
+ void *memmove(void *dest,const void *src,int size)
+{
+ unsigned long d,s;
+ int i;
+ if (dest==src || !size) return(dest);
+
+ d = (unsigned long)dest;
+ s = (unsigned long)src;
+
+ if ((d >= (s+size)) || (s >= (d+size))) {
+ /* no overlap */
+ memcpy(dest,src,size);
+ return(dest);
+ }
+
+ if (d < s) {
+ /* we can forward copy */
+ if (s-d >= sizeof(int) &&
+ !(s%sizeof(int)) &&
+ !(d%sizeof(int)) &&
+ !(size%sizeof(int))) {
+ /* do it all as words */
+ int *idest = (int *)dest;
+ int *isrc = (int *)src;
+ size /= sizeof(int);
+ for (i=0;i<size;i++) idest[i] = isrc[i];
+ } else {
+ /* simplest */
+ char *cdest = (char *)dest;
+ char *csrc = (char *)src;
+ for (i=0;i<size;i++) cdest[i] = csrc[i];
+ }
+ } else {
+ /* must backward copy */
+ if (d-s >= sizeof(int) &&
+ !(s%sizeof(int)) &&
+ !(d%sizeof(int)) &&
+ !(size%sizeof(int))) {
+ /* do it all as words */
+ int *idest = (int *)dest;
+ int *isrc = (int *)src;
+ size /= sizeof(int);
+ for (i=size-1;i>=0;i--) idest[i] = isrc[i];
+ } else {
+ /* simplest */
+ char *cdest = (char *)dest;
+ char *csrc = (char *)src;
+ for (i=size-1;i>=0;i--) cdest[i] = csrc[i];
+ }
+ }
+ return(dest);
+}
+#endif /* HAVE_MEMMOVE */
+
+#ifndef HAVE_STRDUP
+/****************************************************************************
+duplicate a string
+****************************************************************************/
+ char *strdup(const char *s)
+{
+ size_t len;
+ char *ret;
+
+ if (!s) return(NULL);
+
+ len = strlen(s)+1;
+ ret = (char *)malloc(len);
+ if (!ret) return(NULL);
+ memcpy(ret,s,len);
+ return(ret);
+}
+#endif /* HAVE_STRDUP */
+
+#ifdef REPLACE_INET_NTOA
+char *rep_inet_ntoa(struct in_addr ip)
+{
+ unsigned char *p = (unsigned char *)&ip.s_addr;
+ static char buf[18];
+ slprintf(buf, 17, "%d.%d.%d.%d",
+ (int)p[0], (int)p[1], (int)p[2], (int)p[3]);
+ return buf;
+}
+#endif /* REPLACE_INET_NTOA */
+
+#ifndef HAVE_STRTOUL
+#ifndef ULONG_MAX
+#define ULONG_MAX ((unsigned long)(~0L)) /* 0xFFFFFFFF */
+#endif
+
+/*
+ * Convert a string to an unsigned long integer.
+ * Taken from libg++ - libiberty code.
+ *
+ * Ignores `locale' stuff. Assumes that the upper and lower case
+ * alphabets and digits are each contiguous.
+ */
+ unsigned long strtoul(const char *nptr, char **endptr, int base)
+{
+ const char *s = nptr;
+ unsigned long acc;
+ int c;
+ unsigned long cutoff;
+ int neg = 0, any, cutlim;
+
+ /*
+ * See strtol for comments as to the logic used.
+ */
+ do {
+ c = *s++;
+ } while (isspace(c));
+ if (c == '-') {
+ neg = 1;
+ c = *s++;
+ } else if (c == '+')
+ c = *s++;
+ if ((base == 0 || base == 16) &&
+ c == '0' && (*s == 'x' || *s == 'X')) {
+ c = s[1];
+ s += 2;
+ base = 16;
+ }
+ if (base == 0)
+ base = c == '0' ? 8 : 10;
+ cutoff = (unsigned long)ULONG_MAX / (unsigned long)base;
+ cutlim = (int)((unsigned long)ULONG_MAX % (unsigned long)base);
+ for (acc = 0, any = 0;; c = *s++) {
+ if (isdigit(c))
+ c -= '0';
+ else if (isalpha(c))
+ c -= isupper(c) ? 'A' - 10 : 'a' - 10;
+ else
+ break;
+ if (c >= base)
+ break;
+ if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim)
+ any = -1;
+ else {
+ any = 1;
+ acc *= base;
+ acc += c;
+ }
+ }
+ if (any < 0) {
+ acc = ULONG_MAX;
+ errno = ERANGE;
+ } else if (neg)
+ acc = -acc;
+ if (endptr != 0)
+ *endptr = (char *) (any ? s - 1 : nptr);
+ return (acc);
+}
+#endif /* HAVE_STRTOUL */
+
+#ifndef HAVE_SETLINEBUF
+ int setlinebuf(FILE *stream)
+{
+ return setvbuf(stream, (char *)NULL, _IOLBF, 0);
+}
+#endif /* HAVE_SETLINEBUF */
+
+#ifndef HAVE_VSYSLOG
+#ifdef HAVE_SYSLOG
+ void vsyslog (int facility_priority, char *format, va_list arglist)
+{
+ char *msg = NULL;
+ vasprintf(&msg, format, arglist);
+ if (!msg)
+ return;
+ syslog(facility_priority, "%s", msg);
+ SAFE_FREE(msg);
+}
+#endif /* HAVE_SYSLOG */
+#endif /* HAVE_VSYSLOG */
+
+
+#ifndef HAVE_TIMEGM
+/*
+ yes, I know this looks insane, but its really needed. The function in the
+ Linux timegm() manpage does not work on solaris.
+*/
+ time_t timegm(struct tm *tm)
+{
+ struct tm tm2, tm3;
+ time_t t;
+
+ tm2 = *tm;
+
+ t = mktime(&tm2);
+ tm3 = *localtime(&t);
+ tm2 = *tm;
+ tm2.tm_isdst = tm3.tm_isdst;
+ t = mktime(&tm2);
+ t -= TimeDiff(t);
+
+ return t;
+}
+#endif
diff --git a/source/lib/replace1.c b/source/lib/replace1.c
new file mode 100644
index 00000000000..e1be56eb128
--- /dev/null
+++ b/source/lib/replace1.c
@@ -0,0 +1,42 @@
+/*
+ Unix SMB/CIFS implementation.
+ replacement routines for broken systems
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+ void replace1_dummy(void);
+ void replace1_dummy(void) {}
+
+#ifndef HAVE_SETENV
+ int setenv(const char *name, const char *value, int overwrite)
+{
+ char *p = NULL;
+ int ret = -1;
+
+ asprintf(&p, "%s=%s", name, value);
+
+ if (overwrite || getenv(name)) {
+ if (p) ret = putenv(p);
+ } else {
+ ret = 0;
+ }
+
+ return ret;
+}
+#endif
diff --git a/source/lib/secace.c b/source/lib/secace.c
new file mode 100644
index 00000000000..8c54c970433
--- /dev/null
+++ b/source/lib/secace.c
@@ -0,0 +1,285 @@
+/*
+ * Unix SMB/Netbios implementation.
+ * SEC_ACE handling functions
+ * Copyright (C) Andrew Tridgell 1992-1998,
+ * Copyright (C) Jeremy R. Allison 1995-2003.
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ * Copyright (C) Paul Ashton 1997-1998.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+/*******************************************************************
+ Check if ACE has OBJECT type.
+********************************************************************/
+
+BOOL sec_ace_object(uint8 type)
+{
+ if (type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
+ type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT ||
+ type == SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT ||
+ type == SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT) {
+ return True;
+ }
+ return False;
+}
+
+/*******************************************************************
+ copy a SEC_ACE structure.
+********************************************************************/
+void sec_ace_copy(SEC_ACE *ace_dest, SEC_ACE *ace_src)
+{
+ ace_dest->type = ace_src->type;
+ ace_dest->flags = ace_src->flags;
+ ace_dest->size = ace_src->size;
+ ace_dest->info.mask = ace_src->info.mask;
+ ace_dest->obj_flags = ace_src->obj_flags;
+ memcpy(&ace_dest->obj_guid, &ace_src->obj_guid, sizeof(struct uuid));
+ memcpy(&ace_dest->inh_guid, &ace_src->inh_guid, sizeof(struct uuid));
+ sid_copy(&ace_dest->trustee, &ace_src->trustee);
+}
+
+/*******************************************************************
+ Sets up a SEC_ACE structure.
+********************************************************************/
+
+void init_sec_ace(SEC_ACE *t, DOM_SID *sid, uint8 type, SEC_ACCESS mask, uint8 flag)
+{
+ t->type = type;
+ t->flags = flag;
+ t->size = sid_size(sid) + 8;
+ t->info = mask;
+
+ ZERO_STRUCTP(&t->trustee);
+ sid_copy(&t->trustee, sid);
+}
+
+/*******************************************************************
+ adds new SID with its permissions to ACE list
+********************************************************************/
+
+NTSTATUS sec_ace_add_sid(TALLOC_CTX *ctx, SEC_ACE **new, SEC_ACE *old, unsigned *num, DOM_SID *sid, uint32 mask)
+{
+ unsigned int i = 0;
+
+ if (!ctx || !new || !old || !sid || !num) return NT_STATUS_INVALID_PARAMETER;
+
+ *num += 1;
+
+ if((new[0] = (SEC_ACE *) talloc_zero(ctx, (*num) * sizeof(SEC_ACE))) == 0)
+ return NT_STATUS_NO_MEMORY;
+
+ for (i = 0; i < *num - 1; i ++)
+ sec_ace_copy(&(*new)[i], &old[i]);
+
+ (*new)[i].type = 0;
+ (*new)[i].flags = 0;
+ (*new)[i].size = SEC_ACE_HEADER_SIZE + sid_size(sid);
+ (*new)[i].info.mask = mask;
+ sid_copy(&(*new)[i].trustee, sid);
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ modify SID's permissions at ACL
+********************************************************************/
+
+NTSTATUS sec_ace_mod_sid(SEC_ACE *ace, size_t num, DOM_SID *sid, uint32 mask)
+{
+ unsigned int i = 0;
+
+ if (!ace || !sid) return NT_STATUS_INVALID_PARAMETER;
+
+ for (i = 0; i < num; i ++) {
+ if (sid_compare(&ace[i].trustee, sid) == 0) {
+ ace[i].info.mask = mask;
+ return NT_STATUS_OK;
+ }
+ }
+ return NT_STATUS_NOT_FOUND;
+}
+
+/*******************************************************************
+ delete SID from ACL
+********************************************************************/
+
+NTSTATUS sec_ace_del_sid(TALLOC_CTX *ctx, SEC_ACE **new, SEC_ACE *old, uint32 *num, DOM_SID *sid)
+{
+ unsigned int i = 0;
+ unsigned int n_del = 0;
+
+ if (!ctx || !new || !old || !sid || !num) return NT_STATUS_INVALID_PARAMETER;
+
+ if((new[0] = (SEC_ACE *) talloc_zero(ctx, (*num) * sizeof(SEC_ACE))) == 0)
+ return NT_STATUS_NO_MEMORY;
+
+ for (i = 0; i < *num; i ++) {
+ if (sid_compare(&old[i].trustee, sid) != 0)
+ sec_ace_copy(&(*new)[i], &old[i]);
+ else
+ n_del ++;
+ }
+ if (n_del == 0)
+ return NT_STATUS_NOT_FOUND;
+ else {
+ *num -= n_del;
+ return NT_STATUS_OK;
+ }
+}
+
+/*******************************************************************
+ Compares two SEC_ACE structures
+********************************************************************/
+
+BOOL sec_ace_equal(SEC_ACE *s1, SEC_ACE *s2)
+{
+ /* Trivial case */
+
+ if (!s1 && !s2) return True;
+
+ /* Check top level stuff */
+
+ if (s1->type != s2->type || s1->flags != s2->flags ||
+ s1->info.mask != s2->info.mask) {
+ return False;
+ }
+
+ /* Check SID */
+
+ if (!sid_equal(&s1->trustee, &s2->trustee)) {
+ return False;
+ }
+
+ return True;
+}
+
+int nt_ace_inherit_comp( SEC_ACE *a1, SEC_ACE *a2)
+{
+ int a1_inh = a1->flags & SEC_ACE_FLAG_INHERITED_ACE;
+ int a2_inh = a2->flags & SEC_ACE_FLAG_INHERITED_ACE;
+
+ if (a1_inh == a2_inh)
+ return 0;
+
+ if (!a1_inh && a2_inh)
+ return -1;
+ return 1;
+}
+
+/*******************************************************************
+ Comparison function to apply the order explained below in a group.
+*******************************************************************/
+
+int nt_ace_canon_comp( SEC_ACE *a1, SEC_ACE *a2)
+{
+ if ((a1->type == SEC_ACE_TYPE_ACCESS_DENIED) &&
+ (a2->type != SEC_ACE_TYPE_ACCESS_DENIED))
+ return -1;
+
+ if ((a2->type == SEC_ACE_TYPE_ACCESS_DENIED) &&
+ (a1->type != SEC_ACE_TYPE_ACCESS_DENIED))
+ return 1;
+
+ /* Both access denied or access allowed. */
+
+ /* 1. ACEs that apply to the object itself */
+
+ if (!(a1->flags & SEC_ACE_FLAG_INHERIT_ONLY) &&
+ (a2->flags & SEC_ACE_FLAG_INHERIT_ONLY))
+ return -1;
+ else if (!(a2->flags & SEC_ACE_FLAG_INHERIT_ONLY) &&
+ (a1->flags & SEC_ACE_FLAG_INHERIT_ONLY))
+ return 1;
+
+ /* 2. ACEs that apply to a subobject of the object, such as
+ * a property set or property. */
+
+ if (a1->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT) &&
+ !(a2->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT)))
+ return -1;
+ else if (a2->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT) &&
+ !(a1->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT)))
+ return 1;
+
+ return 0;
+}
+
+/*******************************************************************
+ Functions to convert a SEC_DESC ACE DACL list into canonical order.
+ JRA.
+
+--- from http://msdn.microsoft.com/library/default.asp?url=/library/en-us/security/security/order_of_aces_in_a_dacl.asp
+
+The following describes the preferred order:
+
+ To ensure that noninherited ACEs have precedence over inherited ACEs,
+ place all noninherited ACEs in a group before any inherited ACEs.
+ This ordering ensures, for example, that a noninherited access-denied ACE
+ is enforced regardless of any inherited ACE that allows access.
+
+ Within the groups of noninherited ACEs and inherited ACEs, order ACEs according to ACE type, as the following shows:
+ 1. Access-denied ACEs that apply to the object itself
+ 2. Access-denied ACEs that apply to a subobject of the object, such as a property set or property
+ 3. Access-allowed ACEs that apply to the object itself
+ 4. Access-allowed ACEs that apply to a subobject of the object"
+
+********************************************************************/
+
+void dacl_sort_into_canonical_order(SEC_ACE *srclist, unsigned int num_aces)
+{
+ unsigned int i;
+
+ if (!srclist || num_aces == 0)
+ return;
+
+ /* Sort so that non-inherited ACE's come first. */
+ qsort( srclist, num_aces, sizeof(srclist[0]), QSORT_CAST nt_ace_inherit_comp);
+
+ /* Find the boundary between non-inherited ACEs. */
+ for (i = 0; i < num_aces; i++ ) {
+ SEC_ACE *curr_ace = &srclist[i];
+
+ if (curr_ace->flags & SEC_ACE_FLAG_INHERITED_ACE)
+ break;
+ }
+
+ /* i now points at entry number of the first inherited ACE. */
+
+ /* Sort the non-inherited ACEs. */
+ if (i)
+ qsort( srclist, i, sizeof(srclist[0]), QSORT_CAST nt_ace_canon_comp);
+
+ /* Now sort the inherited ACEs. */
+ if (num_aces - i)
+ qsort( &srclist[i], num_aces - i, sizeof(srclist[0]), QSORT_CAST nt_ace_canon_comp);
+}
+
+/*******************************************************************
+ Check if this ACE has a SID in common with the token.
+********************************************************************/
+
+BOOL token_sid_in_ace(const NT_USER_TOKEN *token, const SEC_ACE *ace)
+{
+ size_t i;
+
+ for (i = 0; i < token->num_sids; i++) {
+ if (sid_equal(&ace->trustee, &token->user_sids[i]))
+ return True;
+ }
+
+ return False;
+}
diff --git a/source/lib/secacl.c b/source/lib/secacl.c
new file mode 100644
index 00000000000..756685a8216
--- /dev/null
+++ b/source/lib/secacl.c
@@ -0,0 +1,118 @@
+/*
+ * Unix SMB/Netbios implementation.
+ * SEC_ACL handling routines
+ * Copyright (C) Andrew Tridgell 1992-1998,
+ * Copyright (C) Jeremy R. Allison 1995-2003.
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ * Copyright (C) Paul Ashton 1997-1998.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+/*******************************************************************
+ Create a SEC_ACL structure.
+********************************************************************/
+
+SEC_ACL *make_sec_acl(TALLOC_CTX *ctx, uint16 revision, int num_aces, SEC_ACE *ace_list)
+{
+ SEC_ACL *dst;
+ int i;
+
+ if((dst = (SEC_ACL *)talloc_zero(ctx,sizeof(SEC_ACL))) == NULL)
+ return NULL;
+
+ dst->revision = revision;
+ dst->num_aces = num_aces;
+ dst->size = SEC_ACL_HEADER_SIZE;
+
+ /* Now we need to return a non-NULL address for the ace list even
+ if the number of aces required is zero. This is because there
+ is a distinct difference between a NULL ace and an ace with zero
+ entries in it. This is achieved by checking that num_aces is a
+ positive number. */
+
+ if ((num_aces) &&
+ ((dst->ace = (SEC_ACE *)talloc(ctx, sizeof(SEC_ACE) * num_aces))
+ == NULL)) {
+ return NULL;
+ }
+
+ for (i = 0; i < num_aces; i++) {
+ dst->ace[i] = ace_list[i]; /* Structure copy. */
+ dst->size += ace_list[i].size;
+ }
+
+ return dst;
+}
+
+/*******************************************************************
+ Duplicate a SEC_ACL structure.
+********************************************************************/
+
+SEC_ACL *dup_sec_acl(TALLOC_CTX *ctx, SEC_ACL *src)
+{
+ if(src == NULL)
+ return NULL;
+
+ return make_sec_acl(ctx, src->revision, src->num_aces, src->ace);
+}
+
+/*******************************************************************
+ Compares two SEC_ACL structures
+********************************************************************/
+
+BOOL sec_acl_equal(SEC_ACL *s1, SEC_ACL *s2)
+{
+ unsigned int i, j;
+
+ /* Trivial cases */
+
+ if (!s1 && !s2) return True;
+ if (!s1 || !s2) return False;
+
+ /* Check top level stuff */
+
+ if (s1->revision != s2->revision) {
+ DEBUG(10, ("sec_acl_equal(): revision differs (%d != %d)\n",
+ s1->revision, s2->revision));
+ return False;
+ }
+
+ if (s1->num_aces != s2->num_aces) {
+ DEBUG(10, ("sec_acl_equal(): num_aces differs (%d != %d)\n",
+ s1->revision, s2->revision));
+ return False;
+ }
+
+ /* The ACEs could be in any order so check each ACE in s1 against
+ each ACE in s2. */
+
+ for (i = 0; i < s1->num_aces; i++) {
+ BOOL found = False;
+
+ for (j = 0; j < s2->num_aces; j++) {
+ if (sec_ace_equal(&s1->ace[i], &s2->ace[j])) {
+ found = True;
+ break;
+ }
+ }
+
+ if (!found) return False;
+ }
+
+ return True;
+}
diff --git a/source/lib/secdesc.c b/source/lib/secdesc.c
new file mode 100644
index 00000000000..411185dbfa6
--- /dev/null
+++ b/source/lib/secdesc.c
@@ -0,0 +1,522 @@
+/*
+ * Unix SMB/Netbios implementation.
+ * SEC_DESC handling functions
+ * Copyright (C) Andrew Tridgell 1992-1998,
+ * Copyright (C) Jeremy R. Allison 1995-2003.
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ * Copyright (C) Paul Ashton 1997-1998.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+/*******************************************************************
+ Works out the linearization size of a SEC_DESC.
+********************************************************************/
+
+size_t sec_desc_size(SEC_DESC *psd)
+{
+ size_t offset;
+
+ if (!psd) return 0;
+
+ offset = SEC_DESC_HEADER_SIZE;
+
+ /* don't align */
+
+ if (psd->owner_sid != NULL)
+ offset += sid_size(psd->owner_sid);
+
+ if (psd->grp_sid != NULL)
+ offset += sid_size(psd->grp_sid);
+
+ if (psd->sacl != NULL)
+ offset += psd->sacl->size;
+
+ if (psd->dacl != NULL)
+ offset += psd->dacl->size;
+
+ return offset;
+}
+
+/*******************************************************************
+ Compares two SEC_DESC structures
+********************************************************************/
+
+BOOL sec_desc_equal(SEC_DESC *s1, SEC_DESC *s2)
+{
+ /* Trivial case */
+
+ if (!s1 && !s2) {
+ goto done;
+ }
+
+ /* Check top level stuff */
+
+ if (s1->revision != s2->revision) {
+ DEBUG(10, ("sec_desc_equal(): revision differs (%d != %d)\n",
+ s1->revision, s2->revision));
+ return False;
+ }
+
+ if (s1->type!= s2->type) {
+ DEBUG(10, ("sec_desc_equal(): type differs (%d != %d)\n",
+ s1->type, s2->type));
+ return False;
+ }
+
+ /* Check owner and group */
+
+ if (!sid_equal(s1->owner_sid, s2->owner_sid)) {
+ fstring str1, str2;
+
+ sid_to_string(str1, s1->owner_sid);
+ sid_to_string(str2, s2->owner_sid);
+
+ DEBUG(10, ("sec_desc_equal(): owner differs (%s != %s)\n",
+ str1, str2));
+ return False;
+ }
+
+ if (!sid_equal(s1->grp_sid, s2->grp_sid)) {
+ fstring str1, str2;
+
+ sid_to_string(str1, s1->grp_sid);
+ sid_to_string(str2, s2->grp_sid);
+
+ DEBUG(10, ("sec_desc_equal(): group differs (%s != %s)\n",
+ str1, str2));
+ return False;
+ }
+
+ /* Check ACLs present in one but not the other */
+
+ if ((s1->dacl && !s2->dacl) || (!s1->dacl && s2->dacl) ||
+ (s1->sacl && !s2->sacl) || (!s1->sacl && s2->sacl)) {
+ DEBUG(10, ("sec_desc_equal(): dacl or sacl not present\n"));
+ return False;
+ }
+
+ /* Sigh - we have to do it the hard way by iterating over all
+ the ACEs in the ACLs */
+
+ if (!sec_acl_equal(s1->dacl, s2->dacl) ||
+ !sec_acl_equal(s1->sacl, s2->sacl)) {
+ DEBUG(10, ("sec_desc_equal(): dacl/sacl list not equal\n"));
+ return False;
+ }
+
+ done:
+ DEBUG(10, ("sec_desc_equal(): secdescs are identical\n"));
+ return True;
+}
+
+/*******************************************************************
+ Merge part of security descriptor old_sec in to the empty sections of
+ security descriptor new_sec.
+********************************************************************/
+
+SEC_DESC_BUF *sec_desc_merge(TALLOC_CTX *ctx, SEC_DESC_BUF *new_sdb, SEC_DESC_BUF *old_sdb)
+{
+ DOM_SID *owner_sid, *group_sid;
+ SEC_DESC_BUF *return_sdb;
+ SEC_ACL *dacl, *sacl;
+ SEC_DESC *psd = NULL;
+ uint16 secdesc_type;
+ size_t secdesc_size;
+
+ /* Copy over owner and group sids. There seems to be no flag for
+ this so just check the pointer values. */
+
+ owner_sid = new_sdb->sec->owner_sid ? new_sdb->sec->owner_sid :
+ old_sdb->sec->owner_sid;
+
+ group_sid = new_sdb->sec->grp_sid ? new_sdb->sec->grp_sid :
+ old_sdb->sec->grp_sid;
+
+ secdesc_type = new_sdb->sec->type;
+
+ /* Ignore changes to the system ACL. This has the effect of making
+ changes through the security tab audit button not sticking.
+ Perhaps in future Samba could implement these settings somehow. */
+
+ sacl = NULL;
+ secdesc_type &= ~SEC_DESC_SACL_PRESENT;
+
+ /* Copy across discretionary ACL */
+
+ if (secdesc_type & SEC_DESC_DACL_PRESENT) {
+ dacl = new_sdb->sec->dacl;
+ } else {
+ dacl = old_sdb->sec->dacl;
+ }
+
+ /* Create new security descriptor from bits */
+
+ psd = make_sec_desc(ctx, new_sdb->sec->revision, secdesc_type,
+ owner_sid, group_sid, sacl, dacl, &secdesc_size);
+
+ return_sdb = make_sec_desc_buf(ctx, secdesc_size, psd);
+
+ return(return_sdb);
+}
+
+/*******************************************************************
+ Creates a SEC_DESC structure
+********************************************************************/
+
+SEC_DESC *make_sec_desc(TALLOC_CTX *ctx, uint16 revision, uint16 type,
+ DOM_SID *owner_sid, DOM_SID *grp_sid,
+ SEC_ACL *sacl, SEC_ACL *dacl, size_t *sd_size)
+{
+ SEC_DESC *dst;
+ uint32 offset = 0;
+
+ *sd_size = 0;
+
+ if(( dst = (SEC_DESC *)talloc_zero(ctx, sizeof(SEC_DESC))) == NULL)
+ return NULL;
+
+ dst->revision = revision;
+ dst->type = type;
+
+ if (sacl)
+ dst->type |= SEC_DESC_SACL_PRESENT;
+ if (dacl)
+ dst->type |= SEC_DESC_DACL_PRESENT;
+
+ dst->off_owner_sid = 0;
+ dst->off_grp_sid = 0;
+ dst->off_sacl = 0;
+ dst->off_dacl = 0;
+
+ if(owner_sid && ((dst->owner_sid = sid_dup_talloc(ctx,owner_sid)) == NULL))
+ goto error_exit;
+
+ if(grp_sid && ((dst->grp_sid = sid_dup_talloc(ctx,grp_sid)) == NULL))
+ goto error_exit;
+
+ if(sacl && ((dst->sacl = dup_sec_acl(ctx, sacl)) == NULL))
+ goto error_exit;
+
+ if(dacl && ((dst->dacl = dup_sec_acl(ctx, dacl)) == NULL))
+ goto error_exit;
+
+ offset = SEC_DESC_HEADER_SIZE;
+
+ /*
+ * Work out the linearization sizes.
+ */
+
+ if (dst->sacl != NULL) {
+ dst->off_sacl = offset;
+ offset += dst->sacl->size;
+ }
+ if (dst->dacl != NULL) {
+ dst->off_dacl = offset;
+ offset += dst->dacl->size;
+ }
+
+ if (dst->owner_sid != NULL) {
+ dst->off_owner_sid = offset;
+ offset += sid_size(dst->owner_sid);
+ }
+
+ if (dst->grp_sid != NULL) {
+ dst->off_grp_sid = offset;
+ offset += sid_size(dst->grp_sid);
+ }
+
+ *sd_size = (size_t)offset;
+ return dst;
+
+error_exit:
+
+ *sd_size = 0;
+ return NULL;
+}
+
+/*******************************************************************
+ Duplicate a SEC_DESC structure.
+********************************************************************/
+
+SEC_DESC *dup_sec_desc(TALLOC_CTX *ctx, const SEC_DESC *src)
+{
+ size_t dummy;
+
+ if(src == NULL)
+ return NULL;
+
+ return make_sec_desc( ctx, src->revision, src->type,
+ src->owner_sid, src->grp_sid, src->sacl,
+ src->dacl, &dummy);
+}
+
+/*******************************************************************
+ Creates a SEC_DESC structure with typical defaults.
+********************************************************************/
+
+SEC_DESC *make_standard_sec_desc(TALLOC_CTX *ctx, DOM_SID *owner_sid, DOM_SID *grp_sid,
+ SEC_ACL *dacl, size_t *sd_size)
+{
+ return make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE,
+ owner_sid, grp_sid, NULL, dacl, sd_size);
+}
+
+/*******************************************************************
+ Creates a SEC_DESC_BUF structure.
+********************************************************************/
+
+SEC_DESC_BUF *make_sec_desc_buf(TALLOC_CTX *ctx, size_t len, SEC_DESC *sec_desc)
+{
+ SEC_DESC_BUF *dst;
+
+ if((dst = (SEC_DESC_BUF *)talloc_zero(ctx, sizeof(SEC_DESC_BUF))) == NULL)
+ return NULL;
+
+ /* max buffer size (allocated size) */
+ dst->max_len = (uint32)len;
+ dst->len = (uint32)len;
+
+ if(sec_desc && ((dst->sec = dup_sec_desc(ctx, sec_desc)) == NULL)) {
+ return NULL;
+ }
+
+ dst->ptr = 0x1;
+
+ return dst;
+}
+
+/*******************************************************************
+ Duplicates a SEC_DESC_BUF structure.
+********************************************************************/
+
+SEC_DESC_BUF *dup_sec_desc_buf(TALLOC_CTX *ctx, SEC_DESC_BUF *src)
+{
+ if(src == NULL)
+ return NULL;
+
+ return make_sec_desc_buf( ctx, src->len, src->sec);
+}
+
+/*******************************************************************
+ Add a new SID with its permissions to SEC_DESC.
+********************************************************************/
+
+NTSTATUS sec_desc_add_sid(TALLOC_CTX *ctx, SEC_DESC **psd, DOM_SID *sid, uint32 mask, size_t *sd_size)
+{
+ SEC_DESC *sd = 0;
+ SEC_ACL *dacl = 0;
+ SEC_ACE *ace = 0;
+ NTSTATUS status;
+
+ *sd_size = 0;
+
+ if (!ctx || !psd || !sid || !sd_size)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ status = sec_ace_add_sid(ctx, &ace, psd[0]->dacl->ace, &psd[0]->dacl->num_aces, sid, mask);
+
+ if (!NT_STATUS_IS_OK(status))
+ return status;
+
+ if (!(dacl = make_sec_acl(ctx, psd[0]->dacl->revision, psd[0]->dacl->num_aces, ace)))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ if (!(sd = make_sec_desc(ctx, psd[0]->revision, psd[0]->type, psd[0]->owner_sid,
+ psd[0]->grp_sid, psd[0]->sacl, dacl, sd_size)))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ *psd = sd;
+ sd = 0;
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Modify a SID's permissions in a SEC_DESC.
+********************************************************************/
+
+NTSTATUS sec_desc_mod_sid(SEC_DESC *sd, DOM_SID *sid, uint32 mask)
+{
+ NTSTATUS status;
+
+ if (!sd || !sid)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ status = sec_ace_mod_sid(sd->dacl->ace, sd->dacl->num_aces, sid, mask);
+
+ if (!NT_STATUS_IS_OK(status))
+ return status;
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Delete a SID from a SEC_DESC.
+********************************************************************/
+
+NTSTATUS sec_desc_del_sid(TALLOC_CTX *ctx, SEC_DESC **psd, DOM_SID *sid, size_t *sd_size)
+{
+ SEC_DESC *sd = 0;
+ SEC_ACL *dacl = 0;
+ SEC_ACE *ace = 0;
+ NTSTATUS status;
+
+ *sd_size = 0;
+
+ if (!ctx || !psd[0] || !sid || !sd_size)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ status = sec_ace_del_sid(ctx, &ace, psd[0]->dacl->ace, &psd[0]->dacl->num_aces, sid);
+
+ if (!NT_STATUS_IS_OK(status))
+ return status;
+
+ if (!(dacl = make_sec_acl(ctx, psd[0]->dacl->revision, psd[0]->dacl->num_aces, ace)))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ if (!(sd = make_sec_desc(ctx, psd[0]->revision, psd[0]->type, psd[0]->owner_sid,
+ psd[0]->grp_sid, psd[0]->sacl, dacl, sd_size)))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ *psd = sd;
+ sd = 0;
+ return NT_STATUS_OK;
+}
+
+/* Create a child security descriptor using another security descriptor as
+ the parent container. This child object can either be a container or
+ non-container object. */
+
+SEC_DESC_BUF *se_create_child_secdesc(TALLOC_CTX *ctx, SEC_DESC *parent_ctr,
+ BOOL child_container)
+{
+ SEC_DESC_BUF *sdb;
+ SEC_DESC *sd;
+ SEC_ACL *new_dacl, *the_acl;
+ SEC_ACE *new_ace_list = NULL;
+ unsigned int new_ace_list_ndx = 0, i;
+ size_t size;
+
+ /* Currently we only process the dacl when creating the child. The
+ sacl should also be processed but this is left out as sacls are
+ not implemented in Samba at the moment.*/
+
+ the_acl = parent_ctr->dacl;
+
+ if (!(new_ace_list = talloc(ctx, sizeof(SEC_ACE) * the_acl->num_aces)))
+ return NULL;
+
+ for (i = 0; the_acl && i < the_acl->num_aces; i++) {
+ SEC_ACE *ace = &the_acl->ace[i];
+ SEC_ACE *new_ace = &new_ace_list[new_ace_list_ndx];
+ uint8 new_flags = 0;
+ BOOL inherit = False;
+ fstring sid_str;
+
+ /* The OBJECT_INHERIT_ACE flag causes the ACE to be
+ inherited by non-container children objects. Container
+ children objects will inherit it as an INHERIT_ONLY
+ ACE. */
+
+ if (ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) {
+
+ if (!child_container) {
+ new_flags |= SEC_ACE_FLAG_OBJECT_INHERIT;
+ } else {
+ new_flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+ }
+
+ inherit = True;
+ }
+
+ /* The CONAINER_INHERIT_ACE flag means all child container
+ objects will inherit and use the ACE. */
+
+ if (ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) {
+ if (!child_container) {
+ inherit = False;
+ } else {
+ new_flags |= SEC_ACE_FLAG_CONTAINER_INHERIT;
+ }
+ }
+
+ /* The INHERIT_ONLY_ACE is not used by the se_access_check()
+ function for the parent container, but is inherited by
+ all child objects as a normal ACE. */
+
+ if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+ /* Move along, nothing to see here */
+ }
+
+ /* The SEC_ACE_FLAG_NO_PROPAGATE_INHERIT flag means the ACE
+ is inherited by child objects but not grandchildren
+ objects. We clear the object inherit and container
+ inherit flags in the inherited ACE. */
+
+ if (ace->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
+ new_flags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT |
+ SEC_ACE_FLAG_CONTAINER_INHERIT);
+ }
+
+ /* Add ACE to ACE list */
+
+ if (!inherit)
+ continue;
+
+ init_sec_access(&new_ace->info, ace->info.mask);
+ init_sec_ace(new_ace, &ace->trustee, ace->type,
+ new_ace->info, new_flags);
+
+ sid_to_string(sid_str, &ace->trustee);
+
+ DEBUG(5, ("se_create_child_secdesc(): %s:%d/0x%02x/0x%08x "
+ " inherited as %s:%d/0x%02x/0x%08x\n", sid_str,
+ ace->type, ace->flags, ace->info.mask,
+ sid_str, new_ace->type, new_ace->flags,
+ new_ace->info.mask));
+
+ new_ace_list_ndx++;
+ }
+
+ /* Create child security descriptor to return */
+
+ new_dacl = make_sec_acl(ctx, ACL_REVISION, new_ace_list_ndx, new_ace_list);
+
+ /* Use the existing user and group sids. I don't think this is
+ correct. Perhaps the user and group should be passed in as
+ parameters by the caller? */
+
+ sd = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE,
+ parent_ctr->owner_sid,
+ parent_ctr->grp_sid,
+ parent_ctr->sacl,
+ new_dacl, &size);
+
+ sdb = make_sec_desc_buf(ctx, size, sd);
+
+ return sdb;
+}
+
+/*******************************************************************
+ Sets up a SEC_ACCESS structure.
+********************************************************************/
+
+void init_sec_access(SEC_ACCESS *t, uint32 mask)
+{
+ t->mask = mask;
+}
+
diff --git a/source/lib/select.c b/source/lib/select.c
new file mode 100644
index 00000000000..f88ad52de65
--- /dev/null
+++ b/source/lib/select.c
@@ -0,0 +1,159 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ Samba select/poll implementation
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* This is here because it allows us to avoid a nasty race in signal handling.
+ We need to guarantee that when we get a signal we get out of a select immediately
+ but doing that involves a race condition. We can avoid the race by getting the
+ signal handler to write to a pipe that is in the select/poll list
+
+ This means all Samba signal handlers should call sys_select_signal().
+*/
+
+static pid_t initialised;
+static int select_pipe[2];
+static VOLATILE unsigned pipe_written, pipe_read;
+
+/*******************************************************************
+ Call this from all Samba signal handlers if you want to avoid a
+ nasty signal race condition.
+********************************************************************/
+
+void sys_select_signal(void)
+{
+ char c = 1;
+ if (!initialised) return;
+
+ if (pipe_written > pipe_read+256) return;
+
+ if (write(select_pipe[1], &c, 1) == 1) pipe_written++;
+}
+
+/*******************************************************************
+ Like select() but avoids the signal race using a pipe
+ it also guuarantees that fds on return only ever contains bits set
+ for file descriptors that were readable.
+********************************************************************/
+
+int sys_select(int maxfd, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *tval)
+{
+ int ret, saved_errno;
+ fd_set *readfds2, readfds_buf;
+
+ if (initialised != sys_getpid()) {
+ pipe(select_pipe);
+
+ /*
+ * These next two lines seem to fix a bug with the Linux
+ * 2.0.x kernel (and probably other UNIXes as well) where
+ * the one byte read below can block even though the
+ * select returned that there is data in the pipe and
+ * the pipe_written variable was incremented. Thanks to
+ * HP for finding this one. JRA.
+ */
+
+ if(set_blocking(select_pipe[0],0)==-1)
+ smb_panic("select_pipe[0]: O_NONBLOCK failed.\n");
+ if(set_blocking(select_pipe[1],0)==-1)
+ smb_panic("select_pipe[1]: O_NONBLOCK failed.\n");
+
+ initialised = sys_getpid();
+ }
+
+ maxfd = MAX(select_pipe[0]+1, maxfd);
+
+ /* If readfds is NULL we need to provide our own set. */
+ if (readfds) {
+ readfds2 = readfds;
+ } else {
+ readfds2 = &readfds_buf;
+ FD_ZERO(readfds2);
+ }
+ FD_SET(select_pipe[0], readfds2);
+
+ errno = 0;
+ ret = select(maxfd,readfds2,writefds,errorfds,tval);
+
+ if (ret <= 0) {
+ FD_ZERO(readfds2);
+ if (writefds)
+ FD_ZERO(writefds);
+ if (errorfds)
+ FD_ZERO(errorfds);
+ }
+
+ if (FD_ISSET(select_pipe[0], readfds2)) {
+ char c;
+ saved_errno = errno;
+ if (read(select_pipe[0], &c, 1) == 1) {
+ pipe_read++;
+ }
+ errno = saved_errno;
+ FD_CLR(select_pipe[0], readfds2);
+ ret--;
+ if (ret == 0) {
+ ret = -1;
+ errno = EINTR;
+ }
+ }
+
+ return ret;
+}
+
+/*******************************************************************
+ Similar to sys_select() but catch EINTR and continue.
+ This is what sys_select() used to do in Samba.
+********************************************************************/
+
+int sys_select_intr(int maxfd, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *tval)
+{
+ int ret;
+ fd_set *readfds2, readfds_buf, *writefds2, writefds_buf, *errorfds2, errorfds_buf;
+ struct timeval tval2, *ptval;
+
+ readfds2 = (readfds ? &readfds_buf : NULL);
+ writefds2 = (writefds ? &writefds_buf : NULL);
+ errorfds2 = (errorfds ? &errorfds_buf : NULL);
+ ptval = (tval ? &tval2 : NULL);
+
+ do {
+ if (readfds)
+ readfds_buf = *readfds;
+ if (writefds)
+ writefds_buf = *writefds;
+ if (errorfds)
+ errorfds_buf = *errorfds;
+ if (tval)
+ tval2 = *tval;
+
+ ret = sys_select(maxfd, readfds2, writefds2, errorfds2, ptval);
+ } while (ret == -1 && errno == EINTR);
+
+ if (readfds)
+ *readfds = readfds_buf;
+ if (writefds)
+ *writefds = writefds_buf;
+ if (errorfds)
+ *errorfds = errorfds_buf;
+
+ return ret;
+}
diff --git a/source/lib/sendfile.c b/source/lib/sendfile.c
new file mode 100644
index 00000000000..4aa76a0c74a
--- /dev/null
+++ b/source/lib/sendfile.c
@@ -0,0 +1,382 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 2.2.x / 3.0.x
+ sendfile implementations.
+ Copyright (C) Jeremy Allison 2002.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ * This file handles the OS dependent sendfile implementations.
+ * The API is such that it returns -1 on error, else returns the
+ * number of bytes written.
+ */
+
+#include "includes.h"
+
+#if defined(LINUX_SENDFILE_API)
+
+#include <sys/sendfile.h>
+
+#ifndef MSG_MORE
+#define MSG_MORE 0x8000
+#endif
+
+ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
+{
+ size_t total=0;
+ ssize_t ret;
+ size_t hdr_len = 0;
+
+ /*
+ * Send the header first.
+ * Use MSG_MORE to cork the TCP output until sendfile is called.
+ */
+
+ if (header) {
+ hdr_len = header->length;
+ while (total < hdr_len) {
+ ret = sys_send(tofd, header->data + total,hdr_len - total, MSG_MORE);
+ if (ret == -1)
+ return -1;
+ total += ret;
+ }
+ }
+
+ total = count;
+ while (total) {
+ ssize_t nwritten;
+ do {
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILE64)
+ nwritten = sendfile64(tofd, fromfd, &offset, total);
+#else
+ nwritten = sendfile(tofd, fromfd, &offset, total);
+#endif
+ } while (nwritten == -1 && errno == EINTR);
+ if (nwritten == -1)
+ return -1;
+ if (nwritten == 0)
+ return -1; /* I think we're at EOF here... */
+ total -= nwritten;
+ }
+ return count + hdr_len;
+}
+
+#elif defined(LINUX_BROKEN_SENDFILE_API)
+
+/*
+ * We must use explicit 32 bit types here. This code path means Linux
+ * won't do proper 64-bit sendfile. JRA.
+ */
+
+extern int32 sendfile (int out_fd, int in_fd, int32 *offset, uint32 count);
+
+
+#ifndef MSG_MORE
+#define MSG_MORE 0x8000
+#endif
+
+ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
+{
+ size_t total=0;
+ ssize_t ret;
+ ssize_t hdr_len = 0;
+ uint32 small_total = 0;
+ int32 small_offset;
+
+ /*
+ * Fix for broken Linux 2.4 systems with no working sendfile64().
+ * If the offset+count > 2 GB then pretend we don't have the
+ * system call sendfile at all. The upper layer catches this
+ * and uses a normal read. JRA.
+ */
+
+ if ((sizeof(SMB_OFF_T) >= 8) && (offset + count > (SMB_OFF_T)0x7FFFFFFF)) {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ /*
+ * Send the header first.
+ * Use MSG_MORE to cork the TCP output until sendfile is called.
+ */
+
+ if (header) {
+ hdr_len = header->length;
+ while (total < hdr_len) {
+ ret = sys_send(tofd, header->data + total,hdr_len - total, MSG_MORE);
+ if (ret == -1)
+ return -1;
+ total += ret;
+ }
+ }
+
+ small_total = (uint32)count;
+ small_offset = (int32)offset;
+
+ while (small_total) {
+ int32 nwritten;
+ do {
+ nwritten = sendfile(tofd, fromfd, &small_offset, small_total);
+ } while (nwritten == -1 && errno == EINTR);
+ if (nwritten == -1)
+ return -1;
+ if (nwritten == 0)
+ return -1; /* I think we're at EOF here... */
+ small_total -= nwritten;
+ }
+ return count + hdr_len;
+}
+
+
+#elif defined(SOLARIS_SENDFILE_API)
+
+/*
+ * Solaris sendfile code written by Pierre Belanger <belanger@pobox.com>.
+ */
+
+#include <sys/sendfile.h>
+
+ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
+{
+ int sfvcnt;
+ size_t total, xferred;
+ struct sendfilevec vec[2];
+ ssize_t hdr_len = 0;
+
+ if (header) {
+ sfvcnt = 2;
+
+ vec[0].sfv_fd = SFV_FD_SELF;
+ vec[0].sfv_flag = 0;
+ vec[0].sfv_off = (off_t)header->data;
+ vec[0].sfv_len = hdr_len = header->length;
+
+ vec[1].sfv_fd = fromfd;
+ vec[1].sfv_flag = 0;
+ vec[1].sfv_off = offset;
+ vec[1].sfv_len = count;
+
+ } else {
+ sfvcnt = 1;
+
+ vec[0].sfv_fd = fromfd;
+ vec[0].sfv_flag = 0;
+ vec[0].sfv_off = offset;
+ vec[0].sfv_len = count;
+ }
+
+ total = count + hdr_len;
+
+ while (total) {
+ ssize_t nwritten;
+
+ /*
+ * Although not listed in the API error returns, this is almost certainly
+ * a slow system call and will be interrupted by a signal with EINTR. JRA.
+ */
+
+ xferred = 0;
+
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILEV64)
+ nwritten = sendfilev64(tofd, vec, sfvcnt, &xferred);
+#else
+ nwritten = sendfilev(tofd, vec, sfvcnt, &xferred);
+#endif
+ if (nwritten == -1 && errno == EINTR) {
+ if (xferred == 0)
+ continue; /* Nothing written yet. */
+ else
+ nwritten = xferred;
+ }
+
+ if (nwritten == -1)
+ return -1;
+ if (nwritten == 0)
+ return -1; /* I think we're at EOF here... */
+
+ /*
+ * If this was a short (signal interrupted) write we may need
+ * to subtract it from the header data, or null out the header
+ * data altogether if we wrote more than vec[0].sfv_len bytes.
+ * We move vec[1].* to vec[0].* and set sfvcnt to 1
+ */
+
+ if (sfvcnt == 2 && nwritten >= vec[0].sfv_len) {
+ vec[1].sfv_off += nwritten - vec[0].sfv_len;
+ vec[1].sfv_len -= nwritten - vec[0].sfv_len;
+
+ /* Move vec[1].* to vec[0].* and set sfvcnt to 1 */
+ vec[0] = vec[1];
+ sfvcnt = 1;
+ } else {
+ vec[0].sfv_off += nwritten;
+ vec[0].sfv_len -= nwritten;
+ }
+ total -= nwritten;
+ }
+ return count + hdr_len;
+}
+
+#elif defined(HPUX_SENDFILE_API)
+
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
+{
+ size_t total=0;
+ struct iovec hdtrl[2];
+ size_t hdr_len = 0;
+
+ if (header) {
+ /* Set up the header/trailer iovec. */
+ hdtrl[0].iov_base = header->data;
+ hdtrl[0].iov_len = hdr_len = header->length;
+ } else {
+ hdtrl[0].iov_base = NULL;
+ hdtrl[0].iov_len = hdr_len = 0;
+ }
+ hdtrl[1].iov_base = NULL;
+ hdtrl[1].iov_base = 0;
+
+ total = count;
+ while (total + hdtrl[0].iov_len) {
+ ssize_t nwritten;
+
+ /*
+ * HPUX guarantees that if any data was written before
+ * a signal interrupt then sendfile returns the number of
+ * bytes written (which may be less than requested) not -1.
+ * nwritten includes the header data sent.
+ */
+
+ do {
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILE64)
+ nwritten = sendfile64(tofd, fromfd, offset, total, &hdtrl[0], 0);
+#else
+ nwritten = sendfile(tofd, fromfd, offset, total, &hdtrl[0], 0);
+#endif
+ } while (nwritten == -1 && errno == EINTR);
+ if (nwritten == -1)
+ return -1;
+ if (nwritten == 0)
+ return -1; /* I think we're at EOF here... */
+
+ /*
+ * If this was a short (signal interrupted) write we may need
+ * to subtract it from the header data, or null out the header
+ * data altogether if we wrote more than hdtrl[0].iov_len bytes.
+ * We change nwritten to be the number of file bytes written.
+ */
+
+ if (hdtrl[0].iov_base && hdtrl[0].iov_len) {
+ if (nwritten >= hdtrl[0].iov_len) {
+ nwritten -= hdtrl[0].iov_len;
+ hdtrl[0].iov_base = NULL;
+ hdtrl[0].iov_len = 0;
+ } else {
+ /* iov_base is defined as a void *... */
+ hdtrl[0].iov_base = ((char *)hdtrl[0].iov_base) + nwritten;
+ hdtrl[0].iov_len -= nwritten;
+ nwritten = 0;
+ }
+ }
+ total -= nwritten;
+ offset += nwritten;
+ }
+ return count + hdr_len;
+}
+
+#elif defined(FREEBSD_SENDFILE_API)
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
+{
+ size_t total=0;
+ struct sf_hdtr hdr;
+ struct iovec hdtrl;
+ size_t hdr_len = 0;
+
+ hdr.headers = &hdtrl;
+ hdr.hdr_cnt = 1;
+ hdr.trailers = NULL;
+ hdr.trl_cnt = 0;
+
+ /* Set up the header iovec. */
+ if (header) {
+ hdtrl.iov_base = header->data;
+ hdtrl.iov_len = hdr_len = header->length;
+ } else {
+ hdtrl.iov_base = NULL;
+ hdtrl.iov_len = 0;
+ }
+
+ total = count;
+ while (total + hdtrl.iov_len) {
+ SMB_OFF_T nwritten;
+ int ret;
+
+ /*
+ * FreeBSD sendfile returns 0 on success, -1 on error.
+ * Remember, the tofd and fromfd are reversed..... :-).
+ * nwritten includes the header data sent.
+ */
+
+ do {
+ ret = sendfile(fromfd, tofd, offset, total, &hdr, &nwritten, 0);
+ } while (ret == -1 && errno == EINTR);
+ if (ret == -1)
+ return -1;
+
+ if (nwritten == 0)
+ return -1; /* I think we're at EOF here... */
+
+ /*
+ * If this was a short (signal interrupted) write we may need
+ * to subtract it from the header data, or null out the header
+ * data altogether if we wrote more than hdtrl.iov_len bytes.
+ * We change nwritten to be the number of file bytes written.
+ */
+
+ if (hdtrl.iov_base && hdtrl.iov_len) {
+ if (nwritten >= hdtrl.iov_len) {
+ nwritten -= hdtrl.iov_len;
+ hdtrl.iov_base = NULL;
+ hdtrl.iov_len = 0;
+ } else {
+ hdtrl.iov_base += nwritten;
+ hdtrl.iov_len -= nwritten;
+ nwritten = 0;
+ }
+ }
+ total -= nwritten;
+ offset += nwritten;
+ }
+ return count + hdr_len;
+}
+
+#else /* No sendfile implementation. Return error. */
+
+ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
+{
+ /* No sendfile syscall. */
+ errno = ENOSYS;
+ return -1;
+}
+#endif
diff --git a/source/lib/server_mutex.c b/source/lib/server_mutex.c
new file mode 100644
index 00000000000..3e5512c7342
--- /dev/null
+++ b/source/lib/server_mutex.c
@@ -0,0 +1,56 @@
+/*
+ Unix SMB/CIFS implementation.
+ Authenticate against a remote domain
+ Copyright (C) Andrew Tridgell 1992-2002
+ Copyright (C) Andrew Bartlett 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* For reasons known only to MS, many of their NT/Win2k versions
+ need serialised access only. Two connections at the same time
+ may (in certain situations) cause connections to be reset,
+ or access to be denied.
+
+ This locking allows smbd's mutlithread architecture to look
+ like the single-connection that NT makes. */
+
+static char *mutex_server_name;
+
+BOOL grab_server_mutex(const char *name)
+{
+ mutex_server_name = strdup(name);
+ if (!mutex_server_name) {
+ DEBUG(0,("grab_server_mutex: malloc failed for %s\n", name));
+ return False;
+ }
+ if (!secrets_named_mutex(mutex_server_name, 10)) {
+ DEBUG(10,("grab_server_mutex: failed for %s\n", name));
+ SAFE_FREE(mutex_server_name);
+ return False;
+ }
+
+ return True;
+}
+
+void release_server_mutex(void)
+{
+ if (mutex_server_name) {
+ secrets_named_mutex_release(mutex_server_name);
+ SAFE_FREE(mutex_server_name);
+ }
+}
diff --git a/source/lib/signal.c b/source/lib/signal.c
new file mode 100644
index 00000000000..bff4b91c1a0
--- /dev/null
+++ b/source/lib/signal.c
@@ -0,0 +1,139 @@
+/*
+ Unix SMB/CIFS implementation.
+ signal handling functions
+
+ Copyright (C) Andrew Tridgell 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+ Catch child exits and reap the child zombie status.
+****************************************************************************/
+
+static void sig_cld(int signum)
+{
+ while (sys_waitpid((pid_t)-1,(int *)NULL, WNOHANG) > 0)
+ ;
+
+ /*
+ * Turns out it's *really* important not to
+ * restore the signal handler here if we have real POSIX
+ * signal handling. If we do, then we get the signal re-delivered
+ * immediately - hey presto - instant loop ! JRA.
+ */
+
+#if !defined(HAVE_SIGACTION)
+ CatchSignal(SIGCLD, sig_cld);
+#endif
+}
+
+/****************************************************************************
+catch child exits - leave status;
+****************************************************************************/
+
+static void sig_cld_leave_status(int signum)
+{
+ /*
+ * Turns out it's *really* important not to
+ * restore the signal handler here if we have real POSIX
+ * signal handling. If we do, then we get the signal re-delivered
+ * immediately - hey presto - instant loop ! JRA.
+ */
+
+#if !defined(HAVE_SIGACTION)
+ CatchSignal(SIGCLD, sig_cld_leave_status);
+#endif
+}
+
+/*******************************************************************
+ Block sigs.
+********************************************************************/
+
+void BlockSignals(BOOL block,int signum)
+{
+#ifdef HAVE_SIGPROCMASK
+ sigset_t set;
+ sigemptyset(&set);
+ sigaddset(&set,signum);
+ sigprocmask(block?SIG_BLOCK:SIG_UNBLOCK,&set,NULL);
+#elif defined(HAVE_SIGBLOCK)
+ if (block) {
+ sigblock(sigmask(signum));
+ } else {
+ sigsetmask(siggetmask() & ~sigmask(signum));
+ }
+#else
+ /* yikes! This platform can't block signals? */
+ static int done;
+ if (!done) {
+ DEBUG(0,("WARNING: No signal blocking available\n"));
+ done=1;
+ }
+#endif
+}
+
+/*******************************************************************
+ Catch a signal. This should implement the following semantics:
+
+ 1) The handler remains installed after being called.
+ 2) The signal should be blocked during handler execution.
+********************************************************************/
+
+void (*CatchSignal(int signum,void (*handler)(int )))(int)
+{
+#ifdef HAVE_SIGACTION
+ struct sigaction act;
+ struct sigaction oldact;
+
+ ZERO_STRUCT(act);
+
+ act.sa_handler = handler;
+#ifdef SA_RESTART
+ /*
+ * We *want* SIGALRM to interrupt a system call.
+ */
+ if(signum != SIGALRM)
+ act.sa_flags = SA_RESTART;
+#endif
+ sigemptyset(&act.sa_mask);
+ sigaddset(&act.sa_mask,signum);
+ sigaction(signum,&act,&oldact);
+ return oldact.sa_handler;
+#else /* !HAVE_SIGACTION */
+ /* FIXME: need to handle sigvec and systems with broken signal() */
+ return signal(signum, handler);
+#endif
+}
+
+/*******************************************************************
+ Ignore SIGCLD via whatever means is necessary for this OS.
+********************************************************************/
+
+void CatchChild(void)
+{
+ CatchSignal(SIGCLD, sig_cld);
+}
+
+/*******************************************************************
+ Catch SIGCLD but leave the child around so it's status can be reaped.
+********************************************************************/
+
+void CatchChildLeaveStatus(void)
+{
+ CatchSignal(SIGCLD, sig_cld_leave_status);
+}
diff --git a/source/lib/smbldap.c b/source/lib/smbldap.c
new file mode 100644
index 00000000000..14a46fc5fb0
--- /dev/null
+++ b/source/lib/smbldap.c
@@ -0,0 +1,1234 @@
+/*
+ Unix SMB/CIFS mplementation.
+ LDAP protocol helper functions for SAMBA
+ Copyright (C) Jean François Micouleau 1998
+ Copyright (C) Gerald Carter 2001-2003
+ Copyright (C) Shahms King 2001
+ Copyright (C) Andrew Bartlett 2002-2003
+ Copyright (C) Stefan (metze) Metzmacher 2002-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+#include "smbldap.h"
+
+#ifndef LDAP_OPT_SUCCESS
+#define LDAP_OPT_SUCCESS 0
+#endif
+
+/* Try not to hit the up or down server forever */
+
+#define SMBLDAP_DONT_PING_TIME 10 /* ping only all 10 seconds */
+#define SMBLDAP_NUM_RETRIES 8 /* retry only 8 times */
+
+#define SMBLDAP_IDLE_TIME 150 /* After 2.5 minutes disconnect */
+
+
+/* attributes used by Samba 2.2 */
+
+ATTRIB_MAP_ENTRY attrib_map_v22[] = {
+ { LDAP_ATTR_UID, "uid" },
+ { LDAP_ATTR_UIDNUMBER, LDAP_ATTRIBUTE_UIDNUMBER},
+ { LDAP_ATTR_GIDNUMBER, LDAP_ATTRIBUTE_GIDNUMBER},
+ { LDAP_ATTR_UNIX_HOME, "homeDirectory" },
+ { LDAP_ATTR_PWD_LAST_SET, "pwdLastSet" },
+ { LDAP_ATTR_PWD_CAN_CHANGE, "pwdCanChange" },
+ { LDAP_ATTR_PWD_MUST_CHANGE, "pwdMustChange" },
+ { LDAP_ATTR_LOGON_TIME, "logonTime" },
+ { LDAP_ATTR_LOGOFF_TIME, "logoffTime" },
+ { LDAP_ATTR_KICKOFF_TIME, "kickoffTime" },
+ { LDAP_ATTR_CN, "cn" },
+ { LDAP_ATTR_DISPLAY_NAME, "displayName" },
+ { LDAP_ATTR_HOME_PATH, "smbHome" },
+ { LDAP_ATTR_HOME_DRIVE, "homeDrives" },
+ { LDAP_ATTR_LOGON_SCRIPT, "scriptPath" },
+ { LDAP_ATTR_PROFILE_PATH, "profilePath" },
+ { LDAP_ATTR_DESC, "description" },
+ { LDAP_ATTR_USER_WKS, "userWorkstations"},
+ { LDAP_ATTR_USER_RID, "rid" },
+ { LDAP_ATTR_PRIMARY_GROUP_RID, "primaryGroupID"},
+ { LDAP_ATTR_LMPW, "lmPassword" },
+ { LDAP_ATTR_NTPW, "ntPassword" },
+ { LDAP_ATTR_DOMAIN, "domain" },
+ { LDAP_ATTR_OBJCLASS, "objectClass" },
+ { LDAP_ATTR_ACB_INFO, "acctFlags" },
+ { LDAP_ATTR_LIST_END, NULL }
+};
+
+/* attributes used by Samba 3.0's sambaSamAccount */
+
+ATTRIB_MAP_ENTRY attrib_map_v30[] = {
+ { LDAP_ATTR_UID, "uid" },
+ { LDAP_ATTR_UIDNUMBER, LDAP_ATTRIBUTE_UIDNUMBER},
+ { LDAP_ATTR_GIDNUMBER, LDAP_ATTRIBUTE_GIDNUMBER},
+ { LDAP_ATTR_UNIX_HOME, "homeDirectory" },
+ { LDAP_ATTR_PWD_LAST_SET, "sambaPwdLastSet" },
+ { LDAP_ATTR_PWD_CAN_CHANGE, "sambaPwdCanChange" },
+ { LDAP_ATTR_PWD_MUST_CHANGE, "sambaPwdMustChange" },
+ { LDAP_ATTR_LOGON_TIME, "sambaLogonTime" },
+ { LDAP_ATTR_LOGOFF_TIME, "sambaLogoffTime" },
+ { LDAP_ATTR_KICKOFF_TIME, "sambaKickoffTime" },
+ { LDAP_ATTR_CN, "cn" },
+ { LDAP_ATTR_DISPLAY_NAME, "displayName" },
+ { LDAP_ATTR_HOME_DRIVE, "sambaHomeDrive" },
+ { LDAP_ATTR_HOME_PATH, "sambaHomePath" },
+ { LDAP_ATTR_LOGON_SCRIPT, "sambaLogonScript" },
+ { LDAP_ATTR_PROFILE_PATH, "sambaProfilePath" },
+ { LDAP_ATTR_DESC, "description" },
+ { LDAP_ATTR_USER_WKS, "sambaUserWorkstations" },
+ { LDAP_ATTR_USER_SID, LDAP_ATTRIBUTE_SID },
+ { LDAP_ATTR_PRIMARY_GROUP_SID, "sambaPrimaryGroupSID" },
+ { LDAP_ATTR_LMPW, "sambaLMPassword" },
+ { LDAP_ATTR_NTPW, "sambaNTPassword" },
+ { LDAP_ATTR_DOMAIN, "sambaDomainName" },
+ { LDAP_ATTR_OBJCLASS, "objectClass" },
+ { LDAP_ATTR_ACB_INFO, "sambaAcctFlags" },
+ { LDAP_ATTR_MUNGED_DIAL, "sambaMungedDial" },
+ { LDAP_ATTR_BAD_PASSWORD_COUNT, "sambaBadPasswordCount" },
+ { LDAP_ATTR_BAD_PASSWORD_TIME, "sambaBadPasswordTime" },
+ { LDAP_ATTR_MOD_TIMESTAMP, "modifyTimestamp" },
+ { LDAP_ATTR_LIST_END, NULL }
+};
+
+/* attributes used for allocating RIDs */
+
+ATTRIB_MAP_ENTRY dominfo_attr_list[] = {
+ { LDAP_ATTR_DOMAIN, "sambaDomainName" },
+ { LDAP_ATTR_NEXT_RID, "sambaNextRid" },
+ { LDAP_ATTR_NEXT_USERRID, "sambaNextUserRid" },
+ { LDAP_ATTR_NEXT_GROUPRID, "sambaNextGroupRid" },
+ { LDAP_ATTR_DOM_SID, LDAP_ATTRIBUTE_SID },
+ { LDAP_ATTR_ALGORITHMIC_RID_BASE,"sambaAlgorithmicRidBase"},
+ { LDAP_ATTR_OBJCLASS, "objectClass" },
+ { LDAP_ATTR_LIST_END, NULL },
+};
+
+/* Samba 3.0 group mapping attributes */
+
+ATTRIB_MAP_ENTRY groupmap_attr_list[] = {
+ { LDAP_ATTR_GIDNUMBER, LDAP_ATTRIBUTE_GIDNUMBER},
+ { LDAP_ATTR_GROUP_SID, LDAP_ATTRIBUTE_SID },
+ { LDAP_ATTR_GROUP_TYPE, "sambaGroupType" },
+ { LDAP_ATTR_SID_LIST, "sambaSIDList" },
+ { LDAP_ATTR_DESC, "description" },
+ { LDAP_ATTR_DISPLAY_NAME, "displayName" },
+ { LDAP_ATTR_CN, "cn" },
+ { LDAP_ATTR_OBJCLASS, "objectClass" },
+ { LDAP_ATTR_LIST_END, NULL }
+};
+
+ATTRIB_MAP_ENTRY groupmap_attr_list_to_delete[] = {
+ { LDAP_ATTR_GROUP_SID, LDAP_ATTRIBUTE_SID },
+ { LDAP_ATTR_GROUP_TYPE, "sambaGroupType" },
+ { LDAP_ATTR_DESC, "description" },
+ { LDAP_ATTR_DISPLAY_NAME, "displayName" },
+ { LDAP_ATTR_SID_LIST, "sambaSIDList" },
+ { LDAP_ATTR_LIST_END, NULL }
+};
+
+/* idmap_ldap sambaUnixIdPool */
+
+ATTRIB_MAP_ENTRY idpool_attr_list[] = {
+ { LDAP_ATTR_UIDNUMBER, LDAP_ATTRIBUTE_UIDNUMBER},
+ { LDAP_ATTR_GIDNUMBER, LDAP_ATTRIBUTE_GIDNUMBER},
+ { LDAP_ATTR_OBJCLASS, "objectClass" },
+ { LDAP_ATTR_LIST_END, NULL }
+};
+
+ATTRIB_MAP_ENTRY sidmap_attr_list[] = {
+ { LDAP_ATTR_SID, LDAP_ATTRIBUTE_SID },
+ { LDAP_ATTR_UIDNUMBER, LDAP_ATTRIBUTE_UIDNUMBER},
+ { LDAP_ATTR_GIDNUMBER, LDAP_ATTRIBUTE_GIDNUMBER},
+ { LDAP_ATTR_OBJCLASS, "objectClass" },
+ { LDAP_ATTR_LIST_END, NULL }
+};
+
+/* privileges */
+
+ATTRIB_MAP_ENTRY privilege_attr_list[] = {
+ { LDAP_ATTR_CN, "sambaPrivName" },
+ { LDAP_ATTR_SID_LIST, LDAP_ATTRIBUTE_SID_LIST },
+ { LDAP_ATTR_DESC, "description" },
+ { LDAP_ATTR_OBJCLASS, "objectClass" },
+ { LDAP_ATTR_LIST_END, NULL }
+};
+
+/**********************************************************************
+ perform a simple table lookup and return the attribute name
+ **********************************************************************/
+
+ const char* get_attr_key2string( ATTRIB_MAP_ENTRY table[], int key )
+{
+ int i = 0;
+
+ while ( table[i].attrib != LDAP_ATTR_LIST_END ) {
+ if ( table[i].attrib == key )
+ return table[i].name;
+ i++;
+ }
+
+ return NULL;
+}
+
+
+/**********************************************************************
+ Return the list of attribute names from a mapping table
+ **********************************************************************/
+
+ char** get_attr_list( ATTRIB_MAP_ENTRY table[] )
+{
+ char **names;
+ int i = 0;
+
+ while ( table[i].attrib != LDAP_ATTR_LIST_END )
+ i++;
+ i++;
+
+ names = (char**)malloc( sizeof(char*)*i );
+ if ( !names ) {
+ DEBUG(0,("get_attr_list: out of memory\n"));
+ return NULL;
+ }
+
+ i = 0;
+ while ( table[i].attrib != LDAP_ATTR_LIST_END ) {
+ names[i] = strdup( table[i].name );
+ i++;
+ }
+ names[i] = NULL;
+
+ return names;
+}
+
+/*********************************************************************
+ Cleanup
+ ********************************************************************/
+
+ void free_attr_list( char **list )
+{
+ int i = 0;
+
+ if ( !list )
+ return;
+
+ while ( list[i] ) {
+ SAFE_FREE( list[i] );
+ i+=1;
+ }
+
+ SAFE_FREE( list );
+}
+
+/*******************************************************************
+ find the ldap password
+******************************************************************/
+static BOOL fetch_ldap_pw(char **dn, char** pw)
+{
+ char *key = NULL;
+ size_t size;
+
+ *dn = smb_xstrdup(lp_ldap_admin_dn());
+
+ if (asprintf(&key, "%s/%s", SECRETS_LDAP_BIND_PW, *dn) < 0) {
+ SAFE_FREE(*dn);
+ DEBUG(0, ("fetch_ldap_pw: asprintf failed!\n"));
+ }
+
+ *pw=secrets_fetch(key, &size);
+ SAFE_FREE(key);
+
+ if (!size) {
+ /* Upgrade 2.2 style entry */
+ char *p;
+ char* old_style_key = strdup(*dn);
+ char *data;
+ fstring old_style_pw;
+
+ if (!old_style_key) {
+ DEBUG(0, ("fetch_ldap_pw: strdup failed!\n"));
+ return False;
+ }
+
+ for (p=old_style_key; *p; p++)
+ if (*p == ',') *p = '/';
+
+ data=secrets_fetch(old_style_key, &size);
+ if (!size && size < sizeof(old_style_pw)) {
+ DEBUG(0,("fetch_ldap_pw: neither ldap secret retrieved!\n"));
+ SAFE_FREE(old_style_key);
+ SAFE_FREE(*dn);
+ return False;
+ }
+
+ size = MIN(size, sizeof(fstring)-1);
+ strncpy(old_style_pw, data, size);
+ old_style_pw[size] = 0;
+
+ SAFE_FREE(data);
+
+ if (!secrets_store_ldap_pw(*dn, old_style_pw)) {
+ DEBUG(0,("fetch_ldap_pw: ldap secret could not be upgraded!\n"));
+ SAFE_FREE(old_style_key);
+ SAFE_FREE(*dn);
+ return False;
+ }
+ if (!secrets_delete(old_style_key)) {
+ DEBUG(0,("fetch_ldap_pw: old ldap secret could not be deleted!\n"));
+ }
+
+ SAFE_FREE(old_style_key);
+
+ *pw = smb_xstrdup(old_style_pw);
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Search an attribute and return the first value found.
+******************************************************************/
+
+ BOOL smbldap_get_single_attribute (LDAP * ldap_struct, LDAPMessage * entry,
+ const char *attribute, char *value,
+ int max_len)
+{
+ char **values;
+
+ if ( !attribute )
+ return False;
+
+ value[0] = '\0';
+
+ if ((values = ldap_get_values (ldap_struct, entry, attribute)) == NULL) {
+ DEBUG (10, ("smbldap_get_single_attribute: [%s] = [<does not exist>]\n", attribute));
+
+ return False;
+ }
+
+ if (convert_string(CH_UTF8, CH_UNIX,values[0], -1, value, max_len, False) == (size_t)-1) {
+ DEBUG(1, ("smbldap_get_single_attribute: string conversion of [%s] = [%s] failed!\n",
+ attribute, values[0]));
+ ldap_value_free(values);
+ return False;
+ }
+
+ ldap_value_free(values);
+#ifdef DEBUG_PASSWORDS
+ DEBUG (100, ("smbldap_get_single_attribute: [%s] = [%s]\n", attribute, value));
+#endif
+ return True;
+}
+
+ BOOL smbldap_get_single_pstring (LDAP * ldap_struct, LDAPMessage * entry,
+ const char *attribute, pstring value)
+{
+ return smbldap_get_single_attribute(ldap_struct, entry,
+ attribute, value,
+ sizeof(pstring));
+}
+
+/************************************************************************
+ Routine to manage the LDAPMod structure array
+ manage memory used by the array, by each struct, and values
+ ***********************************************************************/
+
+ void smbldap_set_mod (LDAPMod *** modlist, int modop, const char *attribute, const char *value)
+{
+ LDAPMod **mods;
+ int i;
+ int j;
+
+ mods = *modlist;
+
+ /* sanity checks on the mod values */
+
+ if (attribute == NULL || *attribute == '\0')
+ return;
+#if 0 /* commented out after discussion with abartlet. Do not reenable.
+ left here so other so re-add similar code --jerry */
+ if (value == NULL || *value == '\0')
+ return;
+#endif
+
+ if (mods == NULL)
+ {
+ mods = (LDAPMod **) malloc(sizeof(LDAPMod *));
+ if (mods == NULL)
+ {
+ DEBUG(0, ("make_a_mod: out of memory!\n"));
+ return;
+ }
+ mods[0] = NULL;
+ }
+
+ for (i = 0; mods[i] != NULL; ++i) {
+ if (mods[i]->mod_op == modop && strequal(mods[i]->mod_type, attribute))
+ break;
+ }
+
+ if (mods[i] == NULL)
+ {
+ mods = (LDAPMod **) Realloc (mods, (i + 2) * sizeof (LDAPMod *));
+ if (mods == NULL)
+ {
+ DEBUG(0, ("make_a_mod: out of memory!\n"));
+ return;
+ }
+ mods[i] = (LDAPMod *) malloc(sizeof(LDAPMod));
+ if (mods[i] == NULL)
+ {
+ DEBUG(0, ("make_a_mod: out of memory!\n"));
+ return;
+ }
+ mods[i]->mod_op = modop;
+ mods[i]->mod_values = NULL;
+ mods[i]->mod_type = strdup(attribute);
+ mods[i + 1] = NULL;
+ }
+
+ if (value != NULL)
+ {
+ char *utf8_value = NULL;
+
+ j = 0;
+ if (mods[i]->mod_values != NULL) {
+ for (; mods[i]->mod_values[j] != NULL; j++);
+ }
+ mods[i]->mod_values = (char **)Realloc(mods[i]->mod_values,
+ (j + 2) * sizeof (char *));
+
+ if (mods[i]->mod_values == NULL) {
+ DEBUG (0, ("make_a_mod: Memory allocation failure!\n"));
+ return;
+ }
+
+ if (push_utf8_allocate(&utf8_value, value) == (size_t)-1) {
+ DEBUG (0, ("make_a_mod: String conversion failure!\n"));
+ return;
+ }
+
+ mods[i]->mod_values[j] = utf8_value;
+
+ mods[i]->mod_values[j + 1] = NULL;
+ }
+ *modlist = mods;
+}
+
+/**********************************************************************
+ Set attribute to newval in LDAP, regardless of what value the
+ attribute had in LDAP before.
+*********************************************************************/
+
+ void smbldap_make_mod(LDAP *ldap_struct, LDAPMessage *existing,
+ LDAPMod ***mods,
+ const char *attribute, const char *newval)
+{
+ char oldval[2048]; /* current largest allowed value is mungeddial */
+ BOOL existed;
+
+ if (existing != NULL) {
+ existed = smbldap_get_single_attribute(ldap_struct, existing, attribute, oldval, sizeof(oldval));
+ } else {
+ existed = False;
+ *oldval = '\0';
+ }
+
+ /* all of our string attributes are case insensitive */
+
+ if (existed && newval && (StrCaseCmp(oldval, newval) == 0)) {
+
+ /* Believe it or not, but LDAP will deny a delete and
+ an add at the same time if the values are the
+ same... */
+ return;
+ }
+
+ if (existed) {
+ /* There has been no value before, so don't delete it.
+ * Here's a possible race: We might end up with
+ * duplicate attributes */
+ /* By deleting exactly the value we found in the entry this
+ * should be race-free in the sense that the LDAP-Server will
+ * deny the complete operation if somebody changed the
+ * attribute behind our back. */
+ /* This will also allow modifying single valued attributes
+ * in Novell NDS. In NDS you have to first remove attribute and then
+ * you could add new value */
+
+ smbldap_set_mod(mods, LDAP_MOD_DELETE, attribute, oldval);
+ }
+
+ /* Regardless of the real operation (add or modify)
+ we add the new value here. We rely on deleting
+ the old value, should it exist. */
+
+ if ((newval != NULL) && (strlen(newval) > 0)) {
+ smbldap_set_mod(mods, LDAP_MOD_ADD, attribute, newval);
+ }
+}
+
+/**********************************************************************
+ Some varients of the LDAP rebind code do not pass in the third 'arg'
+ pointer to a void*, so we try and work around it by assuming that the
+ value of the 'LDAP *' pointer is the same as the one we had passed in
+ **********************************************************************/
+
+struct smbldap_state_lookup {
+ LDAP *ld;
+ struct smbldap_state *smbldap_state;
+ struct smbldap_state_lookup *prev, *next;
+};
+
+static struct smbldap_state_lookup *smbldap_state_lookup_list;
+
+static struct smbldap_state *smbldap_find_state(LDAP *ld)
+{
+ struct smbldap_state_lookup *t;
+
+ for (t = smbldap_state_lookup_list; t; t = t->next) {
+ if (t->ld == ld) {
+ return t->smbldap_state;
+ }
+ }
+ return NULL;
+}
+
+static void smbldap_delete_state(struct smbldap_state *smbldap_state)
+{
+ struct smbldap_state_lookup *t;
+
+ for (t = smbldap_state_lookup_list; t; t = t->next) {
+ if (t->smbldap_state == smbldap_state) {
+ DLIST_REMOVE(smbldap_state_lookup_list, t);
+ SAFE_FREE(t);
+ return;
+ }
+ }
+}
+
+static void smbldap_store_state(LDAP *ld, struct smbldap_state *smbldap_state)
+{
+ struct smbldap_state *tmp_ldap_state;
+ struct smbldap_state_lookup *t;
+ struct smbldap_state_lookup *tmp;
+
+ if ((tmp_ldap_state = smbldap_find_state(ld))) {
+ SMB_ASSERT(tmp_ldap_state == smbldap_state);
+ return;
+ }
+
+ t = smb_xmalloc(sizeof(*t));
+ ZERO_STRUCTP(t);
+
+ DLIST_ADD_END(smbldap_state_lookup_list, t, tmp);
+ t->ld = ld;
+ t->smbldap_state = smbldap_state;
+}
+
+/*******************************************************************
+ open a connection to the ldap server.
+******************************************************************/
+static int smbldap_open_connection (struct smbldap_state *ldap_state)
+
+{
+ int rc = LDAP_SUCCESS;
+ int version;
+ BOOL ldap_v3 = False;
+ LDAP **ldap_struct = &ldap_state->ldap_struct;
+
+#ifdef HAVE_LDAP_INITIALIZE
+ DEBUG(10, ("smbldap_open_connection: %s\n", ldap_state->uri));
+
+ if ((rc = ldap_initialize(ldap_struct, ldap_state->uri)) != LDAP_SUCCESS) {
+ DEBUG(0, ("ldap_initialize: %s\n", ldap_err2string(rc)));
+ return rc;
+ }
+#else
+
+ /* Parse the string manually */
+
+ {
+ int port = 0;
+ fstring protocol;
+ fstring host;
+ const char *p = ldap_state->uri;
+ SMB_ASSERT(sizeof(protocol)>10 && sizeof(host)>254);
+
+ /* skip leading "URL:" (if any) */
+ if ( strnequal( p, "URL:", 4 ) ) {
+ p += 4;
+ }
+
+ sscanf(p, "%10[^:]://%254[^:/]:%d", protocol, host, &port);
+
+ if (port == 0) {
+ if (strequal(protocol, "ldap")) {
+ port = LDAP_PORT;
+ } else if (strequal(protocol, "ldaps")) {
+ port = LDAPS_PORT;
+ } else {
+ DEBUG(0, ("unrecognised protocol (%s)!\n", protocol));
+ }
+ }
+
+ if ((*ldap_struct = ldap_init(host, port)) == NULL) {
+ DEBUG(0, ("ldap_init failed !\n"));
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if (strequal(protocol, "ldaps")) {
+#ifdef LDAP_OPT_X_TLS
+ int tls = LDAP_OPT_X_TLS_HARD;
+ if (ldap_set_option (*ldap_struct, LDAP_OPT_X_TLS, &tls) != LDAP_SUCCESS)
+ {
+ DEBUG(0, ("Failed to setup a TLS session\n"));
+ }
+
+ DEBUG(3,("LDAPS option set...!\n"));
+#else
+ DEBUG(0,("smbldap_open_connection: Secure connection not supported by LDAP client libraries!\n"));
+ return LDAP_OPERATIONS_ERROR;
+#endif
+ }
+ }
+#endif
+
+ /* Store the LDAP pointer in a lookup list */
+
+ smbldap_store_state(*ldap_struct, ldap_state);
+
+ /* Upgrade to LDAPv3 if possible */
+
+ if (ldap_get_option(*ldap_struct, LDAP_OPT_PROTOCOL_VERSION, &version) == LDAP_OPT_SUCCESS)
+ {
+ if (version != LDAP_VERSION3)
+ {
+ version = LDAP_VERSION3;
+ if (ldap_set_option (*ldap_struct, LDAP_OPT_PROTOCOL_VERSION, &version) == LDAP_OPT_SUCCESS) {
+ ldap_v3 = True;
+ }
+ } else {
+ ldap_v3 = True;
+ }
+ }
+
+ if (lp_ldap_ssl() == LDAP_SSL_START_TLS) {
+#ifdef LDAP_OPT_X_TLS
+ if (ldap_v3) {
+ if ((rc = ldap_start_tls_s (*ldap_struct, NULL, NULL)) != LDAP_SUCCESS)
+ {
+ DEBUG(0,("Failed to issue the StartTLS instruction: %s\n",
+ ldap_err2string(rc)));
+ return rc;
+ }
+ DEBUG (3, ("StartTLS issued: using a TLS connection\n"));
+ } else {
+
+ DEBUG(0, ("Need LDAPv3 for Start TLS\n"));
+ return LDAP_OPERATIONS_ERROR;
+ }
+#else
+ DEBUG(0,("smbldap_open_connection: StartTLS not supported by LDAP client libraries!\n"));
+ return LDAP_OPERATIONS_ERROR;
+#endif
+ }
+
+ DEBUG(2, ("smbldap_open_connection: connection opened\n"));
+ return rc;
+}
+
+
+/*******************************************************************
+ a rebind function for authenticated referrals
+ This version takes a void* that we can shove useful stuff in :-)
+******************************************************************/
+#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
+#else
+static int rebindproc_with_state (LDAP * ld, char **whop, char **credp,
+ int *methodp, int freeit, void *arg)
+{
+ struct smbldap_state *ldap_state = arg;
+
+ /** @TODO Should we be doing something to check what servers we rebind to?
+ Could we get a referral to a machine that we don't want to give our
+ username and password to? */
+
+ if (freeit) {
+ SAFE_FREE(*whop);
+ memset(*credp, '\0', strlen(*credp));
+ SAFE_FREE(*credp);
+ } else {
+ DEBUG(5,("rebind_proc_with_state: Rebinding as \"%s\"\n",
+ ldap_state->bind_dn));
+
+ *whop = strdup(ldap_state->bind_dn);
+ if (!*whop) {
+ return LDAP_NO_MEMORY;
+ }
+ *credp = strdup(ldap_state->bind_secret);
+ if (!*credp) {
+ SAFE_FREE(*whop);
+ return LDAP_NO_MEMORY;
+ }
+ *methodp = LDAP_AUTH_SIMPLE;
+ }
+
+ gettimeofday(&(ldap_state->last_rebind),NULL);
+
+ return 0;
+}
+#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
+
+/*******************************************************************
+ a rebind function for authenticated referrals
+ This version takes a void* that we can shove useful stuff in :-)
+ and actually does the connection.
+******************************************************************/
+#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
+static int rebindproc_connect_with_state (LDAP *ldap_struct,
+ LDAP_CONST char *url,
+ ber_tag_t request,
+ ber_int_t msgid, void *arg)
+{
+ struct smbldap_state *ldap_state = arg;
+ int rc;
+ DEBUG(5,("rebindproc_connect_with_state: Rebinding as \"%s\"\n",
+ ldap_state->bind_dn));
+
+ /** @TODO Should we be doing something to check what servers we rebind to?
+ Could we get a referral to a machine that we don't want to give our
+ username and password to? */
+
+ rc = ldap_simple_bind_s(ldap_struct, ldap_state->bind_dn, ldap_state->bind_secret);
+
+ gettimeofday(&(ldap_state->last_rebind),NULL);
+
+ return rc;
+}
+#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
+
+/*******************************************************************
+ Add a rebind function for authenticated referrals
+******************************************************************/
+#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
+#else
+# if LDAP_SET_REBIND_PROC_ARGS == 2
+static int rebindproc (LDAP *ldap_struct, char **whop, char **credp,
+ int *method, int freeit )
+{
+ struct smbldap_state *ldap_state = smbldap_find_state(ldap_struct);
+
+ return rebindproc_with_state(ldap_struct, whop, credp,
+ method, freeit, ldap_state);
+
+}
+# endif /*LDAP_SET_REBIND_PROC_ARGS == 2*/
+#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
+
+/*******************************************************************
+ a rebind function for authenticated referrals
+ this also does the connection, but no void*.
+******************************************************************/
+#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
+# if LDAP_SET_REBIND_PROC_ARGS == 2
+static int rebindproc_connect (LDAP * ld, LDAP_CONST char *url, int request,
+ ber_int_t msgid)
+{
+ struct smbldap_state *ldap_state = smbldap_find_state(ld);
+
+ return rebindproc_connect_with_state(ld, url, (ber_tag_t)request, msgid,
+ ldap_state);
+}
+# endif /*LDAP_SET_REBIND_PROC_ARGS == 2*/
+#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
+
+/*******************************************************************
+ connect to the ldap server under system privilege.
+******************************************************************/
+static int smbldap_connect_system(struct smbldap_state *ldap_state, LDAP * ldap_struct)
+{
+ int rc;
+ char *ldap_dn;
+ char *ldap_secret;
+
+ /* get the password */
+ if (!fetch_ldap_pw(&ldap_dn, &ldap_secret))
+ {
+ DEBUG(0, ("ldap_connect_system: Failed to retrieve password from secrets.tdb\n"));
+ return LDAP_INVALID_CREDENTIALS;
+ }
+
+ ldap_state->bind_dn = ldap_dn;
+ ldap_state->bind_secret = ldap_secret;
+
+ /* removed the sasl_bind_s "EXTERNAL" stuff, as my testsuite
+ (OpenLDAP) doesnt' seem to support it */
+
+ DEBUG(10,("ldap_connect_system: Binding to ldap server %s as \"%s\"\n",
+ ldap_state->uri, ldap_dn));
+
+#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
+# if LDAP_SET_REBIND_PROC_ARGS == 2
+ ldap_set_rebind_proc(ldap_struct, &rebindproc_connect);
+# endif
+# if LDAP_SET_REBIND_PROC_ARGS == 3
+ ldap_set_rebind_proc(ldap_struct, &rebindproc_connect_with_state, (void *)ldap_state);
+# endif
+#else /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
+# if LDAP_SET_REBIND_PROC_ARGS == 2
+ ldap_set_rebind_proc(ldap_struct, &rebindproc);
+# endif
+# if LDAP_SET_REBIND_PROC_ARGS == 3
+ ldap_set_rebind_proc(ldap_struct, &rebindproc_with_state, (void *)ldap_state);
+# endif
+#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
+
+ rc = ldap_simple_bind_s(ldap_struct, ldap_dn, ldap_secret);
+
+ if (rc != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(ldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
+ &ld_error);
+ DEBUG(ldap_state->num_failures ? 2 : 0,
+ ("failed to bind to server with dn= %s Error: %s\n\t%s\n",
+ ldap_dn ? ldap_dn : "(unknown)", ldap_err2string(rc),
+ ld_error ? ld_error : "(unknown)"));
+ SAFE_FREE(ld_error);
+ ldap_state->num_failures++;
+ return rc;
+ }
+
+ ldap_state->num_failures = 0;
+
+ DEBUG(3, ("ldap_connect_system: succesful connection to the LDAP server\n"));
+ return rc;
+}
+
+/**********************************************************************
+Connect to LDAP server (called before every ldap operation)
+*********************************************************************/
+static int smbldap_open(struct smbldap_state *ldap_state)
+{
+ int rc;
+ SMB_ASSERT(ldap_state);
+
+#ifndef NO_LDAP_SECURITY
+ if (geteuid() != 0) {
+ DEBUG(0, ("smbldap_open: cannot access LDAP when not root..\n"));
+ return LDAP_INSUFFICIENT_ACCESS;
+ }
+#endif
+
+ if ((ldap_state->ldap_struct != NULL) && ((ldap_state->last_ping + SMBLDAP_DONT_PING_TIME) < time(NULL))) {
+ struct sockaddr_un addr;
+ socklen_t len = sizeof(addr);
+ int sd;
+ if (ldap_get_option(ldap_state->ldap_struct, LDAP_OPT_DESC, &sd) == 0 &&
+ getpeername(sd, (struct sockaddr *) &addr, &len) < 0) {
+ /* the other end has died. reopen. */
+ ldap_unbind_ext(ldap_state->ldap_struct, NULL, NULL);
+ ldap_state->ldap_struct = NULL;
+ ldap_state->last_ping = (time_t)0;
+ } else {
+ ldap_state->last_ping = time(NULL);
+ }
+ }
+
+ if (ldap_state->ldap_struct != NULL) {
+ DEBUG(11,("smbldap_open: already connected to the LDAP server\n"));
+ return LDAP_SUCCESS;
+ }
+
+ if ((rc = smbldap_open_connection(ldap_state))) {
+ return rc;
+ }
+
+ if ((rc = smbldap_connect_system(ldap_state, ldap_state->ldap_struct))) {
+ ldap_unbind_ext(ldap_state->ldap_struct, NULL, NULL);
+ ldap_state->ldap_struct = NULL;
+ return rc;
+ }
+
+
+ ldap_state->last_ping = time(NULL);
+ DEBUG(4,("The LDAP server is succesful connected\n"));
+
+ return LDAP_SUCCESS;
+}
+
+/**********************************************************************
+Disconnect from LDAP server
+*********************************************************************/
+static NTSTATUS smbldap_close(struct smbldap_state *ldap_state)
+{
+ if (!ldap_state)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (ldap_state->ldap_struct != NULL) {
+ ldap_unbind_ext(ldap_state->ldap_struct, NULL, NULL);
+ ldap_state->ldap_struct = NULL;
+ }
+
+ smbldap_delete_state(ldap_state);
+
+ DEBUG(5,("The connection to the LDAP server was closed\n"));
+ /* maybe free the results here --metze */
+
+
+
+ return NT_STATUS_OK;
+}
+
+int smbldap_retry_open(struct smbldap_state *ldap_state, int *attempts)
+{
+ int rc;
+
+ SMB_ASSERT(ldap_state && attempts);
+
+ if (*attempts != 0) {
+ unsigned int sleep_time;
+ uint8 rand_byte;
+
+ /* Sleep for a random timeout */
+ rand_byte = (char)(sys_random());
+
+ sleep_time = (((*attempts)*(*attempts))/2)*rand_byte*2;
+ /* we retry after (0.5, 1, 2, 3, 4.5, 6) seconds
+ on average.
+ */
+ DEBUG(3, ("Sleeping for %u milliseconds before reconnecting\n",
+ sleep_time));
+ smb_msleep(sleep_time);
+ }
+ (*attempts)++;
+
+ if ((rc = smbldap_open(ldap_state))) {
+ DEBUG(1,("Connection to LDAP Server failed for the %d try!\n",*attempts));
+ return rc;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+
+/*********************************************************************
+ ********************************************************************/
+
+int smbldap_search(struct smbldap_state *ldap_state,
+ const char *base, int scope, const char *filter,
+ char *attrs[], int attrsonly,
+ LDAPMessage **res)
+{
+ int rc = LDAP_SERVER_DOWN;
+ int attempts = 0;
+ char *utf8_filter;
+
+ SMB_ASSERT(ldap_state);
+
+ DEBUG(5,("smbldap_search: base => [%s], filter => [%s], scope => [%d]\n",
+ base, filter, scope));
+
+ if (ldap_state->last_rebind.tv_sec > 0) {
+ struct timeval tval;
+ int tdiff = 0;
+ int sleep_time = 0;
+
+ ZERO_STRUCT(tval);
+
+ gettimeofday(&tval,NULL);
+
+ tdiff = 1000000 *(tval.tv_sec - ldap_state->last_rebind.tv_sec) +
+ (tval.tv_usec - ldap_state->last_rebind.tv_usec);
+
+ sleep_time = ((1000*lp_ldap_replication_sleep())-tdiff)/1000;
+
+ if (sleep_time > 0) {
+ /* we wait for the LDAP replication */
+ DEBUG(5,("smbldap_search: waiting %d milliseconds for LDAP replication.\n",sleep_time));
+ smb_msleep(sleep_time);
+ DEBUG(5,("smbldap_search: go on!\n"));
+ ZERO_STRUCT(ldap_state->last_rebind);
+ }
+ }
+
+ if (push_utf8_allocate(&utf8_filter, filter) == (size_t)-1) {
+ return LDAP_NO_MEMORY;
+ }
+
+ while ((rc == LDAP_SERVER_DOWN) && (attempts < SMBLDAP_NUM_RETRIES)) {
+
+ if ((rc = smbldap_retry_open(ldap_state,&attempts)) != LDAP_SUCCESS)
+ continue;
+
+ rc = ldap_search_s(ldap_state->ldap_struct, base, scope,
+ utf8_filter, attrs, attrsonly, res);
+ }
+
+ if (rc == LDAP_SERVER_DOWN) {
+ DEBUG(0,("%s: LDAP server is down!\n",FUNCTION_MACRO));
+ smbldap_close(ldap_state);
+ }
+
+ ldap_state->last_use = time(NULL);
+
+ SAFE_FREE(utf8_filter);
+ return rc;
+}
+
+int smbldap_modify(struct smbldap_state *ldap_state, const char *dn, LDAPMod *attrs[])
+{
+ int rc = LDAP_SERVER_DOWN;
+ int attempts = 0;
+ char *utf8_dn;
+
+ SMB_ASSERT(ldap_state);
+
+ DEBUG(5,("smbldap_modify: dn => [%s]\n", dn ));
+
+ if (push_utf8_allocate(&utf8_dn, dn) == (size_t)-1) {
+ return LDAP_NO_MEMORY;
+ }
+
+ while ((rc == LDAP_SERVER_DOWN) && (attempts < SMBLDAP_NUM_RETRIES)) {
+
+ if ((rc = smbldap_retry_open(ldap_state,&attempts)) != LDAP_SUCCESS)
+ continue;
+
+ rc = ldap_modify_s(ldap_state->ldap_struct, utf8_dn, attrs);
+ }
+
+ if (rc == LDAP_SERVER_DOWN) {
+ DEBUG(0,("%s: LDAP server is down!\n",FUNCTION_MACRO));
+ smbldap_close(ldap_state);
+ }
+
+ ldap_state->last_use = time(NULL);
+
+ SAFE_FREE(utf8_dn);
+ return rc;
+}
+
+int smbldap_add(struct smbldap_state *ldap_state, const char *dn, LDAPMod *attrs[])
+{
+ int rc = LDAP_SERVER_DOWN;
+ int attempts = 0;
+ char *utf8_dn;
+
+ SMB_ASSERT(ldap_state);
+
+ DEBUG(5,("smbldap_add: dn => [%s]\n", dn ));
+
+ if (push_utf8_allocate(&utf8_dn, dn) == (size_t)-1) {
+ return LDAP_NO_MEMORY;
+ }
+
+ while ((rc == LDAP_SERVER_DOWN) && (attempts < SMBLDAP_NUM_RETRIES)) {
+
+ if ((rc = smbldap_retry_open(ldap_state,&attempts)) != LDAP_SUCCESS)
+ continue;
+
+ rc = ldap_add_s(ldap_state->ldap_struct, utf8_dn, attrs);
+ }
+
+ if (rc == LDAP_SERVER_DOWN) {
+ DEBUG(0,("%s: LDAP server is down!\n",FUNCTION_MACRO));
+ smbldap_close(ldap_state);
+ }
+
+ ldap_state->last_use = time(NULL);
+
+ SAFE_FREE(utf8_dn);
+ return rc;
+}
+
+int smbldap_delete(struct smbldap_state *ldap_state, const char *dn)
+{
+ int rc = LDAP_SERVER_DOWN;
+ int attempts = 0;
+ char *utf8_dn;
+
+ SMB_ASSERT(ldap_state);
+
+ DEBUG(5,("smbldap_delete: dn => [%s]\n", dn ));
+
+ if (push_utf8_allocate(&utf8_dn, dn) == (size_t)-1) {
+ return LDAP_NO_MEMORY;
+ }
+
+ while ((rc == LDAP_SERVER_DOWN) && (attempts < SMBLDAP_NUM_RETRIES)) {
+
+ if ((rc = smbldap_retry_open(ldap_state,&attempts)) != LDAP_SUCCESS)
+ continue;
+
+ rc = ldap_delete_s(ldap_state->ldap_struct, utf8_dn);
+ }
+
+ if (rc == LDAP_SERVER_DOWN) {
+ DEBUG(0,("%s: LDAP server is down!\n",FUNCTION_MACRO));
+ smbldap_close(ldap_state);
+ }
+
+ ldap_state->last_use = time(NULL);
+
+ SAFE_FREE(utf8_dn);
+ return rc;
+}
+
+int smbldap_extended_operation(struct smbldap_state *ldap_state,
+ LDAP_CONST char *reqoid, struct berval *reqdata,
+ LDAPControl **serverctrls, LDAPControl **clientctrls,
+ char **retoidp, struct berval **retdatap)
+{
+ int rc = LDAP_SERVER_DOWN;
+ int attempts = 0;
+
+ if (!ldap_state)
+ return (-1);
+
+ while ((rc == LDAP_SERVER_DOWN) && (attempts < SMBLDAP_NUM_RETRIES)) {
+
+ if ((rc = smbldap_retry_open(ldap_state,&attempts)) != LDAP_SUCCESS)
+ continue;
+
+ rc = ldap_extended_operation_s(ldap_state->ldap_struct, reqoid, reqdata,
+ serverctrls, clientctrls, retoidp, retdatap);
+ }
+
+ if (rc == LDAP_SERVER_DOWN) {
+ DEBUG(0,("%s: LDAP server is down!\n",FUNCTION_MACRO));
+ smbldap_close(ldap_state);
+ }
+
+ ldap_state->last_use = time(NULL);
+
+ return rc;
+}
+
+/*******************************************************************
+ run the search by name.
+******************************************************************/
+int smbldap_search_suffix (struct smbldap_state *ldap_state, const char *filter,
+ char **search_attr, LDAPMessage ** result)
+{
+ int scope = LDAP_SCOPE_SUBTREE;
+ int rc;
+
+ rc = smbldap_search(ldap_state, lp_ldap_suffix(), scope, filter, search_attr, 0, result);
+
+ if (rc != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(ldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
+ &ld_error);
+ DEBUG(0,("smbldap_search_suffix: Problem during the LDAP search: %s (%s)\n",
+ ld_error?ld_error:"(unknown)", ldap_err2string (rc)));
+ SAFE_FREE(ld_error);
+ }
+
+ return rc;
+}
+
+static void smbldap_idle_fn(void **data, time_t *interval, time_t now)
+{
+ struct smbldap_state *state = (struct smbldap_state *)(*data);
+
+ if (state->ldap_struct == NULL) {
+ DEBUG(10,("ldap connection not connected...\n"));
+ return;
+ }
+
+ if ((state->last_use+SMBLDAP_IDLE_TIME) > now) {
+ DEBUG(10,("ldap connection not idle...\n"));
+ return;
+ }
+
+ DEBUG(7,("ldap connection idle...closing connection\n"));
+ smbldap_close(state);
+}
+
+/**********************************************************************
+ Housekeeping
+ *********************************************************************/
+
+void smbldap_free_struct(struct smbldap_state **ldap_state)
+{
+ smbldap_close(*ldap_state);
+
+ if ((*ldap_state)->bind_secret) {
+ memset((*ldap_state)->bind_secret, '\0', strlen((*ldap_state)->bind_secret));
+ }
+
+ SAFE_FREE((*ldap_state)->bind_dn);
+ SAFE_FREE((*ldap_state)->bind_secret);
+
+ smb_unregister_idle_event((*ldap_state)->event_id);
+
+ *ldap_state = NULL;
+
+ /* No need to free any further, as it is talloc()ed */
+}
+
+
+/**********************************************************************
+ Intitalise the 'general' ldap structures, on which ldap operations may be conducted
+ *********************************************************************/
+
+NTSTATUS smbldap_init(TALLOC_CTX *mem_ctx, const char *location, struct smbldap_state **smbldap_state)
+{
+ *smbldap_state = talloc_zero(mem_ctx, sizeof(**smbldap_state));
+ if (!*smbldap_state) {
+ DEBUG(0, ("talloc() failed for ldapsam private_data!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (location) {
+ (*smbldap_state)->uri = talloc_strdup(mem_ctx, location);
+ } else {
+ (*smbldap_state)->uri = "ldap://localhost";
+ }
+
+ (*smbldap_state)->event_id =
+ smb_register_idle_event(smbldap_idle_fn, (void *)(*smbldap_state),
+ SMBLDAP_IDLE_TIME);
+
+ if ((*smbldap_state)->event_id == SMB_EVENT_ID_INVALID) {
+ DEBUG(0,("Failed to register LDAP idle event!\n"));
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Return a copy of the DN for a LDAPMessage. Convert from utf8 to CH_UNIX.
+********************************************************************/
+
+char *smbldap_get_dn(LDAP *ld, LDAPMessage *entry)
+{
+ char *utf8_dn, *unix_dn;
+
+ utf8_dn = ldap_get_dn(ld, entry);
+ if (!utf8_dn) {
+ DEBUG (5, ("smbldap_get_dn: ldap_get_dn failed\n"));
+ return NULL;
+ }
+ if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
+ DEBUG (0, ("smbldap_get_dn: String conversion failure utf8 [%s]\n", utf8_dn));
+ return NULL;
+ }
+ ldap_memfree(utf8_dn);
+ return unix_dn;
+}
diff --git a/source/lib/smbldap_util.c b/source/lib/smbldap_util.c
new file mode 100644
index 00000000000..f6097599bc5
--- /dev/null
+++ b/source/lib/smbldap_util.c
@@ -0,0 +1,203 @@
+/*
+ Unix SMB/CIFS mplementation.
+ LDAP protocol helper functions for SAMBA
+ Copyright (C) Jean François Micouleau 1998
+ Copyright (C) Gerald Carter 2001-2003
+ Copyright (C) Shahms King 2001
+ Copyright (C) Andrew Bartlett 2002-2003
+ Copyright (C) Stefan (metze) Metzmacher 2002-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+#include "smbldap.h"
+
+/**********************************************************************
+ Add the sambaDomain to LDAP, so we don't have to search for this stuff
+ again. This is a once-add operation for now.
+
+ TODO: Add other attributes, and allow modification.
+*********************************************************************/
+static NTSTATUS add_new_domain_info(struct smbldap_state *ldap_state,
+ const char *domain_name)
+{
+ fstring sid_string;
+ fstring algorithmic_rid_base_string;
+ pstring filter, dn;
+ LDAPMod **mods = NULL;
+ int rc;
+ int ldap_op;
+ LDAPMessage *result = NULL;
+ int num_result;
+ char **attr_list;
+ uid_t u_low, u_high;
+ gid_t g_low, g_high;
+ uint32 rid_low, rid_high;
+
+ slprintf (filter, sizeof (filter) - 1, "(&(%s=%s)(objectclass=%s))",
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_DOMAIN),
+ domain_name, LDAP_OBJ_DOMINFO);
+
+ attr_list = get_attr_list( dominfo_attr_list );
+ rc = smbldap_search_suffix(ldap_state, filter, attr_list, &result);
+ free_attr_list( attr_list );
+
+ if (rc != LDAP_SUCCESS) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ num_result = ldap_count_entries(ldap_state->ldap_struct, result);
+
+ if (num_result > 1) {
+ DEBUG (0, ("More than domain with that name exists: bailing out!\n"));
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Check if we need to add an entry */
+ DEBUG(3,("Adding new domain\n"));
+ ldap_op = LDAP_MOD_ADD;
+
+ pstr_sprintf(dn, "%s=%s,%s", get_attr_key2string(dominfo_attr_list, LDAP_ATTR_DOMAIN),
+ domain_name, lp_ldap_suffix());
+
+ /* Free original search */
+ ldap_msgfree(result);
+
+ /* make the changes - the entry *must* not already have samba attributes */
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, get_attr_key2string(dominfo_attr_list, LDAP_ATTR_DOMAIN),
+ domain_name);
+
+ /* If we don't have an entry, then ask secrets.tdb for what it thinks.
+ It may choose to make it up */
+
+ sid_to_string(sid_string, get_global_sam_sid());
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, get_attr_key2string(dominfo_attr_list, LDAP_ATTR_DOM_SID), sid_string);
+
+ slprintf(algorithmic_rid_base_string, sizeof(algorithmic_rid_base_string) - 1, "%i", algorithmic_rid_base());
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, get_attr_key2string(dominfo_attr_list, LDAP_ATTR_ALGORITHMIC_RID_BASE),
+ algorithmic_rid_base_string);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectclass", LDAP_OBJ_DOMINFO);
+
+ /* add the sambaNext[User|Group]Rid attributes if the idmap ranges are set.
+ TODO: fix all the places where the line between idmap and normal operations
+ needed by smbd gets fuzzy --jerry 2003-08-11 */
+
+ if ( lp_idmap_uid(&u_low, &u_high) && lp_idmap_gid(&g_low, &g_high)
+ && get_free_rid_range(&rid_low, &rid_high) )
+ {
+ fstring rid_str;
+
+ fstr_sprintf( rid_str, "%i", rid_high|USER_RID_TYPE );
+ DEBUG(10,("setting next available user rid [%s]\n", rid_str));
+ smbldap_set_mod(&mods, LDAP_MOD_ADD,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_USERRID),
+ rid_str);
+
+ fstr_sprintf( rid_str, "%i", rid_high|GROUP_RID_TYPE );
+ DEBUG(10,("setting next available group rid [%s]\n", rid_str));
+ smbldap_set_mod(&mods, LDAP_MOD_ADD,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_GROUPRID),
+ rid_str);
+
+ }
+
+
+ switch(ldap_op)
+ {
+ case LDAP_MOD_ADD:
+ rc = smbldap_add(ldap_state, dn, mods);
+ break;
+ case LDAP_MOD_REPLACE:
+ rc = smbldap_modify(ldap_state, dn, mods);
+ break;
+ default:
+ DEBUG(0,("Wrong LDAP operation type: %d!\n", ldap_op));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (rc!=LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(ldap_state->ldap_struct, LDAP_OPT_ERROR_STRING, &ld_error);
+ DEBUG(1,("failed to %s domain dn= %s with: %s\n\t%s\n",
+ ldap_op == LDAP_MOD_ADD ? "add" : "modify",
+ dn, ldap_err2string(rc),
+ ld_error?ld_error:"unknown"));
+ SAFE_FREE(ld_error);
+
+ ldap_mods_free(mods, True);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(2,("added: domain = %s in the LDAP database\n", domain_name));
+ ldap_mods_free(mods, True);
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+Search for the domain info entry
+*********************************************************************/
+NTSTATUS smbldap_search_domain_info(struct smbldap_state *ldap_state,
+ LDAPMessage ** result, const char *domain_name,
+ BOOL try_add)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ pstring filter;
+ int rc;
+ char **attr_list;
+ int count;
+
+ pstr_sprintf(filter, "(&(objectClass=%s)(%s=%s))",
+ LDAP_OBJ_DOMINFO,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_DOMAIN),
+ domain_name);
+
+ DEBUG(2, ("Searching for:[%s]\n", filter));
+
+
+ attr_list = get_attr_list( dominfo_attr_list );
+ rc = smbldap_search_suffix(ldap_state, filter, attr_list , result);
+ free_attr_list( attr_list );
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(2,("Problem during LDAPsearch: %s\n", ldap_err2string (rc)));
+ DEBUG(2,("Query was: %s, %s\n", lp_ldap_suffix(), filter));
+ } else if (ldap_count_entries(ldap_state->ldap_struct, *result) < 1) {
+ DEBUG(3, ("Got no domain info entries for domain\n"));
+ ldap_msgfree(*result);
+ *result = NULL;
+ if (try_add && NT_STATUS_IS_OK(ret = add_new_domain_info(ldap_state, domain_name))) {
+ return smbldap_search_domain_info(ldap_state, result, domain_name, False);
+ }
+ else {
+ DEBUG(0, ("Adding domain info for %s failed with %s\n",
+ domain_name, nt_errstr(ret)));
+ return ret;
+ }
+ } else if ((count = ldap_count_entries(ldap_state->ldap_struct, *result)) > 1) {
+ DEBUG(0, ("Got too many (%d) domain info entries for domain %s\n",
+ count, domain_name));
+ ldap_msgfree(*result);
+ *result = NULL;
+ return ret;
+ } else {
+ return NT_STATUS_OK;
+ }
+
+ return ret;
+}
+
diff --git a/source/lib/smbrun.c b/source/lib/smbrun.c
new file mode 100644
index 00000000000..592543bc43b
--- /dev/null
+++ b/source/lib/smbrun.c
@@ -0,0 +1,180 @@
+/*
+ Unix SMB/CIFS implementation.
+ run a command as a specified user
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* need to move this from here!! need some sleep ... */
+struct current_user current_user;
+
+/****************************************************************************
+This is a utility function of smbrun().
+****************************************************************************/
+
+static int setup_out_fd(void)
+{
+ int fd;
+ pstring path;
+
+ slprintf(path, sizeof(path)-1, "%s/smb.XXXXXX", tmpdir());
+
+ /* now create the file */
+ fd = smb_mkstemp(path);
+
+ if (fd == -1) {
+ DEBUG(0,("setup_out_fd: Failed to create file %s. (%s)\n",
+ path, strerror(errno) ));
+ return -1;
+ }
+
+ DEBUG(10,("setup_out_fd: Created tmp file %s\n", path ));
+
+ /* Ensure file only kept around by open fd. */
+ unlink(path);
+ return fd;
+}
+
+/****************************************************************************
+run a command being careful about uid/gid handling and putting the output in
+outfd (or discard it if outfd is NULL).
+****************************************************************************/
+
+int smbrun(char *cmd, int *outfd)
+{
+ pid_t pid;
+ uid_t uid = current_user.uid;
+ gid_t gid = current_user.gid;
+
+ /*
+ * Lose any kernel oplock capabilities we may have.
+ */
+ oplock_set_capability(False, False);
+
+ /* point our stdout at the file we want output to go into */
+
+ if (outfd && ((*outfd = setup_out_fd()) == -1)) {
+ return -1;
+ }
+
+ /* in this method we will exec /bin/sh with the correct
+ arguments, after first setting stdout to point at the file */
+
+ /*
+ * We need to temporarily stop CatchChild from eating
+ * SIGCLD signals as it also eats the exit status code. JRA.
+ */
+
+ CatchChildLeaveStatus();
+
+ if ((pid=sys_fork()) < 0) {
+ DEBUG(0,("smbrun: fork failed with error %s\n", strerror(errno) ));
+ CatchChild();
+ if (outfd) {
+ close(*outfd);
+ *outfd = -1;
+ }
+ return errno;
+ }
+
+ if (pid) {
+ /*
+ * Parent.
+ */
+ int status=0;
+ pid_t wpid;
+
+
+ /* the parent just waits for the child to exit */
+ while((wpid = sys_waitpid(pid,&status,0)) < 0) {
+ if(errno == EINTR) {
+ errno = 0;
+ continue;
+ }
+ break;
+ }
+
+ CatchChild();
+
+ if (wpid != pid) {
+ DEBUG(2,("waitpid(%d) : %s\n",(int)pid,strerror(errno)));
+ if (outfd) {
+ close(*outfd);
+ *outfd = -1;
+ }
+ return -1;
+ }
+
+ /* Reset the seek pointer. */
+ if (outfd) {
+ sys_lseek(*outfd, 0, SEEK_SET);
+ }
+
+#if defined(WIFEXITED) && defined(WEXITSTATUS)
+ if (WIFEXITED(status)) {
+ return WEXITSTATUS(status);
+ }
+#endif
+
+ return status;
+ }
+
+ CatchChild();
+
+ /* we are in the child. we exec /bin/sh to do the work for us. we
+ don't directly exec the command we want because it may be a
+ pipeline or anything else the config file specifies */
+
+ /* point our stdout at the file we want output to go into */
+ if (outfd) {
+ close(1);
+ if (sys_dup2(*outfd,1) != 1) {
+ DEBUG(2,("Failed to create stdout file descriptor\n"));
+ close(*outfd);
+ exit(80);
+ }
+ }
+
+ /* now completely lose our privileges. This is a fairly paranoid
+ way of doing it, but it does work on all systems that I know of */
+
+ become_user_permanently(uid, gid);
+
+ if (getuid() != uid || geteuid() != uid ||
+ getgid() != gid || getegid() != gid) {
+ /* we failed to lose our privileges - do not execute
+ the command */
+ exit(81); /* we can't print stuff at this stage,
+ instead use exit codes for debugging */
+ }
+
+#ifndef __INSURE__
+ /* close all other file descriptors, leaving only 0, 1 and 2. 0 and
+ 2 point to /dev/null from the startup code */
+ {
+ int fd;
+ for (fd=3;fd<256;fd++) close(fd);
+ }
+#endif
+
+ execl("/bin/sh","sh","-c",cmd,NULL);
+
+ /* not reached */
+ exit(82);
+ return 1;
+}
diff --git a/source/lib/snprintf.c b/source/lib/snprintf.c
new file mode 100644
index 00000000000..5b0cfa1ab33
--- /dev/null
+++ b/source/lib/snprintf.c
@@ -0,0 +1,1021 @@
+/*
+ * NOTE: If you change this file, please merge it into rsync, samba, etc.
+ */
+
+/*
+ * Copyright Patrick Powell 1995
+ * This code is based on code written by Patrick Powell (papowell@astart.com)
+ * It may be used for any purpose as long as this notice remains intact
+ * on all source code distributions
+ */
+
+/**************************************************************
+ * Original:
+ * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
+ * A bombproof version of doprnt (dopr) included.
+ * Sigh. This sort of thing is always nasty do deal with. Note that
+ * the version here does not include floating point...
+ *
+ * snprintf() is used instead of sprintf() as it does limit checks
+ * for string length. This covers a nasty loophole.
+ *
+ * The other functions are there to prevent NULL pointers from
+ * causing nast effects.
+ *
+ * More Recently:
+ * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
+ * This was ugly. It is still ugly. I opted out of floating point
+ * numbers, but the formatter understands just about everything
+ * from the normal C string format, at least as far as I can tell from
+ * the Solaris 2.5 printf(3S) man page.
+ *
+ * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
+ * Ok, added some minimal floating point support, which means this
+ * probably requires libm on most operating systems. Don't yet
+ * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
+ * was pretty badly broken, it just wasn't being exercised in ways
+ * which showed it, so that's been fixed. Also, formated the code
+ * to mutt conventions, and removed dead code left over from the
+ * original. Also, there is now a builtin-test, just compile with:
+ * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
+ * and run snprintf for results.
+ *
+ * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
+ * The PGP code was using unsigned hexadecimal formats.
+ * Unfortunately, unsigned formats simply didn't work.
+ *
+ * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
+ * The original code assumed that both snprintf() and vsnprintf() were
+ * missing. Some systems only have snprintf() but not vsnprintf(), so
+ * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
+ *
+ * Andrew Tridgell (tridge@samba.org) Oct 1998
+ * fixed handling of %.0f
+ * added test for HAVE_LONG_DOUBLE
+ *
+ * tridge@samba.org, idra@samba.org, April 2001
+ * got rid of fcvt code (twas buggy and made testing harder)
+ * added C99 semantics
+ *
+ * date: 2002/12/19 19:56:31; author: herb; state: Exp; lines: +2 -0
+ * actually print args for %g and %e
+ *
+ * date: 2002/06/03 13:37:52; author: jmcd; state: Exp; lines: +8 -0
+ * Since includes.h isn't included here, VA_COPY has to be defined here. I don't
+ * see any include file that is guaranteed to be here, so I'm defining it
+ * locally. Fixes AIX and Solaris builds.
+ *
+ * date: 2002/06/03 03:07:24; author: tridge; state: Exp; lines: +5 -13
+ * put the ifdef for HAVE_VA_COPY in one place rather than in lots of
+ * functions
+ *
+ * date: 2002/05/17 14:51:22; author: jmcd; state: Exp; lines: +21 -4
+ * Fix usage of va_list passed as an arg. Use __va_copy before using it
+ * when it exists.
+ *
+ * date: 2002/04/16 22:38:04; author: idra; state: Exp; lines: +20 -14
+ * Fix incorrect zpadlen handling in fmtfp.
+ * Thanks to Ollie Oldham <ollie.oldham@metro-optix.com> for spotting it.
+ * few mods to make it easier to compile the tests.
+ * addedd the "Ollie" test to the floating point ones.
+ *
+ * Martin Pool (mbp@samba.org) April 2003
+ * Remove NO_CONFIG_H so that the test case can be built within a source
+ * tree with less trouble.
+ * Remove unnecessary SAFE_FREE() definition.
+ *
+ * Martin Pool (mbp@samba.org) May 2003
+ * Put in a prototype for dummy_snprintf() to quiet compiler warnings.
+ *
+ * Move #endif to make sure VA_COPY, LDOUBLE, etc are defined even
+ * if the C library has some snprintf functions already.
+ **************************************************************/
+
+#ifndef NO_CONFIG_H
+#include "config.h"
+#else
+#define NULL 0
+#endif
+
+#ifdef TEST_SNPRINTF /* need math library headers for testing */
+
+/* In test mode, we pretend that this system doesn't have any snprintf
+ * functions, regardless of what config.h says. */
+# undef HAVE_SNPRINTF
+# undef HAVE_VSNPRINTF
+# undef HAVE_C99_VSNPRINTF
+# undef HAVE_ASPRINTF
+# undef HAVE_VASPRINTF
+# include <math.h>
+#endif /* TEST_SNPRINTF */
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+#include <sys/types.h>
+#include <stdarg.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF)
+/* only include stdio.h if we are not re-defining snprintf or vsnprintf */
+#include <stdio.h>
+ /* make the compiler happy with an empty file */
+ void dummy_snprintf(void);
+ void dummy_snprintf(void) {}
+#endif /* HAVE_SNPRINTF, etc */
+
+#ifdef HAVE_LONG_DOUBLE
+#define LDOUBLE long double
+#else
+#define LDOUBLE double
+#endif
+
+#ifdef HAVE_LONG_LONG
+#define LLONG long long
+#else
+#define LLONG long
+#endif
+
+#ifndef VA_COPY
+#ifdef HAVE_VA_COPY
+#define VA_COPY(dest, src) va_copy(dest, src)
+#else
+#ifdef HAVE___VA_COPY
+#define VA_COPY(dest, src) __va_copy(dest, src)
+#else
+#define VA_COPY(dest, src) (dest) = (src)
+#endif
+#endif
+
+/*
+ * dopr(): poor man's version of doprintf
+ */
+
+/* format read states */
+#define DP_S_DEFAULT 0
+#define DP_S_FLAGS 1
+#define DP_S_MIN 2
+#define DP_S_DOT 3
+#define DP_S_MAX 4
+#define DP_S_MOD 5
+#define DP_S_CONV 6
+#define DP_S_DONE 7
+
+/* format flags - Bits */
+#define DP_F_MINUS (1 << 0)
+#define DP_F_PLUS (1 << 1)
+#define DP_F_SPACE (1 << 2)
+#define DP_F_NUM (1 << 3)
+#define DP_F_ZERO (1 << 4)
+#define DP_F_UP (1 << 5)
+#define DP_F_UNSIGNED (1 << 6)
+
+/* Conversion Flags */
+#define DP_C_SHORT 1
+#define DP_C_LONG 2
+#define DP_C_LDOUBLE 3
+#define DP_C_LLONG 4
+
+#define char_to_int(p) ((p)- '0')
+#ifndef MAX
+#define MAX(p,q) (((p) >= (q)) ? (p) : (q))
+#endif
+
+/* yes this really must be a ||. Don't muck with this (tridge) */
+#if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
+
+static size_t dopr(char *buffer, size_t maxlen, const char *format,
+ va_list args_in);
+static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
+ char *value, int flags, int min, int max);
+static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
+ long value, int base, int min, int max, int flags);
+static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
+ LDOUBLE fvalue, int min, int max, int flags);
+static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
+
+static size_t dopr(char *buffer, size_t maxlen, const char *format, va_list args_in)
+{
+ char ch;
+ LLONG value;
+ LDOUBLE fvalue;
+ char *strvalue;
+ int min;
+ int max;
+ int state;
+ int flags;
+ int cflags;
+ size_t currlen;
+ va_list args;
+
+ VA_COPY(args, args_in);
+
+ state = DP_S_DEFAULT;
+ currlen = flags = cflags = min = 0;
+ max = -1;
+ ch = *format++;
+
+ while (state != DP_S_DONE) {
+ if (ch == '\0')
+ state = DP_S_DONE;
+
+ switch(state) {
+ case DP_S_DEFAULT:
+ if (ch == '%')
+ state = DP_S_FLAGS;
+ else
+ dopr_outch (buffer, &currlen, maxlen, ch);
+ ch = *format++;
+ break;
+ case DP_S_FLAGS:
+ switch (ch) {
+ case '-':
+ flags |= DP_F_MINUS;
+ ch = *format++;
+ break;
+ case '+':
+ flags |= DP_F_PLUS;
+ ch = *format++;
+ break;
+ case ' ':
+ flags |= DP_F_SPACE;
+ ch = *format++;
+ break;
+ case '#':
+ flags |= DP_F_NUM;
+ ch = *format++;
+ break;
+ case '0':
+ flags |= DP_F_ZERO;
+ ch = *format++;
+ break;
+ default:
+ state = DP_S_MIN;
+ break;
+ }
+ break;
+ case DP_S_MIN:
+ if (isdigit((unsigned char)ch)) {
+ min = 10*min + char_to_int (ch);
+ ch = *format++;
+ } else if (ch == '*') {
+ min = va_arg (args, int);
+ ch = *format++;
+ state = DP_S_DOT;
+ } else {
+ state = DP_S_DOT;
+ }
+ break;
+ case DP_S_DOT:
+ if (ch == '.') {
+ state = DP_S_MAX;
+ ch = *format++;
+ } else {
+ state = DP_S_MOD;
+ }
+ break;
+ case DP_S_MAX:
+ if (isdigit((unsigned char)ch)) {
+ if (max < 0)
+ max = 0;
+ max = 10*max + char_to_int (ch);
+ ch = *format++;
+ } else if (ch == '*') {
+ max = va_arg (args, int);
+ ch = *format++;
+ state = DP_S_MOD;
+ } else {
+ state = DP_S_MOD;
+ }
+ break;
+ case DP_S_MOD:
+ switch (ch) {
+ case 'h':
+ cflags = DP_C_SHORT;
+ ch = *format++;
+ break;
+ case 'l':
+ cflags = DP_C_LONG;
+ ch = *format++;
+ if (ch == 'l') { /* It's a long long */
+ cflags = DP_C_LLONG;
+ ch = *format++;
+ }
+ break;
+ case 'L':
+ cflags = DP_C_LDOUBLE;
+ ch = *format++;
+ break;
+ default:
+ break;
+ }
+ state = DP_S_CONV;
+ break;
+ case DP_S_CONV:
+ switch (ch) {
+ case 'd':
+ case 'i':
+ if (cflags == DP_C_SHORT)
+ value = va_arg (args, int);
+ else if (cflags == DP_C_LONG)
+ value = va_arg (args, long int);
+ else if (cflags == DP_C_LLONG)
+ value = va_arg (args, LLONG);
+ else
+ value = va_arg (args, int);
+ fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
+ break;
+ case 'o':
+ flags |= DP_F_UNSIGNED;
+ if (cflags == DP_C_SHORT)
+ value = va_arg (args, unsigned int);
+ else if (cflags == DP_C_LONG)
+ value = (long)va_arg (args, unsigned long int);
+ else if (cflags == DP_C_LLONG)
+ value = (long)va_arg (args, unsigned LLONG);
+ else
+ value = (long)va_arg (args, unsigned int);
+ fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
+ break;
+ case 'u':
+ flags |= DP_F_UNSIGNED;
+ if (cflags == DP_C_SHORT)
+ value = va_arg (args, unsigned int);
+ else if (cflags == DP_C_LONG)
+ value = (long)va_arg (args, unsigned long int);
+ else if (cflags == DP_C_LLONG)
+ value = (LLONG)va_arg (args, unsigned LLONG);
+ else
+ value = (long)va_arg (args, unsigned int);
+ fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
+ break;
+ case 'X':
+ flags |= DP_F_UP;
+ case 'x':
+ flags |= DP_F_UNSIGNED;
+ if (cflags == DP_C_SHORT)
+ value = va_arg (args, unsigned int);
+ else if (cflags == DP_C_LONG)
+ value = (long)va_arg (args, unsigned long int);
+ else if (cflags == DP_C_LLONG)
+ value = (LLONG)va_arg (args, unsigned LLONG);
+ else
+ value = (long)va_arg (args, unsigned int);
+ fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
+ break;
+ case 'f':
+ if (cflags == DP_C_LDOUBLE)
+ fvalue = va_arg (args, LDOUBLE);
+ else
+ fvalue = va_arg (args, double);
+ /* um, floating point? */
+ fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
+ break;
+ case 'E':
+ flags |= DP_F_UP;
+ case 'e':
+ if (cflags == DP_C_LDOUBLE)
+ fvalue = va_arg (args, LDOUBLE);
+ else
+ fvalue = va_arg (args, double);
+ fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
+ break;
+ case 'G':
+ flags |= DP_F_UP;
+ case 'g':
+ if (cflags == DP_C_LDOUBLE)
+ fvalue = va_arg (args, LDOUBLE);
+ else
+ fvalue = va_arg (args, double);
+ fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
+ break;
+ case 'c':
+ dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
+ break;
+ case 's':
+ strvalue = va_arg (args, char *);
+ if (!strvalue) strvalue = "(NULL)";
+ if (max == -1) {
+ max = strlen(strvalue);
+ }
+ if (min > 0 && max >= 0 && min > max) max = min;
+ fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
+ break;
+ case 'p':
+ strvalue = va_arg (args, void *);
+ fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
+ break;
+ case 'n':
+ if (cflags == DP_C_SHORT) {
+ short int *num;
+ num = va_arg (args, short int *);
+ *num = currlen;
+ } else if (cflags == DP_C_LONG) {
+ long int *num;
+ num = va_arg (args, long int *);
+ *num = (long int)currlen;
+ } else if (cflags == DP_C_LLONG) {
+ LLONG *num;
+ num = va_arg (args, LLONG *);
+ *num = (LLONG)currlen;
+ } else {
+ int *num;
+ num = va_arg (args, int *);
+ *num = currlen;
+ }
+ break;
+ case '%':
+ dopr_outch (buffer, &currlen, maxlen, ch);
+ break;
+ case 'w':
+ /* not supported yet, treat as next char */
+ ch = *format++;
+ break;
+ default:
+ /* Unknown, skip */
+ break;
+ }
+ ch = *format++;
+ state = DP_S_DEFAULT;
+ flags = cflags = min = 0;
+ max = -1;
+ break;
+ case DP_S_DONE:
+ break;
+ default:
+ /* hmm? */
+ break; /* some picky compilers need this */
+ }
+ }
+ if (maxlen != 0) {
+ if (currlen < maxlen - 1)
+ buffer[currlen] = '\0';
+ else if (maxlen > 0)
+ buffer[maxlen - 1] = '\0';
+ }
+
+ return currlen;
+}
+
+static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
+ char *value, int flags, int min, int max)
+{
+ int padlen, strln; /* amount to pad */
+ int cnt = 0;
+
+#ifdef DEBUG_SNPRINTF
+ printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value);
+#endif
+ if (value == 0) {
+ value = "<NULL>";
+ }
+
+ for (strln = 0; value[strln]; ++strln); /* strlen */
+ padlen = min - strln;
+ if (padlen < 0)
+ padlen = 0;
+ if (flags & DP_F_MINUS)
+ padlen = -padlen; /* Left Justify */
+
+ while ((padlen > 0) && (cnt < max)) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ --padlen;
+ ++cnt;
+ }
+ while (*value && (cnt < max)) {
+ dopr_outch (buffer, currlen, maxlen, *value++);
+ ++cnt;
+ }
+ while ((padlen < 0) && (cnt < max)) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ ++padlen;
+ ++cnt;
+ }
+}
+
+/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
+
+static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
+ long value, int base, int min, int max, int flags)
+{
+ int signvalue = 0;
+ unsigned long uvalue;
+ char convert[20];
+ int place = 0;
+ int spadlen = 0; /* amount to space pad */
+ int zpadlen = 0; /* amount to zero pad */
+ int caps = 0;
+
+ if (max < 0)
+ max = 0;
+
+ uvalue = value;
+
+ if(!(flags & DP_F_UNSIGNED)) {
+ if( value < 0 ) {
+ signvalue = '-';
+ uvalue = -value;
+ } else {
+ if (flags & DP_F_PLUS) /* Do a sign (+/i) */
+ signvalue = '+';
+ else if (flags & DP_F_SPACE)
+ signvalue = ' ';
+ }
+ }
+
+ if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
+
+ do {
+ convert[place++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")
+ [uvalue % (unsigned)base ];
+ uvalue = (uvalue / (unsigned)base );
+ } while(uvalue && (place < 20));
+ if (place == 20) place--;
+ convert[place] = 0;
+
+ zpadlen = max - place;
+ spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
+ if (zpadlen < 0) zpadlen = 0;
+ if (spadlen < 0) spadlen = 0;
+ if (flags & DP_F_ZERO) {
+ zpadlen = MAX(zpadlen, spadlen);
+ spadlen = 0;
+ }
+ if (flags & DP_F_MINUS)
+ spadlen = -spadlen; /* Left Justifty */
+
+#ifdef DEBUG_SNPRINTF
+ printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
+ zpadlen, spadlen, min, max, place);
+#endif
+
+ /* Spaces */
+ while (spadlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ --spadlen;
+ }
+
+ /* Sign */
+ if (signvalue)
+ dopr_outch (buffer, currlen, maxlen, signvalue);
+
+ /* Zeros */
+ if (zpadlen > 0) {
+ while (zpadlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, '0');
+ --zpadlen;
+ }
+ }
+
+ /* Digits */
+ while (place > 0)
+ dopr_outch (buffer, currlen, maxlen, convert[--place]);
+
+ /* Left Justified spaces */
+ while (spadlen < 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ ++spadlen;
+ }
+}
+
+static LDOUBLE abs_val(LDOUBLE value)
+{
+ LDOUBLE result = value;
+
+ if (value < 0)
+ result = -value;
+
+ return result;
+}
+
+static LDOUBLE POW10(int exp)
+{
+ LDOUBLE result = 1;
+
+ while (exp) {
+ result *= 10;
+ exp--;
+ }
+
+ return result;
+}
+
+static LLONG ROUND(LDOUBLE value)
+{
+ LLONG intpart;
+
+ intpart = (LLONG)value;
+ value = value - intpart;
+ if (value >= 0.5) intpart++;
+
+ return intpart;
+}
+
+/* a replacement for modf that doesn't need the math library. Should
+ be portable, but slow */
+static double my_modf(double x0, double *iptr)
+{
+ int i;
+ long l;
+ double x = x0;
+ double f = 1.0;
+
+ for (i=0;i<100;i++) {
+ l = (long)x;
+ if (l <= (x+1) && l >= (x-1)) break;
+ x *= 0.1;
+ f *= 10.0;
+ }
+
+ if (i == 100) {
+ /* yikes! the number is beyond what we can handle. What do we do? */
+ (*iptr) = 0;
+ return 0;
+ }
+
+ if (i != 0) {
+ double i2;
+ double ret;
+
+ ret = my_modf(x0-l*f, &i2);
+ (*iptr) = l*f + i2;
+ return ret;
+ }
+
+ (*iptr) = l;
+ return x - (*iptr);
+}
+
+
+static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
+ LDOUBLE fvalue, int min, int max, int flags)
+{
+ int signvalue = 0;
+ double ufvalue;
+ char iconvert[311];
+ char fconvert[311];
+ int iplace = 0;
+ int fplace = 0;
+ int padlen = 0; /* amount to pad */
+ int zpadlen = 0;
+ int caps = 0;
+ int idx;
+ double intpart;
+ double fracpart;
+ double temp;
+
+ /*
+ * AIX manpage says the default is 0, but Solaris says the default
+ * is 6, and sprintf on AIX defaults to 6
+ */
+ if (max < 0)
+ max = 6;
+
+ ufvalue = abs_val (fvalue);
+
+ if (fvalue < 0) {
+ signvalue = '-';
+ } else {
+ if (flags & DP_F_PLUS) { /* Do a sign (+/i) */
+ signvalue = '+';
+ } else {
+ if (flags & DP_F_SPACE)
+ signvalue = ' ';
+ }
+ }
+
+#if 0
+ if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
+#endif
+
+#if 0
+ if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */
+#endif
+
+ /*
+ * Sorry, we only support 16 digits past the decimal because of our
+ * conversion method
+ */
+ if (max > 16)
+ max = 16;
+
+ /* We "cheat" by converting the fractional part to integer by
+ * multiplying by a factor of 10
+ */
+
+ temp = ufvalue;
+ my_modf(temp, &intpart);
+
+ fracpart = ROUND((POW10(max)) * (ufvalue - intpart));
+
+ if (fracpart >= POW10(max)) {
+ intpart++;
+ fracpart -= POW10(max);
+ }
+
+
+ /* Convert integer part */
+ do {
+ temp = intpart*0.1;
+ my_modf(temp, &intpart);
+ idx = (int) ((temp -intpart +0.05)* 10.0);
+ /* idx = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */
+ /* printf ("%llf, %f, %x\n", temp, intpart, idx); */
+ iconvert[iplace++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")[idx];
+ } while (intpart && (iplace < 311));
+ if (iplace == 311) iplace--;
+ iconvert[iplace] = 0;
+
+ /* Convert fractional part */
+ if (fracpart)
+ {
+ do {
+ temp = fracpart*0.1;
+ my_modf(temp, &fracpart);
+ idx = (int) ((temp -fracpart +0.05)* 10.0);
+ /* idx = (int) ((((temp/10) -fracpart) +0.05) *10); */
+ /* printf ("%lf, %lf, %ld\n", temp, fracpart, idx ); */
+ fconvert[fplace++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")[idx];
+ } while(fracpart && (fplace < 311));
+ if (fplace == 311) fplace--;
+ }
+ fconvert[fplace] = 0;
+
+ /* -1 for decimal point, another -1 if we are printing a sign */
+ padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
+ zpadlen = max - fplace;
+ if (zpadlen < 0) zpadlen = 0;
+ if (padlen < 0)
+ padlen = 0;
+ if (flags & DP_F_MINUS)
+ padlen = -padlen; /* Left Justifty */
+
+ if ((flags & DP_F_ZERO) && (padlen > 0)) {
+ if (signvalue) {
+ dopr_outch (buffer, currlen, maxlen, signvalue);
+ --padlen;
+ signvalue = 0;
+ }
+ while (padlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, '0');
+ --padlen;
+ }
+ }
+ while (padlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ --padlen;
+ }
+ if (signvalue)
+ dopr_outch (buffer, currlen, maxlen, signvalue);
+
+ while (iplace > 0)
+ dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
+
+#ifdef DEBUG_SNPRINTF
+ printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
+#endif
+
+ /*
+ * Decimal point. This should probably use locale to find the correct
+ * char to print out.
+ */
+ if (max > 0) {
+ dopr_outch (buffer, currlen, maxlen, '.');
+
+ while (zpadlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, '0');
+ --zpadlen;
+ }
+
+ while (fplace > 0)
+ dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
+ }
+
+ while (padlen < 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ ++padlen;
+ }
+}
+
+static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
+{
+ if (*currlen < maxlen) {
+ buffer[(*currlen)] = c;
+ }
+ (*currlen)++;
+}
+
+ int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
+{
+ return dopr(str, count, fmt, args);
+}
+#endif
+
+/* yes this really must be a ||. Don't muck with this (tridge)
+ *
+ * The logic for these two is that we need our own definition if the
+ * OS *either* has no definition of *sprintf, or if it does have one
+ * that doesn't work properly according to the autoconf test.
+ */
+#if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
+int smb_snprintf(char *str,size_t count,const char *fmt,...)
+{
+ size_t ret;
+ va_list ap;
+
+ va_start(ap, fmt);
+ ret = vsnprintf(str, count, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+#endif
+
+#endif
+
+#ifndef HAVE_VASPRINTF
+ int vasprintf(char **ptr, const char *format, va_list ap)
+{
+ int ret;
+ va_list ap2;
+
+ VA_COPY(ap2, ap);
+
+ ret = vsnprintf(NULL, 0, format, ap2);
+ if (ret <= 0) return ret;
+
+ (*ptr) = (char *)malloc(ret+1);
+ if (!*ptr) return -1;
+
+ VA_COPY(ap2, ap);
+
+ ret = vsnprintf(*ptr, ret+1, format, ap2);
+
+ return ret;
+}
+#endif
+
+
+#ifndef HAVE_ASPRINTF
+ int asprintf(char **ptr, const char *format, ...)
+{
+ va_list ap;
+ int ret;
+
+ *ptr = NULL;
+ va_start(ap, format);
+ ret = vasprintf(ptr, format, ap);
+ va_end(ap);
+
+ return ret;
+}
+#endif
+
+#ifdef TEST_SNPRINTF
+
+ int sprintf(char *str,const char *fmt,...);
+
+ int main (void)
+{
+ char buf1[1024];
+ char buf2[1024];
+ char *fp_fmt[] = {
+ "%1.1f",
+ "%-1.5f",
+ "%1.5f",
+ "%123.9f",
+ "%10.5f",
+ "% 10.5f",
+ "%+22.9f",
+ "%+4.9f",
+ "%01.3f",
+ "%4f",
+ "%3.1f",
+ "%3.2f",
+ "%.0f",
+ "%f",
+ "-16.16f",
+ NULL
+ };
+ double fp_nums[] = { 6442452944.1234, -1.5, 134.21, 91340.2, 341.1234, 203.9, 0.96, 0.996,
+ 0.9996, 1.996, 4.136, 5.030201, 0.00205,
+ /* END LIST */ 0};
+ char *int_fmt[] = {
+ "%-1.5d",
+ "%1.5d",
+ "%123.9d",
+ "%5.5d",
+ "%10.5d",
+ "% 10.5d",
+ "%+22.33d",
+ "%01.3d",
+ "%4d",
+ "%d",
+ NULL
+ };
+ long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
+ char *str_fmt[] = {
+ "10.5s",
+ "5.10s",
+ "10.1s",
+ "0.10s",
+ "10.0s",
+ "1.10s",
+ "%s",
+ "%.1s",
+ "%.10s",
+ "%10s",
+ NULL
+ };
+ char *str_vals[] = {"hello", "a", "", "a longer string", NULL};
+ int x, y;
+ int fail = 0;
+ int num = 0;
+
+ printf ("Testing snprintf format codes against system sprintf...\n");
+
+ for (x = 0; fp_fmt[x] ; x++) {
+ for (y = 0; fp_nums[y] != 0 ; y++) {
+ int l1 = snprintf(NULL, 0, fp_fmt[x], fp_nums[y]);
+ int l2 = snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
+ sprintf (buf2, fp_fmt[x], fp_nums[y]);
+ if (strcmp (buf1, buf2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n",
+ fp_fmt[x], buf1, buf2);
+ fail++;
+ }
+ if (l1 != l2) {
+ printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, fp_fmt[x]);
+ fail++;
+ }
+ num++;
+ }
+ }
+
+ for (x = 0; int_fmt[x] ; x++) {
+ for (y = 0; int_nums[y] != 0 ; y++) {
+ int l1 = snprintf(NULL, 0, int_fmt[x], int_nums[y]);
+ int l2 = snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
+ sprintf (buf2, int_fmt[x], int_nums[y]);
+ if (strcmp (buf1, buf2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n",
+ int_fmt[x], buf1, buf2);
+ fail++;
+ }
+ if (l1 != l2) {
+ printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, int_fmt[x]);
+ fail++;
+ }
+ num++;
+ }
+ }
+
+ for (x = 0; str_fmt[x] ; x++) {
+ for (y = 0; str_vals[y] != 0 ; y++) {
+ int l1 = snprintf(NULL, 0, str_fmt[x], str_vals[y]);
+ int l2 = snprintf(buf1, sizeof(buf1), str_fmt[x], str_vals[y]);
+ sprintf (buf2, str_fmt[x], str_vals[y]);
+ if (strcmp (buf1, buf2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n",
+ str_fmt[x], buf1, buf2);
+ fail++;
+ }
+ if (l1 != l2) {
+ printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, str_fmt[x]);
+ fail++;
+ }
+ num++;
+ }
+ }
+
+ printf ("%d tests failed out of %d.\n", fail, num);
+
+ printf("seeing how many digits we support\n");
+ {
+ double v0 = 0.12345678901234567890123456789012345678901;
+ for (x=0; x<100; x++) {
+ double p = pow(10, x);
+ double r = v0*p;
+ snprintf(buf1, sizeof(buf1), "%1.1f", r);
+ sprintf(buf2, "%1.1f", r);
+ if (strcmp(buf1, buf2)) {
+ printf("we seem to support %d digits\n", x-1);
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+#endif /* TEST_SNPRINTF */
diff --git a/source/lib/sock_exec.c b/source/lib/sock_exec.c
new file mode 100644
index 00000000000..52c5a8ce52c
--- /dev/null
+++ b/source/lib/sock_exec.c
@@ -0,0 +1,115 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Tim Potter 2000-2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*******************************************************************
+this is like socketpair but uses tcp. It is used by the Samba
+regression test code
+The function guarantees that nobody else can attach to the socket,
+or if they do that this function fails and the socket gets closed
+returns 0 on success, -1 on failure
+the resulting file descriptors are symmetrical
+ ******************************************************************/
+static int socketpair_tcp(int fd[2])
+{
+ int listener;
+ struct sockaddr_in sock;
+ struct sockaddr_in sock2;
+ socklen_t socklen = sizeof(sock);
+ int connect_done = 0;
+
+ fd[0] = fd[1] = listener = -1;
+
+ memset(&sock, 0, sizeof(sock));
+
+ if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1) goto failed;
+
+ memset(&sock2, 0, sizeof(sock2));
+#ifdef HAVE_SOCK_SIN_LEN
+ sock2.sin_len = sizeof(sock2);
+#endif
+ sock2.sin_family = PF_INET;
+
+ bind(listener, (struct sockaddr *)&sock2, sizeof(sock2));
+
+ if (listen(listener, 1) != 0) goto failed;
+
+ if (getsockname(listener, (struct sockaddr *)&sock, &socklen) != 0) goto failed;
+
+ if ((fd[1] = socket(PF_INET, SOCK_STREAM, 0)) == -1) goto failed;
+
+ set_blocking(fd[1], 0);
+
+ sock.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ if (connect(fd[1],(struct sockaddr *)&sock,sizeof(sock)) == -1) {
+ if (errno != EINPROGRESS) goto failed;
+ } else {
+ connect_done = 1;
+ }
+
+ if ((fd[0] = accept(listener, (struct sockaddr *)&sock, &socklen)) == -1) goto failed;
+
+ close(listener);
+ if (connect_done == 0) {
+ if (connect(fd[1],(struct sockaddr *)&sock,sizeof(sock)) != 0
+ && errno != EISCONN) goto failed;
+ }
+
+ set_blocking(fd[1], 1);
+
+ /* all OK! */
+ return 0;
+
+ failed:
+ if (fd[0] != -1) close(fd[0]);
+ if (fd[1] != -1) close(fd[1]);
+ if (listener != -1) close(listener);
+ return -1;
+}
+
+
+/*******************************************************************
+run a program on a local tcp socket, this is used to launch smbd
+when regression testing
+the return value is a socket which is attached to a subprocess
+running "prog". stdin and stdout are attached. stderr is left
+attached to the original stderr
+ ******************************************************************/
+int sock_exec(const char *prog)
+{
+ int fd[2];
+ if (socketpair_tcp(fd) != 0) {
+ DEBUG(0,("socketpair_tcp failed (%s)\n", strerror(errno)));
+ return -1;
+ }
+ if (fork() == 0) {
+ close(fd[0]);
+ close(0);
+ close(1);
+ dup(fd[1]);
+ dup(fd[1]);
+ exit(system(prog));
+ }
+ close(fd[1]);
+ return fd[0];
+}
diff --git a/source/lib/substitute.c b/source/lib/substitute.c
new file mode 100644
index 00000000000..fed11c22982
--- /dev/null
+++ b/source/lib/substitute.c
@@ -0,0 +1,802 @@
+/*
+ Unix SMB/CIFS implementation.
+ string substitution functions
+ Copyright (C) Andrew Tridgell 1992-2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+#include "includes.h"
+
+fstring local_machine="";
+fstring remote_arch="UNKNOWN";
+userdom_struct current_user_info;
+fstring remote_proto="UNKNOWN";
+
+static fstring remote_machine;
+static fstring smb_user_name;
+
+/**
+ * Set the 'local' machine name
+ * @param local_name the name we are being called
+ * @param if this is the 'final' name for us, not be be changed again
+ */
+
+void set_local_machine_name(const char* local_name, BOOL perm)
+{
+ static BOOL already_perm = False;
+ fstring tmp_local_machine;
+
+ /*
+ * Windows NT/2k uses "*SMBSERVER" and XP uses "*SMBSERV"
+ * arrggg!!!
+ */
+
+ if (strequal(local_name, "*SMBSERVER"))
+ return;
+
+ if (strequal(local_name, "*SMBSERV"))
+ return;
+
+ if (already_perm)
+ return;
+
+ already_perm = perm;
+
+ fstrcpy(tmp_local_machine,local_name);
+ trim_char(tmp_local_machine,' ',' ');
+ alpha_strcpy(local_machine,tmp_local_machine,SAFE_NETBIOS_CHARS,sizeof(local_machine)-1);
+ strlower_m(local_machine);
+}
+
+/**
+ * Set the 'remote' machine name
+ * @param remote_name the name our client wants to be called by
+ * @param if this is the 'final' name for them, not be be changed again
+ */
+
+void set_remote_machine_name(const char* remote_name, BOOL perm)
+{
+ static BOOL already_perm = False;
+ fstring tmp_remote_machine;
+
+ if (already_perm)
+ return;
+
+ already_perm = perm;
+
+ fstrcpy(tmp_remote_machine,remote_name);
+ trim_char(tmp_remote_machine,' ',' ');
+ alpha_strcpy(remote_machine,tmp_remote_machine,SAFE_NETBIOS_CHARS,sizeof(remote_machine)-1);
+ strlower_m(remote_machine);
+}
+
+const char* get_remote_machine_name(void)
+{
+ return remote_machine;
+}
+
+const char* get_local_machine_name(void)
+{
+ if (!*local_machine) {
+ return global_myname();
+ }
+
+ return local_machine;
+}
+
+/*******************************************************************
+ Setup the string used by %U substitution.
+********************************************************************/
+
+void sub_set_smb_name(const char *name)
+{
+ fstring tmp;
+
+ /* don't let anonymous logins override the name */
+ if (! *name)
+ return;
+
+ fstrcpy(tmp,name);
+ trim_char(tmp,' ',' ');
+ strlower_m(tmp);
+ alpha_strcpy(smb_user_name,tmp,SAFE_NETBIOS_CHARS,sizeof(smb_user_name)-1);
+}
+
+char* sub_get_smb_name( void )
+{
+ return smb_user_name;
+}
+
+/*******************************************************************
+ Setup the strings used by substitutions. Called per packet. Ensure
+ %U name is set correctly also.
+********************************************************************/
+
+void set_current_user_info(const userdom_struct *pcui)
+{
+ current_user_info = *pcui;
+ /* The following is safe as current_user_info.smb_name
+ * has already been sanitised in register_vuid. */
+ fstrcpy(smb_user_name, current_user_info.smb_name);
+}
+
+/*******************************************************************
+ Given a pointer to a %$(NAME) expand it as an environment variable.
+ Return the number of characters by which the pointer should be advanced.
+ Based on code by Branko Cibej <branko.cibej@hermes.si>
+ When this is called p points at the '%' character.
+********************************************************************/
+
+static size_t expand_env_var(char *p, int len)
+{
+ fstring envname;
+ char *envval;
+ char *q, *r;
+ int copylen;
+
+ if (p[1] != '$')
+ return 1;
+
+ if (p[2] != '(')
+ return 2;
+
+ /*
+ * Look for the terminating ')'.
+ */
+
+ if ((q = strchr_m(p,')')) == NULL) {
+ DEBUG(0,("expand_env_var: Unterminated environment variable [%s]\n", p));
+ return 2;
+ }
+
+ /*
+ * Extract the name from within the %$(NAME) string.
+ */
+
+ r = p+3;
+ copylen = MIN((q-r),(sizeof(envname)-1));
+ strncpy(envname,r,copylen);
+ envname[copylen] = '\0';
+
+ if ((envval = getenv(envname)) == NULL) {
+ DEBUG(0,("expand_env_var: Environment variable [%s] not set\n", envname));
+ return 2;
+ }
+
+ /*
+ * Copy the full %$(NAME) into envname so it
+ * can be replaced.
+ */
+
+ copylen = MIN((q+1-p),(sizeof(envname)-1));
+ strncpy(envname,p,copylen);
+ envname[copylen] = '\0';
+ string_sub(p,envname,envval,len);
+ return 0; /* Allow the environment contents to be parsed. */
+}
+
+/*******************************************************************
+ Given a pointer to a %$(NAME) in p and the whole string in str
+ expand it as an environment variable.
+ Return a new allocated and expanded string.
+ Based on code by Branko Cibej <branko.cibej@hermes.si>
+ When this is called p points at the '%' character.
+ May substitute multiple occurrencies of the same env var.
+********************************************************************/
+
+
+static char * realloc_expand_env_var(char *str, char *p)
+{
+ char *envname;
+ char *envval;
+ char *q, *r;
+ int copylen;
+
+ if (p[0] != '%' || p[1] != '$' || p[2] != '(')
+ return str;
+
+ /*
+ * Look for the terminating ')'.
+ */
+
+ if ((q = strchr_m(p,')')) == NULL) {
+ DEBUG(0,("expand_env_var: Unterminated environment variable [%s]\n", p));
+ return str;
+ }
+
+ /*
+ * Extract the name from within the %$(NAME) string.
+ */
+
+ r = p + 3;
+ copylen = q - r;
+ envname = (char *)malloc(copylen + 1 + 4); /* reserve space for use later add %$() chars */
+ if (envname == NULL) return NULL;
+ strncpy(envname,r,copylen);
+ envname[copylen] = '\0';
+
+ if ((envval = getenv(envname)) == NULL) {
+ DEBUG(0,("expand_env_var: Environment variable [%s] not set\n", envname));
+ SAFE_FREE(envname);
+ return str;
+ }
+
+ /*
+ * Copy the full %$(NAME) into envname so it
+ * can be replaced.
+ */
+
+ copylen = q + 1 - p;
+ strncpy(envname,p,copylen);
+ envname[copylen] = '\0';
+ r = realloc_string_sub(str, envname, envval);
+ SAFE_FREE(envname);
+ if (r == NULL) return NULL;
+ return r;
+}
+
+/*******************************************************************
+ Patch from jkf@soton.ac.uk
+ Added this to implement %p (NIS auto-map version of %H)
+*******************************************************************/
+
+static char *automount_path(const char *user_name)
+{
+ static pstring server_path;
+
+ /* use the passwd entry as the default */
+ /* this will be the default if WITH_AUTOMOUNT is not used or fails */
+
+ pstrcpy(server_path, get_user_home_dir(user_name));
+
+#if (defined(HAVE_NETGROUP) && defined (WITH_AUTOMOUNT))
+
+ if (lp_nis_home_map()) {
+ char *home_path_start;
+ char *automount_value = automount_lookup(user_name);
+
+ if(strlen(automount_value) > 0) {
+ home_path_start = strchr_m(automount_value,':');
+ if (home_path_start != NULL) {
+ DEBUG(5, ("NIS lookup succeeded. Home path is: %s\n",
+ home_path_start?(home_path_start+1):""));
+ pstrcpy(server_path, home_path_start+1);
+ }
+ } else {
+ /* NIS key lookup failed: default to user home directory from password file */
+ DEBUG(5, ("NIS lookup failed. Using Home path from passwd file. Home path is: %s\n", server_path ));
+ }
+ }
+#endif
+
+ DEBUG(4,("Home server path: %s\n", server_path));
+
+ return server_path;
+}
+
+/*******************************************************************
+ Patch from jkf@soton.ac.uk
+ This is Luke's original function with the NIS lookup code
+ moved out to a separate function.
+*******************************************************************/
+
+static const char *automount_server(const char *user_name)
+{
+ static pstring server_name;
+ const char *local_machine_name = get_local_machine_name();
+
+ /* use the local machine name as the default */
+ /* this will be the default if WITH_AUTOMOUNT is not used or fails */
+ if (local_machine_name && *local_machine_name)
+ pstrcpy(server_name, local_machine_name);
+ else
+ pstrcpy(server_name, global_myname());
+
+#if (defined(HAVE_NETGROUP) && defined (WITH_AUTOMOUNT))
+
+ if (lp_nis_home_map()) {
+ int home_server_len;
+ char *automount_value = automount_lookup(user_name);
+ home_server_len = strcspn(automount_value,":");
+ DEBUG(5, ("NIS lookup succeeded. Home server length: %d\n",home_server_len));
+ if (home_server_len > sizeof(pstring))
+ home_server_len = sizeof(pstring);
+ strncpy(server_name, automount_value, home_server_len);
+ server_name[home_server_len] = '\0';
+ }
+#endif
+
+ DEBUG(4,("Home server: %s\n", server_name));
+
+ return server_name;
+}
+
+/****************************************************************************
+ Do some standard substitutions in a string.
+ len is the length in bytes of the space allowed in string str. If zero means
+ don't allow expansions.
+****************************************************************************/
+
+void standard_sub_basic(const char *smb_name, char *str,size_t len)
+{
+ char *p, *s;
+ fstring pidstr;
+ struct passwd *pass;
+ const char *local_machine_name = get_local_machine_name();
+
+ for (s=str; (p=strchr_m(s, '%'));s=p) {
+ fstring tmp_str;
+
+ int l = (int)len - (int)(p-str);
+
+ if (l < 0)
+ l = 0;
+
+ switch (*(p+1)) {
+ case 'U' :
+ fstrcpy(tmp_str, smb_name);
+ strlower_m(tmp_str);
+ string_sub(p,"%U",tmp_str,l);
+ break;
+ case 'G' :
+ fstrcpy(tmp_str, smb_name);
+ if ((pass = Get_Pwnam(tmp_str))!=NULL) {
+ string_sub(p,"%G",gidtoname(pass->pw_gid),l);
+ } else {
+ p += 2;
+ }
+ break;
+ case 'D' :
+ fstrcpy(tmp_str, current_user_info.domain);
+ strupper_m(tmp_str);
+ string_sub(p,"%D", tmp_str,l);
+ break;
+ case 'I' :
+ string_sub(p,"%I", client_addr(),l);
+ break;
+ case 'i' :
+ string_sub(p,"%i", client_socket_addr(),l);
+ break;
+ case 'L' :
+ if (local_machine_name && *local_machine_name)
+ string_sub(p,"%L", local_machine_name,l);
+ else {
+ pstring temp_name;
+
+ pstrcpy(temp_name, global_myname());
+ strlower_m(temp_name);
+ string_sub(p,"%L", temp_name,l);
+ }
+ break;
+ case 'M' :
+ string_sub(p,"%M", client_name(),l);
+ break;
+ case 'R' :
+ string_sub(p,"%R", remote_proto,l);
+ break;
+ case 'T' :
+ string_sub(p,"%T", timestring(False),l);
+ break;
+ case 'a' :
+ string_sub(p,"%a", remote_arch,l);
+ break;
+ case 'd' :
+ slprintf(pidstr,sizeof(pidstr)-1, "%d",(int)sys_getpid());
+ string_sub(p,"%d", pidstr,l);
+ break;
+ case 'h' :
+ string_sub(p,"%h", myhostname(),l);
+ break;
+ case 'm' :
+ string_sub(p,"%m", get_remote_machine_name(),l);
+ break;
+ case 'v' :
+ string_sub(p,"%v", SAMBA_VERSION_STRING,l);
+ break;
+ case '$' :
+ p += expand_env_var(p,l);
+ break; /* Expand environment variables */
+ case '\0':
+ p++;
+ break; /* don't run off the end of the string */
+
+ default: p+=2;
+ break;
+ }
+ }
+}
+
+static void standard_sub_advanced(int snum, const char *user,
+ const char *connectpath, gid_t gid,
+ const char *smb_name, char *str, size_t len)
+{
+ char *p, *s, *home;
+
+ for (s=str; (p=strchr_m(s, '%'));s=p) {
+ int l = (int)len - (int)(p-str);
+
+ if (l < 0)
+ l = 0;
+
+ switch (*(p+1)) {
+ case 'N' :
+ string_sub(p,"%N", automount_server(user),l);
+ break;
+ case 'H':
+ if ((home = get_user_home_dir(user)))
+ string_sub(p,"%H",home, l);
+ else
+ p += 2;
+ break;
+ case 'P':
+ string_sub(p,"%P", connectpath, l);
+ break;
+ case 'S':
+ string_sub(p,"%S", lp_servicename(snum), l);
+ break;
+ case 'g':
+ string_sub(p,"%g", gidtoname(gid), l);
+ break;
+ case 'u':
+ string_sub(p,"%u", user, l);
+ break;
+
+ /* Patch from jkf@soton.ac.uk Left the %N (NIS
+ * server name) in standard_sub_basic as it is
+ * a feature for logon servers, hence uses the
+ * username. The %p (NIS server path) code is
+ * here as it is used instead of the default
+ * "path =" string in [homes] and so needs the
+ * service name, not the username. */
+ case 'p':
+ string_sub(p,"%p", automount_path(lp_servicename(snum)), l);
+ break;
+ case '\0':
+ p++;
+ break; /* don't run off the end of the string */
+
+ default: p+=2;
+ break;
+ }
+ }
+
+ standard_sub_basic(smb_name, str, len);
+}
+
+/****************************************************************************
+ Do some standard substitutions in a string.
+ This function will return an allocated string that have to be freed.
+****************************************************************************/
+
+char *talloc_sub_basic(TALLOC_CTX *mem_ctx, const char *smb_name, const char *str)
+{
+ char *a, *t;
+ a = alloc_sub_basic(smb_name, str);
+ if (!a) return NULL;
+ t = talloc_strdup(mem_ctx, a);
+ SAFE_FREE(a);
+ return t;
+}
+
+char *alloc_sub_basic(const char *smb_name, const char *str)
+{
+ char *b, *p, *s, *t, *r, *a_string;
+ fstring pidstr;
+ struct passwd *pass;
+ const char *local_machine_name = get_local_machine_name();
+
+ /* workaround to prevent a crash while lookinf at bug #687 */
+
+ if ( !str ) {
+ DEBUG(0,("alloc_sub_basic: NULL source string! This should not happen\n"));
+ return NULL;
+ }
+
+ a_string = strdup(str);
+ if (a_string == NULL) {
+ DEBUG(0, ("alloc_sub_specified: Out of memory!\n"));
+ return NULL;
+ }
+
+ for (b = s = a_string; (p = strchr_m(s, '%')); s = a_string + (p - b)) {
+
+ r = NULL;
+ b = t = a_string;
+
+ switch (*(p+1)) {
+ case 'U' :
+ r = strdup_lower(smb_name);
+ if (r == NULL) goto error;
+ t = realloc_string_sub(t, "%U", r);
+ break;
+ case 'G' :
+ r = strdup(smb_name);
+ if (r == NULL) goto error;
+ if ((pass = Get_Pwnam(r))!=NULL) {
+ t = realloc_string_sub(t, "%G", gidtoname(pass->pw_gid));
+ }
+ break;
+ case 'D' :
+ r = strdup_upper(current_user_info.domain);
+ if (r == NULL) goto error;
+ t = realloc_string_sub(t, "%D", r);
+ break;
+ case 'I' :
+ t = realloc_string_sub(t, "%I", client_addr());
+ break;
+ case 'L' :
+ if (local_machine_name && *local_machine_name)
+ t = realloc_string_sub(t, "%L", local_machine_name);
+ else
+ t = realloc_string_sub(t, "%L", global_myname());
+ break;
+ case 'N':
+ t = realloc_string_sub(t, "%N", automount_server(smb_name));
+ break;
+ case 'M' :
+ t = realloc_string_sub(t, "%M", client_name());
+ break;
+ case 'R' :
+ t = realloc_string_sub(t, "%R", remote_proto);
+ break;
+ case 'T' :
+ t = realloc_string_sub(t, "%T", timestring(False));
+ break;
+ case 'a' :
+ t = realloc_string_sub(t, "%a", remote_arch);
+ break;
+ case 'd' :
+ slprintf(pidstr,sizeof(pidstr)-1, "%d",(int)sys_getpid());
+ t = realloc_string_sub(t, "%d", pidstr);
+ break;
+ case 'h' :
+ t = realloc_string_sub(t, "%h", myhostname());
+ break;
+ case 'm' :
+ t = realloc_string_sub(t, "%m", remote_machine);
+ break;
+ case 'v' :
+ t = realloc_string_sub(t, "%v", SAMBA_VERSION_STRING);
+ break;
+ case '$' :
+ t = realloc_expand_env_var(t, p); /* Expand environment variables */
+ break;
+
+ default:
+ break;
+ }
+
+ p++;
+ SAFE_FREE(r);
+ if (t == NULL) goto error;
+ a_string = t;
+ }
+
+ return a_string;
+error:
+ SAFE_FREE(a_string);
+ return NULL;
+}
+
+/****************************************************************************
+ Do some specific substitutions in a string.
+ This function will return an allocated string that have to be freed.
+****************************************************************************/
+
+char *talloc_sub_specified(TALLOC_CTX *mem_ctx,
+ const char *input_string,
+ const char *username,
+ const char *domain,
+ uid_t uid,
+ gid_t gid)
+{
+ char *a, *t;
+ a = alloc_sub_specified(input_string, username, domain, uid, gid);
+ if (!a) return NULL;
+ t = talloc_strdup(mem_ctx, a);
+ SAFE_FREE(a);
+ return t;
+}
+
+char *alloc_sub_specified(const char *input_string,
+ const char *username,
+ const char *domain,
+ uid_t uid,
+ gid_t gid)
+{
+ char *a_string, *ret_string;
+ char *b, *p, *s, *t;
+
+ a_string = strdup(input_string);
+ if (a_string == NULL) {
+ DEBUG(0, ("alloc_sub_specified: Out of memory!\n"));
+ return NULL;
+ }
+
+ for (b = s = a_string; (p = strchr_m(s, '%')); s = a_string + (p - b)) {
+
+ b = t = a_string;
+
+ switch (*(p+1)) {
+ case 'U' :
+ t = realloc_string_sub(t, "%U", username);
+ break;
+ case 'u' :
+ t = realloc_string_sub(t, "%u", username);
+ break;
+ case 'G' :
+ if (gid != -1) {
+ t = realloc_string_sub(t, "%G", gidtoname(gid));
+ } else {
+ t = realloc_string_sub(t, "%G", "NO_GROUP");
+ }
+ break;
+ case 'g' :
+ if (gid != -1) {
+ t = realloc_string_sub(t, "%g", gidtoname(gid));
+ } else {
+ t = realloc_string_sub(t, "%g", "NO_GROUP");
+ }
+ break;
+ case 'D' :
+ t = realloc_string_sub(t, "%D", domain);
+ break;
+ case 'N' :
+ t = realloc_string_sub(t, "%N", automount_server(username));
+ break;
+ default:
+ break;
+ }
+
+ p++;
+ if (t == NULL) {
+ SAFE_FREE(a_string);
+ return NULL;
+ }
+ a_string = t;
+ }
+
+ ret_string = alloc_sub_basic(username, a_string);
+ SAFE_FREE(a_string);
+ return ret_string;
+}
+
+char *talloc_sub_advanced(TALLOC_CTX *mem_ctx,
+ int snum,
+ const char *user,
+ const char *connectpath,
+ gid_t gid,
+ const char *smb_name,
+ const char *str)
+{
+ char *a, *t;
+ a = alloc_sub_advanced(snum, user, connectpath, gid, smb_name, str);
+ if (!a) return NULL;
+ t = talloc_strdup(mem_ctx, a);
+ SAFE_FREE(a);
+ return t;
+}
+
+char *alloc_sub_advanced(int snum, const char *user,
+ const char *connectpath, gid_t gid,
+ const char *smb_name, const char *str)
+{
+ char *a_string, *ret_string;
+ char *b, *p, *s, *t, *h;
+
+ a_string = strdup(str);
+ if (a_string == NULL) {
+ DEBUG(0, ("alloc_sub_specified: Out of memory!\n"));
+ return NULL;
+ }
+
+ for (b = s = a_string; (p = strchr_m(s, '%')); s = a_string + (p - b)) {
+
+ b = t = a_string;
+
+ switch (*(p+1)) {
+ case 'N' :
+ t = realloc_string_sub(t, "%N", automount_server(user));
+ break;
+ case 'H':
+ if ((h = get_user_home_dir(user)))
+ t = realloc_string_sub(t, "%H", h);
+ break;
+ case 'P':
+ t = realloc_string_sub(t, "%P", connectpath);
+ break;
+ case 'S':
+ t = realloc_string_sub(t, "%S", lp_servicename(snum));
+ break;
+ case 'g':
+ t = realloc_string_sub(t, "%g", gidtoname(gid));
+ break;
+ case 'u':
+ t = realloc_string_sub(t, "%u", user);
+ break;
+
+ /* Patch from jkf@soton.ac.uk Left the %N (NIS
+ * server name) in standard_sub_basic as it is
+ * a feature for logon servers, hence uses the
+ * username. The %p (NIS server path) code is
+ * here as it is used instead of the default
+ * "path =" string in [homes] and so needs the
+ * service name, not the username. */
+ case 'p':
+ t = realloc_string_sub(t, "%p", automount_path(lp_servicename(snum)));
+ break;
+
+ default:
+ break;
+ }
+
+ p++;
+ if (t == NULL) {
+ SAFE_FREE(a_string);
+ return NULL;
+ }
+ a_string = t;
+ }
+
+ ret_string = alloc_sub_basic(smb_name, a_string);
+ SAFE_FREE(a_string);
+ return ret_string;
+}
+
+/****************************************************************************
+ Do some standard substitutions in a string.
+****************************************************************************/
+
+void standard_sub_conn(connection_struct *conn, char *str, size_t len)
+{
+ standard_sub_advanced(SNUM(conn), conn->user, conn->connectpath,
+ conn->gid, smb_user_name, str, len);
+}
+
+char *talloc_sub_conn(TALLOC_CTX *mem_ctx, connection_struct *conn, const char *str)
+{
+ return talloc_sub_advanced(mem_ctx, SNUM(conn), conn->user,
+ conn->connectpath, conn->gid,
+ smb_user_name, str);
+}
+
+char *alloc_sub_conn(connection_struct *conn, const char *str)
+{
+ return alloc_sub_advanced(SNUM(conn), conn->user, conn->connectpath,
+ conn->gid, smb_user_name, str);
+}
+
+/****************************************************************************
+ Like standard_sub but by snum.
+****************************************************************************/
+
+void standard_sub_snum(int snum, char *str, size_t len)
+{
+ extern struct current_user current_user;
+ static uid_t cached_uid = -1;
+ static fstring cached_user;
+ /* calling uidtoname() on every substitute would be too expensive, so
+ we cache the result here as nearly every call is for the same uid */
+
+ if (cached_uid != current_user.uid) {
+ fstrcpy(cached_user, uidtoname(current_user.uid));
+ cached_uid = current_user.uid;
+ }
+
+ standard_sub_advanced(snum, cached_user, "", -1,
+ smb_user_name, str, len);
+}
diff --git a/source/lib/sysacls.c b/source/lib/sysacls.c
new file mode 100644
index 00000000000..00d06e4a5ae
--- /dev/null
+++ b/source/lib/sysacls.c
@@ -0,0 +1,3198 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba system utilities for ACL support.
+ Copyright (C) Jeremy Allison 2000.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*
+ This file wraps all differing system ACL interfaces into a consistent
+ one based on the POSIX interface. It also returns the correct errors
+ for older UNIX systems that don't support ACLs.
+
+ The interfaces that each ACL implementation must support are as follows :
+
+ int sys_acl_get_entry( SMB_ACL_T theacl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+ int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+ int sys_acl_get_permset( SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p
+ void *sys_acl_get_qualifier( SMB_ACL_ENTRY_T entry_d)
+ SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type)
+ SMB_ACL_T sys_acl_get_fd(int fd)
+ int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset);
+ int sys_acl_add_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm);
+ char *sys_acl_to_text( SMB_ACL_T theacl, ssize_t *plen)
+ SMB_ACL_T sys_acl_init( int count)
+ int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+ int sys_acl_set_tag_type( SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype)
+ int sys_acl_set_qualifier( SMB_ACL_ENTRY_T entry, void *qual)
+ int sys_acl_set_permset( SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset)
+ int sys_acl_valid( SMB_ACL_T theacl )
+ int sys_acl_set_file( const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+ int sys_acl_set_fd( int fd, SMB_ACL_T theacl)
+ int sys_acl_delete_def_file(const char *path)
+
+ This next one is not POSIX complient - but we *have* to have it !
+ More POSIX braindamage.
+
+ int sys_acl_get_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+
+ The generic POSIX free is the following call. We split this into
+ several different free functions as we may need to add tag info
+ to structures when emulating the POSIX interface.
+
+ int sys_acl_free( void *obj_p)
+
+ The calls we actually use are :
+
+ int sys_acl_free_text(char *text) - free acl_to_text
+ int sys_acl_free_acl(SMB_ACL_T posix_acl)
+ int sys_acl_free_qualifier(void *qualifier, SMB_ACL_TAG_T tagtype)
+
+*/
+
+#if defined(HAVE_POSIX_ACLS)
+
+/* Identity mapping - easy. */
+
+int sys_acl_get_entry( SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ return acl_get_entry( the_acl, entry_id, entry_p);
+}
+
+int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+ return acl_get_tag_type( entry_d, tag_type_p);
+}
+
+int sys_acl_get_permset( SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+ return acl_get_permset( entry_d, permset_p);
+}
+
+void *sys_acl_get_qualifier( SMB_ACL_ENTRY_T entry_d)
+{
+ return acl_get_qualifier( entry_d);
+}
+
+SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type)
+{
+ return acl_get_file( path_p, type);
+}
+
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ return acl_get_fd(fd);
+}
+
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset)
+{
+ return acl_clear_perms(permset);
+}
+
+int sys_acl_add_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+ return acl_add_perm(permset, perm);
+}
+
+int sys_acl_get_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+#if defined(HAVE_ACL_GET_PERM_NP)
+ /*
+ * Required for TrustedBSD-based ACL implementations where
+ * non-POSIX.1e functions are denoted by a _np (non-portable)
+ * suffix.
+ */
+ return acl_get_perm_np(permset, perm);
+#else
+ return acl_get_perm(permset, perm);
+#endif
+}
+
+char *sys_acl_to_text( SMB_ACL_T the_acl, ssize_t *plen)
+{
+ return acl_to_text( the_acl, plen);
+}
+
+SMB_ACL_T sys_acl_init( int count)
+{
+ return acl_init(count);
+}
+
+int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+ return acl_create_entry(pacl, pentry);
+}
+
+int sys_acl_set_tag_type( SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype)
+{
+ return acl_set_tag_type(entry, tagtype);
+}
+
+int sys_acl_set_qualifier( SMB_ACL_ENTRY_T entry, void *qual)
+{
+ return acl_set_qualifier(entry, qual);
+}
+
+int sys_acl_set_permset( SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset)
+{
+ return acl_set_permset(entry, permset);
+}
+
+int sys_acl_valid( SMB_ACL_T theacl )
+{
+ return acl_valid(theacl);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+ return acl_set_file(name, acltype, theacl);
+}
+
+int sys_acl_set_fd( int fd, SMB_ACL_T theacl)
+{
+ return acl_set_fd(fd, theacl);
+}
+
+int sys_acl_delete_def_file(const char *name)
+{
+ return acl_delete_def_file(name);
+}
+
+int sys_acl_free_text(char *text)
+{
+ return acl_free(text);
+}
+
+int sys_acl_free_acl(SMB_ACL_T the_acl)
+{
+ return acl_free(the_acl);
+}
+
+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype)
+{
+ return acl_free(qual);
+}
+
+#elif defined(HAVE_TRU64_ACLS)
+/*
+ * The interface to DEC/Compaq Tru64 UNIX ACLs
+ * is based on Draft 13 of the POSIX spec which is
+ * slightly different from the Draft 16 interface.
+ *
+ * Also, some of the permset manipulation functions
+ * such as acl_clear_perm() and acl_add_perm() appear
+ * to be broken on Tru64 so we have to manipulate
+ * the permission bits in the permset directly.
+ */
+int sys_acl_get_entry( SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ SMB_ACL_ENTRY_T entry;
+
+ if (entry_id == SMB_ACL_FIRST_ENTRY && acl_first_entry(the_acl) != 0) {
+ return -1;
+ }
+
+ errno = 0;
+ if ((entry = acl_get_entry(the_acl)) != NULL) {
+ *entry_p = entry;
+ return 1;
+ }
+
+ return errno ? -1 : 0;
+}
+
+int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+ return acl_get_tag_type( entry_d, tag_type_p);
+}
+
+int sys_acl_get_permset( SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+ return acl_get_permset( entry_d, permset_p);
+}
+
+void *sys_acl_get_qualifier( SMB_ACL_ENTRY_T entry_d)
+{
+ return acl_get_qualifier( entry_d);
+}
+
+SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type)
+{
+ return acl_get_file((char *)path_p, type);
+}
+
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ return acl_get_fd(fd, ACL_TYPE_ACCESS);
+}
+
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset)
+{
+ *permset = 0; /* acl_clear_perm() is broken on Tru64 */
+
+ return 0;
+}
+
+int sys_acl_add_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+ if (perm & ~(SMB_ACL_READ | SMB_ACL_WRITE | SMB_ACL_EXECUTE)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ *permset |= perm; /* acl_add_perm() is broken on Tru64 */
+
+ return 0;
+}
+
+int sys_acl_get_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+ return *permset & perm; /* Tru64 doesn't have acl_get_perm() */
+}
+
+char *sys_acl_to_text( SMB_ACL_T the_acl, ssize_t *plen)
+{
+ return acl_to_text( the_acl, plen);
+}
+
+SMB_ACL_T sys_acl_init( int count)
+{
+ return acl_init(count);
+}
+
+int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+ SMB_ACL_ENTRY_T entry;
+
+ if ((entry = acl_create_entry(pacl)) == NULL) {
+ return -1;
+ }
+
+ *pentry = entry;
+ return 0;
+}
+
+int sys_acl_set_tag_type( SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype)
+{
+ return acl_set_tag_type(entry, tagtype);
+}
+
+int sys_acl_set_qualifier( SMB_ACL_ENTRY_T entry, void *qual)
+{
+ return acl_set_qualifier(entry, qual);
+}
+
+int sys_acl_set_permset( SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset)
+{
+ return acl_set_permset(entry, permset);
+}
+
+int sys_acl_valid( SMB_ACL_T theacl )
+{
+ acl_entry_t entry;
+
+ return acl_valid(theacl, &entry);
+}
+
+int sys_acl_set_file( const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+ return acl_set_file((char *)name, acltype, theacl);
+}
+
+int sys_acl_set_fd( int fd, SMB_ACL_T theacl)
+{
+ return acl_set_fd(fd, ACL_TYPE_ACCESS, theacl);
+}
+
+int sys_acl_delete_def_file(const char *name)
+{
+ return acl_delete_def_file((char *)name);
+}
+
+int sys_acl_free_text(char *text)
+{
+ /*
+ * (void) cast and explicit return 0 are for DEC UNIX
+ * which just #defines acl_free_text() to be free()
+ */
+ (void) acl_free_text(text);
+ return 0;
+}
+
+int sys_acl_free_acl(SMB_ACL_T the_acl)
+{
+ return acl_free(the_acl);
+}
+
+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype)
+{
+ return acl_free_qualifier(qual, tagtype);
+}
+
+#elif defined(HAVE_UNIXWARE_ACLS) || defined(HAVE_SOLARIS_ACLS)
+
+/*
+ * Donated by Michael Davidson <md@sco.COM> for UnixWare / OpenUNIX.
+ * Modified by Toomas Soome <tsoome@ut.ee> for Solaris.
+ */
+
+/*
+ * Note that while this code implements sufficient functionality
+ * to support the sys_acl_* interfaces it does not provide all
+ * of the semantics of the POSIX ACL interfaces.
+ *
+ * In particular, an ACL entry descriptor (SMB_ACL_ENTRY_T) returned
+ * from a call to sys_acl_get_entry() should not be assumed to be
+ * valid after calling any of the following functions, which may
+ * reorder the entries in the ACL.
+ *
+ * sys_acl_valid()
+ * sys_acl_set_file()
+ * sys_acl_set_fd()
+ */
+
+/*
+ * The only difference between Solaris and UnixWare / OpenUNIX is
+ * that the #defines for the ACL operations have different names
+ */
+#if defined(HAVE_UNIXWARE_ACLS)
+
+#define SETACL ACL_SET
+#define GETACL ACL_GET
+#define GETACLCNT ACL_CNT
+
+#endif
+
+
+int sys_acl_get_entry(SMB_ACL_T acl_d, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ if (entry_id != SMB_ACL_FIRST_ENTRY && entry_id != SMB_ACL_NEXT_ENTRY) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_p == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_id == SMB_ACL_FIRST_ENTRY) {
+ acl_d->next = 0;
+ }
+
+ if (acl_d->next < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->next >= acl_d->count) {
+ return 0;
+ }
+
+ *entry_p = &acl_d->acl[acl_d->next++];
+
+ return 1;
+}
+
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *type_p)
+{
+ *type_p = entry_d->a_type;
+
+ return 0;
+}
+
+int sys_acl_get_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+ *permset_p = &entry_d->a_perm;
+
+ return 0;
+}
+
+void *sys_acl_get_qualifier(SMB_ACL_ENTRY_T entry_d)
+{
+ if (entry_d->a_type != SMB_ACL_USER
+ && entry_d->a_type != SMB_ACL_GROUP) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return &entry_d->a_id;
+}
+
+/*
+ * There is no way of knowing what size the ACL returned by
+ * GETACL will be unless you first call GETACLCNT which means
+ * making an additional system call.
+ *
+ * In the hope of avoiding the cost of the additional system
+ * call in most cases, we initially allocate enough space for
+ * an ACL with INITIAL_ACL_SIZE entries. If this turns out to
+ * be too small then we use GETACLCNT to find out the actual
+ * size, reallocate the ACL buffer, and then call GETACL again.
+ */
+
+#define INITIAL_ACL_SIZE 16
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+ SMB_ACL_T acl_d;
+ int count; /* # of ACL entries allocated */
+ int naccess; /* # of access ACL entries */
+ int ndefault; /* # of default ACL entries */
+
+ if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ count = INITIAL_ACL_SIZE;
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+
+ /*
+ * If there isn't enough space for the ACL entries we use
+ * GETACLCNT to determine the actual number of ACL entries
+ * reallocate and try again. This is in a loop because it
+ * is possible that someone else could modify the ACL and
+ * increase the number of entries between the call to
+ * GETACLCNT and the call to GETACL.
+ */
+ while ((count = acl(path_p, GETACL, count, &acl_d->acl[0])) < 0
+ && errno == ENOSPC) {
+
+ sys_acl_free_acl(acl_d);
+
+ if ((count = acl(path_p, GETACLCNT, 0, NULL)) < 0) {
+ return NULL;
+ }
+
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+ }
+
+ if (count < 0) {
+ sys_acl_free_acl(acl_d);
+ return NULL;
+ }
+
+ /*
+ * calculate the number of access and default ACL entries
+ *
+ * Note: we assume that the acl() system call returned a
+ * well formed ACL which is sorted so that all of the
+ * access ACL entries preceed any default ACL entries
+ */
+ for (naccess = 0; naccess < count; naccess++) {
+ if (acl_d->acl[naccess].a_type & ACL_DEFAULT)
+ break;
+ }
+ ndefault = count - naccess;
+
+ /*
+ * if the caller wants the default ACL we have to copy
+ * the entries down to the start of the acl[] buffer
+ * and mask out the ACL_DEFAULT flag from the type field
+ */
+ if (type == SMB_ACL_TYPE_DEFAULT) {
+ int i, j;
+
+ for (i = 0, j = naccess; i < ndefault; i++, j++) {
+ acl_d->acl[i] = acl_d->acl[j];
+ acl_d->acl[i].a_type &= ~ACL_DEFAULT;
+ }
+
+ acl_d->count = ndefault;
+ } else {
+ acl_d->count = naccess;
+ }
+
+ return acl_d;
+}
+
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ SMB_ACL_T acl_d;
+ int count; /* # of ACL entries allocated */
+ int naccess; /* # of access ACL entries */
+
+ count = INITIAL_ACL_SIZE;
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+
+ while ((count = facl(fd, GETACL, count, &acl_d->acl[0])) < 0
+ && errno == ENOSPC) {
+
+ sys_acl_free_acl(acl_d);
+
+ if ((count = facl(fd, GETACLCNT, 0, NULL)) < 0) {
+ return NULL;
+ }
+
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+ }
+
+ if (count < 0) {
+ sys_acl_free_acl(acl_d);
+ return NULL;
+ }
+
+ /*
+ * calculate the number of access ACL entries
+ */
+ for (naccess = 0; naccess < count; naccess++) {
+ if (acl_d->acl[naccess].a_type & ACL_DEFAULT)
+ break;
+ }
+
+ acl_d->count = naccess;
+
+ return acl_d;
+}
+
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset_d)
+{
+ *permset_d = 0;
+
+ return 0;
+}
+
+int sys_acl_add_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm)
+{
+ if (perm != SMB_ACL_READ && perm != SMB_ACL_WRITE
+ && perm != SMB_ACL_EXECUTE) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (permset_d == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ *permset_d |= perm;
+
+ return 0;
+}
+
+int sys_acl_get_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm)
+{
+ return *permset_d & perm;
+}
+
+char *sys_acl_to_text(SMB_ACL_T acl_d, ssize_t *len_p)
+{
+ int i;
+ int len, maxlen;
+ char *text;
+
+ /*
+ * use an initial estimate of 20 bytes per ACL entry
+ * when allocating memory for the text representation
+ * of the ACL
+ */
+ len = 0;
+ maxlen = 20 * acl_d->count;
+ if ((text = malloc(maxlen)) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ for (i = 0; i < acl_d->count; i++) {
+ struct acl *ap = &acl_d->acl[i];
+ struct passwd *pw;
+ struct group *gr;
+ char tagbuf[12];
+ char idbuf[12];
+ char *tag;
+ char *id = "";
+ char perms[4];
+ int nbytes;
+
+ switch (ap->a_type) {
+ /*
+ * for debugging purposes it's probably more
+ * useful to dump unknown tag types rather
+ * than just returning an error
+ */
+ default:
+ slprintf(tagbuf, sizeof(tagbuf)-1, "0x%x",
+ ap->a_type);
+ tag = tagbuf;
+ slprintf(idbuf, sizeof(idbuf)-1, "%ld",
+ (long)ap->a_id);
+ id = idbuf;
+ break;
+
+ case SMB_ACL_USER:
+ id = uidtoname(ap->a_id);
+ case SMB_ACL_USER_OBJ:
+ tag = "user";
+ break;
+
+ case SMB_ACL_GROUP:
+ if ((gr = getgrgid(ap->a_id)) == NULL) {
+ slprintf(idbuf, sizeof(idbuf)-1, "%ld",
+ (long)ap->a_id);
+ id = idbuf;
+ } else {
+ id = gr->gr_name;
+ }
+ case SMB_ACL_GROUP_OBJ:
+ tag = "group";
+ break;
+
+ case SMB_ACL_OTHER:
+ tag = "other";
+ break;
+
+ case SMB_ACL_MASK:
+ tag = "mask";
+ break;
+
+ }
+
+ perms[0] = (ap->a_perm & SMB_ACL_READ) ? 'r' : '-';
+ perms[1] = (ap->a_perm & SMB_ACL_WRITE) ? 'w' : '-';
+ perms[2] = (ap->a_perm & SMB_ACL_EXECUTE) ? 'x' : '-';
+ perms[3] = '\0';
+
+ /* <tag> : <qualifier> : rwx \n \0 */
+ nbytes = strlen(tag) + 1 + strlen(id) + 1 + 3 + 1 + 1;
+
+ /*
+ * If this entry would overflow the buffer
+ * allocate enough additional memory for this
+ * entry and an estimate of another 20 bytes
+ * for each entry still to be processed
+ */
+ if ((len + nbytes) > maxlen) {
+ char *oldtext = text;
+
+ maxlen += nbytes + 20 * (acl_d->count - i);
+
+ if ((text = Realloc(oldtext, maxlen)) == NULL) {
+ SAFE_FREE(oldtext);
+ errno = ENOMEM;
+ return NULL;
+ }
+ }
+
+ slprintf(&text[len], nbytes-1, "%s:%s:%s\n", tag, id, perms);
+ len += nbytes - 1;
+ }
+
+ if (len_p)
+ *len_p = len;
+
+ return text;
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+ SMB_ACL_T a;
+
+ if (count < 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /*
+ * note that since the definition of the structure pointed
+ * to by the SMB_ACL_T includes the first element of the
+ * acl[] array, this actually allocates an ACL with room
+ * for (count+1) entries
+ */
+ if ((a = malloc(sizeof(*a) + count * sizeof(struct acl))) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ a->size = count + 1;
+ a->count = 0;
+ a->next = -1;
+
+ return a;
+}
+
+
+int sys_acl_create_entry(SMB_ACL_T *acl_p, SMB_ACL_ENTRY_T *entry_p)
+{
+ SMB_ACL_T acl_d;
+ SMB_ACL_ENTRY_T entry_d;
+
+ if (acl_p == NULL || entry_p == NULL || (acl_d = *acl_p) == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->count >= acl_d->size) {
+ errno = ENOSPC;
+ return -1;
+ }
+
+ entry_d = &acl_d->acl[acl_d->count++];
+ entry_d->a_type = 0;
+ entry_d->a_id = -1;
+ entry_d->a_perm = 0;
+ *entry_p = entry_d;
+
+ return 0;
+}
+
+int sys_acl_set_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T tag_type)
+{
+ switch (tag_type) {
+ case SMB_ACL_USER:
+ case SMB_ACL_USER_OBJ:
+ case SMB_ACL_GROUP:
+ case SMB_ACL_GROUP_OBJ:
+ case SMB_ACL_OTHER:
+ case SMB_ACL_MASK:
+ entry_d->a_type = tag_type;
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ return 0;
+}
+
+int sys_acl_set_qualifier(SMB_ACL_ENTRY_T entry_d, void *qual_p)
+{
+ if (entry_d->a_type != SMB_ACL_GROUP
+ && entry_d->a_type != SMB_ACL_USER) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ entry_d->a_id = *((id_t *)qual_p);
+
+ return 0;
+}
+
+int sys_acl_set_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T permset_d)
+{
+ if (*permset_d & ~(SMB_ACL_READ|SMB_ACL_WRITE|SMB_ACL_EXECUTE)) {
+ return EINVAL;
+ }
+
+ entry_d->a_perm = *permset_d;
+
+ return 0;
+}
+
+/*
+ * sort the ACL and check it for validity
+ *
+ * if it's a minimal ACL with only 4 entries then we
+ * need to recalculate the mask permissions to make
+ * sure that they are the same as the GROUP_OBJ
+ * permissions as required by the UnixWare acl() system call.
+ *
+ * (note: since POSIX allows minimal ACLs which only contain
+ * 3 entries - ie there is no mask entry - we should, in theory,
+ * check for this and add a mask entry if necessary - however
+ * we "know" that the caller of this interface always specifies
+ * a mask so, in practice "this never happens" (tm) - if it *does*
+ * happen aclsort() will fail and return an error and someone will
+ * have to fix it ...)
+ */
+
+static int acl_sort(SMB_ACL_T acl_d)
+{
+ int fixmask = (acl_d->count <= 4);
+
+ if (aclsort(acl_d->count, fixmask, acl_d->acl) != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+int sys_acl_valid(SMB_ACL_T acl_d)
+{
+ return acl_sort(acl_d);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T type, SMB_ACL_T acl_d)
+{
+ struct stat s;
+ struct acl *acl_p;
+ int acl_count;
+ struct acl *acl_buf = NULL;
+ int ret;
+
+ if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_sort(acl_d) != 0) {
+ return -1;
+ }
+
+ acl_p = &acl_d->acl[0];
+ acl_count = acl_d->count;
+
+ /*
+ * if it's a directory there is extra work to do
+ * since the acl() system call will replace both
+ * the access ACLs and the default ACLs (if any)
+ */
+ if (stat(name, &s) != 0) {
+ return -1;
+ }
+ if (S_ISDIR(s.st_mode)) {
+ SMB_ACL_T acc_acl;
+ SMB_ACL_T def_acl;
+ SMB_ACL_T tmp_acl;
+ int i;
+
+ if (type == SMB_ACL_TYPE_ACCESS) {
+ acc_acl = acl_d;
+ def_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_DEFAULT);
+
+ } else {
+ def_acl = acl_d;
+ acc_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_ACCESS);
+ }
+
+ if (tmp_acl == NULL) {
+ return -1;
+ }
+
+ /*
+ * allocate a temporary buffer for the complete ACL
+ */
+ acl_count = acc_acl->count + def_acl->count;
+ acl_p = acl_buf = malloc(acl_count * sizeof(acl_buf[0]));
+
+ if (acl_buf == NULL) {
+ sys_acl_free_acl(tmp_acl);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /*
+ * copy the access control and default entries into the buffer
+ */
+ memcpy(&acl_buf[0], &acc_acl->acl[0],
+ acc_acl->count * sizeof(acl_buf[0]));
+
+ memcpy(&acl_buf[acc_acl->count], &def_acl->acl[0],
+ def_acl->count * sizeof(acl_buf[0]));
+
+ /*
+ * set the ACL_DEFAULT flag on the default entries
+ */
+ for (i = acc_acl->count; i < acl_count; i++) {
+ acl_buf[i].a_type |= ACL_DEFAULT;
+ }
+
+ sys_acl_free_acl(tmp_acl);
+
+ } else if (type != SMB_ACL_TYPE_ACCESS) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ret = acl(name, SETACL, acl_count, acl_p);
+
+ SAFE_FREE(acl_buf);
+
+ return ret;
+}
+
+int sys_acl_set_fd(int fd, SMB_ACL_T acl_d)
+{
+ if (acl_sort(acl_d) != 0) {
+ return -1;
+ }
+
+ return facl(fd, SETACL, acl_d->count, &acl_d->acl[0]);
+}
+
+int sys_acl_delete_def_file(const char *path)
+{
+ SMB_ACL_T acl_d;
+ int ret;
+
+ /*
+ * fetching the access ACL and rewriting it has
+ * the effect of deleting the default ACL
+ */
+ if ((acl_d = sys_acl_get_file(path, SMB_ACL_TYPE_ACCESS)) == NULL) {
+ return -1;
+ }
+
+ ret = acl(path, SETACL, acl_d->count, acl_d->acl);
+
+ sys_acl_free_acl(acl_d);
+
+ return ret;
+}
+
+int sys_acl_free_text(char *text)
+{
+ SAFE_FREE(text);
+ return 0;
+}
+
+int sys_acl_free_acl(SMB_ACL_T acl_d)
+{
+ SAFE_FREE(acl_d);
+ return 0;
+}
+
+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype)
+{
+ return 0;
+}
+
+#elif defined(HAVE_HPUX_ACLS)
+#include <dl.h>
+
+/*
+ * Based on the Solaris/SCO code - with modifications.
+ */
+
+/*
+ * Note that while this code implements sufficient functionality
+ * to support the sys_acl_* interfaces it does not provide all
+ * of the semantics of the POSIX ACL interfaces.
+ *
+ * In particular, an ACL entry descriptor (SMB_ACL_ENTRY_T) returned
+ * from a call to sys_acl_get_entry() should not be assumed to be
+ * valid after calling any of the following functions, which may
+ * reorder the entries in the ACL.
+ *
+ * sys_acl_valid()
+ * sys_acl_set_file()
+ * sys_acl_set_fd()
+ */
+
+/* This checks if the POSIX ACL system call is defined */
+/* which basically corresponds to whether JFS 3.3 or */
+/* higher is installed. If acl() was called when it */
+/* isn't defined, it causes the process to core dump */
+/* so it is important to check this and avoid acl() */
+/* calls if it isn't there. */
+
+static BOOL hpux_acl_call_presence(void)
+{
+
+ shl_t handle = NULL;
+ void *value;
+ int ret_val=0;
+ static BOOL already_checked=0;
+
+ if(already_checked)
+ return True;
+
+
+ ret_val = shl_findsym(&handle, "acl", TYPE_PROCEDURE, &value);
+
+ if(ret_val != 0) {
+ DEBUG(5, ("hpux_acl_call_presence: shl_findsym() returned %d, errno = %d, error %s\n",
+ ret_val, errno, strerror(errno)));
+ DEBUG(5,("hpux_acl_call_presence: acl() system call is not present. Check if you have JFS 3.3 and above?\n"));
+ return False;
+ }
+
+ DEBUG(10,("hpux_acl_call_presence: acl() system call is present. We have JFS 3.3 or above \n"));
+
+ already_checked = True;
+ return True;
+}
+
+int sys_acl_get_entry(SMB_ACL_T acl_d, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ if (entry_id != SMB_ACL_FIRST_ENTRY && entry_id != SMB_ACL_NEXT_ENTRY) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_p == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_id == SMB_ACL_FIRST_ENTRY) {
+ acl_d->next = 0;
+ }
+
+ if (acl_d->next < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->next >= acl_d->count) {
+ return 0;
+ }
+
+ *entry_p = &acl_d->acl[acl_d->next++];
+
+ return 1;
+}
+
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *type_p)
+{
+ *type_p = entry_d->a_type;
+
+ return 0;
+}
+
+int sys_acl_get_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+ *permset_p = &entry_d->a_perm;
+
+ return 0;
+}
+
+void *sys_acl_get_qualifier(SMB_ACL_ENTRY_T entry_d)
+{
+ if (entry_d->a_type != SMB_ACL_USER
+ && entry_d->a_type != SMB_ACL_GROUP) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return &entry_d->a_id;
+}
+
+/*
+ * There is no way of knowing what size the ACL returned by
+ * ACL_GET will be unless you first call ACL_CNT which means
+ * making an additional system call.
+ *
+ * In the hope of avoiding the cost of the additional system
+ * call in most cases, we initially allocate enough space for
+ * an ACL with INITIAL_ACL_SIZE entries. If this turns out to
+ * be too small then we use ACL_CNT to find out the actual
+ * size, reallocate the ACL buffer, and then call ACL_GET again.
+ */
+
+#define INITIAL_ACL_SIZE 16
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+ SMB_ACL_T acl_d;
+ int count; /* # of ACL entries allocated */
+ int naccess; /* # of access ACL entries */
+ int ndefault; /* # of default ACL entries */
+
+ if(hpux_acl_call_presence() == False) {
+ /* Looks like we don't have the acl() system call on HPUX.
+ * May be the system doesn't have the latest version of JFS.
+ */
+ return NULL;
+ }
+
+ if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ count = INITIAL_ACL_SIZE;
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+
+ /*
+ * If there isn't enough space for the ACL entries we use
+ * ACL_CNT to determine the actual number of ACL entries
+ * reallocate and try again. This is in a loop because it
+ * is possible that someone else could modify the ACL and
+ * increase the number of entries between the call to
+ * ACL_CNT and the call to ACL_GET.
+ */
+ while ((count = acl(path_p, ACL_GET, count, &acl_d->acl[0])) < 0 && errno == ENOSPC) {
+
+ sys_acl_free_acl(acl_d);
+
+ if ((count = acl(path_p, ACL_CNT, 0, NULL)) < 0) {
+ return NULL;
+ }
+
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+ }
+
+ if (count < 0) {
+ sys_acl_free_acl(acl_d);
+ return NULL;
+ }
+
+ /*
+ * calculate the number of access and default ACL entries
+ *
+ * Note: we assume that the acl() system call returned a
+ * well formed ACL which is sorted so that all of the
+ * access ACL entries preceed any default ACL entries
+ */
+ for (naccess = 0; naccess < count; naccess++) {
+ if (acl_d->acl[naccess].a_type & ACL_DEFAULT)
+ break;
+ }
+ ndefault = count - naccess;
+
+ /*
+ * if the caller wants the default ACL we have to copy
+ * the entries down to the start of the acl[] buffer
+ * and mask out the ACL_DEFAULT flag from the type field
+ */
+ if (type == SMB_ACL_TYPE_DEFAULT) {
+ int i, j;
+
+ for (i = 0, j = naccess; i < ndefault; i++, j++) {
+ acl_d->acl[i] = acl_d->acl[j];
+ acl_d->acl[i].a_type &= ~ACL_DEFAULT;
+ }
+
+ acl_d->count = ndefault;
+ } else {
+ acl_d->count = naccess;
+ }
+
+ return acl_d;
+}
+
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ /*
+ * HPUX doesn't have the facl call. Fake it using the path.... JRA.
+ */
+
+ files_struct *fsp = file_find_fd(fd);
+
+ if (fsp == NULL) {
+ errno = EBADF;
+ return NULL;
+ }
+
+ /*
+ * We know we're in the same conn context. So we
+ * can use the relative path.
+ */
+
+ return sys_acl_get_file(fsp->fsp_name, SMB_ACL_TYPE_ACCESS);
+}
+
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset_d)
+{
+ *permset_d = 0;
+
+ return 0;
+}
+
+int sys_acl_add_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm)
+{
+ if (perm != SMB_ACL_READ && perm != SMB_ACL_WRITE
+ && perm != SMB_ACL_EXECUTE) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (permset_d == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ *permset_d |= perm;
+
+ return 0;
+}
+
+int sys_acl_get_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm)
+{
+ return *permset_d & perm;
+}
+
+char *sys_acl_to_text(SMB_ACL_T acl_d, ssize_t *len_p)
+{
+ int i;
+ int len, maxlen;
+ char *text;
+
+ /*
+ * use an initial estimate of 20 bytes per ACL entry
+ * when allocating memory for the text representation
+ * of the ACL
+ */
+ len = 0;
+ maxlen = 20 * acl_d->count;
+ if ((text = malloc(maxlen)) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ for (i = 0; i < acl_d->count; i++) {
+ struct acl *ap = &acl_d->acl[i];
+ struct passwd *pw;
+ struct group *gr;
+ char tagbuf[12];
+ char idbuf[12];
+ char *tag;
+ char *id = "";
+ char perms[4];
+ int nbytes;
+
+ switch (ap->a_type) {
+ /*
+ * for debugging purposes it's probably more
+ * useful to dump unknown tag types rather
+ * than just returning an error
+ */
+ default:
+ slprintf(tagbuf, sizeof(tagbuf)-1, "0x%x",
+ ap->a_type);
+ tag = tagbuf;
+ slprintf(idbuf, sizeof(idbuf)-1, "%ld",
+ (long)ap->a_id);
+ id = idbuf;
+ break;
+
+ case SMB_ACL_USER:
+ id = uidtoname(ap->a_id);
+ case SMB_ACL_USER_OBJ:
+ tag = "user";
+ break;
+
+ case SMB_ACL_GROUP:
+ if ((gr = getgrgid(ap->a_id)) == NULL) {
+ slprintf(idbuf, sizeof(idbuf)-1, "%ld",
+ (long)ap->a_id);
+ id = idbuf;
+ } else {
+ id = gr->gr_name;
+ }
+ case SMB_ACL_GROUP_OBJ:
+ tag = "group";
+ break;
+
+ case SMB_ACL_OTHER:
+ tag = "other";
+ break;
+
+ case SMB_ACL_MASK:
+ tag = "mask";
+ break;
+
+ }
+
+ perms[0] = (ap->a_perm & SMB_ACL_READ) ? 'r' : '-';
+ perms[1] = (ap->a_perm & SMB_ACL_WRITE) ? 'w' : '-';
+ perms[2] = (ap->a_perm & SMB_ACL_EXECUTE) ? 'x' : '-';
+ perms[3] = '\0';
+
+ /* <tag> : <qualifier> : rwx \n \0 */
+ nbytes = strlen(tag) + 1 + strlen(id) + 1 + 3 + 1 + 1;
+
+ /*
+ * If this entry would overflow the buffer
+ * allocate enough additional memory for this
+ * entry and an estimate of another 20 bytes
+ * for each entry still to be processed
+ */
+ if ((len + nbytes) > maxlen) {
+ char *oldtext = text;
+
+ maxlen += nbytes + 20 * (acl_d->count - i);
+
+ if ((text = Realloc(oldtext, maxlen)) == NULL) {
+ free(oldtext);
+ errno = ENOMEM;
+ return NULL;
+ }
+ }
+
+ slprintf(&text[len], nbytes-1, "%s:%s:%s\n", tag, id, perms);
+ len += nbytes - 1;
+ }
+
+ if (len_p)
+ *len_p = len;
+
+ return text;
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+ SMB_ACL_T a;
+
+ if (count < 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /*
+ * note that since the definition of the structure pointed
+ * to by the SMB_ACL_T includes the first element of the
+ * acl[] array, this actually allocates an ACL with room
+ * for (count+1) entries
+ */
+ if ((a = malloc(sizeof(*a) + count * sizeof(struct acl))) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ a->size = count + 1;
+ a->count = 0;
+ a->next = -1;
+
+ return a;
+}
+
+
+int sys_acl_create_entry(SMB_ACL_T *acl_p, SMB_ACL_ENTRY_T *entry_p)
+{
+ SMB_ACL_T acl_d;
+ SMB_ACL_ENTRY_T entry_d;
+
+ if (acl_p == NULL || entry_p == NULL || (acl_d = *acl_p) == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->count >= acl_d->size) {
+ errno = ENOSPC;
+ return -1;
+ }
+
+ entry_d = &acl_d->acl[acl_d->count++];
+ entry_d->a_type = 0;
+ entry_d->a_id = -1;
+ entry_d->a_perm = 0;
+ *entry_p = entry_d;
+
+ return 0;
+}
+
+int sys_acl_set_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T tag_type)
+{
+ switch (tag_type) {
+ case SMB_ACL_USER:
+ case SMB_ACL_USER_OBJ:
+ case SMB_ACL_GROUP:
+ case SMB_ACL_GROUP_OBJ:
+ case SMB_ACL_OTHER:
+ case SMB_ACL_MASK:
+ entry_d->a_type = tag_type;
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ return 0;
+}
+
+int sys_acl_set_qualifier(SMB_ACL_ENTRY_T entry_d, void *qual_p)
+{
+ if (entry_d->a_type != SMB_ACL_GROUP
+ && entry_d->a_type != SMB_ACL_USER) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ entry_d->a_id = *((id_t *)qual_p);
+
+ return 0;
+}
+
+int sys_acl_set_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T permset_d)
+{
+ if (*permset_d & ~(SMB_ACL_READ|SMB_ACL_WRITE|SMB_ACL_EXECUTE)) {
+ return EINVAL;
+ }
+
+ entry_d->a_perm = *permset_d;
+
+ return 0;
+}
+
+/* Structure to capture the count for each type of ACE. */
+
+struct hpux_acl_types {
+ int n_user;
+ int n_def_user;
+ int n_user_obj;
+ int n_def_user_obj;
+
+ int n_group;
+ int n_def_group;
+ int n_group_obj;
+ int n_def_group_obj;
+
+ int n_other;
+ int n_other_obj;
+ int n_def_other_obj;
+
+ int n_class_obj;
+ int n_def_class_obj;
+
+ int n_illegal_obj;
+};
+
+/* count_obj:
+ * Counts the different number of objects in a given array of ACL
+ * structures.
+ * Inputs:
+ *
+ * acl_count - Count of ACLs in the array of ACL strucutres.
+ * aclp - Array of ACL structures.
+ * acl_type_count - Pointer to acl_types structure. Should already be
+ * allocated.
+ * Output:
+ *
+ * acl_type_count - This structure is filled up with counts of various
+ * acl types.
+ */
+
+static int hpux_count_obj(int acl_count, struct acl *aclp, struct hpux_acl_types *acl_type_count)
+{
+ int i;
+
+ memset(acl_type_count, 0, sizeof(struct hpux_acl_types));
+
+ for(i=0;i<acl_count;i++) {
+ switch(aclp[i].a_type) {
+ case USER:
+ acl_type_count->n_user++;
+ break;
+ case USER_OBJ:
+ acl_type_count->n_user_obj++;
+ break;
+ case DEF_USER_OBJ:
+ acl_type_count->n_def_user_obj++;
+ break;
+ case GROUP:
+ acl_type_count->n_group++;
+ break;
+ case GROUP_OBJ:
+ acl_type_count->n_group_obj++;
+ break;
+ case DEF_GROUP_OBJ:
+ acl_type_count->n_def_group_obj++;
+ break;
+ case OTHER_OBJ:
+ acl_type_count->n_other_obj++;
+ break;
+ case DEF_OTHER_OBJ:
+ acl_type_count->n_def_other_obj++;
+ break;
+ case CLASS_OBJ:
+ acl_type_count->n_class_obj++;
+ break;
+ case DEF_CLASS_OBJ:
+ acl_type_count->n_def_class_obj++;
+ break;
+ case DEF_USER:
+ acl_type_count->n_def_user++;
+ break;
+ case DEF_GROUP:
+ acl_type_count->n_def_group++;
+ break;
+ default:
+ acl_type_count->n_illegal_obj++;
+ break;
+ }
+ }
+}
+
+/* swap_acl_entries: Swaps two ACL entries.
+ *
+ * Inputs: aclp0, aclp1 - ACL entries to be swapped.
+ */
+
+static void hpux_swap_acl_entries(struct acl *aclp0, struct acl *aclp1)
+{
+ struct acl temp_acl;
+
+ temp_acl.a_type = aclp0->a_type;
+ temp_acl.a_id = aclp0->a_id;
+ temp_acl.a_perm = aclp0->a_perm;
+
+ aclp0->a_type = aclp1->a_type;
+ aclp0->a_id = aclp1->a_id;
+ aclp0->a_perm = aclp1->a_perm;
+
+ aclp1->a_type = temp_acl.a_type;
+ aclp1->a_id = temp_acl.a_id;
+ aclp1->a_perm = temp_acl.a_perm;
+}
+
+/* prohibited_duplicate_type
+ * Identifies if given ACL type can have duplicate entries or
+ * not.
+ *
+ * Inputs: acl_type - ACL Type.
+ *
+ * Outputs:
+ *
+ * Return..
+ *
+ * True - If the ACL type matches any of the prohibited types.
+ * False - If the ACL type doesn't match any of the prohibited types.
+ */
+
+static BOOL hpux_prohibited_duplicate_type(int acl_type)
+{
+ switch(acl_type) {
+ case USER:
+ case GROUP:
+ case DEF_USER:
+ case DEF_GROUP:
+ return True;
+ default:
+ return False;
+ }
+}
+
+/* get_needed_class_perm
+ * Returns the permissions of a ACL structure only if the ACL
+ * type matches one of the pre-determined types for computing
+ * CLASS_OBJ permissions.
+ *
+ * Inputs: aclp - Pointer to ACL structure.
+ */
+
+static int hpux_get_needed_class_perm(struct acl *aclp)
+{
+ switch(aclp->a_type) {
+ case USER:
+ case GROUP_OBJ:
+ case GROUP:
+ case DEF_USER_OBJ:
+ case DEF_USER:
+ case DEF_GROUP_OBJ:
+ case DEF_GROUP:
+ case DEF_CLASS_OBJ:
+ case DEF_OTHER_OBJ:
+ return aclp->a_perm;
+ default:
+ return 0;
+ }
+}
+
+/* acl_sort for HPUX.
+ * Sorts the array of ACL structures as per the description in
+ * aclsort man page. Refer to aclsort man page for more details
+ *
+ * Inputs:
+ *
+ * acl_count - Count of ACLs in the array of ACL structures.
+ * calclass - If this is not zero, then we compute the CLASS_OBJ
+ * permissions.
+ * aclp - Array of ACL structures.
+ *
+ * Outputs:
+ *
+ * aclp - Sorted array of ACL structures.
+ *
+ * Outputs:
+ *
+ * Returns 0 for success -1 for failure. Prints a message to the Samba
+ * debug log in case of failure.
+ */
+
+static int hpux_acl_sort(int acl_count, int calclass, struct acl *aclp)
+{
+#if !defined(HAVE_HPUX_ACLSORT)
+ /*
+ * The aclsort() system call is availabe on the latest HPUX General
+ * Patch Bundles. So for HPUX, we developed our version of acl_sort
+ * function. Because, we don't want to update to a new
+ * HPUX GR bundle just for aclsort() call.
+ */
+
+ struct hpux_acl_types acl_obj_count;
+ int n_class_obj_perm = 0;
+ int i, j;
+
+ if(!acl_count) {
+ DEBUG(10,("Zero acl count passed. Returning Success\n"));
+ return 0;
+ }
+
+ if(aclp == NULL) {
+ DEBUG(0,("Null ACL pointer in hpux_acl_sort. Returning Failure. \n"));
+ return -1;
+ }
+
+ /* Count different types of ACLs in the ACLs array */
+
+ hpux_count_obj(acl_count, aclp, &acl_obj_count);
+
+ /* There should be only one entry each of type USER_OBJ, GROUP_OBJ,
+ * CLASS_OBJ and OTHER_OBJ
+ */
+
+ if( (acl_obj_count.n_user_obj != 1) ||
+ (acl_obj_count.n_group_obj != 1) ||
+ (acl_obj_count.n_class_obj != 1) ||
+ (acl_obj_count.n_other_obj != 1)
+ ) {
+ DEBUG(0,("hpux_acl_sort: More than one entry or no entries for \
+USER OBJ or GROUP_OBJ or OTHER_OBJ or CLASS_OBJ\n"));
+ return -1;
+ }
+
+ /* If any of the default objects are present, there should be only
+ * one of them each.
+ */
+
+ if( (acl_obj_count.n_def_user_obj > 1) || (acl_obj_count.n_def_group_obj > 1) ||
+ (acl_obj_count.n_def_other_obj > 1) || (acl_obj_count.n_def_class_obj > 1) ) {
+ DEBUG(0,("hpux_acl_sort: More than one entry for DEF_CLASS_OBJ \
+or DEF_USER_OBJ or DEF_GROUP_OBJ or DEF_OTHER_OBJ\n"));
+ return -1;
+ }
+
+ /* We now have proper number of OBJ and DEF_OBJ entries. Now sort the acl
+ * structures.
+ *
+ * Sorting crieteria - First sort by ACL type. If there are multiple entries of
+ * same ACL type, sort by ACL id.
+ *
+ * I am using the trival kind of sorting method here because, performance isn't
+ * really effected by the ACLs feature. More over there aren't going to be more
+ * than 17 entries on HPUX.
+ */
+
+ for(i=0; i<acl_count;i++) {
+ for (j=i+1; j<acl_count; j++) {
+ if( aclp[i].a_type > aclp[j].a_type ) {
+ /* ACL entries out of order, swap them */
+
+ hpux_swap_acl_entries((aclp+i), (aclp+j));
+
+ } else if ( aclp[i].a_type == aclp[j].a_type ) {
+
+ /* ACL entries of same type, sort by id */
+
+ if(aclp[i].a_id > aclp[j].a_id) {
+ hpux_swap_acl_entries((aclp+i), (aclp+j));
+ } else if (aclp[i].a_id == aclp[j].a_id) {
+ /* We have a duplicate entry. */
+ if(hpux_prohibited_duplicate_type(aclp[i].a_type)) {
+ DEBUG(0, ("hpux_acl_sort: Duplicate entry: Type(hex): %x Id: %d\n",
+ aclp[i].a_type, aclp[i].a_id));
+ return -1;
+ }
+ }
+
+ }
+ }
+ }
+
+ /* set the class obj permissions to the computed one. */
+ if(calclass) {
+ int n_class_obj_index = -1;
+
+ for(i=0;i<acl_count;i++) {
+ n_class_obj_perm |= hpux_get_needed_class_perm((aclp+i));
+
+ if(aclp[i].a_type == CLASS_OBJ)
+ n_class_obj_index = i;
+ }
+ aclp[n_class_obj_index].a_perm = n_class_obj_perm;
+ }
+
+ return 0;
+#else
+ return aclsort(acl_count, calclass, aclp);
+#endif
+}
+
+/*
+ * sort the ACL and check it for validity
+ *
+ * if it's a minimal ACL with only 4 entries then we
+ * need to recalculate the mask permissions to make
+ * sure that they are the same as the GROUP_OBJ
+ * permissions as required by the UnixWare acl() system call.
+ *
+ * (note: since POSIX allows minimal ACLs which only contain
+ * 3 entries - ie there is no mask entry - we should, in theory,
+ * check for this and add a mask entry if necessary - however
+ * we "know" that the caller of this interface always specifies
+ * a mask so, in practice "this never happens" (tm) - if it *does*
+ * happen aclsort() will fail and return an error and someone will
+ * have to fix it ...)
+ */
+
+static int acl_sort(SMB_ACL_T acl_d)
+{
+ int fixmask = (acl_d->count <= 4);
+
+ if (hpux_acl_sort(acl_d->count, fixmask, acl_d->acl) != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+int sys_acl_valid(SMB_ACL_T acl_d)
+{
+ return acl_sort(acl_d);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T type, SMB_ACL_T acl_d)
+{
+ struct stat s;
+ struct acl *acl_p;
+ int acl_count;
+ struct acl *acl_buf = NULL;
+ int ret;
+
+ if(hpux_acl_call_presence() == False) {
+ /* Looks like we don't have the acl() system call on HPUX.
+ * May be the system doesn't have the latest version of JFS.
+ */
+ errno=ENOSYS;
+ return -1;
+ }
+
+ if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_sort(acl_d) != 0) {
+ return -1;
+ }
+
+ acl_p = &acl_d->acl[0];
+ acl_count = acl_d->count;
+
+ /*
+ * if it's a directory there is extra work to do
+ * since the acl() system call will replace both
+ * the access ACLs and the default ACLs (if any)
+ */
+ if (stat(name, &s) != 0) {
+ return -1;
+ }
+ if (S_ISDIR(s.st_mode)) {
+ SMB_ACL_T acc_acl;
+ SMB_ACL_T def_acl;
+ SMB_ACL_T tmp_acl;
+ int i;
+
+ if (type == SMB_ACL_TYPE_ACCESS) {
+ acc_acl = acl_d;
+ def_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_DEFAULT);
+
+ } else {
+ def_acl = acl_d;
+ acc_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_ACCESS);
+ }
+
+ if (tmp_acl == NULL) {
+ return -1;
+ }
+
+ /*
+ * allocate a temporary buffer for the complete ACL
+ */
+ acl_count = acc_acl->count + def_acl->count;
+ acl_p = acl_buf = malloc(acl_count * sizeof(acl_buf[0]));
+
+ if (acl_buf == NULL) {
+ sys_acl_free_acl(tmp_acl);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /*
+ * copy the access control and default entries into the buffer
+ */
+ memcpy(&acl_buf[0], &acc_acl->acl[0],
+ acc_acl->count * sizeof(acl_buf[0]));
+
+ memcpy(&acl_buf[acc_acl->count], &def_acl->acl[0],
+ def_acl->count * sizeof(acl_buf[0]));
+
+ /*
+ * set the ACL_DEFAULT flag on the default entries
+ */
+ for (i = acc_acl->count; i < acl_count; i++) {
+ acl_buf[i].a_type |= ACL_DEFAULT;
+ }
+
+ sys_acl_free_acl(tmp_acl);
+
+ } else if (type != SMB_ACL_TYPE_ACCESS) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ret = acl(name, ACL_SET, acl_count, acl_p);
+
+ if (acl_buf) {
+ free(acl_buf);
+ }
+
+ return ret;
+}
+
+int sys_acl_set_fd(int fd, SMB_ACL_T acl_d)
+{
+ /*
+ * HPUX doesn't have the facl call. Fake it using the path.... JRA.
+ */
+
+ files_struct *fsp = file_find_fd(fd);
+
+ if (fsp == NULL) {
+ errno = EBADF;
+ return NULL;
+ }
+
+ if (acl_sort(acl_d) != 0) {
+ return -1;
+ }
+
+ /*
+ * We know we're in the same conn context. So we
+ * can use the relative path.
+ */
+
+ return sys_acl_set_file(fsp->fsp_name, SMB_ACL_TYPE_ACCESS, acl_d);
+}
+
+int sys_acl_delete_def_file(const char *path)
+{
+ SMB_ACL_T acl_d;
+ int ret;
+
+ /*
+ * fetching the access ACL and rewriting it has
+ * the effect of deleting the default ACL
+ */
+ if ((acl_d = sys_acl_get_file(path, SMB_ACL_TYPE_ACCESS)) == NULL) {
+ return -1;
+ }
+
+ ret = acl(path, ACL_SET, acl_d->count, acl_d->acl);
+
+ sys_acl_free_acl(acl_d);
+
+ return ret;
+}
+
+int sys_acl_free_text(char *text)
+{
+ free(text);
+ return 0;
+}
+
+int sys_acl_free_acl(SMB_ACL_T acl_d)
+{
+ free(acl_d);
+ return 0;
+}
+
+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype)
+{
+ return 0;
+}
+
+#elif defined(HAVE_IRIX_ACLS)
+
+int sys_acl_get_entry(SMB_ACL_T acl_d, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ if (entry_id != SMB_ACL_FIRST_ENTRY && entry_id != SMB_ACL_NEXT_ENTRY) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_p == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_id == SMB_ACL_FIRST_ENTRY) {
+ acl_d->next = 0;
+ }
+
+ if (acl_d->next < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->next >= acl_d->aclp->acl_cnt) {
+ return 0;
+ }
+
+ *entry_p = &acl_d->aclp->acl_entry[acl_d->next++];
+
+ return 1;
+}
+
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *type_p)
+{
+ *type_p = entry_d->ae_tag;
+
+ return 0;
+}
+
+int sys_acl_get_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+ *permset_p = entry_d;
+
+ return 0;
+}
+
+void *sys_acl_get_qualifier(SMB_ACL_ENTRY_T entry_d)
+{
+ if (entry_d->ae_tag != SMB_ACL_USER
+ && entry_d->ae_tag != SMB_ACL_GROUP) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return &entry_d->ae_id;
+}
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+ SMB_ACL_T a;
+
+ if ((a = malloc(sizeof(*a))) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ if ((a->aclp = acl_get_file(path_p, type)) == NULL) {
+ SAFE_FREE(a);
+ return NULL;
+ }
+ a->next = -1;
+ a->freeaclp = True;
+ return a;
+}
+
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ SMB_ACL_T a;
+
+ if ((a = malloc(sizeof(*a))) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ if ((a->aclp = acl_get_fd(fd)) == NULL) {
+ SAFE_FREE(a);
+ return NULL;
+ }
+ a->next = -1;
+ a->freeaclp = True;
+ return a;
+}
+
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset_d)
+{
+ permset_d->ae_perm = 0;
+
+ return 0;
+}
+
+int sys_acl_add_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm)
+{
+ if (perm != SMB_ACL_READ && perm != SMB_ACL_WRITE
+ && perm != SMB_ACL_EXECUTE) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (permset_d == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ permset_d->ae_perm |= perm;
+
+ return 0;
+}
+
+int sys_acl_get_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm)
+{
+ return permset_d->ae_perm & perm;
+}
+
+char *sys_acl_to_text(SMB_ACL_T acl_d, ssize_t *len_p)
+{
+ return acl_to_text(acl_d->aclp, len_p);
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+ SMB_ACL_T a;
+
+ if (count < 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if ((a = malloc(sizeof(*a) + sizeof(struct acl))) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ a->next = -1;
+ a->freeaclp = False;
+ a->aclp = (struct acl *)(&a->aclp + sizeof(struct acl *));
+ a->aclp->acl_cnt = 0;
+
+ return a;
+}
+
+
+int sys_acl_create_entry(SMB_ACL_T *acl_p, SMB_ACL_ENTRY_T *entry_p)
+{
+ SMB_ACL_T acl_d;
+ SMB_ACL_ENTRY_T entry_d;
+
+ if (acl_p == NULL || entry_p == NULL || (acl_d = *acl_p) == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->aclp->acl_cnt >= ACL_MAX_ENTRIES) {
+ errno = ENOSPC;
+ return -1;
+ }
+
+ entry_d = &acl_d->aclp->acl_entry[acl_d->aclp->acl_cnt++];
+ entry_d->ae_tag = 0;
+ entry_d->ae_id = 0;
+ entry_d->ae_perm = 0;
+ *entry_p = entry_d;
+
+ return 0;
+}
+
+int sys_acl_set_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T tag_type)
+{
+ switch (tag_type) {
+ case SMB_ACL_USER:
+ case SMB_ACL_USER_OBJ:
+ case SMB_ACL_GROUP:
+ case SMB_ACL_GROUP_OBJ:
+ case SMB_ACL_OTHER:
+ case SMB_ACL_MASK:
+ entry_d->ae_tag = tag_type;
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ return 0;
+}
+
+int sys_acl_set_qualifier(SMB_ACL_ENTRY_T entry_d, void *qual_p)
+{
+ if (entry_d->ae_tag != SMB_ACL_GROUP
+ && entry_d->ae_tag != SMB_ACL_USER) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ entry_d->ae_id = *((id_t *)qual_p);
+
+ return 0;
+}
+
+int sys_acl_set_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T permset_d)
+{
+ if (permset_d->ae_perm & ~(SMB_ACL_READ|SMB_ACL_WRITE|SMB_ACL_EXECUTE)) {
+ return EINVAL;
+ }
+
+ entry_d->ae_perm = permset_d->ae_perm;
+
+ return 0;
+}
+
+int sys_acl_valid(SMB_ACL_T acl_d)
+{
+ return acl_valid(acl_d->aclp);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T type, SMB_ACL_T acl_d)
+{
+ return acl_set_file(name, type, acl_d->aclp);
+}
+
+int sys_acl_set_fd(int fd, SMB_ACL_T acl_d)
+{
+ return acl_set_fd(fd, acl_d->aclp);
+}
+
+int sys_acl_delete_def_file(const char *name)
+{
+ return acl_delete_def_file(name);
+}
+
+int sys_acl_free_text(char *text)
+{
+ return acl_free(text);
+}
+
+int sys_acl_free_acl(SMB_ACL_T acl_d)
+{
+ if (acl_d->freeaclp) {
+ acl_free(acl_d->aclp);
+ }
+ acl_free(acl_d);
+ return 0;
+}
+
+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype)
+{
+ return 0;
+}
+
+#elif defined(HAVE_AIX_ACLS)
+
+/* Donated by Medha Date, mdate@austin.ibm.com, for IBM */
+
+int sys_acl_get_entry( SMB_ACL_T theacl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ struct acl_entry_link *link;
+ struct new_acl_entry *entry;
+ int keep_going;
+
+ DEBUG(10,("This is the count: %d\n",theacl->count));
+
+ /* Check if count was previously set to -1. *
+ * If it was, that means we reached the end *
+ * of the acl last time. */
+ if(theacl->count == -1)
+ return(0);
+
+ link = theacl;
+ /* To get to the next acl, traverse linked list until index *
+ * of acl matches the count we are keeping. This count is *
+ * incremented each time we return an acl entry. */
+
+ for(keep_going = 0; keep_going < theacl->count; keep_going++)
+ link = link->nextp;
+
+ entry = *entry_p = link->entryp;
+
+ DEBUG(10,("*entry_p is %d\n",entry_p));
+ DEBUG(10,("*entry_p->ace_access is %d\n",entry->ace_access));
+
+ /* Increment count */
+ theacl->count++;
+ if(link->nextp == NULL)
+ theacl->count = -1;
+
+ return(1);
+}
+
+int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+ /* Initialize tag type */
+
+ *tag_type_p = -1;
+ DEBUG(10,("the tagtype is %d\n",entry_d->ace_id->id_type));
+
+ /* Depending on what type of entry we have, *
+ * return tag type. */
+ switch(entry_d->ace_id->id_type) {
+ case ACEID_USER:
+ *tag_type_p = SMB_ACL_USER;
+ break;
+ case ACEID_GROUP:
+ *tag_type_p = SMB_ACL_GROUP;
+ break;
+
+ case SMB_ACL_USER_OBJ:
+ case SMB_ACL_GROUP_OBJ:
+ case SMB_ACL_OTHER:
+ *tag_type_p = entry_d->ace_id->id_type;
+ break;
+
+ default:
+ return(-1);
+ }
+
+ return(0);
+}
+
+int sys_acl_get_permset( SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+ DEBUG(10,("Starting AIX sys_acl_get_permset\n"));
+ *permset_p = &entry_d->ace_access;
+ DEBUG(10,("**permset_p is %d\n",**permset_p));
+ if(!(**permset_p & S_IXUSR) &&
+ !(**permset_p & S_IWUSR) &&
+ !(**permset_p & S_IRUSR) &&
+ (**permset_p != 0))
+ return(-1);
+
+ DEBUG(10,("Ending AIX sys_acl_get_permset\n"));
+ return(0);
+}
+
+void *sys_acl_get_qualifier( SMB_ACL_ENTRY_T entry_d)
+{
+ return(entry_d->ace_id->id_data);
+}
+
+SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type)
+{
+ struct acl *file_acl = (struct acl *)NULL;
+ struct acl_entry *acl_entry;
+ struct new_acl_entry *new_acl_entry;
+ struct ace_id *idp;
+ struct acl_entry_link *acl_entry_link;
+ struct acl_entry_link *acl_entry_link_head;
+ int i;
+ int rc = 0;
+ uid_t user_id;
+
+ /* Get the acl using statacl */
+
+ DEBUG(10,("Entering sys_acl_get_file\n"));
+ DEBUG(10,("path_p is %s\n",path_p));
+
+ file_acl = (struct acl *)malloc(BUFSIZ);
+
+ if(file_acl == NULL) {
+ errno=ENOMEM;
+ DEBUG(0,("Error in AIX sys_acl_get_file: %d\n",errno));
+ return(NULL);
+ }
+
+ memset(file_acl,0,BUFSIZ);
+
+ rc = statacl((char *)path_p,0,file_acl,BUFSIZ);
+ if(rc == -1) {
+ DEBUG(0,("statacl returned %d with errno %d\n",rc,errno));
+ SAFE_FREE(file_acl);
+ return(NULL);
+ }
+
+ DEBUG(10,("Got facl and returned it\n"));
+
+ /* Point to the first acl entry in the acl */
+ acl_entry = file_acl->acl_ext;
+
+ /* Begin setting up the head of the linked list *
+ * that will be used for the storing the acl *
+ * in a way that is useful for the posix_acls.c *
+ * code. */
+
+ acl_entry_link_head = acl_entry_link = sys_acl_init(0);
+ if(acl_entry_link_head == NULL)
+ return(NULL);
+
+ acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry));
+ if(acl_entry_link->entryp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno));
+ return(NULL);
+ }
+
+ DEBUG(10,("acl_entry is %d\n",acl_entry));
+ DEBUG(10,("acl_last(file_acl) id %d\n",acl_last(file_acl)));
+
+ /* Check if the extended acl bit is on. *
+ * If it isn't, do not show the *
+ * contents of the acl since AIX intends *
+ * the extended info to remain unused */
+
+ if(file_acl->acl_mode & S_IXACL){
+ /* while we are not pointing to the very end */
+ while(acl_entry < acl_last(file_acl)) {
+ /* before we malloc anything, make sure this is */
+ /* a valid acl entry and one that we want to map */
+ idp = id_nxt(acl_entry->ace_id);
+ if((acl_entry->ace_type == ACC_SPECIFY ||
+ (acl_entry->ace_type == ACC_PERMIT)) && (idp != id_last(acl_entry))) {
+ acl_entry = acl_nxt(acl_entry);
+ continue;
+ }
+
+ idp = acl_entry->ace_id;
+
+ /* Check if this is the first entry in the linked list. *
+ * The first entry needs to keep prevp pointing to NULL *
+ * and already has entryp allocated. */
+
+ if(acl_entry_link_head->count != 0) {
+ acl_entry_link->nextp = (struct acl_entry_link *)
+ malloc(sizeof(struct acl_entry_link));
+
+ if(acl_entry_link->nextp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno));
+ return(NULL);
+ }
+
+ acl_entry_link->nextp->prevp = acl_entry_link;
+ acl_entry_link = acl_entry_link->nextp;
+ acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry));
+ if(acl_entry_link->entryp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno));
+ return(NULL);
+ }
+ acl_entry_link->nextp = NULL;
+ }
+
+ acl_entry_link->entryp->ace_len = acl_entry->ace_len;
+
+ /* Don't really need this since all types are going *
+ * to be specified but, it's better than leaving it 0 */
+
+ acl_entry_link->entryp->ace_type = acl_entry->ace_type;
+
+ acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+
+ memcpy(acl_entry_link->entryp->ace_id,idp,sizeof(struct ace_id));
+
+ /* The access in the acl entries must be left shifted by *
+ * three bites, because they will ultimately be compared *
+ * to S_IRUSR, S_IWUSR, and S_IXUSR. */
+
+ switch(acl_entry->ace_type){
+ case ACC_PERMIT:
+ case ACC_SPECIFY:
+ acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+ acl_entry_link->entryp->ace_access <<= 6;
+ acl_entry_link_head->count++;
+ break;
+ case ACC_DENY:
+ /* Since there is no way to return a DENY acl entry *
+ * change to PERMIT and then shift. */
+ DEBUG(10,("acl_entry->ace_access is %d\n",acl_entry->ace_access));
+ acl_entry_link->entryp->ace_access = ~acl_entry->ace_access & 7;
+ DEBUG(10,("acl_entry_link->entryp->ace_access is %d\n",acl_entry_link->entryp->ace_access));
+ acl_entry_link->entryp->ace_access <<= 6;
+ acl_entry_link_head->count++;
+ break;
+ default:
+ return(0);
+ }
+
+ DEBUG(10,("acl_entry = %d\n",acl_entry));
+ DEBUG(10,("The ace_type is %d\n",acl_entry->ace_type));
+
+ acl_entry = acl_nxt(acl_entry);
+ }
+ } /* end of if enabled */
+
+ /* Since owner, group, other acl entries are not *
+ * part of the acl entries in an acl, they must *
+ * be dummied up to become part of the list. */
+
+ for( i = 1; i < 4; i++) {
+ DEBUG(10,("i is %d\n",i));
+ if(acl_entry_link_head->count != 0) {
+ acl_entry_link->nextp = (struct acl_entry_link *)malloc(sizeof(struct acl_entry_link));
+ if(acl_entry_link->nextp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno));
+ return(NULL);
+ }
+
+ acl_entry_link->nextp->prevp = acl_entry_link;
+ acl_entry_link = acl_entry_link->nextp;
+ acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry));
+ if(acl_entry_link->entryp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno));
+ return(NULL);
+ }
+ }
+
+ acl_entry_link->nextp = NULL;
+
+ new_acl_entry = acl_entry_link->entryp;
+ idp = new_acl_entry->ace_id;
+
+ new_acl_entry->ace_len = sizeof(struct acl_entry);
+ new_acl_entry->ace_type = ACC_PERMIT;
+ idp->id_len = sizeof(struct ace_id);
+ DEBUG(10,("idp->id_len = %d\n",idp->id_len));
+ memset(idp->id_data,0,sizeof(uid_t));
+
+ switch(i) {
+ case 2:
+ new_acl_entry->ace_access = file_acl->g_access << 6;
+ idp->id_type = SMB_ACL_GROUP_OBJ;
+ break;
+
+ case 3:
+ new_acl_entry->ace_access = file_acl->o_access << 6;
+ idp->id_type = SMB_ACL_OTHER;
+ break;
+
+ case 1:
+ new_acl_entry->ace_access = file_acl->u_access << 6;
+ idp->id_type = SMB_ACL_USER_OBJ;
+ break;
+
+ default:
+ return(NULL);
+
+ }
+
+ acl_entry_link_head->count++;
+ DEBUG(10,("new_acl_entry->ace_access = %d\n",new_acl_entry->ace_access));
+ }
+
+ acl_entry_link_head->count = 0;
+ SAFE_FREE(file_acl);
+
+ return(acl_entry_link_head);
+}
+
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ struct acl *file_acl = (struct acl *)NULL;
+ struct acl_entry *acl_entry;
+ struct new_acl_entry *new_acl_entry;
+ struct ace_id *idp;
+ struct acl_entry_link *acl_entry_link;
+ struct acl_entry_link *acl_entry_link_head;
+ int i;
+ int rc = 0;
+ uid_t user_id;
+
+ /* Get the acl using fstatacl */
+
+ DEBUG(10,("Entering sys_acl_get_fd\n"));
+ DEBUG(10,("fd is %d\n",fd));
+ file_acl = (struct acl *)malloc(BUFSIZ);
+
+ if(file_acl == NULL) {
+ errno=ENOMEM;
+ DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno));
+ return(NULL);
+ }
+
+ memset(file_acl,0,BUFSIZ);
+
+ rc = fstatacl(fd,0,file_acl,BUFSIZ);
+ if(rc == -1) {
+ DEBUG(0,("The fstatacl call returned %d with errno %d\n",rc,errno));
+ SAFE_FREE(file_acl);
+ return(NULL);
+ }
+
+ DEBUG(10,("Got facl and returned it\n"));
+
+ /* Point to the first acl entry in the acl */
+
+ acl_entry = file_acl->acl_ext;
+ /* Begin setting up the head of the linked list *
+ * that will be used for the storing the acl *
+ * in a way that is useful for the posix_acls.c *
+ * code. */
+
+ acl_entry_link_head = acl_entry_link = sys_acl_init(0);
+ if(acl_entry_link_head == NULL){
+ SAFE_FREE(file_acl);
+ return(NULL);
+ }
+
+ acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry));
+
+ if(acl_entry_link->entryp == NULL) {
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno));
+ SAFE_FREE(file_acl);
+ return(NULL);
+ }
+
+ DEBUG(10,("acl_entry is %d\n",acl_entry));
+ DEBUG(10,("acl_last(file_acl) id %d\n",acl_last(file_acl)));
+
+ /* Check if the extended acl bit is on. *
+ * If it isn't, do not show the *
+ * contents of the acl since AIX intends *
+ * the extended info to remain unused */
+
+ if(file_acl->acl_mode & S_IXACL){
+ /* while we are not pointing to the very end */
+ while(acl_entry < acl_last(file_acl)) {
+ /* before we malloc anything, make sure this is */
+ /* a valid acl entry and one that we want to map */
+
+ idp = id_nxt(acl_entry->ace_id);
+ if((acl_entry->ace_type == ACC_SPECIFY ||
+ (acl_entry->ace_type == ACC_PERMIT)) && (idp != id_last(acl_entry))) {
+ acl_entry = acl_nxt(acl_entry);
+ continue;
+ }
+
+ idp = acl_entry->ace_id;
+
+ /* Check if this is the first entry in the linked list. *
+ * The first entry needs to keep prevp pointing to NULL *
+ * and already has entryp allocated. */
+
+ if(acl_entry_link_head->count != 0) {
+ acl_entry_link->nextp = (struct acl_entry_link *)malloc(sizeof(struct acl_entry_link));
+ if(acl_entry_link->nextp == NULL) {
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno));
+ SAFE_FREE(file_acl);
+ return(NULL);
+ }
+ acl_entry_link->nextp->prevp = acl_entry_link;
+ acl_entry_link = acl_entry_link->nextp;
+ acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry));
+ if(acl_entry_link->entryp == NULL) {
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno));
+ SAFE_FREE(file_acl);
+ return(NULL);
+ }
+
+ acl_entry_link->nextp = NULL;
+ }
+
+ acl_entry_link->entryp->ace_len = acl_entry->ace_len;
+
+ /* Don't really need this since all types are going *
+ * to be specified but, it's better than leaving it 0 */
+
+ acl_entry_link->entryp->ace_type = acl_entry->ace_type;
+ acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+
+ memcpy(acl_entry_link->entryp->ace_id, idp, sizeof(struct ace_id));
+
+ /* The access in the acl entries must be left shifted by *
+ * three bites, because they will ultimately be compared *
+ * to S_IRUSR, S_IWUSR, and S_IXUSR. */
+
+ switch(acl_entry->ace_type){
+ case ACC_PERMIT:
+ case ACC_SPECIFY:
+ acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+ acl_entry_link->entryp->ace_access <<= 6;
+ acl_entry_link_head->count++;
+ break;
+ case ACC_DENY:
+ /* Since there is no way to return a DENY acl entry *
+ * change to PERMIT and then shift. */
+ DEBUG(10,("acl_entry->ace_access is %d\n",acl_entry->ace_access));
+ acl_entry_link->entryp->ace_access = ~acl_entry->ace_access & 7;
+ DEBUG(10,("acl_entry_link->entryp->ace_access is %d\n",acl_entry_link->entryp->ace_access));
+ acl_entry_link->entryp->ace_access <<= 6;
+ acl_entry_link_head->count++;
+ break;
+ default:
+ return(0);
+ }
+
+ DEBUG(10,("acl_entry = %d\n",acl_entry));
+ DEBUG(10,("The ace_type is %d\n",acl_entry->ace_type));
+
+ acl_entry = acl_nxt(acl_entry);
+ }
+ } /* end of if enabled */
+
+ /* Since owner, group, other acl entries are not *
+ * part of the acl entries in an acl, they must *
+ * be dummied up to become part of the list. */
+
+ for( i = 1; i < 4; i++) {
+ DEBUG(10,("i is %d\n",i));
+ if(acl_entry_link_head->count != 0){
+ acl_entry_link->nextp = (struct acl_entry_link *)malloc(sizeof(struct acl_entry_link));
+ if(acl_entry_link->nextp == NULL) {
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno));
+ SAFE_FREE(file_acl);
+ return(NULL);
+ }
+
+ acl_entry_link->nextp->prevp = acl_entry_link;
+ acl_entry_link = acl_entry_link->nextp;
+ acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry));
+
+ if(acl_entry_link->entryp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno));
+ return(NULL);
+ }
+ }
+
+ acl_entry_link->nextp = NULL;
+
+ new_acl_entry = acl_entry_link->entryp;
+ idp = new_acl_entry->ace_id;
+
+ new_acl_entry->ace_len = sizeof(struct acl_entry);
+ new_acl_entry->ace_type = ACC_PERMIT;
+ idp->id_len = sizeof(struct ace_id);
+ DEBUG(10,("idp->id_len = %d\n",idp->id_len));
+ memset(idp->id_data,0,sizeof(uid_t));
+
+ switch(i) {
+ case 2:
+ new_acl_entry->ace_access = file_acl->g_access << 6;
+ idp->id_type = SMB_ACL_GROUP_OBJ;
+ break;
+
+ case 3:
+ new_acl_entry->ace_access = file_acl->o_access << 6;
+ idp->id_type = SMB_ACL_OTHER;
+ break;
+
+ case 1:
+ new_acl_entry->ace_access = file_acl->u_access << 6;
+ idp->id_type = SMB_ACL_USER_OBJ;
+ break;
+
+ default:
+ return(NULL);
+ }
+
+ acl_entry_link_head->count++;
+ DEBUG(10,("new_acl_entry->ace_access = %d\n",new_acl_entry->ace_access));
+ }
+
+ acl_entry_link_head->count = 0;
+ SAFE_FREE(file_acl);
+
+ return(acl_entry_link_head);
+}
+
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset)
+{
+ *permset = *permset & ~0777;
+ return(0);
+}
+
+int sys_acl_add_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+ if((perm != 0) &&
+ (perm & (S_IXUSR | S_IWUSR | S_IRUSR)) == 0)
+ return(-1);
+
+ *permset |= perm;
+ DEBUG(10,("This is the permset now: %d\n",*permset));
+ return(0);
+}
+
+char *sys_acl_to_text( SMB_ACL_T theacl, ssize_t *plen)
+{
+ return(NULL);
+}
+
+SMB_ACL_T sys_acl_init( int count)
+{
+ struct acl_entry_link *theacl = NULL;
+
+ DEBUG(10,("Entering sys_acl_init\n"));
+
+ theacl = (struct acl_entry_link *)malloc(sizeof(struct acl_entry_link));
+ if(theacl == NULL) {
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_init is %d\n",errno));
+ return(NULL);
+ }
+
+ theacl->count = 0;
+ theacl->nextp = NULL;
+ theacl->prevp = NULL;
+ theacl->entryp = NULL;
+ DEBUG(10,("Exiting sys_acl_init\n"));
+ return(theacl);
+}
+
+int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+ struct acl_entry_link *theacl;
+ struct acl_entry_link *acl_entryp;
+ struct acl_entry_link *temp_entry;
+ int counting;
+
+ DEBUG(10,("Entering the sys_acl_create_entry\n"));
+
+ theacl = acl_entryp = *pacl;
+
+ /* Get to the end of the acl before adding entry */
+
+ for(counting=0; counting < theacl->count; counting++){
+ DEBUG(10,("The acl_entryp is %d\n",acl_entryp));
+ temp_entry = acl_entryp;
+ acl_entryp = acl_entryp->nextp;
+ }
+
+ if(theacl->count != 0){
+ temp_entry->nextp = acl_entryp = (struct acl_entry_link *)malloc(sizeof(struct acl_entry_link));
+ if(acl_entryp == NULL) {
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_create_entry is %d\n",errno));
+ return(-1);
+ }
+
+ DEBUG(10,("The acl_entryp is %d\n",acl_entryp));
+ acl_entryp->prevp = temp_entry;
+ DEBUG(10,("The acl_entryp->prevp is %d\n",acl_entryp->prevp));
+ }
+
+ *pentry = acl_entryp->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry));
+ if(*pentry == NULL) {
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_create_entry is %d\n",errno));
+ return(-1);
+ }
+
+ memset(*pentry,0,sizeof(struct new_acl_entry));
+ acl_entryp->entryp->ace_len = sizeof(struct acl_entry);
+ acl_entryp->entryp->ace_type = ACC_PERMIT;
+ acl_entryp->entryp->ace_id->id_len = sizeof(struct ace_id);
+ acl_entryp->nextp = NULL;
+ theacl->count++;
+ DEBUG(10,("Exiting sys_acl_create_entry\n"));
+ return(0);
+}
+
+int sys_acl_set_tag_type( SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype)
+{
+ DEBUG(10,("Starting AIX sys_acl_set_tag_type\n"));
+ entry->ace_id->id_type = tagtype;
+ DEBUG(10,("The tag type is %d\n",entry->ace_id->id_type));
+ DEBUG(10,("Ending AIX sys_acl_set_tag_type\n"));
+}
+
+int sys_acl_set_qualifier( SMB_ACL_ENTRY_T entry, void *qual)
+{
+ DEBUG(10,("Starting AIX sys_acl_set_qualifier\n"));
+ memcpy(entry->ace_id->id_data,qual,sizeof(uid_t));
+ DEBUG(10,("Ending AIX sys_acl_set_qualifier\n"));
+ return(0);
+}
+
+int sys_acl_set_permset( SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset)
+{
+ DEBUG(10,("Starting AIX sys_acl_set_permset\n"));
+ if(!(*permset & S_IXUSR) &&
+ !(*permset & S_IWUSR) &&
+ !(*permset & S_IRUSR) &&
+ (*permset != 0))
+ return(-1);
+
+ entry->ace_access = *permset;
+ DEBUG(10,("entry->ace_access = %d\n",entry->ace_access));
+ DEBUG(10,("Ending AIX sys_acl_set_permset\n"));
+ return(0);
+}
+
+int sys_acl_valid( SMB_ACL_T theacl )
+{
+ int user_obj = 0;
+ int group_obj = 0;
+ int other_obj = 0;
+ struct acl_entry_link *acl_entry;
+
+ for(acl_entry=theacl; acl_entry != NULL; acl_entry = acl_entry->nextp) {
+ user_obj += (acl_entry->entryp->ace_id->id_type == SMB_ACL_USER_OBJ);
+ group_obj += (acl_entry->entryp->ace_id->id_type == SMB_ACL_GROUP_OBJ);
+ other_obj += (acl_entry->entryp->ace_id->id_type == SMB_ACL_OTHER);
+ }
+
+ DEBUG(10,("user_obj=%d, group_obj=%d, other_obj=%d\n",user_obj,group_obj,other_obj));
+
+ if(user_obj != 1 || group_obj != 1 || other_obj != 1)
+ return(-1);
+
+ return(0);
+}
+
+int sys_acl_set_file( const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+ struct acl_entry_link *acl_entry_link = NULL;
+ struct acl *file_acl = NULL;
+ struct acl *file_acl_temp = NULL;
+ struct acl_entry *acl_entry = NULL;
+ struct ace_id *ace_id = NULL;
+ uint id_type;
+ uint ace_access;
+ uint user_id;
+ uint acl_length;
+ uint rc;
+
+ DEBUG(10,("Entering sys_acl_set_file\n"));
+ DEBUG(10,("File name is %s\n",name));
+
+ /* AIX has no default ACL */
+ if(acltype == SMB_ACL_TYPE_DEFAULT)
+ return(0);
+
+ acl_length = BUFSIZ;
+ file_acl = (struct acl *)malloc(BUFSIZ);
+
+ if(file_acl == NULL) {
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_set_file is %d\n",errno));
+ return(-1);
+ }
+
+ memset(file_acl,0,BUFSIZ);
+
+ file_acl->acl_len = ACL_SIZ;
+ file_acl->acl_mode = S_IXACL;
+
+ for(acl_entry_link=theacl; acl_entry_link != NULL; acl_entry_link = acl_entry_link->nextp) {
+ acl_entry_link->entryp->ace_access >>= 6;
+ id_type = acl_entry_link->entryp->ace_id->id_type;
+
+ switch(id_type) {
+ case SMB_ACL_USER_OBJ:
+ file_acl->u_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_GROUP_OBJ:
+ file_acl->g_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_OTHER:
+ file_acl->o_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_MASK:
+ continue;
+ }
+
+ if((file_acl->acl_len + sizeof(struct acl_entry)) > acl_length) {
+ acl_length += sizeof(struct acl_entry);
+ file_acl_temp = (struct acl *)malloc(acl_length);
+ if(file_acl_temp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_set_file is %d\n",errno));
+ return(-1);
+ }
+
+ memcpy(file_acl_temp,file_acl,file_acl->acl_len);
+ SAFE_FREE(file_acl);
+ file_acl = file_acl_temp;
+ }
+
+ acl_entry = (struct acl_entry *)((char *)file_acl + file_acl->acl_len);
+ file_acl->acl_len += sizeof(struct acl_entry);
+ acl_entry->ace_len = acl_entry_link->entryp->ace_len;
+ acl_entry->ace_access = acl_entry_link->entryp->ace_access;
+
+ /* In order to use this, we'll need to wait until we can get denies */
+ /* if(!acl_entry->ace_access && acl_entry->ace_type == ACC_PERMIT)
+ acl_entry->ace_type = ACC_SPECIFY; */
+
+ acl_entry->ace_type = ACC_SPECIFY;
+
+ ace_id = acl_entry->ace_id;
+
+ ace_id->id_type = acl_entry_link->entryp->ace_id->id_type;
+ DEBUG(10,("The id type is %d\n",ace_id->id_type));
+ ace_id->id_len = acl_entry_link->entryp->ace_id->id_len;
+ memcpy(&user_id, acl_entry_link->entryp->ace_id->id_data, sizeof(uid_t));
+ memcpy(acl_entry->ace_id->id_data, &user_id, sizeof(uid_t));
+ }
+
+ rc = chacl(name,file_acl,file_acl->acl_len);
+ DEBUG(10,("errno is %d\n",errno));
+ DEBUG(10,("return code is %d\n",rc));
+ SAFE_FREE(file_acl);
+ DEBUG(10,("Exiting the sys_acl_set_file\n"));
+ return(rc);
+}
+
+int sys_acl_set_fd( int fd, SMB_ACL_T theacl)
+{
+ struct acl_entry_link *acl_entry_link = NULL;
+ struct acl *file_acl = NULL;
+ struct acl *file_acl_temp = NULL;
+ struct acl_entry *acl_entry = NULL;
+ struct ace_id *ace_id = NULL;
+ uint id_type;
+ uint user_id;
+ uint acl_length;
+ uint rc;
+
+ DEBUG(10,("Entering sys_acl_set_fd\n"));
+ acl_length = BUFSIZ;
+ file_acl = (struct acl *)malloc(BUFSIZ);
+
+ if(file_acl == NULL) {
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_set_fd is %d\n",errno));
+ return(-1);
+ }
+
+ memset(file_acl,0,BUFSIZ);
+
+ file_acl->acl_len = ACL_SIZ;
+ file_acl->acl_mode = S_IXACL;
+
+ for(acl_entry_link=theacl; acl_entry_link != NULL; acl_entry_link = acl_entry_link->nextp) {
+ acl_entry_link->entryp->ace_access >>= 6;
+ id_type = acl_entry_link->entryp->ace_id->id_type;
+ DEBUG(10,("The id_type is %d\n",id_type));
+
+ switch(id_type) {
+ case SMB_ACL_USER_OBJ:
+ file_acl->u_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_GROUP_OBJ:
+ file_acl->g_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_OTHER:
+ file_acl->o_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_MASK:
+ continue;
+ }
+
+ if((file_acl->acl_len + sizeof(struct acl_entry)) > acl_length) {
+ acl_length += sizeof(struct acl_entry);
+ file_acl_temp = (struct acl *)malloc(acl_length);
+ if(file_acl_temp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_set_fd is %d\n",errno));
+ return(-1);
+ }
+
+ memcpy(file_acl_temp,file_acl,file_acl->acl_len);
+ SAFE_FREE(file_acl);
+ file_acl = file_acl_temp;
+ }
+
+ acl_entry = (struct acl_entry *)((char *)file_acl + file_acl->acl_len);
+ file_acl->acl_len += sizeof(struct acl_entry);
+ acl_entry->ace_len = acl_entry_link->entryp->ace_len;
+ acl_entry->ace_access = acl_entry_link->entryp->ace_access;
+
+ /* In order to use this, we'll need to wait until we can get denies */
+ /* if(!acl_entry->ace_access && acl_entry->ace_type == ACC_PERMIT)
+ acl_entry->ace_type = ACC_SPECIFY; */
+
+ acl_entry->ace_type = ACC_SPECIFY;
+
+ ace_id = acl_entry->ace_id;
+
+ ace_id->id_type = acl_entry_link->entryp->ace_id->id_type;
+ DEBUG(10,("The id type is %d\n",ace_id->id_type));
+ ace_id->id_len = acl_entry_link->entryp->ace_id->id_len;
+ memcpy(&user_id, acl_entry_link->entryp->ace_id->id_data, sizeof(uid_t));
+ memcpy(ace_id->id_data, &user_id, sizeof(uid_t));
+ }
+
+ rc = fchacl(fd,file_acl,file_acl->acl_len);
+ DEBUG(10,("errno is %d\n",errno));
+ DEBUG(10,("return code is %d\n",rc));
+ SAFE_FREE(file_acl);
+ DEBUG(10,("Exiting sys_acl_set_fd\n"));
+ return(rc);
+}
+
+int sys_acl_delete_def_file(const char *name)
+{
+ /* AIX has no default ACL */
+ return 0;
+}
+
+int sys_acl_get_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+ return(*permset & perm);
+}
+
+int sys_acl_free_text(char *text)
+{
+ return(0);
+}
+
+int sys_acl_free_acl(SMB_ACL_T posix_acl)
+{
+ struct acl_entry_link *acl_entry_link;
+
+ for(acl_entry_link = posix_acl->nextp; acl_entry_link->nextp != NULL; acl_entry_link = acl_entry_link->nextp) {
+ SAFE_FREE(acl_entry_link->prevp->entryp);
+ SAFE_FREE(acl_entry_link->prevp);
+ }
+
+ SAFE_FREE(acl_entry_link->prevp->entryp);
+ SAFE_FREE(acl_entry_link->prevp);
+ SAFE_FREE(acl_entry_link->entryp);
+ SAFE_FREE(acl_entry_link);
+
+ return(0);
+}
+
+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype)
+{
+ return(0);
+}
+
+#else /* No ACLs. */
+
+int sys_acl_get_entry( SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_acl_get_permset( SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+void *sys_acl_get_qualifier( SMB_ACL_ENTRY_T entry_d)
+{
+ errno = ENOSYS;
+ return NULL;
+}
+
+SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type)
+{
+ errno = ENOSYS;
+ return (SMB_ACL_T)NULL;
+}
+
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ errno = ENOSYS;
+ return (SMB_ACL_T)NULL;
+}
+
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_acl_add_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_acl_get_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+ errno = ENOSYS;
+ return (permset & perm) ? 1 : 0;
+}
+
+char *sys_acl_to_text( SMB_ACL_T the_acl, ssize_t *plen)
+{
+ errno = ENOSYS;
+ return NULL;
+}
+
+int sys_acl_free_text(char *text)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+SMB_ACL_T sys_acl_init( int count)
+{
+ errno = ENOSYS;
+ return NULL;
+}
+
+int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_acl_set_tag_type( SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_acl_set_qualifier( SMB_ACL_ENTRY_T entry, void *qual)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_acl_set_permset( SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_acl_valid( SMB_ACL_T theacl )
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_acl_set_file( const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_acl_set_fd( int fd, SMB_ACL_T theacl)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_acl_delete_def_file(const char *name)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_acl_free_acl(SMB_ACL_T the_acl)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+#endif /* No ACLs. */
diff --git a/source/lib/sysquotas.c b/source/lib/sysquotas.c
new file mode 100644
index 00000000000..1c5c7e8bd4f
--- /dev/null
+++ b/source/lib/sysquotas.c
@@ -0,0 +1,505 @@
+/*
+ Unix SMB/CIFS implementation.
+ System QUOTA function wrappers
+ Copyright (C) Stefan (metze) Metzmacher 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_QUOTA
+
+#ifdef HAVE_SYS_QUOTAS
+
+#if defined(HAVE_QUOTACTL_4A)
+
+/*#endif HAVE_QUOTACTL_4A */
+#elif defined(HAVE_QUOTACTL_4B)
+
+#error HAVE_QUOTACTL_4B not implemeted
+
+/*#endif HAVE_QUOTACTL_4B */
+#elif defined(HAVE_QUOTACTL_3)
+
+#error HAVE_QUOTACTL_3 not implemented
+
+/* #endif HAVE_QUOTACTL_3 */
+#else /* NO_QUOTACTL_USED */
+
+#endif /* NO_QUOTACTL_USED */
+
+#ifdef HAVE_MNTENT
+static int sys_path_to_bdev(const char *path, char **mntpath, char **bdev, char **fs)
+{
+ int ret = -1;
+ SMB_STRUCT_STAT S;
+ FILE *fp;
+ struct mntent *mnt;
+ SMB_DEV_T devno;
+
+ /* find the block device file */
+
+ if (!path||!mntpath||!bdev||!fs)
+ smb_panic("sys_path_to_bdev: called with NULL pointer");
+
+ (*mntpath) = NULL;
+ (*bdev) = NULL;
+ (*fs) = NULL;
+
+ if ( sys_stat(path, &S) == -1 )
+ return (-1);
+
+ devno = S.st_dev ;
+
+ fp = setmntent(MOUNTED,"r");
+
+ while ((mnt = getmntent(fp))) {
+ if ( sys_stat(mnt->mnt_dir,&S) == -1 )
+ continue ;
+
+ if (S.st_dev == devno) {
+ (*mntpath) = strdup(mnt->mnt_dir);
+ (*bdev) = strdup(mnt->mnt_fsname);
+ (*fs) = strdup(mnt->mnt_type);
+ if ((*mntpath)&&(*bdev)&&(*fs)) {
+ ret = 0;
+ } else {
+ SAFE_FREE(*mntpath);
+ SAFE_FREE(*bdev);
+ SAFE_FREE(*fs);
+ ret = -1;
+ }
+
+ break;
+ }
+ }
+
+ endmntent(fp) ;
+
+ return ret;
+}
+/* #endif HAVE_MNTENT */
+#elif defined(HAVE_DEVNM)
+
+/* we have this on HPUX, ... */
+static int sys_path_to_bdev(const char *path, char **mntpath, char **bdev, char **fs)
+{
+ int ret = -1;
+ char dev_disk[256];
+ SMB_STRUCT_STAT S;
+
+ if (!path||!mntpath||!bdev||!fs)
+ smb_panic("sys_path_to_bdev: called with NULL pointer");
+
+ (*mntpath) = NULL;
+ (*bdev) = NULL;
+ (*fs) = NULL;
+
+ /* find the block device file */
+
+ if ((ret=sys_stat(path, &S))!=0) {
+ return ret;
+ }
+
+ if ((ret=devnm(S_IFBLK, S.st_dev, dev_disk, 256, 1))!=0) {
+ return ret;
+ }
+
+ /* we should get the mntpath right...
+ * but I don't know how
+ * --metze
+ */
+ (*mntpath) = strdup(path);
+ (*bdev) = strdup(dev_disk);
+ if ((*mntpath)&&(*bdev)) {
+ ret = 0;
+ } else {
+ SAFE_FREE(*mntpath);
+ SAFE_FREE(*bdev);
+ ret = -1;
+ }
+
+
+ return ret;
+}
+
+/* #endif HAVE_DEVNM */
+#else
+/* we should fake this up...*/
+static int sys_path_to_bdev(const char *path, char **mntpath, char **bdev, char **fs)
+{
+ int ret = -1;
+
+ if (!path||!mntpath||!bdev||!fs)
+ smb_panic("sys_path_to_bdev: called with NULL pointer");
+
+ (*mntpath) = NULL;
+ (*bdev) = NULL;
+ (*fs) = NULL;
+
+ (*mntpath) = strdup(path);
+ if (*mntpath) {
+ ret = 0;
+ } else {
+ SAFE_FREE(*mntpath);
+ ret = -1;
+ }
+
+ return ret;
+}
+#endif
+
+/*********************************************************************
+ Now the list of all filesystem specific quota systems we have found
+**********************************************************************/
+static struct {
+ const char *name;
+ int (*get_quota)(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp);
+ int (*set_quota)(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp);
+} sys_quota_backends[] = {
+#ifdef HAVE_XFS_QUOTAS
+ {"xfs", sys_get_xfs_quota, sys_set_xfs_quota},
+#endif /* HAVE_XFS_QUOTAS */
+ {NULL, NULL, NULL}
+};
+
+static int command_get_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ const char *get_quota_command;
+
+ get_quota_command = lp_get_quota_command();
+ if (get_quota_command && *get_quota_command) {
+ const char *p;
+ char *p2;
+ char **lines;
+ pstring syscmd;
+ int _id = -1;
+
+ switch(qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ case SMB_USER_FS_QUOTA_TYPE:
+ _id = id.uid;
+ break;
+ case SMB_GROUP_QUOTA_TYPE:
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ _id = id.gid;
+ break;
+ default:
+ DEBUG(0,("invalid quota type.\n"));
+ return -1;
+ }
+
+ slprintf(syscmd, sizeof(syscmd)-1,
+ "%s \"%s\" %d %d",
+ get_quota_command, path, qtype, _id);
+
+ DEBUG (3, ("get_quota: Running command %s\n", syscmd));
+
+ lines = file_lines_pload(syscmd, NULL);
+ if (lines) {
+ char *line = lines[0];
+
+ DEBUG (3, ("Read output from get_quota, \"%s\"\n", line));
+
+ /* we need to deal with long long unsigned here, if supported */
+
+ dp->qflags = (enum SMB_QUOTA_TYPE)strtoul(line, &p2, 10);
+ p = p2;
+ while (p && *p && isspace(*p))
+ p++;
+ if (p && *p)
+ dp->curblocks = STR_TO_SMB_BIG_UINT(p, &p);
+ else
+ goto invalid_param;
+ while (p && *p && isspace(*p))
+ p++;
+ if (p && *p)
+ dp->softlimit = STR_TO_SMB_BIG_UINT(p, &p);
+ else
+ goto invalid_param;
+ while (p && *p && isspace(*p))
+ p++;
+ if (p && *p)
+ dp->hardlimit = STR_TO_SMB_BIG_UINT(p, &p);
+ else
+ goto invalid_param;
+ while (p && *p && isspace(*p))
+ p++;
+ if (p && *p)
+ dp->curinodes = STR_TO_SMB_BIG_UINT(p, &p);
+ else
+ goto invalid_param;
+ while (p && *p && isspace(*p))
+ p++;
+ if (p && *p)
+ dp->isoftlimit = STR_TO_SMB_BIG_UINT(p, &p);
+ else
+ goto invalid_param;
+ while (p && *p && isspace(*p))
+ p++;
+ if (p && *p)
+ dp->ihardlimit = STR_TO_SMB_BIG_UINT(p, &p);
+ else
+ goto invalid_param;
+ while (p && *p && isspace(*p))
+ p++;
+ if (p && *p)
+ dp->bsize = STR_TO_SMB_BIG_UINT(p, NULL);
+ else
+ dp->bsize = 1024;
+ file_lines_free(lines);
+ DEBUG (3, ("Parsed output of get_quota, ...\n"));
+
+#ifdef LARGE_SMB_OFF_T
+ DEBUGADD (5,(
+ "qflags:%u curblocks:%llu softlimit:%llu hardlimit:%llu\n"
+ "curinodes:%llu isoftlimit:%llu ihardlimit:%llu bsize:%llu\n",
+ dp->qflags,(long long unsigned)dp->curblocks,
+ (long long unsigned)dp->softlimit,(long long unsigned)dp->hardlimit,
+ (long long unsigned)dp->curinodes,
+ (long long unsigned)dp->isoftlimit,(long long unsigned)dp->ihardlimit,
+ (long long unsigned)dp->bsize));
+#else /* LARGE_SMB_OFF_T */
+ DEBUGADD (5,(
+ "qflags:%u curblocks:%lu softlimit:%lu hardlimit:%lu\n"
+ "curinodes:%lu isoftlimit:%lu ihardlimit:%lu bsize:%lu\n",
+ dp->qflags,(long unsigned)dp->curblocks,
+ (long unsigned)dp->softlimit,(long unsigned)dp->hardlimit,
+ (long unsigned)dp->curinodes,
+ (long unsigned)dp->isoftlimit,(long unsigned)dp->ihardlimit,
+ (long unsigned)dp->bsize));
+#endif /* LARGE_SMB_OFF_T */
+ return 0;
+ }
+
+ DEBUG (0, ("get_quota_command failed!\n"));
+ return -1;
+ }
+
+ errno = ENOSYS;
+ return -1;
+
+invalid_param:
+ DEBUG(0,("The output of get_quota_command is invalid!\n"));
+ return -1;
+}
+
+static int command_set_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ const char *set_quota_command;
+
+ set_quota_command = lp_set_quota_command();
+ if (set_quota_command && *set_quota_command) {
+ char **lines;
+ pstring syscmd;
+ int _id = -1;
+
+ switch(qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ case SMB_USER_FS_QUOTA_TYPE:
+ _id = id.uid;
+ break;
+ case SMB_GROUP_QUOTA_TYPE:
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ _id = id.gid;
+ break;
+ default:
+ return -1;
+ }
+
+#ifdef LARGE_SMB_OFF_T
+ slprintf(syscmd, sizeof(syscmd)-1,
+ "%s \"%s\" %d %d "
+ "%u %llu %llu "
+ "%llu %llu %llu ",
+ set_quota_command, path, qtype, _id, dp->qflags,
+ (long long unsigned)dp->softlimit,(long long unsigned)dp->hardlimit,
+ (long long unsigned)dp->isoftlimit,(long long unsigned)dp->ihardlimit,
+ (long long unsigned)dp->bsize);
+#else /* LARGE_SMB_OFF_T */
+ slprintf(syscmd, sizeof(syscmd)-1,
+ "%s \"%s\" %d %d "
+ "%u %lu %lu "
+ "%lu %lu %lu ",
+ set_quota_command, path, qtype, _id, dp->qflags,
+ (long unsigned)dp->softlimit,(long unsigned)dp->hardlimit,
+ (long unsigned)dp->isoftlimit,(long unsigned)dp->ihardlimit,
+ (long unsigned)dp->bsize);
+#endif /* LARGE_SMB_OFF_T */
+
+
+
+ DEBUG (3, ("get_quota: Running command %s\n", syscmd));
+
+ lines = file_lines_pload(syscmd, NULL);
+ if (lines) {
+ char *line = lines[0];
+
+ DEBUG (3, ("Read output from set_quota, \"%s\"\n", line));
+
+ file_lines_free(lines);
+
+ return 0;
+ }
+ DEBUG (0, ("set_quota_command failed!\n"));
+ return -1;
+ }
+
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_get_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+ int i;
+ BOOL ready = False;
+ char *mntpath = NULL;
+ char *bdev = NULL;
+ char *fs = NULL;
+
+ if (!path||!dp)
+ smb_panic("sys_get_quota: called with NULL pointer");
+
+ if (command_get_quota(path, qtype, id, dp)==0) {
+ return 0;
+ } else if (errno != ENOSYS) {
+ return -1;
+ }
+
+ if ((ret=sys_path_to_bdev(path,&mntpath,&bdev,&fs))!=0) {
+ DEBUG(0,("sys_path_to_bdev() failed for path [%s]!\n",path));
+ return ret;
+ }
+
+ errno = 0;
+ DEBUG(10,("sys_get_quota() uid(%u, %u)\n", (unsigned)getuid(), (unsigned)geteuid()));
+
+ for (i=0;(fs && sys_quota_backends[i].name && sys_quota_backends[i].get_quota);i++) {
+ if (strcmp(fs,sys_quota_backends[i].name)==0) {
+ ret = sys_quota_backends[i].get_quota(mntpath, bdev, qtype, id, dp);
+ if (ret!=0) {
+ DEBUG(3,("sys_get_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s.\n",
+ fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno)));
+ } else {
+ DEBUG(10,("sys_get_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n",
+ fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid)));
+ }
+ ready = True;
+ break;
+ }
+ }
+
+ if (!ready) {
+ /* use the default vfs quota functions */
+ ret=sys_get_vfs_quota(mntpath, bdev, qtype, id, dp);
+ if (ret!=0) {
+ DEBUG(3,("sys_get_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s\n",
+ "vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno)));
+ } else {
+ DEBUG(10,("sys_get_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n",
+ "vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid)));
+ }
+ }
+
+ SAFE_FREE(mntpath);
+ SAFE_FREE(bdev);
+ SAFE_FREE(fs);
+
+ if ((ret!=0)&& (errno == EDQUOT)) {
+ DEBUG(10,("sys_get_quota() warning over quota!\n"));
+ return 0;
+ }
+
+ return ret;
+}
+
+int sys_set_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+ int i;
+ BOOL ready = False;
+ char *mntpath = NULL;
+ char *bdev = NULL;
+ char *fs = NULL;
+
+ /* find the block device file */
+
+ if (!path||!dp)
+ smb_panic("get_smb_quota: called with NULL pointer");
+
+ if (command_set_quota(path, qtype, id, dp)==0) {
+ return 0;
+ } else if (errno != ENOSYS) {
+ return -1;
+ }
+
+ if ((ret=sys_path_to_bdev(path,&mntpath,&bdev,&fs))!=0) {
+ DEBUG(0,("sys_path_to_bdev() failed for path [%s]!\n",path));
+ return ret;
+ }
+
+ errno = 0;
+ DEBUG(10,("sys_set_quota() uid(%u, %u)\n", (unsigned)getuid(), (unsigned)geteuid()));
+
+ for (i=0;(fs && sys_quota_backends[i].name && sys_quota_backends[i].set_quota);i++) {
+ if (strcmp(fs,sys_quota_backends[i].name)==0) {
+ ret = sys_quota_backends[i].set_quota(mntpath, bdev, qtype, id, dp);
+ if (ret!=0) {
+ DEBUG(3,("sys_set_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s.\n",
+ fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno)));
+ } else {
+ DEBUG(10,("sys_set_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n",
+ fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid)));
+ }
+ ready = True;
+ break;
+ }
+ }
+
+ if (!ready) {
+ /* use the default vfs quota functions */
+ ret=sys_set_vfs_quota(mntpath, bdev, qtype, id, dp);
+ if (ret!=0) {
+ DEBUG(3,("sys_set_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s.\n",
+ "vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno)));
+ } else {
+ DEBUG(10,("sys_set_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n",
+ "vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid)));
+ }
+ }
+
+ SAFE_FREE(mntpath);
+ SAFE_FREE(bdev);
+ SAFE_FREE(fs);
+
+ if ((ret!=0)&& (errno == EDQUOT)) {
+ DEBUG(10,("sys_set_quota() warning over quota!\n"));
+ return 0;
+ }
+
+ return ret;
+}
+
+#else /* HAVE_SYS_QUOTAS */
+ void dummy_sysquotas_c(void)
+{
+ return;
+}
+#endif /* HAVE_SYS_QUOTAS */
+
diff --git a/source/lib/sysquotas_4A.c b/source/lib/sysquotas_4A.c
new file mode 100644
index 00000000000..ffb4123799d
--- /dev/null
+++ b/source/lib/sysquotas_4A.c
@@ -0,0 +1,339 @@
+/*
+ Unix SMB/CIFS implementation.
+ System QUOTA function wrappers for QUOTACTL_4A
+ Copyright (C) Stefan (metze) Metzmacher 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_QUOTA
+
+#ifdef HAVE_QUOTACTL_4A
+/* long quotactl(int cmd, char *special, qid_t id, caddr_t addr) */
+/* this is used by: HPUX,IRIX */
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_ASM_TYPES_H
+#include <asm/types.h>
+#endif
+
+#ifdef HAVE_SYS_QUOTA_H
+#include <sys/quota.h>
+#endif
+
+#ifndef Q_SETQLIM
+#define Q_SETQLIM Q_SETQUOTA
+#endif
+
+#ifndef QCMD
+#define QCMD(x,y) x
+#endif
+
+#ifndef QCMD
+#define QCMD(x,y) x
+#endif
+
+#ifdef GRPQUOTA
+#define HAVE_GROUP_QUOTA
+#endif
+
+#ifndef QUOTABLOCK_SIZE
+#define QUOTABLOCK_SIZE DEV_BSIZE
+#endif
+
+#ifdef HAVE_DQB_FSOFTLIMIT
+#define dqb_isoftlimit dqb_fsoftlimit
+#define dqb_ihardlimit dqb_fhardlimit
+#define dqb_curinodes dqb_curfiles
+#endif
+
+#ifdef INITQFNAMES
+#define USERQUOTAFILE_EXTENSION ".user"
+#else
+#define USERQUOTAFILE_EXTENSION ""
+#endif
+
+#if !defined(QUOTAFILENAME) && defined(QFILENAME)
+#define QUOTAFILENAME QFILENAME
+#endif
+
+/****************************************************************************
+ Abstract out the quotactl_4A get calls.
+****************************************************************************/
+int sys_get_vfs_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+ uint32 qflags = 0;
+ struct dqblk D;
+ SMB_BIG_UINT bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
+
+ ZERO_STRUCT(D);
+ ZERO_STRUCT(*dp);
+ dp->qtype = qtype;
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ DEBUG(10,("sys_get_vfs_quota: path[%s] bdev[%s] SMB_USER_QUOTA_TYPE uid[%u]\n",
+ path, bdev, (unsigned)id.uid));
+
+ if ((ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), bdev, id.uid, (void *)&D))&&errno != EDQUOT) {
+ return ret;
+ }
+
+ if ((D.dqb_curblocks==0)&&
+ (D.dqb_bsoftlimit==0)&&
+ (D.dqb_bhardlimit==0)) {
+ /* the upper layer functions don't want empty quota records...*/
+ return -1;
+ }
+
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_QUOTA_TYPE:
+ DEBUG(10,("sys_get_vfs_quota: path[%s] bdev[%s] SMB_GROUP_QUOTA_TYPE gid[%u]\n",
+ path, bdev, (unsigned)id.gid));
+
+ if ((ret = quotactl(QCMD(Q_GETQUOTA,GRPQUOTA), bdev, id.gid, (void *)&D))&&errno != EDQUOT) {
+ return ret;
+ }
+
+ if ((D.dqb_curblocks==0)&&
+ (D.dqb_bsoftlimit==0)&&
+ (D.dqb_bhardlimit==0)) {
+ /* the upper layer functions don't want empty quota records...*/
+ return -1;
+ }
+
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ case SMB_USER_FS_QUOTA_TYPE:
+ id.uid = getuid();
+
+ DEBUG(10,("sys_get_vfs_quota: path[%s] bdev[%s] SMB_USER_FS_QUOTA_TYPE (uid[%u])\n",
+ path, bdev, (unsigned)id.uid));
+
+ if ((ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), bdev, id.uid, (void *)&D))==0) {
+ qflags |= QUOTAS_DENY_DISK;
+ }
+
+ ret = 0;
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ id.gid = getgid();
+
+ DEBUG(10,("sys_get_vfs_quota: path[%s] bdev[%s] SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n",
+ path, bdev, (unsigned)id.gid));
+
+ if ((ret = quotactl(QCMD(Q_GETQUOTA,GRPQUOTA), bdev, id.gid, (void *)&D))==0) {
+ qflags |= QUOTAS_DENY_DISK;
+ }
+
+ ret = 0;
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ default:
+ errno = ENOSYS;
+ return -1;
+ }
+
+ dp->bsize = bsize;
+ dp->softlimit = (SMB_BIG_UINT)D.dqb_bsoftlimit;
+ dp->hardlimit = (SMB_BIG_UINT)D.dqb_bhardlimit;
+ dp->ihardlimit = (SMB_BIG_UINT)D.dqb_ihardlimit;
+ dp->isoftlimit = (SMB_BIG_UINT)D.dqb_isoftlimit;
+ dp->curinodes = (SMB_BIG_UINT)D.dqb_curinodes;
+ dp->curblocks = (SMB_BIG_UINT)D.dqb_curblocks;
+
+
+ dp->qflags = qflags;
+
+ return ret;
+}
+
+/****************************************************************************
+ Abstract out the quotactl_4A set calls.
+****************************************************************************/
+int sys_set_vfs_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+ uint32 qflags = 0;
+ uint32 oldqflags = 0;
+ struct dqblk D;
+ SMB_BIG_UINT bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
+
+ ZERO_STRUCT(D);
+
+ if (bsize == dp->bsize) {
+ D.dqb_bsoftlimit = dp->softlimit;
+ D.dqb_bhardlimit = dp->hardlimit;
+ D.dqb_ihardlimit = dp->ihardlimit;
+ D.dqb_isoftlimit = dp->isoftlimit;
+ } else {
+ D.dqb_bsoftlimit = (dp->softlimit*dp->bsize)/bsize;
+ D.dqb_bhardlimit = (dp->hardlimit*dp->bsize)/bsize;
+ D.dqb_ihardlimit = (dp->ihardlimit*dp->bsize)/bsize;
+ D.dqb_isoftlimit = (dp->isoftlimit*dp->bsize)/bsize;
+ }
+
+ qflags = dp->qflags;
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ DEBUG(10,("sys_set_vfs_quota: path[%s] bdev[%s] SMB_USER_QUOTA_TYPE uid[%u]\n",
+ path, bdev, (unsigned)id.uid));
+
+ ret = quotactl(QCMD(Q_SETQLIM,USRQUOTA), bdev, id.uid, (void *)&D);
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_QUOTA_TYPE:
+ DEBUG(10,("sys_set_vfs_quota: path[%s] bdev[%s] SMB_GROUP_QUOTA_TYPE gid[%u]\n",
+ path, bdev, (unsigned)id.gid));
+
+ ret = quotactl(QCMD(Q_SETQLIM,GRPQUOTA), bdev, id.gid, (void *)&D);
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ case SMB_USER_FS_QUOTA_TYPE:
+ /* this stuff didn't work as it should:
+ * switching on/off quota via quotactl()
+ * didn't work!
+ * So we just return 0
+ * --metze
+ *
+ * On HPUX we didn't have the mount path,
+ * we need to fix sys_path_to_bdev()
+ *
+ */
+ id.uid = getuid();
+ DEBUG(10,("sys_set_vfs_quota: path[%s] bdev[%s] SMB_USER_FS_QUOTA_TYPE (uid[%u])\n",
+ path, bdev, (unsigned)id.uid));
+
+#if 0
+ ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), bdev, id.uid, (void *)&D);
+
+ if ((qflags&QUOTAS_DENY_DISK)||(qflags&QUOTAS_ENABLED)) {
+ if (ret == 0) {
+ char *quota_file = NULL;
+
+ asprintf(&quota_file,"/%s/%s%s",path, QUOTAFILENAME,USERQUOTAFILE_EXTENSION);
+ if (quota_file == NULL) {
+ DEBUG(0,("asprintf() failed!\n"));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ ret = quotactl(QCMD(Q_QUOTAON,USRQUOTA), bdev, -1,(void *)quota_file);
+ } else {
+ ret = 0;
+ }
+ } else {
+ if (ret != 0) {
+ /* turn off */
+ ret = quotactl(QCMD(Q_QUOTAOFF,USRQUOTA), bdev, -1, (void *)0);
+ } else {
+ ret = 0;
+ }
+ }
+
+ DEBUG(0,("sys_set_vfs_quota: ret(%d) errno(%d)[%s] uid(%d) bdev[%s]\n",
+ ret,errno,strerror(errno),id.uid,bdev));
+#else
+ if ((ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), bdev, id.uid, (void *)&D))==0) {
+ oldqflags |= QUOTAS_DENY_DISK;
+ }
+
+ if (oldqflags == qflags) {
+ ret = 0;
+ } else {
+ ret = -1;
+ }
+#endif
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ /* this stuff didn't work as it should:
+ * switching on/off quota via quotactl()
+ * didn't work!
+ * So we just return 0
+ * --metze
+ *
+ * On HPUX we didn't have the mount path,
+ * we need to fix sys_path_to_bdev()
+ *
+ */
+ id.gid = getgid();
+ DEBUG(10,("sys_set_vfs_quota: path[%s] bdev[%s] SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n",
+ path, bdev, (unsigned)id.gid));
+
+#if 0
+ ret = quotactl(QCMD(Q_GETQUOTA,GRPQUOTA), bdev, id, (void *)&D);
+
+ if ((qflags&QUOTAS_DENY_DISK)||(qflags&QUOTAS_ENABLED)) {
+ if (ret == 0) {
+ char *quota_file = NULL;
+
+ asprintf(&quota_file,"/%s/%s%s",path, QUOTAFILENAME,GROUPQUOTAFILE_EXTENSION);
+ if (quota_file == NULL) {
+ DEBUG(0,("asprintf() failed!\n"));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ ret = quotactl(QCMD(Q_QUOTAON,GRPQUOTA), bdev, -1,(void *)quota_file);
+ } else {
+ ret = 0;
+ }
+ } else {
+ if (ret != 0) {
+ /* turn off */
+ ret = quotactl(QCMD(Q_QUOTAOFF,GRPQUOTA), bdev, -1, (void *)0);
+ } else {
+ ret = 0;
+ }
+ }
+
+ DEBUG(0,("sys_set_vfs_quota: ret(%d) errno(%d)[%s] uid(%d) bdev[%s]\n",
+ ret,errno,strerror(errno),id.gid,bdev));
+#else
+ if ((ret = quotactl(QCMD(Q_GETQUOTA,GRPQUOTA), bdev, id.gid, (void *)&D))==0) {
+ oldqflags |= QUOTAS_DENY_DISK;
+ }
+
+ if (oldqflags == qflags) {
+ ret = 0;
+ } else {
+ ret = -1;
+ }
+#endif
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ default:
+ errno = ENOSYS;
+ return -1;
+ }
+
+ return ret;
+}
+
+#else /* HAVE_QUOTACTL_4A */
+ void dummy_sysquotas_4A(void){}
+#endif /* HAVE_QUOTACTL_4A */
diff --git a/source/lib/sysquotas_linux.c b/source/lib/sysquotas_linux.c
new file mode 100644
index 00000000000..3867c1b0f9b
--- /dev/null
+++ b/source/lib/sysquotas_linux.c
@@ -0,0 +1,560 @@
+/*
+ Unix SMB/CIFS implementation.
+ System QUOTA function wrappers for LINUX
+ Copyright (C) Stefan (metze) Metzmacher 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_QUOTA
+
+#ifdef HAVE_QUOTACTL_LINUX
+
+#include "samba_linux_quota.h"
+
+/****************************************************************************
+ Abstract out the v1 Linux quota get calls.
+****************************************************************************/
+static int sys_get_linux_v1_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+ uint32 qflags = 0;
+ struct v1_kern_dqblk D;
+ SMB_BIG_UINT bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
+
+ ZERO_STRUCT(D);
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ DEBUG(10,("sys_get_linux_v1_quota: path[%s] bdev[%s] SMB_USER_QUOTA_TYPE uid[%u]\n",
+ path, bdev, (unsigned)id.uid));
+
+ if ((ret = quotactl(QCMD(Q_V1_GETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D))&&errno != EDQUOT) {
+ return ret;
+ }
+
+ break;
+ case SMB_GROUP_QUOTA_TYPE:
+ DEBUG(10,("sys_get_linux_v1_quota: path[%s] bdev[%s] SMB_GROUP_QUOTA_TYPE gid[%u]\n",
+ path, bdev, (unsigned)id.gid));
+
+ if ((ret = quotactl(QCMD(Q_V1_GETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D))&&errno != EDQUOT) {
+ return ret;
+ }
+
+ break;
+ case SMB_USER_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_get_linux_v1_quota: path[%s] bdev[%s] SMB_USER_FS_QUOTA_TYPE (uid[%u])\n",
+ path, bdev, (unsigned)id.uid));
+
+ if ((ret = quotactl(QCMD(Q_V1_GETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D))==0) {
+ qflags |= QUOTAS_DENY_DISK;
+ }
+
+ break;
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_get_linux_v1_quota: path[%s] bdev[%s] SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n",
+ path, bdev, (unsigned)id.gid));
+
+ if ((ret = quotactl(QCMD(Q_V1_GETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D))==0) {
+ qflags |= QUOTAS_DENY_DISK;
+ }
+
+ break;
+ default:
+ errno = ENOSYS;
+ return -1;
+ }
+
+ dp->bsize = bsize;
+ dp->softlimit = (SMB_BIG_UINT)D.dqb_bsoftlimit;
+ dp->hardlimit = (SMB_BIG_UINT)D.dqb_bhardlimit;
+ dp->ihardlimit = (SMB_BIG_UINT)D.dqb_ihardlimit;
+ dp->isoftlimit = (SMB_BIG_UINT)D.dqb_isoftlimit;
+ dp->curinodes = (SMB_BIG_UINT)D.dqb_curinodes;
+ dp->curblocks = (SMB_BIG_UINT)D.dqb_curblocks;
+
+
+ dp->qflags = qflags;
+
+ return ret;
+}
+
+/****************************************************************************
+ Abstract out the v1 Linux quota set calls.
+****************************************************************************/
+static int sys_set_linux_v1_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+ uint32 qflags = 0;
+ uint32 oldqflags = 0;
+ struct v1_kern_dqblk D;
+ SMB_BIG_UINT bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
+
+ ZERO_STRUCT(D);
+
+ if (bsize == dp->bsize) {
+ D.dqb_bsoftlimit = dp->softlimit;
+ D.dqb_bhardlimit = dp->hardlimit;
+ D.dqb_ihardlimit = dp->ihardlimit;
+ D.dqb_isoftlimit = dp->isoftlimit;
+ } else {
+ D.dqb_bsoftlimit = (dp->softlimit*dp->bsize)/bsize;
+ D.dqb_bhardlimit = (dp->hardlimit*dp->bsize)/bsize;
+ D.dqb_ihardlimit = (dp->ihardlimit*dp->bsize)/bsize;
+ D.dqb_isoftlimit = (dp->isoftlimit*dp->bsize)/bsize;
+ }
+
+ qflags = dp->qflags;
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ DEBUG(10,("sys_set_linux_v1_quota: path[%s] bdev[%s] SMB_USER_QUOTA_TYPE uid[%u]\n",
+ path, bdev, (unsigned)id.uid));
+
+ ret = quotactl(QCMD(Q_V1_SETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D);
+ break;
+ case SMB_GROUP_QUOTA_TYPE:
+ DEBUG(10,("sys_set_linux_v1_quota: path[%s] bdev[%s] SMB_GROUP_QUOTA_TYPE gid[%u]\n",
+ path, bdev, (unsigned)id.gid));
+
+ ret = quotactl(QCMD(Q_V1_SETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D);
+ break;
+ case SMB_USER_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_set_linux_v1_quota: path[%s] bdev[%s] SMB_USER_FS_QUOTA_TYPE (uid[%u])\n",
+ path, bdev, (unsigned)id.uid));
+
+ if ((ret = quotactl(QCMD(Q_V1_GETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D))==0) {
+ oldqflags |= QUOTAS_DENY_DISK;
+ }
+
+ break;
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_set_linux_v1_quota: path[%s] bdev[%s] SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n",
+ path, bdev, (unsigned)id.gid));
+
+ if ((ret = quotactl(QCMD(Q_V1_GETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D))==0) {
+ oldqflags |= QUOTAS_DENY_DISK;
+ }
+
+ break;
+ default:
+ errno = ENOSYS;
+ return -1;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ Abstract out the v2 Linux quota get calls.
+****************************************************************************/
+static int sys_get_linux_v2_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+ uint32 qflags = 0;
+ struct v2_kern_dqblk D;
+ SMB_BIG_UINT bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
+
+ ZERO_STRUCT(D);
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ DEBUG(10,("sys_get_linux_v2_quota: path[%s] bdev[%s] SMB_USER_QUOTA_TYPE uid[%u]\n",
+ path, bdev, (unsigned)id.uid));
+
+ if ((ret = quotactl(QCMD(Q_V2_GETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D))&&errno != EDQUOT) {
+ return ret;
+ }
+
+ break;
+ case SMB_GROUP_QUOTA_TYPE:
+ DEBUG(10,("sys_get_linux_v2_quota: path[%s] bdev[%s] SMB_GROUP_QUOTA_TYPE gid[%u]\n",
+ path, bdev, (unsigned)id.gid));
+
+ if ((ret = quotactl(QCMD(Q_V2_GETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D))&&errno != EDQUOT) {
+ return ret;
+ }
+
+ break;
+ case SMB_USER_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_get_linux_v2_quota: path[%s] bdev[%s] SMB_USER_FS_QUOTA_TYPE (uid[%u])\n",
+ path, bdev, (unsigned)id.uid));
+
+ if ((ret = quotactl(QCMD(Q_V2_GETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D))==0) {
+ qflags |= QUOTAS_DENY_DISK;
+ }
+
+ break;
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_get_linux_v2_quota: path[%s] bdev[%s] SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n",
+ path, bdev, (unsigned)id.gid));
+
+ if ((ret = quotactl(QCMD(Q_V2_GETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D))==0) {
+ qflags |= QUOTAS_DENY_DISK;
+ }
+
+ break;
+ default:
+ errno = ENOSYS;
+ return -1;
+ }
+
+ dp->bsize = bsize;
+ dp->softlimit = (SMB_BIG_UINT)D.dqb_bsoftlimit;
+ dp->hardlimit = (SMB_BIG_UINT)D.dqb_bhardlimit;
+ dp->ihardlimit = (SMB_BIG_UINT)D.dqb_ihardlimit;
+ dp->isoftlimit = (SMB_BIG_UINT)D.dqb_isoftlimit;
+ dp->curinodes = (SMB_BIG_UINT)D.dqb_curinodes;
+ dp->curblocks = (SMB_BIG_UINT)D.dqb_curspace/bsize;
+
+
+ dp->qflags = qflags;
+
+ return ret;
+}
+
+/****************************************************************************
+ Abstract out the v2 Linux quota set calls.
+****************************************************************************/
+static int sys_set_linux_v2_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+ uint32 qflags = 0;
+ uint32 oldqflags = 0;
+ struct v2_kern_dqblk D;
+ SMB_BIG_UINT bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
+
+ ZERO_STRUCT(D);
+
+ if (bsize == dp->bsize) {
+ D.dqb_bsoftlimit = dp->softlimit;
+ D.dqb_bhardlimit = dp->hardlimit;
+ D.dqb_ihardlimit = dp->ihardlimit;
+ D.dqb_isoftlimit = dp->isoftlimit;
+ } else {
+ D.dqb_bsoftlimit = (dp->softlimit*dp->bsize)/bsize;
+ D.dqb_bhardlimit = (dp->hardlimit*dp->bsize)/bsize;
+ D.dqb_ihardlimit = (dp->ihardlimit*dp->bsize)/bsize;
+ D.dqb_isoftlimit = (dp->isoftlimit*dp->bsize)/bsize;
+ }
+
+ qflags = dp->qflags;
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ DEBUG(10,("sys_set_linux_v2_quota: path[%s] bdev[%s] SMB_USER_QUOTA_TYPE uid[%u]\n",
+ path, bdev, (unsigned)id.uid));
+
+ ret = quotactl(QCMD(Q_V2_SETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D);
+ break;
+ case SMB_GROUP_QUOTA_TYPE:
+ DEBUG(10,("sys_set_linux_v2_quota: path[%s] bdev[%s] SMB_GROUP_QUOTA_TYPE gid[%u]\n",
+ path, bdev, (unsigned)id.gid));
+
+ ret = quotactl(QCMD(Q_V2_SETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D);
+ break;
+ case SMB_USER_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_set_linux_v2_quota: path[%s] bdev[%s] SMB_USER_FS_QUOTA_TYPE (uid[%u])\n",
+ path, bdev, (unsigned)id.uid));
+
+ if ((ret = quotactl(QCMD(Q_V2_GETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D))==0) {
+ oldqflags |= QUOTAS_DENY_DISK;
+ }
+
+ break;
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_set_linux_v2_quota: path[%s] bdev[%s] SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n",
+ path, bdev, (unsigned)id.gid));
+
+ if ((ret = quotactl(QCMD(Q_V2_GETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D))==0) {
+ oldqflags |= QUOTAS_DENY_DISK;
+ }
+
+ break;
+ default:
+ errno = ENOSYS;
+ return -1;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ Abstract out the generic Linux quota get calls.
+****************************************************************************/
+static int sys_get_linux_gen_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+ uint32 qflags = 0;
+ struct if_dqblk D;
+ SMB_BIG_UINT bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
+
+ ZERO_STRUCT(D);
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ DEBUG(10,("sys_get_linux_gen_quota: path[%s] bdev[%s] SMB_USER_QUOTA_TYPE uid[%u]\n",
+ path, bdev, (unsigned)id.uid));
+
+ if ((ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D))&&errno != EDQUOT) {
+ return ret;
+ }
+
+ break;
+ case SMB_GROUP_QUOTA_TYPE:
+ DEBUG(10,("sys_get_linux_gen_quota: path[%s] bdev[%s] SMB_GROUP_QUOTA_TYPE gid[%u]\n",
+ path, bdev, (unsigned)id.gid));
+
+ if ((ret = quotactl(QCMD(Q_GETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D))&&errno != EDQUOT) {
+ return ret;
+ }
+
+ break;
+ case SMB_USER_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_get_linux_gen_quota: path[%s] bdev[%s] SMB_USER_FS_QUOTA_TYPE (uid[%u])\n",
+ path, bdev, (unsigned)id.uid));
+
+ if ((ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D))==0) {
+ qflags |= QUOTAS_DENY_DISK;
+ }
+
+ break;
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_get_linux_gen_quota: path[%s] bdev[%s] SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n",
+ path, bdev, (unsigned)id.gid));
+
+ if ((ret = quotactl(QCMD(Q_GETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D))==0) {
+ qflags |= QUOTAS_DENY_DISK;
+ }
+
+ break;
+ default:
+ errno = ENOSYS;
+ return -1;
+ }
+
+ dp->bsize = bsize;
+ dp->softlimit = (SMB_BIG_UINT)D.dqb_bsoftlimit;
+ dp->hardlimit = (SMB_BIG_UINT)D.dqb_bhardlimit;
+ dp->ihardlimit = (SMB_BIG_UINT)D.dqb_ihardlimit;
+ dp->isoftlimit = (SMB_BIG_UINT)D.dqb_isoftlimit;
+ dp->curinodes = (SMB_BIG_UINT)D.dqb_curinodes;
+ dp->curblocks = (SMB_BIG_UINT)D.dqb_curspace/bsize;
+
+
+ dp->qflags = qflags;
+
+ return ret;
+}
+
+/****************************************************************************
+ Abstract out the gen Linux quota set calls.
+****************************************************************************/
+static int sys_set_linux_gen_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+ uint32 qflags = 0;
+ uint32 oldqflags = 0;
+ struct if_dqblk D;
+ SMB_BIG_UINT bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
+
+ ZERO_STRUCT(D);
+
+ if (bsize == dp->bsize) {
+ D.dqb_bsoftlimit = dp->softlimit;
+ D.dqb_bhardlimit = dp->hardlimit;
+ D.dqb_ihardlimit = dp->ihardlimit;
+ D.dqb_isoftlimit = dp->isoftlimit;
+ } else {
+ D.dqb_bsoftlimit = (dp->softlimit*dp->bsize)/bsize;
+ D.dqb_bhardlimit = (dp->hardlimit*dp->bsize)/bsize;
+ D.dqb_ihardlimit = (dp->ihardlimit*dp->bsize)/bsize;
+ D.dqb_isoftlimit = (dp->isoftlimit*dp->bsize)/bsize;
+ }
+
+ qflags = dp->qflags;
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ DEBUG(10,("sys_set_linux_gen_quota: path[%s] bdev[%s] SMB_USER_QUOTA_TYPE uid[%u]\n",
+ path, bdev, (unsigned)id.uid));
+
+ ret = quotactl(QCMD(Q_SETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D);
+ break;
+ case SMB_GROUP_QUOTA_TYPE:
+ DEBUG(10,("sys_set_linux_gen_quota: path[%s] bdev[%s] SMB_GROUP_QUOTA_TYPE gid[%u]\n",
+ path, bdev, (unsigned)id.gid));
+
+ ret = quotactl(QCMD(Q_SETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D);
+ break;
+ case SMB_USER_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_set_linux_gen_quota: path[%s] bdev[%s] SMB_USER_FS_QUOTA_TYPE (uid[%u])\n",
+ path, bdev, (unsigned)id.uid));
+
+ if ((ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D))==0) {
+ oldqflags |= QUOTAS_DENY_DISK;
+ }
+
+ break;
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_set_linux_gen_quota: path[%s] bdev[%s] SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n",
+ path, bdev, (unsigned)id.gid));
+
+ if ((ret = quotactl(QCMD(Q_GETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D))==0) {
+ oldqflags |= QUOTAS_DENY_DISK;
+ }
+
+ break;
+ default:
+ errno = ENOSYS;
+ return -1;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ Abstract out the Linux quota get calls.
+****************************************************************************/
+int sys_get_vfs_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+
+ if (!path||!bdev||!dp)
+ smb_panic("sys_set_vfs_quota: called with NULL pointer");
+
+ ZERO_STRUCT(*dp);
+ dp->qtype = qtype;
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ case SMB_GROUP_QUOTA_TYPE:
+ if ((ret=sys_get_linux_gen_quota(path, bdev, qtype, id, dp))&&errno != EDQUOT) {
+ if ((ret=sys_get_linux_v2_quota(path, bdev, qtype, id, dp))&&errno != EDQUOT) {
+ if ((ret=sys_get_linux_v1_quota(path, bdev, qtype, id, dp))&&errno != EDQUOT) {
+ return ret;
+ }
+ }
+ }
+
+ if ((dp->curblocks==0)&&
+ (dp->softlimit==0)&&
+ (dp->hardlimit==0)) {
+ /* the upper layer functions don't want empty quota records...*/
+ return -1;
+ }
+
+ break;
+ case SMB_USER_FS_QUOTA_TYPE:
+ id.uid = getuid();
+
+ if ((ret=sys_get_linux_gen_quota(path, bdev, qtype, id, dp))&&errno != EDQUOT) {
+ if ((ret=sys_get_linux_v2_quota(path, bdev, qtype, id, dp))&&errno != EDQUOT) {
+ ret=sys_get_linux_v1_quota(path, bdev, qtype, id, dp);
+ }
+ }
+
+ ret = 0;
+ break;
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ id.gid = getgid();
+
+ if ((ret=sys_get_linux_gen_quota(path, bdev, qtype, id, dp))&&errno != EDQUOT) {
+ if ((ret=sys_get_linux_v2_quota(path, bdev, qtype, id, dp))&&errno != EDQUOT) {
+ ret=sys_get_linux_v1_quota(path, bdev, qtype, id, dp);
+ }
+ }
+
+ ret = 0;
+ break;
+ default:
+ errno = ENOSYS;
+ return -1;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ Abstract out the Linux quota set calls.
+****************************************************************************/
+int sys_set_vfs_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+ uint32 oldqflags = 0;
+
+ if (!path||!bdev||!dp)
+ smb_panic("sys_set_vfs_quota: called with NULL pointer");
+
+ oldqflags = dp->qflags;
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ case SMB_GROUP_QUOTA_TYPE:
+ if ((ret=sys_set_linux_gen_quota(path, bdev, qtype, id, dp))) {
+ if ((ret=sys_set_linux_v2_quota(path, bdev, qtype, id, dp))) {
+ if ((ret=sys_set_linux_v1_quota(path, bdev, qtype, id, dp))) {
+ return ret;
+ }
+ }
+ }
+ break;
+ case SMB_USER_FS_QUOTA_TYPE:
+ id.uid = getuid();
+
+ if ((ret=sys_get_linux_gen_quota(path, bdev, qtype, id, dp))) {
+ if ((ret=sys_get_linux_v2_quota(path, bdev, qtype, id, dp))) {
+ ret=sys_get_linux_v1_quota(path, bdev, qtype, id, dp);
+ }
+ }
+
+ if (oldqflags == dp->qflags) {
+ ret = 0;
+ } else {
+ ret = -1;
+ }
+ break;
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ id.gid = getgid();
+
+ if ((ret=sys_get_linux_gen_quota(path, bdev, qtype, id, dp))) {
+ if ((ret=sys_get_linux_v2_quota(path, bdev, qtype, id, dp))) {
+ ret=sys_get_linux_v1_quota(path, bdev, qtype, id, dp);
+ }
+ }
+
+ if (oldqflags == dp->qflags) {
+ ret = 0;
+ } else {
+ ret = -1;
+ }
+
+ break;
+ default:
+ errno = ENOSYS;
+ return -1;
+ }
+
+ return ret;
+}
+
+#else /* HAVE_QUOTACTL_LINUX */
+ void dummy_sysquotas_linux(void){}
+#endif /* HAVE_QUOTACTL_LINUX */
diff --git a/source/lib/sysquotas_xfs.c b/source/lib/sysquotas_xfs.c
new file mode 100644
index 00000000000..9fe4ec0d992
--- /dev/null
+++ b/source/lib/sysquotas_xfs.c
@@ -0,0 +1,333 @@
+/*
+ Unix SMB/CIFS implementation.
+ System QUOTA function wrappers for XFS
+ Copyright (C) Stefan (metze) Metzmacher 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_QUOTA
+
+#ifndef HAVE_SYS_QUOTAS
+#ifdef HAVE_XFS_QUOTAS
+#undef HAVE_XFS_QUOTAS
+#endif
+#endif
+
+#ifdef HAVE_XFS_QUOTAS
+
+#ifdef HAVE_LINUX_XFS_QUOTAS
+#include "samba_linux_quota.h"
+#include "samba_xfs_quota.h"
+#define HAVE_GROUP_QUOTA
+#else /* IRIX */
+#include <sys/quota.h>
+#endif
+
+/* on IRIX */
+#ifndef Q_XQUOTAON
+#define Q_XQUOTAON Q_QUOTAON
+#endif /* Q_XQUOTAON */
+#ifndef Q_XQUOTAOFF
+#define Q_XQUOTAOFF Q_QUOTAOFF
+#endif /* Q_XQUOTAOFF */
+#ifndef Q_XGETQSTAT
+#define Q_XGETQSTAT Q_GETQSTAT
+#endif /* Q_XGETQSTAT */
+
+/* currently doesn't support Group and Project quotas on IRIX
+ */
+
+#ifndef QCMD
+#define QCMD(x,y) x
+#endif
+
+/*
+ * IRIX has BBSIZE in <sys/param.h>
+ */
+#ifndef BBSHIFT
+#define BBSHIFT 9
+#endif /* BBSHIFT */
+#ifndef BBSIZE
+#define BBSIZE (1<<BBSHIFT)
+#endif /* BBSIZE */
+
+/****************************************************************************
+ Abstract out the XFS Quota Manager quota get call.
+****************************************************************************/
+int sys_get_xfs_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+ uint32 qflags = 0;
+ SMB_BIG_UINT bsize = (SMB_BIG_UINT)BBSIZE;
+ struct fs_disk_quota D;
+ struct fs_quota_stat F;
+ ZERO_STRUCT(D);
+ ZERO_STRUCT(F);
+
+ if (!bdev||!dp)
+ smb_panic("sys_get_xfs_quota: called with NULL pointer");
+
+ ZERO_STRUCT(*dp);
+ dp->qtype = qtype;
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ DEBUG(10,("sys_get_xfs_quota: path[%s] bdev[%s] SMB_USER_QUOTA_TYPE uid[%u]\n",
+ path, bdev, (unsigned)id.uid));
+
+ if ((ret=quotactl(QCMD(Q_XGETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D)))
+ return ret;
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_QUOTA_TYPE:
+ DEBUG(10,("sys_get_xfs_quota: path[%s] bdev[%s] SMB_GROUP_QUOTA_TYPE gid[%u]\n",
+ path, bdev, (unsigned)id.gid));
+
+ if ((ret=quotactl(QCMD(Q_XGETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D)))
+ return ret;
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ case SMB_USER_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_get_xfs_quota: path[%s] bdev[%s] SMB_USER_FS_QUOTA_TYPE (uid[%u])\n",
+ path, bdev, (unsigned)id.uid));
+
+ quotactl(QCMD(Q_XGETQSTAT,USRQUOTA), bdev, -1, (caddr_t)&F);
+
+ if (F.qs_flags & XFS_QUOTA_UDQ_ENFD) {
+ qflags |= QUOTAS_DENY_DISK;
+ }
+ else if (F.qs_flags & XFS_QUOTA_UDQ_ACCT) {
+ qflags |= QUOTAS_ENABLED;
+ }
+
+ ret = 0;
+
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_get_xfs_quota: path[%s] bdev[%s] SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n",
+ path, bdev, (unsigned)id.gid));
+
+ quotactl(QCMD(Q_XGETQSTAT,GRPQUOTA), bdev, -1, (caddr_t)&F);
+
+ if (F.qs_flags & XFS_QUOTA_GDQ_ENFD) {
+ qflags |= QUOTAS_DENY_DISK;
+ }
+ else if (F.qs_flags & XFS_QUOTA_GDQ_ACCT) {
+ qflags |= QUOTAS_ENABLED;
+ }
+
+ ret = 0;
+
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ default:
+ errno = ENOSYS;
+ return -1;
+ }
+
+ dp->bsize = bsize;
+ dp->softlimit = (SMB_BIG_UINT)D.d_blk_softlimit;
+ dp->hardlimit = (SMB_BIG_UINT)D.d_blk_hardlimit;
+ dp->ihardlimit = (SMB_BIG_UINT)D.d_ino_hardlimit;
+ dp->isoftlimit = (SMB_BIG_UINT)D.d_ino_softlimit;
+ dp->curinodes = (SMB_BIG_UINT)D.d_icount;
+ dp->curblocks = (SMB_BIG_UINT)D.d_bcount;
+ dp->qflags = qflags;
+
+ return ret;
+}
+
+/****************************************************************************
+ Abstract out the XFS Quota Manager quota set call.
+****************************************************************************/
+int sys_set_xfs_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+ uint32 qflags = 0;
+ SMB_BIG_UINT bsize = (SMB_BIG_UINT)BBSIZE;
+ struct fs_disk_quota D;
+ struct fs_quota_stat F;
+ int q_on = 0;
+ int q_off = 0;
+ ZERO_STRUCT(D);
+ ZERO_STRUCT(F);
+
+ if (!bdev||!dp)
+ smb_panic("sys_set_xfs_quota: called with NULL pointer");
+
+ if (bsize == dp->bsize) {
+ D.d_blk_softlimit = dp->softlimit;
+ D.d_blk_hardlimit = dp->hardlimit;
+ D.d_ino_hardlimit = dp->ihardlimit;
+ D.d_ino_softlimit = dp->isoftlimit;
+ } else {
+ D.d_blk_softlimit = (dp->softlimit*dp->bsize)/bsize;
+ D.d_blk_hardlimit = (dp->hardlimit*dp->bsize)/bsize;
+ D.d_ino_hardlimit = (dp->ihardlimit*dp->bsize)/bsize;
+ D.d_ino_softlimit = (dp->isoftlimit*dp->bsize)/bsize;
+ }
+
+ qflags = dp->qflags;
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ DEBUG(10,("sys_set_xfs_quota: path[%s] bdev[%s] SMB_USER_QUOTA_TYPE uid[%u]\n",
+ path, bdev, (unsigned)id.uid));
+
+ D.d_fieldmask |= FS_DQ_LIMIT_MASK;
+ ret = quotactl(QCMD(Q_XSETQLIM,USRQUOTA), bdev, id.uid, (caddr_t)&D);
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_QUOTA_TYPE:
+ DEBUG(10,("sys_set_xfs_quota: path[%s] bdev[%s] SMB_GROUP_QUOTA_TYPE gid[%u]\n",
+ path, bdev, (unsigned)id.gid));
+
+ D.d_fieldmask |= FS_DQ_LIMIT_MASK;
+ ret = quotactl(QCMD(Q_XSETQLIM,GRPQUOTA), bdev, id.gid, (caddr_t)&D);
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ case SMB_USER_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_set_xfs_quota: path[%s] bdev[%s] SMB_USER_FS_QUOTA_TYPE (uid[%u])\n",
+ path, bdev, (unsigned)id.uid));
+
+ quotactl(QCMD(Q_XGETQSTAT,USRQUOTA), bdev, -1, (caddr_t)&F);
+
+ if (qflags & QUOTAS_DENY_DISK) {
+ if (!(F.qs_flags & XFS_QUOTA_UDQ_ENFD))
+ q_on |= XFS_QUOTA_UDQ_ENFD;
+ if (!(F.qs_flags & XFS_QUOTA_UDQ_ACCT))
+ q_on |= XFS_QUOTA_UDQ_ACCT;
+
+ if (q_on != 0) {
+ ret = quotactl(QCMD(Q_XQUOTAON,USRQUOTA),bdev, -1, (caddr_t)&q_on);
+ } else {
+ ret = 0;
+ }
+
+ } else if (qflags & QUOTAS_ENABLED) {
+ if (F.qs_flags & XFS_QUOTA_UDQ_ENFD)
+ q_off |= XFS_QUOTA_UDQ_ENFD;
+
+ if (q_off != 0) {
+ ret = quotactl(QCMD(Q_XQUOTAOFF,USRQUOTA),bdev, -1, (caddr_t)&q_off);
+ } else {
+ ret = 0;
+ }
+
+ if (!(F.qs_flags & XFS_QUOTA_UDQ_ACCT))
+ q_on |= XFS_QUOTA_UDQ_ACCT;
+
+ if (q_on != 0) {
+ ret = quotactl(QCMD(Q_XQUOTAON,USRQUOTA),bdev, -1, (caddr_t)&q_on);
+ } else {
+ ret = 0;
+ }
+ } else {
+#if 0
+ /* Switch on XFS_QUOTA_UDQ_ACCT didn't work!
+ * only swittching off XFS_QUOTA_UDQ_ACCT work
+ */
+ if (F.qs_flags & XFS_QUOTA_UDQ_ENFD)
+ q_off |= XFS_QUOTA_UDQ_ENFD;
+ if (F.qs_flags & XFS_QUOTA_UDQ_ACCT)
+ q_off |= XFS_QUOTA_UDQ_ACCT;
+
+ if (q_off !=0) {
+ ret = quotactl(QCMD(Q_XQUOTAOFF,USRQUOTA),bdev, -1, (caddr_t)&q_off);
+ } else {
+ ret = 0;
+ }
+#else
+ ret = -1;
+#endif
+ }
+
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_set_xfs_quota: path[%s] bdev[%s] SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n",
+ path, bdev, (unsigned)id.gid));
+
+ quotactl(QCMD(Q_XGETQSTAT,GRPQUOTA), bdev, -1, (caddr_t)&F);
+
+ if (qflags & QUOTAS_DENY_DISK) {
+ if (!(F.qs_flags & XFS_QUOTA_GDQ_ENFD))
+ q_on |= XFS_QUOTA_GDQ_ENFD;
+ if (!(F.qs_flags & XFS_QUOTA_GDQ_ACCT))
+ q_on |= XFS_QUOTA_GDQ_ACCT;
+
+ if (q_on != 0) {
+ ret = quotactl(QCMD(Q_XQUOTAON,GRPQUOTA),bdev, -1, (caddr_t)&q_on);
+ } else {
+ ret = 0;
+ }
+
+ } else if (qflags & QUOTAS_ENABLED) {
+ if (F.qs_flags & XFS_QUOTA_GDQ_ENFD)
+ q_off |= XFS_QUOTA_GDQ_ENFD;
+
+ if (q_off != 0) {
+ ret = quotactl(QCMD(Q_XQUOTAOFF,GRPQUOTA),bdev, -1, (caddr_t)&q_off);
+ } else {
+ ret = 0;
+ }
+
+ if (!(F.qs_flags & XFS_QUOTA_GDQ_ACCT))
+ q_on |= XFS_QUOTA_GDQ_ACCT;
+
+ if (q_on != 0) {
+ ret = quotactl(QCMD(Q_XQUOTAON,GRPQUOTA),bdev, -1, (caddr_t)&q_on);
+ } else {
+ ret = 0;
+ }
+ } else {
+#if 0
+ /* Switch on XFS_QUOTA_UDQ_ACCT didn't work!
+ * only swittching off XFS_QUOTA_UDQ_ACCT work
+ */
+ if (F.qs_flags & XFS_QUOTA_GDQ_ENFD)
+ q_off |= XFS_QUOTA_GDQ_ENFD;
+ if (F.qs_flags & XFS_QUOTA_GDQ_ACCT)
+ q_off |= XFS_QUOTA_GDQ_ACCT;
+
+ if (q_off !=0) {
+ ret = quotactl(QCMD(Q_XQUOTAOFF,GRPQUOTA),bdev, -1, (caddr_t)&q_off);
+ } else {
+ ret = 0;
+ }
+#else
+ ret = -1;
+#endif
+ }
+
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ default:
+ errno = ENOSYS;
+ return -1;
+ }
+
+ return ret;
+}
+
+#else /* HAVE_XFS_QUOTAS */
+ void dummy_sysquotas_xfs(void){}
+#endif /* HAVE_XFS_QUOTAS */
diff --git a/source/lib/system.c b/source/lib/system.c
new file mode 100644
index 00000000000..a0007ec83cd
--- /dev/null
+++ b/source/lib/system.c
@@ -0,0 +1,1582 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba system utilities
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Jeremy Allison 1998-2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*
+ The idea is that this file will eventually have wrappers around all
+ important system calls in samba. The aims are:
+
+ - to enable easier porting by putting OS dependent stuff in here
+
+ - to allow for hooks into other "pseudo-filesystems"
+
+ - to allow easier integration of things like the japanese extensions
+
+ - to support the philosophy of Samba to expose the features of
+ the OS within the SMB model. In general whatever file/printer/variable
+ expansions/etc make sense to the OS should be acceptable to Samba.
+*/
+
+
+
+/*******************************************************************
+ A wrapper for usleep in case we don't have one.
+********************************************************************/
+
+int sys_usleep(long usecs)
+{
+#ifndef HAVE_USLEEP
+ struct timeval tval;
+#endif
+
+ /*
+ * We need this braindamage as the glibc usleep
+ * is not SPEC1170 complient... grumble... JRA.
+ */
+
+ if(usecs < 0 || usecs > 1000000) {
+ errno = EINVAL;
+ return -1;
+ }
+
+#if HAVE_USLEEP
+ usleep(usecs);
+ return 0;
+#else /* HAVE_USLEEP */
+ /*
+ * Fake it with select...
+ */
+ tval.tv_sec = 0;
+ tval.tv_usec = usecs/1000;
+ select(0,NULL,NULL,NULL,&tval);
+ return 0;
+#endif /* HAVE_USLEEP */
+}
+
+/*******************************************************************
+A read wrapper that will deal with EINTR.
+********************************************************************/
+
+ssize_t sys_read(int fd, void *buf, size_t count)
+{
+ ssize_t ret;
+
+ do {
+ ret = read(fd, buf, count);
+ } while (ret == -1 && errno == EINTR);
+ return ret;
+}
+
+/*******************************************************************
+A write wrapper that will deal with EINTR.
+********************************************************************/
+
+ssize_t sys_write(int fd, const void *buf, size_t count)
+{
+ ssize_t ret;
+
+ do {
+ ret = write(fd, buf, count);
+ } while (ret == -1 && errno == EINTR);
+ return ret;
+}
+
+
+/*******************************************************************
+A pread wrapper that will deal with EINTR and 64-bit file offsets.
+********************************************************************/
+
+#if defined(HAVE_PREAD) || defined(HAVE_PREAD64)
+ssize_t sys_pread(int fd, void *buf, size_t count, SMB_OFF_T off)
+{
+ ssize_t ret;
+
+ do {
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_PREAD64)
+ ret = pread64(fd, buf, count, off);
+#else
+ ret = pread(fd, buf, count, off);
+#endif
+ } while (ret == -1 && errno == EINTR);
+ return ret;
+}
+#endif
+
+/*******************************************************************
+A write wrapper that will deal with EINTR and 64-bit file offsets.
+********************************************************************/
+
+#if defined(HAVE_PWRITE) || defined(HAVE_PWRITE64)
+ssize_t sys_pwrite(int fd, const void *buf, size_t count, SMB_OFF_T off)
+{
+ ssize_t ret;
+
+ do {
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_PWRITE64)
+ ret = pwrite64(fd, buf, count, off);
+#else
+ ret = pwrite(fd, buf, count, off);
+#endif
+ } while (ret == -1 && errno == EINTR);
+ return ret;
+}
+#endif
+
+/*******************************************************************
+A send wrapper that will deal with EINTR.
+********************************************************************/
+
+ssize_t sys_send(int s, const void *msg, size_t len, int flags)
+{
+ ssize_t ret;
+
+ do {
+ ret = send(s, msg, len, flags);
+ } while (ret == -1 && errno == EINTR);
+ return ret;
+}
+
+/*******************************************************************
+A sendto wrapper that will deal with EINTR.
+********************************************************************/
+
+ssize_t sys_sendto(int s, const void *msg, size_t len, int flags, const struct sockaddr *to, socklen_t tolen)
+{
+ ssize_t ret;
+
+ do {
+ ret = sendto(s, msg, len, flags, to, tolen);
+ } while (ret == -1 && errno == EINTR);
+ return ret;
+}
+
+/*******************************************************************
+A recvfrom wrapper that will deal with EINTR.
+********************************************************************/
+
+ssize_t sys_recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen)
+{
+ ssize_t ret;
+
+ do {
+ ret = recvfrom(s, buf, len, flags, from, fromlen);
+ } while (ret == -1 && errno == EINTR);
+ return ret;
+}
+
+/*******************************************************************
+A fcntl wrapper that will deal with EINTR.
+********************************************************************/
+
+int sys_fcntl_ptr(int fd, int cmd, void *arg)
+{
+ int ret;
+
+ do {
+ ret = fcntl(fd, cmd, arg);
+ } while (ret == -1 && errno == EINTR);
+ return ret;
+}
+
+/*******************************************************************
+A fcntl wrapper that will deal with EINTR.
+********************************************************************/
+
+int sys_fcntl_long(int fd, int cmd, long arg)
+{
+ int ret;
+
+ do {
+ ret = fcntl(fd, cmd, arg);
+ } while (ret == -1 && errno == EINTR);
+ return ret;
+}
+
+/*******************************************************************
+A stat() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+int sys_stat(const char *fname,SMB_STRUCT_STAT *sbuf)
+{
+ int ret;
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_STAT64)
+ ret = stat64(fname, sbuf);
+#else
+ ret = stat(fname, sbuf);
+#endif
+ /* we always want directories to appear zero size */
+ if (ret == 0 && S_ISDIR(sbuf->st_mode)) sbuf->st_size = 0;
+ return ret;
+}
+
+/*******************************************************************
+ An fstat() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+int sys_fstat(int fd,SMB_STRUCT_STAT *sbuf)
+{
+ int ret;
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_FSTAT64)
+ ret = fstat64(fd, sbuf);
+#else
+ ret = fstat(fd, sbuf);
+#endif
+ /* we always want directories to appear zero size */
+ if (ret == 0 && S_ISDIR(sbuf->st_mode)) sbuf->st_size = 0;
+ return ret;
+}
+
+/*******************************************************************
+ An lstat() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+int sys_lstat(const char *fname,SMB_STRUCT_STAT *sbuf)
+{
+ int ret;
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_LSTAT64)
+ ret = lstat64(fname, sbuf);
+#else
+ ret = lstat(fname, sbuf);
+#endif
+ /* we always want directories to appear zero size */
+ if (ret == 0 && S_ISDIR(sbuf->st_mode)) sbuf->st_size = 0;
+ return ret;
+}
+
+/*******************************************************************
+ An ftruncate() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+int sys_ftruncate(int fd, SMB_OFF_T offset)
+{
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_FTRUNCATE64)
+ return ftruncate64(fd, offset);
+#else
+ return ftruncate(fd, offset);
+#endif
+}
+
+/*******************************************************************
+ An lseek() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+SMB_OFF_T sys_lseek(int fd, SMB_OFF_T offset, int whence)
+{
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_LSEEK64)
+ return lseek64(fd, offset, whence);
+#else
+ return lseek(fd, offset, whence);
+#endif
+}
+
+/*******************************************************************
+ An fseek() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+int sys_fseek(FILE *fp, SMB_OFF_T offset, int whence)
+{
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(LARGE_SMB_OFF_T) && defined(HAVE_FSEEK64)
+ return fseek64(fp, offset, whence);
+#elif defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(LARGE_SMB_OFF_T) && defined(HAVE_FSEEKO64)
+ return fseeko64(fp, offset, whence);
+#else
+ return fseek(fp, offset, whence);
+#endif
+}
+
+/*******************************************************************
+ An ftell() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+SMB_OFF_T sys_ftell(FILE *fp)
+{
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(LARGE_SMB_OFF_T) && defined(HAVE_FTELL64)
+ return (SMB_OFF_T)ftell64(fp);
+#elif defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(LARGE_SMB_OFF_T) && defined(HAVE_FTELLO64)
+ return (SMB_OFF_T)ftello64(fp);
+#else
+ return (SMB_OFF_T)ftell(fp);
+#endif
+}
+
+/*******************************************************************
+ A creat() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+int sys_creat(const char *path, mode_t mode)
+{
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_CREAT64)
+ return creat64(path, mode);
+#else
+ /*
+ * If creat64 isn't defined then ensure we call a potential open64.
+ * JRA.
+ */
+ return sys_open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
+#endif
+}
+
+/*******************************************************************
+ An open() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+int sys_open(const char *path, int oflag, mode_t mode)
+{
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OPEN64)
+ return open64(path, oflag, mode);
+#else
+ return open(path, oflag, mode);
+#endif
+}
+
+/*******************************************************************
+ An fopen() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+FILE *sys_fopen(const char *path, const char *type)
+{
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_FOPEN64)
+ return fopen64(path, type);
+#else
+ return fopen(path, type);
+#endif
+}
+
+/*******************************************************************
+ A readdir wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+SMB_STRUCT_DIRENT *sys_readdir(DIR *dirp)
+{
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_READDIR64)
+ return readdir64(dirp);
+#else
+ return readdir(dirp);
+#endif
+}
+
+/*******************************************************************
+ An mknod() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+int sys_mknod(const char *path, mode_t mode, SMB_DEV_T dev)
+{
+#if defined(HAVE_MKNOD) || defined(HAVE_MKNOD64)
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_MKNOD64) && defined(HAVE_DEV64_T)
+ return mknod64(path, mode, dev);
+#else
+ return mknod(path, mode, dev);
+#endif
+#else
+ /* No mknod system call. */
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+/*******************************************************************
+ Wrapper for realpath.
+********************************************************************/
+
+char *sys_realpath(const char *path, char *resolved_path)
+{
+#if defined(HAVE_REALPATH)
+ return realpath(path, resolved_path);
+#else
+ /* As realpath is not a system call we can't return ENOSYS. */
+ errno = EINVAL;
+ return NULL;
+#endif
+}
+
+/*******************************************************************
+The wait() calls vary between systems
+********************************************************************/
+
+int sys_waitpid(pid_t pid,int *status,int options)
+{
+#ifdef HAVE_WAITPID
+ return waitpid(pid,status,options);
+#else /* HAVE_WAITPID */
+ return wait4(pid, status, options, NULL);
+#endif /* HAVE_WAITPID */
+}
+
+/*******************************************************************
+ System wrapper for getwd
+********************************************************************/
+
+char *sys_getwd(char *s)
+{
+ char *wd;
+#ifdef HAVE_GETCWD
+ wd = (char *)getcwd(s, sizeof (pstring));
+#else
+ wd = (char *)getwd(s);
+#endif
+ return wd;
+}
+
+/*******************************************************************
+system wrapper for symlink
+********************************************************************/
+
+int sys_symlink(const char *oldpath, const char *newpath)
+{
+#ifndef HAVE_SYMLINK
+ errno = ENOSYS;
+ return -1;
+#else
+ return symlink(oldpath, newpath);
+#endif
+}
+
+/*******************************************************************
+system wrapper for readlink
+********************************************************************/
+
+int sys_readlink(const char *path, char *buf, size_t bufsiz)
+{
+#ifndef HAVE_READLINK
+ errno = ENOSYS;
+ return -1;
+#else
+ return readlink(path, buf, bufsiz);
+#endif
+}
+
+/*******************************************************************
+system wrapper for link
+********************************************************************/
+
+int sys_link(const char *oldpath, const char *newpath)
+{
+#ifndef HAVE_LINK
+ errno = ENOSYS;
+ return -1;
+#else
+ return link(oldpath, newpath);
+#endif
+}
+
+/*******************************************************************
+chown isn't used much but OS/2 doesn't have it
+********************************************************************/
+
+int sys_chown(const char *fname,uid_t uid,gid_t gid)
+{
+#ifndef HAVE_CHOWN
+ static int done;
+ if (!done) {
+ DEBUG(1,("WARNING: no chown!\n"));
+ done=1;
+ }
+ errno = ENOSYS;
+ return -1;
+#else
+ return(chown(fname,uid,gid));
+#endif
+}
+
+/*******************************************************************
+os/2 also doesn't have chroot
+********************************************************************/
+int sys_chroot(const char *dname)
+{
+#ifndef HAVE_CHROOT
+ static int done;
+ if (!done) {
+ DEBUG(1,("WARNING: no chroot!\n"));
+ done=1;
+ }
+ errno = ENOSYS;
+ return -1;
+#else
+ return(chroot(dname));
+#endif
+}
+
+/**************************************************************************
+A wrapper for gethostbyname() that tries avoids looking up hostnames
+in the root domain, which can cause dial-on-demand links to come up for no
+apparent reason.
+****************************************************************************/
+
+struct hostent *sys_gethostbyname(const char *name)
+{
+#ifdef REDUCE_ROOT_DNS_LOOKUPS
+ char query[256], hostname[256];
+ char *domain;
+
+ /* Does this name have any dots in it? If so, make no change */
+
+ if (strchr_m(name, '.'))
+ return(gethostbyname(name));
+
+ /* Get my hostname, which should have domain name
+ attached. If not, just do the gethostname on the
+ original string.
+ */
+
+ gethostname(hostname, sizeof(hostname) - 1);
+ hostname[sizeof(hostname) - 1] = 0;
+ if ((domain = strchr_m(hostname, '.')) == NULL)
+ return(gethostbyname(name));
+
+ /* Attach domain name to query and do modified query.
+ If names too large, just do gethostname on the
+ original string.
+ */
+
+ if((strlen(name) + strlen(domain)) >= sizeof(query))
+ return(gethostbyname(name));
+
+ slprintf(query, sizeof(query)-1, "%s%s", name, domain);
+ return(gethostbyname(query));
+#else /* REDUCE_ROOT_DNS_LOOKUPS */
+ return(gethostbyname(name));
+#endif /* REDUCE_ROOT_DNS_LOOKUPS */
+}
+
+
+#if defined(HAVE_IRIX_SPECIFIC_CAPABILITIES)
+/**************************************************************************
+ Try and abstract process capabilities (for systems that have them).
+****************************************************************************/
+static BOOL set_process_capability( uint32 cap_flag, BOOL enable )
+{
+ if(cap_flag == KERNEL_OPLOCK_CAPABILITY) {
+ cap_t cap = cap_get_proc();
+
+ if (cap == NULL) {
+ DEBUG(0,("set_process_capability: cap_get_proc failed. Error was %s\n",
+ strerror(errno)));
+ return False;
+ }
+
+ if(enable)
+ cap->cap_effective |= CAP_NETWORK_MGT;
+ else
+ cap->cap_effective &= ~CAP_NETWORK_MGT;
+
+ if (cap_set_proc(cap) == -1) {
+ DEBUG(0,("set_process_capability: cap_set_proc failed. Error was %s\n",
+ strerror(errno)));
+ cap_free(cap);
+ return False;
+ }
+
+ cap_free(cap);
+
+ DEBUG(10,("set_process_capability: Set KERNEL_OPLOCK_CAPABILITY.\n"));
+ }
+ return True;
+}
+
+/**************************************************************************
+ Try and abstract inherited process capabilities (for systems that have them).
+****************************************************************************/
+
+static BOOL set_inherited_process_capability( uint32 cap_flag, BOOL enable )
+{
+ if(cap_flag == KERNEL_OPLOCK_CAPABILITY) {
+ cap_t cap = cap_get_proc();
+
+ if (cap == NULL) {
+ DEBUG(0,("set_inherited_process_capability: cap_get_proc failed. Error was %s\n",
+ strerror(errno)));
+ return False;
+ }
+
+ if(enable)
+ cap->cap_inheritable |= CAP_NETWORK_MGT;
+ else
+ cap->cap_inheritable &= ~CAP_NETWORK_MGT;
+
+ if (cap_set_proc(cap) == -1) {
+ DEBUG(0,("set_inherited_process_capability: cap_set_proc failed. Error was %s\n",
+ strerror(errno)));
+ cap_free(cap);
+ return False;
+ }
+
+ cap_free(cap);
+
+ DEBUG(10,("set_inherited_process_capability: Set KERNEL_OPLOCK_CAPABILITY.\n"));
+ }
+ return True;
+}
+#endif
+
+/****************************************************************************
+ Gain the oplock capability from the kernel if possible.
+****************************************************************************/
+
+void oplock_set_capability(BOOL this_process, BOOL inherit)
+{
+#if HAVE_KERNEL_OPLOCKS_IRIX
+ set_process_capability(KERNEL_OPLOCK_CAPABILITY,this_process);
+ set_inherited_process_capability(KERNEL_OPLOCK_CAPABILITY,inherit);
+#endif
+}
+
+/**************************************************************************
+ Wrapper for random().
+****************************************************************************/
+
+long sys_random(void)
+{
+#if defined(HAVE_RANDOM)
+ return (long)random();
+#elif defined(HAVE_RAND)
+ return (long)rand();
+#else
+ DEBUG(0,("Error - no random function available !\n"));
+ exit(1);
+#endif
+}
+
+/**************************************************************************
+ Wrapper for srandom().
+****************************************************************************/
+
+void sys_srandom(unsigned int seed)
+{
+#if defined(HAVE_SRANDOM)
+ srandom(seed);
+#elif defined(HAVE_SRAND)
+ srand(seed);
+#else
+ DEBUG(0,("Error - no srandom function available !\n"));
+ exit(1);
+#endif
+}
+
+/**************************************************************************
+ Returns equivalent to NGROUPS_MAX - using sysconf if needed.
+****************************************************************************/
+
+int groups_max(void)
+{
+#if defined(SYSCONF_SC_NGROUPS_MAX)
+ int ret = sysconf(_SC_NGROUPS_MAX);
+ return (ret == -1) ? NGROUPS_MAX : ret;
+#else
+ return NGROUPS_MAX;
+#endif
+}
+
+/**************************************************************************
+ Wrapper for getgroups. Deals with broken (int) case.
+****************************************************************************/
+
+int sys_getgroups(int setlen, gid_t *gidset)
+{
+#if !defined(HAVE_BROKEN_GETGROUPS)
+ return getgroups(setlen, gidset);
+#else
+
+ GID_T gid;
+ GID_T *group_list;
+ int i, ngroups;
+
+ if(setlen == 0) {
+ return getgroups(setlen, &gid);
+ }
+
+ /*
+ * Broken case. We need to allocate a
+ * GID_T array of size setlen.
+ */
+
+ if(setlen < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (setlen == 0)
+ setlen = groups_max();
+
+ if((group_list = (GID_T *)malloc(setlen * sizeof(GID_T))) == NULL) {
+ DEBUG(0,("sys_getgroups: Malloc fail.\n"));
+ return -1;
+ }
+
+ if((ngroups = getgroups(setlen, group_list)) < 0) {
+ int saved_errno = errno;
+ SAFE_FREE(group_list);
+ errno = saved_errno;
+ return -1;
+ }
+
+ for(i = 0; i < ngroups; i++)
+ gidset[i] = (gid_t)group_list[i];
+
+ SAFE_FREE(group_list);
+ return ngroups;
+#endif /* HAVE_BROKEN_GETGROUPS */
+}
+
+
+/**************************************************************************
+ Wrapper for setgroups. Deals with broken (int) case. Automatically used
+ if we have broken getgroups.
+****************************************************************************/
+
+int sys_setgroups(int setlen, gid_t *gidset)
+{
+#if !defined(HAVE_SETGROUPS)
+ errno = ENOSYS;
+ return -1;
+#endif /* HAVE_SETGROUPS */
+
+#if !defined(HAVE_BROKEN_GETGROUPS)
+ return setgroups(setlen, gidset);
+#else
+
+ GID_T *group_list;
+ int i ;
+
+ if (setlen == 0)
+ return 0 ;
+
+ if (setlen < 0 || setlen > groups_max()) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /*
+ * Broken case. We need to allocate a
+ * GID_T array of size setlen.
+ */
+
+ if((group_list = (GID_T *)malloc(setlen * sizeof(GID_T))) == NULL) {
+ DEBUG(0,("sys_setgroups: Malloc fail.\n"));
+ return -1;
+ }
+
+ for(i = 0; i < setlen; i++)
+ group_list[i] = (GID_T) gidset[i];
+
+ if(setgroups(setlen, group_list) != 0) {
+ int saved_errno = errno;
+ SAFE_FREE(group_list);
+ errno = saved_errno;
+ return -1;
+ }
+
+ SAFE_FREE(group_list);
+ return 0 ;
+#endif /* HAVE_BROKEN_GETGROUPS */
+}
+
+/**************************************************************************
+ Wrappers for setpwent(), getpwent() and endpwent()
+****************************************************************************/
+
+void sys_setpwent(void)
+{
+ setpwent();
+}
+
+struct passwd *sys_getpwent(void)
+{
+ return getpwent();
+}
+
+void sys_endpwent(void)
+{
+ endpwent();
+}
+
+/**************************************************************************
+ Wrappers for getpwnam(), getpwuid(), getgrnam(), getgrgid()
+****************************************************************************/
+
+struct passwd *sys_getpwnam(const char *name)
+{
+ return getpwnam(name);
+}
+
+struct passwd *sys_getpwuid(uid_t uid)
+{
+ return getpwuid(uid);
+}
+
+struct group *sys_getgrnam(const char *name)
+{
+ return getgrnam(name);
+}
+
+struct group *sys_getgrgid(gid_t gid)
+{
+ return getgrgid(gid);
+}
+
+#if 0 /* NOT CURRENTLY USED - JRA */
+/**************************************************************************
+ The following are the UNICODE versions of *all* system interface functions
+ called within Samba. Ok, ok, the exceptions are the gethostbyXX calls,
+ which currently are left as ascii as they are not used other than in name
+ resolution.
+****************************************************************************/
+
+/**************************************************************************
+ Wide stat. Just narrow and call sys_xxx.
+****************************************************************************/
+
+int wsys_stat(const smb_ucs2_t *wfname,SMB_STRUCT_STAT *sbuf)
+{
+ pstring fname;
+ return sys_stat(unicode_to_unix(fname,wfname,sizeof(fname)), sbuf);
+}
+
+/**************************************************************************
+ Wide lstat. Just narrow and call sys_xxx.
+****************************************************************************/
+
+int wsys_lstat(const smb_ucs2_t *wfname,SMB_STRUCT_STAT *sbuf)
+{
+ pstring fname;
+ return sys_lstat(unicode_to_unix(fname,wfname,sizeof(fname)), sbuf);
+}
+
+/**************************************************************************
+ Wide creat. Just narrow and call sys_xxx.
+****************************************************************************/
+
+int wsys_creat(const smb_ucs2_t *wfname, mode_t mode)
+{
+ pstring fname;
+ return sys_creat(unicode_to_unix(fname,wfname,sizeof(fname)), mode);
+}
+
+/**************************************************************************
+ Wide open. Just narrow and call sys_xxx.
+****************************************************************************/
+
+int wsys_open(const smb_ucs2_t *wfname, int oflag, mode_t mode)
+{
+ pstring fname;
+ return sys_open(unicode_to_unix(fname,wfname,sizeof(fname)), oflag, mode);
+}
+
+/**************************************************************************
+ Wide fopen. Just narrow and call sys_xxx.
+****************************************************************************/
+
+FILE *wsys_fopen(const smb_ucs2_t *wfname, const char *type)
+{
+ pstring fname;
+ return sys_fopen(unicode_to_unix(fname,wfname,sizeof(fname)), type);
+}
+
+/**************************************************************************
+ Wide opendir. Just narrow and call sys_xxx.
+****************************************************************************/
+
+DIR *wsys_opendir(const smb_ucs2_t *wfname)
+{
+ pstring fname;
+ return opendir(unicode_to_unix(fname,wfname,sizeof(fname)));
+}
+
+/**************************************************************************
+ Wide readdir. Return a structure pointer containing a wide filename.
+****************************************************************************/
+
+SMB_STRUCT_WDIRENT *wsys_readdir(DIR *dirp)
+{
+ static SMB_STRUCT_WDIRENT retval;
+ SMB_STRUCT_DIRENT *dirval = sys_readdir(dirp);
+
+ if(!dirval)
+ return NULL;
+
+ /*
+ * The only POSIX defined member of this struct is d_name.
+ */
+
+ unix_to_unicode(retval.d_name,dirval->d_name,sizeof(retval.d_name));
+
+ return &retval;
+}
+
+/**************************************************************************
+ Wide getwd. Call sys_xxx and widen. Assumes s points to a wpstring.
+****************************************************************************/
+
+smb_ucs2_t *wsys_getwd(smb_ucs2_t *s)
+{
+ pstring fname;
+ char *p = sys_getwd(fname);
+
+ if(!p)
+ return NULL;
+
+ return unix_to_unicode(s, p, sizeof(wpstring));
+}
+
+/**************************************************************************
+ Wide chown. Just narrow and call sys_xxx.
+****************************************************************************/
+
+int wsys_chown(const smb_ucs2_t *wfname, uid_t uid, gid_t gid)
+{
+ pstring fname;
+ return chown(unicode_to_unix(fname,wfname,sizeof(fname)), uid, gid);
+}
+
+/**************************************************************************
+ Wide chroot. Just narrow and call sys_xxx.
+****************************************************************************/
+
+int wsys_chroot(const smb_ucs2_t *wfname)
+{
+ pstring fname;
+ return chroot(unicode_to_unix(fname,wfname,sizeof(fname)));
+}
+
+/**************************************************************************
+ Wide getpwnam. Return a structure pointer containing wide names.
+****************************************************************************/
+
+SMB_STRUCT_WPASSWD *wsys_getpwnam(const smb_ucs2_t *wname)
+{
+ static SMB_STRUCT_WPASSWD retval;
+ fstring name;
+ struct passwd *pwret = sys_getpwnam(unicode_to_unix(name,wname,sizeof(name)));
+
+ if(!pwret)
+ return NULL;
+
+ unix_to_unicode(retval.pw_name, pwret->pw_name, sizeof(retval.pw_name));
+ retval.pw_passwd = pwret->pw_passwd;
+ retval.pw_uid = pwret->pw_uid;
+ retval.pw_gid = pwret->pw_gid;
+ unix_to_unicode(retval.pw_gecos, pwret->pw_gecos, sizeof(retval.pw_gecos));
+ unix_to_unicode(retval.pw_dir, pwret->pw_dir, sizeof(retval.pw_dir));
+ unix_to_unicode(retval.pw_shell, pwret->pw_shell, sizeof(retval.pw_shell));
+
+ return &retval;
+}
+
+/**************************************************************************
+ Wide getpwuid. Return a structure pointer containing wide names.
+****************************************************************************/
+
+SMB_STRUCT_WPASSWD *wsys_getpwuid(uid_t uid)
+{
+ static SMB_STRUCT_WPASSWD retval;
+ struct passwd *pwret = sys_getpwuid(uid);
+
+ if(!pwret)
+ return NULL;
+
+ unix_to_unicode(retval.pw_name, pwret->pw_name, sizeof(retval.pw_name));
+ retval.pw_passwd = pwret->pw_passwd;
+ retval.pw_uid = pwret->pw_uid;
+ retval.pw_gid = pwret->pw_gid;
+ unix_to_unicode(retval.pw_gecos, pwret->pw_gecos, sizeof(retval.pw_gecos));
+ unix_to_unicode(retval.pw_dir, pwret->pw_dir, sizeof(retval.pw_dir));
+ unix_to_unicode(retval.pw_shell, pwret->pw_shell, sizeof(retval.pw_shell));
+
+ return &retval;
+}
+#endif /* NOT CURRENTLY USED - JRA */
+
+/**************************************************************************
+ Extract a command into an arg list. Uses a static pstring for storage.
+ Caller frees returned arg list (which contains pointers into the static pstring).
+****************************************************************************/
+
+static char **extract_args(const char *command)
+{
+ static pstring trunc_cmd;
+ char *ptr;
+ int argcl;
+ char **argl = NULL;
+ int i;
+
+ pstrcpy(trunc_cmd, command);
+
+ if(!(ptr = strtok(trunc_cmd, " \t"))) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /*
+ * Count the args.
+ */
+
+ for( argcl = 1; ptr; ptr = strtok(NULL, " \t"))
+ argcl++;
+
+ if((argl = (char **)malloc((argcl + 1) * sizeof(char *))) == NULL)
+ return NULL;
+
+ /*
+ * Now do the extraction.
+ */
+
+ pstrcpy(trunc_cmd, command);
+
+ ptr = strtok(trunc_cmd, " \t");
+ i = 0;
+ argl[i++] = ptr;
+
+ while((ptr = strtok(NULL, " \t")) != NULL)
+ argl[i++] = ptr;
+
+ argl[i++] = NULL;
+ return argl;
+}
+
+/**************************************************************************
+ Wrapper for fork. Ensures that mypid is reset. Used so we can write
+ a sys_getpid() that only does a system call *once*.
+****************************************************************************/
+
+static pid_t mypid = (pid_t)-1;
+
+pid_t sys_fork(void)
+{
+ pid_t forkret = fork();
+
+ if (forkret == (pid_t)0) /* Child - reset mypid so sys_getpid does a system call. */
+ mypid = (pid_t) -1;
+
+ return forkret;
+}
+
+/**************************************************************************
+ Wrapper for getpid. Ensures we only do a system call *once*.
+****************************************************************************/
+
+pid_t sys_getpid(void)
+{
+ if (mypid == (pid_t)-1)
+ mypid = getpid();
+
+ return mypid;
+}
+
+/**************************************************************************
+ Wrapper for popen. Safer as it doesn't search a path.
+ Modified from the glibc sources.
+ modified by tridge to return a file descriptor. We must kick our FILE* habit
+****************************************************************************/
+
+typedef struct _popen_list
+{
+ int fd;
+ pid_t child_pid;
+ struct _popen_list *next;
+} popen_list;
+
+static popen_list *popen_chain;
+
+int sys_popen(const char *command)
+{
+ int parent_end, child_end;
+ int pipe_fds[2];
+ popen_list *entry = NULL;
+ char **argl = NULL;
+
+ if (pipe(pipe_fds) < 0)
+ return -1;
+
+ parent_end = pipe_fds[0];
+ child_end = pipe_fds[1];
+
+ if (!*command) {
+ errno = EINVAL;
+ goto err_exit;
+ }
+
+ if((entry = (popen_list *)malloc(sizeof(popen_list))) == NULL)
+ goto err_exit;
+
+ ZERO_STRUCTP(entry);
+
+ /*
+ * Extract the command and args into a NULL terminated array.
+ */
+
+ if(!(argl = extract_args(command)))
+ goto err_exit;
+
+ entry->child_pid = sys_fork();
+
+ if (entry->child_pid == -1) {
+ goto err_exit;
+ }
+
+ if (entry->child_pid == 0) {
+
+ /*
+ * Child !
+ */
+
+ int child_std_end = STDOUT_FILENO;
+ popen_list *p;
+
+ close(parent_end);
+ if (child_end != child_std_end) {
+ dup2 (child_end, child_std_end);
+ close (child_end);
+ }
+
+ /*
+ * POSIX.2: "popen() shall ensure that any streams from previous
+ * popen() calls that remain open in the parent process are closed
+ * in the new child process."
+ */
+
+ for (p = popen_chain; p; p = p->next)
+ close(p->fd);
+
+ execv(argl[0], argl);
+ _exit (127);
+ }
+
+ /*
+ * Parent.
+ */
+
+ close (child_end);
+ SAFE_FREE(argl);
+
+ /* Link into popen_chain. */
+ entry->next = popen_chain;
+ popen_chain = entry;
+ entry->fd = parent_end;
+
+ return entry->fd;
+
+err_exit:
+
+ SAFE_FREE(entry);
+ SAFE_FREE(argl);
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ return -1;
+}
+
+/**************************************************************************
+ Wrapper for pclose. Modified from the glibc sources.
+****************************************************************************/
+
+int sys_pclose(int fd)
+{
+ int wstatus;
+ popen_list **ptr = &popen_chain;
+ popen_list *entry = NULL;
+ pid_t wait_pid;
+ int status = -1;
+
+ /* Unlink from popen_chain. */
+ for ( ; *ptr != NULL; ptr = &(*ptr)->next) {
+ if ((*ptr)->fd == fd) {
+ entry = *ptr;
+ *ptr = (*ptr)->next;
+ status = 0;
+ break;
+ }
+ }
+
+ if (status < 0 || close(entry->fd) < 0)
+ return -1;
+
+ /*
+ * As Samba is catching and eating child process
+ * exits we don't really care about the child exit
+ * code, a -1 with errno = ECHILD will do fine for us.
+ */
+
+ do {
+ wait_pid = sys_waitpid (entry->child_pid, &wstatus, 0);
+ } while (wait_pid == -1 && errno == EINTR);
+
+ SAFE_FREE(entry);
+
+ if (wait_pid == -1)
+ return -1;
+ return wstatus;
+}
+
+/**************************************************************************
+ Wrappers for dlopen, dlsym, dlclose.
+****************************************************************************/
+
+void *sys_dlopen(const char *name, int flags)
+{
+#if defined(HAVE_DLOPEN)
+ return dlopen(name, flags);
+#else
+ return NULL;
+#endif
+}
+
+void *sys_dlsym(void *handle, const char *symbol)
+{
+#if defined(HAVE_DLSYM)
+ return dlsym(handle, symbol);
+#else
+ return NULL;
+#endif
+}
+
+int sys_dlclose (void *handle)
+{
+#if defined(HAVE_DLCLOSE)
+ return dlclose(handle);
+#else
+ return 0;
+#endif
+}
+
+const char *sys_dlerror(void)
+{
+#if defined(HAVE_DLERROR)
+ return dlerror();
+#else
+ return NULL;
+#endif
+}
+
+int sys_dup2(int oldfd, int newfd)
+{
+#if defined(HAVE_DUP2)
+ return dup2(oldfd, newfd);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+/**************************************************************************
+ Wrapper for Admin Logs.
+****************************************************************************/
+
+ void sys_adminlog(int priority, const char *format_str, ...)
+{
+ va_list ap;
+ int ret;
+ char *msgbuf = NULL;
+
+ va_start( ap, format_str );
+ ret = vasprintf( &msgbuf, format_str, ap );
+ va_end( ap );
+
+ if (ret == -1)
+ return;
+
+#if defined(HAVE_SYSLOG)
+ syslog( priority, "%s", msgbuf );
+#else
+ DEBUG(0,("%s", msgbuf ));
+#endif
+ SAFE_FREE(msgbuf);
+}
+
+/**************************************************************************
+ Wrappers for extented attribute calls. Based on the Linux package with
+ support for IRIX also. Expand as other systems have them.
+****************************************************************************/
+
+ssize_t sys_getxattr (const char *path, const char *name, void *value, size_t size)
+{
+#if defined(HAVE_GETXATTR)
+ return getxattr(path, name, value, size);
+#elif defined(HAVE_ATTR_GET)
+ int retval, flags = 0;
+ int valuelength = (int)size;
+ char *attrname = strchr(name,'.') +1;
+
+ if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
+
+ retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
+
+ return retval ? retval : valuelength;
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+ssize_t sys_lgetxattr (const char *path, const char *name, void *value, size_t size)
+{
+#if defined(HAVE_LGETXATTR)
+ return lgetxattr(path, name, value, size);
+#elif defined(HAVE_ATTR_GET)
+ int retval, flags = ATTR_DONTFOLLOW;
+ int valuelength = (int)size;
+ char *attrname = strchr(name,'.') +1;
+
+ if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
+
+ retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
+
+ return retval ? retval : valuelength;
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+ssize_t sys_fgetxattr (int filedes, const char *name, void *value, size_t size)
+{
+#if defined(HAVE_FGETXATTR)
+ return fgetxattr(filedes, name, value, size);
+#elif defined(HAVE_ATTR_GETF)
+ int retval, flags = 0;
+ int valuelength = (int)size;
+ char *attrname = strchr(name,'.') +1;
+
+ if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
+
+ retval = attr_getf(filedes, attrname, (char *)value, &valuelength, flags);
+
+ return retval ? retval : valuelength;
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+#if defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
+static char attr_buffer[ATTR_MAX_VALUELEN];
+
+static ssize_t irix_attr_list(const char *path, int filedes, char *list, size_t size, int flags)
+{
+ int retval = 0, index;
+ attrlist_cursor_t *cursor = 0;
+ int total_size = 0;
+ attrlist_t * al = (attrlist_t *)attr_buffer;
+ attrlist_ent_t *ae;
+ size_t ent_size, left = size;
+ char *bp = list;
+
+ while (True) {
+ if (filedes)
+ retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
+ else
+ retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
+ if (retval) break;
+ for (index = 0; index < al->al_count; index++) {
+ ae = ATTR_ENTRY(attr_buffer, index);
+ ent_size = strlen(ae->a_name) + sizeof("user.");
+ if (left >= ent_size) {
+ strncpy(bp, "user.", sizeof("user."));
+ strncat(bp, ae->a_name, ent_size - sizeof("user."));
+ bp += ent_size;
+ left -= ent_size;
+ } else if (size) {
+ errno = ERANGE;
+ retval = -1;
+ break;
+ }
+ total_size += ent_size;
+ }
+ if (al->al_more == 0) break;
+ }
+ if (retval == 0) {
+ flags |= ATTR_ROOT;
+ cursor = 0;
+ while (True) {
+ if (filedes)
+ retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
+ else
+ retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
+ if (retval) break;
+ for (index = 0; index < al->al_count; index++) {
+ ae = ATTR_ENTRY(attr_buffer, index);
+ ent_size = strlen(ae->a_name) + sizeof("system.");
+ if (left >= ent_size) {
+ strncpy(bp, "system.", sizeof("system."));
+ strncat(bp, ae->a_name, ent_size - sizeof("system."));
+ bp += ent_size;
+ left -= ent_size;
+ } else if (size) {
+ errno = ERANGE;
+ retval = -1;
+ break;
+ }
+ total_size += ent_size;
+ }
+ if (al->al_more == 0) break;
+ }
+ }
+ return (ssize_t)(retval ? retval : total_size);
+}
+
+#endif
+
+ssize_t sys_listxattr (const char *path, char *list, size_t size)
+{
+#if defined(HAVE_LISTXATTR)
+ return listxattr(path, list, size);
+#elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
+ return irix_attr_list(path, 0, list, size, 0);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+ssize_t sys_llistxattr (const char *path, char *list, size_t size)
+{
+#if defined(HAVE_LLISTXATTR)
+ return llistxattr(path, list, size);
+#elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
+ return irix_attr_list(path, 0, list, size, ATTR_DONTFOLLOW);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+ssize_t sys_flistxattr (int filedes, char *list, size_t size)
+{
+#if defined(HAVE_FLISTXATTR)
+ return flistxattr(filedes, list, size);
+#elif defined(HAVE_ATTR_LISTF)
+ return irix_attr_list(NULL, filedes, list, size, 0);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+int sys_removexattr (const char *path, const char *name)
+{
+#if defined(HAVE_REMOVEXATTR)
+ return removexattr(path, name);
+#elif defined(HAVE_ATTR_REMOVE)
+ int flags = 0;
+ char *attrname = strchr(name,'.') +1;
+
+ if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
+
+ return attr_remove(path, attrname, flags);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+int sys_lremovexattr (const char *path, const char *name)
+{
+#if defined(HAVE_LREMOVEXATTR)
+ return lremovexattr(path, name);
+#elif defined(HAVE_ATTR_REMOVE)
+ int flags = ATTR_DONTFOLLOW;
+ char *attrname = strchr(name,'.') +1;
+
+ if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
+
+ return attr_remove(path, attrname, flags);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+int sys_fremovexattr (int filedes, const char *name)
+{
+#if defined(HAVE_FREMOVEXATTR)
+ return fremovexattr(filedes, name);
+#elif defined(HAVE_ATTR_REMOVEF)
+ int flags = 0;
+ char *attrname = strchr(name,'.') +1;
+
+ if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
+
+ return attr_removef(filedes, attrname, flags);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+#if !defined(HAVE_SETXATTR)
+#define XATTR_CREATE 0x1 /* set value, fail if attr already exists */
+#define XATTR_REPLACE 0x2 /* set value, fail if attr does not exist */
+#endif
+
+int sys_setxattr (const char *path, const char *name, const void *value, size_t size, int flags)
+{
+#if defined(HAVE_SETXATTR)
+ return setxattr(path, name, value, size, flags);
+#elif defined(HAVE_ATTR_SET)
+ int myflags = 0;
+ char *attrname = strchr(name,'.') +1;
+
+ if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
+ if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
+ if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
+
+ return attr_set(path, attrname, (const char *)value, size, myflags);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+int sys_lsetxattr (const char *path, const char *name, const void *value, size_t size, int flags)
+{
+#if defined(HAVE_LSETXATTR)
+ return lsetxattr(path, name, value, size, flags);
+#elif defined(HAVE_ATTR_SET)
+ int myflags = ATTR_DONTFOLLOW;
+ char *attrname = strchr(name,'.') +1;
+
+ if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
+ if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
+ if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
+
+ return attr_set(path, attrname, (const char *)value, size, myflags);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+int sys_fsetxattr (int filedes, const char *name, const void *value, size_t size, int flags)
+{
+#if defined(HAVE_FSETXATTR)
+ return fsetxattr(filedes, name, value, size, flags);
+#elif defined(HAVE_ATTR_SETF)
+ int myflags = 0;
+ char *attrname = strchr(name,'.') +1;
+
+ if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
+ if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
+ if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
+
+ return attr_setf(filedes, attrname, (const char *)value, size, myflags);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
diff --git a/source/lib/system_smbd.c b/source/lib/system_smbd.c
new file mode 100644
index 00000000000..73c910e631d
--- /dev/null
+++ b/source/lib/system_smbd.c
@@ -0,0 +1,137 @@
+/*
+ Unix SMB/CIFS implementation.
+ system call wrapper interface.
+ Copyright (C) Andrew Tridgell 2002
+ Copyright (C) Andrew Barteltt 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ This file may assume linkage with smbd - for things like become_root()
+ etc.
+*/
+
+#include "includes.h"
+
+#ifndef HAVE_GETGROUPLIST
+/*
+ This is a *much* faster way of getting the list of groups for a user
+ without changing the current supplemenrary group list. The old
+ method used getgrent() which could take 20 minutes on a really big
+ network with hundeds of thousands of groups and users. The new method
+ takes a couple of seconds.
+
+ NOTE!! this function only works if it is called as root!
+ */
+static int getgrouplist_internals(const char *user, gid_t gid, gid_t *groups, int *grpcnt)
+{
+ gid_t *gids_saved;
+ int ret, ngrp_saved, num_gids;
+
+ if (non_root_mode()) {
+ *grpcnt = 0;
+ return 0;
+ }
+
+ /* work out how many groups we need to save */
+ ngrp_saved = getgroups(0, NULL);
+ if (ngrp_saved == -1) {
+ /* this shouldn't happen */
+ return -1;
+ }
+
+ gids_saved = (gid_t *)malloc(sizeof(gid_t) * (ngrp_saved+1));
+ if (!gids_saved) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ ngrp_saved = getgroups(ngrp_saved, gids_saved);
+ if (ngrp_saved == -1) {
+ SAFE_FREE(gids_saved);
+ /* very strange! */
+ return -1;
+ }
+
+ if (initgroups(user, gid) != 0) {
+ DEBUG(0, ("getgrouplist_internals: initgroups() failed!\n"));
+ SAFE_FREE(gids_saved);
+ return -1;
+ }
+
+ /* this must be done to cope with systems that put the current egid in the
+ return from getgroups() */
+ save_re_gid();
+ set_effective_gid(gid);
+ setgid(gid);
+
+ num_gids = getgroups(0, NULL);
+ if (num_gids + 1 > *grpcnt) {
+ *grpcnt = num_gids + 1;
+ ret = -1;
+ } else {
+ ret = getgroups(*grpcnt - 1, &groups[1]);
+ if (ret >= 0) {
+ groups[0] = gid;
+ *grpcnt = ret + 1;
+ }
+ }
+
+ restore_re_gid();
+
+ if (sys_setgroups(ngrp_saved, gids_saved) != 0) {
+ /* yikes! */
+ DEBUG(0,("ERROR: getgrouplist: failed to reset group list!\n"));
+ smb_panic("getgrouplist: failed to reset group list!\n");
+ free(gids_saved);
+ return -1;
+ }
+
+ free(gids_saved);
+ return ret;
+}
+#endif
+
+int sys_getgrouplist(const char *user, gid_t gid, gid_t *groups, int *grpcnt)
+{
+ char *p;
+ int retval;
+
+ DEBUG(10,("sys_getgrouplist: user [%s]\n", user));
+
+ /* see if we should disable winbindd lookups for local users */
+ if ( (p = strchr(user, *lp_winbind_separator())) == NULL ) {
+ if ( !winbind_off() )
+ DEBUG(0,("sys_getgroup_list: Insufficient environment space for %s\n",
+ WINBINDD_DONT_ENV));
+ else
+ DEBUG(10,("sys_getgrouplist(): disabled winbindd for group lookup [user == %s]\n",
+ user));
+ }
+
+#ifdef HAVE_GETGROUPLIST
+ retval = getgrouplist(user, gid, groups, grpcnt);
+#else
+ become_root();
+ retval = getgrouplist_internals(user, gid, groups, grpcnt);
+ unbecome_root();
+#endif
+
+ /* allow winbindd lookups */
+ winbind_on();
+
+ return retval;
+}
diff --git a/source/lib/talloc.c b/source/lib/talloc.c
new file mode 100644
index 00000000000..485dc28f31d
--- /dev/null
+++ b/source/lib/talloc.c
@@ -0,0 +1,449 @@
+/*
+ Samba Unix SMB/CIFS implementation.
+ Samba temporary memory allocation functions
+ Copyright (C) Andrew Tridgell 2000
+ Copyright (C) 2001, 2002 by Martin Pool <mbp@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/**
+ @defgroup talloc Simple memory allocator
+ @{
+
+ This is a very simple temporary memory allocator. To use it do the following:
+
+ 1) when you first want to allocate a pool of meomry use
+ talloc_init() and save the resulting context pointer somewhere
+
+ 2) to allocate memory use talloc()
+
+ 3) when _all_ of the memory allocated using this context is no longer needed
+ use talloc_destroy()
+
+ talloc does not zero the memory. It guarantees memory of a
+ TALLOC_ALIGN alignment
+
+ @sa talloc.h
+*/
+
+/**
+ * @todo We could allocate both the talloc_chunk structure, and the
+ * memory it contains all in one allocation, which might be a bit
+ * faster and perhaps use less memory overhead.
+ *
+ * That smells like a premature optimization, though. -- mbp
+ **/
+
+/**
+ * If you want testing for memory corruption, link with dmalloc or use
+ * Insure++. It doesn't seem useful to duplicate them here.
+ **/
+
+#include "includes.h"
+
+
+/**
+ * Start of linked list of all talloc pools.
+ *
+ * @todo We should turn the global list off when using Insure++,
+ * otherwise all the memory will be seen as still reachable.
+ **/
+static TALLOC_CTX *list_head = NULL;
+
+
+/**
+ * Add to the global list
+ **/
+static void talloc_enroll(TALLOC_CTX *t)
+{
+ t->next_ctx = list_head;
+ list_head = t;
+}
+
+
+static void talloc_disenroll(TALLOC_CTX *t)
+{
+ TALLOC_CTX **ttmp;
+
+ /* Use a double-* so that no special case is required for the
+ * list head. */
+ for (ttmp = &list_head; *ttmp; ttmp = &((*ttmp)->next_ctx))
+ if (*ttmp == t) {
+ /* ttmp is the link that points to t, either
+ * list_head or the next_ctx link in its
+ * predecessor */
+ *ttmp = t->next_ctx;
+ t->next_ctx = NULL; /* clobber */
+ return;
+ }
+ abort(); /* oops, this talloc was already
+ * clobbered or something else went
+ * wrong. */
+}
+
+
+/** Create a new talloc context. **/
+static TALLOC_CTX *talloc_init_internal(void)
+{
+ TALLOC_CTX *t;
+
+ t = (TALLOC_CTX *)malloc(sizeof(TALLOC_CTX));
+ if (t) {
+ t->list = NULL;
+ t->total_alloc_size = 0;
+ t->name = NULL;
+ talloc_enroll(t);
+ }
+
+ return t;
+}
+
+
+
+/**
+ * Create a new talloc context, with a name specifying its purpose.
+ **/
+
+ TALLOC_CTX *talloc_init(char const *fmt, ...)
+{
+ TALLOC_CTX *t;
+ va_list ap;
+
+ t = talloc_init_internal();
+ if (t && fmt) {
+ /*
+ * t->name must not be talloced.
+ * as destroying the pool would destroy it. JRA.
+ */
+ t->name = NULL;
+ va_start(ap, fmt);
+ vasprintf(&t->name, fmt, ap);
+ va_end(ap);
+ if (!t->name) {
+ talloc_destroy(t);
+ t = NULL;
+ }
+ }
+
+ return t;
+}
+
+
+/** Allocate a bit of memory from the specified pool **/
+void *talloc(TALLOC_CTX *t, size_t size)
+{
+ void *p;
+ struct talloc_chunk *tc;
+
+ if (!t || size == 0) return NULL;
+
+ p = malloc(size);
+ if (p) {
+ tc = malloc(sizeof(*tc));
+ if (tc) {
+ tc->ptr = p;
+ tc->size = size;
+ tc->next = t->list;
+ t->list = tc;
+ t->total_alloc_size += size;
+ }
+ else {
+ SAFE_FREE(p);
+ }
+ }
+ return p;
+}
+
+/** A talloc version of realloc */
+void *talloc_realloc(TALLOC_CTX *t, void *ptr, size_t size)
+{
+ struct talloc_chunk *tc;
+ void *new_ptr;
+
+ /* size zero is equivalent to free() */
+ if (!t || size == 0)
+ return NULL;
+
+ /* realloc(NULL) is equavalent to malloc() */
+ if (ptr == NULL)
+ return talloc(t, size);
+
+ for (tc=t->list; tc; tc=tc->next) {
+ if (tc->ptr == ptr) {
+ new_ptr = Realloc(ptr, size);
+ if (new_ptr) {
+ t->total_alloc_size += (size - tc->size);
+ tc->size = size;
+ tc->ptr = new_ptr;
+ }
+ return new_ptr;
+ }
+ }
+ return NULL;
+}
+
+/** Destroy all the memory allocated inside @p t, but not @p t
+ * itself. */
+void talloc_destroy_pool(TALLOC_CTX *t)
+{
+ struct talloc_chunk *c;
+
+ if (!t)
+ return;
+
+ while (t->list) {
+ c = t->list->next;
+ SAFE_FREE(t->list->ptr);
+ SAFE_FREE(t->list);
+ t->list = c;
+ }
+
+ t->total_alloc_size = 0;
+}
+
+/** Destroy a whole pool including the context */
+void talloc_destroy(TALLOC_CTX *t)
+{
+ if (!t)
+ return;
+
+ talloc_destroy_pool(t);
+ talloc_disenroll(t);
+ SAFE_FREE(t->name);
+ memset(t, 0, sizeof(TALLOC_CTX));
+ SAFE_FREE(t);
+}
+
+/** Return the current total size of the pool. */
+size_t talloc_pool_size(TALLOC_CTX *t)
+{
+ if (t)
+ return t->total_alloc_size;
+ else
+ return 0;
+}
+
+const char * talloc_pool_name(TALLOC_CTX const *t)
+{
+ if (t)
+ return t->name;
+ else
+ return NULL;
+}
+
+
+/** talloc and zero memory. */
+void *talloc_zero(TALLOC_CTX *t, size_t size)
+{
+ void *p = talloc(t, size);
+
+ if (p)
+ memset(p, '\0', size);
+
+ return p;
+}
+
+/** memdup with a talloc. */
+void *talloc_memdup(TALLOC_CTX *t, const void *p, size_t size)
+{
+ void *newp = talloc(t,size);
+
+ if (newp)
+ memcpy(newp, p, size);
+
+ return newp;
+}
+
+/** strdup with a talloc */
+char *talloc_strdup(TALLOC_CTX *t, const char *p)
+{
+ if (p)
+ return talloc_memdup(t, p, strlen(p) + 1);
+ else
+ return NULL;
+}
+
+/** strdup_w with a talloc */
+smb_ucs2_t *talloc_strdup_w(TALLOC_CTX *t, const smb_ucs2_t *p)
+{
+ if (p)
+ return talloc_memdup(t, p, (strlen_w(p) + 1) * sizeof(smb_ucs2_t));
+ else
+ return NULL;
+}
+
+/**
+ * Perform string formatting, and return a pointer to newly allocated
+ * memory holding the result, inside a memory pool.
+ **/
+ char *talloc_asprintf(TALLOC_CTX *t, const char *fmt, ...)
+{
+ va_list ap;
+ char *ret;
+
+ va_start(ap, fmt);
+ ret = talloc_vasprintf(t, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+
+ char *talloc_vasprintf(TALLOC_CTX *t, const char *fmt, va_list ap)
+{
+ int len;
+ char *ret;
+ va_list ap2;
+
+ VA_COPY(ap2, ap);
+
+ len = vsnprintf(NULL, 0, fmt, ap2);
+
+ ret = talloc(t, len+1);
+ if (ret) {
+ VA_COPY(ap2, ap);
+ vsnprintf(ret, len+1, fmt, ap2);
+ }
+
+ return ret;
+}
+
+
+/**
+ * Realloc @p s to append the formatted result of @p fmt and return @p
+ * s, which may have moved. Good for gradually accumulating output
+ * into a string buffer.
+ **/
+ char *talloc_asprintf_append(TALLOC_CTX *t, char *s,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ s = talloc_vasprintf_append(t, s, fmt, ap);
+ va_end(ap);
+ return s;
+}
+
+
+
+/**
+ * Realloc @p s to append the formatted result of @p fmt and @p ap,
+ * and return @p s, which may have moved. Good for gradually
+ * accumulating output into a string buffer.
+ **/
+ char *talloc_vasprintf_append(TALLOC_CTX *t, char *s,
+ const char *fmt, va_list ap)
+{
+ int len, s_len;
+ va_list ap2;
+
+ VA_COPY(ap2, ap);
+
+ s_len = strlen(s);
+ len = vsnprintf(NULL, 0, fmt, ap2);
+
+ s = talloc_realloc(t, s, s_len + len+1);
+ if (!s) return NULL;
+
+ VA_COPY(ap2, ap);
+
+ vsnprintf(s+s_len, len+1, fmt, ap2);
+
+ return s;
+}
+
+
+/**
+ * Return a human-readable description of all talloc memory usage.
+ * The result is allocated from @p t.
+ **/
+char *talloc_describe_all(TALLOC_CTX *rt)
+{
+ int n_pools = 0, total_chunks = 0;
+ size_t total_bytes = 0;
+ TALLOC_CTX *it;
+ char *s;
+
+ if (!rt) return NULL;
+
+ s = talloc_asprintf(rt, "global talloc allocations in pid: %u\n",
+ (unsigned) sys_getpid());
+ s = talloc_asprintf_append(rt, s, "%-40s %8s %8s\n",
+ "name", "chunks", "bytes");
+ s = talloc_asprintf_append(rt, s, "%-40s %8s %8s\n",
+ "----------------------------------------",
+ "--------",
+ "--------");
+
+ for (it = list_head; it; it = it->next_ctx) {
+ size_t bytes;
+ int n_chunks;
+ fstring what;
+
+ n_pools++;
+
+ talloc_get_allocation(it, &bytes, &n_chunks);
+
+ if (it->name)
+ fstrcpy(what, it->name);
+ else
+ slprintf(what, sizeof(what), "@%p", it);
+
+ s = talloc_asprintf_append(rt, s, "%-40s %8u %8u\n",
+ what,
+ (unsigned) n_chunks,
+ (unsigned) bytes);
+ total_bytes += bytes;
+ total_chunks += n_chunks;
+ }
+
+ s = talloc_asprintf_append(rt, s, "%-40s %8s %8s\n",
+ "----------------------------------------",
+ "--------",
+ "--------");
+
+ s = talloc_asprintf_append(rt, s, "%-40s %8u %8u\n",
+ "TOTAL",
+ (unsigned) total_chunks, (unsigned) total_bytes);
+
+ return s;
+}
+
+
+
+/**
+ * Return an estimated memory usage for the specified pool. This does
+ * not include memory used by the underlying malloc implementation.
+ **/
+void talloc_get_allocation(TALLOC_CTX *t,
+ size_t *total_bytes,
+ int *n_chunks)
+{
+ struct talloc_chunk *chunk;
+
+ if (t) {
+ *total_bytes = 0;
+ *n_chunks = 0;
+
+ for (chunk = t->list; chunk; chunk = chunk->next) {
+ n_chunks[0]++;
+ *total_bytes += chunk->size;
+ }
+ }
+}
+
+
+/** @} */
diff --git a/source/lib/tallocmsg.c b/source/lib/tallocmsg.c
new file mode 100644
index 00000000000..bbe1ee60a46
--- /dev/null
+++ b/source/lib/tallocmsg.c
@@ -0,0 +1,58 @@
+/*
+ samba -- Unix SMB/CIFS implementation.
+ Copyright (C) 2001, 2002 by Martin Pool
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/**
+ * @file tallocmsg.c
+ *
+ * Glue code between talloc profiling and the Samba messaging system.
+ **/
+
+
+/**
+ * Respond to a POOL_USAGE message by sending back string form of memory
+ * usage stats.
+ **/
+void msg_pool_usage(int msg_type, pid_t src_pid,
+ void *UNUSED(buf), size_t UNUSED(len))
+{
+ char *reply;
+ TALLOC_CTX *reply_pool = talloc_init("msg_pool_usage");
+
+ SMB_ASSERT(msg_type == MSG_REQ_POOL_USAGE);
+
+ DEBUG(2,("Got POOL_USAGE\n"));
+
+ reply = talloc_describe_all(reply_pool);
+
+ message_send_pid(src_pid, MSG_POOL_USAGE,
+ reply, strlen(reply)+1, True);
+
+ talloc_destroy(reply_pool);
+}
+
+/**
+ * Register handler for MSG_REQ_POOL_USAGE
+ **/
+void register_msg_pool_usage(void)
+{
+ message_register(MSG_REQ_POOL_USAGE, msg_pool_usage);
+ DEBUG(2, ("Registered MSG_REQ_POOL_USAGE\n"));
+}
diff --git a/source/lib/talloctort.c b/source/lib/talloctort.c
new file mode 100644
index 00000000000..0cdf693bb91
--- /dev/null
+++ b/source/lib/talloctort.c
@@ -0,0 +1,65 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba temporary memory allocation functions -- torturer
+ Copyright (C) 2001 by Martin Pool <mbp@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#define NCTX 10
+#define NOBJ 20
+
+int main(void)
+{
+ int i;
+ TALLOC_CTX *ctx[NCTX];
+
+ for (i = 0; i < NCTX; i++) {
+ ctx[i] = talloc_init("torture(%d)", i);
+ }
+
+ for (i = 0; i < NCTX; i++) {
+ int j;
+ for (j = 0; j < NOBJ; j++) {
+ char *p;
+ size_t size = 1<<(i/3+j);
+
+ p = talloc(ctx[i], size);
+ if (!p) {
+ fprintf(stderr,
+ "failed to talloc %.0f bytes\n",
+ (double) size);
+ exit(1);
+ }
+
+ memset(p, 'A' + j, size);
+ }
+ }
+
+ for (i = 0; i < NCTX; i++) {
+ printf("talloc@%p %-40s %ldkB\n", ctx[i],
+ talloc_pool_name(ctx[i]),
+ (unsigned long)talloc_pool_size(ctx[i]) >> 10);
+ }
+
+ printf("%s", talloc_describe_all(ctx[0]));
+
+ for (i = NCTX - 1; i >= 0; i--)
+ talloc_destroy(ctx[i]);
+
+ return 0;
+}
diff --git a/source/lib/time.c b/source/lib/time.c
new file mode 100644
index 00000000000..faca2cba879
--- /dev/null
+++ b/source/lib/time.c
@@ -0,0 +1,756 @@
+/*
+ Unix SMB/CIFS implementation.
+ time handling functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Stefan (metze) Metzmacher 2002
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*
+ This stuff was largely rewritten by Paul Eggert <eggert@twinsun.com>
+ in May 1996
+ */
+
+
+int extra_time_offset = 0;
+
+#ifndef CHAR_BIT
+#define CHAR_BIT 8
+#endif
+
+#ifndef TIME_T_MIN
+#define TIME_T_MIN ((time_t)0 < (time_t) -1 ? (time_t) 0 \
+ : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1))
+#endif
+#ifndef TIME_T_MAX
+#define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
+#endif
+
+void get_nttime_max(NTTIME *t)
+{
+ /* FIXME: This is incorrect */
+ unix_to_nt_time(t, get_time_t_max());
+}
+
+/*******************************************************************
+ External access to time_t_min and time_t_max.
+********************************************************************/
+
+time_t get_time_t_max(void)
+{
+ return TIME_T_MAX;
+}
+
+/*******************************************************************
+a gettimeofday wrapper
+********************************************************************/
+void GetTimeOfDay(struct timeval *tval)
+{
+#ifdef HAVE_GETTIMEOFDAY_TZ
+ gettimeofday(tval,NULL);
+#else
+ gettimeofday(tval);
+#endif
+}
+
+#define TM_YEAR_BASE 1900
+
+/*******************************************************************
+yield the difference between *A and *B, in seconds, ignoring leap seconds
+********************************************************************/
+static int tm_diff(struct tm *a, struct tm *b)
+{
+ int ay = a->tm_year + (TM_YEAR_BASE - 1);
+ int by = b->tm_year + (TM_YEAR_BASE - 1);
+ int intervening_leap_days =
+ (ay/4 - by/4) - (ay/100 - by/100) + (ay/400 - by/400);
+ int years = ay - by;
+ int days = 365*years + intervening_leap_days + (a->tm_yday - b->tm_yday);
+ int hours = 24*days + (a->tm_hour - b->tm_hour);
+ int minutes = 60*hours + (a->tm_min - b->tm_min);
+ int seconds = 60*minutes + (a->tm_sec - b->tm_sec);
+
+ return seconds;
+}
+
+/*******************************************************************
+ return the UTC offset in seconds west of UTC, or 0 if it cannot be determined
+ ******************************************************************/
+static int TimeZone(time_t t)
+{
+ struct tm *tm = gmtime(&t);
+ struct tm tm_utc;
+ if (!tm)
+ return 0;
+ tm_utc = *tm;
+ tm = localtime(&t);
+ if (!tm)
+ return 0;
+ return tm_diff(&tm_utc,tm);
+
+}
+
+static BOOL done_serverzone_init;
+
+/* Return the smb serverzone value */
+
+static int get_serverzone(void)
+{
+ static int serverzone;
+
+ if (!done_serverzone_init) {
+ serverzone = TimeZone(time(NULL));
+
+ if ((serverzone % 60) != 0) {
+ DEBUG(1,("WARNING: Your timezone is not a multiple of 1 minute.\n"));
+ }
+
+ DEBUG(4,("Serverzone is %d\n",serverzone));
+
+ done_serverzone_init = True;
+ }
+
+ return serverzone;
+}
+
+/* Re-read the smb serverzone value */
+
+static struct timeval start_time_hires;
+
+void TimeInit(void)
+{
+ done_serverzone_init = False;
+ get_serverzone();
+ /* Save the start time of this process. */
+ if (start_time_hires.tv_sec == 0 && start_time_hires.tv_usec == 0)
+ GetTimeOfDay(&start_time_hires);
+}
+
+/**********************************************************************
+ Return a timeval struct of the uptime of this process. As TimeInit is
+ done before a daemon fork then this is the start time from the parent
+ daemon start. JRA.
+***********************************************************************/
+
+void get_process_uptime(struct timeval *ret_time)
+{
+ struct timeval time_now_hires;
+
+ GetTimeOfDay(&time_now_hires);
+ ret_time->tv_sec = time_now_hires.tv_sec - start_time_hires.tv_sec;
+ ret_time->tv_usec = time_now_hires.tv_usec - start_time_hires.tv_usec;
+ if (time_now_hires.tv_usec < start_time_hires.tv_usec) {
+ ret_time->tv_sec -= 1;
+ ret_time->tv_usec = 1000000 + (time_now_hires.tv_usec - start_time_hires.tv_usec);
+ } else
+ ret_time->tv_usec = time_now_hires.tv_usec - start_time_hires.tv_usec;
+}
+
+/*******************************************************************
+return the same value as TimeZone, but it should be more efficient.
+
+We keep a table of DST offsets to prevent calling localtime() on each
+call of this function. This saves a LOT of time on many unixes.
+
+Updated by Paul Eggert <eggert@twinsun.com>
+********************************************************************/
+static int TimeZoneFaster(time_t t)
+{
+ static struct dst_table {time_t start,end; int zone;} *tdt, *dst_table = NULL;
+ static int table_size = 0;
+ int i;
+ int zone = 0;
+
+ if (t == 0) t = time(NULL);
+
+ /* Tunis has a 8 day DST region, we need to be careful ... */
+#define MAX_DST_WIDTH (365*24*60*60)
+#define MAX_DST_SKIP (7*24*60*60)
+
+ for (i=0;i<table_size;i++)
+ if (t >= dst_table[i].start && t <= dst_table[i].end) break;
+
+ if (i<table_size) {
+ zone = dst_table[i].zone;
+ } else {
+ time_t low,high;
+
+ zone = TimeZone(t);
+ tdt = (struct dst_table *)Realloc(dst_table,
+ sizeof(dst_table[0])*(i+1));
+ if (!tdt) {
+ DEBUG(0,("TimeZoneFaster: out of memory!\n"));
+ SAFE_FREE(dst_table);
+ table_size = 0;
+ } else {
+ dst_table = tdt;
+ table_size++;
+
+ dst_table[i].zone = zone;
+ dst_table[i].start = dst_table[i].end = t;
+
+ /* no entry will cover more than 6 months */
+ low = t - MAX_DST_WIDTH/2;
+ if (t < low)
+ low = TIME_T_MIN;
+
+ high = t + MAX_DST_WIDTH/2;
+ if (high < t)
+ high = TIME_T_MAX;
+
+ /* widen the new entry using two bisection searches */
+ while (low+60*60 < dst_table[i].start) {
+ if (dst_table[i].start - low > MAX_DST_SKIP*2)
+ t = dst_table[i].start - MAX_DST_SKIP;
+ else
+ t = low + (dst_table[i].start-low)/2;
+ if (TimeZone(t) == zone)
+ dst_table[i].start = t;
+ else
+ low = t;
+ }
+
+ while (high-60*60 > dst_table[i].end) {
+ if (high - dst_table[i].end > MAX_DST_SKIP*2)
+ t = dst_table[i].end + MAX_DST_SKIP;
+ else
+ t = high - (high-dst_table[i].end)/2;
+ if (TimeZone(t) == zone)
+ dst_table[i].end = t;
+ else
+ high = t;
+ }
+#if 0
+ DEBUG(1,("Added DST entry from %s ",
+ asctime(localtime(&dst_table[i].start))));
+ DEBUG(1,("to %s (%d)\n",asctime(localtime(&dst_table[i].end)),
+ dst_table[i].zone));
+#endif
+ }
+ }
+ return zone;
+}
+
+/****************************************************************************
+ return the UTC offset in seconds west of UTC, adjusted for extra time offset
+ **************************************************************************/
+int TimeDiff(time_t t)
+{
+ return TimeZoneFaster(t) + 60*extra_time_offset;
+}
+
+
+/****************************************************************************
+ return the UTC offset in seconds west of UTC, adjusted for extra time
+ offset, for a local time value. If ut = lt + LocTimeDiff(lt), then
+ lt = ut - TimeDiff(ut), but the converse does not necessarily hold near
+ daylight savings transitions because some local times are ambiguous.
+ LocTimeDiff(t) equals TimeDiff(t) except near daylight savings transitions.
+ +**************************************************************************/
+static int LocTimeDiff(time_t lte)
+{
+ time_t lt = lte - 60*extra_time_offset;
+ int d = TimeZoneFaster(lt);
+ time_t t = lt + d;
+
+ /* if overflow occurred, ignore all the adjustments so far */
+ if (((lte < lt) ^ (extra_time_offset < 0)) | ((t < lt) ^ (d < 0)))
+ t = lte;
+
+ /* now t should be close enough to the true UTC to yield the right answer */
+ return TimeDiff(t);
+}
+
+
+/****************************************************************************
+try to optimise the localtime call, it can be quite expensive on some machines
+****************************************************************************/
+struct tm *LocalTime(time_t *t)
+{
+ time_t t2 = *t;
+
+ t2 -= TimeDiff(t2);
+
+ return(gmtime(&t2));
+}
+
+#define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
+
+/****************************************************************************
+interpret an 8 byte "filetime" structure to a time_t
+It's originally in "100ns units since jan 1st 1601"
+
+It appears to be kludge-GMT (at least for file listings). This means
+its the GMT you get by taking a localtime and adding the
+serverzone. This is NOT the same as GMT in some cases. This routine
+converts this to real GMT.
+****************************************************************************/
+time_t nt_time_to_unix(NTTIME *nt)
+{
+ double d;
+ time_t ret;
+ /* The next two lines are a fix needed for the
+ broken SCO compiler. JRA. */
+ time_t l_time_min = TIME_T_MIN;
+ time_t l_time_max = TIME_T_MAX;
+
+ if (nt->high == 0 || (nt->high == 0xffffffff && nt->low == 0xffffffff))
+ return(0);
+
+ d = ((double)nt->high)*4.0*(double)(1<<30);
+ d += (nt->low&0xFFF00000);
+ d *= 1.0e-7;
+
+ /* now adjust by 369 years to make the secs since 1970 */
+ d -= TIME_FIXUP_CONSTANT;
+
+ if (d <= l_time_min)
+ return (l_time_min);
+
+ if (d >= l_time_max)
+ return (l_time_max);
+
+ ret = (time_t)(d+0.5);
+
+ /* this takes us from kludge-GMT to real GMT */
+ ret -= get_serverzone();
+ ret += LocTimeDiff(ret);
+
+ return(ret);
+}
+
+/****************************************************************************
+ Convert a NTTIME structure to a time_t.
+ It's originally in "100ns units".
+
+ This is an absolute version of the one above.
+ By absolute I mean, it doesn't adjust from 1/1/1601 to 1/1/1970
+ if the NTTIME was 5 seconds, the time_t is 5 seconds. JFM
+****************************************************************************/
+
+time_t nt_time_to_unix_abs(NTTIME *nt)
+{
+ double d;
+ time_t ret;
+ /* The next two lines are a fix needed for the
+ broken SCO compiler. JRA. */
+ time_t l_time_min = TIME_T_MIN;
+ time_t l_time_max = TIME_T_MAX;
+
+ if (nt->high == 0)
+ return(0);
+
+ if (nt->high==0x80000000 && nt->low==0)
+ return -1;
+
+ /* reverse the time */
+ /* it's a negative value, turn it to positive */
+ nt->high=~nt->high;
+ nt->low=~nt->low;
+
+ d = ((double)nt->high)*4.0*(double)(1<<30);
+ d += (nt->low&0xFFF00000);
+ d *= 1.0e-7;
+
+ if (!(l_time_min <= d && d <= l_time_max))
+ return(0);
+
+ ret = (time_t)(d+0.5);
+
+ return(ret);
+}
+
+/****************************************************************************
+interprets an nt time into a unix time_t
+****************************************************************************/
+time_t interpret_long_date(char *p)
+{
+ NTTIME nt;
+ nt.low = IVAL(p,0);
+ nt.high = IVAL(p,4);
+ return nt_time_to_unix(&nt);
+}
+
+/****************************************************************************
+put a 8 byte filetime from a time_t
+This takes real GMT as input and converts to kludge-GMT
+****************************************************************************/
+void unix_to_nt_time(NTTIME *nt, time_t t)
+{
+ double d;
+
+ if (t==0)
+ {
+ nt->low = 0;
+ nt->high = 0;
+ return;
+ }
+ if (t == TIME_T_MAX)
+ {
+ nt->low = 0xffffffff;
+ nt->high = 0x7fffffff;
+ return;
+ }
+ if (t == -1)
+ {
+ nt->low = 0xffffffff;
+ nt->high = 0xffffffff;
+ return;
+ }
+
+ /* this converts GMT to kludge-GMT */
+ t -= TimeDiff(t) - get_serverzone();
+
+ d = (double)(t);
+ d += TIME_FIXUP_CONSTANT;
+ d *= 1.0e7;
+
+ nt->high = (uint32)(d * (1.0/(4.0*(double)(1<<30))));
+ nt->low = (uint32)(d - ((double)nt->high)*4.0*(double)(1<<30));
+}
+
+/****************************************************************************
+ Convert a time_t to a NTTIME structure
+
+ This is an absolute version of the one above.
+ By absolute I mean, it doesn't adjust from 1/1/1970 to 1/1/1601
+ If the nttime_t was 5 seconds, the NTTIME is 5 seconds. JFM
+****************************************************************************/
+
+void unix_to_nt_time_abs(NTTIME *nt, time_t t)
+{
+ double d;
+
+ if (t==0) {
+ nt->low = 0;
+ nt->high = 0;
+ return;
+ }
+
+ if (t == TIME_T_MAX) {
+ nt->low = 0xffffffff;
+ nt->high = 0x7fffffff;
+ return;
+ }
+
+ if (t == -1) {
+ /* that's what NT uses for infinite */
+ nt->low = 0x0;
+ nt->high = 0x80000000;
+ return;
+ }
+
+ d = (double)(t);
+ d *= 1.0e7;
+
+ nt->high = (uint32)(d * (1.0/(4.0*(double)(1<<30))));
+ nt->low = (uint32)(d - ((double)nt->high)*4.0*(double)(1<<30));
+
+ /* convert to a negative value */
+ nt->high=~nt->high;
+ nt->low=~nt->low;
+}
+
+/****************************************************************************
+take a Unix time and convert to an NTTIME structure and place in buffer
+pointed to by p.
+****************************************************************************/
+void put_long_date(char *p,time_t t)
+{
+ NTTIME nt;
+ unix_to_nt_time(&nt, t);
+ SIVAL(p, 0, nt.low);
+ SIVAL(p, 4, nt.high);
+}
+
+/****************************************************************************
+check if it's a null mtime
+****************************************************************************/
+BOOL null_mtime(time_t mtime)
+{
+ if (mtime == 0 || mtime == (time_t)0xFFFFFFFF || mtime == (time_t)-1)
+ return(True);
+ return(False);
+}
+
+/*******************************************************************
+ create a 16 bit dos packed date
+********************************************************************/
+static uint16 make_dos_date1(struct tm *t)
+{
+ uint16 ret=0;
+ ret = (((unsigned)(t->tm_mon+1)) >> 3) | ((t->tm_year-80) << 1);
+ ret = ((ret&0xFF)<<8) | (t->tm_mday | (((t->tm_mon+1) & 0x7) << 5));
+ return(ret);
+}
+
+/*******************************************************************
+ create a 16 bit dos packed time
+********************************************************************/
+static uint16 make_dos_time1(struct tm *t)
+{
+ uint16 ret=0;
+ ret = ((((unsigned)t->tm_min >> 3)&0x7) | (((unsigned)t->tm_hour) << 3));
+ ret = ((ret&0xFF)<<8) | ((t->tm_sec/2) | ((t->tm_min & 0x7) << 5));
+ return(ret);
+}
+
+/*******************************************************************
+ create a 32 bit dos packed date/time from some parameters
+ This takes a GMT time and returns a packed localtime structure
+********************************************************************/
+static uint32 make_dos_date(time_t unixdate)
+{
+ struct tm *t;
+ uint32 ret=0;
+
+ t = LocalTime(&unixdate);
+ if (!t)
+ return 0xFFFFFFFF;
+
+ ret = make_dos_date1(t);
+ ret = ((ret&0xFFFF)<<16) | make_dos_time1(t);
+
+ return(ret);
+}
+
+/*******************************************************************
+put a dos date into a buffer (time/date format)
+This takes GMT time and puts local time in the buffer
+********************************************************************/
+void put_dos_date(char *buf,int offset,time_t unixdate)
+{
+ uint32 x = make_dos_date(unixdate);
+ SIVAL(buf,offset,x);
+}
+
+/*******************************************************************
+put a dos date into a buffer (date/time format)
+This takes GMT time and puts local time in the buffer
+********************************************************************/
+void put_dos_date2(char *buf,int offset,time_t unixdate)
+{
+ uint32 x = make_dos_date(unixdate);
+ x = ((x&0xFFFF)<<16) | ((x&0xFFFF0000)>>16);
+ SIVAL(buf,offset,x);
+}
+
+/*******************************************************************
+put a dos 32 bit "unix like" date into a buffer. This routine takes
+GMT and converts it to LOCAL time before putting it (most SMBs assume
+localtime for this sort of date)
+********************************************************************/
+void put_dos_date3(char *buf,int offset,time_t unixdate)
+{
+ if (!null_mtime(unixdate))
+ unixdate -= TimeDiff(unixdate);
+ SIVAL(buf,offset,unixdate);
+}
+
+/*******************************************************************
+ interpret a 32 bit dos packed date/time to some parameters
+********************************************************************/
+static void interpret_dos_date(uint32 date,int *year,int *month,int *day,int *hour,int *minute,int *second)
+{
+ uint32 p0,p1,p2,p3;
+
+ p0=date&0xFF; p1=((date&0xFF00)>>8)&0xFF;
+ p2=((date&0xFF0000)>>16)&0xFF; p3=((date&0xFF000000)>>24)&0xFF;
+
+ *second = 2*(p0 & 0x1F);
+ *minute = ((p0>>5)&0xFF) + ((p1&0x7)<<3);
+ *hour = (p1>>3)&0xFF;
+ *day = (p2&0x1F);
+ *month = ((p2>>5)&0xFF) + ((p3&0x1)<<3) - 1;
+ *year = ((p3>>1)&0xFF) + 80;
+}
+
+/*******************************************************************
+ create a unix date (int GMT) from a dos date (which is actually in
+ localtime)
+********************************************************************/
+time_t make_unix_date(void *date_ptr)
+{
+ uint32 dos_date=0;
+ struct tm t;
+ time_t ret;
+
+ dos_date = IVAL(date_ptr,0);
+
+ if (dos_date == 0) return(0);
+
+ interpret_dos_date(dos_date,&t.tm_year,&t.tm_mon,
+ &t.tm_mday,&t.tm_hour,&t.tm_min,&t.tm_sec);
+ t.tm_isdst = -1;
+
+ /* mktime() also does the local to GMT time conversion for us */
+ ret = mktime(&t);
+
+ return(ret);
+}
+
+/*******************************************************************
+like make_unix_date() but the words are reversed
+********************************************************************/
+time_t make_unix_date2(void *date_ptr)
+{
+ uint32 x,x2;
+
+ x = IVAL(date_ptr,0);
+ x2 = ((x&0xFFFF)<<16) | ((x&0xFFFF0000)>>16);
+ SIVAL(&x,0,x2);
+
+ return(make_unix_date((void *)&x));
+}
+
+/*******************************************************************
+ create a unix GMT date from a dos date in 32 bit "unix like" format
+ these generally arrive as localtimes, with corresponding DST
+ ******************************************************************/
+time_t make_unix_date3(void *date_ptr)
+{
+ time_t t = (time_t)IVAL(date_ptr,0);
+ if (!null_mtime(t))
+ t += LocTimeDiff(t);
+ return(t);
+}
+
+
+/***************************************************************************
+return a HTTP/1.0 time string
+ ***************************************************************************/
+char *http_timestring(time_t t)
+{
+ static fstring buf;
+ struct tm *tm = LocalTime(&t);
+
+ if (!tm)
+ slprintf(buf,sizeof(buf)-1,"%ld seconds since the Epoch",(long)t);
+ else
+#ifndef HAVE_STRFTIME
+ fstrcpy(buf, asctime(tm));
+ if(buf[strlen(buf)-1] == '\n')
+ buf[strlen(buf)-1] = 0;
+#else /* !HAVE_STRFTIME */
+ strftime(buf, sizeof(buf)-1, "%a, %d %b %Y %H:%M:%S %Z", tm);
+#endif /* !HAVE_STRFTIME */
+ return buf;
+}
+
+
+
+/****************************************************************************
+ Return the date and time as a string
+****************************************************************************/
+
+char *timestring(BOOL hires)
+{
+ static fstring TimeBuf;
+ struct timeval tp;
+ time_t t;
+ struct tm *tm;
+
+ if (hires) {
+ GetTimeOfDay(&tp);
+ t = (time_t)tp.tv_sec;
+ } else {
+ t = time(NULL);
+ }
+ tm = LocalTime(&t);
+ if (!tm) {
+ if (hires) {
+ slprintf(TimeBuf,
+ sizeof(TimeBuf)-1,
+ "%ld.%06ld seconds since the Epoch",
+ (long)tp.tv_sec,
+ (long)tp.tv_usec);
+ } else {
+ slprintf(TimeBuf,
+ sizeof(TimeBuf)-1,
+ "%ld seconds since the Epoch",
+ (long)t);
+ }
+ } else {
+#ifdef HAVE_STRFTIME
+ if (hires) {
+ strftime(TimeBuf,sizeof(TimeBuf)-1,"%Y/%m/%d %H:%M:%S",tm);
+ slprintf(TimeBuf+strlen(TimeBuf),
+ sizeof(TimeBuf)-1 - strlen(TimeBuf),
+ ".%06ld",
+ (long)tp.tv_usec);
+ } else {
+ strftime(TimeBuf,sizeof(TimeBuf)-1,"%Y/%m/%d %H:%M:%S",tm);
+ }
+#else
+ if (hires) {
+ slprintf(TimeBuf,
+ sizeof(TimeBuf)-1,
+ "%s.%06ld",
+ asctime(tm),
+ (long)tp.tv_usec);
+ } else {
+ fstrcpy(TimeBuf, asctime(tm));
+ }
+#endif
+ }
+ return(TimeBuf);
+}
+
+/****************************************************************************
+ return the best approximation to a 'create time' under UNIX from a stat
+ structure.
+****************************************************************************/
+
+time_t get_create_time(SMB_STRUCT_STAT *st,BOOL fake_dirs)
+{
+ time_t ret, ret1;
+
+ if(S_ISDIR(st->st_mode) && fake_dirs)
+ return (time_t)315493200L; /* 1/1/1980 */
+
+ ret = MIN(st->st_ctime, st->st_mtime);
+ ret1 = MIN(ret, st->st_atime);
+
+ if(ret1 != (time_t)0)
+ return ret1;
+
+ /*
+ * One of ctime, mtime or atime was zero (probably atime).
+ * Just return MIN(ctime, mtime).
+ */
+ return ret;
+}
+
+/****************************************************************************
+initialise an NTTIME to -1, which means "unknown" or "don't expire"
+****************************************************************************/
+
+void init_nt_time(NTTIME *nt)
+{
+ nt->high = 0x7FFFFFFF;
+ nt->low = 0xFFFFFFFF;
+}
+
+/****************************************************************************
+check if NTTIME is 0
+****************************************************************************/
+BOOL nt_time_is_zero(NTTIME *nt)
+{
+ if(nt->high==0)
+ return True;
+ return False;
+}
diff --git a/source/lib/ufc.c b/source/lib/ufc.c
new file mode 100644
index 00000000000..ecc04d9e97c
--- /dev/null
+++ b/source/lib/ufc.c
@@ -0,0 +1,771 @@
+/*
+ This bit of code was derived from the UFC-crypt package which
+ carries the following copyright
+
+ Modified for use by Samba by Andrew Tridgell, October 1994
+
+ Note that this routine is only faster on some machines. Under Linux 1.1.51
+ libc 4.5.26 I actually found this routine to be slightly slower.
+
+ Under SunOS I found a huge speedup by using these routines
+ (a factor of 20 or so)
+
+ Warning: I've had a report from Steve Kennedy <steve@gbnet.org>
+ that this crypt routine may sometimes get the wrong answer. Only
+ use UFC_CRYT if you really need it.
+
+*/
+
+#include "includes.h"
+
+#ifndef HAVE_CRYPT
+
+/*
+ * UFC-crypt: ultra fast crypt(3) implementation
+ *
+ * Copyright (C) 1991-1998, Free Software Foundation, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * @(#)crypt_util.c 2.31 02/08/92
+ *
+ * Support routines
+ *
+ */
+
+
+#ifndef long32
+#define long32 int32
+#endif
+
+#ifndef long64
+#define long64 int64
+#endif
+
+#ifndef ufc_long
+#define ufc_long unsigned
+#endif
+
+#ifndef _UFC_64_
+#define _UFC_32_
+#endif
+
+/*
+ * Permutation done once on the 56 bit
+ * key derived from the original 8 byte ASCII key.
+ */
+static int pc1[56] = {
+ 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18,
+ 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36,
+ 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22,
+ 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4
+};
+
+/*
+ * How much to rotate each 28 bit half of the pc1 permutated
+ * 56 bit key before using pc2 to give the i' key
+ */
+static int rots[16] = {
+ 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
+};
+
+/*
+ * Permutation giving the key
+ * of the i' DES round
+ */
+static int pc2[48] = {
+ 14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10,
+ 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2,
+ 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48,
+ 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32
+};
+
+/*
+ * The E expansion table which selects
+ * bits from the 32 bit intermediate result.
+ */
+static int esel[48] = {
+ 32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9,
+ 8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17,
+ 16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25,
+ 24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1
+};
+static int e_inverse[64];
+
+/*
+ * Permutation done on the
+ * result of sbox lookups
+ */
+static int perm32[32] = {
+ 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10,
+ 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25
+};
+
+/*
+ * The sboxes
+ */
+static int sbox[8][4][16]= {
+ { { 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7 },
+ { 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8 },
+ { 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0 },
+ { 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 }
+ },
+
+ { { 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10 },
+ { 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5 },
+ { 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15 },
+ { 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 }
+ },
+
+ { { 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8 },
+ { 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1 },
+ { 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7 },
+ { 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 }
+ },
+
+ { { 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15 },
+ { 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9 },
+ { 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4 },
+ { 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 }
+ },
+
+ { { 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9 },
+ { 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6 },
+ { 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14 },
+ { 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 }
+ },
+
+ { { 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11 },
+ { 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8 },
+ { 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6 },
+ { 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 }
+ },
+
+ { { 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1 },
+ { 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6 },
+ { 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2 },
+ { 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 }
+ },
+
+ { { 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7 },
+ { 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2 },
+ { 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8 },
+ { 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 }
+ }
+};
+
+/*
+ * This is the final
+ * permutation matrix
+ */
+static int final_perm[64] = {
+ 40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31,
+ 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29,
+ 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27,
+ 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25
+};
+
+/*
+ * The 16 DES keys in BITMASK format
+ */
+#ifdef _UFC_32_
+long32 _ufc_keytab[16][2];
+#endif
+
+#ifdef _UFC_64_
+long64 _ufc_keytab[16];
+#endif
+
+
+#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
+#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
+
+/* Macro to set a bit (0..23) */
+#define BITMASK(i) ( (1<<(11-(i)%12+3)) << ((i)<12?16:0) )
+
+/*
+ * sb arrays:
+ *
+ * Workhorses of the inner loop of the DES implementation.
+ * They do sbox lookup, shifting of this value, 32 bit
+ * permutation and E permutation for the next round.
+ *
+ * Kept in 'BITMASK' format.
+ */
+
+#ifdef _UFC_32_
+long32 _ufc_sb0[8192], _ufc_sb1[8192], _ufc_sb2[8192], _ufc_sb3[8192];
+static long32 *sb[4] = {_ufc_sb0, _ufc_sb1, _ufc_sb2, _ufc_sb3};
+#endif
+
+#ifdef _UFC_64_
+long64 _ufc_sb0[4096], _ufc_sb1[4096], _ufc_sb2[4096], _ufc_sb3[4096];
+static long64 *sb[4] = {_ufc_sb0, _ufc_sb1, _ufc_sb2, _ufc_sb3};
+#endif
+
+/*
+ * eperm32tab: do 32 bit permutation and E selection
+ *
+ * The first index is the byte number in the 32 bit value to be permuted
+ * - second - is the value of this byte
+ * - third - selects the two 32 bit values
+ *
+ * The table is used and generated internally in init_des to speed it up
+ */
+static ufc_long eperm32tab[4][256][2];
+
+/*
+ * do_pc1: permform pc1 permutation in the key schedule generation.
+ *
+ * The first index is the byte number in the 8 byte ASCII key
+ * - second - - the two 28 bits halfs of the result
+ * - third - selects the 7 bits actually used of each byte
+ *
+ * The result is kept with 28 bit per 32 bit with the 4 most significant
+ * bits zero.
+ */
+static ufc_long do_pc1[8][2][128];
+
+/*
+ * do_pc2: permform pc2 permutation in the key schedule generation.
+ *
+ * The first index is the septet number in the two 28 bit intermediate values
+ * - second - - - septet values
+ *
+ * Knowledge of the structure of the pc2 permutation is used.
+ *
+ * The result is kept with 28 bit per 32 bit with the 4 most significant
+ * bits zero.
+ */
+static ufc_long do_pc2[8][128];
+
+/*
+ * efp: undo an extra e selection and do final
+ * permutation giving the DES result.
+ *
+ * Invoked 6 bit a time on two 48 bit values
+ * giving two 32 bit longs.
+ */
+static ufc_long efp[16][64][2];
+
+static unsigned char bytemask[8] = {
+ 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01
+};
+
+static ufc_long longmask[32] = {
+ 0x80000000, 0x40000000, 0x20000000, 0x10000000,
+ 0x08000000, 0x04000000, 0x02000000, 0x01000000,
+ 0x00800000, 0x00400000, 0x00200000, 0x00100000,
+ 0x00080000, 0x00040000, 0x00020000, 0x00010000,
+ 0x00008000, 0x00004000, 0x00002000, 0x00001000,
+ 0x00000800, 0x00000400, 0x00000200, 0x00000100,
+ 0x00000080, 0x00000040, 0x00000020, 0x00000010,
+ 0x00000008, 0x00000004, 0x00000002, 0x00000001
+};
+
+
+/*
+ * Silly rewrite of 'bzero'. I do so
+ * because some machines don't have
+ * bzero and some don't have memset.
+ */
+
+static void clearmem(char *start, int cnt)
+ { while(cnt--)
+ *start++ = '\0';
+ }
+
+static int initialized = 0;
+
+/* lookup a 6 bit value in sbox */
+
+#define s_lookup(i,s) sbox[(i)][(((s)>>4) & 0x2)|((s) & 0x1)][((s)>>1) & 0xf];
+
+/*
+ * Initialize unit - may be invoked directly
+ * by fcrypt users.
+ */
+
+static void ufc_init_des(void)
+ { int comes_from_bit;
+ int bit, sg;
+ ufc_long j;
+ ufc_long mask1, mask2;
+
+ /*
+ * Create the do_pc1 table used
+ * to affect pc1 permutation
+ * when generating keys
+ */
+ for(bit = 0; bit < 56; bit++) {
+ comes_from_bit = pc1[bit] - 1;
+ mask1 = bytemask[comes_from_bit % 8 + 1];
+ mask2 = longmask[bit % 28 + 4];
+ for(j = 0; j < 128; j++) {
+ if(j & mask1)
+ do_pc1[comes_from_bit / 8][bit / 28][j] |= mask2;
+ }
+ }
+
+ /*
+ * Create the do_pc2 table used
+ * to affect pc2 permutation when
+ * generating keys
+ */
+ for(bit = 0; bit < 48; bit++) {
+ comes_from_bit = pc2[bit] - 1;
+ mask1 = bytemask[comes_from_bit % 7 + 1];
+ mask2 = BITMASK(bit % 24);
+ for(j = 0; j < 128; j++) {
+ if(j & mask1)
+ do_pc2[comes_from_bit / 7][j] |= mask2;
+ }
+ }
+
+ /*
+ * Now generate the table used to do combined
+ * 32 bit permutation and e expansion
+ *
+ * We use it because we have to permute 16384 32 bit
+ * longs into 48 bit in order to initialize sb.
+ *
+ * Looping 48 rounds per permutation becomes
+ * just too slow...
+ *
+ */
+
+ clearmem((char*)eperm32tab, sizeof(eperm32tab));
+
+ for(bit = 0; bit < 48; bit++) {
+ ufc_long inner_mask1,comes_from;
+
+ comes_from = perm32[esel[bit]-1]-1;
+ inner_mask1 = bytemask[comes_from % 8];
+
+ for(j = 256; j--;) {
+ if(j & inner_mask1)
+ eperm32tab[comes_from / 8][j][bit / 24] |= BITMASK(bit % 24);
+ }
+ }
+
+ /*
+ * Create the sb tables:
+ *
+ * For each 12 bit segment of an 48 bit intermediate
+ * result, the sb table precomputes the two 4 bit
+ * values of the sbox lookups done with the two 6
+ * bit halves, shifts them to their proper place,
+ * sends them through perm32 and finally E expands
+ * them so that they are ready for the next
+ * DES round.
+ *
+ */
+ for(sg = 0; sg < 4; sg++) {
+ int j1, j2;
+ int s1, s2;
+
+ for(j1 = 0; j1 < 64; j1++) {
+ s1 = s_lookup(2 * sg, j1);
+ for(j2 = 0; j2 < 64; j2++) {
+ ufc_long to_permute, inx;
+
+ s2 = s_lookup(2 * sg + 1, j2);
+ to_permute = ((s1 << 4) | s2) << (24 - 8 * sg);
+
+#ifdef _UFC_32_
+ inx = ((j1 << 6) | j2) << 1;
+ sb[sg][inx ] = eperm32tab[0][(to_permute >> 24) & 0xff][0];
+ sb[sg][inx+1] = eperm32tab[0][(to_permute >> 24) & 0xff][1];
+ sb[sg][inx ] |= eperm32tab[1][(to_permute >> 16) & 0xff][0];
+ sb[sg][inx+1] |= eperm32tab[1][(to_permute >> 16) & 0xff][1];
+ sb[sg][inx ] |= eperm32tab[2][(to_permute >> 8) & 0xff][0];
+ sb[sg][inx+1] |= eperm32tab[2][(to_permute >> 8) & 0xff][1];
+ sb[sg][inx ] |= eperm32tab[3][(to_permute) & 0xff][0];
+ sb[sg][inx+1] |= eperm32tab[3][(to_permute) & 0xff][1];
+#endif
+#ifdef _UFC_64_
+ inx = ((j1 << 6) | j2);
+ sb[sg][inx] =
+ ((long64)eperm32tab[0][(to_permute >> 24) & 0xff][0] << 32) |
+ (long64)eperm32tab[0][(to_permute >> 24) & 0xff][1];
+ sb[sg][inx] |=
+ ((long64)eperm32tab[1][(to_permute >> 16) & 0xff][0] << 32) |
+ (long64)eperm32tab[1][(to_permute >> 16) & 0xff][1];
+ sb[sg][inx] |=
+ ((long64)eperm32tab[2][(to_permute >> 8) & 0xff][0] << 32) |
+ (long64)eperm32tab[2][(to_permute >> 8) & 0xff][1];
+ sb[sg][inx] |=
+ ((long64)eperm32tab[3][(to_permute) & 0xff][0] << 32) |
+ (long64)eperm32tab[3][(to_permute) & 0xff][1];
+#endif
+ }
+ }
+ }
+
+ /*
+ * Create an inverse matrix for esel telling
+ * where to plug out bits if undoing it
+ */
+ for(bit=48; bit--;) {
+ e_inverse[esel[bit] - 1 ] = bit;
+ e_inverse[esel[bit] - 1 + 32] = bit + 48;
+ }
+
+ /*
+ * create efp: the matrix used to
+ * undo the E expansion and effect final permutation
+ */
+ clearmem((char*)efp, sizeof efp);
+ for(bit = 0; bit < 64; bit++) {
+ int o_bit, o_long;
+ ufc_long word_value, inner_mask1, inner_mask2;
+ int comes_from_f_bit, comes_from_e_bit;
+ int comes_from_word, bit_within_word;
+
+ /* See where bit i belongs in the two 32 bit long's */
+ o_long = bit / 32; /* 0..1 */
+ o_bit = bit % 32; /* 0..31 */
+
+ /*
+ * And find a bit in the e permutated value setting this bit.
+ *
+ * Note: the e selection may have selected the same bit several
+ * times. By the initialization of e_inverse, we only look
+ * for one specific instance.
+ */
+ comes_from_f_bit = final_perm[bit] - 1; /* 0..63 */
+ comes_from_e_bit = e_inverse[comes_from_f_bit]; /* 0..95 */
+ comes_from_word = comes_from_e_bit / 6; /* 0..15 */
+ bit_within_word = comes_from_e_bit % 6; /* 0..5 */
+
+ inner_mask1 = longmask[bit_within_word + 26];
+ inner_mask2 = longmask[o_bit];
+
+ for(word_value = 64; word_value--;) {
+ if(word_value & inner_mask1)
+ efp[comes_from_word][word_value][o_long] |= inner_mask2;
+ }
+ }
+ initialized++;
+ }
+
+/*
+ * Process the elements of the sb table permuting the
+ * bits swapped in the expansion by the current salt.
+ */
+
+#ifdef _UFC_32_
+static void shuffle_sb(long32 *k, ufc_long saltbits)
+ { ufc_long j;
+ long32 x;
+ for(j=4096; j--;) {
+ x = (k[0] ^ k[1]) & (long32)saltbits;
+ *k++ ^= x;
+ *k++ ^= x;
+ }
+ }
+#endif
+
+#ifdef _UFC_64_
+static void shuffle_sb(long64 *k, ufc_long saltbits)
+ { ufc_long j;
+ long64 x;
+ for(j=4096; j--;) {
+ x = ((*k >> 32) ^ *k) & (long64)saltbits;
+ *k++ ^= (x << 32) | x;
+ }
+ }
+#endif
+
+/*
+ * Setup the unit for a new salt
+ * Hopefully we'll not see a new salt in each crypt call.
+ */
+
+static unsigned char current_salt[3] = "&&"; /* invalid value */
+static ufc_long current_saltbits = 0;
+static int direction = 0;
+
+static void setup_salt(const char *s1)
+ { ufc_long i, j, saltbits;
+ const unsigned char *s2 = (const unsigned char *)s1;
+
+ if(!initialized)
+ ufc_init_des();
+
+ if(s2[0] == current_salt[0] && s2[1] == current_salt[1])
+ return;
+ current_salt[0] = s2[0]; current_salt[1] = s2[1];
+
+ /*
+ * This is the only crypt change to DES:
+ * entries are swapped in the expansion table
+ * according to the bits set in the salt.
+ */
+ saltbits = 0;
+ for(i = 0; i < 2; i++) {
+ long c=ascii_to_bin(s2[i]);
+ if(c < 0 || c > 63)
+ c = 0;
+ for(j = 0; j < 6; j++) {
+ if((c >> j) & 0x1)
+ saltbits |= BITMASK(6 * i + j);
+ }
+ }
+
+ /*
+ * Permute the sb table values
+ * to reflect the changed e
+ * selection table
+ */
+ shuffle_sb(_ufc_sb0, current_saltbits ^ saltbits);
+ shuffle_sb(_ufc_sb1, current_saltbits ^ saltbits);
+ shuffle_sb(_ufc_sb2, current_saltbits ^ saltbits);
+ shuffle_sb(_ufc_sb3, current_saltbits ^ saltbits);
+
+ current_saltbits = saltbits;
+ }
+
+static void ufc_mk_keytab(char *key)
+ { ufc_long v1, v2, *k1;
+ int i;
+#ifdef _UFC_32_
+ long32 v, *k2 = &_ufc_keytab[0][0];
+#endif
+#ifdef _UFC_64_
+ long64 v, *k2 = &_ufc_keytab[0];
+#endif
+
+ v1 = v2 = 0; k1 = &do_pc1[0][0][0];
+ for(i = 8; i--;) {
+ v1 |= k1[*key & 0x7f]; k1 += 128;
+ v2 |= k1[*key++ & 0x7f]; k1 += 128;
+ }
+
+ for(i = 0; i < 16; i++) {
+ k1 = &do_pc2[0][0];
+
+ v1 = (v1 << rots[i]) | (v1 >> (28 - rots[i]));
+ v = k1[(v1 >> 21) & 0x7f]; k1 += 128;
+ v |= k1[(v1 >> 14) & 0x7f]; k1 += 128;
+ v |= k1[(v1 >> 7) & 0x7f]; k1 += 128;
+ v |= k1[(v1 ) & 0x7f]; k1 += 128;
+
+#ifdef _UFC_32_
+ *k2++ = v;
+ v = 0;
+#endif
+#ifdef _UFC_64_
+ v <<= 32;
+#endif
+
+ v2 = (v2 << rots[i]) | (v2 >> (28 - rots[i]));
+ v |= k1[(v2 >> 21) & 0x7f]; k1 += 128;
+ v |= k1[(v2 >> 14) & 0x7f]; k1 += 128;
+ v |= k1[(v2 >> 7) & 0x7f]; k1 += 128;
+ v |= k1[(v2 ) & 0x7f];
+
+ *k2++ = v;
+ }
+
+ direction = 0;
+ }
+
+/*
+ * Undo an extra E selection and do final permutations
+ */
+
+ufc_long *_ufc_dofinalperm(ufc_long l1, ufc_long l2, ufc_long r1, ufc_long r2)
+ { ufc_long v1, v2, x;
+ static ufc_long ary[2];
+
+ x = (l1 ^ l2) & current_saltbits; l1 ^= x; l2 ^= x;
+ x = (r1 ^ r2) & current_saltbits; r1 ^= x; r2 ^= x;
+
+ v1=v2=0; l1 >>= 3; l2 >>= 3; r1 >>= 3; r2 >>= 3;
+
+ v1 |= efp[15][ r2 & 0x3f][0]; v2 |= efp[15][ r2 & 0x3f][1];
+ v1 |= efp[14][(r2 >>= 6) & 0x3f][0]; v2 |= efp[14][ r2 & 0x3f][1];
+ v1 |= efp[13][(r2 >>= 10) & 0x3f][0]; v2 |= efp[13][ r2 & 0x3f][1];
+ v1 |= efp[12][(r2 >>= 6) & 0x3f][0]; v2 |= efp[12][ r2 & 0x3f][1];
+
+ v1 |= efp[11][ r1 & 0x3f][0]; v2 |= efp[11][ r1 & 0x3f][1];
+ v1 |= efp[10][(r1 >>= 6) & 0x3f][0]; v2 |= efp[10][ r1 & 0x3f][1];
+ v1 |= efp[ 9][(r1 >>= 10) & 0x3f][0]; v2 |= efp[ 9][ r1 & 0x3f][1];
+ v1 |= efp[ 8][(r1 >>= 6) & 0x3f][0]; v2 |= efp[ 8][ r1 & 0x3f][1];
+
+ v1 |= efp[ 7][ l2 & 0x3f][0]; v2 |= efp[ 7][ l2 & 0x3f][1];
+ v1 |= efp[ 6][(l2 >>= 6) & 0x3f][0]; v2 |= efp[ 6][ l2 & 0x3f][1];
+ v1 |= efp[ 5][(l2 >>= 10) & 0x3f][0]; v2 |= efp[ 5][ l2 & 0x3f][1];
+ v1 |= efp[ 4][(l2 >>= 6) & 0x3f][0]; v2 |= efp[ 4][ l2 & 0x3f][1];
+
+ v1 |= efp[ 3][ l1 & 0x3f][0]; v2 |= efp[ 3][ l1 & 0x3f][1];
+ v1 |= efp[ 2][(l1 >>= 6) & 0x3f][0]; v2 |= efp[ 2][ l1 & 0x3f][1];
+ v1 |= efp[ 1][(l1 >>= 10) & 0x3f][0]; v2 |= efp[ 1][ l1 & 0x3f][1];
+ v1 |= efp[ 0][(l1 >>= 6) & 0x3f][0]; v2 |= efp[ 0][ l1 & 0x3f][1];
+
+ ary[0] = v1; ary[1] = v2;
+ return ary;
+ }
+
+/*
+ * crypt only: convert from 64 bit to 11 bit ASCII
+ * prefixing with the salt
+ */
+
+static char *output_conversion(ufc_long v1, ufc_long v2, const char *salt)
+ { static char outbuf[14];
+ int i, s;
+
+ outbuf[0] = salt[0];
+ outbuf[1] = salt[1] ? salt[1] : salt[0];
+
+ for(i = 0; i < 5; i++)
+ outbuf[i + 2] = bin_to_ascii((v1 >> (26 - 6 * i)) & 0x3f);
+
+ s = (v2 & 0xf) << 2;
+ v2 = (v2 >> 2) | ((v1 & 0x3) << 30);
+
+ for(i = 5; i < 10; i++)
+ outbuf[i + 2] = bin_to_ascii((v2 >> (56 - 6 * i)) & 0x3f);
+
+ outbuf[12] = bin_to_ascii(s);
+ outbuf[13] = 0;
+
+ return outbuf;
+ }
+
+/*
+ * UNIX crypt function
+ */
+
+static ufc_long *_ufc_doit(ufc_long , ufc_long, ufc_long, ufc_long, ufc_long);
+
+char *ufc_crypt(const char *key,const char *salt)
+ { ufc_long *s;
+ char ktab[9];
+
+ /*
+ * Hack DES tables according to salt
+ */
+ setup_salt(salt);
+
+ /*
+ * Setup key schedule
+ */
+ clearmem(ktab, sizeof ktab);
+ StrnCpy(ktab, key, 8);
+ ufc_mk_keytab(ktab);
+
+ /*
+ * Go for the 25 DES encryptions
+ */
+ s = _ufc_doit((ufc_long)0, (ufc_long)0,
+ (ufc_long)0, (ufc_long)0, (ufc_long)25);
+
+ /*
+ * And convert back to 6 bit ASCII
+ */
+ return output_conversion(s[0], s[1], salt);
+ }
+
+
+#ifdef _UFC_32_
+
+/*
+ * 32 bit version
+ */
+
+extern long32 _ufc_keytab[16][2];
+extern long32 _ufc_sb0[], _ufc_sb1[], _ufc_sb2[], _ufc_sb3[];
+
+#define SBA(sb, v) (*(long32*)((char*)(sb)+(v)))
+
+static ufc_long *_ufc_doit(ufc_long l1, ufc_long l2, ufc_long r1, ufc_long r2, ufc_long itr)
+ { int i;
+ long32 s, *k;
+
+ while(itr--) {
+ k = &_ufc_keytab[0][0];
+ for(i=8; i--; ) {
+ s = *k++ ^ r1;
+ l1 ^= SBA(_ufc_sb1, s & 0xffff); l2 ^= SBA(_ufc_sb1, (s & 0xffff)+4);
+ l1 ^= SBA(_ufc_sb0, s >>= 16); l2 ^= SBA(_ufc_sb0, (s) +4);
+ s = *k++ ^ r2;
+ l1 ^= SBA(_ufc_sb3, s & 0xffff); l2 ^= SBA(_ufc_sb3, (s & 0xffff)+4);
+ l1 ^= SBA(_ufc_sb2, s >>= 16); l2 ^= SBA(_ufc_sb2, (s) +4);
+
+ s = *k++ ^ l1;
+ r1 ^= SBA(_ufc_sb1, s & 0xffff); r2 ^= SBA(_ufc_sb1, (s & 0xffff)+4);
+ r1 ^= SBA(_ufc_sb0, s >>= 16); r2 ^= SBA(_ufc_sb0, (s) +4);
+ s = *k++ ^ l2;
+ r1 ^= SBA(_ufc_sb3, s & 0xffff); r2 ^= SBA(_ufc_sb3, (s & 0xffff)+4);
+ r1 ^= SBA(_ufc_sb2, s >>= 16); r2 ^= SBA(_ufc_sb2, (s) +4);
+ }
+ s=l1; l1=r1; r1=s; s=l2; l2=r2; r2=s;
+ }
+ return _ufc_dofinalperm(l1, l2, r1, r2);
+ }
+
+#endif
+
+#ifdef _UFC_64_
+
+/*
+ * 64 bit version
+ */
+
+extern long64 _ufc_keytab[16];
+extern long64 _ufc_sb0[], _ufc_sb1[], _ufc_sb2[], _ufc_sb3[];
+
+#define SBA(sb, v) (*(long64*)((char*)(sb)+(v)))
+
+static ufc_long *_ufc_doit(ufc_long l1, ufc_long l2, ufc_long r1, ufc_long r2, ufc_long itr)
+ { int i;
+ long64 l, r, s, *k;
+
+ l = (((long64)l1) << 32) | ((long64)l2);
+ r = (((long64)r1) << 32) | ((long64)r2);
+
+ while(itr--) {
+ k = &_ufc_keytab[0];
+ for(i=8; i--; ) {
+ s = *k++ ^ r;
+ l ^= SBA(_ufc_sb3, (s >> 0) & 0xffff);
+ l ^= SBA(_ufc_sb2, (s >> 16) & 0xffff);
+ l ^= SBA(_ufc_sb1, (s >> 32) & 0xffff);
+ l ^= SBA(_ufc_sb0, (s >> 48) & 0xffff);
+
+ s = *k++ ^ l;
+ r ^= SBA(_ufc_sb3, (s >> 0) & 0xffff);
+ r ^= SBA(_ufc_sb2, (s >> 16) & 0xffff);
+ r ^= SBA(_ufc_sb1, (s >> 32) & 0xffff);
+ r ^= SBA(_ufc_sb0, (s >> 48) & 0xffff);
+ }
+ s=l; l=r; r=s;
+ }
+
+ l1 = l >> 32; l2 = l & 0xffffffff;
+ r1 = r >> 32; r2 = r & 0xffffffff;
+ return _ufc_dofinalperm(l1, l2, r1, r2);
+ }
+
+#endif
+
+
+#else
+ int ufc_dummy_procedure(void);
+ int ufc_dummy_procedure(void) {return 0;}
+#endif
diff --git a/source/lib/username.c b/source/lib/username.c
new file mode 100644
index 00000000000..ac5530b5c71
--- /dev/null
+++ b/source/lib/username.c
@@ -0,0 +1,690 @@
+/*
+ Unix SMB/CIFS implementation.
+ Username handling
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Jeremy Allison 1997-2001.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* internal functions */
+static struct passwd *uname_string_combinations(char *s, struct passwd * (*fn) (const char *), int N);
+static struct passwd *uname_string_combinations2(char *s, int offset, struct passwd * (*fn) (const char *), int N);
+
+/*****************************************************************
+ Check if a user or group name is local (this is a *local* name for
+ *local* people, there's nothing for you here...).
+*****************************************************************/
+
+static BOOL name_is_local(const char *name)
+{
+ return !(strchr_m(name, *lp_winbind_separator()));
+}
+
+/*****************************************************************
+ Splits passed user or group name to domain and user/group name parts
+ Returns True if name was splitted and False otherwise.
+*****************************************************************/
+
+BOOL split_domain_and_name(const char *name, char *domain, char* username)
+{
+ char *p = strchr(name,*lp_winbind_separator());
+
+
+ /* Parse a string of the form DOMAIN/user into a domain and a user */
+ DEBUG(10,("split_domain_and_name: checking whether name |%s| local or not\n", name));
+
+ if (p) {
+ fstrcpy(username, p+1);
+ fstrcpy(domain, name);
+ domain[PTR_DIFF(p, name)] = 0;
+ } else if (lp_winbind_use_default_domain()) {
+ fstrcpy(username, name);
+ fstrcpy(domain, lp_workgroup());
+ } else {
+ return False;
+ }
+
+ DEBUG(10,("split_domain_and_name: all is fine, domain is |%s| and name is |%s|\n", domain, username));
+ return True;
+}
+
+/****************************************************************************
+ Get a users home directory.
+****************************************************************************/
+
+char *get_user_home_dir(const char *user)
+{
+ static struct passwd *pass;
+
+ /* Ensure the user exists. */
+
+ pass = Get_Pwnam(user);
+
+ if (!pass)
+ return(NULL);
+ /* Return home directory from struct passwd. */
+
+ return(pass->pw_dir);
+}
+
+/*******************************************************************
+ Map a username from a dos name to a unix name by looking in the username
+ map. Note that this modifies the name in place.
+ This is the main function that should be called *once* on
+ any incoming or new username - in order to canonicalize the name.
+ This is being done to de-couple the case conversions from the user mapping
+ function. Previously, the map_username was being called
+ every time Get_Pwnam was called.
+ Returns True if username was changed, false otherwise.
+********************************************************************/
+
+BOOL map_username(char *user)
+{
+ static BOOL initialised=False;
+ static fstring last_from,last_to;
+ XFILE *f;
+ char *mapfile = lp_username_map();
+ char *s;
+ pstring buf;
+ BOOL mapped_user = False;
+
+ if (!*user)
+ return False;
+
+ if (!*mapfile)
+ return False;
+
+ if (!initialised) {
+ *last_from = *last_to = 0;
+ initialised = True;
+ }
+
+ if (strequal(user,last_to))
+ return False;
+
+ if (strequal(user,last_from)) {
+ DEBUG(3,("Mapped user %s to %s\n",user,last_to));
+ fstrcpy(user,last_to);
+ return True;
+ }
+
+ f = x_fopen(mapfile,O_RDONLY, 0);
+ if (!f) {
+ DEBUG(0,("can't open username map %s. Error %s\n",mapfile, strerror(errno) ));
+ return False;
+ }
+
+ DEBUG(4,("Scanning username map %s\n",mapfile));
+
+ while((s=fgets_slash(buf,sizeof(buf),f))!=NULL) {
+ char *unixname = s;
+ char *dosname = strchr_m(unixname,'=');
+ char **dosuserlist;
+ BOOL return_if_mapped = False;
+
+ if (!dosname)
+ continue;
+
+ *dosname++ = 0;
+
+ while (isspace((int)*unixname))
+ unixname++;
+
+ if ('!' == *unixname) {
+ return_if_mapped = True;
+ unixname++;
+ while (*unixname && isspace((int)*unixname))
+ unixname++;
+ }
+
+ if (!*unixname || strchr_m("#;",*unixname))
+ continue;
+
+ {
+ int l = strlen(unixname);
+ while (l && isspace((int)unixname[l-1])) {
+ unixname[l-1] = 0;
+ l--;
+ }
+ }
+
+ dosuserlist = str_list_make(dosname, NULL);
+ if (!dosuserlist) {
+ DEBUG(0,("Unable to build user list\n"));
+ return False;
+ }
+
+ if (strchr_m(dosname,'*') || user_in_list(user, (const char **)dosuserlist, NULL, 0)) {
+ DEBUG(3,("Mapped user %s to %s\n",user,unixname));
+ mapped_user = True;
+ fstrcpy(last_from,user);
+ sscanf(unixname,"%s",user);
+ fstrcpy(last_to,user);
+ if(return_if_mapped) {
+ str_list_free (&dosuserlist);
+ x_fclose(f);
+ return True;
+ }
+ }
+
+ str_list_free (&dosuserlist);
+ }
+
+ x_fclose(f);
+
+ /*
+ * Setup the last_from and last_to as an optimization so
+ * that we don't scan the file again for the same user.
+ */
+ fstrcpy(last_from,user);
+ fstrcpy(last_to,user);
+
+ return mapped_user;
+}
+
+/****************************************************************************
+ * A wrapper for sys_getpwnam(). The following variations are tried:
+ * - as transmitted
+ * - in all lower case if this differs from transmitted
+ * - in all upper case if this differs from transmitted
+ * - using lp_usernamelevel() for permutations.
+****************************************************************************/
+
+static struct passwd *Get_Pwnam_ret = NULL;
+
+static struct passwd *Get_Pwnam_internals(const char *user, char *user2)
+{
+ struct passwd *ret = NULL;
+
+ if (!user2 || !(*user2))
+ return(NULL);
+
+ if (!user || !(*user))
+ return(NULL);
+
+ /* Try in all lower case first as this is the most
+ common case on UNIX systems */
+ strlower_m(user2);
+ DEBUG(5,("Trying _Get_Pwnam(), username as lowercase is %s\n",user2));
+ ret = getpwnam_alloc(user2);
+ if(ret)
+ goto done;
+
+ /* Try as given, if username wasn't originally lowercase */
+ if(strcmp(user, user2) != 0) {
+ DEBUG(5,("Trying _Get_Pwnam(), username as given is %s\n", user));
+ ret = getpwnam_alloc(user);
+ if(ret)
+ goto done;
+ }
+
+ /* Try as uppercase, if username wasn't originally uppercase */
+ strupper_m(user2);
+ if(strcmp(user, user2) != 0) {
+ DEBUG(5,("Trying _Get_Pwnam(), username as uppercase is %s\n", user2));
+ ret = getpwnam_alloc(user2);
+ if(ret)
+ goto done;
+ }
+
+ /* Try all combinations up to usernamelevel */
+ strlower_m(user2);
+ DEBUG(5,("Checking combinations of %d uppercase letters in %s\n", lp_usernamelevel(), user2));
+ ret = uname_string_combinations(user2, getpwnam_alloc, lp_usernamelevel());
+
+done:
+ DEBUG(5,("Get_Pwnam_internals %s find user [%s]!\n",ret ? "did":"didn't", user));
+
+ /* This call used to just return the 'passwd' static buffer.
+ This could then have accidental reuse implications, so
+ we now malloc a copy, and free it in the next use.
+
+ This should cause the (ab)user to segfault if it
+ uses an old struct.
+
+ This is better than useing the wrong data in security
+ critical operations.
+
+ The real fix is to make the callers free the returned
+ malloc'ed data.
+ */
+
+ if (Get_Pwnam_ret) {
+ passwd_free(&Get_Pwnam_ret);
+ }
+
+ Get_Pwnam_ret = ret;
+
+ return ret;
+}
+
+/****************************************************************************
+ Get_Pwnam wrapper without modification.
+ NOTE: This with NOT modify 'user'!
+****************************************************************************/
+
+struct passwd *Get_Pwnam(const char *user)
+{
+ fstring user2;
+ struct passwd *ret;
+
+ if ( *user == '\0' ) {
+ DEBUG(10,("Get_Pwnam: empty username!\n"));
+ return NULL;
+ }
+
+ fstrcpy(user2, user);
+
+ DEBUG(5,("Finding user %s\n", user));
+
+ ret = Get_Pwnam_internals(user, user2);
+
+ return ret;
+}
+
+/****************************************************************************
+ Check if a user is in a netgroup user list. If at first we don't succeed,
+ try lower case.
+****************************************************************************/
+
+static BOOL user_in_netgroup_list(const char *user, const char *ngname)
+{
+#ifdef HAVE_NETGROUP
+ static char *mydomain = NULL;
+ fstring lowercase_user, lowercase_ngname;
+
+ if (mydomain == NULL)
+ yp_get_default_domain(&mydomain);
+
+ if(mydomain == NULL) {
+ DEBUG(5,("Unable to get default yp domain\n"));
+ return False;
+ }
+
+ DEBUG(5,("looking for user %s of domain %s in netgroup %s\n",
+ user, mydomain, ngname));
+ DEBUG(5,("innetgr is %s\n", innetgr(ngname, NULL, user, mydomain)
+ ? "TRUE" : "FALSE"));
+
+ if (innetgr(ngname, NULL, user, mydomain))
+ return (True);
+
+ /*
+ * Ok, innetgr is case sensitive. Try once more with lowercase
+ * just in case. Attempt to fix #703. JRA.
+ */
+
+ fstrcpy(lowercase_user, user);
+ strlower_m(lowercase_user);
+ fstrcpy(lowercase_ngname, ngname);
+ strlower_m(lowercase_ngname);
+
+ if (innetgr(lowercase_ngname, NULL, lowercase_user, mydomain))
+ return (True);
+
+#endif /* HAVE_NETGROUP */
+ return False;
+}
+
+/****************************************************************************
+ Check if a user is in a winbind group.
+****************************************************************************/
+
+static BOOL user_in_winbind_group_list(const char *user, const char *gname, BOOL *winbind_answered)
+{
+ int i;
+ gid_t gid, gid_low, gid_high;
+ BOOL ret = False;
+ static gid_t *groups = NULL;
+ static int num_groups = 0;
+ static fstring last_user = "";
+
+ *winbind_answered = False;
+
+ if ((gid = nametogid(gname)) == (gid_t)-1) {
+ DEBUG(0,("user_in_winbind_group_list: nametogid for group %s failed.\n",
+ gname ));
+ goto err;
+ }
+
+ if (!lp_idmap_gid(&gid_low, &gid_high)) {
+ DEBUG(4, ("winbind gid range not configured, therefore %s cannot be a winbind group\n", gname));
+ goto err;
+ }
+
+ if (gid < gid_low || gid > gid_high) {
+ DEBUG(4, ("group %s is not a winbind group\n", gname));
+ goto err;
+ }
+
+ /* try to user the last user we looked up */
+ /* otherwise fall back to lookups */
+
+ if ( !strequal( last_user, user ) || !groups )
+ {
+ /* clear any cached information */
+
+ SAFE_FREE(groups);
+ fstrcpy( last_user, "" );
+
+ /*
+ * Get the gid's that this user belongs to.
+ */
+
+ if ((num_groups = winbind_getgroups(user, &groups)) == -1)
+ return False;
+
+ if ( num_groups == -1 )
+ return False;
+
+ if ( num_groups == 0 ) {
+ *winbind_answered = True;
+ return False;
+ }
+
+ /* save the last username */
+
+ fstrcpy( last_user, user );
+
+ }
+ else
+ DEBUG(10,("user_in_winbind_group_list: using cached user groups for [%s]\n", user));
+
+ if ( DEBUGLEVEL >= 10 ) {
+ DEBUG(10,("user_in_winbind_group_list: using groups -- "));
+ for ( i=0; i<num_groups; i++ )
+ DEBUGADD(10,("%lu ", (unsigned long)groups[i]));
+ DEBUGADD(10,("\n"));
+ }
+
+ /*
+ * Now we have the gid list for this user - convert the gname
+ * to a gid_t via either winbind or the local UNIX lookup and do the comparison.
+ */
+
+ for (i = 0; i < num_groups; i++) {
+ if (gid == groups[i]) {
+ ret = True;
+ break;
+ }
+ }
+
+ *winbind_answered = True;
+ SAFE_FREE(groups);
+ return ret;
+
+ err:
+
+ *winbind_answered = False;
+ SAFE_FREE(groups);
+ return False;
+}
+
+/****************************************************************************
+ Check if a user is in a UNIX group.
+****************************************************************************/
+
+BOOL user_in_unix_group_list(const char *user,const char *gname)
+{
+ struct passwd *pass = Get_Pwnam(user);
+ struct sys_userlist *user_list;
+ struct sys_userlist *member;
+
+ DEBUG(10,("user_in_unix_group_list: checking user %s in group %s\n", user, gname));
+
+ /*
+ * We need to check the users primary group as this
+ * group is implicit and often not listed in the group database.
+ */
+
+ if (pass) {
+ if (strequal(gname,gidtoname(pass->pw_gid))) {
+ DEBUG(10,("user_in_unix_group_list: group %s is primary group.\n", gname ));
+ return True;
+ }
+ }
+
+ user_list = get_users_in_group(gname);
+ if (user_list == NULL) {
+ DEBUG(10,("user_in_unix_group_list: no such group %s\n", gname ));
+ return False;
+ }
+
+ for (member = user_list; member; member = member->next) {
+ DEBUG(10,("user_in_unix_group_list: checking user %s against member %s\n",
+ user, member->unix_name ));
+ if (strequal(member->unix_name,user)) {
+ free_userlist(user_list);
+ return(True);
+ }
+ }
+
+ free_userlist(user_list);
+ return False;
+}
+
+/****************************************************************************
+ Check if a user is in a group list. Ask winbind first, then use UNIX.
+****************************************************************************/
+
+BOOL user_in_group_list(const char *user, const char *gname, gid_t *groups, size_t n_groups)
+{
+ BOOL winbind_answered = False;
+ BOOL ret;
+ gid_t gid;
+ unsigned i;
+
+ gid = nametogid(gname);
+ if (gid == (gid_t)-1)
+ return False;
+
+ if (groups && n_groups > 0) {
+ for (i=0; i < n_groups; i++) {
+ if (groups[i] == gid) {
+ return True;
+ }
+ }
+ return False;
+ }
+
+ /* fallback if we don't yet have the group list */
+
+ ret = user_in_winbind_group_list(user, gname, &winbind_answered);
+ if (!winbind_answered)
+ ret = user_in_unix_group_list(user, gname);
+
+ if (ret)
+ DEBUG(10,("user_in_group_list: user |%s| is in group |%s|\n", user, gname));
+ return ret;
+}
+
+/****************************************************************************
+ Check if a user is in a user list - can check combinations of UNIX
+ and netgroup lists.
+****************************************************************************/
+
+BOOL user_in_list(const char *user,const char **list, gid_t *groups, size_t n_groups)
+{
+ if (!list || !*list)
+ return False;
+
+ DEBUG(10,("user_in_list: checking user %s in list\n", user));
+
+ while (*list) {
+
+ DEBUG(10,("user_in_list: checking user |%s| against |%s|\n", user, *list));
+
+ /*
+ * Check raw username.
+ */
+ if (strequal(user, *list))
+ return(True);
+
+ /*
+ * Now check to see if any combination
+ * of UNIX and netgroups has been specified.
+ */
+
+ if(**list == '@') {
+ /*
+ * Old behaviour. Check netgroup list
+ * followed by UNIX list.
+ */
+ if(user_in_netgroup_list(user, *list +1))
+ return True;
+ if(user_in_group_list(user, *list +1, groups, n_groups))
+ return True;
+ } else if (**list == '+') {
+
+ if((*(*list +1)) == '&') {
+ /*
+ * Search UNIX list followed by netgroup.
+ */
+ if(user_in_group_list(user, *list +2, groups, n_groups))
+ return True;
+ if(user_in_netgroup_list(user, *list +2))
+ return True;
+
+ } else {
+
+ /*
+ * Just search UNIX list.
+ */
+
+ if(user_in_group_list(user, *list +1, groups, n_groups))
+ return True;
+ }
+
+ } else if (**list == '&') {
+
+ if(*(*list +1) == '+') {
+ /*
+ * Search netgroup list followed by UNIX list.
+ */
+ if(user_in_netgroup_list(user, *list +2))
+ return True;
+ if(user_in_group_list(user, *list +2, groups, n_groups))
+ return True;
+ } else {
+ /*
+ * Just search netgroup list.
+ */
+ if(user_in_netgroup_list(user, *list +1))
+ return True;
+ }
+ } else if (!name_is_local(*list)) {
+ /*
+ * If user name did not match and token is not
+ * a unix group and the token has a winbind separator in the
+ * name then see if it is a Windows group.
+ */
+
+ DOM_SID g_sid;
+ enum SID_NAME_USE name_type;
+ BOOL winbind_answered = False;
+ BOOL ret;
+ fstring groupname, domain;
+
+ /* Parse a string of the form DOMAIN/user into a domain and a user */
+
+ char *p = strchr(*list,*lp_winbind_separator());
+
+ DEBUG(10,("user_in_list: checking if user |%s| is in winbind group |%s|\n", user, *list));
+
+ if (p) {
+ fstrcpy(groupname, p+1);
+ fstrcpy(domain, *list);
+ domain[PTR_DIFF(p, *list)] = 0;
+
+ /* Check to see if name is a Windows group; Win2k native mode DCs
+ will return domain local groups; while NT4 or mixed mode 2k DCs
+ will not */
+
+ if ( winbind_lookup_name(domain, groupname, &g_sid, &name_type)
+ && ( name_type==SID_NAME_DOM_GRP ||
+ (strequal(lp_workgroup(), domain) && name_type==SID_NAME_ALIAS) ) )
+ {
+
+ /* Check if user name is in the Windows group */
+ ret = user_in_winbind_group_list(user, *list, &winbind_answered);
+
+ if (winbind_answered && ret == True) {
+ DEBUG(10,("user_in_list: user |%s| is in winbind group |%s|\n", user, *list));
+ return ret;
+ }
+ }
+ }
+ }
+
+ list++;
+ }
+ return(False);
+}
+
+/* The functions below have been taken from password.c and slightly modified */
+/****************************************************************************
+ Apply a function to upper/lower case combinations
+ of a string and return true if one of them returns true.
+ Try all combinations with N uppercase letters.
+ offset is the first char to try and change (start with 0)
+ it assumes the string starts lowercased
+****************************************************************************/
+
+static struct passwd *uname_string_combinations2(char *s,int offset,struct passwd *(*fn)(const char *),int N)
+{
+ ssize_t len = (ssize_t)strlen(s);
+ int i;
+ struct passwd *ret;
+
+ if (N <= 0 || offset >= len)
+ return(fn(s));
+
+ for (i=offset;i<(len-(N-1));i++) {
+ char c = s[i];
+ if (!islower((int)c))
+ continue;
+ s[i] = toupper(c);
+ ret = uname_string_combinations2(s,i+1,fn,N-1);
+ if(ret)
+ return(ret);
+ s[i] = c;
+ }
+ return(NULL);
+}
+
+/****************************************************************************
+ Apply a function to upper/lower case combinations
+ of a string and return true if one of them returns true.
+ Try all combinations with up to N uppercase letters.
+ offset is the first char to try and change (start with 0)
+ it assumes the string starts lowercased
+****************************************************************************/
+
+static struct passwd * uname_string_combinations(char *s,struct passwd * (*fn)(const char *),int N)
+{
+ int n;
+ struct passwd *ret;
+
+ for (n=1;n<=N;n++) {
+ ret = uname_string_combinations2(s,0,fn,n);
+ if(ret)
+ return(ret);
+ }
+ return(NULL);
+}
+
diff --git a/source/lib/util.c b/source/lib/util.c
new file mode 100644
index 00000000000..3f57048a00b
--- /dev/null
+++ b/source/lib/util.c
@@ -0,0 +1,2501 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Jeremy Allison 2001-2002
+ Copyright (C) Simo Sorce 2001
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#if (defined(HAVE_NETGROUP) && defined (WITH_AUTOMOUNT))
+#ifdef WITH_NISPLUS_HOME
+#ifdef BROKEN_NISPLUS_INCLUDE_FILES
+/*
+ * The following lines are needed due to buggy include files
+ * in Solaris 2.6 which define GROUP in both /usr/include/sys/acl.h and
+ * also in /usr/include/rpcsvc/nis.h. The definitions conflict. JRA.
+ * Also GROUP_OBJ is defined as 0x4 in /usr/include/sys/acl.h and as
+ * an enum in /usr/include/rpcsvc/nis.h.
+ */
+
+#if defined(GROUP)
+#undef GROUP
+#endif
+
+#if defined(GROUP_OBJ)
+#undef GROUP_OBJ
+#endif
+
+#endif /* BROKEN_NISPLUS_INCLUDE_FILES */
+
+#include <rpcsvc/nis.h>
+
+#else /* !WITH_NISPLUS_HOME */
+
+#include "rpcsvc/ypclnt.h"
+
+#endif /* WITH_NISPLUS_HOME */
+#endif /* HAVE_NETGROUP && WITH_AUTOMOUNT */
+
+int Protocol = PROTOCOL_COREPLUS;
+
+/* a default finfo structure to ensure all fields are sensible */
+file_info def_finfo = {-1,0,0,0,0,0,0,"",""};
+
+/* this is used by the chaining code */
+int chain_size = 0;
+
+int trans_num = 0;
+
+/*
+ case handling on filenames
+*/
+int case_default = CASE_LOWER;
+
+/* the following control case operations - they are put here so the
+ client can link easily */
+BOOL case_sensitive;
+BOOL case_preserve;
+BOOL use_mangled_map = False;
+BOOL short_case_preserve;
+BOOL case_mangle;
+
+static enum remote_arch_types ra_type = RA_UNKNOWN;
+pstring user_socket_options=DEFAULT_SOCKET_OPTIONS;
+
+/***********************************************************************
+ Definitions for all names.
+***********************************************************************/
+
+static char *smb_myname;
+static char *smb_myworkgroup;
+static char *smb_scope;
+static int smb_num_netbios_names;
+static char **smb_my_netbios_names;
+
+/***********************************************************************
+ Allocate and set myname. Ensure upper case.
+***********************************************************************/
+
+BOOL set_global_myname(const char *myname)
+{
+ SAFE_FREE(smb_myname);
+ smb_myname = strdup(myname);
+ if (!smb_myname)
+ return False;
+ strupper_m(smb_myname);
+ return True;
+}
+
+const char *global_myname(void)
+{
+ return smb_myname;
+}
+
+/***********************************************************************
+ Allocate and set myworkgroup. Ensure upper case.
+***********************************************************************/
+
+BOOL set_global_myworkgroup(const char *myworkgroup)
+{
+ SAFE_FREE(smb_myworkgroup);
+ smb_myworkgroup = strdup(myworkgroup);
+ if (!smb_myworkgroup)
+ return False;
+ strupper_m(smb_myworkgroup);
+ return True;
+}
+
+const char *lp_workgroup(void)
+{
+ return smb_myworkgroup;
+}
+
+/***********************************************************************
+ Allocate and set scope. Ensure upper case.
+***********************************************************************/
+
+BOOL set_global_scope(const char *scope)
+{
+ SAFE_FREE(smb_scope);
+ smb_scope = strdup(scope);
+ if (!smb_scope)
+ return False;
+ strupper_m(smb_scope);
+ return True;
+}
+
+/*********************************************************************
+ Ensure scope is never null string.
+*********************************************************************/
+
+const char *global_scope(void)
+{
+ if (!smb_scope)
+ set_global_scope("");
+ return smb_scope;
+}
+
+static void free_netbios_names_array(void)
+{
+ int i;
+
+ for (i = 0; i < smb_num_netbios_names; i++)
+ SAFE_FREE(smb_my_netbios_names[i]);
+
+ SAFE_FREE(smb_my_netbios_names);
+ smb_num_netbios_names = 0;
+}
+
+static BOOL allocate_my_netbios_names_array(size_t number)
+{
+ free_netbios_names_array();
+
+ smb_num_netbios_names = number + 1;
+ smb_my_netbios_names = (char **)malloc( sizeof(char *) * smb_num_netbios_names );
+
+ if (!smb_my_netbios_names)
+ return False;
+
+ memset(smb_my_netbios_names, '\0', sizeof(char *) * smb_num_netbios_names);
+ return True;
+}
+
+static BOOL set_my_netbios_names(const char *name, int i)
+{
+ SAFE_FREE(smb_my_netbios_names[i]);
+
+ smb_my_netbios_names[i] = strdup(name);
+ if (!smb_my_netbios_names[i])
+ return False;
+ strupper_m(smb_my_netbios_names[i]);
+ return True;
+}
+
+const char *my_netbios_names(int i)
+{
+ return smb_my_netbios_names[i];
+}
+
+BOOL set_netbios_aliases(const char **str_array)
+{
+ size_t namecount;
+
+ /* Work out the max number of netbios aliases that we have */
+ for( namecount=0; str_array && (str_array[namecount] != NULL); namecount++ )
+ ;
+
+ if ( global_myname() && *global_myname())
+ namecount++;
+
+ /* Allocate space for the netbios aliases */
+ if (!allocate_my_netbios_names_array(namecount))
+ return False;
+
+ /* Use the global_myname string first */
+ namecount=0;
+ if ( global_myname() && *global_myname()) {
+ set_my_netbios_names( global_myname(), namecount );
+ namecount++;
+ }
+
+ if (str_array) {
+ size_t i;
+ for ( i = 0; str_array[i] != NULL; i++) {
+ size_t n;
+ BOOL duplicate = False;
+
+ /* Look for duplicates */
+ for( n=0; n<namecount; n++ ) {
+ if( strequal( str_array[i], my_netbios_names(n) ) ) {
+ duplicate = True;
+ break;
+ }
+ }
+ if (!duplicate) {
+ if (!set_my_netbios_names(str_array[i], namecount))
+ return False;
+ namecount++;
+ }
+ }
+ }
+ return True;
+}
+
+/****************************************************************************
+ Common name initialization code.
+****************************************************************************/
+
+BOOL init_names(void)
+{
+ extern fstring local_machine;
+ char *p;
+ int n;
+
+ if (global_myname() == NULL || *global_myname() == '\0') {
+ if (!set_global_myname(myhostname())) {
+ DEBUG( 0, ( "init_structs: malloc fail.\n" ) );
+ return False;
+ }
+ }
+
+ if (!set_netbios_aliases(lp_netbios_aliases())) {
+ DEBUG( 0, ( "init_structs: malloc fail.\n" ) );
+ return False;
+ }
+
+ fstrcpy( local_machine, global_myname() );
+ trim_char( local_machine, ' ', ' ' );
+ p = strchr( local_machine, ' ' );
+ if (p)
+ *p = 0;
+ strlower_m( local_machine );
+
+ DEBUG( 5, ("Netbios name list:-\n") );
+ for( n=0; my_netbios_names(n); n++ )
+ DEBUGADD( 5, ( "my_netbios_names[%d]=\"%s\"\n", n, my_netbios_names(n) ) );
+
+ return( True );
+}
+
+/**************************************************************************n
+ Find a suitable temporary directory. The result should be copied immediately
+ as it may be overwritten by a subsequent call.
+****************************************************************************/
+
+const char *tmpdir(void)
+{
+ char *p;
+ if ((p = getenv("TMPDIR")))
+ return p;
+ return "/tmp";
+}
+
+/****************************************************************************
+ Determine whether we are in the specified group.
+****************************************************************************/
+
+BOOL in_group(gid_t group, gid_t current_gid, int ngroups, const gid_t *groups)
+{
+ int i;
+
+ if (group == current_gid)
+ return(True);
+
+ for (i=0;i<ngroups;i++)
+ if (group == groups[i])
+ return(True);
+
+ return(False);
+}
+
+/****************************************************************************
+ Like atoi but gets the value up to the separator character.
+****************************************************************************/
+
+static const char *Atoic(const char *p, int *n, const char *c)
+{
+ if (!isdigit((int)*p)) {
+ DEBUG(5, ("Atoic: malformed number\n"));
+ return NULL;
+ }
+
+ (*n) = atoi(p);
+
+ while ((*p) && isdigit((int)*p))
+ p++;
+
+ if (strchr_m(c, *p) == NULL) {
+ DEBUG(5, ("Atoic: no separator characters (%s) not found\n", c));
+ return NULL;
+ }
+
+ return p;
+}
+
+/*************************************************************************
+ Reads a list of numbers.
+ *************************************************************************/
+
+const char *get_numlist(const char *p, uint32 **num, int *count)
+{
+ int val;
+
+ if (num == NULL || count == NULL)
+ return NULL;
+
+ (*count) = 0;
+ (*num ) = NULL;
+
+ while ((p = Atoic(p, &val, ":,")) != NULL && (*p) != ':') {
+ uint32 *tn;
+
+ tn = Realloc((*num), ((*count)+1) * sizeof(uint32));
+ if (tn == NULL) {
+ SAFE_FREE(*num);
+ return NULL;
+ } else
+ (*num) = tn;
+ (*num)[(*count)] = val;
+ (*count)++;
+ p++;
+ }
+
+ return p;
+}
+
+/*******************************************************************
+ Check if a file exists - call vfs_file_exist for samba files.
+********************************************************************/
+
+BOOL file_exist(const char *fname,SMB_STRUCT_STAT *sbuf)
+{
+ SMB_STRUCT_STAT st;
+ if (!sbuf)
+ sbuf = &st;
+
+ if (sys_stat(fname,sbuf) != 0)
+ return(False);
+
+ return((S_ISREG(sbuf->st_mode)) || (S_ISFIFO(sbuf->st_mode)));
+}
+
+/*******************************************************************
+ Check a files mod time.
+********************************************************************/
+
+time_t file_modtime(const char *fname)
+{
+ SMB_STRUCT_STAT st;
+
+ if (sys_stat(fname,&st) != 0)
+ return(0);
+
+ return(st.st_mtime);
+}
+
+/*******************************************************************
+ Check if a directory exists.
+********************************************************************/
+
+BOOL directory_exist(char *dname,SMB_STRUCT_STAT *st)
+{
+ SMB_STRUCT_STAT st2;
+ BOOL ret;
+
+ if (!st)
+ st = &st2;
+
+ if (sys_stat(dname,st) != 0)
+ return(False);
+
+ ret = S_ISDIR(st->st_mode);
+ if(!ret)
+ errno = ENOTDIR;
+ return ret;
+}
+
+/*******************************************************************
+ Returns the size in bytes of the named file.
+********************************************************************/
+
+SMB_OFF_T get_file_size(char *file_name)
+{
+ SMB_STRUCT_STAT buf;
+ buf.st_size = 0;
+ if(sys_stat(file_name,&buf) != 0)
+ return (SMB_OFF_T)-1;
+ return(buf.st_size);
+}
+
+/*******************************************************************
+ Return a string representing an attribute for a file.
+********************************************************************/
+
+char *attrib_string(uint16 mode)
+{
+ static fstring attrstr;
+
+ attrstr[0] = 0;
+
+ if (mode & aVOLID) fstrcat(attrstr,"V");
+ if (mode & aDIR) fstrcat(attrstr,"D");
+ if (mode & aARCH) fstrcat(attrstr,"A");
+ if (mode & aHIDDEN) fstrcat(attrstr,"H");
+ if (mode & aSYSTEM) fstrcat(attrstr,"S");
+ if (mode & aRONLY) fstrcat(attrstr,"R");
+
+ return(attrstr);
+}
+
+/*******************************************************************
+ Show a smb message structure.
+********************************************************************/
+
+void show_msg(char *buf)
+{
+ int i;
+ int bcc=0;
+
+ if (!DEBUGLVL(5))
+ return;
+
+ DEBUG(5,("size=%d\nsmb_com=0x%x\nsmb_rcls=%d\nsmb_reh=%d\nsmb_err=%d\nsmb_flg=%d\nsmb_flg2=%d\n",
+ smb_len(buf),
+ (int)CVAL(buf,smb_com),
+ (int)CVAL(buf,smb_rcls),
+ (int)CVAL(buf,smb_reh),
+ (int)SVAL(buf,smb_err),
+ (int)CVAL(buf,smb_flg),
+ (int)SVAL(buf,smb_flg2)));
+ DEBUGADD(5,("smb_tid=%d\nsmb_pid=%d\nsmb_uid=%d\nsmb_mid=%d\n",
+ (int)SVAL(buf,smb_tid),
+ (int)SVAL(buf,smb_pid),
+ (int)SVAL(buf,smb_uid),
+ (int)SVAL(buf,smb_mid)));
+ DEBUGADD(5,("smt_wct=%d\n",(int)CVAL(buf,smb_wct)));
+
+ for (i=0;i<(int)CVAL(buf,smb_wct);i++)
+ DEBUGADD(5,("smb_vwv[%2d]=%5d (0x%X)\n",i,
+ SVAL(buf,smb_vwv+2*i),SVAL(buf,smb_vwv+2*i)));
+
+ bcc = (int)SVAL(buf,smb_vwv+2*(CVAL(buf,smb_wct)));
+
+ DEBUGADD(5,("smb_bcc=%d\n",bcc));
+
+ if (DEBUGLEVEL < 10)
+ return;
+
+ if (DEBUGLEVEL < 50)
+ bcc = MIN(bcc, 512);
+
+ dump_data(10, smb_buf(buf), bcc);
+}
+
+/*******************************************************************
+ Set the length and marker of an smb packet.
+********************************************************************/
+
+void smb_setlen(char *buf,int len)
+{
+ _smb_setlen(buf,len);
+
+ SCVAL(buf,4,0xFF);
+ SCVAL(buf,5,'S');
+ SCVAL(buf,6,'M');
+ SCVAL(buf,7,'B');
+}
+
+/*******************************************************************
+ Setup the word count and byte count for a smb message.
+********************************************************************/
+
+int set_message(char *buf,int num_words,int num_bytes,BOOL zero)
+{
+ if (zero)
+ memset(buf + smb_size,'\0',num_words*2 + num_bytes);
+ SCVAL(buf,smb_wct,num_words);
+ SSVAL(buf,smb_vwv + num_words*SIZEOFWORD,num_bytes);
+ smb_setlen(buf,smb_size + num_words*2 + num_bytes - 4);
+ return (smb_size + num_words*2 + num_bytes);
+}
+
+/*******************************************************************
+ Setup only the byte count for a smb message.
+********************************************************************/
+
+int set_message_bcc(char *buf,int num_bytes)
+{
+ int num_words = CVAL(buf,smb_wct);
+ SSVAL(buf,smb_vwv + num_words*SIZEOFWORD,num_bytes);
+ smb_setlen(buf,smb_size + num_words*2 + num_bytes - 4);
+ return (smb_size + num_words*2 + num_bytes);
+}
+
+/*******************************************************************
+ Setup only the byte count for a smb message, using the end of the
+ message as a marker.
+********************************************************************/
+
+int set_message_end(void *outbuf,void *end_ptr)
+{
+ return set_message_bcc((char *)outbuf,PTR_DIFF(end_ptr,smb_buf((char *)outbuf)));
+}
+
+/*******************************************************************
+ Reduce a file name, removing .. elements.
+********************************************************************/
+
+void dos_clean_name(char *s)
+{
+ char *p=NULL;
+
+ DEBUG(3,("dos_clean_name [%s]\n",s));
+
+ /* remove any double slashes */
+ all_string_sub(s, "\\\\", "\\", 0);
+
+ while ((p = strstr_m(s,"\\..\\")) != NULL) {
+ pstring s1;
+
+ *p = 0;
+ pstrcpy(s1,p+3);
+
+ if ((p=strrchr_m(s,'\\')) != NULL)
+ *p = 0;
+ else
+ *s = 0;
+ pstrcat(s,s1);
+ }
+
+ trim_string(s,NULL,"\\..");
+
+ all_string_sub(s, "\\.\\", "\\", 0);
+}
+
+/*******************************************************************
+ Reduce a file name, removing .. elements.
+********************************************************************/
+
+void unix_clean_name(char *s)
+{
+ char *p=NULL;
+
+ DEBUG(3,("unix_clean_name [%s]\n",s));
+
+ /* remove any double slashes */
+ all_string_sub(s, "//","/", 0);
+
+ /* Remove leading ./ characters */
+ if(strncmp(s, "./", 2) == 0) {
+ trim_string(s, "./", NULL);
+ if(*s == 0)
+ pstrcpy(s,"./");
+ }
+
+ while ((p = strstr_m(s,"/../")) != NULL) {
+ pstring s1;
+
+ *p = 0;
+ pstrcpy(s1,p+3);
+
+ if ((p=strrchr_m(s,'/')) != NULL)
+ *p = 0;
+ else
+ *s = 0;
+ pstrcat(s,s1);
+ }
+
+ trim_string(s,NULL,"/..");
+}
+
+/****************************************************************************
+ Make a dir struct.
+****************************************************************************/
+
+void make_dir_struct(char *buf, const char *mask, const char *fname,SMB_OFF_T size,int mode,time_t date)
+{
+ char *p;
+ pstring mask2;
+
+ pstrcpy(mask2,mask);
+
+ if ((mode & aDIR) != 0)
+ size = 0;
+
+ memset(buf+1,' ',11);
+ if ((p = strchr_m(mask2,'.')) != NULL) {
+ *p = 0;
+ push_ascii(buf+1,mask2,8, 0);
+ push_ascii(buf+9,p+1,3, 0);
+ *p = '.';
+ } else
+ push_ascii(buf+1,mask2,11, 0);
+
+ memset(buf+21,'\0',DIR_STRUCT_SIZE-21);
+ SCVAL(buf,21,mode);
+ put_dos_date(buf,22,date);
+ SSVAL(buf,26,size & 0xFFFF);
+ SSVAL(buf,28,(size >> 16)&0xFFFF);
+ push_ascii(buf+30,fname,12, case_sensitive ? 0 : STR_UPPER);
+ DEBUG(8,("put name [%s] from [%s] into dir struct\n",buf+30, fname));
+}
+
+/*******************************************************************
+ Close the low 3 fd's and open dev/null in their place.
+********************************************************************/
+
+void close_low_fds(BOOL stderr_too)
+{
+#ifndef VALGRIND
+ int fd;
+ int i;
+
+ close(0);
+ close(1);
+
+ if (stderr_too)
+ close(2);
+
+ /* try and use up these file descriptors, so silly
+ library routines writing to stdout etc won't cause havoc */
+ for (i=0;i<3;i++) {
+ if (i == 2 && !stderr_too)
+ continue;
+
+ fd = sys_open("/dev/null",O_RDWR,0);
+ if (fd < 0)
+ fd = sys_open("/dev/null",O_WRONLY,0);
+ if (fd < 0) {
+ DEBUG(0,("Can't open /dev/null\n"));
+ return;
+ }
+ if (fd != i) {
+ DEBUG(0,("Didn't get file descriptor %d\n",i));
+ return;
+ }
+ }
+#endif
+}
+
+/****************************************************************************
+ Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
+ else
+ if SYSV use O_NDELAY
+ if BSD use FNDELAY
+****************************************************************************/
+
+int set_blocking(int fd, BOOL set)
+{
+ int val;
+#ifdef O_NONBLOCK
+#define FLAG_TO_SET O_NONBLOCK
+#else
+#ifdef SYSV
+#define FLAG_TO_SET O_NDELAY
+#else /* BSD */
+#define FLAG_TO_SET FNDELAY
+#endif
+#endif
+
+ if((val = sys_fcntl_long(fd, F_GETFL, 0)) == -1)
+ return -1;
+ if(set) /* Turn blocking on - ie. clear nonblock flag */
+ val &= ~FLAG_TO_SET;
+ else
+ val |= FLAG_TO_SET;
+ return sys_fcntl_long( fd, F_SETFL, val);
+#undef FLAG_TO_SET
+}
+
+/****************************************************************************
+ Transfer some data between two fd's.
+****************************************************************************/
+
+#ifndef TRANSFER_BUF_SIZE
+#define TRANSFER_BUF_SIZE 65536
+#endif
+
+ssize_t transfer_file_internal(int infd, int outfd, size_t n, ssize_t (*read_fn)(int, void *, size_t),
+ ssize_t (*write_fn)(int, const void *, size_t))
+{
+ char *buf;
+ size_t total = 0;
+ ssize_t read_ret;
+ ssize_t write_ret;
+ size_t num_to_read_thistime;
+ size_t num_written = 0;
+
+ if ((buf = malloc(TRANSFER_BUF_SIZE)) == NULL)
+ return -1;
+
+ while (total < n) {
+ num_to_read_thistime = MIN((n - total), TRANSFER_BUF_SIZE);
+
+ read_ret = (*read_fn)(infd, buf, num_to_read_thistime);
+ if (read_ret == -1) {
+ DEBUG(0,("transfer_file_internal: read failure. Error = %s\n", strerror(errno) ));
+ SAFE_FREE(buf);
+ return -1;
+ }
+ if (read_ret == 0)
+ break;
+
+ num_written = 0;
+
+ while (num_written < read_ret) {
+ write_ret = (*write_fn)(outfd,buf + num_written, read_ret - num_written);
+
+ if (write_ret == -1) {
+ DEBUG(0,("transfer_file_internal: write failure. Error = %s\n", strerror(errno) ));
+ SAFE_FREE(buf);
+ return -1;
+ }
+ if (write_ret == 0)
+ return (ssize_t)total;
+
+ num_written += (size_t)write_ret;
+ }
+
+ total += (size_t)read_ret;
+ }
+
+ SAFE_FREE(buf);
+ return (ssize_t)total;
+}
+
+SMB_OFF_T transfer_file(int infd,int outfd,SMB_OFF_T n)
+{
+ return (SMB_OFF_T)transfer_file_internal(infd, outfd, (size_t)n, sys_read, sys_write);
+}
+
+/*******************************************************************
+ Sleep for a specified number of milliseconds.
+********************************************************************/
+
+void smb_msleep(unsigned int t)
+{
+ unsigned int tdiff=0;
+ struct timeval tval,t1,t2;
+ fd_set fds;
+
+ GetTimeOfDay(&t1);
+ GetTimeOfDay(&t2);
+
+ while (tdiff < t) {
+ tval.tv_sec = (t-tdiff)/1000;
+ tval.tv_usec = 1000*((t-tdiff)%1000);
+
+ /* Never wait for more than 1 sec. */
+ if (tval.tv_sec > 1) {
+ tval.tv_sec = 1;
+ tval.tv_usec = 0;
+ }
+
+ FD_ZERO(&fds);
+ errno = 0;
+ sys_select_intr(0,&fds,NULL,NULL,&tval);
+
+ GetTimeOfDay(&t2);
+ if (t2.tv_sec < t1.tv_sec) {
+ /* Someone adjusted time... */
+ t1 = t2;
+ }
+
+ tdiff = TvalDiff(&t1,&t2);
+ }
+}
+
+/****************************************************************************
+ Become a daemon, discarding the controlling terminal.
+****************************************************************************/
+
+void become_daemon(BOOL Fork)
+{
+ if (Fork) {
+ if (sys_fork()) {
+ _exit(0);
+ }
+ }
+
+ /* detach from the terminal */
+#ifdef HAVE_SETSID
+ setsid();
+#elif defined(TIOCNOTTY)
+ {
+ int i = sys_open("/dev/tty", O_RDWR, 0);
+ if (i != -1) {
+ ioctl(i, (int) TIOCNOTTY, (char *)0);
+ close(i);
+ }
+ }
+#endif /* HAVE_SETSID */
+
+ /* Close fd's 0,1,2. Needed if started by rsh */
+ close_low_fds(False); /* Don't close stderr, let the debug system
+ attach it to the logfile */
+}
+
+/****************************************************************************
+ Put up a yes/no prompt.
+****************************************************************************/
+
+BOOL yesno(char *p)
+{
+ pstring ans;
+ printf("%s",p);
+
+ if (!fgets(ans,sizeof(ans)-1,stdin))
+ return(False);
+
+ if (*ans == 'y' || *ans == 'Y')
+ return(True);
+
+ return(False);
+}
+
+/****************************************************************************
+ Expand a pointer to be a particular size.
+****************************************************************************/
+
+void *Realloc(void *p,size_t size)
+{
+ void *ret=NULL;
+
+ if (size == 0) {
+ SAFE_FREE(p);
+ DEBUG(5,("Realloc asked for 0 bytes\n"));
+ return NULL;
+ }
+
+ if (!p)
+ ret = (void *)malloc(size);
+ else
+ ret = (void *)realloc(p,size);
+
+ if (!ret)
+ DEBUG(0,("Memory allocation error: failed to expand to %d bytes\n",(int)size));
+
+ return(ret);
+}
+
+void *Realloc_zero(void *ptr, size_t size)
+{
+ void *tptr = NULL;
+
+ tptr = Realloc(ptr, size);
+ if(tptr == NULL)
+ return NULL;
+
+ memset((char *)tptr,'\0',size);
+
+ return tptr;
+}
+
+/****************************************************************************
+ Free memory, checks for NULL.
+ Use directly SAFE_FREE()
+ Exists only because we need to pass a function pointer somewhere --SSS
+****************************************************************************/
+
+void safe_free(void *p)
+{
+ SAFE_FREE(p);
+}
+
+/****************************************************************************
+ Get my own name and IP.
+****************************************************************************/
+
+BOOL get_myname(char *my_name)
+{
+ pstring hostname;
+
+ *hostname = 0;
+
+ /* get my host name */
+ if (gethostname(hostname, sizeof(hostname)) == -1) {
+ DEBUG(0,("gethostname failed\n"));
+ return False;
+ }
+
+ /* Ensure null termination. */
+ hostname[sizeof(hostname)-1] = '\0';
+
+ if (my_name) {
+ /* split off any parts after an initial . */
+ char *p = strchr_m(hostname,'.');
+
+ if (p)
+ *p = 0;
+
+ fstrcpy(my_name,hostname);
+ }
+
+ return(True);
+}
+
+/****************************************************************************
+ Get my own canonical name, including domain.
+****************************************************************************/
+
+BOOL get_mydnsfullname(fstring my_dnsname)
+{
+ static fstring dnshostname;
+ struct hostent *hp;
+
+ if (!*dnshostname) {
+ /* get my host name */
+ if (gethostname(dnshostname, sizeof(dnshostname)) == -1) {
+ *dnshostname = '\0';
+ DEBUG(0,("gethostname failed\n"));
+ return False;
+ }
+
+ /* Ensure null termination. */
+ dnshostname[sizeof(dnshostname)-1] = '\0';
+
+ /* Ensure we get the cannonical name. */
+ if (!(hp = sys_gethostbyname(dnshostname))) {
+ *dnshostname = '\0';
+ return False;
+ }
+ fstrcpy(dnshostname, hp->h_name);
+ }
+ fstrcpy(my_dnsname, dnshostname);
+ return True;
+}
+
+/****************************************************************************
+ Get my own domain name.
+****************************************************************************/
+
+BOOL get_mydnsdomname(fstring my_domname)
+{
+ fstring domname;
+ char *p;
+
+ *my_domname = '\0';
+ if (!get_mydnsfullname(domname)) {
+ return False;
+ }
+ p = strchr_m(domname, '.');
+ if (p) {
+ p++;
+ fstrcpy(my_domname, p);
+ }
+
+ return False;
+}
+
+/****************************************************************************
+ Interpret a protocol description string, with a default.
+****************************************************************************/
+
+int interpret_protocol(const char *str,int def)
+{
+ if (strequal(str,"NT1"))
+ return(PROTOCOL_NT1);
+ if (strequal(str,"LANMAN2"))
+ return(PROTOCOL_LANMAN2);
+ if (strequal(str,"LANMAN1"))
+ return(PROTOCOL_LANMAN1);
+ if (strequal(str,"CORE"))
+ return(PROTOCOL_CORE);
+ if (strequal(str,"COREPLUS"))
+ return(PROTOCOL_COREPLUS);
+ if (strequal(str,"CORE+"))
+ return(PROTOCOL_COREPLUS);
+
+ DEBUG(0,("Unrecognised protocol level %s\n",str));
+
+ return(def);
+}
+
+/****************************************************************************
+ Return true if a string could be a pure IP address.
+****************************************************************************/
+
+BOOL is_ipaddress(const char *str)
+{
+ BOOL pure_address = True;
+ int i;
+
+ for (i=0; pure_address && str[i]; i++)
+ if (!(isdigit((int)str[i]) || str[i] == '.'))
+ pure_address = False;
+
+ /* Check that a pure number is not misinterpreted as an IP */
+ pure_address = pure_address && (strchr_m(str, '.') != NULL);
+
+ return pure_address;
+}
+
+/****************************************************************************
+ Interpret an internet address or name into an IP address in 4 byte form.
+****************************************************************************/
+
+uint32 interpret_addr(const char *str)
+{
+ struct hostent *hp;
+ uint32 res;
+
+ if (strcmp(str,"0.0.0.0") == 0)
+ return(0);
+ if (strcmp(str,"255.255.255.255") == 0)
+ return(0xFFFFFFFF);
+
+ /* if it's in the form of an IP address then get the lib to interpret it */
+ if (is_ipaddress(str)) {
+ res = inet_addr(str);
+ } else {
+ /* otherwise assume it's a network name of some sort and use
+ sys_gethostbyname */
+ if ((hp = sys_gethostbyname(str)) == 0) {
+ DEBUG(3,("sys_gethostbyname: Unknown host. %s\n",str));
+ return 0;
+ }
+
+ if(hp->h_addr == NULL) {
+ DEBUG(3,("sys_gethostbyname: host address is invalid for host %s\n",str));
+ return 0;
+ }
+ putip((char *)&res,(char *)hp->h_addr);
+ }
+
+ if (res == (uint32)-1)
+ return(0);
+
+ return(res);
+}
+
+/*******************************************************************
+ A convenient addition to interpret_addr().
+******************************************************************/
+
+struct in_addr *interpret_addr2(const char *str)
+{
+ static struct in_addr ret;
+ uint32 a = interpret_addr(str);
+ ret.s_addr = a;
+ return(&ret);
+}
+
+/*******************************************************************
+ Check if an IP is the 0.0.0.0.
+******************************************************************/
+
+BOOL is_zero_ip(struct in_addr ip)
+{
+ uint32 a;
+ putip((char *)&a,(char *)&ip);
+ return(a == 0);
+}
+
+/*******************************************************************
+ Set an IP to 0.0.0.0.
+******************************************************************/
+
+void zero_ip(struct in_addr *ip)
+{
+ static BOOL init;
+ static struct in_addr ipzero;
+
+ if (!init) {
+ ipzero = *interpret_addr2("0.0.0.0");
+ init = True;
+ }
+
+ *ip = ipzero;
+}
+
+#if (defined(HAVE_NETGROUP) && defined(WITH_AUTOMOUNT))
+/******************************************************************
+ Remove any mount options such as -rsize=2048,wsize=2048 etc.
+ Based on a fix from <Thomas.Hepper@icem.de>.
+*******************************************************************/
+
+static void strip_mount_options( pstring *str)
+{
+ if (**str == '-') {
+ char *p = *str;
+ while(*p && !isspace(*p))
+ p++;
+ while(*p && isspace(*p))
+ p++;
+ if(*p) {
+ pstring tmp_str;
+
+ pstrcpy(tmp_str, p);
+ pstrcpy(*str, tmp_str);
+ }
+ }
+}
+
+/*******************************************************************
+ Patch from jkf@soton.ac.uk
+ Split Luke's automount_server into YP lookup and string splitter
+ so can easily implement automount_path().
+ As we may end up doing both, cache the last YP result.
+*******************************************************************/
+
+#ifdef WITH_NISPLUS_HOME
+char *automount_lookup(const char *user_name)
+{
+ static fstring last_key = "";
+ static pstring last_value = "";
+
+ char *nis_map = (char *)lp_nis_home_map_name();
+
+ char buffer[NIS_MAXATTRVAL + 1];
+ nis_result *result;
+ nis_object *object;
+ entry_obj *entry;
+
+ if (strcmp(user_name, last_key)) {
+ slprintf(buffer, sizeof(buffer)-1, "[key=%s],%s", user_name, nis_map);
+ DEBUG(5, ("NIS+ querystring: %s\n", buffer));
+
+ if (result = nis_list(buffer, FOLLOW_PATH|EXPAND_NAME|HARD_LOOKUP, NULL, NULL)) {
+ if (result->status != NIS_SUCCESS) {
+ DEBUG(3, ("NIS+ query failed: %s\n", nis_sperrno(result->status)));
+ fstrcpy(last_key, ""); pstrcpy(last_value, "");
+ } else {
+ object = result->objects.objects_val;
+ if (object->zo_data.zo_type == ENTRY_OBJ) {
+ entry = &object->zo_data.objdata_u.en_data;
+ DEBUG(5, ("NIS+ entry type: %s\n", entry->en_type));
+ DEBUG(3, ("NIS+ result: %s\n", entry->en_cols.en_cols_val[1].ec_value.ec_value_val));
+
+ pstrcpy(last_value, entry->en_cols.en_cols_val[1].ec_value.ec_value_val);
+ pstring_sub(last_value, "&", user_name);
+ fstrcpy(last_key, user_name);
+ }
+ }
+ }
+ nis_freeresult(result);
+ }
+
+ strip_mount_options(&last_value);
+
+ DEBUG(4, ("NIS+ Lookup: %s resulted in %s\n", user_name, last_value));
+ return last_value;
+}
+#else /* WITH_NISPLUS_HOME */
+
+char *automount_lookup(const char *user_name)
+{
+ static fstring last_key = "";
+ static pstring last_value = "";
+
+ int nis_error; /* returned by yp all functions */
+ char *nis_result; /* yp_match inits this */
+ int nis_result_len; /* and set this */
+ char *nis_domain; /* yp_get_default_domain inits this */
+ char *nis_map = (char *)lp_nis_home_map_name();
+
+ if ((nis_error = yp_get_default_domain(&nis_domain)) != 0) {
+ DEBUG(3, ("YP Error: %s\n", yperr_string(nis_error)));
+ return last_value;
+ }
+
+ DEBUG(5, ("NIS Domain: %s\n", nis_domain));
+
+ if (!strcmp(user_name, last_key)) {
+ nis_result = last_value;
+ nis_result_len = strlen(last_value);
+ nis_error = 0;
+ } else {
+ if ((nis_error = yp_match(nis_domain, nis_map, user_name, strlen(user_name),
+ &nis_result, &nis_result_len)) == 0) {
+ if (!nis_error && nis_result_len >= sizeof(pstring)) {
+ nis_result_len = sizeof(pstring)-1;
+ }
+ fstrcpy(last_key, user_name);
+ strncpy(last_value, nis_result, nis_result_len);
+ last_value[nis_result_len] = '\0';
+ strip_mount_options(&last_value);
+
+ } else if(nis_error == YPERR_KEY) {
+
+ /* If Key lookup fails user home server is not in nis_map
+ use default information for server, and home directory */
+ last_value[0] = 0;
+ DEBUG(3, ("YP Key not found: while looking up \"%s\" in map \"%s\"\n",
+ user_name, nis_map));
+ DEBUG(3, ("using defaults for server and home directory\n"));
+ } else {
+ DEBUG(3, ("YP Error: \"%s\" while looking up \"%s\" in map \"%s\"\n",
+ yperr_string(nis_error), user_name, nis_map));
+ }
+ }
+
+ DEBUG(4, ("YP Lookup: %s resulted in %s\n", user_name, last_value));
+ return last_value;
+}
+#endif /* WITH_NISPLUS_HOME */
+#endif
+
+/*******************************************************************
+ Are two IPs on the same subnet?
+********************************************************************/
+
+BOOL same_net(struct in_addr ip1,struct in_addr ip2,struct in_addr mask)
+{
+ uint32 net1,net2,nmask;
+
+ nmask = ntohl(mask.s_addr);
+ net1 = ntohl(ip1.s_addr);
+ net2 = ntohl(ip2.s_addr);
+
+ return((net1 & nmask) == (net2 & nmask));
+}
+
+
+/****************************************************************************
+ Check if a process exists. Does this work on all unixes?
+****************************************************************************/
+
+BOOL process_exists(pid_t pid)
+{
+ /* Doing kill with a non-positive pid causes messages to be
+ * sent to places we don't want. */
+ SMB_ASSERT(pid > 0);
+ return(kill(pid,0) == 0 || errno != ESRCH);
+}
+
+/*******************************************************************
+ Convert a uid into a user name.
+********************************************************************/
+
+const char *uidtoname(uid_t uid)
+{
+ static fstring name;
+ struct passwd *pass;
+
+ pass = getpwuid_alloc(uid);
+ if (pass) {
+ fstrcpy(name, pass->pw_name);
+ passwd_free(&pass);
+ } else {
+ slprintf(name, sizeof(name) - 1, "%ld",(long int)uid);
+ }
+ return name;
+}
+
+
+/*******************************************************************
+ Convert a gid into a group name.
+********************************************************************/
+
+char *gidtoname(gid_t gid)
+{
+ static fstring name;
+ struct group *grp;
+
+ grp = getgrgid(gid);
+ if (grp)
+ return(grp->gr_name);
+ slprintf(name,sizeof(name) - 1, "%d",(int)gid);
+ return(name);
+}
+
+/*******************************************************************
+ Convert a user name into a uid.
+********************************************************************/
+
+uid_t nametouid(const char *name)
+{
+ struct passwd *pass;
+ char *p;
+ uid_t u;
+
+ pass = getpwnam_alloc(name);
+ if (pass) {
+ u = pass->pw_uid;
+ passwd_free(&pass);
+ return u;
+ }
+
+ u = (uid_t)strtol(name, &p, 0);
+ if ((p != name) && (*p == '\0'))
+ return u;
+
+ return (uid_t)-1;
+}
+
+/*******************************************************************
+ Convert a name to a gid_t if possible. Return -1 if not a group.
+********************************************************************/
+
+gid_t nametogid(const char *name)
+{
+ struct group *grp;
+ char *p;
+ gid_t g;
+
+ g = (gid_t)strtol(name, &p, 0);
+ if ((p != name) && (*p == '\0'))
+ return g;
+
+ grp = sys_getgrnam(name);
+ if (grp)
+ return(grp->gr_gid);
+ return (gid_t)-1;
+}
+
+/*******************************************************************
+ legacy wrapper for smb_panic2()
+********************************************************************/
+void smb_panic( const char *why )
+{
+ smb_panic2( why, True );
+}
+
+/*******************************************************************
+ Something really nasty happened - panic !
+********************************************************************/
+
+#ifdef HAVE_LIBEXC_H
+#include <libexc.h>
+#endif
+
+void smb_panic2(const char *why, BOOL decrement_pid_count )
+{
+ char *cmd;
+ int result;
+#ifdef HAVE_BACKTRACE_SYMBOLS
+ void *backtrace_stack[BACKTRACE_STACK_SIZE];
+ size_t backtrace_size;
+ char **backtrace_strings;
+#endif
+
+#ifdef DEVELOPER
+ {
+ extern char *global_clobber_region_function;
+ extern unsigned int global_clobber_region_line;
+
+ if (global_clobber_region_function) {
+ DEBUG(0,("smb_panic: clobber_region() last called from [%s(%u)]\n",
+ global_clobber_region_function,
+ global_clobber_region_line));
+ }
+ }
+#endif
+
+ /* only smbd needs to decrement the smbd counter in connections.tdb */
+ if ( decrement_pid_count )
+ decrement_smbd_process_count();
+
+ cmd = lp_panic_action();
+ if (cmd && *cmd) {
+ DEBUG(0, ("smb_panic(): calling panic action [%s]\n", cmd));
+ result = system(cmd);
+
+ if (result == -1)
+ DEBUG(0, ("smb_panic(): fork failed in panic action: %s\n",
+ strerror(errno)));
+ else
+ DEBUG(0, ("smb_panic(): action returned status %d\n",
+ WEXITSTATUS(result)));
+ }
+ DEBUG(0,("PANIC: %s\n", why));
+
+#ifdef HAVE_BACKTRACE_SYMBOLS
+ /* get the backtrace (stack frames) */
+ backtrace_size = backtrace(backtrace_stack,BACKTRACE_STACK_SIZE);
+ backtrace_strings = backtrace_symbols(backtrace_stack, backtrace_size);
+
+ DEBUG(0, ("BACKTRACE: %lu stack frames:\n",
+ (unsigned long)backtrace_size));
+
+ if (backtrace_strings) {
+ int i;
+
+ for (i = 0; i < backtrace_size; i++)
+ DEBUGADD(0, (" #%u %s\n", i, backtrace_strings[i]));
+
+ /* Leak the backtrace_strings, rather than risk what free() might do */
+ }
+
+#elif HAVE_LIBEXC
+
+#define NAMESIZE 32 /* Arbitrary */
+
+ /* The IRIX libexc library provides an API for unwinding the stack. See
+ * libexc(3) for details. Apparantly trace_back_stack leaks memory, but
+ * since we are about to abort anyway, it hardly matters.
+ *
+ * Note that if we paniced due to a SIGSEGV or SIGBUS (or similar) this
+ * will fail with a nasty message upon failing to open the /proc entry.
+ */
+ {
+ __uint64_t addrs[BACKTRACE_STACK_SIZE];
+ char * names[BACKTRACE_STACK_SIZE];
+ char namebuf[BACKTRACE_STACK_SIZE * NAMESIZE];
+
+ int i;
+ int levels;
+
+ ZERO_ARRAY(addrs);
+ ZERO_ARRAY(names);
+ ZERO_ARRAY(namebuf);
+
+ for (i = 0; i < BACKTRACE_STACK_SIZE; i++) {
+ names[i] = namebuf + (i * NAMESIZE);
+ }
+
+ levels = trace_back_stack(0, addrs, names,
+ BACKTRACE_STACK_SIZE, NAMESIZE);
+
+ DEBUG(0, ("BACKTRACE: %d stack frames:\n", levels));
+ for (i = 0; i < levels; i++) {
+ DEBUGADD(0, (" #%d 0x%llx %s\n", i, addrs[i], names[i]));
+ }
+ }
+#undef NAMESIZE
+#endif
+
+ dbgflush();
+ abort();
+}
+
+/*******************************************************************
+ A readdir wrapper which just returns the file name.
+ ********************************************************************/
+
+const char *readdirname(DIR *p)
+{
+ SMB_STRUCT_DIRENT *ptr;
+ char *dname;
+
+ if (!p)
+ return(NULL);
+
+ ptr = (SMB_STRUCT_DIRENT *)sys_readdir(p);
+ if (!ptr)
+ return(NULL);
+
+ dname = ptr->d_name;
+
+#ifdef NEXT2
+ if (telldir(p) < 0)
+ return(NULL);
+#endif
+
+#ifdef HAVE_BROKEN_READDIR
+ /* using /usr/ucb/cc is BAD */
+ dname = dname - 2;
+#endif
+
+ {
+ static pstring buf;
+ int len = NAMLEN(ptr);
+ memcpy(buf, dname, len);
+ buf[len] = 0;
+ dname = buf;
+ }
+
+ return(dname);
+}
+
+/*******************************************************************
+ Utility function used to decide if the last component
+ of a path matches a (possibly wildcarded) entry in a namelist.
+********************************************************************/
+
+BOOL is_in_path(const char *name, name_compare_entry *namelist)
+{
+ pstring last_component;
+ char *p;
+
+ DEBUG(8, ("is_in_path: %s\n", name));
+
+ /* if we have no list it's obviously not in the path */
+ if((namelist == NULL ) || ((namelist != NULL) && (namelist[0].name == NULL))) {
+ DEBUG(8,("is_in_path: no name list.\n"));
+ return False;
+ }
+
+ /* Get the last component of the unix name. */
+ p = strrchr_m(name, '/');
+ strncpy(last_component, p ? ++p : name, sizeof(last_component)-1);
+ last_component[sizeof(last_component)-1] = '\0';
+
+ for(; namelist->name != NULL; namelist++) {
+ if(namelist->is_wild) {
+ if (mask_match(last_component, namelist->name, case_sensitive)) {
+ DEBUG(8,("is_in_path: mask match succeeded\n"));
+ return True;
+ }
+ } else {
+ if((case_sensitive && (strcmp(last_component, namelist->name) == 0))||
+ (!case_sensitive && (StrCaseCmp(last_component, namelist->name) == 0))) {
+ DEBUG(8,("is_in_path: match succeeded\n"));
+ return True;
+ }
+ }
+ }
+ DEBUG(8,("is_in_path: match not found\n"));
+
+ return False;
+}
+
+/*******************************************************************
+ Strip a '/' separated list into an array of
+ name_compare_enties structures suitable for
+ passing to is_in_path(). We do this for
+ speed so we can pre-parse all the names in the list
+ and don't do it for each call to is_in_path().
+ namelist is modified here and is assumed to be
+ a copy owned by the caller.
+ We also check if the entry contains a wildcard to
+ remove a potentially expensive call to mask_match
+ if possible.
+********************************************************************/
+
+void set_namearray(name_compare_entry **ppname_array, char *namelist)
+{
+ char *name_end;
+ char *nameptr = namelist;
+ int num_entries = 0;
+ int i;
+
+ (*ppname_array) = NULL;
+
+ if((nameptr == NULL ) || ((nameptr != NULL) && (*nameptr == '\0')))
+ return;
+
+ /* We need to make two passes over the string. The
+ first to count the number of elements, the second
+ to split it.
+ */
+
+ while(*nameptr) {
+ if ( *nameptr == '/' ) {
+ /* cope with multiple (useless) /s) */
+ nameptr++;
+ continue;
+ }
+ /* find the next / */
+ name_end = strchr_m(nameptr, '/');
+
+ /* oops - the last check for a / didn't find one. */
+ if (name_end == NULL)
+ break;
+
+ /* next segment please */
+ nameptr = name_end + 1;
+ num_entries++;
+ }
+
+ if(num_entries == 0)
+ return;
+
+ if(( (*ppname_array) = (name_compare_entry *)malloc(
+ (num_entries + 1) * sizeof(name_compare_entry))) == NULL) {
+ DEBUG(0,("set_namearray: malloc fail\n"));
+ return;
+ }
+
+ /* Now copy out the names */
+ nameptr = namelist;
+ i = 0;
+ while(*nameptr) {
+ if ( *nameptr == '/' ) {
+ /* cope with multiple (useless) /s) */
+ nameptr++;
+ continue;
+ }
+ /* find the next / */
+ if ((name_end = strchr_m(nameptr, '/')) != NULL)
+ *name_end = 0;
+
+ /* oops - the last check for a / didn't find one. */
+ if(name_end == NULL)
+ break;
+
+ (*ppname_array)[i].is_wild = ms_has_wild(nameptr);
+ if(((*ppname_array)[i].name = strdup(nameptr)) == NULL) {
+ DEBUG(0,("set_namearray: malloc fail (1)\n"));
+ return;
+ }
+
+ /* next segment please */
+ nameptr = name_end + 1;
+ i++;
+ }
+
+ (*ppname_array)[i].name = NULL;
+
+ return;
+}
+
+/****************************************************************************
+ Routine to free a namearray.
+****************************************************************************/
+
+void free_namearray(name_compare_entry *name_array)
+{
+ int i;
+
+ if(name_array == NULL)
+ return;
+
+ for(i=0; name_array[i].name!=NULL; i++)
+ SAFE_FREE(name_array[i].name);
+ SAFE_FREE(name_array);
+}
+
+/****************************************************************************
+ Simple routine to do POSIX file locking. Cruft in NFS and 64->32 bit mapping
+ is dealt with in posix.c
+****************************************************************************/
+
+BOOL fcntl_lock(int fd, int op, SMB_OFF_T offset, SMB_OFF_T count, int type)
+{
+ SMB_STRUCT_FLOCK lock;
+ int ret;
+
+ DEBUG(8,("fcntl_lock %d %d %.0f %.0f %d\n",fd,op,(double)offset,(double)count,type));
+
+ lock.l_type = type;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = offset;
+ lock.l_len = count;
+ lock.l_pid = 0;
+
+ ret = sys_fcntl_ptr(fd,op,&lock);
+
+ if (ret == -1 && errno != 0)
+ DEBUG(3,("fcntl_lock: fcntl lock gave errno %d (%s)\n",errno,strerror(errno)));
+
+ /* a lock query */
+ if (op == SMB_F_GETLK) {
+ if ((ret != -1) &&
+ (lock.l_type != F_UNLCK) &&
+ (lock.l_pid != 0) &&
+ (lock.l_pid != sys_getpid())) {
+ DEBUG(3,("fcntl_lock: fd %d is locked by pid %d\n",fd,(int)lock.l_pid));
+ return(True);
+ }
+
+ /* it must be not locked or locked by me */
+ return(False);
+ }
+
+ /* a lock set or unset */
+ if (ret == -1) {
+ DEBUG(3,("fcntl_lock: lock failed at offset %.0f count %.0f op %d type %d (%s)\n",
+ (double)offset,(double)count,op,type,strerror(errno)));
+ return(False);
+ }
+
+ /* everything went OK */
+ DEBUG(8,("fcntl_lock: Lock call successful\n"));
+
+ return(True);
+}
+
+/*******************************************************************
+ Is the name specified one of my netbios names.
+ Returns true if it is equal, false otherwise.
+********************************************************************/
+
+BOOL is_myname(const char *s)
+{
+ int n;
+ BOOL ret = False;
+
+ for (n=0; my_netbios_names(n); n++) {
+ if (strequal(my_netbios_names(n), s)) {
+ ret=True;
+ break;
+ }
+ }
+ DEBUG(8, ("is_myname(\"%s\") returns %d\n", s, ret));
+ return(ret);
+}
+
+/********************************************************************
+ Return only the first IP address of our configured interfaces
+ as a string
+ *******************************************************************/
+
+const char* get_my_primary_ip (void)
+{
+ static fstring ip_string;
+ int n;
+ struct iface_struct nics[MAX_INTERFACES];
+
+ if ((n=get_interfaces(nics, MAX_INTERFACES)) <= 0)
+ return NULL;
+
+ fstrcpy(ip_string, inet_ntoa(nics[0].ip));
+ return ip_string;
+}
+
+BOOL is_myname_or_ipaddr(const char *s)
+{
+ /* optimize for the common case */
+ if (strequal(s, global_myname()))
+ return True;
+
+ /* maybe its an IP address? */
+ if (is_ipaddress(s)) {
+ struct iface_struct nics[MAX_INTERFACES];
+ int i, n;
+ uint32 ip;
+
+ ip = interpret_addr(s);
+ if ((ip==0) || (ip==0xffffffff))
+ return False;
+
+ n = get_interfaces(nics, MAX_INTERFACES);
+ for (i=0; i<n; i++) {
+ if (ip == nics[i].ip.s_addr)
+ return True;
+ }
+ }
+
+ /* check for an alias */
+ if (is_myname(s))
+ return True;
+
+ /* no match */
+ return False;
+}
+
+/*******************************************************************
+ Is the name specified our workgroup/domain.
+ Returns true if it is equal, false otherwise.
+********************************************************************/
+
+BOOL is_myworkgroup(const char *s)
+{
+ BOOL ret = False;
+
+ if (strequal(s, lp_workgroup())) {
+ ret=True;
+ }
+
+ DEBUG(8, ("is_myworkgroup(\"%s\") returns %d\n", s, ret));
+ return(ret);
+}
+
+/*******************************************************************
+ we distinguish between 2K and XP by the "Native Lan Manager" string
+ WinXP => "Windows 2002 5.1"
+ Win2k => "Windows 2000 5.0"
+ NT4 => "Windows NT 4.0"
+ Win9x => "Windows 4.0"
+ Windows 2003 doesn't set the native lan manager string but
+ they do set the domain to "Windows 2003 5.2" (probably a bug).
+********************************************************************/
+
+void ra_lanman_string( const char *native_lanman )
+{
+ if ( strcmp( native_lanman, "Windows 2002 5.1" ) == 0 )
+ set_remote_arch( RA_WINXP );
+ else if ( strcmp( native_lanman, "Windows Server 2003 5.2" ) == 0 )
+ set_remote_arch( RA_WIN2K3 );
+}
+
+/*******************************************************************
+ Set the horrid remote_arch string based on an enum.
+********************************************************************/
+
+void set_remote_arch(enum remote_arch_types type)
+{
+ extern fstring remote_arch;
+ ra_type = type;
+ switch( type ) {
+ case RA_WFWG:
+ fstrcpy(remote_arch, "WfWg");
+ break;
+ case RA_OS2:
+ fstrcpy(remote_arch, "OS2");
+ break;
+ case RA_WIN95:
+ fstrcpy(remote_arch, "Win95");
+ break;
+ case RA_WINNT:
+ fstrcpy(remote_arch, "WinNT");
+ break;
+ case RA_WIN2K:
+ fstrcpy(remote_arch, "Win2K");
+ break;
+ case RA_WINXP:
+ fstrcpy(remote_arch, "WinXP");
+ break;
+ case RA_WIN2K3:
+ fstrcpy(remote_arch, "Win2K3");
+ break;
+ case RA_SAMBA:
+ fstrcpy(remote_arch,"Samba");
+ break;
+ default:
+ ra_type = RA_UNKNOWN;
+ fstrcpy(remote_arch, "UNKNOWN");
+ break;
+ }
+
+ DEBUG(10,("set_remote_arch: Client arch is \'%s\'\n", remote_arch));
+}
+
+/*******************************************************************
+ Get the remote_arch type.
+********************************************************************/
+
+enum remote_arch_types get_remote_arch(void)
+{
+ return ra_type;
+}
+
+void print_asc(int level, const unsigned char *buf,int len)
+{
+ int i;
+ for (i=0;i<len;i++)
+ DEBUG(level,("%c", isprint(buf[i])?buf[i]:'.'));
+}
+
+void dump_data(int level, const char *buf1,int len)
+{
+ const unsigned char *buf = (const unsigned char *)buf1;
+ int i=0;
+ if (len<=0) return;
+
+ if (!DEBUGLVL(level)) return;
+
+ DEBUGADD(level,("[%03X] ",i));
+ for (i=0;i<len;) {
+ DEBUGADD(level,("%02X ",(int)buf[i]));
+ i++;
+ if (i%8 == 0) DEBUGADD(level,(" "));
+ if (i%16 == 0) {
+ print_asc(level,&buf[i-16],8); DEBUGADD(level,(" "));
+ print_asc(level,&buf[i-8],8); DEBUGADD(level,("\n"));
+ if (i<len) DEBUGADD(level,("[%03X] ",i));
+ }
+ }
+ if (i%16) {
+ int n;
+ n = 16 - (i%16);
+ DEBUGADD(level,(" "));
+ if (n>8) DEBUGADD(level,(" "));
+ while (n--) DEBUGADD(level,(" "));
+ n = MIN(8,i%16);
+ print_asc(level,&buf[i-(i%16)],n); DEBUGADD(level,( " " ));
+ n = (i%16) - n;
+ if (n>0) print_asc(level,&buf[i-n],n);
+ DEBUGADD(level,("\n"));
+ }
+}
+
+void dump_data_pw(const char *msg, const uchar * data, size_t len)
+{
+#ifdef DEBUG_PASSWORD
+ DEBUG(11, ("%s", msg));
+ if (data != NULL && len > 0)
+ {
+ dump_data(11, data, len);
+ }
+#endif
+}
+
+char *tab_depth(int depth)
+{
+ static pstring spaces;
+ memset(spaces, ' ', depth * 4);
+ spaces[depth * 4] = 0;
+ return spaces;
+}
+
+/*****************************************************************************
+ Provide a checksum on a string
+
+ Input: s - the null-terminated character string for which the checksum
+ will be calculated.
+
+ Output: The checksum value calculated for s.
+*****************************************************************************/
+
+int str_checksum(const char *s)
+{
+ int res = 0;
+ int c;
+ int i=0;
+
+ while(*s) {
+ c = *s;
+ res ^= (c << (i % 15)) ^ (c >> (15-(i%15)));
+ s++;
+ i++;
+ }
+ return(res);
+}
+
+/*****************************************************************
+ Zero a memory area then free it. Used to catch bugs faster.
+*****************************************************************/
+
+void zero_free(void *p, size_t size)
+{
+ memset(p, 0, size);
+ SAFE_FREE(p);
+}
+
+/*****************************************************************
+ Set our open file limit to a requested max and return the limit.
+*****************************************************************/
+
+int set_maxfiles(int requested_max)
+{
+#if (defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE))
+ struct rlimit rlp;
+ int saved_current_limit;
+
+ if(getrlimit(RLIMIT_NOFILE, &rlp)) {
+ DEBUG(0,("set_maxfiles: getrlimit (1) for RLIMIT_NOFILE failed with error %s\n",
+ strerror(errno) ));
+ /* just guess... */
+ return requested_max;
+ }
+
+ /*
+ * Set the fd limit to be real_max_open_files + MAX_OPEN_FUDGEFACTOR to
+ * account for the extra fd we need
+ * as well as the log files and standard
+ * handles etc. Save the limit we want to set in case
+ * we are running on an OS that doesn't support this limit (AIX)
+ * which always returns RLIM_INFINITY for rlp.rlim_max.
+ */
+
+ /* Try raising the hard (max) limit to the requested amount. */
+
+#if defined(RLIM_INFINITY)
+ if (rlp.rlim_max != RLIM_INFINITY) {
+ int orig_max = rlp.rlim_max;
+
+ if ( rlp.rlim_max < requested_max )
+ rlp.rlim_max = requested_max;
+
+ /* This failing is not an error - many systems (Linux) don't
+ support our default request of 10,000 open files. JRA. */
+
+ if(setrlimit(RLIMIT_NOFILE, &rlp)) {
+ DEBUG(3,("set_maxfiles: setrlimit for RLIMIT_NOFILE for %d max files failed with error %s\n",
+ (int)rlp.rlim_max, strerror(errno) ));
+
+ /* Set failed - restore original value from get. */
+ rlp.rlim_max = orig_max;
+ }
+ }
+#endif
+
+ /* Now try setting the soft (current) limit. */
+
+ saved_current_limit = rlp.rlim_cur = MIN(requested_max,rlp.rlim_max);
+
+ if(setrlimit(RLIMIT_NOFILE, &rlp)) {
+ DEBUG(0,("set_maxfiles: setrlimit for RLIMIT_NOFILE for %d files failed with error %s\n",
+ (int)rlp.rlim_cur, strerror(errno) ));
+ /* just guess... */
+ return saved_current_limit;
+ }
+
+ if(getrlimit(RLIMIT_NOFILE, &rlp)) {
+ DEBUG(0,("set_maxfiles: getrlimit (2) for RLIMIT_NOFILE failed with error %s\n",
+ strerror(errno) ));
+ /* just guess... */
+ return saved_current_limit;
+ }
+
+#if defined(RLIM_INFINITY)
+ if(rlp.rlim_cur == RLIM_INFINITY)
+ return saved_current_limit;
+#endif
+
+ if((int)rlp.rlim_cur > saved_current_limit)
+ return saved_current_limit;
+
+ return rlp.rlim_cur;
+#else /* !defined(HAVE_GETRLIMIT) || !defined(RLIMIT_NOFILE) */
+ /*
+ * No way to know - just guess...
+ */
+ return requested_max;
+#endif
+}
+
+/*****************************************************************
+ Splits out the start of the key (HKLM or HKU) and the rest of the key.
+*****************************************************************/
+
+BOOL reg_split_key(const char *full_keyname, uint32 *reg_type, char *key_name)
+{
+ pstring tmp;
+
+ if (!next_token(&full_keyname, tmp, "\\", sizeof(tmp)))
+ return False;
+
+ (*reg_type) = 0;
+
+ DEBUG(10, ("reg_split_key: hive %s\n", tmp));
+
+ if (strequal(tmp, "HKLM") || strequal(tmp, "HKEY_LOCAL_MACHINE"))
+ (*reg_type) = HKEY_LOCAL_MACHINE;
+ else if (strequal(tmp, "HKU") || strequal(tmp, "HKEY_USERS"))
+ (*reg_type) = HKEY_USERS;
+ else {
+ DEBUG(10,("reg_split_key: unrecognised hive key %s\n", tmp));
+ return False;
+ }
+
+ if (next_token(&full_keyname, tmp, "\n\r", sizeof(tmp)))
+ fstrcpy(key_name, tmp);
+ else
+ key_name[0] = 0;
+
+ DEBUG(10, ("reg_split_key: name %s\n", key_name));
+
+ return True;
+}
+
+/*****************************************************************
+ Possibly replace mkstemp if it is broken.
+*****************************************************************/
+
+int smb_mkstemp(char *template)
+{
+#if HAVE_SECURE_MKSTEMP
+ return mkstemp(template);
+#else
+ /* have a reasonable go at emulating it. Hope that
+ the system mktemp() isn't completly hopeless */
+ char *p = mktemp(template);
+ if (!p)
+ return -1;
+ return open(p, O_CREAT|O_EXCL|O_RDWR, 0600);
+#endif
+}
+
+/*****************************************************************
+ malloc that aborts with smb_panic on fail or zero size.
+ *****************************************************************/
+
+void *smb_xmalloc(size_t size)
+{
+ void *p;
+ if (size == 0)
+ smb_panic("smb_xmalloc: called with zero size.\n");
+ if ((p = malloc(size)) == NULL) {
+ DEBUG(0, ("smb_xmalloc() failed to allocate %lu bytes\n", (unsigned long)size));
+ smb_panic("smb_xmalloc: malloc fail.\n");
+ }
+ return p;
+}
+
+/**
+ Memdup with smb_panic on fail.
+**/
+
+void *smb_xmemdup(const void *p, size_t size)
+{
+ void *p2;
+ p2 = smb_xmalloc(size);
+ memcpy(p2, p, size);
+ return p2;
+}
+
+/**
+ strdup that aborts on malloc fail.
+**/
+
+char *smb_xstrdup(const char *s)
+{
+ char *s1 = strdup(s);
+ if (!s1)
+ smb_panic("smb_xstrdup: malloc fail\n");
+ return s1;
+}
+
+/**
+ strndup that aborts on malloc fail.
+**/
+
+char *smb_xstrndup(const char *s, size_t n)
+{
+ char *s1 = strndup(s, n);
+ if (!s1)
+ smb_panic("smb_xstrndup: malloc fail\n");
+ return s1;
+}
+
+/*
+ vasprintf that aborts on malloc fail
+*/
+
+ int smb_xvasprintf(char **ptr, const char *format, va_list ap)
+{
+ int n;
+ va_list ap2;
+
+ VA_COPY(ap2, ap);
+
+ n = vasprintf(ptr, format, ap2);
+ if (n == -1 || ! *ptr)
+ smb_panic("smb_xvasprintf: out of memory");
+ return n;
+}
+
+/*****************************************************************
+ Like strdup but for memory.
+*****************************************************************/
+
+void *memdup(const void *p, size_t size)
+{
+ void *p2;
+ if (size == 0)
+ return NULL;
+ p2 = malloc(size);
+ if (!p2)
+ return NULL;
+ memcpy(p2, p, size);
+ return p2;
+}
+
+/*****************************************************************
+ Get local hostname and cache result.
+*****************************************************************/
+
+char *myhostname(void)
+{
+ static pstring ret;
+ if (ret[0] == 0)
+ get_myname(ret);
+ return ret;
+}
+
+/*****************************************************************
+ A useful function for returning a path in the Samba lock directory.
+*****************************************************************/
+
+char *lock_path(const char *name)
+{
+ static pstring fname;
+
+ pstrcpy(fname,lp_lockdir());
+ trim_char(fname,'\0','/');
+
+ if (!directory_exist(fname,NULL))
+ mkdir(fname,0755);
+
+ pstrcat(fname,"/");
+ pstrcat(fname,name);
+
+ return fname;
+}
+
+/*****************************************************************
+ A useful function for returning a path in the Samba pid directory.
+*****************************************************************/
+
+char *pid_path(const char *name)
+{
+ static pstring fname;
+
+ pstrcpy(fname,lp_piddir());
+ trim_char(fname,'\0','/');
+
+ if (!directory_exist(fname,NULL))
+ mkdir(fname,0755);
+
+ pstrcat(fname,"/");
+ pstrcat(fname,name);
+
+ return fname;
+}
+
+/**
+ * @brief Returns an absolute path to a file in the Samba lib directory.
+ *
+ * @param name File to find, relative to LIBDIR.
+ *
+ * @retval Pointer to a static #pstring containing the full path.
+ **/
+
+char *lib_path(const char *name)
+{
+ static pstring fname;
+ fstr_sprintf(fname, "%s/%s", dyn_LIBDIR, name);
+ return fname;
+}
+
+/**
+ * @brief Returns the platform specific shared library extension.
+ *
+ * @retval Pointer to a static #fstring containing the extension.
+ **/
+
+const char *shlib_ext(void)
+{
+ return dyn_SHLIBEXT;
+}
+
+/*******************************************************************
+ Given a filename - get its directory name
+ NB: Returned in static storage. Caveats:
+ o Not safe in thread environment.
+ o Caller must not free.
+ o If caller wishes to preserve, they should copy.
+********************************************************************/
+
+char *parent_dirname(const char *path)
+{
+ static pstring dirpath;
+ char *p;
+
+ if (!path)
+ return(NULL);
+
+ pstrcpy(dirpath, path);
+ p = strrchr_m(dirpath, '/'); /* Find final '/', if any */
+ if (!p) {
+ pstrcpy(dirpath, "."); /* No final "/", so dir is "." */
+ } else {
+ if (p == dirpath)
+ ++p; /* For root "/", leave "/" in place */
+ *p = '\0';
+ }
+ return dirpath;
+}
+
+
+/*******************************************************************
+ Determine if a pattern contains any Microsoft wildcard characters.
+*******************************************************************/
+
+BOOL ms_has_wild(const char *s)
+{
+ char c;
+ while ((c = *s++)) {
+ switch (c) {
+ case '*':
+ case '?':
+ case '<':
+ case '>':
+ case '"':
+ return True;
+ }
+ }
+ return False;
+}
+
+BOOL ms_has_wild_w(const smb_ucs2_t *s)
+{
+ smb_ucs2_t c;
+ if (!s) return False;
+ while ((c = *s++)) {
+ switch (c) {
+ case UCS2_CHAR('*'):
+ case UCS2_CHAR('?'):
+ case UCS2_CHAR('<'):
+ case UCS2_CHAR('>'):
+ case UCS2_CHAR('"'):
+ return True;
+ }
+ }
+ return False;
+}
+
+/*******************************************************************
+ A wrapper that handles case sensitivity and the special handling
+ of the ".." name.
+*******************************************************************/
+
+BOOL mask_match(const char *string, char *pattern, BOOL is_case_sensitive)
+{
+ if (strcmp(string,"..") == 0)
+ string = ".";
+ if (strcmp(pattern,".") == 0)
+ return False;
+
+ return ms_fnmatch(pattern, string, Protocol, is_case_sensitive) == 0;
+}
+
+/*******************************************************************
+ A wrapper that handles a list of patters and calls mask_match()
+ on each. Returns True if any of the patterns match.
+*******************************************************************/
+
+BOOL mask_match_list(const char *string, char **list, int listLen, BOOL is_case_sensitive)
+{
+ while (listLen-- > 0) {
+ if (mask_match(string, *list++, is_case_sensitive))
+ return True;
+ }
+ return False;
+}
+
+/*********************************************************
+ Recursive routine that is called by unix_wild_match.
+*********************************************************/
+
+static BOOL unix_do_match(const char *regexp, const char *str)
+{
+ const char *p;
+
+ for( p = regexp; *p && *str; ) {
+
+ switch(*p) {
+ case '?':
+ str++;
+ p++;
+ break;
+
+ case '*':
+
+ /*
+ * Look for a character matching
+ * the one after the '*'.
+ */
+ p++;
+ if(!*p)
+ return True; /* Automatic match */
+ while(*str) {
+
+ while(*str && (*p != *str))
+ str++;
+
+ /*
+ * Patch from weidel@multichart.de. In the case of the regexp
+ * '*XX*' we want to ensure there are at least 2 'X' characters
+ * in the string after the '*' for a match to be made.
+ */
+
+ {
+ int matchcount=0;
+
+ /*
+ * Eat all the characters that match, but count how many there were.
+ */
+
+ while(*str && (*p == *str)) {
+ str++;
+ matchcount++;
+ }
+
+ /*
+ * Now check that if the regexp had n identical characters that
+ * matchcount had at least that many matches.
+ */
+
+ while ( *(p+1) && (*(p+1) == *p)) {
+ p++;
+ matchcount--;
+ }
+
+ if ( matchcount <= 0 )
+ return False;
+ }
+
+ str--; /* We've eaten the match char after the '*' */
+
+ if(unix_do_match(p, str))
+ return True;
+
+ if(!*str)
+ return False;
+ else
+ str++;
+ }
+ return False;
+
+ default:
+ if(*str != *p)
+ return False;
+ str++;
+ p++;
+ break;
+ }
+ }
+
+ if(!*p && !*str)
+ return True;
+
+ if (!*p && str[0] == '.' && str[1] == 0)
+ return(True);
+
+ if (!*str && *p == '?') {
+ while (*p == '?')
+ p++;
+ return(!*p);
+ }
+
+ if(!*str && (*p == '*' && p[1] == '\0'))
+ return True;
+
+ return False;
+}
+
+/*******************************************************************
+ Simple case insensitive interface to a UNIX wildcard matcher.
+*******************************************************************/
+
+BOOL unix_wild_match(const char *pattern, const char *string)
+{
+ pstring p2, s2;
+ char *p;
+
+ pstrcpy(p2, pattern);
+ pstrcpy(s2, string);
+ strlower_m(p2);
+ strlower_m(s2);
+
+ /* Remove any *? and ** from the pattern as they are meaningless */
+ for(p = p2; *p; p++)
+ while( *p == '*' && (p[1] == '?' ||p[1] == '*'))
+ pstrcpy( &p[1], &p[2]);
+
+ if (strequal(p2,"*"))
+ return True;
+
+ return unix_do_match(p2, s2) == 0;
+}
+
+
+#ifdef __INSURE__
+
+/*******************************************************************
+This routine is a trick to immediately catch errors when debugging
+with insure. A xterm with a gdb is popped up when insure catches
+a error. It is Linux specific.
+********************************************************************/
+
+int _Insure_trap_error(int a1, int a2, int a3, int a4, int a5, int a6)
+{
+ static int (*fn)();
+ int ret;
+ char pidstr[10];
+ /* you can get /usr/bin/backtrace from
+ http://samba.org/ftp/unpacked/junkcode/backtrace */
+ pstring cmd = "/usr/bin/backtrace %d";
+
+ slprintf(pidstr, sizeof(pidstr)-1, "%d", sys_getpid());
+ pstring_sub(cmd, "%d", pidstr);
+
+ if (!fn) {
+ static void *h;
+ h = dlopen("/usr/local/parasoft/insure++lite/lib.linux2/libinsure.so", RTLD_LAZY);
+ fn = dlsym(h, "_Insure_trap_error");
+
+ if (!h || h == _Insure_trap_error) {
+ h = dlopen("/usr/local/parasoft/lib.linux2/libinsure.so", RTLD_LAZY);
+ fn = dlsym(h, "_Insure_trap_error");
+ }
+ }
+
+ ret = fn(a1, a2, a3, a4, a5, a6);
+
+ system(cmd);
+
+ return ret;
+}
+#endif
diff --git a/source/lib/util_file.c b/source/lib/util_file.c
new file mode 100644
index 00000000000..bd505ac921c
--- /dev/null
+++ b/source/lib/util_file.c
@@ -0,0 +1,606 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * SMB parameters and setup
+ * Copyright (C) Andrew Tridgell 1992-1998 Modified by Jeremy Allison 1995.
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 675
+ * Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *)-1)
+#endif
+
+
+static int gotalarm;
+
+/***************************************************************
+ Signal function to tell us we timed out.
+****************************************************************/
+
+static void gotalarm_sig(void)
+{
+ gotalarm = 1;
+}
+
+/***************************************************************
+ Lock or unlock a fd for a known lock type. Abandon after waitsecs
+ seconds.
+****************************************************************/
+
+BOOL do_file_lock(int fd, int waitsecs, int type)
+{
+ SMB_STRUCT_FLOCK lock;
+ int ret;
+ void (*oldsig_handler)(int);
+
+ gotalarm = 0;
+ oldsig_handler = CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
+
+ lock.l_type = type;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 1;
+ lock.l_pid = 0;
+
+ alarm(waitsecs);
+ /* Note we must *NOT* use sys_fcntl here ! JRA */
+ ret = fcntl(fd, SMB_F_SETLKW, &lock);
+ alarm(0);
+ CatchSignal(SIGALRM, SIGNAL_CAST oldsig_handler);
+
+ if (gotalarm) {
+ DEBUG(0, ("do_file_lock: failed to %s file.\n",
+ type == F_UNLCK ? "unlock" : "lock"));
+ return False;
+ }
+
+ return (ret == 0);
+}
+
+
+/***************************************************************
+ Lock an fd. Abandon after waitsecs seconds.
+****************************************************************/
+
+BOOL file_lock(int fd, int type, int secs, int *plock_depth)
+{
+ if (fd < 0)
+ return False;
+
+ (*plock_depth)++;
+
+ if ((*plock_depth) == 0)
+ {
+ if (!do_file_lock(fd, secs, type)) {
+ DEBUG(10,("file_lock: locking file failed, error = %s.\n",
+ strerror(errno)));
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/***************************************************************
+ Unlock an fd. Abandon after waitsecs seconds.
+****************************************************************/
+
+BOOL file_unlock(int fd, int *plock_depth)
+{
+ BOOL ret=True;
+
+ if(*plock_depth == 1)
+ ret = do_file_lock(fd, 5, F_UNLCK);
+
+ (*plock_depth)--;
+
+ if(!ret)
+ DEBUG(10,("file_unlock: unlocking file failed, error = %s.\n",
+ strerror(errno)));
+ return ret;
+}
+
+/***************************************************************
+ locks a file for enumeration / modification.
+ update to be set = True if modification is required.
+****************************************************************/
+
+void *startfilepwent(char *pfile, char *s_readbuf, int bufsize,
+ int *file_lock_depth, BOOL update)
+{
+ FILE *fp = NULL;
+
+ if (!*pfile)
+ {
+ DEBUG(0, ("startfilepwent: No file set\n"));
+ return (NULL);
+ }
+ DEBUG(10, ("startfilepwent: opening file %s\n", pfile));
+
+ fp = sys_fopen(pfile, update ? "r+b" : "rb");
+
+ if (fp == NULL) {
+ DEBUG(0, ("startfilepwent: unable to open file %s\n", pfile));
+ return NULL;
+ }
+
+ /* Set a buffer to do more efficient reads */
+ setvbuf(fp, s_readbuf, _IOFBF, bufsize);
+
+ if (!file_lock(fileno(fp), (update ? F_WRLCK : F_RDLCK), 5, file_lock_depth))
+ {
+ DEBUG(0, ("startfilepwent: unable to lock file %s\n", pfile));
+ fclose(fp);
+ return NULL;
+ }
+
+ /* Make sure it is only rw by the owner */
+ chmod(pfile, 0600);
+
+ /* We have a lock on the file. */
+ return (void *)fp;
+}
+
+/***************************************************************
+ End enumeration of the file.
+****************************************************************/
+void endfilepwent(void *vp, int *file_lock_depth)
+{
+ FILE *fp = (FILE *)vp;
+
+ file_unlock(fileno(fp), file_lock_depth);
+ fclose(fp);
+ DEBUG(7, ("endfilepwent: closed file.\n"));
+}
+
+/*************************************************************************
+ Return the current position in the file list as an SMB_BIG_UINT.
+ This must be treated as an opaque token.
+*************************************************************************/
+SMB_BIG_UINT getfilepwpos(void *vp)
+{
+ return (SMB_BIG_UINT)sys_ftell((FILE *)vp);
+}
+
+/*************************************************************************
+ Set the current position in the file list from an SMB_BIG_UINT.
+ This must be treated as an opaque token.
+*************************************************************************/
+BOOL setfilepwpos(void *vp, SMB_BIG_UINT tok)
+{
+ return !sys_fseek((FILE *)vp, (SMB_OFF_T)tok, SEEK_SET);
+}
+
+/*************************************************************************
+ gets a line out of a file.
+ line is of format "xxxx:xxxxxx:xxxxx:".
+ lines with "#" at the front are ignored.
+*************************************************************************/
+int getfileline(void *vp, char *linebuf, int linebuf_size)
+{
+ /* Static buffers we will return. */
+ FILE *fp = (FILE *)vp;
+ unsigned char c;
+ unsigned char *p;
+ size_t linebuf_len;
+
+ if (fp == NULL)
+ {
+ DEBUG(0,("getfileline: Bad file pointer.\n"));
+ return -1;
+ }
+
+ /*
+ * Scan the file, a line at a time.
+ */
+ while (!feof(fp))
+ {
+ linebuf[0] = '\0';
+
+ fgets(linebuf, linebuf_size, fp);
+ if (ferror(fp))
+ {
+ return -1;
+ }
+
+ /*
+ * Check if the string is terminated with a newline - if not
+ * then we must keep reading and discard until we get one.
+ */
+
+ linebuf_len = strlen(linebuf);
+ if (linebuf_len == 0)
+ {
+ linebuf[0] = '\0';
+ return 0;
+ }
+
+ if (linebuf[linebuf_len - 1] != '\n')
+ {
+ c = '\0';
+ while (!ferror(fp) && !feof(fp))
+ {
+ c = fgetc(fp);
+ if (c == '\n')
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ linebuf[linebuf_len - 1] = '\0';
+ }
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("getfileline: got line |%s|\n", linebuf));
+#endif
+ if ((linebuf[0] == 0) && feof(fp))
+ {
+ DEBUG(4, ("getfileline: end of file reached\n"));
+ return 0;
+ }
+
+ if (linebuf[0] == '#' || linebuf[0] == '\0')
+ {
+ DEBUG(6, ("getfileline: skipping comment or blank line\n"));
+ continue;
+ }
+
+ p = (unsigned char *) strchr_m(linebuf, ':');
+ if (p == NULL)
+ {
+ DEBUG(0, ("getfileline: malformed line entry (no :)\n"));
+ continue;
+ }
+ return linebuf_len;
+ }
+ return -1;
+}
+
+
+/****************************************************************************
+read a line from a file with possible \ continuation chars.
+Blanks at the start or end of a line are stripped.
+The string will be allocated if s2 is NULL
+****************************************************************************/
+char *fgets_slash(char *s2,int maxlen,XFILE *f)
+{
+ char *s=s2;
+ int len = 0;
+ int c;
+ BOOL start_of_line = True;
+
+ if (x_feof(f))
+ return(NULL);
+
+ if (maxlen <2) return(NULL);
+
+ if (!s2)
+ {
+ maxlen = MIN(maxlen,8);
+ s = (char *)malloc(maxlen);
+ }
+
+ if (!s) return(NULL);
+
+ *s = 0;
+
+ while (len < maxlen-1)
+ {
+ c = x_getc(f);
+ switch (c)
+ {
+ case '\r':
+ break;
+ case '\n':
+ while (len > 0 && s[len-1] == ' ')
+ {
+ s[--len] = 0;
+ }
+ if (len > 0 && s[len-1] == '\\')
+ {
+ s[--len] = 0;
+ start_of_line = True;
+ break;
+ }
+ return(s);
+ case EOF:
+ if (len <= 0 && !s2)
+ SAFE_FREE(s);
+ return(len>0?s:NULL);
+ case ' ':
+ if (start_of_line)
+ break;
+ default:
+ start_of_line = False;
+ s[len++] = c;
+ s[len] = 0;
+ }
+ if (!s2 && len > maxlen-3)
+ {
+ char *t;
+
+ maxlen *= 2;
+ t = (char *)Realloc(s,maxlen);
+ if (!t) {
+ DEBUG(0,("fgets_slash: failed to expand buffer!\n"));
+ SAFE_FREE(s);
+ return(NULL);
+ } else s = t;
+ }
+ }
+ return(s);
+}
+
+
+/****************************************************************************
+load from a pipe into memory
+****************************************************************************/
+char *file_pload(char *syscmd, size_t *size)
+{
+ int fd, n;
+ char *p, *tp;
+ pstring buf;
+ size_t total;
+
+ fd = sys_popen(syscmd);
+ if (fd == -1) return NULL;
+
+ p = NULL;
+ total = 0;
+
+ while ((n = read(fd, buf, sizeof(buf))) > 0) {
+ tp = Realloc(p, total + n + 1);
+ if (!tp) {
+ DEBUG(0,("file_pload: failed to expand buffer!\n"));
+ close(fd);
+ SAFE_FREE(p);
+ return NULL;
+ } else p = tp;
+ memcpy(p+total, buf, n);
+ total += n;
+ }
+ if (p) p[total] = 0;
+
+ /* FIXME: Perhaps ought to check that the command completed
+ * successfully (returned 0); if not the data may be
+ * truncated. */
+ sys_pclose(fd);
+
+ if (size) *size = total;
+
+ return p;
+}
+
+/****************************************************************************
+load a file into memory from a fd.
+****************************************************************************/
+
+char *fd_load(int fd, size_t *size)
+{
+ SMB_STRUCT_STAT sbuf;
+ char *p;
+
+ if (sys_fstat(fd, &sbuf) != 0) return NULL;
+
+ p = (char *)malloc(sbuf.st_size+1);
+ if (!p) return NULL;
+
+ if (read(fd, p, sbuf.st_size) != sbuf.st_size) {
+ SAFE_FREE(p);
+ return NULL;
+ }
+ p[sbuf.st_size] = 0;
+
+ if (size) *size = sbuf.st_size;
+
+ return p;
+}
+
+/****************************************************************************
+load a file into memory
+****************************************************************************/
+char *file_load(const char *fname, size_t *size)
+{
+ int fd;
+ char *p;
+
+ if (!fname || !*fname) return NULL;
+
+ fd = open(fname,O_RDONLY);
+ if (fd == -1) return NULL;
+
+ p = fd_load(fd, size);
+
+ close(fd);
+
+ return p;
+}
+
+
+/*******************************************************************
+mmap (if possible) or read a file
+********************************************************************/
+void *map_file(char *fname, size_t size)
+{
+ size_t s2 = 0;
+ void *p = NULL;
+#ifdef HAVE_MMAP
+ if (lp_use_mmap()) {
+ int fd;
+ fd = open(fname, O_RDONLY, 0);
+ if (fd == -1) {
+ DEBUG(2,("Failed to load %s - %s\n", fname, strerror(errno)));
+ return NULL;
+ }
+ p = mmap(NULL, size, PROT_READ, MAP_SHARED|MAP_FILE, fd, 0);
+ close(fd);
+ if (p == MAP_FAILED) {
+ DEBUG(1,("Failed to mmap %s - %s\n", fname, strerror(errno)));
+ return NULL;
+ }
+ }
+#endif
+ if (!p) {
+ p = file_load(fname, &s2);
+ if (!p) return NULL;
+ if (s2 != size) {
+ DEBUG(1,("incorrect size for %s - got %lu expected %lu\n",
+ fname, (unsigned long)s2, (unsigned long)size));
+ if (p) free(p);
+ return NULL;
+ }
+ }
+
+ return p;
+}
+
+
+/****************************************************************************
+parse a buffer into lines
+****************************************************************************/
+static char **file_lines_parse(char *p, size_t size, int *numlines)
+{
+ int i;
+ char *s, **ret;
+
+ if (!p) return NULL;
+
+ for (s = p, i=0; s < p+size; s++) {
+ if (s[0] == '\n') i++;
+ }
+
+ ret = (char **)malloc(sizeof(ret[0])*(i+2));
+ if (!ret) {
+ SAFE_FREE(p);
+ return NULL;
+ }
+ memset(ret, 0, sizeof(ret[0])*(i+2));
+ if (numlines) *numlines = i;
+
+ ret[0] = p;
+ for (s = p, i=0; s < p+size; s++) {
+ if (s[0] == '\n') {
+ s[0] = 0;
+ i++;
+ ret[i] = s+1;
+ }
+ if (s[0] == '\r') s[0] = 0;
+ }
+
+ return ret;
+}
+
+
+/****************************************************************************
+load a file into memory and return an array of pointers to lines in the file
+must be freed with file_lines_free().
+****************************************************************************/
+char **file_lines_load(const char *fname, int *numlines)
+{
+ char *p;
+ size_t size;
+
+ p = file_load(fname, &size);
+ if (!p) return NULL;
+
+ return file_lines_parse(p, size, numlines);
+}
+
+/****************************************************************************
+load a fd into memory and return an array of pointers to lines in the file
+must be freed with file_lines_free(). If convert is true calls unix_to_dos on
+the list.
+****************************************************************************/
+char **fd_lines_load(int fd, int *numlines)
+{
+ char *p;
+ size_t size;
+
+ p = fd_load(fd, &size);
+ if (!p) return NULL;
+
+ return file_lines_parse(p, size, numlines);
+}
+
+
+/****************************************************************************
+load a pipe into memory and return an array of pointers to lines in the data
+must be freed with file_lines_free().
+****************************************************************************/
+char **file_lines_pload(char *syscmd, int *numlines)
+{
+ char *p;
+ size_t size;
+
+ p = file_pload(syscmd, &size);
+ if (!p) return NULL;
+
+ return file_lines_parse(p, size, numlines);
+}
+
+/****************************************************************************
+free lines loaded with file_lines_load
+****************************************************************************/
+void file_lines_free(char **lines)
+{
+ if (!lines) return;
+ SAFE_FREE(lines[0]);
+ SAFE_FREE(lines);
+}
+
+
+/****************************************************************************
+take a lislist of lines and modify them to produce a list where \ continues
+a line
+****************************************************************************/
+void file_lines_slashcont(char **lines)
+{
+ int i, j;
+
+ for (i=0; lines[i];) {
+ int len = strlen(lines[i]);
+ if (lines[i][len-1] == '\\') {
+ lines[i][len-1] = ' ';
+ if (lines[i+1]) {
+ char *p = &lines[i][len];
+ while (p < lines[i+1]) *p++ = ' ';
+ for (j = i+1; lines[j]; j++) lines[j] = lines[j+1];
+ }
+ } else {
+ i++;
+ }
+ }
+}
+
+/*
+ save a lump of data into a file. Mostly used for debugging
+*/
+BOOL file_save(const char *fname, void *packet, size_t length)
+{
+ int fd;
+ fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ if (fd == -1) {
+ return False;
+ }
+ if (write(fd, packet, length) != (size_t)length) {
+ return False;
+ }
+ close(fd);
+ return True;
+}
diff --git a/source/lib/util_getent.c b/source/lib/util_getent.c
new file mode 100644
index 00000000000..3544c1678cc
--- /dev/null
+++ b/source/lib/util_getent.c
@@ -0,0 +1,306 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Simo Sorce 2001
+ Copyright (C) Jeremy Allison 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+
+/****************************************************************
+ Returns a single linked list of group entries.
+ Use grent_free() to free it after use.
+****************************************************************/
+
+struct sys_grent * getgrent_list(void)
+{
+ struct sys_grent *glist;
+ struct sys_grent *gent;
+ struct group *grp;
+
+ gent = (struct sys_grent *) malloc(sizeof(struct sys_grent));
+ if (gent == NULL) {
+ DEBUG (0, ("Out of memory in getgrent_list!\n"));
+ return NULL;
+ }
+ memset(gent, '\0', sizeof(struct sys_grent));
+ glist = gent;
+
+ setgrent();
+ grp = getgrent();
+ if (grp == NULL) {
+ endgrent();
+ SAFE_FREE(glist);
+ return NULL;
+ }
+
+ while (grp != NULL) {
+ int i,num;
+
+ if (grp->gr_name) {
+ if ((gent->gr_name = strdup(grp->gr_name)) == NULL)
+ goto err;
+ }
+ if (grp->gr_passwd) {
+ if ((gent->gr_passwd = strdup(grp->gr_passwd)) == NULL)
+ goto err;
+ }
+ gent->gr_gid = grp->gr_gid;
+
+ /* number of strings in gr_mem */
+ for (num = 0; grp->gr_mem[num]; num++)
+ ;
+
+ /* alloc space for gr_mem string pointers */
+ if ((gent->gr_mem = (char **) malloc((num+1) * sizeof(char *))) == NULL)
+ goto err;
+
+ memset(gent->gr_mem, '\0', (num+1) * sizeof(char *));
+
+ for (i=0; i < num; i++) {
+ if ((gent->gr_mem[i] = strdup(grp->gr_mem[i])) == NULL)
+ goto err;
+ }
+ gent->gr_mem[num] = NULL;
+
+ grp = getgrent();
+ if (grp) {
+ gent->next = (struct sys_grent *) malloc(sizeof(struct sys_grent));
+ if (gent->next == NULL)
+ goto err;
+ gent = gent->next;
+ memset(gent, '\0', sizeof(struct sys_grent));
+ }
+ }
+
+ endgrent();
+ return glist;
+
+ err:
+
+ endgrent();
+ DEBUG(0, ("Out of memory in getgrent_list!\n"));
+ grent_free(glist);
+ return NULL;
+}
+
+/****************************************************************
+ Free the single linked list of group entries made by
+ getgrent_list()
+****************************************************************/
+
+void grent_free (struct sys_grent *glist)
+{
+ while (glist) {
+ struct sys_grent *prev;
+
+ SAFE_FREE(glist->gr_name);
+ SAFE_FREE(glist->gr_passwd);
+ if (glist->gr_mem) {
+ int i;
+ for (i = 0; glist->gr_mem[i]; i++)
+ SAFE_FREE(glist->gr_mem[i]);
+ SAFE_FREE(glist->gr_mem);
+ }
+ prev = glist;
+ glist = glist->next;
+ SAFE_FREE(prev);
+ }
+}
+
+/****************************************************************
+ Returns a single linked list of passwd entries.
+ Use pwent_free() to free it after use.
+****************************************************************/
+
+struct sys_pwent * getpwent_list(void)
+{
+ struct sys_pwent *plist;
+ struct sys_pwent *pent;
+ struct passwd *pwd;
+
+ pent = (struct sys_pwent *) malloc(sizeof(struct sys_pwent));
+ if (pent == NULL) {
+ DEBUG (0, ("Out of memory in getpwent_list!\n"));
+ return NULL;
+ }
+ plist = pent;
+
+ setpwent();
+ pwd = getpwent();
+ while (pwd != NULL) {
+ memset(pent, '\0', sizeof(struct sys_pwent));
+ if (pwd->pw_name) {
+ if ((pent->pw_name = strdup(pwd->pw_name)) == NULL)
+ goto err;
+ }
+ if (pwd->pw_passwd) {
+ if ((pent->pw_passwd = strdup(pwd->pw_passwd)) == NULL)
+ goto err;
+ }
+ pent->pw_uid = pwd->pw_uid;
+ pent->pw_gid = pwd->pw_gid;
+ if (pwd->pw_gecos) {
+ if ((pent->pw_gecos = strdup(pwd->pw_gecos)) == NULL)
+ goto err;
+ }
+ if (pwd->pw_dir) {
+ if ((pent->pw_dir = strdup(pwd->pw_dir)) == NULL)
+ goto err;
+ }
+ if (pwd->pw_shell) {
+ if ((pent->pw_shell = strdup(pwd->pw_shell)) == NULL)
+ goto err;
+ }
+
+ pwd = getpwent();
+ if (pwd) {
+ pent->next = (struct sys_pwent *) malloc(sizeof(struct sys_pwent));
+ if (pent->next == NULL)
+ goto err;
+ pent = pent->next;
+ }
+ }
+
+ endpwent();
+ return plist;
+
+ err:
+
+ endpwent();
+ DEBUG(0, ("Out of memory in getpwent_list!\n"));
+ pwent_free(plist);
+ return NULL;
+}
+
+/****************************************************************
+ Free the single linked list of passwd entries made by
+ getpwent_list()
+****************************************************************/
+
+void pwent_free (struct sys_pwent *plist)
+{
+ while (plist) {
+ struct sys_pwent *prev;
+
+ SAFE_FREE(plist->pw_name);
+ SAFE_FREE(plist->pw_passwd);
+ SAFE_FREE(plist->pw_gecos);
+ SAFE_FREE(plist->pw_dir);
+ SAFE_FREE(plist->pw_shell);
+
+ prev = plist;
+ plist = plist->next;
+ SAFE_FREE(prev);
+ }
+}
+
+/****************************************************************
+ Add the individual group users onto the list.
+****************************************************************/
+
+static struct sys_userlist *add_members_to_userlist(struct sys_userlist *list_head, const struct group *grp)
+{
+ size_t num_users, i;
+
+ /* Count the number of users. */
+ for (num_users = 0; grp->gr_mem[num_users]; num_users++)
+ ;
+
+ for (i = 0; i < num_users; i++) {
+ struct sys_userlist *entry = (struct sys_userlist *)malloc(sizeof(*entry));
+ if (entry == NULL) {
+ free_userlist(list_head);
+ return NULL;
+ }
+ entry->unix_name = (char *)strdup(grp->gr_mem[i]);
+ if (entry->unix_name == NULL) {
+ SAFE_FREE(entry);
+ free_userlist(list_head);
+ return NULL;
+ }
+ DLIST_ADD(list_head, entry);
+ }
+ return list_head;
+}
+
+/****************************************************************
+ Get the list of UNIX users in a group.
+ We have to enumerate the /etc/group file as some UNIX getgrnam()
+ calls won't do that for us (notably Tru64 UNIX).
+****************************************************************/
+
+struct sys_userlist *get_users_in_group(const char *gname)
+{
+ struct sys_userlist *list_head = NULL;
+ struct group *gptr;
+ fstring domain;
+ fstring groupname;
+ DOM_SID sid;
+ enum SID_NAME_USE name_type;
+
+ /* No point using winbind if we can't split it in the
+ first place */
+ if (split_domain_and_name(gname, domain, groupname)) {
+
+ /*
+ * If we're doing this via winbindd, don't do the
+ * entire group list enumeration as we know this is
+ * pointless (and slow).
+ */
+
+ if (winbind_lookup_name(domain, groupname, &sid, &name_type)
+ && name_type == SID_NAME_DOM_GRP) {
+ if ((gptr = (struct group *)getgrnam(gname)) == NULL)
+ return NULL;
+ return add_members_to_userlist(list_head, gptr);
+ }
+ }
+
+#if !defined(BROKEN_GETGRNAM)
+ if ((gptr = (struct group *)getgrnam(gname)) == NULL)
+ return NULL;
+ return add_members_to_userlist(list_head, gptr);
+#else
+ /* BROKEN_GETGRNAM - True64 */
+ setgrent();
+ while((gptr = getgrent()) != NULL) {
+ if (strequal(gname, gptr->gr_name)) {
+ list_head = add_members_to_userlist(list_head, gptr);
+ if (list_head == NULL)
+ return NULL;
+ }
+ }
+ endgrent();
+ return list_head;
+#endif
+}
+
+/****************************************************************
+ Free list allocated above.
+****************************************************************/
+
+void free_userlist(struct sys_userlist *list_head)
+{
+ while (list_head) {
+ struct sys_userlist *old_head = list_head;
+ DLIST_REMOVE(list_head, list_head);
+ SAFE_FREE(old_head->unix_name);
+ SAFE_FREE(old_head);
+ }
+}
diff --git a/source/lib/util_pw.c b/source/lib/util_pw.c
new file mode 100644
index 00000000000..9d075a05e88
--- /dev/null
+++ b/source/lib/util_pw.c
@@ -0,0 +1,89 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Safe versions of getpw* calls
+
+ Copyright (C) Andrew Bartlett 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static struct passwd *alloc_copy_passwd(const struct passwd *from)
+{
+ struct passwd *ret = smb_xmalloc(sizeof(struct passwd));
+ ZERO_STRUCTP(ret);
+ ret->pw_name = smb_xstrdup(from->pw_name);
+ ret->pw_passwd = smb_xstrdup(from->pw_passwd);
+ ret->pw_uid = from->pw_uid;
+ ret->pw_gid = from->pw_gid;
+ ret->pw_gecos = smb_xstrdup(from->pw_gecos);
+ ret->pw_dir = smb_xstrdup(from->pw_dir);
+ ret->pw_shell = smb_xstrdup(from->pw_shell);
+ return ret;
+}
+
+void passwd_free (struct passwd **buf)
+{
+ if (!*buf) {
+ DEBUG(0, ("attempted double-free of allocated passwd\n"));
+ return;
+ }
+
+ SAFE_FREE((*buf)->pw_name);
+ SAFE_FREE((*buf)->pw_passwd);
+ SAFE_FREE((*buf)->pw_gecos);
+ SAFE_FREE((*buf)->pw_dir);
+ SAFE_FREE((*buf)->pw_shell);
+
+ SAFE_FREE(*buf);
+}
+
+struct passwd *getpwnam_alloc(const char *name)
+{
+ struct passwd *temp;
+
+ temp = sys_getpwnam(name);
+
+ if (!temp) {
+#if 0
+ if (errno == ENOMEM) {
+ /* what now? */
+ }
+#endif
+ return NULL;
+ }
+
+ return alloc_copy_passwd(temp);
+}
+
+struct passwd *getpwuid_alloc(uid_t uid)
+{
+ struct passwd *temp;
+
+ temp = sys_getpwuid(uid);
+
+ if (!temp) {
+#if 0
+ if (errno == ENOMEM) {
+ /* what now? */
+ }
+#endif
+ return NULL;
+ }
+
+ return alloc_copy_passwd(temp);
+}
diff --git a/source/lib/util_seaccess.c b/source/lib/util_seaccess.c
new file mode 100644
index 00000000000..cb0f46e2f9d
--- /dev/null
+++ b/source/lib/util_seaccess.c
@@ -0,0 +1,357 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000.
+ Copyright (C) Tim Potter 2000.
+ Copyright (C) Re-written by Jeremy Allison 2000.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+extern DOM_SID global_sid_Builtin;
+
+/*********************************************************************************
+ Check an ACE against a SID. We return the remaining needed permission
+ bits not yet granted. Zero means permission allowed (no more needed bits).
+**********************************************************************************/
+
+static uint32 check_ace(SEC_ACE *ace, const NT_USER_TOKEN *token, uint32 acc_desired,
+ NTSTATUS *status)
+{
+ uint32 mask = ace->info.mask;
+
+ /*
+ * Inherit only is ignored.
+ */
+
+ if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+ return acc_desired;
+ }
+
+ /*
+ * If this ACE has no SID in common with the token,
+ * ignore it as it cannot be used to make an access
+ * determination.
+ */
+
+ if (!token_sid_in_ace( token, ace))
+ return acc_desired;
+
+ switch (ace->type) {
+ case SEC_ACE_TYPE_ACCESS_ALLOWED:
+ /*
+ * This is explicitly allowed.
+ * Remove the bits from the remaining
+ * access required. Return the remaining
+ * bits needed.
+ */
+ acc_desired &= ~mask;
+ break;
+ case SEC_ACE_TYPE_ACCESS_DENIED:
+ /*
+ * This is explicitly denied.
+ * If any bits match terminate here,
+ * we are denied.
+ */
+ if (acc_desired & mask) {
+ *status = NT_STATUS_ACCESS_DENIED;
+ return 0xFFFFFFFF;
+ }
+ break;
+ case SEC_ACE_TYPE_SYSTEM_ALARM:
+ case SEC_ACE_TYPE_SYSTEM_AUDIT:
+ *status = NT_STATUS_NOT_IMPLEMENTED;
+ return 0xFFFFFFFF;
+ default:
+ *status = NT_STATUS_INVALID_PARAMETER;
+ return 0xFFFFFFFF;
+ }
+
+ return acc_desired;
+}
+
+/*********************************************************************************
+ Maximum access was requested. Calculate the max possible. Fail if it doesn't
+ include other bits requested.
+**********************************************************************************/
+
+static BOOL get_max_access( SEC_ACL *the_acl, const NT_USER_TOKEN *token, uint32 *granted,
+ uint32 desired,
+ NTSTATUS *status)
+{
+ uint32 acc_denied = 0;
+ uint32 acc_granted = 0;
+ size_t i;
+
+ for ( i = 0 ; i < the_acl->num_aces; i++) {
+ SEC_ACE *ace = &the_acl->ace[i];
+ uint32 mask = ace->info.mask;
+
+ if (!token_sid_in_ace( token, ace))
+ continue;
+
+ switch (ace->type) {
+ case SEC_ACE_TYPE_ACCESS_ALLOWED:
+ acc_granted |= (mask & ~acc_denied);
+ break;
+ case SEC_ACE_TYPE_ACCESS_DENIED:
+ acc_denied |= (mask & ~acc_granted);
+ break;
+ case SEC_ACE_TYPE_SYSTEM_ALARM:
+ case SEC_ACE_TYPE_SYSTEM_AUDIT:
+ *status = NT_STATUS_NOT_IMPLEMENTED;
+ *granted = 0;
+ return False;
+ default:
+ *status = NT_STATUS_INVALID_PARAMETER;
+ *granted = 0;
+ return False;
+ }
+ }
+
+ /*
+ * If we were granted no access, or we desired bits that we
+ * didn't get, then deny.
+ */
+
+ if ((acc_granted == 0) || ((acc_granted & desired) != desired)) {
+ *status = NT_STATUS_ACCESS_DENIED;
+ *granted = 0;
+ return False;
+ }
+
+ /*
+ * Return the access we did get.
+ */
+
+ *granted = acc_granted;
+ *status = NT_STATUS_OK;
+ return True;
+}
+
+/* Map generic access rights to object specific rights. This technique is
+ used to give meaning to assigning read, write, execute and all access to
+ objects. Each type of object has its own mapping of generic to object
+ specific access rights. */
+
+void se_map_generic(uint32 *access_mask, struct generic_mapping *mapping)
+{
+ uint32 old_mask = *access_mask;
+
+ if (*access_mask & GENERIC_READ_ACCESS) {
+ *access_mask &= ~GENERIC_READ_ACCESS;
+ *access_mask |= mapping->generic_read;
+ }
+
+ if (*access_mask & GENERIC_WRITE_ACCESS) {
+ *access_mask &= ~GENERIC_WRITE_ACCESS;
+ *access_mask |= mapping->generic_write;
+ }
+
+ if (*access_mask & GENERIC_EXECUTE_ACCESS) {
+ *access_mask &= ~GENERIC_EXECUTE_ACCESS;
+ *access_mask |= mapping->generic_execute;
+ }
+
+ if (*access_mask & GENERIC_ALL_ACCESS) {
+ *access_mask &= ~GENERIC_ALL_ACCESS;
+ *access_mask |= mapping->generic_all;
+ }
+
+ if (old_mask != *access_mask) {
+ DEBUG(10, ("se_map_generic(): mapped mask 0x%08x to 0x%08x\n",
+ old_mask, *access_mask));
+ }
+}
+
+/* Map standard access rights to object specific rights. This technique is
+ used to give meaning to assigning read, write, execute and all access to
+ objects. Each type of object has its own mapping of standard to object
+ specific access rights. */
+
+void se_map_standard(uint32 *access_mask, struct standard_mapping *mapping)
+{
+ uint32 old_mask = *access_mask;
+
+ if (*access_mask & READ_CONTROL_ACCESS) {
+ *access_mask &= ~READ_CONTROL_ACCESS;
+ *access_mask |= mapping->std_read;
+ }
+
+ if (*access_mask & (DELETE_ACCESS|WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS|SYNCHRONIZE_ACCESS)) {
+ *access_mask &= ~(DELETE_ACCESS|WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS|SYNCHRONIZE_ACCESS);
+ *access_mask |= mapping->std_all;
+ }
+
+ if (old_mask != *access_mask) {
+ DEBUG(10, ("se_map_standard(): mapped mask 0x%08x to 0x%08x\n",
+ old_mask, *access_mask));
+ }
+}
+
+/*****************************************************************************
+ Check access rights of a user against a security descriptor. Look at
+ each ACE in the security descriptor until an access denied ACE denies
+ any of the desired rights to the user or any of the users groups, or one
+ or more ACEs explicitly grant all requested access rights. See
+ "Access-Checking" document in MSDN.
+*****************************************************************************/
+
+BOOL se_access_check(const SEC_DESC *sd, const NT_USER_TOKEN *token,
+ uint32 acc_desired, uint32 *acc_granted,
+ NTSTATUS *status)
+{
+ extern NT_USER_TOKEN anonymous_token;
+ size_t i;
+ SEC_ACL *the_acl;
+ fstring sid_str;
+ uint32 tmp_acc_desired = acc_desired;
+
+ if (!status || !acc_granted)
+ return False;
+
+ if (!token)
+ token = &anonymous_token;
+
+ *status = NT_STATUS_OK;
+ *acc_granted = 0;
+
+ DEBUG(10,("se_access_check: requested access 0x%08x, for NT token with %u entries and first sid %s.\n",
+ (unsigned int)acc_desired, (unsigned int)token->num_sids,
+ sid_to_string(sid_str, &token->user_sids[0])));
+
+ /*
+ * No security descriptor or security descriptor with no DACL
+ * present allows all access.
+ */
+
+ /* ACL must have something in it */
+
+ if (!sd || (sd && (!(sd->type & SEC_DESC_DACL_PRESENT) || sd->dacl == NULL))) {
+ *status = NT_STATUS_OK;
+ *acc_granted = acc_desired;
+ DEBUG(5, ("se_access_check: no sd or blank DACL, access allowed\n"));
+ return True;
+ }
+
+ /* The user sid is the first in the token */
+ if (DEBUGLVL(3)) {
+ DEBUG(3, ("se_access_check: user sid is %s\n", sid_to_string(sid_str, &token->user_sids[PRIMARY_USER_SID_INDEX]) ));
+
+ for (i = 1; i < token->num_sids; i++) {
+ DEBUGADD(3, ("se_access_check: also %s\n",
+ sid_to_string(sid_str, &token->user_sids[i])));
+ }
+ }
+
+ /* Is the token the owner of the SID ? */
+
+ if (sd->owner_sid) {
+ for (i = 0; i < token->num_sids; i++) {
+ if (sid_equal(&token->user_sids[i], sd->owner_sid)) {
+ /*
+ * The owner always has SEC_RIGHTS_WRITE_DAC & READ_CONTROL.
+ */
+ if (tmp_acc_desired & WRITE_DAC_ACCESS)
+ tmp_acc_desired &= ~WRITE_DAC_ACCESS;
+ if (tmp_acc_desired & READ_CONTROL_ACCESS)
+ tmp_acc_desired &= ~READ_CONTROL_ACCESS;
+ }
+ }
+ }
+
+ the_acl = sd->dacl;
+
+ if (tmp_acc_desired & MAXIMUM_ALLOWED_ACCESS) {
+ tmp_acc_desired &= ~MAXIMUM_ALLOWED_ACCESS;
+ return get_max_access( the_acl, token, acc_granted, tmp_acc_desired,
+ status);
+ }
+
+ for ( i = 0 ; i < the_acl->num_aces && tmp_acc_desired != 0; i++) {
+ SEC_ACE *ace = &the_acl->ace[i];
+
+ DEBUGADD(10,("se_access_check: ACE %u: type %d, flags = 0x%02x, SID = %s mask = %x, current desired = %x\n",
+ (unsigned int)i, ace->type, ace->flags,
+ sid_to_string(sid_str, &ace->trustee),
+ (unsigned int) ace->info.mask,
+ (unsigned int)tmp_acc_desired ));
+
+ tmp_acc_desired = check_ace( ace, token, tmp_acc_desired, status);
+ if (NT_STATUS_V(*status)) {
+ *acc_granted = 0;
+ DEBUG(5,("se_access_check: ACE %u denied with status %s.\n", (unsigned int)i, nt_errstr(*status)));
+ return False;
+ }
+ }
+
+ /*
+ * If there are no more desired permissions left then
+ * access was allowed.
+ */
+
+ if (tmp_acc_desired == 0) {
+ *acc_granted = acc_desired;
+ *status = NT_STATUS_OK;
+ DEBUG(5,("se_access_check: access (%x) granted.\n", (unsigned int)acc_desired ));
+ return True;
+ }
+
+ *acc_granted = 0;
+ *status = NT_STATUS_ACCESS_DENIED;
+ DEBUG(5,("se_access_check: access (%x) denied.\n", (unsigned int)acc_desired ));
+ return False;
+}
+
+
+/*******************************************************************
+ samr_make_sam_obj_sd
+ ********************************************************************/
+
+NTSTATUS samr_make_sam_obj_sd(TALLOC_CTX *ctx, SEC_DESC **psd, size_t *sd_size)
+{
+ extern DOM_SID global_sid_World;
+ DOM_SID adm_sid;
+ DOM_SID act_sid;
+
+ SEC_ACE ace[3];
+ SEC_ACCESS mask;
+
+ SEC_ACL *psa = NULL;
+
+ sid_copy(&adm_sid, &global_sid_Builtin);
+ sid_append_rid(&adm_sid, BUILTIN_ALIAS_RID_ADMINS);
+
+ sid_copy(&act_sid, &global_sid_Builtin);
+ sid_append_rid(&act_sid, BUILTIN_ALIAS_RID_ACCOUNT_OPS);
+
+ /*basic access for every one*/
+ init_sec_access(&mask, GENERIC_RIGHTS_SAM_EXECUTE | GENERIC_RIGHTS_SAM_READ);
+ init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ /*full access for builtin aliases Administrators and Account Operators*/
+ init_sec_access(&mask, GENERIC_RIGHTS_SAM_ALL_ACCESS);
+ init_sec_ace(&ace[1], &adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+ init_sec_ace(&ace[2], &act_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 3, ace)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if ((*psd = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL, psa, sd_size)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ return NT_STATUS_OK;
+}
diff --git a/source/lib/util_sec.c b/source/lib/util_sec.c
new file mode 100644
index 00000000000..26be27ea515
--- /dev/null
+++ b/source/lib/util_sec.c
@@ -0,0 +1,467 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) Jeremy Allison 1998.
+ rewritten for version 2.0.6 by Tridge
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef AUTOCONF_TEST
+#include "includes.h"
+#else
+/* we are running this code in autoconf test mode to see which type of setuid
+ function works */
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <errno.h>
+
+#ifdef HAVE_SYS_PRIV_H
+#include <sys/priv.h>
+#endif
+#ifdef HAVE_SYS_ID_H
+#include <sys/id.h>
+#endif
+
+#define DEBUG(x, y) printf y
+#define smb_panic(x) exit(1)
+#define BOOL int
+#endif
+
+/* are we running as non-root? This is used by the regresison test code,
+ and potentially also for sites that want non-root smbd */
+static uid_t initial_uid;
+static gid_t initial_gid;
+
+/****************************************************************************
+remember what uid we got started as - this allows us to run correctly
+as non-root while catching trapdoor systems
+****************************************************************************/
+void sec_init(void)
+{
+ initial_uid = geteuid();
+ initial_gid = getegid();
+}
+
+/****************************************************************************
+some code (eg. winbindd) needs to know what uid we started as
+****************************************************************************/
+uid_t sec_initial_uid(void)
+{
+ return initial_uid;
+}
+
+/****************************************************************************
+some code (eg. winbindd, profiling shm) needs to know what gid we started as
+****************************************************************************/
+gid_t sec_initial_gid(void)
+{
+ return initial_gid;
+}
+
+/****************************************************************************
+are we running in non-root mode?
+****************************************************************************/
+BOOL non_root_mode(void)
+{
+ return (initial_uid != (uid_t)0);
+}
+
+/****************************************************************************
+abort if we haven't set the uid correctly
+****************************************************************************/
+static void assert_uid(uid_t ruid, uid_t euid)
+{
+ if ((euid != (uid_t)-1 && geteuid() != euid) ||
+ (ruid != (uid_t)-1 && getuid() != ruid)) {
+ if (!non_root_mode()) {
+ DEBUG(0,("Failed to set uid privileges to (%d,%d) now set to (%d,%d)\n",
+ (int)ruid, (int)euid,
+ (int)getuid(), (int)geteuid()));
+ smb_panic("failed to set uid\n");
+ exit(1);
+ }
+ }
+}
+
+/****************************************************************************
+abort if we haven't set the gid correctly
+****************************************************************************/
+static void assert_gid(gid_t rgid, gid_t egid)
+{
+ if ((egid != (gid_t)-1 && getegid() != egid) ||
+ (rgid != (gid_t)-1 && getgid() != rgid)) {
+ if (!non_root_mode()) {
+ DEBUG(0,("Failed to set gid privileges to (%d,%d) now set to (%d,%d) uid=(%d,%d)\n",
+ (int)rgid, (int)egid,
+ (int)getgid(), (int)getegid(),
+ (int)getuid(), (int)geteuid()));
+ smb_panic("failed to set gid\n");
+ exit(1);
+ }
+ }
+}
+
+/****************************************************************************
+ Gain root privilege before doing something.
+ We want to end up with ruid==euid==0
+****************************************************************************/
+void gain_root_privilege(void)
+{
+#if USE_SETRESUID
+ setresuid(0,0,0);
+#endif
+
+#if USE_SETEUID
+ seteuid(0);
+#endif
+
+#if USE_SETREUID
+ setreuid(0, 0);
+#endif
+
+#if USE_SETUIDX
+ setuidx(ID_EFFECTIVE, 0);
+ setuidx(ID_REAL, 0);
+#endif
+
+ /* this is needed on some systems */
+ setuid(0);
+
+ assert_uid(0, 0);
+}
+
+
+/****************************************************************************
+ Ensure our real and effective groups are zero.
+ we want to end up with rgid==egid==0
+****************************************************************************/
+void gain_root_group_privilege(void)
+{
+#if USE_SETRESUID
+ setresgid(0,0,0);
+#endif
+
+#if USE_SETREUID
+ setregid(0,0);
+#endif
+
+#if USE_SETEUID
+ setegid(0);
+#endif
+
+#if USE_SETUIDX
+ setgidx(ID_EFFECTIVE, 0);
+ setgidx(ID_REAL, 0);
+#endif
+
+ setgid(0);
+
+ assert_gid(0, 0);
+}
+
+
+/****************************************************************************
+ Set effective uid, and possibly the real uid too.
+ We want to end up with either:
+
+ ruid==uid and euid==uid
+
+ or
+
+ ruid==0 and euid==uid
+
+ depending on what the local OS will allow us to regain root from.
+****************************************************************************/
+void set_effective_uid(uid_t uid)
+{
+#if USE_SETRESUID
+ /* Set the effective as well as the real uid. */
+ setresuid(uid,uid,-1);
+#endif
+
+#if USE_SETREUID
+ setreuid(-1,uid);
+#endif
+
+#if USE_SETEUID
+ seteuid(uid);
+#endif
+
+#if USE_SETUIDX
+ setuidx(ID_EFFECTIVE, uid);
+#endif
+
+ assert_uid(-1, uid);
+}
+
+/****************************************************************************
+ Set *only* the effective gid.
+ we want to end up with rgid==0 and egid==gid
+****************************************************************************/
+void set_effective_gid(gid_t gid)
+{
+#if USE_SETRESUID
+ setresgid(-1,gid,-1);
+#endif
+
+#if USE_SETREUID
+ setregid(-1,gid);
+#endif
+
+#if USE_SETEUID
+ setegid(gid);
+#endif
+
+#if USE_SETUIDX
+ setgidx(ID_EFFECTIVE, gid);
+#endif
+
+ assert_gid(-1, gid);
+}
+
+static uid_t saved_euid, saved_ruid;
+static gid_t saved_egid, saved_rgid;
+
+/****************************************************************************
+ save the real and effective uid for later restoration. Used by the quotas
+ code
+****************************************************************************/
+void save_re_uid(void)
+{
+ saved_ruid = getuid();
+ saved_euid = geteuid();
+}
+
+
+/****************************************************************************
+ and restore them!
+****************************************************************************/
+void restore_re_uid(void)
+{
+ set_effective_uid(0);
+
+#if USE_SETRESUID
+ setresuid(saved_ruid, saved_euid, -1);
+#elif USE_SETREUID
+ setreuid(saved_ruid, -1);
+ setreuid(-1,saved_euid);
+#elif USE_SETUIDX
+ setuidx(ID_REAL, saved_ruid);
+ setuidx(ID_EFFECTIVE, saved_euid);
+#else
+ set_effective_uid(saved_euid);
+ if (getuid() != saved_ruid)
+ setuid(saved_ruid);
+ set_effective_uid(saved_euid);
+#endif
+
+ assert_uid(saved_ruid, saved_euid);
+}
+
+
+/****************************************************************************
+ save the real and effective gid for later restoration. Used by the
+ getgroups code
+****************************************************************************/
+void save_re_gid(void)
+{
+ saved_rgid = getgid();
+ saved_egid = getegid();
+}
+
+/****************************************************************************
+ and restore them!
+****************************************************************************/
+void restore_re_gid(void)
+{
+#if USE_SETRESUID
+ setresgid(saved_rgid, saved_egid, -1);
+#elif USE_SETREUID
+ setregid(saved_rgid, -1);
+ setregid(-1,saved_egid);
+#elif USE_SETUIDX
+ setgidx(ID_REAL, saved_rgid);
+ setgidx(ID_EFFECTIVE, saved_egid);
+#else
+ set_effective_gid(saved_egid);
+ if (getgid() != saved_rgid)
+ setgid(saved_rgid);
+ set_effective_gid(saved_egid);
+#endif
+
+ assert_gid(saved_rgid, saved_egid);
+}
+
+
+/****************************************************************************
+ set the real AND effective uid to the current effective uid in a way that
+ allows root to be regained.
+ This is only possible on some platforms.
+****************************************************************************/
+int set_re_uid(void)
+{
+ uid_t uid = geteuid();
+
+#if USE_SETRESUID
+ setresuid(geteuid(), -1, -1);
+#endif
+
+#if USE_SETREUID
+ setreuid(0, 0);
+ setreuid(uid, -1);
+ setreuid(-1, uid);
+#endif
+
+#if USE_SETEUID
+ /* can't be done */
+ return -1;
+#endif
+
+#if USE_SETUIDX
+ /* can't be done */
+ return -1;
+#endif
+
+ assert_uid(uid, uid);
+ return 0;
+}
+
+
+/****************************************************************************
+ Become the specified uid and gid - permanently !
+ there should be no way back if possible
+****************************************************************************/
+void become_user_permanently(uid_t uid, gid_t gid)
+{
+ /*
+ * First - gain root privilege. We do this to ensure
+ * we can lose it again.
+ */
+
+ gain_root_privilege();
+ gain_root_group_privilege();
+
+#if USE_SETRESUID
+ setresgid(gid,gid,gid);
+ setgid(gid);
+ setresuid(uid,uid,uid);
+ setuid(uid);
+#endif
+
+#if USE_SETREUID
+ setregid(gid,gid);
+ setgid(gid);
+ setreuid(uid,uid);
+ setuid(uid);
+#endif
+
+#if USE_SETEUID
+ setegid(gid);
+ setgid(gid);
+ setuid(uid);
+ seteuid(uid);
+ setuid(uid);
+#endif
+
+#if USE_SETUIDX
+ setgidx(ID_REAL, gid);
+ setgidx(ID_EFFECTIVE, gid);
+ setgid(gid);
+ setuidx(ID_REAL, uid);
+ setuidx(ID_EFFECTIVE, uid);
+ setuid(uid);
+#endif
+
+ assert_uid(uid, uid);
+ assert_gid(gid, gid);
+}
+
+#ifdef AUTOCONF_TEST
+
+/****************************************************************************
+this function just checks that we don't get ENOSYS back
+****************************************************************************/
+static int have_syscall(void)
+{
+ errno = 0;
+
+#if USE_SETRESUID
+ setresuid(-1,-1,-1);
+#endif
+
+#if USE_SETREUID
+ setreuid(-1,-1);
+#endif
+
+#if USE_SETEUID
+ seteuid(-1);
+#endif
+
+#if USE_SETUIDX
+ setuidx(ID_EFFECTIVE, -1);
+#endif
+
+ if (errno == ENOSYS) return -1;
+
+ return 0;
+}
+
+main()
+{
+ if (getuid() != 0) {
+#if (defined(AIX) && defined(USE_SETREUID))
+ /* setreuid is badly broken on AIX 4.1, we avoid it completely */
+ fprintf(stderr,"avoiding possibly broken setreuid\n");
+ exit(1);
+#endif
+
+ /* if not running as root then at least check to see if we get ENOSYS - this
+ handles Linux 2.0.x with glibc 2.1 */
+ fprintf(stderr,"not running as root: checking for ENOSYS\n");
+ exit(have_syscall());
+ }
+
+ gain_root_privilege();
+ gain_root_group_privilege();
+ set_effective_gid(1);
+ set_effective_uid(1);
+ save_re_uid();
+ restore_re_uid();
+ gain_root_privilege();
+ gain_root_group_privilege();
+ become_user_permanently(1, 1);
+ setuid(0);
+ if (getuid() == 0) {
+ fprintf(stderr,"uid not set permanently\n");
+ exit(1);
+ }
+
+ printf("OK\n");
+
+ exit(0);
+}
+#endif
+
+/****************************************************************************
+Check if we are setuid root. Used in libsmb and smbpasswd paranoia checks.
+****************************************************************************/
+BOOL is_setuid_root(void)
+{
+ return (geteuid() == (uid_t)0) && (getuid() != (uid_t)0);
+}
diff --git a/source/lib/util_sid.c b/source/lib/util_sid.c
new file mode 100644
index 00000000000..2c0bd797859
--- /dev/null
+++ b/source/lib/util_sid.c
@@ -0,0 +1,636 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Luke Kenneth Caseson Leighton 1998-1999
+ Copyright (C) Jeremy Allison 1999
+ Copyright (C) Stefan (metze) Metzmacher 2002
+ Copyright (C) Simo Sorce 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*
+ * Some useful sids
+ */
+
+DOM_SID global_sid_World_Domain; /* Everyone domain */
+DOM_SID global_sid_World; /* Everyone */
+DOM_SID global_sid_Creator_Owner_Domain; /* Creator Owner domain */
+DOM_SID global_sid_NT_Authority; /* NT Authority */
+DOM_SID global_sid_System; /* System */
+DOM_SID global_sid_NULL; /* NULL sid */
+DOM_SID global_sid_Authenticated_Users; /* All authenticated rids */
+DOM_SID global_sid_Network; /* Network rids */
+
+DOM_SID global_sid_Creator_Owner; /* Creator Owner */
+DOM_SID global_sid_Creator_Group; /* Creator Group */
+DOM_SID global_sid_Anonymous; /* Anonymous login */
+
+DOM_SID global_sid_Builtin; /* Local well-known domain */
+DOM_SID global_sid_Builtin_Administrators; /* Builtin administrators */
+DOM_SID global_sid_Builtin_Users; /* Builtin users */
+DOM_SID global_sid_Builtin_Guests; /* Builtin guest users */
+DOM_SID global_sid_Builtin_Power_Users; /* Builtin power users */
+DOM_SID global_sid_Builtin_Account_Operators; /* Builtin account operators */
+DOM_SID global_sid_Builtin_Server_Operators; /* Builtin server operators */
+DOM_SID global_sid_Builtin_Print_Operators; /* Builtin print operators */
+DOM_SID global_sid_Builtin_Backup_Operators; /* Builtin backup operators */
+DOM_SID global_sid_Builtin_Replicator; /* Builtin replicator */
+
+#define SECURITY_NULL_SID_AUTHORITY 0
+#define SECURITY_WORLD_SID_AUTHORITY 1
+#define SECURITY_LOCAL_SID_AUTHORITY 2
+#define SECURITY_CREATOR_SID_AUTHORITY 3
+#define SECURITY_NT_AUTHORITY 5
+
+/*
+ * An NT compatible anonymous token.
+ */
+
+static DOM_SID anon_sid_array[3];
+
+NT_USER_TOKEN anonymous_token = {
+ 3,
+ anon_sid_array
+};
+
+static DOM_SID system_sid_array[4];
+NT_USER_TOKEN system_token = {
+ 1,
+ system_sid_array
+};
+
+/****************************************************************************
+ Lookup string names for SID types.
+****************************************************************************/
+
+static const struct {
+ enum SID_NAME_USE sid_type;
+ const char *string;
+} sid_name_type[] = {
+ {SID_NAME_USER, "User"},
+ {SID_NAME_DOM_GRP, "Domain Group"},
+ {SID_NAME_DOMAIN, "Domain"},
+ {SID_NAME_ALIAS, "Local Group"},
+ {SID_NAME_WKN_GRP, "Well-known Group"},
+ {SID_NAME_DELETED, "Deleted Account"},
+ {SID_NAME_INVALID, "Invalid Account"},
+ {SID_NAME_UNKNOWN, "UNKNOWN"},
+ {SID_NAME_COMPUTER, "Computer"},
+
+ {(enum SID_NAME_USE)0, NULL}
+};
+
+const char *sid_type_lookup(uint32 sid_type)
+{
+ int i = 0;
+
+ /* Look through list */
+ while(sid_name_type[i].sid_type != 0) {
+ if (sid_name_type[i].sid_type == sid_type)
+ return sid_name_type[i].string;
+ i++;
+ }
+
+ /* Default return */
+ return "SID *TYPE* is INVALID";
+}
+
+/****************************************************************************
+ Creates some useful well known sids
+****************************************************************************/
+
+void generate_wellknown_sids(void)
+{
+ static BOOL initialised = False;
+
+ if (initialised)
+ return;
+
+ /* SECURITY_NULL_SID_AUTHORITY */
+ string_to_sid(&global_sid_NULL, "S-1-0-0");
+
+ /* SECURITY_WORLD_SID_AUTHORITY */
+ string_to_sid(&global_sid_World_Domain, "S-1-1");
+ string_to_sid(&global_sid_World, "S-1-1-0");
+
+ /* SECURITY_CREATOR_SID_AUTHORITY */
+ string_to_sid(&global_sid_Creator_Owner_Domain, "S-1-3");
+ string_to_sid(&global_sid_Creator_Owner, "S-1-3-0");
+ string_to_sid(&global_sid_Creator_Group, "S-1-3-1");
+
+ /* SECURITY_NT_AUTHORITY */
+ string_to_sid(&global_sid_NT_Authority, "S-1-5");
+ string_to_sid(&global_sid_Network, "S-1-5-2");
+ string_to_sid(&global_sid_Anonymous, "S-1-5-7");
+ string_to_sid(&global_sid_Authenticated_Users, "S-1-5-11");
+ string_to_sid(&global_sid_System, "S-1-5-18");
+
+ /* SECURITY_BUILTIN_DOMAIN_RID */
+ string_to_sid(&global_sid_Builtin, "S-1-5-32");
+ string_to_sid(&global_sid_Builtin_Administrators, "S-1-5-32-544");
+ string_to_sid(&global_sid_Builtin_Users, "S-1-5-32-545");
+ string_to_sid(&global_sid_Builtin_Guests, "S-1-5-32-546");
+ string_to_sid(&global_sid_Builtin_Power_Users, "S-1-5-32-547");
+ string_to_sid(&global_sid_Builtin_Account_Operators, "S-1-5-32-548");
+ string_to_sid(&global_sid_Builtin_Server_Operators, "S-1-5-32-549");
+ string_to_sid(&global_sid_Builtin_Print_Operators, "S-1-5-32-550");
+ string_to_sid(&global_sid_Builtin_Backup_Operators, "S-1-5-32-551");
+ string_to_sid(&global_sid_Builtin_Replicator, "S-1-5-32-552");
+
+ /* Create the anon token. */
+ sid_copy( &anonymous_token.user_sids[0], &global_sid_World);
+ sid_copy( &anonymous_token.user_sids[1], &global_sid_Network);
+ sid_copy( &anonymous_token.user_sids[2], &global_sid_Anonymous);
+
+ /* Create the system token. */
+ sid_copy( &system_token.user_sids[0], &global_sid_System);
+
+ initialised = True;
+}
+
+/**************************************************************************
+ Create the SYSTEM token.
+***************************************************************************/
+
+NT_USER_TOKEN *get_system_token(void)
+{
+ generate_wellknown_sids(); /* The token is initialised here */
+ return &system_token;
+}
+
+/**************************************************************************
+ Splits a name of format \DOMAIN\name or name into its two components.
+ Sets the DOMAIN name to global_myname() if it has not been specified.
+***************************************************************************/
+
+void split_domain_name(const char *fullname, char *domain, char *name)
+{
+ pstring full_name;
+ const char *sep;
+ char *p;
+
+ sep = lp_winbind_separator();
+
+ *domain = *name = '\0';
+
+ if (fullname[0] == sep[0] || fullname[0] == '\\')
+ fullname++;
+
+ pstrcpy(full_name, fullname);
+ p = strchr_m(full_name+1, '\\');
+ if (!p) p = strchr_m(full_name+1, sep[0]);
+
+ if (p != NULL) {
+ *p = 0;
+ fstrcpy(domain, full_name);
+ fstrcpy(name, p+1);
+ } else {
+ fstrcpy(domain, global_myname());
+ fstrcpy(name, full_name);
+ }
+
+ DEBUG(10,("split_domain_name:name '%s' split into domain :'%s' and user :'%s'\n",
+ fullname, domain, name));
+}
+
+/****************************************************************************
+ Test if a SID is wellknown and resolvable.
+****************************************************************************/
+
+BOOL resolvable_wellknown_sid(DOM_SID *sid)
+{
+ uint32 ia = (sid->id_auth[5]) +
+ (sid->id_auth[4] << 8 ) +
+ (sid->id_auth[3] << 16) +
+ (sid->id_auth[2] << 24);
+
+ if (sid->sid_rev_num != SEC_DESC_REVISION || sid->num_auths < 1)
+ return False;
+
+ return (ia == SECURITY_WORLD_SID_AUTHORITY ||
+ ia == SECURITY_CREATOR_SID_AUTHORITY);
+}
+
+/*****************************************************************
+ Convert a SID to an ascii string.
+*****************************************************************/
+
+char *sid_to_string(fstring sidstr_out, const DOM_SID *sid)
+{
+ char subauth[16];
+ int i;
+ uint32 ia;
+
+ if (!sid) {
+ fstrcpy(sidstr_out, "(NULL SID)");
+ return sidstr_out;
+ }
+
+ /*
+ * BIG NOTE: this function only does SIDS where the identauth is not >= 2^32
+ * in a range of 2^48.
+ */
+ ia = (sid->id_auth[5]) +
+ (sid->id_auth[4] << 8 ) +
+ (sid->id_auth[3] << 16) +
+ (sid->id_auth[2] << 24);
+
+ slprintf(sidstr_out, sizeof(fstring) - 1, "S-%u-%lu", (unsigned int)sid->sid_rev_num, (unsigned long)ia);
+
+ for (i = 0; i < sid->num_auths; i++) {
+ slprintf(subauth, sizeof(subauth)-1, "-%lu", (unsigned long)sid->sub_auths[i]);
+ fstrcat(sidstr_out, subauth);
+ }
+
+ return sidstr_out;
+}
+
+/*****************************************************************
+ Useful function for debug lines.
+*****************************************************************/
+
+const char *sid_string_static(const DOM_SID *sid)
+{
+ static fstring sid_str;
+ sid_to_string(sid_str, sid);
+ return sid_str;
+}
+
+/*****************************************************************
+ Convert a string to a SID. Returns True on success, False on fail.
+*****************************************************************/
+
+BOOL string_to_sid(DOM_SID *sidout, const char *sidstr)
+{
+ pstring tok;
+ char *q;
+ const char *p;
+ /* BIG NOTE: this function only does SIDS where the identauth is not >= 2^32 */
+ uint32 ia;
+
+ if (StrnCaseCmp( sidstr, "S-", 2)) {
+ DEBUG(0,("string_to_sid: Sid %s does not start with 'S-'.\n", sidstr));
+ return False;
+ }
+
+ memset((char *)sidout, '\0', sizeof(DOM_SID));
+
+ p = q = strdup(sidstr + 2);
+ if (p == NULL) {
+ DEBUG(0, ("string_to_sid: out of memory!\n"));
+ return False;
+ }
+
+ if (!next_token(&p, tok, "-", sizeof(tok))) {
+ DEBUG(0,("string_to_sid: Sid %s is not in a valid format.\n", sidstr));
+ SAFE_FREE(q);
+ return False;
+ }
+
+ /* Get the revision number. */
+ sidout->sid_rev_num = (uint8)strtoul(tok, NULL, 10);
+
+ if (!next_token(&p, tok, "-", sizeof(tok))) {
+ DEBUG(0,("string_to_sid: Sid %s is not in a valid format.\n", sidstr));
+ SAFE_FREE(q);
+ return False;
+ }
+
+ /* identauth in decimal should be < 2^32 */
+ ia = (uint32)strtoul(tok, NULL, 10);
+
+ /* NOTE - the ia value is in big-endian format. */
+ sidout->id_auth[0] = 0;
+ sidout->id_auth[1] = 0;
+ sidout->id_auth[2] = (ia & 0xff000000) >> 24;
+ sidout->id_auth[3] = (ia & 0x00ff0000) >> 16;
+ sidout->id_auth[4] = (ia & 0x0000ff00) >> 8;
+ sidout->id_auth[5] = (ia & 0x000000ff);
+
+ sidout->num_auths = 0;
+
+ while(next_token(&p, tok, "-", sizeof(tok)) &&
+ sidout->num_auths < MAXSUBAUTHS) {
+ /*
+ * NOTE - the subauths are in native machine-endian format. They
+ * are converted to little-endian when linearized onto the wire.
+ */
+ sid_append_rid(sidout, (uint32)strtoul(tok, NULL, 10));
+ }
+
+ SAFE_FREE(q);
+ return True;
+}
+
+/*****************************************************************
+ Add a rid to the end of a sid
+*****************************************************************/
+
+BOOL sid_append_rid(DOM_SID *sid, uint32 rid)
+{
+ if (sid->num_auths < MAXSUBAUTHS) {
+ sid->sub_auths[sid->num_auths++] = rid;
+ return True;
+ }
+ return False;
+}
+
+/*****************************************************************
+ Removes the last rid from the end of a sid
+*****************************************************************/
+
+BOOL sid_split_rid(DOM_SID *sid, uint32 *rid)
+{
+ if (sid->num_auths > 0) {
+ sid->num_auths--;
+ *rid = sid->sub_auths[sid->num_auths];
+ return True;
+ }
+ return False;
+}
+
+/*****************************************************************
+ Return the last rid from the end of a sid
+*****************************************************************/
+
+BOOL sid_peek_rid(const DOM_SID *sid, uint32 *rid)
+{
+ if (!sid || !rid)
+ return False;
+
+ if (sid->num_auths > 0) {
+ *rid = sid->sub_auths[sid->num_auths - 1];
+ return True;
+ }
+ return False;
+}
+
+/*****************************************************************
+ Return the last rid from the end of a sid
+ and check the sid against the exp_dom_sid
+*****************************************************************/
+
+BOOL sid_peek_check_rid(const DOM_SID *exp_dom_sid, const DOM_SID *sid, uint32 *rid)
+{
+ if (!exp_dom_sid || !sid || !rid)
+ return False;
+
+ if (sid->num_auths != (exp_dom_sid->num_auths+1)) {
+ return False;
+ }
+
+ if (sid_compare_domain(exp_dom_sid, sid)!=0){
+ *rid=(-1);
+ return False;
+ }
+
+ return sid_peek_rid(sid, rid);
+}
+
+/*****************************************************************
+ Copies a sid
+*****************************************************************/
+
+void sid_copy(DOM_SID *dst, const DOM_SID *src)
+{
+ int i;
+
+ ZERO_STRUCTP(dst);
+
+ dst->sid_rev_num = src->sid_rev_num;
+ dst->num_auths = src->num_auths;
+
+ memcpy(&dst->id_auth[0], &src->id_auth[0], sizeof(src->id_auth));
+
+ for (i = 0; i < src->num_auths; i++)
+ dst->sub_auths[i] = src->sub_auths[i];
+}
+
+/*****************************************************************
+ Write a sid out into on-the-wire format.
+*****************************************************************/
+
+BOOL sid_linearize(char *outbuf, size_t len, const DOM_SID *sid)
+{
+ size_t i;
+
+ if (len < sid_size(sid))
+ return False;
+
+ SCVAL(outbuf,0,sid->sid_rev_num);
+ SCVAL(outbuf,1,sid->num_auths);
+ memcpy(&outbuf[2], sid->id_auth, 6);
+ for(i = 0; i < sid->num_auths; i++)
+ SIVAL(outbuf, 8 + (i*4), sid->sub_auths[i]);
+
+ return True;
+}
+
+/*****************************************************************
+ Parse a on-the-wire SID to a DOM_SID.
+*****************************************************************/
+
+BOOL sid_parse(const char *inbuf, size_t len, DOM_SID *sid)
+{
+ int i;
+ if (len < 8)
+ return False;
+
+ ZERO_STRUCTP(sid);
+
+ sid->sid_rev_num = CVAL(inbuf, 0);
+ sid->num_auths = CVAL(inbuf, 1);
+ memcpy(sid->id_auth, inbuf+2, 6);
+ if (len < 8 + sid->num_auths*4)
+ return False;
+ for (i=0;i<sid->num_auths;i++)
+ sid->sub_auths[i] = IVAL(inbuf, 8+i*4);
+ return True;
+}
+
+/*****************************************************************
+ Compare the auth portion of two sids.
+*****************************************************************/
+
+static int sid_compare_auth(const DOM_SID *sid1, const DOM_SID *sid2)
+{
+ int i;
+
+ if (sid1 == sid2)
+ return 0;
+ if (!sid1)
+ return -1;
+ if (!sid2)
+ return 1;
+
+ if (sid1->sid_rev_num != sid2->sid_rev_num)
+ return sid1->sid_rev_num - sid2->sid_rev_num;
+
+ for (i = 0; i < 6; i++)
+ if (sid1->id_auth[i] != sid2->id_auth[i])
+ return sid1->id_auth[i] - sid2->id_auth[i];
+
+ return 0;
+}
+
+/*****************************************************************
+ Compare two sids.
+*****************************************************************/
+
+int sid_compare(const DOM_SID *sid1, const DOM_SID *sid2)
+{
+ int i;
+
+ if (sid1 == sid2)
+ return 0;
+ if (!sid1)
+ return -1;
+ if (!sid2)
+ return 1;
+
+ /* Compare most likely different rids, first: i.e start at end */
+ if (sid1->num_auths != sid2->num_auths)
+ return sid1->num_auths - sid2->num_auths;
+
+ for (i = sid1->num_auths-1; i >= 0; --i)
+ if (sid1->sub_auths[i] != sid2->sub_auths[i])
+ return sid1->sub_auths[i] - sid2->sub_auths[i];
+
+ return sid_compare_auth(sid1, sid2);
+}
+
+/*****************************************************************
+ See if 2 SIDs are in the same domain
+ this just compares the leading sub-auths
+*****************************************************************/
+
+int sid_compare_domain(const DOM_SID *sid1, const DOM_SID *sid2)
+{
+ int n, i;
+
+ n = MIN(sid1->num_auths, sid2->num_auths);
+
+ for (i = n-1; i >= 0; --i)
+ if (sid1->sub_auths[i] != sid2->sub_auths[i])
+ return sid1->sub_auths[i] - sid2->sub_auths[i];
+
+ return sid_compare_auth(sid1, sid2);
+}
+
+/*****************************************************************
+ Compare two sids.
+*****************************************************************/
+
+BOOL sid_equal(const DOM_SID *sid1, const DOM_SID *sid2)
+{
+ return sid_compare(sid1, sid2) == 0;
+}
+
+/*****************************************************************
+ Check if the SID is the builtin SID (S-1-5-32).
+*****************************************************************/
+
+BOOL sid_check_is_builtin(const DOM_SID *sid)
+{
+ return sid_equal(sid, &global_sid_Builtin);
+}
+
+/*****************************************************************
+ Check if the SID is one of the builtin SIDs (S-1-5-32-a).
+*****************************************************************/
+
+BOOL sid_check_is_in_builtin(const DOM_SID *sid)
+{
+ DOM_SID dom_sid;
+ uint32 rid;
+
+ sid_copy(&dom_sid, sid);
+ sid_split_rid(&dom_sid, &rid);
+
+ return sid_equal(&dom_sid, &global_sid_Builtin);
+}
+
+/*****************************************************************
+ Calculates size of a sid.
+*****************************************************************/
+
+size_t sid_size(const DOM_SID *sid)
+{
+ if (sid == NULL)
+ return 0;
+
+ return sid->num_auths * sizeof(uint32) + 8;
+}
+
+/*****************************************************************
+ Returns true if SID is internal (and non-mappable).
+*****************************************************************/
+
+BOOL non_mappable_sid(DOM_SID *sid)
+{
+ DOM_SID dom;
+ uint32 rid;
+
+ sid_copy(&dom, sid);
+ sid_split_rid(&dom, &rid);
+
+ if (sid_equal(&dom, &global_sid_Builtin))
+ return True;
+
+ if (sid_equal(&dom, &global_sid_NT_Authority))
+ return True;
+
+ return False;
+}
+
+/*****************************************************************
+ Return the binary string representation of a DOM_SID.
+ Caller must free.
+*****************************************************************/
+
+char *sid_binstring(const DOM_SID *sid)
+{
+ char *buf, *s;
+ int len = sid_size(sid);
+ buf = malloc(len);
+ if (!buf)
+ return NULL;
+ sid_linearize(buf, len, sid);
+ s = binary_string(buf, len);
+ free(buf);
+ return s;
+}
+
+/*******************************************************************
+ Tallocs a duplicate SID.
+********************************************************************/
+
+DOM_SID *sid_dup_talloc(TALLOC_CTX *ctx, const DOM_SID *src)
+{
+ DOM_SID *dst;
+
+ if(!src)
+ return NULL;
+
+ if((dst = talloc_zero(ctx, sizeof(DOM_SID))) != NULL) {
+ sid_copy( dst, src);
+ }
+
+ return dst;
+}
diff --git a/source/lib/util_smbd.c b/source/lib/util_smbd.c
new file mode 100644
index 00000000000..071f20b4162
--- /dev/null
+++ b/source/lib/util_smbd.c
@@ -0,0 +1,65 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions, used in smbd only
+ Copyright (C) Andrew Tridgell 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*
+ This function requires sys_getgrouplist - which is only
+ available in smbd due to it's use of become_root() in a
+ legacy systems hack.
+*/
+
+/*
+ return a full list of groups for a user
+
+ returns the number of groups the user is a member of. The return will include the
+ users primary group.
+
+ remember to free the resulting gid_t array
+
+ NOTE! uses become_root() to gain correct priviages on systems
+ that lack a native getgroups() call (uses initgroups and getgroups)
+*/
+int getgroups_user(const char *user, gid_t **groups)
+{
+ struct passwd *pwd;
+ int ngrp, max_grp;
+
+ pwd = getpwnam_alloc(user);
+ if (!pwd) return -1;
+
+ max_grp = groups_max();
+ (*groups) = (gid_t *)malloc(sizeof(gid_t) * max_grp);
+ if (! *groups) {
+ passwd_free(&pwd);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ ngrp = sys_getgrouplist(user, pwd->pw_gid, *groups, &max_grp);
+ if (ngrp <= 0) {
+ passwd_free(&pwd);
+ free(*groups);
+ return ngrp;
+ }
+
+ passwd_free(&pwd);
+ return ngrp;
+}
diff --git a/source/lib/util_sock.c b/source/lib/util_sock.c
new file mode 100644
index 00000000000..845aaa4b13a
--- /dev/null
+++ b/source/lib/util_sock.c
@@ -0,0 +1,1078 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Tim Potter 2000-2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* the last IP received from */
+struct in_addr lastip;
+
+/* the last port received from */
+int lastport=0;
+
+int smb_read_error = 0;
+
+static char *get_socket_addr(int fd)
+{
+ struct sockaddr sa;
+ struct sockaddr_in *sockin = (struct sockaddr_in *) (&sa);
+ int length = sizeof(sa);
+ static fstring addr_buf;
+
+ fstrcpy(addr_buf,"0.0.0.0");
+
+ if (fd == -1) {
+ return addr_buf;
+ }
+
+ if (getsockname(fd, &sa, &length) < 0) {
+ DEBUG(0,("getpeername failed. Error was %s\n", strerror(errno) ));
+ return addr_buf;
+ }
+
+ fstrcpy(addr_buf,(char *)inet_ntoa(sockin->sin_addr));
+
+ return addr_buf;
+}
+
+/****************************************************************************
+ Determine if a file descriptor is in fact a socket.
+****************************************************************************/
+
+BOOL is_a_socket(int fd)
+{
+ int v,l;
+ l = sizeof(int);
+ return(getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&v, &l) == 0);
+}
+
+enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON};
+
+typedef struct smb_socket_option {
+ const char *name;
+ int level;
+ int option;
+ int value;
+ int opttype;
+} smb_socket_option;
+
+static const smb_socket_option socket_options[] = {
+ {"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, 0, OPT_BOOL},
+ {"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, 0, OPT_BOOL},
+ {"SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, 0, OPT_BOOL},
+#ifdef TCP_NODELAY
+ {"TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY, 0, OPT_BOOL},
+#endif
+#ifdef IPTOS_LOWDELAY
+ {"IPTOS_LOWDELAY", IPPROTO_IP, IP_TOS, IPTOS_LOWDELAY, OPT_ON},
+#endif
+#ifdef IPTOS_THROUGHPUT
+ {"IPTOS_THROUGHPUT", IPPROTO_IP, IP_TOS, IPTOS_THROUGHPUT, OPT_ON},
+#endif
+#ifdef SO_REUSEPORT
+ {"SO_REUSEPORT", SOL_SOCKET, SO_REUSEPORT, 0, OPT_BOOL},
+#endif
+#ifdef SO_SNDBUF
+ {"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, 0, OPT_INT},
+#endif
+#ifdef SO_RCVBUF
+ {"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, 0, OPT_INT},
+#endif
+#ifdef SO_SNDLOWAT
+ {"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, 0, OPT_INT},
+#endif
+#ifdef SO_RCVLOWAT
+ {"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, 0, OPT_INT},
+#endif
+#ifdef SO_SNDTIMEO
+ {"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, 0, OPT_INT},
+#endif
+#ifdef SO_RCVTIMEO
+ {"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, 0, OPT_INT},
+#endif
+ {NULL,0,0,0,0}};
+
+/****************************************************************************
+ Print socket options.
+****************************************************************************/
+
+static void print_socket_options(int s)
+{
+ int value, vlen = 4;
+ const smb_socket_option *p = &socket_options[0];
+
+ /* wrapped in if statement to prevent streams leak in SCO Openserver 5.0 */
+ /* reported on samba-technical --jerry */
+ if ( DEBUGLEVEL >= 5 ) {
+ for (; p->name != NULL; p++) {
+ if (getsockopt(s, p->level, p->option, (void *)&value, &vlen) == -1) {
+ DEBUG(5,("Could not test socket option %s.\n", p->name));
+ } else {
+ DEBUG(5,("socket option %s = %d\n",p->name,value));
+ }
+ }
+ }
+ }
+
+/****************************************************************************
+ Set user socket options.
+****************************************************************************/
+
+void set_socket_options(int fd, const char *options)
+{
+ fstring tok;
+
+ while (next_token(&options,tok," \t,", sizeof(tok))) {
+ int ret=0,i;
+ int value = 1;
+ char *p;
+ BOOL got_value = False;
+
+ if ((p = strchr_m(tok,'='))) {
+ *p = 0;
+ value = atoi(p+1);
+ got_value = True;
+ }
+
+ for (i=0;socket_options[i].name;i++)
+ if (strequal(socket_options[i].name,tok))
+ break;
+
+ if (!socket_options[i].name) {
+ DEBUG(0,("Unknown socket option %s\n",tok));
+ continue;
+ }
+
+ switch (socket_options[i].opttype) {
+ case OPT_BOOL:
+ case OPT_INT:
+ ret = setsockopt(fd,socket_options[i].level,
+ socket_options[i].option,(char *)&value,sizeof(int));
+ break;
+
+ case OPT_ON:
+ if (got_value)
+ DEBUG(0,("syntax error - %s does not take a value\n",tok));
+
+ {
+ int on = socket_options[i].value;
+ ret = setsockopt(fd,socket_options[i].level,
+ socket_options[i].option,(char *)&on,sizeof(int));
+ }
+ break;
+ }
+
+ if (ret != 0)
+ DEBUG(0,("Failed to set socket option %s (Error %s)\n",tok, strerror(errno) ));
+ }
+
+ print_socket_options(fd);
+}
+
+/****************************************************************************
+ Read from a socket.
+****************************************************************************/
+
+ssize_t read_udp_socket(int fd,char *buf,size_t len)
+{
+ ssize_t ret;
+ struct sockaddr_in sock;
+ socklen_t socklen = sizeof(sock);
+
+ memset((char *)&sock,'\0',socklen);
+ memset((char *)&lastip,'\0',sizeof(lastip));
+ ret = (ssize_t)sys_recvfrom(fd,buf,len,0,(struct sockaddr *)&sock,&socklen);
+ if (ret <= 0) {
+ DEBUG(2,("read socket failed. ERRNO=%s\n",strerror(errno)));
+ return(0);
+ }
+
+ lastip = sock.sin_addr;
+ lastport = ntohs(sock.sin_port);
+
+ DEBUG(10,("read_udp_socket: lastip %s lastport %d read: %lu\n",
+ inet_ntoa(lastip), lastport, (unsigned long)ret));
+
+ return(ret);
+}
+
+/****************************************************************************
+ Read data from a socket with a timout in msec.
+ mincount = if timeout, minimum to read before returning
+ maxcount = number to be read.
+ time_out = timeout in milliseconds
+****************************************************************************/
+
+ssize_t read_socket_with_timeout(int fd,char *buf,size_t mincnt,size_t maxcnt,unsigned int time_out)
+{
+ fd_set fds;
+ int selrtn;
+ ssize_t readret;
+ size_t nread = 0;
+ struct timeval timeout;
+
+ /* just checking .... */
+ if (maxcnt <= 0)
+ return(0);
+
+ smb_read_error = 0;
+
+ /* Blocking read */
+ if (time_out <= 0) {
+ if (mincnt == 0) mincnt = maxcnt;
+
+ while (nread < mincnt) {
+ readret = sys_read(fd, buf + nread, maxcnt - nread);
+
+ if (readret == 0) {
+ DEBUG(5,("read_socket_with_timeout: blocking read. EOF from client.\n"));
+ smb_read_error = READ_EOF;
+ return -1;
+ }
+
+ if (readret == -1) {
+ DEBUG(0,("read_socket_with_timeout: read error = %s.\n", strerror(errno) ));
+ smb_read_error = READ_ERROR;
+ return -1;
+ }
+ nread += readret;
+ }
+ return((ssize_t)nread);
+ }
+
+ /* Most difficult - timeout read */
+ /* If this is ever called on a disk file and
+ mincnt is greater then the filesize then
+ system performance will suffer severely as
+ select always returns true on disk files */
+
+ /* Set initial timeout */
+ timeout.tv_sec = (time_t)(time_out / 1000);
+ timeout.tv_usec = (long)(1000 * (time_out % 1000));
+
+ for (nread=0; nread < mincnt; ) {
+ FD_ZERO(&fds);
+ FD_SET(fd,&fds);
+
+ selrtn = sys_select_intr(fd+1,&fds,NULL,NULL,&timeout);
+
+ /* Check if error */
+ if (selrtn == -1) {
+ /* something is wrong. Maybe the socket is dead? */
+ DEBUG(0,("read_socket_with_timeout: timeout read. select error = %s.\n", strerror(errno) ));
+ smb_read_error = READ_ERROR;
+ return -1;
+ }
+
+ /* Did we timeout ? */
+ if (selrtn == 0) {
+ DEBUG(10,("read_socket_with_timeout: timeout read. select timed out.\n"));
+ smb_read_error = READ_TIMEOUT;
+ return -1;
+ }
+
+ readret = sys_read(fd, buf+nread, maxcnt-nread);
+
+ if (readret == 0) {
+ /* we got EOF on the file descriptor */
+ DEBUG(5,("read_socket_with_timeout: timeout read. EOF from client.\n"));
+ smb_read_error = READ_EOF;
+ return -1;
+ }
+
+ if (readret == -1) {
+ /* the descriptor is probably dead */
+ DEBUG(0,("read_socket_with_timeout: timeout read. read error = %s.\n", strerror(errno) ));
+ smb_read_error = READ_ERROR;
+ return -1;
+ }
+
+ nread += readret;
+ }
+
+ /* Return the number we got */
+ return (ssize_t)nread;
+}
+
+/****************************************************************************
+ Read data from the client, reading exactly N bytes.
+****************************************************************************/
+
+ssize_t read_data(int fd,char *buffer,size_t N)
+{
+ ssize_t ret;
+ size_t total=0;
+
+ smb_read_error = 0;
+
+ while (total < N) {
+ ret = sys_read(fd,buffer + total,N - total);
+
+ if (ret == 0) {
+ DEBUG(10,("read_data: read of %d returned 0. Error = %s\n", (int)(N - total), strerror(errno) ));
+ smb_read_error = READ_EOF;
+ return 0;
+ }
+
+ if (ret == -1) {
+ DEBUG(0,("read_data: read failure for %d. Error = %s\n", (int)(N - total), strerror(errno) ));
+ smb_read_error = READ_ERROR;
+ return -1;
+ }
+ total += ret;
+ }
+ return (ssize_t)total;
+}
+
+/****************************************************************************
+ Read data from a socket, reading exactly N bytes.
+****************************************************************************/
+
+static ssize_t read_socket_data(int fd,char *buffer,size_t N)
+{
+ ssize_t ret;
+ size_t total=0;
+
+ smb_read_error = 0;
+
+ while (total < N) {
+ ret = sys_read(fd,buffer + total,N - total);
+
+ if (ret == 0) {
+ DEBUG(10,("read_socket_data: recv of %d returned 0. Error = %s\n", (int)(N - total), strerror(errno) ));
+ smb_read_error = READ_EOF;
+ return 0;
+ }
+
+ if (ret == -1) {
+ DEBUG(0,("read_socket_data: recv failure for %d. Error = %s\n", (int)(N - total), strerror(errno) ));
+ smb_read_error = READ_ERROR;
+ return -1;
+ }
+ total += ret;
+ }
+ return (ssize_t)total;
+}
+
+/****************************************************************************
+ Write data to a fd.
+****************************************************************************/
+
+ssize_t write_data(int fd,char *buffer,size_t N)
+{
+ size_t total=0;
+ ssize_t ret;
+
+ while (total < N) {
+ ret = sys_write(fd,buffer + total,N - total);
+
+ if (ret == -1) {
+ DEBUG(0,("write_data: write failure. Error = %s\n", strerror(errno) ));
+ return -1;
+ }
+ if (ret == 0)
+ return total;
+
+ total += ret;
+ }
+ return (ssize_t)total;
+}
+
+/****************************************************************************
+ Write data to a socket - use send rather than write.
+****************************************************************************/
+
+static ssize_t write_socket_data(int fd,char *buffer,size_t N)
+{
+ size_t total=0;
+ ssize_t ret;
+
+ while (total < N) {
+ ret = sys_send(fd,buffer + total,N - total,0);
+
+ if (ret == -1) {
+ DEBUG(0,("write_socket_data: write failure. Error = %s\n", strerror(errno) ));
+ return -1;
+ }
+ if (ret == 0)
+ return total;
+
+ total += ret;
+ }
+ return (ssize_t)total;
+}
+
+/****************************************************************************
+ Write to a socket.
+****************************************************************************/
+
+ssize_t write_socket(int fd,char *buf,size_t len)
+{
+ ssize_t ret=0;
+
+ DEBUG(6,("write_socket(%d,%d)\n",fd,(int)len));
+ ret = write_socket_data(fd,buf,len);
+
+ DEBUG(6,("write_socket(%d,%d) wrote %d\n",fd,(int)len,(int)ret));
+ if(ret <= 0)
+ DEBUG(0,("write_socket: Error writing %d bytes to socket %d: ERRNO = %s\n",
+ (int)len, fd, strerror(errno) ));
+
+ return(ret);
+}
+
+/****************************************************************************
+ Send a keepalive packet (rfc1002).
+****************************************************************************/
+
+BOOL send_keepalive(int client)
+{
+ unsigned char buf[4];
+
+ buf[0] = SMBkeepalive;
+ buf[1] = buf[2] = buf[3] = 0;
+
+ return(write_socket_data(client,(char *)buf,4) == 4);
+}
+
+
+/****************************************************************************
+ Read 4 bytes of a smb packet and return the smb length of the packet.
+ Store the result in the buffer.
+ This version of the function will return a length of zero on receiving
+ a keepalive packet.
+ Timeout is in milliseconds.
+****************************************************************************/
+
+static ssize_t read_smb_length_return_keepalive(int fd,char *inbuf,unsigned int timeout)
+{
+ ssize_t len=0;
+ int msg_type;
+ BOOL ok = False;
+
+ while (!ok) {
+ if (timeout > 0)
+ ok = (read_socket_with_timeout(fd,inbuf,4,4,timeout) == 4);
+ else
+ ok = (read_socket_data(fd,inbuf,4) == 4);
+
+ if (!ok)
+ return(-1);
+
+ len = smb_len(inbuf);
+ msg_type = CVAL(inbuf,0);
+
+ if (msg_type == SMBkeepalive)
+ DEBUG(5,("Got keepalive packet\n"));
+ }
+
+ DEBUG(10,("got smb length of %lu\n",(unsigned long)len));
+
+ return(len);
+}
+
+/****************************************************************************
+ Read 4 bytes of a smb packet and return the smb length of the packet.
+ Store the result in the buffer. This version of the function will
+ never return a session keepalive (length of zero).
+ Timeout is in milliseconds.
+****************************************************************************/
+
+ssize_t read_smb_length(int fd,char *inbuf,unsigned int timeout)
+{
+ ssize_t len;
+
+ for(;;) {
+ len = read_smb_length_return_keepalive(fd, inbuf, timeout);
+
+ if(len < 0)
+ return len;
+
+ /* Ignore session keepalives. */
+ if(CVAL(inbuf,0) != SMBkeepalive)
+ break;
+ }
+
+ DEBUG(10,("read_smb_length: got smb length of %lu\n",
+ (unsigned long)len));
+
+ return len;
+}
+
+/****************************************************************************
+ Read an smb from a fd. Note that the buffer *MUST* be of size
+ BUFFER_SIZE+SAFETY_MARGIN.
+ The timeout is in milliseconds.
+ This function will return on receipt of a session keepalive packet.
+ Doesn't check the MAC on signed packets.
+****************************************************************************/
+
+BOOL receive_smb_raw(int fd,char *buffer, unsigned int timeout)
+{
+ ssize_t len,ret;
+
+ smb_read_error = 0;
+
+ memset(buffer,'\0',smb_size + 100);
+
+ len = read_smb_length_return_keepalive(fd,buffer,timeout);
+ if (len < 0) {
+ DEBUG(10,("receive_smb_raw: length < 0!\n"));
+
+ /*
+ * Correct fix. smb_read_error may have already been
+ * set. Only set it here if not already set. Global
+ * variables still suck :-). JRA.
+ */
+
+ if (smb_read_error == 0)
+ smb_read_error = READ_ERROR;
+ return False;
+ }
+
+ /*
+ * A WRITEX with CAP_LARGE_WRITEX can be 64k worth of data plus 65 bytes
+ * of header. Don't print the error if this fits.... JRA.
+ */
+
+ if (len > (BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE)) {
+ DEBUG(0,("Invalid packet length! (%lu bytes).\n",(unsigned long)len));
+ if (len > BUFFER_SIZE + (SAFETY_MARGIN/2)) {
+
+ /*
+ * Correct fix. smb_read_error may have already been
+ * set. Only set it here if not already set. Global
+ * variables still suck :-). JRA.
+ */
+
+ if (smb_read_error == 0)
+ smb_read_error = READ_ERROR;
+ return False;
+ }
+ }
+
+ if(len > 0) {
+ ret = read_socket_data(fd,buffer+4,len);
+ if (ret != len) {
+ if (smb_read_error == 0)
+ smb_read_error = READ_ERROR;
+ return False;
+ }
+
+ /* not all of samba3 properly checks for packet-termination of strings. This
+ ensures that we don't run off into empty space. */
+ SSVAL(buffer+4,len, 0);
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Wrapper for receive_smb_raw().
+ Checks the MAC on signed packets.
+****************************************************************************/
+
+BOOL receive_smb(int fd,char *buffer, unsigned int timeout)
+{
+ if (!receive_smb_raw(fd, buffer, timeout)) {
+ return False;
+ }
+
+ /* Check the incoming SMB signature. */
+ if (!srv_check_sign_mac(buffer, True)) {
+ DEBUG(0, ("receive_smb: SMB Signature verification failed on incoming packet!\n"));
+ if (smb_read_error == 0)
+ smb_read_error = READ_BAD_SIG;
+ return False;
+ };
+
+ return(True);
+}
+
+/****************************************************************************
+ Send an smb to a fd.
+****************************************************************************/
+
+BOOL send_smb(int fd,char *buffer)
+{
+ size_t len;
+ size_t nwritten=0;
+ ssize_t ret;
+
+ /* Sign the outgoing packet if required. */
+ srv_calculate_sign_mac(buffer);
+
+ len = smb_len(buffer) + 4;
+
+ while (nwritten < len) {
+ ret = write_socket(fd,buffer+nwritten,len - nwritten);
+ if (ret <= 0) {
+ DEBUG(0,("Error writing %d bytes to client. %d. (%s)\n",
+ (int)len,(int)ret, strerror(errno) ));
+ return False;
+ }
+ nwritten += ret;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Open a socket of the specified type, port, and address for incoming data.
+****************************************************************************/
+
+int open_socket_in( int type, int port, int dlevel, uint32 socket_addr, BOOL rebind )
+{
+ struct sockaddr_in sock;
+ int res;
+
+ memset( (char *)&sock, '\0', sizeof(sock) );
+
+#ifdef HAVE_SOCK_SIN_LEN
+ sock.sin_len = sizeof(sock);
+#endif
+ sock.sin_port = htons( port );
+ sock.sin_family = AF_INET;
+ sock.sin_addr.s_addr = socket_addr;
+
+ res = socket( AF_INET, type, 0 );
+ if( res == -1 ) {
+ if( DEBUGLVL(0) ) {
+ dbgtext( "open_socket_in(): socket() call failed: " );
+ dbgtext( "%s\n", strerror( errno ) );
+ }
+ return -1;
+ }
+
+ /* This block sets/clears the SO_REUSEADDR and possibly SO_REUSEPORT. */
+ {
+ int val = rebind ? 1 : 0;
+ if( setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val)) == -1 ) {
+ if( DEBUGLVL( dlevel ) ) {
+ dbgtext( "open_socket_in(): setsockopt: " );
+ dbgtext( "SO_REUSEADDR = %s ", val?"True":"False" );
+ dbgtext( "on port %d failed ", port );
+ dbgtext( "with error = %s\n", strerror(errno) );
+ }
+ }
+#ifdef SO_REUSEPORT
+ if( setsockopt(res,SOL_SOCKET,SO_REUSEPORT,(char *)&val,sizeof(val)) == -1 ) {
+ if( DEBUGLVL( dlevel ) ) {
+ dbgtext( "open_socket_in(): setsockopt: ");
+ dbgtext( "SO_REUSEPORT = %s ", val?"True":"False" );
+ dbgtext( "on port %d failed ", port );
+ dbgtext( "with error = %s\n", strerror(errno) );
+ }
+ }
+#endif /* SO_REUSEPORT */
+ }
+
+ /* now we've got a socket - we need to bind it */
+ if( bind( res, (struct sockaddr *)&sock, sizeof(sock) ) == -1 ) {
+ if( DEBUGLVL(dlevel) && (port == SMB_PORT1 || port == SMB_PORT2 || port == NMB_PORT) ) {
+ dbgtext( "bind failed on port %d ", port );
+ dbgtext( "socket_addr = %s.\n", inet_ntoa( sock.sin_addr ) );
+ dbgtext( "Error = %s\n", strerror(errno) );
+ }
+ close( res );
+ return( -1 );
+ }
+
+ DEBUG( 10, ( "bind succeeded on port %d\n", port ) );
+
+ return( res );
+ }
+
+/****************************************************************************
+ Create an outgoing socket. timeout is in milliseconds.
+**************************************************************************/
+
+int open_socket_out(int type, struct in_addr *addr, int port ,int timeout)
+{
+ struct sockaddr_in sock_out;
+ int res,ret;
+ int connect_loop = 10;
+ int increment = 10;
+
+ /* create a socket to write to */
+ res = socket(PF_INET, type, 0);
+ if (res == -1) {
+ DEBUG(0,("socket error (%s)\n", strerror(errno)));
+ return -1;
+ }
+
+ if (type != SOCK_STREAM)
+ return(res);
+
+ memset((char *)&sock_out,'\0',sizeof(sock_out));
+ putip((char *)&sock_out.sin_addr,(char *)addr);
+
+ sock_out.sin_port = htons( port );
+ sock_out.sin_family = PF_INET;
+
+ /* set it non-blocking */
+ set_blocking(res,False);
+
+ DEBUG(3,("Connecting to %s at port %d\n",inet_ntoa(*addr),port));
+
+ /* and connect it to the destination */
+ connect_again:
+
+ ret = connect(res,(struct sockaddr *)&sock_out,sizeof(sock_out));
+
+ /* Some systems return EAGAIN when they mean EINPROGRESS */
+ if (ret < 0 && (errno == EINPROGRESS || errno == EALREADY ||
+ errno == EAGAIN) && (connect_loop < timeout) ) {
+ smb_msleep(connect_loop);
+ connect_loop += increment;
+ if (increment < 250) {
+ /* After 8 rounds we end up at a max of 255 msec */
+ increment *= 1.5;
+ }
+ goto connect_again;
+ }
+
+ if (ret < 0 && (errno == EINPROGRESS || errno == EALREADY ||
+ errno == EAGAIN)) {
+ DEBUG(1,("timeout connecting to %s:%d\n",inet_ntoa(*addr),port));
+ close(res);
+ return -1;
+ }
+
+#ifdef EISCONN
+
+ if (ret < 0 && errno == EISCONN) {
+ errno = 0;
+ ret = 0;
+ }
+#endif
+
+ if (ret < 0) {
+ DEBUG(2,("error connecting to %s:%d (%s)\n",
+ inet_ntoa(*addr),port,strerror(errno)));
+ close(res);
+ return -1;
+ }
+
+ /* set it blocking again */
+ set_blocking(res,True);
+
+ return res;
+}
+
+/****************************************************************************
+ Open a connected UDP socket to host on port
+**************************************************************************/
+
+int open_udp_socket(const char *host, int port)
+{
+ int type = SOCK_DGRAM;
+ struct sockaddr_in sock_out;
+ int res;
+ struct in_addr *addr;
+
+ addr = interpret_addr2(host);
+
+ res = socket(PF_INET, type, 0);
+ if (res == -1) {
+ return -1;
+ }
+
+ memset((char *)&sock_out,'\0',sizeof(sock_out));
+ putip((char *)&sock_out.sin_addr,(char *)addr);
+ sock_out.sin_port = htons(port);
+ sock_out.sin_family = PF_INET;
+
+ if (connect(res,(struct sockaddr *)&sock_out,sizeof(sock_out))) {
+ close(res);
+ return -1;
+ }
+
+ return res;
+}
+
+
+/* the following 3 client_*() functions are nasty ways of allowing
+ some generic functions to get info that really should be hidden in
+ particular modules */
+static int client_fd = -1;
+
+void client_setfd(int fd)
+{
+ client_fd = fd;
+}
+
+char *client_name(void)
+{
+ return get_peer_name(client_fd,False);
+}
+
+char *client_addr(void)
+{
+ return get_peer_addr(client_fd);
+}
+
+char *client_socket_addr(void)
+{
+ return get_socket_addr(client_fd);
+}
+
+struct in_addr *client_inaddr(struct sockaddr *sa)
+{
+ struct sockaddr_in *sockin = (struct sockaddr_in *) (sa);
+ int length = sizeof(*sa);
+
+ if (getpeername(client_fd, sa, &length) < 0) {
+ DEBUG(0,("getpeername failed. Error was %s\n", strerror(errno) ));
+ return NULL;
+ }
+
+ return &sockin->sin_addr;
+}
+
+/*******************************************************************
+ Matchname - determine if host name matches IP address. Used to
+ confirm a hostname lookup to prevent spoof attacks.
+******************************************************************/
+
+static BOOL matchname(char *remotehost,struct in_addr addr)
+{
+ struct hostent *hp;
+ int i;
+
+ if ((hp = sys_gethostbyname(remotehost)) == 0) {
+ DEBUG(0,("sys_gethostbyname(%s): lookup failure.\n", remotehost));
+ return False;
+ }
+
+ /*
+ * Make sure that gethostbyname() returns the "correct" host name.
+ * Unfortunately, gethostbyname("localhost") sometimes yields
+ * "localhost.domain". Since the latter host name comes from the
+ * local DNS, we just have to trust it (all bets are off if the local
+ * DNS is perverted). We always check the address list, though.
+ */
+
+ if (!strequal(remotehost, hp->h_name)
+ && !strequal(remotehost, "localhost")) {
+ DEBUG(0,("host name/name mismatch: %s != %s\n",
+ remotehost, hp->h_name));
+ return False;
+ }
+
+ /* Look up the host address in the address list we just got. */
+ for (i = 0; hp->h_addr_list[i]; i++) {
+ if (memcmp(hp->h_addr_list[i], (char *) & addr, sizeof(addr)) == 0)
+ return True;
+ }
+
+ /*
+ * The host name does not map to the original host address. Perhaps
+ * someone has compromised a name server. More likely someone botched
+ * it, but that could be dangerous, too.
+ */
+
+ DEBUG(0,("host name/address mismatch: %s != %s\n",
+ inet_ntoa(addr), hp->h_name));
+ return False;
+}
+
+/*******************************************************************
+ Return the DNS name of the remote end of a socket.
+******************************************************************/
+
+char *get_peer_name(int fd, BOOL force_lookup)
+{
+ static pstring name_buf;
+ pstring tmp_name;
+ static fstring addr_buf;
+ struct hostent *hp;
+ struct in_addr addr;
+ char *p;
+
+ /* reverse lookups can be *very* expensive, and in many
+ situations won't work because many networks don't link dhcp
+ with dns. To avoid the delay we avoid the lookup if
+ possible */
+ if (!lp_hostname_lookups() && (force_lookup == False)) {
+ return get_peer_addr(fd);
+ }
+
+ p = get_peer_addr(fd);
+
+ /* it might be the same as the last one - save some DNS work */
+ if (strcmp(p, addr_buf) == 0)
+ return name_buf;
+
+ pstrcpy(name_buf,"UNKNOWN");
+ if (fd == -1)
+ return name_buf;
+
+ fstrcpy(addr_buf, p);
+
+ addr = *interpret_addr2(p);
+
+ /* Look up the remote host name. */
+ if ((hp = gethostbyaddr((char *)&addr.s_addr, sizeof(addr.s_addr), AF_INET)) == 0) {
+ DEBUG(1,("Gethostbyaddr failed for %s\n",p));
+ pstrcpy(name_buf, p);
+ } else {
+ pstrcpy(name_buf,(char *)hp->h_name);
+ if (!matchname(name_buf, addr)) {
+ DEBUG(0,("Matchname failed on %s %s\n",name_buf,p));
+ pstrcpy(name_buf,"UNKNOWN");
+ }
+ }
+
+ /* can't pass the same source and dest strings in when you
+ use --enable-developer or the clobber_region() call will
+ get you */
+
+ pstrcpy( tmp_name, name_buf );
+ alpha_strcpy(name_buf, tmp_name, "_-.", sizeof(name_buf));
+ if (strstr(name_buf,"..")) {
+ pstrcpy(name_buf, "UNKNOWN");
+ }
+
+ return name_buf;
+}
+
+/*******************************************************************
+ Return the IP addr of the remote end of a socket as a string.
+ ******************************************************************/
+
+char *get_peer_addr(int fd)
+{
+ struct sockaddr sa;
+ struct sockaddr_in *sockin = (struct sockaddr_in *) (&sa);
+ int length = sizeof(sa);
+ static fstring addr_buf;
+
+ fstrcpy(addr_buf,"0.0.0.0");
+
+ if (fd == -1) {
+ return addr_buf;
+ }
+
+ if (getpeername(fd, &sa, &length) < 0) {
+ DEBUG(0,("getpeername failed. Error was %s\n", strerror(errno) ));
+ return addr_buf;
+ }
+
+ fstrcpy(addr_buf,(char *)inet_ntoa(sockin->sin_addr));
+
+ return addr_buf;
+}
+
+/*******************************************************************
+ Create protected unix domain socket.
+
+ Some unixes cannot set permissions on a ux-dom-sock, so we
+ have to make sure that the directory contains the protection
+ permissions instead.
+ ******************************************************************/
+
+int create_pipe_sock(const char *socket_dir,
+ const char *socket_name,
+ mode_t dir_perms)
+{
+#ifdef HAVE_UNIXSOCKET
+ struct sockaddr_un sunaddr;
+ struct stat st;
+ int sock;
+ mode_t old_umask;
+ pstring path;
+
+ old_umask = umask(0);
+
+ /* Create the socket directory or reuse the existing one */
+
+ if (lstat(socket_dir, &st) == -1) {
+ if (errno == ENOENT) {
+ /* Create directory */
+ if (mkdir(socket_dir, dir_perms) == -1) {
+ DEBUG(0, ("error creating socket directory "
+ "%s: %s\n", socket_dir,
+ strerror(errno)));
+ goto out_umask;
+ }
+ } else {
+ DEBUG(0, ("lstat failed on socket directory %s: %s\n",
+ socket_dir, strerror(errno)));
+ goto out_umask;
+ }
+ } else {
+ /* Check ownership and permission on existing directory */
+ if (!S_ISDIR(st.st_mode)) {
+ DEBUG(0, ("socket directory %s isn't a directory\n",
+ socket_dir));
+ goto out_umask;
+ }
+ if ((st.st_uid != sec_initial_uid()) ||
+ ((st.st_mode & 0777) != dir_perms)) {
+ DEBUG(0, ("invalid permissions on socket directory "
+ "%s\n", socket_dir));
+ goto out_umask;
+ }
+ }
+
+ /* Create the socket file */
+
+ sock = socket(AF_UNIX, SOCK_STREAM, 0);
+
+ if (sock == -1) {
+ perror("socket");
+ goto out_umask;
+ }
+
+ pstr_sprintf(path, "%s/%s", socket_dir, socket_name);
+
+ unlink(path);
+ memset(&sunaddr, 0, sizeof(sunaddr));
+ sunaddr.sun_family = AF_UNIX;
+ safe_strcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path)-1);
+
+ if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1) {
+ DEBUG(0, ("bind failed on pipe socket %s: %s\n", path,
+ strerror(errno)));
+ goto out_close;
+ }
+
+ if (listen(sock, 5) == -1) {
+ DEBUG(0, ("listen failed on pipe socket %s: %s\n", path,
+ strerror(errno)));
+ goto out_close;
+ }
+
+ umask(old_umask);
+ return sock;
+
+out_close:
+ close(sock);
+
+out_umask:
+ umask(old_umask);
+ return -1;
+
+#else
+ DEBUG(0, ("create_pipe_sock: No Unix sockets on this system\n"));
+ return -1;
+#endif /* HAVE_UNIXSOCKET */
+}
diff --git a/source/lib/util_str.c b/source/lib/util_str.c
new file mode 100644
index 00000000000..be1e2ffeb1b
--- /dev/null
+++ b/source/lib/util_str.c
@@ -0,0 +1,2047 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+
+ Copyright (C) Andrew Tridgell 1992-2001
+ Copyright (C) Simo Sorce 2001-2002
+ Copyright (C) Martin Pool 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/**
+ * @file
+ * @brief String utilities.
+ **/
+
+/**
+ * Get the next token from a string, return False if none found.
+ * Handles double-quotes.
+ *
+ * Based on a routine by GJC@VILLAGE.COM.
+ * Extensively modified by Andrew.Tridgell@anu.edu.au
+ **/
+BOOL next_token(const char **ptr,char *buff, const char *sep, size_t bufsize)
+{
+ char *s;
+ char *pbuf;
+ BOOL quoted;
+ size_t len=1;
+
+ if (!ptr)
+ return(False);
+
+ s = (char *)*ptr;
+
+ /* default to simple separators */
+ if (!sep)
+ sep = " \t\n\r";
+
+ /* find the first non sep char */
+ while (*s && strchr_m(sep,*s))
+ s++;
+
+ /* nothing left? */
+ if (! *s)
+ return(False);
+
+ /* copy over the token */
+ pbuf = buff;
+ for (quoted = False; len < bufsize && *s && (quoted || !strchr_m(sep,*s)); s++) {
+ if (*s == '\"' || *s == '\'') {
+ quoted = !quoted;
+ } else {
+ len++;
+ *pbuf++ = *s;
+ }
+ }
+
+ *ptr = (*s) ? s+1 : s;
+ *pbuf = 0;
+
+ return(True);
+}
+
+/**
+This is like next_token but is not re-entrant and "remembers" the first
+parameter so you can pass NULL. This is useful for user interface code
+but beware the fact that it is not re-entrant!
+**/
+
+static const char *last_ptr=NULL;
+
+BOOL next_token_nr(const char **ptr,char *buff, const char *sep, size_t bufsize)
+{
+ BOOL ret;
+ if (!ptr)
+ ptr = &last_ptr;
+
+ ret = next_token(ptr, buff, sep, bufsize);
+ last_ptr = *ptr;
+ return ret;
+}
+
+static uint16 tmpbuf[sizeof(pstring)];
+
+void set_first_token(char *ptr)
+{
+ last_ptr = ptr;
+}
+
+/**
+ Convert list of tokens to array; dependent on above routine.
+ Uses last_ptr from above - bit of a hack.
+**/
+
+char **toktocliplist(int *ctok, const char *sep)
+{
+ char *s=(char *)last_ptr;
+ int ictok=0;
+ char **ret, **iret;
+
+ if (!sep)
+ sep = " \t\n\r";
+
+ while(*s && strchr_m(sep,*s))
+ s++;
+
+ /* nothing left? */
+ if (!*s)
+ return(NULL);
+
+ do {
+ ictok++;
+ while(*s && (!strchr_m(sep,*s)))
+ s++;
+ while(*s && strchr_m(sep,*s))
+ *s++=0;
+ } while(*s);
+
+ *ctok=ictok;
+ s=(char *)last_ptr;
+
+ if (!(ret=iret=malloc(ictok*sizeof(char *))))
+ return NULL;
+
+ while(ictok--) {
+ *iret++=s;
+ while(*s++)
+ ;
+ while(!*s)
+ s++;
+ }
+
+ return ret;
+}
+
+/**
+ * Case insensitive string compararison.
+ *
+ * iconv does not directly give us a way to compare strings in
+ * arbitrary unix character sets -- all we can is convert and then
+ * compare. This is expensive.
+ *
+ * As an optimization, we do a first pass that considers only the
+ * prefix of the strings that is entirely 7-bit. Within this, we
+ * check whether they have the same value.
+ *
+ * Hopefully this will often give the answer without needing to copy.
+ * In particular it should speed comparisons to literal ascii strings
+ * or comparisons of strings that are "obviously" different.
+ *
+ * If we find a non-ascii character we fall back to converting via
+ * iconv.
+ *
+ * This should never be slower than convering the whole thing, and
+ * often faster.
+ *
+ * A different optimization would be to compare for bitwise equality
+ * in the binary encoding. (It would be possible thought hairy to do
+ * both simultaneously.) But in that case if they turn out to be
+ * different, we'd need to restart the whole thing.
+ *
+ * Even better is to implement strcasecmp for each encoding and use a
+ * function pointer.
+ **/
+int StrCaseCmp(const char *s, const char *t)
+{
+
+ const char * ps, * pt;
+ size_t size;
+ smb_ucs2_t *buffer_s, *buffer_t;
+ int ret;
+
+ for (ps = s, pt = t; ; ps++, pt++) {
+ char us, ut;
+
+ if (!*ps && !*pt)
+ return 0; /* both ended */
+ else if (!*ps)
+ return -1; /* s is a prefix */
+ else if (!*pt)
+ return +1; /* t is a prefix */
+ else if ((*ps & 0x80) || (*pt & 0x80))
+ /* not ascii anymore, do it the hard way from here on in */
+ break;
+
+ us = toupper(*ps);
+ ut = toupper(*pt);
+ if (us == ut)
+ continue;
+ else if (us < ut)
+ return -1;
+ else if (us > ut)
+ return +1;
+ }
+
+ size = push_ucs2_allocate(&buffer_s, s);
+ if (size == (size_t)-1) {
+ return strcmp(s, t);
+ /* Not quite the right answer, but finding the right one
+ under this failure case is expensive, and it's pretty close */
+ }
+
+ size = push_ucs2_allocate(&buffer_t, t);
+ if (size == (size_t)-1) {
+ SAFE_FREE(buffer_s);
+ return strcmp(s, t);
+ /* Not quite the right answer, but finding the right one
+ under this failure case is expensive, and it's pretty close */
+ }
+
+ ret = strcasecmp_w(buffer_s, buffer_t);
+ SAFE_FREE(buffer_s);
+ SAFE_FREE(buffer_t);
+ return ret;
+}
+
+
+/**
+ Case insensitive string compararison, length limited.
+**/
+int StrnCaseCmp(const char *s, const char *t, size_t n)
+{
+ pstring buf1, buf2;
+ unix_strupper(s, strlen(s)+1, buf1, sizeof(buf1));
+ unix_strupper(t, strlen(t)+1, buf2, sizeof(buf2));
+ return strncmp(buf1,buf2,n);
+}
+
+/**
+ * Compare 2 strings.
+ *
+ * @note The comparison is case-insensitive.
+ **/
+BOOL strequal(const char *s1, const char *s2)
+{
+ if (s1 == s2)
+ return(True);
+ if (!s1 || !s2)
+ return(False);
+
+ return(StrCaseCmp(s1,s2)==0);
+}
+
+/**
+ * Compare 2 strings up to and including the nth char.
+ *
+ * @note The comparison is case-insensitive.
+ **/
+BOOL strnequal(const char *s1,const char *s2,size_t n)
+{
+ if (s1 == s2)
+ return(True);
+ if (!s1 || !s2 || !n)
+ return(False);
+
+ return(StrnCaseCmp(s1,s2,n)==0);
+}
+
+/**
+ Compare 2 strings (case sensitive).
+**/
+
+BOOL strcsequal(const char *s1,const char *s2)
+{
+ if (s1 == s2)
+ return(True);
+ if (!s1 || !s2)
+ return(False);
+
+ return(strcmp(s1,s2)==0);
+}
+
+/**
+Do a case-insensitive, whitespace-ignoring string compare.
+**/
+
+int strwicmp(const char *psz1, const char *psz2)
+{
+ /* if BOTH strings are NULL, return TRUE, if ONE is NULL return */
+ /* appropriate value. */
+ if (psz1 == psz2)
+ return (0);
+ else if (psz1 == NULL)
+ return (-1);
+ else if (psz2 == NULL)
+ return (1);
+
+ /* sync the strings on first non-whitespace */
+ while (1) {
+ while (isspace((int)*psz1))
+ psz1++;
+ while (isspace((int)*psz2))
+ psz2++;
+ if (toupper(*psz1) != toupper(*psz2) || *psz1 == '\0'
+ || *psz2 == '\0')
+ break;
+ psz1++;
+ psz2++;
+ }
+ return (*psz1 - *psz2);
+}
+
+
+/**
+ Convert a string to upper case, but don't modify it.
+**/
+
+char *strupper_static(const char *s)
+{
+ static pstring str;
+
+ pstrcpy(str, s);
+ strupper_m(str);
+
+ return str;
+}
+
+/**
+ Convert a string to "normal" form.
+**/
+
+void strnorm(char *s)
+{
+ extern int case_default;
+ if (case_default == CASE_UPPER)
+ strupper_m(s);
+ else
+ strlower_m(s);
+}
+
+/**
+ Check if a string is in "normal" case.
+**/
+
+BOOL strisnormal(const char *s)
+{
+ extern int case_default;
+ if (case_default == CASE_UPPER)
+ return(!strhaslower(s));
+
+ return(!strhasupper(s));
+}
+
+
+/**
+ String replace.
+ NOTE: oldc and newc must be 7 bit characters
+**/
+
+void string_replace(pstring s,char oldc,char newc)
+{
+ unsigned char *p;
+
+ /* this is quite a common operation, so we want it to be
+ fast. We optimise for the ascii case, knowing that all our
+ supported multi-byte character sets are ascii-compatible
+ (ie. they match for the first 128 chars) */
+
+ for (p = (unsigned char *)s; *p; p++) {
+ if (*p & 0x80) /* mb string - slow path. */
+ break;
+ if (*p == oldc)
+ *p = newc;
+ }
+
+ if (!*p)
+ return;
+
+ /* Slow (mb) path. */
+#ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS
+ /* With compose characters we must restart from the beginning. JRA. */
+ p = s;
+#endif
+ push_ucs2(NULL, tmpbuf, p, sizeof(tmpbuf), STR_TERMINATE);
+ string_replace_w(tmpbuf, UCS2_CHAR(oldc), UCS2_CHAR(newc));
+ pull_ucs2(NULL, p, tmpbuf, -1, sizeof(tmpbuf), STR_TERMINATE);
+}
+
+/**
+ Skip past some strings in a buffer.
+**/
+
+char *skip_string(char *buf,size_t n)
+{
+ while (n--)
+ buf += strlen(buf) + 1;
+ return(buf);
+}
+
+/**
+ Count the number of characters in a string. Normally this will
+ be the same as the number of bytes in a string for single byte strings,
+ but will be different for multibyte.
+**/
+
+size_t str_charnum(const char *s)
+{
+ uint16 tmpbuf2[sizeof(pstring)];
+ push_ucs2(NULL, tmpbuf2,s, sizeof(tmpbuf2), STR_TERMINATE);
+ return strlen_w(tmpbuf2);
+}
+
+/**
+ Count the number of characters in a string. Normally this will
+ be the same as the number of bytes in a string for single byte strings,
+ but will be different for multibyte.
+**/
+
+size_t str_ascii_charnum(const char *s)
+{
+ pstring tmpbuf2;
+ push_ascii(tmpbuf2, s, sizeof(tmpbuf2), STR_TERMINATE);
+ return strlen(tmpbuf2);
+}
+
+BOOL trim_char(char *s,char cfront,char cback)
+{
+ BOOL ret = False;
+ char *ep;
+ char *fp = s;
+
+ /* Ignore null or empty strings. */
+ if (!s || (s[0] == '\0'))
+ return False;
+
+ if (cfront) {
+ while (*fp && *fp == cfront)
+ fp++;
+ if (!*fp) {
+ /* We ate the string. */
+ s[0] = '\0';
+ return True;
+ }
+ if (fp != s)
+ ret = True;
+ }
+
+ ep = fp + strlen(fp) - 1;
+ if (cback) {
+ /* Attempt ascii only. Bail for mb strings. */
+ while ((ep >= fp) && (*ep == cback)) {
+ ret = True;
+ if ((ep > fp) && (((unsigned char)ep[-1]) & 0x80)) {
+ /* Could be mb... bail back to tim_string. */
+ char fs[2], bs[2];
+ if (cfront) {
+ fs[0] = cfront;
+ fs[1] = '\0';
+ }
+ bs[0] = cback;
+ bs[1] = '\0';
+ return trim_string(s, cfront ? fs : NULL, bs);
+ } else {
+ ep--;
+ }
+ }
+ if (ep < fp) {
+ /* We ate the string. */
+ s[0] = '\0';
+ return True;
+ }
+ }
+
+ ep[1] = '\0';
+ memmove(s, fp, ep-fp+2);
+ return ret;
+}
+
+/**
+ Trim the specified elements off the front and back of a string.
+**/
+
+BOOL trim_string(char *s,const char *front,const char *back)
+{
+ BOOL ret = False;
+ size_t front_len;
+ size_t back_len;
+ size_t len;
+
+ /* Ignore null or empty strings. */
+ if (!s || (s[0] == '\0'))
+ return False;
+
+ front_len = front? strlen(front) : 0;
+ back_len = back? strlen(back) : 0;
+
+ len = strlen(s);
+
+ if (front_len) {
+ while (len && strncmp(s, front, front_len)==0) {
+ /* Must use memmove here as src & dest can
+ * easily overlap. Found by valgrind. JRA. */
+ memmove(s, s+front_len, (len-front_len)+1);
+ len -= front_len;
+ ret=True;
+ }
+ }
+
+ if (back_len) {
+ while ((len >= back_len) && strncmp(s+len-back_len,back,back_len)==0) {
+ s[len-back_len]='\0';
+ len -= back_len;
+ ret=True;
+ }
+ }
+ return ret;
+}
+
+/**
+ Does a string have any uppercase chars in it?
+**/
+
+BOOL strhasupper(const char *s)
+{
+ smb_ucs2_t *ptr;
+ push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE);
+ for(ptr=tmpbuf;*ptr;ptr++)
+ if(isupper_w(*ptr))
+ return True;
+ return(False);
+}
+
+/**
+ Does a string have any lowercase chars in it?
+**/
+
+BOOL strhaslower(const char *s)
+{
+ smb_ucs2_t *ptr;
+ push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE);
+ for(ptr=tmpbuf;*ptr;ptr++)
+ if(islower_w(*ptr))
+ return True;
+ return(False);
+}
+
+/**
+ Find the number of 'c' chars in a string
+**/
+
+size_t count_chars(const char *s,char c)
+{
+ smb_ucs2_t *ptr;
+ int count;
+ smb_ucs2_t *alloc_tmpbuf = NULL;
+
+ if (push_ucs2_allocate(&alloc_tmpbuf, s) == (size_t)-1) {
+ return 0;
+ }
+
+ for(count=0,ptr=alloc_tmpbuf;*ptr;ptr++)
+ if(*ptr==UCS2_CHAR(c))
+ count++;
+
+ SAFE_FREE(alloc_tmpbuf);
+ return(count);
+}
+
+/**
+ Safe string copy into a known length string. maxlength does not
+ include the terminating zero.
+**/
+
+char *safe_strcpy_fn(const char *fn, int line, char *dest,const char *src, size_t maxlength)
+{
+ size_t len;
+
+ if (!dest) {
+ DEBUG(0,("ERROR: NULL dest in safe_strcpy, called from [%s][%d]\n", fn, line));
+ return NULL;
+ }
+
+#ifdef DEVELOPER
+ clobber_region(fn,line,dest, maxlength+1);
+#endif
+
+ if (!src) {
+ *dest = 0;
+ return dest;
+ }
+
+ len = strnlen(src, maxlength+1);
+
+ if (len > maxlength) {
+ DEBUG(0,("ERROR: string overflow by %lu (%lu - %lu) in safe_strcpy [%.50s]\n",
+ (unsigned long)(len-maxlength), (unsigned long)len,
+ (unsigned long)maxlength, src));
+ len = maxlength;
+ }
+
+ memmove(dest, src, len);
+ dest[len] = 0;
+ return dest;
+}
+
+/**
+ Safe string cat into a string. maxlength does not
+ include the terminating zero.
+**/
+char *safe_strcat_fn(const char *fn, int line, char *dest, const char *src, size_t maxlength)
+{
+ size_t src_len, dest_len;
+
+ if (!dest) {
+ DEBUG(0,("ERROR: NULL dest in safe_strcat, called from [%s][%d]\n", fn, line));
+ return NULL;
+ }
+
+ if (!src)
+ return dest;
+
+ src_len = strnlen(src, maxlength + 1);
+ dest_len = strnlen(dest, maxlength + 1);
+
+#ifdef DEVELOPER
+ clobber_region(fn, line, dest + dest_len, maxlength + 1 - dest_len);
+#endif
+
+ if (src_len + dest_len > maxlength) {
+ DEBUG(0,("ERROR: string overflow by %d in safe_strcat [%.50s]\n",
+ (int)(src_len + dest_len - maxlength), src));
+ if (maxlength > dest_len) {
+ memcpy(&dest[dest_len], src, maxlength - dest_len);
+ }
+ dest[maxlength] = 0;
+ return NULL;
+ }
+
+ memcpy(&dest[dest_len], src, src_len);
+ dest[dest_len + src_len] = 0;
+ return dest;
+}
+
+/**
+ Paranoid strcpy into a buffer of given length (includes terminating
+ zero. Strips out all but 'a-Z0-9' and the character in other_safe_chars
+ and replaces with '_'. Deliberately does *NOT* check for multibyte
+ characters. Don't change it !
+**/
+char *alpha_strcpy_fn(const char *fn, int line, char *dest, const char *src, const char *other_safe_chars, size_t maxlength)
+{
+ size_t len, i;
+
+#ifdef DEVELOPER
+ clobber_region(fn, line, dest, maxlength);
+#endif
+
+ if (!dest) {
+ DEBUG(0,("ERROR: NULL dest in alpha_strcpy, called from [%s][%d]\n", fn, line));
+ return NULL;
+ }
+
+ if (!src) {
+ *dest = 0;
+ return dest;
+ }
+
+ len = strlen(src);
+ if (len >= maxlength)
+ len = maxlength - 1;
+
+ if (!other_safe_chars)
+ other_safe_chars = "";
+
+ for(i = 0; i < len; i++) {
+ int val = (src[i] & 0xff);
+ if (isupper(val) || islower(val) || isdigit(val) || strchr_m(other_safe_chars, val))
+ dest[i] = src[i];
+ else
+ dest[i] = '_';
+ }
+
+ dest[i] = '\0';
+
+ return dest;
+}
+
+/**
+ Like strncpy but always null terminates. Make sure there is room!
+ The variable n should always be one less than the available size.
+**/
+char *StrnCpy_fn(const char *fn, int line,char *dest,const char *src,size_t n)
+{
+ char *d = dest;
+
+#ifdef DEVELOPER
+ clobber_region(fn, line, dest, n+1);
+#endif
+
+ if (!dest) {
+ DEBUG(0,("ERROR: NULL dest in StrnCpy, called from [%s][%d]\n", fn, line));
+ return(NULL);
+ }
+
+ if (!src) {
+ *dest = 0;
+ return(dest);
+ }
+
+ while (n-- && (*d = *src)) {
+ d++;
+ src++;
+ }
+
+ *d = 0;
+ return(dest);
+}
+
+#if 0
+/**
+ Like strncpy but copies up to the character marker. always null terminates.
+ returns a pointer to the character marker in the source string (src).
+**/
+
+static char *strncpyn(char *dest, const char *src, size_t n, char c)
+{
+ char *p;
+ size_t str_len;
+
+#ifdef DEVELOPER
+ clobber_region(dest, n+1);
+#endif
+ p = strchr_m(src, c);
+ if (p == NULL) {
+ DEBUG(5, ("strncpyn: separator character (%c) not found\n", c));
+ return NULL;
+ }
+
+ str_len = PTR_DIFF(p, src);
+ strncpy(dest, src, MIN(n, str_len));
+ dest[str_len] = '\0';
+
+ return p;
+}
+#endif
+
+/**
+ Routine to get hex characters and turn them into a 16 byte array.
+ the array can be variable length, and any non-hex-numeric
+ characters are skipped. "0xnn" or "0Xnn" is specially catered
+ for.
+
+ valid examples: "0A5D15"; "0x15, 0x49, 0xa2"; "59\ta9\te3\n"
+
+**/
+
+size_t strhex_to_str(char *p, size_t len, const char *strhex)
+{
+ size_t i;
+ size_t num_chars = 0;
+ unsigned char lonybble, hinybble;
+ const char *hexchars = "0123456789ABCDEF";
+ char *p1 = NULL, *p2 = NULL;
+
+ for (i = 0; i < len && strhex[i] != 0; i++) {
+ if (strnequal(hexchars, "0x", 2)) {
+ i++; /* skip two chars */
+ continue;
+ }
+
+ if (!(p1 = strchr_m(hexchars, toupper(strhex[i]))))
+ break;
+
+ i++; /* next hex digit */
+
+ if (!(p2 = strchr_m(hexchars, toupper(strhex[i]))))
+ break;
+
+ /* get the two nybbles */
+ hinybble = PTR_DIFF(p1, hexchars);
+ lonybble = PTR_DIFF(p2, hexchars);
+
+ p[num_chars] = (hinybble << 4) | lonybble;
+ num_chars++;
+
+ p1 = NULL;
+ p2 = NULL;
+ }
+ return num_chars;
+}
+
+/**
+ * Routine to print a buffer as HEX digits, into an allocated string.
+ */
+
+void hex_encode(const unsigned char *buff_in, size_t len, char **out_hex_buffer)
+{
+ int i;
+ char *hex_buffer;
+
+ *out_hex_buffer = smb_xmalloc((len*2)+1);
+ hex_buffer = *out_hex_buffer;
+
+ for (i = 0; i < len; i++)
+ slprintf(&hex_buffer[i*2], 3, "%02X", buff_in[i]);
+}
+
+/**
+ Check if a string is part of a list.
+**/
+
+BOOL in_list(char *s,char *list,BOOL casesensitive)
+{
+ pstring tok;
+ const char *p=list;
+
+ if (!list)
+ return(False);
+
+ while (next_token(&p,tok,LIST_SEP,sizeof(tok))) {
+ if (casesensitive) {
+ if (strcmp(tok,s) == 0)
+ return(True);
+ } else {
+ if (StrCaseCmp(tok,s) == 0)
+ return(True);
+ }
+ }
+ return(False);
+}
+
+/* this is used to prevent lots of mallocs of size 1 */
+static char *null_string = NULL;
+
+/**
+ Set a string value, allocing the space for the string
+**/
+
+static BOOL string_init(char **dest,const char *src)
+{
+ size_t l;
+ if (!src)
+ src = "";
+
+ l = strlen(src);
+
+ if (l == 0) {
+ if (!null_string) {
+ if((null_string = (char *)malloc(1)) == NULL) {
+ DEBUG(0,("string_init: malloc fail for null_string.\n"));
+ return False;
+ }
+ *null_string = 0;
+ }
+ *dest = null_string;
+ } else {
+ (*dest) = strdup(src);
+ if ((*dest) == NULL) {
+ DEBUG(0,("Out of memory in string_init\n"));
+ return False;
+ }
+ }
+ return(True);
+}
+
+/**
+ Free a string value.
+**/
+
+void string_free(char **s)
+{
+ if (!s || !(*s))
+ return;
+ if (*s == null_string)
+ *s = NULL;
+ SAFE_FREE(*s);
+}
+
+/**
+ Set a string value, deallocating any existing space, and allocing the space
+ for the string
+**/
+
+BOOL string_set(char **dest,const char *src)
+{
+ string_free(dest);
+ return(string_init(dest,src));
+}
+
+/**
+ Substitute a string for a pattern in another string. Make sure there is
+ enough room!
+
+ This routine looks for pattern in s and replaces it with
+ insert. It may do multiple replacements.
+
+ Any of " ; ' $ or ` in the insert string are replaced with _
+ if len==0 then the string cannot be extended. This is different from the old
+ use of len==0 which was for no length checks to be done.
+**/
+
+void string_sub(char *s,const char *pattern, const char *insert, size_t len)
+{
+ char *p;
+ ssize_t ls,lp,li, i;
+
+ if (!insert || !pattern || !*pattern || !s)
+ return;
+
+ ls = (ssize_t)strlen(s);
+ lp = (ssize_t)strlen(pattern);
+ li = (ssize_t)strlen(insert);
+
+ if (len == 0)
+ len = ls + 1; /* len is number of *bytes* */
+
+ while (lp <= ls && (p = strstr_m(s,pattern))) {
+ if (ls + (li-lp) >= len) {
+ DEBUG(0,("ERROR: string overflow by %d in string_sub(%.50s, %d)\n",
+ (int)(ls + (li-lp) - len),
+ pattern, (int)len));
+ break;
+ }
+ if (li != lp) {
+ memmove(p+li,p+lp,strlen(p+lp)+1);
+ }
+ for (i=0;i<li;i++) {
+ switch (insert[i]) {
+ case '`':
+ case '"':
+ case '\'':
+ case ';':
+ case '$':
+ case '%':
+ case '\r':
+ case '\n':
+ p[i] = '_';
+ break;
+ default:
+ p[i] = insert[i];
+ }
+ }
+ s = p + li;
+ ls += (li-lp);
+ }
+}
+
+void fstring_sub(char *s,const char *pattern,const char *insert)
+{
+ string_sub(s, pattern, insert, sizeof(fstring));
+}
+
+void pstring_sub(char *s,const char *pattern,const char *insert)
+{
+ string_sub(s, pattern, insert, sizeof(pstring));
+}
+
+/**
+ Similar to string_sub, but it will accept only allocated strings
+ and may realloc them so pay attention at what you pass on no
+ pointers inside strings, no pstrings or const may be passed
+ as string.
+**/
+
+char *realloc_string_sub(char *string, const char *pattern, const char *insert)
+{
+ char *p, *in;
+ char *s;
+ ssize_t ls,lp,li,ld, i;
+
+ if (!insert || !pattern || !*pattern || !string || !*string)
+ return NULL;
+
+ s = string;
+
+ in = strdup(insert);
+ if (!in) {
+ DEBUG(0, ("realloc_string_sub: out of memory!\n"));
+ return NULL;
+ }
+ ls = (ssize_t)strlen(s);
+ lp = (ssize_t)strlen(pattern);
+ li = (ssize_t)strlen(insert);
+ ld = li - lp;
+ for (i=0;i<li;i++) {
+ switch (in[i]) {
+ case '`':
+ case '"':
+ case '\'':
+ case ';':
+ case '$':
+ case '%':
+ case '\r':
+ case '\n':
+ in[i] = '_';
+ default:
+ /* ok */
+ break;
+ }
+ }
+
+ while ((p = strstr_m(s,pattern))) {
+ if (ld > 0) {
+ int offset = PTR_DIFF(s,string);
+ char *t = Realloc(string, ls + ld + 1);
+ if (!t) {
+ DEBUG(0, ("realloc_string_sub: out of memory!\n"));
+ SAFE_FREE(in);
+ return NULL;
+ }
+ string = t;
+ p = t + offset + (p - s);
+ }
+ if (li != lp) {
+ memmove(p+li,p+lp,strlen(p+lp)+1);
+ }
+ memcpy(p, in, li);
+ s = p + li;
+ ls += ld;
+ }
+ SAFE_FREE(in);
+ return string;
+}
+
+/**
+ Similar to string_sub() but allows for any character to be substituted.
+ Use with caution!
+ if len==0 then the string cannot be extended. This is different from the old
+ use of len==0 which was for no length checks to be done.
+**/
+
+void all_string_sub(char *s,const char *pattern,const char *insert, size_t len)
+{
+ char *p;
+ ssize_t ls,lp,li;
+
+ if (!insert || !pattern || !s)
+ return;
+
+ ls = (ssize_t)strlen(s);
+ lp = (ssize_t)strlen(pattern);
+ li = (ssize_t)strlen(insert);
+
+ if (!*pattern)
+ return;
+
+ if (len == 0)
+ len = ls + 1; /* len is number of *bytes* */
+
+ while (lp <= ls && (p = strstr_m(s,pattern))) {
+ if (ls + (li-lp) >= len) {
+ DEBUG(0,("ERROR: string overflow by %d in all_string_sub(%.50s, %d)\n",
+ (int)(ls + (li-lp) - len),
+ pattern, (int)len));
+ break;
+ }
+ if (li != lp) {
+ memmove(p+li,p+lp,strlen(p+lp)+1);
+ }
+ memcpy(p, insert, li);
+ s = p + li;
+ ls += (li-lp);
+ }
+}
+
+/**
+ Similar to all_string_sub but for unicode strings.
+ Return a new allocated unicode string.
+ similar to string_sub() but allows for any character to be substituted.
+ Use with caution!
+**/
+
+static smb_ucs2_t *all_string_sub_w(const smb_ucs2_t *s, const smb_ucs2_t *pattern,
+ const smb_ucs2_t *insert)
+{
+ smb_ucs2_t *r, *rp;
+ const smb_ucs2_t *sp;
+ size_t lr, lp, li, lt;
+
+ if (!insert || !pattern || !*pattern || !s)
+ return NULL;
+
+ lt = (size_t)strlen_w(s);
+ lp = (size_t)strlen_w(pattern);
+ li = (size_t)strlen_w(insert);
+
+ if (li > lp) {
+ const smb_ucs2_t *st = s;
+ int ld = li - lp;
+ while ((sp = strstr_w(st, pattern))) {
+ st = sp + lp;
+ lt += ld;
+ }
+ }
+
+ r = rp = (smb_ucs2_t *)malloc((lt + 1)*(sizeof(smb_ucs2_t)));
+ if (!r) {
+ DEBUG(0, ("all_string_sub_w: out of memory!\n"));
+ return NULL;
+ }
+
+ while ((sp = strstr_w(s, pattern))) {
+ memcpy(rp, s, (sp - s));
+ rp += ((sp - s) / sizeof(smb_ucs2_t));
+ memcpy(rp, insert, (li * sizeof(smb_ucs2_t)));
+ s = sp + lp;
+ rp += li;
+ }
+ lr = ((rp - r) / sizeof(smb_ucs2_t));
+ if (lr < lt) {
+ memcpy(rp, s, ((lt - lr) * sizeof(smb_ucs2_t)));
+ rp += (lt - lr);
+ }
+ *rp = 0;
+
+ return r;
+}
+
+smb_ucs2_t *all_string_sub_wa(smb_ucs2_t *s, const char *pattern,
+ const char *insert)
+{
+ wpstring p, i;
+
+ if (!insert || !pattern || !s)
+ return NULL;
+ push_ucs2(NULL, p, pattern, sizeof(wpstring) - 1, STR_TERMINATE);
+ push_ucs2(NULL, i, insert, sizeof(wpstring) - 1, STR_TERMINATE);
+ return all_string_sub_w(s, p, i);
+}
+
+#if 0
+/**
+ Splits out the front and back at a separator.
+**/
+
+static void split_at_last_component(char *path, char *front, char sep, char *back)
+{
+ char *p = strrchr_m(path, sep);
+
+ if (p != NULL)
+ *p = 0;
+
+ if (front != NULL)
+ pstrcpy(front, path);
+
+ if (p != NULL) {
+ if (back != NULL)
+ pstrcpy(back, p+1);
+ *p = '\\';
+ } else {
+ if (back != NULL)
+ back[0] = 0;
+ }
+}
+#endif
+
+/**
+ Write an octal as a string.
+**/
+
+const char *octal_string(int i)
+{
+ static char ret[64];
+ if (i == -1)
+ return "-1";
+ slprintf(ret, sizeof(ret)-1, "0%o", i);
+ return ret;
+}
+
+
+/**
+ Truncate a string at a specified length.
+**/
+
+char *string_truncate(char *s, unsigned int length)
+{
+ if (s && strlen(s) > length)
+ s[length] = 0;
+ return s;
+}
+
+/**
+ Strchr and strrchr_m are very hard to do on general multi-byte strings.
+ We convert via ucs2 for now.
+**/
+
+char *strchr_m(const char *src, char c)
+{
+ wpstring ws;
+ pstring s2;
+ smb_ucs2_t *p;
+ const char *s;
+
+ /* this is quite a common operation, so we want it to be
+ fast. We optimise for the ascii case, knowing that all our
+ supported multi-byte character sets are ascii-compatible
+ (ie. they match for the first 128 chars) */
+
+ for (s = src; *s && !(((unsigned char)s[0]) & 0x80); s++) {
+ if (*s == c)
+ return (char *)s;
+ }
+
+ if (!*s)
+ return NULL;
+
+#ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS
+ /* With compose characters we must restart from the beginning. JRA. */
+ s = src;
+#endif
+
+ push_ucs2(NULL, ws, s, sizeof(ws), STR_TERMINATE);
+ p = strchr_w(ws, UCS2_CHAR(c));
+ if (!p)
+ return NULL;
+ *p = 0;
+ pull_ucs2_pstring(s2, ws);
+ return (char *)(s+strlen(s2));
+}
+
+char *strrchr_m(const char *s, char c)
+{
+ /* this is quite a common operation, so we want it to be
+ fast. We optimise for the ascii case, knowing that all our
+ supported multi-byte character sets are ascii-compatible
+ (ie. they match for the first 128 chars). Also, in Samba
+ we only search for ascii characters in 'c' and that
+ in all mb character sets with a compound character
+ containing c, if 'c' is not a match at position
+ p, then p[-1] > 0x7f. JRA. */
+
+ {
+ size_t len = strlen(s);
+ const char *cp = s;
+ BOOL got_mb = False;
+
+ if (len == 0)
+ return NULL;
+ cp += (len - 1);
+ do {
+ if (c == *cp) {
+ /* Could be a match. Part of a multibyte ? */
+ if ((cp > s) && (((unsigned char)cp[-1]) & 0x80)) {
+ /* Yep - go slow :-( */
+ got_mb = True;
+ break;
+ }
+ /* No - we have a match ! */
+ return (char *)cp;
+ }
+ } while (cp-- != s);
+ if (!got_mb)
+ return NULL;
+ }
+
+ /* String contained a non-ascii char. Slow path. */
+ {
+ wpstring ws;
+ pstring s2;
+ smb_ucs2_t *p;
+
+ push_ucs2(NULL, ws, s, sizeof(ws), STR_TERMINATE);
+ p = strrchr_w(ws, UCS2_CHAR(c));
+ if (!p)
+ return NULL;
+ *p = 0;
+ pull_ucs2_pstring(s2, ws);
+ return (char *)(s+strlen(s2));
+ }
+}
+
+/***********************************************************************
+ Return the equivalent of doing strrchr 'n' times - always going
+ backwards.
+***********************************************************************/
+
+char *strnrchr_m(const char *s, char c, unsigned int n)
+{
+ wpstring ws;
+ pstring s2;
+ smb_ucs2_t *p;
+
+ push_ucs2(NULL, ws, s, sizeof(ws), STR_TERMINATE);
+ p = strnrchr_w(ws, UCS2_CHAR(c), n);
+ if (!p)
+ return NULL;
+ *p = 0;
+ pull_ucs2_pstring(s2, ws);
+ return (char *)(s+strlen(s2));
+}
+
+/***********************************************************************
+ strstr_m - We convert via ucs2 for now.
+***********************************************************************/
+
+char *strstr_m(const char *src, const char *findstr)
+{
+ smb_ucs2_t *p;
+ smb_ucs2_t *src_w, *find_w;
+ const char *s;
+ char *s2;
+ char *retp;
+
+ size_t findstr_len = 0;
+
+ /* for correctness */
+ if (!findstr[0]) {
+ return src;
+ }
+
+ /* Samba does single character findstr calls a *lot*. */
+ if (findstr[1] == '\0')
+ return strchr_m(src, *findstr);
+
+ /* We optimise for the ascii case, knowing that all our
+ supported multi-byte character sets are ascii-compatible
+ (ie. they match for the first 128 chars) */
+
+ for (s = src; *s && !(((unsigned char)s[0]) & 0x80); s++) {
+ if (*s == *findstr) {
+ if (!findstr_len)
+ findstr_len = strlen(findstr);
+
+ if (strncmp(s, findstr, findstr_len) == 0) {
+ return (char *)s;
+ }
+ }
+ }
+
+ if (!*s)
+ return NULL;
+
+#if 1 /* def BROKEN_UNICODE_COMPOSE_CHARACTERS */
+ /* 'make check' fails unless we do this */
+
+ /* With compose characters we must restart from the beginning. JRA. */
+ s = src;
+#endif
+
+ if (push_ucs2_allocate(&src_w, src) == (size_t)-1) {
+ DEBUG(0,("strstr_m: src malloc fail\n"));
+ return NULL;
+ }
+
+ if (push_ucs2_allocate(&find_w, findstr) == (size_t)-1) {
+ SAFE_FREE(src_w);
+ DEBUG(0,("strstr_m: find malloc fail\n"));
+ return NULL;
+ }
+
+ p = strstr_w(src_w, find_w);
+
+ if (!p) {
+ SAFE_FREE(src_w);
+ SAFE_FREE(find_w);
+ return NULL;
+ }
+
+ *p = 0;
+ if (pull_ucs2_allocate(&s2, src_w) == (size_t)-1) {
+ SAFE_FREE(src_w);
+ SAFE_FREE(find_w);
+ DEBUG(0,("strstr_m: dest malloc fail\n"));
+ return NULL;
+ }
+ retp = (char *)(s+strlen(s2));
+ SAFE_FREE(src_w);
+ SAFE_FREE(find_w);
+ SAFE_FREE(s2);
+ return retp;
+}
+
+/**
+ Convert a string to lower case.
+**/
+
+void strlower_m(char *s)
+{
+ size_t len;
+
+ /* this is quite a common operation, so we want it to be
+ fast. We optimise for the ascii case, knowing that all our
+ supported multi-byte character sets are ascii-compatible
+ (ie. they match for the first 128 chars) */
+
+ while (*s && !(((unsigned char)s[0]) & 0x80)) {
+ *s = tolower((unsigned char)*s);
+ s++;
+ }
+
+ if (!*s)
+ return;
+
+ /* I assume that lowercased string takes the same number of bytes
+ * as source string even in UTF-8 encoding. (VIV) */
+ len = strlen(s) + 1;
+ errno = 0;
+ unix_strlower(s,len,s,len);
+ /* Catch mb conversion errors that may not terminate. */
+ if (errno)
+ s[len-1] = '\0';
+}
+
+/**
+ Convert a string to upper case.
+**/
+
+void strupper_m(char *s)
+{
+ size_t len;
+
+ /* this is quite a common operation, so we want it to be
+ fast. We optimise for the ascii case, knowing that all our
+ supported multi-byte character sets are ascii-compatible
+ (ie. they match for the first 128 chars) */
+
+ while (*s && !(((unsigned char)s[0]) & 0x80)) {
+ *s = toupper((unsigned char)*s);
+ s++;
+ }
+
+ if (!*s)
+ return;
+
+ /* I assume that lowercased string takes the same number of bytes
+ * as source string even in multibyte encoding. (VIV) */
+ len = strlen(s) + 1;
+ errno = 0;
+ unix_strupper(s,len,s,len);
+ /* Catch mb conversion errors that may not terminate. */
+ if (errno)
+ s[len-1] = '\0';
+}
+
+/**
+ Return a RFC2254 binary string representation of a buffer.
+ Used in LDAP filters.
+ Caller must free.
+**/
+
+char *binary_string(char *buf, int len)
+{
+ char *s;
+ int i, j;
+ const char *hex = "0123456789ABCDEF";
+ s = malloc(len * 3 + 1);
+ if (!s)
+ return NULL;
+ for (j=i=0;i<len;i++) {
+ s[j] = '\\';
+ s[j+1] = hex[((unsigned char)buf[i]) >> 4];
+ s[j+2] = hex[((unsigned char)buf[i]) & 0xF];
+ j += 3;
+ }
+ s[j] = 0;
+ return s;
+}
+
+/**
+ Just a typesafety wrapper for snprintf into a pstring.
+**/
+
+ int pstr_sprintf(pstring s, const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = vsnprintf(s, PSTRING_LEN, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+
+/**
+ Just a typesafety wrapper for snprintf into a fstring.
+**/
+
+int fstr_sprintf(fstring s, const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = vsnprintf(s, FSTRING_LEN, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+
+#ifndef HAVE_STRNDUP
+/**
+ Some platforms don't have strndup.
+**/
+
+ char *strndup(const char *s, size_t n)
+{
+ char *ret;
+
+ n = strnlen(s, n);
+ ret = malloc(n+1);
+ if (!ret)
+ return NULL;
+ memcpy(ret, s, n);
+ ret[n] = 0;
+
+ return ret;
+}
+#endif
+
+#ifndef HAVE_STRNLEN
+/**
+ Some platforms don't have strnlen
+**/
+
+ size_t strnlen(const char *s, size_t n)
+{
+ int i;
+ for (i=0; s[i] && i<n; i++)
+ /* noop */ ;
+ return i;
+}
+#endif
+
+/**
+ List of Strings manipulation functions
+**/
+
+#define S_LIST_ABS 16 /* List Allocation Block Size */
+
+char **str_list_make(const char *string, const char *sep)
+{
+ char **list, **rlist;
+ const char *str;
+ char *s;
+ int num, lsize;
+ pstring tok;
+
+ if (!string || !*string)
+ return NULL;
+ s = strdup(string);
+ if (!s) {
+ DEBUG(0,("str_list_make: Unable to allocate memory"));
+ return NULL;
+ }
+ if (!sep) sep = LIST_SEP;
+
+ num = lsize = 0;
+ list = NULL;
+
+ str = s;
+ while (next_token(&str, tok, sep, sizeof(tok))) {
+ if (num == lsize) {
+ lsize += S_LIST_ABS;
+ rlist = (char **)Realloc(list, ((sizeof(char **)) * (lsize +1)));
+ if (!rlist) {
+ DEBUG(0,("str_list_make: Unable to allocate memory"));
+ str_list_free(&list);
+ SAFE_FREE(s);
+ return NULL;
+ } else
+ list = rlist;
+ memset (&list[num], 0, ((sizeof(char**)) * (S_LIST_ABS +1)));
+ }
+
+ list[num] = strdup(tok);
+ if (!list[num]) {
+ DEBUG(0,("str_list_make: Unable to allocate memory"));
+ str_list_free(&list);
+ SAFE_FREE(s);
+ return NULL;
+ }
+
+ num++;
+ }
+
+ SAFE_FREE(s);
+ return list;
+}
+
+BOOL str_list_copy(char ***dest, const char **src)
+{
+ char **list, **rlist;
+ int num, lsize;
+
+ *dest = NULL;
+ if (!src)
+ return False;
+
+ num = lsize = 0;
+ list = NULL;
+
+ while (src[num]) {
+ if (num == lsize) {
+ lsize += S_LIST_ABS;
+ rlist = (char **)Realloc(list, ((sizeof(char **)) * (lsize +1)));
+ if (!rlist) {
+ DEBUG(0,("str_list_copy: Unable to re-allocate memory"));
+ str_list_free(&list);
+ return False;
+ } else
+ list = rlist;
+ memset (&list[num], 0, ((sizeof(char **)) * (S_LIST_ABS +1)));
+ }
+
+ list[num] = strdup(src[num]);
+ if (!list[num]) {
+ DEBUG(0,("str_list_copy: Unable to allocate memory"));
+ str_list_free(&list);
+ return False;
+ }
+
+ num++;
+ }
+
+ *dest = list;
+ return True;
+}
+
+/**
+ * Return true if all the elements of the list match exactly.
+ **/
+BOOL str_list_compare(char **list1, char **list2)
+{
+ int num;
+
+ if (!list1 || !list2)
+ return (list1 == list2);
+
+ for (num = 0; list1[num]; num++) {
+ if (!list2[num])
+ return False;
+ if (!strcsequal(list1[num], list2[num]))
+ return False;
+ }
+ if (list2[num])
+ return False; /* if list2 has more elements than list1 fail */
+
+ return True;
+}
+
+void str_list_free(char ***list)
+{
+ char **tlist;
+
+ if (!list || !*list)
+ return;
+ tlist = *list;
+ for(; *tlist; tlist++)
+ SAFE_FREE(*tlist);
+ SAFE_FREE(*list);
+}
+
+/******************************************************************************
+ version of standard_sub_basic() for string lists; uses alloc_sub_basic()
+ for the work
+ *****************************************************************************/
+
+BOOL str_list_sub_basic( char **list, const char *smb_name )
+{
+ char *s, *tmpstr;
+
+ while ( *list ) {
+ s = *list;
+ tmpstr = alloc_sub_basic(smb_name, s);
+ if ( !tmpstr ) {
+ DEBUG(0,("str_list_sub_basic: alloc_sub_basic() return NULL!\n"));
+ return False;
+ }
+
+ *list = tmpstr;
+
+ list++;
+ }
+
+ return True;
+}
+
+/******************************************************************************
+ substritute a specific pattern in a string list
+ *****************************************************************************/
+
+BOOL str_list_substitute(char **list, const char *pattern, const char *insert)
+{
+ char *p, *s, *t;
+ ssize_t ls, lp, li, ld, i, d;
+
+ if (!list)
+ return False;
+ if (!pattern)
+ return False;
+ if (!insert)
+ return False;
+
+ lp = (ssize_t)strlen(pattern);
+ li = (ssize_t)strlen(insert);
+ ld = li -lp;
+
+ while (*list) {
+ s = *list;
+ ls = (ssize_t)strlen(s);
+
+ while ((p = strstr_m(s, pattern))) {
+ t = *list;
+ d = p -t;
+ if (ld) {
+ t = (char *) malloc(ls +ld +1);
+ if (!t) {
+ DEBUG(0,("str_list_substitute: Unable to allocate memory"));
+ return False;
+ }
+ memcpy(t, *list, d);
+ memcpy(t +d +li, p +lp, ls -d -lp +1);
+ SAFE_FREE(*list);
+ *list = t;
+ ls += ld;
+ s = t +d +li;
+ }
+
+ for (i = 0; i < li; i++) {
+ switch (insert[i]) {
+ case '`':
+ case '"':
+ case '\'':
+ case ';':
+ case '$':
+ case '%':
+ case '\r':
+ case '\n':
+ t[d +i] = '_';
+ break;
+ default:
+ t[d +i] = insert[i];
+ }
+ }
+ }
+
+
+ list++;
+ }
+
+ return True;
+}
+
+
+#define IPSTR_LIST_SEP ","
+#define IPSTR_LIST_CHAR ','
+
+/**
+ * Add ip string representation to ipstr list. Used also
+ * as part of @function ipstr_list_make
+ *
+ * @param ipstr_list pointer to string containing ip list;
+ * MUST BE already allocated and IS reallocated if necessary
+ * @param ipstr_size pointer to current size of ipstr_list (might be changed
+ * as a result of reallocation)
+ * @param ip IP address which is to be added to list
+ * @return pointer to string appended with new ip and possibly
+ * reallocated to new length
+ **/
+
+char* ipstr_list_add(char** ipstr_list, const struct ip_service *service)
+{
+ char* new_ipstr = NULL;
+
+ /* arguments checking */
+ if (!ipstr_list || !service) return NULL;
+
+ /* attempt to convert ip to a string and append colon separator to it */
+ if (*ipstr_list) {
+ asprintf(&new_ipstr, "%s%s%s:%d", *ipstr_list, IPSTR_LIST_SEP,
+ inet_ntoa(service->ip), service->port);
+ SAFE_FREE(*ipstr_list);
+ } else {
+ asprintf(&new_ipstr, "%s:%d", inet_ntoa(service->ip), service->port);
+ }
+ *ipstr_list = new_ipstr;
+ return *ipstr_list;
+}
+
+
+/**
+ * Allocate and initialise an ipstr list using ip adresses
+ * passed as arguments.
+ *
+ * @param ipstr_list pointer to string meant to be allocated and set
+ * @param ip_list array of ip addresses to place in the list
+ * @param ip_count number of addresses stored in ip_list
+ * @return pointer to allocated ip string
+ **/
+
+char* ipstr_list_make(char** ipstr_list, const struct ip_service* ip_list, int ip_count)
+{
+ int i;
+
+ /* arguments checking */
+ if (!ip_list && !ipstr_list) return 0;
+
+ *ipstr_list = NULL;
+
+ /* process ip addresses given as arguments */
+ for (i = 0; i < ip_count; i++)
+ *ipstr_list = ipstr_list_add(ipstr_list, &ip_list[i]);
+
+ return (*ipstr_list);
+}
+
+
+/**
+ * Parse given ip string list into array of ip addresses
+ * (as ip_service structures)
+ * e.g. 192.168.1.100:389,192.168.1.78, ...
+ *
+ * @param ipstr ip string list to be parsed
+ * @param ip_list pointer to array of ip addresses which is
+ * allocated by this function and must be freed by caller
+ * @return number of succesfully parsed addresses
+ **/
+
+int ipstr_list_parse(const char* ipstr_list, struct ip_service **ip_list)
+{
+ fstring token_str;
+ size_t count;
+ int i;
+
+ if (!ipstr_list || !ip_list)
+ return 0;
+
+ count = count_chars(ipstr_list, IPSTR_LIST_CHAR) + 1;
+ if ( (*ip_list = (struct ip_service*)malloc(count * sizeof(struct ip_service))) == NULL ) {
+ DEBUG(0,("ipstr_list_parse: malloc failed for %lu entries\n", (unsigned long)count));
+ return 0;
+ }
+
+ for ( i=0;
+ next_token(&ipstr_list, token_str, IPSTR_LIST_SEP, FSTRING_LEN) && i<count;
+ i++ )
+ {
+ struct in_addr addr;
+ unsigned port = 0;
+ char *p = strchr(token_str, ':');
+
+ if (p) {
+ *p = 0;
+ port = atoi(p+1);
+ }
+
+ /* convert single token to ip address */
+ if ( (addr.s_addr = inet_addr(token_str)) == INADDR_NONE )
+ break;
+
+ (*ip_list)[i].ip = addr;
+ (*ip_list)[i].port = port;
+ }
+
+ return count;
+}
+
+
+/**
+ * Safely free ip string list
+ *
+ * @param ipstr_list ip string list to be freed
+ **/
+
+void ipstr_list_free(char* ipstr_list)
+{
+ SAFE_FREE(ipstr_list);
+}
+
+
+/**
+ Unescape a URL encoded string, in place.
+**/
+
+void rfc1738_unescape(char *buf)
+{
+ char *p=buf;
+
+ while (p && *p && (p=strchr_m(p,'%'))) {
+ int c1 = p[1];
+ int c2 = p[2];
+
+ if (c1 >= '0' && c1 <= '9')
+ c1 = c1 - '0';
+ else if (c1 >= 'A' && c1 <= 'F')
+ c1 = 10 + c1 - 'A';
+ else if (c1 >= 'a' && c1 <= 'f')
+ c1 = 10 + c1 - 'a';
+ else {p++; continue;}
+
+ if (c2 >= '0' && c2 <= '9')
+ c2 = c2 - '0';
+ else if (c2 >= 'A' && c2 <= 'F')
+ c2 = 10 + c2 - 'A';
+ else if (c2 >= 'a' && c2 <= 'f')
+ c2 = 10 + c2 - 'a';
+ else {p++; continue;}
+
+ *p = (c1<<4) | c2;
+
+ memmove(p+1, p+3, strlen(p+3)+1);
+ p++;
+ }
+}
+
+static const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/**
+ * Decode a base64 string into a DATA_BLOB - simple and slow algorithm
+ **/
+DATA_BLOB base64_decode_data_blob(const char *s)
+{
+ int bit_offset, byte_offset, idx, i, n;
+ DATA_BLOB decoded = data_blob(s, strlen(s)+1);
+ unsigned char *d = decoded.data;
+ char *p;
+
+ n=i=0;
+
+ while (*s && (p=strchr_m(b64,*s))) {
+ idx = (int)(p - b64);
+ byte_offset = (i*6)/8;
+ bit_offset = (i*6)%8;
+ d[byte_offset] &= ~((1<<(8-bit_offset))-1);
+ if (bit_offset < 3) {
+ d[byte_offset] |= (idx << (2-bit_offset));
+ n = byte_offset+1;
+ } else {
+ d[byte_offset] |= (idx >> (bit_offset-2));
+ d[byte_offset+1] = 0;
+ d[byte_offset+1] |= (idx << (8-(bit_offset-2))) & 0xFF;
+ n = byte_offset+2;
+ }
+ s++; i++;
+ }
+
+ if (*s == '=') n -= 1;
+
+ /* fix up length */
+ decoded.length = n;
+ return decoded;
+}
+
+/**
+ * Decode a base64 string in-place - wrapper for the above
+ **/
+void base64_decode_inplace(char *s)
+{
+ DATA_BLOB decoded = base64_decode_data_blob(s);
+ memcpy(s, decoded.data, decoded.length);
+ /* null terminate */
+ s[decoded.length] = '\0';
+
+ data_blob_free(&decoded);
+}
+
+/**
+ * Encode a base64 string into a malloc()ed string caller to free.
+ *
+ *From SQUID: adopted from http://ftp.sunet.se/pub2/gnu/vm/base64-encode.c with adjustments
+ **/
+char * base64_encode_data_blob(DATA_BLOB data)
+{
+ int bits = 0;
+ int char_count = 0;
+ size_t out_cnt = 0;
+ size_t len = data.length;
+ size_t output_len = data.length * 2;
+ char *result = malloc(output_len); /* get us plenty of space */
+
+ while (len-- && out_cnt < (data.length * 2) - 5) {
+ int c = (unsigned char) *(data.data++);
+ bits += c;
+ char_count++;
+ if (char_count == 3) {
+ result[out_cnt++] = b64[bits >> 18];
+ result[out_cnt++] = b64[(bits >> 12) & 0x3f];
+ result[out_cnt++] = b64[(bits >> 6) & 0x3f];
+ result[out_cnt++] = b64[bits & 0x3f];
+ bits = 0;
+ char_count = 0;
+ } else {
+ bits <<= 8;
+ }
+ }
+ if (char_count != 0) {
+ bits <<= 16 - (8 * char_count);
+ result[out_cnt++] = b64[bits >> 18];
+ result[out_cnt++] = b64[(bits >> 12) & 0x3f];
+ if (char_count == 1) {
+ result[out_cnt++] = '=';
+ result[out_cnt++] = '=';
+ } else {
+ result[out_cnt++] = b64[(bits >> 6) & 0x3f];
+ result[out_cnt++] = '=';
+ }
+ }
+ result[out_cnt] = '\0'; /* terminate */
+ return result;
+}
+
+/* read a SMB_BIG_UINT from a string */
+SMB_BIG_UINT STR_TO_SMB_BIG_UINT(const char *nptr, const char **entptr)
+{
+
+ SMB_BIG_UINT val = -1;
+ const char *p = nptr;
+
+ while (p && *p && isspace(*p))
+ p++;
+#ifdef LARGE_SMB_OFF_T
+ sscanf(p,"%llu",&val);
+#else /* LARGE_SMB_OFF_T */
+ sscanf(p,"%lu",&val);
+#endif /* LARGE_SMB_OFF_T */
+ if (entptr) {
+ while (p && *p && isdigit(*p))
+ p++;
+ *entptr = p;
+ }
+
+ return val;
+}
+
+void string_append(char **left, const char *right)
+{
+ int new_len = strlen(right) + 1;
+
+ if (*left == NULL) {
+ *left = malloc(new_len);
+ *left[0] = '\0';
+ } else {
+ new_len += strlen(*left);
+ *left = Realloc(*left, new_len);
+ }
+
+ if (*left == NULL)
+ return;
+
+ safe_strcat(*left, right, new_len-1);
+}
diff --git a/source/lib/util_unistr.c b/source/lib/util_unistr.c
new file mode 100644
index 00000000000..005f10a4c0a
--- /dev/null
+++ b/source/lib/util_unistr.c
@@ -0,0 +1,847 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Andrew Tridgell 1992-2001
+ Copyright (C) Simo Sorce 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#ifndef MAXUNI
+#define MAXUNI 1024
+#endif
+
+/* these 3 tables define the unicode case handling. They are loaded
+ at startup either via mmap() or read() from the lib directory */
+static smb_ucs2_t *upcase_table;
+static smb_ucs2_t *lowcase_table;
+static uint8 *valid_table;
+
+/**
+ * This table says which Unicode characters are valid dos
+ * characters.
+ *
+ * Each value is just a single bit.
+ **/
+static uint8 doschar_table[8192]; /* 65536 characters / 8 bits/byte */
+
+
+/**
+ * Load or generate the case handling tables.
+ *
+ * The case tables are defined in UCS2 and don't depend on any
+ * configured parameters, so they never need to be reloaded.
+ **/
+void load_case_tables(void)
+{
+ static int initialised;
+ int i;
+
+ if (initialised) return;
+ initialised = 1;
+
+ upcase_table = map_file(lib_path("upcase.dat"), 0x20000);
+ lowcase_table = map_file(lib_path("lowcase.dat"), 0x20000);
+
+ /* we would like Samba to limp along even if these tables are
+ not available */
+ if (!upcase_table) {
+ DEBUG(1,("creating lame upcase table\n"));
+ upcase_table = malloc(0x20000);
+ for (i=0;i<0x10000;i++) {
+ smb_ucs2_t v;
+ SSVAL(&v, 0, i);
+ upcase_table[v] = i;
+ }
+ for (i=0;i<256;i++) {
+ smb_ucs2_t v;
+ SSVAL(&v, 0, UCS2_CHAR(i));
+ upcase_table[v] = UCS2_CHAR(islower(i)?toupper(i):i);
+ }
+ }
+
+ if (!lowcase_table) {
+ DEBUG(1,("creating lame lowcase table\n"));
+ lowcase_table = malloc(0x20000);
+ for (i=0;i<0x10000;i++) {
+ smb_ucs2_t v;
+ SSVAL(&v, 0, i);
+ lowcase_table[v] = i;
+ }
+ for (i=0;i<256;i++) {
+ smb_ucs2_t v;
+ SSVAL(&v, 0, UCS2_CHAR(i));
+ lowcase_table[v] = UCS2_CHAR(isupper(i)?tolower(i):i);
+ }
+ }
+}
+
+/*
+ see if a ucs2 character can be mapped correctly to a dos character
+ and mapped back to the same character in ucs2
+*/
+int check_dos_char(smb_ucs2_t c)
+{
+ lazy_initialize_conv();
+
+ /* Find the right byte, and right bit within the byte; return
+ * 1 or 0 */
+ return (doschar_table[(c & 0xffff) / 8] & (1 << (c & 7))) != 0;
+}
+
+
+static int check_dos_char_slowly(smb_ucs2_t c)
+{
+ char buf[10];
+ smb_ucs2_t c2 = 0;
+ int len1, len2;
+ len1 = convert_string(CH_UCS2, CH_DOS, &c, 2, buf, sizeof(buf),False);
+ if (len1 == 0) return 0;
+ len2 = convert_string(CH_DOS, CH_UCS2, buf, len1, &c2, 2,False);
+ if (len2 != 2) return 0;
+ return (c == c2);
+}
+
+
+/**
+ * Fill out doschar table the hard way, by examining each character
+ **/
+void init_doschar_table(void)
+{
+ int i, j, byteval;
+
+ /* For each byte of packed table */
+
+ for (i = 0; i <= 0xffff; i += 8) {
+ byteval = 0;
+ for (j = 0; j <= 7; j++) {
+ smb_ucs2_t c;
+
+ c = i + j;
+
+ if (check_dos_char_slowly(c))
+ byteval |= 1 << j;
+ }
+ doschar_table[i/8] = byteval;
+ }
+}
+
+
+/**
+ * Load the valid character map table from <tt>valid.dat</tt> or
+ * create from the configured codepage.
+ *
+ * This function is called whenever the configuration is reloaded.
+ * However, the valid character table is not changed if it's loaded
+ * from a file, because we can't unmap files.
+ **/
+void init_valid_table(void)
+{
+ static int mapped_file;
+ int i;
+ const char *allowed = ".!#$%&'()_-@^`~";
+ uint8 *valid_file;
+
+ if (mapped_file) {
+ /* Can't unmap files, so stick with what we have */
+ return;
+ }
+
+ valid_file = map_file(lib_path("valid.dat"), 0x10000);
+ if (valid_file) {
+ valid_table = valid_file;
+ mapped_file = 1;
+ return;
+ }
+
+ /* Otherwise, we're using a dynamically created valid_table.
+ * It might need to be regenerated if the code page changed.
+ * We know that we're not using a mapped file, so we can
+ * free() the old one. */
+ if (valid_table) free(valid_table);
+
+ DEBUG(2,("creating default valid table\n"));
+ valid_table = malloc(0x10000);
+ for (i=0;i<128;i++)
+ valid_table[i] = isalnum(i) || strchr(allowed,i);
+
+ for (;i<0x10000;i++) {
+ smb_ucs2_t c;
+ SSVAL(&c, 0, i);
+ valid_table[i] = check_dos_char(c);
+ }
+}
+
+
+
+/*******************************************************************
+ Write a string in (little-endian) unicode format. src is in
+ the current DOS codepage. len is the length in bytes of the
+ string pointed to by dst.
+
+ if null_terminate is True then null terminate the packet (adds 2 bytes)
+
+ the return value is the length in bytes consumed by the string, including the
+ null termination if applied
+********************************************************************/
+
+size_t dos_PutUniCode(char *dst,const char *src, ssize_t len, BOOL null_terminate)
+{
+ return push_ucs2(NULL, dst, src, len,
+ STR_UNICODE|STR_NOALIGN | (null_terminate?STR_TERMINATE:0));
+}
+
+
+/*******************************************************************
+ Skip past a unicode string, but not more than len. Always move
+ past a terminating zero if found.
+********************************************************************/
+
+char *skip_unibuf(char *src, size_t len)
+{
+ char *srcend = src + len;
+
+ while (src < srcend && SVAL(src,0))
+ src += 2;
+
+ if(!SVAL(src,0))
+ src += 2;
+
+ return src;
+}
+
+/* Copy a string from little-endian or big-endian unicode source (depending
+ * on flags) to internal samba format destination
+ */
+int rpcstr_pull(char* dest, void *src, int dest_len, int src_len, int flags)
+{
+ if (!src) {
+ dest[0] = 0;
+ return 0;
+ }
+ if(dest_len==-1) dest_len=MAXUNI-3;
+ return pull_ucs2(NULL, dest, src, dest_len, src_len, flags|STR_UNICODE|STR_NOALIGN);
+}
+
+/* Copy a string from a unistr2 source to internal samba format
+ destination. Use this instead of direct calls to rpcstr_pull() to avoid
+ having to determine whether the source string is null terminated. */
+
+int rpcstr_pull_unistr2_fstring(char *dest, UNISTR2 *src)
+{
+ return pull_ucs2(NULL, dest, src->buffer, sizeof(fstring),
+ src->uni_str_len * 2, 0);
+}
+
+/* Converts a string from internal samba format to unicode
+ */
+int rpcstr_push(void* dest, const char *src, int dest_len, int flags)
+{
+ return push_ucs2(NULL, dest, src, dest_len, flags|STR_UNICODE|STR_NOALIGN);
+}
+
+/*******************************************************************
+ Return a DOS codepage version of a little-endian unicode string.
+ len is the filename length (ignoring any terminating zero) in uin16
+ units. Always null terminates.
+ Hack alert: uses fixed buffer(s).
+********************************************************************/
+char *dos_unistrn2(const uint16 *src, int len)
+{
+ static char lbufs[8][MAXUNI];
+ static int nexti;
+ char *lbuf = lbufs[nexti];
+ nexti = (nexti+1)%8;
+ pull_ucs2(NULL, lbuf, src, MAXUNI-3, len*2, STR_NOALIGN);
+ return lbuf;
+}
+
+/*******************************************************************
+ Convert a (little-endian) UNISTR2 structure to an ASCII string
+********************************************************************/
+void unistr2_to_ascii(char *dest, const UNISTR2 *str, size_t maxlen)
+{
+ if (str == NULL) {
+ *dest='\0';
+ return;
+ }
+ pull_ucs2(NULL, dest, str->buffer, maxlen, str->uni_str_len*2, STR_NOALIGN);
+}
+
+/*******************************************************************
+give a static string for displaying a UNISTR2
+********************************************************************/
+const char *unistr2_static(const UNISTR2 *str)
+{
+ static pstring ret;
+ unistr2_to_ascii(ret, str, sizeof(ret));
+ return ret;
+}
+
+
+/*******************************************************************
+ duplicate a UNISTR2 string into a null terminated char*
+ using a talloc context
+********************************************************************/
+char *unistr2_tdup(TALLOC_CTX *ctx, const UNISTR2 *str)
+{
+ char *s;
+ int maxlen = (str->uni_str_len+1)*4;
+ if (!str->buffer) return NULL;
+ s = (char *)talloc(ctx, maxlen); /* convervative */
+ if (!s) return NULL;
+ pull_ucs2(NULL, s, str->buffer, maxlen, str->uni_str_len*2,
+ STR_NOALIGN);
+ return s;
+}
+
+
+/*******************************************************************
+Return a number stored in a buffer
+********************************************************************/
+
+uint32 buffer2_to_uint32(BUFFER2 *str)
+{
+ if (str->buf_len == 4)
+ return IVAL(str->buffer, 0);
+ else
+ return 0;
+}
+
+/*******************************************************************
+ Convert a wchar to upper case.
+********************************************************************/
+
+smb_ucs2_t toupper_w(smb_ucs2_t val)
+{
+ return upcase_table[SVAL(&val,0)];
+}
+
+/*******************************************************************
+ Convert a wchar to lower case.
+********************************************************************/
+
+smb_ucs2_t tolower_w( smb_ucs2_t val )
+{
+ return lowcase_table[SVAL(&val,0)];
+
+}
+
+/*******************************************************************
+determine if a character is lowercase
+********************************************************************/
+BOOL islower_w(smb_ucs2_t c)
+{
+ return upcase_table[SVAL(&c,0)] != c;
+}
+
+/*******************************************************************
+determine if a character is uppercase
+********************************************************************/
+BOOL isupper_w(smb_ucs2_t c)
+{
+ return lowcase_table[SVAL(&c,0)] != c;
+}
+
+
+/*******************************************************************
+determine if a character is valid in a 8.3 name
+********************************************************************/
+BOOL isvalid83_w(smb_ucs2_t c)
+{
+ return valid_table[SVAL(&c,0)] != 0;
+}
+
+/*******************************************************************
+ Count the number of characters in a smb_ucs2_t string.
+********************************************************************/
+size_t strlen_w(const smb_ucs2_t *src)
+{
+ size_t len;
+
+ for(len = 0; *src++; len++) ;
+
+ return len;
+}
+
+/*******************************************************************
+ Count up to max number of characters in a smb_ucs2_t string.
+********************************************************************/
+size_t strnlen_w(const smb_ucs2_t *src, size_t max)
+{
+ size_t len;
+
+ for(len = 0; *src++ && (len < max); len++) ;
+
+ return len;
+}
+
+/*******************************************************************
+ Wide strchr().
+********************************************************************/
+
+smb_ucs2_t *strchr_w(const smb_ucs2_t *s, smb_ucs2_t c)
+{
+ while (*s != 0) {
+ if (c == *s) return (smb_ucs2_t *)s;
+ s++;
+ }
+ if (c == *s) return (smb_ucs2_t *)s;
+
+ return NULL;
+}
+
+smb_ucs2_t *strchr_wa(const smb_ucs2_t *s, char c)
+{
+ return strchr_w(s, UCS2_CHAR(c));
+}
+
+/*******************************************************************
+ Wide strrchr().
+********************************************************************/
+
+smb_ucs2_t *strrchr_w(const smb_ucs2_t *s, smb_ucs2_t c)
+{
+ const smb_ucs2_t *p = s;
+ int len = strlen_w(s);
+ if (len == 0) return NULL;
+ p += (len - 1);
+ do {
+ if (c == *p) return (smb_ucs2_t *)p;
+ } while (p-- != s);
+ return NULL;
+}
+
+/*******************************************************************
+ Wide version of strrchr that returns after doing strrchr 'n' times.
+********************************************************************/
+
+smb_ucs2_t *strnrchr_w(const smb_ucs2_t *s, smb_ucs2_t c, unsigned int n)
+{
+ const smb_ucs2_t *p = s;
+ int len = strlen_w(s);
+ if (len == 0 || !n)
+ return NULL;
+ p += (len - 1);
+ do {
+ if (c == *p)
+ n--;
+
+ if (!n)
+ return (smb_ucs2_t *)p;
+ } while (p-- != s);
+ return NULL;
+}
+
+/*******************************************************************
+ Wide strstr().
+********************************************************************/
+
+smb_ucs2_t *strstr_w(const smb_ucs2_t *s, const smb_ucs2_t *ins)
+{
+ smb_ucs2_t *r;
+ size_t slen, inslen;
+
+ if (!s || !*s || !ins || !*ins) return NULL;
+ slen = strlen_w(s);
+ inslen = strlen_w(ins);
+ r = (smb_ucs2_t *)s;
+ while ((r = strchr_w(r, *ins))) {
+ if (strncmp_w(r, ins, inslen) == 0) return r;
+ r++;
+ }
+ return NULL;
+}
+
+/*******************************************************************
+ Convert a string to lower case.
+ return True if any char is converted
+********************************************************************/
+BOOL strlower_w(smb_ucs2_t *s)
+{
+ BOOL ret = False;
+ while (*s) {
+ smb_ucs2_t v = tolower_w(*s);
+ if (v != *s) {
+ *s = v;
+ ret = True;
+ }
+ s++;
+ }
+ return ret;
+}
+
+/*******************************************************************
+ Convert a string to upper case.
+ return True if any char is converted
+********************************************************************/
+BOOL strupper_w(smb_ucs2_t *s)
+{
+ BOOL ret = False;
+ while (*s) {
+ smb_ucs2_t v = toupper_w(*s);
+ if (v != *s) {
+ *s = v;
+ ret = True;
+ }
+ s++;
+ }
+ return ret;
+}
+
+/*******************************************************************
+ convert a string to "normal" form
+********************************************************************/
+void strnorm_w(smb_ucs2_t *s)
+{
+ extern int case_default;
+ if (case_default == CASE_UPPER)
+ strupper_w(s);
+ else
+ strlower_w(s);
+}
+
+int strcmp_w(const smb_ucs2_t *a, const smb_ucs2_t *b)
+{
+ while (*b && *a == *b) { a++; b++; }
+ return (*a - *b);
+ /* warning: if *a != *b and both are not 0 we retrun a random
+ greater or lesser than 0 number not realted to which
+ string is longer */
+}
+
+int strncmp_w(const smb_ucs2_t *a, const smb_ucs2_t *b, size_t len)
+{
+ size_t n = 0;
+ while ((n < len) && *b && *a == *b) { a++; b++; n++;}
+ return (len - n)?(*a - *b):0;
+}
+
+/*******************************************************************
+case insensitive string comparison
+********************************************************************/
+int strcasecmp_w(const smb_ucs2_t *a, const smb_ucs2_t *b)
+{
+ while (*b && toupper_w(*a) == toupper_w(*b)) { a++; b++; }
+ return (tolower_w(*a) - tolower_w(*b));
+}
+
+/*******************************************************************
+case insensitive string comparison, lenght limited
+********************************************************************/
+int strncasecmp_w(const smb_ucs2_t *a, const smb_ucs2_t *b, size_t len)
+{
+ size_t n = 0;
+ while ((n < len) && *b && (toupper_w(*a) == toupper_w(*b))) { a++; b++; n++; }
+ return (len - n)?(tolower_w(*a) - tolower_w(*b)):0;
+}
+
+/*******************************************************************
+ compare 2 strings
+********************************************************************/
+BOOL strequal_w(const smb_ucs2_t *s1, const smb_ucs2_t *s2)
+{
+ if (s1 == s2) return(True);
+ if (!s1 || !s2) return(False);
+
+ return(strcasecmp_w(s1,s2)==0);
+}
+
+/*******************************************************************
+ compare 2 strings up to and including the nth char.
+ ******************************************************************/
+BOOL strnequal_w(const smb_ucs2_t *s1,const smb_ucs2_t *s2,size_t n)
+{
+ if (s1 == s2) return(True);
+ if (!s1 || !s2 || !n) return(False);
+
+ return(strncasecmp_w(s1,s2,n)==0);
+}
+
+/*******************************************************************
+duplicate string
+********************************************************************/
+smb_ucs2_t *strdup_w(const smb_ucs2_t *src)
+{
+ return strndup_w(src, 0);
+}
+
+/* if len == 0 then duplicate the whole string */
+smb_ucs2_t *strndup_w(const smb_ucs2_t *src, size_t len)
+{
+ smb_ucs2_t *dest;
+
+ if (!len) len = strlen_w(src);
+ dest = (smb_ucs2_t *)malloc((len + 1) * sizeof(smb_ucs2_t));
+ if (!dest) {
+ DEBUG(0,("strdup_w: out of memory!\n"));
+ return NULL;
+ }
+
+ memcpy(dest, src, len * sizeof(smb_ucs2_t));
+ dest[len] = 0;
+
+ return dest;
+}
+
+/*******************************************************************
+copy a string with max len
+********************************************************************/
+
+smb_ucs2_t *strncpy_w(smb_ucs2_t *dest, const smb_ucs2_t *src, const size_t max)
+{
+ size_t len;
+
+ if (!dest || !src) return NULL;
+
+ for (len = 0; (src[len] != 0) && (len < max); len++)
+ dest[len] = src[len];
+ while (len < max)
+ dest[len++] = 0;
+
+ return dest;
+}
+
+
+/*******************************************************************
+append a string of len bytes and add a terminator
+********************************************************************/
+
+smb_ucs2_t *strncat_w(smb_ucs2_t *dest, const smb_ucs2_t *src, const size_t max)
+{
+ size_t start;
+ size_t len;
+
+ if (!dest || !src) return NULL;
+
+ start = strlen_w(dest);
+ len = strnlen_w(src, max);
+
+ memcpy(&dest[start], src, len*sizeof(smb_ucs2_t));
+ dest[start+len] = 0;
+
+ return dest;
+}
+
+smb_ucs2_t *strcat_w(smb_ucs2_t *dest, const smb_ucs2_t *src)
+{
+ size_t start;
+ size_t len;
+
+ if (!dest || !src) return NULL;
+
+ start = strlen_w(dest);
+ len = strlen_w(src);
+
+ memcpy(&dest[start], src, len*sizeof(smb_ucs2_t));
+ dest[start+len] = 0;
+
+ return dest;
+}
+
+
+/*******************************************************************
+replace any occurence of oldc with newc in unicode string
+********************************************************************/
+
+void string_replace_w(smb_ucs2_t *s, smb_ucs2_t oldc, smb_ucs2_t newc)
+{
+ for(;*s;s++) {
+ if(*s==oldc) *s=newc;
+ }
+}
+
+/*******************************************************************
+trim unicode string
+********************************************************************/
+
+BOOL trim_string_w(smb_ucs2_t *s, const smb_ucs2_t *front,
+ const smb_ucs2_t *back)
+{
+ BOOL ret = False;
+ size_t len, front_len, back_len;
+
+ if (!s || !*s) return False;
+
+ len = strlen_w(s);
+
+ if (front && *front) {
+ front_len = strlen_w(front);
+ while (len && strncmp_w(s, front, front_len) == 0) {
+ memmove(s, (s + front_len), (len - front_len + 1) * sizeof(smb_ucs2_t));
+ len -= front_len;
+ ret = True;
+ }
+ }
+
+ if (back && *back) {
+ back_len = strlen_w(back);
+ while (len && strncmp_w((s + (len - back_len)), back, back_len) == 0) {
+ s[len - back_len] = 0;
+ len -= back_len;
+ ret = True;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ The *_wa() functions take a combination of 7 bit ascii
+ and wide characters They are used so that you can use string
+ functions combining C string constants with ucs2 strings
+
+ The char* arguments must NOT be multibyte - to be completely sure
+ of this only pass string constants */
+
+
+void pstrcpy_wa(smb_ucs2_t *dest, const char *src)
+{
+ int i;
+ for (i=0;i<PSTRING_LEN;i++) {
+ dest[i] = UCS2_CHAR(src[i]);
+ if (src[i] == 0) return;
+ }
+}
+
+int strcmp_wa(const smb_ucs2_t *a, const char *b)
+{
+ while (*b && *a == UCS2_CHAR(*b)) { a++; b++; }
+ return (*a - UCS2_CHAR(*b));
+}
+
+int strncmp_wa(const smb_ucs2_t *a, const char *b, size_t len)
+{
+ size_t n = 0;
+ while ((n < len) && *b && *a == UCS2_CHAR(*b)) { a++; b++; n++;}
+ return (len - n)?(*a - UCS2_CHAR(*b)):0;
+}
+
+smb_ucs2_t *strpbrk_wa(const smb_ucs2_t *s, const char *p)
+{
+ while (*s != 0) {
+ int i;
+ for (i=0; p[i] && *s != UCS2_CHAR(p[i]); i++)
+ ;
+ if (p[i]) return (smb_ucs2_t *)s;
+ s++;
+ }
+ return NULL;
+}
+
+smb_ucs2_t *strstr_wa(const smb_ucs2_t *s, const char *ins)
+{
+ smb_ucs2_t *r;
+ size_t slen, inslen;
+
+ if (!s || !*s || !ins || !*ins) return NULL;
+ slen = strlen_w(s);
+ inslen = strlen(ins);
+ r = (smb_ucs2_t *)s;
+ while ((r = strchr_w(r, UCS2_CHAR(*ins)))) {
+ if (strncmp_wa(r, ins, inslen) == 0) return r;
+ r++;
+ }
+ return NULL;
+}
+
+BOOL trim_string_wa(smb_ucs2_t *s, const char *front,
+ const char *back)
+{
+ wpstring f, b;
+
+ if (front) push_ucs2(NULL, f, front, sizeof(wpstring) - 1, STR_TERMINATE);
+ else *f = 0;
+ if (back) push_ucs2(NULL, b, back, sizeof(wpstring) - 1, STR_TERMINATE);
+ else *b = 0;
+ return trim_string_w(s, f, b);
+}
+
+/*******************************************************************
+ returns the length in number of wide characters
+ ******************************************************************/
+int unistrlen(uint16 *s)
+{
+ int len;
+
+ if (!s)
+ return -1;
+
+ for (len=0; *s; s++,len++);
+
+ return len;
+}
+
+/*******************************************************************
+ Strcpy for unicode strings. returns length (in num of wide chars)
+********************************************************************/
+
+int unistrcpy(uint16 *dst, uint16 *src)
+{
+ int num_wchars = 0;
+
+ while (*src) {
+ *dst++ = *src++;
+ num_wchars++;
+ }
+ *dst = 0;
+
+ return num_wchars;
+}
+
+/**
+ * Samba ucs2 type to UNISTR2 conversion
+ *
+ * @param ctx Talloc context to create the dst strcture (if null) and the
+ * contents of the unicode string.
+ * @param dst UNISTR2 destination. If equals null, then it's allocated.
+ * @param src smb_ucs2_t source.
+ * @param max_len maximum number of unicode characters to copy. If equals
+ * null, then null-termination of src is taken
+ *
+ * @return copied UNISTR2 destination
+ **/
+UNISTR2* ucs2_to_unistr2(TALLOC_CTX *ctx, UNISTR2* dst, smb_ucs2_t* src)
+{
+ size_t len;
+
+ if (!src)
+ return NULL;
+ len = strlen_w(src);
+
+ /* allocate UNISTR2 destination if not given */
+ if (!dst) {
+ dst = (UNISTR2*) talloc(ctx, sizeof(UNISTR2));
+ if (!dst)
+ return NULL;
+ }
+ if (!dst->buffer) {
+ dst->buffer = (uint16*) talloc(ctx, sizeof(uint16) * (len + 1));
+ if (!dst->buffer)
+ return NULL;
+ }
+
+ /* set UNISTR2 parameters */
+ dst->uni_max_len = len + 1;
+ dst->offset = 0;
+ dst->uni_str_len = len;
+
+ /* copy the actual unicode string */
+ strncpy_w(dst->buffer, src, dst->uni_max_len);
+
+ return dst;
+}
diff --git a/source/lib/util_uuid.c b/source/lib/util_uuid.c
new file mode 100644
index 00000000000..4c35236c902
--- /dev/null
+++ b/source/lib/util_uuid.c
@@ -0,0 +1,174 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * UUID server routines
+ * Copyright (C) Theodore Ts'o 1996, 1997,
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002, 2003
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+/*
+ * Offset between 15-Oct-1582 and 1-Jan-70
+ */
+#define TIME_OFFSET_HIGH 0x01B21DD2
+#define TIME_OFFSET_LOW 0x13814000
+
+void smb_uuid_pack(const struct uuid uu, UUID_FLAT *ptr)
+{
+ SIVAL(ptr, 0, uu.time_low);
+ SSVAL(ptr, 4, uu.time_mid);
+ SSVAL(ptr, 6, uu.time_hi_and_version);
+ memcpy(ptr+8, uu.clock_seq, 2);
+ memcpy(ptr+10, uu.node, 6);
+}
+
+void smb_uuid_unpack(const UUID_FLAT in, struct uuid *uu)
+{
+ uu->time_low = IVAL(in.info, 0);
+ uu->time_mid = SVAL(in.info, 4);
+ uu->time_hi_and_version = SVAL(in.info, 6);
+ memcpy(uu->clock_seq, in.info+8, 2);
+ memcpy(uu->node, in.info+10, 6);
+}
+
+const struct uuid smb_uuid_unpack_static(const UUID_FLAT in)
+{
+ static struct uuid uu;
+
+ smb_uuid_unpack(in, &uu);
+ return uu;
+}
+
+void smb_uuid_generate_random(struct uuid *uu)
+{
+ UUID_FLAT tmp;
+
+ generate_random_buffer(tmp.info, sizeof(tmp.info), True);
+ smb_uuid_unpack(tmp, uu);
+
+ uu->clock_seq[0] = (uu->clock_seq[0] & 0x3F) | 0x80;
+ uu->time_hi_and_version = (uu->time_hi_and_version & 0x0FFF) | 0x4000;
+}
+
+char *smb_uuid_to_string(const struct uuid uu)
+{
+ char *out;
+
+ asprintf(&out, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ uu.time_low, uu.time_mid, uu.time_hi_and_version,
+ uu.clock_seq[0], uu.clock_seq[1],
+ uu.node[0], uu.node[1], uu.node[2],
+ uu.node[3], uu.node[4], uu.node[5]);
+
+ return out;
+}
+
+const char *smb_uuid_string_static(const struct uuid uu)
+{
+ static char out[37];
+
+ slprintf(out, sizeof(out),
+ "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ uu.time_low, uu.time_mid, uu.time_hi_and_version,
+ uu.clock_seq[0], uu.clock_seq[1],
+ uu.node[0], uu.node[1], uu.node[2],
+ uu.node[3], uu.node[4], uu.node[5]);
+ return out;
+}
+
+BOOL smb_string_to_uuid(const char *in, struct uuid* uu)
+{
+ BOOL ret = False;
+ const char *ptr = in;
+ char *end = (char *)in;
+ int i;
+
+ if (!in || !uu) goto out;
+
+ uu->time_low = strtoul(ptr, &end, 16);
+ if ((end - ptr) != 8 || *end != '-') goto out;
+ ptr = (end + 1);
+
+ uu->time_mid = strtoul(ptr, &end, 16);
+ if ((end - ptr) != 4 || *end != '-') goto out;
+ ptr = (end + 1);
+
+ uu->time_hi_and_version = strtoul(ptr, &end, 16);
+ if ((end - ptr) != 4 || *end != '-') goto out;
+ ptr = (end + 1);
+
+ for (i = 0; i < 2; i++) {
+ int adj = 0;
+ if (*ptr >= '0' && *ptr <= '9') {
+ adj = '0';
+ } else if (*ptr >= 'a' && *ptr <= 'f') {
+ adj = 'a';
+ } else if (*ptr >= 'A' && *ptr <= 'F') {
+ adj = 'A';
+ } else {
+ goto out;
+ }
+ uu->clock_seq[i] = (*ptr - adj) << 4;
+ ptr++;
+
+ if (*ptr >= '0' && *ptr <= '9') {
+ adj = '0';
+ } else if (*ptr >= 'a' && *ptr <= 'f') {
+ adj = 'a';
+ } else if (*ptr >= 'A' && *ptr <= 'F') {
+ adj = 'A';
+ } else {
+ goto out;
+ }
+ uu->clock_seq[i] |= (*ptr - adj);
+ ptr++;
+ }
+
+ if (*ptr != '-') goto out;
+ ptr++;
+
+ for (i = 0; i < 6; i++) {
+ int adj = 0;
+ if (*ptr >= '0' && *ptr <= '9') {
+ adj = '0';
+ } else if (*ptr >= 'a' && *ptr <= 'f') {
+ adj = 'a';
+ } else if (*ptr >= 'A' && *ptr <= 'F') {
+ adj = 'A';
+ } else {
+ goto out;
+ }
+ uu->node[i] = (*ptr - adj) << 4;
+ ptr++;
+
+ if (*ptr >= '0' && *ptr <= '9') {
+ adj = '0';
+ } else if (*ptr >= 'a' && *ptr <= 'f') {
+ adj = 'a';
+ } else if (*ptr >= 'A' && *ptr <= 'F') {
+ adj = 'A';
+ } else {
+ goto out;
+ }
+ uu->node[i] |= (*ptr - adj);
+ ptr++;
+ }
+
+ ret = True;
+out:
+ return ret;
+}
diff --git a/source/lib/version.c b/source/lib/version.c
new file mode 100644
index 00000000000..99f836c2d5b
--- /dev/null
+++ b/source/lib/version.c
@@ -0,0 +1,42 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba Version functions
+
+ Copyright (C) Stefan Metzmacher 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+const char *samba_version_string(void)
+{
+#ifndef SAMBA_VERSION_VENDOR_SUFFIX
+ return SAMBA_VERSION_OFFICIAL_STRING;
+#else
+ static fstring samba_version;
+ static BOOL init_samba_version;
+
+ if (init_samba_version)
+ return samba_version;
+
+ snprintf(samba_version,sizeof(samba_version),"%s-%s",
+ SAMBA_VERSION_OFFICIAL_STRING,
+ SAMBA_VERSION_VENDOR_SUFFIX);
+
+ init_samba_version = True;
+ return samba_version;
+#endif
+}
diff --git a/source/lib/wins_srv.c b/source/lib/wins_srv.c
new file mode 100644
index 00000000000..4a54762fde7
--- /dev/null
+++ b/source/lib/wins_srv.c
@@ -0,0 +1,356 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba wins server helper functions
+ Copyright (C) Andrew Tridgell 1992-2002
+ Copyright (C) Christopher R. Hertel 2000
+ Copyright (C) Tim Potter 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*
+ This is pretty much a complete rewrite of the earlier code. The main
+ aim of the rewrite is to add support for having multiple wins server
+ lists, so Samba can register with multiple groups of wins servers
+ and each group has a failover list of wins servers.
+
+ Central to the way it all works is the idea of a wins server
+ 'tag'. A wins tag is a label for a group of wins servers. For
+ example if you use
+
+ wins server = fred:192.168.2.10 mary:192.168.3.199 fred:192.168.2.61
+
+ then you would have two groups of wins servers, one tagged with the
+ name 'fred' and the other with the name 'mary'. I would usually
+ recommend using interface names instead of 'fred' and 'mary' but
+ they can be any alpha string.
+
+ Now, how does it all work. Well, nmbd needs to register each of its
+ IPs with each of its names once with each group of wins servers. So
+ it tries registering with the first one mentioned in the list, then
+ if that fails it marks that WINS server dead and moves onto the next
+ one.
+
+ In the client code things are a bit different. As each of the groups
+ of wins servers is a separate name space we need to try each of the
+ groups until we either succeed or we run out of wins servers to
+ try. If we get a negative response from a wins server then that
+ means the name doesn't exist in that group, so we give up on that
+ group and move to the next group. If we don't get a response at all
+ then maybe the wins server is down, in which case we need to
+ failover to the next one for that group.
+
+ confused yet? (tridge)
+*/
+
+/* how long a server is marked dead for */
+#define DEATH_TIME 600
+
+/* The list of dead wins servers is stored in gencache.tdb. Each server is
+ marked dead from the point of view of a given source address. We keep a
+ separate dead list for each src address to cope with multiple interfaces
+ that are not routable to each other.
+ */
+
+#define WINS_SRV_FMT "WINS_SRV_DEAD/%s,%s" /* wins_ip,src_ip */
+
+static char *wins_srv_keystr(struct in_addr wins_ip, struct in_addr src_ip)
+{
+ char *keystr = NULL, *wins_ip_addr = NULL, *src_ip_addr = NULL;
+
+ wins_ip_addr = strdup(inet_ntoa(wins_ip));
+ src_ip_addr = strdup(inet_ntoa(src_ip));
+
+ if ( !wins_ip_addr || !src_ip_addr ) {
+ DEBUG(0,("wins_srv_keystr: malloc error\n"));
+ goto done;
+ }
+
+ if (asprintf(&keystr, WINS_SRV_FMT, wins_ip_addr, src_ip_addr) == -1) {
+ DEBUG(0, (": ns_srv_keystr: malloc error for key string\n"));
+ }
+
+done:
+ SAFE_FREE(wins_ip_addr);
+ SAFE_FREE(src_ip_addr);
+
+ return keystr;
+}
+
+/*
+ see if an ip is on the dead list
+*/
+
+BOOL wins_srv_is_dead(struct in_addr wins_ip, struct in_addr src_ip)
+{
+ char *keystr = wins_srv_keystr(wins_ip, src_ip);
+ BOOL result;
+
+ /* If the key exists then the WINS server has been marked as dead */
+
+ result = gencache_get(keystr, NULL, NULL);
+ SAFE_FREE(keystr);
+
+ DEBUG(4, ("wins_srv_is_dead: %s is %s\n", inet_ntoa(wins_ip),
+ result ? "dead" : "alive"));
+
+ return result;
+}
+
+
+/*
+ mark a wins server as being alive (for the moment)
+*/
+void wins_srv_alive(struct in_addr wins_ip, struct in_addr src_ip)
+{
+ char *keystr = wins_srv_keystr(wins_ip, src_ip);
+
+ gencache_del(keystr);
+ SAFE_FREE(keystr);
+
+ DEBUG(4, ("wins_srv_alive: marking wins server %s alive\n",
+ inet_ntoa(wins_ip)));
+}
+
+/*
+ mark a wins server as temporarily dead
+*/
+void wins_srv_died(struct in_addr wins_ip, struct in_addr src_ip)
+{
+ char *keystr;
+
+ if (is_zero_ip(wins_ip) || wins_srv_is_dead(wins_ip, src_ip))
+ return;
+
+ keystr = wins_srv_keystr(wins_ip, src_ip);
+
+ gencache_set(keystr, "DOWN", time(NULL) + DEATH_TIME);
+
+ SAFE_FREE(keystr);
+
+ DEBUG(4,("Marking wins server %s dead for %u seconds from source %s\n",
+ inet_ntoa(wins_ip), DEATH_TIME, inet_ntoa(src_ip)));
+}
+
+/*
+ return the total number of wins servers, dead or not
+*/
+unsigned wins_srv_count(void)
+{
+ const char **list;
+ int count = 0;
+
+ if (lp_wins_support()) {
+ /* simple - just talk to ourselves */
+ return 1;
+ }
+
+ list = lp_wins_server_list();
+ for (count=0; list && list[count]; count++)
+ /* nop */ ;
+
+ return count;
+}
+
+/* an internal convenience structure for an IP with a short string tag
+ attached */
+struct tagged_ip {
+ fstring tag;
+ struct in_addr ip;
+};
+
+/*
+ parse an IP string that might be in tagged format
+ the result is a tagged_ip structure containing the tag
+ and the ip in in_addr format. If there is no tag then
+ use the tag '*'
+*/
+static void parse_ip(struct tagged_ip *ip, const char *str)
+{
+ char *s = strchr(str, ':');
+ if (!s) {
+ fstrcpy(ip->tag, "*");
+ ip->ip = *interpret_addr2(str);
+ return;
+ }
+
+ ip->ip = *interpret_addr2(s+1);
+ fstrcpy(ip->tag, str);
+ s = strchr(ip->tag, ':');
+ if (s) *s = 0;
+}
+
+
+
+/*
+ return the list of wins server tags. A 'tag' is used to distinguish
+ wins server as either belonging to the same name space or a separate
+ name space. Usually you would setup your 'wins server' option to
+ list one or more wins server per interface and use the interface
+ name as your tag, but you are free to use any tag you like.
+*/
+char **wins_srv_tags(void)
+{
+ char **ret = NULL;
+ int count=0, i, j;
+ const char **list;
+
+ if (lp_wins_support()) {
+ /* give the caller something to chew on. This makes
+ the rest of the logic simpler (ie. less special cases) */
+ ret = (char **)malloc(sizeof(char *)*2);
+ if (!ret) return NULL;
+ ret[0] = strdup("*");
+ ret[1] = NULL;
+ return ret;
+ }
+
+ list = lp_wins_server_list();
+ if (!list)
+ return NULL;
+
+ /* yes, this is O(n^2) but n is very small */
+ for (i=0;list[i];i++) {
+ struct tagged_ip t_ip;
+
+ parse_ip(&t_ip, list[i]);
+
+ /* see if we already have it */
+ for (j=0;j<count;j++) {
+ if (strcmp(ret[j], t_ip.tag) == 0) {
+ break;
+ }
+ }
+
+ if (j != count) {
+ /* we already have it. Move along */
+ continue;
+ }
+
+ /* add it to the list */
+ ret = (char **)Realloc(ret, (count+2) * sizeof(char *));
+ ret[count] = strdup(t_ip.tag);
+ if (!ret[count]) break;
+ count++;
+ }
+
+ if (count) {
+ /* make sure we null terminate */
+ ret[count] = NULL;
+ }
+
+ return ret;
+}
+
+/* free a list of wins server tags given by wins_srv_tags */
+void wins_srv_tags_free(char **list)
+{
+ int i;
+ if (!list) return;
+ for (i=0; list[i]; i++) {
+ free(list[i]);
+ }
+ free(list);
+}
+
+
+/*
+ return the IP of the currently active wins server for the given tag,
+ or the zero IP otherwise
+*/
+struct in_addr wins_srv_ip_tag(const char *tag, struct in_addr src_ip)
+{
+ const char **list;
+ int i;
+ struct tagged_ip t_ip;
+
+ /* if we are a wins server then we always just talk to ourselves */
+ if (lp_wins_support()) {
+ extern struct in_addr loopback_ip;
+ return loopback_ip;
+ }
+
+ list = lp_wins_server_list();
+ if (!list || !list[0]) {
+ struct in_addr ip;
+ zero_ip(&ip);
+ return ip;
+ }
+
+ /* find the first live one for this tag */
+ for (i=0; list[i]; i++) {
+ parse_ip(&t_ip, list[i]);
+ if (strcmp(tag, t_ip.tag) != 0) {
+ /* not for the right tag. Move along */
+ continue;
+ }
+ if (!wins_srv_is_dead(t_ip.ip, src_ip)) {
+ fstring src_name;
+ fstrcpy(src_name, inet_ntoa(src_ip));
+ DEBUG(6,("Current wins server for tag '%s' with source %s is %s\n",
+ tag,
+ src_name,
+ inet_ntoa(t_ip.ip)));
+ return t_ip.ip;
+ }
+ }
+
+ /* they're all dead - try the first one until they revive */
+ for (i=0; list[i]; i++) {
+ parse_ip(&t_ip, list[i]);
+ if (strcmp(tag, t_ip.tag) != 0) {
+ continue;
+ }
+ return t_ip.ip;
+ }
+
+ /* this can't happen?? */
+ zero_ip(&t_ip.ip);
+ return t_ip.ip;
+}
+
+
+/*
+ return a count of the number of IPs for a particular tag, including
+ dead ones
+*/
+unsigned wins_srv_count_tag(const char *tag)
+{
+ const char **list;
+ int i, count=0;
+
+ /* if we are a wins server then we always just talk to ourselves */
+ if (lp_wins_support()) {
+ return 1;
+ }
+
+ list = lp_wins_server_list();
+ if (!list || !list[0]) {
+ return 0;
+ }
+
+ /* find the first live one for this tag */
+ for (i=0; list[i]; i++) {
+ struct tagged_ip t_ip;
+ parse_ip(&t_ip, list[i]);
+ if (strcmp(tag, t_ip.tag) == 0) {
+ count++;
+ }
+ }
+
+ return count;
+}
diff --git a/source/lib/xfile.c b/source/lib/xfile.c
new file mode 100644
index 00000000000..1534dd855e9
--- /dev/null
+++ b/source/lib/xfile.c
@@ -0,0 +1,378 @@
+/*
+ Unix SMB/CIFS implementation.
+ stdio replacement
+ Copyright (C) Andrew Tridgell 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ stdio is very convenient, but on some systems the file descriptor
+ in FILE* is 8 bits, so it fails when more than 255 files are open.
+
+ XFILE replaces stdio. It is less efficient, but at least it works
+ when you have lots of files open
+
+ The main restriction on XFILE is that it doesn't support seeking,
+ and doesn't support O_RDWR. That keeps the code simple.
+*/
+
+#include "includes.h"
+
+#define XBUFSIZE BUFSIZ
+
+static XFILE _x_stdin = { 0, NULL, NULL, XBUFSIZE, 0, O_RDONLY, X_IOFBF, 0 };
+static XFILE _x_stdout = { 1, NULL, NULL, XBUFSIZE, 0, O_WRONLY, X_IOLBF, 0 };
+static XFILE _x_stderr = { 2, NULL, NULL, 0, 0, O_WRONLY, X_IONBF, 0 };
+
+XFILE *x_stdin = &_x_stdin;
+XFILE *x_stdout = &_x_stdout;
+XFILE *x_stderr = &_x_stderr;
+
+#define X_FLAG_EOF 1
+#define X_FLAG_ERROR 2
+#define X_FLAG_EINVAL 3
+
+/* simulate setvbuf() */
+int x_setvbuf(XFILE *f, char *buf, int mode, size_t size)
+{
+ x_fflush(f);
+ if (f->bufused) return -1;
+
+ /* on files being read full buffering is the only option */
+ if ((f->open_flags & O_ACCMODE) == O_RDONLY) {
+ mode = X_IOFBF;
+ }
+
+ /* destroy any earlier buffer */
+ SAFE_FREE(f->buf);
+ f->buf = 0;
+ f->bufsize = 0;
+ f->next = NULL;
+ f->bufused = 0;
+ f->buftype = mode;
+
+ if (f->buftype == X_IONBF) return 0;
+
+ /* if buffering then we need some size */
+ if (size == 0) size = XBUFSIZE;
+
+ f->bufsize = size;
+ f->bufused = 0;
+
+ return 0;
+}
+
+/* allocate the buffer */
+static int x_allocate_buffer(XFILE *f)
+{
+ if (f->buf) return 1;
+ if (f->bufsize == 0) return 0;
+ f->buf = malloc(f->bufsize);
+ if (!f->buf) return 0;
+ f->next = f->buf;
+ return 1;
+}
+
+
+/* this looks more like open() than fopen(), but that is quite deliberate.
+ I want programmers to *think* about O_EXCL, O_CREAT etc not just
+ get them magically added
+*/
+XFILE *x_fopen(const char *fname, int flags, mode_t mode)
+{
+ XFILE *ret;
+
+ ret = (XFILE *)malloc(sizeof(XFILE));
+ if (!ret) return NULL;
+
+ memset(ret, 0, sizeof(XFILE));
+
+ if ((flags & O_ACCMODE) == O_RDWR) {
+ /* we don't support RDWR in XFILE - use file
+ descriptors instead */
+ errno = EINVAL;
+ return NULL;
+ }
+
+ ret->open_flags = flags;
+
+ ret->fd = sys_open(fname, flags, mode);
+ if (ret->fd == -1) {
+ SAFE_FREE(ret);
+ return NULL;
+ }
+
+ x_setvbuf(ret, NULL, X_IOFBF, XBUFSIZE);
+
+ return ret;
+}
+
+/* simulate fclose() */
+int x_fclose(XFILE *f)
+{
+ int ret;
+
+ /* make sure we flush any buffered data */
+ x_fflush(f);
+
+ ret = close(f->fd);
+ f->fd = -1;
+ if (f->buf) {
+ /* make sure data can't leak into a later malloc */
+ memset(f->buf, 0, f->bufsize);
+ SAFE_FREE(f->buf);
+ }
+ SAFE_FREE(f);
+ return ret;
+}
+
+/* simulate fwrite() */
+size_t x_fwrite(const void *p, size_t size, size_t nmemb, XFILE *f)
+{
+ ssize_t ret;
+ size_t total=0;
+
+ /* we might be writing unbuffered */
+ if (f->buftype == X_IONBF ||
+ (!f->buf && !x_allocate_buffer(f))) {
+ ret = write(f->fd, p, size*nmemb);
+ if (ret == -1) return -1;
+ return ret/size;
+ }
+
+
+ while (total < size*nmemb) {
+ size_t n = f->bufsize - f->bufused;
+ n = MIN(n, (size*nmemb)-total);
+
+ if (n == 0) {
+ /* it's full, flush it */
+ x_fflush(f);
+ continue;
+ }
+
+ memcpy(f->buf + f->bufused, total+(const char *)p, n);
+ f->bufused += n;
+ total += n;
+ }
+
+ /* when line buffered we need to flush at the last linefeed. This can
+ flush a bit more than necessary, but that is harmless */
+ if (f->buftype == X_IOLBF && f->bufused) {
+ int i;
+ for (i=(size*nmemb)-1; i>=0; i--) {
+ if (*(i+(const char *)p) == '\n') {
+ x_fflush(f);
+ break;
+ }
+ }
+ }
+
+ return total/size;
+}
+
+/* thank goodness for asprintf() */
+ int x_vfprintf(XFILE *f, const char *format, va_list ap)
+{
+ char *p;
+ int len, ret;
+ va_list ap2;
+
+ VA_COPY(ap2, ap);
+
+ len = vasprintf(&p, format, ap2);
+ if (len <= 0) return len;
+ ret = x_fwrite(p, 1, len, f);
+ SAFE_FREE(p);
+ return ret;
+}
+
+ int x_fprintf(XFILE *f, const char *format, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, format);
+ ret = x_vfprintf(f, format, ap);
+ va_end(ap);
+ return ret;
+}
+
+/* at least fileno() is simple! */
+int x_fileno(XFILE *f)
+{
+ return f->fd;
+}
+
+/* simulate fflush() */
+int x_fflush(XFILE *f)
+{
+ int ret;
+
+ if (f->flags & X_FLAG_ERROR) return -1;
+
+ if ((f->open_flags & O_ACCMODE) != O_WRONLY) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (f->bufused == 0) return 0;
+
+ ret = write(f->fd, f->buf, f->bufused);
+ if (ret == -1) return -1;
+
+ f->bufused -= ret;
+ if (f->bufused > 0) {
+ f->flags |= X_FLAG_ERROR;
+ memmove(f->buf, ret + (char *)f->buf, f->bufused);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* simulate setbuffer() */
+void x_setbuffer(XFILE *f, char *buf, size_t size)
+{
+ x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, size);
+}
+
+/* simulate setbuf() */
+void x_setbuf(XFILE *f, char *buf)
+{
+ x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, XBUFSIZE);
+}
+
+/* simulate setlinebuf() */
+void x_setlinebuf(XFILE *f)
+{
+ x_setvbuf(f, NULL, X_IOLBF, 0);
+}
+
+
+/* simulate feof() */
+int x_feof(XFILE *f)
+{
+ if (f->flags & X_FLAG_EOF) return 1;
+ return 0;
+}
+
+/* simulate ferror() */
+int x_ferror(XFILE *f)
+{
+ if (f->flags & X_FLAG_ERROR) return 1;
+ return 0;
+}
+
+/* fill the read buffer */
+static void x_fillbuf(XFILE *f)
+{
+ int n;
+
+ if (f->bufused) return;
+
+ if (!f->buf && !x_allocate_buffer(f)) return;
+
+ n = read(f->fd, f->buf, f->bufsize);
+ if (n <= 0) return;
+ f->bufused = n;
+ f->next = f->buf;
+}
+
+/* simulate fgetc() */
+int x_fgetc(XFILE *f)
+{
+ int ret;
+
+ if (f->flags & (X_FLAG_EOF | X_FLAG_ERROR)) return EOF;
+
+ if (f->bufused == 0) x_fillbuf(f);
+
+ if (f->bufused == 0) {
+ f->flags |= X_FLAG_EOF;
+ return EOF;
+ }
+
+ ret = *(unsigned char *)(f->next);
+ f->next++;
+ f->bufused--;
+ return ret;
+}
+
+/* simulate fread */
+size_t x_fread(void *p, size_t size, size_t nmemb, XFILE *f)
+{
+ size_t total = 0;
+ while (total < size*nmemb) {
+ int c = x_fgetc(f);
+ if (c == EOF) break;
+ (total+(char *)p)[0] = (char)c;
+ total++;
+ }
+ return total/size;
+}
+
+/* simulate fgets() */
+char *x_fgets(char *s, int size, XFILE *stream)
+{
+ char *s0 = s;
+ int l = size;
+ while (l>1) {
+ int c = x_fgetc(stream);
+ if (c == EOF) break;
+ *s++ = (char)c;
+ l--;
+ if (c == '\n') break;
+ }
+ if (l==size || x_ferror(stream)) {
+ return 0;
+ }
+ *s = 0;
+ return s0;
+}
+
+/* trivial seek, works only for SEEK_SET and SEEK_END if SEEK_CUR is
+ * set then an error is returned */
+off_t x_tseek(XFILE *f, off_t offset, int whence)
+{
+ if (f->flags & X_FLAG_ERROR)
+ return -1;
+
+ /* only SEEK_SET and SEEK_END are supported */
+ /* SEEK_CUR needs internal offset counter */
+ if (whence != SEEK_SET && whence != SEEK_END) {
+ f->flags |= X_FLAG_EINVAL;
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* empty the buffer */
+ switch (f->open_flags & O_ACCMODE) {
+ case O_RDONLY:
+ f->bufused = 0;
+ break;
+ case O_WRONLY:
+ if (x_fflush(f) != 0)
+ return -1;
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ f->flags &= ~X_FLAG_EOF;
+ return (off_t)sys_lseek(f->fd, offset, whence);
+}
diff --git a/source/libads/.cvsignore b/source/libads/.cvsignore
new file mode 100644
index 00000000000..5f2a5c4cf75
--- /dev/null
+++ b/source/libads/.cvsignore
@@ -0,0 +1,2 @@
+*.po
+*.po32
diff --git a/source/libads/ads_ldap.c b/source/libads/ads_ldap.c
new file mode 100644
index 00000000000..944cb1599cc
--- /dev/null
+++ b/source/libads/ads_ldap.c
@@ -0,0 +1,234 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind ADS backend functions
+
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Andrew Bartlett 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#ifdef HAVE_LDAP
+
+/* convert a single name to a sid in a domain */
+NTSTATUS ads_name_to_sid(ADS_STRUCT *ads,
+ const char *name,
+ DOM_SID *sid,
+ enum SID_NAME_USE *type)
+{
+ const char *attrs[] = {"objectSid", "sAMAccountType", NULL};
+ int count;
+ ADS_STATUS rc;
+ void *res = NULL;
+ char *ldap_exp;
+ uint32 t;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ char *escaped_name = escape_ldap_string_alloc(name);
+ char *escaped_realm = escape_ldap_string_alloc(ads->config.realm);
+
+ if (!escaped_name || !escaped_realm) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ if (asprintf(&ldap_exp, "(|(sAMAccountName=%s)(userPrincipalName=%s@%s))",
+ escaped_name, escaped_name, escaped_realm) == -1) {
+ DEBUG(1,("ads_name_to_sid: asprintf failed!\n"));
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ rc = ads_search_retry(ads, &res, ldap_exp, attrs);
+ free(ldap_exp);
+ if (!ADS_ERR_OK(rc)) {
+ DEBUG(1,("name_to_sid ads_search: %s\n", ads_errstr(rc)));
+ goto done;
+ }
+
+ count = ads_count_replies(ads, res);
+ if (count != 1) {
+ DEBUG(1,("name_to_sid: %s not found\n", name));
+ goto done;
+ }
+
+ if (!ads_pull_sid(ads, res, "objectSid", sid)) {
+ DEBUG(1,("No sid for %s !?\n", name));
+ goto done;
+ }
+
+ if (!ads_pull_uint32(ads, res, "sAMAccountType", &t)) {
+ DEBUG(1,("No sAMAccountType for %s !?\n", name));
+ goto done;
+ }
+
+ *type = ads_atype_map(t);
+
+ status = NT_STATUS_OK;
+
+ DEBUG(3,("ads name_to_sid mapped %s\n", name));
+
+done:
+ if (res) ads_msgfree(ads, res);
+
+ SAFE_FREE(escaped_name);
+ SAFE_FREE(escaped_realm);
+
+ return status;
+}
+
+/* convert a sid to a user or group name */
+NTSTATUS ads_sid_to_name(ADS_STRUCT *ads,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *sid,
+ char **name,
+ enum SID_NAME_USE *type)
+{
+ const char *attrs[] = {"userPrincipalName",
+ "sAMAccountName",
+ "sAMAccountType", NULL};
+ ADS_STATUS rc;
+ void *msg = NULL;
+ char *ldap_exp = NULL;
+ char *sidstr = NULL;
+ uint32 atype;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+
+ if (!(sidstr = sid_binstring(sid))) {
+ DEBUG(1,("ads_sid_to_name: sid_binstring failed!\n"));
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ if (asprintf(&ldap_exp, "(objectSid=%s)", sidstr) == -1) {
+ DEBUG(1,("ads_sid_to_name: asprintf failed!\n"));
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ rc = ads_search_retry(ads, &msg, ldap_exp, attrs);
+ if (!ADS_ERR_OK(rc)) {
+ status = ads_ntstatus(rc);
+ DEBUG(1,("ads_sid_to_name ads_search: %s\n", ads_errstr(rc)));
+ goto done;
+ }
+
+ if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype)) {
+ goto done;
+ }
+
+ *name = ads_pull_username(ads, mem_ctx, msg);
+ if (!*name) {
+ DEBUG(1,("ads_sid_to_name: ads_pull_username retuned NULL!\n"));
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ *type = ads_atype_map(atype);
+
+ status = NT_STATUS_OK;
+
+ DEBUG(3,("ads sid_to_name mapped %s\n", *name));
+
+done:
+ if (msg) ads_msgfree(ads, msg);
+
+ SAFE_FREE(ldap_exp);
+ SAFE_FREE(sidstr);
+
+ return status;
+}
+
+
+/* convert a sid to a DN */
+
+ADS_STATUS ads_sid_to_dn(ADS_STRUCT *ads,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *sid,
+ char **dn)
+{
+ ADS_STATUS rc;
+ LDAPMessage *msg = NULL;
+ LDAPMessage *entry = NULL;
+ char *ldap_exp;
+ char *sidstr = NULL;
+ int count;
+ char *dn2 = NULL;
+
+ const char *attr[] = {
+ "dn",
+ NULL
+ };
+
+ if (!(sidstr = sid_binstring(sid))) {
+ DEBUG(1,("ads_sid_to_dn: sid_binstring failed!\n"));
+ rc = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ goto done;
+ }
+
+ if(!(ldap_exp = talloc_asprintf(mem_ctx, "(objectSid=%s)", sidstr))) {
+ DEBUG(1,("ads_sid_to_dn: talloc_asprintf failed!\n"));
+ rc = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ goto done;
+ }
+
+ rc = ads_search_retry(ads, (void **)&msg, ldap_exp, attr);
+
+ if (!ADS_ERR_OK(rc)) {
+ DEBUG(1,("ads_sid_to_dn ads_search: %s\n", ads_errstr(rc)));
+ goto done;
+ }
+
+ if ((count = ads_count_replies(ads, msg)) != 1) {
+ fstring sid_string;
+ DEBUG(1,("ads_sid_to_dn (sid=%s): Not found (count=%d)\n",
+ sid_to_string(sid_string, sid), count));
+ rc = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+ goto done;
+ }
+
+ entry = ads_first_entry(ads, msg);
+
+ dn2 = ads_get_dn(ads, entry);
+
+ if (!dn2) {
+ rc = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ goto done;
+ }
+
+ *dn = talloc_strdup(mem_ctx, dn2);
+
+ if (!*dn) {
+ ads_memfree(ads, dn2);
+ rc = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ goto done;
+ }
+
+ rc = ADS_ERROR_NT(NT_STATUS_OK);
+
+ DEBUG(3,("ads sid_to_dn mapped %s\n", dn2));
+
+ SAFE_FREE(dn2);
+done:
+ if (msg) ads_msgfree(ads, msg);
+ if (dn2) ads_memfree(ads, dn2);
+
+ SAFE_FREE(sidstr);
+
+ return rc;
+}
+
+#endif
diff --git a/source/libads/ads_status.c b/source/libads/ads_status.c
new file mode 100644
index 00000000000..463f647f9c7
--- /dev/null
+++ b/source/libads/ads_status.c
@@ -0,0 +1,142 @@
+/*
+ Unix SMB/CIFS implementation.
+ ads (active directory) utility library
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Remus Koos 2001
+ Copyright (C) Andrew Bartlett 2001
+
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*
+ build a ADS_STATUS structure
+*/
+ADS_STATUS ads_build_error(enum ads_error_type etype,
+ int rc, int minor_status)
+{
+ ADS_STATUS ret;
+
+ if (etype == ENUM_ADS_ERROR_NT) {
+ DEBUG(0,("don't use ads_build_error with ENUM_ADS_ERROR_NT!\n"));
+ ret.err.rc = -1;
+ ret.error_type = ENUM_ADS_ERROR_SYSTEM;
+ ret.minor_status = 0;
+ return ret;
+ }
+
+ ret.err.rc = rc;
+ ret.error_type = etype;
+ ret.minor_status = minor_status;
+ return ret;
+}
+
+ADS_STATUS ads_build_nt_error(enum ads_error_type etype,
+ NTSTATUS nt_status)
+{
+ ADS_STATUS ret;
+
+ if (etype != ENUM_ADS_ERROR_NT) {
+ DEBUG(0,("don't use ads_build_nt_error without ENUM_ADS_ERROR_NT!\n"));
+ ret.err.rc = -1;
+ ret.error_type = ENUM_ADS_ERROR_SYSTEM;
+ ret.minor_status = 0;
+ return ret;
+ }
+ ret.err.nt_status = nt_status;
+ ret.error_type = etype;
+ ret.minor_status = 0;
+ return ret;
+}
+
+/*
+ do a rough conversion between ads error codes and NT status codes
+ we'll need to fill this in more
+*/
+NTSTATUS ads_ntstatus(ADS_STATUS status)
+{
+ if (status.error_type == ENUM_ADS_ERROR_NT){
+ return status.err.nt_status;
+ }
+#ifdef HAVE_LDAP
+ if ((status.error_type == ENUM_ADS_ERROR_LDAP)
+ && (status.err.rc == LDAP_NO_MEMORY)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+#endif
+#ifdef HAVE_KRB5
+ if (status.error_type == ENUM_ADS_ERROR_KRB5) {
+ if (status.err.rc == KRB5KDC_ERR_PREAUTH_FAILED) {
+ return NT_STATUS_LOGON_FAILURE;
+ } else if (status.err.rc == KRB5_KDC_UNREACH) {
+ return NT_STATUS_NO_LOGON_SERVERS;
+ }
+ }
+#endif
+ if (ADS_ERR_OK(status)) return NT_STATUS_OK;
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/*
+ return a string for an error from a ads routine
+*/
+const char *ads_errstr(ADS_STATUS status)
+{
+ uint32 msg_ctx;
+ static char *ret;
+
+ SAFE_FREE(ret);
+ msg_ctx = 0;
+
+ switch (status.error_type) {
+ case ENUM_ADS_ERROR_SYSTEM:
+ return strerror(status.err.rc);
+#ifdef HAVE_LDAP
+ case ENUM_ADS_ERROR_LDAP:
+ return ldap_err2string(status.err.rc);
+#endif
+#ifdef HAVE_KRB5
+ case ENUM_ADS_ERROR_KRB5:
+ return error_message(status.err.rc);
+#endif
+#ifdef HAVE_GSSAPI
+ case ENUM_ADS_ERROR_GSS:
+ {
+ uint32 minor;
+
+ gss_buffer_desc msg1, msg2;
+ msg1.value = NULL;
+ msg2.value = NULL;
+ gss_display_status(&minor, status.err.rc, GSS_C_GSS_CODE,
+ GSS_C_NULL_OID, &msg_ctx, &msg1);
+ gss_display_status(&minor, status.minor_status, GSS_C_MECH_CODE,
+ GSS_C_NULL_OID, &msg_ctx, &msg2);
+ asprintf(&ret, "%s : %s", (char *)msg1.value, (char *)msg2.value);
+ gss_release_buffer(&minor, &msg1);
+ gss_release_buffer(&minor, &msg2);
+ return ret;
+ }
+#endif
+ case ENUM_ADS_ERROR_NT:
+ return get_friendly_nt_error_msg(ads_ntstatus(status));
+ default:
+ return "Unknown ADS error type!? (not compiled in?)";
+ }
+
+}
+
+
diff --git a/source/libads/ads_struct.c b/source/libads/ads_struct.c
new file mode 100644
index 00000000000..92f37093f46
--- /dev/null
+++ b/source/libads/ads_struct.c
@@ -0,0 +1,143 @@
+/*
+ Unix SMB/CIFS implementation.
+ ads (active directory) utility library
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Andrew Bartlett 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* return a ldap dn path from a string, given separators and field name
+ caller must free
+*/
+char *ads_build_path(const char *realm, const char *sep, const char *field, int reverse)
+{
+ char *p, *r;
+ int numbits = 0;
+ char *ret;
+ int len;
+
+ r = strdup(realm);
+
+ if (!r || !*r)
+ return r;
+
+ for (p=r; *p; p++)
+ if (strchr(sep, *p))
+ numbits++;
+
+ len = (numbits+1)*(strlen(field)+1) + strlen(r) + 1;
+
+ ret = malloc(len);
+ if (!ret)
+ return NULL;
+
+ strlcpy(ret,field, len);
+ p=strtok(r,sep);
+ strlcat(ret, p, len);
+
+ while ((p=strtok(NULL,sep))) {
+ char *s;
+ if (reverse)
+ asprintf(&s, "%s%s,%s", field, p, ret);
+ else
+ asprintf(&s, "%s,%s%s", ret, field, p);
+ free(ret);
+ ret = s;
+ }
+
+ free(r);
+ return ret;
+}
+
+/* return a dn of the form "dc=AA,dc=BB,dc=CC" from a
+ realm of the form AA.BB.CC
+ caller must free
+*/
+char *ads_build_dn(const char *realm)
+{
+ return ads_build_path(realm, ".", "dc=", 0);
+}
+
+
+#ifndef LDAP_PORT
+#define LDAP_PORT 389
+#endif
+
+/*
+ initialise a ADS_STRUCT, ready for some ads_ ops
+*/
+ADS_STRUCT *ads_init(const char *realm,
+ const char *workgroup,
+ const char *ldap_server)
+{
+ ADS_STRUCT *ads;
+
+ ads = (ADS_STRUCT *)smb_xmalloc(sizeof(*ads));
+ ZERO_STRUCTP(ads);
+
+ ads->server.realm = realm? strdup(realm) : NULL;
+ ads->server.workgroup = workgroup ? strdup(workgroup) : NULL;
+ ads->server.ldap_server = ldap_server? strdup(ldap_server) : NULL;
+
+ /* we need to know if this is a foreign realm */
+ if (realm && *realm && !strequal(lp_realm(), realm)) {
+ ads->server.foreign = 1;
+ }
+ if (workgroup && *workgroup && !strequal(lp_workgroup(), workgroup)) {
+ ads->server.foreign = 1;
+ }
+
+ /* the caller will own the memory by default */
+ ads->is_mine = 1;
+
+ return ads;
+}
+
+/*
+ free the memory used by the ADS structure initialized with 'ads_init(...)'
+*/
+void ads_destroy(ADS_STRUCT **ads)
+{
+ if (ads && *ads) {
+ BOOL is_mine;
+
+ is_mine = (*ads)->is_mine;
+#if HAVE_LDAP
+ if ((*ads)->ld) ldap_unbind((*ads)->ld);
+#endif
+ SAFE_FREE((*ads)->server.realm);
+ SAFE_FREE((*ads)->server.workgroup);
+ SAFE_FREE((*ads)->server.ldap_server);
+ SAFE_FREE((*ads)->server.ldap_uri);
+
+ SAFE_FREE((*ads)->auth.realm);
+ SAFE_FREE((*ads)->auth.password);
+ SAFE_FREE((*ads)->auth.user_name);
+ SAFE_FREE((*ads)->auth.kdc_server);
+
+ SAFE_FREE((*ads)->config.realm);
+ SAFE_FREE((*ads)->config.bind_path);
+ SAFE_FREE((*ads)->config.ldap_server_name);
+
+
+ ZERO_STRUCTP(*ads);
+
+ if ( is_mine )
+ SAFE_FREE(*ads);
+ }
+}
diff --git a/source/libads/ads_utils.c b/source/libads/ads_utils.c
new file mode 100644
index 00000000000..1aad0bed547
--- /dev/null
+++ b/source/libads/ads_utils.c
@@ -0,0 +1,137 @@
+/*
+ Unix SMB/CIFS implementation.
+ ads (active directory) utility library
+
+ Copyright (C) Stefan (metze) Metzmacher 2002
+ Copyright (C) Andrew Tridgell 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*
+translated the ACB_CTRL Flags to UserFlags (userAccountControl)
+*/
+uint32 ads_acb2uf(uint16 acb)
+{
+ uint32 uf = 0x00000000;
+
+ if (acb & ACB_DISABLED) uf |= UF_ACCOUNTDISABLE;
+ if (acb & ACB_HOMDIRREQ) uf |= UF_HOMEDIR_REQUIRED;
+ if (acb & ACB_PWNOTREQ) uf |= UF_PASSWD_NOTREQD;
+ if (acb & ACB_TEMPDUP) uf |= UF_TEMP_DUPLICATE_ACCOUNT;
+ if (acb & ACB_NORMAL) uf |= UF_NORMAL_ACCOUNT;
+ if (acb & ACB_MNS) uf |= UF_MNS_LOGON_ACCOUNT;
+ if (acb & ACB_DOMTRUST) uf |= UF_INTERDOMAIN_TRUST_ACCOUNT;
+ if (acb & ACB_WSTRUST) uf |= UF_WORKSTATION_TRUST_ACCOUNT;
+ if (acb & ACB_SVRTRUST) uf |= UF_SERVER_TRUST_ACCOUNT;
+ if (acb & ACB_PWNOEXP) uf |= UF_DONT_EXPIRE_PASSWD;
+ if (acb & ACB_AUTOLOCK) uf |= UF_LOCKOUT;
+
+ return uf;
+}
+
+/*
+translated the UserFlags (userAccountControl) to ACB_CTRL Flags
+*/
+uint16 ads_uf2acb(uint32 uf)
+{
+ uint16 acb = 0x0000;
+
+ if (uf & UF_ACCOUNTDISABLE) acb |= ACB_DISABLED;
+ if (uf & UF_HOMEDIR_REQUIRED) acb |= ACB_HOMDIRREQ;
+ if (uf & UF_PASSWD_NOTREQD) acb |= ACB_PWNOTREQ;
+ if (uf & UF_MNS_LOGON_ACCOUNT) acb |= ACB_MNS;
+ if (uf & UF_DONT_EXPIRE_PASSWD) acb |= ACB_PWNOEXP;
+ if (uf & UF_LOCKOUT) acb |= ACB_AUTOLOCK;
+
+ switch (uf & UF_ACCOUNT_TYPE_MASK)
+ {
+ case UF_TEMP_DUPLICATE_ACCOUNT: acb |= ACB_TEMPDUP;break;
+ case UF_NORMAL_ACCOUNT: acb |= ACB_NORMAL;break;
+ case UF_INTERDOMAIN_TRUST_ACCOUNT: acb |= ACB_DOMTRUST;break;
+ case UF_WORKSTATION_TRUST_ACCOUNT: acb |= ACB_WSTRUST;break;
+ case UF_SERVER_TRUST_ACCOUNT: acb |= ACB_SVRTRUST;break;
+ /*Fix Me: what should we do here? */
+ default: acb |= ACB_NORMAL;break;
+ }
+
+ return acb;
+}
+
+/*
+get the accountType from the UserFlags
+*/
+uint32 ads_uf2atype(uint32 uf)
+{
+ uint32 atype = 0x00000000;
+
+ if (uf & UF_NORMAL_ACCOUNT) atype = ATYPE_NORMAL_ACCOUNT;
+ else if (uf & UF_TEMP_DUPLICATE_ACCOUNT) atype = ATYPE_NORMAL_ACCOUNT;
+ else if (uf & UF_SERVER_TRUST_ACCOUNT) atype = ATYPE_WORKSTATION_TRUST;
+ else if (uf & UF_WORKSTATION_TRUST_ACCOUNT) atype = ATYPE_WORKSTATION_TRUST;
+ else if (uf & UF_INTERDOMAIN_TRUST_ACCOUNT) atype = ATYPE_INTERDOMAIN_TRUST;
+
+ return atype;
+}
+
+/*
+get the accountType from the groupType
+*/
+uint32 ads_gtype2atype(uint32 gtype)
+{
+ uint32 atype = 0x00000000;
+
+ switch(gtype) {
+ case GTYPE_SECURITY_BUILTIN_LOCAL_GROUP:
+ atype = ATYPE_SECURITY_LOCAL_GROUP;
+ break;
+ case GTYPE_SECURITY_DOMAIN_LOCAL_GROUP:
+ atype = ATYPE_SECURITY_LOCAL_GROUP;
+ break;
+ case GTYPE_SECURITY_GLOBAL_GROUP:
+ atype = ATYPE_SECURITY_GLOBAL_GROUP;
+ break;
+
+ case GTYPE_DISTRIBUTION_GLOBAL_GROUP:
+ atype = ATYPE_DISTRIBUTION_GLOBAL_GROUP;
+ break;
+ case GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP:
+ atype = ATYPE_DISTRIBUTION_UNIVERSAL_GROUP;
+ break;
+ case GTYPE_DISTRIBUTION_UNIVERSAL_GROUP:
+ atype = ATYPE_DISTRIBUTION_LOCAL_GROUP;
+ break;
+ }
+
+ return atype;
+}
+
+/* turn a sAMAccountType into a SID_NAME_USE */
+enum SID_NAME_USE ads_atype_map(uint32 atype)
+{
+ switch (atype & 0xF0000000) {
+ case ATYPE_GLOBAL_GROUP:
+ return SID_NAME_DOM_GRP;
+ case ATYPE_SECURITY_LOCAL_GROUP:
+ return SID_NAME_ALIAS;
+ case ATYPE_ACCOUNT:
+ return SID_NAME_USER;
+ default:
+ DEBUG(1,("hmm, need to map account type 0x%x\n", atype));
+ }
+ return SID_NAME_UNKNOWN;
+}
diff --git a/source/libads/authdata.c b/source/libads/authdata.c
new file mode 100644
index 00000000000..29170af377e
--- /dev/null
+++ b/source/libads/authdata.c
@@ -0,0 +1,617 @@
+/*
+ Unix SMB/CIFS implementation.
+ kerberos authorization data (PAC) utility library
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#ifdef HAVE_KRB5
+
+static DATA_BLOB unwrap_pac(DATA_BLOB *auth_data)
+{
+ DATA_BLOB pac_contents;
+ ASN1_DATA data;
+ int data_type;
+
+ asn1_load(&data, *auth_data);
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+ asn1_start_tag(&data, ASN1_CONTEXT(0));
+ asn1_read_Integer(&data, &data_type);
+ asn1_end_tag(&data);
+ asn1_start_tag(&data, ASN1_CONTEXT(1));
+ asn1_read_OctetString(&data, &pac_contents);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+ asn1_free(&data);
+ return pac_contents;
+}
+
+static BOOL pac_io_unknown_type_10(const char *desc, UNKNOWN_TYPE_10 *type_10,
+ prs_struct *ps, int depth)
+{
+ if (NULL == type_10)
+ return False;
+
+ prs_debug(ps, depth, desc, "pac_io_unknown_type_10");
+ depth++;
+
+ if (!smb_io_time("unknown_time", &type_10->unknown_time, ps, depth))
+ return False;
+
+ if (!prs_uint16("len", ps, depth, &type_10->len))
+ return False;
+
+ if (UNMARSHALLING(ps) && type_10->len) {
+ type_10->username = (uint16 *) prs_alloc_mem(ps, type_10->len);
+ if (!type_10->username) {
+ DEBUG(3, ("No memory available\n"));
+ return False;
+ }
+ }
+
+ if (!prs_uint16s(True, "name", ps, depth, type_10->username,
+ (type_10->len / sizeof(uint16))))
+ return False;
+
+ return True;
+
+}
+
+
+static BOOL pac_io_krb_sids(const char *desc, KRB_SID_AND_ATTRS *sid_and_attr,
+ prs_struct *ps, int depth)
+{
+ if (NULL == sid_and_attr)
+ return False;
+
+ prs_debug(ps, depth, desc, "pac_io_krb_sids");
+ depth++;
+
+ if (UNMARSHALLING(ps)) {
+ sid_and_attr->sid =
+ (DOM_SID2 * ) prs_alloc_mem(ps, sizeof(DOM_SID2));
+ if (!sid_and_attr->sid) {
+ DEBUG(3, ("No memory available\n"));
+ return False;
+ }
+ }
+
+ if(!smb_io_dom_sid2("sid", sid_and_attr->sid, ps, depth))
+ return False;
+
+ return True;
+}
+
+
+static BOOL pac_io_krb_attrs(const char *desc, KRB_SID_AND_ATTRS *sid_and_attr,
+ prs_struct *ps, int depth)
+{
+ if (NULL == sid_and_attr)
+ return False;
+
+ prs_debug(ps, depth, desc, "pac_io_krb_attrs");
+ depth++;
+
+ if (!prs_uint32("sid_ptr", ps, depth, &sid_and_attr->sid_ptr))
+ return False;
+ if (!prs_uint32("attrs", ps, depth, &sid_and_attr->attrs))
+ return False;
+
+ return True;
+}
+
+static BOOL pac_io_krb_sid_and_attr_array(const char *desc,
+ KRB_SID_AND_ATTR_ARRAY *array,
+ uint32 num,
+ prs_struct *ps, int depth)
+{
+ int i;
+
+ if (NULL == array)
+ return False;
+
+ prs_debug(ps, depth, desc, "pac_io_krb_sid_and_attr_array");
+ depth++;
+
+
+ if (!prs_uint32("count", ps, depth, &array->count))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ array->krb_sid_and_attrs = (KRB_SID_AND_ATTRS *)
+ prs_alloc_mem(ps, sizeof(KRB_SID_AND_ATTRS) * num);
+ if (!array->krb_sid_and_attrs) {
+ DEBUG(3, ("No memory available\n"));
+ return False;
+ }
+ }
+
+ for (i=0; i<num; i++) {
+ if (!pac_io_krb_attrs(desc,
+ &array->krb_sid_and_attrs[i],
+ ps, depth))
+ return False;
+
+ }
+ for (i=0; i<num; i++) {
+ if (!pac_io_krb_sids(desc,
+ &array->krb_sid_and_attrs[i],
+ ps, depth))
+ return False;
+
+ }
+
+ return True;
+
+}
+
+static BOOL pac_io_group_membership(const char *desc,
+ GROUP_MEMBERSHIP *membership,
+ prs_struct *ps, int depth)
+{
+ if (NULL == membership)
+ return False;
+
+ prs_debug(ps, depth, desc, "pac_io_group_membership");
+ depth++;
+
+ if (!prs_uint32("rid", ps, depth, &membership->rid))
+ return False;
+ if (!prs_uint32("attrs", ps, depth, &membership->attrs))
+ return False;
+
+ return True;
+}
+
+
+static BOOL pac_io_group_membership_array(const char *desc,
+ GROUP_MEMBERSHIP_ARRAY *array,
+ uint32 num,
+ prs_struct *ps, int depth)
+{
+ int i;
+
+ if (NULL == array)
+ return False;
+
+ prs_debug(ps, depth, desc, "pac_io_group_membership_array");
+ depth++;
+
+
+ if (!prs_uint32("count", ps, depth, &array->count))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ array->group_membership = (GROUP_MEMBERSHIP *)
+ prs_alloc_mem(ps, sizeof(GROUP_MEMBERSHIP) * num);
+ if (!array->group_membership) {
+ DEBUG(3, ("No memory available\n"));
+ return False;
+ }
+ }
+
+ for (i=0; i<num; i++) {
+ if (!pac_io_group_membership(desc,
+ &array->group_membership[i],
+ ps, depth))
+ return False;
+
+ }
+
+ return True;
+
+}
+
+static BOOL pac_io_pac_logon_info(const char *desc, PAC_LOGON_INFO *info,
+ prs_struct *ps, int depth)
+{
+ uint32 garbage;
+ if (NULL == info)
+ return False;
+
+ prs_debug(ps, depth, desc, "pac_io_pac_logon_info");
+ depth++;
+
+ if (!prs_uint32("unknown", ps, depth, &garbage))
+ return False;
+ if (!prs_uint32("unknown", ps, depth, &garbage))
+ return False;
+ if (!prs_uint32("bufferlen", ps, depth, &garbage))
+ return False;
+ if (!prs_uint32("bufferlenhi", ps, depth, &garbage))
+ return False;
+ if (!prs_uint32("pointer", ps, depth, &garbage))
+ return False;
+
+ if (!smb_io_time("logon_time", &info->logon_time, ps, depth))
+ return False;
+ if (!smb_io_time("logoff_time", &info->logoff_time, ps, depth))
+ return False;
+ if (!smb_io_time("kickoff_time", &info->kickoff_time, ps, depth))
+ return False;
+ if (!smb_io_time("pass_last_set_time", &info->pass_last_set_time,
+ ps, depth))
+ return False;
+ if (!smb_io_time("pass_can_change_time", &info->pass_can_change_time,
+ ps, depth))
+ return False;
+ if (!smb_io_time("pass_must_change_time", &info->pass_must_change_time,
+ ps, depth))
+ return False;
+
+ if (!smb_io_unihdr("hdr_user_name", &info->hdr_user_name, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_full_name", &info->hdr_full_name, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_logon_script", &info->hdr_logon_script,
+ ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_profile_path", &info->hdr_profile_path,
+ ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_home_dir", &info->hdr_home_dir, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_dir_drive", &info->hdr_dir_drive, ps, depth))
+ return False;
+
+ if (!prs_uint16("logon_count", ps, depth, &info->logon_count))
+ return False;
+ if (!prs_uint16("reserved12", ps, depth, &info->reserved12))
+ return False;
+ if (!prs_uint32("user_rid", ps, depth, &info->user_rid))
+ return False;
+ if (!prs_uint32("group_rid", ps, depth, &info->group_rid))
+ return False;
+ if (!prs_uint32("group_count", ps, depth, &info->group_count))
+ return False;
+ /* I haven't seen this contain anything yet, but when it does
+ we will have to make sure we decode the contents in the middle
+ all the unistr2s ... */
+ if (!prs_uint32("group_mem_ptr", ps, depth,
+ &info->group_membership_ptr))
+ return False;
+ if (!prs_uint32("user_flags", ps, depth, &info->user_flags))
+ return False;
+
+ if (!prs_uint32("reserved13.0", ps, depth, &info->reserved13[0]))
+ return False;
+ if (!prs_uint32("reserved13.1", ps, depth, &info->reserved13[1]))
+ return False;
+ if (!prs_uint32("reserved13.2", ps, depth, &info->reserved13[2]))
+ return False;
+ if (!prs_uint32("reserved13.3", ps, depth, &info->reserved13[3]))
+ return False;
+
+ if (!smb_io_unihdr("hdr_dom_controller",
+ &info->hdr_dom_controller, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_dom_name", &info->hdr_dom_name, ps, depth))
+ return False;
+
+ /* this should be followed, but just get ptr for now */
+ if (!prs_uint32("ptr_dom_sid", ps, depth, &info->ptr_dom_sid))
+ return False;
+
+ if (!prs_uint32("reserved16.0", ps, depth, &info->reserved16[0]))
+ return False;
+ if (!prs_uint32("reserved16.1", ps, depth, &info->reserved16[1]))
+ return False;
+
+ /* might be acb_info */
+ if (!prs_uint32("reserved17", ps, depth, &info->reserved17))
+ return False;
+
+
+ if (!prs_uint32("reserved18.0", ps, depth, &info->reserved18[0]))
+ return False;
+ if (!prs_uint32("reserved18.1", ps, depth, &info->reserved18[1]))
+ return False;
+ if (!prs_uint32("reserved18.2", ps, depth, &info->reserved18[2]))
+ return False;
+ if (!prs_uint32("reserved18.3", ps, depth, &info->reserved18[3]))
+ return False;
+ if (!prs_uint32("reserved18.4", ps, depth, &info->reserved18[4]))
+ return False;
+ if (!prs_uint32("reserved18.5", ps, depth, &info->reserved18[5]))
+ return False;
+ if (!prs_uint32("reserved18.6", ps, depth, &info->reserved18[6]))
+ return False;
+
+ if (!prs_uint32("sid_count", ps, depth, &info->sid_count))
+ return False;
+ if (!prs_uint32("ptr_extra_sids", ps, depth, &info->ptr_extra_sids))
+ return False;
+ if (!prs_uint32("ptr_res_group_dom_sid", ps, depth,
+ &info->ptr_res_group_dom_sid))
+ return False;
+ if (!prs_uint32("res_group_count", ps, depth, &info->res_group_count))
+ return False;
+ if (!prs_uint32("ptr_res_groups", ps, depth, &info->ptr_res_groups))
+ return False;
+
+ if(!smb_io_unistr2("uni_user_name", &info->uni_user_name,
+ info->hdr_user_name.buffer, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_full_name", &info->uni_full_name,
+ info->hdr_full_name.buffer, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_logon_script", &info->uni_logon_script,
+ info->hdr_logon_script.buffer, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_profile_path", &info->uni_profile_path,
+ info->hdr_profile_path.buffer, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_home_dir", &info->uni_home_dir,
+ info->hdr_home_dir.buffer, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_dir_drive", &info->uni_dir_drive,
+ info->hdr_dir_drive.buffer, ps, depth))
+ return False;
+
+ if (info->group_membership_ptr) {
+ if (!pac_io_group_membership_array("group membership",
+ &info->groups,
+ info->group_count,
+ ps, depth))
+ return False;
+ }
+
+
+ if(!smb_io_unistr2("uni_dom_controller", &info->uni_dom_controller,
+ info->hdr_dom_controller.buffer, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_dom_name", &info->uni_dom_name,
+ info->hdr_dom_name.buffer, ps, depth))
+ return False;
+
+ if(info->ptr_dom_sid)
+ if(!smb_io_dom_sid2("dom_sid", &info->dom_sid, ps, depth))
+ return False;
+
+
+ if (info->sid_count && info->ptr_extra_sids)
+ if (!pac_io_krb_sid_and_attr_array("extra_sids",
+ &info->extra_sids,
+ info->sid_count,
+ ps, depth))
+ return False;
+
+ if (info->ptr_res_group_dom_sid)
+ if (!smb_io_dom_sid2("res_group_dom_sid",
+ &info->res_group_dom_sid, ps, depth))
+ return False;
+
+ if (info->ptr_res_groups)
+ if (!pac_io_group_membership_array("res group membership",
+ &info->res_groups,
+ info->res_group_count,
+ ps, depth))
+ return False;
+
+ return True;
+}
+
+
+static BOOL pac_io_pac_signature_data(const char *desc,
+ PAC_SIGNATURE_DATA *data, uint32 length,
+ prs_struct *ps, int depth)
+{
+ uint32 siglen = length - sizeof(uint32);
+ if (NULL == data)
+ return False;
+
+ prs_debug(ps, depth, desc, "pac_io_pac_signature_data");
+ depth++;
+
+ if (!prs_uint32("type", ps, depth, &data->type))
+ return False;
+ if (UNMARSHALLING(ps)) {
+ data->signature = (unsigned char *)prs_alloc_mem(ps, siglen);
+ if (!data->signature) {
+ DEBUG(3, ("No memory available\n"));
+ return False;
+ }
+ }
+ if (!prs_uint8s(False, "signature", ps, depth, data->signature,siglen))
+ return False;
+
+ return True;
+}
+
+static BOOL pac_io_pac_info_hdr_ctr(const char *desc, PAC_INFO_HDR *hdr,
+ prs_struct *ps, int depth)
+{
+ if (NULL == hdr)
+ return False;
+
+ prs_debug(ps, depth, desc, "pac_io_pac_info_hdr_ctr");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (hdr->offset != prs_offset(ps)) {
+ DEBUG(5, ("offset in header(x%x) and data(x%x) do not match\n",
+ hdr->offset, prs_offset(ps)));
+ prs_set_offset(ps, hdr->offset);
+ }
+
+ if (UNMARSHALLING(ps) && hdr->size > 0) {
+ hdr->ctr = (PAC_INFO_CTR *)
+ prs_alloc_mem(ps, sizeof(PAC_INFO_CTR));
+ if (!hdr->ctr) {
+ DEBUG(3, ("No memory available\n"));
+ return False;
+ }
+ }
+
+ switch(hdr->type) {
+ case PAC_TYPE_LOGON_INFO:
+ DEBUG(5, ("PAC_TYPE_LOGON_INFO\n"));
+ if (UNMARSHALLING(ps))
+ hdr->ctr->pac.logon_info = (PAC_LOGON_INFO *)
+ prs_alloc_mem(ps, sizeof(PAC_LOGON_INFO));
+ if (!hdr->ctr->pac.logon_info) {
+ DEBUG(3, ("No memory available\n"));
+ return False;
+ }
+ if (!pac_io_pac_logon_info(desc, hdr->ctr->pac.logon_info,
+ ps, depth))
+ return False;
+ break;
+
+ case PAC_TYPE_SERVER_CHECKSUM:
+ DEBUG(5, ("PAC_TYPE_SERVER_CHECKSUM\n"));
+ if (UNMARSHALLING(ps))
+ hdr->ctr->pac.srv_cksum = (PAC_SIGNATURE_DATA *)
+ prs_alloc_mem(ps, sizeof(PAC_SIGNATURE_DATA));
+ if (!hdr->ctr->pac.srv_cksum) {
+ DEBUG(3, ("No memory available\n"));
+ return False;
+ }
+ if (!pac_io_pac_signature_data(desc, hdr->ctr->pac.srv_cksum,
+ hdr->size, ps, depth))
+ return False;
+ break;
+
+ case PAC_TYPE_PRIVSVR_CHECKSUM:
+ DEBUG(5, ("PAC_TYPE_PRIVSVR_CHECKSUM\n"));
+ if (UNMARSHALLING(ps))
+ hdr->ctr->pac.privsrv_cksum = (PAC_SIGNATURE_DATA *)
+ prs_alloc_mem(ps, sizeof(PAC_SIGNATURE_DATA));
+ if (!hdr->ctr->pac.privsrv_cksum) {
+ DEBUG(3, ("No memory available\n"));
+ return False;
+ }
+ if (!pac_io_pac_signature_data(desc,
+ hdr->ctr->pac.privsrv_cksum,
+ hdr->size, ps, depth))
+ return False;
+ break;
+
+ case PAC_TYPE_UNKNOWN_10:
+ DEBUG(5, ("PAC_TYPE_UNKNOWN_10\n"));
+ if (UNMARSHALLING(ps))
+ hdr->ctr->pac.type_10 = (UNKNOWN_TYPE_10 *)
+ prs_alloc_mem(ps, sizeof(UNKNOWN_TYPE_10));
+ if (!hdr->ctr->pac.type_10) {
+ DEBUG(3, ("No memory available\n"));
+ return False;
+ }
+ if (!pac_io_unknown_type_10(desc, hdr->ctr->pac.type_10,
+ ps, depth))
+ return False;
+ break;
+
+ default:
+ /* dont' know, so we need to skip it */
+ DEBUG(3, ("unknown PAC type %d\n", hdr->type));
+ prs_set_offset(ps, prs_offset(ps) + hdr->size);
+ }
+
+ return True;
+}
+
+static BOOL pac_io_pac_info_hdr(const char *desc, PAC_INFO_HDR *hdr,
+ prs_struct *ps, int depth)
+{
+ if (NULL == hdr)
+ return False;
+
+ prs_debug(ps, depth, desc, "pac_io_pac_info_hdr");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("type", ps, depth, &hdr->type))
+ return False;
+ if (!prs_uint32("size", ps, depth, &hdr->size))
+ return False;
+ if (!prs_uint32("offset", ps, depth, &hdr->offset))
+ return False;
+ if (!prs_uint32("offsethi", ps, depth, &hdr->offsethi))
+ return False;
+
+ return True;
+}
+
+static BOOL pac_io_pac_data(const char *desc, PAC_DATA *data,
+ prs_struct *ps, int depth)
+{
+ int i;
+
+ if (NULL == data)
+ return False;
+
+ prs_debug(ps, depth, desc, "pac_io_pac_data");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("num_buffers", ps, depth, &data->num_buffers))
+ return False;
+ if (!prs_uint32("version", ps, depth, &data->version))
+ return False;
+
+ if (UNMARSHALLING(ps) && data->num_buffers > 0) {
+ if ((data->pac_info_hdr_ptr = (PAC_INFO_HDR *)
+ prs_alloc_mem(ps, sizeof(PAC_INFO_HDR) *
+ data->num_buffers)) == NULL) {
+ return False;
+ }
+ }
+
+ for (i=0; i<data->num_buffers; i++) {
+ if (!pac_io_pac_info_hdr(desc, &data->pac_info_hdr_ptr[i], ps,
+ depth))
+ return False;
+ }
+
+ for (i=0; i<data->num_buffers; i++) {
+ if (!pac_io_pac_info_hdr_ctr(desc, &data->pac_info_hdr_ptr[i],
+ ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+PAC_DATA *decode_pac_data(DATA_BLOB *auth_data, TALLOC_CTX *ctx)
+{
+ DATA_BLOB pac_data_blob = unwrap_pac(auth_data);
+ prs_struct ps;
+ PAC_DATA *pac_data;
+
+ DEBUG(5,("dump_pac_data\n"));
+ prs_init(&ps, pac_data_blob.length, ctx, UNMARSHALL);
+ prs_copy_data_in(&ps, (char *)pac_data_blob.data, pac_data_blob.length);
+ prs_set_offset(&ps, 0);
+
+ data_blob_free(&pac_data_blob);
+
+ pac_data = (PAC_DATA *) talloc_zero(ctx, sizeof(PAC_DATA));
+ pac_io_pac_data("pac data", pac_data, &ps, 0);
+
+ prs_mem_free(&ps);
+
+ return pac_data;
+}
+
+#endif
diff --git a/source/libads/disp_sec.c b/source/libads/disp_sec.c
new file mode 100644
index 00000000000..c9de447e698
--- /dev/null
+++ b/source/libads/disp_sec.c
@@ -0,0 +1,159 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions. ADS stuff
+ Copyright (C) Alexey Kotovich 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static struct perm_mask_str {
+ uint32 mask;
+ const char *str;
+} perms[] = {
+ {SEC_RIGHTS_FULL_CTRL, "[Full Control]"},
+
+ {SEC_RIGHTS_LIST_CONTENTS, "[List Contents]"},
+ {SEC_RIGHTS_LIST_OBJECT, "[List Object]"},
+
+ {SEC_RIGHTS_READ_ALL_PROP, "[Read All Properties]"},
+ {SEC_RIGHTS_READ_PERMS, "[Read Permissions]"},
+
+ {SEC_RIGHTS_WRITE_ALL_VALID, "[All validate writes]"},
+ {SEC_RIGHTS_WRITE_ALL_PROP, "[Write All Properties]"},
+
+ {SEC_RIGHTS_MODIFY_PERMS, "[Modify Permissions]"},
+ {SEC_RIGHTS_MODIFY_OWNER, "[Modify Owner]"},
+
+ {SEC_RIGHTS_CREATE_CHILD, "[Create All Child Objects]"},
+
+ {SEC_RIGHTS_DELETE, "[Delete]"},
+ {SEC_RIGHTS_DELETE_SUBTREE, "[Delete Subtree]"},
+ {SEC_RIGHTS_DELETE_CHILD, "[Delete All Child Objects]"},
+
+ {SEC_RIGHTS_CHANGE_PASSWD, "[Change Password]"},
+ {SEC_RIGHTS_RESET_PASSWD, "[Reset Password]"},
+ {0, 0}
+};
+
+/* convert a security permissions into a string */
+static void ads_disp_perms(uint32 type)
+{
+ int i = 0;
+ int j = 0;
+
+ printf("Permissions: ");
+
+ if (type == SEC_RIGHTS_FULL_CTRL) {
+ printf("%s\n", perms[j].str);
+ return;
+ }
+
+ for (i = 0; i < 32; i++) {
+ if (type & (1 << i)) {
+ for (j = 1; perms[j].str; j ++) {
+ if (perms[j].mask == (((unsigned) 1) << i)) {
+ printf("\n\t%s", perms[j].str);
+ }
+ }
+ type &= ~(1 << i);
+ }
+ }
+
+ /* remaining bits get added on as-is */
+ if (type != 0) {
+ printf("[%08x]", type);
+ }
+ puts("");
+}
+
+/* display ACE */
+static void ads_disp_ace(SEC_ACE *sec_ace)
+{
+ const char *access_type = "UNKNOWN";
+
+ if (!sec_ace_object(sec_ace->type)) {
+ printf("------- ACE (type: 0x%02x, flags: 0x%02x, size: 0x%02x, mask: 0x%x)\n",
+ sec_ace->type,
+ sec_ace->flags,
+ sec_ace->size,
+ sec_ace->info.mask);
+ } else {
+ printf("------- ACE (type: 0x%02x, flags: 0x%02x, size: 0x%02x, mask: 0x%x, object flags: 0x%x)\n",
+ sec_ace->type,
+ sec_ace->flags,
+ sec_ace->size,
+ sec_ace->info.mask,
+ sec_ace->obj_flags);
+ }
+
+ if (sec_ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED) {
+ access_type = "ALLOWED";
+ } else if (sec_ace->type == SEC_ACE_TYPE_ACCESS_DENIED) {
+ access_type = "DENIED";
+ } else if (sec_ace->type == SEC_ACE_TYPE_SYSTEM_AUDIT) {
+ access_type = "SYSTEM AUDIT";
+ } else if (sec_ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT) {
+ access_type = "ALLOWED OBJECT";
+ } else if (sec_ace->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT) {
+ access_type = "DENIED OBJECT";
+ } else if (sec_ace->type == SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT) {
+ access_type = "AUDIT OBJECT";
+ }
+
+ printf("access SID: %s\naccess type: %s\n",
+ sid_string_static(&sec_ace->trustee), access_type);
+
+ ads_disp_perms(sec_ace->info.mask);
+}
+
+/* display ACL */
+static void ads_disp_acl(SEC_ACL *sec_acl, const char *type)
+{
+ if (!sec_acl)
+ printf("------- (%s) ACL not present\n", type);
+ else {
+ printf("------- (%s) ACL (revision: %d, size: %d, number of ACEs: %d)\n",
+ type,
+ sec_acl->revision,
+ sec_acl->size,
+ sec_acl->num_aces);
+ }
+}
+
+/* display SD */
+void ads_disp_sd(SEC_DESC *sd)
+{
+ int i;
+
+ printf("-------------- Security Descriptor (revision: %d, type: 0x%02x)\n",
+ sd->revision,
+ sd->type);
+ printf("owner SID: %s\n", sid_string_static(sd->owner_sid));
+ printf("group SID: %s\n", sid_string_static(sd->grp_sid));
+
+ ads_disp_acl(sd->sacl, "system");
+ for (i = 0; i < sd->sacl->num_aces; i ++)
+ ads_disp_ace(&sd->sacl->ace[i]);
+
+ ads_disp_acl(sd->dacl, "user");
+ for (i = 0; i < sd->dacl->num_aces; i ++)
+ ads_disp_ace(&sd->dacl->ace[i]);
+
+ printf("-------------- End Of Security Descriptor\n");
+}
+
+
diff --git a/source/libads/kerberos.c b/source/libads/kerberos.c
new file mode 100644
index 00000000000..70f6f3386c7
--- /dev/null
+++ b/source/libads/kerberos.c
@@ -0,0 +1,175 @@
+/*
+ Unix SMB/CIFS implementation.
+ kerberos utility library
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Remus Koos 2001
+
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#ifdef HAVE_KRB5
+
+/*
+ we use a prompter to avoid a crash bug in the kerberos libs when
+ dealing with empty passwords
+ this prompter is just a string copy ...
+*/
+static krb5_error_code
+kerb_prompter(krb5_context ctx, void *data,
+ const char *name,
+ const char *banner,
+ int num_prompts,
+ krb5_prompt prompts[])
+{
+ if (num_prompts == 0) return 0;
+
+ memset(prompts[0].reply->data, 0, prompts[0].reply->length);
+ if (prompts[0].reply->length > 0) {
+ if (data) {
+ strncpy(prompts[0].reply->data, data, prompts[0].reply->length-1);
+ prompts[0].reply->length = strlen(prompts[0].reply->data);
+ } else {
+ prompts[0].reply->length = 0;
+ }
+ }
+ return 0;
+}
+
+/*
+ simulate a kinit, putting the tgt in the default cache location
+ remus@snapserver.com
+*/
+int kerberos_kinit_password(const char *principal, const char *password, int time_offset, time_t *expire_time)
+{
+ krb5_context ctx;
+ krb5_error_code code = 0;
+ krb5_ccache cc;
+ krb5_principal me;
+ krb5_creds my_creds;
+
+ if ((code = krb5_init_context(&ctx)))
+ return code;
+
+ if (time_offset != 0) {
+ krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
+ }
+
+ if ((code = krb5_cc_default(ctx, &cc))) {
+ krb5_free_context(ctx);
+ return code;
+ }
+
+ if ((code = krb5_parse_name(ctx, principal, &me))) {
+ krb5_free_context(ctx);
+ return code;
+ }
+
+ if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, NULL,
+ kerb_prompter,
+ password, 0, NULL, NULL))) {
+ krb5_free_principal(ctx, me);
+ krb5_free_context(ctx);
+ return code;
+ }
+
+ if ((code = krb5_cc_initialize(ctx, cc, me))) {
+ krb5_free_cred_contents(ctx, &my_creds);
+ krb5_free_principal(ctx, me);
+ krb5_free_context(ctx);
+ return code;
+ }
+
+ if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
+ krb5_cc_close(ctx, cc);
+ krb5_free_cred_contents(ctx, &my_creds);
+ krb5_free_principal(ctx, me);
+ krb5_free_context(ctx);
+ return code;
+ }
+
+ if (expire_time)
+ *expire_time = (time_t) my_creds.times.endtime;
+
+ krb5_cc_close(ctx, cc);
+ krb5_free_cred_contents(ctx, &my_creds);
+ krb5_free_principal(ctx, me);
+ krb5_free_context(ctx);
+
+ return 0;
+}
+
+
+
+/* run kinit to setup our ccache */
+int ads_kinit_password(ADS_STRUCT *ads)
+{
+ char *s;
+ int ret;
+
+ if (asprintf(&s, "%s@%s", ads->auth.user_name, ads->auth.realm) == -1) {
+ return KRB5_CC_NOMEM;
+ }
+
+ if (!ads->auth.password) {
+ return KRB5_LIBOS_CANTREADPWD;
+ }
+
+ ret = kerberos_kinit_password(s, ads->auth.password, ads->auth.time_offset, &ads->auth.expire);
+
+ if (ret) {
+ DEBUG(0,("kerberos_kinit_password %s failed: %s\n",
+ s, error_message(ret)));
+ }
+ free(s);
+ return ret;
+}
+
+int ads_kdestroy(const char *cc_name)
+{
+ krb5_error_code code;
+ krb5_context ctx;
+ krb5_ccache cc;
+
+ if ((code = krb5_init_context (&ctx))) {
+ DEBUG(3, ("ads_kdestroy: kdb5_init_context rc=%d\n", code));
+ return code;
+ }
+
+ if (!cc_name) {
+ if ((code = krb5_cc_default(ctx, &cc))) {
+ krb5_free_context(ctx);
+ return code;
+ }
+ } else {
+ if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
+ DEBUG(3, ("ads_kdestroy: krb5_cc_resolve rc=%d\n",
+ code));
+ krb5_free_context(ctx);
+ return code;
+ }
+ }
+
+ if ((code = krb5_cc_destroy (ctx, cc))) {
+ DEBUG(3, ("ads_kdestroy: krb5_cc_destroy rc=%d\n", code));
+ }
+
+ krb5_free_context (ctx);
+ return code;
+}
+
+#endif
diff --git a/source/libads/kerberos_verify.c b/source/libads/kerberos_verify.c
new file mode 100644
index 00000000000..47559c1abb7
--- /dev/null
+++ b/source/libads/kerberos_verify.c
@@ -0,0 +1,271 @@
+/*
+ Unix SMB/CIFS implementation.
+ kerberos utility library
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Remus Koos 2001
+ Copyright (C) Luke Howard 2003
+ Copyright (C) Guenther Deschner 2003
+ Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#ifdef HAVE_KRB5
+
+/*
+ verify an incoming ticket and parse out the principal name and
+ authorization_data if available
+*/
+NTSTATUS ads_verify_ticket(const char *realm, const DATA_BLOB *ticket,
+ char **principal, DATA_BLOB *auth_data,
+ DATA_BLOB *ap_rep,
+ DATA_BLOB *session_key)
+{
+ NTSTATUS sret = NT_STATUS_LOGON_FAILURE;
+ krb5_context context = NULL;
+ krb5_auth_context auth_context = NULL;
+ krb5_data packet;
+ krb5_ticket *tkt = NULL;
+ krb5_rcache rcache = NULL;
+ int ret, i;
+ krb5_keyblock *key = NULL;
+
+ krb5_principal host_princ;
+ char *host_princ_s = NULL;
+ BOOL free_host_princ = False;
+ BOOL got_replay_mutex = False;
+
+ fstring myname;
+ char *password_s = NULL;
+ krb5_data password;
+ krb5_enctype *enctypes = NULL;
+#if 0
+ krb5_address local_addr;
+ krb5_address remote_addr;
+#endif
+ BOOL auth_ok = False;
+
+ ZERO_STRUCT(packet);
+ ZERO_STRUCT(password);
+ ZERO_STRUCTP(auth_data);
+ ZERO_STRUCTP(ap_rep);
+
+ if (!secrets_init()) {
+ DEBUG(1,("ads_verify_ticket: secrets_init failed\n"));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ password_s = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
+ if (!password_s) {
+ DEBUG(1,("ads_verify_ticket: failed to fetch machine password\n"));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ password.data = password_s;
+ password.length = strlen(password_s);
+
+ ret = krb5_init_context(&context);
+ if (ret) {
+ DEBUG(1,("ads_verify_ticket: krb5_init_context failed (%s)\n", error_message(ret)));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ ret = krb5_set_default_realm(context, realm);
+ if (ret) {
+ DEBUG(1,("ads_verify_ticket: krb5_set_default_realm failed (%s)\n", error_message(ret)));
+ sret = NT_STATUS_LOGON_FAILURE;
+ goto out;
+ }
+
+ /* This whole process is far more complex than I would
+ like. We have to go through all this to allow us to store
+ the secret internally, instead of using /etc/krb5.keytab */
+
+ ret = krb5_auth_con_init(context, &auth_context);
+ if (ret) {
+ DEBUG(1,("ads_verify_ticket: krb5_auth_con_init failed (%s)\n", error_message(ret)));
+ sret = NT_STATUS_LOGON_FAILURE;
+ goto out;
+ }
+
+ fstrcpy(myname, global_myname());
+ strlower_m(myname);
+ asprintf(&host_princ_s, "HOST/%s@%s", myname, lp_realm());
+ ret = krb5_parse_name(context, host_princ_s, &host_princ);
+ if (ret) {
+ DEBUG(1,("ads_verify_ticket: krb5_parse_name(%s) failed (%s)\n",
+ host_princ_s, error_message(ret)));
+ sret = NT_STATUS_LOGON_FAILURE;
+ goto out;
+ }
+
+ free_host_princ = True;
+
+ /*
+ * JRA. We must set the rcache here. This will prevent replay attacks.
+ */
+
+ ret = krb5_get_server_rcache(context, krb5_princ_component(context, host_princ, 0), &rcache);
+ if (ret) {
+ DEBUG(1,("ads_verify_ticket: krb5_get_server_rcache failed (%s)\n", error_message(ret)));
+ sret = NT_STATUS_LOGON_FAILURE;
+ goto out;
+ }
+
+ ret = krb5_auth_con_setrcache(context, auth_context, rcache);
+ if (ret) {
+ DEBUG(1,("ads_verify_ticket: krb5_auth_con_setrcache failed (%s)\n", error_message(ret)));
+ sret = NT_STATUS_LOGON_FAILURE;
+ goto out;
+ }
+
+ /* CIFS doesn't use addresses in tickets. This would breat NAT. JRA */
+
+ if ((ret = get_kerberos_allowed_etypes(context, &enctypes))) {
+ DEBUG(1,("ads_verify_ticket: krb5_get_permitted_enctypes failed (%s)\n",
+ error_message(ret)));
+ sret = NT_STATUS_LOGON_FAILURE;
+ goto out;
+ }
+
+ /* Lock a mutex surrounding the replay as there is no locking in the MIT krb5
+ * code surrounding the replay cache... */
+
+ if (!grab_server_mutex("replay cache mutex")) {
+ DEBUG(1,("ads_verify_ticket: unable to protect replay cache with mutex.\n"));
+ sret = NT_STATUS_LOGON_FAILURE;
+ goto out;
+ }
+
+ got_replay_mutex = True;
+
+ /* We need to setup a auth context with each possible encoding type in turn. */
+ for (i=0;enctypes[i];i++) {
+ if (!(key = (krb5_keyblock *)malloc(sizeof(*key)))) {
+ sret = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ if (create_kerberos_key_from_string(context, host_princ, &password, key, enctypes[i])) {
+ continue;
+ }
+
+ krb5_auth_con_setuseruserkey(context, auth_context, key);
+
+ krb5_free_keyblock(context, key);
+
+ packet.length = ticket->length;
+ packet.data = (krb5_pointer)ticket->data;
+
+ if (!(ret = krb5_rd_req(context, &auth_context, &packet,
+ NULL,
+ NULL, NULL, &tkt))) {
+ DEBUG(10,("ads_verify_ticket: enc type [%u] decrypted message !\n",
+ (unsigned int)enctypes[i] ));
+ auth_ok = True;
+ break;
+ }
+
+ DEBUG((ret != KRB5_BAD_ENCTYPE) ? 3 : 10,
+ ("ads_verify_ticket: enc type [%u] failed to decrypt with error %s\n",
+ (unsigned int)enctypes[i], error_message(ret)));
+ }
+
+ release_server_mutex();
+ got_replay_mutex = False;
+
+ if (!auth_ok) {
+ DEBUG(3,("ads_verify_ticket: krb5_rd_req with auth failed (%s)\n",
+ error_message(ret)));
+ sret = NT_STATUS_LOGON_FAILURE;
+ goto out;
+ }
+
+ ret = krb5_mk_rep(context, auth_context, &packet);
+ if (ret) {
+ DEBUG(3,("ads_verify_ticket: Failed to generate mutual authentication reply (%s)\n",
+ error_message(ret)));
+ sret = NT_STATUS_LOGON_FAILURE;
+ goto out;
+ }
+
+ *ap_rep = data_blob(packet.data, packet.length);
+ free(packet.data);
+
+ get_krb5_smb_session_key(context, auth_context, session_key, True);
+ dump_data_pw("SMB session key (from ticket)\n", session_key->data, session_key->length);
+
+#if 0
+ file_save("/tmp/ticket.dat", ticket->data, ticket->length);
+#endif
+
+ get_auth_data_from_tkt(auth_data, tkt);
+
+ {
+ TALLOC_CTX *ctx = talloc_init("pac data");
+ decode_pac_data(auth_data, ctx);
+ talloc_destroy(ctx);
+ }
+
+#if 0
+ if (tkt->enc_part2) {
+ file_save("/tmp/authdata.dat",
+ tkt->enc_part2->authorization_data[0]->contents,
+ tkt->enc_part2->authorization_data[0]->length);
+ }
+#endif
+
+ if ((ret = krb5_unparse_name(context, get_principal_from_tkt(tkt),
+ principal))) {
+ DEBUG(3,("ads_verify_ticket: krb5_unparse_name failed (%s)\n",
+ error_message(ret)));
+ sret = NT_STATUS_LOGON_FAILURE;
+ goto out;
+ }
+
+ sret = NT_STATUS_OK;
+
+ out:
+
+ if (got_replay_mutex)
+ release_server_mutex();
+
+ if (!NT_STATUS_IS_OK(sret))
+ data_blob_free(auth_data);
+
+ if (!NT_STATUS_IS_OK(sret))
+ data_blob_free(ap_rep);
+
+ if (free_host_princ)
+ krb5_free_principal(context, host_princ);
+
+ if (tkt != NULL)
+ krb5_free_ticket(context, tkt);
+ free_kerberos_etypes(context, enctypes);
+ SAFE_FREE(password_s);
+ SAFE_FREE(host_princ_s);
+
+ if (auth_context)
+ krb5_auth_con_free(context, auth_context);
+
+ if (context)
+ krb5_free_context(context);
+
+ return sret;
+}
+
+#endif /* HAVE_KRB5 */
diff --git a/source/libads/krb5_setpw.c b/source/libads/krb5_setpw.c
new file mode 100644
index 00000000000..16d3df83e93
--- /dev/null
+++ b/source/libads/krb5_setpw.c
@@ -0,0 +1,690 @@
+/*
+ Unix SMB/CIFS implementation.
+ krb5 set password implementation
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Remus Koos 2001 (remuskoos@yahoo.com)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#ifdef HAVE_KRB5
+
+#define DEFAULT_KPASSWD_PORT 464
+#define KRB5_KPASSWD_VERS_CHANGEPW 1
+#define KRB5_KPASSWD_VERS_SETPW 2
+#define KRB5_KPASSWD_VERS_SETPW_MS 0xff80
+#define KRB5_KPASSWD_ACCESSDENIED 5
+#define KRB5_KPASSWD_BAD_VERSION 6
+#define KRB5_KPASSWD_INITIAL_FLAG_NEEDED 7
+
+/* Those are defined by kerberos-set-passwd-02.txt and are probably
+ * not supported by M$ implementation */
+#define KRB5_KPASSWD_POLICY_REJECT 8
+#define KRB5_KPASSWD_BAD_PRINCIPAL 9
+#define KRB5_KPASSWD_ETYPE_NOSUPP 10
+
+/* This implements kerberos password change protocol as specified in
+ * kerb-chg-password-02.txt and kerberos-set-passwd-02.txt
+ * as well as microsoft version of the protocol
+ * as specified in kerberos-set-passwd-00.txt
+ */
+static DATA_BLOB encode_krb5_setpw(const char *principal, const char *password)
+{
+ char* princ_part1 = NULL;
+ char* princ_part2 = NULL;
+ char* realm = NULL;
+ char* c;
+ char* princ;
+
+ ASN1_DATA req;
+ DATA_BLOB ret;
+
+
+ princ = strdup(principal);
+
+ if ((c = strchr(princ, '/')) == NULL) {
+ c = princ;
+ } else {
+ *c = '\0';
+ c++;
+ princ_part1 = princ;
+ }
+
+ princ_part2 = c;
+
+ if ((c = strchr(c, '@')) != NULL) {
+ *c = '\0';
+ c++;
+ realm = c;
+ }
+
+ memset(&req, 0, sizeof(req));
+
+ asn1_push_tag(&req, ASN1_SEQUENCE(0));
+ asn1_push_tag(&req, ASN1_CONTEXT(0));
+ asn1_write_OctetString(&req, password, strlen(password));
+ asn1_pop_tag(&req);
+
+ asn1_push_tag(&req, ASN1_CONTEXT(1));
+ asn1_push_tag(&req, ASN1_SEQUENCE(0));
+
+ asn1_push_tag(&req, ASN1_CONTEXT(0));
+ asn1_write_Integer(&req, 1);
+ asn1_pop_tag(&req);
+
+ asn1_push_tag(&req, ASN1_CONTEXT(1));
+ asn1_push_tag(&req, ASN1_SEQUENCE(0));
+
+ if (princ_part1)
+ asn1_write_GeneralString(&req, princ_part1);
+
+ asn1_write_GeneralString(&req, princ_part2);
+ asn1_pop_tag(&req);
+ asn1_pop_tag(&req);
+ asn1_pop_tag(&req);
+ asn1_pop_tag(&req);
+
+ asn1_push_tag(&req, ASN1_CONTEXT(2));
+ asn1_write_GeneralString(&req, realm);
+ asn1_pop_tag(&req);
+ asn1_pop_tag(&req);
+
+ ret = data_blob(req.data, req.length);
+ asn1_free(&req);
+
+ free(princ);
+
+ return ret;
+}
+
+static krb5_error_code build_kpasswd_request(uint16 pversion,
+ krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_data *ap_req,
+ const char *princ,
+ const char *passwd,
+ krb5_data *packet)
+{
+ krb5_error_code ret;
+ krb5_data cipherpw;
+ krb5_data encoded_setpw;
+ krb5_replay_data replay;
+ char *p;
+ DATA_BLOB setpw;
+
+ ret = krb5_auth_con_setflags(context,
+ auth_context,KRB5_AUTH_CONTEXT_DO_SEQUENCE);
+ if (ret) {
+ DEBUG(1,("krb5_auth_con_setflags failed (%s)\n",
+ error_message(ret)));
+ return ret;
+ }
+
+ /* handle protocol differences in chpw and setpw */
+ if (pversion == KRB5_KPASSWD_VERS_CHANGEPW)
+ setpw = data_blob(passwd, strlen(passwd));
+ else if (pversion == KRB5_KPASSWD_VERS_SETPW ||
+ pversion == KRB5_KPASSWD_VERS_SETPW_MS)
+ setpw = encode_krb5_setpw(princ, passwd);
+ else
+ return EINVAL;
+
+ encoded_setpw.data = (char *)setpw.data;
+ encoded_setpw.length = setpw.length;
+
+ ret = krb5_mk_priv(context, auth_context,
+ &encoded_setpw, &cipherpw, &replay);
+
+ data_blob_free(&setpw); /*from 'encode_krb5_setpw(...)' */
+
+ if (ret) {
+ DEBUG(1,("krb5_mk_priv failed (%s)\n", error_message(ret)));
+ return ret;
+ }
+
+ packet->data = (char *)malloc(ap_req->length + cipherpw.length + 6);
+ if (!packet->data)
+ return -1;
+
+ /* see the RFC for details */
+ p = ((char *)packet->data) + 2;
+ RSSVAL(p, 0, pversion);
+ p += 2;
+ RSSVAL(p, 0, ap_req->length);
+ p += 2;
+ memcpy(p, ap_req->data, ap_req->length);
+ p += ap_req->length;
+ memcpy(p, cipherpw.data, cipherpw.length);
+ p += cipherpw.length;
+ packet->length = PTR_DIFF(p,packet->data);
+ RSSVAL(packet->data, 0, packet->length);
+
+ free(cipherpw.data); /* from 'krb5_mk_priv(...)' */
+
+ return 0;
+}
+
+static const struct kpasswd_errors {
+ int result_code;
+ const char *error_string;
+} kpasswd_errors[] = {
+ {KRB5_KPASSWD_MALFORMED, "Malformed request error"},
+ {KRB5_KPASSWD_HARDERROR, "Server error"},
+ {KRB5_KPASSWD_AUTHERROR, "Authentication error"},
+ {KRB5_KPASSWD_SOFTERROR, "Password change rejected"},
+ {KRB5_KPASSWD_ACCESSDENIED, "Client does not have proper authorization"},
+ {KRB5_KPASSWD_BAD_VERSION, "Protocol version not supported"},
+ {KRB5_KPASSWD_INITIAL_FLAG_NEEDED, "Authorization ticket must have initial flag set"},
+ {KRB5_KPASSWD_POLICY_REJECT, "Password rejected due to policy requirements"},
+ {KRB5_KPASSWD_BAD_PRINCIPAL, "Target principal does not exist"},
+ {KRB5_KPASSWD_ETYPE_NOSUPP, "Unsupported encryption type"},
+ {0, NULL}
+};
+
+static krb5_error_code setpw_result_code_string(krb5_context context,
+ int result_code,
+ const char **code_string)
+{
+ unsigned int idx = 0;
+
+ while (kpasswd_errors[idx].error_string != NULL) {
+ if (kpasswd_errors[idx].result_code ==
+ result_code) {
+ *code_string = kpasswd_errors[idx].error_string;
+ return 0;
+ }
+ idx++;
+ }
+ *code_string = "Password change failed";
+ return (0);
+}
+
+static krb5_error_code parse_setpw_reply(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_data *packet)
+{
+ krb5_data ap_rep;
+ char *p;
+ int vnum, ret, res_code;
+ krb5_data cipherresult;
+ krb5_data clearresult;
+ krb5_ap_rep_enc_part *ap_rep_enc;
+ krb5_replay_data replay;
+
+ if (packet->length < 4) {
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ p = packet->data;
+
+ if (((char *)packet->data)[0] == 0x7e || ((char *)packet->data)[0] == 0x5e) {
+ /* it's an error packet. We should parse it ... */
+ DEBUG(1,("Got error packet 0x%x from kpasswd server\n",
+ ((char *)packet->data)[0]));
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ if (RSVAL(p, 0) != packet->length) {
+ DEBUG(1,("Bad packet length (%d/%d) from kpasswd server\n",
+ RSVAL(p, 0), packet->length));
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ p += 2;
+
+ vnum = RSVAL(p, 0); p += 2;
+
+ /* FIXME: According to standard there is only one type of reply */
+ if (vnum != KRB5_KPASSWD_VERS_SETPW &&
+ vnum != KRB5_KPASSWD_VERS_SETPW_MS &&
+ vnum != KRB5_KPASSWD_VERS_CHANGEPW) {
+ DEBUG(1,("Bad vnum (%d) from kpasswd server\n", vnum));
+ return KRB5KDC_ERR_BAD_PVNO;
+ }
+
+ ap_rep.length = RSVAL(p, 0); p += 2;
+
+ if (p + ap_rep.length >= (char *)packet->data + packet->length) {
+ DEBUG(1,("ptr beyond end of packet from kpasswd server\n"));
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ if (ap_rep.length == 0) {
+ DEBUG(1,("got unencrypted setpw result?!\n"));
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ /* verify ap_rep */
+ ap_rep.data = p;
+ p += ap_rep.length;
+
+ ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc);
+ if (ret) {
+ DEBUG(1,("failed to rd setpw reply (%s)\n", error_message(ret)));
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ krb5_free_ap_rep_enc_part(context, ap_rep_enc);
+
+ cipherresult.data = p;
+ cipherresult.length = ((char *)packet->data + packet->length) - p;
+
+ ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult,
+ &replay);
+ if (ret) {
+ DEBUG(1,("failed to decrypt setpw reply (%s)\n", error_message(ret)));
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ if (clearresult.length < 2) {
+ free(clearresult.data);
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ p = clearresult.data;
+
+ res_code = RSVAL(p, 0);
+
+ free(clearresult.data);
+
+ if ((res_code < KRB5_KPASSWD_SUCCESS) ||
+ (res_code > KRB5_KPASSWD_ETYPE_NOSUPP)) {
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ if(res_code == KRB5_KPASSWD_SUCCESS)
+ return 0;
+ else {
+ const char *errstr;
+ setpw_result_code_string(context, res_code, &errstr);
+ DEBUG(1, ("Error changing password: %s\n", errstr));
+
+ switch(res_code) {
+ case KRB5_KPASSWD_ACCESSDENIED:
+ return KRB5KDC_ERR_BADOPTION;
+ break;
+ case KRB5_KPASSWD_INITIAL_FLAG_NEEDED:
+ return KRB5KDC_ERR_BADOPTION;
+ /* return KV5M_ALT_METHOD; MIT-only define */
+ break;
+ case KRB5_KPASSWD_ETYPE_NOSUPP:
+ return KRB5KDC_ERR_ETYPE_NOSUPP;
+ break;
+ case KRB5_KPASSWD_BAD_PRINCIPAL:
+ return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
+ break;
+ case KRB5_KPASSWD_POLICY_REJECT:
+ return KRB5KDC_ERR_POLICY;
+ break;
+ default:
+ return KRB5KRB_ERR_GENERIC;
+ break;
+ }
+ }
+}
+
+static ADS_STATUS do_krb5_kpasswd_request(krb5_context context,
+ const char *kdc_host,
+ uint16 pversion,
+ krb5_creds *credsp,
+ const char *princ,
+ const char *newpw)
+{
+ krb5_auth_context auth_context = NULL;
+ krb5_data ap_req, chpw_req, chpw_rep;
+ int ret, sock, addr_len;
+ struct sockaddr remote_addr, local_addr;
+ krb5_address local_kaddr, remote_kaddr;
+
+ ret = krb5_mk_req_extended(context, &auth_context, AP_OPTS_USE_SUBKEY,
+ NULL, credsp, &ap_req);
+ if (ret) {
+ DEBUG(1,("krb5_mk_req_extended failed (%s)\n", error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ sock = open_udp_socket(kdc_host, DEFAULT_KPASSWD_PORT);
+ if (sock == -1) {
+ int rc = errno;
+ free(ap_req.data);
+ krb5_auth_con_free(context, auth_context);
+ DEBUG(1,("failed to open kpasswd socket to %s (%s)\n",
+ kdc_host, strerror(errno)));
+ return ADS_ERROR_SYSTEM(rc);
+ }
+
+ addr_len = sizeof(remote_addr);
+ getpeername(sock, &remote_addr, &addr_len);
+ addr_len = sizeof(local_addr);
+ getsockname(sock, &local_addr, &addr_len);
+
+ setup_kaddr(&remote_kaddr, &remote_addr);
+ setup_kaddr(&local_kaddr, &local_addr);
+
+ ret = krb5_auth_con_setaddrs(context, auth_context, &local_kaddr, NULL);
+ if (ret) {
+ close(sock);
+ free(ap_req.data);
+ krb5_auth_con_free(context, auth_context);
+ DEBUG(1,("krb5_auth_con_setaddrs failed (%s)\n", error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ ret = build_kpasswd_request(pversion, context, auth_context, &ap_req,
+ princ, newpw, &chpw_req);
+ if (ret) {
+ close(sock);
+ free(ap_req.data);
+ krb5_auth_con_free(context, auth_context);
+ DEBUG(1,("build_setpw_request failed (%s)\n", error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ if (write(sock, chpw_req.data, chpw_req.length) != chpw_req.length) {
+ close(sock);
+ free(chpw_req.data);
+ free(ap_req.data);
+ krb5_auth_con_free(context, auth_context);
+ DEBUG(1,("send of chpw failed (%s)\n", strerror(errno)));
+ return ADS_ERROR_SYSTEM(errno);
+ }
+
+ free(chpw_req.data);
+
+ chpw_rep.length = 1500;
+ chpw_rep.data = (char *) malloc(chpw_rep.length);
+ if (!chpw_rep.data) {
+ close(sock);
+ free(ap_req.data);
+ krb5_auth_con_free(context, auth_context);
+ DEBUG(1,("send of chpw failed (%s)\n", strerror(errno)));
+ errno = ENOMEM;
+ return ADS_ERROR_SYSTEM(errno);
+ }
+
+ ret = read(sock, chpw_rep.data, chpw_rep.length);
+ if (ret < 0) {
+ close(sock);
+ free(chpw_rep.data);
+ free(ap_req.data);
+ krb5_auth_con_free(context, auth_context);
+ DEBUG(1,("recv of chpw reply failed (%s)\n", strerror(errno)));
+ return ADS_ERROR_SYSTEM(errno);
+ }
+
+ close(sock);
+ chpw_rep.length = ret;
+
+ ret = krb5_auth_con_setaddrs(context, auth_context, NULL,&remote_kaddr);
+ if (ret) {
+ free(chpw_rep.data);
+ free(ap_req.data);
+ krb5_auth_con_free(context, auth_context);
+ DEBUG(1,("krb5_auth_con_setaddrs on reply failed (%s)\n",
+ error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ ret = parse_setpw_reply(context, auth_context, &chpw_rep);
+ free(chpw_rep.data);
+
+ if (ret) {
+ free(ap_req.data);
+ krb5_auth_con_free(context, auth_context);
+ DEBUG(1,("parse_setpw_reply failed (%s)\n",
+ error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ free(ap_req.data);
+ krb5_auth_con_free(context, auth_context);
+
+ return ADS_SUCCESS;
+}
+
+ADS_STATUS ads_krb5_set_password(const char *kdc_host, const char *princ,
+ const char *newpw, int time_offset)
+{
+
+ ADS_STATUS aret;
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_principal principal;
+ char *princ_name;
+ char *realm;
+ krb5_creds creds, *credsp;
+ krb5_ccache ccache;
+
+ ret = krb5_init_context(&context);
+ if (ret) {
+ DEBUG(1,("Failed to init krb5 context (%s)\n", error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ if (time_offset != 0) {
+ krb5_set_real_time(context, time(NULL) + time_offset, 0);
+ }
+
+ ret = krb5_cc_default(context, &ccache);
+ if (ret) {
+ krb5_free_context(context);
+ DEBUG(1,("Failed to get default creds (%s)\n", error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ ZERO_STRUCT(creds);
+
+ realm = strchr(princ, '@');
+ realm++;
+
+ asprintf(&princ_name, "kadmin/changepw@%s", realm);
+ ret = krb5_parse_name(context, princ_name, &creds.server);
+ if (ret) {
+ krb5_free_context(context);
+ DEBUG(1,("Failed to parse kadmin/changepw (%s)\n", error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+ free(princ_name);
+
+ /* parse the principal we got as a function argument */
+ ret = krb5_parse_name(context, princ, &principal);
+ if (ret) {
+ krb5_free_context(context);
+ DEBUG(1,("Failed to parse %s (%s)\n", princ_name, error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ krb5_princ_set_realm(context, creds.server,
+ krb5_princ_realm(context, principal));
+
+ ret = krb5_cc_get_principal(context, ccache, &creds.client);
+ if (ret) {
+ krb5_free_principal(context, principal);
+ krb5_free_context(context);
+ DEBUG(1,("Failed to get principal from ccache (%s)\n",
+ error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ ret = krb5_get_credentials(context, 0, ccache, &creds, &credsp);
+ if (ret) {
+ krb5_free_principal(context, creds.client);
+ krb5_free_principal(context, principal);
+ krb5_free_context(context);
+ DEBUG(1,("krb5_get_credentials failed (%s)\n", error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ /* we might have to call krb5_free_creds(...) from now on ... */
+
+ aret = do_krb5_kpasswd_request(context, kdc_host,
+ KRB5_KPASSWD_VERS_SETPW_MS,
+ credsp, princ, newpw);
+
+ krb5_free_creds(context, credsp);
+ krb5_free_principal(context, creds.client);
+ krb5_free_principal(context, principal);
+ krb5_free_context(context);
+
+ return aret;
+}
+
+/*
+ we use a prompter to avoid a crash bug in the kerberos libs when
+ dealing with empty passwords
+ this prompter is just a string copy ...
+*/
+static krb5_error_code
+kerb_prompter(krb5_context ctx, void *data,
+ const char *name,
+ const char *banner,
+ int num_prompts,
+ krb5_prompt prompts[])
+{
+ if (num_prompts == 0) return 0;
+
+ memset(prompts[0].reply->data, 0, prompts[0].reply->length);
+ if (prompts[0].reply->length > 0) {
+ if (data) {
+ strncpy(prompts[0].reply->data, data, prompts[0].reply->length-1);
+ prompts[0].reply->length = strlen(prompts[0].reply->data);
+ } else {
+ prompts[0].reply->length = 0;
+ }
+ }
+ return 0;
+}
+
+static ADS_STATUS ads_krb5_chg_password(const char *kdc_host,
+ const char *principal,
+ const char *oldpw,
+ const char *newpw,
+ int time_offset)
+{
+ ADS_STATUS aret;
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_principal princ;
+ krb5_get_init_creds_opt opts;
+ krb5_creds creds;
+ char *chpw_princ = NULL, *password;
+
+ ret = krb5_init_context(&context);
+ if (ret) {
+ DEBUG(1,("Failed to init krb5 context (%s)\n", error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ if ((ret = krb5_parse_name(context, principal,
+ &princ))) {
+ krb5_free_context(context);
+ DEBUG(1,("Failed to parse %s (%s)\n", principal, error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ krb5_get_init_creds_opt_init(&opts);
+ krb5_get_init_creds_opt_set_tkt_life(&opts, 5*60);
+ krb5_get_init_creds_opt_set_renew_life(&opts, 0);
+ krb5_get_init_creds_opt_set_forwardable(&opts, 0);
+ krb5_get_init_creds_opt_set_proxiable(&opts, 0);
+
+ /* We have to obtain an INITIAL changepw ticket for changing password */
+ asprintf(&chpw_princ, "kadmin/changepw@%s",
+ (char *) krb5_princ_realm(context, princ));
+ password = strdup(oldpw);
+ ret = krb5_get_init_creds_password(context, &creds, princ, password,
+ kerb_prompter, NULL,
+ 0, chpw_princ, &opts);
+ SAFE_FREE(chpw_princ);
+ SAFE_FREE(password);
+
+ if (ret) {
+ if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY)
+ DEBUG(1,("Password incorrect while getting initial ticket"));
+ else
+ DEBUG(1,("krb5_get_init_creds_password failed (%s)\n", error_message(ret)));
+
+ krb5_free_principal(context, princ);
+ krb5_free_context(context);
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ aret = do_krb5_kpasswd_request(context, kdc_host,
+ KRB5_KPASSWD_VERS_CHANGEPW,
+ &creds, principal, newpw);
+
+ krb5_free_principal(context, princ);
+ krb5_free_context(context);
+
+ return aret;
+}
+
+
+ADS_STATUS kerberos_set_password(const char *kpasswd_server,
+ const char *auth_principal, const char *auth_password,
+ const char *target_principal, const char *new_password,
+ int time_offset)
+{
+ int ret;
+
+ if ((ret = kerberos_kinit_password(auth_principal, auth_password, time_offset, NULL))) {
+ DEBUG(1,("Failed kinit for principal %s (%s)\n", auth_principal, error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ if (!strcmp(auth_principal, target_principal))
+ return ads_krb5_chg_password(kpasswd_server, target_principal,
+ auth_password, new_password, time_offset);
+ else
+ return ads_krb5_set_password(kpasswd_server, target_principal,
+ new_password, time_offset);
+}
+
+
+/**
+ * Set the machine account password
+ * @param ads connection to ads server
+ * @param hostname machine whose password is being set
+ * @param password new password
+ * @return status of password change
+ **/
+ADS_STATUS ads_set_machine_password(ADS_STRUCT *ads,
+ const char *machine_account,
+ const char *password)
+{
+ ADS_STATUS status;
+ char *principal = NULL;
+
+ /*
+ we need to use the '$' form of the name here (the machine account name),
+ as otherwise the server might end up setting the password for a user
+ instead
+ */
+ asprintf(&principal, "%s@%s", machine_account, ads->config.realm);
+
+ status = ads_krb5_set_password(ads->auth.kdc_server, principal,
+ password, ads->auth.time_offset);
+
+ free(principal);
+
+ return status;
+}
+
+
+
+#endif
diff --git a/source/libads/ldap.c b/source/libads/ldap.c
new file mode 100644
index 00000000000..20a36dfdf5c
--- /dev/null
+++ b/source/libads/ldap.c
@@ -0,0 +1,2135 @@
+/*
+ Unix SMB/CIFS implementation.
+ ads (active directory) utility library
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Remus Koos 2001
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#ifdef HAVE_LDAP
+
+/**
+ * @file ldap.c
+ * @brief basic ldap client-side routines for ads server communications
+ *
+ * The routines contained here should do the necessary ldap calls for
+ * ads setups.
+ *
+ * Important note: attribute names passed into ads_ routines must
+ * already be in UTF-8 format. We do not convert them because in almost
+ * all cases, they are just ascii (which is represented with the same
+ * codepoints in UTF-8). This may have to change at some point
+ **/
+
+
+/*
+ try a connection to a given ldap server, returning True and setting the servers IP
+ in the ads struct if successful
+
+ TODO : add a negative connection cache in here leveraged off of the one
+ found in the rpc code. --jerry
+ */
+static BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port)
+{
+ char *srv;
+
+ if (!server || !*server) {
+ return False;
+ }
+
+ DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server, port));
+
+ /* this copes with inet_ntoa brokenness */
+ srv = strdup(server);
+
+ ads->ld = ldap_open(srv, port);
+ if (!ads->ld) {
+ free(srv);
+ return False;
+ }
+ ads->ldap_port = port;
+ ads->ldap_ip = *interpret_addr2(srv);
+ free(srv);
+
+ return True;
+}
+
+/*
+ try a connection to a given ldap server, based on URL, returning True if successful
+ */
+static BOOL ads_try_connect_uri(ADS_STRUCT *ads)
+{
+#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
+ DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n",
+ ads->server.ldap_uri));
+
+
+ if (ldap_initialize((LDAP**)&(ads->ld), ads->server.ldap_uri) == LDAP_SUCCESS) {
+ return True;
+ }
+ DEBUG(0, ("ldap_initialize: %s\n", strerror(errno)));
+
+#else
+
+ DEBUG(1, ("no URL support in LDAP libs!\n"));
+#endif
+
+ return False;
+}
+
+/**********************************************************************
+ Try to find an AD dc using our internal name resolution routines
+ Try the realm first and then then workgroup name if netbios is not
+ disabled
+**********************************************************************/
+
+static BOOL ads_find_dc(ADS_STRUCT *ads)
+{
+ const char *c_realm;
+ int count, i=0;
+ struct ip_service *ip_list;
+ pstring realm;
+ BOOL got_realm = False;
+ BOOL use_own_domain = False;
+
+ /* if the realm and workgroup are both empty, assume they are ours */
+
+ /* realm */
+ c_realm = ads->server.realm;
+
+ if ( !c_realm || !*c_realm ) {
+ /* special case where no realm and no workgroup means our own */
+ if ( !ads->server.workgroup || !*ads->server.workgroup ) {
+ use_own_domain = True;
+ c_realm = lp_realm();
+ }
+ }
+
+ if (c_realm && *c_realm)
+ got_realm = True;
+
+again:
+ /* we need to try once with the realm name and fallback to the
+ netbios domain name if we fail (if netbios has not been disabled */
+
+ if ( !got_realm && !lp_disable_netbios() ) {
+ c_realm = ads->server.workgroup;
+ if (!c_realm || !*c_realm) {
+ if ( use_own_domain )
+ c_realm = lp_workgroup();
+ }
+
+ if ( !c_realm || !*c_realm ) {
+ DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
+ return False;
+ }
+ }
+
+ pstrcpy( realm, c_realm );
+
+ DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
+ (got_realm ? "realm" : "domain"), realm));
+
+ if ( !get_sorted_dc_list(realm, &ip_list, &count, got_realm) ) {
+ /* fall back to netbios if we can */
+ if ( got_realm && !lp_disable_netbios() ) {
+ got_realm = False;
+ goto again;
+ }
+
+ return False;
+ }
+
+ /* if we fail this loop, then giveup since all the IP addresses returned were dead */
+ for ( i=0; i<count; i++ ) {
+ /* since this is an ads conection request, default to LDAP_PORT is not set */
+ int port = (ip_list[i].port!=PORT_NONE) ? ip_list[i].port : LDAP_PORT;
+ fstring server;
+
+ fstrcpy( server, inet_ntoa(ip_list[i].ip) );
+
+ if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
+ continue;
+
+ if ( ads_try_connect(ads, server, port) ) {
+ SAFE_FREE(ip_list);
+ return True;
+ }
+
+ /* keep track of failures */
+ add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
+ }
+
+ SAFE_FREE(ip_list);
+
+ return False;
+}
+
+
+/**
+ * Connect to the LDAP server
+ * @param ads Pointer to an existing ADS_STRUCT
+ * @return status of connection
+ **/
+ADS_STATUS ads_connect(ADS_STRUCT *ads)
+{
+ int version = LDAP_VERSION3;
+ ADS_STATUS status;
+
+ ads->last_attempt = time(NULL);
+ ads->ld = NULL;
+
+ /* try with a URL based server */
+
+ if (ads->server.ldap_uri &&
+ ads_try_connect_uri(ads)) {
+ goto got_connection;
+ }
+
+ /* try with a user specified server */
+ if (ads->server.ldap_server &&
+ ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
+ goto got_connection;
+ }
+
+ if (ads_find_dc(ads)) {
+ goto got_connection;
+ }
+
+ return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
+
+got_connection:
+ DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
+
+ status = ads_server_info(ads);
+ if (!ADS_ERR_OK(status)) {
+ DEBUG(1,("Failed to get ldap server info\n"));
+ return status;
+ }
+
+ ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
+
+ if (!ads->auth.user_name) {
+ /* by default use the machine account */
+ fstring myname;
+ fstrcpy(myname, global_myname());
+ strlower_m(myname);
+ asprintf(&ads->auth.user_name, "HOST/%s", myname);
+ }
+
+ if (!ads->auth.realm) {
+ ads->auth.realm = strdup(ads->config.realm);
+ }
+
+ if (!ads->auth.kdc_server) {
+ ads->auth.kdc_server = strdup(inet_ntoa(ads->ldap_ip));
+ }
+
+#if KRB5_DNS_HACK
+ /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
+ to MIT kerberos to work (tridge) */
+ {
+ char *env;
+ asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
+ setenv(env, ads->auth.kdc_server, 1);
+ free(env);
+ }
+#endif
+
+ if (ads->auth.flags & ADS_AUTH_NO_BIND) {
+ return ADS_SUCCESS;
+ }
+
+ if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
+ return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
+ }
+
+ if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
+ return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
+ }
+
+ return ads_sasl_bind(ads);
+}
+
+/*
+ Duplicate a struct berval into talloc'ed memory
+ */
+static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
+{
+ struct berval *value;
+
+ if (!in_val) return NULL;
+
+ value = talloc_zero(ctx, sizeof(struct berval));
+ if (value == NULL)
+ return NULL;
+ if (in_val->bv_len == 0) return value;
+
+ value->bv_len = in_val->bv_len;
+ value->bv_val = talloc_memdup(ctx, in_val->bv_val, in_val->bv_len);
+ return value;
+}
+
+/*
+ Make a values list out of an array of (struct berval *)
+ */
+static struct berval **ads_dup_values(TALLOC_CTX *ctx,
+ const struct berval **in_vals)
+{
+ struct berval **values;
+ int i;
+
+ if (!in_vals) return NULL;
+ for (i=0; in_vals[i]; i++); /* count values */
+ values = (struct berval **) talloc_zero(ctx,
+ (i+1)*sizeof(struct berval *));
+ if (!values) return NULL;
+
+ for (i=0; in_vals[i]; i++) {
+ values[i] = dup_berval(ctx, in_vals[i]);
+ }
+ return values;
+}
+
+/*
+ UTF8-encode a values list out of an array of (char *)
+ */
+static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
+{
+ char **values;
+ int i;
+
+ if (!in_vals) return NULL;
+ for (i=0; in_vals[i]; i++); /* count values */
+ values = (char ** ) talloc_zero(ctx, (i+1)*sizeof(char *));
+ if (!values) return NULL;
+
+ for (i=0; in_vals[i]; i++) {
+ push_utf8_talloc(ctx, &values[i], in_vals[i]);
+ }
+ return values;
+}
+
+/*
+ Pull a (char *) array out of a UTF8-encoded values list
+ */
+static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
+{
+ char **values;
+ int i;
+
+ if (!in_vals) return NULL;
+ for (i=0; in_vals[i]; i++); /* count values */
+ values = (char **) talloc_zero(ctx, (i+1)*sizeof(char *));
+ if (!values) return NULL;
+
+ for (i=0; in_vals[i]; i++) {
+ pull_utf8_talloc(ctx, &values[i], in_vals[i]);
+ }
+ return values;
+}
+
+/**
+ * Do a search with paged results. cookie must be null on the first
+ * call, and then returned on each subsequent call. It will be null
+ * again when the entire search is complete
+ * @param ads connection to ads server
+ * @param bind_path Base dn for the search
+ * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
+ * @param expr Search expression - specified in local charset
+ * @param attrs Attributes to retrieve - specified in utf8 or ascii
+ * @param res ** which will contain results - free res* with ads_msgfree()
+ * @param count Number of entries retrieved on this page
+ * @param cookie The paged results cookie to be returned on subsequent calls
+ * @return status of search
+ **/
+ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
+ int scope, const char *expr,
+ const char **attrs, void **res,
+ int *count, void **cookie)
+{
+ int rc, i, version;
+ char *utf8_expr, *utf8_path, **search_attrs;
+ LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
+ BerElement *cookie_be = NULL;
+ struct berval *cookie_bv= NULL;
+ TALLOC_CTX *ctx;
+
+ *res = NULL;
+
+ if (!(ctx = talloc_init("ads_do_paged_search")))
+ return ADS_ERROR(LDAP_NO_MEMORY);
+
+ /* 0 means the conversion worked but the result was empty
+ so we only fail if it's -1. In any case, it always
+ at least nulls out the dest */
+ if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
+ (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
+ rc = LDAP_NO_MEMORY;
+ goto done;
+ }
+
+ if (!attrs || !(*attrs))
+ search_attrs = NULL;
+ else {
+ /* This would be the utf8-encoded version...*/
+ /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
+ if (!(str_list_copy(&search_attrs, attrs))) {
+ rc = LDAP_NO_MEMORY;
+ goto done;
+ }
+ }
+
+
+ /* Paged results only available on ldap v3 or later */
+ ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
+ if (version < LDAP_VERSION3) {
+ rc = LDAP_NOT_SUPPORTED;
+ goto done;
+ }
+
+ cookie_be = ber_alloc_t(LBER_USE_DER);
+ if (cookie && *cookie) {
+ ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
+ ber_bvfree(*cookie); /* don't need it from last time */
+ *cookie = NULL;
+ } else {
+ ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
+ }
+ ber_flatten(cookie_be, &cookie_bv);
+ PagedResults.ldctl_oid = ADS_PAGE_CTL_OID;
+ PagedResults.ldctl_iscritical = (char) 1;
+ PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
+ PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
+
+ NoReferrals.ldctl_oid = ADS_NO_REFERRALS_OID;
+ NoReferrals.ldctl_iscritical = (char) 0;
+ NoReferrals.ldctl_value.bv_len = 0;
+ NoReferrals.ldctl_value.bv_val = "";
+
+
+ controls[0] = &NoReferrals;
+ controls[1] = &PagedResults;
+ controls[2] = NULL;
+
+ *res = NULL;
+
+ /* we need to disable referrals as the openldap libs don't
+ handle them and paged results at the same time. Using them
+ together results in the result record containing the server
+ page control being removed from the result list (tridge/jmcd)
+
+ leaving this in despite the control that says don't generate
+ referrals, in case the server doesn't support it (jmcd)
+ */
+ ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
+
+ rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_expr,
+ search_attrs, 0, controls,
+ NULL, NULL, LDAP_NO_LIMIT, (LDAPMessage **)res);
+
+ ber_free(cookie_be, 1);
+ ber_bvfree(cookie_bv);
+
+ if (rc) {
+ DEBUG(3,("ldap_search_ext_s(%s) -> %s\n", expr, ldap_err2string(rc)));
+ goto done;
+ }
+
+ rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
+ NULL, &rcontrols, 0);
+
+ if (!rcontrols) {
+ goto done;
+ }
+
+ for (i=0; rcontrols[i]; i++) {
+ if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
+ cookie_be = ber_init(&rcontrols[i]->ldctl_value);
+ ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
+ &cookie_bv);
+ /* the berval is the cookie, but must be freed when
+ it is all done */
+ if (cookie_bv->bv_len) /* still more to do */
+ *cookie=ber_bvdup(cookie_bv);
+ else
+ *cookie=NULL;
+ ber_bvfree(cookie_bv);
+ ber_free(cookie_be, 1);
+ break;
+ }
+ }
+ ldap_controls_free(rcontrols);
+
+done:
+ talloc_destroy(ctx);
+ /* if/when we decide to utf8-encode attrs, take out this next line */
+ str_list_free(&search_attrs);
+
+ return ADS_ERROR(rc);
+}
+
+
+/**
+ * Get all results for a search. This uses ads_do_paged_search() to return
+ * all entries in a large search.
+ * @param ads connection to ads server
+ * @param bind_path Base dn for the search
+ * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
+ * @param expr Search expression
+ * @param attrs Attributes to retrieve
+ * @param res ** which will contain results - free res* with ads_msgfree()
+ * @return status of search
+ **/
+ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
+ int scope, const char *expr,
+ const char **attrs, void **res)
+{
+ void *cookie = NULL;
+ int count = 0;
+ ADS_STATUS status;
+
+ status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, res,
+ &count, &cookie);
+
+ if (!ADS_ERR_OK(status)) return status;
+
+ while (cookie) {
+ void *res2 = NULL;
+ ADS_STATUS status2;
+ LDAPMessage *msg, *next;
+
+ status2 = ads_do_paged_search(ads, bind_path, scope, expr,
+ attrs, &res2, &count, &cookie);
+
+ if (!ADS_ERR_OK(status2)) break;
+
+ /* this relies on the way that ldap_add_result_entry() works internally. I hope
+ that this works on all ldap libs, but I have only tested with openldap */
+ for (msg = ads_first_entry(ads, res2); msg; msg = next) {
+ next = ads_next_entry(ads, msg);
+ ldap_add_result_entry((LDAPMessage **)res, msg);
+ }
+ /* note that we do not free res2, as the memory is now
+ part of the main returned list */
+ }
+
+ return status;
+}
+
+/**
+ * Run a function on all results for a search. Uses ads_do_paged_search() and
+ * runs the function as each page is returned, using ads_process_results()
+ * @param ads connection to ads server
+ * @param bind_path Base dn for the search
+ * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
+ * @param expr Search expression - specified in local charset
+ * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
+ * @param fn Function which takes attr name, values list, and data_area
+ * @param data_area Pointer which is passed to function on each call
+ * @return status of search
+ **/
+ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
+ int scope, const char *expr, const char **attrs,
+ BOOL(*fn)(char *, void **, void *),
+ void *data_area)
+{
+ void *cookie = NULL;
+ int count = 0;
+ ADS_STATUS status;
+ void *res;
+
+ status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
+ &count, &cookie);
+
+ if (!ADS_ERR_OK(status)) return status;
+
+ ads_process_results(ads, res, fn, data_area);
+ ads_msgfree(ads, res);
+
+ while (cookie) {
+ status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
+ &res, &count, &cookie);
+
+ if (!ADS_ERR_OK(status)) break;
+
+ ads_process_results(ads, res, fn, data_area);
+ ads_msgfree(ads, res);
+ }
+
+ return status;
+}
+
+/**
+ * Do a search with a timeout.
+ * @param ads connection to ads server
+ * @param bind_path Base dn for the search
+ * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
+ * @param expr Search expression
+ * @param attrs Attributes to retrieve
+ * @param res ** which will contain results - free res* with ads_msgfree()
+ * @return status of search
+ **/
+ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
+ const char *expr,
+ const char **attrs, void **res)
+{
+ struct timeval timeout;
+ int rc;
+ char *utf8_expr, *utf8_path, **search_attrs = NULL;
+ TALLOC_CTX *ctx;
+
+ if (!(ctx = talloc_init("ads_do_search"))) {
+ DEBUG(1,("ads_do_search: talloc_init() failed!"));
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ }
+
+ /* 0 means the conversion worked but the result was empty
+ so we only fail if it's negative. In any case, it always
+ at least nulls out the dest */
+ if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
+ (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
+ DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
+ rc = LDAP_NO_MEMORY;
+ goto done;
+ }
+
+ if (!attrs || !(*attrs))
+ search_attrs = NULL;
+ else {
+ /* This would be the utf8-encoded version...*/
+ /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
+ if (!(str_list_copy(&search_attrs, attrs)))
+ {
+ DEBUG(1,("ads_do_search: str_list_copy() failed!"));
+ rc = LDAP_NO_MEMORY;
+ goto done;
+ }
+ }
+
+ timeout.tv_sec = ADS_SEARCH_TIMEOUT;
+ timeout.tv_usec = 0;
+ *res = NULL;
+
+ /* see the note in ads_do_paged_search - we *must* disable referrals */
+ ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
+
+ rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_expr,
+ search_attrs, 0, NULL, NULL,
+ &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
+
+ if (rc == LDAP_SIZELIMIT_EXCEEDED) {
+ DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
+ rc = 0;
+ }
+
+ done:
+ talloc_destroy(ctx);
+ /* if/when we decide to utf8-encode attrs, take out this next line */
+ str_list_free(&search_attrs);
+ return ADS_ERROR(rc);
+}
+/**
+ * Do a general ADS search
+ * @param ads connection to ads server
+ * @param res ** which will contain results - free res* with ads_msgfree()
+ * @param expr Search expression
+ * @param attrs Attributes to retrieve
+ * @return status of search
+ **/
+ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
+ const char *expr,
+ const char **attrs)
+{
+ return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
+ expr, attrs, res);
+}
+
+/**
+ * Do a search on a specific DistinguishedName
+ * @param ads connection to ads server
+ * @param res ** which will contain results - free res* with ads_msgfree()
+ * @param dn DistinguishName to search
+ * @param attrs Attributes to retrieve
+ * @return status of search
+ **/
+ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
+ const char *dn,
+ const char **attrs)
+{
+ return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
+}
+
+/**
+ * Free up memory from a ads_search
+ * @param ads connection to ads server
+ * @param msg Search results to free
+ **/
+void ads_msgfree(ADS_STRUCT *ads, void *msg)
+{
+ if (!msg) return;
+ ldap_msgfree(msg);
+}
+
+/**
+ * Free up memory from various ads requests
+ * @param ads connection to ads server
+ * @param mem Area to free
+ **/
+void ads_memfree(ADS_STRUCT *ads, void *mem)
+{
+ SAFE_FREE(mem);
+}
+
+/**
+ * Get a dn from search results
+ * @param ads connection to ads server
+ * @param msg Search result
+ * @return dn string
+ **/
+char *ads_get_dn(ADS_STRUCT *ads, void *msg)
+{
+ char *utf8_dn, *unix_dn;
+
+ utf8_dn = ldap_get_dn(ads->ld, msg);
+
+ if (!utf8_dn) {
+ DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
+ return NULL;
+ }
+
+ if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
+ DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
+ utf8_dn ));
+ return NULL;
+ }
+ ldap_memfree(utf8_dn);
+ return unix_dn;
+}
+
+/**
+ * Find a machine account given a hostname
+ * @param ads connection to ads server
+ * @param res ** which will contain results - free res* with ads_msgfree()
+ * @param host Hostname to search for
+ * @return status of search
+ **/
+ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
+{
+ ADS_STATUS status;
+ char *expr;
+ const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
+
+ /* the easiest way to find a machine account anywhere in the tree
+ is to look for hostname$ */
+ if (asprintf(&expr, "(samAccountName=%s$)", host) == -1) {
+ DEBUG(1, ("asprintf failed!\n"));
+ return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ status = ads_search(ads, res, expr, attrs);
+ free(expr);
+ return status;
+}
+
+/**
+ * Initialize a list of mods to be used in a modify request
+ * @param ctx An initialized TALLOC_CTX
+ * @return allocated ADS_MODLIST
+ **/
+ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
+{
+#define ADS_MODLIST_ALLOC_SIZE 10
+ LDAPMod **mods;
+
+ if ((mods = (LDAPMod **) talloc_zero(ctx, sizeof(LDAPMod *) *
+ (ADS_MODLIST_ALLOC_SIZE + 1))))
+ /* -1 is safety to make sure we don't go over the end.
+ need to reset it to NULL before doing ldap modify */
+ mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
+
+ return mods;
+}
+
+
+/*
+ add an attribute to the list, with values list already constructed
+*/
+static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
+ int mod_op, const char *name,
+ const void **invals)
+{
+ int curmod;
+ LDAPMod **modlist = (LDAPMod **) *mods;
+ struct berval **ber_values = NULL;
+ char **char_values = NULL;
+
+ if (!invals) {
+ mod_op = LDAP_MOD_DELETE;
+ } else {
+ if (mod_op & LDAP_MOD_BVALUES)
+ ber_values = ads_dup_values(ctx,
+ (const struct berval **)invals);
+ else
+ char_values = ads_push_strvals(ctx,
+ (const char **) invals);
+ }
+
+ /* find the first empty slot */
+ for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
+ curmod++);
+ if (modlist[curmod] == (LDAPMod *) -1) {
+ if (!(modlist = talloc_realloc(ctx, modlist,
+ (curmod+ADS_MODLIST_ALLOC_SIZE+1)*sizeof(LDAPMod *))))
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ memset(&modlist[curmod], 0,
+ ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
+ modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
+ *mods = modlist;
+ }
+
+ if (!(modlist[curmod] = talloc_zero(ctx, sizeof(LDAPMod))))
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ modlist[curmod]->mod_type = talloc_strdup(ctx, name);
+ if (mod_op & LDAP_MOD_BVALUES) {
+ modlist[curmod]->mod_bvalues = ber_values;
+ } else if (mod_op & LDAP_MOD_DELETE) {
+ modlist[curmod]->mod_values = NULL;
+ } else {
+ modlist[curmod]->mod_values = char_values;
+ }
+
+ modlist[curmod]->mod_op = mod_op;
+ return ADS_ERROR(LDAP_SUCCESS);
+}
+
+/**
+ * Add a single string value to a mod list
+ * @param ctx An initialized TALLOC_CTX
+ * @param mods An initialized ADS_MODLIST
+ * @param name The attribute name to add
+ * @param val The value to add - NULL means DELETE
+ * @return ADS STATUS indicating success of add
+ **/
+ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
+ const char *name, const char *val)
+{
+ const char *values[2];
+
+ values[0] = val;
+ values[1] = NULL;
+
+ if (!val)
+ return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
+ return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
+ (const void **) values);
+}
+
+/**
+ * Add an array of string values to a mod list
+ * @param ctx An initialized TALLOC_CTX
+ * @param mods An initialized ADS_MODLIST
+ * @param name The attribute name to add
+ * @param vals The array of string values to add - NULL means DELETE
+ * @return ADS STATUS indicating success of add
+ **/
+ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
+ const char *name, const char **vals)
+{
+ if (!vals)
+ return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
+ return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
+ name, (const void **) vals);
+}
+
+/**
+ * Add a single ber-encoded value to a mod list
+ * @param ctx An initialized TALLOC_CTX
+ * @param mods An initialized ADS_MODLIST
+ * @param name The attribute name to add
+ * @param val The value to add - NULL means DELETE
+ * @return ADS STATUS indicating success of add
+ **/
+static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
+ const char *name, const struct berval *val)
+{
+ const struct berval *values[2];
+
+ values[0] = val;
+ values[1] = NULL;
+ if (!val)
+ return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
+ return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
+ name, (const void **) values);
+}
+
+/**
+ * Perform an ldap modify
+ * @param ads connection to ads server
+ * @param mod_dn DistinguishedName to modify
+ * @param mods list of modifications to perform
+ * @return status of modify
+ **/
+ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
+{
+ int ret,i;
+ char *utf8_dn = NULL;
+ /*
+ this control is needed to modify that contains a currently
+ non-existent attribute (but allowable for the object) to run
+ */
+ LDAPControl PermitModify = {
+ ADS_PERMIT_MODIFY_OID,
+ {0, NULL},
+ (char) 1};
+ LDAPControl *controls[2];
+
+ controls[0] = &PermitModify;
+ controls[1] = NULL;
+
+ if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
+ return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ /* find the end of the list, marked by NULL or -1 */
+ for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
+ /* make sure the end of the list is NULL */
+ mods[i] = NULL;
+ ret = ldap_modify_ext_s(ads->ld, utf8_dn,
+ (LDAPMod **) mods, controls, NULL);
+ SAFE_FREE(utf8_dn);
+ return ADS_ERROR(ret);
+}
+
+/**
+ * Perform an ldap add
+ * @param ads connection to ads server
+ * @param new_dn DistinguishedName to add
+ * @param mods list of attributes and values for DN
+ * @return status of add
+ **/
+ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
+{
+ int ret, i;
+ char *utf8_dn = NULL;
+
+ if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
+ DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
+ return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ /* find the end of the list, marked by NULL or -1 */
+ for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
+ /* make sure the end of the list is NULL */
+ mods[i] = NULL;
+
+ ret = ldap_add_s(ads->ld, utf8_dn, mods);
+ SAFE_FREE(utf8_dn);
+ return ADS_ERROR(ret);
+}
+
+/**
+ * Delete a DistinguishedName
+ * @param ads connection to ads server
+ * @param new_dn DistinguishedName to delete
+ * @return status of delete
+ **/
+ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
+{
+ int ret;
+ char *utf8_dn = NULL;
+ if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
+ DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
+ return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ ret = ldap_delete_s(ads->ld, utf8_dn);
+ return ADS_ERROR(ret);
+}
+
+/**
+ * Build an org unit string
+ * if org unit is Computers or blank then assume a container, otherwise
+ * assume a \ separated list of organisational units
+ * @param org_unit Organizational unit
+ * @return org unit string - caller must free
+ **/
+char *ads_ou_string(const char *org_unit)
+{
+ if (!org_unit || !*org_unit || strequal(org_unit, "Computers")) {
+ return strdup("cn=Computers");
+ }
+
+ return ads_build_path(org_unit, "\\/", "ou=", 1);
+}
+
+
+
+/*
+ add a machine account to the ADS server
+*/
+static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname,
+ uint32 account_type,
+ const char *org_unit)
+{
+ ADS_STATUS ret, status;
+ char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
+ char *ou_str;
+ TALLOC_CTX *ctx;
+ ADS_MODLIST mods;
+ const char *objectClass[] = {"top", "person", "organizationalPerson",
+ "user", "computer", NULL};
+ const char *servicePrincipalName[5] = {NULL, NULL, NULL, NULL, NULL};
+ char *psp, *psp2;
+ unsigned acct_control;
+ unsigned exists=0;
+ LDAPMessage *res;
+
+ status = ads_find_machine_acct(ads, (void **)&res, hostname);
+ if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
+ DEBUG(0, ("Host account for %s already exists - modifying old account\n", hostname));
+ exists=1;
+ }
+
+ if (!(ctx = talloc_init("machine_account")))
+ return ADS_ERROR(LDAP_NO_MEMORY);
+
+ ret = ADS_ERROR(LDAP_NO_MEMORY);
+
+ if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
+ goto done;
+ if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
+ goto done;
+ ou_str = ads_ou_string(org_unit);
+ if (!ou_str) {
+ DEBUG(1, ("ads_ou_string returned NULL (malloc failure?)\n"));
+ goto done;
+ }
+ new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str,
+ ads->config.bind_path);
+ servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", hostname);
+ psp = talloc_asprintf(ctx, "HOST/%s.%s",
+ hostname,
+ ads->config.realm);
+ strlower_m(&psp[5]);
+ servicePrincipalName[1] = psp;
+ servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", hostname);
+ psp2 = talloc_asprintf(ctx, "CIFS/%s.%s",
+ hostname,
+ ads->config.realm);
+ strlower_m(&psp2[5]);
+ servicePrincipalName[3] = psp2;
+
+ free(ou_str);
+ if (!new_dn)
+ goto done;
+
+ if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
+ goto done;
+
+ acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
+#ifndef ENCTYPE_ARCFOUR_HMAC
+ acct_control |= UF_USE_DES_KEY_ONLY;
+#endif
+
+ if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control)))
+ goto done;
+
+ if (!(mods = ads_init_mods(ctx)))
+ goto done;
+
+ if (!exists) {
+ ads_mod_str(ctx, &mods, "cn", hostname);
+ ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
+ ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
+ ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
+ }
+ ads_mod_str(ctx, &mods, "dNSHostName", hostname);
+ ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
+ ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
+ ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
+ ads_mod_str(ctx, &mods, "operatingSystemVersion", SAMBA_VERSION_STRING);
+
+ if (!exists)
+ ret = ads_gen_add(ads, new_dn, mods);
+ else
+ ret = ads_gen_mod(ads, new_dn, mods);
+
+ if (!ADS_ERR_OK(ret))
+ goto done;
+
+ /* Do not fail if we can't set security descriptor
+ * it shouldn't be mandatory and probably we just
+ * don't have enough rights to do it.
+ */
+ if (!exists) {
+ status = ads_set_machine_sd(ads, hostname, new_dn);
+
+ if (!ADS_ERR_OK(status)) {
+ DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
+ ads_errstr(status)));
+ }
+ }
+done:
+ talloc_destroy(ctx);
+ return ret;
+}
+
+/*
+ dump a binary result from ldap
+*/
+static void dump_binary(const char *field, struct berval **values)
+{
+ int i, j;
+ for (i=0; values[i]; i++) {
+ printf("%s: ", field);
+ for (j=0; j<values[i]->bv_len; j++) {
+ printf("%02X", (unsigned char)values[i]->bv_val[j]);
+ }
+ printf("\n");
+ }
+}
+
+static void dump_guid(const char *field, struct berval **values)
+{
+ int i;
+ UUID_FLAT guid;
+ for (i=0; values[i]; i++) {
+ memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
+ printf("%s: %s\n", field,
+ smb_uuid_string_static(smb_uuid_unpack_static(guid)));
+ }
+}
+
+/*
+ dump a sid result from ldap
+*/
+static void dump_sid(const char *field, struct berval **values)
+{
+ int i;
+ for (i=0; values[i]; i++) {
+ DOM_SID sid;
+ sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
+ printf("%s: %s\n", field, sid_string_static(&sid));
+ }
+}
+
+/*
+ dump ntSecurityDescriptor
+*/
+static void dump_sd(const char *filed, struct berval **values)
+{
+ prs_struct ps;
+
+ SEC_DESC *psd = 0;
+ TALLOC_CTX *ctx = 0;
+
+ if (!(ctx = talloc_init("sec_io_desc")))
+ return;
+
+ /* prepare data */
+ prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
+ prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
+ prs_set_offset(&ps,0);
+
+ /* parse secdesc */
+ if (!sec_io_desc("sd", &psd, &ps, 1)) {
+ prs_mem_free(&ps);
+ talloc_destroy(ctx);
+ return;
+ }
+ if (psd) ads_disp_sd(psd);
+
+ prs_mem_free(&ps);
+ talloc_destroy(ctx);
+}
+
+/*
+ dump a string result from ldap
+*/
+static void dump_string(const char *field, char **values)
+{
+ int i;
+ for (i=0; values[i]; i++) {
+ printf("%s: %s\n", field, values[i]);
+ }
+}
+
+/*
+ dump a field from LDAP on stdout
+ used for debugging
+*/
+
+static BOOL ads_dump_field(char *field, void **values, void *data_area)
+{
+ const struct {
+ const char *name;
+ BOOL string;
+ void (*handler)(const char *, struct berval **);
+ } handlers[] = {
+ {"objectGUID", False, dump_guid},
+ {"nTSecurityDescriptor", False, dump_sd},
+ {"dnsRecord", False, dump_binary},
+ {"objectSid", False, dump_sid},
+ {"tokenGroups", False, dump_sid},
+ {NULL, True, NULL}
+ };
+ int i;
+
+ if (!field) { /* must be end of an entry */
+ printf("\n");
+ return False;
+ }
+
+ for (i=0; handlers[i].name; i++) {
+ if (StrCaseCmp(handlers[i].name, field) == 0) {
+ if (!values) /* first time, indicate string or not */
+ return handlers[i].string;
+ handlers[i].handler(field, (struct berval **) values);
+ break;
+ }
+ }
+ if (!handlers[i].name) {
+ if (!values) /* first time, indicate string conversion */
+ return True;
+ dump_string(field, (char **)values);
+ }
+ return False;
+}
+
+/**
+ * Dump a result from LDAP on stdout
+ * used for debugging
+ * @param ads connection to ads server
+ * @param res Results to dump
+ **/
+
+void ads_dump(ADS_STRUCT *ads, void *res)
+{
+ ads_process_results(ads, res, ads_dump_field, NULL);
+}
+
+/**
+ * Walk through results, calling a function for each entry found.
+ * The function receives a field name, a berval * array of values,
+ * and a data area passed through from the start. The function is
+ * called once with null for field and values at the end of each
+ * entry.
+ * @param ads connection to ads server
+ * @param res Results to process
+ * @param fn Function for processing each result
+ * @param data_area user-defined area to pass to function
+ **/
+void ads_process_results(ADS_STRUCT *ads, void *res,
+ BOOL(*fn)(char *, void **, void *),
+ void *data_area)
+{
+ void *msg;
+ TALLOC_CTX *ctx;
+
+ if (!(ctx = talloc_init("ads_process_results")))
+ return;
+
+ for (msg = ads_first_entry(ads, res); msg;
+ msg = ads_next_entry(ads, msg)) {
+ char *utf8_field;
+ BerElement *b;
+
+ for (utf8_field=ldap_first_attribute(ads->ld,
+ (LDAPMessage *)msg,&b);
+ utf8_field;
+ utf8_field=ldap_next_attribute(ads->ld,
+ (LDAPMessage *)msg,b)) {
+ struct berval **ber_vals;
+ char **str_vals, **utf8_vals;
+ char *field;
+ BOOL string;
+
+ pull_utf8_talloc(ctx, &field, utf8_field);
+ string = fn(field, NULL, data_area);
+
+ if (string) {
+ utf8_vals = ldap_get_values(ads->ld,
+ (LDAPMessage *)msg, field);
+ str_vals = ads_pull_strvals(ctx,
+ (const char **) utf8_vals);
+ fn(field, (void **) str_vals, data_area);
+ ldap_value_free(utf8_vals);
+ } else {
+ ber_vals = ldap_get_values_len(ads->ld,
+ (LDAPMessage *)msg, field);
+ fn(field, (void **) ber_vals, data_area);
+
+ ldap_value_free_len(ber_vals);
+ }
+ ldap_memfree(utf8_field);
+ }
+ ber_free(b, 0);
+ talloc_destroy_pool(ctx);
+ fn(NULL, NULL, data_area); /* completed an entry */
+
+ }
+ talloc_destroy(ctx);
+}
+
+/**
+ * count how many replies are in a LDAPMessage
+ * @param ads connection to ads server
+ * @param res Results to count
+ * @return number of replies
+ **/
+int ads_count_replies(ADS_STRUCT *ads, void *res)
+{
+ return ldap_count_entries(ads->ld, (LDAPMessage *)res);
+}
+
+/**
+ * Join a machine to a realm
+ * Creates the machine account and sets the machine password
+ * @param ads connection to ads server
+ * @param hostname name of host to add
+ * @param org_unit Organizational unit to place machine in
+ * @return status of join
+ **/
+ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname,
+ uint32 account_type, const char *org_unit)
+{
+ ADS_STATUS status;
+ LDAPMessage *res;
+ char *host;
+
+ /* hostname must be lowercase */
+ host = strdup(hostname);
+ strlower_m(host);
+
+ /*
+ status = ads_find_machine_acct(ads, (void **)&res, host);
+ if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
+ DEBUG(0, ("Host account for %s already exists - deleting old account\n", host));
+ status = ads_leave_realm(ads, host);
+ if (!ADS_ERR_OK(status)) {
+ DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
+ host, ads->config.realm));
+ return status;
+ }
+ }
+ */
+
+ status = ads_add_machine_acct(ads, host, account_type, org_unit);
+ if (!ADS_ERR_OK(status)) {
+ DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
+ return status;
+ }
+
+ status = ads_find_machine_acct(ads, (void **)&res, host);
+ if (!ADS_ERR_OK(status)) {
+ DEBUG(0, ("Host account test failed\n"));
+ return status;
+ }
+
+ free(host);
+
+ return status;
+}
+
+/**
+ * Delete a machine from the realm
+ * @param ads connection to ads server
+ * @param hostname Machine to remove
+ * @return status of delete
+ **/
+ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
+{
+ ADS_STATUS status;
+ void *res, *msg;
+ char *hostnameDN, *host;
+ int rc;
+
+ /* hostname must be lowercase */
+ host = strdup(hostname);
+ strlower_m(host);
+
+ status = ads_find_machine_acct(ads, &res, host);
+ if (!ADS_ERR_OK(status)) {
+ DEBUG(0, ("Host account for %s does not exist.\n", host));
+ return status;
+ }
+
+ msg = ads_first_entry(ads, res);
+ if (!msg) {
+ return ADS_ERROR_SYSTEM(ENOENT);
+ }
+
+ hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
+ rc = ldap_delete_s(ads->ld, hostnameDN);
+ ads_memfree(ads, hostnameDN);
+ if (rc != LDAP_SUCCESS) {
+ return ADS_ERROR(rc);
+ }
+
+ status = ads_find_machine_acct(ads, &res, host);
+ if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
+ DEBUG(0, ("Failed to remove host account.\n"));
+ return status;
+ }
+
+ free(host);
+
+ return status;
+}
+
+/**
+ * add machine account to existing security descriptor
+ * @param ads connection to ads server
+ * @param hostname machine to add
+ * @param dn DN of security descriptor
+ * @return status
+ **/
+ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
+{
+ const char *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
+ char *expr = 0;
+ size_t sd_size = 0;
+ struct berval bval = {0, NULL};
+ prs_struct ps_wire;
+ char *escaped_hostname = escape_ldap_string_alloc(hostname);
+
+ LDAPMessage *res = 0;
+ LDAPMessage *msg = 0;
+ ADS_MODLIST mods = 0;
+
+ NTSTATUS status;
+ ADS_STATUS ret;
+ DOM_SID sid;
+ SEC_DESC *psd = NULL;
+ TALLOC_CTX *ctx = NULL;
+
+ /* Avoid segmentation fault in prs_mem_free if
+ * we have to bail out before prs_init */
+ ps_wire.is_dynamic = False;
+
+ if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
+
+ ret = ADS_ERROR(LDAP_SUCCESS);
+
+ if (!escaped_hostname) {
+ return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
+ DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
+ SAFE_FREE(escaped_hostname);
+ return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ SAFE_FREE(escaped_hostname);
+
+ ret = ads_search(ads, (void *) &res, expr, attrs);
+
+ if (!ADS_ERR_OK(ret)) return ret;
+
+ if ( !(msg = ads_first_entry(ads, res) )) {
+ ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+ goto ads_set_sd_error;
+ }
+
+ if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
+ ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ goto ads_set_sd_error;
+ }
+
+ if (!(ctx = talloc_init("sec_io_desc"))) {
+ ret = ADS_ERROR(LDAP_NO_MEMORY);
+ goto ads_set_sd_error;
+ }
+
+ if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
+ ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ goto ads_set_sd_error;
+ }
+
+ status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = ADS_ERROR_NT(status);
+ goto ads_set_sd_error;
+ }
+
+ if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
+ ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
+ ret = ADS_ERROR(LDAP_NO_MEMORY);
+ goto ads_set_sd_error;
+ }
+
+#if 0
+ file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
+#endif
+ if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
+
+ bval.bv_len = prs_offset(&ps_wire);
+ bval.bv_val = talloc(ctx, bval.bv_len);
+ if (!bval.bv_val) {
+ ret = ADS_ERROR(LDAP_NO_MEMORY);
+ goto ads_set_sd_error;
+ }
+
+ prs_set_offset(&ps_wire, 0);
+
+ if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
+ ret = ADS_ERROR(LDAP_NO_MEMORY);
+ goto ads_set_sd_error;
+ }
+
+ ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
+ if (ADS_ERR_OK(ret)) {
+ ret = ads_gen_mod(ads, dn, mods);
+ }
+
+ads_set_sd_error:
+ ads_msgfree(ads, res);
+ prs_mem_free(&ps_wire);
+ talloc_destroy(ctx);
+ return ret;
+}
+
+/**
+ * pull the first entry from a ADS result
+ * @param ads connection to ads server
+ * @param res Results of search
+ * @return first entry from result
+ **/
+void *ads_first_entry(ADS_STRUCT *ads, void *res)
+{
+ return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
+}
+
+/**
+ * pull the next entry from a ADS result
+ * @param ads connection to ads server
+ * @param res Results of search
+ * @return next entry from result
+ **/
+void *ads_next_entry(ADS_STRUCT *ads, void *res)
+{
+ return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
+}
+
+/**
+ * pull a single string from a ADS result
+ * @param ads connection to ads server
+ * @param mem_ctx TALLOC_CTX to use for allocating result string
+ * @param msg Results of search
+ * @param field Attribute to retrieve
+ * @return Result string in talloc context
+ **/
+char *ads_pull_string(ADS_STRUCT *ads,
+ TALLOC_CTX *mem_ctx, void *msg, const char *field)
+{
+ char **values;
+ char *ret = NULL;
+ char *ux_string;
+ size_t rc;
+
+ values = ldap_get_values(ads->ld, msg, field);
+ if (!values)
+ return NULL;
+
+ if (values[0]) {
+ rc = pull_utf8_talloc(mem_ctx, &ux_string,
+ values[0]);
+ if (rc != (size_t)-1)
+ ret = ux_string;
+
+ }
+ ldap_value_free(values);
+ return ret;
+}
+
+/**
+ * pull an array of strings from a ADS result
+ * @param ads connection to ads server
+ * @param mem_ctx TALLOC_CTX to use for allocating result string
+ * @param msg Results of search
+ * @param field Attribute to retrieve
+ * @return Result strings in talloc context
+ **/
+char **ads_pull_strings(ADS_STRUCT *ads,
+ TALLOC_CTX *mem_ctx, void *msg, const char *field,
+ size_t *num_values)
+{
+ char **values;
+ char **ret = NULL;
+ int i;
+
+ values = ldap_get_values(ads->ld, msg, field);
+ if (!values)
+ return NULL;
+
+ *num_values = ldap_count_values(values);
+
+ ret = talloc(mem_ctx, sizeof(char *) * (*num_values+1));
+ if (!ret) {
+ ldap_value_free(values);
+ return NULL;
+ }
+
+ for (i=0;i<*num_values;i++) {
+ if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
+ ldap_value_free(values);
+ return NULL;
+ }
+ }
+ ret[i] = NULL;
+
+ ldap_value_free(values);
+ return ret;
+}
+
+/**
+ * pull an array of strings from a ADS result
+ * (handle large multivalue attributes with range retrieval)
+ * @param ads connection to ads server
+ * @param mem_ctx TALLOC_CTX to use for allocating result string
+ * @param msg Results of search
+ * @param field Attribute to retrieve
+ * @param current_strings strings returned by a previous call to this function
+ * @param next_attribute The next query should ask for this attribute
+ * @param num_values How many values did we get this time?
+ * @param more_values Are there more values to get?
+ * @return Result strings in talloc context
+ **/
+char **ads_pull_strings_range(ADS_STRUCT *ads,
+ TALLOC_CTX *mem_ctx,
+ void *msg, const char *field,
+ char **current_strings,
+ const char **next_attribute,
+ size_t *num_strings,
+ BOOL *more_strings)
+{
+ char *attr;
+ char *expected_range_attrib, *range_attr;
+ BerElement *ptr = NULL;
+ char **strings;
+ char **new_strings;
+ size_t num_new_strings;
+ unsigned long int range_start;
+ unsigned long int range_end;
+
+ /* we might have been given the whole lot anyway */
+ if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
+ *more_strings = False;
+ return strings;
+ }
+
+ expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
+
+ /* look for Range result */
+ for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
+ attr;
+ attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
+ /* we ignore the fact that this is utf8, as all attributes are ascii... */
+ if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
+ range_attr = attr;
+ break;
+ }
+ ldap_memfree(attr);
+ }
+ if (!attr) {
+ ber_free(ptr, 0);
+ /* nothing here - this field is just empty */
+ *more_strings = False;
+ return NULL;
+ }
+
+ if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
+ &range_start, &range_end) == 2) {
+ *more_strings = True;
+ } else {
+ if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
+ &range_start) == 1) {
+ *more_strings = False;
+ } else {
+ DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
+ range_attr));
+ ldap_memfree(range_attr);
+ *more_strings = False;
+ return NULL;
+ }
+ }
+
+ if ((*num_strings) != range_start) {
+ DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
+ " - aborting range retreival\n",
+ range_attr, *num_strings + 1, range_start));
+ ldap_memfree(range_attr);
+ *more_strings = False;
+ return NULL;
+ }
+
+ new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
+
+ if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
+ DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
+ "strings in this bunch, but we only got %lu - aborting range retreival\n",
+ range_attr, (unsigned long int)range_end - range_start + 1,
+ (unsigned long int)num_new_strings));
+ ldap_memfree(range_attr);
+ *more_strings = False;
+ return NULL;
+ }
+
+ strings = talloc_realloc(mem_ctx, current_strings,
+ sizeof(*current_strings) *
+ (*num_strings + num_new_strings));
+
+ if (strings == NULL) {
+ ldap_memfree(range_attr);
+ *more_strings = False;
+ return NULL;
+ }
+
+ memcpy(&strings[*num_strings], new_strings,
+ sizeof(*new_strings) * num_new_strings);
+
+ (*num_strings) += num_new_strings;
+
+ if (*more_strings) {
+ *next_attribute = talloc_asprintf(mem_ctx,
+ "%s;range=%d-*",
+ field,
+ *num_strings);
+
+ if (!*next_attribute) {
+ DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
+ ldap_memfree(range_attr);
+ *more_strings = False;
+ return NULL;
+ }
+ }
+
+ ldap_memfree(range_attr);
+
+ return strings;
+}
+
+/**
+ * pull a single uint32 from a ADS result
+ * @param ads connection to ads server
+ * @param msg Results of search
+ * @param field Attribute to retrieve
+ * @param v Pointer to int to store result
+ * @return boolean inidicating success
+*/
+BOOL ads_pull_uint32(ADS_STRUCT *ads,
+ void *msg, const char *field, uint32 *v)
+{
+ char **values;
+
+ values = ldap_get_values(ads->ld, msg, field);
+ if (!values)
+ return False;
+ if (!values[0]) {
+ ldap_value_free(values);
+ return False;
+ }
+
+ *v = atoi(values[0]);
+ ldap_value_free(values);
+ return True;
+}
+
+/**
+ * pull a single objectGUID from an ADS result
+ * @param ads connection to ADS server
+ * @param msg results of search
+ * @param guid 37-byte area to receive text guid
+ * @return boolean indicating success
+ **/
+BOOL ads_pull_guid(ADS_STRUCT *ads,
+ void *msg, struct uuid *guid)
+{
+ char **values;
+ UUID_FLAT flat_guid;
+
+ values = ldap_get_values(ads->ld, msg, "objectGUID");
+ if (!values)
+ return False;
+
+ if (values[0]) {
+ memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
+ smb_uuid_unpack(flat_guid, guid);
+ ldap_value_free(values);
+ return True;
+ }
+ ldap_value_free(values);
+ return False;
+
+}
+
+
+/**
+ * pull a single DOM_SID from a ADS result
+ * @param ads connection to ads server
+ * @param msg Results of search
+ * @param field Attribute to retrieve
+ * @param sid Pointer to sid to store result
+ * @return boolean inidicating success
+*/
+BOOL ads_pull_sid(ADS_STRUCT *ads,
+ void *msg, const char *field, DOM_SID *sid)
+{
+ struct berval **values;
+ BOOL ret = False;
+
+ values = ldap_get_values_len(ads->ld, msg, field);
+
+ if (!values)
+ return False;
+
+ if (values[0])
+ ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
+
+ ldap_value_free_len(values);
+ return ret;
+}
+
+/**
+ * pull an array of DOM_SIDs from a ADS result
+ * @param ads connection to ads server
+ * @param mem_ctx TALLOC_CTX for allocating sid array
+ * @param msg Results of search
+ * @param field Attribute to retrieve
+ * @param sids pointer to sid array to allocate
+ * @return the count of SIDs pulled
+ **/
+int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
+ void *msg, const char *field, DOM_SID **sids)
+{
+ struct berval **values;
+ BOOL ret;
+ int count, i;
+
+ values = ldap_get_values_len(ads->ld, msg, field);
+
+ if (!values)
+ return 0;
+
+ for (i=0; values[i]; i++)
+ /* nop */ ;
+
+ (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
+ if (!(*sids)) {
+ ldap_value_free_len(values);
+ return 0;
+ }
+
+ count = 0;
+ for (i=0; values[i]; i++) {
+ ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
+ if (ret) {
+ fstring sid;
+ DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
+ count++;
+ }
+ }
+
+ ldap_value_free_len(values);
+ return count;
+}
+
+/**
+ * pull a SEC_DESC from a ADS result
+ * @param ads connection to ads server
+ * @param mem_ctx TALLOC_CTX for allocating sid array
+ * @param msg Results of search
+ * @param field Attribute to retrieve
+ * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
+ * @return boolean inidicating success
+*/
+BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
+ void *msg, const char *field, SEC_DESC **sd)
+{
+ struct berval **values;
+ prs_struct ps;
+ BOOL ret = False;
+
+ values = ldap_get_values_len(ads->ld, msg, field);
+
+ if (!values) return False;
+
+ if (values[0]) {
+ prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
+ prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
+ prs_set_offset(&ps,0);
+
+ ret = sec_io_desc("sd", sd, &ps, 1);
+ }
+
+ ldap_value_free_len(values);
+ return ret;
+}
+
+/*
+ * in order to support usernames longer than 21 characters we need to
+ * use both the sAMAccountName and the userPrincipalName attributes
+ * It seems that not all users have the userPrincipalName attribute set
+ *
+ * @param ads connection to ads server
+ * @param mem_ctx TALLOC_CTX for allocating sid array
+ * @param msg Results of search
+ * @return the username
+ */
+char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
+{
+ char *ret, *p;
+
+ ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
+ if (ret && (p = strchr(ret, '@'))) {
+ *p = 0;
+ return ret;
+ }
+ return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
+}
+
+
+/**
+ * find the update serial number - this is the core of the ldap cache
+ * @param ads connection to ads server
+ * @param ads connection to ADS server
+ * @param usn Pointer to retrieved update serial number
+ * @return status of search
+ **/
+ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
+{
+ const char *attrs[] = {"highestCommittedUSN", NULL};
+ ADS_STATUS status;
+ void *res;
+
+ status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
+ if (!ADS_ERR_OK(status))
+ return status;
+
+ if (ads_count_replies(ads, res) != 1) {
+ return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+ }
+
+ ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
+ ads_msgfree(ads, res);
+ return ADS_SUCCESS;
+}
+
+/* parse a ADS timestring - typical string is
+ '20020917091222.0Z0' which means 09:12.22 17th September
+ 2002, timezone 0 */
+static time_t ads_parse_time(const char *str)
+{
+ struct tm tm;
+
+ ZERO_STRUCT(tm);
+
+ if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
+ &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
+ &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
+ return 0;
+ }
+ tm.tm_year -= 1900;
+ tm.tm_mon -= 1;
+
+ return timegm(&tm);
+}
+
+
+/**
+ * Find the servers name and realm - this can be done before authentication
+ * The ldapServiceName field on w2k looks like this:
+ * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
+ * @param ads connection to ads server
+ * @return status of search
+ **/
+ADS_STATUS ads_server_info(ADS_STRUCT *ads)
+{
+ const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
+ ADS_STATUS status;
+ void *res;
+ char *value;
+ char *p;
+ char *timestr;
+ TALLOC_CTX *ctx;
+
+ if (!(ctx = talloc_init("ads_server_info"))) {
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ }
+
+ status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
+ if (!ADS_ERR_OK(status)) return status;
+
+ value = ads_pull_string(ads, ctx, res, "ldapServiceName");
+ if (!value) {
+ return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+ }
+
+ timestr = ads_pull_string(ads, ctx, res, "currentTime");
+ if (!timestr) {
+ return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+ }
+
+ ldap_msgfree(res);
+
+ p = strchr(value, ':');
+ if (!p) {
+ talloc_destroy(ctx);
+ DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' "
+ "so was deemed invalid\n"));
+ return ADS_ERROR(LDAP_DECODING_ERROR);
+ }
+
+ SAFE_FREE(ads->config.ldap_server_name);
+
+ ads->config.ldap_server_name = strdup(p+1);
+ p = strchr(ads->config.ldap_server_name, '$');
+ if (!p || p[1] != '@') {
+ talloc_destroy(ctx);
+ DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@'"
+ " so was deemed invalid\n", ads->config.ldap_server_name));
+ SAFE_FREE(ads->config.ldap_server_name);
+ return ADS_ERROR(LDAP_DECODING_ERROR);
+ }
+
+ *p = 0;
+
+ SAFE_FREE(ads->config.realm);
+ SAFE_FREE(ads->config.bind_path);
+
+ ads->config.realm = strdup(p+2);
+ ads->config.bind_path = ads_build_dn(ads->config.realm);
+
+ DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
+ ads->config.ldap_server_name, ads->config.realm,
+ ads->config.bind_path));
+
+ ads->config.current_time = ads_parse_time(timestr);
+
+ if (ads->config.current_time != 0) {
+ ads->auth.time_offset = ads->config.current_time - time(NULL);
+ DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
+ }
+
+ talloc_destroy(ctx);
+
+ return ADS_SUCCESS;
+}
+
+/**
+ * find the domain sid for our domain
+ * @param ads connection to ads server
+ * @param sid Pointer to domain sid
+ * @return status of search
+ **/
+ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
+{
+ const char *attrs[] = {"objectSid", NULL};
+ void *res;
+ ADS_STATUS rc;
+
+ rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
+ attrs, &res);
+ if (!ADS_ERR_OK(rc)) return rc;
+ if (!ads_pull_sid(ads, res, "objectSid", sid)) {
+ return ADS_ERROR_SYSTEM(ENOENT);
+ }
+ ads_msgfree(ads, res);
+
+ return ADS_SUCCESS;
+}
+
+/* this is rather complex - we need to find the allternate (netbios) name
+ for the domain, but there isn't a simple query to do this. Instead
+ we look for the principle names on the DCs account and find one that has
+ the right form, then extract the netbios name of the domain from that
+
+ NOTE! better method is this:
+
+bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
+
+but you need to force the bind path to match the configurationNamingContext from the rootDSE
+
+*/
+ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **workgroup)
+{
+ char *expr;
+ ADS_STATUS rc;
+ char **principles;
+ char *prefix;
+ int prefix_length;
+ int i;
+ void *res;
+ const char *attrs[] = {"servicePrincipalName", NULL};
+ int num_principals;
+
+ (*workgroup) = NULL;
+
+ asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))",
+ ads->config.ldap_server_name, ads->config.realm);
+ rc = ads_search(ads, &res, expr, attrs);
+ free(expr);
+
+ if (!ADS_ERR_OK(rc)) {
+ return rc;
+ }
+
+ principles = ads_pull_strings(ads, mem_ctx, res,
+ "servicePrincipalName", &num_principals);
+
+ ads_msgfree(ads, res);
+
+ if (!principles) {
+ return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+ }
+
+ asprintf(&prefix, "HOST/%s.%s/",
+ ads->config.ldap_server_name,
+ ads->config.realm);
+
+ prefix_length = strlen(prefix);
+
+ for (i=0;principles[i]; i++) {
+ if (strnequal(principles[i], prefix, prefix_length) &&
+ !strequal(ads->config.realm, principles[i]+prefix_length) &&
+ !strchr(principles[i]+prefix_length, '.')) {
+ /* found an alternate (short) name for the domain. */
+ DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
+ principles[i]+prefix_length,
+ ads->config.realm));
+ (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
+ break;
+ }
+ }
+ free(prefix);
+
+ if (!*workgroup) {
+ return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+ }
+
+ return ADS_SUCCESS;
+}
+
+#endif
diff --git a/source/libads/ldap_printer.c b/source/libads/ldap_printer.c
new file mode 100644
index 00000000000..1448074ea02
--- /dev/null
+++ b/source/libads/ldap_printer.c
@@ -0,0 +1,354 @@
+/*
+ Unix SMB/CIFS implementation.
+ ads (active directory) printer utility library
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#ifdef HAVE_ADS
+
+/*
+ find a printer given the name and the hostname
+ Note that results "res" may be allocated on return so that the
+ results can be used. It should be freed using ads_msgfree.
+*/
+ADS_STATUS ads_find_printer_on_server(ADS_STRUCT *ads, void **res,
+ const char *printer, const char *servername)
+{
+ ADS_STATUS status;
+ char *srv_dn, **srv_cn, *s;
+ const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
+
+ status = ads_find_machine_acct(ads, res, servername);
+ if (!ADS_ERR_OK(status)) {
+ DEBUG(1, ("ads_add_printer: cannot find host %s in ads\n",
+ servername));
+ return status;
+ }
+ srv_dn = ldap_get_dn(ads->ld, *res);
+ srv_cn = ldap_explode_dn(srv_dn, 1);
+ ads_msgfree(ads, *res);
+
+ asprintf(&s, "(cn=%s-%s)", srv_cn[0], printer);
+ status = ads_search(ads, res, s, attrs);
+
+ ldap_memfree(srv_dn);
+ ldap_value_free(srv_cn);
+ free(s);
+ return status;
+}
+
+ADS_STATUS ads_find_printers(ADS_STRUCT *ads, void **res)
+{
+ char *ldap_expr;
+ const char *attrs[] = { "objectClass", "printerName", "location", "driverName",
+ "serverName", "description", NULL };
+
+ /* For the moment only display all printers */
+
+ ldap_expr = "(&(!(showInAdvancedViewOnly=TRUE))(uncName=*)"
+ "(objectCategory=printQueue))";
+
+ return ads_search(ads, res, ldap_expr, attrs);
+}
+
+/*
+ modify a printer entry in the directory
+*/
+ADS_STATUS ads_mod_printer_entry(ADS_STRUCT *ads, char *prt_dn,
+ TALLOC_CTX *ctx, const ADS_MODLIST *mods)
+{
+ return ads_gen_mod(ads, prt_dn, *mods);
+}
+
+/*
+ add a printer to the directory
+*/
+ADS_STATUS ads_add_printer_entry(ADS_STRUCT *ads, char *prt_dn,
+ TALLOC_CTX *ctx, ADS_MODLIST *mods)
+{
+ ads_mod_str(ctx, mods, "objectClass", "printQueue");
+ return ads_gen_add(ads, prt_dn, *mods);
+}
+
+/*
+ map a REG_SZ to an ldap mod
+*/
+static BOOL map_sz(TALLOC_CTX *ctx, ADS_MODLIST *mods,
+ const REGISTRY_VALUE *value)
+{
+ char *str_value = NULL;
+ ADS_STATUS status;
+
+ if (value->type != REG_SZ)
+ return False;
+
+ if (value->size && *((smb_ucs2_t *) value->data_p)) {
+ pull_ucs2_talloc(ctx, &str_value, (const smb_ucs2_t *) value->data_p);
+ status = ads_mod_str(ctx, mods, value->valuename, str_value);
+ return ADS_ERR_OK(status);
+ }
+ return True;
+
+}
+
+/*
+ map a REG_DWORD to an ldap mod
+*/
+static BOOL map_dword(TALLOC_CTX *ctx, ADS_MODLIST *mods,
+ const REGISTRY_VALUE *value)
+{
+ char *str_value = NULL;
+ ADS_STATUS status;
+
+ if (value->type != REG_DWORD)
+ return False;
+ str_value = talloc_asprintf(ctx, "%d", *((uint32 *) value->data_p));
+ status = ads_mod_str(ctx, mods, value->valuename, str_value);
+ return ADS_ERR_OK(status);
+}
+
+/*
+ map a boolean REG_BINARY to an ldap mod
+*/
+static BOOL map_bool(TALLOC_CTX *ctx, ADS_MODLIST *mods,
+ const REGISTRY_VALUE *value)
+{
+ char *str_value;
+ ADS_STATUS status;
+
+ if ((value->type != REG_BINARY) || (value->size != 1))
+ return False;
+ str_value = talloc_asprintf(ctx, "%s",
+ *(value->data_p) ? "TRUE" : "FALSE");
+ status = ads_mod_str(ctx, mods, value->valuename, str_value);
+ return ADS_ERR_OK(status);
+}
+
+/*
+ map a REG_MULTI_SZ to an ldap mod
+*/
+static BOOL map_multi_sz(TALLOC_CTX *ctx, ADS_MODLIST *mods,
+ const REGISTRY_VALUE *value)
+{
+ char **str_values = NULL;
+ smb_ucs2_t *cur_str = (smb_ucs2_t *) value->data_p;
+ uint32 size = 0, num_vals = 0, i=0;
+ ADS_STATUS status;
+
+ if (value->type != REG_MULTI_SZ)
+ return False;
+
+ while(cur_str && *cur_str && (size < value->size)) {
+ size += 2 * (strlen_w(cur_str) + 1);
+ cur_str += strlen_w(cur_str) + 1;
+ num_vals++;
+ };
+
+ if (num_vals) {
+ str_values = talloc(ctx,
+ (num_vals + 1) * sizeof(smb_ucs2_t *));
+ memset(str_values, '\0',
+ (num_vals + 1) * sizeof(smb_ucs2_t *));
+
+ cur_str = (smb_ucs2_t *) value->data_p;
+ for (i=0; i < num_vals; i++)
+ cur_str += pull_ucs2_talloc(ctx, &str_values[i],
+ cur_str);
+
+ status = ads_mod_strlist(ctx, mods, value->valuename,
+ (const char **) str_values);
+ return ADS_ERR_OK(status);
+ }
+ return True;
+}
+
+struct valmap_to_ads {
+ const char *valname;
+ BOOL (*fn)(TALLOC_CTX *, ADS_MODLIST *, const REGISTRY_VALUE *);
+};
+
+/*
+ map a REG_SZ to an ldap mod
+*/
+static void map_regval_to_ads(TALLOC_CTX *ctx, ADS_MODLIST *mods,
+ REGISTRY_VALUE *value)
+{
+ const struct valmap_to_ads map[] = {
+ {SPOOL_REG_ASSETNUMBER, map_sz},
+ {SPOOL_REG_BYTESPERMINUTE, map_dword},
+ {SPOOL_REG_DEFAULTPRIORITY, map_dword},
+ {SPOOL_REG_DESCRIPTION, map_sz},
+ {SPOOL_REG_DRIVERNAME, map_sz},
+ {SPOOL_REG_DRIVERVERSION, map_dword},
+ {SPOOL_REG_FLAGS, map_dword},
+ {SPOOL_REG_LOCATION, map_sz},
+ {SPOOL_REG_OPERATINGSYSTEM, map_sz},
+ {SPOOL_REG_OPERATINGSYSTEMHOTFIX, map_sz},
+ {SPOOL_REG_OPERATINGSYSTEMSERVICEPACK, map_sz},
+ {SPOOL_REG_OPERATINGSYSTEMVERSION, map_sz},
+ {SPOOL_REG_PORTNAME, map_multi_sz},
+ {SPOOL_REG_PRINTATTRIBUTES, map_dword},
+ {SPOOL_REG_PRINTBINNAMES, map_multi_sz},
+ {SPOOL_REG_PRINTCOLLATE, map_bool},
+ {SPOOL_REG_PRINTCOLOR, map_bool},
+ {SPOOL_REG_PRINTDUPLEXSUPPORTED, map_bool},
+ {SPOOL_REG_PRINTENDTIME, map_dword},
+ {SPOOL_REG_PRINTFORMNAME, map_sz},
+ {SPOOL_REG_PRINTKEEPPRINTEDJOBS, map_bool},
+ {SPOOL_REG_PRINTLANGUAGE, map_multi_sz},
+ {SPOOL_REG_PRINTMACADDRESS, map_sz},
+ {SPOOL_REG_PRINTMAXCOPIES, map_sz},
+ {SPOOL_REG_PRINTMAXRESOLUTIONSUPPORTED, map_dword},
+ {SPOOL_REG_PRINTMAXXEXTENT, map_dword},
+ {SPOOL_REG_PRINTMAXYEXTENT, map_dword},
+ {SPOOL_REG_PRINTMEDIAREADY, map_multi_sz},
+ {SPOOL_REG_PRINTMEDIASUPPORTED, map_multi_sz},
+ {SPOOL_REG_PRINTMEMORY, map_dword},
+ {SPOOL_REG_PRINTMINXEXTENT, map_dword},
+ {SPOOL_REG_PRINTMINYEXTENT, map_dword},
+ {SPOOL_REG_PRINTNETWORKADDRESS, map_sz},
+ {SPOOL_REG_PRINTNOTIFY, map_sz},
+ {SPOOL_REG_PRINTNUMBERUP, map_dword},
+ {SPOOL_REG_PRINTORIENTATIONSSUPPORTED, map_multi_sz},
+ {SPOOL_REG_PRINTOWNER, map_sz},
+ {SPOOL_REG_PRINTPAGESPERMINUTE, map_dword},
+ {SPOOL_REG_PRINTRATE, map_dword},
+ {SPOOL_REG_PRINTRATEUNIT, map_sz},
+ {SPOOL_REG_PRINTSEPARATORFILE, map_sz},
+ {SPOOL_REG_PRINTSHARENAME, map_sz},
+ {SPOOL_REG_PRINTSPOOLING, map_sz},
+ {SPOOL_REG_PRINTSTAPLINGSUPPORTED, map_bool},
+ {SPOOL_REG_PRINTSTARTTIME, map_dword},
+ {SPOOL_REG_PRINTSTATUS, map_sz},
+ {SPOOL_REG_PRIORITY, map_dword},
+ {SPOOL_REG_SERVERNAME, map_sz},
+ {SPOOL_REG_SHORTSERVERNAME, map_sz},
+ {SPOOL_REG_UNCNAME, map_sz},
+ {SPOOL_REG_URL, map_sz},
+ {SPOOL_REG_VERSIONNUMBER, map_dword},
+ {NULL, NULL}
+ };
+ int i;
+
+ for (i=0; map[i].valname; i++) {
+ if (StrCaseCmp(map[i].valname, value->valuename) == 0) {
+ if (!map[i].fn(ctx, mods, value)) {
+ DEBUG(5, ("Add of value %s to modlist failed\n", value->valuename));
+ } else {
+ DEBUG(7, ("Mapped value %s\n", value->valuename));
+ }
+
+ }
+ }
+}
+
+
+WERROR get_remote_printer_publishing_data(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ ADS_MODLIST *mods,
+ const char *printer)
+{
+ WERROR result;
+ char *printername, *servername;
+ REGVAL_CTR dsdriver_ctr, dsspooler_ctr;
+ BOOL got_dsdriver = False, got_dsspooler = False;
+ uint32 needed, i;
+ POLICY_HND pol;
+
+ asprintf(&servername, "\\\\%s", cli->desthost);
+ asprintf(&printername, "%s\\%s", servername, printer);
+ if (!servername || !printername) {
+ DEBUG(3, ("Insufficient memory\n"));
+ return WERR_NOMEM;
+ }
+
+ result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername,
+ "", MAXIMUM_ALLOWED_ACCESS,
+ servername, cli->user_name, &pol);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(3, ("Unable to open printer %s, error is %s.\n",
+ printername, dos_errstr(result)));
+ return result;
+ }
+
+ result = cli_spoolss_enumprinterdataex(cli, mem_ctx, 0, &needed,
+ &pol, SPOOL_DSDRIVER_KEY, NULL);
+
+ if (W_ERROR_V(result) == ERRmoredata)
+ result = cli_spoolss_enumprinterdataex(cli, mem_ctx, needed,
+ NULL, &pol,
+ SPOOL_DSDRIVER_KEY,
+ &dsdriver_ctr);
+
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(3, ("Unable to do enumdataex on %s, error is %s.\n",
+ printername, dos_errstr(result)));
+ } else {
+
+ /* Have the data we need now, so start building */
+ got_dsdriver = True;
+ for (i=0; i < dsdriver_ctr.num_values; i++)
+ map_regval_to_ads(mem_ctx, mods,
+ dsdriver_ctr.values[i]);
+ }
+
+ result = cli_spoolss_enumprinterdataex(cli, mem_ctx, 0, &needed,
+ &pol, SPOOL_DSSPOOLER_KEY,
+ NULL);
+
+ if (W_ERROR_V(result) == ERRmoredata)
+ result = cli_spoolss_enumprinterdataex(cli, mem_ctx, needed,
+ NULL, &pol,
+ SPOOL_DSSPOOLER_KEY,
+ &dsspooler_ctr);
+
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(3, ("Unable to do enumdataex on %s, error is %s.\n",
+ printername, dos_errstr(result)));
+ } else {
+ got_dsspooler = True;
+ for (i=0; i < dsspooler_ctr.num_values; i++)
+ map_regval_to_ads(mem_ctx, mods,
+ dsspooler_ctr.values[i]);
+ }
+
+ ads_mod_str(mem_ctx, mods, SPOOL_REG_PRINTERNAME, printer);
+
+ if (got_dsdriver) regval_ctr_destroy(&dsdriver_ctr);
+ if (got_dsspooler) regval_ctr_destroy(&dsspooler_ctr);
+ cli_spoolss_close_printer(cli, mem_ctx, &pol);
+
+ return result;
+}
+
+BOOL get_local_printer_publishing_data(TALLOC_CTX *mem_ctx,
+ ADS_MODLIST *mods,
+ NT_PRINTER_DATA *data)
+{
+ uint32 key,val;
+
+ for (key=0; key < data->num_keys; key++) {
+ REGVAL_CTR ctr = data->keys[key].values;
+ for (val=0; val < ctr.num_values; val++)
+ map_regval_to_ads(mem_ctx, mods, ctr.values[val]);
+ }
+ return True;
+}
+
+#endif
diff --git a/source/libads/ldap_user.c b/source/libads/ldap_user.c
new file mode 100644
index 00000000000..56a0d8013b2
--- /dev/null
+++ b/source/libads/ldap_user.c
@@ -0,0 +1,119 @@
+/*
+ Unix SMB/CIFS implementation.
+ ads (active directory) utility library
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#ifdef HAVE_ADS
+
+/*
+ find a user account
+*/
+ADS_STATUS ads_find_user_acct(ADS_STRUCT *ads, void **res, const char *user)
+{
+ ADS_STATUS status;
+ char *ldap_exp;
+ const char *attrs[] = {"*", NULL};
+ char *escaped_user = escape_ldap_string_alloc(user);
+ if (!escaped_user) {
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ }
+
+ asprintf(&ldap_exp, "(samAccountName=%s)", escaped_user);
+ status = ads_search(ads, res, ldap_exp, attrs);
+ SAFE_FREE(ldap_exp);
+ SAFE_FREE(escaped_user);
+ return status;
+}
+
+ADS_STATUS ads_add_user_acct(ADS_STRUCT *ads, const char *user,
+ const char *container, const char *fullname)
+{
+ TALLOC_CTX *ctx;
+ ADS_MODLIST mods;
+ ADS_STATUS status;
+ const char *upn, *new_dn, *name, *controlstr;
+ const char *objectClass[] = {"top", "person", "organizationalPerson",
+ "user", NULL};
+
+ if (fullname && *fullname) name = fullname;
+ else name = user;
+
+ if (!(ctx = talloc_init("ads_add_user_acct")))
+ return ADS_ERROR(LDAP_NO_MEMORY);
+
+ status = ADS_ERROR(LDAP_NO_MEMORY);
+
+ if (!(upn = talloc_asprintf(ctx, "%s@%s", user, ads->config.realm)))
+ goto done;
+ if (!(new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", name, container,
+ ads->config.bind_path)))
+ goto done;
+ if (!(controlstr = talloc_asprintf(ctx, "%u", UF_NORMAL_ACCOUNT)))
+ goto done;
+ if (!(mods = ads_init_mods(ctx)))
+ goto done;
+
+ ads_mod_str(ctx, &mods, "cn", name);
+ ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
+ ads_mod_str(ctx, &mods, "userPrincipalName", upn);
+ ads_mod_str(ctx, &mods, "name", name);
+ ads_mod_str(ctx, &mods, "displayName", name);
+ ads_mod_str(ctx, &mods, "sAMAccountName", user);
+ ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
+ status = ads_gen_add(ads, new_dn, mods);
+
+ done:
+ talloc_destroy(ctx);
+ return status;
+}
+
+ADS_STATUS ads_add_group_acct(ADS_STRUCT *ads, const char *group,
+ const char *container, const char *comment)
+{
+ TALLOC_CTX *ctx;
+ ADS_MODLIST mods;
+ ADS_STATUS status;
+ char *new_dn;
+ const char *objectClass[] = {"top", "group", NULL};
+
+ if (!(ctx = talloc_init("ads_add_group_acct")))
+ return ADS_ERROR(LDAP_NO_MEMORY);
+
+ status = ADS_ERROR(LDAP_NO_MEMORY);
+
+ if (!(new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", group, container,
+ ads->config.bind_path)))
+ goto done;
+ if (!(mods = ads_init_mods(ctx)))
+ goto done;
+
+ ads_mod_str(ctx, &mods, "cn", group);
+ ads_mod_strlist(ctx, &mods, "objectClass",objectClass);
+ ads_mod_str(ctx, &mods, "name", group);
+ if (comment && *comment)
+ ads_mod_str(ctx, &mods, "description", comment);
+ ads_mod_str(ctx, &mods, "sAMAccountName", group);
+ status = ads_gen_add(ads, new_dn, mods);
+
+ done:
+ talloc_destroy(ctx);
+ return status;
+}
+#endif
diff --git a/source/libads/ldap_utils.c b/source/libads/ldap_utils.c
new file mode 100644
index 00000000000..991f16c8454
--- /dev/null
+++ b/source/libads/ldap_utils.c
@@ -0,0 +1,108 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Some Helpful wrappers on LDAP
+
+ Copyright (C) Andrew Tridgell 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#ifdef HAVE_LDAP
+/*
+ a wrapper around ldap_search_s that retries depending on the error code
+ this is supposed to catch dropped connections and auto-reconnect
+*/
+ADS_STATUS ads_do_search_retry(ADS_STRUCT *ads, const char *bind_path, int scope,
+ const char *expr,
+ const char **attrs, void **res)
+{
+ ADS_STATUS status;
+ int count = 3;
+ char *bp;
+
+ *res = NULL;
+
+ if (!ads->ld &&
+ time(NULL) - ads->last_attempt < ADS_RECONNECT_TIME) {
+ return ADS_ERROR(LDAP_SERVER_DOWN);
+ }
+
+ bp = strdup(bind_path);
+
+ if (!bp) {
+ return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ while (count--) {
+ *res = NULL;
+ status = ads_do_search_all(ads, bp, scope, expr, attrs, res);
+ if (ADS_ERR_OK(status)) {
+ DEBUG(5,("Search for %s gave %d replies\n",
+ expr, ads_count_replies(ads, *res)));
+ SAFE_FREE(bp);
+ return status;
+ }
+
+ if (*res)
+ ads_msgfree(ads, *res);
+ *res = NULL;
+
+ DEBUG(3,("Reopening ads connection to realm '%s' after error %s\n",
+ ads->config.realm, ads_errstr(status)));
+
+ if (ads->ld) {
+ ldap_unbind(ads->ld);
+ }
+
+ ads->ld = NULL;
+ status = ads_connect(ads);
+
+ if (!ADS_ERR_OK(status)) {
+ DEBUG(1,("ads_search_retry: failed to reconnect (%s)\n",
+ ads_errstr(status)));
+ ads_destroy(&ads);
+ SAFE_FREE(bp);
+ return status;
+ }
+ }
+ SAFE_FREE(bp);
+
+ if (!ADS_ERR_OK(status))
+ DEBUG(1,("ads reopen failed after error %s\n",
+ ads_errstr(status)));
+
+ return status;
+}
+
+
+ADS_STATUS ads_search_retry(ADS_STRUCT *ads, void **res,
+ const char *expr,
+ const char **attrs)
+{
+ return ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
+ expr, attrs, res);
+}
+
+ADS_STATUS ads_search_retry_dn(ADS_STRUCT *ads, void **res,
+ const char *dn,
+ const char **attrs)
+{
+ return ads_do_search_retry(ads, dn, LDAP_SCOPE_BASE,
+ "(objectclass=*)", attrs, res);
+}
+#endif
diff --git a/source/libads/sasl.c b/source/libads/sasl.c
new file mode 100644
index 00000000000..1ab71c6ee51
--- /dev/null
+++ b/source/libads/sasl.c
@@ -0,0 +1,438 @@
+/*
+ Unix SMB/CIFS implementation.
+ ads sasl code
+ Copyright (C) Andrew Tridgell 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#ifdef HAVE_LDAP
+
+/*
+ perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
+ we fit on one socket??)
+*/
+static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
+{
+ const char *mechs[] = {OID_NTLMSSP, NULL};
+ DATA_BLOB msg1;
+ DATA_BLOB blob, chal1, chal2, auth;
+ uint8 challenge[8];
+ uint8 nthash[24], lmhash[24], sess_key[16];
+ uint32 neg_flags;
+ struct berval cred, *scred;
+ ADS_STATUS status;
+ int rc;
+
+ if (!ads->auth.password) {
+ /* No password, don't segfault below... */
+ return ADS_ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ neg_flags = NTLMSSP_NEGOTIATE_UNICODE |
+ NTLMSSP_NEGOTIATE_128 |
+ NTLMSSP_NEGOTIATE_NTLM;
+
+ memset(sess_key, 0, 16);
+
+ /* generate the ntlmssp negotiate packet */
+ msrpc_gen(&blob, "CddB",
+ "NTLMSSP",
+ NTLMSSP_NEGOTIATE,
+ neg_flags,
+ sess_key, 16);
+
+ /* and wrap it in a SPNEGO wrapper */
+ msg1 = gen_negTokenTarg(mechs, blob);
+ data_blob_free(&blob);
+
+ cred.bv_val = (char *)msg1.data;
+ cred.bv_len = msg1.length;
+
+ rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
+ if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
+ status = ADS_ERROR(rc);
+ goto failed;
+ }
+
+ blob = data_blob(scred->bv_val, scred->bv_len);
+
+ /* the server gives us back two challenges */
+ if (!spnego_parse_challenge(blob, &chal1, &chal2)) {
+ DEBUG(3,("Failed to parse challenges\n"));
+ status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
+ goto failed;
+ }
+
+ data_blob_free(&blob);
+
+ /* encrypt the password with the challenge */
+ memcpy(challenge, chal1.data + 24, 8);
+ SMBencrypt(ads->auth.password, challenge,lmhash);
+ SMBNTencrypt(ads->auth.password, challenge,nthash);
+
+ data_blob_free(&chal1);
+ data_blob_free(&chal2);
+
+ /* this generates the actual auth packet */
+ msrpc_gen(&blob, "CdBBUUUBd",
+ "NTLMSSP",
+ NTLMSSP_AUTH,
+ lmhash, 24,
+ nthash, 24,
+ lp_workgroup(),
+ ads->auth.user_name,
+ global_myname(),
+ sess_key, 16,
+ neg_flags);
+
+ /* wrap it in SPNEGO */
+ auth = spnego_gen_auth(blob);
+
+ data_blob_free(&blob);
+
+ /* now send the auth packet and we should be done */
+ cred.bv_val = (char *)auth.data;
+ cred.bv_len = auth.length;
+
+ rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
+
+ return ADS_ERROR(rc);
+
+failed:
+ return status;
+}
+
+/*
+ perform a LDAP/SASL/SPNEGO/KRB5 bind
+*/
+static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal)
+{
+ DATA_BLOB blob;
+ struct berval cred, *scred;
+ DATA_BLOB session_key;
+ int rc;
+
+ rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key);
+
+ if (rc) {
+ return ADS_ERROR_KRB5(rc);
+ }
+
+ /* now send the auth packet and we should be done */
+ cred.bv_val = (char *)blob.data;
+ cred.bv_len = blob.length;
+
+ rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
+
+ data_blob_free(&blob);
+ data_blob_free(&session_key);
+
+ return ADS_ERROR(rc);
+}
+
+/*
+ this performs a SASL/SPNEGO bind
+*/
+static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
+{
+ struct berval *scred=NULL;
+ int rc, i;
+ ADS_STATUS status;
+ DATA_BLOB blob;
+ char *principal;
+ char *OIDs[ASN1_MAX_OIDS];
+ BOOL got_kerberos_mechanism = False;
+
+ rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
+
+ if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
+ status = ADS_ERROR(rc);
+ goto failed;
+ }
+
+ blob = data_blob(scred->bv_val, scred->bv_len);
+
+ ber_bvfree(scred);
+
+#if 0
+ file_save("sasl_spnego.dat", blob.data, blob.length);
+#endif
+
+ /* the server sent us the first part of the SPNEGO exchange in the negprot
+ reply */
+ if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
+ data_blob_free(&blob);
+ status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
+ goto failed;
+ }
+ data_blob_free(&blob);
+
+ /* make sure the server understands kerberos */
+ for (i=0;OIDs[i];i++) {
+ DEBUG(3,("got OID=%s\n", OIDs[i]));
+ if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
+ strcmp(OIDs[i], OID_KERBEROS5) == 0) {
+ got_kerberos_mechanism = True;
+ }
+ free(OIDs[i]);
+ }
+ DEBUG(3,("got principal=%s\n", principal));
+
+#ifdef HAVE_KRB5
+ if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
+ got_kerberos_mechanism) {
+ status = ads_sasl_spnego_krb5_bind(ads, principal);
+ if (ADS_ERR_OK(status))
+ return status;
+
+ status = ADS_ERROR_KRB5(ads_kinit_password(ads));
+
+ if (ADS_ERR_OK(status)) {
+ status = ads_sasl_spnego_krb5_bind(ads, principal);
+ }
+
+ /* only fallback to NTLMSSP if allowed */
+ if (ADS_ERR_OK(status) ||
+ !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
+ return status;
+ }
+ }
+#endif
+
+ /* lets do NTLMSSP ... this has the big advantage that we don't need
+ to sync clocks, and we don't rely on special versions of the krb5
+ library for HMAC_MD4 encryption */
+ return ads_sasl_spnego_ntlmssp_bind(ads);
+
+failed:
+ return status;
+}
+
+#ifdef HAVE_GSSAPI
+#define MAX_GSS_PASSES 3
+
+/* this performs a SASL/gssapi bind
+ we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
+ is very dependent on correctly configured DNS whereas
+ this routine is much less fragile
+ see RFC2078 and RFC2222 for details
+*/
+static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
+{
+ uint32 minor_status;
+ gss_name_t serv_name;
+ gss_buffer_desc input_name;
+ gss_ctx_id_t context_handle;
+ gss_OID mech_type = GSS_C_NULL_OID;
+ gss_buffer_desc output_token, input_token;
+ uint32 ret_flags, conf_state;
+ struct berval cred;
+ struct berval *scred;
+ int i=0;
+ int gss_rc, rc;
+ uint8 *p;
+ uint32 max_msg_size;
+ char *sname;
+ unsigned sec_layer;
+ ADS_STATUS status;
+ krb5_principal principal;
+ krb5_context ctx;
+ krb5_enctype enc_types[] = {
+#ifdef ENCTYPE_ARCFOUR_HMAC
+ ENCTYPE_ARCFOUR_HMAC,
+#endif
+ ENCTYPE_DES_CBC_MD5,
+ ENCTYPE_NULL};
+ gss_OID_desc nt_principal =
+ {10, "\052\206\110\206\367\022\001\002\002\002"};
+
+ /* we need to fetch a service ticket as the ldap user in the
+ servers realm, regardless of our realm */
+ asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
+ krb5_init_context(&ctx);
+ krb5_set_default_tgs_ktypes(ctx, enc_types);
+ krb5_parse_name(ctx, sname, &principal);
+ free(sname);
+ krb5_free_context(ctx);
+
+ input_name.value = &principal;
+ input_name.length = sizeof(principal);
+
+ gss_rc = gss_import_name(&minor_status,&input_name,&nt_principal, &serv_name);
+ if (gss_rc) {
+ return ADS_ERROR_GSS(gss_rc, minor_status);
+ }
+
+ context_handle = GSS_C_NO_CONTEXT;
+
+ input_token.value = NULL;
+ input_token.length = 0;
+
+ for (i=0; i < MAX_GSS_PASSES; i++) {
+ gss_rc = gss_init_sec_context(&minor_status,
+ GSS_C_NO_CREDENTIAL,
+ &context_handle,
+ serv_name,
+ mech_type,
+ GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
+ 0,
+ NULL,
+ &input_token,
+ NULL,
+ &output_token,
+ &ret_flags,
+ NULL);
+
+ if (input_token.value) {
+ gss_release_buffer(&minor_status, &input_token);
+ }
+
+ if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
+ status = ADS_ERROR_GSS(gss_rc, minor_status);
+ goto failed;
+ }
+
+ cred.bv_val = output_token.value;
+ cred.bv_len = output_token.length;
+
+ rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
+ &scred);
+ if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
+ status = ADS_ERROR(rc);
+ goto failed;
+ }
+
+ if (output_token.value) {
+ gss_release_buffer(&minor_status, &output_token);
+ }
+
+ if (scred) {
+ input_token.value = scred->bv_val;
+ input_token.length = scred->bv_len;
+ } else {
+ input_token.value = NULL;
+ input_token.length = 0;
+ }
+
+ if (gss_rc == 0) break;
+ }
+
+ gss_release_name(&minor_status, &serv_name);
+
+ gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
+ (int *)&conf_state,NULL);
+ if (gss_rc) {
+ status = ADS_ERROR_GSS(gss_rc, minor_status);
+ goto failed;
+ }
+
+ gss_release_buffer(&minor_status, &input_token);
+
+ p = (uint8 *)output_token.value;
+
+ file_save("sasl_gssapi.dat", output_token.value, output_token.length);
+
+ max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
+ sec_layer = *p;
+
+ gss_release_buffer(&minor_status, &output_token);
+
+ output_token.value = malloc(strlen(ads->config.bind_path) + 8);
+ p = output_token.value;
+
+ *p++ = 1; /* no sign & seal selection */
+ /* choose the same size as the server gave us */
+ *p++ = max_msg_size>>16;
+ *p++ = max_msg_size>>8;
+ *p++ = max_msg_size;
+ snprintf((char *)p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
+ p += strlen((const char *)p);
+
+ output_token.length = PTR_DIFF(p, output_token.value);
+
+ gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
+ &output_token, (int *)&conf_state,
+ &input_token);
+ if (gss_rc) {
+ status = ADS_ERROR_GSS(gss_rc, minor_status);
+ goto failed;
+ }
+
+ free(output_token.value);
+
+ cred.bv_val = input_token.value;
+ cred.bv_len = input_token.length;
+
+ rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
+ &scred);
+ status = ADS_ERROR(rc);
+
+ gss_release_buffer(&minor_status, &input_token);
+
+failed:
+ return status;
+}
+#endif
+
+/* mapping between SASL mechanisms and functions */
+static struct {
+ const char *name;
+ ADS_STATUS (*fn)(ADS_STRUCT *);
+} sasl_mechanisms[] = {
+ {"GSS-SPNEGO", ads_sasl_spnego_bind},
+#ifdef HAVE_GSSAPI
+ {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
+#endif
+ {NULL, NULL}
+};
+
+ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
+{
+ const char *attrs[] = {"supportedSASLMechanisms", NULL};
+ char **values;
+ ADS_STATUS status;
+ int i, j;
+ void *res;
+
+ /* get a list of supported SASL mechanisms */
+ status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
+ if (!ADS_ERR_OK(status)) return status;
+
+ values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
+
+ /* try our supported mechanisms in order */
+ for (i=0;sasl_mechanisms[i].name;i++) {
+ /* see if the server supports it */
+ for (j=0;values && values[j];j++) {
+ if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
+ DEBUG(4,("Found SASL mechanism %s\n", values[j]));
+ status = sasl_mechanisms[i].fn(ads);
+ ldap_value_free(values);
+ ldap_msgfree(res);
+ return status;
+ }
+ }
+ }
+
+ ldap_value_free(values);
+ ldap_msgfree(res);
+ return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
+}
+
+#endif
+
diff --git a/source/libads/util.c b/source/libads/util.c
new file mode 100644
index 00000000000..9912a7ba831
--- /dev/null
+++ b/source/libads/util.c
@@ -0,0 +1,62 @@
+/*
+ Unix SMB/CIFS implementation.
+ krb5 set password implementation
+ Copyright (C) Remus Koos 2001 (remuskoos@yahoo.com)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#ifdef HAVE_KRB5
+
+ADS_STATUS ads_change_trust_account_password(ADS_STRUCT *ads, char *host_principal)
+{
+ char *tmp_password;
+ char *password;
+ char *new_password;
+ char *service_principal;
+ ADS_STATUS ret;
+ uint32 sec_channel_type;
+
+ if ((password = secrets_fetch_machine_password(lp_workgroup(), NULL, &sec_channel_type)) == NULL) {
+ DEBUG(1,("Failed to retrieve password for principal %s\n", host_principal));
+ return ADS_ERROR_SYSTEM(ENOENT);
+ }
+
+ tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
+ new_password = strdup(tmp_password);
+
+ asprintf(&service_principal, "HOST/%s", host_principal);
+
+ ret = kerberos_set_password(ads->auth.kdc_server, service_principal, password, service_principal, new_password, ads->auth.time_offset);
+
+ if (!ADS_ERR_OK(ret)) goto failed;
+
+ if (!secrets_store_machine_password(new_password, lp_workgroup(), sec_channel_type)) {
+ DEBUG(1,("Failed to save machine password\n"));
+ return ADS_ERROR_SYSTEM(EACCES);
+ }
+
+failed:
+ SAFE_FREE(service_principal);
+ SAFE_FREE(new_password);
+
+ return ret;
+}
+
+
+
+#endif
diff --git a/source/libsmb/.cvsignore b/source/libsmb/.cvsignore
new file mode 100644
index 00000000000..07da2225c72
--- /dev/null
+++ b/source/libsmb/.cvsignore
@@ -0,0 +1,3 @@
+*.po
+*.po32
+
diff --git a/source/libsmb/asn1.c b/source/libsmb/asn1.c
new file mode 100644
index 00000000000..ecc5e3dee64
--- /dev/null
+++ b/source/libsmb/asn1.c
@@ -0,0 +1,441 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple SPNEGO routines
+ Copyright (C) Andrew Tridgell 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* free an asn1 structure */
+void asn1_free(ASN1_DATA *data)
+{
+ SAFE_FREE(data->data);
+}
+
+/* write to the ASN1 buffer, advancing the buffer pointer */
+BOOL asn1_write(ASN1_DATA *data, const void *p, int len)
+{
+ if (data->has_error) return False;
+ if (data->length < data->ofs+len) {
+ uint8 *newp;
+ newp = Realloc(data->data, data->ofs+len);
+ if (!newp) {
+ SAFE_FREE(data->data);
+ data->has_error = True;
+ return False;
+ }
+ data->data = newp;
+ data->length = data->ofs+len;
+ }
+ memcpy(data->data + data->ofs, p, len);
+ data->ofs += len;
+ return True;
+}
+
+/* useful fn for writing a uint8 */
+BOOL asn1_write_uint8(ASN1_DATA *data, uint8 v)
+{
+ return asn1_write(data, &v, 1);
+}
+
+/* push a tag onto the asn1 data buffer. Used for nested structures */
+BOOL asn1_push_tag(ASN1_DATA *data, uint8 tag)
+{
+ struct nesting *nesting;
+
+ asn1_write_uint8(data, tag);
+ nesting = (struct nesting *)malloc(sizeof(struct nesting));
+ if (!nesting) {
+ data->has_error = True;
+ return False;
+ }
+
+ nesting->start = data->ofs;
+ nesting->next = data->nesting;
+ data->nesting = nesting;
+ return asn1_write_uint8(data, 0xff);
+}
+
+/* pop a tag */
+BOOL asn1_pop_tag(ASN1_DATA *data)
+{
+ struct nesting *nesting;
+ size_t len;
+
+ nesting = data->nesting;
+
+ if (!nesting) {
+ data->has_error = True;
+ return False;
+ }
+ len = data->ofs - (nesting->start+1);
+ /* yes, this is ugly. We don't know in advance how many bytes the length
+ of a tag will take, so we assumed 1 byte. If we were wrong then we
+ need to correct our mistake */
+ if (len > 255) {
+ data->data[nesting->start] = 0x82;
+ if (!asn1_write_uint8(data, 0)) return False;
+ if (!asn1_write_uint8(data, 0)) return False;
+ memmove(data->data+nesting->start+3, data->data+nesting->start+1, len);
+ data->data[nesting->start+1] = len>>8;
+ data->data[nesting->start+2] = len&0xff;
+ } else if (len > 127) {
+ data->data[nesting->start] = 0x81;
+ if (!asn1_write_uint8(data, 0)) return False;
+ memmove(data->data+nesting->start+2, data->data+nesting->start+1, len);
+ data->data[nesting->start+1] = len;
+ } else {
+ data->data[nesting->start] = len;
+ }
+
+ data->nesting = nesting->next;
+ free(nesting);
+ return True;
+}
+
+
+/* write an integer */
+BOOL asn1_write_Integer(ASN1_DATA *data, int i)
+{
+ if (!asn1_push_tag(data, ASN1_INTEGER)) return False;
+ do {
+ asn1_write_uint8(data, i);
+ i = i >> 8;
+ } while (i);
+ return asn1_pop_tag(data);
+}
+
+/* write an object ID to a ASN1 buffer */
+BOOL asn1_write_OID(ASN1_DATA *data, const char *OID)
+{
+ unsigned v, v2;
+ const char *p = (const char *)OID;
+ char *newp;
+
+ if (!asn1_push_tag(data, ASN1_OID))
+ return False;
+ v = strtol(p, &newp, 10);
+ p = newp;
+ v2 = strtol(p, &newp, 10);
+ p = newp;
+ if (!asn1_write_uint8(data, 40*v + v2))
+ return False;
+
+ while (*p) {
+ v = strtol(p, &newp, 10);
+ p = newp;
+ if (v >= (1<<28)) asn1_write_uint8(data, 0x80 | ((v>>28)&0xff));
+ if (v >= (1<<21)) asn1_write_uint8(data, 0x80 | ((v>>21)&0xff));
+ if (v >= (1<<14)) asn1_write_uint8(data, 0x80 | ((v>>14)&0xff));
+ if (v >= (1<<7)) asn1_write_uint8(data, 0x80 | ((v>>7)&0xff));
+ if (!asn1_write_uint8(data, v&0x7f))
+ return False;
+ }
+ return asn1_pop_tag(data);
+}
+
+/* write an octet string */
+BOOL asn1_write_OctetString(ASN1_DATA *data, const void *p, size_t length)
+{
+ asn1_push_tag(data, ASN1_OCTET_STRING);
+ asn1_write(data, p, length);
+ asn1_pop_tag(data);
+ return !data->has_error;
+}
+
+/* write a general string */
+BOOL asn1_write_GeneralString(ASN1_DATA *data, const char *s)
+{
+ asn1_push_tag(data, ASN1_GENERAL_STRING);
+ asn1_write(data, s, strlen(s));
+ asn1_pop_tag(data);
+ return !data->has_error;
+}
+
+/* write a BOOLEAN */
+BOOL asn1_write_BOOLEAN(ASN1_DATA *data, BOOL v)
+{
+ asn1_write_uint8(data, ASN1_BOOLEAN);
+ asn1_write_uint8(data, v);
+ return !data->has_error;
+}
+
+/* write a BOOLEAN - hmm, I suspect this one is the correct one, and the
+ above boolean is bogus. Need to check */
+BOOL asn1_write_BOOLEAN2(ASN1_DATA *data, BOOL v)
+{
+ asn1_push_tag(data, ASN1_BOOLEAN);
+ asn1_write_uint8(data, v);
+ asn1_pop_tag(data);
+ return !data->has_error;
+}
+
+/* check a BOOLEAN */
+BOOL asn1_check_BOOLEAN(ASN1_DATA *data, BOOL v)
+{
+ uint8 b = 0;
+
+ asn1_read_uint8(data, &b);
+ if (b != ASN1_BOOLEAN) {
+ data->has_error = True;
+ return False;
+ }
+ asn1_read_uint8(data, &b);
+ if (b != v) {
+ data->has_error = True;
+ return False;
+ }
+ return !data->has_error;
+}
+
+
+/* load a ASN1_DATA structure with a lump of data, ready to be parsed */
+BOOL asn1_load(ASN1_DATA *data, DATA_BLOB blob)
+{
+ ZERO_STRUCTP(data);
+ data->data = memdup(blob.data, blob.length);
+ if (!data->data) {
+ data->has_error = True;
+ return False;
+ }
+ data->length = blob.length;
+ return True;
+}
+
+/* read from a ASN1 buffer, advancing the buffer pointer */
+BOOL asn1_read(ASN1_DATA *data, void *p, int len)
+{
+ if (len < 0 || data->ofs + len < data->ofs || data->ofs + len < len) {
+ data->has_error = True;
+ return False;
+ }
+
+ if (data->ofs + len > data->length) {
+ data->has_error = True;
+ return False;
+ }
+ memcpy(p, data->data + data->ofs, len);
+ data->ofs += len;
+ return True;
+}
+
+/* read a uint8 from a ASN1 buffer */
+BOOL asn1_read_uint8(ASN1_DATA *data, uint8 *v)
+{
+ return asn1_read(data, v, 1);
+}
+
+/* start reading a nested asn1 structure */
+BOOL asn1_start_tag(ASN1_DATA *data, uint8 tag)
+{
+ uint8 b;
+ struct nesting *nesting;
+
+ if (!asn1_read_uint8(data, &b))
+ return False;
+
+ if (b != tag) {
+ data->has_error = True;
+ return False;
+ }
+ nesting = (struct nesting *)malloc(sizeof(struct nesting));
+ if (!nesting) {
+ data->has_error = True;
+ return False;
+ }
+
+ if (!asn1_read_uint8(data, &b)) {
+ return False;
+ }
+
+ if (b & 0x80) {
+ int n = b & 0x7f;
+ if (!asn1_read_uint8(data, &b))
+ return False;
+ nesting->taglen = b;
+ while (n > 1) {
+ if (!asn1_read_uint8(data, &b))
+ return False;
+ nesting->taglen = (nesting->taglen << 8) | b;
+ n--;
+ }
+ } else {
+ nesting->taglen = b;
+ }
+ nesting->start = data->ofs;
+ nesting->next = data->nesting;
+ data->nesting = nesting;
+ return !data->has_error;
+}
+
+
+/* stop reading a tag */
+BOOL asn1_end_tag(ASN1_DATA *data)
+{
+ struct nesting *nesting;
+
+ /* make sure we read it all */
+ if (asn1_tag_remaining(data) != 0) {
+ data->has_error = True;
+ return False;
+ }
+
+ nesting = data->nesting;
+
+ if (!nesting) {
+ data->has_error = True;
+ return False;
+ }
+
+ data->nesting = nesting->next;
+ free(nesting);
+ return True;
+}
+
+/* work out how many bytes are left in this nested tag */
+int asn1_tag_remaining(ASN1_DATA *data)
+{
+ if (!data->nesting) {
+ data->has_error = True;
+ return -1;
+ }
+ return data->nesting->taglen - (data->ofs - data->nesting->start);
+}
+
+/* read an object ID from a ASN1 buffer */
+BOOL asn1_read_OID(ASN1_DATA *data, char **OID)
+{
+ uint8 b;
+ pstring oid;
+ fstring el;
+
+ if (!asn1_start_tag(data, ASN1_OID)) return False;
+ asn1_read_uint8(data, &b);
+
+ oid[0] = 0;
+ fstr_sprintf(el, "%u", b/40);
+ pstrcat(oid, el);
+ fstr_sprintf(el, " %u", b%40);
+ pstrcat(oid, el);
+
+ while (asn1_tag_remaining(data) > 0) {
+ unsigned v = 0;
+ do {
+ asn1_read_uint8(data, &b);
+ v = (v<<7) | (b&0x7f);
+ } while (!data->has_error && b & 0x80);
+ fstr_sprintf(el, " %u", v);
+ pstrcat(oid, el);
+ }
+
+ asn1_end_tag(data);
+
+ *OID = strdup(oid);
+
+ return !data->has_error;
+}
+
+/* check that the next object ID is correct */
+BOOL asn1_check_OID(ASN1_DATA *data, const char *OID)
+{
+ char *id;
+
+ if (!asn1_read_OID(data, &id)) return False;
+
+ if (strcmp(id, OID) != 0) {
+ data->has_error = True;
+ return False;
+ }
+ free(id);
+ return True;
+}
+
+/* read a GeneralString from a ASN1 buffer */
+BOOL asn1_read_GeneralString(ASN1_DATA *data, char **s)
+{
+ int len;
+ if (!asn1_start_tag(data, ASN1_GENERAL_STRING)) return False;
+ len = asn1_tag_remaining(data);
+ if (len < 0) {
+ data->has_error = True;
+ return False;
+ }
+ *s = malloc(len+1);
+ if (! *s) {
+ data->has_error = True;
+ return False;
+ }
+ asn1_read(data, *s, len);
+ (*s)[len] = 0;
+ asn1_end_tag(data);
+ return !data->has_error;
+}
+
+/* read a octet string blob */
+BOOL asn1_read_OctetString(ASN1_DATA *data, DATA_BLOB *blob)
+{
+ int len;
+ ZERO_STRUCTP(blob);
+ if (!asn1_start_tag(data, ASN1_OCTET_STRING)) return False;
+ len = asn1_tag_remaining(data);
+ if (len < 0) {
+ data->has_error = True;
+ return False;
+ }
+ *blob = data_blob(NULL, len);
+ asn1_read(data, blob->data, len);
+ asn1_end_tag(data);
+ return !data->has_error;
+}
+
+/* read an interger */
+BOOL asn1_read_Integer(ASN1_DATA *data, int *i)
+{
+ uint8 b;
+ *i = 0;
+
+ if (!asn1_start_tag(data, ASN1_INTEGER)) return False;
+ while (asn1_tag_remaining(data)>0) {
+ asn1_read_uint8(data, &b);
+ *i = (*i << 8) + b;
+ }
+ return asn1_end_tag(data);
+
+}
+
+/* check a enumarted value is correct */
+BOOL asn1_check_enumerated(ASN1_DATA *data, int v)
+{
+ uint8 b;
+ if (!asn1_start_tag(data, ASN1_ENUMERATED)) return False;
+ asn1_read_uint8(data, &b);
+ asn1_end_tag(data);
+
+ if (v != b)
+ data->has_error = False;
+
+ return !data->has_error;
+}
+
+/* write an enumarted value to the stream */
+BOOL asn1_write_enumerated(ASN1_DATA *data, uint8 v)
+{
+ if (!asn1_push_tag(data, ASN1_ENUMERATED)) return False;
+ asn1_write_uint8(data, v);
+ asn1_pop_tag(data);
+ return !data->has_error;
+}
diff --git a/source/libsmb/cliconnect.c b/source/libsmb/cliconnect.c
new file mode 100644
index 00000000000..c39044e10af
--- /dev/null
+++ b/source/libsmb/cliconnect.c
@@ -0,0 +1,1683 @@
+/*
+ Unix SMB/CIFS implementation.
+ client connect/disconnect routines
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Andrew Bartlett 2001-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+
+static const struct {
+ int prot;
+ const char *name;
+} prots[] = {
+ {PROTOCOL_CORE,"PC NETWORK PROGRAM 1.0"},
+ {PROTOCOL_COREPLUS,"MICROSOFT NETWORKS 1.03"},
+ {PROTOCOL_LANMAN1,"MICROSOFT NETWORKS 3.0"},
+ {PROTOCOL_LANMAN1,"LANMAN1.0"},
+ {PROTOCOL_LANMAN2,"LM1.2X002"},
+ {PROTOCOL_LANMAN2,"DOS LANMAN2.1"},
+ {PROTOCOL_LANMAN2,"Samba"},
+ {PROTOCOL_NT1,"NT LANMAN 1.0"},
+ {PROTOCOL_NT1,"NT LM 0.12"},
+ {-1,NULL}
+};
+
+/****************************************************************************
+ Do an old lanman2 style session setup.
+****************************************************************************/
+
+static BOOL cli_session_setup_lanman2(struct cli_state *cli, const char *user,
+ const char *pass, size_t passlen, const char *workgroup)
+{
+ fstring pword;
+ char *p;
+
+ if (passlen > sizeof(pword)-1)
+ return False;
+
+ /* LANMAN servers predate NT status codes and Unicode and ignore those
+ smb flags so we must disable the corresponding default capabilities
+ that would otherwise cause the Unicode and NT Status flags to be
+ set (and even returned by the server) */
+
+ cli->capabilities &= ~(CAP_UNICODE | CAP_STATUS32);
+
+ /* if in share level security then don't send a password now */
+ if (!(cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL))
+ passlen = 0;
+
+ if (passlen > 0 && (cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) && passlen != 24) {
+ /* Encrypted mode needed, and non encrypted password supplied. */
+ passlen = 24;
+ SMBencrypt(pass,cli->secblob.data,(uchar *)pword);
+ } else if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) && passlen == 24) {
+ /* Encrypted mode needed, and encrypted password supplied. */
+ memcpy(pword, pass, passlen);
+ } else if (passlen > 0) {
+ /* Plaintext mode needed, assume plaintext supplied. */
+ passlen = clistr_push(cli, pword, pass, sizeof(pword), STR_TERMINATE);
+ }
+
+ /* send a session setup command */
+ memset(cli->outbuf,'\0',smb_size);
+ set_message(cli->outbuf,10, 0, True);
+ SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
+ cli_setup_packet(cli);
+
+ SCVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,cli->max_xmit);
+ SSVAL(cli->outbuf,smb_vwv3,2);
+ SSVAL(cli->outbuf,smb_vwv4,1);
+ SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
+ SSVAL(cli->outbuf,smb_vwv7,passlen);
+
+ p = smb_buf(cli->outbuf);
+ memcpy(p,pword,passlen);
+ p += passlen;
+ p += clistr_push(cli, p, user, -1, STR_TERMINATE|STR_UPPER);
+ p += clistr_push(cli, p, workgroup, -1, STR_TERMINATE|STR_UPPER);
+ p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE);
+ p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE);
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli))
+ return False;
+
+ show_msg(cli->inbuf);
+
+ if (cli_is_error(cli))
+ return False;
+
+ /* use the returned vuid from now on */
+ cli->vuid = SVAL(cli->inbuf,smb_uid);
+ fstrcpy(cli->user_name, user);
+
+ return True;
+}
+
+/****************************************************************************
+ Work out suitable capabilities to offer the server.
+****************************************************************************/
+
+static uint32 cli_session_setup_capabilities(struct cli_state *cli)
+{
+ uint32 capabilities = CAP_NT_SMBS;
+
+ if (!cli->force_dos_errors)
+ capabilities |= CAP_STATUS32;
+
+ if (cli->use_level_II_oplocks)
+ capabilities |= CAP_LEVEL_II_OPLOCKS;
+
+ if (cli->capabilities & CAP_UNICODE)
+ capabilities |= CAP_UNICODE;
+
+ if (cli->capabilities & CAP_LARGE_FILES)
+ capabilities |= CAP_LARGE_FILES;
+
+ return capabilities;
+}
+
+/****************************************************************************
+ Do a NT1 guest session setup.
+****************************************************************************/
+
+static BOOL cli_session_setup_guest(struct cli_state *cli)
+{
+ char *p;
+ uint32 capabilities = cli_session_setup_capabilities(cli);
+
+ set_message(cli->outbuf,13,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
+ cli_setup_packet(cli);
+
+ SCVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE);
+ SSVAL(cli->outbuf,smb_vwv3,2);
+ SSVAL(cli->outbuf,smb_vwv4,cli->pid);
+ SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
+ SSVAL(cli->outbuf,smb_vwv7,0);
+ SSVAL(cli->outbuf,smb_vwv8,0);
+ SIVAL(cli->outbuf,smb_vwv11,capabilities);
+ p = smb_buf(cli->outbuf);
+ p += clistr_push(cli, p, "", -1, STR_TERMINATE); /* username */
+ p += clistr_push(cli, p, "", -1, STR_TERMINATE); /* workgroup */
+ p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE);
+ p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE);
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli))
+ return False;
+
+ show_msg(cli->inbuf);
+
+ if (cli_is_error(cli))
+ return False;
+
+ cli->vuid = SVAL(cli->inbuf,smb_uid);
+
+ p = smb_buf(cli->inbuf);
+ p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, STR_TERMINATE);
+ p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), -1, STR_TERMINATE);
+ p += clistr_pull(cli, cli->server_domain, p, sizeof(fstring), -1, STR_TERMINATE);
+
+ fstrcpy(cli->user_name, "");
+
+ return True;
+}
+
+/****************************************************************************
+ Do a NT1 plaintext session setup.
+****************************************************************************/
+
+static BOOL cli_session_setup_plaintext(struct cli_state *cli, const char *user,
+ const char *pass, const char *workgroup)
+{
+ uint32 capabilities = cli_session_setup_capabilities(cli);
+ char *p;
+ fstring lanman;
+
+ fstr_sprintf( lanman, "Samba %s", SAMBA_VERSION_STRING);
+
+ set_message(cli->outbuf,13,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
+ cli_setup_packet(cli);
+
+ SCVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE);
+ SSVAL(cli->outbuf,smb_vwv3,2);
+ SSVAL(cli->outbuf,smb_vwv4,cli->pid);
+ SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
+ SSVAL(cli->outbuf,smb_vwv8,0);
+ SIVAL(cli->outbuf,smb_vwv11,capabilities);
+ p = smb_buf(cli->outbuf);
+
+ /* check wether to send the ASCII or UNICODE version of the password */
+
+ if ( (capabilities & CAP_UNICODE) == 0 ) {
+ p += clistr_push(cli, p, pass, -1, STR_TERMINATE); /* password */
+ SSVAL(cli->outbuf,smb_vwv7,PTR_DIFF(p, smb_buf(cli->outbuf)));
+ }
+ else {
+ p += clistr_push(cli, p, pass, -1, STR_UNICODE|STR_TERMINATE); /* unicode password */
+ SSVAL(cli->outbuf,smb_vwv8,PTR_DIFF(p, smb_buf(cli->outbuf)));
+ }
+
+ p += clistr_push(cli, p, user, -1, STR_TERMINATE); /* username */
+ p += clistr_push(cli, p, workgroup, -1, STR_TERMINATE); /* workgroup */
+ p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE);
+ p += clistr_push(cli, p, lanman, -1, STR_TERMINATE);
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli))
+ return False;
+
+ show_msg(cli->inbuf);
+
+ if (cli_is_error(cli))
+ return False;
+
+ cli->vuid = SVAL(cli->inbuf,smb_uid);
+ p = smb_buf(cli->inbuf);
+ p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, STR_TERMINATE);
+ p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), -1, STR_TERMINATE);
+ p += clistr_pull(cli, cli->server_domain, p, sizeof(fstring), -1, STR_TERMINATE);
+ fstrcpy(cli->user_name, user);
+
+ return True;
+}
+
+/**
+ * Set the user session key for a connection
+ * @param cli The cli structure to add it too
+ * @param session_key The session key used. (A copy of this is taken for the cli struct)
+ *
+ */
+
+static void cli_set_session_key (struct cli_state *cli, const DATA_BLOB session_key)
+{
+ cli->user_session_key = data_blob(session_key.data, session_key.length);
+}
+
+/****************************************************************************
+ do a NT1 NTLM/LM encrypted session setup - for when extended security
+ is not negotiated.
+ @param cli client state to create do session setup on
+ @param user username
+ @param pass *either* cleartext password (passlen !=24) or LM response.
+ @param ntpass NT response, implies ntpasslen >=24, implies pass is not clear
+ @param workgroup The user's domain.
+****************************************************************************/
+
+static BOOL cli_session_setup_nt1(struct cli_state *cli, const char *user,
+ const char *pass, size_t passlen,
+ const char *ntpass, size_t ntpasslen,
+ const char *workgroup)
+{
+ uint32 capabilities = cli_session_setup_capabilities(cli);
+ DATA_BLOB lm_response = data_blob(NULL, 0);
+ DATA_BLOB nt_response = data_blob(NULL, 0);
+ DATA_BLOB session_key = data_blob(NULL, 0);
+ BOOL ret = False;
+ char *p;
+
+ if (passlen == 0) {
+ /* do nothing - guest login */
+ } else if (passlen != 24) {
+ if (lp_client_ntlmv2_auth()) {
+ DATA_BLOB server_chal;
+ DATA_BLOB names_blob;
+ server_chal = data_blob(cli->secblob.data, MIN(cli->secblob.length, 8));
+
+ /* note that the 'workgroup' here is a best guess - we don't know
+ the server's domain at this point. The 'server name' is also
+ dodgy...
+ */
+ names_blob = NTLMv2_generate_names_blob(cli->called.name, workgroup);
+
+ if (!SMBNTLMv2encrypt(user, workgroup, pass, &server_chal,
+ &names_blob,
+ &lm_response, &nt_response, &session_key)) {
+ data_blob_free(&names_blob);
+ data_blob_free(&server_chal);
+ return False;
+ }
+ data_blob_free(&names_blob);
+ data_blob_free(&server_chal);
+
+ } else {
+ uchar nt_hash[16];
+ E_md4hash(pass, nt_hash);
+
+ nt_response = data_blob(NULL, 24);
+ SMBNTencrypt(pass,cli->secblob.data,nt_response.data);
+
+ /* non encrypted password supplied. Ignore ntpass. */
+ if (lp_client_lanman_auth()) {
+ lm_response = data_blob(NULL, 24);
+ SMBencrypt(pass,cli->secblob.data, lm_response.data);
+ } else {
+ /* LM disabled, place NT# in LM field instead */
+ lm_response = data_blob(nt_response.data, nt_response.length);
+ }
+
+ session_key = data_blob(NULL, 16);
+ SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
+ }
+ cli_simple_set_signing(cli, session_key, nt_response);
+ } else {
+ /* pre-encrypted password supplied. Only used for
+ security=server, can't do
+ signing because we don't have original key */
+
+ lm_response = data_blob(pass, passlen);
+ nt_response = data_blob(ntpass, ntpasslen);
+ }
+
+ /* send a session setup command */
+ memset(cli->outbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,13,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
+ cli_setup_packet(cli);
+
+ SCVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE);
+ SSVAL(cli->outbuf,smb_vwv3,2);
+ SSVAL(cli->outbuf,smb_vwv4,cli->pid);
+ SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
+ SSVAL(cli->outbuf,smb_vwv7,lm_response.length);
+ SSVAL(cli->outbuf,smb_vwv8,nt_response.length);
+ SIVAL(cli->outbuf,smb_vwv11,capabilities);
+ p = smb_buf(cli->outbuf);
+ if (lm_response.length) {
+ memcpy(p,lm_response.data, lm_response.length); p += lm_response.length;
+ }
+ if (nt_response.length) {
+ memcpy(p,nt_response.data, nt_response.length); p += nt_response.length;
+ }
+ p += clistr_push(cli, p, user, -1, STR_TERMINATE);
+ p += clistr_push(cli, p, workgroup, -1, STR_TERMINATE);
+ p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE);
+ p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE);
+ cli_setup_bcc(cli, p);
+
+ if (!cli_send_smb(cli) || !cli_receive_smb(cli)) {
+ ret = False;
+ goto end;
+ }
+
+ /* show_msg(cli->inbuf); */
+
+ if (cli_is_error(cli)) {
+ ret = False;
+ goto end;
+ }
+
+ /* use the returned vuid from now on */
+ cli->vuid = SVAL(cli->inbuf,smb_uid);
+
+ p = smb_buf(cli->inbuf);
+ p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, STR_TERMINATE);
+ p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), -1, STR_TERMINATE);
+ p += clistr_pull(cli, cli->server_domain, p, sizeof(fstring), -1, STR_TERMINATE);
+
+ fstrcpy(cli->user_name, user);
+
+ if (session_key.data) {
+ /* Have plaintext orginal */
+ cli_set_session_key(cli, session_key);
+ }
+
+ ret = True;
+end:
+ data_blob_free(&lm_response);
+ data_blob_free(&nt_response);
+
+ if (!ret)
+ data_blob_free(&session_key);
+ return ret;
+}
+
+/****************************************************************************
+ Send a extended security session setup blob
+****************************************************************************/
+
+static BOOL cli_session_setup_blob_send(struct cli_state *cli, DATA_BLOB blob)
+{
+ uint32 capabilities = cli_session_setup_capabilities(cli);
+ char *p;
+
+ capabilities |= CAP_EXTENDED_SECURITY;
+
+ /* send a session setup command */
+ memset(cli->outbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,12,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
+
+ cli_setup_packet(cli);
+
+ SCVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE);
+ SSVAL(cli->outbuf,smb_vwv3,2);
+ SSVAL(cli->outbuf,smb_vwv4,1);
+ SIVAL(cli->outbuf,smb_vwv5,0);
+ SSVAL(cli->outbuf,smb_vwv7,blob.length);
+ SIVAL(cli->outbuf,smb_vwv10,capabilities);
+ p = smb_buf(cli->outbuf);
+ memcpy(p, blob.data, blob.length);
+ p += blob.length;
+ p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE);
+ p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE);
+ cli_setup_bcc(cli, p);
+ return cli_send_smb(cli);
+}
+
+/****************************************************************************
+ Send a extended security session setup blob, returning a reply blob.
+****************************************************************************/
+
+static DATA_BLOB cli_session_setup_blob_receive(struct cli_state *cli)
+{
+ DATA_BLOB blob2 = data_blob(NULL, 0);
+ char *p;
+ size_t len;
+
+ if (!cli_receive_smb(cli))
+ return blob2;
+
+ show_msg(cli->inbuf);
+
+ if (cli_is_error(cli) && !NT_STATUS_EQUAL(cli_nt_error(cli),
+ NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ return blob2;
+ }
+
+ /* use the returned vuid from now on */
+ cli->vuid = SVAL(cli->inbuf,smb_uid);
+
+ p = smb_buf(cli->inbuf);
+
+ blob2 = data_blob(p, SVAL(cli->inbuf, smb_vwv3));
+
+ p += blob2.length;
+ p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, STR_TERMINATE);
+
+ /* w2k with kerberos doesn't properly null terminate this field */
+ len = smb_buflen(cli->inbuf) - PTR_DIFF(p, smb_buf(cli->inbuf));
+ p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), len, 0);
+
+ return blob2;
+}
+
+#ifdef HAVE_KRB5
+
+/****************************************************************************
+ Send a extended security session setup blob, returning a reply blob.
+****************************************************************************/
+
+static DATA_BLOB cli_session_setup_blob(struct cli_state *cli, DATA_BLOB blob)
+{
+ DATA_BLOB blob2 = data_blob(NULL, 0);
+ if (!cli_session_setup_blob_send(cli, blob)) {
+ return blob2;
+ }
+
+ return cli_session_setup_blob_receive(cli);
+}
+
+/****************************************************************************
+ Use in-memory credentials cache
+****************************************************************************/
+
+static void use_in_memory_ccache(void) {
+ setenv(KRB5_ENV_CCNAME, "MEMORY:cliconnect", 1);
+}
+
+/****************************************************************************
+ Do a spnego/kerberos encrypted session setup.
+****************************************************************************/
+
+static ADS_STATUS cli_session_setup_kerberos(struct cli_state *cli, const char *principal, const char *workgroup)
+{
+ DATA_BLOB blob2, negTokenTarg;
+ DATA_BLOB session_key_krb5;
+ DATA_BLOB null_blob = data_blob(NULL, 0);
+ int rc;
+
+ DEBUG(2,("Doing kerberos session setup\n"));
+
+ /* generate the encapsulated kerberos5 ticket */
+ rc = spnego_gen_negTokenTarg(principal, 0, &negTokenTarg, &session_key_krb5);
+
+ if (rc) {
+ DEBUG(1, ("spnego_gen_negTokenTarg failed: %s\n", error_message(rc)));
+ return ADS_ERROR_KRB5(rc);
+ }
+
+#if 0
+ file_save("negTokenTarg.dat", negTokenTarg.data, negTokenTarg.length);
+#endif
+
+ cli_simple_set_signing(cli, session_key_krb5, null_blob);
+
+ blob2 = cli_session_setup_blob(cli, negTokenTarg);
+
+ /* we don't need this blob for kerberos */
+ data_blob_free(&blob2);
+
+ cli_set_session_key(cli, session_key_krb5);
+
+ data_blob_free(&negTokenTarg);
+
+ if (cli_is_error(cli)) {
+ if (NT_STATUS_IS_OK(cli_nt_error(cli))) {
+ return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+ }
+ }
+ return ADS_ERROR_NT(cli_nt_error(cli));
+}
+#endif /* HAVE_KRB5 */
+
+
+/****************************************************************************
+ Do a spnego/NTLMSSP encrypted session setup.
+****************************************************************************/
+
+static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *user,
+ const char *pass, const char *domain)
+{
+ struct ntlmssp_state *ntlmssp_state;
+ NTSTATUS nt_status;
+ int turn = 1;
+ DATA_BLOB msg1;
+ DATA_BLOB blob;
+ DATA_BLOB blob_in = data_blob(NULL, 0);
+ DATA_BLOB blob_out;
+
+ cli_temp_set_signing(cli);
+
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_client_start(&ntlmssp_state))) {
+ return nt_status;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, user))) {
+ return nt_status;
+ }
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, domain))) {
+ return nt_status;
+ }
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_password(ntlmssp_state, pass))) {
+ return nt_status;
+ }
+
+ do {
+ nt_status = ntlmssp_update(ntlmssp_state,
+ blob_in, &blob_out);
+ data_blob_free(&blob_in);
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ if (turn == 1) {
+ /* and wrap it in a SPNEGO wrapper */
+ msg1 = gen_negTokenInit(OID_NTLMSSP, blob_out);
+ } else {
+ /* wrap it in SPNEGO */
+ msg1 = spnego_gen_auth(blob_out);
+ }
+
+ /* now send that blob on its way */
+ if (!cli_session_setup_blob_send(cli, msg1)) {
+ DEBUG(3, ("Failed to send NTLMSSP/SPNEGO blob to server!\n"));
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ } else {
+ data_blob_free(&msg1);
+
+ blob = cli_session_setup_blob_receive(cli);
+
+ nt_status = cli_nt_error(cli);
+ if (cli_is_error(cli) && NT_STATUS_IS_OK(nt_status)) {
+ if (cli->smb_rw_error == READ_BAD_SIG) {
+ nt_status = NT_STATUS_ACCESS_DENIED;
+ } else {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+ }
+ }
+
+ if (!blob.length) {
+ if (NT_STATUS_IS_OK(nt_status)) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ }
+ } else if ((turn == 1) &&
+ NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ DATA_BLOB tmp_blob = data_blob(NULL, 0);
+ /* the server might give us back two challenges */
+ if (!spnego_parse_challenge(blob, &blob_in,
+ &tmp_blob)) {
+ DEBUG(3,("Failed to parse challenges\n"));
+ nt_status = NT_STATUS_INVALID_PARAMETER;
+ }
+ data_blob_free(&tmp_blob);
+ } else {
+ if (!spnego_parse_auth_response(blob, nt_status,
+ &blob_in)) {
+ DEBUG(3,("Failed to parse auth response\n"));
+ if (NT_STATUS_IS_OK(nt_status)
+ || NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED))
+ nt_status = NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+ data_blob_free(&blob);
+ data_blob_free(&blob_out);
+ turn++;
+ } while (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED));
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+
+ DATA_BLOB key = data_blob(ntlmssp_state->session_key.data,
+ ntlmssp_state->session_key.length);
+ DATA_BLOB null_blob = data_blob(NULL, 0);
+
+ fstrcpy(cli->server_domain, ntlmssp_state->server_domain);
+ cli_set_session_key(cli, ntlmssp_state->session_key);
+
+ if (cli_simple_set_signing(cli, key, null_blob)) {
+
+ /* 'resign' the last message, so we get the right sequence numbers
+ for checking the first reply from the server */
+ cli_calculate_sign_mac(cli);
+
+ if (!cli_check_sign_mac(cli, True)) {
+ nt_status = NT_STATUS_ACCESS_DENIED;
+ }
+ }
+ }
+
+ /* we have a reference conter on ntlmssp_state, if we are signing
+ then the state will be kept by the signing engine */
+
+ ntlmssp_end(&ntlmssp_state);
+
+ return nt_status;
+}
+
+/****************************************************************************
+ Do a spnego encrypted session setup.
+****************************************************************************/
+
+ADS_STATUS cli_session_setup_spnego(struct cli_state *cli, const char *user,
+ const char *pass, const char *domain)
+{
+ char *principal;
+ char *OIDs[ASN1_MAX_OIDS];
+ int i;
+ BOOL got_kerberos_mechanism = False;
+ DATA_BLOB blob;
+
+ DEBUG(3,("Doing spnego session setup (blob length=%lu)\n", (unsigned long)cli->secblob.length));
+
+ /* the server might not even do spnego */
+ if (cli->secblob.length <= 16) {
+ DEBUG(3,("server didn't supply a full spnego negprot\n"));
+ goto ntlmssp;
+ }
+
+#if 0
+ file_save("negprot.dat", cli->secblob.data, cli->secblob.length);
+#endif
+
+ /* there is 16 bytes of GUID before the real spnego packet starts */
+ blob = data_blob(cli->secblob.data+16, cli->secblob.length-16);
+
+ /* the server sent us the first part of the SPNEGO exchange in the negprot
+ reply */
+ if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
+ data_blob_free(&blob);
+ return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ }
+ data_blob_free(&blob);
+
+ /* make sure the server understands kerberos */
+ for (i=0;OIDs[i];i++) {
+ DEBUG(3,("got OID=%s\n", OIDs[i]));
+ if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
+ strcmp(OIDs[i], OID_KERBEROS5) == 0) {
+ got_kerberos_mechanism = True;
+ }
+ free(OIDs[i]);
+ }
+ DEBUG(3,("got principal=%s\n", principal));
+
+ fstrcpy(cli->user_name, user);
+
+#ifdef HAVE_KRB5
+ /* If password is set we reauthenticate to kerberos server
+ * and do not store results */
+
+ if (got_kerberos_mechanism && cli->use_kerberos) {
+ if (pass && *pass) {
+ int ret;
+
+ use_in_memory_ccache();
+ ret = kerberos_kinit_password(user, pass, 0 /* no time correction for now */, NULL);
+
+ if (ret){
+ DEBUG(0, ("Kinit failed: %s\n", error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+ }
+
+ return cli_session_setup_kerberos(cli, principal, domain);
+ }
+#endif
+
+ free(principal);
+
+ntlmssp:
+
+ return ADS_ERROR_NT(cli_session_setup_ntlmssp(cli, user, pass, domain));
+}
+
+/****************************************************************************
+ Send a session setup. The username and workgroup is in UNIX character
+ format and must be converted to DOS codepage format before sending. If the
+ password is in plaintext, the same should be done.
+****************************************************************************/
+
+BOOL cli_session_setup(struct cli_state *cli,
+ const char *user,
+ const char *pass, int passlen,
+ const char *ntpass, int ntpasslen,
+ const char *workgroup)
+{
+ char *p;
+ fstring user2;
+
+ /* allow for workgroups as part of the username */
+ fstrcpy(user2, user);
+ if ((p=strchr_m(user2,'\\')) || (p=strchr_m(user2,'/')) ||
+ (p=strchr_m(user2,*lp_winbind_separator()))) {
+ *p = 0;
+ user = p+1;
+ workgroup = user2;
+ }
+
+ if (cli->protocol < PROTOCOL_LANMAN1)
+ return True;
+
+ /* now work out what sort of session setup we are going to
+ do. I have split this into separate functions to make the
+ flow a bit easier to understand (tridge) */
+
+ /* if its an older server then we have to use the older request format */
+
+ if (cli->protocol < PROTOCOL_NT1) {
+ if (!lp_client_lanman_auth() && passlen != 24 && (*pass)) {
+ DEBUG(1, ("Server requested LM password but 'client lanman auth'"
+ " is disabled\n"));
+ return False;
+ }
+
+ if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0 &&
+ !lp_client_plaintext_auth() && (*pass)) {
+ DEBUG(1, ("Server requested plaintext password but 'client use plaintext auth'"
+ " is disabled\n"));
+ return False;
+ }
+
+ return cli_session_setup_lanman2(cli, user, pass, passlen, workgroup);
+ }
+
+ /* if no user is supplied then we have to do an anonymous connection.
+ passwords are ignored */
+
+ if (!user || !*user)
+ return cli_session_setup_guest(cli);
+
+ /* if the server is share level then send a plaintext null
+ password at this point. The password is sent in the tree
+ connect */
+
+ if ((cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL) == 0)
+ return cli_session_setup_plaintext(cli, user, "", workgroup);
+
+ /* if the server doesn't support encryption then we have to use
+ plaintext. The second password is ignored */
+
+ if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
+ if (!lp_client_plaintext_auth() && (*pass)) {
+ DEBUG(1, ("Server requested plaintext password but 'client use plaintext auth'"
+ " is disabled\n"));
+ return False;
+ }
+ return cli_session_setup_plaintext(cli, user, pass, workgroup);
+ }
+
+ /* if the server supports extended security then use SPNEGO */
+
+ if (cli->capabilities & CAP_EXTENDED_SECURITY) {
+ ADS_STATUS status = cli_session_setup_spnego(cli, user, pass, workgroup);
+ if (!ADS_ERR_OK(status)) {
+ DEBUG(3, ("SPNEGO login failed: %s\n", ads_errstr(status)));
+ return False;
+ }
+ return True;
+ }
+
+ /* otherwise do a NT1 style session setup */
+
+ return cli_session_setup_nt1(cli, user,
+ pass, passlen, ntpass, ntpasslen,
+ workgroup);
+}
+
+/****************************************************************************
+ Send a uloggoff.
+*****************************************************************************/
+
+BOOL cli_ulogoff(struct cli_state *cli)
+{
+ memset(cli->outbuf,'\0',smb_size);
+ set_message(cli->outbuf,2,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBulogoffX);
+ cli_setup_packet(cli);
+ SSVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,0); /* no additional info */
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli))
+ return False;
+
+ return !cli_is_error(cli);
+}
+
+/****************************************************************************
+ Send a tconX.
+****************************************************************************/
+BOOL cli_send_tconX(struct cli_state *cli,
+ const char *share, const char *dev, const char *pass, int passlen)
+{
+ fstring fullshare, pword;
+ char *p;
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ fstrcpy(cli->share, share);
+
+ /* in user level security don't send a password now */
+ if (cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL) {
+ passlen = 1;
+ pass = "";
+ }
+
+ if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) && *pass && passlen != 24) {
+ if (!lp_client_lanman_auth()) {
+ DEBUG(1, ("Server requested LANMAN password but 'client use lanman auth'"
+ " is disabled\n"));
+ return False;
+ }
+
+ /*
+ * Non-encrypted passwords - convert to DOS codepage before encryption.
+ */
+ passlen = 24;
+ SMBencrypt(pass,cli->secblob.data,(uchar *)pword);
+ } else {
+ if((cli->sec_mode & (NEGOTIATE_SECURITY_USER_LEVEL|NEGOTIATE_SECURITY_CHALLENGE_RESPONSE)) == 0) {
+ if (!lp_client_plaintext_auth() && (*pass)) {
+ DEBUG(1, ("Server requested plaintext password but 'client use plaintext auth'"
+ " is disabled\n"));
+ return False;
+ }
+
+ /*
+ * Non-encrypted passwords - convert to DOS codepage before using.
+ */
+ passlen = clistr_push(cli, pword, pass, sizeof(pword), STR_TERMINATE);
+
+ } else {
+ memcpy(pword, pass, passlen);
+ }
+ }
+
+ slprintf(fullshare, sizeof(fullshare)-1,
+ "\\\\%s\\%s", cli->desthost, share);
+
+ set_message(cli->outbuf,4, 0, True);
+ SCVAL(cli->outbuf,smb_com,SMBtconX);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv3,passlen);
+
+ p = smb_buf(cli->outbuf);
+ memcpy(p,pword,passlen);
+ p += passlen;
+ p += clistr_push(cli, p, fullshare, -1, STR_TERMINATE |STR_UPPER);
+ p += clistr_push(cli, p, dev, -1, STR_TERMINATE |STR_UPPER | STR_ASCII);
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli))
+ return False;
+
+ if (cli_is_error(cli))
+ return False;
+
+ clistr_pull(cli, cli->dev, smb_buf(cli->inbuf), sizeof(fstring), -1, STR_TERMINATE|STR_ASCII);
+
+ if (cli->protocol >= PROTOCOL_NT1 &&
+ smb_buflen(cli->inbuf) == 3) {
+ /* almost certainly win95 - enable bug fixes */
+ cli->win95 = True;
+ }
+
+ cli->cnum = SVAL(cli->inbuf,smb_tid);
+ return True;
+}
+
+/****************************************************************************
+ Send a tree disconnect.
+****************************************************************************/
+
+BOOL cli_tdis(struct cli_state *cli)
+{
+ memset(cli->outbuf,'\0',smb_size);
+ set_message(cli->outbuf,0,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBtdis);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli))
+ return False;
+
+ return !cli_is_error(cli);
+}
+
+/****************************************************************************
+ Send a negprot command.
+****************************************************************************/
+
+void cli_negprot_send(struct cli_state *cli)
+{
+ char *p;
+ int numprots;
+
+ if (cli->protocol < PROTOCOL_NT1)
+ cli->use_spnego = False;
+
+ memset(cli->outbuf,'\0',smb_size);
+
+ /* setup the protocol strings */
+ set_message(cli->outbuf,0,0,True);
+
+ p = smb_buf(cli->outbuf);
+ for (numprots=0;
+ prots[numprots].name && prots[numprots].prot<=cli->protocol;
+ numprots++) {
+ *p++ = 2;
+ p += clistr_push(cli, p, prots[numprots].name, -1, STR_TERMINATE);
+ }
+
+ SCVAL(cli->outbuf,smb_com,SMBnegprot);
+ cli_setup_bcc(cli, p);
+ cli_setup_packet(cli);
+
+ SCVAL(smb_buf(cli->outbuf),0,2);
+
+ cli_send_smb(cli);
+}
+
+/****************************************************************************
+ Send a negprot command.
+****************************************************************************/
+
+BOOL cli_negprot(struct cli_state *cli)
+{
+ char *p;
+ int numprots;
+ int plength;
+
+ if (cli->protocol < PROTOCOL_NT1)
+ cli->use_spnego = False;
+
+ memset(cli->outbuf,'\0',smb_size);
+
+ /* setup the protocol strings */
+ for (plength=0,numprots=0;
+ prots[numprots].name && prots[numprots].prot<=cli->protocol;
+ numprots++)
+ plength += strlen(prots[numprots].name)+2;
+
+ set_message(cli->outbuf,0,plength,True);
+
+ p = smb_buf(cli->outbuf);
+ for (numprots=0;
+ prots[numprots].name && prots[numprots].prot<=cli->protocol;
+ numprots++) {
+ *p++ = 2;
+ p += clistr_push(cli, p, prots[numprots].name, -1, STR_TERMINATE);
+ }
+
+ SCVAL(cli->outbuf,smb_com,SMBnegprot);
+ cli_setup_packet(cli);
+
+ SCVAL(smb_buf(cli->outbuf),0,2);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli))
+ return False;
+
+ show_msg(cli->inbuf);
+
+ if (cli_is_error(cli) ||
+ ((int)SVAL(cli->inbuf,smb_vwv0) >= numprots)) {
+ return(False);
+ }
+
+ cli->protocol = prots[SVAL(cli->inbuf,smb_vwv0)].prot;
+
+ if ((cli->protocol < PROTOCOL_NT1) && cli->sign_info.mandatory_signing) {
+ DEBUG(0,("cli_negprot: SMB signing is mandatory and the selected protocol level doesn't support it.\n"));
+ return False;
+ }
+
+ if (cli->protocol >= PROTOCOL_NT1) {
+ /* NT protocol */
+ cli->sec_mode = CVAL(cli->inbuf,smb_vwv1);
+ cli->max_mux = SVAL(cli->inbuf, smb_vwv1+1);
+ cli->max_xmit = IVAL(cli->inbuf,smb_vwv3+1);
+ cli->sesskey = IVAL(cli->inbuf,smb_vwv7+1);
+ cli->serverzone = SVALS(cli->inbuf,smb_vwv15+1);
+ cli->serverzone *= 60;
+ /* this time arrives in real GMT */
+ cli->servertime = interpret_long_date(cli->inbuf+smb_vwv11+1);
+ cli->secblob = data_blob(smb_buf(cli->inbuf),smb_buflen(cli->inbuf));
+ cli->capabilities = IVAL(cli->inbuf,smb_vwv9+1);
+ if (cli->capabilities & CAP_RAW_MODE) {
+ cli->readbraw_supported = True;
+ cli->writebraw_supported = True;
+ }
+ /* work out if they sent us a workgroup */
+ if (!(cli->capabilities & CAP_EXTENDED_SECURITY) &&
+ smb_buflen(cli->inbuf) > 8) {
+ clistr_pull(cli, cli->server_domain,
+ smb_buf(cli->inbuf)+8, sizeof(cli->server_domain),
+ smb_buflen(cli->inbuf)-8, STR_UNICODE|STR_NOALIGN);
+ }
+
+ /*
+ * As signing is slow we only turn it on if either the client or
+ * the server require it. JRA.
+ */
+
+ if (cli->sec_mode & NEGOTIATE_SECURITY_SIGNATURES_REQUIRED) {
+ /* Fail if server says signing is mandatory and we don't want to support it. */
+ if (!cli->sign_info.allow_smb_signing) {
+ DEBUG(0,("cli_negprot: SMB signing is mandatory and we have disabled it.\n"));
+ return False;
+ }
+ cli->sign_info.negotiated_smb_signing = True;
+ cli->sign_info.mandatory_signing = True;
+ } else if (cli->sign_info.mandatory_signing && cli->sign_info.allow_smb_signing) {
+ /* Fail if client says signing is mandatory and the server doesn't support it. */
+ if (!(cli->sec_mode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED)) {
+ DEBUG(1,("cli_negprot: SMB signing is mandatory and the server doesn't support it.\n"));
+ return False;
+ }
+ cli->sign_info.negotiated_smb_signing = True;
+ cli->sign_info.mandatory_signing = True;
+ } else if (cli->sign_info.allow_smb_signing && cli->sec_mode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED) {
+ cli->sign_info.negotiated_smb_signing = True;
+ }
+
+ } else if (cli->protocol >= PROTOCOL_LANMAN1) {
+ cli->use_spnego = False;
+ cli->sec_mode = SVAL(cli->inbuf,smb_vwv1);
+ cli->max_xmit = SVAL(cli->inbuf,smb_vwv2);
+ cli->sesskey = IVAL(cli->inbuf,smb_vwv6);
+ cli->serverzone = SVALS(cli->inbuf,smb_vwv10);
+ cli->serverzone *= 60;
+ /* this time is converted to GMT by make_unix_date */
+ cli->servertime = make_unix_date(cli->inbuf+smb_vwv8);
+ cli->readbraw_supported = ((SVAL(cli->inbuf,smb_vwv5) & 0x1) != 0);
+ cli->writebraw_supported = ((SVAL(cli->inbuf,smb_vwv5) & 0x2) != 0);
+ cli->secblob = data_blob(smb_buf(cli->inbuf),smb_buflen(cli->inbuf));
+ } else {
+ /* the old core protocol */
+ cli->use_spnego = False;
+ cli->sec_mode = 0;
+ cli->serverzone = TimeDiff(time(NULL));
+ }
+
+ cli->max_xmit = MIN(cli->max_xmit, CLI_BUFFER_SIZE);
+
+ /* a way to force ascii SMB */
+ if (getenv("CLI_FORCE_ASCII"))
+ cli->capabilities &= ~CAP_UNICODE;
+
+ return True;
+}
+
+/****************************************************************************
+ Send a session request. See rfc1002.txt 4.3 and 4.3.2.
+****************************************************************************/
+
+BOOL cli_session_request(struct cli_state *cli,
+ struct nmb_name *calling, struct nmb_name *called)
+{
+ char *p;
+ int len = 4;
+ extern pstring user_socket_options;
+
+ memcpy(&(cli->calling), calling, sizeof(*calling));
+ memcpy(&(cli->called ), called , sizeof(*called ));
+
+ /* put in the destination name */
+ p = cli->outbuf+len;
+ name_mangle(cli->called .name, p, cli->called .name_type);
+ len += name_len(p);
+
+ /* and my name */
+ p = cli->outbuf+len;
+ name_mangle(cli->calling.name, p, cli->calling.name_type);
+ len += name_len(p);
+
+ /* 445 doesn't have session request */
+ if (cli->port == 445)
+ return True;
+
+ /* send a session request (RFC 1002) */
+ /* setup the packet length
+ * Remove four bytes from the length count, since the length
+ * field in the NBT Session Service header counts the number
+ * of bytes which follow. The cli_send_smb() function knows
+ * about this and accounts for those four bytes.
+ * CRH.
+ */
+ len -= 4;
+ _smb_setlen(cli->outbuf,len);
+ SCVAL(cli->outbuf,0,0x81);
+
+ cli_send_smb(cli);
+ DEBUG(5,("Sent session request\n"));
+
+ if (!cli_receive_smb(cli))
+ return False;
+
+ if (CVAL(cli->inbuf,0) == 0x84) {
+ /* C. Hoch 9/14/95 Start */
+ /* For information, here is the response structure.
+ * We do the byte-twiddling to for portability.
+ struct RetargetResponse{
+ unsigned char type;
+ unsigned char flags;
+ int16 length;
+ int32 ip_addr;
+ int16 port;
+ };
+ */
+ int port = (CVAL(cli->inbuf,8)<<8)+CVAL(cli->inbuf,9);
+ /* SESSION RETARGET */
+ putip((char *)&cli->dest_ip,cli->inbuf+4);
+
+ cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip, port, LONG_CONNECT_TIMEOUT);
+ if (cli->fd == -1)
+ return False;
+
+ DEBUG(3,("Retargeted\n"));
+
+ set_socket_options(cli->fd,user_socket_options);
+
+ /* Try again */
+ {
+ static int depth;
+ BOOL ret;
+ if (depth > 4) {
+ DEBUG(0,("Retarget recursion - failing\n"));
+ return False;
+ }
+ depth++;
+ ret = cli_session_request(cli, calling, called);
+ depth--;
+ return ret;
+ }
+ } /* C. Hoch 9/14/95 End */
+
+ if (CVAL(cli->inbuf,0) != 0x82) {
+ /* This is the wrong place to put the error... JRA. */
+ cli->rap_error = CVAL(cli->inbuf,4);
+ return False;
+ }
+ return(True);
+}
+
+/****************************************************************************
+ Open the client sockets.
+****************************************************************************/
+
+BOOL cli_connect(struct cli_state *cli, const char *host, struct in_addr *ip)
+{
+ extern pstring user_socket_options;
+ int name_type = 0x20;
+ char *p;
+
+ /* reasonable default hostname */
+ if (!host) host = "*SMBSERVER";
+
+ fstrcpy(cli->desthost, host);
+
+ /* allow hostnames of the form NAME#xx and do a netbios lookup */
+ if ((p = strchr(cli->desthost, '#'))) {
+ name_type = strtol(p+1, NULL, 16);
+ *p = 0;
+ }
+
+ if (!ip || is_zero_ip(*ip)) {
+ if (!resolve_name(cli->desthost, &cli->dest_ip, name_type)) {
+ return False;
+ }
+ if (ip) *ip = cli->dest_ip;
+ } else {
+ cli->dest_ip = *ip;
+ }
+
+ if (getenv("LIBSMB_PROG")) {
+ cli->fd = sock_exec(getenv("LIBSMB_PROG"));
+ } else {
+ /* try 445 first, then 139 */
+ int port = cli->port?cli->port:445;
+ cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip,
+ port, cli->timeout);
+ if (cli->fd == -1 && cli->port == 0) {
+ port = 139;
+ cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip,
+ port, cli->timeout);
+ }
+ if (cli->fd != -1)
+ cli->port = port;
+ }
+ if (cli->fd == -1) {
+ DEBUG(1,("Error connecting to %s (%s)\n",
+ ip?inet_ntoa(*ip):host,strerror(errno)));
+ return False;
+ }
+
+ set_socket_options(cli->fd,user_socket_options);
+
+ return True;
+}
+
+/****************************************************************************
+ Initialise client credentials for authenticated pipe access.
+****************************************************************************/
+
+void init_creds(struct ntuser_creds *creds, const char* username,
+ const char* domain, const char* password)
+{
+ ZERO_STRUCTP(creds);
+
+ pwd_set_cleartext(&creds->pwd, password);
+
+ fstrcpy(creds->user_name, username);
+ fstrcpy(creds->domain, domain);
+
+ if (!*username) {
+ creds->pwd.null_pwd = True;
+ }
+}
+
+/**
+ establishes a connection to after the negprot.
+ @param output_cli A fully initialised cli structure, non-null only on success
+ @param dest_host The netbios name of the remote host
+ @param dest_ip (optional) The the destination IP, NULL for name based lookup
+ @param port (optional) The destination port (0 for default)
+ @param retry BOOL. Did this connection fail with a retryable error ?
+
+*/
+NTSTATUS cli_start_connection(struct cli_state **output_cli,
+ const char *my_name,
+ const char *dest_host,
+ struct in_addr *dest_ip, int port,
+ int signing_state, int flags,
+ BOOL *retry)
+{
+ NTSTATUS nt_status;
+ struct nmb_name calling;
+ struct nmb_name called;
+ struct cli_state *cli;
+ struct in_addr ip;
+
+ if (retry)
+ *retry = False;
+
+ if (!my_name)
+ my_name = global_myname();
+
+ if (!(cli = cli_initialise(NULL)))
+ return NT_STATUS_NO_MEMORY;
+
+ make_nmb_name(&calling, my_name, 0x0);
+ make_nmb_name(&called , dest_host, 0x20);
+
+ if (cli_set_port(cli, port) != port) {
+ cli_shutdown(cli);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ cli_set_timeout(cli, 10000); /* 10 seconds. */
+
+ if (dest_ip)
+ ip = *dest_ip;
+ else
+ ZERO_STRUCT(ip);
+
+again:
+
+ DEBUG(3,("Connecting to host=%s\n", dest_host));
+
+ if (!cli_connect(cli, dest_host, &ip)) {
+ DEBUG(1,("cli_full_connection: failed to connect to %s (%s)\n",
+ nmb_namestr(&called), inet_ntoa(ip)));
+ cli_shutdown(cli);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (retry)
+ *retry = True;
+
+ if (!cli_session_request(cli, &calling, &called)) {
+ char *p;
+ DEBUG(1,("session request to %s failed (%s)\n",
+ called.name, cli_errstr(cli)));
+ if ((p=strchr(called.name, '.')) && !is_ipaddress(called.name)) {
+ *p = 0;
+ goto again;
+ }
+ if (strcmp(called.name, "*SMBSERVER")) {
+ make_nmb_name(&called , "*SMBSERVER", 0x20);
+ goto again;
+ }
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ cli_setup_signing_state(cli, signing_state);
+
+ if (flags & CLI_FULL_CONNECTION_DONT_SPNEGO)
+ cli->use_spnego = False;
+ else if (flags & CLI_FULL_CONNECTION_USE_KERBEROS)
+ cli->use_kerberos = True;
+
+ if (!cli_negprot(cli)) {
+ DEBUG(1,("failed negprot\n"));
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ cli_shutdown(cli);
+ return nt_status;
+ }
+
+ *output_cli = cli;
+ return NT_STATUS_OK;
+}
+
+
+/**
+ establishes a connection right up to doing tconX, password specified.
+ @param output_cli A fully initialised cli structure, non-null only on success
+ @param dest_host The netbios name of the remote host
+ @param dest_ip (optional) The the destination IP, NULL for name based lookup
+ @param port (optional) The destination port (0 for default)
+ @param service (optional) The share to make the connection to. Should be 'unqualified' in any way.
+ @param service_type The 'type' of serivice.
+ @param user Username, unix string
+ @param domain User's domain
+ @param password User's password, unencrypted unix string.
+ @param retry BOOL. Did this connection fail with a retryable error ?
+*/
+
+NTSTATUS cli_full_connection(struct cli_state **output_cli,
+ const char *my_name,
+ const char *dest_host,
+ struct in_addr *dest_ip, int port,
+ const char *service, const char *service_type,
+ const char *user, const char *domain,
+ const char *password, int flags,
+ int signing_state,
+ BOOL *retry)
+{
+ struct ntuser_creds creds;
+ NTSTATUS nt_status;
+ struct cli_state *cli = NULL;
+
+ nt_status = cli_start_connection(&cli, my_name, dest_host,
+ dest_ip, port, signing_state, flags, retry);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ if (!cli_session_setup(cli, user, password, strlen(password)+1,
+ password, strlen(password)+1,
+ domain)) {
+ if ((flags & CLI_FULL_CONNECTION_ANNONYMOUS_FALLBACK)
+ && cli_session_setup(cli, "", "", 0, "", 0, domain)) {
+ } else {
+ nt_status = cli_nt_error(cli);
+ DEBUG(1,("failed session setup with %s\n", nt_errstr(nt_status)));
+ cli_shutdown(cli);
+ if (NT_STATUS_IS_OK(nt_status))
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ return nt_status;
+ }
+ }
+
+ if (service) {
+ if (!cli_send_tconX(cli, service, service_type,
+ password, strlen(password)+1)) {
+ nt_status = cli_nt_error(cli);
+ DEBUG(1,("failed tcon_X with %s\n", nt_errstr(nt_status)));
+ cli_shutdown(cli);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ }
+ return nt_status;
+ }
+ }
+
+ init_creds(&creds, user, domain, password);
+ cli_init_creds(cli, &creds);
+
+ *output_cli = cli;
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Attempt a NetBIOS session request, falling back to *SMBSERVER if needed.
+****************************************************************************/
+
+BOOL attempt_netbios_session_request(struct cli_state *cli, const char *srchost, const char *desthost,
+ struct in_addr *pdest_ip)
+{
+ struct nmb_name calling, called;
+
+ make_nmb_name(&calling, srchost, 0x0);
+
+ /*
+ * If the called name is an IP address
+ * then use *SMBSERVER immediately.
+ */
+
+ if(is_ipaddress(desthost))
+ make_nmb_name(&called, "*SMBSERVER", 0x20);
+ else
+ make_nmb_name(&called, desthost, 0x20);
+
+ if (!cli_session_request(cli, &calling, &called)) {
+ struct nmb_name smbservername;
+
+ make_nmb_name(&smbservername , "*SMBSERVER", 0x20);
+
+ /*
+ * If the name wasn't *SMBSERVER then
+ * try with *SMBSERVER if the first name fails.
+ */
+
+ if (nmb_name_equal(&called, &smbservername)) {
+
+ /*
+ * The name used was *SMBSERVER, don't bother with another name.
+ */
+
+ DEBUG(0,("attempt_netbios_session_request: %s rejected the session for name *SMBSERVER \
+with error %s.\n", desthost, cli_errstr(cli) ));
+ return False;
+ }
+
+ /*
+ * We need to close the connection here but can't call cli_shutdown as
+ * will free an allocated cli struct. cli_close_connection was invented
+ * for this purpose. JRA. Based on work by "Kim R. Pedersen" <krp@filanet.dk>.
+ */
+
+ cli_close_connection(cli);
+
+ if (!cli_initialise(cli) ||
+ !cli_connect(cli, desthost, pdest_ip) ||
+ !cli_session_request(cli, &calling, &smbservername)) {
+ DEBUG(0,("attempt_netbios_session_request: %s rejected the session for \
+name *SMBSERVER with error %s\n", desthost, cli_errstr(cli) ));
+ return False;
+ }
+ }
+
+ return True;
+}
+
+
+
+
+
+/****************************************************************************
+ Send an old style tcon.
+****************************************************************************/
+NTSTATUS cli_raw_tcon(struct cli_state *cli,
+ const char *service, const char *pass, const char *dev,
+ uint16 *max_xmit, uint16 *tid)
+{
+ char *p;
+
+ if (!lp_client_plaintext_auth() && (*pass)) {
+ DEBUG(1, ("Server requested plaintext password but 'client use plaintext auth'"
+ " is disabled\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf, 0, 0, True);
+ SCVAL(cli->outbuf,smb_com,SMBtcon);
+ cli_setup_packet(cli);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4; p += clistr_push(cli, p, service, -1, STR_TERMINATE | STR_NOALIGN);
+ *p++ = 4; p += clistr_push(cli, p, pass, -1, STR_TERMINATE | STR_NOALIGN);
+ *p++ = 4; p += clistr_push(cli, p, dev, -1, STR_TERMINATE | STR_NOALIGN);
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+
+ if (cli_is_error(cli)) {
+ return cli_nt_error(cli);
+ }
+
+ *max_xmit = SVAL(cli->inbuf, smb_vwv0);
+ *tid = SVAL(cli->inbuf, smb_vwv1);
+
+ return NT_STATUS_OK;
+}
+
+/* Return a cli_state pointing at the IPC$ share for the given server */
+
+struct cli_state *get_ipc_connect(char *server, struct in_addr *server_ip,
+ struct user_auth_info *user_info)
+{
+ struct cli_state *cli;
+ pstring myname;
+ NTSTATUS nt_status;
+
+ get_myname(myname);
+
+ nt_status = cli_full_connection(&cli, myname, server, server_ip, 0, "IPC$", "IPC",
+ user_info->username, lp_workgroup(), user_info->password,
+ CLI_FULL_CONNECTION_ANNONYMOUS_FALLBACK, Undefined, NULL);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ return cli;
+ } else if (is_ipaddress(server)) {
+ /* windows 9* needs a correct NMB name for connections */
+ fstring remote_name;
+
+ if (name_status_find("*", 0, 0, *server_ip, remote_name)) {
+ cli = get_ipc_connect(remote_name, server_ip, user_info);
+ if (cli)
+ return cli;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Given the IP address of a master browser on the network, return its
+ * workgroup and connect to it.
+ *
+ * This function is provided to allow additional processing beyond what
+ * get_ipc_connect_master_ip_bcast() does, e.g. to retrieve the list of master
+ * browsers and obtain each master browsers' list of domains (in case the
+ * first master browser is recently on the network and has not yet
+ * synchronized with other master browsers and therefore does not yet have the
+ * entire network browse list)
+ */
+
+struct cli_state *get_ipc_connect_master_ip(struct ip_service * mb_ip, pstring workgroup, struct user_auth_info *user_info)
+{
+ static fstring name;
+ struct cli_state *cli;
+ struct in_addr server_ip;
+
+ DEBUG(99, ("Looking up name of master browser %s\n",
+ inet_ntoa(mb_ip->ip)));
+
+ /*
+ * Do a name status query to find out the name of the master browser.
+ * We use <01><02>__MSBROWSE__<02>#01 if *#00 fails because a domain
+ * master browser will not respond to a wildcard query (or, at least,
+ * an NT4 server acting as the domain master browser will not).
+ *
+ * We might be able to use ONLY the query on MSBROWSE, but that's not
+ * yet been tested with all Windows versions, so until it is, leave
+ * the original wildcard query as the first choice and fall back to
+ * MSBROWSE if the wildcard query fails.
+ */
+ if (!name_status_find("*", 0, 0x1d, mb_ip->ip, name) &&
+ !name_status_find(MSBROWSE, 1, 0x1d, mb_ip->ip, name)) {
+
+ DEBUG(99, ("Could not retrieve name status for %s\n",
+ inet_ntoa(mb_ip->ip)));
+ return NULL;
+ }
+
+ if (!find_master_ip(name, &server_ip)) {
+ DEBUG(99, ("Could not find master ip for %s\n", name));
+ return NULL;
+ }
+
+ pstrcpy(workgroup, name);
+
+ DEBUG(4, ("found master browser %s, %s\n",
+ name, inet_ntoa(mb_ip->ip)));
+
+ cli = get_ipc_connect(inet_ntoa(server_ip), &server_ip, user_info);
+
+ return cli;
+
+}
+
+/*
+ * Return the IP address and workgroup of a master browser on the network, and
+ * connect to it.
+ */
+
+struct cli_state *get_ipc_connect_master_ip_bcast(pstring workgroup, struct user_auth_info *user_info)
+{
+ struct ip_service *ip_list;
+ struct cli_state *cli;
+ int i, count;
+
+ DEBUG(99, ("Do broadcast lookup for workgroups on local network\n"));
+
+ /* Go looking for workgroups by broadcasting on the local network */
+
+ if (!name_resolve_bcast(MSBROWSE, 1, &ip_list, &count)) {
+ DEBUG(99, ("No master browsers responded\n"));
+ return False;
+ }
+
+ for (i = 0; i < count; i++) {
+ DEBUG(99, ("Found master browser %s\n", inet_ntoa(ip_list[i].ip)));
+
+ cli = get_ipc_connect_master_ip(&ip_list[i], workgroup, user_info);
+ if (cli)
+ return(cli);
+ }
+
+ return NULL;
+}
diff --git a/source/libsmb/clidgram.c b/source/libsmb/clidgram.c
new file mode 100644
index 00000000000..c4675f1938a
--- /dev/null
+++ b/source/libsmb/clidgram.c
@@ -0,0 +1,264 @@
+/*
+ Unix SMB/CIFS implementation.
+ client dgram calls
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Richard Sharpe 2001
+ Copyright (C) John Terpstra 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*
+ * cli_send_mailslot, send a mailslot for client code ...
+ */
+
+int cli_send_mailslot(int dgram_sock, BOOL unique, const char *mailslot,
+ char *buf, int len,
+ const char *srcname, int src_type,
+ const char *dstname, int dest_type,
+ struct in_addr dest_ip, struct in_addr src_ip,
+ int dest_port, int src_port)
+{
+ struct packet_struct p;
+ struct dgram_packet *dgram = &p.packet.dgram;
+ char *ptr, *p2;
+ char tmp[4];
+
+ memset((char *)&p, '\0', sizeof(p));
+
+ /*
+ * Next, build the DGRAM ...
+ */
+
+ /* DIRECT GROUP or UNIQUE datagram. */
+ dgram->header.msg_type = unique ? 0x10 : 0x11;
+ dgram->header.flags.node_type = M_NODE;
+ dgram->header.flags.first = True;
+ dgram->header.flags.more = False;
+ dgram->header.dgm_id = ((unsigned)time(NULL)%(unsigned)0x7FFF) + ((unsigned)sys_getpid()%(unsigned)100);
+ dgram->header.source_ip.s_addr = src_ip.s_addr;
+ dgram->header.source_port = ntohs(src_port);
+ dgram->header.dgm_length = 0; /* Let build_dgram() handle this. */
+ dgram->header.packet_offset = 0;
+
+ make_nmb_name(&dgram->source_name,srcname,src_type);
+ make_nmb_name(&dgram->dest_name,dstname,dest_type);
+
+ ptr = &dgram->data[0];
+
+ /* Setup the smb part. */
+ ptr -= 4; /* XXX Ugliness because of handling of tcp SMB length. */
+ memcpy(tmp,ptr,4);
+ set_message(ptr,17,17 + len,True);
+ memcpy(ptr,tmp,4);
+
+ SCVAL(ptr,smb_com,SMBtrans);
+ SSVAL(ptr,smb_vwv1,len);
+ SSVAL(ptr,smb_vwv11,len);
+ SSVAL(ptr,smb_vwv12,70 + strlen(mailslot));
+ SSVAL(ptr,smb_vwv13,3);
+ SSVAL(ptr,smb_vwv14,1);
+ SSVAL(ptr,smb_vwv15,1);
+ SSVAL(ptr,smb_vwv16,2);
+ p2 = smb_buf(ptr);
+ fstrcpy(p2,mailslot);
+ p2 = skip_string(p2,1);
+
+ memcpy(p2,buf,len);
+ p2 += len;
+
+ dgram->datasize = PTR_DIFF(p2,ptr+4); /* +4 for tcp length. */
+
+ p.ip = dest_ip;
+ p.port = dest_port;
+ p.fd = dgram_sock;
+ p.timestamp = time(NULL);
+ p.packet_type = DGRAM_PACKET;
+
+ DEBUG(4,("send_mailslot: Sending to mailslot %s from %s IP %s ", mailslot,
+ nmb_namestr(&dgram->source_name), inet_ntoa(src_ip)));
+ DEBUG(4,("to %s IP %s\n", nmb_namestr(&dgram->dest_name), inet_ntoa(dest_ip)));
+
+ return send_packet(&p);
+
+}
+
+/*
+ * cli_get_response: Get a response ...
+ */
+int cli_get_response(int dgram_sock, BOOL unique, const char *mailslot, char *buf, int bufsiz)
+{
+ struct packet_struct *packet;
+
+ packet = receive_dgram_packet(dgram_sock, 5, mailslot);
+
+ if (packet) { /* We got one, pull what we want out of the SMB data ... */
+
+ struct dgram_packet *dgram = &packet->packet.dgram;
+
+ /*
+ * We should probably parse the SMB, but for now, we will pull what
+ * from fixed, known locations ...
+ */
+
+ /* Copy the data to buffer, respecting sizes ... */
+
+ memcpy(buf, &dgram->data[92], MIN(bufsiz, (dgram->datasize - 92)));
+
+ }
+ else
+ return -1;
+
+ return 0;
+
+}
+
+/*
+ * cli_get_backup_list: Send a get backup list request ...
+ */
+
+static char cli_backup_list[1024];
+
+int cli_get_backup_list(const char *myname, const char *send_to_name)
+{
+ pstring outbuf;
+ char *p;
+ struct in_addr sendto_ip, my_ip;
+ int dgram_sock;
+ struct sockaddr_in sock_out;
+ socklen_t name_size;
+
+ if (!resolve_name(send_to_name, &sendto_ip, 0x1d)) {
+
+ DEBUG(0, ("Could not resolve name: %s<1D>\n", send_to_name));
+ return False;
+
+ }
+
+ my_ip.s_addr = inet_addr("0.0.0.0");
+
+ if (!resolve_name(myname, &my_ip, 0x00)) { /* FIXME: Call others here */
+
+ DEBUG(0, ("Could not resolve name: %s<00>\n", myname));
+
+ }
+
+ if ((dgram_sock = open_socket_out(SOCK_DGRAM, &sendto_ip, 138, LONG_CONNECT_TIMEOUT)) < 0) {
+
+ DEBUG(4, ("open_sock_out failed ..."));
+ return False;
+
+ }
+
+ /* Make it a broadcast socket ... */
+
+ set_socket_options(dgram_sock, "SO_BROADCAST");
+
+ /* Make it non-blocking??? */
+
+ if (fcntl(dgram_sock, F_SETFL, O_NONBLOCK) < 0) {
+
+ DEBUG(0, ("Unable to set non blocking on dgram sock\n"));
+
+ }
+
+ /* Now, bind a local addr to it ... Try port 138 first ... */
+
+ memset((char *)&sock_out, '\0', sizeof(sock_out));
+ sock_out.sin_addr.s_addr = INADDR_ANY;
+ sock_out.sin_port = htons(138);
+ sock_out.sin_family = AF_INET;
+
+ if (bind(dgram_sock, (struct sockaddr *)&sock_out, sizeof(sock_out)) < 0) {
+
+ /* Try again on any port ... */
+
+ sock_out.sin_port = INADDR_ANY;
+
+ if (bind(dgram_sock, (struct sockaddr *)&sock_out, sizeof(sock_out)) < 0) {
+
+ DEBUG(4, ("failed to bind socket to address ...\n"));
+ return False;
+
+ }
+
+ }
+
+ /* Now, figure out what socket name we were bound to. We want the port */
+
+ name_size = sizeof(sock_out);
+
+ getsockname(dgram_sock, (struct sockaddr *)&sock_out, &name_size);
+
+ DEBUG(5, ("Socket bound to IP:%s, port: %d\n", inet_ntoa(sock_out.sin_addr), ntohs(sock_out.sin_port)));
+
+ /* Now, build the request */
+
+ memset(cli_backup_list, '\0', sizeof(cli_backup_list));
+ memset(outbuf, '\0', sizeof(outbuf));
+
+ p = outbuf;
+
+ SCVAL(p, 0, ANN_GetBackupListReq);
+ p++;
+
+ SCVAL(p, 0, 1); /* Count pointer ... */
+ p++;
+
+ SIVAL(p, 0, 1); /* The sender's token ... */
+ p += 4;
+
+ cli_send_mailslot(dgram_sock, True, "\\MAILSLOT\\BROWSE", outbuf,
+ PTR_DIFF(p, outbuf), myname, 0, send_to_name,
+ 0x1d, sendto_ip, my_ip, 138, sock_out.sin_port);
+
+ /* We should check the error and return if we got one */
+
+ /* Now, get the response ... */
+
+ cli_get_response(dgram_sock, True, "\\MAILSLOT\\BROWSE", cli_backup_list, sizeof(cli_backup_list));
+
+ /* Should check the response here ... FIXME */
+
+ close(dgram_sock);
+
+ return True;
+
+}
+
+/*
+ * cli_get_backup_server: Get the backup list and retrieve a server from it
+ */
+
+int cli_get_backup_server(char *my_name, char *target, char *servername, int namesize)
+{
+
+ /* Get the backup list first. We could pull this from the cache later */
+
+ cli_get_backup_list(my_name, target); /* FIXME: Check the response */
+
+ if (!cli_backup_list[0]) { /* Empty list ... try again */
+
+ cli_get_backup_list(my_name, target);
+
+ }
+
+ strncpy(servername, cli_backup_list, MIN(16, namesize));
+
+ return True;
+
+}
diff --git a/source/libsmb/clientgen.c b/source/libsmb/clientgen.c
new file mode 100644
index 00000000000..66edc3ce38b
--- /dev/null
+++ b/source/libsmb/clientgen.c
@@ -0,0 +1,455 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client generic functions
+ Copyright (C) Andrew Tridgell 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+/****************************************************************************
+ Change the timeout (in milliseconds).
+****************************************************************************/
+
+unsigned int cli_set_timeout(struct cli_state *cli, unsigned int timeout)
+{
+ unsigned int old_timeout = cli->timeout;
+ cli->timeout = timeout;
+ return old_timeout;
+}
+
+/****************************************************************************
+ Change the port number used to call on.
+****************************************************************************/
+
+int cli_set_port(struct cli_state *cli, int port)
+{
+ cli->port = port;
+ return port;
+}
+
+/****************************************************************************
+ Read an smb from a fd ignoring all keepalive packets. Note that the buffer
+ *MUST* be of size BUFFER_SIZE+SAFETY_MARGIN.
+ The timeout is in milliseconds
+
+ This is exactly the same as receive_smb except that it never returns
+ a session keepalive packet (just as receive_smb used to do).
+ receive_smb was changed to return keepalives as the oplock processing means this call
+ should never go into a blocking read.
+****************************************************************************/
+
+static BOOL client_receive_smb(int fd,char *buffer, unsigned int timeout)
+{
+ BOOL ret;
+
+ for(;;) {
+ ret = receive_smb_raw(fd, buffer, timeout);
+
+ if (!ret) {
+ DEBUG(10,("client_receive_smb failed\n"));
+ show_msg(buffer);
+ return ret;
+ }
+
+ /* Ignore session keepalive packets. */
+ if(CVAL(buffer,0) != SMBkeepalive)
+ break;
+ }
+ show_msg(buffer);
+ return ret;
+}
+
+/****************************************************************************
+ Recv an smb.
+****************************************************************************/
+
+BOOL cli_receive_smb(struct cli_state *cli)
+{
+ extern int smb_read_error;
+ BOOL ret;
+
+ /* fd == -1 causes segfaults -- Tom (tom@ninja.nl) */
+ if (cli->fd == -1)
+ return False;
+
+ again:
+ ret = client_receive_smb(cli->fd,cli->inbuf,cli->timeout);
+
+ if (ret) {
+ /* it might be an oplock break request */
+ if (!(CVAL(cli->inbuf, smb_flg) & FLAG_REPLY) &&
+ CVAL(cli->inbuf,smb_com) == SMBlockingX &&
+ SVAL(cli->inbuf,smb_vwv6) == 0 &&
+ SVAL(cli->inbuf,smb_vwv7) == 0) {
+ if (cli->oplock_handler) {
+ int fnum = SVAL(cli->inbuf,smb_vwv2);
+ unsigned char level = CVAL(cli->inbuf,smb_vwv3+1);
+ if (!cli->oplock_handler(cli, fnum, level)) return False;
+ }
+ /* try to prevent loops */
+ SCVAL(cli->inbuf,smb_com,0xFF);
+ goto again;
+ }
+ }
+
+ /* If the server is not responding, note that now */
+
+ if (!ret) {
+ cli->smb_rw_error = smb_read_error;
+ close(cli->fd);
+ cli->fd = -1;
+ return ret;
+ }
+
+ if (!cli_check_sign_mac(cli, True)) {
+ DEBUG(0, ("SMB Signature verification failed on incoming packet!\n"));
+ cli->smb_rw_error = READ_BAD_SIG;
+ close(cli->fd);
+ cli->fd = -1;
+ return False;
+ };
+ return True;
+}
+
+/****************************************************************************
+ Send an smb to a fd.
+****************************************************************************/
+
+BOOL cli_send_smb(struct cli_state *cli)
+{
+ size_t len;
+ size_t nwritten=0;
+ ssize_t ret;
+
+ /* fd == -1 causes segfaults -- Tom (tom@ninja.nl) */
+ if (cli->fd == -1)
+ return False;
+
+ cli_calculate_sign_mac(cli);
+
+ len = smb_len(cli->outbuf) + 4;
+
+ while (nwritten < len) {
+ ret = write_socket(cli->fd,cli->outbuf+nwritten,len - nwritten);
+ if (ret <= 0) {
+ close(cli->fd);
+ cli->fd = -1;
+ cli->smb_rw_error = WRITE_ERROR;
+ DEBUG(0,("Error writing %d bytes to client. %d (%s)\n",
+ (int)len,(int)ret, strerror(errno) ));
+ return False;
+ }
+ nwritten += ret;
+ }
+ /* Increment the mid so we can tell between responses. */
+ cli->mid++;
+ if (!cli->mid)
+ cli->mid++;
+ return True;
+}
+
+/****************************************************************************
+ Setup basics in a outgoing packet.
+****************************************************************************/
+
+void cli_setup_packet(struct cli_state *cli)
+{
+ cli->rap_error = 0;
+ SSVAL(cli->outbuf,smb_pid,cli->pid);
+ SSVAL(cli->outbuf,smb_uid,cli->vuid);
+ SSVAL(cli->outbuf,smb_mid,cli->mid);
+ if (cli->protocol > PROTOCOL_CORE) {
+ uint16 flags2;
+ SCVAL(cli->outbuf,smb_flg,0x8);
+ flags2 = FLAGS2_LONG_PATH_COMPONENTS;
+ if (cli->capabilities & CAP_UNICODE)
+ flags2 |= FLAGS2_UNICODE_STRINGS;
+ if (cli->capabilities & CAP_STATUS32)
+ flags2 |= FLAGS2_32_BIT_ERROR_CODES;
+ if (cli->use_spnego)
+ flags2 |= FLAGS2_EXTENDED_SECURITY;
+ SSVAL(cli->outbuf,smb_flg2, flags2);
+ }
+}
+
+/****************************************************************************
+ Setup the bcc length of the packet from a pointer to the end of the data.
+****************************************************************************/
+
+void cli_setup_bcc(struct cli_state *cli, void *p)
+{
+ set_message_bcc(cli->outbuf, PTR_DIFF(p, smb_buf(cli->outbuf)));
+}
+
+/****************************************************************************
+ Initialise credentials of a client structure.
+****************************************************************************/
+
+void cli_init_creds(struct cli_state *cli, const struct ntuser_creds *usr)
+{
+ /* copy_nt_creds(&cli->usr, usr); */
+ fstrcpy(cli->domain , usr->domain);
+ fstrcpy(cli->user_name, usr->user_name);
+ memcpy(&cli->pwd, &usr->pwd, sizeof(usr->pwd));
+
+ DEBUG(10,("cli_init_creds: user %s domain %s\n",
+ cli->user_name, cli->domain));
+}
+
+/****************************************************************************
+ Set the signing state (used from the command line).
+****************************************************************************/
+
+void cli_setup_signing_state(struct cli_state *cli, int signing_state)
+{
+ if (signing_state == Undefined)
+ return;
+
+ if (signing_state == False) {
+ cli->sign_info.allow_smb_signing = False;
+ cli->sign_info.mandatory_signing = False;
+ return;
+ }
+
+ cli->sign_info.allow_smb_signing = True;
+
+ if (signing_state == Required)
+ cli->sign_info.mandatory_signing = True;
+}
+
+/****************************************************************************
+ Initialise a client structure.
+****************************************************************************/
+
+struct cli_state *cli_initialise(struct cli_state *cli)
+{
+ BOOL alloced_cli = False;
+
+ /* Check the effective uid - make sure we are not setuid */
+ if (is_setuid_root()) {
+ DEBUG(0,("libsmb based programs must *NOT* be setuid root.\n"));
+ return NULL;
+ }
+
+ if (!cli) {
+ cli = (struct cli_state *)malloc(sizeof(*cli));
+ if (!cli)
+ return NULL;
+ ZERO_STRUCTP(cli);
+ alloced_cli = True;
+ }
+
+ if (cli->initialised)
+ cli_close_connection(cli);
+
+ ZERO_STRUCTP(cli);
+
+ cli->port = 0;
+ cli->fd = -1;
+ cli->cnum = -1;
+ cli->pid = (uint16)sys_getpid();
+ cli->mid = 1;
+ cli->vuid = UID_FIELD_INVALID;
+ cli->protocol = PROTOCOL_NT1;
+ cli->timeout = 20000; /* Timeout is in milliseconds. */
+ cli->bufsize = CLI_BUFFER_SIZE+4;
+ cli->max_xmit = cli->bufsize;
+ cli->outbuf = (char *)malloc(cli->bufsize+SAFETY_MARGIN);
+ cli->inbuf = (char *)malloc(cli->bufsize+SAFETY_MARGIN);
+ cli->oplock_handler = cli_oplock_ack;
+
+ cli->use_spnego = lp_client_use_spnego();
+
+ cli->capabilities = CAP_UNICODE | CAP_STATUS32;
+
+ /* Set the CLI_FORCE_DOSERR environment variable to test
+ client routines using DOS errors instead of STATUS32
+ ones. This intended only as a temporary hack. */
+ if (getenv("CLI_FORCE_DOSERR"))
+ cli->force_dos_errors = True;
+
+ if (lp_client_signing())
+ cli->sign_info.allow_smb_signing = True;
+
+ if (lp_client_signing() == Required)
+ cli->sign_info.mandatory_signing = True;
+
+ if (!cli->outbuf || !cli->inbuf)
+ goto error;
+
+ if ((cli->mem_ctx = talloc_init("cli based talloc")) == NULL)
+ goto error;
+
+ memset(cli->outbuf, 0, cli->bufsize);
+ memset(cli->inbuf, 0, cli->bufsize);
+
+
+#if defined(DEVELOPER)
+ /* just because we over-allocate, doesn't mean it's right to use it */
+ clobber_region(FUNCTION_MACRO, __LINE__, cli->outbuf+cli->bufsize, SAFETY_MARGIN);
+ clobber_region(FUNCTION_MACRO, __LINE__, cli->inbuf+cli->bufsize, SAFETY_MARGIN);
+#endif
+
+ /* initialise signing */
+ cli_null_set_signing(cli);
+
+ cli->nt_pipe_fnum = 0;
+ cli->saved_netlogon_pipe_fnum = 0;
+
+ cli->initialised = 1;
+ cli->allocated = alloced_cli;
+
+ cli->pipe_idx = -1;
+
+ return cli;
+
+ /* Clean up after malloc() error */
+
+ error:
+
+ SAFE_FREE(cli->inbuf);
+ SAFE_FREE(cli->outbuf);
+
+ if (alloced_cli)
+ SAFE_FREE(cli);
+
+ return NULL;
+}
+
+/****************************************************************************
+close the session
+****************************************************************************/
+
+void cli_nt_session_close(struct cli_state *cli)
+{
+ if (cli->ntlmssp_pipe_state) {
+ ntlmssp_end(&cli->ntlmssp_pipe_state);
+ }
+
+ if (cli->nt_pipe_fnum != 0)
+ cli_close(cli, cli->nt_pipe_fnum);
+
+ cli->nt_pipe_fnum = 0;
+ cli->pipe_idx = -1;
+}
+
+/****************************************************************************
+close the NETLOGON session holding the session key for NETSEC
+****************************************************************************/
+
+void cli_nt_netlogon_netsec_session_close(struct cli_state *cli)
+{
+ if (cli->saved_netlogon_pipe_fnum != 0) {
+ cli_close(cli, cli->saved_netlogon_pipe_fnum);
+ cli->saved_netlogon_pipe_fnum = 0;
+ }
+}
+
+/****************************************************************************
+ Close a client connection and free the memory without destroying cli itself.
+****************************************************************************/
+
+void cli_close_connection(struct cli_state *cli)
+{
+ /*
+ * tell our peer to free his resources. Wihtout this, when an
+ * application attempts to do a graceful shutdown and calls
+ * smbc_free_context() to clean up all connections, some connections
+ * can remain active on the peer end, until some (long) timeout period
+ * later. This tree disconnect forces the peer to clean up, since the
+ * connection will be going away.
+ */
+ if ( cli->cnum != (uint16)-1 )
+ cli_tdis(cli);
+
+ cli_nt_session_close(cli);
+ cli_nt_netlogon_netsec_session_close(cli);
+
+ SAFE_FREE(cli->outbuf);
+ SAFE_FREE(cli->inbuf);
+
+ cli_free_signing_context(cli);
+ data_blob_free(&cli->secblob);
+ data_blob_free(&cli->user_session_key);
+
+ if (cli->ntlmssp_pipe_state)
+ ntlmssp_end(&cli->ntlmssp_pipe_state);
+
+ if (cli->mem_ctx) {
+ talloc_destroy(cli->mem_ctx);
+ cli->mem_ctx = NULL;
+ }
+
+ if (cli->fd != -1)
+ close(cli->fd);
+ cli->fd = -1;
+ cli->smb_rw_error = 0;
+
+}
+
+/****************************************************************************
+ Shutdown a client structure.
+****************************************************************************/
+
+void cli_shutdown(struct cli_state *cli)
+{
+ BOOL allocated = cli->allocated;
+ cli_close_connection(cli);
+ ZERO_STRUCTP(cli);
+ if (allocated)
+ free(cli);
+}
+
+/****************************************************************************
+ Set socket options on a open connection.
+****************************************************************************/
+
+void cli_sockopt(struct cli_state *cli, const char *options)
+{
+ set_socket_options(cli->fd, options);
+}
+
+/****************************************************************************
+ Set the PID to use for smb messages. Return the old pid.
+****************************************************************************/
+
+uint16 cli_setpid(struct cli_state *cli, uint16 pid)
+{
+ uint16 ret = cli->pid;
+ cli->pid = pid;
+ return ret;
+}
+
+/****************************************************************************
+Send a keepalive packet to the server
+****************************************************************************/
+BOOL cli_send_keepalive(struct cli_state *cli)
+{
+ if (cli->fd == -1) {
+ DEBUG(3, ("cli_send_keepalive: fd == -1\n"));
+ return False;
+ }
+ if (!send_keepalive(cli->fd)) {
+ close(cli->fd);
+ cli->fd = -1;
+ DEBUG(0,("Error sending keepalive packet to client.\n"));
+ return False;
+ }
+ return True;
+}
diff --git a/source/libsmb/clierror.c b/source/libsmb/clierror.c
new file mode 100644
index 00000000000..c27e1955e20
--- /dev/null
+++ b/source/libsmb/clierror.c
@@ -0,0 +1,405 @@
+/*
+ Unix SMB/CIFS implementation.
+ client error handling routines
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Jelmer Vernooij 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+/*****************************************************
+ RAP error codes - a small start but will be extended.
+
+ XXX: Perhaps these should move into a common function because they're
+ duplicated in clirap2.c
+
+*******************************************************/
+
+static const struct
+{
+ int err;
+ const char *message;
+} rap_errmap[] =
+{
+ {5, "RAP5: User has insufficient privilege" },
+ {50, "RAP50: Not supported by server" },
+ {65, "RAP65: Access denied" },
+ {86, "RAP86: The specified password is invalid" },
+ {2220, "RAP2220: Group does not exist" },
+ {2221, "RAP2221: User does not exist" },
+ {2226, "RAP2226: Operation only permitted on a Primary Domain Controller" },
+ {2237, "RAP2237: User is not in group" },
+ {2242, "RAP2242: The password of this user has expired." },
+ {2243, "RAP2243: The password of this user cannot change." },
+ {2244, "RAP2244: This password cannot be used now (password history conflict)." },
+ {2245, "RAP2245: The password is shorter than required." },
+ {2246, "RAP2246: The password of this user is too recent to change."},
+
+ /* these really shouldn't be here ... */
+ {0x80, "Not listening on called name"},
+ {0x81, "Not listening for calling name"},
+ {0x82, "Called name not present"},
+ {0x83, "Called name present, but insufficient resources"},
+
+ {0, NULL}
+};
+
+/****************************************************************************
+ return a description of an SMB error
+****************************************************************************/
+static const char *cli_smb_errstr(struct cli_state *cli)
+{
+ return smb_dos_errstr(cli->inbuf);
+}
+
+/***************************************************************************
+ Return an error message - either an NT error, SMB error or a RAP error.
+ Note some of the NT errors are actually warnings or "informational" errors
+ in which case they can be safely ignored.
+****************************************************************************/
+
+const char *cli_errstr(struct cli_state *cli)
+{
+ static fstring cli_error_message;
+ uint32 flgs2 = SVAL(cli->inbuf,smb_flg2), errnum;
+ uint8 errclass;
+ int i;
+
+ if (!cli->initialised) {
+ fstrcpy(cli_error_message, "[Programmer's error] cli_errstr called on unitialized cli_stat struct!\n");
+ return cli_error_message;
+ }
+
+ /* Was it server socket error ? */
+ if (cli->fd == -1 && cli->smb_rw_error) {
+ switch(cli->smb_rw_error) {
+ case READ_TIMEOUT:
+ slprintf(cli_error_message, sizeof(cli_error_message) - 1,
+ "Call timed out: server did not respond after %d milliseconds",
+ cli->timeout);
+ break;
+ case READ_EOF:
+ slprintf(cli_error_message, sizeof(cli_error_message) - 1,
+ "Call returned zero bytes (EOF)" );
+ break;
+ case READ_ERROR:
+ slprintf(cli_error_message, sizeof(cli_error_message) - 1,
+ "Read error: %s", strerror(errno) );
+ break;
+ case WRITE_ERROR:
+ slprintf(cli_error_message, sizeof(cli_error_message) - 1,
+ "Write error: %s", strerror(errno) );
+ break;
+ case READ_BAD_SIG:
+ slprintf(cli_error_message, sizeof(cli_error_message) - 1,
+ "Server packet had invalid SMB signature!");
+ break;
+ default:
+ slprintf(cli_error_message, sizeof(cli_error_message) - 1,
+ "Unknown error code %d\n", cli->smb_rw_error );
+ break;
+ }
+ return cli_error_message;
+ }
+
+ /* Case #1: RAP error */
+ if (cli->rap_error) {
+ for (i = 0; rap_errmap[i].message != NULL; i++) {
+ if (rap_errmap[i].err == cli->rap_error) {
+ return rap_errmap[i].message;
+ }
+ }
+
+ slprintf(cli_error_message, sizeof(cli_error_message) - 1, "RAP code %d",
+ cli->rap_error);
+
+ return cli_error_message;
+ }
+
+ /* Case #2: 32-bit NT errors */
+ if (flgs2 & FLAGS2_32_BIT_ERROR_CODES) {
+ NTSTATUS status = NT_STATUS(IVAL(cli->inbuf,smb_rcls));
+
+ return nt_errstr(status);
+ }
+
+ cli_dos_error(cli, &errclass, &errnum);
+
+ /* Case #3: SMB error */
+
+ return cli_smb_errstr(cli);
+}
+
+
+/* Return the 32-bit NT status code from the last packet */
+NTSTATUS cli_nt_error(struct cli_state *cli)
+{
+ int flgs2 = SVAL(cli->inbuf,smb_flg2);
+
+ if (!(flgs2 & FLAGS2_32_BIT_ERROR_CODES)) {
+ int class = CVAL(cli->inbuf,smb_rcls);
+ int code = SVAL(cli->inbuf,smb_err);
+ return dos_to_ntstatus(class, code);
+ }
+
+ return NT_STATUS(IVAL(cli->inbuf,smb_rcls));
+}
+
+
+/* Return the DOS error from the last packet - an error class and an error
+ code. */
+void cli_dos_error(struct cli_state *cli, uint8 *eclass, uint32 *ecode)
+{
+ int flgs2;
+ char rcls;
+ int code;
+
+ if(!cli->initialised) return;
+
+ flgs2 = SVAL(cli->inbuf,smb_flg2);
+
+ if (flgs2 & FLAGS2_32_BIT_ERROR_CODES) {
+ NTSTATUS ntstatus = NT_STATUS(IVAL(cli->inbuf, smb_rcls));
+ ntstatus_to_dos(ntstatus, eclass, ecode);
+ return;
+ }
+
+ rcls = CVAL(cli->inbuf,smb_rcls);
+ code = SVAL(cli->inbuf,smb_err);
+
+ if (eclass) *eclass = rcls;
+ if (ecode) *ecode = code;
+}
+
+/* Return a UNIX errno from a dos error class, error number tuple */
+
+static int cli_errno_from_dos(uint8 eclass, uint32 num)
+{
+ if (eclass == ERRDOS) {
+ switch (num) {
+ case ERRbadfile: return ENOENT;
+ case ERRbadpath: return ENOTDIR;
+ case ERRnoaccess: return EACCES;
+ case ERRfilexists: return EEXIST;
+ case ERRrename: return EEXIST;
+ case ERRbadshare: return EBUSY;
+ case ERRlock: return EBUSY;
+ case ERRinvalidname: return ENOENT;
+ case ERRnosuchshare: return ENODEV;
+ }
+ }
+
+ if (eclass == ERRSRV) {
+ switch (num) {
+ case ERRbadpw: return EPERM;
+ case ERRaccess: return EACCES;
+ case ERRnoresource: return ENOMEM;
+ case ERRinvdevice: return ENODEV;
+ case ERRinvnetname: return ENODEV;
+ }
+ }
+
+ /* for other cases */
+ return EINVAL;
+}
+
+/* Return a UNIX errno from a NT status code */
+static struct {
+ NTSTATUS status;
+ int error;
+} nt_errno_map[] = {
+ {NT_STATUS_ACCESS_VIOLATION, EACCES},
+ {NT_STATUS_INVALID_HANDLE, EBADF},
+ {NT_STATUS_ACCESS_DENIED, EACCES},
+ {NT_STATUS_OBJECT_NAME_NOT_FOUND, ENOENT},
+ {NT_STATUS_SHARING_VIOLATION, EBUSY},
+ {NT_STATUS_OBJECT_PATH_INVALID, ENOTDIR},
+ {NT_STATUS_OBJECT_NAME_COLLISION, EEXIST},
+ {NT_STATUS_PATH_NOT_COVERED, ENOENT},
+ {NT_STATUS_UNSUCCESSFUL, EINVAL},
+ {NT_STATUS_NOT_IMPLEMENTED, ENOSYS},
+ {NT_STATUS_IN_PAGE_ERROR, EFAULT},
+ {NT_STATUS_BAD_NETWORK_NAME, ENOENT},
+#ifdef EDQUOT
+ {NT_STATUS_PAGEFILE_QUOTA, EDQUOT},
+ {NT_STATUS_QUOTA_EXCEEDED, EDQUOT},
+ {NT_STATUS_REGISTRY_QUOTA_LIMIT, EDQUOT},
+ {NT_STATUS_LICENSE_QUOTA_EXCEEDED, EDQUOT},
+#endif
+#ifdef ETIME
+ {NT_STATUS_TIMER_NOT_CANCELED, ETIME},
+#endif
+ {NT_STATUS_INVALID_PARAMETER, EINVAL},
+ {NT_STATUS_NO_SUCH_DEVICE, ENODEV},
+ {NT_STATUS_NO_SUCH_FILE, ENOENT},
+#ifdef ENODATA
+ {NT_STATUS_END_OF_FILE, ENODATA},
+#endif
+#ifdef ENOMEDIUM
+ {NT_STATUS_NO_MEDIA_IN_DEVICE, ENOMEDIUM},
+ {NT_STATUS_NO_MEDIA, ENOMEDIUM},
+#endif
+ {NT_STATUS_NONEXISTENT_SECTOR, ESPIPE},
+ {NT_STATUS_NO_MEMORY, ENOMEM},
+ {NT_STATUS_CONFLICTING_ADDRESSES, EADDRINUSE},
+ {NT_STATUS_NOT_MAPPED_VIEW, EINVAL},
+ {NT_STATUS_UNABLE_TO_FREE_VM, EADDRINUSE},
+ {NT_STATUS_ACCESS_DENIED, EACCES},
+ {NT_STATUS_BUFFER_TOO_SMALL, ENOBUFS},
+ {NT_STATUS_WRONG_PASSWORD, EACCES},
+ {NT_STATUS_LOGON_FAILURE, EACCES},
+ {NT_STATUS_INVALID_WORKSTATION, EACCES},
+ {NT_STATUS_INVALID_LOGON_HOURS, EACCES},
+ {NT_STATUS_PASSWORD_EXPIRED, EACCES},
+ {NT_STATUS_ACCOUNT_DISABLED, EACCES},
+ {NT_STATUS_DISK_FULL, ENOSPC},
+ {NT_STATUS_INVALID_PIPE_STATE, EPIPE},
+ {NT_STATUS_PIPE_BUSY, EPIPE},
+ {NT_STATUS_PIPE_DISCONNECTED, EPIPE},
+ {NT_STATUS_PIPE_NOT_AVAILABLE, ENOSYS},
+ {NT_STATUS_FILE_IS_A_DIRECTORY, EISDIR},
+ {NT_STATUS_NOT_SUPPORTED, ENOSYS},
+ {NT_STATUS_NOT_A_DIRECTORY, ENOTDIR},
+ {NT_STATUS_DIRECTORY_NOT_EMPTY, ENOTEMPTY},
+ {NT_STATUS_NETWORK_UNREACHABLE, ENETUNREACH},
+ {NT_STATUS_HOST_UNREACHABLE, EHOSTUNREACH},
+ {NT_STATUS_CONNECTION_ABORTED, ECONNABORTED},
+ {NT_STATUS_CONNECTION_REFUSED, ECONNREFUSED},
+ {NT_STATUS_TOO_MANY_LINKS, EMLINK},
+ {NT_STATUS_NETWORK_BUSY, EBUSY},
+ {NT_STATUS_DEVICE_DOES_NOT_EXIST, ENODEV},
+#ifdef ELIBACC
+ {NT_STATUS_DLL_NOT_FOUND, ELIBACC},
+#endif
+ {NT_STATUS_PIPE_BROKEN, EPIPE},
+ {NT_STATUS_REMOTE_NOT_LISTENING, ECONNREFUSED},
+ {NT_STATUS_NETWORK_ACCESS_DENIED, EACCES},
+ {NT_STATUS_TOO_MANY_OPENED_FILES, EMFILE},
+#ifdef EPROTO
+ {NT_STATUS_DEVICE_PROTOCOL_ERROR, EPROTO},
+#endif
+ {NT_STATUS_FLOAT_OVERFLOW, ERANGE},
+ {NT_STATUS_FLOAT_UNDERFLOW, ERANGE},
+ {NT_STATUS_INTEGER_OVERFLOW, ERANGE},
+ {NT_STATUS_MEDIA_WRITE_PROTECTED, EROFS},
+ {NT_STATUS_PIPE_CONNECTED, EISCONN},
+ {NT_STATUS_MEMORY_NOT_ALLOCATED, EFAULT},
+ {NT_STATUS_FLOAT_INEXACT_RESULT, ERANGE},
+ {NT_STATUS_ILL_FORMED_PASSWORD, EACCES},
+ {NT_STATUS_PASSWORD_RESTRICTION, EACCES},
+ {NT_STATUS_ACCOUNT_RESTRICTION, EACCES},
+ {NT_STATUS_PORT_CONNECTION_REFUSED, ECONNREFUSED},
+ {NT_STATUS_NAME_TOO_LONG, ENAMETOOLONG},
+ {NT_STATUS_REMOTE_DISCONNECT, ESHUTDOWN},
+ {NT_STATUS_CONNECTION_DISCONNECTED, ECONNABORTED},
+ {NT_STATUS_CONNECTION_RESET, ENETRESET},
+#ifdef ENOTUNIQ
+ {NT_STATUS_IP_ADDRESS_CONFLICT1, ENOTUNIQ},
+ {NT_STATUS_IP_ADDRESS_CONFLICT2, ENOTUNIQ},
+#endif
+ {NT_STATUS_PORT_MESSAGE_TOO_LONG, EMSGSIZE},
+ {NT_STATUS_PROTOCOL_UNREACHABLE, ENOPROTOOPT},
+ {NT_STATUS_ADDRESS_ALREADY_EXISTS, EADDRINUSE},
+ {NT_STATUS_PORT_UNREACHABLE, EHOSTUNREACH},
+ {NT_STATUS_IO_TIMEOUT, ETIMEDOUT},
+ {NT_STATUS_RETRY, EAGAIN},
+#ifdef ECOMM
+ {NT_STATUS_NET_WRITE_FAULT, ECOMM},
+#endif
+
+ {NT_STATUS(0), 0}
+};
+
+static int cli_errno_from_nt(NTSTATUS status)
+{
+ int i;
+ DEBUG(10,("cli_errno_from_nt: 32 bit codes: code=%08x\n", NT_STATUS_V(status)));
+
+ /* Status codes without this bit set are not errors */
+
+ if (!(NT_STATUS_V(status) & 0xc0000000))
+ return 0;
+
+ for (i=0;nt_errno_map[i].error;i++) {
+ if (NT_STATUS_V(nt_errno_map[i].status) ==
+ NT_STATUS_V(status)) return nt_errno_map[i].error;
+ }
+
+ /* for all other cases - a default code */
+ return EINVAL;
+}
+
+/* Return a UNIX errno appropriate for the error received in the last
+ packet. */
+
+int cli_errno(struct cli_state *cli)
+{
+ NTSTATUS status;
+
+ if (cli_is_dos_error(cli)) {
+ uint8 eclass;
+ uint32 ecode;
+
+ cli_dos_error(cli, &eclass, &ecode);
+ return cli_errno_from_dos(eclass, ecode);
+ }
+
+ status = cli_nt_error(cli);
+
+ return cli_errno_from_nt(status);
+}
+
+/* Return true if the last packet was in error */
+
+BOOL cli_is_error(struct cli_state *cli)
+{
+ uint32 flgs2 = SVAL(cli->inbuf,smb_flg2), rcls = 0;
+
+ if (cli->fd == -1 && cli->smb_rw_error != 0)
+ return True;
+
+ if (flgs2 & FLAGS2_32_BIT_ERROR_CODES) {
+ /* Return error is error bits are set */
+ rcls = IVAL(cli->inbuf, smb_rcls);
+ return (rcls & 0xF0000000) == 0xC0000000;
+ }
+
+ /* Return error if error class in non-zero */
+
+ rcls = CVAL(cli->inbuf, smb_rcls);
+ return rcls != 0;
+}
+
+/* Return true if the last error was an NT error */
+
+BOOL cli_is_nt_error(struct cli_state *cli)
+{
+ uint32 flgs2 = SVAL(cli->inbuf,smb_flg2);
+
+ return cli_is_error(cli) && (flgs2 & FLAGS2_32_BIT_ERROR_CODES);
+}
+
+/* Return true if the last error was a DOS error */
+
+BOOL cli_is_dos_error(struct cli_state *cli)
+{
+ uint32 flgs2 = SVAL(cli->inbuf,smb_flg2);
+
+ return cli_is_error(cli) && !(flgs2 & FLAGS2_32_BIT_ERROR_CODES);
+}
diff --git a/source/libsmb/clifile.c b/source/libsmb/clifile.c
new file mode 100644
index 00000000000..bf7923ec788
--- /dev/null
+++ b/source/libsmb/clifile.c
@@ -0,0 +1,1417 @@
+/*
+ Unix SMB/CIFS implementation.
+ client file operations
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Jeremy Allison 2001-2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+/****************************************************************************
+ Hard/Symlink a file (UNIX extensions).
+****************************************************************************/
+
+static BOOL cli_link_internal(struct cli_state *cli, const char *fname_src, const char *fname_dst, BOOL hard_link)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ uint16 setup = TRANSACT2_SETPATHINFO;
+ char param[sizeof(pstring)+6];
+ pstring data;
+ char *rparam=NULL, *rdata=NULL;
+ char *p;
+ size_t srclen = 2*(strlen(fname_src)+1);
+ size_t destlen = 2*(strlen(fname_dst) + 1);
+
+ memset(param, 0, sizeof(param));
+ SSVAL(param,0,hard_link ? SMB_SET_FILE_UNIX_HLINK : SMB_SET_FILE_UNIX_LINK);
+ p = &param[6];
+
+ p += clistr_push(cli, p, fname_src, MIN(srclen, sizeof(param)-6), STR_TERMINATE);
+ param_len = PTR_DIFF(p, param);
+
+ p = data;
+ p += clistr_push(cli, p, fname_dst, MIN(destlen,sizeof(data)), STR_TERMINATE);
+ data_len = PTR_DIFF(p, data);
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL, /* name */
+ -1, 0, /* fid, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 2, /* param, length, max */
+ (char *)&data, data_len, cli->max_xmit /* data, length, max */
+ )) {
+ return False;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ return False;
+ }
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ return True;
+}
+
+/****************************************************************************
+ Map standard UNIX permissions onto wire representations.
+****************************************************************************/
+
+uint32 unix_perms_to_wire(mode_t perms)
+{
+ unsigned int ret = 0;
+
+ ret |= ((perms & S_IXOTH) ? UNIX_X_OTH : 0);
+ ret |= ((perms & S_IWOTH) ? UNIX_W_OTH : 0);
+ ret |= ((perms & S_IROTH) ? UNIX_R_OTH : 0);
+ ret |= ((perms & S_IXGRP) ? UNIX_X_GRP : 0);
+ ret |= ((perms & S_IWGRP) ? UNIX_W_GRP : 0);
+ ret |= ((perms & S_IRGRP) ? UNIX_R_GRP : 0);
+ ret |= ((perms & S_IXUSR) ? UNIX_X_USR : 0);
+ ret |= ((perms & S_IWUSR) ? UNIX_W_USR : 0);
+ ret |= ((perms & S_IRUSR) ? UNIX_R_USR : 0);
+#ifdef S_ISVTX
+ ret |= ((perms & S_ISVTX) ? UNIX_STICKY : 0);
+#endif
+#ifdef S_ISGID
+ ret |= ((perms & S_ISGID) ? UNIX_SET_GID : 0);
+#endif
+#ifdef S_ISUID
+ ret |= ((perms & S_ISUID) ? UNIX_SET_UID : 0);
+#endif
+ return ret;
+}
+
+/****************************************************************************
+ Symlink a file (UNIX extensions).
+****************************************************************************/
+
+BOOL cli_unix_symlink(struct cli_state *cli, const char *fname_src, const char *fname_dst)
+{
+ return cli_link_internal(cli, fname_src, fname_dst, False);
+}
+
+/****************************************************************************
+ Hard a file (UNIX extensions).
+****************************************************************************/
+
+BOOL cli_unix_hardlink(struct cli_state *cli, const char *fname_src, const char *fname_dst)
+{
+ return cli_link_internal(cli, fname_src, fname_dst, True);
+}
+
+/****************************************************************************
+ Chmod or chown a file internal (UNIX extensions).
+****************************************************************************/
+
+static BOOL cli_unix_chmod_chown_internal(struct cli_state *cli, const char *fname, uint32 mode, uint32 uid, uint32 gid)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ uint16 setup = TRANSACT2_SETPATHINFO;
+ char param[sizeof(pstring)+6];
+ char data[100];
+ char *rparam=NULL, *rdata=NULL;
+ char *p;
+
+ memset(param, 0, sizeof(param));
+ memset(data, 0, sizeof(data));
+ SSVAL(param,0,SMB_SET_FILE_UNIX_BASIC);
+ p = &param[6];
+
+ p += clistr_push(cli, p, fname, -1, STR_TERMINATE);
+ param_len = PTR_DIFF(p, param);
+
+ SIVAL(data,40,uid);
+ SIVAL(data,48,gid);
+ SIVAL(data,84,mode);
+
+ data_len = 100;
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL, /* name */
+ -1, 0, /* fid, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 2, /* param, length, max */
+ (char *)&data, data_len, cli->max_xmit /* data, length, max */
+ )) {
+ return False;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ return False;
+ }
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ return True;
+}
+
+/****************************************************************************
+ chmod a file (UNIX extensions).
+****************************************************************************/
+
+BOOL cli_unix_chmod(struct cli_state *cli, const char *fname, mode_t mode)
+{
+ return cli_unix_chmod_chown_internal(cli, fname,
+ unix_perms_to_wire(mode), SMB_UID_NO_CHANGE, SMB_GID_NO_CHANGE);
+}
+
+/****************************************************************************
+ chown a file (UNIX extensions).
+****************************************************************************/
+
+BOOL cli_unix_chown(struct cli_state *cli, const char *fname, uid_t uid, gid_t gid)
+{
+ return cli_unix_chmod_chown_internal(cli, fname, SMB_MODE_NO_CHANGE, (uint32)uid, (uint32)gid);
+}
+
+/****************************************************************************
+ Rename a file.
+****************************************************************************/
+
+BOOL cli_rename(struct cli_state *cli, const char *fname_src, const char *fname_dst)
+{
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,1, 0, True);
+
+ SCVAL(cli->outbuf,smb_com,SMBmv);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,aSYSTEM | aHIDDEN | aDIR);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ p += clistr_push(cli, p, fname_src, -1, STR_TERMINATE);
+ *p++ = 4;
+ p += clistr_push(cli, p, fname_dst, -1, STR_TERMINATE);
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli))
+ return False;
+
+ if (cli_is_error(cli))
+ return False;
+
+ return True;
+}
+
+/****************************************************************************
+ NT Rename a file.
+****************************************************************************/
+
+BOOL cli_ntrename(struct cli_state *cli, const char *fname_src, const char *fname_dst)
+{
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf, 4, 0, True);
+
+ SCVAL(cli->outbuf,smb_com,SMBntrename);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,aSYSTEM | aHIDDEN | aDIR);
+ SSVAL(cli->outbuf,smb_vwv1, RENAME_FLAG_RENAME);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ p += clistr_push(cli, p, fname_src, -1, STR_TERMINATE);
+ *p++ = 4;
+ p += clistr_push(cli, p, fname_dst, -1, STR_TERMINATE);
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli))
+ return False;
+
+ if (cli_is_error(cli))
+ return False;
+
+ return True;
+}
+
+/****************************************************************************
+ NT hardlink a file.
+****************************************************************************/
+
+BOOL cli_nt_hardlink(struct cli_state *cli, const char *fname_src, const char *fname_dst)
+{
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf, 4, 0, True);
+
+ SCVAL(cli->outbuf,smb_com,SMBntrename);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,aSYSTEM | aHIDDEN | aDIR);
+ SSVAL(cli->outbuf,smb_vwv1, RENAME_FLAG_HARD_LINK);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ p += clistr_push(cli, p, fname_src, -1, STR_TERMINATE);
+ *p++ = 4;
+ p += clistr_push(cli, p, fname_dst, -1, STR_TERMINATE);
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli))
+ return False;
+
+ if (cli_is_error(cli))
+ return False;
+
+ return True;
+}
+
+/****************************************************************************
+ Delete a file.
+****************************************************************************/
+
+BOOL cli_unlink(struct cli_state *cli, const char *fname)
+{
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,1, 0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBunlink);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,aSYSTEM | aHIDDEN);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ p += clistr_push(cli, p, fname, -1, STR_TERMINATE);
+
+ cli_setup_bcc(cli, p);
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ if (cli_is_error(cli)) {
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Create a directory.
+****************************************************************************/
+
+BOOL cli_mkdir(struct cli_state *cli, const char *dname)
+{
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,0, 0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBmkdir);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ p += clistr_push(cli, p, dname, -1, STR_TERMINATE);
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ if (cli_is_error(cli)) {
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Remove a directory.
+****************************************************************************/
+
+BOOL cli_rmdir(struct cli_state *cli, const char *dname)
+{
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,0, 0, True);
+
+ SCVAL(cli->outbuf,smb_com,SMBrmdir);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ p += clistr_push(cli, p, dname, -1, STR_TERMINATE);
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ if (cli_is_error(cli)) {
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Set or clear the delete on close flag.
+****************************************************************************/
+
+int cli_nt_delete_on_close(struct cli_state *cli, int fnum, BOOL flag)
+{
+ unsigned int data_len = 1;
+ unsigned int param_len = 6;
+ uint16 setup = TRANSACT2_SETFILEINFO;
+ pstring param;
+ unsigned char data;
+ char *rparam=NULL, *rdata=NULL;
+
+ memset(param, 0, param_len);
+ SSVAL(param,0,fnum);
+ SSVAL(param,2,SMB_SET_FILE_DISPOSITION_INFO);
+
+ data = flag ? 1 : 0;
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL, /* name */
+ -1, 0, /* fid, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 2, /* param, length, max */
+ (char *)&data, data_len, cli->max_xmit /* data, length, max */
+ )) {
+ return False;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ return False;
+ }
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ return True;
+}
+
+/****************************************************************************
+ Open a file - exposing the full horror of the NT API :-).
+ Used in smbtorture.
+****************************************************************************/
+
+int cli_nt_create_full(struct cli_state *cli, const char *fname,
+ uint32 CreatFlags, uint32 DesiredAccess,
+ uint32 FileAttributes, uint32 ShareAccess,
+ uint32 CreateDisposition, uint32 CreateOptions,
+ uint8 SecuityFlags)
+{
+ char *p;
+ int len;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,24,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBntcreateX);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,0xFF);
+ if (cli->use_oplocks)
+ CreatFlags |= (REQUEST_OPLOCK|REQUEST_BATCH_OPLOCK);
+
+ SIVAL(cli->outbuf,smb_ntcreate_Flags, CreatFlags);
+ SIVAL(cli->outbuf,smb_ntcreate_RootDirectoryFid, 0x0);
+ SIVAL(cli->outbuf,smb_ntcreate_DesiredAccess, DesiredAccess);
+ SIVAL(cli->outbuf,smb_ntcreate_FileAttributes, FileAttributes);
+ SIVAL(cli->outbuf,smb_ntcreate_ShareAccess, ShareAccess);
+ SIVAL(cli->outbuf,smb_ntcreate_CreateDisposition, CreateDisposition);
+ SIVAL(cli->outbuf,smb_ntcreate_CreateOptions, CreateOptions);
+ SIVAL(cli->outbuf,smb_ntcreate_ImpersonationLevel, 0x02);
+ SCVAL(cli->outbuf,smb_ntcreate_SecurityFlags, SecuityFlags);
+
+ p = smb_buf(cli->outbuf);
+ /* this alignment and termination is critical for netapp filers. Don't change */
+ p += clistr_align_out(cli, p, 0);
+ len = clistr_push(cli, p, fname, -1, 0);
+ p += len;
+ SSVAL(cli->outbuf,smb_ntcreate_NameLength, len);
+ /* sigh. this copes with broken netapp filer behaviour */
+ p += clistr_push(cli, p, "", -1, STR_TERMINATE);
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return -1;
+ }
+
+ if (cli_is_error(cli)) {
+ return -1;
+ }
+
+ return SVAL(cli->inbuf,smb_vwv2 + 1);
+}
+
+/****************************************************************************
+ Open a file.
+****************************************************************************/
+
+int cli_nt_create(struct cli_state *cli, const char *fname, uint32 DesiredAccess)
+{
+ return cli_nt_create_full(cli, fname, 0, DesiredAccess, 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_EXISTS_OPEN, 0x0, 0x0);
+}
+
+/****************************************************************************
+ Open a file
+ WARNING: if you open with O_WRONLY then getattrE won't work!
+****************************************************************************/
+
+int cli_open(struct cli_state *cli, const char *fname, int flags, int share_mode)
+{
+ char *p;
+ unsigned openfn=0;
+ unsigned accessmode=0;
+
+ if (flags & O_CREAT)
+ openfn |= (1<<4);
+ if (!(flags & O_EXCL)) {
+ if (flags & O_TRUNC)
+ openfn |= (1<<1);
+ else
+ openfn |= (1<<0);
+ }
+
+ accessmode = (share_mode<<4);
+
+ if ((flags & O_ACCMODE) == O_RDWR) {
+ accessmode |= 2;
+ } else if ((flags & O_ACCMODE) == O_WRONLY) {
+ accessmode |= 1;
+ }
+
+#if defined(O_SYNC)
+ if ((flags & O_SYNC) == O_SYNC) {
+ accessmode |= (1<<14);
+ }
+#endif /* O_SYNC */
+
+ if (share_mode == DENY_FCB) {
+ accessmode = 0xFF;
+ }
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,15,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBopenX);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,0); /* no additional info */
+ SSVAL(cli->outbuf,smb_vwv3,accessmode);
+ SSVAL(cli->outbuf,smb_vwv4,aSYSTEM | aHIDDEN);
+ SSVAL(cli->outbuf,smb_vwv5,0);
+ SSVAL(cli->outbuf,smb_vwv8,openfn);
+
+ if (cli->use_oplocks) {
+ /* if using oplocks then ask for a batch oplock via
+ core and extended methods */
+ SCVAL(cli->outbuf,smb_flg, CVAL(cli->outbuf,smb_flg)|
+ FLAG_REQUEST_OPLOCK|FLAG_REQUEST_BATCH_OPLOCK);
+ SSVAL(cli->outbuf,smb_vwv2,SVAL(cli->outbuf,smb_vwv2) | 6);
+ }
+
+ p = smb_buf(cli->outbuf);
+ p += clistr_push(cli, p, fname, -1, STR_TERMINATE);
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return -1;
+ }
+
+ if (cli_is_error(cli)) {
+ return -1;
+ }
+
+ return SVAL(cli->inbuf,smb_vwv2);
+}
+
+/****************************************************************************
+ Close a file.
+****************************************************************************/
+
+BOOL cli_close(struct cli_state *cli, int fnum)
+{
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,3,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBclose);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,fnum);
+ SIVALS(cli->outbuf,smb_vwv1,-1);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ return !cli_is_error(cli);
+}
+
+
+/****************************************************************************
+ send a lock with a specified locktype
+ this is used for testing LOCKING_ANDX_CANCEL_LOCK
+****************************************************************************/
+NTSTATUS cli_locktype(struct cli_state *cli, int fnum,
+ uint32 offset, uint32 len, int timeout, unsigned char locktype)
+{
+ char *p;
+ int saved_timeout = cli->timeout;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0', smb_size);
+
+ set_message(cli->outbuf,8,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBlockingX);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SCVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,fnum);
+ SCVAL(cli->outbuf,smb_vwv3,locktype);
+ SIVALS(cli->outbuf, smb_vwv4, timeout);
+ SSVAL(cli->outbuf,smb_vwv6,0);
+ SSVAL(cli->outbuf,smb_vwv7,1);
+
+ p = smb_buf(cli->outbuf);
+ SSVAL(p, 0, cli->pid);
+ SIVAL(p, 2, offset);
+ SIVAL(p, 6, len);
+
+ p += 10;
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+
+ if (timeout != 0) {
+ cli->timeout = (timeout == -1) ? 0x7FFFFFFF : (timeout + 2*1000);
+ }
+
+ if (!cli_receive_smb(cli)) {
+ cli->timeout = saved_timeout;
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ cli->timeout = saved_timeout;
+
+ return cli_nt_error(cli);
+}
+
+
+/****************************************************************************
+ Lock a file.
+ note that timeout is in units of 2 milliseconds
+****************************************************************************/
+BOOL cli_lock(struct cli_state *cli, int fnum,
+ uint32 offset, uint32 len, int timeout, enum brl_type lock_type)
+{
+ char *p;
+ int saved_timeout = cli->timeout;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0', smb_size);
+
+ set_message(cli->outbuf,8,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBlockingX);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SCVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,fnum);
+ SCVAL(cli->outbuf,smb_vwv3,(lock_type == READ_LOCK? 1 : 0));
+ SIVALS(cli->outbuf, smb_vwv4, timeout);
+ SSVAL(cli->outbuf,smb_vwv6,0);
+ SSVAL(cli->outbuf,smb_vwv7,1);
+
+ p = smb_buf(cli->outbuf);
+ SSVAL(p, 0, cli->pid);
+ SIVAL(p, 2, offset);
+ SIVAL(p, 6, len);
+
+ p += 10;
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+
+ if (timeout != 0) {
+ cli->timeout = (timeout == -1) ? 0x7FFFFFFF : (timeout*2 + 5*1000);
+ }
+
+ if (!cli_receive_smb(cli)) {
+ cli->timeout = saved_timeout;
+ return False;
+ }
+
+ cli->timeout = saved_timeout;
+
+ if (cli_is_error(cli)) {
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Unlock a file.
+****************************************************************************/
+
+BOOL cli_unlock(struct cli_state *cli, int fnum, uint32 offset, uint32 len)
+{
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,8,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBlockingX);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SCVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,fnum);
+ SCVAL(cli->outbuf,smb_vwv3,0);
+ SIVALS(cli->outbuf, smb_vwv4, 0);
+ SSVAL(cli->outbuf,smb_vwv6,1);
+ SSVAL(cli->outbuf,smb_vwv7,0);
+
+ p = smb_buf(cli->outbuf);
+ SSVAL(p, 0, cli->pid);
+ SIVAL(p, 2, offset);
+ SIVAL(p, 6, len);
+ p += 10;
+ cli_setup_bcc(cli, p);
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ if (cli_is_error(cli)) {
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Lock a file with 64 bit offsets.
+****************************************************************************/
+
+BOOL cli_lock64(struct cli_state *cli, int fnum,
+ SMB_BIG_UINT offset, SMB_BIG_UINT len, int timeout, enum brl_type lock_type)
+{
+ char *p;
+ int saved_timeout = cli->timeout;
+ int ltype;
+
+ if (! (cli->capabilities & CAP_LARGE_FILES)) {
+ return cli_lock(cli, fnum, offset, len, timeout, lock_type);
+ }
+
+ ltype = (lock_type == READ_LOCK? 1 : 0);
+ ltype |= LOCKING_ANDX_LARGE_FILES;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0', smb_size);
+
+ set_message(cli->outbuf,8,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBlockingX);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SCVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,fnum);
+ SCVAL(cli->outbuf,smb_vwv3,ltype);
+ SIVALS(cli->outbuf, smb_vwv4, timeout);
+ SSVAL(cli->outbuf,smb_vwv6,0);
+ SSVAL(cli->outbuf,smb_vwv7,1);
+
+ p = smb_buf(cli->outbuf);
+ SIVAL(p, 0, cli->pid);
+ SOFF_T_R(p, 4, offset);
+ SOFF_T_R(p, 12, len);
+ p += 20;
+
+ cli_setup_bcc(cli, p);
+ cli_send_smb(cli);
+
+ if (timeout != 0) {
+ cli->timeout = (timeout == -1) ? 0x7FFFFFFF : (timeout + 5*1000);
+ }
+
+ if (!cli_receive_smb(cli)) {
+ cli->timeout = saved_timeout;
+ return False;
+ }
+
+ cli->timeout = saved_timeout;
+
+ if (cli_is_error(cli)) {
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Unlock a file with 64 bit offsets.
+****************************************************************************/
+
+BOOL cli_unlock64(struct cli_state *cli, int fnum, SMB_BIG_UINT offset, SMB_BIG_UINT len)
+{
+ char *p;
+
+ if (! (cli->capabilities & CAP_LARGE_FILES)) {
+ return cli_unlock(cli, fnum, offset, len);
+ }
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,8,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBlockingX);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SCVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,fnum);
+ SCVAL(cli->outbuf,smb_vwv3,LOCKING_ANDX_LARGE_FILES);
+ SIVALS(cli->outbuf, smb_vwv4, 0);
+ SSVAL(cli->outbuf,smb_vwv6,1);
+ SSVAL(cli->outbuf,smb_vwv7,0);
+
+ p = smb_buf(cli->outbuf);
+ SIVAL(p, 0, cli->pid);
+ SOFF_T_R(p, 4, offset);
+ SOFF_T_R(p, 12, len);
+ p += 20;
+ cli_setup_bcc(cli, p);
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ if (cli_is_error(cli)) {
+ return False;
+ }
+
+ return True;
+}
+
+
+/****************************************************************************
+ Do a SMBgetattrE call.
+****************************************************************************/
+
+BOOL cli_getattrE(struct cli_state *cli, int fd,
+ uint16 *attr, SMB_BIG_UINT *size,
+ time_t *c_time, time_t *a_time, time_t *m_time)
+{
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,1,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBgetattrE);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,fd);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ if (cli_is_error(cli)) {
+ return False;
+ }
+
+ if (size) {
+ *size = IVAL(cli->inbuf, smb_vwv6);
+ }
+
+ if (attr) {
+ *attr = SVAL(cli->inbuf,smb_vwv10);
+ }
+
+ if (c_time) {
+ *c_time = make_unix_date3(cli->inbuf+smb_vwv0);
+ }
+
+ if (a_time) {
+ *a_time = make_unix_date3(cli->inbuf+smb_vwv2);
+ }
+
+ if (m_time) {
+ *m_time = make_unix_date3(cli->inbuf+smb_vwv4);
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Do a SMBgetatr call
+****************************************************************************/
+
+BOOL cli_getatr(struct cli_state *cli, const char *fname,
+ uint16 *attr, size_t *size, time_t *t)
+{
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,0,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBgetatr);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ p += clistr_push(cli, p, fname, -1, STR_TERMINATE);
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ if (cli_is_error(cli)) {
+ return False;
+ }
+
+ if (size) {
+ *size = IVAL(cli->inbuf, smb_vwv3);
+ }
+
+ if (t) {
+ *t = make_unix_date3(cli->inbuf+smb_vwv1);
+ }
+
+ if (attr) {
+ *attr = SVAL(cli->inbuf,smb_vwv0);
+ }
+
+
+ return True;
+}
+
+/****************************************************************************
+ Do a SMBsetatr call.
+****************************************************************************/
+
+BOOL cli_setatr(struct cli_state *cli, const char *fname, uint16 attr, time_t t)
+{
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,8,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBsetatr);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0, attr);
+ put_dos_date3(cli->outbuf,smb_vwv1, t);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ p += clistr_push(cli, p, fname, -1, STR_TERMINATE);
+ *p++ = 4;
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ if (cli_is_error(cli)) {
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Check for existance of a dir.
+****************************************************************************/
+BOOL cli_chkpath(struct cli_state *cli, const char *path)
+{
+ pstring path2;
+ char *p;
+
+ pstrcpy(path2,path);
+ trim_char(path2,'\0','\\');
+ if (!*path2)
+ *path2 = '\\';
+
+ memset(cli->outbuf,'\0',smb_size);
+ set_message(cli->outbuf,0,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBchkpth);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ p += clistr_push(cli, p, path2, -1, STR_TERMINATE);
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ if (cli_is_error(cli)) return False;
+
+ return True;
+}
+
+/****************************************************************************
+ Query disk space.
+****************************************************************************/
+
+BOOL cli_dskattr(struct cli_state *cli, int *bsize, int *total, int *avail)
+{
+ memset(cli->outbuf,'\0',smb_size);
+ set_message(cli->outbuf,0,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBdskattr);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ *bsize = SVAL(cli->inbuf,smb_vwv1)*SVAL(cli->inbuf,smb_vwv2);
+ *total = SVAL(cli->inbuf,smb_vwv0);
+ *avail = SVAL(cli->inbuf,smb_vwv3);
+
+ return True;
+}
+
+/****************************************************************************
+ Create and open a temporary file.
+****************************************************************************/
+
+int cli_ctemp(struct cli_state *cli, const char *path, char **tmp_path)
+{
+ int len;
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,3,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBctemp);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,0);
+ SIVALS(cli->outbuf,smb_vwv1,-1);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ p += clistr_push(cli, p, path, -1, STR_TERMINATE);
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return -1;
+ }
+
+ if (cli_is_error(cli)) {
+ return -1;
+ }
+
+ /* despite the spec, the result has a -1, followed by
+ length, followed by name */
+ p = smb_buf(cli->inbuf);
+ p += 4;
+ len = smb_buflen(cli->inbuf) - 4;
+ if (len <= 0) return -1;
+
+ if (tmp_path) {
+ pstring path2;
+ clistr_pull(cli, path2, p,
+ sizeof(path2), len, STR_ASCII);
+ *tmp_path = strdup(path2);
+ }
+
+ return SVAL(cli->inbuf,smb_vwv0);
+}
+
+
+/*
+ send a raw ioctl - used by the torture code
+*/
+NTSTATUS cli_raw_ioctl(struct cli_state *cli, int fnum, uint32 code, DATA_BLOB *blob)
+{
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf, 3, 0, True);
+ SCVAL(cli->outbuf,smb_com,SMBioctl);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf, smb_vwv0, fnum);
+ SSVAL(cli->outbuf, smb_vwv1, code>>16);
+ SSVAL(cli->outbuf, smb_vwv2, (code&0xFFFF));
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+
+ if (cli_is_error(cli)) {
+ return cli_nt_error(cli);
+ }
+
+ *blob = data_blob(NULL, 0);
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************
+ Set an extended attribute utility fn.
+*********************************************************/
+
+static BOOL cli_set_ea(struct cli_state *cli, uint16 setup, char *param, unsigned int param_len,
+ const char *ea_name, const char *ea_val, size_t ea_len)
+{
+ unsigned int data_len = 0;
+ char *data = NULL;
+ char *rparam=NULL, *rdata=NULL;
+ char *p;
+ size_t ea_namelen = strlen(ea_name);
+
+ data_len = 4 + 4 + ea_namelen + 1 + ea_len;
+ data = malloc(data_len);
+ if (!data) {
+ return False;
+ }
+ p = data;
+ SIVAL(p,0,data_len);
+ p += 4;
+ SCVAL(p, 0, 0); /* EA flags. */
+ SCVAL(p, 1, ea_namelen);
+ SSVAL(p, 2, ea_len);
+ memcpy(p+4, ea_name, ea_namelen+1); /* Copy in the name. */
+ memcpy(p+4+ea_namelen+1, ea_val, ea_len);
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL, /* name */
+ -1, 0, /* fid, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 2, /* param, length, max */
+ data, data_len, cli->max_xmit /* data, length, max */
+ )) {
+ return False;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ return False;
+ }
+
+ SAFE_FREE(data);
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ return True;
+}
+
+/*********************************************************
+ Set an extended attribute on a pathname.
+*********************************************************/
+
+BOOL cli_set_ea_path(struct cli_state *cli, const char *path, const char *ea_name, const char *ea_val, size_t ea_len)
+{
+ uint16 setup = TRANSACT2_SETPATHINFO;
+ unsigned int param_len = 0;
+ char param[sizeof(pstring)+6];
+ size_t srclen = 2*(strlen(path)+1);
+ char *p;
+
+ memset(param, 0, sizeof(param));
+ SSVAL(param,0,SMB_INFO_SET_EA);
+ p = &param[6];
+
+ p += clistr_push(cli, p, path, MIN(srclen, sizeof(param)-6), STR_TERMINATE);
+ param_len = PTR_DIFF(p, param);
+
+ return cli_set_ea(cli, setup, param, param_len, ea_name, ea_val, ea_len);
+}
+
+/*********************************************************
+ Set an extended attribute on an fnum.
+*********************************************************/
+
+BOOL cli_set_ea_fnum(struct cli_state *cli, int fnum, const char *ea_name, const char *ea_val, size_t ea_len)
+{
+ char param[6];
+ uint16 setup = TRANSACT2_SETFILEINFO;
+
+ memset(param, 0, 6);
+ SSVAL(param,0,fnum);
+ SSVAL(param,2,SMB_INFO_SET_EA);
+
+ return cli_set_ea(cli, setup, param, 6, ea_name, ea_val, ea_len);
+}
+
+/*********************************************************
+ Get an extended attribute list tility fn.
+*********************************************************/
+
+static BOOL cli_get_ea_list(struct cli_state *cli,
+ uint16 setup, char *param, unsigned int param_len,
+ TALLOC_CTX *ctx,
+ size_t *pnum_eas,
+ struct ea_struct **pea_list)
+{
+ unsigned int data_len = 0;
+ unsigned int rparam_len, rdata_len;
+ char *rparam=NULL, *rdata=NULL;
+ char *p;
+ size_t ea_size;
+ size_t num_eas;
+ BOOL ret = False;
+ struct ea_struct *ea_list;
+
+ *pnum_eas = 0;
+ *pea_list = NULL;
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL, /* Name */
+ -1, 0, /* fid, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 10, /* param, length, max */
+ NULL, data_len, cli->max_xmit /* data, length, max */
+ )) {
+ return False;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &rparam_len,
+ &rdata, &rdata_len)) {
+ return False;
+ }
+
+ if (!rdata || rdata_len < 4) {
+ goto out;
+ }
+
+ ea_size = (size_t)IVAL(rdata,0);
+ if (ea_size > rdata_len) {
+ goto out;
+ }
+
+ if (ea_size == 0) {
+ /* No EA's present. */
+ ret = True;
+ goto out;
+ }
+
+ p = rdata + 4;
+ ea_size -= 4;
+
+ /* Validate the EA list and count it. */
+ for (num_eas = 0; ea_size >= 4; num_eas++) {
+ unsigned int ea_namelen = CVAL(p,1);
+ unsigned int ea_valuelen = SVAL(p,2);
+ if (ea_namelen == 0) {
+ goto out;
+ }
+ if (4 + ea_namelen + 1 + ea_valuelen > ea_size) {
+ goto out;
+ }
+ ea_size -= 4 + ea_namelen + 1 + ea_valuelen;
+ p += 4 + ea_namelen + 1 + ea_valuelen;
+ }
+
+ if (num_eas == 0) {
+ ret = True;
+ goto out;
+ }
+
+ *pnum_eas = num_eas;
+ if (!pea_list) {
+ /* Caller only wants number of EA's. */
+ ret = True;
+ goto out;
+ }
+
+ ea_list = (struct ea_struct *)talloc(ctx, num_eas*sizeof(struct ea_struct));
+ if (!ea_list) {
+ goto out;
+ }
+
+ ea_size = (size_t)IVAL(rdata,0);
+ p = rdata + 4;
+
+ for (num_eas = 0; num_eas < *pnum_eas; num_eas++ ) {
+ struct ea_struct *ea = &ea_list[num_eas];
+ fstring unix_ea_name;
+ unsigned int ea_namelen = CVAL(p,1);
+ unsigned int ea_valuelen = SVAL(p,2);
+
+ ea->flags = CVAL(p,0);
+ unix_ea_name[0] = '\0';
+ pull_ascii_fstring(unix_ea_name, p + 4);
+ ea->name = talloc_strdup(ctx, unix_ea_name);
+ /* Ensure the value is null terminated (in case it's a string). */
+ ea->value = data_blob_talloc(ctx, NULL, ea_valuelen + 1);
+ if (!ea->value.data) {
+ goto out;
+ }
+ if (ea_valuelen) {
+ memcpy(ea->value.data, p+4+ea_namelen+1, ea_valuelen);
+ }
+ ea->value.data[ea_valuelen] = 0;
+ ea->value.length--;
+ p += 4 + ea_namelen + 1 + ea_valuelen;
+ }
+
+ *pea_list = ea_list;
+ ret = True;
+
+ out :
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return ret;
+}
+
+/*********************************************************
+ Get an extended attribute list from a pathname.
+*********************************************************/
+
+BOOL cli_get_ea_list_path(struct cli_state *cli, const char *path,
+ TALLOC_CTX *ctx,
+ size_t *pnum_eas,
+ struct ea_struct **pea_list)
+{
+ uint16 setup = TRANSACT2_QPATHINFO;
+ unsigned int param_len = 0;
+ char param[sizeof(pstring)+6];
+ char *p;
+
+ p = param;
+ memset(p, 0, 6);
+ SSVAL(p, 0, SMB_INFO_QUERY_ALL_EAS);
+ p += 6;
+ p += clistr_push(cli, p, path, sizeof(pstring)-6, STR_TERMINATE);
+ param_len = PTR_DIFF(p, param);
+
+ return cli_get_ea_list(cli, setup, param, param_len, ctx, pnum_eas, pea_list);
+}
+
+/*********************************************************
+ Get an extended attribute list from an fnum.
+*********************************************************/
+
+BOOL cli_get_ea_list_fnum(struct cli_state *cli, int fnum,
+ TALLOC_CTX *ctx,
+ size_t *pnum_eas,
+ struct ea_struct **pea_list)
+{
+ uint16 setup = TRANSACT2_QFILEINFO;
+ char param[6];
+
+ memset(param, 0, 6);
+ SSVAL(param,0,fnum);
+ SSVAL(param,2,SMB_INFO_SET_EA);
+
+ return cli_get_ea_list(cli, setup, param, 6, ctx, pnum_eas, pea_list);
+}
diff --git a/source/libsmb/clifsinfo.c b/source/libsmb/clifsinfo.c
new file mode 100644
index 00000000000..00fe189e9a9
--- /dev/null
+++ b/source/libsmb/clifsinfo.c
@@ -0,0 +1,76 @@
+/*
+ Unix SMB/CIFS implementation.
+ FS info functions
+ Copyright (C) Stefan (metze) Metzmacher 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+
+BOOL cli_get_fs_attr_info(struct cli_state *cli, uint32 *fs_attr)
+{
+ BOOL ret = False;
+ uint16 setup;
+ char param[2];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+
+ if (!cli||!fs_attr)
+ smb_panic("cli_get_fs_attr_info() called with NULL Pionter!");
+
+ setup = TRANSACT2_QFSINFO;
+
+ SSVAL(param,0,SMB_QUERY_FS_ATTRIBUTE_INFO);
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL,
+ 0, 0,
+ &setup, 1, 0,
+ param, 2, 0,
+ NULL, 0, 560)) {
+ goto cleanup;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ goto cleanup;
+ }
+
+ if (cli_is_error(cli)) {
+ ret = False;
+ goto cleanup;
+ } else {
+ ret = True;
+ }
+
+ if (rdata_count < 12) {
+ goto cleanup;
+ }
+
+ *fs_attr = IVAL(rdata,0);
+
+ /* todo: but not yet needed
+ * return the other stuff
+ */
+
+cleanup:
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return ret;
+}
diff --git a/source/libsmb/clikrb5.c b/source/libsmb/clikrb5.c
new file mode 100644
index 00000000000..15b244a83dc
--- /dev/null
+++ b/source/libsmb/clikrb5.c
@@ -0,0 +1,421 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple kerberos5 routines for active directory
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Luke Howard 2002-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#ifdef HAVE_KRB5
+
+#ifdef HAVE_KRB5_KEYBLOCK_KEYVALUE
+#define KRB5_KEY_TYPE(k) ((k)->keytype)
+#define KRB5_KEY_LENGTH(k) ((k)->keyvalue.length)
+#define KRB5_KEY_DATA(k) ((k)->keyvalue.data)
+#else
+#define KRB5_KEY_TYPE(k) ((k)->enctype)
+#define KRB5_KEY_LENGTH(k) ((k)->length)
+#define KRB5_KEY_DATA(k) ((k)->contents)
+#endif /* HAVE_KRB5_KEYBLOCK_KEYVALUE */
+
+#ifndef HAVE_KRB5_SET_REAL_TIME
+/*
+ * This function is not in the Heimdal mainline.
+ */
+ krb5_error_code krb5_set_real_time(krb5_context context, int32_t seconds, int32_t microseconds)
+{
+ krb5_error_code ret;
+ int32_t sec, usec;
+
+ ret = krb5_us_timeofday(context, &sec, &usec);
+ if (ret)
+ return ret;
+
+ context->kdc_sec_offset = seconds - sec;
+ context->kdc_usec_offset = microseconds - usec;
+
+ return 0;
+}
+#endif
+
+#if defined(HAVE_KRB5_SET_DEFAULT_IN_TKT_ETYPES) && !defined(HAVE_KRB5_SET_DEFAULT_TGS_KTYPES)
+ krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc)
+{
+ return krb5_set_default_in_tkt_etypes(ctx, enc);
+}
+#endif
+
+#if defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS)
+/* HEIMDAL */
+ void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr)
+{
+ pkaddr->addr_type = KRB5_ADDRESS_INET;
+ pkaddr->address.length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
+ pkaddr->address.data = (char *)&(((struct sockaddr_in *)paddr)->sin_addr);
+}
+#elif defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS)
+/* MIT */
+ void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr)
+{
+ pkaddr->addrtype = ADDRTYPE_INET;
+ pkaddr->length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
+ pkaddr->contents = (krb5_octet *)&(((struct sockaddr_in *)paddr)->sin_addr);
+}
+#else
+ __ERROR__XX__UNKNOWN_ADDRTYPE
+#endif
+
+#if defined(HAVE_KRB5_PRINCIPAL2SALT) && defined(HAVE_KRB5_USE_ENCTYPE) && defined(HAVE_KRB5_STRING_TO_KEY)
+ int create_kerberos_key_from_string(krb5_context context,
+ krb5_principal host_princ,
+ krb5_data *password,
+ krb5_keyblock *key,
+ krb5_enctype enctype)
+{
+ int ret;
+ krb5_data salt;
+ krb5_encrypt_block eblock;
+
+ ret = krb5_principal2salt(context, host_princ, &salt);
+ if (ret) {
+ DEBUG(1,("krb5_principal2salt failed (%s)\n", error_message(ret)));
+ return ret;
+ }
+ krb5_use_enctype(context, &eblock, enctype);
+ ret = krb5_string_to_key(context, &eblock, key, password, &salt);
+ SAFE_FREE(salt.data);
+ return ret;
+}
+#elif defined(HAVE_KRB5_GET_PW_SALT) && defined(HAVE_KRB5_STRING_TO_KEY_SALT)
+ int create_kerberos_key_from_string(krb5_context context,
+ krb5_principal host_princ,
+ krb5_data *password,
+ krb5_keyblock *key,
+ krb5_enctype enctype)
+{
+ int ret;
+ krb5_salt salt;
+
+ ret = krb5_get_pw_salt(context, host_princ, &salt);
+ if (ret) {
+ DEBUG(1,("krb5_get_pw_salt failed (%s)\n", error_message(ret)));
+ return ret;
+ }
+ return krb5_string_to_key_salt(context, enctype, password->data,
+ salt, key);
+}
+#else
+ __ERROR_XX_UNKNOWN_CREATE_KEY_FUNCTIONS
+#endif
+
+#if defined(HAVE_KRB5_GET_PERMITTED_ENCTYPES)
+krb5_error_code get_kerberos_allowed_etypes(krb5_context context,
+ krb5_enctype **enctypes)
+{
+ return krb5_get_permitted_enctypes(context, enctypes);
+}
+#elif defined(HAVE_KRB5_GET_DEFAULT_IN_TKT_ETYPES)
+krb5_error_code get_kerberos_allowed_etypes(krb5_context context,
+ krb5_enctype **enctypes)
+{
+ return krb5_get_default_in_tkt_etypes(context, enctypes);
+}
+#else
+#error UNKNOWN_GET_ENCTYPES_FUNCTIONS
+#endif
+
+ void free_kerberos_etypes(krb5_context context,
+ krb5_enctype *enctypes)
+{
+#if defined(HAVE_KRB5_FREE_KTYPES)
+ krb5_free_ktypes(context, enctypes);
+ return;
+#else
+ SAFE_FREE(enctypes);
+ return;
+#endif
+}
+
+#if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY)
+ krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_keyblock *keyblock)
+{
+ return krb5_auth_con_setkey(context, auth_context, keyblock);
+}
+#endif
+
+ void get_auth_data_from_tkt(DATA_BLOB *auth_data, krb5_ticket *tkt)
+{
+#if defined(HAVE_KRB5_TKT_ENC_PART2)
+ if (tkt->enc_part2)
+ *auth_data = data_blob(tkt->enc_part2->authorization_data[0]->contents,
+ tkt->enc_part2->authorization_data[0]->length);
+#else
+ if (tkt->ticket.authorization_data && tkt->ticket.authorization_data->len)
+ *auth_data = data_blob(tkt->ticket.authorization_data->val->ad_data.data,
+ tkt->ticket.authorization_data->val->ad_data.length);
+#endif
+}
+
+ krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt)
+{
+#if defined(HAVE_KRB5_TKT_ENC_PART2)
+ return tkt->enc_part2->client;
+#else
+ return tkt->client;
+#endif
+}
+
+#if !defined(HAVE_KRB5_LOCATE_KDC)
+ krb5_error_code krb5_locate_kdc(krb5_context ctx, const krb5_data *realm, struct sockaddr **addr_pp, int *naddrs, int get_masters)
+{
+ krb5_krbhst_handle hnd;
+ krb5_krbhst_info *hinfo;
+ krb5_error_code rc;
+ int num_kdcs, i;
+ struct sockaddr *sa;
+
+ *addr_pp = NULL;
+ *naddrs = 0;
+
+ rc = krb5_krbhst_init(ctx, realm->data, KRB5_KRBHST_KDC, &hnd);
+ if (rc) {
+ DEBUG(0, ("krb5_locate_kdc: krb5_krbhst_init failed (%s)\n", error_message(rc)));
+ return rc;
+ }
+
+ for ( num_kdcs = 0; (rc = krb5_krbhst_next(ctx, hnd, &hinfo) == 0); num_kdcs++)
+ ;
+
+ krb5_krbhst_reset(ctx, hnd);
+
+ if (!num_kdcs) {
+ DEBUG(0, ("krb5_locate_kdc: zero kdcs found !\n"));
+ krb5_krbhst_free(ctx, hnd);
+ return -1;
+ }
+
+ sa = malloc( sizeof(struct sockaddr) * num_kdcs );
+ if (!sa) {
+ DEBUG(0, ("krb5_locate_kdc: malloc failed\n"));
+ krb5_krbhst_free(ctx, hnd);
+ naddrs = 0;
+ return -1;
+ }
+
+ memset(*addr_pp, '\0', sizeof(struct sockaddr) * num_kdcs );
+
+ for (i = 0; i < num_kdcs && (rc = krb5_krbhst_next(ctx, hnd, &hinfo) == 0); i++) {
+ if (hinfo->ai->ai_family == AF_INET)
+ memcpy(&sa[i], hinfo->ai->ai_addr, sizeof(struct sockaddr));
+ }
+
+ krb5_krbhst_free(ctx, hnd);
+
+ *naddrs = num_kdcs;
+ *addr_pp = sa;
+ return 0;
+}
+#endif
+
+/*
+ we can't use krb5_mk_req because w2k wants the service to be in a particular format
+*/
+static krb5_error_code ads_krb5_mk_req(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_flags ap_req_options,
+ const char *principal,
+ krb5_ccache ccache,
+ krb5_data *outbuf)
+{
+ krb5_error_code retval;
+ krb5_principal server;
+ krb5_creds * credsp;
+ krb5_creds creds;
+ krb5_data in_data;
+
+ retval = krb5_parse_name(context, principal, &server);
+ if (retval) {
+ DEBUG(1,("Failed to parse principal %s\n", principal));
+ return retval;
+ }
+
+ /* obtain ticket & session key */
+ ZERO_STRUCT(creds);
+ if ((retval = krb5_copy_principal(context, server, &creds.server))) {
+ DEBUG(1,("krb5_copy_principal failed (%s)\n",
+ error_message(retval)));
+ goto cleanup_princ;
+ }
+
+ if ((retval = krb5_cc_get_principal(context, ccache, &creds.client))) {
+ DEBUG(1,("krb5_cc_get_principal failed (%s)\n",
+ error_message(retval)));
+ goto cleanup_creds;
+ }
+
+ if ((retval = krb5_get_credentials(context, 0,
+ ccache, &creds, &credsp))) {
+ DEBUG(1,("krb5_get_credentials failed for %s (%s)\n",
+ principal, error_message(retval)));
+ goto cleanup_creds;
+ }
+
+ /* cope with the ticket being in the future due to clock skew */
+ if ((unsigned)credsp->times.starttime > time(NULL)) {
+ time_t t = time(NULL);
+ int time_offset = (unsigned)credsp->times.starttime - t;
+ DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
+ krb5_set_real_time(context, t + time_offset + 1, 0);
+ }
+
+ in_data.length = 0;
+ retval = krb5_mk_req_extended(context, auth_context, ap_req_options,
+ &in_data, credsp, outbuf);
+ if (retval) {
+ DEBUG(1,("krb5_mk_req_extended failed (%s)\n",
+ error_message(retval)));
+ }
+
+ krb5_free_creds(context, credsp);
+
+cleanup_creds:
+ krb5_free_cred_contents(context, &creds);
+
+cleanup_princ:
+ krb5_free_principal(context, server);
+
+ return retval;
+}
+
+/*
+ get a kerberos5 ticket for the given service
+*/
+int cli_krb5_get_ticket(const char *principal, time_t time_offset,
+ DATA_BLOB *ticket, DATA_BLOB *session_key_krb5)
+{
+ krb5_error_code retval;
+ krb5_data packet;
+ krb5_ccache ccdef;
+ krb5_context context;
+ krb5_auth_context auth_context = NULL;
+ krb5_enctype enc_types[] = {
+#ifdef ENCTYPE_ARCFOUR_HMAC
+ ENCTYPE_ARCFOUR_HMAC,
+#endif
+ ENCTYPE_DES_CBC_MD5,
+ ENCTYPE_DES_CBC_CRC,
+ ENCTYPE_NULL};
+
+ retval = krb5_init_context(&context);
+ if (retval) {
+ DEBUG(1,("krb5_init_context failed (%s)\n",
+ error_message(retval)));
+ goto failed;
+ }
+
+ if (time_offset != 0) {
+ krb5_set_real_time(context, time(NULL) + time_offset, 0);
+ }
+
+ if ((retval = krb5_cc_default(context, &ccdef))) {
+ DEBUG(1,("krb5_cc_default failed (%s)\n",
+ error_message(retval)));
+ goto failed;
+ }
+
+ if ((retval = krb5_set_default_tgs_ktypes(context, enc_types))) {
+ DEBUG(1,("krb5_set_default_tgs_ktypes failed (%s)\n",
+ error_message(retval)));
+ goto failed;
+ }
+
+ if ((retval = ads_krb5_mk_req(context,
+ &auth_context,
+ AP_OPTS_USE_SUBKEY,
+ principal,
+ ccdef, &packet))) {
+ goto failed;
+ }
+
+ get_krb5_smb_session_key(context, auth_context, session_key_krb5, False);
+
+ *ticket = data_blob(packet.data, packet.length);
+
+/* Hmm, heimdal dooesn't have this - what's the correct call? */
+#ifdef HAVE_KRB5_FREE_DATA_CONTENTS
+ krb5_free_data_contents(context, &packet);
+#endif
+
+failed:
+ if ( context )
+ krb5_free_context(context);
+
+ return retval;
+}
+
+ BOOL get_krb5_smb_session_key(krb5_context context, krb5_auth_context auth_context, DATA_BLOB *session_key, BOOL remote)
+ {
+ krb5_keyblock *skey;
+ krb5_error_code err;
+ BOOL ret = False;
+
+ memset(session_key, 0, 16);
+
+ if (remote)
+ err = krb5_auth_con_getremotesubkey(context, auth_context, &skey);
+ else
+ err = krb5_auth_con_getlocalsubkey(context, auth_context, &skey);
+ if (err == 0 && skey != NULL) {
+ DEBUG(10, ("Got KRB5 session key of length %d\n", KRB5_KEY_LENGTH(skey)));
+ *session_key = data_blob(KRB5_KEY_DATA(skey), KRB5_KEY_LENGTH(skey));
+ dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length);
+
+ ret = True;
+
+ krb5_free_keyblock(context, skey);
+ } else {
+ DEBUG(10, ("KRB5 error getting session key %d\n", err));
+ }
+
+ return ret;
+ }
+
+
+#if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT)
+ const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i )
+{
+ static krb5_data kdata;
+
+ kdata.data = krb5_principal_get_comp_string(context, principal, i);
+ kdata.length = strlen(kdata.data);
+ return &kdata;
+}
+#endif
+
+#else /* HAVE_KRB5 */
+ /* this saves a few linking headaches */
+int cli_krb5_get_ticket(const char *principal, time_t time_offset,
+ DATA_BLOB *ticket, DATA_BLOB *session_key_krb5)
+{
+ DEBUG(0,("NO KERBEROS SUPPORT\n"));
+ return 1;
+}
+
+#endif
diff --git a/source/libsmb/clilist.c b/source/libsmb/clilist.c
new file mode 100644
index 00000000000..ab157d48e96
--- /dev/null
+++ b/source/libsmb/clilist.c
@@ -0,0 +1,487 @@
+/*
+ Unix SMB/CIFS implementation.
+ client directory list routines
+ Copyright (C) Andrew Tridgell 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+/****************************************************************************
+ Interpret a long filename structure - this is mostly guesses at the moment.
+ The length of the structure is returned
+ The structure of a long filename depends on the info level. 260 is used
+ by NT and 2 is used by OS/2
+****************************************************************************/
+
+static int interpret_long_filename(struct cli_state *cli,
+ int level,char *p,file_info *finfo)
+{
+ extern file_info def_finfo;
+ file_info finfo2;
+ int len;
+ char *base = p;
+
+ if (!finfo) finfo = &finfo2;
+
+ memcpy(finfo,&def_finfo,sizeof(*finfo));
+
+ switch (level) {
+ case 1: /* OS/2 understands this */
+ /* these dates are converted to GMT by
+ make_unix_date */
+ finfo->ctime = make_unix_date2(p+4);
+ finfo->atime = make_unix_date2(p+8);
+ finfo->mtime = make_unix_date2(p+12);
+ finfo->size = IVAL(p,16);
+ finfo->mode = CVAL(p,24);
+ len = CVAL(p, 26);
+ p += 27;
+ p += clistr_align_in(cli, p, 0);
+ /* the len+2 below looks strange but it is
+ important to cope with the differences
+ between win2000 and win9x for this call
+ (tridge) */
+ p += clistr_pull(cli, finfo->name, p,
+ sizeof(finfo->name),
+ len+2,
+ STR_TERMINATE);
+ return PTR_DIFF(p, base);
+
+ case 2: /* this is what OS/2 uses mostly */
+ /* these dates are converted to GMT by
+ make_unix_date */
+ finfo->ctime = make_unix_date2(p+4);
+ finfo->atime = make_unix_date2(p+8);
+ finfo->mtime = make_unix_date2(p+12);
+ finfo->size = IVAL(p,16);
+ finfo->mode = CVAL(p,24);
+ len = CVAL(p, 30);
+ p += 31;
+ /* check for unisys! */
+ p += clistr_pull(cli, finfo->name, p,
+ sizeof(finfo->name),
+ len,
+ STR_NOALIGN);
+ return PTR_DIFF(p, base) + 1;
+
+ case 260: /* NT uses this, but also accepts 2 */
+ {
+ size_t namelen, slen;
+ p += 4; /* next entry offset */
+ p += 4; /* fileindex */
+
+ /* these dates appear to arrive in a
+ weird way. It seems to be localtime
+ plus the serverzone given in the
+ initial connect. This is GMT when
+ DST is not in effect and one hour
+ from GMT otherwise. Can this really
+ be right??
+
+ I suppose this could be called
+ kludge-GMT. Is is the GMT you get
+ by using the current DST setting on
+ a different localtime. It will be
+ cheap to calculate, I suppose, as
+ no DST tables will be needed */
+
+ finfo->ctime = interpret_long_date(p);
+ p += 8;
+ finfo->atime = interpret_long_date(p);
+ p += 8;
+ finfo->mtime = interpret_long_date(p);
+ p += 8;
+ p += 8;
+ finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
+ p += 8;
+ p += 8; /* alloc size */
+ finfo->mode = CVAL(p,0);
+ p += 4;
+ namelen = IVAL(p,0);
+ p += 4;
+ p += 4; /* EA size */
+ slen = SVAL(p, 0);
+ p += 2;
+ {
+ /* stupid NT bugs. grr */
+ int flags = 0;
+ if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
+ clistr_pull(cli, finfo->short_name, p,
+ sizeof(finfo->short_name),
+ slen, flags);
+ }
+ p += 24; /* short name? */
+ clistr_pull(cli, finfo->name, p,
+ sizeof(finfo->name),
+ namelen, 0);
+ return SVAL(base, 0);
+ }
+ }
+
+ DEBUG(1,("Unknown long filename format %d\n",level));
+ return(SVAL(p,0));
+}
+
+/****************************************************************************
+ Do a directory listing, calling fn on each file found.
+****************************************************************************/
+
+int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute,
+ void (*fn)(file_info *, const char *, void *), void *state)
+{
+#if 0
+ int max_matches = 1366; /* Match W2k - was 512. */
+#else
+ int max_matches = 512;
+#endif
+ int info_level;
+ char *p, *p2;
+ pstring mask;
+ file_info finfo;
+ int i;
+ char *tdl, *dirlist = NULL;
+ int dirlist_len = 0;
+ int total_received = -1;
+ BOOL First = True;
+ int ff_searchcount=0;
+ int ff_eos=0;
+ int ff_lastname=0;
+ int ff_dir_handle=0;
+ int loop_count = 0;
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int param_len, data_len;
+ uint16 setup;
+ pstring param;
+
+ /* NT uses 260, OS/2 uses 2. Both accept 1. */
+ info_level = (cli->capabilities&CAP_NT_SMBS)?260:1;
+
+ pstrcpy(mask,Mask);
+
+ while (ff_eos == 0) {
+ loop_count++;
+ if (loop_count > 200) {
+ DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
+ break;
+ }
+
+ if (First) {
+ setup = TRANSACT2_FINDFIRST;
+ SSVAL(param,0,attribute); /* attribute */
+ SSVAL(param,2,max_matches); /* max count */
+ SSVAL(param,4,4+2); /* resume required + close on end */
+ SSVAL(param,6,info_level);
+ SIVAL(param,8,0);
+ p = param+12;
+ p += clistr_push(cli, param+12, mask, sizeof(param)-12,
+ STR_TERMINATE);
+ } else {
+ setup = TRANSACT2_FINDNEXT;
+ SSVAL(param,0,ff_dir_handle);
+ SSVAL(param,2,max_matches); /* max count */
+ SSVAL(param,4,info_level);
+ SIVAL(param,6,0); /* ff_resume_key */
+ SSVAL(param,10,8+4+2); /* continue + resume required + close on end */
+ p = param+12;
+ p += clistr_push(cli, param+12, mask, sizeof(param)-12,
+ STR_TERMINATE);
+ }
+
+ param_len = PTR_DIFF(p, param);
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL, /* Name */
+ -1, 0, /* fid, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 10, /* param, length, max */
+ NULL, 0,
+#if 0
+ /* w2k value. */
+ MIN(16384,cli->max_xmit) /* data, length, max. */
+#else
+ cli->max_xmit /* data, length, max. */
+#endif
+ )) {
+ break;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len) &&
+ cli_is_dos_error(cli)) {
+ /* we need to work around a Win95 bug - sometimes
+ it gives ERRSRV/ERRerror temprarily */
+ uint8 eclass;
+ uint32 ecode;
+ cli_dos_error(cli, &eclass, &ecode);
+ if (eclass != ERRSRV || ecode != ERRerror)
+ break;
+ smb_msleep(100);
+ continue;
+ }
+
+ if (cli_is_error(cli) || !rdata || !rparam)
+ break;
+
+ if (total_received == -1)
+ total_received = 0;
+
+ /* parse out some important return info */
+ p = rparam;
+ if (First) {
+ ff_dir_handle = SVAL(p,0);
+ ff_searchcount = SVAL(p,2);
+ ff_eos = SVAL(p,4);
+ ff_lastname = SVAL(p,8);
+ } else {
+ ff_searchcount = SVAL(p,0);
+ ff_eos = SVAL(p,2);
+ ff_lastname = SVAL(p,6);
+ }
+
+ if (ff_searchcount == 0)
+ break;
+
+ /* point to the data bytes */
+ p = rdata;
+
+ /* we might need the lastname for continuations */
+ if (ff_lastname > 0) {
+ switch(info_level) {
+ case 260:
+ clistr_pull(cli, mask, p+ff_lastname,
+ sizeof(mask),
+ data_len-ff_lastname,
+ STR_TERMINATE);
+ break;
+ case 1:
+ clistr_pull(cli, mask, p+ff_lastname+1,
+ sizeof(mask),
+ -1,
+ STR_TERMINATE);
+ break;
+ }
+ } else {
+ pstrcpy(mask,"");
+ }
+
+ /* and add them to the dirlist pool */
+ tdl = Realloc(dirlist,dirlist_len + data_len);
+
+ if (!tdl) {
+ DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
+ break;
+ } else {
+ dirlist = tdl;
+ }
+
+ /* put in a length for the last entry, to ensure we can chain entries
+ into the next packet */
+ for (p2=p,i=0;i<(ff_searchcount-1);i++)
+ p2 += interpret_long_filename(cli,info_level,p2,NULL);
+ SSVAL(p2,0,data_len - PTR_DIFF(p2,p));
+
+ /* grab the data for later use */
+ memcpy(dirlist+dirlist_len,p,data_len);
+ dirlist_len += data_len;
+
+ total_received += ff_searchcount;
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ DEBUG(3,("received %d entries (eos=%d)\n",
+ ff_searchcount,ff_eos));
+
+ if (ff_searchcount > 0)
+ loop_count = 0;
+
+ First = False;
+ }
+
+ for (p=dirlist,i=0;i<total_received;i++) {
+ p += interpret_long_filename(cli,info_level,p,&finfo);
+ fn(&finfo, Mask, state);
+ }
+
+ /* free up the dirlist buffer */
+ SAFE_FREE(dirlist);
+ return(total_received);
+}
+
+/****************************************************************************
+ Interpret a short filename structure.
+ The length of the structure is returned.
+****************************************************************************/
+
+static int interpret_short_filename(struct cli_state *cli, char *p,file_info *finfo)
+{
+ extern file_info def_finfo;
+
+ *finfo = def_finfo;
+
+ finfo->mode = CVAL(p,21);
+
+ /* this date is converted to GMT by make_unix_date */
+ finfo->ctime = make_unix_date(p+22);
+ finfo->mtime = finfo->atime = finfo->ctime;
+ finfo->size = IVAL(p,26);
+ clistr_pull(cli, finfo->name, p+30, sizeof(finfo->name), 12, STR_ASCII);
+ if (strcmp(finfo->name, "..") && strcmp(finfo->name, ".")) {
+ strncpy(finfo->short_name,finfo->name, sizeof(finfo->short_name)-1);
+ finfo->short_name[sizeof(finfo->short_name)-1] = '\0';
+ }
+
+ return(DIR_STRUCT_SIZE);
+}
+
+
+/****************************************************************************
+ Do a directory listing, calling fn on each file found.
+ this uses the old SMBsearch interface. It is needed for testing Samba,
+ but should otherwise not be used.
+****************************************************************************/
+
+int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
+ void (*fn)(file_info *, const char *, void *), void *state)
+{
+ char *p;
+ int received = 0;
+ BOOL first = True;
+ char status[21];
+ int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
+ int num_received = 0;
+ int i;
+ char *tdl, *dirlist = NULL;
+ pstring mask;
+
+ ZERO_ARRAY(status);
+
+ pstrcpy(mask,Mask);
+
+ while (1) {
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,2,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBsearch);
+
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,num_asked);
+ SSVAL(cli->outbuf,smb_vwv1,attribute);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+
+ p += clistr_push(cli, p, first?mask:"", -1, STR_TERMINATE);
+ *p++ = 5;
+ if (first) {
+ SSVAL(p,0,0);
+ p += 2;
+ } else {
+ SSVAL(p,0,21);
+ p += 2;
+ memcpy(p,status,21);
+ p += 21;
+ }
+
+ cli_setup_bcc(cli, p);
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) break;
+
+ received = SVAL(cli->inbuf,smb_vwv0);
+ if (received <= 0) break;
+
+ first = False;
+
+ tdl = Realloc(dirlist,(num_received + received)*DIR_STRUCT_SIZE);
+
+ if (!tdl) {
+ DEBUG(0,("cli_list_old: failed to expand dirlist"));
+ SAFE_FREE(dirlist);
+ return 0;
+ }
+ else dirlist = tdl;
+
+ p = smb_buf(cli->inbuf) + 3;
+
+ memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
+ p,received*DIR_STRUCT_SIZE);
+
+ memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
+
+ num_received += received;
+
+ if (cli_is_error(cli)) break;
+ }
+
+ if (!first) {
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,2,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBfclose);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
+ SSVAL(cli->outbuf, smb_vwv1, attribute);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ fstrcpy(p, "");
+ p += strlen(p) + 1;
+ *p++ = 5;
+ SSVAL(p, 0, 21);
+ p += 2;
+ memcpy(p,status,21);
+ p += 21;
+
+ cli_setup_bcc(cli, p);
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ DEBUG(0,("Error closing search: %s\n",cli_errstr(cli)));
+ }
+ }
+
+ for (p=dirlist,i=0;i<num_received;i++) {
+ file_info finfo;
+ p += interpret_short_filename(cli, p,&finfo);
+ fn(&finfo, Mask, state);
+ }
+
+ SAFE_FREE(dirlist);
+ return(num_received);
+}
+
+/****************************************************************************
+ Do a directory listing, calling fn on each file found.
+ This auto-switches between old and new style.
+****************************************************************************/
+
+int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute,
+ void (*fn)(file_info *, const char *, void *), void *state)
+{
+ if (cli->protocol <= PROTOCOL_LANMAN1)
+ return cli_list_old(cli, Mask, attribute, fn, state);
+ return cli_list_new(cli, Mask, attribute, fn, state);
+}
diff --git a/source/libsmb/climessage.c b/source/libsmb/climessage.c
new file mode 100644
index 00000000000..8429ca4f413
--- /dev/null
+++ b/source/libsmb/climessage.c
@@ -0,0 +1,156 @@
+/*
+ Unix SMB/CIFS implementation.
+ client message handling routines
+ Copyright (C) Andrew Tridgell 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+
+/****************************************************************************
+start a message sequence
+****************************************************************************/
+int cli_message_start_build(struct cli_state *cli, char *host, char *username)
+{
+ char *p;
+
+ /* construct a SMBsendstrt command */
+ memset(cli->outbuf,'\0',smb_size);
+ set_message(cli->outbuf,0,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBsendstrt);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ p += clistr_push(cli, p, username, -1, STR_ASCII|STR_TERMINATE);
+ *p++ = 4;
+ p += clistr_push(cli, p, host, -1, STR_ASCII|STR_TERMINATE);
+
+ cli_setup_bcc(cli, p);
+
+ return(PTR_DIFF(p, cli->outbuf));
+}
+
+BOOL cli_message_start(struct cli_state *cli, char *host, char *username,
+ int *grp)
+{
+ cli_message_start_build(cli, host, username);
+
+ cli_send_smb(cli);
+
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ if (cli_is_error(cli)) return False;
+
+ *grp = SVAL(cli->inbuf,smb_vwv0);
+
+ return True;
+}
+
+
+/****************************************************************************
+send a message
+****************************************************************************/
+int cli_message_text_build(struct cli_state *cli, char *msg, int len, int grp)
+{
+ char *msgdos;
+ int lendos;
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ set_message(cli->outbuf,1,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBsendtxt);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,grp);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 1;
+
+ if ((lendos = (int)convert_string_allocate(NULL,CH_UNIX, CH_DOS, msg,len, (void **) &msgdos, True)) < 0 || !msgdos) {
+ DEBUG(3,("Conversion failed, sending message in UNIX charset\n"));
+ SSVAL(p, 0, len); p += 2;
+ memcpy(p, msg, len);
+ p += len;
+ } else {
+ SSVAL(p, 0, lendos); p += 2;
+ memcpy(p, msgdos, lendos);
+ p += lendos;
+ SAFE_FREE(msgdos);
+ }
+
+ cli_setup_bcc(cli, p);
+
+ return(PTR_DIFF(p, cli->outbuf));
+}
+
+BOOL cli_message_text(struct cli_state *cli, char *msg, int len, int grp)
+{
+ cli_message_text_build(cli, msg, len, grp);
+
+ cli_send_smb(cli);
+
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ if (cli_is_error(cli)) return False;
+
+ return True;
+}
+
+/****************************************************************************
+end a message
+****************************************************************************/
+int cli_message_end_build(struct cli_state *cli, int grp)
+{
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ set_message(cli->outbuf,1,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBsendend);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+
+ SSVAL(cli->outbuf,smb_vwv0,grp);
+
+ cli_setup_packet(cli);
+
+ p = smb_buf(cli->outbuf);
+
+ return(PTR_DIFF(p, cli->outbuf));
+}
+
+BOOL cli_message_end(struct cli_state *cli, int grp)
+{
+ cli_message_end_build(cli, grp);
+
+ cli_send_smb(cli);
+
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ if (cli_is_error(cli)) return False;
+
+ return True;
+}
diff --git a/source/libsmb/clioplock.c b/source/libsmb/clioplock.c
new file mode 100644
index 00000000000..0ffeb1926b0
--- /dev/null
+++ b/source/libsmb/clioplock.c
@@ -0,0 +1,68 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client oplock functions
+ Copyright (C) Andrew Tridgell 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+/****************************************************************************
+send an ack for an oplock break request
+****************************************************************************/
+BOOL cli_oplock_ack(struct cli_state *cli, int fnum, unsigned char level)
+{
+ char *oldbuf = cli->outbuf;
+ pstring buf;
+ BOOL ret;
+
+ cli->outbuf = buf;
+
+ memset(buf,'\0',smb_size);
+ set_message(buf,8,0,True);
+
+ SCVAL(buf,smb_com,SMBlockingX);
+ SSVAL(buf,smb_tid, cli->cnum);
+ cli_setup_packet(cli);
+ SSVAL(buf,smb_vwv0,0xFF);
+ SSVAL(buf,smb_vwv1,0);
+ SSVAL(buf,smb_vwv2,fnum);
+ if (level == 1)
+ SSVAL(buf,smb_vwv3,0x102); /* levelII oplock break ack */
+ else
+ SSVAL(buf,smb_vwv3,2); /* exclusive oplock break ack */
+ SIVAL(buf,smb_vwv4,0); /* timoeut */
+ SSVAL(buf,smb_vwv6,0); /* unlockcount */
+ SSVAL(buf,smb_vwv7,0); /* lockcount */
+
+ ret = cli_send_smb(cli);
+
+ cli->outbuf = oldbuf;
+
+ return ret;
+}
+
+
+/****************************************************************************
+set the oplock handler for a connection
+****************************************************************************/
+void cli_oplock_handler(struct cli_state *cli,
+ BOOL (*handler)(struct cli_state *, int, unsigned char))
+{
+ cli->oplock_handler = handler;
+}
diff --git a/source/libsmb/cliprint.c b/source/libsmb/cliprint.c
new file mode 100644
index 00000000000..2fb0e59acac
--- /dev/null
+++ b/source/libsmb/cliprint.c
@@ -0,0 +1,158 @@
+/*
+ Unix SMB/CIFS implementation.
+ client print routines
+ Copyright (C) Andrew Tridgell 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+/*****************************************************************************
+ Convert a character pointer in a cli_call_api() response to a form we can use.
+ This function contains code to prevent core dumps if the server returns
+ invalid data.
+*****************************************************************************/
+static const char *fix_char_ptr(unsigned int datap, unsigned int converter,
+ char *rdata, int rdrcnt)
+{
+ if (datap == 0) { /* turn NULL pointers into zero length strings */
+ return "";
+ } else {
+ unsigned int offset = datap - converter;
+
+ if (offset >= rdrcnt) {
+ DEBUG(1,("bad char ptr: datap=%u, converter=%u rdrcnt=%d>",
+ datap, converter, rdrcnt));
+ return "<ERROR>";
+ } else {
+ return &rdata[offset];
+ }
+ }
+}
+
+
+/****************************************************************************
+call fn() on each entry in a print queue
+****************************************************************************/
+int cli_print_queue(struct cli_state *cli,
+ void (*fn)(struct print_job_info *))
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt, rprcnt;
+ pstring param;
+ int result_code=0;
+ int i = -1;
+
+ memset(param,'\0',sizeof(param));
+
+ p = param;
+ SSVAL(p,0,76); /* API function number 76 (DosPrintJobEnum) */
+ p += 2;
+ pstrcpy_base(p,"zWrLeh", param); /* parameter description? */
+ p = skip_string(p,1);
+ pstrcpy_base(p,"WWzWWDDzz", param); /* returned data format */
+ p = skip_string(p,1);
+ pstrcpy_base(p,cli->share, param); /* name of queue */
+ p = skip_string(p,1);
+ SSVAL(p,0,2); /* API function level 2, PRJINFO_2 data structure */
+ SSVAL(p,2,1000); /* size of bytes of returned data buffer */
+ p += 4;
+ pstrcpy_base(p,"", param); /* subformat */
+ p = skip_string(p,1);
+
+ DEBUG(4,("doing cli_print_queue for %s\n", cli->share));
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, CLI_BUFFER_SIZE, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) { /* return data, length */
+ int converter;
+ result_code = SVAL(rparam,0);
+ converter = SVAL(rparam,2); /* conversion factor */
+
+ if (result_code == 0) {
+ struct print_job_info job;
+
+ p = rdata;
+
+ for (i = 0; i < SVAL(rparam,4); ++i) {
+ job.id = SVAL(p,0);
+ job.priority = SVAL(p,2);
+ fstrcpy(job.user,
+ fix_char_ptr(SVAL(p,4), converter,
+ rdata, rdrcnt));
+ job.t = make_unix_date3(p + 12);
+ job.size = IVAL(p,16);
+ fstrcpy(job.name,fix_char_ptr(SVAL(p,24),
+ converter,
+ rdata, rdrcnt));
+ fn(&job);
+ p += 28;
+ }
+ }
+ }
+
+ /* If any parameters or data were returned, free the storage. */
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return i;
+}
+
+/****************************************************************************
+ cancel a print job
+ ****************************************************************************/
+int cli_printjob_del(struct cli_state *cli, int job)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int ret = -1;
+ pstring param;
+
+ memset(param,'\0',sizeof(param));
+
+ p = param;
+ SSVAL(p,0,81); /* DosPrintJobDel() */
+ p += 2;
+ pstrcpy_base(p,"W", param);
+ p = skip_string(p,1);
+ pstrcpy_base(p,"", param);
+ p = skip_string(p,1);
+ SSVAL(p,0,job);
+ p += 2;
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, CLI_BUFFER_SIZE, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) { /* return data, length */
+ ret = SVAL(rparam,0);
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return ret;
+}
+
+
diff --git a/source/libsmb/cliquota.c b/source/libsmb/cliquota.c
new file mode 100644
index 00000000000..ed808aa1f5c
--- /dev/null
+++ b/source/libsmb/cliquota.c
@@ -0,0 +1,633 @@
+/*
+ Unix SMB/CIFS implementation.
+ client quota functions
+ Copyright (C) Stefan (metze) Metzmacher 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+BOOL cli_get_quota_handle(struct cli_state *cli, int *quota_fnum)
+{
+ *quota_fnum = cli_nt_create_full(cli, FAKE_FILE_NAME_QUOTA,
+ 0x00000016, DESIRED_ACCESS_PIPE,
+ 0x00000000, FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OPEN, 0x00000000, 0x03);
+
+ if (*quota_fnum == (-1)) {
+ return False;
+ }
+
+ return True;
+}
+
+void free_ntquota_list(SMB_NTQUOTA_LIST **qt_list)
+{
+ if (!qt_list)
+ return;
+
+ if ((*qt_list)->mem_ctx)
+ talloc_destroy((*qt_list)->mem_ctx);
+
+ (*qt_list) = NULL;
+
+ return;
+}
+
+static BOOL parse_user_quota_record(const char *rdata, unsigned int rdata_count, unsigned int *offset, SMB_NTQUOTA_STRUCT *pqt)
+{
+ int sid_len;
+ SMB_NTQUOTA_STRUCT qt;
+
+ ZERO_STRUCT(qt);
+
+ if (!rdata||!offset||!pqt)
+ smb_panic("parse_quota_record: called with NULL POINTER!\n");
+
+ if (rdata_count < 40) {
+ return False;
+ }
+
+ /* offset to next quota record.
+ * 4 bytes IVAL(rdata,0)
+ * unused here...
+ */
+ *offset = IVAL(rdata,0);
+
+ /* sid len */
+ sid_len = IVAL(rdata,4);
+
+ if (rdata_count < 40+sid_len) {
+ return False;
+ }
+
+ /* unknown 8 bytes in pdata
+ * maybe its the change time in NTTIME
+ */
+
+ /* the used space 8 bytes (SMB_BIG_UINT)*/
+ qt.usedspace = (SMB_BIG_UINT)IVAL(rdata,16);
+#ifdef LARGE_SMB_OFF_T
+ qt.usedspace |= (((SMB_BIG_UINT)IVAL(rdata,20)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if ((IVAL(rdata,20) != 0)&&
+ ((qt.usedspace != 0xFFFFFFFF)||
+ (IVAL(rdata,20)!=0xFFFFFFFF))) {
+ /* more than 32 bits? */
+ return False;
+ }
+#endif /* LARGE_SMB_OFF_T */
+
+ /* the soft quotas 8 bytes (SMB_BIG_UINT)*/
+ qt.softlim = (SMB_BIG_UINT)IVAL(rdata,24);
+#ifdef LARGE_SMB_OFF_T
+ qt.softlim |= (((SMB_BIG_UINT)IVAL(rdata,28)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if ((IVAL(rdata,28) != 0)&&
+ ((qt.softlim != 0xFFFFFFFF)||
+ (IVAL(rdata,28)!=0xFFFFFFFF))) {
+ /* more than 32 bits? */
+ return False;
+ }
+#endif /* LARGE_SMB_OFF_T */
+
+ /* the hard quotas 8 bytes (SMB_BIG_UINT)*/
+ qt.hardlim = (SMB_BIG_UINT)IVAL(rdata,32);
+#ifdef LARGE_SMB_OFF_T
+ qt.hardlim |= (((SMB_BIG_UINT)IVAL(rdata,36)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if ((IVAL(rdata,36) != 0)&&
+ ((qt.hardlim != 0xFFFFFFFF)||
+ (IVAL(rdata,36)!=0xFFFFFFFF))) {
+ /* more than 32 bits? */
+ return False;
+ }
+#endif /* LARGE_SMB_OFF_T */
+
+ sid_parse(rdata+40,sid_len,&qt.sid);
+
+ qt.qtype = SMB_USER_QUOTA_TYPE;
+
+ *pqt = qt;
+
+ return True;
+}
+
+BOOL cli_get_user_quota(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_STRUCT *pqt)
+{
+ BOOL ret = False;
+ uint16 setup;
+ char params[16];
+ unsigned int data_len;
+ char data[SID_MAX_SIZE+8];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+ unsigned int sid_len;
+ unsigned int offset;
+
+ if (!cli||!pqt)
+ smb_panic("cli_get_user_quota() called with NULL Pointer!");
+
+ setup = NT_TRANSACT_GET_USER_QUOTA;
+
+ SSVAL(params, 0,quota_fnum);
+ SSVAL(params, 2,TRANSACT_GET_USER_QUOTA_FOR_SID);
+ SIVAL(params, 4,0x00000024);
+ SIVAL(params, 8,0x00000000);
+ SIVAL(params,12,0x00000024);
+
+ sid_len = sid_size(&pqt->sid);
+ data_len = sid_len+8;
+ SIVAL(data, 0, 0x00000000);
+ SIVAL(data, 4, sid_len);
+ sid_linearize(data+8, sid_len, &pqt->sid);
+
+ if (!cli_send_nt_trans(cli,
+ NT_TRANSACT_GET_USER_QUOTA,
+ 0,
+ &setup, 1, 0,
+ params, 16, 4,
+ data, data_len, 112)) {
+ DEBUG(1,("Failed to send NT_TRANSACT_GET_USER_QUOTA\n"));
+ goto cleanup;
+ }
+
+
+ if (!cli_receive_nt_trans(cli,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ DEBUG(1,("Failed to recv NT_TRANSACT_GET_USER_QUOTA\n"));
+ goto cleanup;
+ }
+
+ if (cli_is_error(cli)) {
+ ret = False;
+ goto cleanup;
+ } else {
+ ret = True;
+ }
+
+ if ((rparam&&rdata)&&(rparam_count>=4&&rdata_count>=8)) {
+ ret = parse_user_quota_record(rdata, rdata_count, &offset, pqt);
+ } else {
+ DEBUG(0,("Got INVALID NT_TRANSACT_GET_USER_QUOTA reply.\n"));
+ ret = False;
+ }
+
+ cleanup:
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+ return ret;
+}
+
+BOOL cli_set_user_quota(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_STRUCT *pqt)
+{
+ BOOL ret = False;
+ uint16 setup;
+ char params[2];
+ char data[112];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+ unsigned int sid_len;
+ memset(data,'\0',112);
+
+ if (!cli||!pqt)
+ smb_panic("cli_set_user_quota() called with NULL Pointer!");
+
+ setup = NT_TRANSACT_SET_USER_QUOTA;
+
+ SSVAL(params,0,quota_fnum);
+
+ sid_len = sid_size(&pqt->sid);
+ SIVAL(data,0,0);
+ SIVAL(data,4,sid_len);
+ SBIG_UINT(data, 8,(SMB_BIG_UINT)0);
+ SBIG_UINT(data,16,pqt->usedspace);
+ SBIG_UINT(data,24,pqt->softlim);
+ SBIG_UINT(data,32,pqt->hardlim);
+ sid_linearize(data+40, sid_len, &pqt->sid);
+
+ if (!cli_send_nt_trans(cli,
+ NT_TRANSACT_SET_USER_QUOTA,
+ 0,
+ &setup, 1, 0,
+ params, 2, 0,
+ data, 112, 0)) {
+ DEBUG(1,("Failed to send NT_TRANSACT_SET_USER_QUOTA\n"));
+ goto cleanup;
+ }
+
+
+ if (!cli_receive_nt_trans(cli,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ DEBUG(1,("NT_TRANSACT_SET_USER_QUOTA failed\n"));
+ goto cleanup;
+ }
+
+ if (cli_is_error(cli)) {
+ ret = False;
+ goto cleanup;
+ } else {
+ ret = True;
+ }
+
+ cleanup:
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+ return ret;
+}
+
+BOOL cli_list_user_quota(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_LIST **pqt_list)
+{
+ BOOL ret = False;
+ uint16 setup;
+ char params[16];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+ unsigned int offset;
+ const char *curdata = NULL;
+ unsigned int curdata_count = 0;
+ TALLOC_CTX *mem_ctx = NULL;
+ SMB_NTQUOTA_STRUCT qt;
+ SMB_NTQUOTA_LIST *tmp_list_ent;
+
+ if (!cli||!pqt_list)
+ smb_panic("cli_list_user_quota() called with NULL Pointer!");
+
+ setup = NT_TRANSACT_GET_USER_QUOTA;
+
+ SSVAL(params, 0,quota_fnum);
+ SSVAL(params, 2,TRANSACT_GET_USER_QUOTA_LIST_START);
+ SIVAL(params, 4,0x00000000);
+ SIVAL(params, 8,0x00000000);
+ SIVAL(params,12,0x00000000);
+
+ if (!cli_send_nt_trans(cli,
+ NT_TRANSACT_GET_USER_QUOTA,
+ 0,
+ &setup, 1, 0,
+ params, 16, 4,
+ NULL, 0, 2048)) {
+ DEBUG(1,("Failed to send NT_TRANSACT_GET_USER_QUOTA\n"));
+ goto cleanup;
+ }
+
+
+ if (!cli_receive_nt_trans(cli,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ DEBUG(1,("Failed to recv NT_TRANSACT_GET_USER_QUOTA\n"));
+ goto cleanup;
+ }
+
+ if (cli_is_error(cli)) {
+ ret = False;
+ goto cleanup;
+ } else {
+ ret = True;
+ }
+
+ if (rdata_count == 0) {
+ *pqt_list = NULL;
+ return True;
+ }
+
+ if ((mem_ctx=talloc_init("SMB_USER_QUOTA_LIST"))==NULL) {
+ DEBUG(0,("talloc_init() failed\n"));
+ return (-1);
+ }
+
+ offset = 1;
+ for (curdata=rdata,curdata_count=rdata_count;
+ ((curdata)&&(curdata_count>=8)&&(offset>0));
+ curdata +=offset,curdata_count -= offset) {
+ ZERO_STRUCT(qt);
+ if (!parse_user_quota_record(curdata, curdata_count, &offset, &qt)) {
+ DEBUG(1,("Failed to parse the quota record\n"));
+ goto cleanup;
+ }
+
+ if ((tmp_list_ent=(SMB_NTQUOTA_LIST *)talloc_zero(mem_ctx,sizeof(SMB_NTQUOTA_LIST)))==NULL) {
+ DEBUG(0,("talloc_zero() failed\n"));
+ return (-1);
+ }
+
+ if ((tmp_list_ent->quotas=(SMB_NTQUOTA_STRUCT *)talloc_zero(mem_ctx,sizeof(SMB_NTQUOTA_STRUCT)))==NULL) {
+ DEBUG(0,("talloc_zero() failed\n"));
+ return (-1);
+ }
+
+ memcpy(tmp_list_ent->quotas,&qt,sizeof(qt));
+ tmp_list_ent->mem_ctx = mem_ctx;
+
+ DLIST_ADD((*pqt_list),tmp_list_ent);
+ }
+
+ SSVAL(params, 2,TRANSACT_GET_USER_QUOTA_LIST_CONTINUE);
+ while(1) {
+ if (!cli_send_nt_trans(cli,
+ NT_TRANSACT_GET_USER_QUOTA,
+ 0,
+ &setup, 1, 0,
+ params, 16, 4,
+ NULL, 0, 2048)) {
+ DEBUG(1,("Failed to send NT_TRANSACT_GET_USER_QUOTA\n"));
+ goto cleanup;
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+ if (!cli_receive_nt_trans(cli,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ DEBUG(1,("Failed to recv NT_TRANSACT_GET_USER_QUOTA\n"));
+ goto cleanup;
+ }
+
+ if (cli_is_error(cli)) {
+ ret = False;
+ goto cleanup;
+ } else {
+ ret = True;
+ }
+
+ if (rdata_count == 0) {
+ break;
+ }
+
+ offset = 1;
+ for (curdata=rdata,curdata_count=rdata_count;
+ ((curdata)&&(curdata_count>=8)&&(offset>0));
+ curdata +=offset,curdata_count -= offset) {
+ ZERO_STRUCT(qt);
+ if (!parse_user_quota_record(curdata, curdata_count, &offset, &qt)) {
+ DEBUG(1,("Failed to parse the quota record\n"));
+ goto cleanup;
+ }
+
+ if ((tmp_list_ent=(SMB_NTQUOTA_LIST *)talloc_zero(mem_ctx,sizeof(SMB_NTQUOTA_LIST)))==NULL) {
+ DEBUG(0,("talloc_zero() failed\n"));
+ talloc_destroy(mem_ctx);
+ goto cleanup;
+ }
+
+ if ((tmp_list_ent->quotas=(SMB_NTQUOTA_STRUCT *)talloc_zero(mem_ctx,sizeof(SMB_NTQUOTA_STRUCT)))==NULL) {
+ DEBUG(0,("talloc_zero() failed\n"));
+ talloc_destroy(mem_ctx);
+ goto cleanup;
+ }
+
+ memcpy(tmp_list_ent->quotas,&qt,sizeof(qt));
+ tmp_list_ent->mem_ctx = mem_ctx;
+
+ DLIST_ADD((*pqt_list),tmp_list_ent);
+ }
+ }
+
+
+ ret = True;
+ cleanup:
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return ret;
+}
+
+BOOL cli_get_fs_quota_info(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_STRUCT *pqt)
+{
+ BOOL ret = False;
+ uint16 setup;
+ char param[2];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+ SMB_NTQUOTA_STRUCT qt;
+ ZERO_STRUCT(qt);
+
+ if (!cli||!pqt)
+ smb_panic("cli_get_fs_quota_info() called with NULL Pointer!");
+
+ setup = TRANSACT2_QFSINFO;
+
+ SSVAL(param,0,SMB_FS_QUOTA_INFORMATION);
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL,
+ 0, 0,
+ &setup, 1, 0,
+ param, 2, 0,
+ NULL, 0, 560)) {
+ goto cleanup;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ goto cleanup;
+ }
+
+ if (cli_is_error(cli)) {
+ ret = False;
+ goto cleanup;
+ } else {
+ ret = True;
+ }
+
+ if (rdata_count < 48) {
+ goto cleanup;
+ }
+
+ /* unknown_1 24 NULL bytes in pdata*/
+
+ /* the soft quotas 8 bytes (SMB_BIG_UINT)*/
+ qt.softlim = (SMB_BIG_UINT)IVAL(rdata,24);
+#ifdef LARGE_SMB_OFF_T
+ qt.softlim |= (((SMB_BIG_UINT)IVAL(rdata,28)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if ((IVAL(rdata,28) != 0)&&
+ ((qt.softlim != 0xFFFFFFFF)||
+ (IVAL(rdata,28)!=0xFFFFFFFF))) {
+ /* more than 32 bits? */
+ goto cleanup;
+ }
+#endif /* LARGE_SMB_OFF_T */
+
+ /* the hard quotas 8 bytes (SMB_BIG_UINT)*/
+ qt.hardlim = (SMB_BIG_UINT)IVAL(rdata,32);
+#ifdef LARGE_SMB_OFF_T
+ qt.hardlim |= (((SMB_BIG_UINT)IVAL(rdata,36)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if ((IVAL(rdata,36) != 0)&&
+ ((qt.hardlim != 0xFFFFFFFF)||
+ (IVAL(rdata,36)!=0xFFFFFFFF))) {
+ /* more than 32 bits? */
+ goto cleanup;
+ }
+#endif /* LARGE_SMB_OFF_T */
+
+ /* quota_flags 2 bytes **/
+ qt.qflags = SVAL(rdata,40);
+
+ qt.qtype = SMB_USER_FS_QUOTA_TYPE;
+
+ *pqt = qt;
+
+ ret = True;
+cleanup:
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return ret;
+}
+
+BOOL cli_set_fs_quota_info(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_STRUCT *pqt)
+{
+ BOOL ret = False;
+ uint16 setup;
+ char param[4];
+ char data[48];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+ SMB_NTQUOTA_STRUCT qt;
+ ZERO_STRUCT(qt);
+ memset(data,'\0',48);
+
+ if (!cli||!pqt)
+ smb_panic("cli_set_fs_quota_info() called with NULL Pointer!");
+
+ setup = TRANSACT2_SETFSINFO;
+
+ SSVAL(param,0,quota_fnum);
+ SSVAL(param,2,SMB_FS_QUOTA_INFORMATION);
+
+ /* Unknown1 24 NULL bytes*/
+
+ /* Default Soft Quota 8 bytes */
+ SBIG_UINT(data,24,pqt->softlim);
+
+ /* Default Hard Quota 8 bytes */
+ SBIG_UINT(data,32,pqt->hardlim);
+
+ /* Quota flag 2 bytes */
+ SSVAL(data,40,pqt->qflags);
+
+ /* Unknown3 6 NULL bytes */
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL,
+ 0, 0,
+ &setup, 1, 0,
+ param, 4, 0,
+ data, 48, 0)) {
+ goto cleanup;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ goto cleanup;
+ }
+
+ if (cli_is_error(cli)) {
+ ret = False;
+ goto cleanup;
+ } else {
+ ret = True;
+ }
+
+cleanup:
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return ret;
+}
+
+static char *quota_str_static(SMB_BIG_UINT val, BOOL special, BOOL _numeric)
+{
+ static fstring buffer;
+
+ memset(buffer,'\0',sizeof(buffer));
+
+ if (!_numeric&&special&&(val == SMB_NTQUOTAS_NO_LIMIT)) {
+ fstr_sprintf(buffer,"NO LIMIT");
+ return buffer;
+ }
+#if defined(HAVE_LONGLONG)
+ fstr_sprintf(buffer,"%llu",val);
+#else
+ fstr_sprintf(buffer,"%lu",val);
+#endif
+ return buffer;
+}
+
+void dump_ntquota(SMB_NTQUOTA_STRUCT *qt, BOOL _verbose, BOOL _numeric, void (*_sidtostring)(fstring str, DOM_SID *sid, BOOL _numeric))
+{
+ if (!qt)
+ smb_panic("dump_ntquota() called with NULL pointer");
+
+ switch (qt->qtype) {
+ case SMB_USER_FS_QUOTA_TYPE:
+ {
+ d_printf("File System QUOTAS:\n");
+ d_printf("Limits:\n");
+ d_printf(" Default Soft Limit: %15s\n",quota_str_static(qt->softlim,True,_numeric));
+ d_printf(" Default Hard Limit: %15s\n",quota_str_static(qt->hardlim,True,_numeric));
+ d_printf("Quota Flags:\n");
+ d_printf(" Quotas Enabled: %s\n",
+ ((qt->qflags&QUOTAS_ENABLED)||(qt->qflags&QUOTAS_DENY_DISK))?"On":"Off");
+ d_printf(" Deny Disk: %s\n",(qt->qflags&QUOTAS_DENY_DISK)?"On":"Off");
+ d_printf(" Log Soft Limit: %s\n",(qt->qflags&QUOTAS_LOG_THRESHOLD)?"On":"Off");
+ d_printf(" Log Hard Limit: %s\n",(qt->qflags&QUOTAS_LOG_LIMIT)?"On":"Off");
+ }
+ break;
+ case SMB_USER_QUOTA_TYPE:
+ {
+ fstring username_str = {0};
+
+ if (_sidtostring) {
+ _sidtostring(username_str,&qt->sid,_numeric);
+ } else {
+ fstrcpy(username_str,sid_string_static(&qt->sid));
+ }
+
+ if (_verbose) {
+ d_printf("Quotas for User: %s\n",username_str);
+ d_printf("Used Space: %15s\n",quota_str_static(qt->usedspace,False,_numeric));
+ d_printf("Soft Limit: %15s\n",quota_str_static(qt->softlim,True,_numeric));
+ d_printf("Hard Limit: %15s\n",quota_str_static(qt->hardlim,True,_numeric));
+ } else {
+ d_printf("%-30s: ",username_str);
+ d_printf("%15s/",quota_str_static(qt->usedspace,False,_numeric));
+ d_printf("%15s/",quota_str_static(qt->softlim,True,_numeric));
+ d_printf("%15s\n",quota_str_static(qt->hardlim,True,_numeric));
+ }
+ }
+ break;
+ default:
+ d_printf("dump_ntquota() invalid qtype(%d)\n",qt->qtype);
+ return;
+ }
+}
+
+void dump_ntquota_list(SMB_NTQUOTA_LIST **qtl, BOOL _verbose, BOOL _numeric, void (*_sidtostring)(fstring str, DOM_SID *sid, BOOL _numeric))
+{
+ SMB_NTQUOTA_LIST *cur;
+
+ for (cur = *qtl;cur;cur = cur->next) {
+ if (cur->quotas)
+ dump_ntquota(cur->quotas,_verbose,_numeric,_sidtostring);
+ }
+}
diff --git a/source/libsmb/clirap.c b/source/libsmb/clirap.c
new file mode 100644
index 00000000000..f8204e05d68
--- /dev/null
+++ b/source/libsmb/clirap.c
@@ -0,0 +1,743 @@
+/*
+ Unix SMB/CIFS implementation.
+ client RAP calls
+ Copyright (C) Andrew Tridgell 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+
+/****************************************************************************
+Call a remote api on an arbitrary pipe. takes param, data and setup buffers.
+****************************************************************************/
+BOOL cli_api_pipe(struct cli_state *cli, const char *pipe_name,
+ uint16 *setup, uint32 setup_count, uint32 max_setup_count,
+ char *params, uint32 param_count, uint32 max_param_count,
+ char *data, uint32 data_count, uint32 max_data_count,
+ char **rparam, uint32 *rparam_count,
+ char **rdata, uint32 *rdata_count)
+{
+ cli_send_trans(cli, SMBtrans,
+ pipe_name,
+ 0,0, /* fid, flags */
+ setup, setup_count, max_setup_count,
+ params, param_count, max_param_count,
+ data, data_count, max_data_count);
+
+ return (cli_receive_trans(cli, SMBtrans,
+ rparam, (unsigned int *)rparam_count,
+ rdata, (unsigned int *)rdata_count));
+}
+
+/****************************************************************************
+call a remote api
+****************************************************************************/
+BOOL cli_api(struct cli_state *cli,
+ char *param, int prcnt, int mprcnt,
+ char *data, int drcnt, int mdrcnt,
+ char **rparam, unsigned int *rprcnt,
+ char **rdata, unsigned int *rdrcnt)
+{
+ cli_send_trans(cli,SMBtrans,
+ PIPE_LANMAN, /* Name */
+ 0,0, /* fid, flags */
+ NULL,0,0, /* Setup, length, max */
+ param, prcnt, mprcnt, /* Params, length, max */
+ data, drcnt, mdrcnt /* Data, length, max */
+ );
+
+ return (cli_receive_trans(cli,SMBtrans,
+ rparam, rprcnt,
+ rdata, rdrcnt));
+}
+
+
+/****************************************************************************
+perform a NetWkstaUserLogon
+****************************************************************************/
+BOOL cli_NetWkstaUserLogon(struct cli_state *cli,char *user, char *workstation)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ pstring param;
+
+ memset(param, 0, sizeof(param));
+
+ /* send a SMBtrans command with api NetWkstaUserLogon */
+ p = param;
+ SSVAL(p,0,132); /* api number */
+ p += 2;
+ pstrcpy_base(p,"OOWb54WrLh",param);
+ p = skip_string(p,1);
+ pstrcpy_base(p,"WB21BWDWWDDDDDDDzzzD",param);
+ p = skip_string(p,1);
+ SSVAL(p,0,1);
+ p += 2;
+ pstrcpy_base(p,user,param);
+ strupper_m(p);
+ p += 21;
+ p++;
+ p += 15;
+ p++;
+ pstrcpy_base(p, workstation, param);
+ strupper_m(p);
+ p += 16;
+ SSVAL(p, 0, CLI_BUFFER_SIZE);
+ p += 2;
+ SSVAL(p, 0, CLI_BUFFER_SIZE);
+ p += 2;
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),1024, /* param, length, max */
+ NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */
+ &rparam, &rprcnt, /* return params, return size */
+ &rdata, &rdrcnt /* return data, return size */
+ )) {
+ cli->rap_error = rparam? SVAL(rparam,0) : -1;
+ p = rdata;
+
+ if (cli->rap_error == 0) {
+ DEBUG(4,("NetWkstaUserLogon success\n"));
+ cli->privileges = SVAL(p, 24);
+ /* The cli->eff_name field used to be set here
+ but it wasn't used anywhere else. */
+ } else {
+ DEBUG(1,("NetwkstaUserLogon gave error %d\n", cli->rap_error));
+ }
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+ return (cli->rap_error == 0);
+}
+
+/****************************************************************************
+call a NetShareEnum - try and browse available connections on a host
+****************************************************************************/
+int cli_RNetShareEnum(struct cli_state *cli, void (*fn)(const char *, uint32, const char *, void *), void *state)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ pstring param;
+ int count = -1;
+
+ /* now send a SMBtrans command with api RNetShareEnum */
+ p = param;
+ SSVAL(p,0,0); /* api number */
+ p += 2;
+ pstrcpy_base(p,"WrLeh",param);
+ p = skip_string(p,1);
+ pstrcpy_base(p,"B13BWz",param);
+ p = skip_string(p,1);
+ SSVAL(p,0,1);
+ /*
+ * Win2k needs a *smaller* buffer than 0xFFFF here -
+ * it returns "out of server memory" with 0xFFFF !!! JRA.
+ */
+ SSVAL(p,2,0xFFE0);
+ p += 4;
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 0xFFE0, /* data, length, maxlen - Win2k needs a small buffer here too ! */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ int res = rparam? SVAL(rparam,0) : -1;
+
+ if (res == 0 || res == ERRmoredata) {
+ int converter=SVAL(rparam,2);
+ int i;
+
+ count=SVAL(rparam,4);
+ p = rdata;
+
+ for (i=0;i<count;i++,p+=20) {
+ char *sname = p;
+ int type = SVAL(p,14);
+ int comment_offset = IVAL(p,16) & 0xFFFF;
+ const char *cmnt = comment_offset?(rdata+comment_offset-converter):"";
+ pstring s1, s2;
+
+ pull_ascii_pstring(s1, sname);
+ pull_ascii_pstring(s2, cmnt);
+
+ fn(s1, type, s2, state);
+ }
+ } else {
+ DEBUG(4,("NetShareEnum res=%d\n", res));
+ }
+ } else {
+ DEBUG(4,("NetShareEnum failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return count;
+}
+
+
+/****************************************************************************
+call a NetServerEnum for the specified workgroup and servertype mask. This
+function then calls the specified callback function for each name returned.
+
+The callback function takes 4 arguments: the machine name, the server type,
+the comment and a state pointer.
+****************************************************************************/
+BOOL cli_NetServerEnum(struct cli_state *cli, char *workgroup, uint32 stype,
+ void (*fn)(const char *, uint32, const char *, void *),
+ void *state)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rdrcnt,rprcnt;
+ char *p;
+ pstring param;
+ int uLevel = 1;
+ int count = -1;
+
+ /* send a SMBtrans command with api NetServerEnum */
+ p = param;
+ SSVAL(p,0,0x68); /* api number */
+ p += 2;
+ pstrcpy_base(p,"WrLehDz", param);
+ p = skip_string(p,1);
+
+ pstrcpy_base(p,"B16BBDz", param);
+
+ p = skip_string(p,1);
+ SSVAL(p,0,uLevel);
+ SSVAL(p,2,CLI_BUFFER_SIZE);
+ p += 4;
+ SIVAL(p,0,stype);
+ p += 4;
+
+ p += push_ascii(p, workgroup, sizeof(pstring)-PTR_DIFF(p,param)-1, STR_TERMINATE|STR_UPPER);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 8, /* params, length, max */
+ NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */
+ &rparam, &rprcnt, /* return params, return size */
+ &rdata, &rdrcnt /* return data, return size */
+ )) {
+ int res = rparam? SVAL(rparam,0) : -1;
+
+ if (res == 0 || res == ERRmoredata) {
+ int i;
+ int converter=SVAL(rparam,2);
+
+ count=SVAL(rparam,4);
+ p = rdata;
+
+ for (i = 0;i < count;i++, p += 26) {
+ char *sname = p;
+ int comment_offset = (IVAL(p,22) & 0xFFFF)-converter;
+ const char *cmnt = comment_offset?(rdata+comment_offset):"";
+ pstring s1, s2;
+
+ if (comment_offset < 0 || comment_offset > (int)rdrcnt) continue;
+
+ stype = IVAL(p,18) & ~SV_TYPE_LOCAL_LIST_ONLY;
+
+ pull_ascii_pstring(s1, sname);
+ pull_ascii_pstring(s2, cmnt);
+ fn(s1, stype, s2, state);
+ }
+ }
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return(count > 0);
+}
+
+
+
+/****************************************************************************
+Send a SamOEMChangePassword command
+****************************************************************************/
+BOOL cli_oem_change_password(struct cli_state *cli, const char *user, const char *new_password,
+ const char *old_password)
+{
+ pstring param;
+ char data[532];
+ char *p = param;
+ unsigned char old_pw_hash[16];
+ unsigned char new_pw_hash[16];
+ unsigned int data_len;
+ unsigned int param_len = 0;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+
+ if (strlen(user) >= sizeof(fstring)-1) {
+ DEBUG(0,("cli_oem_change_password: user name %s is too long.\n", user));
+ return False;
+ }
+
+ SSVAL(p,0,214); /* SamOEMChangePassword command. */
+ p += 2;
+ pstrcpy_base(p, "zsT", param);
+ p = skip_string(p,1);
+ pstrcpy_base(p, "B516B16", param);
+ p = skip_string(p,1);
+ pstrcpy_base(p,user, param);
+ p = skip_string(p,1);
+ SSVAL(p,0,532);
+ p += 2;
+
+ param_len = PTR_DIFF(p,param);
+
+ /*
+ * Get the Lanman hash of the old password, we
+ * use this as the key to make_oem_passwd_hash().
+ */
+ E_deshash(old_password, old_pw_hash);
+
+ encode_pw_buffer(data, new_password, STR_ASCII);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("make_oem_passwd_hash\n"));
+ dump_data(100, data, 516);
+#endif
+ SamOEMhash( (unsigned char *)data, (unsigned char *)old_pw_hash, 516);
+
+ /*
+ * Now place the old password hash in the data.
+ */
+ E_deshash(new_password, new_pw_hash);
+
+ E_old_pw_hash( new_pw_hash, old_pw_hash, (uchar *)&data[516]);
+
+ data_len = 532;
+
+ if (cli_send_trans(cli,SMBtrans,
+ PIPE_LANMAN, /* name */
+ 0,0, /* fid, flags */
+ NULL,0,0, /* setup, length, max */
+ param,param_len,2, /* param, length, max */
+ data,data_len,0 /* data, length, max */
+ ) == False) {
+ DEBUG(0,("cli_oem_change_password: Failed to send password change for user %s\n",
+ user ));
+ return False;
+ }
+
+ if (!cli_receive_trans(cli,SMBtrans,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ DEBUG(0,("cli_oem_change_password: Failed to recieve reply to password change for user %s\n",
+ user ));
+ return False;
+ }
+
+ if (rparam)
+ cli->rap_error = SVAL(rparam,0);
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return (cli->rap_error == 0);
+}
+
+
+/****************************************************************************
+send a qpathinfo call
+****************************************************************************/
+BOOL cli_qpathinfo(struct cli_state *cli, const char *fname,
+ time_t *c_time, time_t *a_time, time_t *m_time,
+ size_t *size, uint16 *mode)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ unsigned int rparam_len, rdata_len;
+ uint16 setup = TRANSACT2_QPATHINFO;
+ pstring param;
+ char *rparam=NULL, *rdata=NULL;
+ int count=8;
+ BOOL ret;
+ time_t (*date_fn)(void *);
+ char *p;
+
+ p = param;
+ memset(p, 0, 6);
+ SSVAL(p, 0, SMB_INFO_STANDARD);
+ p += 6;
+ p += clistr_push(cli, p, fname, sizeof(pstring)-6, STR_TERMINATE);
+
+ param_len = PTR_DIFF(p, param);
+
+ do {
+ ret = (cli_send_trans(cli, SMBtrans2,
+ NULL, /* Name */
+ -1, 0, /* fid, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 10, /* param, length, max */
+ NULL, data_len, cli->max_xmit /* data, length, max */
+ ) &&
+ cli_receive_trans(cli, SMBtrans2,
+ &rparam, &rparam_len,
+ &rdata, &rdata_len));
+ if (!cli_is_dos_error(cli)) break;
+ if (!ret) {
+ /* we need to work around a Win95 bug - sometimes
+ it gives ERRSRV/ERRerror temprarily */
+ uint8 eclass;
+ uint32 ecode;
+ cli_dos_error(cli, &eclass, &ecode);
+ if (eclass != ERRSRV || ecode != ERRerror) break;
+ smb_msleep(100);
+ }
+ } while (count-- && ret==False);
+
+ if (!ret || !rdata || rdata_len < 22) {
+ return False;
+ }
+
+ if (cli->win95) {
+ date_fn = make_unix_date;
+ } else {
+ date_fn = make_unix_date2;
+ }
+
+ if (c_time) {
+ *c_time = date_fn(rdata+0);
+ }
+ if (a_time) {
+ *a_time = date_fn(rdata+4);
+ }
+ if (m_time) {
+ *m_time = date_fn(rdata+8);
+ }
+ if (size) {
+ *size = IVAL(rdata, 12);
+ }
+ if (mode) {
+ *mode = SVAL(rdata,l1_attrFile);
+ }
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return True;
+}
+
+/****************************************************************************
+send a qpathinfo call with the SMB_QUERY_FILE_ALL_INFO info level
+****************************************************************************/
+BOOL cli_qpathinfo2(struct cli_state *cli, const char *fname,
+ time_t *c_time, time_t *a_time, time_t *m_time,
+ time_t *w_time, size_t *size, uint16 *mode,
+ SMB_INO_T *ino)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ uint16 setup = TRANSACT2_QPATHINFO;
+ pstring param;
+ char *rparam=NULL, *rdata=NULL;
+ char *p;
+
+ p = param;
+ memset(p, 0, 6);
+ SSVAL(p, 0, SMB_QUERY_FILE_ALL_INFO);
+ p += 6;
+ p += clistr_push(cli, p, fname, sizeof(pstring)-6, STR_TERMINATE);
+
+ param_len = PTR_DIFF(p, param);
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL, /* name */
+ -1, 0, /* fid, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 10, /* param, length, max */
+ NULL, data_len, cli->max_xmit /* data, length, max */
+ )) {
+ return False;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ return False;
+ }
+
+ if (!rdata || data_len < 22) {
+ return False;
+ }
+
+ if (c_time) {
+ *c_time = interpret_long_date(rdata+0) - cli->serverzone;
+ }
+ if (a_time) {
+ *a_time = interpret_long_date(rdata+8) - cli->serverzone;
+ }
+ if (m_time) {
+ *m_time = interpret_long_date(rdata+16) - cli->serverzone;
+ }
+ if (w_time) {
+ *w_time = interpret_long_date(rdata+24) - cli->serverzone;
+ }
+ if (mode) {
+ *mode = SVAL(rdata, 32);
+ }
+ if (size) {
+ *size = IVAL(rdata, 48);
+ }
+ if (ino) {
+ *ino = IVAL(rdata, 64);
+ }
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return True;
+}
+
+
+/****************************************************************************
+send a qfileinfo QUERY_FILE_NAME_INFO call
+****************************************************************************/
+BOOL cli_qfilename(struct cli_state *cli, int fnum,
+ pstring name)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ uint16 setup = TRANSACT2_QFILEINFO;
+ pstring param;
+ char *rparam=NULL, *rdata=NULL;
+
+ param_len = 4;
+ memset(param, 0, param_len);
+ SSVAL(param, 0, fnum);
+ SSVAL(param, 2, SMB_QUERY_FILE_NAME_INFO);
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL, /* name */
+ -1, 0, /* fid, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 2, /* param, length, max */
+ NULL, data_len, cli->max_xmit /* data, length, max */
+ )) {
+ return False;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ return False;
+ }
+
+ if (!rdata || data_len < 4) {
+ return False;
+ }
+
+ clistr_pull(cli, name, rdata+4, sizeof(pstring), IVAL(rdata, 0), STR_UNICODE);
+
+ return True;
+}
+
+
+/****************************************************************************
+send a qfileinfo call
+****************************************************************************/
+BOOL cli_qfileinfo(struct cli_state *cli, int fnum,
+ uint16 *mode, size_t *size,
+ time_t *c_time, time_t *a_time, time_t *m_time,
+ time_t *w_time, SMB_INO_T *ino)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ uint16 setup = TRANSACT2_QFILEINFO;
+ pstring param;
+ char *rparam=NULL, *rdata=NULL;
+
+ /* if its a win95 server then fail this - win95 totally screws it
+ up */
+ if (cli->win95) return False;
+
+ param_len = 4;
+
+ memset(param, 0, param_len);
+ SSVAL(param, 0, fnum);
+ SSVAL(param, 2, SMB_QUERY_FILE_ALL_INFO);
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL, /* name */
+ -1, 0, /* fid, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 2, /* param, length, max */
+ NULL, data_len, cli->max_xmit /* data, length, max */
+ )) {
+ return False;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ return False;
+ }
+
+ if (!rdata || data_len < 68) {
+ return False;
+ }
+
+ if (c_time) {
+ *c_time = interpret_long_date(rdata+0) - cli->serverzone;
+ }
+ if (a_time) {
+ *a_time = interpret_long_date(rdata+8) - cli->serverzone;
+ }
+ if (m_time) {
+ *m_time = interpret_long_date(rdata+16) - cli->serverzone;
+ }
+ if (w_time) {
+ *w_time = interpret_long_date(rdata+24) - cli->serverzone;
+ }
+ if (mode) {
+ *mode = SVAL(rdata, 32);
+ }
+ if (size) {
+ *size = IVAL(rdata, 48);
+ }
+ if (ino) {
+ *ino = IVAL(rdata, 64);
+ }
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return True;
+}
+
+/****************************************************************************
+send a qfileinfo call
+****************************************************************************/
+BOOL cli_qfileinfo_test(struct cli_state *cli, int fnum, int level, char **poutdata, uint32 *poutlen)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ uint16 setup = TRANSACT2_QFILEINFO;
+ pstring param;
+ char *rparam=NULL, *rdata=NULL;
+
+ *poutdata = NULL;
+ *poutlen = 0;
+
+ /* if its a win95 server then fail this - win95 totally screws it
+ up */
+ if (cli->win95)
+ return False;
+
+ param_len = 4;
+
+ memset(param, 0, param_len);
+ SSVAL(param, 0, fnum);
+ SSVAL(param, 2, level);
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL, /* name */
+ -1, 0, /* fid, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 2, /* param, length, max */
+ NULL, data_len, cli->max_xmit /* data, length, max */
+ )) {
+ return False;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ return False;
+ }
+
+ *poutdata = memdup(rdata, data_len);
+ *poutlen = data_len;
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return True;
+}
+
+
+
+/****************************************************************************
+send a qpathinfo SMB_QUERY_FILE_ALT_NAME_INFO call
+****************************************************************************/
+NTSTATUS cli_qpathinfo_alt_name(struct cli_state *cli, const char *fname, fstring alt_name)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ uint16 setup = TRANSACT2_QPATHINFO;
+ pstring param;
+ char *rparam=NULL, *rdata=NULL;
+ int count=8;
+ char *p;
+ BOOL ret;
+ unsigned int len;
+
+ p = param;
+ memset(p, 0, 6);
+ SSVAL(p, 0, SMB_QUERY_FILE_ALT_NAME_INFO);
+ p += 6;
+ p += clistr_push(cli, p, fname, sizeof(pstring)-6, STR_TERMINATE);
+
+ param_len = PTR_DIFF(p, param);
+
+ do {
+ ret = (cli_send_trans(cli, SMBtrans2,
+ NULL, /* Name */
+ -1, 0, /* fid, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 10, /* param, length, max */
+ NULL, data_len, cli->max_xmit /* data, length, max */
+ ) &&
+ cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len));
+ if (!ret && cli_is_dos_error(cli)) {
+ /* we need to work around a Win95 bug - sometimes
+ it gives ERRSRV/ERRerror temprarily */
+ uint8 eclass;
+ uint32 ecode;
+ cli_dos_error(cli, &eclass, &ecode);
+ if (eclass != ERRSRV || ecode != ERRerror) break;
+ smb_msleep(100);
+ }
+ } while (count-- && ret==False);
+
+ if (!ret || !rdata || data_len < 4) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ len = IVAL(rdata, 0);
+
+ if (len > data_len - 4) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ clistr_pull(cli, alt_name, rdata+4, sizeof(fstring), len, STR_UNICODE);
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ return NT_STATUS_OK;
+}
diff --git a/source/libsmb/clirap2.c b/source/libsmb/clirap2.c
new file mode 100644
index 00000000000..12a3d63aff3
--- /dev/null
+++ b/source/libsmb/clirap2.c
@@ -0,0 +1,1970 @@
+/*
+ Samba Unix/Linux SMB client library
+ More client RAP (SMB Remote Procedure Calls) functions
+ Copyright (C) 2001 Steve French (sfrench@us.ibm.com)
+ Copyright (C) 2001 Jim McDonough (jmcd@us.ibm.com)
+
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*****************************************************/
+/* */
+/* Additional RAP functionality */
+/* */
+/* RAP is the original SMB RPC, documented */
+/* by Microsoft and X/Open in the 1990s and */
+/* supported by most SMB/CIFS servers although */
+/* it is unlikely that any one implementation */
+/* supports all RAP command codes since some */
+/* are quite obsolete and a few are specific */
+/* to a particular network operating system */
+/* */
+/* Although it has largely been replaced */
+/* for complex remote admistration and management */
+/* (of servers) by the relatively newer */
+/* DCE/RPC based remote API (which better handles */
+/* large >64K data structures), there are many */
+/* important administrative and resource location */
+/* tasks and user tasks (e.g. password change) */
+/* that are performed via RAP. */
+/* */
+/* Although a few of the RAP calls are implemented */
+/* in the Samba client library already (clirap.c) */
+/* the new ones are in clirap2.c for easy patching */
+/* and integration and a corresponding header */
+/* file, rap.h, has been created. */
+/* */
+/* This is based on data from the CIFS spec */
+/* and the LAN Server and LAN Manager */
+/* Programming Reference books and published */
+/* RAP document and CIFS forum postings and */
+/* lots of trial and error */
+/* */
+/* Function names changed from API_ (as they are */
+/* in the CIFS specification) to RAP_ in order */
+/* to avoid confusion with other API calls */
+/* sent via DCE RPC */
+/* */
+/*****************************************************/
+
+/*****************************************************/
+/* */
+/* cifsrap.c already includes support for: */
+/* */
+/* WshareEnum ( API number 0, level 1) */
+/* NetServerEnum2 (API num 104, level 1) */
+/* WWkstaUserLogon (132) */
+/* SamOEMchgPasswordUser2_P (214) */
+/* */
+/* cifsprint.c already includes support for: */
+/* */
+/* WPrintJobEnum (API num 76, level 2) */
+/* WPrintJobDel (API num 81) */
+/* */
+/*****************************************************/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+#define WORDSIZE 2
+#define DWORDSIZE 4
+
+#define PUTBYTE(p,b) do {SCVAL(p,0,b); p++;} while(0)
+#define GETBYTE(p,b) do {b = CVAL(p,0); p++;} while(0)
+#define PUTWORD(p,w) do {SSVAL(p,0,w); p += WORDSIZE;} while(0)
+#define GETWORD(p,w) do {w = SVAL(p,0); p += WORDSIZE;} while(0)
+#define PUTDWORD(p,d) do {SIVAL(p,0,d); p += DWORDSIZE;} while(0)
+#define GETDWORD(p,d) do {d = IVAL(p,0); p += DWORDSIZE;} while(0)
+#define GETRES(p) p ? SVAL(p,0) : -1
+/* put string s at p with max len n and increment p past string */
+#define PUTSTRING(p,s,n) do {\
+ push_ascii(p,s?s:"",n?n:256,STR_TERMINATE);\
+ p = skip_string(p,1);\
+ } while(0)
+/* put string s and p, using fixed len l, and increment p by l */
+#define PUTSTRINGF(p,s,l) do {\
+ push_ascii(p,s?s:"",l,STR_TERMINATE);\
+ p += l;\
+ } while (0)
+/* put string pointer at p, supplying offset o from rdata r, store */
+/* dword offset at p, increment p by 4 and o by length of s. This */
+/* means on the first call, you must calc the offset yourself! */
+#define PUTSTRINGP(p,s,r,o) do {\
+ if (s) {\
+ push_ascii(r+o,s,strlen(s)+1,STR_TERMINATE);\
+ PUTDWORD(p,o);\
+ o += strlen(s) + 1;\
+ } else PUTDWORD(p,0);\
+ }while(0);
+/* get asciiz string s from p, increment p past string */
+#define GETSTRING(p,s) do {\
+ pull_ascii_pstring(s,p);\
+ p = skip_string(p,1);\
+ } while(0)
+/* get fixed length l string s from p, increment p by l */
+#define GETSTRINGF(p,s,l) do {\
+ pull_ascii_pstring(s,p);\
+ p += l;\
+ } while(0)
+/* get string s from offset (obtained at p) from rdata r - converter c */
+#define GETSTRINGP(p,s,r,c) do {\
+ uint32 off;\
+ GETDWORD(p,off);\
+ off &= 0x0000FFFF; /* mask the obsolete segment number from the offset */ \
+ pull_ascii_pstring(s, off?(r+off-c):"");\
+ } while(0)
+
+static char *make_header(char *param, uint16 apinum, const char *reqfmt, const char *datafmt)
+{
+ PUTWORD(param,apinum);
+ if (reqfmt)
+ PUTSTRING(param,reqfmt,0);
+ else
+ *param++ = (char) 0;
+
+ if (datafmt)
+ PUTSTRING(param,datafmt,0);
+ else
+ *param++ = (char) 0;
+
+ return param;
+}
+
+
+/****************************************************************************
+ call a NetGroupDelete - delete user group from remote server
+****************************************************************************/
+int cli_NetGroupDelete(struct cli_state *cli, const char *group_name )
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetGroupDel_REQ) /* parm string */
+ +1 /* no ret string */
+ +RAP_GROUPNAME_LEN /* group to del */
+ +WORDSIZE]; /* reserved word */
+
+ /* now send a SMBtrans command with api GroupDel */
+ p = make_header(param, RAP_WGroupDel, RAP_NetGroupDel_REQ, NULL);
+ PUTSTRING(p, group_name, RAP_GROUPNAME_LEN);
+ PUTWORD(p,0); /* reserved word MBZ on input */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 200, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ res = GETRES(rparam);
+
+ if (res == 0) {
+ /* nothing to do */
+ }
+ else if ((res == 5) || (res == 65)) {
+ DEBUG(1, ("Access Denied\n"));
+ }
+ else if (res == 2220) {
+ DEBUG (1, ("Group does not exist\n"));
+ }
+ else {
+ DEBUG(4,("NetGroupDelete res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetGroupDelete failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+ call a NetGroupAdd - add user group to remote server
+****************************************************************************/
+int cli_NetGroupAdd(struct cli_state *cli, RAP_GROUP_INFO_1 * grinfo )
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetGroupAdd_REQ) /* req string */
+ +sizeof(RAP_GROUP_INFO_L1) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* reserved word */
+
+ char data[1024];
+
+ /* offset into data of free format strings. Will be updated */
+ /* by PUTSTRINGP macro and end up with total data length. */
+ int soffset = RAP_GROUPNAME_LEN + 1 + DWORDSIZE;
+
+ /* now send a SMBtrans command with api WGroupAdd */
+
+ p = make_header(param, RAP_WGroupAdd,
+ RAP_NetGroupAdd_REQ, RAP_GROUP_INFO_L1);
+ PUTWORD(p, 1); /* info level */
+ PUTWORD(p, 0); /* reserved word 0 */
+
+ p = data;
+ PUTSTRINGF(p, grinfo->group_name, RAP_GROUPNAME_LEN);
+ PUTBYTE(p, 0); /* pad byte 0 */
+ PUTSTRINGP(p, grinfo->comment, data, soffset);
+
+ if (cli_api(cli,
+ param, sizeof(param), 1024, /* Param, length, maxlen */
+ data, soffset, sizeof(data), /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ res = GETRES(rparam);
+
+ if (res == 0) {
+ /* nothing to do */
+ } else if ((res == 5) || (res == 65)) {
+ DEBUG(1, ("Access Denied\n"));
+ }
+ else if (res == 2223) {
+ DEBUG (1, ("Group already exists\n"));
+ }
+ else {
+ DEBUG(4,("NetGroupAdd res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetGroupAdd failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+call a NetGroupEnum - try and list user groups on a different host
+****************************************************************************/
+int cli_RNetGroupEnum(struct cli_state *cli, void (*fn)(const char *, const char *, void *), void *state)
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetGroupEnum_REQ) /* parm string */
+ +sizeof(RAP_GROUP_INFO_L1) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+
+ memset(param, '\0', sizeof(param));
+ p = make_header(param, RAP_WGroupEnum,
+ RAP_NetGroupEnum_REQ, RAP_GROUP_INFO_L1);
+ PUTWORD(p,1); /* Info level 1 */ /* add level 0 */
+ PUTWORD(p,0xFFE0); /* Return buffer size */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),8,
+ NULL, 0, 0xFFE0 /* data area size */,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ res = GETRES(rparam);
+ cli->rap_error = res;
+ if(cli->rap_error == 234)
+ DEBUG(1,("Not all group names were returned (such as those longer than 21 characters)\n"));
+ else if (cli->rap_error != 0) {
+ DEBUG(1,("NetGroupEnum gave error %d\n", cli->rap_error));
+ }
+ }
+
+ if (rdata) {
+ if (res == 0 || res == ERRmoredata) {
+ int i, converter, count;
+
+ p = rparam + WORDSIZE; /* skip result */
+ GETWORD(p, converter);
+ GETWORD(p, count);
+
+ for (i=0,p=rdata;i<count;i++) {
+ pstring comment;
+ char groupname[RAP_GROUPNAME_LEN];
+
+ GETSTRINGF(p, groupname, RAP_GROUPNAME_LEN);
+ p++; /* pad byte */
+ GETSTRINGP(p, comment, rdata, converter);
+
+ fn(groupname, comment, cli);
+ }
+ } else {
+ DEBUG(4,("NetGroupEnum res=%d\n", res));
+ }
+ } else {
+ DEBUG(4,("NetGroupEnum no data returned\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+int cli_NetGroupDelUser(struct cli_state * cli, const char *group_name, const char *user_name)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetGroupDelUser_REQ) /* parm string */
+ +1 /* no ret string */
+ +RAP_GROUPNAME_LEN /* group name */
+ +RAP_USERNAME_LEN]; /* user to del */
+
+ /* now send a SMBtrans command with api GroupMemberAdd */
+ p = make_header(param, RAP_WGroupDelUser, RAP_NetGroupDelUser_REQ, NULL);
+ PUTSTRING(p,group_name,RAP_GROUPNAME_LEN);
+ PUTSTRING(p,user_name,RAP_USERNAME_LEN);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 200, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ res = GETRES(rparam);
+
+ switch(res) {
+ case 0:
+ break;
+ case 5:
+ case 65:
+ DEBUG(1, ("Access Denied\n"));
+ break;
+ case 50:
+ DEBUG(1, ("Not supported by server\n"));
+ break;
+ case 2220:
+ DEBUG(1, ("Group does not exist\n"));
+ break;
+ case 2221:
+ DEBUG(1, ("User does not exist\n"));
+ break;
+ case 2237:
+ DEBUG(1, ("User is not in group\n"));
+ break;
+ default:
+ DEBUG(4,("NetGroupDelUser res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetGroupDelUser failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+int cli_NetGroupAddUser(struct cli_state * cli, const char *group_name, const char *user_name)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetGroupAddUser_REQ) /* parm string */
+ +1 /* no ret string */
+ +RAP_GROUPNAME_LEN /* group name */
+ +RAP_USERNAME_LEN]; /* user to add */
+
+ /* now send a SMBtrans command with api GroupMemberAdd */
+ p = make_header(param, RAP_WGroupAddUser, RAP_NetGroupAddUser_REQ, NULL);
+ PUTSTRING(p,group_name,RAP_GROUPNAME_LEN);
+ PUTSTRING(p,user_name,RAP_USERNAME_LEN);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 200, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ res = GETRES(rparam);
+
+ switch(res) {
+ case 0:
+ break;
+ case 5:
+ case 65:
+ DEBUG(1, ("Access Denied\n"));
+ break;
+ case 50:
+ DEBUG(1, ("Not supported by server\n"));
+ break;
+ case 2220:
+ DEBUG(1, ("Group does not exist\n"));
+ break;
+ case 2221:
+ DEBUG(1, ("User does not exist\n"));
+ break;
+ default:
+ DEBUG(4,("NetGroupAddUser res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetGroupAddUser failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+
+int cli_NetGroupGetUsers(struct cli_state * cli, const char *group_name, void (*fn)(const char *, void *), void *state )
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res = -1;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetGroupGetUsers_REQ)/* parm string */
+ +sizeof(RAP_GROUP_USERS_INFO_0) /* return string */
+ +RAP_GROUPNAME_LEN /* group name */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+
+ /* now send a SMBtrans command with api GroupGetUsers */
+ p = make_header(param, RAP_WGroupGetUsers,
+ RAP_NetGroupGetUsers_REQ, RAP_GROUP_USERS_INFO_0);
+ PUTSTRING(p,group_name,RAP_GROUPNAME_LEN-1);
+ PUTWORD(p,0); /* info level 0 */
+ PUTWORD(p,0xFFE0); /* return buffer size */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),PTR_DIFF(p,param),
+ NULL, 0, CLI_BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ res = GETRES(rparam);
+ cli->rap_error = res;
+ if (res != 0) {
+ DEBUG(1,("NetGroupGetUsers gave error %d\n", res));
+ }
+ }
+ if (rdata) {
+ if (res == 0 || res == ERRmoredata) {
+ int i, converter, count;
+ fstring username;
+ p = rparam +WORDSIZE;
+ GETWORD(p, converter);
+ GETWORD(p, count);
+
+ for (i=0,p=rdata; i<count; i++) {
+ GETSTRINGF(p, username, RAP_USERNAME_LEN);
+ fn(username, state);
+ }
+ } else {
+ DEBUG(4,("NetGroupGetUsers res=%d\n", res));
+ }
+ } else {
+ DEBUG(4,("NetGroupGetUsers no data returned\n"));
+ }
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return res;
+}
+
+int cli_NetUserGetGroups(struct cli_state * cli, const char *user_name, void (*fn)(const char *, void *), void *state )
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res = -1;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetUserGetGroups_REQ)/* parm string */
+ +sizeof(RAP_GROUP_USERS_INFO_0) /* return string */
+ +RAP_USERNAME_LEN /* user name */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+
+ /* now send a SMBtrans command with api GroupGetUsers */
+ p = make_header(param, RAP_WUserGetGroups,
+ RAP_NetUserGetGroups_REQ, RAP_GROUP_USERS_INFO_0);
+ PUTSTRING(p,user_name,RAP_USERNAME_LEN-1);
+ PUTWORD(p,0); /* info level 0 */
+ PUTWORD(p,0xFFE0); /* return buffer size */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),PTR_DIFF(p,param),
+ NULL, 0, CLI_BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ res = GETRES(rparam);
+ cli->rap_error = res;
+ if (res != 0) {
+ DEBUG(1,("NetUserGetGroups gave error %d\n", res));
+ }
+ }
+ if (rdata) {
+ if (res == 0 || res == ERRmoredata) {
+ int i, converter, count;
+ fstring groupname;
+ p = rparam +WORDSIZE;
+ GETWORD(p, converter);
+ GETWORD(p, count);
+
+ for (i=0,p=rdata; i<count; i++) {
+ GETSTRINGF(p, groupname, RAP_USERNAME_LEN);
+ fn(groupname, state);
+ }
+ } else {
+ DEBUG(4,("NetUserGetGroups res=%d\n", res));
+ }
+ } else {
+ DEBUG(4,("NetUserGetGroups no data returned\n"));
+ }
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return res;
+}
+
+
+/****************************************************************************
+ call a NetUserDelete - delete user from remote server
+****************************************************************************/
+int cli_NetUserDelete(struct cli_state *cli, const char * user_name )
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetGroupDel_REQ) /* parm string */
+ +1 /* no ret string */
+ +RAP_USERNAME_LEN /* user to del */
+ +WORDSIZE]; /* reserved word */
+
+ /* now send a SMBtrans command with api UserDel */
+ p = make_header(param, RAP_WUserDel, RAP_NetGroupDel_REQ, NULL);
+ PUTSTRING(p, user_name, RAP_USERNAME_LEN);
+ PUTWORD(p,0); /* reserved word MBZ on input */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 200, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ res = GETRES(rparam);
+
+ if (res == 0) {
+ /* nothing to do */
+ }
+ else if ((res == 5) || (res == 65)) {
+ DEBUG(1, ("Access Denied\n"));
+ }
+ else if (res == 2221) {
+ DEBUG (1, ("User does not exist\n"));
+ }
+ else {
+ DEBUG(4,("NetUserDelete res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetUserDelete failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+ call a NetUserAdd - add user to remote server
+****************************************************************************/
+int cli_NetUserAdd(struct cli_state *cli, RAP_USER_INFO_1 * userinfo )
+{
+
+
+
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetUserAdd2_REQ) /* req string */
+ +sizeof(RAP_USER_INFO_L1) /* data string */
+ +WORDSIZE /* info level */
+ +WORDSIZE /* buffer length */
+ +WORDSIZE]; /* reserved */
+
+ char data[1024];
+ /* offset into data of free format strings. Will be updated */
+ /* by PUTSTRINGP macro and end up with total data length. */
+ int soffset=RAP_USERNAME_LEN+1 /* user name + pad */
+ + RAP_UPASSWD_LEN /* password */
+ + DWORDSIZE /* password age */
+ + WORDSIZE /* privilege */
+ + DWORDSIZE /* home dir ptr */
+ + DWORDSIZE /* comment ptr */
+ + WORDSIZE /* flags */
+ + DWORDSIZE; /* login script ptr*/
+
+ /* now send a SMBtrans command with api NetUserAdd */
+ p = make_header(param, RAP_WUserAdd2,
+ RAP_NetUserAdd2_REQ, RAP_USER_INFO_L1);
+ PUTWORD(p, 1); /* info level */
+
+ PUTWORD(p, 0); /* pwencrypt */
+ if(userinfo->passwrd)
+ PUTWORD(p,MIN(strlen(userinfo->passwrd), RAP_UPASSWD_LEN));
+ else
+ PUTWORD(p, 0); /* password length */
+
+ p = data;
+ memset(data, '\0', soffset);
+
+ PUTSTRINGF(p, userinfo->user_name, RAP_USERNAME_LEN);
+ PUTBYTE(p, 0); /* pad byte 0 */
+ PUTSTRINGF(p, userinfo->passwrd, RAP_UPASSWD_LEN);
+ PUTDWORD(p, 0); /* pw age - n.a. on user add */
+ PUTWORD(p, userinfo->priv);
+ PUTSTRINGP(p, userinfo->home_dir, data, soffset);
+ PUTSTRINGP(p, userinfo->comment, data, soffset);
+ PUTWORD(p, userinfo->userflags);
+ PUTSTRINGP(p, userinfo->logon_script, data, soffset);
+
+ if (cli_api(cli,
+ param, sizeof(param), 1024, /* Param, length, maxlen */
+ data, soffset, sizeof(data), /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ res = GETRES(rparam);
+
+ if (res == 0) {
+ /* nothing to do */
+ }
+ else if ((res == 5) || (res == 65)) {
+ DEBUG(1, ("Access Denied\n"));
+ }
+ else if (res == 2224) {
+ DEBUG (1, ("User already exists\n"));
+ }
+ else {
+ DEBUG(4,("NetUserAdd res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetUserAdd failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+call a NetUserEnum - try and list users on a different host
+****************************************************************************/
+int cli_RNetUserEnum(struct cli_state *cli, void (*fn)(const char *, const char *, const char *, const char *, void *), void *state)
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetUserEnum_REQ) /* parm string */
+ +sizeof(RAP_USER_INFO_L1) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+
+ memset(param, '\0', sizeof(param));
+ p = make_header(param, RAP_WUserEnum,
+ RAP_NetUserEnum_REQ, RAP_USER_INFO_L1);
+ PUTWORD(p,1); /* Info level 1 */
+ PUTWORD(p,0xFF00); /* Return buffer size */
+
+/* BB Fix handling of large numbers of users to be returned */
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),8,
+ NULL, 0, CLI_BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ res = GETRES(rparam);
+ cli->rap_error = res;
+ if (cli->rap_error != 0) {
+ DEBUG(1,("NetUserEnum gave error %d\n", cli->rap_error));
+ }
+ }
+ if (rdata) {
+ if (res == 0 || res == ERRmoredata) {
+ int i, converter, count;
+ char username[RAP_USERNAME_LEN];
+ char userpw[RAP_UPASSWD_LEN];
+ pstring comment, homedir, logonscript;
+ int pwage, priv, flags;
+
+ p = rparam + WORDSIZE; /* skip result */
+ GETWORD(p, converter);
+ GETWORD(p, count);
+
+ for (i=0,p=rdata;i<count;i++) {
+ GETSTRINGF(p, username, RAP_USERNAME_LEN);
+ p++; /* pad byte */
+ GETSTRINGF(p, userpw, RAP_UPASSWD_LEN);
+ GETDWORD(p, pwage); /* password age */
+ GETWORD(p, priv); /* 0=guest, 1=user, 2=admin */
+ GETSTRINGP(p, homedir, rdata, converter);
+ GETSTRINGP(p, comment, rdata, converter);
+ GETWORD(p, flags);
+ GETSTRINGP(p, logonscript, rdata, converter);
+
+ fn(username, comment, homedir, logonscript, cli);
+ }
+ } else {
+ DEBUG(4,("NetUserEnum res=%d\n", res));
+ }
+ } else {
+ DEBUG(4,("NetUserEnum no data returned\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+ call a NetFileClose2 - close open file on another session to server
+****************************************************************************/
+int cli_NetFileClose(struct cli_state *cli, uint32 file_id )
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_WFileClose2_REQ) /* req string */
+ +1 /* no ret string */
+ +DWORDSIZE]; /* file ID */
+ int res = -1;
+
+ /* now send a SMBtrans command with api RNetShareEnum */
+ p = make_header(param, RAP_WFileClose2, RAP_WFileClose2_REQ, NULL);
+ PUTDWORD(p, file_id);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 200, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ res = GETRES(rparam);
+
+ if (res == 0) {
+ /* nothing to do */
+ } else if (res == 2314){
+ DEBUG(1, ("NetFileClose2 - attempt to close non-existant file open instance\n"));
+ } else {
+ DEBUG(4,("NetFileClose2 res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetFileClose2 failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+call a NetFileGetInfo - get information about server file opened from other
+ workstation
+****************************************************************************/
+int cli_NetFileGetInfo(struct cli_state *cli, uint32 file_id, void (*fn)(const char *, const char *, uint16, uint16, uint32))
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_WFileGetInfo2_REQ) /* req string */
+ +sizeof(RAP_FILE_INFO_L3) /* return string */
+ +DWORDSIZE /* file ID */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+
+ /* now send a SMBtrans command with api RNetShareEnum */
+ p = make_header(param, RAP_WFileGetInfo2,
+ RAP_WFileGetInfo2_REQ, RAP_FILE_INFO_L3);
+ PUTDWORD(p, file_id);
+ PUTWORD(p, 3); /* info level */
+ PUTWORD(p, 0x1000); /* buffer size */
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 0x1000, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ res = GETRES(rparam);
+ if (res == 0 || res == ERRmoredata) {
+ int converter,id, perms, locks;
+ pstring fpath, fuser;
+
+ p = rparam + WORDSIZE; /* skip result */
+ GETWORD(p, converter);
+
+ p = rdata;
+ GETDWORD(p, id);
+ GETWORD(p, perms);
+ GETWORD(p, locks);
+ GETSTRINGP(p, fpath, rdata, converter);
+ GETSTRINGP(p, fuser, rdata, converter);
+
+ fn(fpath, fuser, perms, locks, id);
+ } else {
+ DEBUG(4,("NetFileGetInfo2 res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetFileGetInfo2 failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+* Call a NetFileEnum2 - list open files on an SMB server
+*
+* PURPOSE: Remotes a NetFileEnum API call to the current server or target
+* server listing the files open via the network (and their
+* corresponding open instance ids)
+*
+* Dependencies: none
+*
+* Parameters:
+* cli - pointer to cli_state structure
+* user - if present, return only files opened by this remote user
+* base_path - if present, return only files opened below this
+* base path
+* fn - display function to invoke for each entry in the result
+*
+*
+* Returns:
+* True - success
+* False - failure
+*
+****************************************************************************/
+int cli_NetFileEnum(struct cli_state *cli, char * user, char * base_path, void (*fn)(const char *, const char *, uint16, uint16, uint32))
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_WFileEnum2_REQ) /* req string */
+ +sizeof(RAP_FILE_INFO_L3) /* return string */
+ +256 /* base path (opt) */
+ +RAP_USERNAME_LEN /* user name (opt) */
+ +WORDSIZE /* info level */
+ +WORDSIZE /* buffer size */
+ +DWORDSIZE /* resume key ? */
+ +DWORDSIZE]; /* resume key ? */
+ int count = -1;
+
+ /* now send a SMBtrans command with api RNetShareEnum */
+ p = make_header(param, RAP_WFileEnum2,
+ RAP_WFileEnum2_REQ, RAP_FILE_INFO_L3);
+
+ PUTSTRING(p, base_path, 256);
+ PUTSTRING(p, user, RAP_USERNAME_LEN);
+ PUTWORD(p, 3); /* info level */
+ PUTWORD(p, 0xFF00); /* buffer size */
+ PUTDWORD(p, 0); /* zero out the resume key */
+ PUTDWORD(p, 0); /* or is this one the resume key? */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 0xFF00, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ int res = GETRES(rparam);
+
+ if (res == 0 || res == ERRmoredata) {
+ int converter, i;
+
+ p = rparam + WORDSIZE; /* skip result */
+ GETWORD(p, converter);
+ GETWORD(p, count);
+
+ p = rdata;
+ for (i=0; i<count; i++) {
+ int id, perms, locks;
+ pstring fpath, fuser;
+
+ GETDWORD(p, id);
+ GETWORD(p, perms);
+ GETWORD(p, locks);
+ GETSTRINGP(p, fpath, rdata, converter);
+ GETSTRINGP(p, fuser, rdata, converter);
+
+ fn(fpath, fuser, perms, locks, id);
+ } /* BB fix ERRmoredata case to send resume request */
+ } else {
+ DEBUG(4,("NetFileEnum2 res=%d\n", res));
+ }
+ } else {
+ DEBUG(4,("NetFileEnum2 failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return count;
+}
+
+/****************************************************************************
+ call a NetShareAdd - share/export directory on remote server
+****************************************************************************/
+int cli_NetShareAdd(struct cli_state *cli, RAP_SHARE_INFO_2 * sinfo )
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_WShareAdd_REQ) /* req string */
+ +sizeof(RAP_SHARE_INFO_L2) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* reserved word */
+ char data[1024];
+ /* offset to free format string section following fixed length data. */
+ /* will be updated by PUTSTRINGP macro and will end up with total len */
+ int soffset = RAP_SHARENAME_LEN + 1 /* share name + pad */
+ + WORDSIZE /* share type */
+ + DWORDSIZE /* comment pointer */
+ + WORDSIZE /* permissions */
+ + WORDSIZE /* max users */
+ + WORDSIZE /* active users */
+ + DWORDSIZE /* share path */
+ + RAP_SPASSWD_LEN + 1; /* share password + pad */
+
+ memset(param,'\0',sizeof(param));
+ /* now send a SMBtrans command with api RNetShareAdd */
+ p = make_header(param, RAP_WshareAdd,
+ RAP_WShareAdd_REQ, RAP_SHARE_INFO_L2);
+ PUTWORD(p, 2); /* info level */
+ PUTWORD(p, 0); /* reserved word 0 */
+
+ p = data;
+ PUTSTRINGF(p, sinfo->share_name, RAP_SHARENAME_LEN);
+ PUTBYTE(p, 0); /* pad byte 0 */
+
+ PUTWORD(p, sinfo->share_type);
+ PUTSTRINGP(p, sinfo->comment, data, soffset);
+ PUTWORD(p, sinfo->perms);
+ PUTWORD(p, sinfo->maximum_users);
+ PUTWORD(p, sinfo->active_users);
+ PUTSTRINGP(p, sinfo->path, data, soffset);
+ PUTSTRINGF(p, sinfo->password, RAP_SPASSWD_LEN);
+ SCVAL(p,-1,0x0A); /* required 0x0A at end of password */
+
+ if (cli_api(cli,
+ param, sizeof(param), 1024, /* Param, length, maxlen */
+ data, soffset, sizeof(data), /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ res = rparam? SVAL(rparam,0) : -1;
+
+ if (res == 0) {
+ /* nothing to do */
+ }
+ else {
+ DEBUG(4,("NetShareAdd res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetShareAdd failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+/****************************************************************************
+ call a NetShareDelete - unshare exported directory on remote server
+****************************************************************************/
+int cli_NetShareDelete(struct cli_state *cli, const char * share_name )
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_WShareDel_REQ) /* req string */
+ +1 /* no ret string */
+ +RAP_SHARENAME_LEN /* share to del */
+ +WORDSIZE]; /* reserved word */
+
+
+ /* now send a SMBtrans command with api RNetShareDelete */
+ p = make_header(param, RAP_WshareDel, RAP_WShareDel_REQ, NULL);
+ PUTSTRING(p,share_name,RAP_SHARENAME_LEN);
+ PUTWORD(p,0); /* reserved word MBZ on input */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 200, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ res = GETRES(rparam);
+
+ if (res == 0) {
+ /* nothing to do */
+ }
+ else {
+ DEBUG(4,("NetShareDelete res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetShareDelete failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+/*************************************************************************
+*
+* Function Name: cli_get_pdc_name
+*
+* PURPOSE: Remotes a NetServerEnum API call to the current server
+* requesting the name of a server matching the server
+* type of SV_TYPE_DOMAIN_CTRL (PDC).
+*
+* Dependencies: none
+*
+* Parameters:
+* cli - pointer to cli_state structure
+* workgroup - pointer to string containing name of domain
+* pdc_name - pointer to string that will contain PDC name
+* on successful return
+*
+* Returns:
+* True - success
+* False - failure
+*
+************************************************************************/
+BOOL cli_get_pdc_name(struct cli_state *cli, char *workgroup, char *pdc_name)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rdrcnt,rprcnt;
+ char *p;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetServerEnum2_REQ) /* req string */
+ +sizeof(RAP_SERVER_INFO_L1) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE /* buffer size */
+ +DWORDSIZE /* server type */
+ +RAP_MACHNAME_LEN]; /* workgroup */
+ int count = -1;
+
+ *pdc_name = '\0';
+
+ /* send a SMBtrans command with api NetServerEnum */
+ p = make_header(param, RAP_NetServerEnum2,
+ RAP_NetServerEnum2_REQ, RAP_SERVER_INFO_L1);
+ PUTWORD(p, 1); /* info level */
+ PUTWORD(p, CLI_BUFFER_SIZE);
+ PUTDWORD(p, SV_TYPE_DOMAIN_CTRL);
+ PUTSTRING(p, workgroup, RAP_MACHNAME_LEN);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 8, /* params, length, max */
+ NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */
+ &rparam, &rprcnt, /* return params, return size */
+ &rdata, &rdrcnt /* return data, return size */
+ )) {
+ cli->rap_error = GETRES(rparam);
+
+ /*
+ * We only really care to copy a name if the
+ * API succeeded and we got back a name.
+ */
+ if (cli->rap_error == 0) {
+ p = rparam + WORDSIZE + WORDSIZE; /* skip result and converter */
+ GETWORD(p, count);
+ p = rdata;
+
+ if (count > 0)
+ GETSTRING(p, pdc_name);
+ }
+ else {
+ DEBUG(4,("cli_get_pdc_name: machine %s failed the NetServerEnum call. "
+ "Error was : %s.\n", cli->desthost, cli_errstr(cli) ));
+ }
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return(count > 0);
+}
+
+
+/*************************************************************************
+*
+* Function Name: cli_get_server_domain
+*
+* PURPOSE: Remotes a NetWkstaGetInfo API call to the current server
+* requesting wksta_info_10 level information to determine
+* the domain the server belongs to. On success, this
+* routine sets the server_domain field in the cli_state structure
+* to the server's domain name.
+*
+* Dependencies: none
+*
+* Parameters:
+* cli - pointer to cli_state structure
+*
+* Returns:
+* True - success
+* False - failure
+*
+* Origins: samba 2.0.6 source/libsmb/clientgen.c cli_NetServerEnum()
+*
+************************************************************************/
+BOOL cli_get_server_domain(struct cli_state *cli)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rdrcnt,rprcnt;
+ char *p;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_WWkstaGetInfo_REQ) /* req string */
+ +sizeof(RAP_WKSTA_INFO_L10) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+ int res = -1;
+
+ /* send a SMBtrans command with api NetWkstaGetInfo */
+ p = make_header(param, RAP_WWkstaGetInfo,
+ RAP_WWkstaGetInfo_REQ, RAP_WKSTA_INFO_L10);
+ PUTWORD(p, 10); /* info level */
+ PUTWORD(p, CLI_BUFFER_SIZE);
+
+ if (cli_api(cli, param, PTR_DIFF(p,param), 8, /* params, length, max */
+ NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */
+ &rparam, &rprcnt, /* return params, return size */
+ &rdata, &rdrcnt)) { /* return data, return size */
+ res = GETRES(rparam);
+ p = rdata;
+
+ if (res == 0) {
+ int converter;
+
+ p = rparam + WORDSIZE;
+ GETWORD(p, converter);
+
+ p = rdata + DWORDSIZE + DWORDSIZE; /* skip computer & user names */
+ GETSTRINGP(p, cli->server_domain, rdata, converter);
+ }
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return(res == 0);
+}
+
+
+/*************************************************************************
+*
+* Function Name: cli_get_server_type
+*
+* PURPOSE: Remotes a NetServerGetInfo API call to the current server
+* requesting server_info_1 level information to retrieve
+* the server type.
+*
+* Dependencies: none
+*
+* Parameters:
+* cli - pointer to cli_state structure
+* pstype - pointer to uint32 to contain returned server type
+*
+* Returns:
+* True - success
+* False - failure
+*
+* Origins: samba 2.0.6 source/libsmb/clientgen.c cli_NetServerEnum()
+*
+************************************************************************/
+BOOL cli_get_server_type(struct cli_state *cli, uint32 *pstype)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rdrcnt,rprcnt;
+ char *p;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_WserverGetInfo_REQ) /* req string */
+ +sizeof(RAP_SERVER_INFO_L1) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+ int res = -1;
+
+ /* send a SMBtrans command with api NetServerGetInfo */
+ p = make_header(param, RAP_WserverGetInfo,
+ RAP_WserverGetInfo_REQ, RAP_SERVER_INFO_L1);
+ PUTWORD(p, 1); /* info level */
+ PUTWORD(p, CLI_BUFFER_SIZE);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 8, /* params, length, max */
+ NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */
+ &rparam, &rprcnt, /* return params, return size */
+ &rdata, &rdrcnt /* return data, return size */
+ )) {
+
+ res = GETRES(rparam);
+
+ if (res == 0 || res == ERRmoredata) {
+ p = rdata;
+ *pstype = IVAL(p,18) & ~SV_TYPE_LOCAL_LIST_ONLY;
+ }
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return(res == 0 || res == ERRmoredata);
+}
+
+
+/*************************************************************************
+*
+* Function Name: cli_ns_check_server_type
+*
+* PURPOSE: Remotes a NetServerEnum2 API call to the current server
+* requesting server_info_0 level information of machines
+* matching the given server type. If the returned server
+* list contains the machine name contained in cli->desthost
+* then we conclude the server type checks out. This routine
+* is useful to retrieve list of server's of a certain
+* type when all you have is a null session connection and
+* can't remote API calls such as NetWkstaGetInfo or
+* NetServerGetInfo.
+*
+* Dependencies: none
+*
+* Parameters:
+* cli - pointer to cli_state structure
+* workgroup - pointer to string containing domain
+* stype - server type
+*
+* Returns:
+* True - success
+* False - failure
+*
+************************************************************************/
+BOOL cli_ns_check_server_type(struct cli_state *cli, char *workgroup, uint32 stype)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rdrcnt,rprcnt;
+ char *p;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetServerEnum2_REQ) /* req string */
+ +sizeof(RAP_SERVER_INFO_L0) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE /* buffer size */
+ +DWORDSIZE /* server type */
+ +RAP_MACHNAME_LEN]; /* workgroup */
+ BOOL found_server = False;
+ int res = -1;
+
+ /* send a SMBtrans command with api NetServerEnum */
+ p = make_header(param, RAP_NetServerEnum2,
+ RAP_NetServerEnum2_REQ, RAP_SERVER_INFO_L0);
+ PUTWORD(p, 0); /* info level 0 */
+ PUTWORD(p, CLI_BUFFER_SIZE);
+ PUTDWORD(p, stype);
+ PUTSTRING(p, workgroup, RAP_MACHNAME_LEN);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 8, /* params, length, max */
+ NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */
+ &rparam, &rprcnt, /* return params, return size */
+ &rdata, &rdrcnt /* return data, return size */
+ )) {
+
+ res = GETRES(rparam);
+ cli->rap_error = res;
+
+ if (res == 0 || res == ERRmoredata) {
+ int i, converter, count;
+
+ p = rparam + WORDSIZE;
+ GETWORD(p, converter);
+ GETWORD(p, count);
+
+ p = rdata;
+ for (i = 0;i < count;i++, p += 16) {
+ char ret_server[RAP_MACHNAME_LEN];
+
+ GETSTRINGF(p, ret_server, RAP_MACHNAME_LEN);
+ if (strequal(ret_server, cli->desthost)) {
+ found_server = True;
+ break;
+ }
+ }
+ }
+ else {
+ DEBUG(4,("cli_ns_check_server_type: machine %s failed the NetServerEnum call. "
+ "Error was : %s.\n", cli->desthost, cli_errstr(cli) ));
+ }
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return found_server;
+ }
+
+
+/****************************************************************************
+ perform a NetWkstaUserLogoff
+****************************************************************************/
+BOOL cli_NetWkstaUserLogoff(struct cli_state *cli,char *user, char *workstation)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetWkstaUserLogoff_REQ) /* req string */
+ +sizeof(RAP_USER_LOGOFF_INFO_L1) /* return string */
+ +RAP_USERNAME_LEN+1 /* user name+pad */
+ +RAP_MACHNAME_LEN /* wksta name */
+ +WORDSIZE /* buffer size */
+ +WORDSIZE]; /* buffer size? */
+ fstring upperbuf;
+
+ memset(param, 0, sizeof(param));
+
+ /* send a SMBtrans command with api NetWkstaUserLogoff */
+ p = make_header(param, RAP_WWkstaUserLogoff,
+ RAP_NetWkstaUserLogoff_REQ, RAP_USER_LOGOFF_INFO_L1);
+ PUTDWORD(p, 0); /* Null pointer */
+ PUTDWORD(p, 0); /* Null pointer */
+ fstrcpy(upperbuf, user);
+ strupper_m(upperbuf);
+ PUTSTRINGF(p, upperbuf, RAP_USERNAME_LEN);
+ p++; /* strange format, but ok */
+ fstrcpy(upperbuf, workstation);
+ strupper_m(upperbuf);
+ PUTSTRINGF(p, upperbuf, RAP_MACHNAME_LEN);
+ PUTWORD(p, CLI_BUFFER_SIZE);
+ PUTWORD(p, CLI_BUFFER_SIZE);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),1024, /* param, length, max */
+ NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */
+ &rparam, &rprcnt, /* return params, return size */
+ &rdata, &rdrcnt /* return data, return size */
+ )) {
+ cli->rap_error = GETRES(rparam);
+
+ if (cli->rap_error != 0) {
+ DEBUG(4,("NetwkstaUserLogoff gave error %d\n", cli->rap_error));
+ }
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+ return (cli->rap_error == 0);
+}
+
+int cli_NetPrintQEnum(struct cli_state *cli,
+ void (*qfn)(const char*,uint16,uint16,uint16,const char*,const char*,const char*,const char*,const char*,uint16,uint16),
+ void (*jfn)(uint16,const char*,const char*,const char*,const char*,uint16,uint16,const char*,uint,uint,const char*))
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetPrintQEnum_REQ) /* req string */
+ +sizeof(RAP_PRINTQ_INFO_L2) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE /* buffer size */
+ +sizeof(RAP_SMB_PRINT_JOB_L1)]; /* more ret data */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+
+ memset(param, '\0',sizeof(param));
+ p = make_header(param, RAP_WPrintQEnum,
+ RAP_NetPrintQEnum_REQ, RAP_PRINTQ_INFO_L2);
+ PUTWORD(p,2); /* Info level 2 */
+ PUTWORD(p,0xFFE0); /* Return buffer size */
+ PUTSTRING(p, RAP_SMB_PRINT_JOB_L1, 0);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),1024,
+ NULL, 0, CLI_BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ res = GETRES(rparam);
+ cli->rap_error = res;
+ if (res != 0) {
+ DEBUG(1,("NetPrintQEnum gave error %d\n", res));
+ }
+ }
+
+ if (rdata) {
+ if (res == 0 || res == ERRmoredata) {
+ int i, converter, count;
+
+ p = rparam + WORDSIZE;
+ GETWORD(p, converter);
+ GETWORD(p, count);
+
+ p = rdata;
+ for (i=0;i<count;i++) {
+ pstring qname, sep_file, print_proc, dest, parms, comment;
+ uint16 jobcount, priority, start_time, until_time, status;
+
+ GETSTRINGF(p, qname, RAP_SHARENAME_LEN);
+ p++; /* pad */
+ GETWORD(p, priority);
+ GETWORD(p, start_time);
+ GETWORD(p, until_time);
+ GETSTRINGP(p, sep_file, rdata, converter);
+ GETSTRINGP(p, print_proc, rdata, converter);
+ GETSTRINGP(p, dest, rdata, converter);
+ GETSTRINGP(p, parms, rdata, converter);
+ GETSTRINGP(p, parms, comment, converter);
+ GETWORD(p, status);
+ GETWORD(p, jobcount);
+
+ qfn(qname, priority, start_time, until_time, sep_file, print_proc,
+ dest, parms, comment, status, jobcount);
+
+ if (jobcount) {
+ int j;
+ for (j=0;j<jobcount;j++) {
+ uint16 jid, pos, fsstatus;
+ pstring ownername, notifyname, datatype, jparms, jstatus, jcomment;
+ unsigned int submitted, jsize;
+
+ GETWORD(p, jid);
+ GETSTRINGF(p, ownername, RAP_USERNAME_LEN);
+ p++; /* pad byte */
+ GETSTRINGF(p, notifyname, RAP_MACHNAME_LEN);
+ GETSTRINGF(p, datatype, RAP_DATATYPE_LEN);
+ GETSTRINGP(p, jparms, rdata, converter);
+ GETWORD(p, pos);
+ GETWORD(p, fsstatus);
+ GETSTRINGP(p, jstatus, rdata, converter);
+ GETDWORD(p, submitted);
+ GETDWORD(p, jsize);
+ GETSTRINGP(p, jcomment, rdata, converter);
+
+ jfn(jid, ownername, notifyname, datatype, jparms, pos, fsstatus,
+ jstatus, submitted, jsize, jcomment);
+ }
+ }
+ }
+ } else {
+ DEBUG(4,("NetPrintQEnum res=%d\n", res));
+ }
+ } else {
+ DEBUG(4,("NetPrintQEnum no data returned\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+int cli_NetPrintQGetInfo(struct cli_state *cli, const char *printer,
+ void (*qfn)(const char*,uint16,uint16,uint16,const char*,const char*,const char*,const char*,const char*,uint16,uint16),
+ void (*jfn)(uint16,const char*,const char*,const char*,const char*,uint16,uint16,const char*,uint,uint,const char*))
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetPrintQGetInfo_REQ) /* req string */
+ +sizeof(RAP_PRINTQ_INFO_L2) /* return string */
+ +RAP_SHARENAME_LEN /* printer name */
+ +WORDSIZE /* info level */
+ +WORDSIZE /* buffer size */
+ +sizeof(RAP_SMB_PRINT_JOB_L1)]; /* more ret data */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+
+ memset(param, '\0',sizeof(param));
+ p = make_header(param, RAP_WPrintQGetInfo,
+ RAP_NetPrintQGetInfo_REQ, RAP_PRINTQ_INFO_L2);
+ PUTSTRING(p, printer, RAP_SHARENAME_LEN-1);
+ PUTWORD(p, 2); /* Info level 2 */
+ PUTWORD(p,0xFFE0); /* Return buffer size */
+ PUTSTRING(p, RAP_SMB_PRINT_JOB_L1, 0);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),1024,
+ NULL, 0, CLI_BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ res = GETRES(rparam);
+ cli->rap_error = res;
+ if (res != 0) {
+ DEBUG(1,("NetPrintQGetInfo gave error %d\n", res));
+ }
+ }
+
+ if (rdata) {
+ if (res == 0 || res == ERRmoredata) {
+ int rsize, converter;
+ pstring qname, sep_file, print_proc, dest, parms, comment;
+ uint16 jobcount, priority, start_time, until_time, status;
+
+ p = rparam + WORDSIZE;
+ GETWORD(p, converter);
+ GETWORD(p, rsize);
+
+ p = rdata;
+ GETSTRINGF(p, qname, RAP_SHARENAME_LEN);
+ p++; /* pad */
+ GETWORD(p, priority);
+ GETWORD(p, start_time);
+ GETWORD(p, until_time);
+ GETSTRINGP(p, sep_file, rdata, converter);
+ GETSTRINGP(p, print_proc, rdata, converter);
+ GETSTRINGP(p, dest, rdata, converter);
+ GETSTRINGP(p, parms, rdata, converter);
+ GETSTRINGP(p, comment, rdata, converter);
+ GETWORD(p, status);
+ GETWORD(p, jobcount);
+ qfn(qname, priority, start_time, until_time, sep_file, print_proc,
+ dest, parms, comment, status, jobcount);
+ if (jobcount) {
+ int j;
+ for (j=0;(j<jobcount)&&(PTR_DIFF(p,rdata)< rsize);j++) {
+ uint16 jid, pos, fsstatus;
+ pstring ownername, notifyname, datatype, jparms, jstatus, jcomment;
+ unsigned int submitted, jsize;
+
+ GETWORD(p, jid);
+ GETSTRINGF(p, ownername, RAP_USERNAME_LEN);
+ p++; /* pad byte */
+ GETSTRINGF(p, notifyname, RAP_MACHNAME_LEN);
+ GETSTRINGF(p, datatype, RAP_DATATYPE_LEN);
+ GETSTRINGP(p, jparms, rdata, converter);
+ GETWORD(p, pos);
+ GETWORD(p, fsstatus);
+ GETSTRINGP(p, jstatus, rdata, converter);
+ GETDWORD(p, submitted);
+ GETDWORD(p, jsize);
+ GETSTRINGP(p, jcomment, rdata, converter);
+
+ jfn(jid, ownername, notifyname, datatype, jparms, pos, fsstatus,
+ jstatus, submitted, jsize, jcomment);
+ }
+ }
+ } else {
+ DEBUG(4,("NetPrintQGetInfo res=%d\n", res));
+ }
+ } else {
+ DEBUG(4,("NetPrintQGetInfo no data returned\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+call a NetServiceEnum - list running services on a different host
+****************************************************************************/
+int cli_RNetServiceEnum(struct cli_state *cli, void (*fn)(const char *, const char *, void *), void *state)
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetServiceEnum_REQ) /* parm string */
+ +sizeof(RAP_SERVICE_INFO_L2) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+
+ memset(param, '\0', sizeof(param));
+ p = make_header(param, RAP_WServiceEnum,
+ RAP_NetServiceEnum_REQ, RAP_SERVICE_INFO_L2);
+ PUTWORD(p,2); /* Info level 2 */
+ PUTWORD(p,0xFFE0); /* Return buffer size */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),8,
+ NULL, 0, 0xFFE0 /* data area size */,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ res = GETRES(rparam);
+ cli->rap_error = res;
+ if(cli->rap_error == 234)
+ DEBUG(1,("Not all service names were returned (such as those longer than 15 characters)\n"));
+ else if (cli->rap_error != 0) {
+ DEBUG(1,("NetServiceEnum gave error %d\n", cli->rap_error));
+ }
+ }
+
+ if (rdata) {
+ if (res == 0 || res == ERRmoredata) {
+ int i, converter, count;
+
+ p = rparam + WORDSIZE; /* skip result */
+ GETWORD(p, converter);
+ GETWORD(p, count);
+
+ for (i=0,p=rdata;i<count;i++) {
+ pstring comment;
+ char servicename[RAP_SRVCNAME_LEN];
+
+ GETSTRINGF(p, servicename, RAP_SRVCNAME_LEN);
+ p+=8; /* pass status words */
+ GETSTRINGF(p, comment, RAP_SRVCCMNT_LEN);
+
+ fn(servicename, comment, cli); /* BB add status too */
+ }
+ } else {
+ DEBUG(4,("NetServiceEnum res=%d\n", res));
+ }
+ } else {
+ DEBUG(4,("NetServiceEnum no data returned\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+
+/****************************************************************************
+call a NetSessionEnum - list workstations with sessions to an SMB server
+****************************************************************************/
+int cli_NetSessionEnum(struct cli_state *cli, void (*fn)(char *, char *, uint16, uint16, uint16, uint, uint, uint, char *))
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetSessionEnum_REQ) /* parm string */
+ +sizeof(RAP_SESSION_INFO_L2) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+ memset(param, '\0', sizeof(param));
+ p = make_header(param, RAP_WsessionEnum,
+ RAP_NetSessionEnum_REQ, RAP_SESSION_INFO_L2);
+ PUTWORD(p,2); /* Info level 2 */
+ PUTWORD(p,0xFF); /* Return buffer size */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),8,
+ NULL, 0, CLI_BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ res = GETRES(rparam);
+ cli->rap_error = res;
+ if (res != 0) {
+ DEBUG(1,("NetSessionEnum gave error %d\n", res));
+ }
+ }
+
+ if (rdata) {
+ if (res == 0 || res == ERRmoredata) {
+ int i, converter, count;
+
+ p = rparam + WORDSIZE;
+ GETWORD(p, converter);
+ GETWORD(p, count);
+
+ for (i=0,p=rdata;i<count;i++) {
+ pstring wsname, username, clitype_name;
+ uint16 num_conns, num_opens, num_users;
+ unsigned int sess_time, idle_time, user_flags;
+
+ GETSTRINGP(p, wsname, rdata, converter);
+ GETSTRINGP(p, username, rdata, converter);
+ GETWORD(p, num_conns);
+ GETWORD(p, num_opens);
+ GETWORD(p, num_users);
+ GETDWORD(p, sess_time);
+ GETDWORD(p, idle_time);
+ GETDWORD(p, user_flags);
+ GETSTRINGP(p, clitype_name, rdata, converter);
+
+ fn(wsname, username, num_conns, num_opens, num_users, sess_time,
+ idle_time, user_flags, clitype_name);
+ }
+
+ } else {
+ DEBUG(4,("NetSessionEnum res=%d\n", res));
+ }
+ } else {
+ DEBUG(4,("NetSesssionEnum no data returned\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+ Call a NetSessionGetInfo - get information about other session to an SMB server.
+****************************************************************************/
+
+int cli_NetSessionGetInfo(struct cli_state *cli, const char *workstation, void (*fn)(const char *, const char *, uint16, uint16, uint16, uint, uint, uint, const char *))
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetSessionGetInfo_REQ) /* req string */
+ +sizeof(RAP_SESSION_INFO_L2) /* return string */
+ +RAP_MACHNAME_LEN /* wksta name */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+
+ memset(param, '\0', sizeof(param));
+ p = make_header(param, RAP_WsessionGetInfo,
+ RAP_NetSessionGetInfo_REQ, RAP_SESSION_INFO_L2);
+ PUTSTRING(p, workstation, RAP_MACHNAME_LEN-1);
+ PUTWORD(p,2); /* Info level 2 */
+ PUTWORD(p,0xFF); /* Return buffer size */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),PTR_DIFF(p,param),
+ NULL, 0, CLI_BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ cli->rap_error = SVAL(rparam,0);
+ if (cli->rap_error != 0) {
+ DEBUG(1,("NetSessionGetInfo gave error %d\n", cli->rap_error));
+ }
+ }
+
+ if (rdata) {
+ res = GETRES(rparam);
+
+ if (res == 0 || res == ERRmoredata) {
+ int rsize, converter;
+ pstring wsname, username, clitype_name;
+ uint16 num_conns, num_opens, num_users;
+ unsigned int sess_time, idle_time, user_flags;
+
+ p = rparam + WORDSIZE;
+ GETWORD(p, converter);
+ GETWORD(p, rsize);
+
+ p = rdata;
+ GETSTRINGP(p, wsname, rdata, converter);
+ GETSTRINGP(p, username, rdata, converter);
+ GETWORD(p, num_conns);
+ GETWORD(p, num_opens);
+ GETWORD(p, num_users);
+ GETDWORD(p, sess_time);
+ GETDWORD(p, idle_time);
+ GETDWORD(p, user_flags);
+ GETSTRINGP(p, clitype_name, rdata, converter);
+
+ fn(wsname, username, num_conns, num_opens, num_users, sess_time,
+ idle_time, user_flags, clitype_name);
+ } else {
+ DEBUG(4,("NetSessionGetInfo res=%d\n", res));
+ }
+ } else {
+ DEBUG(4,("NetSessionGetInfo no data returned\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+call a NetSessionDel - close a session to an SMB server
+****************************************************************************/
+int cli_NetSessionDel(struct cli_state *cli, const char *workstation)
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetSessionDel_REQ) /* req string */
+ +1 /* no return string */
+ +RAP_MACHNAME_LEN /* workstation name */
+ +WORDSIZE]; /* reserved (0) */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res;
+
+ memset(param, '\0', sizeof(param));
+ p = make_header(param, RAP_WsessionDel, RAP_NetSessionDel_REQ, NULL);
+ PUTSTRING(p, workstation, RAP_MACHNAME_LEN-1);
+ PUTWORD(p,0); /* reserved word of 0 */
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 200, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ res = GETRES(rparam);
+ cli->rap_error = res;
+
+ if (res == 0) {
+ /* nothing to do */
+ }
+ else {
+ DEBUG(4,("NetFileClose2 res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetFileClose2 failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+
+int cli_NetConnectionEnum(struct cli_state *cli, const char *qualifier, void (*fn)(uint16 conid, uint16 contype, uint16 numopens, uint16 numusers, uint32 contime, const char *username, const char *netname))
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetConnectionEnum_REQ) /* req string */
+ +sizeof(RAP_CONNECTION_INFO_L1) /* return string */
+ +RAP_MACHNAME_LEN /* wksta name */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+ memset(param, '\0', sizeof(param));
+ p = make_header(param, RAP_WconnectionEnum,
+ RAP_NetConnectionEnum_REQ, RAP_CONNECTION_INFO_L1);
+ PUTSTRING(p, qualifier, RAP_MACHNAME_LEN-1);/* Workstation name */
+ PUTWORD(p,1); /* Info level 1 */
+ PUTWORD(p,0xFFE0); /* Return buffer size */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),PTR_DIFF(p,param),
+ NULL, 0, CLI_BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ res = GETRES(rparam);
+ cli->rap_error = res;
+ if (res != 0) {
+ DEBUG(1,("NetConnectionEnum gave error %d\n", res));
+ }
+ }
+ if (rdata) {
+ if (res == 0 || res == ERRmoredata) {
+ int i, converter, count;
+
+ p = rparam + WORDSIZE;
+ GETWORD(p, converter);
+ GETWORD(p, count);
+
+ for (i=0,p=rdata;i<count;i++) {
+ pstring netname, username;
+ uint16 conn_id, conn_type, num_opens, num_users;
+ unsigned int conn_time;
+
+ GETWORD(p,conn_id);
+ GETWORD(p,conn_type);
+ GETWORD(p,num_opens);
+ GETWORD(p,num_users);
+ GETDWORD(p,conn_time);
+ GETSTRINGP(p, username, rdata, converter);
+ GETSTRINGP(p, netname, rdata, converter);
+
+ fn(conn_id, conn_type, num_opens, num_users, conn_time,
+ username, netname);
+ }
+
+ } else {
+ DEBUG(4,("NetConnectionEnum res=%d\n", res));
+ }
+ } else {
+ DEBUG(4,("NetConnectionEnum no data returned\n"));
+ }
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return res;
+}
diff --git a/source/libsmb/clireadwrite.c b/source/libsmb/clireadwrite.c
new file mode 100644
index 00000000000..8eac7d07d8b
--- /dev/null
+++ b/source/libsmb/clireadwrite.c
@@ -0,0 +1,418 @@
+/*
+ Unix SMB/CIFS implementation.
+ client file read/write routines
+ Copyright (C) Andrew Tridgell 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+/****************************************************************************
+Issue a single SMBread and don't wait for a reply.
+****************************************************************************/
+
+static BOOL cli_issue_read(struct cli_state *cli, int fnum, off_t offset,
+ size_t size, int i)
+{
+ BOOL bigoffset = False;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ if ((SMB_BIG_UINT)offset >> 32)
+ bigoffset = True;
+
+ set_message(cli->outbuf,bigoffset ? 12 : 10,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBreadX);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SCVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,fnum);
+ SIVAL(cli->outbuf,smb_vwv3,offset);
+ SSVAL(cli->outbuf,smb_vwv5,size);
+ SSVAL(cli->outbuf,smb_vwv6,size);
+ SSVAL(cli->outbuf,smb_mid,cli->mid + i);
+
+ if (bigoffset)
+ SIVAL(cli->outbuf,smb_vwv10,(offset>>32) & 0xffffffff);
+
+ return cli_send_smb(cli);
+}
+
+/****************************************************************************
+ Read size bytes at offset offset using SMBreadX.
+****************************************************************************/
+
+ssize_t cli_read(struct cli_state *cli, int fnum, char *buf, off_t offset, size_t size)
+{
+ char *p;
+ int size2;
+ int readsize;
+ ssize_t total = 0;
+
+ if (size == 0)
+ return 0;
+
+ /*
+ * Set readsize to the maximum size we can handle in one readX,
+ * rounded down to a multiple of 1024.
+ */
+
+ readsize = (cli->max_xmit - (smb_size+32)) & ~1023;
+
+ while (total < size) {
+ readsize = MIN(readsize, size-total);
+
+ /* Issue a read and receive a reply */
+
+ if (!cli_issue_read(cli, fnum, offset, readsize, 0))
+ return -1;
+
+ if (!cli_receive_smb(cli))
+ return -1;
+
+ /* Check for error. Make sure to check for DOS and NT
+ errors. */
+
+ if (cli_is_error(cli)) {
+ BOOL recoverable_error = False;
+ NTSTATUS status = NT_STATUS_OK;
+ uint8 eclass = 0;
+ uint32 ecode = 0;
+
+ if (cli_is_nt_error(cli))
+ status = cli_nt_error(cli);
+ else
+ cli_dos_error(cli, &eclass, &ecode);
+
+ /*
+ * ERRDOS ERRmoredata or STATUS_MORE_ENRTIES is a
+ * recoverable error, plus we have valid data in the
+ * packet so don't error out here.
+ */
+
+ if ((eclass == ERRDOS && ecode == ERRmoredata) ||
+ NT_STATUS_V(status) == NT_STATUS_V(STATUS_MORE_ENTRIES))
+ recoverable_error = True;
+
+ if (!recoverable_error)
+ return -1;
+ }
+
+ size2 = SVAL(cli->inbuf, smb_vwv5);
+
+ if (size2 > readsize) {
+ DEBUG(5,("server returned more than we wanted!\n"));
+ return -1;
+ } else if (size2 < 0) {
+ DEBUG(5,("read return < 0!\n"));
+ return -1;
+ }
+
+ /* Copy data into buffer */
+
+ p = smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_vwv6);
+ memcpy(buf + total, p, size2);
+
+ total += size2;
+ offset += size2;
+
+ /*
+ * If the server returned less than we asked for we're at EOF.
+ */
+
+ if (size2 < readsize)
+ break;
+ }
+
+ return total;
+}
+
+#if 0 /* relies on client_receive_smb(), now a static in libsmb/clientgen.c */
+
+/* This call is INCOMPATIBLE with SMB signing. If you remove the #if 0
+ you must fix ensure you don't attempt to sign the packets - data
+ *will* be currupted */
+
+/****************************************************************************
+Issue a single SMBreadraw and don't wait for a reply.
+****************************************************************************/
+
+static BOOL cli_issue_readraw(struct cli_state *cli, int fnum, off_t offset,
+ size_t size, int i)
+{
+
+ if (!cli->sign_info.use_smb_signing) {
+ DEBUG(0, ("Cannot use readraw and SMB Signing\n"));
+ return False;
+ }
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,10,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBreadbraw);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,fnum);
+ SIVAL(cli->outbuf,smb_vwv1,offset);
+ SSVAL(cli->outbuf,smb_vwv2,size);
+ SSVAL(cli->outbuf,smb_vwv3,size);
+ SSVAL(cli->outbuf,smb_mid,cli->mid + i);
+
+ return cli_send_smb(cli);
+}
+
+/****************************************************************************
+ Tester for the readraw call.
+****************************************************************************/
+
+ssize_t cli_readraw(struct cli_state *cli, int fnum, char *buf, off_t offset, size_t size)
+{
+ char *p;
+ int size2;
+ size_t readsize;
+ ssize_t total = 0;
+
+ if (size == 0)
+ return 0;
+
+ /*
+ * Set readsize to the maximum size we can handle in one readraw.
+ */
+
+ readsize = 0xFFFF;
+
+ while (total < size) {
+ readsize = MIN(readsize, size-total);
+
+ /* Issue a read and receive a reply */
+
+ if (!cli_issue_readraw(cli, fnum, offset, readsize, 0))
+ return -1;
+
+ if (!client_receive_smb(cli->fd, cli->inbuf, cli->timeout))
+ return -1;
+
+ size2 = smb_len(cli->inbuf);
+
+ if (size2 > readsize) {
+ DEBUG(5,("server returned more than we wanted!\n"));
+ return -1;
+ } else if (size2 < 0) {
+ DEBUG(5,("read return < 0!\n"));
+ return -1;
+ }
+
+ /* Copy data into buffer */
+
+ if (size2) {
+ p = cli->inbuf + 4;
+ memcpy(buf + total, p, size2);
+ }
+
+ total += size2;
+ offset += size2;
+
+ /*
+ * If the server returned less than we asked for we're at EOF.
+ */
+
+ if (size2 < readsize)
+ break;
+ }
+
+ return total;
+}
+#endif
+/****************************************************************************
+issue a single SMBwrite and don't wait for a reply
+****************************************************************************/
+
+static BOOL cli_issue_write(struct cli_state *cli, int fnum, off_t offset,
+ uint16 mode, const char *buf,
+ size_t size, int i)
+{
+ char *p;
+ BOOL bigoffset = False;
+
+ if (size > cli->bufsize) {
+ cli->outbuf = realloc(cli->outbuf, size + 1024);
+ cli->inbuf = realloc(cli->inbuf, size + 1024);
+ if (cli->outbuf == NULL || cli->inbuf == NULL)
+ return False;
+ cli->bufsize = size + 1024;
+ }
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ if ((SMB_BIG_UINT)offset >> 32)
+ bigoffset = True;
+
+ if (bigoffset)
+ set_message(cli->outbuf,14,0,True);
+ else
+ set_message(cli->outbuf,12,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBwriteX);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SCVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,fnum);
+
+ SIVAL(cli->outbuf,smb_vwv3,offset);
+ SIVAL(cli->outbuf,smb_vwv5,0);
+ SSVAL(cli->outbuf,smb_vwv7,mode);
+
+ SSVAL(cli->outbuf,smb_vwv8,(mode & 0x0008) ? size : 0);
+ /*
+ * According to CIFS-TR-1p00, this following field should only
+ * be set if CAP_LARGE_WRITEX is set. We should check this
+ * locally. However, this check might already have been
+ * done by our callers.
+ */
+ SSVAL(cli->outbuf,smb_vwv9,((size>>16)&1));
+ SSVAL(cli->outbuf,smb_vwv10,size);
+ SSVAL(cli->outbuf,smb_vwv11,
+ smb_buf(cli->outbuf) - smb_base(cli->outbuf));
+
+ if (bigoffset)
+ SIVAL(cli->outbuf,smb_vwv12,(offset>>32) & 0xffffffff);
+
+ p = smb_base(cli->outbuf) + SVAL(cli->outbuf,smb_vwv11);
+ memcpy(p, buf, size);
+ cli_setup_bcc(cli, p+size);
+
+ SSVAL(cli->outbuf,smb_mid,cli->mid + i);
+
+ show_msg(cli->outbuf);
+ return cli_send_smb(cli);
+}
+
+/****************************************************************************
+ write to a file
+ write_mode: 0x0001 disallow write cacheing
+ 0x0002 return bytes remaining
+ 0x0004 use raw named pipe protocol
+ 0x0008 start of message mode named pipe protocol
+****************************************************************************/
+
+ssize_t cli_write(struct cli_state *cli,
+ int fnum, uint16 write_mode,
+ const char *buf, off_t offset, size_t size)
+{
+ int bwritten = 0;
+ int issued = 0;
+ int received = 0;
+ int mpx = MAX(cli->max_mux-1, 1);
+ int block = cli->max_xmit - (smb_size+32);
+ int blocks = (size + (block-1)) / block;
+
+ while (received < blocks) {
+
+ while ((issued - received < mpx) && (issued < blocks)) {
+ int bsent = issued * block;
+ int size1 = MIN(block, size - bsent);
+
+ if (!cli_issue_write(cli, fnum, offset + bsent,
+ write_mode,
+ buf + bsent,
+ size1, issued))
+ return -1;
+ issued++;
+ }
+
+ if (!cli_receive_smb(cli))
+ return bwritten;
+
+ received++;
+
+ if (cli_is_error(cli))
+ break;
+
+ bwritten += SVAL(cli->inbuf, smb_vwv2);
+ bwritten += (((int)(SVAL(cli->inbuf, smb_vwv4)))>>16);
+ }
+
+ while (received < issued && cli_receive_smb(cli))
+ received++;
+
+ return bwritten;
+}
+
+/****************************************************************************
+ write to a file using a SMBwrite and not bypassing 0 byte writes
+****************************************************************************/
+
+ssize_t cli_smbwrite(struct cli_state *cli,
+ int fnum, char *buf, off_t offset, size_t size1)
+{
+ char *p;
+ ssize_t total = 0;
+
+ do {
+ size_t size = MIN(size1, cli->max_xmit - 48);
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,5, 0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBwrite);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,fnum);
+ SSVAL(cli->outbuf,smb_vwv1,size);
+ SIVAL(cli->outbuf,smb_vwv2,offset);
+ SSVAL(cli->outbuf,smb_vwv4,0);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 1;
+ SSVAL(p, 0, size); p += 2;
+ memcpy(p, buf, size); p += size;
+
+ cli_setup_bcc(cli, p);
+
+ if (!cli_send_smb(cli))
+ return -1;
+
+ if (!cli_receive_smb(cli))
+ return -1;
+
+ if (cli_is_error(cli))
+ return -1;
+
+ size = SVAL(cli->inbuf,smb_vwv0);
+ if (size == 0)
+ break;
+
+ size1 -= size;
+ total += size;
+ offset += size;
+
+ } while (size1);
+
+ return total;
+}
diff --git a/source/libsmb/clisecdesc.c b/source/libsmb/clisecdesc.c
new file mode 100644
index 00000000000..2989966f4de
--- /dev/null
+++ b/source/libsmb/clisecdesc.c
@@ -0,0 +1,139 @@
+/*
+ Unix SMB/CIFS implementation.
+ client security descriptor functions
+ Copyright (C) Andrew Tridgell 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+ query the security descriptor for a open file
+ ****************************************************************************/
+SEC_DESC *cli_query_secdesc(struct cli_state *cli, int fnum,
+ TALLOC_CTX *mem_ctx)
+{
+ char param[8];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+ prs_struct pd;
+ SEC_DESC *psd = NULL;
+
+ SIVAL(param, 0, fnum);
+ SIVAL(param, 4, 0x7);
+
+ if (!cli_send_nt_trans(cli,
+ NT_TRANSACT_QUERY_SECURITY_DESC,
+ 0,
+ NULL, 0, 0,
+ param, 8, 4,
+ NULL, 0, 0x10000)) {
+ DEBUG(1,("Failed to send NT_TRANSACT_QUERY_SECURITY_DESC\n"));
+ goto cleanup;
+ }
+
+
+ if (!cli_receive_nt_trans(cli,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ DEBUG(1,("Failed to recv NT_TRANSACT_QUERY_SECURITY_DESC\n"));
+ goto cleanup;
+ }
+
+ prs_init(&pd, rdata_count, mem_ctx, UNMARSHALL);
+ prs_copy_data_in(&pd, rdata, rdata_count);
+ prs_set_offset(&pd,0);
+
+ if (!sec_io_desc("sd data", &psd, &pd, 1)) {
+ DEBUG(1,("Failed to parse secdesc\n"));
+ goto cleanup;
+ }
+
+ cleanup:
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ prs_mem_free(&pd);
+ return psd;
+}
+
+/****************************************************************************
+ set the security descriptor for a open file
+ ****************************************************************************/
+BOOL cli_set_secdesc(struct cli_state *cli, int fnum, SEC_DESC *sd)
+{
+ char param[8];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+ uint32 sec_info = 0;
+ TALLOC_CTX *mem_ctx;
+ prs_struct pd;
+ BOOL ret = False;
+
+ if ((mem_ctx = talloc_init("cli_set_secdesc")) == NULL) {
+ DEBUG(0,("talloc_init failed.\n"));
+ goto cleanup;
+ }
+
+ prs_init(&pd, 0, mem_ctx, MARSHALL);
+ prs_give_memory(&pd, NULL, 0, True);
+
+ if (!sec_io_desc("sd data", &sd, &pd, 1)) {
+ DEBUG(1,("Failed to marshall secdesc\n"));
+ goto cleanup;
+ }
+
+ SIVAL(param, 0, fnum);
+
+ if (sd->off_dacl)
+ sec_info |= DACL_SECURITY_INFORMATION;
+ if (sd->off_owner_sid)
+ sec_info |= OWNER_SECURITY_INFORMATION;
+ if (sd->off_grp_sid)
+ sec_info |= GROUP_SECURITY_INFORMATION;
+ SSVAL(param, 4, sec_info);
+
+ if (!cli_send_nt_trans(cli,
+ NT_TRANSACT_SET_SECURITY_DESC,
+ 0,
+ NULL, 0, 0,
+ param, 8, 0,
+ prs_data_p(&pd), prs_offset(&pd), 0)) {
+ DEBUG(1,("Failed to send NT_TRANSACT_SET_SECURITY_DESC\n"));
+ goto cleanup;
+ }
+
+
+ if (!cli_receive_nt_trans(cli,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ DEBUG(1,("NT_TRANSACT_SET_SECURITY_DESC failed\n"));
+ goto cleanup;
+ }
+
+ ret = True;
+
+ cleanup:
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ talloc_destroy(mem_ctx);
+
+ prs_mem_free(&pd);
+ return ret;
+}
diff --git a/source/libsmb/clispnego.c b/source/libsmb/clispnego.c
new file mode 100644
index 00000000000..e6cadc466c1
--- /dev/null
+++ b/source/libsmb/clispnego.c
@@ -0,0 +1,539 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple kerberos5/SPNEGO routines
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
+ Copyright (C) Luke Howard 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*
+ generate a negTokenInit packet given a GUID, a list of supported
+ OIDs (the mechanisms) and a principal name string
+*/
+DATA_BLOB spnego_gen_negTokenInit(uint8 guid[16],
+ const char *OIDs[],
+ const char *principal)
+{
+ int i;
+ ASN1_DATA data;
+ DATA_BLOB ret;
+
+ memset(&data, 0, sizeof(data));
+
+ asn1_write(&data, guid, 16);
+ asn1_push_tag(&data,ASN1_APPLICATION(0));
+ asn1_write_OID(&data,OID_SPNEGO);
+ asn1_push_tag(&data,ASN1_CONTEXT(0));
+ asn1_push_tag(&data,ASN1_SEQUENCE(0));
+
+ asn1_push_tag(&data,ASN1_CONTEXT(0));
+ asn1_push_tag(&data,ASN1_SEQUENCE(0));
+ for (i=0; OIDs[i]; i++) {
+ asn1_write_OID(&data,OIDs[i]);
+ }
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ asn1_push_tag(&data, ASN1_CONTEXT(3));
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+ asn1_push_tag(&data, ASN1_CONTEXT(0));
+ asn1_write_GeneralString(&data,principal);
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ asn1_pop_tag(&data);
+
+ if (data.has_error) {
+ DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data.ofs));
+ asn1_free(&data);
+ }
+
+ ret = data_blob(data.data, data.length);
+ asn1_free(&data);
+
+ return ret;
+}
+
+/*
+ Generate a negTokenInit as used by the client side ... It has a mechType
+ (OID), and a mechToken (a security blob) ...
+
+ Really, we need to break out the NTLMSSP stuff as well, because it could be
+ raw in the packets!
+*/
+DATA_BLOB gen_negTokenInit(const char *OID, DATA_BLOB blob)
+{
+ ASN1_DATA data;
+ DATA_BLOB ret;
+
+ memset(&data, 0, sizeof(data));
+
+ asn1_push_tag(&data, ASN1_APPLICATION(0));
+ asn1_write_OID(&data,OID_SPNEGO);
+ asn1_push_tag(&data, ASN1_CONTEXT(0));
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+
+ asn1_push_tag(&data, ASN1_CONTEXT(0));
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+ asn1_write_OID(&data, OID);
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ asn1_push_tag(&data, ASN1_CONTEXT(2));
+ asn1_write_OctetString(&data,blob.data,blob.length);
+ asn1_pop_tag(&data);
+
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ asn1_pop_tag(&data);
+
+ if (data.has_error) {
+ DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data.ofs));
+ asn1_free(&data);
+ }
+
+ ret = data_blob(data.data, data.length);
+ asn1_free(&data);
+
+ return ret;
+}
+
+/*
+ parse a negTokenInit packet giving a GUID, a list of supported
+ OIDs (the mechanisms) and a principal name string
+*/
+BOOL spnego_parse_negTokenInit(DATA_BLOB blob,
+ char *OIDs[ASN1_MAX_OIDS],
+ char **principal)
+{
+ int i;
+ BOOL ret;
+ ASN1_DATA data;
+
+ asn1_load(&data, blob);
+
+ asn1_start_tag(&data,ASN1_APPLICATION(0));
+ asn1_check_OID(&data,OID_SPNEGO);
+ asn1_start_tag(&data,ASN1_CONTEXT(0));
+ asn1_start_tag(&data,ASN1_SEQUENCE(0));
+
+ asn1_start_tag(&data,ASN1_CONTEXT(0));
+ asn1_start_tag(&data,ASN1_SEQUENCE(0));
+ for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS; i++) {
+ char *oid = NULL;
+ asn1_read_OID(&data,&oid);
+ OIDs[i] = oid;
+ }
+ OIDs[i] = NULL;
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ asn1_start_tag(&data, ASN1_CONTEXT(3));
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+ asn1_start_tag(&data, ASN1_CONTEXT(0));
+ asn1_read_GeneralString(&data,principal);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ asn1_end_tag(&data);
+
+ ret = !data.has_error;
+ asn1_free(&data);
+ return ret;
+}
+
+
+/*
+ generate a negTokenTarg packet given a list of OIDs and a security blob
+*/
+DATA_BLOB gen_negTokenTarg(const char *OIDs[], DATA_BLOB blob)
+{
+ int i;
+ ASN1_DATA data;
+ DATA_BLOB ret;
+
+ memset(&data, 0, sizeof(data));
+
+ asn1_push_tag(&data, ASN1_APPLICATION(0));
+ asn1_write_OID(&data,OID_SPNEGO);
+ asn1_push_tag(&data, ASN1_CONTEXT(0));
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+
+ asn1_push_tag(&data, ASN1_CONTEXT(0));
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+ for (i=0; OIDs[i]; i++) {
+ asn1_write_OID(&data,OIDs[i]);
+ }
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ asn1_push_tag(&data, ASN1_CONTEXT(2));
+ asn1_write_OctetString(&data,blob.data,blob.length);
+ asn1_pop_tag(&data);
+
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ asn1_pop_tag(&data);
+
+ if (data.has_error) {
+ DEBUG(1,("Failed to build negTokenTarg at offset %d\n", (int)data.ofs));
+ asn1_free(&data);
+ }
+
+ ret = data_blob(data.data, data.length);
+ asn1_free(&data);
+
+ return ret;
+}
+
+
+/*
+ parse a negTokenTarg packet giving a list of OIDs and a security blob
+*/
+BOOL parse_negTokenTarg(DATA_BLOB blob, char *OIDs[ASN1_MAX_OIDS], DATA_BLOB *secblob)
+{
+ int i;
+ ASN1_DATA data;
+
+ asn1_load(&data, blob);
+ asn1_start_tag(&data, ASN1_APPLICATION(0));
+ asn1_check_OID(&data,OID_SPNEGO);
+ asn1_start_tag(&data, ASN1_CONTEXT(0));
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+
+ asn1_start_tag(&data, ASN1_CONTEXT(0));
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+ for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS; i++) {
+ char *oid = NULL;
+ asn1_read_OID(&data,&oid);
+ OIDs[i] = oid;
+ }
+ OIDs[i] = NULL;
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ asn1_start_tag(&data, ASN1_CONTEXT(2));
+ asn1_read_OctetString(&data,secblob);
+ asn1_end_tag(&data);
+
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ asn1_end_tag(&data);
+
+ if (data.has_error) {
+ DEBUG(1,("Failed to parse negTokenTarg at offset %d\n", (int)data.ofs));
+ asn1_free(&data);
+ return False;
+ }
+
+ asn1_free(&data);
+ return True;
+}
+
+/*
+ generate a krb5 GSS-API wrapper packet given a ticket
+*/
+DATA_BLOB spnego_gen_krb5_wrap(DATA_BLOB ticket, const uint8 tok_id[2])
+{
+ ASN1_DATA data;
+ DATA_BLOB ret;
+
+ memset(&data, 0, sizeof(data));
+
+ asn1_push_tag(&data, ASN1_APPLICATION(0));
+ asn1_write_OID(&data, OID_KERBEROS5);
+
+ asn1_write(&data, tok_id, 2);
+ asn1_write(&data, ticket.data, ticket.length);
+ asn1_pop_tag(&data);
+
+ if (data.has_error) {
+ DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data.ofs));
+ asn1_free(&data);
+ }
+
+ ret = data_blob(data.data, data.length);
+ asn1_free(&data);
+
+ return ret;
+}
+
+/*
+ parse a krb5 GSS-API wrapper packet giving a ticket
+*/
+BOOL spnego_parse_krb5_wrap(DATA_BLOB blob, DATA_BLOB *ticket, uint8 tok_id[2])
+{
+ BOOL ret;
+ ASN1_DATA data;
+ int data_remaining;
+
+ asn1_load(&data, blob);
+ asn1_start_tag(&data, ASN1_APPLICATION(0));
+ asn1_check_OID(&data, OID_KERBEROS5);
+
+ data_remaining = asn1_tag_remaining(&data);
+
+ if (data_remaining < 3) {
+ data.has_error = True;
+ } else {
+ asn1_read(&data, tok_id, 2);
+ data_remaining -= 2;
+ *ticket = data_blob(NULL, data_remaining);
+ asn1_read(&data, ticket->data, ticket->length);
+ }
+
+ asn1_end_tag(&data);
+
+ ret = !data.has_error;
+
+ asn1_free(&data);
+
+ return ret;
+}
+
+
+/*
+ generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY
+ kerberos session setup
+*/
+int spnego_gen_negTokenTarg(const char *principal, int time_offset,
+ DATA_BLOB *targ,
+ DATA_BLOB *session_key_krb5)
+{
+ int retval;
+ DATA_BLOB tkt, tkt_wrapped;
+ const char *krb_mechs[] = {OID_KERBEROS5_OLD, OID_NTLMSSP, NULL};
+
+ /* get a kerberos ticket for the service and extract the session key */
+ retval = cli_krb5_get_ticket(principal, time_offset, &tkt, session_key_krb5);
+
+ if (retval)
+ return retval;
+
+ /* wrap that up in a nice GSS-API wrapping */
+ tkt_wrapped = spnego_gen_krb5_wrap(tkt, TOK_ID_KRB_AP_REQ);
+
+ /* and wrap that in a shiny SPNEGO wrapper */
+ *targ = gen_negTokenTarg(krb_mechs, tkt_wrapped);
+
+ data_blob_free(&tkt_wrapped);
+ data_blob_free(&tkt);
+
+ return retval;
+}
+
+
+/*
+ parse a spnego NTLMSSP challenge packet giving two security blobs
+*/
+BOOL spnego_parse_challenge(const DATA_BLOB blob,
+ DATA_BLOB *chal1, DATA_BLOB *chal2)
+{
+ BOOL ret;
+ ASN1_DATA data;
+
+ ZERO_STRUCTP(chal1);
+ ZERO_STRUCTP(chal2);
+
+ asn1_load(&data, blob);
+ asn1_start_tag(&data,ASN1_CONTEXT(1));
+ asn1_start_tag(&data,ASN1_SEQUENCE(0));
+
+ asn1_start_tag(&data,ASN1_CONTEXT(0));
+ asn1_check_enumerated(&data,1);
+ asn1_end_tag(&data);
+
+ asn1_start_tag(&data,ASN1_CONTEXT(1));
+ asn1_check_OID(&data, OID_NTLMSSP);
+ asn1_end_tag(&data);
+
+ asn1_start_tag(&data,ASN1_CONTEXT(2));
+ asn1_read_OctetString(&data, chal1);
+ asn1_end_tag(&data);
+
+ /* the second challenge is optional (XP doesn't send it) */
+ if (asn1_tag_remaining(&data)) {
+ asn1_start_tag(&data,ASN1_CONTEXT(3));
+ asn1_read_OctetString(&data, chal2);
+ asn1_end_tag(&data);
+ }
+
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ ret = !data.has_error;
+ asn1_free(&data);
+ return ret;
+}
+
+
+/*
+ generate a SPNEGO auth packet. This will contain the encrypted passwords
+*/
+DATA_BLOB spnego_gen_auth(DATA_BLOB blob)
+{
+ ASN1_DATA data;
+ DATA_BLOB ret;
+
+ memset(&data, 0, sizeof(data));
+
+ asn1_push_tag(&data, ASN1_CONTEXT(1));
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+ asn1_push_tag(&data, ASN1_CONTEXT(2));
+ asn1_write_OctetString(&data,blob.data,blob.length);
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ ret = data_blob(data.data, data.length);
+
+ asn1_free(&data);
+
+ return ret;
+}
+
+/*
+ parse a SPNEGO auth packet. This contains the encrypted passwords
+*/
+BOOL spnego_parse_auth(DATA_BLOB blob, DATA_BLOB *auth)
+{
+ ASN1_DATA data;
+
+ asn1_load(&data, blob);
+ asn1_start_tag(&data, ASN1_CONTEXT(1));
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+ asn1_start_tag(&data, ASN1_CONTEXT(2));
+ asn1_read_OctetString(&data,auth);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ if (data.has_error) {
+ DEBUG(3,("spnego_parse_auth failed at %d\n", (int)data.ofs));
+ asn1_free(&data);
+ return False;
+ }
+
+ asn1_free(&data);
+ return True;
+}
+
+/*
+ generate a minimal SPNEGO response packet. Doesn't contain much.
+*/
+DATA_BLOB spnego_gen_auth_response(DATA_BLOB *reply, NTSTATUS nt_status,
+ const char *mechOID)
+{
+ ASN1_DATA data;
+ DATA_BLOB ret;
+ uint8 negResult;
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ negResult = SPNEGO_NEG_RESULT_ACCEPT;
+ } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ negResult = SPNEGO_NEG_RESULT_INCOMPLETE;
+ } else {
+ negResult = SPNEGO_NEG_RESULT_REJECT;
+ }
+
+ ZERO_STRUCT(data);
+
+ asn1_push_tag(&data, ASN1_CONTEXT(1));
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+ asn1_push_tag(&data, ASN1_CONTEXT(0));
+ asn1_write_enumerated(&data, negResult);
+ asn1_pop_tag(&data);
+
+ if (reply->data != NULL) {
+ asn1_push_tag(&data,ASN1_CONTEXT(1));
+ asn1_write_OID(&data, mechOID);
+ asn1_pop_tag(&data);
+
+ asn1_push_tag(&data,ASN1_CONTEXT(2));
+ asn1_write_OctetString(&data, reply->data, reply->length);
+ asn1_pop_tag(&data);
+ }
+
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ ret = data_blob(data.data, data.length);
+ asn1_free(&data);
+ return ret;
+}
+
+/*
+ parse a SPNEGO NTLMSSP auth packet. This contains the encrypted passwords
+*/
+BOOL spnego_parse_auth_response(DATA_BLOB blob, NTSTATUS nt_status,
+ DATA_BLOB *auth)
+{
+ ASN1_DATA data;
+ uint8 negResult;
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ negResult = SPNEGO_NEG_RESULT_ACCEPT;
+ } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ negResult = SPNEGO_NEG_RESULT_INCOMPLETE;
+ } else {
+ negResult = SPNEGO_NEG_RESULT_REJECT;
+ }
+
+ asn1_load(&data, blob);
+ asn1_start_tag(&data, ASN1_CONTEXT(1));
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+ asn1_start_tag(&data, ASN1_CONTEXT(0));
+ asn1_check_enumerated(&data, negResult);
+ asn1_end_tag(&data);
+
+ if (negResult == SPNEGO_NEG_RESULT_INCOMPLETE) {
+ asn1_start_tag(&data,ASN1_CONTEXT(1));
+ asn1_check_OID(&data, OID_NTLMSSP);
+ asn1_end_tag(&data);
+
+ asn1_start_tag(&data,ASN1_CONTEXT(2));
+ asn1_read_OctetString(&data, auth);
+ asn1_end_tag(&data);
+ }
+
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ if (data.has_error) {
+ DEBUG(3,("spnego_parse_auth_response failed at %d\n", (int)data.ofs));
+ asn1_free(&data);
+ data_blob_free(auth);
+ return False;
+ }
+
+ asn1_free(&data);
+ return True;
+}
+
diff --git a/source/libsmb/clistr.c b/source/libsmb/clistr.c
new file mode 100644
index 00000000000..c61445c0735
--- /dev/null
+++ b/source/libsmb/clistr.c
@@ -0,0 +1,58 @@
+/*
+ Unix SMB/CIFS implementation.
+ client string routines
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+size_t clistr_push_fn(const char *function, unsigned int line,
+ struct cli_state *cli, void *dest,
+ const char *src, int dest_len, int flags)
+{
+ size_t buf_used = PTR_DIFF(dest, cli->outbuf);
+ if (dest_len == -1) {
+ if (((ptrdiff_t)dest < (ptrdiff_t)cli->outbuf) || (buf_used > cli->bufsize)) {
+ DEBUG(0, ("Pushing string of 'unlimited' length into non-SMB buffer!\n"));
+ return push_string_fn(function, line, cli->outbuf, dest, src, -1, flags);
+ }
+ return push_string_fn(function, line, cli->outbuf, dest, src, cli->bufsize - buf_used, flags);
+ }
+
+ /* 'normal' push into size-specified buffer */
+ return push_string_fn(function, line, cli->outbuf, dest, src, dest_len, flags);
+}
+
+size_t clistr_pull_fn(const char *function, unsigned int line,
+ struct cli_state *cli, char *dest, const void *src,
+ int dest_len, int src_len,
+ int flags)
+{
+ return pull_string_fn(function, line, cli->inbuf, dest, src, dest_len, src_len, flags);
+}
+
+
+size_t clistr_align_out(struct cli_state *cli, const void *p, int flags)
+{
+ return align_string(cli->outbuf, p, flags);
+}
+
+size_t clistr_align_in(struct cli_state *cli, const void *p, int flags)
+{
+ return align_string(cli->inbuf, p, flags);
+}
diff --git a/source/libsmb/clitrans.c b/source/libsmb/clitrans.c
new file mode 100644
index 00000000000..ae44ca1a779
--- /dev/null
+++ b/source/libsmb/clitrans.c
@@ -0,0 +1,658 @@
+/*
+ Unix SMB/CIFS implementation.
+ client transaction calls
+ Copyright (C) Andrew Tridgell 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+
+/****************************************************************************
+ Send a SMB trans or trans2 request.
+****************************************************************************/
+
+BOOL cli_send_trans(struct cli_state *cli, int trans,
+ const char *pipe_name,
+ int fid, int flags,
+ uint16 *setup, unsigned int lsetup, unsigned int msetup,
+ const char *param, unsigned int lparam, unsigned int mparam,
+ const char *data, unsigned int ldata, unsigned int mdata)
+{
+ unsigned int i;
+ unsigned int this_ldata,this_lparam;
+ unsigned int tot_data=0,tot_param=0;
+ char *outdata,*outparam;
+ char *p;
+ int pipe_name_len=0;
+ uint16 mid;
+
+ this_lparam = MIN(lparam,cli->max_xmit - (500+lsetup*2)); /* hack */
+ this_ldata = MIN(ldata,cli->max_xmit - (500+lsetup*2+this_lparam));
+
+ memset(cli->outbuf,'\0',smb_size);
+ set_message(cli->outbuf,14+lsetup,0,True);
+ SCVAL(cli->outbuf,smb_com,trans);
+ SSVAL(cli->outbuf,smb_tid, cli->cnum);
+ cli_setup_packet(cli);
+
+ /*
+ * Save the mid we're using. We need this for finding
+ * signing replies.
+ */
+
+ mid = cli->mid;
+
+ if (pipe_name) {
+ pipe_name_len = clistr_push(cli, smb_buf(cli->outbuf), pipe_name, -1, STR_TERMINATE);
+ }
+
+ outparam = smb_buf(cli->outbuf)+(trans==SMBtrans ? pipe_name_len : 3);
+ outdata = outparam+this_lparam;
+
+ /* primary request */
+ SSVAL(cli->outbuf,smb_tpscnt,lparam); /* tpscnt */
+ SSVAL(cli->outbuf,smb_tdscnt,ldata); /* tdscnt */
+ SSVAL(cli->outbuf,smb_mprcnt,mparam); /* mprcnt */
+ SSVAL(cli->outbuf,smb_mdrcnt,mdata); /* mdrcnt */
+ SCVAL(cli->outbuf,smb_msrcnt,msetup); /* msrcnt */
+ SSVAL(cli->outbuf,smb_flags,flags); /* flags */
+ SIVAL(cli->outbuf,smb_timeout,0); /* timeout */
+ SSVAL(cli->outbuf,smb_pscnt,this_lparam); /* pscnt */
+ SSVAL(cli->outbuf,smb_psoff,smb_offset(outparam,cli->outbuf)); /* psoff */
+ SSVAL(cli->outbuf,smb_dscnt,this_ldata); /* dscnt */
+ SSVAL(cli->outbuf,smb_dsoff,smb_offset(outdata,cli->outbuf)); /* dsoff */
+ SCVAL(cli->outbuf,smb_suwcnt,lsetup); /* suwcnt */
+ for (i=0;i<lsetup;i++) /* setup[] */
+ SSVAL(cli->outbuf,smb_setup+i*2,setup[i]);
+ p = smb_buf(cli->outbuf);
+ if (trans != SMBtrans) {
+ *p++ = 0; /* put in a null smb_name */
+ *p++ = 'D'; *p++ = ' '; /* observed in OS/2 */
+ }
+ if (this_lparam) /* param[] */
+ memcpy(outparam,param,this_lparam);
+ if (this_ldata) /* data[] */
+ memcpy(outdata,data,this_ldata);
+ cli_setup_bcc(cli, outdata+this_ldata);
+
+ show_msg(cli->outbuf);
+
+ if (!cli_send_smb(cli)) {
+ return False;
+ }
+
+ if (this_ldata < ldata || this_lparam < lparam) {
+ /* receive interim response */
+ if (!cli_receive_smb(cli) || cli_is_error(cli)) {
+ return(False);
+ }
+
+ tot_data = this_ldata;
+ tot_param = this_lparam;
+
+ while (tot_data < ldata || tot_param < lparam) {
+ this_lparam = MIN(lparam-tot_param,cli->max_xmit - 500); /* hack */
+ this_ldata = MIN(ldata-tot_data,cli->max_xmit - (500+this_lparam));
+
+ set_message(cli->outbuf,trans==SMBtrans?8:9,0,True);
+ SCVAL(cli->outbuf,smb_com,(trans==SMBtrans ? SMBtranss : SMBtranss2));
+
+ outparam = smb_buf(cli->outbuf);
+ outdata = outparam+this_lparam;
+
+ /* secondary request */
+ SSVAL(cli->outbuf,smb_tpscnt,lparam); /* tpscnt */
+ SSVAL(cli->outbuf,smb_tdscnt,ldata); /* tdscnt */
+ SSVAL(cli->outbuf,smb_spscnt,this_lparam); /* pscnt */
+ SSVAL(cli->outbuf,smb_spsoff,smb_offset(outparam,cli->outbuf)); /* psoff */
+ SSVAL(cli->outbuf,smb_spsdisp,tot_param); /* psdisp */
+ SSVAL(cli->outbuf,smb_sdscnt,this_ldata); /* dscnt */
+ SSVAL(cli->outbuf,smb_sdsoff,smb_offset(outdata,cli->outbuf)); /* dsoff */
+ SSVAL(cli->outbuf,smb_sdsdisp,tot_data); /* dsdisp */
+ if (trans==SMBtrans2)
+ SSVALS(cli->outbuf,smb_sfid,fid); /* fid */
+ if (this_lparam) /* param[] */
+ memcpy(outparam,param+tot_param,this_lparam);
+ if (this_ldata) /* data[] */
+ memcpy(outdata,data+tot_data,this_ldata);
+ cli_setup_bcc(cli, outdata+this_ldata);
+
+ /*
+ * Save the mid we're using. We need this for finding
+ * signing replies.
+ */
+ mid = cli->mid;
+
+ show_msg(cli->outbuf);
+ if (!cli_send_smb(cli)) {
+ return False;
+ }
+
+ /* Ensure we use the same mid for the secondaries. */
+ cli->mid = mid;
+
+ tot_data += this_ldata;
+ tot_param += this_lparam;
+ }
+ }
+
+ /* Note we're in a trans state. Save the sequence
+ * numbers for replies. */
+
+ cli_signing_trans_start(cli, mid);
+ return(True);
+}
+
+/****************************************************************************
+ Receive a SMB trans or trans2 response allocating the necessary memory.
+****************************************************************************/
+
+BOOL cli_receive_trans(struct cli_state *cli,int trans,
+ char **param, unsigned int *param_len,
+ char **data, unsigned int *data_len)
+{
+ unsigned int total_data=0;
+ unsigned int total_param=0;
+ unsigned int this_data,this_param;
+ NTSTATUS status;
+ char *tdata;
+ char *tparam;
+
+ *data_len = *param_len = 0;
+
+ if (!cli_receive_smb(cli)) {
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+
+ show_msg(cli->inbuf);
+
+ /* sanity check */
+ if (CVAL(cli->inbuf,smb_com) != trans) {
+ DEBUG(0,("Expected %s response, got command 0x%02x\n",
+ trans==SMBtrans?"SMBtrans":"SMBtrans2",
+ CVAL(cli->inbuf,smb_com)));
+ cli_signing_trans_stop(cli);
+ return(False);
+ }
+
+ /*
+ * An NT RPC pipe call can return ERRDOS, ERRmoredata
+ * to a trans call. This is not an error and should not
+ * be treated as such.
+ */
+ status = cli_nt_error(cli);
+
+ if (NT_STATUS_IS_ERR(status)) {
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+
+ /* parse out the lengths */
+ total_data = SVAL(cli->inbuf,smb_tdrcnt);
+ total_param = SVAL(cli->inbuf,smb_tprcnt);
+
+ /* allocate it */
+ if (total_data!=0) {
+ tdata = Realloc(*data,total_data);
+ if (!tdata) {
+ DEBUG(0,("cli_receive_trans: failed to enlarge data buffer\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+ else
+ *data = tdata;
+ }
+
+ if (total_param!=0) {
+ tparam = Realloc(*param,total_param);
+ if (!tparam) {
+ DEBUG(0,("cli_receive_trans: failed to enlarge param buffer\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+ else
+ *param = tparam;
+ }
+
+ for (;;) {
+ this_data = SVAL(cli->inbuf,smb_drcnt);
+ this_param = SVAL(cli->inbuf,smb_prcnt);
+
+ if (this_data + *data_len > total_data ||
+ this_param + *param_len > total_param) {
+ DEBUG(1,("Data overflow in cli_receive_trans\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+
+ if (this_data + *data_len < this_data ||
+ this_data + *data_len < *data_len ||
+ this_param + *param_len < this_param ||
+ this_param + *param_len < *param_len) {
+ DEBUG(1,("Data overflow in cli_receive_trans\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+
+ if (this_data) {
+ unsigned int data_offset_out = SVAL(cli->inbuf,smb_drdisp);
+ unsigned int data_offset_in = SVAL(cli->inbuf,smb_droff);
+
+ if (data_offset_out > total_data ||
+ data_offset_out + this_data > total_data ||
+ data_offset_out + this_data < data_offset_out ||
+ data_offset_out + this_data < this_data) {
+ DEBUG(1,("Data overflow in cli_receive_trans\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+ if (data_offset_in > cli->bufsize ||
+ data_offset_in + this_data > cli->bufsize ||
+ data_offset_in + this_data < data_offset_in ||
+ data_offset_in + this_data < this_data) {
+ DEBUG(1,("Data overflow in cli_receive_trans\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+
+ memcpy(*data + data_offset_out, smb_base(cli->inbuf) + data_offset_in, this_data);
+ }
+ if (this_param) {
+ unsigned int param_offset_out = SVAL(cli->inbuf,smb_prdisp);
+ unsigned int param_offset_in = SVAL(cli->inbuf,smb_proff);
+
+ if (param_offset_out > total_param ||
+ param_offset_out + this_param > total_param ||
+ param_offset_out + this_param < param_offset_out ||
+ param_offset_out + this_param < this_param) {
+ DEBUG(1,("Param overflow in cli_receive_trans\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+ if (param_offset_in > cli->bufsize ||
+ param_offset_in + this_param > cli->bufsize ||
+ param_offset_in + this_param < param_offset_in ||
+ param_offset_in + this_param < this_param) {
+ DEBUG(1,("Param overflow in cli_receive_trans\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+
+ memcpy(*param + param_offset_out, smb_base(cli->inbuf) + param_offset_in, this_param);
+ }
+ *data_len += this_data;
+ *param_len += this_param;
+
+ if (total_data <= *data_len && total_param <= *param_len)
+ break;
+
+ if (!cli_receive_smb(cli)) {
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+
+ show_msg(cli->inbuf);
+
+ /* sanity check */
+ if (CVAL(cli->inbuf,smb_com) != trans) {
+ DEBUG(0,("Expected %s response, got command 0x%02x\n",
+ trans==SMBtrans?"SMBtrans":"SMBtrans2",
+ CVAL(cli->inbuf,smb_com)));
+ cli_signing_trans_stop(cli);
+ return(False);
+ }
+ if (NT_STATUS_IS_ERR(cli_nt_error(cli))) {
+ cli_signing_trans_stop(cli);
+ return(False);
+ }
+
+ /* parse out the total lengths again - they can shrink! */
+ if (SVAL(cli->inbuf,smb_tdrcnt) < total_data)
+ total_data = SVAL(cli->inbuf,smb_tdrcnt);
+ if (SVAL(cli->inbuf,smb_tprcnt) < total_param)
+ total_param = SVAL(cli->inbuf,smb_tprcnt);
+
+ if (total_data <= *data_len && total_param <= *param_len)
+ break;
+
+ }
+
+ cli_signing_trans_stop(cli);
+ return(True);
+}
+
+/****************************************************************************
+ Send a SMB nttrans request.
+****************************************************************************/
+
+BOOL cli_send_nt_trans(struct cli_state *cli,
+ int function,
+ int flags,
+ uint16 *setup, unsigned int lsetup, unsigned int msetup,
+ char *param, unsigned int lparam, unsigned int mparam,
+ char *data, unsigned int ldata, unsigned int mdata)
+{
+ unsigned int i;
+ unsigned int this_ldata,this_lparam;
+ unsigned int tot_data=0,tot_param=0;
+ uint16 mid;
+ char *outdata,*outparam;
+
+ this_lparam = MIN(lparam,cli->max_xmit - (500+lsetup*2)); /* hack */
+ this_ldata = MIN(ldata,cli->max_xmit - (500+lsetup*2+this_lparam));
+
+ memset(cli->outbuf,'\0',smb_size);
+ set_message(cli->outbuf,19+lsetup,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBnttrans);
+ SSVAL(cli->outbuf,smb_tid, cli->cnum);
+ cli_setup_packet(cli);
+
+ /*
+ * Save the mid we're using. We need this for finding
+ * signing replies.
+ */
+
+ mid = cli->mid;
+
+ outparam = smb_buf(cli->outbuf)+3;
+ outdata = outparam+this_lparam;
+
+ /* primary request */
+ SCVAL(cli->outbuf,smb_nt_MaxSetupCount,msetup);
+ SCVAL(cli->outbuf,smb_nt_Flags,flags);
+ SIVAL(cli->outbuf,smb_nt_TotalParameterCount, lparam);
+ SIVAL(cli->outbuf,smb_nt_TotalDataCount, ldata);
+ SIVAL(cli->outbuf,smb_nt_MaxParameterCount, mparam);
+ SIVAL(cli->outbuf,smb_nt_MaxDataCount, mdata);
+ SIVAL(cli->outbuf,smb_nt_ParameterCount, this_lparam);
+ SIVAL(cli->outbuf,smb_nt_ParameterOffset, smb_offset(outparam,cli->outbuf));
+ SIVAL(cli->outbuf,smb_nt_DataCount, this_ldata);
+ SIVAL(cli->outbuf,smb_nt_DataOffset, smb_offset(outdata,cli->outbuf));
+ SIVAL(cli->outbuf,smb_nt_SetupCount, lsetup);
+ SIVAL(cli->outbuf,smb_nt_Function, function);
+ for (i=0;i<lsetup;i++) /* setup[] */
+ SSVAL(cli->outbuf,smb_nt_SetupStart+i*2,setup[i]);
+
+ if (this_lparam) /* param[] */
+ memcpy(outparam,param,this_lparam);
+ if (this_ldata) /* data[] */
+ memcpy(outdata,data,this_ldata);
+
+ cli_setup_bcc(cli, outdata+this_ldata);
+
+ show_msg(cli->outbuf);
+ if (!cli_send_smb(cli)) {
+ return False;
+ }
+
+ if (this_ldata < ldata || this_lparam < lparam) {
+ /* receive interim response */
+ if (!cli_receive_smb(cli) || cli_is_error(cli)) {
+ return(False);
+ }
+
+ tot_data = this_ldata;
+ tot_param = this_lparam;
+
+ while (tot_data < ldata || tot_param < lparam) {
+ this_lparam = MIN(lparam-tot_param,cli->max_xmit - 500); /* hack */
+ this_ldata = MIN(ldata-tot_data,cli->max_xmit - (500+this_lparam));
+
+ set_message(cli->outbuf,18,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBnttranss);
+
+ /* XXX - these should probably be aligned */
+ outparam = smb_buf(cli->outbuf);
+ outdata = outparam+this_lparam;
+
+ /* secondary request */
+ SIVAL(cli->outbuf,smb_nts_TotalParameterCount,lparam);
+ SIVAL(cli->outbuf,smb_nts_TotalDataCount,ldata);
+ SIVAL(cli->outbuf,smb_nts_ParameterCount,this_lparam);
+ SIVAL(cli->outbuf,smb_nts_ParameterOffset,smb_offset(outparam,cli->outbuf));
+ SIVAL(cli->outbuf,smb_nts_ParameterDisplacement,tot_param);
+ SIVAL(cli->outbuf,smb_nts_DataCount,this_ldata);
+ SIVAL(cli->outbuf,smb_nts_DataOffset,smb_offset(outdata,cli->outbuf));
+ SIVAL(cli->outbuf,smb_nts_DataDisplacement,tot_data);
+ if (this_lparam) /* param[] */
+ memcpy(outparam,param+tot_param,this_lparam);
+ if (this_ldata) /* data[] */
+ memcpy(outdata,data+tot_data,this_ldata);
+ cli_setup_bcc(cli, outdata+this_ldata);
+
+ /*
+ * Save the mid we're using. We need this for finding
+ * signing replies.
+ */
+ mid = cli->mid;
+
+ show_msg(cli->outbuf);
+
+ if (!cli_send_smb(cli)) {
+ return False;
+ }
+
+ /* Ensure we use the same mid for the secondaries. */
+ cli->mid = mid;
+
+ tot_data += this_ldata;
+ tot_param += this_lparam;
+ }
+ }
+
+ /* Note we're in a trans state. Save the sequence
+ * numbers for replies. */
+
+ cli_signing_trans_start(cli, mid);
+ return(True);
+}
+
+/****************************************************************************
+ receive a SMB nttrans response allocating the necessary memory
+ ****************************************************************************/
+
+BOOL cli_receive_nt_trans(struct cli_state *cli,
+ char **param, unsigned int *param_len,
+ char **data, unsigned int *data_len)
+{
+ unsigned int total_data=0;
+ unsigned int total_param=0;
+ unsigned int this_data,this_param;
+ uint8 eclass;
+ uint32 ecode;
+ char *tdata;
+ char *tparam;
+
+ *data_len = *param_len = 0;
+
+ if (!cli_receive_smb(cli)) {
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+
+ show_msg(cli->inbuf);
+
+ /* sanity check */
+ if (CVAL(cli->inbuf,smb_com) != SMBnttrans) {
+ DEBUG(0,("Expected SMBnttrans response, got command 0x%02x\n",
+ CVAL(cli->inbuf,smb_com)));
+ cli_signing_trans_stop(cli);
+ return(False);
+ }
+
+ /*
+ * An NT RPC pipe call can return ERRDOS, ERRmoredata
+ * to a trans call. This is not an error and should not
+ * be treated as such.
+ */
+ if (cli_is_dos_error(cli)) {
+ cli_dos_error(cli, &eclass, &ecode);
+ if (cli->nt_pipe_fnum == 0 || !(eclass == ERRDOS && ecode == ERRmoredata)) {
+ cli_signing_trans_stop(cli);
+ return(False);
+ }
+ }
+
+ /*
+ * Likewise for NT_STATUS_BUFFER_TOO_SMALL
+ */
+ if (cli_is_nt_error(cli)) {
+ if (!NT_STATUS_EQUAL(cli_nt_error(cli),
+ NT_STATUS_BUFFER_TOO_SMALL)) {
+ cli_signing_trans_stop(cli);
+ return(False);
+ }
+ }
+
+ /* parse out the lengths */
+ total_data = SVAL(cli->inbuf,smb_ntr_TotalDataCount);
+ total_param = SVAL(cli->inbuf,smb_ntr_TotalParameterCount);
+
+ /* allocate it */
+ if (total_data) {
+ tdata = Realloc(*data,total_data);
+ if (!tdata) {
+ DEBUG(0,("cli_receive_nt_trans: failed to enlarge data buffer to %d\n",total_data));
+ cli_signing_trans_stop(cli);
+ return False;
+ } else {
+ *data = tdata;
+ }
+ }
+
+ if (total_param) {
+ tparam = Realloc(*param,total_param);
+ if (!tparam) {
+ DEBUG(0,("cli_receive_nt_trans: failed to enlarge param buffer to %d\n", total_param));
+ cli_signing_trans_stop(cli);
+ return False;
+ } else {
+ *param = tparam;
+ }
+ }
+
+ while (1) {
+ this_data = SVAL(cli->inbuf,smb_ntr_DataCount);
+ this_param = SVAL(cli->inbuf,smb_ntr_ParameterCount);
+
+ if (this_data + *data_len > total_data ||
+ this_param + *param_len > total_param) {
+ DEBUG(1,("Data overflow in cli_receive_nt_trans\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+
+ if (this_data + *data_len < this_data ||
+ this_data + *data_len < *data_len ||
+ this_param + *param_len < this_param ||
+ this_param + *param_len < *param_len) {
+ DEBUG(1,("Data overflow in cli_receive_nt_trans\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+
+ if (this_data) {
+ unsigned int data_offset_out = SVAL(cli->inbuf,smb_ntr_DataDisplacement);
+ unsigned int data_offset_in = SVAL(cli->inbuf,smb_ntr_DataOffset);
+
+ if (data_offset_out > total_data ||
+ data_offset_out + this_data > total_data ||
+ data_offset_out + this_data < data_offset_out ||
+ data_offset_out + this_data < this_data) {
+ DEBUG(1,("Data overflow in cli_receive_nt_trans\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+ if (data_offset_in > cli->bufsize ||
+ data_offset_in + this_data > cli->bufsize ||
+ data_offset_in + this_data < data_offset_in ||
+ data_offset_in + this_data < this_data) {
+ DEBUG(1,("Data overflow in cli_receive_nt_trans\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+
+ memcpy(*data + data_offset_out, smb_base(cli->inbuf) + data_offset_in, this_data);
+ }
+
+ if (this_param) {
+ unsigned int param_offset_out = SVAL(cli->inbuf,smb_ntr_ParameterDisplacement);
+ unsigned int param_offset_in = SVAL(cli->inbuf,smb_ntr_ParameterOffset);
+
+ if (param_offset_out > total_param ||
+ param_offset_out + this_param > total_param ||
+ param_offset_out + this_param < param_offset_out ||
+ param_offset_out + this_param < this_param) {
+ DEBUG(1,("Param overflow in cli_receive_nt_trans\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+ if (param_offset_in > cli->bufsize ||
+ param_offset_in + this_param > cli->bufsize ||
+ param_offset_in + this_param < param_offset_in ||
+ param_offset_in + this_param < this_param) {
+ DEBUG(1,("Param overflow in cli_receive_nt_trans\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+
+ memcpy(*param + param_offset_out, smb_base(cli->inbuf) + param_offset_in, this_param);
+ }
+
+ *data_len += this_data;
+ *param_len += this_param;
+
+ if (total_data <= *data_len && total_param <= *param_len)
+ break;
+
+ if (!cli_receive_smb(cli)) {
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+
+ show_msg(cli->inbuf);
+
+ /* sanity check */
+ if (CVAL(cli->inbuf,smb_com) != SMBnttrans) {
+ DEBUG(0,("Expected SMBnttrans response, got command 0x%02x\n",
+ CVAL(cli->inbuf,smb_com)));
+ cli_signing_trans_stop(cli);
+ return(False);
+ }
+ if (cli_is_dos_error(cli)) {
+ cli_dos_error(cli, &eclass, &ecode);
+ if(cli->nt_pipe_fnum == 0 || !(eclass == ERRDOS && ecode == ERRmoredata)) {
+ cli_signing_trans_stop(cli);
+ return(False);
+ }
+ }
+ /* parse out the total lengths again - they can shrink! */
+ if (SVAL(cli->inbuf,smb_ntr_TotalDataCount) < total_data)
+ total_data = SVAL(cli->inbuf,smb_ntr_TotalDataCount);
+ if (SVAL(cli->inbuf,smb_ntr_TotalParameterCount) < total_param)
+ total_param = SVAL(cli->inbuf,smb_ntr_TotalParameterCount);
+
+ if (total_data <= *data_len && total_param <= *param_len)
+ break;
+ }
+
+ cli_signing_trans_stop(cli);
+ return(True);
+}
diff --git a/source/libsmb/conncache.c b/source/libsmb/conncache.c
new file mode 100644
index 00000000000..e6604617d68
--- /dev/null
+++ b/source/libsmb/conncache.c
@@ -0,0 +1,158 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon connection manager
+
+ Copyright (C) Tim Potter 2001
+ Copyright (C) Andrew Bartlett 2002
+ Copyright (C) Gerald (Jerry) Carter 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+#include "includes.h"
+
+#define FAILED_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */
+
+#define CONNCACHE_ADDR 1
+#define CONNCACHE_NAME 2
+
+/* cache entry contains either a server name **or** and IP address as
+ the key. This means that a server could have two entries (one for each key) */
+
+struct failed_connection_cache {
+ fstring domain_name;
+ fstring controller;
+ time_t lookup_time;
+ NTSTATUS nt_status;
+ struct failed_connection_cache *prev, *next;
+};
+
+static struct failed_connection_cache *failed_connection_cache;
+
+/**********************************************************************
+ Check for a previously failed connection
+**********************************************************************/
+
+NTSTATUS check_negative_conn_cache( const char *domain, const char *server )
+{
+ struct failed_connection_cache *fcc;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ /* can't check if we don't have strings */
+
+ if ( !domain || !server )
+ return NT_STATUS_OK;
+
+ for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
+
+ if ( !(strequal(domain, fcc->domain_name) && strequal(server, fcc->controller)) )
+ continue; /* no match; check the next entry */
+
+ /* we have a match so see if it is still current */
+
+ if ((time(NULL) - fcc->lookup_time) > FAILED_CONNECTION_CACHE_TIMEOUT)
+ {
+ /* Cache entry has expired, delete it */
+
+ DEBUG(10, ("check_negative_conn_cache: cache entry expired for %s, %s\n",
+ domain, server ));
+
+ DLIST_REMOVE(failed_connection_cache, fcc);
+ SAFE_FREE(fcc);
+
+ return NT_STATUS_OK;
+ }
+
+ /* The timeout hasn't expired yet so return false */
+
+ DEBUG(10, ("check_negative_conn_cache: returning negative entry for %s, %s\n",
+ domain, server ));
+
+ result = fcc->nt_status;
+ return result;
+ }
+
+ /* end of function means no cache entry */
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Add an entry to the failed conneciton cache (aither a name of dotted
+ decimal IP
+**********************************************************************/
+
+void add_failed_connection_entry(const char *domain, const char *server, NTSTATUS result)
+{
+ struct failed_connection_cache *fcc;
+
+ SMB_ASSERT(!NT_STATUS_IS_OK(result));
+
+ /* Check we already aren't in the cache. We always have to have
+ a domain, but maybe not a specific DC name. */
+
+ for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
+ if ( strequal(fcc->domain_name, domain) && strequal(fcc->controller, server) )
+ {
+ DEBUG(10, ("add_failed_connection_entry: domain %s (%s) already tried and failed\n",
+ domain, server ));
+ return;
+ }
+ }
+
+ /* Create negative lookup cache entry for this domain and controller */
+
+ if ( !(fcc = (struct failed_connection_cache *)malloc(sizeof(struct failed_connection_cache))) )
+ {
+ DEBUG(0, ("malloc failed in add_failed_connection_entry!\n"));
+ return;
+ }
+
+ ZERO_STRUCTP(fcc);
+
+ fstrcpy( fcc->domain_name, domain );
+ fstrcpy( fcc->controller, server );
+ fcc->lookup_time = time(NULL);
+ fcc->nt_status = result;
+
+ DEBUG(10,("add_failed_connection_entry: added domain %s (%s) to failed conn cache\n",
+ domain, server ));
+
+ DLIST_ADD(failed_connection_cache, fcc);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+void flush_negative_conn_cache( void )
+{
+ struct failed_connection_cache *fcc;
+
+ fcc = failed_connection_cache;
+
+ while (fcc) {
+ struct failed_connection_cache *fcc_next;
+
+ fcc_next = fcc->next;
+ DLIST_REMOVE(failed_connection_cache, fcc);
+ free(fcc);
+
+ fcc = fcc_next;
+ }
+
+}
+
+
diff --git a/source/libsmb/credentials.c b/source/libsmb/credentials.c
new file mode 100644
index 00000000000..0d521bae8ac
--- /dev/null
+++ b/source/libsmb/credentials.c
@@ -0,0 +1,215 @@
+/*
+ Unix SMB/CIFS implementation.
+ code to manipulate domain credentials
+ Copyright (C) Andrew Tridgell 1997-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+represent a credential as a string
+****************************************************************************/
+char *credstr(const uchar *cred)
+{
+ static fstring buf;
+ slprintf(buf, sizeof(buf) - 1, "%02X%02X%02X%02X%02X%02X%02X%02X",
+ cred[0], cred[1], cred[2], cred[3],
+ cred[4], cred[5], cred[6], cred[7]);
+ return buf;
+}
+
+
+/****************************************************************************
+ setup the session key.
+Input: 8 byte challenge block
+ 8 byte server challenge block
+ 16 byte md4 encrypted password
+Output:
+ 8 byte session key
+****************************************************************************/
+void cred_session_key(const DOM_CHAL *clnt_chal, const DOM_CHAL *srv_chal, const uchar *pass,
+ uchar session_key[8])
+{
+ uint32 sum[2];
+ unsigned char sum2[8];
+
+ sum[0] = IVAL(clnt_chal->data, 0) + IVAL(srv_chal->data, 0);
+ sum[1] = IVAL(clnt_chal->data, 4) + IVAL(srv_chal->data, 4);
+
+ SIVAL(sum2,0,sum[0]);
+ SIVAL(sum2,4,sum[1]);
+
+ cred_hash1(session_key, sum2, pass);
+
+ /* debug output */
+ DEBUG(4,("cred_session_key\n"));
+
+ DEBUG(5,(" clnt_chal: %s\n", credstr(clnt_chal->data)));
+ DEBUG(5,(" srv_chal : %s\n", credstr(srv_chal->data)));
+ DEBUG(5,(" clnt+srv : %s\n", credstr(sum2)));
+ DEBUG(5,(" sess_key : %s\n", credstr(session_key)));
+}
+
+
+/****************************************************************************
+create a credential
+
+Input:
+ 8 byte sesssion key
+ 8 byte stored credential
+ 4 byte timestamp
+
+Output:
+ 8 byte credential
+****************************************************************************/
+void cred_create(uchar session_key[8], DOM_CHAL *stor_cred, UTIME timestamp,
+ DOM_CHAL *cred)
+{
+ DOM_CHAL time_cred;
+
+ SIVAL(time_cred.data, 0, IVAL(stor_cred->data, 0) + timestamp.time);
+ SIVAL(time_cred.data, 4, IVAL(stor_cred->data, 4));
+
+ cred_hash2(cred->data, time_cred.data, session_key);
+
+ /* debug output*/
+ DEBUG(4,("cred_create\n"));
+
+ DEBUG(5,(" sess_key : %s\n", credstr(session_key)));
+ DEBUG(5,(" stor_cred: %s\n", credstr(stor_cred->data)));
+ DEBUG(5,(" timestamp: %x\n" , timestamp.time));
+ DEBUG(5,(" timecred : %s\n", credstr(time_cred.data)));
+ DEBUG(5,(" calc_cred: %s\n", credstr(cred->data)));
+}
+
+
+/****************************************************************************
+ check a supplied credential
+
+Input:
+ 8 byte received credential
+ 8 byte sesssion key
+ 8 byte stored credential
+ 4 byte timestamp
+
+Output:
+ returns 1 if computed credential matches received credential
+ returns 0 otherwise
+****************************************************************************/
+int cred_assert(DOM_CHAL *cred, uchar session_key[8], DOM_CHAL *stored_cred,
+ UTIME timestamp)
+{
+ DOM_CHAL cred2;
+
+ cred_create(session_key, stored_cred, timestamp, &cred2);
+
+ /* debug output*/
+ DEBUG(4,("cred_assert\n"));
+
+ DEBUG(5,(" challenge : %s\n", credstr(cred->data)));
+ DEBUG(5,(" calculated: %s\n", credstr(cred2.data)));
+
+ if (memcmp(cred->data, cred2.data, 8) == 0)
+ {
+ DEBUG(5, ("credentials check ok\n"));
+ return True;
+ }
+ else
+ {
+ DEBUG(5, ("credentials check wrong\n"));
+ return False;
+ }
+}
+
+
+/****************************************************************************
+ checks credentials; generates next step in the credential chain
+****************************************************************************/
+BOOL clnt_deal_with_creds(uchar sess_key[8],
+ DOM_CRED *sto_clnt_cred, DOM_CRED *rcv_srv_cred)
+{
+ UTIME new_clnt_time;
+ uint32 new_cred;
+
+ DEBUG(5,("clnt_deal_with_creds: %d\n", __LINE__));
+
+ /* increment client time by one second */
+ new_clnt_time.time = sto_clnt_cred->timestamp.time + 1;
+
+ /* check that the received server credentials are valid */
+ if (!cred_assert(&rcv_srv_cred->challenge, sess_key,
+ &sto_clnt_cred->challenge, new_clnt_time))
+ {
+ return False;
+ }
+
+ /* first 4 bytes of the new seed is old client 4 bytes + clnt time + 1 */
+ new_cred = IVAL(sto_clnt_cred->challenge.data, 0);
+ new_cred += new_clnt_time.time;
+
+ /* store new seed in client credentials */
+ SIVAL(sto_clnt_cred->challenge.data, 0, new_cred);
+
+ DEBUG(5,(" new clnt cred: %s\n", credstr(sto_clnt_cred->challenge.data)));
+ return True;
+}
+
+
+/****************************************************************************
+ checks credentials; generates next step in the credential chain
+****************************************************************************/
+BOOL deal_with_creds(uchar sess_key[8],
+ DOM_CRED *sto_clnt_cred,
+ DOM_CRED *rcv_clnt_cred, DOM_CRED *rtn_srv_cred)
+{
+ UTIME new_clnt_time;
+ uint32 new_cred;
+
+ DEBUG(5,("deal_with_creds: %d\n", __LINE__));
+
+ /* check that the received client credentials are valid */
+ if (!cred_assert(&rcv_clnt_cred->challenge, sess_key,
+ &sto_clnt_cred->challenge, rcv_clnt_cred->timestamp))
+ {
+ return False;
+ }
+
+ /* increment client time by one second */
+ new_clnt_time.time = rcv_clnt_cred->timestamp.time + 1;
+
+ /* first 4 bytes of the new seed is old client 4 bytes + clnt time + 1 */
+ new_cred = IVAL(sto_clnt_cred->challenge.data, 0);
+ new_cred += new_clnt_time.time;
+
+ DEBUG(5,("deal_with_creds: new_cred[0]=%x\n", new_cred));
+
+ /* doesn't matter that server time is 0 */
+ rtn_srv_cred->timestamp.time = 0;
+
+ DEBUG(5,("deal_with_creds: new_clnt_time=%x\n", new_clnt_time.time));
+
+ /* create return credentials for inclusion in the reply */
+ cred_create(sess_key, &sto_clnt_cred->challenge, new_clnt_time,
+ &rtn_srv_cred->challenge);
+
+ DEBUG(5,("deal_with_creds: clnt_cred=%s\n", credstr(sto_clnt_cred->challenge.data)));
+
+ /* store new seed in client credentials */
+ SIVAL(sto_clnt_cred->challenge.data, 0, new_cred);
+
+ return True;
+}
diff --git a/source/libsmb/doserr.c b/source/libsmb/doserr.c
new file mode 100644
index 00000000000..c6348568cf9
--- /dev/null
+++ b/source/libsmb/doserr.c
@@ -0,0 +1,92 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * DOS error routines
+ * Copyright (C) Tim Potter 2002.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* DOS error codes. please read doserr.h */
+
+#include "includes.h"
+
+typedef const struct
+{
+ const char *dos_errstr;
+ WERROR werror;
+} werror_code_struct;
+
+werror_code_struct dos_errs[] =
+{
+ { "WERR_OK", WERR_OK },
+ { "WERR_BADFILE", WERR_BADFILE },
+ { "WERR_ACCESS_DENIED", WERR_ACCESS_DENIED },
+ { "WERR_BADFID", WERR_BADFID },
+ { "WERR_BADFUNC", WERR_BADFUNC },
+ { "WERR_INSUFFICIENT_BUFFER", WERR_INSUFFICIENT_BUFFER },
+ { "WERR_NO_SUCH_SHARE", WERR_NO_SUCH_SHARE },
+ { "WERR_ALREADY_EXISTS", WERR_ALREADY_EXISTS },
+ { "WERR_INVALID_PARAM", WERR_INVALID_PARAM },
+ { "WERR_NOT_SUPPORTED", WERR_NOT_SUPPORTED },
+ { "WERR_BAD_PASSWORD", WERR_BAD_PASSWORD },
+ { "WERR_NOMEM", WERR_NOMEM },
+ { "WERR_INVALID_NAME", WERR_INVALID_NAME },
+ { "WERR_UNKNOWN_LEVEL", WERR_UNKNOWN_LEVEL },
+ { "WERR_OBJECT_PATH_INVALID", WERR_OBJECT_PATH_INVALID },
+ { "WERR_NO_MORE_ITEMS", WERR_NO_MORE_ITEMS },
+ { "WERR_MORE_DATA", WERR_MORE_DATA },
+ { "WERR_UNKNOWN_PRINTER_DRIVER", WERR_UNKNOWN_PRINTER_DRIVER },
+ { "WERR_INVALID_PRINTER_NAME", WERR_INVALID_PRINTER_NAME },
+ { "WERR_PRINTER_ALREADY_EXISTS", WERR_PRINTER_ALREADY_EXISTS },
+ { "WERR_INVALID_DATATYPE", WERR_INVALID_DATATYPE },
+ { "WERR_INVALID_ENVIRONMENT", WERR_INVALID_ENVIRONMENT },
+ { "WERR_INVALID_FORM_NAME", WERR_INVALID_FORM_NAME },
+ { "WERR_INVALID_FORM_SIZE", WERR_INVALID_FORM_SIZE },
+ { "WERR_BUF_TOO_SMALL", WERR_BUF_TOO_SMALL },
+ { "WERR_JOB_NOT_FOUND", WERR_JOB_NOT_FOUND },
+ { "WERR_DEST_NOT_FOUND", WERR_DEST_NOT_FOUND },
+ { "WERR_NOT_LOCAL_DOMAIN", WERR_NOT_LOCAL_DOMAIN },
+ { "WERR_PRINTER_DRIVER_IN_USE", WERR_PRINTER_DRIVER_IN_USE },
+ { "WERR_STATUS_MORE_ENTRIES ", WERR_STATUS_MORE_ENTRIES },
+ { "WERR_DFS_NO_SUCH_VOL", WERR_DFS_NO_SUCH_VOL },
+ { "WERR_DFS_NO_SUCH_SHARE", WERR_DFS_NO_SUCH_SHARE },
+ { "WERR_DFS_NO_SUCH_SERVER", WERR_DFS_NO_SUCH_SERVER },
+ { "WERR_DFS_INTERNAL_ERROR", WERR_DFS_INTERNAL_ERROR },
+ { "WERR_DFS_CANT_CREATE_JUNCT", WERR_DFS_CANT_CREATE_JUNCT },
+ { "WERR_INVALID_SECURITY_DESCRIPTOR", WERR_INVALID_SECURITY_DESCRIPTOR },
+ { "WERR_INVALID_OWNER", WERR_INVALID_OWNER },
+ { "WERR_SERVER_UNAVAILABLE", WERR_SERVER_UNAVAILABLE },
+ { NULL, W_ERROR(0) }
+};
+
+/*****************************************************************************
+ returns a DOS error message. not amazingly helpful, but better than a number.
+ *****************************************************************************/
+const char *dos_errstr(WERROR werror)
+{
+ static pstring msg;
+ int idx = 0;
+
+ slprintf(msg, sizeof(msg), "DOS code 0x%08x", W_ERROR_V(werror));
+
+ while (dos_errs[idx].dos_errstr != NULL) {
+ if (W_ERROR_V(dos_errs[idx].werror) ==
+ W_ERROR_V(werror))
+ return dos_errs[idx].dos_errstr;
+ idx++;
+ }
+
+ return msg;
+}
diff --git a/source/libsmb/errormap.c b/source/libsmb/errormap.c
new file mode 100644
index 00000000000..aeb68b6596c
--- /dev/null
+++ b/source/libsmb/errormap.c
@@ -0,0 +1,1548 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * error mapping functions
+ * Copyright (C) Andrew Tridgell 2001
+ * Copyright (C) Andrew Bartlett 2001
+ * Copyright (C) Tim Potter 2000
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+/* This map was extracted by the ERRMAPEXTRACT smbtorture command.
+ The setup was a Samba HEAD (2002-01-03) PDC and an Win2k member
+ workstation. The PDC was modified (by using the 'name_to_nt_status'
+ authentication module) to convert the username (in hex) into the
+ corresponding NTSTATUS error return.
+
+ By opening two nbt sessions to the Win2k workstation, one negotiating
+ DOS and one negotiating NT errors it was possible to extract the
+ error mapping. (Because the server only supplies NT errors, the
+ NT4 workstation had to use its own error tables to convert these
+ to dos errors).
+
+ Some errors show up as 'squashed' because the NT error connection
+ got back a different error to the one it sent, so a mapping could
+ not be determined (a guess has been made in this case, to map the
+ error as squashed). This is done mainly to prevent users from getting
+ NT_STATUS_WRONG_PASSWORD and NT_STATUS_NO_SUCH_USER errors (they get
+ NT_STATUS_LOGON_FAILURE instead.
+
+ -- abartlet (2002-01-03)
+*/
+
+/* NT status -> dos error map */
+static const struct {
+ uint8 dos_class;
+ uint32 dos_code;
+ NTSTATUS ntstatus;
+} ntstatus_to_dos_map[] = {
+ {ERRDOS, ERRgeneral, NT_STATUS_UNSUCCESSFUL},
+ {ERRDOS, ERRbadfunc, NT_STATUS_NOT_IMPLEMENTED},
+ {ERRDOS, 87, NT_STATUS_INVALID_INFO_CLASS},
+ {ERRDOS, 24, NT_STATUS_INFO_LENGTH_MISMATCH},
+ {ERRHRD, ERRgeneral, NT_STATUS_ACCESS_VIOLATION},
+ {ERRHRD, ERRgeneral, NT_STATUS_IN_PAGE_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA},
+ {ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_INITIAL_STACK},
+ {ERRDOS, 193, NT_STATUS_BAD_INITIAL_PC},
+ {ERRDOS, 87, NT_STATUS_INVALID_CID},
+ {ERRHRD, ERRgeneral, NT_STATUS_TIMER_NOT_CANCELED},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER},
+ {ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_DEVICE},
+ {ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_FILE},
+ {ERRDOS, ERRbadfunc, NT_STATUS_INVALID_DEVICE_REQUEST},
+ {ERRDOS, 38, NT_STATUS_END_OF_FILE},
+ {ERRDOS, 34, NT_STATUS_WRONG_VOLUME},
+ {ERRDOS, 21, NT_STATUS_NO_MEDIA_IN_DEVICE},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_MEDIA},
+ {ERRDOS, 27, NT_STATUS_NONEXISTENT_SECTOR},
+/** Session setup succeeded. This shouldn't happen...*/
+/** Session setup succeeded. This shouldn't happen...*/
+/** NT error on DOS connection! (NT_STATUS_OK) */
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_MORE_PROCESSING_REQUIRED to NT_STATUS_OK
+ during the session setup }
+*/
+#if 0
+ {SUCCESS, 0, NT_STATUS_OK},
+#endif
+ {ERRDOS, ERRnomem, NT_STATUS_NO_MEMORY},
+ {ERRDOS, 487, NT_STATUS_CONFLICTING_ADDRESSES},
+ {ERRDOS, 487, NT_STATUS_NOT_MAPPED_VIEW},
+ {ERRDOS, 87, NT_STATUS_UNABLE_TO_FREE_VM},
+ {ERRDOS, 87, NT_STATUS_UNABLE_TO_DELETE_SECTION},
+ {ERRDOS, 2142, NT_STATUS_INVALID_SYSTEM_SERVICE},
+ {ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_INSTRUCTION},
+ {ERRDOS, ERRnoaccess, NT_STATUS_INVALID_LOCK_SEQUENCE},
+ {ERRDOS, ERRnoaccess, NT_STATUS_INVALID_VIEW_SIZE},
+ {ERRDOS, 193, NT_STATUS_INVALID_FILE_FOR_SECTION},
+ {ERRDOS, ERRnoaccess, NT_STATUS_ALREADY_COMMITTED},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_ACCESS_DENIED to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE
+ during the session setup }
+*/
+ {ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED},
+ {ERRDOS, 111, NT_STATUS_BUFFER_TOO_SMALL},
+/*
+ * Not an official error, as only bit 0x80000000, not bits 0xC0000000 are set.
+ */
+ {ERRDOS, ERRmoredata, STATUS_BUFFER_OVERFLOW},
+ {ERRDOS, ERRbadfid, NT_STATUS_OBJECT_TYPE_MISMATCH},
+ {ERRHRD, ERRgeneral, NT_STATUS_NONCONTINUABLE_EXCEPTION},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_DISPOSITION},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNWIND},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_STACK},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_UNWIND_TARGET},
+ {ERRDOS, 158, NT_STATUS_NOT_LOCKED},
+ {ERRHRD, ERRgeneral, NT_STATUS_PARITY_ERROR},
+ {ERRDOS, 487, NT_STATUS_UNABLE_TO_DECOMMIT_VM},
+ {ERRDOS, 487, NT_STATUS_NOT_COMMITTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_PORT_ATTRIBUTES},
+ {ERRHRD, ERRgeneral, NT_STATUS_PORT_MESSAGE_TOO_LONG},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_MIX},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_QUOTA_LOWER},
+ {ERRHRD, ERRgeneral, NT_STATUS_DISK_CORRUPT_ERROR},
+ {ERRDOS, ERRinvalidname, NT_STATUS_OBJECT_NAME_INVALID},
+ {ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_NOT_FOUND},
+ {ERRDOS, 183, NT_STATUS_OBJECT_NAME_COLLISION},
+ {ERRHRD, ERRgeneral, NT_STATUS_HANDLE_NOT_WAITABLE},
+ {ERRDOS, ERRbadfid, NT_STATUS_PORT_DISCONNECTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_ALREADY_ATTACHED},
+ {ERRDOS, 161, NT_STATUS_OBJECT_PATH_INVALID},
+ {ERRDOS, ERRbadpath, NT_STATUS_OBJECT_PATH_NOT_FOUND},
+ {ERRDOS, 161, NT_STATUS_OBJECT_PATH_SYNTAX_BAD},
+ {ERRHRD, ERRgeneral, NT_STATUS_DATA_OVERRUN},
+ {ERRHRD, ERRgeneral, NT_STATUS_DATA_LATE_ERROR},
+ {ERRDOS, 23, NT_STATUS_DATA_ERROR},
+ {ERRDOS, 23, NT_STATUS_CRC_ERROR},
+ {ERRDOS, ERRnomem, NT_STATUS_SECTION_TOO_BIG},
+ {ERRDOS, ERRnoaccess, NT_STATUS_PORT_CONNECTION_REFUSED},
+ {ERRDOS, ERRbadfid, NT_STATUS_INVALID_PORT_HANDLE},
+ {ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION},
+ {ERRHRD, ERRgeneral, NT_STATUS_QUOTA_EXCEEDED},
+ {ERRDOS, 87, NT_STATUS_INVALID_PAGE_PROTECTION},
+ {ERRDOS, 288, NT_STATUS_MUTANT_NOT_OWNED},
+ {ERRDOS, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED},
+ {ERRDOS, 87, NT_STATUS_PORT_ALREADY_SET},
+ {ERRDOS, 87, NT_STATUS_SECTION_NOT_IMAGE},
+ {ERRDOS, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED},
+ {ERRDOS, ERRnoaccess, NT_STATUS_THREAD_IS_TERMINATING},
+ {ERRDOS, 87, NT_STATUS_BAD_WORKING_SET_LIMIT},
+ {ERRDOS, 87, NT_STATUS_INCOMPATIBLE_FILE_MAP},
+ {ERRDOS, 87, NT_STATUS_SECTION_PROTECTION},
+ {ERRDOS, 282, NT_STATUS_EAS_NOT_SUPPORTED},
+ {ERRDOS, 255, NT_STATUS_EA_TOO_LARGE},
+ {ERRHRD, ERRgeneral, NT_STATUS_NONEXISTENT_EA_ENTRY},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_EAS_ON_FILE},
+ {ERRHRD, ERRgeneral, NT_STATUS_EA_CORRUPT_ERROR},
+ {ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT},
+ {ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED},
+ {ERRDOS, ERRnoaccess, NT_STATUS_DELETE_PENDING},
+ {ERRDOS, ERRunsup, NT_STATUS_CTL_FILE_NOT_SUPPORTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNKNOWN_REVISION},
+ {ERRHRD, ERRgeneral, NT_STATUS_REVISION_MISMATCH},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_OWNER},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_PRIMARY_GROUP},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_IMPERSONATION_TOKEN},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANT_DISABLE_MANDATORY},
+ {ERRDOS, 2215, NT_STATUS_NO_LOGON_SERVERS},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_LOGON_SESSION},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PRIVILEGE},
+ {ERRDOS, ERRnoaccess, NT_STATUS_PRIVILEGE_NOT_HELD},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACCOUNT_NAME},
+ {ERRHRD, ERRgeneral, NT_STATUS_USER_EXISTS},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_NO_SUCH_USER to NT_STATUS_LOGON_FAILURE
+ during the session setup }
+*/
+ {ERRDOS, ERRnoaccess, NT_STATUS_NO_SUCH_USER},
+ {ERRHRD, ERRgeneral, NT_STATUS_GROUP_EXISTS},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_GROUP},
+ {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_GROUP},
+ {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_GROUP},
+ {ERRHRD, ERRgeneral, NT_STATUS_LAST_ADMIN},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_WRONG_PASSWORD to NT_STATUS_LOGON_FAILURE
+ during the session setup }
+*/
+ {ERRSRV, ERRbadpw, NT_STATUS_WRONG_PASSWORD},
+ {ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_PASSWORD},
+ {ERRHRD, ERRgeneral, NT_STATUS_PASSWORD_RESTRICTION},
+ {ERRDOS, ERRnoaccess, NT_STATUS_LOGON_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_ACCOUNT_RESTRICTION},
+ {ERRSRV, 2241, NT_STATUS_INVALID_LOGON_HOURS},
+ {ERRSRV, 2240, NT_STATUS_INVALID_WORKSTATION},
+ {ERRSRV, 2242, NT_STATUS_PASSWORD_EXPIRED},
+ {ERRSRV, 2239, NT_STATUS_ACCOUNT_DISABLED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NONE_MAPPED},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_LUIDS_REQUESTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_LUIDS_EXHAUSTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SUB_AUTHORITY},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACL},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SID},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SECURITY_DESCR},
+ {ERRDOS, 127, NT_STATUS_PROCEDURE_NOT_FOUND},
+ {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_FORMAT},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_TOKEN},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_INHERITANCE_ACL},
+ {ERRDOS, 158, NT_STATUS_RANGE_NOT_LOCKED},
+ {ERRDOS, 112, NT_STATUS_DISK_FULL},
+ {ERRHRD, ERRgeneral, NT_STATUS_SERVER_DISABLED},
+ {ERRHRD, ERRgeneral, NT_STATUS_SERVER_NOT_DISABLED},
+ {ERRDOS, 68, NT_STATUS_TOO_MANY_GUIDS_REQUESTED},
+ {ERRDOS, 259, NT_STATUS_GUIDS_EXHAUSTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ID_AUTHORITY},
+ {ERRDOS, 259, NT_STATUS_AGENTS_EXHAUSTED},
+ {ERRDOS, 154, NT_STATUS_INVALID_VOLUME_LABEL},
+ {ERRDOS, ERRres, NT_STATUS_SECTION_NOT_EXTENDED},
+ {ERRDOS, 487, NT_STATUS_NOT_MAPPED_DATA},
+ {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_DATA_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_TYPE_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_NAME_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_ARRAY_BOUNDS_EXCEEDED},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DENORMAL_OPERAND},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DIVIDE_BY_ZERO},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INEXACT_RESULT},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INVALID_OPERATION},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_OVERFLOW},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_STACK_CHECK},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_UNDERFLOW},
+ {ERRHRD, ERRgeneral, NT_STATUS_INTEGER_DIVIDE_BY_ZERO},
+ {ERRDOS, 534, NT_STATUS_INTEGER_OVERFLOW},
+ {ERRHRD, ERRgeneral, NT_STATUS_PRIVILEGED_INSTRUCTION},
+ {ERRDOS, ERRnomem, NT_STATUS_TOO_MANY_PAGING_FILES},
+ {ERRHRD, ERRgeneral, NT_STATUS_FILE_INVALID},
+ {ERRHRD, ERRgeneral, NT_STATUS_ALLOTTED_SPACE_EXCEEDED},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_INSUFFICIENT_RESOURCES to NT_STATUS_INSUFF_SERVER_RESOURCES
+ during the session setup }
+*/
+ {ERRDOS, ERRnomem, NT_STATUS_INSUFFICIENT_RESOURCES},
+ {ERRDOS, ERRbadpath, NT_STATUS_DFS_EXIT_PATH_FOUND},
+ {ERRDOS, 23, NT_STATUS_DEVICE_DATA_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_CONNECTED},
+ {ERRDOS, 21, NT_STATUS_DEVICE_POWER_FAILURE},
+ {ERRDOS, 487, NT_STATUS_FREE_VM_NOT_AT_BASE},
+ {ERRDOS, 487, NT_STATUS_MEMORY_NOT_ALLOCATED},
+ {ERRHRD, ERRgeneral, NT_STATUS_WORKING_SET_QUOTA},
+ {ERRDOS, 19, NT_STATUS_MEDIA_WRITE_PROTECTED},
+ {ERRDOS, 21, NT_STATUS_DEVICE_NOT_READY},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_GROUP_ATTRIBUTES},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_IMPERSONATION_LEVEL},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANT_OPEN_ANONYMOUS},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_VALIDATION_CLASS},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_TOKEN_TYPE},
+ {ERRDOS, 87, NT_STATUS_BAD_MASTER_BOOT_RECORD},
+ {ERRHRD, ERRgeneral, NT_STATUS_INSTRUCTION_MISALIGNMENT},
+ {ERRDOS, ERRpipebusy, NT_STATUS_INSTANCE_NOT_AVAILABLE},
+ {ERRDOS, ERRpipebusy, NT_STATUS_PIPE_NOT_AVAILABLE},
+ {ERRDOS, ERRbadpipe, NT_STATUS_INVALID_PIPE_STATE},
+ {ERRDOS, ERRpipebusy, NT_STATUS_PIPE_BUSY},
+ {ERRDOS, ERRbadfunc, NT_STATUS_ILLEGAL_FUNCTION},
+ {ERRDOS, ERRnotconnected, NT_STATUS_PIPE_DISCONNECTED},
+ {ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_CLOSING},
+ {ERRHRD, ERRgeneral, NT_STATUS_PIPE_CONNECTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_PIPE_LISTENING},
+ {ERRDOS, ERRbadpipe, NT_STATUS_INVALID_READ_MODE},
+ {ERRDOS, 121, NT_STATUS_IO_TIMEOUT},
+ {ERRDOS, 38, NT_STATUS_FILE_FORCED_CLOSED},
+ {ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STARTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STOPPED},
+ {ERRHRD, ERRgeneral, NT_STATUS_COULD_NOT_INTERPRET},
+ {ERRDOS, ERRnoaccess, NT_STATUS_FILE_IS_A_DIRECTORY},
+ {ERRDOS, ERRunsup, NT_STATUS_NOT_SUPPORTED},
+ {ERRDOS, 51, NT_STATUS_REMOTE_NOT_LISTENING},
+ {ERRDOS, 52, NT_STATUS_DUPLICATE_NAME},
+ {ERRDOS, 53, NT_STATUS_BAD_NETWORK_PATH},
+ {ERRDOS, 54, NT_STATUS_NETWORK_BUSY},
+ {ERRDOS, 55, NT_STATUS_DEVICE_DOES_NOT_EXIST},
+ {ERRDOS, 56, NT_STATUS_TOO_MANY_COMMANDS},
+ {ERRDOS, 57, NT_STATUS_ADAPTER_HARDWARE_ERROR},
+ {ERRDOS, 58, NT_STATUS_INVALID_NETWORK_RESPONSE},
+ {ERRDOS, 59, NT_STATUS_UNEXPECTED_NETWORK_ERROR},
+ {ERRDOS, 60, NT_STATUS_BAD_REMOTE_ADAPTER},
+ {ERRDOS, 61, NT_STATUS_PRINT_QUEUE_FULL},
+ {ERRDOS, 62, NT_STATUS_NO_SPOOL_SPACE},
+ {ERRDOS, 63, NT_STATUS_PRINT_CANCELLED},
+ {ERRDOS, 64, NT_STATUS_NETWORK_NAME_DELETED},
+ {ERRDOS, 65, NT_STATUS_NETWORK_ACCESS_DENIED},
+ {ERRDOS, 66, NT_STATUS_BAD_DEVICE_TYPE},
+ {ERRDOS, ERRnosuchshare, NT_STATUS_BAD_NETWORK_NAME},
+ {ERRDOS, 68, NT_STATUS_TOO_MANY_NAMES},
+ {ERRDOS, 69, NT_STATUS_TOO_MANY_SESSIONS},
+ {ERRDOS, 70, NT_STATUS_SHARING_PAUSED},
+ {ERRDOS, 71, NT_STATUS_REQUEST_NOT_ACCEPTED},
+ {ERRDOS, 72, NT_STATUS_REDIRECTOR_PAUSED},
+ {ERRDOS, 88, NT_STATUS_NET_WRITE_FAULT},
+ {ERRHRD, ERRgeneral, NT_STATUS_PROFILING_AT_LIMIT},
+ {ERRDOS, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE},
+ {ERRDOS, ERRnoaccess, NT_STATUS_FILE_RENAMED},
+ {ERRDOS, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SECURITY_ON_OBJECT},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANT_WAIT},
+ {ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_EMPTY},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANT_ACCESS_DOMAIN_INFO},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANT_TERMINATE_SELF},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SERVER_STATE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_STATE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_ROLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_DOMAIN},
+ {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_EXISTS},
+ {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_LIMIT_EXCEEDED},
+ {ERRDOS, 300, NT_STATUS_OPLOCK_NOT_GRANTED},
+ {ERRDOS, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL},
+ {ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_CORRUPTION},
+ {ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_GENERIC_NOT_MAPPED},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_DESCRIPTOR_FORMAT},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_USER_BUFFER},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_IO_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_CREATE_ERR},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_MAP_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_EXTEND_ERR},
+ {ERRHRD, ERRgeneral, NT_STATUS_NOT_LOGON_PROCESS},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_EXISTS},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_1},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_2},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_3},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_4},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_5},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_6},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_7},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_8},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_9},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_10},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_11},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_12},
+ {ERRDOS, ERRbadpath, NT_STATUS_REDIRECTOR_NOT_STARTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_REDIRECTOR_STARTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PACKAGE},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_FUNCTION_TABLE},
+ {ERRDOS, 203, NT_STATUS(0xc0000100)},
+ {ERRDOS, 145, NT_STATUS_DIRECTORY_NOT_EMPTY},
+ {ERRHRD, ERRgeneral, NT_STATUS_FILE_CORRUPT_ERROR},
+ {ERRDOS, 267, NT_STATUS_NOT_A_DIRECTORY},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_LOGON_SESSION_STATE},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_COLLISION},
+ {ERRDOS, 206, NT_STATUS_NAME_TOO_LONG},
+ {ERRDOS, 2401, NT_STATUS_FILES_OPEN},
+ {ERRDOS, 2404, NT_STATUS_CONNECTION_IN_USE},
+ {ERRHRD, ERRgeneral, NT_STATUS_MESSAGE_NOT_FOUND},
+ {ERRDOS, ERRnoaccess, NT_STATUS_PROCESS_IS_TERMINATING},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LOGON_TYPE},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_GUID_TRANSLATION},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANNOT_IMPERSONATE},
+ {ERRHRD, ERRgeneral, NT_STATUS_IMAGE_ALREADY_LOADED},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_PRESENT},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_NOT_EXIST},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_ALREADY_OWNED},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_LID_OWNER},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_COMMAND},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_LID},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_SELECTOR},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_LDT},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_SIZE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_OFFSET},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_DESCRIPTOR},
+ {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NE_FORMAT},
+ {ERRHRD, ERRgeneral, NT_STATUS_RXACT_INVALID_STATE},
+ {ERRHRD, ERRgeneral, NT_STATUS_RXACT_COMMIT_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_MAPPED_FILE_SIZE_ZERO},
+ {ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANCELLED},
+ {ERRDOS, ERRnoaccess, NT_STATUS_CANNOT_DELETE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_COMPUTER_NAME},
+ {ERRDOS, ERRnoaccess, NT_STATUS_FILE_DELETED},
+ {ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_ACCOUNT},
+ {ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_GROUP},
+ {ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_USER},
+ {ERRHRD, ERRgeneral, NT_STATUS_MEMBERS_PRIMARY_GROUP},
+ {ERRDOS, ERRbadfid, NT_STATUS_FILE_CLOSED},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_THREADS},
+ {ERRHRD, ERRgeneral, NT_STATUS_THREAD_NOT_IN_PROCESS},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOKEN_ALREADY_IN_USE},
+ {ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA_EXCEEDED},
+ {ERRHRD, ERRgeneral, NT_STATUS_COMMITMENT_LIMIT},
+ {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_LE_FORMAT},
+ {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NOT_MZ},
+ {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_PROTECT},
+ {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_WIN_16},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOGON_SERVER_CONFLICT},
+ {ERRHRD, ERRgeneral, NT_STATUS_TIME_DIFFERENCE_AT_DC},
+ {ERRHRD, ERRgeneral, NT_STATUS_SYNCHRONIZATION_REQUIRED},
+ {ERRDOS, 126, NT_STATUS_DLL_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_OPEN_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_IO_PRIVILEGE_FAILED},
+ {ERRDOS, 182, NT_STATUS_ORDINAL_NOT_FOUND},
+ {ERRDOS, 127, NT_STATUS_ENTRYPOINT_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONTROL_C_EXIT},
+ {ERRDOS, 64, NT_STATUS_LOCAL_DISCONNECT},
+ {ERRDOS, 64, NT_STATUS_REMOTE_DISCONNECT},
+ {ERRDOS, 51, NT_STATUS_REMOTE_RESOURCES},
+ {ERRDOS, 59, NT_STATUS_LINK_FAILED},
+ {ERRDOS, 59, NT_STATUS_LINK_TIMEOUT},
+ {ERRDOS, 59, NT_STATUS_INVALID_CONNECTION},
+ {ERRDOS, 59, NT_STATUS_INVALID_ADDRESS},
+ {ERRHRD, ERRgeneral, NT_STATUS_DLL_INIT_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_MISSING_SYSTEMFILE},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNHANDLED_EXCEPTION},
+ {ERRHRD, ERRgeneral, NT_STATUS_APP_INIT_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_CREATE_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_PAGEFILE},
+ {ERRDOS, 124, NT_STATUS_INVALID_LEVEL},
+ {ERRDOS, 86, NT_STATUS_WRONG_PASSWORD_CORE},
+ {ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_FLOAT_CONTEXT},
+ {ERRDOS, 109, NT_STATUS_PIPE_BROKEN},
+ {ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_CORRUPT},
+ {ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_IO_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_EVENT_PAIR},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_VOLUME},
+ {ERRHRD, ERRgeneral, NT_STATUS_SERIAL_NO_DEVICE_INITED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_ALIAS},
+ {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_ALIAS},
+ {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_ALIAS},
+ {ERRHRD, ERRgeneral, NT_STATUS_ALIAS_EXISTS},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOGON_NOT_GRANTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SECRETS},
+ {ERRHRD, ERRgeneral, NT_STATUS_SECRET_TOO_LONG},
+ {ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_FULLSCREEN_MODE},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_CONTEXT_IDS},
+ {ERRDOS, ERRnoaccess, NT_STATUS_LOGON_TYPE_NOT_GRANTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NOT_REGISTRY_FILE},
+ {ERRHRD, ERRgeneral, NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED},
+ {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_FT_MISSING_MEMBER},
+ {ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_SERVICE_ENTRY},
+ {ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_CHARACTER},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNMAPPABLE_CHARACTER},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNDEFINED_CHARACTER},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_VOLUME},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_WRONG_CYLINDER},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_UNKNOWN_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_BAD_REGISTERS},
+ {ERRHRD, ERRgeneral, NT_STATUS_DISK_RECALIBRATE_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_DISK_OPERATION_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_DISK_RESET_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_SHARED_IRQ_BUSY},
+ {ERRHRD, ERRgeneral, NT_STATUS_FT_ORPHANING},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000016e)},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000016f)},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc0000170)},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc0000171)},
+ {ERRHRD, ERRgeneral, NT_STATUS_PARTITION_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_BLOCK_LENGTH},
+ {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_PARTITIONED},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_LOCK_MEDIA},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_UNLOAD_MEDIA},
+ {ERRHRD, ERRgeneral, NT_STATUS_EOM_OVERFLOW},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_MEDIA},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc0000179)},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_MEMBER},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_MEMBER},
+ {ERRHRD, ERRgeneral, NT_STATUS_KEY_DELETED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_LOG_SPACE},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SIDS},
+ {ERRHRD, ERRgeneral, NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED},
+ {ERRHRD, ERRgeneral, NT_STATUS_KEY_HAS_CHILDREN},
+ {ERRHRD, ERRgeneral, NT_STATUS_CHILD_MUST_BE_VOLATILE},
+ {ERRDOS, 87, NT_STATUS_DEVICE_CONFIGURATION_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_DRIVER_INTERNAL_ERROR},
+ {ERRDOS, 22, NT_STATUS_INVALID_DEVICE_STATE},
+ {ERRHRD, ERRgeneral, NT_STATUS_IO_DEVICE_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_PROTOCOL_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_BACKUP_CONTROLLER},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOG_FILE_FULL},
+ {ERRDOS, 19, NT_STATUS_TOO_LATE},
+ {ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_LSA_SECRET},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_NO_TRUST_SAM_ACCOUNT to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE
+ during the session setup }
+*/
+ {ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_SAM_ACCOUNT},
+ {ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_DOMAIN_FAILURE},
+ {ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CORRUPT},
+ {ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_CANT_START},
+ {ERRDOS, ERRnoaccess, NT_STATUS_TRUST_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_MUTANT_LIMIT_EXCEEDED},
+ {ERRDOS, ERRinvgroup, NT_STATUS_NETLOGON_NOT_STARTED},
+ {ERRSRV, 2239, NT_STATUS_ACCOUNT_EXPIRED},
+ {ERRHRD, ERRgeneral, NT_STATUS_POSSIBLE_DEADLOCK},
+ {ERRHRD, ERRgeneral, NT_STATUS_NETWORK_CREDENTIAL_CONFLICT},
+ {ERRHRD, ERRgeneral, NT_STATUS_REMOTE_SESSION_LIMIT},
+ {ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CHANGED},
+ {ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT},
+ {ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT},
+ {ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_DOMAIN_TRUST_INCONSISTENT to NT_STATUS_LOGON_FAILURE
+ during the session setup }
+*/
+ {ERRDOS, ERRnoaccess, NT_STATUS_DOMAIN_TRUST_INCONSISTENT},
+ {ERRHRD, ERRgeneral, NT_STATUS_FS_DRIVER_REQUIRED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_USER_SESSION_KEY},
+ {ERRDOS, 59, NT_STATUS_USER_SESSION_DELETED},
+ {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_LANG_NOT_FOUND},
+ {ERRDOS, ERRnomem, NT_STATUS_INSUFF_SERVER_RESOURCES},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_BUFFER_SIZE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_COMPONENT},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_WILDCARD},
+ {ERRDOS, 68, NT_STATUS_TOO_MANY_ADDRESSES},
+ {ERRDOS, 52, NT_STATUS_ADDRESS_ALREADY_EXISTS},
+ {ERRDOS, 64, NT_STATUS_ADDRESS_CLOSED},
+ {ERRDOS, 64, NT_STATUS_CONNECTION_DISCONNECTED},
+ {ERRDOS, 64, NT_STATUS_CONNECTION_RESET},
+ {ERRDOS, 68, NT_STATUS_TOO_MANY_NODES},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_ABORTED},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_TIMED_OUT},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_NO_RELEASE},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_NO_MATCH},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_RESPONDED},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_ID},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_TYPE},
+ {ERRDOS, ERRunsup, NT_STATUS_NOT_SERVER_SESSION},
+ {ERRDOS, ERRunsup, NT_STATUS_NOT_CLIENT_SESSION},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANNOT_LOAD_REGISTRY_FILE},
+ {ERRHRD, ERRgeneral, NT_STATUS_DEBUG_ATTACH_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_SYSTEM_PROCESS_TERMINATED},
+ {ERRHRD, ERRgeneral, NT_STATUS_DATA_NOT_ACCEPTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_BROWSER_SERVERS_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_VDM_HARD_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_DRIVER_CANCEL_TIMEOUT},
+ {ERRHRD, ERRgeneral, NT_STATUS_REPLY_MESSAGE_MISMATCH},
+ {ERRHRD, ERRgeneral, NT_STATUS_MAPPED_ALIGNMENT},
+ {ERRDOS, 193, NT_STATUS_IMAGE_CHECKSUM_MISMATCH},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOST_WRITEBEHIND_DATA},
+ {ERRHRD, ERRgeneral, NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID},
+ {ERRSRV, 2242, NT_STATUS_PASSWORD_MUST_CHANGE},
+ {ERRHRD, ERRgeneral, NT_STATUS_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_NOT_TINY_STREAM},
+ {ERRHRD, ERRgeneral, NT_STATUS_RECOVERY_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW_READ},
+ {ERRHRD, ERRgeneral, NT_STATUS_FAIL_CHECK},
+ {ERRHRD, ERRgeneral, NT_STATUS_DUPLICATE_OBJECTID},
+ {ERRHRD, ERRgeneral, NT_STATUS_OBJECTID_EXISTS},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONVERT_TO_LARGE},
+ {ERRHRD, ERRgeneral, NT_STATUS_RETRY},
+ {ERRHRD, ERRgeneral, NT_STATUS_FOUND_OUT_OF_SCOPE},
+ {ERRHRD, ERRgeneral, NT_STATUS_ALLOCATE_BUCKET},
+ {ERRHRD, ERRgeneral, NT_STATUS_PROPSET_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_MARSHALL_OVERFLOW},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_VARIANT},
+ {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND},
+ {ERRDOS, ERRnoaccess, NT_STATUS_ACCOUNT_LOCKED_OUT},
+ {ERRDOS, ERRbadfid, NT_STATUS_HANDLE_NOT_CLOSABLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_REFUSED},
+ {ERRHRD, ERRgeneral, NT_STATUS_GRACEFUL_DISCONNECT},
+ {ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_ALREADY_ASSOCIATED},
+ {ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_NOT_ASSOCIATED},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_INVALID},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ACTIVE},
+ {ERRHRD, ERRgeneral, NT_STATUS_NETWORK_UNREACHABLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_HOST_UNREACHABLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_PROTOCOL_UNREACHABLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_PORT_UNREACHABLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_REQUEST_ABORTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ABORTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_COMPRESSION_BUFFER},
+ {ERRHRD, ERRgeneral, NT_STATUS_USER_MAPPED_FILE},
+ {ERRHRD, ERRgeneral, NT_STATUS_AUDIT_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_TIMER_RESOLUTION_NOT_SET},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_COUNT_LIMIT},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOGIN_TIME_RESTRICTION},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOGIN_WKSTA_RESTRICTION},
+ {ERRDOS, 193, NT_STATUS_IMAGE_MP_UP_MISMATCH},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000024a)},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000024b)},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000024c)},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000024d)},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000024e)},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000024f)},
+ {ERRHRD, ERRgeneral, NT_STATUS_INSUFFICIENT_LOGON_INFO},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_DLL_ENTRYPOINT},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_SERVICE_ENTRYPOINT},
+ {ERRHRD, ERRgeneral, NT_STATUS_LPC_REPLY_LOST},
+ {ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT1},
+ {ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT2},
+ {ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_QUOTA_LIMIT},
+ {ERRSRV, ERRbadtype, NT_STATUS_PATH_NOT_COVERED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_CALLBACK_ACTIVE},
+ {ERRHRD, ERRgeneral, NT_STATUS_LICENSE_QUOTA_EXCEEDED},
+ {ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_SHORT},
+ {ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_RECENT},
+ {ERRHRD, ERRgeneral, NT_STATUS_PWD_HISTORY_CONFLICT},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000025d)},
+ {ERRHRD, ERRgeneral, NT_STATUS_PLUGPLAY_NO_DEVICE},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNSUPPORTED_COMPRESSION},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_HW_PROFILE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH},
+ {ERRDOS, 182, NT_STATUS_DRIVER_ORDINAL_NOT_FOUND},
+ {ERRDOS, 127, NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND},
+ {ERRDOS, 288, NT_STATUS_RESOURCE_NOT_OWNED},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_LINKS},
+ {ERRHRD, ERRgeneral, NT_STATUS_QUOTA_LIST_INCONSISTENT},
+ {ERRHRD, ERRgeneral, NT_STATUS_FILE_IS_OFFLINE},
+ {ERRDOS, 21, NT_STATUS(0xc000026e)},
+ {ERRDOS, 161, NT_STATUS(0xc0000281)},
+ {ERRDOS, ERRnoaccess, NT_STATUS(0xc000028a)},
+ {ERRDOS, ERRnoaccess, NT_STATUS(0xc000028b)},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000028c)},
+ {ERRDOS, ERRnoaccess, NT_STATUS(0xc000028d)},
+ {ERRDOS, ERRnoaccess, NT_STATUS(0xc000028e)},
+ {ERRDOS, ERRnoaccess, NT_STATUS(0xc000028f)},
+ {ERRDOS, ERRnoaccess, NT_STATUS(0xc0000290)},
+ {ERRDOS, ERRbadfunc, NT_STATUS(0xc000029c)},
+};
+
+
+/* dos -> nt status error map */
+static const struct {
+ uint8 dos_class;
+ uint32 dos_code;
+ NTSTATUS ntstatus;
+} dos_to_ntstatus_map[] = {
+ {ERRDOS, ERRbadfunc, NT_STATUS_NOT_IMPLEMENTED},
+ {ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_FILE},
+ {ERRDOS, ERRbadpath, NT_STATUS_OBJECT_PATH_NOT_FOUND},
+ {ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES},
+ {ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED},
+ {ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE},
+ {ERRDOS, ERRnomem, NT_STATUS_INSUFFICIENT_RESOURCES},
+ {ERRDOS, ERRbadaccess, NT_STATUS_INVALID_LOCK_SEQUENCE},
+ {ERRDOS, ERRbaddata, NT_STATUS_DATA_ERROR},
+ {ERRDOS, 14, NT_STATUS_SECTION_NOT_EXTENDED},
+ {ERRDOS, ERRremcd, NT_STATUS_DIRECTORY_NOT_EMPTY},
+ {ERRDOS, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE},
+ {ERRDOS, ERRnofiles, NT_STATUS(0x80000006)},
+ {ERRDOS, 19, NT_STATUS_MEDIA_WRITE_PROTECTED},
+ {ERRDOS, 21, NT_STATUS_NO_MEDIA_IN_DEVICE},
+ {ERRDOS, 22, NT_STATUS_INVALID_DEVICE_STATE},
+ {ERRDOS, 23, NT_STATUS_DATA_ERROR},
+ {ERRDOS, 24, NT_STATUS_DATA_ERROR},
+ {ERRDOS, 26, NT_STATUS_DISK_CORRUPT_ERROR},
+ {ERRDOS, 27, NT_STATUS_NONEXISTENT_SECTOR},
+ {ERRDOS, 28, NT_STATUS(0x8000000e)},
+ {ERRDOS, 31, NT_STATUS_UNSUCCESSFUL},
+ {ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION},
+ {ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT},
+ {ERRDOS, 34, NT_STATUS_WRONG_VOLUME},
+ {ERRDOS, 38, NT_STATUS_END_OF_FILE},
+ {ERRDOS, ERRunsup, NT_STATUS_CTL_FILE_NOT_SUPPORTED},
+ {ERRDOS, 51, NT_STATUS_REMOTE_NOT_LISTENING},
+ {ERRDOS, 52, NT_STATUS_DUPLICATE_NAME},
+ {ERRDOS, 53, NT_STATUS_BAD_NETWORK_PATH},
+ {ERRDOS, 54, NT_STATUS_NETWORK_BUSY},
+ {ERRDOS, 55, NT_STATUS_DEVICE_DOES_NOT_EXIST},
+ {ERRDOS, 56, NT_STATUS_TOO_MANY_COMMANDS},
+ {ERRDOS, 57, NT_STATUS_ADAPTER_HARDWARE_ERROR},
+ {ERRDOS, 58, NT_STATUS_INVALID_NETWORK_RESPONSE},
+ {ERRDOS, 59, NT_STATUS_UNEXPECTED_NETWORK_ERROR},
+ {ERRDOS, 60, NT_STATUS_BAD_REMOTE_ADAPTER},
+ {ERRDOS, 61, NT_STATUS_PRINT_QUEUE_FULL},
+ {ERRDOS, 62, NT_STATUS_NO_SPOOL_SPACE},
+ {ERRDOS, 63, NT_STATUS_PRINT_CANCELLED},
+ {ERRDOS, 64, NT_STATUS_NETWORK_NAME_DELETED},
+ {ERRDOS, 65, NT_STATUS_NETWORK_ACCESS_DENIED},
+ {ERRDOS, 66, NT_STATUS_BAD_DEVICE_TYPE},
+ {ERRDOS, ERRnosuchshare, NT_STATUS_BAD_NETWORK_NAME},
+ {ERRDOS, 68, NT_STATUS_TOO_MANY_GUIDS_REQUESTED},
+ {ERRDOS, 69, NT_STATUS_TOO_MANY_SESSIONS},
+ {ERRDOS, 70, NT_STATUS_SHARING_PAUSED},
+ {ERRDOS, 71, NT_STATUS_REQUEST_NOT_ACCEPTED},
+ {ERRDOS, 72, NT_STATUS_REDIRECTOR_PAUSED},
+ {ERRDOS, ERRfilexists, NT_STATUS_OBJECT_NAME_COLLISION},
+ {ERRDOS, 86, NT_STATUS_WRONG_PASSWORD},
+ {ERRDOS, 87, NT_STATUS_INVALID_INFO_CLASS},
+ {ERRDOS, 88, NT_STATUS_NET_WRITE_FAULT},
+ {ERRDOS, 109, NT_STATUS_PIPE_BROKEN},
+ {ERRDOS, 111, STATUS_MORE_ENTRIES},
+ {ERRDOS, 112, NT_STATUS_DISK_FULL},
+ {ERRDOS, 121, NT_STATUS_IO_TIMEOUT},
+ {ERRDOS, 122, NT_STATUS_BUFFER_TOO_SMALL},
+ {ERRDOS, ERRinvalidname, NT_STATUS_OBJECT_NAME_INVALID},
+ {ERRDOS, 124, NT_STATUS_INVALID_LEVEL},
+ {ERRDOS, 126, NT_STATUS_DLL_NOT_FOUND},
+ {ERRDOS, 127, NT_STATUS_PROCEDURE_NOT_FOUND},
+ {ERRDOS, 145, NT_STATUS_DIRECTORY_NOT_EMPTY},
+ {ERRDOS, 154, NT_STATUS_INVALID_VOLUME_LABEL},
+ {ERRDOS, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED},
+ {ERRDOS, 158, NT_STATUS_NOT_LOCKED},
+ {ERRDOS, 161, NT_STATUS_OBJECT_PATH_INVALID},
+ {ERRDOS, 170, NT_STATUS(0x80000011)},
+ {ERRDOS, 182, NT_STATUS_ORDINAL_NOT_FOUND},
+ {ERRDOS, 183, NT_STATUS_OBJECT_NAME_COLLISION},
+ {ERRDOS, 193, NT_STATUS_BAD_INITIAL_PC},
+ {ERRDOS, 203, NT_STATUS(0xc0000100)},
+ {ERRDOS, 206, NT_STATUS_NAME_TOO_LONG},
+ {ERRDOS, ERRbadpipe, NT_STATUS_INVALID_INFO_CLASS},
+ {ERRDOS, ERRpipebusy, NT_STATUS_INSTANCE_NOT_AVAILABLE},
+ {ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_CLOSING},
+ {ERRDOS, ERRnotconnected, NT_STATUS_PIPE_DISCONNECTED},
+ {ERRDOS, ERRmoredata, NT_STATUS_MORE_PROCESSING_REQUIRED},
+ {ERRDOS, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED},
+ {ERRDOS, 254, NT_STATUS(0x80000013)},
+ {ERRDOS, 255, NT_STATUS_EA_TOO_LARGE},
+ {ERRDOS, 259, NT_STATUS_GUIDS_EXHAUSTED},
+ {ERRDOS, 267, NT_STATUS_NOT_A_DIRECTORY},
+ {ERRDOS, 275, NT_STATUS_EA_TOO_LARGE},
+ {ERRDOS, 276, NT_STATUS_NONEXISTENT_EA_ENTRY},
+ {ERRDOS, 277, NT_STATUS_NONEXISTENT_EA_ENTRY},
+ {ERRDOS, 278, NT_STATUS_NONEXISTENT_EA_ENTRY},
+ {ERRDOS, 282, NT_STATUS_EAS_NOT_SUPPORTED},
+ {ERRDOS, 288, NT_STATUS_MUTANT_NOT_OWNED},
+ {ERRDOS, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED},
+ {ERRDOS, 299, NT_STATUS(0x8000000d)},
+ {ERRDOS, 300, NT_STATUS_OPLOCK_NOT_GRANTED},
+ {ERRDOS, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL},
+ {ERRDOS, 487, NT_STATUS_CONFLICTING_ADDRESSES},
+ {ERRDOS, 534, NT_STATUS_INTEGER_OVERFLOW},
+ {ERRDOS, 535, NT_STATUS_PIPE_CONNECTED},
+ {ERRDOS, 536, NT_STATUS_PIPE_LISTENING},
+ {ERRDOS, 995, NT_STATUS_CANCELLED},
+ {ERRDOS, 997, NT_STATUS(0x00000103)},
+ {ERRDOS, 998, NT_STATUS_ACCESS_VIOLATION},
+ {ERRDOS, 999, NT_STATUS_IN_PAGE_ERROR},
+ {ERRDOS, 1001, NT_STATUS_BAD_INITIAL_STACK},
+ {ERRDOS, 1005, NT_STATUS_UNRECOGNIZED_VOLUME},
+ {ERRDOS, 1006, NT_STATUS_FILE_INVALID},
+ {ERRDOS, 1007, NT_STATUS_FULLSCREEN_MODE},
+ {ERRDOS, 1008, NT_STATUS_NO_TOKEN},
+ {ERRDOS, 1009, NT_STATUS_REGISTRY_CORRUPT},
+ {ERRDOS, 1016, NT_STATUS_REGISTRY_IO_FAILED},
+ {ERRDOS, 1017, NT_STATUS_NOT_REGISTRY_FILE},
+ {ERRDOS, 1018, NT_STATUS_KEY_DELETED},
+ {ERRDOS, 1019, NT_STATUS_NO_LOG_SPACE},
+ {ERRDOS, 1020, NT_STATUS_KEY_HAS_CHILDREN},
+ {ERRDOS, 1021, NT_STATUS_CHILD_MUST_BE_VOLATILE},
+ {ERRDOS, 1022, NT_STATUS(0x0000010c)},
+ {ERRSRV, ERRbadpw, NT_STATUS_WRONG_PASSWORD},
+ {ERRSRV, ERRbadtype, NT_STATUS_BAD_DEVICE_TYPE},
+ {ERRSRV, ERRaccess, NT_STATUS_NETWORK_ACCESS_DENIED},
+ {ERRSRV, ERRinvnid, NT_STATUS_NETWORK_NAME_DELETED},
+ {ERRSRV, ERRinvnetname, NT_STATUS_BAD_NETWORK_NAME},
+ {ERRSRV, ERRinvdevice, NT_STATUS_BAD_DEVICE_TYPE},
+ {ERRSRV, ERRqfull, NT_STATUS_PRINT_QUEUE_FULL},
+ {ERRSRV, ERRqtoobig, NT_STATUS_NO_SPOOL_SPACE},
+ {ERRSRV, ERRinvpfid, NT_STATUS_PRINT_CANCELLED},
+ {ERRSRV, ERRsmbcmd, NT_STATUS_NOT_IMPLEMENTED},
+ {ERRSRV, ERRbadpermits, NT_STATUS_NETWORK_ACCESS_DENIED},
+ {ERRSRV, ERRpaused, NT_STATUS_SHARING_PAUSED},
+ {ERRSRV, ERRmsgoff, NT_STATUS_REQUEST_NOT_ACCEPTED},
+ {ERRSRV, ERRnoroom, NT_STATUS_DISK_FULL},
+ {ERRSRV, ERRnoresource, NT_STATUS_REQUEST_NOT_ACCEPTED},
+ {ERRSRV, ERRtoomanyuids, NT_STATUS_TOO_MANY_SESSIONS},
+ {ERRSRV, 123, NT_STATUS_OBJECT_NAME_INVALID},
+ {ERRSRV, 206, NT_STATUS_OBJECT_NAME_INVALID},
+ {ERRHRD, 1, NT_STATUS_NOT_IMPLEMENTED},
+ {ERRHRD, 2, NT_STATUS_NO_SUCH_DEVICE},
+ {ERRHRD, 3, NT_STATUS_OBJECT_PATH_NOT_FOUND},
+ {ERRHRD, 4, NT_STATUS_TOO_MANY_OPENED_FILES},
+ {ERRHRD, 5, NT_STATUS_INVALID_LOCK_SEQUENCE},
+ {ERRHRD, 6, NT_STATUS_INVALID_HANDLE},
+ {ERRHRD, 8, NT_STATUS_INSUFFICIENT_RESOURCES},
+ {ERRHRD, 12, NT_STATUS_INVALID_LOCK_SEQUENCE},
+ {ERRHRD, 13, NT_STATUS_DATA_ERROR},
+ {ERRHRD, 14, NT_STATUS_SECTION_NOT_EXTENDED},
+ {ERRHRD, 16, NT_STATUS_DIRECTORY_NOT_EMPTY},
+ {ERRHRD, 17, NT_STATUS_NOT_SAME_DEVICE},
+ {ERRHRD, 18, NT_STATUS(0x80000006)},
+ {ERRHRD, ERRnowrite, NT_STATUS_MEDIA_WRITE_PROTECTED},
+ {ERRHRD, ERRnotready, NT_STATUS_NO_MEDIA_IN_DEVICE},
+ {ERRHRD, ERRbadcmd, NT_STATUS_INVALID_DEVICE_STATE},
+ {ERRHRD, ERRdata, NT_STATUS_DATA_ERROR},
+ {ERRHRD, ERRbadreq, NT_STATUS_DATA_ERROR},
+ {ERRHRD, ERRbadmedia, NT_STATUS_DISK_CORRUPT_ERROR},
+ {ERRHRD, ERRbadsector, NT_STATUS_NONEXISTENT_SECTOR},
+ {ERRHRD, ERRnopaper, NT_STATUS(0x8000000e)},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNSUCCESSFUL},
+ {ERRHRD, ERRbadshare, NT_STATUS_SHARING_VIOLATION},
+ {ERRHRD, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT},
+ {ERRHRD, ERRwrongdisk, NT_STATUS_WRONG_VOLUME},
+ {ERRHRD, 38, NT_STATUS_END_OF_FILE},
+ {ERRHRD, ERRdiskfull, NT_STATUS_DISK_FULL},
+ {ERRHRD, 50, NT_STATUS_CTL_FILE_NOT_SUPPORTED},
+ {ERRHRD, 51, NT_STATUS_REMOTE_NOT_LISTENING},
+ {ERRHRD, 52, NT_STATUS_DUPLICATE_NAME},
+ {ERRHRD, 53, NT_STATUS_BAD_NETWORK_PATH},
+ {ERRHRD, 54, NT_STATUS_NETWORK_BUSY},
+ {ERRHRD, 55, NT_STATUS_DEVICE_DOES_NOT_EXIST},
+ {ERRHRD, 56, NT_STATUS_TOO_MANY_COMMANDS},
+ {ERRHRD, 57, NT_STATUS_ADAPTER_HARDWARE_ERROR},
+ {ERRHRD, 58, NT_STATUS_INVALID_NETWORK_RESPONSE},
+ {ERRHRD, 59, NT_STATUS_UNEXPECTED_NETWORK_ERROR},
+ {ERRHRD, 60, NT_STATUS_BAD_REMOTE_ADAPTER},
+ {ERRHRD, 61, NT_STATUS_PRINT_QUEUE_FULL},
+ {ERRHRD, 62, NT_STATUS_NO_SPOOL_SPACE},
+ {ERRHRD, 63, NT_STATUS_PRINT_CANCELLED},
+ {ERRHRD, 64, NT_STATUS_NETWORK_NAME_DELETED},
+ {ERRHRD, 65, NT_STATUS_NETWORK_ACCESS_DENIED},
+ {ERRHRD, 66, NT_STATUS_BAD_DEVICE_TYPE},
+ {ERRHRD, 67, NT_STATUS_BAD_NETWORK_NAME},
+ {ERRHRD, 68, NT_STATUS_TOO_MANY_GUIDS_REQUESTED},
+ {ERRHRD, 69, NT_STATUS_TOO_MANY_SESSIONS},
+ {ERRHRD, 70, NT_STATUS_SHARING_PAUSED},
+ {ERRHRD, 71, NT_STATUS_REQUEST_NOT_ACCEPTED},
+ {ERRHRD, 72, NT_STATUS_REDIRECTOR_PAUSED},
+ {ERRHRD, 80, NT_STATUS_OBJECT_NAME_COLLISION},
+ {ERRHRD, 86, NT_STATUS_WRONG_PASSWORD},
+ {ERRHRD, 87, NT_STATUS_INVALID_INFO_CLASS},
+ {ERRHRD, 88, NT_STATUS_NET_WRITE_FAULT},
+ {ERRHRD, 109, NT_STATUS_PIPE_BROKEN},
+ {ERRHRD, 111, STATUS_MORE_ENTRIES},
+ {ERRHRD, 112, NT_STATUS_DISK_FULL},
+ {ERRHRD, 121, NT_STATUS_IO_TIMEOUT},
+ {ERRHRD, 122, NT_STATUS_BUFFER_TOO_SMALL},
+ {ERRHRD, 123, NT_STATUS_OBJECT_NAME_INVALID},
+ {ERRHRD, 124, NT_STATUS_INVALID_LEVEL},
+ {ERRHRD, 126, NT_STATUS_DLL_NOT_FOUND},
+ {ERRHRD, 127, NT_STATUS_PROCEDURE_NOT_FOUND},
+ {ERRHRD, 145, NT_STATUS_DIRECTORY_NOT_EMPTY},
+ {ERRHRD, 154, NT_STATUS_INVALID_VOLUME_LABEL},
+ {ERRHRD, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED},
+ {ERRHRD, 158, NT_STATUS_NOT_LOCKED},
+ {ERRHRD, 161, NT_STATUS_OBJECT_PATH_INVALID},
+ {ERRHRD, 170, NT_STATUS(0x80000011)},
+ {ERRHRD, 182, NT_STATUS_ORDINAL_NOT_FOUND},
+ {ERRHRD, 183, NT_STATUS_OBJECT_NAME_COLLISION},
+ {ERRHRD, 193, NT_STATUS_BAD_INITIAL_PC},
+ {ERRHRD, 203, NT_STATUS(0xc0000100)},
+ {ERRHRD, 206, NT_STATUS_NAME_TOO_LONG},
+ {ERRHRD, 230, NT_STATUS_INVALID_INFO_CLASS},
+ {ERRHRD, 231, NT_STATUS_INSTANCE_NOT_AVAILABLE},
+ {ERRHRD, 232, NT_STATUS_PIPE_CLOSING},
+ {ERRHRD, 233, NT_STATUS_PIPE_DISCONNECTED},
+ {ERRHRD, 234, STATUS_MORE_ENTRIES},
+ {ERRHRD, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED},
+ {ERRHRD, 254, NT_STATUS(0x80000013)},
+ {ERRHRD, 255, NT_STATUS_EA_TOO_LARGE},
+ {ERRHRD, 259, NT_STATUS_GUIDS_EXHAUSTED},
+ {ERRHRD, 267, NT_STATUS_NOT_A_DIRECTORY},
+ {ERRHRD, 275, NT_STATUS_EA_TOO_LARGE},
+ {ERRHRD, 276, NT_STATUS_NONEXISTENT_EA_ENTRY},
+ {ERRHRD, 277, NT_STATUS_NONEXISTENT_EA_ENTRY},
+ {ERRHRD, 278, NT_STATUS_NONEXISTENT_EA_ENTRY},
+ {ERRHRD, 282, NT_STATUS_EAS_NOT_SUPPORTED},
+ {ERRHRD, 288, NT_STATUS_MUTANT_NOT_OWNED},
+ {ERRHRD, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED},
+ {ERRHRD, 299, NT_STATUS(0x8000000d)},
+ {ERRHRD, 300, NT_STATUS_OPLOCK_NOT_GRANTED},
+ {ERRHRD, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL},
+ {ERRHRD, 487, NT_STATUS_CONFLICTING_ADDRESSES},
+ {ERRHRD, 534, NT_STATUS_INTEGER_OVERFLOW},
+ {ERRHRD, 535, NT_STATUS_PIPE_CONNECTED},
+ {ERRHRD, 536, NT_STATUS_PIPE_LISTENING},
+ {ERRHRD, 995, NT_STATUS_CANCELLED},
+ {ERRHRD, 997, NT_STATUS(0x00000103)},
+ {ERRHRD, 998, NT_STATUS_ACCESS_VIOLATION},
+ {ERRHRD, 999, NT_STATUS_IN_PAGE_ERROR},
+ {ERRHRD, 1001, NT_STATUS_BAD_INITIAL_STACK},
+ {ERRHRD, 1005, NT_STATUS_UNRECOGNIZED_VOLUME},
+ {ERRHRD, 1006, NT_STATUS_FILE_INVALID},
+ {ERRHRD, 1007, NT_STATUS_FULLSCREEN_MODE},
+ {ERRHRD, 1008, NT_STATUS_NO_TOKEN},
+ {ERRHRD, 1009, NT_STATUS_REGISTRY_CORRUPT},
+ {ERRHRD, 1016, NT_STATUS_REGISTRY_IO_FAILED},
+ {ERRHRD, 1017, NT_STATUS_NOT_REGISTRY_FILE},
+ {ERRHRD, 1018, NT_STATUS_KEY_DELETED},
+ {ERRHRD, 1019, NT_STATUS_NO_LOG_SPACE},
+ {ERRHRD, 1020, NT_STATUS_KEY_HAS_CHILDREN},
+ {ERRHRD, 1021, NT_STATUS_CHILD_MUST_BE_VOLATILE},
+ {ERRHRD, 1022, NT_STATUS(0x0000010c)},
+};
+
+/* errmap NTSTATUS->Win32 */
+static const struct {
+ NTSTATUS ntstatus;
+ WERROR werror;
+} ntstatus_to_werror_map[] = {
+ {NT_STATUS(0x103), W_ERROR(0x3e5)},
+ {NT_STATUS(0x105), W_ERROR(0xea)},
+ {NT_STATUS(0x106), W_ERROR(0x514)},
+ {NT_STATUS(0x107), W_ERROR(0x515)},
+ {NT_STATUS(0x10c), W_ERROR(0x3fe)},
+ {NT_STATUS(0x10d), W_ERROR(0x516)},
+ {NT_STATUS(0x121), W_ERROR(0x2009)},
+ {NT_STATUS(0xc0000001), W_ERROR(0x1f)},
+ {NT_STATUS(0xc0000002), W_ERROR(0x1)},
+ {NT_STATUS(0xc0000003), W_ERROR(0x57)},
+ {NT_STATUS(0xc0000004), W_ERROR(0x18)},
+ {NT_STATUS(0xc0000005), W_ERROR(0x3e6)},
+ {NT_STATUS(0xc0000006), W_ERROR(0x3e7)},
+ {NT_STATUS(0xc0000007), W_ERROR(0x5ae)},
+ {NT_STATUS(0xc0000008), W_ERROR(0x6)},
+ {NT_STATUS(0xc0000009), W_ERROR(0x3e9)},
+ {NT_STATUS(0xc000000a), W_ERROR(0xc1)},
+ {NT_STATUS(0xc000000b), W_ERROR(0x57)},
+ {NT_STATUS(0xc000000d), W_ERROR(0x57)},
+ {NT_STATUS(0xc000000e), W_ERROR(0x2)},
+ {NT_STATUS(0xc000000f), W_ERROR(0x2)},
+ {NT_STATUS(0xc0000010), W_ERROR(0x1)},
+ {NT_STATUS(0xc0000011), W_ERROR(0x26)},
+ {NT_STATUS(0xc0000012), W_ERROR(0x22)},
+ {NT_STATUS(0xc0000013), W_ERROR(0x15)},
+ {NT_STATUS(0xc0000014), W_ERROR(0x6f9)},
+ {NT_STATUS(0xc0000015), W_ERROR(0x1b)},
+ {NT_STATUS(0xc0000016), W_ERROR(0xea)},
+ {NT_STATUS(0xc0000017), W_ERROR(0x8)},
+ {NT_STATUS(0xc0000018), W_ERROR(0x1e7)},
+ {NT_STATUS(0xc0000019), W_ERROR(0x1e7)},
+ {NT_STATUS(0xc000001a), W_ERROR(0x57)},
+ {NT_STATUS(0xc000001b), W_ERROR(0x57)},
+ {NT_STATUS(0xc000001c), W_ERROR(0x1)},
+ {NT_STATUS(0xc000001d), W_ERROR(0xc000001d)},
+ {NT_STATUS(0xc000001e), W_ERROR(0x5)},
+ {NT_STATUS(0xc000001f), W_ERROR(0x5)},
+ {NT_STATUS(0xc0000020), W_ERROR(0xc1)},
+ {NT_STATUS(0xc0000021), W_ERROR(0x5)},
+ {NT_STATUS(0xc0000022), W_ERROR(0x5)},
+ {NT_STATUS(0xc0000023), W_ERROR(0x7a)},
+ {NT_STATUS(0xc0000024), W_ERROR(0x6)},
+ {NT_STATUS(0xc0000025), W_ERROR(0xc0000025)},
+ {NT_STATUS(0xc0000026), W_ERROR(0xc0000026)},
+ {NT_STATUS(0xc000002a), W_ERROR(0x9e)},
+ {NT_STATUS(0xc000002b), W_ERROR(0xc000002b)},
+ {NT_STATUS(0xc000002c), W_ERROR(0x1e7)},
+ {NT_STATUS(0xc000002d), W_ERROR(0x1e7)},
+ {NT_STATUS(0xc0000030), W_ERROR(0x57)},
+ {NT_STATUS(0xc0000032), W_ERROR(0x571)},
+ {NT_STATUS(0xc0000033), W_ERROR(0x7b)},
+ {NT_STATUS(0xc0000034), W_ERROR(0x2)},
+ {NT_STATUS(0xc0000035), W_ERROR(0xb7)},
+ {NT_STATUS(0xc0000037), W_ERROR(0x6)},
+ {NT_STATUS(0xc0000039), W_ERROR(0xa1)},
+ {NT_STATUS(0xc000003a), W_ERROR(0x3)},
+ {NT_STATUS(0xc000003b), W_ERROR(0xa1)},
+ {NT_STATUS(0xc000003c), W_ERROR(0x45d)},
+ {NT_STATUS(0xc000003d), W_ERROR(0x45d)},
+ {NT_STATUS(0xc000003e), W_ERROR(0x17)},
+ {NT_STATUS(0xc000003f), W_ERROR(0x17)},
+ {NT_STATUS(0xc0000040), W_ERROR(0x8)},
+ {NT_STATUS(0xc0000041), W_ERROR(0x5)},
+ {NT_STATUS(0xc0000042), W_ERROR(0x6)},
+ {NT_STATUS(0xc0000043), W_ERROR(0x20)},
+ {NT_STATUS(0xc0000044), W_ERROR(0x718)},
+ {NT_STATUS(0xc0000045), W_ERROR(0x57)},
+ {NT_STATUS(0xc0000046), W_ERROR(0x120)},
+ {NT_STATUS(0xc0000047), W_ERROR(0x12a)},
+ {NT_STATUS(0xc0000048), W_ERROR(0x57)},
+ {NT_STATUS(0xc0000049), W_ERROR(0x57)},
+ {NT_STATUS(0xc000004a), W_ERROR(0x9c)},
+ {NT_STATUS(0xc000004b), W_ERROR(0x5)},
+ {NT_STATUS(0xc000004c), W_ERROR(0x57)},
+ {NT_STATUS(0xc000004d), W_ERROR(0x57)},
+ {NT_STATUS(0xc000004e), W_ERROR(0x57)},
+ {NT_STATUS(0xc000004f), W_ERROR(0x11a)},
+ {NT_STATUS(0xc0000050), W_ERROR(0xff)},
+ {NT_STATUS(0xc0000051), W_ERROR(0x570)},
+ {NT_STATUS(0xc0000052), W_ERROR(0x570)},
+ {NT_STATUS(0xc0000053), W_ERROR(0x570)},
+ {NT_STATUS(0xc0000054), W_ERROR(0x21)},
+ {NT_STATUS(0xc0000055), W_ERROR(0x21)},
+ {NT_STATUS(0xc0000056), W_ERROR(0x5)},
+ {NT_STATUS(0xc0000057), W_ERROR(0x32)},
+ {NT_STATUS(0xc0000058), W_ERROR(0x519)},
+ {NT_STATUS(0xc0000059), W_ERROR(0x51a)},
+ {NT_STATUS(0xc000005a), W_ERROR(0x51b)},
+ {NT_STATUS(0xc000005b), W_ERROR(0x51c)},
+ {NT_STATUS(0xc000005c), W_ERROR(0x51d)},
+ {NT_STATUS(0xc000005d), W_ERROR(0x51e)},
+ {NT_STATUS(0xc000005e), W_ERROR(0x51f)},
+ {NT_STATUS(0xc000005f), W_ERROR(0x520)},
+ {NT_STATUS(0xc0000060), W_ERROR(0x521)},
+ {NT_STATUS(0xc0000061), W_ERROR(0x522)},
+ {NT_STATUS(0xc0000062), W_ERROR(0x523)},
+ {NT_STATUS(0xc0000063), W_ERROR(0x524)},
+ {NT_STATUS(0xc0000064), W_ERROR(0x525)},
+ {NT_STATUS(0xc0000065), W_ERROR(0x526)},
+ {NT_STATUS(0xc0000066), W_ERROR(0x527)},
+ {NT_STATUS(0xc0000067), W_ERROR(0x528)},
+ {NT_STATUS(0xc0000068), W_ERROR(0x529)},
+ {NT_STATUS(0xc0000069), W_ERROR(0x52a)},
+ {NT_STATUS(0xc000006a), W_ERROR(0x56)},
+ {NT_STATUS(0xc000006b), W_ERROR(0x52c)},
+ {NT_STATUS(0xc000006c), W_ERROR(0x52d)},
+ {NT_STATUS(0xc000006d), W_ERROR(0x52e)},
+ {NT_STATUS(0xc000006e), W_ERROR(0x52f)},
+ {NT_STATUS(0xc000006f), W_ERROR(0x530)},
+ {NT_STATUS(0xc0000070), W_ERROR(0x531)},
+ {NT_STATUS(0xc0000071), W_ERROR(0x532)},
+ {NT_STATUS(0xc0000072), W_ERROR(0x533)},
+ {NT_STATUS(0xc0000073), W_ERROR(0x534)},
+ {NT_STATUS(0xc0000074), W_ERROR(0x535)},
+ {NT_STATUS(0xc0000075), W_ERROR(0x536)},
+ {NT_STATUS(0xc0000076), W_ERROR(0x537)},
+ {NT_STATUS(0xc0000077), W_ERROR(0x538)},
+ {NT_STATUS(0xc0000078), W_ERROR(0x539)},
+ {NT_STATUS(0xc0000079), W_ERROR(0x53a)},
+ {NT_STATUS(0xc000007a), W_ERROR(0x7f)},
+ {NT_STATUS(0xc000007b), W_ERROR(0xc1)},
+ {NT_STATUS(0xc000007c), W_ERROR(0x3f0)},
+ {NT_STATUS(0xc000007d), W_ERROR(0x53c)},
+ {NT_STATUS(0xc000007e), W_ERROR(0x9e)},
+ {NT_STATUS(0xc000007f), W_ERROR(0x70)},
+ {NT_STATUS(0xc0000080), W_ERROR(0x53d)},
+ {NT_STATUS(0xc0000081), W_ERROR(0x53e)},
+ {NT_STATUS(0xc0000082), W_ERROR(0x44)},
+ {NT_STATUS(0xc0000083), W_ERROR(0x103)},
+ {NT_STATUS(0xc0000084), W_ERROR(0x53f)},
+ {NT_STATUS(0xc0000085), W_ERROR(0x103)},
+ {NT_STATUS(0xc0000086), W_ERROR(0x9a)},
+ {NT_STATUS(0xc0000087), W_ERROR(0xe)},
+ {NT_STATUS(0xc0000088), W_ERROR(0x1e7)},
+ {NT_STATUS(0xc0000089), W_ERROR(0x714)},
+ {NT_STATUS(0xc000008a), W_ERROR(0x715)},
+ {NT_STATUS(0xc000008b), W_ERROR(0x716)},
+ {NT_STATUS(0xc000008c), W_ERROR(0xc000008c)},
+ {NT_STATUS(0xc000008d), W_ERROR(0xc000008d)},
+ {NT_STATUS(0xc000008e), W_ERROR(0xc000008e)},
+ {NT_STATUS(0xc000008f), W_ERROR(0xc000008f)},
+ {NT_STATUS(0xc0000090), W_ERROR(0xc0000090)},
+ {NT_STATUS(0xc0000091), W_ERROR(0xc0000091)},
+ {NT_STATUS(0xc0000092), W_ERROR(0xc0000092)},
+ {NT_STATUS(0xc0000093), W_ERROR(0xc0000093)},
+ {NT_STATUS(0xc0000094), W_ERROR(0xc0000094)},
+ {NT_STATUS(0xc0000095), W_ERROR(0x216)},
+ {NT_STATUS(0xc0000096), W_ERROR(0xc0000096)},
+ {NT_STATUS(0xc0000097), W_ERROR(0x8)},
+ {NT_STATUS(0xc0000098), W_ERROR(0x3ee)},
+ {NT_STATUS(0xc0000099), W_ERROR(0x540)},
+ {NT_STATUS(0xc000009a), W_ERROR(0x5aa)},
+ {NT_STATUS(0xc000009b), W_ERROR(0x3)},
+ {NT_STATUS(0xc000009c), W_ERROR(0x17)},
+ {NT_STATUS(0xc000009d), W_ERROR(0x48f)},
+ {NT_STATUS(0xc000009e), W_ERROR(0x15)},
+ {NT_STATUS(0xc000009f), W_ERROR(0x1e7)},
+ {NT_STATUS(0xc00000a0), W_ERROR(0x1e7)},
+ {NT_STATUS(0xc00000a1), W_ERROR(0x5ad)},
+ {NT_STATUS(0xc00000a2), W_ERROR(0x13)},
+ {NT_STATUS(0xc00000a3), W_ERROR(0x15)},
+ {NT_STATUS(0xc00000a4), W_ERROR(0x541)},
+ {NT_STATUS(0xc00000a5), W_ERROR(0x542)},
+ {NT_STATUS(0xc00000a6), W_ERROR(0x543)},
+ {NT_STATUS(0xc00000a7), W_ERROR(0x544)},
+ {NT_STATUS(0xc00000a8), W_ERROR(0x545)},
+ {NT_STATUS(0xc00000a9), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000ab), W_ERROR(0xe7)},
+ {NT_STATUS(0xc00000ac), W_ERROR(0xe7)},
+ {NT_STATUS(0xc00000ad), W_ERROR(0xe6)},
+ {NT_STATUS(0xc00000ae), W_ERROR(0xe7)},
+ {NT_STATUS(0xc00000af), W_ERROR(0x1)},
+ {NT_STATUS(0xc00000b0), W_ERROR(0xe9)},
+ {NT_STATUS(0xc00000b1), W_ERROR(0xe8)},
+ {NT_STATUS(0xc00000b2), W_ERROR(0x217)},
+ {NT_STATUS(0xc00000b3), W_ERROR(0x218)},
+ {NT_STATUS(0xc00000b4), W_ERROR(0xe6)},
+ {NT_STATUS(0xc00000b5), W_ERROR(0x79)},
+ {NT_STATUS(0xc00000b6), W_ERROR(0x26)},
+ {NT_STATUS(0xc00000ba), W_ERROR(0x5)},
+ {NT_STATUS(0xc00000bb), W_ERROR(0x32)},
+ {NT_STATUS(0xc00000bc), W_ERROR(0x33)},
+ {NT_STATUS(0xc00000bd), W_ERROR(0x34)},
+ {NT_STATUS(0xc00000be), W_ERROR(0x35)},
+ {NT_STATUS(0xc00000bf), W_ERROR(0x36)},
+ {NT_STATUS(0xc00000c0), W_ERROR(0x37)},
+ {NT_STATUS(0xc00000c1), W_ERROR(0x38)},
+ {NT_STATUS(0xc00000c2), W_ERROR(0x39)},
+ {NT_STATUS(0xc00000c3), W_ERROR(0x3a)},
+ {NT_STATUS(0xc00000c4), W_ERROR(0x3b)},
+ {NT_STATUS(0xc00000c5), W_ERROR(0x3c)},
+ {NT_STATUS(0xc00000c6), W_ERROR(0x3d)},
+ {NT_STATUS(0xc00000c7), W_ERROR(0x3e)},
+ {NT_STATUS(0xc00000c8), W_ERROR(0x3f)},
+ {NT_STATUS(0xc00000c9), W_ERROR(0x40)},
+ {NT_STATUS(0xc00000ca), W_ERROR(0x41)},
+ {NT_STATUS(0xc00000cb), W_ERROR(0x42)},
+ {NT_STATUS(0xc00000cc), W_ERROR(0x43)},
+ {NT_STATUS(0xc00000cd), W_ERROR(0x44)},
+ {NT_STATUS(0xc00000ce), W_ERROR(0x45)},
+ {NT_STATUS(0xc00000cf), W_ERROR(0x46)},
+ {NT_STATUS(0xc00000d0), W_ERROR(0x47)},
+ {NT_STATUS(0xc00000d1), W_ERROR(0x48)},
+ {NT_STATUS(0xc00000d2), W_ERROR(0x58)},
+ {NT_STATUS(0xc00000d4), W_ERROR(0x11)},
+ {NT_STATUS(0xc00000d5), W_ERROR(0x5)},
+ {NT_STATUS(0xc00000d6), W_ERROR(0xf0)},
+ {NT_STATUS(0xc00000d7), W_ERROR(0x546)},
+ {NT_STATUS(0xc00000d9), W_ERROR(0xe8)},
+ {NT_STATUS(0xc00000da), W_ERROR(0x547)},
+ {NT_STATUS(0xc00000dc), W_ERROR(0x548)},
+ {NT_STATUS(0xc00000dd), W_ERROR(0x549)},
+ {NT_STATUS(0xc00000de), W_ERROR(0x54a)},
+ {NT_STATUS(0xc00000df), W_ERROR(0x54b)},
+ {NT_STATUS(0xc00000e0), W_ERROR(0x54c)},
+ {NT_STATUS(0xc00000e1), W_ERROR(0x54d)},
+ {NT_STATUS(0xc00000e2), W_ERROR(0x12c)},
+ {NT_STATUS(0xc00000e3), W_ERROR(0x12d)},
+ {NT_STATUS(0xc00000e4), W_ERROR(0x54e)},
+ {NT_STATUS(0xc00000e5), W_ERROR(0x54f)},
+ {NT_STATUS(0xc00000e6), W_ERROR(0x550)},
+ {NT_STATUS(0xc00000e7), W_ERROR(0x551)},
+ {NT_STATUS(0xc00000e8), W_ERROR(0x6f8)},
+ {NT_STATUS(0xc00000ed), W_ERROR(0x552)},
+ {NT_STATUS(0xc00000ee), W_ERROR(0x553)},
+ {NT_STATUS(0xc00000ef), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f0), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f1), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f2), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f3), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f4), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f5), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f6), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f7), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f8), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f9), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000fa), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000fb), W_ERROR(0x3)},
+ {NT_STATUS(0xc00000fd), W_ERROR(0x3e9)},
+ {NT_STATUS(0xc00000fe), W_ERROR(0x554)},
+ {NT_STATUS(0xc0000100), W_ERROR(0xcb)},
+ {NT_STATUS(0xc0000101), W_ERROR(0x91)},
+ {NT_STATUS(0xc0000102), W_ERROR(0x570)},
+ {NT_STATUS(0xc0000103), W_ERROR(0x10b)},
+ {NT_STATUS(0xc0000104), W_ERROR(0x555)},
+ {NT_STATUS(0xc0000105), W_ERROR(0x556)},
+ {NT_STATUS(0xc0000106), W_ERROR(0xce)},
+ {NT_STATUS(0xc0000107), W_ERROR(0x961)},
+ {NT_STATUS(0xc0000108), W_ERROR(0x964)},
+ {NT_STATUS(0xc000010a), W_ERROR(0x5)},
+ {NT_STATUS(0xc000010b), W_ERROR(0x557)},
+ {NT_STATUS(0xc000010d), W_ERROR(0x558)},
+ {NT_STATUS(0xc000010e), W_ERROR(0x420)},
+ {NT_STATUS(0xc0000117), W_ERROR(0x5a4)},
+ {NT_STATUS(0xc000011b), W_ERROR(0xc1)},
+ {NT_STATUS(0xc000011c), W_ERROR(0x559)},
+ {NT_STATUS(0xc000011d), W_ERROR(0x55a)},
+ {NT_STATUS(0xc000011e), W_ERROR(0x3ee)},
+ {NT_STATUS(0xc000011f), W_ERROR(0x4)},
+ {NT_STATUS(0xc0000120), W_ERROR(0x3e3)},
+ {NT_STATUS(0xc0000121), W_ERROR(0x5)},
+ {NT_STATUS(0xc0000122), W_ERROR(0x4ba)},
+ {NT_STATUS(0xc0000123), W_ERROR(0x5)},
+ {NT_STATUS(0xc0000124), W_ERROR(0x55b)},
+ {NT_STATUS(0xc0000125), W_ERROR(0x55c)},
+ {NT_STATUS(0xc0000126), W_ERROR(0x55d)},
+ {NT_STATUS(0xc0000127), W_ERROR(0x55e)},
+ {NT_STATUS(0xc0000128), W_ERROR(0x6)},
+ {NT_STATUS(0xc000012b), W_ERROR(0x55f)},
+ {NT_STATUS(0xc000012d), W_ERROR(0x5af)},
+ {NT_STATUS(0xc000012e), W_ERROR(0xc1)},
+ {NT_STATUS(0xc000012f), W_ERROR(0xc1)},
+ {NT_STATUS(0xc0000130), W_ERROR(0xc1)},
+ {NT_STATUS(0xc0000131), W_ERROR(0xc1)},
+ {NT_STATUS(0xc0000133), W_ERROR(0x576)},
+ {NT_STATUS(0xc0000135), W_ERROR(0x7e)},
+ {NT_STATUS(0xc0000138), W_ERROR(0xb6)},
+ {NT_STATUS(0xc0000139), W_ERROR(0x7f)},
+ {NT_STATUS(0xc000013b), W_ERROR(0x40)},
+ {NT_STATUS(0xc000013c), W_ERROR(0x40)},
+ {NT_STATUS(0xc000013d), W_ERROR(0x33)},
+ {NT_STATUS(0xc000013e), W_ERROR(0x3b)},
+ {NT_STATUS(0xc000013f), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000140), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000141), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000142), W_ERROR(0x45a)},
+ {NT_STATUS(0xc0000148), W_ERROR(0x7c)},
+ {NT_STATUS(0xc0000149), W_ERROR(0x56)},
+ {NT_STATUS(0xc000014b), W_ERROR(0x6d)},
+ {NT_STATUS(0xc000014c), W_ERROR(0x3f1)},
+ {NT_STATUS(0xc000014d), W_ERROR(0x3f8)},
+ {NT_STATUS(0xc000014f), W_ERROR(0x3ed)},
+ {NT_STATUS(0xc0000150), W_ERROR(0x45e)},
+ {NT_STATUS(0xc0000151), W_ERROR(0x560)},
+ {NT_STATUS(0xc0000152), W_ERROR(0x561)},
+ {NT_STATUS(0xc0000153), W_ERROR(0x562)},
+ {NT_STATUS(0xc0000154), W_ERROR(0x563)},
+ {NT_STATUS(0xc0000155), W_ERROR(0x564)},
+ {NT_STATUS(0xc0000156), W_ERROR(0x565)},
+ {NT_STATUS(0xc0000157), W_ERROR(0x566)},
+ {NT_STATUS(0xc0000158), W_ERROR(0x567)},
+ {NT_STATUS(0xc0000159), W_ERROR(0x3ef)},
+ {NT_STATUS(0xc000015a), W_ERROR(0x568)},
+ {NT_STATUS(0xc000015b), W_ERROR(0x569)},
+ {NT_STATUS(0xc000015c), W_ERROR(0x3f9)},
+ {NT_STATUS(0xc000015d), W_ERROR(0x56a)},
+ {NT_STATUS(0xc000015f), W_ERROR(0x45d)},
+ {NT_STATUS(0xc0000162), W_ERROR(0x459)},
+ {NT_STATUS(0xc0000165), W_ERROR(0x462)},
+ {NT_STATUS(0xc0000166), W_ERROR(0x463)},
+ {NT_STATUS(0xc0000167), W_ERROR(0x464)},
+ {NT_STATUS(0xc0000168), W_ERROR(0x465)},
+ {NT_STATUS(0xc0000169), W_ERROR(0x466)},
+ {NT_STATUS(0xc000016a), W_ERROR(0x467)},
+ {NT_STATUS(0xc000016b), W_ERROR(0x468)},
+ {NT_STATUS(0xc000016c), W_ERROR(0x45f)},
+ {NT_STATUS(0xc000016d), W_ERROR(0x45d)},
+ {NT_STATUS(0xc0000172), W_ERROR(0x451)},
+ {NT_STATUS(0xc0000173), W_ERROR(0x452)},
+ {NT_STATUS(0xc0000174), W_ERROR(0x453)},
+ {NT_STATUS(0xc0000175), W_ERROR(0x454)},
+ {NT_STATUS(0xc0000176), W_ERROR(0x455)},
+ {NT_STATUS(0xc0000177), W_ERROR(0x469)},
+ {NT_STATUS(0xc0000178), W_ERROR(0x458)},
+ {NT_STATUS(0xc000017a), W_ERROR(0x56b)},
+ {NT_STATUS(0xc000017b), W_ERROR(0x56c)},
+ {NT_STATUS(0xc000017c), W_ERROR(0x3fa)},
+ {NT_STATUS(0xc000017d), W_ERROR(0x3fb)},
+ {NT_STATUS(0xc000017e), W_ERROR(0x56d)},
+ {NT_STATUS(0xc000017f), W_ERROR(0x56e)},
+ {NT_STATUS(0xc0000180), W_ERROR(0x3fc)},
+ {NT_STATUS(0xc0000181), W_ERROR(0x3fd)},
+ {NT_STATUS(0xc0000182), W_ERROR(0x57)},
+ {NT_STATUS(0xc0000183), W_ERROR(0x45d)},
+ {NT_STATUS(0xc0000184), W_ERROR(0x16)},
+ {NT_STATUS(0xc0000185), W_ERROR(0x45d)},
+ {NT_STATUS(0xc0000186), W_ERROR(0x45d)},
+ {NT_STATUS(0xc0000188), W_ERROR(0x5de)},
+ {NT_STATUS(0xc0000189), W_ERROR(0x13)},
+ {NT_STATUS(0xc000018a), W_ERROR(0x6fa)},
+ {NT_STATUS(0xc000018b), W_ERROR(0x6fb)},
+ {NT_STATUS(0xc000018c), W_ERROR(0x6fc)},
+ {NT_STATUS(0xc000018d), W_ERROR(0x6fd)},
+ {NT_STATUS(0xc000018e), W_ERROR(0x5dc)},
+ {NT_STATUS(0xc000018f), W_ERROR(0x5dd)},
+ {NT_STATUS(0xc0000190), W_ERROR(0x6fe)},
+ {NT_STATUS(0xc0000192), W_ERROR(0x700)},
+ {NT_STATUS(0xc0000193), W_ERROR(0x701)},
+ {NT_STATUS(0xc0000194), W_ERROR(0x46b)},
+ {NT_STATUS(0xc0000195), W_ERROR(0x4c3)},
+ {NT_STATUS(0xc0000196), W_ERROR(0x4c4)},
+ {NT_STATUS(0xc0000197), W_ERROR(0x5df)},
+ {NT_STATUS(0xc0000198), W_ERROR(0x70f)},
+ {NT_STATUS(0xc0000199), W_ERROR(0x710)},
+ {NT_STATUS(0xc000019a), W_ERROR(0x711)},
+ {NT_STATUS(0xc000019b), W_ERROR(0x712)},
+ {NT_STATUS(0xc0000202), W_ERROR(0x572)},
+ {NT_STATUS(0xc0000203), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000204), W_ERROR(0x717)},
+ {NT_STATUS(0xc0000205), W_ERROR(0x46a)},
+ {NT_STATUS(0xc0000206), W_ERROR(0x6f8)},
+ {NT_STATUS(0xc0000207), W_ERROR(0x4be)},
+ {NT_STATUS(0xc0000208), W_ERROR(0x4be)},
+ {NT_STATUS(0xc0000209), W_ERROR(0x44)},
+ {NT_STATUS(0xc000020a), W_ERROR(0x34)},
+ {NT_STATUS(0xc000020b), W_ERROR(0x40)},
+ {NT_STATUS(0xc000020c), W_ERROR(0x40)},
+ {NT_STATUS(0xc000020d), W_ERROR(0x40)},
+ {NT_STATUS(0xc000020e), W_ERROR(0x44)},
+ {NT_STATUS(0xc000020f), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000210), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000211), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000212), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000213), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000214), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000215), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000216), W_ERROR(0x32)},
+ {NT_STATUS(0xc0000217), W_ERROR(0x32)},
+ {NT_STATUS(0xc000021c), W_ERROR(0x17e6)},
+ {NT_STATUS(0xc0000220), W_ERROR(0x46c)},
+ {NT_STATUS(0xc0000221), W_ERROR(0xc1)},
+ {NT_STATUS(0xc0000224), W_ERROR(0x773)},
+ {NT_STATUS(0xc0000225), W_ERROR(0x490)},
+ {NT_STATUS(0xc000022a), W_ERROR(0xc000022a)},
+ {NT_STATUS(0xc000022b), W_ERROR(0xc000022b)},
+ {NT_STATUS(0xc000022d), W_ERROR(0x4d5)},
+ {NT_STATUS(0xc0000230), W_ERROR(0x492)},
+ {NT_STATUS(0xc0000233), W_ERROR(0x774)},
+ {NT_STATUS(0xc0000234), W_ERROR(0x775)},
+ {NT_STATUS(0xc0000235), W_ERROR(0x6)},
+ {NT_STATUS(0xc0000236), W_ERROR(0x4c9)},
+ {NT_STATUS(0xc0000237), W_ERROR(0x4ca)},
+ {NT_STATUS(0xc0000238), W_ERROR(0x4cb)},
+ {NT_STATUS(0xc0000239), W_ERROR(0x4cc)},
+ {NT_STATUS(0xc000023a), W_ERROR(0x4cd)},
+ {NT_STATUS(0xc000023b), W_ERROR(0x4ce)},
+ {NT_STATUS(0xc000023c), W_ERROR(0x4cf)},
+ {NT_STATUS(0xc000023d), W_ERROR(0x4d0)},
+ {NT_STATUS(0xc000023e), W_ERROR(0x4d1)},
+ {NT_STATUS(0xc000023f), W_ERROR(0x4d2)},
+ {NT_STATUS(0xc0000240), W_ERROR(0x4d3)},
+ {NT_STATUS(0xc0000241), W_ERROR(0x4d4)},
+ {NT_STATUS(0xc0000243), W_ERROR(0x4c8)},
+ {NT_STATUS(0xc0000246), W_ERROR(0x4d6)},
+ {NT_STATUS(0xc0000247), W_ERROR(0x4d7)},
+ {NT_STATUS(0xc0000248), W_ERROR(0x4d8)},
+ {NT_STATUS(0xc0000249), W_ERROR(0xc1)},
+ {NT_STATUS(0xc0000253), W_ERROR(0x54f)},
+ {NT_STATUS(0xc0000257), W_ERROR(0x4d0)},
+ {NT_STATUS(0xc0000259), W_ERROR(0x573)},
+ {NT_STATUS(0xc000025e), W_ERROR(0x422)},
+ {NT_STATUS(0xc0000262), W_ERROR(0xb6)},
+ {NT_STATUS(0xc0000263), W_ERROR(0x7f)},
+ {NT_STATUS(0xc0000264), W_ERROR(0x120)},
+ {NT_STATUS(0xc0000265), W_ERROR(0x476)},
+ {NT_STATUS(0xc0000267), W_ERROR(0x10fe)},
+ {NT_STATUS(0xc000026c), W_ERROR(0x7d1)},
+ {NT_STATUS(0xc000026d), W_ERROR(0x4b1)},
+ {NT_STATUS(0xc000026e), W_ERROR(0x15)},
+ {NT_STATUS(0xc0000272), W_ERROR(0x491)},
+ {NT_STATUS(0xc0000275), W_ERROR(0x1126)},
+ {NT_STATUS(0xc0000276), W_ERROR(0x1129)},
+ {NT_STATUS(0xc0000277), W_ERROR(0x112a)},
+ {NT_STATUS(0xc0000278), W_ERROR(0x1128)},
+ {NT_STATUS(0xc0000279), W_ERROR(0x780)},
+ {NT_STATUS(0xc0000280), W_ERROR(0x781)},
+ {NT_STATUS(0xc0000281), W_ERROR(0xa1)},
+ {NT_STATUS(0xc0000283), W_ERROR(0x488)},
+ {NT_STATUS(0xc0000284), W_ERROR(0x489)},
+ {NT_STATUS(0xc0000285), W_ERROR(0x48a)},
+ {NT_STATUS(0xc0000286), W_ERROR(0x48b)},
+ {NT_STATUS(0xc0000287), W_ERROR(0x48c)},
+ {NT_STATUS(0xc000028a), W_ERROR(0x5)},
+ {NT_STATUS(0xc000028b), W_ERROR(0x5)},
+ {NT_STATUS(0xc000028d), W_ERROR(0x5)},
+ {NT_STATUS(0xc000028e), W_ERROR(0x5)},
+ {NT_STATUS(0xc000028f), W_ERROR(0x5)},
+ {NT_STATUS(0xc0000290), W_ERROR(0x5)},
+ {NT_STATUS(0xc0000291), W_ERROR(0x1777)},
+ {NT_STATUS(0xc0000292), W_ERROR(0x1778)},
+ {NT_STATUS(0xc0000293), W_ERROR(0x1772)},
+ {NT_STATUS(0xc0000295), W_ERROR(0x1068)},
+ {NT_STATUS(0xc0000296), W_ERROR(0x1069)},
+ {NT_STATUS(0xc0000297), W_ERROR(0x106a)},
+ {NT_STATUS(0xc0000298), W_ERROR(0x106b)},
+ {NT_STATUS(0xc0000299), W_ERROR(0x201a)},
+ {NT_STATUS(0xc000029a), W_ERROR(0x201b)},
+ {NT_STATUS(0xc000029b), W_ERROR(0x201c)},
+ {NT_STATUS(0xc000029c), W_ERROR(0x1)},
+ {NT_STATUS(0xc000029d), W_ERROR(0x10ff)},
+ {NT_STATUS(0xc000029e), W_ERROR(0x1100)},
+ {NT_STATUS(0xc000029f), W_ERROR(0x494)},
+ {NT_STATUS(0xc00002a1), W_ERROR(0x200a)},
+ {NT_STATUS(0xc00002a2), W_ERROR(0x200b)},
+ {NT_STATUS(0xc00002a3), W_ERROR(0x200c)},
+ {NT_STATUS(0xc00002a4), W_ERROR(0x200d)},
+ {NT_STATUS(0xc00002a5), W_ERROR(0x200e)},
+ {NT_STATUS(0xc00002a6), W_ERROR(0x200f)},
+ {NT_STATUS(0xc00002a7), W_ERROR(0x2010)},
+ {NT_STATUS(0xc00002a8), W_ERROR(0x2011)},
+ {NT_STATUS(0xc00002a9), W_ERROR(0x2012)},
+ {NT_STATUS(0xc00002aa), W_ERROR(0x2013)},
+ {NT_STATUS(0xc00002ab), W_ERROR(0x2014)},
+ {NT_STATUS(0xc00002ac), W_ERROR(0x2015)},
+ {NT_STATUS(0xc00002ad), W_ERROR(0x2016)},
+ {NT_STATUS(0xc00002ae), W_ERROR(0x2017)},
+ {NT_STATUS(0xc00002af), W_ERROR(0x2018)},
+ {NT_STATUS(0xc00002b0), W_ERROR(0x2019)},
+ {NT_STATUS(0xc00002b1), W_ERROR(0x211e)},
+ {NT_STATUS(0xc00002b2), W_ERROR(0x1127)},
+ {NT_STATUS(0xc00002b6), W_ERROR(0x651)},
+ {NT_STATUS(0xc00002b7), W_ERROR(0x49a)},
+ {NT_STATUS(0xc00002b8), W_ERROR(0x49b)},
+ {NT_STATUS(0xc00002c1), W_ERROR(0x2024)},
+ {NT_STATUS(0xc00002c3), W_ERROR(0x575)},
+ {NT_STATUS(0xc00002c5), W_ERROR(0x3e6)},
+ {NT_STATUS(0xc00002c6), W_ERROR(0x1075)},
+ {NT_STATUS(0xc00002c7), W_ERROR(0x1076)},
+ {NT_STATUS(0xc00002ca), W_ERROR(0x10e8)},
+ {NT_STATUS(0xc00002cb), W_ERROR(0x2138)},
+ {NT_STATUS(0xc00002cc), W_ERROR(0x4e3)},
+ {NT_STATUS(0xc00002cd), W_ERROR(0x2139)},
+ {NT_STATUS(0xc00002cf), W_ERROR(0x49d)},
+ {NT_STATUS(0xc00002d0), W_ERROR(0x213a)},
+ {NT_STATUS(0xc00002d4), W_ERROR(0x2141)},
+ {NT_STATUS(0xc00002d5), W_ERROR(0x2142)},
+ {NT_STATUS(0xc00002d6), W_ERROR(0x2143)},
+ {NT_STATUS(0xc00002d7), W_ERROR(0x2144)},
+ {NT_STATUS(0xc00002d8), W_ERROR(0x2145)},
+ {NT_STATUS(0xc00002d9), W_ERROR(0x2146)},
+ {NT_STATUS(0xc00002da), W_ERROR(0x2147)},
+ {NT_STATUS(0xc00002db), W_ERROR(0x2148)},
+ {NT_STATUS(0xc00002dc), W_ERROR(0x2149)},
+ {NT_STATUS(0xc00002dd), W_ERROR(0x32)},
+ {NT_STATUS(0xc00002df), W_ERROR(0x2151)},
+ {NT_STATUS(0xc00002e0), W_ERROR(0x2152)},
+ {NT_STATUS(0xc00002e1), W_ERROR(0x2153)},
+ {NT_STATUS(0xc00002e2), W_ERROR(0x2154)},
+ {NT_STATUS(0xc00002e3), W_ERROR(0x215d)},
+ {NT_STATUS(0xc00002e4), W_ERROR(0x2163)},
+ {NT_STATUS(0xc00002e5), W_ERROR(0x2164)},
+ {NT_STATUS(0xc00002e6), W_ERROR(0x2165)},
+ {NT_STATUS(0xc00002e7), W_ERROR(0x216d)},
+ {NT_STATUS(0xc00002fe), W_ERROR(0x45b)},
+ {NT_STATUS(0xc00002ff), W_ERROR(0x4e7)},
+ {NT_STATUS(0xc0000300), W_ERROR(0x4e6)},
+ {NT_STATUS(0x80000001), W_ERROR(0x80000001)},
+ {NT_STATUS(0x80000002), W_ERROR(0x3e6)},
+ {NT_STATUS(0x80000003), W_ERROR(0x80000003)},
+ {NT_STATUS(0x80000004), W_ERROR(0x80000004)},
+ {NT_STATUS(0x80000005), W_ERROR(0xea)},
+ {NT_STATUS(0x80000006), W_ERROR(0x12)},
+ {NT_STATUS(0x8000000b), W_ERROR(0x56f)},
+ {NT_STATUS(0x8000000d), W_ERROR(0x12b)},
+ {NT_STATUS(0x8000000e), W_ERROR(0x1c)},
+ {NT_STATUS(0x8000000f), W_ERROR(0x15)},
+ {NT_STATUS(0x80000010), W_ERROR(0x15)},
+ {NT_STATUS(0x80000011), W_ERROR(0xaa)},
+ {NT_STATUS(0x80000012), W_ERROR(0x103)},
+ {NT_STATUS(0x80000013), W_ERROR(0xfe)},
+ {NT_STATUS(0x80000014), W_ERROR(0xff)},
+ {NT_STATUS(0x80000015), W_ERROR(0xff)},
+ {NT_STATUS(0x80000016), W_ERROR(0x456)},
+ {NT_STATUS(0x8000001a), W_ERROR(0x103)},
+ {NT_STATUS(0x8000001b), W_ERROR(0x44d)},
+ {NT_STATUS(0x8000001c), W_ERROR(0x456)},
+ {NT_STATUS(0x8000001d), W_ERROR(0x457)},
+ {NT_STATUS(0x8000001e), W_ERROR(0x44c)},
+ {NT_STATUS(0x8000001f), W_ERROR(0x44e)},
+ {NT_STATUS(0x80000021), W_ERROR(0x44f)},
+ {NT_STATUS(0x80000022), W_ERROR(0x450)},
+ {NT_STATUS(0x80000025), W_ERROR(0x962)},
+ {NT_STATUS(0x80000288), W_ERROR(0x48d)},
+ {NT_STATUS(0x80000289), W_ERROR(0x48e)},
+ {NT_STATUS_OK, WERR_OK}};
+
+
+/*****************************************************************************
+convert a dos eclas/ecode to a NT status32 code
+ *****************************************************************************/
+NTSTATUS dos_to_ntstatus(uint8 eclass, uint32 ecode)
+{
+ int i;
+ if (eclass == 0 && ecode == 0) return NT_STATUS_OK;
+ for (i=0; NT_STATUS_V(dos_to_ntstatus_map[i].ntstatus); i++) {
+ if (eclass == dos_to_ntstatus_map[i].dos_class &&
+ ecode == dos_to_ntstatus_map[i].dos_code) {
+ return dos_to_ntstatus_map[i].ntstatus;
+ }
+ }
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+
+/*****************************************************************************
+convert a NT status code to a dos class/code
+ *****************************************************************************/
+void ntstatus_to_dos(NTSTATUS ntstatus, uint8 *eclass, uint32 *ecode)
+{
+ int i;
+ if (NT_STATUS_IS_OK(ntstatus)) {
+ *eclass = 0;
+ *ecode = 0;
+ return;
+ }
+ for (i=0; NT_STATUS_V(ntstatus_to_dos_map[i].ntstatus); i++) {
+ if (NT_STATUS_V(ntstatus) ==
+ NT_STATUS_V(ntstatus_to_dos_map[i].ntstatus)) {
+ *eclass = ntstatus_to_dos_map[i].dos_class;
+ *ecode = ntstatus_to_dos_map[i].dos_code;
+ return;
+ }
+ }
+ *eclass = ERRHRD;
+ *ecode = ERRgeneral;
+}
+
+
+/*****************************************************************************
+convert a WERROR to a NT status32 code
+ *****************************************************************************/
+NTSTATUS werror_to_ntstatus(WERROR error)
+{
+ int i;
+ if (W_ERROR_IS_OK(error)) return NT_STATUS_OK;
+ for (i=0; NT_STATUS_V(ntstatus_to_werror_map[i].ntstatus); i++) {
+ if (W_ERROR_V(error) ==
+ W_ERROR_V(ntstatus_to_werror_map[i].werror)) {
+ return ntstatus_to_werror_map[i].ntstatus;
+ }
+ }
+
+ /* just guess ... */
+ return NT_STATUS(W_ERROR_V(error) | 0xc0000000);
+}
+
+/*****************************************************************************
+convert a NTSTATUS to a WERROR
+ *****************************************************************************/
+WERROR ntstatus_to_werror(NTSTATUS error)
+{
+ int i;
+ if (NT_STATUS_IS_OK(error)) return WERR_OK;
+ for (i=0; NT_STATUS_V(ntstatus_to_werror_map[i].ntstatus); i++) {
+ if (NT_STATUS_V(error) ==
+ NT_STATUS_V(ntstatus_to_werror_map[i].ntstatus)) {
+ return ntstatus_to_werror_map[i].werror;
+ }
+ }
+
+ /* a lame guess */
+ return W_ERROR(NT_STATUS_V(error) & 0xffff);
+}
+
+/* Mapping between Unix, DOS and NT error numbers */
+
+const struct unix_error_map unix_dos_nt_errmap[] = {
+ { EPERM, ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED },
+ { EACCES, ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED },
+ { ENOENT, ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_NOT_FOUND },
+ { ENOTDIR, ERRDOS, ERRbadpath, NT_STATUS_NOT_A_DIRECTORY },
+ { EIO, ERRHRD, ERRgeneral, NT_STATUS_IO_DEVICE_ERROR },
+ { EBADF, ERRSRV, ERRsrverror, NT_STATUS_INVALID_HANDLE },
+ { EINVAL, ERRSRV, ERRsrverror, NT_STATUS_INVALID_HANDLE },
+ { EEXIST, ERRDOS, ERRfilexists, NT_STATUS_OBJECT_NAME_COLLISION},
+ { ENFILE, ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES },
+ { EMFILE, ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES },
+ { ENOSPC, ERRHRD, ERRdiskfull, NT_STATUS_DISK_FULL },
+ { ENOMEM, ERRDOS, ERRnomem, NT_STATUS_NO_MEMORY },
+ { EISDIR, ERRDOS, ERRnoaccess, NT_STATUS_FILE_IS_A_DIRECTORY},
+#ifdef EDQUOT
+ { EDQUOT, ERRHRD, ERRdiskfull, NT_STATUS_DISK_FULL },
+#endif
+#ifdef ENOTEMPTY
+ { ENOTEMPTY, ERRDOS, ERRnoaccess, NT_STATUS_DIRECTORY_NOT_EMPTY },
+#endif
+#ifdef EXDEV
+ { EXDEV, ERRDOS, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE },
+#endif
+#ifdef EROFS
+ { EROFS, ERRHRD, ERRnowrite, NT_STATUS_ACCESS_DENIED },
+#endif
+#ifdef ENAMETOOLONG
+ { ENAMETOOLONG, ERRDOS, 206, NT_STATUS_OBJECT_NAME_INVALID },
+#endif
+#ifdef EFBIG
+ { EFBIG, ERRHRD, ERRdiskfull, NT_STATUS_DISK_FULL },
+#endif
+ { 0, 0, 0, NT_STATUS_OK }
+};
+
+/*********************************************************************
+ Map an NT error code from a Unix error code.
+*********************************************************************/
+
+NTSTATUS map_nt_error_from_unix(int unix_error)
+{
+ int i = 0;
+
+ if (unix_error == 0)
+ return NT_STATUS_OK;
+
+ /* Look through list */
+ while(unix_dos_nt_errmap[i].unix_error != 0) {
+ if (unix_dos_nt_errmap[i].unix_error == unix_error)
+ return unix_dos_nt_errmap[i].nt_error;
+ i++;
+ }
+
+ /* Default return */
+ return NT_STATUS_ACCESS_DENIED;
+}
diff --git a/source/libsmb/libsmb_cache.c b/source/libsmb/libsmb_cache.c
new file mode 100644
index 00000000000..cb40b4aaa6b
--- /dev/null
+++ b/source/libsmb/libsmb_cache.c
@@ -0,0 +1,196 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client library implementation (server cache)
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*
+ * Define this to get the real SMBCFILE and SMBCSRV structures
+ */
+#define _SMBC_INTERNAL
+#include "../include/libsmbclient.h"
+
+/*
+ * Structure we use if internal caching mechanism is used
+ * nothing fancy here.
+ */
+struct smbc_server_cache {
+ char *server_name;
+ char *share_name;
+ char *workgroup;
+ char *username;
+ SMBCSRV *server;
+
+ struct smbc_server_cache *next, *prev;
+};
+
+
+
+/*
+ * Add a new connection to the server cache.
+ * This function is only used if the external cache is not enabled
+ */
+static int smbc_add_cached_server(SMBCCTX * context, SMBCSRV * new,
+ const char * server, const char * share,
+ const char * workgroup, const char * username)
+{
+ struct smbc_server_cache * srvcache = NULL;
+
+ if (!(srvcache = malloc(sizeof(*srvcache)))) {
+ errno = ENOMEM;
+ DEBUG(3, ("Not enough space for server cache allocation\n"));
+ return 1;
+ }
+
+ ZERO_STRUCTP(srvcache);
+
+ srvcache->server = new;
+
+ srvcache->server_name = strdup(server);
+ if (!srvcache->server_name) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ srvcache->share_name = strdup(share);
+ if (!srvcache->share_name) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ srvcache->workgroup = strdup(workgroup);
+ if (!srvcache->workgroup) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ srvcache->username = strdup(username);
+ if (!srvcache->username) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ DLIST_ADD((context->server_cache), srvcache);
+ return 0;
+
+ failed:
+ SAFE_FREE(srvcache->server_name);
+ SAFE_FREE(srvcache->share_name);
+ SAFE_FREE(srvcache->workgroup);
+ SAFE_FREE(srvcache->username);
+
+ return 1;
+}
+
+
+
+/*
+ * Search the server cache for a server
+ * returns server_fd on success, -1 on error (not found)
+ * This function is only used if the external cache is not enabled
+ */
+static SMBCSRV * smbc_get_cached_server(SMBCCTX * context, const char * server,
+ const char * share, const char * workgroup, const char * user)
+{
+ struct smbc_server_cache * srv = NULL;
+
+ /* Search the cache lines */
+ for (srv=((struct smbc_server_cache *)context->server_cache);srv;srv=srv->next) {
+ if (strcmp(server,srv->server_name) == 0 &&
+ strcmp(share,srv->share_name) == 0 &&
+ strcmp(workgroup,srv->workgroup) == 0 &&
+ strcmp(user, srv->username) == 0)
+ return srv->server;
+ }
+
+ return NULL;
+}
+
+
+/*
+ * Search the server cache for a server and remove it
+ * returns 0 on success
+ * This function is only used if the external cache is not enabled
+ */
+static int smbc_remove_cached_server(SMBCCTX * context, SMBCSRV * server)
+{
+ struct smbc_server_cache * srv = NULL;
+
+ for (srv=((struct smbc_server_cache *)context->server_cache);srv;srv=srv->next) {
+ if (server == srv->server) {
+
+ /* remove this sucker */
+ DLIST_REMOVE(context->server_cache, srv);
+ SAFE_FREE(srv->server_name);
+ SAFE_FREE(srv->share_name);
+ SAFE_FREE(srv->workgroup);
+ SAFE_FREE(srv->username);
+ SAFE_FREE(srv);
+ return 0;
+ }
+ }
+ /* server not found */
+ return 1;
+}
+
+
+/*
+ * Try to remove all the servers in cache
+ * returns 1 on failure and 0 if all servers could be removed.
+ */
+static int smbc_purge_cached(SMBCCTX * context)
+{
+ struct smbc_server_cache * srv;
+ struct smbc_server_cache * next;
+ int could_not_purge_all = 0;
+
+ for (srv = ((struct smbc_server_cache *) context->server_cache),
+ next = (srv ? srv->next :NULL);
+ srv;
+ srv = next, next = (srv ? srv->next : NULL)) {
+
+ if (smbc_remove_unused_server(context, srv->server)) {
+ /* could not be removed */
+ could_not_purge_all = 1;
+ }
+ }
+ return could_not_purge_all;
+}
+
+
+
+/*
+ * This functions initializes all server-cache related functions
+ * to the default (internal) system.
+ *
+ * We use this to make the rest of the cache system static.
+ */
+
+int smbc_default_cache_functions(SMBCCTX * context)
+{
+ context->callbacks.add_cached_srv_fn = smbc_add_cached_server;
+ context->callbacks.get_cached_srv_fn = smbc_get_cached_server;
+ context->callbacks.remove_cached_srv_fn = smbc_remove_cached_server;
+ context->callbacks.purge_cached_fn = smbc_purge_cached;
+
+ return 0;
+}
diff --git a/source/libsmb/libsmb_compat.c b/source/libsmb/libsmb_compat.c
new file mode 100644
index 00000000000..cc23835ae3d
--- /dev/null
+++ b/source/libsmb/libsmb_compat.c
@@ -0,0 +1,434 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client library implementation (Old interface compatibility)
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+#include "includes.h"
+
+#include "../include/libsmb_internal.h"
+
+struct smbc_compat_fdlist {
+ SMBCFILE * file;
+ int fd;
+ struct smbc_compat_fdlist *next, *prev;
+};
+
+static SMBCCTX * statcont = NULL;
+static int smbc_compat_initialized = 0;
+static int smbc_compat_nextfd = 0;
+static struct smbc_compat_fdlist * smbc_compat_fd_in_use = NULL;
+static struct smbc_compat_fdlist * smbc_compat_fd_avail = NULL;
+
+/* Find an fd and return the SMBCFILE * or NULL on failure */
+static SMBCFILE * find_fd(int fd)
+{
+ struct smbc_compat_fdlist * f = smbc_compat_fd_in_use;
+ while (f) {
+ if (f->fd == fd)
+ return f->file;
+ f = f->next;
+ }
+ return NULL;
+}
+
+/* Add an fd, returns 0 on success, -1 on error with errno set */
+static int add_fd(SMBCFILE * file)
+{
+ struct smbc_compat_fdlist * f = smbc_compat_fd_avail;
+
+ if (f) {
+ /* We found one that's available */
+ DLIST_REMOVE(smbc_compat_fd_avail, f);
+
+ } else {
+ /*
+ * None were available, so allocate one. Keep the number of
+ * file descriptors determinate. This allows the application
+ * to allocate bitmaps or mapping of file descriptors based on
+ * a known maximum number of file descriptors that will ever
+ * be returned.
+ */
+ if (smbc_compat_nextfd >= FD_SETSIZE) {
+ errno = EMFILE;
+ return -1;
+ }
+
+ f = malloc(sizeof(struct smbc_compat_fdlist));
+ if (!f) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ f->fd = SMBC_BASE_FD + smbc_compat_nextfd++;
+ }
+
+ f->file = file;
+ DLIST_ADD(smbc_compat_fd_in_use, f);
+
+ return f->fd;
+}
+
+
+
+/* Delete an fd, returns 0 on success */
+static int del_fd(int fd)
+{
+ struct smbc_compat_fdlist * f = smbc_compat_fd_in_use;
+
+ while (f) {
+ if (f->fd == fd)
+ break;
+ f = f->next;
+ }
+
+ if (f) {
+ /* found */
+ DLIST_REMOVE(smbc_compat_fd_in_use, f);
+ f->file = NULL;
+ DLIST_ADD(smbc_compat_fd_avail, f);
+ return 0;
+ }
+ return 1;
+}
+
+
+
+int smbc_init(smbc_get_auth_data_fn fn, int debug)
+{
+ if (!smbc_compat_initialized) {
+ statcont = smbc_new_context();
+ if (!statcont)
+ return -1;
+
+ statcont->debug = debug;
+ statcont->callbacks.auth_fn = fn;
+
+ if (!smbc_init_context(statcont)) {
+ smbc_free_context(statcont, False);
+ return -1;
+ }
+
+ smbc_compat_initialized = 1;
+
+ return 0;
+ }
+ return 0;
+}
+
+
+SMBCCTX *smbc_set_context(SMBCCTX * context)
+{
+ SMBCCTX *old_context = statcont;
+
+ if (context) {
+ /* Save provided context. It must have been initialized! */
+ statcont = context;
+
+ /* You'd better know what you're doing. We won't help you. */
+ smbc_compat_initialized = 1;
+ }
+
+ return old_context;
+}
+
+
+int smbc_open(const char *furl, int flags, mode_t mode)
+{
+ SMBCFILE * file;
+ int fd;
+
+ file = statcont->open(statcont, furl, flags, mode);
+ if (!file)
+ return -1;
+
+ fd = add_fd(file);
+ if (fd == -1)
+ statcont->close(statcont, file);
+ return fd;
+}
+
+
+int smbc_creat(const char *furl, mode_t mode)
+{
+ SMBCFILE * file;
+ int fd;
+
+ file = statcont->creat(statcont, furl, mode);
+ if (!file)
+ return -1;
+
+ fd = add_fd(file);
+ if (fd == -1) {
+ /* Hmm... should we delete the file too ? I guess we could try */
+ statcont->close(statcont, file);
+ statcont->unlink(statcont, furl);
+ }
+ return fd;
+}
+
+
+ssize_t smbc_read(int fd, void *buf, size_t bufsize)
+{
+ SMBCFILE * file = find_fd(fd);
+ return statcont->read(statcont, file, buf, bufsize);
+}
+
+ssize_t smbc_write(int fd, void *buf, size_t bufsize)
+{
+ SMBCFILE * file = find_fd(fd);
+ return statcont->write(statcont, file, buf, bufsize);
+}
+
+off_t smbc_lseek(int fd, off_t offset, int whence)
+{
+ SMBCFILE * file = find_fd(fd);
+ return statcont->lseek(statcont, file, offset, whence);
+}
+
+int smbc_close(int fd)
+{
+ SMBCFILE * file = find_fd(fd);
+ del_fd(fd);
+ return statcont->close(statcont, file);
+}
+
+int smbc_unlink(const char *fname)
+{
+ return statcont->unlink(statcont, fname);
+}
+
+int smbc_rename(const char *ourl, const char *nurl)
+{
+ return statcont->rename(statcont, ourl, statcont, nurl);
+}
+
+int smbc_opendir(const char *durl)
+{
+ SMBCFILE * file;
+ int fd;
+
+ file = statcont->opendir(statcont, durl);
+ if (!file)
+ return -1;
+
+ fd = add_fd(file);
+ if (fd == -1)
+ statcont->closedir(statcont, file);
+
+ return fd;
+}
+
+int smbc_closedir(int dh)
+{
+ SMBCFILE * file = find_fd(dh);
+ del_fd(dh);
+ return statcont->closedir(statcont, file);
+}
+
+int smbc_getdents(unsigned int dh, struct smbc_dirent *dirp, int count)
+{
+ SMBCFILE * file = find_fd(dh);
+ return statcont->getdents(statcont, file,dirp, count);
+}
+
+struct smbc_dirent* smbc_readdir(unsigned int dh)
+{
+ SMBCFILE * file = find_fd(dh);
+ return statcont->readdir(statcont, file);
+}
+
+off_t smbc_telldir(int dh)
+{
+ SMBCFILE * file = find_fd(dh);
+ return statcont->telldir(statcont, file);
+}
+
+int smbc_lseekdir(int fd, off_t offset)
+{
+ SMBCFILE * file = find_fd(fd);
+ return statcont->lseekdir(statcont, file, offset);
+}
+
+int smbc_mkdir(const char *durl, mode_t mode)
+{
+ return statcont->mkdir(statcont, durl, mode);
+}
+
+int smbc_rmdir(const char *durl)
+{
+ return statcont->rmdir(statcont, durl);
+}
+
+int smbc_stat(const char *url, struct stat *st)
+{
+ return statcont->stat(statcont, url, st);
+}
+
+int smbc_fstat(int fd, struct stat *st)
+{
+ SMBCFILE * file = find_fd(fd);
+ return statcont->fstat(statcont, file, st);
+}
+
+int smbc_chmod(const char *url, mode_t mode)
+{
+ return statcont->chmod(statcont, url, mode);
+}
+
+int smbc_utimes(const char *fname, struct timeval *tbuf)
+{
+ return statcont->utimes(statcont, fname, tbuf);
+}
+
+#ifdef HAVE_UTIME_H
+int smbc_utime(const char *fname, struct utimbuf *utbuf)
+{
+ struct timeval tv;
+
+ if (utbuf == NULL)
+ return statcont->utimes(statcont, fname, NULL);
+
+ tv.tv_sec = utbuf->modtime;
+ tv.tv_usec = 0;
+ return statcont->utimes(statcont, fname, &tv);
+}
+#endif
+
+int smbc_setxattr(const char *fname,
+ const char *name,
+ const void *value,
+ size_t size,
+ int flags)
+{
+ return statcont->setxattr(statcont, fname, name, value, size, flags);
+}
+
+int smbc_lsetxattr(const char *fname,
+ const char *name,
+ const void *value,
+ size_t size,
+ int flags)
+{
+ return statcont->setxattr(statcont, fname, name, value, size, flags);
+}
+
+int smbc_fsetxattr(int fd,
+ const char *name,
+ const void *value,
+ size_t size,
+ int flags)
+{
+ SMBCFILE * file = find_fd(fd);
+ return statcont->setxattr(statcont, file->fname,
+ name, value, size, flags);
+}
+
+int smbc_getxattr(const char *fname,
+ const char *name,
+ const void *value,
+ size_t size)
+{
+ return statcont->getxattr(statcont, fname, name, value, size);
+}
+
+int smbc_lgetxattr(const char *fname,
+ const char *name,
+ const void *value,
+ size_t size)
+{
+ return statcont->getxattr(statcont, fname, name, value, size);
+}
+
+int smbc_fgetxattr(int fd,
+ const char *name,
+ const void *value,
+ size_t size)
+{
+ SMBCFILE * file = find_fd(fd);
+ return statcont->getxattr(statcont, file->fname, name, value, size);
+}
+
+int smbc_removexattr(const char *fname,
+ const char *name)
+{
+ return statcont->removexattr(statcont, fname, name);
+}
+
+int smbc_lremovexattr(const char *fname,
+ const char *name)
+{
+ return statcont->removexattr(statcont, fname, name);
+}
+
+int smbc_fremovexattr(int fd,
+ const char *name)
+{
+ SMBCFILE * file = find_fd(fd);
+ return statcont->removexattr(statcont, file->fname, name);
+}
+
+int smbc_listxattr(const char *fname,
+ char *list,
+ size_t size)
+{
+ return statcont->listxattr(statcont, fname, list, size);
+}
+
+int smbc_llistxattr(const char *fname,
+ char *list,
+ size_t size)
+{
+ return statcont->listxattr(statcont, fname, list, size);
+}
+
+int smbc_flistxattr(int fd,
+ char *list,
+ size_t size)
+{
+ SMBCFILE * file = find_fd(fd);
+ return statcont->listxattr(statcont, file->fname, list, size);
+}
+
+int smbc_print_file(const char *fname, const char *printq)
+{
+ return statcont->print_file(statcont, fname, statcont, printq);
+}
+
+int smbc_open_print_job(const char *fname)
+{
+ SMBCFILE * file = statcont->open_print_job(statcont, fname);
+ if (!file) return -1;
+ return (int) file;
+}
+
+int smbc_list_print_jobs(const char *purl, smbc_list_print_job_fn fn)
+{
+ return statcont->list_print_jobs(statcont, purl, fn);
+}
+
+int smbc_unlink_print_job(const char *purl, int id)
+{
+ return statcont->unlink_print_job(statcont, purl, id);
+}
+
+
diff --git a/source/libsmb/libsmbclient.c b/source/libsmb/libsmbclient.c
new file mode 100644
index 00000000000..68bb6661ebd
--- /dev/null
+++ b/source/libsmb/libsmbclient.c
@@ -0,0 +1,4534 @@
+/*
+ Unix SMB/Netbios implementation.
+ SMB client library implementation
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000, 2002
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#include "../include/libsmb_internal.h"
+
+/*
+ * Internal flags for extended attributes
+ */
+
+/* internal mode values */
+#define SMBC_XATTR_MODE_ADD 1
+#define SMBC_XATTR_MODE_REMOVE 2
+#define SMBC_XATTR_MODE_REMOVE_ALL 3
+#define SMBC_XATTR_MODE_SET 4
+#define SMBC_XATTR_MODE_CHOWN 5
+#define SMBC_XATTR_MODE_CHGRP 6
+
+#define CREATE_ACCESS_READ READ_CONTROL_ACCESS
+
+/*We should test for this in configure ... */
+#ifndef ENOTSUP
+#define ENOTSUP EOPNOTSUPP
+#endif
+
+/*
+ * Functions exported by libsmb_cache.c that we need here
+ */
+int smbc_default_cache_functions(SMBCCTX *context);
+
+/*
+ * check if an element is part of the list.
+ * FIXME: Does not belong here !
+ * Can anyone put this in a macro in dlinklist.h ?
+ * -- Tom
+ */
+static int DLIST_CONTAINS(SMBCFILE * list, SMBCFILE *p) {
+ if (!p || !list) return False;
+ do {
+ if (p == list) return True;
+ list = list->next;
+ } while (list);
+ return False;
+}
+
+extern BOOL in_client;
+
+/*
+ * Is the logging working / configfile read ?
+ */
+static int smbc_initialized = 0;
+
+static int
+hex2int( unsigned int _char )
+{
+ if ( _char >= 'A' && _char <='F')
+ return _char - 'A' + 10;
+ if ( _char >= 'a' && _char <='f')
+ return _char - 'a' + 10;
+ if ( _char >= '0' && _char <='9')
+ return _char - '0';
+ return -1;
+}
+
+static void
+decode_urlpart(char *segment, size_t sizeof_segment)
+{
+ int old_length = strlen(segment);
+ int new_length = 0;
+ int new_length2 = 0;
+ int i = 0;
+ pstring new_segment;
+ char *new_usegment = 0;
+
+ if ( !old_length ) {
+ return;
+ }
+
+ /* make a copy of the old one */
+ new_usegment = (char*)malloc( old_length * 3 + 1 );
+
+ while( i < old_length ) {
+ int bReencode = False;
+ unsigned char character = segment[ i++ ];
+ if ((character <= ' ') || (character > 127))
+ bReencode = True;
+
+ new_usegment [ new_length2++ ] = character;
+ if (character == '%' ) {
+ int a = i+1 < old_length ? hex2int( segment[i] ) : -1;
+ int b = i+1 < old_length ? hex2int( segment[i+1] ) : -1;
+ if ((a == -1) || (b == -1)) { /* Only replace if sequence is valid */
+ /* Contains stray %, make sure to re-encode! */
+ bReencode = True;
+ } else {
+ /* Valid %xx sequence */
+ character = a * 16 + b; /* Replace with value of %dd */
+ if (!character)
+ break; /* Stop at %00 */
+
+ new_usegment [ new_length2++ ] = (unsigned char) segment[i++];
+ new_usegment [ new_length2++ ] = (unsigned char) segment[i++];
+ }
+ }
+ if (bReencode) {
+ unsigned int c = character / 16;
+ new_length2--;
+ new_usegment [ new_length2++ ] = '%';
+
+ c += (c > 9) ? ('A' - 10) : '0';
+ new_usegment[ new_length2++ ] = c;
+
+ c = character % 16;
+ c += (c > 9) ? ('A' - 10) : '0';
+ new_usegment[ new_length2++ ] = c;
+ }
+
+ new_segment [ new_length++ ] = character;
+ }
+ new_segment [ new_length ] = 0;
+
+ free(new_usegment);
+
+ /* realloc it with unix charset */
+ pull_utf8_allocate(&new_usegment, new_segment);
+
+ /* this assumes (very safely) that removing %aa sequences
+ only shortens the string */
+ strncpy(segment, new_usegment, sizeof_segment);
+
+ free(new_usegment);
+}
+
+/*
+ * Function to parse a path and turn it into components
+ *
+ * The general format of an SMB URI is explain in Christopher Hertel's CIFS
+ * book, at http://ubiqx.org/cifs/Appendix-D.html. We accept a subset of the
+ * general format ("smb:" only; we do not look for "cifs:"), and expand on
+ * what he calls "context", herein called "options" to avoid conflict with the
+ * SMBCCTX context used throughout this library. We add the "mb" keyword
+ * which applies as follows:
+ *
+ *
+ * We accept:
+ * smb://[[[domain;]user[:password@]]server[/share[/path[/file]]]][?options]
+ *
+ * Meaning of URLs:
+ *
+ * smb:// show all workgroups known by the first master browser found
+ * smb://?mb=.any same as smb:// (i.e. without any options)
+ *
+ * smb://?mb=.all show all workgroups known by every master browser found.
+ * Why might you want this? In an "appliance" application
+ * where the workgroup/domain being used on the local network
+ * is not known ahead of time, but where one wanted to
+ * provide network services via samba, a unique workgroup
+ * could be used. However, when the appliance is first
+ * started, the local samba instance's master browser has not
+ * synchronized with the other master browser(s) on the
+ * network (and might not synchronize for 12 minutes) and
+ * therefore is not aware of the workgroup/ domain names
+ * available on the network. This option may be used to
+ * overcome the problem of a libsmbclient application
+ * arbitrarily selecting the local (still ignorant) master
+ * browser to obtain its list of workgroups/domains and
+ * getting back a practically emmpty list. By requesting
+ * the list of workgroups/domains from each found master
+ * browser on the local network, a complete list of
+ * workgroups/domains can be built.
+ *
+ * smb://?mb=name NOT YET IMPLEMENTED -- show all workgroups known by the
+ * master browser whose name is "name"
+ *
+ * smb://name/ if name<1D> or name<1B> exists, list servers in
+ * workgroup, else, if name<20> exists, list all shares
+ * for server ...
+ *
+ * If "options" are provided, this function returns the entire option list as
+ * a string, for later parsing by the caller.
+ */
+
+static const char *smbc_prefix = "smb:";
+
+static int
+smbc_parse_path(SMBCCTX *context,
+ const char *fname,
+ char *server, int server_len,
+ char *share, int share_len,
+ char *path, int path_len,
+ char *user, int user_len,
+ char *password, int password_len,
+ char *options, int options_len)
+{
+ static pstring s;
+ pstring userinfo;
+ const char *p;
+ char *q, *r;
+ int len;
+
+ server[0] = share[0] = path[0] = user[0] = password[0] = (char)0;
+ if (options != NULL && options_len > 0) {
+ options[0] = (char)0;
+ }
+ pstrcpy(s, fname);
+
+ /* see if it has the right prefix */
+ len = strlen(smbc_prefix);
+ if (strncmp(s,smbc_prefix,len) || (s[len] != '/' && s[len] != 0)) {
+ return -1; /* What about no smb: ? */
+ }
+
+ p = s + len;
+
+ /* Watch the test below, we are testing to see if we should exit */
+
+ if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) {
+
+ DEBUG(1, ("Invalid path (does not begin with smb://"));
+ return -1;
+
+ }
+
+ p += 2; /* Skip the double slash */
+
+ /* See if any options were specified */
+ if (q = strrchr(p, '?')) {
+ /* There are options. Null terminate here and point to them */
+ *q++ = '\0';
+
+ DEBUG(4, ("Found options '%s'", q));
+
+ /* Copy the options */
+ if (options != NULL && options_len > 0) {
+ safe_strcpy(options, q, options_len - 1);
+ }
+ }
+
+ if (*p == (char)0)
+ goto decoding;
+
+ if (*p == '/') {
+
+ strncpy(server, context->workgroup,
+ (strlen(context->workgroup) < 16)?strlen(context->workgroup):16);
+ return 0;
+
+ }
+
+ /*
+ * ok, its for us. Now parse out the server, share etc.
+ *
+ * However, we want to parse out [[domain;]user[:password]@] if it
+ * exists ...
+ */
+
+ /* check that '@' occurs before '/', if '/' exists at all */
+ q = strchr_m(p, '@');
+ r = strchr_m(p, '/');
+ if (q && (!r || q < r)) {
+ pstring username, passwd, domain;
+ const char *u = userinfo;
+
+ next_token(&p, userinfo, "@", sizeof(fstring));
+
+ username[0] = passwd[0] = domain[0] = 0;
+
+ if (strchr_m(u, ';')) {
+
+ next_token(&u, domain, ";", sizeof(fstring));
+
+ }
+
+ if (strchr_m(u, ':')) {
+
+ next_token(&u, username, ":", sizeof(fstring));
+
+ pstrcpy(passwd, u);
+
+ }
+ else {
+
+ pstrcpy(username, u);
+
+ }
+
+ if (username[0])
+ strncpy(user, username, user_len); /* FIXME, domain */
+
+ if (passwd[0])
+ strncpy(password, passwd, password_len);
+
+ }
+
+ if (!next_token(&p, server, "/", sizeof(fstring))) {
+
+ return -1;
+
+ }
+
+ if (*p == (char)0) goto decoding; /* That's it ... */
+
+ if (!next_token(&p, share, "/", sizeof(fstring))) {
+
+ return -1;
+
+ }
+
+ safe_strcpy(path, p, path_len - 1);
+
+ all_string_sub(path, "/", "\\", 0);
+
+ decoding:
+ decode_urlpart(path, path_len);
+ decode_urlpart(server, server_len);
+ decode_urlpart(share, share_len);
+ decode_urlpart(user, user_len);
+ decode_urlpart(password, password_len);
+
+ return 0;
+}
+
+/*
+ * Verify that the options specified in a URL are valid
+ */
+static int smbc_check_options(char *server, char *share, char *path, char *options)
+{
+ DEBUG(4, ("smbc_check_options(): server='%s' share='%s' path='%s' options='%s'\n", server, share, path, options));
+
+ /* No options at all is always ok */
+ if (! *options) return 0;
+
+ /*
+ * For right now, we only support a very few options possibilities.
+ * No options are supported if server, share, or path are not empty.
+ * If all are empty, then we support the following two choices right
+ * now:
+ *
+ * mb=.any
+ * mb=.all
+ */
+ if ((*server || *share || *path) && *options) {
+ /* Invalid: options provided with server, share, or path */
+ DEBUG(1, ("Found unsupported options (%s) with non-empty server, share, or path\n", options));
+ return -1;
+ }
+
+ if (strcmp(options, "mb=.any") != 0 &&
+ strcmp(options, "mb=.all") != 0) {
+ DEBUG(1, ("Found unsupported options (%s)\n", options));
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Convert an SMB error into a UNIX error ...
+ */
+static int smbc_errno(SMBCCTX *context, struct cli_state *c)
+{
+ int ret = cli_errno(c);
+
+ if (cli_is_dos_error(c)) {
+ uint8 eclass;
+ uint32 ecode;
+
+ cli_dos_error(c, &eclass, &ecode);
+
+ DEBUG(3,("smbc_error %d %d (0x%x) -> %d\n",
+ (int)eclass, (int)ecode, (int)ecode, ret));
+ } else {
+ NTSTATUS status;
+
+ status = cli_nt_error(c);
+
+ DEBUG(3,("smbc errno %s -> %d\n",
+ nt_errstr(status), ret));
+ }
+
+ return ret;
+}
+
+/*
+ * Check a server_fd.
+ * returns 0 if the server is in shape. Returns 1 on error
+ *
+ * Also useable outside libsmbclient to enable external cache
+ * to do some checks too.
+ */
+int smbc_check_server(SMBCCTX * context, SMBCSRV * server)
+{
+ if ( send_keepalive(server->cli.fd) == False )
+ return 1;
+
+ /* connection is ok */
+ return 0;
+}
+
+/*
+ * Remove a server from the cached server list it's unused.
+ * On success, 0 is returned. 1 is returned if the server could not be removed.
+ *
+ * Also useable outside libsmbclient
+ */
+int smbc_remove_unused_server(SMBCCTX * context, SMBCSRV * srv)
+{
+ SMBCFILE * file;
+
+ /* are we being fooled ? */
+ if (!context || !context->internal ||
+ !context->internal->_initialized || !srv) return 1;
+
+
+ /* Check all open files/directories for a relation with this server */
+ for (file = context->internal->_files; file; file=file->next) {
+ if (file->srv == srv) {
+ /* Still used */
+ DEBUG(3, ("smbc_remove_usused_server: %p still used by %p.\n",
+ srv, file));
+ return 1;
+ }
+ }
+
+ DLIST_REMOVE(context->internal->_servers, srv);
+
+ cli_shutdown(&srv->cli);
+
+ DEBUG(3, ("smbc_remove_usused_server: %p removed.\n", srv));
+
+ context->callbacks.remove_cached_srv_fn(context, srv);
+
+ SAFE_FREE(srv);
+
+ return 0;
+}
+
+SMBCSRV *find_server(SMBCCTX *context,
+ const char *server,
+ const char *share,
+ fstring workgroup,
+ fstring username,
+ fstring password)
+{
+ SMBCSRV *srv;
+ int auth_called = 0;
+
+ check_server_cache:
+
+ srv = context->callbacks.get_cached_srv_fn(context, server, share,
+ workgroup, username);
+
+ if (!auth_called && !srv && (!username[0] || !password[0])) {
+ context->callbacks.auth_fn(server, share,
+ workgroup, sizeof(fstring),
+ username, sizeof(fstring),
+ password, sizeof(fstring));
+ /*
+ * However, smbc_auth_fn may have picked up info relating to
+ * an existing connection, so try for an existing connection
+ * again ...
+ */
+ auth_called = 1;
+ goto check_server_cache;
+
+ }
+
+ if (srv) {
+ if (context->callbacks.check_server_fn(context, srv)) {
+ /*
+ * This server is no good anymore
+ * Try to remove it and check for more possible
+ * servers in the cache
+ */
+ if (context->callbacks.remove_unused_server_fn(context,
+ srv)) {
+ /*
+ * We could not remove the server completely,
+ * remove it from the cache so we will not get
+ * it again. It will be removed when the last
+ * file/dir is closed.
+ */
+ context->callbacks.remove_cached_srv_fn(context,
+ srv);
+ }
+
+ /*
+ * Maybe there are more cached connections to this
+ * server
+ */
+ goto check_server_cache;
+ }
+ return srv;
+ }
+
+ return NULL;
+}
+
+/*
+ * Connect to a server, possibly on an existing connection
+ *
+ * Here, what we want to do is: If the server and username
+ * match an existing connection, reuse that, otherwise, establish a
+ * new connection.
+ *
+ * If we have to create a new connection, call the auth_fn to get the
+ * info we need, unless the username and password were passed in.
+ */
+
+SMBCSRV *smbc_server(SMBCCTX *context,
+ const char *server, const char *share,
+ fstring workgroup, fstring username,
+ fstring password)
+{
+ SMBCSRV *srv=NULL;
+ struct cli_state c;
+ struct nmb_name called, calling;
+ char *p;
+ const char *server_n = server;
+ fstring group;
+ pstring ipenv;
+ struct in_addr ip;
+ int tried_reverse = 0;
+
+ zero_ip(&ip);
+ ZERO_STRUCT(c);
+
+ if (server[0] == 0) {
+ errno = EPERM;
+ return NULL;
+ }
+
+ srv = find_server(context, server, share,
+ workgroup, username, password);
+ if (srv)
+ return srv;
+
+ make_nmb_name(&calling, context->netbios_name, 0x0);
+ make_nmb_name(&called , server, 0x20);
+
+ DEBUG(4,("smbc_server: server_n=[%s] server=[%s]\n", server_n, server));
+
+#if 0 /* djl: obsolete code? neither group nor p is used beyond here */
+ if ((p=strchr_m(server_n,'#')) &&
+ (strcmp(p+1,"1D")==0 || strcmp(p+1,"01")==0)) {
+
+ fstrcpy(group, server_n);
+ p = strchr_m(group,'#');
+ *p = 0;
+
+ }
+#endif
+
+ DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server));
+
+ again:
+ slprintf(ipenv,sizeof(ipenv)-1,"HOST_%s", server_n);
+
+ zero_ip(&ip);
+
+ /* have to open a new connection */
+ if (!cli_initialise(&c)) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ c.timeout = context->timeout;
+
+ /* Force use of port 139 for first try, so browse lists can work */
+ c.port = 139;
+
+ if (!cli_connect(&c, server_n, &ip)) {
+ /*
+ * Port 139 connection failed. Try port 445 to handle
+ * connections to newer (e.g. XP) hosts with NetBIOS disabled.
+ */
+ c.port = 445;
+ if (!cli_connect(&c, server_n, &ip)) {
+ errno = ENETUNREACH;
+ return NULL;
+ }
+ }
+
+ if (!cli_session_request(&c, &calling, &called)) {
+ cli_shutdown(&c);
+ if (strcmp(called.name, "*SMBSERVER")) {
+ make_nmb_name(&called , "*SMBSERVER", 0x20);
+ goto again;
+ }
+ else { /* Try one more time, but ensure we don't loop */
+
+ /* Only try this if server is an IP address ... */
+
+ if (is_ipaddress(server) && !tried_reverse) {
+ fstring remote_name;
+ struct in_addr rem_ip;
+
+ if ((rem_ip.s_addr=inet_addr(server)) == INADDR_NONE) {
+ DEBUG(4, ("Could not convert IP address %s to struct in_addr\n", server));
+ errno = ENOENT;
+ return NULL;
+ }
+
+ tried_reverse++; /* Yuck */
+
+ if (name_status_find("*", 0, 0, rem_ip, remote_name)) {
+ make_nmb_name(&called, remote_name, 0x20);
+ goto again;
+ }
+
+
+ }
+ }
+ errno = ENOENT;
+ return NULL;
+ }
+
+ DEBUG(4,(" session request ok\n"));
+
+ if (!cli_negprot(&c)) {
+ cli_shutdown(&c);
+ errno = ENOENT;
+ return NULL;
+ }
+
+ if (!cli_session_setup(&c, username,
+ password, strlen(password),
+ password, strlen(password),
+ workgroup) &&
+ /* try an anonymous login if it failed */
+ !cli_session_setup(&c, "", "", 1,"", 0, workgroup)) {
+ cli_shutdown(&c);
+ errno = EPERM;
+ return NULL;
+ }
+
+ DEBUG(4,(" session setup ok\n"));
+
+ if (!cli_send_tconX(&c, share, "?????",
+ password, strlen(password)+1)) {
+ errno = smbc_errno(context, &c);
+ cli_shutdown(&c);
+ return NULL;
+ }
+
+ DEBUG(4,(" tconx ok\n"));
+
+ /*
+ * Ok, we have got a nice connection
+ * Let's find a free server_fd
+ */
+
+ srv = (SMBCSRV *)malloc(sizeof(*srv));
+ if (!srv) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ ZERO_STRUCTP(srv);
+ srv->cli = c;
+ srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share));
+
+ /* now add it to the cache (internal or external) */
+ if (context->callbacks.add_cached_srv_fn(context, srv, server, share, workgroup, username)) {
+ DEBUG(3, (" Failed to add server to cache\n"));
+ goto failed;
+ }
+
+
+ DEBUG(2, ("Server connect ok: //%s/%s: %p\n",
+ server, share, srv));
+
+ DLIST_ADD(context->internal->_servers, srv);
+ return srv;
+
+ failed:
+ cli_shutdown(&c);
+ if (!srv) return NULL;
+
+ SAFE_FREE(srv);
+ return NULL;
+}
+
+/*
+ * Connect to a server for getting/setting attributes, possibly on an existing
+ * connection. This works similarly to smbc_server().
+ */
+SMBCSRV *smbc_attr_server(SMBCCTX *context,
+ const char *server, const char *share,
+ fstring workgroup,
+ fstring username, fstring password,
+ POLICY_HND *pol)
+{
+ struct in_addr ip;
+ struct cli_state *ipc_cli;
+ NTSTATUS nt_status;
+ SMBCSRV *ipc_srv=NULL;
+
+ /*
+ * See if we've already created this special connection. Reference
+ * our "special" share name 'IPC$$'.
+ */
+ ipc_srv = find_server(context, server, "IPC$$",
+ workgroup, username, password);
+ if (!ipc_srv) {
+
+ /* We didn't find a cached connection. Get the password */
+ if (*password == '\0') {
+ /* ... then retrieve it now. */
+ context->callbacks.auth_fn(server, share,
+ workgroup, sizeof(fstring),
+ username, sizeof(fstring),
+ password, sizeof(fstring));
+ }
+
+ zero_ip(&ip);
+ nt_status = cli_full_connection(&ipc_cli,
+ global_myname(), server,
+ &ip, 0, "IPC$", "?????",
+ username, workgroup,
+ password, 0,
+ Undefined, NULL);
+ if (! NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1,("cli_full_connection failed! (%s)\n",
+ nt_errstr(nt_status)));
+ errno = ENOTSUP;
+ return NULL;
+ }
+
+ if (!cli_nt_session_open(ipc_cli, PI_LSARPC)) {
+ DEBUG(1, ("cli_nt_session_open fail!\n"));
+ errno = ENOTSUP;
+ cli_shutdown(ipc_cli);
+ return NULL;
+ }
+
+ /* Some systems don't support SEC_RIGHTS_MAXIMUM_ALLOWED,
+ but NT sends 0x2000000 so we might as well do it too. */
+
+ nt_status = cli_lsa_open_policy(ipc_cli,
+ ipc_cli->mem_ctx,
+ True,
+ GENERIC_EXECUTE_ACCESS,
+ pol);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ errno = smbc_errno(context, ipc_cli);
+ cli_shutdown(ipc_cli);
+ return NULL;
+ }
+
+ ipc_srv = (SMBCSRV *)malloc(sizeof(*ipc_srv));
+ if (!ipc_srv) {
+ errno = ENOMEM;
+ cli_shutdown(ipc_cli);
+ return NULL;
+ }
+
+ ZERO_STRUCTP(ipc_srv);
+ ipc_srv->cli = *ipc_cli;
+
+ free(ipc_cli);
+
+ /* now add it to the cache (internal or external) */
+
+ errno = 0; /* let cache function set errno if it likes */
+ if (context->callbacks.add_cached_srv_fn(context, ipc_srv,
+ server,
+ "IPC$$",
+ workgroup,
+ username)) {
+ DEBUG(3, (" Failed to add server to cache\n"));
+ if (errno == 0) {
+ errno = ENOMEM;
+ }
+ cli_shutdown(&ipc_srv->cli);
+ free(ipc_srv);
+ return NULL;
+ }
+
+ DLIST_ADD(context->internal->_servers, ipc_srv);
+ }
+
+ return ipc_srv;
+}
+
+/*
+ * Routine to open() a file ...
+ */
+
+static SMBCFILE *smbc_open_ctx(SMBCCTX *context, const char *fname, int flags, mode_t mode)
+{
+ fstring server, share, user, password, workgroup;
+ pstring path;
+ SMBCSRV *srv = NULL;
+ SMBCFILE *file = NULL;
+ int fd;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ return NULL;
+
+ }
+
+ if (!fname) {
+
+ errno = EINVAL;
+ return NULL;
+
+ }
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(user),
+ password, sizeof(password),
+ NULL, 0)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (user[0] == (char)0) fstrcpy(user, context->user);
+
+ fstrcpy(workgroup, context->workgroup);
+
+ srv = smbc_server(context, server, share, workgroup, user, password);
+
+ if (!srv) {
+
+ if (errno == EPERM) errno = EACCES;
+ return NULL; /* smbc_server sets errno */
+
+ }
+
+ /* Hmmm, the test for a directory is suspect here ... FIXME */
+
+ if (strlen(path) > 0 && path[strlen(path) - 1] == '\\') {
+
+ fd = -1;
+
+ }
+ else {
+
+ file = malloc(sizeof(SMBCFILE));
+
+ if (!file) {
+
+ errno = ENOMEM;
+ return NULL;
+
+ }
+
+ ZERO_STRUCTP(file);
+
+ if ((fd = cli_open(&srv->cli, path, flags, DENY_NONE)) < 0) {
+
+ /* Handle the error ... */
+
+ SAFE_FREE(file);
+ errno = smbc_errno(context, &srv->cli);
+ return NULL;
+
+ }
+
+ /* Fill in file struct */
+
+ file->cli_fd = fd;
+ file->fname = strdup(fname);
+ file->srv = srv;
+ file->offset = 0;
+ file->file = True;
+
+ DLIST_ADD(context->internal->_files, file);
+ return file;
+
+ }
+
+ /* Check if opendir needed ... */
+
+ if (fd == -1) {
+ int eno = 0;
+
+ eno = smbc_errno(context, &srv->cli);
+ file = context->opendir(context, fname);
+ if (!file) errno = eno;
+ return file;
+
+ }
+
+ errno = EINVAL; /* FIXME, correct errno ? */
+ return NULL;
+
+}
+
+/*
+ * Routine to create a file
+ */
+
+static int creat_bits = O_WRONLY | O_CREAT | O_TRUNC; /* FIXME: Do we need this */
+
+static SMBCFILE *smbc_creat_ctx(SMBCCTX *context, const char *path, mode_t mode)
+{
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return NULL;
+
+ }
+
+ return smbc_open_ctx(context, path, creat_bits, mode);
+}
+
+/*
+ * Routine to read() a file ...
+ */
+
+static ssize_t smbc_read_ctx(SMBCCTX *context, SMBCFILE *file, void *buf, size_t count)
+{
+ int ret;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ DEBUG(4, ("smbc_read(%p, %d)\n", file, (int)count));
+
+ if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
+
+ errno = EBADF;
+ return -1;
+
+ }
+
+ /* Check that the buffer exists ... */
+
+ if (buf == NULL) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ ret = cli_read(&file->srv->cli, file->cli_fd, buf, file->offset, count);
+
+ if (ret < 0) {
+
+ errno = smbc_errno(context, &file->srv->cli);
+ return -1;
+
+ }
+
+ file->offset += ret;
+
+ DEBUG(4, (" --> %d\n", ret));
+
+ return ret; /* Success, ret bytes of data ... */
+
+}
+
+/*
+ * Routine to write() a file ...
+ */
+
+static ssize_t smbc_write_ctx(SMBCCTX *context, SMBCFILE *file, void *buf, size_t count)
+{
+ int ret;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
+
+ errno = EBADF;
+ return -1;
+
+ }
+
+ /* Check that the buffer exists ... */
+
+ if (buf == NULL) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ ret = cli_write(&file->srv->cli, file->cli_fd, 0, buf, file->offset, count);
+
+ if (ret <= 0) {
+
+ errno = smbc_errno(context, &file->srv->cli);
+ return -1;
+
+ }
+
+ file->offset += ret;
+
+ return ret; /* Success, 0 bytes of data ... */
+}
+
+/*
+ * Routine to close() a file ...
+ */
+
+static int smbc_close_ctx(SMBCCTX *context, SMBCFILE *file)
+{
+ SMBCSRV *srv;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
+
+ errno = EBADF;
+ return -1;
+
+ }
+
+ /* IS a dir ... */
+ if (!file->file) {
+
+ return context->closedir(context, file);
+
+ }
+
+ if (!cli_close(&file->srv->cli, file->cli_fd)) {
+
+ DEBUG(3, ("cli_close failed on %s. purging server.\n",
+ file->fname));
+ /* Deallocate slot and remove the server
+ * from the server cache if unused */
+ errno = smbc_errno(context, &file->srv->cli);
+ srv = file->srv;
+ DLIST_REMOVE(context->internal->_files, file);
+ SAFE_FREE(file->fname);
+ SAFE_FREE(file);
+ context->callbacks.remove_unused_server_fn(context, srv);
+
+ return -1;
+
+ }
+
+ DLIST_REMOVE(context->internal->_files, file);
+ SAFE_FREE(file->fname);
+ SAFE_FREE(file);
+
+ return 0;
+}
+
+/*
+ * Get info from an SMB server on a file. Use a qpathinfo call first
+ * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo
+ */
+static BOOL smbc_getatr(SMBCCTX * context, SMBCSRV *srv, char *path,
+ uint16 *mode, size_t *size,
+ time_t *c_time, time_t *a_time, time_t *m_time,
+ SMB_INO_T *ino)
+{
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ DEBUG(4,("smbc_getatr: sending qpathinfo\n"));
+
+ if (!srv->no_pathinfo2 &&
+ cli_qpathinfo2(&srv->cli, path, c_time, a_time, m_time, NULL,
+ size, mode, ino)) return True;
+
+ /* if this is NT then don't bother with the getatr */
+ if (srv->cli.capabilities & CAP_NT_SMBS) {
+ errno = EPERM;
+ return False;
+ }
+
+ if (cli_getatr(&srv->cli, path, mode, size, m_time)) {
+ a_time = c_time = m_time;
+ srv->no_pathinfo2 = True;
+ return True;
+ }
+
+ errno = EPERM;
+ return False;
+
+}
+
+/*
+ * Routine to unlink() a file
+ */
+
+static int smbc_unlink_ctx(SMBCCTX *context, const char *fname)
+{
+ fstring server, share, user, password, workgroup;
+ pstring path;
+ SMBCSRV *srv = NULL;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ return -1;
+
+ }
+
+ if (!fname) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(user),
+ password, sizeof(password),
+ NULL, 0)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (user[0] == (char)0) fstrcpy(user, context->user);
+
+ fstrcpy(workgroup, context->workgroup);
+
+ srv = smbc_server(context, server, share, workgroup, user, password);
+
+ if (!srv) {
+
+ return -1; /* smbc_server sets errno */
+
+ }
+
+ /* if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
+
+ int job = smbc_stat_printjob(srv, path, NULL, NULL);
+ if (job == -1) {
+
+ return -1;
+
+ }
+ if ((err = cli_printjob_del(&srv->cli, job)) != 0) {
+
+
+ return -1;
+
+ }
+ } else */
+
+ if (!cli_unlink(&srv->cli, path)) {
+
+ errno = smbc_errno(context, &srv->cli);
+
+ if (errno == EACCES) { /* Check if the file is a directory */
+
+ int saverr = errno;
+ size_t size = 0;
+ uint16 mode = 0;
+ time_t m_time = 0, a_time = 0, c_time = 0;
+ SMB_INO_T ino = 0;
+
+ if (!smbc_getatr(context, srv, path, &mode, &size,
+ &c_time, &a_time, &m_time, &ino)) {
+
+ /* Hmmm, bad error ... What? */
+
+ errno = smbc_errno(context, &srv->cli);
+ return -1;
+
+ }
+ else {
+
+ if (IS_DOS_DIR(mode))
+ errno = EISDIR;
+ else
+ errno = saverr; /* Restore this */
+
+ }
+ }
+
+ return -1;
+
+ }
+
+ return 0; /* Success ... */
+
+}
+
+/*
+ * Routine to rename() a file
+ */
+
+static int smbc_rename_ctx(SMBCCTX *ocontext, const char *oname,
+ SMBCCTX *ncontext, const char *nname)
+{
+ fstring server1, share1, server2, share2, user1, user2, password1, password2, workgroup;
+ pstring path1, path2;
+ SMBCSRV *srv = NULL;
+
+ if (!ocontext || !ncontext ||
+ !ocontext->internal || !ncontext->internal ||
+ !ocontext->internal->_initialized ||
+ !ncontext->internal->_initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ return -1;
+
+ }
+
+ if (!oname || !nname) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ DEBUG(4, ("smbc_rename(%s,%s)\n", oname, nname));
+
+ smbc_parse_path(ocontext, oname,
+ server1, sizeof(server1),
+ share1, sizeof(share1),
+ path1, sizeof(path1),
+ user1, sizeof(user1),
+ password1, sizeof(password1),
+ NULL, 0);
+
+ if (user1[0] == (char)0) fstrcpy(user1, ocontext->user);
+
+ smbc_parse_path(ncontext, nname,
+ server2, sizeof(server2),
+ share2, sizeof(share2),
+ path2, sizeof(path2),
+ user2, sizeof(user2),
+ password2, sizeof(password2),
+ NULL, 0);
+
+ if (user2[0] == (char)0) fstrcpy(user2, ncontext->user);
+
+ if (strcmp(server1, server2) || strcmp(share1, share2) ||
+ strcmp(user1, user2)) {
+
+ /* Can't rename across file systems, or users?? */
+
+ errno = EXDEV;
+ return -1;
+
+ }
+
+ fstrcpy(workgroup, ocontext->workgroup);
+ /* HELP !!! Which workgroup should I use ? Or are they always the same -- Tom */
+ srv = smbc_server(ocontext, server1, share1, workgroup, user1, password1);
+ if (!srv) {
+
+ return -1;
+
+ }
+
+ if (!cli_rename(&srv->cli, path1, path2)) {
+ int eno = smbc_errno(ocontext, &srv->cli);
+
+ if (eno != EEXIST ||
+ !cli_unlink(&srv->cli, path2) ||
+ !cli_rename(&srv->cli, path1, path2)) {
+
+ errno = eno;
+ return -1;
+
+ }
+ }
+
+ return 0; /* Success */
+
+}
+
+/*
+ * A routine to lseek() a file
+ */
+
+static off_t smbc_lseek_ctx(SMBCCTX *context, SMBCFILE *file, off_t offset, int whence)
+{
+ size_t size;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
+
+ errno = EBADF;
+ return -1;
+
+ }
+
+ if (!file->file) {
+
+ errno = EINVAL;
+ return -1; /* Can't lseek a dir ... */
+
+ }
+
+ switch (whence) {
+ case SEEK_SET:
+ file->offset = offset;
+ break;
+
+ case SEEK_CUR:
+ file->offset += offset;
+ break;
+
+ case SEEK_END:
+ if (!cli_qfileinfo(&file->srv->cli, file->cli_fd, NULL, &size, NULL, NULL,
+ NULL, NULL, NULL))
+ {
+ SMB_BIG_UINT b_size = size;
+ if (!cli_getattrE(&file->srv->cli, file->cli_fd, NULL, &b_size, NULL, NULL,
+ NULL))
+ {
+ errno = EINVAL;
+ return -1;
+ } else
+ size = b_size;
+ }
+ file->offset = size + offset;
+ break;
+
+ default:
+ errno = EINVAL;
+ break;
+
+ }
+
+ return file->offset;
+
+}
+
+/*
+ * Generate an inode number from file name for those things that need it
+ */
+
+static
+ino_t smbc_inode(SMBCCTX *context, const char *name)
+{
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (!*name) return 2; /* FIXME, why 2 ??? */
+ return (ino_t)str_checksum(name);
+
+}
+
+/*
+ * Routine to put basic stat info into a stat structure ... Used by stat and
+ * fstat below.
+ */
+
+static
+int smbc_setup_stat(SMBCCTX *context, struct stat *st, char *fname, size_t size, int mode)
+{
+
+ st->st_mode = 0;
+
+ if (IS_DOS_DIR(mode)) {
+ st->st_mode = SMBC_DIR_MODE;
+ } else {
+ st->st_mode = SMBC_FILE_MODE;
+ }
+
+ if (IS_DOS_ARCHIVE(mode)) st->st_mode |= S_IXUSR;
+ if (IS_DOS_SYSTEM(mode)) st->st_mode |= S_IXGRP;
+ if (IS_DOS_HIDDEN(mode)) st->st_mode |= S_IXOTH;
+ if (!IS_DOS_READONLY(mode)) st->st_mode |= S_IWUSR;
+
+ st->st_size = size;
+#ifdef HAVE_STAT_ST_BLKSIZE
+ st->st_blksize = 512;
+#endif
+#ifdef HAVE_STAT_ST_BLOCKS
+ st->st_blocks = (size+511)/512;
+#endif
+ st->st_uid = getuid();
+ st->st_gid = getgid();
+
+ if (IS_DOS_DIR(mode)) {
+ st->st_nlink = 2;
+ } else {
+ st->st_nlink = 1;
+ }
+
+ if (st->st_ino == 0) {
+ st->st_ino = smbc_inode(context, fname);
+ }
+
+ return True; /* FIXME: Is this needed ? */
+
+}
+
+/*
+ * Routine to stat a file given a name
+ */
+
+static int smbc_stat_ctx(SMBCCTX *context, const char *fname, struct stat *st)
+{
+ SMBCSRV *srv;
+ fstring server, share, user, password, workgroup;
+ pstring path;
+ time_t m_time = 0, a_time = 0, c_time = 0;
+ size_t size = 0;
+ uint16 mode = 0;
+ SMB_INO_T ino = 0;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ return -1;
+
+ }
+
+ if (!fname) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ DEBUG(4, ("smbc_stat(%s)\n", fname));
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(user),
+ password, sizeof(password),
+ NULL, 0)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (user[0] == (char)0) fstrcpy(user, context->user);
+
+ fstrcpy(workgroup, context->workgroup);
+
+ srv = smbc_server(context, server, share, workgroup, user, password);
+
+ if (!srv) {
+ return -1; /* errno set by smbc_server */
+ }
+
+ if (!smbc_getatr(context, srv, path, &mode, &size,
+ &c_time, &a_time, &m_time, &ino)) {
+
+ errno = smbc_errno(context, &srv->cli);
+ return -1;
+
+ }
+
+ st->st_ino = ino;
+
+ smbc_setup_stat(context, st, path, size, mode);
+
+ st->st_atime = a_time;
+ st->st_ctime = c_time;
+ st->st_mtime = m_time;
+ st->st_dev = srv->dev;
+
+ return 0;
+
+}
+
+/*
+ * Routine to stat a file given an fd
+ */
+
+static int smbc_fstat_ctx(SMBCCTX *context, SMBCFILE *file, struct stat *st)
+{
+ time_t c_time, a_time, m_time;
+ size_t size;
+ uint16 mode;
+ SMB_INO_T ino = 0;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
+
+ errno = EBADF;
+ return -1;
+
+ }
+
+ if (!file->file) {
+
+ return context->fstatdir(context, file, st);
+
+ }
+
+ if (!cli_qfileinfo(&file->srv->cli, file->cli_fd,
+ &mode, &size, &c_time, &a_time, &m_time, NULL, &ino)) {
+ SMB_BIG_UINT b_size = size;
+ if (!cli_getattrE(&file->srv->cli, file->cli_fd,
+ &mode, &b_size, &c_time, &a_time, &m_time)) {
+
+ errno = EINVAL;
+ return -1;
+ } else
+ size = b_size;
+
+ }
+
+ st->st_ino = ino;
+
+ smbc_setup_stat(context, st, file->fname, size, mode);
+
+ st->st_atime = a_time;
+ st->st_ctime = c_time;
+ st->st_mtime = m_time;
+ st->st_dev = file->srv->dev;
+
+ return 0;
+
+}
+
+/*
+ * Routine to open a directory
+ * We accept the URL syntax explained in smbc_parse_path(), above.
+ */
+
+static void smbc_remove_dir(SMBCFILE *dir)
+{
+ struct smbc_dir_list *d,*f;
+
+ d = dir->dir_list;
+ while (d) {
+
+ f = d; d = d->next;
+
+ SAFE_FREE(f->dirent);
+ SAFE_FREE(f);
+
+ }
+
+ dir->dir_list = dir->dir_end = dir->dir_next = NULL;
+
+}
+
+static int add_dirent(SMBCFILE *dir, const char *name, const char *comment, uint32 type)
+{
+ struct smbc_dirent *dirent;
+ int size;
+ char *u_name = NULL, *u_comment = NULL;
+ size_t u_name_len = 0, u_comment_len = 0;
+
+ if (name)
+ u_name_len = push_utf8_allocate(&u_name, name);
+ if (comment)
+ u_comment_len = push_utf8_allocate(&u_comment, comment);
+
+ /*
+ * Allocate space for the dirent, which must be increased by the
+ * size of the name and the comment and 1 for the null on the comment.
+ * The null on the name is already accounted for.
+ */
+
+ size = sizeof(struct smbc_dirent) + u_name_len + u_comment_len + 1;
+
+ dirent = malloc(size);
+
+ if (!dirent) {
+
+ dir->dir_error = ENOMEM;
+ return -1;
+
+ }
+
+ ZERO_STRUCTP(dirent);
+
+ if (dir->dir_list == NULL) {
+
+ dir->dir_list = malloc(sizeof(struct smbc_dir_list));
+ if (!dir->dir_list) {
+
+ SAFE_FREE(dirent);
+ dir->dir_error = ENOMEM;
+ return -1;
+
+ }
+ ZERO_STRUCTP(dir->dir_list);
+
+ dir->dir_end = dir->dir_next = dir->dir_list;
+ }
+ else {
+
+ dir->dir_end->next = malloc(sizeof(struct smbc_dir_list));
+
+ if (!dir->dir_end->next) {
+
+ SAFE_FREE(dirent);
+ dir->dir_error = ENOMEM;
+ return -1;
+
+ }
+ ZERO_STRUCTP(dir->dir_end->next);
+
+ dir->dir_end = dir->dir_end->next;
+ }
+
+ dir->dir_end->next = NULL;
+ dir->dir_end->dirent = dirent;
+
+ dirent->smbc_type = type;
+ dirent->namelen = u_name_len;
+ dirent->commentlen = u_comment_len;
+ dirent->dirlen = size;
+
+ strncpy(dirent->name, (u_name?u_name:""), dirent->namelen + 1);
+
+ dirent->comment = (char *)(&dirent->name + dirent->namelen + 1);
+ strncpy(dirent->comment, (u_comment?u_comment:""), dirent->commentlen + 1);
+
+ SAFE_FREE(u_comment);
+ SAFE_FREE(u_name);
+
+ return 0;
+
+}
+
+static void
+list_unique_wg_fn(const char *name, uint32 type, const char *comment, void *state)
+{
+ SMBCFILE *dir = (SMBCFILE *)state;
+ struct smbc_dir_list *dir_list;
+ struct smbc_dirent *dirent;
+ int dirent_type;
+ int remove = 0;
+
+ dirent_type = dir->dir_type;
+
+ if (add_dirent(dir, name, comment, dirent_type) < 0) {
+
+ /* An error occurred, what do we do? */
+ /* FIXME: Add some code here */
+ }
+
+ /* Point to the one just added */
+ dirent = dir->dir_end->dirent;
+
+ /* See if this was a duplicate */
+ for (dir_list = dir->dir_list;
+ dir_list != dir->dir_end;
+ dir_list = dir_list->next) {
+ if (! remove &&
+ strcmp(dir_list->dirent->name, dirent->name) == 0) {
+ /* Duplicate. End end of list need to be removed. */
+ remove = 1;
+ }
+
+ if (remove && dir_list->next == dir->dir_end) {
+ /* Found the end of the list. Remove it. */
+ dir->dir_end = dir_list;
+ free(dir_list->next);
+ dir_list->next = NULL;
+ break;
+ }
+ }
+}
+
+static void
+list_fn(const char *name, uint32 type, const char *comment, void *state)
+{
+ SMBCFILE *dir = (SMBCFILE *)state;
+ int dirent_type;
+
+ /* We need to process the type a little ... */
+
+ if (dir->dir_type == SMBC_FILE_SHARE) {
+
+ switch (type) {
+ case 0: /* Directory tree */
+ dirent_type = SMBC_FILE_SHARE;
+ break;
+
+ case 1:
+ dirent_type = SMBC_PRINTER_SHARE;
+ break;
+
+ case 2:
+ dirent_type = SMBC_COMMS_SHARE;
+ break;
+
+ case 3:
+ dirent_type = SMBC_IPC_SHARE;
+ break;
+
+ default:
+ dirent_type = SMBC_FILE_SHARE; /* FIXME, error? */
+ break;
+ }
+ }
+ else dirent_type = dir->dir_type;
+
+ if (add_dirent(dir, name, comment, dirent_type) < 0) {
+
+ /* An error occurred, what do we do? */
+ /* FIXME: Add some code here */
+
+ }
+}
+
+static void
+dir_list_fn(file_info *finfo, const char *mask, void *state)
+{
+
+ if (add_dirent((SMBCFILE *)state, finfo->name, "",
+ (finfo->mode&aDIR?SMBC_DIR:SMBC_FILE)) < 0) {
+
+ /* Handle an error ... */
+
+ /* FIXME: Add some code ... */
+
+ }
+
+}
+
+static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
+{
+ fstring server, share, user, password, options;
+ pstring workgroup;
+ pstring path;
+ SMBCSRV *srv = NULL;
+ SMBCFILE *dir = NULL;
+ struct in_addr rem_ip;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+ DEBUG(4, ("no valid context\n"));
+ errno = EINVAL;
+ return NULL;
+
+ }
+
+ if (!fname) {
+ DEBUG(4, ("no valid fname\n"));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(path),
+ password, sizeof(password),
+ options, sizeof(options))) {
+ DEBUG(4, ("no valid path\n"));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ DEBUG(4, ("parsed path: fname='%s' server='%s' share='%s' path='%s' options='%s'\n", fname, server, share, path, options));
+
+ /* Ensure the options are valid */
+ if (smbc_check_options(server, share, path, options)) {
+ DEBUG(4, ("unacceptable options (%s)\n", options));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (user[0] == (char)0) fstrcpy(user, context->user);
+
+ pstrcpy(workgroup, context->workgroup);
+
+ dir = malloc(sizeof(*dir));
+
+ if (!dir) {
+
+ errno = ENOMEM;
+ return NULL;
+
+ }
+
+ ZERO_STRUCTP(dir);
+
+ dir->cli_fd = 0;
+ dir->fname = strdup(fname);
+ dir->srv = NULL;
+ dir->offset = 0;
+ dir->file = False;
+ dir->dir_list = dir->dir_next = dir->dir_end = NULL;
+
+ if (server[0] == (char)0 &&
+ (! *options || strcmp(options, "mb=.any") == 0)) {
+ struct in_addr server_ip;
+ if (share[0] != (char)0 || path[0] != (char)0) {
+
+ errno = EINVAL;
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ return NULL;
+ }
+
+ /*
+ * We have server and share and path empty ... so list the
+ * workgroups first try to get the LMB for our workgroup, and
+ * if that fails, try the DMB
+ */
+
+ pstrcpy(workgroup, lp_workgroup());
+
+ if (!find_master_ip(workgroup, &server_ip)) {
+ struct user_auth_info u_info;
+ struct cli_state *cli;
+
+ DEBUG(4, ("Unable to find master browser for workgroup %s\n",
+ workgroup));
+
+ /* find the name of the server ... */
+ pstrcpy(u_info.username, user);
+ pstrcpy(u_info.password, password);
+
+ if (!(cli = get_ipc_connect_master_ip_bcast(workgroup, &u_info))) {
+ DEBUG(4, ("Unable to find master browser by "
+ "broadcast\n"));
+ errno = ENOENT;
+ return NULL;
+ }
+
+ fstrcpy(server, cli->desthost);
+
+ cli_shutdown(cli);
+ } else {
+ /*
+ * Do a name status query to find out the name of the
+ * master browser. We use <01><02>__MSBROWSE__<02>#01 if
+ * *#00 fails because a domain master browser will not
+ * respond to a wildcard query (or, at least, an NT4
+ * server acting as the domain master browser will not).
+ *
+ * We might be able to use ONLY the query on MSBROWSE, but
+ * that's not yet been tested with all Windows versions,
+ * so until it is, leave the original wildcard query as
+ * the first choice and fall back to MSBROWSE if the
+ * wildcard query fails.
+ */
+ if (!name_status_find("*", 0, 0x1d, server_ip, server) &&
+ !name_status_find(MSBROWSE, 1, 0x1d, server_ip, server)) {
+ errno = ENOENT;
+ return NULL;
+ }
+ }
+
+ DEBUG(4, ("using workgroup %s %s\n", workgroup, server));
+
+ /*
+ * Get a connection to IPC$ on the server if we do not already
+ * have one
+ */
+
+ srv = smbc_server(context, server, "IPC$", workgroup, user, password);
+ if (!srv) {
+
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ return NULL;
+ }
+
+ dir->srv = srv;
+ dir->dir_type = SMBC_WORKGROUP;
+
+ /* Now, list the stuff ... */
+
+ if (!cli_NetServerEnum(&srv->cli, workgroup, SV_TYPE_DOMAIN_ENUM, list_fn,
+ (void *)dir)) {
+
+ DEBUG(1, ("Could not enumerate domains using '%s'\n", workgroup));
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ errno = cli_errno(&srv->cli);
+
+ return NULL;
+
+ }
+ } else if (server[0] == (char)0 &&
+ (! *options || strcmp(options, "mb=.all") == 0)) {
+
+ int i;
+ int count;
+ struct ip_service *ip_list;
+ struct ip_service server_addr;
+ struct user_auth_info u_info;
+ struct cli_state *cli;
+
+ if (share[0] != (char)0 || path[0] != (char)0) {
+
+ errno = EINVAL;
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ return NULL;
+ }
+
+ pstrcpy(u_info.username, user);
+ pstrcpy(u_info.password, password);
+
+ /*
+ * We have server and share and path empty but options
+ * requesting that we scan all master browsers for their list
+ * of workgroups/domains. This implies that we must first try
+ * broadcast queries to find all master browsers, and if that
+ * doesn't work, then try our other methods which return only
+ * a single master browser.
+ */
+
+ if (!name_resolve_bcast(MSBROWSE, 1, &ip_list, &count)) {
+ if (!find_master_ip(workgroup, &server_addr.ip)) {
+
+ errno = ENOENT;
+ return NULL;
+ }
+
+ ip_list = &server_addr;
+ count = 1;
+ }
+
+ for (i = 0; i < count; i++) {
+ DEBUG(99, ("Found master browser %s\n", inet_ntoa(ip_list[i].ip)));
+
+ cli = get_ipc_connect_master_ip(&ip_list[i], workgroup, &u_info);
+ fstrcpy(server, cli->desthost);
+ cli_shutdown(cli);
+
+ DEBUG(4, ("using workgroup %s %s\n", workgroup, server));
+
+ /*
+ * For each returned master browser IP address, get a
+ * connection to IPC$ on the server if we do not
+ * already have one, and determine the
+ * workgroups/domains that it knows about.
+ */
+
+ srv = smbc_server(context, server,
+ "IPC$", workgroup, user, password);
+ if (!srv) {
+
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ return NULL;
+ }
+
+ dir->srv = srv;
+ dir->dir_type = SMBC_WORKGROUP;
+
+ /* Now, list the stuff ... */
+
+ if (!cli_NetServerEnum(&srv->cli, workgroup, SV_TYPE_DOMAIN_ENUM, list_unique_wg_fn,
+ (void *)dir)) {
+
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ errno = cli_errno(&srv->cli);
+
+ return NULL;
+
+ }
+ }
+ } else {
+ /*
+ * Server not an empty string ... Check the rest and see what
+ * gives
+ */
+ if (share[0] == (char)0) {
+
+ if (path[0] != (char)0) { /* Should not have empty share with path */
+
+ errno = EINVAL;
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ return NULL;
+
+ }
+
+ /* Check to see if <server><1D>, <server><1B>, or <server><20> translates */
+ /* However, we check to see if <server> is an IP address first */
+
+ if (!is_ipaddress(server) && /* Not an IP addr so check next */
+ (resolve_name(server, &rem_ip, 0x1d) || /* Found LMB */
+ resolve_name(server, &rem_ip, 0x1b) )) { /* Found DMB */
+ fstring buserver;
+
+ dir->dir_type = SMBC_SERVER;
+
+ /*
+ * Get the backup list ...
+ */
+
+
+ if (!name_status_find(server, 0, 0, rem_ip, buserver)) {
+
+ DEBUG(0, ("Could not get name of local/domain master browser for server %s\n", server));
+ errno = EPERM; /* FIXME, is this correct */
+ return NULL;
+
+ }
+
+ /*
+ * Get a connection to IPC$ on the server if we do not already have one
+ */
+
+ srv = smbc_server(context, buserver, "IPC$", workgroup, user, password);
+
+ if (!srv) {
+ DEBUG(0, ("got no contact to IPC$\n"));
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ return NULL;
+
+ }
+
+ dir->srv = srv;
+
+ /* Now, list the servers ... */
+
+ if (!cli_NetServerEnum(&srv->cli, server, 0x0000FFFE, list_fn,
+ (void *)dir)) {
+
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ errno = cli_errno(&srv->cli);
+ return NULL;
+
+ }
+ }
+ else {
+
+ if (resolve_name(server, &rem_ip, 0x20)) {
+
+ /* Now, list the shares ... */
+
+ dir->dir_type = SMBC_FILE_SHARE;
+
+ srv = smbc_server(context, server, "IPC$", workgroup, user, password);
+
+ if (!srv) {
+
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ return NULL;
+
+ }
+
+ dir->srv = srv;
+
+ /* Now, list the servers ... */
+
+ if (cli_RNetShareEnum(&srv->cli, list_fn,
+ (void *)dir) < 0) {
+
+ errno = cli_errno(&srv->cli);
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ return NULL;
+
+ }
+
+ }
+ else {
+
+ errno = ENODEV; /* Neither the workgroup nor server exists */
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ return NULL;
+
+ }
+
+ }
+
+ }
+ else { /* The server and share are specified ... work from there ... */
+
+ /* Well, we connect to the server and list the directory */
+
+ dir->dir_type = SMBC_FILE_SHARE;
+
+ srv = smbc_server(context, server, share, workgroup, user, password);
+
+ if (!srv) {
+
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ return NULL;
+
+ }
+
+ dir->srv = srv;
+
+ /* Now, list the files ... */
+
+ pstrcat(path, "\\*");
+
+ if (cli_list(&srv->cli, path, aDIR | aSYSTEM | aHIDDEN, dir_list_fn,
+ (void *)dir) < 0) {
+
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ errno = smbc_errno(context, &srv->cli);
+ return NULL;
+
+ }
+ }
+
+ }
+
+ DLIST_ADD(context->internal->_files, dir);
+ return dir;
+
+}
+
+/*
+ * Routine to close a directory
+ */
+
+static int smbc_closedir_ctx(SMBCCTX *context, SMBCFILE *dir)
+{
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
+
+ errno = EBADF;
+ return -1;
+
+ }
+
+ smbc_remove_dir(dir); /* Clean it up */
+
+ DLIST_REMOVE(context->internal->_files, dir);
+
+ if (dir) {
+
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir); /* Free the space too */
+ }
+
+ return 0;
+
+}
+
+/*
+ * Routine to get a directory entry
+ */
+
+struct smbc_dirent *smbc_readdir_ctx(SMBCCTX *context, SMBCFILE *dir)
+{
+ struct smbc_dirent *dirp, *dirent;
+
+ /* Check that all is ok first ... */
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ DEBUG(0, ("Invalid context in smbc_readdir_ctx()\n"));
+ return NULL;
+
+ }
+
+ if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
+
+ errno = EBADF;
+ DEBUG(0, ("Invalid dir in smbc_readdir_ctx()\n"));
+ return NULL;
+
+ }
+
+ if (dir->file != False) { /* FIXME, should be dir, perhaps */
+
+ errno = ENOTDIR;
+ DEBUG(0, ("Found file vs directory in smbc_readdir_ctx()\n"));
+ return NULL;
+
+ }
+
+ if (!dir->dir_next) {
+ return NULL;
+ }
+ else {
+
+ dirent = dir->dir_next->dirent;
+ if (!dirent) {
+
+ errno = ENOENT;
+ return NULL;
+
+ }
+
+ /* Hmmm, do I even need to copy it? */
+
+ memcpy(context->internal->_dirent, dirent, dirent->dirlen); /* Copy the dirent */
+ dirp = (struct smbc_dirent *)context->internal->_dirent;
+ dirp->comment = (char *)(&dirp->name + dirent->namelen + 1);
+ dir->dir_next = dir->dir_next->next;
+
+ return (struct smbc_dirent *)context->internal->_dirent;
+ }
+
+}
+
+/*
+ * Routine to get directory entries
+ */
+
+static int smbc_getdents_ctx(SMBCCTX *context, SMBCFILE *dir, struct smbc_dirent *dirp, int count)
+{
+ struct smbc_dir_list *dirlist;
+ int rem = count, reqd;
+ char *ndir = (char *)dirp;
+
+ /* Check that all is ok first ... */
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
+
+ errno = EBADF;
+ return -1;
+
+ }
+
+ if (dir->file != False) { /* FIXME, should be dir, perhaps */
+
+ errno = ENOTDIR;
+ return -1;
+
+ }
+
+ /*
+ * Now, retrieve the number of entries that will fit in what was passed
+ * We have to figure out if the info is in the list, or we need to
+ * send a request to the server to get the info.
+ */
+
+ while ((dirlist = dir->dir_next)) {
+ struct smbc_dirent *dirent;
+
+ if (!dirlist->dirent) {
+
+ errno = ENOENT; /* Bad error */
+ return -1;
+
+ }
+
+ if (rem < (reqd = (sizeof(struct smbc_dirent) + dirlist->dirent->namelen +
+ dirlist->dirent->commentlen + 1))) {
+
+ if (rem < count) { /* We managed to copy something */
+
+ errno = 0;
+ return count - rem;
+
+ }
+ else { /* Nothing copied ... */
+
+ errno = EINVAL; /* Not enough space ... */
+ return -1;
+
+ }
+
+ }
+
+ dirent = dirlist->dirent;
+
+ memcpy(ndir, dirent, reqd); /* Copy the data in ... */
+
+ ((struct smbc_dirent *)ndir)->comment =
+ (char *)(&((struct smbc_dirent *)ndir)->name + dirent->namelen + 1);
+
+ ndir += reqd;
+
+ rem -= reqd;
+
+ dir->dir_next = dirlist = dirlist -> next;
+ }
+
+ if (rem == count)
+ return 0;
+ else
+ return count - rem;
+
+}
+
+/*
+ * Routine to create a directory ...
+ */
+
+static int smbc_mkdir_ctx(SMBCCTX *context, const char *fname, mode_t mode)
+{
+ SMBCSRV *srv;
+ fstring server, share, user, password, workgroup;
+ pstring path;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (!fname) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ DEBUG(4, ("smbc_mkdir(%s)\n", fname));
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(user),
+ password, sizeof(password),
+ NULL, 0)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (user[0] == (char)0) fstrcpy(user, context->user);
+
+ fstrcpy(workgroup, context->workgroup);
+
+ srv = smbc_server(context, server, share, workgroup, user, password);
+
+ if (!srv) {
+
+ return -1; /* errno set by smbc_server */
+
+ }
+
+ /* if (strncmp(srv->cli.dev, "IPC", 3) == 0) {
+
+ mode = aDIR | aRONLY;
+
+ }
+ else if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
+
+ if (strcmp(path, "\\") == 0) {
+
+ mode = aDIR | aRONLY;
+
+ }
+ else {
+
+ mode = aRONLY;
+ smbc_stat_printjob(srv, path, &size, &m_time);
+ c_time = a_time = m_time;
+
+ }
+ else { */
+
+ if (!cli_mkdir(&srv->cli, path)) {
+
+ errno = smbc_errno(context, &srv->cli);
+ return -1;
+
+ }
+
+ return 0;
+
+}
+
+/*
+ * Our list function simply checks to see if a directory is not empty
+ */
+
+static int smbc_rmdir_dirempty = True;
+
+static void rmdir_list_fn(file_info *finfo, const char *mask, void *state)
+{
+
+ if (strncmp(finfo->name, ".", 1) != 0 && strncmp(finfo->name, "..", 2) != 0)
+ smbc_rmdir_dirempty = False;
+
+}
+
+/*
+ * Routine to remove a directory
+ */
+
+static int smbc_rmdir_ctx(SMBCCTX *context, const char *fname)
+{
+ SMBCSRV *srv;
+ fstring server, share, user, password, workgroup;
+ pstring path;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (!fname) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ DEBUG(4, ("smbc_rmdir(%s)\n", fname));
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(user),
+ password, sizeof(password),
+ NULL, 0))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (user[0] == (char)0) fstrcpy(user, context->user);
+
+ fstrcpy(workgroup, context->workgroup);
+
+ srv = smbc_server(context, server, share, workgroup, user, password);
+
+ if (!srv) {
+
+ return -1; /* errno set by smbc_server */
+
+ }
+
+ /* if (strncmp(srv->cli.dev, "IPC", 3) == 0) {
+
+ mode = aDIR | aRONLY;
+
+ }
+ else if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
+
+ if (strcmp(path, "\\") == 0) {
+
+ mode = aDIR | aRONLY;
+
+ }
+ else {
+
+ mode = aRONLY;
+ smbc_stat_printjob(srv, path, &size, &m_time);
+ c_time = a_time = m_time;
+
+ }
+ else { */
+
+ if (!cli_rmdir(&srv->cli, path)) {
+
+ errno = smbc_errno(context, &srv->cli);
+
+ if (errno == EACCES) { /* Check if the dir empty or not */
+
+ pstring lpath; /* Local storage to avoid buffer overflows */
+
+ smbc_rmdir_dirempty = True; /* Make this so ... */
+
+ pstrcpy(lpath, path);
+ pstrcat(lpath, "\\*");
+
+ if (cli_list(&srv->cli, lpath, aDIR | aSYSTEM | aHIDDEN, rmdir_list_fn,
+ NULL) < 0) {
+
+ /* Fix errno to ignore latest error ... */
+
+ DEBUG(5, ("smbc_rmdir: cli_list returned an error: %d\n",
+ smbc_errno(context, &srv->cli)));
+ errno = EACCES;
+
+ }
+
+ if (smbc_rmdir_dirempty)
+ errno = EACCES;
+ else
+ errno = ENOTEMPTY;
+
+ }
+
+ return -1;
+
+ }
+
+ return 0;
+
+}
+
+/*
+ * Routine to return the current directory position
+ */
+
+static off_t smbc_telldir_ctx(SMBCCTX *context, SMBCFILE *dir)
+{
+ off_t ret_val; /* Squash warnings about cast */
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
+
+ errno = EBADF;
+ return -1;
+
+ }
+
+ if (dir->file != False) { /* FIXME, should be dir, perhaps */
+
+ errno = ENOTDIR;
+ return -1;
+
+ }
+
+ /*
+ * We return the pointer here as the offset
+ */
+ ret_val = (int)dir->dir_next;
+ return ret_val;
+
+}
+
+/*
+ * A routine to run down the list and see if the entry is OK
+ */
+
+struct smbc_dir_list *smbc_check_dir_ent(struct smbc_dir_list *list,
+ struct smbc_dirent *dirent)
+{
+
+ /* Run down the list looking for what we want */
+
+ if (dirent) {
+
+ struct smbc_dir_list *tmp = list;
+
+ while (tmp) {
+
+ if (tmp->dirent == dirent)
+ return tmp;
+
+ tmp = tmp->next;
+
+ }
+
+ }
+
+ return NULL; /* Not found, or an error */
+
+}
+
+
+/*
+ * Routine to seek on a directory
+ */
+
+static int smbc_lseekdir_ctx(SMBCCTX *context, SMBCFILE *dir, off_t offset)
+{
+ long int l_offset = offset; /* Handle problems of size */
+ struct smbc_dirent *dirent = (struct smbc_dirent *)l_offset;
+ struct smbc_dir_list *list_ent = (struct smbc_dir_list *)NULL;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (dir->file != False) { /* FIXME, should be dir, perhaps */
+
+ errno = ENOTDIR;
+ return -1;
+
+ }
+
+ /* Now, check what we were passed and see if it is OK ... */
+
+ if (dirent == NULL) { /* Seek to the begining of the list */
+
+ dir->dir_next = dir->dir_list;
+ return 0;
+
+ }
+
+ /* Now, run down the list and make sure that the entry is OK */
+ /* This may need to be changed if we change the format of the list */
+
+ if ((list_ent = smbc_check_dir_ent(dir->dir_list, dirent)) == NULL) {
+
+ errno = EINVAL; /* Bad entry */
+ return -1;
+
+ }
+
+ dir->dir_next = list_ent;
+
+ return 0;
+
+}
+
+/*
+ * Routine to fstat a dir
+ */
+
+static int smbc_fstatdir_ctx(SMBCCTX *context, SMBCFILE *dir, struct stat *st)
+{
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ /* No code yet ... */
+
+ return 0;
+
+}
+
+int smbc_chmod_ctx(SMBCCTX *context, const char *fname, mode_t newmode)
+{
+ SMBCSRV *srv;
+ fstring server, share, user, password, workgroup;
+ pstring path;
+ uint16 mode;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ return -1;
+
+ }
+
+ if (!fname) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ DEBUG(4, ("smbc_chmod(%s, 0%3o)\n", fname, newmode));
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(user),
+ password, sizeof(password),
+ NULL, 0)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (user[0] == (char)0) fstrcpy(user, context->user);
+
+ fstrcpy(workgroup, context->workgroup);
+
+ srv = smbc_server(context, server, share, workgroup, user, password);
+
+ if (!srv) {
+ return -1; /* errno set by smbc_server */
+ }
+
+ mode = 0;
+
+ if (!(newmode & (S_IWUSR | S_IWGRP | S_IWOTH))) mode |= aRONLY;
+ if ((newmode & S_IXUSR) && lp_map_archive(-1)) mode |= aARCH;
+ if ((newmode & S_IXGRP) && lp_map_system(-1)) mode |= aSYSTEM;
+ if ((newmode & S_IXOTH) && lp_map_hidden(-1)) mode |= aHIDDEN;
+
+ if (!cli_setatr(&srv->cli, path, mode, 0)) {
+ errno = smbc_errno(context, &srv->cli);
+ return -1;
+ }
+
+ return 0;
+}
+
+int smbc_utimes_ctx(SMBCCTX *context, const char *fname, struct timeval *tbuf)
+{
+ SMBCSRV *srv;
+ fstring server, share, user, password, workgroup;
+ pstring path;
+ uint16 mode;
+ time_t t = (tbuf == NULL ? time(NULL) : tbuf->tv_sec);
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ return -1;
+
+ }
+
+ if (!fname) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ DEBUG(4, ("smbc_utimes(%s, [%s])\n", fname, ctime(&t)));
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(user),
+ password, sizeof(password),
+ NULL, 0)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (user[0] == (char)0) fstrcpy(user, context->user);
+
+ fstrcpy(workgroup, context->workgroup);
+
+ srv = smbc_server(context, server, share, workgroup, user, password);
+
+ if (!srv) {
+ return -1; /* errno set by smbc_server */
+ }
+
+ if (!smbc_getatr(context, srv, path,
+ &mode, NULL,
+ NULL, NULL, NULL,
+ NULL)) {
+ return -1;
+ }
+
+ if (!cli_setatr(&srv->cli, path, mode, t)) {
+ /* some servers always refuse directory changes */
+ if (!(mode & aDIR)) {
+ errno = smbc_errno(context, &srv->cli);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/* The MSDN is contradictory over the ordering of ACE entries in an ACL.
+ However NT4 gives a "The information may have been modified by a
+ computer running Windows NT 5.0" if denied ACEs do not appear before
+ allowed ACEs. */
+
+static int ace_compare(SEC_ACE *ace1, SEC_ACE *ace2)
+{
+ if (sec_ace_equal(ace1, ace2))
+ return 0;
+
+ if (ace1->type != ace2->type)
+ return ace2->type - ace1->type;
+
+ if (sid_compare(&ace1->trustee, &ace2->trustee))
+ return sid_compare(&ace1->trustee, &ace2->trustee);
+
+ if (ace1->flags != ace2->flags)
+ return ace1->flags - ace2->flags;
+
+ if (ace1->info.mask != ace2->info.mask)
+ return ace1->info.mask - ace2->info.mask;
+
+ if (ace1->size != ace2->size)
+ return ace1->size - ace2->size;
+
+ return memcmp(ace1, ace2, sizeof(SEC_ACE));
+}
+
+
+static void sort_acl(SEC_ACL *the_acl)
+{
+ uint32 i;
+ if (!the_acl) return;
+
+ qsort(the_acl->ace, the_acl->num_aces, sizeof(the_acl->ace[0]), QSORT_CAST ace_compare);
+
+ for (i=1;i<the_acl->num_aces;) {
+ if (sec_ace_equal(&the_acl->ace[i-1], &the_acl->ace[i])) {
+ int j;
+ for (j=i; j<the_acl->num_aces-1; j++) {
+ the_acl->ace[j] = the_acl->ace[j+1];
+ }
+ the_acl->num_aces--;
+ } else {
+ i++;
+ }
+ }
+}
+
+/* convert a SID to a string, either numeric or username/group */
+static void convert_sid_to_string(struct cli_state *ipc_cli,
+ POLICY_HND *pol,
+ fstring str,
+ BOOL numeric,
+ DOM_SID *sid)
+{
+ char **domains = NULL;
+ char **names = NULL;
+ uint32 *types = NULL;
+
+ sid_to_string(str, sid);
+
+ if (numeric) return; /* no lookup desired */
+
+ /* Ask LSA to convert the sid to a name */
+
+ if (!NT_STATUS_IS_OK(cli_lsa_lookup_sids(ipc_cli, ipc_cli->mem_ctx,
+ pol, 1, sid, &domains,
+ &names, &types)) ||
+ !domains || !domains[0] || !names || !names[0]) {
+ return;
+ }
+
+ /* Converted OK */
+
+ slprintf(str, sizeof(fstring) - 1, "%s%s%s",
+ domains[0], lp_winbind_separator(),
+ names[0]);
+}
+
+/* convert a string to a SID, either numeric or username/group */
+static BOOL convert_string_to_sid(struct cli_state *ipc_cli,
+ POLICY_HND *pol,
+ BOOL numeric,
+ DOM_SID *sid,
+ const char *str)
+{
+ uint32 *types = NULL;
+ DOM_SID *sids = NULL;
+ BOOL result = True;
+
+ if (numeric) {
+ if (strncmp(str, "S-", 2) == 0) {
+ return string_to_sid(sid, str);
+ }
+
+ result = False;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(cli_lsa_lookup_names(ipc_cli, ipc_cli->mem_ctx,
+ pol, 1, &str, &sids,
+ &types))) {
+ result = False;
+ goto done;
+ }
+
+ sid_copy(sid, &sids[0]);
+ done:
+
+ return result;
+}
+
+
+/* parse an ACE in the same format as print_ace() */
+static BOOL parse_ace(struct cli_state *ipc_cli,
+ POLICY_HND *pol,
+ SEC_ACE *ace,
+ BOOL numeric,
+ char *str)
+{
+ char *p;
+ const char *cp;
+ fstring tok;
+ unsigned atype, aflags, amask;
+ DOM_SID sid;
+ SEC_ACCESS mask;
+ const struct perm_value *v;
+ struct perm_value {
+ const char *perm;
+ uint32 mask;
+ };
+
+ /* These values discovered by inspection */
+ static const struct perm_value special_values[] = {
+ { "R", 0x00120089 },
+ { "W", 0x00120116 },
+ { "X", 0x001200a0 },
+ { "D", 0x00010000 },
+ { "P", 0x00040000 },
+ { "O", 0x00080000 },
+ { NULL, 0 },
+ };
+
+ static const struct perm_value standard_values[] = {
+ { "READ", 0x001200a9 },
+ { "CHANGE", 0x001301bf },
+ { "FULL", 0x001f01ff },
+ { NULL, 0 },
+ };
+
+
+ ZERO_STRUCTP(ace);
+ p = strchr_m(str,':');
+ if (!p) return False;
+ *p = '\0';
+ p++;
+ /* Try to parse numeric form */
+
+ if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 &&
+ convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
+ goto done;
+ }
+
+ /* Try to parse text form */
+
+ if (!convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
+ return False;
+ }
+
+ cp = p;
+ if (!next_token(&cp, tok, "/", sizeof(fstring))) {
+ return False;
+ }
+
+ if (StrnCaseCmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
+ atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
+ } else if (StrnCaseCmp(tok, "DENIED", strlen("DENIED")) == 0) {
+ atype = SEC_ACE_TYPE_ACCESS_DENIED;
+ } else {
+ return False;
+ }
+
+ /* Only numeric form accepted for flags at present */
+
+ if (!(next_token(&cp, tok, "/", sizeof(fstring)) &&
+ sscanf(tok, "%i", &aflags))) {
+ return False;
+ }
+
+ if (!next_token(&cp, tok, "/", sizeof(fstring))) {
+ return False;
+ }
+
+ if (strncmp(tok, "0x", 2) == 0) {
+ if (sscanf(tok, "%i", &amask) != 1) {
+ return False;
+ }
+ goto done;
+ }
+
+ for (v = standard_values; v->perm; v++) {
+ if (strcmp(tok, v->perm) == 0) {
+ amask = v->mask;
+ goto done;
+ }
+ }
+
+ p = tok;
+
+ while(*p) {
+ BOOL found = False;
+
+ for (v = special_values; v->perm; v++) {
+ if (v->perm[0] == *p) {
+ amask |= v->mask;
+ found = True;
+ }
+ }
+
+ if (!found) return False;
+ p++;
+ }
+
+ if (*p) {
+ return False;
+ }
+
+ done:
+ mask.mask = amask;
+ init_sec_ace(ace, &sid, atype, mask, aflags);
+ return True;
+}
+
+/* add an ACE to a list of ACEs in a SEC_ACL */
+static BOOL add_ace(SEC_ACL **the_acl, SEC_ACE *ace, TALLOC_CTX *ctx)
+{
+ SEC_ACL *new;
+ SEC_ACE *aces;
+ if (! *the_acl) {
+ (*the_acl) = make_sec_acl(ctx, 3, 1, ace);
+ return True;
+ }
+
+ aces = calloc(1+(*the_acl)->num_aces,sizeof(SEC_ACE));
+ memcpy(aces, (*the_acl)->ace, (*the_acl)->num_aces * sizeof(SEC_ACE));
+ memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE));
+ new = make_sec_acl(ctx,(*the_acl)->revision,1+(*the_acl)->num_aces, aces);
+ SAFE_FREE(aces);
+ (*the_acl) = new;
+ return True;
+}
+
+
+/* parse a ascii version of a security descriptor */
+static SEC_DESC *sec_desc_parse(TALLOC_CTX *ctx,
+ struct cli_state *ipc_cli,
+ POLICY_HND *pol,
+ BOOL numeric,
+ char *str)
+{
+ const char *p = str;
+ fstring tok;
+ SEC_DESC *ret;
+ size_t sd_size;
+ DOM_SID *grp_sid=NULL, *owner_sid=NULL;
+ SEC_ACL *dacl=NULL;
+ int revision=1;
+
+ while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) {
+
+ if (StrnCaseCmp(tok,"REVISION:", 9) == 0) {
+ revision = strtol(tok+9, NULL, 16);
+ continue;
+ }
+
+ if (StrnCaseCmp(tok,"OWNER:", 6) == 0) {
+ owner_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
+ if (!owner_sid ||
+ !convert_string_to_sid(ipc_cli, pol,
+ numeric,
+ owner_sid, tok+6)) {
+ DEBUG(5, ("Failed to parse owner sid\n"));
+ return NULL;
+ }
+ continue;
+ }
+
+ if (StrnCaseCmp(tok,"OWNER+:", 7) == 0) {
+ owner_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
+ if (!owner_sid ||
+ !convert_string_to_sid(ipc_cli, pol,
+ False,
+ owner_sid, tok+7)) {
+ DEBUG(5, ("Failed to parse owner sid\n"));
+ return NULL;
+ }
+ continue;
+ }
+
+ if (StrnCaseCmp(tok,"GROUP:", 6) == 0) {
+ grp_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
+ if (!grp_sid ||
+ !convert_string_to_sid(ipc_cli, pol,
+ numeric,
+ grp_sid, tok+6)) {
+ DEBUG(5, ("Failed to parse group sid\n"));
+ return NULL;
+ }
+ continue;
+ }
+
+ if (StrnCaseCmp(tok,"GROUP+:", 7) == 0) {
+ grp_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
+ if (!grp_sid ||
+ !convert_string_to_sid(ipc_cli, pol,
+ False,
+ grp_sid, tok+6)) {
+ DEBUG(5, ("Failed to parse group sid\n"));
+ return NULL;
+ }
+ continue;
+ }
+
+ if (StrnCaseCmp(tok,"ACL:", 4) == 0) {
+ SEC_ACE ace;
+ if (!parse_ace(ipc_cli, pol, &ace, numeric, tok+4)) {
+ DEBUG(5, ("Failed to parse ACL %s\n", tok));
+ return NULL;
+ }
+ if(!add_ace(&dacl, &ace, ctx)) {
+ DEBUG(5, ("Failed to add ACL %s\n", tok));
+ return NULL;
+ }
+ continue;
+ }
+
+ if (StrnCaseCmp(tok,"ACL+:", 5) == 0) {
+ SEC_ACE ace;
+ if (!parse_ace(ipc_cli, pol, &ace, False, tok+5)) {
+ DEBUG(5, ("Failed to parse ACL %s\n", tok));
+ return NULL;
+ }
+ if(!add_ace(&dacl, &ace, ctx)) {
+ DEBUG(5, ("Failed to add ACL %s\n", tok));
+ return NULL;
+ }
+ continue;
+ }
+
+ DEBUG(5, ("Failed to parse security descriptor\n"));
+ return NULL;
+ }
+
+ ret = make_sec_desc(ctx, revision, SEC_DESC_SELF_RELATIVE,
+ owner_sid, grp_sid, NULL, dacl, &sd_size);
+
+ SAFE_FREE(grp_sid);
+ SAFE_FREE(owner_sid);
+
+ return ret;
+}
+
+
+/*****************************************************
+retrieve the acls for a file
+*******************************************************/
+static int cacl_get(TALLOC_CTX *ctx, struct cli_state *cli,
+ struct cli_state *ipc_cli, POLICY_HND *pol,
+ char *filename, char *name, char *buf, int bufsize)
+{
+ uint32 i;
+ int n = 0;
+ int n_used;
+ BOOL all;
+ BOOL numeric = True;
+ BOOL determine_size = (bufsize == 0);
+ int fnum = -1;
+ SEC_DESC *sd;
+ fstring sidstr;
+ char *p;
+
+ fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
+
+ if (fnum == -1) {
+ DEBUG(5, ("cacl_get failed to open %s: %s\n",
+ filename, cli_errstr(cli)));
+ errno = 0;
+ return -1;
+ }
+
+ sd = cli_query_secdesc(cli, fnum, ctx);
+
+ if (!sd) {
+ DEBUG(5, ("cacl_get Failed to query old descriptor\n"));
+ errno = 0;
+ return -1;
+ }
+
+ cli_close(cli, fnum);
+
+ all = (*name == '*');
+ numeric = (* (name + strlen(name) - 1) != '+');
+
+ n_used = 0;
+
+ if (all) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx,
+ "REVISION:%d", sd->revision);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "REVISION:%d", sd->revision);
+ }
+ } else if (StrCaseCmp(name, "revision") == 0) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, "%d", sd->revision);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize, "%d", sd->revision);
+ }
+ }
+
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+
+ /* Get owner and group sid */
+
+ if (sd->owner_sid) {
+ convert_sid_to_string(ipc_cli, pol,
+ sidstr, numeric, sd->owner_sid);
+ } else {
+ fstrcpy(sidstr, "");
+ }
+
+ if (all) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, ",OWNER:%s", sidstr);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize, ",OWNER:%s", sidstr);
+ }
+ } else if (StrnCaseCmp(name, "owner", 5) == 0) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, "%s", sidstr);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize, "%s", sidstr);
+ }
+ }
+
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+
+ if (sd->grp_sid) {
+ convert_sid_to_string(ipc_cli, pol,
+ sidstr, numeric, sd->grp_sid);
+ } else {
+ fstrcpy(sidstr, "");
+ }
+
+ if (all) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, ",GROUP:%s", sidstr);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize, ",GROUP:%s", sidstr);
+ }
+ } else if (StrnCaseCmp(name, "group", 5) == 0) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, "%s", sidstr);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize, "%s", sidstr);
+ }
+ }
+
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+
+ /* Add aces to value buffer */
+ for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
+
+ SEC_ACE *ace = &sd->dacl->ace[i];
+ convert_sid_to_string(ipc_cli, pol,
+ sidstr, numeric, &ace->trustee);
+
+ if (all) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx,
+ ",ACL:%s:%d/%d/0x%08x",
+ sidstr,
+ ace->type,
+ ace->flags,
+ ace->info.mask);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ ",ACL:%s:%d/%d/0x%08x",
+ sidstr,
+ ace->type,
+ ace->flags,
+ ace->info.mask);
+ }
+ } else if ((StrnCaseCmp(name, "acl", 3) == 0 &&
+ StrCaseCmp(name + 3, sidstr) == 0) ||
+ (StrnCaseCmp(name, "acl+", 4) == 0 &&
+ StrCaseCmp(name + 4, sidstr) == 0)) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx,
+ "%d/%d/0x%08x",
+ ace->type,
+ ace->flags,
+ ace->info.mask);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "%d/%d/0x%08x",
+ ace->type, ace->flags, ace->info.mask);
+ }
+ }
+ if (n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+ }
+
+ if (n_used == 0) {
+ errno = ENOATTR;
+ return -1;
+ }
+ return n_used;
+}
+
+
+/*****************************************************
+set the ACLs on a file given an ascii description
+*******************************************************/
+static int cacl_set(TALLOC_CTX *ctx, struct cli_state *cli,
+ struct cli_state *ipc_cli, POLICY_HND *pol,
+ const char *filename, const char *the_acl,
+ int mode, int flags)
+{
+ int fnum;
+ int err = 0;
+ SEC_DESC *sd = NULL, *old;
+ SEC_ACL *dacl = NULL;
+ DOM_SID *owner_sid = NULL;
+ DOM_SID *grp_sid = NULL;
+ uint32 i, j;
+ size_t sd_size;
+ int ret = 0;
+ char *p;
+ BOOL numeric = True;
+
+ /* the_acl will be null for REMOVE_ALL operations */
+ if (the_acl) {
+ numeric = ((p = strchr(the_acl, ':')) != NULL &&
+ p > the_acl &&
+ p[-1] != '+');
+
+ /* if this is to set the entire ACL... */
+ if (*the_acl == '*') {
+ /* ... then increment past the first colon */
+ the_acl = p + 1;
+ }
+
+ sd = sec_desc_parse(ctx, ipc_cli, pol,
+ numeric, (char *) the_acl);
+
+ if (!sd) {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ /* The desired access below is the only one I could find that works
+ with NT4, W2KP and Samba */
+
+ fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
+
+ if (fnum == -1) {
+ DEBUG(5, ("cacl_set failed to open %s: %s\n",
+ filename, cli_errstr(cli)));
+ errno = 0;
+ return -1;
+ }
+
+ old = cli_query_secdesc(cli, fnum, ctx);
+
+ if (!old) {
+ DEBUG(5, ("cacl_set Failed to query old descriptor\n"));
+ errno = 0;
+ return -1;
+ }
+
+ cli_close(cli, fnum);
+
+ switch (mode) {
+ case SMBC_XATTR_MODE_REMOVE_ALL:
+ old->dacl->num_aces = 0;
+ SAFE_FREE(old->dacl->ace);
+ SAFE_FREE(old->dacl);
+ old->off_dacl = 0;
+ dacl = old->dacl;
+ break;
+
+ case SMBC_XATTR_MODE_REMOVE:
+ for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
+ BOOL found = False;
+
+ for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
+ if (sec_ace_equal(&sd->dacl->ace[i],
+ &old->dacl->ace[j])) {
+ uint32 k;
+ for (k=j; k<old->dacl->num_aces-1;k++) {
+ old->dacl->ace[k] = old->dacl->ace[k+1];
+ }
+ old->dacl->num_aces--;
+ if (old->dacl->num_aces == 0) {
+ SAFE_FREE(old->dacl->ace);
+ SAFE_FREE(old->dacl);
+ old->off_dacl = 0;
+ }
+ found = True;
+ dacl = old->dacl;
+ break;
+ }
+ }
+
+ if (!found) {
+ err = ENOATTR;
+ ret = -1;
+ goto failed;
+ }
+ }
+ break;
+
+ case SMBC_XATTR_MODE_ADD:
+ for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
+ BOOL found = False;
+
+ for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
+ if (sid_equal(&sd->dacl->ace[i].trustee,
+ &old->dacl->ace[j].trustee)) {
+ if (!(flags & SMBC_XATTR_FLAG_CREATE)) {
+ err = EEXIST;
+ ret = -1;
+ goto failed;
+ }
+ old->dacl->ace[j] = sd->dacl->ace[i];
+ ret = -1;
+ found = True;
+ }
+ }
+
+ if (!found && (flags & SMBC_XATTR_FLAG_REPLACE)) {
+ err = ENOATTR;
+ ret = -1;
+ goto failed;
+ }
+
+ for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
+ add_ace(&old->dacl, &sd->dacl->ace[i], ctx);
+ }
+ }
+ dacl = old->dacl;
+ break;
+
+ case SMBC_XATTR_MODE_SET:
+ old = sd;
+ owner_sid = old->owner_sid;
+ grp_sid = old->grp_sid;
+ dacl = old->dacl;
+ break;
+
+ case SMBC_XATTR_MODE_CHOWN:
+ owner_sid = sd->owner_sid;
+ break;
+
+ case SMBC_XATTR_MODE_CHGRP:
+ grp_sid = sd->grp_sid;
+ break;
+ }
+
+ /* Denied ACE entries must come before allowed ones */
+ sort_acl(old->dacl);
+
+ /* Create new security descriptor and set it */
+ sd = make_sec_desc(ctx, old->revision, SEC_DESC_SELF_RELATIVE,
+ owner_sid, grp_sid, NULL, dacl, &sd_size);
+
+ fnum = cli_nt_create(cli, filename,
+ WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS);
+
+ if (fnum == -1) {
+ DEBUG(5, ("cacl_set failed to open %s: %s\n",
+ filename, cli_errstr(cli)));
+ errno = 0;
+ return -1;
+ }
+
+ if (!cli_set_secdesc(cli, fnum, sd)) {
+ DEBUG(5, ("ERROR: secdesc set failed: %s\n", cli_errstr(cli)));
+ ret = -1;
+ }
+
+ /* Clean up */
+
+ failed:
+ cli_close(cli, fnum);
+
+ if (err != 0) {
+ errno = err;
+ }
+
+ return ret;
+}
+
+
+int smbc_setxattr_ctx(SMBCCTX *context,
+ const char *fname,
+ const char *name,
+ const void *value,
+ size_t size,
+ int flags)
+{
+ int ret;
+ SMBCSRV *srv;
+ SMBCSRV *ipc_srv;
+ fstring server, share, user, password, workgroup;
+ pstring path;
+ TALLOC_CTX *ctx;
+ POLICY_HND pol;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ return -1;
+
+ }
+
+ if (!fname) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ DEBUG(4, ("smbc_setxattr(%s, %s, %.*s)\n",
+ fname, name, (int) size, (char *) value));
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(user),
+ password, sizeof(password),
+ NULL, 0)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (user[0] == (char)0) fstrcpy(user, context->user);
+
+ fstrcpy(workgroup, context->workgroup);
+
+ srv = smbc_server(context, server, share, workgroup, user, password);
+ if (!srv) {
+ return -1; /* errno set by smbc_server */
+ }
+
+ ipc_srv = smbc_attr_server(context, server, share,
+ workgroup, user, password,
+ &pol);
+ if (!ipc_srv) {
+ return -1;
+ }
+
+ ctx = talloc_init("smbc_setxattr");
+ if (!ctx) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /*
+ * Are they asking to set an access control element or to set
+ * the entire access control list?
+ */
+ if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
+ StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
+ StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
+
+ /* Yup. */
+ char *namevalue =
+ talloc_asprintf(ctx, "%s:%s", name+19, (char *) value);
+ if (! namevalue) {
+ errno = ENOMEM;
+ ret = -1;
+ } else {
+ ret = cacl_set(ctx, &srv->cli,
+ &ipc_srv->cli, &pol, path,
+ namevalue,
+ (*namevalue == '*'
+ ? SMBC_XATTR_MODE_SET
+ : SMBC_XATTR_MODE_ADD),
+ flags);
+ }
+ talloc_destroy(ctx);
+ return ret;
+ }
+
+ /*
+ * Are they asking to set the owner?
+ */
+ if (StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0) {
+
+ /* Yup. */
+ char *namevalue =
+ talloc_asprintf(ctx, "%s:%s", name+19, (char *) value);
+ if (! namevalue) {
+ errno = ENOMEM;
+ ret = -1;
+ } else {
+ ret = cacl_set(ctx, &srv->cli,
+ &ipc_srv->cli, &pol, path,
+ namevalue, SMBC_XATTR_MODE_CHOWN, 0);
+ }
+ talloc_destroy(ctx);
+ return ret;
+ }
+
+ /*
+ * Are they asking to set the group?
+ */
+ if (StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.group+") == 0) {
+
+ /* Yup. */
+ char *namevalue =
+ talloc_asprintf(ctx, "%s:%s", name+19, (char *) value);
+ if (! namevalue) {
+ errno = ENOMEM;
+ ret = -1;
+ } else {
+ ret = cacl_set(ctx, &srv->cli,
+ &ipc_srv->cli, &pol, path,
+ namevalue, SMBC_XATTR_MODE_CHOWN, 0);
+ }
+ talloc_destroy(ctx);
+ return ret;
+ }
+
+ /* Unsupported attribute name */
+ talloc_destroy(ctx);
+ errno = EINVAL;
+ return -1;
+}
+
+int smbc_getxattr_ctx(SMBCCTX *context,
+ const char *fname,
+ const char *name,
+ const void *value,
+ size_t size)
+{
+ int ret;
+ SMBCSRV *srv;
+ SMBCSRV *ipc_srv;
+ fstring server, share, user, password, workgroup;
+ pstring path;
+ TALLOC_CTX *ctx;
+ POLICY_HND pol;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ return -1;
+
+ }
+
+ if (!fname) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ DEBUG(4, ("smbc_getxattr(%s, %s)\n", fname, name));
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(user),
+ password, sizeof(password),
+ NULL, 0)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (user[0] == (char)0) fstrcpy(user, context->user);
+
+ fstrcpy(workgroup, context->workgroup);
+
+ srv = smbc_server(context, server, share, workgroup, user, password);
+ if (!srv) {
+ return -1; /* errno set by smbc_server */
+ }
+
+ ipc_srv = smbc_attr_server(context, server, share,
+ workgroup, user, password,
+ &pol);
+ if (!ipc_srv) {
+ return -1;
+ }
+
+ ctx = talloc_init("smbc:getxattr");
+ if (!ctx) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /* Are they requesting a supported attribute? */
+ if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
+ StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
+ StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
+
+ /* Yup. */
+ ret = cacl_get(ctx, &srv->cli,
+ &ipc_srv->cli, &pol,
+ (char *) path, (char *) name + 19,
+ (char *) value, size);
+ if (ret < 0 && errno == 0) {
+ errno = smbc_errno(context, &srv->cli);
+ }
+ talloc_destroy(ctx);
+ return ret;
+ }
+
+ /* Unsupported attribute name */
+ talloc_destroy(ctx);
+ errno = EINVAL;
+ return -1;
+}
+
+
+int smbc_removexattr_ctx(SMBCCTX *context,
+ const char *fname,
+ const char *name)
+{
+ int ret;
+ SMBCSRV *srv;
+ SMBCSRV *ipc_srv;
+ fstring server, share, user, password, workgroup;
+ pstring path;
+ TALLOC_CTX *ctx;
+ POLICY_HND pol;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ return -1;
+
+ }
+
+ if (!fname) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ DEBUG(4, ("smbc_removexattr(%s, %s)\n", fname, name));
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(user),
+ password, sizeof(password),
+ NULL, 0)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (user[0] == (char)0) fstrcpy(user, context->user);
+
+ fstrcpy(workgroup, context->workgroup);
+
+ srv = smbc_server(context, server, share, workgroup, user, password);
+ if (!srv) {
+ return -1; /* errno set by smbc_server */
+ }
+
+ ipc_srv = smbc_attr_server(context, server, share,
+ workgroup, user, password,
+ &pol);
+ if (!ipc_srv) {
+ return -1;
+ }
+
+ ipc_srv = smbc_attr_server(context, server, share,
+ workgroup, user, password,
+ &pol);
+ if (!ipc_srv) {
+ return -1;
+ }
+
+ ctx = talloc_init("smbc_removexattr");
+ if (!ctx) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /* Are they asking to set the entire ACL? */
+ if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.*+") == 0) {
+
+ /* Yup. */
+ ret = cacl_set(ctx, &srv->cli,
+ &ipc_srv->cli, &pol, path,
+ NULL, SMBC_XATTR_MODE_REMOVE_ALL, 0);
+ talloc_destroy(ctx);
+ return ret;
+ }
+
+ /*
+ * Are they asking to remove one or more spceific security descriptor
+ * attributes?
+ */
+ if (StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
+ StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
+ StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
+
+ /* Yup. */
+ ret = cacl_set(ctx, &srv->cli,
+ &ipc_srv->cli, &pol, path,
+ name + 19, SMBC_XATTR_MODE_REMOVE, 0);
+ talloc_destroy(ctx);
+ return ret;
+ }
+
+ /* Unsupported attribute name */
+ talloc_destroy(ctx);
+ errno = EINVAL;
+ return -1;
+}
+
+int smbc_listxattr_ctx(SMBCCTX *context,
+ const char *fname,
+ char *list,
+ size_t size)
+{
+ /*
+ * This isn't quite what listxattr() is supposed to do. This returns
+ * the complete set of attributes, always, rather than only those
+ * attribute names which actually exist for a file. Hmmm...
+ */
+ const char supported[] =
+ "system.nt_sec_desc.revision\0"
+ "system.nt_sec_desc.owner\0"
+ "system.nt_sec_desc.owner+\0"
+ "system.nt_sec_desc.group\0"
+ "system.nt_sec_desc.group+\0"
+ "system.nt_sec_desc.acl\0"
+ "system.nt_sec_desc.acl+\0"
+ "system.nt_sec_desc.*\0"
+ "system.nt_sec_desc.*+\0"
+ ;
+
+ if (size == 0) {
+ return sizeof(supported);
+ }
+
+ if (sizeof(supported) > size) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ /* this can't be strcpy() because there are embedded null characters */
+ memcpy(list, supported, sizeof(supported));
+ return sizeof(supported);
+}
+
+
+/*
+ * Open a print file to be written to by other calls
+ */
+
+static SMBCFILE *smbc_open_print_job_ctx(SMBCCTX *context, const char *fname)
+{
+ fstring server, share, user, password;
+ pstring path;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return NULL;
+
+ }
+
+ if (!fname) {
+
+ errno = EINVAL;
+ return NULL;
+
+ }
+
+ DEBUG(4, ("smbc_open_print_job_ctx(%s)\n", fname));
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(user),
+ password, sizeof(password),
+ NULL, 0)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* What if the path is empty, or the file exists? */
+
+ return context->open(context, fname, O_WRONLY, 666);
+
+}
+
+/*
+ * Routine to print a file on a remote server ...
+ *
+ * We open the file, which we assume to be on a remote server, and then
+ * copy it to a print file on the share specified by printq.
+ */
+
+static int smbc_print_file_ctx(SMBCCTX *c_file, const char *fname, SMBCCTX *c_print, const char *printq)
+{
+ SMBCFILE *fid1, *fid2;
+ int bytes, saverr, tot_bytes = 0;
+ char buf[4096];
+
+ if (!c_file || !c_file->internal->_initialized || !c_print ||
+ !c_print->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (!fname && !printq) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ /* Try to open the file for reading ... */
+
+ if ((int)(fid1 = c_file->open(c_file, fname, O_RDONLY, 0666)) < 0) {
+
+ DEBUG(3, ("Error, fname=%s, errno=%i\n", fname, errno));
+ return -1; /* smbc_open sets errno */
+
+ }
+
+ /* Now, try to open the printer file for writing */
+
+ if ((int)(fid2 = c_print->open_print_job(c_print, printq)) < 0) {
+
+ saverr = errno; /* Save errno */
+ c_file->close(c_file, fid1);
+ errno = saverr;
+ return -1;
+
+ }
+
+ while ((bytes = c_file->read(c_file, fid1, buf, sizeof(buf))) > 0) {
+
+ tot_bytes += bytes;
+
+ if ((c_print->write(c_print, fid2, buf, bytes)) < 0) {
+
+ saverr = errno;
+ c_file->close(c_file, fid1);
+ c_print->close(c_print, fid2);
+ errno = saverr;
+
+ }
+
+ }
+
+ saverr = errno;
+
+ c_file->close(c_file, fid1); /* We have to close these anyway */
+ c_print->close(c_print, fid2);
+
+ if (bytes < 0) {
+
+ errno = saverr;
+ return -1;
+
+ }
+
+ return tot_bytes;
+
+}
+
+/*
+ * Routine to list print jobs on a printer share ...
+ */
+
+static int smbc_list_print_jobs_ctx(SMBCCTX *context, const char *fname, smbc_list_print_job_fn fn)
+{
+ SMBCSRV *srv;
+ fstring server, share, user, password, workgroup;
+ pstring path;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (!fname) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ DEBUG(4, ("smbc_list_print_jobs(%s)\n", fname));
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(user),
+ password, sizeof(password),
+ NULL, 0)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (user[0] == (char)0) fstrcpy(user, context->user);
+
+ fstrcpy(workgroup, context->workgroup);
+
+ srv = smbc_server(context, server, share, workgroup, user, password);
+
+ if (!srv) {
+
+ return -1; /* errno set by smbc_server */
+
+ }
+
+ if (cli_print_queue(&srv->cli, (void (*)(struct print_job_info *))fn) < 0) {
+
+ errno = smbc_errno(context, &srv->cli);
+ return -1;
+
+ }
+
+ return 0;
+
+}
+
+/*
+ * Delete a print job from a remote printer share
+ */
+
+static int smbc_unlink_print_job_ctx(SMBCCTX *context, const char *fname, int id)
+{
+ SMBCSRV *srv;
+ fstring server, share, user, password, workgroup;
+ pstring path;
+ int err;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (!fname) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ DEBUG(4, ("smbc_unlink_print_job(%s)\n", fname));
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(user),
+ password, sizeof(password),
+ NULL, 0)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (user[0] == (char)0) fstrcpy(user, context->user);
+
+ fstrcpy(workgroup, context->workgroup);
+
+ srv = smbc_server(context, server, share, workgroup, user, password);
+
+ if (!srv) {
+
+ return -1; /* errno set by smbc_server */
+
+ }
+
+ if ((err = cli_printjob_del(&srv->cli, id)) != 0) {
+
+ if (err < 0)
+ errno = smbc_errno(context, &srv->cli);
+ else if (err == ERRnosuchprintjob)
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ return 0;
+
+}
+
+/*
+ * Get a new empty handle to fill in with your own info
+ */
+SMBCCTX * smbc_new_context(void)
+{
+ SMBCCTX * context;
+
+ context = malloc(sizeof(SMBCCTX));
+ if (!context) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ ZERO_STRUCTP(context);
+
+ context->internal = malloc(sizeof(struct smbc_internal_data));
+ if (!context->internal) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ ZERO_STRUCTP(context->internal);
+
+
+ /* ADD REASONABLE DEFAULTS */
+ context->debug = 0;
+ context->timeout = 20000; /* 20 seconds */
+
+ context->open = smbc_open_ctx;
+ context->creat = smbc_creat_ctx;
+ context->read = smbc_read_ctx;
+ context->write = smbc_write_ctx;
+ context->close = smbc_close_ctx;
+ context->unlink = smbc_unlink_ctx;
+ context->rename = smbc_rename_ctx;
+ context->lseek = smbc_lseek_ctx;
+ context->stat = smbc_stat_ctx;
+ context->fstat = smbc_fstat_ctx;
+ context->opendir = smbc_opendir_ctx;
+ context->closedir = smbc_closedir_ctx;
+ context->readdir = smbc_readdir_ctx;
+ context->getdents = smbc_getdents_ctx;
+ context->mkdir = smbc_mkdir_ctx;
+ context->rmdir = smbc_rmdir_ctx;
+ context->telldir = smbc_telldir_ctx;
+ context->lseekdir = smbc_lseekdir_ctx;
+ context->fstatdir = smbc_fstatdir_ctx;
+ context->chmod = smbc_chmod_ctx;
+ context->utimes = smbc_utimes_ctx;
+ context->setxattr = smbc_setxattr_ctx;
+ context->getxattr = smbc_getxattr_ctx;
+ context->removexattr = smbc_removexattr_ctx;
+ context->listxattr = smbc_listxattr_ctx;
+ context->open_print_job = smbc_open_print_job_ctx;
+ context->print_file = smbc_print_file_ctx;
+ context->list_print_jobs = smbc_list_print_jobs_ctx;
+ context->unlink_print_job = smbc_unlink_print_job_ctx;
+
+ context->callbacks.check_server_fn = smbc_check_server;
+ context->callbacks.remove_unused_server_fn = smbc_remove_unused_server;
+
+ smbc_default_cache_functions(context);
+
+ return context;
+}
+
+/*
+ * Free a context
+ *
+ * Returns 0 on success. Otherwise returns 1, the SMBCCTX is _not_ freed
+ * and thus you'll be leaking memory if not handled properly.
+ *
+ */
+int smbc_free_context(SMBCCTX * context, int shutdown_ctx)
+{
+ if (!context) {
+ errno = EBADF;
+ return 1;
+ }
+
+ if (shutdown_ctx) {
+ SMBCFILE * f;
+ DEBUG(1,("Performing aggressive shutdown.\n"));
+
+ f = context->internal->_files;
+ while (f) {
+ context->close(context, f);
+ f = f->next;
+ }
+ context->internal->_files = NULL;
+
+ /* First try to remove the servers the nice way. */
+ if (context->callbacks.purge_cached_fn(context)) {
+ SMBCSRV * s;
+ SMBCSRV * next;
+ DEBUG(1, ("Could not purge all servers, Nice way shutdown failed.\n"));
+ s = context->internal->_servers;
+ while (s) {
+ DEBUG(1, ("Forced shutdown: %p (fd=%d)\n", s, s->cli.fd));
+ cli_shutdown(&s->cli);
+ context->callbacks.remove_cached_srv_fn(context, s);
+ next = s->next;
+ DLIST_REMOVE(context->internal->_servers, s);
+ SAFE_FREE(s);
+ s = next;
+ }
+ context->internal->_servers = NULL;
+ }
+ }
+ else {
+ /* This is the polite way */
+ if (context->callbacks.purge_cached_fn(context)) {
+ DEBUG(1, ("Could not purge all servers, free_context failed.\n"));
+ errno = EBUSY;
+ return 1;
+ }
+ if (context->internal->_servers) {
+ DEBUG(1, ("Active servers in context, free_context failed.\n"));
+ errno = EBUSY;
+ return 1;
+ }
+ if (context->internal->_files) {
+ DEBUG(1, ("Active files in context, free_context failed.\n"));
+ errno = EBUSY;
+ return 1;
+ }
+ }
+
+ /* Things we have to clean up */
+ SAFE_FREE(context->workgroup);
+ SAFE_FREE(context->netbios_name);
+ SAFE_FREE(context->user);
+
+ DEBUG(3, ("Context %p succesfully freed\n", context));
+ SAFE_FREE(context->internal);
+ SAFE_FREE(context);
+ return 0;
+}
+
+
+/*
+ * Initialise the library etc
+ *
+ * We accept a struct containing handle information.
+ * valid values for info->debug from 0 to 100,
+ * and insist that info->fn must be non-null.
+ */
+SMBCCTX * smbc_init_context(SMBCCTX * context)
+{
+ pstring conf;
+ int pid;
+ char *user = NULL, *home = NULL;
+
+ if (!context || !context->internal) {
+ errno = EBADF;
+ return NULL;
+ }
+
+ /* Do not initialise the same client twice */
+ if (context->internal->_initialized) {
+ return 0;
+ }
+
+ if (!context->callbacks.auth_fn || context->debug < 0 || context->debug > 100) {
+
+ errno = EINVAL;
+ return NULL;
+
+ }
+
+ if (!smbc_initialized) {
+ /* Do some library wide intialisations the first time we get called */
+
+ /* Set this to what the user wants */
+ DEBUGLEVEL = context->debug;
+
+ setup_logging( "libsmbclient", True);
+
+ /* Here we would open the smb.conf file if needed ... */
+
+ home = getenv("HOME");
+
+ slprintf(conf, sizeof(conf), "%s/.smb/smb.conf", home);
+
+ load_interfaces(); /* Load the list of interfaces ... */
+
+ in_client = True; /* FIXME, make a param */
+
+ if (!lp_load(conf, True, False, False)) {
+
+ /*
+ * Well, if that failed, try the dyn_CONFIGFILE
+ * Which points to the standard locn, and if that
+ * fails, silently ignore it and use the internal
+ * defaults ...
+ */
+
+ if (!lp_load(dyn_CONFIGFILE, True, False, False)) {
+ DEBUG(5, ("Could not load either config file: %s or %s\n",
+ conf, dyn_CONFIGFILE));
+ }
+ }
+
+ reopen_logs(); /* Get logging working ... */
+
+ /*
+ * Block SIGPIPE (from lib/util_sock.c: write())
+ * It is not needed and should not stop execution
+ */
+ BlockSignals(True, SIGPIPE);
+
+ /* Done with one-time initialisation */
+ smbc_initialized = 1;
+
+ }
+
+ if (!context->user) {
+ /*
+ * FIXME: Is this the best way to get the user info?
+ */
+ user = getenv("USER");
+ /* walk around as "guest" if no username can be found */
+ if (!user) context->user = strdup("guest");
+ else context->user = strdup(user);
+ }
+
+ if (!context->netbios_name) {
+ /*
+ * We try to get our netbios name from the config. If that fails we fall
+ * back on constructing our netbios name from our hostname etc
+ */
+ if (global_myname()) {
+ context->netbios_name = strdup(global_myname());
+ }
+ else {
+ /*
+ * Hmmm, I want to get hostname as well, but I am too lazy for the moment
+ */
+ pid = sys_getpid();
+ context->netbios_name = malloc(17);
+ if (!context->netbios_name) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ slprintf(context->netbios_name, 16, "smbc%s%d", context->user, pid);
+ }
+ }
+
+ DEBUG(1, ("Using netbios name %s.\n", context->netbios_name));
+
+ if (!context->workgroup) {
+ if (lp_workgroup()) {
+ context->workgroup = strdup(lp_workgroup());
+ }
+ else {
+ /* TODO: Think about a decent default workgroup */
+ context->workgroup = strdup("samba");
+ }
+ }
+
+ DEBUG(1, ("Using workgroup %s.\n", context->workgroup));
+
+ /* shortest timeout is 1 second */
+ if (context->timeout > 0 && context->timeout < 1000)
+ context->timeout = 1000;
+
+ /*
+ * FIXME: Should we check the function pointers here?
+ */
+
+ context->internal->_initialized = 1;
+
+ return context;
+}
diff --git a/source/libsmb/namecache.c b/source/libsmb/namecache.c
new file mode 100644
index 00000000000..e3e7ac4e3c2
--- /dev/null
+++ b/source/libsmb/namecache.c
@@ -0,0 +1,321 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ NetBIOS name cache module on top of gencache mechanism.
+
+ Copyright (C) Tim Potter 2002
+ Copyright (C) Rafal Szczesniak 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#define NBTKEY_FMT "NBT/%s#%02X"
+
+
+/**
+ * Initialise namecache system. Function calls gencache
+ * initialisation function to perform necessary actions
+ *
+ * @return true upon successful initialisation of the cache or
+ * false on failure
+ **/
+
+BOOL namecache_enable(void)
+{
+ /*
+ * Check if name caching disabled by setting the name cache
+ * timeout to zero.
+ */
+
+ if (lp_name_cache_timeout() == 0) {
+ DEBUG(5, ("namecache_enable: disabling netbios name cache\n"));
+ return False;
+ }
+
+ /* Init namecache by calling gencache initialisation */
+
+ if (!gencache_init()) {
+ DEBUG(2, ("namecache_enable: Couldn't initialise namecache on top of gencache.\n"));
+ return False;
+ }
+
+ /* I leave it for now, though I don't think we really need this (mimir, 27.09.2002) */
+ DEBUG(5, ("namecache_enable: enabling netbios namecache, timeout %d "
+ "seconds\n", lp_name_cache_timeout()));
+
+ return True;
+}
+
+
+/**
+ * Shutdown namecache. Routine calls gencache close function
+ * to safely close gencache file.
+ *
+ * @return true upon successful shutdown of the cache or
+ * false on failure
+ **/
+
+BOOL namecache_shutdown(void)
+{
+ if (!gencache_shutdown()) {
+ DEBUG(2, ("namecache_shutdown: Couldn't close namecache on top of gencache.\n"));
+ return False;
+ }
+
+ DEBUG(5, ("namecache_shutdown: netbios namecache closed successfully.\n"));
+ return True;
+}
+
+
+/**
+ * Generates a key for netbios name lookups on basis of
+ * netbios name and type.
+ * The caller must free returned key string when finished.
+ *
+ * @param name netbios name string (case insensitive)
+ * @param name_type netbios type of the name being looked up
+ *
+ * @return string consisted of uppercased name and appended
+ * type number
+ */
+
+static char* namecache_key(const char *name, int name_type)
+{
+ char *keystr;
+ asprintf(&keystr, NBTKEY_FMT, strupper_static(name), name_type);
+
+ return keystr;
+}
+
+
+/**
+ * Store a name(s) in the name cache
+ *
+ * @param name netbios names array
+ * @param name_type integer netbios name type
+ * @param num_names number of names being stored
+ * @param ip_list array of in_addr structures containing
+ * ip addresses being stored
+ **/
+
+BOOL namecache_store(const char *name, int name_type,
+ int num_names, struct ip_service *ip_list)
+{
+ time_t expiry;
+ char *key, *value_string;
+ int i;
+ BOOL ret;
+
+ /*
+ * we use gecache call to avoid annoying debug messages about
+ * initialised namecache again and again...
+ */
+ if (!gencache_init()) return False;
+
+ if ( DEBUGLEVEL >= 5 ) {
+ DEBUG(5, ("namecache_store: storing %d address%s for %s#%02x: ",
+ num_names, num_names == 1 ? "": "es", name, name_type));
+
+ for (i = 0; i < num_names; i++)
+ DEBUGADD(5, ("%s:%d%s", inet_ntoa(ip_list[i].ip),
+ ip_list[i].port, (i == (num_names - 1) ? "" : ",")));
+
+ DEBUGADD(5, ("\n"));
+ }
+
+ key = namecache_key(name, name_type);
+ expiry = time(NULL) + lp_name_cache_timeout();
+
+ /*
+ * Generate string representation of ip addresses list
+ * First, store the number of ip addresses and then
+ * place each single ip
+ */
+ if (!ipstr_list_make(&value_string, ip_list, num_names)) {
+ SAFE_FREE(key);
+ SAFE_FREE(value_string);
+ return False;
+ }
+
+ /* set the entry */
+ ret = gencache_set(key, value_string, expiry);
+ SAFE_FREE(key);
+ SAFE_FREE(value_string);
+ return ret;
+}
+
+
+/**
+ * Look up a name in the cache.
+ *
+ * @param name netbios name to look up for
+ * @param name_type netbios name type of @param name
+ * @param ip_list mallocated list of IP addresses if found in the cache,
+ * NULL otherwise
+ * @param num_names number of entries found
+ *
+ * @return true upon successful fetch or
+ * false if name isn't found in the cache or has expired
+ **/
+
+BOOL namecache_fetch(const char *name, int name_type, struct ip_service **ip_list,
+ int *num_names)
+{
+ char *key, *value;
+ time_t timeout;
+
+ *num_names = 0;
+
+ /* exit now if null pointers were passed as they're required further */
+ if (!ip_list || !num_names) return False;
+
+ if (!gencache_init())
+ return False;
+
+ /*
+ * Use gencache interface - lookup the key
+ */
+ key = namecache_key(name, name_type);
+
+ if (!gencache_get(key, &value, &timeout)) {
+ DEBUG(5, ("no entry for %s#%02X found.\n", name, name_type));
+ gencache_del(key);
+ SAFE_FREE(key);
+ SAFE_FREE(value);
+ return False;
+ } else {
+ DEBUG(5, ("name %s#%02X found.\n", name, name_type));
+ }
+
+ /*
+ * Split up the stored value into the list of IP adresses
+ */
+ *num_names = ipstr_list_parse(value, ip_list);
+
+ SAFE_FREE(key);
+ SAFE_FREE(value);
+
+ return *num_names > 0; /* true only if some ip has been fetched */
+}
+
+
+/**
+ * Delete single namecache entry. Look at the
+ * gencache_iterate definition.
+ *
+ **/
+
+static void flush_netbios_name(const char* key, const char *value, time_t timeout, void* dptr)
+{
+ gencache_del(key);
+ DEBUG(5, ("Deleting entry %s\n", key));
+}
+
+
+/**
+ * Flush all names from the name cache.
+ * It's done by gencache_iterate()
+ *
+ * @return True upon successful deletion or
+ * False in case of an error
+ **/
+
+void namecache_flush(void)
+{
+ if (!gencache_init())
+ return;
+
+ /*
+ * iterate through each NBT cache's entry and flush it
+ * by flush_netbios_name function
+ */
+ gencache_iterate(flush_netbios_name, NULL, "NBT/*");
+ DEBUG(5, ("Namecache flushed\n"));
+}
+
+/* Construct a name status record key. */
+
+static char *namecache_status_record_key(const char *name, int name_type1,
+ int name_type2, struct in_addr keyip)
+{
+ char *keystr;
+
+ asprintf(&keystr, "NBT/%s#%02X.%02X.%s",
+ strupper_static(name), name_type1, name_type2, inet_ntoa(keyip));
+ return keystr;
+}
+
+/* Store a name status record. */
+
+BOOL namecache_status_store(const char *keyname, int keyname_type,
+ int name_type, struct in_addr keyip,
+ const char *srvname)
+{
+ char *key;
+ time_t expiry;
+ BOOL ret;
+
+ if (!gencache_init())
+ return False;
+
+ key = namecache_status_record_key(keyname, keyname_type, name_type, keyip);
+ if (!key)
+ return False;
+
+ expiry = time(NULL) + lp_name_cache_timeout();
+ ret = gencache_set(key, srvname, expiry);
+
+ if (ret)
+ DEBUG(5, ("namecache_status_store: entry %s -> %s\n", key, srvname ));
+ else
+ DEBUG(5, ("namecache_status_store: entry %s store failed.\n", key ));
+
+ SAFE_FREE(key);
+ return ret;
+}
+
+/* Fetch a name status record. */
+
+BOOL namecache_status_fetch(const char *keyname, int keyname_type,
+ int name_type, struct in_addr keyip, char *srvname_out)
+{
+ char *key = NULL;
+ char *value = NULL;
+ time_t timeout;
+
+ if (!gencache_init())
+ return False;
+
+ key = namecache_status_record_key(keyname, keyname_type, name_type, keyip);
+ if (!key)
+ return False;
+
+ if (!gencache_get(key, &value, &timeout)) {
+ DEBUG(5, ("namecache_status_fetch: no entry for %s found.\n", key));
+ gencache_del(key);
+ SAFE_FREE(key);
+ SAFE_FREE(value);
+ return False;
+ } else {
+ DEBUG(5, ("namecache_status_fetch: key %s -> %s\n", key, value ));
+ }
+
+ strlcpy(srvname_out, value, 16);
+ SAFE_FREE(key);
+ SAFE_FREE(value);
+ return True;
+}
diff --git a/source/libsmb/namequery.c b/source/libsmb/namequery.c
new file mode 100644
index 00000000000..b9bc4e11664
--- /dev/null
+++ b/source/libsmb/namequery.c
@@ -0,0 +1,1406 @@
+/*
+ Unix SMB/CIFS implementation.
+ name query routines
+ Copyright (C) Andrew Tridgell 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+
+/* nmbd.c sets this to True. */
+BOOL global_in_nmbd = False;
+
+/****************************************************************************
+ Generate a random trn_id.
+****************************************************************************/
+
+static int generate_trn_id(void)
+{
+ static int trn_id;
+
+ if (trn_id == 0) {
+ sys_srandom(sys_getpid());
+ }
+
+ trn_id = sys_random();
+
+ return trn_id % (unsigned)0x7FFF;
+}
+
+/****************************************************************************
+ Parse a node status response into an array of structures.
+****************************************************************************/
+
+static struct node_status *parse_node_status(char *p, int *num_names)
+{
+ struct node_status *ret;
+ int i;
+
+ *num_names = CVAL(p,0);
+
+ if (*num_names == 0)
+ return NULL;
+
+ ret = (struct node_status *)malloc(sizeof(struct node_status)* (*num_names));
+ if (!ret) return NULL;
+
+ p++;
+ for (i=0;i< *num_names;i++) {
+ StrnCpy(ret[i].name,p,15);
+ trim_char(ret[i].name,'\0',' ');
+ ret[i].type = CVAL(p,15);
+ ret[i].flags = p[16];
+ p += 18;
+ DEBUG(10, ("%s#%02x: flags = 0x%02x\n", ret[i].name,
+ ret[i].type, ret[i].flags));
+ }
+ return ret;
+}
+
+
+/****************************************************************************
+ Do a NBT node status query on an open socket and return an array of
+ structures holding the returned names or NULL if the query failed.
+**************************************************************************/
+
+struct node_status *node_status_query(int fd,struct nmb_name *name,
+ struct in_addr to_ip, int *num_names)
+{
+ BOOL found=False;
+ int retries = 2;
+ int retry_time = 2000;
+ struct timeval tval;
+ struct packet_struct p;
+ struct packet_struct *p2;
+ struct nmb_packet *nmb = &p.packet.nmb;
+ struct node_status *ret;
+
+ ZERO_STRUCT(p);
+
+ nmb->header.name_trn_id = generate_trn_id();
+ nmb->header.opcode = 0;
+ nmb->header.response = False;
+ nmb->header.nm_flags.bcast = False;
+ nmb->header.nm_flags.recursion_available = False;
+ nmb->header.nm_flags.recursion_desired = False;
+ nmb->header.nm_flags.trunc = False;
+ nmb->header.nm_flags.authoritative = False;
+ nmb->header.rcode = 0;
+ nmb->header.qdcount = 1;
+ nmb->header.ancount = 0;
+ nmb->header.nscount = 0;
+ nmb->header.arcount = 0;
+ nmb->question.question_name = *name;
+ nmb->question.question_type = 0x21;
+ nmb->question.question_class = 0x1;
+
+ p.ip = to_ip;
+ p.port = NMB_PORT;
+ p.fd = fd;
+ p.timestamp = time(NULL);
+ p.packet_type = NMB_PACKET;
+
+ GetTimeOfDay(&tval);
+
+ if (!send_packet(&p))
+ return NULL;
+
+ retries--;
+
+ while (1) {
+ struct timeval tval2;
+ GetTimeOfDay(&tval2);
+ if (TvalDiff(&tval,&tval2) > retry_time) {
+ if (!retries)
+ break;
+ if (!found && !send_packet(&p))
+ return NULL;
+ GetTimeOfDay(&tval);
+ retries--;
+ }
+
+ if ((p2=receive_nmb_packet(fd,90,nmb->header.name_trn_id))) {
+ struct nmb_packet *nmb2 = &p2->packet.nmb;
+ debug_nmb_packet(p2);
+
+ if (nmb2->header.opcode != 0 ||
+ nmb2->header.nm_flags.bcast ||
+ nmb2->header.rcode ||
+ !nmb2->header.ancount ||
+ nmb2->answers->rr_type != 0x21) {
+ /* XXXX what do we do with this? could be a
+ redirect, but we'll discard it for the
+ moment */
+ free_packet(p2);
+ continue;
+ }
+
+ ret = parse_node_status(&nmb2->answers->rdata[0], num_names);
+ free_packet(p2);
+ return ret;
+ }
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Find the first type XX name in a node status reply - used for finding
+ a servers name given its IP. Return the matched name in *name.
+**************************************************************************/
+
+BOOL name_status_find(const char *q_name, int q_type, int type, struct in_addr to_ip, fstring name)
+{
+ struct node_status *status = NULL;
+ struct nmb_name nname;
+ int count, i;
+ int sock;
+ BOOL result = False;
+
+ if (lp_disable_netbios()) {
+ DEBUG(5,("name_status_find(%s#%02x): netbios is disabled\n", q_name, q_type));
+ return False;
+ }
+
+ DEBUG(10, ("name_status_find: looking up %s#%02x at %s\n", q_name,
+ q_type, inet_ntoa(to_ip)));
+
+ /* Check the cache first. */
+
+ if (namecache_status_fetch(q_name, q_type, type, to_ip, name))
+ return True;
+
+ sock = open_socket_in(SOCK_DGRAM, 0, 3, interpret_addr(lp_socket_address()), True);
+ if (sock == -1)
+ goto done;
+
+ /* W2K PDC's seem not to respond to '*'#0. JRA */
+ make_nmb_name(&nname, q_name, q_type);
+ status = node_status_query(sock, &nname, to_ip, &count);
+ close(sock);
+ if (!status)
+ goto done;
+
+ for (i=0;i<count;i++) {
+ if (status[i].type == type)
+ break;
+ }
+ if (i == count)
+ goto done;
+
+ pull_ascii_nstring(name, sizeof(fstring), status[i].name);
+
+ /* Store the result in the cache. */
+ /* but don't store an entry for 0x1c names here. Here we have
+ a single host and DOMAIN<0x1c> names should be a list of hosts */
+
+ if ( q_type != 0x1c )
+ namecache_status_store(q_name, q_type, type, to_ip, name);
+
+ result = True;
+
+ done:
+ SAFE_FREE(status);
+
+ DEBUG(10, ("name_status_find: name %sfound", result ? "" : "not "));
+
+ if (result)
+ DEBUGADD(10, (", name %s ip address is %s", name, inet_ntoa(to_ip)));
+
+ DEBUG(10, ("\n"));
+
+ return result;
+}
+
+/*
+ comparison function used by sort_ip_list
+*/
+
+static int ip_compare(struct in_addr *ip1, struct in_addr *ip2)
+{
+ int max_bits1=0, max_bits2=0;
+ int num_interfaces = iface_count();
+ int i;
+
+ for (i=0;i<num_interfaces;i++) {
+ struct in_addr ip;
+ int bits1, bits2;
+ ip = *iface_n_bcast(i);
+ bits1 = matching_quad_bits((uchar *)&ip1->s_addr, (uchar *)&ip.s_addr);
+ bits2 = matching_quad_bits((uchar *)&ip2->s_addr, (uchar *)&ip.s_addr);
+ max_bits1 = MAX(bits1, max_bits1);
+ max_bits2 = MAX(bits2, max_bits2);
+ }
+
+ /* bias towards directly reachable IPs */
+ if (iface_local(*ip1)) {
+ max_bits1 += 32;
+ }
+ if (iface_local(*ip2)) {
+ max_bits2 += 32;
+ }
+
+ return max_bits2 - max_bits1;
+}
+
+/*******************************************************************
+ compare 2 ldap IPs by nearness to our interfaces - used in qsort
+*******************************************************************/
+
+static int ip_service_compare(struct ip_service *ip1, struct ip_service *ip2)
+{
+ int result;
+
+ if ( (result = ip_compare(&ip1->ip, &ip2->ip)) != 0 )
+ return result;
+
+ if ( ip1->port > ip2->port )
+ return 1;
+
+ if ( ip1->port < ip2->port )
+ return -1;
+
+ return 0;
+}
+
+/*
+ sort an IP list so that names that are close to one of our interfaces
+ are at the top. This prevents the problem where a WINS server returns an IP that
+ is not reachable from our subnet as the first match
+*/
+
+static void sort_ip_list(struct in_addr *iplist, int count)
+{
+ if (count <= 1) {
+ return;
+ }
+
+ qsort(iplist, count, sizeof(struct in_addr), QSORT_CAST ip_compare);
+}
+
+static void sort_ip_list2(struct ip_service *iplist, int count)
+{
+ if (count <= 1) {
+ return;
+ }
+
+ qsort(iplist, count, sizeof(struct ip_service), QSORT_CAST ip_service_compare);
+}
+
+/**********************************************************************
+ Remove any duplicate address/port pairs in the list
+ *********************************************************************/
+
+static int remove_duplicate_addrs2( struct ip_service *iplist, int count )
+{
+ int i, j;
+
+ DEBUG(10,("remove_duplicate_addrs2: looking for duplicate address/port pairs\n"));
+
+ /* one loop to remove duplicates */
+ for ( i=0; i<count; i++ ) {
+ if ( is_zero_ip(iplist[i].ip) )
+ continue;
+
+ for ( j=i+1; j<count; j++ ) {
+ if ( ip_service_equal(iplist[i], iplist[j]) )
+ zero_ip(&iplist[j].ip);
+ }
+ }
+
+ /* one loop to clean up any holes we left */
+ /* first ip should never be a zero_ip() */
+ for (i = 0; i<count; ) {
+ if ( is_zero_ip(iplist[i].ip) ) {
+ if (i != count-1 )
+ memmove(&iplist[i], &iplist[i+1], (count - i - 1)*sizeof(iplist[i]));
+ count--;
+ continue;
+ }
+ i++;
+ }
+
+ return count;
+}
+
+/****************************************************************************
+ Do a netbios name query to find someones IP.
+ Returns an array of IP addresses or NULL if none.
+ *count will be set to the number of addresses returned.
+ *timed_out is set if we failed by timing out
+****************************************************************************/
+
+struct in_addr *name_query(int fd,const char *name,int name_type,
+ BOOL bcast,BOOL recurse,
+ struct in_addr to_ip, int *count, int *flags,
+ BOOL *timed_out)
+{
+ BOOL found=False;
+ int i, retries = 3;
+ int retry_time = bcast?250:2000;
+ struct timeval tval;
+ struct packet_struct p;
+ struct packet_struct *p2;
+ struct nmb_packet *nmb = &p.packet.nmb;
+ struct in_addr *ip_list = NULL;
+
+ if (lp_disable_netbios()) {
+ DEBUG(5,("name_query(%s#%02x): netbios is disabled\n", name, name_type));
+ return NULL;
+ }
+
+ if (timed_out) {
+ *timed_out = False;
+ }
+
+ memset((char *)&p,'\0',sizeof(p));
+ (*count) = 0;
+ (*flags) = 0;
+
+ nmb->header.name_trn_id = generate_trn_id();
+ nmb->header.opcode = 0;
+ nmb->header.response = False;
+ nmb->header.nm_flags.bcast = bcast;
+ nmb->header.nm_flags.recursion_available = False;
+ nmb->header.nm_flags.recursion_desired = recurse;
+ nmb->header.nm_flags.trunc = False;
+ nmb->header.nm_flags.authoritative = False;
+ nmb->header.rcode = 0;
+ nmb->header.qdcount = 1;
+ nmb->header.ancount = 0;
+ nmb->header.nscount = 0;
+ nmb->header.arcount = 0;
+
+ make_nmb_name(&nmb->question.question_name,name,name_type);
+
+ nmb->question.question_type = 0x20;
+ nmb->question.question_class = 0x1;
+
+ p.ip = to_ip;
+ p.port = NMB_PORT;
+ p.fd = fd;
+ p.timestamp = time(NULL);
+ p.packet_type = NMB_PACKET;
+
+ GetTimeOfDay(&tval);
+
+ if (!send_packet(&p))
+ return NULL;
+
+ retries--;
+
+ while (1) {
+ struct timeval tval2;
+ struct in_addr *tmp_ip_list;
+
+ GetTimeOfDay(&tval2);
+ if (TvalDiff(&tval,&tval2) > retry_time) {
+ if (!retries)
+ break;
+ if (!found && !send_packet(&p))
+ return NULL;
+ GetTimeOfDay(&tval);
+ retries--;
+ }
+
+ if ((p2=receive_nmb_packet(fd,90,nmb->header.name_trn_id))) {
+ struct nmb_packet *nmb2 = &p2->packet.nmb;
+ debug_nmb_packet(p2);
+
+ /* If we get a Negative Name Query Response from a WINS
+ * server, we should report it and give up.
+ */
+ if( 0 == nmb2->header.opcode /* A query response */
+ && !(bcast) /* from a WINS server */
+ && nmb2->header.rcode /* Error returned */
+ ) {
+
+ if( DEBUGLVL( 3 ) ) {
+ /* Only executed if DEBUGLEVEL >= 3 */
+ dbgtext( "Negative name query response, rcode 0x%02x: ", nmb2->header.rcode );
+ switch( nmb2->header.rcode ) {
+ case 0x01:
+ dbgtext( "Request was invalidly formatted.\n" );
+ break;
+ case 0x02:
+ dbgtext( "Problem with NBNS, cannot process name.\n");
+ break;
+ case 0x03:
+ dbgtext( "The name requested does not exist.\n" );
+ break;
+ case 0x04:
+ dbgtext( "Unsupported request error.\n" );
+ break;
+ case 0x05:
+ dbgtext( "Query refused error.\n" );
+ break;
+ default:
+ dbgtext( "Unrecognized error code.\n" );
+ break;
+ }
+ }
+ free_packet(p2);
+ return( NULL );
+ }
+
+ if (nmb2->header.opcode != 0 ||
+ nmb2->header.nm_flags.bcast ||
+ nmb2->header.rcode ||
+ !nmb2->header.ancount) {
+ /*
+ * XXXX what do we do with this? Could be a
+ * redirect, but we'll discard it for the
+ * moment.
+ */
+ free_packet(p2);
+ continue;
+ }
+
+ tmp_ip_list = (struct in_addr *)Realloc( ip_list, sizeof( ip_list[0] )
+ * ( (*count) + nmb2->answers->rdlength/6 ) );
+
+ if (!tmp_ip_list) {
+ DEBUG(0,("name_query: Realloc failed.\n"));
+ SAFE_FREE(ip_list);
+ }
+
+ ip_list = tmp_ip_list;
+
+ if (ip_list) {
+ DEBUG(2,("Got a positive name query response from %s ( ", inet_ntoa(p2->ip)));
+ for (i=0;i<nmb2->answers->rdlength/6;i++) {
+ putip((char *)&ip_list[(*count)],&nmb2->answers->rdata[2+i*6]);
+ DEBUGADD(2,("%s ",inet_ntoa(ip_list[(*count)])));
+ (*count)++;
+ }
+ DEBUGADD(2,(")\n"));
+ }
+
+ found=True;
+ retries=0;
+ /* We add the flags back ... */
+ if (nmb2->header.response)
+ (*flags) |= NM_FLAGS_RS;
+ if (nmb2->header.nm_flags.authoritative)
+ (*flags) |= NM_FLAGS_AA;
+ if (nmb2->header.nm_flags.trunc)
+ (*flags) |= NM_FLAGS_TC;
+ if (nmb2->header.nm_flags.recursion_desired)
+ (*flags) |= NM_FLAGS_RD;
+ if (nmb2->header.nm_flags.recursion_available)
+ (*flags) |= NM_FLAGS_RA;
+ if (nmb2->header.nm_flags.bcast)
+ (*flags) |= NM_FLAGS_B;
+ free_packet(p2);
+ /*
+ * If we're doing a unicast lookup we only
+ * expect one reply. Don't wait the full 2
+ * seconds if we got one. JRA.
+ */
+ if(!bcast && found)
+ break;
+ }
+ }
+
+ /* only set timed_out if we didn't fund what we where looking for*/
+
+ if ( !found && timed_out ) {
+ *timed_out = True;
+ }
+
+ /* sort the ip list so we choose close servers first if possible */
+ sort_ip_list(ip_list, *count);
+
+ return ip_list;
+}
+
+/********************************************************
+ Start parsing the lmhosts file.
+*********************************************************/
+
+XFILE *startlmhosts(char *fname)
+{
+ XFILE *fp = x_fopen(fname,O_RDONLY, 0);
+ if (!fp) {
+ DEBUG(4,("startlmhosts: Can't open lmhosts file %s. Error was %s\n",
+ fname, strerror(errno)));
+ return NULL;
+ }
+ return fp;
+}
+
+/********************************************************
+ Parse the next line in the lmhosts file.
+*********************************************************/
+
+BOOL getlmhostsent( XFILE *fp, pstring name, int *name_type, struct in_addr *ipaddr)
+{
+ pstring line;
+
+ while(!x_feof(fp) && !x_ferror(fp)) {
+ pstring ip,flags,extra;
+ const char *ptr;
+ char *ptr1;
+ int count = 0;
+
+ *name_type = -1;
+
+ if (!fgets_slash(line,sizeof(pstring),fp))
+ continue;
+
+ if (*line == '#')
+ continue;
+
+ pstrcpy(ip,"");
+ pstrcpy(name,"");
+ pstrcpy(flags,"");
+
+ ptr = line;
+
+ if (next_token(&ptr,ip ,NULL,sizeof(ip)))
+ ++count;
+ if (next_token(&ptr,name ,NULL, sizeof(pstring)))
+ ++count;
+ if (next_token(&ptr,flags,NULL, sizeof(flags)))
+ ++count;
+ if (next_token(&ptr,extra,NULL, sizeof(extra)))
+ ++count;
+
+ if (count <= 0)
+ continue;
+
+ if (count > 0 && count < 2)
+ {
+ DEBUG(0,("getlmhostsent: Ill formed hosts line [%s]\n",line));
+ continue;
+ }
+
+ if (count >= 4)
+ {
+ DEBUG(0,("getlmhostsent: too many columns in lmhosts file (obsolete syntax)\n"));
+ continue;
+ }
+
+ DEBUG(4, ("getlmhostsent: lmhost entry: %s %s %s\n", ip, name, flags));
+
+ if (strchr_m(flags,'G') || strchr_m(flags,'S'))
+ {
+ DEBUG(0,("getlmhostsent: group flag in lmhosts ignored (obsolete)\n"));
+ continue;
+ }
+
+ *ipaddr = *interpret_addr2(ip);
+
+ /* Extra feature. If the name ends in '#XX', where XX is a hex number,
+ then only add that name type. */
+ if((ptr1 = strchr_m(name, '#')) != NULL)
+ {
+ char *endptr;
+
+ ptr1++;
+ *name_type = (int)strtol(ptr1, &endptr, 16);
+
+ if(!*ptr1 || (endptr == ptr1))
+ {
+ DEBUG(0,("getlmhostsent: invalid name %s containing '#'.\n", name));
+ continue;
+ }
+
+ *(--ptr1) = '\0'; /* Truncate at the '#' */
+ }
+
+ return True;
+ }
+
+ return False;
+}
+
+/********************************************************
+ Finish parsing the lmhosts file.
+*********************************************************/
+
+void endlmhosts(XFILE *fp)
+{
+ x_fclose(fp);
+}
+
+/********************************************************
+ convert an array if struct in_addrs to struct ip_service
+ return False on failure. Port is set to PORT_NONE;
+*********************************************************/
+
+static BOOL convert_ip2service( struct ip_service **return_iplist, struct in_addr *ip_list, int count )
+{
+ int i;
+
+ if ( count==0 || !ip_list )
+ return False;
+
+ /* copy the ip address; port will be PORT_NONE */
+ if ( (*return_iplist = (struct ip_service*)malloc(count*sizeof(struct ip_service))) == NULL ) {
+ DEBUG(0,("convert_ip2service: malloc failed for %d enetries!\n", count ));
+ return False;
+ }
+
+ for ( i=0; i<count; i++ ) {
+ (*return_iplist)[i].ip = ip_list[i];
+ (*return_iplist)[i].port = PORT_NONE;
+ }
+
+ return True;
+}
+/********************************************************
+ Resolve via "bcast" method.
+*********************************************************/
+
+BOOL name_resolve_bcast(const char *name, int name_type,
+ struct ip_service **return_iplist, int *return_count)
+{
+ int sock, i;
+ int num_interfaces = iface_count();
+ struct in_addr *ip_list;
+ BOOL ret;
+
+ if (lp_disable_netbios()) {
+ DEBUG(5,("name_resolve_bcast(%s#%02x): netbios is disabled\n", name, name_type));
+ return False;
+ }
+
+ *return_iplist = NULL;
+ *return_count = 0;
+
+ /*
+ * "bcast" means do a broadcast lookup on all the local interfaces.
+ */
+
+ DEBUG(3,("name_resolve_bcast: Attempting broadcast lookup for name %s<0x%x>\n", name, name_type));
+
+ sock = open_socket_in( SOCK_DGRAM, 0, 3,
+ interpret_addr(lp_socket_address()), True );
+
+ if (sock == -1) return False;
+
+ set_socket_options(sock,"SO_BROADCAST");
+ /*
+ * Lookup the name on all the interfaces, return on
+ * the first successful match.
+ */
+ for( i = num_interfaces-1; i >= 0; i--) {
+ struct in_addr sendto_ip;
+ int flags;
+ /* Done this way to fix compiler error on IRIX 5.x */
+ sendto_ip = *iface_n_bcast(i);
+ ip_list = name_query(sock, name, name_type, True,
+ True, sendto_ip, return_count, &flags, NULL);
+ if( ip_list )
+ goto success;
+ }
+
+ /* failed - no response */
+
+ close(sock);
+ return False;
+
+success:
+ ret = True;
+ if ( !convert_ip2service(return_iplist, ip_list, *return_count) )
+ ret = False;
+
+ SAFE_FREE( ip_list );
+ close(sock);
+ return ret;
+}
+
+/********************************************************
+ Resolve via "wins" method.
+*********************************************************/
+
+BOOL resolve_wins(const char *name, int name_type,
+ struct ip_service **return_iplist, int *return_count)
+{
+ int sock, t, i;
+ char **wins_tags;
+ struct in_addr src_ip, *ip_list = NULL;
+ BOOL ret;
+
+ if (lp_disable_netbios()) {
+ DEBUG(5,("resolve_wins(%s#%02x): netbios is disabled\n", name, name_type));
+ return False;
+ }
+
+ *return_iplist = NULL;
+ *return_count = 0;
+
+ DEBUG(3,("resolve_wins: Attempting wins lookup for name %s<0x%x>\n", name, name_type));
+
+ if (wins_srv_count() < 1) {
+ DEBUG(3,("resolve_wins: WINS server resolution selected and no WINS servers listed.\n"));
+ return False;
+ }
+
+ /* we try a lookup on each of the WINS tags in turn */
+ wins_tags = wins_srv_tags();
+
+ if (!wins_tags) {
+ /* huh? no tags?? give up in disgust */
+ return False;
+ }
+
+ /* the address we will be sending from */
+ src_ip = *interpret_addr2(lp_socket_address());
+
+ /* in the worst case we will try every wins server with every
+ tag! */
+ for (t=0; wins_tags && wins_tags[t]; t++) {
+ int srv_count = wins_srv_count_tag(wins_tags[t]);
+ for (i=0; i<srv_count; i++) {
+ struct in_addr wins_ip;
+ int flags;
+ BOOL timed_out;
+
+ wins_ip = wins_srv_ip_tag(wins_tags[t], src_ip);
+
+ if (global_in_nmbd && ismyip(wins_ip)) {
+ /* yikes! we'll loop forever */
+ continue;
+ }
+
+ /* skip any that have been unresponsive lately */
+ if (wins_srv_is_dead(wins_ip, src_ip)) {
+ continue;
+ }
+
+ DEBUG(3,("resolve_wins: using WINS server %s and tag '%s'\n", inet_ntoa(wins_ip), wins_tags[t]));
+
+ sock = open_socket_in(SOCK_DGRAM, 0, 3, src_ip.s_addr, True);
+ if (sock == -1) {
+ continue;
+ }
+
+ ip_list = name_query(sock,name,name_type, False,
+ True, wins_ip, return_count, &flags,
+ &timed_out);
+
+ /* exit loop if we got a list of addresses */
+
+ if ( ip_list )
+ goto success;
+
+ close(sock);
+
+ if (timed_out) {
+ /* Timed out wating for WINS server to respond. Mark it dead. */
+ wins_srv_died(wins_ip, src_ip);
+ } else {
+ /* The name definately isn't in this
+ group of WINS servers. goto the next group */
+ break;
+ }
+ }
+ }
+
+ wins_srv_tags_free(wins_tags);
+ return False;
+
+success:
+ ret = True;
+ if ( !convert_ip2service( return_iplist, ip_list, *return_count ) )
+ ret = False;
+
+ SAFE_FREE( ip_list );
+ wins_srv_tags_free(wins_tags);
+ close(sock);
+
+ return ret;
+}
+
+/********************************************************
+ Resolve via "lmhosts" method.
+*********************************************************/
+
+static BOOL resolve_lmhosts(const char *name, int name_type,
+ struct ip_service **return_iplist, int *return_count)
+{
+ /*
+ * "lmhosts" means parse the local lmhosts file.
+ */
+
+ XFILE *fp;
+ pstring lmhost_name;
+ int name_type2;
+ struct in_addr return_ip;
+
+ *return_iplist = NULL;
+ *return_count = 0;
+
+ DEBUG(3,("resolve_lmhosts: Attempting lmhosts lookup for name %s<0x%x>\n", name, name_type));
+
+ fp = startlmhosts(dyn_LMHOSTSFILE);
+ if(fp) {
+ while (getlmhostsent(fp, lmhost_name, &name_type2, &return_ip)) {
+ if (strequal(name, lmhost_name) &&
+ ((name_type2 == -1) || (name_type == name_type2))
+ ) {
+ endlmhosts(fp);
+ if ( (*return_iplist = (struct ip_service *)malloc(sizeof(struct ip_service))) == NULL ) {
+ DEBUG(3,("resolve_lmhosts: malloc fail !\n"));
+ return False;
+ }
+ (*return_iplist)[0].ip = return_ip;
+ (*return_iplist)[0].port = PORT_NONE;
+ *return_count = 1;
+ return True;
+ }
+ }
+ endlmhosts(fp);
+ }
+ return False;
+}
+
+
+/********************************************************
+ Resolve via "hosts" method.
+*********************************************************/
+
+static BOOL resolve_hosts(const char *name, int name_type,
+ struct ip_service **return_iplist, int *return_count)
+{
+ /*
+ * "host" means do a localhost, or dns lookup.
+ */
+ struct hostent *hp;
+
+ if ( name_type != 0x20 && name_type != 0x0) {
+ DEBUG(5, ("resolve_hosts: not appropriate for name type <0x%x>\n", name_type));
+ return False;
+ }
+
+ *return_iplist = NULL;
+ *return_count = 0;
+
+ DEBUG(3,("resolve_hosts: Attempting host lookup for name %s<0x%x>\n", name, name_type));
+
+ if (((hp = sys_gethostbyname(name)) != NULL) && (hp->h_addr != NULL)) {
+ struct in_addr return_ip;
+ putip((char *)&return_ip,(char *)hp->h_addr);
+ *return_iplist = (struct ip_service *)malloc(sizeof(struct ip_service));
+ if(*return_iplist == NULL) {
+ DEBUG(3,("resolve_hosts: malloc fail !\n"));
+ return False;
+ }
+ (*return_iplist)->ip = return_ip;
+ (*return_iplist)->port = PORT_NONE;
+ *return_count = 1;
+ return True;
+ }
+ return False;
+}
+
+/********************************************************
+ Resolve via "ADS" method.
+*********************************************************/
+
+static BOOL resolve_ads(const char *name, int name_type,
+ struct ip_service **return_iplist, int *return_count)
+{
+
+#ifdef HAVE_ADS
+ if ( name_type == 0x1c ) {
+ int count, i = 0;
+ char *list = NULL;
+ const char *ptr;
+ pstring tok;
+
+ /* try to lookup the _ldap._tcp.<domain> if we are using ADS */
+ if ( lp_security() != SEC_ADS )
+ return False;
+
+ DEBUG(5,("resolve_hosts: Attempting to resolve DC's for %s using DNS\n",
+ name));
+
+ if (ldap_domain2hostlist(name, &list) != LDAP_SUCCESS)
+ return False;
+
+ count = count_chars(list, ' ') + 1;
+ if ( (*return_iplist = malloc(count * sizeof(struct ip_service))) == NULL ) {
+ DEBUG(0,("resolve_hosts: malloc failed for %d entries\n", count ));
+ return False;
+ }
+
+ ptr = list;
+ while (next_token(&ptr, tok, " ", sizeof(tok))) {
+ unsigned port = LDAP_PORT;
+ char *p = strchr(tok, ':');
+ if (p) {
+ *p = 0;
+ port = atoi(p+1);
+ }
+ (*return_iplist)[i].ip = *interpret_addr2(tok);
+ (*return_iplist)[i].port = port;
+
+ /* make sure it is a valid IP. I considered checking the negative
+ connection cache, but this is the wrong place for it. Maybe only
+ as a hac. After think about it, if all of the IP addresses retuend
+ from DNS are dead, what hope does a netbios name lookup have?
+ The standard reason for falling back to netbios lookups is that
+ our DNS server doesn't know anything about the DC's -- jerry */
+
+ if ( is_zero_ip((*return_iplist)[i].ip) )
+ continue;
+
+ i++;
+ }
+ SAFE_FREE(list);
+
+ *return_count = i;
+
+ return True;
+ } else
+#endif /* HAVE_ADS */
+ {
+ return False;
+ }
+}
+
+/*******************************************************************
+ Internal interface to resolve a name into an IP address.
+ Use this function if the string is either an IP address, DNS
+ or host name or NetBIOS name. This uses the name switch in the
+ smb.conf to determine the order of name resolution.
+
+ Added support for ip addr/port to support ADS ldap servers.
+ the only place we currently care about the port is in the
+ resolve_hosts() when looking up DC's via SRV RR entries in DNS
+**********************************************************************/
+
+static BOOL internal_resolve_name(const char *name, int name_type,
+ struct ip_service **return_iplist,
+ int *return_count, const char *resolve_order)
+{
+ pstring name_resolve_list;
+ fstring tok;
+ const char *ptr;
+ BOOL allones = (strcmp(name,"255.255.255.255") == 0);
+ BOOL allzeros = (strcmp(name,"0.0.0.0") == 0);
+ BOOL is_address = is_ipaddress(name);
+ BOOL result = False;
+ int i;
+
+ *return_iplist = NULL;
+ *return_count = 0;
+
+ DEBUG(10, ("internal_resolve_name: looking up %s#%x\n", name, name_type));
+
+ if (allzeros || allones || is_address) {
+
+ if ( (*return_iplist = (struct ip_service *)malloc(sizeof(struct ip_service))) == NULL ) {
+ DEBUG(0,("internal_resolve_name: malloc fail !\n"));
+ return False;
+ }
+
+ if(is_address) {
+ /* ignore the port here */
+ (*return_iplist)->port = PORT_NONE;
+
+ /* if it's in the form of an IP address then get the lib to interpret it */
+ if (((*return_iplist)->ip.s_addr = inet_addr(name)) == 0xFFFFFFFF ){
+ DEBUG(1,("internal_resolve_name: inet_addr failed on %s\n", name));
+ return False;
+ }
+ } else {
+ (*return_iplist)->ip.s_addr = allones ? 0xFFFFFFFF : 0;
+ }
+ *return_count = 1;
+ return True;
+ }
+
+ /* Check name cache */
+
+ if (namecache_fetch(name, name_type, return_iplist, return_count)) {
+ /* This could be a negative response */
+ return (*return_count > 0);
+ }
+
+ /* set the name resolution order */
+
+ if ( strcmp( resolve_order, "NULL") == 0 ) {
+ DEBUG(8,("internal_resolve_name: all lookups disabled\n"));
+ return False;
+ }
+
+ if ( !resolve_order )
+ pstrcpy(name_resolve_list, lp_name_resolve_order());
+ else
+ pstrcpy(name_resolve_list, resolve_order);
+
+ if ( !name_resolve_list[0] )
+ ptr = "host";
+ else
+ ptr = name_resolve_list;
+
+ /* iterate through the name resolution backends */
+
+ while (next_token(&ptr, tok, LIST_SEP, sizeof(tok))) {
+ if((strequal(tok, "host") || strequal(tok, "hosts"))) {
+ if (resolve_hosts(name, name_type, return_iplist, return_count)) {
+ result = True;
+ goto done;
+ }
+ } else if(strequal( tok, "ads")) {
+ /* deal with 0x1c names here. This will result in a
+ SRV record lookup for _ldap._tcp.<domain> if we
+ are using 'security = ads' */
+ if (resolve_ads(name, name_type, return_iplist, return_count)) {
+ result = True;
+ goto done;
+ }
+ } else if(strequal( tok, "lmhosts")) {
+ if (resolve_lmhosts(name, name_type, return_iplist, return_count)) {
+ result = True;
+ goto done;
+ }
+ } else if(strequal( tok, "wins")) {
+ /* don't resolve 1D via WINS */
+ if (name_type != 0x1D &&
+ resolve_wins(name, name_type, return_iplist, return_count)) {
+ result = True;
+ goto done;
+ }
+ } else if(strequal( tok, "bcast")) {
+ if (name_resolve_bcast(name, name_type, return_iplist, return_count)) {
+ result = True;
+ goto done;
+ }
+ } else {
+ DEBUG(0,("resolve_name: unknown name switch type %s\n", tok));
+ }
+ }
+
+ /* All of the resolve_* functions above have returned false. */
+
+ SAFE_FREE(*return_iplist);
+ *return_count = 0;
+
+ return False;
+
+ done:
+
+ /* Remove duplicate entries. Some queries, notably #1c (domain
+ controllers) return the PDC in iplist[0] and then all domain
+ controllers including the PDC in iplist[1..n]. Iterating over
+ the iplist when the PDC is down will cause two sets of timeouts. */
+
+ if ( *return_count ) {
+ *return_count = remove_duplicate_addrs2( *return_iplist, *return_count );
+ }
+
+ /* Save in name cache */
+ if ( DEBUGLEVEL >= 100 ) {
+ for (i = 0; i < *return_count && DEBUGLEVEL == 100; i++)
+ DEBUG(100, ("Storing name %s of type %d (%s:%d)\n", name,
+ name_type, inet_ntoa((*return_iplist)[i].ip), (*return_iplist)[i].port));
+ }
+
+ namecache_store(name, name_type, *return_count, *return_iplist);
+
+ /* Display some debugging info */
+
+ if ( DEBUGLEVEL >= 10 ) {
+ DEBUG(10, ("internal_resolve_name: returning %d addresses: ",
+ *return_count));
+
+ for (i = 0; i < *return_count; i++)
+ DEBUGADD(10, ("%s:%d ", inet_ntoa((*return_iplist)[i].ip), (*return_iplist)[i].port));
+
+ DEBUG(10, ("\n"));
+ }
+
+ return result;
+}
+
+/********************************************************
+ Internal interface to resolve a name into one IP address.
+ Use this function if the string is either an IP address, DNS
+ or host name or NetBIOS name. This uses the name switch in the
+ smb.conf to determine the order of name resolution.
+*********************************************************/
+
+BOOL resolve_name(const char *name, struct in_addr *return_ip, int name_type)
+{
+ struct ip_service *ip_list = NULL;
+ int count = 0;
+
+ if (is_ipaddress(name)) {
+ *return_ip = *interpret_addr2(name);
+ return True;
+ }
+
+ if (internal_resolve_name(name, name_type, &ip_list, &count, lp_name_resolve_order())) {
+ int i;
+
+ /* only return valid addresses for TCP connections */
+ for (i=0; i<count; i++) {
+ char *ip_str = inet_ntoa(ip_list[i].ip);
+ if (ip_str &&
+ strcmp(ip_str, "255.255.255.255") != 0 &&
+ strcmp(ip_str, "0.0.0.0") != 0)
+ {
+ *return_ip = ip_list[i].ip;
+ SAFE_FREE(ip_list);
+ return True;
+ }
+ }
+ }
+
+ SAFE_FREE(ip_list);
+ return False;
+}
+
+/********************************************************
+ Find the IP address of the master browser or DMB for a workgroup.
+*********************************************************/
+
+BOOL find_master_ip(const char *group, struct in_addr *master_ip)
+{
+ struct ip_service *ip_list = NULL;
+ int count = 0;
+
+ if (lp_disable_netbios()) {
+ DEBUG(5,("find_master_ip(%s): netbios is disabled\n", group));
+ return False;
+ }
+
+ if (internal_resolve_name(group, 0x1D, &ip_list, &count, lp_name_resolve_order())) {
+ *master_ip = ip_list[0].ip;
+ SAFE_FREE(ip_list);
+ return True;
+ }
+ if(internal_resolve_name(group, 0x1B, &ip_list, &count, lp_name_resolve_order())) {
+ *master_ip = ip_list[0].ip;
+ SAFE_FREE(ip_list);
+ return True;
+ }
+
+ SAFE_FREE(ip_list);
+ return False;
+}
+
+/********************************************************
+ Get the IP address list of the primary domain controller
+ for a domain.
+*********************************************************/
+
+BOOL get_pdc_ip(const char *domain, struct in_addr *ip)
+{
+ struct ip_service *ip_list;
+ int count;
+
+ /* Look up #1B name */
+
+ if (!internal_resolve_name(domain, 0x1b, &ip_list, &count, lp_name_resolve_order()))
+ return False;
+
+ /* if we get more than 1 IP back we have to assume it is a
+ multi-homed PDC and not a mess up */
+
+ if ( count > 1 ) {
+ DEBUG(6,("get_pdc_ip: PDC has %d IP addresses!\n", count));
+ sort_ip_list2( ip_list, count );
+ }
+
+ *ip = ip_list[0].ip;
+
+ SAFE_FREE(ip_list);
+
+ return True;
+}
+
+/********************************************************
+ Get the IP address list of the domain controllers for
+ a domain.
+*********************************************************/
+
+static BOOL get_dc_list(const char *domain, struct ip_service **ip_list,
+ int *count, BOOL ads_only, int *ordered)
+{
+ fstring resolve_order;
+
+ /* if we are restricted to solely using DNS for looking
+ up a domain controller, make sure that host lookups
+ are enabled for the 'name resolve order'. If host lookups
+ are disabled and ads_only is True, then set the string to
+ NULL. */
+
+ fstrcpy( resolve_order, lp_name_resolve_order() );
+ strlower_m( resolve_order );
+ if ( ads_only ) {
+ if ( strstr( resolve_order, "host" ) )
+ fstrcpy( resolve_order, "ads" );
+ else
+ fstrcpy( resolve_order, "NULL" );
+ }
+
+
+ *ordered = False;
+
+ /* If it's our domain then use the 'password server' parameter. */
+
+ if ( strequal(domain, lp_workgroup()) || strequal(domain, lp_realm()) ) {
+ const char *p;
+ char *pserver = lp_passwordserver(); /* UNIX charset. */
+ char *port_str;
+ int port;
+ fstring name;
+ int num_addresses = 0;
+ int local_count, i, j;
+ struct ip_service *return_iplist = NULL;
+ struct ip_service *auto_ip_list = NULL;
+ BOOL done_auto_lookup = False;
+ int auto_count = 0;
+
+
+ if (!*pserver)
+ return internal_resolve_name(domain, 0x1C, ip_list, count, resolve_order);
+
+ p = pserver;
+
+ /*
+ * if '*' appears in the "password server" list then add
+ * an auto lookup to the list of manually configured
+ * DC's. If any DC is listed by name, then the list should be
+ * considered to be ordered
+ */
+
+ while (next_token(&p,name,LIST_SEP,sizeof(name))) {
+ if (strequal(name, "*")) {
+ if ( internal_resolve_name(domain, 0x1C, &auto_ip_list, &auto_count, resolve_order) )
+ num_addresses += auto_count;
+ done_auto_lookup = True;
+ DEBUG(8,("Adding %d DC's from auto lookup\n", auto_count));
+ }
+ else
+ num_addresses++;
+ }
+
+ /* if we have no addresses and haven't done the auto lookup, then
+ just return the list of DC's */
+
+ if ( (num_addresses == 0) && !done_auto_lookup )
+ return internal_resolve_name(domain, 0x1C, ip_list, count, resolve_order);
+
+ /* maybe we just failed? */
+
+ if ( num_addresses == 0 ) {
+ DEBUG(4,("get_dc_list: no servers found\n"));
+ return False;
+ }
+
+ if ( (return_iplist = (struct ip_service *)
+ malloc(num_addresses * sizeof(struct ip_service))) == NULL )
+ {
+ DEBUG(3,("get_dc_list: malloc fail !\n"));
+ return False;
+ }
+
+ p = pserver;
+ local_count = 0;
+
+ /* fill in the return list now with real IP's */
+
+ while ( (local_count<num_addresses) && next_token(&p,name,LIST_SEP,sizeof(name)) ) {
+ struct in_addr name_ip;
+
+ /* copy any addersses from the auto lookup */
+
+ if ( strequal(name, "*") ) {
+ for ( j=0; j<auto_count; j++ ) {
+ return_iplist[local_count].ip = auto_ip_list[j].ip;
+ return_iplist[local_count].port = auto_ip_list[j].port;
+ local_count++;
+ }
+ continue;
+ }
+
+
+ /* added support for address:port syntax for ads (not that I think
+ anyone will ever run the LDAP server in an AD domain on something
+ other than port 389 */
+
+ port = (lp_security() == SEC_ADS) ? LDAP_PORT : PORT_NONE;
+ if ( (port_str=strchr(name, ':')) != NULL ) {
+ *port_str = '\0';
+ port_str++;
+ port = atoi( port_str );
+ }
+
+ /* explicit lookup; resolve_name() will handle names & IP addresses */
+ if ( resolve_name( name, &name_ip, 0x20 ) ) {
+ return_iplist[local_count].ip = name_ip;
+ return_iplist[local_count].port = port;
+ local_count++;
+ *ordered = True;
+ }
+ }
+
+ SAFE_FREE(auto_ip_list);
+
+ /* need to remove duplicates in the list if we have any
+ explicit password servers */
+
+ if ( local_count )
+ local_count = remove_duplicate_addrs2( return_iplist, local_count );
+
+ if ( DEBUGLEVEL >= 4 ) {
+ DEBUG(4,("get_dc_list: returning %d ip addresses in an %sordered list\n", local_count,
+ *ordered ? "":"un"));
+ DEBUG(4,("get_dc_list: "));
+ for ( i=0; i<local_count; i++ )
+ DEBUGADD(4,("%s:%d ", inet_ntoa(return_iplist[i].ip), return_iplist[i].port ));
+ DEBUGADD(4,("\n"));
+ }
+
+ *ip_list = return_iplist;
+ *count = local_count;
+
+ return (*count != 0);
+ }
+
+ DEBUG(10,("get_dc_list: defaulting to internal auto lookup for domain %s\n", domain));
+
+ return internal_resolve_name(domain, 0x1C, ip_list, count, resolve_order);
+}
+
+/*********************************************************************
+ small wrapper function to get the DC list and sort it if neccessary
+*********************************************************************/
+BOOL get_sorted_dc_list( const char *domain, struct ip_service **ip_list, int *count, BOOL ads_only )
+{
+ BOOL ordered;
+
+ DEBUG(8,("get_sorted_dc_list: attempting lookup using [%s]\n",
+ (ads_only ? "ads" : lp_name_resolve_order())));
+
+ if ( !get_dc_list(domain, ip_list, count, ads_only, &ordered) )
+ return False;
+
+ /* only sort if we don't already have an ordered list */
+ if ( !ordered )
+ sort_ip_list2( *ip_list, *count );
+
+ return True;
+}
+
diff --git a/source/libsmb/namequery_dc.c b/source/libsmb/namequery_dc.c
new file mode 100644
index 00000000000..31d759e0d2c
--- /dev/null
+++ b/source/libsmb/namequery_dc.c
@@ -0,0 +1,185 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon connection manager
+
+ Copyright (C) Tim Potter 2001
+ Copyright (C) Andrew Bartlett 2002
+ Copyright (C) Gerald Carter 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+#include "includes.h"
+
+/**************************************************************************
+ Find the name and IP address for a server in he realm/domain
+ *************************************************************************/
+
+static BOOL ads_dc_name(const char *domain, const char *realm, struct in_addr *dc_ip, fstring srv_name)
+{
+ ADS_STRUCT *ads;
+
+ if (!realm && strequal(domain, lp_workgroup()))
+ realm = lp_realm();
+
+ ads = ads_init(realm, domain, NULL);
+ if (!ads)
+ return False;
+
+ DEBUG(4,("ads_dc_name: domain=%s\n", domain));
+
+#ifdef HAVE_ADS
+ /* we don't need to bind, just connect */
+ ads->auth.flags |= ADS_AUTH_NO_BIND;
+
+ ads_connect(ads);
+#endif
+
+ if (!ads->config.realm)
+ return False;
+
+ fstrcpy(srv_name, ads->config.ldap_server_name);
+ strupper_m(srv_name);
+ *dc_ip = ads->ldap_ip;
+ ads_destroy(&ads);
+
+ DEBUG(4,("ads_dc_name: using server='%s' IP=%s\n",
+ srv_name, inet_ntoa(*dc_ip)));
+
+ return True;
+}
+
+/****************************************************************************
+ Utility function to return the name of a DC. The name is guaranteed to be
+ valid since we have already done a name_status_find on it
+ ***************************************************************************/
+
+static BOOL rpc_dc_name(const char *domain, fstring srv_name, struct in_addr *ip_out)
+{
+ struct ip_service *ip_list = NULL;
+ struct in_addr dc_ip, exclude_ip;
+ int count, i;
+ BOOL use_pdc_only;
+ NTSTATUS result;
+
+ zero_ip(&exclude_ip);
+
+ use_pdc_only = must_use_pdc(domain);
+
+ /* Lookup domain controller name */
+
+ if ( use_pdc_only && get_pdc_ip(domain, &dc_ip) )
+ {
+ DEBUG(10,("rpc_dc_name: Atempting to lookup PDC to avoid sam sync delays\n"));
+
+ /* check the connection cache and perform the node status
+ lookup only if the IP is not found to be bad */
+
+ if (name_status_find(domain, 0x1b, 0x20, dc_ip, srv_name) ) {
+ result = check_negative_conn_cache( domain, srv_name );
+ if ( NT_STATUS_IS_OK(result) )
+ goto done;
+ }
+ /* Didn't get name, remember not to talk to this DC. */
+ exclude_ip = dc_ip;
+ }
+
+ /* get a list of all domain controllers */
+
+ if ( !get_sorted_dc_list(domain, &ip_list, &count, False) ) {
+ DEBUG(3, ("Could not look up dc's for domain %s\n", domain));
+ return False;
+ }
+
+ /* Remove the entry we've already failed with (should be the PDC). */
+
+ if ( use_pdc_only ) {
+ for (i = 0; i < count; i++) {
+ if (ip_equal( exclude_ip, ip_list[i].ip))
+ zero_ip(&ip_list[i].ip);
+ }
+ }
+
+ for (i = 0; i < count; i++) {
+ if (is_zero_ip(ip_list[i].ip))
+ continue;
+
+ if (name_status_find(domain, 0x1c, 0x20, ip_list[i].ip, srv_name)) {
+ result = check_negative_conn_cache( domain, srv_name );
+ if ( NT_STATUS_IS_OK(result) ) {
+ dc_ip = ip_list[i].ip;
+ goto done;
+ }
+ }
+ }
+
+
+ SAFE_FREE(ip_list);
+
+ /* No-one to talk to )-: */
+ return False; /* Boo-hoo */
+
+ done:
+ /* We have the netbios name and IP address of a domain controller.
+ Ideally we should sent a SAMLOGON request to determine whether
+ the DC is alive and kicking. If we can catch a dead DC before
+ performing a cli_connect() we can avoid a 30-second timeout. */
+
+ DEBUG(3, ("rpc_dc_name: Returning DC %s (%s) for domain %s\n", srv_name,
+ inet_ntoa(dc_ip), domain));
+
+ *ip_out = dc_ip;
+
+ SAFE_FREE(ip_list);
+
+ return True;
+}
+
+/**********************************************************************
+ wrapper around ads and rpc methods of finds DC's
+**********************************************************************/
+
+BOOL get_dc_name(const char *domain, const char *realm, fstring srv_name, struct in_addr *ip_out)
+{
+ struct in_addr dc_ip;
+ BOOL ret;
+ BOOL our_domain = False;
+
+ zero_ip(&dc_ip);
+
+ ret = False;
+
+ if ( strequal(lp_workgroup(), domain) || strequal(lp_realm(), realm) )
+ our_domain = True;
+
+ /* always try to obey what the admin specified in smb.conf
+ (for the local domain) */
+
+ if ( (our_domain && lp_security()==SEC_ADS) || realm ) {
+ ret = ads_dc_name(domain, realm, &dc_ip, srv_name);
+ }
+
+ if (!ret) {
+ /* fall back on rpc methods if the ADS methods fail */
+ ret = rpc_dc_name(domain, srv_name, &dc_ip);
+ }
+
+ *ip_out = dc_ip;
+
+ return ret;
+}
+
diff --git a/source/libsmb/nmblib.c b/source/libsmb/nmblib.c
new file mode 100644
index 00000000000..3c25eba744f
--- /dev/null
+++ b/source/libsmb/nmblib.c
@@ -0,0 +1,1343 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios library routines
+ Copyright (C) Andrew Tridgell 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+
+int num_good_sends = 0;
+int num_good_receives = 0;
+
+static const struct opcode_names {
+ const char *nmb_opcode_name;
+ int opcode;
+} nmb_header_opcode_names[] = {
+ {"Query", 0 },
+ {"Registration", 5 },
+ {"Release", 6 },
+ {"WACK", 7 },
+ {"Refresh", 8 },
+ {"Refresh(altcode)", 9 },
+ {"Multi-homed Registration", 15 },
+ {0, -1 }
+};
+
+/****************************************************************************
+ Lookup a nmb opcode name.
+****************************************************************************/
+
+static const char *lookup_opcode_name( int opcode )
+{
+ const struct opcode_names *op_namep;
+ int i;
+
+ for(i = 0; nmb_header_opcode_names[i].nmb_opcode_name != 0; i++) {
+ op_namep = &nmb_header_opcode_names[i];
+ if(opcode == op_namep->opcode)
+ return op_namep->nmb_opcode_name;
+ }
+ return "<unknown opcode>";
+}
+
+/****************************************************************************
+ Print out a res_rec structure.
+****************************************************************************/
+
+static void debug_nmb_res_rec(struct res_rec *res, const char *hdr)
+{
+ int i, j;
+
+ DEBUGADD( 4, ( " %s: nmb_name=%s rr_type=%d rr_class=%d ttl=%d\n",
+ hdr,
+ nmb_namestr(&res->rr_name),
+ res->rr_type,
+ res->rr_class,
+ res->ttl ) );
+
+ if( res->rdlength == 0 || res->rdata == NULL )
+ return;
+
+ for (i = 0; i < res->rdlength; i+= MAX_NETBIOSNAME_LEN) {
+ DEBUGADD(4, (" %s %3x char ", hdr, i));
+
+ for (j = 0; j < MAX_NETBIOSNAME_LEN; j++) {
+ unsigned char x = res->rdata[i+j];
+ if (x < 32 || x > 127)
+ x = '.';
+
+ if (i+j >= res->rdlength)
+ break;
+ DEBUGADD(4, ("%c", x));
+ }
+
+ DEBUGADD(4, (" hex "));
+
+ for (j = 0; j < MAX_NETBIOSNAME_LEN; j++) {
+ if (i+j >= res->rdlength)
+ break;
+ DEBUGADD(4, ("%02X", (unsigned char)res->rdata[i+j]));
+ }
+
+ DEBUGADD(4, ("\n"));
+ }
+}
+
+/****************************************************************************
+ Process a nmb packet.
+****************************************************************************/
+
+void debug_nmb_packet(struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+
+ if( DEBUGLVL( 4 ) ) {
+ dbgtext( "nmb packet from %s(%d) header: id=%d opcode=%s(%d) response=%s\n",
+ inet_ntoa(p->ip), p->port,
+ nmb->header.name_trn_id,
+ lookup_opcode_name(nmb->header.opcode),
+ nmb->header.opcode,
+ BOOLSTR(nmb->header.response) );
+ dbgtext( " header: flags: bcast=%s rec_avail=%s rec_des=%s trunc=%s auth=%s\n",
+ BOOLSTR(nmb->header.nm_flags.bcast),
+ BOOLSTR(nmb->header.nm_flags.recursion_available),
+ BOOLSTR(nmb->header.nm_flags.recursion_desired),
+ BOOLSTR(nmb->header.nm_flags.trunc),
+ BOOLSTR(nmb->header.nm_flags.authoritative) );
+ dbgtext( " header: rcode=%d qdcount=%d ancount=%d nscount=%d arcount=%d\n",
+ nmb->header.rcode,
+ nmb->header.qdcount,
+ nmb->header.ancount,
+ nmb->header.nscount,
+ nmb->header.arcount );
+ }
+
+ if (nmb->header.qdcount) {
+ DEBUGADD( 4, ( " question: q_name=%s q_type=%d q_class=%d\n",
+ nmb_namestr(&nmb->question.question_name),
+ nmb->question.question_type,
+ nmb->question.question_class) );
+ }
+
+ if (nmb->answers && nmb->header.ancount) {
+ debug_nmb_res_rec(nmb->answers,"answers");
+ }
+ if (nmb->nsrecs && nmb->header.nscount) {
+ debug_nmb_res_rec(nmb->nsrecs,"nsrecs");
+ }
+ if (nmb->additional && nmb->header.arcount) {
+ debug_nmb_res_rec(nmb->additional,"additional");
+ }
+}
+
+/*******************************************************************
+ Handle "compressed" name pointers.
+******************************************************************/
+
+static BOOL handle_name_ptrs(unsigned char *ubuf,int *offset,int length,
+ BOOL *got_pointer,int *ret)
+{
+ int loop_count=0;
+
+ while ((ubuf[*offset] & 0xC0) == 0xC0) {
+ if (!*got_pointer)
+ (*ret) += 2;
+ (*got_pointer)=True;
+ (*offset) = ((ubuf[*offset] & ~0xC0)<<8) | ubuf[(*offset)+1];
+ if (loop_count++ == 10 || (*offset) < 0 || (*offset)>(length-2)) {
+ return(False);
+ }
+ }
+ return(True);
+}
+
+/*******************************************************************
+ Parse a nmb name from "compressed" format to something readable
+ return the space taken by the name, or 0 if the name is invalid
+******************************************************************/
+
+static int parse_nmb_name(char *inbuf,int ofs,int length, struct nmb_name *name)
+{
+ int m,n=0;
+ unsigned char *ubuf = (unsigned char *)inbuf;
+ int ret = 0;
+ BOOL got_pointer=False;
+ int loop_count=0;
+ int offset = ofs;
+
+ if (length - offset < 2)
+ return(0);
+
+ /* handle initial name pointers */
+ if (!handle_name_ptrs(ubuf,&offset,length,&got_pointer,&ret))
+ return(0);
+
+ m = ubuf[offset];
+
+ if (!m)
+ return(0);
+ if ((m & 0xC0) || offset+m+2 > length)
+ return(0);
+
+ memset((char *)name,'\0',sizeof(*name));
+
+ /* the "compressed" part */
+ if (!got_pointer)
+ ret += m + 2;
+ offset++;
+ while (m > 0) {
+ unsigned char c1,c2;
+ c1 = ubuf[offset++]-'A';
+ c2 = ubuf[offset++]-'A';
+ if ((c1 & 0xF0) || (c2 & 0xF0) || (n > sizeof(name->name)-1))
+ return(0);
+ name->name[n++] = (c1<<4) | c2;
+ m -= 2;
+ }
+ name->name[n] = 0;
+
+ if (n==MAX_NETBIOSNAME_LEN) {
+ /* parse out the name type, its always in the 16th byte of the name */
+ name->name_type = ((unsigned char)name->name[15]) & 0xff;
+
+ /* remove trailing spaces */
+ name->name[15] = 0;
+ n = 14;
+ while (n && name->name[n]==' ')
+ name->name[n--] = 0;
+ }
+
+ /* now the domain parts (if any) */
+ n = 0;
+ while (ubuf[offset]) {
+ /* we can have pointers within the domain part as well */
+ if (!handle_name_ptrs(ubuf,&offset,length,&got_pointer,&ret))
+ return(0);
+
+ m = ubuf[offset];
+ /*
+ * Don't allow null domain parts.
+ */
+ if (!m)
+ return(0);
+ if (!got_pointer)
+ ret += m+1;
+ if (n)
+ name->scope[n++] = '.';
+ if (m+2+offset>length || n+m+1>sizeof(name->scope))
+ return(0);
+ offset++;
+ while (m--)
+ name->scope[n++] = (char)ubuf[offset++];
+
+ /*
+ * Watch for malicious loops.
+ */
+ if (loop_count++ == 10)
+ return 0;
+ }
+ name->scope[n++] = 0;
+
+ return(ret);
+}
+
+/****************************************************************************
+ Put a netbios name, padding(s) and a name type into a 16 character buffer.
+ name is already in DOS charset.
+ [15 bytes name + padding][1 byte name type].
+****************************************************************************/
+
+static void put_name(char *dest, const char *name, int pad, unsigned int name_type)
+{
+ size_t len = strlen(name);
+
+ memcpy(dest, name, (len < MAX_NETBIOSNAME_LEN) ? len : MAX_NETBIOSNAME_LEN - 1);
+ if (len < MAX_NETBIOSNAME_LEN - 1) {
+ memset(dest + len, pad, MAX_NETBIOSNAME_LEN - 1 - len);
+ }
+ dest[MAX_NETBIOSNAME_LEN - 1] = name_type;
+}
+
+/*******************************************************************
+ Put a compressed nmb name into a buffer. Return the length of the
+ compressed name.
+
+ Compressed names are really weird. The "compression" doubles the
+ size. The idea is that it also means that compressed names conform
+ to the doman name system. See RFC1002.
+******************************************************************/
+
+static int put_nmb_name(char *buf,int offset,struct nmb_name *name)
+{
+ int ret,m;
+ nstring buf1;
+ char *p;
+
+ if (strcmp(name->name,"*") == 0) {
+ /* special case for wildcard name */
+ put_name(buf1, "*", '\0', name->name_type);
+ } else {
+ put_name(buf1, name->name, ' ', name->name_type);
+ }
+
+ buf[offset] = 0x20;
+
+ ret = 34;
+
+ for (m=0;m<MAX_NETBIOSNAME_LEN;m++) {
+ buf[offset+1+2*m] = 'A' + ((buf1[m]>>4)&0xF);
+ buf[offset+2+2*m] = 'A' + (buf1[m]&0xF);
+ }
+ offset += 33;
+
+ buf[offset] = 0;
+
+ if (name->scope[0]) {
+ /* XXXX this scope handling needs testing */
+ ret += strlen(name->scope) + 1;
+ safe_strcpy(&buf[offset+1],name->scope,sizeof(name->scope));
+
+ p = &buf[offset+1];
+ while ((p = strchr_m(p,'.'))) {
+ buf[offset] = PTR_DIFF(p,&buf[offset+1]);
+ offset += (buf[offset] + 1);
+ p = &buf[offset+1];
+ }
+ buf[offset] = strlen(&buf[offset+1]);
+ }
+
+ return(ret);
+}
+
+/*******************************************************************
+ Useful for debugging messages.
+******************************************************************/
+
+char *nmb_namestr(struct nmb_name *n)
+{
+ static int i=0;
+ static fstring ret[4];
+ fstring name;
+ char *p = ret[i];
+
+ pull_ascii_fstring(name, n->name);
+ if (!n->scope[0])
+ slprintf(p,sizeof(fstring)-1, "%s<%02x>",name,n->name_type);
+ else
+ slprintf(p,sizeof(fstring)-1, "%s<%02x>.%s",name,n->name_type,n->scope);
+
+ i = (i+1)%4;
+ return(p);
+}
+
+/*******************************************************************
+ Allocate and parse some resource records.
+******************************************************************/
+
+static BOOL parse_alloc_res_rec(char *inbuf,int *offset,int length,
+ struct res_rec **recs, int count)
+{
+ int i;
+
+ *recs = (struct res_rec *)malloc(sizeof(**recs)*count);
+ if (!*recs)
+ return(False);
+
+ memset((char *)*recs,'\0',sizeof(**recs)*count);
+
+ for (i=0;i<count;i++) {
+ int l = parse_nmb_name(inbuf,*offset,length,&(*recs)[i].rr_name);
+ (*offset) += l;
+ if (!l || (*offset)+10 > length) {
+ SAFE_FREE(*recs);
+ return(False);
+ }
+ (*recs)[i].rr_type = RSVAL(inbuf,(*offset));
+ (*recs)[i].rr_class = RSVAL(inbuf,(*offset)+2);
+ (*recs)[i].ttl = RIVAL(inbuf,(*offset)+4);
+ (*recs)[i].rdlength = RSVAL(inbuf,(*offset)+8);
+ (*offset) += 10;
+ if ((*recs)[i].rdlength>sizeof((*recs)[i].rdata) ||
+ (*offset)+(*recs)[i].rdlength > length) {
+ SAFE_FREE(*recs);
+ return(False);
+ }
+ memcpy((*recs)[i].rdata,inbuf+(*offset),(*recs)[i].rdlength);
+ (*offset) += (*recs)[i].rdlength;
+ }
+ return(True);
+}
+
+/*******************************************************************
+ Put a resource record into a packet.
+******************************************************************/
+
+static int put_res_rec(char *buf,int offset,struct res_rec *recs,int count)
+{
+ int ret=0;
+ int i;
+
+ for (i=0;i<count;i++) {
+ int l = put_nmb_name(buf,offset,&recs[i].rr_name);
+ offset += l;
+ ret += l;
+ RSSVAL(buf,offset,recs[i].rr_type);
+ RSSVAL(buf,offset+2,recs[i].rr_class);
+ RSIVAL(buf,offset+4,recs[i].ttl);
+ RSSVAL(buf,offset+8,recs[i].rdlength);
+ memcpy(buf+offset+10,recs[i].rdata,recs[i].rdlength);
+ offset += 10+recs[i].rdlength;
+ ret += 10+recs[i].rdlength;
+ }
+
+ return(ret);
+}
+
+/*******************************************************************
+ Put a compressed name pointer record into a packet.
+******************************************************************/
+
+static int put_compressed_name_ptr(unsigned char *buf,int offset,struct res_rec *rec,int ptr_offset)
+{
+ int ret=0;
+ buf[offset] = (0xC0 | ((ptr_offset >> 8) & 0xFF));
+ buf[offset+1] = (ptr_offset & 0xFF);
+ offset += 2;
+ ret += 2;
+ RSSVAL(buf,offset,rec->rr_type);
+ RSSVAL(buf,offset+2,rec->rr_class);
+ RSIVAL(buf,offset+4,rec->ttl);
+ RSSVAL(buf,offset+8,rec->rdlength);
+ memcpy(buf+offset+10,rec->rdata,rec->rdlength);
+ offset += 10+rec->rdlength;
+ ret += 10+rec->rdlength;
+
+ return(ret);
+}
+
+/*******************************************************************
+ Parse a dgram packet. Return False if the packet can't be parsed
+ or is invalid for some reason, True otherwise.
+
+ This is documented in section 4.4.1 of RFC1002.
+******************************************************************/
+
+static BOOL parse_dgram(char *inbuf,int length,struct dgram_packet *dgram)
+{
+ int offset;
+ int flags;
+
+ memset((char *)dgram,'\0',sizeof(*dgram));
+
+ if (length < 14)
+ return(False);
+
+ dgram->header.msg_type = CVAL(inbuf,0);
+ flags = CVAL(inbuf,1);
+ dgram->header.flags.node_type = (enum node_type)((flags>>2)&3);
+ if (flags & 1)
+ dgram->header.flags.more = True;
+ if (flags & 2)
+ dgram->header.flags.first = True;
+ dgram->header.dgm_id = RSVAL(inbuf,2);
+ putip((char *)&dgram->header.source_ip,inbuf+4);
+ dgram->header.source_port = RSVAL(inbuf,8);
+ dgram->header.dgm_length = RSVAL(inbuf,10);
+ dgram->header.packet_offset = RSVAL(inbuf,12);
+
+ offset = 14;
+
+ if (dgram->header.msg_type == 0x10 ||
+ dgram->header.msg_type == 0x11 ||
+ dgram->header.msg_type == 0x12) {
+ offset += parse_nmb_name(inbuf,offset,length,&dgram->source_name);
+ offset += parse_nmb_name(inbuf,offset,length,&dgram->dest_name);
+ }
+
+ if (offset >= length || (length-offset > sizeof(dgram->data)))
+ return(False);
+
+ dgram->datasize = length-offset;
+ memcpy(dgram->data,inbuf+offset,dgram->datasize);
+
+ return(True);
+}
+
+/*******************************************************************
+ Parse a nmb packet. Return False if the packet can't be parsed
+ or is invalid for some reason, True otherwise.
+******************************************************************/
+
+static BOOL parse_nmb(char *inbuf,int length,struct nmb_packet *nmb)
+{
+ int nm_flags,offset;
+
+ memset((char *)nmb,'\0',sizeof(*nmb));
+
+ if (length < 12)
+ return(False);
+
+ /* parse the header */
+ nmb->header.name_trn_id = RSVAL(inbuf,0);
+
+ DEBUG(10,("parse_nmb: packet id = %d\n", nmb->header.name_trn_id));
+
+ nmb->header.opcode = (CVAL(inbuf,2) >> 3) & 0xF;
+ nmb->header.response = ((CVAL(inbuf,2)>>7)&1)?True:False;
+ nm_flags = ((CVAL(inbuf,2) & 0x7) << 4) + (CVAL(inbuf,3)>>4);
+ nmb->header.nm_flags.bcast = (nm_flags&1)?True:False;
+ nmb->header.nm_flags.recursion_available = (nm_flags&8)?True:False;
+ nmb->header.nm_flags.recursion_desired = (nm_flags&0x10)?True:False;
+ nmb->header.nm_flags.trunc = (nm_flags&0x20)?True:False;
+ nmb->header.nm_flags.authoritative = (nm_flags&0x40)?True:False;
+ nmb->header.rcode = CVAL(inbuf,3) & 0xF;
+ nmb->header.qdcount = RSVAL(inbuf,4);
+ nmb->header.ancount = RSVAL(inbuf,6);
+ nmb->header.nscount = RSVAL(inbuf,8);
+ nmb->header.arcount = RSVAL(inbuf,10);
+
+ if (nmb->header.qdcount) {
+ offset = parse_nmb_name(inbuf,12,length,&nmb->question.question_name);
+ if (!offset)
+ return(False);
+
+ if (length - (12+offset) < 4)
+ return(False);
+ nmb->question.question_type = RSVAL(inbuf,12+offset);
+ nmb->question.question_class = RSVAL(inbuf,12+offset+2);
+
+ offset += 12+4;
+ } else {
+ offset = 12;
+ }
+
+ /* and any resource records */
+ if (nmb->header.ancount && !parse_alloc_res_rec(inbuf,&offset,length,&nmb->answers,
+ nmb->header.ancount))
+ return(False);
+
+ if (nmb->header.nscount && !parse_alloc_res_rec(inbuf,&offset,length,&nmb->nsrecs,
+ nmb->header.nscount))
+ return(False);
+
+ if (nmb->header.arcount && !parse_alloc_res_rec(inbuf,&offset,length,&nmb->additional,
+ nmb->header.arcount))
+ return(False);
+
+ return(True);
+}
+
+/*******************************************************************
+ 'Copy constructor' for an nmb packet.
+******************************************************************/
+
+static struct packet_struct *copy_nmb_packet(struct packet_struct *packet)
+{
+ struct nmb_packet *nmb;
+ struct nmb_packet *copy_nmb;
+ struct packet_struct *pkt_copy;
+
+ if(( pkt_copy = (struct packet_struct *)malloc(sizeof(*packet))) == NULL) {
+ DEBUG(0,("copy_nmb_packet: malloc fail.\n"));
+ return NULL;
+ }
+
+ /* Structure copy of entire thing. */
+
+ *pkt_copy = *packet;
+
+ /* Ensure this copy is not locked. */
+ pkt_copy->locked = False;
+
+ /* Ensure this copy has no resource records. */
+ nmb = &packet->packet.nmb;
+ copy_nmb = &pkt_copy->packet.nmb;
+
+ copy_nmb->answers = NULL;
+ copy_nmb->nsrecs = NULL;
+ copy_nmb->additional = NULL;
+
+ /* Now copy any resource records. */
+
+ if (nmb->answers) {
+ if((copy_nmb->answers = (struct res_rec *)
+ malloc(nmb->header.ancount * sizeof(struct res_rec))) == NULL)
+ goto free_and_exit;
+ memcpy((char *)copy_nmb->answers, (char *)nmb->answers,
+ nmb->header.ancount * sizeof(struct res_rec));
+ }
+ if (nmb->nsrecs) {
+ if((copy_nmb->nsrecs = (struct res_rec *)
+ malloc(nmb->header.nscount * sizeof(struct res_rec))) == NULL)
+ goto free_and_exit;
+ memcpy((char *)copy_nmb->nsrecs, (char *)nmb->nsrecs,
+ nmb->header.nscount * sizeof(struct res_rec));
+ }
+ if (nmb->additional) {
+ if((copy_nmb->additional = (struct res_rec *)
+ malloc(nmb->header.arcount * sizeof(struct res_rec))) == NULL)
+ goto free_and_exit;
+ memcpy((char *)copy_nmb->additional, (char *)nmb->additional,
+ nmb->header.arcount * sizeof(struct res_rec));
+ }
+
+ return pkt_copy;
+
+ free_and_exit:
+
+ SAFE_FREE(copy_nmb->answers);
+ SAFE_FREE(copy_nmb->nsrecs);
+ SAFE_FREE(copy_nmb->additional);
+ SAFE_FREE(pkt_copy);
+
+ DEBUG(0,("copy_nmb_packet: malloc fail in resource records.\n"));
+ return NULL;
+}
+
+/*******************************************************************
+ 'Copy constructor' for a dgram packet.
+******************************************************************/
+
+static struct packet_struct *copy_dgram_packet(struct packet_struct *packet)
+{
+ struct packet_struct *pkt_copy;
+
+ if(( pkt_copy = (struct packet_struct *)malloc(sizeof(*packet))) == NULL) {
+ DEBUG(0,("copy_dgram_packet: malloc fail.\n"));
+ return NULL;
+ }
+
+ /* Structure copy of entire thing. */
+
+ *pkt_copy = *packet;
+
+ /* Ensure this copy is not locked. */
+ pkt_copy->locked = False;
+
+ /* There are no additional pointers in a dgram packet,
+ we are finished. */
+ return pkt_copy;
+}
+
+/*******************************************************************
+ 'Copy constructor' for a generic packet.
+******************************************************************/
+
+struct packet_struct *copy_packet(struct packet_struct *packet)
+{
+ if(packet->packet_type == NMB_PACKET)
+ return copy_nmb_packet(packet);
+ else if (packet->packet_type == DGRAM_PACKET)
+ return copy_dgram_packet(packet);
+ return NULL;
+}
+
+/*******************************************************************
+ Free up any resources associated with an nmb packet.
+******************************************************************/
+
+static void free_nmb_packet(struct nmb_packet *nmb)
+{
+ SAFE_FREE(nmb->answers);
+ SAFE_FREE(nmb->nsrecs);
+ SAFE_FREE(nmb->additional);
+}
+
+/*******************************************************************
+ Free up any resources associated with a dgram packet.
+******************************************************************/
+
+static void free_dgram_packet(struct dgram_packet *nmb)
+{
+ /* We have nothing to do for a dgram packet. */
+}
+
+/*******************************************************************
+ Free up any resources associated with a packet.
+******************************************************************/
+
+void free_packet(struct packet_struct *packet)
+{
+ if (packet->locked)
+ return;
+ if (packet->packet_type == NMB_PACKET)
+ free_nmb_packet(&packet->packet.nmb);
+ else if (packet->packet_type == DGRAM_PACKET)
+ free_dgram_packet(&packet->packet.dgram);
+ ZERO_STRUCTPN(packet);
+ SAFE_FREE(packet);
+}
+
+/*******************************************************************
+ Parse a packet buffer into a packet structure.
+******************************************************************/
+
+struct packet_struct *parse_packet(char *buf,int length,
+ enum packet_type packet_type)
+{
+ extern struct in_addr lastip;
+ extern int lastport;
+ struct packet_struct *p;
+ BOOL ok=False;
+
+ p = (struct packet_struct *)malloc(sizeof(*p));
+ if (!p)
+ return(NULL);
+
+ p->next = NULL;
+ p->prev = NULL;
+ p->ip = lastip;
+ p->port = lastport;
+ p->locked = False;
+ p->timestamp = time(NULL);
+ p->packet_type = packet_type;
+
+ switch (packet_type) {
+ case NMB_PACKET:
+ ok = parse_nmb(buf,length,&p->packet.nmb);
+ break;
+
+ case DGRAM_PACKET:
+ ok = parse_dgram(buf,length,&p->packet.dgram);
+ break;
+ }
+
+ if (!ok) {
+ free_packet(p);
+ return NULL;
+ }
+
+ return p;
+}
+
+/*******************************************************************
+ Read a packet from a socket and parse it, returning a packet ready
+ to be used or put on the queue. This assumes a UDP socket.
+******************************************************************/
+
+struct packet_struct *read_packet(int fd,enum packet_type packet_type)
+{
+ struct packet_struct *packet;
+ char buf[MAX_DGRAM_SIZE];
+ int length;
+
+ length = read_udp_socket(fd,buf,sizeof(buf));
+ if (length < MIN_DGRAM_SIZE)
+ return(NULL);
+
+ packet = parse_packet(buf, length, packet_type);
+ if (!packet)
+ return NULL;
+
+ packet->fd = fd;
+
+ num_good_receives++;
+
+ DEBUG(5,("Received a packet of len %d from (%s) port %d\n",
+ length, inet_ntoa(packet->ip), packet->port ) );
+
+ return(packet);
+}
+
+/*******************************************************************
+ Send a udp packet on a already open socket.
+******************************************************************/
+
+static BOOL send_udp(int fd,char *buf,int len,struct in_addr ip,int port)
+{
+ BOOL ret = False;
+ int i;
+ struct sockaddr_in sock_out;
+
+ /* set the address and port */
+ memset((char *)&sock_out,'\0',sizeof(sock_out));
+ putip((char *)&sock_out.sin_addr,(char *)&ip);
+ sock_out.sin_port = htons( port );
+ sock_out.sin_family = AF_INET;
+
+ DEBUG( 5, ( "Sending a packet of len %d to (%s) on port %d\n",
+ len, inet_ntoa(ip), port ) );
+
+ /*
+ * Patch to fix asynch error notifications from Linux kernel.
+ */
+
+ for (i = 0; i < 5; i++) {
+ ret = (sendto(fd,buf,len,0,(struct sockaddr *)&sock_out, sizeof(sock_out)) >= 0);
+ if (ret || errno != ECONNREFUSED)
+ break;
+ }
+
+ if (!ret)
+ DEBUG(0,("Packet send failed to %s(%d) ERRNO=%s\n",
+ inet_ntoa(ip),port,strerror(errno)));
+
+ if (ret)
+ num_good_sends++;
+
+ return(ret);
+}
+
+/*******************************************************************
+ Build a dgram packet ready for sending.
+
+ XXXX This currently doesn't handle packets too big for one
+ datagram. It should split them and use the packet_offset, more and
+ first flags to handle the fragmentation. Yuck.
+
+ [...but it isn't clear that we would ever need to send a
+ a fragmented NBT Datagram. The IP layer does its own
+ fragmentation to ensure that messages can fit into the path
+ MTU. It *is* important to be able to receive and rebuild
+ fragmented NBT datagrams, just in case someone out there
+ really has implemented this 'feature'. crh -)------ ]
+
+******************************************************************/
+
+static int build_dgram(char *buf,struct packet_struct *p)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ unsigned char *ubuf = (unsigned char *)buf;
+ int offset=0;
+
+ /* put in the header */
+ ubuf[0] = dgram->header.msg_type;
+ ubuf[1] = (((int)dgram->header.flags.node_type)<<2);
+ if (dgram->header.flags.more)
+ ubuf[1] |= 1;
+ if (dgram->header.flags.first)
+ ubuf[1] |= 2;
+ RSSVAL(ubuf,2,dgram->header.dgm_id);
+ putip(ubuf+4,(char *)&dgram->header.source_ip);
+ RSSVAL(ubuf,8,dgram->header.source_port);
+ RSSVAL(ubuf,12,dgram->header.packet_offset);
+
+ offset = 14;
+
+ if (dgram->header.msg_type == 0x10 ||
+ dgram->header.msg_type == 0x11 ||
+ dgram->header.msg_type == 0x12) {
+ offset += put_nmb_name((char *)ubuf,offset,&dgram->source_name);
+ offset += put_nmb_name((char *)ubuf,offset,&dgram->dest_name);
+ }
+
+ memcpy(ubuf+offset,dgram->data,dgram->datasize);
+ offset += dgram->datasize;
+
+ /* automatically set the dgm_length
+ * NOTE: RFC1002 says the dgm_length does *not*
+ * include the fourteen-byte header. crh
+ */
+ dgram->header.dgm_length = (offset - 14);
+ RSSVAL(ubuf,10,dgram->header.dgm_length);
+
+ return(offset);
+}
+
+/*******************************************************************
+ Build a nmb name
+*******************************************************************/
+
+void make_nmb_name( struct nmb_name *n, const char *name, int type)
+{
+ fstring unix_name;
+ memset( (char *)n, '\0', sizeof(struct nmb_name) );
+ fstrcpy(unix_name, name);
+ strupper_m(unix_name);
+ push_ascii(n->name, unix_name, sizeof(n->name), STR_TERMINATE);
+ n->name_type = (unsigned int)type & 0xFF;
+ push_ascii(n->scope, global_scope(), 64, STR_TERMINATE);
+}
+
+/*******************************************************************
+ Compare two nmb names
+******************************************************************/
+
+BOOL nmb_name_equal(struct nmb_name *n1, struct nmb_name *n2)
+{
+ return ((n1->name_type == n2->name_type) &&
+ strequal(n1->name ,n2->name ) &&
+ strequal(n1->scope,n2->scope));
+}
+
+/*******************************************************************
+ Build a nmb packet ready for sending.
+
+ XXXX this currently relies on not being passed something that expands
+ to a packet too big for the buffer. Eventually this should be
+ changed to set the trunc bit so the receiver can request the rest
+ via tcp (when that becomes supported)
+******************************************************************/
+
+static int build_nmb(char *buf,struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ unsigned char *ubuf = (unsigned char *)buf;
+ int offset=0;
+
+ /* put in the header */
+ RSSVAL(ubuf,offset,nmb->header.name_trn_id);
+ ubuf[offset+2] = (nmb->header.opcode & 0xF) << 3;
+ if (nmb->header.response)
+ ubuf[offset+2] |= (1<<7);
+ if (nmb->header.nm_flags.authoritative &&
+ nmb->header.response)
+ ubuf[offset+2] |= 0x4;
+ if (nmb->header.nm_flags.trunc)
+ ubuf[offset+2] |= 0x2;
+ if (nmb->header.nm_flags.recursion_desired)
+ ubuf[offset+2] |= 0x1;
+ if (nmb->header.nm_flags.recursion_available &&
+ nmb->header.response)
+ ubuf[offset+3] |= 0x80;
+ if (nmb->header.nm_flags.bcast)
+ ubuf[offset+3] |= 0x10;
+ ubuf[offset+3] |= (nmb->header.rcode & 0xF);
+
+ RSSVAL(ubuf,offset+4,nmb->header.qdcount);
+ RSSVAL(ubuf,offset+6,nmb->header.ancount);
+ RSSVAL(ubuf,offset+8,nmb->header.nscount);
+ RSSVAL(ubuf,offset+10,nmb->header.arcount);
+
+ offset += 12;
+ if (nmb->header.qdcount) {
+ /* XXXX this doesn't handle a qdcount of > 1 */
+ offset += put_nmb_name((char *)ubuf,offset,&nmb->question.question_name);
+ RSSVAL(ubuf,offset,nmb->question.question_type);
+ RSSVAL(ubuf,offset+2,nmb->question.question_class);
+ offset += 4;
+ }
+
+ if (nmb->header.ancount)
+ offset += put_res_rec((char *)ubuf,offset,nmb->answers,
+ nmb->header.ancount);
+
+ if (nmb->header.nscount)
+ offset += put_res_rec((char *)ubuf,offset,nmb->nsrecs,
+ nmb->header.nscount);
+
+ /*
+ * The spec says we must put compressed name pointers
+ * in the following outgoing packets :
+ * NAME_REGISTRATION_REQUEST, NAME_REFRESH_REQUEST,
+ * NAME_RELEASE_REQUEST.
+ */
+
+ if((nmb->header.response == False) &&
+ ((nmb->header.opcode == NMB_NAME_REG_OPCODE) ||
+ (nmb->header.opcode == NMB_NAME_RELEASE_OPCODE) ||
+ (nmb->header.opcode == NMB_NAME_REFRESH_OPCODE_8) ||
+ (nmb->header.opcode == NMB_NAME_REFRESH_OPCODE_9) ||
+ (nmb->header.opcode == NMB_NAME_MULTIHOMED_REG_OPCODE)) &&
+ (nmb->header.arcount == 1)) {
+
+ offset += put_compressed_name_ptr(ubuf,offset,nmb->additional,12);
+
+ } else if (nmb->header.arcount) {
+ offset += put_res_rec((char *)ubuf,offset,nmb->additional,
+ nmb->header.arcount);
+ }
+ return(offset);
+}
+
+/*******************************************************************
+ Linearise a packet.
+******************************************************************/
+
+int build_packet(char *buf, struct packet_struct *p)
+{
+ int len = 0;
+
+ switch (p->packet_type) {
+ case NMB_PACKET:
+ len = build_nmb(buf,p);
+ break;
+
+ case DGRAM_PACKET:
+ len = build_dgram(buf,p);
+ break;
+ }
+
+ return len;
+}
+
+/*******************************************************************
+ Send a packet_struct.
+******************************************************************/
+
+BOOL send_packet(struct packet_struct *p)
+{
+ char buf[1024];
+ int len=0;
+
+ memset(buf,'\0',sizeof(buf));
+
+ len = build_packet(buf, p);
+
+ if (!len)
+ return(False);
+
+ return(send_udp(p->fd,buf,len,p->ip,p->port));
+}
+
+/****************************************************************************
+ Receive a packet with timeout on a open UDP filedescriptor.
+ The timeout is in milliseconds
+***************************************************************************/
+
+struct packet_struct *receive_packet(int fd,enum packet_type type,int t)
+{
+ fd_set fds;
+ struct timeval timeout;
+ int ret;
+
+ FD_ZERO(&fds);
+ FD_SET(fd,&fds);
+ timeout.tv_sec = t/1000;
+ timeout.tv_usec = 1000*(t%1000);
+
+ if ((ret = sys_select_intr(fd+1,&fds,NULL,NULL,&timeout)) == -1) {
+ /* errno should be EBADF or EINVAL. */
+ DEBUG(0,("select returned -1, errno = %s (%d)\n", strerror(errno), errno));
+ return NULL;
+ }
+
+ if (ret == 0) /* timeout */
+ return NULL;
+
+ if (FD_ISSET(fd,&fds))
+ return(read_packet(fd,type));
+
+ return(NULL);
+}
+
+/****************************************************************************
+ Receive a UDP/137 packet either via UDP or from the unexpected packet
+ queue. The packet must be a reply packet and have the specified trn_id.
+ The timeout is in milliseconds.
+***************************************************************************/
+
+struct packet_struct *receive_nmb_packet(int fd, int t, int trn_id)
+{
+ struct packet_struct *p;
+
+ p = receive_packet(fd, NMB_PACKET, t);
+
+ if (p && p->packet.nmb.header.response &&
+ p->packet.nmb.header.name_trn_id == trn_id) {
+ return p;
+ }
+ if (p)
+ free_packet(p);
+
+ /* try the unexpected packet queue */
+ return receive_unexpected(NMB_PACKET, trn_id, NULL);
+}
+
+/****************************************************************************
+ Receive a UDP/138 packet either via UDP or from the unexpected packet
+ queue. The packet must be a reply packet and have the specified mailslot name
+ The timeout is in milliseconds.
+***************************************************************************/
+
+struct packet_struct *receive_dgram_packet(int fd, int t, const char *mailslot_name)
+{
+ struct packet_struct *p;
+
+ p = receive_packet(fd, DGRAM_PACKET, t);
+
+ if (p && match_mailslot_name(p, mailslot_name)) {
+ return p;
+ }
+ if (p)
+ free_packet(p);
+
+ /* try the unexpected packet queue */
+ return receive_unexpected(DGRAM_PACKET, 0, mailslot_name);
+}
+
+/****************************************************************************
+ See if a datagram has the right mailslot name.
+***************************************************************************/
+
+BOOL match_mailslot_name(struct packet_struct *p, const char *mailslot_name)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ char *buf;
+
+ buf = &dgram->data[0];
+ buf -= 4;
+
+ buf = smb_buf(buf);
+
+ if (memcmp(buf, mailslot_name, strlen(mailslot_name)+1) == 0) {
+ return True;
+ }
+
+ return False;
+}
+
+/****************************************************************************
+ Return the number of bits that match between two 4 character buffers
+***************************************************************************/
+
+int matching_quad_bits(unsigned char *p1, unsigned char *p2)
+{
+ int i, j, ret = 0;
+ for (i=0; i<4; i++) {
+ if (p1[i] != p2[i])
+ break;
+ ret += 8;
+ }
+
+ if (i==4)
+ return ret;
+
+ for (j=0; j<8; j++) {
+ if ((p1[i] & (1<<(7-j))) != (p2[i] & (1<<(7-j))))
+ break;
+ ret++;
+ }
+
+ return ret;
+}
+
+static unsigned char sort_ip[4];
+
+/****************************************************************************
+ Compare two query reply records.
+***************************************************************************/
+
+static int name_query_comp(unsigned char *p1, unsigned char *p2)
+{
+ return matching_quad_bits(p2+2, sort_ip) - matching_quad_bits(p1+2, sort_ip);
+}
+
+/****************************************************************************
+ Sort a set of 6 byte name query response records so that the IPs that
+ have the most leading bits in common with the specified address come first.
+***************************************************************************/
+
+void sort_query_replies(char *data, int n, struct in_addr ip)
+{
+ if (n <= 1)
+ return;
+
+ putip(sort_ip, (char *)&ip);
+
+ qsort(data, n, 6, QSORT_CAST name_query_comp);
+}
+
+#define TRUNCATE_NETBIOS_NAME 1
+
+/*******************************************************************
+ Convert, possibly using a stupid microsoft-ism which has destroyed
+ the transport independence of netbios (for CIFS vendors that usually
+ use the Win95-type methods, not for NT to NT communication, which uses
+ DCE/RPC and therefore full-length unicode strings...) a dns name into
+ a netbios name.
+
+ The netbios name (NOT necessarily null-terminated) is truncated to 15
+ characters.
+
+ ******************************************************************/
+
+char *dns_to_netbios_name(const char *dns_name)
+{
+ static nstring netbios_name;
+ int i;
+ StrnCpy(netbios_name, dns_name, MAX_NETBIOSNAME_LEN-1);
+ netbios_name[15] = 0;
+
+#ifdef TRUNCATE_NETBIOS_NAME
+ /* ok. this is because of a stupid microsoft-ism. if the called host
+ name contains a '.', microsoft clients expect you to truncate the
+ netbios name up to and including the '.' this even applies, by
+ mistake, to workgroup (domain) names, which is _really_ daft.
+ */
+ for (i = 0; i >= 15; i--) {
+ if (netbios_name[i] == '.') {
+ netbios_name[i] = 0;
+ break;
+ }
+ }
+#endif /* TRUNCATE_NETBIOS_NAME */
+
+ return netbios_name;
+}
+
+/****************************************************************************
+ Interpret the weird netbios "name" into a unix fstring. Return the name type.
+****************************************************************************/
+
+static int name_interpret(char *in, fstring name)
+{
+ int ret;
+ int len = (*in++) / 2;
+ fstring out_string;
+ char *out = out_string;
+
+ *out=0;
+
+ if (len > 30 || len<1)
+ return(0);
+
+ while (len--) {
+ if (in[0] < 'A' || in[0] > 'P' || in[1] < 'A' || in[1] > 'P') {
+ *out = 0;
+ return(0);
+ }
+ *out = ((in[0]-'A')<<4) + (in[1]-'A');
+ in += 2;
+ out++;
+ }
+ ret = out[-1];
+ out[-1] = 0;
+
+#ifdef NETBIOS_SCOPE
+ /* Handle any scope names */
+ while(*in) {
+ *out++ = '.'; /* Scope names are separated by periods */
+ len = *(unsigned char *)in++;
+ StrnCpy(out, in, len);
+ out += len;
+ *out=0;
+ in += len;
+ }
+#endif
+ pull_ascii_fstring(name, out_string);
+
+ return(ret);
+}
+
+/****************************************************************************
+ Mangle a name into netbios format.
+ Note: <Out> must be (33 + strlen(scope) + 2) bytes long, at minimum.
+****************************************************************************/
+
+int name_mangle( char *In, char *Out, char name_type )
+{
+ int i;
+ int len;
+ nstring buf;
+ char *p = Out;
+
+ /* Safely copy the input string, In, into buf[]. */
+ if (strcmp(In,"*") == 0)
+ put_name(buf, "*", '\0', 0x00);
+ else {
+ /* We use an fstring here as mb dos names can expend x3 when
+ going to utf8. */
+ fstring buf_unix;
+ nstring buf_dos;
+
+ pull_ascii_fstring(buf_unix, In);
+ strupper_m(buf_unix);
+
+ push_ascii_nstring(buf_dos, buf_unix);
+ put_name(buf, buf_dos, ' ', name_type);
+ }
+
+ /* Place the length of the first field into the output buffer. */
+ p[0] = 32;
+ p++;
+
+ /* Now convert the name to the rfc1001/1002 format. */
+ for( i = 0; i < MAX_NETBIOSNAME_LEN; i++ ) {
+ p[i*2] = ( (buf[i] >> 4) & 0x000F ) + 'A';
+ p[(i*2)+1] = (buf[i] & 0x000F) + 'A';
+ }
+ p += 32;
+ p[0] = '\0';
+
+ /* Add the scope string. */
+ for( i = 0, len = 0; *(global_scope()) != '\0'; i++, len++ ) {
+ switch( (global_scope())[i] ) {
+ case '\0':
+ p[0] = len;
+ if( len > 0 )
+ p[len+1] = 0;
+ return( name_len(Out) );
+ case '.':
+ p[0] = len;
+ p += (len + 1);
+ len = -1;
+ break;
+ default:
+ p[len+1] = (global_scope())[i];
+ break;
+ }
+ }
+
+ return( name_len(Out) );
+}
+
+/****************************************************************************
+ Find a pointer to a netbios name.
+****************************************************************************/
+
+static char *name_ptr(char *buf,int ofs)
+{
+ unsigned char c = *(unsigned char *)(buf+ofs);
+
+ if ((c & 0xC0) == 0xC0) {
+ uint16 l = RSVAL(buf, ofs) & 0x3FFF;
+ DEBUG(5,("name ptr to pos %d from %d is %s\n",l,ofs,buf+l));
+ return(buf + l);
+ } else {
+ return(buf+ofs);
+ }
+}
+
+/****************************************************************************
+ Extract a netbios name from a buf (into a unix string) return name type.
+****************************************************************************/
+
+int name_extract(char *buf,int ofs, fstring name)
+{
+ char *p = name_ptr(buf,ofs);
+ int d = PTR_DIFF(p,buf+ofs);
+
+ name[0] = '\0';
+ if (d < -50 || d > 50)
+ return(0);
+ return(name_interpret(p,name));
+}
+
+/****************************************************************************
+ Return the total storage length of a mangled name.
+****************************************************************************/
+
+int name_len(char *s1)
+{
+ /* NOTE: this argument _must_ be unsigned */
+ unsigned char *s = (unsigned char *)s1;
+ int len;
+
+ /* If the two high bits of the byte are set, return 2. */
+ if (0xC0 == (*s & 0xC0))
+ return(2);
+
+ /* Add up the length bytes. */
+ for (len = 1; (*s); s += (*s) + 1) {
+ len += *s + 1;
+ SMB_ASSERT(len < 80);
+ }
+
+ return(len);
+}
diff --git a/source/libsmb/nterr.c b/source/libsmb/nterr.c
new file mode 100644
index 00000000000..166229ec6c4
--- /dev/null
+++ b/source/libsmb/nterr.c
@@ -0,0 +1,719 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Luke Kenneth Casson Leighton 1997-2001.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* NT error codes. please read nterr.h */
+
+#include "includes.h"
+
+typedef const struct
+{
+ const char *nt_errstr;
+ NTSTATUS nt_errcode;
+} nt_err_code_struct;
+
+static nt_err_code_struct nt_errs[] =
+{
+ { "NT_STATUS_OK", NT_STATUS_OK },
+ { "NT_STATUS_UNSUCCESSFUL", NT_STATUS_UNSUCCESSFUL },
+ { "NT_STATUS_NOT_IMPLEMENTED", NT_STATUS_NOT_IMPLEMENTED },
+ { "NT_STATUS_INVALID_INFO_CLASS", NT_STATUS_INVALID_INFO_CLASS },
+ { "NT_STATUS_INFO_LENGTH_MISMATCH", NT_STATUS_INFO_LENGTH_MISMATCH },
+ { "NT_STATUS_ACCESS_VIOLATION", NT_STATUS_ACCESS_VIOLATION },
+ { "STATUS_BUFFER_OVERFLOW", STATUS_BUFFER_OVERFLOW },
+ { "NT_STATUS_IN_PAGE_ERROR", NT_STATUS_IN_PAGE_ERROR },
+ { "NT_STATUS_PAGEFILE_QUOTA", NT_STATUS_PAGEFILE_QUOTA },
+ { "NT_STATUS_INVALID_HANDLE", NT_STATUS_INVALID_HANDLE },
+ { "NT_STATUS_BAD_INITIAL_STACK", NT_STATUS_BAD_INITIAL_STACK },
+ { "NT_STATUS_BAD_INITIAL_PC", NT_STATUS_BAD_INITIAL_PC },
+ { "NT_STATUS_INVALID_CID", NT_STATUS_INVALID_CID },
+ { "NT_STATUS_TIMER_NOT_CANCELED", NT_STATUS_TIMER_NOT_CANCELED },
+ { "NT_STATUS_INVALID_PARAMETER", NT_STATUS_INVALID_PARAMETER },
+ { "NT_STATUS_NO_SUCH_DEVICE", NT_STATUS_NO_SUCH_DEVICE },
+ { "NT_STATUS_NO_SUCH_FILE", NT_STATUS_NO_SUCH_FILE },
+ { "NT_STATUS_INVALID_DEVICE_REQUEST", NT_STATUS_INVALID_DEVICE_REQUEST },
+ { "NT_STATUS_END_OF_FILE", NT_STATUS_END_OF_FILE },
+ { "NT_STATUS_WRONG_VOLUME", NT_STATUS_WRONG_VOLUME },
+ { "NT_STATUS_NO_MEDIA_IN_DEVICE", NT_STATUS_NO_MEDIA_IN_DEVICE },
+ { "NT_STATUS_UNRECOGNIZED_MEDIA", NT_STATUS_UNRECOGNIZED_MEDIA },
+ { "NT_STATUS_NONEXISTENT_SECTOR", NT_STATUS_NONEXISTENT_SECTOR },
+ { "NT_STATUS_MORE_PROCESSING_REQUIRED", NT_STATUS_MORE_PROCESSING_REQUIRED },
+ { "NT_STATUS_NO_MEMORY", NT_STATUS_NO_MEMORY },
+ { "NT_STATUS_CONFLICTING_ADDRESSES", NT_STATUS_CONFLICTING_ADDRESSES },
+ { "NT_STATUS_NOT_MAPPED_VIEW", NT_STATUS_NOT_MAPPED_VIEW },
+ { "NT_STATUS_UNABLE_TO_FREE_VM", NT_STATUS_UNABLE_TO_FREE_VM },
+ { "NT_STATUS_UNABLE_TO_DELETE_SECTION", NT_STATUS_UNABLE_TO_DELETE_SECTION },
+ { "NT_STATUS_INVALID_SYSTEM_SERVICE", NT_STATUS_INVALID_SYSTEM_SERVICE },
+ { "NT_STATUS_ILLEGAL_INSTRUCTION", NT_STATUS_ILLEGAL_INSTRUCTION },
+ { "NT_STATUS_INVALID_LOCK_SEQUENCE", NT_STATUS_INVALID_LOCK_SEQUENCE },
+ { "NT_STATUS_INVALID_VIEW_SIZE", NT_STATUS_INVALID_VIEW_SIZE },
+ { "NT_STATUS_INVALID_FILE_FOR_SECTION", NT_STATUS_INVALID_FILE_FOR_SECTION },
+ { "NT_STATUS_ALREADY_COMMITTED", NT_STATUS_ALREADY_COMMITTED },
+ { "NT_STATUS_ACCESS_DENIED", NT_STATUS_ACCESS_DENIED },
+ { "NT_STATUS_BUFFER_TOO_SMALL", NT_STATUS_BUFFER_TOO_SMALL },
+ { "NT_STATUS_OBJECT_TYPE_MISMATCH", NT_STATUS_OBJECT_TYPE_MISMATCH },
+ { "NT_STATUS_NONCONTINUABLE_EXCEPTION", NT_STATUS_NONCONTINUABLE_EXCEPTION },
+ { "NT_STATUS_INVALID_DISPOSITION", NT_STATUS_INVALID_DISPOSITION },
+ { "NT_STATUS_UNWIND", NT_STATUS_UNWIND },
+ { "NT_STATUS_BAD_STACK", NT_STATUS_BAD_STACK },
+ { "NT_STATUS_INVALID_UNWIND_TARGET", NT_STATUS_INVALID_UNWIND_TARGET },
+ { "NT_STATUS_NOT_LOCKED", NT_STATUS_NOT_LOCKED },
+ { "NT_STATUS_PARITY_ERROR", NT_STATUS_PARITY_ERROR },
+ { "NT_STATUS_UNABLE_TO_DECOMMIT_VM", NT_STATUS_UNABLE_TO_DECOMMIT_VM },
+ { "NT_STATUS_NOT_COMMITTED", NT_STATUS_NOT_COMMITTED },
+ { "NT_STATUS_INVALID_PORT_ATTRIBUTES", NT_STATUS_INVALID_PORT_ATTRIBUTES },
+ { "NT_STATUS_PORT_MESSAGE_TOO_LONG", NT_STATUS_PORT_MESSAGE_TOO_LONG },
+ { "NT_STATUS_INVALID_PARAMETER_MIX", NT_STATUS_INVALID_PARAMETER_MIX },
+ { "NT_STATUS_INVALID_QUOTA_LOWER", NT_STATUS_INVALID_QUOTA_LOWER },
+ { "NT_STATUS_DISK_CORRUPT_ERROR", NT_STATUS_DISK_CORRUPT_ERROR },
+ { "NT_STATUS_OBJECT_NAME_INVALID", NT_STATUS_OBJECT_NAME_INVALID },
+ { "NT_STATUS_OBJECT_NAME_NOT_FOUND", NT_STATUS_OBJECT_NAME_NOT_FOUND },
+ { "NT_STATUS_OBJECT_NAME_COLLISION", NT_STATUS_OBJECT_NAME_COLLISION },
+ { "NT_STATUS_HANDLE_NOT_WAITABLE", NT_STATUS_HANDLE_NOT_WAITABLE },
+ { "NT_STATUS_PORT_DISCONNECTED", NT_STATUS_PORT_DISCONNECTED },
+ { "NT_STATUS_DEVICE_ALREADY_ATTACHED", NT_STATUS_DEVICE_ALREADY_ATTACHED },
+ { "NT_STATUS_OBJECT_PATH_INVALID", NT_STATUS_OBJECT_PATH_INVALID },
+ { "NT_STATUS_OBJECT_PATH_NOT_FOUND", NT_STATUS_OBJECT_PATH_NOT_FOUND },
+ { "NT_STATUS_OBJECT_PATH_SYNTAX_BAD", NT_STATUS_OBJECT_PATH_SYNTAX_BAD },
+ { "NT_STATUS_DATA_OVERRUN", NT_STATUS_DATA_OVERRUN },
+ { "NT_STATUS_DATA_LATE_ERROR", NT_STATUS_DATA_LATE_ERROR },
+ { "NT_STATUS_DATA_ERROR", NT_STATUS_DATA_ERROR },
+ { "NT_STATUS_CRC_ERROR", NT_STATUS_CRC_ERROR },
+ { "NT_STATUS_SECTION_TOO_BIG", NT_STATUS_SECTION_TOO_BIG },
+ { "NT_STATUS_PORT_CONNECTION_REFUSED", NT_STATUS_PORT_CONNECTION_REFUSED },
+ { "NT_STATUS_INVALID_PORT_HANDLE", NT_STATUS_INVALID_PORT_HANDLE },
+ { "NT_STATUS_SHARING_VIOLATION", NT_STATUS_SHARING_VIOLATION },
+ { "NT_STATUS_QUOTA_EXCEEDED", NT_STATUS_QUOTA_EXCEEDED },
+ { "NT_STATUS_INVALID_PAGE_PROTECTION", NT_STATUS_INVALID_PAGE_PROTECTION },
+ { "NT_STATUS_MUTANT_NOT_OWNED", NT_STATUS_MUTANT_NOT_OWNED },
+ { "NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED", NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED },
+ { "NT_STATUS_PORT_ALREADY_SET", NT_STATUS_PORT_ALREADY_SET },
+ { "NT_STATUS_SECTION_NOT_IMAGE", NT_STATUS_SECTION_NOT_IMAGE },
+ { "NT_STATUS_SUSPEND_COUNT_EXCEEDED", NT_STATUS_SUSPEND_COUNT_EXCEEDED },
+ { "NT_STATUS_THREAD_IS_TERMINATING", NT_STATUS_THREAD_IS_TERMINATING },
+ { "NT_STATUS_BAD_WORKING_SET_LIMIT", NT_STATUS_BAD_WORKING_SET_LIMIT },
+ { "NT_STATUS_INCOMPATIBLE_FILE_MAP", NT_STATUS_INCOMPATIBLE_FILE_MAP },
+ { "NT_STATUS_SECTION_PROTECTION", NT_STATUS_SECTION_PROTECTION },
+ { "NT_STATUS_EAS_NOT_SUPPORTED", NT_STATUS_EAS_NOT_SUPPORTED },
+ { "NT_STATUS_EA_TOO_LARGE", NT_STATUS_EA_TOO_LARGE },
+ { "NT_STATUS_NONEXISTENT_EA_ENTRY", NT_STATUS_NONEXISTENT_EA_ENTRY },
+ { "NT_STATUS_NO_EAS_ON_FILE", NT_STATUS_NO_EAS_ON_FILE },
+ { "NT_STATUS_EA_CORRUPT_ERROR", NT_STATUS_EA_CORRUPT_ERROR },
+ { "NT_STATUS_FILE_LOCK_CONFLICT", NT_STATUS_FILE_LOCK_CONFLICT },
+ { "NT_STATUS_LOCK_NOT_GRANTED", NT_STATUS_LOCK_NOT_GRANTED },
+ { "NT_STATUS_DELETE_PENDING", NT_STATUS_DELETE_PENDING },
+ { "NT_STATUS_CTL_FILE_NOT_SUPPORTED", NT_STATUS_CTL_FILE_NOT_SUPPORTED },
+ { "NT_STATUS_UNKNOWN_REVISION", NT_STATUS_UNKNOWN_REVISION },
+ { "NT_STATUS_REVISION_MISMATCH", NT_STATUS_REVISION_MISMATCH },
+ { "NT_STATUS_INVALID_OWNER", NT_STATUS_INVALID_OWNER },
+ { "NT_STATUS_INVALID_PRIMARY_GROUP", NT_STATUS_INVALID_PRIMARY_GROUP },
+ { "NT_STATUS_NO_IMPERSONATION_TOKEN", NT_STATUS_NO_IMPERSONATION_TOKEN },
+ { "NT_STATUS_CANT_DISABLE_MANDATORY", NT_STATUS_CANT_DISABLE_MANDATORY },
+ { "NT_STATUS_NO_LOGON_SERVERS", NT_STATUS_NO_LOGON_SERVERS },
+ { "NT_STATUS_NO_SUCH_LOGON_SESSION", NT_STATUS_NO_SUCH_LOGON_SESSION },
+ { "NT_STATUS_NO_SUCH_PRIVILEGE", NT_STATUS_NO_SUCH_PRIVILEGE },
+ { "NT_STATUS_PRIVILEGE_NOT_HELD", NT_STATUS_PRIVILEGE_NOT_HELD },
+ { "NT_STATUS_INVALID_ACCOUNT_NAME", NT_STATUS_INVALID_ACCOUNT_NAME },
+ { "NT_STATUS_USER_EXISTS", NT_STATUS_USER_EXISTS },
+ { "NT_STATUS_NO_SUCH_USER", NT_STATUS_NO_SUCH_USER },
+ { "NT_STATUS_GROUP_EXISTS", NT_STATUS_GROUP_EXISTS },
+ { "NT_STATUS_NO_SUCH_GROUP", NT_STATUS_NO_SUCH_GROUP },
+ { "NT_STATUS_MEMBER_IN_GROUP", NT_STATUS_MEMBER_IN_GROUP },
+ { "NT_STATUS_MEMBER_NOT_IN_GROUP", NT_STATUS_MEMBER_NOT_IN_GROUP },
+ { "NT_STATUS_LAST_ADMIN", NT_STATUS_LAST_ADMIN },
+ { "NT_STATUS_WRONG_PASSWORD", NT_STATUS_WRONG_PASSWORD },
+ { "NT_STATUS_ILL_FORMED_PASSWORD", NT_STATUS_ILL_FORMED_PASSWORD },
+ { "NT_STATUS_PASSWORD_RESTRICTION", NT_STATUS_PASSWORD_RESTRICTION },
+ { "NT_STATUS_LOGON_FAILURE", NT_STATUS_LOGON_FAILURE },
+ { "NT_STATUS_ACCOUNT_RESTRICTION", NT_STATUS_ACCOUNT_RESTRICTION },
+ { "NT_STATUS_INVALID_LOGON_HOURS", NT_STATUS_INVALID_LOGON_HOURS },
+ { "NT_STATUS_INVALID_WORKSTATION", NT_STATUS_INVALID_WORKSTATION },
+ { "NT_STATUS_PASSWORD_EXPIRED", NT_STATUS_PASSWORD_EXPIRED },
+ { "NT_STATUS_ACCOUNT_DISABLED", NT_STATUS_ACCOUNT_DISABLED },
+ { "NT_STATUS_NONE_MAPPED", NT_STATUS_NONE_MAPPED },
+ { "NT_STATUS_TOO_MANY_LUIDS_REQUESTED", NT_STATUS_TOO_MANY_LUIDS_REQUESTED },
+ { "NT_STATUS_LUIDS_EXHAUSTED", NT_STATUS_LUIDS_EXHAUSTED },
+ { "NT_STATUS_INVALID_SUB_AUTHORITY", NT_STATUS_INVALID_SUB_AUTHORITY },
+ { "NT_STATUS_INVALID_ACL", NT_STATUS_INVALID_ACL },
+ { "NT_STATUS_INVALID_SID", NT_STATUS_INVALID_SID },
+ { "NT_STATUS_INVALID_SECURITY_DESCR", NT_STATUS_INVALID_SECURITY_DESCR },
+ { "NT_STATUS_PROCEDURE_NOT_FOUND", NT_STATUS_PROCEDURE_NOT_FOUND },
+ { "NT_STATUS_INVALID_IMAGE_FORMAT", NT_STATUS_INVALID_IMAGE_FORMAT },
+ { "NT_STATUS_NO_TOKEN", NT_STATUS_NO_TOKEN },
+ { "NT_STATUS_BAD_INHERITANCE_ACL", NT_STATUS_BAD_INHERITANCE_ACL },
+ { "NT_STATUS_RANGE_NOT_LOCKED", NT_STATUS_RANGE_NOT_LOCKED },
+ { "NT_STATUS_DISK_FULL", NT_STATUS_DISK_FULL },
+ { "NT_STATUS_SERVER_DISABLED", NT_STATUS_SERVER_DISABLED },
+ { "NT_STATUS_SERVER_NOT_DISABLED", NT_STATUS_SERVER_NOT_DISABLED },
+ { "NT_STATUS_TOO_MANY_GUIDS_REQUESTED", NT_STATUS_TOO_MANY_GUIDS_REQUESTED },
+ { "NT_STATUS_GUIDS_EXHAUSTED", NT_STATUS_GUIDS_EXHAUSTED },
+ { "NT_STATUS_INVALID_ID_AUTHORITY", NT_STATUS_INVALID_ID_AUTHORITY },
+ { "NT_STATUS_AGENTS_EXHAUSTED", NT_STATUS_AGENTS_EXHAUSTED },
+ { "NT_STATUS_INVALID_VOLUME_LABEL", NT_STATUS_INVALID_VOLUME_LABEL },
+ { "NT_STATUS_SECTION_NOT_EXTENDED", NT_STATUS_SECTION_NOT_EXTENDED },
+ { "NT_STATUS_NOT_MAPPED_DATA", NT_STATUS_NOT_MAPPED_DATA },
+ { "NT_STATUS_RESOURCE_DATA_NOT_FOUND", NT_STATUS_RESOURCE_DATA_NOT_FOUND },
+ { "NT_STATUS_RESOURCE_TYPE_NOT_FOUND", NT_STATUS_RESOURCE_TYPE_NOT_FOUND },
+ { "NT_STATUS_RESOURCE_NAME_NOT_FOUND", NT_STATUS_RESOURCE_NAME_NOT_FOUND },
+ { "NT_STATUS_ARRAY_BOUNDS_EXCEEDED", NT_STATUS_ARRAY_BOUNDS_EXCEEDED },
+ { "NT_STATUS_FLOAT_DENORMAL_OPERAND", NT_STATUS_FLOAT_DENORMAL_OPERAND },
+ { "NT_STATUS_FLOAT_DIVIDE_BY_ZERO", NT_STATUS_FLOAT_DIVIDE_BY_ZERO },
+ { "NT_STATUS_FLOAT_INEXACT_RESULT", NT_STATUS_FLOAT_INEXACT_RESULT },
+ { "NT_STATUS_FLOAT_INVALID_OPERATION", NT_STATUS_FLOAT_INVALID_OPERATION },
+ { "NT_STATUS_FLOAT_OVERFLOW", NT_STATUS_FLOAT_OVERFLOW },
+ { "NT_STATUS_FLOAT_STACK_CHECK", NT_STATUS_FLOAT_STACK_CHECK },
+ { "NT_STATUS_FLOAT_UNDERFLOW", NT_STATUS_FLOAT_UNDERFLOW },
+ { "NT_STATUS_INTEGER_DIVIDE_BY_ZERO", NT_STATUS_INTEGER_DIVIDE_BY_ZERO },
+ { "NT_STATUS_INTEGER_OVERFLOW", NT_STATUS_INTEGER_OVERFLOW },
+ { "NT_STATUS_PRIVILEGED_INSTRUCTION", NT_STATUS_PRIVILEGED_INSTRUCTION },
+ { "NT_STATUS_TOO_MANY_PAGING_FILES", NT_STATUS_TOO_MANY_PAGING_FILES },
+ { "NT_STATUS_FILE_INVALID", NT_STATUS_FILE_INVALID },
+ { "NT_STATUS_ALLOTTED_SPACE_EXCEEDED", NT_STATUS_ALLOTTED_SPACE_EXCEEDED },
+ { "NT_STATUS_INSUFFICIENT_RESOURCES", NT_STATUS_INSUFFICIENT_RESOURCES },
+ { "NT_STATUS_DFS_EXIT_PATH_FOUND", NT_STATUS_DFS_EXIT_PATH_FOUND },
+ { "NT_STATUS_DEVICE_DATA_ERROR", NT_STATUS_DEVICE_DATA_ERROR },
+ { "NT_STATUS_DEVICE_NOT_CONNECTED", NT_STATUS_DEVICE_NOT_CONNECTED },
+ { "NT_STATUS_DEVICE_POWER_FAILURE", NT_STATUS_DEVICE_POWER_FAILURE },
+ { "NT_STATUS_FREE_VM_NOT_AT_BASE", NT_STATUS_FREE_VM_NOT_AT_BASE },
+ { "NT_STATUS_MEMORY_NOT_ALLOCATED", NT_STATUS_MEMORY_NOT_ALLOCATED },
+ { "NT_STATUS_WORKING_SET_QUOTA", NT_STATUS_WORKING_SET_QUOTA },
+ { "NT_STATUS_MEDIA_WRITE_PROTECTED", NT_STATUS_MEDIA_WRITE_PROTECTED },
+ { "NT_STATUS_DEVICE_NOT_READY", NT_STATUS_DEVICE_NOT_READY },
+ { "NT_STATUS_INVALID_GROUP_ATTRIBUTES", NT_STATUS_INVALID_GROUP_ATTRIBUTES },
+ { "NT_STATUS_BAD_IMPERSONATION_LEVEL", NT_STATUS_BAD_IMPERSONATION_LEVEL },
+ { "NT_STATUS_CANT_OPEN_ANONYMOUS", NT_STATUS_CANT_OPEN_ANONYMOUS },
+ { "NT_STATUS_BAD_VALIDATION_CLASS", NT_STATUS_BAD_VALIDATION_CLASS },
+ { "NT_STATUS_BAD_TOKEN_TYPE", NT_STATUS_BAD_TOKEN_TYPE },
+ { "NT_STATUS_BAD_MASTER_BOOT_RECORD", NT_STATUS_BAD_MASTER_BOOT_RECORD },
+ { "NT_STATUS_INSTRUCTION_MISALIGNMENT", NT_STATUS_INSTRUCTION_MISALIGNMENT },
+ { "NT_STATUS_INSTANCE_NOT_AVAILABLE", NT_STATUS_INSTANCE_NOT_AVAILABLE },
+ { "NT_STATUS_PIPE_NOT_AVAILABLE", NT_STATUS_PIPE_NOT_AVAILABLE },
+ { "NT_STATUS_INVALID_PIPE_STATE", NT_STATUS_INVALID_PIPE_STATE },
+ { "NT_STATUS_PIPE_BUSY", NT_STATUS_PIPE_BUSY },
+ { "NT_STATUS_ILLEGAL_FUNCTION", NT_STATUS_ILLEGAL_FUNCTION },
+ { "NT_STATUS_PIPE_DISCONNECTED", NT_STATUS_PIPE_DISCONNECTED },
+ { "NT_STATUS_PIPE_CLOSING", NT_STATUS_PIPE_CLOSING },
+ { "NT_STATUS_PIPE_CONNECTED", NT_STATUS_PIPE_CONNECTED },
+ { "NT_STATUS_PIPE_LISTENING", NT_STATUS_PIPE_LISTENING },
+ { "NT_STATUS_INVALID_READ_MODE", NT_STATUS_INVALID_READ_MODE },
+ { "NT_STATUS_IO_TIMEOUT", NT_STATUS_IO_TIMEOUT },
+ { "NT_STATUS_FILE_FORCED_CLOSED", NT_STATUS_FILE_FORCED_CLOSED },
+ { "NT_STATUS_PROFILING_NOT_STARTED", NT_STATUS_PROFILING_NOT_STARTED },
+ { "NT_STATUS_PROFILING_NOT_STOPPED", NT_STATUS_PROFILING_NOT_STOPPED },
+ { "NT_STATUS_COULD_NOT_INTERPRET", NT_STATUS_COULD_NOT_INTERPRET },
+ { "NT_STATUS_FILE_IS_A_DIRECTORY", NT_STATUS_FILE_IS_A_DIRECTORY },
+ { "NT_STATUS_NOT_SUPPORTED", NT_STATUS_NOT_SUPPORTED },
+ { "NT_STATUS_REMOTE_NOT_LISTENING", NT_STATUS_REMOTE_NOT_LISTENING },
+ { "NT_STATUS_DUPLICATE_NAME", NT_STATUS_DUPLICATE_NAME },
+ { "NT_STATUS_BAD_NETWORK_PATH", NT_STATUS_BAD_NETWORK_PATH },
+ { "NT_STATUS_NETWORK_BUSY", NT_STATUS_NETWORK_BUSY },
+ { "NT_STATUS_DEVICE_DOES_NOT_EXIST", NT_STATUS_DEVICE_DOES_NOT_EXIST },
+ { "NT_STATUS_TOO_MANY_COMMANDS", NT_STATUS_TOO_MANY_COMMANDS },
+ { "NT_STATUS_ADAPTER_HARDWARE_ERROR", NT_STATUS_ADAPTER_HARDWARE_ERROR },
+ { "NT_STATUS_INVALID_NETWORK_RESPONSE", NT_STATUS_INVALID_NETWORK_RESPONSE },
+ { "NT_STATUS_UNEXPECTED_NETWORK_ERROR", NT_STATUS_UNEXPECTED_NETWORK_ERROR },
+ { "NT_STATUS_BAD_REMOTE_ADAPTER", NT_STATUS_BAD_REMOTE_ADAPTER },
+ { "NT_STATUS_PRINT_QUEUE_FULL", NT_STATUS_PRINT_QUEUE_FULL },
+ { "NT_STATUS_NO_SPOOL_SPACE", NT_STATUS_NO_SPOOL_SPACE },
+ { "NT_STATUS_PRINT_CANCELLED", NT_STATUS_PRINT_CANCELLED },
+ { "NT_STATUS_NETWORK_NAME_DELETED", NT_STATUS_NETWORK_NAME_DELETED },
+ { "NT_STATUS_NETWORK_ACCESS_DENIED", NT_STATUS_NETWORK_ACCESS_DENIED },
+ { "NT_STATUS_BAD_DEVICE_TYPE", NT_STATUS_BAD_DEVICE_TYPE },
+ { "NT_STATUS_BAD_NETWORK_NAME", NT_STATUS_BAD_NETWORK_NAME },
+ { "NT_STATUS_TOO_MANY_NAMES", NT_STATUS_TOO_MANY_NAMES },
+ { "NT_STATUS_TOO_MANY_SESSIONS", NT_STATUS_TOO_MANY_SESSIONS },
+ { "NT_STATUS_SHARING_PAUSED", NT_STATUS_SHARING_PAUSED },
+ { "NT_STATUS_REQUEST_NOT_ACCEPTED", NT_STATUS_REQUEST_NOT_ACCEPTED },
+ { "NT_STATUS_REDIRECTOR_PAUSED", NT_STATUS_REDIRECTOR_PAUSED },
+ { "NT_STATUS_NET_WRITE_FAULT", NT_STATUS_NET_WRITE_FAULT },
+ { "NT_STATUS_PROFILING_AT_LIMIT", NT_STATUS_PROFILING_AT_LIMIT },
+ { "NT_STATUS_NOT_SAME_DEVICE", NT_STATUS_NOT_SAME_DEVICE },
+ { "NT_STATUS_FILE_RENAMED", NT_STATUS_FILE_RENAMED },
+ { "NT_STATUS_VIRTUAL_CIRCUIT_CLOSED", NT_STATUS_VIRTUAL_CIRCUIT_CLOSED },
+ { "NT_STATUS_NO_SECURITY_ON_OBJECT", NT_STATUS_NO_SECURITY_ON_OBJECT },
+ { "NT_STATUS_CANT_WAIT", NT_STATUS_CANT_WAIT },
+ { "NT_STATUS_PIPE_EMPTY", NT_STATUS_PIPE_EMPTY },
+ { "NT_STATUS_CANT_ACCESS_DOMAIN_INFO", NT_STATUS_CANT_ACCESS_DOMAIN_INFO },
+ { "NT_STATUS_CANT_TERMINATE_SELF", NT_STATUS_CANT_TERMINATE_SELF },
+ { "NT_STATUS_INVALID_SERVER_STATE", NT_STATUS_INVALID_SERVER_STATE },
+ { "NT_STATUS_INVALID_DOMAIN_STATE", NT_STATUS_INVALID_DOMAIN_STATE },
+ { "NT_STATUS_INVALID_DOMAIN_ROLE", NT_STATUS_INVALID_DOMAIN_ROLE },
+ { "NT_STATUS_NO_SUCH_DOMAIN", NT_STATUS_NO_SUCH_DOMAIN },
+ { "NT_STATUS_DOMAIN_EXISTS", NT_STATUS_DOMAIN_EXISTS },
+ { "NT_STATUS_DOMAIN_LIMIT_EXCEEDED", NT_STATUS_DOMAIN_LIMIT_EXCEEDED },
+ { "NT_STATUS_OPLOCK_NOT_GRANTED", NT_STATUS_OPLOCK_NOT_GRANTED },
+ { "NT_STATUS_INVALID_OPLOCK_PROTOCOL", NT_STATUS_INVALID_OPLOCK_PROTOCOL },
+ { "NT_STATUS_INTERNAL_DB_CORRUPTION", NT_STATUS_INTERNAL_DB_CORRUPTION },
+ { "NT_STATUS_INTERNAL_ERROR", NT_STATUS_INTERNAL_ERROR },
+ { "NT_STATUS_GENERIC_NOT_MAPPED", NT_STATUS_GENERIC_NOT_MAPPED },
+ { "NT_STATUS_BAD_DESCRIPTOR_FORMAT", NT_STATUS_BAD_DESCRIPTOR_FORMAT },
+ { "NT_STATUS_INVALID_USER_BUFFER", NT_STATUS_INVALID_USER_BUFFER },
+ { "NT_STATUS_UNEXPECTED_IO_ERROR", NT_STATUS_UNEXPECTED_IO_ERROR },
+ { "NT_STATUS_UNEXPECTED_MM_CREATE_ERR", NT_STATUS_UNEXPECTED_MM_CREATE_ERR },
+ { "NT_STATUS_UNEXPECTED_MM_MAP_ERROR", NT_STATUS_UNEXPECTED_MM_MAP_ERROR },
+ { "NT_STATUS_UNEXPECTED_MM_EXTEND_ERR", NT_STATUS_UNEXPECTED_MM_EXTEND_ERR },
+ { "NT_STATUS_NOT_LOGON_PROCESS", NT_STATUS_NOT_LOGON_PROCESS },
+ { "NT_STATUS_LOGON_SESSION_EXISTS", NT_STATUS_LOGON_SESSION_EXISTS },
+ { "NT_STATUS_INVALID_PARAMETER_1", NT_STATUS_INVALID_PARAMETER_1 },
+ { "NT_STATUS_INVALID_PARAMETER_2", NT_STATUS_INVALID_PARAMETER_2 },
+ { "NT_STATUS_INVALID_PARAMETER_3", NT_STATUS_INVALID_PARAMETER_3 },
+ { "NT_STATUS_INVALID_PARAMETER_4", NT_STATUS_INVALID_PARAMETER_4 },
+ { "NT_STATUS_INVALID_PARAMETER_5", NT_STATUS_INVALID_PARAMETER_5 },
+ { "NT_STATUS_INVALID_PARAMETER_6", NT_STATUS_INVALID_PARAMETER_6 },
+ { "NT_STATUS_INVALID_PARAMETER_7", NT_STATUS_INVALID_PARAMETER_7 },
+ { "NT_STATUS_INVALID_PARAMETER_8", NT_STATUS_INVALID_PARAMETER_8 },
+ { "NT_STATUS_INVALID_PARAMETER_9", NT_STATUS_INVALID_PARAMETER_9 },
+ { "NT_STATUS_INVALID_PARAMETER_10", NT_STATUS_INVALID_PARAMETER_10 },
+ { "NT_STATUS_INVALID_PARAMETER_11", NT_STATUS_INVALID_PARAMETER_11 },
+ { "NT_STATUS_INVALID_PARAMETER_12", NT_STATUS_INVALID_PARAMETER_12 },
+ { "NT_STATUS_REDIRECTOR_NOT_STARTED", NT_STATUS_REDIRECTOR_NOT_STARTED },
+ { "NT_STATUS_REDIRECTOR_STARTED", NT_STATUS_REDIRECTOR_STARTED },
+ { "NT_STATUS_STACK_OVERFLOW", NT_STATUS_STACK_OVERFLOW },
+ { "NT_STATUS_NO_SUCH_PACKAGE", NT_STATUS_NO_SUCH_PACKAGE },
+ { "NT_STATUS_BAD_FUNCTION_TABLE", NT_STATUS_BAD_FUNCTION_TABLE },
+ { "NT_STATUS_DIRECTORY_NOT_EMPTY", NT_STATUS_DIRECTORY_NOT_EMPTY },
+ { "NT_STATUS_FILE_CORRUPT_ERROR", NT_STATUS_FILE_CORRUPT_ERROR },
+ { "NT_STATUS_NOT_A_DIRECTORY", NT_STATUS_NOT_A_DIRECTORY },
+ { "NT_STATUS_BAD_LOGON_SESSION_STATE", NT_STATUS_BAD_LOGON_SESSION_STATE },
+ { "NT_STATUS_LOGON_SESSION_COLLISION", NT_STATUS_LOGON_SESSION_COLLISION },
+ { "NT_STATUS_NAME_TOO_LONG", NT_STATUS_NAME_TOO_LONG },
+ { "NT_STATUS_FILES_OPEN", NT_STATUS_FILES_OPEN },
+ { "NT_STATUS_CONNECTION_IN_USE", NT_STATUS_CONNECTION_IN_USE },
+ { "NT_STATUS_MESSAGE_NOT_FOUND", NT_STATUS_MESSAGE_NOT_FOUND },
+ { "NT_STATUS_PROCESS_IS_TERMINATING", NT_STATUS_PROCESS_IS_TERMINATING },
+ { "NT_STATUS_INVALID_LOGON_TYPE", NT_STATUS_INVALID_LOGON_TYPE },
+ { "NT_STATUS_NO_GUID_TRANSLATION", NT_STATUS_NO_GUID_TRANSLATION },
+ { "NT_STATUS_CANNOT_IMPERSONATE", NT_STATUS_CANNOT_IMPERSONATE },
+ { "NT_STATUS_IMAGE_ALREADY_LOADED", NT_STATUS_IMAGE_ALREADY_LOADED },
+ { "NT_STATUS_ABIOS_NOT_PRESENT", NT_STATUS_ABIOS_NOT_PRESENT },
+ { "NT_STATUS_ABIOS_LID_NOT_EXIST", NT_STATUS_ABIOS_LID_NOT_EXIST },
+ { "NT_STATUS_ABIOS_LID_ALREADY_OWNED", NT_STATUS_ABIOS_LID_ALREADY_OWNED },
+ { "NT_STATUS_ABIOS_NOT_LID_OWNER", NT_STATUS_ABIOS_NOT_LID_OWNER },
+ { "NT_STATUS_ABIOS_INVALID_COMMAND", NT_STATUS_ABIOS_INVALID_COMMAND },
+ { "NT_STATUS_ABIOS_INVALID_LID", NT_STATUS_ABIOS_INVALID_LID },
+ { "NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE", NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE },
+ { "NT_STATUS_ABIOS_INVALID_SELECTOR", NT_STATUS_ABIOS_INVALID_SELECTOR },
+ { "NT_STATUS_NO_LDT", NT_STATUS_NO_LDT },
+ { "NT_STATUS_INVALID_LDT_SIZE", NT_STATUS_INVALID_LDT_SIZE },
+ { "NT_STATUS_INVALID_LDT_OFFSET", NT_STATUS_INVALID_LDT_OFFSET },
+ { "NT_STATUS_INVALID_LDT_DESCRIPTOR", NT_STATUS_INVALID_LDT_DESCRIPTOR },
+ { "NT_STATUS_INVALID_IMAGE_NE_FORMAT", NT_STATUS_INVALID_IMAGE_NE_FORMAT },
+ { "NT_STATUS_RXACT_INVALID_STATE", NT_STATUS_RXACT_INVALID_STATE },
+ { "NT_STATUS_RXACT_COMMIT_FAILURE", NT_STATUS_RXACT_COMMIT_FAILURE },
+ { "NT_STATUS_MAPPED_FILE_SIZE_ZERO", NT_STATUS_MAPPED_FILE_SIZE_ZERO },
+ { "NT_STATUS_TOO_MANY_OPENED_FILES", NT_STATUS_TOO_MANY_OPENED_FILES },
+ { "NT_STATUS_CANCELLED", NT_STATUS_CANCELLED },
+ { "NT_STATUS_CANNOT_DELETE", NT_STATUS_CANNOT_DELETE },
+ { "NT_STATUS_INVALID_COMPUTER_NAME", NT_STATUS_INVALID_COMPUTER_NAME },
+ { "NT_STATUS_FILE_DELETED", NT_STATUS_FILE_DELETED },
+ { "NT_STATUS_SPECIAL_ACCOUNT", NT_STATUS_SPECIAL_ACCOUNT },
+ { "NT_STATUS_SPECIAL_GROUP", NT_STATUS_SPECIAL_GROUP },
+ { "NT_STATUS_SPECIAL_USER", NT_STATUS_SPECIAL_USER },
+ { "NT_STATUS_MEMBERS_PRIMARY_GROUP", NT_STATUS_MEMBERS_PRIMARY_GROUP },
+ { "NT_STATUS_FILE_CLOSED", NT_STATUS_FILE_CLOSED },
+ { "NT_STATUS_TOO_MANY_THREADS", NT_STATUS_TOO_MANY_THREADS },
+ { "NT_STATUS_THREAD_NOT_IN_PROCESS", NT_STATUS_THREAD_NOT_IN_PROCESS },
+ { "NT_STATUS_TOKEN_ALREADY_IN_USE", NT_STATUS_TOKEN_ALREADY_IN_USE },
+ { "NT_STATUS_PAGEFILE_QUOTA_EXCEEDED", NT_STATUS_PAGEFILE_QUOTA_EXCEEDED },
+ { "NT_STATUS_COMMITMENT_LIMIT", NT_STATUS_COMMITMENT_LIMIT },
+ { "NT_STATUS_INVALID_IMAGE_LE_FORMAT", NT_STATUS_INVALID_IMAGE_LE_FORMAT },
+ { "NT_STATUS_INVALID_IMAGE_NOT_MZ", NT_STATUS_INVALID_IMAGE_NOT_MZ },
+ { "NT_STATUS_INVALID_IMAGE_PROTECT", NT_STATUS_INVALID_IMAGE_PROTECT },
+ { "NT_STATUS_INVALID_IMAGE_WIN_16", NT_STATUS_INVALID_IMAGE_WIN_16 },
+ { "NT_STATUS_LOGON_SERVER_CONFLICT", NT_STATUS_LOGON_SERVER_CONFLICT },
+ { "NT_STATUS_TIME_DIFFERENCE_AT_DC", NT_STATUS_TIME_DIFFERENCE_AT_DC },
+ { "NT_STATUS_SYNCHRONIZATION_REQUIRED", NT_STATUS_SYNCHRONIZATION_REQUIRED },
+ { "NT_STATUS_DLL_NOT_FOUND", NT_STATUS_DLL_NOT_FOUND },
+ { "NT_STATUS_OPEN_FAILED", NT_STATUS_OPEN_FAILED },
+ { "NT_STATUS_IO_PRIVILEGE_FAILED", NT_STATUS_IO_PRIVILEGE_FAILED },
+ { "NT_STATUS_ORDINAL_NOT_FOUND", NT_STATUS_ORDINAL_NOT_FOUND },
+ { "NT_STATUS_ENTRYPOINT_NOT_FOUND", NT_STATUS_ENTRYPOINT_NOT_FOUND },
+ { "NT_STATUS_CONTROL_C_EXIT", NT_STATUS_CONTROL_C_EXIT },
+ { "NT_STATUS_LOCAL_DISCONNECT", NT_STATUS_LOCAL_DISCONNECT },
+ { "NT_STATUS_REMOTE_DISCONNECT", NT_STATUS_REMOTE_DISCONNECT },
+ { "NT_STATUS_REMOTE_RESOURCES", NT_STATUS_REMOTE_RESOURCES },
+ { "NT_STATUS_LINK_FAILED", NT_STATUS_LINK_FAILED },
+ { "NT_STATUS_LINK_TIMEOUT", NT_STATUS_LINK_TIMEOUT },
+ { "NT_STATUS_INVALID_CONNECTION", NT_STATUS_INVALID_CONNECTION },
+ { "NT_STATUS_INVALID_ADDRESS", NT_STATUS_INVALID_ADDRESS },
+ { "NT_STATUS_DLL_INIT_FAILED", NT_STATUS_DLL_INIT_FAILED },
+ { "NT_STATUS_MISSING_SYSTEMFILE", NT_STATUS_MISSING_SYSTEMFILE },
+ { "NT_STATUS_UNHANDLED_EXCEPTION", NT_STATUS_UNHANDLED_EXCEPTION },
+ { "NT_STATUS_APP_INIT_FAILURE", NT_STATUS_APP_INIT_FAILURE },
+ { "NT_STATUS_PAGEFILE_CREATE_FAILED", NT_STATUS_PAGEFILE_CREATE_FAILED },
+ { "NT_STATUS_NO_PAGEFILE", NT_STATUS_NO_PAGEFILE },
+ { "NT_STATUS_INVALID_LEVEL", NT_STATUS_INVALID_LEVEL },
+ { "NT_STATUS_WRONG_PASSWORD_CORE", NT_STATUS_WRONG_PASSWORD_CORE },
+ { "NT_STATUS_ILLEGAL_FLOAT_CONTEXT", NT_STATUS_ILLEGAL_FLOAT_CONTEXT },
+ { "NT_STATUS_PIPE_BROKEN", NT_STATUS_PIPE_BROKEN },
+ { "NT_STATUS_REGISTRY_CORRUPT", NT_STATUS_REGISTRY_CORRUPT },
+ { "NT_STATUS_REGISTRY_IO_FAILED", NT_STATUS_REGISTRY_IO_FAILED },
+ { "NT_STATUS_NO_EVENT_PAIR", NT_STATUS_NO_EVENT_PAIR },
+ { "NT_STATUS_UNRECOGNIZED_VOLUME", NT_STATUS_UNRECOGNIZED_VOLUME },
+ { "NT_STATUS_SERIAL_NO_DEVICE_INITED", NT_STATUS_SERIAL_NO_DEVICE_INITED },
+ { "NT_STATUS_NO_SUCH_ALIAS", NT_STATUS_NO_SUCH_ALIAS },
+ { "NT_STATUS_MEMBER_NOT_IN_ALIAS", NT_STATUS_MEMBER_NOT_IN_ALIAS },
+ { "NT_STATUS_MEMBER_IN_ALIAS", NT_STATUS_MEMBER_IN_ALIAS },
+ { "NT_STATUS_ALIAS_EXISTS", NT_STATUS_ALIAS_EXISTS },
+ { "NT_STATUS_LOGON_NOT_GRANTED", NT_STATUS_LOGON_NOT_GRANTED },
+ { "NT_STATUS_TOO_MANY_SECRETS", NT_STATUS_TOO_MANY_SECRETS },
+ { "NT_STATUS_SECRET_TOO_LONG", NT_STATUS_SECRET_TOO_LONG },
+ { "NT_STATUS_INTERNAL_DB_ERROR", NT_STATUS_INTERNAL_DB_ERROR },
+ { "NT_STATUS_FULLSCREEN_MODE", NT_STATUS_FULLSCREEN_MODE },
+ { "NT_STATUS_TOO_MANY_CONTEXT_IDS", NT_STATUS_TOO_MANY_CONTEXT_IDS },
+ { "NT_STATUS_LOGON_TYPE_NOT_GRANTED", NT_STATUS_LOGON_TYPE_NOT_GRANTED },
+ { "NT_STATUS_NOT_REGISTRY_FILE", NT_STATUS_NOT_REGISTRY_FILE },
+ { "NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED", NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED },
+ { "NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR", NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR },
+ { "NT_STATUS_FT_MISSING_MEMBER", NT_STATUS_FT_MISSING_MEMBER },
+ { "NT_STATUS_ILL_FORMED_SERVICE_ENTRY", NT_STATUS_ILL_FORMED_SERVICE_ENTRY },
+ { "NT_STATUS_ILLEGAL_CHARACTER", NT_STATUS_ILLEGAL_CHARACTER },
+ { "NT_STATUS_UNMAPPABLE_CHARACTER", NT_STATUS_UNMAPPABLE_CHARACTER },
+ { "NT_STATUS_UNDEFINED_CHARACTER", NT_STATUS_UNDEFINED_CHARACTER },
+ { "NT_STATUS_FLOPPY_VOLUME", NT_STATUS_FLOPPY_VOLUME },
+ { "NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND", NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND },
+ { "NT_STATUS_FLOPPY_WRONG_CYLINDER", NT_STATUS_FLOPPY_WRONG_CYLINDER },
+ { "NT_STATUS_FLOPPY_UNKNOWN_ERROR", NT_STATUS_FLOPPY_UNKNOWN_ERROR },
+ { "NT_STATUS_FLOPPY_BAD_REGISTERS", NT_STATUS_FLOPPY_BAD_REGISTERS },
+ { "NT_STATUS_DISK_RECALIBRATE_FAILED", NT_STATUS_DISK_RECALIBRATE_FAILED },
+ { "NT_STATUS_DISK_OPERATION_FAILED", NT_STATUS_DISK_OPERATION_FAILED },
+ { "NT_STATUS_DISK_RESET_FAILED", NT_STATUS_DISK_RESET_FAILED },
+ { "NT_STATUS_SHARED_IRQ_BUSY", NT_STATUS_SHARED_IRQ_BUSY },
+ { "NT_STATUS_FT_ORPHANING", NT_STATUS_FT_ORPHANING },
+ { "NT_STATUS_PARTITION_FAILURE", NT_STATUS_PARTITION_FAILURE },
+ { "NT_STATUS_INVALID_BLOCK_LENGTH", NT_STATUS_INVALID_BLOCK_LENGTH },
+ { "NT_STATUS_DEVICE_NOT_PARTITIONED", NT_STATUS_DEVICE_NOT_PARTITIONED },
+ { "NT_STATUS_UNABLE_TO_LOCK_MEDIA", NT_STATUS_UNABLE_TO_LOCK_MEDIA },
+ { "NT_STATUS_UNABLE_TO_UNLOAD_MEDIA", NT_STATUS_UNABLE_TO_UNLOAD_MEDIA },
+ { "NT_STATUS_EOM_OVERFLOW", NT_STATUS_EOM_OVERFLOW },
+ { "NT_STATUS_NO_MEDIA", NT_STATUS_NO_MEDIA },
+ { "NT_STATUS_NO_SUCH_MEMBER", NT_STATUS_NO_SUCH_MEMBER },
+ { "NT_STATUS_INVALID_MEMBER", NT_STATUS_INVALID_MEMBER },
+ { "NT_STATUS_KEY_DELETED", NT_STATUS_KEY_DELETED },
+ { "NT_STATUS_NO_LOG_SPACE", NT_STATUS_NO_LOG_SPACE },
+ { "NT_STATUS_TOO_MANY_SIDS", NT_STATUS_TOO_MANY_SIDS },
+ { "NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED", NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED },
+ { "NT_STATUS_KEY_HAS_CHILDREN", NT_STATUS_KEY_HAS_CHILDREN },
+ { "NT_STATUS_CHILD_MUST_BE_VOLATILE", NT_STATUS_CHILD_MUST_BE_VOLATILE },
+ { "NT_STATUS_DEVICE_CONFIGURATION_ERROR", NT_STATUS_DEVICE_CONFIGURATION_ERROR },
+ { "NT_STATUS_DRIVER_INTERNAL_ERROR", NT_STATUS_DRIVER_INTERNAL_ERROR },
+ { "NT_STATUS_INVALID_DEVICE_STATE", NT_STATUS_INVALID_DEVICE_STATE },
+ { "NT_STATUS_IO_DEVICE_ERROR", NT_STATUS_IO_DEVICE_ERROR },
+ { "NT_STATUS_DEVICE_PROTOCOL_ERROR", NT_STATUS_DEVICE_PROTOCOL_ERROR },
+ { "NT_STATUS_BACKUP_CONTROLLER", NT_STATUS_BACKUP_CONTROLLER },
+ { "NT_STATUS_LOG_FILE_FULL", NT_STATUS_LOG_FILE_FULL },
+ { "NT_STATUS_TOO_LATE", NT_STATUS_TOO_LATE },
+ { "NT_STATUS_NO_TRUST_LSA_SECRET", NT_STATUS_NO_TRUST_LSA_SECRET },
+ { "NT_STATUS_NO_TRUST_SAM_ACCOUNT", NT_STATUS_NO_TRUST_SAM_ACCOUNT },
+ { "NT_STATUS_TRUSTED_DOMAIN_FAILURE", NT_STATUS_TRUSTED_DOMAIN_FAILURE },
+ { "NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE", NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE },
+ { "NT_STATUS_EVENTLOG_FILE_CORRUPT", NT_STATUS_EVENTLOG_FILE_CORRUPT },
+ { "NT_STATUS_EVENTLOG_CANT_START", NT_STATUS_EVENTLOG_CANT_START },
+ { "NT_STATUS_TRUST_FAILURE", NT_STATUS_TRUST_FAILURE },
+ { "NT_STATUS_MUTANT_LIMIT_EXCEEDED", NT_STATUS_MUTANT_LIMIT_EXCEEDED },
+ { "NT_STATUS_NETLOGON_NOT_STARTED", NT_STATUS_NETLOGON_NOT_STARTED },
+ { "NT_STATUS_ACCOUNT_EXPIRED", NT_STATUS_ACCOUNT_EXPIRED },
+ { "NT_STATUS_POSSIBLE_DEADLOCK", NT_STATUS_POSSIBLE_DEADLOCK },
+ { "NT_STATUS_NETWORK_CREDENTIAL_CONFLICT", NT_STATUS_NETWORK_CREDENTIAL_CONFLICT },
+ { "NT_STATUS_REMOTE_SESSION_LIMIT", NT_STATUS_REMOTE_SESSION_LIMIT },
+ { "NT_STATUS_EVENTLOG_FILE_CHANGED", NT_STATUS_EVENTLOG_FILE_CHANGED },
+ { "NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT", NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT },
+ { "NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT", NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT },
+ { "NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT", NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT },
+ { "NT_STATUS_DOMAIN_TRUST_INCONSISTENT", NT_STATUS_DOMAIN_TRUST_INCONSISTENT },
+ { "NT_STATUS_FS_DRIVER_REQUIRED", NT_STATUS_FS_DRIVER_REQUIRED },
+ { "NT_STATUS_NO_USER_SESSION_KEY", NT_STATUS_NO_USER_SESSION_KEY },
+ { "NT_STATUS_USER_SESSION_DELETED", NT_STATUS_USER_SESSION_DELETED },
+ { "NT_STATUS_RESOURCE_LANG_NOT_FOUND", NT_STATUS_RESOURCE_LANG_NOT_FOUND },
+ { "NT_STATUS_INSUFF_SERVER_RESOURCES", NT_STATUS_INSUFF_SERVER_RESOURCES },
+ { "NT_STATUS_INVALID_BUFFER_SIZE", NT_STATUS_INVALID_BUFFER_SIZE },
+ { "NT_STATUS_INVALID_ADDRESS_COMPONENT", NT_STATUS_INVALID_ADDRESS_COMPONENT },
+ { "NT_STATUS_INVALID_ADDRESS_WILDCARD", NT_STATUS_INVALID_ADDRESS_WILDCARD },
+ { "NT_STATUS_TOO_MANY_ADDRESSES", NT_STATUS_TOO_MANY_ADDRESSES },
+ { "NT_STATUS_ADDRESS_ALREADY_EXISTS", NT_STATUS_ADDRESS_ALREADY_EXISTS },
+ { "NT_STATUS_ADDRESS_CLOSED", NT_STATUS_ADDRESS_CLOSED },
+ { "NT_STATUS_CONNECTION_DISCONNECTED", NT_STATUS_CONNECTION_DISCONNECTED },
+ { "NT_STATUS_CONNECTION_RESET", NT_STATUS_CONNECTION_RESET },
+ { "NT_STATUS_TOO_MANY_NODES", NT_STATUS_TOO_MANY_NODES },
+ { "NT_STATUS_TRANSACTION_ABORTED", NT_STATUS_TRANSACTION_ABORTED },
+ { "NT_STATUS_TRANSACTION_TIMED_OUT", NT_STATUS_TRANSACTION_TIMED_OUT },
+ { "NT_STATUS_TRANSACTION_NO_RELEASE", NT_STATUS_TRANSACTION_NO_RELEASE },
+ { "NT_STATUS_TRANSACTION_NO_MATCH", NT_STATUS_TRANSACTION_NO_MATCH },
+ { "NT_STATUS_TRANSACTION_RESPONDED", NT_STATUS_TRANSACTION_RESPONDED },
+ { "NT_STATUS_TRANSACTION_INVALID_ID", NT_STATUS_TRANSACTION_INVALID_ID },
+ { "NT_STATUS_TRANSACTION_INVALID_TYPE", NT_STATUS_TRANSACTION_INVALID_TYPE },
+ { "NT_STATUS_NOT_SERVER_SESSION", NT_STATUS_NOT_SERVER_SESSION },
+ { "NT_STATUS_NOT_CLIENT_SESSION", NT_STATUS_NOT_CLIENT_SESSION },
+ { "NT_STATUS_CANNOT_LOAD_REGISTRY_FILE", NT_STATUS_CANNOT_LOAD_REGISTRY_FILE },
+ { "NT_STATUS_DEBUG_ATTACH_FAILED", NT_STATUS_DEBUG_ATTACH_FAILED },
+ { "NT_STATUS_SYSTEM_PROCESS_TERMINATED", NT_STATUS_SYSTEM_PROCESS_TERMINATED },
+ { "NT_STATUS_DATA_NOT_ACCEPTED", NT_STATUS_DATA_NOT_ACCEPTED },
+ { "NT_STATUS_NO_BROWSER_SERVERS_FOUND", NT_STATUS_NO_BROWSER_SERVERS_FOUND },
+ { "NT_STATUS_VDM_HARD_ERROR", NT_STATUS_VDM_HARD_ERROR },
+ { "NT_STATUS_DRIVER_CANCEL_TIMEOUT", NT_STATUS_DRIVER_CANCEL_TIMEOUT },
+ { "NT_STATUS_REPLY_MESSAGE_MISMATCH", NT_STATUS_REPLY_MESSAGE_MISMATCH },
+ { "NT_STATUS_MAPPED_ALIGNMENT", NT_STATUS_MAPPED_ALIGNMENT },
+ { "NT_STATUS_IMAGE_CHECKSUM_MISMATCH", NT_STATUS_IMAGE_CHECKSUM_MISMATCH },
+ { "NT_STATUS_LOST_WRITEBEHIND_DATA", NT_STATUS_LOST_WRITEBEHIND_DATA },
+ { "NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID", NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID },
+ { "NT_STATUS_PASSWORD_MUST_CHANGE", NT_STATUS_PASSWORD_MUST_CHANGE },
+ { "NT_STATUS_NOT_FOUND", NT_STATUS_NOT_FOUND },
+ { "NT_STATUS_NOT_TINY_STREAM", NT_STATUS_NOT_TINY_STREAM },
+ { "NT_STATUS_RECOVERY_FAILURE", NT_STATUS_RECOVERY_FAILURE },
+ { "NT_STATUS_STACK_OVERFLOW_READ", NT_STATUS_STACK_OVERFLOW_READ },
+ { "NT_STATUS_FAIL_CHECK", NT_STATUS_FAIL_CHECK },
+ { "NT_STATUS_DUPLICATE_OBJECTID", NT_STATUS_DUPLICATE_OBJECTID },
+ { "NT_STATUS_OBJECTID_EXISTS", NT_STATUS_OBJECTID_EXISTS },
+ { "NT_STATUS_CONVERT_TO_LARGE", NT_STATUS_CONVERT_TO_LARGE },
+ { "NT_STATUS_RETRY", NT_STATUS_RETRY },
+ { "NT_STATUS_FOUND_OUT_OF_SCOPE", NT_STATUS_FOUND_OUT_OF_SCOPE },
+ { "NT_STATUS_ALLOCATE_BUCKET", NT_STATUS_ALLOCATE_BUCKET },
+ { "NT_STATUS_PROPSET_NOT_FOUND", NT_STATUS_PROPSET_NOT_FOUND },
+ { "NT_STATUS_MARSHALL_OVERFLOW", NT_STATUS_MARSHALL_OVERFLOW },
+ { "NT_STATUS_INVALID_VARIANT", NT_STATUS_INVALID_VARIANT },
+ { "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND", NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND },
+ { "NT_STATUS_ACCOUNT_LOCKED_OUT", NT_STATUS_ACCOUNT_LOCKED_OUT },
+ { "NT_STATUS_HANDLE_NOT_CLOSABLE", NT_STATUS_HANDLE_NOT_CLOSABLE },
+ { "NT_STATUS_CONNECTION_REFUSED", NT_STATUS_CONNECTION_REFUSED },
+ { "NT_STATUS_GRACEFUL_DISCONNECT", NT_STATUS_GRACEFUL_DISCONNECT },
+ { "NT_STATUS_ADDRESS_ALREADY_ASSOCIATED", NT_STATUS_ADDRESS_ALREADY_ASSOCIATED },
+ { "NT_STATUS_ADDRESS_NOT_ASSOCIATED", NT_STATUS_ADDRESS_NOT_ASSOCIATED },
+ { "NT_STATUS_CONNECTION_INVALID", NT_STATUS_CONNECTION_INVALID },
+ { "NT_STATUS_CONNECTION_ACTIVE", NT_STATUS_CONNECTION_ACTIVE },
+ { "NT_STATUS_NETWORK_UNREACHABLE", NT_STATUS_NETWORK_UNREACHABLE },
+ { "NT_STATUS_HOST_UNREACHABLE", NT_STATUS_HOST_UNREACHABLE },
+ { "NT_STATUS_PROTOCOL_UNREACHABLE", NT_STATUS_PROTOCOL_UNREACHABLE },
+ { "NT_STATUS_PORT_UNREACHABLE", NT_STATUS_PORT_UNREACHABLE },
+ { "NT_STATUS_REQUEST_ABORTED", NT_STATUS_REQUEST_ABORTED },
+ { "NT_STATUS_CONNECTION_ABORTED", NT_STATUS_CONNECTION_ABORTED },
+ { "NT_STATUS_BAD_COMPRESSION_BUFFER", NT_STATUS_BAD_COMPRESSION_BUFFER },
+ { "NT_STATUS_USER_MAPPED_FILE", NT_STATUS_USER_MAPPED_FILE },
+ { "NT_STATUS_AUDIT_FAILED", NT_STATUS_AUDIT_FAILED },
+ { "NT_STATUS_TIMER_RESOLUTION_NOT_SET", NT_STATUS_TIMER_RESOLUTION_NOT_SET },
+ { "NT_STATUS_CONNECTION_COUNT_LIMIT", NT_STATUS_CONNECTION_COUNT_LIMIT },
+ { "NT_STATUS_LOGIN_TIME_RESTRICTION", NT_STATUS_LOGIN_TIME_RESTRICTION },
+ { "NT_STATUS_LOGIN_WKSTA_RESTRICTION", NT_STATUS_LOGIN_WKSTA_RESTRICTION },
+ { "NT_STATUS_IMAGE_MP_UP_MISMATCH", NT_STATUS_IMAGE_MP_UP_MISMATCH },
+ { "NT_STATUS_INSUFFICIENT_LOGON_INFO", NT_STATUS_INSUFFICIENT_LOGON_INFO },
+ { "NT_STATUS_BAD_DLL_ENTRYPOINT", NT_STATUS_BAD_DLL_ENTRYPOINT },
+ { "NT_STATUS_BAD_SERVICE_ENTRYPOINT", NT_STATUS_BAD_SERVICE_ENTRYPOINT },
+ { "NT_STATUS_LPC_REPLY_LOST", NT_STATUS_LPC_REPLY_LOST },
+ { "NT_STATUS_IP_ADDRESS_CONFLICT1", NT_STATUS_IP_ADDRESS_CONFLICT1 },
+ { "NT_STATUS_IP_ADDRESS_CONFLICT2", NT_STATUS_IP_ADDRESS_CONFLICT2 },
+ { "NT_STATUS_REGISTRY_QUOTA_LIMIT", NT_STATUS_REGISTRY_QUOTA_LIMIT },
+ { "NT_STATUS_PATH_NOT_COVERED", NT_STATUS_PATH_NOT_COVERED },
+ { "NT_STATUS_NO_CALLBACK_ACTIVE", NT_STATUS_NO_CALLBACK_ACTIVE },
+ { "NT_STATUS_LICENSE_QUOTA_EXCEEDED", NT_STATUS_LICENSE_QUOTA_EXCEEDED },
+ { "NT_STATUS_PWD_TOO_SHORT", NT_STATUS_PWD_TOO_SHORT },
+ { "NT_STATUS_PWD_TOO_RECENT", NT_STATUS_PWD_TOO_RECENT },
+ { "NT_STATUS_PWD_HISTORY_CONFLICT", NT_STATUS_PWD_HISTORY_CONFLICT },
+ { "NT_STATUS_PLUGPLAY_NO_DEVICE", NT_STATUS_PLUGPLAY_NO_DEVICE },
+ { "NT_STATUS_UNSUPPORTED_COMPRESSION", NT_STATUS_UNSUPPORTED_COMPRESSION },
+ { "NT_STATUS_INVALID_HW_PROFILE", NT_STATUS_INVALID_HW_PROFILE },
+ { "NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH", NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH },
+ { "NT_STATUS_DRIVER_ORDINAL_NOT_FOUND", NT_STATUS_DRIVER_ORDINAL_NOT_FOUND },
+ { "NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND", NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND },
+ { "NT_STATUS_RESOURCE_NOT_OWNED", NT_STATUS_RESOURCE_NOT_OWNED },
+ { "NT_STATUS_TOO_MANY_LINKS", NT_STATUS_TOO_MANY_LINKS },
+ { "NT_STATUS_QUOTA_LIST_INCONSISTENT", NT_STATUS_QUOTA_LIST_INCONSISTENT },
+ { "NT_STATUS_FILE_IS_OFFLINE", NT_STATUS_FILE_IS_OFFLINE },
+ { "NT_STATUS_NOT_A_REPARSE_POINT", NT_STATUS_NOT_A_REPARSE_POINT },
+ { "NT_STATUS_NO_MORE_ENTRIES", NT_STATUS_NO_MORE_ENTRIES },
+ { "STATUS_MORE_ENTRIES", STATUS_MORE_ENTRIES },
+ { "STATUS_SOME_UNMAPPED", STATUS_SOME_UNMAPPED },
+ { NULL, NT_STATUS(0) }
+};
+
+nt_err_code_struct nt_err_desc[] =
+{
+ { "Success", NT_STATUS_OK },
+ { "Undetermined error", NT_STATUS_UNSUCCESSFUL },
+ { "Access denied", NT_STATUS_ACCESS_DENIED },
+ { "Account locked out", NT_STATUS_ACCOUNT_LOCKED_OUT },
+ { "Must change password", NT_STATUS_PASSWORD_MUST_CHANGE },
+ { "Password is too short", NT_STATUS_PWD_TOO_SHORT },
+ { "Password is too recent", NT_STATUS_PWD_TOO_RECENT },
+ { "Password history conflict", NT_STATUS_PWD_HISTORY_CONFLICT },
+ { "No logon servers", NT_STATUS_NO_LOGON_SERVERS },
+ { "Improperly formed account name", NT_STATUS_INVALID_ACCOUNT_NAME },
+ { "User exists", NT_STATUS_USER_EXISTS },
+ { "No such user", NT_STATUS_NO_SUCH_USER },
+ { "Group exists", NT_STATUS_GROUP_EXISTS },
+ { "No such group", NT_STATUS_NO_SUCH_GROUP },
+ { "Member not in group", NT_STATUS_MEMBER_NOT_IN_GROUP },
+ { "Wrong Password", NT_STATUS_WRONG_PASSWORD },
+ { "Ill formed password", NT_STATUS_ILL_FORMED_PASSWORD },
+ { "Password restriction", NT_STATUS_PASSWORD_RESTRICTION },
+ { "Logon failure", NT_STATUS_LOGON_FAILURE },
+ { "Account restriction", NT_STATUS_ACCOUNT_RESTRICTION },
+ { "Invalid logon hours", NT_STATUS_INVALID_LOGON_HOURS },
+ { "Invalid workstation", NT_STATUS_INVALID_WORKSTATION },
+ { "Password expired", NT_STATUS_PASSWORD_EXPIRED },
+ { "Account disabled", NT_STATUS_ACCOUNT_DISABLED },
+ { "Unexpected information received", NT_STATUS_INVALID_PARAMETER },
+ { "Memory allocation error", NT_STATUS_NO_MEMORY },
+ { "No domain controllers located", NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND },
+ { "Account locked out", NT_STATUS_ACCOUNT_LOCKED_OUT },
+ { "Named pipe not available", NT_STATUS_PIPE_NOT_AVAILABLE },
+ { "Not implemented", NT_STATUS_NOT_IMPLEMENTED },
+ { "Invalid information class", NT_STATUS_INVALID_INFO_CLASS },
+ { "Information length mismatch", NT_STATUS_INFO_LENGTH_MISMATCH },
+ { "Access violation", NT_STATUS_ACCESS_VIOLATION },
+ { "Invalid handle", NT_STATUS_INVALID_HANDLE },
+ { "Invalid parameter", NT_STATUS_INVALID_PARAMETER },
+ { "No memory", NT_STATUS_NO_MEMORY },
+ { "Buffer too small", NT_STATUS_BUFFER_TOO_SMALL },
+ { "Revision mismatch", NT_STATUS_REVISION_MISMATCH },
+ { "No logon servers", NT_STATUS_NO_LOGON_SERVERS },
+ { "No such logon session", NT_STATUS_NO_SUCH_LOGON_SESSION },
+ { "No such privilege", NT_STATUS_NO_SUCH_PRIVILEGE },
+ { "Procedure not found", NT_STATUS_PROCEDURE_NOT_FOUND },
+ { "Server disabled", NT_STATUS_SERVER_DISABLED },
+ { "Invalid pipe state", NT_STATUS_INVALID_PIPE_STATE },
+ { "Named pipe busy", NT_STATUS_PIPE_BUSY },
+ { "Illegal function", NT_STATUS_ILLEGAL_FUNCTION },
+ { "Named pipe dicconnected", NT_STATUS_PIPE_DISCONNECTED },
+ { "Named pipe closing", NT_STATUS_PIPE_CLOSING },
+ { "Remote host not listening", NT_STATUS_REMOTE_NOT_LISTENING },
+ { "Duplicate name on network", NT_STATUS_DUPLICATE_NAME },
+ { "Print queue is full", NT_STATUS_PRINT_QUEUE_FULL },
+ { "No print spool space available", NT_STATUS_NO_SPOOL_SPACE },
+ { "Too many names", NT_STATUS_TOO_MANY_NAMES },
+ { "Too many sessions", NT_STATUS_TOO_MANY_SESSIONS },
+ { "Invalid server state", NT_STATUS_INVALID_SERVER_STATE },
+ { "Invalid domain state", NT_STATUS_INVALID_DOMAIN_STATE },
+ { "Invalid domain role", NT_STATUS_INVALID_DOMAIN_ROLE },
+ { "No such domain", NT_STATUS_NO_SUCH_DOMAIN },
+ { "Domain exists", NT_STATUS_DOMAIN_EXISTS },
+ { "Domain limit exceeded", NT_STATUS_DOMAIN_LIMIT_EXCEEDED },
+ { "Bad logon session state", NT_STATUS_BAD_LOGON_SESSION_STATE },
+ { "Logon session collision", NT_STATUS_LOGON_SESSION_COLLISION },
+ { "Invalid logon type", NT_STATUS_INVALID_LOGON_TYPE },
+ { "Cancelled", NT_STATUS_CANCELLED },
+ { "Invalid computer name", NT_STATUS_INVALID_COMPUTER_NAME },
+ { "Logon server conflict", NT_STATUS_LOGON_SERVER_CONFLICT },
+ { "Time difference at domain controller", NT_STATUS_TIME_DIFFERENCE_AT_DC },
+ { "Pipe broken", NT_STATUS_PIPE_BROKEN },
+ { "Registry corrupt", NT_STATUS_REGISTRY_CORRUPT },
+ { "Too many secrets", NT_STATUS_TOO_MANY_SECRETS },
+ { "Too many SIDs", NT_STATUS_TOO_MANY_SIDS },
+ { "Lanmanager cross encryption required", NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED },
+ { "Log file full", NT_STATUS_LOG_FILE_FULL },
+ { "No trusted LSA secret", NT_STATUS_NO_TRUST_LSA_SECRET },
+ { "No trusted SAM account", NT_STATUS_NO_TRUST_SAM_ACCOUNT },
+ { "Trusted domain failure", NT_STATUS_TRUSTED_DOMAIN_FAILURE },
+ { "Trust relationship failure", NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE },
+ { "Trust failure", NT_STATUS_TRUST_FAILURE },
+ { "Netlogon service not started", NT_STATUS_NETLOGON_NOT_STARTED },
+ { "Account expired", NT_STATUS_ACCOUNT_EXPIRED },
+ { "Network credential conflict", NT_STATUS_NETWORK_CREDENTIAL_CONFLICT },
+ { "Remote session limit", NT_STATUS_REMOTE_SESSION_LIMIT },
+ { "No logon interdomain trust account", NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT },
+ { "No logon workstation trust account", NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT },
+ { "No logon server trust account", NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT },
+ { "Domain trust inconsistent", NT_STATUS_DOMAIN_TRUST_INCONSISTENT },
+ { "No user session key available", NT_STATUS_NO_USER_SESSION_KEY },
+ { "User session deleted", NT_STATUS_USER_SESSION_DELETED },
+ { "Insufficient server resources", NT_STATUS_INSUFF_SERVER_RESOURCES },
+ { "Insufficient logon information", NT_STATUS_INSUFFICIENT_LOGON_INFO },
+
+ { "License quota exceeded", NT_STATUS_LICENSE_QUOTA_EXCEEDED },
+
+ { NULL, NT_STATUS(0) }
+};
+
+
+/*****************************************************************************
+ returns an NT error message. not amazingly helpful, but better than a number.
+ *****************************************************************************/
+const char *nt_errstr(NTSTATUS nt_code)
+{
+ static pstring msg;
+ int idx = 0;
+
+ slprintf(msg, sizeof(msg), "NT code 0x%08x", NT_STATUS_V(nt_code));
+
+ while (nt_errs[idx].nt_errstr != NULL) {
+ if (NT_STATUS_V(nt_errs[idx].nt_errcode) ==
+ NT_STATUS_V(nt_code)) {
+ return nt_errs[idx].nt_errstr;
+ }
+ idx++;
+ }
+
+ return msg;
+}
+
+/************************************************************************
+ Print friendler version fo NT error code
+ ***********************************************************************/
+
+const char *get_friendly_nt_error_msg(NTSTATUS nt_code)
+{
+ int idx = 0;
+
+ while (nt_err_desc[idx].nt_errstr != NULL) {
+ if (NT_STATUS_V(nt_err_desc[idx].nt_errcode) == NT_STATUS_V(nt_code))
+ {
+ return nt_err_desc[idx].nt_errstr;
+ }
+ idx++;
+ }
+
+ /* fall back to NT_STATUS_XXX string */
+
+ return nt_errstr(nt_code);
+}
+
+/*****************************************************************************
+ returns an NT_STATUS constant as a string for inclusion in autogen C code
+ *****************************************************************************/
+const char *get_nt_error_c_code(NTSTATUS nt_code)
+{
+ static pstring out;
+ int idx = 0;
+
+ while (nt_errs[idx].nt_errstr != NULL) {
+ if (NT_STATUS_V(nt_errs[idx].nt_errcode) ==
+ NT_STATUS_V(nt_code)) {
+ return nt_errs[idx].nt_errstr;
+ }
+ idx++;
+ }
+
+ slprintf(out, sizeof(out), "NT_STATUS(0x%08x)", NT_STATUS_V(nt_code));
+
+ return out;
+}
+
+/*****************************************************************************
+ returns the NT_STATUS constant matching the string supplied (as an NTSTATUS)
+ *****************************************************************************/
+NTSTATUS nt_status_string_to_code(char *nt_status_str)
+{
+ int idx = 0;
+
+ while (nt_errs[idx].nt_errstr != NULL) {
+ if (strcmp(nt_errs[idx].nt_errstr, nt_status_str) == 0) {
+ return nt_errs[idx].nt_errcode;
+ }
+ idx++;
+ }
+ return NT_STATUS_UNSUCCESSFUL;
+}
diff --git a/source/libsmb/ntlm_check.c b/source/libsmb/ntlm_check.c
new file mode 100644
index 00000000000..362b640f913
--- /dev/null
+++ b/source/libsmb/ntlm_check.c
@@ -0,0 +1,377 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Andrew Bartlett 2001-2003
+ Copyright (C) Gerald Carter 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/****************************************************************************
+ Core of smb password checking routine.
+****************************************************************************/
+
+static BOOL smb_pwd_check_ntlmv1(const DATA_BLOB *nt_response,
+ const uchar *part_passwd,
+ const DATA_BLOB *sec_blob,
+ DATA_BLOB *user_sess_key)
+{
+ /* Finish the encryption of part_passwd. */
+ uchar p24[24];
+
+ if (part_passwd == NULL) {
+ DEBUG(10,("No password set - DISALLOWING access\n"));
+ /* No password set - always false ! */
+ return False;
+ }
+
+ if (sec_blob->length != 8) {
+ DEBUG(0, ("smb_pwd_check_ntlmv1: incorrect challenge size (%lu)\n",
+ (unsigned long)sec_blob->length));
+ return False;
+ }
+
+ if (nt_response->length != 24) {
+ DEBUG(0, ("smb_pwd_check_ntlmv1: incorrect password length (%lu)\n",
+ (unsigned long)nt_response->length));
+ return False;
+ }
+
+ SMBOWFencrypt(part_passwd, sec_blob->data, p24);
+ if (user_sess_key != NULL) {
+ *user_sess_key = data_blob(NULL, 16);
+ SMBsesskeygen_ntv1(part_passwd, NULL, user_sess_key->data);
+ }
+
+
+#if DEBUG_PASSWORD
+ DEBUG(100,("Part password (P16) was |\n"));
+ dump_data(100, part_passwd, 16);
+ DEBUGADD(100,("Password from client was |\n"));
+ dump_data(100, nt_response->data, nt_response->length);
+ DEBUGADD(100,("Given challenge was |\n"));
+ dump_data(100, sec_blob->data, sec_blob->length);
+ DEBUGADD(100,("Value from encryption was |\n"));
+ dump_data(100, p24, 24);
+#endif
+ return (memcmp(p24, nt_response->data, 24) == 0);
+}
+
+/****************************************************************************
+ Core of smb password checking routine. (NTLMv2, LMv2)
+ Note: The same code works with both NTLMv2 and LMv2.
+****************************************************************************/
+
+static BOOL smb_pwd_check_ntlmv2(const DATA_BLOB *ntv2_response,
+ const uchar *part_passwd,
+ const DATA_BLOB *sec_blob,
+ const char *user, const char *domain,
+ DATA_BLOB *user_sess_key)
+{
+ /* Finish the encryption of part_passwd. */
+ uchar kr[16];
+ uchar value_from_encryption[16];
+ uchar client_response[16];
+ DATA_BLOB client_key_data;
+
+ if (part_passwd == NULL) {
+ DEBUG(10,("No password set - DISALLOWING access\n"));
+ /* No password set - always False */
+ return False;
+ }
+
+ if (sec_blob->length != 8) {
+ DEBUG(0, ("smb_pwd_check_ntlmv2: incorrect challenge size (%lu)\n",
+ (unsigned long)sec_blob->length));
+ return False;
+ }
+
+ if (ntv2_response->length < 24) {
+ /* We MUST have more than 16 bytes, or the stuff below will go
+ crazy. No known implementation sends less than the 24 bytes
+ for LMv2, let alone NTLMv2. */
+ DEBUG(0, ("smb_pwd_check_ntlmv2: incorrect password length (%lu)\n",
+ (unsigned long)ntv2_response->length));
+ return False;
+ }
+
+ client_key_data = data_blob(ntv2_response->data+16, ntv2_response->length-16);
+ /*
+ todo: should we be checking this for anything? We can't for LMv2,
+ but for NTLMv2 it is meant to contain the current time etc.
+ */
+
+ memcpy(client_response, ntv2_response->data, sizeof(client_response));
+
+ if (!ntv2_owf_gen(part_passwd, user, domain, kr)) {
+ return False;
+ }
+
+ SMBOWFencrypt_ntv2(kr, sec_blob, &client_key_data, value_from_encryption);
+ if (user_sess_key != NULL) {
+ *user_sess_key = data_blob(NULL, 16);
+ SMBsesskeygen_ntv2(kr, value_from_encryption, user_sess_key->data);
+ }
+
+#if DEBUG_PASSWORD
+ DEBUG(100,("Part password (P16) was |\n"));
+ dump_data(100, part_passwd, 16);
+ DEBUGADD(100,("Password from client was |\n"));
+ dump_data(100, ntv2_response->data, ntv2_response->length);
+ DEBUGADD(100,("Variable data from client was |\n"));
+ dump_data(100, client_key_data.data, client_key_data.length);
+ DEBUGADD(100,("Given challenge was |\n"));
+ dump_data(100, sec_blob->data, sec_blob->length);
+ DEBUGADD(100,("Value from encryption was |\n"));
+ dump_data(100, value_from_encryption, 16);
+#endif
+ data_blob_clear_free(&client_key_data);
+ return (memcmp(value_from_encryption, client_response, 16) == 0);
+}
+
+/**
+ * Check a challenge-response password against the value of the NT or
+ * LM password hash.
+ *
+ * @param mem_ctx talloc context
+ * @param challenge 8-byte challenge. If all zero, forces plaintext comparison
+ * @param nt_response 'unicode' NT response to the challenge, or unicode password
+ * @param lm_response ASCII or LANMAN response to the challenge, or password in DOS code page
+ * @param username internal Samba username, for log messages
+ * @param client_username username the client used
+ * @param client_domain domain name the client used (may be mapped)
+ * @param nt_pw MD4 unicode password from our passdb or similar
+ * @param lm_pw LANMAN ASCII password from our passdb or similar
+ * @param user_sess_key User session key
+ * @param lm_sess_key LM session key (first 8 bytes of the LM hash)
+ */
+
+NTSTATUS ntlm_password_check(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *challenge,
+ const DATA_BLOB *lm_response,
+ const DATA_BLOB *nt_response,
+ const char *username,
+ const char *client_username,
+ const char *client_domain,
+ const uint8 *lm_pw, const uint8 *nt_pw,
+ DATA_BLOB *user_sess_key,
+ DATA_BLOB *lm_sess_key)
+{
+ static const unsigned char zeros[8];
+ if (nt_pw == NULL) {
+ DEBUG(3,("ntlm_password_check: NO NT password stored for user %s.\n",
+ username));
+ }
+
+ /* Check for cleartext netlogon. Used by Exchange 5.5. */
+ if (challenge->length == sizeof(zeros) &&
+ (memcmp(challenge->data, zeros, challenge->length) == 0 )) {
+
+ DEBUG(4,("ntlm_password_check: checking plaintext passwords for user %s\n",
+ username));
+ if (nt_pw && nt_response->length) {
+ unsigned char pwhash[16];
+ mdfour(pwhash, nt_response->data, nt_response->length);
+ if (memcmp(pwhash, nt_pw, sizeof(pwhash)) == 0) {
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("ntlm_password_check: NT (Unicode) plaintext password check failed for user %s\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ } else if (!lp_lanman_auth()) {
+ DEBUG(3,("ntlm_password_check: (plaintext password check) LANMAN passwords NOT PERMITTED for user %s\n",
+ username));
+
+ } else if (lm_pw && lm_response->length) {
+ uchar dospwd[14];
+ uchar p16[16];
+ ZERO_STRUCT(dospwd);
+
+ memcpy(dospwd, lm_response->data, MIN(lm_response->length, sizeof(dospwd)));
+ /* Only the fisrt 14 chars are considered, password need not be null terminated. */
+
+ /* we *might* need to upper-case the string here */
+ E_P16((const unsigned char *)dospwd, p16);
+
+ if (memcmp(p16, lm_pw, sizeof(p16)) == 0) {
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("ntlm_password_check: LANMAN (ASCII) plaintext password check failed for user %s\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ } else {
+ DEBUG(3, ("Plaintext authentication for user %s attempted, but neither NT nor LM passwords available\n", username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ }
+
+ if (nt_response->length != 0 && nt_response->length < 24) {
+ DEBUG(2,("ntlm_password_check: invalid NT password length (%lu) for user %s\n",
+ (unsigned long)nt_response->length, username));
+ }
+
+ if (nt_response->length >= 24 && nt_pw) {
+ if (nt_response->length > 24) {
+ /* We have the NT MD4 hash challenge available - see if we can
+ use it (ie. does it exist in the smbpasswd file).
+ */
+ DEBUG(4,("ntlm_password_check: Checking NTLMv2 password with domain [%s]\n", client_domain));
+ if (smb_pwd_check_ntlmv2( nt_response,
+ nt_pw, challenge,
+ client_username,
+ client_domain,
+ user_sess_key)) {
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(4,("ntlm_password_check: Checking NTLMv2 password without a domain\n"));
+ if (smb_pwd_check_ntlmv2( nt_response,
+ nt_pw, challenge,
+ client_username,
+ "",
+ user_sess_key)) {
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("ntlm_password_check: NTLMv2 password check failed\n"));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ }
+
+ if (lp_ntlm_auth()) {
+ /* We have the NT MD4 hash challenge available - see if we can
+ use it (ie. does it exist in the smbpasswd file).
+ */
+ DEBUG(4,("ntlm_password_check: Checking NT MD4 password\n"));
+ if (smb_pwd_check_ntlmv1(nt_response,
+ nt_pw, challenge,
+ user_sess_key)) {
+ /* The LM session key for this response is not very secure,
+ so use it only if we otherwise allow LM authentication */
+
+ if (lp_lanman_auth() && lm_pw) {
+ uint8 first_8_lm_hash[16];
+ memcpy(first_8_lm_hash, lm_pw, 8);
+ memset(first_8_lm_hash + 8, '\0', 8);
+ *lm_sess_key = data_blob(first_8_lm_hash, 16);
+ }
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("ntlm_password_check: NT MD4 password check failed for user %s\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ } else {
+ DEBUG(2,("ntlm_password_check: NTLMv1 passwords NOT PERMITTED for user %s\n",
+ username));
+ /* no return, becouse we might pick up LMv2 in the LM field */
+ }
+ }
+
+ if (lm_response->length == 0) {
+ DEBUG(3,("ntlm_password_check: NEITHER LanMan nor NT password supplied for user %s\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ if (lm_response->length < 24) {
+ DEBUG(2,("ntlm_password_check: invalid LanMan password length (%lu) for user %s\n",
+ (unsigned long)nt_response->length, username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ if (!lp_lanman_auth()) {
+ DEBUG(3,("ntlm_password_check: Lanman passwords NOT PERMITTED for user %s\n",
+ username));
+ } else if (!lm_pw) {
+ DEBUG(3,("ntlm_password_check: NO LanMan password set for user %s (and no NT password supplied)\n",
+ username));
+ } else {
+ DEBUG(4,("ntlm_password_check: Checking LM password\n"));
+ if (smb_pwd_check_ntlmv1(lm_response,
+ lm_pw, challenge,
+ NULL)) {
+ uint8 first_8_lm_hash[16];
+ memcpy(first_8_lm_hash, lm_pw, 8);
+ memset(first_8_lm_hash + 8, '\0', 8);
+ *user_sess_key = data_blob(first_8_lm_hash, 16);
+ *lm_sess_key = data_blob(first_8_lm_hash, 16);
+ return NT_STATUS_OK;
+ }
+ }
+
+ if (!nt_pw) {
+ DEBUG(4,("ntlm_password_check: LM password check failed for user, no NT password %s\n",username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* This is for 'LMv2' authentication. almost NTLMv2 but limited to 24 bytes.
+ - related to Win9X, legacy NAS pass-though authentication
+ */
+ DEBUG(4,("ntlm_password_check: Checking LMv2 password with domain %s\n", client_domain));
+ if (smb_pwd_check_ntlmv2( lm_response,
+ nt_pw, challenge,
+ client_username,
+ client_domain,
+ NULL)) {
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(4,("ntlm_password_check: Checking LMv2 password without a domain\n"));
+ if (smb_pwd_check_ntlmv2( lm_response,
+ nt_pw, challenge,
+ client_username,
+ "",
+ NULL)) {
+ return NT_STATUS_OK;
+ }
+
+ /* Apparently NT accepts NT responses in the LM field
+ - I think this is related to Win9X pass-though authentication
+ */
+ DEBUG(4,("ntlm_password_check: Checking NT MD4 password in LM field\n"));
+ if (lp_ntlm_auth()) {
+ if (smb_pwd_check_ntlmv1(lm_response,
+ nt_pw, challenge,
+ NULL)) {
+ /* The session key for this response is still very odd.
+ It not very secure, so use it only if we otherwise
+ allow LM authentication */
+
+ if (lp_lanman_auth() && lm_pw) {
+ uint8 first_8_lm_hash[16];
+ memcpy(first_8_lm_hash, lm_pw, 8);
+ memset(first_8_lm_hash + 8, '\0', 8);
+ *user_sess_key = data_blob(first_8_lm_hash, 16);
+ *lm_sess_key = data_blob(first_8_lm_hash, 16);
+ }
+ return NT_STATUS_OK;
+ }
+ DEBUG(3,("ntlm_password_check: LM password, NT MD4 password in LM field and LMv2 failed for user %s\n",username));
+ } else {
+ DEBUG(3,("ntlm_password_check: LM password and LMv2 failed for user %s, and NT MD4 password in LM field not permitted\n",username));
+ }
+ return NT_STATUS_WRONG_PASSWORD;
+}
+
diff --git a/source/libsmb/ntlmssp.c b/source/libsmb/ntlmssp.c
new file mode 100644
index 00000000000..60523ddf9d0
--- /dev/null
+++ b/source/libsmb/ntlmssp.c
@@ -0,0 +1,1112 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ handle NLTMSSP, server side
+
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Andrew Bartlett 2001-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static NTSTATUS ntlmssp_client_initial(struct ntlmssp_state *ntlmssp_state,
+ DATA_BLOB reply, DATA_BLOB *next_request);
+static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state,
+ const DATA_BLOB in, DATA_BLOB *out);
+static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_state *ntlmssp_state,
+ const DATA_BLOB reply, DATA_BLOB *next_request);
+static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state,
+ const DATA_BLOB request, DATA_BLOB *reply);
+
+/**
+ * Callbacks for NTLMSSP - for both client and server operating modes
+ *
+ */
+
+static const struct ntlmssp_callbacks {
+ enum NTLMSSP_ROLE role;
+ enum NTLM_MESSAGE_TYPE ntlmssp_command;
+ NTSTATUS (*fn)(struct ntlmssp_state *ntlmssp_state,
+ DATA_BLOB in, DATA_BLOB *out);
+} ntlmssp_callbacks[] = {
+ {NTLMSSP_CLIENT, NTLMSSP_INITIAL, ntlmssp_client_initial},
+ {NTLMSSP_SERVER, NTLMSSP_NEGOTIATE, ntlmssp_server_negotiate},
+ {NTLMSSP_CLIENT, NTLMSSP_CHALLENGE, ntlmssp_client_challenge},
+ {NTLMSSP_SERVER, NTLMSSP_AUTH, ntlmssp_server_auth},
+ {NTLMSSP_CLIENT, NTLMSSP_UNKNOWN, NULL},
+ {NTLMSSP_SERVER, NTLMSSP_UNKNOWN, NULL}
+};
+
+
+/**
+ * Print out the NTLMSSP flags for debugging
+ * @param neg_flags The flags from the packet
+ */
+
+void debug_ntlmssp_flags(uint32 neg_flags)
+{
+ DEBUG(3,("Got NTLMSSP neg_flags=0x%08x\n", neg_flags));
+
+ if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_UNICODE\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_OEM)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_OEM\n"));
+ if (neg_flags & NTLMSSP_REQUEST_TARGET)
+ DEBUGADD(4, (" NTLMSSP_REQUEST_TARGET\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_SIGN)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_SIGN\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_SEAL)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_SEAL\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_LM_KEY)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_LM_KEY\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_NETWARE)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NETWARE\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_NTLM)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NTLM\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_ALWAYS_SIGN\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_NTLM2)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NTLM2\n"));
+ if (neg_flags & NTLMSSP_CHAL_TARGET_INFO)
+ DEBUGADD(4, (" NTLMSSP_CHAL_TARGET_INFO\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_128)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_128\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_KEY_EXCH\n"));
+}
+
+/**
+ * Default challenge generation code.
+ *
+ */
+
+static const uint8 *get_challenge(const struct ntlmssp_state *ntlmssp_state)
+{
+ static uchar chal[8];
+ generate_random_buffer(chal, sizeof(chal), False);
+
+ return chal;
+}
+
+/**
+ * Default 'we can set the challenge to anything we like' implementation
+ *
+ */
+
+static BOOL may_set_challenge(const struct ntlmssp_state *ntlmssp_state)
+{
+ return True;
+}
+
+/**
+ * Default 'we can set the challenge to anything we like' implementation
+ *
+ * Does not actually do anything, as the value is always in the structure anyway.
+ *
+ */
+
+static NTSTATUS set_challenge(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *challenge)
+{
+ SMB_ASSERT(challenge->length == 8);
+ return NT_STATUS_OK;
+}
+
+/**
+ * Set a username on an NTLMSSP context - ensures it is talloc()ed
+ *
+ */
+
+NTSTATUS ntlmssp_set_username(NTLMSSP_STATE *ntlmssp_state, const char *user)
+{
+ ntlmssp_state->user = talloc_strdup(ntlmssp_state->mem_ctx, user);
+ if (!ntlmssp_state->user) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}
+
+/**
+ * Set a password on an NTLMSSP context - ensures it is talloc()ed
+ *
+ */
+NTSTATUS ntlmssp_set_password(NTLMSSP_STATE *ntlmssp_state, const char *password)
+{
+ if (!password) {
+ ntlmssp_state->password = NULL;
+ } else {
+ ntlmssp_state->password = talloc_strdup(ntlmssp_state->mem_ctx, password);
+ if (!ntlmssp_state->password) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+/**
+ * Set a domain on an NTLMSSP context - ensures it is talloc()ed
+ *
+ */
+NTSTATUS ntlmssp_set_domain(NTLMSSP_STATE *ntlmssp_state, const char *domain)
+{
+ ntlmssp_state->domain = talloc_strdup(ntlmssp_state->mem_ctx, domain);
+ if (!ntlmssp_state->domain) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}
+
+/**
+ * Set a workstation on an NTLMSSP context - ensures it is talloc()ed
+ *
+ */
+NTSTATUS ntlmssp_set_workstation(NTLMSSP_STATE *ntlmssp_state, const char *workstation)
+{
+ ntlmssp_state->workstation = talloc_strdup(ntlmssp_state->mem_ctx, workstation);
+ if (!ntlmssp_state->domain) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}
+
+/**
+ * Store a DATA_BLOB containing an NTLMSSP response, for use later.
+ * This copies the data blob
+ */
+
+NTSTATUS ntlmssp_store_response(NTLMSSP_STATE *ntlmssp_state,
+ DATA_BLOB response)
+{
+ ntlmssp_state->stored_response = data_blob_talloc(ntlmssp_state->mem_ctx,
+ response.data, response.length);
+ return NT_STATUS_OK;
+}
+
+/**
+ * Next state function for the NTLMSSP state machine
+ *
+ * @param ntlmssp_state NTLMSSP State
+ * @param in The packet in from the NTLMSSP partner, as a DATA_BLOB
+ * @param out The reply, as an allocated DATA_BLOB, caller to free.
+ * @return Errors, NT_STATUS_MORE_PROCESSING_REQUIRED or NT_STATUS_OK.
+ */
+
+NTSTATUS ntlmssp_update(NTLMSSP_STATE *ntlmssp_state,
+ const DATA_BLOB in, DATA_BLOB *out)
+{
+ DATA_BLOB input;
+ uint32 ntlmssp_command;
+ int i;
+
+ *out = data_blob(NULL, 0);
+
+ if (!in.length && ntlmssp_state->stored_response.length) {
+ input = ntlmssp_state->stored_response;
+
+ /* we only want to read the stored response once - overwrite it */
+ ntlmssp_state->stored_response = data_blob(NULL, 0);
+ } else {
+ input = in;
+ }
+
+ if (!input.length) {
+ switch (ntlmssp_state->role) {
+ case NTLMSSP_CLIENT:
+ ntlmssp_command = NTLMSSP_INITIAL;
+ break;
+ case NTLMSSP_SERVER:
+ /* 'datagram' mode - no neg packet */
+ ntlmssp_command = NTLMSSP_NEGOTIATE;
+ break;
+ }
+ } else {
+ if (!msrpc_parse(&input, "Cd",
+ "NTLMSSP",
+ &ntlmssp_command)) {
+ DEBUG(1, ("Failed to parse NTLMSSP packet, could not extract NTLMSSP command\n"));
+ dump_data(2, (const char *)input.data, input.length);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ if (ntlmssp_command != ntlmssp_state->expected_state) {
+ DEBUG(1, ("got NTLMSSP command %u, expected %u\n", ntlmssp_command, ntlmssp_state->expected_state));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ for (i=0; ntlmssp_callbacks[i].fn; i++) {
+ if (ntlmssp_callbacks[i].role == ntlmssp_state->role
+ && ntlmssp_callbacks[i].ntlmssp_command == ntlmssp_command) {
+ return ntlmssp_callbacks[i].fn(ntlmssp_state, input, out);
+ }
+ }
+
+ DEBUG(1, ("failed to find NTLMSSP callback for NTLMSSP mode %u, command %u\n",
+ ntlmssp_state->role, ntlmssp_command));
+
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+/**
+ * End an NTLMSSP state machine
+ *
+ * @param ntlmssp_state NTLMSSP State, free()ed by this function
+ */
+
+void ntlmssp_end(NTLMSSP_STATE **ntlmssp_state)
+{
+ TALLOC_CTX *mem_ctx = (*ntlmssp_state)->mem_ctx;
+
+ (*ntlmssp_state)->ref_count--;
+
+ if ((*ntlmssp_state)->ref_count == 0) {
+ data_blob_free(&(*ntlmssp_state)->chal);
+ data_blob_free(&(*ntlmssp_state)->lm_resp);
+ data_blob_free(&(*ntlmssp_state)->nt_resp);
+
+ talloc_destroy(mem_ctx);
+ }
+
+ *ntlmssp_state = NULL;
+ return;
+}
+
+/**
+ * Determine correct target name flags for reply, given server role
+ * and negotiated flags
+ *
+ * @param ntlmssp_state NTLMSSP State
+ * @param neg_flags The flags from the packet
+ * @param chal_flags The flags to be set in the reply packet
+ * @return The 'target name' string.
+ */
+
+static const char *ntlmssp_target_name(struct ntlmssp_state *ntlmssp_state,
+ uint32 neg_flags, uint32 *chal_flags)
+{
+ if (neg_flags & NTLMSSP_REQUEST_TARGET) {
+ *chal_flags |= NTLMSSP_CHAL_TARGET_INFO;
+ *chal_flags |= NTLMSSP_REQUEST_TARGET;
+ if (ntlmssp_state->server_role == ROLE_STANDALONE) {
+ *chal_flags |= NTLMSSP_TARGET_TYPE_SERVER;
+ return ntlmssp_state->get_global_myname();
+ } else {
+ *chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN;
+ return ntlmssp_state->get_domain();
+ };
+ } else {
+ return "";
+ }
+}
+
+static void ntlmssp_handle_neg_flags(struct ntlmssp_state *ntlmssp_state,
+ uint32 neg_flags, BOOL allow_lm) {
+ if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) {
+ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE;
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_OEM;
+ ntlmssp_state->unicode = True;
+ } else {
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_UNICODE;
+ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM;
+ ntlmssp_state->unicode = False;
+ }
+
+ if ((neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) && allow_lm) {
+ /* other end forcing us to use LM */
+ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_LM_KEY;
+ ntlmssp_state->use_ntlmv2 = False;
+ } else {
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
+ }
+
+ if (neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) {
+ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
+ }
+
+ if (!(neg_flags & NTLMSSP_NEGOTIATE_NTLM2)) {
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
+ }
+
+ if (!(neg_flags & NTLMSSP_NEGOTIATE_128)) {
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_128;
+ }
+
+ if (!(neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH)) {
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_KEY_EXCH;
+ }
+
+ if ((neg_flags & NTLMSSP_REQUEST_TARGET)) {
+ ntlmssp_state->neg_flags |= NTLMSSP_REQUEST_TARGET;
+ }
+
+}
+
+
+/**
+ * Next state function for the Negotiate packet
+ *
+ * @param ntlmssp_state NTLMSSP State
+ * @param request The request, as a DATA_BLOB
+ * @param request The reply, as an allocated DATA_BLOB, caller to free.
+ * @return Errors or MORE_PROCESSING_REQUIRED if a reply is sent.
+ */
+
+static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state,
+ const DATA_BLOB request, DATA_BLOB *reply)
+{
+ DATA_BLOB struct_blob;
+ fstring dnsname, dnsdomname;
+ uint32 neg_flags = 0;
+ uint32 ntlmssp_command, chal_flags;
+ char *cliname=NULL, *domname=NULL;
+ const uint8 *cryptkey;
+ const char *target_name;
+
+ /* parse the NTLMSSP packet */
+#if 0
+ file_save("ntlmssp_negotiate.dat", request.data, request.length);
+#endif
+
+ if (request.length) {
+ if (!msrpc_parse(&request, "CddAA",
+ "NTLMSSP",
+ &ntlmssp_command,
+ &neg_flags,
+ &cliname,
+ &domname)) {
+ DEBUG(1, ("ntlmssp_server_negotiate: failed to parse NTLMSSP:\n"));
+ dump_data(2, (const char *)request.data, request.length);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ SAFE_FREE(cliname);
+ SAFE_FREE(domname);
+
+ debug_ntlmssp_flags(neg_flags);
+ }
+
+ ntlmssp_handle_neg_flags(ntlmssp_state, neg_flags, lp_lanman_auth());
+
+ /* Ask our caller what challenge they would like in the packet */
+ cryptkey = ntlmssp_state->get_challenge(ntlmssp_state);
+
+ /* Check if we may set the challenge */
+ if (!ntlmssp_state->may_set_challenge(ntlmssp_state)) {
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
+ }
+
+ /* The flags we send back are not just the negotiated flags,
+ * they are also 'what is in this packet'. Therfore, we
+ * operate on 'chal_flags' from here on
+ */
+
+ chal_flags = ntlmssp_state->neg_flags;
+
+ /* get the right name to fill in as 'target' */
+ target_name = ntlmssp_target_name(ntlmssp_state,
+ neg_flags, &chal_flags);
+ if (target_name == NULL)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ ntlmssp_state->chal = data_blob_talloc(ntlmssp_state->mem_ctx, cryptkey, 8);
+ ntlmssp_state->internal_chal = data_blob_talloc(ntlmssp_state->mem_ctx, cryptkey, 8);
+
+
+ /* This should be a 'netbios domain -> DNS domain' mapping */
+ dnsdomname[0] = '\0';
+ get_mydnsdomname(dnsdomname);
+ strlower_m(dnsdomname);
+
+ dnsname[0] = '\0';
+ get_mydnsfullname(dnsname);
+
+ /* This creates the 'blob' of names that appears at the end of the packet */
+ if (chal_flags & NTLMSSP_CHAL_TARGET_INFO)
+ {
+ const char *target_name_dns = "";
+ if (chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN) {
+ target_name_dns = dnsdomname;
+ } else if (chal_flags |= NTLMSSP_TARGET_TYPE_SERVER) {
+ target_name_dns = dnsname;
+ }
+
+ msrpc_gen(&struct_blob, "aaaaa",
+ NTLMSSP_NAME_TYPE_DOMAIN, target_name,
+ NTLMSSP_NAME_TYPE_SERVER, ntlmssp_state->get_global_myname(),
+ NTLMSSP_NAME_TYPE_DOMAIN_DNS, dnsdomname,
+ NTLMSSP_NAME_TYPE_SERVER_DNS, dnsname,
+ 0, "");
+ } else {
+ struct_blob = data_blob(NULL, 0);
+ }
+
+ {
+ /* Marshel the packet in the right format, be it unicode or ASCII */
+ const char *gen_string;
+ if (ntlmssp_state->unicode) {
+ gen_string = "CdUdbddB";
+ } else {
+ gen_string = "CdAdbddB";
+ }
+
+ msrpc_gen(reply, gen_string,
+ "NTLMSSP",
+ NTLMSSP_CHALLENGE,
+ target_name,
+ chal_flags,
+ cryptkey, 8,
+ 0, 0,
+ struct_blob.data, struct_blob.length);
+ }
+
+ data_blob_free(&struct_blob);
+
+ ntlmssp_state->expected_state = NTLMSSP_AUTH;
+
+ return NT_STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+/**
+ * Next state function for the Authenticate packet
+ *
+ * @param ntlmssp_state NTLMSSP State
+ * @param request The request, as a DATA_BLOB
+ * @param request The reply, as an allocated DATA_BLOB, caller to free.
+ * @return Errors or NT_STATUS_OK.
+ */
+
+static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state,
+ const DATA_BLOB request, DATA_BLOB *reply)
+{
+ DATA_BLOB encrypted_session_key = data_blob(NULL, 0);
+ DATA_BLOB nt_session_key = data_blob(NULL, 0);
+ DATA_BLOB lm_session_key = data_blob(NULL, 0);
+ DATA_BLOB session_key = data_blob(NULL, 0);
+ uint32 ntlmssp_command, auth_flags;
+ NTSTATUS nt_status;
+
+ /* used by NTLM2 */
+ BOOL doing_ntlm2 = False;
+
+ uchar session_nonce[16];
+ uchar session_nonce_hash[16];
+
+ const char *parse_string;
+ char *domain = NULL;
+ char *user = NULL;
+ char *workstation = NULL;
+
+ /* parse the NTLMSSP packet */
+ *reply = data_blob(NULL, 0);
+
+#if 0
+ file_save("ntlmssp_auth.dat", request.data, request.length);
+#endif
+
+ if (ntlmssp_state->unicode) {
+ parse_string = "CdBBUUUBd";
+ } else {
+ parse_string = "CdBBAAABd";
+ }
+
+ data_blob_free(&ntlmssp_state->lm_resp);
+ data_blob_free(&ntlmssp_state->nt_resp);
+
+ ntlmssp_state->user = NULL;
+ ntlmssp_state->domain = NULL;
+ ntlmssp_state->workstation = NULL;
+
+ /* now the NTLMSSP encoded auth hashes */
+ if (!msrpc_parse(&request, parse_string,
+ "NTLMSSP",
+ &ntlmssp_command,
+ &ntlmssp_state->lm_resp,
+ &ntlmssp_state->nt_resp,
+ &domain,
+ &user,
+ &workstation,
+ &encrypted_session_key,
+ &auth_flags)) {
+ DEBUG(1, ("ntlmssp_server_auth: failed to parse NTLMSSP:\n"));
+ dump_data(2, (const char *)request.data, request.length);
+ SAFE_FREE(domain);
+ SAFE_FREE(user);
+ SAFE_FREE(workstation);
+ data_blob_free(&encrypted_session_key);
+ auth_flags = 0;
+
+ /* Try again with a shorter string (Win9X truncates this packet) */
+ if (ntlmssp_state->unicode) {
+ parse_string = "CdBBUUU";
+ } else {
+ parse_string = "CdBBAAA";
+ }
+
+ /* now the NTLMSSP encoded auth hashes */
+ if (!msrpc_parse(&request, parse_string,
+ "NTLMSSP",
+ &ntlmssp_command,
+ &ntlmssp_state->lm_resp,
+ &ntlmssp_state->nt_resp,
+ &domain,
+ &user,
+ &workstation)) {
+ DEBUG(1, ("ntlmssp_server_auth: failed to parse NTLMSSP:\n"));
+ dump_data(2, (const char *)request.data, request.length);
+ SAFE_FREE(domain);
+ SAFE_FREE(user);
+ SAFE_FREE(workstation);
+
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ if (auth_flags)
+ ntlmssp_handle_neg_flags(ntlmssp_state, auth_flags, lp_lanman_auth());
+
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, domain))) {
+ SAFE_FREE(domain);
+ SAFE_FREE(user);
+ SAFE_FREE(workstation);
+ data_blob_free(&encrypted_session_key);
+ return nt_status;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, user))) {
+ SAFE_FREE(domain);
+ SAFE_FREE(user);
+ SAFE_FREE(workstation);
+ data_blob_free(&encrypted_session_key);
+ return nt_status;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_workstation(ntlmssp_state, workstation))) {
+ SAFE_FREE(domain);
+ SAFE_FREE(user);
+ SAFE_FREE(workstation);
+ data_blob_free(&encrypted_session_key);
+ return nt_status;
+ }
+
+ SAFE_FREE(domain);
+ SAFE_FREE(user);
+ SAFE_FREE(workstation);
+
+ DEBUG(3,("Got user=[%s] domain=[%s] workstation=[%s] len1=%lu len2=%lu\n",
+ ntlmssp_state->user, ntlmssp_state->domain, ntlmssp_state->workstation, (unsigned long)ntlmssp_state->lm_resp.length, (unsigned long)ntlmssp_state->nt_resp.length));
+
+#if 0
+ file_save("nthash1.dat", &ntlmssp_state->nt_resp.data, &ntlmssp_state->nt_resp.length);
+ file_save("lmhash1.dat", &ntlmssp_state->lm_resp.data, &ntlmssp_state->lm_resp.length);
+#endif
+
+ /* NTLM2 uses a 'challenge' that is made of up both the server challenge, and a
+ client challenge
+
+ However, the NTLM2 flag may still be set for the real NTLMv2 logins, be careful.
+ */
+ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+ if (ntlmssp_state->nt_resp.length == 24 && ntlmssp_state->lm_resp.length == 24) {
+ struct MD5Context md5_session_nonce_ctx;
+ SMB_ASSERT(ntlmssp_state->internal_chal.data && ntlmssp_state->internal_chal.length == 8);
+
+ doing_ntlm2 = True;
+
+ memcpy(session_nonce, ntlmssp_state->internal_chal.data, 8);
+ memcpy(&session_nonce[8], ntlmssp_state->lm_resp.data, 8);
+
+ MD5Init(&md5_session_nonce_ctx);
+ MD5Update(&md5_session_nonce_ctx, session_nonce, 16);
+ MD5Final(session_nonce_hash, &md5_session_nonce_ctx);
+
+ ntlmssp_state->chal = data_blob_talloc(ntlmssp_state->mem_ctx, session_nonce_hash, 8);
+
+ /* LM response is no longer useful */
+ data_blob_free(&ntlmssp_state->lm_resp);
+
+ /* We changed the effective challenge - set it */
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_state->set_challenge(ntlmssp_state, &ntlmssp_state->chal))) {
+ data_blob_free(&encrypted_session_key);
+ return nt_status;
+ }
+ }
+ }
+
+ /*
+ * Note we don't check here for NTLMv2 auth settings. If NTLMv2 auth
+ * is required (by "ntlm auth = no" and "lm auth = no" being set in the
+ * smb.conf file) and no NTLMv2 response was sent then the password check
+ * will fail here. JRA.
+ */
+
+ /* Finally, actually ask if the password is OK */
+
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_state->check_password(ntlmssp_state,
+ &nt_session_key, &lm_session_key))) {
+ data_blob_free(&encrypted_session_key);
+ return nt_status;
+ }
+
+ dump_data_pw("NT session key:\n", nt_session_key.data, nt_session_key.length);
+ dump_data_pw("LM first-8:\n", lm_session_key.data, lm_session_key.length);
+
+ /* Handle the different session key derivation for NTLM2 */
+ if (doing_ntlm2) {
+ if (nt_session_key.data && nt_session_key.length == 16) {
+ session_key = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 16);
+ hmac_md5(nt_session_key.data, session_nonce,
+ sizeof(session_nonce), session_key.data);
+ DEBUG(10,("ntlmssp_server_auth: Created NTLM2 session key.\n"));
+ dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length);
+
+ } else {
+ DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM2 session key.\n"));
+ session_key = data_blob(NULL, 0);
+ }
+ } else if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) {
+ if (lm_session_key.data && lm_session_key.length >= 8) {
+ if (ntlmssp_state->lm_resp.data && ntlmssp_state->lm_resp.length == 24) {
+ session_key = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 16);
+ SMBsesskeygen_lm_sess_key(lm_session_key.data, ntlmssp_state->lm_resp.data,
+ session_key.data);
+ DEBUG(10,("ntlmssp_server_auth: Created NTLM session key.\n"));
+ dump_data_pw("LM session key:\n", session_key.data, session_key.length);
+ } else {
+ /* use the key unmodified - it's
+ * probably a NULL key from the guest
+ * login */
+ session_key = lm_session_key;
+ }
+ } else {
+ DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM session key.\n"));
+ session_key = data_blob(NULL, 0);
+ }
+ } else if (nt_session_key.data) {
+ session_key = nt_session_key;
+ DEBUG(10,("ntlmssp_server_auth: Using unmodified nt session key.\n"));
+ dump_data_pw("unmodified session key:\n", session_key.data, session_key.length);
+ } else if (lm_session_key.data) {
+ session_key = lm_session_key;
+ DEBUG(10,("ntlmssp_server_auth: Using unmodified lm session key.\n"));
+ dump_data_pw("unmodified session key:\n", session_key.data, session_key.length);
+ } else {
+ DEBUG(10,("ntlmssp_server_auth: Failed to create unmodified session key.\n"));
+ session_key = data_blob(NULL, 0);
+ }
+
+ /* With KEY_EXCH, the client supplies the proposed session key,
+ but encrypts it with the long-term key */
+ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
+ if (!encrypted_session_key.data || encrypted_session_key.length != 16) {
+ data_blob_free(&encrypted_session_key);
+ DEBUG(1, ("Client-supplied KEY_EXCH session key was of invalid length (%u)!\n",
+ encrypted_session_key.length));
+ return NT_STATUS_INVALID_PARAMETER;
+ } else if (!session_key.data || session_key.length != 16) {
+ DEBUG(5, ("server session key is invalid (len == %u), cannot do KEY_EXCH!\n",
+ session_key.length));
+ ntlmssp_state->session_key = session_key;
+ } else {
+ dump_data_pw("KEY_EXCH session key (enc):\n", encrypted_session_key.data, encrypted_session_key.length);
+ SamOEMhash(encrypted_session_key.data,
+ session_key.data,
+ encrypted_session_key.length);
+ ntlmssp_state->session_key = data_blob_talloc(ntlmssp_state->mem_ctx,
+ encrypted_session_key.data,
+ encrypted_session_key.length);
+ dump_data_pw("KEY_EXCH session key:\n", encrypted_session_key.data,
+ encrypted_session_key.length);
+ }
+ } else {
+ ntlmssp_state->session_key = session_key;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ ntlmssp_state->session_key = data_blob(NULL, 0);
+ } else if (ntlmssp_state->session_key.length) {
+ nt_status = ntlmssp_sign_init(ntlmssp_state);
+ }
+
+ data_blob_free(&encrypted_session_key);
+
+ /* allow arbitarily many authentications */
+ ntlmssp_state->expected_state = NTLMSSP_AUTH;
+
+ return nt_status;
+}
+
+/**
+ * Create an NTLMSSP state machine
+ *
+ * @param ntlmssp_state NTLMSSP State, allocated by this function
+ */
+
+NTSTATUS ntlmssp_server_start(NTLMSSP_STATE **ntlmssp_state)
+{
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_init("NTLMSSP context");
+
+ *ntlmssp_state = talloc_zero(mem_ctx, sizeof(**ntlmssp_state));
+ if (!*ntlmssp_state) {
+ DEBUG(0,("ntlmssp_server_start: talloc failed!\n"));
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*ntlmssp_state)->role = NTLMSSP_SERVER;
+
+ (*ntlmssp_state)->mem_ctx = mem_ctx;
+ (*ntlmssp_state)->get_challenge = get_challenge;
+ (*ntlmssp_state)->set_challenge = set_challenge;
+ (*ntlmssp_state)->may_set_challenge = may_set_challenge;
+
+ (*ntlmssp_state)->get_global_myname = global_myname;
+ (*ntlmssp_state)->get_domain = lp_workgroup;
+ (*ntlmssp_state)->server_role = ROLE_DOMAIN_MEMBER; /* a good default */
+
+ (*ntlmssp_state)->expected_state = NTLMSSP_NEGOTIATE;
+
+ (*ntlmssp_state)->ref_count = 1;
+
+ (*ntlmssp_state)->neg_flags =
+ NTLMSSP_NEGOTIATE_128 |
+ NTLMSSP_NEGOTIATE_NTLM |
+ NTLMSSP_NEGOTIATE_NTLM2 |
+ NTLMSSP_NEGOTIATE_KEY_EXCH |
+ NTLMSSP_NEGOTIATE_SIGN;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Client side NTLMSSP
+*********************************************************************/
+
+/**
+ * Next state function for the Initial packet
+ *
+ * @param ntlmssp_state NTLMSSP State
+ * @param request The request, as a DATA_BLOB. reply.data must be NULL
+ * @param request The reply, as an allocated DATA_BLOB, caller to free.
+ * @return Errors or NT_STATUS_OK.
+ */
+
+static NTSTATUS ntlmssp_client_initial(struct ntlmssp_state *ntlmssp_state,
+ DATA_BLOB reply, DATA_BLOB *next_request)
+{
+ if (ntlmssp_state->unicode) {
+ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE;
+ } else {
+ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM;
+ }
+
+ if (ntlmssp_state->use_ntlmv2) {
+ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2;
+ }
+
+ /* generate the ntlmssp negotiate packet */
+ msrpc_gen(next_request, "CddAA",
+ "NTLMSSP",
+ NTLMSSP_NEGOTIATE,
+ ntlmssp_state->neg_flags,
+ ntlmssp_state->get_domain(),
+ ntlmssp_state->get_global_myname());
+
+ ntlmssp_state->expected_state = NTLMSSP_CHALLENGE;
+
+ return NT_STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+/**
+ * Next state function for the Challenge Packet. Generate an auth packet.
+ *
+ * @param ntlmssp_state NTLMSSP State
+ * @param request The request, as a DATA_BLOB. reply.data must be NULL
+ * @param request The reply, as an allocated DATA_BLOB, caller to free.
+ * @return Errors or NT_STATUS_OK.
+ */
+
+static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_state *ntlmssp_state,
+ const DATA_BLOB reply, DATA_BLOB *next_request)
+{
+ uint32 chal_flags, ntlmssp_command, unkn1, unkn2;
+ DATA_BLOB server_domain_blob;
+ DATA_BLOB challenge_blob;
+ DATA_BLOB struct_blob = data_blob(NULL, 0);
+ char *server_domain;
+ const char *chal_parse_string;
+ const char *auth_gen_string;
+ DATA_BLOB lm_response = data_blob(NULL, 0);
+ DATA_BLOB nt_response = data_blob(NULL, 0);
+ DATA_BLOB session_key = data_blob(NULL, 0);
+ DATA_BLOB encrypted_session_key = data_blob(NULL, 0);
+ NTSTATUS nt_status;
+
+ if (!msrpc_parse(&reply, "CdBd",
+ "NTLMSSP",
+ &ntlmssp_command,
+ &server_domain_blob,
+ &chal_flags)) {
+ DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#1)\n"));
+ dump_data(2, (const char *)reply.data, reply.length);
+
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ data_blob_free(&server_domain_blob);
+
+ DEBUG(3, ("Got challenge flags:\n"));
+ debug_ntlmssp_flags(chal_flags);
+
+ ntlmssp_handle_neg_flags(ntlmssp_state, chal_flags, lp_client_lanman_auth());
+
+ if (ntlmssp_state->unicode) {
+ if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) {
+ chal_parse_string = "CdUdbddB";
+ } else {
+ chal_parse_string = "CdUdbdd";
+ }
+ auth_gen_string = "CdBBUUUBd";
+ } else {
+ if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) {
+ chal_parse_string = "CdAdbddB";
+ } else {
+ chal_parse_string = "CdAdbdd";
+ }
+
+ auth_gen_string = "CdBBAAABd";
+ }
+
+ DEBUG(3, ("NTLMSSP: Set final flags:\n"));
+ debug_ntlmssp_flags(ntlmssp_state->neg_flags);
+
+ if (!msrpc_parse(&reply, chal_parse_string,
+ "NTLMSSP",
+ &ntlmssp_command,
+ &server_domain,
+ &chal_flags,
+ &challenge_blob, 8,
+ &unkn1, &unkn2,
+ &struct_blob)) {
+ DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#2)\n"));
+ dump_data(2, (const char *)reply.data, reply.length);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ntlmssp_state->server_domain = talloc_strdup(ntlmssp_state->mem_ctx,
+ server_domain);
+
+ SAFE_FREE(server_domain);
+ if (challenge_blob.length != 8) {
+ data_blob_free(&struct_blob);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!ntlmssp_state->password) {
+ static const uchar zeros[16];
+ /* do nothing - blobs are zero length */
+
+ /* session key is all zeros */
+ session_key = data_blob_talloc(ntlmssp_state->mem_ctx, zeros, 16);
+
+ /* not doing NLTM2 without a password */
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
+ } else if (ntlmssp_state->use_ntlmv2) {
+
+ if (!struct_blob.length) {
+ /* be lazy, match win2k - we can't do NTLMv2 without it */
+ DEBUG(1, ("Server did not provide 'target information', required for NTLMv2\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* TODO: if the remote server is standalone, then we should replace 'domain'
+ with the server name as supplied above */
+
+ if (!SMBNTLMv2encrypt(ntlmssp_state->user,
+ ntlmssp_state->domain,
+ ntlmssp_state->password, &challenge_blob,
+ &struct_blob,
+ &lm_response, &nt_response, &session_key)) {
+ data_blob_free(&challenge_blob);
+ data_blob_free(&struct_blob);
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+ struct MD5Context md5_session_nonce_ctx;
+ uchar nt_hash[16];
+ uchar session_nonce[16];
+ uchar session_nonce_hash[16];
+ uchar nt_session_key[16];
+ E_md4hash(ntlmssp_state->password, nt_hash);
+
+ lm_response = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 24);
+ generate_random_buffer(lm_response.data, 8, False);
+ memset(lm_response.data+8, 0, 16);
+
+ memcpy(session_nonce, challenge_blob.data, 8);
+ memcpy(&session_nonce[8], lm_response.data, 8);
+
+ MD5Init(&md5_session_nonce_ctx);
+ MD5Update(&md5_session_nonce_ctx, challenge_blob.data, 8);
+ MD5Update(&md5_session_nonce_ctx, lm_response.data, 8);
+ MD5Final(session_nonce_hash, &md5_session_nonce_ctx);
+
+ DEBUG(5, ("NTLMSSP challenge set by NTLM2\n"));
+ DEBUG(5, ("challenge is: \n"));
+ dump_data(5, (const char *)session_nonce_hash, 8);
+
+ nt_response = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 24);
+ SMBNTencrypt(ntlmssp_state->password,
+ session_nonce_hash,
+ nt_response.data);
+
+ session_key = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 16);
+
+ SMBsesskeygen_ntv1(nt_hash, NULL, nt_session_key);
+ hmac_md5(nt_session_key, session_nonce, sizeof(session_nonce), session_key.data);
+ dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length);
+ } else {
+
+
+ uchar lm_hash[16];
+ uchar nt_hash[16];
+ E_deshash(ntlmssp_state->password, lm_hash);
+ E_md4hash(ntlmssp_state->password, nt_hash);
+
+ /* lanman auth is insecure, it may be disabled */
+ if (lp_client_lanman_auth()) {
+ lm_response = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 24);
+ SMBencrypt(ntlmssp_state->password,challenge_blob.data,
+ lm_response.data);
+ }
+
+ nt_response = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 24);
+ SMBNTencrypt(ntlmssp_state->password,challenge_blob.data,
+ nt_response.data);
+
+ session_key = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 16);
+ if ((ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY)
+ && lp_client_lanman_auth()) {
+ SMBsesskeygen_lmv1(lm_hash, lm_response.data,
+ session_key.data);
+ dump_data_pw("LM session key\n", session_key.data, session_key.length);
+ } else {
+ SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
+ dump_data_pw("NT session key:\n", session_key.data, session_key.length);
+ }
+ }
+ data_blob_free(&struct_blob);
+
+ /* Key exchange encryptes a new client-generated session key with
+ the password-derived key */
+ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
+ uint8 client_session_key[16];
+
+ generate_random_buffer(client_session_key, sizeof(client_session_key), False);
+ encrypted_session_key = data_blob(client_session_key, sizeof(client_session_key));
+ dump_data_pw("KEY_EXCH session key:\n", encrypted_session_key.data, encrypted_session_key.length);
+
+ SamOEMhash(encrypted_session_key.data, session_key.data, encrypted_session_key.length);
+ data_blob_free(&session_key);
+ session_key = data_blob_talloc(ntlmssp_state->mem_ctx, client_session_key, sizeof(client_session_key));
+ dump_data_pw("KEY_EXCH session key (enc):\n", encrypted_session_key.data, encrypted_session_key.length);
+ }
+
+ /* this generates the actual auth packet */
+ if (!msrpc_gen(next_request, auth_gen_string,
+ "NTLMSSP",
+ NTLMSSP_AUTH,
+ lm_response.data, lm_response.length,
+ nt_response.data, nt_response.length,
+ ntlmssp_state->domain,
+ ntlmssp_state->user,
+ ntlmssp_state->get_global_myname(),
+ encrypted_session_key.data, encrypted_session_key.length,
+ ntlmssp_state->neg_flags)) {
+
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ data_blob_free(&encrypted_session_key);
+
+ data_blob_free(&ntlmssp_state->chal);
+
+ ntlmssp_state->chal = challenge_blob;
+ ntlmssp_state->lm_resp = lm_response;
+ ntlmssp_state->nt_resp = nt_response;
+ ntlmssp_state->session_key = session_key;
+
+ ntlmssp_state->expected_state = NTLMSSP_UNKNOWN;
+
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_sign_init(ntlmssp_state))) {
+ DEBUG(1, ("Could not setup NTLMSSP signing/sealing system (error was: %s)\n", nt_errstr(nt_status)));
+ return nt_status;
+ }
+
+ return NT_STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+NTSTATUS ntlmssp_client_start(NTLMSSP_STATE **ntlmssp_state)
+{
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_init("NTLMSSP Client context");
+
+ *ntlmssp_state = talloc_zero(mem_ctx, sizeof(**ntlmssp_state));
+ if (!*ntlmssp_state) {
+ DEBUG(0,("ntlmssp_client_start: talloc failed!\n"));
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*ntlmssp_state)->role = NTLMSSP_CLIENT;
+
+ (*ntlmssp_state)->mem_ctx = mem_ctx;
+
+ (*ntlmssp_state)->get_global_myname = global_myname;
+ (*ntlmssp_state)->get_domain = lp_workgroup;
+
+ (*ntlmssp_state)->unicode = True;
+
+ (*ntlmssp_state)->use_ntlmv2 = lp_client_ntlmv2_auth();
+
+ (*ntlmssp_state)->expected_state = NTLMSSP_INITIAL;
+
+ (*ntlmssp_state)->ref_count = 1;
+
+ (*ntlmssp_state)->neg_flags =
+ NTLMSSP_NEGOTIATE_128 |
+ NTLMSSP_NEGOTIATE_NTLM |
+ NTLMSSP_NEGOTIATE_NTLM2 |
+ NTLMSSP_NEGOTIATE_KEY_EXCH |
+ /*
+ * We need to set this to allow a later SetPassword
+ * via the SAMR pipe to succeed. Strange.... We could
+ * also add NTLMSSP_NEGOTIATE_SEAL here. JRA.
+ * */
+ NTLMSSP_NEGOTIATE_SIGN |
+ NTLMSSP_REQUEST_TARGET;
+
+ return NT_STATUS_OK;
+}
+
diff --git a/source/libsmb/ntlmssp_parse.c b/source/libsmb/ntlmssp_parse.c
new file mode 100644
index 00000000000..4b3043aec80
--- /dev/null
+++ b/source/libsmb/ntlmssp_parse.c
@@ -0,0 +1,315 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple kerberos5/SPNEGO routines
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
+ Copyright (C) Andrew Bartlett 2002-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*
+ this is a tiny msrpc packet generator. I am only using this to
+ avoid tying this code to a particular varient of our rpc code. This
+ generator is not general enough for all our rpc needs, its just
+ enough for the spnego/ntlmssp code
+
+ format specifiers are:
+
+ U = unicode string (input is unix string)
+ a = address (input is char *unix_string)
+ (1 byte type, 1 byte length, unicode/ASCII string, all inline)
+ A = ASCII string (input is unix string)
+ B = data blob (pointer + length)
+ b = data blob in header (pointer + length)
+ D
+ d = word (4 bytes)
+ C = constant ascii string
+ */
+BOOL msrpc_gen(DATA_BLOB *blob,
+ const char *format, ...)
+{
+ int i, n;
+ va_list ap;
+ char *s;
+ uint8 *b;
+ int head_size=0, data_size=0;
+ int head_ofs, data_ofs;
+
+ /* first scan the format to work out the header and body size */
+ va_start(ap, format);
+ for (i=0; format[i]; i++) {
+ switch (format[i]) {
+ case 'U':
+ s = va_arg(ap, char *);
+ head_size += 8;
+ data_size += str_charnum(s) * 2;
+ break;
+ case 'A':
+ s = va_arg(ap, char *);
+ head_size += 8;
+ data_size += str_ascii_charnum(s);
+ break;
+ case 'a':
+ n = va_arg(ap, int);
+ s = va_arg(ap, char *);
+ data_size += (str_charnum(s) * 2) + 4;
+ break;
+ case 'B':
+ b = va_arg(ap, uint8 *);
+ head_size += 8;
+ data_size += va_arg(ap, int);
+ break;
+ case 'b':
+ b = va_arg(ap, uint8 *);
+ head_size += va_arg(ap, int);
+ break;
+ case 'd':
+ n = va_arg(ap, int);
+ head_size += 4;
+ break;
+ case 'C':
+ s = va_arg(ap, char *);
+ head_size += str_charnum(s) + 1;
+ break;
+ }
+ }
+ va_end(ap);
+
+ /* allocate the space, then scan the format again to fill in the values */
+ *blob = data_blob(NULL, head_size + data_size);
+
+ head_ofs = 0;
+ data_ofs = head_size;
+
+ va_start(ap, format);
+ for (i=0; format[i]; i++) {
+ switch (format[i]) {
+ case 'U':
+ s = va_arg(ap, char *);
+ n = str_charnum(s);
+ SSVAL(blob->data, head_ofs, n*2); head_ofs += 2;
+ SSVAL(blob->data, head_ofs, n*2); head_ofs += 2;
+ SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
+ push_string(NULL, blob->data+data_ofs, s, n*2, STR_UNICODE|STR_NOALIGN);
+ data_ofs += n*2;
+ break;
+ case 'A':
+ s = va_arg(ap, char *);
+ n = str_ascii_charnum(s);
+ SSVAL(blob->data, head_ofs, n); head_ofs += 2;
+ SSVAL(blob->data, head_ofs, n); head_ofs += 2;
+ SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
+ push_string(NULL, blob->data+data_ofs, s, n, STR_ASCII|STR_NOALIGN);
+ data_ofs += n;
+ break;
+ case 'a':
+ n = va_arg(ap, int);
+ SSVAL(blob->data, data_ofs, n); data_ofs += 2;
+ s = va_arg(ap, char *);
+ n = str_charnum(s);
+ SSVAL(blob->data, data_ofs, n*2); data_ofs += 2;
+ if (0 < n) {
+ push_string(NULL, blob->data+data_ofs, s, n*2,
+ STR_UNICODE|STR_NOALIGN);
+ }
+ data_ofs += n*2;
+ break;
+
+ case 'B':
+ b = va_arg(ap, uint8 *);
+ n = va_arg(ap, int);
+ SSVAL(blob->data, head_ofs, n); head_ofs += 2;
+ SSVAL(blob->data, head_ofs, n); head_ofs += 2;
+ SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
+ if (n && b) /* don't follow null pointers... */
+ memcpy(blob->data+data_ofs, b, n);
+ data_ofs += n;
+ break;
+ case 'd':
+ n = va_arg(ap, int);
+ SIVAL(blob->data, head_ofs, n); head_ofs += 4;
+ break;
+ case 'b':
+ b = va_arg(ap, uint8 *);
+ n = va_arg(ap, int);
+ memcpy(blob->data + head_ofs, b, n);
+ head_ofs += n;
+ break;
+ case 'C':
+ s = va_arg(ap, char *);
+ head_ofs += push_string(NULL, blob->data+head_ofs, s, -1,
+ STR_ASCII|STR_TERMINATE);
+ break;
+ }
+ }
+ va_end(ap);
+
+ return True;
+}
+
+
+/* a helpful macro to avoid running over the end of our blob */
+#define NEED_DATA(amount) \
+if ((head_ofs + amount) > blob->length) { \
+ return False; \
+}
+
+/*
+ this is a tiny msrpc packet parser. This the the partner of msrpc_gen
+
+ format specifiers are:
+
+ U = unicode string (output is unix string)
+ A = ascii string
+ B = data blob
+ b = data blob in header
+ d = word (4 bytes)
+ C = constant ascii string
+ */
+
+BOOL msrpc_parse(const DATA_BLOB *blob,
+ const char *format, ...)
+{
+ int i;
+ va_list ap;
+ char **ps, *s;
+ DATA_BLOB *b;
+ size_t head_ofs = 0;
+ uint16 len1, len2;
+ uint32 ptr;
+ uint32 *v;
+ pstring p;
+
+ va_start(ap, format);
+ for (i=0; format[i]; i++) {
+ switch (format[i]) {
+ case 'U':
+ NEED_DATA(8);
+ len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
+ len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
+ ptr = IVAL(blob->data, head_ofs); head_ofs += 4;
+
+ ps = va_arg(ap, char **);
+ if (len1 == 0 && len2 == 0) {
+ *ps = smb_xstrdup("");
+ } else {
+ /* make sure its in the right format - be strict */
+ if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
+ return False;
+ }
+ if (len1 & 1) {
+ /* if odd length and unicode */
+ return False;
+ }
+ if (blob->data + ptr < (uint8 *)ptr || blob->data + ptr < blob->data)
+ return False;
+
+ if (0 < len1) {
+ pull_string(NULL, p, blob->data + ptr, sizeof(p),
+ len1,
+ STR_UNICODE|STR_NOALIGN);
+ (*ps) = smb_xstrdup(p);
+ } else {
+ (*ps) = smb_xstrdup("");
+ }
+ }
+ break;
+ case 'A':
+ NEED_DATA(8);
+ len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
+ len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
+ ptr = IVAL(blob->data, head_ofs); head_ofs += 4;
+
+ ps = va_arg(ap, char **);
+ /* make sure its in the right format - be strict */
+ if (len1 == 0 && len2 == 0) {
+ *ps = smb_xstrdup("");
+ } else {
+ if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
+ return False;
+ }
+
+ if (blob->data + ptr < (uint8 *)ptr || blob->data + ptr < blob->data)
+ return False;
+
+ if (0 < len1) {
+ pull_string(NULL, p, blob->data + ptr, sizeof(p),
+ len1,
+ STR_ASCII|STR_NOALIGN);
+ (*ps) = smb_xstrdup(p);
+ } else {
+ (*ps) = smb_xstrdup("");
+ }
+ }
+ break;
+ case 'B':
+ NEED_DATA(8);
+ len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
+ len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
+ ptr = IVAL(blob->data, head_ofs); head_ofs += 4;
+
+ b = (DATA_BLOB *)va_arg(ap, void *);
+ if (len1 == 0 && len2 == 0) {
+ *b = data_blob(NULL, 0);
+ } else {
+ /* make sure its in the right format - be strict */
+ if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
+ return False;
+ }
+
+ if (blob->data + ptr < (uint8 *)ptr || blob->data + ptr < blob->data)
+ return False;
+
+ *b = data_blob(blob->data + ptr, len1);
+ }
+ break;
+ case 'b':
+ b = (DATA_BLOB *)va_arg(ap, void *);
+ len1 = va_arg(ap, unsigned);
+ /* make sure its in the right format - be strict */
+ NEED_DATA(len1);
+ if (blob->data + head_ofs < (uint8 *)head_ofs || blob->data + head_ofs < blob->data)
+ return False;
+
+ *b = data_blob(blob->data + head_ofs, len1);
+ head_ofs += len1;
+ break;
+ case 'd':
+ v = va_arg(ap, uint32 *);
+ NEED_DATA(4);
+ *v = IVAL(blob->data, head_ofs); head_ofs += 4;
+ break;
+ case 'C':
+ s = va_arg(ap, char *);
+
+ if (blob->data + head_ofs < (uint8 *)head_ofs || blob->data + head_ofs < blob->data)
+ return False;
+
+ head_ofs += pull_string(NULL, p, blob->data+head_ofs, sizeof(p),
+ blob->length - head_ofs,
+ STR_ASCII|STR_TERMINATE);
+ if (strcmp(s, p) != 0) {
+ return False;
+ }
+ break;
+ }
+ }
+ va_end(ap);
+
+ return True;
+}
diff --git a/source/libsmb/ntlmssp_sign.c b/source/libsmb/ntlmssp_sign.c
new file mode 100644
index 00000000000..2347619e57d
--- /dev/null
+++ b/source/libsmb/ntlmssp_sign.c
@@ -0,0 +1,407 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Version 3.0
+ * NTLMSSP Signing routines
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-2001
+ * Copyright (C) Andrew Bartlett 2003
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "includes.h"
+
+#define CLI_SIGN "session key to client-to-server signing key magic constant"
+#define CLI_SEAL "session key to client-to-server sealing key magic constant"
+#define SRV_SIGN "session key to server-to-client signing key magic constant"
+#define SRV_SEAL "session key to server-to-client sealing key magic constant"
+
+static void NTLMSSPcalc_ap( unsigned char *hash, unsigned char *data, int len)
+{
+ unsigned char index_i = hash[256];
+ unsigned char index_j = hash[257];
+ int ind;
+
+ for (ind = 0; ind < len; ind++)
+ {
+ unsigned char tc;
+ unsigned char t;
+
+ index_i++;
+ index_j += hash[index_i];
+
+ tc = hash[index_i];
+ hash[index_i] = hash[index_j];
+ hash[index_j] = tc;
+
+ t = hash[index_i] + hash[index_j];
+ data[ind] = data[ind] ^ hash[t];
+ }
+
+ hash[256] = index_i;
+ hash[257] = index_j;
+}
+
+static void calc_hash(unsigned char hash[258], const char *k2, int k2l)
+{
+ unsigned char j = 0;
+ int ind;
+
+ for (ind = 0; ind < 256; ind++)
+ {
+ hash[ind] = (unsigned char)ind;
+ }
+
+ for (ind = 0; ind < 256; ind++)
+ {
+ unsigned char tc;
+
+ j += (hash[ind] + k2[ind%k2l]);
+
+ tc = hash[ind];
+ hash[ind] = hash[j];
+ hash[j] = tc;
+ }
+
+ hash[256] = 0;
+ hash[257] = 0;
+}
+
+static void calc_ntlmv2_hash(unsigned char hash[258], unsigned char digest[16],
+ DATA_BLOB session_key,
+ const char *constant)
+{
+ struct MD5Context ctx3;
+
+ /* NOTE: This code is currently complate fantasy - it's
+ got more in common with reality than the previous code
+ (the LM session key is not the right thing to use) but
+ it still needs work */
+
+ MD5Init(&ctx3);
+ MD5Update(&ctx3, session_key.data, session_key.length);
+ MD5Update(&ctx3, (const unsigned char *)constant, strlen(constant)+1);
+ MD5Final(digest, &ctx3);
+
+ calc_hash(hash, digest, 16);
+}
+
+enum ntlmssp_direction {
+ NTLMSSP_SEND,
+ NTLMSSP_RECEIVE
+};
+
+static NTSTATUS ntlmssp_make_packet_signature(NTLMSSP_STATE *ntlmssp_state,
+ const uchar *data, size_t length,
+ enum ntlmssp_direction direction,
+ DATA_BLOB *sig)
+{
+ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+ HMACMD5Context ctx;
+ uchar seq_num[4];
+ uchar digest[16];
+ SIVAL(seq_num, 0, ntlmssp_state->ntlmssp_seq_num);
+
+ hmac_md5_init_limK_to_64((const unsigned char *)(ntlmssp_state->send_sign_const), 16, &ctx);
+ hmac_md5_update(seq_num, 4, &ctx);
+ hmac_md5_update(data, length, &ctx);
+ hmac_md5_final(digest, &ctx);
+
+ if (!msrpc_gen(sig, "dBd", NTLMSSP_SIGN_VERSION, digest, 8 /* only copy first 8 bytes */
+ , ntlmssp_state->ntlmssp_seq_num)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
+ switch (direction) {
+ case NTLMSSP_SEND:
+ NTLMSSPcalc_ap(ntlmssp_state->send_sign_hash, sig->data+4, sig->length-4);
+ break;
+ case NTLMSSP_RECEIVE:
+ NTLMSSPcalc_ap(ntlmssp_state->recv_sign_hash, sig->data+4, sig->length-4);
+ break;
+ }
+ }
+ } else {
+ uint32 crc;
+ crc = crc32_calc_buffer((const char *)data, length);
+ if (!msrpc_gen(sig, "dddd", NTLMSSP_SIGN_VERSION, 0, crc, ntlmssp_state->ntlmssp_seq_num)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dump_data_pw("ntlmssp hash:\n", ntlmssp_state->ntlmssp_hash,
+ sizeof(ntlmssp_state->ntlmssp_hash));
+ NTLMSSPcalc_ap(ntlmssp_state->ntlmssp_hash, sig->data+4, sig->length-4);
+ }
+ return NT_STATUS_OK;
+}
+
+NTSTATUS ntlmssp_sign_packet(NTLMSSP_STATE *ntlmssp_state,
+ const uchar *data, size_t length,
+ DATA_BLOB *sig)
+{
+ NTSTATUS nt_status;
+ if (!ntlmssp_state->session_key.length) {
+ DEBUG(3, ("NO session key, cannot check sign packet\n"));
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ nt_status = ntlmssp_make_packet_signature(ntlmssp_state, data, length, NTLMSSP_SEND, sig);
+
+ /* increment counter on send */
+ ntlmssp_state->ntlmssp_seq_num++;
+ return nt_status;
+}
+
+/**
+ * Check the signature of an incoming packet
+ * @note caller *must* check that the signature is the size it expects
+ *
+ */
+
+NTSTATUS ntlmssp_check_packet(NTLMSSP_STATE *ntlmssp_state,
+ const uchar *data, size_t length,
+ const DATA_BLOB *sig)
+{
+ DATA_BLOB local_sig;
+ NTSTATUS nt_status;
+
+ if (!ntlmssp_state->session_key.length) {
+ DEBUG(3, ("NO session key, cannot check packet signature\n"));
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ if (sig->length < 8) {
+ DEBUG(0, ("NTLMSSP packet check failed due to short signature (%lu bytes)!\n",
+ (unsigned long)sig->length));
+ }
+
+ nt_status = ntlmssp_make_packet_signature(ntlmssp_state, data,
+ length, NTLMSSP_RECEIVE, &local_sig);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("NTLMSSP packet check failed with %s\n", nt_errstr(nt_status)));
+ return nt_status;
+ }
+
+ if (memcmp(sig->data+sig->length - 8, local_sig.data+local_sig.length - 8, 8) != 0) {
+ DEBUG(5, ("BAD SIG: wanted signature of\n"));
+ dump_data(5, (const char *)local_sig.data, local_sig.length);
+
+ DEBUG(5, ("BAD SIG: got signature of\n"));
+ dump_data(5, (const char *)(sig->data), sig->length);
+
+ DEBUG(0, ("NTLMSSP packet check failed due to invalid signature!\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* increment counter on recieive */
+ ntlmssp_state->ntlmssp_seq_num++;
+
+ return NT_STATUS_OK;
+}
+
+
+/**
+ * Seal data with the NTLMSSP algorithm
+ *
+ */
+
+NTSTATUS ntlmssp_seal_packet(NTLMSSP_STATE *ntlmssp_state,
+ uchar *data, size_t length,
+ DATA_BLOB *sig)
+{
+ if (!ntlmssp_state->session_key.length) {
+ DEBUG(3, ("NO session key, cannot seal packet\n"));
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ DEBUG(10,("ntlmssp_seal_data: seal\n"));
+ dump_data_pw("ntlmssp clear data\n", data, length);
+ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+ HMACMD5Context ctx;
+ char seq_num[4];
+ uchar digest[16];
+ SIVAL(seq_num, 0, ntlmssp_state->ntlmssp_seq_num);
+
+ hmac_md5_init_limK_to_64((const unsigned char *)(ntlmssp_state->send_sign_const), 16, &ctx);
+ hmac_md5_update((const unsigned char *)seq_num, 4, &ctx);
+ hmac_md5_update(data, length, &ctx);
+ hmac_md5_final(digest, &ctx);
+
+ if (!msrpc_gen(sig, "dBd", NTLMSSP_SIGN_VERSION, digest, 8 /* only copy first 8 bytes */
+ , ntlmssp_state->ntlmssp_seq_num)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dump_data_pw("ntlmssp client sealing hash:\n",
+ ntlmssp_state->send_seal_hash,
+ sizeof(ntlmssp_state->send_seal_hash));
+ NTLMSSPcalc_ap(ntlmssp_state->send_seal_hash, data, length);
+ dump_data_pw("ntlmssp client signing hash:\n",
+ ntlmssp_state->send_sign_hash,
+ sizeof(ntlmssp_state->send_sign_hash));
+ NTLMSSPcalc_ap(ntlmssp_state->send_sign_hash, sig->data+4, sig->length-4);
+ } else {
+ uint32 crc;
+ crc = crc32_calc_buffer((const char *)data, length);
+ if (!msrpc_gen(sig, "dddd", NTLMSSP_SIGN_VERSION, 0, crc, ntlmssp_state->ntlmssp_seq_num)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* The order of these two operations matters - we must first seal the packet,
+ then seal the sequence number - this is becouse the ntlmssp_hash is not
+ constant, but is is rather updated with each iteration */
+
+ dump_data_pw("ntlmssp hash:\n", ntlmssp_state->ntlmssp_hash,
+ sizeof(ntlmssp_state->ntlmssp_hash));
+ NTLMSSPcalc_ap(ntlmssp_state->ntlmssp_hash, data, length);
+
+ dump_data_pw("ntlmssp hash:\n", ntlmssp_state->ntlmssp_hash,
+ sizeof(ntlmssp_state->ntlmssp_hash));
+ NTLMSSPcalc_ap(ntlmssp_state->ntlmssp_hash, sig->data+4, sig->length-4);
+ }
+ dump_data_pw("ntlmssp sealed data\n", data, length);
+
+ /* increment counter on send */
+ ntlmssp_state->ntlmssp_seq_num++;
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * Unseal data with the NTLMSSP algorithm
+ *
+ */
+
+NTSTATUS ntlmssp_unseal_packet(NTLMSSP_STATE *ntlmssp_state,
+ uchar *data, size_t length,
+ DATA_BLOB *sig)
+{
+ if (!ntlmssp_state->session_key.length) {
+ DEBUG(3, ("NO session key, cannot unseal packet\n"));
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ DEBUG(10,("ntlmssp__unseal_data: seal\n"));
+ dump_data_pw("ntlmssp sealed data\n", data, length);
+ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+ NTLMSSPcalc_ap(ntlmssp_state->recv_seal_hash, data, length);
+ } else {
+ dump_data_pw("ntlmssp hash:\n", ntlmssp_state->ntlmssp_hash,
+ sizeof(ntlmssp_state->ntlmssp_hash));
+ NTLMSSPcalc_ap(ntlmssp_state->ntlmssp_hash, data, length);
+ }
+ dump_data_pw("ntlmssp clear data\n", data, length);
+
+ return ntlmssp_check_packet(ntlmssp_state, data, length, sig);
+}
+
+/**
+ Initialise the state for NTLMSSP signing.
+*/
+NTSTATUS ntlmssp_sign_init(NTLMSSP_STATE *ntlmssp_state)
+{
+ unsigned char p24[24];
+ ZERO_STRUCT(p24);
+
+ DEBUG(3, ("NTLMSSP Sign/Seal - Initialising with flags:\n"));
+ debug_ntlmssp_flags(ntlmssp_state->neg_flags);
+
+ if (!ntlmssp_state->session_key.length) {
+ DEBUG(3, ("NO session key, cannot intialise signing\n"));
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2)
+ {
+ const char *send_sign_const;
+ const char *send_seal_const;
+ const char *recv_sign_const;
+ const char *recv_seal_const;
+
+ switch (ntlmssp_state->role) {
+ case NTLMSSP_CLIENT:
+ send_sign_const = CLI_SIGN;
+ send_seal_const = CLI_SEAL;
+ recv_sign_const = SRV_SIGN;
+ recv_seal_const = SRV_SEAL;
+ break;
+ case NTLMSSP_SERVER:
+ send_sign_const = SRV_SIGN;
+ send_seal_const = SRV_SEAL;
+ recv_sign_const = CLI_SIGN;
+ recv_seal_const = CLI_SEAL;
+ break;
+ }
+
+ calc_ntlmv2_hash(ntlmssp_state->send_sign_hash,
+ ntlmssp_state->send_sign_const,
+ ntlmssp_state->session_key, send_sign_const);
+ dump_data_pw("NTLMSSP send sign hash:\n",
+ ntlmssp_state->send_sign_hash,
+ sizeof(ntlmssp_state->send_sign_hash));
+
+ calc_ntlmv2_hash(ntlmssp_state->send_seal_hash,
+ ntlmssp_state->send_seal_const,
+ ntlmssp_state->session_key, send_seal_const);
+ dump_data_pw("NTLMSSP send sesl hash:\n",
+ ntlmssp_state->send_seal_hash,
+ sizeof(ntlmssp_state->send_seal_hash));
+
+ calc_ntlmv2_hash(ntlmssp_state->recv_sign_hash,
+ ntlmssp_state->recv_sign_const,
+ ntlmssp_state->session_key, recv_sign_const);
+ dump_data_pw("NTLMSSP receive sign hash:\n",
+ ntlmssp_state->recv_sign_hash,
+ sizeof(ntlmssp_state->recv_sign_hash));
+
+ calc_ntlmv2_hash(ntlmssp_state->recv_seal_hash,
+ ntlmssp_state->recv_seal_const,
+ ntlmssp_state->session_key, recv_seal_const);
+ dump_data_pw("NTLMSSP receive seal hash:\n",
+ ntlmssp_state->recv_sign_hash,
+ sizeof(ntlmssp_state->recv_sign_hash));
+
+ }
+ else if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) {
+ if (!ntlmssp_state->session_key.data || ntlmssp_state->session_key.length < 8) {
+ /* can't sign or check signatures yet */
+ DEBUG(5, ("NTLMSSP Sign/Seal - cannot use LM KEY yet\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(5, ("NTLMSSP Sign/Seal - using LM KEY\n"));
+
+ calc_hash(ntlmssp_state->ntlmssp_hash, (const char *)(ntlmssp_state->session_key.data), 8);
+ dump_data_pw("NTLMSSP hash:\n", ntlmssp_state->ntlmssp_hash,
+ sizeof(ntlmssp_state->ntlmssp_hash));
+ } else {
+ if (!ntlmssp_state->session_key.data || ntlmssp_state->session_key.length < 16) {
+ /* can't sign or check signatures yet */
+ DEBUG(5, ("NTLMSSP Sign/Seal - cannot use NT KEY yet\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(5, ("NTLMSSP Sign/Seal - using NT KEY\n"));
+
+ calc_hash(ntlmssp_state->ntlmssp_hash, (const char *)(ntlmssp_state->session_key.data), 16);
+ dump_data_pw("NTLMSSP hash:\n", ntlmssp_state->ntlmssp_hash,
+ sizeof(ntlmssp_state->ntlmssp_hash));
+ }
+
+ ntlmssp_state->ntlmssp_seq_num = 0;
+
+ return NT_STATUS_OK;
+}
diff --git a/source/libsmb/passchange.c b/source/libsmb/passchange.c
new file mode 100644
index 00000000000..dc0cbbcb7cc
--- /dev/null
+++ b/source/libsmb/passchange.c
@@ -0,0 +1,152 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client password change routine
+ Copyright (C) Andrew Tridgell 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*************************************************************
+change a password on a remote machine using IPC calls
+*************************************************************/
+BOOL remote_password_change(const char *remote_machine, const char *user_name,
+ const char *old_passwd, const char *new_passwd,
+ char *err_str, size_t err_str_len)
+{
+ struct nmb_name calling, called;
+ struct cli_state cli;
+ struct in_addr ip;
+ struct ntuser_creds creds;
+
+ NTSTATUS result;
+
+ *err_str = '\0';
+
+ if(!resolve_name( remote_machine, &ip, 0x20)) {
+ slprintf(err_str, err_str_len-1, "unable to find an IP address for machine %s.\n",
+ remote_machine );
+ return False;
+ }
+
+ ZERO_STRUCT(cli);
+
+ if (!cli_initialise(&cli) || !cli_connect(&cli, remote_machine, &ip)) {
+ slprintf(err_str, err_str_len-1, "unable to connect to SMB server on machine %s. Error was : %s.\n",
+ remote_machine, cli_errstr(&cli) );
+ return False;
+ }
+
+ make_nmb_name(&calling, global_myname() , 0x0);
+ make_nmb_name(&called , remote_machine, 0x20);
+
+ if (!cli_session_request(&cli, &calling, &called)) {
+ slprintf(err_str, err_str_len-1, "machine %s rejected the session setup. Error was : %s.\n",
+ remote_machine, cli_errstr(&cli) );
+ cli_shutdown(&cli);
+ return False;
+ }
+
+ cli.protocol = PROTOCOL_NT1;
+
+ if (!cli_negprot(&cli)) {
+ slprintf(err_str, err_str_len-1, "machine %s rejected the negotiate protocol. Error was : %s.\n",
+ remote_machine, cli_errstr(&cli) );
+ cli_shutdown(&cli);
+ return False;
+ }
+
+ /* Given things like SMB signing, restrict anonymous and the like,
+ try an authenticated connection first */
+ if (!cli_session_setup(&cli, user_name, old_passwd, strlen(old_passwd)+1, old_passwd, strlen(old_passwd)+1, "")) {
+ /*
+ * We should connect as the anonymous user here, in case
+ * the server has "must change password" checked...
+ * Thanks to <Nicholas.S.Jenkins@cdc.com> for this fix.
+ */
+
+ if (!cli_session_setup(&cli, "", "", 0, "", 0, "")) {
+ slprintf(err_str, err_str_len-1, "machine %s rejected the session setup. Error was : %s.\n",
+ remote_machine, cli_errstr(&cli) );
+ cli_shutdown(&cli);
+ return False;
+ }
+
+ init_creds(&creds, "", "", NULL);
+ cli_init_creds(&cli, &creds);
+ } else {
+ init_creds(&creds, user_name, "", old_passwd);
+ cli_init_creds(&cli, &creds);
+ }
+
+ if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) {
+ slprintf(err_str, err_str_len-1, "machine %s rejected the tconX on the IPC$ share. Error was : %s.\n",
+ remote_machine, cli_errstr(&cli) );
+ cli_shutdown(&cli);
+ return False;
+ }
+
+ /* Try not to give the password away to easily */
+
+ cli.pipe_auth_flags = AUTH_PIPE_NTLMSSP;
+ cli.pipe_auth_flags |= AUTH_PIPE_SIGN;
+ cli.pipe_auth_flags |= AUTH_PIPE_SEAL;
+
+ if ( !cli_nt_session_open( &cli, PI_SAMR ) ) {
+ if (lp_client_lanman_auth()) {
+ if (!cli_oem_change_password(&cli, user_name, new_passwd, old_passwd)) {
+ slprintf(err_str, err_str_len-1, "machine %s rejected the password change: Error was : %s.\n",
+ remote_machine, cli_errstr(&cli) );
+ cli_shutdown(&cli);
+ return False;
+ }
+ } else {
+ slprintf(err_str, err_str_len-1, "machine %s does not support SAMR connections, but LANMAN password changed are disabled\n",
+ remote_machine);
+ cli_shutdown(&cli);
+ return False;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(result = cli_samr_chgpasswd_user(&cli, cli.mem_ctx, user_name,
+ new_passwd, old_passwd))) {
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)
+ || NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
+ /* try the old Lanman method */
+ if (lp_client_lanman_auth()) {
+ if (!cli_oem_change_password(&cli, user_name, new_passwd, old_passwd)) {
+ slprintf(err_str, err_str_len-1, "machine %s rejected the password change: Error was : %s.\n",
+ remote_machine, cli_errstr(&cli) );
+ cli_shutdown(&cli);
+ return False;
+ }
+ } else {
+ slprintf(err_str, err_str_len-1, "machine %s does not support SAMR connections, but LANMAN password changed are disabled\n",
+ remote_machine);
+ cli_shutdown(&cli);
+ return False;
+ }
+ } else {
+ slprintf(err_str, err_str_len-1, "machine %s rejected the password change: Error was : %s.\n",
+ remote_machine, get_friendly_nt_error_msg(result));
+ cli_shutdown(&cli);
+ return False;
+ }
+ }
+ cli_shutdown(&cli);
+ return True;
+}
diff --git a/source/libsmb/pwd_cache.c b/source/libsmb/pwd_cache.c
new file mode 100644
index 00000000000..e010f226a02
--- /dev/null
+++ b/source/libsmb/pwd_cache.c
@@ -0,0 +1,63 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password cacheing. obfuscation is planned
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+ Initialises a password structure.
+****************************************************************************/
+
+static void pwd_init(struct pwd_info *pwd)
+{
+ memset((char *)pwd->password , '\0', sizeof(pwd->password ));
+
+ pwd->null_pwd = True; /* safest option... */
+}
+
+/****************************************************************************
+ Stores a cleartext password.
+****************************************************************************/
+
+void pwd_set_cleartext(struct pwd_info *pwd, const char *clr)
+{
+ pwd_init(pwd);
+ if (clr) {
+ fstrcpy(pwd->password, clr);
+ pwd->null_pwd = False;
+ } else {
+ pwd->null_pwd = True;
+ }
+
+ pwd->cleartext = True;
+}
+
+/****************************************************************************
+ Gets a cleartext password.
+****************************************************************************/
+
+void pwd_get_cleartext(struct pwd_info *pwd, fstring clr)
+{
+ if (pwd->cleartext)
+ fstrcpy(clr, pwd->password);
+ else
+ clr[0] = 0;
+
+}
+
diff --git a/source/libsmb/samlogon_cache.c b/source/libsmb/samlogon_cache.c
new file mode 100644
index 00000000000..4cd642c4e35
--- /dev/null
+++ b/source/libsmb/samlogon_cache.c
@@ -0,0 +1,238 @@
+/*
+ Unix SMB/CIFS implementation.
+ Net_sam_logon info3 helpers
+ Copyright (C) Alexander Bokovoy 2002.
+ Copyright (C) Andrew Bartlett 2002.
+ Copyright (C) Gerald Carter 2003.
+ Copyright (C) Tim Potter 2003.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#define NETSAMLOGON_TDB "netsamlogon_cache.tdb"
+
+static TDB_CONTEXT *netsamlogon_tdb = NULL;
+
+/***********************************************************************
+ open the tdb
+ ***********************************************************************/
+
+BOOL netsamlogon_cache_init(void)
+{
+ if (!netsamlogon_tdb) {
+ netsamlogon_tdb = tdb_open_log(lock_path(NETSAMLOGON_TDB), 0,
+ TDB_DEFAULT, O_RDWR | O_CREAT, 0600);
+ }
+
+ return (netsamlogon_tdb != NULL);
+}
+
+
+/***********************************************************************
+ Shutdown samlogon_cache database
+***********************************************************************/
+
+BOOL netsamlogon_cache_shutdown(void)
+{
+ if(netsamlogon_tdb)
+ return (tdb_close(netsamlogon_tdb) == 0);
+
+ return True;
+}
+
+/***********************************************************************
+ Clear cache getpwnam and getgroups entries from the winbindd cache
+***********************************************************************/
+void netsamlogon_clear_cached_user(TDB_CONTEXT *tdb, NET_USER_INFO_3 *user)
+{
+ fstring domain;
+ TDB_DATA key;
+ BOOL got_tdb = False;
+
+ /* We may need to call this function from smbd which will not have
+ winbindd_cache.tdb open. Open the tdb if a NULL is passed. */
+
+ if (!tdb) {
+ tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000,
+ TDB_DEFAULT, O_RDWR, 0600);
+ if (!tdb) {
+ DEBUG(5, ("netsamlogon_clear_cached_user: failed to open cache\n"));
+ return;
+ }
+ got_tdb = True;
+ }
+
+ unistr2_to_ascii(domain, &user->uni_logon_dom, sizeof(domain) - 1);
+
+ /* Clear U/DOMAIN/RID cache entry */
+
+ asprintf(&key.dptr, "U/%s/%d", domain, user->user_rid);
+ key.dsize = strlen(key.dptr) - 1; /* keys are not NULL terminated */
+
+ DEBUG(10, ("netsamlogon_clear_cached_user: clearing %s\n", key.dptr));
+
+ tdb_delete(tdb, key);
+
+ SAFE_FREE(key.dptr);
+
+ /* Clear UG/DOMAIN/RID cache entry */
+
+ asprintf(&key.dptr, "UG/%s/%d", domain, user->user_rid);
+ key.dsize = strlen(key.dptr) - 1; /* keys are not NULL terminated */
+
+ DEBUG(10, ("netsamlogon_clear_cached_user: clearing %s\n", key.dptr));
+
+ tdb_delete(tdb, key);
+
+ SAFE_FREE(key.dptr);
+
+ if (got_tdb)
+ tdb_close(tdb);
+}
+
+/***********************************************************************
+ Store a NET_USER_INFO_3 structure in a tdb for later user
+***********************************************************************/
+
+BOOL netsamlogon_cache_store(TALLOC_CTX *mem_ctx, NET_USER_INFO_3 *user)
+{
+ TDB_DATA data;
+ fstring keystr;
+ prs_struct ps;
+ BOOL result = False;
+ DOM_SID user_sid;
+ time_t t = time(NULL);
+
+
+ if (!netsamlogon_cache_init()) {
+ DEBUG(0,("netsamlogon_cache_store: cannot open %s for write!\n", NETSAMLOGON_TDB));
+ return False;
+ }
+
+ sid_copy( &user_sid, &user->dom_sid.sid );
+ sid_append_rid( &user_sid, user->user_rid );
+
+ /* Prepare key as DOMAIN-SID/USER-RID string */
+ slprintf(keystr, sizeof(keystr), "%s", sid_string_static(&user_sid));
+
+ DEBUG(10,("netsamlogon_cache_store: SID [%s]\n", keystr));
+
+ /* Prepare data */
+
+ prs_init( &ps,MAX_PDU_FRAG_LEN , mem_ctx, MARSHALL);
+
+ if ( !prs_uint32( "timestamp", &ps, 0, (uint32*)&t ) )
+ return False;
+
+ if ( net_io_user_info3("", user, &ps, 0, 3) )
+ {
+ data.dsize = prs_offset( &ps );
+ data.dptr = prs_data_p( &ps );
+
+ if (tdb_store_bystring(netsamlogon_tdb, keystr, data, TDB_REPLACE) != -1)
+ result = True;
+
+ prs_mem_free( &ps );
+ }
+
+ return result;
+}
+
+/***********************************************************************
+ Retrieves a NET_USER_INFO_3 structure from a tdb. Caller must
+ free the user_info struct (malloc()'d memory)
+***********************************************************************/
+
+NET_USER_INFO_3* netsamlogon_cache_get( TALLOC_CTX *mem_ctx, const DOM_SID *user_sid)
+{
+ NET_USER_INFO_3 *user = NULL;
+ TDB_DATA data, key;
+ prs_struct ps;
+ fstring keystr;
+ uint32 t;
+
+ if (!netsamlogon_cache_init()) {
+ DEBUG(0,("netsamlogon_cache_store: cannot open %s for write!\n", NETSAMLOGON_TDB));
+ return False;
+ }
+
+ /* Prepare key as DOMAIN-SID/USER-RID string */
+ slprintf(keystr, sizeof(keystr), "%s", sid_string_static(user_sid));
+ DEBUG(10,("netsamlogon_cache_get: SID [%s]\n", keystr));
+ key.dptr = keystr;
+ key.dsize = strlen(keystr)+1;
+ data = tdb_fetch( netsamlogon_tdb, key );
+
+ if ( data.dptr ) {
+
+ if ( (user = (NET_USER_INFO_3*)malloc(sizeof(NET_USER_INFO_3))) == NULL )
+ return NULL;
+
+ prs_init( &ps, 0, mem_ctx, UNMARSHALL );
+ prs_give_memory( &ps, data.dptr, data.dsize, True );
+
+ if ( !prs_uint32( "timestamp", &ps, 0, &t ) ) {
+ prs_mem_free( &ps );
+ return False;
+ }
+
+ if ( !net_io_user_info3("", user, &ps, 0, 3) ) {
+ SAFE_FREE( user );
+ }
+
+ prs_mem_free( &ps );
+
+#if 0 /* The netsamlogon cache needs to hang around. Something about
+ this feels wrong, but it is the only way we can get all of the
+ groups. The old universal groups cache didn't expire either.
+ --jerry */
+ {
+ time_t now = time(NULL);
+ uint32 time_diff;
+
+ /* is the entry expired? */
+ time_diff = now - t;
+
+ if ( (time_diff < 0 ) || (time_diff > lp_winbind_cache_time()) ) {
+ DEBUG(10,("netsamlogon_cache_get: cache entry expired \n"));
+ tdb_delete( netsamlogon_tdb, key );
+ SAFE_FREE( user );
+ }
+#endif
+ }
+
+ return user;
+}
+
+BOOL netsamlogon_cache_have(const DOM_SID *user_sid)
+{
+ TALLOC_CTX *mem_ctx = talloc_init("netsamlogon_cache_have");
+ NET_USER_INFO_3 *user = NULL;
+ BOOL result;
+
+ if (!mem_ctx)
+ return False;
+
+ user = netsamlogon_cache_get(mem_ctx, user_sid);
+
+ result = (user != NULL);
+
+ talloc_destroy(mem_ctx);
+ SAFE_FREE(user);
+
+ return result;
+}
diff --git a/source/libsmb/smb_signing.c b/source/libsmb/smb_signing.c
new file mode 100644
index 00000000000..28ff0e0c2e9
--- /dev/null
+++ b/source/libsmb/smb_signing.c
@@ -0,0 +1,1052 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB Signing Code
+ Copyright (C) Jeremy Allison 2003.
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2002-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* Lookup a packet's MID (multiplex id) and figure out it's sequence number */
+struct outstanding_packet_lookup {
+ uint16 mid;
+ uint32 reply_seq_num;
+ struct outstanding_packet_lookup *prev, *next;
+};
+
+/* Store the data for an ongoing trans/trans2/nttrans operation. */
+struct trans_info_context {
+ uint16 mid;
+ uint32 send_seq_num;
+ uint32 reply_seq_num;
+};
+
+struct smb_basic_signing_context {
+ DATA_BLOB mac_key;
+ uint32 send_seq_num;
+ struct trans_info_context *trans_info;
+ struct outstanding_packet_lookup *outstanding_packet_list;
+};
+
+static void store_sequence_for_reply(struct outstanding_packet_lookup **list,
+ uint16 mid, uint32 reply_seq_num)
+{
+ struct outstanding_packet_lookup *t;
+
+ t = smb_xmalloc(sizeof(*t));
+ ZERO_STRUCTP(t);
+
+ t->mid = mid;
+ t->reply_seq_num = reply_seq_num;
+
+ /*
+ * Add to the *start* of the list not the end of the list.
+ * This ensures that the *last* send sequence with this mid
+ * is returned by preference.
+ * This can happen if the mid wraps and one of the early
+ * mid numbers didn't get a reply and is still lurking on
+ * the list. JRA. Found by Fran Fabrizio <fran@cis.uab.edu>.
+ */
+
+ DLIST_ADD(*list, t);
+ DEBUG(10,("store_sequence_for_reply: stored seq = %u mid = %u\n",
+ (unsigned int)reply_seq_num, (unsigned int)mid ));
+}
+
+static BOOL get_sequence_for_reply(struct outstanding_packet_lookup **list,
+ uint16 mid, uint32 *reply_seq_num)
+{
+ struct outstanding_packet_lookup *t;
+
+ for (t = *list; t; t = t->next) {
+ if (t->mid == mid) {
+ *reply_seq_num = t->reply_seq_num;
+ DEBUG(10,("get_sequence_for_reply: found seq = %u mid = %u\n",
+ (unsigned int)t->reply_seq_num, (unsigned int)t->mid ));
+ DLIST_REMOVE(*list, t);
+ SAFE_FREE(t);
+ return True;
+ }
+ }
+ return False;
+}
+
+/***********************************************************
+ SMB signing - Common code before we set a new signing implementation
+************************************************************/
+
+static BOOL cli_set_smb_signing_common(struct cli_state *cli)
+{
+ if (!cli->sign_info.negotiated_smb_signing
+ && !cli->sign_info.mandatory_signing) {
+ return False;
+ }
+
+ if (cli->sign_info.doing_signing) {
+ return False;
+ }
+
+ if (cli->sign_info.free_signing_context)
+ cli->sign_info.free_signing_context(&cli->sign_info);
+
+ /* These calls are INCOMPATIBLE with SMB signing */
+ cli->readbraw_supported = False;
+ cli->writebraw_supported = False;
+
+ return True;
+}
+
+/***********************************************************
+ SMB signing - Common code for 'real' implementations
+************************************************************/
+
+static BOOL set_smb_signing_real_common(struct smb_sign_info *si)
+{
+ if (si->mandatory_signing) {
+ DEBUG(5, ("Mandatory SMB signing enabled!\n"));
+ }
+
+ si->doing_signing = True;
+ DEBUG(5, ("SMB signing enabled!\n"));
+
+ return True;
+}
+
+static void mark_packet_signed(char *outbuf)
+{
+ uint16 flags2;
+ flags2 = SVAL(outbuf,smb_flg2);
+ flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES;
+ SSVAL(outbuf,smb_flg2, flags2);
+}
+
+/***********************************************************
+ SMB signing - NULL implementation - calculate a MAC to send.
+************************************************************/
+
+static void null_sign_outgoing_message(char *outbuf, struct smb_sign_info *si)
+{
+ /* we can't zero out the sig, as we might be trying to send a
+ session request - which is NBT-level, not SMB level and doesn't
+ have the field */
+ return;
+}
+
+/***********************************************************
+ SMB signing - NULL implementation - check a MAC sent by server.
+************************************************************/
+
+static BOOL null_check_incoming_message(char *inbuf, struct smb_sign_info *si, BOOL expected_ok)
+{
+ return True;
+}
+
+/***********************************************************
+ SMB signing - NULL implementation - free signing context
+************************************************************/
+
+static void null_free_signing_context(struct smb_sign_info *si)
+{
+ return;
+}
+
+/**
+ SMB signing - NULL implementation - setup the MAC key.
+
+ @note Used as an initialisation only - it will not correctly
+ shut down a real signing mechanism
+*/
+
+static BOOL null_set_signing(struct smb_sign_info *si)
+{
+ si->signing_context = NULL;
+
+ si->sign_outgoing_message = null_sign_outgoing_message;
+ si->check_incoming_message = null_check_incoming_message;
+ si->free_signing_context = null_free_signing_context;
+
+ return True;
+}
+
+/**
+ * Free the signing context
+ */
+
+static void free_signing_context(struct smb_sign_info *si)
+{
+ if (si->free_signing_context) {
+ si->free_signing_context(si);
+ si->signing_context = NULL;
+ }
+
+ null_set_signing(si);
+}
+
+
+static BOOL signing_good(char *inbuf, struct smb_sign_info *si, BOOL good, uint32 seq, BOOL expected_ok)
+{
+ if (good) {
+
+ if (!si->doing_signing) {
+ si->doing_signing = True;
+ }
+
+ if (!si->seen_valid) {
+ si->seen_valid = True;
+ }
+
+ } else {
+ if (!si->mandatory_signing && !si->seen_valid) {
+
+ if (!expected_ok) {
+ return True;
+ }
+ /* Non-mandatory signing - just turn off if this is the first bad packet.. */
+ DEBUG(5, ("signing_good: signing negotiated but not required and the other side \
+isn't sending correct signatures. Turning signatures off.\n"));
+ si->negotiated_smb_signing = False;
+ si->allow_smb_signing = False;
+ si->doing_signing = False;
+ free_signing_context(si);
+ return True;
+ } else if (!expected_ok) {
+ /* This packet is known to be unsigned */
+ return True;
+ } else {
+ /* Mandatory signing or bad packet after signing started - fail and disconnect. */
+ if (seq)
+ DEBUG(0, ("signing_good: BAD SIG: seq %u\n", (unsigned int)seq));
+ return False;
+ }
+ }
+ return True;
+}
+
+/***********************************************************
+ SMB signing - Simple implementation - calculate a MAC on the packet
+************************************************************/
+
+static void simple_packet_signature(struct smb_basic_signing_context *data,
+ const uchar *buf, uint32 seq_number,
+ unsigned char calc_md5_mac[16])
+{
+ const size_t offset_end_of_sig = (smb_ss_field + 8);
+ unsigned char sequence_buf[8];
+ struct MD5Context md5_ctx;
+
+ /*
+ * Firstly put the sequence number into the first 4 bytes.
+ * and zero out the next 4 bytes.
+ *
+ * We do this here, to avoid modifying the packet.
+ */
+
+ DEBUG(10,("simple_packet_signature: sequence number %u\n", seq_number ));
+
+ SIVAL(sequence_buf, 0, seq_number);
+ SIVAL(sequence_buf, 4, 0);
+
+ /* Calculate the 16 byte MAC - but don't alter the data in the
+ incoming packet.
+
+ This makes for a bit of fussing about, but it's not too bad.
+ */
+ MD5Init(&md5_ctx);
+
+ /* intialise with the key */
+ MD5Update(&md5_ctx, data->mac_key.data,
+ data->mac_key.length);
+
+ /* copy in the first bit of the SMB header */
+ MD5Update(&md5_ctx, buf + 4, smb_ss_field - 4);
+
+ /* copy in the sequence number, instead of the signature */
+ MD5Update(&md5_ctx, sequence_buf, sizeof(sequence_buf));
+
+ /* copy in the rest of the packet in, skipping the signature */
+ MD5Update(&md5_ctx, buf + offset_end_of_sig,
+ smb_len(buf) - (offset_end_of_sig - 4));
+
+ /* calculate the MD5 sig */
+ MD5Final(calc_md5_mac, &md5_ctx);
+}
+
+
+/***********************************************************
+ SMB signing - Client implementation - send the MAC.
+************************************************************/
+
+static void client_sign_outgoing_message(char *outbuf, struct smb_sign_info *si)
+{
+ unsigned char calc_md5_mac[16];
+ struct smb_basic_signing_context *data = si->signing_context;
+ uint32 send_seq_num;
+
+ if (!si->doing_signing)
+ return;
+
+ /* JRA Paranioa test - we should be able to get rid of this... */
+ if (smb_len(outbuf) < (smb_ss_field + 8 - 4)) {
+ DEBUG(1, ("client_sign_outgoing_message: Logic error. Can't check signature on short packet! smb_len = %u\n",
+ smb_len(outbuf) ));
+ abort();
+ }
+
+ /* mark the packet as signed - BEFORE we sign it...*/
+ mark_packet_signed(outbuf);
+
+ if (data->trans_info)
+ send_seq_num = data->trans_info->send_seq_num;
+ else
+ send_seq_num = data->send_seq_num;
+
+ simple_packet_signature(data, (const unsigned char *)outbuf, send_seq_num, calc_md5_mac);
+
+ DEBUG(10, ("client_sign_outgoing_message: sent SMB signature of\n"));
+ dump_data(10, (const char *)calc_md5_mac, 8);
+
+ memcpy(&outbuf[smb_ss_field], calc_md5_mac, 8);
+
+/* cli->outbuf[smb_ss_field+2]=0;
+ Uncomment this to test if the remote server actually verifies signatures...*/
+
+ if (data->trans_info)
+ return;
+
+ data->send_seq_num++;
+ store_sequence_for_reply(&data->outstanding_packet_list,
+ SVAL(outbuf,smb_mid), data->send_seq_num);
+ data->send_seq_num++;
+}
+
+/***********************************************************
+ SMB signing - Client implementation - check a MAC sent by server.
+************************************************************/
+
+static BOOL client_check_incoming_message(char *inbuf, struct smb_sign_info *si, BOOL expected_ok)
+{
+ BOOL good;
+ uint32 reply_seq_number;
+ uint32 saved_seq;
+ unsigned char calc_md5_mac[16];
+ unsigned char *server_sent_mac;
+
+ struct smb_basic_signing_context *data = si->signing_context;
+
+ if (!si->doing_signing)
+ return True;
+
+ if (smb_len(inbuf) < (smb_ss_field + 8 - 4)) {
+ DEBUG(1, ("client_check_incoming_message: Can't check signature on short packet! smb_len = %u\n", smb_len(inbuf)));
+ return False;
+ }
+
+ if (data->trans_info) {
+ reply_seq_number = data->trans_info->reply_seq_num;
+ } else if (!get_sequence_for_reply(&data->outstanding_packet_list,
+ SVAL(inbuf, smb_mid), &reply_seq_number)) {
+ DEBUG(1, ("client_check_incoming_message: failed to get sequence number %u for reply.\n",
+ (unsigned int) SVAL(inbuf, smb_mid) ));
+ return False;
+ }
+
+ saved_seq = reply_seq_number;
+ simple_packet_signature(data, (const unsigned char *)inbuf, reply_seq_number, calc_md5_mac);
+
+ server_sent_mac = (unsigned char *)&inbuf[smb_ss_field];
+ good = (memcmp(server_sent_mac, calc_md5_mac, 8) == 0);
+
+ if (!good) {
+ DEBUG(5, ("client_check_incoming_message: BAD SIG: wanted SMB signature of\n"));
+ dump_data(5, (const char *)calc_md5_mac, 8);
+
+ DEBUG(5, ("client_check_incoming_message: BAD SIG: got SMB signature of\n"));
+ dump_data(5, (const char *)server_sent_mac, 8);
+#if 1 /* JRATEST */
+ {
+ int i;
+ reply_seq_number -= 5;
+ for (i = 0; i < 10; i++, reply_seq_number++) {
+ simple_packet_signature(data, (const unsigned char *)inbuf, reply_seq_number, calc_md5_mac);
+ if (memcmp(server_sent_mac, calc_md5_mac, 8) == 0) {
+ DEBUG(0,("client_check_incoming_message: out of seq. seq num %u matches. \
+We were expecting seq %u\n", reply_seq_number, saved_seq ));
+ break;
+ }
+ }
+ }
+#endif /* JRATEST */
+
+ } else {
+ DEBUG(10, ("client_check_incoming_message: seq %u: got good SMB signature of\n", (unsigned int)reply_seq_number));
+ dump_data(10, (const char *)server_sent_mac, 8);
+ }
+ return signing_good(inbuf, si, good, saved_seq, expected_ok);
+}
+
+/***********************************************************
+ SMB signing - Simple implementation - free signing context
+************************************************************/
+
+static void simple_free_signing_context(struct smb_sign_info *si)
+{
+ struct smb_basic_signing_context *data = si->signing_context;
+ struct outstanding_packet_lookup *list = data->outstanding_packet_list;
+
+ while (list) {
+ struct outstanding_packet_lookup *old_head = list;
+ DLIST_REMOVE(list, list);
+ SAFE_FREE(old_head);
+ }
+
+ data_blob_free(&data->mac_key);
+
+ if (data->trans_info)
+ SAFE_FREE(data->trans_info);
+
+ SAFE_FREE(si->signing_context);
+
+ return;
+}
+
+/***********************************************************
+ SMB signing - Simple implementation - setup the MAC key.
+************************************************************/
+
+BOOL cli_simple_set_signing(struct cli_state *cli,
+ const DATA_BLOB user_session_key,
+ const DATA_BLOB response)
+{
+ struct smb_basic_signing_context *data;
+
+ if (!user_session_key.length)
+ return False;
+
+ if (!cli_set_smb_signing_common(cli)) {
+ return False;
+ }
+
+ if (!set_smb_signing_real_common(&cli->sign_info)) {
+ return False;
+ }
+
+ data = smb_xmalloc(sizeof(*data));
+ memset(data, '\0', sizeof(*data));
+
+ cli->sign_info.signing_context = data;
+
+ data->mac_key = data_blob(NULL, response.length + user_session_key.length);
+
+ memcpy(&data->mac_key.data[0], user_session_key.data, user_session_key.length);
+
+ DEBUG(10, ("cli_simple_set_signing: user_session_key\n"));
+ dump_data(10, (const char *)user_session_key.data, user_session_key.length);
+
+ if (response.length) {
+ memcpy(&data->mac_key.data[user_session_key.length],response.data, response.length);
+ DEBUG(10, ("cli_simple_set_signing: response_data\n"));
+ dump_data(10, (const char *)response.data, response.length);
+ } else {
+ DEBUG(10, ("cli_simple_set_signing: NULL response_data\n"));
+ }
+
+ dump_data_pw("MAC ssession key is:\n", data->mac_key.data, data->mac_key.length);
+
+ /* Initialise the sequence number */
+ data->send_seq_num = 0;
+
+ /* Initialise the list of outstanding packets */
+ data->outstanding_packet_list = NULL;
+
+ cli->sign_info.sign_outgoing_message = client_sign_outgoing_message;
+ cli->sign_info.check_incoming_message = client_check_incoming_message;
+ cli->sign_info.free_signing_context = simple_free_signing_context;
+
+ return True;
+}
+
+/***********************************************************
+ Tell client code we are in a multiple trans reply state.
+ We call this after the last outgoing trans2 packet (which
+ has incremented the sequence numbers), so we must save the
+ current mid and sequence number -2.
+************************************************************/
+
+void cli_signing_trans_start(struct cli_state *cli, uint16 mid)
+{
+ struct smb_basic_signing_context *data = cli->sign_info.signing_context;
+
+ if (!cli->sign_info.doing_signing || !data)
+ return;
+
+ data->trans_info = smb_xmalloc(sizeof(struct trans_info_context));
+ ZERO_STRUCTP(data->trans_info);
+
+ data->trans_info->send_seq_num = data->send_seq_num-2;
+ data->trans_info->mid = mid;
+ data->trans_info->reply_seq_num = data->send_seq_num-1;
+
+ DEBUG(10,("cli_signing_trans_start: storing mid = %u, reply_seq_num = %u, send_seq_num = %u \
+data->send_seq_num = %u\n",
+ (unsigned int)data->trans_info->mid,
+ (unsigned int)data->trans_info->reply_seq_num,
+ (unsigned int)data->trans_info->send_seq_num,
+ (unsigned int)data->send_seq_num ));
+}
+
+/***********************************************************
+ Tell client code we are out of a multiple trans reply state.
+************************************************************/
+
+void cli_signing_trans_stop(struct cli_state *cli)
+{
+ struct smb_basic_signing_context *data = cli->sign_info.signing_context;
+
+ if (!cli->sign_info.doing_signing || !data)
+ return;
+
+ DEBUG(10,("cli_signing_trans_stop: freeing mid = %u, reply_seq_num = %u, send_seq_num = %u \
+data->send_seq_num = %u\n",
+ (unsigned int)data->trans_info->mid,
+ (unsigned int)data->trans_info->reply_seq_num,
+ (unsigned int)data->trans_info->send_seq_num,
+ (unsigned int)data->send_seq_num ));
+
+ SAFE_FREE(data->trans_info);
+ data->trans_info = NULL;
+}
+
+/***********************************************************
+ SMB signing - TEMP implementation - calculate a MAC to send.
+************************************************************/
+
+static void temp_sign_outgoing_message(char *outbuf, struct smb_sign_info *si)
+{
+ /* mark the packet as signed - BEFORE we sign it...*/
+ mark_packet_signed(outbuf);
+
+ /* I wonder what BSRSPYL stands for - but this is what MS
+ actually sends! */
+ memcpy(&outbuf[smb_ss_field], "BSRSPYL ", 8);
+ return;
+}
+
+/***********************************************************
+ SMB signing - TEMP implementation - check a MAC sent by server.
+************************************************************/
+
+static BOOL temp_check_incoming_message(char *inbuf, struct smb_sign_info *si, BOOL expected_ok)
+{
+ return True;
+}
+
+/***********************************************************
+ SMB signing - TEMP implementation - free signing context
+************************************************************/
+
+static void temp_free_signing_context(struct smb_sign_info *si)
+{
+ return;
+}
+
+/***********************************************************
+ SMB signing - NULL implementation - setup the MAC key.
+************************************************************/
+
+BOOL cli_null_set_signing(struct cli_state *cli)
+{
+ return null_set_signing(&cli->sign_info);
+}
+
+/***********************************************************
+ SMB signing - temp implementation - setup the MAC key.
+************************************************************/
+
+BOOL cli_temp_set_signing(struct cli_state *cli)
+{
+ if (!cli_set_smb_signing_common(cli)) {
+ return False;
+ }
+
+ cli->sign_info.signing_context = NULL;
+
+ cli->sign_info.sign_outgoing_message = temp_sign_outgoing_message;
+ cli->sign_info.check_incoming_message = temp_check_incoming_message;
+ cli->sign_info.free_signing_context = temp_free_signing_context;
+
+ return True;
+}
+
+void cli_free_signing_context(struct cli_state *cli)
+{
+ free_signing_context(&cli->sign_info);
+}
+
+/**
+ * Sign a packet with the current mechanism
+ */
+
+void cli_calculate_sign_mac(struct cli_state *cli)
+{
+ cli->sign_info.sign_outgoing_message(cli->outbuf, &cli->sign_info);
+}
+
+/**
+ * Check a packet with the current mechanism
+ * @return False if we had an established signing connection
+ * which had a bad checksum, True otherwise.
+ */
+
+BOOL cli_check_sign_mac(struct cli_state *cli, BOOL expected_ok)
+{
+ if (!cli->sign_info.check_incoming_message(cli->inbuf, &cli->sign_info, expected_ok)) {
+ free_signing_context(&cli->sign_info);
+ return False;
+ }
+ return True;
+}
+
+/***********************************************************
+ SMB signing - Server implementation - send the MAC.
+************************************************************/
+
+static void srv_sign_outgoing_message(char *outbuf, struct smb_sign_info *si)
+{
+ unsigned char calc_md5_mac[16];
+ struct smb_basic_signing_context *data = si->signing_context;
+ uint32 send_seq_number = data->send_seq_num;
+ BOOL was_deferred_packet = False;
+ uint16 mid;
+
+ if (!si->doing_signing) {
+ return;
+ }
+
+ /* JRA Paranioa test - we should be able to get rid of this... */
+ if (smb_len(outbuf) < (smb_ss_field + 8 - 4)) {
+ DEBUG(1, ("srv_sign_outgoing_message: Logic error. Can't send signature on short packet! smb_len = %u\n",
+ smb_len(outbuf) ));
+ abort();
+ }
+
+ /* mark the packet as signed - BEFORE we sign it...*/
+ mark_packet_signed(outbuf);
+
+ mid = SVAL(outbuf, smb_mid);
+
+ /* See if this is a reply for a deferred packet. */
+ was_deferred_packet = get_sequence_for_reply(&data->outstanding_packet_list, mid, &send_seq_number);
+
+ if (data->trans_info && (data->trans_info->mid == mid)) {
+ /* This is a reply in a trans stream. Use the sequence
+ * number associated with the stream mid. */
+ send_seq_number = data->trans_info->send_seq_num;
+ }
+
+ simple_packet_signature(data, (const unsigned char *)outbuf, send_seq_number, calc_md5_mac);
+
+ DEBUG(10, ("srv_sign_outgoing_message: seq %u: sent SMB signature of\n", (unsigned int)send_seq_number));
+ dump_data(10, (const char *)calc_md5_mac, 8);
+
+ memcpy(&outbuf[smb_ss_field], calc_md5_mac, 8);
+
+/* cli->outbuf[smb_ss_field+2]=0;
+ Uncomment this to test if the remote client actually verifies signatures...*/
+
+ /* Don't mess with the sequence number for a deferred packet. */
+ if (was_deferred_packet) {
+ return;
+ }
+
+ if (!data->trans_info) {
+ /* Always increment if not in a trans stream. */
+ data->send_seq_num++;
+ } else if ((data->trans_info->send_seq_num == data->send_seq_num) || (data->trans_info->mid != mid)) {
+ /* Increment if this is the first reply in a trans stream or a
+ * packet that doesn't belong to this stream (different mid). */
+ data->send_seq_num++;
+ }
+}
+
+/***********************************************************
+ Is an incoming packet an oplock break reply ?
+************************************************************/
+
+static BOOL is_oplock_break(char *inbuf)
+{
+ if (CVAL(inbuf,smb_com) != SMBlockingX)
+ return False;
+
+ if (!(CVAL(inbuf,smb_vwv3) & LOCKING_ANDX_OPLOCK_RELEASE))
+ return False;
+
+ DEBUG(10,("is_oplock_break: Packet is oplock break\n"));
+ return True;
+}
+
+/***********************************************************
+ SMB signing - Server implementation - check a MAC sent by server.
+************************************************************/
+
+static BOOL srv_check_incoming_message(char *inbuf, struct smb_sign_info *si, BOOL expected_ok)
+{
+ BOOL good;
+ struct smb_basic_signing_context *data = si->signing_context;
+ uint32 reply_seq_number = data->send_seq_num;
+ uint32 saved_seq;
+ unsigned char calc_md5_mac[16];
+ unsigned char *server_sent_mac;
+ uint mid;
+
+ if (!si->doing_signing)
+ return True;
+
+ if (smb_len(inbuf) < (smb_ss_field + 8 - 4)) {
+ DEBUG(1, ("srv_check_incoming_message: Can't check signature on short packet! smb_len = %u\n", smb_len(inbuf)));
+ return False;
+ }
+
+ mid = SVAL(inbuf, smb_mid);
+
+ /* Is this part of a trans stream ? */
+ if (data->trans_info && (data->trans_info->mid == mid)) {
+ /* If so we don't increment the sequence. */
+ reply_seq_number = data->trans_info->reply_seq_num;
+ } else {
+ /* We always increment the sequence number. */
+ data->send_seq_num++;
+
+ /* If we get an asynchronous oplock break reply and there
+ * isn't a reply pending we need to re-sync the sequence
+ * number.
+ */
+ if (is_oplock_break(inbuf)) {
+ DEBUG(10,("srv_check_incoming_message: oplock break at seq num %u\n", data->send_seq_num));
+ data->send_seq_num++;
+ }
+ }
+
+ saved_seq = reply_seq_number;
+ simple_packet_signature(data, (const unsigned char *)inbuf, reply_seq_number, calc_md5_mac);
+
+ server_sent_mac = (unsigned char *)&inbuf[smb_ss_field];
+ good = (memcmp(server_sent_mac, calc_md5_mac, 8) == 0);
+
+ if (!good) {
+
+ DEBUG(5, ("srv_check_incoming_message: BAD SIG: seq %u wanted SMB signature of\n",
+ (unsigned int)saved_seq));
+ dump_data(5, (const char *)calc_md5_mac, 8);
+
+ DEBUG(5, ("srv_check_incoming_message: BAD SIG: seq %u got SMB signature of\n",
+ (unsigned int)saved_seq));
+ dump_data(5, (const char *)server_sent_mac, 8);
+
+#if 1 /* JRATEST */
+ {
+ int i;
+ reply_seq_number -= 5;
+ for (i = 0; i < 10; i++, reply_seq_number++) {
+ simple_packet_signature(data, (const unsigned char *)inbuf, reply_seq_number, calc_md5_mac);
+ if (memcmp(server_sent_mac, calc_md5_mac, 8) == 0) {
+ DEBUG(0,("srv_check_incoming_message: out of seq. seq num %u matches. \
+We were expecting seq %u\n", reply_seq_number, saved_seq ));
+ break;
+ }
+ }
+ }
+#endif /* JRATEST */
+
+ } else {
+ DEBUG(10, ("srv_check_incoming_message: seq %u: (current is %u) got good SMB signature of\n", (unsigned int)reply_seq_number, (unsigned int)data->send_seq_num));
+ dump_data(10, (const char *)server_sent_mac, 8);
+ }
+
+ return (signing_good(inbuf, si, good, saved_seq, expected_ok));
+}
+
+/***********************************************************
+ SMB signing - server API's.
+************************************************************/
+
+static struct smb_sign_info srv_sign_info = {
+ null_sign_outgoing_message,
+ null_check_incoming_message,
+ null_free_signing_context,
+ NULL,
+ False,
+ False,
+ False,
+ False
+};
+
+/***********************************************************
+ Turn signing off or on for oplock break code.
+************************************************************/
+
+BOOL srv_oplock_set_signing(BOOL onoff)
+{
+ BOOL ret = srv_sign_info.doing_signing;
+ srv_sign_info.doing_signing = onoff;
+ return ret;
+}
+
+/***********************************************************
+ Called to validate an incoming packet from the client.
+************************************************************/
+
+BOOL srv_check_sign_mac(char *inbuf, BOOL expected_ok)
+{
+ /* Check if it's a session keepalive. */
+ if(CVAL(inbuf,0) == SMBkeepalive)
+ return True;
+
+ return srv_sign_info.check_incoming_message(inbuf, &srv_sign_info, expected_ok);
+}
+
+/***********************************************************
+ Called to sign an outgoing packet to the client.
+************************************************************/
+
+void srv_calculate_sign_mac(char *outbuf)
+{
+ /* Check if it's a session keepalive. */
+ /* JRA Paranioa test - do we ever generate these in the server ? */
+ if(CVAL(outbuf,0) == SMBkeepalive)
+ return;
+
+ srv_sign_info.sign_outgoing_message(outbuf, &srv_sign_info);
+}
+
+/***********************************************************
+ Called by server to defer an outgoing packet.
+************************************************************/
+
+void srv_defer_sign_response(uint16 mid)
+{
+ struct smb_basic_signing_context *data;
+
+ if (!srv_sign_info.doing_signing)
+ return;
+
+ data = (struct smb_basic_signing_context *)srv_sign_info.signing_context;
+
+ if (!data)
+ return;
+
+ store_sequence_for_reply(&data->outstanding_packet_list,
+ mid, data->send_seq_num);
+ data->send_seq_num++;
+}
+
+/***********************************************************
+ Called to remove sequence records when a deferred packet is
+ cancelled by mid. This should never find one....
+************************************************************/
+
+void srv_cancel_sign_response(uint16 mid)
+{
+ struct smb_basic_signing_context *data;
+ uint32 dummy_seq;
+
+ if (!srv_sign_info.doing_signing)
+ return;
+
+ data = (struct smb_basic_signing_context *)srv_sign_info.signing_context;
+
+ if (!data)
+ return;
+
+ DEBUG(10,("srv_cancel_sign_response: for mid %u\n", (unsigned int)mid ));
+
+ while (get_sequence_for_reply(&data->outstanding_packet_list, mid, &dummy_seq))
+ ;
+}
+
+/***********************************************************
+ Called by server negprot when signing has been negotiated.
+************************************************************/
+
+void srv_set_signing_negotiated(void)
+{
+ srv_sign_info.allow_smb_signing = True;
+ srv_sign_info.negotiated_smb_signing = True;
+ if (lp_server_signing() == Required)
+ srv_sign_info.mandatory_signing = True;
+
+ srv_sign_info.sign_outgoing_message = temp_sign_outgoing_message;
+ srv_sign_info.check_incoming_message = temp_check_incoming_message;
+ srv_sign_info.free_signing_context = temp_free_signing_context;
+}
+
+/***********************************************************
+ Returns whether signing is active. We can't use sendfile or raw
+ reads/writes if it is.
+************************************************************/
+
+BOOL srv_is_signing_active(void)
+{
+ return srv_sign_info.doing_signing;
+}
+
+
+/***********************************************************
+ Returns whether signing is negotiated. We can't use it unless it was
+ in the negprot.
+************************************************************/
+
+BOOL srv_is_signing_negotiated(void)
+{
+ return srv_sign_info.negotiated_smb_signing;
+}
+
+/***********************************************************
+ Returns whether signing is negotiated. We can't use it unless it was
+ in the negprot.
+************************************************************/
+
+BOOL srv_signing_started(void)
+{
+ struct smb_basic_signing_context *data;
+
+ if (!srv_sign_info.doing_signing) {
+ return False;
+ }
+
+ data = (struct smb_basic_signing_context *)srv_sign_info.signing_context;
+ if (!data)
+ return False;
+
+ if (data->send_seq_num == 0) {
+ return False;
+ }
+
+ return True;
+}
+
+
+/***********************************************************
+ Tell server code we are in a multiple trans reply state.
+************************************************************/
+
+void srv_signing_trans_start(uint16 mid)
+{
+ struct smb_basic_signing_context *data;
+
+ if (!srv_sign_info.doing_signing)
+ return;
+
+ data = (struct smb_basic_signing_context *)srv_sign_info.signing_context;
+ if (!data)
+ return;
+
+ data->trans_info = smb_xmalloc(sizeof(struct trans_info_context));
+ ZERO_STRUCTP(data->trans_info);
+
+ data->trans_info->reply_seq_num = data->send_seq_num-1;
+ data->trans_info->mid = mid;
+ data->trans_info->send_seq_num = data->send_seq_num;
+
+ DEBUG(10,("srv_signing_trans_start: storing mid = %u, reply_seq_num = %u, send_seq_num = %u \
+data->send_seq_num = %u\n",
+ (unsigned int)mid,
+ (unsigned int)data->trans_info->reply_seq_num,
+ (unsigned int)data->trans_info->send_seq_num,
+ (unsigned int)data->send_seq_num ));
+}
+
+/***********************************************************
+ Tell server code we are out of a multiple trans reply state.
+************************************************************/
+
+void srv_signing_trans_stop(void)
+{
+ struct smb_basic_signing_context *data;
+
+ if (!srv_sign_info.doing_signing)
+ return;
+
+ data = (struct smb_basic_signing_context *)srv_sign_info.signing_context;
+ if (!data || !data->trans_info)
+ return;
+
+ DEBUG(10,("srv_signing_trans_stop: removing mid = %u, reply_seq_num = %u, send_seq_num = %u \
+data->send_seq_num = %u\n",
+ (unsigned int)data->trans_info->mid,
+ (unsigned int)data->trans_info->reply_seq_num,
+ (unsigned int)data->trans_info->send_seq_num,
+ (unsigned int)data->send_seq_num ));
+
+ SAFE_FREE(data->trans_info);
+ data->trans_info = NULL;
+}
+
+/***********************************************************
+ Turn on signing from this packet onwards.
+************************************************************/
+
+void srv_set_signing(const DATA_BLOB user_session_key, const DATA_BLOB response)
+{
+ struct smb_basic_signing_context *data;
+
+ if (!user_session_key.length)
+ return;
+
+ if (!srv_sign_info.negotiated_smb_signing && !srv_sign_info.mandatory_signing) {
+ DEBUG(5,("srv_set_signing: signing negotiated = %u, mandatory_signing = %u. Not allowing smb signing.\n",
+ (unsigned int)srv_sign_info.negotiated_smb_signing,
+ (unsigned int)srv_sign_info.mandatory_signing ));
+ return;
+ }
+
+ /* Once we've turned on, ignore any more sessionsetups. */
+ if (srv_sign_info.doing_signing) {
+ return;
+ }
+
+ if (srv_sign_info.free_signing_context)
+ srv_sign_info.free_signing_context(&srv_sign_info);
+
+ srv_sign_info.doing_signing = True;
+
+ data = smb_xmalloc(sizeof(*data));
+ memset(data, '\0', sizeof(*data));
+
+ srv_sign_info.signing_context = data;
+
+ data->mac_key = data_blob(NULL, response.length + user_session_key.length);
+
+ memcpy(&data->mac_key.data[0], user_session_key.data, user_session_key.length);
+ if (response.length)
+ memcpy(&data->mac_key.data[user_session_key.length],response.data, response.length);
+
+ dump_data_pw("MAC ssession key is:\n", data->mac_key.data, data->mac_key.length);
+
+ DEBUG(3,("srv_set_signing: turning on SMB signing: signing negotiated = %s, mandatory_signing = %s.\n",
+ BOOLSTR(srv_sign_info.negotiated_smb_signing),
+ BOOLSTR(srv_sign_info.mandatory_signing) ));
+
+ /* Initialise the sequence number */
+ data->send_seq_num = 0;
+
+ /* Initialise the list of outstanding packets */
+ data->outstanding_packet_list = NULL;
+
+ srv_sign_info.sign_outgoing_message = srv_sign_outgoing_message;
+ srv_sign_info.check_incoming_message = srv_check_incoming_message;
+ srv_sign_info.free_signing_context = simple_free_signing_context;
+}
diff --git a/source/libsmb/smbdes.c b/source/libsmb/smbdes.c
new file mode 100644
index 00000000000..ae946b4a660
--- /dev/null
+++ b/source/libsmb/smbdes.c
@@ -0,0 +1,455 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ a partial implementation of DES designed for use in the
+ SMB authentication protocol
+
+ Copyright (C) Andrew Tridgell 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* NOTES:
+
+ This code makes no attempt to be fast! In fact, it is a very
+ slow implementation
+
+ This code is NOT a complete DES implementation. It implements only
+ the minimum necessary for SMB authentication, as used by all SMB
+ products (including every copy of Microsoft Windows95 ever sold)
+
+ In particular, it can only do a unchained forward DES pass. This
+ means it is not possible to use this code for encryption/decryption
+ of data, instead it is only useful as a "hash" algorithm.
+
+ There is no entry point into this code that allows normal DES operation.
+
+ I believe this means that this code does not come under ITAR
+ regulations but this is NOT a legal opinion. If you are concerned
+ about the applicability of ITAR regulations to this code then you
+ should confirm it for yourself (and maybe let me know if you come
+ up with a different answer to the one above)
+*/
+
+
+#define uchar unsigned char
+
+static const uchar perm1[56] = {57, 49, 41, 33, 25, 17, 9,
+ 1, 58, 50, 42, 34, 26, 18,
+ 10, 2, 59, 51, 43, 35, 27,
+ 19, 11, 3, 60, 52, 44, 36,
+ 63, 55, 47, 39, 31, 23, 15,
+ 7, 62, 54, 46, 38, 30, 22,
+ 14, 6, 61, 53, 45, 37, 29,
+ 21, 13, 5, 28, 20, 12, 4};
+
+static const uchar perm2[48] = {14, 17, 11, 24, 1, 5,
+ 3, 28, 15, 6, 21, 10,
+ 23, 19, 12, 4, 26, 8,
+ 16, 7, 27, 20, 13, 2,
+ 41, 52, 31, 37, 47, 55,
+ 30, 40, 51, 45, 33, 48,
+ 44, 49, 39, 56, 34, 53,
+ 46, 42, 50, 36, 29, 32};
+
+static const uchar perm3[64] = {58, 50, 42, 34, 26, 18, 10, 2,
+ 60, 52, 44, 36, 28, 20, 12, 4,
+ 62, 54, 46, 38, 30, 22, 14, 6,
+ 64, 56, 48, 40, 32, 24, 16, 8,
+ 57, 49, 41, 33, 25, 17, 9, 1,
+ 59, 51, 43, 35, 27, 19, 11, 3,
+ 61, 53, 45, 37, 29, 21, 13, 5,
+ 63, 55, 47, 39, 31, 23, 15, 7};
+
+static const uchar perm4[48] = { 32, 1, 2, 3, 4, 5,
+ 4, 5, 6, 7, 8, 9,
+ 8, 9, 10, 11, 12, 13,
+ 12, 13, 14, 15, 16, 17,
+ 16, 17, 18, 19, 20, 21,
+ 20, 21, 22, 23, 24, 25,
+ 24, 25, 26, 27, 28, 29,
+ 28, 29, 30, 31, 32, 1};
+
+static const uchar perm5[32] = { 16, 7, 20, 21,
+ 29, 12, 28, 17,
+ 1, 15, 23, 26,
+ 5, 18, 31, 10,
+ 2, 8, 24, 14,
+ 32, 27, 3, 9,
+ 19, 13, 30, 6,
+ 22, 11, 4, 25};
+
+
+static const uchar perm6[64] ={ 40, 8, 48, 16, 56, 24, 64, 32,
+ 39, 7, 47, 15, 55, 23, 63, 31,
+ 38, 6, 46, 14, 54, 22, 62, 30,
+ 37, 5, 45, 13, 53, 21, 61, 29,
+ 36, 4, 44, 12, 52, 20, 60, 28,
+ 35, 3, 43, 11, 51, 19, 59, 27,
+ 34, 2, 42, 10, 50, 18, 58, 26,
+ 33, 1, 41, 9, 49, 17, 57, 25};
+
+
+static const uchar sc[16] = {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1};
+
+static const uchar sbox[8][4][16] = {
+ {{14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7},
+ {0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8},
+ {4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0},
+ {15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13}},
+
+ {{15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10},
+ {3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5},
+ {0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15},
+ {13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9}},
+
+ {{10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8},
+ {13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1},
+ {13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7},
+ {1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12}},
+
+ {{7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15},
+ {13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9},
+ {10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4},
+ {3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14}},
+
+ {{2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9},
+ {14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6},
+ {4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14},
+ {11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3}},
+
+ {{12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11},
+ {10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8},
+ {9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6},
+ {4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13}},
+
+ {{4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1},
+ {13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6},
+ {1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2},
+ {6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12}},
+
+ {{13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7},
+ {1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2},
+ {7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8},
+ {2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11}}};
+
+static void permute(char *out, const char *in, const uchar *p, int n)
+{
+ int i;
+ for (i=0;i<n;i++)
+ out[i] = in[p[i]-1];
+}
+
+static void lshift(char *d, int count, int n)
+{
+ char out[64];
+ int i;
+ for (i=0;i<n;i++)
+ out[i] = d[(i+count)%n];
+ for (i=0;i<n;i++)
+ d[i] = out[i];
+}
+
+static void concat(char *out, char *in1, char *in2, int l1, int l2)
+{
+ while (l1--)
+ *out++ = *in1++;
+ while (l2--)
+ *out++ = *in2++;
+}
+
+static void xor(char *out, char *in1, char *in2, int n)
+{
+ int i;
+ for (i=0;i<n;i++)
+ out[i] = in1[i] ^ in2[i];
+}
+
+static void dohash(char *out, char *in, char *key, int forw)
+{
+ int i, j, k;
+ char pk1[56];
+ char c[28];
+ char d[28];
+ char cd[56];
+ char ki[16][48];
+ char pd1[64];
+ char l[32], r[32];
+ char rl[64];
+
+ permute(pk1, key, perm1, 56);
+
+ for (i=0;i<28;i++)
+ c[i] = pk1[i];
+ for (i=0;i<28;i++)
+ d[i] = pk1[i+28];
+
+ for (i=0;i<16;i++) {
+ lshift(c, sc[i], 28);
+ lshift(d, sc[i], 28);
+
+ concat(cd, c, d, 28, 28);
+ permute(ki[i], cd, perm2, 48);
+ }
+
+ permute(pd1, in, perm3, 64);
+
+ for (j=0;j<32;j++) {
+ l[j] = pd1[j];
+ r[j] = pd1[j+32];
+ }
+
+ for (i=0;i<16;i++) {
+ char er[48];
+ char erk[48];
+ char b[8][6];
+ char cb[32];
+ char pcb[32];
+ char r2[32];
+
+ permute(er, r, perm4, 48);
+
+ xor(erk, er, ki[forw ? i : 15 - i], 48);
+
+ for (j=0;j<8;j++)
+ for (k=0;k<6;k++)
+ b[j][k] = erk[j*6 + k];
+
+ for (j=0;j<8;j++) {
+ int m, n;
+ m = (b[j][0]<<1) | b[j][5];
+
+ n = (b[j][1]<<3) | (b[j][2]<<2) | (b[j][3]<<1) | b[j][4];
+
+ for (k=0;k<4;k++)
+ b[j][k] = (sbox[j][m][n] & (1<<(3-k)))?1:0;
+ }
+
+ for (j=0;j<8;j++)
+ for (k=0;k<4;k++)
+ cb[j*4+k] = b[j][k];
+ permute(pcb, cb, perm5, 32);
+
+ xor(r2, l, pcb, 32);
+
+ for (j=0;j<32;j++)
+ l[j] = r[j];
+
+ for (j=0;j<32;j++)
+ r[j] = r2[j];
+ }
+
+ concat(rl, r, l, 32, 32);
+
+ permute(out, rl, perm6, 64);
+}
+
+static void str_to_key(const unsigned char *str,unsigned char *key)
+{
+ int i;
+
+ key[0] = str[0]>>1;
+ key[1] = ((str[0]&0x01)<<6) | (str[1]>>2);
+ key[2] = ((str[1]&0x03)<<5) | (str[2]>>3);
+ key[3] = ((str[2]&0x07)<<4) | (str[3]>>4);
+ key[4] = ((str[3]&0x0F)<<3) | (str[4]>>5);
+ key[5] = ((str[4]&0x1F)<<2) | (str[5]>>6);
+ key[6] = ((str[5]&0x3F)<<1) | (str[6]>>7);
+ key[7] = str[6]&0x7F;
+ for (i=0;i<8;i++) {
+ key[i] = (key[i]<<1);
+ }
+}
+
+
+static void smbhash(unsigned char *out, const unsigned char *in, const unsigned char *key, int forw)
+{
+ int i;
+ char outb[64];
+ char inb[64];
+ char keyb[64];
+ unsigned char key2[8];
+
+ str_to_key(key, key2);
+
+ for (i=0;i<64;i++) {
+ inb[i] = (in[i/8] & (1<<(7-(i%8)))) ? 1 : 0;
+ keyb[i] = (key2[i/8] & (1<<(7-(i%8)))) ? 1 : 0;
+ outb[i] = 0;
+ }
+
+ dohash(outb, inb, keyb, forw);
+
+ for (i=0;i<8;i++) {
+ out[i] = 0;
+ }
+
+ for (i=0;i<64;i++) {
+ if (outb[i])
+ out[i/8] |= (1<<(7-(i%8)));
+ }
+}
+
+void E_P16(const unsigned char *p14,unsigned char *p16)
+{
+ unsigned char sp8[8] = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25};
+ smbhash(p16, sp8, p14, 1);
+ smbhash(p16+8, sp8, p14+7, 1);
+}
+
+void E_P24(const unsigned char *p21, const unsigned char *c8, unsigned char *p24)
+{
+ smbhash(p24, c8, p21, 1);
+ smbhash(p24+8, c8, p21+7, 1);
+ smbhash(p24+16, c8, p21+14, 1);
+}
+
+void D_P16(const unsigned char *p14, const unsigned char *in, unsigned char *out)
+{
+ smbhash(out, in, p14, 0);
+ smbhash(out+8, in+8, p14+7, 0);
+}
+
+void E_old_pw_hash( unsigned char *p14, const unsigned char *in, unsigned char *out)
+{
+ smbhash(out, in, p14, 1);
+ smbhash(out+8, in+8, p14+7, 1);
+}
+
+void cred_hash1(unsigned char *out, const unsigned char *in, const unsigned char *key)
+{
+ unsigned char buf[8];
+
+ smbhash(buf, in, key, 1);
+ smbhash(out, buf, key+9, 1);
+}
+
+void cred_hash2(unsigned char *out, const unsigned char *in, const unsigned char *key)
+{
+ unsigned char buf[8];
+ static unsigned char key2[8];
+
+ smbhash(buf, in, key, 1);
+ key2[0] = key[7];
+ smbhash(out, buf, key2, 1);
+}
+
+void cred_hash3(unsigned char *out, unsigned char *in, const unsigned char *key, int forw)
+{
+ static unsigned char key2[8];
+
+ smbhash(out, in, key, forw);
+ key2[0] = key[7];
+ smbhash(out + 8, in + 8, key2, forw);
+}
+
+void SamOEMhash( unsigned char *data, const unsigned char *key, int val)
+{
+ unsigned char s_box[256];
+ unsigned char index_i = 0;
+ unsigned char index_j = 0;
+ unsigned char j = 0;
+ int ind;
+
+ for (ind = 0; ind < 256; ind++)
+ {
+ s_box[ind] = (unsigned char)ind;
+ }
+
+ for( ind = 0; ind < 256; ind++)
+ {
+ unsigned char tc;
+
+ j += (s_box[ind] + key[ind%16]);
+
+ tc = s_box[ind];
+ s_box[ind] = s_box[j];
+ s_box[j] = tc;
+ }
+ for( ind = 0; ind < val; ind++)
+ {
+ unsigned char tc;
+ unsigned char t;
+
+ index_i++;
+ index_j += s_box[index_i];
+
+ tc = s_box[index_i];
+ s_box[index_i] = s_box[index_j];
+ s_box[index_j] = tc;
+
+ t = s_box[index_i] + s_box[index_j];
+ data[ind] = data[ind] ^ s_box[t];
+ }
+}
+
+void SamOEMhashBlob( unsigned char *data, int len, DATA_BLOB *key)
+{
+ unsigned char s_box[256];
+ unsigned char index_i = 0;
+ unsigned char index_j = 0;
+ unsigned char j = 0;
+ int ind;
+
+ for (ind = 0; ind < 256; ind++)
+ {
+ s_box[ind] = (unsigned char)ind;
+ }
+
+ for( ind = 0; ind < 256; ind++)
+ {
+ unsigned char tc;
+
+ j += (s_box[ind] + key->data[ind%key->length]);
+
+ tc = s_box[ind];
+ s_box[ind] = s_box[j];
+ s_box[j] = tc;
+ }
+ for( ind = 0; ind < len; ind++)
+ {
+ unsigned char tc;
+ unsigned char t;
+
+ index_i++;
+ index_j += s_box[index_i];
+
+ tc = s_box[index_i];
+ s_box[index_i] = s_box[index_j];
+ s_box[index_j] = tc;
+
+ t = s_box[index_i] + s_box[index_j];
+ data[ind] = data[ind] ^ s_box[t];
+ }
+}
+
+/* Decode a sam password hash into a password. The password hash is the
+ same method used to store passwords in the NT registry. The DES key
+ used is based on the RID of the user. */
+
+void sam_pwd_hash(unsigned int rid, const uchar *in, uchar *out, int forw)
+{
+ uchar s[14];
+
+ s[0] = s[4] = s[8] = s[12] = (uchar)(rid & 0xFF);
+ s[1] = s[5] = s[9] = s[13] = (uchar)((rid >> 8) & 0xFF);
+ s[2] = s[6] = s[10] = (uchar)((rid >> 16) & 0xFF);
+ s[3] = s[7] = s[11] = (uchar)((rid >> 24) & 0xFF);
+
+ smbhash(out, in, s, forw);
+ smbhash(out+8, in+8, s+7, forw);
+}
diff --git a/source/libsmb/smbencrypt.c b/source/libsmb/smbencrypt.c
new file mode 100644
index 00000000000..c5acedae51c
--- /dev/null
+++ b/source/libsmb/smbencrypt.c
@@ -0,0 +1,526 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-1998
+ Modified by Jeremy Allison 1995.
+ Copyright (C) Jeremy Allison 1995-2000.
+ Copyright (C) Luke Kennethc Casson Leighton 1996-2000.
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2002-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "byteorder.h"
+
+/*
+ This implements the X/Open SMB password encryption
+ It takes a password ('unix' string), a 8 byte "crypt key"
+ and puts 24 bytes of encrypted password into p24 */
+void SMBencrypt(const char *passwd, const uchar *c8, uchar p24[24])
+{
+ uchar p21[21];
+
+ memset(p21,'\0',21);
+ E_deshash(passwd, p21);
+
+ SMBOWFencrypt(p21, c8, p24);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("SMBencrypt: lm#, challenge, response\n"));
+ dump_data(100, (char *)p21, 16);
+ dump_data(100, (const char *)c8, 8);
+ dump_data(100, (char *)p24, 24);
+#endif
+}
+
+/**
+ * Creates the MD4 Hash of the users password in NT UNICODE.
+ * @param passwd password in 'unix' charset.
+ * @param p16 return password hashed with md4, caller allocated 16 byte buffer
+ */
+
+void E_md4hash(const char *passwd, uchar p16[16])
+{
+ int len;
+ smb_ucs2_t wpwd[129];
+
+ /* Password must be converted to NT unicode - null terminated. */
+ push_ucs2(NULL, wpwd, (const char *)passwd, 256, STR_UNICODE|STR_NOALIGN|STR_TERMINATE);
+ /* Calculate length in bytes */
+ len = strlen_w(wpwd) * sizeof(int16);
+
+ mdfour(p16, (unsigned char *)wpwd, len);
+ ZERO_STRUCT(wpwd);
+}
+
+/**
+ * Creates the DES forward-only Hash of the users password in DOS ASCII charset
+ * @param passwd password in 'unix' charset.
+ * @param p16 return password hashed with DES, caller allocated 16 byte buffer
+ * @return False if password was > 14 characters, and therefore may be incorrect, otherwise True
+ * @note p16 is filled in regardless
+ */
+
+BOOL E_deshash(const char *passwd, uchar p16[16])
+{
+ BOOL ret = True;
+ fstring dospwd;
+ ZERO_STRUCT(dospwd);
+
+ /* Password must be converted to DOS charset - null terminated, uppercase. */
+ push_ascii(dospwd, passwd, sizeof(dospwd), STR_UPPER|STR_TERMINATE);
+
+ /* Only the fisrt 14 chars are considered, password need not be null terminated. */
+ E_P16((const unsigned char *)dospwd, p16);
+
+ if (strlen(dospwd) > 14) {
+ ret = False;
+ }
+
+ ZERO_STRUCT(dospwd);
+
+ return ret;
+}
+
+/**
+ * Creates the MD4 and DES (LM) Hash of the users password.
+ * MD4 is of the NT Unicode, DES is of the DOS UPPERCASE password.
+ * @param passwd password in 'unix' charset.
+ * @param nt_p16 return password hashed with md4, caller allocated 16 byte buffer
+ * @param p16 return password hashed with des, caller allocated 16 byte buffer
+ */
+
+/* Does both the NT and LM owfs of a user's password */
+void nt_lm_owf_gen(const char *pwd, uchar nt_p16[16], uchar p16[16])
+{
+ /* Calculate the MD4 hash (NT compatible) of the password */
+ memset(nt_p16, '\0', 16);
+ E_md4hash(pwd, nt_p16);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("nt_lm_owf_gen: pwd, nt#\n"));
+ dump_data(120, pwd, strlen(pwd));
+ dump_data(100, (char *)nt_p16, 16);
+#endif
+
+ E_deshash(pwd, (uchar *)p16);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("nt_lm_owf_gen: pwd, lm#\n"));
+ dump_data(120, pwd, strlen(pwd));
+ dump_data(100, (char *)p16, 16);
+#endif
+}
+
+/* Does both the NTLMv2 owfs of a user's password */
+BOOL ntv2_owf_gen(const uchar owf[16],
+ const char *user_in, const char *domain_in, uchar kr_buf[16])
+{
+ smb_ucs2_t *user;
+ smb_ucs2_t *domain;
+
+ size_t user_byte_len;
+ size_t domain_byte_len;
+
+ HMACMD5Context ctx;
+
+ user_byte_len = push_ucs2_allocate(&user, user_in);
+ if (user_byte_len == (size_t)-1) {
+ DEBUG(0, ("push_uss2_allocate() for user returned -1 (probably malloc() failure)\n"));
+ return False;
+ }
+
+ domain_byte_len = push_ucs2_allocate(&domain, domain_in);
+ if (domain_byte_len == (size_t)-1) {
+ DEBUG(0, ("push_uss2_allocate() for domain returned -1 (probably malloc() failure)\n"));
+ return False;
+ }
+
+ strupper_w(user);
+ strupper_w(domain);
+
+ SMB_ASSERT(user_byte_len >= 2);
+ SMB_ASSERT(domain_byte_len >= 2);
+
+ /* We don't want null termination */
+ user_byte_len = user_byte_len - 2;
+ domain_byte_len = domain_byte_len - 2;
+
+ hmac_md5_init_limK_to_64(owf, 16, &ctx);
+ hmac_md5_update((const unsigned char *)user, user_byte_len, &ctx);
+ hmac_md5_update((const unsigned char *)domain, domain_byte_len, &ctx);
+ hmac_md5_final(kr_buf, &ctx);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("ntv2_owf_gen: user, domain, owfkey, kr\n"));
+ dump_data(100, (const char *)user, user_byte_len);
+ dump_data(100, (const char *)domain, domain_byte_len);
+ dump_data(100, owf, 16);
+ dump_data(100, kr_buf, 16);
+#endif
+
+ SAFE_FREE(user);
+ SAFE_FREE(domain);
+ return True;
+}
+
+/* Does the des encryption from the NT or LM MD4 hash. */
+void SMBOWFencrypt(const uchar passwd[16], const uchar *c8, uchar p24[24])
+{
+ uchar p21[21];
+
+ ZERO_STRUCT(p21);
+
+ memcpy(p21, passwd, 16);
+ E_P24(p21, c8, p24);
+}
+
+/* Does the des encryption from the FIRST 8 BYTES of the NT or LM MD4 hash. */
+void NTLMSSPOWFencrypt(const uchar passwd[8], const uchar *ntlmchalresp, uchar p24[24])
+{
+ uchar p21[21];
+
+ memset(p21,'\0',21);
+ memcpy(p21, passwd, 8);
+ memset(p21 + 8, 0xbd, 8);
+
+ E_P24(p21, ntlmchalresp, p24);
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("NTLMSSPOWFencrypt: p21, c8, p24\n"));
+ dump_data(100, (char *)p21, 21);
+ dump_data(100, (const char *)ntlmchalresp, 8);
+ dump_data(100, (char *)p24, 24);
+#endif
+}
+
+
+/* Does the NT MD4 hash then des encryption. */
+
+void SMBNTencrypt(const char *passwd, uchar *c8, uchar *p24)
+{
+ uchar p21[21];
+
+ memset(p21,'\0',21);
+
+ E_md4hash(passwd, p21);
+ SMBOWFencrypt(p21, c8, p24);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("SMBNTencrypt: nt#, challenge, response\n"));
+ dump_data(100, (char *)p21, 16);
+ dump_data(100, (char *)c8, 8);
+ dump_data(100, (char *)p24, 24);
+#endif
+}
+
+/* Does the md5 encryption from the Key Response for NTLMv2. */
+void SMBOWFencrypt_ntv2(const uchar kr[16],
+ const DATA_BLOB *srv_chal,
+ const DATA_BLOB *cli_chal,
+ uchar resp_buf[16])
+{
+ HMACMD5Context ctx;
+
+ hmac_md5_init_limK_to_64(kr, 16, &ctx);
+ hmac_md5_update(srv_chal->data, srv_chal->length, &ctx);
+ hmac_md5_update(cli_chal->data, cli_chal->length, &ctx);
+ hmac_md5_final(resp_buf, &ctx);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("SMBOWFencrypt_ntv2: srv_chal, cli_chal, resp_buf\n"));
+ dump_data(100, srv_chal->data, srv_chal->length);
+ dump_data(100, cli_chal->data, cli_chal->length);
+ dump_data(100, resp_buf, 16);
+#endif
+}
+
+void SMBsesskeygen_ntv2(const uchar kr[16],
+ const uchar * nt_resp, uint8 sess_key[16])
+{
+ /* a very nice, 128 bit, variable session key */
+
+ HMACMD5Context ctx;
+
+ hmac_md5_init_limK_to_64(kr, 16, &ctx);
+ hmac_md5_update(nt_resp, 16, &ctx);
+ hmac_md5_final((unsigned char *)sess_key, &ctx);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("SMBsesskeygen_ntv2:\n"));
+ dump_data(100, sess_key, 16);
+#endif
+}
+
+void SMBsesskeygen_ntv1(const uchar kr[16],
+ const uchar * nt_resp, uint8 sess_key[16])
+{
+ /* yes, this session key does not change - yes, this
+ is a problem - but it is 128 bits */
+
+ mdfour((unsigned char *)sess_key, kr, 16);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("SMBsesskeygen_ntv1:\n"));
+ dump_data(100, sess_key, 16);
+#endif
+}
+
+void SMBsesskeygen_lmv1(const uchar lm_hash[16],
+ const uchar lm_resp[24], /* only uses 8 */
+ uint8 sess_key[16])
+{
+ /* Calculate the LM session key (effective length 40 bits,
+ but changes with each session) */
+
+ uchar p24[24];
+ uchar partial_lm_hash[16];
+
+ memcpy(partial_lm_hash, lm_hash, 8);
+ memset(partial_lm_hash + 8, 0xbd, 8);
+
+ SMBOWFencrypt(lm_hash, lm_resp, p24);
+
+ memcpy(sess_key, p24, 16);
+ sess_key[5] = 0xe5;
+ sess_key[6] = 0x38;
+ sess_key[7] = 0xb0;
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("SMBsesskeygen_lmv1:\n"));
+ dump_data(100, sess_key, 16);
+#endif
+}
+
+void SMBsesskeygen_lm_sess_key(const uchar lm_hash[16],
+ const uchar lm_resp[24], /* only uses 8 */
+ uint8 sess_key[16])
+{
+ uchar p24[24];
+ uchar partial_lm_hash[16];
+
+ memcpy(partial_lm_hash, lm_hash, 8);
+ memset(partial_lm_hash + 8, 0xbd, 8);
+
+ SMBOWFencrypt(partial_lm_hash, lm_resp, p24);
+
+ memcpy(sess_key, p24, 16);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("SMBsesskeygen_lmv1_jerry:\n"));
+ dump_data(100, sess_key, 16);
+#endif
+}
+
+DATA_BLOB NTLMv2_generate_names_blob(const char *hostname,
+ const char *domain)
+{
+ DATA_BLOB names_blob = data_blob(NULL, 0);
+
+ msrpc_gen(&names_blob, "aaa",
+ NTLMSSP_NAME_TYPE_DOMAIN, domain,
+ NTLMSSP_NAME_TYPE_SERVER, hostname,
+ 0, "");
+ return names_blob;
+}
+
+static DATA_BLOB NTLMv2_generate_client_data(const DATA_BLOB *names_blob)
+{
+ uchar client_chal[8];
+ DATA_BLOB response = data_blob(NULL, 0);
+ char long_date[8];
+
+ generate_random_buffer(client_chal, sizeof(client_chal), False);
+
+ put_long_date(long_date, time(NULL));
+
+ /* See http://www.ubiqx.org/cifs/SMB.html#SMB.8.5 */
+
+ msrpc_gen(&response, "ddbbdb",
+ 0x00000101, /* Header */
+ 0, /* 'Reserved' */
+ long_date, 8, /* Timestamp */
+ client_chal, 8, /* client challenge */
+ 0, /* Unknown */
+ names_blob->data, names_blob->length); /* End of name list */
+
+ return response;
+}
+
+static DATA_BLOB NTLMv2_generate_response(const uchar ntlm_v2_hash[16],
+ const DATA_BLOB *server_chal,
+ const DATA_BLOB *names_blob)
+{
+ uchar ntlmv2_response[16];
+ DATA_BLOB ntlmv2_client_data;
+ DATA_BLOB final_response;
+
+ /* NTLMv2 */
+ /* generate some data to pass into the response function - including
+ the hostname and domain name of the server */
+ ntlmv2_client_data = NTLMv2_generate_client_data(names_blob);
+
+ /* Given that data, and the challenge from the server, generate a response */
+ SMBOWFencrypt_ntv2(ntlm_v2_hash, server_chal, &ntlmv2_client_data, ntlmv2_response);
+
+ final_response = data_blob(NULL, sizeof(ntlmv2_response) + ntlmv2_client_data.length);
+
+ memcpy(final_response.data, ntlmv2_response, sizeof(ntlmv2_response));
+
+ memcpy(final_response.data+sizeof(ntlmv2_response),
+ ntlmv2_client_data.data, ntlmv2_client_data.length);
+
+ data_blob_free(&ntlmv2_client_data);
+
+ return final_response;
+}
+
+static DATA_BLOB LMv2_generate_response(const uchar ntlm_v2_hash[16],
+ const DATA_BLOB *server_chal)
+{
+ uchar lmv2_response[16];
+ DATA_BLOB lmv2_client_data = data_blob(NULL, 8);
+ DATA_BLOB final_response = data_blob(NULL, 24);
+
+ /* LMv2 */
+ /* client-supplied random data */
+ generate_random_buffer(lmv2_client_data.data, lmv2_client_data.length, False);
+
+ /* Given that data, and the challenge from the server, generate a response */
+ SMBOWFencrypt_ntv2(ntlm_v2_hash, server_chal, &lmv2_client_data, lmv2_response);
+ memcpy(final_response.data, lmv2_response, sizeof(lmv2_response));
+
+ /* after the first 16 bytes is the random data we generated above,
+ so the server can verify us with it */
+ memcpy(final_response.data+sizeof(lmv2_response),
+ lmv2_client_data.data, lmv2_client_data.length);
+
+ data_blob_free(&lmv2_client_data);
+
+ return final_response;
+}
+
+BOOL SMBNTLMv2encrypt(const char *user, const char *domain, const char *password,
+ const DATA_BLOB *server_chal,
+ const DATA_BLOB *names_blob,
+ DATA_BLOB *lm_response, DATA_BLOB *nt_response,
+ DATA_BLOB *nt_session_key)
+{
+ uchar nt_hash[16];
+ uchar ntlm_v2_hash[16];
+ E_md4hash(password, nt_hash);
+
+ /* We don't use the NT# directly. Instead we use it mashed up with
+ the username and domain.
+ This prevents username swapping during the auth exchange
+ */
+ if (!ntv2_owf_gen(nt_hash, user, domain, ntlm_v2_hash)) {
+ return False;
+ }
+
+ if (nt_response) {
+ *nt_response = NTLMv2_generate_response(ntlm_v2_hash, server_chal,
+ names_blob);
+ if (nt_session_key) {
+ *nt_session_key = data_blob(NULL, 16);
+
+ /* The NTLMv2 calculations also provide a session key, for signing etc later */
+ /* use only the first 16 bytes of nt_response for session key */
+ SMBsesskeygen_ntv2(ntlm_v2_hash, nt_response->data, nt_session_key->data);
+ }
+ }
+
+ /* LMv2 */
+
+ if (lm_response) {
+ *lm_response = LMv2_generate_response(ntlm_v2_hash, server_chal);
+ }
+
+ return True;
+}
+
+/***********************************************************
+ encode a password buffer with a unicode password. The buffer
+ is filled with random data to make it harder to attack.
+************************************************************/
+BOOL encode_pw_buffer(char buffer[516], const char *password, int string_flags)
+{
+ uchar new_pw[512];
+ size_t new_pw_len;
+
+ new_pw_len = push_string(NULL, new_pw,
+ password,
+ sizeof(new_pw), string_flags);
+
+ memcpy(&buffer[512 - new_pw_len], new_pw, new_pw_len);
+
+ generate_random_buffer((unsigned char *)buffer, 512 - new_pw_len, True);
+
+ /*
+ * The length of the new password is in the last 4 bytes of
+ * the data buffer.
+ */
+ SIVAL(buffer, 512, new_pw_len);
+ ZERO_STRUCT(new_pw);
+ return True;
+}
+
+
+/***********************************************************
+ decode a password buffer
+ *new_pw_len is the length in bytes of the possibly mulitbyte
+ returned password including termination.
+************************************************************/
+BOOL decode_pw_buffer(char in_buffer[516], char *new_pwrd,
+ int new_pwrd_size, uint32 *new_pw_len,
+ int string_flags)
+{
+ int byte_len=0;
+
+ /*
+ Warning !!! : This function is called from some rpc call.
+ The password IN the buffer may be a UNICODE string.
+ The password IN new_pwrd is an ASCII string
+ If you reuse that code somewhere else check first.
+ */
+
+ /* The length of the new password is in the last 4 bytes of the data buffer. */
+
+ byte_len = IVAL(in_buffer, 512);
+
+#ifdef DEBUG_PASSWORD
+ dump_data(100, in_buffer, 516);
+#endif
+
+ /* Password cannot be longer than the size of the password buffer */
+ if ( (byte_len < 0) || (byte_len > 512)) {
+ DEBUG(0, ("decode_pw_buffer: incorrect password length (%d).\n", byte_len));
+ DEBUG(0, ("decode_pw_buffer: check that 'encrypt passwords = yes'\n"));
+ return False;
+ }
+
+ /* decode into the return buffer. Buffer length supplied */
+ *new_pw_len = pull_string(NULL, new_pwrd, &in_buffer[512 - byte_len], new_pwrd_size,
+ byte_len, string_flags);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("decode_pw_buffer: new_pwrd: "));
+ dump_data(100, (char *)new_pwrd, *new_pw_len);
+ DEBUG(100,("multibyte len:%d\n", *new_pw_len));
+ DEBUG(100,("original char len:%d\n", byte_len/2));
+#endif
+
+ return True;
+}
diff --git a/source/libsmb/smberr.c b/source/libsmb/smberr.c
new file mode 100644
index 00000000000..82efbdb6898
--- /dev/null
+++ b/source/libsmb/smberr.c
@@ -0,0 +1,256 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) Andrew Tridgell 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+/* error code stuff - put together by Merik Karman
+ merik@blackadder.dsh.oz.au */
+
+
+/* There is a big list of error codes and their meanings at:
+
+ http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/errlist_7oz7.asp
+
+ and if you don't like MSDN try:
+
+ http://www.siris.gr/computers/library/error.htm
+
+*/
+
+typedef const struct
+{
+ const char *name;
+ int code;
+ const char *message;
+} err_code_struct;
+
+/* Dos Error Messages */
+err_code_struct dos_msgs[] = {
+ {"ERRbadfunc",ERRbadfunc,"Invalid function."},
+ {"ERRbadfile",ERRbadfile,"File not found."},
+ {"ERRbadpath",ERRbadpath,"Directory invalid."},
+ {"ERRnofids",ERRnofids,"No file descriptors available"},
+ {"ERRnoaccess",ERRnoaccess,"Access denied."},
+ {"ERRbadfid",ERRbadfid,"Invalid file handle."},
+ {"ERRbadmcb",ERRbadmcb,"Memory control blocks destroyed."},
+ {"ERRnomem",ERRnomem,"Insufficient server memory to perform the requested function."},
+ {"ERRbadmem",ERRbadmem,"Invalid memory block address."},
+ {"ERRbadenv",ERRbadenv,"Invalid environment."},
+ {"ERRbadformat",11,"Invalid format."},
+ {"ERRbadaccess",ERRbadaccess,"Invalid open mode."},
+ {"ERRbaddata",ERRbaddata,"Invalid data."},
+ {"ERRres",ERRres,"reserved."},
+ {"ERRbaddrive",ERRbaddrive,"Invalid drive specified."},
+ {"ERRremcd",ERRremcd,"A Delete Directory request attempted to remove the server's current directory."},
+ {"ERRdiffdevice",ERRdiffdevice,"Not same device."},
+ {"ERRnofiles",ERRnofiles,"A File Search command can find no more files matching the specified criteria."},
+ {"ERRbadshare",ERRbadshare,"The sharing mode specified for an Open conflicts with existing FIDs on the file."},
+ {"ERRlock",ERRlock,"A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process."},
+ {"ERRunsup", ERRunsup, "The operation is unsupported"},
+ {"ERRnosuchshare", ERRnosuchshare, "You specified an invalid share name"},
+ {"ERRfilexists",ERRfilexists,"The file named in a Create Directory, Make New File or Link request already exists."},
+ {"ERRinvalidname",ERRinvalidname, "Invalid name"},
+ {"ERRbadpipe",ERRbadpipe,"Pipe invalid."},
+ {"ERRpipebusy",ERRpipebusy,"All instances of the requested pipe are busy."},
+ {"ERRpipeclosing",ERRpipeclosing,"Pipe close in progress."},
+ {"ERRnotconnected",ERRnotconnected,"No process on other end of pipe."},
+ {"ERRmoredata",ERRmoredata,"There is more data to be returned."},
+ {"ERRinvgroup",ERRinvgroup,"Invalid workgroup (try the -W option)"},
+ {"ERRlogonfailure",ERRlogonfailure,"Logon failure"},
+ {"ERRdiskfull",ERRdiskfull,"Disk full"},
+ {"ERRgeneral",ERRgeneral, "General failure"},
+ {"ERRunknownlevel",ERRunknownlevel, "Unknown info level"},
+ {NULL,-1,NULL}};
+
+/* Server Error Messages */
+err_code_struct server_msgs[] = {
+ {"ERRerror",1,"Non-specific error code."},
+ {"ERRbadpw",2,"Bad password - name/password pair in a Tree Connect or Session Setup are invalid."},
+ {"ERRbadtype",3,"reserved."},
+ {"ERRaccess",4,"The requester does not have the necessary access rights within the specified context for the requested function. The context is defined by the TID or the UID."},
+ {"ERRinvnid",5,"The tree ID (TID) specified in a command was invalid."},
+ {"ERRinvnetname",6,"Invalid network name in tree connect."},
+ {"ERRinvdevice",7,"Invalid device - printer request made to non-printer connection or non-printer request made to printer connection."},
+ {"ERRqfull",49,"Print queue full (files) -- returned by open print file."},
+ {"ERRqtoobig",50,"Print queue full -- no space."},
+ {"ERRqeof",51,"EOF on print queue dump."},
+ {"ERRinvpfid",52,"Invalid print file FID."},
+ {"ERRsmbcmd",64,"The server did not recognize the command received."},
+ {"ERRsrverror",65,"The server encountered an internal error, e.g., system file unavailable."},
+ {"ERRfilespecs",67,"The file handle (FID) and pathname parameters contained an invalid combination of values."},
+ {"ERRreserved",68,"reserved."},
+ {"ERRbadpermits",69,"The access permissions specified for a file or directory are not a valid combination. The server cannot set the requested attribute."},
+ {"ERRreserved",70,"reserved."},
+ {"ERRsetattrmode",71,"The attribute mode in the Set File Attribute request is invalid."},
+ {"ERRpaused",81,"Server is paused."},
+ {"ERRmsgoff",82,"Not receiving messages."},
+ {"ERRnoroom",83,"No room to buffer message."},
+ {"ERRrmuns",87,"Too many remote user names."},
+ {"ERRtimeout",88,"Operation timed out."},
+ {"ERRnoresource",89,"No resources currently available for request."},
+ {"ERRtoomanyuids",90,"Too many UIDs active on this session."},
+ {"ERRbaduid",91,"The UID is not known as a valid ID on this session."},
+ {"ERRusempx",250,"Temp unable to support Raw, use MPX mode."},
+ {"ERRusestd",251,"Temp unable to support Raw, use standard read/write."},
+ {"ERRcontmpx",252,"Continue in MPX mode."},
+ {"ERRreserved",253,"reserved."},
+ {"ERRreserved",254,"reserved."},
+ {"ERRnosupport",0xFFFF,"Function not supported."},
+ {NULL,-1,NULL}};
+
+/* Hard Error Messages */
+err_code_struct hard_msgs[] = {
+ {"ERRnowrite",19,"Attempt to write on write-protected diskette."},
+ {"ERRbadunit",20,"Unknown unit."},
+ {"ERRnotready",21,"Drive not ready."},
+ {"ERRbadcmd",22,"Unknown command."},
+ {"ERRdata",23,"Data error (CRC)."},
+ {"ERRbadreq",24,"Bad request structure length."},
+ {"ERRseek",25 ,"Seek error."},
+ {"ERRbadmedia",26,"Unknown media type."},
+ {"ERRbadsector",27,"Sector not found."},
+ {"ERRnopaper",28,"Printer out of paper."},
+ {"ERRwrite",29,"Write fault."},
+ {"ERRread",30,"Read fault."},
+ {"ERRgeneral",31,"General failure."},
+ {"ERRbadshare",32,"An open conflicts with an existing open."},
+ {"ERRlock",33,"A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process."},
+ {"ERRwrongdisk",34,"The wrong disk was found in a drive."},
+ {"ERRFCBUnavail",35,"No FCBs are available to process request."},
+ {"ERRsharebufexc",36,"A sharing buffer has been exceeded."},
+ {NULL,-1,NULL}};
+
+
+const struct
+{
+ int code;
+ const char *class;
+ err_code_struct *err_msgs;
+} err_classes[] = {
+ {0,"SUCCESS",NULL},
+ {0x01,"ERRDOS",dos_msgs},
+ {0x02,"ERRSRV",server_msgs},
+ {0x03,"ERRHRD",hard_msgs},
+ {0x04,"ERRXOS",NULL},
+ {0xE1,"ERRRMX1",NULL},
+ {0xE2,"ERRRMX2",NULL},
+ {0xE3,"ERRRMX3",NULL},
+ {0xFF,"ERRCMD",NULL},
+ {-1,NULL,NULL}};
+
+
+/****************************************************************************
+return a SMB error name from a class and code
+****************************************************************************/
+const char *smb_dos_err_name(uint8 class, uint16 num)
+{
+ static pstring ret;
+ int i,j;
+
+ for (i=0;err_classes[i].class;i++)
+ if (err_classes[i].code == class) {
+ if (err_classes[i].err_msgs) {
+ err_code_struct *err = err_classes[i].err_msgs;
+ for (j=0;err[j].name;j++)
+ if (num == err[j].code) {
+ return err[j].name;
+ }
+ }
+ slprintf(ret, sizeof(ret) - 1, "%d",num);
+ return ret;
+ }
+
+ slprintf(ret, sizeof(ret) - 1, "Error: Unknown error class (%d,%d)",class,num);
+ return(ret);
+}
+
+/* Return a string for a DOS error */
+
+const char *get_dos_error_msg(WERROR result)
+{
+ uint16 errnum;
+
+ errnum = W_ERROR_V(result);
+
+ return smb_dos_err_name(ERRDOS, errnum);
+}
+
+/****************************************************************************
+return a SMB error class name as a string.
+****************************************************************************/
+const char *smb_dos_err_class(uint8 class)
+{
+ static pstring ret;
+ int i;
+
+ for (i=0;err_classes[i].class;i++) {
+ if (err_classes[i].code == class) {
+ return err_classes[i].class;
+ }
+ }
+
+ slprintf(ret, sizeof(ret) - 1, "Error: Unknown class (%d)",class);
+ return(ret);
+}
+
+/****************************************************************************
+return a SMB string from an SMB buffer
+****************************************************************************/
+char *smb_dos_errstr(char *inbuf)
+{
+ static pstring ret;
+ int class = CVAL(inbuf,smb_rcls);
+ int num = SVAL(inbuf,smb_err);
+ int i,j;
+
+ for (i=0;err_classes[i].class;i++)
+ if (err_classes[i].code == class) {
+ if (err_classes[i].err_msgs) {
+ err_code_struct *err = err_classes[i].err_msgs;
+ for (j=0;err[j].name;j++)
+ if (num == err[j].code) {
+ if (DEBUGLEVEL > 0)
+ slprintf(ret, sizeof(ret) - 1, "%s - %s (%s)",
+ err_classes[i].class,
+ err[j].name,err[j].message);
+ else
+ slprintf(ret, sizeof(ret) - 1, "%s - %s",
+ err_classes[i].class,err[j].name);
+ return ret;
+ }
+ }
+
+ slprintf(ret, sizeof(ret) - 1, "%s - %d",err_classes[i].class,num);
+ return ret;
+ }
+
+ slprintf(ret, sizeof(ret) - 1, "Error: Unknown error (%d,%d)",class,num);
+ return(ret);
+}
+
+/*****************************************************************************
+map a unix errno to a win32 error
+ *****************************************************************************/
+WERROR map_werror_from_unix(int error)
+{
+ NTSTATUS status = map_nt_error_from_unix(error);
+ return ntstatus_to_werror(status);
+}
diff --git a/source/libsmb/spnego.c b/source/libsmb/spnego.c
new file mode 100644
index 00000000000..50caf7b4c0e
--- /dev/null
+++ b/source/libsmb/spnego.c
@@ -0,0 +1,343 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ RFC2478 Compliant SPNEGO implementation
+
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+static BOOL read_negTokenInit(ASN1_DATA *asn1, negTokenInit_t *token)
+{
+ ZERO_STRUCTP(token);
+
+ asn1_start_tag(asn1, ASN1_CONTEXT(0));
+ asn1_start_tag(asn1, ASN1_SEQUENCE(0));
+
+ while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
+ int i;
+
+ switch (asn1->data[asn1->ofs]) {
+ /* Read mechTypes */
+ case ASN1_CONTEXT(0):
+ asn1_start_tag(asn1, ASN1_CONTEXT(0));
+ asn1_start_tag(asn1, ASN1_SEQUENCE(0));
+
+ token->mechTypes = malloc(sizeof(*token->mechTypes));
+ for (i = 0; !asn1->has_error &&
+ 0 < asn1_tag_remaining(asn1); i++) {
+ token->mechTypes =
+ realloc(token->mechTypes, (i + 2) *
+ sizeof(*token->mechTypes));
+ asn1_read_OID(asn1, token->mechTypes + i);
+ }
+ token->mechTypes[i] = NULL;
+
+ asn1_end_tag(asn1);
+ asn1_end_tag(asn1);
+ break;
+ /* Read reqFlags */
+ case ASN1_CONTEXT(1):
+ asn1_start_tag(asn1, ASN1_CONTEXT(1));
+ asn1_read_Integer(asn1, &token->reqFlags);
+ token->reqFlags |= SPNEGO_REQ_FLAG;
+ asn1_end_tag(asn1);
+ break;
+ /* Read mechToken */
+ case ASN1_CONTEXT(2):
+ asn1_start_tag(asn1, ASN1_CONTEXT(2));
+ asn1_read_OctetString(asn1, &token->mechToken);
+ asn1_end_tag(asn1);
+ break;
+ /* Read mecListMIC */
+ case ASN1_CONTEXT(3):
+ asn1_start_tag(asn1, ASN1_CONTEXT(3));
+ if (asn1->data[asn1->ofs] == ASN1_OCTET_STRING) {
+ asn1_read_OctetString(asn1,
+ &token->mechListMIC);
+ } else {
+ /* RFC 2478 says we have an Octet String here,
+ but W2k sends something different... */
+ char *mechListMIC;
+ asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+ asn1_push_tag(asn1, ASN1_CONTEXT(0));
+ asn1_read_GeneralString(asn1, &mechListMIC);
+ asn1_pop_tag(asn1);
+ asn1_pop_tag(asn1);
+
+ token->mechListMIC =
+ data_blob(mechListMIC, strlen(mechListMIC));
+ SAFE_FREE(mechListMIC);
+ }
+ asn1_end_tag(asn1);
+ break;
+ default:
+ asn1->has_error = True;
+ break;
+ }
+ }
+
+ asn1_end_tag(asn1);
+ asn1_end_tag(asn1);
+
+ return !asn1->has_error;
+}
+
+static BOOL write_negTokenInit(ASN1_DATA *asn1, negTokenInit_t *token)
+{
+ asn1_push_tag(asn1, ASN1_CONTEXT(0));
+ asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+
+ /* Write mechTypes */
+ if (token->mechTypes && *token->mechTypes) {
+ int i;
+
+ asn1_push_tag(asn1, ASN1_CONTEXT(0));
+ asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+ for (i = 0; token->mechTypes[i]; i++) {
+ asn1_write_OID(asn1, token->mechTypes[i]);
+ }
+ asn1_pop_tag(asn1);
+ asn1_pop_tag(asn1);
+ }
+
+ /* write reqFlags */
+ if (token->reqFlags & SPNEGO_REQ_FLAG) {
+ int flags = token->reqFlags & ~SPNEGO_REQ_FLAG;
+
+ asn1_push_tag(asn1, ASN1_CONTEXT(1));
+ asn1_write_Integer(asn1, flags);
+ asn1_pop_tag(asn1);
+ }
+
+ /* write mechToken */
+ if (token->mechToken.data) {
+ asn1_push_tag(asn1, ASN1_CONTEXT(2));
+ asn1_write_OctetString(asn1, token->mechToken.data,
+ token->mechToken.length);
+ asn1_pop_tag(asn1);
+ }
+
+ /* write mechListMIC */
+ if (token->mechListMIC.data) {
+ asn1_push_tag(asn1, ASN1_CONTEXT(3));
+#if 0
+ /* This is what RFC 2478 says ... */
+ asn1_write_OctetString(asn1, token->mechListMIC.data,
+ token->mechListMIC.length);
+#else
+ /* ... but unfortunately this is what Windows
+ sends/expects */
+ asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+ asn1_push_tag(asn1, ASN1_CONTEXT(0));
+ asn1_push_tag(asn1, ASN1_GENERAL_STRING);
+ asn1_write(asn1, token->mechListMIC.data,
+ token->mechListMIC.length);
+ asn1_pop_tag(asn1);
+ asn1_pop_tag(asn1);
+ asn1_pop_tag(asn1);
+#endif
+ asn1_pop_tag(asn1);
+ }
+
+ asn1_pop_tag(asn1);
+ asn1_pop_tag(asn1);
+
+ return !asn1->has_error;
+}
+
+static BOOL read_negTokenTarg(ASN1_DATA *asn1, negTokenTarg_t *token)
+{
+ ZERO_STRUCTP(token);
+
+ asn1_start_tag(asn1, ASN1_CONTEXT(1));
+ asn1_start_tag(asn1, ASN1_SEQUENCE(0));
+
+ while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
+ switch (asn1->data[asn1->ofs]) {
+ case ASN1_CONTEXT(0):
+ asn1_start_tag(asn1, ASN1_CONTEXT(0));
+ asn1_start_tag(asn1, ASN1_ENUMERATED);
+ asn1_read_uint8(asn1, &token->negResult);
+ asn1_end_tag(asn1);
+ asn1_end_tag(asn1);
+ break;
+ case ASN1_CONTEXT(1):
+ asn1_start_tag(asn1, ASN1_CONTEXT(1));
+ asn1_read_OID(asn1, &token->supportedMech);
+ asn1_end_tag(asn1);
+ break;
+ case ASN1_CONTEXT(2):
+ asn1_start_tag(asn1, ASN1_CONTEXT(2));
+ asn1_read_OctetString(asn1, &token->responseToken);
+ asn1_end_tag(asn1);
+ break;
+ case ASN1_CONTEXT(3):
+ asn1_start_tag(asn1, ASN1_CONTEXT(3));
+ asn1_read_OctetString(asn1, &token->mechListMIC);
+ asn1_end_tag(asn1);
+ break;
+ default:
+ asn1->has_error = True;
+ break;
+ }
+ }
+
+ asn1_end_tag(asn1);
+ asn1_end_tag(asn1);
+
+ return !asn1->has_error;
+}
+
+static BOOL write_negTokenTarg(ASN1_DATA *asn1, negTokenTarg_t *token)
+{
+ asn1_push_tag(asn1, ASN1_CONTEXT(1));
+ asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+
+ asn1_push_tag(asn1, ASN1_CONTEXT(0));
+ asn1_write_enumerated(asn1, token->negResult);
+ asn1_pop_tag(asn1);
+
+ if (token->supportedMech) {
+ asn1_push_tag(asn1, ASN1_CONTEXT(1));
+ asn1_write_OID(asn1, token->supportedMech);
+ asn1_pop_tag(asn1);
+ }
+
+ if (token->responseToken.data) {
+ asn1_push_tag(asn1, ASN1_CONTEXT(2));
+ asn1_write_OctetString(asn1, token->responseToken.data,
+ token->responseToken.length);
+ asn1_pop_tag(asn1);
+ }
+
+ if (token->mechListMIC.data) {
+ asn1_push_tag(asn1, ASN1_CONTEXT(3));
+ asn1_write_OctetString(asn1, token->mechListMIC.data,
+ token->mechListMIC.length);
+ asn1_pop_tag(asn1);
+ }
+
+ asn1_pop_tag(asn1);
+ asn1_pop_tag(asn1);
+
+ return !asn1->has_error;
+}
+
+ssize_t read_spnego_data(DATA_BLOB data, SPNEGO_DATA *token)
+{
+ ASN1_DATA asn1;
+ ssize_t ret = -1;
+
+ ZERO_STRUCTP(token);
+ ZERO_STRUCT(asn1);
+ asn1_load(&asn1, data);
+
+ switch (asn1.data[asn1.ofs]) {
+ case ASN1_APPLICATION(0):
+ asn1_start_tag(&asn1, ASN1_APPLICATION(0));
+ asn1_check_OID(&asn1, OID_SPNEGO);
+ if (read_negTokenInit(&asn1, &token->negTokenInit)) {
+ token->type = SPNEGO_NEG_TOKEN_INIT;
+ }
+ asn1_end_tag(&asn1);
+ break;
+ case ASN1_CONTEXT(1):
+ if (read_negTokenTarg(&asn1, &token->negTokenTarg)) {
+ token->type = SPNEGO_NEG_TOKEN_TARG;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!asn1.has_error) ret = asn1.ofs;
+ asn1_free(&asn1);
+
+ return ret;
+}
+
+ssize_t write_spnego_data(DATA_BLOB *blob, SPNEGO_DATA *spnego)
+{
+ ASN1_DATA asn1;
+ ssize_t ret = -1;
+
+ ZERO_STRUCT(asn1);
+
+ switch (spnego->type) {
+ case SPNEGO_NEG_TOKEN_INIT:
+ asn1_push_tag(&asn1, ASN1_APPLICATION(0));
+ asn1_write_OID(&asn1, OID_SPNEGO);
+ write_negTokenInit(&asn1, &spnego->negTokenInit);
+ asn1_pop_tag(&asn1);
+ break;
+ case SPNEGO_NEG_TOKEN_TARG:
+ write_negTokenTarg(&asn1, &spnego->negTokenTarg);
+ break;
+ default:
+ asn1.has_error = True;
+ break;
+ }
+
+ if (!asn1.has_error) {
+ *blob = data_blob(asn1.data, asn1.length);
+ ret = asn1.ofs;
+ }
+ asn1_free(&asn1);
+
+ return ret;
+}
+
+BOOL free_spnego_data(SPNEGO_DATA *spnego)
+{
+ BOOL ret = True;
+
+ if (!spnego) goto out;
+
+ switch(spnego->type) {
+ case SPNEGO_NEG_TOKEN_INIT:
+ if (spnego->negTokenInit.mechTypes) {
+ int i;
+ for (i = 0; spnego->negTokenInit.mechTypes[i]; i++) {
+ free(spnego->negTokenInit.mechTypes[i]);
+ }
+ free(spnego->negTokenInit.mechTypes);
+ }
+ data_blob_free(&spnego->negTokenInit.mechToken);
+ data_blob_free(&spnego->negTokenInit.mechListMIC);
+ break;
+ case SPNEGO_NEG_TOKEN_TARG:
+ if (spnego->negTokenTarg.supportedMech) {
+ free(spnego->negTokenTarg.supportedMech);
+ }
+ data_blob_free(&spnego->negTokenTarg.responseToken);
+ data_blob_free(&spnego->negTokenTarg.mechListMIC);
+ break;
+ default:
+ ret = False;
+ break;
+ }
+ ZERO_STRUCTP(spnego);
+out:
+ return ret;
+}
+
diff --git a/source/libsmb/trustdom_cache.c b/source/libsmb/trustdom_cache.c
new file mode 100644
index 00000000000..0128d080062
--- /dev/null
+++ b/source/libsmb/trustdom_cache.c
@@ -0,0 +1,342 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Trusted domain names cache on top of gencache.
+
+ Copyright (C) Rafal Szczesniak 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_ALL /* there's no proper class yet */
+
+#define TDOMKEY_FMT "TDOM/%s"
+#define TDOMTSKEY "TDOMCACHE/TIMESTAMP"
+
+
+/**
+ * @file trustdom_cache.c
+ *
+ * Implementation of trusted domain names cache useful when
+ * samba acts as domain member server. In such case, caching
+ * domain names currently trusted gives a performance gain
+ * because there's no need to query PDC each time we need
+ * list of trusted domains
+ **/
+
+
+/**
+ * Initialise trustdom name caching system. Call gencache
+ * initialisation routine to perform necessary activities.
+ *
+ * @return true upon successful cache initialisation or
+ * false if cache init failed
+ **/
+
+BOOL trustdom_cache_enable(void)
+{
+ /* Init trustdom cache by calling gencache initialisation */
+ if (!gencache_init()) {
+ DEBUG(2, ("trustdomcache_enable: Couldn't initialise trustdom cache on top of gencache.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/**
+ * Shutdown trustdom name caching system. Calls gencache
+ * shutdown function.
+ *
+ * @return true upon successful cache close or
+ * false if it failed
+ **/
+
+BOOL trustdom_cache_shutdown(void)
+{
+ /* Close trustdom cache by calling gencache shutdown */
+ if (!gencache_shutdown()) {
+ DEBUG(2, ("trustdomcache_shutdown: Couldn't shutdown trustdom cache on top of gencache.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/**
+ * Form up trustdom name key. It is based only
+ * on domain name now.
+ *
+ * @param name trusted domain name
+ * @return cache key for use in gencache mechanism
+ **/
+
+static char* trustdom_cache_key(const char* name)
+{
+ char* keystr = NULL;
+ asprintf(&keystr, TDOMKEY_FMT, strupper_static(name));
+
+ return keystr;
+}
+
+
+/**
+ * Store trusted domain in gencache as the domain name (key)
+ * and ip address of domain controller (value)
+ *
+ * @param name trusted domain name
+ * @param alt_name alternative trusted domain name (used in ADS domains)
+ * @param sid trusted domain's SID
+ * @param timeout cache entry expiration time
+ * @return true upon successful value storing or
+ * false if store attempt failed
+ **/
+
+BOOL trustdom_cache_store(char* name, char* alt_name, const DOM_SID *sid,
+ time_t timeout)
+{
+ char *key, *alt_key;
+ fstring sid_string;
+
+ /*
+ * we use gecache call to avoid annoying debug messages
+ * about initialised trustdom
+ */
+ if (!gencache_init()) return False;
+
+ DEBUG(5, ("trustdom_store: storing SID %s of domain %s\n",
+ sid_string_static(sid), name));
+
+ key = trustdom_cache_key(name);
+ alt_key = alt_name ? trustdom_cache_key(alt_name) : NULL;
+
+ /* Generate string representation domain SID */
+ sid_to_string(sid_string, sid);
+
+ /*
+ * try to put the names in the cache
+ */
+ if (alt_key) {
+ return (gencache_set(alt_key, sid_string, timeout)
+ && gencache_set(key, sid_string, timeout));
+ }
+
+ return gencache_set(key, sid_string, timeout);
+}
+
+
+/**
+ * Fetch trusted domain's dc from the gencache.
+ * This routine can also be used to check whether given
+ * domain is currently trusted one.
+ *
+ * @param name trusted domain name
+ * @param sid trusted domain's SID to be returned
+ * @return true if entry is found or
+ * false if has expired/doesn't exist
+ **/
+
+BOOL trustdom_cache_fetch(const char* name, DOM_SID* sid)
+{
+ char *key, *value;
+ time_t timeout;
+
+ /* init the cache */
+ if (!gencache_init()) return False;
+
+ /* exit now if null pointers were passed as they're required further */
+ if (!sid) return False;
+
+ /* prepare a key and get the value */
+ key = trustdom_cache_key(name);
+ if (!key) return False;
+
+ if (!gencache_get(key, &value, &timeout)) {
+ DEBUG(5, ("no entry for trusted domain %s found.\n", name));
+ SAFE_FREE(key);
+ return False;
+ } else {
+ SAFE_FREE(key);
+ DEBUG(5, ("trusted domain %s found (%s)\n", name, value));
+ }
+
+ /* convert ip string representation into in_addr structure */
+ if(! string_to_sid(sid, value)) {
+ sid = NULL;
+ return False;
+ }
+
+ return True;
+}
+
+
+/*******************************************************************
+ fetch the timestamp from the last update
+*******************************************************************/
+
+uint32 trustdom_cache_fetch_timestamp( void )
+{
+ char *value;
+ time_t timeout;
+ uint32 timestamp;
+
+ /* init the cache */
+ if (!gencache_init())
+ return False;
+
+ if (!gencache_get(TDOMTSKEY, &value, &timeout)) {
+ DEBUG(5, ("no timestamp for trusted domain cache located.\n"));
+ return 0;
+ }
+
+ timestamp = atoi(value);
+
+ return timestamp;
+}
+
+/*******************************************************************
+ store the timestamp from the last update
+*******************************************************************/
+
+BOOL trustdom_cache_store_timestamp( uint32 t, time_t timeout )
+{
+ fstring value;
+
+ /* init the cache */
+ if (!gencache_init())
+ return False;
+
+ fstr_sprintf(value, "%d", t );
+
+ if (!gencache_set(TDOMTSKEY, value, timeout)) {
+ DEBUG(5, ("failed to set timestamp for trustdom_cache\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/*******************************************************************
+ lock the timestamp entry in the trustdom_cache
+*******************************************************************/
+
+BOOL trustdom_cache_lock_timestamp( void )
+{
+ return gencache_lock_entry( TDOMTSKEY ) != -1;
+}
+
+/*******************************************************************
+ unlock the timestamp entry in the trustdom_cache
+*******************************************************************/
+
+void trustdom_cache_unlock_timestamp( void )
+{
+ gencache_unlock_entry( TDOMTSKEY );
+}
+
+/**
+ * Delete single trustdom entry. Look at the
+ * gencache_iterate definition.
+ *
+ **/
+
+static void flush_trustdom_name(const char* key, const char *value, time_t timeout, void* dptr)
+{
+ gencache_del(key);
+ DEBUG(5, ("Deleting entry %s\n", key));
+}
+
+
+/**
+ * Flush all the trusted domains entries from the cache.
+ **/
+
+void trustdom_cache_flush(void)
+{
+ if (!gencache_init())
+ return;
+
+ /*
+ * iterate through each TDOM cache's entry and flush it
+ * by flush_trustdom_name function
+ */
+ gencache_iterate(flush_trustdom_name, NULL, trustdom_cache_key("*"));
+ DEBUG(5, ("Trusted domains cache flushed\n"));
+}
+
+/********************************************************************
+ update the trustdom_cache if needed
+********************************************************************/
+#define TRUSTDOM_UPDATE_INTERVAL 600
+
+void update_trustdom_cache( void )
+{
+ char **domain_names;
+ DOM_SID *dom_sids;
+ uint32 num_domains;
+ uint32 last_check;
+ int time_diff;
+ TALLOC_CTX *mem_ctx = NULL;
+ time_t now = time(NULL);
+ int i;
+
+ /* get the timestamp. We have to initialise it if the last timestamp == 0 */
+
+ if ( (last_check = trustdom_cache_fetch_timestamp()) == 0 )
+ trustdom_cache_store_timestamp(0, now+TRUSTDOM_UPDATE_INTERVAL);
+
+ time_diff = now - last_check;
+
+ if ( (time_diff > 0) && (time_diff < TRUSTDOM_UPDATE_INTERVAL) ) {
+ DEBUG(10,("update_trustdom_cache: not time to update trustdom_cache yet\n"));
+ return;
+ }
+
+ /* lock the timestamp */
+ if ( !trustdom_cache_lock_timestamp() )
+ return;
+
+ if ( !(mem_ctx = talloc_init("update_trustdom_cache")) ) {
+ DEBUG(0,("update_trustdom_cache: talloc_init() failed!\n"));
+ goto done;
+ }
+
+ /* get the domains and store them */
+
+ if ( enumerate_domain_trusts(mem_ctx, lp_workgroup(), &domain_names,
+ &num_domains, &dom_sids) )
+ {
+ for ( i=0; i<num_domains; i++ ) {
+ trustdom_cache_store( domain_names[i], NULL, &dom_sids[i],
+ now+TRUSTDOM_UPDATE_INTERVAL);
+ }
+
+ trustdom_cache_store_timestamp( now, now+TRUSTDOM_UPDATE_INTERVAL );
+ }
+
+done:
+ /* unlock and we're done */
+ trustdom_cache_unlock_timestamp();
+
+ talloc_destroy( mem_ctx );
+
+ return;
+}
diff --git a/source/libsmb/trusts_util.c b/source/libsmb/trusts_util.c
new file mode 100644
index 00000000000..b420e4fa081
--- /dev/null
+++ b/source/libsmb/trusts_util.c
@@ -0,0 +1,190 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Routines to operate on various trust relationships
+ * Copyright (C) Andrew Bartlett 2001
+ * Copyright (C) Rafal Szczesniak 2003
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+/*********************************************************
+ Change the domain password on the PDC.
+
+ Just changes the password betwen the two values specified.
+
+ Caller must have the cli connected to the netlogon pipe
+ already.
+**********************************************************/
+static NTSTATUS just_change_the_password(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ unsigned char orig_trust_passwd_hash[16],
+ unsigned char new_trust_passwd_hash[16],
+ uint32 sec_channel_type)
+{
+ NTSTATUS result;
+
+ /* ensure that schannel uses the right domain */
+ fstrcpy(cli->domain, lp_workgroup());
+ if (! NT_STATUS_IS_OK(result = cli_nt_establish_netlogon(cli, sec_channel_type, orig_trust_passwd_hash))) {
+ DEBUG(3,("just_change_the_password: unable to setup creds (%s)!\n",
+ nt_errstr(result)));
+ return result;
+ }
+
+ result = cli_net_srv_pwset(cli, mem_ctx, global_myname(), new_trust_passwd_hash);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0,("just_change_the_password: unable to change password (%s)!\n",
+ nt_errstr(result)));
+ }
+ return result;
+}
+
+/*********************************************************
+ Change the domain password on the PDC.
+ Store the password ourselves, but use the supplied password
+ Caller must have already setup the connection to the NETLOGON pipe
+**********************************************************/
+
+NTSTATUS trust_pw_change_and_store_it(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const char *domain,
+ unsigned char orig_trust_passwd_hash[16],
+ uint32 sec_channel_type)
+{
+ unsigned char new_trust_passwd_hash[16];
+ char *new_trust_passwd;
+ char *str;
+ NTSTATUS nt_status;
+
+ /* Create a random machine account password */
+ str = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
+ new_trust_passwd = talloc_strdup(mem_ctx, str);
+
+ E_md4hash(new_trust_passwd, new_trust_passwd_hash);
+
+ nt_status = just_change_the_password(cli, mem_ctx, orig_trust_passwd_hash,
+ new_trust_passwd_hash, sec_channel_type);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(3,("%s : trust_pw_change_and_store_it: Changed password.\n",
+ timestring(False)));
+ /*
+ * Return the result of trying to write the new password
+ * back into the trust account file.
+ */
+ if (!secrets_store_machine_password(new_trust_passwd, domain, sec_channel_type)) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ return nt_status;
+}
+
+/*********************************************************
+ Change the domain password on the PDC.
+ Do most of the legwork ourselfs. Caller must have
+ already setup the connection to the NETLOGON pipe
+**********************************************************/
+
+NTSTATUS trust_pw_find_change_and_store_it(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ const char *domain)
+{
+ unsigned char old_trust_passwd_hash[16];
+ char *up_domain;
+ uint32 sec_channel_type = 0;
+
+ up_domain = talloc_strdup(mem_ctx, domain);
+
+ if (!secrets_fetch_trust_account_password(domain,
+ old_trust_passwd_hash,
+ NULL, &sec_channel_type)) {
+ DEBUG(0, ("could not fetch domain secrets for domain %s!\n", domain));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return trust_pw_change_and_store_it(cli, mem_ctx, domain,
+ old_trust_passwd_hash,
+ sec_channel_type);
+
+}
+
+/*********************************************************************
+ Enumerate the list of trusted domains from a DC
+*********************************************************************/
+
+BOOL enumerate_domain_trusts( TALLOC_CTX *mem_ctx, const char *domain,
+ char ***domain_names, uint32 *num_domains,
+ DOM_SID **sids )
+{
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ fstring dc_name;
+ struct in_addr dc_ip;
+ uint32 enum_ctx = 0;
+ struct cli_state *cli = NULL;
+ BOOL retry;
+
+ *domain_names = NULL;
+ *num_domains = 0;
+ *sids = NULL;
+
+ /* lookup a DC first */
+
+ if ( !get_dc_name(domain, NULL, dc_name, &dc_ip) ) {
+ DEBUG(3,("enumerate_domain_trusts: can't locate a DC for domain %s\n",
+ domain));
+ return False;
+ }
+
+ /* setup the anonymous connection */
+
+ result = cli_full_connection( &cli, global_myname(), dc_name, &dc_ip, 0, "IPC$", "IPC",
+ "", "", "", 0, Undefined, &retry);
+ if ( !NT_STATUS_IS_OK(result) )
+ goto done;
+
+ /* open the LSARPC_PIPE */
+
+ if ( !cli_nt_session_open( cli, PI_LSARPC ) ) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* get a handle */
+
+ result = cli_lsa_open_policy(cli, mem_ctx, True,
+ POLICY_VIEW_LOCAL_INFORMATION, &pol);
+ if ( !NT_STATUS_IS_OK(result) )
+ goto done;
+
+ /* Lookup list of trusted domains */
+
+ result = cli_lsa_enum_trust_dom(cli, mem_ctx, &pol, &enum_ctx,
+ num_domains, domain_names, sids);
+ if ( !NT_STATUS_IS_OK(result) )
+ goto done;
+
+done:
+ /* cleanup */
+ if (cli) {
+ DEBUG(10,("enumerate_domain_trusts: shutting down connection...\n"));
+ cli_shutdown( cli );
+ }
+
+ return NT_STATUS_IS_OK(result);
+}
+
diff --git a/source/libsmb/unexpected.c b/source/libsmb/unexpected.c
new file mode 100644
index 00000000000..97d6071e714
--- /dev/null
+++ b/source/libsmb/unexpected.c
@@ -0,0 +1,164 @@
+/*
+ Unix SMB/CIFS implementation.
+ handle unexpected packets
+ Copyright (C) Andrew Tridgell 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+
+static TDB_CONTEXT *tdbd = NULL;
+
+/* the key type used in the unexpeceted packet database */
+struct unexpected_key {
+ enum packet_type packet_type;
+ time_t timestamp;
+ int count;
+};
+
+
+
+/****************************************************************************
+ all unexpected packets are passed in here, to be stored in a unexpected
+ packet database. This allows nmblookup and other tools to receive packets
+ erroneoously sent to the wrong port by broken MS systems
+ **************************************************************************/
+void unexpected_packet(struct packet_struct *p)
+{
+ static int count;
+ TDB_DATA kbuf, dbuf;
+ struct unexpected_key key;
+ char buf[1024];
+ int len=0;
+
+ if (!tdbd) {
+ tdbd = tdb_open_log(lock_path("unexpected.tdb"), 0,
+ TDB_CLEAR_IF_FIRST|TDB_DEFAULT,
+ O_RDWR | O_CREAT, 0644);
+ if (!tdbd) {
+ DEBUG(0,("Failed to open unexpected.tdb\n"));
+ return;
+ }
+ }
+
+ memset(buf,'\0',sizeof(buf));
+
+ len = build_packet(buf, p);
+
+ key.packet_type = p->packet_type;
+ key.timestamp = p->timestamp;
+ key.count = count++;
+
+ kbuf.dptr = (char *)&key;
+ kbuf.dsize = sizeof(key);
+ dbuf.dptr = buf;
+ dbuf.dsize = len;
+
+ tdb_store(tdbd, kbuf, dbuf, TDB_REPLACE);
+}
+
+
+static time_t lastt;
+
+/****************************************************************************
+delete the record if it is too old
+ **************************************************************************/
+static int traverse_fn(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
+{
+ struct unexpected_key key;
+
+ memcpy(&key, kbuf.dptr, sizeof(key));
+
+ if (lastt - key.timestamp > NMBD_UNEXPECTED_TIMEOUT) {
+ tdb_delete(ttdb, kbuf);
+ }
+
+ return 0;
+}
+
+
+/****************************************************************************
+delete all old unexpected packets
+ **************************************************************************/
+void clear_unexpected(time_t t)
+{
+ if (!tdbd) return;
+
+ if ((lastt != 0) && (t < lastt + NMBD_UNEXPECTED_TIMEOUT))
+ return;
+
+ lastt = t;
+
+ tdb_traverse(tdbd, traverse_fn, NULL);
+}
+
+
+static struct packet_struct *matched_packet;
+static int match_id;
+static enum packet_type match_type;
+static const char *match_name;
+
+/****************************************************************************
+tdb traversal fn to find a matching 137 packet
+ **************************************************************************/
+static int traverse_match(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
+{
+ struct unexpected_key key;
+ struct packet_struct *p;
+
+ memcpy(&key, kbuf.dptr, sizeof(key));
+
+ if (key.packet_type != match_type) return 0;
+
+ p = parse_packet(dbuf.dptr, dbuf.dsize, match_type);
+
+ if ((match_type == NMB_PACKET &&
+ p->packet.nmb.header.name_trn_id == match_id) ||
+ (match_type == DGRAM_PACKET &&
+ match_mailslot_name(p, match_name))) {
+ matched_packet = p;
+ return -1;
+ }
+
+ free_packet(p);
+
+ return 0;
+}
+
+
+/****************************************************************************
+check for a particular packet in the unexpected packet queue
+ **************************************************************************/
+struct packet_struct *receive_unexpected(enum packet_type packet_type, int id,
+ const char *mailslot_name)
+{
+ TDB_CONTEXT *tdb2;
+
+ tdb2 = tdb_open_log(lock_path("unexpected.tdb"), 0, 0, O_RDONLY, 0);
+ if (!tdb2) return NULL;
+
+ matched_packet = NULL;
+ match_id = id;
+ match_type = packet_type;
+ match_name = mailslot_name;
+
+ tdb_traverse(tdb2, traverse_match, NULL);
+
+ tdb_close(tdb2);
+
+ return matched_packet;
+}
diff --git a/source/locking/brlock.c b/source/locking/brlock.c
new file mode 100644
index 00000000000..47001c8b89c
--- /dev/null
+++ b/source/locking/brlock.c
@@ -0,0 +1,714 @@
+/*
+ Unix SMB/CIFS implementation.
+ byte range locking code
+ Updated to handle range splits/merges.
+
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Jeremy Allison 1992-2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* This module implements a tdb based byte range locking service,
+ replacing the fcntl() based byte range locking previously
+ used. This allows us to provide the same semantics as NT */
+
+#include "includes.h"
+
+#define ZERO_ZERO 0
+
+/* This contains elements that differentiate locks. The smbpid is a
+ client supplied pid, and is essentially the locking context for
+ this client */
+
+struct lock_context {
+ uint16 smbpid;
+ uint16 tid;
+ pid_t pid;
+};
+
+/* The data in brlock records is an unsorted linear array of these
+ records. It is unnecessary to store the count as tdb provides the
+ size of the record */
+
+struct lock_struct {
+ struct lock_context context;
+ br_off start;
+ br_off size;
+ int fnum;
+ enum brl_type lock_type;
+};
+
+/* The key used in the brlock database. */
+
+struct lock_key {
+ SMB_DEV_T device;
+ SMB_INO_T inode;
+};
+
+/* The open brlock.tdb database. */
+
+static TDB_CONTEXT *tdb;
+
+/****************************************************************************
+ Create a locking key - ensuring zero filled for pad purposes.
+****************************************************************************/
+
+static TDB_DATA locking_key(SMB_DEV_T dev, SMB_INO_T inode)
+{
+ static struct lock_key key;
+ TDB_DATA kbuf;
+
+ memset(&key, '\0', sizeof(key));
+ key.device = dev;
+ key.inode = inode;
+ kbuf.dptr = (char *)&key;
+ kbuf.dsize = sizeof(key);
+ return kbuf;
+}
+
+/****************************************************************************
+ See if two locking contexts are equal.
+****************************************************************************/
+
+static BOOL brl_same_context(struct lock_context *ctx1,
+ struct lock_context *ctx2)
+{
+ return (ctx1->pid == ctx2->pid) &&
+ (ctx1->smbpid == ctx2->smbpid) &&
+ (ctx1->tid == ctx2->tid);
+}
+
+/****************************************************************************
+ See if lock2 can be added when lock1 is in place.
+****************************************************************************/
+
+static BOOL brl_conflict(struct lock_struct *lck1,
+ struct lock_struct *lck2)
+{
+ if (lck1->lock_type == PENDING_LOCK || lck2->lock_type == PENDING_LOCK )
+ return False;
+
+ if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK) {
+ return False;
+ }
+
+ if (brl_same_context(&lck1->context, &lck2->context) &&
+ lck2->lock_type == READ_LOCK && lck1->fnum == lck2->fnum) {
+ return False;
+ }
+
+ if (lck1->start >= (lck2->start + lck2->size) ||
+ lck2->start >= (lck1->start + lck1->size)) {
+ return False;
+ }
+
+ return True;
+}
+
+#if ZERO_ZERO
+static BOOL brl_conflict1(struct lock_struct *lck1,
+ struct lock_struct *lck2)
+{
+ if (lck1->lock_type == PENDING_LOCK || lck2->lock_type == PENDING_LOCK )
+ return False;
+
+ if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK) {
+ return False;
+ }
+
+ if (brl_same_context(&lck1->context, &lck2->context) &&
+ lck2->lock_type == READ_LOCK && lck1->fnum == lck2->fnum) {
+ return False;
+ }
+
+ if (lck2->start == 0 && lck2->size == 0 && lck1->size != 0) {
+ return True;
+ }
+
+ if (lck1->start >= (lck2->start + lck2->size) ||
+ lck2->start >= (lck1->start + lck1->size)) {
+ return False;
+ }
+
+ return True;
+}
+#endif
+
+/****************************************************************************
+ Check to see if this lock conflicts, but ignore our own locks on the
+ same fnum only.
+****************************************************************************/
+
+static BOOL brl_conflict_other(struct lock_struct *lck1, struct lock_struct *lck2)
+{
+ if (lck1->lock_type == PENDING_LOCK || lck2->lock_type == PENDING_LOCK )
+ return False;
+
+ if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK)
+ return False;
+
+ /*
+ * Incoming WRITE locks conflict with existing READ locks even
+ * if the context is the same. JRA. See LOCKTEST7 in smbtorture.
+ */
+
+ if (!(lck2->lock_type == WRITE_LOCK && lck1->lock_type == READ_LOCK)) {
+ if (brl_same_context(&lck1->context, &lck2->context) &&
+ lck1->fnum == lck2->fnum)
+ return False;
+ }
+
+ if (lck1->start >= (lck2->start + lck2->size) ||
+ lck2->start >= (lck1->start + lck1->size))
+ return False;
+
+ return True;
+}
+
+
+#if DONT_DO_THIS
+ /* doing this traversal could kill solaris machines under high load (tridge) */
+ /* delete any dead locks */
+
+/****************************************************************************
+ Delete a record if it is for a dead process, if check_self is true, then
+ delete any records belonging to this pid also (there shouldn't be any).
+****************************************************************************/
+
+static int delete_fn(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
+{
+ struct lock_struct *locks;
+ int count, i;
+ BOOL check_self = *(BOOL *)state;
+ pid_t mypid = sys_getpid();
+
+ tdb_chainlock(tdb, kbuf);
+
+ locks = (struct lock_struct *)dbuf.dptr;
+
+ count = dbuf.dsize / sizeof(*locks);
+ for (i=0; i<count; i++) {
+ struct lock_struct *lock = &locks[i];
+
+ /* If check_self is true we want to remove our own records. */
+ if (check_self && (mypid == lock->context.pid)) {
+
+ DEBUG(0,("brlock : delete_fn. LOGIC ERROR ! Shutting down and a record for my pid (%u) exists !\n",
+ (unsigned int)lock->context.pid ));
+
+ } else if (process_exists(lock->context.pid)) {
+
+ DEBUG(10,("brlock : delete_fn. pid %u exists.\n", (unsigned int)lock->context.pid ));
+ continue;
+ }
+
+ DEBUG(10,("brlock : delete_fn. Deleting record for process %u\n",
+ (unsigned int)lock->context.pid ));
+
+ if (count > 1 && i < count-1) {
+ memmove(&locks[i], &locks[i+1],
+ sizeof(*locks)*((count-1) - i));
+ }
+ count--;
+ i--;
+ }
+
+ if (count == 0) {
+ tdb_delete(tdb, kbuf);
+ } else if (count < (dbuf.dsize / sizeof(*locks))) {
+ dbuf.dsize = count * sizeof(*locks);
+ tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
+ }
+
+ tdb_chainunlock(tdb, kbuf);
+ return 0;
+}
+#endif
+
+/****************************************************************************
+ Open up the brlock.tdb database.
+****************************************************************************/
+
+void brl_init(int read_only)
+{
+ if (tdb)
+ return;
+ tdb = tdb_open_ex(lock_path("brlock.tdb"), 0, TDB_DEFAULT|(read_only?0x0:TDB_CLEAR_IF_FIRST),
+ read_only?O_RDONLY:(O_RDWR|O_CREAT), 0644, smbd_tdb_log);
+ if (!tdb) {
+ DEBUG(0,("Failed to open byte range locking database\n"));
+ return;
+ }
+
+#if DONT_DO_THIS
+ /* doing this traversal could kill solaris machines under high load (tridge) */
+ /* delete any dead locks */
+ if (!read_only) {
+ BOOL check_self = False;
+ tdb_traverse(tdb, delete_fn, &check_self);
+ }
+#endif
+}
+
+/****************************************************************************
+ Close down the brlock.tdb database.
+****************************************************************************/
+
+void brl_shutdown(int read_only)
+{
+ if (!tdb)
+ return;
+
+#if DONT_DO_THIS
+ /* doing this traversal could kill solaris machines under high load (tridge) */
+ /* delete any dead locks */
+ if (!read_only) {
+ BOOL check_self = True;
+ tdb_traverse(tdb, delete_fn, &check_self);
+ }
+#endif
+
+ tdb_close(tdb);
+}
+
+#if ZERO_ZERO
+/****************************************************************************
+compare two locks for sorting
+****************************************************************************/
+static int lock_compare(struct lock_struct *lck1,
+ struct lock_struct *lck2)
+{
+ if (lck1->start != lck2->start) return (lck1->start - lck2->start);
+ if (lck2->size != lck1->size) {
+ return ((int)lck1->size - (int)lck2->size);
+ }
+ return 0;
+}
+#endif
+
+/****************************************************************************
+ Lock a range of bytes.
+****************************************************************************/
+
+NTSTATUS brl_lock(SMB_DEV_T dev, SMB_INO_T ino, int fnum,
+ uint16 smbpid, pid_t pid, uint16 tid,
+ br_off start, br_off size,
+ enum brl_type lock_type, BOOL *my_lock_ctx)
+{
+ TDB_DATA kbuf, dbuf;
+ int count, i;
+ struct lock_struct lock, *locks;
+ char *tp;
+ NTSTATUS status = NT_STATUS_OK;
+ static int last_failed = -1;
+ static br_off last_failed_start;
+
+ *my_lock_ctx = False;
+ kbuf = locking_key(dev,ino);
+
+ dbuf.dptr = NULL;
+
+#if !ZERO_ZERO
+ if (start == 0 && size == 0) {
+ DEBUG(0,("client sent 0/0 lock - please report this\n"));
+ }
+#endif
+
+ tdb_chainlock(tdb, kbuf);
+ dbuf = tdb_fetch(tdb, kbuf);
+
+ lock.context.smbpid = smbpid;
+ lock.context.pid = pid;
+ lock.context.tid = tid;
+ lock.start = start;
+ lock.size = size;
+ lock.fnum = fnum;
+ lock.lock_type = lock_type;
+
+ if (dbuf.dptr) {
+ /* there are existing locks - make sure they don't conflict */
+ locks = (struct lock_struct *)dbuf.dptr;
+ count = dbuf.dsize / sizeof(*locks);
+ for (i=0; i<count; i++) {
+ if (brl_conflict(&locks[i], &lock)) {
+ status = NT_STATUS_LOCK_NOT_GRANTED;
+ /* Did we block ourselves ? */
+ if (brl_same_context(&locks[i].context, &lock.context))
+ *my_lock_ctx = True;
+ goto fail;
+ }
+#if ZERO_ZERO
+ if (lock.start == 0 && lock.size == 0 &&
+ locks[i].size == 0) {
+ break;
+ }
+#endif
+ }
+ }
+
+ /* no conflicts - add it to the list of locks */
+ tp = Realloc(dbuf.dptr, dbuf.dsize + sizeof(*locks));
+ if (!tp) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ } else {
+ dbuf.dptr = tp;
+ }
+ memcpy(dbuf.dptr + dbuf.dsize, &lock, sizeof(lock));
+ dbuf.dsize += sizeof(lock);
+
+#if ZERO_ZERO
+ /* sort the lock list */
+ qsort(dbuf.dptr, dbuf.dsize/sizeof(lock), sizeof(lock), lock_compare);
+#endif
+
+ tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
+
+ SAFE_FREE(dbuf.dptr);
+ tdb_chainunlock(tdb, kbuf);
+ return NT_STATUS_OK;
+
+ fail:
+ /* this is a nasty hack to try to simulate the lock result cache code in w2k.
+ It isn't completely accurate as I haven't yet worked out the correct
+ semantics (tridge)
+ */
+ if (last_failed == fnum &&
+ last_failed_start == start &&
+ NT_STATUS_EQUAL(status, NT_STATUS_LOCK_NOT_GRANTED)) {
+ status = NT_STATUS_FILE_LOCK_CONFLICT;
+ }
+ last_failed = fnum;
+ last_failed_start = start;
+
+ SAFE_FREE(dbuf.dptr);
+ tdb_chainunlock(tdb, kbuf);
+ return status;
+}
+
+/****************************************************************************
+ Check if an unlock overlaps a pending lock.
+****************************************************************************/
+
+static BOOL brl_pending_overlap(struct lock_struct *lock, struct lock_struct *pend_lock)
+{
+ if ((lock->start <= pend_lock->start) && (lock->start + lock->size > pend_lock->start))
+ return True;
+ if ((lock->start >= pend_lock->start) && (lock->start <= pend_lock->start + pend_lock->size))
+ return True;
+ return False;
+}
+
+/****************************************************************************
+ Unlock a range of bytes.
+****************************************************************************/
+
+BOOL brl_unlock(SMB_DEV_T dev, SMB_INO_T ino, int fnum,
+ uint16 smbpid, pid_t pid, uint16 tid,
+ br_off start, br_off size,
+ BOOL remove_pending_locks_only,
+ void (*pre_unlock_fn)(void *),
+ void *pre_unlock_data)
+{
+ TDB_DATA kbuf, dbuf;
+ int count, i, j;
+ struct lock_struct *locks;
+ struct lock_context context;
+
+ kbuf = locking_key(dev,ino);
+
+ dbuf.dptr = NULL;
+
+ tdb_chainlock(tdb, kbuf);
+ dbuf = tdb_fetch(tdb, kbuf);
+
+ if (!dbuf.dptr) {
+ DEBUG(10,("brl_unlock: tdb_fetch failed !\n"));
+ goto fail;
+ }
+
+ context.smbpid = smbpid;
+ context.pid = pid;
+ context.tid = tid;
+
+ /* there are existing locks - find a match */
+ locks = (struct lock_struct *)dbuf.dptr;
+ count = dbuf.dsize / sizeof(*locks);
+
+#if ZERO_ZERO
+ for (i=0; i<count; i++) {
+ struct lock_struct *lock = &locks[i];
+
+ if (lock->lock_type == WRITE_LOCK &&
+ brl_same_context(&lock->context, &context) &&
+ lock->fnum == fnum &&
+ lock->start == start &&
+ lock->size == size) {
+
+ if (pre_unlock_fn)
+ (*pre_unlock_fn)(pre_unlock_data);
+
+ /* found it - delete it */
+ if (count == 1) {
+ tdb_delete(tdb, kbuf);
+ } else {
+ if (i < count-1) {
+ memmove(&locks[i], &locks[i+1],
+ sizeof(*locks)*((count-1) - i));
+ }
+ dbuf.dsize -= sizeof(*locks);
+ tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
+ }
+
+ SAFE_FREE(dbuf.dptr);
+ tdb_chainunlock(tdb, kbuf);
+ return True;
+ }
+ }
+#endif
+
+ locks = (struct lock_struct *)dbuf.dptr;
+ count = dbuf.dsize / sizeof(*locks);
+ for (i=0; i<count; i++) {
+ struct lock_struct *lock = &locks[i];
+
+ if (brl_same_context(&lock->context, &context) &&
+ lock->fnum == fnum &&
+ lock->start == start &&
+ lock->size == size) {
+
+ if (remove_pending_locks_only && lock->lock_type != PENDING_LOCK)
+ continue;
+
+ if (lock->lock_type != PENDING_LOCK) {
+
+ /* Do any POSIX unlocks needed. */
+ if (pre_unlock_fn)
+ (*pre_unlock_fn)(pre_unlock_data);
+
+ /* Send unlock messages to any pending waiters that overlap. */
+ for (j=0; j<count; j++) {
+ struct lock_struct *pend_lock = &locks[j];
+
+ /* Ignore non-pending locks. */
+ if (pend_lock->lock_type != PENDING_LOCK)
+ continue;
+
+ /* We could send specific lock info here... */
+ if (brl_pending_overlap(lock, pend_lock)) {
+ DEBUG(10,("brl_unlock: sending unlock message to pid %u\n",
+ (unsigned int)pend_lock->context.pid ));
+
+ message_send_pid(pend_lock->context.pid,
+ MSG_SMB_UNLOCK,
+ NULL, 0, True);
+ }
+ }
+ }
+
+ /* found it - delete it */
+ if (count == 1) {
+ tdb_delete(tdb, kbuf);
+ } else {
+ if (i < count-1) {
+ memmove(&locks[i], &locks[i+1],
+ sizeof(*locks)*((count-1) - i));
+ }
+ dbuf.dsize -= sizeof(*locks);
+ tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
+ }
+
+ SAFE_FREE(dbuf.dptr);
+ tdb_chainunlock(tdb, kbuf);
+ return True;
+ }
+ }
+
+ /* we didn't find it */
+
+ fail:
+ SAFE_FREE(dbuf.dptr);
+ tdb_chainunlock(tdb, kbuf);
+ return False;
+}
+
+
+/****************************************************************************
+ Test if we could add a lock if we wanted to.
+****************************************************************************/
+
+BOOL brl_locktest(SMB_DEV_T dev, SMB_INO_T ino, int fnum,
+ uint16 smbpid, pid_t pid, uint16 tid,
+ br_off start, br_off size,
+ enum brl_type lock_type, int check_self)
+{
+ TDB_DATA kbuf, dbuf;
+ int count, i;
+ struct lock_struct lock, *locks;
+
+ kbuf = locking_key(dev,ino);
+
+ dbuf.dptr = NULL;
+
+ tdb_chainlock(tdb, kbuf);
+ dbuf = tdb_fetch(tdb, kbuf);
+
+ lock.context.smbpid = smbpid;
+ lock.context.pid = pid;
+ lock.context.tid = tid;
+ lock.start = start;
+ lock.size = size;
+ lock.fnum = fnum;
+ lock.lock_type = lock_type;
+
+ if (dbuf.dptr) {
+ /* there are existing locks - make sure they don't conflict */
+ locks = (struct lock_struct *)dbuf.dptr;
+ count = dbuf.dsize / sizeof(*locks);
+ for (i=0; i<count; i++) {
+ if (check_self) {
+ if (brl_conflict(&locks[i], &lock))
+ goto fail;
+ } else {
+ /*
+ * Our own locks don't conflict.
+ */
+ if (brl_conflict_other(&locks[i], &lock))
+ goto fail;
+ }
+ }
+ }
+
+ /* no conflicts - we could have added it */
+ SAFE_FREE(dbuf.dptr);
+ tdb_chainunlock(tdb, kbuf);
+ return True;
+
+ fail:
+ SAFE_FREE(dbuf.dptr);
+ tdb_chainunlock(tdb, kbuf);
+ return False;
+}
+
+/****************************************************************************
+ Remove any locks associated with a open file.
+****************************************************************************/
+
+void brl_close(SMB_DEV_T dev, SMB_INO_T ino, pid_t pid, int tid, int fnum)
+{
+ TDB_DATA kbuf, dbuf;
+ int count, i, j, dcount=0;
+ struct lock_struct *locks;
+
+ kbuf = locking_key(dev,ino);
+
+ dbuf.dptr = NULL;
+
+ tdb_chainlock(tdb, kbuf);
+ dbuf = tdb_fetch(tdb, kbuf);
+
+ if (!dbuf.dptr) goto fail;
+
+ /* there are existing locks - remove any for this fnum */
+ locks = (struct lock_struct *)dbuf.dptr;
+ count = dbuf.dsize / sizeof(*locks);
+
+ for (i=0; i<count; i++) {
+ struct lock_struct *lock = &locks[i];
+
+ if (lock->context.tid == tid &&
+ lock->context.pid == pid &&
+ lock->fnum == fnum) {
+
+ /* Send unlock messages to any pending waiters that overlap. */
+ for (j=0; j<count; j++) {
+ struct lock_struct *pend_lock = &locks[j];
+
+ /* Ignore our own or non-pending locks. */
+ if (pend_lock->lock_type != PENDING_LOCK)
+ continue;
+
+ if (pend_lock->context.tid == tid &&
+ pend_lock->context.pid == pid &&
+ pend_lock->fnum == fnum)
+ continue;
+
+ /* We could send specific lock info here... */
+ if (brl_pending_overlap(lock, pend_lock))
+ message_send_pid(pend_lock->context.pid,
+ MSG_SMB_UNLOCK,
+ NULL, 0, True);
+ }
+
+ /* found it - delete it */
+ if (count > 1 && i < count-1) {
+ memmove(&locks[i], &locks[i+1],
+ sizeof(*locks)*((count-1) - i));
+ }
+ count--;
+ i--;
+ dcount++;
+ }
+ }
+
+ if (count == 0) {
+ tdb_delete(tdb, kbuf);
+ } else if (count < (dbuf.dsize / sizeof(*locks))) {
+ dbuf.dsize -= dcount * sizeof(*locks);
+ tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
+ }
+
+ /* we didn't find it */
+ fail:
+ SAFE_FREE(dbuf.dptr);
+ tdb_chainunlock(tdb, kbuf);
+}
+
+/****************************************************************************
+ Traverse the whole database with this function, calling traverse_callback
+ on each lock.
+****************************************************************************/
+
+static int traverse_fn(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
+{
+ struct lock_struct *locks;
+ struct lock_key *key;
+ int i;
+
+ BRLOCK_FN(traverse_callback) = (BRLOCK_FN_CAST())state;
+
+ locks = (struct lock_struct *)dbuf.dptr;
+ key = (struct lock_key *)kbuf.dptr;
+
+ for (i=0;i<dbuf.dsize/sizeof(*locks);i++) {
+ traverse_callback(key->device, key->inode,
+ locks[i].context.pid,
+ locks[i].lock_type,
+ locks[i].start,
+ locks[i].size);
+ }
+ return 0;
+}
+
+/*******************************************************************
+ Call the specified function on each lock in the database.
+********************************************************************/
+
+int brl_forall(BRLOCK_FN(fn))
+{
+ if (!tdb) return 0;
+ return tdb_traverse(tdb, traverse_fn, (void *)fn);
+}
diff --git a/source/locking/locking.c b/source/locking/locking.c
new file mode 100644
index 00000000000..42036cc70cf
--- /dev/null
+++ b/source/locking/locking.c
@@ -0,0 +1,881 @@
+/*
+ Unix SMB/CIFS implementation.
+ Locking functions
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Jeremy Allison 1992-2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Revision History:
+
+ 12 aug 96: Erik.Devriendt@te6.siemens.be
+ added support for shared memory implementation of share mode locking
+
+ May 1997. Jeremy Allison (jallison@whistle.com). Modified share mode
+ locking to deal with multiple share modes per open file.
+
+ September 1997. Jeremy Allison (jallison@whistle.com). Added oplock
+ support.
+
+ rewrtten completely to use new tdb code. Tridge, Dec '99
+
+ Added POSIX locking support. Jeremy Allison (jeremy@valinux.com), Apr. 2000.
+*/
+
+#include "includes.h"
+uint16 global_smbpid;
+
+/* the locking database handle */
+static TDB_CONTEXT *tdb;
+
+/****************************************************************************
+ Debugging aid :-).
+****************************************************************************/
+
+static const char *lock_type_name(enum brl_type lock_type)
+{
+ return (lock_type == READ_LOCK) ? "READ" : "WRITE";
+}
+
+/****************************************************************************
+ Utility function called to see if a file region is locked.
+ If check_self is True, then checks on our own fd with the same locking context
+ are still made. If check_self is False, then checks are not made on our own fd
+ with the same locking context are not made.
+****************************************************************************/
+
+BOOL is_locked(files_struct *fsp,connection_struct *conn,
+ SMB_BIG_UINT count,SMB_BIG_UINT offset,
+ enum brl_type lock_type, BOOL check_self)
+{
+ int snum = SNUM(conn);
+ BOOL ret;
+
+ if (count == 0)
+ return(False);
+
+ if (!lp_locking(snum) || !lp_strict_locking(snum))
+ return(False);
+
+ ret = !brl_locktest(fsp->dev, fsp->inode, fsp->fnum,
+ global_smbpid, sys_getpid(), conn->cnum,
+ offset, count, lock_type, check_self);
+
+ DEBUG(10,("is_locked: brl start=%.0f len=%.0f %s for file %s\n",
+ (double)offset, (double)count, ret ? "locked" : "unlocked",
+ fsp->fsp_name ));
+
+ /*
+ * There is no lock held by an SMB daemon, check to
+ * see if there is a POSIX lock from a UNIX or NFS process.
+ */
+
+ if(!ret && lp_posix_locking(snum)) {
+ ret = is_posix_locked(fsp, offset, count, lock_type);
+
+ DEBUG(10,("is_locked: posix start=%.0f len=%.0f %s for file %s\n",
+ (double)offset, (double)count, ret ? "locked" : "unlocked",
+ fsp->fsp_name ));
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ Utility function called by locking requests.
+****************************************************************************/
+
+static NTSTATUS do_lock(files_struct *fsp,connection_struct *conn, uint16 lock_pid,
+ SMB_BIG_UINT count,SMB_BIG_UINT offset,enum brl_type lock_type, BOOL *my_lock_ctx)
+{
+ NTSTATUS status = NT_STATUS_LOCK_NOT_GRANTED;
+
+ if (!lp_locking(SNUM(conn)))
+ return NT_STATUS_OK;
+
+ /* NOTE! 0 byte long ranges ARE allowed and should be stored */
+
+ DEBUG(10,("do_lock: lock type %s start=%.0f len=%.0f requested for file %s\n",
+ lock_type_name(lock_type), (double)offset, (double)count, fsp->fsp_name ));
+
+ if (OPEN_FSP(fsp) && fsp->can_lock && (fsp->conn == conn)) {
+ status = brl_lock(fsp->dev, fsp->inode, fsp->fnum,
+ lock_pid, sys_getpid(), conn->cnum,
+ offset, count,
+ lock_type, my_lock_ctx);
+
+ if (NT_STATUS_IS_OK(status) && lp_posix_locking(SNUM(conn))) {
+
+ /*
+ * Try and get a POSIX lock on this range.
+ * Note that this is ok if it is a read lock
+ * overlapping on a different fd. JRA.
+ */
+
+ if (!set_posix_lock(fsp, offset, count, lock_type)) {
+ if (errno == EACCES || errno == EAGAIN)
+ status = NT_STATUS_FILE_LOCK_CONFLICT;
+ else
+ status = map_nt_error_from_unix(errno);
+
+ /*
+ * We failed to map - we must now remove the brl
+ * lock entry.
+ */
+ (void)brl_unlock(fsp->dev, fsp->inode, fsp->fnum,
+ lock_pid, sys_getpid(), conn->cnum,
+ offset, count, False,
+ NULL, NULL);
+ }
+ }
+ }
+
+ return status;
+}
+
+/****************************************************************************
+ Utility function called by locking requests. This is *DISGUSTING*. It also
+ appears to be "What Windows Does" (tm). Andrew, ever wonder why Windows 2000
+ is so slow on the locking tests...... ? This is the reason. Much though I hate
+ it, we need this. JRA.
+****************************************************************************/
+
+NTSTATUS do_lock_spin(files_struct *fsp,connection_struct *conn, uint16 lock_pid,
+ SMB_BIG_UINT count,SMB_BIG_UINT offset,enum brl_type lock_type, BOOL *my_lock_ctx)
+{
+ int j, maxj = lp_lock_spin_count();
+ int sleeptime = lp_lock_sleep_time();
+ NTSTATUS status, ret;
+
+ if (maxj <= 0)
+ maxj = 1;
+
+ ret = NT_STATUS_OK; /* to keep dumb compilers happy */
+
+ for (j = 0; j < maxj; j++) {
+ status = do_lock(fsp, conn, lock_pid, count, offset, lock_type, my_lock_ctx);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_LOCK_NOT_GRANTED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_FILE_LOCK_CONFLICT)) {
+ return status;
+ }
+ /* if we do fail then return the first error code we got */
+ if (j == 0) {
+ ret = status;
+ /* Don't spin if we blocked ourselves. */
+ if (*my_lock_ctx)
+ return ret;
+ }
+ if (sleeptime)
+ sys_usleep(sleeptime);
+ }
+ return ret;
+}
+
+/* Struct passed to brl_unlock. */
+struct posix_unlock_data_struct {
+ files_struct *fsp;
+ SMB_BIG_UINT offset;
+ SMB_BIG_UINT count;
+};
+
+/****************************************************************************
+ Function passed to brl_unlock to allow POSIX unlock to be done first.
+****************************************************************************/
+
+static void posix_unlock(void *pre_data)
+{
+ struct posix_unlock_data_struct *pdata = (struct posix_unlock_data_struct *)pre_data;
+
+ if (lp_posix_locking(SNUM(pdata->fsp->conn)))
+ release_posix_lock(pdata->fsp, pdata->offset, pdata->count);
+}
+
+/****************************************************************************
+ Utility function called by unlocking requests.
+****************************************************************************/
+
+NTSTATUS do_unlock(files_struct *fsp,connection_struct *conn, uint16 lock_pid,
+ SMB_BIG_UINT count,SMB_BIG_UINT offset)
+{
+ BOOL ok = False;
+ struct posix_unlock_data_struct posix_data;
+
+ if (!lp_locking(SNUM(conn)))
+ return NT_STATUS_OK;
+
+ if (!OPEN_FSP(fsp) || !fsp->can_lock || (fsp->conn != conn)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ DEBUG(10,("do_unlock: unlock start=%.0f len=%.0f requested for file %s\n",
+ (double)offset, (double)count, fsp->fsp_name ));
+
+ /*
+ * Remove the existing lock record from the tdb lockdb
+ * before looking at POSIX locks. If this record doesn't
+ * match then don't bother looking to remove POSIX locks.
+ */
+
+ posix_data.fsp = fsp;
+ posix_data.offset = offset;
+ posix_data.count = count;
+
+ ok = brl_unlock(fsp->dev, fsp->inode, fsp->fnum,
+ lock_pid, sys_getpid(), conn->cnum, offset, count,
+ False, posix_unlock, (void *)&posix_data);
+
+ if (!ok) {
+ DEBUG(10,("do_unlock: returning ERRlock.\n" ));
+ return NT_STATUS_RANGE_NOT_LOCKED;
+ }
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Remove any locks on this fd. Called from file_close().
+****************************************************************************/
+
+void locking_close_file(files_struct *fsp)
+{
+ pid_t pid = sys_getpid();
+
+ if (!lp_locking(SNUM(fsp->conn)))
+ return;
+
+ /*
+ * Just release all the brl locks, no need to release individually.
+ */
+
+ brl_close(fsp->dev, fsp->inode, pid, fsp->conn->cnum, fsp->fnum);
+
+ if(lp_posix_locking(SNUM(fsp->conn))) {
+
+ /*
+ * Release all the POSIX locks.
+ */
+ posix_locking_close_file(fsp);
+
+ }
+}
+
+/****************************************************************************
+ Initialise the locking functions.
+****************************************************************************/
+
+static int open_read_only;
+
+BOOL locking_init(int read_only)
+{
+ brl_init(read_only);
+
+ if (tdb)
+ return True;
+
+ tdb = tdb_open_ex(lock_path("locking.tdb"),
+ 0, TDB_DEFAULT|(read_only?0x0:TDB_CLEAR_IF_FIRST),
+ read_only?O_RDONLY:O_RDWR|O_CREAT,
+ 0644, smbd_tdb_log);
+
+ if (!tdb) {
+ DEBUG(0,("ERROR: Failed to initialise locking database\n"));
+ return False;
+ }
+
+ if (!posix_locking_init(read_only))
+ return False;
+
+ open_read_only = read_only;
+
+ return True;
+}
+
+/*******************************************************************
+ Deinitialize the share_mode management.
+******************************************************************/
+
+BOOL locking_end(void)
+{
+
+ brl_shutdown(open_read_only);
+ if (tdb) {
+
+ if (tdb_close(tdb) != 0)
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Form a static locking key for a dev/inode pair.
+******************************************************************/
+
+static TDB_DATA locking_key(SMB_DEV_T dev, SMB_INO_T inode)
+{
+ static struct locking_key key;
+ TDB_DATA kbuf;
+
+ memset(&key, '\0', sizeof(key));
+ key.dev = dev;
+ key.inode = inode;
+ kbuf.dptr = (char *)&key;
+ kbuf.dsize = sizeof(key);
+ return kbuf;
+}
+
+static TDB_DATA locking_key_fsp(files_struct *fsp)
+{
+ return locking_key(fsp->dev, fsp->inode);
+}
+
+/*******************************************************************
+ Lock a hash bucket entry.
+******************************************************************/
+
+BOOL lock_share_entry(connection_struct *conn,
+ SMB_DEV_T dev, SMB_INO_T inode)
+{
+ return tdb_chainlock(tdb, locking_key(dev, inode)) == 0;
+}
+
+/*******************************************************************
+ Unlock a hash bucket entry.
+******************************************************************/
+
+void unlock_share_entry(connection_struct *conn,
+ SMB_DEV_T dev, SMB_INO_T inode)
+{
+ tdb_chainunlock(tdb, locking_key(dev, inode));
+}
+
+/*******************************************************************
+ Lock a hash bucket entry. use a fsp for convenience
+******************************************************************/
+
+BOOL lock_share_entry_fsp(files_struct *fsp)
+{
+ return tdb_chainlock(tdb, locking_key(fsp->dev, fsp->inode)) == 0;
+}
+
+/*******************************************************************
+ Unlock a hash bucket entry.
+******************************************************************/
+
+void unlock_share_entry_fsp(files_struct *fsp)
+{
+ tdb_chainunlock(tdb, locking_key(fsp->dev, fsp->inode));
+}
+
+/*******************************************************************
+ Print out a share mode.
+********************************************************************/
+
+char *share_mode_str(int num, share_mode_entry *e)
+{
+ static pstring share_str;
+
+ slprintf(share_str, sizeof(share_str)-1, "share_mode_entry[%d]: \
+pid = %lu, share_mode = 0x%x, desired_access = 0x%x, port = 0x%x, type= 0x%x, file_id = %lu, dev = 0x%x, inode = %.0f",
+ num, (unsigned long)e->pid, e->share_mode, (unsigned int)e->desired_access, e->op_port, e->op_type, e->share_file_id,
+ (unsigned int)e->dev, (double)e->inode );
+
+ return share_str;
+}
+
+/*******************************************************************
+ Print out a share mode table.
+********************************************************************/
+
+static void print_share_mode_table(struct locking_data *data)
+{
+ int num_share_modes = data->u.num_share_mode_entries;
+ share_mode_entry *shares = (share_mode_entry *)(data + 1);
+ int i;
+
+ for (i = 0; i < num_share_modes; i++) {
+ share_mode_entry *entry_p = &shares[i];
+ DEBUG(10,("print_share_mode_table: %s\n", share_mode_str(i, entry_p) ));
+ }
+}
+
+/*******************************************************************
+ Get all share mode entries for a dev/inode pair.
+********************************************************************/
+
+int get_share_modes(connection_struct *conn,
+ SMB_DEV_T dev, SMB_INO_T inode,
+ share_mode_entry **pp_shares)
+{
+ TDB_DATA dbuf;
+ struct locking_data *data;
+ int num_share_modes;
+ share_mode_entry *shares = NULL;
+ TDB_DATA key = locking_key(dev, inode);
+ *pp_shares = NULL;
+
+ dbuf = tdb_fetch(tdb, key);
+ if (!dbuf.dptr)
+ return 0;
+
+ data = (struct locking_data *)dbuf.dptr;
+ num_share_modes = data->u.num_share_mode_entries;
+ if(num_share_modes) {
+ int i;
+ int del_count = 0;
+
+ shares = (share_mode_entry *)memdup(dbuf.dptr + sizeof(*data),
+ num_share_modes * sizeof(share_mode_entry));
+
+ if (!shares) {
+ SAFE_FREE(dbuf.dptr);
+ return 0;
+ }
+
+ /*
+ * Ensure that each entry has a real process attached.
+ */
+
+ for (i = 0; i < num_share_modes; ) {
+ share_mode_entry *entry_p = &shares[i];
+ if (process_exists(entry_p->pid)) {
+ DEBUG(10,("get_share_modes: %s\n", share_mode_str(i, entry_p) ));
+ i++;
+ } else {
+ DEBUG(10,("get_share_modes: deleted %s\n", share_mode_str(i, entry_p) ));
+ memcpy( &shares[i], &shares[i+1],
+ sizeof(share_mode_entry) * (num_share_modes - i - 1));
+ num_share_modes--;
+ del_count++;
+ }
+ }
+
+ /* Did we delete any ? If so, re-store in tdb. */
+ if (del_count) {
+ data->u.num_share_mode_entries = num_share_modes;
+
+ if (num_share_modes)
+ memcpy(dbuf.dptr + sizeof(*data), shares,
+ num_share_modes * sizeof(share_mode_entry));
+
+ /* The record has shrunk a bit */
+ dbuf.dsize -= del_count * sizeof(share_mode_entry);
+
+ if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) {
+ SAFE_FREE(shares);
+ SAFE_FREE(dbuf.dptr);
+ return 0;
+ }
+ }
+ }
+
+ SAFE_FREE(dbuf.dptr);
+ *pp_shares = shares;
+ return num_share_modes;
+}
+
+/*******************************************************************
+ Fill a share mode entry.
+********************************************************************/
+
+static void fill_share_mode(char *p, files_struct *fsp, uint16 port, uint16 op_type)
+{
+ share_mode_entry *e = (share_mode_entry *)p;
+ void *x = &e->time; /* Needed to force alignment. p may not be aligned.... */
+
+ memset(e, '\0', sizeof(share_mode_entry));
+ e->pid = sys_getpid();
+ e->share_mode = fsp->share_mode;
+ e->desired_access = fsp->desired_access;
+ e->op_port = port;
+ e->op_type = op_type;
+ memcpy(x, &fsp->open_time, sizeof(struct timeval));
+ e->share_file_id = fsp->file_id;
+ e->dev = fsp->dev;
+ e->inode = fsp->inode;
+}
+
+/*******************************************************************
+ Check if two share mode entries are identical, ignoring oplock
+ and port info and desired_access.
+********************************************************************/
+
+BOOL share_modes_identical( share_mode_entry *e1, share_mode_entry *e2)
+{
+#if 1 /* JRA PARANOIA TEST - REMOVE LATER */
+ if (e1->pid == e2->pid &&
+ e1->share_file_id == e2->share_file_id &&
+ e1->dev == e2->dev &&
+ e1->inode == e2->inode &&
+ (e1->share_mode & ~DELETE_ON_CLOSE_FLAG) != (e2->share_mode & ~DELETE_ON_CLOSE_FLAG)) {
+ DEBUG(0,("PANIC: share_modes_identical: share_mode missmatch (e1 = %u, e2 = %u). Logic error.\n",
+ (unsigned int)(e1->share_mode & ~DELETE_ON_CLOSE_FLAG),
+ (unsigned int)(e2->share_mode & ~DELETE_ON_CLOSE_FLAG) ));
+ smb_panic("PANIC: share_modes_identical logic error.\n");
+ }
+#endif
+
+ return (e1->pid == e2->pid &&
+ (e1->share_mode & ~DELETE_ON_CLOSE_FLAG) == (e2->share_mode & ~DELETE_ON_CLOSE_FLAG) &&
+ e1->dev == e2->dev &&
+ e1->inode == e2->inode &&
+ e1->share_file_id == e2->share_file_id );
+}
+
+/*******************************************************************
+ Delete a specific share mode. Return the number
+ of entries left, and a memdup'ed copy of the entry deleted (if required).
+ Ignore if no entry deleted.
+********************************************************************/
+
+ssize_t del_share_entry( SMB_DEV_T dev, SMB_INO_T inode,
+ share_mode_entry *entry, share_mode_entry **ppse)
+{
+ TDB_DATA dbuf;
+ struct locking_data *data;
+ int i, del_count=0;
+ share_mode_entry *shares;
+ ssize_t count = 0;
+ TDB_DATA key = locking_key(dev, inode);
+
+ if (ppse)
+ *ppse = NULL;
+
+ /* read in the existing share modes */
+ dbuf = tdb_fetch(tdb, key);
+ if (!dbuf.dptr)
+ return -1;
+
+ data = (struct locking_data *)dbuf.dptr;
+ shares = (share_mode_entry *)(dbuf.dptr + sizeof(*data));
+
+ /*
+ * Find any with this pid and delete it
+ * by overwriting with the rest of the data
+ * from the record.
+ */
+
+ DEBUG(10,("del_share_entry: num_share_modes = %d\n", data->u.num_share_mode_entries ));
+
+ for (i=0;i<data->u.num_share_mode_entries;) {
+ if (share_modes_identical(&shares[i], entry)) {
+ DEBUG(10,("del_share_entry: deleted %s\n",
+ share_mode_str(i, &shares[i]) ));
+ if (ppse)
+ *ppse = memdup(&shares[i], sizeof(*shares));
+ data->u.num_share_mode_entries--;
+ memmove(&shares[i], &shares[i+1],
+ dbuf.dsize - (sizeof(*data) + (i+1)*sizeof(*shares)));
+ del_count++;
+
+ DEBUG(10,("del_share_entry: deleting entry %d\n", i ));
+
+ } else {
+ i++;
+ }
+ }
+
+ if (del_count) {
+ /* the record may have shrunk a bit */
+ dbuf.dsize -= del_count * sizeof(*shares);
+
+ count = (ssize_t)data->u.num_share_mode_entries;
+
+ /* store it back in the database */
+ if (data->u.num_share_mode_entries == 0) {
+ if (tdb_delete(tdb, key) == -1)
+ count = -1;
+ } else {
+ if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1)
+ count = -1;
+ }
+ }
+ DEBUG(10,("del_share_entry: Remaining table.\n"));
+ print_share_mode_table((struct locking_data *)dbuf.dptr);
+ SAFE_FREE(dbuf.dptr);
+ return count;
+}
+
+/*******************************************************************
+ Del the share mode of a file for this process. Return the number
+ of entries left, and a memdup'ed copy of the entry deleted.
+********************************************************************/
+
+ssize_t del_share_mode(files_struct *fsp, share_mode_entry **ppse)
+{
+ share_mode_entry entry;
+
+ /*
+ * Fake up a share_mode_entry for comparisons.
+ */
+
+ fill_share_mode((char *)&entry, fsp, 0, 0);
+ return del_share_entry(fsp->dev, fsp->inode, &entry, ppse);
+}
+
+/*******************************************************************
+ Set the share mode of a file. Return False on fail, True on success.
+********************************************************************/
+
+BOOL set_share_mode(files_struct *fsp, uint16 port, uint16 op_type)
+{
+ TDB_DATA dbuf;
+ struct locking_data *data;
+ char *p=NULL;
+ int size;
+ TDB_DATA key = locking_key_fsp(fsp);
+ BOOL ret = True;
+
+ /* read in the existing share modes if any */
+ dbuf = tdb_fetch(tdb, key);
+ if (!dbuf.dptr) {
+ size_t offset;
+ /* we'll need to create a new record */
+ pstring fname;
+
+ pstrcpy(fname, fsp->conn->connectpath);
+ pstrcat(fname, "/");
+ pstrcat(fname, fsp->fsp_name);
+
+ size = sizeof(*data) + sizeof(share_mode_entry) + strlen(fname) + 1;
+ p = (char *)malloc(size);
+ if (!p)
+ return False;
+ data = (struct locking_data *)p;
+ data->u.num_share_mode_entries = 1;
+
+ DEBUG(10,("set_share_mode: creating entry for file %s. num_share_modes = 1\n",
+ fsp->fsp_name ));
+
+ offset = sizeof(*data) + sizeof(share_mode_entry);
+ safe_strcpy(p + offset, fname, size - offset - 1);
+ fill_share_mode(p + sizeof(*data), fsp, port, op_type);
+ dbuf.dptr = p;
+ dbuf.dsize = size;
+ if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1)
+ ret = False;
+
+ print_share_mode_table((struct locking_data *)p);
+
+ SAFE_FREE(p);
+ return ret;
+ }
+
+ /* we're adding to an existing entry - this is a bit fiddly */
+ data = (struct locking_data *)dbuf.dptr;
+
+ data->u.num_share_mode_entries++;
+
+ DEBUG(10,("set_share_mode: adding entry for file %s. new num_share_modes = %d\n",
+ fsp->fsp_name, data->u.num_share_mode_entries ));
+
+ size = dbuf.dsize + sizeof(share_mode_entry);
+ p = malloc(size);
+ if (!p) {
+ SAFE_FREE(dbuf.dptr);
+ return False;
+ }
+ memcpy(p, dbuf.dptr, sizeof(*data));
+ fill_share_mode(p + sizeof(*data), fsp, port, op_type);
+ memcpy(p + sizeof(*data) + sizeof(share_mode_entry), dbuf.dptr + sizeof(*data),
+ dbuf.dsize - sizeof(*data));
+ SAFE_FREE(dbuf.dptr);
+ dbuf.dptr = p;
+ dbuf.dsize = size;
+ if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1)
+ ret = False;
+ print_share_mode_table((struct locking_data *)p);
+ SAFE_FREE(p);
+ return ret;
+}
+
+/*******************************************************************
+ A generic in-place modification call for share mode entries.
+********************************************************************/
+
+static BOOL mod_share_mode( SMB_DEV_T dev, SMB_INO_T inode, share_mode_entry *entry,
+ void (*mod_fn)(share_mode_entry *, SMB_DEV_T, SMB_INO_T, void *),
+ void *param)
+{
+ TDB_DATA dbuf;
+ struct locking_data *data;
+ int i;
+ share_mode_entry *shares;
+ BOOL need_store=False;
+ BOOL ret = True;
+ TDB_DATA key = locking_key(dev, inode);
+
+ /* read in the existing share modes */
+ dbuf = tdb_fetch(tdb, key);
+ if (!dbuf.dptr)
+ return False;
+
+ data = (struct locking_data *)dbuf.dptr;
+ shares = (share_mode_entry *)(dbuf.dptr + sizeof(*data));
+
+ /* find any with our pid and call the supplied function */
+ for (i=0;i<data->u.num_share_mode_entries;i++) {
+ if (share_modes_identical(entry, &shares[i])) {
+ mod_fn(&shares[i], dev, inode, param);
+ need_store=True;
+ }
+ }
+
+ /* if the mod fn was called then store it back */
+ if (need_store) {
+ if (data->u.num_share_mode_entries == 0) {
+ if (tdb_delete(tdb, key) == -1)
+ ret = False;
+ } else {
+ if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1)
+ ret = False;
+ }
+ }
+
+ SAFE_FREE(dbuf.dptr);
+ return ret;
+}
+
+/*******************************************************************
+ Static function that actually does the work for the generic function
+ below.
+********************************************************************/
+
+static void remove_share_oplock_fn(share_mode_entry *entry, SMB_DEV_T dev, SMB_INO_T inode,
+ void *param)
+{
+ DEBUG(10,("remove_share_oplock_fn: removing oplock info for entry dev=%x ino=%.0f\n",
+ (unsigned int)dev, (double)inode ));
+ /* Delete the oplock info. */
+ entry->op_port = 0;
+ entry->op_type = NO_OPLOCK;
+}
+
+/*******************************************************************
+ Remove an oplock port and mode entry from a share mode.
+********************************************************************/
+
+BOOL remove_share_oplock(files_struct *fsp)
+{
+ share_mode_entry entry;
+ /*
+ * Fake up an entry for comparisons...
+ */
+ fill_share_mode((char *)&entry, fsp, 0, 0);
+ return mod_share_mode(fsp->dev, fsp->inode, &entry, remove_share_oplock_fn, NULL);
+}
+
+/*******************************************************************
+ Static function that actually does the work for the generic function
+ below.
+********************************************************************/
+
+static void downgrade_share_oplock_fn(share_mode_entry *entry, SMB_DEV_T dev, SMB_INO_T inode,
+ void *param)
+{
+ DEBUG(10,("downgrade_share_oplock_fn: downgrading oplock info for entry dev=%x ino=%.0f\n",
+ (unsigned int)dev, (double)inode ));
+ entry->op_type = LEVEL_II_OPLOCK;
+}
+
+/*******************************************************************
+ Downgrade a oplock type from exclusive to level II.
+********************************************************************/
+
+BOOL downgrade_share_oplock(files_struct *fsp)
+{
+ share_mode_entry entry;
+ /*
+ * Fake up an entry for comparisons...
+ */
+ fill_share_mode((char *)&entry, fsp, 0, 0);
+ return mod_share_mode(fsp->dev, fsp->inode, &entry, downgrade_share_oplock_fn, NULL);
+}
+
+/*******************************************************************
+ Get/Set the delete on close flag in a set of share modes.
+ Return False on fail, True on success.
+********************************************************************/
+
+BOOL modify_delete_flag( SMB_DEV_T dev, SMB_INO_T inode, BOOL delete_on_close)
+{
+ TDB_DATA dbuf;
+ struct locking_data *data;
+ int i;
+ share_mode_entry *shares;
+ TDB_DATA key = locking_key(dev, inode);
+
+ /* read in the existing share modes */
+ dbuf = tdb_fetch(tdb, key);
+ if (!dbuf.dptr)
+ return False;
+
+ data = (struct locking_data *)dbuf.dptr;
+ shares = (share_mode_entry *)(dbuf.dptr + sizeof(*data));
+
+ /* Set/Unset the delete on close element. */
+ for (i=0;i<data->u.num_share_mode_entries;i++,shares++) {
+ shares->share_mode = (delete_on_close ?
+ (shares->share_mode | DELETE_ON_CLOSE_FLAG) :
+ (shares->share_mode & ~DELETE_ON_CLOSE_FLAG) );
+ }
+
+ /* store it back */
+ if (data->u.num_share_mode_entries) {
+ if (tdb_store(tdb, key, dbuf, TDB_REPLACE)==-1) {
+ SAFE_FREE(dbuf.dptr);
+ return False;
+ }
+ }
+
+ SAFE_FREE(dbuf.dptr);
+ return True;
+}
+
+/****************************************************************************
+ Traverse the whole database with this function, calling traverse_callback
+ on each share mode
+****************************************************************************/
+
+static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
+ void* state)
+{
+ struct locking_data *data;
+ share_mode_entry *shares;
+ char *name;
+ int i;
+
+ SHAREMODE_FN(traverse_callback) = (SHAREMODE_FN_CAST())state;
+
+ data = (struct locking_data *)dbuf.dptr;
+ shares = (share_mode_entry *)(dbuf.dptr + sizeof(*data));
+ name = dbuf.dptr + sizeof(*data) + data->u.num_share_mode_entries*sizeof(*shares);
+
+ for (i=0;i<data->u.num_share_mode_entries;i++) {
+ traverse_callback(&shares[i], name);
+ }
+ return 0;
+}
+
+/*******************************************************************
+ Call the specified function on each entry under management by the
+ share mode system.
+********************************************************************/
+
+int share_mode_forall(SHAREMODE_FN(fn))
+{
+ if (!tdb)
+ return 0;
+ return tdb_traverse(tdb, traverse_fn, (void*)fn);
+}
diff --git a/source/locking/posix.c b/source/locking/posix.c
new file mode 100644
index 00000000000..6173c80b2fd
--- /dev/null
+++ b/source/locking/posix.c
@@ -0,0 +1,1312 @@
+/*
+ Unix SMB/CIFS implementation.
+ Locking functions
+ Copyright (C) Jeremy Allison 1992-2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Revision History:
+
+ POSIX locking support. Jeremy Allison (jeremy@valinux.com), Apr. 2000.
+*/
+
+#include "includes.h"
+
+/*
+ * The POSIX locking database handle.
+ */
+
+static TDB_CONTEXT *posix_lock_tdb;
+
+/*
+ * The pending close database handle.
+ */
+
+static TDB_CONTEXT *posix_pending_close_tdb;
+
+/*
+ * The data in POSIX lock records is an unsorted linear array of these
+ * records. It is unnecessary to store the count as tdb provides the
+ * size of the record.
+ */
+
+struct posix_lock {
+ int fd;
+ SMB_OFF_T start;
+ SMB_OFF_T size;
+ int lock_type;
+};
+
+/*
+ * The data in POSIX pending close records is an unsorted linear array of int
+ * records. It is unnecessary to store the count as tdb provides the
+ * size of the record.
+ */
+
+/* The key used in both the POSIX databases. */
+
+struct posix_lock_key {
+ SMB_DEV_T device;
+ SMB_INO_T inode;
+};
+
+/*******************************************************************
+ Form a static locking key for a dev/inode pair.
+******************************************************************/
+
+static TDB_DATA locking_key(SMB_DEV_T dev, SMB_INO_T inode)
+{
+ static struct posix_lock_key key;
+ TDB_DATA kbuf;
+
+ memset(&key, '\0', sizeof(key));
+ key.device = dev;
+ key.inode = inode;
+ kbuf.dptr = (char *)&key;
+ kbuf.dsize = sizeof(key);
+ return kbuf;
+}
+
+/*******************************************************************
+ Convenience function to get a key from an fsp.
+******************************************************************/
+
+static TDB_DATA locking_key_fsp(files_struct *fsp)
+{
+ return locking_key(fsp->dev, fsp->inode);
+}
+
+/****************************************************************************
+ Add an fd to the pending close tdb.
+****************************************************************************/
+
+static BOOL add_fd_to_close_entry(files_struct *fsp)
+{
+ TDB_DATA kbuf = locking_key_fsp(fsp);
+ TDB_DATA dbuf;
+ char *tp;
+
+ dbuf.dptr = NULL;
+
+ dbuf = tdb_fetch(posix_pending_close_tdb, kbuf);
+
+ tp = Realloc(dbuf.dptr, dbuf.dsize + sizeof(int));
+ if (!tp) {
+ DEBUG(0,("add_fd_to_close_entry: Realloc fail !\n"));
+ SAFE_FREE(dbuf.dptr);
+ return False;
+ } else
+ dbuf.dptr = tp;
+
+ memcpy(dbuf.dptr + dbuf.dsize, &fsp->fd, sizeof(int));
+ dbuf.dsize += sizeof(int);
+
+ if (tdb_store(posix_pending_close_tdb, kbuf, dbuf, TDB_REPLACE) == -1) {
+ DEBUG(0,("add_fd_to_close_entry: tdb_store fail !\n"));
+ }
+
+ SAFE_FREE(dbuf.dptr);
+ return True;
+}
+
+/****************************************************************************
+ Remove all fd entries for a specific dev/inode pair from the tdb.
+****************************************************************************/
+
+static void delete_close_entries(files_struct *fsp)
+{
+ TDB_DATA kbuf = locking_key_fsp(fsp);
+
+ if (tdb_delete(posix_pending_close_tdb, kbuf) == -1)
+ DEBUG(0,("delete_close_entries: tdb_delete fail !\n"));
+}
+
+/****************************************************************************
+ Get the array of POSIX pending close records for an open fsp. Caller must
+ free. Returns number of entries.
+****************************************************************************/
+
+static size_t get_posix_pending_close_entries(files_struct *fsp, int **entries)
+{
+ TDB_DATA kbuf = locking_key_fsp(fsp);
+ TDB_DATA dbuf;
+ size_t count = 0;
+
+ *entries = NULL;
+ dbuf.dptr = NULL;
+
+ dbuf = tdb_fetch(posix_pending_close_tdb, kbuf);
+
+ if (!dbuf.dptr) {
+ return 0;
+ }
+
+ *entries = (int *)dbuf.dptr;
+ count = (size_t)(dbuf.dsize / sizeof(int));
+
+ return count;
+}
+
+/****************************************************************************
+ Get the array of POSIX locks for an fsp. Caller must free. Returns
+ number of entries.
+****************************************************************************/
+
+static size_t get_posix_lock_entries(files_struct *fsp, struct posix_lock **entries)
+{
+ TDB_DATA kbuf = locking_key_fsp(fsp);
+ TDB_DATA dbuf;
+ size_t count = 0;
+
+ *entries = NULL;
+
+ dbuf.dptr = NULL;
+
+ dbuf = tdb_fetch(posix_lock_tdb, kbuf);
+
+ if (!dbuf.dptr) {
+ return 0;
+ }
+
+ *entries = (struct posix_lock *)dbuf.dptr;
+ count = (size_t)(dbuf.dsize / sizeof(struct posix_lock));
+
+ return count;
+}
+
+/****************************************************************************
+ Deal with pending closes needed by POSIX locking support.
+ Note that posix_locking_close_file() is expected to have been called
+ to delete all locks on this fsp before this function is called.
+****************************************************************************/
+
+int fd_close_posix(struct connection_struct *conn, files_struct *fsp)
+{
+ int saved_errno = 0;
+ int ret;
+ size_t count, i;
+ struct posix_lock *entries = NULL;
+ int *fd_array = NULL;
+ BOOL locks_on_other_fds = False;
+
+ if (!lp_posix_locking(SNUM(conn))) {
+ /*
+ * No POSIX to worry about, just close.
+ */
+ ret = SMB_VFS_CLOSE(fsp,fsp->fd);
+ fsp->fd = -1;
+ return ret;
+ }
+
+ /*
+ * Get the number of outstanding POSIX locks on this dev/inode pair.
+ */
+
+ count = get_posix_lock_entries(fsp, &entries);
+
+ /*
+ * Check if there are any outstanding locks belonging to
+ * other fd's. This should never be the case if posix_locking_close_file()
+ * has been called first, but it never hurts to be *sure*.
+ */
+
+ for (i = 0; i < count; i++) {
+ if (entries[i].fd != fsp->fd) {
+ locks_on_other_fds = True;
+ break;
+ }
+ }
+
+ if (locks_on_other_fds) {
+
+ /*
+ * There are outstanding locks on this dev/inode pair on other fds.
+ * Add our fd to the pending close tdb and set fsp->fd to -1.
+ */
+
+ if (!add_fd_to_close_entry(fsp)) {
+ SAFE_FREE(entries);
+ return False;
+ }
+
+ SAFE_FREE(entries);
+ fsp->fd = -1;
+ return 0;
+ }
+
+ SAFE_FREE(entries);
+
+ /*
+ * No outstanding POSIX locks. Get the pending close fd's
+ * from the tdb and close them all.
+ */
+
+ count = get_posix_pending_close_entries(fsp, &fd_array);
+
+ if (count) {
+ DEBUG(10,("fd_close_posix: doing close on %u fd's.\n", (unsigned int)count ));
+
+ for(i = 0; i < count; i++) {
+ if (SMB_VFS_CLOSE(fsp,fd_array[i]) == -1) {
+ saved_errno = errno;
+ }
+ }
+
+ /*
+ * Delete all fd's stored in the tdb
+ * for this dev/inode pair.
+ */
+
+ delete_close_entries(fsp);
+ }
+
+ SAFE_FREE(fd_array);
+
+ /*
+ * Finally close the fd associated with this fsp.
+ */
+
+ ret = SMB_VFS_CLOSE(fsp,fsp->fd);
+
+ if (saved_errno != 0) {
+ errno = saved_errno;
+ ret = -1;
+ }
+
+ fsp->fd = -1;
+
+ return ret;
+}
+
+/****************************************************************************
+ Debugging aid :-).
+****************************************************************************/
+
+static const char *posix_lock_type_name(int lock_type)
+{
+ return (lock_type == F_RDLCK) ? "READ" : "WRITE";
+}
+
+/****************************************************************************
+ Delete a POSIX lock entry by index number. Used if the tdb add succeeds, but
+ then the POSIX fcntl lock fails.
+****************************************************************************/
+
+static BOOL delete_posix_lock_entry_by_index(files_struct *fsp, size_t entry)
+{
+ TDB_DATA kbuf = locking_key_fsp(fsp);
+ TDB_DATA dbuf;
+ struct posix_lock *locks;
+ size_t count;
+
+ dbuf.dptr = NULL;
+
+ dbuf = tdb_fetch(posix_lock_tdb, kbuf);
+
+ if (!dbuf.dptr) {
+ DEBUG(10,("delete_posix_lock_entry_by_index: tdb_fetch failed !\n"));
+ goto fail;
+ }
+
+ count = (size_t)(dbuf.dsize / sizeof(struct posix_lock));
+ locks = (struct posix_lock *)dbuf.dptr;
+
+ if (count == 1) {
+ tdb_delete(posix_lock_tdb, kbuf);
+ } else {
+ if (entry < count-1) {
+ memmove(&locks[entry], &locks[entry+1], sizeof(*locks)*((count-1) - entry));
+ }
+ dbuf.dsize -= sizeof(*locks);
+ tdb_store(posix_lock_tdb, kbuf, dbuf, TDB_REPLACE);
+ }
+
+ SAFE_FREE(dbuf.dptr);
+
+ return True;
+
+ fail:
+
+ SAFE_FREE(dbuf.dptr);
+ return False;
+}
+
+/****************************************************************************
+ Add an entry into the POSIX locking tdb. We return the index number of the
+ added lock (used in case we need to delete *exactly* this entry). Returns
+ False on fail, True on success.
+****************************************************************************/
+
+static BOOL add_posix_lock_entry(files_struct *fsp, SMB_OFF_T start, SMB_OFF_T size, int lock_type, size_t *pentry_num)
+{
+ TDB_DATA kbuf = locking_key_fsp(fsp);
+ TDB_DATA dbuf;
+ struct posix_lock pl;
+ char *tp;
+
+ dbuf.dptr = NULL;
+
+ dbuf = tdb_fetch(posix_lock_tdb, kbuf);
+
+ *pentry_num = (size_t)(dbuf.dsize / sizeof(pl));
+
+ /*
+ * Add new record.
+ */
+
+ pl.fd = fsp->fd;
+ pl.start = start;
+ pl.size = size;
+ pl.lock_type = lock_type;
+
+ tp = Realloc(dbuf.dptr, dbuf.dsize + sizeof(pl));
+ if (!tp) {
+ DEBUG(0,("add_posix_lock_entry: Realloc fail !\n"));
+ goto fail;
+ } else
+ dbuf.dptr = tp;
+
+ memcpy(dbuf.dptr + dbuf.dsize, &pl, sizeof(pl));
+ dbuf.dsize += sizeof(pl);
+
+ if (tdb_store(posix_lock_tdb, kbuf, dbuf, TDB_REPLACE) == -1) {
+ DEBUG(0,("add_posix_lock: Failed to add lock entry on file %s\n", fsp->fsp_name));
+ goto fail;
+ }
+
+ SAFE_FREE(dbuf.dptr);
+
+ DEBUG(10,("add_posix_lock: File %s: type = %s: start=%.0f size=%.0f: dev=%.0f inode=%.0f\n",
+ fsp->fsp_name, posix_lock_type_name(lock_type), (double)start, (double)size,
+ (double)fsp->dev, (double)fsp->inode ));
+
+ return True;
+
+ fail:
+
+ SAFE_FREE(dbuf.dptr);
+ return False;
+}
+
+/****************************************************************************
+ Calculate if locks have any overlap at all.
+****************************************************************************/
+
+static BOOL does_lock_overlap(SMB_OFF_T start1, SMB_OFF_T size1, SMB_OFF_T start2, SMB_OFF_T size2)
+{
+ if (start1 >= start2 && start1 <= start2 + size2)
+ return True;
+
+ if (start1 < start2 && start1 + size1 > start2)
+ return True;
+
+ return False;
+}
+
+/****************************************************************************
+ Delete an entry from the POSIX locking tdb. Returns a copy of the entry being
+ deleted and the number of records that are overlapped by this one, or -1 on error.
+****************************************************************************/
+
+static int delete_posix_lock_entry(files_struct *fsp, SMB_OFF_T start, SMB_OFF_T size, struct posix_lock *pl)
+{
+ TDB_DATA kbuf = locking_key_fsp(fsp);
+ TDB_DATA dbuf;
+ struct posix_lock *locks;
+ size_t i, count;
+ BOOL found = False;
+ int num_overlapping_records = 0;
+
+ dbuf.dptr = NULL;
+
+ dbuf = tdb_fetch(posix_lock_tdb, kbuf);
+
+ if (!dbuf.dptr) {
+ DEBUG(10,("delete_posix_lock_entry: tdb_fetch failed !\n"));
+ goto fail;
+ }
+
+ /* There are existing locks - find a match. */
+ locks = (struct posix_lock *)dbuf.dptr;
+ count = (size_t)(dbuf.dsize / sizeof(*locks));
+
+ /*
+ * Search for and delete the first record that matches the
+ * unlock criteria.
+ */
+
+ for (i=0; i<count; i++) {
+ struct posix_lock *entry = &locks[i];
+
+ if (entry->fd == fsp->fd &&
+ entry->start == start &&
+ entry->size == size) {
+
+ /* Make a copy if requested. */
+ if (pl)
+ *pl = *entry;
+
+ /* Found it - delete it. */
+ if (count == 1) {
+ tdb_delete(posix_lock_tdb, kbuf);
+ } else {
+ if (i < count-1) {
+ memmove(&locks[i], &locks[i+1], sizeof(*locks)*((count-1) - i));
+ }
+ dbuf.dsize -= sizeof(*locks);
+ tdb_store(posix_lock_tdb, kbuf, dbuf, TDB_REPLACE);
+ }
+ count--;
+ found = True;
+ break;
+ }
+ }
+
+ if (!found)
+ goto fail;
+
+ /*
+ * Count the number of entries that are
+ * overlapped by this unlock request.
+ */
+
+ for (i = 0; i < count; i++) {
+ struct posix_lock *entry = &locks[i];
+
+ if (fsp->fd == entry->fd &&
+ does_lock_overlap( start, size, entry->start, entry->size))
+ num_overlapping_records++;
+ }
+
+ DEBUG(10,("delete_posix_lock_entry: type = %s: start=%.0f size=%.0f, num_records = %d\n",
+ posix_lock_type_name(pl->lock_type), (double)pl->start, (double)pl->size,
+ (unsigned int)num_overlapping_records ));
+
+ SAFE_FREE(dbuf.dptr);
+
+ return num_overlapping_records;
+
+ fail:
+
+ SAFE_FREE(dbuf.dptr);
+ return -1;
+}
+
+/****************************************************************************
+ Utility function to map a lock type correctly depending on the open
+ mode of a file.
+****************************************************************************/
+
+static int map_posix_lock_type( files_struct *fsp, enum brl_type lock_type)
+{
+ if((lock_type == WRITE_LOCK) && !fsp->can_write) {
+ /*
+ * Many UNIX's cannot get a write lock on a file opened read-only.
+ * Win32 locking semantics allow this.
+ * Do the best we can and attempt a read-only lock.
+ */
+ DEBUG(10,("map_posix_lock_type: Downgrading write lock to read due to read-only file.\n"));
+ return F_RDLCK;
+ } else if((lock_type == READ_LOCK) && !fsp->can_read) {
+ /*
+ * Ditto for read locks on write only files.
+ */
+ DEBUG(10,("map_posix_lock_type: Changing read lock to write due to write-only file.\n"));
+ return F_WRLCK;
+ }
+
+ /*
+ * This return should be the most normal, as we attempt
+ * to always open files read/write.
+ */
+
+ return (lock_type == READ_LOCK) ? F_RDLCK : F_WRLCK;
+}
+
+/****************************************************************************
+ Check to see if the given unsigned lock range is within the possible POSIX
+ range. Modifies the given args to be in range if possible, just returns
+ False if not.
+****************************************************************************/
+
+static BOOL posix_lock_in_range(SMB_OFF_T *offset_out, SMB_OFF_T *count_out,
+ SMB_BIG_UINT u_offset, SMB_BIG_UINT u_count)
+{
+ SMB_OFF_T offset = (SMB_OFF_T)u_offset;
+ SMB_OFF_T count = (SMB_OFF_T)u_count;
+
+ /*
+ * For the type of system we are, attempt to
+ * find the maximum positive lock offset as an SMB_OFF_T.
+ */
+
+#if defined(MAX_POSITIVE_LOCK_OFFSET) /* Some systems have arbitrary limits. */
+
+ SMB_OFF_T max_positive_lock_offset = (MAX_POSITIVE_LOCK_OFFSET);
+
+#elif defined(LARGE_SMB_OFF_T) && !defined(HAVE_BROKEN_FCNTL64_LOCKS)
+
+ /*
+ * In this case SMB_OFF_T is 64 bits,
+ * and the underlying system can handle 64 bit signed locks.
+ */
+
+ SMB_OFF_T mask2 = ((SMB_OFF_T)0x4) << (SMB_OFF_T_BITS-4);
+ SMB_OFF_T mask = (mask2<<1);
+ SMB_OFF_T max_positive_lock_offset = ~mask;
+
+#else /* !LARGE_SMB_OFF_T || HAVE_BROKEN_FCNTL64_LOCKS */
+
+ /*
+ * In this case either SMB_OFF_T is 32 bits,
+ * or the underlying system cannot handle 64 bit signed locks.
+ * All offsets & counts must be 2^31 or less.
+ */
+
+ SMB_OFF_T max_positive_lock_offset = 0x7FFFFFFF;
+
+#endif /* !LARGE_SMB_OFF_T || HAVE_BROKEN_FCNTL64_LOCKS */
+
+ /*
+ * POSIX locks of length zero mean lock to end-of-file.
+ * Win32 locks of length zero are point probes. Ignore
+ * any Win32 locks of length zero. JRA.
+ */
+
+ if (count == (SMB_OFF_T)0) {
+ DEBUG(10,("posix_lock_in_range: count = 0, ignoring.\n"));
+ return False;
+ }
+
+ /*
+ * If the given offset was > max_positive_lock_offset then we cannot map this at all
+ * ignore this lock.
+ */
+
+ if (u_offset & ~((SMB_BIG_UINT)max_positive_lock_offset)) {
+ DEBUG(10,("posix_lock_in_range: (offset = %.0f) offset > %.0f and we cannot handle this. Ignoring lock.\n",
+ (double)u_offset, (double)((SMB_BIG_UINT)max_positive_lock_offset) ));
+ return False;
+ }
+
+ /*
+ * We must truncate the count to less than max_positive_lock_offset.
+ */
+
+ if (u_count & ~((SMB_BIG_UINT)max_positive_lock_offset))
+ count = max_positive_lock_offset;
+
+ /*
+ * Truncate count to end at max lock offset.
+ */
+
+ if (offset + count < 0 || offset + count > max_positive_lock_offset)
+ count = max_positive_lock_offset - offset;
+
+ /*
+ * If we ate all the count, ignore this lock.
+ */
+
+ if (count == 0) {
+ DEBUG(10,("posix_lock_in_range: Count = 0. Ignoring lock u_offset = %.0f, u_count = %.0f\n",
+ (double)u_offset, (double)u_count ));
+ return False;
+ }
+
+ /*
+ * The mapping was successful.
+ */
+
+ DEBUG(10,("posix_lock_in_range: offset_out = %.0f, count_out = %.0f\n",
+ (double)offset, (double)count ));
+
+ *offset_out = offset;
+ *count_out = count;
+
+ return True;
+}
+
+/****************************************************************************
+ Actual function that does POSIX locks. Copes with 64 -> 32 bit cruft and
+ broken NFS implementations.
+****************************************************************************/
+
+static BOOL posix_fcntl_lock(files_struct *fsp, int op, SMB_OFF_T offset, SMB_OFF_T count, int type)
+{
+ int ret;
+
+ DEBUG(8,("posix_fcntl_lock %d %d %.0f %.0f %d\n",fsp->fd,op,(double)offset,(double)count,type));
+
+ ret = SMB_VFS_LOCK(fsp,fsp->fd,op,offset,count,type);
+
+ if (!ret && ((errno == EFBIG) || (errno == ENOLCK) || (errno == EINVAL))) {
+
+ DEBUG(0,("posix_fcntl_lock: WARNING: lock request at offset %.0f, length %.0f returned\n",
+ (double)offset,(double)count));
+ DEBUG(0,("an %s error. This can happen when using 64 bit lock offsets\n", strerror(errno)));
+ DEBUG(0,("on 32 bit NFS mounted file systems.\n"));
+
+ /*
+ * If the offset is > 0x7FFFFFFF then this will cause problems on
+ * 32 bit NFS mounted filesystems. Just ignore it.
+ */
+
+ if (offset & ~((SMB_OFF_T)0x7fffffff)) {
+ DEBUG(0,("Offset greater than 31 bits. Returning success.\n"));
+ return True;
+ }
+
+ if (count & ~((SMB_OFF_T)0x7fffffff)) {
+ /* 32 bit NFS file system, retry with smaller offset */
+ DEBUG(0,("Count greater than 31 bits - retrying with 31 bit truncated length.\n"));
+ errno = 0;
+ count &= 0x7fffffff;
+ ret = SMB_VFS_LOCK(fsp,fsp->fd,op,offset,count,type);
+ }
+ }
+
+ DEBUG(8,("posix_fcntl_lock: Lock call %s\n", ret ? "successful" : "failed"));
+
+ return ret;
+}
+
+/****************************************************************************
+ POSIX function to see if a file region is locked. Returns True if the
+ region is locked, False otherwise.
+****************************************************************************/
+
+BOOL is_posix_locked(files_struct *fsp, SMB_BIG_UINT u_offset, SMB_BIG_UINT u_count, enum brl_type lock_type)
+{
+ SMB_OFF_T offset;
+ SMB_OFF_T count;
+ int posix_lock_type = map_posix_lock_type(fsp,lock_type);
+
+ DEBUG(10,("is_posix_locked: File %s, offset = %.0f, count = %.0f, type = %s\n",
+ fsp->fsp_name, (double)u_offset, (double)u_count, posix_lock_type_name(lock_type) ));
+
+ /*
+ * If the requested lock won't fit in the POSIX range, we will
+ * never set it, so presume it is not locked.
+ */
+
+ if(!posix_lock_in_range(&offset, &count, u_offset, u_count))
+ return False;
+
+ /*
+ * Note that most UNIX's can *test* for a write lock on
+ * a read-only fd, just not *set* a write lock on a read-only
+ * fd. So we don't need to use map_lock_type here.
+ */
+
+ return posix_fcntl_lock(fsp,SMB_F_GETLK,offset,count,posix_lock_type);
+}
+
+/*
+ * Structure used when splitting a lock range
+ * into a POSIX lock range. Doubly linked list.
+ */
+
+struct lock_list {
+ struct lock_list *next;
+ struct lock_list *prev;
+ SMB_OFF_T start;
+ SMB_OFF_T size;
+};
+
+/****************************************************************************
+ Create a list of lock ranges that don't overlap a given range. Used in calculating
+ POSIX locks and unlocks. This is a difficult function that requires ASCII art to
+ understand it :-).
+****************************************************************************/
+
+static struct lock_list *posix_lock_list(TALLOC_CTX *ctx, struct lock_list *lhead, files_struct *fsp)
+{
+ TDB_DATA kbuf = locking_key_fsp(fsp);
+ TDB_DATA dbuf;
+ struct posix_lock *locks;
+ size_t num_locks, i;
+
+ dbuf.dptr = NULL;
+
+ dbuf = tdb_fetch(posix_lock_tdb, kbuf);
+
+ if (!dbuf.dptr)
+ return lhead;
+
+ locks = (struct posix_lock *)dbuf.dptr;
+ num_locks = (size_t)(dbuf.dsize / sizeof(*locks));
+
+ /*
+ * Check the current lock list on this dev/inode pair.
+ * Quit if the list is deleted.
+ */
+
+ DEBUG(10,("posix_lock_list: curr: start=%.0f,size=%.0f\n",
+ (double)lhead->start, (double)lhead->size ));
+
+ for (i=0; i<num_locks && lhead; i++) {
+
+ struct posix_lock *lock = &locks[i];
+ struct lock_list *l_curr;
+
+ /*
+ * Walk the lock list, checking for overlaps. Note that
+ * the lock list can expand within this loop if the current
+ * range being examined needs to be split.
+ */
+
+ for (l_curr = lhead; l_curr;) {
+
+ DEBUG(10,("posix_lock_list: lock: fd=%d: start=%.0f,size=%.0f:type=%s", lock->fd,
+ (double)lock->start, (double)lock->size, posix_lock_type_name(lock->lock_type) ));
+
+ if ( (l_curr->start >= (lock->start + lock->size)) ||
+ (lock->start >= (l_curr->start + l_curr->size))) {
+
+ /* No overlap with this lock - leave this range alone. */
+/*********************************************
+ +---------+
+ | l_curr |
+ +---------+
+ +-------+
+ | lock |
+ +-------+
+OR....
+ +---------+
+ | l_curr |
+ +---------+
+**********************************************/
+
+ DEBUG(10,("no overlap case.\n" ));
+
+ l_curr = l_curr->next;
+
+ } else if ( (l_curr->start >= lock->start) &&
+ (l_curr->start + l_curr->size <= lock->start + lock->size) ) {
+
+ /*
+ * This unlock is completely overlapped by this existing lock range
+ * and thus should have no effect (not be unlocked). Delete it from the list.
+ */
+/*********************************************
+ +---------+
+ | l_curr |
+ +---------+
+ +---------------------------+
+ | lock |
+ +---------------------------+
+**********************************************/
+ /* Save the next pointer */
+ struct lock_list *ul_next = l_curr->next;
+
+ DEBUG(10,("delete case.\n" ));
+
+ DLIST_REMOVE(lhead, l_curr);
+ if(lhead == NULL)
+ break; /* No more list... */
+
+ l_curr = ul_next;
+
+ } else if ( (l_curr->start >= lock->start) &&
+ (l_curr->start < lock->start + lock->size) &&
+ (l_curr->start + l_curr->size > lock->start + lock->size) ) {
+
+ /*
+ * This unlock overlaps the existing lock range at the high end.
+ * Truncate by moving start to existing range end and reducing size.
+ */
+/*********************************************
+ +---------------+
+ | l_curr |
+ +---------------+
+ +---------------+
+ | lock |
+ +---------------+
+BECOMES....
+ +-------+
+ | l_curr|
+ +-------+
+**********************************************/
+
+ l_curr->size = (l_curr->start + l_curr->size) - (lock->start + lock->size);
+ l_curr->start = lock->start + lock->size;
+
+ DEBUG(10,("truncate high case: start=%.0f,size=%.0f\n",
+ (double)l_curr->start, (double)l_curr->size ));
+
+ l_curr = l_curr->next;
+
+ } else if ( (l_curr->start < lock->start) &&
+ (l_curr->start + l_curr->size > lock->start) &&
+ (l_curr->start + l_curr->size <= lock->start + lock->size) ) {
+
+ /*
+ * This unlock overlaps the existing lock range at the low end.
+ * Truncate by reducing size.
+ */
+/*********************************************
+ +---------------+
+ | l_curr |
+ +---------------+
+ +---------------+
+ | lock |
+ +---------------+
+BECOMES....
+ +-------+
+ | l_curr|
+ +-------+
+**********************************************/
+
+ l_curr->size = lock->start - l_curr->start;
+
+ DEBUG(10,("truncate low case: start=%.0f,size=%.0f\n",
+ (double)l_curr->start, (double)l_curr->size ));
+
+ l_curr = l_curr->next;
+
+ } else if ( (l_curr->start < lock->start) &&
+ (l_curr->start + l_curr->size > lock->start + lock->size) ) {
+ /*
+ * Worst case scenario. Unlock request completely overlaps an existing
+ * lock range. Split the request into two, push the new (upper) request
+ * into the dlink list, and continue with the entry after ul_new (as we
+ * know that ul_new will not overlap with this lock).
+ */
+/*********************************************
+ +---------------------------+
+ | l_curr |
+ +---------------------------+
+ +---------+
+ | lock |
+ +---------+
+BECOMES.....
+ +-------+ +---------+
+ | l_curr| | l_new |
+ +-------+ +---------+
+**********************************************/
+ struct lock_list *l_new = (struct lock_list *)talloc(ctx,
+ sizeof(struct lock_list));
+
+ if(l_new == NULL) {
+ DEBUG(0,("posix_lock_list: talloc fail.\n"));
+ return NULL; /* The talloc_destroy takes care of cleanup. */
+ }
+
+ ZERO_STRUCTP(l_new);
+ l_new->start = lock->start + lock->size;
+ l_new->size = l_curr->start + l_curr->size - l_new->start;
+
+ /* Truncate the l_curr. */
+ l_curr->size = lock->start - l_curr->start;
+
+ DEBUG(10,("split case: curr: start=%.0f,size=%.0f \
+new: start=%.0f,size=%.0f\n", (double)l_curr->start, (double)l_curr->size,
+ (double)l_new->start, (double)l_new->size ));
+
+ /*
+ * Add into the dlink list after the l_curr point - NOT at lhead.
+ * Note we can't use DLINK_ADD here as this inserts at the head of the given list.
+ */
+
+ l_new->prev = l_curr;
+ l_new->next = l_curr->next;
+ l_curr->next = l_new;
+
+ /* And move after the link we added. */
+ l_curr = l_new->next;
+
+ } else {
+
+ /*
+ * This logic case should never happen. Ensure this is the
+ * case by forcing an abort.... Remove in production.
+ */
+ pstring msg;
+
+ slprintf(msg, sizeof(msg)-1, "logic flaw in cases: l_curr: start = %.0f, size = %.0f : \
+lock: start = %.0f, size = %.0f\n", (double)l_curr->start, (double)l_curr->size, (double)lock->start, (double)lock->size );
+
+ smb_panic(msg);
+ }
+ } /* end for ( l_curr = lhead; l_curr;) */
+ } /* end for (i=0; i<num_locks && ul_head; i++) */
+
+ SAFE_FREE(dbuf.dptr);
+
+ return lhead;
+}
+
+/****************************************************************************
+ POSIX function to acquire a lock. Returns True if the
+ lock could be granted, False if not.
+****************************************************************************/
+
+BOOL set_posix_lock(files_struct *fsp, SMB_BIG_UINT u_offset, SMB_BIG_UINT u_count, enum brl_type lock_type)
+{
+ SMB_OFF_T offset;
+ SMB_OFF_T count;
+ BOOL ret = True;
+ size_t entry_num = 0;
+ size_t lock_count;
+ TALLOC_CTX *l_ctx = NULL;
+ struct lock_list *llist = NULL;
+ struct lock_list *ll = NULL;
+ int posix_lock_type = map_posix_lock_type(fsp,lock_type);
+
+ DEBUG(5,("set_posix_lock: File %s, offset = %.0f, count = %.0f, type = %s\n",
+ fsp->fsp_name, (double)u_offset, (double)u_count, posix_lock_type_name(lock_type) ));
+
+ /*
+ * If the requested lock won't fit in the POSIX range, we will
+ * pretend it was successful.
+ */
+
+ if(!posix_lock_in_range(&offset, &count, u_offset, u_count))
+ return True;
+
+ /*
+ * Windows is very strange. It allows read locks to be overlayed
+ * (even over a write lock), but leaves the write lock in force until the first
+ * unlock. It also reference counts the locks. This means the following sequence :
+ *
+ * process1 process2
+ * ------------------------------------------------------------------------
+ * WRITE LOCK : start = 2, len = 10
+ * READ LOCK: start =0, len = 10 - FAIL
+ * READ LOCK : start = 0, len = 14
+ * READ LOCK: start =0, len = 10 - FAIL
+ * UNLOCK : start = 2, len = 10
+ * READ LOCK: start =0, len = 10 - OK
+ *
+ * Under POSIX, the same sequence in steps 1 and 2 would not be reference counted, but
+ * would leave a single read lock over the 0-14 region. In order to
+ * re-create Windows semantics mapped to POSIX locks, we create multiple TDB
+ * entries, one for each overlayed lock request. We are guarenteed by the brlock
+ * semantics that if a write lock is added, then it will be first in the array.
+ */
+
+ if ((l_ctx = talloc_init("set_posix_lock")) == NULL) {
+ DEBUG(0,("set_posix_lock: unable to init talloc context.\n"));
+ return True; /* Not a fatal error. */
+ }
+
+ if ((ll = (struct lock_list *)talloc(l_ctx, sizeof(struct lock_list))) == NULL) {
+ DEBUG(0,("set_posix_lock: unable to talloc unlock list.\n"));
+ talloc_destroy(l_ctx);
+ return True; /* Not a fatal error. */
+ }
+
+ /*
+ * Create the initial list entry containing the
+ * lock we want to add.
+ */
+
+ ZERO_STRUCTP(ll);
+ ll->start = offset;
+ ll->size = count;
+
+ DLIST_ADD(llist, ll);
+
+ /*
+ * The following call calculates if there are any
+ * overlapping locks held by this process on
+ * fd's open on the same file and splits this list
+ * into a list of lock ranges that do not overlap with existing
+ * POSIX locks.
+ */
+
+ llist = posix_lock_list(l_ctx, llist, fsp);
+
+ /*
+ * Now we have the list of ranges to lock it is safe to add the
+ * entry into the POSIX lock tdb. We take note of the entry we
+ * added here in case we have to remove it on POSIX lock fail.
+ */
+
+ if (!add_posix_lock_entry(fsp,offset,count,posix_lock_type,&entry_num)) {
+ DEBUG(0,("set_posix_lock: Unable to create posix lock entry !\n"));
+ talloc_destroy(l_ctx);
+ return False;
+ }
+
+ /*
+ * Add the POSIX locks on the list of ranges returned.
+ * As the lock is supposed to be added atomically, we need to
+ * back out all the locks if any one of these calls fail.
+ */
+
+ for (lock_count = 0, ll = llist; ll; ll = ll->next, lock_count++) {
+ offset = ll->start;
+ count = ll->size;
+
+ DEBUG(5,("set_posix_lock: Real lock: Type = %s: offset = %.0f, count = %.0f\n",
+ posix_lock_type_name(posix_lock_type), (double)offset, (double)count ));
+
+ if (!posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,posix_lock_type)) {
+ DEBUG(5,("set_posix_lock: Lock fail !: Type = %s: offset = %.0f, count = %.0f. Errno = %s\n",
+ posix_lock_type_name(posix_lock_type), (double)offset, (double)count, strerror(errno) ));
+ ret = False;
+ break;
+ }
+ }
+
+ if (!ret) {
+
+ /*
+ * Back out all the POSIX locks we have on fail.
+ */
+
+ for (ll = llist; lock_count; ll = ll->next, lock_count--) {
+ offset = ll->start;
+ count = ll->size;
+
+ DEBUG(5,("set_posix_lock: Backing out locks: Type = %s: offset = %.0f, count = %.0f\n",
+ posix_lock_type_name(posix_lock_type), (double)offset, (double)count ));
+
+ posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,F_UNLCK);
+ }
+
+ /*
+ * Remove the tdb entry for this lock.
+ */
+
+ delete_posix_lock_entry_by_index(fsp,entry_num);
+ }
+
+ talloc_destroy(l_ctx);
+ return ret;
+}
+
+/****************************************************************************
+ POSIX function to release a lock. Returns True if the
+ lock could be released, False if not.
+****************************************************************************/
+
+BOOL release_posix_lock(files_struct *fsp, SMB_BIG_UINT u_offset, SMB_BIG_UINT u_count)
+{
+ SMB_OFF_T offset;
+ SMB_OFF_T count;
+ BOOL ret = True;
+ TALLOC_CTX *ul_ctx = NULL;
+ struct lock_list *ulist = NULL;
+ struct lock_list *ul = NULL;
+ struct posix_lock deleted_lock;
+ int num_overlapped_entries;
+
+ DEBUG(5,("release_posix_lock: File %s, offset = %.0f, count = %.0f\n",
+ fsp->fsp_name, (double)u_offset, (double)u_count ));
+
+ /*
+ * If the requested lock won't fit in the POSIX range, we will
+ * pretend it was successful.
+ */
+
+ if(!posix_lock_in_range(&offset, &count, u_offset, u_count))
+ return True;
+
+ /*
+ * We treat this as one unlock request for POSIX accounting purposes even
+ * if it may later be split into multiple smaller POSIX unlock ranges.
+ * num_overlapped_entries is the number of existing locks that have any
+ * overlap with this unlock request.
+ */
+
+ num_overlapped_entries = delete_posix_lock_entry(fsp, offset, count, &deleted_lock);
+
+ if (num_overlapped_entries == -1) {
+ smb_panic("release_posix_lock: unable find entry to delete !\n");
+ }
+
+ /*
+ * If num_overlapped_entries is > 0, and the lock_type we just deleted from the tdb was
+ * a POSIX write lock, then before doing the unlock we need to downgrade
+ * the POSIX lock to a read lock. This allows any overlapping read locks
+ * to be atomically maintained.
+ */
+
+ if (num_overlapped_entries > 0 && deleted_lock.lock_type == F_WRLCK) {
+ if (!posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,F_RDLCK)) {
+ DEBUG(0,("release_posix_lock: downgrade of lock failed with error %s !\n", strerror(errno) ));
+ return False;
+ }
+ }
+
+ if ((ul_ctx = talloc_init("release_posix_lock")) == NULL) {
+ DEBUG(0,("release_posix_lock: unable to init talloc context.\n"));
+ return True; /* Not a fatal error. */
+ }
+
+ if ((ul = (struct lock_list *)talloc(ul_ctx, sizeof(struct lock_list))) == NULL) {
+ DEBUG(0,("release_posix_lock: unable to talloc unlock list.\n"));
+ talloc_destroy(ul_ctx);
+ return True; /* Not a fatal error. */
+ }
+
+ /*
+ * Create the initial list entry containing the
+ * lock we want to remove.
+ */
+
+ ZERO_STRUCTP(ul);
+ ul->start = offset;
+ ul->size = count;
+
+ DLIST_ADD(ulist, ul);
+
+ /*
+ * The following call calculates if there are any
+ * overlapping locks held by this process on
+ * fd's open on the same file and creates a
+ * list of unlock ranges that will allow
+ * POSIX lock ranges to remain on the file whilst the
+ * unlocks are performed.
+ */
+
+ ulist = posix_lock_list(ul_ctx, ulist, fsp);
+
+ /*
+ * Release the POSIX locks on the list of ranges returned.
+ */
+
+ for(; ulist; ulist = ulist->next) {
+ offset = ulist->start;
+ count = ulist->size;
+
+ DEBUG(5,("release_posix_lock: Real unlock: offset = %.0f, count = %.0f\n",
+ (double)offset, (double)count ));
+
+ if (!posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,F_UNLCK))
+ ret = False;
+ }
+
+ talloc_destroy(ul_ctx);
+
+ return ret;
+}
+
+/****************************************************************************
+ Remove all lock entries for a specific dev/inode pair from the tdb.
+****************************************************************************/
+
+static void delete_posix_lock_entries(files_struct *fsp)
+{
+ TDB_DATA kbuf = locking_key_fsp(fsp);
+
+ if (tdb_delete(posix_lock_tdb, kbuf) == -1)
+ DEBUG(0,("delete_close_entries: tdb_delete fail !\n"));
+}
+
+/****************************************************************************
+ Debug function.
+****************************************************************************/
+
+static void dump_entry(struct posix_lock *pl)
+{
+ DEBUG(10,("entry: start=%.0f, size=%.0f, type=%d, fd=%i\n",
+ (double)pl->start, (double)pl->size, (int)pl->lock_type, pl->fd ));
+}
+
+/****************************************************************************
+ Remove any locks on this fd. Called from file_close().
+****************************************************************************/
+
+void posix_locking_close_file(files_struct *fsp)
+{
+ struct posix_lock *entries = NULL;
+ size_t count, i;
+
+ /*
+ * Optimization for the common case where we are the only
+ * opener of a file. If all fd entries are our own, we don't
+ * need to explicitly release all the locks via the POSIX functions,
+ * we can just remove all the entries in the tdb and allow the
+ * close to remove the real locks.
+ */
+
+ count = get_posix_lock_entries(fsp, &entries);
+
+ if (count == 0) {
+ DEBUG(10,("posix_locking_close_file: file %s has no outstanding locks.\n", fsp->fsp_name ));
+ return;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (entries[i].fd != fsp->fd )
+ break;
+
+ dump_entry(&entries[i]);
+ }
+
+ if (i == count) {
+ /* All locks are ours. */
+ DEBUG(10,("posix_locking_close_file: file %s has %u outstanding locks, but all on one fd.\n",
+ fsp->fsp_name, (unsigned int)count ));
+ SAFE_FREE(entries);
+ delete_posix_lock_entries(fsp);
+ return;
+ }
+
+ /*
+ * Difficult case. We need to delete all our locks, whilst leaving
+ * all other POSIX locks in place.
+ */
+
+ for (i = 0; i < count; i++) {
+ struct posix_lock *pl = &entries[i];
+ if (pl->fd == fsp->fd)
+ release_posix_lock(fsp, (SMB_BIG_UINT)pl->start, (SMB_BIG_UINT)pl->size );
+ }
+ SAFE_FREE(entries);
+}
+
+/*******************************************************************
+ Create the in-memory POSIX lock databases.
+********************************************************************/
+
+BOOL posix_locking_init(int read_only)
+{
+ if (posix_lock_tdb && posix_pending_close_tdb)
+ return True;
+
+ if (!posix_lock_tdb)
+ posix_lock_tdb = tdb_open_log(NULL, 0, TDB_INTERNAL,
+ read_only?O_RDONLY:(O_RDWR|O_CREAT), 0644);
+ if (!posix_lock_tdb) {
+ DEBUG(0,("Failed to open POSIX byte range locking database.\n"));
+ return False;
+ }
+ if (!posix_pending_close_tdb)
+ posix_pending_close_tdb = tdb_open_log(NULL, 0, TDB_INTERNAL,
+ read_only?O_RDONLY:(O_RDWR|O_CREAT), 0644);
+ if (!posix_pending_close_tdb) {
+ DEBUG(0,("Failed to open POSIX pending close database.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Delete the in-memory POSIX lock databases.
+********************************************************************/
+
+BOOL posix_locking_end(void)
+{
+ if (posix_lock_tdb && tdb_close(posix_lock_tdb) != 0)
+ return False;
+ if (posix_pending_close_tdb && tdb_close(posix_pending_close_tdb) != 0)
+ return False;
+ return True;
+}
diff --git a/source/mainpage.dox b/source/mainpage.dox
new file mode 100644
index 00000000000..8b72f804627
--- /dev/null
+++ b/source/mainpage.dox
@@ -0,0 +1,7 @@
+/**
+
+@mainpage
+
+@li \ref CodingSuggestions
+
+**/
diff --git a/source/modules/.cvsignore b/source/modules/.cvsignore
new file mode 100644
index 00000000000..6d609cec52b
--- /dev/null
+++ b/source/modules/.cvsignore
@@ -0,0 +1 @@
+*.po
diff --git a/source/modules/CP437.c b/source/modules/CP437.c
new file mode 100644
index 00000000000..620f6cd9b69
--- /dev/null
+++ b/source/modules/CP437.c
@@ -0,0 +1,136 @@
+/*
+ * Conversion table for CP437 charset also known as IBM437
+ *
+ * Copyright (C) Alexander Bokovoy 2003
+ *
+ * Conversion tables are generated using GNU libc 2.2.5's
+ * localedata/charmaps/IBM437 table and source/script/gen-8bit-gap.sh script
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+static const uint16 to_ucs2[256] = {
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
+ 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
+ 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
+ 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
+ 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
+ 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
+ 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
+ 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
+ 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+ 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4,
+ 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
+ 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,
+ 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0,
+};
+
+static const struct charset_gap_table from_idx[] = {
+ { 0x0000, 0x007f, 0 },
+ { 0x00a0, 0x00c9, -32 },
+ { 0x00d1, 0x00ff, -39 },
+ { 0x0192, 0x0192, -185 },
+ { 0x0393, 0x0398, -697 },
+ { 0x03a3, 0x03a9, -707 },
+ { 0x03b1, 0x03b5, -714 },
+ { 0x03c0, 0x03c6, -724 },
+ { 0x207f, 0x207f, -8076 },
+ { 0x20a7, 0x20a7, -8115 },
+ { 0x2219, 0x221e, -8484 },
+ { 0x2229, 0x2229, -8494 },
+ { 0x2248, 0x2248, -8524 },
+ { 0x2261, 0x2265, -8548 },
+ { 0x2310, 0x2310, -8718 },
+ { 0x2320, 0x2321, -8733 },
+ { 0x2500, 0x2502, -9211 },
+ { 0x250c, 0x251c, -9220 },
+ { 0x2524, 0x2524, -9227 },
+ { 0x252c, 0x252c, -9234 },
+ { 0x2534, 0x2534, -9241 },
+ { 0x253c, 0x253c, -9248 },
+ { 0x2550, 0x256c, -9267 },
+ { 0x2580, 0x2593, -9286 },
+ { 0x25a0, 0x25a0, -9298 },
+ { 0xffff, 0xffff, 0 }
+};
+
+static const unsigned char from_ucs2[] = {
+
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+ 0xff, 0xad, 0x9b, 0x9c, 0x00, 0x9d, 0x00, 0x00,
+ 0x00, 0x00, 0xa6, 0xae, 0xaa, 0x00, 0x00, 0x00,
+ 0xf8, 0xf1, 0xfd, 0x00, 0x00, 0xe6, 0x00, 0xfa,
+ 0x00, 0x00, 0xa7, 0xaf, 0xac, 0xab, 0x00, 0xa8,
+ 0x00, 0x00, 0x00, 0x00, 0x8e, 0x8f, 0x92, 0x80,
+ 0x00, 0x90, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x99,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00,
+ 0xe1, 0x85, 0xa0, 0x83, 0x00, 0x84, 0x86, 0x91,
+ 0x87, 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c,
+ 0x8b, 0x00, 0xa4, 0x95, 0xa2, 0x93, 0x00, 0x94,
+ 0xf6, 0x00, 0x97, 0xa3, 0x96, 0x81, 0x00, 0x00,
+ 0x98, 0x9f, 0xe2, 0x00, 0x00, 0x00, 0x00, 0xe9,
+ 0xe4, 0x00, 0x00, 0xe8, 0x00, 0x00, 0xea, 0xe0,
+ 0x00, 0x00, 0xeb, 0xee, 0xe3, 0x00, 0x00, 0xe5,
+ 0xe7, 0x00, 0xed, 0xfc, 0x9e, 0xf9, 0xfb, 0x00,
+ 0x00, 0x00, 0xec, 0xef, 0xf7, 0xf0, 0x00, 0x00,
+ 0xf3, 0xf2, 0xa9, 0xf4, 0xf5, 0xc4, 0x00, 0xb3,
+ 0xda, 0x00, 0x00, 0x00, 0xbf, 0x00, 0x00, 0x00,
+ 0xc0, 0x00, 0x00, 0x00, 0xd9, 0x00, 0x00, 0x00,
+ 0xc3, 0xb4, 0xc2, 0xc1, 0xc5, 0xcd, 0xba, 0xd5,
+ 0xd6, 0xc9, 0xb8, 0xb7, 0xbb, 0xd4, 0xd3, 0xc8,
+ 0xbe, 0xbd, 0xbc, 0xc6, 0xc7, 0xcc, 0xb5, 0xb6,
+ 0xb9, 0xd1, 0xd2, 0xcb, 0xcf, 0xd0, 0xca, 0xd8,
+ 0xd7, 0xce, 0xdf, 0x00, 0x00, 0x00, 0xdc, 0x00,
+ 0x00, 0x00, 0xdb, 0x00, 0x00, 0x00, 0xdd, 0x00,
+ 0x00, 0x00, 0xde, 0xb0, 0xb1, 0xb2, 0xfe,
+};
+
+SMB_GENERATE_CHARSET_MODULE_8_BIT_GAP(CP437)
diff --git a/source/modules/CP850.c b/source/modules/CP850.c
new file mode 100644
index 00000000000..e6f70aa17af
--- /dev/null
+++ b/source/modules/CP850.c
@@ -0,0 +1,122 @@
+/*
+ * Conversion table for CP850 charset also known as IBM850.
+ *
+ * Copyright (C) Alexander Bokovoy 2003
+ *
+ * Conversion tables are generated using GNU libc 2.2.5's
+ * localedata/charmaps/IBM850 table and source/script/gen-8bit-gap.sh script
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+static const uint16 to_ucs2[256] = {
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
+ 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
+ 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
+ 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,
+ 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
+ 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,
+ 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,
+ 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+ 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE,
+ 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
+ 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE,
+ 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,
+ 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,
+ 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0,
+};
+
+static const struct charset_gap_table from_idx[] = {
+ /* start, end, idx */
+ { 0x0000, 0x007f, 0 },
+ { 0x00a0, 0x00ff, -32 },
+ { 0x0131, 0x0131, -81 },
+ { 0x0192, 0x0192, -177 },
+ { 0x2017, 0x2017, -7989 },
+ { 0x2500, 0x2502, -9245 },
+ { 0x250c, 0x251c, -9254 },
+ { 0x2524, 0x2524, -9261 },
+ { 0x252c, 0x252c, -9268 },
+ { 0x2534, 0x2534, -9275 },
+ { 0x253c, 0x253c, -9282 },
+ { 0x2550, 0x256c, -9301 },
+ { 0x2580, 0x2588, -9320 },
+ { 0x2591, 0x2593, -9328 },
+ { 0x25a0, 0x25a0, -9340 },
+ { 0xffff, 0xffff, 0 }
+};
+static const unsigned char from_ucs2[] = {
+
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+ 0xff, 0xad, 0xbd, 0x9c, 0xcf, 0xbe, 0xdd, 0xf5,
+ 0xf9, 0xb8, 0xa6, 0xae, 0xaa, 0xf0, 0xa9, 0xee,
+ 0xf8, 0xf1, 0xfd, 0xfc, 0xef, 0xe6, 0xf4, 0xfa,
+ 0xf7, 0xfb, 0xa7, 0xaf, 0xac, 0xab, 0xf3, 0xa8,
+ 0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80,
+ 0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8,
+ 0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0x9e,
+ 0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0xe1,
+ 0x85, 0xa0, 0x83, 0xc6, 0x84, 0x86, 0x91, 0x87,
+ 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
+ 0xd0, 0xa4, 0x95, 0xa2, 0x93, 0xe4, 0x94, 0xf6,
+ 0x9b, 0x97, 0xa3, 0x96, 0x81, 0xec, 0xe7, 0x98,
+ 0xd5, 0x9f, 0xf2, 0xc4, 0x00, 0xb3, 0xda, 0x00,
+ 0x00, 0x00, 0xbf, 0x00, 0x00, 0x00, 0xc0, 0x00,
+ 0x00, 0x00, 0xd9, 0x00, 0x00, 0x00, 0xc3, 0xb4,
+ 0xc2, 0xc1, 0xc5, 0xcd, 0xba, 0x00, 0x00, 0xc9,
+ 0x00, 0x00, 0xbb, 0x00, 0x00, 0xc8, 0x00, 0x00,
+ 0xbc, 0x00, 0x00, 0xcc, 0x00, 0x00, 0xb9, 0x00,
+ 0x00, 0xcb, 0x00, 0x00, 0xca, 0x00, 0x00, 0xce,
+ 0xdf, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00,
+ 0xdb, 0xb0, 0xb1, 0xb2, 0xfe,
+};
+
+SMB_GENERATE_CHARSET_MODULE_8_BIT_GAP(CP850)
+
diff --git a/source/modules/charset_macosxfs.c b/source/modules/charset_macosxfs.c
new file mode 100644
index 00000000000..6f50d879ba4
--- /dev/null
+++ b/source/modules/charset_macosxfs.c
@@ -0,0 +1,602 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba charset module for Mac OS X/Darwin
+ Copyright (C) Benjamin Riefenstahl 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ * modules/charset_macosxfs.c
+ *
+ * A Samba charset module to use on Mac OS X/Darwin as the filesystem
+ * and display encoding.
+ *
+ * Actually two implementations are provided here. The default
+ * implementation is based on the official CFString API. The other is
+ * based on internal CFString APIs as defined in the OpenDarwin
+ * source.
+ */
+
+#include "includes.h"
+
+/*
+ * Include OS frameworks. These are only needed in this module.
+ */
+#include <CoreFoundation/CFString.h>
+
+/*
+ * See if autoconf has found us the internal headers in some form.
+ */
+#if HAVE_COREFOUNDATION_CFSTRINGENCODINGCONVERTER_H
+# include <Corefoundation/CFStringEncodingConverter.h>
+# include <Corefoundation/CFUnicodePrecomposition.h>
+# define USE_INTERNAL_API 1
+#elif HAVE_CFSTRINGENCODINGCONVERTER_H
+# include <CFStringEncodingConverter.h>
+# include <CFUnicodePrecomposition.h>
+# define USE_INTERNAL_API 1
+#endif
+
+/*
+ * Compile time configuration: Do we want debug output?
+ */
+/* #define DEBUG_STRINGS 1 */
+
+/*
+ * A simple, but efficient memory provider for our buffers.
+ */
+static inline void *resize_buffer (void *buffer, size_t *size, size_t newsize)
+{
+ if (newsize > *size) {
+ *size = newsize + 128;
+ buffer = realloc(buffer, *size);
+ }
+ return buffer;
+}
+
+/*
+ * While there is a version of OpenDarwin for intel, the usual case is
+ * big-endian PPC. So we need byte swapping to handle the
+ * little-endian byte order of the network protocol. We also need an
+ * additional dynamic buffer to do this work for incoming data blocks,
+ * because we have to consider the original data as constant.
+ *
+ * We abstract the differences away by providing a simple facade with
+ * these functions/macros:
+ *
+ * le_to_native(dst,src,len)
+ * native_to_le(cp,len)
+ * set_ucbuffer_with_le(buffer,bufsize,data,size)
+ * set_ucbuffer_with_le_copy(buffer,bufsize,data,size,reserve)
+ */
+#ifdef WORDS_BIGENDIAN
+
+static inline void swap_bytes (char * dst, const char * src, size_t len)
+{
+ const char *srcend = src + len;
+ while (src < srcend) {
+ dst[0] = src[1];
+ dst[1] = src[0];
+ dst += 2;
+ src += 2;
+ }
+}
+static inline void swap_bytes_inplace (char * cp, size_t len)
+{
+ char temp;
+ char *end = cp + len;
+ while (cp < end) {
+ temp = cp[1];
+ cp[1] = cp[0];
+ cp[0] = temp;
+ cp += 2;
+ }
+}
+
+#define le_to_native(dst,src,len) swap_bytes(dst,src,len)
+#define native_to_le(cp,len) swap_bytes_inplace(cp,len)
+#define set_ucbuffer_with_le(buffer,bufsize,data,size) \
+ set_ucbuffer_with_le_copy(buffer,bufsize,data,size,0)
+
+#else /* ! WORDS_BIGENDIAN */
+
+#define le_to_native(dst,src,len) memcpy(dst,src,len)
+#define native_to_le(cp,len) /* nothing */
+#define set_ucbuffer_with_le(buffer,bufsize,data,size) \
+ (((void)(bufsize)),(UniChar*)(data))
+
+#endif
+
+static inline UniChar *set_ucbuffer_with_le_copy (
+ UniChar *buffer, size_t *bufsize,
+ const void *data, size_t size, size_t reserve)
+{
+ buffer = resize_buffer(buffer, bufsize, size+reserve);
+ le_to_native((char*)buffer,data,size);
+ return buffer;
+}
+
+
+/*
+ * A simple hexdump function for debugging error conditions.
+ */
+#define debug_out(s) DEBUG(0,(s))
+
+#ifdef DEBUG_STRINGS
+
+static void hexdump( const char * label, const char * s, size_t len )
+{
+ size_t restlen = len;
+ debug_out("<<<<<<<\n");
+ debug_out(label);
+ debug_out("\n");
+ while (restlen > 0) {
+ char line[100];
+ size_t i, j;
+ char * d = line;
+#undef sprintf
+ d += sprintf(d, "%04X ", (unsigned)(len-restlen));
+ *d++ = ' ';
+ for( i = 0; i<restlen && i<8; ++i ) {
+ d += sprintf(d, "%02X ", ((unsigned)s[i]) & 0xFF);
+ }
+ for( j = i; j<8; ++j ) {
+ d += sprintf(d, " ");
+ }
+ *d++ = ' ';
+ for( i = 8; i<restlen && i<16; ++i ) {
+ d += sprintf(d, "%02X ", ((unsigned)s[i]) & 0xFF);
+ }
+ for( j = i; j<16; ++j ) {
+ d += sprintf(d, " ");
+ }
+ *d++ = ' ';
+ for( i = 0; i<restlen && i<16; ++i ) {
+ if(s[i] < ' ' || s[i] >= 0x7F || !isprint(s[i]))
+ *d++ = '.';
+ else
+ *d++ = s[i];
+ }
+ *d++ = '\n';
+ *d = 0;
+ restlen -= i;
+ s += i;
+ debug_out(line);
+ }
+ debug_out(">>>>>>>\n");
+}
+
+#else /* !DEBUG_STRINGS */
+
+#define hexdump(label,s,len) /* nothing */
+
+#endif
+
+
+#if !USE_INTERNAL_API
+
+/*
+ * An implementation based on documented Mac OS X APIs.
+ *
+ * This does a certain amount of memory management, creating and
+ * manipulating CFString objects. We try to minimize the impact by
+ * keeping those objects around and re-using them. We also use
+ * external backing store for the CFStrings where this is possible and
+ * benficial.
+ *
+ * The Unicode normalizations forms available at this level are
+ * generic, not specifically for the file system. So they may not be
+ * perfect fits.
+ */
+static size_t macosxfs_encoding_pull(
+ void *cd, /* Encoder handle */
+ char **inbuf, size_t *inbytesleft, /* Script string */
+ char **outbuf, size_t *outbytesleft) /* UTF-16-LE string */
+{
+ static const int script_code = kCFStringEncodingUTF8;
+ static CFMutableStringRef cfstring = NULL;
+ size_t outsize;
+ CFRange range;
+
+ (void) cd; /* UNUSED */
+
+ if (0 == *inbytesleft) {
+ return 0;
+ }
+
+ if (NULL == cfstring) {
+ /*
+ * A version with an external backing store as in the
+ * push function should have been more efficient, but
+ * testing shows, that it is actually slower (!).
+ * Maybe kCFAllocatorDefault gets shortcut evaluation
+ * internally, while kCFAllocatorNull doesn't.
+ */
+ cfstring = CFStringCreateMutable(kCFAllocatorDefault,0);
+ }
+
+ /*
+ * Three methods of appending to a CFString, choose the most
+ * efficient.
+ */
+ if (0 == (*inbuf)[*inbytesleft-1]) {
+ CFStringAppendCString(cfstring, *inbuf, script_code);
+ } else if (*inbytesleft <= 255) {
+ Str255 buffer;
+ buffer[0] = *inbytesleft;
+ memcpy(buffer+1, *inbuf, buffer[0]);
+ CFStringAppendPascalString(cfstring, buffer, script_code);
+ } else {
+ /*
+ * We would like to use a fixed buffer and a loop
+ * here, but than we can't garantee that the input is
+ * well-formed UTF-8, as we are supposed to do.
+ */
+ static char *buffer = NULL;
+ static size_t buflen = 0;
+ buffer = resize_buffer(buffer, &buflen, *inbytesleft+1);
+ memcpy(buffer, *inbuf, *inbytesleft);
+ buffer[*inbytesleft] = 0;
+ CFStringAppendCString(cfstring, *inbuf, script_code);
+ }
+
+ /*
+ * Compose characters, using the non-canonical composition
+ * form.
+ */
+ CFStringNormalize(cfstring, kCFStringNormalizationFormC);
+
+ outsize = CFStringGetLength(cfstring);
+ range = CFRangeMake(0,outsize);
+
+ if (outsize == 0) {
+ /*
+ * HACK: smbd/mangle_hash2.c:is_legal_name() expects
+ * errors here. That function will always pass 2
+ * characters. smbd/open.c:check_for_pipe() cuts a
+ * patchname to 10 characters blindly. Suppress the
+ * debug output in those cases.
+ */
+ if(2 != *inbytesleft && 10 != *inbytesleft) {
+ debug_out("String conversion: "
+ "An unknown error occurred\n");
+ hexdump("UTF8->UTF16LE (old) input",
+ *inbuf, *inbytesleft);
+ }
+ errno = EILSEQ; /* Not sure, but this is what we have
+ * actually seen. */
+ return -1;
+ }
+ if (outsize*2 > *outbytesleft) {
+ CFStringDelete(cfstring, range);
+ debug_out("String conversion: "
+ "Output buffer too small\n");
+ hexdump("UTF8->UTF16LE (old) input",
+ *inbuf, *inbytesleft);
+ errno = E2BIG;
+ return -1;
+ }
+
+ CFStringGetCharacters(cfstring, range, (UniChar*)*outbuf);
+ CFStringDelete(cfstring, range);
+
+ native_to_le(*outbuf, outsize*2);
+
+ /*
+ * Add a converted null byte, if the CFString conversions
+ * prevented that until now.
+ */
+ if (0 == (*inbuf)[*inbytesleft-1] &&
+ (0 != (*outbuf)[outsize*2-1] || 0 != (*outbuf)[outsize*2-2])) {
+
+ if ((outsize*2+2) > *outbytesleft) {
+ debug_out("String conversion: "
+ "Output buffer too small\n");
+ hexdump("UTF8->UTF16LE (old) input",
+ *inbuf, *inbytesleft);
+ errno = E2BIG;
+ return -1;
+ }
+
+ (*outbuf)[outsize*2] = (*outbuf)[outsize*2+1] = 0;
+ outsize += 2;
+ }
+
+ *inbuf += *inbytesleft;
+ *inbytesleft = 0;
+ *outbuf += outsize*2;
+ *outbytesleft -= outsize*2;
+
+ return 0;
+}
+
+static size_t macosxfs_encoding_push(
+ void *cd, /* Encoder handle */
+ char **inbuf, size_t *inbytesleft, /* UTF-16-LE string */
+ char **outbuf, size_t *outbytesleft) /* Script string */
+{
+ static const int script_code = kCFStringEncodingUTF8;
+ static CFMutableStringRef cfstring = NULL;
+ static UniChar *buffer = NULL;
+ static size_t buflen = 0;
+ CFIndex outsize, cfsize, charsconverted;
+
+ (void) cd; /* UNUSED */
+
+ if (0 == *inbytesleft) {
+ return 0;
+ }
+
+ /*
+ * We need a buffer that can hold 4 times the original data,
+ * because that is the theoretical maximum that decomposition
+ * can create currently (in Unicode 4.0).
+ */
+ buffer = set_ucbuffer_with_le_copy(
+ buffer, &buflen, *inbuf, *inbytesleft, 3 * *inbytesleft);
+
+ if (NULL == cfstring) {
+ cfstring = CFStringCreateMutableWithExternalCharactersNoCopy(
+ kCFAllocatorDefault,
+ buffer, *inbytesleft/2, buflen/2,
+ kCFAllocatorNull);
+ } else {
+ CFStringSetExternalCharactersNoCopy(
+ cfstring,
+ buffer, *inbytesleft/2, buflen/2);
+ }
+
+ /*
+ * Decompose characters, using the non-canonical decomposition
+ * form.
+ *
+ * NB: This isn't exactly what HFS+ wants (see note on
+ * kCFStringEncodingUseHFSPlusCanonical in
+ * CFStringEncodingConverter.h), but AFAIK it's the best that
+ * the official API can do.
+ */
+ CFStringNormalize(cfstring, kCFStringNormalizationFormD);
+
+ cfsize = CFStringGetLength(cfstring);
+ charsconverted = CFStringGetBytes(
+ cfstring, CFRangeMake(0,cfsize),
+ script_code, 0, False,
+ *outbuf, *outbytesleft, &outsize);
+
+ if (0 == charsconverted) {
+ debug_out("String conversion: "
+ "Buffer too small or not convertable\n");
+ hexdump("UTF16LE->UTF8 (old) input",
+ *inbuf, *inbytesleft);
+ errno = EILSEQ; /* Probably more likely. */
+ return -1;
+ }
+
+ /*
+ * Add a converted null byte, if the CFString conversions
+ * prevented that until now.
+ */
+ if (0 == (*inbuf)[*inbytesleft-1] && 0 == (*inbuf)[*inbytesleft-2] &&
+ (0 != (*outbuf)[outsize-1])) {
+
+ if (((size_t)outsize+1) > *outbytesleft) {
+ debug_out("String conversion: "
+ "Output buffer too small\n");
+ hexdump("UTF16LE->UTF8 (old) input",
+ *inbuf, *inbytesleft);
+ errno = E2BIG;
+ return -1;
+ }
+
+ (*outbuf)[outsize] = 0;
+ ++outsize;
+ }
+
+ *inbuf += *inbytesleft;
+ *inbytesleft = 0;
+ *outbuf += outsize;
+ *outbytesleft -= outsize;
+
+ return 0;
+}
+
+#else /* USE_INTERNAL_API */
+
+/*
+ * An implementation based on internal code as known from the
+ * OpenDarwin CVS.
+ *
+ * This code doesn't need much memory management because it uses
+ * functions that operate on the raw memory directly.
+ *
+ * The push routine here is faster and more compatible with HFS+ than
+ * the other implementation above. The pull routine is only faster
+ * for some strings, slightly slower for others. The pull routine
+ * looses because it has to iterate over the data twice, once to
+ * decode UTF-8 and than to do the character composition required by
+ * Windows.
+ */
+static size_t macosxfs_encoding_pull(
+ void *cd, /* Encoder handle */
+ char **inbuf, size_t *inbytesleft, /* Script string */
+ char **outbuf, size_t *outbytesleft) /* UTF-16-LE string */
+{
+ static const int script_code = kCFStringEncodingUTF8;
+ UInt32 srcCharsUsed = 0;
+ UInt32 dstCharsUsed = 0;
+ UInt32 result;
+ uint32_t dstDecomposedUsed = 0;
+ uint32_t dstPrecomposedUsed = 0;
+
+ (void) cd; /* UNUSED */
+
+ if (0 == *inbytesleft) {
+ return 0;
+ }
+
+ result = CFStringEncodingBytesToUnicode(
+ script_code, kCFStringEncodingComposeCombinings,
+ *inbuf, *inbytesleft, &srcCharsUsed,
+ (UniChar*)*outbuf, *outbytesleft, &dstCharsUsed);
+
+ switch(result) {
+ case kCFStringEncodingConversionSuccess:
+ if (*inbytesleft == srcCharsUsed)
+ break;
+ else
+ ; /*fall through*/
+ case kCFStringEncodingInsufficientOutputBufferLength:
+ debug_out("String conversion: "
+ "Output buffer too small\n");
+ hexdump("UTF8->UTF16LE (new) input",
+ *inbuf, *inbytesleft);
+ errno = E2BIG;
+ return -1;
+ case kCFStringEncodingInvalidInputStream:
+ /*
+ * HACK: smbd/mangle_hash2.c:is_legal_name() expects
+ * errors here. That function will always pass 2
+ * characters. smbd/open.c:check_for_pipe() cuts a
+ * patchname to 10 characters blindly. Suppress the
+ * debug output in those cases.
+ */
+ if(2 != *inbytesleft && 10 != *inbytesleft) {
+ debug_out("String conversion: "
+ "Invalid input sequence\n");
+ hexdump("UTF8->UTF16LE (new) input",
+ *inbuf, *inbytesleft);
+ }
+ errno = EILSEQ;
+ return -1;
+ case kCFStringEncodingConverterUnavailable:
+ debug_out("String conversion: "
+ "Unknown encoding\n");
+ hexdump("UTF8->UTF16LE (new) input",
+ *inbuf, *inbytesleft);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /*
+ * It doesn't look like CFStringEncodingBytesToUnicode() can
+ * produce precomposed characters (flags=ComposeCombinings
+ * doesn't do it), so we need another pass over the data here.
+ * We can do this in-place, as the string can only get
+ * shorter.
+ *
+ * (Actually in theory there should be an internal
+ * decomposition and reordering before the actual composition
+ * step. But we should be able to rely on that we always get
+ * fully decomposed strings for input, so this can't create
+ * problems in reality.)
+ */
+ CFUniCharPrecompose(
+ (const UTF16Char *)*outbuf, dstCharsUsed, &dstDecomposedUsed,
+ (UTF16Char *)*outbuf, dstCharsUsed, &dstPrecomposedUsed);
+
+ native_to_le(*outbuf, dstPrecomposedUsed*2);
+
+ *inbuf += srcCharsUsed;
+ *inbytesleft -= srcCharsUsed;
+ *outbuf += dstPrecomposedUsed*2;
+ *outbytesleft -= dstPrecomposedUsed*2;
+
+ return 0;
+}
+
+static size_t macosxfs_encoding_push(
+ void *cd, /* Encoder handle */
+ char **inbuf, size_t *inbytesleft, /* UTF-16-LE string */
+ char **outbuf, size_t *outbytesleft) /* Script string */
+{
+ static const int script_code = kCFStringEncodingUTF8;
+ static UniChar *buffer = NULL;
+ static size_t buflen = 0;
+ UInt32 srcCharsUsed=0, dstCharsUsed=0, result;
+
+ (void) cd; /* UNUSED */
+
+ if (0 == *inbytesleft) {
+ return 0;
+ }
+
+ buffer = set_ucbuffer_with_le(
+ buffer, &buflen, *inbuf, *inbytesleft);
+
+ result = CFStringEncodingUnicodeToBytes(
+ script_code, kCFStringEncodingUseHFSPlusCanonical,
+ buffer, *inbytesleft/2, &srcCharsUsed,
+ *outbuf, *outbytesleft, &dstCharsUsed);
+
+ switch(result) {
+ case kCFStringEncodingConversionSuccess:
+ if (*inbytesleft/2 == srcCharsUsed)
+ break;
+ else
+ ; /*fall through*/
+ case kCFStringEncodingInsufficientOutputBufferLength:
+ debug_out("String conversion: "
+ "Output buffer too small\n");
+ hexdump("UTF16LE->UTF8 (new) input",
+ *inbuf, *inbytesleft);
+ errno = E2BIG;
+ return -1;
+ case kCFStringEncodingInvalidInputStream:
+ /*
+ * HACK: smbd/open.c:check_for_pipe():is_legal_name()
+ * cuts a pathname to 10 characters blindly. Suppress
+ * the debug output in those cases.
+ */
+ if(10 != *inbytesleft) {
+ debug_out("String conversion: "
+ "Invalid input sequence\n");
+ hexdump("UTF16LE->UTF8 (new) input",
+ *inbuf, *inbytesleft);
+ }
+ errno = EILSEQ;
+ return -1;
+ case kCFStringEncodingConverterUnavailable:
+ debug_out("String conversion: "
+ "Unknown encoding\n");
+ hexdump("UTF16LE->UTF8 (new) input",
+ *inbuf, *inbytesleft);
+ errno = EINVAL;
+ return -1;
+ }
+
+ *inbuf += srcCharsUsed*2;
+ *inbytesleft -= srcCharsUsed*2;
+ *outbuf += dstCharsUsed;
+ *outbytesleft -= dstCharsUsed;
+
+ return 0;
+}
+
+#endif /* USE_INTERNAL_API */
+
+/*
+ * For initialization, actually install the encoding as "macosxfs".
+ */
+static struct charset_functions macosxfs_encoding_functions = {
+ "MACOSXFS", macosxfs_encoding_pull, macosxfs_encoding_push
+};
+
+NTSTATUS init_module(void)
+{
+ return smb_register_charset(&macosxfs_encoding_functions);
+}
+
+/* eof */
diff --git a/source/modules/developer.c b/source/modules/developer.c
new file mode 100644
index 00000000000..7ffc3ff50d2
--- /dev/null
+++ b/source/modules/developer.c
@@ -0,0 +1,132 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba module with developer tools
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Jelmer Vernooij 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static struct {
+ char from;
+ char *to;
+ int len;
+} weird_table[] = {
+ {'q', "^q^", 3},
+ {'Q', "^Q^", 3},
+ {0, NULL}
+};
+
+static size_t weird_pull(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+ while (*inbytesleft >= 1 && *outbytesleft >= 2) {
+ int i;
+ int done = 0;
+ for (i=0;weird_table[i].from;i++) {
+ if (strncmp((*inbuf),
+ weird_table[i].to,
+ weird_table[i].len) == 0) {
+ if (*inbytesleft < weird_table[i].len) {
+ DEBUG(0,("ERROR: truncated weird string\n"));
+ /* smb_panic("weird_pull"); */
+
+ } else {
+ (*outbuf)[0] = weird_table[i].from;
+ (*outbuf)[1] = 0;
+ (*inbytesleft) -= weird_table[i].len;
+ (*outbytesleft) -= 2;
+ (*inbuf) += weird_table[i].len;
+ (*outbuf) += 2;
+ done = 1;
+ break;
+ }
+ }
+ }
+ if (done) continue;
+ (*outbuf)[0] = (*inbuf)[0];
+ (*outbuf)[1] = 0;
+ (*inbytesleft) -= 1;
+ (*outbytesleft) -= 2;
+ (*inbuf) += 1;
+ (*outbuf) += 2;
+ }
+
+ if (*inbytesleft > 0) {
+ errno = E2BIG;
+ return -1;
+ }
+
+ return 0;
+}
+
+static size_t weird_push(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+ int ir_count=0;
+
+ while (*inbytesleft >= 2 && *outbytesleft >= 1) {
+ int i;
+ int done=0;
+ for (i=0;weird_table[i].from;i++) {
+ if ((*inbuf)[0] == weird_table[i].from &&
+ (*inbuf)[1] == 0) {
+ if (*outbytesleft < weird_table[i].len) {
+ DEBUG(0,("No room for weird character\n"));
+ /* smb_panic("weird_push"); */
+ } else {
+ memcpy(*outbuf, weird_table[i].to,
+ weird_table[i].len);
+ (*inbytesleft) -= 2;
+ (*outbytesleft) -= weird_table[i].len;
+ (*inbuf) += 2;
+ (*outbuf) += weird_table[i].len;
+ done = 1;
+ break;
+ }
+ }
+ }
+ if (done) continue;
+
+ (*outbuf)[0] = (*inbuf)[0];
+ if ((*inbuf)[1]) ir_count++;
+ (*inbytesleft) -= 2;
+ (*outbytesleft) -= 1;
+ (*inbuf) += 2;
+ (*outbuf) += 1;
+ }
+
+ if (*inbytesleft == 1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (*inbytesleft > 1) {
+ errno = E2BIG;
+ return -1;
+ }
+
+ return ir_count;
+}
+
+struct charset_functions weird_functions = {"WEIRD", weird_pull, weird_push};
+
+int charset_weird_init(void)
+{
+ smb_register_charset(&weird_functions);
+ return True;
+}
diff --git a/source/modules/getdate.c b/source/modules/getdate.c
new file mode 100644
index 00000000000..491c51294e9
--- /dev/null
+++ b/source/modules/getdate.c
@@ -0,0 +1,2460 @@
+/* A Bison parser, made by GNU Bison 1.875a. */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+/* Written by Richard Stallman by simplifying the original so called
+ ``semantic'' parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 1
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ tAGO = 258,
+ tDST = 259,
+ tDAY = 260,
+ tDAY_UNIT = 261,
+ tDAYZONE = 262,
+ tHOUR_UNIT = 263,
+ tLOCAL_ZONE = 264,
+ tMERIDIAN = 265,
+ tMINUTE_UNIT = 266,
+ tMONTH = 267,
+ tMONTH_UNIT = 268,
+ tSEC_UNIT = 269,
+ tYEAR_UNIT = 270,
+ tZONE = 271,
+ tSNUMBER = 272,
+ tUNUMBER = 273
+ };
+#endif
+#define tAGO 258
+#define tDST 259
+#define tDAY 260
+#define tDAY_UNIT 261
+#define tDAYZONE 262
+#define tHOUR_UNIT 263
+#define tLOCAL_ZONE 264
+#define tMERIDIAN 265
+#define tMINUTE_UNIT 266
+#define tMONTH 267
+#define tMONTH_UNIT 268
+#define tSEC_UNIT 269
+#define tYEAR_UNIT 270
+#define tZONE 271
+#define tSNUMBER 272
+#define tUNUMBER 273
+
+
+
+
+/* Copy the first part of user declarations. */
+#line 1 "getdate.y"
+
+/* Parse a string into an internal time stamp.
+ Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* Originally written by Steven M. Bellovin <smb@research.att.com> while
+ at the University of North Carolina at Chapel Hill. Later tweaked by
+ a couple of people on Usenet. Completely overhauled by Rich $alz
+ <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990.
+
+ Modified by Paul Eggert <eggert@twinsun.com> in August 1999 to do
+ the right thing about local DST. Unlike previous versions, this
+ version is reentrant. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+# ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+# endif
+#endif
+
+/* Since the code of getdate.y is not included in the Emacs executable
+ itself, there is no need to #define static in this file. Even if
+ the code were included in the Emacs executable, it probably
+ wouldn't do any harm to #undef it here; this will only cause
+ problems if we try to write to a static variable, which I don't
+ think this code needs to do. */
+#ifdef emacs
+# undef static
+#endif
+
+#include <ctype.h>
+
+#if HAVE_STDLIB_H
+# include <stdlib.h> /* for `free'; used by Bison 1.27 */
+#endif
+
+#if STDC_HEADERS || (! defined isascii && ! HAVE_ISASCII)
+# define IN_CTYPE_DOMAIN(c) 1
+#else
+# define IN_CTYPE_DOMAIN(c) isascii (c)
+#endif
+
+#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
+#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
+#define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c))
+#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
+
+/* ISDIGIT differs from ISDIGIT_LOCALE, as follows:
+ - Its arg may be any int or unsigned int; it need not be an unsigned char.
+ - It's guaranteed to evaluate its argument exactly once.
+ - It's typically faster.
+ POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
+ ISDIGIT_LOCALE unless it's important to use the locale's definition
+ of `digit' even when the host does not conform to POSIX. */
+#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
+
+#if STDC_HEADERS || HAVE_STRING_H
+# include <string.h>
+#endif
+
+#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
+# define __attribute__(x)
+#endif
+
+#ifndef ATTRIBUTE_UNUSED
+# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
+#endif
+
+#define EPOCH_YEAR 1970
+#define TM_YEAR_BASE 1900
+
+#define HOUR(x) ((x) * 60)
+
+/* An integer value, and the number of digits in its textual
+ representation. */
+typedef struct
+{
+ int value;
+ int digits;
+} textint;
+
+/* An entry in the lexical lookup table. */
+typedef struct
+{
+ char const *name;
+ int type;
+ int value;
+} table;
+
+/* Meridian: am, pm, or 24-hour style. */
+enum { MERam, MERpm, MER24 };
+
+/* Information passed to and from the parser. */
+typedef struct
+{
+ /* The input string remaining to be parsed. */
+ const char *input;
+
+ /* N, if this is the Nth Tuesday. */
+ int day_ordinal;
+
+ /* Day of week; Sunday is 0. */
+ int day_number;
+
+ /* tm_isdst flag for the local zone. */
+ int local_isdst;
+
+ /* Time zone, in minutes east of UTC. */
+ int time_zone;
+
+ /* Style used for time. */
+ int meridian;
+
+ /* Gregorian year, month, day, hour, minutes, and seconds. */
+ textint year;
+ int month;
+ int day;
+ int hour;
+ int minutes;
+ int seconds;
+
+ /* Relative year, month, day, hour, minutes, and seconds. */
+ int rel_year;
+ int rel_month;
+ int rel_day;
+ int rel_hour;
+ int rel_minutes;
+ int rel_seconds;
+
+ /* Counts of nonterminals of various flavors parsed so far. */
+ int dates_seen;
+ int days_seen;
+ int local_zones_seen;
+ int rels_seen;
+ int times_seen;
+ int zones_seen;
+
+ /* Table of local time zone abbrevations, terminated by a null entry. */
+ table local_time_zone_table[3];
+} parser_control;
+
+#define PC (* (parser_control *) parm)
+#define YYLEX_PARAM parm
+#define YYPARSE_PARAM parm
+
+static int yyerror ();
+static int yylex ();
+
+
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
+#line 172 "getdate.y"
+typedef union YYSTYPE {
+ int intval;
+ textint textintval;
+} YYSTYPE;
+/* Line 191 of yacc.c. */
+#line 281 "getdate.c"
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 214 of yacc.c. */
+#line 293 "getdate.c"
+
+#if ! defined (yyoverflow) || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# if YYSTACK_USE_ALLOCA
+# define YYSTACK_ALLOC alloca
+# else
+# ifndef YYSTACK_USE_ALLOCA
+# if defined (alloca) || defined (_ALLOCA_H)
+# define YYSTACK_ALLOC alloca
+# else
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+# else
+# if defined (__STDC__) || defined (__cplusplus)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# endif
+# define YYSTACK_ALLOC malloc
+# define YYSTACK_FREE free
+# endif
+#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */
+
+
+#if (! defined (yyoverflow) \
+ && (! defined (__cplusplus) \
+ || (YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ short yyss;
+ YYSTYPE yyvs;
+ };
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (short) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ register YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (0)
+# endif
+# endif
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack, Stack, yysize); \
+ Stack = &yyptr->Stack; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (0)
+
+#endif
+
+#if defined (__STDC__) || defined (__cplusplus)
+ typedef signed char yysigned_char;
+#else
+ typedef short yysigned_char;
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 2
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 52
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 22
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 12
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 54
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 64
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 273
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const unsigned char yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 20, 2, 2, 21, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 19, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const unsigned char yyprhs[] =
+{
+ 0, 0, 3, 4, 7, 9, 11, 13, 15, 17,
+ 19, 21, 24, 29, 34, 41, 48, 50, 53, 55,
+ 57, 60, 62, 65, 68, 72, 78, 82, 86, 89,
+ 94, 97, 101, 104, 106, 109, 112, 114, 117, 120,
+ 122, 125, 128, 130, 133, 136, 138, 141, 144, 146,
+ 149, 152, 154, 156, 157
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yysigned_char yyrhs[] =
+{
+ 23, 0, -1, -1, 23, 24, -1, 25, -1, 26,
+ -1, 27, -1, 29, -1, 28, -1, 30, -1, 32,
+ -1, 18, 10, -1, 18, 19, 18, 33, -1, 18,
+ 19, 18, 17, -1, 18, 19, 18, 19, 18, 33,
+ -1, 18, 19, 18, 19, 18, 17, -1, 9, -1,
+ 9, 4, -1, 16, -1, 7, -1, 16, 4, -1,
+ 5, -1, 5, 20, -1, 18, 5, -1, 18, 21,
+ 18, -1, 18, 21, 18, 21, 18, -1, 18, 17,
+ 17, -1, 18, 12, 17, -1, 12, 18, -1, 12,
+ 18, 20, 18, -1, 18, 12, -1, 18, 12, 18,
+ -1, 31, 3, -1, 31, -1, 18, 15, -1, 17,
+ 15, -1, 15, -1, 18, 13, -1, 17, 13, -1,
+ 13, -1, 18, 6, -1, 17, 6, -1, 6, -1,
+ 18, 8, -1, 17, 8, -1, 8, -1, 18, 11,
+ -1, 17, 11, -1, 11, -1, 18, 14, -1, 17,
+ 14, -1, 14, -1, 18, -1, -1, 10, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const unsigned short yyrline[] =
+{
+ 0, 188, 188, 190, 194, 196, 198, 200, 202, 204,
+ 206, 210, 217, 224, 232, 239, 251, 253, 258, 260,
+ 262, 267, 272, 277, 285, 290, 310, 317, 325, 330,
+ 336, 341, 350, 359, 363, 365, 367, 369, 371, 373,
+ 375, 377, 379, 381, 383, 385, 387, 389, 391, 393,
+ 395, 397, 402, 439, 440
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE
+/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "tAGO", "tDST", "tDAY", "tDAY_UNIT",
+ "tDAYZONE", "tHOUR_UNIT", "tLOCAL_ZONE", "tMERIDIAN", "tMINUTE_UNIT",
+ "tMONTH", "tMONTH_UNIT", "tSEC_UNIT", "tYEAR_UNIT", "tZONE", "tSNUMBER",
+ "tUNUMBER", "':'", "','", "'/'", "$accept", "spec", "item", "time",
+ "local_zone", "zone", "day", "date", "rel", "relunit", "number",
+ "o_merid", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const unsigned short yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 58,
+ 44, 47
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const unsigned char yyr1[] =
+{
+ 0, 22, 23, 23, 24, 24, 24, 24, 24, 24,
+ 24, 25, 25, 25, 25, 25, 26, 26, 27, 27,
+ 27, 28, 28, 28, 29, 29, 29, 29, 29, 29,
+ 29, 29, 30, 30, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 32, 33, 33
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const unsigned char yyr2[] =
+{
+ 0, 2, 0, 2, 1, 1, 1, 1, 1, 1,
+ 1, 2, 4, 4, 6, 6, 1, 2, 1, 1,
+ 2, 1, 2, 2, 3, 5, 3, 3, 2, 4,
+ 2, 3, 2, 1, 2, 2, 1, 2, 2, 1,
+ 2, 2, 1, 2, 2, 1, 2, 2, 1, 2,
+ 2, 1, 1, 0, 1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const unsigned char yydefact[] =
+{
+ 2, 0, 1, 21, 42, 19, 45, 16, 48, 0,
+ 39, 51, 36, 18, 0, 52, 3, 4, 5, 6,
+ 8, 7, 9, 33, 10, 22, 17, 28, 20, 41,
+ 44, 47, 38, 50, 35, 23, 40, 43, 11, 46,
+ 30, 37, 49, 34, 0, 0, 0, 32, 0, 27,
+ 31, 26, 53, 24, 29, 54, 13, 0, 12, 0,
+ 53, 25, 15, 14
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yysigned_char yydefgoto[] =
+{
+ -1, 1, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 58
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -17
+static const yysigned_char yypact[] =
+{
+ -17, 0, -17, 1, -17, -17, -17, 19, -17, -14,
+ -17, -17, -17, 32, 26, 14, -17, -17, -17, -17,
+ -17, -17, -17, 27, -17, -17, -17, 22, -17, -17,
+ -17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
+ -16, -17, -17, -17, 29, 25, 30, -17, 31, -17,
+ -17, -17, 28, 23, -17, -17, -17, 33, -17, 34,
+ -7, -17, -17, -17
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yysigned_char yypgoto[] =
+{
+ -17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
+ -17, -10
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If zero, do what YYDEFACT says.
+ If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -1
+static const unsigned char yytable[] =
+{
+ 2, 49, 50, 55, 27, 3, 4, 5, 6, 7,
+ 62, 8, 9, 10, 11, 12, 13, 14, 15, 35,
+ 36, 25, 37, 26, 38, 39, 40, 41, 42, 43,
+ 47, 44, 29, 45, 30, 46, 28, 31, 55, 32,
+ 33, 34, 48, 52, 59, 56, 51, 57, 53, 54,
+ 63, 60, 61
+};
+
+static const unsigned char yycheck[] =
+{
+ 0, 17, 18, 10, 18, 5, 6, 7, 8, 9,
+ 17, 11, 12, 13, 14, 15, 16, 17, 18, 5,
+ 6, 20, 8, 4, 10, 11, 12, 13, 14, 15,
+ 3, 17, 6, 19, 8, 21, 4, 11, 10, 13,
+ 14, 15, 20, 18, 21, 17, 17, 19, 18, 18,
+ 60, 18, 18
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const unsigned char yystos[] =
+{
+ 0, 23, 0, 5, 6, 7, 8, 9, 11, 12,
+ 13, 14, 15, 16, 17, 18, 24, 25, 26, 27,
+ 28, 29, 30, 31, 32, 20, 4, 18, 4, 6,
+ 8, 11, 13, 14, 15, 5, 6, 8, 10, 11,
+ 12, 13, 14, 15, 17, 19, 21, 3, 20, 17,
+ 18, 17, 18, 18, 18, 10, 17, 19, 33, 21,
+ 18, 18, 17, 33
+};
+
+#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__)
+# define YYSIZE_T __SIZE_TYPE__
+#endif
+#if ! defined (YYSIZE_T) && defined (size_t)
+# define YYSIZE_T size_t
+#endif
+#if ! defined (YYSIZE_T)
+# if defined (__STDC__) || defined (__cplusplus)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# endif
+#endif
+#if ! defined (YYSIZE_T)
+# define YYSIZE_T unsigned int
+#endif
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrlab1
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+
+#define YYFAIL goto yyerrlab
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ yytoken = YYTRANSLATE (yychar); \
+ YYPOPSTACK; \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror ("syntax error: cannot back up");\
+ YYERROR; \
+ } \
+while (0)
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+/* YYLLOC_DEFAULT -- Compute the default location (before the actions
+ are run). */
+
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ Current.first_line = Rhs[1].first_line; \
+ Current.first_column = Rhs[1].first_column; \
+ Current.last_line = Rhs[N].last_line; \
+ Current.last_column = Rhs[N].last_column;
+#endif
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (&yylval, YYLEX_PARAM)
+#else
+# define YYLEX yylex (&yylval)
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (0)
+
+# define YYDSYMPRINT(Args) \
+do { \
+ if (yydebug) \
+ yysymprint Args; \
+} while (0)
+
+# define YYDSYMPRINTF(Title, Token, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yysymprint (stderr, \
+ Token, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (0)
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (cinluded). |
+`------------------------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_stack_print (short *bottom, short *top)
+#else
+static void
+yy_stack_print (bottom, top)
+ short *bottom;
+ short *top;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (/* Nothing. */; bottom <= top; ++bottom)
+ YYFPRINTF (stderr, " %d", *bottom);
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_reduce_print (int yyrule)
+#else
+static void
+yy_reduce_print (yyrule)
+ int yyrule;
+#endif
+{
+ int yyi;
+ unsigned int yylineno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ",
+ yyrule - 1, yylineno);
+ /* Print the symbols being reduced, and their result. */
+ for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++)
+ YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]);
+ YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]);
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (Rule); \
+} while (0)
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YYDSYMPRINT(Args)
+# define YYDSYMPRINTF(Title, Token, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#if YYMAXDEPTH == 0
+# undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined (__GLIBC__) && defined (_STRING_H)
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+static YYSIZE_T
+# if defined (__STDC__) || defined (__cplusplus)
+yystrlen (const char *yystr)
+# else
+yystrlen (yystr)
+ const char *yystr;
+# endif
+{
+ register const char *yys = yystr;
+
+ while (*yys++ != '\0')
+ continue;
+
+ return yys - yystr - 1;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE)
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+static char *
+# if defined (__STDC__) || defined (__cplusplus)
+yystpcpy (char *yydest, const char *yysrc)
+# else
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+# endif
+{
+ register char *yyd = yydest;
+ register const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+#endif /* !YYERROR_VERBOSE */
+
+
+
+#if YYDEBUG
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yysymprint (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ /* Pacify ``unused variable'' warnings. */
+ (void) yyvaluep;
+
+ if (yytype < YYNTOKENS)
+ {
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+# ifdef YYPRINT
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# endif
+ }
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+ YYFPRINTF (yyoutput, ")");
+}
+
+#endif /* ! YYDEBUG */
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yydestruct (int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yytype, yyvaluep)
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ /* Pacify ``unused variable'' warnings. */
+ (void) yyvaluep;
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM);
+# else
+int yyparse ();
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM)
+# else
+int yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+ /* The lookahead symbol. */
+int yychar;
+
+/* The semantic value of the lookahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+ register int yystate;
+ register int yyn;
+ int yyresult;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+ /* Lookahead token as an internal (translated) token number. */
+ int yytoken = 0;
+
+ /* Three stacks and their tools:
+ `yyss': related to states,
+ `yyvs': related to semantic values,
+ `yyls': related to locations.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ short yyssa[YYINITDEPTH];
+ short *yyss = yyssa;
+ register short *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs = yyvsa;
+ register YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK (yyvsp--, yyssp--)
+
+ YYSIZE_T yystacksize = YYINITDEPTH;
+
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+
+ /* When reducing, the number of symbols on the RHS of the reduced
+ rule. */
+ int yylen;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. so pushing a state here evens the stacks.
+ */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ short *yyss1 = yyss;
+
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow ("parser stack overflow",
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyoverflowlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyoverflowlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ short *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyoverflowlab;
+ YYSTACK_RELOCATE (yyss);
+ YYSTACK_RELOCATE (yyvs);
+
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+/* Do appropriate processing given the current state. */
+/* Read a lookahead token if we need one and don't already have one. */
+/* yyresume: */
+
+ /* First try to decide what to do without reference to lookahead token. */
+
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YYDSYMPRINTF ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Shift the lookahead token. */
+ YYDPRINTF ((stderr, "Shifting token %s, ", yytname[yytoken]));
+
+ /* Discard the token being shifted unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ *++yyvsp = yylval;
+
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 4:
+#line 195 "getdate.y"
+ { PC.times_seen++; }
+ break;
+
+ case 5:
+#line 197 "getdate.y"
+ { PC.local_zones_seen++; }
+ break;
+
+ case 6:
+#line 199 "getdate.y"
+ { PC.zones_seen++; }
+ break;
+
+ case 7:
+#line 201 "getdate.y"
+ { PC.dates_seen++; }
+ break;
+
+ case 8:
+#line 203 "getdate.y"
+ { PC.days_seen++; }
+ break;
+
+ case 9:
+#line 205 "getdate.y"
+ { PC.rels_seen++; }
+ break;
+
+ case 11:
+#line 211 "getdate.y"
+ {
+ PC.hour = yyvsp[-1].textintval.value;
+ PC.minutes = 0;
+ PC.seconds = 0;
+ PC.meridian = yyvsp[0].intval;
+ }
+ break;
+
+ case 12:
+#line 218 "getdate.y"
+ {
+ PC.hour = yyvsp[-3].textintval.value;
+ PC.minutes = yyvsp[-1].textintval.value;
+ PC.seconds = 0;
+ PC.meridian = yyvsp[0].intval;
+ }
+ break;
+
+ case 13:
+#line 225 "getdate.y"
+ {
+ PC.hour = yyvsp[-3].textintval.value;
+ PC.minutes = yyvsp[-1].textintval.value;
+ PC.meridian = MER24;
+ PC.zones_seen++;
+ PC.time_zone = yyvsp[0].textintval.value % 100 + (yyvsp[0].textintval.value / 100) * 60;
+ }
+ break;
+
+ case 14:
+#line 233 "getdate.y"
+ {
+ PC.hour = yyvsp[-5].textintval.value;
+ PC.minutes = yyvsp[-3].textintval.value;
+ PC.seconds = yyvsp[-1].textintval.value;
+ PC.meridian = yyvsp[0].intval;
+ }
+ break;
+
+ case 15:
+#line 240 "getdate.y"
+ {
+ PC.hour = yyvsp[-5].textintval.value;
+ PC.minutes = yyvsp[-3].textintval.value;
+ PC.seconds = yyvsp[-1].textintval.value;
+ PC.meridian = MER24;
+ PC.zones_seen++;
+ PC.time_zone = yyvsp[0].textintval.value % 100 + (yyvsp[0].textintval.value / 100) * 60;
+ }
+ break;
+
+ case 16:
+#line 252 "getdate.y"
+ { PC.local_isdst = yyvsp[0].intval; }
+ break;
+
+ case 17:
+#line 254 "getdate.y"
+ { PC.local_isdst = yyvsp[-1].intval < 0 ? 1 : yyvsp[-1].intval + 1; }
+ break;
+
+ case 18:
+#line 259 "getdate.y"
+ { PC.time_zone = yyvsp[0].intval; }
+ break;
+
+ case 19:
+#line 261 "getdate.y"
+ { PC.time_zone = yyvsp[0].intval + 60; }
+ break;
+
+ case 20:
+#line 263 "getdate.y"
+ { PC.time_zone = yyvsp[-1].intval + 60; }
+ break;
+
+ case 21:
+#line 268 "getdate.y"
+ {
+ PC.day_ordinal = 1;
+ PC.day_number = yyvsp[0].intval;
+ }
+ break;
+
+ case 22:
+#line 273 "getdate.y"
+ {
+ PC.day_ordinal = 1;
+ PC.day_number = yyvsp[-1].intval;
+ }
+ break;
+
+ case 23:
+#line 278 "getdate.y"
+ {
+ PC.day_ordinal = yyvsp[-1].textintval.value;
+ PC.day_number = yyvsp[0].intval;
+ }
+ break;
+
+ case 24:
+#line 286 "getdate.y"
+ {
+ PC.month = yyvsp[-2].textintval.value;
+ PC.day = yyvsp[0].textintval.value;
+ }
+ break;
+
+ case 25:
+#line 291 "getdate.y"
+ {
+ /* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
+ otherwise as MM/DD/YY.
+ The goal in recognizing YYYY/MM/DD is solely to support legacy
+ machine-generated dates like those in an RCS log listing. If
+ you want portability, use the ISO 8601 format. */
+ if (4 <= yyvsp[-4].textintval.digits)
+ {
+ PC.year = yyvsp[-4].textintval;
+ PC.month = yyvsp[-2].textintval.value;
+ PC.day = yyvsp[0].textintval.value;
+ }
+ else
+ {
+ PC.month = yyvsp[-4].textintval.value;
+ PC.day = yyvsp[-2].textintval.value;
+ PC.year = yyvsp[0].textintval;
+ }
+ }
+ break;
+
+ case 26:
+#line 311 "getdate.y"
+ {
+ /* ISO 8601 format. YYYY-MM-DD. */
+ PC.year = yyvsp[-2].textintval;
+ PC.month = -yyvsp[-1].textintval.value;
+ PC.day = -yyvsp[0].textintval.value;
+ }
+ break;
+
+ case 27:
+#line 318 "getdate.y"
+ {
+ /* e.g. 17-JUN-1992. */
+ PC.day = yyvsp[-2].textintval.value;
+ PC.month = yyvsp[-1].intval;
+ PC.year.value = -yyvsp[0].textintval.value;
+ PC.year.digits = yyvsp[0].textintval.digits;
+ }
+ break;
+
+ case 28:
+#line 326 "getdate.y"
+ {
+ PC.month = yyvsp[-1].intval;
+ PC.day = yyvsp[0].textintval.value;
+ }
+ break;
+
+ case 29:
+#line 331 "getdate.y"
+ {
+ PC.month = yyvsp[-3].intval;
+ PC.day = yyvsp[-2].textintval.value;
+ PC.year = yyvsp[0].textintval;
+ }
+ break;
+
+ case 30:
+#line 337 "getdate.y"
+ {
+ PC.day = yyvsp[-1].textintval.value;
+ PC.month = yyvsp[0].intval;
+ }
+ break;
+
+ case 31:
+#line 342 "getdate.y"
+ {
+ PC.day = yyvsp[-2].textintval.value;
+ PC.month = yyvsp[-1].intval;
+ PC.year = yyvsp[0].textintval;
+ }
+ break;
+
+ case 32:
+#line 351 "getdate.y"
+ {
+ PC.rel_seconds = -PC.rel_seconds;
+ PC.rel_minutes = -PC.rel_minutes;
+ PC.rel_hour = -PC.rel_hour;
+ PC.rel_day = -PC.rel_day;
+ PC.rel_month = -PC.rel_month;
+ PC.rel_year = -PC.rel_year;
+ }
+ break;
+
+ case 34:
+#line 364 "getdate.y"
+ { PC.rel_year += yyvsp[-1].textintval.value * yyvsp[0].intval; }
+ break;
+
+ case 35:
+#line 366 "getdate.y"
+ { PC.rel_year += yyvsp[-1].textintval.value * yyvsp[0].intval; }
+ break;
+
+ case 36:
+#line 368 "getdate.y"
+ { PC.rel_year += yyvsp[0].intval; }
+ break;
+
+ case 37:
+#line 370 "getdate.y"
+ { PC.rel_month += yyvsp[-1].textintval.value * yyvsp[0].intval; }
+ break;
+
+ case 38:
+#line 372 "getdate.y"
+ { PC.rel_month += yyvsp[-1].textintval.value * yyvsp[0].intval; }
+ break;
+
+ case 39:
+#line 374 "getdate.y"
+ { PC.rel_month += yyvsp[0].intval; }
+ break;
+
+ case 40:
+#line 376 "getdate.y"
+ { PC.rel_day += yyvsp[-1].textintval.value * yyvsp[0].intval; }
+ break;
+
+ case 41:
+#line 378 "getdate.y"
+ { PC.rel_day += yyvsp[-1].textintval.value * yyvsp[0].intval; }
+ break;
+
+ case 42:
+#line 380 "getdate.y"
+ { PC.rel_day += yyvsp[0].intval; }
+ break;
+
+ case 43:
+#line 382 "getdate.y"
+ { PC.rel_hour += yyvsp[-1].textintval.value * yyvsp[0].intval; }
+ break;
+
+ case 44:
+#line 384 "getdate.y"
+ { PC.rel_hour += yyvsp[-1].textintval.value * yyvsp[0].intval; }
+ break;
+
+ case 45:
+#line 386 "getdate.y"
+ { PC.rel_hour += yyvsp[0].intval; }
+ break;
+
+ case 46:
+#line 388 "getdate.y"
+ { PC.rel_minutes += yyvsp[-1].textintval.value * yyvsp[0].intval; }
+ break;
+
+ case 47:
+#line 390 "getdate.y"
+ { PC.rel_minutes += yyvsp[-1].textintval.value * yyvsp[0].intval; }
+ break;
+
+ case 48:
+#line 392 "getdate.y"
+ { PC.rel_minutes += yyvsp[0].intval; }
+ break;
+
+ case 49:
+#line 394 "getdate.y"
+ { PC.rel_seconds += yyvsp[-1].textintval.value * yyvsp[0].intval; }
+ break;
+
+ case 50:
+#line 396 "getdate.y"
+ { PC.rel_seconds += yyvsp[-1].textintval.value * yyvsp[0].intval; }
+ break;
+
+ case 51:
+#line 398 "getdate.y"
+ { PC.rel_seconds += yyvsp[0].intval; }
+ break;
+
+ case 52:
+#line 403 "getdate.y"
+ {
+ if (PC.dates_seen
+ && ! PC.rels_seen && (PC.times_seen || 2 < yyvsp[0].textintval.digits))
+ PC.year = yyvsp[0].textintval;
+ else
+ {
+ if (4 < yyvsp[0].textintval.digits)
+ {
+ PC.dates_seen++;
+ PC.day = yyvsp[0].textintval.value % 100;
+ PC.month = (yyvsp[0].textintval.value / 100) % 100;
+ PC.year.value = yyvsp[0].textintval.value / 10000;
+ PC.year.digits = yyvsp[0].textintval.digits - 4;
+ }
+ else
+ {
+ PC.times_seen++;
+ if (yyvsp[0].textintval.digits <= 2)
+ {
+ PC.hour = yyvsp[0].textintval.value;
+ PC.minutes = 0;
+ }
+ else
+ {
+ PC.hour = yyvsp[0].textintval.value / 100;
+ PC.minutes = yyvsp[0].textintval.value % 100;
+ }
+ PC.seconds = 0;
+ PC.meridian = MER24;
+ }
+ }
+ }
+ break;
+
+ case 53:
+#line 439 "getdate.y"
+ { yyval.intval = MER24; }
+ break;
+
+ case 54:
+#line 441 "getdate.y"
+ { yyval.intval = yyvsp[0].intval; }
+ break;
+
+
+ }
+
+/* Line 999 of yacc.c. */
+#line 1593 "getdate.c"
+
+ yyvsp -= yylen;
+ yyssp -= yylen;
+
+
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if YYERROR_VERBOSE
+ yyn = yypact[yystate];
+
+ if (YYPACT_NINF < yyn && yyn < YYLAST)
+ {
+ YYSIZE_T yysize = 0;
+ int yytype = YYTRANSLATE (yychar);
+ char *yymsg;
+ int yyx, yycount;
+
+ yycount = 0;
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ for (yyx = yyn < 0 ? -yyn : 0;
+ yyx < (int) (sizeof (yytname) / sizeof (char *)); yyx++)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ yysize += yystrlen (yytname[yyx]) + 15, yycount++;
+ yysize += yystrlen ("syntax error, unexpected ") + 1;
+ yysize += yystrlen (yytname[yytype]);
+ yymsg = (char *) YYSTACK_ALLOC (yysize);
+ if (yymsg != 0)
+ {
+ char *yyp = yystpcpy (yymsg, "syntax error, unexpected ");
+ yyp = yystpcpy (yyp, yytname[yytype]);
+
+ if (yycount < 5)
+ {
+ yycount = 0;
+ for (yyx = yyn < 0 ? -yyn : 0;
+ yyx < (int) (sizeof (yytname) / sizeof (char *));
+ yyx++)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ const char *yyq = ! yycount ? ", expecting " : " or ";
+ yyp = yystpcpy (yyp, yyq);
+ yyp = yystpcpy (yyp, yytname[yyx]);
+ yycount++;
+ }
+ }
+ yyerror (yymsg);
+ YYSTACK_FREE (yymsg);
+ }
+ else
+ yyerror ("syntax error; also virtual memory exhausted");
+ }
+ else
+#endif /* YYERROR_VERBOSE */
+ yyerror ("syntax error");
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse lookahead token after an
+ error, discard it. */
+
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ {
+ /* Pop the error token. */
+ YYPOPSTACK;
+ /* Pop the rest of the stack. */
+ while (yyss < yyssp)
+ {
+ YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
+ yydestruct (yystos[*yyssp], yyvsp);
+ YYPOPSTACK;
+ }
+ YYABORT;
+ }
+
+ YYDSYMPRINTF ("Error: discarding", yytoken, &yylval, &yylloc);
+ yydestruct (yytoken, &yylval);
+ yychar = YYEMPTY;
+
+ }
+
+ /* Else will try to reuse lookahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*----------------------------------------------------.
+| yyerrlab1 -- error raised explicitly by an action. |
+`----------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+ YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
+ yydestruct (yystos[yystate], yyvsp);
+ yyvsp--;
+ yystate = *--yyssp;
+
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ YYDPRINTF ((stderr, "Shifting error token, "));
+
+ *++yyvsp = yylval;
+
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#ifndef yyoverflow
+/*----------------------------------------------.
+| yyoverflowlab -- parser overflow comes here. |
+`----------------------------------------------*/
+yyoverflowlab:
+ yyerror ("parser stack overflow");
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+ return yyresult;
+}
+
+
+#line 444 "getdate.y"
+
+
+/* Include this file down here because bison inserts code above which
+ may define-away `const'. We want the prototype for get_date to have
+ the same signature as the function definition. */
+#include "modules/getdate.h"
+
+#ifndef gmtime
+struct tm *gmtime ();
+#endif
+#ifndef localtime
+struct tm *localtime ();
+#endif
+#ifndef mktime
+time_t mktime ();
+#endif
+
+static table const meridian_table[] =
+{
+ { "AM", tMERIDIAN, MERam },
+ { "A.M.", tMERIDIAN, MERam },
+ { "PM", tMERIDIAN, MERpm },
+ { "P.M.", tMERIDIAN, MERpm },
+ { 0, 0, 0 }
+};
+
+static table const dst_table[] =
+{
+ { "DST", tDST, 0 }
+};
+
+static table const month_and_day_table[] =
+{
+ { "JANUARY", tMONTH, 1 },
+ { "FEBRUARY", tMONTH, 2 },
+ { "MARCH", tMONTH, 3 },
+ { "APRIL", tMONTH, 4 },
+ { "MAY", tMONTH, 5 },
+ { "JUNE", tMONTH, 6 },
+ { "JULY", tMONTH, 7 },
+ { "AUGUST", tMONTH, 8 },
+ { "SEPTEMBER",tMONTH, 9 },
+ { "SEPT", tMONTH, 9 },
+ { "OCTOBER", tMONTH, 10 },
+ { "NOVEMBER", tMONTH, 11 },
+ { "DECEMBER", tMONTH, 12 },
+ { "SUNDAY", tDAY, 0 },
+ { "MONDAY", tDAY, 1 },
+ { "TUESDAY", tDAY, 2 },
+ { "TUES", tDAY, 2 },
+ { "WEDNESDAY",tDAY, 3 },
+ { "WEDNES", tDAY, 3 },
+ { "THURSDAY", tDAY, 4 },
+ { "THUR", tDAY, 4 },
+ { "THURS", tDAY, 4 },
+ { "FRIDAY", tDAY, 5 },
+ { "SATURDAY", tDAY, 6 },
+ { 0, 0, 0 }
+};
+
+static table const time_units_table[] =
+{
+ { "YEAR", tYEAR_UNIT, 1 },
+ { "MONTH", tMONTH_UNIT, 1 },
+ { "FORTNIGHT",tDAY_UNIT, 14 },
+ { "WEEK", tDAY_UNIT, 7 },
+ { "DAY", tDAY_UNIT, 1 },
+ { "HOUR", tHOUR_UNIT, 1 },
+ { "MINUTE", tMINUTE_UNIT, 1 },
+ { "MIN", tMINUTE_UNIT, 1 },
+ { "SECOND", tSEC_UNIT, 1 },
+ { "SEC", tSEC_UNIT, 1 },
+ { 0, 0, 0 }
+};
+
+/* Assorted relative-time words. */
+static table const relative_time_table[] =
+{
+ { "TOMORROW", tMINUTE_UNIT, 24 * 60 },
+ { "YESTERDAY",tMINUTE_UNIT, - (24 * 60) },
+ { "TODAY", tMINUTE_UNIT, 0 },
+ { "NOW", tMINUTE_UNIT, 0 },
+ { "LAST", tUNUMBER, -1 },
+ { "THIS", tUNUMBER, 0 },
+ { "NEXT", tUNUMBER, 1 },
+ { "FIRST", tUNUMBER, 1 },
+/*{ "SECOND", tUNUMBER, 2 }, */
+ { "THIRD", tUNUMBER, 3 },
+ { "FOURTH", tUNUMBER, 4 },
+ { "FIFTH", tUNUMBER, 5 },
+ { "SIXTH", tUNUMBER, 6 },
+ { "SEVENTH", tUNUMBER, 7 },
+ { "EIGHTH", tUNUMBER, 8 },
+ { "NINTH", tUNUMBER, 9 },
+ { "TENTH", tUNUMBER, 10 },
+ { "ELEVENTH", tUNUMBER, 11 },
+ { "TWELFTH", tUNUMBER, 12 },
+ { "AGO", tAGO, 1 },
+ { 0, 0, 0 }
+};
+
+/* The time zone table. This table is necessarily incomplete, as time
+ zone abbreviations are ambiguous; e.g. Australians interpret "EST"
+ as Eastern time in Australia, not as US Eastern Standard Time.
+ You cannot rely on getdate to handle arbitrary time zone
+ abbreviations; use numeric abbreviations like `-0500' instead. */
+static table const time_zone_table[] =
+{
+ { "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */
+ { "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
+ { "UTC", tZONE, HOUR ( 0) },
+ { "WET", tZONE, HOUR ( 0) }, /* Western European */
+ { "WEST", tDAYZONE, HOUR ( 0) }, /* Western European Summer */
+ { "BST", tDAYZONE, HOUR ( 0) }, /* British Summer */
+ { "ART", tZONE, -HOUR ( 3) }, /* Argentina */
+ { "BRT", tZONE, -HOUR ( 3) }, /* Brazil */
+ { "BRST", tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
+ { "NST", tZONE, -(HOUR ( 3) + 30) }, /* Newfoundland Standard */
+ { "NDT", tDAYZONE,-(HOUR ( 3) + 30) }, /* Newfoundland Daylight */
+ { "AST", tZONE, -HOUR ( 4) }, /* Atlantic Standard */
+ { "ADT", tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
+ { "CLT", tZONE, -HOUR ( 4) }, /* Chile */
+ { "CLST", tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
+ { "EST", tZONE, -HOUR ( 5) }, /* Eastern Standard */
+ { "EDT", tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
+ { "CST", tZONE, -HOUR ( 6) }, /* Central Standard */
+ { "CDT", tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
+ { "MST", tZONE, -HOUR ( 7) }, /* Mountain Standard */
+ { "MDT", tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
+ { "PST", tZONE, -HOUR ( 8) }, /* Pacific Standard */
+ { "PDT", tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
+ { "AKST", tZONE, -HOUR ( 9) }, /* Alaska Standard */
+ { "AKDT", tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
+ { "HST", tZONE, -HOUR (10) }, /* Hawaii Standard */
+ { "HAST", tZONE, -HOUR (10) }, /* Hawaii-Aleutian Standard */
+ { "HADT", tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
+ { "SST", tZONE, -HOUR (12) }, /* Samoa Standard */
+ { "WAT", tZONE, HOUR ( 1) }, /* West Africa */
+ { "CET", tZONE, HOUR ( 1) }, /* Central European */
+ { "CEST", tDAYZONE, HOUR ( 1) }, /* Central European Summer */
+ { "MET", tZONE, HOUR ( 1) }, /* Middle European */
+ { "MEZ", tZONE, HOUR ( 1) }, /* Middle European */
+ { "MEST", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
+ { "MESZ", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
+ { "EET", tZONE, HOUR ( 2) }, /* Eastern European */
+ { "EEST", tDAYZONE, HOUR ( 2) }, /* Eastern European Summer */
+ { "CAT", tZONE, HOUR ( 2) }, /* Central Africa */
+ { "SAST", tZONE, HOUR ( 2) }, /* South Africa Standard */
+ { "EAT", tZONE, HOUR ( 3) }, /* East Africa */
+ { "MSK", tZONE, HOUR ( 3) }, /* Moscow */
+ { "MSD", tDAYZONE, HOUR ( 3) }, /* Moscow Daylight */
+ { "IST", tZONE, (HOUR ( 5) + 30) }, /* India Standard */
+ { "SGT", tZONE, HOUR ( 8) }, /* Singapore */
+ { "KST", tZONE, HOUR ( 9) }, /* Korea Standard */
+ { "JST", tZONE, HOUR ( 9) }, /* Japan Standard */
+ { "GST", tZONE, HOUR (10) }, /* Guam Standard */
+ { "NZST", tZONE, HOUR (12) }, /* New Zealand Standard */
+ { "NZDT", tDAYZONE, HOUR (12) }, /* New Zealand Daylight */
+ { 0, 0, 0 }
+};
+
+/* Military time zone table. */
+static table const military_table[] =
+{
+ { "A", tZONE, -HOUR ( 1) },
+ { "B", tZONE, -HOUR ( 2) },
+ { "C", tZONE, -HOUR ( 3) },
+ { "D", tZONE, -HOUR ( 4) },
+ { "E", tZONE, -HOUR ( 5) },
+ { "F", tZONE, -HOUR ( 6) },
+ { "G", tZONE, -HOUR ( 7) },
+ { "H", tZONE, -HOUR ( 8) },
+ { "I", tZONE, -HOUR ( 9) },
+ { "K", tZONE, -HOUR (10) },
+ { "L", tZONE, -HOUR (11) },
+ { "M", tZONE, -HOUR (12) },
+ { "N", tZONE, HOUR ( 1) },
+ { "O", tZONE, HOUR ( 2) },
+ { "P", tZONE, HOUR ( 3) },
+ { "Q", tZONE, HOUR ( 4) },
+ { "R", tZONE, HOUR ( 5) },
+ { "S", tZONE, HOUR ( 6) },
+ { "T", tZONE, HOUR ( 7) },
+ { "U", tZONE, HOUR ( 8) },
+ { "V", tZONE, HOUR ( 9) },
+ { "W", tZONE, HOUR (10) },
+ { "X", tZONE, HOUR (11) },
+ { "Y", tZONE, HOUR (12) },
+ { "Z", tZONE, HOUR ( 0) },
+ { 0, 0, 0 }
+};
+
+
+
+static int
+to_hour (int hours, int meridian)
+{
+ switch (meridian)
+ {
+ case MER24:
+ return 0 <= hours && hours < 24 ? hours : -1;
+ case MERam:
+ return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
+ case MERpm:
+ return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
+ default:
+ abort ();
+ }
+ /* NOTREACHED */
+ return 0;
+}
+
+static int
+to_year (textint textyear)
+{
+ int year = textyear.value;
+
+ if (year < 0)
+ year = -year;
+
+ /* XPG4 suggests that years 00-68 map to 2000-2068, and
+ years 69-99 map to 1969-1999. */
+ if (textyear.digits == 2)
+ year += year < 69 ? 2000 : 1900;
+
+ return year;
+}
+
+static table const *
+lookup_zone (parser_control const *pc, char const *name)
+{
+ table const *tp;
+
+ /* Try local zone abbreviations first; they're more likely to be right. */
+ for (tp = pc->local_time_zone_table; tp->name; tp++)
+ if (strcmp (name, tp->name) == 0)
+ return tp;
+
+ for (tp = time_zone_table; tp->name; tp++)
+ if (strcmp (name, tp->name) == 0)
+ return tp;
+
+ return 0;
+}
+
+#if ! HAVE_TM_GMTOFF
+/* Yield the difference between *A and *B,
+ measured in seconds, ignoring leap seconds.
+ The body of this function is taken directly from the GNU C Library;
+ see src/strftime.c. */
+static int
+tm_diff (struct tm const *a, struct tm const *b)
+{
+ /* Compute intervening leap days correctly even if year is negative.
+ Take care to avoid int overflow in leap day calculations,
+ but it's OK to assume that A and B are close to each other. */
+ int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3);
+ int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3);
+ int a100 = a4 / 25 - (a4 % 25 < 0);
+ int b100 = b4 / 25 - (b4 % 25 < 0);
+ int a400 = a100 >> 2;
+ int b400 = b100 >> 2;
+ int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
+ int years = a->tm_year - b->tm_year;
+ int days = (365 * years + intervening_leap_days
+ + (a->tm_yday - b->tm_yday));
+ return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
+ + (a->tm_min - b->tm_min))
+ + (a->tm_sec - b->tm_sec));
+}
+#endif /* ! HAVE_TM_GMTOFF */
+
+static table const *
+lookup_word (parser_control const *pc, char *word)
+{
+ char *p;
+ char *q;
+ size_t wordlen;
+ table const *tp;
+ int i;
+ int abbrev;
+
+ /* Make it uppercase. */
+ for (p = word; *p; p++)
+ if (ISLOWER ((unsigned char) *p))
+ *p = toupper ((unsigned char) *p);
+
+ for (tp = meridian_table; tp->name; tp++)
+ if (strcmp (word, tp->name) == 0)
+ return tp;
+
+ /* See if we have an abbreviation for a month. */
+ wordlen = strlen (word);
+ abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
+
+ for (tp = month_and_day_table; tp->name; tp++)
+ if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
+ return tp;
+
+ if ((tp = lookup_zone (pc, word)))
+ return tp;
+
+ if (strcmp (word, dst_table[0].name) == 0)
+ return dst_table;
+
+ for (tp = time_units_table; tp->name; tp++)
+ if (strcmp (word, tp->name) == 0)
+ return tp;
+
+ /* Strip off any plural and try the units table again. */
+ if (word[wordlen - 1] == 'S')
+ {
+ word[wordlen - 1] = '\0';
+ for (tp = time_units_table; tp->name; tp++)
+ if (strcmp (word, tp->name) == 0)
+ return tp;
+ word[wordlen - 1] = 'S'; /* For "this" in relative_time_table. */
+ }
+
+ for (tp = relative_time_table; tp->name; tp++)
+ if (strcmp (word, tp->name) == 0)
+ return tp;
+
+ /* Military time zones. */
+ if (wordlen == 1)
+ for (tp = military_table; tp->name; tp++)
+ if (word[0] == tp->name[0])
+ return tp;
+
+ /* Drop out any periods and try the time zone table again. */
+ for (i = 0, p = q = word; (*p = *q); q++)
+ if (*q == '.')
+ i = 1;
+ else
+ p++;
+ if (i && (tp = lookup_zone (pc, word)))
+ return tp;
+
+ return 0;
+}
+
+static int
+yylex (YYSTYPE *lvalp, parser_control *pc)
+{
+ unsigned char c;
+ int count;
+
+ for (;;)
+ {
+ while (c = *pc->input, ISSPACE (c))
+ pc->input++;
+
+ if (ISDIGIT (c) || c == '-' || c == '+')
+ {
+ char const *p;
+ int sign;
+ int value;
+ if (c == '-' || c == '+')
+ {
+ sign = c == '-' ? -1 : 1;
+ c = *++pc->input;
+ if (! ISDIGIT (c))
+ /* skip the '-' sign */
+ continue;
+ }
+ else
+ sign = 0;
+ p = pc->input;
+ value = 0;
+ do
+ {
+ value = 10 * value + c - '0';
+ c = *++p;
+ }
+ while (ISDIGIT (c));
+ lvalp->textintval.value = sign < 0 ? -value : value;
+ lvalp->textintval.digits = p - pc->input;
+ pc->input = p;
+ return sign ? tSNUMBER : tUNUMBER;
+ }
+
+ if (ISALPHA (c))
+ {
+ char buff[20];
+ char *p = buff;
+ table const *tp;
+
+ do
+ {
+ if (p < buff + sizeof buff - 1)
+ *p++ = c;
+ c = *++pc->input;
+ }
+ while (ISALPHA (c) || c == '.');
+
+ *p = '\0';
+ tp = lookup_word (pc, buff);
+ if (! tp)
+ return '?';
+ lvalp->intval = tp->value;
+ return tp->type;
+ }
+
+ if (c != '(')
+ return *pc->input++;
+ count = 0;
+ do
+ {
+ c = *pc->input++;
+ if (c == '\0')
+ return c;
+ if (c == '(')
+ count++;
+ else if (c == ')')
+ count--;
+ }
+ while (count > 0);
+ }
+}
+
+/* Do nothing if the parser reports an error. */
+static int
+yyerror (char *s ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* Parse a date/time string P. Return the corresponding time_t value,
+ or (time_t) -1 if there is an error. P can be an incomplete or
+ relative time specification; if so, use *NOW as the basis for the
+ returned time. */
+time_t
+get_date (const char *p, const time_t *now)
+{
+ time_t Start = now ? *now : time (0);
+ struct tm *tmp = localtime (&Start);
+ struct tm tm;
+ struct tm tm0;
+ parser_control pc;
+
+ if (! tmp)
+ return -1;
+
+ pc.input = p;
+ pc.year.value = tmp->tm_year + TM_YEAR_BASE;
+ pc.year.digits = 4;
+ pc.month = tmp->tm_mon + 1;
+ pc.day = tmp->tm_mday;
+ pc.hour = tmp->tm_hour;
+ pc.minutes = tmp->tm_min;
+ pc.seconds = tmp->tm_sec;
+ tm.tm_isdst = tmp->tm_isdst;
+
+ pc.meridian = MER24;
+ pc.rel_seconds = 0;
+ pc.rel_minutes = 0;
+ pc.rel_hour = 0;
+ pc.rel_day = 0;
+ pc.rel_month = 0;
+ pc.rel_year = 0;
+ pc.dates_seen = 0;
+ pc.days_seen = 0;
+ pc.rels_seen = 0;
+ pc.times_seen = 0;
+ pc.local_zones_seen = 0;
+ pc.zones_seen = 0;
+
+#if HAVE_STRUCT_TM_TM_ZONE
+ pc.local_time_zone_table[0].name = tmp->tm_zone;
+ pc.local_time_zone_table[0].type = tLOCAL_ZONE;
+ pc.local_time_zone_table[0].value = tmp->tm_isdst;
+ pc.local_time_zone_table[1].name = 0;
+
+ /* Probe the names used in the next three calendar quarters, looking
+ for a tm_isdst different from the one we already have. */
+ {
+ int quarter;
+ for (quarter = 1; quarter <= 3; quarter++)
+ {
+ time_t probe = Start + quarter * (90 * 24 * 60 * 60);
+ struct tm *probe_tm = localtime (&probe);
+ if (probe_tm && probe_tm->tm_zone
+ && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
+ {
+ {
+ pc.local_time_zone_table[1].name = probe_tm->tm_zone;
+ pc.local_time_zone_table[1].type = tLOCAL_ZONE;
+ pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
+ pc.local_time_zone_table[2].name = 0;
+ }
+ break;
+ }
+ }
+ }
+#else
+#if HAVE_TZNAME
+ {
+# ifndef tzname
+ extern char *tzname[];
+# endif
+ int i;
+ for (i = 0; i < 2; i++)
+ {
+ pc.local_time_zone_table[i].name = tzname[i];
+ pc.local_time_zone_table[i].type = tLOCAL_ZONE;
+ pc.local_time_zone_table[i].value = i;
+ }
+ pc.local_time_zone_table[i].name = 0;
+ }
+#else
+ pc.local_time_zone_table[0].name = 0;
+#endif
+#endif
+
+ if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
+ && ! strcmp (pc.local_time_zone_table[0].name,
+ pc.local_time_zone_table[1].name))
+ {
+ /* This locale uses the same abbrevation for standard and
+ daylight times. So if we see that abbreviation, we don't
+ know whether it's daylight time. */
+ pc.local_time_zone_table[0].value = -1;
+ pc.local_time_zone_table[1].name = 0;
+ }
+
+ if (yyparse (&pc) != 0
+ || 1 < pc.times_seen || 1 < pc.dates_seen || 1 < pc.days_seen
+ || 1 < (pc.local_zones_seen + pc.zones_seen)
+ || (pc.local_zones_seen && 1 < pc.local_isdst))
+ return -1;
+
+ tm.tm_year = to_year (pc.year) - TM_YEAR_BASE + pc.rel_year;
+ tm.tm_mon = pc.month - 1 + pc.rel_month;
+ tm.tm_mday = pc.day + pc.rel_day;
+ if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
+ {
+ tm.tm_hour = to_hour (pc.hour, pc.meridian);
+ if (tm.tm_hour < 0)
+ return -1;
+ tm.tm_min = pc.minutes;
+ tm.tm_sec = pc.seconds;
+ }
+ else
+ {
+ tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+ }
+
+ /* Let mktime deduce tm_isdst if we have an absolute time stamp,
+ or if the relative time stamp mentions days, months, or years. */
+ if (pc.dates_seen | pc.days_seen | pc.times_seen | pc.rel_day
+ | pc.rel_month | pc.rel_year)
+ tm.tm_isdst = -1;
+
+ /* But if the input explicitly specifies local time with or without
+ DST, give mktime that information. */
+ if (pc.local_zones_seen)
+ tm.tm_isdst = pc.local_isdst;
+
+ tm0 = tm;
+
+ Start = mktime (&tm);
+
+ if (Start == (time_t) -1)
+ {
+
+ /* Guard against falsely reporting errors near the time_t boundaries
+ when parsing times in other time zones. For example, if the min
+ time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
+ of UTC, then the min localtime value is 1970-01-01 08:00:00; if
+ we apply mktime to 1970-01-01 00:00:00 we will get an error, so
+ we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
+ zone by 24 hours to compensate. This algorithm assumes that
+ there is no DST transition within a day of the time_t boundaries. */
+ if (pc.zones_seen)
+ {
+ tm = tm0;
+ if (tm.tm_year <= EPOCH_YEAR - TM_YEAR_BASE)
+ {
+ tm.tm_mday++;
+ pc.time_zone += 24 * 60;
+ }
+ else
+ {
+ tm.tm_mday--;
+ pc.time_zone -= 24 * 60;
+ }
+ Start = mktime (&tm);
+ }
+
+ if (Start == (time_t) -1)
+ return Start;
+ }
+
+ if (pc.days_seen && ! pc.dates_seen)
+ {
+ tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
+ + 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
+ tm.tm_isdst = -1;
+ Start = mktime (&tm);
+ if (Start == (time_t) -1)
+ return Start;
+ }
+
+ if (pc.zones_seen)
+ {
+ int delta = pc.time_zone * 60;
+#ifdef HAVE_TM_GMTOFF
+ delta -= tm.tm_gmtoff;
+#else
+ struct tm *gmt = gmtime (&Start);
+ if (! gmt)
+ return -1;
+ delta -= tm_diff (&tm, gmt);
+#endif
+ if ((Start < Start - delta) != (delta < 0))
+ return -1; /* time_t overflow */
+ Start -= delta;
+ }
+
+ /* Add relative hours, minutes, and seconds. Ignore leap seconds;
+ i.e. "+ 10 minutes" means 600 seconds, even if one of them is a
+ leap second. Typically this is not what the user wants, but it's
+ too hard to do it the other way, because the time zone indicator
+ must be applied before relative times, and if mktime is applied
+ again the time zone will be lost. */
+ {
+ time_t t0 = Start;
+ long d1 = 60 * 60 * (long) pc.rel_hour;
+ time_t t1 = t0 + d1;
+ long d2 = 60 * (long) pc.rel_minutes;
+ time_t t2 = t1 + d2;
+ int d3 = pc.rel_seconds;
+ time_t t3 = t2 + d3;
+ if ((d1 / (60 * 60) ^ pc.rel_hour)
+ | (d2 / 60 ^ pc.rel_minutes)
+ | ((t0 + d1 < t0) ^ (d1 < 0))
+ | ((t1 + d2 < t1) ^ (d2 < 0))
+ | ((t2 + d3 < t2) ^ (d3 < 0)))
+ return -1;
+ Start = t3;
+ }
+
+ return Start;
+}
+
+#if TEST
+
+#include <stdio.h>
+
+int
+main (int ac, char **av)
+{
+ char buff[BUFSIZ];
+ time_t d;
+
+ printf ("Enter date, or blank line to exit.\n\t> ");
+ fflush (stdout);
+
+ buff[BUFSIZ - 1] = 0;
+ while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
+ {
+ d = get_date (buff, 0);
+ if (d == (time_t) -1)
+ printf ("Bad format - couldn't convert.\n");
+ else
+ printf ("%s", ctime (&d));
+ printf ("\t> ");
+ fflush (stdout);
+ }
+ return 0;
+}
+#endif /* defined TEST */
+
+
diff --git a/source/modules/getdate.h b/source/modules/getdate.h
new file mode 100644
index 00000000000..674c474f115
--- /dev/null
+++ b/source/modules/getdate.h
@@ -0,0 +1,46 @@
+/* Copyright (C) 1995, 1997, 1998 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifndef PARAMS
+# if defined PROTOTYPES || (defined __STDC__ && __STDC__)
+# define PARAMS(Args) Args
+# else
+# define PARAMS(Args) ()
+# endif
+#endif
+
+#ifdef vms
+# include <types.h>
+# include <time.h>
+#else
+# include <sys/types.h>
+# if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+# else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+# endif
+#endif /* defined (vms) */
+
+time_t get_date PARAMS ((const char *p, const time_t *now));
diff --git a/source/modules/getdate.y b/source/modules/getdate.y
new file mode 100644
index 00000000000..aab37f4d235
--- /dev/null
+++ b/source/modules/getdate.y
@@ -0,0 +1,1115 @@
+%{
+/* Parse a string into an internal time stamp.
+ Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* Originally written by Steven M. Bellovin <smb@research.att.com> while
+ at the University of North Carolina at Chapel Hill. Later tweaked by
+ a couple of people on Usenet. Completely overhauled by Rich $alz
+ <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990.
+
+ Modified by Paul Eggert <eggert@twinsun.com> in August 1999 to do
+ the right thing about local DST. Unlike previous versions, this
+ version is reentrant. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+# ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+# endif
+#endif
+
+/* Since the code of getdate.y is not included in the Emacs executable
+ itself, there is no need to #define static in this file. Even if
+ the code were included in the Emacs executable, it probably
+ wouldn't do any harm to #undef it here; this will only cause
+ problems if we try to write to a static variable, which I don't
+ think this code needs to do. */
+#ifdef emacs
+# undef static
+#endif
+
+#include <ctype.h>
+
+#if HAVE_STDLIB_H
+# include <stdlib.h> /* for `free'; used by Bison 1.27 */
+#endif
+
+#if STDC_HEADERS || (! defined isascii && ! HAVE_ISASCII)
+# define IN_CTYPE_DOMAIN(c) 1
+#else
+# define IN_CTYPE_DOMAIN(c) isascii (c)
+#endif
+
+#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
+#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
+#define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c))
+#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
+
+/* ISDIGIT differs from ISDIGIT_LOCALE, as follows:
+ - Its arg may be any int or unsigned int; it need not be an unsigned char.
+ - It's guaranteed to evaluate its argument exactly once.
+ - It's typically faster.
+ POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
+ ISDIGIT_LOCALE unless it's important to use the locale's definition
+ of `digit' even when the host does not conform to POSIX. */
+#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
+
+#if STDC_HEADERS || HAVE_STRING_H
+# include <string.h>
+#endif
+
+#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
+# define __attribute__(x)
+#endif
+
+#ifndef ATTRIBUTE_UNUSED
+# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
+#endif
+
+#define EPOCH_YEAR 1970
+#define TM_YEAR_BASE 1900
+
+#define HOUR(x) ((x) * 60)
+
+/* An integer value, and the number of digits in its textual
+ representation. */
+typedef struct
+{
+ int value;
+ int digits;
+} textint;
+
+/* An entry in the lexical lookup table. */
+typedef struct
+{
+ char const *name;
+ int type;
+ int value;
+} table;
+
+/* Meridian: am, pm, or 24-hour style. */
+enum { MERam, MERpm, MER24 };
+
+/* Information passed to and from the parser. */
+typedef struct
+{
+ /* The input string remaining to be parsed. */
+ const char *input;
+
+ /* N, if this is the Nth Tuesday. */
+ int day_ordinal;
+
+ /* Day of week; Sunday is 0. */
+ int day_number;
+
+ /* tm_isdst flag for the local zone. */
+ int local_isdst;
+
+ /* Time zone, in minutes east of UTC. */
+ int time_zone;
+
+ /* Style used for time. */
+ int meridian;
+
+ /* Gregorian year, month, day, hour, minutes, and seconds. */
+ textint year;
+ int month;
+ int day;
+ int hour;
+ int minutes;
+ int seconds;
+
+ /* Relative year, month, day, hour, minutes, and seconds. */
+ int rel_year;
+ int rel_month;
+ int rel_day;
+ int rel_hour;
+ int rel_minutes;
+ int rel_seconds;
+
+ /* Counts of nonterminals of various flavors parsed so far. */
+ int dates_seen;
+ int days_seen;
+ int local_zones_seen;
+ int rels_seen;
+ int times_seen;
+ int zones_seen;
+
+ /* Table of local time zone abbrevations, terminated by a null entry. */
+ table local_time_zone_table[3];
+} parser_control;
+
+#define PC (* (parser_control *) parm)
+#define YYLEX_PARAM parm
+#define YYPARSE_PARAM parm
+
+static int yyerror ();
+static int yylex ();
+
+%}
+
+/* We want a reentrant parser. */
+%pure_parser
+
+/* This grammar has 13 shift/reduce conflicts. */
+%expect 13
+
+%union
+{
+ int intval;
+ textint textintval;
+}
+
+%token tAGO tDST
+
+%token <intval> tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tLOCAL_ZONE tMERIDIAN
+%token <intval> tMINUTE_UNIT tMONTH tMONTH_UNIT tSEC_UNIT tYEAR_UNIT tZONE
+
+%token <textintval> tSNUMBER tUNUMBER
+
+%type <intval> o_merid
+
+%%
+
+spec:
+ /* empty */
+ | spec item
+ ;
+
+item:
+ time
+ { PC.times_seen++; }
+ | local_zone
+ { PC.local_zones_seen++; }
+ | zone
+ { PC.zones_seen++; }
+ | date
+ { PC.dates_seen++; }
+ | day
+ { PC.days_seen++; }
+ | rel
+ { PC.rels_seen++; }
+ | number
+ ;
+
+time:
+ tUNUMBER tMERIDIAN
+ {
+ PC.hour = $1.value;
+ PC.minutes = 0;
+ PC.seconds = 0;
+ PC.meridian = $2;
+ }
+ | tUNUMBER ':' tUNUMBER o_merid
+ {
+ PC.hour = $1.value;
+ PC.minutes = $3.value;
+ PC.seconds = 0;
+ PC.meridian = $4;
+ }
+ | tUNUMBER ':' tUNUMBER tSNUMBER
+ {
+ PC.hour = $1.value;
+ PC.minutes = $3.value;
+ PC.meridian = MER24;
+ PC.zones_seen++;
+ PC.time_zone = $4.value % 100 + ($4.value / 100) * 60;
+ }
+ | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid
+ {
+ PC.hour = $1.value;
+ PC.minutes = $3.value;
+ PC.seconds = $5.value;
+ PC.meridian = $6;
+ }
+ | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER
+ {
+ PC.hour = $1.value;
+ PC.minutes = $3.value;
+ PC.seconds = $5.value;
+ PC.meridian = MER24;
+ PC.zones_seen++;
+ PC.time_zone = $6.value % 100 + ($6.value / 100) * 60;
+ }
+ ;
+
+local_zone:
+ tLOCAL_ZONE
+ { PC.local_isdst = $1; }
+ | tLOCAL_ZONE tDST
+ { PC.local_isdst = $1 < 0 ? 1 : $1 + 1; }
+ ;
+
+zone:
+ tZONE
+ { PC.time_zone = $1; }
+ | tDAYZONE
+ { PC.time_zone = $1 + 60; }
+ | tZONE tDST
+ { PC.time_zone = $1 + 60; }
+ ;
+
+day:
+ tDAY
+ {
+ PC.day_ordinal = 1;
+ PC.day_number = $1;
+ }
+ | tDAY ','
+ {
+ PC.day_ordinal = 1;
+ PC.day_number = $1;
+ }
+ | tUNUMBER tDAY
+ {
+ PC.day_ordinal = $1.value;
+ PC.day_number = $2;
+ }
+ ;
+
+date:
+ tUNUMBER '/' tUNUMBER
+ {
+ PC.month = $1.value;
+ PC.day = $3.value;
+ }
+ | tUNUMBER '/' tUNUMBER '/' tUNUMBER
+ {
+ /* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
+ otherwise as MM/DD/YY.
+ The goal in recognizing YYYY/MM/DD is solely to support legacy
+ machine-generated dates like those in an RCS log listing. If
+ you want portability, use the ISO 8601 format. */
+ if (4 <= $1.digits)
+ {
+ PC.year = $1;
+ PC.month = $3.value;
+ PC.day = $5.value;
+ }
+ else
+ {
+ PC.month = $1.value;
+ PC.day = $3.value;
+ PC.year = $5;
+ }
+ }
+ | tUNUMBER tSNUMBER tSNUMBER
+ {
+ /* ISO 8601 format. YYYY-MM-DD. */
+ PC.year = $1;
+ PC.month = -$2.value;
+ PC.day = -$3.value;
+ }
+ | tUNUMBER tMONTH tSNUMBER
+ {
+ /* e.g. 17-JUN-1992. */
+ PC.day = $1.value;
+ PC.month = $2;
+ PC.year.value = -$3.value;
+ PC.year.digits = $3.digits;
+ }
+ | tMONTH tUNUMBER
+ {
+ PC.month = $1;
+ PC.day = $2.value;
+ }
+ | tMONTH tUNUMBER ',' tUNUMBER
+ {
+ PC.month = $1;
+ PC.day = $2.value;
+ PC.year = $4;
+ }
+ | tUNUMBER tMONTH
+ {
+ PC.day = $1.value;
+ PC.month = $2;
+ }
+ | tUNUMBER tMONTH tUNUMBER
+ {
+ PC.day = $1.value;
+ PC.month = $2;
+ PC.year = $3;
+ }
+ ;
+
+rel:
+ relunit tAGO
+ {
+ PC.rel_seconds = -PC.rel_seconds;
+ PC.rel_minutes = -PC.rel_minutes;
+ PC.rel_hour = -PC.rel_hour;
+ PC.rel_day = -PC.rel_day;
+ PC.rel_month = -PC.rel_month;
+ PC.rel_year = -PC.rel_year;
+ }
+ | relunit
+ ;
+
+relunit:
+ tUNUMBER tYEAR_UNIT
+ { PC.rel_year += $1.value * $2; }
+ | tSNUMBER tYEAR_UNIT
+ { PC.rel_year += $1.value * $2; }
+ | tYEAR_UNIT
+ { PC.rel_year += $1; }
+ | tUNUMBER tMONTH_UNIT
+ { PC.rel_month += $1.value * $2; }
+ | tSNUMBER tMONTH_UNIT
+ { PC.rel_month += $1.value * $2; }
+ | tMONTH_UNIT
+ { PC.rel_month += $1; }
+ | tUNUMBER tDAY_UNIT
+ { PC.rel_day += $1.value * $2; }
+ | tSNUMBER tDAY_UNIT
+ { PC.rel_day += $1.value * $2; }
+ | tDAY_UNIT
+ { PC.rel_day += $1; }
+ | tUNUMBER tHOUR_UNIT
+ { PC.rel_hour += $1.value * $2; }
+ | tSNUMBER tHOUR_UNIT
+ { PC.rel_hour += $1.value * $2; }
+ | tHOUR_UNIT
+ { PC.rel_hour += $1; }
+ | tUNUMBER tMINUTE_UNIT
+ { PC.rel_minutes += $1.value * $2; }
+ | tSNUMBER tMINUTE_UNIT
+ { PC.rel_minutes += $1.value * $2; }
+ | tMINUTE_UNIT
+ { PC.rel_minutes += $1; }
+ | tUNUMBER tSEC_UNIT
+ { PC.rel_seconds += $1.value * $2; }
+ | tSNUMBER tSEC_UNIT
+ { PC.rel_seconds += $1.value * $2; }
+ | tSEC_UNIT
+ { PC.rel_seconds += $1; }
+ ;
+
+number:
+ tUNUMBER
+ {
+ if (PC.dates_seen
+ && ! PC.rels_seen && (PC.times_seen || 2 < $1.digits))
+ PC.year = $1;
+ else
+ {
+ if (4 < $1.digits)
+ {
+ PC.dates_seen++;
+ PC.day = $1.value % 100;
+ PC.month = ($1.value / 100) % 100;
+ PC.year.value = $1.value / 10000;
+ PC.year.digits = $1.digits - 4;
+ }
+ else
+ {
+ PC.times_seen++;
+ if ($1.digits <= 2)
+ {
+ PC.hour = $1.value;
+ PC.minutes = 0;
+ }
+ else
+ {
+ PC.hour = $1.value / 100;
+ PC.minutes = $1.value % 100;
+ }
+ PC.seconds = 0;
+ PC.meridian = MER24;
+ }
+ }
+ }
+ ;
+
+o_merid:
+ /* empty */
+ { $$ = MER24; }
+ | tMERIDIAN
+ { $$ = $1; }
+ ;
+
+%%
+
+/* Include this file down here because bison inserts code above which
+ may define-away `const'. We want the prototype for get_date to have
+ the same signature as the function definition. */
+#include "modules/getdate.h"
+
+#ifndef gmtime
+struct tm *gmtime ();
+#endif
+#ifndef localtime
+struct tm *localtime ();
+#endif
+#ifndef mktime
+time_t mktime ();
+#endif
+
+static table const meridian_table[] =
+{
+ { "AM", tMERIDIAN, MERam },
+ { "A.M.", tMERIDIAN, MERam },
+ { "PM", tMERIDIAN, MERpm },
+ { "P.M.", tMERIDIAN, MERpm },
+ { 0, 0, 0 }
+};
+
+static table const dst_table[] =
+{
+ { "DST", tDST, 0 }
+};
+
+static table const month_and_day_table[] =
+{
+ { "JANUARY", tMONTH, 1 },
+ { "FEBRUARY", tMONTH, 2 },
+ { "MARCH", tMONTH, 3 },
+ { "APRIL", tMONTH, 4 },
+ { "MAY", tMONTH, 5 },
+ { "JUNE", tMONTH, 6 },
+ { "JULY", tMONTH, 7 },
+ { "AUGUST", tMONTH, 8 },
+ { "SEPTEMBER",tMONTH, 9 },
+ { "SEPT", tMONTH, 9 },
+ { "OCTOBER", tMONTH, 10 },
+ { "NOVEMBER", tMONTH, 11 },
+ { "DECEMBER", tMONTH, 12 },
+ { "SUNDAY", tDAY, 0 },
+ { "MONDAY", tDAY, 1 },
+ { "TUESDAY", tDAY, 2 },
+ { "TUES", tDAY, 2 },
+ { "WEDNESDAY",tDAY, 3 },
+ { "WEDNES", tDAY, 3 },
+ { "THURSDAY", tDAY, 4 },
+ { "THUR", tDAY, 4 },
+ { "THURS", tDAY, 4 },
+ { "FRIDAY", tDAY, 5 },
+ { "SATURDAY", tDAY, 6 },
+ { 0, 0, 0 }
+};
+
+static table const time_units_table[] =
+{
+ { "YEAR", tYEAR_UNIT, 1 },
+ { "MONTH", tMONTH_UNIT, 1 },
+ { "FORTNIGHT",tDAY_UNIT, 14 },
+ { "WEEK", tDAY_UNIT, 7 },
+ { "DAY", tDAY_UNIT, 1 },
+ { "HOUR", tHOUR_UNIT, 1 },
+ { "MINUTE", tMINUTE_UNIT, 1 },
+ { "MIN", tMINUTE_UNIT, 1 },
+ { "SECOND", tSEC_UNIT, 1 },
+ { "SEC", tSEC_UNIT, 1 },
+ { 0, 0, 0 }
+};
+
+/* Assorted relative-time words. */
+static table const relative_time_table[] =
+{
+ { "TOMORROW", tMINUTE_UNIT, 24 * 60 },
+ { "YESTERDAY",tMINUTE_UNIT, - (24 * 60) },
+ { "TODAY", tMINUTE_UNIT, 0 },
+ { "NOW", tMINUTE_UNIT, 0 },
+ { "LAST", tUNUMBER, -1 },
+ { "THIS", tUNUMBER, 0 },
+ { "NEXT", tUNUMBER, 1 },
+ { "FIRST", tUNUMBER, 1 },
+/*{ "SECOND", tUNUMBER, 2 }, */
+ { "THIRD", tUNUMBER, 3 },
+ { "FOURTH", tUNUMBER, 4 },
+ { "FIFTH", tUNUMBER, 5 },
+ { "SIXTH", tUNUMBER, 6 },
+ { "SEVENTH", tUNUMBER, 7 },
+ { "EIGHTH", tUNUMBER, 8 },
+ { "NINTH", tUNUMBER, 9 },
+ { "TENTH", tUNUMBER, 10 },
+ { "ELEVENTH", tUNUMBER, 11 },
+ { "TWELFTH", tUNUMBER, 12 },
+ { "AGO", tAGO, 1 },
+ { 0, 0, 0 }
+};
+
+/* The time zone table. This table is necessarily incomplete, as time
+ zone abbreviations are ambiguous; e.g. Australians interpret "EST"
+ as Eastern time in Australia, not as US Eastern Standard Time.
+ You cannot rely on getdate to handle arbitrary time zone
+ abbreviations; use numeric abbreviations like `-0500' instead. */
+static table const time_zone_table[] =
+{
+ { "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */
+ { "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
+ { "UTC", tZONE, HOUR ( 0) },
+ { "WET", tZONE, HOUR ( 0) }, /* Western European */
+ { "WEST", tDAYZONE, HOUR ( 0) }, /* Western European Summer */
+ { "BST", tDAYZONE, HOUR ( 0) }, /* British Summer */
+ { "ART", tZONE, -HOUR ( 3) }, /* Argentina */
+ { "BRT", tZONE, -HOUR ( 3) }, /* Brazil */
+ { "BRST", tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
+ { "NST", tZONE, -(HOUR ( 3) + 30) }, /* Newfoundland Standard */
+ { "NDT", tDAYZONE,-(HOUR ( 3) + 30) }, /* Newfoundland Daylight */
+ { "AST", tZONE, -HOUR ( 4) }, /* Atlantic Standard */
+ { "ADT", tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
+ { "CLT", tZONE, -HOUR ( 4) }, /* Chile */
+ { "CLST", tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
+ { "EST", tZONE, -HOUR ( 5) }, /* Eastern Standard */
+ { "EDT", tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
+ { "CST", tZONE, -HOUR ( 6) }, /* Central Standard */
+ { "CDT", tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
+ { "MST", tZONE, -HOUR ( 7) }, /* Mountain Standard */
+ { "MDT", tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
+ { "PST", tZONE, -HOUR ( 8) }, /* Pacific Standard */
+ { "PDT", tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
+ { "AKST", tZONE, -HOUR ( 9) }, /* Alaska Standard */
+ { "AKDT", tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
+ { "HST", tZONE, -HOUR (10) }, /* Hawaii Standard */
+ { "HAST", tZONE, -HOUR (10) }, /* Hawaii-Aleutian Standard */
+ { "HADT", tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
+ { "SST", tZONE, -HOUR (12) }, /* Samoa Standard */
+ { "WAT", tZONE, HOUR ( 1) }, /* West Africa */
+ { "CET", tZONE, HOUR ( 1) }, /* Central European */
+ { "CEST", tDAYZONE, HOUR ( 1) }, /* Central European Summer */
+ { "MET", tZONE, HOUR ( 1) }, /* Middle European */
+ { "MEZ", tZONE, HOUR ( 1) }, /* Middle European */
+ { "MEST", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
+ { "MESZ", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
+ { "EET", tZONE, HOUR ( 2) }, /* Eastern European */
+ { "EEST", tDAYZONE, HOUR ( 2) }, /* Eastern European Summer */
+ { "CAT", tZONE, HOUR ( 2) }, /* Central Africa */
+ { "SAST", tZONE, HOUR ( 2) }, /* South Africa Standard */
+ { "EAT", tZONE, HOUR ( 3) }, /* East Africa */
+ { "MSK", tZONE, HOUR ( 3) }, /* Moscow */
+ { "MSD", tDAYZONE, HOUR ( 3) }, /* Moscow Daylight */
+ { "IST", tZONE, (HOUR ( 5) + 30) }, /* India Standard */
+ { "SGT", tZONE, HOUR ( 8) }, /* Singapore */
+ { "KST", tZONE, HOUR ( 9) }, /* Korea Standard */
+ { "JST", tZONE, HOUR ( 9) }, /* Japan Standard */
+ { "GST", tZONE, HOUR (10) }, /* Guam Standard */
+ { "NZST", tZONE, HOUR (12) }, /* New Zealand Standard */
+ { "NZDT", tDAYZONE, HOUR (12) }, /* New Zealand Daylight */
+ { 0, 0, 0 }
+};
+
+/* Military time zone table. */
+static table const military_table[] =
+{
+ { "A", tZONE, -HOUR ( 1) },
+ { "B", tZONE, -HOUR ( 2) },
+ { "C", tZONE, -HOUR ( 3) },
+ { "D", tZONE, -HOUR ( 4) },
+ { "E", tZONE, -HOUR ( 5) },
+ { "F", tZONE, -HOUR ( 6) },
+ { "G", tZONE, -HOUR ( 7) },
+ { "H", tZONE, -HOUR ( 8) },
+ { "I", tZONE, -HOUR ( 9) },
+ { "K", tZONE, -HOUR (10) },
+ { "L", tZONE, -HOUR (11) },
+ { "M", tZONE, -HOUR (12) },
+ { "N", tZONE, HOUR ( 1) },
+ { "O", tZONE, HOUR ( 2) },
+ { "P", tZONE, HOUR ( 3) },
+ { "Q", tZONE, HOUR ( 4) },
+ { "R", tZONE, HOUR ( 5) },
+ { "S", tZONE, HOUR ( 6) },
+ { "T", tZONE, HOUR ( 7) },
+ { "U", tZONE, HOUR ( 8) },
+ { "V", tZONE, HOUR ( 9) },
+ { "W", tZONE, HOUR (10) },
+ { "X", tZONE, HOUR (11) },
+ { "Y", tZONE, HOUR (12) },
+ { "Z", tZONE, HOUR ( 0) },
+ { 0, 0, 0 }
+};
+
+
+
+static int
+to_hour (int hours, int meridian)
+{
+ switch (meridian)
+ {
+ case MER24:
+ return 0 <= hours && hours < 24 ? hours : -1;
+ case MERam:
+ return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
+ case MERpm:
+ return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
+ default:
+ abort ();
+ }
+ /* NOTREACHED */
+ return 0;
+}
+
+static int
+to_year (textint textyear)
+{
+ int year = textyear.value;
+
+ if (year < 0)
+ year = -year;
+
+ /* XPG4 suggests that years 00-68 map to 2000-2068, and
+ years 69-99 map to 1969-1999. */
+ if (textyear.digits == 2)
+ year += year < 69 ? 2000 : 1900;
+
+ return year;
+}
+
+static table const *
+lookup_zone (parser_control const *pc, char const *name)
+{
+ table const *tp;
+
+ /* Try local zone abbreviations first; they're more likely to be right. */
+ for (tp = pc->local_time_zone_table; tp->name; tp++)
+ if (strcmp (name, tp->name) == 0)
+ return tp;
+
+ for (tp = time_zone_table; tp->name; tp++)
+ if (strcmp (name, tp->name) == 0)
+ return tp;
+
+ return 0;
+}
+
+#if ! HAVE_TM_GMTOFF
+/* Yield the difference between *A and *B,
+ measured in seconds, ignoring leap seconds.
+ The body of this function is taken directly from the GNU C Library;
+ see src/strftime.c. */
+static int
+tm_diff (struct tm const *a, struct tm const *b)
+{
+ /* Compute intervening leap days correctly even if year is negative.
+ Take care to avoid int overflow in leap day calculations,
+ but it's OK to assume that A and B are close to each other. */
+ int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3);
+ int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3);
+ int a100 = a4 / 25 - (a4 % 25 < 0);
+ int b100 = b4 / 25 - (b4 % 25 < 0);
+ int a400 = a100 >> 2;
+ int b400 = b100 >> 2;
+ int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
+ int years = a->tm_year - b->tm_year;
+ int days = (365 * years + intervening_leap_days
+ + (a->tm_yday - b->tm_yday));
+ return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
+ + (a->tm_min - b->tm_min))
+ + (a->tm_sec - b->tm_sec));
+}
+#endif /* ! HAVE_TM_GMTOFF */
+
+static table const *
+lookup_word (parser_control const *pc, char *word)
+{
+ char *p;
+ char *q;
+ size_t wordlen;
+ table const *tp;
+ int i;
+ int abbrev;
+
+ /* Make it uppercase. */
+ for (p = word; *p; p++)
+ if (ISLOWER ((unsigned char) *p))
+ *p = toupper ((unsigned char) *p);
+
+ for (tp = meridian_table; tp->name; tp++)
+ if (strcmp (word, tp->name) == 0)
+ return tp;
+
+ /* See if we have an abbreviation for a month. */
+ wordlen = strlen (word);
+ abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
+
+ for (tp = month_and_day_table; tp->name; tp++)
+ if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
+ return tp;
+
+ if ((tp = lookup_zone (pc, word)))
+ return tp;
+
+ if (strcmp (word, dst_table[0].name) == 0)
+ return dst_table;
+
+ for (tp = time_units_table; tp->name; tp++)
+ if (strcmp (word, tp->name) == 0)
+ return tp;
+
+ /* Strip off any plural and try the units table again. */
+ if (word[wordlen - 1] == 'S')
+ {
+ word[wordlen - 1] = '\0';
+ for (tp = time_units_table; tp->name; tp++)
+ if (strcmp (word, tp->name) == 0)
+ return tp;
+ word[wordlen - 1] = 'S'; /* For "this" in relative_time_table. */
+ }
+
+ for (tp = relative_time_table; tp->name; tp++)
+ if (strcmp (word, tp->name) == 0)
+ return tp;
+
+ /* Military time zones. */
+ if (wordlen == 1)
+ for (tp = military_table; tp->name; tp++)
+ if (word[0] == tp->name[0])
+ return tp;
+
+ /* Drop out any periods and try the time zone table again. */
+ for (i = 0, p = q = word; (*p = *q); q++)
+ if (*q == '.')
+ i = 1;
+ else
+ p++;
+ if (i && (tp = lookup_zone (pc, word)))
+ return tp;
+
+ return 0;
+}
+
+static int
+yylex (YYSTYPE *lvalp, parser_control *pc)
+{
+ unsigned char c;
+ int count;
+
+ for (;;)
+ {
+ while (c = *pc->input, ISSPACE (c))
+ pc->input++;
+
+ if (ISDIGIT (c) || c == '-' || c == '+')
+ {
+ char const *p;
+ int sign;
+ int value;
+ if (c == '-' || c == '+')
+ {
+ sign = c == '-' ? -1 : 1;
+ c = *++pc->input;
+ if (! ISDIGIT (c))
+ /* skip the '-' sign */
+ continue;
+ }
+ else
+ sign = 0;
+ p = pc->input;
+ value = 0;
+ do
+ {
+ value = 10 * value + c - '0';
+ c = *++p;
+ }
+ while (ISDIGIT (c));
+ lvalp->textintval.value = sign < 0 ? -value : value;
+ lvalp->textintval.digits = p - pc->input;
+ pc->input = p;
+ return sign ? tSNUMBER : tUNUMBER;
+ }
+
+ if (ISALPHA (c))
+ {
+ char buff[20];
+ char *p = buff;
+ table const *tp;
+
+ do
+ {
+ if (p < buff + sizeof buff - 1)
+ *p++ = c;
+ c = *++pc->input;
+ }
+ while (ISALPHA (c) || c == '.');
+
+ *p = '\0';
+ tp = lookup_word (pc, buff);
+ if (! tp)
+ return '?';
+ lvalp->intval = tp->value;
+ return tp->type;
+ }
+
+ if (c != '(')
+ return *pc->input++;
+ count = 0;
+ do
+ {
+ c = *pc->input++;
+ if (c == '\0')
+ return c;
+ if (c == '(')
+ count++;
+ else if (c == ')')
+ count--;
+ }
+ while (count > 0);
+ }
+}
+
+/* Do nothing if the parser reports an error. */
+static int
+yyerror (char *s ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* Parse a date/time string P. Return the corresponding time_t value,
+ or (time_t) -1 if there is an error. P can be an incomplete or
+ relative time specification; if so, use *NOW as the basis for the
+ returned time. */
+time_t
+get_date (const char *p, const time_t *now)
+{
+ time_t Start = now ? *now : time (0);
+ struct tm *tmp = localtime (&Start);
+ struct tm tm;
+ struct tm tm0;
+ parser_control pc;
+
+ if (! tmp)
+ return -1;
+
+ pc.input = p;
+ pc.year.value = tmp->tm_year + TM_YEAR_BASE;
+ pc.year.digits = 4;
+ pc.month = tmp->tm_mon + 1;
+ pc.day = tmp->tm_mday;
+ pc.hour = tmp->tm_hour;
+ pc.minutes = tmp->tm_min;
+ pc.seconds = tmp->tm_sec;
+ tm.tm_isdst = tmp->tm_isdst;
+
+ pc.meridian = MER24;
+ pc.rel_seconds = 0;
+ pc.rel_minutes = 0;
+ pc.rel_hour = 0;
+ pc.rel_day = 0;
+ pc.rel_month = 0;
+ pc.rel_year = 0;
+ pc.dates_seen = 0;
+ pc.days_seen = 0;
+ pc.rels_seen = 0;
+ pc.times_seen = 0;
+ pc.local_zones_seen = 0;
+ pc.zones_seen = 0;
+
+#if HAVE_STRUCT_TM_TM_ZONE
+ pc.local_time_zone_table[0].name = tmp->tm_zone;
+ pc.local_time_zone_table[0].type = tLOCAL_ZONE;
+ pc.local_time_zone_table[0].value = tmp->tm_isdst;
+ pc.local_time_zone_table[1].name = 0;
+
+ /* Probe the names used in the next three calendar quarters, looking
+ for a tm_isdst different from the one we already have. */
+ {
+ int quarter;
+ for (quarter = 1; quarter <= 3; quarter++)
+ {
+ time_t probe = Start + quarter * (90 * 24 * 60 * 60);
+ struct tm *probe_tm = localtime (&probe);
+ if (probe_tm && probe_tm->tm_zone
+ && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
+ {
+ {
+ pc.local_time_zone_table[1].name = probe_tm->tm_zone;
+ pc.local_time_zone_table[1].type = tLOCAL_ZONE;
+ pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
+ pc.local_time_zone_table[2].name = 0;
+ }
+ break;
+ }
+ }
+ }
+#else
+#if HAVE_TZNAME
+ {
+# ifndef tzname
+ extern char *tzname[];
+# endif
+ int i;
+ for (i = 0; i < 2; i++)
+ {
+ pc.local_time_zone_table[i].name = tzname[i];
+ pc.local_time_zone_table[i].type = tLOCAL_ZONE;
+ pc.local_time_zone_table[i].value = i;
+ }
+ pc.local_time_zone_table[i].name = 0;
+ }
+#else
+ pc.local_time_zone_table[0].name = 0;
+#endif
+#endif
+
+ if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
+ && ! strcmp (pc.local_time_zone_table[0].name,
+ pc.local_time_zone_table[1].name))
+ {
+ /* This locale uses the same abbrevation for standard and
+ daylight times. So if we see that abbreviation, we don't
+ know whether it's daylight time. */
+ pc.local_time_zone_table[0].value = -1;
+ pc.local_time_zone_table[1].name = 0;
+ }
+
+ if (yyparse (&pc) != 0
+ || 1 < pc.times_seen || 1 < pc.dates_seen || 1 < pc.days_seen
+ || 1 < (pc.local_zones_seen + pc.zones_seen)
+ || (pc.local_zones_seen && 1 < pc.local_isdst))
+ return -1;
+
+ tm.tm_year = to_year (pc.year) - TM_YEAR_BASE + pc.rel_year;
+ tm.tm_mon = pc.month - 1 + pc.rel_month;
+ tm.tm_mday = pc.day + pc.rel_day;
+ if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
+ {
+ tm.tm_hour = to_hour (pc.hour, pc.meridian);
+ if (tm.tm_hour < 0)
+ return -1;
+ tm.tm_min = pc.minutes;
+ tm.tm_sec = pc.seconds;
+ }
+ else
+ {
+ tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+ }
+
+ /* Let mktime deduce tm_isdst if we have an absolute time stamp,
+ or if the relative time stamp mentions days, months, or years. */
+ if (pc.dates_seen | pc.days_seen | pc.times_seen | pc.rel_day
+ | pc.rel_month | pc.rel_year)
+ tm.tm_isdst = -1;
+
+ /* But if the input explicitly specifies local time with or without
+ DST, give mktime that information. */
+ if (pc.local_zones_seen)
+ tm.tm_isdst = pc.local_isdst;
+
+ tm0 = tm;
+
+ Start = mktime (&tm);
+
+ if (Start == (time_t) -1)
+ {
+
+ /* Guard against falsely reporting errors near the time_t boundaries
+ when parsing times in other time zones. For example, if the min
+ time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
+ of UTC, then the min localtime value is 1970-01-01 08:00:00; if
+ we apply mktime to 1970-01-01 00:00:00 we will get an error, so
+ we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
+ zone by 24 hours to compensate. This algorithm assumes that
+ there is no DST transition within a day of the time_t boundaries. */
+ if (pc.zones_seen)
+ {
+ tm = tm0;
+ if (tm.tm_year <= EPOCH_YEAR - TM_YEAR_BASE)
+ {
+ tm.tm_mday++;
+ pc.time_zone += 24 * 60;
+ }
+ else
+ {
+ tm.tm_mday--;
+ pc.time_zone -= 24 * 60;
+ }
+ Start = mktime (&tm);
+ }
+
+ if (Start == (time_t) -1)
+ return Start;
+ }
+
+ if (pc.days_seen && ! pc.dates_seen)
+ {
+ tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
+ + 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
+ tm.tm_isdst = -1;
+ Start = mktime (&tm);
+ if (Start == (time_t) -1)
+ return Start;
+ }
+
+ if (pc.zones_seen)
+ {
+ int delta = pc.time_zone * 60;
+#ifdef HAVE_TM_GMTOFF
+ delta -= tm.tm_gmtoff;
+#else
+ struct tm *gmt = gmtime (&Start);
+ if (! gmt)
+ return -1;
+ delta -= tm_diff (&tm, gmt);
+#endif
+ if ((Start < Start - delta) != (delta < 0))
+ return -1; /* time_t overflow */
+ Start -= delta;
+ }
+
+ /* Add relative hours, minutes, and seconds. Ignore leap seconds;
+ i.e. "+ 10 minutes" means 600 seconds, even if one of them is a
+ leap second. Typically this is not what the user wants, but it's
+ too hard to do it the other way, because the time zone indicator
+ must be applied before relative times, and if mktime is applied
+ again the time zone will be lost. */
+ {
+ time_t t0 = Start;
+ long d1 = 60 * 60 * (long) pc.rel_hour;
+ time_t t1 = t0 + d1;
+ long d2 = 60 * (long) pc.rel_minutes;
+ time_t t2 = t1 + d2;
+ int d3 = pc.rel_seconds;
+ time_t t3 = t2 + d3;
+ if ((d1 / (60 * 60) ^ pc.rel_hour)
+ | (d2 / 60 ^ pc.rel_minutes)
+ | ((t0 + d1 < t0) ^ (d1 < 0))
+ | ((t1 + d2 < t1) ^ (d2 < 0))
+ | ((t2 + d3 < t2) ^ (d3 < 0)))
+ return -1;
+ Start = t3;
+ }
+
+ return Start;
+}
+
+#if TEST
+
+#include <stdio.h>
+
+int
+main (int ac, char **av)
+{
+ char buff[BUFSIZ];
+ time_t d;
+
+ printf ("Enter date, or blank line to exit.\n\t> ");
+ fflush (stdout);
+
+ buff[BUFSIZ - 1] = 0;
+ while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
+ {
+ d = get_date (buff, 0);
+ if (d == (time_t) -1)
+ printf ("Bad format - couldn't convert.\n");
+ else
+ printf ("%s", ctime (&d));
+ printf ("\t> ");
+ fflush (stdout);
+ }
+ return 0;
+}
+#endif /* defined TEST */
diff --git a/source/modules/vfs_audit.c b/source/modules/vfs_audit.c
new file mode 100644
index 00000000000..550d918b43c
--- /dev/null
+++ b/source/modules/vfs_audit.c
@@ -0,0 +1,280 @@
+/*
+ * Auditing VFS module for samba. Log selected file operations to syslog
+ * facility.
+ *
+ * Copyright (C) Tim Potter 1999-2000
+ * Copyright (C) Alexander Bokovoy 2002
+ * Copyright (C) Stefan (metze) Metzmacher 2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_VFS
+
+/* Function prototypes */
+
+static int audit_connect(vfs_handle_struct *handle, connection_struct *conn, const char *svc, const char *user);
+static void audit_disconnect(vfs_handle_struct *handle, connection_struct *conn);
+static DIR *audit_opendir(vfs_handle_struct *handle, connection_struct *conn, const char *fname);
+static int audit_mkdir(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode);
+static int audit_rmdir(vfs_handle_struct *handle, connection_struct *conn, const char *path);
+static int audit_open(vfs_handle_struct *handle, connection_struct *conn, const char *fname, int flags, mode_t mode);
+static int audit_close(vfs_handle_struct *handle, files_struct *fsp, int fd);
+static int audit_rename(vfs_handle_struct *handle, connection_struct *conn, const char *old, const char *new);
+static int audit_unlink(vfs_handle_struct *handle, connection_struct *conn, const char *path);
+static int audit_chmod(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode);
+static int audit_chmod_acl(vfs_handle_struct *handle, connection_struct *conn, const char *name, mode_t mode);
+static int audit_fchmod(vfs_handle_struct *handle, files_struct *fsp, int fd, mode_t mode);
+static int audit_fchmod_acl(vfs_handle_struct *handle, files_struct *fsp, int fd, mode_t mode);
+
+/* VFS operations */
+
+static vfs_op_tuple audit_op_tuples[] = {
+
+ /* Disk operations */
+
+ {SMB_VFS_OP(audit_connect), SMB_VFS_OP_CONNECT, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_disconnect), SMB_VFS_OP_DISCONNECT, SMB_VFS_LAYER_LOGGER},
+
+ /* Directory operations */
+
+ {SMB_VFS_OP(audit_opendir), SMB_VFS_OP_OPENDIR, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_mkdir), SMB_VFS_OP_MKDIR, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_rmdir), SMB_VFS_OP_RMDIR, SMB_VFS_LAYER_LOGGER},
+
+ /* File operations */
+
+ {SMB_VFS_OP(audit_open), SMB_VFS_OP_OPEN, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_close), SMB_VFS_OP_CLOSE, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_rename), SMB_VFS_OP_RENAME, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_unlink), SMB_VFS_OP_UNLINK, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_chmod), SMB_VFS_OP_CHMOD, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_fchmod), SMB_VFS_OP_FCHMOD, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_chmod_acl), SMB_VFS_OP_CHMOD_ACL, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_fchmod_acl), SMB_VFS_OP_FCHMOD_ACL, SMB_VFS_LAYER_LOGGER},
+
+ /* Finish VFS operations definition */
+
+ {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+
+static int audit_syslog_facility(vfs_handle_struct *handle)
+{
+ /* fix me: let this be configurable by:
+ * lp_param_enum(SNUM(handle->conn),(handle->param?handle->param:"audit"),"syslog facility",
+ * audit_enum_facility,LOG_USER);
+ */
+ return LOG_USER;
+}
+
+
+static int audit_syslog_priority(vfs_handle_struct *handle)
+{
+ /* fix me: let this be configurable by:
+ * lp_param_enum(SNUM(handle->conn),(handle->param?handle->param:"audit"),"syslog priority",
+ * audit_enum_priority,LOG_NOTICE);
+ */
+ return LOG_NOTICE;
+}
+
+/* Implementation of vfs_ops. Pass everything on to the default
+ operation but log event first. */
+
+static int audit_connect(vfs_handle_struct *handle, connection_struct *conn, const char *svc, const char *user)
+{
+ int result;
+
+ openlog("smbd_audit", LOG_PID, audit_syslog_facility(handle));
+
+ syslog(audit_syslog_priority(handle), "connect to service %s by user %s\n",
+ svc, user);
+
+ result = SMB_VFS_NEXT_CONNECT(handle, conn, svc, user);
+
+ return result;
+}
+
+static void audit_disconnect(vfs_handle_struct *handle, connection_struct *conn)
+{
+ syslog(audit_syslog_priority(handle), "disconnected\n");
+ SMB_VFS_NEXT_DISCONNECT(handle, conn);
+
+ return;
+}
+
+static DIR *audit_opendir(vfs_handle_struct *handle, connection_struct *conn, const char *fname)
+{
+ DIR *result;
+
+ result = SMB_VFS_NEXT_OPENDIR(handle, conn, fname);
+
+ syslog(audit_syslog_priority(handle), "opendir %s %s%s\n",
+ fname,
+ (result == NULL) ? "failed: " : "",
+ (result == NULL) ? strerror(errno) : "");
+
+ return result;
+}
+
+static int audit_mkdir(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_MKDIR(handle, conn, path, mode);
+
+ syslog(audit_syslog_priority(handle), "mkdir %s %s%s\n",
+ path,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+
+ return result;
+}
+
+static int audit_rmdir(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_RMDIR(handle, conn, path);
+
+ syslog(audit_syslog_priority(handle), "rmdir %s %s%s\n",
+ path,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+
+ return result;
+}
+
+static int audit_open(vfs_handle_struct *handle, connection_struct *conn, const char *fname, int flags, mode_t mode)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_OPEN(handle, conn, fname, flags, mode);
+
+ syslog(audit_syslog_priority(handle), "open %s (fd %d) %s%s%s\n",
+ fname, result,
+ ((flags & O_WRONLY) || (flags & O_RDWR)) ? "for writing " : "",
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+
+ return result;
+}
+
+static int audit_close(vfs_handle_struct *handle, files_struct *fsp, int fd)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_CLOSE(handle, fsp, fd);
+
+ syslog(audit_syslog_priority(handle), "close fd %d %s%s\n",
+ fd,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+
+ return result;
+}
+
+static int audit_rename(vfs_handle_struct *handle, connection_struct *conn, const char *old, const char *new)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_RENAME(handle, conn, old, new);
+
+ syslog(audit_syslog_priority(handle), "rename %s -> %s %s%s\n",
+ old, new,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+
+ return result;
+}
+
+static int audit_unlink(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_UNLINK(handle, conn, path);
+
+ syslog(audit_syslog_priority(handle), "unlink %s %s%s\n",
+ path,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+
+ return result;
+}
+
+static int audit_chmod(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_CHMOD(handle, conn, path, mode);
+
+ syslog(audit_syslog_priority(handle), "chmod %s mode 0x%x %s%s\n",
+ path, mode,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+
+ return result;
+}
+
+static int audit_chmod_acl(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_CHMOD_ACL(handle, conn, path, mode);
+
+ syslog(audit_syslog_priority(handle), "chmod_acl %s mode 0x%x %s%s\n",
+ path, mode,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+
+ return result;
+}
+
+static int audit_fchmod(vfs_handle_struct *handle, files_struct *fsp, int fd, mode_t mode)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_FCHMOD(handle, fsp, fd, mode);
+
+ syslog(audit_syslog_priority(handle), "fchmod %s mode 0x%x %s%s\n",
+ fsp->fsp_name, mode,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+
+ return result;
+}
+
+static int audit_fchmod_acl(vfs_handle_struct *handle, files_struct *fsp, int fd, mode_t mode)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_FCHMOD_ACL(handle, fsp, fd, mode);
+
+ syslog(audit_syslog_priority(handle), "fchmod_acl %s mode 0x%x %s%s\n",
+ fsp->fsp_name, mode,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+
+ return result;
+}
+
+NTSTATUS vfs_audit_init(void)
+{
+ return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "audit", audit_op_tuples);
+}
diff --git a/source/modules/vfs_cap.c b/source/modules/vfs_cap.c
new file mode 100644
index 00000000000..0526276acb8
--- /dev/null
+++ b/source/modules/vfs_cap.c
@@ -0,0 +1,448 @@
+/*
+ * CAP VFS module for Samba 3.x Version 0.3
+ *
+ * Copyright (C) Tim Potter, 1999-2000
+ * Copyright (C) Alexander Bokovoy, 2002-2003
+ * Copyright (C) Stefan (metze) Metzmacher, 2003
+ * Copyright (C) TAKAHASHI Motonobu (monyo), 2003
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include "includes.h"
+
+/* cap functions */
+static char *capencode(char *to, const char *from);
+static char *capdecode(char *to, const char *from);
+
+static SMB_BIG_UINT cap_disk_free(vfs_handle_struct *handle, connection_struct *conn, const char *path,
+ BOOL small_query, SMB_BIG_UINT *bsize,
+ SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_DISK_FREE(handle, conn, cappath, small_query, bsize,
+ dfree, dsize);
+}
+
+static DIR *cap_opendir(vfs_handle_struct *handle, connection_struct *conn, const char *fname)
+{
+ pstring capname;
+ capencode(capname, fname);
+ return SMB_VFS_NEXT_OPENDIR(handle, conn, capname);
+}
+
+static struct dirent *cap_readdir(vfs_handle_struct *handle, connection_struct *conn, DIR *dirp)
+{
+ struct dirent *result;
+ DEBUG(3,("cap: cap_readdir\n"));
+ result = SMB_VFS_NEXT_READDIR(handle, conn, dirp);
+ if (result) {
+ DEBUG(3,("cap: cap_readdir: %s\n", result->d_name));
+ capdecode(result->d_name, result->d_name);
+ }
+ return result;
+}
+
+static int cap_mkdir(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode)
+{
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_MKDIR(handle, conn, cappath, mode);
+}
+
+static int cap_rmdir(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_RMDIR(handle, conn, cappath);
+}
+
+static int cap_open(vfs_handle_struct *handle, connection_struct *conn, const char *fname, int flags, mode_t mode)
+{
+ pstring capname;
+ DEBUG(3,("cap: cap_open for %s\n", fname));
+ capencode(capname, fname);
+ return SMB_VFS_NEXT_OPEN(handle, conn, capname, flags, mode);
+}
+
+static int cap_rename(vfs_handle_struct *handle, connection_struct *conn, const char *old, const char *new)
+{
+ pstring capold, capnew;
+ capencode(capold, old);
+ capencode(capnew, new);
+
+ return SMB_VFS_NEXT_RENAME(handle, conn, capold, capnew);
+}
+
+static int cap_stat(vfs_handle_struct *handle, connection_struct *conn, const char *fname, SMB_STRUCT_STAT *sbuf)
+{
+ pstring capname;
+ capencode(capname, fname);
+ return SMB_VFS_NEXT_STAT(handle, conn, capname, sbuf);
+}
+
+static int cap_lstat(vfs_handle_struct *handle, connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf)
+{
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_LSTAT(handle, conn, cappath, sbuf);
+}
+
+static int cap_unlink(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_UNLINK(handle, conn, cappath);
+}
+
+static int cap_chmod(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode)
+{
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_CHMOD(handle, conn, cappath, mode);
+}
+
+static int cap_chown(vfs_handle_struct *handle, connection_struct *conn, const char *path, uid_t uid, gid_t gid)
+{
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_CHOWN(handle, conn, cappath, uid, gid);
+}
+
+static int cap_chdir(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ pstring cappath;
+ DEBUG(3,("cap: cap_chdir for %s\n", path));
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_CHDIR(handle, conn, cappath);
+}
+
+static int cap_utime(vfs_handle_struct *handle, connection_struct *conn, const char *path, struct utimbuf *times)
+{
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_UTIME(handle, conn, cappath, times);
+}
+
+
+static BOOL cap_symlink(vfs_handle_struct *handle, connection_struct *conn, const char *oldpath, const char *newpath)
+{
+ pstring capoldpath, capnewpath;
+ capencode(capoldpath, oldpath);
+ capencode(capnewpath, newpath);
+ return SMB_VFS_NEXT_SYMLINK(handle, conn, capoldpath, capnewpath);
+}
+
+static BOOL cap_readlink(vfs_handle_struct *handle, connection_struct *conn, const char *path, char *buf, size_t bufsiz)
+{
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_READLINK(handle, conn, cappath, buf, bufsiz);
+}
+
+static int cap_link(vfs_handle_struct *handle, connection_struct *conn, const char *oldpath, const char *newpath)
+{
+ pstring capoldpath, capnewpath;
+ capencode(capoldpath, oldpath);
+ capencode(capnewpath, newpath);
+ return SMB_VFS_NEXT_LINK(handle, conn, capoldpath, capnewpath);
+}
+
+static int cap_mknod(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode, SMB_DEV_T dev)
+{
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_MKNOD(handle, conn, cappath, mode, dev);
+}
+
+static char *cap_realpath(vfs_handle_struct *handle, connection_struct *conn, const char *path, char *resolved_path)
+{
+ /* monyo need capencode'ed and capdecode'ed? */
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_REALPATH(handle, conn, path, resolved_path);
+}
+
+static BOOL cap_set_nt_acl(vfs_handle_struct *handle, files_struct *fsp, const char *name, uint32 security_info_sent, struct security_descriptor_info *psd)
+{
+ pstring capname;
+ capencode(capname, name);
+ return SMB_VFS_NEXT_SET_NT_ACL(handle, fsp, capname, security_info_sent, psd);
+}
+
+static int cap_chmod_acl(vfs_handle_struct *handle, connection_struct *conn, const char *name, mode_t mode)
+{
+ pstring capname;
+ capencode(capname, name);
+
+ /* If the underlying VFS doesn't have ACL support... */
+ if (!handle->vfs_next.ops.chmod_acl) {
+ errno = ENOSYS;
+ return -1;
+ }
+ return SMB_VFS_NEXT_CHMOD_ACL(handle, conn, capname, mode);
+}
+
+static SMB_ACL_T cap_sys_acl_get_file(vfs_handle_struct *handle, connection_struct *conn, const char *path_p, SMB_ACL_TYPE_T type)
+{
+ pstring cappath_p;
+ capencode(cappath_p, path_p);
+ return SMB_VFS_NEXT_SYS_ACL_GET_FILE(handle, conn, cappath_p, type);
+}
+
+static int cap_sys_acl_set_file(vfs_handle_struct *handle, connection_struct *conn, const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+ pstring capname;
+ capencode(capname, name);
+ return SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, conn, capname, acltype, theacl);
+}
+
+static int cap_sys_acl_delete_def_file(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(handle, conn, cappath);
+}
+
+static ssize_t cap_getxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, void *value, size_t size)
+{
+ pstring cappath, capname;
+ capencode(cappath, path);
+ capencode(capname, name);
+ return SMB_VFS_NEXT_GETXATTR(handle, conn, cappath, capname, value, size);
+}
+
+static ssize_t cap_lgetxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, void *value, size_t
+size)
+{
+ pstring cappath, capname;
+ capencode(cappath, path);
+ capencode(capname, name);
+ return SMB_VFS_NEXT_LGETXATTR(handle, conn, cappath, capname, value, size);
+}
+
+static ssize_t cap_fgetxattr(vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name, void *value, size_t size)
+{
+ pstring capname;
+ capencode(capname, name);
+ return SMB_VFS_NEXT_FGETXATTR(handle, fsp, fd, capname, value, size);
+}
+
+static ssize_t cap_listxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, char *list, size_t size)
+{
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_LISTXATTR(handle, conn, cappath, list, size);
+}
+
+static ssize_t cap_llistxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, char *list, size_t size)
+{
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_LLISTXATTR(handle, conn, cappath, list, size);
+}
+
+static int cap_removexattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name)
+{
+ pstring cappath, capname;
+ capencode(cappath, path);
+ capencode(capname, name);
+ return SMB_VFS_NEXT_REMOVEXATTR(handle, conn, cappath, capname);
+}
+
+static int cap_lremovexattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name)
+{
+ pstring cappath, capname;
+ capencode(cappath, path);
+ capencode(capname, name);
+ return SMB_VFS_NEXT_LREMOVEXATTR(handle, conn, cappath, capname);
+}
+
+static int cap_fremovexattr(vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name)
+{
+ pstring capname;
+ capencode(capname, name);
+ return SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp, fd, capname);
+}
+
+static int cap_setxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, const void *value, size_t size, int flags)
+{
+ pstring cappath, capname;
+ capencode(cappath, path);
+ capencode(capname, name);
+ return SMB_VFS_NEXT_SETXATTR(handle, conn, cappath, capname, value, size, flags);
+}
+
+static int cap_lsetxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, const void *value, size_t size, int flags)
+{
+ pstring cappath, capname;
+ capencode(cappath, path);
+ capencode(capname, name);
+ return SMB_VFS_NEXT_LSETXATTR(handle, conn, cappath, capname, value, size, flags);
+}
+
+static int cap_fsetxattr(vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name, const void *value, size_t size, int flags)
+{
+ pstring capname;
+ capencode(capname, name);
+ return SMB_VFS_NEXT_FSETXATTR(handle, fsp, fd, capname, value, size, flags);
+}
+
+/* VFS operations structure */
+
+static vfs_op_tuple cap_op_tuples[] = {
+
+ /* Disk operations */
+
+ {SMB_VFS_OP(cap_disk_free), SMB_VFS_OP_DISK_FREE, SMB_VFS_LAYER_TRANSPARENT},
+
+ /* Directory operations */
+
+ {SMB_VFS_OP(cap_opendir), SMB_VFS_OP_OPENDIR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_readdir), SMB_VFS_OP_READDIR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_mkdir), SMB_VFS_OP_MKDIR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_rmdir), SMB_VFS_OP_RMDIR, SMB_VFS_LAYER_TRANSPARENT},
+
+ /* File operations */
+
+ {SMB_VFS_OP(cap_open), SMB_VFS_OP_OPEN, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_rename), SMB_VFS_OP_RENAME, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_stat), SMB_VFS_OP_STAT, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_lstat), SMB_VFS_OP_LSTAT, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_unlink), SMB_VFS_OP_UNLINK, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_chmod), SMB_VFS_OP_CHMOD, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_chown), SMB_VFS_OP_CHOWN, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_chdir), SMB_VFS_OP_CHDIR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_utime), SMB_VFS_OP_UTIME, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_symlink), SMB_VFS_OP_SYMLINK, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_readlink), SMB_VFS_OP_READLINK, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_link), SMB_VFS_OP_LINK, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_mknod), SMB_VFS_OP_MKNOD, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_realpath), SMB_VFS_OP_REALPATH, SMB_VFS_LAYER_TRANSPARENT},
+
+ /* NT File ACL operations */
+
+ {SMB_VFS_OP(cap_set_nt_acl), SMB_VFS_OP_SET_NT_ACL, SMB_VFS_LAYER_TRANSPARENT},
+
+ /* POSIX ACL operations */
+
+ {SMB_VFS_OP(cap_chmod_acl), SMB_VFS_OP_CHMOD_ACL, SMB_VFS_LAYER_TRANSPARENT},
+
+ {SMB_VFS_OP(cap_sys_acl_get_file), SMB_VFS_OP_SYS_ACL_GET_FILE, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_sys_acl_set_file), SMB_VFS_OP_SYS_ACL_SET_FILE, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_sys_acl_delete_def_file), SMB_VFS_OP_SYS_ACL_DELETE_DEF_FILE, SMB_VFS_LAYER_TRANSPARENT},
+
+ /* EA operations. */
+ {SMB_VFS_OP(cap_getxattr), SMB_VFS_OP_GETXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_lgetxattr), SMB_VFS_OP_LGETXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_fgetxattr), SMB_VFS_OP_FGETXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_listxattr), SMB_VFS_OP_LISTXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_llistxattr), SMB_VFS_OP_LLISTXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_removexattr), SMB_VFS_OP_REMOVEXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_lremovexattr), SMB_VFS_OP_LREMOVEXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_fremovexattr), SMB_VFS_OP_FREMOVEXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_setxattr), SMB_VFS_OP_SETXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_lsetxattr), SMB_VFS_OP_LSETXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_fsetxattr), SMB_VFS_OP_FSETXATTR, SMB_VFS_LAYER_TRANSPARENT},
+
+ {NULL, SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+NTSTATUS vfs_cap_init(void)
+{
+ return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "cap", cap_op_tuples);
+}
+
+/* For CAP functions */
+#define hex_tag ':'
+#define hex2bin(c) hex2bin_table[(unsigned char)(c)]
+#define bin2hex(c) bin2hex_table[(unsigned char)(c)]
+#define is_hex(s) ((s)[0] == hex_tag)
+
+static unsigned char hex2bin_table[256] = {
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 */
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 */
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 */
+0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 0x30 */
+0000, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0000, /* 0x40 */
+0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 */
+0000, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0000, /* 0x60 */
+0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 */
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 */
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 */
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 */
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 */
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 */
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 */
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 */
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 0xf0 */
+};
+static unsigned char bin2hex_table[256] = "0123456789abcdef";
+
+/*******************************************************************
+ original code -> ":xx" - CAP format
+********************************************************************/
+static char *capencode(char *to, const char *from)
+{
+ pstring cvtbuf;
+ char *out;
+
+ if (to == from) {
+ from = pstrcpy ((char *) cvtbuf, from);
+ }
+
+ for (out = to; *from && (out - to < sizeof(pstring)-7);) {
+ /* buffer husoku error */
+ if ((unsigned char)*from >= 0x80) {
+ *out++ = hex_tag;
+ *out++ = bin2hex (((*from)>>4)&0x0f);
+ *out++ = bin2hex ((*from)&0x0f);
+ from++;
+ }
+ else {
+ *out++ = *from++;
+ }
+ }
+ *out = '\0';
+ return to;
+}
+
+/*******************************************************************
+ CAP -> original code
+********************************************************************/
+/* ":xx" -> a byte */
+static char *capdecode(char *to, const char *from)
+{
+ pstring cvtbuf;
+ char *out;
+
+ if (to == from) {
+ from = pstrcpy ((char *) cvtbuf, from);
+ }
+ for (out = to; *from && (out - to < sizeof(pstring)-3);) {
+ if (is_hex(from)) {
+ *out++ = (hex2bin (from[1])<<4) | (hex2bin (from[2]));
+ from += 3;
+ } else {
+ *out++ = *from++;
+ }
+ }
+ *out = '\0';
+ return to;
+}
diff --git a/source/modules/vfs_default_quota.c b/source/modules/vfs_default_quota.c
new file mode 100644
index 00000000000..1294a515333
--- /dev/null
+++ b/source/modules/vfs_default_quota.c
@@ -0,0 +1,180 @@
+/*
+ * Store default Quotas in a specified quota record
+ *
+ * Copyright (C) Stefan (metze) Metzmacher 2003
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_VFS
+
+#define DEFAULT_QUOTA_NAME "default_quota"
+
+#define DEFAULT_QUOTA_UID_DEFAULT 0
+#define DEFAULT_QUOTA_UID_NOLIMIT_DEFAULT True
+#define DEFAULT_QUOTA_GID_DEFAULT 0
+#define DEFAULT_QUOTA_GID_NOLIMIT_DEFAULT True
+
+#define DEFAULT_QUOTA_UID(handle) \
+ (uid_t)lp_parm_int(SNUM((handle)->conn),DEFAULT_QUOTA_NAME,"uid",DEFAULT_QUOTA_UID_DEFAULT)
+
+#define DEFAULT_QUOTA_UID_NOLIMIT(handle) \
+ lp_parm_bool(SNUM((handle)->conn),DEFAULT_QUOTA_NAME,"uid nolimit",DEFAULT_QUOTA_UID_NOLIMIT_DEFAULT)
+
+#define DEFAULT_QUOTA_GID(handle) \
+ (gid_t)lp_parm_int(SNUM((handle)->conn),DEFAULT_QUOTA_NAME,"gid",DEFAULT_QUOTA_GID_DEFAULT)
+
+#define DEFAULT_QUOTA_GID_NOLIMIT(handle) \
+ lp_parm_bool(SNUM((handle)->conn),DEFAULT_QUOTA_NAME,"gid nolimit",DEFAULT_QUOTA_GID_NOLIMIT_DEFAULT)
+
+static int default_quota_get_quota(vfs_handle_struct *handle, connection_struct *conn, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dq)
+{
+ int ret = -1;
+
+ if ((ret=SMB_VFS_NEXT_GET_QUOTA(handle, conn, qtype, id, dq))!=0) {
+ return ret;
+ }
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ /* we use id.uid == 0 for default quotas */
+ if ((id.uid==DEFAULT_QUOTA_UID(handle)) &&
+ DEFAULT_QUOTA_UID_NOLIMIT(handle)) {
+ SMB_QUOTAS_SET_NO_LIMIT(dq);
+ }
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_QUOTA_TYPE:
+ /* we use id.gid == 0 for default quotas */
+ if ((id.gid==DEFAULT_QUOTA_GID(handle)) &&
+ DEFAULT_QUOTA_GID_NOLIMIT(handle)) {
+ SMB_QUOTAS_SET_NO_LIMIT(dq);
+ }
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ case SMB_USER_FS_QUOTA_TYPE:
+ {
+ unid_t qid;
+ uint32 qflags = dq->qflags;
+ qid.uid = DEFAULT_QUOTA_UID(handle);
+ SMB_VFS_NEXT_GET_QUOTA(handle, conn, SMB_USER_QUOTA_TYPE, qid, dq);
+ dq->qflags = qflags;
+ }
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ {
+ unid_t qid;
+ uint32 qflags = dq->qflags;
+ qid.gid = DEFAULT_QUOTA_GID(handle);
+ SMB_VFS_NEXT_GET_QUOTA(handle, conn, SMB_GROUP_QUOTA_TYPE, qid, dq);
+ dq->qflags = qflags;
+ }
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ default:
+ errno = ENOSYS;
+ return -1;
+ break;
+ }
+
+ return ret;
+}
+
+static int default_quota_set_quota(vfs_handle_struct *handle, connection_struct *conn, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dq)
+{
+ int ret = -1;
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ /* we use id.uid == 0 for default quotas */
+ if ((id.uid==DEFAULT_QUOTA_UID(handle)) &&
+ DEFAULT_QUOTA_UID_NOLIMIT(handle)) {
+ return -1;
+ }
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_QUOTA_TYPE:
+ /* we use id.gid == 0 for default quotas */
+ if ((id.gid==DEFAULT_QUOTA_GID(handle)) &&
+ DEFAULT_QUOTA_GID_NOLIMIT(handle)) {
+ return -1;
+ }
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ case SMB_USER_FS_QUOTA_TYPE:
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ default:
+ errno = ENOSYS;
+ return -1;
+ break;
+ }
+
+ if ((ret=SMB_VFS_NEXT_SET_QUOTA(handle, conn, qtype, id, dq))!=0) {
+ return ret;
+ }
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_QUOTA_TYPE:
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ case SMB_USER_FS_QUOTA_TYPE:
+ {
+ unid_t qid;
+ qid.uid = DEFAULT_QUOTA_UID(handle);
+ ret = SMB_VFS_NEXT_SET_QUOTA(handle, conn, SMB_USER_QUOTA_TYPE, qid, dq);
+ }
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ {
+ unid_t qid;
+ qid.gid = DEFAULT_QUOTA_GID(handle);
+ ret = SMB_VFS_NEXT_SET_QUOTA(handle, conn, SMB_GROUP_QUOTA_TYPE, qid, dq);
+ }
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ default:
+ errno = ENOSYS;
+ return -1;
+ break;
+ }
+
+ return ret;
+}
+
+/* VFS operations structure */
+
+static vfs_op_tuple default_quota_ops[] = {
+ {SMB_VFS_OP(default_quota_get_quota), SMB_VFS_OP_GET_QUOTA, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(default_quota_set_quota), SMB_VFS_OP_SET_QUOTA, SMB_VFS_LAYER_TRANSPARENT},
+
+ {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+NTSTATUS vfs_default_quota_init(void)
+{
+ return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, DEFAULT_QUOTA_NAME, default_quota_ops);
+}
diff --git a/source/modules/vfs_expand_msdfs.c b/source/modules/vfs_expand_msdfs.c
new file mode 100644
index 00000000000..07fbe59825e
--- /dev/null
+++ b/source/modules/vfs_expand_msdfs.c
@@ -0,0 +1,191 @@
+/*
+ * Expand msdfs targets based on client IP
+ *
+ * Copyright (C) Volker Lendecke, 2004
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_VFS
+
+/**********************************************************
+ Under mapfile we expect a table of the following format:
+
+ IP-Prefix whitespace expansion
+
+ For example:
+ 192.168.234 local.samba.org
+ 192.168 remote.samba.org
+ default.samba.org
+
+ This is to redirect a DFS client to a host close to it.
+***********************************************************/
+
+static BOOL read_target_host(const char *mapfile, pstring targethost)
+{
+ XFILE *f;
+ pstring buf;
+ char *s, *space = buf;
+ BOOL found = False;
+
+ f = x_fopen(mapfile, O_RDONLY, 0);
+
+ if (f == NULL) {
+ DEBUG(0,("can't open IP map %s. Error %s\n",
+ mapfile, strerror(errno) ));
+ return False;
+ }
+
+ DEBUG(10, ("Scanning mapfile [%s]\n", mapfile));
+
+ while ((s=x_fgets(buf, sizeof(buf), f)) != NULL) {
+
+ if ((strlen(buf) > 0) && (buf[strlen(buf)-1] == '\n'))
+ buf[strlen(buf)-1] = '\0';
+
+ DEBUG(10, ("Scanning line [%s]\n", buf));
+
+ space = strchr_m(buf, ' ');
+
+ if (space == NULL) {
+ DEBUG(0, ("Ignoring invalid line %s\n", buf));
+ continue;
+ }
+
+ *space = '\0';
+
+ if (strncmp(client_addr(), buf, strlen(buf)) == 0) {
+ found = True;
+ break;
+ }
+ }
+
+ x_fclose(f);
+
+ if (!found)
+ return False;
+
+ space += 1;
+
+ while (isspace(*space))
+ space += 1;
+
+ pstrcpy(targethost, space);
+ return True;
+}
+
+/**********************************************************
+
+ Expand the msdfs target host using read_target_host
+ explained above. The syntax used in the msdfs link is
+
+ msdfs:@table-filename@/share
+
+ Everything between and including the two @-signs is
+ replaced by the substitution string found in the table
+ described above.
+
+***********************************************************/
+
+static BOOL expand_msdfs_target(connection_struct* conn, pstring target)
+{
+ pstring mapfilename;
+ char *filename_start = strchr_m(target, '@');
+ char *filename_end;
+ int filename_len;
+ pstring targethost;
+ pstring new_target;
+
+ if (filename_start == NULL) {
+ DEBUG(10, ("No filename start in %s\n", target));
+ return False;
+ }
+
+ filename_end = strchr_m(filename_start+1, '@');
+
+ if (filename_end == NULL) {
+ DEBUG(10, ("No filename end in %s\n", target));
+ return False;
+ }
+
+ filename_len = PTR_DIFF(filename_end, filename_start+1);
+ pstrcpy(mapfilename, filename_start+1);
+ mapfilename[filename_len] = '\0';
+
+ DEBUG(10, ("Expanding from table [%s]\n", mapfilename));
+
+ if (!read_target_host(mapfilename, targethost)) {
+ DEBUG(1, ("Could not expand target host from file %s\n",
+ mapfilename));
+ return False;
+ }
+
+ standard_sub_conn(conn, mapfilename, sizeof(mapfilename));
+
+ DEBUG(10, ("Expanded targethost to %s\n", targethost));
+
+ *filename_start = '\0';
+ pstrcpy(new_target, target);
+ pstrcat(new_target, targethost);
+ pstrcat(new_target, filename_end+1);
+
+ DEBUG(10, ("New DFS target: %s\n", new_target));
+ pstrcpy(target, new_target);
+ return True;
+}
+
+static int expand_msdfs_readlink(struct vfs_handle_struct *handle,
+ struct connection_struct *conn,
+ const char *path, char *buf, size_t bufsiz)
+{
+ pstring target;
+ int result;
+
+ result = SMB_VFS_NEXT_READLINK(handle, conn, path, target,
+ sizeof(target));
+
+ if (result < 0)
+ return result;
+
+ target[result] = '\0';
+
+ if ((strncmp(target, "msdfs:", strlen("msdfs:")) == 0) &&
+ (strchr_m(target, '@') != NULL)) {
+ if (!expand_msdfs_target(conn, target)) {
+ errno = ENOENT;
+ return -1;
+ }
+ }
+
+ safe_strcpy(buf, target, bufsiz-1);
+ return strlen(buf);
+}
+
+/* VFS operations structure */
+
+static vfs_op_tuple expand_msdfs_ops[] = {
+ {SMB_VFS_OP(expand_msdfs_readlink), SMB_VFS_OP_READLINK,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+NTSTATUS vfs_expand_msdfs_init(void)
+{
+ return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "expand_msdfs",
+ expand_msdfs_ops);
+}
diff --git a/source/modules/vfs_extd_audit.c b/source/modules/vfs_extd_audit.c
new file mode 100644
index 00000000000..06cddc78e43
--- /dev/null
+++ b/source/modules/vfs_extd_audit.c
@@ -0,0 +1,343 @@
+/*
+ * Auditing VFS module for samba. Log selected file operations to syslog
+ * facility.
+ *
+ * Copyright (C) Tim Potter, 1999-2000
+ * Copyright (C) Alexander Bokovoy, 2002
+ * Copyright (C) John H Terpstra, 2003
+ * Copyright (C) Stefan (metze) Metzmacher, 2003
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include "includes.h"
+
+static int vfs_extd_audit_debug_level = DBGC_VFS;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS vfs_extd_audit_debug_level
+
+/* Function prototypes */
+
+static int audit_connect(vfs_handle_struct *handle, connection_struct *conn, const char *svc, const char *user);
+static void audit_disconnect(vfs_handle_struct *handle, connection_struct *conn);
+static DIR *audit_opendir(vfs_handle_struct *handle, connection_struct *conn, const char *fname);
+static int audit_mkdir(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode);
+static int audit_rmdir(vfs_handle_struct *handle, connection_struct *conn, const char *path);
+static int audit_open(vfs_handle_struct *handle, connection_struct *conn, const char *fname, int flags, mode_t mode);
+static int audit_close(vfs_handle_struct *handle, files_struct *fsp, int fd);
+static int audit_rename(vfs_handle_struct *handle, connection_struct *conn, const char *old, const char *new);
+static int audit_unlink(vfs_handle_struct *handle, connection_struct *conn, const char *path);
+static int audit_chmod(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode);
+static int audit_chmod_acl(vfs_handle_struct *handle, connection_struct *conn, const char *name, mode_t mode);
+static int audit_fchmod(vfs_handle_struct *handle, files_struct *fsp, int fd, mode_t mode);
+static int audit_fchmod_acl(vfs_handle_struct *handle, files_struct *fsp, int fd, mode_t mode);
+
+/* VFS operations */
+
+static vfs_op_tuple audit_op_tuples[] = {
+
+ /* Disk operations */
+
+ {SMB_VFS_OP(audit_connect), SMB_VFS_OP_CONNECT, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_disconnect), SMB_VFS_OP_DISCONNECT, SMB_VFS_LAYER_LOGGER},
+
+ /* Directory operations */
+
+ {SMB_VFS_OP(audit_opendir), SMB_VFS_OP_OPENDIR, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_mkdir), SMB_VFS_OP_MKDIR, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_rmdir), SMB_VFS_OP_RMDIR, SMB_VFS_LAYER_LOGGER},
+
+ /* File operations */
+
+ {SMB_VFS_OP(audit_open), SMB_VFS_OP_OPEN, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_close), SMB_VFS_OP_CLOSE, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_rename), SMB_VFS_OP_RENAME, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_unlink), SMB_VFS_OP_UNLINK, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_chmod), SMB_VFS_OP_CHMOD, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_fchmod), SMB_VFS_OP_FCHMOD, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_chmod_acl), SMB_VFS_OP_CHMOD_ACL, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_fchmod_acl), SMB_VFS_OP_FCHMOD_ACL, SMB_VFS_LAYER_LOGGER},
+
+ /* Finish VFS operations definition */
+
+ {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+
+static int audit_syslog_facility(vfs_handle_struct *handle)
+{
+ /* fix me: let this be configurable by:
+ * lp_param_enum(SNUM(handle->conn),(handle->param?handle->param:"extd_audit"),"syslog facility",
+ * audit_enum_facility,LOG_USER);
+ */
+ return LOG_USER;
+}
+
+
+static int audit_syslog_priority(vfs_handle_struct *handle)
+{
+ /* fix me: let this be configurable by:
+ * lp_param_enum(SNUM(handle->conn),(handle->param?handle->param:"extd_audit"),"syslog priority",
+ * audit_enum_priority,LOG_NOTICE);
+ */
+ return LOG_NOTICE;
+}
+
+/* Implementation of vfs_ops. Pass everything on to the default
+ operation but log event first. */
+
+static int audit_connect(vfs_handle_struct *handle, connection_struct *conn, const char *svc, const char *user)
+{
+ int result;
+
+ openlog("smbd_audit", LOG_PID, audit_syslog_facility(handle));
+
+ syslog(audit_syslog_priority(handle), "connect to service %s by user %s\n",
+ svc, user);
+ DEBUG(10, ("Connected to service %s as user %s\n",
+ svc, user));
+
+ result = SMB_VFS_NEXT_CONNECT(handle, conn, svc, user);
+
+ return result;
+}
+
+static void audit_disconnect(vfs_handle_struct *handle, connection_struct *conn)
+{
+ syslog(audit_syslog_priority(handle), "disconnected\n");
+ DEBUG(10, ("Disconnected from VFS module extd_audit\n"));
+ SMB_VFS_NEXT_DISCONNECT(handle, conn);
+
+ return;
+}
+
+static DIR *audit_opendir(vfs_handle_struct *handle, connection_struct *conn, const char *fname)
+{
+ DIR *result;
+
+ result = SMB_VFS_NEXT_OPENDIR(handle, conn, fname);
+
+ syslog(audit_syslog_priority(handle), "opendir %s %s%s\n",
+ fname,
+ (result == NULL) ? "failed: " : "",
+ (result == NULL) ? strerror(errno) : "");
+ DEBUG(1, ("vfs_extd_audit: opendir %s %s %s\n",
+ fname,
+ (result == NULL) ? "failed: " : "",
+ (result == NULL) ? strerror(errno) : ""));
+
+ return result;
+}
+
+static int audit_mkdir(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_MKDIR(handle, conn, path, mode);
+
+ syslog(audit_syslog_priority(handle), "mkdir %s %s%s\n",
+ path,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+ DEBUG(0, ("vfs_extd_audit: mkdir %s %s %s\n",
+ path,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : ""));
+
+ return result;
+}
+
+static int audit_rmdir(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_RMDIR(handle, conn, path);
+
+ syslog(audit_syslog_priority(handle), "rmdir %s %s%s\n",
+ path,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+ DEBUG(0, ("vfs_extd_audit: rmdir %s %s %s\n",
+ path,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : ""));
+
+ return result;
+}
+
+static int audit_open(vfs_handle_struct *handle, connection_struct *conn, const char *fname, int flags, mode_t mode)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_OPEN(handle, conn, fname, flags, mode);
+
+ syslog(audit_syslog_priority(handle), "open %s (fd %d) %s%s%s\n",
+ fname, result,
+ ((flags & O_WRONLY) || (flags & O_RDWR)) ? "for writing " : "",
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+ DEBUG(2, ("vfs_extd_audit: open %s %s %s\n",
+ fname,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : ""));
+
+ return result;
+}
+
+static int audit_close(vfs_handle_struct *handle, files_struct *fsp, int fd)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_CLOSE(handle, fsp, fd);
+
+ syslog(audit_syslog_priority(handle), "close fd %d %s%s\n",
+ fd,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+ DEBUG(2, ("vfs_extd_audit: close fd %d %s %s\n",
+ fd,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : ""));
+
+ return result;
+}
+
+static int audit_rename(vfs_handle_struct *handle, connection_struct *conn, const char *old, const char *new)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_RENAME(handle, conn, old, new);
+
+ syslog(audit_syslog_priority(handle), "rename %s -> %s %s%s\n",
+ old, new,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+ DEBUG(1, ("vfs_extd_audit: rename old: %s new: %s %s %s\n",
+ old, new,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : ""));
+
+ return result;
+}
+
+static int audit_unlink(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_UNLINK(handle, conn, path);
+
+ syslog(audit_syslog_priority(handle), "unlink %s %s%s\n",
+ path,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+ DEBUG(0, ("vfs_extd_audit: unlink %s %s %s\n",
+ path,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : ""));
+
+ return result;
+}
+
+static int audit_chmod(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_CHMOD(handle, conn, path, mode);
+
+ syslog(audit_syslog_priority(handle), "chmod %s mode 0x%x %s%s\n",
+ path, mode,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+ DEBUG(1, ("vfs_extd_audit: chmod %s mode 0x%x %s %s\n",
+ path, mode,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : ""));
+
+ return result;
+}
+
+static int audit_chmod_acl(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_CHMOD_ACL(handle, conn, path, mode);
+
+ syslog(audit_syslog_priority(handle), "chmod_acl %s mode 0x%x %s%s\n",
+ path, mode,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+ DEBUG(1, ("vfs_extd_audit: chmod_acl %s mode 0x%x %s %s\n",
+ path, mode,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : ""));
+
+ return result;
+}
+
+static int audit_fchmod(vfs_handle_struct *handle, files_struct *fsp, int fd, mode_t mode)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_FCHMOD(handle, fsp, fd, mode);
+
+ syslog(audit_syslog_priority(handle), "fchmod %s mode 0x%x %s%s\n",
+ fsp->fsp_name, mode,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+ DEBUG(1, ("vfs_extd_audit: fchmod %s mode 0x%x %s %s",
+ fsp->fsp_name, mode,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : ""));
+
+ return result;
+}
+
+static int audit_fchmod_acl(vfs_handle_struct *handle, files_struct *fsp, int fd, mode_t mode)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_FCHMOD_ACL(handle, fsp, fd, mode);
+
+ syslog(audit_syslog_priority(handle), "fchmod_acl %s mode 0x%x %s%s\n",
+ fsp->fsp_name, mode,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+ DEBUG(1, ("vfs_extd_audit: fchmod_acl %s mode 0x%x %s %s",
+ fsp->fsp_name, mode,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : ""));
+
+ return result;
+}
+
+NTSTATUS vfs_extd_audit_init(void)
+{
+ NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "extd_audit", audit_op_tuples);
+
+ if (!NT_STATUS_IS_OK(ret))
+ return ret;
+
+ vfs_extd_audit_debug_level = debug_add_class("extd_audit");
+ if (vfs_extd_audit_debug_level == -1) {
+ vfs_extd_audit_debug_level = DBGC_VFS;
+ DEBUG(0, ("vfs_extd_audit: Couldn't register custom debugging class!\n"));
+ } else {
+ DEBUG(10, ("vfs_extd_audit: Debug class number of 'extd_audit': %d\n", vfs_extd_audit_debug_level));
+ }
+
+ return ret;
+}
diff --git a/source/modules/vfs_fake_perms.c b/source/modules/vfs_fake_perms.c
new file mode 100644
index 00000000000..740218dcd41
--- /dev/null
+++ b/source/modules/vfs_fake_perms.c
@@ -0,0 +1,81 @@
+/*
+ * Fake Perms VFS module. Implements passthrough operation of all VFS
+ * calls to disk functions, except for file permissions, which are now
+ * mode 0700 for the current uid/gid.
+ *
+ * Copyright (C) Tim Potter, 1999-2000
+ * Copyright (C) Alexander Bokovoy, 2002
+ * Copyright (C) Andrew Bartlett, 2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_VFS
+
+static int fake_perms_stat(vfs_handle_struct *handle, connection_struct *conn, const char *fname, SMB_STRUCT_STAT *sbuf)
+{
+ int ret = -1;
+
+ ret = SMB_VFS_NEXT_STAT(handle, conn, fname, sbuf);
+ if (ret == 0) {
+ extern struct current_user current_user;
+
+ if (S_ISDIR(sbuf->st_mode)) {
+ sbuf->st_mode = S_IFDIR | S_IRWXU;
+ } else {
+ sbuf->st_mode = S_IRWXU;
+ }
+ sbuf->st_uid = current_user.uid;
+ sbuf->st_gid = current_user.gid;
+ }
+
+ return ret;
+}
+
+static int fake_perms_fstat(vfs_handle_struct *handle, files_struct *fsp, int fd, SMB_STRUCT_STAT *sbuf)
+{
+ int ret = -1;
+
+ ret = SMB_VFS_NEXT_FSTAT(handle, fsp, fd, sbuf);
+ if (ret == 0) {
+ extern struct current_user current_user;
+
+ if (S_ISDIR(sbuf->st_mode)) {
+ sbuf->st_mode = S_IFDIR | S_IRWXU;
+ } else {
+ sbuf->st_mode = S_IRWXU;
+ }
+ sbuf->st_uid = current_user.uid;
+ sbuf->st_gid = current_user.gid;
+ }
+ return ret;
+}
+
+/* VFS operations structure */
+
+static vfs_op_tuple fake_perms_ops[] = {
+ {SMB_VFS_OP(fake_perms_stat), SMB_VFS_OP_STAT, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(fake_perms_fstat), SMB_VFS_OP_FSTAT, SMB_VFS_LAYER_TRANSPARENT},
+
+ {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+NTSTATUS vfs_fake_perms_init(void)
+{
+ return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fake_perms", fake_perms_ops);
+}
diff --git a/source/modules/vfs_netatalk.c b/source/modules/vfs_netatalk.c
new file mode 100644
index 00000000000..ae6286e292d
--- /dev/null
+++ b/source/modules/vfs_netatalk.c
@@ -0,0 +1,401 @@
+/*
+ * AppleTalk VFS module for Samba-3.x
+ *
+ * Copyright (C) Alexei Kotovich, 2002
+ * Copyright (C) Stefan (metze) Metzmacher, 2003
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_VFS
+
+#define APPLEDOUBLE ".AppleDouble"
+#define ADOUBLEMODE 0777
+
+/* atalk functions */
+
+static int atalk_build_paths(TALLOC_CTX *ctx, const char *path,
+ const char *fname, char **adbl_path, char **orig_path,
+ SMB_STRUCT_STAT *adbl_info, SMB_STRUCT_STAT *orig_info);
+
+static int atalk_unlink_file(const char *path);
+
+static int atalk_get_path_ptr(char *path)
+{
+ int i = 0;
+ int ptr = 0;
+
+ for (i = 0; path[i]; i ++) {
+ if (path[i] == '/')
+ ptr = i;
+ /* get out some 'spam';) from win32's file name */
+ else if (path[i] == ':') {
+ path[i] = '\0';
+ break;
+ }
+ }
+
+ return ptr;
+}
+
+static int atalk_build_paths(TALLOC_CTX *ctx, const char *path, const char *fname,
+ char **adbl_path, char **orig_path,
+ SMB_STRUCT_STAT *adbl_info, SMB_STRUCT_STAT *orig_info)
+{
+ int ptr0 = 0;
+ int ptr1 = 0;
+ char *dname = 0;
+ char *name = 0;
+
+ if (!ctx || !path || !fname || !adbl_path || !orig_path ||
+ !adbl_info || !orig_info)
+ return -1;
+#if 0
+ DEBUG(3, ("ATALK: PATH: %s[%s]\n", path, fname));
+#endif
+ if (strstr(path, APPLEDOUBLE) || strstr(fname, APPLEDOUBLE)) {
+ DEBUG(3, ("ATALK: path %s[%s] already contains %s\n", path, fname, APPLEDOUBLE));
+ return -1;
+ }
+
+ if (fname[0] == '.') ptr0 ++;
+ if (fname[1] == '/') ptr0 ++;
+
+ *orig_path = talloc_asprintf(ctx, "%s/%s", path, &fname[ptr0]);
+
+ /* get pointer to last '/' */
+ ptr1 = atalk_get_path_ptr(*orig_path);
+
+ sys_lstat(*orig_path, orig_info);
+
+ if (S_ISDIR(orig_info->st_mode)) {
+ *adbl_path = talloc_asprintf(ctx, "%s/%s/%s/",
+ path, &fname[ptr0], APPLEDOUBLE);
+ } else {
+ dname = talloc_strdup(ctx, *orig_path);
+ dname[ptr1] = '\0';
+ name = *orig_path;
+ *adbl_path = talloc_asprintf(ctx, "%s/%s/%s",
+ dname, APPLEDOUBLE, &name[ptr1 + 1]);
+ }
+#if 0
+ DEBUG(3, ("ATALK: DEBUG:\n%s\n%s\n", *orig_path, *adbl_path));
+#endif
+ sys_lstat(*adbl_path, adbl_info);
+ return 0;
+}
+
+static int atalk_unlink_file(const char *path)
+{
+ int ret = 0;
+
+ become_root();
+ ret = unlink(path);
+ unbecome_root();
+
+ return ret;
+}
+
+static void atalk_add_to_list(name_compare_entry **list)
+{
+ int i, count = 0;
+ name_compare_entry *new_list = 0;
+ name_compare_entry *cur_list = 0;
+
+ cur_list = *list;
+
+ if (cur_list) {
+ for (i = 0, count = 0; cur_list[i].name; i ++, count ++) {
+ if (strstr(cur_list[i].name, APPLEDOUBLE))
+ return;
+ }
+ }
+
+ if (!(new_list = calloc(1,
+ (count == 0 ? 1 : count + 1) * sizeof(name_compare_entry))))
+ return;
+
+ for (i = 0; i < count; i ++) {
+ new_list[i].name = strdup(cur_list[i].name);
+ new_list[i].is_wild = cur_list[i].is_wild;
+ }
+
+ new_list[i].name = strdup(APPLEDOUBLE);
+ new_list[i].is_wild = False;
+
+ free_namearray(*list);
+
+ *list = new_list;
+ new_list = 0;
+ cur_list = 0;
+}
+
+static void atalk_rrmdir(TALLOC_CTX *ctx, char *path)
+{
+ char *dpath;
+ struct dirent *dent = 0;
+ DIR *dir;
+
+ if (!path) return;
+
+ dir = opendir(path);
+ if (!dir) return;
+
+ while (NULL != (dent = readdir(dir))) {
+ if (strcmp(dent->d_name, ".") == 0 ||
+ strcmp(dent->d_name, "..") == 0)
+ continue;
+ if (!(dpath = talloc_asprintf(ctx, "%s/%s",
+ path, dent->d_name)))
+ continue;
+ atalk_unlink_file(dpath);
+ }
+
+ closedir(dir);
+}
+
+/* Disk operations */
+
+/* Directory operations */
+
+DIR *atalk_opendir(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *fname)
+{
+ DIR *ret = 0;
+
+ ret = SMB_VFS_NEXT_OPENDIR(handle, conn, fname);
+
+ /*
+ * when we try to perform delete operation upon file which has fork
+ * in ./.AppleDouble and this directory wasn't hidden by Samba,
+ * MS Windows explorer causes the error: "Cannot find the specified file"
+ * There is some workaround to avoid this situation, i.e. if
+ * connection has not .AppleDouble entry in either veto or hide
+ * list then it would be nice to add one.
+ */
+
+ atalk_add_to_list(&conn->hide_list);
+ atalk_add_to_list(&conn->veto_list);
+
+ return ret;
+}
+
+static int atalk_rmdir(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path)
+{
+ BOOL add = False;
+ TALLOC_CTX *ctx = 0;
+ char *dpath;
+
+ if (!conn || !conn->origpath || !path) goto exit_rmdir;
+
+ /* due to there is no way to change bDeleteVetoFiles variable
+ * from this module, gotta use talloc stuff..
+ */
+
+ strstr(path, APPLEDOUBLE) ? (add = False) : (add = True);
+
+ if (!(ctx = talloc_init("remove_directory")))
+ goto exit_rmdir;
+
+ if (!(dpath = talloc_asprintf(ctx, "%s/%s%s",
+ conn->origpath, path, add ? "/"APPLEDOUBLE : "")))
+ goto exit_rmdir;
+
+ atalk_rrmdir(ctx, dpath);
+
+exit_rmdir:
+ talloc_destroy(ctx);
+ return SMB_VFS_NEXT_RMDIR(handle, conn, path);
+}
+
+/* File operations */
+
+static int atalk_rename(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *old, const char *new)
+{
+ int ret = 0;
+ char *adbl_path = 0;
+ char *orig_path = 0;
+ SMB_STRUCT_STAT adbl_info;
+ SMB_STRUCT_STAT orig_info;
+ TALLOC_CTX *ctx;
+
+ ret = SMB_VFS_NEXT_RENAME(handle, conn, old, new);
+
+ if (!conn || !old) return ret;
+
+ if (!(ctx = talloc_init("rename_file")))
+ return ret;
+
+ if (atalk_build_paths(ctx, conn->origpath, old, &adbl_path, &orig_path,
+ &adbl_info, &orig_info) != 0)
+ return ret;
+
+ if (S_ISDIR(orig_info.st_mode) || S_ISREG(orig_info.st_mode)) {
+ DEBUG(3, ("ATALK: %s has passed..\n", adbl_path));
+ goto exit_rename;
+ }
+
+ atalk_unlink_file(adbl_path);
+
+exit_rename:
+ talloc_destroy(ctx);
+ return ret;
+}
+
+static int atalk_unlink(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path)
+{
+ int ret = 0, i;
+ char *adbl_path = 0;
+ char *orig_path = 0;
+ SMB_STRUCT_STAT adbl_info;
+ SMB_STRUCT_STAT orig_info;
+ TALLOC_CTX *ctx;
+
+ ret = SMB_VFS_NEXT_UNLINK(handle, conn, path);
+
+ if (!conn || !path) return ret;
+
+ /* no .AppleDouble sync if veto or hide list is empty,
+ * otherwise "Cannot find the specified file" error will be caused
+ */
+
+ if (!conn->veto_list) return ret;
+ if (!conn->hide_list) return ret;
+
+ for (i = 0; conn->veto_list[i].name; i ++) {
+ if (strstr(conn->veto_list[i].name, APPLEDOUBLE))
+ break;
+ }
+
+ if (!conn->veto_list[i].name) {
+ for (i = 0; conn->hide_list[i].name; i ++) {
+ if (strstr(conn->hide_list[i].name, APPLEDOUBLE))
+ break;
+ else {
+ DEBUG(3, ("ATALK: %s is not hidden, skipped..\n",
+ APPLEDOUBLE));
+ return ret;
+ }
+ }
+ }
+
+ if (!(ctx = talloc_init("unlink_file")))
+ return ret;
+
+ if (atalk_build_paths(ctx, conn->origpath, path, &adbl_path, &orig_path,
+ &adbl_info, &orig_info) != 0)
+ return ret;
+
+ if (S_ISDIR(orig_info.st_mode) || S_ISREG(orig_info.st_mode)) {
+ DEBUG(3, ("ATALK: %s has passed..\n", adbl_path));
+ goto exit_unlink;
+ }
+
+ atalk_unlink_file(adbl_path);
+
+exit_unlink:
+ talloc_destroy(ctx);
+ return ret;
+}
+
+static int atalk_chmod(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, mode_t mode)
+{
+ int ret = 0;
+ char *adbl_path = 0;
+ char *orig_path = 0;
+ SMB_STRUCT_STAT adbl_info;
+ SMB_STRUCT_STAT orig_info;
+ TALLOC_CTX *ctx;
+
+ ret = SMB_VFS_NEXT_CHMOD(handle, conn, path, mode);
+
+ if (!conn || !path) return ret;
+
+ if (!(ctx = talloc_init("chmod_file")))
+ return ret;
+
+ if (atalk_build_paths(ctx, conn->origpath, path, &adbl_path, &orig_path,
+ &adbl_info, &orig_info) != 0)
+ return ret;
+
+ if (!S_ISDIR(orig_info.st_mode) && !S_ISREG(orig_info.st_mode)) {
+ DEBUG(3, ("ATALK: %s has passed..\n", orig_path));
+ goto exit_chmod;
+ }
+
+ chmod(adbl_path, ADOUBLEMODE);
+
+exit_chmod:
+ talloc_destroy(ctx);
+ return ret;
+}
+
+static int atalk_chown(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, uid_t uid, gid_t gid)
+{
+ int ret = 0;
+ char *adbl_path = 0;
+ char *orig_path = 0;
+ SMB_STRUCT_STAT adbl_info;
+ SMB_STRUCT_STAT orig_info;
+ TALLOC_CTX *ctx;
+
+ ret = SMB_VFS_NEXT_CHOWN(handle, conn, path, uid, gid);
+
+ if (!conn || !path) return ret;
+
+ if (!(ctx = talloc_init("chown_file")))
+ return ret;
+
+ if (atalk_build_paths(ctx, conn->origpath, path, &adbl_path, &orig_path,
+ &adbl_info, &orig_info) != 0)
+ return ret;
+
+ if (!S_ISDIR(orig_info.st_mode) && !S_ISREG(orig_info.st_mode)) {
+ DEBUG(3, ("ATALK: %s has passed..\n", orig_path));
+ goto exit_chown;
+ }
+
+ chown(adbl_path, uid, gid);
+
+exit_chown:
+ talloc_destroy(ctx);
+ return ret;
+}
+
+static vfs_op_tuple atalk_ops[] = {
+
+ /* Directory operations */
+
+ {SMB_VFS_OP(atalk_opendir), SMB_VFS_OP_OPENDIR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(atalk_rmdir), SMB_VFS_OP_RMDIR, SMB_VFS_LAYER_TRANSPARENT},
+
+ /* File operations */
+
+ {SMB_VFS_OP(atalk_rename), SMB_VFS_OP_RENAME, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(atalk_unlink), SMB_VFS_OP_UNLINK, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(atalk_chmod), SMB_VFS_OP_CHMOD, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(atalk_chown), SMB_VFS_OP_CHOWN, SMB_VFS_LAYER_TRANSPARENT},
+
+ /* Finish VFS operations definition */
+
+ {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+NTSTATUS vfs_netatalk_init(void)
+{
+ return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "netatalk", atalk_ops);
+}
diff --git a/source/modules/vfs_readonly.c b/source/modules/vfs_readonly.c
new file mode 100644
index 00000000000..ee9e40c2fca
--- /dev/null
+++ b/source/modules/vfs_readonly.c
@@ -0,0 +1,98 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ VFS module to perform read-only limitation based on a time period
+ Copyright (C) Alexander Bokovoy 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ This work was sponsored by Optifacio Software Services, Inc.
+*/
+
+#include "includes.h"
+#include "getdate.h"
+
+/*
+ This module performs a read-only limitation for specified share
+ (or all of them if it is loaded in a [global] section) based on period
+ definition in smb.conf. You can stack this module multiple times under
+ different names to get multiple limit intervals.
+
+ The module uses get_date() function from coreutils' date utility to parse
+ specified dates according to date(1) rules. Look into info page for date(1)
+ to understand the syntax.
+
+ The module accepts one parameter:
+
+ readonly: period = "begin date","end date"
+
+ where "begin date" and "end date" are mandatory and should comply with date(1)
+ syntax for date strings.
+
+ Example:
+
+ readonly: period = "today 14:00","today 15:00"
+
+ Default:
+
+ readonly: period = "today 0:0:0","tomorrow 0:0:0"
+
+ The default covers whole day thus making the share readonly
+
+ */
+
+#define MODULE_NAME "readonly"
+static int readonly_connect(vfs_handle_struct *handle,
+ connection_struct *conn,
+ const char *service,
+ const char *user)
+{
+ const char *period_def[] = {"today 0:0:0", "tomorrow 0:0:0"};
+
+ const char **period = lp_parm_string_list(SNUM(handle->conn),
+ (handle->param ? handle->param : MODULE_NAME),
+ "period", period_def);
+
+ if (period && period[0] && period[1]) {
+ time_t current_time = time(NULL);
+ time_t begin_period = get_date(period[0], &current_time);
+ time_t end_period = get_date(period[1], &current_time);
+
+ if ((current_time >= begin_period) && (current_time <= end_period)) {
+ conn->read_only = True;
+ }
+
+ return SMB_VFS_NEXT_CONNECT(handle, conn, service, user);
+
+ } else {
+
+ return 1;
+
+ }
+}
+
+
+/* VFS operations structure */
+
+static vfs_op_tuple readonly_op_tuples[] = {
+ /* Disk operations */
+ {SMB_VFS_OP(readonly_connect), SMB_VFS_OP_CONNECT, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+NTSTATUS vfs_readonly_init(void)
+{
+ return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, MODULE_NAME, readonly_op_tuples);
+}
diff --git a/source/modules/vfs_recycle.c b/source/modules/vfs_recycle.c
new file mode 100644
index 00000000000..1cb1cb327b4
--- /dev/null
+++ b/source/modules/vfs_recycle.c
@@ -0,0 +1,498 @@
+/*
+ * Recycle bin VFS module for Samba.
+ *
+ * Copyright (C) 2001, Brandon Stone, Amherst College, <bbstone@amherst.edu>.
+ * Copyright (C) 2002, Jeremy Allison - modified to make a VFS module.
+ * Copyright (C) 2002, Alexander Bokovoy - cascaded VFS adoption,
+ * Copyright (C) 2002, Juergen Hasch - added some options.
+ * Copyright (C) 2002, Simo Sorce
+ * Copyright (C) 2002, Stefan (metze) Metzmacher
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+#define ALLOC_CHECK(ptr, label) do { if ((ptr) == NULL) { DEBUG(0, ("recycle.bin: out of memory!\n")); errno = ENOMEM; goto label; } } while(0)
+
+static int vfs_recycle_debug_level = DBGC_VFS;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS vfs_recycle_debug_level
+
+static int recycle_connect(vfs_handle_struct *handle, connection_struct *conn, const char *service, const char *user);
+static void recycle_disconnect(vfs_handle_struct *handle, connection_struct *conn);
+static int recycle_unlink(vfs_handle_struct *handle, connection_struct *conn, const char *name);
+
+static vfs_op_tuple recycle_ops[] = {
+
+ /* Disk operations */
+ {SMB_VFS_OP(recycle_connect), SMB_VFS_OP_CONNECT, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(recycle_disconnect), SMB_VFS_OP_DISCONNECT, SMB_VFS_LAYER_TRANSPARENT},
+
+ /* File operations */
+ {SMB_VFS_OP(recycle_unlink), SMB_VFS_OP_UNLINK, SMB_VFS_LAYER_TRANSPARENT},
+
+ {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+static int recycle_connect(vfs_handle_struct *handle, connection_struct *conn, const char *service, const char *user)
+{
+ DEBUG(10,("recycle_connect() connect to service[%s] as user[%s].\n",
+ service,user));
+
+ return SMB_VFS_NEXT_CONNECT(handle, conn, service, user);
+}
+
+static void recycle_disconnect(vfs_handle_struct *handle, connection_struct *conn)
+{
+ DEBUG(10,("recycle_disconnect() connect to service[%s].\n",
+ lp_servicename(SNUM(conn))));
+
+ SMB_VFS_NEXT_DISCONNECT(handle, conn);
+}
+
+static const char *recycle_repository(vfs_handle_struct *handle)
+{
+ const char *tmp_str = NULL;
+
+
+ tmp_str = lp_parm_const_string(SNUM(handle->conn), "recycle", "repository",".recycle");
+
+ DEBUG(10, ("recycle: repository = %s\n", tmp_str));
+
+ return tmp_str;
+}
+
+static BOOL recycle_keep_dir_tree(vfs_handle_struct *handle)
+{
+ BOOL ret;
+
+ ret = lp_parm_bool(SNUM(handle->conn), "recycle", "keeptree", False);
+
+ DEBUG(10, ("recycle_bin: keeptree = %s\n", ret?"True":"False"));
+
+ return ret;
+}
+
+static BOOL recycle_versions(vfs_handle_struct *handle)
+{
+ BOOL ret;
+
+ ret = lp_parm_bool(SNUM(handle->conn), "recycle", "versions", False);
+
+ DEBUG(10, ("recycle: versions = %s\n", ret?"True":"False"));
+
+ return ret;
+}
+
+static BOOL recycle_touch(vfs_handle_struct *handle)
+{
+ BOOL ret;
+
+ ret = lp_parm_bool(SNUM(handle->conn), "recycle", "touch", False);
+
+ DEBUG(10, ("recycle: touch = %s\n", ret?"True":"False"));
+
+ return ret;
+}
+
+static const char **recycle_exclude(vfs_handle_struct *handle)
+{
+ const char **tmp_lp;
+
+ tmp_lp = lp_parm_string_list(SNUM(handle->conn), "recycle", "exclude", NULL);
+
+ DEBUG(10, ("recycle: exclude = %s ...\n", tmp_lp?*tmp_lp:""));
+
+ return tmp_lp;
+}
+
+static const char **recycle_exclude_dir(vfs_handle_struct *handle)
+{
+ const char **tmp_lp;
+
+ tmp_lp = lp_parm_string_list(SNUM(handle->conn), "recycle", "exclude_dir", NULL);
+
+ DEBUG(10, ("recycle: exclude_dir = %s ...\n", tmp_lp?*tmp_lp:""));
+
+ return tmp_lp;
+}
+
+static const char **recycle_noversions(vfs_handle_struct *handle)
+{
+ const char **tmp_lp;
+
+ tmp_lp = lp_parm_string_list(SNUM(handle->conn), "recycle", "noversions", NULL);
+
+ DEBUG(10, ("recycle: noversions = %s\n", tmp_lp?*tmp_lp:""));
+
+ return tmp_lp;
+}
+
+static int recycle_maxsize(vfs_handle_struct *handle)
+{
+ int maxsize;
+
+ maxsize = lp_parm_int(SNUM(handle->conn), "recycle", "maxsize", -1);
+
+ DEBUG(10, ("recycle: maxsize = %d\n", maxsize));
+
+ return maxsize;
+}
+
+static BOOL recycle_directory_exist(vfs_handle_struct *handle, const char *dname)
+{
+ SMB_STRUCT_STAT st;
+
+ if (SMB_VFS_NEXT_STAT(handle, handle->conn, dname, &st) == 0) {
+ if (S_ISDIR(st.st_mode)) {
+ return True;
+ }
+ }
+
+ return False;
+}
+
+static BOOL recycle_file_exist(vfs_handle_struct *handle, const char *fname)
+{
+ SMB_STRUCT_STAT st;
+
+ if (SMB_VFS_NEXT_STAT(handle, handle->conn, fname, &st) == 0) {
+ if (S_ISREG(st.st_mode)) {
+ return True;
+ }
+ }
+
+ return False;
+}
+
+/**
+ * Return file size
+ * @param conn connection
+ * @param fname file name
+ * @return size in bytes
+ **/
+static SMB_OFF_T recycle_get_file_size(vfs_handle_struct *handle, const char *fname)
+{
+ SMB_STRUCT_STAT st;
+
+ if (SMB_VFS_NEXT_STAT(handle, handle->conn, fname, &st) != 0) {
+ DEBUG(0,("recycle: stat for %s returned %s\n", fname, strerror(errno)));
+ return (SMB_OFF_T)0;
+ }
+
+ return(st.st_size);
+}
+
+/**
+ * Create directory tree
+ * @param conn connection
+ * @param dname Directory tree to be created
+ * @return Returns True for success
+ **/
+static BOOL recycle_create_dir(vfs_handle_struct *handle, const char *dname)
+{
+ int len;
+ mode_t mode;
+ char *new_dir = NULL;
+ char *tmp_str = NULL;
+ char *token;
+ char *tok_str;
+ BOOL ret = False;
+
+ mode = S_IRUSR | S_IWUSR | S_IXUSR;
+
+ tmp_str = strdup(dname);
+ ALLOC_CHECK(tmp_str, done);
+ tok_str = tmp_str;
+
+ len = strlen(dname)+1;
+ new_dir = (char *)malloc(len + 1);
+ ALLOC_CHECK(new_dir, done);
+ *new_dir = '\0';
+
+ /* Create directory tree if neccessary */
+ for(token = strtok(tok_str, "/"); token; token = strtok(NULL, "/")) {
+ safe_strcat(new_dir, token, len);
+ if (recycle_directory_exist(handle, new_dir))
+ DEBUG(10, ("recycle: dir %s already exists\n", new_dir));
+ else {
+ DEBUG(5, ("recycle: creating new dir %s\n", new_dir));
+ if (SMB_VFS_NEXT_MKDIR(handle, handle->conn, new_dir, mode) != 0) {
+ DEBUG(1,("recycle: mkdir failed for %s with error: %s\n", new_dir, strerror(errno)));
+ ret = False;
+ goto done;
+ }
+ }
+ safe_strcat(new_dir, "/", len);
+ }
+
+ ret = True;
+done:
+ SAFE_FREE(tmp_str);
+ SAFE_FREE(new_dir);
+ return ret;
+}
+
+/**
+ * Check if needle is contained exactly in haystack
+ * @param haystack list of parameters separated by delimimiter character
+ * @param needle string to be matched exactly to haystack
+ * @return True if found
+ **/
+static BOOL checkparam(const char **haystack_list, const char *needle)
+{
+ int i;
+
+ if (haystack_list == NULL || haystack_list[0] == NULL ||
+ *haystack_list[0] == '\0' || needle == NULL || *needle == '\0') {
+ return False;
+ }
+
+ for(i=0; haystack_list[i] ; i++) {
+ if(strequal(haystack_list[i], needle)) {
+ return True;
+ }
+ }
+
+ return False;
+}
+
+/**
+ * Check if needle is contained in haystack, * and ? patterns are resolved
+ * @param haystack list of parameters separated by delimimiter character
+ * @param needle string to be matched exectly to haystack including pattern matching
+ * @return True if found
+ **/
+static BOOL matchparam(const char **haystack_list, const char *needle)
+{
+ int i;
+
+ if (haystack_list == NULL || haystack_list[0] == NULL ||
+ *haystack_list[0] == '\0' || needle == NULL || *needle == '\0') {
+ return False;
+ }
+
+ for(i=0; haystack_list[i] ; i++) {
+ if(!unix_wild_match(haystack_list[i], needle)) {
+ return True;
+ }
+ }
+
+ return False;
+}
+
+/**
+ * Touch access date
+ **/
+static void recycle_do_touch(vfs_handle_struct *handle, const char *fname)
+{
+ SMB_STRUCT_STAT st;
+ struct utimbuf tb;
+ time_t currtime;
+
+ if (SMB_VFS_NEXT_STAT(handle, handle->conn, fname, &st) != 0) {
+ DEBUG(0,("recycle: stat for %s returned %s\n", fname, strerror(errno)));
+ return;
+ }
+ currtime = time(&currtime);
+ tb.actime = currtime;
+ tb.modtime = st.st_mtime;
+
+ if (SMB_VFS_NEXT_UTIME(handle, handle->conn, fname, &tb) == -1 ) {
+ DEBUG(0, ("recycle: touching %s failed, reason = %s\n", fname, strerror(errno)));
+ }
+}
+
+/**
+ * Check if file should be recycled
+ **/
+static int recycle_unlink(vfs_handle_struct *handle, connection_struct *conn, const char *file_name)
+{
+ char *path_name = NULL;
+ char *temp_name = NULL;
+ char *final_name = NULL;
+ const char *base;
+ char *repository = NULL;
+ int i = 1;
+ int maxsize;
+ SMB_OFF_T file_size; /* space_avail; */
+ BOOL exist;
+ int rc = -1;
+
+ repository = alloc_sub_conn(conn, recycle_repository(handle));
+ ALLOC_CHECK(repository, done);
+ /* shouldn't we allow absolute path names here? --metze */
+ trim_char(repository, '/', '/');
+
+ if(!repository || *(repository) == '\0') {
+ DEBUG(3, ("recycle: repository path not set, purging %s...\n", file_name));
+ rc = SMB_VFS_NEXT_UNLINK(handle, conn, file_name);
+ goto done;
+ }
+
+ /* we don't recycle the recycle bin... */
+ if (strncmp(file_name, repository, strlen(repository)) == 0) {
+ DEBUG(3, ("recycle: File is within recycling bin, unlinking ...\n"));
+ rc = SMB_VFS_NEXT_UNLINK(handle, conn, file_name);
+ goto done;
+ }
+
+ file_size = recycle_get_file_size(handle, file_name);
+ /* it is wrong to purge filenames only because they are empty imho
+ * --- simo
+ *
+ if(fsize == 0) {
+ DEBUG(3, ("recycle: File %s is empty, purging...\n", file_name));
+ rc = SMB_VFS_NEXT_UNLINK(handle,conn,file_name);
+ goto done;
+ }
+ */
+
+ /* FIXME: this is wrong, we should check the hole size of the recycle bin is
+ * not greater then maxsize, not the size of the single file, also it is better
+ * to remove older files
+ */
+ maxsize = recycle_maxsize(handle);
+ if(maxsize > 0 && file_size > maxsize) {
+ DEBUG(3, ("recycle: File %s exceeds maximum recycle size, purging... \n", file_name));
+ rc = SMB_VFS_NEXT_UNLINK(handle, conn, file_name);
+ goto done;
+ }
+
+ /* FIXME: this is wrong: moving files with rename does not change the disk space
+ * allocation
+ *
+ space_avail = SMB_VFS_NEXT_DISK_FREE(handle, conn, ".", True, &bsize, &dfree, &dsize) * 1024L;
+ DEBUG(5, ("space_avail = %Lu, file_size = %Lu\n", space_avail, file_size));
+ if(space_avail < file_size) {
+ DEBUG(3, ("recycle: Not enough diskspace, purging file %s\n", file_name));
+ rc = SMB_VFS_NEXT_UNLINK(handle, conn, file_name);
+ goto done;
+ }
+ */
+
+ /* extract filename and path */
+ base = strrchr(file_name, '/');
+ if (base == NULL) {
+ base = file_name;
+ path_name = strdup("/");
+ ALLOC_CHECK(path_name, done);
+ }
+ else {
+ path_name = strdup(file_name);
+ ALLOC_CHECK(path_name, done);
+ path_name[base - file_name] = '\0';
+ base++;
+ }
+
+ DEBUG(10, ("recycle: fname = %s\n", file_name)); /* original filename with path */
+ DEBUG(10, ("recycle: fpath = %s\n", path_name)); /* original path */
+ DEBUG(10, ("recycle: base = %s\n", base)); /* filename without path */
+
+ if (matchparam(recycle_exclude(handle), base)) {
+ DEBUG(3, ("recycle: file %s is excluded \n", base));
+ rc = SMB_VFS_NEXT_UNLINK(handle, conn, file_name);
+ goto done;
+ }
+
+ /* FIXME: this check will fail if we have more than one level of directories,
+ * we shoud check for every level 1, 1/2, 1/2/3, 1/2/3/4 ....
+ * ---simo
+ */
+ if (checkparam(recycle_exclude_dir(handle), path_name)) {
+ DEBUG(3, ("recycle: directory %s is excluded \n", path_name));
+ rc = SMB_VFS_NEXT_UNLINK(handle, conn, file_name);
+ goto done;
+ }
+
+ if (recycle_keep_dir_tree(handle) == True) {
+ asprintf(&temp_name, "%s/%s", repository, path_name);
+ } else {
+ temp_name = strdup(repository);
+ }
+ ALLOC_CHECK(temp_name, done);
+
+ exist = recycle_directory_exist(handle, temp_name);
+ if (exist) {
+ DEBUG(10, ("recycle: Directory already exists\n"));
+ } else {
+ DEBUG(10, ("recycle: Creating directory %s\n", temp_name));
+ if (recycle_create_dir(handle, temp_name) == False) {
+ DEBUG(3, ("recycle: Could not create directory, purging %s...\n", file_name));
+ rc = SMB_VFS_NEXT_UNLINK(handle, conn, file_name);
+ goto done;
+ }
+ }
+
+ asprintf(&final_name, "%s/%s", temp_name, base);
+ ALLOC_CHECK(final_name, done);
+ DEBUG(10, ("recycle: recycled file name: %s\n", final_name)); /* new filename with path */
+
+ /* check if we should delete file from recycle bin */
+ if (recycle_file_exist(handle, final_name)) {
+ if (recycle_versions(handle) == False || matchparam(recycle_noversions(handle), base) == True) {
+ DEBUG(3, ("recycle: Removing old file %s from recycle bin\n", final_name));
+ if (SMB_VFS_NEXT_UNLINK(handle, conn, final_name) != 0) {
+ DEBUG(1, ("recycle: Error deleting old file: %s\n", strerror(errno)));
+ }
+ }
+ }
+
+ /* rename file we move to recycle bin */
+ i = 1;
+ while (recycle_file_exist(handle, final_name)) {
+ SAFE_FREE(final_name);
+ asprintf(&final_name, "%s/Copy #%d of %s", temp_name, i++, base);
+ }
+
+ DEBUG(10, ("recycle: Moving %s to %s\n", file_name, final_name));
+ rc = SMB_VFS_NEXT_RENAME(handle, conn, file_name, final_name);
+ if (rc != 0) {
+ DEBUG(3, ("recycle: Move error %d (%s), purging file %s (%s)\n", errno, strerror(errno), file_name, final_name));
+ rc = SMB_VFS_NEXT_UNLINK(handle, conn, file_name);
+ goto done;
+ }
+
+ /* touch access date of moved file */
+ if (recycle_touch(handle) == True )
+ recycle_do_touch(handle, final_name);
+
+done:
+ SAFE_FREE(path_name);
+ SAFE_FREE(temp_name);
+ SAFE_FREE(final_name);
+ SAFE_FREE(repository);
+ return rc;
+}
+
+NTSTATUS vfs_recycle_init(void)
+{
+ NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "recycle", recycle_ops);
+
+ if (!NT_STATUS_IS_OK(ret))
+ return ret;
+
+ vfs_recycle_debug_level = debug_add_class("recycle");
+ if (vfs_recycle_debug_level == -1) {
+ vfs_recycle_debug_level = DBGC_VFS;
+ DEBUG(0, ("vfs_recycle: Couldn't register custom debugging class!\n"));
+ } else {
+ DEBUG(10, ("vfs_recycle: Debug class number of 'recycle': %d\n", vfs_recycle_debug_level));
+ }
+
+ return ret;
+}
diff --git a/source/modules/weird.c b/source/modules/weird.c
new file mode 100644
index 00000000000..444853f3831
--- /dev/null
+++ b/source/modules/weird.c
@@ -0,0 +1,131 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba module with developer tools
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Jelmer Vernooij 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static struct {
+ char from;
+ char *to;
+ int len;
+} weird_table[] = {
+ {'q', "^q^", 3},
+ {'Q', "^Q^", 3},
+ {0, NULL}
+};
+
+static size_t weird_pull(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+ while (*inbytesleft >= 1 && *outbytesleft >= 2) {
+ int i;
+ int done = 0;
+ for (i=0;weird_table[i].from;i++) {
+ if (strncmp((*inbuf),
+ weird_table[i].to,
+ weird_table[i].len) == 0) {
+ if (*inbytesleft < weird_table[i].len) {
+ DEBUG(0,("ERROR: truncated weird string\n"));
+ /* smb_panic("weird_pull"); */
+
+ } else {
+ (*outbuf)[0] = weird_table[i].from;
+ (*outbuf)[1] = 0;
+ (*inbytesleft) -= weird_table[i].len;
+ (*outbytesleft) -= 2;
+ (*inbuf) += weird_table[i].len;
+ (*outbuf) += 2;
+ done = 1;
+ break;
+ }
+ }
+ }
+ if (done) continue;
+ (*outbuf)[0] = (*inbuf)[0];
+ (*outbuf)[1] = 0;
+ (*inbytesleft) -= 1;
+ (*outbytesleft) -= 2;
+ (*inbuf) += 1;
+ (*outbuf) += 2;
+ }
+
+ if (*inbytesleft > 0) {
+ errno = E2BIG;
+ return -1;
+ }
+
+ return 0;
+}
+
+static size_t weird_push(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+ int ir_count=0;
+
+ while (*inbytesleft >= 2 && *outbytesleft >= 1) {
+ int i;
+ int done=0;
+ for (i=0;weird_table[i].from;i++) {
+ if ((*inbuf)[0] == weird_table[i].from &&
+ (*inbuf)[1] == 0) {
+ if (*outbytesleft < weird_table[i].len) {
+ DEBUG(0,("No room for weird character\n"));
+ /* smb_panic("weird_push"); */
+ } else {
+ memcpy(*outbuf, weird_table[i].to,
+ weird_table[i].len);
+ (*inbytesleft) -= 2;
+ (*outbytesleft) -= weird_table[i].len;
+ (*inbuf) += 2;
+ (*outbuf) += weird_table[i].len;
+ done = 1;
+ break;
+ }
+ }
+ }
+ if (done) continue;
+
+ (*outbuf)[0] = (*inbuf)[0];
+ if ((*inbuf)[1]) ir_count++;
+ (*inbytesleft) -= 2;
+ (*outbytesleft) -= 1;
+ (*inbuf) += 2;
+ (*outbuf) += 1;
+ }
+
+ if (*inbytesleft == 1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (*inbytesleft > 1) {
+ errno = E2BIG;
+ return -1;
+ }
+
+ return ir_count;
+}
+
+struct charset_functions weird_functions = {"WEIRD", weird_pull, weird_push};
+
+NTSTATUS charset_weird_init(void)
+{
+ return smb_register_charset(&weird_functions);
+}
diff --git a/source/msdfs/msdfs.c b/source/msdfs/msdfs.c
new file mode 100644
index 00000000000..2ac7bda1754
--- /dev/null
+++ b/source/msdfs/msdfs.c
@@ -0,0 +1,1017 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ MSDfs services for Samba
+ Copyright (C) Shirish Kalele 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+
+extern fstring local_machine;
+extern uint32 global_client_caps;
+
+/**********************************************************************
+ Parse the pathname of the form \hostname\service\reqpath
+ into the dfs_path structure
+ **********************************************************************/
+
+static BOOL parse_dfs_path(char* pathname, struct dfs_path* pdp)
+{
+ pstring pathname_local;
+ char* p,*temp;
+
+ pstrcpy(pathname_local,pathname);
+ p = temp = pathname_local;
+
+ ZERO_STRUCTP(pdp);
+
+ trim_char(temp,'\\','\\');
+ DEBUG(10,("temp in parse_dfs_path: .%s. after trimming \\'s\n",temp));
+
+ /* now tokenize */
+ /* parse out hostname */
+ p = strchr_m(temp,'\\');
+ if(p == NULL)
+ return False;
+ *p = '\0';
+ pstrcpy(pdp->hostname,temp);
+ DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
+
+ /* parse out servicename */
+ temp = p+1;
+ p = strchr_m(temp,'\\');
+ if(p == NULL) {
+ pstrcpy(pdp->servicename,temp);
+ pdp->reqpath[0] = '\0';
+ return True;
+ }
+ *p = '\0';
+ pstrcpy(pdp->servicename,temp);
+ DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
+
+ /* rest is reqpath */
+ check_path_syntax(pdp->reqpath, p+1);
+
+ DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
+ return True;
+}
+
+/**********************************************************************
+ Parse the pathname of the form /hostname/service/reqpath
+ into the dfs_path structure
+ **********************************************************************/
+
+static BOOL parse_processed_dfs_path(char* pathname, struct dfs_path* pdp)
+{
+ pstring pathname_local;
+ char* p,*temp;
+
+ pstrcpy(pathname_local,pathname);
+ p = temp = pathname_local;
+
+ ZERO_STRUCTP(pdp);
+
+ trim_char(temp,'/','/');
+ DEBUG(10,("temp in parse_processed_dfs_path: .%s. after trimming \\'s\n",temp));
+
+ /* now tokenize */
+ /* parse out hostname */
+ p = strchr_m(temp,'/');
+ if(p == NULL)
+ return False;
+ *p = '\0';
+ pstrcpy(pdp->hostname,temp);
+ DEBUG(10,("parse_processed_dfs_path: hostname: %s\n",pdp->hostname));
+
+ /* parse out servicename */
+ temp = p+1;
+ p = strchr_m(temp,'/');
+ if(p == NULL) {
+ pstrcpy(pdp->servicename,temp);
+ pdp->reqpath[0] = '\0';
+ return True;
+ }
+ *p = '\0';
+ pstrcpy(pdp->servicename,temp);
+ DEBUG(10,("parse_processed_dfs_path: servicename: %s\n",pdp->servicename));
+
+ /* rest is reqpath */
+ check_path_syntax(pdp->reqpath, p+1);
+
+ DEBUG(10,("parse_processed_dfs_path: rest of the path: %s\n",pdp->reqpath));
+ return True;
+}
+
+/********************************************************
+ Fake up a connection struct for the VFS layer.
+ Note this CHANGES CWD !!!! JRA.
+*********************************************************/
+
+static BOOL create_conn_struct( connection_struct *conn, int snum, char *path)
+{
+ ZERO_STRUCTP(conn);
+ conn->service = snum;
+ conn->connectpath = path;
+ pstring_sub(conn->connectpath , "%S", lp_servicename(snum));
+
+ /* needed for smbd_vfs_init() */
+
+ if ( (conn->mem_ctx=talloc_init("connection_struct")) == NULL ) {
+ DEBUG(0,("talloc_init(connection_struct) failed!\n"));
+ return False;
+ }
+
+ if (!smbd_vfs_init(conn)) {
+ DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
+ talloc_destroy( conn->mem_ctx );
+ return False;
+ }
+
+ /*
+ * Windows seems to insist on doing trans2getdfsreferral() calls on the IPC$
+ * share as the anonymous user. If we try to chdir as that user we will
+ * fail.... WTF ? JRA.
+ */
+
+ if (vfs_ChDir(conn,conn->connectpath) != 0) {
+ DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. Error was %s\n",
+ conn->connectpath, strerror(errno) ));
+ talloc_destroy( conn->mem_ctx );
+ return False;
+ }
+ return True;
+}
+
+
+/**********************************************************************
+ Parse the contents of a symlink to verify if it is an msdfs referral
+ A valid referral is of the form: msdfs:server1\share1,server2\share2
+ **********************************************************************/
+
+static BOOL parse_symlink(char* buf,struct referral** preflist,
+ int* refcount)
+{
+ pstring temp;
+ char* prot;
+ char* alt_path[MAX_REFERRAL_COUNT];
+ int count=0, i;
+ struct referral* reflist;
+
+ pstrcpy(temp,buf);
+
+ prot = strtok(temp,":");
+
+ if (!strequal(prot, "msdfs"))
+ return False;
+
+ /* No referral list requested. Just yes/no. */
+ if (!preflist)
+ return True;
+
+ /* parse out the alternate paths */
+ while(((alt_path[count] = strtok(NULL,",")) != NULL) && count<MAX_REFERRAL_COUNT)
+ count++;
+
+ DEBUG(10,("parse_symlink: count=%d\n", count));
+
+ reflist = *preflist = (struct referral*) malloc(count * sizeof(struct referral));
+ if(reflist == NULL) {
+ DEBUG(0,("parse_symlink: Malloc failed!\n"));
+ return False;
+ }
+
+ for(i=0;i<count;i++) {
+ char *p;
+
+ /* replace all /'s in the alternate path by a \ */
+ for(p = alt_path[i]; *p && ((p = strchr_m(p,'/'))!=NULL); p++) {
+ *p = '\\';
+ }
+
+ /* Remove leading '\\'s */
+ p = alt_path[i];
+ while (*p && (*p == '\\')) {
+ p++;
+ }
+
+ pstrcpy(reflist[i].alternate_path, "\\");
+ pstrcat(reflist[i].alternate_path, p);
+ reflist[i].proximity = 0;
+ reflist[i].ttl = REFERRAL_TTL;
+ DEBUG(10, ("parse_symlink: Created alt path: %s\n", reflist[i].alternate_path));
+ }
+
+ if(refcount)
+ *refcount = count;
+
+ return True;
+}
+
+/**********************************************************************
+ Returns true if the unix path is a valid msdfs symlink
+ **********************************************************************/
+
+BOOL is_msdfs_link(connection_struct* conn, char * path,
+ struct referral** reflistp, int* refcnt,
+ SMB_STRUCT_STAT *sbufp)
+{
+ SMB_STRUCT_STAT st;
+ pstring referral;
+ int referral_len = 0;
+
+ if (!path || !conn)
+ return False;
+
+ if (sbufp == NULL)
+ sbufp = &st;
+
+ if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
+ DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
+ return False;
+ }
+
+ if (S_ISLNK(sbufp->st_mode)) {
+ /* open the link and read it */
+ referral_len = SMB_VFS_READLINK(conn, path, referral,
+ sizeof(pstring));
+ if (referral_len == -1) {
+ DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n", path, strerror(errno)));
+ return False;
+ }
+
+ referral[referral_len] = '\0';
+ DEBUG(5,("is_msdfs_link: %s -> %s\n",path,referral));
+ if (parse_symlink(referral, reflistp, refcnt))
+ return True;
+ }
+ return False;
+}
+
+/*****************************************************************
+ Used by other functions to decide if a dfs path is remote,
+and to get the list of referred locations for that remote path.
+
+findfirst_flag: For findfirsts, dfs links themselves are not
+redirected, but paths beyond the links are. For normal smb calls,
+even dfs links need to be redirected.
+
+self_referralp: clients expect a dfs referral for the same share when
+they request referrals for dfs roots on a server.
+
+consumedcntp: how much of the dfs path is being redirected. the client
+should try the remaining path on the redirected server.
+
+*****************************************************************/
+
+static BOOL resolve_dfs_path(pstring dfspath, struct dfs_path* dp,
+ connection_struct* conn,
+ BOOL findfirst_flag,
+ struct referral** reflistpp, int* refcntp,
+ BOOL* self_referralp, int* consumedcntp)
+{
+ pstring localpath;
+ int consumed_level = 1;
+ char *p;
+ BOOL bad_path = False;
+ SMB_STRUCT_STAT sbuf;
+ pstring reqpath;
+
+ if (!dp || !conn) {
+ DEBUG(1,("resolve_dfs_path: NULL dfs_path* or NULL connection_struct*!\n"));
+ return False;
+ }
+
+ if (dp->reqpath[0] == '\0') {
+ if (self_referralp) {
+ DEBUG(6,("resolve_dfs_path: self-referral. returning False\n"));
+ *self_referralp = True;
+ }
+ return False;
+ }
+
+ DEBUG(10,("resolve_dfs_path: Conn path = %s req_path = %s\n", conn->connectpath, dp->reqpath));
+
+ unix_convert(dp->reqpath,conn,0,&bad_path,&sbuf);
+ /* JRA... should we strlower the last component here.... ? */
+ pstrcpy(localpath, dp->reqpath);
+
+ /* check if need to redirect */
+ if (is_msdfs_link(conn, localpath, reflistpp, refcntp, NULL)) {
+ if (findfirst_flag) {
+ DEBUG(6,("resolve_dfs_path (FindFirst) No redirection "
+ "for dfs link %s.\n", dfspath));
+ return False;
+ } else {
+ DEBUG(6,("resolve_dfs_path: %s resolves to a valid Dfs link.\n",
+ dfspath));
+ if (consumedcntp)
+ *consumedcntp = strlen(dfspath);
+ return True;
+ }
+ }
+
+ /* redirect if any component in the path is a link */
+ pstrcpy(reqpath, dp->reqpath);
+ p = strrchr_m(reqpath, '/');
+ while (p) {
+ *p = '\0';
+ pstrcpy(localpath, reqpath);
+ if (is_msdfs_link(conn, localpath, reflistpp, refcntp, NULL)) {
+ DEBUG(4, ("resolve_dfs_path: Redirecting %s because parent %s is dfs link\n", dfspath, localpath));
+
+ /* To find the path consumed, we truncate the original
+ DFS pathname passed to use to remove the last
+ component. The length of the resulting string is
+ the path consumed
+ */
+ if (consumedcntp) {
+ char *q;
+ pstring buf;
+ pstrcpy(buf, dfspath);
+ trim_char(buf, '\0', '\\');
+ for (; consumed_level; consumed_level--) {
+ q = strrchr_m(buf, '\\');
+ if (q)
+ *q = 0;
+ }
+ *consumedcntp = strlen(buf);
+ DEBUG(10, ("resolve_dfs_path: Path consumed: %s (%d)\n", buf, *consumedcntp));
+ }
+
+ return True;
+ }
+ p = strrchr_m(reqpath, '/');
+ consumed_level++;
+ }
+
+ return False;
+}
+
+/*****************************************************************
+ Decides if a dfs pathname should be redirected or not.
+ If not, the pathname is converted to a tcon-relative local unix path
+*****************************************************************/
+
+BOOL dfs_redirect(pstring pathname, connection_struct* conn,
+ BOOL findfirst_flag)
+{
+ struct dfs_path dp;
+
+ if (!conn || !pathname)
+ return False;
+
+ parse_processed_dfs_path(pathname, &dp);
+
+ /* if dfs pathname for a non-dfs share, convert to tcon-relative
+ path and return false */
+ if (!lp_msdfs_root(SNUM(conn))) {
+ pstrcpy(pathname, dp.reqpath);
+ return False;
+ }
+
+ if (!strequal(dp.servicename, lp_servicename(SNUM(conn)) ))
+ return False;
+
+ if (resolve_dfs_path(pathname, &dp, conn, findfirst_flag,
+ NULL, NULL, NULL, NULL)) {
+ DEBUG(3,("dfs_redirect: Redirecting %s\n", pathname));
+ return True;
+ } else {
+ DEBUG(3,("dfs_redirect: Not redirecting %s.\n", pathname));
+
+ /* Form non-dfs tcon-relative path */
+ pstrcpy(pathname, dp.reqpath);
+ DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n",
+ pathname));
+ return False;
+ }
+
+ /* never reached */
+}
+
+/**********************************************************************
+ Return a self referral.
+**********************************************************************/
+
+static BOOL self_ref(char *pathname, struct junction_map *jucn,
+ int *consumedcntp, BOOL *self_referralp)
+{
+ struct referral *ref;
+
+ if (self_referralp != NULL)
+ *self_referralp = True;
+
+ jucn->referral_count = 1;
+ if((ref = (struct referral*) malloc(sizeof(struct referral))) == NULL) {
+ DEBUG(0,("self_ref: malloc failed for referral\n"));
+ return False;
+ }
+
+ pstrcpy(ref->alternate_path,pathname);
+ ref->proximity = 0;
+ ref->ttl = REFERRAL_TTL;
+ jucn->referral_list = ref;
+ if (consumedcntp)
+ *consumedcntp = strlen(pathname);
+
+ return True;
+}
+
+/**********************************************************************
+ Gets valid referrals for a dfs path and fills up the
+ junction_map structure
+**********************************************************************/
+
+BOOL get_referred_path(char *pathname, struct junction_map *jucn,
+ int *consumedcntp, BOOL *self_referralp)
+{
+ struct dfs_path dp;
+
+ struct connection_struct conns;
+ struct connection_struct* conn = &conns;
+ pstring conn_path;
+ int snum;
+ BOOL ret = False;
+ BOOL self_referral = False;
+
+ if (!pathname || !jucn)
+ return False;
+
+ ZERO_STRUCT(conns);
+
+ if (self_referralp)
+ *self_referralp = False;
+ else
+ self_referralp = &self_referral;
+
+ parse_dfs_path(pathname, &dp);
+
+ /* Verify hostname in path */
+ if (local_machine && (!strequal(local_machine, dp.hostname))) {
+ /* Hostname mismatch, check if one of our IP addresses */
+ if (!ismyip(*interpret_addr2(dp.hostname))) {
+ DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
+ dp.hostname, pathname));
+ return False;
+ }
+ }
+
+ pstrcpy(jucn->service_name, dp.servicename);
+ pstrcpy(jucn->volume_name, dp.reqpath);
+
+ /* Verify the share is a dfs root */
+ snum = lp_servicenumber(jucn->service_name);
+ if(snum < 0) {
+ if ((snum = find_service(jucn->service_name)) < 0)
+ return False;
+ }
+
+ if (!lp_msdfs_root(snum)) {
+ DEBUG(3,("get_referred_path: .%s. in dfs path %s is not a dfs root.\n",
+ dp.servicename, pathname));
+ goto out;
+ }
+
+ /*
+ * Self referrals are tested with a anonymous IPC connection and
+ * a GET_DFS_REFERRAL call to \\server\share. (which means dp.reqpath[0] points
+ * to an empty string). create_conn_struct cd's into the directory and will
+ * fail if it cannot (as the anonymous user). Cope with this.
+ */
+
+ if (dp.reqpath[0] == '\0') {
+ return self_ref(pathname, jucn, consumedcntp, self_referralp);
+ }
+
+ pstrcpy(conn_path, lp_pathname(snum));
+ if (!create_conn_struct(conn, snum, conn_path))
+ return False;
+
+ if (*lp_msdfs_proxy(snum) != '\0') {
+ struct referral* ref;
+ jucn->referral_count = 1;
+ if ((ref = (struct referral*) malloc(sizeof(struct referral))) == NULL) {
+ DEBUG(0, ("malloc failed for referral\n"));
+ goto out;
+ }
+
+ pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
+ if (dp.reqpath[0] != '\0')
+ pstrcat(ref->alternate_path, dp.reqpath);
+ ref->proximity = 0;
+ ref->ttl = REFERRAL_TTL;
+ jucn->referral_list = ref;
+ if (consumedcntp)
+ *consumedcntp = strlen(pathname);
+ ret = True;
+ goto out;
+ }
+
+ /* If not remote & not a self referral, return False */
+ if (!resolve_dfs_path(pathname, &dp, conn, False,
+ &jucn->referral_list, &jucn->referral_count,
+ self_referralp, consumedcntp)) {
+ if (!*self_referralp) {
+ DEBUG(3,("get_referred_path: No valid referrals for path %s\n", pathname));
+ goto out;
+ }
+ }
+
+ /* if self_referral, fill up the junction map */
+ if (*self_referralp) {
+ if (self_ref(pathname, jucn, consumedcntp, self_referralp) == False) {
+ goto out;
+ }
+ }
+
+ ret = True;
+
+out:
+ if (conn->mem_ctx)
+ talloc_destroy( conn->mem_ctx );
+
+ return ret;
+}
+
+static int setup_ver2_dfs_referral(char* pathname, char** ppdata,
+ struct junction_map* junction,
+ int consumedcnt,
+ BOOL self_referral)
+{
+ char* pdata = *ppdata;
+
+ unsigned char uni_requestedpath[1024];
+ int uni_reqpathoffset1,uni_reqpathoffset2;
+ int uni_curroffset;
+ int requestedpathlen=0;
+ int offset;
+ int reply_size = 0;
+ int i=0;
+
+ DEBUG(10,("setting up version2 referral\nRequested path:\n"));
+
+ requestedpathlen = rpcstr_push(uni_requestedpath, pathname, -1,
+ STR_TERMINATE);
+
+ dump_data(10, (const char *) uni_requestedpath,requestedpathlen);
+
+ DEBUG(10,("ref count = %u\n",junction->referral_count));
+
+ uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
+ VERSION2_REFERRAL_SIZE * junction->referral_count;
+
+ uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
+
+ uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
+
+ reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
+ 2 * requestedpathlen;
+ DEBUG(10,("reply_size: %u\n",reply_size));
+
+ /* add up the unicode lengths of all the referral paths */
+ for(i=0;i<junction->referral_count;i++) {
+ DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
+ reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
+ }
+
+ DEBUG(10,("reply_size = %u\n",reply_size));
+ /* add the unexplained 0x16 bytes */
+ reply_size += 0x16;
+
+ pdata = Realloc(pdata,reply_size);
+ if(pdata == NULL) {
+ DEBUG(0,("malloc failed for Realloc!\n"));
+ return -1;
+ } else
+ *ppdata = pdata;
+
+ /* copy in the dfs requested paths.. required for offset calculations */
+ memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
+ memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
+
+ /* create the header */
+ SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
+ SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
+ if(self_referral)
+ SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
+ else
+ SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
+
+ offset = 8;
+ /* add the referral elements */
+ for(i=0;i<junction->referral_count;i++) {
+ struct referral* ref = &junction->referral_list[i];
+ int unilen;
+
+ SSVAL(pdata,offset,2); /* version 2 */
+ SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
+ if(self_referral)
+ SSVAL(pdata,offset+4,1);
+ else
+ SSVAL(pdata,offset+4,0);
+ SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
+ SIVAL(pdata,offset+8,ref->proximity);
+ SIVAL(pdata,offset+12,ref->ttl);
+
+ SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
+ SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
+ /* copy referred path into current offset */
+ unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
+ -1, STR_UNICODE);
+
+ SSVAL(pdata,offset+20,uni_curroffset-offset);
+
+ uni_curroffset += unilen;
+ offset += VERSION2_REFERRAL_SIZE;
+ }
+ /* add in the unexplained 22 (0x16) bytes at the end */
+ memset(pdata+uni_curroffset,'\0',0x16);
+ return reply_size;
+}
+
+static int setup_ver3_dfs_referral(char* pathname, char** ppdata,
+ struct junction_map* junction,
+ int consumedcnt,
+ BOOL self_referral)
+{
+ char* pdata = *ppdata;
+
+ unsigned char uni_reqpath[1024];
+ int uni_reqpathoffset1, uni_reqpathoffset2;
+ int uni_curroffset;
+ int reply_size = 0;
+
+ int reqpathlen = 0;
+ int offset,i=0;
+
+ DEBUG(10,("setting up version3 referral\n"));
+
+ reqpathlen = rpcstr_push(uni_reqpath, pathname, -1, STR_TERMINATE);
+
+ dump_data(10, (char *) uni_reqpath,reqpathlen);
+
+ uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
+ uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
+ reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
+
+ for(i=0;i<junction->referral_count;i++) {
+ DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
+ reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
+ }
+
+ pdata = Realloc(pdata,reply_size);
+ if(pdata == NULL) {
+ DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
+ return -1;
+ } else
+ *ppdata = pdata;
+
+ /* create the header */
+ SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
+ SSVAL(pdata,2,junction->referral_count); /* number of referral */
+ if(self_referral)
+ SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
+ else
+ SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
+
+ /* copy in the reqpaths */
+ memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
+ memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
+
+ offset = 8;
+ for(i=0;i<junction->referral_count;i++) {
+ struct referral* ref = &(junction->referral_list[i]);
+ int unilen;
+
+ SSVAL(pdata,offset,3); /* version 3 */
+ SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
+ if(self_referral)
+ SSVAL(pdata,offset+4,1);
+ else
+ SSVAL(pdata,offset+4,0);
+
+ SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
+ SIVAL(pdata,offset+8,ref->ttl);
+
+ SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
+ SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
+ /* copy referred path into current offset */
+ unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
+ -1, STR_UNICODE | STR_TERMINATE);
+ SSVAL(pdata,offset+16,uni_curroffset-offset);
+ /* copy 0x10 bytes of 00's in the ServiceSite GUID */
+ memset(pdata+offset+18,'\0',16);
+
+ uni_curroffset += unilen;
+ offset += VERSION3_REFERRAL_SIZE;
+ }
+ return reply_size;
+}
+
+/******************************************************************
+ * Set up the Dfs referral for the dfs pathname
+ ******************************************************************/
+
+int setup_dfs_referral(connection_struct *orig_conn, char *pathname, int max_referral_level, char** ppdata)
+{
+ struct junction_map junction;
+ int consumedcnt;
+ BOOL self_referral = False;
+ pstring buf;
+ int reply_size = 0;
+ char *pathnamep = pathname;
+
+ ZERO_STRUCT(junction);
+
+ /* get the junction entry */
+ if (!pathnamep)
+ return -1;
+
+ /* Trim pathname sent by client so it begins with only one backslash.
+ Two backslashes confuse some dfs clients
+ */
+ while (pathnamep[0] == '\\' && pathnamep[1] == '\\')
+ pathnamep++;
+
+ pstrcpy(buf, pathnamep);
+ /* The following call can change cwd. */
+ if (!get_referred_path(buf, &junction, &consumedcnt, &self_referral)) {
+ vfs_ChDir(orig_conn,orig_conn->connectpath);
+ return -1;
+ }
+ vfs_ChDir(orig_conn,orig_conn->connectpath);
+
+ if (!self_referral) {
+ pathnamep[consumedcnt] = '\0';
+
+ if( DEBUGLVL( 3 ) ) {
+ int i=0;
+ dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep);
+ for(i=0;i<junction.referral_count;i++)
+ dbgtext(" %s",junction.referral_list[i].alternate_path);
+ dbgtext(".\n");
+ }
+ }
+
+ /* create the referral depeding on version */
+ DEBUG(10,("max_referral_level :%d\n",max_referral_level));
+ if(max_referral_level<2 || max_referral_level>3)
+ max_referral_level = 2;
+
+ switch(max_referral_level) {
+ case 2:
+ reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction,
+ consumedcnt, self_referral);
+ SAFE_FREE(junction.referral_list);
+ break;
+ case 3:
+ reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction,
+ consumedcnt, self_referral);
+ SAFE_FREE(junction.referral_list);
+ break;
+ default:
+ DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
+ return -1;
+ }
+
+ DEBUG(10,("DFS Referral pdata:\n"));
+ dump_data(10,*ppdata,reply_size);
+ return reply_size;
+}
+
+/**********************************************************************
+ The following functions are called by the NETDFS RPC pipe functions
+ **********************************************************************/
+
+/**********************************************************************
+ Creates a junction structure from a Dfs pathname
+ **********************************************************************/
+BOOL create_junction(char* pathname, struct junction_map* jucn)
+{
+ struct dfs_path dp;
+
+ parse_dfs_path(pathname,&dp);
+
+ /* check if path is dfs : validate first token */
+ if (local_machine && (!strequal(local_machine,dp.hostname))) {
+
+ /* Hostname mismatch, check if one of our IP addresses */
+ if (!ismyip(*interpret_addr2(dp.hostname))) {
+ DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
+ dp.hostname, pathname));
+ return False;
+ }
+ }
+
+ /* Check for a non-DFS share */
+ if(!lp_msdfs_root(lp_servicenumber(dp.servicename))) {
+ DEBUG(4,("create_junction: %s is not an msdfs root.\n",
+ dp.servicename));
+ return False;
+ }
+
+ pstrcpy(jucn->service_name,dp.servicename);
+ pstrcpy(jucn->volume_name,dp.reqpath);
+ return True;
+}
+
+/**********************************************************************
+ Forms a valid Unix pathname from the junction
+ **********************************************************************/
+
+static BOOL junction_to_local_path(struct junction_map* jucn, char* path,
+ int max_pathlen, connection_struct *conn)
+{
+ int snum;
+ pstring conn_path;
+
+ if(!path || !jucn)
+ return False;
+
+ snum = lp_servicenumber(jucn->service_name);
+ if(snum < 0)
+ return False;
+
+ safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
+ safe_strcat(path, "/", max_pathlen-1);
+ safe_strcat(path, jucn->volume_name, max_pathlen-1);
+
+ pstrcpy(conn_path, lp_pathname(snum));
+ if (!create_conn_struct(conn, snum, conn_path))
+ return False;
+
+ return True;
+}
+
+BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists)
+{
+ pstring path;
+ pstring msdfs_link;
+ connection_struct conns;
+ connection_struct *conn = &conns;
+ int i=0;
+ BOOL insert_comma = False;
+ BOOL ret = False;
+
+ if(!junction_to_local_path(jucn, path, sizeof(path), conn))
+ return False;
+
+ /* form the msdfs_link contents */
+ pstrcpy(msdfs_link, "msdfs:");
+ for(i=0; i<jucn->referral_count; i++) {
+ char* refpath = jucn->referral_list[i].alternate_path;
+
+ trim_char(refpath, '\\', '\\');
+ if(*refpath == '\0') {
+ if (i == 0)
+ insert_comma = False;
+ continue;
+ }
+ if (i > 0 && insert_comma)
+ pstrcat(msdfs_link, ",");
+
+ pstrcat(msdfs_link, refpath);
+ if (!insert_comma)
+ insert_comma = True;
+
+ }
+
+ DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n", path, msdfs_link));
+
+ if(exists)
+ if(SMB_VFS_UNLINK(conn,path)!=0)
+ goto out;
+
+ if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
+ DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n",
+ path, msdfs_link, strerror(errno)));
+ goto out;
+ }
+
+
+ ret = True;
+
+out:
+ talloc_destroy( conn->mem_ctx );
+ return ret;
+}
+
+BOOL remove_msdfs_link(struct junction_map* jucn)
+{
+ pstring path;
+ connection_struct conns;
+ connection_struct *conn = &conns;
+ BOOL ret = False;
+
+ if( junction_to_local_path(jucn, path, sizeof(path), conn) ) {
+ if( SMB_VFS_UNLINK(conn, path) == 0 )
+ ret = True;
+
+ talloc_destroy( conn->mem_ctx );
+ }
+
+ return ret;
+}
+
+static BOOL form_junctions(int snum, struct junction_map* jucn, int* jn_count)
+{
+ int cnt = *jn_count;
+ DIR *dirp;
+ char* dname;
+ pstring connect_path;
+ char* service_name = lp_servicename(snum);
+ connection_struct conns;
+ connection_struct *conn = &conns;
+ struct referral *ref = NULL;
+ BOOL ret = False;
+
+ pstrcpy(connect_path,lp_pathname(snum));
+
+ if(*connect_path == '\0')
+ return False;
+
+ /*
+ * Fake up a connection struct for the VFS layer.
+ */
+
+ if (!create_conn_struct(conn, snum, connect_path))
+ return False;
+
+ /* form a junction for the msdfs root - convention
+ DO NOT REMOVE THIS: NT clients will not work with us
+ if this is not present
+ */
+ pstrcpy(jucn[cnt].service_name, service_name);
+ jucn[cnt].volume_name[0] = '\0';
+ jucn[cnt].referral_count = 1;
+
+ ref = jucn[cnt].referral_list
+ = (struct referral*) malloc(sizeof(struct referral));
+ if (jucn[cnt].referral_list == NULL) {
+ DEBUG(0, ("Malloc failed!\n"));
+ goto out;
+ }
+
+ ref->proximity = 0;
+ ref->ttl = REFERRAL_TTL;
+ if (*lp_msdfs_proxy(snum) != '\0') {
+ pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
+ *jn_count = ++cnt;
+ ret = True;
+ goto out;
+ }
+
+ slprintf(ref->alternate_path, sizeof(pstring)-1,
+ "\\\\%s\\%s", local_machine, service_name);
+ cnt++;
+
+ /* Now enumerate all dfs links */
+ dirp = SMB_VFS_OPENDIR(conn, ".");
+ if(!dirp)
+ goto out;
+
+ while((dname = vfs_readdirname(conn, dirp)) != NULL) {
+ if (is_msdfs_link(conn, dname, &(jucn[cnt].referral_list),
+ &(jucn[cnt].referral_count), NULL)) {
+ pstrcpy(jucn[cnt].service_name, service_name);
+ pstrcpy(jucn[cnt].volume_name, dname);
+ cnt++;
+ }
+ }
+
+ SMB_VFS_CLOSEDIR(conn,dirp);
+ *jn_count = cnt;
+out:
+ talloc_destroy(conn->mem_ctx);
+ return ret;
+}
+
+int enum_msdfs_links(struct junction_map* jucn)
+{
+ int i=0;
+ int jn_count = 0;
+
+ if(!lp_host_msdfs())
+ return 0;
+
+ for(i=0;i < lp_numservices();i++) {
+ if(lp_msdfs_root(i))
+ form_junctions(i,jucn,&jn_count);
+ }
+ return jn_count;
+}
diff --git a/source/nmbd/.cvsignore b/source/nmbd/.cvsignore
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/source/nmbd/.cvsignore
diff --git a/source/nmbd/asyncdns.c b/source/nmbd/asyncdns.c
new file mode 100644
index 00000000000..653cb97fbb0
--- /dev/null
+++ b/source/nmbd/asyncdns.c
@@ -0,0 +1,353 @@
+/*
+ Unix SMB/CIFS implementation.
+ a async DNS handler
+ Copyright (C) Andrew Tridgell 1997-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+/***************************************************************************
+ Add a DNS result to the name cache.
+****************************************************************************/
+
+static struct name_record *add_dns_result(struct nmb_name *question, struct in_addr addr)
+{
+ int name_type = question->name_type;
+ unstring qname;
+
+ pull_ascii_nstring(qname, sizeof(qname), question->name);
+
+ if (!addr.s_addr) {
+ /* add the fail to WINS cache of names. give it 1 hour in the cache */
+ DEBUG(3,("add_dns_result: Negative DNS answer for %s\n", qname));
+ (void)add_name_to_subnet( wins_server_subnet, qname, name_type,
+ NB_ACTIVE, 60*60, DNSFAIL_NAME, 1, &addr );
+ return( NULL );
+ }
+
+ /* add it to our WINS cache of names. give it 2 hours in the cache */
+ DEBUG(3,("add_dns_result: DNS gave answer for %s of %s\n", qname, inet_ntoa(addr)));
+
+ return( add_name_to_subnet( wins_server_subnet, qname, name_type,
+ NB_ACTIVE, 2*60*60, DNS_NAME, 1, &addr ) );
+}
+
+#ifndef SYNC_DNS
+
+static int fd_in = -1, fd_out = -1;
+static pid_t child_pid = -1;
+static int in_dns;
+
+/* this is the structure that is passed between the parent and child */
+struct query_record {
+ struct nmb_name name;
+ struct in_addr result;
+};
+
+/* a queue of pending requests waiting to be sent to the DNS child */
+static struct packet_struct *dns_queue;
+
+/* the packet currently being processed by the dns child */
+static struct packet_struct *dns_current;
+
+
+/***************************************************************************
+ return the fd used to gather async dns replies. This is added to the select
+ loop
+ ****************************************************************************/
+
+int asyncdns_fd(void)
+{
+ return fd_in;
+}
+
+/***************************************************************************
+ handle DNS queries arriving from the parent
+ ****************************************************************************/
+static void asyncdns_process(void)
+{
+ struct query_record r;
+ unstring qname;
+
+ DEBUGLEVEL = -1;
+
+ while (1) {
+ if (read_data(fd_in, (char *)&r, sizeof(r)) != sizeof(r))
+ break;
+
+ pull_ascii_nstring( qname, sizeof(qname), r.name.name);
+ r.result.s_addr = interpret_addr(qname);
+
+ if (write_data(fd_out, (char *)&r, sizeof(r)) != sizeof(r))
+ break;
+ }
+
+ _exit(0);
+}
+
+/**************************************************************************** **
+ catch a sigterm (in the child process - the parent has a different handler
+ see nmbd.c for details).
+ We need a separate term handler here so we don't release any
+ names that our parent is going to release, or overwrite a
+ WINS db that our parent is going to write.
+ **************************************************************************** */
+
+static void sig_term(int sig)
+{
+ _exit(0);
+}
+
+/***************************************************************************
+ Called by the parent process when it receives a SIGTERM - also kills the
+ child so we don't get child async dns processes lying around, causing trouble.
+ ****************************************************************************/
+
+void kill_async_dns_child(void)
+{
+ if (child_pid > 0) {
+ kill(child_pid, SIGTERM);
+ child_pid = -1;
+ }
+}
+
+/***************************************************************************
+ create a child process to handle DNS lookups
+ ****************************************************************************/
+void start_async_dns(void)
+{
+ int fd1[2], fd2[2];
+
+ CatchChild();
+
+ if (pipe(fd1) || pipe(fd2)) {
+ DEBUG(0,("can't create asyncdns pipes\n"));
+ return;
+ }
+
+ child_pid = sys_fork();
+
+ if (child_pid) {
+ fd_in = fd1[0];
+ fd_out = fd2[1];
+ close(fd1[1]);
+ close(fd2[0]);
+ DEBUG(0,("started asyncdns process %d\n", (int)child_pid));
+ return;
+ }
+
+ fd_in = fd2[0];
+ fd_out = fd1[1];
+
+ CatchSignal(SIGUSR2, SIG_IGN);
+ CatchSignal(SIGUSR1, SIG_IGN);
+ CatchSignal(SIGHUP, SIG_IGN);
+ CatchSignal(SIGTERM, SIGNAL_CAST sig_term );
+
+ asyncdns_process();
+}
+
+
+/***************************************************************************
+check if a particular name is already being queried
+ ****************************************************************************/
+static BOOL query_current(struct query_record *r)
+{
+ return dns_current &&
+ nmb_name_equal(&r->name,
+ &dns_current->packet.nmb.question.question_name);
+}
+
+
+/***************************************************************************
+ write a query to the child process
+ ****************************************************************************/
+static BOOL write_child(struct packet_struct *p)
+{
+ struct query_record r;
+
+ r.name = p->packet.nmb.question.question_name;
+
+ return write_data(fd_out, (char *)&r, sizeof(r)) == sizeof(r);
+}
+
+/***************************************************************************
+ check the DNS queue
+ ****************************************************************************/
+void run_dns_queue(void)
+{
+ struct query_record r;
+ struct packet_struct *p, *p2;
+ struct name_record *namerec;
+ int size;
+
+ if (fd_in == -1)
+ return;
+
+ /* Allow SIGTERM to kill us. */
+ BlockSignals(False, SIGTERM);
+
+ if (!process_exists(child_pid)) {
+ close(fd_in);
+ start_async_dns();
+ }
+
+ if ((size=read_data(fd_in, (char *)&r, sizeof(r))) != sizeof(r)) {
+ if (size) {
+ DEBUG(0,("Incomplete DNS answer from child!\n"));
+ fd_in = -1;
+ }
+ BlockSignals(True, SIGTERM);
+ return;
+ }
+
+ BlockSignals(True, SIGTERM);
+
+ namerec = add_dns_result(&r.name, r.result);
+
+ if (dns_current) {
+ if (query_current(&r)) {
+ DEBUG(3,("DNS calling send_wins_name_query_response\n"));
+ in_dns = 1;
+ if(namerec == NULL)
+ send_wins_name_query_response(NAM_ERR, dns_current, NULL);
+ else
+ send_wins_name_query_response(0,dns_current,namerec);
+ in_dns = 0;
+ }
+
+ dns_current->locked = False;
+ free_packet(dns_current);
+ dns_current = NULL;
+ }
+
+ /* loop over the whole dns queue looking for entries that
+ match the result we just got */
+ for (p = dns_queue; p;) {
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+
+ if (nmb_name_equal(question, &r.name)) {
+ DEBUG(3,("DNS calling send_wins_name_query_response\n"));
+ in_dns = 1;
+ if(namerec == NULL)
+ send_wins_name_query_response(NAM_ERR, p, NULL);
+ else
+ send_wins_name_query_response(0,p,namerec);
+ in_dns = 0;
+ p->locked = False;
+
+ if (p->prev)
+ p->prev->next = p->next;
+ else
+ dns_queue = p->next;
+ if (p->next)
+ p->next->prev = p->prev;
+ p2 = p->next;
+ free_packet(p);
+ p = p2;
+ } else {
+ p = p->next;
+ }
+ }
+
+ if (dns_queue) {
+ dns_current = dns_queue;
+ dns_queue = dns_queue->next;
+ if (dns_queue)
+ dns_queue->prev = NULL;
+ dns_current->next = NULL;
+
+ if (!write_child(dns_current)) {
+ DEBUG(3,("failed to send DNS query to child!\n"));
+ return;
+ }
+ }
+}
+
+/***************************************************************************
+queue a DNS query
+ ****************************************************************************/
+
+BOOL queue_dns_query(struct packet_struct *p,struct nmb_name *question,
+ struct name_record **n)
+{
+ if (in_dns || fd_in == -1)
+ return False;
+
+ if (!dns_current) {
+ if (!write_child(p)) {
+ DEBUG(3,("failed to send DNS query to child!\n"));
+ return False;
+ }
+ dns_current = p;
+ p->locked = True;
+ } else {
+ p->locked = True;
+ p->next = dns_queue;
+ p->prev = NULL;
+ if (p->next)
+ p->next->prev = p;
+ dns_queue = p;
+ }
+
+ DEBUG(3,("added DNS query for %s\n", nmb_namestr(question)));
+ return True;
+}
+
+#else
+
+
+/***************************************************************************
+ we use this when we can't do async DNS lookups
+ ****************************************************************************/
+
+BOOL queue_dns_query(struct packet_struct *p,struct nmb_name *question,
+ struct name_record **n)
+{
+ struct in_addr dns_ip;
+ unstring qname;
+
+ pull_ascii_nstring(qname, question->name);
+
+ DEBUG(3,("DNS search for %s - ", nmb_namestr(question)));
+
+ /* Unblock TERM signal so we can be killed in DNS lookup. */
+ BlockSignals(False, SIGTERM);
+
+ dns_ip.s_addr = interpret_addr(qname);
+
+ /* Re-block TERM signal. */
+ BlockSignals(True, SIGTERM);
+
+ *n = add_dns_result(question, dns_ip);
+ if(*n == NULL)
+ send_wins_name_query_response(NAM_ERR, p, NULL);
+ else
+ send_wins_name_query_response(0, p, *n);
+ return False;
+}
+
+/***************************************************************************
+ With sync dns there is no child to kill on SIGTERM.
+ ****************************************************************************/
+
+void kill_async_dns_child(void)
+{
+ return;
+}
+#endif
diff --git a/source/nmbd/nmbd.c b/source/nmbd/nmbd.c
new file mode 100644
index 00000000000..880de7f91bf
--- /dev/null
+++ b/source/nmbd/nmbd.c
@@ -0,0 +1,771 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Jeremy Allison 1997-2002
+ Copyright (C) Jelmer Vernooij 2002,2003 (Conversion to popt)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+
+int ClientNMB = -1;
+int ClientDGRAM = -1;
+int global_nmb_port = -1;
+
+extern BOOL global_in_nmbd;
+
+/* are we running as a daemon ? */
+static BOOL is_daemon;
+
+/* fork or run in foreground ? */
+static BOOL Fork = True;
+
+/* log to standard output ? */
+static BOOL log_stdout;
+
+/* have we found LanMan clients yet? */
+BOOL found_lm_clients = False;
+
+/* what server type are we currently */
+
+time_t StartupTime = 0;
+
+/**************************************************************************** **
+ Handle a SIGTERM in band.
+ **************************************************************************** */
+
+static void terminate(void)
+{
+ DEBUG(0,("Got SIGTERM: going down...\n"));
+
+ /* Write out wins.dat file if samba is a WINS server */
+ wins_write_database(False);
+
+ /* Remove all SELF registered names from WINS */
+ release_wins_names();
+
+ /* Announce all server entries as 0 time-to-live, 0 type. */
+ announce_my_servers_removed();
+
+ /* If there was an async dns child - kill it. */
+ kill_async_dns_child();
+
+ exit(0);
+}
+
+/**************************************************************************** **
+ Handle a SHUTDOWN message from smbcontrol.
+ **************************************************************************** */
+
+static void nmbd_terminate(int msg_type, pid_t src, void *buf, size_t len)
+{
+ terminate();
+}
+
+/**************************************************************************** **
+ Catch a SIGTERM signal.
+ **************************************************************************** */
+
+static SIG_ATOMIC_T got_sig_term;
+
+static void sig_term(int sig)
+{
+ got_sig_term = 1;
+ sys_select_signal();
+}
+
+/**************************************************************************** **
+ Catch a SIGHUP signal.
+ **************************************************************************** */
+
+static SIG_ATOMIC_T reload_after_sighup;
+
+static void sig_hup(int sig)
+{
+ reload_after_sighup = 1;
+ sys_select_signal();
+}
+
+#if DUMP_CORE
+/**************************************************************************** **
+ Prepare to dump a core file - carefully!
+ **************************************************************************** */
+
+static BOOL dump_core(void)
+{
+ char *p;
+ pstring dname;
+ pstrcpy( dname, lp_logfile() );
+ if ((p=strrchr_m(dname,'/')))
+ *p=0;
+ pstrcat( dname, "/corefiles" );
+ mkdir( dname, 0700 );
+ sys_chown( dname, getuid(), getgid() );
+ chmod( dname, 0700 );
+ if ( chdir(dname) )
+ return( False );
+ umask( ~(0700) );
+
+#ifdef HAVE_GETRLIMIT
+#ifdef RLIMIT_CORE
+ {
+ struct rlimit rlp;
+ getrlimit( RLIMIT_CORE, &rlp );
+ rlp.rlim_cur = MAX( 4*1024*1024, rlp.rlim_cur );
+ setrlimit( RLIMIT_CORE, &rlp );
+ getrlimit( RLIMIT_CORE, &rlp );
+ DEBUG( 3, ( "Core limits now %d %d\n", (int)rlp.rlim_cur, (int)rlp.rlim_max ) );
+ }
+#endif
+#endif
+
+
+ DEBUG(0,("Dumping core in %s\n",dname));
+ abort();
+ return( True );
+}
+#endif
+
+/**************************************************************************** **
+ Possibly continue after a fault.
+ **************************************************************************** */
+
+static void fault_continue(void)
+{
+#if DUMP_CORE
+ dump_core();
+#endif
+}
+
+/**************************************************************************** **
+ Expire old names from the namelist and server list.
+ **************************************************************************** */
+
+static void expire_names_and_servers(time_t t)
+{
+ static time_t lastrun = 0;
+
+ if ( !lastrun )
+ lastrun = t;
+ if ( t < (lastrun + 5) )
+ return;
+ lastrun = t;
+
+ /*
+ * Expire any timed out names on all the broadcast
+ * subnets and those registered with the WINS server.
+ * (nmbd_namelistdb.c)
+ */
+
+ expire_names(t);
+
+ /*
+ * Go through all the broadcast subnets and for each
+ * workgroup known on that subnet remove any expired
+ * server names. If a workgroup has an empty serverlist
+ * and has itself timed out then remove the workgroup.
+ * (nmbd_workgroupdb.c)
+ */
+
+ expire_workgroups_and_servers(t);
+}
+
+/************************************************************************** **
+ Reload the list of network interfaces.
+ ************************************************************************** */
+
+static BOOL reload_interfaces(time_t t)
+{
+ static time_t lastt;
+ int n;
+ struct subnet_record *subrec;
+ extern BOOL rescan_listen_set;
+ extern struct in_addr loopback_ip;
+
+ if (t && ((t - lastt) < NMBD_INTERFACES_RELOAD)) return False;
+ lastt = t;
+
+ if (!interfaces_changed()) return False;
+
+ /* the list of probed interfaces has changed, we may need to add/remove
+ some subnets */
+ load_interfaces();
+
+ /* find any interfaces that need adding */
+ for (n=iface_count() - 1; n >= 0; n--) {
+ struct interface *iface = get_interface(n);
+
+ /*
+ * We don't want to add a loopback interface, in case
+ * someone has added 127.0.0.1 for smbd, nmbd needs to
+ * ignore it here. JRA.
+ */
+
+ if (ip_equal(iface->ip, loopback_ip)) {
+ DEBUG(2,("reload_interfaces: Ignoring loopback interface %s\n", inet_ntoa(iface->ip)));
+ continue;
+ }
+
+ for (subrec=subnetlist; subrec; subrec=subrec->next) {
+ if (ip_equal(iface->ip, subrec->myip) &&
+ ip_equal(iface->nmask, subrec->mask_ip)) break;
+ }
+
+ if (!subrec) {
+ /* it wasn't found! add it */
+ DEBUG(2,("Found new interface %s\n",
+ inet_ntoa(iface->ip)));
+ subrec = make_normal_subnet(iface);
+ if (subrec)
+ register_my_workgroup_one_subnet(subrec);
+ }
+ }
+
+ /* find any interfaces that need deleting */
+ for (subrec=subnetlist; subrec; subrec=subrec->next) {
+ for (n=iface_count() - 1; n >= 0; n--) {
+ struct interface *iface = get_interface(n);
+ if (ip_equal(iface->ip, subrec->myip) &&
+ ip_equal(iface->nmask, subrec->mask_ip)) break;
+ }
+ if (n == -1) {
+ /* oops, an interface has disapeared. This is
+ tricky, we don't dare actually free the
+ interface as it could be being used, so
+ instead we just wear the memory leak and
+ remove it from the list of interfaces without
+ freeing it */
+ DEBUG(2,("Deleting dead interface %s\n",
+ inet_ntoa(subrec->myip)));
+ close_subnet(subrec);
+ }
+ }
+
+ rescan_listen_set = True;
+
+ /* We need to shutdown if there are no subnets... */
+ if (FIRST_SUBNET == NULL) {
+ DEBUG(0,("reload_interfaces: No subnets to listen to. Shutting down...\n"));
+ return True;
+ }
+ return False;
+}
+
+/**************************************************************************** **
+ Reload the services file.
+ **************************************************************************** */
+
+static BOOL reload_nmbd_services(BOOL test)
+{
+ BOOL ret;
+
+ set_remote_machine_name("nmbd", False);
+
+ if ( lp_loaded() ) {
+ pstring fname;
+ pstrcpy( fname,lp_configfile());
+ if (file_exist(fname,NULL) && !strcsequal(fname,dyn_CONFIGFILE)) {
+ pstrcpy(dyn_CONFIGFILE,fname);
+ test = False;
+ }
+ }
+
+ if ( test && !lp_file_list_changed() )
+ return(True);
+
+ ret = lp_load( dyn_CONFIGFILE, True , False, False);
+
+ /* perhaps the config filename is now set */
+ if ( !test ) {
+ DEBUG( 3, ( "services not loaded\n" ) );
+ reload_nmbd_services( True );
+ }
+
+ return(ret);
+}
+
+/**************************************************************************** **
+ * React on 'smbcontrol nmbd reload-config' in the same way as to SIGHUP
+ * We use buf here to return BOOL result to process() when reload_interfaces()
+ * detects that there are no subnets.
+ **************************************************************************** */
+
+static void msg_reload_nmbd_services(int msg_type, pid_t src, void *buf, size_t len)
+{
+ write_browse_list( 0, True );
+ dump_all_namelists();
+ reload_nmbd_services( True );
+ reopen_logs();
+
+ if(buf) {
+ /* We were called from process() */
+ /* If reload_interfaces() returned True */
+ /* we need to shutdown if there are no subnets... */
+ /* pass this info back to process() */
+ *((BOOL*)buf) = reload_interfaces(0);
+ }
+}
+
+
+/**************************************************************************** **
+ The main select loop.
+ **************************************************************************** */
+
+static void process(void)
+{
+ BOOL run_election;
+ BOOL no_subnets;
+
+ while( True ) {
+ time_t t = time(NULL);
+
+ /* Check for internal messages */
+
+ message_dispatch();
+
+ /*
+ * Check all broadcast subnets to see if
+ * we need to run an election on any of them.
+ * (nmbd_elections.c)
+ */
+
+ run_election = check_elections();
+
+ /*
+ * Read incoming UDP packets.
+ * (nmbd_packets.c)
+ */
+
+ if(listen_for_packets(run_election))
+ return;
+
+ /*
+ * Handle termination inband.
+ */
+
+ if (got_sig_term) {
+ got_sig_term = 0;
+ terminate();
+ }
+
+ /*
+ * Process all incoming packets
+ * read above. This calls the success and
+ * failure functions registered when response
+ * packets arrrive, and also deals with request
+ * packets from other sources.
+ * (nmbd_packets.c)
+ */
+
+ run_packet_queue();
+
+ /*
+ * Run any elections - initiate becoming
+ * a local master browser if we have won.
+ * (nmbd_elections.c)
+ */
+
+ run_elections(t);
+
+ /*
+ * Send out any broadcast announcements
+ * of our server names. This also announces
+ * the workgroup name if we are a local
+ * master browser.
+ * (nmbd_sendannounce.c)
+ */
+
+ announce_my_server_names(t);
+
+ /*
+ * Send out any LanMan broadcast announcements
+ * of our server names.
+ * (nmbd_sendannounce.c)
+ */
+
+ announce_my_lm_server_names(t);
+
+ /*
+ * If we are a local master browser, periodically
+ * announce ourselves to the domain master browser.
+ * This also deals with syncronising the domain master
+ * browser server lists with ourselves as a local
+ * master browser.
+ * (nmbd_sendannounce.c)
+ */
+
+ announce_myself_to_domain_master_browser(t);
+
+ /*
+ * Fullfill any remote announce requests.
+ * (nmbd_sendannounce.c)
+ */
+
+ announce_remote(t);
+
+ /*
+ * Fullfill any remote browse sync announce requests.
+ * (nmbd_sendannounce.c)
+ */
+
+ browse_sync_remote(t);
+
+ /*
+ * Scan the broadcast subnets, and WINS client
+ * namelists and refresh any that need refreshing.
+ * (nmbd_mynames.c)
+ */
+
+ refresh_my_names(t);
+
+ /*
+ * Scan the subnet namelists and server lists and
+ * expire thos that have timed out.
+ * (nmbd.c)
+ */
+
+ expire_names_and_servers(t);
+
+ /*
+ * Write out a snapshot of our current browse list into
+ * the browse.dat file. This is used by smbd to service
+ * incoming NetServerEnum calls - used to synchronise
+ * browse lists over subnets.
+ * (nmbd_serverlistdb.c)
+ */
+
+ write_browse_list(t, False);
+
+ /*
+ * If we are a domain master browser, we have a list of
+ * local master browsers we should synchronise browse
+ * lists with (these are added by an incoming local
+ * master browser announcement packet). Expire any of
+ * these that are no longer current, and pull the server
+ * lists from each of these known local master browsers.
+ * (nmbd_browsesync.c)
+ */
+
+ dmb_expire_and_sync_browser_lists(t);
+
+ /*
+ * Check that there is a local master browser for our
+ * workgroup for all our broadcast subnets. If one
+ * is not found, start an election (which we ourselves
+ * may or may not participate in, depending on the
+ * setting of the 'local master' parameter.
+ * (nmbd_elections.c)
+ */
+
+ check_master_browser_exists(t);
+
+ /*
+ * If we are configured as a logon server, attempt to
+ * register the special NetBIOS names to become such
+ * (WORKGROUP<1c> name) on all broadcast subnets and
+ * with the WINS server (if used). If we are configured
+ * to become a domain master browser, attempt to register
+ * the special NetBIOS name (WORKGROUP<1b> name) to
+ * become such.
+ * (nmbd_become_dmb.c)
+ */
+
+ add_domain_names(t);
+
+ /*
+ * If we are a WINS server, do any timer dependent
+ * processing required.
+ * (nmbd_winsserver.c)
+ */
+
+ initiate_wins_processing(t);
+
+ /*
+ * If we are a domain master browser, attempt to contact the
+ * WINS server to get a list of all known WORKGROUPS/DOMAINS.
+ * This will only work to a Samba WINS server.
+ * (nmbd_browsesync.c)
+ */
+
+ if (lp_enhanced_browsing())
+ collect_all_workgroup_names_from_wins_server(t);
+
+ /*
+ * Go through the response record queue and time out or re-transmit
+ * and expired entries.
+ * (nmbd_packets.c)
+ */
+
+ retransmit_or_expire_response_records(t);
+
+ /*
+ * check to see if any remote browse sync child processes have completed
+ */
+
+ sync_check_completion();
+
+ /*
+ * regularly sync with any other DMBs we know about
+ */
+
+ if (lp_enhanced_browsing())
+ sync_all_dmbs(t);
+
+ /*
+ * clear the unexpected packet queue
+ */
+
+ clear_unexpected(t);
+
+ /*
+ * Reload the services file if we got a sighup.
+ */
+
+ if(reload_after_sighup) {
+ DEBUG( 0, ( "Got SIGHUP dumping debug info.\n" ) );
+ msg_reload_nmbd_services(MSG_SMB_CONF_UPDATED, (pid_t) 0, (void*) &no_subnets, 0);
+ if(no_subnets)
+ return;
+ reload_after_sighup = 0;
+ }
+
+ /* check for new network interfaces */
+
+ if(reload_interfaces(t))
+ return;
+
+ /* free up temp memory */
+ lp_talloc_free();
+ }
+}
+
+/**************************************************************************** **
+ Open the socket communication.
+ **************************************************************************** */
+
+static BOOL open_sockets(BOOL isdaemon, int port)
+{
+ /*
+ * The sockets opened here will be used to receive broadcast
+ * packets *only*. Interface specific sockets are opened in
+ * make_subnet() in namedbsubnet.c. Thus we bind to the
+ * address "0.0.0.0". The parameter 'socket address' is
+ * now deprecated.
+ */
+
+ if ( isdaemon )
+ ClientNMB = open_socket_in(SOCK_DGRAM, port,
+ 0, interpret_addr(lp_socket_address()),
+ True);
+ else
+ ClientNMB = 0;
+
+ ClientDGRAM = open_socket_in(SOCK_DGRAM,DGRAM_PORT,3,0,True);
+
+ if ( ClientNMB == -1 )
+ return( False );
+
+ /* we are never interested in SIGPIPE */
+ BlockSignals(True,SIGPIPE);
+
+ set_socket_options( ClientNMB, "SO_BROADCAST" );
+ set_socket_options( ClientDGRAM, "SO_BROADCAST" );
+
+ DEBUG( 3, ( "open_sockets: Broadcast sockets opened.\n" ) );
+ return( True );
+}
+
+/**************************************************************************** **
+ main program
+ **************************************************************************** */
+ int main(int argc, const char *argv[])
+{
+ pstring logfile;
+ static BOOL opt_interactive;
+ poptContext pc;
+ int opt;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"daemon", 'D', POPT_ARG_VAL, &is_daemon, True, "Become a daemon(default)" },
+ {"interactive", 'i', POPT_ARG_VAL, &opt_interactive, True, "Run interactive (not a daemon)" },
+ {"foreground", 'F', POPT_ARG_VAL, &Fork, False, "Run daemon in foreground (for daemontools & etc)" },
+ {"log-stdout", 'S', POPT_ARG_VAL, &log_stdout, True, "Log to stdout" },
+ {"hosts", 'H', POPT_ARG_STRING, dyn_LMHOSTSFILE, 'H', "Load a netbios hosts file"},
+ {"port", 'p', POPT_ARG_INT, &global_nmb_port, NMB_PORT, "Listen on the specified port" },
+ POPT_COMMON_SAMBA
+ { NULL }
+ };
+
+ global_nmb_port = NMB_PORT;
+
+ pc = poptGetContext("nmbd", argc, argv, long_options, 0);
+ while ((opt = poptGetNextOpt(pc)) != -1) ;
+ poptFreeContext(pc);
+
+ global_in_nmbd = True;
+
+ StartupTime = time(NULL);
+
+ sys_srandom(time(NULL) ^ sys_getpid());
+
+ slprintf(logfile, sizeof(logfile)-1, "%s/log.nmbd", dyn_LOGFILEBASE);
+ lp_set_logfile(logfile);
+
+ fault_setup((void (*)(void *))fault_continue );
+
+ /* POSIX demands that signals are inherited. If the invoking process has
+ * these signals masked, we will have problems, as we won't receive them. */
+ BlockSignals(False, SIGHUP);
+ BlockSignals(False, SIGUSR1);
+ BlockSignals(False, SIGTERM);
+
+ CatchSignal( SIGHUP, SIGNAL_CAST sig_hup );
+ CatchSignal( SIGTERM, SIGNAL_CAST sig_term );
+
+#if defined(SIGFPE)
+ /* we are never interested in SIGFPE */
+ BlockSignals(True,SIGFPE);
+#endif
+
+ /* We no longer use USR2... */
+#if defined(SIGUSR2)
+ BlockSignals(True, SIGUSR2);
+#endif
+
+ if ( opt_interactive ) {
+ Fork = False;
+ log_stdout = True;
+ }
+
+ if ( log_stdout && Fork ) {
+ DEBUG(0,("ERROR: Can't log to stdout (-S) unless daemon is in foreground (-F) or interactive (-i)\n"));
+ exit(1);
+ }
+
+ setup_logging( argv[0], log_stdout );
+
+ reopen_logs();
+
+ DEBUG( 0, ( "Netbios nameserver version %s started.\n", SAMBA_VERSION_STRING) );
+ DEBUGADD( 0, ( "Copyright Andrew Tridgell and the Samba Team 1994-2004\n" ) );
+
+ if ( !reload_nmbd_services(False) )
+ return(-1);
+
+ if(!init_names())
+ return -1;
+
+ reload_nmbd_services( True );
+
+ if (strequal(lp_workgroup(),"*")) {
+ DEBUG(0,("ERROR: a workgroup name of * is no longer supported\n"));
+ exit(1);
+ }
+
+ set_samba_nb_type();
+
+ if (!is_daemon && !is_a_socket(0)) {
+ DEBUG(0,("standard input is not a socket, assuming -D option\n"));
+ is_daemon = True;
+ }
+
+ if (is_daemon && !opt_interactive) {
+ DEBUG( 2, ( "Becoming a daemon.\n" ) );
+ become_daemon(Fork);
+ }
+
+#if HAVE_SETPGID
+ /*
+ * If we're interactive we want to set our own process group for
+ * signal management.
+ */
+ if (opt_interactive)
+ setpgid( (pid_t)0, (pid_t)0 );
+#endif
+
+#ifndef SYNC_DNS
+ /* Setup the async dns. We do it here so it doesn't have all the other
+ stuff initialised and thus chewing memory and sockets */
+ if(lp_we_are_a_wins_server() && lp_dns_proxy()) {
+ start_async_dns();
+ }
+#endif
+
+ if (!directory_exist(lp_lockdir(), NULL)) {
+ mkdir(lp_lockdir(), 0755);
+ }
+
+ pidfile_create("nmbd");
+ message_init();
+ message_register(MSG_FORCE_ELECTION, nmbd_message_election);
+ message_register(MSG_WINS_NEW_ENTRY, nmbd_wins_new_entry);
+ message_register(MSG_SHUTDOWN, nmbd_terminate);
+ message_register(MSG_SMB_CONF_UPDATED, msg_reload_nmbd_services);
+
+ DEBUG( 3, ( "Opening sockets %d\n", global_nmb_port ) );
+
+ if ( !open_sockets( is_daemon, global_nmb_port ) ) {
+ kill_async_dns_child();
+ return 1;
+ }
+
+ /* Determine all the IP addresses we have. */
+ load_interfaces();
+
+ /* Create an nmbd subnet record for each of the above. */
+ if( False == create_subnets() ) {
+ DEBUG(0,("ERROR: Failed when creating subnet lists. Exiting.\n"));
+ kill_async_dns_child();
+ exit(1);
+ }
+
+ /* Load in any static local names. */
+ load_lmhosts_file(dyn_LMHOSTSFILE);
+ DEBUG(3,("Loaded hosts file %s\n", dyn_LMHOSTSFILE));
+
+ /* If we are acting as a WINS server, initialise data structures. */
+ if( !initialise_wins() ) {
+ DEBUG( 0, ( "nmbd: Failed when initialising WINS server.\n" ) );
+ kill_async_dns_child();
+ exit(1);
+ }
+
+ /*
+ * Register nmbd primary workgroup and nmbd names on all
+ * the broadcast subnets, and on the WINS server (if specified).
+ * Also initiate the startup of our primary workgroup (start
+ * elections if we are setup as being able to be a local
+ * master browser.
+ */
+
+ if( False == register_my_workgroup_and_names() ) {
+ DEBUG(0,("ERROR: Failed when creating my my workgroup. Exiting.\n"));
+ kill_async_dns_child();
+ exit(1);
+ }
+
+ /* We can only take signals in the select. */
+ BlockSignals( True, SIGTERM );
+
+ process();
+
+ if (dbf)
+ x_fclose(dbf);
+ kill_async_dns_child();
+ return(0);
+}
diff --git a/source/nmbd/nmbd_become_dmb.c b/source/nmbd/nmbd_become_dmb.c
new file mode 100644
index 00000000000..fb1fb33a818
--- /dev/null
+++ b/source/nmbd/nmbd_become_dmb.c
@@ -0,0 +1,386 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+
+extern struct in_addr allones_ip;
+
+extern uint16 samba_nb_type; /* Samba's NetBIOS type. */
+
+static void become_domain_master_browser_bcast(const char *);
+
+/****************************************************************************
+ Fail to become a Domain Master Browser on a subnet.
+ ****************************************************************************/
+
+static void become_domain_master_fail(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *fail_name)
+{
+ unstring failname;
+ struct work_record *work;
+ struct server_record *servrec;
+
+ pull_ascii_nstring(failname, sizeof(failname), fail_name->name);
+ work = find_workgroup_on_subnet(subrec, failname);
+ if(!work) {
+ DEBUG(0,("become_domain_master_fail: Error - cannot find \
+workgroup %s on subnet %s\n", failname, subrec->subnet_name));
+ return;
+ }
+
+ /* Set the state back to DOMAIN_NONE. */
+ work->dom_state = DOMAIN_NONE;
+
+ if((servrec = find_server_in_workgroup( work, global_myname())) == NULL) {
+ DEBUG(0,("become_domain_master_fail: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ global_myname(), work->work_group, subrec->subnet_name));
+ return;
+ }
+
+ /* Update our server status. */
+ servrec->serv.type &= ~SV_TYPE_DOMAIN_MASTER;
+
+ /* Tell the namelist writer to write out a change. */
+ subrec->work_changed = True;
+
+ DEBUG(0,("become_domain_master_fail: Failed to become a domain master browser for \
+workgroup %s on subnet %s. Couldn't register name %s.\n",
+ work->work_group, subrec->subnet_name, nmb_namestr(fail_name)));
+}
+
+/****************************************************************************
+ Become a Domain Master Browser on a subnet.
+ ****************************************************************************/
+
+static void become_domain_master_stage2(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *registered_name,
+ uint16 nb_flags,
+ int ttl, struct in_addr registered_ip)
+{
+ unstring regname;
+ struct work_record *work;
+ struct server_record *servrec;
+
+ pull_ascii_nstring(regname, sizeof(regname), registered_name->name);
+ work = find_workgroup_on_subnet( subrec, regname);
+
+ if(!work) {
+ DEBUG(0,("become_domain_master_stage2: Error - cannot find \
+workgroup %s on subnet %s\n", regname, subrec->subnet_name));
+ return;
+ }
+
+ if((servrec = find_server_in_workgroup( work, global_myname())) == NULL) {
+ DEBUG(0,("become_domain_master_stage2: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ global_myname(), regname, subrec->subnet_name));
+ work->dom_state = DOMAIN_NONE;
+ return;
+ }
+
+ /* Set the state in the workgroup structure. */
+ work->dom_state = DOMAIN_MST; /* Become domain master. */
+
+ /* Update our server status. */
+ servrec->serv.type |= (SV_TYPE_NT|SV_TYPE_DOMAIN_MASTER);
+
+ /* Tell the namelist writer to write out a change. */
+ subrec->work_changed = True;
+
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "*****\n\nSamba server %s ", global_myname() );
+ dbgtext( "is now a domain master browser for " );
+ dbgtext( "workgroup %s ", work->work_group );
+ dbgtext( "on subnet %s\n\n*****\n", subrec->subnet_name );
+ }
+
+ if( subrec == unicast_subnet ) {
+ struct nmb_name nmbname;
+ struct in_addr my_first_ip;
+
+ /* Put our name and first IP address into the
+ workgroup struct as domain master browser. This
+ will stop us syncing with ourself if we are also
+ a local master browser. */
+
+ make_nmb_name(&nmbname, global_myname(), 0x20);
+
+ work->dmb_name = nmbname;
+ /* Pick the first interface ip address as the domain master browser ip. */
+ my_first_ip = *iface_n_ip(0);
+
+ putip((char *)&work->dmb_addr, &my_first_ip);
+
+ /* We successfully registered by unicast with the
+ WINS server. We now expect to become the domain
+ master on the local subnets. If this fails, it's
+ probably a 1.9.16p2 to 1.9.16p11 server's fault.
+
+ This is a configuration issue that should be addressed
+ by the network administrator - you shouldn't have
+ several machines configured as a domain master browser
+ for the same WINS scope (except if they are 1.9.17 or
+ greater, and you know what you're doing.
+
+ see docs/DOMAIN.txt.
+
+ */
+ become_domain_master_browser_bcast(work->work_group);
+ } else {
+ /*
+ * Now we are a domain master on a broadcast subnet, we need to add
+ * the WORKGROUP<1b> name to the unicast subnet so that we can answer
+ * unicast requests sent to this name. This bug wasn't found for a while
+ * as it is strange to have a DMB without using WINS. JRA.
+ */
+ insert_permanent_name_into_unicast(subrec, registered_name, nb_flags);
+ }
+}
+
+/****************************************************************************
+ Start the name registration process when becoming a Domain Master Browser
+ on a subnet.
+****************************************************************************/
+
+static void become_domain_master_stage1(struct subnet_record *subrec, const char *wg_name)
+{
+ struct work_record *work;
+
+ DEBUG(2,("become_domain_master_stage1: Becoming domain master browser for \
+workgroup %s on subnet %s\n", wg_name, subrec->subnet_name));
+
+ /* First, find the workgroup on the subnet. */
+ if((work = find_workgroup_on_subnet( subrec, wg_name )) == NULL) {
+ DEBUG(0,("become_domain_master_stage1: Error - unable to find workgroup %s on subnet %s.\n",
+ wg_name, subrec->subnet_name));
+ return;
+ }
+
+ DEBUG(3,("become_domain_master_stage1: go to first stage: register <1b> name\n"));
+ work->dom_state = DOMAIN_WAIT;
+
+ /* WORKGROUP<1b> is the domain master browser name. */
+ register_name(subrec, work->work_group,0x1b,samba_nb_type,
+ become_domain_master_stage2,
+ become_domain_master_fail, NULL);
+}
+
+/****************************************************************************
+ Function called when a query for a WORKGROUP<1b> name succeeds.
+ This is normally a fail condition as it means there is already
+ a domain master browser for a workgroup and we were trying to
+ become one.
+****************************************************************************/
+
+static void become_domain_master_query_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname, struct in_addr ip,
+ struct res_rec *rrec)
+{
+ unstring name;
+ pull_ascii_nstring(name, sizeof(name), nmbname->name);
+
+ /* If the given ip is not ours, then we can't become a domain
+ controler as the name is already registered.
+ */
+
+ /* BUG note. Samba 1.9.16p11 servers seem to return the broadcast
+ address or zero ip for this query. Pretend this is ok. */
+
+ if(ismyip(ip) || ip_equal(allones_ip, ip) || is_zero_ip(ip)) {
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "become_domain_master_query_success():\n" );
+ dbgtext( "Our address (%s) ", inet_ntoa(ip) );
+ dbgtext( "returned in query for name %s ", nmb_namestr(nmbname) );
+ dbgtext( "(domain master browser name) " );
+ dbgtext( "on subnet %s.\n", subrec->subnet_name );
+ dbgtext( "Continuing with domain master code.\n" );
+ }
+
+ become_domain_master_stage1(subrec, name);
+ } else {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "become_domain_master_query_success:\n" );
+ dbgtext( "There is already a domain master browser at " );
+ dbgtext( "IP %s for workgroup %s ", inet_ntoa(ip), name );
+ dbgtext( "registered on subnet %s.\n", subrec->subnet_name );
+ }
+ }
+}
+
+/****************************************************************************
+ Function called when a query for a WORKGROUP<1b> name fails.
+ This is normally a success condition as it then allows us to register
+ our own Domain Master Browser name.
+ ****************************************************************************/
+
+static void become_domain_master_query_fail(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *question_name, int fail_code)
+{
+ unstring name;
+
+ /* If the query was unicast, and the error is not NAM_ERR (name didn't exist),
+ then this is a failure. Otherwise, not finding the name is what we want. */
+
+ if((subrec == unicast_subnet) && (fail_code != NAM_ERR)) {
+ DEBUG(0,("become_domain_master_query_fail: Error %d returned when \
+querying WINS server for name %s.\n",
+ fail_code, nmb_namestr(question_name)));
+ return;
+ }
+
+ /* Otherwise - not having the name allows us to register it. */
+ pull_ascii_nstring(name, sizeof(name), question_name->name);
+ become_domain_master_stage1(subrec, name);
+}
+
+/****************************************************************************
+ Attempt to become a domain master browser on all broadcast subnets.
+ ****************************************************************************/
+
+static void become_domain_master_browser_bcast(const char *workgroup_name)
+{
+ struct subnet_record *subrec;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ struct work_record *work = find_workgroup_on_subnet(subrec, workgroup_name);
+
+ if (work && (work->dom_state == DOMAIN_NONE)) {
+ struct nmb_name nmbname;
+ make_nmb_name(&nmbname,workgroup_name,0x1b);
+
+ /*
+ * Check for our name on the given broadcast subnet first, only initiate
+ * further processing if we cannot find it.
+ */
+
+ if (find_name_on_subnet(subrec, &nmbname, FIND_SELF_NAME) == NULL) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "become_domain_master_browser_bcast:\n" );
+ dbgtext( "Attempting to become domain master browser on " );
+ dbgtext( "workgroup %s on subnet %s\n",
+ workgroup_name, subrec->subnet_name );
+ }
+
+ /* Send out a query to establish whether there's a
+ domain controller on the local subnet. If not,
+ we can become a domain controller.
+ */
+
+ DEBUG(0,("become_domain_master_browser_bcast: querying subnet %s \
+for domain master browser on workgroup %s\n", subrec->subnet_name, workgroup_name));
+
+ query_name(subrec, workgroup_name, nmbname.name_type,
+ become_domain_master_query_success,
+ become_domain_master_query_fail,
+ NULL);
+ }
+ }
+ }
+}
+
+/****************************************************************************
+ Attempt to become a domain master browser by registering with WINS.
+ ****************************************************************************/
+
+static void become_domain_master_browser_wins(const char *workgroup_name)
+{
+ struct work_record *work;
+
+ work = find_workgroup_on_subnet(unicast_subnet, workgroup_name);
+
+ if (work && (work->dom_state == DOMAIN_NONE)) {
+ struct nmb_name nmbname;
+
+ make_nmb_name(&nmbname,workgroup_name,0x1b);
+
+ /*
+ * Check for our name on the unicast subnet first, only initiate
+ * further processing if we cannot find it.
+ */
+
+ if (find_name_on_subnet(unicast_subnet, &nmbname, FIND_SELF_NAME) == NULL) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "become_domain_master_browser_wins:\n" );
+ dbgtext( "Attempting to become domain master browser " );
+ dbgtext( "on workgroup %s, subnet %s.\n",
+ workgroup_name, unicast_subnet->subnet_name );
+ }
+
+ /* Send out a query to establish whether there's a
+ domain master broswer registered with WINS. If not,
+ we can become a domain master browser.
+ */
+
+ DEBUG(0,("become_domain_master_browser_wins: querying WINS server from IP %s \
+for domain master browser name %s on workgroup %s\n",
+ inet_ntoa(unicast_subnet->myip), nmb_namestr(&nmbname), workgroup_name));
+
+ query_name(unicast_subnet, workgroup_name, nmbname.name_type,
+ become_domain_master_query_success,
+ become_domain_master_query_fail,
+ NULL);
+ }
+ }
+}
+
+/****************************************************************************
+ Add the domain logon server and domain master browser names
+ if we are set up to do so.
+ **************************************************************************/
+
+void add_domain_names(time_t t)
+{
+ static time_t lastrun = 0;
+
+ if ((lastrun != 0) && (t < lastrun + (CHECK_TIME_ADD_DOM_NAMES * 60)))
+ return;
+
+ lastrun = t;
+
+ /* Do the "internet group" - <1c> names. */
+ if (lp_domain_logons())
+ add_logon_names();
+
+ /* Do the domain master names. */
+ if(lp_domain_master()) {
+ if(we_are_a_wins_client()) {
+ /* We register the WORKGROUP<1b> name with the WINS
+ server first, and call add_domain_master_bcast()
+ only if this is successful.
+
+ This results in domain logon services being gracefully provided,
+ as opposed to the aggressive nature of 1.9.16p2 to 1.9.16p11.
+ 1.9.16p2 to 1.9.16p11 - due to a bug in namelogon.c,
+ cannot provide domain master / domain logon services.
+ */
+ become_domain_master_browser_wins(lp_workgroup());
+ } else {
+ become_domain_master_browser_bcast(lp_workgroup());
+ }
+ }
+}
diff --git a/source/nmbd/nmbd_become_lmb.c b/source/nmbd/nmbd_become_lmb.c
new file mode 100644
index 00000000000..c536deb6f45
--- /dev/null
+++ b/source/nmbd/nmbd_become_lmb.c
@@ -0,0 +1,594 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+
+extern uint16 samba_nb_type; /* Samba's NetBIOS name type. */
+
+/*******************************************************************
+ Utility function to add a name to the unicast subnet, or add in
+ our IP address if it already exists.
+******************************************************************/
+
+void insert_permanent_name_into_unicast( struct subnet_record *subrec,
+ struct nmb_name *nmbname, uint16 nb_type )
+{
+ unstring name;
+ struct name_record *namerec;
+
+ if((namerec = find_name_on_subnet(unicast_subnet, nmbname, FIND_SELF_NAME)) == NULL) {
+ pull_ascii_nstring(name, sizeof(name), nmbname->name);
+ /* The name needs to be created on the unicast subnet. */
+ (void)add_name_to_subnet( unicast_subnet, name,
+ nmbname->name_type, nb_type,
+ PERMANENT_TTL, PERMANENT_NAME, 1, &subrec->myip);
+ } else {
+ /* The name already exists on the unicast subnet. Add our local
+ IP for the given broadcast subnet to the name. */
+ add_ip_to_name_record( namerec, subrec->myip);
+ }
+}
+
+/*******************************************************************
+ Utility function to remove a name from the unicast subnet.
+******************************************************************/
+
+static void remove_permanent_name_from_unicast( struct subnet_record *subrec,
+ struct nmb_name *nmbname )
+{
+ struct name_record *namerec;
+
+ if((namerec = find_name_on_subnet(unicast_subnet, nmbname, FIND_SELF_NAME)) != NULL) {
+ /* Remove this broadcast subnet IP address from the name. */
+ remove_ip_from_name_record( namerec, subrec->myip);
+ if(namerec->data.num_ips == 0)
+ remove_name_from_namelist( unicast_subnet, namerec);
+ }
+}
+
+/*******************************************************************
+ Utility function always called to set our workgroup and server
+ state back to potential browser, or none.
+******************************************************************/
+
+static void reset_workgroup_state( struct subnet_record *subrec, const char *workgroup_name,
+ BOOL force_new_election )
+{
+ struct work_record *work;
+ struct server_record *servrec;
+ struct nmb_name nmbname;
+
+ if((work = find_workgroup_on_subnet( subrec, workgroup_name)) == NULL) {
+ DEBUG(0,("reset_workgroup_state: Error - cannot find workgroup %s on \
+subnet %s.\n", workgroup_name, subrec->subnet_name ));
+ return;
+ }
+
+ if((servrec = find_server_in_workgroup( work, global_myname())) == NULL) {
+ DEBUG(0,("reset_workgroup_state: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ global_myname(), work->work_group, subrec->subnet_name));
+ work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE;
+ return;
+ }
+
+ /* Update our server status - remove any master flag and replace
+ it with the potential browser flag. */
+ servrec->serv.type &= ~SV_TYPE_MASTER_BROWSER;
+ servrec->serv.type |= (lp_local_master() ? SV_TYPE_POTENTIAL_BROWSER : 0);
+
+ /* Tell the namelist writer to write out a change. */
+ subrec->work_changed = True;
+
+ /* Reset our election flags. */
+ work->ElectionCriterion &= ~0x4;
+
+ work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE;
+
+ /* Forget who the local master browser was for
+ this workgroup. */
+
+ set_workgroup_local_master_browser_name( work, "");
+
+ /*
+ * Ensure the IP address of this subnet is not registered as one
+ * of the IP addresses of the WORKGROUP<1d> name on the unicast
+ * subnet. This undoes what we did below when we became a local
+ * master browser.
+ */
+
+ make_nmb_name(&nmbname, work->work_group, 0x1d);
+
+ remove_permanent_name_from_unicast( subrec, &nmbname);
+
+ if(force_new_election)
+ work->needelection = True;
+}
+
+/*******************************************************************
+ Unbecome the local master browser name release success function.
+******************************************************************/
+
+static void unbecome_local_master_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *released_name,
+ struct in_addr released_ip)
+{
+ BOOL force_new_election = False;
+ unstring relname;
+
+ memcpy((char *)&force_new_election, userdata->data, sizeof(BOOL));
+
+ DEBUG(3,("unbecome_local_master_success: released name %s.\n",
+ nmb_namestr(released_name)));
+
+ /* Now reset the workgroup and server state. */
+ pull_ascii_nstring(relname, sizeof(relname), released_name->name);
+ reset_workgroup_state( subrec, relname, force_new_election );
+
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "*****\n\n" );
+ dbgtext( "Samba name server %s ", global_myname() );
+ dbgtext( "has stopped being a local master browser " );
+ dbgtext( "for workgroup %s ", relname );
+ dbgtext( "on subnet %s\n\n*****\n", subrec->subnet_name );
+ }
+
+}
+
+/*******************************************************************
+ Unbecome the local master browser name release fail function.
+******************************************************************/
+
+static void unbecome_local_master_fail(struct subnet_record *subrec, struct response_record *rrec,
+ struct nmb_name *fail_name)
+{
+ struct name_record *namerec;
+ struct userdata_struct *userdata = rrec->userdata;
+ BOOL force_new_election = False;
+ unstring failname;
+
+ memcpy((char *)&force_new_election, userdata->data, sizeof(BOOL));
+
+ DEBUG(0,("unbecome_local_master_fail: failed to release name %s. \
+Removing from namelist anyway.\n", nmb_namestr(fail_name)));
+
+ /* Do it anyway. */
+ namerec = find_name_on_subnet(subrec, fail_name, FIND_SELF_NAME);
+ if(namerec)
+ remove_name_from_namelist(subrec, namerec);
+
+ /* Now reset the workgroup and server state. */
+ pull_ascii_nstring(failname, sizeof(failname), fail_name->name);
+ reset_workgroup_state( subrec, failname, force_new_election );
+
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "*****\n\n" );
+ dbgtext( "Samba name server %s ", global_myname() );
+ dbgtext( "has stopped being a local master browser " );
+ dbgtext( "for workgroup %s ", failname );
+ dbgtext( "on subnet %s\n\n*****\n", subrec->subnet_name );
+ }
+}
+
+/*******************************************************************
+ Utility function to remove the WORKGROUP<1d> name.
+******************************************************************/
+
+static void release_1d_name( struct subnet_record *subrec, const char *workgroup_name,
+ BOOL force_new_election)
+{
+ struct nmb_name nmbname;
+ struct name_record *namerec;
+
+ make_nmb_name(&nmbname, workgroup_name, 0x1d);
+ if((namerec = find_name_on_subnet( subrec, &nmbname, FIND_SELF_NAME))!=NULL) {
+ struct userdata_struct *userdata;
+ size_t size = sizeof(struct userdata_struct) + sizeof(BOOL);
+
+ if((userdata = (struct userdata_struct *)malloc(size)) == NULL) {
+ DEBUG(0,("release_1d_name: malloc fail.\n"));
+ return;
+ }
+
+ userdata->copy_fn = NULL;
+ userdata->free_fn = NULL;
+ userdata->userdata_len = sizeof(BOOL);
+ memcpy((char *)userdata->data, &force_new_election, sizeof(BOOL));
+
+ release_name(subrec, namerec,
+ unbecome_local_master_success,
+ unbecome_local_master_fail,
+ userdata);
+
+ zero_free(userdata, size);
+ }
+}
+
+/*******************************************************************
+ Unbecome the local master browser MSBROWSE name release success function.
+******************************************************************/
+
+static void release_msbrowse_name_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *released_name,
+ struct in_addr released_ip)
+{
+ DEBUG(4,("release_msbrowse_name_success: Released name %s on subnet %s\n.",
+ nmb_namestr(released_name), subrec->subnet_name ));
+
+ /* Remove the permanent MSBROWSE name added into the unicast subnet. */
+ remove_permanent_name_from_unicast( subrec, released_name);
+}
+
+/*******************************************************************
+ Unbecome the local master browser MSBROWSE name release fail function.
+******************************************************************/
+
+static void release_msbrowse_name_fail( struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *fail_name)
+{
+ struct name_record *namerec;
+
+ DEBUG(4,("release_msbrowse_name_fail: Failed to release name %s on subnet %s\n.",
+ nmb_namestr(fail_name), subrec->subnet_name ));
+
+ /* Release the name anyway. */
+ namerec = find_name_on_subnet(subrec, fail_name, FIND_SELF_NAME);
+ if(namerec)
+ remove_name_from_namelist(subrec, namerec);
+
+ /* Remove the permanent MSBROWSE name added into the unicast subnet. */
+ remove_permanent_name_from_unicast( subrec, fail_name);
+}
+
+/*******************************************************************
+ Unbecome the local master browser. If force_new_election is true, restart
+ the election process after we've unbecome the local master.
+******************************************************************/
+
+void unbecome_local_master_browser(struct subnet_record *subrec, struct work_record *work,
+ BOOL force_new_election)
+{
+ struct name_record *namerec;
+ struct nmb_name nmbname;
+
+ /* Sanity check. */
+
+ DEBUG(2,("unbecome_local_master_browser: unbecoming local master for workgroup %s \
+on subnet %s\n",work->work_group, subrec->subnet_name));
+
+ if(find_server_in_workgroup( work, global_myname()) == NULL) {
+ DEBUG(0,("unbecome_local_master_browser: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ global_myname(), work->work_group, subrec->subnet_name));
+ work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE;
+ return;
+ }
+
+ /* Set the state to unbecoming. */
+ work->mst_state = MST_UNBECOMING_MASTER;
+
+ /*
+ * Release the WORKGROUP<1d> name asap to allow another machine to
+ * claim it.
+ */
+
+ release_1d_name( subrec, work->work_group, force_new_election);
+
+ /* Deregister any browser names we may have. */
+ make_nmb_name(&nmbname, MSBROWSE, 0x1);
+ if((namerec = find_name_on_subnet( subrec, &nmbname, FIND_SELF_NAME))!=NULL) {
+ release_name(subrec, namerec,
+ release_msbrowse_name_success,
+ release_msbrowse_name_fail,
+ NULL);
+ }
+
+ /*
+ * Ensure we have sent and processed these release packets
+ * before returning - we don't want to process any election
+ * packets before dealing with the 1d release.
+ */
+
+ retransmit_or_expire_response_records(time(NULL));
+}
+
+/****************************************************************************
+ Success in registering the WORKGROUP<1d> name.
+ We are now *really* a local master browser.
+ ****************************************************************************/
+
+static void become_local_master_stage2(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *registered_name,
+ uint16 nb_flags,
+ int ttl, struct in_addr registered_ip)
+{
+ int i = 0;
+ struct server_record *sl;
+ struct work_record *work;
+ struct server_record *servrec;
+ unstring regname;
+
+ pull_ascii_nstring(regname, sizeof(regname), registered_name->name);
+ work = find_workgroup_on_subnet( subrec, regname);
+
+ if(!work) {
+ DEBUG(0,("become_local_master_stage2: Error - cannot find \
+workgroup %s on subnet %s\n", regname, subrec->subnet_name));
+ return;
+ }
+
+ if((servrec = find_server_in_workgroup( work, global_myname())) == NULL) {
+ DEBUG(0,("become_local_master_stage2: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ global_myname(), regname, subrec->subnet_name));
+ work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE;
+ return;
+ }
+
+ DEBUG(3,("become_local_master_stage2: registered as master browser for workgroup %s \
+on subnet %s\n", work->work_group, subrec->subnet_name));
+
+ work->mst_state = MST_BROWSER; /* registering WORKGROUP(1d) succeeded */
+
+ /* update our server status */
+ servrec->serv.type |= SV_TYPE_MASTER_BROWSER;
+ servrec->serv.type &= ~SV_TYPE_POTENTIAL_BROWSER;
+
+ /* Tell the namelist writer to write out a change. */
+ subrec->work_changed = True;
+
+ /* Add this name to the workgroup as local master browser. */
+ set_workgroup_local_master_browser_name( work, global_myname());
+
+ /* Count the number of servers we have on our list. If it's
+ less than 10 (just a heuristic) request the servers
+ to announce themselves.
+ */
+ for( sl = work->serverlist; sl != NULL; sl = sl->next)
+ i++;
+
+ if (i < 10) {
+ /* Ask all servers on our local net to announce to us. */
+ broadcast_announce_request(subrec, work);
+ }
+
+ /*
+ * Now we are a local master on a broadcast subnet, we need to add
+ * the WORKGROUP<1d> name to the unicast subnet so that we can answer
+ * unicast requests sent to this name. We can create this name directly on
+ * the unicast subnet as a WINS server always returns true when registering
+ * this name, and discards the registration. We use the number of IP
+ * addresses registered to this name as a reference count, as we
+ * remove this broadcast subnet IP address from it when we stop becoming a local
+ * master browser for this broadcast subnet.
+ */
+
+ insert_permanent_name_into_unicast( subrec, registered_name, nb_flags);
+
+ /* Reset the announce master browser timer so that we try and tell a domain
+ master browser as soon as possible that we are a local master browser. */
+ reset_announce_timer();
+
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "*****\n\n" );
+ dbgtext( "Samba name server %s ", global_myname() );
+ dbgtext( "is now a local master browser " );
+ dbgtext( "for workgroup %s ", work->work_group );
+ dbgtext( "on subnet %s\n\n*****\n", subrec->subnet_name );
+ }
+}
+
+/****************************************************************************
+ Failed to register the WORKGROUP<1d> name.
+ ****************************************************************************/
+
+static void become_local_master_fail2(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *fail_name)
+{
+ unstring failname;
+ struct work_record *work;
+
+ DEBUG(0,("become_local_master_fail2: failed to register name %s on subnet %s. \
+Failed to become a local master browser.\n", nmb_namestr(fail_name), subrec->subnet_name));
+
+ pull_ascii_nstring(failname, sizeof(failname), fail_name->name);
+ work = find_workgroup_on_subnet( subrec, failname);
+
+ if(!work) {
+ DEBUG(0,("become_local_master_fail2: Error - cannot find \
+workgroup %s on subnet %s\n", failname, subrec->subnet_name));
+ return;
+ }
+
+ /* Roll back all the way by calling unbecome_local_master_browser(). */
+ unbecome_local_master_browser(subrec, work, False);
+}
+
+/****************************************************************************
+ Success in registering the MSBROWSE name.
+ ****************************************************************************/
+
+static void become_local_master_stage1(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *registered_name,
+ uint16 nb_flags,
+ int ttl, struct in_addr registered_ip)
+{
+ char *work_name = userdata->data;
+ struct work_record *work = find_workgroup_on_subnet( subrec, work_name);
+
+ if(!work) {
+ DEBUG(0,("become_local_master_stage1: Error - cannot find \
+ %s on subnet %s\n", work_name, subrec->subnet_name));
+ return;
+ }
+
+ DEBUG(3,("become_local_master_stage1: go to stage 2: register the %s<1d> name.\n",
+ work->work_group));
+
+ work->mst_state = MST_MSB; /* Registering MSBROWSE was successful. */
+
+ /*
+ * We registered the MSBROWSE name on a broadcast subnet, now need to add
+ * the MSBROWSE name to the unicast subnet so that we can answer
+ * unicast requests sent to this name. We create this name directly on
+ * the unicast subnet.
+ */
+
+ insert_permanent_name_into_unicast( subrec, registered_name, nb_flags);
+
+ /* Attempt to register the WORKGROUP<1d> name. */
+ register_name(subrec, work->work_group,0x1d,samba_nb_type,
+ become_local_master_stage2,
+ become_local_master_fail2,
+ NULL);
+}
+
+/****************************************************************************
+ Failed to register the MSBROWSE name.
+ ****************************************************************************/
+
+static void become_local_master_fail1(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *fail_name)
+{
+ char *work_name = rrec->userdata->data;
+ struct work_record *work = find_workgroup_on_subnet(subrec, work_name);
+
+ if(!work) {
+ DEBUG(0,("become_local_master_fail1: Error - cannot find \
+workgroup %s on subnet %s\n", work_name, subrec->subnet_name));
+ return;
+ }
+
+ if(find_server_in_workgroup(work, global_myname()) == NULL) {
+ DEBUG(0,("become_local_master_fail1: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ global_myname(), work->work_group, subrec->subnet_name));
+ return;
+ }
+
+ reset_workgroup_state( subrec, work->work_group, False );
+
+ DEBUG(0,("become_local_master_fail1: Failed to become a local master browser for \
+workgroup %s on subnet %s. Couldn't register name %s.\n",
+ work->work_group, subrec->subnet_name, nmb_namestr(fail_name)));
+}
+
+/******************************************************************
+ Become the local master browser on a subnet.
+ This gets called if we win an election on this subnet.
+
+ Stage 1: mst_state was MST_POTENTIAL - go to MST_BACK register ^1^2__MSBROWSE__^2^1.
+ Stage 2: mst_state was MST_BACKUP - go to MST_MSB and register WORKGROUP<1d>.
+ Stage 3: mst_state was MST_MSB - go to MST_BROWSER.
+******************************************************************/
+
+void become_local_master_browser(struct subnet_record *subrec, struct work_record *work)
+{
+ struct userdata_struct *userdata;
+ size_t size = sizeof(struct userdata_struct) + sizeof(fstring) + 1;
+
+ /* Sanity check. */
+ if (!lp_local_master()) {
+ DEBUG(0,("become_local_master_browser: Samba not configured as a local master browser.\n"));
+ return;
+ }
+
+ if(!AM_POTENTIAL_MASTER_BROWSER(work)) {
+ DEBUG(2,("become_local_master_browser: Awaiting potential browser state. Current state is %d\n",
+ work->mst_state ));
+ return;
+ }
+
+ if(find_server_in_workgroup( work, global_myname()) == NULL) {
+ DEBUG(0,("become_local_master_browser: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ global_myname(), work->work_group, subrec->subnet_name));
+ return;
+ }
+
+ DEBUG(2,("become_local_master_browser: Starting to become a master browser for workgroup \
+%s on subnet %s\n", work->work_group, subrec->subnet_name));
+
+ DEBUG(3,("become_local_master_browser: first stage - attempt to register ^1^2__MSBROWSE__^2^1\n"));
+ work->mst_state = MST_BACKUP; /* an election win was successful */
+
+ work->ElectionCriterion |= 0x5;
+
+ /* Tell the namelist writer to write out a change. */
+ subrec->work_changed = True;
+
+ /* Setup the userdata_struct. */
+ if((userdata = (struct userdata_struct *)malloc(size)) == NULL) {
+ DEBUG(0,("become_local_master_browser: malloc fail.\n"));
+ return;
+ }
+
+ userdata->copy_fn = NULL;
+ userdata->free_fn = NULL;
+ userdata->userdata_len = strlen(work->work_group)+1;
+ overmalloc_safe_strcpy(userdata->data, work->work_group, size - sizeof(*userdata) - 1);
+
+ /* Register the special browser group name. */
+ register_name(subrec, MSBROWSE, 0x01, samba_nb_type|NB_GROUP,
+ become_local_master_stage1,
+ become_local_master_fail1,
+ userdata);
+
+ zero_free(userdata, size);
+}
+
+/***************************************************************
+ Utility function to set the local master browser name. Does
+ some sanity checking as old versions of Samba seem to sometimes
+ say that the master browser name for a workgroup is the same
+ as the workgroup name.
+****************************************************************/
+
+void set_workgroup_local_master_browser_name( struct work_record *work, const char *newname)
+{
+ DEBUG(5,("set_workgroup_local_master_browser_name: setting local master name to '%s' \
+for workgroup %s.\n", newname, work->work_group ));
+
+#if 0
+ /*
+ * Apparently some sites use the workgroup name as the local
+ * master browser name. Arrrrggghhhhh ! (JRA).
+ */
+ if(strequal( work->work_group, newname))
+ {
+ DEBUG(5, ("set_workgroup_local_master_browser_name: Refusing to set \
+local_master_browser_name for workgroup %s to workgroup name.\n",
+ work->work_group ));
+ return;
+ }
+#endif
+
+ unstrcpy(work->local_master_browser_name, newname);
+}
diff --git a/source/nmbd/nmbd_browserdb.c b/source/nmbd/nmbd_browserdb.c
new file mode 100644
index 00000000000..c92513fae81
--- /dev/null
+++ b/source/nmbd/nmbd_browserdb.c
@@ -0,0 +1,175 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-1998
+ Copyright (C) Christopher R. Hertel 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/* -------------------------------------------------------------------------- **
+ * Modified July 1998 by CRH.
+ * I converted this module to use the canned doubly-linked lists. I also
+ * added comments above the functions where possible.
+ */
+
+#include "includes.h"
+
+/* -------------------------------------------------------------------------- **
+ * Variables...
+ *
+ * lmb_browserlist - This is our local master browser list.
+ */
+
+ubi_dlNewList( lmb_browserlist );
+
+/* -------------------------------------------------------------------------- **
+ * Functions...
+ */
+
+/* ************************************************************************** **
+ * Remove and free a browser list entry.
+ *
+ * Input: browc - A pointer to the entry to be removed from the list and
+ * freed.
+ * Output: none.
+ *
+ * ************************************************************************** **
+ */
+static void remove_lmb_browser_entry( struct browse_cache_record *browc )
+{
+ safe_free( ubi_dlRemThis( lmb_browserlist, browc ) );
+}
+
+/* ************************************************************************** **
+ * Update a browser death time.
+ *
+ * Input: browc - Pointer to the entry to be updated.
+ * Output: none.
+ *
+ * ************************************************************************** **
+ */
+void update_browser_death_time( struct browse_cache_record *browc )
+{
+ /* Allow the new lmb to miss an announce period before we remove it. */
+ browc->death_time = time(NULL) + ( (CHECK_TIME_MST_ANNOUNCE + 2) * 60 );
+}
+
+/* ************************************************************************** **
+ * Create a browser entry and add it to the local master browser list.
+ *
+ * Input: work_name
+ * browser_name
+ * ip
+ *
+ * Output: Pointer to the new entry, or NULL if malloc() failed.
+ *
+ * ************************************************************************** **
+ */
+struct browse_cache_record *create_browser_in_lmb_cache( const char *work_name,
+ const char *browser_name,
+ struct in_addr ip )
+{
+ struct browse_cache_record *browc;
+ time_t now = time( NULL );
+
+ browc = (struct browse_cache_record *)malloc( sizeof( *browc ) );
+
+ if( NULL == browc ) {
+ DEBUG( 0, ("create_browser_in_lmb_cache: malloc fail !\n") );
+ return( NULL );
+ }
+
+ memset( (char *)browc, '\0', sizeof( *browc ) );
+
+ /* For a new lmb entry we want to sync with it after one minute. This
+ will allow it time to send out a local announce and build its
+ browse list.
+ */
+
+ browc->sync_time = now + 60;
+
+ /* Allow the new lmb to miss an announce period before we remove it. */
+ browc->death_time = now + ( (CHECK_TIME_MST_ANNOUNCE + 2) * 60 );
+
+ unstrcpy( browc->lmb_name, browser_name);
+ unstrcpy( browc->work_group, work_name);
+ strupper_m( browc->lmb_name );
+ strupper_m( browc->work_group );
+
+ browc->ip = ip;
+
+ (void)ubi_dlAddTail( lmb_browserlist, browc );
+
+ if( DEBUGLVL( 3 ) ) {
+ Debug1( "nmbd_browserdb:create_browser_in_lmb_cache()\n" );
+ Debug1( " Added lmb cache entry for workgroup %s ", browc->work_group );
+ Debug1( "name %s IP %s ", browc->lmb_name, inet_ntoa(ip) );
+ Debug1( "ttl %d\n", (int)browc->death_time );
+ }
+
+ return( browc );
+}
+
+/* ************************************************************************** **
+ * Find a browser entry in the local master browser list.
+ *
+ * Input: browser_name - The name for which to search.
+ *
+ * Output: A pointer to the matching entry, or NULL if no match was found.
+ *
+ * ************************************************************************** **
+ */
+struct browse_cache_record *find_browser_in_lmb_cache( const char *browser_name )
+{
+ struct browse_cache_record *browc;
+
+ for( browc = (struct browse_cache_record *)ubi_dlFirst( lmb_browserlist );
+ browc; browc = (struct browse_cache_record *)ubi_dlNext( browc ) )
+ if( strequal( browser_name, browc->lmb_name ) )
+ break;
+
+ return( browc );
+}
+
+/* ************************************************************************** **
+ * Expire timed out browsers in the browserlist.
+ *
+ * Input: t - Expiration time. Entries with death times less than this
+ * value will be removed from the list.
+ * Output: none.
+ *
+ * ************************************************************************** **
+ */
+void expire_lmb_browsers( time_t t )
+{
+ struct browse_cache_record *browc;
+ struct browse_cache_record *nextbrowc;
+
+ for( browc = (struct browse_cache_record *)ubi_dlFirst( lmb_browserlist );
+ browc; browc = nextbrowc ) {
+ nextbrowc = (struct browse_cache_record *)ubi_dlNext( browc );
+
+ if( browc->death_time < t ) {
+ if( DEBUGLVL( 3 ) ) {
+ Debug1( "nmbd_browserdb:expire_lmb_browsers()\n" );
+ Debug1( " Removing timed out lmb entry %s\n", browc->lmb_name );
+ }
+ remove_lmb_browser_entry( browc );
+ }
+ }
+}
diff --git a/source/nmbd/nmbd_browsesync.c b/source/nmbd/nmbd_browsesync.c
new file mode 100644
index 00000000000..15827e21bae
--- /dev/null
+++ b/source/nmbd/nmbd_browsesync.c
@@ -0,0 +1,676 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+
+/* This is our local master browser list database. */
+extern ubi_dlList lmb_browserlist[];
+
+/****************************************************************************
+As a domain master browser, do a sync with a local master browser.
+**************************************************************************/
+
+static void sync_with_lmb(struct browse_cache_record *browc)
+{
+ struct work_record *work;
+
+ if( !(work = find_workgroup_on_subnet(unicast_subnet, browc->work_group)) ) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "sync_with_lmb:\n" );
+ dbgtext( "Failed to get a workgroup for a local master browser " );
+ dbgtext( "cache entry workgroup " );
+ dbgtext( "%s, server %s\n", browc->work_group, browc->lmb_name );
+ }
+ return;
+ }
+
+ /* We should only be doing this if we are a domain master browser for
+ the given workgroup. Ensure this is so. */
+
+ if(!AM_DOMAIN_MASTER_BROWSER(work)) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "sync_with_lmb:\n" );
+ dbgtext( "We are trying to sync with a local master browser " );
+ dbgtext( "%s for workgroup %s\n", browc->lmb_name, browc->work_group );
+ dbgtext( "and we are not a domain master browser on this workgroup.\n" );
+ dbgtext( "Error!\n" );
+ }
+ return;
+ }
+
+ if( DEBUGLVL( 2 ) ) {
+ dbgtext( "sync_with_lmb:\n" );
+ dbgtext( "Initiating sync with local master browser " );
+ dbgtext( "%s<0x20> at IP %s ", browc->lmb_name, inet_ntoa(browc->ip) );
+ dbgtext( "for workgroup %s\n", browc->work_group );
+ }
+
+ sync_browse_lists(work, browc->lmb_name, 0x20, browc->ip, True, True);
+
+ browc->sync_time += (CHECK_TIME_DMB_TO_LMB_SYNC * 60);
+}
+
+/****************************************************************************
+Sync or expire any local master browsers.
+**************************************************************************/
+
+void dmb_expire_and_sync_browser_lists(time_t t)
+{
+ static time_t last_run = 0;
+ struct browse_cache_record *browc;
+
+ /* Only do this every 20 seconds. */
+ if (t - last_run < 20)
+ return;
+
+ last_run = t;
+
+ expire_lmb_browsers(t);
+
+ for( browc = (struct browse_cache_record *)ubi_dlFirst( lmb_browserlist );
+ browc;
+ browc = (struct browse_cache_record *)ubi_dlNext( browc ) ) {
+ if (browc->sync_time < t)
+ sync_with_lmb(browc);
+ }
+}
+
+/****************************************************************************
+As a local master browser, send an announce packet to the domain master browser.
+**************************************************************************/
+
+static void announce_local_master_browser_to_domain_master_browser( struct work_record *work)
+{
+ pstring outbuf;
+ unstring myname;
+ char *p;
+
+ if(ismyip(work->dmb_addr)) {
+ if( DEBUGLVL( 2 ) ) {
+ dbgtext( "announce_local_master_browser_to_domain_master_browser:\n" );
+ dbgtext( "We are both a domain and a local master browser for " );
+ dbgtext( "workgroup %s. ", work->work_group );
+ dbgtext( "Do not announce to ourselves.\n" );
+ }
+ return;
+ }
+
+ memset(outbuf,'\0',sizeof(outbuf));
+ p = outbuf;
+ SCVAL(p,0,ANN_MasterAnnouncement);
+ p++;
+
+ unstrcpy(myname, global_myname());
+ strupper_m(myname);
+ myname[15]='\0';
+ /* The call below does CH_UNIX -> CH_DOS conversion. JRA */
+ push_pstring_base(p, myname, outbuf);
+
+ p = skip_string(p,1);
+
+ if( DEBUGLVL( 4 ) ) {
+ dbgtext( "announce_local_master_browser_to_domain_master_browser:\n" );
+ dbgtext( "Sending local master announce to " );
+ dbgtext( "%s for workgroup %s.\n", nmb_namestr(&work->dmb_name),
+ work->work_group );
+ }
+
+ send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf),
+ global_myname(), 0x0, work->dmb_name.name, 0x0,
+ work->dmb_addr, FIRST_SUBNET->myip, DGRAM_PORT);
+}
+
+/****************************************************************************
+As a local master browser, do a sync with a domain master browser.
+**************************************************************************/
+
+static void sync_with_dmb(struct work_record *work)
+{
+ unstring dmb_name;
+
+ if( DEBUGLVL( 2 ) ) {
+ dbgtext( "sync_with_dmb:\n" );
+ dbgtext( "Initiating sync with domain master browser " );
+ dbgtext( "%s ", nmb_namestr(&work->dmb_name) );
+ dbgtext( "at IP %s ", inet_ntoa(work->dmb_addr) );
+ dbgtext( "for workgroup %s\n", work->work_group );
+ }
+
+ pull_ascii_nstring(dmb_name, sizeof(dmb_name), work->dmb_name.name);
+ sync_browse_lists(work, dmb_name, work->dmb_name.name_type,
+ work->dmb_addr, False, True);
+}
+
+/****************************************************************************
+ Function called when a node status query to a domain master browser IP succeeds.
+****************************************************************************/
+
+static void domain_master_node_status_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct res_rec *answers,
+ struct in_addr from_ip)
+{
+ struct work_record *work = find_workgroup_on_subnet( subrec, userdata->data);
+
+ if( work == NULL ) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "domain_master_node_status_success:\n" );
+ dbgtext( "Unable to find workgroup " );
+ dbgtext( "%s on subnet %s.\n", userdata->data, subrec->subnet_name );
+ }
+ return;
+ }
+
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "domain_master_node_status_success:\n" );
+ dbgtext( "Success in node status for workgroup " );
+ dbgtext( "%s from ip %s\n", work->work_group, inet_ntoa(from_ip) );
+ }
+
+ /* Go through the list of names found at answers->rdata and look for
+ the first SERVER<0x20> name. */
+
+ if(answers->rdata != NULL) {
+ char *p = answers->rdata;
+ int numnames = CVAL(p, 0);
+
+ p += 1;
+
+ while (numnames--) {
+ unstring qname;
+ uint16 nb_flags;
+ int name_type;
+
+ pull_ascii_nstring(qname, sizeof(qname), p);
+ name_type = CVAL(p,15);
+ nb_flags = get_nb_flags(&p[16]);
+ trim_char(qname,'\0',' ');
+
+ p += 18;
+
+ if(!(nb_flags & NB_GROUP) && (name_type == 0x20)) {
+ struct nmb_name nmbname;
+
+ make_nmb_name(&nmbname, qname, name_type);
+
+ /* Copy the dmb name and IP address
+ into the workgroup struct. */
+
+ work->dmb_name = nmbname;
+ putip((char *)&work->dmb_addr, &from_ip);
+
+ /* Do the local master browser announcement to the domain
+ master browser name and IP. */
+ announce_local_master_browser_to_domain_master_browser( work );
+
+ /* Now synchronise lists with the domain master browser. */
+ sync_with_dmb(work);
+ break;
+ }
+ }
+ } else if( DEBUGLVL( 0 ) ) {
+ dbgtext( "domain_master_node_status_success:\n" );
+ dbgtext( "Failed to find a SERVER<0x20> name in reply from IP " );
+ dbgtext( "%s.\n", inet_ntoa(from_ip) );
+ }
+}
+
+/****************************************************************************
+ Function called when a node status query to a domain master browser IP fails.
+****************************************************************************/
+
+static void domain_master_node_status_fail(struct subnet_record *subrec,
+ struct response_record *rrec)
+{
+ struct userdata_struct *userdata = rrec->userdata;
+
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "domain_master_node_status_fail:\n" );
+ dbgtext( "Doing a node status request to the domain master browser\n" );
+ dbgtext( "for workgroup %s ", userdata ? userdata->data : "NULL" );
+ dbgtext( "at IP %s failed.\n", inet_ntoa(rrec->packet->ip) );
+ dbgtext( "Cannot sync browser lists.\n" );
+ }
+}
+
+/****************************************************************************
+ Function called when a query for a WORKGROUP<1b> name succeeds.
+****************************************************************************/
+
+static void find_domain_master_name_query_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata_in,
+ struct nmb_name *q_name, struct in_addr answer_ip, struct res_rec *rrec)
+{
+ /*
+ * Unfortunately, finding the IP address of the Domain Master Browser,
+ * as we have here, is not enough. We need to now do a sync to the
+ * SERVERNAME<0x20> NetBIOS name, as only recent NT servers will
+ * respond to the SMBSERVER name. To get this name from IP
+ * address we do a Node status request, and look for the first
+ * NAME<0x20> in the response, and take that as the server name.
+ * We also keep a cache of the Domain Master Browser name for this
+ * workgroup in the Workgroup struct, so that if the same IP addess
+ * is returned every time, we don't need to do the node status
+ * request.
+ */
+
+ struct work_record *work;
+ struct nmb_name nmbname;
+ struct userdata_struct *userdata;
+ size_t size = sizeof(struct userdata_struct) + sizeof(fstring)+1;
+ unstring qname;
+
+ pull_ascii_nstring(qname, sizeof(qname), q_name->name);
+ if( !(work = find_workgroup_on_subnet(subrec, qname)) ) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "find_domain_master_name_query_success:\n" );
+ dbgtext( "Failed to find workgroup %s\n", qname);
+ }
+ return;
+ }
+
+ /* First check if we already have a dmb for this workgroup. */
+
+ if(!is_zero_ip(work->dmb_addr) && ip_equal(work->dmb_addr, answer_ip)) {
+ /* Do the local master browser announcement to the domain
+ master browser name and IP. */
+ announce_local_master_browser_to_domain_master_browser( work );
+
+ /* Now synchronise lists with the domain master browser. */
+ sync_with_dmb(work);
+ return;
+ } else {
+ zero_ip(&work->dmb_addr);
+ }
+
+ /* Now initiate the node status request. */
+
+ /* We used to use the name "*",0x0 here, but some Windows
+ * servers don't answer that name. However we *know* they
+ * have the name workgroup#1b (as we just looked it up).
+ * So do the node status request on this name instead.
+ * Found at LBL labs. JRA.
+ */
+
+ make_nmb_name(&nmbname,work->work_group,0x1b);
+
+ /* Put the workgroup name into the userdata so we know
+ what workgroup we're talking to when the reply comes
+ back. */
+
+ /* Setup the userdata_struct - this is copied so we can use
+ a stack variable for this. */
+
+ if((userdata = (struct userdata_struct *)malloc(size)) == NULL) {
+ DEBUG(0, ("find_domain_master_name_query_success: malloc fail.\n"));
+ return;
+ }
+
+ userdata->copy_fn = NULL;
+ userdata->free_fn = NULL;
+ userdata->userdata_len = strlen(work->work_group)+1;
+ overmalloc_safe_strcpy(userdata->data, work->work_group, size - sizeof(*userdata) - 1);
+
+ node_status( subrec, &nmbname, answer_ip,
+ domain_master_node_status_success,
+ domain_master_node_status_fail,
+ userdata);
+
+ zero_free(userdata, size);
+}
+
+/****************************************************************************
+ Function called when a query for a WORKGROUP<1b> name fails.
+ ****************************************************************************/
+
+static void find_domain_master_name_query_fail(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *question_name, int fail_code)
+{
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "find_domain_master_name_query_fail:\n" );
+ dbgtext( "Unable to find the Domain Master Browser name " );
+ dbgtext( "%s for the workgroup %s.\n",
+ nmb_namestr(question_name), question_name->name );
+ dbgtext( "Unable to sync browse lists in this workgroup.\n" );
+ }
+}
+
+/****************************************************************************
+As a local master browser for a workgroup find the domain master browser
+name, announce ourselves as local master browser to it and then pull the
+full domain browse lists from it onto the given subnet.
+**************************************************************************/
+
+void announce_and_sync_with_domain_master_browser( struct subnet_record *subrec,
+ struct work_record *work)
+{
+ /* Only do this if we are using a WINS server. */
+ if(we_are_a_wins_client() == False) {
+ if( DEBUGLVL( 10 ) ) {
+ dbgtext( "announce_and_sync_with_domain_master_browser:\n" );
+ dbgtext( "Ignoring, as we are not a WINS client.\n" );
+ }
+ return;
+ }
+
+ /* First, query for the WORKGROUP<1b> name from the WINS server. */
+ query_name(unicast_subnet, work->work_group, 0x1b,
+ find_domain_master_name_query_success,
+ find_domain_master_name_query_fail,
+ NULL);
+}
+
+/****************************************************************************
+ Function called when a node status query to a domain master browser IP succeeds.
+ This function is only called on query to a Samba 1.9.18 or above WINS server.
+
+ Note that adding the workgroup name is enough for this workgroup to be
+ browsable by clients, as clients query the WINS server or broadcast
+ nets for the WORKGROUP<1b> name when they want to browse a workgroup
+ they are not in. We do not need to do a sync with this Domain Master
+ Browser in order for our browse clients to see machines in this workgroup.
+ JRA.
+****************************************************************************/
+
+static void get_domain_master_name_node_status_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct res_rec *answers,
+ struct in_addr from_ip)
+{
+ struct work_record *work;
+ unstring server_name;
+
+ server_name[0] = 0;
+
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "get_domain_master_name_node_status_success:\n" );
+ dbgtext( "Success in node status from ip %s\n", inet_ntoa(from_ip) );
+ }
+
+ /*
+ * Go through the list of names found at answers->rdata and look for
+ * the first WORKGROUP<0x1b> name.
+ */
+
+ if(answers->rdata != NULL) {
+ char *p = answers->rdata;
+ int numnames = CVAL(p, 0);
+
+ p += 1;
+
+ while (numnames--) {
+ unstring qname;
+ uint16 nb_flags;
+ int name_type;
+
+ pull_ascii_nstring(qname, sizeof(qname), p);
+ name_type = CVAL(p,15);
+ nb_flags = get_nb_flags(&p[16]);
+ trim_char(qname,'\0',' ');
+
+ p += 18;
+
+ if(!(nb_flags & NB_GROUP) && (name_type == 0x00) &&
+ server_name[0] == 0) {
+ /* this is almost certainly the server netbios name */
+ unstrcpy(server_name, qname);
+ continue;
+ }
+
+ if(!(nb_flags & NB_GROUP) && (name_type == 0x1b)) {
+ if( DEBUGLVL( 5 ) ) {
+ dbgtext( "get_domain_master_name_node_status_success:\n" );
+ dbgtext( "%s(%s) ", server_name, inet_ntoa(from_ip) );
+ dbgtext( "is a domain master browser for workgroup " );
+ dbgtext( "%s. Adding this name.\n", qname );
+ }
+
+ /*
+ * If we don't already know about this workgroup, add it
+ * to the workgroup list on the unicast_subnet.
+ */
+
+ if((work = find_workgroup_on_subnet( subrec, qname)) == NULL) {
+ struct nmb_name nmbname;
+ /*
+ * Add it - with an hour in the cache.
+ */
+ if(!(work= create_workgroup_on_subnet(subrec, qname, 60*60)))
+ return;
+
+ /* remember who the master is */
+ unstrcpy(work->local_master_browser_name, server_name);
+ make_nmb_name(&nmbname, server_name, 0x20);
+ work->dmb_name = nmbname;
+ work->dmb_addr = from_ip;
+ }
+ break;
+ }
+ }
+ } else if( DEBUGLVL( 0 ) ) {
+ dbgtext( "get_domain_master_name_node_status_success:\n" );
+ dbgtext( "Failed to find a WORKGROUP<0x1b> name in reply from IP " );
+ dbgtext( "%s.\n", inet_ntoa(from_ip) );
+ }
+}
+
+/****************************************************************************
+ Function called when a node status query to a domain master browser IP fails.
+****************************************************************************/
+
+static void get_domain_master_name_node_status_fail(struct subnet_record *subrec,
+ struct response_record *rrec)
+{
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "get_domain_master_name_node_status_fail:\n" );
+ dbgtext( "Doing a node status request to the domain master browser " );
+ dbgtext( "at IP %s failed.\n", inet_ntoa(rrec->packet->ip) );
+ dbgtext( "Cannot get workgroup name.\n" );
+ }
+}
+
+/****************************************************************************
+ Function called when a query for *<1b> name succeeds.
+****************************************************************************/
+
+static void find_all_domain_master_names_query_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata_in,
+ struct nmb_name *q_name, struct in_addr answer_ip, struct res_rec *rrec)
+{
+ /*
+ * We now have a list of all the domain master browsers for all workgroups
+ * that have registered with the WINS server. Now do a node status request
+ * to each one and look for the first 1b name in the reply. This will be
+ * the workgroup name that we will add to the unicast subnet as a 'non-local'
+ * workgroup.
+ */
+
+ struct nmb_name nmbname;
+ struct in_addr send_ip;
+ int i;
+
+ if( DEBUGLVL( 5 ) ) {
+ dbgtext( "find_all_domain_master_names_query_succes:\n" );
+ dbgtext( "Got answer from WINS server of %d ", (rrec->rdlength / 6) );
+ dbgtext( "IP addresses for Domain Master Browsers.\n" );
+ }
+
+ for(i = 0; i < rrec->rdlength / 6; i++) {
+ /* Initiate the node status requests. */
+ make_nmb_name(&nmbname, "*", 0);
+
+ putip((char *)&send_ip, (char *)&rrec->rdata[(i*6) + 2]);
+
+ /*
+ * Don't send node status requests to ourself.
+ */
+
+ if(ismyip( send_ip )) {
+ if( DEBUGLVL( 5 ) ) {
+ dbgtext( "find_all_domain_master_names_query_succes:\n" );
+ dbgtext( "Not sending node status to our own IP " );
+ dbgtext( "%s.\n", inet_ntoa(send_ip) );
+ }
+ continue;
+ }
+
+ if( DEBUGLVL( 5 ) ) {
+ dbgtext( "find_all_domain_master_names_query_success:\n" );
+ dbgtext( "Sending node status request to IP %s.\n", inet_ntoa(send_ip) );
+ }
+
+ node_status( subrec, &nmbname, send_ip,
+ get_domain_master_name_node_status_success,
+ get_domain_master_name_node_status_fail,
+ NULL);
+ }
+}
+
+/****************************************************************************
+ Function called when a query for *<1b> name fails.
+ ****************************************************************************/
+static void find_all_domain_master_names_query_fail(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *question_name, int fail_code)
+{
+ if( DEBUGLVL( 10 ) ) {
+ dbgtext( "find_domain_master_name_query_fail:\n" );
+ dbgtext( "WINS server did not reply to a query for name " );
+ dbgtext( "%s.\nThis means it ", nmb_namestr(question_name) );
+ dbgtext( "is probably not a Samba 1.9.18 or above WINS server.\n" );
+ }
+}
+
+/****************************************************************************
+ If we are a domain master browser on the unicast subnet, do a query to the
+ WINS server for the *<1b> name. This will only work to a Samba WINS server,
+ so ignore it if we fail. If we succeed, contact each of the IP addresses in
+ turn and do a node status request to them. If this succeeds then look for a
+ <1b> name in the reply - this is the workgroup name. Add this to the unicast
+ subnet. This is expensive, so we only do this every 15 minutes.
+**************************************************************************/
+
+void collect_all_workgroup_names_from_wins_server(time_t t)
+{
+ static time_t lastrun = 0;
+ struct work_record *work;
+
+ /* Only do this if we are using a WINS server. */
+ if(we_are_a_wins_client() == False)
+ return;
+
+ /* Check to see if we are a domain master browser on the unicast subnet. */
+ if((work = find_workgroup_on_subnet( unicast_subnet, lp_workgroup())) == NULL) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "collect_all_workgroup_names_from_wins_server:\n" );
+ dbgtext( "Cannot find my workgroup %s ", lp_workgroup() );
+ dbgtext( "on subnet %s.\n", unicast_subnet->subnet_name );
+ }
+ return;
+ }
+
+ if(!AM_DOMAIN_MASTER_BROWSER(work))
+ return;
+
+ if ((lastrun != 0) && (t < lastrun + (15 * 60)))
+ return;
+
+ lastrun = t;
+
+ /* First, query for the *<1b> name from the WINS server. */
+ query_name(unicast_subnet, "*", 0x1b,
+ find_all_domain_master_names_query_success,
+ find_all_domain_master_names_query_fail,
+ NULL);
+}
+
+
+/****************************************************************************
+ If we are a domain master browser on the unicast subnet, do a regular sync
+ with all other DMBs that we know of on that subnet.
+
+To prevent exponential network traffic with large numbers of workgroups
+we use a randomised system where sync probability is inversely proportional
+to the number of known workgroups
+**************************************************************************/
+
+void sync_all_dmbs(time_t t)
+{
+ static time_t lastrun = 0;
+ struct work_record *work;
+ int count=0;
+
+ /* Only do this if we are using a WINS server. */
+ if(we_are_a_wins_client() == False)
+ return;
+
+ /* Check to see if we are a domain master browser on the
+ unicast subnet. */
+ work = find_workgroup_on_subnet(unicast_subnet, lp_workgroup());
+ if (!work)
+ return;
+
+ if (!AM_DOMAIN_MASTER_BROWSER(work))
+ return;
+
+ if ((lastrun != 0) && (t < lastrun + (5 * 60)))
+ return;
+
+ /* count how many syncs we might need to do */
+ for (work=unicast_subnet->workgrouplist; work; work = work->next) {
+ if (strcmp(lp_workgroup(), work->work_group)) {
+ count++;
+ }
+ }
+
+ /* sync with a probability of 1/count */
+ for (work=unicast_subnet->workgrouplist; work; work = work->next) {
+ if (strcmp(lp_workgroup(), work->work_group)) {
+ unstring dmb_name;
+
+ if (((unsigned)sys_random()) % count != 0)
+ continue;
+
+ lastrun = t;
+
+ if (!work->dmb_name.name[0]) {
+ /* we don't know the DMB - assume it is
+ the same as the unicast local master */
+ make_nmb_name(&work->dmb_name,
+ work->local_master_browser_name,
+ 0x20);
+ }
+
+ pull_ascii_nstring(dmb_name, sizeof(dmb_name), work->dmb_name.name);
+
+ DEBUG(3,("Initiating DMB<->DMB sync with %s(%s)\n",
+ dmb_name, inet_ntoa(work->dmb_addr)));
+
+ sync_browse_lists(work,
+ dmb_name,
+ work->dmb_name.name_type,
+ work->dmb_addr, False, False);
+ }
+ }
+}
diff --git a/source/nmbd/nmbd_elections.c b/source/nmbd/nmbd_elections.c
new file mode 100644
index 00000000000..470cf4277b5
--- /dev/null
+++ b/source/nmbd/nmbd_elections.c
@@ -0,0 +1,391 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+
+/* Election parameters. */
+extern time_t StartupTime;
+
+/****************************************************************************
+ Send an election datagram packet.
+**************************************************************************/
+
+static void send_election_dgram(struct subnet_record *subrec, const char *workgroup_name,
+ uint32 criterion, int timeup,const char *server_name)
+{
+ pstring outbuf;
+ unstring srv_name;
+ char *p;
+
+ DEBUG(2,("send_election_dgram: Sending election packet for workgroup %s on subnet %s\n",
+ workgroup_name, subrec->subnet_name ));
+
+ memset(outbuf,'\0',sizeof(outbuf));
+ p = outbuf;
+ SCVAL(p,0,ANN_Election); /* Election opcode. */
+ p++;
+
+ SCVAL(p,0,((criterion == 0 && timeup == 0) ? 0 : ELECTION_VERSION));
+ SIVAL(p,1,criterion);
+ SIVAL(p,5,timeup*1000); /* ms - Despite what the spec says. */
+ p += 13;
+ unstrcpy(srv_name, server_name);
+ strupper_m(srv_name);
+ /* The following call does UNIX -> DOS charset conversion. */
+ pstrcpy_base(p, srv_name, outbuf);
+ p = skip_string(p,1);
+
+ send_mailslot(False, BROWSE_MAILSLOT, outbuf, PTR_DIFF(p,outbuf),
+ global_myname(), 0,
+ workgroup_name, 0x1e,
+ subrec->bcast_ip, subrec->myip, DGRAM_PORT);
+}
+
+/*******************************************************************
+ We found a current master browser on one of our broadcast interfaces.
+******************************************************************/
+
+static void check_for_master_browser_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *answer_name,
+ struct in_addr answer_ip, struct res_rec *rrec)
+{
+ unstring aname;
+ pull_ascii_nstring(aname, sizeof(aname), answer_name->name);
+ DEBUG(3,("check_for_master_browser_success: Local master browser for workgroup %s exists at \
+IP %s (just checking).\n", aname, inet_ntoa(answer_ip) ));
+}
+
+/*******************************************************************
+ We failed to find a current master browser on one of our broadcast interfaces.
+******************************************************************/
+
+static void check_for_master_browser_fail( struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *question_name,
+ int fail_code)
+{
+ unstring workgroup_name;
+ struct work_record *work;
+
+ pull_ascii_nstring(workgroup_name,sizeof(workgroup_name),question_name->name);
+
+ work = find_workgroup_on_subnet(subrec, workgroup_name);
+ if(work == NULL) {
+ DEBUG(0,("check_for_master_browser_fail: Unable to find workgroup %s on subnet %s.=\n",
+ workgroup_name, subrec->subnet_name ));
+ return;
+ }
+
+ if (strequal(work->work_group, lp_workgroup())) {
+
+ if (lp_local_master()) {
+ /* We have discovered that there is no local master
+ browser, and we are configured to initiate
+ an election that we will participate in.
+ */
+ DEBUG(2,("check_for_master_browser_fail: Forcing election on workgroup %s subnet %s\n",
+ work->work_group, subrec->subnet_name ));
+
+ /* Setting this means we will participate when the
+ election is run in run_elections(). */
+ work->needelection = True;
+ } else {
+ /* We need to force an election, because we are configured
+ not to become the local master, but we still need one,
+ having detected that one doesn't exist.
+ */
+ send_election_dgram(subrec, work->work_group, 0, 0, "");
+ }
+ }
+}
+
+/*******************************************************************
+ Ensure there is a local master browser for a workgroup on our
+ broadcast interfaces.
+******************************************************************/
+
+void check_master_browser_exists(time_t t)
+{
+ static time_t lastrun=0;
+ struct subnet_record *subrec;
+ const char *workgroup_name = lp_workgroup();
+
+ if (!lastrun)
+ lastrun = t;
+
+ if (t < (lastrun + (CHECK_TIME_MST_BROWSE * 60)))
+ return;
+
+ lastrun = t;
+
+ dump_workgroups(False);
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ struct work_record *work;
+
+ for (work = subrec->workgrouplist; work; work = work->next) {
+ if (strequal(work->work_group, workgroup_name) && !AM_LOCAL_MASTER_BROWSER(work)) {
+ /* Do a name query for the local master browser on this net. */
+ query_name( subrec, work->work_group, 0x1d,
+ check_for_master_browser_success,
+ check_for_master_browser_fail,
+ NULL);
+ }
+ }
+ }
+}
+
+/*******************************************************************
+ Run an election.
+******************************************************************/
+
+void run_elections(time_t t)
+{
+ static time_t lastime = 0;
+
+ struct subnet_record *subrec;
+
+ /* Send election packets once every 2 seconds - note */
+ if (lastime && (t - lastime < 2))
+ return;
+
+ lastime = t;
+
+ START_PROFILE(run_elections);
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ struct work_record *work;
+
+ for (work = subrec->workgrouplist; work; work = work->next) {
+ if (work->RunningElection) {
+ /*
+ * We can only run an election for a workgroup if we have
+ * registered the WORKGROUP<1e> name, as that's the name
+ * we must listen to.
+ */
+ struct nmb_name nmbname;
+
+ make_nmb_name(&nmbname, work->work_group, 0x1e);
+ if(find_name_on_subnet( subrec, &nmbname, FIND_SELF_NAME)==NULL) {
+ DEBUG(8,("run_elections: Cannot send election packet yet as name %s not \
+yet registered on subnet %s\n", nmb_namestr(&nmbname), subrec->subnet_name ));
+ continue;
+ }
+
+ send_election_dgram(subrec, work->work_group, work->ElectionCriterion,
+ t - StartupTime, global_myname());
+
+ if (work->ElectionCount++ >= 4) {
+ /* Won election (4 packets were sent out uncontested. */
+ DEBUG(2,("run_elections: >>> Won election for workgroup %s on subnet %s <<<\n",
+ work->work_group, subrec->subnet_name ));
+
+ work->RunningElection = False;
+
+ become_local_master_browser(subrec, work);
+ }
+ }
+ }
+ }
+ END_PROFILE(run_elections);
+}
+
+/*******************************************************************
+ Determine if I win an election.
+******************************************************************/
+
+static BOOL win_election(struct work_record *work, int version,
+ uint32 criterion, int timeup, const char *server_name)
+{
+ int mytimeup = time(NULL) - StartupTime;
+ uint32 mycriterion = work->ElectionCriterion;
+
+ /* If local master is false then never win in election broadcasts. */
+ if(!lp_local_master()) {
+ DEBUG(3,("win_election: Losing election as local master == False\n"));
+ return False;
+ }
+
+ DEBUG(4,("win_election: election comparison: %x:%x %x:%x %d:%d %s:%s\n",
+ version, ELECTION_VERSION,
+ criterion, mycriterion,
+ timeup, mytimeup,
+ server_name, global_myname()));
+
+ if (version > ELECTION_VERSION)
+ return(False);
+ if (version < ELECTION_VERSION)
+ return(True);
+
+ if (criterion > mycriterion)
+ return(False);
+ if (criterion < mycriterion)
+ return(True);
+
+ if (timeup > mytimeup)
+ return(False);
+ if (timeup < mytimeup)
+ return(True);
+
+ if (StrCaseCmp(global_myname(), server_name) > 0)
+ return(False);
+
+ return(True);
+}
+
+/*******************************************************************
+ Process an incoming election datagram packet.
+******************************************************************/
+
+void process_election(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ int version = CVAL(buf,0);
+ uint32 criterion = IVAL(buf,1);
+ int timeup = IVAL(buf,5)/1000;
+ unstring server_name;
+ struct work_record *work;
+ unstring workgroup_name;
+
+ pull_ascii_nstring(server_name, sizeof(server_name), buf+13);
+ pull_ascii_nstring(workgroup_name, sizeof(workgroup_name), dgram->dest_name.name);
+
+ START_PROFILE(election);
+ server_name[15] = 0;
+
+ DEBUG(3,("process_election: Election request from %s at IP %s on subnet %s for workgroup %s.\n",
+ server_name,inet_ntoa(p->ip), subrec->subnet_name, workgroup_name ));
+
+ DEBUG(5,("process_election: vers=%d criterion=%08x timeup=%d\n", version,criterion,timeup));
+
+ if(( work = find_workgroup_on_subnet(subrec, workgroup_name)) == NULL) {
+ DEBUG(0,("process_election: Cannot find workgroup %s on subnet %s.\n",
+ workgroup_name, subrec->subnet_name ));
+ goto done;
+ }
+
+ if (!strequal(work->work_group, lp_workgroup())) {
+ DEBUG(3,("process_election: ignoring election request for workgroup %s on subnet %s as this \
+is not my workgroup.\n", work->work_group, subrec->subnet_name ));
+ goto done;
+ }
+
+ if (win_election(work, version,criterion,timeup,server_name)) {
+ /* We take precedence over the requesting server. */
+ if (!work->RunningElection) {
+ /* We weren't running an election - start running one. */
+
+ work->needelection = True;
+ work->ElectionCount=0;
+ }
+
+ /* Note that if we were running an election for this workgroup on this
+ subnet already, we just ignore the server we take precedence over. */
+ } else {
+ /* We lost. Stop participating. */
+ work->needelection = False;
+
+ if (work->RunningElection || AM_LOCAL_MASTER_BROWSER(work)) {
+ work->RunningElection = False;
+ DEBUG(3,("process_election: >>> Lost election for workgroup %s on subnet %s <<<\n",
+ work->work_group, subrec->subnet_name ));
+ if (AM_LOCAL_MASTER_BROWSER(work))
+ unbecome_local_master_browser(subrec, work, False);
+ }
+ }
+done:
+
+ END_PROFILE(election);
+}
+
+/****************************************************************************
+ This function looks over all the workgroups known on all the broadcast
+ subnets and decides if a browser election is to be run on that workgroup.
+ It returns True if any election packets need to be sent (this will then
+ be done by run_elections().
+***************************************************************************/
+
+BOOL check_elections(void)
+{
+ struct subnet_record *subrec;
+ BOOL run_any_election = False;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ struct work_record *work;
+ for (work = subrec->workgrouplist; work; work = work->next) {
+ run_any_election |= work->RunningElection;
+
+ /*
+ * Start an election if we have any chance of winning.
+ * Note this is a change to the previous code, that would
+ * only run an election if nmbd was in the potential browser
+ * state. We need to run elections in any state if we're told
+ * to. JRA.
+ */
+
+ if (work->needelection && !work->RunningElection && lp_local_master()) {
+ /*
+ * We can only run an election for a workgroup if we have
+ * registered the WORKGROUP<1e> name, as that's the name
+ * we must listen to.
+ */
+ struct nmb_name nmbname;
+
+ make_nmb_name(&nmbname, work->work_group, 0x1e);
+ if(find_name_on_subnet( subrec, &nmbname, FIND_SELF_NAME)==NULL) {
+ DEBUG(8,("check_elections: Cannot send election packet yet as name %s not \
+yet registered on subnet %s\n", nmb_namestr(&nmbname), subrec->subnet_name ));
+ continue;
+ }
+
+ DEBUG(3,("check_elections: >>> Starting election for workgroup %s on subnet %s <<<\n",
+ work->work_group, subrec->subnet_name ));
+
+ work->ElectionCount = 0;
+ work->RunningElection = True;
+ work->needelection = False;
+ }
+ }
+ }
+ return run_any_election;
+}
+
+/****************************************************************************
+ Process a internal Samba message forcing an election.
+***************************************************************************/
+
+void nmbd_message_election(int msg_type, pid_t src, void *buf, size_t len)
+{
+ struct subnet_record *subrec;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ struct work_record *work;
+ for (work = subrec->workgrouplist; work; work = work->next) {
+ if (strequal(work->work_group, lp_workgroup())) {
+ work->needelection = True;
+ work->ElectionCount=0;
+ work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE;
+ }
+ }
+ }
+}
diff --git a/source/nmbd/nmbd_incomingdgrams.c b/source/nmbd/nmbd_incomingdgrams.c
new file mode 100644
index 00000000000..53b19471572
--- /dev/null
+++ b/source/nmbd/nmbd_incomingdgrams.c
@@ -0,0 +1,844 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+
+extern BOOL found_lm_clients;
+
+#if 0
+
+/* XXXX note: This function is currently unsuitable for use, as it
+ does not properly check that a server is in a fit state to become
+ a backup browser before asking it to be one.
+ The code is left here to be worked on at a later date.
+*/
+
+/****************************************************************************
+Tell a server to become a backup browser
+**************************************************************************/
+
+void tell_become_backup(void)
+{
+ struct subnet_record *subrec;
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+ {
+ struct work_record *work;
+ for (work = subrec->workgrouplist; work; work = work->next)
+ {
+ struct server_record *servrec;
+ int num_servers = 0;
+ int num_backups = 0;
+
+ for (servrec = work->serverlist; servrec; servrec = servrec->next)
+ {
+ num_servers++;
+
+ if (is_myname(servrec->serv.name))
+ continue;
+
+ if (servrec->serv.type & SV_TYPE_BACKUP_BROWSER)
+ {
+ num_backups++;
+ continue;
+ }
+
+ if (servrec->serv.type & SV_TYPE_MASTER_BROWSER)
+ continue;
+
+ if (!(servrec->serv.type & SV_TYPE_POTENTIAL_BROWSER))
+ continue;
+
+ DEBUG(3,("num servers: %d num backups: %d\n",
+ num_servers, num_backups));
+
+ /* make first server a backup server. thereafter make every
+ tenth server a backup server */
+ if (num_backups != 0 && (num_servers+9) / num_backups > 10)
+ continue;
+
+ DEBUG(2,("sending become backup to %s %s for %s\n",
+ servrec->serv.name, inet_ntoa(subrec->bcast_ip),
+ work->work_group));
+
+ /* type 11 request from MYNAME(20) to WG(1e) for SERVER */
+ do_announce_request(servrec->serv.name, work->work_group,
+ ANN_BecomeBackup, 0x20, 0x1e, subrec->bcast_ip);
+ }
+ }
+ }
+}
+#endif
+
+/*******************************************************************
+ Process an incoming host announcement packet.
+*******************************************************************/
+
+void process_host_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ int ttl = IVAL(buf,1)/1000;
+ unstring announce_name;
+ uint32 servertype = IVAL(buf,23);
+ fstring comment;
+ struct work_record *work;
+ struct server_record *servrec;
+ unstring work_name;
+ unstring source_name;
+
+ START_PROFILE(host_announce);
+
+ pull_ascii_fstring(comment, buf+31);
+ comment[42] = 0;
+
+ pull_ascii_nstring(announce_name, sizeof(announce_name), buf+5);
+ pull_ascii_nstring(source_name, sizeof(source_name), dgram->source_name.name);
+
+ DEBUG(3,("process_host_announce: from %s<%02x> IP %s to \
+%s for server %s.\n", source_name, source_name[15], inet_ntoa(p->ip),
+ nmb_namestr(&dgram->dest_name),announce_name));
+
+ DEBUG(5,("process_host_announce: ttl=%d server type=%08x comment=%s\n",
+ ttl, servertype,comment));
+
+ /* Filter servertype to remove impossible bits. */
+ servertype &= ~(SV_TYPE_LOCAL_LIST_ONLY|SV_TYPE_DOMAIN_ENUM);
+
+ /* A host announcement must be sent to the name WORKGROUP<1d>. */
+ if(dgram->dest_name.name_type != 0x1d) {
+ DEBUG(2,("process_host_announce: incorrect name type for destination from IP %s \
+(was %02x) should be 0x1d. Allowing packet anyway.\n",
+ inet_ntoa(p->ip), dgram->dest_name.name_type));
+ /* Change it so it was. */
+ dgram->dest_name.name_type = 0x1d;
+ }
+
+ /* For a host announce the workgroup name is the destination name. */
+ pull_ascii_nstring(work_name, sizeof(work_name), dgram->dest_name.name);
+
+ /*
+ * Syntax servers version 5.1 send HostAnnounce packets to
+ * *THE WRONG NAME*. They send to LOCAL_MASTER_BROWSER_NAME<00>
+ * instead of WORKGROUP<1d> name. So to fix this we check if
+ * the workgroup name is our own name, and if so change it
+ * to be our primary workgroup name.
+ */
+
+ if(strequal(work_name, global_myname()))
+ unstrcpy(work_name,lp_workgroup());
+
+ /*
+ * We are being very agressive here in adding a workgroup
+ * name on the basis of a host announcing itself as being
+ * in that workgroup. Maybe we should wait for the workgroup
+ * announce instead ? JRA.
+ */
+
+ work = find_workgroup_on_subnet(subrec, work_name);
+
+ if(servertype != 0) {
+ if (work ==NULL ) {
+ /* We have no record of this workgroup. Add it. */
+ if((work = create_workgroup_on_subnet(subrec, work_name, ttl))==NULL)
+ goto done;
+ }
+
+ if((servrec = find_server_in_workgroup( work, announce_name))==NULL) {
+ /* If this server is not already in the workgroup, add it. */
+ create_server_on_workgroup(work, announce_name,
+ servertype|SV_TYPE_LOCAL_LIST_ONLY,
+ ttl, comment);
+ } else {
+ /* Update the record. */
+ servrec->serv.type = servertype|SV_TYPE_LOCAL_LIST_ONLY;
+ update_server_ttl( servrec, ttl);
+ fstrcpy(servrec->serv.comment,comment);
+ }
+ } else {
+ /*
+ * This server is announcing it is going down. Remove it from the
+ * workgroup.
+ */
+ if(!is_myname(announce_name) && (work != NULL) &&
+ ((servrec = find_server_in_workgroup( work, announce_name))!=NULL)) {
+ remove_server_from_workgroup( work, servrec);
+ }
+ }
+
+ subrec->work_changed = True;
+done:
+
+ END_PROFILE(host_announce);
+}
+
+/*******************************************************************
+ Process an incoming WORKGROUP announcement packet.
+*******************************************************************/
+
+void process_workgroup_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ int ttl = IVAL(buf,1)/1000;
+ unstring workgroup_announce_name;
+ unstring master_name;
+ uint32 servertype = IVAL(buf,23);
+ struct work_record *work;
+ unstring source_name;
+ unstring dest_name;
+
+ START_PROFILE(workgroup_announce);
+
+ pull_ascii_nstring(workgroup_announce_name,sizeof(workgroup_announce_name),buf+5);
+ pull_ascii_nstring(master_name,sizeof(master_name),buf+31);
+ pull_ascii_nstring(source_name,sizeof(source_name),dgram->source_name.name);
+ pull_ascii_nstring(dest_name,sizeof(dest_name),dgram->dest_name.name);
+
+ DEBUG(3,("process_workgroup_announce: from %s<%02x> IP %s to \
+%s for workgroup %s.\n", source_name, source_name[15], inet_ntoa(p->ip),
+ nmb_namestr(&dgram->dest_name),workgroup_announce_name));
+
+ DEBUG(5,("process_workgroup_announce: ttl=%d server type=%08x master browser=%s\n",
+ ttl, servertype, master_name));
+
+ /* Workgroup announcements must only go to the MSBROWSE name. */
+ if (!strequal(dest_name, MSBROWSE) || (dgram->dest_name.name_type != 0x1)) {
+ DEBUG(0,("process_workgroup_announce: from IP %s should be to __MSBROWSE__<0x01> not %s\n",
+ inet_ntoa(p->ip), nmb_namestr(&dgram->dest_name)));
+ goto done;
+ }
+
+ if ((work = find_workgroup_on_subnet(subrec, workgroup_announce_name))==NULL) {
+ /* We have no record of this workgroup. Add it. */
+ if((work = create_workgroup_on_subnet(subrec, workgroup_announce_name, ttl))==NULL)
+ goto done;
+ } else {
+ /* Update the workgroup death_time. */
+ update_workgroup_ttl(work, ttl);
+ }
+
+ if(*work->local_master_browser_name == '\0') {
+ /* Set the master browser name. */
+ set_workgroup_local_master_browser_name( work, master_name );
+ }
+
+ subrec->work_changed = True;
+
+done:
+
+ END_PROFILE(workgroup_announce);
+}
+
+/*******************************************************************
+ Process an incoming local master browser announcement packet.
+*******************************************************************/
+
+void process_local_master_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ int ttl = IVAL(buf,1)/1000;
+ unstring server_name;
+ uint32 servertype = IVAL(buf,23);
+ fstring comment;
+ unstring work_name;
+ struct work_record *work;
+ struct server_record *servrec;
+ unstring source_name;
+
+ START_PROFILE(local_master_announce);
+
+ pull_ascii_nstring(server_name,sizeof(server_name),buf+5);
+ pull_ascii_fstring(comment, buf+31);
+ comment[42] = 0;
+ pull_ascii_nstring(source_name, sizeof(source_name), dgram->source_name.name);
+ pull_ascii_nstring(work_name, sizeof(work_name), dgram->dest_name.name);
+
+ DEBUG(3,("process_local_master_announce: from %s<%02x> IP %s to \
+%s for server %s.\n", source_name, source_name[15], inet_ntoa(p->ip),
+ nmb_namestr(&dgram->dest_name),server_name));
+
+ DEBUG(5,("process_local_master_announce: ttl=%d server type=%08x comment=%s\n",
+ ttl, servertype, comment));
+
+ /* A local master announcement must be sent to the name WORKGROUP<1e>. */
+ if(dgram->dest_name.name_type != 0x1e) {
+ DEBUG(0,("process_local_master_announce: incorrect name type for destination from IP %s \
+(was %02x) should be 0x1e. Ignoring packet.\n",
+ inet_ntoa(p->ip), dgram->dest_name.name_type));
+ goto done;
+ }
+
+ /* Filter servertype to remove impossible bits. */
+ servertype &= ~(SV_TYPE_LOCAL_LIST_ONLY|SV_TYPE_DOMAIN_ENUM);
+
+ /* For a local master announce the workgroup name is the destination name. */
+
+ if ((work = find_workgroup_on_subnet(subrec, work_name))==NULL) {
+ /* Don't bother adding if it's a local master release announce. */
+ if(servertype == 0)
+ goto done;
+
+ /* We have no record of this workgroup. Add it. */
+ if((work = create_workgroup_on_subnet(subrec, work_name, ttl))==NULL)
+ goto done;
+ }
+
+ /* If we think we're the local master browser for this workgroup,
+ we should never have got this packet. We don't see our own
+ packets.
+ */
+ if(AM_LOCAL_MASTER_BROWSER(work)) {
+ DEBUG(0,("process_local_master_announce: Server %s at IP %s is announcing itself as \
+a local master browser for workgroup %s and we think we are master. Forcing election.\n",
+ server_name, inet_ntoa(p->ip), work_name));
+
+ /* Samba nmbd versions 1.9.17 to 1.9.17p4 have a bug in that when
+ they have become a local master browser once, they will never
+ stop sending local master announcements. To fix this we send
+ them a reset browser packet, with level 0x2 on the __SAMBA__
+ name that only they should be listening to. */
+
+ send_browser_reset( 0x2, "__SAMBA__" , 0x20, p->ip);
+
+ /* We should demote ourself and force an election. */
+
+ unbecome_local_master_browser( subrec, work, True);
+
+ /* The actual election requests are handled in nmbd_election.c */
+ goto done;
+ }
+
+ /* Find the server record on this workgroup. If it doesn't exist, add it. */
+
+ if(servertype != 0) {
+ if((servrec = find_server_in_workgroup( work, server_name))==NULL) {
+ /* If this server is not already in the workgroup, add it. */
+ create_server_on_workgroup(work, server_name,
+ servertype|SV_TYPE_LOCAL_LIST_ONLY,
+ ttl, comment);
+ } else {
+ /* Update the record. */
+ servrec->serv.type = servertype|SV_TYPE_LOCAL_LIST_ONLY;
+ update_server_ttl(servrec, ttl);
+ fstrcpy(servrec->serv.comment,comment);
+ }
+
+ set_workgroup_local_master_browser_name( work, server_name );
+ } else {
+ /*
+ * This server is announcing it is going down. Remove it from the
+ * workgroup.
+ */
+ if(!is_myname(server_name) && (work != NULL) &&
+ ((servrec = find_server_in_workgroup( work, server_name))!=NULL)) {
+ remove_server_from_workgroup( work, servrec);
+ }
+ }
+
+ subrec->work_changed = True;
+done:
+
+ END_PROFILE(local_master_announce);
+}
+
+/*******************************************************************
+ Process a domain master announcement frame.
+ Domain master browsers receive these from local masters. The Domain
+ master should then issue a sync with the local master, asking for
+ that machines local server list.
+******************************************************************/
+
+void process_master_browser_announce(struct subnet_record *subrec,
+ struct packet_struct *p,char *buf)
+{
+ unstring local_master_name;
+ struct work_record *work;
+ struct browse_cache_record *browrec;
+
+ START_PROFILE(master_browser_announce);
+
+ pull_ascii_nstring(local_master_name,sizeof(local_master_name),buf);
+
+ DEBUG(3,("process_master_browser_announce: Local master announce from %s IP %s.\n",
+ local_master_name, inet_ntoa(p->ip)));
+
+ if (!lp_domain_master()) {
+ DEBUG(0,("process_master_browser_announce: Not configured as domain \
+master - ignoring master announce.\n"));
+ goto done;
+ }
+
+ if((work = find_workgroup_on_subnet(subrec, lp_workgroup())) == NULL) {
+ DEBUG(0,("process_master_browser_announce: Cannot find workgroup %s on subnet %s\n",
+ lp_workgroup(), subrec->subnet_name));
+ goto done;
+ }
+
+ if(!AM_DOMAIN_MASTER_BROWSER(work)) {
+ DEBUG(0,("process_master_browser_announce: Local master announce made to us from \
+%s IP %s and we are not a domain master browser.\n", local_master_name, inet_ntoa(p->ip)));
+ goto done;
+ }
+
+ /* Add this host as a local master browser entry on the browse lists.
+ This causes a sync request to be made to it at a later date.
+ */
+
+ if((browrec = find_browser_in_lmb_cache( local_master_name )) == NULL) {
+ /* Add it. */
+ create_browser_in_lmb_cache( work->work_group, local_master_name, p->ip);
+ } else {
+ update_browser_death_time(browrec);
+ }
+
+done:
+
+ END_PROFILE(master_browser_announce);
+}
+
+/*******************************************************************
+ Process an incoming LanMan host announcement packet.
+*******************************************************************/
+
+void process_lm_host_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ uint32 servertype = IVAL(buf,1);
+ int osmajor=CVAL(buf,5); /* major version of node software */
+ int osminor=CVAL(buf,6); /* minor version of node software */
+ int ttl = SVAL(buf,7);
+ unstring announce_name;
+ struct work_record *work;
+ struct server_record *servrec;
+ unstring work_name;
+ unstring source_name;
+ fstring comment;
+ char *s = buf+9;
+
+ START_PROFILE(lm_host_announce);
+ s = skip_string(s,1);
+ pull_ascii(comment, s, sizeof(fstring), 43, STR_TERMINATE);
+
+ pull_ascii_nstring(announce_name,sizeof(announce_name),buf+9);
+ pull_ascii_nstring(source_name,sizeof(source_name),dgram->source_name.name);
+ /* For a LanMan host announce the workgroup name is the destination name. */
+ pull_ascii_nstring(work_name,sizeof(work_name),dgram->dest_name.name);
+
+ DEBUG(3,("process_lm_host_announce: LM Announcement from %s IP %s to \
+%s for server %s.\n", nmb_namestr(&dgram->source_name), inet_ntoa(p->ip),
+ nmb_namestr(&dgram->dest_name),announce_name));
+
+ DEBUG(5,("process_lm_host_announce: os=(%d,%d) ttl=%d server type=%08x comment=%s\n",
+ osmajor, osminor, ttl, servertype,comment));
+
+ if ((osmajor < 36) || (osmajor > 38) || (osminor !=0)) {
+ DEBUG(5,("process_lm_host_announce: LM Announcement packet does not \
+originate from OS/2 Warp client. Ignoring packet.\n"));
+ /* Could have been from a Windows machine (with its LM Announce enabled),
+ or a Samba server. Then don't disrupt the current browse list. */
+ goto done;
+ }
+
+ /* Filter servertype to remove impossible bits. */
+ servertype &= ~(SV_TYPE_LOCAL_LIST_ONLY|SV_TYPE_DOMAIN_ENUM);
+
+ /* A LanMan host announcement must be sent to the name WORKGROUP<00>. */
+ if(dgram->dest_name.name_type != 0x00) {
+ DEBUG(2,("process_lm_host_announce: incorrect name type for destination from IP %s \
+(was %02x) should be 0x00. Allowing packet anyway.\n",
+ inet_ntoa(p->ip), dgram->dest_name.name_type));
+ /* Change it so it was. */
+ dgram->dest_name.name_type = 0x00;
+ }
+
+ /*
+ * Syntax servers version 5.1 send HostAnnounce packets to
+ * *THE WRONG NAME*. They send to LOCAL_MASTER_BROWSER_NAME<00>
+ * instead of WORKGROUP<1d> name. So to fix this we check if
+ * the workgroup name is our own name, and if so change it
+ * to be our primary workgroup name. This code is probably
+ * not needed in the LanMan announce code, but it won't hurt.
+ */
+
+ if(strequal(work_name, global_myname()))
+ unstrcpy(work_name,lp_workgroup());
+
+ /*
+ * We are being very agressive here in adding a workgroup
+ * name on the basis of a host announcing itself as being
+ * in that workgroup. Maybe we should wait for the workgroup
+ * announce instead ? JRA.
+ */
+
+ work = find_workgroup_on_subnet(subrec, work_name);
+
+ if(servertype != 0) {
+ if (work == NULL) {
+ /* We have no record of this workgroup. Add it. */
+ if((work = create_workgroup_on_subnet(subrec, work_name, ttl))==NULL)
+ goto done;
+ }
+
+ if((servrec = find_server_in_workgroup( work, announce_name))==NULL) {
+ /* If this server is not already in the workgroup, add it. */
+ create_server_on_workgroup(work, announce_name,
+ servertype|SV_TYPE_LOCAL_LIST_ONLY,
+ ttl, comment);
+ } else {
+ /* Update the record. */
+ servrec->serv.type = servertype|SV_TYPE_LOCAL_LIST_ONLY;
+ update_server_ttl( servrec, ttl);
+ fstrcpy(servrec->serv.comment,comment);
+ }
+ } else {
+ /*
+ * This server is announcing it is going down. Remove it from the
+ * workgroup.
+ */
+ if(!is_myname(announce_name) && (work != NULL) &&
+ ((servrec = find_server_in_workgroup( work, announce_name))!=NULL)) {
+ remove_server_from_workgroup( work, servrec);
+ }
+ }
+
+ subrec->work_changed = True;
+ found_lm_clients = True;
+
+done:
+
+ END_PROFILE(lm_host_announce);
+}
+
+/****************************************************************************
+ Send a backup list response.
+*****************************************************************************/
+
+static void send_backup_list_response(struct subnet_record *subrec,
+ struct work_record *work,
+ struct nmb_name *send_to_name,
+ unsigned char max_number_requested,
+ uint32 token, struct in_addr sendto_ip,
+ int port)
+{
+ char outbuf[1024];
+ char *p, *countptr;
+ unsigned int count = 0;
+ unstring send_to_namestr;
+#if 0
+ struct server_record *servrec;
+#endif
+ unstring myname;
+
+ memset(outbuf,'\0',sizeof(outbuf));
+
+ DEBUG(3,("send_backup_list_response: sending backup list for workgroup %s to %s IP %s\n",
+ work->work_group, nmb_namestr(send_to_name), inet_ntoa(sendto_ip)));
+
+ p = outbuf;
+
+ SCVAL(p,0,ANN_GetBackupListResp); /* Backup list response opcode. */
+ p++;
+
+ countptr = p;
+ p++;
+
+ SIVAL(p,0,token); /* The sender's unique info. */
+ p += 4;
+
+ /* We always return at least one name - our own. */
+ count = 1;
+ unstrcpy(myname, global_myname());
+ strupper_m(myname);
+ myname[15]='\0';
+ push_pstring_base(p, myname, outbuf);
+
+ p = skip_string(p,1);
+
+ /* Look for backup browsers in this workgroup. */
+
+#if 0
+ /* we don't currently send become_backup requests so we should never
+ send any other servers names out as backups for our
+ workgroup. That's why this is commented out (tridge) */
+
+ /*
+ * NB. Note that the struct work_record here is not neccessarily
+ * attached to the subnet *subrec.
+ */
+
+ for (servrec = work->serverlist; servrec; servrec = servrec->next)
+ {
+ int len = PTR_DIFF(p, outbuf);
+ if((sizeof(outbuf) - len) < 16)
+ break;
+
+ if(count >= (unsigned int)max_number_requested)
+ break;
+
+ if(strnequal(servrec->serv.name, global_myname(),15))
+ continue;
+
+ if(!(servrec->serv.type & SV_TYPE_BACKUP_BROWSER))
+ continue;
+
+ StrnCpy(p, servrec->serv.name, 15);
+ strupper_m(p);
+ count++;
+
+ DEBUG(5,("send_backup_list_response: Adding server %s number %d\n",
+ p, count));
+
+ p = skip_string(p,1);
+ }
+#endif
+
+ SCVAL(countptr, 0, count);
+
+ pull_ascii_nstring(send_to_namestr, sizeof(send_to_namestr), send_to_name->name);
+
+ DEBUG(4,("send_backup_list_response: sending response to %s<00> IP %s with %d servers.\n",
+ send_to_namestr, inet_ntoa(sendto_ip), count));
+
+ send_mailslot(True, BROWSE_MAILSLOT,
+ outbuf,PTR_DIFF(p,outbuf),
+ global_myname(), 0,
+ send_to_namestr,0,
+ sendto_ip, subrec->myip, port);
+}
+
+/*******************************************************************
+ Process a send backup list request packet.
+
+ A client sends a backup list request to ask for a list of servers on
+ the net that maintain server lists for a domain. A server is then
+ chosen from this list to send NetServerEnum commands to to list
+ available servers.
+
+********************************************************************/
+
+void process_get_backup_list_request(struct subnet_record *subrec,
+ struct packet_struct *p,char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ struct work_record *work;
+ unsigned char max_number_requested = CVAL(buf,0);
+ uint32 token = IVAL(buf,1); /* Sender's key index for the workgroup. */
+ int name_type = dgram->dest_name.name_type;
+ unstring workgroup_name;
+ struct subnet_record *search_subrec = subrec;
+
+ START_PROFILE(get_backup_list);
+ pull_ascii_nstring(workgroup_name, sizeof(workgroup_name), dgram->dest_name.name);
+
+ DEBUG(3,("process_get_backup_list_request: request from %s IP %s to %s.\n",
+ nmb_namestr(&dgram->source_name), inet_ntoa(p->ip),
+ nmb_namestr(&dgram->dest_name)));
+
+ /* We have to be a master browser, or a domain master browser
+ for the requested workgroup. That means it must be our
+ workgroup. */
+
+ if(strequal(workgroup_name, lp_workgroup()) == False) {
+ DEBUG(7,("process_get_backup_list_request: Ignoring announce request for workgroup %s.\n",
+ workgroup_name));
+ goto done;
+ }
+
+ if((work = find_workgroup_on_subnet(search_subrec, workgroup_name)) == NULL) {
+ DEBUG(0,("process_get_backup_list_request: Cannot find workgroup %s on \
+subnet %s.\n", workgroup_name, search_subrec->subnet_name));
+ goto done;
+ }
+
+ /*
+ * If the packet was sent to WORKGROUP<1b> instead
+ * of WORKGROUP<1d> then it was unicast to us a domain master
+ * browser. Change search subrec to unicast.
+ */
+
+ if(name_type == 0x1b) {
+ /* We must be a domain master browser in order to
+ process this packet. */
+
+ if(!AM_DOMAIN_MASTER_BROWSER(work)) {
+ DEBUG(0,("process_get_backup_list_request: domain list requested for workgroup %s \
+and I am not a domain master browser.\n", workgroup_name));
+ goto done;
+ }
+
+ search_subrec = unicast_subnet;
+ } else if (name_type == 0x1d) {
+ /* We must be a local master browser in order to process this packet. */
+
+ if(!AM_LOCAL_MASTER_BROWSER(work)) {
+ DEBUG(0,("process_get_backup_list_request: domain list requested for workgroup %s \
+and I am not a local master browser.\n", workgroup_name));
+ goto done;
+ }
+ } else {
+ DEBUG(0,("process_get_backup_list_request: Invalid name type %x - should be 0x1b or 0x1d.\n",
+ name_type));
+ goto done;
+ }
+
+ send_backup_list_response(subrec, work, &dgram->source_name,
+ max_number_requested, token, p->ip, p->port);
+
+done:
+
+ END_PROFILE(get_backup_list);
+}
+
+/*******************************************************************
+ Process a reset browser state packet.
+
+ Diagnostic packet:
+ 0x1 - Stop being a master browser and become a backup browser.
+ 0x2 - Discard browse lists, stop being a master browser, try again.
+ 0x4 - Stop being a master browser forever.
+
+******************************************************************/
+
+void process_reset_browser(struct subnet_record *subrec,
+ struct packet_struct *p,char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ int state = CVAL(buf,0);
+ struct subnet_record *sr;
+
+ START_PROFILE(reset_browser);
+
+ DEBUG(1,("process_reset_browser: received diagnostic browser reset \
+request from %s IP %s state=0x%X\n",
+ nmb_namestr(&dgram->source_name), inet_ntoa(p->ip), state));
+
+ /* Stop being a local master browser on all our broadcast subnets. */
+ if (state & 0x1) {
+ for (sr = FIRST_SUBNET; sr; sr = NEXT_SUBNET_EXCLUDING_UNICAST(sr)) {
+ struct work_record *work;
+ for (work = sr->workgrouplist; work; work = work->next) {
+ if (AM_LOCAL_MASTER_BROWSER(work))
+ unbecome_local_master_browser(sr, work, True);
+ }
+ }
+ }
+
+ /* Discard our browse lists. */
+ if (state & 0x2) {
+ /*
+ * Calling expire_workgroups_and_servers with a -1
+ * time causes all servers not marked with a PERMANENT_TTL
+ * on the workgroup lists to be discarded, and all
+ * workgroups with empty server lists to be discarded.
+ * This means we keep our own server names and workgroup
+ * as these have a PERMANENT_TTL.
+ */
+
+ expire_workgroups_and_servers(-1);
+ }
+
+ /* Request to stop browsing altogether. */
+ if (state & 0x4)
+ DEBUG(1,("process_reset_browser: ignoring request to stop being a browser.\n"));
+
+ END_PROFILE(reset_browser);
+}
+
+/*******************************************************************
+ Process an announcement request packet.
+ We don't respond immediately, we just check it's a request for
+ our workgroup and then set the flag telling the announce code
+ in nmbd_sendannounce.c:announce_my_server_names that an
+ announcement is needed soon.
+******************************************************************/
+
+void process_announce_request(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ struct work_record *work;
+ unstring workgroup_name;
+
+ START_PROFILE(announce_request);
+
+ pull_ascii_nstring(workgroup_name, sizeof(workgroup_name), dgram->dest_name.name);
+ DEBUG(3,("process_announce_request: Announce request from %s IP %s to %s.\n",
+ nmb_namestr(&dgram->source_name), inet_ntoa(p->ip),
+ nmb_namestr(&dgram->dest_name)));
+
+ /* We only send announcement requests on our workgroup. */
+ if(strequal(workgroup_name, lp_workgroup()) == False) {
+ DEBUG(7,("process_announce_request: Ignoring announce request for workgroup %s.\n",
+ workgroup_name));
+ goto done;
+ }
+
+ if((work = find_workgroup_on_subnet(subrec, workgroup_name)) == NULL) {
+ DEBUG(0,("process_announce_request: Unable to find workgroup %s on subnet !\n",
+ workgroup_name));
+ goto done;
+ }
+
+ work->needannounce = True;
+done:
+
+ END_PROFILE(lm_host_announce);
+}
+
+/*******************************************************************
+ Process a LanMan announcement request packet.
+ We don't respond immediately, we just check it's a request for
+ our workgroup and then set the flag telling that we have found
+ a LanMan client (DOS or OS/2) and that we will have to start
+ sending LanMan announcements (unless specifically disabled
+ through the "lm announce" parameter in smb.conf)
+******************************************************************/
+
+void process_lm_announce_request(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ unstring workgroup_name;
+
+ START_PROFILE(lm_announce_request);
+
+ pull_ascii_nstring(workgroup_name, sizeof(workgroup_name), dgram->dest_name.name);
+ DEBUG(3,("process_lm_announce_request: Announce request from %s IP %s to %s.\n",
+ nmb_namestr(&dgram->source_name), inet_ntoa(p->ip),
+ nmb_namestr(&dgram->dest_name)));
+
+ /* We only send announcement requests on our workgroup. */
+ if(strequal(workgroup_name, lp_workgroup()) == False) {
+ DEBUG(7,("process_lm_announce_request: Ignoring announce request for workgroup %s.\n",
+ workgroup_name));
+ goto done;
+ }
+
+ if(find_workgroup_on_subnet(subrec, workgroup_name) == NULL) {
+ DEBUG(0,("process_announce_request: Unable to find workgroup %s on subnet !\n",
+ workgroup_name));
+ goto done;
+ }
+
+ found_lm_clients = True;
+
+done:
+
+ END_PROFILE(lm_host_announce);
+}
diff --git a/source/nmbd/nmbd_incomingrequests.c b/source/nmbd/nmbd_incomingrequests.c
new file mode 100644
index 00000000000..9214594096c
--- /dev/null
+++ b/source/nmbd/nmbd_incomingrequests.c
@@ -0,0 +1,587 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ This file contains all the code to process NetBIOS requests coming
+ in on port 137. It does not deal with the code needed to service
+ WINS server requests, but only broadcast and unicast requests.
+
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+Send a name release response.
+**************************************************************************/
+
+static void send_name_release_response(int rcode, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ char rdata[6];
+
+ memcpy(&rdata[0], &nmb->additional->rdata[0], 6);
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ rcode, /* Result code. */
+ NMB_REL, /* nmbd type code. */
+ NMB_NAME_RELEASE_OPCODE, /* opcode. */
+ 0, /* ttl. */
+ rdata, /* data to send. */
+ 6); /* data length. */
+}
+
+/****************************************************************************
+Process a name release packet on a broadcast subnet.
+Ignore it if it's not one of our names.
+****************************************************************************/
+
+void process_name_release_request(struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct in_addr owner_ip;
+ struct nmb_name *question = &nmb->question.question_name;
+ unstring qname;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+ BOOL group = (nb_flags & NB_GROUP) ? True : False;
+ struct name_record *namerec;
+ int rcode = 0;
+
+ putip((char *)&owner_ip,&nmb->additional->rdata[2]);
+
+ if(!bcast) {
+ /* We should only get broadcast name release packets here.
+ Anyone trying to release unicast should be going to a WINS
+ server. If the code gets here, then either we are not a wins
+ server and they sent it anyway, or we are a WINS server and
+ the request was malformed. Either way, log an error here.
+ and send an error reply back.
+ */
+ DEBUG(0,("process_name_release_request: unicast name release request \
+received for name %s from IP %s on subnet %s. Error - should be sent to WINS server\n",
+ nmb_namestr(question), inet_ntoa(owner_ip), subrec->subnet_name));
+
+ send_name_release_response(FMT_ERR, p);
+ return;
+ }
+
+ DEBUG(3,("process_name_release_request: Name release on name %s, \
+subnet %s from owner IP %s\n",
+ nmb_namestr(&nmb->question.question_name),
+ subrec->subnet_name, inet_ntoa(owner_ip)));
+
+ /* If someone is releasing a broadcast group name, just ignore it. */
+ if( group && !ismyip(owner_ip) )
+ return;
+
+ /*
+ * Code to work around a bug in FTP OnNet software NBT implementation.
+ * They do a broadcast name release for WORKGROUP<0> and WORKGROUP<1e>
+ * names and *don't set the group bit* !!!!!
+ */
+
+ pull_ascii_nstring(qname, sizeof(qname), question->name);
+ if( !group && !ismyip(owner_ip) && strequal(qname, lp_workgroup()) &&
+ ((question->name_type == 0x0) || (question->name_type == 0x1e))) {
+ DEBUG(6,("process_name_release_request: FTP OnNet bug workaround. Ignoring \
+group release name %s from IP %s on subnet %s with no group bit set.\n",
+ nmb_namestr(question), inet_ntoa(owner_ip), subrec->subnet_name ));
+ return;
+ }
+
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+ /* We only care about someone trying to release one of our names. */
+ if( namerec && ( (namerec->data.source == SELF_NAME)
+ || (namerec->data.source == PERMANENT_NAME) ) ) {
+ rcode = ACT_ERR;
+ DEBUG(0, ("process_name_release_request: Attempt to release name %s from IP %s \
+on subnet %s being rejected as it is one of our names.\n",
+ nmb_namestr(&nmb->question.question_name), inet_ntoa(owner_ip), subrec->subnet_name));
+ }
+
+ if(rcode == 0)
+ return;
+
+ /* Send a NAME RELEASE RESPONSE (pos/neg) see rfc1002.txt 4.2.10-11 */
+ send_name_release_response(rcode, p);
+}
+
+/****************************************************************************
+Send a name registration response.
+**************************************************************************/
+
+static void send_name_registration_response(int rcode, int ttl, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ char rdata[6];
+
+ memcpy(&rdata[0], &nmb->additional->rdata[0], 6);
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ rcode, /* Result code. */
+ NMB_REG, /* nmbd type code. */
+ NMB_NAME_REG_OPCODE, /* opcode. */
+ ttl, /* ttl. */
+ rdata, /* data to send. */
+ 6); /* data length. */
+}
+
+/****************************************************************************
+Process a name refresh request on a broadcast subnet.
+**************************************************************************/
+
+void process_name_refresh_request(struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ struct in_addr from_ip;
+
+ putip((char *)&from_ip,&nmb->additional->rdata[2]);
+
+ if(!bcast) {
+ /* We should only get broadcast name refresh packets here.
+ Anyone trying to refresh unicast should be going to a WINS
+ server. If the code gets here, then either we are not a wins
+ server and they sent it anyway, or we are a WINS server and
+ the request was malformed. Either way, log an error here.
+ and send an error reply back.
+ */
+ DEBUG(0,("process_name_refresh_request: unicast name registration request \
+received for name %s from IP %s on subnet %s.\n",
+ nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+ DEBUG(0,("Error - should be sent to WINS server\n"));
+
+ send_name_registration_response(FMT_ERR, 0, p);
+ return;
+ }
+
+ /* Just log a message. We really don't care about broadcast name refreshes. */
+
+ DEBUG(3,("process_name_refresh_request: Name refresh for name %s \
+IP %s on subnet %s\n", nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+}
+
+/****************************************************************************
+Process a name registration request on a broadcast subnet.
+**************************************************************************/
+
+void process_name_registration_request(struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+ BOOL group = (nb_flags & NB_GROUP) ? True : False;
+ struct name_record *namerec = NULL;
+ int ttl = nmb->additional->ttl;
+ struct in_addr from_ip;
+
+ putip((char *)&from_ip,&nmb->additional->rdata[2]);
+
+ if(!bcast) {
+ /* We should only get broadcast name registration packets here.
+ Anyone trying to register unicast should be going to a WINS
+ server. If the code gets here, then either we are not a wins
+ server and they sent it anyway, or we are a WINS server and
+ the request was malformed. Either way, log an error here.
+ and send an error reply back.
+ */
+ DEBUG(0,("process_name_registration_request: unicast name registration request \
+received for name %s from IP %s on subnet %s. Error - should be sent to WINS server\n",
+ nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+
+ send_name_registration_response(FMT_ERR, 0, p);
+ return;
+ }
+
+ DEBUG(3,("process_name_registration_request: Name registration for name %s \
+IP %s on subnet %s\n", nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+
+ /* See if the name already exists. */
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+ /*
+ * If the name being registered exists and is a WINS_PROXY_NAME
+ * then delete the WINS proxy name entry so we don't reply erroneously
+ * later to queries.
+ */
+
+ if((namerec != NULL) && (namerec->data.source == WINS_PROXY_NAME)) {
+ remove_name_from_namelist( subrec, namerec );
+ namerec = NULL;
+ }
+
+ if (!group) {
+ /* Unique name. */
+
+ if( (namerec != NULL)
+ && ( (namerec->data.source == SELF_NAME)
+ || (namerec->data.source == PERMANENT_NAME)
+ || NAME_GROUP(namerec) ) ) {
+ /* No-one can register one of Samba's names, nor can they
+ register a name that's a group name as a unique name */
+
+ send_name_registration_response(ACT_ERR, 0, p);
+ return;
+ } else if(namerec != NULL) {
+ /* Update the namelist record with the new information. */
+ namerec->data.ip[0] = from_ip;
+ update_name_ttl(namerec, ttl);
+
+ DEBUG(3,("process_name_registration_request: Updated name record %s \
+with IP %s on subnet %s\n",nmb_namestr(&namerec->name),inet_ntoa(from_ip), subrec->subnet_name));
+ return;
+ }
+ } else {
+ /* Group name. */
+
+ if( (namerec != NULL)
+ && !NAME_GROUP(namerec)
+ && ( (namerec->data.source == SELF_NAME)
+ || (namerec->data.source == PERMANENT_NAME) ) ) {
+ /* Disallow group names when we have a unique name. */
+ send_name_registration_response(ACT_ERR, 0, p);
+ return;
+ }
+ }
+}
+
+/****************************************************************************
+This is used to sort names for a name status into a sensible order.
+We put our own names first, then in alphabetical order.
+**************************************************************************/
+
+static int status_compare(char *n1,char *n2)
+{
+ unstring name1, name2;
+ int l1,l2,l3;
+
+ memset(name1, '\0', sizeof(name1));
+ memset(name2, '\0', sizeof(name2));
+ pull_ascii_nstring(name1, sizeof(name1), n1);
+ pull_ascii_nstring(name2, sizeof(name2), n2);
+ n1 = name1;
+ n2 = name2;
+
+ /* It's a bit tricky because the names are space padded */
+ for (l1=0;l1<15 && n1[l1] && n1[l1] != ' ';l1++)
+ ;
+ for (l2=0;l2<15 && n2[l2] && n2[l2] != ' ';l2++)
+ ;
+ l3 = strlen(global_myname());
+
+ if ((l1==l3) && strncmp(n1,global_myname(),l3) == 0 &&
+ (l2!=l3 || strncmp(n2,global_myname(),l3) != 0))
+ return -1;
+
+ if ((l2==l3) && strncmp(n2,global_myname(),l3) == 0 &&
+ (l1!=l3 || strncmp(n1,global_myname(),l3) != 0))
+ return 1;
+
+ return memcmp(n1,n2,sizeof(name1));
+}
+
+/****************************************************************************
+ Process a node status query
+ ****************************************************************************/
+
+void process_node_status_request(struct subnet_record *subrec, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ unstring qname;
+ int ques_type = nmb->question.question_name.name_type;
+ char rdata[MAX_DGRAM_SIZE];
+ char *countptr, *buf, *bufend, *buf0;
+ int names_added,i;
+ struct name_record *namerec;
+
+ pull_ascii_nstring(qname, sizeof(qname), nmb->question.question_name.name);
+
+ DEBUG(3,("process_node_status_request: status request for name %s from IP %s on \
+subnet %s.\n", nmb_namestr(&nmb->question.question_name), inet_ntoa(p->ip), subrec->subnet_name));
+
+ if((namerec = find_name_on_subnet(subrec, &nmb->question.question_name, FIND_SELF_NAME)) == 0) {
+ DEBUG(1,("process_node_status_request: status request for name %s from IP %s on \
+subnet %s - name not found.\n", nmb_namestr(&nmb->question.question_name),
+ inet_ntoa(p->ip), subrec->subnet_name));
+
+ return;
+ }
+
+ /* this is not an exact calculation. the 46 is for the stats buffer
+ and the 60 is to leave room for the header etc */
+ bufend = &rdata[MAX_DGRAM_SIZE] - (18 + 46 + 60);
+ countptr = buf = rdata;
+ buf += 1;
+ buf0 = buf;
+
+ names_added = 0;
+
+ namerec = (struct name_record *)ubi_trFirst( subrec->namelist );
+
+ while (buf < bufend) {
+ if( (namerec->data.source == SELF_NAME) || (namerec->data.source == PERMANENT_NAME) ) {
+ int name_type = namerec->name.name_type;
+ unstring name;
+
+ pull_ascii_nstring(name, sizeof(name), namerec->name.name);
+ strupper_m(name);
+ if (!strequal(name,"*") &&
+ !strequal(name,"__SAMBA__") &&
+ (name_type < 0x1b || name_type >= 0x20 ||
+ ques_type < 0x1b || ques_type >= 0x20 ||
+ strequal(qname, name))) {
+ /* Start with the name. */
+ size_t len;
+ push_ascii_nstring(buf, name);
+ len = strlen(buf);
+ memset(buf + len, ' ', MAX_NETBIOSNAME_LEN - len - 1);
+ buf[MAX_NETBIOSNAME_LEN - 1] = '\0';
+
+ /* Put the name type and netbios flags in the buffer. */
+
+ buf[15] = name_type;
+ set_nb_flags( &buf[16],namerec->data.nb_flags );
+ buf[16] |= NB_ACTIVE; /* all our names are active */
+
+ buf += 18;
+
+ names_added++;
+ }
+ }
+
+ /* Remove duplicate names. */
+ if (names_added > 1) {
+ qsort( buf0, names_added, 18, QSORT_CAST status_compare );
+ }
+
+ for( i=1; i < names_added ; i++ ) {
+ if (memcmp(buf0 + 18*i,buf0 + 18*(i-1),16) == 0) {
+ names_added--;
+ if (names_added == i)
+ break;
+ memmove(buf0 + 18*i,buf0 + 18*(i+1),18*(names_added-i));
+ i--;
+ }
+ }
+
+ buf = buf0 + 18*names_added;
+
+ namerec = (struct name_record *)ubi_trNext( namerec );
+
+ if (!namerec) {
+ /* End of the subnet specific name list. Now
+ add the names on the unicast subnet . */
+ struct subnet_record *uni_subrec = unicast_subnet;
+
+ if (uni_subrec != subrec) {
+ subrec = uni_subrec;
+ namerec = (struct name_record *)ubi_trFirst( subrec->namelist );
+ }
+ }
+ if (!namerec)
+ break;
+
+ }
+
+ SCVAL(countptr,0,names_added);
+
+ /* We don't send any stats as they could be used to attack
+ the protocol. */
+ memset(buf,'\0',46);
+
+ buf += 46;
+
+ /* Send a NODE STATUS RESPONSE */
+ reply_netbios_packet(p, /* Packet to reply to. */
+ 0, /* Result code. */
+ NMB_STATUS, /* nmbd type code. */
+ NMB_NAME_QUERY_OPCODE, /* opcode. */
+ 0, /* ttl. */
+ rdata, /* data to send. */
+ PTR_DIFF(buf,rdata)); /* data length. */
+}
+
+
+/***************************************************************************
+Process a name query.
+
+For broadcast name queries:
+
+ - Only reply if the query is for one of YOUR names.
+ - NEVER send a negative response to a broadcast query.
+
+****************************************************************************/
+
+void process_name_query_request(struct subnet_record *subrec, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ int name_type = question->name_type;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ int ttl=0;
+ int rcode = 0;
+ char *prdata = NULL;
+ char rdata[6];
+ BOOL success = False;
+ struct name_record *namerec = NULL;
+ int reply_data_len = 0;
+ int i;
+
+ DEBUG(3,("process_name_query_request: Name query from %s on subnet %s for name %s\n",
+ inet_ntoa(p->ip), subrec->subnet_name, nmb_namestr(question)));
+
+ /* Look up the name in the cache - if the request is a broadcast request that
+ came from a subnet we don't know about then search all the broadcast subnets
+ for a match (as we don't know what interface the request came in on). */
+
+ if(subrec == remote_broadcast_subnet)
+ namerec = find_name_for_remote_broadcast_subnet( question, FIND_ANY_NAME);
+ else
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+ /* Check if it is a name that expired */
+ if (namerec &&
+ ((namerec->data.death_time != PERMANENT_TTL) &&
+ (namerec->data.death_time < p->timestamp))) {
+ DEBUG(5,("process_name_query_request: expired name %s\n", nmb_namestr(&namerec->name)));
+ namerec = NULL;
+ }
+
+ if (namerec) {
+ /*
+ * Always respond to unicast queries.
+ * Don't respond to broadcast queries unless the query is for
+ * a name we own, a Primary Domain Controller name, or a WINS_PROXY
+ * name with type 0 or 0x20. WINS_PROXY names are only ever added
+ * into the namelist if we were configured as a WINS proxy.
+ */
+
+ if (!bcast ||
+ (bcast && ((name_type == 0x1b) ||
+ (namerec->data.source == SELF_NAME) ||
+ (namerec->data.source == PERMANENT_NAME) ||
+ ((namerec->data.source == WINS_PROXY_NAME) &&
+ ((name_type == 0) || (name_type == 0x20)))))) {
+ /* The requested name is a directed query, or it's SELF or PERMANENT or WINS_PROXY,
+ or it's a Domain Master type. */
+
+ /*
+ * If this is a WINS_PROXY_NAME, then ceck that none of the IP
+ * addresses we are returning is on the same broadcast subnet
+ * as the requesting packet. If it is then don't reply as the
+ * actual machine will be replying also and we don't want two
+ * replies to a broadcast query.
+ */
+
+ if (namerec->data.source == WINS_PROXY_NAME) {
+ for( i = 0; i < namerec->data.num_ips; i++) {
+ if (same_net(namerec->data.ip[i], subrec->myip, subrec->mask_ip)) {
+ DEBUG(5,("process_name_query_request: name %s is a WINS proxy name and is also on the same subnet (%s) as the requestor. Not replying.\n",
+ nmb_namestr(&namerec->name), subrec->subnet_name ));
+ return;
+ }
+ }
+ }
+
+ ttl = (namerec->data.death_time != PERMANENT_TTL) ?
+ namerec->data.death_time - p->timestamp : lp_max_ttl();
+
+ /* Copy all known ip addresses into the return data. */
+ /* Optimise for the common case of one IP address so
+ we don't need a malloc. */
+
+ if (namerec->data.num_ips == 1) {
+ prdata = rdata;
+ } else {
+ if ((prdata = (char *)malloc( namerec->data.num_ips * 6 )) == NULL) {
+ DEBUG(0,("process_name_query_request: malloc fail !\n"));
+ return;
+ }
+ }
+
+ for (i = 0; i < namerec->data.num_ips; i++) {
+ set_nb_flags(&prdata[i*6],namerec->data.nb_flags);
+ putip((char *)&prdata[2+(i*6)], &namerec->data.ip[i]);
+ }
+
+ sort_query_replies(prdata, i, p->ip);
+
+ reply_data_len = namerec->data.num_ips * 6;
+ success = True;
+ }
+ }
+
+ /*
+ * If a machine is broadcasting a name lookup request and we have lp_wins_proxy()
+ * set we should initiate a WINS query here. On success we add the resolved name
+ * into our namelist with a type of WINS_PROXY_NAME and then reply to the query.
+ */
+
+ if(!success && (namerec == NULL) && we_are_a_wins_client() && lp_wins_proxy() &&
+ bcast && (subrec != remote_broadcast_subnet)) {
+ make_wins_proxy_name_query_request( subrec, p, question );
+ return;
+ }
+
+ if (!success && bcast) {
+ if(prdata != rdata)
+ SAFE_FREE(prdata);
+ return; /* Never reply with a negative response to broadcasts. */
+ }
+
+ /*
+ * Final check. From observation, if a unicast packet is sent
+ * to a non-WINS server with the recursion desired bit set
+ * then never send a negative response.
+ */
+
+ if(!success && !bcast && nmb->header.nm_flags.recursion_desired) {
+ if(prdata != rdata)
+ SAFE_FREE(prdata);
+ return;
+ }
+
+ if (success) {
+ rcode = 0;
+ DEBUG(3,("OK\n"));
+ } else {
+ rcode = NAM_ERR;
+ DEBUG(3,("UNKNOWN\n"));
+ }
+
+ /* See rfc1002.txt 4.2.13. */
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ rcode, /* Result code. */
+ NMB_QUERY, /* nmbd type code. */
+ NMB_NAME_QUERY_OPCODE, /* opcode. */
+ ttl, /* ttl. */
+ prdata, /* data to send. */
+ reply_data_len); /* data length. */
+
+ if(prdata != rdata)
+ SAFE_FREE(prdata);
+}
diff --git a/source/nmbd/nmbd_lmhosts.c b/source/nmbd/nmbd_lmhosts.c
new file mode 100644
index 00000000000..b14e13f3a47
--- /dev/null
+++ b/source/nmbd/nmbd_lmhosts.c
@@ -0,0 +1,93 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Jeremy Allison 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Revision History:
+
+ Handle lmhosts file reading.
+
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+Load a lmhosts file.
+****************************************************************************/
+
+void load_lmhosts_file(char *fname)
+{
+ pstring name;
+ int name_type;
+ struct in_addr ipaddr;
+ XFILE *fp = startlmhosts( fname );
+
+ if (!fp) {
+ DEBUG(2,("load_lmhosts_file: Can't open lmhosts file %s. Error was %s\n",
+ fname, strerror(errno)));
+ return;
+ }
+
+ while (getlmhostsent(fp, name, &name_type, &ipaddr) ) {
+ struct subnet_record *subrec = NULL;
+ enum name_source source = LMHOSTS_NAME;
+
+ /* We find a relevent subnet to put this entry on, then add it. */
+ /* Go through all the broadcast subnets and see if the mask matches. */
+ for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ if(same_net(ipaddr, subrec->bcast_ip, subrec->mask_ip))
+ break;
+ }
+
+ /* If none match add the name to the remote_broadcast_subnet. */
+ if(subrec == NULL)
+ subrec = remote_broadcast_subnet;
+
+ if(name_type == -1) {
+ /* Add the (0) and (0x20) names directly into the namelist for this subnet. */
+ (void)add_name_to_subnet(subrec,name,0x00,(uint16)NB_ACTIVE,PERMANENT_TTL,source,1,&ipaddr);
+ (void)add_name_to_subnet(subrec,name,0x20,(uint16)NB_ACTIVE,PERMANENT_TTL,source,1,&ipaddr);
+ } else {
+ /* Add the given name type to the subnet namelist. */
+ (void)add_name_to_subnet(subrec,name,name_type,(uint16)NB_ACTIVE,PERMANENT_TTL,source,1,&ipaddr);
+ }
+ }
+
+ endlmhosts(fp);
+}
+
+/****************************************************************************
+ Find a name read from the lmhosts file. We secretly check the names on
+ the remote_broadcast_subnet as if the name was added to a regular broadcast
+ subnet it will be found by normal name query processing.
+****************************************************************************/
+
+BOOL find_name_in_lmhosts(struct nmb_name *nmbname, struct name_record **namerecp)
+{
+ struct name_record *namerec;
+
+ *namerecp = NULL;
+
+ if((namerec = find_name_on_subnet(remote_broadcast_subnet, nmbname, FIND_ANY_NAME))==NULL)
+ return False;
+
+ if(!NAME_IS_ACTIVE(namerec) || (namerec->data.source != LMHOSTS_NAME))
+ return False;
+
+ *namerecp = namerec;
+ return True;
+}
diff --git a/source/nmbd/nmbd_logonnames.c b/source/nmbd/nmbd_logonnames.c
new file mode 100644
index 00000000000..e4264305911
--- /dev/null
+++ b/source/nmbd/nmbd_logonnames.c
@@ -0,0 +1,173 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+
+extern struct in_addr allones_ip;
+
+extern uint16 samba_nb_type; /* Samba's NetBIOS type. */
+
+/****************************************************************************
+ Fail to become a Logon server on a subnet.
+****************************************************************************/
+
+static void become_logon_server_fail(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *fail_name)
+{
+ unstring failname;
+ struct work_record *work;
+ struct server_record *servrec;
+
+ pull_ascii_nstring(failname, sizeof(failname), fail_name->name);
+ work = find_workgroup_on_subnet(subrec, failname);
+ if(!work) {
+ DEBUG(0,("become_logon_server_fail: Error - cannot find \
+workgroup %s on subnet %s\n", failname, subrec->subnet_name));
+ return;
+ }
+
+ if((servrec = find_server_in_workgroup( work, global_myname())) == NULL) {
+ DEBUG(0,("become_logon_server_fail: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ global_myname(), failname, subrec->subnet_name));
+ work->log_state = LOGON_NONE;
+ return;
+ }
+
+ /* Set the state back to LOGON_NONE. */
+ work->log_state = LOGON_NONE;
+
+ servrec->serv.type &= ~SV_TYPE_DOMAIN_CTRL;
+
+ DEBUG(0,("become_logon_server_fail: Failed to become a domain master for \
+workgroup %s on subnet %s. Couldn't register name %s.\n",
+ work->work_group, subrec->subnet_name, nmb_namestr(fail_name)));
+
+}
+
+/****************************************************************************
+ Become a Logon server on a subnet.
+ ****************************************************************************/
+
+static void become_logon_server_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *registered_name,
+ uint16 nb_flags,
+ int ttl, struct in_addr registered_ip)
+{
+ unstring reg_name;
+ struct work_record *work;
+ struct server_record *servrec;
+
+ pull_ascii_nstring(reg_name, sizeof(reg_name), registered_name->name);
+ work = find_workgroup_on_subnet( subrec, reg_name);
+ if(!work) {
+ DEBUG(0,("become_logon_server_success: Error - cannot find \
+workgroup %s on subnet %s\n", reg_name, subrec->subnet_name));
+ return;
+ }
+
+ if((servrec = find_server_in_workgroup( work, global_myname())) == NULL) {
+ DEBUG(0,("become_logon_server_success: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ global_myname(), reg_name, subrec->subnet_name));
+ work->log_state = LOGON_NONE;
+ return;
+ }
+
+ /* Set the state in the workgroup structure. */
+ work->log_state = LOGON_SRV; /* Become domain master. */
+
+ /* Update our server status. */
+ servrec->serv.type |= (SV_TYPE_NT|SV_TYPE_DOMAIN_MEMBER);
+ /* To allow Win95 policies to load we need to set type domain
+ controller.
+ */
+ servrec->serv.type |= SV_TYPE_DOMAIN_CTRL;
+
+ /* Tell the namelist writer to write out a change. */
+ subrec->work_changed = True;
+
+ /*
+ * Add the WORKGROUP<1C> name to the UNICAST subnet with the IP address
+ * for this subnet so we will respond to queries on this name.
+ */
+
+ {
+ struct nmb_name nmbname;
+ make_nmb_name(&nmbname,lp_workgroup(),0x1c);
+ insert_permanent_name_into_unicast(subrec, &nmbname, 0x1c);
+ }
+
+ DEBUG(0,("become_logon_server_success: Samba is now a logon server \
+for workgroup %s on subnet %s\n", work->work_group, subrec->subnet_name));
+}
+
+/*******************************************************************
+ Become a logon server by attempting to register the WORKGROUP<1c>
+ group name.
+******************************************************************/
+
+static void become_logon_server(struct subnet_record *subrec,
+ struct work_record *work)
+{
+ DEBUG(2,("become_logon_server: Atempting to become logon server for workgroup %s \
+on subnet %s\n", work->work_group,subrec->subnet_name));
+
+ DEBUG(3,("become_logon_server: go to first stage: register %s<1c> name\n",
+ work->work_group));
+ work->log_state = LOGON_WAIT;
+
+ register_name(subrec, work->work_group,0x1c,samba_nb_type|NB_GROUP,
+ become_logon_server_success,
+ become_logon_server_fail, NULL);
+}
+
+/*****************************************************************************
+ Add the internet group <1c> logon names by unicast and broadcast.
+ ****************************************************************************/
+
+void add_logon_names(void)
+{
+ struct subnet_record *subrec;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) {
+ struct work_record *work = find_workgroup_on_subnet(subrec, lp_workgroup());
+
+ if (work && (work->log_state == LOGON_NONE)) {
+ struct nmb_name nmbname;
+ make_nmb_name(&nmbname,lp_workgroup(),0x1c);
+
+ if (find_name_on_subnet(subrec, &nmbname, FIND_SELF_NAME) == NULL) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "add_domain_logon_names:\n" );
+ dbgtext( "Attempting to become logon server " );
+ dbgtext( "for workgroup %s ", lp_workgroup() );
+ dbgtext( "on subnet %s\n", subrec->subnet_name );
+ }
+ become_logon_server(subrec, work);
+ }
+ }
+ }
+}
diff --git a/source/nmbd/nmbd_mynames.c b/source/nmbd/nmbd_mynames.c
new file mode 100644
index 00000000000..07247d5495e
--- /dev/null
+++ b/source/nmbd/nmbd_mynames.c
@@ -0,0 +1,224 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+
+extern uint16 samba_nb_type; /* Samba's NetBIOS type. */
+
+/****************************************************************************
+ Fail funtion when registering my netbios names.
+**************************************************************************/
+
+static void my_name_register_failed(struct subnet_record *subrec,
+ struct response_record *rrec, struct nmb_name *nmbname)
+{
+ DEBUG(0,("my_name_register_failed: Failed to register my name %s on subnet %s.\n",
+ nmb_namestr(nmbname), subrec->subnet_name));
+}
+
+
+/****************************************************************************
+ Add my workgroup and my given names to one subnet
+ Also add the magic Samba names.
+**************************************************************************/
+
+void register_my_workgroup_one_subnet(struct subnet_record *subrec)
+{
+ int i;
+
+ struct work_record *work;
+
+ /* Create the workgroup on the subnet. */
+ if((work = create_workgroup_on_subnet(subrec, lp_workgroup(),
+ PERMANENT_TTL)) == NULL) {
+ DEBUG(0,("register_my_workgroup_and_names: Failed to create my workgroup %s on subnet %s. \
+Exiting.\n", lp_workgroup(), subrec->subnet_name));
+ return;
+ }
+
+ /* Each subnet entry, except for the wins_server_subnet has
+ the magic Samba names. */
+ add_samba_names_to_subnet(subrec);
+
+ /* Register all our names including aliases. */
+ for (i=0; my_netbios_names(i); i++) {
+ register_name(subrec, my_netbios_names(i),0x20,samba_nb_type,
+ NULL,
+ my_name_register_failed, NULL);
+ register_name(subrec, my_netbios_names(i),0x03,samba_nb_type,
+ NULL,
+ my_name_register_failed, NULL);
+ register_name(subrec, my_netbios_names(i),0x00,samba_nb_type,
+ NULL,
+ my_name_register_failed, NULL);
+ }
+
+ /* Initiate election processing, register the workgroup names etc. */
+ initiate_myworkgroup_startup(subrec, work);
+}
+
+/*******************************************************************
+ Utility function to add a name to the unicast subnet, or add in
+ our IP address if it already exists.
+******************************************************************/
+
+static void insert_refresh_name_into_unicast( struct subnet_record *subrec,
+ struct nmb_name *nmbname, uint16 nb_type )
+{
+ struct name_record *namerec;
+
+ if (!we_are_a_wins_client()) {
+ insert_permanent_name_into_unicast(subrec, nmbname, nb_type);
+ return;
+ }
+
+ if((namerec = find_name_on_subnet(unicast_subnet, nmbname, FIND_SELF_NAME)) == NULL) {
+ unstring name;
+ pull_ascii_nstring(name, sizeof(name), nmbname->name);
+ /* The name needs to be created on the unicast subnet. */
+ (void)add_name_to_subnet( unicast_subnet, name,
+ nmbname->name_type, nb_type,
+ MIN(lp_max_ttl(), MAX_REFRESH_TIME), SELF_NAME, 1, &subrec->myip);
+ } else {
+ /* The name already exists on the unicast subnet. Add our local
+ IP for the given broadcast subnet to the name. */
+ add_ip_to_name_record( namerec, subrec->myip);
+ }
+}
+
+/****************************************************************************
+ Add my workgroup and my given names to the subnet lists.
+ Also add the magic Samba names.
+**************************************************************************/
+
+BOOL register_my_workgroup_and_names(void)
+{
+ struct subnet_record *subrec;
+ int i;
+
+ for(subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) {
+ register_my_workgroup_one_subnet(subrec);
+ }
+
+ /* We still need to add the magic Samba
+ names and the netbios names to the unicast subnet directly. This is
+ to allow unicast node status requests and queries to still work
+ in a broadcast only environment. */
+
+ add_samba_names_to_subnet(unicast_subnet);
+
+ for (i=0; my_netbios_names(i); i++) {
+ for(subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ /*
+ * Ensure all the IP addresses are added if we are multihomed.
+ */
+ struct nmb_name nmbname;
+
+ make_nmb_name(&nmbname, my_netbios_names(i),0x20);
+ insert_refresh_name_into_unicast(subrec, &nmbname, samba_nb_type);
+
+ make_nmb_name(&nmbname, my_netbios_names(i),0x3);
+ insert_refresh_name_into_unicast(subrec, &nmbname, samba_nb_type);
+
+ make_nmb_name(&nmbname, my_netbios_names(i),0x0);
+ insert_refresh_name_into_unicast(subrec, &nmbname, samba_nb_type);
+ }
+ }
+
+ /*
+ * Add the WORKGROUP<0> and WORKGROUP<1e> group names to the unicast subnet
+ * also for the same reasons.
+ */
+
+ for(subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ /*
+ * Ensure all the IP addresses are added if we are multihomed.
+ */
+ struct nmb_name nmbname;
+
+ make_nmb_name(&nmbname, lp_workgroup(), 0x0);
+ insert_refresh_name_into_unicast(subrec, &nmbname, samba_nb_type|NB_GROUP);
+
+ make_nmb_name(&nmbname, lp_workgroup(), 0x1e);
+ insert_refresh_name_into_unicast(subrec, &nmbname, samba_nb_type|NB_GROUP);
+ }
+
+ /*
+ * We need to add the Samba names to the remote broadcast subnet,
+ * as NT 4.x does directed broadcast requests to the *<0x0> name.
+ */
+
+ add_samba_names_to_subnet(remote_broadcast_subnet);
+
+ return True;
+}
+
+/****************************************************************************
+ Remove all the names we registered.
+**************************************************************************/
+
+void release_wins_names(void)
+{
+ struct subnet_record *subrec = unicast_subnet;
+ struct name_record *namerec, *nextnamerec;
+
+ for (namerec = (struct name_record *)ubi_trFirst( subrec->namelist ); namerec; namerec = nextnamerec) {
+ nextnamerec = (struct name_record *)ubi_trNext( namerec );
+ if( (namerec->data.source == SELF_NAME)
+ && !NAME_IS_DEREGISTERING(namerec) )
+ release_name( subrec, namerec, standard_success_release,
+ NULL, NULL);
+ }
+}
+
+/*******************************************************************
+ Refresh our registered names with WINS
+******************************************************************/
+
+void refresh_my_names(time_t t)
+{
+ struct name_record *namerec;
+
+ if (wins_srv_count() < 1)
+ return;
+
+ for (namerec = (struct name_record *)ubi_trFirst(unicast_subnet->namelist);
+ namerec;
+ namerec = (struct name_record *)ubi_trNext(namerec)) {
+ /* Each SELF name has an individual time to be refreshed. */
+ if ((namerec->data.source == SELF_NAME) &&
+ (namerec->data.refresh_time < t) &&
+ (namerec->data.death_time != PERMANENT_TTL)) {
+ /* We cheat here and pretend the refresh is going to be
+ successful & update the refresh times. This stops
+ multiple refresh calls being done. We actually
+ deal with refresh failure in the fail_fn.
+ */
+ if (!is_refresh_already_queued(unicast_subnet, namerec)) {
+ wins_refresh_name(namerec);
+ }
+ namerec->data.death_time = t + lp_max_ttl();
+ namerec->data.refresh_time = t + MIN(lp_max_ttl()/2, MAX_REFRESH_TIME);
+ }
+ }
+}
diff --git a/source/nmbd/nmbd_namelistdb.c b/source/nmbd/nmbd_namelistdb.c
new file mode 100644
index 00000000000..bb14ff7641a
--- /dev/null
+++ b/source/nmbd/nmbd_namelistdb.c
@@ -0,0 +1,584 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+
+uint16 samba_nb_type = 0; /* samba's NetBIOS name type */
+
+
+/**************************************************************************
+ Set Samba's NetBIOS name type.
+***************************************************************************/
+
+void set_samba_nb_type(void)
+{
+ if( lp_wins_support() || wins_srv_count() )
+ samba_nb_type = NB_HFLAG; /* samba is a 'hybrid' node type. */
+ else
+ samba_nb_type = NB_BFLAG; /* samba is broadcast-only node type. */
+}
+
+/***************************************************************************
+ Convert a NetBIOS name to upper case.
+***************************************************************************/
+
+static void upcase_name( struct nmb_name *target, struct nmb_name *source )
+{
+ int i;
+ unstring targ;
+ fstring scope;
+
+ if( NULL != source )
+ memcpy( target, source, sizeof( struct nmb_name ) );
+
+ pull_ascii_nstring(targ, sizeof(targ), target->name);
+ strupper_m( targ );
+ push_ascii_nstring( target->name, targ);
+
+ pull_ascii(scope, target->scope, 64, -1, STR_TERMINATE);
+ strupper_m( scope );
+ push_ascii(target->scope, scope, 64, STR_TERMINATE);
+
+ /* fudge... We're using a byte-by-byte compare, so we must be sure that
+ * unused space doesn't have garbage in it.
+ */
+
+ for( i = strlen( target->name ); i < sizeof( target->name ); i++ )
+ target->name[i] = '\0';
+ for( i = strlen( target->scope ); i < sizeof( target->scope ); i++ )
+ target->scope[i] = '\0';
+}
+
+/**************************************************************************
+ Add a new or overwrite an existing namelist entry.
+***************************************************************************/
+
+static void update_name_in_namelist( struct subnet_record *subrec,
+ struct name_record *namerec )
+{
+ struct name_record *oldrec = NULL;
+
+ ubi_trInsert( subrec->namelist, namerec, &(namerec->name), &oldrec );
+ if( oldrec ) {
+ SAFE_FREE( oldrec->data.ip );
+ SAFE_FREE( oldrec );
+ }
+}
+
+/**************************************************************************
+ Remove a name from the namelist.
+***************************************************************************/
+
+void remove_name_from_namelist( struct subnet_record *subrec,
+ struct name_record *namerec )
+{
+ ubi_trRemove( subrec->namelist, namerec );
+ SAFE_FREE(namerec->data.ip);
+ ZERO_STRUCTP(namerec);
+ SAFE_FREE(namerec);
+ subrec->namelist_changed = True;
+}
+
+/**************************************************************************
+ Find a name in a subnet.
+**************************************************************************/
+
+struct name_record *find_name_on_subnet( struct subnet_record *subrec,
+ struct nmb_name *nmbname,
+ BOOL self_only )
+{
+ struct nmb_name uc_name[1];
+ struct name_record *name_ret;
+
+ upcase_name( uc_name, nmbname );
+ name_ret = (struct name_record *)ubi_trFind( subrec->namelist, uc_name );
+ if( name_ret ) {
+ /* Self names only - these include permanent names. */
+ if( self_only && (name_ret->data.source != SELF_NAME) && (name_ret->data.source != PERMANENT_NAME) ) {
+ DEBUG( 9, ( "find_name_on_subnet: on subnet %s - self name %s NOT FOUND\n",
+ subrec->subnet_name, nmb_namestr(nmbname) ) );
+ return( NULL );
+ }
+
+ DEBUG( 9, ("find_name_on_subnet: on subnet %s - found name %s source=%d\n",
+ subrec->subnet_name, nmb_namestr(nmbname), name_ret->data.source) );
+ return( name_ret );
+ }
+
+ DEBUG( 9, ( "find_name_on_subnet: on subnet %s - name %s NOT FOUND\n",
+ subrec->subnet_name, nmb_namestr(nmbname) ) );
+ return( NULL );
+}
+
+/**************************************************************************
+ Find a name over all known broadcast subnets.
+************************************************************************/
+
+struct name_record *find_name_for_remote_broadcast_subnet(
+ struct nmb_name *nmbname,
+ BOOL self_only )
+{
+ struct subnet_record *subrec;
+ struct name_record *namerec = NULL;
+
+ for( subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec) ) {
+ if( NULL != (namerec = find_name_on_subnet(subrec, nmbname, self_only)) )
+ break;
+ }
+
+ return( namerec );
+}
+
+/**************************************************************************
+ Update the ttl of an entry in a subnet name list.
+***************************************************************************/
+
+void update_name_ttl( struct name_record *namerec, int ttl )
+{
+ time_t time_now = time(NULL);
+
+ if( namerec->data.death_time != PERMANENT_TTL )
+ namerec->data.death_time = time_now + ttl;
+
+ namerec->data.refresh_time = time_now + MIN((ttl/2), MAX_REFRESH_TIME);
+
+ namerec->subnet->namelist_changed = True;
+}
+
+/**************************************************************************
+ Add an entry to a subnet name list.
+***********************************************************************/
+
+struct name_record *add_name_to_subnet( struct subnet_record *subrec,
+ const char *name,
+ int type,
+ uint16 nb_flags,
+ int ttl,
+ enum name_source source,
+ int num_ips,
+ struct in_addr *iplist)
+{
+ struct name_record *namerec;
+ time_t time_now = time(NULL);
+
+ namerec = (struct name_record *)malloc( sizeof(*namerec) );
+ if( NULL == namerec ) {
+ DEBUG( 0, ( "add_name_to_subnet: malloc fail.\n" ) );
+ return( NULL );
+ }
+
+ memset( (char *)namerec, '\0', sizeof(*namerec) );
+ namerec->data.ip = (struct in_addr *)malloc( sizeof(struct in_addr) * num_ips );
+ if( NULL == namerec->data.ip ) {
+ DEBUG( 0, ( "add_name_to_subnet: malloc fail when creating ip_flgs.\n" ) );
+ ZERO_STRUCTP(namerec);
+ SAFE_FREE(namerec);
+ return NULL;
+ }
+
+ namerec->subnet = subrec;
+
+ make_nmb_name(&namerec->name, name, type);
+ upcase_name(&namerec->name, NULL );
+
+ /* Enter the name as active. */
+ namerec->data.nb_flags = nb_flags | NB_ACTIVE;
+ namerec->data.wins_flags = WINS_ACTIVE;
+
+ /* If it's our primary name, flag it as so. */
+ if( strequal( my_netbios_names(0), name ) )
+ namerec->data.nb_flags |= NB_PERM;
+
+ /* Copy the IPs. */
+ namerec->data.num_ips = num_ips;
+ memcpy( (namerec->data.ip), iplist, num_ips * sizeof(struct in_addr) );
+
+ /* Data source. */
+ namerec->data.source = source;
+
+ /* Setup the death_time and refresh_time. */
+ if( ttl == PERMANENT_TTL )
+ namerec->data.death_time = PERMANENT_TTL;
+ else
+ namerec->data.death_time = time_now + ttl;
+
+ namerec->data.refresh_time = time_now + MIN((ttl/2), MAX_REFRESH_TIME);
+
+ /* Now add the record to the name list. */
+ update_name_in_namelist( subrec, namerec );
+
+ DEBUG( 3, ( "add_name_to_subnet: Added netbios name %s with first IP %s \
+ttl=%d nb_flags=%2x to subnet %s\n",
+ nmb_namestr( &namerec->name ),
+ inet_ntoa( *iplist ),
+ ttl,
+ (unsigned int)nb_flags,
+ subrec->subnet_name ) );
+
+ subrec->namelist_changed = True;
+
+ return(namerec);
+}
+
+/*******************************************************************
+ Utility function automatically called when a name refresh or register
+ succeeds. By definition this is a SELF_NAME (or we wouldn't be registering
+ it).
+ ******************************************************************/
+
+void standard_success_register(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname, uint16 nb_flags, int ttl,
+ struct in_addr registered_ip)
+{
+ struct name_record *namerec;
+
+ namerec = find_name_on_subnet( subrec, nmbname, FIND_SELF_NAME );
+ if( NULL == namerec ) {
+ unstring name;
+ pull_ascii_nstring(name, sizeof(name), nmbname->name);
+ add_name_to_subnet( subrec, name, nmbname->name_type,
+ nb_flags, ttl, SELF_NAME, 1, &registered_ip );
+ } else {
+ update_name_ttl( namerec, ttl );
+ }
+}
+
+/*******************************************************************
+ Utility function automatically called when a name refresh or register
+ fails. Note that this is only ever called on a broadcast subnet with
+ one IP address per name. This is why it can just delete the name
+ without enumerating the IP adresses. JRA.
+ ******************************************************************/
+
+void standard_fail_register( struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *nmbname )
+{
+ struct name_record *namerec;
+
+ namerec = find_name_on_subnet( subrec, nmbname, FIND_SELF_NAME );
+
+ DEBUG( 0, ( "standard_fail_register: Failed to register/refresh name %s \
+on subnet %s\n", nmb_namestr(nmbname), subrec->subnet_name) );
+
+ /* Remove the name from the subnet. */
+ if( namerec )
+ remove_name_from_namelist(subrec, namerec);
+}
+
+/*******************************************************************
+ Utility function to remove an IP address from a name record.
+ ******************************************************************/
+
+static void remove_nth_ip_in_record( struct name_record *namerec, int ind)
+{
+ if( ind != namerec->data.num_ips )
+ memmove( (char *)(&namerec->data.ip[ind]),
+ (char *)(&namerec->data.ip[ind+1]),
+ ( namerec->data.num_ips - ind - 1) * sizeof(struct in_addr) );
+
+ namerec->data.num_ips--;
+ namerec->subnet->namelist_changed = True;
+}
+
+/*******************************************************************
+ Utility function to check if an IP address exists in a name record.
+ ******************************************************************/
+
+BOOL find_ip_in_name_record( struct name_record *namerec, struct in_addr ip )
+{
+ int i;
+
+ for(i = 0; i < namerec->data.num_ips; i++)
+ if(ip_equal( namerec->data.ip[i], ip))
+ return True;
+
+ return False;
+}
+
+/*******************************************************************
+ Utility function to add an IP address to a name record.
+ ******************************************************************/
+
+void add_ip_to_name_record( struct name_record *namerec, struct in_addr new_ip )
+{
+ struct in_addr *new_list;
+
+ /* Don't add one we already have. */
+ if( find_ip_in_name_record( namerec, new_ip ) )
+ return;
+
+ new_list = (struct in_addr *)malloc( (namerec->data.num_ips + 1) * sizeof(struct in_addr) );
+ if( NULL == new_list ) {
+ DEBUG(0,("add_ip_to_name_record: Malloc fail !\n"));
+ return;
+ }
+
+ memcpy( (char *)new_list, (char *)namerec->data.ip, namerec->data.num_ips * sizeof(struct in_addr) );
+ new_list[namerec->data.num_ips] = new_ip;
+
+ SAFE_FREE(namerec->data.ip);
+ namerec->data.ip = new_list;
+ namerec->data.num_ips += 1;
+
+ namerec->subnet->namelist_changed = True;
+}
+
+/*******************************************************************
+ Utility function to remove an IP address from a name record.
+ ******************************************************************/
+
+void remove_ip_from_name_record( struct name_record *namerec,
+ struct in_addr remove_ip )
+{
+ /* Try and find the requested ip address - remove it. */
+ int i;
+ int orig_num = namerec->data.num_ips;
+
+ for(i = 0; i < orig_num; i++) {
+ if( ip_equal( remove_ip, namerec->data.ip[i]) ) {
+ remove_nth_ip_in_record( namerec, i);
+ break;
+ }
+ }
+}
+
+/*******************************************************************
+ Utility function that release_name callers can plug into as the
+ success function when a name release is successful. Used to save
+ duplication of success_function code.
+ ******************************************************************/
+
+void standard_success_release( struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname,
+ struct in_addr released_ip )
+{
+ struct name_record *namerec;
+
+ namerec = find_name_on_subnet( subrec, nmbname, FIND_ANY_NAME );
+ if( namerec == NULL ) {
+ DEBUG( 0, ( "standard_success_release: Name release for name %s IP %s \
+on subnet %s. Name was not found on subnet.\n", nmb_namestr(nmbname), inet_ntoa(released_ip),
+ subrec->subnet_name) );
+ return;
+ } else {
+ int orig_num = namerec->data.num_ips;
+
+ remove_ip_from_name_record( namerec, released_ip );
+
+ if( namerec->data.num_ips == orig_num )
+ DEBUG( 0, ( "standard_success_release: Name release for name %s IP %s \
+on subnet %s. This ip is not known for this name.\n", nmb_namestr(nmbname), inet_ntoa(released_ip), subrec->subnet_name ) );
+ }
+
+ if( namerec->data.num_ips == 0 )
+ remove_name_from_namelist( subrec, namerec );
+}
+
+/*******************************************************************
+ Expires old names in a subnet namelist.
+******************************************************************/
+
+void expire_names_on_subnet(struct subnet_record *subrec, time_t t)
+{
+ struct name_record *namerec;
+ struct name_record *next_namerec;
+
+ for( namerec = (struct name_record *)ubi_trFirst( subrec->namelist ); namerec; namerec = next_namerec ) {
+ next_namerec = (struct name_record *)ubi_trNext( namerec );
+ if( (namerec->data.death_time != PERMANENT_TTL) && (namerec->data.death_time < t) ) {
+ if( namerec->data.source == SELF_NAME ) {
+ DEBUG( 3, ( "expire_names_on_subnet: Subnet %s not expiring SELF \
+name %s\n", subrec->subnet_name, nmb_namestr(&namerec->name) ) );
+ namerec->data.death_time += 300;
+ namerec->subnet->namelist_changed = True;
+ continue;
+ }
+
+ DEBUG(3,("expire_names_on_subnet: Subnet %s - removing expired name %s\n",
+ subrec->subnet_name, nmb_namestr(&namerec->name)));
+
+ remove_name_from_namelist( subrec, namerec );
+ }
+ }
+}
+
+/*******************************************************************
+ Expires old names in all subnet namelists.
+******************************************************************/
+
+void expire_names(time_t t)
+{
+ struct subnet_record *subrec;
+
+ for( subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec) ) {
+ expire_names_on_subnet( subrec, t );
+ }
+}
+
+/****************************************************************************
+ Add the magic samba names, useful for finding samba servers.
+ These go directly into the name list for a particular subnet,
+ without going through the normal registration process.
+ When adding them to the unicast subnet, add them as a list of
+ all broadcast subnet IP addresses.
+**************************************************************************/
+
+void add_samba_names_to_subnet( struct subnet_record *subrec )
+{
+ struct in_addr *iplist = &subrec->myip;
+ int num_ips = 1;
+
+ /* These names are added permanently (ttl of zero) and will NOT be refreshed. */
+
+ if( (subrec == unicast_subnet) || (subrec == wins_server_subnet) || (subrec == remote_broadcast_subnet) ) {
+ struct subnet_record *bcast_subrecs;
+ int i;
+
+ /* Create an IP list containing all our known subnets. */
+
+ num_ips = iface_count();
+ iplist = (struct in_addr *)malloc( num_ips * sizeof(struct in_addr) );
+ if( NULL == iplist ) {
+ DEBUG(0,("add_samba_names_to_subnet: Malloc fail !\n"));
+ return;
+ }
+
+ for( bcast_subrecs = FIRST_SUBNET, i = 0; bcast_subrecs; bcast_subrecs = NEXT_SUBNET_EXCLUDING_UNICAST(bcast_subrecs), i++ )
+ iplist[i] = bcast_subrecs->myip;
+ }
+
+ add_name_to_subnet(subrec,"*",0x0,samba_nb_type, PERMANENT_TTL,
+ PERMANENT_NAME, num_ips, iplist);
+ add_name_to_subnet(subrec,"*",0x20,samba_nb_type,PERMANENT_TTL,
+ PERMANENT_NAME, num_ips, iplist);
+ add_name_to_subnet(subrec,"__SAMBA__",0x20,samba_nb_type,PERMANENT_TTL,
+ PERMANENT_NAME, num_ips, iplist);
+ add_name_to_subnet(subrec,"__SAMBA__",0x00,samba_nb_type,PERMANENT_TTL,
+ PERMANENT_NAME, num_ips, iplist);
+
+ if(iplist != &subrec->myip)
+ SAFE_FREE(iplist);
+}
+
+/****************************************************************************
+ Dump the contents of the namelists on all the subnets (including unicast)
+ into a file. Initiated by SIGHUP - used to debug the state of the namelists.
+**************************************************************************/
+
+static void dump_subnet_namelist( struct subnet_record *subrec, XFILE *fp)
+{
+ struct name_record *namerec;
+ const char *src_type;
+ struct tm *tm;
+ int i;
+
+ x_fprintf(fp, "Subnet %s\n----------------------\n", subrec->subnet_name);
+ for( namerec = (struct name_record *)ubi_trFirst( subrec->namelist ); namerec;
+ namerec = (struct name_record *)ubi_trNext( namerec ) ) {
+
+ x_fprintf(fp,"\tName = %s\t", nmb_namestr(&namerec->name));
+ switch(namerec->data.source) {
+ case LMHOSTS_NAME:
+ src_type = "LMHOSTS_NAME";
+ break;
+ case WINS_PROXY_NAME:
+ src_type = "WINS_PROXY_NAME";
+ break;
+ case REGISTER_NAME:
+ src_type = "REGISTER_NAME";
+ break;
+ case SELF_NAME:
+ src_type = "SELF_NAME";
+ break;
+ case DNS_NAME:
+ src_type = "DNS_NAME";
+ break;
+ case DNSFAIL_NAME:
+ src_type = "DNSFAIL_NAME";
+ break;
+ case PERMANENT_NAME:
+ src_type = "PERMANENT_NAME";
+ break;
+ default:
+ src_type = "unknown!";
+ break;
+ }
+
+ x_fprintf(fp,"Source = %s\nb_flags = %x\t", src_type, namerec->data.nb_flags);
+
+ if(namerec->data.death_time != PERMANENT_TTL) {
+ tm = LocalTime(&namerec->data.death_time);
+ x_fprintf(fp, "death_time = %s\t", asctime(tm));
+ } else {
+ x_fprintf(fp, "death_time = PERMANENT\t");
+ }
+
+ if(namerec->data.refresh_time != PERMANENT_TTL) {
+ tm = LocalTime(&namerec->data.refresh_time);
+ x_fprintf(fp, "refresh_time = %s\n", asctime(tm));
+ } else {
+ x_fprintf(fp, "refresh_time = PERMANENT\n");
+ }
+
+ x_fprintf(fp, "\t\tnumber of IPS = %d", namerec->data.num_ips);
+ for(i = 0; i < namerec->data.num_ips; i++)
+ x_fprintf(fp, "\t%s", inet_ntoa(namerec->data.ip[i]));
+
+ x_fprintf(fp, "\n\n");
+ }
+}
+
+/****************************************************************************
+ Dump the contents of the namelists on all the subnets (including unicast)
+ into a file. Initiated by SIGHUP - used to debug the state of the namelists.
+**************************************************************************/
+
+void dump_all_namelists(void)
+{
+ XFILE *fp;
+ struct subnet_record *subrec;
+
+ fp = x_fopen(lock_path("namelist.debug"),O_WRONLY|O_CREAT|O_TRUNC, 0644);
+
+ if (!fp) {
+ DEBUG(0,("dump_all_namelists: Can't open file %s. Error was %s\n",
+ "namelist.debug",strerror(errno)));
+ return;
+ }
+
+ for( subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec) )
+ dump_subnet_namelist( subrec, fp );
+
+ if( !we_are_a_wins_client() )
+ dump_subnet_namelist( unicast_subnet, fp );
+
+ if( remote_broadcast_subnet->namelist != NULL )
+ dump_subnet_namelist( remote_broadcast_subnet, fp );
+
+ if( wins_server_subnet != NULL )
+ dump_subnet_namelist( wins_server_subnet, fp );
+ x_fclose( fp );
+}
diff --git a/source/nmbd/nmbd_namequery.c b/source/nmbd/nmbd_namequery.c
new file mode 100644
index 00000000000..1b07852f111
--- /dev/null
+++ b/source/nmbd/nmbd_namequery.c
@@ -0,0 +1,268 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+ Deal with a response packet when querying a name.
+****************************************************************************/
+
+static void query_name_response( struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ BOOL success = False;
+ struct nmb_name *question_name = &rrec->packet->packet.nmb.question.question_name;
+ struct in_addr answer_ip;
+
+ zero_ip(&answer_ip);
+
+ /* Ensure we don't retry the query but leave the response record cleanup
+ to the timeout code. We may get more answer responses in which case
+ we should mark the name in conflict.. */
+ rrec->repeat_count = 0;
+
+ if(rrec->num_msgs == 1) {
+ /* This is the first response. */
+
+ if(nmb->header.opcode == NMB_WACK_OPCODE) {
+ /* WINS server is telling us to wait. Pretend we didn't get
+ the response but don't send out any more query requests. */
+
+ if( DEBUGLVL( 5 ) ) {
+ dbgtext( "query_name_response: " );
+ dbgtext( "WACK from WINS server %s ", inet_ntoa(p->ip) );
+ dbgtext( "in querying name %s ", nmb_namestr(question_name) );
+ dbgtext( "on subnet %s.\n", subrec->subnet_name );
+ }
+
+ rrec->repeat_count = 0;
+ /* How long we should wait for. */
+ rrec->repeat_time = p->timestamp + nmb->answers->ttl;
+ rrec->num_msgs--;
+ return;
+ } else if(nmb->header.rcode != 0) {
+
+ success = False;
+
+ if( DEBUGLVL( 5 ) ) {
+ dbgtext( "query_name_response: On subnet %s ", subrec->subnet_name );
+ dbgtext( "- negative response from IP %s ", inet_ntoa(p->ip) );
+ dbgtext( "for name %s. ", nmb_namestr(question_name) );
+ dbgtext( "Error code was %d.\n", nmb->header.rcode );
+ }
+ } else {
+ if (!nmb->answers) {
+ dbgtext( "query_name_response: On subnet %s ", subrec->subnet_name );
+ dbgtext( "IP %s ", inet_ntoa(p->ip) );
+ dbgtext( "returned a success response with no answer\n" );
+ return;
+ }
+
+ success = True;
+
+ putip((char *)&answer_ip,&nmb->answers->rdata[2]);
+
+ if( DEBUGLVL( 5 ) ) {
+ dbgtext( "query_name_response: On subnet %s ", subrec->subnet_name );
+ dbgtext( "- positive response from IP %s ", inet_ntoa(p->ip) );
+ dbgtext( "for name %s. ", nmb_namestr(question_name) );
+ dbgtext( "IP of that name is %s\n", inet_ntoa(answer_ip) );
+ }
+
+ /* Interestingly, we could add these names to our namelists, and
+ change nmbd to a model that checked its own name cache first,
+ before sending out a query. This is a task for another day, though.
+ */
+ }
+ } else if( rrec->num_msgs > 1) {
+
+ if( DEBUGLVL( 0 ) ) {
+ if (nmb->answers)
+ putip( (char *)&answer_ip, &nmb->answers->rdata[2] );
+ dbgtext( "query_name_response: " );
+ dbgtext( "Multiple (%d) responses ", rrec->num_msgs );
+ dbgtext( "received for a query on subnet %s ", subrec->subnet_name );
+ dbgtext( "for name %s.\nThis response ", nmb_namestr(question_name) );
+ dbgtext( "was from IP %s, reporting ", inet_ntoa(p->ip) );
+ dbgtext( "an IP address of %s.\n", inet_ntoa(answer_ip) );
+ }
+
+ /* We have already called the success or fail function, so we
+ don't call again here. Leave the response record around in
+ case we get more responses. */
+
+ return;
+ }
+
+ if(success && rrec->success_fn)
+ (*(query_name_success_function)rrec->success_fn)(subrec, rrec->userdata, question_name, answer_ip, nmb->answers);
+ else if( rrec->fail_fn)
+ (*(query_name_fail_function)rrec->fail_fn)(subrec, rrec, question_name, nmb->header.rcode);
+
+}
+
+/****************************************************************************
+ Deal with a timeout when querying a name.
+****************************************************************************/
+
+static void query_name_timeout_response(struct subnet_record *subrec,
+ struct response_record *rrec)
+{
+ struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+ /* We can only fail here, never succeed. */
+ BOOL failed = True;
+ struct nmb_name *question_name = &sent_nmb->question.question_name;
+
+ if(rrec->num_msgs != 0) {
+ /* We got at least one response, and have called the success/fail
+ function already. */
+
+ failed = False;
+ }
+
+ if(failed) {
+ if( DEBUGLVL( 5 ) ) {
+ dbgtext( "query_name_timeout_response: No response to " );
+ dbgtext( "query for name %s ", nmb_namestr(question_name) );
+ dbgtext( "on subnet %s.\n", subrec->subnet_name );
+ }
+
+ if(rrec->fail_fn)
+ (*(query_name_fail_function)rrec->fail_fn)(subrec, rrec, question_name, 0);
+ }
+
+ remove_response_record(subrec, rrec);
+}
+
+/****************************************************************************
+ Lookup a name on our local namelists. We check the lmhosts file first. If the
+ name is not there we look for the name on the given subnet.
+****************************************************************************/
+
+static BOOL query_local_namelists(struct subnet_record *subrec, struct nmb_name *nmbname,
+ struct name_record **namerecp)
+{
+ struct name_record *namerec;
+
+ *namerecp = NULL;
+
+ if(find_name_in_lmhosts(nmbname, namerecp))
+ return True;
+
+ if((namerec = find_name_on_subnet(subrec, nmbname, FIND_ANY_NAME))==NULL)
+ return False;
+
+ if( NAME_IS_ACTIVE(namerec) && ( (namerec->data.source == SELF_NAME) || (namerec->data.source == LMHOSTS_NAME) ) ) {
+ *namerecp = namerec;
+ return True;
+ }
+ return False;
+}
+
+/****************************************************************************
+ Try and query for a name.
+****************************************************************************/
+
+BOOL query_name(struct subnet_record *subrec, const char *name, int type,
+ query_name_success_function success_fn,
+ query_name_fail_function fail_fn,
+ struct userdata_struct *userdata)
+{
+ struct nmb_name nmbname;
+ struct name_record *namerec;
+
+ make_nmb_name(&nmbname, name, type);
+
+ /*
+ * We need to check our local namelists first.
+ * It may be an magic name, lmhosts name or just
+ * a name we have registered.
+ */
+
+ if(query_local_namelists(subrec, &nmbname, &namerec) == True) {
+ struct res_rec rrec;
+ int i;
+
+ memset((char *)&rrec, '\0', sizeof(struct res_rec));
+
+ /* Fake up the needed res_rec just in case it's used. */
+ rrec.rr_name = nmbname;
+ rrec.rr_type = RR_TYPE_NB;
+ rrec.rr_class = RR_CLASS_IN;
+ rrec.ttl = PERMANENT_TTL;
+ rrec.rdlength = namerec->data.num_ips * 6;
+ if(rrec.rdlength > MAX_DGRAM_SIZE) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "query_name: nmbd internal error - " );
+ dbgtext( "there are %d ip addresses ", namerec->data.num_ips );
+ dbgtext( "for name %s.\n", nmb_namestr(&nmbname) );
+ }
+ return False;
+ }
+
+ for( i = 0; i < namerec->data.num_ips; i++) {
+ set_nb_flags( &rrec.rdata[i*6], namerec->data.nb_flags );
+ putip( &rrec.rdata[(i*6) + 2], (char *)&namerec->data.ip[i]);
+ }
+
+ /* Call the success function directly. */
+ if(success_fn)
+ (*(query_name_success_function)success_fn)(subrec, userdata, &nmbname, namerec->data.ip[0], &rrec);
+ return False;
+ }
+
+ if(queue_query_name( subrec, query_name_response, query_name_timeout_response, success_fn, fail_fn, userdata, &nmbname) == NULL) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "query_name: Failed to send packet " );
+ dbgtext( "trying to query name %s\n", nmb_namestr(&nmbname) );
+ }
+ return True;
+ }
+ return False;
+}
+
+/****************************************************************************
+ Try and query for a name from nmbd acting as a WINS server.
+****************************************************************************/
+
+BOOL query_name_from_wins_server(struct in_addr ip_to,
+ const char *name, int type,
+ query_name_success_function success_fn,
+ query_name_fail_function fail_fn,
+ struct userdata_struct *userdata)
+{
+ struct nmb_name nmbname;
+
+ make_nmb_name(&nmbname, name, type);
+
+ if(queue_query_name_from_wins_server( ip_to, query_name_response, query_name_timeout_response, success_fn, fail_fn, userdata, &nmbname) == NULL) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "query_name_from_wins_server: Failed to send packet " );
+ dbgtext( "trying to query name %s\n", nmb_namestr(&nmbname) );
+ }
+ return True;
+ }
+ return False;
+}
diff --git a/source/nmbd/nmbd_nameregister.c b/source/nmbd/nmbd_nameregister.c
new file mode 100644
index 00000000000..4e11881f063
--- /dev/null
+++ b/source/nmbd/nmbd_nameregister.c
@@ -0,0 +1,537 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+
+/* forward declarations */
+static void wins_next_registration(struct response_record *rrec);
+
+
+/****************************************************************************
+ Deal with a response packet when registering one of our names.
+****************************************************************************/
+
+static void register_name_response(struct subnet_record *subrec,
+ struct response_record *rrec, struct packet_struct *p)
+{
+ /*
+ * If we are registering broadcast, then getting a response is an
+ * error - we do not have the name. If we are registering unicast,
+ * then we expect to get a response.
+ */
+
+ struct nmb_packet *nmb = &p->packet.nmb;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ BOOL success = True;
+ struct nmb_name *question_name = &rrec->packet->packet.nmb.question.question_name;
+ struct nmb_name *answer_name = &nmb->answers->rr_name;
+ struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+ int ttl = 0;
+ uint16 nb_flags = 0;
+ struct in_addr register_ip;
+ fstring reg_name;
+
+ putip(&register_ip,&sent_nmb->additional->rdata[2]);
+ fstrcpy(reg_name, inet_ntoa(register_ip));
+
+ if (subrec == unicast_subnet) {
+ /* we know that this wins server is definately alive - for the moment! */
+ wins_srv_alive(rrec->packet->ip, register_ip);
+ }
+
+ /* Sanity check. Ensure that the answer name in the incoming packet is the
+ same as the requested name in the outgoing packet. */
+
+ if(!question_name || !answer_name) {
+ DEBUG(0,("register_name_response: malformed response (%s is NULL).\n",
+ question_name ? "question_name" : "answer_name" ));
+ return;
+ }
+
+ if(!nmb_name_equal(question_name, answer_name)) {
+ DEBUG(0,("register_name_response: Answer name %s differs from question name %s.\n",
+ nmb_namestr(answer_name), nmb_namestr(question_name)));
+ return;
+ }
+
+ if(bcast) {
+ /*
+ * Special hack to cope with old Samba nmbd's.
+ * Earlier versions of Samba (up to 1.9.16p11) respond
+ * to a broadcast name registration of WORKGROUP<1b> when
+ * they should not. Hence, until these versions are gone,
+ * we should treat such errors as success for this particular
+ * case only. jallison@whistle.com.
+ */
+
+#if 1 /* OLD_SAMBA_SERVER_HACK */
+ unstring ans_name;
+ pull_ascii_nstring(ans_name, sizeof(ans_name), answer_name->name);
+ if((nmb->header.rcode == ACT_ERR) && strequal(lp_workgroup(), ans_name) &&
+ (answer_name->name_type == 0x1b)) {
+ /* Pretend we did not get this. */
+ rrec->num_msgs--;
+
+ DEBUG(5,("register_name_response: Ignoring broadcast response to registration of name %s due to old Samba server bug.\n",
+ nmb_namestr(answer_name)));
+ return;
+ }
+#endif /* OLD_SAMBA_SERVER_HACK */
+
+ /* Someone else has the name. Log the problem. */
+ DEBUG(1,("register_name_response: Failed to register name %s IP %s on subnet %s via broadcast. Error code was %d. Reject came from IP %s\n",
+ nmb_namestr(answer_name),
+ reg_name,
+ subrec->subnet_name, nmb->header.rcode, inet_ntoa(p->ip)));
+ success = False;
+ } else {
+ /* Unicast - check to see if the response allows us to have the name. */
+ if (nmb->header.opcode == NMB_WACK_OPCODE) {
+ /* WINS server is telling us to wait. Pretend we didn't get
+ the response but don't send out any more register requests. */
+
+ DEBUG(5,("register_name_response: WACK from WINS server %s in registering name %s IP %s\n",
+ inet_ntoa(p->ip), nmb_namestr(answer_name), reg_name));
+
+ rrec->repeat_count = 0;
+ /* How long we should wait for. */
+ rrec->repeat_time = p->timestamp + nmb->answers->ttl;
+ rrec->num_msgs--;
+ return;
+ } else if (nmb->header.rcode != 0) {
+ /* Error code - we didn't get the name. */
+ success = False;
+
+ DEBUG(0,("register_name_response: %sserver at IP %s rejected our name registration of %s IP %s with error code %d.\n",
+ subrec==unicast_subnet?"WINS ":"",
+ inet_ntoa(p->ip),
+ nmb_namestr(answer_name),
+ reg_name,
+ nmb->header.rcode));
+ } else {
+ success = True;
+ /* Get the data we need to pass to the success function. */
+ nb_flags = get_nb_flags(nmb->answers->rdata);
+ ttl = nmb->answers->ttl;
+
+ /* send off a registration for the next IP, if any */
+ wins_next_registration(rrec);
+ }
+ }
+
+ DEBUG(5,("register_name_response: %s in registering %sname %s IP %s with %s.\n",
+ success ? "success" : "failure",
+ subrec==unicast_subnet?"WINS ":"",
+ nmb_namestr(answer_name),
+ reg_name,
+ inet_ntoa(rrec->packet->ip)));
+
+ if(success) {
+ /* Enter the registered name into the subnet name database before calling
+ the success function. */
+ standard_success_register(subrec, rrec->userdata, answer_name, nb_flags, ttl, register_ip);
+ if( rrec->success_fn)
+ (*(register_name_success_function)rrec->success_fn)(subrec, rrec->userdata, answer_name, nb_flags, ttl, register_ip);
+ } else {
+ if( rrec->fail_fn)
+ (*(register_name_fail_function)rrec->fail_fn)(subrec, rrec, question_name);
+ /* Remove the name. */
+ standard_fail_register( subrec, rrec, question_name);
+ }
+
+ /* Ensure we don't retry. */
+ remove_response_record(subrec, rrec);
+}
+
+/****************************************************************************
+ Deal with a timeout of a WINS registration request
+****************************************************************************/
+
+static void wins_registration_timeout(struct subnet_record *subrec,
+ struct response_record *rrec)
+{
+ struct userdata_struct *userdata = rrec->userdata;
+ struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+ struct nmb_name *nmbname = &sent_nmb->question.question_name;
+ struct in_addr register_ip;
+ fstring src_addr;
+
+ putip(&register_ip,&sent_nmb->additional->rdata[2]);
+
+ fstrcpy(src_addr, inet_ntoa(register_ip));
+
+ DEBUG(2,("wins_registration_timeout: WINS server %s timed out registering IP %s\n",
+ inet_ntoa(rrec->packet->ip), src_addr));
+
+ /* mark it temporarily dead for this source address */
+ wins_srv_died(rrec->packet->ip, register_ip);
+
+ /* if we have some userdata then use that to work out what
+ wins server to try next */
+ if (userdata) {
+ const char *tag = (const char *)userdata->data;
+
+ /* try the next wins server in our failover list for
+ this tag */
+ rrec->packet->ip = wins_srv_ip_tag(tag, register_ip);
+ }
+
+ /* if we have run out of wins servers for this tag then they
+ must all have timed out. We treat this as *success*, not
+ failure, and go into our standard name refresh mode. This
+ copes with all the wins servers being down */
+ if (wins_srv_is_dead(rrec->packet->ip, register_ip)) {
+ uint16 nb_flags = get_nb_flags(sent_nmb->additional->rdata);
+ int ttl = sent_nmb->additional->ttl;
+
+ standard_success_register(subrec, userdata, nmbname, nb_flags, ttl, register_ip);
+ if(rrec->success_fn) {
+ (*(register_name_success_function)rrec->success_fn)(subrec,
+ rrec->userdata,
+ nmbname,
+ nb_flags,
+ ttl,
+ register_ip);
+ }
+
+ /* send off a registration for the next IP, if any */
+ wins_next_registration(rrec);
+
+ /* don't need to send this packet any more */
+ remove_response_record(subrec, rrec);
+ return;
+ }
+
+ /* we will be moving to the next WINS server for this group,
+ send it immediately */
+ rrec->repeat_count = 2;
+ rrec->repeat_time = time(NULL) + 1;
+ rrec->in_expiration_processing = False;
+
+ DEBUG(6,("Retrying register of name %s IP %s with WINS server %s\n",
+ nmb_namestr(nmbname), src_addr, inet_ntoa(rrec->packet->ip)));
+
+ /* notice that we don't remove the response record. This keeps
+ us trying to register with each of our failover wins servers */
+}
+
+/****************************************************************************
+ Deal with a timeout when registering one of our names.
+****************************************************************************/
+
+static void register_name_timeout_response(struct subnet_record *subrec,
+ struct response_record *rrec)
+{
+ /*
+ * If we are registering unicast, then NOT getting a response is an
+ * error - we do not have the name. If we are registering broadcast,
+ * then we don't expect to get a response.
+ */
+
+ struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+ BOOL bcast = sent_nmb->header.nm_flags.bcast;
+ BOOL success = False;
+ struct nmb_name *question_name = &sent_nmb->question.question_name;
+ uint16 nb_flags = 0;
+ int ttl = 0;
+ struct in_addr registered_ip;
+
+ if (bcast) {
+ if(rrec->num_msgs == 0) {
+ /* Not receiving a message is success for broadcast registration. */
+ success = True;
+
+ /* Pull the success values from the original request packet. */
+ nb_flags = get_nb_flags(sent_nmb->additional->rdata);
+ ttl = sent_nmb->additional->ttl;
+ putip(&registered_ip,&sent_nmb->additional->rdata[2]);
+ }
+ } else {
+ /* wins timeouts are special */
+ wins_registration_timeout(subrec, rrec);
+ return;
+ }
+
+ DEBUG(5,("register_name_timeout_response: %s in registering name %s on subnet %s.\n",
+ success ? "success" : "failure", nmb_namestr(question_name), subrec->subnet_name));
+ if(success) {
+ /* Enter the registered name into the subnet name database before calling
+ the success function. */
+ standard_success_register(subrec, rrec->userdata, question_name, nb_flags, ttl, registered_ip);
+ if( rrec->success_fn)
+ (*(register_name_success_function)rrec->success_fn)(subrec, rrec->userdata, question_name, nb_flags, ttl, registered_ip);
+ } else {
+ if( rrec->fail_fn)
+ (*(register_name_fail_function)rrec->fail_fn)(subrec, rrec, question_name);
+ /* Remove the name. */
+ standard_fail_register( subrec, rrec, question_name);
+ }
+
+ /* Ensure we don't retry. */
+ remove_response_record(subrec, rrec);
+}
+
+/****************************************************************************
+ Initiate one multi-homed name registration packet.
+****************************************************************************/
+
+static void multihomed_register_one(struct nmb_name *nmbname,
+ uint16 nb_flags,
+ register_name_success_function success_fn,
+ register_name_fail_function fail_fn,
+ struct in_addr ip,
+ const char *tag)
+{
+ struct userdata_struct *userdata;
+ struct in_addr wins_ip = wins_srv_ip_tag(tag, ip);
+ fstring ip_str;
+
+ userdata = (struct userdata_struct *)malloc(sizeof(*userdata) + strlen(tag) + 1);
+ if (!userdata) {
+ DEBUG(0,("Failed to allocate userdata structure!\n"));
+ return;
+ }
+ ZERO_STRUCTP(userdata);
+ userdata->userdata_len = strlen(tag) + 1;
+ strlcpy(userdata->data, tag, userdata->userdata_len);
+
+ fstrcpy(ip_str, inet_ntoa(ip));
+
+ DEBUG(6,("Registering name %s IP %s with WINS server %s using tag '%s'\n",
+ nmb_namestr(nmbname), ip_str, inet_ntoa(wins_ip), tag));
+
+ if (queue_register_multihomed_name(unicast_subnet,
+ register_name_response,
+ register_name_timeout_response,
+ success_fn,
+ fail_fn,
+ userdata,
+ nmbname,
+ nb_flags,
+ ip,
+ wins_ip) == NULL) {
+ DEBUG(0,("multihomed_register_one: Failed to send packet trying to register name %s IP %s\n",
+ nmb_namestr(nmbname), inet_ntoa(ip)));
+ }
+
+ free(userdata);
+}
+
+/****************************************************************************
+ We have finished the registration of one IP and need to see if we have
+ any more IPs left to register with this group of wins server for this name.
+****************************************************************************/
+
+static void wins_next_registration(struct response_record *rrec)
+{
+ struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+ struct nmb_name *nmbname = &sent_nmb->question.question_name;
+ uint16 nb_flags = get_nb_flags(sent_nmb->additional->rdata);
+ struct userdata_struct *userdata = rrec->userdata;
+ const char *tag;
+ struct in_addr last_ip;
+ struct subnet_record *subrec;
+
+ putip(&last_ip,&sent_nmb->additional->rdata[2]);
+
+ if (!userdata) {
+ /* it wasn't multi-homed */
+ return;
+ }
+
+ tag = (const char *)userdata->data;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ if (ip_equal(last_ip, subrec->myip)) {
+ subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec);
+ break;
+ }
+ }
+
+ if (!subrec) {
+ /* no more to do! */
+ return;
+ }
+
+ switch (sent_nmb->header.opcode) {
+ case NMB_NAME_MULTIHOMED_REG_OPCODE:
+ multihomed_register_one(nmbname, nb_flags, NULL, NULL, subrec->myip, tag);
+ break;
+ case NMB_NAME_REFRESH_OPCODE_8:
+ queue_wins_refresh(nmbname,
+ register_name_response,
+ register_name_timeout_response,
+ nb_flags, subrec->myip, tag);
+ break;
+ }
+}
+
+/****************************************************************************
+ Try and register one of our names on the unicast subnet - multihomed.
+****************************************************************************/
+
+static void multihomed_register_name(struct nmb_name *nmbname, uint16 nb_flags,
+ register_name_success_function success_fn,
+ register_name_fail_function fail_fn)
+{
+ /*
+ If we are adding a group name, we just send multiple
+ register name packets to the WINS server (this is an
+ internet group name.
+
+ If we are adding a unique name, We need first to add
+ our names to the unicast subnet namelist. This is
+ because when a WINS server receives a multihomed
+ registration request, the first thing it does is to
+ send a name query to the registering machine, to see
+ if it has put the name in it's local namelist.
+ We need the name there so the query response code in
+ nmbd_incomingrequests.c will find it.
+
+ We are adding this name prematurely (we don't really
+ have it yet), but as this is on the unicast subnet
+ only we will get away with this (only the WINS server
+ will ever query names from us on this subnet).
+ */
+ int num_ips=0;
+ int i, t;
+ struct subnet_record *subrec;
+ char **wins_tags;
+ struct in_addr *ip_list;
+ unstring name;
+
+ for(subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec) )
+ num_ips++;
+
+ if((ip_list = (struct in_addr *)malloc(num_ips * sizeof(struct in_addr)))==NULL) {
+ DEBUG(0,("multihomed_register_name: malloc fail !\n"));
+ return;
+ }
+
+ for (subrec = FIRST_SUBNET, i = 0;
+ subrec;
+ subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec), i++ ) {
+ ip_list[i] = subrec->myip;
+ }
+
+ pull_ascii_nstring(name, sizeof(name), nmbname->name);
+ add_name_to_subnet(unicast_subnet, name, nmbname->name_type,
+ nb_flags, lp_max_ttl(), SELF_NAME,
+ num_ips, ip_list);
+
+ /* get the list of wins tags - we try to register for each of them */
+ wins_tags = wins_srv_tags();
+
+ /* Now try and register the name for each wins tag. Note that
+ at this point we only register our first IP with each wins
+ group. We will register the rest from
+ wins_next_registration() when we get the reply for this
+ one. That follows the way W2K does things (tridge)
+ */
+ for (t=0; wins_tags && wins_tags[t]; t++) {
+ multihomed_register_one(nmbname, nb_flags,
+ success_fn, fail_fn,
+ ip_list[0],
+ wins_tags[t]);
+ }
+
+ wins_srv_tags_free(wins_tags);
+
+ SAFE_FREE(ip_list);
+}
+
+/****************************************************************************
+ Try and register one of our names.
+****************************************************************************/
+
+void register_name(struct subnet_record *subrec,
+ const char *name, int type, uint16 nb_flags,
+ register_name_success_function success_fn,
+ register_name_fail_function fail_fn,
+ struct userdata_struct *userdata)
+{
+ struct nmb_name nmbname;
+ nstring nname;
+
+ errno = 0;
+ push_ascii_nstring(nname, name);
+ if (errno == E2BIG) {
+ unstring tname;
+ pull_ascii_nstring(tname, sizeof(tname), nname);
+ DEBUG(0,("register_name: NetBIOS name %s is too long. Truncating to %s\n",
+ name, tname));
+ make_nmb_name(&nmbname, tname, type);
+ } else {
+ make_nmb_name(&nmbname, name, type);
+ }
+
+ /* Always set the NB_ACTIVE flag on the name we are
+ registering. Doesn't make sense without it.
+ */
+
+ nb_flags |= NB_ACTIVE;
+
+ if (subrec == unicast_subnet) {
+ /* we now always do multi-homed registration if we are
+ registering to a WINS server. This copes much
+ better with complex WINS setups */
+ multihomed_register_name(&nmbname, nb_flags,
+ success_fn, fail_fn);
+ return;
+ }
+
+ if (queue_register_name(subrec,
+ register_name_response,
+ register_name_timeout_response,
+ success_fn,
+ fail_fn,
+ userdata,
+ &nmbname,
+ nb_flags) == NULL) {
+ DEBUG(0,("register_name: Failed to send packet trying to register name %s\n",
+ nmb_namestr(&nmbname)));
+ }
+}
+
+/****************************************************************************
+ Try and refresh one of our names. This is *only* called for WINS refresh
+****************************************************************************/
+
+void wins_refresh_name(struct name_record *namerec)
+{
+ int t;
+ char **wins_tags;
+
+ /* get the list of wins tags - we try to refresh for each of them */
+ wins_tags = wins_srv_tags();
+
+ for (t=0; wins_tags && wins_tags[t]; t++) {
+ queue_wins_refresh(&namerec->name,
+ register_name_response,
+ register_name_timeout_response,
+ namerec->data.nb_flags,
+ namerec->data.ip[0], wins_tags[t]);
+ }
+
+ wins_srv_tags_free(wins_tags);
+}
diff --git a/source/nmbd/nmbd_namerelease.c b/source/nmbd/nmbd_namerelease.c
new file mode 100644
index 00000000000..0611ca93234
--- /dev/null
+++ b/source/nmbd/nmbd_namerelease.c
@@ -0,0 +1,222 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+ Deal with a response packet when releasing one of our names.
+****************************************************************************/
+
+static void release_name_response(struct subnet_record *subrec,
+ struct response_record *rrec, struct packet_struct *p)
+{
+ /*
+ * If we are releasing broadcast, then getting a response is an
+ * error. If we are releasing unicast, then we expect to get a response.
+ */
+ struct nmb_packet *nmb = &p->packet.nmb;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ BOOL success = True;
+ struct nmb_name *question_name = &rrec->packet->packet.nmb.question.question_name;
+ struct nmb_name *answer_name = &nmb->answers->rr_name;
+ struct in_addr released_ip;
+
+ /* Sanity check. Ensure that the answer name in the incoming packet is the
+ same as the requested name in the outgoing packet. */
+ if (!nmb_name_equal(question_name, answer_name)) {
+ DEBUG(0,("release_name_response: Answer name %s differs from question name %s.\n",
+ nmb_namestr(answer_name), nmb_namestr(question_name)));
+ return;
+ }
+
+ if (bcast) {
+ /* Someone sent a response to a bcast release? ignore it. */
+ return;
+ }
+
+ /* Unicast - check to see if the response allows us to release the name. */
+ if (nmb->header.rcode != 0) {
+ /* Error code - we were told not to release the name ! What now ! */
+ success = False;
+
+ DEBUG(0,("release_name_response: WINS server at IP %s rejected our \
+name release of name %s with error code %d.\n",
+ inet_ntoa(p->ip),
+ nmb_namestr(answer_name), nmb->header.rcode));
+ } else if (nmb->header.opcode == NMB_WACK_OPCODE) {
+ /* WINS server is telling us to wait. Pretend we didn't get
+ the response but don't send out any more release requests. */
+
+ DEBUG(5,("release_name_response: WACK from WINS server %s in releasing \
+name %s on subnet %s.\n",
+ inet_ntoa(p->ip), nmb_namestr(answer_name), subrec->subnet_name));
+
+ rrec->repeat_count = 0;
+ /* How long we should wait for. */
+ rrec->repeat_time = p->timestamp + nmb->answers->ttl;
+ rrec->num_msgs--;
+ return;
+ }
+
+ DEBUG(5,("release_name_response: %s in releasing name %s on subnet %s.\n",
+ success ? "success" : "failure", nmb_namestr(answer_name), subrec->subnet_name));
+ if (success) {
+ putip((char*)&released_ip ,&nmb->answers->rdata[2]);
+
+ if(rrec->success_fn)
+ (*(release_name_success_function)rrec->success_fn)(subrec, rrec->userdata, answer_name, released_ip);
+ standard_success_release( subrec, rrec->userdata, answer_name, released_ip);
+ } else {
+ /* We have no standard_fail_release - maybe we should add one ? */
+ if (rrec->fail_fn) {
+ (*(release_name_fail_function)rrec->fail_fn)(subrec, rrec, answer_name);
+ }
+ }
+
+ remove_response_record(subrec, rrec);
+}
+
+/****************************************************************************
+ Deal with a timeout when releasing one of our names.
+****************************************************************************/
+
+static void release_name_timeout_response(struct subnet_record *subrec,
+ struct response_record *rrec)
+{
+ /* a release is *always* considered to be successful when it
+ times out. This doesn't cause problems as if a WINS server
+ doesn't respond and someone else wants the name then the
+ normal WACK/name query from the WINS server will cope */
+ struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+ BOOL bcast = sent_nmb->header.nm_flags.bcast;
+ struct nmb_name *question_name = &sent_nmb->question.question_name;
+ struct in_addr released_ip;
+
+ /* Get the ip address we were trying to release. */
+ putip((char*)&released_ip ,&sent_nmb->additional->rdata[2]);
+
+ if (!bcast) {
+ /* mark the WINS server temporarily dead */
+ wins_srv_died(rrec->packet->ip, released_ip);
+ }
+
+ DEBUG(5,("release_name_timeout_response: success in releasing name %s on subnet %s.\n",
+ nmb_namestr(question_name), subrec->subnet_name));
+
+ if (rrec->success_fn) {
+ (*(release_name_success_function)rrec->success_fn)(subrec, rrec->userdata, question_name, released_ip);
+ }
+
+ standard_success_release( subrec, rrec->userdata, question_name, released_ip);
+ remove_response_record(subrec, rrec);
+}
+
+
+/*
+ when releasing a name with WINS we need to send the release to each of
+ the WINS groups
+*/
+static void wins_release_name(struct name_record *namerec,
+ release_name_success_function success_fn,
+ release_name_fail_function fail_fn,
+ struct userdata_struct *userdata)
+{
+ int t, i;
+ char **wins_tags;
+
+ /* get the list of wins tags - we try to release for each of them */
+ wins_tags = wins_srv_tags();
+
+ for (t=0;wins_tags && wins_tags[t]; t++) {
+ for (i = 0; i < namerec->data.num_ips; i++) {
+ struct in_addr wins_ip = wins_srv_ip_tag(wins_tags[t], namerec->data.ip[i]);
+
+ BOOL last_one = ((i==namerec->data.num_ips - 1) && !wins_tags[t+1]);
+ if (queue_release_name(unicast_subnet,
+ release_name_response,
+ release_name_timeout_response,
+ last_one?success_fn : NULL,
+ last_one? fail_fn : NULL,
+ last_one? userdata : NULL,
+ &namerec->name,
+ namerec->data.nb_flags,
+ namerec->data.ip[i],
+ wins_ip) == NULL) {
+ DEBUG(0,("release_name: Failed to send packet trying to release name %s IP %s\n",
+ nmb_namestr(&namerec->name), inet_ntoa(namerec->data.ip[i]) ));
+ }
+ }
+ }
+
+ wins_srv_tags_free(wins_tags);
+}
+
+
+/****************************************************************************
+ Try and release one of our names.
+****************************************************************************/
+
+void release_name(struct subnet_record *subrec, struct name_record *namerec,
+ release_name_success_function success_fn,
+ release_name_fail_function fail_fn,
+ struct userdata_struct *userdata)
+{
+ int i;
+
+ /* Ensure it's a SELF name, and in the ACTIVE state. */
+ if ((namerec->data.source != SELF_NAME) || !NAME_IS_ACTIVE(namerec)) {
+ DEBUG(0,("release_name: Cannot release name %s from subnet %s. Source was %d \n",
+ nmb_namestr(&namerec->name), subrec->subnet_name, namerec->data.source));
+ return;
+ }
+
+ /* Set the name into the deregistering state. */
+ namerec->data.nb_flags |= NB_DEREG;
+
+ /* wins releases are a bit different */
+ if (subrec == unicast_subnet) {
+ wins_release_name(namerec, success_fn, fail_fn, userdata);
+ return;
+ }
+
+ /*
+ * Go through and release the name for all known ip addresses.
+ * Only call the success/fail function on the last one (it should
+ * only be done once).
+ */
+ for (i = 0; i < namerec->data.num_ips; i++) {
+ if (queue_release_name(subrec,
+ release_name_response,
+ release_name_timeout_response,
+ (i == (namerec->data.num_ips - 1)) ? success_fn : NULL,
+ (i == (namerec->data.num_ips - 1)) ? fail_fn : NULL,
+ (i == (namerec->data.num_ips - 1)) ? userdata : NULL,
+ &namerec->name,
+ namerec->data.nb_flags,
+ namerec->data.ip[i],
+ subrec->bcast_ip) == NULL) {
+ DEBUG(0,("release_name: Failed to send packet trying to release name %s IP %s\n",
+ nmb_namestr(&namerec->name), inet_ntoa(namerec->data.ip[i]) ));
+ }
+ }
+}
diff --git a/source/nmbd/nmbd_nodestatus.c b/source/nmbd/nmbd_nodestatus.c
new file mode 100644
index 00000000000..0ea5d6a8182
--- /dev/null
+++ b/source/nmbd/nmbd_nodestatus.c
@@ -0,0 +1,92 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+ Deal with a successful node status response.
+****************************************************************************/
+
+static void node_status_response(struct subnet_record *subrec,
+ struct response_record *rrec, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question_name = &rrec->packet->packet.nmb.question.question_name;
+ struct nmb_name *answer_name = &nmb->answers->rr_name;
+
+ /* Sanity check. Ensure that the answer name in the incoming packet is the
+ same as the requested name in the outgoing packet. */
+
+ if(!nmb_name_equal(question_name, answer_name)) {
+ DEBUG(0,("node_status_response: Answer name %s differs from question \
+name %s.\n", nmb_namestr(answer_name), nmb_namestr(question_name)));
+ return;
+ }
+
+ DEBUG(5,("node_status_response: response from name %s on subnet %s.\n",
+ nmb_namestr(answer_name), subrec->subnet_name));
+
+ /* Just send the whole answer resource record for the success function to parse. */
+ if(rrec->success_fn)
+ (*(node_status_success_function)rrec->success_fn)(subrec, rrec->userdata, nmb->answers, p->ip);
+
+ /* Ensure we don't retry. */
+ remove_response_record(subrec, rrec);
+}
+
+/****************************************************************************
+ Deal with a timeout when requesting a node status.
+****************************************************************************/
+
+static void node_status_timeout_response(struct subnet_record *subrec,
+ struct response_record *rrec)
+{
+ struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+ struct nmb_name *question_name = &sent_nmb->question.question_name;
+
+ DEBUG(5,("node_status_timeout_response: failed to get node status from name %s on subnet %s\n",
+ nmb_namestr(question_name), subrec->subnet_name));
+
+ if( rrec->fail_fn)
+ (*rrec->fail_fn)(subrec, rrec);
+
+ /* Ensure we don't retry. */
+ remove_response_record(subrec, rrec);
+}
+
+/****************************************************************************
+ Try and do a node status to a name - given the name & IP address.
+****************************************************************************/
+
+BOOL node_status(struct subnet_record *subrec, struct nmb_name *nmbname,
+ struct in_addr send_ip, node_status_success_function success_fn,
+ node_status_fail_function fail_fn, struct userdata_struct *userdata)
+{
+ if(queue_node_status( subrec, node_status_response, node_status_timeout_response,
+ success_fn, fail_fn, userdata, nmbname, send_ip)==NULL) {
+ DEBUG(0,("node_status: Failed to send packet trying to get node status for \
+name %s, IP address %s\n", nmb_namestr(nmbname), inet_ntoa(send_ip)));
+ return True;
+ }
+ return False;
+}
diff --git a/source/nmbd/nmbd_packets.c b/source/nmbd/nmbd_packets.c
new file mode 100644
index 00000000000..d8b50a1b2e9
--- /dev/null
+++ b/source/nmbd/nmbd_packets.c
@@ -0,0 +1,1885 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+
+extern int ClientNMB;
+extern int ClientDGRAM;
+extern int global_nmb_port;
+
+extern int num_response_packets;
+
+extern struct in_addr loopback_ip;
+
+static void queue_packet(struct packet_struct *packet);
+
+BOOL rescan_listen_set = False;
+
+
+/*******************************************************************
+ The global packet linked-list. Incoming entries are
+ added to the end of this list. It is supposed to remain fairly
+ short so we won't bother with an end pointer.
+******************************************************************/
+
+static struct packet_struct *packet_queue = NULL;
+
+/***************************************************************************
+Utility function to find the specific fd to send a packet out on.
+**************************************************************************/
+
+static int find_subnet_fd_for_address( struct in_addr local_ip )
+{
+ struct subnet_record *subrec;
+
+ for( subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+ if(ip_equal(local_ip, subrec->myip))
+ return subrec->nmb_sock;
+
+ return ClientNMB;
+}
+
+/***************************************************************************
+Utility function to find the specific fd to send a mailslot packet out on.
+**************************************************************************/
+
+static int find_subnet_mailslot_fd_for_address( struct in_addr local_ip )
+{
+ struct subnet_record *subrec;
+
+ for( subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+ if(ip_equal(local_ip, subrec->myip))
+ return subrec->dgram_sock;
+
+ return ClientDGRAM;
+}
+
+/***************************************************************************
+Get/Set problematic nb_flags as network byte order 16 bit int.
+**************************************************************************/
+
+uint16 get_nb_flags(char *buf)
+{
+ return ((((uint16)*buf)&0xFFFF) & NB_FLGMSK);
+}
+
+void set_nb_flags(char *buf, uint16 nb_flags)
+{
+ *buf++ = ((nb_flags & NB_FLGMSK) & 0xFF);
+ *buf = '\0';
+}
+
+/***************************************************************************
+Dumps out the browse packet data.
+**************************************************************************/
+
+static void debug_browse_data(char *outbuf, int len)
+{
+ int i,j;
+
+ DEBUG( 4, ( "debug_browse_data():\n" ) );
+ for (i = 0; i < len; i+= 16) {
+ DEBUGADD( 4, ( "%3x char ", i ) );
+
+ for (j = 0; j < 16; j++) {
+ unsigned char x;
+ if (i+j >= len)
+ break;
+
+ x = outbuf[i+j];
+ if (x < 32 || x > 127)
+ x = '.';
+
+ DEBUGADD( 4, ( "%c", x ) );
+ }
+
+ DEBUGADD( 4, ( "%*s hex", 16-j, "" ) );
+
+ for (j = 0; j < 16; j++) {
+ if (i+j >= len)
+ break;
+ DEBUGADD( 4, ( " %02x", (unsigned char)outbuf[i+j] ) );
+ }
+
+ DEBUGADD( 4, ("\n") );
+ }
+}
+
+/***************************************************************************
+ Generates the unique transaction identifier
+**************************************************************************/
+
+static uint16 name_trn_id=0;
+
+static uint16 generate_name_trn_id(void)
+{
+ if (!name_trn_id) {
+ name_trn_id = ((unsigned)time(NULL)%(unsigned)0x7FFF) + ((unsigned)sys_getpid()%(unsigned)100);
+ }
+ name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF;
+ return name_trn_id;
+}
+
+/***************************************************************************
+ Either loops back or sends out a completed NetBIOS packet.
+**************************************************************************/
+
+static BOOL send_netbios_packet(struct packet_struct *p)
+{
+ BOOL loopback_this_packet = False;
+
+ /* Check if we are sending to or from ourselves as a WINS server. */
+ if(ismyip(p->ip) && (p->port == global_nmb_port))
+ loopback_this_packet = True;
+
+ if(loopback_this_packet) {
+ struct packet_struct *lo_packet = NULL;
+ DEBUG(5,("send_netbios_packet: sending packet to ourselves.\n"));
+ if((lo_packet = copy_packet(p)) == NULL)
+ return False;
+ queue_packet(lo_packet);
+ } else if (!send_packet(p)) {
+ DEBUG(0,("send_netbios_packet: send_packet() to IP %s port %d failed\n",
+ inet_ntoa(p->ip),p->port));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ Sets up the common elements of an outgoing NetBIOS packet.
+
+ Note: do not attempt to rationalise whether rec_des should be set or not
+ in a particular situation. Just follow rfc_1002 or look at examples from WinXX.
+ It does NOT follow the rule that requests to the wins server always have
+ rec_des true. See for example name releases and refreshes
+**************************************************************************/
+
+static struct packet_struct *create_and_init_netbios_packet(struct nmb_name *nmbname,
+ BOOL bcast, BOOL rec_des,
+ struct in_addr to_ip)
+{
+ struct packet_struct *packet = NULL;
+ struct nmb_packet *nmb = NULL;
+
+ /* Allocate the packet_struct we will return. */
+ if((packet = (struct packet_struct *)malloc(sizeof(*packet))) == NULL) {
+ DEBUG(0,("create_and_init_netbios_packet: malloc fail (1) for packet struct.\n"));
+ return NULL;
+ }
+
+ memset((char *)packet,'\0',sizeof(*packet));
+
+ nmb = &packet->packet.nmb;
+
+ nmb->header.name_trn_id = generate_name_trn_id();
+ nmb->header.response = False;
+ nmb->header.nm_flags.recursion_desired = rec_des;
+ nmb->header.nm_flags.recursion_available = False;
+ nmb->header.nm_flags.trunc = False;
+ nmb->header.nm_flags.authoritative = False;
+ nmb->header.nm_flags.bcast = bcast;
+
+ nmb->header.rcode = 0;
+ nmb->header.qdcount = 1;
+ nmb->header.ancount = 0;
+ nmb->header.nscount = 0;
+
+ nmb->question.question_name = *nmbname;
+ nmb->question.question_type = QUESTION_TYPE_NB_QUERY;
+ nmb->question.question_class = QUESTION_CLASS_IN;
+
+ packet->ip = to_ip;
+ packet->port = NMB_PORT;
+ packet->fd = ClientNMB;
+ packet->timestamp = time(NULL);
+ packet->packet_type = NMB_PACKET;
+ packet->locked = False;
+
+ return packet; /* Caller must free. */
+}
+
+/***************************************************************************
+ Sets up the common elements of register, refresh or release packet.
+**************************************************************************/
+
+static BOOL create_and_init_additional_record(struct packet_struct *packet,
+ uint16 nb_flags,
+ struct in_addr *register_ip)
+{
+ struct nmb_packet *nmb = &packet->packet.nmb;
+
+ if((nmb->additional = (struct res_rec *)malloc(sizeof(struct res_rec))) == NULL) {
+ DEBUG(0,("initiate_name_register_packet: malloc fail for additional record.\n"));
+ return False;
+ }
+
+ memset((char *)nmb->additional,'\0',sizeof(struct res_rec));
+
+ nmb->additional->rr_name = nmb->question.question_name;
+ nmb->additional->rr_type = RR_TYPE_NB;
+ nmb->additional->rr_class = RR_CLASS_IN;
+
+ /* See RFC 1002, sections 5.1.1.1, 5.1.1.2 and 5.1.1.3 */
+ if (nmb->header.nm_flags.bcast)
+ nmb->additional->ttl = PERMANENT_TTL;
+ else
+ nmb->additional->ttl = lp_max_ttl();
+
+ nmb->additional->rdlength = 6;
+
+ set_nb_flags(nmb->additional->rdata,nb_flags);
+
+ /* Set the address for the name we are registering. */
+ putip(&nmb->additional->rdata[2], register_ip);
+
+ /*
+ it turns out that Jeremys code was correct, we are supposed
+ to send registrations from the IP we are registering. The
+ trick is what to do on timeouts! When we send on a
+ non-routable IP then the reply will timeout, and we should
+ treat this as success, not failure. That means we go into
+ our standard refresh cycle for that name which copes nicely
+ with disconnected networks.
+ */
+ packet->fd = find_subnet_fd_for_address(*register_ip);
+
+ return True;
+}
+
+/***************************************************************************
+ Sends out a name query.
+**************************************************************************/
+
+static BOOL initiate_name_query_packet( struct packet_struct *packet)
+{
+ struct nmb_packet *nmb = NULL;
+
+ nmb = &packet->packet.nmb;
+
+ nmb->header.opcode = NMB_NAME_QUERY_OPCODE;
+ nmb->header.arcount = 0;
+
+ nmb->header.nm_flags.recursion_desired = True;
+
+ DEBUG(4,("initiate_name_query_packet: sending query for name %s (bcast=%s) to IP %s\n",
+ nmb_namestr(&nmb->question.question_name),
+ BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip)));
+
+ return send_netbios_packet( packet );
+}
+
+/***************************************************************************
+ Sends out a name query - from a WINS server.
+**************************************************************************/
+
+static BOOL initiate_name_query_packet_from_wins_server( struct packet_struct *packet)
+{
+ struct nmb_packet *nmb = NULL;
+
+ nmb = &packet->packet.nmb;
+
+ nmb->header.opcode = NMB_NAME_QUERY_OPCODE;
+ nmb->header.arcount = 0;
+
+ nmb->header.nm_flags.recursion_desired = False;
+
+ DEBUG(4,("initiate_name_query_packet_from_wins_server: sending query for name %s (bcast=%s) to IP %s\n",
+ nmb_namestr(&nmb->question.question_name),
+ BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip)));
+
+ return send_netbios_packet( packet );
+}
+
+/***************************************************************************
+ Sends out a name register.
+**************************************************************************/
+
+static BOOL initiate_name_register_packet( struct packet_struct *packet,
+ uint16 nb_flags, struct in_addr *register_ip)
+{
+ struct nmb_packet *nmb = &packet->packet.nmb;
+
+ nmb->header.opcode = NMB_NAME_REG_OPCODE;
+ nmb->header.arcount = 1;
+
+ nmb->header.nm_flags.recursion_desired = True;
+
+ if(create_and_init_additional_record(packet, nb_flags, register_ip) == False)
+ return False;
+
+ DEBUG(4,("initiate_name_register_packet: sending registration for name %s (bcast=%s) to IP %s\n",
+ nmb_namestr(&nmb->additional->rr_name),
+ BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip)));
+
+ return send_netbios_packet( packet );
+}
+
+/***************************************************************************
+ Sends out a multihomed name register.
+**************************************************************************/
+
+static BOOL initiate_multihomed_name_register_packet(struct packet_struct *packet,
+ uint16 nb_flags, struct in_addr *register_ip)
+{
+ struct nmb_packet *nmb = &packet->packet.nmb;
+ fstring second_ip_buf;
+
+ fstrcpy(second_ip_buf, inet_ntoa(packet->ip));
+
+ nmb->header.opcode = NMB_NAME_MULTIHOMED_REG_OPCODE;
+ nmb->header.arcount = 1;
+
+ nmb->header.nm_flags.recursion_desired = True;
+
+ if(create_and_init_additional_record(packet, nb_flags, register_ip) == False)
+ return False;
+
+ DEBUG(4,("initiate_multihomed_name_register_packet: sending registration \
+for name %s IP %s (bcast=%s) to IP %s\n",
+ nmb_namestr(&nmb->additional->rr_name), inet_ntoa(*register_ip),
+ BOOLSTR(nmb->header.nm_flags.bcast), second_ip_buf ));
+
+ return send_netbios_packet( packet );
+}
+
+/***************************************************************************
+ Sends out a name refresh.
+**************************************************************************/
+
+static BOOL initiate_name_refresh_packet( struct packet_struct *packet,
+ uint16 nb_flags, struct in_addr *refresh_ip)
+{
+ struct nmb_packet *nmb = &packet->packet.nmb;
+
+ nmb->header.opcode = NMB_NAME_REFRESH_OPCODE_8;
+ nmb->header.arcount = 1;
+
+ nmb->header.nm_flags.recursion_desired = False;
+
+ if(create_and_init_additional_record(packet, nb_flags, refresh_ip) == False)
+ return False;
+
+ DEBUG(4,("initiate_name_refresh_packet: sending refresh for name %s (bcast=%s) to IP %s\n",
+ nmb_namestr(&nmb->additional->rr_name),
+ BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip)));
+
+ return send_netbios_packet( packet );
+}
+
+/***************************************************************************
+ Sends out a name release.
+**************************************************************************/
+
+static BOOL initiate_name_release_packet( struct packet_struct *packet,
+ uint16 nb_flags, struct in_addr *release_ip)
+{
+ struct nmb_packet *nmb = &packet->packet.nmb;
+
+ nmb->header.opcode = NMB_NAME_RELEASE_OPCODE;
+ nmb->header.arcount = 1;
+
+ nmb->header.nm_flags.recursion_desired = False;
+
+ if(create_and_init_additional_record(packet, nb_flags, release_ip) == False)
+ return False;
+
+ DEBUG(4,("initiate_name_release_packet: sending release for name %s (bcast=%s) to IP %s\n",
+ nmb_namestr(&nmb->additional->rr_name),
+ BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip)));
+
+ return send_netbios_packet( packet );
+}
+
+/***************************************************************************
+ Sends out a node status.
+**************************************************************************/
+
+static BOOL initiate_node_status_packet( struct packet_struct *packet )
+{
+ struct nmb_packet *nmb = &packet->packet.nmb;
+
+ nmb->header.opcode = NMB_NAME_QUERY_OPCODE;
+ nmb->header.arcount = 0;
+
+ nmb->header.nm_flags.recursion_desired = False;
+
+ nmb->question.question_type = QUESTION_TYPE_NB_STATUS;
+
+ DEBUG(4,("initiate_node_status_packet: sending node status request for name %s to IP %s\n",
+ nmb_namestr(&nmb->question.question_name),
+ inet_ntoa(packet->ip)));
+
+ return send_netbios_packet( packet );
+}
+
+/****************************************************************************
+ Simplification functions for queuing standard packets.
+ These should be the only publicly callable functions for sending
+ out packets.
+****************************************************************************/
+
+/****************************************************************************
+ Assertion - we should never be sending nmbd packets on the remote
+ broadcast subnet.
+****************************************************************************/
+
+static BOOL assert_check_subnet(struct subnet_record *subrec)
+{
+ if( subrec == remote_broadcast_subnet) {
+ DEBUG(0,("assert_check_subnet: Attempt to send packet on remote broadcast subnet. \
+This is a bug.\n"));
+ return True;
+ }
+ return False;
+}
+
+/****************************************************************************
+ Queue a register name packet to the broadcast address of a subnet.
+****************************************************************************/
+
+struct response_record *queue_register_name( struct subnet_record *subrec,
+ response_function resp_fn,
+ timeout_response_function timeout_fn,
+ register_name_success_function success_fn,
+ register_name_fail_function fail_fn,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname,
+ uint16 nb_flags)
+{
+ struct packet_struct *p;
+ struct response_record *rrec;
+
+ if(assert_check_subnet(subrec))
+ return NULL;
+
+ /* note that all name registration requests have RD set (rfc1002 - section 4.2.2 */
+ if ((p = create_and_init_netbios_packet(nmbname, (subrec != unicast_subnet), True,
+ subrec->bcast_ip)) == NULL)
+ return NULL;
+
+ if(initiate_name_register_packet( p, nb_flags, iface_ip(subrec->bcast_ip)) == False) {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ if((rrec = make_response_record(subrec, /* subnet record. */
+ p, /* packet we sent. */
+ resp_fn, /* function to call on response. */
+ timeout_fn, /* function to call on timeout. */
+ (success_function)success_fn, /* function to call on operation success. */
+ (fail_function)fail_fn, /* function to call on operation fail. */
+ userdata)) == NULL) {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ return rrec;
+}
+
+/****************************************************************************
+ Queue a refresh name packet to the broadcast address of a subnet.
+****************************************************************************/
+
+void queue_wins_refresh(struct nmb_name *nmbname,
+ response_function resp_fn,
+ timeout_response_function timeout_fn,
+ uint16 nb_flags,
+ struct in_addr refresh_ip,
+ const char *tag)
+{
+ struct packet_struct *p;
+ struct response_record *rrec;
+ struct in_addr wins_ip;
+ struct userdata_struct *userdata;
+ fstring ip_str;
+
+ wins_ip = wins_srv_ip_tag(tag, refresh_ip);
+
+ if ((p = create_and_init_netbios_packet(nmbname, False, False, wins_ip)) == NULL) {
+ return;
+ }
+
+ if (!initiate_name_refresh_packet(p, nb_flags, &refresh_ip)) {
+ p->locked = False;
+ free_packet(p);
+ return;
+ }
+
+ fstrcpy(ip_str, inet_ntoa(refresh_ip));
+
+ DEBUG(6,("Refreshing name %s IP %s with WINS server %s using tag '%s'\n",
+ nmb_namestr(nmbname), ip_str, inet_ntoa(wins_ip), tag));
+
+ userdata = (struct userdata_struct *)malloc(sizeof(*userdata) + strlen(tag) + 1);
+ if (!userdata) {
+ DEBUG(0,("Failed to allocate userdata structure!\n"));
+ return;
+ }
+ ZERO_STRUCTP(userdata);
+ userdata->userdata_len = strlen(tag) + 1;
+ strlcpy(userdata->data, tag, userdata->userdata_len);
+
+ if ((rrec = make_response_record(unicast_subnet,
+ p,
+ resp_fn, timeout_fn,
+ NULL,
+ NULL,
+ userdata)) == NULL) {
+ p->locked = False;
+ free_packet(p);
+ return;
+ }
+
+ free(userdata);
+
+ /* we don't want to repeat refresh packets */
+ rrec->repeat_count = 0;
+}
+
+
+/****************************************************************************
+ Queue a multihomed register name packet to a given WINS server IP
+****************************************************************************/
+
+struct response_record *queue_register_multihomed_name( struct subnet_record *subrec,
+ response_function resp_fn,
+ timeout_response_function timeout_fn,
+ register_name_success_function success_fn,
+ register_name_fail_function fail_fn,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname,
+ uint16 nb_flags,
+ struct in_addr register_ip,
+ struct in_addr wins_ip)
+{
+ struct packet_struct *p;
+ struct response_record *rrec;
+ BOOL ret;
+
+ /* Sanity check. */
+ if(subrec != unicast_subnet) {
+ DEBUG(0,("queue_register_multihomed_name: should only be done on \
+unicast subnet. subnet is %s\n.", subrec->subnet_name ));
+ return NULL;
+ }
+
+ if(assert_check_subnet(subrec))
+ return NULL;
+
+ if ((p = create_and_init_netbios_packet(nmbname, False, True, wins_ip)) == NULL)
+ return NULL;
+
+ if (nb_flags & NB_GROUP)
+ ret = initiate_name_register_packet( p, nb_flags, &register_ip);
+ else
+ ret = initiate_multihomed_name_register_packet(p, nb_flags, &register_ip);
+
+ if (ret == False) {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ if ((rrec = make_response_record(subrec, /* subnet record. */
+ p, /* packet we sent. */
+ resp_fn, /* function to call on response. */
+ timeout_fn, /* function to call on timeout. */
+ (success_function)success_fn, /* function to call on operation success. */
+ (fail_function)fail_fn, /* function to call on operation fail. */
+ userdata)) == NULL) {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ return rrec;
+}
+
+/****************************************************************************
+ Queue a release name packet to the broadcast address of a subnet.
+****************************************************************************/
+
+struct response_record *queue_release_name( struct subnet_record *subrec,
+ response_function resp_fn,
+ timeout_response_function timeout_fn,
+ release_name_success_function success_fn,
+ release_name_fail_function fail_fn,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname,
+ uint16 nb_flags,
+ struct in_addr release_ip,
+ struct in_addr dest_ip)
+{
+ struct packet_struct *p;
+ struct response_record *rrec;
+
+ if(assert_check_subnet(subrec))
+ return NULL;
+
+ if ((p = create_and_init_netbios_packet(nmbname, (subrec != unicast_subnet), False, dest_ip)) == NULL)
+ return NULL;
+
+ if(initiate_name_release_packet( p, nb_flags, &release_ip) == False) {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ if((rrec = make_response_record(subrec, /* subnet record. */
+ p, /* packet we sent. */
+ resp_fn, /* function to call on response. */
+ timeout_fn, /* function to call on timeout. */
+ (success_function)success_fn, /* function to call on operation success. */
+ (fail_function)fail_fn, /* function to call on operation fail. */
+ userdata)) == NULL) {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ /*
+ * For a broadcast release packet, only send once.
+ * This will cause us to remove the name asap. JRA.
+ */
+
+ if (subrec != unicast_subnet) {
+ rrec->repeat_count = 0;
+ rrec->repeat_time = 0;
+ }
+
+ return rrec;
+}
+
+/****************************************************************************
+ Queue a query name packet to the broadcast address of a subnet.
+****************************************************************************/
+
+struct response_record *queue_query_name( struct subnet_record *subrec,
+ response_function resp_fn,
+ timeout_response_function timeout_fn,
+ query_name_success_function success_fn,
+ query_name_fail_function fail_fn,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname)
+{
+ struct packet_struct *p;
+ struct response_record *rrec;
+ struct in_addr to_ip;
+
+ if(assert_check_subnet(subrec))
+ return NULL;
+
+ to_ip = subrec->bcast_ip;
+
+ /* queries to the WINS server turn up here as queries to IP 0.0.0.0
+ These need to be handled a bit differently */
+ if (subrec->type == UNICAST_SUBNET && is_zero_ip(to_ip)) {
+ /* What we really need to do is loop over each of our wins
+ * servers and wins server tags here, but that just doesn't
+ * fit our architecture at the moment (userdata may already
+ * be used when we get here). For now we just query the first
+ * active wins server on the first tag.
+ */
+ char **tags = wins_srv_tags();
+ if (!tags) {
+ return NULL;
+ }
+ to_ip = wins_srv_ip_tag(tags[0], to_ip);
+ wins_srv_tags_free(tags);
+ }
+
+ if(( p = create_and_init_netbios_packet(nmbname,
+ (subrec != unicast_subnet),
+ (subrec == unicast_subnet),
+ to_ip)) == NULL)
+ return NULL;
+
+ if(lp_bind_interfaces_only()) {
+ int i;
+
+ DEBUG(10,("queue_query_name: bind_interfaces_only is set, looking for suitable source IP\n"));
+ for(i = 0; i < iface_count(); i++) {
+ struct in_addr *ifip = iface_n_ip(i);
+
+ if(ifip == NULL) {
+ DEBUG(0,("queue_query_name: interface %d has NULL IP address !\n", i));
+ continue;
+ }
+
+ if (ip_equal(*ifip,loopback_ip)) {
+ DEBUG(5,("queue_query_name: ignoring loopback interface (%d)\n", i));
+ continue;
+ }
+
+ DEBUG(10,("queue_query_name: using source IP %s\n",inet_ntoa(*ifip)));
+ p->fd = find_subnet_fd_for_address( *ifip );
+ break;
+ }
+ }
+
+ if(initiate_name_query_packet( p ) == False) {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ if((rrec = make_response_record(subrec, /* subnet record. */
+ p, /* packet we sent. */
+ resp_fn, /* function to call on response. */
+ timeout_fn, /* function to call on timeout. */
+ (success_function)success_fn, /* function to call on operation success. */
+ (fail_function)fail_fn, /* function to call on operation fail. */
+ userdata)) == NULL) {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ return rrec;
+}
+
+/****************************************************************************
+ Queue a query name packet to a given address from the WINS subnet.
+****************************************************************************/
+
+struct response_record *queue_query_name_from_wins_server( struct in_addr to_ip,
+ response_function resp_fn,
+ timeout_response_function timeout_fn,
+ query_name_success_function success_fn,
+ query_name_fail_function fail_fn,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname)
+{
+ struct packet_struct *p;
+ struct response_record *rrec;
+
+ if ((p = create_and_init_netbios_packet(nmbname, False, False, to_ip)) == NULL)
+ return NULL;
+
+ if(initiate_name_query_packet_from_wins_server( p ) == False) {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ if((rrec = make_response_record(wins_server_subnet, /* subnet record. */
+ p, /* packet we sent. */
+ resp_fn, /* function to call on response. */
+ timeout_fn, /* function to call on timeout. */
+ (success_function)success_fn, /* function to call on operation success. */
+ (fail_function)fail_fn, /* function to call on operation fail. */
+ userdata)) == NULL) {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ return rrec;
+}
+
+/****************************************************************************
+ Queue a node status packet to a given name and address.
+****************************************************************************/
+
+struct response_record *queue_node_status( struct subnet_record *subrec,
+ response_function resp_fn,
+ timeout_response_function timeout_fn,
+ node_status_success_function success_fn,
+ node_status_fail_function fail_fn,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname,
+ struct in_addr send_ip)
+{
+ struct packet_struct *p;
+ struct response_record *rrec;
+
+ /* Sanity check. */
+ if(subrec != unicast_subnet) {
+ DEBUG(0,("queue_register_multihomed_name: should only be done on \
+unicast subnet. subnet is %s\n.", subrec->subnet_name ));
+ return NULL;
+ }
+
+ if(assert_check_subnet(subrec))
+ return NULL;
+
+ if(( p = create_and_init_netbios_packet(nmbname, False, False, send_ip)) == NULL)
+ return NULL;
+
+ if(initiate_node_status_packet(p) == False) {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ if((rrec = make_response_record(subrec, /* subnet record. */
+ p, /* packet we sent. */
+ resp_fn, /* function to call on response. */
+ timeout_fn, /* function to call on timeout. */
+ (success_function)success_fn, /* function to call on operation success. */
+ (fail_function)fail_fn, /* function to call on operation fail. */
+ userdata)) == NULL) {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ return rrec;
+}
+
+/****************************************************************************
+ Reply to a netbios name packet. see rfc1002.txt
+****************************************************************************/
+
+void reply_netbios_packet(struct packet_struct *orig_packet,
+ int rcode, enum netbios_reply_type_code rcv_code, int opcode,
+ int ttl, char *data,int len)
+{
+ struct packet_struct packet;
+ struct nmb_packet *nmb = NULL;
+ struct res_rec answers;
+ struct nmb_packet *orig_nmb = &orig_packet->packet.nmb;
+ BOOL loopback_this_packet = False;
+ const char *packet_type = "unknown";
+
+ /* Check if we are sending to or from ourselves. */
+ if(ismyip(orig_packet->ip) && (orig_packet->port == global_nmb_port))
+ loopback_this_packet = True;
+
+ nmb = &packet.packet.nmb;
+
+ /* Do a partial copy of the packet. We clear the locked flag and
+ the resource record pointers. */
+ packet = *orig_packet; /* Full structure copy. */
+ packet.locked = False;
+ nmb->answers = NULL;
+ nmb->nsrecs = NULL;
+ nmb->additional = NULL;
+
+ switch (rcv_code) {
+ case NMB_STATUS:
+ packet_type = "nmb_status";
+ nmb->header.nm_flags.recursion_desired = False;
+ nmb->header.nm_flags.recursion_available = False;
+ break;
+ case NMB_QUERY:
+ packet_type = "nmb_query";
+ nmb->header.nm_flags.recursion_desired = True;
+ nmb->header.nm_flags.recursion_available = True;
+ break;
+ case NMB_REG:
+ case NMB_REG_REFRESH:
+ packet_type = "nmb_reg";
+ nmb->header.nm_flags.recursion_desired = True;
+ nmb->header.nm_flags.recursion_available = True;
+ break;
+ case NMB_REL:
+ packet_type = "nmb_rel";
+ nmb->header.nm_flags.recursion_desired = False;
+ nmb->header.nm_flags.recursion_available = False;
+ break;
+ case NMB_WAIT_ACK:
+ packet_type = "nmb_wack";
+ nmb->header.nm_flags.recursion_desired = False;
+ nmb->header.nm_flags.recursion_available = False;
+ break;
+ case WINS_REG:
+ packet_type = "wins_reg";
+ nmb->header.nm_flags.recursion_desired = True;
+ nmb->header.nm_flags.recursion_available = True;
+ break;
+ case WINS_QUERY:
+ packet_type = "wins_query";
+ nmb->header.nm_flags.recursion_desired = True;
+ nmb->header.nm_flags.recursion_available = True;
+ break;
+ default:
+ DEBUG(0,("reply_netbios_packet: Unknown packet type: %s %s to ip %s\n",
+ packet_type, nmb_namestr(&orig_nmb->question.question_name),
+ inet_ntoa(packet.ip)));
+ return;
+ }
+
+ DEBUG(4,("reply_netbios_packet: sending a reply of packet type: %s %s to ip %s \
+for id %hu\n", packet_type, nmb_namestr(&orig_nmb->question.question_name),
+ inet_ntoa(packet.ip), orig_nmb->header.name_trn_id));
+
+ nmb->header.name_trn_id = orig_nmb->header.name_trn_id;
+ nmb->header.opcode = opcode;
+ nmb->header.response = True;
+ nmb->header.nm_flags.bcast = False;
+ nmb->header.nm_flags.trunc = False;
+ nmb->header.nm_flags.authoritative = True;
+
+ nmb->header.rcode = rcode;
+ nmb->header.qdcount = 0;
+ nmb->header.ancount = 1;
+ nmb->header.nscount = 0;
+ nmb->header.arcount = 0;
+
+ memset((char*)&nmb->question,'\0',sizeof(nmb->question));
+
+ nmb->answers = &answers;
+ memset((char*)nmb->answers,'\0',sizeof(*nmb->answers));
+
+ nmb->answers->rr_name = orig_nmb->question.question_name;
+ nmb->answers->rr_type = orig_nmb->question.question_type;
+ nmb->answers->rr_class = orig_nmb->question.question_class;
+ nmb->answers->ttl = ttl;
+
+ if (data && len) {
+ nmb->answers->rdlength = len;
+ memcpy(nmb->answers->rdata, data, len);
+ }
+
+ packet.packet_type = NMB_PACKET;
+ /* Ensure we send out on the same fd that the original
+ packet came in on to give the correct source IP address. */
+ packet.fd = orig_packet->fd;
+ packet.timestamp = time(NULL);
+
+ debug_nmb_packet(&packet);
+
+ if(loopback_this_packet) {
+ struct packet_struct *lo_packet;
+ DEBUG(5,("reply_netbios_packet: sending packet to ourselves.\n"));
+ if((lo_packet = copy_packet(&packet)) == NULL)
+ return;
+ queue_packet(lo_packet);
+ } else if (!send_packet(&packet)) {
+ DEBUG(0,("reply_netbios_packet: send_packet to IP %s port %d failed\n",
+ inet_ntoa(packet.ip),packet.port));
+ }
+}
+
+/*******************************************************************
+ Queue a packet into a packet queue
+******************************************************************/
+
+static void queue_packet(struct packet_struct *packet)
+{
+ struct packet_struct *p;
+
+ if (!packet_queue) {
+ packet->prev = NULL;
+ packet->next = NULL;
+ packet_queue = packet;
+ return;
+ }
+
+ /* find the bottom */
+ for (p=packet_queue;p->next;p=p->next)
+ ;
+
+ p->next = packet;
+ packet->next = NULL;
+ packet->prev = p;
+}
+
+/****************************************************************************
+ Try and find a matching subnet record for a datagram port 138 packet.
+****************************************************************************/
+
+static struct subnet_record *find_subnet_for_dgram_browse_packet(struct packet_struct *p)
+{
+ struct subnet_record *subrec;
+
+ /* Go through all the broadcast subnets and see if the mask matches. */
+ for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ if(same_net(p->ip, subrec->bcast_ip, subrec->mask_ip))
+ return subrec;
+ }
+
+ /* If the subnet record is the remote announce broadcast subnet,
+ hack it here to be the first subnet. This is really gross and
+ is needed due to people turning on port 137/138 broadcast
+ forwarding on their routers. May fire and brimstone rain
+ down upon them...
+ */
+
+ return FIRST_SUBNET;
+}
+
+/****************************************************************************
+Dispatch a browse frame from port 138 to the correct processing function.
+****************************************************************************/
+
+static void process_browse_packet(struct packet_struct *p, char *buf,int len)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ int command = CVAL(buf,0);
+ struct subnet_record *subrec = find_subnet_for_dgram_browse_packet(p);
+ char scope[64];
+ unstring src_name;
+
+ /* Drop the packet if it's a different NetBIOS scope, or the source is from one of our names. */
+ pull_ascii(scope, dgram->dest_name.scope, 64, 64, STR_TERMINATE);
+ if (!strequal(scope, global_scope())) {
+ DEBUG(7,("process_browse_packet: Discarding datagram from IP %s. Scope (%s) \
+mismatch with our scope (%s).\n", inet_ntoa(p->ip), scope, global_scope()));
+ return;
+ }
+
+ pull_ascii_nstring(src_name, sizeof(src_name), dgram->source_name.name);
+ if (is_myname(src_name)) {
+ DEBUG(0,("process_browse_packet: Discarding datagram from IP %s. Source name \
+%s is one of our names !\n", inet_ntoa(p->ip), nmb_namestr(&dgram->source_name)));
+ return;
+ }
+
+ switch (command) {
+ case ANN_HostAnnouncement:
+ debug_browse_data(buf, len);
+ process_host_announce(subrec, p, buf+1);
+ break;
+ case ANN_DomainAnnouncement:
+ debug_browse_data(buf, len);
+ process_workgroup_announce(subrec, p, buf+1);
+ break;
+ case ANN_LocalMasterAnnouncement:
+ debug_browse_data(buf, len);
+ process_local_master_announce(subrec, p, buf+1);
+ break;
+ case ANN_AnnouncementRequest:
+ debug_browse_data(buf, len);
+ process_announce_request(subrec, p, buf+1);
+ break;
+ case ANN_Election:
+ debug_browse_data(buf, len);
+ process_election(subrec, p, buf+1);
+ break;
+ case ANN_GetBackupListReq:
+ debug_browse_data(buf, len);
+ process_get_backup_list_request(subrec, p, buf+1);
+ break;
+ case ANN_GetBackupListResp:
+ debug_browse_data(buf, len);
+ /* We never send ANN_GetBackupListReq so we should never get these. */
+ DEBUG(0,("process_browse_packet: Discarding GetBackupListResponse \
+packet from %s IP %s\n", nmb_namestr(&dgram->source_name), inet_ntoa(p->ip)));
+ break;
+ case ANN_ResetBrowserState:
+ debug_browse_data(buf, len);
+ process_reset_browser(subrec, p, buf+1);
+ break;
+ case ANN_MasterAnnouncement:
+ /* Master browser datagrams must be processed on the unicast subnet. */
+ subrec = unicast_subnet;
+
+ debug_browse_data(buf, len);
+ process_master_browser_announce(subrec, p, buf+1);
+ break;
+ case ANN_BecomeBackup:
+ /*
+ * We don't currently implement this. Log it just in case.
+ */
+ debug_browse_data(buf, len);
+ DEBUG(10,("process_browse_packet: On subnet %s ignoring browse packet \
+command ANN_BecomeBackup from %s IP %s to %s\n", subrec->subnet_name, nmb_namestr(&dgram->source_name),
+ inet_ntoa(p->ip), nmb_namestr(&dgram->dest_name)));
+ break;
+ default:
+ debug_browse_data(buf, len);
+ DEBUG(0,("process_browse_packet: On subnet %s ignoring browse packet \
+command code %d from %s IP %s to %s\n", subrec->subnet_name, command, nmb_namestr(&dgram->source_name),
+ inet_ntoa(p->ip), nmb_namestr(&dgram->dest_name)));
+ break;
+ }
+}
+
+/****************************************************************************
+ Dispatch a LanMan browse frame from port 138 to the correct processing function.
+****************************************************************************/
+
+static void process_lanman_packet(struct packet_struct *p, char *buf,int len)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ int command = SVAL(buf,0);
+ struct subnet_record *subrec = find_subnet_for_dgram_browse_packet(p);
+ char scope[64];
+ unstring src_name;
+
+ /* Drop the packet if it's a different NetBIOS scope, or the source is from one of our names. */
+
+ pull_ascii(scope, dgram->dest_name.scope, 64, 64, STR_TERMINATE);
+ if (!strequal(scope, global_scope())) {
+ DEBUG(7,("process_lanman_packet: Discarding datagram from IP %s. Scope (%s) \
+mismatch with our scope (%s).\n", inet_ntoa(p->ip), scope, global_scope()));
+ return;
+ }
+
+ pull_ascii_nstring(src_name, sizeof(src_name), dgram->source_name.name);
+ if (is_myname(src_name)) {
+ DEBUG(0,("process_lanman_packet: Discarding datagram from IP %s. Source name \
+%s is one of our names !\n", inet_ntoa(p->ip), nmb_namestr(&dgram->source_name)));
+ return;
+ }
+
+ switch (command) {
+ case ANN_HostAnnouncement:
+ debug_browse_data(buf, len);
+ process_lm_host_announce(subrec, p, buf+1);
+ break;
+ case ANN_AnnouncementRequest:
+ process_lm_announce_request(subrec, p, buf+1);
+ break;
+ default:
+ DEBUG(0,("process_lanman_packet: On subnet %s ignoring browse packet \
+command code %d from %s IP %s to %s\n", subrec->subnet_name, command, nmb_namestr(&dgram->source_name),
+ inet_ntoa(p->ip), nmb_namestr(&dgram->dest_name)));
+ break;
+ }
+}
+
+/****************************************************************************
+ Determine if a packet is for us on port 138. Note that to have any chance of
+ being efficient we need to drop as many packets as possible at this
+ stage as subsequent processing is expensive.
+****************************************************************************/
+
+static BOOL listening(struct packet_struct *p,struct nmb_name *nbname)
+{
+ struct subnet_record *subrec = NULL;
+
+ for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ if(same_net(p->ip, subrec->bcast_ip, subrec->mask_ip))
+ break;
+ }
+
+ if(subrec == NULL)
+ subrec = unicast_subnet;
+
+ return (find_name_on_subnet(subrec, nbname, FIND_SELF_NAME) != NULL);
+}
+
+/****************************************************************************
+ Process udp 138 datagrams
+****************************************************************************/
+
+static void process_dgram(struct packet_struct *p)
+{
+ char *buf;
+ char *buf2;
+ int len;
+ struct dgram_packet *dgram = &p->packet.dgram;
+
+ /* If we aren't listening to the destination name then ignore the packet */
+ if (!listening(p,&dgram->dest_name)) {
+ unexpected_packet(p);
+ DEBUG(5,("process_dgram: ignoring dgram packet sent to name %s from %s\n",
+ nmb_namestr(&dgram->dest_name), inet_ntoa(p->ip)));
+ return;
+ }
+
+ if (dgram->header.msg_type != 0x10 && dgram->header.msg_type != 0x11 && dgram->header.msg_type != 0x12) {
+ unexpected_packet(p);
+ /* Don't process error packets etc yet */
+ DEBUG(5,("process_dgram: ignoring dgram packet sent to name %s from IP %s as it is \
+an error packet of type %x\n", nmb_namestr(&dgram->dest_name), inet_ntoa(p->ip), dgram->header.msg_type));
+ return;
+ }
+
+ buf = &dgram->data[0];
+ buf -= 4; /* XXXX for the pseudo tcp length - someday I need to get rid of this */
+
+ if (CVAL(buf,smb_com) != SMBtrans)
+ return;
+
+ len = SVAL(buf,smb_vwv11);
+ buf2 = smb_base(buf) + SVAL(buf,smb_vwv12);
+
+ if (len <= 0)
+ return;
+
+ if (buf2 + len > buf + sizeof(dgram->data)) {
+ DEBUG(2,("process_dgram: datagram from %s to %s IP %s for %s len=%d too long.\n",
+ nmb_namestr(&dgram->source_name),nmb_namestr(&dgram->dest_name),
+ inet_ntoa(p->ip), smb_buf(buf),len));
+ len = (buf + sizeof(dgram->data)) - buf;
+ }
+
+ DEBUG(4,("process_dgram: datagram from %s to %s IP %s for %s of type %d len=%d\n",
+ nmb_namestr(&dgram->source_name),nmb_namestr(&dgram->dest_name),
+ inet_ntoa(p->ip), smb_buf(buf),CVAL(buf2,0),len));
+
+ /* Datagram packet received for the browser mailslot */
+ if (strequal(smb_buf(buf),BROWSE_MAILSLOT)) {
+ process_browse_packet(p,buf2,len);
+ return;
+ }
+
+ /* Datagram packet received for the LAN Manager mailslot */
+ if (strequal(smb_buf(buf),LANMAN_MAILSLOT)) {
+ process_lanman_packet(p,buf2,len);
+ return;
+ }
+
+ /* Datagram packet received for the domain logon mailslot */
+ if (strequal(smb_buf(buf),NET_LOGON_MAILSLOT)) {
+ process_logon_packet(p,buf2,len,NET_LOGON_MAILSLOT);
+ return;
+ }
+
+ /* Datagram packet received for the NT domain logon mailslot */
+ if (strequal(smb_buf(buf),NT_LOGON_MAILSLOT)) {
+ process_logon_packet(p,buf2,len,NT_LOGON_MAILSLOT);
+ return;
+ }
+
+ unexpected_packet(p);
+}
+
+/****************************************************************************
+ Validate a response nmb packet.
+****************************************************************************/
+
+static BOOL validate_nmb_response_packet( struct nmb_packet *nmb )
+{
+ BOOL ignore = False;
+
+ switch (nmb->header.opcode) {
+ case NMB_NAME_REG_OPCODE:
+ case NMB_NAME_REFRESH_OPCODE_8: /* ambiguity in rfc1002 about which is correct. */
+ case NMB_NAME_REFRESH_OPCODE_9: /* WinNT uses 8 by default. */
+ if (nmb->header.ancount == 0) {
+ DEBUG(0,("validate_nmb_response_packet: Bad REG/REFRESH Packet. "));
+ ignore = True;
+ }
+ break;
+
+ case NMB_NAME_QUERY_OPCODE:
+ if ((nmb->header.ancount != 0) && (nmb->header.ancount != 1)) {
+ DEBUG(0,("validate_nmb_response_packet: Bad QUERY Packet. "));
+ ignore = True;
+ }
+ break;
+
+ case NMB_NAME_RELEASE_OPCODE:
+ if (nmb->header.ancount == 0) {
+ DEBUG(0,("validate_nmb_response_packet: Bad RELEASE Packet. "));
+ ignore = True;
+ }
+ break;
+
+ case NMB_WACK_OPCODE:
+ /* Check WACK response here. */
+ if (nmb->header.ancount != 1) {
+ DEBUG(0,("validate_nmb_response_packet: Bad WACK Packet. "));
+ ignore = True;
+ }
+ break;
+ default:
+ DEBUG(0,("validate_nmb_response_packet: Ignoring packet with unknown opcode %d.\n",
+ nmb->header.opcode));
+ return True;
+ }
+
+ if(ignore)
+ DEBUG(0,("Ignoring response packet with opcode %d.\n", nmb->header.opcode));
+
+ return ignore;
+}
+
+/****************************************************************************
+ Validate a request nmb packet.
+****************************************************************************/
+
+static BOOL validate_nmb_packet( struct nmb_packet *nmb )
+{
+ BOOL ignore = False;
+
+ switch (nmb->header.opcode) {
+ case NMB_NAME_REG_OPCODE:
+ case NMB_NAME_REFRESH_OPCODE_8: /* ambiguity in rfc1002 about which is correct. */
+ case NMB_NAME_REFRESH_OPCODE_9: /* WinNT uses 8 by default. */
+ case NMB_NAME_MULTIHOMED_REG_OPCODE:
+ if (nmb->header.qdcount==0 || nmb->header.arcount==0) {
+ DEBUG(0,("validate_nmb_packet: Bad REG/REFRESH Packet. "));
+ ignore = True;
+ }
+ break;
+
+ case NMB_NAME_QUERY_OPCODE:
+ if ((nmb->header.qdcount == 0) || ((nmb->question.question_type != QUESTION_TYPE_NB_QUERY) &&
+ (nmb->question.question_type != QUESTION_TYPE_NB_STATUS))) {
+ DEBUG(0,("validate_nmb_packet: Bad QUERY Packet. "));
+ ignore = True;
+ }
+ break;
+
+ case NMB_NAME_RELEASE_OPCODE:
+ if (nmb->header.qdcount==0 || nmb->header.arcount==0) {
+ DEBUG(0,("validate_nmb_packet: Bad RELEASE Packet. "));
+ ignore = True;
+ }
+ break;
+ default:
+ DEBUG(0,("validate_nmb_packet: Ignoring packet with unknown opcode %d.\n",
+ nmb->header.opcode));
+ return True;
+ }
+
+ if(ignore)
+ DEBUG(0,("validate_nmb_packet: Ignoring request packet with opcode %d.\n", nmb->header.opcode));
+
+ return ignore;
+}
+
+/****************************************************************************
+ Find a subnet (and potentially a response record) for a packet.
+****************************************************************************/
+
+static struct subnet_record *find_subnet_for_nmb_packet( struct packet_struct *p,
+ struct response_record **pprrec)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct response_record *rrec = NULL;
+ struct subnet_record *subrec = NULL;
+
+ if(pprrec != NULL)
+ *pprrec = NULL;
+
+ if(nmb->header.response) {
+ /* It's a response packet. Find a record for it or it's an error. */
+
+ rrec = find_response_record( &subrec, nmb->header.name_trn_id);
+ if(rrec == NULL) {
+ DEBUG(3,("find_subnet_for_nmb_packet: response record not found for response id %hu\n",
+ nmb->header.name_trn_id));
+ unexpected_packet(p);
+ return NULL;
+ }
+
+ if(subrec == NULL) {
+ DEBUG(0,("find_subnet_for_nmb_packet: subnet record not found for response id %hu\n",
+ nmb->header.name_trn_id));
+ return NULL;
+ }
+
+ if(pprrec != NULL)
+ *pprrec = rrec;
+ return subrec;
+ }
+
+ /* Try and see what subnet this packet belongs to. */
+
+ /* WINS server ? */
+ if(packet_is_for_wins_server(p))
+ return wins_server_subnet;
+
+ /* If it wasn't a broadcast packet then send to the UNICAST subnet. */
+ if(nmb->header.nm_flags.bcast == False)
+ return unicast_subnet;
+
+ /* Go through all the broadcast subnets and see if the mask matches. */
+ for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ if(same_net(p->ip, subrec->bcast_ip, subrec->mask_ip))
+ return subrec;
+ }
+
+ /* If none match it must have been a directed broadcast - assign the remote_broadcast_subnet. */
+ return remote_broadcast_subnet;
+}
+
+/****************************************************************************
+ Process a nmb request packet - validate the packet and route it.
+****************************************************************************/
+
+static void process_nmb_request(struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct subnet_record *subrec = NULL;
+
+ debug_nmb_packet(p);
+
+ /* Ensure we have a good packet. */
+ if(validate_nmb_packet(nmb))
+ return;
+
+ /* Allocate a subnet to this packet - if we cannot - fail. */
+ if((subrec = find_subnet_for_nmb_packet(p, NULL))==NULL)
+ return;
+
+ switch (nmb->header.opcode) {
+ case NMB_NAME_REG_OPCODE:
+ if(subrec == wins_server_subnet)
+ wins_process_name_registration_request(subrec, p);
+ else
+ process_name_registration_request(subrec, p);
+ break;
+
+ case NMB_NAME_REFRESH_OPCODE_8: /* ambiguity in rfc1002 about which is correct. */
+ case NMB_NAME_REFRESH_OPCODE_9:
+ if(subrec == wins_server_subnet)
+ wins_process_name_refresh_request(subrec, p);
+ else
+ process_name_refresh_request(subrec, p);
+ break;
+
+ case NMB_NAME_MULTIHOMED_REG_OPCODE:
+ if(subrec == wins_server_subnet) {
+ wins_process_multihomed_name_registration_request(subrec, p);
+ } else {
+ DEBUG(0,("process_nmb_request: Multihomed registration request must be \
+directed at a WINS server.\n"));
+ }
+ break;
+
+ case NMB_NAME_QUERY_OPCODE:
+ switch (nmb->question.question_type) {
+ case QUESTION_TYPE_NB_QUERY:
+ if(subrec == wins_server_subnet)
+ wins_process_name_query_request(subrec, p);
+ else
+ process_name_query_request(subrec, p);
+ break;
+ case QUESTION_TYPE_NB_STATUS:
+ if(subrec == wins_server_subnet) {
+ DEBUG(0,("process_nmb_request: NB_STATUS request directed at WINS server is \
+not allowed.\n"));
+ break;
+ } else {
+ process_node_status_request(subrec, p);
+ }
+ break;
+ }
+ break;
+
+ case NMB_NAME_RELEASE_OPCODE:
+ if(subrec == wins_server_subnet)
+ wins_process_name_release_request(subrec, p);
+ else
+ process_name_release_request(subrec, p);
+ break;
+ }
+}
+
+/****************************************************************************
+ Process a nmb response packet - validate the packet and route it.
+ to either the WINS server or a normal response.
+****************************************************************************/
+
+static void process_nmb_response(struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct subnet_record *subrec = NULL;
+ struct response_record *rrec = NULL;
+
+ debug_nmb_packet(p);
+
+ if(validate_nmb_response_packet(nmb))
+ return;
+
+ if((subrec = find_subnet_for_nmb_packet(p, &rrec))==NULL)
+ return;
+
+ if(rrec == NULL) {
+ DEBUG(0,("process_nmb_response: response packet received but no response record \
+found for id = %hu. Ignoring packet.\n", nmb->header.name_trn_id));
+ return;
+ }
+
+ /* Increment the number of responses received for this record. */
+ rrec->num_msgs++;
+ /* Ensure we don't re-send the request. */
+ rrec->repeat_count = 0;
+
+ /* Call the response received function for this packet. */
+ (*rrec->resp_fn)(subrec, rrec, p);
+}
+
+/*******************************************************************
+ Run elements off the packet queue till its empty
+******************************************************************/
+
+void run_packet_queue(void)
+{
+ struct packet_struct *p;
+
+ while ((p = packet_queue)) {
+ packet_queue = p->next;
+ if (packet_queue)
+ packet_queue->prev = NULL;
+ p->next = p->prev = NULL;
+
+ switch (p->packet_type) {
+ case NMB_PACKET:
+ if(p->packet.nmb.header.response)
+ process_nmb_response(p);
+ else
+ process_nmb_request(p);
+ break;
+
+ case DGRAM_PACKET:
+ process_dgram(p);
+ break;
+ }
+ free_packet(p);
+ }
+}
+
+/*******************************************************************
+ Retransmit or timeout elements from all the outgoing subnet response
+ record queues. NOTE that this code must also check the WINS server
+ subnet for response records to timeout as the WINS server code
+ can send requests to check if a client still owns a name.
+ (Patch from Andrey Alekseyev <fetch@muffin.arcadia.spb.ru>).
+******************************************************************/
+
+void retransmit_or_expire_response_records(time_t t)
+{
+ struct subnet_record *subrec;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = get_next_subnet_maybe_unicast_or_wins_server(subrec)) {
+ struct response_record *rrec, *nextrrec;
+
+ for (rrec = subrec->responselist; rrec; rrec = nextrrec) {
+ nextrrec = rrec->next;
+
+ if (rrec->repeat_time <= t) {
+ if (rrec->repeat_count > 0) {
+ /* Resend while we have a non-zero repeat_count. */
+ if(!send_packet(rrec->packet)) {
+ DEBUG(0,("retransmit_or_expire_response_records: Failed to resend packet id %hu \
+to IP %s on subnet %s\n", rrec->response_id, inet_ntoa(rrec->packet->ip), subrec->subnet_name));
+ }
+ rrec->repeat_time = t + rrec->repeat_interval;
+ rrec->repeat_count--;
+ } else {
+ DEBUG(4,("retransmit_or_expire_response_records: timeout for packet id %hu to IP %s \
+on subnet %s\n", rrec->response_id, inet_ntoa(rrec->packet->ip), subrec->subnet_name));
+
+ /*
+ * Check the flag in this record to prevent recursion if we end
+ * up in this function again via the timeout function call.
+ */
+
+ if(!rrec->in_expiration_processing) {
+
+ /*
+ * Set the recursion protection flag in this record.
+ */
+
+ rrec->in_expiration_processing = True;
+
+ /* Call the timeout function. This will deal with removing the
+ timed out packet. */
+ if(rrec->timeout_fn) {
+ (*rrec->timeout_fn)(subrec, rrec);
+ } else {
+ /* We must remove the record ourself if there is
+ no timeout function. */
+ remove_response_record(subrec, rrec);
+ }
+ } /* !rrec->in_expitation_processing */
+ } /* rrec->repeat_count > 0 */
+ } /* rrec->repeat_time <= t */
+ } /* end for rrec */
+ } /* end for subnet */
+}
+
+/****************************************************************************
+ Create an fd_set containing all the sockets in the subnet structures,
+ plus the broadcast sockets.
+***************************************************************************/
+
+static BOOL create_listen_fdset(fd_set **ppset, int **psock_array, int *listen_number)
+{
+ int *sock_array = NULL;
+ struct subnet_record *subrec = NULL;
+ int count = 0;
+ int num = 0;
+ fd_set *pset = (fd_set *)malloc(sizeof(fd_set));
+
+ if(pset == NULL) {
+ DEBUG(0,("create_listen_fdset: malloc fail !\n"));
+ return True;
+ }
+
+ /* Check that we can add all the fd's we need. */
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+ count++;
+
+ if((count*2) + 2 > FD_SETSIZE) {
+ DEBUG(0,("create_listen_fdset: Too many file descriptors needed (%d). We can \
+only use %d.\n", (count*2) + 2, FD_SETSIZE));
+ return True;
+ }
+
+ if((sock_array = (int *)malloc(((count*2) + 2)*sizeof(int))) == NULL) {
+ DEBUG(0,("create_listen_fdset: malloc fail for socket array.\n"));
+ return True;
+ }
+
+ FD_ZERO(pset);
+
+ /* Add in the broadcast socket on 137. */
+ FD_SET(ClientNMB,pset);
+ sock_array[num++] = ClientNMB;
+
+ /* Add in the 137 sockets on all the interfaces. */
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ FD_SET(subrec->nmb_sock,pset);
+ sock_array[num++] = subrec->nmb_sock;
+ }
+
+ /* Add in the broadcast socket on 138. */
+ FD_SET(ClientDGRAM,pset);
+ sock_array[num++] = ClientDGRAM;
+
+ /* Add in the 138 sockets on all the interfaces. */
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ FD_SET(subrec->dgram_sock,pset);
+ sock_array[num++] = subrec->dgram_sock;
+ }
+
+ *listen_number = (count*2) + 2;
+
+ SAFE_FREE(*ppset);
+ SAFE_FREE(*psock_array);
+
+ *ppset = pset;
+ *psock_array = sock_array;
+
+ return False;
+}
+
+/****************************************************************************
+ Listens for NMB or DGRAM packets, and queues them.
+ return True if the socket is dead
+***************************************************************************/
+
+BOOL listen_for_packets(BOOL run_election)
+{
+ static fd_set *listen_set = NULL;
+ static int listen_number = 0;
+ static int *sock_array = NULL;
+ int i;
+
+ fd_set fds;
+ int selrtn;
+ struct timeval timeout;
+#ifndef SYNC_DNS
+ int dns_fd;
+#endif
+
+ if(listen_set == NULL || rescan_listen_set) {
+ if(create_listen_fdset(&listen_set, &sock_array, &listen_number)) {
+ DEBUG(0,("listen_for_packets: Fatal error. unable to create listen set. Exiting.\n"));
+ return True;
+ }
+ rescan_listen_set = False;
+ }
+
+ memcpy((char *)&fds, (char *)listen_set, sizeof(fd_set));
+
+#ifndef SYNC_DNS
+ dns_fd = asyncdns_fd();
+ if (dns_fd != -1) {
+ FD_SET(dns_fd, &fds);
+ }
+#endif
+
+ /*
+ * During elections and when expecting a netbios response packet we
+ * need to send election packets at tighter intervals.
+ * Ideally it needs to be the interval (in ms) between time now and
+ * the time we are expecting the next netbios packet.
+ */
+
+ timeout.tv_sec = (run_election||num_response_packets) ? 1 : NMBD_SELECT_LOOP;
+ timeout.tv_usec = 0;
+
+ /* Prepare for the select - allow certain signals. */
+
+ BlockSignals(False, SIGTERM);
+
+ selrtn = sys_select(FD_SETSIZE,&fds,NULL,NULL,&timeout);
+
+ /* We can only take signals when we are in the select - block them again here. */
+
+ BlockSignals(True, SIGTERM);
+
+ if(selrtn == -1) {
+ return False;
+ }
+
+#ifndef SYNC_DNS
+ if (dns_fd != -1 && FD_ISSET(dns_fd,&fds)) {
+ run_dns_queue();
+ }
+#endif
+
+ for(i = 0; i < listen_number; i++) {
+ if (i < (listen_number/2)) {
+ /* Processing a 137 socket. */
+ if (FD_ISSET(sock_array[i],&fds)) {
+ struct packet_struct *packet = read_packet(sock_array[i], NMB_PACKET);
+ if (packet) {
+ /*
+ * If we got a packet on the broadcast socket and interfaces
+ * only is set then check it came from one of our local nets.
+ */
+ if(lp_bind_interfaces_only() && (sock_array[i] == ClientNMB) &&
+ (!is_local_net(packet->ip))) {
+ DEBUG(7,("discarding nmb packet sent to broadcast socket from %s:%d\n",
+ inet_ntoa(packet->ip),packet->port));
+ free_packet(packet);
+ } else if ((ip_equal(loopback_ip, packet->ip) ||
+ ismyip(packet->ip)) && packet->port == global_nmb_port &&
+ packet->packet.nmb.header.nm_flags.bcast) {
+ DEBUG(7,("discarding own bcast packet from %s:%d\n",
+ inet_ntoa(packet->ip),packet->port));
+ free_packet(packet);
+ } else {
+ /* Save the file descriptor this packet came in on. */
+ packet->fd = sock_array[i];
+ queue_packet(packet);
+ }
+ }
+ }
+ } else {
+ /* Processing a 138 socket. */
+ if (FD_ISSET(sock_array[i],&fds)) {
+ struct packet_struct *packet = read_packet(sock_array[i], DGRAM_PACKET);
+ if (packet) {
+ /*
+ * If we got a packet on the broadcast socket and interfaces
+ * only is set then check it came from one of our local nets.
+ */
+ if(lp_bind_interfaces_only() && (sock_array[i] == ClientDGRAM) &&
+ (!is_local_net(packet->ip))) {
+ DEBUG(7,("discarding dgram packet sent to broadcast socket from %s:%d\n",
+ inet_ntoa(packet->ip),packet->port));
+ free_packet(packet);
+ } else if ((ip_equal(loopback_ip, packet->ip) ||
+ ismyip(packet->ip)) && packet->port == DGRAM_PORT) {
+ DEBUG(7,("discarding own dgram packet from %s:%d\n",
+ inet_ntoa(packet->ip),packet->port));
+ free_packet(packet);
+ } else {
+ /* Save the file descriptor this packet came in on. */
+ packet->fd = sock_array[i];
+ queue_packet(packet);
+ }
+ }
+ }
+ } /* end processing 138 socket. */
+ } /* end for */
+ return False;
+}
+
+/****************************************************************************
+ Construct and send a netbios DGRAM.
+**************************************************************************/
+
+BOOL send_mailslot(BOOL unique, const char *mailslot,char *buf, size_t len,
+ const char *srcname, int src_type,
+ const char *dstname, int dest_type,
+ struct in_addr dest_ip,struct in_addr src_ip,
+ int dest_port)
+{
+ BOOL loopback_this_packet = False;
+ struct packet_struct p;
+ struct dgram_packet *dgram = &p.packet.dgram;
+ char *ptr,*p2;
+ char tmp[4];
+
+ memset((char *)&p,'\0',sizeof(p));
+
+ if(ismyip(dest_ip) && (dest_port == DGRAM_PORT)) /* Only if to DGRAM_PORT */
+ loopback_this_packet = True;
+
+ /* generate_name_trn_id(); */ /* Not used, so gone, RJS */
+
+ /* DIRECT GROUP or UNIQUE datagram. */
+ dgram->header.msg_type = unique ? 0x10 : 0x11;
+ dgram->header.flags.node_type = M_NODE;
+ dgram->header.flags.first = True;
+ dgram->header.flags.more = False;
+ dgram->header.dgm_id = generate_name_trn_id();
+ dgram->header.source_ip = src_ip;
+ dgram->header.source_port = DGRAM_PORT;
+ dgram->header.dgm_length = 0; /* Let build_dgram() handle this. */
+ dgram->header.packet_offset = 0;
+
+ make_nmb_name(&dgram->source_name,srcname,src_type);
+ make_nmb_name(&dgram->dest_name,dstname,dest_type);
+
+ ptr = &dgram->data[0];
+
+ /* Setup the smb part. */
+ ptr -= 4; /* XXX Ugliness because of handling of tcp SMB length. */
+ memcpy(tmp,ptr,4);
+ set_message(ptr,17,strlen(mailslot) + 1 + len,True);
+ memcpy(ptr,tmp,4);
+
+ SCVAL(ptr,smb_com,SMBtrans);
+ SSVAL(ptr,smb_vwv1,len);
+ SSVAL(ptr,smb_vwv11,len);
+ SSVAL(ptr,smb_vwv12,70 + strlen(mailslot));
+ SSVAL(ptr,smb_vwv13,3);
+ SSVAL(ptr,smb_vwv14,1);
+ SSVAL(ptr,smb_vwv15,1);
+ SSVAL(ptr,smb_vwv16,2);
+ p2 = smb_buf(ptr);
+ safe_strcpy_base(p2, mailslot, dgram->data, sizeof(dgram->data));
+ p2 = skip_string(p2,1);
+
+ if (((p2+len) > dgram->data+sizeof(dgram->data)) || ((p2+len) < p2)) {
+ DEBUG(0, ("send_mailslot: Cannot write beyond end of packet\n"));
+ return False;
+ } else {
+ memcpy(p2,buf,len);
+ p2 += len;
+ }
+
+ dgram->datasize = PTR_DIFF(p2,ptr+4); /* +4 for tcp length. */
+
+ p.ip = dest_ip;
+ p.port = dest_port;
+ p.fd = find_subnet_mailslot_fd_for_address( src_ip );
+ p.timestamp = time(NULL);
+ p.packet_type = DGRAM_PACKET;
+
+ DEBUG(4,("send_mailslot: Sending to mailslot %s from %s IP %s ", mailslot,
+ nmb_namestr(&dgram->source_name), inet_ntoa(src_ip)));
+ DEBUG(4,("to %s IP %s\n", nmb_namestr(&dgram->dest_name), inet_ntoa(dest_ip)));
+
+ debug_browse_data(buf, len);
+
+ if(loopback_this_packet) {
+ struct packet_struct *lo_packet = NULL;
+ DEBUG(5,("send_mailslot: sending packet to ourselves.\n"));
+ if((lo_packet = copy_packet(&p)) == NULL)
+ return False;
+ queue_packet(lo_packet);
+ return True;
+ } else {
+ return(send_packet(&p));
+ }
+}
diff --git a/source/nmbd/nmbd_processlogon.c b/source/nmbd/nmbd_processlogon.c
new file mode 100644
index 00000000000..da93224043c
--- /dev/null
+++ b/source/nmbd/nmbd_processlogon.c
@@ -0,0 +1,522 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-2003
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Revision History:
+
+*/
+
+#include "includes.h"
+
+struct sam_database_info {
+ uint32 index;
+ uint32 serial_lo, serial_hi;
+ uint32 date_lo, date_hi;
+};
+
+/****************************************************************************
+Send a message to smbd to do a sam delta sync
+**************************************************************************/
+
+static void send_repl_message(uint32 low_serial)
+{
+ TDB_CONTEXT *tdb;
+
+ tdb = tdb_open_log(lock_path("connections.tdb"), 0,
+ TDB_DEFAULT, O_RDONLY, 0);
+
+ if (!tdb) {
+ DEBUG(3, ("send_repl_message(): failed to open connections "
+ "database\n"));
+ return;
+ }
+
+ DEBUG(3, ("sending replication message, serial = 0x%04x\n",
+ low_serial));
+
+ message_send_all(tdb, MSG_SMB_SAM_REPL, &low_serial,
+ sizeof(low_serial), False, NULL);
+
+ tdb_close(tdb);
+}
+
+/****************************************************************************
+Process a domain logon packet
+**************************************************************************/
+
+void process_logon_packet(struct packet_struct *p, char *buf,int len,
+ const char *mailslot)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ pstring my_name;
+ fstring reply_name;
+ pstring outbuf;
+ int code;
+ uint16 token = 0;
+ uint32 ntversion = 0;
+ uint16 lmnttoken = 0;
+ uint16 lm20token = 0;
+ uint32 domainsidsize;
+ BOOL short_request = False;
+ char *getdc;
+ char *uniuser; /* Unicode user name. */
+ pstring ascuser;
+ char *unicomp; /* Unicode computer name. */
+
+ memset(outbuf, 0, sizeof(outbuf));
+
+ if (!lp_domain_logons()) {
+ DEBUG(5,("process_logon_packet: Logon packet received from IP %s and domain \
+logons are not enabled.\n", inet_ntoa(p->ip) ));
+ return;
+ }
+
+ pstrcpy(my_name, global_myname());
+
+ code = SVAL(buf,0);
+ DEBUG(4,("process_logon_packet: Logon from %s: code = 0x%x\n", inet_ntoa(p->ip), code));
+
+ switch (code) {
+ case 0:
+ {
+ fstring mach_str, user_str, getdc_str;
+ char *q = buf + 2;
+ char *machine = q;
+ char *user = skip_string(machine,1);
+
+ getdc = skip_string(user,1);
+ q = skip_string(getdc,1);
+ token = SVAL(q,3);
+
+ fstrcpy(reply_name,my_name);
+
+ pull_ascii_fstring(mach_str, machine);
+ pull_ascii_fstring(user_str, user);
+ pull_ascii_fstring(getdc_str, getdc);
+
+ DEBUG(5,("process_logon_packet: Domain login request from %s at IP %s user=%s token=%x\n",
+ mach_str,inet_ntoa(p->ip),user_str,token));
+
+ q = outbuf;
+ SSVAL(q, 0, 6);
+ q += 2;
+
+ fstrcpy(reply_name, "\\\\");
+ fstrcat(reply_name, my_name);
+ push_ascii_fstring(q, reply_name);
+ q = skip_string(q, 1); /* PDC name */
+
+ SSVAL(q, 0, token);
+ q += 2;
+
+ dump_data(4, outbuf, PTR_DIFF(q, outbuf));
+
+ send_mailslot(True, getdc_str,
+ outbuf,PTR_DIFF(q,outbuf),
+ global_myname(), 0x0,
+ mach_str,
+ dgram->source_name.name_type,
+ p->ip, *iface_ip(p->ip), p->port);
+ break;
+ }
+
+ case QUERYFORPDC:
+ {
+ fstring mach_str, getdc_str;
+ fstring source_name;
+ char *q = buf + 2;
+ char *machine = q;
+
+ if (!lp_domain_master()) {
+ /* We're not Primary Domain Controller -- ignore this */
+ return;
+ }
+
+ getdc = skip_string(machine,1);
+ q = skip_string(getdc,1);
+ q = ALIGN2(q, buf);
+
+ /* At this point we can work out if this is a W9X or NT style
+ request. Experiments show that the difference is wether the
+ packet ends here. For a W9X request we now end with a pair of
+ bytes (usually 0xFE 0xFF) whereas with NT we have two further
+ strings - the following is a simple way of detecting this */
+
+ if (len - PTR_DIFF(q, buf) <= 3) {
+ short_request = True;
+ } else {
+ unicomp = q;
+
+ /* A full length (NT style) request */
+ q = skip_unibuf(unicomp, PTR_DIFF(buf + len, unicomp));
+
+ if (len - PTR_DIFF(q, buf) > 8) {
+ /* with NT5 clients we can sometimes
+ get additional data - a length specificed string
+ containing the domain name, then 16 bytes of
+ data (no idea what it is) */
+ int dom_len = CVAL(q, 0);
+ q++;
+ if (dom_len != 0) {
+ q += dom_len + 1;
+ }
+ q += 16;
+ }
+ ntversion = IVAL(q, 0);
+ lmnttoken = SVAL(q, 4);
+ lm20token = SVAL(q, 6);
+ }
+
+ /* Construct reply. */
+ q = outbuf;
+ SSVAL(q, 0, QUERYFORPDC_R);
+ q += 2;
+
+ fstrcpy(reply_name,my_name);
+ push_ascii_fstring(q, reply_name);
+ q = skip_string(q, 1); /* PDC name */
+
+ /* PDC and domain name */
+ if (!short_request) {
+ /* Make a full reply */
+ q = ALIGN2(q, outbuf);
+
+ q += dos_PutUniCode(q, my_name, sizeof(pstring), True); /* PDC name */
+ q += dos_PutUniCode(q, lp_workgroup(),sizeof(pstring), True); /* Domain name*/
+ SIVAL(q, 0, 1); /* our nt version */
+ SSVAL(q, 4, 0xffff); /* our lmnttoken */
+ SSVAL(q, 6, 0xffff); /* our lm20token */
+ q += 8;
+ }
+
+ /* RJS, 21-Feb-2000, we send a short reply if the request was short */
+
+ pull_ascii_fstring(mach_str, machine);
+
+ DEBUG(5,("process_logon_packet: GETDC request from %s at IP %s, \
+reporting %s domain %s 0x%x ntversion=%x lm_nt token=%x lm_20 token=%x\n",
+ mach_str,inet_ntoa(p->ip), reply_name, lp_workgroup(),
+ QUERYFORPDC_R, (uint32)ntversion, (uint32)lmnttoken,
+ (uint32)lm20token ));
+
+ dump_data(4, outbuf, PTR_DIFF(q, outbuf));
+
+ pull_ascii_fstring(getdc_str, getdc);
+ pull_ascii_nstring(source_name, sizeof(source_name), dgram->source_name.name);
+
+ send_mailslot(True, getdc_str,
+ outbuf,PTR_DIFF(q,outbuf),
+ global_myname(), 0x0,
+ source_name,
+ dgram->source_name.name_type,
+ p->ip, *iface_ip(p->ip), p->port);
+ return;
+ }
+
+ case SAMLOGON:
+
+ {
+ fstring getdc_str;
+ fstring source_name;
+ char *q = buf + 2;
+ fstring asccomp;
+
+ q += 2;
+ unicomp = q;
+ uniuser = skip_unibuf(unicomp, PTR_DIFF(buf+len, unicomp));
+ getdc = skip_unibuf(uniuser,PTR_DIFF(buf+len, uniuser));
+ q = skip_string(getdc,1);
+ q += 4; /* Account Control Bits - indicating username type */
+ domainsidsize = IVAL(q, 0);
+ q += 4;
+
+ DEBUG(5,("process_logon_packet: SAMLOGON sidsize %d, len = %d\n", domainsidsize, len));
+
+ if (domainsidsize < (len - PTR_DIFF(q, buf)) && (domainsidsize != 0)) {
+ q += domainsidsize;
+ q = ALIGN4(q, buf);
+ }
+
+ DEBUG(5,("process_logon_packet: len = %d PTR_DIFF(q, buf) = %ld\n", len, (unsigned long)PTR_DIFF(q, buf) ));
+
+ if (len - PTR_DIFF(q, buf) > 8) {
+ /* with NT5 clients we can sometimes
+ get additional data - a length specificed string
+ containing the domain name, then 16 bytes of
+ data (no idea what it is) */
+ int dom_len = CVAL(q, 0);
+ q++;
+ if (dom_len < (len - PTR_DIFF(q, buf)) && (dom_len != 0)) {
+ q += dom_len + 1;
+ }
+ q += 16;
+ }
+
+ ntversion = IVAL(q, 0);
+ lmnttoken = SVAL(q, 4);
+ lm20token = SVAL(q, 6);
+ q += 8;
+
+ DEBUG(3,("process_logon_packet: SAMLOGON sidsize %d ntv %d\n", domainsidsize, ntversion));
+
+ /*
+ * we respond regadless of whether the machine is in our password
+ * database. If it isn't then we let smbd send an appropriate error.
+ * Let's ignore the SID.
+ */
+ pull_ucs2_pstring(ascuser, uniuser);
+ pull_ucs2_fstring(asccomp, unicomp);
+ DEBUG(5,("process_logon_packet: SAMLOGON user %s\n", ascuser));
+
+ fstrcpy(reply_name, "\\\\"); /* Here it wants \\LOGONSERVER. */
+ fstrcat(reply_name, my_name);
+
+ DEBUG(5,("process_logon_packet: SAMLOGON request from %s(%s) for %s, returning logon svr %s domain %s code %x token=%x\n",
+ asccomp,inet_ntoa(p->ip), ascuser, reply_name, lp_workgroup(),
+ SAMLOGON_R ,lmnttoken));
+
+ /* Construct reply. */
+
+ q = outbuf;
+ /* we want the simple version unless we are an ADS PDC..which means */
+ /* never, at least for now */
+ if ((ntversion < 11) || (SEC_ADS != lp_security()) || (ROLE_DOMAIN_PDC != lp_server_role())) {
+ if (SVAL(uniuser, 0) == 0) {
+ SSVAL(q, 0, SAMLOGON_UNK_R); /* user unknown */
+ } else {
+ SSVAL(q, 0, SAMLOGON_R);
+ }
+
+ q += 2;
+
+ q += dos_PutUniCode(q, reply_name,sizeof(pstring), True);
+ q += dos_PutUniCode(q, ascuser, sizeof(pstring), True);
+ q += dos_PutUniCode(q, lp_workgroup(),sizeof(pstring), True);
+ }
+#ifdef HAVE_ADS
+ else {
+ struct uuid domain_guid;
+ UUID_FLAT flat_guid;
+ pstring domain;
+ pstring hostname;
+ char *component, *dc, *q1;
+ uint8 size;
+ char *q_orig = q;
+ int str_offset;
+
+ get_mydnsdomname(domain);
+ get_myname(hostname);
+
+ if (SVAL(uniuser, 0) == 0) {
+ SIVAL(q, 0, SAMLOGON_AD_UNK_R); /* user unknown */
+ } else {
+ SIVAL(q, 0, SAMLOGON_AD_R);
+ }
+ q += 4;
+
+ SIVAL(q, 0, ADS_PDC|ADS_GC|ADS_LDAP|ADS_DS|
+ ADS_KDC|ADS_TIMESERV|ADS_CLOSEST|ADS_WRITABLE);
+ q += 4;
+
+ /* Push Domain GUID */
+ if (False == secrets_fetch_domain_guid(domain, &domain_guid)) {
+ DEBUG(2, ("Could not fetch DomainGUID for %s\n", domain));
+ return;
+ }
+
+ smb_uuid_pack(domain_guid, &flat_guid);
+ memcpy(q, &flat_guid.info, UUID_FLAT_SIZE);
+ q += UUID_FLAT_SIZE;
+
+ /* Forest */
+ str_offset = q - q_orig;
+ dc = domain;
+ q1 = q;
+ while ((component = strtok(dc, "."))) {
+ dc = NULL;
+ size = push_ascii(&q[1], component, -1, 0);
+ SCVAL(q, 0, size);
+ q += (size + 1);
+ }
+
+ /* Unk0 */
+ SCVAL(q, 0, 0);
+ q++;
+
+ /* Domain */
+ SCVAL(q, 0, 0xc0 | ((str_offset >> 8) & 0x3F));
+ SCVAL(q, 1, str_offset & 0xFF);
+ q += 2;
+
+ /* Hostname */
+ size = push_ascii(&q[1], hostname, -1, 0);
+ SCVAL(q, 0, size);
+ q += (size + 1);
+ SCVAL(q, 0, 0xc0 | ((str_offset >> 8) & 0x3F));
+ SCVAL(q, 1, str_offset & 0xFF);
+ q += 2;
+
+ /* NETBIOS of domain */
+ size = push_ascii(&q[1], lp_workgroup(), -1, STR_UPPER);
+ SCVAL(q, 0, size);
+ q += (size + 1);
+
+ /* Unk1 */
+ SCVAL(q, 0, 0);
+ q++;
+
+ /* NETBIOS of hostname */
+ size = push_ascii(&q[1], my_name, -1, 0);
+ SCVAL(q, 0, size);
+ q += (size + 1);
+
+ /* Unk2 */
+ SCVAL(q, 0, 0);
+ q++;
+
+ /* User name */
+ if (SVAL(uniuser, 0) != 0) {
+ size = push_ascii(&q[1], ascuser, -1, 0);
+ SCVAL(q, 0, size);
+ q += (size + 1);
+ }
+
+ q_orig = q;
+ /* Site name */
+ size = push_ascii(&q[1], "Default-First-Site-Name", -1, 0);
+ SCVAL(q, 0, size);
+ q += (size + 1);
+
+ /* Site name (2) */
+ str_offset = q - q_orig;
+ SCVAL(q, 0, 0xc0 | ((str_offset >> 8) & 0x3F));
+ SCVAL(q, 1, str_offset & 0xFF);
+ q += 2;
+
+ SCVAL(q, 0, PTR_DIFF(q,q1));
+ SCVAL(q, 1, 0x10); /* unknown */
+
+ SIVAL(q, 0, 0x00000002);
+ q += 4; /* unknown */
+ SIVAL(q, 0, (iface_ip(p->ip))->s_addr);
+ q += 4;
+ SIVAL(q, 0, 0x00000000);
+ q += 4; /* unknown */
+ SIVAL(q, 0, 0x00000000);
+ q += 4; /* unknown */
+ }
+#endif
+
+ /* tell the client what version we are */
+ SIVAL(q, 0, ((ntversion < 11) || (SEC_ADS != lp_security())) ? 1 : 13);
+ /* our ntversion */
+ SSVAL(q, 4, 0xffff); /* our lmnttoken */
+ SSVAL(q, 6, 0xffff); /* our lm20token */
+ q += 8;
+
+ dump_data(4, outbuf, PTR_DIFF(q, outbuf));
+
+ pull_ascii_fstring(getdc_str, getdc);
+ pull_ascii_nstring(source_name, sizeof(source_name), dgram->source_name.name);
+
+ send_mailslot(True, getdc,
+ outbuf,PTR_DIFF(q,outbuf),
+ global_myname(), 0x0,
+ source_name,
+ dgram->source_name.name_type,
+ p->ip, *iface_ip(p->ip), p->port);
+ break;
+ }
+
+ /* Announce change to UAS or SAM. Send by the domain controller when a
+ replication event is required. */
+
+ case SAM_UAS_CHANGE:
+ {
+ struct sam_database_info *db_info;
+ char *q = buf + 2;
+ int i, db_count;
+ uint32 low_serial;
+
+ /* Header */
+
+ low_serial = IVAL(q, 0); q += 4; /* Low serial number */
+
+ q += 4; /* Date/time */
+ q += 4; /* Pulse */
+ q += 4; /* Random */
+
+ /* Domain info */
+
+ q = skip_string(q, 1); /* PDC name */
+ q = skip_string(q, 1); /* Domain name */
+ q = skip_unibuf(q, PTR_DIFF(buf + len, q)); /* Unicode PDC name */
+ q = skip_unibuf(q, PTR_DIFF(buf + len, q)); /* Unicode domain name */
+
+ /* Database info */
+
+ db_count = SVAL(q, 0); q += 2;
+
+ db_info = (struct sam_database_info *)
+ malloc(sizeof(struct sam_database_info) * db_count);
+
+ if (db_info == NULL) {
+ DEBUG(3, ("out of memory allocating info for %d databases\n", db_count));
+ return;
+ }
+
+ for (i = 0; i < db_count; i++) {
+ db_info[i].index = IVAL(q, 0);
+ db_info[i].serial_lo = IVAL(q, 4);
+ db_info[i].serial_hi = IVAL(q, 8);
+ db_info[i].date_lo = IVAL(q, 12);
+ db_info[i].date_hi = IVAL(q, 16);
+ q += 20;
+ }
+
+ /* Domain SID */
+
+#if 0
+ /* We must range check this. */
+ q += IVAL(q, 0) + 4; /* 4 byte length plus data */
+
+ q += 2; /* Alignment? */
+
+ /* Misc other info */
+
+ q += 4; /* NT version (0x1) */
+ q += 2; /* LMNT token (0xff) */
+ q += 2; /* LM20 token (0xff) */
+#endif
+
+ SAFE_FREE(db_info); /* Not sure whether we need to do anything useful with these */
+
+ /* Send message to smbd */
+
+ send_repl_message(low_serial);
+ break;
+ }
+
+ default:
+ DEBUG(3,("process_logon_packet: Unknown domain request %d\n",code));
+ return;
+ }
+}
diff --git a/source/nmbd/nmbd_responserecordsdb.c b/source/nmbd/nmbd_responserecordsdb.c
new file mode 100644
index 00000000000..30c0c129508
--- /dev/null
+++ b/source/nmbd/nmbd_responserecordsdb.c
@@ -0,0 +1,248 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios library routines
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+
+extern int ClientNMB;
+
+int num_response_packets = 0;
+
+/***************************************************************************
+ Add an expected response record into the list
+ **************************************************************************/
+
+static void add_response_record(struct subnet_record *subrec,
+ struct response_record *rrec)
+{
+ struct response_record *rrec2;
+
+ num_response_packets++; /* count of total number of packets still around */
+
+ DEBUG(4,("add_response_record: adding response record id:%hu to subnet %s. num_records:%d\n",
+ rrec->response_id, subrec->subnet_name, num_response_packets));
+
+ if (!subrec->responselist) {
+ subrec->responselist = rrec;
+ rrec->prev = NULL;
+ rrec->next = NULL;
+ return;
+ }
+
+ for (rrec2 = subrec->responselist; rrec2->next; rrec2 = rrec2->next)
+ ;
+
+ rrec2->next = rrec;
+ rrec->next = NULL;
+ rrec->prev = rrec2;
+}
+
+/***************************************************************************
+ Remove an expected response record from the list
+ **************************************************************************/
+
+void remove_response_record(struct subnet_record *subrec,
+ struct response_record *rrec)
+{
+ if (rrec->prev)
+ rrec->prev->next = rrec->next;
+ if (rrec->next)
+ rrec->next->prev = rrec->prev;
+
+ if (subrec->responselist == rrec)
+ subrec->responselist = rrec->next;
+
+ if(rrec->userdata) {
+ if(rrec->userdata->free_fn) {
+ (*rrec->userdata->free_fn)(rrec->userdata);
+ } else {
+ ZERO_STRUCTP(rrec->userdata);
+ SAFE_FREE(rrec->userdata);
+ }
+ }
+
+ /* Ensure we can delete. */
+ rrec->packet->locked = False;
+ free_packet(rrec->packet);
+
+ ZERO_STRUCTP(rrec);
+ SAFE_FREE(rrec);
+
+ num_response_packets--; /* count of total number of packets still around */
+}
+
+/****************************************************************************
+ Create a response record for an outgoing packet.
+ **************************************************************************/
+
+struct response_record *make_response_record( struct subnet_record *subrec,
+ struct packet_struct *p,
+ response_function resp_fn,
+ timeout_response_function timeout_fn,
+ success_function success_fn,
+ fail_function fail_fn,
+ struct userdata_struct *userdata)
+{
+ struct response_record *rrec;
+ struct nmb_packet *nmb = &p->packet.nmb;
+
+ if (!(rrec = (struct response_record *)malloc(sizeof(*rrec)))) {
+ DEBUG(0,("make_response_queue_record: malloc fail for response_record.\n"));
+ return NULL;
+ }
+
+ memset((char *)rrec, '\0', sizeof(*rrec));
+
+ rrec->response_id = nmb->header.name_trn_id;
+
+ rrec->resp_fn = resp_fn;
+ rrec->timeout_fn = timeout_fn;
+ rrec->success_fn = success_fn;
+ rrec->fail_fn = fail_fn;
+
+ rrec->packet = p;
+
+ if(userdata) {
+ /* Intelligent userdata. */
+ if(userdata->copy_fn) {
+ if((rrec->userdata = (*userdata->copy_fn)(userdata)) == NULL) {
+ DEBUG(0,("make_response_queue_record: copy fail for userdata.\n"));
+ ZERO_STRUCTP(rrec);
+ SAFE_FREE(rrec);
+ return NULL;
+ }
+ } else {
+ /* Primitive userdata, do a memcpy. */
+ if((rrec->userdata = (struct userdata_struct *)
+ malloc(sizeof(struct userdata_struct)+userdata->userdata_len)) == NULL) {
+ DEBUG(0,("make_response_queue_record: malloc fail for userdata.\n"));
+ ZERO_STRUCTP(rrec);
+ SAFE_FREE(rrec);
+ return NULL;
+ }
+ rrec->userdata->copy_fn = userdata->copy_fn;
+ rrec->userdata->free_fn = userdata->free_fn;
+ rrec->userdata->userdata_len = userdata->userdata_len;
+ memcpy(rrec->userdata->data, userdata->data, userdata->userdata_len);
+ }
+ } else {
+ rrec->userdata = NULL;
+ }
+
+ rrec->num_msgs = 0;
+
+ if(!nmb->header.nm_flags.bcast)
+ rrec->repeat_interval = 5; /* 5 seconds for unicast packets. */
+ else
+ rrec->repeat_interval = 1; /* XXXX should be in ms */
+ rrec->repeat_count = 3; /* 3 retries */
+ rrec->repeat_time = time(NULL) + rrec->repeat_interval; /* initial retry time */
+
+ /* This packet is not being processed. */
+ rrec->in_expiration_processing = False;
+
+ /* Lock the packet so we won't lose it while it's on the list. */
+ p->locked = True;
+
+ add_response_record(subrec, rrec);
+
+ return rrec;
+}
+
+/****************************************************************************
+ Find a response in a subnet's name query response list.
+ **************************************************************************/
+
+static struct response_record *find_response_record_on_subnet(
+ struct subnet_record *subrec, uint16 id)
+{
+ struct response_record *rrec = NULL;
+
+ for (rrec = subrec->responselist; rrec; rrec = rrec->next) {
+ if (rrec->response_id == id) {
+ DEBUG(4, ("find_response_record: found response record id = %hu on subnet %s\n",
+ id, subrec->subnet_name));
+ break;
+ }
+ }
+ return rrec;
+}
+
+/****************************************************************************
+ Find a response in any subnet's name query response list.
+ **************************************************************************/
+
+struct response_record *find_response_record(struct subnet_record **ppsubrec,
+ uint16 id)
+{
+ struct response_record *rrec = NULL;
+
+ for ((*ppsubrec) = FIRST_SUBNET; (*ppsubrec);
+ (*ppsubrec) = NEXT_SUBNET_INCLUDING_UNICAST(*ppsubrec)) {
+ if((rrec = find_response_record_on_subnet(*ppsubrec, id)) != NULL)
+ return rrec;
+ }
+
+ /* There should never be response records on the remote_broadcast subnet.
+ Sanity check to ensure this is so. */
+ if(remote_broadcast_subnet->responselist != NULL) {
+ DEBUG(0,("find_response_record: response record found on subnet %s. This should \
+never happen !\n", remote_broadcast_subnet->subnet_name));
+ }
+
+ /* Now check the WINS server subnet if it exists. */
+ if(wins_server_subnet != NULL) {
+ *ppsubrec = wins_server_subnet;
+ if((rrec = find_response_record_on_subnet(*ppsubrec, id))!= NULL)
+ return rrec;
+ }
+
+ DEBUG(0,("find_response_record: response packet id %hu received with no \
+matching record.\n", id));
+
+ *ppsubrec = NULL;
+
+ return NULL;
+}
+
+/****************************************************************************
+ Check if a refresh is queued for a particular name on a particular subnet.
+ **************************************************************************/
+
+BOOL is_refresh_already_queued(struct subnet_record *subrec, struct name_record *namerec)
+{
+ struct response_record *rrec = NULL;
+
+ for (rrec = subrec->responselist; rrec; rrec = rrec->next) {
+ struct packet_struct *p = rrec->packet;
+ struct nmb_packet *nmb = &p->packet.nmb;
+
+ if((nmb->header.opcode == NMB_NAME_REFRESH_OPCODE_8) ||
+ (nmb->header.opcode == NMB_NAME_REFRESH_OPCODE_9)) {
+ /* Yes it's a queued refresh - check if the name is correct. */
+ if(nmb_name_equal(&nmb->question.question_name, &namerec->name))
+ return True;
+ }
+ }
+
+ return False;
+}
diff --git a/source/nmbd/nmbd_sendannounce.c b/source/nmbd/nmbd_sendannounce.c
new file mode 100644
index 00000000000..a74dd99196f
--- /dev/null
+++ b/source/nmbd/nmbd_sendannounce.c
@@ -0,0 +1,581 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-1998
+
+ SMB Version handling
+ Copyright (C) John H Terpstra 1995-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+
+extern int updatecount;
+extern BOOL found_lm_clients;
+
+/****************************************************************************
+ Send a browser reset packet.
+**************************************************************************/
+
+void send_browser_reset(int reset_type, const char *to_name, int to_type, struct in_addr to_ip)
+{
+ pstring outbuf;
+ char *p;
+
+ DEBUG(3,("send_browser_reset: sending reset request type %d to %s<%02x> IP %s.\n",
+ reset_type, to_name, to_type, inet_ntoa(to_ip) ));
+
+ memset(outbuf,'\0',sizeof(outbuf));
+ p = outbuf;
+ SCVAL(p,0,ANN_ResetBrowserState);
+ p++;
+ SCVAL(p,0,reset_type);
+ p++;
+
+ send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf),
+ global_myname(), 0x0, to_name, to_type, to_ip,
+ FIRST_SUBNET->myip, DGRAM_PORT);
+}
+
+/****************************************************************************
+ Broadcast a packet to the local net requesting that all servers in this
+ workgroup announce themselves to us.
+ **************************************************************************/
+
+void broadcast_announce_request(struct subnet_record *subrec, struct work_record *work)
+{
+ pstring outbuf;
+ char *p;
+
+ work->needannounce = True;
+
+ DEBUG(3,("broadcast_announce_request: sending announce request for workgroup %s \
+to subnet %s\n", work->work_group, subrec->subnet_name));
+
+ memset(outbuf,'\0',sizeof(outbuf));
+ p = outbuf;
+ SCVAL(p,0,ANN_AnnouncementRequest);
+ p++;
+
+ SCVAL(p,0,work->token); /* (local) Unique workgroup token id. */
+ p++;
+ p += push_string(NULL, p+1, global_myname(), 15, STR_ASCII|STR_UPPER|STR_TERMINATE);
+
+ send_mailslot(False, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf),
+ global_myname(), 0x0, work->work_group,0x1e, subrec->bcast_ip,
+ subrec->myip, DGRAM_PORT);
+}
+
+/****************************************************************************
+ Broadcast an announcement.
+ **************************************************************************/
+
+static void send_announcement(struct subnet_record *subrec, int announce_type,
+ const char *from_name, const char *to_name, int to_type, struct in_addr to_ip,
+ time_t announce_interval,
+ const char *server_name, int server_type, const char *server_comment)
+{
+ pstring outbuf;
+ unstring upper_server_name;
+ char *p;
+
+ memset(outbuf,'\0',sizeof(outbuf));
+ p = outbuf+1;
+
+ SCVAL(outbuf,0,announce_type);
+
+ /* Announcement parameters. */
+ SCVAL(p,0,updatecount);
+ SIVAL(p,1,announce_interval*1000); /* Milliseconds - despite the spec. */
+
+ safe_strcpy(upper_server_name, server_name, sizeof(upper_server_name)-1);
+ strupper_m(upper_server_name);
+ push_string(NULL, p+5, upper_server_name, 16, STR_ASCII|STR_TERMINATE);
+
+ SCVAL(p,21,lp_major_announce_version()); /* Major version. */
+ SCVAL(p,22,lp_minor_announce_version()); /* Minor version. */
+
+ SIVAL(p,23,server_type & ~SV_TYPE_LOCAL_LIST_ONLY);
+ /* Browse version: got from NT/AS 4.00 - Value defined in smb.h (JHT). */
+ SSVAL(p,27,BROWSER_ELECTION_VERSION);
+ SSVAL(p,29,BROWSER_CONSTANT); /* Browse signature. */
+
+ p += 31 + push_string(NULL, p+31, server_comment, -1, STR_ASCII|STR_TERMINATE);
+
+ send_mailslot(False,BROWSE_MAILSLOT, outbuf, PTR_DIFF(p,outbuf),
+ from_name, 0x0, to_name, to_type, to_ip, subrec->myip,
+ DGRAM_PORT);
+}
+
+/****************************************************************************
+ Broadcast a LanMan announcement.
+**************************************************************************/
+
+static void send_lm_announcement(struct subnet_record *subrec, int announce_type,
+ char *from_name, char *to_name, int to_type, struct in_addr to_ip,
+ time_t announce_interval,
+ char *server_name, int server_type, char *server_comment)
+{
+ pstring outbuf;
+ char *p=outbuf;
+
+ memset(outbuf,'\0',sizeof(outbuf));
+
+ SSVAL(p,0,announce_type);
+ SIVAL(p,2,server_type & ~SV_TYPE_LOCAL_LIST_ONLY);
+ SCVAL(p,6,lp_major_announce_version()); /* Major version. */
+ SCVAL(p,7,lp_minor_announce_version()); /* Minor version. */
+ SSVAL(p,8,announce_interval); /* In seconds - according to spec. */
+
+ p += 10;
+ p += push_string(NULL, p, server_name, 15, STR_ASCII|STR_UPPER|STR_TERMINATE);
+ p += push_string(NULL, p, server_comment, sizeof(pstring)-15, STR_ASCII|STR_UPPER|STR_TERMINATE);
+
+ send_mailslot(False,LANMAN_MAILSLOT, outbuf, PTR_DIFF(p,outbuf),
+ from_name, 0x0, to_name, to_type, to_ip, subrec->myip,
+ DGRAM_PORT);
+}
+
+/****************************************************************************
+ We are a local master browser. Announce this to WORKGROUP<1e>.
+****************************************************************************/
+
+static void send_local_master_announcement(struct subnet_record *subrec, struct work_record *work,
+ struct server_record *servrec)
+{
+ /* Ensure we don't have the prohibited bit set. */
+ uint32 type = servrec->serv.type & ~SV_TYPE_LOCAL_LIST_ONLY;
+
+ DEBUG(3,("send_local_master_announcement: type %x for name %s on subnet %s for workgroup %s\n",
+ type, global_myname(), subrec->subnet_name, work->work_group));
+
+ send_announcement(subrec, ANN_LocalMasterAnnouncement,
+ global_myname(), /* From nbt name. */
+ work->work_group, 0x1e, /* To nbt name. */
+ subrec->bcast_ip, /* To ip. */
+ work->announce_interval, /* Time until next announce. */
+ global_myname(), /* Name to announce. */
+ type, /* Type field. */
+ servrec->serv.comment);
+}
+
+/****************************************************************************
+ Announce the workgroup WORKGROUP to MSBROWSE<01>.
+****************************************************************************/
+
+static void send_workgroup_announcement(struct subnet_record *subrec, struct work_record *work)
+{
+ DEBUG(3,("send_workgroup_announcement: on subnet %s for workgroup %s\n",
+ subrec->subnet_name, work->work_group));
+
+ send_announcement(subrec, ANN_DomainAnnouncement,
+ global_myname(), /* From nbt name. */
+ MSBROWSE, 0x1, /* To nbt name. */
+ subrec->bcast_ip, /* To ip. */
+ work->announce_interval, /* Time until next announce. */
+ work->work_group, /* Name to announce. */
+ SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT, /* workgroup announce flags. */
+ global_myname()); /* From name as comment. */
+}
+
+/****************************************************************************
+ Announce the given host to WORKGROUP<1d>.
+****************************************************************************/
+
+static void send_host_announcement(struct subnet_record *subrec, struct work_record *work,
+ struct server_record *servrec)
+{
+ /* Ensure we don't have the prohibited bits set. */
+ uint32 type = servrec->serv.type & ~SV_TYPE_LOCAL_LIST_ONLY;
+
+ DEBUG(3,("send_host_announcement: type %x for host %s on subnet %s for workgroup %s\n",
+ type, servrec->serv.name, subrec->subnet_name, work->work_group));
+
+ send_announcement(subrec, ANN_HostAnnouncement,
+ servrec->serv.name, /* From nbt name. */
+ work->work_group, 0x1d, /* To nbt name. */
+ subrec->bcast_ip, /* To ip. */
+ work->announce_interval, /* Time until next announce. */
+ servrec->serv.name, /* Name to announce. */
+ type, /* Type field. */
+ servrec->serv.comment);
+}
+
+/****************************************************************************
+ Announce the given LanMan host
+****************************************************************************/
+
+static void send_lm_host_announcement(struct subnet_record *subrec, struct work_record *work,
+ struct server_record *servrec, int lm_interval)
+{
+ /* Ensure we don't have the prohibited bits set. */
+ uint32 type = servrec->serv.type & ~SV_TYPE_LOCAL_LIST_ONLY;
+
+ DEBUG(3,("send_lm_host_announcement: type %x for host %s on subnet %s for workgroup %s, ttl: %d\n",
+ type, servrec->serv.name, subrec->subnet_name, work->work_group, lm_interval));
+
+ send_lm_announcement(subrec, ANN_HostAnnouncement,
+ servrec->serv.name, /* From nbt name. */
+ work->work_group, 0x00, /* To nbt name. */
+ subrec->bcast_ip, /* To ip. */
+ lm_interval, /* Time until next announce. */
+ servrec->serv.name, /* Name to announce (fstring not netbios name struct). */
+ type, /* Type field. */
+ servrec->serv.comment);
+}
+
+/****************************************************************************
+ Announce a server record.
+ ****************************************************************************/
+
+static void announce_server(struct subnet_record *subrec, struct work_record *work,
+ struct server_record *servrec)
+{
+ /* Only do domain announcements if we are a master and it's
+ our primary name we're being asked to announce. */
+
+ if (AM_LOCAL_MASTER_BROWSER(work) && strequal(global_myname(),servrec->serv.name)) {
+ send_local_master_announcement(subrec, work, servrec);
+ send_workgroup_announcement(subrec, work);
+ } else {
+ send_host_announcement(subrec, work, servrec);
+ }
+}
+
+/****************************************************************************
+ Go through all my registered names on all broadcast subnets and announce
+ them if the timeout requires it.
+ **************************************************************************/
+
+void announce_my_server_names(time_t t)
+{
+ struct subnet_record *subrec;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ struct work_record *work = find_workgroup_on_subnet(subrec, lp_workgroup());
+
+ if(work) {
+ struct server_record *servrec;
+
+ if (work->needannounce) {
+ /* Drop back to a max 3 minute announce. This is to prevent a
+ single lost packet from breaking things for too long. */
+
+ work->announce_interval = MIN(work->announce_interval,
+ CHECK_TIME_MIN_HOST_ANNCE*60);
+ work->lastannounce_time = t - (work->announce_interval+1);
+ work->needannounce = False;
+ }
+
+ /* Announce every minute at first then progress to every 12 mins */
+ if ((t - work->lastannounce_time) < work->announce_interval)
+ continue;
+
+ if (work->announce_interval < (CHECK_TIME_MAX_HOST_ANNCE * 60))
+ work->announce_interval += 60;
+
+ work->lastannounce_time = t;
+
+ for (servrec = work->serverlist; servrec; servrec = servrec->next) {
+ if (is_myname(servrec->serv.name))
+ announce_server(subrec, work, servrec);
+ }
+ } /* if work */
+ } /* for subrec */
+}
+
+/****************************************************************************
+ Go through all my registered names on all broadcast subnets and announce
+ them as a LanMan server if the timeout requires it.
+**************************************************************************/
+
+void announce_my_lm_server_names(time_t t)
+{
+ struct subnet_record *subrec;
+ static time_t last_lm_announce_time=0;
+ int announce_interval = lp_lm_interval();
+ int lm_announce = lp_lm_announce();
+
+ if ((announce_interval <= 0) || (lm_announce <= 0)) {
+ /* user absolutely does not want LM announcements to be sent. */
+ return;
+ }
+
+ if ((lm_announce >= 2) && (!found_lm_clients)) {
+ /* has been set to 2 (Auto) but no LM clients detected (yet). */
+ return;
+ }
+
+ /* Otherwise: must have been set to 1 (Yes), or LM clients *have*
+ been detected. */
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ struct work_record *work = find_workgroup_on_subnet(subrec, lp_workgroup());
+
+ if(work) {
+ struct server_record *servrec;
+
+ if (last_lm_announce_time && ((t - last_lm_announce_time) < announce_interval ))
+ continue;
+
+ last_lm_announce_time = t;
+
+ for (servrec = work->serverlist; servrec; servrec = servrec->next) {
+ if (is_myname(servrec->serv.name))
+ /* skipping equivalent of announce_server() */
+ send_lm_host_announcement(subrec, work, servrec, announce_interval);
+ }
+ } /* if work */
+ } /* for subrec */
+}
+
+/* Announce timer. Moved into global static so it can be reset
+ when a machine becomes a local master browser. */
+static time_t announce_timer_last=0;
+
+/****************************************************************************
+ Reset the announce_timer so that a local master browser announce will be done
+ immediately.
+ ****************************************************************************/
+
+void reset_announce_timer(void)
+{
+ announce_timer_last = time(NULL) - (CHECK_TIME_MST_ANNOUNCE * 60);
+}
+
+/****************************************************************************
+ Announce myself as a local master browser to a domain master browser.
+ **************************************************************************/
+
+void announce_myself_to_domain_master_browser(time_t t)
+{
+ struct subnet_record *subrec;
+ struct work_record *work;
+
+ if(!we_are_a_wins_client()) {
+ DEBUG(10,("announce_myself_to_domain_master_browser: no unicast subnet, ignoring.\n"));
+ return;
+ }
+
+ if (!announce_timer_last)
+ announce_timer_last = t;
+
+ if ((t-announce_timer_last) < (CHECK_TIME_MST_ANNOUNCE * 60)) {
+ DEBUG(10,("announce_myself_to_domain_master_browser: t (%d) - last(%d) < %d\n",
+ (int)t, (int)announce_timer_last,
+ CHECK_TIME_MST_ANNOUNCE * 60 ));
+ return;
+ }
+
+ announce_timer_last = t;
+
+ /* Look over all our broadcast subnets to see if any of them
+ has the state set as local master browser. */
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ for (work = subrec->workgrouplist; work; work = work->next) {
+ if (AM_LOCAL_MASTER_BROWSER(work)) {
+ DEBUG(4,( "announce_myself_to_domain_master_browser: I am a local master browser for \
+workgroup %s on subnet %s\n", work->work_group, subrec->subnet_name));
+
+ /* Look in nmbd_browsersync.c for the rest of this code. */
+ announce_and_sync_with_domain_master_browser(subrec, work);
+ }
+ }
+ }
+}
+
+/****************************************************************************
+Announce all samba's server entries as 'gone'.
+This must *only* be called on shutdown.
+****************************************************************************/
+
+void announce_my_servers_removed(void)
+{
+ int announce_interval = lp_lm_interval();
+ int lm_announce = lp_lm_announce();
+ struct subnet_record *subrec;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ struct work_record *work;
+ for (work = subrec->workgrouplist; work; work = work->next) {
+ struct server_record *servrec;
+
+ work->announce_interval = 0;
+ for (servrec = work->serverlist; servrec; servrec = servrec->next) {
+ if (!is_myname(servrec->serv.name))
+ continue;
+ servrec->serv.type = 0;
+ if(AM_LOCAL_MASTER_BROWSER(work))
+ send_local_master_announcement(subrec, work, servrec);
+ send_host_announcement(subrec, work, servrec);
+
+ if ((announce_interval <= 0) || (lm_announce <= 0)) {
+ /* user absolutely does not want LM announcements to be sent. */
+ continue;
+ }
+
+ if ((lm_announce >= 2) && (!found_lm_clients)) {
+ /* has been set to 2 (Auto) but no LM clients detected (yet). */
+ continue;
+ }
+
+ /*
+ * lm announce was set or we have seen lm announcements, so do
+ * a lm announcement of host removed.
+ */
+
+ send_lm_host_announcement(subrec, work, servrec, 0);
+ }
+ }
+ }
+}
+
+/****************************************************************************
+ Do all the "remote" announcements. These are used to put ourselves
+ on a remote browse list. They are done blind, no checking is done to
+ see if there is actually a local master browser at the other end.
+ **************************************************************************/
+
+void announce_remote(time_t t)
+{
+ char *s;
+ const char *ptr;
+ static time_t last_time = 0;
+ pstring s2;
+ struct in_addr addr;
+ char *comment;
+ int stype = lp_default_server_announce();
+
+ if (last_time && (t < (last_time + REMOTE_ANNOUNCE_INTERVAL)))
+ return;
+
+ last_time = t;
+
+ s = lp_remote_announce();
+ if (!*s)
+ return;
+
+ comment = string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH);
+
+ for (ptr=s; next_token(&ptr,s2,NULL,sizeof(s2)); ) {
+ /* The entries are of the form a.b.c.d/WORKGROUP with
+ WORKGROUP being optional */
+ const char *wgroup;
+ char *pwgroup;
+ int i;
+
+ pwgroup = strchr_m(s2,'/');
+ if (pwgroup)
+ *pwgroup++ = 0;
+ if (!pwgroup || !*pwgroup)
+ wgroup = lp_workgroup();
+ else
+ wgroup = pwgroup;
+
+ addr = *interpret_addr2(s2);
+
+ /* Announce all our names including aliases */
+ /* Give the ip address as the address of our first
+ broadcast subnet. */
+
+ for(i=0; my_netbios_names(i); i++) {
+ const char *name = my_netbios_names(i);
+
+ DEBUG(5,("announce_remote: Doing remote announce for server %s to IP %s.\n",
+ name, inet_ntoa(addr) ));
+
+ send_announcement(FIRST_SUBNET, ANN_HostAnnouncement,
+ name, /* From nbt name. */
+ wgroup, 0x1d, /* To nbt name. */
+ addr, /* To ip. */
+ REMOTE_ANNOUNCE_INTERVAL, /* Time until next announce. */
+ name, /* Name to announce. */
+ stype, /* Type field. */
+ comment);
+ }
+ }
+}
+
+/****************************************************************************
+ Implement the 'remote browse sync' feature Andrew added.
+ These are used to put our browse lists into remote browse lists.
+**************************************************************************/
+
+void browse_sync_remote(time_t t)
+{
+ char *s;
+ const char *ptr;
+ static time_t last_time = 0;
+ pstring s2;
+ struct in_addr addr;
+ struct work_record *work;
+ pstring outbuf;
+ char *p;
+ unstring myname;
+
+ if (last_time && (t < (last_time + REMOTE_ANNOUNCE_INTERVAL)))
+ return;
+
+ last_time = t;
+
+ s = lp_remote_browse_sync();
+ if (!*s)
+ return;
+
+ /*
+ * We only do this if we are the local master browser
+ * for our workgroup on the firsst subnet.
+ */
+
+ if((work = find_workgroup_on_subnet(FIRST_SUBNET, lp_workgroup())) == NULL) {
+ DEBUG(0,("browse_sync_remote: Cannot find workgroup %s on subnet %s\n",
+ lp_workgroup(), FIRST_SUBNET->subnet_name ));
+ return;
+ }
+
+ if(!AM_LOCAL_MASTER_BROWSER(work)) {
+ DEBUG(5,("browse_sync_remote: We can only do this if we are a local master browser \
+for workgroup %s on subnet %s.\n", lp_workgroup(), FIRST_SUBNET->subnet_name ));
+ return;
+ }
+
+ memset(outbuf,'\0',sizeof(outbuf));
+ p = outbuf;
+ SCVAL(p,0,ANN_MasterAnnouncement);
+ p++;
+
+ unstrcpy(myname, global_myname());
+ strupper_m(myname);
+ myname[15]='\0';
+ push_pstring_base(p, myname, outbuf);
+
+ p = skip_string(p,1);
+
+ for (ptr=s; next_token(&ptr,s2,NULL,sizeof(s2)); ) {
+ /* The entries are of the form a.b.c.d */
+ addr = *interpret_addr2(s2);
+
+ DEBUG(5,("announce_remote: Doing remote browse sync announce for server %s to IP %s.\n",
+ global_myname(), inet_ntoa(addr) ));
+
+ send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf),
+ global_myname(), 0x0, "*", 0x0, addr, FIRST_SUBNET->myip, DGRAM_PORT);
+ }
+}
diff --git a/source/nmbd/nmbd_serverlistdb.c b/source/nmbd/nmbd_serverlistdb.c
new file mode 100644
index 00000000000..e6fad8319d9
--- /dev/null
+++ b/source/nmbd/nmbd_serverlistdb.c
@@ -0,0 +1,422 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+
+extern int ClientNMB;
+
+int updatecount = 0;
+
+/*******************************************************************
+ Remove all the servers in a work group.
+ ******************************************************************/
+
+void remove_all_servers(struct work_record *work)
+{
+ struct server_record *servrec;
+ struct server_record *nexts;
+
+ for (servrec = work->serverlist; servrec; servrec = nexts) {
+ DEBUG(7,("remove_all_servers: Removing server %s\n",servrec->serv.name));
+ nexts = servrec->next;
+
+ if (servrec->prev)
+ servrec->prev->next = servrec->next;
+ if (servrec->next)
+ servrec->next->prev = servrec->prev;
+
+ if (work->serverlist == servrec)
+ work->serverlist = servrec->next;
+
+ ZERO_STRUCTP(servrec);
+ SAFE_FREE(servrec);
+ }
+
+ work->subnet->work_changed = True;
+}
+
+/***************************************************************************
+ Add a server into the a workgroup serverlist.
+ **************************************************************************/
+
+static void add_server_to_workgroup(struct work_record *work,
+ struct server_record *servrec)
+{
+ struct server_record *servrec2;
+
+ if (!work->serverlist) {
+ work->serverlist = servrec;
+ servrec->prev = NULL;
+ servrec->next = NULL;
+ return;
+ }
+
+ for (servrec2 = work->serverlist; servrec2->next; servrec2 = servrec2->next)
+ ;
+
+ servrec2->next = servrec;
+ servrec->next = NULL;
+ servrec->prev = servrec2;
+ work->subnet->work_changed = True;
+}
+
+/****************************************************************************
+ Find a server in a server list.
+ **************************************************************************/
+
+struct server_record *find_server_in_workgroup(struct work_record *work, const char *name)
+{
+ struct server_record *ret;
+
+ for (ret = work->serverlist; ret; ret = ret->next) {
+ if (strequal(ret->serv.name,name))
+ return ret;
+ }
+ return NULL;
+}
+
+
+/****************************************************************************
+ Remove a server entry from this workgroup.
+ ****************************************************************************/
+
+void remove_server_from_workgroup(struct work_record *work, struct server_record *servrec)
+{
+ if (servrec->prev)
+ servrec->prev->next = servrec->next;
+ if (servrec->next)
+ servrec->next->prev = servrec->prev;
+
+ if (work->serverlist == servrec)
+ work->serverlist = servrec->next;
+
+ ZERO_STRUCTP(servrec);
+ SAFE_FREE(servrec);
+ work->subnet->work_changed = True;
+}
+
+/****************************************************************************
+ Create a server entry on this workgroup.
+ ****************************************************************************/
+
+struct server_record *create_server_on_workgroup(struct work_record *work,
+ const char *name,int servertype,
+ int ttl, const char *comment)
+{
+ struct server_record *servrec;
+
+ if (name[0] == '*') {
+ DEBUG(7,("create_server_on_workgroup: not adding name starting with '*' (%s)\n",
+ name));
+ return (NULL);
+ }
+
+ if((servrec = find_server_in_workgroup(work, name)) != NULL) {
+ DEBUG(0,("create_server_on_workgroup: Server %s already exists on \
+workgroup %s. This is a bug.\n", name, work->work_group));
+ return NULL;
+ }
+
+ if((servrec = (struct server_record *)malloc(sizeof(*servrec))) == NULL) {
+ DEBUG(0,("create_server_entry_on_workgroup: malloc fail !\n"));
+ return NULL;
+ }
+
+ memset((char *)servrec,'\0',sizeof(*servrec));
+
+ servrec->subnet = work->subnet;
+
+ fstrcpy(servrec->serv.name,name);
+ fstrcpy(servrec->serv.comment,comment);
+ strupper_m(servrec->serv.name);
+ servrec->serv.type = servertype;
+
+ update_server_ttl(servrec, ttl);
+
+ add_server_to_workgroup(work, servrec);
+
+ DEBUG(3,("create_server_on_workgroup: Created server entry %s of type %x (%s) on \
+workgroup %s.\n", name,servertype,comment, work->work_group));
+
+ work->subnet->work_changed = True;
+
+ return(servrec);
+}
+
+/*******************************************************************
+ Update the ttl field of a server record.
+*******************************************************************/
+
+void update_server_ttl(struct server_record *servrec, int ttl)
+{
+ if(ttl > lp_max_ttl())
+ ttl = lp_max_ttl();
+
+ if(is_myname(servrec->serv.name))
+ servrec->death_time = PERMANENT_TTL;
+ else
+ servrec->death_time = (ttl != PERMANENT_TTL) ? time(NULL)+(ttl*3) : PERMANENT_TTL;
+
+ servrec->subnet->work_changed = True;
+}
+
+/*******************************************************************
+ Expire old servers in the serverlist. A time of -1 indicates
+ everybody dies except those with a death_time of PERMANENT_TTL (which is 0).
+ This should only be called from expire_workgroups_and_servers().
+ ******************************************************************/
+
+void expire_servers(struct work_record *work, time_t t)
+{
+ struct server_record *servrec;
+ struct server_record *nexts;
+
+ for (servrec = work->serverlist; servrec; servrec = nexts) {
+ nexts = servrec->next;
+
+ if ((servrec->death_time != PERMANENT_TTL) && ((t == -1) || (servrec->death_time < t))) {
+ DEBUG(3,("expire_old_servers: Removing timed out server %s\n",servrec->serv.name));
+ remove_server_from_workgroup(work, servrec);
+ work->subnet->work_changed = True;
+ }
+ }
+}
+
+/*******************************************************************
+ Decide if we should write out a server record for this server.
+ We return zero if we should not. Check if we've already written
+ out this server record from an earlier subnet.
+******************************************************************/
+
+static uint32 write_this_server_name( struct subnet_record *subrec,
+ struct work_record *work,
+ struct server_record *servrec)
+{
+ struct subnet_record *ssub;
+ struct work_record *iwork;
+
+ /* Go through all the subnets we have already seen. */
+ for (ssub = FIRST_SUBNET; ssub != subrec; ssub = NEXT_SUBNET_INCLUDING_UNICAST(ssub)) {
+ for(iwork = ssub->workgrouplist; iwork; iwork = iwork->next) {
+ if(find_server_in_workgroup( iwork, servrec->serv.name) != NULL) {
+ /*
+ * We have already written out this server record, don't
+ * do it again. This gives precedence to servers we have seen
+ * on the broadcast subnets over servers that may have been
+ * added via a sync on the unicast_subet.
+ *
+ * The correct way to do this is to have a serverlist file
+ * per subnet - this means changes to smbd as well. I may
+ * add this at a later date (JRA).
+ */
+
+ return 0;
+ }
+ }
+ }
+
+ return servrec->serv.type;
+}
+
+/*******************************************************************
+ Decide if we should write out a workgroup record for this workgroup.
+ We return zero if we should not. Don't write out lp_workgroup() (we've
+ already done it) and also don't write out a second workgroup record
+ on the unicast subnet that we've already written out on one of the
+ broadcast subnets.
+******************************************************************/
+
+static uint32 write_this_workgroup_name( struct subnet_record *subrec,
+ struct work_record *work)
+{
+ struct subnet_record *ssub;
+
+ if(strequal(lp_workgroup(), work->work_group))
+ return 0;
+
+ /* This is a workgroup we have seen on a broadcast subnet. All
+ these have the same type. */
+
+ if(subrec != unicast_subnet)
+ return (SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT|SV_TYPE_LOCAL_LIST_ONLY);
+
+ for(ssub = FIRST_SUBNET; ssub; ssub = NEXT_SUBNET_EXCLUDING_UNICAST(ssub)) {
+ /* This is the unicast subnet so check if we've already written out
+ this subnet when we passed over the broadcast subnets. */
+
+ if(find_workgroup_on_subnet( ssub, work->work_group) != NULL)
+ return 0;
+ }
+
+ /* All workgroups on the unicast subnet (except our own, which we
+ have already written out) cannot be local. */
+
+ return (SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT);
+}
+
+/*******************************************************************
+ Write out the browse.dat file.
+ ******************************************************************/
+
+void write_browse_list_entry(XFILE *fp, const char *name, uint32 rec_type,
+ const char *local_master_browser_name, const char *description)
+{
+ fstring tmp;
+
+ slprintf(tmp,sizeof(tmp)-1, "\"%s\"", name);
+ x_fprintf(fp, "%-25s ", tmp);
+ x_fprintf(fp, "%08x ", rec_type);
+ slprintf(tmp, sizeof(tmp)-1, "\"%s\" ", local_master_browser_name);
+ x_fprintf(fp, "%-30s", tmp);
+ x_fprintf(fp, "\"%s\"\n", description);
+}
+
+void write_browse_list(time_t t, BOOL force_write)
+{
+ struct subnet_record *subrec;
+ struct work_record *work;
+ struct server_record *servrec;
+ pstring fname,fnamenew;
+ uint32 stype;
+ int i;
+ XFILE *fp;
+ BOOL list_changed = force_write;
+ static time_t lasttime = 0;
+
+ /* Always dump if we're being told to by a signal. */
+ if(force_write == False) {
+ if (!lasttime)
+ lasttime = t;
+ if (t - lasttime < 5)
+ return;
+ }
+
+ lasttime = t;
+
+ dump_workgroups(force_write);
+
+ for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) {
+ if(subrec->work_changed) {
+ list_changed = True;
+ break;
+ }
+ }
+
+ if(!list_changed)
+ return;
+
+ updatecount++;
+
+ pstrcpy(fname,lp_lockdir());
+ trim_char(fname,'\0' ,'/');
+ pstrcat(fname,"/");
+ pstrcat(fname,SERVER_LIST);
+ pstrcpy(fnamenew,fname);
+ pstrcat(fnamenew,".");
+
+ fp = x_fopen(fnamenew,O_WRONLY|O_CREAT|O_TRUNC, 0644);
+
+ if (!fp) {
+ DEBUG(0,("write_browse_list: Can't open file %s. Error was %s\n",
+ fnamenew,strerror(errno)));
+ return;
+ }
+
+ /*
+ * Write out a record for our workgroup. Use the record from the first
+ * subnet.
+ */
+
+ if((work = find_workgroup_on_subnet(FIRST_SUBNET, lp_workgroup())) == NULL) {
+ DEBUG(0,("write_browse_list: Fatal error - cannot find my workgroup %s\n",
+ lp_workgroup()));
+ x_fclose(fp);
+ return;
+ }
+
+ write_browse_list_entry(fp, work->work_group,
+ SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT|SV_TYPE_LOCAL_LIST_ONLY,
+ work->local_master_browser_name, work->work_group);
+
+ /*
+ * We need to do something special for our own names.
+ * This is due to the fact that we may be a local master browser on
+ * one of our broadcast subnets, and a domain master on the unicast
+ * subnet. We iterate over the subnets and only write out the name
+ * once.
+ */
+
+ for (i=0; my_netbios_names(i); i++) {
+ stype = 0;
+ for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) {
+ if((work = find_workgroup_on_subnet( subrec, lp_workgroup() )) == NULL)
+ continue;
+ if((servrec = find_server_in_workgroup( work, my_netbios_names(i))) == NULL)
+ continue;
+
+ stype |= servrec->serv.type;
+ }
+
+ /* Output server details, plus what workgroup they're in. */
+ write_browse_list_entry(fp, my_netbios_names(i), stype,
+ string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH), lp_workgroup());
+ }
+
+ for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) {
+ subrec->work_changed = False;
+
+ for (work = subrec->workgrouplist; work ; work = work->next) {
+ /* Write out a workgroup record for a workgroup. */
+ uint32 wg_type = write_this_workgroup_name( subrec, work);
+
+ if(wg_type) {
+ write_browse_list_entry(fp, work->work_group, wg_type,
+ work->local_master_browser_name,
+ work->work_group);
+ }
+
+ /* Now write out any server records a workgroup may have. */
+
+ for (servrec = work->serverlist; servrec ; servrec = servrec->next) {
+ uint32 serv_type;
+
+ /* We have already written our names here. */
+ if(is_myname(servrec->serv.name))
+ continue;
+
+ serv_type = write_this_server_name(subrec, work, servrec);
+ if(serv_type) {
+ /* Output server details, plus what workgroup they're in. */
+ write_browse_list_entry(fp, servrec->serv.name, serv_type,
+ servrec->serv.comment, work->work_group);
+ }
+ }
+ }
+ }
+
+ x_fclose(fp);
+ unlink(fname);
+ chmod(fnamenew,0644);
+ rename(fnamenew,fname);
+ DEBUG(3,("write_browse_list: Wrote browse list into file %s\n",fname));
+}
diff --git a/source/nmbd/nmbd_subnetdb.c b/source/nmbd/nmbd_subnetdb.c
new file mode 100644
index 00000000000..02a91f27606
--- /dev/null
+++ b/source/nmbd/nmbd_subnetdb.c
@@ -0,0 +1,344 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Revision History:
+
+*/
+
+#include "includes.h"
+
+extern int ClientNMB;
+extern int ClientDGRAM;
+extern int global_nmb_port;
+
+/* This is the broadcast subnets database. */
+struct subnet_record *subnetlist = NULL;
+
+/* Extra subnets - keep these separate so enumeration code doesn't
+ run onto it by mistake. */
+
+struct subnet_record *unicast_subnet = NULL;
+struct subnet_record *remote_broadcast_subnet = NULL;
+struct subnet_record *wins_server_subnet = NULL;
+
+extern uint16 samba_nb_type; /* Samba's NetBIOS name type. */
+
+/****************************************************************************
+ Add a subnet into the list.
+ **************************************************************************/
+
+static void add_subnet(struct subnet_record *subrec)
+{
+ DLIST_ADD(subnetlist, subrec);
+}
+
+/* ************************************************************************** **
+ * Comparison routine for ordering the splay-tree based namelists assoicated
+ * with each subnet record.
+ *
+ * Input: Item - Pointer to the comparison key.
+ * Node - Pointer to a node the splay tree.
+ *
+ * Output: The return value will be <0 , ==0, or >0 depending upon the
+ * ordinal relationship of the two keys.
+ *
+ * ************************************************************************** **
+ */
+static int namelist_entry_compare( ubi_trItemPtr Item, ubi_trNodePtr Node )
+{
+ struct name_record *NR = (struct name_record *)Node;
+
+ if( DEBUGLVL( 10 ) ) {
+ struct nmb_name *Iname = (struct nmb_name *)Item;
+
+ Debug1( "nmbd_subnetdb:namelist_entry_compare()\n" );
+ Debug1( "%d == memcmp( \"%s\", \"%s\", %d )\n",
+ memcmp( Item, &(NR->name), sizeof(struct nmb_name) ),
+ nmb_namestr(Iname), nmb_namestr(&NR->name), (int)sizeof(struct nmb_name) );
+ }
+
+ return( memcmp( Item, &(NR->name), sizeof(struct nmb_name) ) );
+}
+
+/****************************************************************************
+stop listening on a subnet
+we don't free the record as we don't have proper reference counting for it
+yet and it may be in use by a response record
+ ****************************************************************************/
+
+void close_subnet(struct subnet_record *subrec)
+{
+ DLIST_REMOVE(subnetlist, subrec);
+
+ if (subrec->dgram_sock != -1) {
+ close(subrec->dgram_sock);
+ subrec->dgram_sock = -1;
+ }
+ if (subrec->nmb_sock != -1) {
+ close(subrec->nmb_sock);
+ subrec->nmb_sock = -1;
+ }
+}
+
+/****************************************************************************
+ Create a subnet entry.
+ ****************************************************************************/
+
+static struct subnet_record *make_subnet(const char *name, enum subnet_type type,
+ struct in_addr myip, struct in_addr bcast_ip,
+ struct in_addr mask_ip)
+{
+ struct subnet_record *subrec = NULL;
+ int nmb_sock, dgram_sock;
+
+ /* Check if we are creating a non broadcast subnet - if so don't create
+ sockets. */
+
+ if(type != NORMAL_SUBNET) {
+ nmb_sock = -1;
+ dgram_sock = -1;
+ } else {
+ /*
+ * Attempt to open the sockets on port 137/138 for this interface
+ * and bind them.
+ * Fail the subnet creation if this fails.
+ */
+
+ if((nmb_sock = open_socket_in(SOCK_DGRAM, global_nmb_port,0, myip.s_addr,True)) == -1) {
+ if( DEBUGLVL( 0 ) ) {
+ Debug1( "nmbd_subnetdb:make_subnet()\n" );
+ Debug1( " Failed to open nmb socket on interface %s ", inet_ntoa(myip) );
+ Debug1( "for port %d. ", global_nmb_port );
+ Debug1( "Error was %s\n", strerror(errno) );
+ }
+ return NULL;
+ }
+
+ if((dgram_sock = open_socket_in(SOCK_DGRAM,DGRAM_PORT,3, myip.s_addr,True)) == -1) {
+ if( DEBUGLVL( 0 ) ) {
+ Debug1( "nmbd_subnetdb:make_subnet()\n" );
+ Debug1( " Failed to open dgram socket on interface %s ", inet_ntoa(myip) );
+ Debug1( "for port %d. ", DGRAM_PORT );
+ Debug1( "Error was %s\n", strerror(errno) );
+ }
+ return NULL;
+ }
+
+ /* Make sure we can broadcast from these sockets. */
+ set_socket_options(nmb_sock,"SO_BROADCAST");
+ set_socket_options(dgram_sock,"SO_BROADCAST");
+ }
+
+ subrec = (struct subnet_record *)malloc(sizeof(*subrec));
+ if (!subrec) {
+ DEBUG(0,("make_subnet: malloc fail !\n"));
+ close(nmb_sock);
+ close(dgram_sock);
+ return(NULL);
+ }
+
+ memset( (char *)subrec, '\0', sizeof(*subrec) );
+ (void)ubi_trInitTree( subrec->namelist,
+ namelist_entry_compare,
+ ubi_trOVERWRITE );
+
+ if((subrec->subnet_name = strdup(name)) == NULL) {
+ DEBUG(0,("make_subnet: malloc fail for subnet name !\n"));
+ close(nmb_sock);
+ close(dgram_sock);
+ ZERO_STRUCTP(subrec);
+ SAFE_FREE(subrec);
+ return(NULL);
+ }
+
+ DEBUG(2, ("making subnet name:%s ", name ));
+ DEBUG(2, ("Broadcast address:%s ", inet_ntoa(bcast_ip)));
+ DEBUG(2, ("Subnet mask:%s\n", inet_ntoa(mask_ip)));
+
+ subrec->namelist_changed = False;
+ subrec->work_changed = False;
+
+ subrec->bcast_ip = bcast_ip;
+ subrec->mask_ip = mask_ip;
+ subrec->myip = myip;
+ subrec->type = type;
+ subrec->nmb_sock = nmb_sock;
+ subrec->dgram_sock = dgram_sock;
+
+ return subrec;
+}
+
+/****************************************************************************
+ Create a normal subnet
+**************************************************************************/
+
+struct subnet_record *make_normal_subnet(struct interface *iface)
+{
+ struct subnet_record *subrec;
+
+ subrec = make_subnet(inet_ntoa(iface->ip), NORMAL_SUBNET,
+ iface->ip, iface->bcast, iface->nmask);
+ if (subrec) {
+ add_subnet(subrec);
+ }
+ return subrec;
+}
+
+/****************************************************************************
+ Create subnet entries.
+**************************************************************************/
+
+BOOL create_subnets(void)
+{
+ int num_interfaces = iface_count();
+ int i;
+ struct in_addr unicast_ip, ipzero;
+ extern struct in_addr loopback_ip;
+
+ if(num_interfaces == 0) {
+ DEBUG(0,("create_subnets: No local interfaces !\n"));
+ DEBUG(0,("create_subnets: Waiting for an interface to appear ...\n"));
+ while (iface_count() == 0) {
+ sleep(5);
+ load_interfaces();
+ }
+ }
+
+ num_interfaces = iface_count();
+
+ /*
+ * Create subnets from all the local interfaces and thread them onto
+ * the linked list.
+ */
+
+ for (i = 0 ; i < num_interfaces; i++) {
+ struct interface *iface = get_interface(i);
+
+ /*
+ * We don't want to add a loopback interface, in case
+ * someone has added 127.0.0.1 for smbd, nmbd needs to
+ * ignore it here. JRA.
+ */
+
+ if (ip_equal(iface->ip, loopback_ip)) {
+ DEBUG(2,("create_subnets: Ignoring loopback interface.\n" ));
+ continue;
+ }
+
+ if (!make_normal_subnet(iface))
+ return False;
+ }
+
+ if (lp_we_are_a_wins_server()) {
+ /* Pick the first interface ip address as the WINS server ip. */
+ unicast_ip = *iface_n_ip(0);
+ } else {
+ /* note that we do not set the wins server IP here. We just
+ set it at zero and let the wins registration code cope
+ with getting the IPs right for each packet */
+ zero_ip(&unicast_ip);
+ }
+
+ /*
+ * Create the unicast and remote broadcast subnets.
+ * Don't put these onto the linked list.
+ * The ip address of the unicast subnet is set to be
+ * the WINS server address, if it exists, or ipzero if not.
+ */
+
+ unicast_subnet = make_subnet( "UNICAST_SUBNET", UNICAST_SUBNET,
+ unicast_ip, unicast_ip, unicast_ip);
+
+ zero_ip(&ipzero);
+
+ remote_broadcast_subnet = make_subnet( "REMOTE_BROADCAST_SUBNET",
+ REMOTE_BROADCAST_SUBNET,
+ ipzero, ipzero, ipzero);
+
+ if((unicast_subnet == NULL) || (remote_broadcast_subnet == NULL))
+ return False;
+
+ /*
+ * If we are WINS server, create the WINS_SERVER_SUBNET - don't put on
+ * the linked list.
+ */
+
+ if (lp_we_are_a_wins_server()) {
+ if( (wins_server_subnet = make_subnet( "WINS_SERVER_SUBNET",
+ WINS_SERVER_SUBNET,
+ ipzero, ipzero, ipzero )) == NULL )
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+Function to tell us if we can use the unicast subnet.
+******************************************************************/
+
+BOOL we_are_a_wins_client(void)
+{
+ if (wins_srv_count() > 0) {
+ return True;
+ }
+
+ return False;
+}
+
+/*******************************************************************
+Access function used by NEXT_SUBNET_INCLUDING_UNICAST
+******************************************************************/
+
+struct subnet_record *get_next_subnet_maybe_unicast(struct subnet_record *subrec)
+{
+ if(subrec == unicast_subnet)
+ return NULL;
+ else if((subrec->next == NULL) && we_are_a_wins_client())
+ return unicast_subnet;
+ else
+ return subrec->next;
+}
+
+/*******************************************************************
+ Access function used by retransmit_or_expire_response_records() in
+ nmbd_packets.c. Patch from Andrey Alekseyev <fetch@muffin.arcadia.spb.ru>
+ Needed when we need to enumerate all the broadcast, unicast and
+ WINS subnets.
+******************************************************************/
+
+struct subnet_record *get_next_subnet_maybe_unicast_or_wins_server(struct subnet_record *subrec)
+{
+ if(subrec == unicast_subnet) {
+ if(wins_server_subnet)
+ return wins_server_subnet;
+ else
+ return NULL;
+ }
+
+ if(wins_server_subnet && subrec == wins_server_subnet)
+ return NULL;
+
+ if((subrec->next == NULL) && we_are_a_wins_client())
+ return unicast_subnet;
+ else
+ return subrec->next;
+}
diff --git a/source/nmbd/nmbd_synclists.c b/source/nmbd/nmbd_synclists.c
new file mode 100644
index 00000000000..86f1f760fd2
--- /dev/null
+++ b/source/nmbd/nmbd_synclists.c
@@ -0,0 +1,304 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/* this file handles asynchronous browse synchronisation requests. The
+ requests are done by forking and putting the result in a file in the
+ locks directory. We do it this way because we don't want nmbd to be
+ blocked waiting for some server to respond on a TCP connection. This
+ also allows us to have more than 1 sync going at once (tridge) */
+
+#include "includes.h"
+
+struct sync_record {
+ struct sync_record *next, *prev;
+ unstring workgroup;
+ unstring server;
+ pstring fname;
+ struct in_addr ip;
+ pid_t pid;
+};
+
+/* a linked list of current sync connections */
+static struct sync_record *syncs;
+
+static XFILE *fp;
+
+/*******************************************************************
+ This is the NetServerEnum callback.
+ Note sname and comment are in UNIX codepage format.
+ ******************************************************************/
+
+static void callback(const char *sname, uint32 stype,
+ const char *comment, void *state)
+{
+ x_fprintf(fp,"\"%s\" %08X \"%s\"\n", sname, stype, comment);
+}
+
+/*******************************************************************
+ Synchronise browse lists with another browse server.
+ Log in on the remote server's SMB port to their IPC$ service,
+ do a NetServerEnum and record the results in fname
+******************************************************************/
+
+static void sync_child(char *name, int nm_type,
+ char *workgroup,
+ struct in_addr ip, BOOL local, BOOL servers,
+ char *fname)
+{
+ extern fstring local_machine;
+ fstring unix_workgroup;
+ static struct cli_state cli;
+ uint32 local_type = local ? SV_TYPE_LOCAL_LIST_ONLY : 0;
+ struct nmb_name called, calling;
+
+ /* W2K DMB's return empty browse lists on port 445. Use 139.
+ * Patch from Andy Levine andyl@epicrealm.com.
+ */
+
+ if (!cli_initialise(&cli) || !cli_set_port(&cli, 139) || !cli_connect(&cli, name, &ip)) {
+ return;
+ }
+
+ make_nmb_name(&calling, local_machine, 0x0);
+ make_nmb_name(&called , name, nm_type);
+
+ if (!cli_session_request(&cli, &calling, &called)) {
+ cli_shutdown(&cli);
+ return;
+ }
+
+ if (!cli_negprot(&cli)) {
+ cli_shutdown(&cli);
+ return;
+ }
+
+ if (!cli_session_setup(&cli, "", "", 1, "", 0, workgroup)) {
+ cli_shutdown(&cli);
+ return;
+ }
+
+ if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) {
+ cli_shutdown(&cli);
+ return;
+ }
+
+ /* All the cli_XX functions take UNIX character set. */
+ fstrcpy(unix_workgroup, cli.server_domain?cli.server_domain:workgroup);
+
+ /* Fetch a workgroup list. */
+ cli_NetServerEnum(&cli, unix_workgroup,
+ local_type|SV_TYPE_DOMAIN_ENUM,
+ callback, NULL);
+
+ /* Now fetch a server list. */
+ if (servers) {
+ fstrcpy(unix_workgroup, workgroup);
+ cli_NetServerEnum(&cli, unix_workgroup,
+ local?SV_TYPE_LOCAL_LIST_ONLY:SV_TYPE_ALL,
+ callback, NULL);
+ }
+
+ cli_shutdown(&cli);
+}
+
+/*******************************************************************
+ initialise a browse sync with another browse server. Log in on the
+ remote server's SMB port to their IPC$ service, do a NetServerEnum
+ and record the results
+******************************************************************/
+
+void sync_browse_lists(struct work_record *work,
+ char *name, int nm_type,
+ struct in_addr ip, BOOL local, BOOL servers)
+{
+ struct sync_record *s;
+ static int counter;
+
+ START_PROFILE(sync_browse_lists);
+ /* Check we're not trying to sync with ourselves. This can
+ happen if we are a domain *and* a local master browser. */
+ if (ismyip(ip)) {
+done:
+ END_PROFILE(sync_browse_lists);
+ return;
+ }
+
+ s = (struct sync_record *)malloc(sizeof(*s));
+ if (!s) goto done;
+
+ ZERO_STRUCTP(s);
+
+ unstrcpy(s->workgroup, work->work_group);
+ unstrcpy(s->server, name);
+ s->ip = ip;
+
+ slprintf(s->fname, sizeof(pstring)-1,
+ "%s/sync.%d", lp_lockdir(), counter++);
+ all_string_sub(s->fname,"//", "/", 0);
+
+ DLIST_ADD(syncs, s);
+
+ /* the parent forks and returns, leaving the child to do the
+ actual sync and call END_PROFILE*/
+ CatchChild();
+ if ((s->pid = sys_fork())) return;
+
+ BlockSignals( False, SIGTERM );
+
+ DEBUG(2,("Initiating browse sync for %s to %s(%s)\n",
+ work->work_group, name, inet_ntoa(ip)));
+
+ fp = x_fopen(s->fname,O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ if (!fp) {
+ END_PROFILE(sync_browse_lists);
+ _exit(1);
+ }
+
+ sync_child(name, nm_type, work->work_group, ip, local, servers,
+ s->fname);
+
+ x_fclose(fp);
+ END_PROFILE(sync_browse_lists);
+ _exit(0);
+}
+
+/**********************************************************************
+ Handle one line from a completed sync file.
+ **********************************************************************/
+
+static void complete_one(struct sync_record *s,
+ char *sname, uint32 stype, char *comment)
+{
+ struct work_record *work;
+ struct server_record *servrec;
+
+ stype &= ~SV_TYPE_LOCAL_LIST_ONLY;
+
+ if (stype & SV_TYPE_DOMAIN_ENUM) {
+ /* See if we can find the workgroup on this subnet. */
+ if((work=find_workgroup_on_subnet(unicast_subnet, sname))) {
+ /* We already know about this workgroup -
+ update the ttl. */
+ update_workgroup_ttl(work,lp_max_ttl());
+ } else {
+ /* Create the workgroup on the subnet. */
+ work = create_workgroup_on_subnet(unicast_subnet,
+ sname, lp_max_ttl());
+ if (work) {
+ /* remember who the master is */
+ unstrcpy(work->local_master_browser_name, comment);
+ }
+ }
+ return;
+ }
+
+ work = find_workgroup_on_subnet(unicast_subnet, s->workgroup);
+ if (!work) {
+ DEBUG(3,("workgroup %s doesn't exist on unicast subnet?\n",
+ s->workgroup));
+ return;
+ }
+
+ if ((servrec = find_server_in_workgroup( work, sname))) {
+ /* Check that this is not a locally known
+ server - if so ignore the entry. */
+ if(!(servrec->serv.type & SV_TYPE_LOCAL_LIST_ONLY)) {
+ /* We already know about this server - update
+ the ttl. */
+ update_server_ttl(servrec, lp_max_ttl());
+ /* Update the type. */
+ servrec->serv.type = stype;
+ }
+ return;
+ }
+
+ /* Create the server in the workgroup. */
+ create_server_on_workgroup(work, sname,stype, lp_max_ttl(), comment);
+}
+
+/**********************************************************************
+ Read the completed sync info.
+**********************************************************************/
+
+static void complete_sync(struct sync_record *s)
+{
+ XFILE *f;
+ unstring server, type_str;
+ unsigned type;
+ pstring comment;
+ pstring line;
+ const char *ptr;
+ int count=0;
+
+ f = x_fopen(s->fname,O_RDONLY, 0);
+
+ if (!f)
+ return;
+
+ while (!x_feof(f)) {
+
+ if (!fgets_slash(line,sizeof(pstring),f))
+ continue;
+
+ ptr = line;
+
+ if (!next_token(&ptr,server,NULL,sizeof(server)) ||
+ !next_token(&ptr,type_str,NULL, sizeof(type_str)) ||
+ !next_token(&ptr,comment,NULL, sizeof(comment))) {
+ continue;
+ }
+
+ sscanf(type_str, "%X", &type);
+
+ complete_one(s, server, type, comment);
+
+ count++;
+ }
+
+ x_fclose(f);
+
+ unlink(s->fname);
+
+ DEBUG(2,("sync with %s(%s) for workgroup %s completed (%d records)\n",
+ s->server, inet_ntoa(s->ip), s->workgroup, count));
+}
+
+/**********************************************************************
+ Check for completion of any of the child processes.
+**********************************************************************/
+
+void sync_check_completion(void)
+{
+ struct sync_record *s, *next;
+
+ for (s=syncs;s;s=next) {
+ next = s->next;
+ if (!process_exists(s->pid)) {
+ /* it has completed - grab the info */
+ complete_sync(s);
+ DLIST_REMOVE(syncs, s);
+ ZERO_STRUCTP(s);
+ SAFE_FREE(s);
+ }
+ }
+}
diff --git a/source/nmbd/nmbd_winsproxy.c b/source/nmbd/nmbd_winsproxy.c
new file mode 100644
index 00000000000..cce168adb2f
--- /dev/null
+++ b/source/nmbd/nmbd_winsproxy.c
@@ -0,0 +1,213 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+
+ Copyright (C) Jeremy Allison 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+Function called when the name lookup succeeded.
+****************************************************************************/
+
+static void wins_proxy_name_query_request_success( struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname, struct in_addr ip, struct res_rec *rrec)
+{
+ unstring name;
+ struct packet_struct *original_packet;
+ struct subnet_record *orig_broadcast_subnet;
+ struct name_record *namerec;
+ uint16 nb_flags;
+ int num_ips;
+ int i;
+ int ttl = 3600; /* By default one hour in the cache. */
+ struct in_addr *iplist;
+
+ /* Extract the original packet and the original broadcast subnet from
+ the userdata. */
+
+ memcpy( (char *)&orig_broadcast_subnet, userdata->data, sizeof(struct subnet_record *) );
+ memcpy( (char *)&original_packet, &userdata->data[sizeof(struct subnet_record *)],
+ sizeof(struct packet_struct *) );
+
+ nb_flags = get_nb_flags( rrec->rdata );
+
+ num_ips = rrec->rdlength / 6;
+ if(num_ips == 0) {
+ DEBUG(0,("wins_proxy_name_query_request_success: Invalid number of IP records (0) \
+returned for name %s.\n", nmb_namestr(nmbname) ));
+ return;
+ }
+
+ if(num_ips == 1) {
+ iplist = &ip;
+ } else {
+ if((iplist = (struct in_addr *)malloc( num_ips * sizeof(struct in_addr) )) == NULL) {
+ DEBUG(0,("wins_proxy_name_query_request_success: malloc fail !\n"));
+ return;
+ }
+
+ for(i = 0; i < num_ips; i++)
+ putip( (char *)&iplist[i], (char *)&rrec->rdata[ (i*6) + 2]);
+ }
+
+ /* Add the queried name to the original subnet as a WINS_PROXY_NAME. */
+
+ if(rrec == PERMANENT_TTL)
+ ttl = lp_max_ttl();
+
+ pull_ascii_nstring(name, sizeof(name), nmbname->name);
+ namerec = add_name_to_subnet( orig_broadcast_subnet, name,
+ nmbname->name_type, nb_flags, ttl,
+ WINS_PROXY_NAME, num_ips, iplist );
+
+ if(iplist != &ip)
+ SAFE_FREE(iplist);
+
+ /*
+ * Check that none of the IP addresses we are returning is on the
+ * same broadcast subnet as the original requesting packet. If it
+ * is then don't reply (although we still need to add the name
+ * to the cache) as the actual machine will be replying also
+ * and we don't want two replies to a broadcast query.
+ */
+
+ if(namerec && original_packet->packet.nmb.header.nm_flags.bcast) {
+ for( i = 0; i < namerec->data.num_ips; i++) {
+ if( same_net( namerec->data.ip[i], orig_broadcast_subnet->myip,
+ orig_broadcast_subnet->mask_ip ) ) {
+ DEBUG( 5, ( "wins_proxy_name_query_request_success: name %s is a WINS \
+proxy name and is also on the same subnet (%s) as the requestor. \
+Not replying.\n", nmb_namestr(&namerec->name), orig_broadcast_subnet->subnet_name ) );
+ return;
+ }
+ }
+ }
+
+ /* Finally reply to the original name query. */
+ reply_netbios_packet(original_packet, /* Packet to reply to. */
+ 0, /* Result code. */
+ NMB_QUERY, /* nmbd type code. */
+ NMB_NAME_QUERY_OPCODE, /* opcode. */
+ ttl, /* ttl. */
+ rrec->rdata, /* data to send. */
+ rrec->rdlength); /* data length. */
+}
+
+/****************************************************************************
+Function called when the name lookup failed.
+****************************************************************************/
+
+static void wins_proxy_name_query_request_fail(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *question_name, int fail_code)
+{
+ DEBUG(4,("wins_proxy_name_query_request_fail: WINS server returned error code %d for lookup \
+of name %s.\n", fail_code, nmb_namestr(question_name) ));
+}
+
+/****************************************************************************
+Function to make a deep copy of the userdata we will need when the WINS
+proxy query returns.
+****************************************************************************/
+
+static struct userdata_struct *wins_proxy_userdata_copy_fn(struct userdata_struct *userdata)
+{
+ struct packet_struct *p, *copy_of_p;
+ struct userdata_struct *new_userdata = (struct userdata_struct *)malloc( userdata->userdata_len );
+
+ if(new_userdata == NULL)
+ return NULL;
+
+ new_userdata->copy_fn = userdata->copy_fn;
+ new_userdata->free_fn = userdata->free_fn;
+ new_userdata->userdata_len = userdata->userdata_len;
+
+ /* Copy the subnet_record pointer. */
+ memcpy( new_userdata->data, userdata->data, sizeof(struct subnet_record *) );
+
+ /* Extract the pointer to the packet struct */
+ memcpy((char *)&p, &userdata->data[sizeof(struct subnet_record *)], sizeof(struct packet_struct *) );
+
+ /* Do a deep copy of the packet. */
+ if((copy_of_p = copy_packet(p)) == NULL) {
+ SAFE_FREE(new_userdata);
+ return NULL;
+ }
+
+ /* Lock the copy. */
+ copy_of_p->locked = True;
+
+ memcpy( &new_userdata->data[sizeof(struct subnet_record *)], (char *)&copy_of_p,
+ sizeof(struct packet_struct *) );
+
+ return new_userdata;
+}
+
+/****************************************************************************
+Function to free the deep copy of the userdata we used when the WINS
+proxy query returned.
+****************************************************************************/
+
+static void wins_proxy_userdata_free_fn(struct userdata_struct *userdata)
+{
+ struct packet_struct *p;
+
+ /* Extract the pointer to the packet struct */
+ memcpy((char *)&p, &userdata->data[sizeof(struct subnet_record *)],
+ sizeof(struct packet_struct *));
+
+ /* Unlock the packet. */
+ p->locked = False;
+
+ free_packet(p);
+ ZERO_STRUCTP(userdata);
+ SAFE_FREE(userdata);
+}
+
+/****************************************************************************
+ Make a WINS query on behalf of a broadcast client name query request.
+****************************************************************************/
+
+void make_wins_proxy_name_query_request( struct subnet_record *subrec,
+ struct packet_struct *incoming_packet,
+ struct nmb_name *question_name)
+{
+ long *ud[(sizeof(struct userdata_struct) + sizeof(struct subrec *) +
+ sizeof(struct packet_struct *))/sizeof(long *) + 1];
+ struct userdata_struct *userdata = (struct userdata_struct *)ud;
+ unstring qname;
+
+ memset(ud, '\0', sizeof(ud));
+
+ userdata->copy_fn = wins_proxy_userdata_copy_fn;
+ userdata->free_fn = wins_proxy_userdata_free_fn;
+ userdata->userdata_len = sizeof(ud);
+ memcpy( userdata->data, (char *)&subrec, sizeof(struct subnet_record *));
+ memcpy( &userdata->data[sizeof(struct subnet_record *)], (char *)&incoming_packet,
+ sizeof(struct packet_struct *));
+
+ /* Now use the unicast subnet to query the name with the WINS server. */
+ pull_ascii_nstring(qname, sizeof(qname), question_name->name);
+ query_name( unicast_subnet, qname, question_name->name_type,
+ wins_proxy_name_query_request_success,
+ wins_proxy_name_query_request_fail,
+ userdata);
+}
diff --git a/source/nmbd/nmbd_winsserver.c b/source/nmbd/nmbd_winsserver.c
new file mode 100644
index 00000000000..0f0190adb61
--- /dev/null
+++ b/source/nmbd/nmbd_winsserver.c
@@ -0,0 +1,2015 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+
+ Copyright (C) Jeremy Allison 1994-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+
+#define WINS_LIST "wins.dat"
+#define WINS_VERSION 1
+
+/****************************************************************************
+ Change the wins owner address in the record.
+*****************************************************************************/
+
+static void update_wins_owner(struct name_record *namerec, struct in_addr wins_ip)
+{
+ if (namerec==NULL)
+ return;
+ namerec->data.wins_ip=wins_ip;
+}
+
+/****************************************************************************
+ Create the wins flags based on the nb flags and the input value.
+*****************************************************************************/
+
+static void update_wins_flag(struct name_record *namerec, int flags)
+{
+ if (namerec==NULL)
+ return;
+
+ namerec->data.wins_flags=0x0;
+
+ /* if it's a group, it can be a normal or a special one */
+ if (namerec->data.nb_flags & NB_GROUP) {
+ if (namerec->name.name_type==0x1C)
+ namerec->data.wins_flags|=WINS_SGROUP;
+ else
+ if (namerec->data.num_ips>1)
+ namerec->data.wins_flags|=WINS_SGROUP;
+ else
+ namerec->data.wins_flags|=WINS_NGROUP;
+ } else {
+ /* can be unique or multi-homed */
+ if (namerec->data.num_ips>1)
+ namerec->data.wins_flags|=WINS_MHOMED;
+ else
+ namerec->data.wins_flags|=WINS_UNIQUE;
+ }
+
+ /* the node type are the same bits */
+ namerec->data.wins_flags|=namerec->data.nb_flags&NB_NODETYPEMASK;
+
+ /* the static bit is elsewhere */
+ if (namerec->data.death_time == PERMANENT_TTL)
+ namerec->data.wins_flags|=WINS_STATIC;
+
+ /* and add the given bits */
+ namerec->data.wins_flags|=flags;
+
+ DEBUG(8,("update_wins_flag: nbflags: 0x%x, ttl: 0x%d, flags: 0x%x, winsflags: 0x%x\n",
+ namerec->data.nb_flags, (int)namerec->data.death_time, flags, namerec->data.wins_flags));
+}
+
+/****************************************************************************
+ Return the general ID value and increase it if requested.
+*****************************************************************************/
+
+static void get_global_id_and_update(SMB_BIG_UINT *current_id, BOOL update)
+{
+ /*
+ * it's kept as a static here, to prevent people from messing
+ * with the value directly
+ */
+
+ static SMB_BIG_UINT general_id = 1;
+
+ DEBUG(5,("get_global_id_and_update: updating version ID: %d\n", (int)general_id));
+
+ *current_id = general_id;
+
+ if (update)
+ general_id++;
+}
+
+/****************************************************************************
+ Possibly call the WINS hook external program when a WINS change is made.
+*****************************************************************************/
+
+static void wins_hook(const char *operation, struct name_record *namerec, int ttl)
+{
+ pstring command;
+ char *cmd = lp_wins_hook();
+ char *p, *namestr;
+ int i;
+
+ if (!cmd || !*cmd) return;
+
+ for (p=namerec->name.name; *p; p++) {
+ if (!(isalnum((int)*p) || strchr_m("._-",*p))) {
+ DEBUG(3,("not calling wins hook for invalid name %s\n", nmb_namestr(&namerec->name)));
+ return;
+ }
+ }
+
+ /* Use the name without the nametype (and scope) appended */
+
+ namestr = nmb_namestr(&namerec->name);
+ if ((p = strchr(namestr, '<')))
+ *p = 0;
+
+ p = command;
+ p += slprintf(p, sizeof(command)-1, "%s %s %s %02x %d",
+ cmd,
+ operation,
+ namestr,
+ namerec->name.name_type,
+ ttl);
+
+ for (i=0;i<namerec->data.num_ips;i++) {
+ p += slprintf(p, sizeof(command) - (p-command) -1, " %s", inet_ntoa(namerec->data.ip[i]));
+ }
+
+ DEBUG(3,("calling wins hook for %s\n", nmb_namestr(&namerec->name)));
+ smbrun(command, NULL);
+}
+
+
+/****************************************************************************
+Determine if this packet should be allocated to the WINS server.
+*****************************************************************************/
+
+BOOL packet_is_for_wins_server(struct packet_struct *packet)
+{
+ struct nmb_packet *nmb = &packet->packet.nmb;
+
+ /* Only unicast packets go to a WINS server. */
+ if((wins_server_subnet == NULL) || (nmb->header.nm_flags.bcast == True)) {
+ DEBUG(10, ("packet_is_for_wins_server: failing WINS test #1.\n"));
+ return False;
+ }
+
+ /* Check for node status requests. */
+ if (nmb->question.question_type != QUESTION_TYPE_NB_QUERY)
+ return False;
+
+ switch(nmb->header.opcode) {
+ /*
+ * A WINS server issues WACKS, not receives them.
+ */
+ case NMB_WACK_OPCODE:
+ DEBUG(10, ("packet_is_for_wins_server: failing WINS test #2 (WACK).\n"));
+ return False;
+ /*
+ * A WINS server only processes registration and
+ * release requests, not responses.
+ */
+ case NMB_NAME_REG_OPCODE:
+ case NMB_NAME_MULTIHOMED_REG_OPCODE:
+ case NMB_NAME_REFRESH_OPCODE_8: /* ambiguity in rfc1002 about which is correct. */
+ case NMB_NAME_REFRESH_OPCODE_9: /* WinNT uses 8 by default. */
+ if(nmb->header.response) {
+ DEBUG(10, ("packet_is_for_wins_server: failing WINS test #3 (response = 1).\n"));
+ return False;
+ }
+ break;
+
+ case NMB_NAME_RELEASE_OPCODE:
+ if(nmb->header.response) {
+ DEBUG(10, ("packet_is_for_wins_server: failing WINS test #4 (response = 1).\n"));
+ return False;
+ }
+ break;
+
+ /*
+ * Only process unicast name queries with rd = 1.
+ */
+ case NMB_NAME_QUERY_OPCODE:
+ if(!nmb->header.response && !nmb->header.nm_flags.recursion_desired) {
+ DEBUG(10, ("packet_is_for_wins_server: failing WINS test #5 (response = 1).\n"));
+ return False;
+ }
+ break;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+Utility function to decide what ttl to give a register/refresh request.
+*****************************************************************************/
+
+static int get_ttl_from_packet(struct nmb_packet *nmb)
+{
+ int ttl = nmb->additional->ttl;
+
+ if(ttl < lp_min_wins_ttl() )
+ ttl = lp_min_wins_ttl();
+
+ if(ttl > lp_max_wins_ttl() )
+ ttl = lp_max_wins_ttl();
+
+ return ttl;
+}
+
+/****************************************************************************
+Load or create the WINS database.
+*****************************************************************************/
+
+BOOL initialise_wins(void)
+{
+ time_t time_now = time(NULL);
+ XFILE *fp;
+ pstring line;
+
+ if(!lp_we_are_a_wins_server())
+ return True;
+
+ add_samba_names_to_subnet(wins_server_subnet);
+
+ if((fp = x_fopen(lock_path(WINS_LIST),O_RDONLY,0)) == NULL) {
+ DEBUG(2,("initialise_wins: Can't open wins database file %s. Error was %s\n",
+ WINS_LIST, strerror(errno) ));
+ return True;
+ }
+
+ while (!x_feof(fp)) {
+ pstring name_str, ip_str, ttl_str, nb_flags_str;
+ unsigned int num_ips;
+ pstring name;
+ struct in_addr *ip_list;
+ int type = 0;
+ int nb_flags;
+ int ttl;
+ const char *ptr;
+ char *p;
+ BOOL got_token;
+ BOOL was_ip;
+ int i;
+ unsigned int hash;
+ int version;
+
+ /* Read a line from the wins.dat file. Strips whitespace
+ from the beginning and end of the line. */
+ if (!fgets_slash(line,sizeof(pstring),fp))
+ continue;
+
+ if (*line == '#')
+ continue;
+
+ if (strncmp(line,"VERSION ", 8) == 0) {
+ if (sscanf(line,"VERSION %d %u", &version, &hash) != 2 ||
+ version != WINS_VERSION) {
+ DEBUG(0,("Discarding invalid wins.dat file [%s]\n",line));
+ x_fclose(fp);
+ return True;
+ }
+ continue;
+ }
+
+ ptr = line;
+
+ /*
+ * Now we handle multiple IP addresses per name we need
+ * to iterate over the line twice. The first time to
+ * determine how many IP addresses there are, the second
+ * time to actually parse them into the ip_list array.
+ */
+
+ if (!next_token(&ptr,name_str,NULL,sizeof(name_str))) {
+ DEBUG(0,("initialise_wins: Failed to parse name when parsing line %s\n", line ));
+ continue;
+ }
+
+ if (!next_token(&ptr,ttl_str,NULL,sizeof(ttl_str))) {
+ DEBUG(0,("initialise_wins: Failed to parse time to live when parsing line %s\n", line ));
+ continue;
+ }
+
+ /*
+ * Determine the number of IP addresses per line.
+ */
+ num_ips = 0;
+ do {
+ got_token = next_token(&ptr,ip_str,NULL,sizeof(ip_str));
+ was_ip = False;
+
+ if(got_token && strchr(ip_str, '.')) {
+ num_ips++;
+ was_ip = True;
+ }
+ } while( got_token && was_ip);
+
+ if(num_ips == 0) {
+ DEBUG(0,("initialise_wins: Missing IP address when parsing line %s\n", line ));
+ continue;
+ }
+
+ if(!got_token) {
+ DEBUG(0,("initialise_wins: Missing nb_flags when parsing line %s\n", line ));
+ continue;
+ }
+
+ /* Allocate the space for the ip_list. */
+ if((ip_list = (struct in_addr *)malloc( num_ips * sizeof(struct in_addr))) == NULL) {
+ DEBUG(0,("initialise_wins: Malloc fail !\n"));
+ return False;
+ }
+
+ /* Reset and re-parse the line. */
+ ptr = line;
+ next_token(&ptr,name_str,NULL,sizeof(name_str));
+ next_token(&ptr,ttl_str,NULL,sizeof(ttl_str));
+ for(i = 0; i < num_ips; i++) {
+ next_token(&ptr, ip_str, NULL, sizeof(ip_str));
+ ip_list[i] = *interpret_addr2(ip_str);
+ }
+ next_token(&ptr,nb_flags_str,NULL, sizeof(nb_flags_str));
+
+ /*
+ * Deal with SELF or REGISTER name encoding. Default is REGISTER
+ * for compatibility with old nmbds.
+ */
+
+ if(nb_flags_str[strlen(nb_flags_str)-1] == 'S') {
+ DEBUG(5,("initialise_wins: Ignoring SELF name %s\n", line));
+ SAFE_FREE(ip_list);
+ continue;
+ }
+
+ if(nb_flags_str[strlen(nb_flags_str)-1] == 'R')
+ nb_flags_str[strlen(nb_flags_str)-1] = '\0';
+
+ /* Netbios name. # divides the name from the type (hex): netbios#xx */
+ pstrcpy(name,name_str);
+
+ if((p = strchr(name,'#')) != NULL) {
+ *p = 0;
+ sscanf(p+1,"%x",&type);
+ }
+
+ /* Decode the netbios flags (hex) and the time-to-live (in seconds). */
+ sscanf(nb_flags_str,"%x",&nb_flags);
+ sscanf(ttl_str,"%d",&ttl);
+
+ /* add all entries that have 60 seconds or more to live */
+ if ((ttl - 60) > time_now || ttl == PERMANENT_TTL) {
+ if(ttl != PERMANENT_TTL)
+ ttl -= time_now;
+
+ DEBUG( 4, ("initialise_wins: add name: %s#%02x ttl = %d first IP %s flags = %2x\n",
+ name, type, ttl, inet_ntoa(ip_list[0]), nb_flags));
+
+ (void)add_name_to_subnet( wins_server_subnet, name, type, nb_flags,
+ ttl, REGISTER_NAME, num_ips, ip_list );
+ } else {
+ DEBUG(4, ("initialise_wins: not adding name (ttl problem) %s#%02x ttl = %d first IP %s flags = %2x\n",
+ name, type, ttl, inet_ntoa(ip_list[0]), nb_flags));
+ }
+
+ SAFE_FREE(ip_list);
+ }
+
+ x_fclose(fp);
+ return True;
+}
+
+/****************************************************************************
+Send a WINS WACK (Wait ACKnowledgement) response.
+**************************************************************************/
+
+static void send_wins_wack_response(int ttl, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ unsigned char rdata[2];
+
+ rdata[0] = rdata[1] = 0;
+
+ /* Taken from nmblib.c - we need to send back almost
+ identical bytes from the requesting packet header. */
+
+ rdata[0] = (nmb->header.opcode & 0xF) << 3;
+ if (nmb->header.nm_flags.authoritative && nmb->header.response)
+ rdata[0] |= 0x4;
+ if (nmb->header.nm_flags.trunc)
+ rdata[0] |= 0x2;
+ if (nmb->header.nm_flags.recursion_desired)
+ rdata[0] |= 0x1;
+ if (nmb->header.nm_flags.recursion_available && nmb->header.response)
+ rdata[1] |= 0x80;
+ if (nmb->header.nm_flags.bcast)
+ rdata[1] |= 0x10;
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ 0, /* Result code. */
+ NMB_WAIT_ACK, /* nmbd type code. */
+ NMB_WACK_OPCODE, /* opcode. */
+ ttl, /* ttl. */
+ (char *)rdata, /* data to send. */
+ 2); /* data length. */
+}
+
+/****************************************************************************
+Send a WINS name registration response.
+**************************************************************************/
+
+static void send_wins_name_registration_response(int rcode, int ttl, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ char rdata[6];
+
+ memcpy(&rdata[0], &nmb->additional->rdata[0], 6);
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ rcode, /* Result code. */
+ WINS_REG, /* nmbd type code. */
+ NMB_NAME_REG_OPCODE, /* opcode. */
+ ttl, /* ttl. */
+ rdata, /* data to send. */
+ 6); /* data length. */
+}
+
+/***********************************************************************
+ Deal with a name refresh request to a WINS server.
+************************************************************************/
+
+void wins_process_name_refresh_request( struct subnet_record *subrec,
+ struct packet_struct *p )
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+ BOOL group = (nb_flags & NB_GROUP) ? True : False;
+ struct name_record *namerec = NULL;
+ int ttl = get_ttl_from_packet(nmb);
+ struct in_addr from_ip;
+ struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
+
+ putip( (char *)&from_ip, &nmb->additional->rdata[2] );
+
+ if(bcast) {
+ /*
+ * We should only get unicast name refresh packets here.
+ * Anyone trying to refresh broadcast should not be going
+ * to a WINS server. Log an error here.
+ */
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "wins_process_name_refresh_request: " );
+ dbgtext( "Broadcast name refresh request received " );
+ dbgtext( "for name %s ", nmb_namestr(question) );
+ dbgtext( "from IP %s ", inet_ntoa(from_ip) );
+ dbgtext( "on subnet %s. ", subrec->subnet_name );
+ dbgtext( "Error - Broadcasts should not be sent " );
+ dbgtext( "to a WINS server\n" );
+ }
+ return;
+ }
+
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "wins_process_name_refresh_request: " );
+ dbgtext( "Name refresh for name %s IP %s\n",
+ nmb_namestr(question), inet_ntoa(from_ip) );
+ }
+
+ /*
+ * See if the name already exists.
+ * If not, handle it as a name registration and return.
+ */
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+ /*
+ * If this is a refresh request and the name doesn't exist then
+ * treat it like a registration request. This allows us to recover
+ * from errors (tridge)
+ */
+ if(namerec == NULL) {
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "wins_process_name_refresh_request: " );
+ dbgtext( "Name refresh for name %s ",
+ nmb_namestr( question ) );
+ dbgtext( "and the name does not exist. Treating " );
+ dbgtext( "as registration.\n" );
+ }
+ wins_process_name_registration_request(subrec,p);
+ return;
+ }
+
+ /*
+ * if the name is present but not active, simply remove it
+ * and treat the refresh request as a registration & return.
+ */
+ if (namerec != NULL && !WINS_STATE_ACTIVE(namerec)) {
+ if( DEBUGLVL( 5 ) ) {
+ dbgtext( "wins_process_name_refresh_request: " );
+ dbgtext( "Name (%s) in WINS ", nmb_namestr(question) );
+ dbgtext( "was not active - removing it.\n" );
+ }
+ remove_name_from_namelist( subrec, namerec );
+ namerec = NULL;
+ wins_process_name_registration_request( subrec, p );
+ return;
+ }
+
+ /*
+ * Check that the group bits for the refreshing name and the
+ * name in our database match. If not, refuse the refresh.
+ * [crh: Why RFS_ERR instead of ACT_ERR? Is this what MS does?]
+ */
+ if( (namerec != NULL) &&
+ ( (group && !NAME_GROUP(namerec))
+ || (!group && NAME_GROUP(namerec)) ) ) {
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "wins_process_name_refresh_request: " );
+ dbgtext( "Name %s ", nmb_namestr(question) );
+ dbgtext( "group bit = %s does not match ",
+ group ? "True" : "False" );
+ dbgtext( "group bit in WINS for this name.\n" );
+ }
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+
+ /*
+ * For a unique name check that the person refreshing the name is
+ * one of the registered IP addresses. If not - fail the refresh.
+ * Do the same for group names with a type of 0x1c.
+ * Just return success for unique 0x1d refreshes. For normal group
+ * names update the ttl and return success.
+ */
+ if( (!group || (group && (question->name_type == 0x1c)))
+ && find_ip_in_name_record(namerec, from_ip) ) {
+ /*
+ * Update the ttl.
+ */
+ update_name_ttl(namerec, ttl);
+
+ /*
+ * if the record is a replica:
+ * we take ownership and update the version ID.
+ */
+ if (!ip_equal(namerec->data.wins_ip, our_fake_ip)) {
+ update_wins_owner(namerec, our_fake_ip);
+ get_global_id_and_update(&namerec->data.id, True);
+ }
+
+ send_wins_name_registration_response(0, ttl, p);
+ wins_hook("refresh", namerec, ttl);
+ return;
+ } else if((group && (question->name_type == 0x1c))) {
+ /*
+ * Added by crh for bug #1079.
+ * Fix from Bert Driehuis
+ */
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "wins_process_name_refresh_request: " );
+ dbgtext( "Name refresh for name %s, ",
+ nmb_namestr(question) );
+ dbgtext( "but IP address %s ", inet_ntoa(from_ip) );
+ dbgtext( "is not yet associated with " );
+ dbgtext( "that name. Treating as registration.\n" );
+ }
+ wins_process_name_registration_request(subrec,p);
+ return;
+ } else if(group) {
+ /*
+ * Normal groups are all registered with an IP address of
+ * 255.255.255.255 so we can't search for the IP address.
+ */
+ update_name_ttl(namerec, ttl);
+ send_wins_name_registration_response(0, ttl, p);
+ return;
+ } else if(!group && (question->name_type == 0x1d)) {
+ /*
+ * Special name type - just pretend the refresh succeeded.
+ */
+ send_wins_name_registration_response(0, ttl, p);
+ return;
+ } else {
+ /*
+ * Fail the refresh.
+ */
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "wins_process_name_refresh_request: " );
+ dbgtext( "Name refresh for name %s with IP %s ",
+ nmb_namestr(question), inet_ntoa(from_ip) );
+ dbgtext( "and is IP is not known to the name.\n" );
+ }
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+}
+
+/***********************************************************************
+ Deal with a name registration request query success to a client that
+ owned the name.
+
+ We have a locked pointer to the original packet stashed away in the
+ userdata pointer. The success here is actually a failure as it means
+ the client we queried wants to keep the name, so we must return
+ a registration failure to the original requestor.
+************************************************************************/
+
+static void wins_register_query_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *question_name,
+ struct in_addr ip,
+ struct res_rec *answers)
+{
+ struct packet_struct *orig_reg_packet;
+
+ memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *));
+
+ DEBUG(3,("wins_register_query_success: Original client at IP %s still wants the \
+name %s. Rejecting registration request.\n", inet_ntoa(ip), nmb_namestr(question_name) ));
+
+ send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet);
+
+ orig_reg_packet->locked = False;
+ free_packet(orig_reg_packet);
+}
+
+/***********************************************************************
+ Deal with a name registration request query failure to a client that
+ owned the name.
+
+ We have a locked pointer to the original packet stashed away in the
+ userdata pointer. The failure here is actually a success as it means
+ the client we queried didn't want to keep the name, so we can remove
+ the old name record and then successfully add the new name.
+************************************************************************/
+
+static void wins_register_query_fail(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *question_name,
+ int rcode)
+{
+ struct userdata_struct *userdata = rrec->userdata;
+ struct packet_struct *orig_reg_packet;
+ struct name_record *namerec = NULL;
+
+ memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *));
+
+ /*
+ * We want to just add the name, as we now know the original owner
+ * didn't want it. But we can't just do that as an arbitary
+ * amount of time may have taken place between the name query
+ * request and this timeout/error response. So we check that
+ * the name still exists and is in the same state - if so
+ * we remove it and call wins_process_name_registration_request()
+ * as we know it will do the right thing now.
+ */
+
+ namerec = find_name_on_subnet(subrec, question_name, FIND_ANY_NAME);
+
+ if( (namerec != NULL) && (namerec->data.source == REGISTER_NAME) && ip_equal(rrec->packet->ip, *namerec->data.ip) ) {
+ remove_name_from_namelist( subrec, namerec);
+ namerec = NULL;
+ }
+
+ if(namerec == NULL)
+ wins_process_name_registration_request(subrec, orig_reg_packet);
+ else
+ DEBUG(2,("wins_register_query_fail: The state of the WINS database changed between \
+querying for name %s in order to replace it and this reply.\n", nmb_namestr(question_name) ));
+
+ orig_reg_packet->locked = False;
+ free_packet(orig_reg_packet);
+}
+
+/***********************************************************************
+ Deal with a name registration request to a WINS server.
+
+ Use the following pseudocode :
+
+ registering_group
+ |
+ |
+ +--------name exists
+ | |
+ | |
+ | +--- existing name is group
+ | | |
+ | | |
+ | | +--- add name (return).
+ | |
+ | |
+ | +--- exiting name is unique
+ | |
+ | |
+ | +--- query existing owner (return).
+ |
+ |
+ +--------name doesn't exist
+ |
+ |
+ +--- add name (return).
+
+ registering_unique
+ |
+ |
+ +--------name exists
+ | |
+ | |
+ | +--- existing name is group
+ | | |
+ | | |
+ | | +--- fail add (return).
+ | |
+ | |
+ | +--- exiting name is unique
+ | |
+ | |
+ | +--- query existing owner (return).
+ |
+ |
+ +--------name doesn't exist
+ |
+ |
+ +--- add name (return).
+
+ As can be seen from the above, the two cases may be collapsed onto each
+ other with the exception of the case where the name already exists and
+ is a group name. This case we handle with an if statement.
+
+************************************************************************/
+
+void wins_process_name_registration_request(struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ unstring name;
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+ int ttl = get_ttl_from_packet(nmb);
+ struct name_record *namerec = NULL;
+ struct in_addr from_ip;
+ BOOL registering_group_name = (nb_flags & NB_GROUP) ? True : False;
+ struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
+
+ putip((char *)&from_ip,&nmb->additional->rdata[2]);
+
+ if(bcast) {
+ /*
+ * We should only get unicast name registration packets here.
+ * Anyone trying to register broadcast should not be going to a WINS
+ * server. Log an error here.
+ */
+
+ DEBUG(0,("wins_process_name_registration_request: broadcast name registration request \
+received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n",
+ nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+ return;
+ }
+
+ DEBUG(3,("wins_process_name_registration_request: %s name registration for name %s \
+IP %s\n", registering_group_name ? "Group" : "Unique", nmb_namestr(question), inet_ntoa(from_ip) ));
+
+ /*
+ * See if the name already exists.
+ */
+
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+ /*
+ * if the record exists but NOT in active state,
+ * consider it dead.
+ */
+ if ( (namerec != NULL) && !WINS_STATE_ACTIVE(namerec)) {
+ DEBUG(5,("wins_process_name_registration_request: Name (%s) in WINS was \
+not active - removing it.\n", nmb_namestr(question) ));
+ remove_name_from_namelist( subrec, namerec );
+ namerec = NULL;
+ }
+
+ /*
+ * Deal with the case where the name found was a dns entry.
+ * Remove it as we now have a NetBIOS client registering the
+ * name.
+ */
+
+ if( (namerec != NULL) && ( (namerec->data.source == DNS_NAME) || (namerec->data.source == DNSFAIL_NAME) ) ) {
+ DEBUG(5,("wins_process_name_registration_request: Name (%s) in WINS was \
+a dns lookup - removing it.\n", nmb_namestr(question) ));
+ remove_name_from_namelist( subrec, namerec );
+ namerec = NULL;
+ }
+
+ /*
+ * Reject if the name exists and is not a REGISTER_NAME.
+ * (ie. Don't allow any static names to be overwritten.
+ */
+
+ if((namerec != NULL) && (namerec->data.source != REGISTER_NAME)) {
+ DEBUG( 3, ( "wins_process_name_registration_request: Attempt \
+to register name %s. Name already exists in WINS with source type %d.\n",
+ nmb_namestr(question), namerec->data.source ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+
+ /*
+ * Special policy decisions based on MS documentation.
+ * 1). All group names (except names ending in 0x1c) are added as 255.255.255.255.
+ * 2). All unique names ending in 0x1d are ignored, although a positive response is sent.
+ */
+
+ /*
+ * A group name is always added as the local broadcast address, except
+ * for group names ending in 0x1c.
+ * Group names with type 0x1c are registered with individual IP addresses.
+ */
+
+ if(registering_group_name && (question->name_type != 0x1c))
+ from_ip = *interpret_addr2("255.255.255.255");
+
+ /*
+ * Ignore all attempts to register a unique 0x1d name, although return success.
+ */
+
+ if(!registering_group_name && (question->name_type == 0x1d)) {
+ DEBUG(3,("wins_process_name_registration_request: Ignoring request \
+to register name %s from IP %s.\n", nmb_namestr(question), inet_ntoa(p->ip) ));
+ send_wins_name_registration_response(0, ttl, p);
+ return;
+ }
+
+ /*
+ * Next two cases are the 'if statement' mentioned above.
+ */
+
+ if((namerec != NULL) && NAME_GROUP(namerec)) {
+ if(registering_group_name) {
+ /*
+ * If we are adding a group name, the name exists and is also a group entry just add this
+ * IP address to it and update the ttl.
+ */
+
+ DEBUG(3,("wins_process_name_registration_request: Adding IP %s to group name %s.\n",
+ inet_ntoa(from_ip), nmb_namestr(question) ));
+
+ /*
+ * Check the ip address is not already in the group.
+ */
+
+ if(!find_ip_in_name_record(namerec, from_ip)) {
+ add_ip_to_name_record(namerec, from_ip);
+ /* we need to update the record for replication */
+ get_global_id_and_update(&namerec->data.id, True);
+
+ /*
+ * if the record is a replica, we must change
+ * the wins owner to us to make the replication updates
+ * it on the other wins servers.
+ * And when the partner will receive this record,
+ * it will update its own record.
+ */
+
+ update_wins_owner(namerec, our_fake_ip);
+ }
+ update_name_ttl(namerec, ttl);
+ send_wins_name_registration_response(0, ttl, p);
+ return;
+ } else {
+
+ /*
+ * If we are adding a unique name, the name exists in the WINS db
+ * and is a group name then reject the registration.
+ *
+ * explanation: groups have a higher priority than unique names.
+ */
+
+ DEBUG(3,("wins_process_name_registration_request: Attempt to register name %s. Name \
+already exists in WINS as a GROUP name.\n", nmb_namestr(question) ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+ }
+
+ /*
+ * From here on down we know that if the name exists in the WINS db it is
+ * a unique name, not a group name.
+ */
+
+ /*
+ * If the name exists and is one of our names then check the
+ * registering IP address. If it's not one of ours then automatically
+ * reject without doing the query - we know we will reject it.
+ */
+
+ if ( namerec != NULL )
+ pull_ascii_nstring(name, sizeof(name), namerec->name.name);
+
+ if( is_myname(name) ) {
+ if(!ismyip(from_ip)) {
+ DEBUG(3,("wins_process_name_registration_request: Attempt to register name %s. Name \
+is one of our (WINS server) names. Denying registration.\n", nmb_namestr(question) ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ } else {
+ /*
+ * It's one of our names and one of our IP's - update the ttl.
+ */
+ update_name_ttl(namerec, ttl);
+ send_wins_name_registration_response(0, ttl, p);
+ wins_hook("refresh", namerec, ttl);
+ return;
+ }
+ }
+
+ /*
+ * If the name exists and it is a unique registration and the registering IP
+ * is the same as the (single) already registered IP then just update the ttl.
+ *
+ * But not if the record is an active replica. IF it's a replica, it means it can be
+ * the same client which has moved and not yet expired. So we don't update
+ * the ttl in this case and go beyond to do a WACK and query the old client
+ */
+
+ if( !registering_group_name
+ && (namerec != NULL)
+ && (namerec->data.num_ips == 1)
+ && ip_equal( namerec->data.ip[0], from_ip )
+ && ip_equal(namerec->data.wins_ip, our_fake_ip) ) {
+ update_name_ttl( namerec, ttl );
+ send_wins_name_registration_response( 0, ttl, p );
+ wins_hook("refresh", namerec, ttl);
+ return;
+ }
+
+ /*
+ * Finally if the name exists do a query to the registering machine
+ * to see if they still claim to have the name.
+ */
+
+ if( namerec != NULL ) {
+ long *ud[(sizeof(struct userdata_struct) + sizeof(struct packet_struct *))/sizeof(long *) + 1];
+ struct userdata_struct *userdata = (struct userdata_struct *)ud;
+
+ /*
+ * First send a WACK to the registering machine.
+ */
+
+ send_wins_wack_response(60, p);
+
+ /*
+ * When the reply comes back we need the original packet.
+ * Lock this so it won't be freed and then put it into
+ * the userdata structure.
+ */
+
+ p->locked = True;
+
+ userdata = (struct userdata_struct *)ud;
+
+ userdata->copy_fn = NULL;
+ userdata->free_fn = NULL;
+ userdata->userdata_len = sizeof(struct packet_struct *);
+ memcpy(userdata->data, (char *)&p, sizeof(struct packet_struct *) );
+
+ /*
+ * Use the new call to send a query directly to an IP address.
+ * This sends the query directly to the IP address, and ensures
+ * the recursion desired flag is not set (you were right Luke :-).
+ * This function should *only* be called from the WINS server
+ * code. JRA.
+ */
+
+ pull_ascii_nstring(name, sizeof(name), question->name);
+ query_name_from_wins_server( *namerec->data.ip,
+ name,
+ question->name_type,
+ wins_register_query_success,
+ wins_register_query_fail,
+ userdata );
+ return;
+ }
+
+ /*
+ * Name did not exist - add it.
+ */
+
+ pull_ascii_nstring(name, sizeof(name), question->name);
+ add_name_to_subnet( subrec, name, question->name_type,
+ nb_flags, ttl, REGISTER_NAME, 1, &from_ip);
+
+ if ((namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME))) {
+ get_global_id_and_update(&namerec->data.id, True);
+ update_wins_owner(namerec, our_fake_ip);
+ update_wins_flag(namerec, WINS_ACTIVE);
+ wins_hook("add", namerec, ttl);
+ }
+
+ send_wins_name_registration_response(0, ttl, p);
+}
+
+/***********************************************************************
+ Deal with a mutihomed name query success to the machine that
+ requested the multihomed name registration.
+
+ We have a locked pointer to the original packet stashed away in the
+ userdata pointer.
+************************************************************************/
+
+static void wins_multihomed_register_query_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *question_name,
+ struct in_addr ip,
+ struct res_rec *answers)
+{
+ struct packet_struct *orig_reg_packet;
+ struct nmb_packet *nmb;
+ struct name_record *namerec = NULL;
+ struct in_addr from_ip;
+ int ttl;
+ struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
+
+ memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *));
+
+ nmb = &orig_reg_packet->packet.nmb;
+
+ putip((char *)&from_ip,&nmb->additional->rdata[2]);
+ ttl = get_ttl_from_packet(nmb);
+
+ /*
+ * We want to just add the new IP, as we now know the requesting
+ * machine claims to own it. But we can't just do that as an arbitary
+ * amount of time may have taken place between the name query
+ * request and this response. So we check that
+ * the name still exists and is in the same state - if so
+ * we just add the extra IP and update the ttl.
+ */
+
+ namerec = find_name_on_subnet(subrec, question_name, FIND_ANY_NAME);
+
+ if( (namerec == NULL) || (namerec->data.source != REGISTER_NAME) || !WINS_STATE_ACTIVE(namerec) ) {
+ DEBUG(3,("wins_multihomed_register_query_success: name %s is not in the correct state to add \
+a subsequent IP address.\n", nmb_namestr(question_name) ));
+ send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet);
+
+ orig_reg_packet->locked = False;
+ free_packet(orig_reg_packet);
+
+ return;
+ }
+
+ if(!find_ip_in_name_record(namerec, from_ip))
+ add_ip_to_name_record(namerec, from_ip);
+
+ get_global_id_and_update(&namerec->data.id, True);
+ update_wins_owner(namerec, our_fake_ip);
+ update_wins_flag(namerec, WINS_ACTIVE);
+ update_name_ttl(namerec, ttl);
+ send_wins_name_registration_response(0, ttl, orig_reg_packet);
+ wins_hook("add", namerec, ttl);
+
+ orig_reg_packet->locked = False;
+ free_packet(orig_reg_packet);
+}
+
+/***********************************************************************
+ Deal with a name registration request query failure to a client that
+ owned the name.
+
+ We have a locked pointer to the original packet stashed away in the
+ userdata pointer.
+************************************************************************/
+
+static void wins_multihomed_register_query_fail(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *question_name,
+ int rcode)
+{
+ struct userdata_struct *userdata = rrec->userdata;
+ struct packet_struct *orig_reg_packet;
+
+ memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *));
+
+ DEBUG(3,("wins_multihomed_register_query_fail: Registering machine at IP %s failed to answer \
+query successfully for name %s.\n", inet_ntoa(orig_reg_packet->ip), nmb_namestr(question_name) ));
+ send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet);
+
+ orig_reg_packet->locked = False;
+ free_packet(orig_reg_packet);
+ return;
+}
+
+/***********************************************************************
+ Deal with a multihomed name registration request to a WINS server.
+ These cannot be group name registrations.
+***********************************************************************/
+
+void wins_process_multihomed_name_registration_request( struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+ int ttl = get_ttl_from_packet(nmb);
+ struct name_record *namerec = NULL;
+ struct in_addr from_ip;
+ BOOL group = (nb_flags & NB_GROUP) ? True : False;
+ struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
+ unstring qname;
+
+ putip((char *)&from_ip,&nmb->additional->rdata[2]);
+
+ if(bcast) {
+ /*
+ * We should only get unicast name registration packets here.
+ * Anyone trying to register broadcast should not be going to a WINS
+ * server. Log an error here.
+ */
+
+ DEBUG(0,("wins_process_multihomed_name_registration_request: broadcast name registration request \
+received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n",
+ nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+ return;
+ }
+
+ /*
+ * Only unique names should be registered multihomed.
+ */
+
+ if(group) {
+ DEBUG(0,("wins_process_multihomed_name_registration_request: group name registration request \
+received for name %s from IP %s on subnet %s. Errror - group names should not be multihomed.\n",
+ nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+ return;
+ }
+
+ DEBUG(3,("wins_process_multihomed_name_registration_request: name registration for name %s \
+IP %s\n", nmb_namestr(question), inet_ntoa(from_ip) ));
+
+ /*
+ * Deal with policy regarding 0x1d names.
+ */
+
+ if(question->name_type == 0x1d) {
+ DEBUG(3,("wins_process_multihomed_name_registration_request: Ignoring request \
+to register name %s from IP %s.", nmb_namestr(question), inet_ntoa(p->ip) ));
+ send_wins_name_registration_response(0, ttl, p);
+ return;
+ }
+
+ /*
+ * See if the name already exists.
+ */
+
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+ /*
+ * if the record exists but NOT in active state,
+ * consider it dead.
+ */
+
+ if ((namerec != NULL) && !WINS_STATE_ACTIVE(namerec)) {
+ DEBUG(5,("wins_process_multihomed_name_registration_request: Name (%s) in WINS was not active - removing it.\n", nmb_namestr(question)));
+ remove_name_from_namelist(subrec, namerec);
+ namerec = NULL;
+ }
+
+ /*
+ * Deal with the case where the name found was a dns entry.
+ * Remove it as we now have a NetBIOS client registering the
+ * name.
+ */
+
+ if( (namerec != NULL) && ( (namerec->data.source == DNS_NAME) || (namerec->data.source == DNSFAIL_NAME) ) ) {
+ DEBUG(5,("wins_process_multihomed_name_registration_request: Name (%s) in WINS was a dns lookup \
+- removing it.\n", nmb_namestr(question) ));
+ remove_name_from_namelist( subrec, namerec);
+ namerec = NULL;
+ }
+
+ /*
+ * Reject if the name exists and is not a REGISTER_NAME.
+ * (ie. Don't allow any static names to be overwritten.
+ */
+
+ if( (namerec != NULL) && (namerec->data.source != REGISTER_NAME) ) {
+ DEBUG( 3, ( "wins_process_multihomed_name_registration_request: Attempt \
+to register name %s. Name already exists in WINS with source type %d.\n",
+ nmb_namestr(question), namerec->data.source ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+
+ /*
+ * Reject if the name exists and is a GROUP name and is active.
+ */
+
+ if((namerec != NULL) && NAME_GROUP(namerec) && WINS_STATE_ACTIVE(namerec)) {
+ DEBUG(3,("wins_process_multihomed_name_registration_request: Attempt to register name %s. Name \
+already exists in WINS as a GROUP name.\n", nmb_namestr(question) ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+
+ /*
+ * From here on down we know that if the name exists in the WINS db it is
+ * a unique name, not a group name.
+ */
+
+ /*
+ * If the name exists and is one of our names then check the
+ * registering IP address. If it's not one of ours then automatically
+ * reject without doing the query - we know we will reject it.
+ */
+
+ if((namerec != NULL) && (is_myname(namerec->name.name)) ) {
+ if(!ismyip(from_ip)) {
+ DEBUG(3,("wins_process_multihomed_name_registration_request: Attempt to register name %s. Name \
+is one of our (WINS server) names. Denying registration.\n", nmb_namestr(question) ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ } else {
+ /*
+ * It's one of our names and one of our IP's. Ensure the IP is in the record and
+ * update the ttl. Update the version ID to force replication.
+ */
+ if(!find_ip_in_name_record(namerec, from_ip)) {
+ get_global_id_and_update(&namerec->data.id, True);
+ update_wins_owner(namerec, our_fake_ip);
+ update_wins_flag(namerec, WINS_ACTIVE);
+
+ add_ip_to_name_record(namerec, from_ip);
+ wins_hook("add", namerec, ttl);
+ } else {
+ wins_hook("refresh", namerec, ttl);
+ }
+
+ update_name_ttl(namerec, ttl);
+ send_wins_name_registration_response(0, ttl, p);
+ return;
+ }
+ }
+
+ /*
+ * If the name exists and is active, check if the IP address is already registered
+ * to that name. If so then update the ttl and reply success.
+ */
+
+ if((namerec != NULL) && find_ip_in_name_record(namerec, from_ip) && WINS_STATE_ACTIVE(namerec)) {
+ update_name_ttl(namerec, ttl);
+
+ /*
+ * If it's a replica, we need to become the wins owner
+ * to force the replication
+ */
+ if (!ip_equal(namerec->data.wins_ip, our_fake_ip)) {
+ get_global_id_and_update(&namerec->data.id, True);
+ update_wins_owner(namerec, our_fake_ip);
+ update_wins_flag(namerec, WINS_ACTIVE);
+ }
+
+ send_wins_name_registration_response(0, ttl, p);
+ wins_hook("refresh", namerec, ttl);
+ return;
+ }
+
+ /*
+ * If the name exists do a query to the owner
+ * to see if they still want the name.
+ */
+
+ if(namerec != NULL) {
+ long *ud[(sizeof(struct userdata_struct) + sizeof(struct packet_struct *))/sizeof(long *) + 1];
+ struct userdata_struct *userdata = (struct userdata_struct *)ud;
+
+ /*
+ * First send a WACK to the registering machine.
+ */
+
+ send_wins_wack_response(60, p);
+
+ /*
+ * When the reply comes back we need the original packet.
+ * Lock this so it won't be freed and then put it into
+ * the userdata structure.
+ */
+
+ p->locked = True;
+
+ userdata = (struct userdata_struct *)ud;
+
+ userdata->copy_fn = NULL;
+ userdata->free_fn = NULL;
+ userdata->userdata_len = sizeof(struct packet_struct *);
+ memcpy(userdata->data, (char *)&p, sizeof(struct packet_struct *) );
+
+ /*
+ * Use the new call to send a query directly to an IP address.
+ * This sends the query directly to the IP address, and ensures
+ * the recursion desired flag is not set (you were right Luke :-).
+ * This function should *only* be called from the WINS server
+ * code. JRA.
+ *
+ * Note that this packet is sent to the current owner of the name,
+ * not the person who sent the packet
+ */
+
+ pull_ascii_nstring( qname, sizeof(qname), question->name);
+ query_name_from_wins_server( namerec->data.ip[0],
+ qname,
+ question->name_type,
+ wins_multihomed_register_query_success,
+ wins_multihomed_register_query_fail,
+ userdata );
+
+ return;
+ }
+
+ /*
+ * Name did not exist - add it.
+ */
+
+ pull_ascii_nstring( qname, sizeof(qname), question->name);
+ add_name_to_subnet( subrec, qname, question->name_type,
+ nb_flags, ttl, REGISTER_NAME, 1, &from_ip);
+
+ if ((namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME))) {
+ get_global_id_and_update(&namerec->data.id, True);
+ update_wins_owner(namerec, our_fake_ip);
+ update_wins_flag(namerec, WINS_ACTIVE);
+ wins_hook("add", namerec, ttl);
+ }
+
+ send_wins_name_registration_response(0, ttl, p);
+}
+
+/***********************************************************************
+ Deal with the special name query for *<1b>.
+***********************************************************************/
+
+static void process_wins_dmb_query_request(struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct name_record *namerec = NULL;
+ char *prdata;
+ int num_ips;
+
+ /*
+ * Go through all the ACTIVE names in the WINS db looking for those
+ * ending in <1b>. Use this to calculate the number of IP
+ * addresses we need to return.
+ */
+
+ num_ips = 0;
+ for( namerec = (struct name_record *)ubi_trFirst( subrec->namelist );
+ namerec; namerec = (struct name_record *)ubi_trNext( namerec ) ) {
+ if(WINS_STATE_ACTIVE(namerec) && namerec->name.name_type == 0x1b )
+ num_ips += namerec->data.num_ips;
+ }
+
+ if(num_ips == 0) {
+ /*
+ * There are no 0x1b names registered. Return name query fail.
+ */
+ send_wins_name_query_response(NAM_ERR, p, NULL);
+ return;
+ }
+
+ if((prdata = (char *)malloc( num_ips * 6 )) == NULL) {
+ DEBUG(0,("process_wins_dmb_query_request: Malloc fail !.\n"));
+ return;
+ }
+
+ /*
+ * Go through all the names again in the WINS db looking for those
+ * ending in <1b>. Add their IP addresses into the list we will
+ * return.
+ */
+
+ num_ips = 0;
+ for( namerec = (struct name_record *)ubi_trFirst( subrec->namelist );
+ namerec; namerec = (struct name_record *)ubi_trNext( namerec ) ) {
+ if(WINS_STATE_ACTIVE(namerec) && namerec->name.name_type == 0x1b) {
+ int i;
+ for(i = 0; i < namerec->data.num_ips; i++) {
+ set_nb_flags(&prdata[num_ips * 6],namerec->data.nb_flags);
+ putip((char *)&prdata[(num_ips * 6) + 2], &namerec->data.ip[i]);
+ num_ips++;
+ }
+ }
+ }
+
+ /*
+ * Send back the reply containing the IP list.
+ */
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ 0, /* Result code. */
+ WINS_QUERY, /* nmbd type code. */
+ NMB_NAME_QUERY_OPCODE, /* opcode. */
+ lp_min_wins_ttl(), /* ttl. */
+ prdata, /* data to send. */
+ num_ips*6); /* data length. */
+
+ SAFE_FREE(prdata);
+}
+
+/****************************************************************************
+Send a WINS name query response.
+**************************************************************************/
+
+void send_wins_name_query_response(int rcode, struct packet_struct *p,
+ struct name_record *namerec)
+{
+ char rdata[6];
+ char *prdata = rdata;
+ int reply_data_len = 0;
+ int ttl = 0;
+ int i;
+
+ memset(rdata,'\0',6);
+
+ if(rcode == 0) {
+ ttl = (namerec->data.death_time != PERMANENT_TTL) ? namerec->data.death_time - p->timestamp : lp_max_wins_ttl();
+
+ /* Copy all known ip addresses into the return data. */
+ /* Optimise for the common case of one IP address so we don't need a malloc. */
+
+ if( namerec->data.num_ips == 1 ) {
+ prdata = rdata;
+ } else {
+ if((prdata = (char *)malloc( namerec->data.num_ips * 6 )) == NULL) {
+ DEBUG(0,("send_wins_name_query_response: malloc fail !\n"));
+ return;
+ }
+ }
+
+ for(i = 0; i < namerec->data.num_ips; i++) {
+ set_nb_flags(&prdata[i*6],namerec->data.nb_flags);
+ putip((char *)&prdata[2+(i*6)], &namerec->data.ip[i]);
+ }
+
+ sort_query_replies(prdata, i, p->ip);
+ reply_data_len = namerec->data.num_ips * 6;
+ }
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ rcode, /* Result code. */
+ WINS_QUERY, /* nmbd type code. */
+ NMB_NAME_QUERY_OPCODE, /* opcode. */
+ ttl, /* ttl. */
+ prdata, /* data to send. */
+ reply_data_len); /* data length. */
+
+ if(prdata != rdata)
+ SAFE_FREE(prdata);
+}
+
+/***********************************************************************
+ Deal with a name query.
+***********************************************************************/
+
+void wins_process_name_query_request(struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ struct name_record *namerec = NULL;
+ unstring qname;
+
+ DEBUG(3,("wins_process_name_query: name query for name %s from IP %s\n",
+ nmb_namestr(question), inet_ntoa(p->ip) ));
+
+ /*
+ * Special name code. If the queried name is *<1b> then search
+ * the entire WINS database and return a list of all the IP addresses
+ * registered to any <1b> name. This is to allow domain master browsers
+ * to discover other domains that may not have a presence on their subnet.
+ */
+
+ pull_ascii_nstring(qname, sizeof(qname), question->name);
+ if(strequal( qname, "*") && (question->name_type == 0x1b)) {
+ process_wins_dmb_query_request( subrec, p);
+ return;
+ }
+
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+ if(namerec != NULL) {
+ /*
+ * If the name is not anymore in active state then reply not found.
+ * it's fair even if we keep it in the cache for days.
+ */
+ if (!WINS_STATE_ACTIVE(namerec)) {
+ DEBUG(3,("wins_process_name_query: name query for name %s - name expired. Returning fail.\n",
+ nmb_namestr(question) ));
+ send_wins_name_query_response(NAM_ERR, p, namerec);
+ return;
+ }
+
+ /*
+ * If it's a DNSFAIL_NAME then reply name not found.
+ */
+
+ if( namerec->data.source == DNSFAIL_NAME ) {
+ DEBUG(3,("wins_process_name_query: name query for name %s returning DNS fail.\n",
+ nmb_namestr(question) ));
+ send_wins_name_query_response(NAM_ERR, p, namerec);
+ return;
+ }
+
+ /*
+ * If the name has expired then reply name not found.
+ */
+
+ if( (namerec->data.death_time != PERMANENT_TTL) && (namerec->data.death_time < p->timestamp) ) {
+ DEBUG(3,("wins_process_name_query: name query for name %s - name expired. Returning fail.\n",
+ nmb_namestr(question) ));
+ send_wins_name_query_response(NAM_ERR, p, namerec);
+ return;
+ }
+
+ DEBUG(3,("wins_process_name_query: name query for name %s returning first IP %s.\n",
+ nmb_namestr(question), inet_ntoa(namerec->data.ip[0]) ));
+
+ send_wins_name_query_response(0, p, namerec);
+ return;
+ }
+
+ /*
+ * Name not found in WINS - try a dns query if it's a 0x20 name.
+ */
+
+ if(lp_dns_proxy() && ((question->name_type == 0x20) || question->name_type == 0)) {
+ DEBUG(3,("wins_process_name_query: name query for name %s not found - doing dns lookup.\n",
+ nmb_namestr(question) ));
+
+ queue_dns_query(p, question, &namerec);
+ return;
+ }
+
+ /*
+ * Name not found - return error.
+ */
+
+ send_wins_name_query_response(NAM_ERR, p, NULL);
+}
+
+/****************************************************************************
+Send a WINS name release response.
+**************************************************************************/
+
+static void send_wins_name_release_response(int rcode, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ char rdata[6];
+
+ memcpy(&rdata[0], &nmb->additional->rdata[0], 6);
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ rcode, /* Result code. */
+ NMB_REL, /* nmbd type code. */
+ NMB_NAME_RELEASE_OPCODE, /* opcode. */
+ 0, /* ttl. */
+ rdata, /* data to send. */
+ 6); /* data length. */
+}
+
+/***********************************************************************
+ Deal with a name release.
+***********************************************************************/
+
+void wins_process_name_release_request(struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+ struct name_record *namerec = NULL;
+ struct in_addr from_ip;
+ BOOL releasing_group_name = (nb_flags & NB_GROUP) ? True : False;;
+
+ putip((char *)&from_ip,&nmb->additional->rdata[2]);
+
+ if(bcast) {
+ /*
+ * We should only get unicast name registration packets here.
+ * Anyone trying to register broadcast should not be going to a WINS
+ * server. Log an error here.
+ */
+
+ DEBUG(0,("wins_process_name_release_request: broadcast name registration request \
+received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n",
+ nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+ return;
+ }
+
+ DEBUG(3,("wins_process_name_release_request: %s name release for name %s \
+IP %s\n", releasing_group_name ? "Group" : "Unique", nmb_namestr(question), inet_ntoa(from_ip) ));
+
+ /*
+ * Deal with policy regarding 0x1d names.
+ */
+
+ if(!releasing_group_name && (question->name_type == 0x1d)) {
+ DEBUG(3,("wins_process_name_release_request: Ignoring request \
+to release name %s from IP %s.", nmb_namestr(question), inet_ntoa(p->ip) ));
+ send_wins_name_release_response(0, p);
+ return;
+ }
+
+ /*
+ * See if the name already exists.
+ */
+
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+ if( (namerec == NULL) || ((namerec != NULL) && (namerec->data.source != REGISTER_NAME)) ) {
+ send_wins_name_release_response(NAM_ERR, p);
+ return;
+ }
+
+ /*
+ * Check that the sending machine has permission to release this name.
+ * If it's a group name not ending in 0x1c then just say yes and let
+ * the group time out.
+ */
+
+ if(releasing_group_name && (question->name_type != 0x1c)) {
+ send_wins_name_release_response(0, p);
+ return;
+ }
+
+ /*
+ * Check that the releasing node is on the list of IP addresses
+ * for this name. Disallow the release if not.
+ */
+
+ if(!find_ip_in_name_record(namerec, from_ip)) {
+ DEBUG(3,("wins_process_name_release_request: Refusing request to \
+release name %s as IP %s is not one of the known IP's for this name.\n",
+ nmb_namestr(question), inet_ntoa(from_ip) ));
+ send_wins_name_release_response(NAM_ERR, p);
+ return;
+ }
+
+ /*
+ * Check if the record is active. IF it's already released
+ * or tombstoned, refuse the release.
+ */
+
+ if (!WINS_STATE_ACTIVE(namerec)) {
+ DEBUG(3,("wins_process_name_release_request: Refusing request to \
+release name %s as this record is not active anymore.\n", nmb_namestr(question) ));
+ send_wins_name_release_response(NAM_ERR, p);
+ return;
+ }
+
+ /*
+ * Check if the record is a 0x1c group
+ * and has more then one ip
+ * remove only this address.
+ */
+
+ if(releasing_group_name && (question->name_type == 0x1c) && (namerec->data.num_ips > 1)) {
+ remove_ip_from_name_record(namerec, from_ip);
+ DEBUG(3,("wins_process_name_release_request: Remove IP %s from NAME: %s\n",
+ inet_ntoa(from_ip),nmb_namestr(question)));
+ send_wins_name_release_response(0, p);
+ return;
+ }
+
+ /*
+ * Send a release response.
+ * Flag the name as released and update the ttl
+ */
+
+ send_wins_name_release_response(0, p);
+
+ namerec->data.wins_flags |= WINS_RELEASED;
+ update_name_ttl(namerec, EXTINCTION_INTERVAL);
+
+ wins_hook("delete", namerec, 0);
+}
+
+/*******************************************************************
+ WINS time dependent processing.
+******************************************************************/
+
+void initiate_wins_processing(time_t t)
+{
+ static time_t lasttime = 0;
+ struct name_record *namerec;
+ struct name_record *next_namerec;
+ struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
+
+ if (!lasttime)
+ lasttime = t;
+ if (t - lasttime < 20)
+ return;
+
+ lasttime = t;
+
+ if(!lp_we_are_a_wins_server())
+ return;
+
+ for( namerec = (struct name_record *)ubi_trFirst( wins_server_subnet->namelist );
+ namerec;
+ namerec = next_namerec ) {
+ next_namerec = (struct name_record *)ubi_trNext( namerec );
+
+ if( (namerec->data.death_time != PERMANENT_TTL)
+ && (namerec->data.death_time < t) ) {
+
+ if( namerec->data.source == SELF_NAME ) {
+ DEBUG( 3, ( "expire_names_on_subnet: Subnet %s not expiring SELF name %s\n",
+ wins_server_subnet->subnet_name, nmb_namestr(&namerec->name) ) );
+ namerec->data.death_time += 300;
+ namerec->subnet->namelist_changed = True;
+ continue;
+ }
+
+ /* handle records, samba is the wins owner */
+ if (ip_equal(namerec->data.wins_ip, our_fake_ip)) {
+ switch (namerec->data.wins_flags | WINS_STATE_MASK) {
+ case WINS_ACTIVE:
+ namerec->data.wins_flags&=~WINS_STATE_MASK;
+ namerec->data.wins_flags|=WINS_RELEASED;
+ namerec->data.death_time = t + EXTINCTION_INTERVAL;
+ DEBUG(3,("initiate_wins_processing: expiring %s\n", nmb_namestr(&namerec->name)));
+ break;
+ case WINS_RELEASED:
+ namerec->data.wins_flags&=~WINS_STATE_MASK;
+ namerec->data.wins_flags|=WINS_TOMBSTONED;
+ namerec->data.death_time = t + EXTINCTION_TIMEOUT;
+ get_global_id_and_update(&namerec->data.id, True);
+ DEBUG(3,("initiate_wins_processing: tombstoning %s\n", nmb_namestr(&namerec->name)));
+ break;
+ case WINS_TOMBSTONED:
+ DEBUG(3,("initiate_wins_processing: deleting %s\n", nmb_namestr(&namerec->name)));
+ remove_name_from_namelist( wins_server_subnet, namerec );
+ break;
+ }
+ } else {
+ switch (namerec->data.wins_flags | WINS_STATE_MASK) {
+ case WINS_ACTIVE:
+ /* that's not as MS says it should be */
+ namerec->data.wins_flags&=~WINS_STATE_MASK;
+ namerec->data.wins_flags|=WINS_TOMBSTONED;
+ namerec->data.death_time = t + EXTINCTION_TIMEOUT;
+ DEBUG(3,("initiate_wins_processing: tombstoning %s\n", nmb_namestr(&namerec->name)));
+ case WINS_TOMBSTONED:
+ DEBUG(3,("initiate_wins_processing: deleting %s\n", nmb_namestr(&namerec->name)));
+ remove_name_from_namelist( wins_server_subnet, namerec );
+ break;
+ case WINS_RELEASED:
+ DEBUG(0,("initiate_wins_processing: %s is in released state and\
+we are not the wins owner !\n", nmb_namestr(&namerec->name)));
+ break;
+ }
+ }
+
+ }
+ }
+
+ if(wins_server_subnet->namelist_changed)
+ wins_write_database(True);
+
+ wins_server_subnet->namelist_changed = False;
+}
+
+/*******************************************************************
+ Write out the current WINS database.
+******************************************************************/
+
+void wins_write_database(BOOL background)
+{
+ struct name_record *namerec;
+ pstring fname, fnamenew;
+
+ XFILE *fp;
+
+ if(!lp_we_are_a_wins_server())
+ return;
+
+ /* We will do the writing in a child process to ensure that the parent doesn't block while this is done */
+ if (background) {
+ CatchChild();
+ if (sys_fork()) {
+ return;
+ }
+ }
+
+ slprintf(fname,sizeof(fname)-1,"%s/%s", lp_lockdir(), WINS_LIST);
+ all_string_sub(fname,"//", "/", 0);
+ slprintf(fnamenew,sizeof(fnamenew)-1,"%s.%u", fname, (unsigned int)sys_getpid());
+
+ if((fp = x_fopen(fnamenew,O_WRONLY|O_CREAT,0644)) == NULL) {
+ DEBUG(0,("wins_write_database: Can't open %s. Error was %s\n", fnamenew, strerror(errno)));
+ if (background) {
+ _exit(0);
+ }
+ return;
+ }
+
+ DEBUG(4,("wins_write_database: Dump of WINS name list.\n"));
+
+ x_fprintf(fp,"VERSION %d %u\n", WINS_VERSION, 0);
+
+ for( namerec = (struct name_record *)ubi_trFirst( wins_server_subnet->namelist ); namerec; namerec = (struct name_record *)ubi_trNext( namerec ) ) {
+ int i;
+ struct tm *tm;
+
+ DEBUGADD(4,("%-19s ", nmb_namestr(&namerec->name) ));
+
+ if( namerec->data.death_time != PERMANENT_TTL ) {
+ char *ts, *nl;
+
+ tm = LocalTime(&namerec->data.death_time);
+ ts = asctime(tm);
+ nl = strrchr( ts, '\n' );
+ if( NULL != nl )
+ *nl = '\0';
+ DEBUGADD(4,("TTL = %s ", ts ));
+ } else {
+ DEBUGADD(4,("TTL = PERMANENT "));
+ }
+
+ for (i = 0; i < namerec->data.num_ips; i++)
+ DEBUGADD(4,("%15s ", inet_ntoa(namerec->data.ip[i]) ));
+ DEBUGADD(4,("%2x\n", namerec->data.nb_flags ));
+
+ if( namerec->data.source == REGISTER_NAME ) {
+ unstring name;
+ pull_ascii_nstring(name, sizeof(name), namerec->name.name);
+ x_fprintf(fp, "\"%s#%02x\" %d ", name,namerec->name.name_type, /* Ignore scope. */
+ (int)namerec->data.death_time);
+
+ for (i = 0; i < namerec->data.num_ips; i++)
+ x_fprintf( fp, "%s ", inet_ntoa( namerec->data.ip[i] ) );
+ x_fprintf( fp, "%2xR\n", namerec->data.nb_flags );
+ }
+ }
+
+ x_fclose(fp);
+ chmod(fnamenew,0644);
+ unlink(fname);
+ rename(fnamenew,fname);
+ if (background) {
+ _exit(0);
+ }
+}
+
+/****************************************************************************
+ Process a internal Samba message receiving a wins record.
+***************************************************************************/
+
+void nmbd_wins_new_entry(int msg_type, pid_t src, void *buf, size_t len)
+{
+ WINS_RECORD *record;
+ struct name_record *namerec = NULL;
+ struct name_record *new_namerec = NULL;
+ struct nmb_name question;
+ BOOL overwrite=False;
+ struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
+ int i;
+
+ if (buf==NULL)
+ return;
+
+ /* Record should use UNIX codepage. Ensure this is so in the wrepld code. JRA. */
+ record=(WINS_RECORD *)buf;
+
+ make_nmb_name(&question, record->name, record->type);
+
+ namerec = find_name_on_subnet(wins_server_subnet, &question, FIND_ANY_NAME);
+
+ /* record doesn't exist, add it */
+ if (namerec == NULL) {
+ DEBUG(3,("nmbd_wins_new_entry: adding new replicated record: %s<%02x> for wins server: %s\n",
+ record->name, record->type, inet_ntoa(record->wins_ip)));
+
+ new_namerec=add_name_to_subnet( wins_server_subnet, record->name, record->type, record->nb_flags,
+ EXTINCTION_INTERVAL, REGISTER_NAME, record->num_ips, record->ip);
+ if (new_namerec!=NULL) {
+ update_wins_owner(new_namerec, record->wins_ip);
+ update_wins_flag(new_namerec, record->wins_flags);
+ new_namerec->data.id=record->id;
+
+ wins_server_subnet->namelist_changed = True;
+ }
+ }
+
+ /* check if we have a conflict */
+ if (namerec != NULL) {
+ /* both records are UNIQUE */
+ if (namerec->data.wins_flags&WINS_UNIQUE && record->wins_flags&WINS_UNIQUE) {
+
+ /* the database record is a replica */
+ if (!ip_equal(namerec->data.wins_ip, our_fake_ip)) {
+ if (namerec->data.wins_flags&WINS_ACTIVE && record->wins_flags&WINS_TOMBSTONED) {
+ if (ip_equal(namerec->data.wins_ip, record->wins_ip))
+ overwrite=True;
+ } else
+ overwrite=True;
+ } else {
+ /* we are the wins owner of the database record */
+ /* the 2 records have the same IP address */
+ if (ip_equal(namerec->data.ip[0], record->ip[0])) {
+ if (namerec->data.wins_flags&WINS_ACTIVE && record->wins_flags&WINS_TOMBSTONED)
+ get_global_id_and_update(&namerec->data.id, True);
+ else
+ overwrite=True;
+
+ } else {
+ /* the 2 records have different IP address */
+ if (namerec->data.wins_flags&WINS_ACTIVE) {
+ if (record->wins_flags&WINS_TOMBSTONED)
+ get_global_id_and_update(&namerec->data.id, True);
+ if (record->wins_flags&WINS_ACTIVE)
+ /* send conflict challenge to the replica node */
+ ;
+ } else
+ overwrite=True;
+ }
+
+ }
+ }
+
+ /* the replica is a standard group */
+ if (record->wins_flags&WINS_NGROUP || record->wins_flags&WINS_SGROUP) {
+ /* if the database record is unique and active force a name release */
+ if (namerec->data.wins_flags&WINS_UNIQUE)
+ /* send a release name to the unique node */
+ ;
+ overwrite=True;
+
+ }
+
+ /* the replica is a special group */
+ if (record->wins_flags&WINS_SGROUP && namerec->data.wins_flags&WINS_SGROUP) {
+ if (namerec->data.wins_flags&WINS_ACTIVE) {
+ for (i=0; i<record->num_ips; i++)
+ if(!find_ip_in_name_record(namerec, record->ip[i]))
+ add_ip_to_name_record(namerec, record->ip[i]);
+ } else {
+ overwrite=True;
+ }
+ }
+
+ /* the replica is a multihomed host */
+
+ /* I'm giving up on multi homed. Too much complex to understand */
+
+ if (record->wins_flags&WINS_MHOMED) {
+ if (! (namerec->data.wins_flags&WINS_ACTIVE)) {
+ if ( !(namerec->data.wins_flags&WINS_RELEASED) && !(namerec->data.wins_flags&WINS_NGROUP))
+ overwrite=True;
+ }
+ else {
+ if (ip_equal(record->wins_ip, namerec->data.wins_ip))
+ overwrite=True;
+
+ if (ip_equal(namerec->data.wins_ip, our_fake_ip))
+ if (namerec->data.wins_flags&WINS_UNIQUE)
+ get_global_id_and_update(&namerec->data.id, True);
+
+ }
+
+ if (record->wins_flags&WINS_ACTIVE && namerec->data.wins_flags&WINS_ACTIVE)
+ if (namerec->data.wins_flags&WINS_UNIQUE ||
+ namerec->data.wins_flags&WINS_MHOMED)
+ if (ip_equal(record->wins_ip, namerec->data.wins_ip))
+ overwrite=True;
+
+ }
+
+ if (overwrite == False)
+ DEBUG(3, ("nmbd_wins_new_entry: conflict in adding record: %s<%02x> from wins server: %s\n",
+ record->name, record->type, inet_ntoa(record->wins_ip)));
+ else {
+ DEBUG(3, ("nmbd_wins_new_entry: replacing record: %s<%02x> from wins server: %s\n",
+ record->name, record->type, inet_ntoa(record->wins_ip)));
+
+ /* remove the old record and add a new one */
+ remove_name_from_namelist( wins_server_subnet, namerec );
+ new_namerec=add_name_to_subnet( wins_server_subnet, record->name, record->type, record->nb_flags,
+ EXTINCTION_INTERVAL, REGISTER_NAME, record->num_ips, record->ip);
+ if (new_namerec!=NULL) {
+ update_wins_owner(new_namerec, record->wins_ip);
+ update_wins_flag(new_namerec, record->wins_flags);
+ new_namerec->data.id=record->id;
+
+ wins_server_subnet->namelist_changed = True;
+ }
+
+ wins_server_subnet->namelist_changed = True;
+ }
+
+ }
+}
diff --git a/source/nmbd/nmbd_workgroupdb.c b/source/nmbd/nmbd_workgroupdb.c
new file mode 100644
index 00000000000..8880cb58bb4
--- /dev/null
+++ b/source/nmbd/nmbd_workgroupdb.c
@@ -0,0 +1,319 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+
+extern int ClientNMB;
+
+extern uint16 samba_nb_type;
+
+int workgroup_count = 0; /* unique index key: one for each workgroup */
+
+/****************************************************************************
+ Add a workgroup into the list.
+**************************************************************************/
+
+static void add_workgroup(struct subnet_record *subrec, struct work_record *work)
+{
+ work->subnet = subrec;
+ DLIST_ADD(subrec->workgrouplist, work);
+ subrec->work_changed = True;
+}
+
+/****************************************************************************
+ Create an empty workgroup.
+**************************************************************************/
+
+static struct work_record *create_workgroup(const char *name, int ttl)
+{
+ struct work_record *work;
+ struct subnet_record *subrec;
+ nstring nname;
+
+ int t = -1;
+
+ if((work = (struct work_record *)malloc(sizeof(*work))) == NULL) {
+ DEBUG(0,("create_workgroup: malloc fail !\n"));
+ return NULL;
+ }
+ memset((char *)work, '\0', sizeof(*work));
+
+ errno = 0;
+ push_ascii_nstring(nname, name);
+ if (errno == E2BIG) {
+ unstring tname;
+ pull_ascii_nstring(tname, sizeof(tname), nname);
+ unstrcpy(work->work_group,tname);
+ DEBUG(0,("create_workgroup: workgroup name %s is too long. Truncating to %s\n",
+ name, tname));
+ } else {
+ unstrcpy(work->work_group,name);
+ }
+ work->serverlist = NULL;
+
+ work->RunningElection = False;
+ work->ElectionCount = 0;
+ work->announce_interval = 0;
+ work->needelection = False;
+ work->needannounce = True;
+ work->lastannounce_time = time(NULL);
+ work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE;
+ work->dom_state = DOMAIN_NONE;
+ work->log_state = LOGON_NONE;
+
+ work->death_time = (ttl != PERMANENT_TTL) ? time(NULL)+(ttl*3) : PERMANENT_TTL;
+
+ /* Make sure all token representations of workgroups are unique. */
+
+ for (subrec = FIRST_SUBNET; subrec && (t == -1); subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) {
+ struct work_record *w;
+ for (w = subrec->workgrouplist; w && t == -1; w = w->next) {
+ if (strequal(w->work_group, work->work_group))
+ t = w->token;
+ }
+ }
+
+ if (t == -1)
+ work->token = ++workgroup_count;
+ else
+ work->token = t;
+
+ /* No known local master browser as yet. */
+ *work->local_master_browser_name = '\0';
+
+ /* No known domain master browser as yet. */
+ *work->dmb_name.name = '\0';
+ zero_ip(&work->dmb_addr);
+
+ /* WfWg uses 01040b01 */
+ /* Win95 uses 01041501 */
+ /* NTAS uses ???????? */
+ work->ElectionCriterion = (MAINTAIN_LIST)|(BROWSER_ELECTION_VERSION<<8);
+ work->ElectionCriterion |= (lp_os_level() << 24);
+ if (lp_domain_master())
+ work->ElectionCriterion |= 0x80;
+
+ return work;
+}
+
+/*******************************************************************
+ Remove a workgroup.
+******************************************************************/
+
+static struct work_record *remove_workgroup_from_subnet(struct subnet_record *subrec,
+ struct work_record *work)
+{
+ struct work_record *ret_work = NULL;
+
+ DEBUG(3,("remove_workgroup: Removing workgroup %s\n", work->work_group));
+
+ ret_work = work->next;
+
+ remove_all_servers(work);
+
+ if (!work->serverlist) {
+ if (work->prev)
+ work->prev->next = work->next;
+ if (work->next)
+ work->next->prev = work->prev;
+
+ if (subrec->workgrouplist == work)
+ subrec->workgrouplist = work->next;
+
+ ZERO_STRUCTP(work);
+ SAFE_FREE(work);
+ }
+
+ subrec->work_changed = True;
+
+ return ret_work;
+}
+
+/****************************************************************************
+ Find a workgroup in the workgroup list of a subnet.
+**************************************************************************/
+
+struct work_record *find_workgroup_on_subnet(struct subnet_record *subrec,
+ const char *name)
+{
+ struct work_record *ret;
+
+ DEBUG(4, ("find_workgroup_on_subnet: workgroup search for %s on subnet %s: ",
+ name, subrec->subnet_name));
+
+ for (ret = subrec->workgrouplist; ret; ret = ret->next) {
+ if (strequal(ret->work_group,name)) {
+ DEBUGADD(4, ("found.\n"));
+ return(ret);
+ }
+ }
+ DEBUGADD(4, ("not found.\n"));
+ return NULL;
+}
+
+/****************************************************************************
+ Create a workgroup in the workgroup list of the subnet.
+**************************************************************************/
+
+struct work_record *create_workgroup_on_subnet(struct subnet_record *subrec,
+ const char *name, int ttl)
+{
+ struct work_record *work = NULL;
+
+ DEBUG(4,("create_workgroup_on_subnet: creating group %s on subnet %s\n",
+ name, subrec->subnet_name));
+
+ if ((work = create_workgroup(name, ttl))) {
+ add_workgroup(subrec, work);
+ subrec->work_changed = True;
+ return(work);
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Update a workgroup ttl.
+**************************************************************************/
+
+void update_workgroup_ttl(struct work_record *work, int ttl)
+{
+ if(work->death_time != PERMANENT_TTL)
+ work->death_time = time(NULL)+(ttl*3);
+ work->subnet->work_changed = True;
+}
+
+/****************************************************************************
+ Fail function called if we cannot register the WORKGROUP<0> and
+ WORKGROUP<1e> names on the net.
+**************************************************************************/
+
+static void fail_register(struct subnet_record *subrec, struct response_record *rrec,
+ struct nmb_name *nmbname)
+{
+ DEBUG(0,("fail_register: Failed to register name %s on subnet %s.\n",
+ nmb_namestr(nmbname), subrec->subnet_name));
+}
+
+/****************************************************************************
+ If the workgroup is our primary workgroup, add the required names to it.
+**************************************************************************/
+
+void initiate_myworkgroup_startup(struct subnet_record *subrec, struct work_record *work)
+{
+ int i;
+
+ if(!strequal(lp_workgroup(), work->work_group))
+ return;
+
+ /* If this is a broadcast subnet then start elections on it if we are so configured. */
+
+ if ((subrec != unicast_subnet) && (subrec != remote_broadcast_subnet) &&
+ (subrec != wins_server_subnet) && lp_preferred_master() && lp_local_master()) {
+ DEBUG(3, ("initiate_myworkgroup_startup: preferred master startup for \
+workgroup %s on subnet %s\n", work->work_group, subrec->subnet_name));
+ work->needelection = True;
+ work->ElectionCriterion |= (1<<3);
+ }
+
+ /* Register the WORKGROUP<0> and WORKGROUP<1e> names on the network. */
+
+ register_name(subrec,lp_workgroup(),0x0,samba_nb_type|NB_GROUP, NULL, fail_register,NULL);
+ register_name(subrec,lp_workgroup(),0x1e,samba_nb_type|NB_GROUP, NULL, fail_register,NULL);
+
+ for( i = 0; my_netbios_names(i); i++) {
+ const char *name = my_netbios_names(i);
+ int stype = lp_default_server_announce() | (lp_local_master() ? SV_TYPE_POTENTIAL_BROWSER : 0 );
+
+ if(!strequal(global_myname(), name))
+ stype &= ~(SV_TYPE_MASTER_BROWSER|SV_TYPE_POTENTIAL_BROWSER|SV_TYPE_DOMAIN_MASTER|SV_TYPE_DOMAIN_MEMBER);
+
+ create_server_on_workgroup(work,name,stype|SV_TYPE_LOCAL_LIST_ONLY, PERMANENT_TTL,
+ string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH));
+ DEBUG(3,("initiate_myworkgroup_startup: Added server name entry %s \
+on subnet %s\n", name, subrec->subnet_name));
+ }
+}
+
+/****************************************************************************
+ Dump a copy of the workgroup database into the log file.
+ **************************************************************************/
+
+void dump_workgroups(BOOL force_write)
+{
+ struct subnet_record *subrec;
+ int debuglevel = force_write ? 0 : 4;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) {
+ if (subrec->workgrouplist) {
+ struct work_record *work;
+
+ if( DEBUGLVL( debuglevel ) ) {
+ dbgtext( "dump_workgroups()\n " );
+ dbgtext( "dump workgroup on subnet %15s: ", subrec->subnet_name );
+ dbgtext( "netmask=%15s:\n", inet_ntoa(subrec->mask_ip) );
+ }
+
+ for (work = subrec->workgrouplist; work; work = work->next) {
+ DEBUGADD( debuglevel, ( "\t%s(%d) current master browser = %s\n", work->work_group,
+ work->token, *work->local_master_browser_name ? work->local_master_browser_name : "UNKNOWN" ) );
+ if (work->serverlist) {
+ struct server_record *servrec;
+ for (servrec = work->serverlist; servrec; servrec = servrec->next) {
+ DEBUGADD( debuglevel, ( "\t\t%s %8x (%s)\n",
+ servrec->serv.name,
+ servrec->serv.type,
+ servrec->serv.comment ) );
+ }
+ }
+ }
+ }
+ }
+}
+
+/****************************************************************************
+ Expire any dead servers on all workgroups. If the workgroup has expired
+ remove it.
+ **************************************************************************/
+
+void expire_workgroups_and_servers(time_t t)
+{
+ struct subnet_record *subrec;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) {
+ struct work_record *work;
+ struct work_record *nextwork;
+
+ for (work = subrec->workgrouplist; work; work = nextwork) {
+ nextwork = work->next;
+ expire_servers(work, t);
+
+ if ((work->serverlist == NULL) && (work->death_time != PERMANENT_TTL) &&
+ ((t == -1) || (work->death_time < t))) {
+ DEBUG(3,("expire_workgroups_and_servers: Removing timed out workgroup %s\n",
+ work->work_group));
+ remove_workgroup_from_subnet(subrec, work);
+ }
+ }
+ }
+}
diff --git a/source/nsswitch/.cvsignore b/source/nsswitch/.cvsignore
new file mode 100644
index 00000000000..658d50a680f
--- /dev/null
+++ b/source/nsswitch/.cvsignore
@@ -0,0 +1,4 @@
+*.po
+*.po32
+diffs
+winbindd_proto.h
diff --git a/source/nsswitch/pam_winbind.c b/source/nsswitch/pam_winbind.c
new file mode 100644
index 00000000000..123f6703665
--- /dev/null
+++ b/source/nsswitch/pam_winbind.c
@@ -0,0 +1,754 @@
+/* pam_winbind module
+
+ Copyright Andrew Tridgell <tridge@samba.org> 2000
+ Copyright Tim Potter <tpot@samba.org> 2000
+ Copyright Andrew Bartlett <abartlet@samba.org> 2002
+
+ largely based on pam_userdb by Christian Gafton <gafton@redhat.com>
+ also contains large slabs of code from pam_unix by Elliot Lee <sopwith@redhat.com>
+ (see copyright below for full details)
+*/
+
+#include "pam_winbind.h"
+
+/* data tokens */
+
+#define MAX_PASSWD_TRIES 3
+
+/* some syslogging */
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog(MODULE_NAME, LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+static int _pam_parse(int argc, const char **argv)
+{
+ int ctrl;
+ /* step through arguments */
+ for (ctrl = 0; argc-- > 0; ++argv) {
+
+ /* generic options */
+
+ if (!strcmp(*argv,"debug"))
+ ctrl |= WINBIND_DEBUG_ARG;
+ else if (!strcasecmp(*argv, "use_authtok"))
+ ctrl |= WINBIND_USE_AUTHTOK_ARG;
+ else if (!strcasecmp(*argv, "use_first_pass"))
+ ctrl |= WINBIND_USE_FIRST_PASS_ARG;
+ else if (!strcasecmp(*argv, "try_first_pass"))
+ ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
+ else if (!strcasecmp(*argv, "unknown_ok"))
+ ctrl |= WINBIND_UNKNOWN_OK_ARG;
+ else {
+ _pam_log(LOG_ERR, "pam_parse: unknown option; %s", *argv);
+ }
+ }
+
+ return ctrl;
+}
+
+/* --- authentication management functions --- */
+
+/* Attempt a conversation */
+
+static int converse(pam_handle_t *pamh, int nargs,
+ struct pam_message **message,
+ struct pam_response **response)
+{
+ int retval;
+ struct pam_conv *conv;
+
+ retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv ) ;
+ if (retval == PAM_SUCCESS) {
+ retval = conv->conv(nargs, (const struct pam_message **)message,
+ response, conv->appdata_ptr);
+ }
+
+ return retval; /* propagate error status */
+}
+
+
+static int _make_remark(pam_handle_t * pamh, int type, const char *text)
+{
+ int retval = PAM_SUCCESS;
+
+ struct pam_message *pmsg[1], msg[1];
+ struct pam_response *resp;
+
+ pmsg[0] = &msg[0];
+ msg[0].msg = text;
+ msg[0].msg_style = type;
+
+ resp = NULL;
+ retval = converse(pamh, 1, pmsg, &resp);
+
+ if (resp) {
+ _pam_drop_reply(resp, 1);
+ }
+ return retval;
+}
+
+static int pam_winbind_request(enum winbindd_cmd req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response)
+{
+
+ /* Fill in request and send down pipe */
+ init_request(request, req_type);
+
+ if (write_sock(request, sizeof(*request)) == -1) {
+ _pam_log(LOG_ERR, "write to socket failed!");
+ close_sock();
+ return PAM_SERVICE_ERR;
+ }
+
+ /* Wait for reply */
+ if (read_reply(response) == -1) {
+ _pam_log(LOG_ERR, "read from socket failed!");
+ close_sock();
+ return PAM_SERVICE_ERR;
+ }
+
+ /* We are done with the socket - close it and avoid mischeif */
+ close_sock();
+
+ /* Copy reply data from socket */
+ if (response->result != WINBINDD_OK) {
+ if (response->data.auth.pam_error != PAM_SUCCESS) {
+ _pam_log(LOG_ERR, "request failed: %s, PAM error was %d, NT error was %s",
+ response->data.auth.error_string,
+ response->data.auth.pam_error,
+ response->data.auth.nt_status_string);
+ return response->data.auth.pam_error;
+ } else {
+ _pam_log(LOG_ERR, "request failed, but PAM error 0!");
+ return PAM_SERVICE_ERR;
+ }
+ }
+
+ return PAM_SUCCESS;
+}
+
+static int pam_winbind_request_log(enum winbindd_cmd req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response,
+ int ctrl,
+ const char *user)
+{
+ int retval;
+
+ retval = pam_winbind_request(req_type, request, response);
+
+ switch (retval) {
+ case PAM_AUTH_ERR:
+ /* incorrect password */
+ _pam_log(LOG_WARNING, "user `%s' denied access (incorrect password)", user);
+ return retval;
+ case PAM_ACCT_EXPIRED:
+ /* account expired */
+ _pam_log(LOG_WARNING, "user `%s' account expired", user);
+ return retval;
+ case PAM_AUTHTOK_EXPIRED:
+ /* password expired */
+ _pam_log(LOG_WARNING, "user `%s' password expired", user);
+ return retval;
+ case PAM_NEW_AUTHTOK_REQD:
+ /* password expired */
+ _pam_log(LOG_WARNING, "user `%s' new password required", user);
+ return retval;
+ case PAM_USER_UNKNOWN:
+ /* the user does not exist */
+ if (ctrl & WINBIND_DEBUG_ARG)
+ _pam_log(LOG_NOTICE, "user `%s' not found",
+ user);
+ if (ctrl & WINBIND_UNKNOWN_OK_ARG) {
+ return PAM_IGNORE;
+ }
+ return retval;
+ case PAM_SUCCESS:
+ if (req_type == WINBINDD_PAM_AUTH) {
+ /* Otherwise, the authentication looked good */
+ _pam_log(LOG_NOTICE, "user '%s' granted acces", user);
+ } else if (req_type == WINBINDD_PAM_CHAUTHTOK) {
+ /* Otherwise, the authentication looked good */
+ _pam_log(LOG_NOTICE, "user '%s' password changed", user);
+ } else {
+ /* Otherwise, the authentication looked good */
+ _pam_log(LOG_NOTICE, "user '%s' OK", user);
+ }
+ return retval;
+ default:
+ /* we don't know anything about this return value */
+ _pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s'",
+ retval, user);
+ return retval;
+ }
+}
+
+/* talk to winbindd */
+static int winbind_auth_request(const char *user, const char *pass, int ctrl)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+
+ strncpy(request.data.auth.user, user,
+ sizeof(request.data.auth.user)-1);
+
+ strncpy(request.data.auth.pass, pass,
+ sizeof(request.data.auth.pass)-1);
+
+
+ return pam_winbind_request_log(WINBINDD_PAM_AUTH, &request, &response, ctrl, user);
+}
+
+/* talk to winbindd */
+static int winbind_chauthtok_request(const char *user, const char *oldpass,
+ const char *newpass, int ctrl)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+
+ if (request.data.chauthtok.user == NULL) return -2;
+
+ strncpy(request.data.chauthtok.user, user,
+ sizeof(request.data.chauthtok.user) - 1);
+
+ if (oldpass != NULL) {
+ strncpy(request.data.chauthtok.oldpass, oldpass,
+ sizeof(request.data.chauthtok.oldpass) - 1);
+ } else {
+ request.data.chauthtok.oldpass[0] = '\0';
+ }
+
+ if (newpass != NULL) {
+ strncpy(request.data.chauthtok.newpass, newpass,
+ sizeof(request.data.chauthtok.newpass) - 1);
+ } else {
+ request.data.chauthtok.newpass[0] = '\0';
+ }
+
+ return pam_winbind_request_log(WINBINDD_PAM_CHAUTHTOK, &request, &response, ctrl, user);
+}
+
+/*
+ * Checks if a user has an account
+ *
+ * return values:
+ * 1 = User not found
+ * 0 = OK
+ * -1 = System error
+ */
+static int valid_user(const char *user)
+{
+ if (getpwnam(user)) return 0;
+ return 1;
+}
+
+static char *_pam_delete(register char *xx)
+{
+ _pam_overwrite(xx);
+ _pam_drop(xx);
+ return NULL;
+}
+
+/*
+ * obtain a password from the user
+ */
+
+static int _winbind_read_password(pam_handle_t * pamh
+ ,unsigned int ctrl
+ ,const char *comment
+ ,const char *prompt1
+ ,const char *prompt2
+ ,const char **pass)
+{
+ int authtok_flag;
+ int retval;
+ const char *item;
+ char *token;
+
+ /*
+ * make sure nothing inappropriate gets returned
+ */
+
+ *pass = token = NULL;
+
+ /*
+ * which authentication token are we getting?
+ */
+
+ authtok_flag = on(WINBIND__OLD_PASSWORD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
+
+ /*
+ * should we obtain the password from a PAM item ?
+ */
+
+ if (on(WINBIND_TRY_FIRST_PASS_ARG, ctrl) || on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
+ retval = pam_get_item(pamh, authtok_flag, (const void **) &item);
+ if (retval != PAM_SUCCESS) {
+ /* very strange. */
+ _pam_log(LOG_ALERT,
+ "pam_get_item returned error to unix-read-password"
+ );
+ return retval;
+ } else if (item != NULL) { /* we have a password! */
+ *pass = item;
+ item = NULL;
+ return PAM_SUCCESS;
+ } else if (on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
+ return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
+ } else if (on(WINBIND_USE_AUTHTOK_ARG, ctrl)
+ && off(WINBIND__OLD_PASSWORD, ctrl)) {
+ return PAM_AUTHTOK_RECOVER_ERR;
+ }
+ }
+ /*
+ * getting here implies we will have to get the password from the
+ * user directly.
+ */
+
+ {
+ struct pam_message msg[3], *pmsg[3];
+ struct pam_response *resp;
+ int i, replies;
+
+ /* prepare to converse */
+
+ if (comment != NULL) {
+ pmsg[0] = &msg[0];
+ msg[0].msg_style = PAM_TEXT_INFO;
+ msg[0].msg = comment;
+ i = 1;
+ } else {
+ i = 0;
+ }
+
+ pmsg[i] = &msg[i];
+ msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[i++].msg = prompt1;
+ replies = 1;
+
+ if (prompt2 != NULL) {
+ pmsg[i] = &msg[i];
+ msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[i++].msg = prompt2;
+ ++replies;
+ }
+ /* so call the conversation expecting i responses */
+ resp = NULL;
+ retval = converse(pamh, i, pmsg, &resp);
+
+ if (resp != NULL) {
+
+ /* interpret the response */
+
+ if (retval == PAM_SUCCESS) { /* a good conversation */
+
+ token = x_strdup(resp[i - replies].resp);
+ if (token != NULL) {
+ if (replies == 2) {
+
+ /* verify that password entered correctly */
+ if (!resp[i - 1].resp
+ || strcmp(token, resp[i - 1].resp)) {
+ _pam_delete(token); /* mistyped */
+ retval = PAM_AUTHTOK_RECOVER_ERR;
+ _make_remark(pamh ,PAM_ERROR_MSG, MISTYPED_PASS);
+ }
+ }
+ } else {
+ _pam_log(LOG_NOTICE
+ ,"could not recover authentication token");
+ }
+
+ }
+ /*
+ * tidy up the conversation (resp_retcode) is ignored
+ * -- what is it for anyway? AGM
+ */
+
+ _pam_drop_reply(resp, i);
+
+ } else {
+ retval = (retval == PAM_SUCCESS)
+ ? PAM_AUTHTOK_RECOVER_ERR : retval;
+ }
+ }
+
+ if (retval != PAM_SUCCESS) {
+ if (on(WINBIND_DEBUG_ARG, ctrl))
+ _pam_log(LOG_DEBUG,
+ "unable to obtain a password");
+ return retval;
+ }
+ /* 'token' is the entered password */
+
+ /* we store this password as an item */
+
+ retval = pam_set_item(pamh, authtok_flag, token);
+ _pam_delete(token); /* clean it up */
+ if (retval != PAM_SUCCESS
+ || (retval = pam_get_item(pamh, authtok_flag
+ ,(const void **) &item))
+ != PAM_SUCCESS) {
+
+ _pam_log(LOG_CRIT, "error manipulating password");
+ return retval;
+
+ }
+
+ *pass = item;
+ item = NULL; /* break link to password */
+
+ return PAM_SUCCESS;
+}
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ const char *username;
+ const char *password;
+ int retval = PAM_AUTH_ERR;
+
+ /* parse arguments */
+ int ctrl = _pam_parse(argc, argv);
+
+ /* Get the username */
+ retval = pam_get_user(pamh, &username, NULL);
+ if ((retval != PAM_SUCCESS) || (!username)) {
+ if (ctrl & WINBIND_DEBUG_ARG)
+ _pam_log(LOG_DEBUG,"can not get the username");
+ return PAM_SERVICE_ERR;
+ }
+
+ retval = _winbind_read_password(pamh, ctrl, NULL,
+ "Password: ", NULL,
+ &password);
+
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_ERR, "Could not retrieve user's password");
+ return PAM_AUTHTOK_ERR;
+ }
+
+ if (ctrl & WINBIND_DEBUG_ARG) {
+
+ /* Let's not give too much away in the log file */
+
+#ifdef DEBUG_PASSWORD
+ _pam_log(LOG_INFO, "Verify user `%s' with password `%s'",
+ username, password);
+#else
+ _pam_log(LOG_INFO, "Verify user `%s'", username);
+#endif
+ }
+
+ /* Now use the username to look up password */
+ return winbind_auth_request(username, password, ctrl);
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+/*
+ * Account management. We want to verify that the account exists
+ * before returning PAM_SUCCESS
+ */
+PAM_EXTERN
+int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ const char *username;
+ int retval = PAM_USER_UNKNOWN;
+
+ /* parse arguments */
+ int ctrl = _pam_parse(argc, argv);
+
+ /* Get the username */
+ retval = pam_get_user(pamh, &username, NULL);
+ if ((retval != PAM_SUCCESS) || (!username)) {
+ if (ctrl & WINBIND_DEBUG_ARG)
+ _pam_log(LOG_DEBUG,"can not get the username");
+ return PAM_SERVICE_ERR;
+ }
+
+ /* Verify the username */
+ retval = valid_user(username);
+ switch (retval) {
+ case -1:
+ /* some sort of system error. The log was already printed */
+ return PAM_SERVICE_ERR;
+ case 1:
+ /* the user does not exist */
+ if (ctrl & WINBIND_DEBUG_ARG)
+ _pam_log(LOG_NOTICE, "user `%s' not found",
+ username);
+ if (ctrl & WINBIND_UNKNOWN_OK_ARG)
+ return PAM_IGNORE;
+ return PAM_USER_UNKNOWN;
+ case 0:
+ /* Otherwise, the authentication looked good */
+ _pam_log(LOG_NOTICE, "user '%s' granted acces", username);
+ return PAM_SUCCESS;
+ default:
+ /* we don't know anything about this return value */
+ _pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s'",
+ retval, username);
+ return PAM_SERVICE_ERR;
+ }
+
+ /* should not be reached */
+ return PAM_IGNORE;
+}
+PAM_EXTERN
+int pam_sm_open_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ /* parse arguments */
+ int ctrl = _pam_parse(argc, argv);
+ if (ctrl & WINBIND_DEBUG_ARG)
+ _pam_log(LOG_DEBUG,"libpam_winbind:pam_sm_open_session handler");
+ return PAM_SUCCESS;
+}
+PAM_EXTERN
+int pam_sm_close_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ /* parse arguments */
+ int ctrl = _pam_parse(argc, argv);
+ if (ctrl & WINBIND_DEBUG_ARG)
+ _pam_log(LOG_DEBUG,"libpam_winbind:pam_sm_close_session handler");
+ return PAM_SUCCESS;
+}
+
+
+
+PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
+ int argc, const char **argv)
+{
+ unsigned int lctrl;
+ int retval;
+ unsigned int ctrl = _pam_parse(argc, argv);
+
+ /* <DO NOT free() THESE> */
+ const char *user;
+ char *pass_old, *pass_new;
+ /* </DO NOT free() THESE> */
+
+ char *Announce;
+
+ int retry = 0;
+
+ /*
+ * First get the name of a user
+ */
+ retval = pam_get_user(pamh, &user, "Username: ");
+ if (retval == PAM_SUCCESS) {
+ if (user == NULL) {
+ _pam_log(LOG_ERR, "username was NULL!");
+ return PAM_USER_UNKNOWN;
+ }
+ if (retval == PAM_SUCCESS && on(WINBIND_DEBUG_ARG, ctrl))
+ _pam_log(LOG_DEBUG, "username [%s] obtained",
+ user);
+ } else {
+ if (on(WINBIND_DEBUG_ARG, ctrl))
+ _pam_log(LOG_DEBUG,
+ "password - could not identify user");
+ return retval;
+ }
+
+ /*
+ * obtain and verify the current password (OLDAUTHTOK) for
+ * the user.
+ */
+
+ if (flags & PAM_PRELIM_CHECK) {
+
+ /* instruct user what is happening */
+#define greeting "Changing password for "
+ Announce = (char *) malloc(sizeof(greeting) + strlen(user));
+ if (Announce == NULL) {
+ _pam_log(LOG_CRIT,
+ "password - out of memory");
+ return PAM_BUF_ERR;
+ }
+ (void) strcpy(Announce, greeting);
+ (void) strcpy(Announce + sizeof(greeting) - 1, user);
+#undef greeting
+
+ lctrl = ctrl | WINBIND__OLD_PASSWORD;
+ retval = _winbind_read_password(pamh, lctrl
+ ,Announce
+ ,"(current) NT password: "
+ ,NULL
+ ,(const char **) &pass_old);
+ free(Announce);
+
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_NOTICE
+ ,"password - (old) token not obtained");
+ return retval;
+ }
+ /* verify that this is the password for this user */
+
+ retval = winbind_auth_request(user, pass_old, ctrl);
+
+ if (retval != PAM_ACCT_EXPIRED
+ && retval != PAM_AUTHTOK_EXPIRED
+ && retval != PAM_NEW_AUTHTOK_REQD
+ && retval != PAM_SUCCESS) {
+ pass_old = NULL;
+ return retval;
+ }
+
+ retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
+ pass_old = NULL;
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_CRIT,
+ "failed to set PAM_OLDAUTHTOK");
+ }
+ } else if (flags & PAM_UPDATE_AUTHTOK) {
+
+ /*
+ * obtain the proposed password
+ */
+
+ /*
+ * get the old token back.
+ */
+
+ retval = pam_get_item(pamh, PAM_OLDAUTHTOK
+ ,(const void **) &pass_old);
+
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_NOTICE, "user not authenticated");
+ return retval;
+ }
+
+ lctrl = ctrl;
+
+ if (on(WINBIND_USE_AUTHTOK_ARG, lctrl)) {
+ ctrl = WINBIND_USE_FIRST_PASS_ARG | lctrl;
+ }
+ retry = 0;
+ retval = PAM_AUTHTOK_ERR;
+ while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
+ /*
+ * use_authtok is to force the use of a previously entered
+ * password -- needed for pluggable password strength checking
+ */
+
+ retval = _winbind_read_password(pamh, lctrl
+ ,NULL
+ ,"Enter new NT password: "
+ ,"Retype new NT password: "
+ ,(const char **) &pass_new);
+
+ if (retval != PAM_SUCCESS) {
+ if (on(WINBIND_DEBUG_ARG, ctrl)) {
+ _pam_log(LOG_ALERT
+ ,"password - new password not obtained");
+ }
+ pass_old = NULL;/* tidy up */
+ return retval;
+ }
+
+ /*
+ * At this point we know who the user is and what they
+ * propose as their new password. Verify that the new
+ * password is acceptable.
+ */
+
+ if (pass_new[0] == '\0') {/* "\0" password = NULL */
+ pass_new = NULL;
+ }
+ }
+
+ /*
+ * By reaching here we have approved the passwords and must now
+ * rebuild the password database file.
+ */
+
+ retval = winbind_chauthtok_request(user, pass_old, pass_new, ctrl);
+ _pam_overwrite(pass_new);
+ _pam_overwrite(pass_old);
+ pass_old = pass_new = NULL;
+ } else {
+ retval = PAM_SERVICE_ERR;
+ }
+
+ return retval;
+}
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_winbind_modstruct = {
+ MODULE_NAME,
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ pam_sm_acct_mgmt,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ pam_sm_chauthtok
+};
+
+#endif
+
+/*
+ * Copyright (c) Andrew Tridgell <tridge@samba.org> 2000
+ * Copyright (c) Tim Potter <tpot@samba.org> 2000
+ * Copyright (c) Andrew Bartlettt <abartlet@samba.org> 2002
+ * Copyright (c) Jan Rêkorajski 1999.
+ * Copyright (c) Andrew G. Morgan 1996-8.
+ * Copyright (c) Alex O. Yuriev, 1996.
+ * Copyright (c) Cristian Gafton 1996.
+ * Copyright (C) Elliot Lee <sopwith@redhat.com> 1996, Red Hat Software.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/source/nsswitch/pam_winbind.h b/source/nsswitch/pam_winbind.h
new file mode 100644
index 00000000000..0afcceb6aa2
--- /dev/null
+++ b/source/nsswitch/pam_winbind.h
@@ -0,0 +1,96 @@
+/* pam_winbind header file
+ (Solaris needs some macros from Linux for common PAM code)
+
+ Shirish Kalele 2000
+*/
+
+#ifdef HAVE_FEATURES_H
+#include <features.h>
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <config.h>
+
+#define MODULE_NAME "pam_winbind"
+#define PAM_SM_AUTH
+#define PAM_SM_ACCOUNT
+#define PAM_SM_PASSWORD
+
+#if defined(SUNOS5) || defined(SUNOS4) || defined(HPUX) || defined(FREEBSD)
+
+/* Solaris always uses dynamic pam modules */
+#define PAM_EXTERN extern
+#include <security/pam_appl.h>
+
+#ifndef PAM_AUTHTOK_RECOVER_ERR
+#define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_RECOVERY_ERR
+#endif
+
+#endif
+
+#ifdef HAVE_SECURITY_PAM_MODULES_H
+#include <security/pam_modules.h>
+#endif
+
+#ifdef HAVE_SECURITY__PAM_MACROS_H
+#include <security/_pam_macros.h>
+#else
+/* Define required macros from (Linux PAM 0.68) security/_pam_macros.h */
+#define _pam_drop_reply(/* struct pam_response * */ reply, /* int */ replies) \
+do { \
+ int reply_i; \
+ \
+ for (reply_i=0; reply_i<replies; ++reply_i) { \
+ if (reply[reply_i].resp) { \
+ _pam_overwrite(reply[reply_i].resp); \
+ free(reply[reply_i].resp); \
+ } \
+ } \
+ if (reply) \
+ free(reply); \
+} while (0)
+
+#define _pam_overwrite(x) \
+do { \
+ register char *__xx__; \
+ if ((__xx__=(x))) \
+ while (*__xx__) \
+ *__xx__++ = '\0'; \
+} while (0)
+
+/*
+ * Don't just free it, forget it too.
+ */
+
+#define _pam_drop(X) SAFE_FREE(X)
+
+#define x_strdup(s) ( (s) ? strdup(s):NULL )
+#endif
+
+#define WINBIND_DEBUG_ARG (1<<0)
+#define WINBIND_USE_AUTHTOK_ARG (1<<1)
+#define WINBIND_UNKNOWN_OK_ARG (1<<2)
+#define WINBIND_TRY_FIRST_PASS_ARG (1<<3)
+#define WINBIND_USE_FIRST_PASS_ARG (1<<4)
+#define WINBIND__OLD_PASSWORD (1<<5)
+
+/*
+ * here is the string to inform the user that the new passwords they
+ * typed were not the same.
+ */
+
+#define MISTYPED_PASS "Sorry, passwords do not match"
+
+#define on(x, y) (x & y)
+#define off(x, y) (!(x & y))
+
+#include "winbind_client.h"
diff --git a/source/nsswitch/wb_client.c b/source/nsswitch/wb_client.c
new file mode 100644
index 00000000000..32dfc8decac
--- /dev/null
+++ b/source/nsswitch/wb_client.c
@@ -0,0 +1,693 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ winbind client code
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Andrew Tridgell 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "includes.h"
+#include "nsswitch/winbind_nss.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+extern DOM_SID global_sid_NULL; /* NULL sid */
+
+NSS_STATUS winbindd_request(int req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response);
+
+/* Call winbindd to convert a name to a sid */
+
+BOOL winbind_lookup_name(const char *dom_name, const char *name, DOM_SID *sid,
+ enum SID_NAME_USE *name_type)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ if (!sid || !name_type)
+ return False;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.data.name.dom_name, dom_name);
+ fstrcpy(request.data.name.name, name);
+
+ if ((result = winbindd_request(WINBINDD_LOOKUPNAME, &request,
+ &response)) == NSS_STATUS_SUCCESS) {
+ if (!string_to_sid(sid, response.data.sid.sid))
+ return False;
+ *name_type = (enum SID_NAME_USE)response.data.sid.type;
+ }
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/* Call winbindd to convert sid to name */
+
+BOOL winbind_lookup_sid(const DOM_SID *sid,
+ fstring dom_name, fstring name,
+ enum SID_NAME_USE *name_type)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ fstring sid_str;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ sid_to_string(sid_str, sid);
+ fstrcpy(request.data.sid, sid_str);
+
+ /* Make request */
+
+ result = winbindd_request(WINBINDD_LOOKUPSID, &request, &response);
+
+ /* Copy out result */
+
+ if (result == NSS_STATUS_SUCCESS) {
+ fstrcpy(dom_name, response.data.name.dom_name);
+ fstrcpy(name, response.data.name.name);
+ *name_type = (enum SID_NAME_USE)response.data.name.type;
+
+ DEBUG(10, ("winbind_lookup_sid: SUCCESS: SID %s -> %s %s\n",
+ sid_str, dom_name, name));
+ }
+
+ return (result == NSS_STATUS_SUCCESS);
+}
+
+/* Call winbindd to convert SID to uid */
+
+BOOL winbind_sid_to_uid(uid_t *puid, const DOM_SID *sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int result;
+ fstring sid_str;
+
+ if (!puid)
+ return False;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ sid_to_string(sid_str, sid);
+ fstrcpy(request.data.sid, sid_str);
+
+ /* Make request */
+
+ result = winbindd_request(WINBINDD_SID_TO_UID, &request, &response);
+
+ /* Copy out result */
+
+ if (result == NSS_STATUS_SUCCESS) {
+ *puid = response.data.uid;
+ }
+
+ return (result == NSS_STATUS_SUCCESS);
+}
+
+/* Call winbindd to convert uid to sid */
+
+BOOL winbind_uid_to_sid(DOM_SID *sid, uid_t uid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int result;
+
+ if (!sid)
+ return False;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.data.uid = uid;
+
+ /* Make request */
+
+ result = winbindd_request(WINBINDD_UID_TO_SID, &request, &response);
+
+ /* Copy out result */
+
+ if (result == NSS_STATUS_SUCCESS) {
+ if (!string_to_sid(sid, response.data.sid.sid))
+ return False;
+ } else {
+ sid_copy(sid, &global_sid_NULL);
+ }
+
+ return (result == NSS_STATUS_SUCCESS);
+}
+
+/* Call winbindd to convert SID to gid */
+
+BOOL winbind_sid_to_gid(gid_t *pgid, const DOM_SID *sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int result;
+ fstring sid_str;
+
+ if (!pgid)
+ return False;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ sid_to_string(sid_str, sid);
+ fstrcpy(request.data.sid, sid_str);
+
+ /* Make request */
+
+ result = winbindd_request(WINBINDD_SID_TO_GID, &request, &response);
+
+ /* Copy out result */
+
+ if (result == NSS_STATUS_SUCCESS) {
+ *pgid = response.data.gid;
+ }
+
+ return (result == NSS_STATUS_SUCCESS);
+}
+
+/* Call winbindd to convert gid to sid */
+
+BOOL winbind_gid_to_sid(DOM_SID *sid, gid_t gid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int result;
+
+ if (!sid)
+ return False;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.data.gid = gid;
+
+ /* Make request */
+
+ result = winbindd_request(WINBINDD_GID_TO_SID, &request, &response);
+
+ /* Copy out result */
+
+ if (result == NSS_STATUS_SUCCESS) {
+ if (!string_to_sid(sid, response.data.sid.sid))
+ return False;
+ } else {
+ sid_copy(sid, &global_sid_NULL);
+ }
+
+ return (result == NSS_STATUS_SUCCESS);
+}
+
+BOOL winbind_allocate_rid(uint32 *rid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int result;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Make request */
+
+ result = winbindd_request(WINBINDD_ALLOCATE_RID, &request, &response);
+
+ if (result != NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Copy out result */
+ *rid = response.data.rid;
+
+ return True;
+}
+
+/* Fetch the list of groups a user is a member of from winbindd. This is
+ used by winbind_getgroups. */
+
+static int wb_getgroups(const char *user, gid_t **groups)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int result;
+
+ /* Call winbindd */
+
+ fstrcpy(request.data.username, user);
+
+ ZERO_STRUCT(response);
+
+ result = winbindd_request(WINBINDD_GETGROUPS, &request, &response);
+
+ if (result == NSS_STATUS_SUCCESS) {
+
+ /* Return group list. Don't forget to free the group list
+ when finished. */
+
+ *groups = (gid_t *)response.extra_data;
+ return response.data.num_entries;
+ }
+
+ return -1;
+}
+
+/* Call winbindd to initialise group membership. This is necessary for
+ some systems (i.e RH5.2) that do not have an initgroups function as part
+ of the nss extension. In RH5.2 this is implemented using getgrent()
+ which can be amazingly inefficient as well as having problems with
+ username case. */
+
+int winbind_initgroups(char *user, gid_t gid)
+{
+ gid_t *tgr, *groups = NULL;
+ int result;
+
+ /* Call normal initgroups if we are a local user */
+
+ if (!strchr(user, *lp_winbind_separator())) {
+ return initgroups(user, gid);
+ }
+
+ result = wb_getgroups(user, &groups);
+
+ DEBUG(10,("winbind_getgroups: %s: result = %s\n", user,
+ result == -1 ? "FAIL" : "SUCCESS"));
+
+ if (result != -1) {
+ int ngroups = result, i;
+ BOOL is_member = False;
+
+ /* Check to see if the passed gid is already in the list */
+
+ for (i = 0; i < ngroups; i++) {
+ if (groups[i] == gid) {
+ is_member = True;
+ }
+ }
+
+ /* Add group to list if necessary */
+
+ if (!is_member) {
+ tgr = (gid_t *)Realloc(groups, sizeof(gid_t) * ngroups + 1);
+
+ if (!tgr) {
+ errno = ENOMEM;
+ result = -1;
+ goto done;
+ }
+ else groups = tgr;
+
+ groups[ngroups] = gid;
+ ngroups++;
+ }
+
+ /* Set the groups */
+
+ if (sys_setgroups(ngroups, groups) == -1) {
+ errno = EPERM;
+ result = -1;
+ goto done;
+ }
+
+ } else {
+
+ /* The call failed. Set errno to something so we don't get
+ a bogus value from the last failed system call. */
+
+ errno = EIO;
+ }
+
+ /* Free response data if necessary */
+
+ done:
+ SAFE_FREE(groups);
+
+ return result;
+}
+
+/* Return a list of groups the user is a member of. This function is
+ useful for large systems where inverting the group database would be too
+ time consuming. If size is zero, list is not modified and the total
+ number of groups for the user is returned. */
+
+int winbind_getgroups(const char *user, gid_t **list)
+{
+ /*
+ * Don't do the lookup if the name has no separator _and_ we are not in
+ * 'winbind use default domain' mode.
+ */
+
+ if (!(strchr(user, *lp_winbind_separator()) || lp_winbind_use_default_domain()))
+ return -1;
+
+ /* Fetch list of groups */
+
+ return wb_getgroups(user, list);
+}
+
+/**********************************************************************
+ simple wrapper function to see if winbindd is alive
+**********************************************************************/
+
+BOOL winbind_ping( void )
+{
+ NSS_STATUS result;
+
+ result = winbindd_request(WINBINDD_PING, NULL, NULL);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/**********************************************************************
+ Ask winbindd to create a local user
+**********************************************************************/
+
+BOOL winbind_create_user( const char *name, uint32 *rid )
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ if ( !lp_winbind_enable_local_accounts() )
+ return False;
+
+ if ( !name )
+ return False;
+
+ DEBUG(10,("winbind_create_user: %s\n", name));
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* see if the caller wants a new RID returned */
+
+ if ( rid )
+ request.flags = WBFLAG_ALLOCATE_RID;
+
+ fstrcpy( request.data.acct_mgt.username, name );
+ fstrcpy( request.data.acct_mgt.groupname, "" );
+
+ result = winbindd_request( WINBINDD_CREATE_USER, &request, &response);
+
+ if ( rid )
+ *rid = response.data.rid;
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/**********************************************************************
+ Ask winbindd to create a local group
+**********************************************************************/
+
+BOOL winbind_create_group( const char *name, uint32 *rid )
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ if ( !lp_winbind_enable_local_accounts() )
+ return False;
+
+ if ( !name )
+ return False;
+
+ DEBUG(10,("winbind_create_group: %s\n", name));
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* see if the caller wants a new RID returned */
+
+ if ( rid )
+ request.flags = WBFLAG_ALLOCATE_RID;
+
+ fstrcpy( request.data.acct_mgt.groupname, name );
+
+
+ result = winbindd_request( WINBINDD_CREATE_GROUP, &request, &response);
+
+ if ( rid )
+ *rid = response.data.rid;
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/**********************************************************************
+ Ask winbindd to add a user to a local group
+**********************************************************************/
+
+BOOL winbind_add_user_to_group( const char *user, const char *group )
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ if ( !lp_winbind_enable_local_accounts() )
+ return False;
+
+ if ( !user || !group )
+ return False;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ DEBUG(10,("winbind_add_user_to_group: user(%s), group(%s) \n",
+ user, group));
+
+ fstrcpy( request.data.acct_mgt.username, user );
+ fstrcpy( request.data.acct_mgt.groupname, group );
+
+ result = winbindd_request( WINBINDD_ADD_USER_TO_GROUP, &request, &response);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/**********************************************************************
+ Ask winbindd to remove a user to a local group
+**********************************************************************/
+
+BOOL winbind_remove_user_from_group( const char *user, const char *group )
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ if ( !lp_winbind_enable_local_accounts() )
+ return False;
+
+ if ( !user || !group )
+ return False;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ DEBUG(10,("winbind_remove_user_from_group: user(%s), group(%s) \n",
+ user, group));
+
+ ZERO_STRUCT(response);
+
+ fstrcpy( request.data.acct_mgt.username, user );
+ fstrcpy( request.data.acct_mgt.groupname, group );
+
+ result = winbindd_request( WINBINDD_REMOVE_USER_FROM_GROUP, &request, &response);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/**********************************************************************
+ Ask winbindd to set the primary group for a user local user
+**********************************************************************/
+
+BOOL winbind_set_user_primary_group( const char *user, const char *group )
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ if ( !lp_winbind_enable_local_accounts() )
+ return False;
+
+ if ( !user || !group )
+ return False;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ DEBUG(10,("winbind_set_user_primary_group: user(%s), group(%s) \n",
+ user, group));
+
+ fstrcpy( request.data.acct_mgt.username, user );
+ fstrcpy( request.data.acct_mgt.groupname, group );
+
+ result = winbindd_request( WINBINDD_SET_USER_PRIMARY_GROUP, &request, &response);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+
+/**********************************************************************
+ Ask winbindd to remove a user from its lists of accounts
+**********************************************************************/
+
+BOOL winbind_delete_user( const char *user )
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ if ( !lp_winbind_enable_local_accounts() )
+ return False;
+
+ if ( !user )
+ return False;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ DEBUG(10,("winbind_delete_user: user (%s)\n", user));
+
+ fstrcpy( request.data.acct_mgt.username, user );
+
+ result = winbindd_request( WINBINDD_DELETE_USER, &request, &response);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/**********************************************************************
+ Ask winbindd to remove a group from its lists of accounts
+**********************************************************************/
+
+BOOL winbind_delete_group( const char *group )
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ if ( !lp_winbind_enable_local_accounts() )
+ return False;
+
+ if ( !group )
+ return False;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ DEBUG(10,("winbind_delete_group: group (%s)\n", group));
+
+ fstrcpy( request.data.acct_mgt.groupname, group );
+
+ result = winbindd_request( WINBINDD_DELETE_GROUP, &request, &response);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/***********************************************************************/
+/* Call winbindd to convert SID to uid. Do not allocate */
+
+BOOL winbind_sid_to_uid_query(uid_t *puid, const DOM_SID *sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int result;
+ fstring sid_str;
+
+ if (!puid)
+ return False;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ sid_to_string(sid_str, sid);
+ fstrcpy(request.data.sid, sid_str);
+
+ request.flags = WBFLAG_QUERY_ONLY;
+
+ /* Make request */
+
+ result = winbindd_request(WINBINDD_SID_TO_UID, &request, &response);
+
+ /* Copy out result */
+
+ if (result == NSS_STATUS_SUCCESS) {
+ *puid = response.data.uid;
+ }
+
+ return (result == NSS_STATUS_SUCCESS);
+}
+
+/* Call winbindd to convert SID to gid. Do not allocate */
+
+BOOL winbind_sid_to_gid_query(gid_t *pgid, const DOM_SID *sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int result;
+ fstring sid_str;
+
+ if (!pgid)
+ return False;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ sid_to_string(sid_str, sid);
+ fstrcpy(request.data.sid, sid_str);
+
+ request.flags = WBFLAG_QUERY_ONLY;
+
+ /* Make request */
+
+ result = winbindd_request(WINBINDD_SID_TO_GID, &request, &response);
+
+ /* Copy out result */
+
+ if (result == NSS_STATUS_SUCCESS) {
+ *pgid = response.data.gid;
+ }
+
+ return (result == NSS_STATUS_SUCCESS);
+}
+
+/***********************************************************************/
+
diff --git a/source/nsswitch/wb_common.c b/source/nsswitch/wb_common.c
new file mode 100644
index 00000000000..40221b69feb
--- /dev/null
+++ b/source/nsswitch/wb_common.c
@@ -0,0 +1,494 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ winbind client common code
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Andrew Tridgell 2000
+ Copyright (C) Andrew Bartlett 2002
+
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "winbind_client.h"
+
+/* Global variables. These are effectively the client state information */
+
+int winbindd_fd = -1; /* fd for winbindd socket */
+
+/* Free a response structure */
+
+void free_response(struct winbindd_response *response)
+{
+ /* Free any allocated extra_data */
+
+ if (response)
+ SAFE_FREE(response->extra_data);
+}
+
+/* Initialise a request structure */
+
+void init_request(struct winbindd_request *request, int request_type)
+{
+ request->length = sizeof(struct winbindd_request);
+
+ request->cmd = (enum winbindd_cmd)request_type;
+ request->pid = getpid();
+
+}
+
+/* Initialise a response structure */
+
+void init_response(struct winbindd_response *response)
+{
+ /* Initialise return value */
+
+ response->result = WINBINDD_ERROR;
+}
+
+/* Close established socket */
+
+void close_sock(void)
+{
+ if (winbindd_fd != -1) {
+ close(winbindd_fd);
+ winbindd_fd = -1;
+ }
+}
+
+/* Make sure socket handle isn't stdin, stdout or stderr */
+#define RECURSION_LIMIT 3
+
+static int make_nonstd_fd_internals(int fd, int limit /* Recursion limiter */)
+{
+ int new_fd;
+ if (fd >= 0 && fd <= 2) {
+#ifdef F_DUPFD
+ if ((new_fd = fcntl(fd, F_DUPFD, 3)) == -1) {
+ return -1;
+ }
+ /* Paranoia */
+ if (new_fd < 3) {
+ close(new_fd);
+ return -1;
+ }
+ close(fd);
+ return new_fd;
+#else
+ if (limit <= 0)
+ return -1;
+
+ new_fd = dup(fd);
+ if (new_fd == -1)
+ return -1;
+
+ /* use the program stack to hold our list of FDs to close */
+ new_fd = make_nonstd_fd_internals(new_fd, limit - 1);
+ close(fd);
+ return new_fd;
+#endif
+ }
+ return fd;
+}
+
+static int make_safe_fd(int fd)
+{
+ int result, flags;
+ int new_fd = make_nonstd_fd_internals(fd, RECURSION_LIMIT);
+ if (new_fd == -1) {
+ close(fd);
+ return -1;
+ }
+ /* Socket should be closed on exec() */
+
+#ifdef FD_CLOEXEC
+ result = flags = fcntl(new_fd, F_GETFD, 0);
+ if (flags >= 0) {
+ flags |= FD_CLOEXEC;
+ result = fcntl( new_fd, F_SETFD, flags );
+ }
+ if (result < 0) {
+ close(new_fd);
+ return -1;
+ }
+#endif
+ return new_fd;
+}
+
+/* Connect to winbindd socket */
+
+static int winbind_named_pipe_sock(const char *dir)
+{
+ struct sockaddr_un sunaddr;
+ struct stat st;
+ pstring path;
+ int fd;
+
+ /* Check permissions on unix socket directory */
+
+ if (lstat(dir, &st) == -1) {
+ return -1;
+ }
+
+ if (!S_ISDIR(st.st_mode) ||
+ (st.st_uid != 0 && st.st_uid != geteuid())) {
+ return -1;
+ }
+
+ /* Connect to socket */
+
+ strncpy(path, dir, sizeof(path) - 1);
+ path[sizeof(path) - 1] = '\0';
+
+ strncat(path, "/", sizeof(path) - 1 - strlen(path));
+ path[sizeof(path) - 1] = '\0';
+
+ strncat(path, WINBINDD_SOCKET_NAME, sizeof(path) - 1 - strlen(path));
+ path[sizeof(path) - 1] = '\0';
+
+ ZERO_STRUCT(sunaddr);
+ sunaddr.sun_family = AF_UNIX;
+ strncpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path) - 1);
+
+ /* If socket file doesn't exist, don't bother trying to connect
+ with retry. This is an attempt to make the system usable when
+ the winbindd daemon is not running. */
+
+ if (lstat(path, &st) == -1) {
+ return -1;
+ }
+
+ /* Check permissions on unix socket file */
+
+ if (!S_ISSOCK(st.st_mode) ||
+ (st.st_uid != 0 && st.st_uid != geteuid())) {
+ return -1;
+ }
+
+ /* Connect to socket */
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+ return -1;
+ }
+
+ if ((fd = make_safe_fd( fd)) == -1) {
+ return fd;
+ }
+
+ if (connect(fd, (struct sockaddr *)&sunaddr,
+ sizeof(sunaddr)) == -1) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+/* Connect to winbindd socket */
+
+int winbind_open_pipe_sock(void)
+{
+#ifdef HAVE_UNIXSOCKET
+ static pid_t our_pid;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (our_pid != getpid()) {
+ close_sock();
+ our_pid = getpid();
+ }
+
+ if (winbindd_fd != -1) {
+ return winbindd_fd;
+ }
+
+ if ((winbindd_fd = winbind_named_pipe_sock(WINBINDD_SOCKET_DIR)) == -1) {
+ return -1;
+ }
+
+ /* version-check the socket */
+
+ if ((winbindd_request(WINBINDD_INTERFACE_VERSION, &request, &response) != NSS_STATUS_SUCCESS) || (response.data.interface_version != WINBIND_INTERFACE_VERSION)) {
+ close_sock();
+ return -1;
+ }
+
+ /* try and get priv pipe */
+
+ if (winbindd_request(WINBINDD_PRIV_PIPE_DIR, &request, &response) == NSS_STATUS_SUCCESS) {
+ int fd;
+ if ((fd = winbind_named_pipe_sock(response.extra_data)) != -1) {
+ close(winbindd_fd);
+ winbindd_fd = fd;
+ }
+ }
+
+ SAFE_FREE(response.extra_data);
+
+ return winbindd_fd;
+#else
+ return -1;
+#endif /* HAVE_UNIXSOCKET */
+}
+
+/* Write data to winbindd socket */
+
+int write_sock(void *buffer, int count)
+{
+ int result, nwritten;
+
+ /* Open connection to winbind daemon */
+
+ restart:
+
+ if (winbind_open_pipe_sock() == -1) {
+ return -1;
+ }
+
+ /* Write data to socket */
+
+ nwritten = 0;
+
+ while(nwritten < count) {
+ struct timeval tv;
+ fd_set r_fds;
+
+ /* Catch pipe close on other end by checking if a read()
+ call would not block by calling select(). */
+
+ FD_ZERO(&r_fds);
+ FD_SET(winbindd_fd, &r_fds);
+ ZERO_STRUCT(tv);
+
+ if (select(winbindd_fd + 1, &r_fds, NULL, NULL, &tv) == -1) {
+ close_sock();
+ return -1; /* Select error */
+ }
+
+ /* Write should be OK if fd not available for reading */
+
+ if (!FD_ISSET(winbindd_fd, &r_fds)) {
+
+ /* Do the write */
+
+ result = write(winbindd_fd,
+ (char *)buffer + nwritten,
+ count - nwritten);
+
+ if ((result == -1) || (result == 0)) {
+
+ /* Write failed */
+
+ close_sock();
+ return -1;
+ }
+
+ nwritten += result;
+
+ } else {
+
+ /* Pipe has closed on remote end */
+
+ close_sock();
+ goto restart;
+ }
+ }
+
+ return nwritten;
+}
+
+/* Read data from winbindd socket */
+
+static int read_sock(void *buffer, int count)
+{
+ int result = 0, nread = 0;
+
+ /* Read data from socket */
+
+ while(nread < count) {
+
+ result = read(winbindd_fd, (char *)buffer + nread,
+ count - nread);
+
+ if ((result == -1) || (result == 0)) {
+
+ /* Read failed. I think the only useful thing we
+ can do here is just return -1 and fail since the
+ transaction has failed half way through. */
+
+ close_sock();
+ return -1;
+ }
+
+ nread += result;
+ }
+
+ return result;
+}
+
+/* Read reply */
+
+int read_reply(struct winbindd_response *response)
+{
+ int result1, result2 = 0;
+
+ if (!response) {
+ return -1;
+ }
+
+ /* Read fixed length response */
+
+ if ((result1 = read_sock(response, sizeof(struct winbindd_response)))
+ == -1) {
+
+ return -1;
+ }
+
+ /* We actually send the pointer value of the extra_data field from
+ the server. This has no meaning in the client's address space
+ so we clear it out. */
+
+ response->extra_data = NULL;
+
+ /* Read variable length response */
+
+ if (response->length > sizeof(struct winbindd_response)) {
+ int extra_data_len = response->length -
+ sizeof(struct winbindd_response);
+
+ /* Mallocate memory for extra data */
+
+ if (!(response->extra_data = malloc(extra_data_len))) {
+ return -1;
+ }
+
+ if ((result2 = read_sock(response->extra_data, extra_data_len))
+ == -1) {
+ free_response(response);
+ return -1;
+ }
+ }
+
+ /* Return total amount of data read */
+
+ return result1 + result2;
+}
+
+/*
+ * send simple types of requests
+ */
+
+NSS_STATUS winbindd_send_request(int req_type, struct winbindd_request *request)
+{
+ struct winbindd_request lrequest;
+ char *env;
+ int value;
+
+ /* Check for our tricky environment variable */
+
+ if ( (env = getenv(WINBINDD_DONT_ENV)) != NULL ) {
+ value = atoi(env);
+ if ( value == 1 )
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ if (!request) {
+ ZERO_STRUCT(lrequest);
+ request = &lrequest;
+ }
+
+ /* Fill in request and send down pipe */
+
+ init_request(request, req_type);
+
+ if (write_sock(request, sizeof(*request)) == -1) {
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ return NSS_STATUS_SUCCESS;
+}
+
+/*
+ * Get results from winbindd request
+ */
+
+NSS_STATUS winbindd_get_response(struct winbindd_response *response)
+{
+ struct winbindd_response lresponse;
+
+ if (!response) {
+ ZERO_STRUCT(lresponse);
+ response = &lresponse;
+ }
+
+ init_response(response);
+
+ /* Wait for reply */
+ if (read_reply(response) == -1) {
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ /* Throw away extra data if client didn't request it */
+ if (response == &lresponse) {
+ free_response(response);
+ }
+
+ /* Copy reply data from socket */
+ if (response->result != WINBINDD_OK) {
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ return NSS_STATUS_SUCCESS;
+}
+
+/* Handle simple types of requests */
+
+NSS_STATUS winbindd_request(int req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response)
+{
+ NSS_STATUS status;
+
+ status = winbindd_send_request(req_type, request);
+ if (status != NSS_STATUS_SUCCESS)
+ return(status);
+ return winbindd_get_response(response);
+}
+
+/*************************************************************************
+ A couple of simple functions to disable winbindd lookups and re-
+ enable them
+ ************************************************************************/
+
+/* Use putenv() instead of setenv() in these functions as not all
+ environments have the latter. */
+
+BOOL winbind_off( void )
+{
+ static char *s = WINBINDD_DONT_ENV "=1";
+
+ return putenv(s) != -1;
+}
+
+BOOL winbind_on( void )
+{
+ static char *s = WINBINDD_DONT_ENV "=0";
+
+ return putenv(s) != -1;
+}
diff --git a/source/nsswitch/wbinfo.c b/source/nsswitch/wbinfo.c
new file mode 100644
index 00000000000..5c1722dcaa5
--- /dev/null
+++ b/source/nsswitch/wbinfo.c
@@ -0,0 +1,1313 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind status program.
+
+ Copyright (C) Tim Potter 2000-2003
+ Copyright (C) Andrew Bartlett 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "debug.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+extern int winbindd_fd;
+
+static char winbind_separator(void)
+{
+ struct winbindd_response response;
+ static BOOL got_sep;
+ static char sep;
+
+ if (got_sep)
+ return sep;
+
+ ZERO_STRUCT(response);
+
+ /* Send off request */
+
+ if (winbindd_request(WINBINDD_INFO, NULL, &response) !=
+ NSS_STATUS_SUCCESS) {
+ d_printf("could not obtain winbind separator!\n");
+ /* HACK: (this module should not call lp_ funtions) */
+ return *lp_winbind_separator();
+ }
+
+ sep = response.data.info.winbind_separator;
+ got_sep = True;
+
+ if (!sep) {
+ d_printf("winbind separator was NULL!\n");
+ /* HACK: (this module should not call lp_ funtions) */
+ sep = *lp_winbind_separator();
+ }
+
+ return sep;
+}
+
+static const char *get_winbind_domain(void)
+{
+ struct winbindd_response response;
+ static fstring winbind_domain;
+
+ ZERO_STRUCT(response);
+
+ /* Send off request */
+
+ if (winbindd_request(WINBINDD_DOMAIN_NAME, NULL, &response) !=
+ NSS_STATUS_SUCCESS) {
+ d_printf("could not obtain winbind domain name!\n");
+
+ /* HACK: (this module should not call lp_ funtions) */
+ return lp_workgroup();
+ }
+
+ fstrcpy(winbind_domain, response.data.domain_name);
+
+ return winbind_domain;
+
+}
+
+/* Copy of parse_domain_user from winbindd_util.c. Parse a string of the
+ form DOMAIN/user into a domain and a user */
+
+static BOOL parse_wbinfo_domain_user(const char *domuser, fstring domain,
+ fstring user)
+{
+
+ char *p = strchr(domuser,winbind_separator());
+
+ if (!p) {
+ fstrcpy(user, domuser);
+ fstrcpy(domain, get_winbind_domain());
+ return True;
+ }
+
+ fstrcpy(user, p+1);
+ fstrcpy(domain, domuser);
+ domain[PTR_DIFF(p, domuser)] = 0;
+ strupper_m(domain);
+
+ return True;
+}
+
+/* List groups a user is a member of */
+
+static BOOL wbinfo_get_usergroups(char *user)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ int i;
+
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ fstrcpy(request.data.username, user);
+
+ result = winbindd_request(WINBINDD_GETGROUPS, &request, &response);
+
+ if (result != NSS_STATUS_SUCCESS)
+ return False;
+
+ for (i = 0; i < response.data.num_entries; i++)
+ d_printf("%d\n", (int)((gid_t *)response.extra_data)[i]);
+
+ SAFE_FREE(response.extra_data);
+
+ return True;
+}
+
+
+/* List group SIDs a user SID is a member of */
+static BOOL wbinfo_get_usersids(char *user_sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ int i;
+ const char *s;
+
+ ZERO_STRUCT(response);
+
+ /* Send request */
+ fstrcpy(request.data.sid, user_sid);
+
+ result = winbindd_request(WINBINDD_GETUSERSIDS, &request, &response);
+
+ if (result != NSS_STATUS_SUCCESS)
+ return False;
+
+ s = response.extra_data;
+ for (i = 0; i < response.data.num_entries; i++) {
+ d_printf("%s\n", s);
+ s += strlen(s) + 1;
+ }
+
+ SAFE_FREE(response.extra_data);
+
+ return True;
+}
+
+/* Convert NetBIOS name to IP */
+
+static BOOL wbinfo_wins_byname(char *name)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ fstrcpy(request.data.winsreq, name);
+
+ if (winbindd_request(WINBINDD_WINS_BYNAME, &request, &response) !=
+ NSS_STATUS_SUCCESS) {
+ return False;
+ }
+
+ /* Display response */
+
+ printf("%s\n", response.data.winsresp);
+
+ return True;
+}
+
+/* Convert IP to NetBIOS name */
+
+static BOOL wbinfo_wins_byip(char *ip)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ fstrcpy(request.data.winsreq, ip);
+
+ if (winbindd_request(WINBINDD_WINS_BYIP, &request, &response) !=
+ NSS_STATUS_SUCCESS) {
+ return False;
+ }
+
+ /* Display response */
+
+ printf("%s\n", response.data.winsresp);
+
+ return True;
+}
+
+/* List trusted domains */
+
+static BOOL wbinfo_list_domains(void)
+{
+ struct winbindd_response response;
+ fstring name;
+
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ if (winbindd_request(WINBINDD_LIST_TRUSTDOM, NULL, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Display response */
+
+ if (response.extra_data) {
+ const char *extra_data = (char *)response.extra_data;
+
+ while(next_token(&extra_data, name, ",", sizeof(fstring)))
+ d_printf("%s\n", name);
+
+ SAFE_FREE(response.extra_data);
+ }
+
+ return True;
+}
+
+
+/* show sequence numbers */
+static BOOL wbinfo_show_sequence(const char *domain)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ if ( domain )
+ fstrcpy( request.domain_name, domain );
+
+ /* Send request */
+
+ if (winbindd_request(WINBINDD_SHOW_SEQUENCE, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Display response */
+
+ if (response.extra_data) {
+ char *extra_data = (char *)response.extra_data;
+ d_printf("%s", extra_data);
+ SAFE_FREE(response.extra_data);
+ }
+
+ return True;
+}
+
+/* Show domain info */
+
+static BOOL wbinfo_domain_info(const char *domain_name)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.domain_name, domain_name);
+
+ /* Send request */
+
+ if (winbindd_request(WINBINDD_DOMAIN_INFO, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Display response */
+
+ d_printf("Name : %s\n", response.data.domain_info.name);
+ d_printf("Alt_Name : %s\n", response.data.domain_info.alt_name);
+
+ d_printf("SID : %s\n", response.data.domain_info.sid);
+
+ d_printf("Active Directory : %s\n",
+ response.data.domain_info.active_directory ? "Yes" : "No");
+ d_printf("Native : %s\n",
+ response.data.domain_info.native_mode ? "Yes" : "No");
+
+ d_printf("Primary : %s\n",
+ response.data.domain_info.primary ? "Yes" : "No");
+
+ d_printf("Sequence : %d\n", response.data.domain_info.sequence_number);
+
+ return True;
+}
+
+/* Check trust account password */
+
+static BOOL wbinfo_check_secret(void)
+{
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ ZERO_STRUCT(response);
+
+ result = winbindd_request(WINBINDD_CHECK_MACHACC, NULL, &response);
+
+ d_printf("checking the trust secret via RPC calls %s\n",
+ (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed");
+
+ if (result != NSS_STATUS_SUCCESS)
+ d_printf("error code was %s (0x%x)\n",
+ response.data.auth.nt_status_string,
+ response.data.auth.nt_status);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/* Convert uid to sid */
+
+static BOOL wbinfo_uid_to_sid(uid_t uid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ request.data.uid = uid;
+
+ if (winbindd_request(WINBINDD_UID_TO_SID, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Display response */
+
+ d_printf("%s\n", response.data.sid.sid);
+
+ return True;
+}
+
+/* Convert gid to sid */
+
+static BOOL wbinfo_gid_to_sid(gid_t gid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ request.data.gid = gid;
+
+ if (winbindd_request(WINBINDD_GID_TO_SID, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Display response */
+
+ d_printf("%s\n", response.data.sid.sid);
+
+ return True;
+}
+
+/* Convert sid to uid */
+
+static BOOL wbinfo_sid_to_uid(char *sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ fstrcpy(request.data.sid, sid);
+
+ if (winbindd_request(WINBINDD_SID_TO_UID, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Display response */
+
+ d_printf("%d\n", (int)response.data.uid);
+
+ return True;
+}
+
+static BOOL wbinfo_sid_to_gid(char *sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ fstrcpy(request.data.sid, sid);
+
+ if (winbindd_request(WINBINDD_SID_TO_GID, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Display response */
+
+ d_printf("%d\n", (int)response.data.gid);
+
+ return True;
+}
+
+static BOOL wbinfo_allocate_rid(void)
+{
+ uint32 rid;
+
+ if (!winbind_allocate_rid(&rid))
+ return False;
+
+ d_printf("New rid: %d\n", rid);
+
+ return True;
+}
+
+/* Convert sid to string */
+
+static BOOL wbinfo_lookupsid(char *sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send off request */
+
+ fstrcpy(request.data.sid, sid);
+
+ if (winbindd_request(WINBINDD_LOOKUPSID, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Display response */
+
+ d_printf("%s%c%s %d\n", response.data.name.dom_name,
+ winbind_separator(), response.data.name.name,
+ response.data.name.type);
+
+ return True;
+}
+
+/* Convert string to sid */
+
+static BOOL wbinfo_lookupname(char *name)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ parse_wbinfo_domain_user(name, request.data.name.dom_name,
+ request.data.name.name);
+
+ if (winbindd_request(WINBINDD_LOOKUPNAME, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Display response */
+
+ d_printf("%s %s (%d)\n", response.data.sid.sid, sid_type_lookup(response.data.sid.type), response.data.sid.type);
+
+ return True;
+}
+
+/* Authenticate a user with a plaintext password */
+
+static BOOL wbinfo_auth(char *username)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ char *p;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ p = strchr(username, '%');
+
+ if (p) {
+ *p = 0;
+ fstrcpy(request.data.auth.user, username);
+ fstrcpy(request.data.auth.pass, p + 1);
+ *p = '%';
+ } else
+ fstrcpy(request.data.auth.user, username);
+
+ result = winbindd_request(WINBINDD_PAM_AUTH, &request, &response);
+
+ /* Display response */
+
+ d_printf("plaintext password authentication %s\n",
+ (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed");
+
+ if (response.data.auth.nt_status)
+ d_printf("error code was %s (0x%x)\nerror messsage was: %s\n",
+ response.data.auth.nt_status_string,
+ response.data.auth.nt_status,
+ response.data.auth.error_string);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/* Authenticate a user with a challenge/response */
+
+static BOOL wbinfo_auth_crap(char *username)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ fstring name_user;
+ fstring name_domain;
+ fstring pass;
+ char *p;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ p = strchr(username, '%');
+
+ if (p) {
+ *p = 0;
+ fstrcpy(pass, p + 1);
+ }
+
+ parse_wbinfo_domain_user(username, name_domain, name_user);
+
+ if (push_utf8_fstring(request.data.auth_crap.user, name_user) == -1) {
+ d_printf("unable to create utf8 string for '%s'\n",
+ name_user);
+ return False;
+ }
+
+ if (push_utf8_fstring(request.data.auth_crap.domain,
+ name_domain) == -1) {
+ d_printf("unable to create utf8 string for '%s'\n",
+ name_domain);
+ return False;
+ }
+
+ generate_random_buffer(request.data.auth_crap.chal, 8, False);
+
+ SMBencrypt(pass, request.data.auth_crap.chal,
+ (uchar *)request.data.auth_crap.lm_resp);
+ SMBNTencrypt(pass, request.data.auth_crap.chal,
+ (uchar *)request.data.auth_crap.nt_resp);
+
+ request.data.auth_crap.lm_resp_len = 24;
+ request.data.auth_crap.nt_resp_len = 24;
+
+ result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response);
+
+ /* Display response */
+
+ d_printf("challenge/response password authentication %s\n",
+ (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed");
+
+ if (response.data.auth.nt_status)
+ d_printf("error code was %s (0x%x)\nerror messsage was: %s\n",
+ response.data.auth.nt_status_string,
+ response.data.auth.nt_status,
+ response.data.auth.error_string);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/* Authenticate a user with a plaintext password and set a token */
+
+static BOOL wbinfo_klog(char *username)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ char *p;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ p = strchr(username, '%');
+
+ if (p) {
+ *p = 0;
+ fstrcpy(request.data.auth.user, username);
+ fstrcpy(request.data.auth.pass, p + 1);
+ *p = '%';
+ } else {
+ fstrcpy(request.data.auth.user, username);
+ fstrcpy(request.data.auth.pass, getpass("Password: "));
+ }
+
+ request.flags |= WBFLAG_PAM_AFS_TOKEN;
+
+ result = winbindd_request(WINBINDD_PAM_AUTH, &request, &response);
+
+ /* Display response */
+
+ d_printf("plaintext password authentication %s\n",
+ (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed");
+
+ if (response.data.auth.nt_status)
+ d_printf("error code was %s (0x%x)\nerror messsage was: %s\n",
+ response.data.auth.nt_status_string,
+ response.data.auth.nt_status,
+ response.data.auth.error_string);
+
+ if (result != NSS_STATUS_SUCCESS)
+ return False;
+
+ if (response.extra_data == NULL) {
+ d_printf("Did not get token data\n");
+ return False;
+ }
+
+ if (!afs_settoken_str((char *)response.extra_data)) {
+ d_printf("Could not set token\n");
+ return False;
+ }
+
+ d_printf("Successfully created AFS token\n");
+ return True;
+}
+
+/******************************************************************
+ create a winbindd user
+******************************************************************/
+
+static BOOL wbinfo_create_user(char *username)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.flags = WBFLAG_ALLOCATE_RID;
+ fstrcpy(request.data.acct_mgt.username, username);
+
+ result = winbindd_request(WINBINDD_CREATE_USER, &request, &response);
+
+ if ( result == NSS_STATUS_SUCCESS )
+ d_printf("New RID is %d\n", response.data.rid);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/******************************************************************
+ remove a winbindd user
+******************************************************************/
+
+static BOOL wbinfo_delete_user(char *username)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.data.acct_mgt.username, username);
+
+ result = winbindd_request(WINBINDD_DELETE_USER, &request, &response);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/******************************************************************
+ create a winbindd group
+******************************************************************/
+
+static BOOL wbinfo_create_group(char *groupname)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.data.acct_mgt.groupname, groupname);
+
+ result = winbindd_request(WINBINDD_CREATE_GROUP, &request, &response);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/******************************************************************
+ remove a winbindd group
+******************************************************************/
+
+static BOOL wbinfo_delete_group(char *groupname)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.data.acct_mgt.groupname, groupname);
+
+ result = winbindd_request(WINBINDD_DELETE_GROUP, &request, &response);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/******************************************************************
+ parse a string in the form user:group
+******************************************************************/
+
+static BOOL parse_user_group( const char *string, fstring user, fstring group )
+{
+ char *p;
+
+ if ( !string )
+ return False;
+
+ if ( !(p = strchr( string, ':' )) )
+ return False;
+
+ *p = '\0';
+ p++;
+
+ fstrcpy( user, string );
+ fstrcpy( group, p );
+
+ return True;
+}
+
+/******************************************************************
+ add a user to a winbindd group
+******************************************************************/
+
+static BOOL wbinfo_add_user_to_group(char *string)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if ( !parse_user_group( string, request.data.acct_mgt.username,
+ request.data.acct_mgt.groupname))
+ {
+ d_printf("Can't parse user:group from %s\n", string);
+ return False;
+ }
+
+ result = winbindd_request(WINBINDD_ADD_USER_TO_GROUP, &request, &response);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/******************************************************************
+ remove a user from a winbindd group
+******************************************************************/
+
+static BOOL wbinfo_remove_user_from_group(char *string)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if ( !parse_user_group( string, request.data.acct_mgt.username,
+ request.data.acct_mgt.groupname))
+ {
+ d_printf("Can't parse user:group from %s\n", string);
+ return False;
+ }
+
+ result = winbindd_request(WINBINDD_REMOVE_USER_FROM_GROUP, &request, &response);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/* Print domain users */
+
+static BOOL print_domain_users(const char *domain)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ const char *extra_data;
+ fstring name;
+
+ /* Send request to winbind daemon */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (domain) {
+ /* '.' is the special sign for our own domwin */
+ if ( strequal(domain, ".") )
+ fstrcpy( request.domain_name, lp_workgroup() );
+ else
+ fstrcpy( request.domain_name, domain );
+ }
+
+ if (winbindd_request(WINBINDD_LIST_USERS, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Look through extra data */
+
+ if (!response.extra_data)
+ return False;
+
+ extra_data = (const char *)response.extra_data;
+
+ while(next_token(&extra_data, name, ",", sizeof(fstring)))
+ d_printf("%s\n", name);
+
+ SAFE_FREE(response.extra_data);
+
+ return True;
+}
+
+/* Print domain groups */
+
+static BOOL print_domain_groups(const char *domain)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ const char *extra_data;
+ fstring name;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (domain) {
+ if ( strequal(domain, ".") )
+ fstrcpy( request.domain_name, lp_workgroup() );
+ else
+ fstrcpy( request.domain_name, domain );
+ }
+
+ if (winbindd_request(WINBINDD_LIST_GROUPS, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Look through extra data */
+
+ if (!response.extra_data)
+ return False;
+
+ extra_data = (const char *)response.extra_data;
+
+ while(next_token(&extra_data, name, ",", sizeof(fstring)))
+ d_printf("%s\n", name);
+
+ SAFE_FREE(response.extra_data);
+
+ return True;
+}
+
+/* Set the authorised user for winbindd access in secrets.tdb */
+
+static BOOL wbinfo_set_auth_user(char *username)
+{
+ char *password;
+ fstring user, domain;
+
+ /* Separate into user and password */
+
+ parse_wbinfo_domain_user(username, domain, user);
+
+ password = strchr(user, '%');
+
+ if (password) {
+ *password = 0;
+ password++;
+ } else {
+ char *thepass = getpass("Password: ");
+ if (thepass) {
+ password = thepass;
+ } else
+ password = "";
+ }
+
+ /* Store or remove DOMAIN\username%password in secrets.tdb */
+
+ secrets_init();
+
+ if (user[0]) {
+
+ if (!secrets_store(SECRETS_AUTH_USER, user,
+ strlen(user) + 1)) {
+ d_fprintf(stderr, "error storing username\n");
+ return False;
+ }
+
+ /* We always have a domain name added by the
+ parse_wbinfo_domain_user() function. */
+
+ if (!secrets_store(SECRETS_AUTH_DOMAIN, domain,
+ strlen(domain) + 1)) {
+ d_fprintf(stderr, "error storing domain name\n");
+ return False;
+ }
+
+ } else {
+ secrets_delete(SECRETS_AUTH_USER);
+ secrets_delete(SECRETS_AUTH_DOMAIN);
+ }
+
+ if (password[0]) {
+
+ if (!secrets_store(SECRETS_AUTH_PASSWORD, password,
+ strlen(password) + 1)) {
+ d_fprintf(stderr, "error storing password\n");
+ return False;
+ }
+
+ } else
+ secrets_delete(SECRETS_AUTH_PASSWORD);
+
+ return True;
+}
+
+static void wbinfo_get_auth_user(void)
+{
+ char *user, *domain, *password;
+
+ /* Lift data from secrets file */
+
+ secrets_fetch_ipc_userpass(&user, &domain, &password);
+
+ if ((!user || !*user) && (!domain || !*domain ) && (!password || !*password)){
+
+ SAFE_FREE(user);
+ SAFE_FREE(domain);
+ SAFE_FREE(password);
+ d_printf("No authorised user configured\n");
+ return;
+ }
+
+ /* Pretty print authorised user info */
+
+ d_printf("%s%s%s%s%s\n", domain ? domain : "", domain ? lp_winbind_separator(): "",
+ user, password ? "%" : "", password ? password : "");
+
+ SAFE_FREE(user);
+ SAFE_FREE(domain);
+ SAFE_FREE(password);
+}
+
+static BOOL wbinfo_ping(void)
+{
+ NSS_STATUS result;
+
+ result = winbindd_request(WINBINDD_PING, NULL, NULL);
+
+ /* Display response */
+
+ d_printf("Ping to winbindd %s on fd %d\n",
+ (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed", winbindd_fd);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/* Main program */
+
+enum {
+ OPT_SET_AUTH_USER = 1000,
+ OPT_GET_AUTH_USER,
+ OPT_DOMAIN_NAME,
+ OPT_SEQUENCE,
+ OPT_USERSIDS
+};
+
+int main(int argc, char **argv)
+{
+ int opt;
+
+ poptContext pc;
+ static char *string_arg;
+ static char *opt_domain_name;
+ static int int_arg;
+ int result = 1;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+
+ /* longName, shortName, argInfo, argPtr, value, descrip,
+ argDesc */
+
+ { "domain-users", 'u', POPT_ARG_NONE, 0, 'u', "Lists all domain users", "domain"},
+ { "domain-groups", 'g', POPT_ARG_NONE, 0, 'g', "Lists all domain groups", "domain" },
+ { "WINS-by-name", 'N', POPT_ARG_STRING, &string_arg, 'N', "Converts NetBIOS name to IP", "NETBIOS-NAME" },
+ { "WINS-by-ip", 'I', POPT_ARG_STRING, &string_arg, 'I', "Converts IP address to NetBIOS name", "IP" },
+ { "name-to-sid", 'n', POPT_ARG_STRING, &string_arg, 'n', "Converts name to sid", "NAME" },
+ { "sid-to-name", 's', POPT_ARG_STRING, &string_arg, 's', "Converts sid to name", "SID" },
+ { "uid-to-sid", 'U', POPT_ARG_INT, &int_arg, 'U', "Converts uid to sid" , "UID" },
+ { "gid-to-sid", 'G', POPT_ARG_INT, &int_arg, 'G', "Converts gid to sid", "GID" },
+ { "sid-to-uid", 'S', POPT_ARG_STRING, &string_arg, 'S', "Converts sid to uid", "SID" },
+ { "sid-to-gid", 'Y', POPT_ARG_STRING, &string_arg, 'Y', "Converts sid to gid", "SID" },
+ { "allocate-rid", 'A', POPT_ARG_NONE, 0, 'A', "Get a new RID out of idmap" },
+ { "create-user", 'c', POPT_ARG_STRING, &string_arg, 'c', "Create a local user account", "name" },
+ { "delete-user", 'x', POPT_ARG_STRING, &string_arg, 'x', "Delete a local user account", "name" },
+ { "create-group", 'C', POPT_ARG_STRING, &string_arg, 'C', "Create a local group", "name" },
+ { "delete-group", 'X', POPT_ARG_STRING, &string_arg, 'X', "Delete a local group", "name" },
+ { "add-to-group", 'o', POPT_ARG_STRING, &string_arg, 'o', "Add user to group", "user:group" },
+ { "del-from-group", 'O', POPT_ARG_STRING, &string_arg, 'O', "Remove user from group", "user:group" },
+ { "check-secret", 't', POPT_ARG_NONE, 0, 't', "Check shared secret" },
+ { "trusted-domains", 'm', POPT_ARG_NONE, 0, 'm', "List trusted domains" },
+ { "sequence", 0, POPT_ARG_NONE, 0, OPT_SEQUENCE, "Show sequence numbers of all domains" },
+ { "domain-info", 'D', POPT_ARG_STRING, &string_arg, 'D', "Show most of the info we have about the domain" },
+ { "user-groups", 'r', POPT_ARG_STRING, &string_arg, 'r', "Get user groups", "USER" },
+ { "user-sids", 0, POPT_ARG_STRING, &string_arg, OPT_USERSIDS, "Get user group sids for user SID", "SID" },
+ { "authenticate", 'a', POPT_ARG_STRING, &string_arg, 'a', "authenticate user", "user%password" },
+ { "set-auth-user", 0, POPT_ARG_STRING, &string_arg, OPT_SET_AUTH_USER, "Store user and password used by winbindd (root only)", "user%password" },
+ { "get-auth-user", 0, POPT_ARG_NONE, NULL, OPT_GET_AUTH_USER, "Retrieve user and password used by winbindd (root only)", NULL },
+ { "ping", 'p', POPT_ARG_NONE, 0, 'p', "Ping winbindd to see if it is alive" },
+ { "domain", 0, POPT_ARG_STRING, &opt_domain_name, OPT_DOMAIN_NAME, "Define to the domain to restrict operation", "domain" },
+#ifdef WITH_FAKE_KASERVER
+ { "klog", 'k', POPT_ARG_STRING, &string_arg, 'k', "set an AFS token from winbind", "user%password" },
+#endif
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ /* Samba client initialisation */
+
+ if (!lp_load(dyn_CONFIGFILE, True, False, False)) {
+ d_fprintf(stderr, "wbinfo: error opening config file %s. Error was %s\n",
+ dyn_CONFIGFILE, strerror(errno));
+ exit(1);
+ }
+
+ if (!init_names())
+ return 1;
+
+ load_interfaces();
+
+ /* Parse options */
+
+ pc = poptGetContext("wbinfo", argc, (const char **)argv, long_options, 0);
+
+ /* Parse command line options */
+
+ if (argc == 1) {
+ poptPrintHelp(pc, stderr, 0);
+ return 1;
+ }
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ /* get the generic configuration parameters like --domain */
+ }
+
+ poptFreeContext(pc);
+
+ pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'u':
+ if (!print_domain_users(opt_domain_name)) {
+ d_printf("Error looking up domain users\n");
+ goto done;
+ }
+ break;
+ case 'g':
+ if (!print_domain_groups(opt_domain_name)) {
+ d_printf("Error looking up domain groups\n");
+ goto done;
+ }
+ break;
+ case 's':
+ if (!wbinfo_lookupsid(string_arg)) {
+ d_printf("Could not lookup sid %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case 'n':
+ if (!wbinfo_lookupname(string_arg)) {
+ d_printf("Could not lookup name %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case 'N':
+ if (!wbinfo_wins_byname(string_arg)) {
+ d_printf("Could not lookup WINS by name %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case 'I':
+ if (!wbinfo_wins_byip(string_arg)) {
+ d_printf("Could not lookup WINS by IP %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case 'U':
+ if (!wbinfo_uid_to_sid(int_arg)) {
+ d_printf("Could not convert uid %d to sid\n", int_arg);
+ goto done;
+ }
+ break;
+ case 'G':
+ if (!wbinfo_gid_to_sid(int_arg)) {
+ d_printf("Could not convert gid %d to sid\n",
+ int_arg);
+ goto done;
+ }
+ break;
+ case 'S':
+ if (!wbinfo_sid_to_uid(string_arg)) {
+ d_printf("Could not convert sid %s to uid\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case 'Y':
+ if (!wbinfo_sid_to_gid(string_arg)) {
+ d_printf("Could not convert sid %s to gid\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case 'A':
+ if (!wbinfo_allocate_rid()) {
+ d_printf("Could not allocate a RID\n");
+ goto done;
+ }
+ break;
+ case 't':
+ if (!wbinfo_check_secret()) {
+ d_printf("Could not check secret\n");
+ goto done;
+ }
+ break;
+ case 'm':
+ if (!wbinfo_list_domains()) {
+ d_printf("Could not list trusted domains\n");
+ goto done;
+ }
+ break;
+ case OPT_SEQUENCE:
+ if (!wbinfo_show_sequence(opt_domain_name)) {
+ d_printf("Could not show sequence numbers\n");
+ goto done;
+ }
+ break;
+ case 'D':
+ if (!wbinfo_domain_info(string_arg)) {
+ d_printf("Could not get domain info\n");
+ goto done;
+ }
+ break;
+ case 'r':
+ if (!wbinfo_get_usergroups(string_arg)) {
+ d_printf("Could not get groups for user %s\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case OPT_USERSIDS:
+ if (!wbinfo_get_usersids(string_arg)) {
+ d_printf("Could not get group SIDs for user SID %s\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case 'a': {
+ BOOL got_error = False;
+
+ if (!wbinfo_auth(string_arg)) {
+ d_printf("Could not authenticate user %s with "
+ "plaintext password\n", string_arg);
+ got_error = True;
+ }
+
+ if (!wbinfo_auth_crap(string_arg)) {
+ d_printf("Could not authenticate user %s with "
+ "challenge/response\n", string_arg);
+ got_error = True;
+ }
+
+ if (got_error)
+ goto done;
+ break;
+ }
+ case 'k':
+ if (!wbinfo_klog(string_arg)) {
+ d_printf("Could not klog user\n");
+ goto done;
+ }
+ break;
+ case 'c':
+ if ( !wbinfo_create_user(string_arg) ) {
+ d_printf("Could not create user account\n");
+ goto done;
+ }
+ break;
+ case 'C':
+ if ( !wbinfo_create_group(string_arg) ) {
+ d_printf("Could not create group\n");
+ goto done;
+ }
+ break;
+ case 'o':
+ if ( !wbinfo_add_user_to_group(string_arg) ) {
+ d_printf("Could not add user to group\n");
+ goto done;
+ }
+ break;
+ case 'O':
+ if ( !wbinfo_remove_user_from_group(string_arg) ) {
+ d_printf("Could not remove user from group\n");
+ goto done;
+ }
+ break;
+ case 'x':
+ if ( !wbinfo_delete_user(string_arg) ) {
+ d_printf("Could not delete user account\n");
+ goto done;
+ }
+ break;
+ case 'X':
+ if ( !wbinfo_delete_group(string_arg) ) {
+ d_printf("Could not delete group\n");
+ goto done;
+ }
+ break;
+ case 'p':
+ if (!wbinfo_ping()) {
+ d_printf("could not ping winbindd!\n");
+ goto done;
+ }
+ break;
+ case OPT_SET_AUTH_USER:
+ wbinfo_set_auth_user(string_arg);
+ break;
+ case OPT_GET_AUTH_USER:
+ wbinfo_get_auth_user();
+ break;
+ /* generic configuration options */
+ case OPT_DOMAIN_NAME:
+ break;
+ default:
+ d_fprintf(stderr, "Invalid option\n");
+ poptPrintHelp(pc, stderr, 0);
+ goto done;
+ }
+ }
+
+ result = 0;
+
+ /* Exit code */
+
+ done:
+ poptFreeContext(pc);
+ return result;
+}
diff --git a/source/nsswitch/winbind_client.h b/source/nsswitch/winbind_client.h
new file mode 100644
index 00000000000..4de2d57cc7d
--- /dev/null
+++ b/source/nsswitch/winbind_client.h
@@ -0,0 +1,16 @@
+#include "winbind_nss_config.h"
+#include "winbindd_nss.h"
+
+void init_request(struct winbindd_request *req,int rq_type);
+NSS_STATUS winbindd_send_request(int req_type,
+ struct winbindd_request *request);
+NSS_STATUS winbindd_get_response(struct winbindd_response *response);
+NSS_STATUS winbindd_request(int req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response);
+int winbind_open_pipe_sock(void);
+int write_sock(void *buffer, int count);
+int read_reply(struct winbindd_response *response);
+void close_sock(void);
+void free_response(struct winbindd_response *response);
+
diff --git a/source/nsswitch/winbind_nss.h b/source/nsswitch/winbind_nss.h
new file mode 100644
index 00000000000..5416ae211e3
--- /dev/null
+++ b/source/nsswitch/winbind_nss.h
@@ -0,0 +1,71 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ A common place to work out how to define NSS_STATUS on various
+ platforms.
+
+ Copyright (C) Tim Potter 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _NSSWITCH_NSS_H
+#define _NSSWITCH_NSS_H
+
+#ifdef HAVE_NSS_COMMON_H
+
+/*
+ * Sun Solaris
+ */
+
+#include "nsswitch/winbind_nss_solaris.h"
+
+#elif HAVE_NSS_H
+
+/*
+ * Linux (glibc)
+ */
+
+#include <nss.h>
+typedef enum nss_status NSS_STATUS;
+
+#elif HAVE_NS_API_H
+
+/*
+ * SGI IRIX
+ */
+
+#include "nsswitch/winbind_nss_irix.h"
+
+#elif defined(HPUX) && defined(HAVE_NSSWITCH_H)
+
+/* HP-UX 11 */
+
+#include "nsswitch/winbind_nss_hpux.h"
+
+#else /* Nothing's defined. Neither gnu nor sun nor hp */
+
+typedef enum
+{
+ NSS_STATUS_SUCCESS=0,
+ NSS_STATUS_NOTFOUND=1,
+ NSS_STATUS_UNAVAIL=2,
+ NSS_STATUS_TRYAGAIN=3
+} NSS_STATUS;
+
+#endif
+
+#endif /* _NSSWITCH_NSS_H */
diff --git a/source/nsswitch/winbind_nss_aix.c b/source/nsswitch/winbind_nss_aix.c
new file mode 100644
index 00000000000..3e00e54e5c4
--- /dev/null
+++ b/source/nsswitch/winbind_nss_aix.c
@@ -0,0 +1,1019 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ AIX loadable authentication module, providing identification and
+ authentication routines against Samba winbind/Windows NT Domain
+
+ Copyright (C) Tim Potter 2003
+ Copyright (C) Steve Roylance 2003
+ Copyright (C) Andrew Tridgell 2003-2004
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/*
+
+ To install this module copy nsswitch/WINBIND to /usr/lib/security and add
+ "WINBIND" in /usr/lib/security/methods.cfg and /etc/security/user
+
+ Note that this module also provides authentication and password
+ changing routines, so you do not need to install the winbind PAM
+ module.
+
+ see
+ http://publib16.boulder.ibm.com/doc_link/en_US/a_doc_lib/aixprggd/kernextc/sec_load_mod.htm
+ for some information in the interface that this module implements
+
+ Many thanks to Julianne Haugh for explaining some of the finer
+ details of this interface.
+
+ To debug this module use uess_test.c (which you can get from tridge)
+ or set "options=debug" in /usr/lib/security/methods.cfg
+
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <usersec.h>
+#include <errno.h>
+#include <stdarg.h>
+
+#include "winbind_client.h"
+
+#define WB_AIX_ENCODED '_'
+
+static int debug_enabled;
+
+
+static void logit(const char *format, ...)
+{
+ va_list ap;
+ FILE *f;
+ if (!debug_enabled) {
+ return;
+ }
+ f = fopen("/tmp/WINBIND_DEBUG.log", "a");
+ if (!f) return;
+ va_start(ap, format);
+ vfprintf(f, format, ap);
+ va_end(ap);
+ fclose(f);
+}
+
+
+#define HANDLE_ERRORS(ret) do { \
+ if ((ret) == NSS_STATUS_NOTFOUND) { \
+ errno = ENOENT; \
+ return NULL; \
+ } else if ((ret) != NSS_STATUS_SUCCESS) { \
+ errno = EIO; \
+ return NULL; \
+ } \
+} while (0)
+
+#define STRCPY_RET(dest, src) \
+do { \
+ if (strlen(src)+1 > sizeof(dest)) { errno = EINVAL; return -1; } \
+ strcpy(dest, src); \
+} while (0)
+
+#define STRCPY_RETNULL(dest, src) \
+do { \
+ if (strlen(src)+1 > sizeof(dest)) { errno = EINVAL; return NULL; } \
+ strcpy(dest, src); \
+} while (0)
+
+
+/* free a passwd structure */
+static void free_pwd(struct passwd *pwd)
+{
+ free(pwd->pw_name);
+ free(pwd->pw_passwd);
+ free(pwd->pw_gecos);
+ free(pwd->pw_dir);
+ free(pwd->pw_shell);
+ free(pwd);
+}
+
+/* free a group structure */
+static void free_grp(struct group *grp)
+{
+ int i;
+
+ free(grp->gr_name);
+ free(grp->gr_passwd);
+
+ if (!grp->gr_mem) {
+ free(grp);
+ return;
+ }
+
+ for (i=0; grp->gr_mem[i]; i++) {
+ free(grp->gr_mem[i]);
+ }
+
+ free(grp->gr_mem);
+ free(grp);
+}
+
+
+/* replace commas with nulls, and null terminate */
+static void replace_commas(char *s)
+{
+ char *p, *p0=s;
+ for (p=strchr(s, ','); p; p = strchr(p+1, ',')) {
+ *p=0;
+ p0 = p+1;
+ }
+
+ p0[strlen(p0)+1] = 0;
+}
+
+
+/* the decode_*() routines are used to cope with the fact that AIX 5.2
+ and below cannot handle user or group names longer than 8
+ characters in some interfaces. We use the normalize method to
+ provide a mapping to a username that fits, by using the form '_UID'
+ or '_GID'.
+
+ this only works if you can guarantee that the WB_AIX_ENCODED char
+ is not used as the first char of any other username
+*/
+static unsigned decode_id(const char *name)
+{
+ unsigned id;
+ sscanf(name+1, "%u", &id);
+ return id;
+}
+
+static char *decode_user(const char *name)
+{
+ struct passwd *pwd;
+ unsigned id;
+ char *ret;
+ static struct passwd *wb_aix_getpwuid(uid_t uid);
+
+ sscanf(name+1, "%u", &id);
+ pwd = wb_aix_getpwuid(id);
+ if (!pwd) {
+ return NULL;
+ }
+ ret = strdup(pwd->pw_name);
+
+ free_pwd(pwd);
+
+ logit("decoded '%s' -> '%s'\n", name, ret);
+
+ return ret;
+}
+
+
+/*
+ fill a struct passwd from a winbindd_pw struct, allocating as a single block
+*/
+static struct passwd *fill_pwent(struct winbindd_pw *pw)
+{
+ struct passwd *result;
+
+ result = calloc(1, sizeof(struct passwd));
+ if (!result) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ result->pw_uid = pw->pw_uid;
+ result->pw_gid = pw->pw_gid;
+ result->pw_name = strdup(pw->pw_name);
+ result->pw_passwd = strdup(pw->pw_passwd);
+ result->pw_gecos = strdup(pw->pw_gecos);
+ result->pw_dir = strdup(pw->pw_dir);
+ result->pw_shell = strdup(pw->pw_shell);
+
+ return result;
+}
+
+
+/*
+ fill a struct group from a winbindd_pw struct, allocating as a single block
+*/
+static struct group *fill_grent(struct winbindd_gr *gr, char *gr_mem)
+{
+ int i;
+ struct group *result;
+ char *p, *name;
+
+ result = calloc(1, sizeof(struct group));
+ if (!result) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ result->gr_gid = gr->gr_gid;
+
+ result->gr_name = strdup(gr->gr_name);
+ result->gr_passwd = strdup(gr->gr_passwd);
+
+ /* Group membership */
+ if ((gr->num_gr_mem < 0) || !gr_mem) {
+ gr->num_gr_mem = 0;
+ }
+
+ if (gr->num_gr_mem == 0) {
+ /* Group is empty */
+ return result;
+ }
+
+ result->gr_mem = (char **)malloc(sizeof(char *) * (gr->num_gr_mem+1));
+ if (!result->gr_mem) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ /* Start looking at extra data */
+ i=0;
+ for (name = strtok_r(gr_mem, ",", &p);
+ name;
+ name = strtok_r(NULL, ",", &p)) {
+ if (i == gr->num_gr_mem) {
+ break;
+ }
+ result->gr_mem[i] = strdup(name);
+ i++;
+ }
+
+ /* Terminate list */
+ result->gr_mem[i] = NULL;
+
+ return result;
+}
+
+
+
+/* take a group id and return a filled struct group */
+static struct group *wb_aix_getgrgid(gid_t gid)
+{
+ struct winbindd_response response;
+ struct winbindd_request request;
+ struct group *grp;
+ NSS_STATUS ret;
+
+ logit("getgrgid %d\n", gid);
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ request.data.gid = gid;
+
+ ret = winbindd_request(WINBINDD_GETGRGID, &request, &response);
+
+ logit("getgrgid ret=%d\n", ret);
+
+ HANDLE_ERRORS(ret);
+
+ grp = fill_grent(&response.data.gr, response.extra_data);
+
+ free_response(&response);
+
+ return grp;
+}
+
+/* take a group name and return a filled struct group */
+static struct group *wb_aix_getgrnam(const char *name)
+{
+ struct winbindd_response response;
+ struct winbindd_request request;
+ NSS_STATUS ret;
+ struct group *grp;
+
+ if (*name == WB_AIX_ENCODED) {
+ return wb_aix_getgrgid(decode_id(name));
+ }
+
+ logit("getgrnam '%s'\n", name);
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ STRCPY_RETNULL(request.data.groupname, name);
+
+ ret = winbindd_request(WINBINDD_GETGRNAM, &request, &response);
+
+ HANDLE_ERRORS(ret);
+
+ grp = fill_grent(&response.data.gr, response.extra_data);
+
+ free_response(&response);
+
+ return grp;
+}
+
+
+/* this call doesn't have to fill in the gr_mem, but we do anyway
+ for simplicity */
+static struct group *wb_aix_getgracct(void *id, int type)
+{
+ if (type == 1) {
+ return wb_aix_getgrnam((char *)id);
+ }
+ if (type == 0) {
+ return wb_aix_getgrgid(*(int *)id);
+ }
+ errno = EINVAL;
+ return NULL;
+}
+
+
+/* take a username and return a string containing a comma-separated
+ list of group id numbers to which the user belongs */
+static char *wb_aix_getgrset(char *user)
+{
+ struct winbindd_response response;
+ struct winbindd_request request;
+ NSS_STATUS ret;
+ int i, idx;
+ char *tmpbuf;
+ int num_gids;
+ gid_t *gid_list;
+ char *r_user = user;
+
+ if (*user == WB_AIX_ENCODED) {
+ r_user = decode_user(r_user);
+ if (!r_user) {
+ errno = ENOENT;
+ return NULL;
+ }
+ }
+
+ logit("getgrset '%s'\n", r_user);
+
+ STRCPY_RETNULL(request.data.username, r_user);
+
+ if (*user == WB_AIX_ENCODED) {
+ free(r_user);
+ }
+
+ ret = winbindd_request(WINBINDD_GETGROUPS, &request, &response);
+
+ HANDLE_ERRORS(ret);
+
+ num_gids = response.data.num_entries;
+ gid_list = (gid_t *)response.extra_data;
+
+ /* allocate a space large enough to contruct the string */
+ tmpbuf = malloc(num_gids*12);
+ if (!tmpbuf) {
+ return NULL;
+ }
+
+ for (idx=i=0; i < num_gids-1; i++) {
+ idx += sprintf(tmpbuf+idx, "%u,", gid_list[i]);
+ }
+ idx += sprintf(tmpbuf+idx, "%u", gid_list[i]);
+
+ free_response(&response);
+
+ return tmpbuf;
+}
+
+
+/* take a uid and return a filled struct passwd */
+static struct passwd *wb_aix_getpwuid(uid_t uid)
+{
+ struct winbindd_response response;
+ struct winbindd_request request;
+ NSS_STATUS ret;
+ struct passwd *pwd;
+
+ logit("getpwuid '%d'\n", uid);
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ request.data.uid = uid;
+
+ ret = winbindd_request(WINBINDD_GETPWUID, &request, &response);
+
+ HANDLE_ERRORS(ret);
+
+ pwd = fill_pwent(&response.data.pw);
+
+ free_response(&response);
+
+ logit("getpwuid gave ptr %p\n", pwd);
+
+ return pwd;
+}
+
+
+/* take a username and return a filled struct passwd */
+static struct passwd *wb_aix_getpwnam(const char *name)
+{
+ struct winbindd_response response;
+ struct winbindd_request request;
+ NSS_STATUS ret;
+ struct passwd *pwd;
+
+ if (*name == WB_AIX_ENCODED) {
+ return wb_aix_getpwuid(decode_id(name));
+ }
+
+ logit("getpwnam '%s'\n", name);
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ STRCPY_RETNULL(request.data.username, name);
+
+ ret = winbindd_request(WINBINDD_GETPWNAM, &request, &response);
+
+ HANDLE_ERRORS(ret);
+
+ pwd = fill_pwent(&response.data.pw);
+
+ free_response(&response);
+
+ logit("getpwnam gave ptr %p\n", pwd);
+
+ return pwd;
+}
+
+/*
+ list users
+*/
+static int wb_aix_lsuser(char *attributes[], attrval_t results[], int size)
+{
+ NSS_STATUS ret;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int len;
+ char *s;
+
+ if (size != 1 || strcmp(attributes[0], S_USERS) != 0) {
+ logit("invalid lsuser op\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ ret = winbindd_request(WINBINDD_LIST_USERS, &request, &response);
+ if (ret != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ len = strlen(response.extra_data);
+
+ s = malloc(len+2);
+ if (!s) {
+ free_response(&response);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ memcpy(s, response.extra_data, len+1);
+
+ replace_commas(s);
+
+ results[0].attr_un.au_char = s;
+ results[0].attr_flag = 0;
+
+ free_response(&response);
+
+ return 0;
+}
+
+
+/*
+ list groups
+*/
+static int wb_aix_lsgroup(char *attributes[], attrval_t results[], int size)
+{
+ NSS_STATUS ret;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int len;
+ char *s;
+
+ if (size != 1 || strcmp(attributes[0], S_GROUPS) != 0) {
+ logit("invalid lsgroup op\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ ret = winbindd_request(WINBINDD_LIST_GROUPS, &request, &response);
+ if (ret != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ len = strlen(response.extra_data);
+
+ s = malloc(len+2);
+ if (!s) {
+ free_response(&response);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ memcpy(s, response.extra_data, len+1);
+
+ replace_commas(s);
+
+ results[0].attr_un.au_char = s;
+ results[0].attr_flag = 0;
+
+ free_response(&response);
+
+ return 0;
+}
+
+
+static attrval_t pwd_to_group(struct passwd *pwd)
+{
+ attrval_t r;
+ struct group *grp = wb_aix_getgrgid(pwd->pw_gid);
+
+ if (!grp) {
+ r.attr_flag = EINVAL;
+ } else {
+ r.attr_flag = 0;
+ r.attr_un.au_char = strdup(grp->gr_name);
+ free_grp(grp);
+ }
+
+ return r;
+}
+
+static attrval_t pwd_to_groupsids(struct passwd *pwd)
+{
+ attrval_t r;
+ char *s, *p;
+
+ s = wb_aix_getgrset(pwd->pw_name);
+ if (!s) {
+ r.attr_flag = EINVAL;
+ return r;
+ }
+
+ p = malloc(strlen(s)+2);
+ if (!p) {
+ r.attr_flag = ENOMEM;
+ return r;
+ }
+
+ strcpy(p, s);
+ replace_commas(p);
+ free(s);
+
+ r.attr_un.au_char = p;
+
+ return r;
+}
+
+static attrval_t pwd_to_sid(struct passwd *pwd)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ attrval_t r;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.data.uid = pwd->pw_uid;
+
+ if (winbindd_request(WINBINDD_UID_TO_SID, &request, &response) !=
+ NSS_STATUS_SUCCESS) {
+ r.attr_flag = ENOENT;
+ } else {
+ r.attr_flag = 0;
+ r.attr_un.au_char = strdup(response.data.sid.sid);
+ }
+
+ return r;
+}
+
+static int wb_aix_user_attrib(const char *key, char *attributes[],
+ attrval_t results[], int size)
+{
+ struct passwd *pwd;
+ int i;
+
+ pwd = wb_aix_getpwnam(key);
+ if (!pwd) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ for (i=0;i<size;i++) {
+ results[i].attr_flag = 0;
+
+ if (strcmp(attributes[i], S_ID) == 0) {
+ results[i].attr_un.au_int = pwd->pw_uid;
+ } else if (strcmp(attributes[i], S_PWD) == 0) {
+ results[i].attr_un.au_char = strdup(pwd->pw_passwd);
+ } else if (strcmp(attributes[i], S_HOME) == 0) {
+ results[i].attr_un.au_char = strdup(pwd->pw_dir);
+ } else if (strcmp(attributes[0], S_SHELL) == 0) {
+ results[i].attr_un.au_char = strdup(pwd->pw_shell);
+ } else if (strcmp(attributes[0], S_REGISTRY) == 0) {
+ results[i].attr_un.au_char = strdup("WINBIND");
+ } else if (strcmp(attributes[0], S_GECOS) == 0) {
+ results[i].attr_un.au_char = strdup(pwd->pw_gecos);
+ } else if (strcmp(attributes[0], S_PGRP) == 0) {
+ results[i] = pwd_to_group(pwd);
+ } else if (strcmp(attributes[0], S_GECOS) == 0) {
+ results[i].attr_un.au_char = strdup(pwd->pw_gecos);
+ } else if (strcmp(attributes[0], S_GROUPSIDS) == 0) {
+ results[i] = pwd_to_groupsids(pwd);
+ } else if (strcmp(attributes[0], "SID") == 0) {
+ results[i] = pwd_to_sid(pwd);
+ } else {
+ logit("Unknown user attribute '%s'\n", attributes[i]);
+ results[i].attr_flag = EINVAL;
+ }
+ }
+
+ free_pwd(pwd);
+
+ return 0;
+}
+
+static int wb_aix_group_attrib(const char *key, char *attributes[],
+ attrval_t results[], int size)
+{
+ struct group *grp;
+ int i;
+
+ grp = wb_aix_getgrnam(key);
+ if (!grp) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ for (i=0;i<size;i++) {
+ results[i].attr_flag = 0;
+
+ if (strcmp(attributes[i], S_PWD) == 0) {
+ results[i].attr_un.au_char = strdup(grp->gr_passwd);
+ } else if (strcmp(attributes[i], S_ID) == 0) {
+ results[i].attr_un.au_int = grp->gr_gid;
+ } else {
+ logit("Unknown group attribute '%s'\n", attributes[i]);
+ results[i].attr_flag = EINVAL;
+ }
+ }
+
+ free_grp(grp);
+
+ return 0;
+}
+
+
+/*
+ called for user/group enumerations
+*/
+static int wb_aix_getentry(char *key, char *table, char *attributes[],
+ attrval_t results[], int size)
+{
+ logit("Got getentry with key='%s' table='%s' size=%d attributes[0]='%s'\n",
+ key, table, size, attributes[0]);
+
+ if (strcmp(key, "ALL") == 0 &&
+ strcmp(table, "user") == 0) {
+ return wb_aix_lsuser(attributes, results, size);
+ }
+
+ if (strcmp(key, "ALL") == 0 &&
+ strcmp(table, "group") == 0) {
+ return wb_aix_lsgroup(attributes, results, size);
+ }
+
+ if (strcmp(table, "user") == 0) {
+ return wb_aix_user_attrib(key, attributes, results, size);
+ }
+
+ if (strcmp(table, "group") == 0) {
+ return wb_aix_group_attrib(key, attributes, results, size);
+ }
+
+ logit("Unknown getentry operation key='%s' table='%s'\n", key, table);
+
+ errno = ENOSYS;
+ return -1;
+}
+
+
+
+/*
+ called to start the backend
+*/
+static void *wb_aix_open(const char *name, const char *domain, int mode, char *options)
+{
+ if (strstr(options, "debug")) {
+ debug_enabled = 1;
+ }
+ logit("open name='%s' mode=%d domain='%s' options='%s'\n", name, domain,
+ mode, options);
+ return NULL;
+}
+
+static void wb_aix_close(void *token)
+{
+ logit("close\n");
+ return;
+}
+
+/*
+ return a list of additional attributes supported by the backend
+*/
+static attrlist_t **wb_aix_attrlist(void)
+{
+ attrlist_t **ret;
+ logit("method attrlist called\n");
+ ret = malloc(2*sizeof(attrlist_t *) + sizeof(attrlist_t));
+ if (!ret) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ ret[0] = (attrlist_t *)(ret+2);
+
+ /* just one extra attribute - the windows SID */
+ ret[0]->al_name = strdup("SID");
+ ret[0]->al_flags = AL_USERATTR;
+ ret[0]->al_type = SEC_CHAR;
+ ret[1] = NULL;
+
+ return ret;
+}
+
+
+/*
+ turn a long username into a short one. Needed to cope with the 8 char
+ username limit in AIX 5.2 and below
+*/
+static int wb_aix_normalize(char *longname, char *shortname)
+{
+ struct passwd *pwd;
+
+ logit("normalize '%s'\n", longname);
+
+ /* automatically cope with AIX 5.3 with longer usernames
+ when it comes out */
+ if (S_NAMELEN > strlen(longname)) {
+ strcpy(shortname, longname);
+ return 1;
+ }
+
+ pwd = wb_aix_getpwnam(longname);
+ if (!pwd) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ sprintf(shortname, "%c%07u", WB_AIX_ENCODED, pwd->pw_uid);
+
+ free_pwd(pwd);
+
+ return 1;
+}
+
+
+/*
+ authenticate a user
+ */
+static int wb_aix_authenticate(char *user, char *pass,
+ int *reenter, char **message)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ char *r_user = user;
+
+ logit("authenticate '%s' response='%s'\n", user, pass);
+
+ *reenter = 0;
+ *message = NULL;
+
+ /* Send off request */
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (*user == WB_AIX_ENCODED) {
+ r_user = decode_user(r_user);
+ if (!r_user) {
+ return AUTH_NOTFOUND;
+ }
+ }
+
+ STRCPY_RET(request.data.auth.user, r_user);
+ STRCPY_RET(request.data.auth.pass, pass);
+
+ if (*user == WB_AIX_ENCODED) {
+ free(r_user);
+ }
+
+ result = winbindd_request(WINBINDD_PAM_AUTH, &request, &response);
+
+ free_response(&response);
+
+ logit("auth result %d for '%s'\n", result, user);
+
+ if (result == NSS_STATUS_SUCCESS) {
+ errno = 0;
+ return AUTH_SUCCESS;
+ }
+
+ return AUTH_FAILURE;
+}
+
+
+/*
+ change a user password
+*/
+static int wb_aix_chpass(char *user, char *oldpass, char *newpass, char **message)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ char *r_user = user;
+
+ if (*user == WB_AIX_ENCODED) {
+ r_user = decode_user(r_user);
+ if (!r_user) {
+ errno = ENOENT;
+ return -1;
+ }
+ }
+
+ logit("chpass '%s' old='%s' new='%s'\n", r_user, oldpass, newpass);
+
+ *message = NULL;
+
+ /* Send off request */
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ STRCPY_RET(request.data.chauthtok.user, r_user);
+ STRCPY_RET(request.data.chauthtok.oldpass, oldpass);
+ STRCPY_RET(request.data.chauthtok.newpass, newpass);
+
+ if (*user == WB_AIX_ENCODED) {
+ free(r_user);
+ }
+
+ result = winbindd_request(WINBINDD_PAM_CHAUTHTOK, &request, &response);
+
+ free_response(&response);
+
+ if (result == NSS_STATUS_SUCCESS) {
+ errno = 0;
+ return 0;
+ }
+
+ errno = EINVAL;
+ return -1;
+}
+
+/*
+ don't do any password strength testing for now
+*/
+static int wb_aix_passwdrestrictions(char *user, char *newpass, char *oldpass,
+ char **message)
+{
+ logit("passwdresrictions called for '%s'\n", user);
+ return 0;
+}
+
+
+static int wb_aix_passwdexpired(char *user, char **message)
+{
+ logit("passwdexpired '%s'\n", user);
+ /* we should check the account bits here */
+ return 0;
+}
+
+
+/*
+ we can't return a crypt() password
+*/
+static char *wb_aix_getpasswd(char *user)
+{
+ logit("getpasswd '%s'\n", user);
+ errno = ENOSYS;
+ return NULL;
+}
+
+/*
+ this is called to update things like the last login time. We don't
+ currently pass this onto the DC
+*/
+static int wb_aix_putentry(char *key, char *table, char *attributes[],
+ attrval_t values[], int size)
+{
+ logit("putentry key='%s' table='%s' attrib='%s'\n",
+ key, table, size>=1?attributes[0]:"<null>");
+ errno = ENOSYS;
+ return -1;
+}
+
+static int wb_aix_commit(char *key, char *table)
+{
+ logit("commit key='%s' table='%s'\n");
+ errno = ENOSYS;
+ return -1;
+}
+
+static int wb_aix_getgrusers(char *group, void *result, int type, int *size)
+{
+ logit("getgrusers group='%s'\n", group);
+ errno = ENOSYS;
+ return -1;
+}
+
+
+#define DECL_METHOD(x) \
+int method_ ## x(void) \
+{ \
+ logit("UNIMPLEMENTED METHOD '%s'\n", #x); \
+ errno = EINVAL; \
+ return -1; \
+}
+
+#if LOG_UNIMPLEMENTED_CALLS
+DECL_METHOD(delgroup);
+DECL_METHOD(deluser);
+DECL_METHOD(newgroup);
+DECL_METHOD(newuser);
+DECL_METHOD(putgrent);
+DECL_METHOD(putgrusers);
+DECL_METHOD(putpwent);
+DECL_METHOD(lock);
+DECL_METHOD(unlock);
+DECL_METHOD(getcred);
+DECL_METHOD(setcred);
+DECL_METHOD(deletecred);
+#endif
+
+int wb_aix_init(struct secmethod_table *methods)
+{
+ ZERO_STRUCTP(methods);
+
+ methods->method_version = SECMETHOD_VERSION_520;
+
+ methods->method_getgrgid = wb_aix_getgrgid;
+ methods->method_getgrnam = wb_aix_getgrnam;
+ methods->method_getgrset = wb_aix_getgrset;
+ methods->method_getpwnam = wb_aix_getpwnam;
+ methods->method_getpwuid = wb_aix_getpwuid;
+ methods->method_getentry = wb_aix_getentry;
+ methods->method_open = wb_aix_open;
+ methods->method_close = wb_aix_close;
+ methods->method_normalize = wb_aix_normalize;
+ methods->method_passwdexpired = wb_aix_passwdexpired;
+ methods->method_putentry = wb_aix_putentry;
+ methods->method_getpasswd = wb_aix_getpasswd;
+ methods->method_authenticate = wb_aix_authenticate;
+ methods->method_commit = wb_aix_commit;
+ methods->method_chpass = wb_aix_chpass;
+ methods->method_passwdrestrictions = wb_aix_passwdrestrictions;
+ methods->method_getgracct = wb_aix_getgracct;
+ methods->method_getgrusers = wb_aix_getgrusers;
+ methods->method_attrlist = wb_aix_attrlist;
+
+#if LOG_UNIMPLEMENTED_CALLS
+ methods->method_delgroup = method_delgroup;
+ methods->method_deluser = method_deluser;
+ methods->method_newgroup = method_newgroup;
+ methods->method_newuser = method_newuser;
+ methods->method_putgrent = method_putgrent;
+ methods->method_putgrusers = method_putgrusers;
+ methods->method_putpwent = method_putpwent;
+ methods->method_lock = method_lock;
+ methods->method_unlock = method_unlock;
+ methods->method_getcred = method_getcred;
+ methods->method_setcred = method_setcred;
+ methods->method_deletecred = method_deletecred;
+#endif
+
+ return AUTH_SUCCESS;
+}
+
diff --git a/source/nsswitch/winbind_nss_config.h b/source/nsswitch/winbind_nss_config.h
new file mode 100644
index 00000000000..77d1dbe26e0
--- /dev/null
+++ b/source/nsswitch/winbind_nss_config.h
@@ -0,0 +1,142 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _WINBIND_NSS_CONFIG_H
+#define _WINBIND_NSS_CONFIG_H
+
+/* Include header files from data in config.h file */
+
+#ifndef NO_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_UNIXSOCKET
+#include <sys/un.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#else
+#ifdef HAVE_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <pwd.h>
+#include "nsswitch/winbind_nss.h"
+
+/* I'm trying really hard not to include anything from smb.h with the
+ result of some silly looking redeclaration of structures. */
+
+#ifndef _PSTRING
+#define _PSTRING
+#define PSTRING_LEN 1024
+#define FSTRING_LEN 256
+typedef char pstring[PSTRING_LEN];
+typedef char fstring[FSTRING_LEN];
+#endif
+
+#ifndef _BOOL
+#define _BOOL /* So we don't typedef BOOL again in vfs.h */
+#define False (0)
+#define True (1)
+#define Auto (2)
+typedef int BOOL;
+#endif
+
+#if !defined(uint32)
+#if (SIZEOF_INT == 4)
+#define uint32 unsigned int
+#elif (SIZEOF_LONG == 4)
+#define uint32 unsigned long
+#elif (SIZEOF_SHORT == 4)
+#define uint32 unsigned short
+#endif
+#endif
+
+#if !defined(uint16)
+#if (SIZEOF_SHORT == 4)
+#define uint16 __ERROR___CANNOT_DETERMINE_TYPE_FOR_INT16;
+#else /* SIZEOF_SHORT != 4 */
+#define uint16 unsigned short
+#endif /* SIZEOF_SHORT != 4 */
+#endif
+
+#ifndef uint8
+#define uint8 unsigned char
+#endif
+
+/* zero a structure */
+#ifndef ZERO_STRUCT
+#define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x))
+#endif
+
+/* zero a structure given a pointer to the structure */
+#ifndef ZERO_STRUCTP
+#define ZERO_STRUCTP(x) { if ((x) != NULL) memset((char *)(x), 0, sizeof(*(x))); }
+#endif
+
+/* Some systems (SCO) treat UNIX domain sockets as FIFOs */
+
+#ifndef S_IFSOCK
+#define S_IFSOCK S_IFIFO
+#endif
+
+#ifndef S_ISSOCK
+#define S_ISSOCK(mode) ((mode & S_IFSOCK) == S_IFSOCK)
+#endif
+
+#endif
diff --git a/source/nsswitch/winbind_nss_freebsd.c b/source/nsswitch/winbind_nss_freebsd.c
new file mode 100644
index 00000000000..b73a4ce44f8
--- /dev/null
+++ b/source/nsswitch/winbind_nss_freebsd.c
@@ -0,0 +1,81 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ AIX loadable authentication module, providing identification
+ routines against Samba winbind/Windows NT Domain
+
+ Copyright (C) Aaron Collins 2003
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "winbind_client.h"
+
+/* Make sure that the module gets registered needed by freebsd 5.1 */
+
+extern enum nss_status _nss_winbind_getgrent_r(struct group *, char *, size_t,
+ int *);
+extern enum nss_status _nss_winbind_getgrnam_r(const char *, struct group *,
+ char *, size_t, int *);
+extern enum nss_status _nss_winbind_getgrgid_r(gid_t gid, struct group *, char *,
+ size_t, int *);
+extern enum nss_status _nss_winbind_setgrent(void);
+extern enum nss_status _nss_winbind_endgrent(void);
+
+extern enum nss_status _nss_winbind_getpwent_r(struct passwd *, char *, size_t,
+ int *);
+extern enum nss_status _nss_winbind_getpwnam_r(const char *, struct passwd *,
+ char *, size_t, int *);
+extern enum nss_status _nss_winbind_getpwuid_r(gid_t gid, struct passwd *, char *,
+ size_t, int *);
+extern enum nss_status _nss_winbind_setpwent(void);
+extern enum nss_status _nss_winbind_endpwent(void);
+
+NSS_METHOD_PROTOTYPE(__nss_compat_getgrnam_r);
+NSS_METHOD_PROTOTYPE(__nss_compat_getgrgid_r);
+NSS_METHOD_PROTOTYPE(__nss_compat_getgrent_r);
+NSS_METHOD_PROTOTYPE(__nss_compat_setgrent);
+NSS_METHOD_PROTOTYPE(__nss_compat_endgrent);
+
+NSS_METHOD_PROTOTYPE(__nss_compat_getpwnam_r);
+NSS_METHOD_PROTOTYPE(__nss_compat_getpwuid_r);
+NSS_METHOD_PROTOTYPE(__nss_compat_getpwent_r);
+NSS_METHOD_PROTOTYPE(__nss_compat_setpwent);
+NSS_METHOD_PROTOTYPE(__nss_compat_endpwent);
+
+static ns_mtab methods[] = {
+{ NSDB_GROUP, "getgrnam_r", __nss_compat_getgrnam_r, _nss_winbind_getgrnam_r },
+{ NSDB_GROUP, "getgrgid_r", __nss_compat_getgrgid_r, _nss_winbind_getgrgid_r },
+{ NSDB_GROUP, "getgrent_r", __nss_compat_getgrent_r, _nss_winbind_getgrent_r },
+{ NSDB_GROUP, "endgrent", __nss_compat_setgrent, _nss_winbind_setgrent },
+{ NSDB_GROUP, "setgrent", __nss_compat_endgrent, _nss_winbind_endgrent },
+
+{ NSDB_PASSWD, "getpwnam_r", __nss_compat_getpwnam_r, _nss_winbind_getpwnam_r },
+{ NSDB_PASSWD, "getpwuid_r", __nss_compat_getpwuid_r, _nss_winbind_getpwuid_r },
+{ NSDB_PASSWD, "getpwent_r", __nss_compat_getpwent_r, _nss_winbind_getpwent_r },
+{ NSDB_PASSWD, "endpwent", __nss_compat_setpwent, _nss_winbind_setpwent },
+{ NSDB_PASSWD, "setpwent", __nss_compat_endpwent, _nss_winbind_endpwent },
+
+};
+
+ns_mtab *
+nss_module_register(const char *source, unsigned int *mtabsize,
+ nss_module_unregister_fn *unreg)
+{
+ *mtabsize = sizeof(methods)/sizeof(methods[0]);
+ *unreg = NULL;
+ return (methods);
+}
diff --git a/source/nsswitch/winbind_nss_hpux.h b/source/nsswitch/winbind_nss_hpux.h
new file mode 100644
index 00000000000..d2a5057bf51
--- /dev/null
+++ b/source/nsswitch/winbind_nss_hpux.h
@@ -0,0 +1,139 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Donated by HP to enable Winbindd to build on HPUX 11.x.
+ Copyright (C) Jeremy Allison 2002.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _WINBIND_NSS_HPUX_H
+#define _WINBIND_NSS_HPUX_H
+
+#include <nsswitch.h>
+
+#define NSS_STATUS_SUCCESS NSS_SUCCESS
+#define NSS_STATUS_NOTFOUND NSS_NOTFOUND
+#define NSS_STATUS_UNAVAIL NSS_UNAVAIL
+#define NSS_STATUS_TRYAGAIN NSS_TRYAGAIN
+
+#ifdef HAVE_SYNCH_H
+#include <synch.h>
+#endif
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+
+typedef enum {
+ NSS_SUCCESS,
+ NSS_NOTFOUND,
+ NSS_UNAVAIL,
+ NSS_TRYAGAIN
+} nss_status_t;
+
+typedef nss_status_t NSS_STATUS;
+
+struct nss_backend;
+
+typedef nss_status_t (*nss_backend_op_t)(struct nss_backend *, void *args);
+
+struct nss_backend {
+ nss_backend_op_t *ops;
+ int n_ops;
+};
+typedef struct nss_backend nss_backend_t;
+typedef int nss_dbop_t;
+
+#include <errno.h>
+#include <netdb.h>
+#include <limits.h>
+
+#ifndef NSS_INCLUDE_UNSAFE
+#define NSS_INCLUDE_UNSAFE 1 /* Build old, MT-unsafe interfaces, */
+#endif /* NSS_INCLUDE_UNSAFE */
+
+enum nss_netgr_argn {
+ NSS_NETGR_MACHINE,
+ NSS_NETGR_USER,
+ NSS_NETGR_DOMAIN,
+ NSS_NETGR_N
+};
+
+enum nss_netgr_status {
+ NSS_NETGR_FOUND,
+ NSS_NETGR_NO,
+ NSS_NETGR_NOMEM
+};
+
+typedef unsigned nss_innetgr_argc;
+typedef char **nss_innetgr_argv;
+
+struct nss_innetgr_1arg {
+ nss_innetgr_argc argc;
+ nss_innetgr_argv argv;
+};
+
+typedef struct {
+ void *result; /* "result" parameter to getXbyY_r() */
+ char *buffer; /* "buffer" " " */
+ int buflen; /* "buflen" " " */
+} nss_XbyY_buf_t;
+
+extern nss_XbyY_buf_t *_nss_XbyY_buf_alloc(int struct_size, int buffer_size);
+extern void _nss_XbyY_buf_free(nss_XbyY_buf_t *);
+
+union nss_XbyY_key {
+ uid_t uid;
+ gid_t gid;
+ const char *name;
+ int number;
+ struct {
+ long net;
+ int type;
+ } netaddr;
+ struct {
+ const char *addr;
+ int len;
+ int type;
+ } hostaddr;
+ struct {
+ union {
+ const char *name;
+ int port;
+ } serv;
+ const char *proto;
+ } serv;
+ void *ether;
+};
+
+typedef struct nss_XbyY_args {
+ nss_XbyY_buf_t buf;
+ int stayopen;
+ /*
+ * Support for setXXXent(stayopen)
+ * Used only in hosts, protocols,
+ * networks, rpc, and services.
+ */
+ int (*str2ent)(const char *instr, int instr_len, void *ent, char *buffer, int buflen);
+ union nss_XbyY_key key;
+
+ void *returnval;
+ int erange;
+ int h_errno;
+ nss_status_t status;
+} nss_XbyY_args_t;
+
+#endif /* _WINBIND_NSS_HPUX_H */
diff --git a/source/nsswitch/winbind_nss_irix.c b/source/nsswitch/winbind_nss_irix.c
new file mode 100644
index 00000000000..3a9d6c01ab5
--- /dev/null
+++ b/source/nsswitch/winbind_nss_irix.c
@@ -0,0 +1,515 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Windows NT Domain nsswitch module
+
+ Copyright (C) Tim Potter 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "winbind_client.h"
+
+#ifdef HAVE_NS_API_H
+#undef VOLATILE
+#include <ns_daemon.h>
+#endif
+
+/* Maximum number of users to pass back over the unix domain socket
+ per call. This is not a static limit on the total number of users
+ or groups returned in total. */
+
+#define MAX_GETPWENT_USERS 250
+#define MAX_GETGRENT_USERS 250
+
+/* Prototypes from wb_common.c */
+
+extern int winbindd_fd;
+
+#ifdef HAVE_NS_API_H
+
+/* IRIX version */
+
+static int send_next_request(nsd_file_t *, struct winbindd_request *);
+static int do_list(int state, nsd_file_t *rq);
+
+static nsd_file_t *current_rq = NULL;
+static int current_winbind_xid = 0;
+static int next_winbind_xid = 0;
+
+typedef struct winbind_xid {
+ int xid;
+ nsd_file_t *rq;
+ struct winbindd_request *request;
+ struct winbind_xid *next;
+} winbind_xid_t;
+
+static winbind_xid_t *winbind_xids = (winbind_xid_t *)0;
+
+static int
+winbind_xid_new(int xid, nsd_file_t *rq, struct winbindd_request *request)
+{
+ winbind_xid_t *new;
+
+ nsd_logprintf(NSD_LOG_LOW,
+ "entering winbind_xid_new xid = %d rq = 0x%x, request = 0x%x\n",
+ xid, rq, request);
+ new = (winbind_xid_t *)nsd_calloc(1,sizeof(winbind_xid_t));
+ if (!new) {
+ nsd_logprintf(NSD_LOG_RESOURCE,"winbind_xid_new: failed malloc\n");
+ return NSD_ERROR;
+ }
+
+ new->xid = xid;
+ new->rq = rq;
+ new->request = request;
+ new->next = winbind_xids;
+ winbind_xids = new;
+
+ return NSD_CONTINUE;
+}
+
+/*
+** This routine will look down the xid list and return the request
+** associated with an xid. We remove the record if it is found.
+*/
+nsd_file_t *
+winbind_xid_lookup(int xid, struct winbindd_request **requestp)
+{
+ winbind_xid_t **last, *dx;
+ nsd_file_t *result=0;
+
+ for (last = &winbind_xids, dx = winbind_xids; dx && (dx->xid != xid);
+ last = &dx->next, dx = dx->next);
+ if (dx) {
+ *last = dx->next;
+ result = dx->rq;
+ *requestp = dx->request;
+ SAFE_FREE(dx);
+ }
+ nsd_logprintf(NSD_LOG_LOW,
+ "entering winbind_xid_lookup xid = %d rq = 0x%x, request = 0x%x\n",
+ xid, result, dx->request);
+
+ return result;
+}
+
+static int
+winbind_startnext_timeout(nsd_file_t **rqp, nsd_times_t *to)
+{
+ nsd_file_t *rq;
+ struct winbindd_request *request;
+
+ nsd_logprintf(NSD_LOG_MIN, "timeout (winbind startnext)\n");
+ rq = to->t_file;
+ *rqp = rq;
+ nsd_timeout_remove(rq);
+ request = to->t_clientdata;
+ return(send_next_request(rq, request));
+}
+
+static void
+dequeue_request()
+{
+ nsd_file_t *rq;
+ struct winbindd_request *request;
+
+ /*
+ * Check for queued requests
+ */
+ if (winbind_xids) {
+ nsd_logprintf(NSD_LOG_MIN, "timeout (winbind) unqueue xid %d\n",
+ current_winbind_xid);
+ rq = winbind_xid_lookup(current_winbind_xid++, &request);
+ /* cause a timeout on the queued request so we can send it */
+ nsd_timeout_new(rq,1,winbind_startnext_timeout,request);
+ }
+}
+
+static int
+do_request(nsd_file_t *rq, struct winbindd_request *request)
+{
+ if (winbind_xids == NULL) {
+ /*
+ * No outstanding requests.
+ * Send off the request to winbindd
+ */
+ nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) sending request\n");
+ return(send_next_request(rq, request));
+ } else {
+ /*
+ * Just queue it up for now - previous callout or timout
+ * will start it up
+ */
+ nsd_logprintf(NSD_LOG_MIN,
+ "lookup (winbind): queue request xid = %d\n",
+ next_winbind_xid);
+ return(winbind_xid_new(next_winbind_xid++, rq, request));
+ }
+}
+
+static int
+winbind_callback(nsd_file_t **rqp, int fd)
+{
+ struct winbindd_response response;
+ struct winbindd_pw *pw = &response.data.pw;
+ struct winbindd_gr *gr = &response.data.gr;
+ nsd_file_t *rq;
+ NSS_STATUS status;
+ fstring result;
+ char *members;
+ int i, maxlen;
+
+ dequeue_request();
+
+ nsd_logprintf(NSD_LOG_MIN, "entering callback (winbind)\n");
+
+ rq = current_rq;
+ *rqp = rq;
+
+ nsd_timeout_remove(rq);
+ nsd_callback_remove(fd);
+
+ ZERO_STRUCT(response);
+ status = winbindd_get_response(&response);
+
+ if (status != NSS_STATUS_SUCCESS) {
+ /* free any extra data area in response structure */
+ free_response(&response);
+ nsd_logprintf(NSD_LOG_MIN,
+ "callback (winbind) returning not found, status = %d\n",
+ status);
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+ }
+
+ maxlen = sizeof(result) - 1;
+
+ switch ((int)rq->f_cmd_data) {
+ case WINBINDD_WINS_BYNAME:
+ case WINBINDD_WINS_BYIP:
+ snprintf(result,maxlen,"%s\n",response.data.winsresp);
+ break;
+ case WINBINDD_GETPWUID:
+ case WINBINDD_GETPWNAM:
+ snprintf(result,maxlen,"%s:%s:%d:%d:%s:%s:%s\n",
+ pw->pw_name,
+ pw->pw_passwd,
+ pw->pw_uid,
+ pw->pw_gid,
+ pw->pw_gecos,
+ pw->pw_dir,
+ pw->pw_shell);
+ break;
+ case WINBINDD_GETGRNAM:
+ case WINBINDD_GETGRGID:
+ if (gr->num_gr_mem && response.extra_data)
+ members = response.extra_data;
+ else
+ members = "";
+ snprintf(result,maxlen,"%s:%s:%d:%s\n",
+ gr->gr_name, gr->gr_passwd, gr->gr_gid, members);
+ break;
+ case WINBINDD_SETGRENT:
+ case WINBINDD_SETPWENT:
+ nsd_logprintf(NSD_LOG_MIN, "callback (winbind) - SETPWENT/SETGRENT\n");
+ free_response(&response);
+ return(do_list(1,rq));
+ case WINBINDD_GETGRENT:
+ case WINBINDD_GETGRLST:
+ nsd_logprintf(NSD_LOG_MIN,
+ "callback (winbind) - %d GETGRENT responses\n",
+ response.data.num_entries);
+ if (response.data.num_entries) {
+ gr = (struct winbindd_gr *)response.extra_data;
+ if (! gr ) {
+ nsd_logprintf(NSD_LOG_MIN, " no extra_data\n");
+ free_response(&response);
+ return NSD_ERROR;
+ }
+ members = (char *)response.extra_data +
+ (response.data.num_entries * sizeof(struct winbindd_gr));
+ for (i = 0; i < response.data.num_entries; i++) {
+ snprintf(result,maxlen,"%s:%s:%d:%s\n",
+ gr->gr_name, gr->gr_passwd, gr->gr_gid,
+ &members[gr->gr_mem_ofs]);
+ nsd_logprintf(NSD_LOG_MIN, " GETGRENT %s\n",result);
+ nsd_append_element(rq,NS_SUCCESS,result,strlen(result));
+ gr++;
+ }
+ }
+ i = response.data.num_entries;
+ free_response(&response);
+ if (i < MAX_GETPWENT_USERS)
+ return(do_list(2,rq));
+ else
+ return(do_list(1,rq));
+ case WINBINDD_GETPWENT:
+ nsd_logprintf(NSD_LOG_MIN,
+ "callback (winbind) - %d GETPWENT responses\n",
+ response.data.num_entries);
+ if (response.data.num_entries) {
+ pw = (struct winbindd_pw *)response.extra_data;
+ if (! pw ) {
+ nsd_logprintf(NSD_LOG_MIN, " no extra_data\n");
+ free_response(&response);
+ return NSD_ERROR;
+ }
+ for (i = 0; i < response.data.num_entries; i++) {
+ snprintf(result,maxlen,"%s:%s:%d:%d:%s:%s:%s",
+ pw->pw_name,
+ pw->pw_passwd,
+ pw->pw_uid,
+ pw->pw_gid,
+ pw->pw_gecos,
+ pw->pw_dir,
+ pw->pw_shell);
+ nsd_logprintf(NSD_LOG_MIN, " GETPWENT %s\n",result);
+ nsd_append_element(rq,NS_SUCCESS,result,strlen(result));
+ pw++;
+ }
+ }
+ i = response.data.num_entries;
+ free_response(&response);
+ if (i < MAX_GETPWENT_USERS)
+ return(do_list(2,rq));
+ else
+ return(do_list(1,rq));
+ case WINBINDD_ENDGRENT:
+ case WINBINDD_ENDPWENT:
+ nsd_logprintf(NSD_LOG_MIN, "callback (winbind) - ENDPWENT/ENDGRENT\n");
+ nsd_append_element(rq,NS_SUCCESS,"\n",1);
+ free_response(&response);
+ return NSD_NEXT;
+ default:
+ free_response(&response);
+ nsd_logprintf(NSD_LOG_MIN, "callback (winbind) - no valid command\n");
+ return NSD_NEXT;
+ }
+ nsd_logprintf(NSD_LOG_MIN, "callback (winbind) %s\n", result);
+ /* free any extra data area in response structure */
+ free_response(&response);
+ nsd_set_result(rq,NS_SUCCESS,result,strlen(result),VOLATILE);
+ return NSD_OK;
+}
+
+static int
+winbind_timeout(nsd_file_t **rqp, nsd_times_t *to)
+{
+ nsd_file_t *rq;
+
+ dequeue_request();
+
+ nsd_logprintf(NSD_LOG_MIN, "timeout (winbind)\n");
+
+ rq = to->t_file;
+ *rqp = rq;
+
+ /* Remove the callback and timeout */
+ nsd_callback_remove(winbindd_fd);
+ nsd_timeout_remove(rq);
+
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+}
+
+static int
+send_next_request(nsd_file_t *rq, struct winbindd_request *request)
+{
+ NSS_STATUS status;
+ long timeout;
+
+ timeout = 1000;
+
+ nsd_logprintf(NSD_LOG_MIN, "send_next_request (winbind) %d to = %d\n",
+ rq->f_cmd_data, timeout);
+ status = winbindd_send_request((int)rq->f_cmd_data,request);
+ SAFE_FREE(request);
+
+ if (status != NSS_STATUS_SUCCESS) {
+ nsd_logprintf(NSD_LOG_MIN,
+ "send_next_request (winbind) error status = %d\n",status);
+ rq->f_status = status;
+ return NSD_NEXT;
+ }
+
+ current_rq = rq;
+
+ /*
+ * Set up callback and timeouts
+ */
+ nsd_logprintf(NSD_LOG_MIN, "send_next_request (winbind) fd = %d\n",winbindd_fd);
+ nsd_callback_new(winbindd_fd,winbind_callback,NSD_READ);
+ nsd_timeout_new(rq,timeout,winbind_timeout,(void *)0);
+ return NSD_CONTINUE;
+}
+
+int init(void)
+{
+ nsd_logprintf(NSD_LOG_MIN, "entering init (winbind)\n");
+ return(NSD_OK);
+}
+
+int lookup(nsd_file_t *rq)
+{
+ char *map;
+ char *key;
+ struct winbindd_request *request;
+
+ nsd_logprintf(NSD_LOG_MIN, "entering lookup (winbind)\n");
+ if (! rq)
+ return NSD_ERROR;
+
+ map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
+ key = nsd_attr_fetch_string(rq->f_attrs, "key", (char*)0);
+ if (! map || ! key) {
+ nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) table or key not defined\n");
+ rq->f_status = NS_BADREQ;
+ return NSD_ERROR;
+ }
+
+ nsd_logprintf(NSD_LOG_MIN, "lookup (winbind %s)\n",map);
+
+ request = (struct winbindd_request *)nsd_calloc(1,sizeof(struct winbindd_request));
+ if (! request) {
+ nsd_logprintf(NSD_LOG_RESOURCE,
+ "lookup (winbind): failed malloc\n");
+ return NSD_ERROR;
+ }
+
+ if (strcasecmp(map,"passwd.byuid") == 0) {
+ request->data.uid = atoi(key);
+ rq->f_cmd_data = (void *)WINBINDD_GETPWUID;
+ } else if (strcasecmp(map,"passwd.byname") == 0) {
+ strncpy(request->data.username, key,
+ sizeof(request->data.username) - 1);
+ request->data.username[sizeof(request->data.username) - 1] = '\0';
+ rq->f_cmd_data = (void *)WINBINDD_GETPWNAM;
+ } else if (strcasecmp(map,"group.byname") == 0) {
+ strncpy(request->data.groupname, key,
+ sizeof(request->data.groupname) - 1);
+ request->data.groupname[sizeof(request->data.groupname) - 1] = '\0';
+ rq->f_cmd_data = (void *)WINBINDD_GETGRNAM;
+ } else if (strcasecmp(map,"group.bygid") == 0) {
+ request->data.gid = atoi(key);
+ rq->f_cmd_data = (void *)WINBINDD_GETGRGID;
+ } else if (strcasecmp(map,"hosts.byname") == 0) {
+ strncpy(request->data.winsreq, key, sizeof(request->data.winsreq) - 1);
+ request->data.winsreq[sizeof(request->data.winsreq) - 1] = '\0';
+ rq->f_cmd_data = (void *)WINBINDD_WINS_BYNAME;
+ } else if (strcasecmp(map,"hosts.byaddr") == 0) {
+ strncpy(request->data.winsreq, key, sizeof(request->data.winsreq) - 1);
+ request->data.winsreq[sizeof(request->data.winsreq) - 1] = '\0';
+ rq->f_cmd_data = (void *)WINBINDD_WINS_BYIP;
+ } else {
+ /*
+ * Don't understand this map - just return not found
+ */
+ nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) unknown table\n");
+ SAFE_FREE(request);
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+ }
+
+ return(do_request(rq, request));
+}
+
+int list(nsd_file_t *rq)
+{
+ char *map;
+
+ nsd_logprintf(NSD_LOG_MIN, "entering list (winbind)\n");
+ if (! rq)
+ return NSD_ERROR;
+
+ map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
+ if (! map ) {
+ nsd_logprintf(NSD_LOG_MIN, "list (winbind) table not defined\n");
+ rq->f_status = NS_BADREQ;
+ return NSD_ERROR;
+ }
+
+ nsd_logprintf(NSD_LOG_MIN, "list (winbind %s)\n",map);
+
+ return (do_list(0,rq));
+}
+
+static int
+do_list(int state, nsd_file_t *rq)
+{
+ char *map;
+ struct winbindd_request *request;
+
+ nsd_logprintf(NSD_LOG_MIN, "entering do_list (winbind) state = %d\n",state);
+
+ map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
+ request = (struct winbindd_request *)nsd_calloc(1,sizeof(struct winbindd_request));
+ if (! request) {
+ nsd_logprintf(NSD_LOG_RESOURCE,
+ "do_list (winbind): failed malloc\n");
+ return NSD_ERROR;
+ }
+
+ if (strcasecmp(map,"passwd.byname") == 0) {
+ switch (state) {
+ case 0:
+ rq->f_cmd_data = (void *)WINBINDD_SETPWENT;
+ break;
+ case 1:
+ request->data.num_entries = MAX_GETPWENT_USERS;
+ rq->f_cmd_data = (void *)WINBINDD_GETPWENT;
+ break;
+ case 2:
+ rq->f_cmd_data = (void *)WINBINDD_ENDPWENT;
+ break;
+ default:
+ nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown state\n");
+ SAFE_FREE(request);
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+ }
+ } else if (strcasecmp(map,"group.byname") == 0) {
+ switch (state) {
+ case 0:
+ rq->f_cmd_data = (void *)WINBINDD_SETGRENT;
+ break;
+ case 1:
+ request->data.num_entries = MAX_GETGRENT_USERS;
+ rq->f_cmd_data = (void *)WINBINDD_GETGRENT;
+ break;
+ case 2:
+ rq->f_cmd_data = (void *)WINBINDD_ENDGRENT;
+ break;
+ default:
+ nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown state\n");
+ SAFE_FREE(request);
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+ }
+ } else {
+ /*
+ * Don't understand this map - just return not found
+ */
+ nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown table\n");
+ SAFE_FREE(request);
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+ }
+
+ return(do_request(rq, request));
+}
+
+#endif /* HAVE_NS_API_H */
diff --git a/source/nsswitch/winbind_nss_irix.h b/source/nsswitch/winbind_nss_irix.h
new file mode 100644
index 00000000000..7878abb981a
--- /dev/null
+++ b/source/nsswitch/winbind_nss_irix.h
@@ -0,0 +1,48 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _WINBIND_NSS_IRIX_H
+#define _WINBIND_NSS_IRIX_H
+
+/* following required to prevent warnings of double definition
+ * of datum from ns_api.h
+*/
+#ifdef DATUM
+#define _DATUM_DEFINED
+#endif
+
+#include <ns_api.h>
+
+typedef enum
+{
+ NSS_STATUS_SUCCESS=NS_SUCCESS,
+ NSS_STATUS_NOTFOUND=NS_NOTFOUND,
+ NSS_STATUS_UNAVAIL=NS_UNAVAIL,
+ NSS_STATUS_TRYAGAIN=NS_TRYAGAIN
+} NSS_STATUS;
+
+#define NSD_MEM_STATIC 0
+#define NSD_MEM_VOLATILE 1
+#define NSD_MEM_DYNAMIC 2
+
+#endif /* _WINBIND_NSS_IRIX_H */
diff --git a/source/nsswitch/winbind_nss_linux.c b/source/nsswitch/winbind_nss_linux.c
new file mode 100644
index 00000000000..ae2bcc7ade9
--- /dev/null
+++ b/source/nsswitch/winbind_nss_linux.c
@@ -0,0 +1,1149 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Windows NT Domain nsswitch module
+
+ Copyright (C) Tim Potter 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "winbind_client.h"
+
+/* Maximum number of users to pass back over the unix domain socket
+ per call. This is not a static limit on the total number of users
+ or groups returned in total. */
+
+#define MAX_GETPWENT_USERS 250
+#define MAX_GETGRENT_USERS 250
+
+/* Prototypes from wb_common.c */
+
+extern int winbindd_fd;
+
+/* Allocate some space from the nss static buffer. The buffer and buflen
+ are the pointers passed in by the C library to the _nss_ntdom_*
+ functions. */
+
+static char *get_static(char **buffer, size_t *buflen, size_t len)
+{
+ char *result;
+
+ /* Error check. We return false if things aren't set up right, or
+ there isn't enough buffer space left. */
+
+ if ((buffer == NULL) || (buflen == NULL) || (*buflen < len)) {
+ return NULL;
+ }
+
+ /* Return an index into the static buffer */
+
+ result = *buffer;
+ *buffer += len;
+ *buflen -= len;
+
+ return result;
+}
+
+/* I've copied the strtok() replacement function next_token() from
+ lib/util_str.c as I really don't want to have to link in any other
+ objects if I can possibly avoid it. */
+
+BOOL next_token(char **ptr,char *buff,char *sep, size_t bufsize)
+{
+ char *s;
+ BOOL quoted;
+ size_t len=1;
+
+ if (!ptr) return(False);
+
+ s = *ptr;
+
+ /* default to simple separators */
+ if (!sep) sep = " \t\n\r";
+
+ /* find the first non sep char */
+ while (*s && strchr(sep,*s)) s++;
+
+ /* nothing left? */
+ if (! *s) return(False);
+
+ /* copy over the token */
+ for (quoted = False; len < bufsize && *s && (quoted || !strchr(sep,*s)); s++) {
+ if (*s == '\"') {
+ quoted = !quoted;
+ } else {
+ len++;
+ *buff++ = *s;
+ }
+ }
+
+ *ptr = (*s) ? s+1 : s;
+ *buff = 0;
+
+ return(True);
+}
+
+
+/* Fill a pwent structure from a winbindd_response structure. We use
+ the static data passed to us by libc to put strings and stuff in.
+ Return NSS_STATUS_TRYAGAIN if we run out of memory. */
+
+static NSS_STATUS fill_pwent(struct passwd *result,
+ struct winbindd_pw *pw,
+ char **buffer, size_t *buflen)
+{
+ /* User name */
+
+ if ((result->pw_name =
+ get_static(buffer, buflen, strlen(pw->pw_name) + 1)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ strcpy(result->pw_name, pw->pw_name);
+
+ /* Password */
+
+ if ((result->pw_passwd =
+ get_static(buffer, buflen, strlen(pw->pw_passwd) + 1)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ strcpy(result->pw_passwd, pw->pw_passwd);
+
+ /* [ug]id */
+
+ result->pw_uid = pw->pw_uid;
+ result->pw_gid = pw->pw_gid;
+
+ /* GECOS */
+
+ if ((result->pw_gecos =
+ get_static(buffer, buflen, strlen(pw->pw_gecos) + 1)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ strcpy(result->pw_gecos, pw->pw_gecos);
+
+ /* Home directory */
+
+ if ((result->pw_dir =
+ get_static(buffer, buflen, strlen(pw->pw_dir) + 1)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ strcpy(result->pw_dir, pw->pw_dir);
+
+ /* Logon shell */
+
+ if ((result->pw_shell =
+ get_static(buffer, buflen, strlen(pw->pw_shell) + 1)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ strcpy(result->pw_shell, pw->pw_shell);
+
+ /* The struct passwd for Solaris has some extra fields which must
+ be initialised or nscd crashes. */
+
+#if HAVE_PASSWD_PW_COMMENT
+ result->pw_comment = "";
+#endif
+
+#if HAVE_PASSWD_PW_AGE
+ result->pw_age = "";
+#endif
+
+ return NSS_STATUS_SUCCESS;
+}
+
+/* Fill a grent structure from a winbindd_response structure. We use
+ the static data passed to us by libc to put strings and stuff in.
+ Return NSS_STATUS_TRYAGAIN if we run out of memory. */
+
+static NSS_STATUS fill_grent(struct group *result, struct winbindd_gr *gr,
+ char *gr_mem, char **buffer, size_t *buflen)
+{
+ fstring name;
+ int i;
+ char *tst;
+
+ /* Group name */
+
+ if ((result->gr_name =
+ get_static(buffer, buflen, strlen(gr->gr_name) + 1)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ strcpy(result->gr_name, gr->gr_name);
+
+ /* Password */
+
+ if ((result->gr_passwd =
+ get_static(buffer, buflen, strlen(gr->gr_passwd) + 1)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ strcpy(result->gr_passwd, gr->gr_passwd);
+
+ /* gid */
+
+ result->gr_gid = gr->gr_gid;
+
+ /* Group membership */
+
+ if ((gr->num_gr_mem < 0) || !gr_mem) {
+ gr->num_gr_mem = 0;
+ }
+
+ /* this next value is a pointer to a pointer so let's align it */
+
+ /* Calculate number of extra bytes needed to align on pointer size boundry */
+ if ((i = (unsigned long)(*buffer) % sizeof(char*)) != 0)
+ i = sizeof(char*) - i;
+
+ if ((tst = get_static(buffer, buflen, ((gr->num_gr_mem + 1) *
+ sizeof(char *)+i))) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+ result->gr_mem = (char **)(tst + i);
+
+ if (gr->num_gr_mem == 0) {
+
+ /* Group is empty */
+
+ *(result->gr_mem) = NULL;
+ return NSS_STATUS_SUCCESS;
+ }
+
+ /* Start looking at extra data */
+
+ i = 0;
+
+ while(next_token((char **)&gr_mem, name, ",", sizeof(fstring))) {
+
+ /* Allocate space for member */
+
+ if (((result->gr_mem)[i] =
+ get_static(buffer, buflen, strlen(name) + 1)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ strcpy((result->gr_mem)[i], name);
+ i++;
+ }
+
+ /* Terminate list */
+
+ (result->gr_mem)[i] = NULL;
+
+ return NSS_STATUS_SUCCESS;
+}
+
+/*
+ * NSS user functions
+ */
+
+static struct winbindd_response getpwent_response;
+
+static int ndx_pw_cache; /* Current index into pwd cache */
+static int num_pw_cache; /* Current size of pwd cache */
+
+/* Rewind "file pointer" to start of ntdom password database */
+
+NSS_STATUS
+_nss_winbind_setpwent(void)
+{
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: setpwent\n", getpid());
+#endif
+
+ if (num_pw_cache > 0) {
+ ndx_pw_cache = num_pw_cache = 0;
+ free_response(&getpwent_response);
+ }
+
+ return winbindd_request(WINBINDD_SETPWENT, NULL, NULL);
+}
+
+/* Close ntdom password database "file pointer" */
+
+NSS_STATUS
+_nss_winbind_endpwent(void)
+{
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: endpwent\n", getpid());
+#endif
+
+ if (num_pw_cache > 0) {
+ ndx_pw_cache = num_pw_cache = 0;
+ free_response(&getpwent_response);
+ }
+
+ return winbindd_request(WINBINDD_ENDPWENT, NULL, NULL);
+}
+
+/* Fetch the next password entry from ntdom password database */
+
+NSS_STATUS
+_nss_winbind_getpwent_r(struct passwd *result, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_request request;
+ static int called_again;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getpwent\n", getpid());
+#endif
+
+ /* Return an entry from the cache if we have one, or if we are
+ called again because we exceeded our static buffer. */
+
+ if ((ndx_pw_cache < num_pw_cache) || called_again) {
+ goto return_result;
+ }
+
+ /* Else call winbindd to get a bunch of entries */
+
+ if (num_pw_cache > 0) {
+ free_response(&getpwent_response);
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(getpwent_response);
+
+ request.data.num_entries = MAX_GETPWENT_USERS;
+
+ ret = winbindd_request(WINBINDD_GETPWENT, &request,
+ &getpwent_response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+ struct winbindd_pw *pw_cache;
+
+ /* Fill cache */
+
+ ndx_pw_cache = 0;
+ num_pw_cache = getpwent_response.data.num_entries;
+
+ /* Return a result */
+
+ return_result:
+
+ pw_cache = getpwent_response.extra_data;
+
+ /* Check data is valid */
+
+ if (pw_cache == NULL) {
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ ret = fill_pwent(result, &pw_cache[ndx_pw_cache],
+ &buffer, &buflen);
+
+ /* Out of memory - try again */
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ called_again = True;
+ *errnop = errno = ERANGE;
+ return ret;
+ }
+
+ *errnop = errno = 0;
+ called_again = False;
+ ndx_pw_cache++;
+
+ /* If we've finished with this lot of results free cache */
+
+ if (ndx_pw_cache == num_pw_cache) {
+ ndx_pw_cache = num_pw_cache = 0;
+ free_response(&getpwent_response);
+ }
+ }
+
+ return ret;
+}
+
+/* Return passwd struct from uid */
+
+NSS_STATUS
+_nss_winbind_getpwuid_r(uid_t uid, struct passwd *result, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ static struct winbindd_response response;
+ struct winbindd_request request;
+ static int keep_response=0;
+
+ /* If our static buffer needs to be expanded we are called again */
+ if (!keep_response) {
+
+ /* Call for the first time */
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ request.data.uid = uid;
+
+ ret = winbindd_request(WINBINDD_GETPWUID, &request, &response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+ ret = fill_pwent(result, &response.data.pw,
+ &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = True;
+ *errnop = errno = ERANGE;
+ return ret;
+ }
+ }
+
+ } else {
+
+ /* We've been called again */
+
+ ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = True;
+ *errnop = errno = ERANGE;
+ return ret;
+ }
+
+ keep_response = False;
+ *errnop = errno = 0;
+ }
+
+ free_response(&response);
+ return ret;
+}
+
+/* Return passwd struct from username */
+
+NSS_STATUS
+_nss_winbind_getpwnam_r(const char *name, struct passwd *result, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ static struct winbindd_response response;
+ struct winbindd_request request;
+ static int keep_response;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getpwnam %s\n", getpid(), name);
+#endif
+
+ /* If our static buffer needs to be expanded we are called again */
+
+ if (!keep_response) {
+
+ /* Call for the first time */
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ strncpy(request.data.username, name,
+ sizeof(request.data.username) - 1);
+ request.data.username
+ [sizeof(request.data.username) - 1] = '\0';
+
+ ret = winbindd_request(WINBINDD_GETPWNAM, &request, &response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+ ret = fill_pwent(result, &response.data.pw, &buffer,
+ &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = True;
+ *errnop = errno = ERANGE;
+ return ret;
+ }
+ }
+
+ } else {
+
+ /* We've been called again */
+
+ ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = True;
+ *errnop = errno = ERANGE;
+ return ret;
+ }
+
+ keep_response = False;
+ *errnop = errno = 0;
+ }
+
+ free_response(&response);
+ return ret;
+}
+
+/*
+ * NSS group functions
+ */
+
+static struct winbindd_response getgrent_response;
+
+static int ndx_gr_cache; /* Current index into grp cache */
+static int num_gr_cache; /* Current size of grp cache */
+
+/* Rewind "file pointer" to start of ntdom group database */
+
+NSS_STATUS
+_nss_winbind_setgrent(void)
+{
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: setgrent\n", getpid());
+#endif
+
+ if (num_gr_cache > 0) {
+ ndx_gr_cache = num_gr_cache = 0;
+ free_response(&getgrent_response);
+ }
+
+ return winbindd_request(WINBINDD_SETGRENT, NULL, NULL);
+}
+
+/* Close "file pointer" for ntdom group database */
+
+NSS_STATUS
+_nss_winbind_endgrent(void)
+{
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: endgrent\n", getpid());
+#endif
+
+ if (num_gr_cache > 0) {
+ ndx_gr_cache = num_gr_cache = 0;
+ free_response(&getgrent_response);
+ }
+
+ return winbindd_request(WINBINDD_ENDGRENT, NULL, NULL);
+}
+
+/* Get next entry from ntdom group database */
+
+static NSS_STATUS
+winbind_getgrent(enum winbindd_cmd cmd,
+ struct group *result,
+ char *buffer, size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ static struct winbindd_request request;
+ static int called_again;
+
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getgrent\n", getpid());
+#endif
+
+ /* Return an entry from the cache if we have one, or if we are
+ called again because we exceeded our static buffer. */
+
+ if ((ndx_gr_cache < num_gr_cache) || called_again) {
+ goto return_result;
+ }
+
+ /* Else call winbindd to get a bunch of entries */
+
+ if (num_gr_cache > 0) {
+ free_response(&getgrent_response);
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(getgrent_response);
+
+ request.data.num_entries = MAX_GETGRENT_USERS;
+
+ ret = winbindd_request(cmd, &request,
+ &getgrent_response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+ struct winbindd_gr *gr_cache;
+ int mem_ofs;
+
+ /* Fill cache */
+
+ ndx_gr_cache = 0;
+ num_gr_cache = getgrent_response.data.num_entries;
+
+ /* Return a result */
+
+ return_result:
+
+ gr_cache = getgrent_response.extra_data;
+
+ /* Check data is valid */
+
+ if (gr_cache == NULL) {
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ /* Fill group membership. The offset into the extra data
+ for the group membership is the reported offset plus the
+ size of all the winbindd_gr records returned. */
+
+ mem_ofs = gr_cache[ndx_gr_cache].gr_mem_ofs +
+ num_gr_cache * sizeof(struct winbindd_gr);
+
+ ret = fill_grent(result, &gr_cache[ndx_gr_cache],
+ ((char *)getgrent_response.extra_data)+mem_ofs,
+ &buffer, &buflen);
+
+ /* Out of memory - try again */
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ called_again = True;
+ *errnop = errno = ERANGE;
+ return ret;
+ }
+
+ *errnop = 0;
+ called_again = False;
+ ndx_gr_cache++;
+
+ /* If we've finished with this lot of results free cache */
+
+ if (ndx_gr_cache == num_gr_cache) {
+ ndx_gr_cache = num_gr_cache = 0;
+ free_response(&getgrent_response);
+ }
+ }
+
+ return ret;
+}
+
+
+NSS_STATUS
+_nss_winbind_getgrent_r(struct group *result,
+ char *buffer, size_t buflen, int *errnop)
+{
+ return winbind_getgrent(WINBINDD_GETGRENT, result, buffer, buflen, errnop);
+}
+
+NSS_STATUS
+_nss_winbind_getgrlst_r(struct group *result,
+ char *buffer, size_t buflen, int *errnop)
+{
+ return winbind_getgrent(WINBINDD_GETGRLST, result, buffer, buflen, errnop);
+}
+
+/* Return group struct from group name */
+
+NSS_STATUS
+_nss_winbind_getgrnam_r(const char *name,
+ struct group *result, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ static struct winbindd_response response;
+ struct winbindd_request request;
+ static int keep_response;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getgrnam %s\n", getpid(), name);
+#endif
+
+ /* If our static buffer needs to be expanded we are called again */
+
+ if (!keep_response) {
+
+ /* Call for the first time */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ strncpy(request.data.groupname, name,
+ sizeof(request.data.groupname));
+ request.data.groupname
+ [sizeof(request.data.groupname) - 1] = '\0';
+
+ ret = winbindd_request(WINBINDD_GETGRNAM, &request, &response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+ ret = fill_grent(result, &response.data.gr,
+ response.extra_data,
+ &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = True;
+ *errnop = errno = ERANGE;
+ return ret;
+ }
+ }
+
+ } else {
+
+ /* We've been called again */
+
+ ret = fill_grent(result, &response.data.gr,
+ response.extra_data, &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = True;
+ *errnop = errno = ERANGE;
+ return ret;
+ }
+
+ keep_response = False;
+ *errnop = 0;
+ }
+
+ free_response(&response);
+ return ret;
+}
+
+/* Return group struct from gid */
+
+NSS_STATUS
+_nss_winbind_getgrgid_r(gid_t gid,
+ struct group *result, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ static struct winbindd_response response;
+ struct winbindd_request request;
+ static int keep_response;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getgrgid %d\n", getpid(), gid);
+#endif
+
+ /* If our static buffer needs to be expanded we are called again */
+
+ if (!keep_response) {
+
+ /* Call for the first time */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.data.gid = gid;
+
+ ret = winbindd_request(WINBINDD_GETGRGID, &request, &response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+
+ ret = fill_grent(result, &response.data.gr,
+ response.extra_data,
+ &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = True;
+ *errnop = errno = ERANGE;
+ return ret;
+ }
+ }
+
+ } else {
+
+ /* We've been called again */
+
+ ret = fill_grent(result, &response.data.gr,
+ response.extra_data, &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = True;
+ *errnop = errno = ERANGE;
+ return ret;
+ }
+
+ keep_response = False;
+ *errnop = 0;
+ }
+
+ free_response(&response);
+ return ret;
+}
+
+/* Initialise supplementary groups */
+
+NSS_STATUS
+_nss_winbind_initgroups_dyn(char *user, gid_t group, long int *start,
+ long int *size, gid_t **groups, long int limit,
+ int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int i;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: initgroups %s (%d)\n", getpid(),
+ user, group);
+#endif
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ strncpy(request.data.username, user,
+ sizeof(request.data.username) - 1);
+
+ ret = winbindd_request(WINBINDD_GETGROUPS, &request, &response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+ int num_gids = response.data.num_entries;
+ gid_t *gid_list = (gid_t *)response.extra_data;
+
+ /* Copy group list to client */
+
+ for (i = 0; i < num_gids; i++) {
+
+ /* Skip primary group */
+
+ if (gid_list[i] == group) continue;
+
+ /* Add to buffer */
+
+ if (*start == *size && limit <= 0) {
+ (*groups) = realloc(
+ (*groups), (2 * (*size) + 1) * sizeof(**groups));
+ if (! *groups) goto done;
+ *size = 2 * (*size) + 1;
+ }
+
+ if (*start == *size) goto done;
+
+ (*groups)[*start] = gid_list[i];
+ *start += 1;
+
+ /* Filled buffer? */
+
+ if (*start == limit) goto done;
+ }
+ }
+
+ /* Back to your regularly scheduled programming */
+
+ done:
+ return ret;
+}
+
+
+/* return a list of group SIDs for a user SID */
+NSS_STATUS
+_nss_winbind_getusersids(const char *user_sid, char **group_sids,
+ int *num_groups,
+ char *buffer, size_t buf_size, int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getusersids %s\n", getpid(), user_sid);
+#endif
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ strncpy(request.data.sid, user_sid,sizeof(request.data.sid) - 1);
+ request.data.sid[sizeof(request.data.sid) - 1] = '\0';
+
+ ret = winbindd_request(WINBINDD_GETUSERSIDS, &request, &response);
+
+ if (ret != NSS_STATUS_SUCCESS) {
+ goto done;
+ }
+
+ if (buf_size < response.length - sizeof(response)) {
+ ret = NSS_STATUS_TRYAGAIN;
+ errno = *errnop = ERANGE;
+ goto done;
+ }
+
+ *num_groups = response.data.num_entries;
+ *group_sids = buffer;
+ memcpy(buffer, response.extra_data, response.length - sizeof(response));
+ errno = *errnop = 0;
+
+ done:
+ free_response(&response);
+ return ret;
+}
+
+
+/* map a user or group name to a SID string */
+NSS_STATUS
+_nss_winbind_nametosid(const char *name, char **sid, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_response response;
+ struct winbindd_request request;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: nametosid %s\n", getpid(), name);
+#endif
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ strncpy(request.data.name.name, name,
+ sizeof(request.data.name.name) - 1);
+ request.data.name.name[sizeof(request.data.name.name) - 1] = '\0';
+
+ ret = winbindd_request(WINBINDD_LOOKUPNAME, &request, &response);
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno = EINVAL;
+ goto failed;
+ }
+
+ if (buflen < strlen(response.data.sid.sid)+1) {
+ ret = NSS_STATUS_TRYAGAIN;
+ *errnop = errno = ERANGE;
+ goto failed;
+ }
+
+ *errnop = errno = 0;
+ *sid = buffer;
+ strcpy(*sid, response.data.sid.sid);
+
+failed:
+ free_response(&response);
+ return ret;
+}
+
+/* map a sid string to a user or group name */
+NSS_STATUS
+_nss_winbind_sidtoname(const char *sid, char **name, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_response response;
+ struct winbindd_request request;
+ static char sep_char;
+ unsigned needed;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: sidtoname %s\n", getpid(), sid);
+#endif
+
+ /* we need to fetch the separator first time through */
+ if (!sep_char) {
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ ret = winbindd_request(WINBINDD_INFO, &request, &response);
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno = EINVAL;
+ goto failed;
+ }
+
+ sep_char = response.data.info.winbind_separator;
+ free_response(&response);
+ }
+
+
+ strncpy(request.data.sid, sid,
+ sizeof(request.data.sid) - 1);
+ request.data.sid[sizeof(request.data.sid) - 1] = '\0';
+
+ ret = winbindd_request(WINBINDD_LOOKUPSID, &request, &response);
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno = EINVAL;
+ goto failed;
+ }
+
+ needed =
+ strlen(response.data.name.dom_name) +
+ strlen(response.data.name.name) + 2;
+
+ if (buflen < needed) {
+ ret = NSS_STATUS_TRYAGAIN;
+ *errnop = errno = ERANGE;
+ goto failed;
+ }
+
+ snprintf(buffer, needed, "%s%c%s",
+ response.data.name.dom_name,
+ sep_char,
+ response.data.name.name);
+
+ *name = buffer;
+ *errnop = errno = 0;
+
+failed:
+ free_response(&response);
+ return ret;
+}
+
+/* map a sid to a uid */
+NSS_STATUS
+_nss_winbind_sidtouid(const char *sid, uid_t *uid, int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_response response;
+ struct winbindd_request request;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: sidtouid %s\n", getpid(), sid);
+#endif
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ strncpy(request.data.sid, sid, sizeof(request.data.sid) - 1);
+ request.data.sid[sizeof(request.data.sid) - 1] = '\0';
+
+ ret = winbindd_request(WINBINDD_SID_TO_UID, &request, &response);
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno = EINVAL;
+ goto failed;
+ }
+
+ *uid = response.data.uid;
+
+failed:
+ return ret;
+}
+
+/* map a sid to a gid */
+NSS_STATUS
+_nss_winbind_sidtogid(const char *sid, gid_t *gid, int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_response response;
+ struct winbindd_request request;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: sidtogid %s\n", getpid(), sid);
+#endif
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ strncpy(request.data.sid, sid, sizeof(request.data.sid) - 1);
+ request.data.sid[sizeof(request.data.sid) - 1] = '\0';
+
+ ret = winbindd_request(WINBINDD_SID_TO_GID, &request, &response);
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno = EINVAL;
+ goto failed;
+ }
+
+ *gid = response.data.gid;
+
+failed:
+ return ret;
+}
+
+/* map a uid to a SID string */
+NSS_STATUS
+_nss_winbind_uidtosid(uid_t uid, char **sid, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_response response;
+ struct winbindd_request request;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5u]: uidtosid %u\n", (unsigned int)getpid(), (unsigned int)uid);
+#endif
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ request.data.uid = uid;
+
+ ret = winbindd_request(WINBINDD_UID_TO_SID, &request, &response);
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno = EINVAL;
+ goto failed;
+ }
+
+ if (buflen < strlen(response.data.sid.sid)+1) {
+ ret = NSS_STATUS_TRYAGAIN;
+ *errnop = errno = ERANGE;
+ goto failed;
+ }
+
+ *errnop = errno = 0;
+ *sid = buffer;
+ strcpy(*sid, response.data.sid.sid);
+
+failed:
+ free_response(&response);
+ return ret;
+}
+
+/* map a gid to a SID string */
+NSS_STATUS
+_nss_winbind_gidtosid(gid_t gid, char **sid, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_response response;
+ struct winbindd_request request;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5u]: gidtosid %u\n", (unsigned int)getpid(), (unsigned int)gid);
+#endif
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ request.data.gid = gid;
+
+ ret = winbindd_request(WINBINDD_GID_TO_SID, &request, &response);
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno = EINVAL;
+ goto failed;
+ }
+
+ if (buflen < strlen(response.data.sid.sid)+1) {
+ ret = NSS_STATUS_TRYAGAIN;
+ *errnop = errno = ERANGE;
+ goto failed;
+ }
+
+ *errnop = errno = 0;
+ *sid = buffer;
+ strcpy(*sid, response.data.sid.sid);
+
+failed:
+ free_response(&response);
+ return ret;
+}
diff --git a/source/nsswitch/winbind_nss_linux.h b/source/nsswitch/winbind_nss_linux.h
new file mode 100644
index 00000000000..1c7e8300373
--- /dev/null
+++ b/source/nsswitch/winbind_nss_linux.h
@@ -0,0 +1,35 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _WINBIND_NSS_LINUX_H
+#define _WINBIND_NSS_LINUX_H
+
+#if HAVE_NSS_H
+
+#include <nss.h>
+
+typedef enum nss_status NSS_STATUS;
+
+#endif /* HAVE_NSS_H */
+
+#endif /* _WINBIND_NSS_LINUX_H */
diff --git a/source/nsswitch/winbind_nss_solaris.c b/source/nsswitch/winbind_nss_solaris.c
new file mode 100644
index 00000000000..8f03eb4cd6e
--- /dev/null
+++ b/source/nsswitch/winbind_nss_solaris.c
@@ -0,0 +1,315 @@
+/*
+ Solaris NSS wrapper for winbind
+ - Shirish Kalele 2000
+
+ Based on Luke Howard's ldap_nss module for Solaris
+ */
+
+/*
+ Copyright (C) 1997-2003 Luke Howard.
+ This file is part of the nss_ldap library.
+
+ The nss_ldap library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The nss_ldap library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the nss_ldap library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <string.h>
+#include <pwd.h>
+#include "includes.h"
+#include <syslog.h>
+#if !defined(HPUX)
+#include <sys/syslog.h>
+#endif /*hpux*/
+#include "winbind_nss_config.h"
+
+#if defined(HAVE_NSS_COMMON_H) || defined(HPUX)
+
+#undef NSS_DEBUG
+
+#ifdef NSS_DEBUG
+#define NSS_DEBUG(str) syslog(LOG_DEBUG, "nss_winbind: %s", str);
+#else
+#define NSS_DEBUG(str) ;
+#endif
+
+#define NSS_ARGS(args) ((nss_XbyY_args_t *)args)
+
+#define make_pwent_str(dest, src) \
+{ \
+ if((dest = get_static(buffer, buflen, strlen(src)+1)) == NULL) \
+ { \
+ *errnop = ERANGE; \
+ NSS_DEBUG("ERANGE error"); \
+ return NSS_STATUS_TRYAGAIN; \
+ } \
+ strcpy(dest, src); \
+}
+
+static NSS_STATUS _nss_winbind_setpwent_solwrap (nss_backend_t* be, void* args)
+{
+ NSS_DEBUG("_nss_winbind_setpwent_solwrap");
+ return _nss_winbind_setpwent();
+}
+
+static NSS_STATUS
+_nss_winbind_endpwent_solwrap (nss_backend_t * be, void *args)
+{
+ NSS_DEBUG("_nss_winbind_endpwent_solwrap");
+ return _nss_winbind_endpwent();
+}
+
+static NSS_STATUS
+_nss_winbind_getpwent_solwrap (nss_backend_t* be, void *args)
+{
+ NSS_STATUS ret;
+ char* buffer = NSS_ARGS(args)->buf.buffer;
+ int buflen = NSS_ARGS(args)->buf.buflen;
+ struct passwd* result = (struct passwd*) NSS_ARGS(args)->buf.result;
+ int* errnop = &NSS_ARGS(args)->erange;
+ char logmsg[80];
+
+ ret = _nss_winbind_getpwent_r(result, buffer,
+ buflen, errnop);
+
+ if(ret == NSS_STATUS_SUCCESS)
+ {
+ snprintf(logmsg, 79, "_nss_winbind_getpwent_solwrap: Returning user: %s\n",
+ result->pw_name);
+ NSS_DEBUG(logmsg);
+ NSS_ARGS(args)->returnval = (void*) result;
+ } else {
+ snprintf(logmsg, 79, "_nss_winbind_getpwent_solwrap: Returning error: %d.\n",ret);
+ NSS_DEBUG(logmsg);
+ }
+
+ return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_getpwnam_solwrap (nss_backend_t* be, void* args)
+{
+ NSS_STATUS ret;
+ struct passwd* result = (struct passwd*) NSS_ARGS(args)->buf.result;
+
+ NSS_DEBUG("_nss_winbind_getpwnam_solwrap");
+
+ ret = _nss_winbind_getpwnam_r (NSS_ARGS(args)->key.name,
+ result,
+ NSS_ARGS(args)->buf.buffer,
+ NSS_ARGS(args)->buf.buflen,
+ &NSS_ARGS(args)->erange);
+ if(ret == NSS_STATUS_SUCCESS)
+ NSS_ARGS(args)->returnval = (void*) result;
+
+ return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_getpwuid_solwrap(nss_backend_t* be, void* args)
+{
+ NSS_STATUS ret;
+ struct passwd* result = (struct passwd*) NSS_ARGS(args)->buf.result;
+
+ NSS_DEBUG("_nss_winbind_getpwuid_solwrap");
+ ret = _nss_winbind_getpwuid_r (NSS_ARGS(args)->key.uid,
+ result,
+ NSS_ARGS(args)->buf.buffer,
+ NSS_ARGS(args)->buf.buflen,
+ &NSS_ARGS(args)->erange);
+ if(ret == NSS_STATUS_SUCCESS)
+ NSS_ARGS(args)->returnval = (void*) result;
+
+ return ret;
+}
+
+static NSS_STATUS _nss_winbind_passwd_destr (nss_backend_t * be, void *args)
+{
+ SAFE_FREE(be);
+ NSS_DEBUG("_nss_winbind_passwd_destr");
+ return NSS_STATUS_SUCCESS;
+}
+
+static nss_backend_op_t passwd_ops[] =
+{
+ _nss_winbind_passwd_destr,
+ _nss_winbind_endpwent_solwrap, /* NSS_DBOP_ENDENT */
+ _nss_winbind_setpwent_solwrap, /* NSS_DBOP_SETENT */
+ _nss_winbind_getpwent_solwrap, /* NSS_DBOP_GETENT */
+ _nss_winbind_getpwnam_solwrap, /* NSS_DBOP_PASSWD_BYNAME */
+ _nss_winbind_getpwuid_solwrap /* NSS_DBOP_PASSWD_BYUID */
+};
+
+nss_backend_t*
+_nss_winbind_passwd_constr (const char* db_name,
+ const char* src_name,
+ const char* cfg_args)
+{
+ nss_backend_t *be;
+
+ if(!(be = (nss_backend_t*) malloc(sizeof(nss_backend_t))) )
+ return NULL;
+
+ be->ops = passwd_ops;
+ be->n_ops = sizeof(passwd_ops) / sizeof(nss_backend_op_t);
+
+ NSS_DEBUG("Initialized nss_winbind passwd backend");
+ return be;
+}
+
+/*****************************************************************
+ GROUP database backend
+ *****************************************************************/
+
+static NSS_STATUS _nss_winbind_setgrent_solwrap (nss_backend_t* be, void* args)
+{
+ NSS_DEBUG("_nss_winbind_setgrent_solwrap");
+ return _nss_winbind_setgrent();
+}
+
+static NSS_STATUS
+_nss_winbind_endgrent_solwrap (nss_backend_t * be, void *args)
+{
+ NSS_DEBUG("_nss_winbind_endgrent_solwrap");
+ return _nss_winbind_endgrent();
+}
+
+static NSS_STATUS
+_nss_winbind_getgrent_solwrap(nss_backend_t* be, void* args)
+{
+ NSS_STATUS ret;
+ char* buffer = NSS_ARGS(args)->buf.buffer;
+ int buflen = NSS_ARGS(args)->buf.buflen;
+ struct group* result = (struct group*) NSS_ARGS(args)->buf.result;
+ int* errnop = &NSS_ARGS(args)->erange;
+ char logmsg[80];
+
+ ret = _nss_winbind_getgrent_r(result, buffer,
+ buflen, errnop);
+
+ if(ret == NSS_STATUS_SUCCESS)
+ {
+ snprintf(logmsg, 79, "_nss_winbind_getgrent_solwrap: Returning group: %s\n", result->gr_name);
+ NSS_DEBUG(logmsg);
+ NSS_ARGS(args)->returnval = (void*) result;
+ } else {
+ snprintf(logmsg, 79, "_nss_winbind_getgrent_solwrap: Returning error: %d.\n", ret);
+ NSS_DEBUG(logmsg);
+ }
+
+ return ret;
+
+}
+
+static NSS_STATUS
+_nss_winbind_getgrnam_solwrap(nss_backend_t* be, void* args)
+{
+ NSS_STATUS ret;
+ struct group* result = (struct group*) NSS_ARGS(args)->buf.result;
+
+ NSS_DEBUG("_nss_winbind_getgrnam_solwrap");
+ ret = _nss_winbind_getgrnam_r(NSS_ARGS(args)->key.name,
+ result,
+ NSS_ARGS(args)->buf.buffer,
+ NSS_ARGS(args)->buf.buflen,
+ &NSS_ARGS(args)->erange);
+
+ if(ret == NSS_STATUS_SUCCESS)
+ NSS_ARGS(args)->returnval = (void*) result;
+
+ return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_getgrgid_solwrap(nss_backend_t* be, void* args)
+{
+ NSS_STATUS ret;
+ struct group* result = (struct group*) NSS_ARGS(args)->buf.result;
+
+ NSS_DEBUG("_nss_winbind_getgrgid_solwrap");
+ ret = _nss_winbind_getgrgid_r (NSS_ARGS(args)->key.gid,
+ result,
+ NSS_ARGS(args)->buf.buffer,
+ NSS_ARGS(args)->buf.buflen,
+ &NSS_ARGS(args)->erange);
+
+ if(ret == NSS_STATUS_SUCCESS)
+ NSS_ARGS(args)->returnval = (void*) result;
+
+ return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_getgroupsbymember_solwrap(nss_backend_t* be, void* args)
+{
+ int errnop;
+ struct nss_groupsbymem *gmem = (struct nss_groupsbymem *)args;
+
+ NSS_DEBUG("_nss_winbind_getgroupsbymember");
+
+ _nss_winbind_initgroups_dyn(gmem->username,
+ gmem->gid_array[0], /* Primary Group */
+ &gmem->numgids,
+ &gmem->maxgids,
+ &gmem->gid_array,
+ gmem->maxgids,
+ &errnop);
+
+ /*
+ * Always return NOTFOUND so nsswitch will get info from all
+ * the database backends specified in the nsswitch.conf file.
+ */
+ return NSS_STATUS_NOTFOUND;
+}
+
+static NSS_STATUS
+_nss_winbind_group_destr (nss_backend_t* be, void* args)
+{
+ SAFE_FREE(be);
+ NSS_DEBUG("_nss_winbind_group_destr");
+ return NSS_STATUS_SUCCESS;
+}
+
+static nss_backend_op_t group_ops[] =
+{
+ _nss_winbind_group_destr,
+ _nss_winbind_endgrent_solwrap,
+ _nss_winbind_setgrent_solwrap,
+ _nss_winbind_getgrent_solwrap,
+ _nss_winbind_getgrnam_solwrap,
+ _nss_winbind_getgrgid_solwrap,
+ _nss_winbind_getgroupsbymember_solwrap
+};
+
+nss_backend_t*
+_nss_winbind_group_constr (const char* db_name,
+ const char* src_name,
+ const char* cfg_args)
+{
+ nss_backend_t* be;
+
+ if(!(be = (nss_backend_t*) malloc(sizeof(nss_backend_t))) )
+ return NULL;
+
+ be->ops = group_ops;
+ be->n_ops = sizeof(group_ops) / sizeof(nss_backend_op_t);
+
+ NSS_DEBUG("Initialized nss_winbind group backend");
+ return be;
+}
+
+#endif /* SUN_NSS */
diff --git a/source/nsswitch/winbind_nss_solaris.h b/source/nsswitch/winbind_nss_solaris.h
new file mode 100644
index 00000000000..567de411aa6
--- /dev/null
+++ b/source/nsswitch/winbind_nss_solaris.h
@@ -0,0 +1,61 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _WINBIND_NSS_SOLARIS_H
+#define _WINBIND_NSS_SOLARIS_H
+
+#include <nss_common.h>
+#include <nss_dbdefs.h>
+#include <nsswitch.h>
+
+typedef nss_status_t NSS_STATUS;
+
+#define NSS_STATUS_SUCCESS NSS_SUCCESS
+#define NSS_STATUS_NOTFOUND NSS_NOTFOUND
+#define NSS_STATUS_UNAVAIL NSS_UNAVAIL
+#define NSS_STATUS_TRYAGAIN NSS_TRYAGAIN
+
+/* The solaris winbind is implemented as a wrapper around the linux
+ version. */
+
+NSS_STATUS _nss_winbind_setpwent(void);
+NSS_STATUS _nss_winbind_endpwent(void);
+NSS_STATUS _nss_winbind_getpwent_r(struct passwd* result, char* buffer,
+ size_t buflen, int* errnop);
+NSS_STATUS _nss_winbind_getpwuid_r(uid_t, struct passwd*, char* buffer,
+ size_t buflen, int* errnop);
+NSS_STATUS _nss_winbind_getpwnam_r(const char* name, struct passwd* result,
+ char* buffer, size_t buflen, int* errnop);
+
+NSS_STATUS _nss_winbind_setgrent(void);
+NSS_STATUS _nss_winbind_endgrent(void);
+NSS_STATUS _nss_winbind_getgrent_r(struct group* result, char* buffer,
+ size_t buflen, int* errnop);
+NSS_STATUS _nss_winbind_getgrnam_r(const char *name,
+ struct group *result, char *buffer,
+ size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_getgrgid_r(gid_t gid,
+ struct group *result, char *buffer,
+ size_t buflen, int *errnop);
+
+#endif /* _WINBIND_NSS_SOLARIS_H */
diff --git a/source/nsswitch/winbindd.c b/source/nsswitch/winbindd.c
new file mode 100644
index 00000000000..283b2e4a89c
--- /dev/null
+++ b/source/nsswitch/winbindd.c
@@ -0,0 +1,947 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) by Tim Potter 2000-2002
+ Copyright (C) Andrew Tridgell 2002
+ Copyright (C) Jelmer Vernooij 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+
+BOOL opt_nocache = False;
+BOOL opt_dual_daemon = True;
+
+/* Reload configuration */
+
+static BOOL reload_services_file(void)
+{
+ BOOL ret;
+
+ if (lp_loaded()) {
+ pstring fname;
+
+ pstrcpy(fname,lp_configfile());
+ if (file_exist(fname,NULL) && !strcsequal(fname,dyn_CONFIGFILE)) {
+ pstrcpy(dyn_CONFIGFILE,fname);
+ }
+ }
+
+ reopen_logs();
+ ret = lp_load(dyn_CONFIGFILE,False,False,True);
+
+ reopen_logs();
+ load_interfaces();
+
+ return(ret);
+}
+
+
+#if DUMP_CORE
+
+/**************************************************************************** **
+ Prepare to dump a core file - carefully!
+ **************************************************************************** */
+
+static BOOL dump_core(void)
+{
+ char *p;
+ pstring dname;
+ pstrcpy( dname, lp_logfile() );
+ if ((p=strrchr(dname,'/')))
+ *p=0;
+ pstrcat( dname, "/corefiles" );
+ mkdir( dname, 0700 );
+ sys_chown( dname, getuid(), getgid() );
+ chmod( dname, 0700 );
+ if ( chdir(dname) )
+ return( False );
+ umask( ~(0700) );
+
+#ifdef HAVE_GETRLIMIT
+#ifdef RLIMIT_CORE
+ {
+ struct rlimit rlp;
+ getrlimit( RLIMIT_CORE, &rlp );
+ rlp.rlim_cur = MAX( 4*1024*1024, rlp.rlim_cur );
+ setrlimit( RLIMIT_CORE, &rlp );
+ getrlimit( RLIMIT_CORE, &rlp );
+ DEBUG( 3, ( "Core limits now %d %d\n", (int)rlp.rlim_cur, (int)rlp.rlim_max ) );
+ }
+#endif
+#endif
+
+ DEBUG(0,("Dumping core in %s\n",dname));
+ abort();
+ return( True );
+} /* dump_core */
+#endif
+
+/**************************************************************************** **
+ Handle a fault..
+ **************************************************************************** */
+
+static void fault_quit(void)
+{
+#if DUMP_CORE
+ dump_core();
+#endif
+}
+
+static void winbindd_status(void)
+{
+ struct winbindd_cli_state *tmp;
+
+ DEBUG(0, ("winbindd status:\n"));
+
+ /* Print client state information */
+
+ DEBUG(0, ("\t%d clients currently active\n", winbindd_num_clients()));
+
+ if (DEBUGLEVEL >= 2 && winbindd_num_clients()) {
+ DEBUG(2, ("\tclient list:\n"));
+ for(tmp = winbindd_client_list(); tmp; tmp = tmp->next) {
+ DEBUG(2, ("\t\tpid %lu, sock %d, rbl %d, wbl %d\n",
+ (unsigned long)tmp->pid, tmp->sock, tmp->read_buf_len,
+ tmp->write_buf_len));
+ }
+ }
+}
+
+/* Print winbindd status to log file */
+
+static void print_winbindd_status(void)
+{
+ winbindd_status();
+ winbindd_cm_status();
+}
+
+/* Flush client cache */
+
+static void flush_caches(void)
+{
+#if 0
+ /* Clear cached user and group enumation info */
+ if (!opt_dual_daemon) /* Until we have coherent cache flush. */
+ wcache_flush_cache();
+#endif
+
+ /* We need to invalidate cached user list entries on a SIGHUP
+ otherwise cached access denied errors due to restrict anonymous
+ hang around until the sequence number changes. */
+
+ wcache_invalidate_cache();
+}
+
+/* Handle the signal by unlinking socket and exiting */
+
+static void terminate(void)
+{
+ pstring path;
+
+ idmap_close();
+
+ /* Remove socket file */
+ pstr_sprintf(path, "%s/%s",
+ WINBINDD_SOCKET_DIR, WINBINDD_SOCKET_NAME);
+ unlink(path);
+ exit(0);
+}
+
+static BOOL do_sigterm;
+
+static void termination_handler(int signum)
+{
+ do_sigterm = True;
+ sys_select_signal();
+}
+
+static BOOL do_sigusr2;
+
+static void sigusr2_handler(int signum)
+{
+ do_sigusr2 = True;
+ sys_select_signal();
+}
+
+static BOOL do_sighup;
+
+static void sighup_handler(int signum)
+{
+ do_sighup = True;
+ sys_select_signal();
+}
+
+/* React on 'smbcontrol winbindd reload-config' in the same way as on SIGHUP*/
+static void msg_reload_services(int msg_type, pid_t src, void *buf, size_t len)
+{
+ /* Flush various caches */
+ flush_caches();
+ reload_services_file();
+}
+
+/* React on 'smbcontrol winbindd shutdown' in the same way as on SIGTERM*/
+static void msg_shutdown(int msg_type, pid_t src, void *buf, size_t len)
+{
+ terminate();
+}
+
+struct dispatch_table {
+ enum winbindd_cmd cmd;
+ enum winbindd_result (*fn)(struct winbindd_cli_state *state);
+ const char *winbindd_cmd_name;
+};
+
+static struct dispatch_table dispatch_table[] = {
+
+ /* User functions */
+
+ { WINBINDD_GETPWNAM, winbindd_getpwnam, "GETPWNAM" },
+ { WINBINDD_GETPWUID, winbindd_getpwuid, "GETPWUID" },
+
+ { WINBINDD_SETPWENT, winbindd_setpwent, "SETPWENT" },
+ { WINBINDD_ENDPWENT, winbindd_endpwent, "ENDPWENT" },
+ { WINBINDD_GETPWENT, winbindd_getpwent, "GETPWENT" },
+
+ { WINBINDD_GETGROUPS, winbindd_getgroups, "GETGROUPS" },
+ { WINBINDD_GETUSERSIDS, winbindd_getusersids, "GETUSERSIDS" },
+
+ /* Group functions */
+
+ { WINBINDD_GETGRNAM, winbindd_getgrnam, "GETGRNAM" },
+ { WINBINDD_GETGRGID, winbindd_getgrgid, "GETGRGID" },
+ { WINBINDD_SETGRENT, winbindd_setgrent, "SETGRENT" },
+ { WINBINDD_ENDGRENT, winbindd_endgrent, "ENDGRENT" },
+ { WINBINDD_GETGRENT, winbindd_getgrent, "GETGRENT" },
+ { WINBINDD_GETGRLST, winbindd_getgrent, "GETGRLST" },
+
+ /* PAM auth functions */
+
+ { WINBINDD_PAM_AUTH, winbindd_pam_auth, "PAM_AUTH" },
+ { WINBINDD_PAM_AUTH_CRAP, winbindd_pam_auth_crap, "AUTH_CRAP" },
+ { WINBINDD_PAM_CHAUTHTOK, winbindd_pam_chauthtok, "CHAUTHTOK" },
+
+ /* Enumeration functions */
+
+ { WINBINDD_LIST_USERS, winbindd_list_users, "LIST_USERS" },
+ { WINBINDD_LIST_GROUPS, winbindd_list_groups, "LIST_GROUPS" },
+ { WINBINDD_LIST_TRUSTDOM, winbindd_list_trusted_domains, "LIST_TRUSTDOM" },
+ { WINBINDD_SHOW_SEQUENCE, winbindd_show_sequence, "SHOW_SEQUENCE" },
+
+ /* SID related functions */
+
+ { WINBINDD_LOOKUPSID, winbindd_lookupsid, "LOOKUPSID" },
+ { WINBINDD_LOOKUPNAME, winbindd_lookupname, "LOOKUPNAME" },
+
+ /* Lookup related functions */
+
+ { WINBINDD_SID_TO_UID, winbindd_sid_to_uid, "SID_TO_UID" },
+ { WINBINDD_SID_TO_GID, winbindd_sid_to_gid, "SID_TO_GID" },
+ { WINBINDD_GID_TO_SID, winbindd_gid_to_sid, "GID_TO_SID" },
+ { WINBINDD_UID_TO_SID, winbindd_uid_to_sid, "UID_TO_SID" },
+ { WINBINDD_ALLOCATE_RID, winbindd_allocate_rid, "ALLOCATE_RID" },
+
+ /* Miscellaneous */
+
+ { WINBINDD_CHECK_MACHACC, winbindd_check_machine_acct, "CHECK_MACHACC" },
+ { WINBINDD_PING, winbindd_ping, "PING" },
+ { WINBINDD_INFO, winbindd_info, "INFO" },
+ { WINBINDD_INTERFACE_VERSION, winbindd_interface_version, "INTERFACE_VERSION" },
+ { WINBINDD_DOMAIN_NAME, winbindd_domain_name, "DOMAIN_NAME" },
+ { WINBINDD_DOMAIN_INFO, winbindd_domain_info, "DOMAIN_INFO" },
+ { WINBINDD_NETBIOS_NAME, winbindd_netbios_name, "NETBIOS_NAME" },
+ { WINBINDD_PRIV_PIPE_DIR, winbindd_priv_pipe_dir, "WINBINDD_PRIV_PIPE_DIR" },
+
+ /* WINS functions */
+
+ { WINBINDD_WINS_BYNAME, winbindd_wins_byname, "WINS_BYNAME" },
+ { WINBINDD_WINS_BYIP, winbindd_wins_byip, "WINS_BYIP" },
+
+ /* UNIX account management functions */
+ { WINBINDD_CREATE_USER, winbindd_create_user, "CREATE_USER" },
+ { WINBINDD_CREATE_GROUP, winbindd_create_group, "CREATE_GROUP" },
+ { WINBINDD_ADD_USER_TO_GROUP, winbindd_add_user_to_group, "ADD_USER_TO_GROUP" },
+ { WINBINDD_REMOVE_USER_FROM_GROUP, winbindd_remove_user_from_group,"REMOVE_USER_FROM_GROUP"},
+ { WINBINDD_SET_USER_PRIMARY_GROUP, winbindd_set_user_primary_group,"SET_USER_PRIMARY_GROUP"},
+ { WINBINDD_DELETE_USER, winbindd_delete_user, "DELETE_USER" },
+ { WINBINDD_DELETE_GROUP, winbindd_delete_group, "DELETE_GROUP" },
+
+ /* End of list */
+
+ { WINBINDD_NUM_CMDS, NULL, "NONE" }
+};
+
+static void process_request(struct winbindd_cli_state *state)
+{
+ struct dispatch_table *table = dispatch_table;
+
+ /* Free response data - we may be interrupted and receive another
+ command before being able to send this data off. */
+
+ SAFE_FREE(state->response.extra_data);
+
+ ZERO_STRUCT(state->response);
+
+ state->response.result = WINBINDD_ERROR;
+ state->response.length = sizeof(struct winbindd_response);
+
+ /* Process command */
+
+ for (table = dispatch_table; table->fn; table++) {
+ if (state->request.cmd == table->cmd) {
+ DEBUG(10,("process_request: request fn %s\n", table->winbindd_cmd_name ));
+ state->response.result = table->fn(state);
+ break;
+ }
+ }
+
+ if (!table->fn)
+ DEBUG(10,("process_request: unknown request fn number %d\n", (int)state->request.cmd ));
+
+ /* In case extra data pointer is NULL */
+
+ if (!state->response.extra_data)
+ state->response.length = sizeof(struct winbindd_response);
+}
+
+/* Process a new connection by adding it to the client connection list */
+
+static void new_connection(int listen_sock, BOOL privileged)
+{
+ struct sockaddr_un sunaddr;
+ struct winbindd_cli_state *state;
+ socklen_t len;
+ int sock;
+
+ /* Accept connection */
+
+ len = sizeof(sunaddr);
+
+ do {
+ sock = accept(listen_sock, (struct sockaddr *)&sunaddr, &len);
+ } while (sock == -1 && errno == EINTR);
+
+ if (sock == -1)
+ return;
+
+ DEBUG(6,("accepted socket %d\n", sock));
+
+ /* Create new connection structure */
+
+ if ((state = (struct winbindd_cli_state *)
+ malloc(sizeof(*state))) == NULL)
+ return;
+
+ ZERO_STRUCTP(state);
+ state->sock = sock;
+
+ state->last_access = time(NULL);
+
+ state->privileged = privileged;
+
+ /* Add to connection list */
+
+ winbindd_add_client(state);
+}
+
+/* Remove a client connection from client connection list */
+
+static void remove_client(struct winbindd_cli_state *state)
+{
+ /* It's a dead client - hold a funeral */
+
+ if (state != NULL) {
+
+ /* Close socket */
+
+ close(state->sock);
+
+ /* Free any getent state */
+
+ free_getent_state(state->getpwent_state);
+ free_getent_state(state->getgrent_state);
+
+ /* We may have some extra data that was not freed if the
+ client was killed unexpectedly */
+
+ SAFE_FREE(state->response.extra_data);
+
+ /* Remove from list and free */
+
+ winbindd_remove_client(state);
+ SAFE_FREE(state);
+ }
+}
+
+
+/* Shutdown client connection which has been idle for the longest time */
+
+static BOOL remove_idle_client(void)
+{
+ struct winbindd_cli_state *state, *remove_state = NULL;
+ time_t last_access = 0;
+ int nidle = 0;
+
+ for (state = winbindd_client_list(); state; state = state->next) {
+ if (state->read_buf_len == 0 && state->write_buf_len == 0 &&
+ !state->getpwent_state && !state->getgrent_state) {
+ nidle++;
+ if (!last_access || state->last_access < last_access) {
+ last_access = state->last_access;
+ remove_state = state;
+ }
+ }
+ }
+
+ if (remove_state) {
+ DEBUG(5,("Found %d idle client connections, shutting down sock %d, pid %u\n",
+ nidle, remove_state->sock, (unsigned int)remove_state->pid));
+ remove_client(remove_state);
+ return True;
+ }
+
+ return False;
+}
+
+/* Process a complete received packet from a client */
+
+void winbind_process_packet(struct winbindd_cli_state *state)
+{
+ /* Process request */
+
+ /* Ensure null termination of entire request */
+ state->request.null_term = '\0';
+
+ state->pid = state->request.pid;
+
+ process_request(state);
+
+ /* Update client state */
+
+ state->read_buf_len = 0;
+ state->write_buf_len = sizeof(struct winbindd_response);
+
+ /* we might need to send it to the dual daemon */
+ if (opt_dual_daemon) {
+ dual_send_request(state);
+ }
+}
+
+/* Read some data from a client connection */
+
+void winbind_client_read(struct winbindd_cli_state *state)
+{
+ int n;
+
+ /* Read data */
+
+ n = sys_read(state->sock, state->read_buf_len +
+ (char *)&state->request,
+ sizeof(state->request) - state->read_buf_len);
+
+ DEBUG(10,("client_read: read %d bytes. Need %ld more for a full request.\n", n, (unsigned long)(sizeof(state->request) - n - state->read_buf_len) ));
+
+ /* Read failed, kill client */
+
+ if (n == -1 || n == 0) {
+ DEBUG(5,("read failed on sock %d, pid %lu: %s\n",
+ state->sock, (unsigned long)state->pid,
+ (n == -1) ? strerror(errno) : "EOF"));
+
+ state->finished = True;
+ return;
+ }
+
+ /* Update client state */
+
+ state->read_buf_len += n;
+ state->last_access = time(NULL);
+}
+
+/* Write some data to a client connection */
+
+static void client_write(struct winbindd_cli_state *state)
+{
+ char *data;
+ int num_written;
+
+ /* Write some data */
+ /*
+ * The fancy calculation of data below allows us to handle the
+ * case where write (sys_write) does not write all the data we
+ * gave it. In that case, we will come back through here again
+ * because of the loop above us, and we want to pick up where
+ * we left off.
+ */
+
+ if (!state->write_extra_data) {
+
+ /* Write response structure */
+
+ data = (char *)&state->response + sizeof(state->response) -
+ state->write_buf_len;
+
+ } else {
+
+ /* Write extra data */
+
+ data = (char *)state->response.extra_data +
+ state->response.length -
+ sizeof(struct winbindd_response) -
+ state->write_buf_len;
+ }
+
+ num_written = sys_write(state->sock, data, state->write_buf_len);
+
+ DEBUG(10,("client_write: wrote %d bytes.\n", num_written ));
+
+ /* Write failed, kill cilent */
+
+ if (num_written == -1 || num_written == 0) {
+
+ DEBUG(3,("write failed on sock %d, pid %lu: %s\n",
+ state->sock, (unsigned long)state->pid,
+ (num_written == -1) ? strerror(errno) : "EOF"));
+
+ state->finished = True;
+
+ SAFE_FREE(state->response.extra_data);
+
+ return;
+ }
+
+ /* Update client state */
+
+ state->write_buf_len -= num_written;
+ state->last_access = time(NULL);
+
+ /* Have we written all data? */
+
+ if (state->write_buf_len == 0) {
+
+ /* Take care of extra data */
+
+ if (state->write_extra_data) {
+
+ SAFE_FREE(state->response.extra_data);
+
+ state->write_extra_data = False;
+
+ DEBUG(10,("client_write: client_write: complete response written.\n"));
+
+ } else if (state->response.length >
+ sizeof(struct winbindd_response)) {
+
+ /* Start writing extra data */
+
+ state->write_buf_len =
+ state->response.length -
+ sizeof(struct winbindd_response);
+
+ DEBUG(10,("client_write: need to write %d extra data bytes.\n", (int)state->write_buf_len));
+
+ state->write_extra_data = True;
+ }
+ }
+}
+
+/* Process incoming clients on listen_sock. We use a tricky non-blocking,
+ non-forking, non-threaded model which allows us to handle many
+ simultaneous connections while remaining impervious to many denial of
+ service attacks. */
+
+static void process_loop(void)
+{
+ /* We'll be doing this a lot */
+
+ while (1) {
+ struct winbindd_cli_state *state;
+ fd_set r_fds, w_fds;
+ int maxfd, listen_sock, listen_priv_sock, selret;
+ struct timeval timeout;
+
+ /* Handle messages */
+
+ message_dispatch();
+
+ /* refresh the trusted domain cache */
+
+ rescan_trusted_domains();
+
+ /* Free up temporary memory */
+
+ lp_talloc_free();
+ main_loop_talloc_free();
+
+ /* Initialise fd lists for select() */
+
+ listen_sock = open_winbindd_socket();
+ listen_priv_sock = open_winbindd_priv_socket();
+
+ if (listen_sock == -1 || listen_priv_sock == -1) {
+ perror("open_winbind_socket");
+ exit(1);
+ }
+
+ maxfd = MAX(listen_sock, listen_priv_sock);
+
+ FD_ZERO(&r_fds);
+ FD_ZERO(&w_fds);
+ FD_SET(listen_sock, &r_fds);
+ FD_SET(listen_priv_sock, &r_fds);
+
+ timeout.tv_sec = WINBINDD_ESTABLISH_LOOP;
+ timeout.tv_usec = 0;
+
+ if (opt_dual_daemon) {
+ maxfd = dual_select_setup(&w_fds, maxfd);
+ }
+
+ /* Set up client readers and writers */
+
+ state = winbindd_client_list();
+
+ while (state) {
+
+ /* Dispose of client connection if it is marked as
+ finished */
+
+ if (state->finished) {
+ struct winbindd_cli_state *next = state->next;
+
+ remove_client(state);
+ state = next;
+ continue;
+ }
+
+ /* Select requires we know the highest fd used */
+
+ if (state->sock > maxfd)
+ maxfd = state->sock;
+
+ /* Add fd for reading */
+
+ if (state->read_buf_len != sizeof(state->request))
+ FD_SET(state->sock, &r_fds);
+
+ /* Add fd for writing */
+
+ if (state->write_buf_len)
+ FD_SET(state->sock, &w_fds);
+
+ state = state->next;
+ }
+
+ /* Call select */
+
+ selret = sys_select(maxfd + 1, &r_fds, &w_fds, NULL, &timeout);
+
+ if (selret == 0)
+ continue;
+
+ if ((selret == -1 && errno != EINTR) || selret == 0) {
+
+ /* Select error, something is badly wrong */
+
+ perror("select");
+ exit(1);
+ }
+
+ /* Create a new connection if listen_sock readable */
+
+ if (selret > 0) {
+
+ if (opt_dual_daemon) {
+ dual_select(&w_fds);
+ }
+
+ if (FD_ISSET(listen_sock, &r_fds)) {
+ while (winbindd_num_clients() > WINBINDD_MAX_SIMULTANEOUS_CLIENTS - 1) {
+ DEBUG(5,("winbindd: Exceeding %d client connections, removing idle connection.\n",
+ WINBINDD_MAX_SIMULTANEOUS_CLIENTS));
+ if (!remove_idle_client()) {
+ DEBUG(0,("winbindd: Exceeding %d client connections, no idle connection found\n",
+ WINBINDD_MAX_SIMULTANEOUS_CLIENTS));
+ break;
+ }
+ }
+ /* new, non-privileged connection */
+ new_connection(listen_sock, False);
+ }
+
+ if (FD_ISSET(listen_priv_sock, &r_fds)) {
+ while (winbindd_num_clients() > WINBINDD_MAX_SIMULTANEOUS_CLIENTS - 1) {
+ DEBUG(5,("winbindd: Exceeding %d client connections, removing idle connection.\n",
+ WINBINDD_MAX_SIMULTANEOUS_CLIENTS));
+ if (!remove_idle_client()) {
+ DEBUG(0,("winbindd: Exceeding %d client connections, no idle connection found\n",
+ WINBINDD_MAX_SIMULTANEOUS_CLIENTS));
+ break;
+ }
+ }
+ /* new, privileged connection */
+ new_connection(listen_priv_sock, True);
+ }
+
+ /* Process activity on client connections */
+
+ for (state = winbindd_client_list(); state;
+ state = state->next) {
+
+ /* Data available for reading */
+
+ if (FD_ISSET(state->sock, &r_fds)) {
+
+ /* Read data */
+
+ winbind_client_read(state);
+
+ /*
+ * If we have the start of a
+ * packet, then check the
+ * length field to make sure
+ * the client's not talking
+ * Mock Swedish.
+ */
+
+ if (state->read_buf_len >= sizeof(uint32)
+ && *(uint32 *) &state->request != sizeof(state->request)) {
+ DEBUG(0,("process_loop: Invalid request size from pid %lu: %d bytes sent, should be %ld\n",
+ (unsigned long)state->request.pid, *(uint32 *) &state->request, (unsigned long)sizeof(state->request)));
+ DEBUGADD(0, ("This usually means that you are running old wbinfo, pam_winbind or libnss_winbind clients\n"));
+
+ remove_client(state);
+ break;
+ }
+
+ /* A request packet might be
+ complete */
+
+ if (state->read_buf_len ==
+ sizeof(state->request)) {
+ winbind_process_packet(state);
+ }
+ }
+
+ /* Data available for writing */
+
+ if (FD_ISSET(state->sock, &w_fds))
+ client_write(state);
+ }
+ }
+
+#if 0
+ winbindd_check_cache_size(time(NULL));
+#endif
+
+ /* Check signal handling things */
+
+ if (do_sigterm)
+ terminate();
+
+ if (do_sighup) {
+
+ DEBUG(3, ("got SIGHUP\n"));
+
+ msg_reload_services(MSG_SMB_CONF_UPDATED, (pid_t) 0, NULL, 0);
+ do_sighup = False;
+ }
+
+ if (do_sigusr2) {
+ print_winbindd_status();
+ do_sigusr2 = False;
+ }
+ }
+}
+
+/* Main function */
+
+struct winbindd_state server_state; /* Server state information */
+
+int main(int argc, char **argv)
+{
+ pstring logfile;
+ static BOOL interactive = False;
+ static BOOL Fork = True;
+ static BOOL log_stdout = False;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "stdout", 'S', POPT_ARG_VAL, &log_stdout, True, "Log to stdout" },
+ { "foreground", 'F', POPT_ARG_VAL, &Fork, False, "Daemon in foreground mode" },
+ { "interactive", 'i', POPT_ARG_NONE, NULL, 'i', "Interactive mode" },
+ { "single-daemon", 'Y', POPT_ARG_VAL, &opt_dual_daemon, False, "Single daemon mode" },
+ { "no-caching", 'n', POPT_ARG_VAL, &opt_nocache, True, "Disable caching" },
+ POPT_COMMON_SAMBA
+ POPT_TABLEEND
+ };
+ poptContext pc;
+ int opt;
+
+ /* glibc (?) likes to print "User defined signal 1" and exit if a
+ SIGUSR[12] is received before a handler is installed */
+
+ CatchSignal(SIGUSR1, SIG_IGN);
+ CatchSignal(SIGUSR2, SIG_IGN);
+
+ fault_setup((void (*)(void *))fault_quit );
+
+ /* Initialise for running in non-root mode */
+
+ sec_init();
+
+ set_remote_machine_name("winbindd", False);
+
+ /* Set environment variable so we don't recursively call ourselves.
+ This may also be useful interactively. */
+
+ setenv(WINBINDD_DONT_ENV, "1", 1);
+
+ /* Initialise samba/rpc client stuff */
+
+ pc = poptGetContext("winbindd", argc, (const char **)argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ /* Don't become a daemon */
+ case 'i':
+ interactive = True;
+ log_stdout = True;
+ Fork = False;
+ break;
+ }
+ }
+
+
+ if (log_stdout && Fork) {
+ printf("Can't log to stdout (-S) unless daemon is in foreground +(-F) or interactive (-i)\n");
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+
+ pstr_sprintf(logfile, "%s/log.winbindd", dyn_LOGFILEBASE);
+ lp_set_logfile(logfile);
+ setup_logging("winbindd", log_stdout);
+ reopen_logs();
+
+ DEBUG(1, ("winbindd version %s started.\n", SAMBA_VERSION_STRING) );
+ DEBUGADD( 1, ( "Copyright The Samba Team 2000-2004\n" ) );
+
+ if (!reload_services_file()) {
+ DEBUG(0, ("error opening config file\n"));
+ exit(1);
+ }
+
+ /* Setup names. */
+
+ if (!init_names())
+ exit(1);
+
+ load_interfaces();
+
+ if (!secrets_init()) {
+
+ DEBUG(0,("Could not initialize domain trust account secrets. Giving up\n"));
+ return False;
+ }
+
+ /* Enable netbios namecache */
+
+ namecache_enable();
+
+ /* Check winbindd parameters are valid */
+
+ ZERO_STRUCT(server_state);
+
+ if (!winbindd_param_init())
+ return 1;
+
+ /* Winbind daemon initialisation */
+
+ if (!winbindd_upgrade_idmap())
+ return 1;
+
+ if (!idmap_init(lp_idmap_backend()))
+ return 1;
+
+ /* Unblock all signals we are interested in as they may have been
+ blocked by the parent process. */
+
+ BlockSignals(False, SIGINT);
+ BlockSignals(False, SIGQUIT);
+ BlockSignals(False, SIGTERM);
+ BlockSignals(False, SIGUSR1);
+ BlockSignals(False, SIGUSR2);
+ BlockSignals(False, SIGHUP);
+
+ /* Setup signal handlers */
+
+ CatchSignal(SIGINT, termination_handler); /* Exit on these sigs */
+ CatchSignal(SIGQUIT, termination_handler);
+ CatchSignal(SIGTERM, termination_handler);
+
+ CatchSignal(SIGPIPE, SIG_IGN); /* Ignore sigpipe */
+
+ CatchSignal(SIGUSR2, sigusr2_handler); /* Debugging sigs */
+ CatchSignal(SIGHUP, sighup_handler);
+
+ if (!interactive)
+ become_daemon(Fork);
+
+ pidfile_create("winbindd");
+
+#if HAVE_SETPGID
+ /*
+ * If we're interactive we want to set our own process group for
+ * signal management.
+ */
+ if (interactive)
+ setpgid( (pid_t)0, (pid_t)0);
+#endif
+
+ if (opt_dual_daemon) {
+ do_dual_daemon();
+ }
+
+ /* Initialise messaging system */
+
+ if (!message_init()) {
+ DEBUG(0, ("unable to initialise messaging system\n"));
+ exit(1);
+ }
+
+ /* React on 'smbcontrol winbindd reload-config' in the same way
+ as to SIGHUP signal */
+ message_register(MSG_SMB_CONF_UPDATED, msg_reload_services);
+ message_register(MSG_SHUTDOWN, msg_shutdown);
+
+ poptFreeContext(pc);
+
+ netsamlogon_cache_init(); /* Non-critical */
+
+ init_domain_list();
+
+ /* Loop waiting for requests */
+
+ process_loop();
+
+ trustdom_cache_shutdown();
+
+ return 0;
+}
diff --git a/source/nsswitch/winbindd.h b/source/nsswitch/winbindd.h
new file mode 100644
index 00000000000..5c05a1b0457
--- /dev/null
+++ b/source/nsswitch/winbindd.h
@@ -0,0 +1,238 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _WINBINDD_H
+#define _WINBINDD_H
+
+#include "nterr.h"
+
+#include "winbindd_nss.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* Client state structure */
+
+struct winbindd_cli_state {
+ struct winbindd_cli_state *prev, *next; /* Linked list pointers */
+ int sock; /* Open socket from client */
+ pid_t pid; /* pid of client */
+ int read_buf_len, write_buf_len; /* Indexes in request/response */
+ BOOL finished; /* Can delete from list */
+ BOOL write_extra_data; /* Write extra_data field */
+ time_t last_access; /* Time of last access (read or write) */
+ BOOL privileged; /* Is the client 'privileged' */
+
+ struct winbindd_request request; /* Request from client */
+ struct winbindd_response response; /* Respose to client */
+ struct getent_state *getpwent_state; /* State for getpwent() */
+ struct getent_state *getgrent_state; /* State for getgrent() */
+};
+
+/* State between get{pw,gr}ent() calls */
+
+struct getent_state {
+ struct getent_state *prev, *next;
+ void *sam_entries;
+ uint32 sam_entry_index, num_sam_entries;
+ BOOL got_sam_entries;
+ fstring domain_name;
+};
+
+/* Storage for cached getpwent() user entries */
+
+struct getpwent_user {
+ fstring name; /* Account name */
+ fstring gecos; /* User information */
+ DOM_SID user_sid; /* NT user and primary group SIDs */
+ DOM_SID group_sid;
+};
+
+/* Server state structure */
+
+struct winbindd_state {
+
+ /* User and group id pool */
+
+ uid_t uid_low, uid_high; /* Range of uids to allocate */
+ gid_t gid_low, gid_high; /* Range of gids to allocate */
+};
+
+extern struct winbindd_state server_state; /* Server information */
+
+typedef struct {
+ char *acct_name;
+ char *full_name;
+ DOM_SID *user_sid; /* NT user and primary group SIDs */
+ DOM_SID *group_sid;
+} WINBIND_USERINFO;
+
+/* Structures to hold per domain information */
+
+struct winbindd_domain {
+ fstring name; /* Domain name */
+ fstring alt_name; /* alt Domain name (if any) */
+ DOM_SID sid; /* SID for this domain */
+ BOOL native_mode; /* is this a win2k domain in native mode ? */
+ BOOL active_directory; /* is this a win2k active directory ? */
+ BOOL primary; /* is this our primary domain ? */
+ BOOL internal; /* BUILTIN and member SAM */
+
+ /* Lookup methods for this domain (LDAP or RPC) */
+ struct winbindd_methods *methods;
+
+ /* the backend methods are used by the cache layer to find the right
+ backend */
+ struct winbindd_methods *backend;
+
+ /* Private data for the backends (used for connection cache) */
+
+ void *private;
+
+ /* Sequence number stuff */
+
+ time_t last_seq_check;
+ uint32 sequence_number;
+ NTSTATUS last_status;
+
+ /* Linked list info */
+
+ struct winbindd_domain *prev, *next;
+};
+
+/* per-domain methods. This is how LDAP vs RPC is selected
+ */
+struct winbindd_methods {
+ /* does this backend provide a consistent view of the data? (ie. is the primary group
+ always correct) */
+ BOOL consistent;
+
+ /* get a list of users, returning a WINBIND_USERINFO for each one */
+ NTSTATUS (*query_user_list)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ WINBIND_USERINFO **info);
+
+ /* get a list of domain groups */
+ NTSTATUS (*enum_dom_groups)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info);
+
+ /* get a list of domain local groups */
+ NTSTATUS (*enum_local_groups)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info);
+
+ /* convert one user or group name to a sid */
+ NTSTATUS (*name_to_sid)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *name,
+ DOM_SID *sid,
+ enum SID_NAME_USE *type);
+
+ /* convert a sid to a user or group name */
+ NTSTATUS (*sid_to_name)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *sid,
+ char **name,
+ enum SID_NAME_USE *type);
+
+ /* lookup user info for a given SID */
+ NTSTATUS (*query_user)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *user_sid,
+ WINBIND_USERINFO *user_info);
+
+ /* lookup all groups that a user is a member of. The backend
+ can also choose to lookup by username or rid for this
+ function */
+ NTSTATUS (*lookup_usergroups)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *user_sid,
+ uint32 *num_groups, DOM_SID ***user_gids);
+
+ /* find all members of the group with the specified group_rid */
+ NTSTATUS (*lookup_groupmem)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *group_sid,
+ uint32 *num_names,
+ DOM_SID ***sid_mem, char ***names,
+ uint32 **name_types);
+
+ /* return the current global sequence number */
+ NTSTATUS (*sequence_number)(struct winbindd_domain *domain, uint32 *seq);
+
+ /* enumerate trusted domains */
+ NTSTATUS (*trusted_domains)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_domains,
+ char ***names,
+ char ***alt_names,
+ DOM_SID **dom_sids);
+
+ /* find the domain sid */
+ NTSTATUS (*domain_sid)(struct winbindd_domain *domain,
+ DOM_SID *sid);
+
+ /* setup the list of alternate names for the domain, if any */
+ NTSTATUS (*alternate_name)(struct winbindd_domain *domain);
+};
+
+/* Used to glue a policy handle and cli_state together */
+
+typedef struct {
+ struct cli_state *cli;
+ POLICY_HND pol;
+} CLI_POLICY_HND;
+
+/* Filled out by IDMAP backends */
+struct winbindd_idmap_methods {
+ /* Called when backend is first loaded */
+ BOOL (*init)(void);
+
+ BOOL (*get_sid_from_uid)(uid_t uid, DOM_SID *sid);
+ BOOL (*get_sid_from_gid)(gid_t gid, DOM_SID *sid);
+
+ BOOL (*get_uid_from_sid)(DOM_SID *sid, uid_t *uid);
+ BOOL (*get_gid_from_sid)(DOM_SID *sid, gid_t *gid);
+
+ /* Called when backend is unloaded */
+ BOOL (*close)(void);
+ /* Called to dump backend status */
+ void (*status)(void);
+};
+
+#include "../nsswitch/winbindd_proto.h"
+
+#include "rpc_parse.h"
+#include "rpc_client.h"
+
+#define WINBINDD_ESTABLISH_LOOP 30
+#define WINBINDD_RESCAN_FREQ 300
+
+#define DOM_SEQUENCE_NONE ((uint32)-1)
+
+#endif /* _WINBINDD_H */
diff --git a/source/nsswitch/winbindd_acct.c b/source/nsswitch/winbindd_acct.c
new file mode 100644
index 00000000000..e6496695cb6
--- /dev/null
+++ b/source/nsswitch/winbindd_acct.c
@@ -0,0 +1,1223 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind account management functions
+
+ Copyright (C) by Gerald (Jerry) Carter 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+#define WBKEY_PASSWD "WBA_PASSWD"
+#define WBKEY_GROUP "WBA_GROUP"
+
+#define NUM_PW_FIELDS 7
+#define NUM_GRP_FIELDS 4
+
+/* Globals */
+
+static TDB_CONTEXT *account_tdb;
+
+extern userdom_struct current_user_info;
+
+struct _check_primary_grp {
+ gid_t gid;
+ BOOL found;
+};
+
+/**********************************************************************
+**********************************************************************/
+
+static void free_winbindd_gr( WINBINDD_GR *grp )
+{
+ int i;
+
+ if ( !grp )
+ return;
+
+ for ( i=0; i<grp->num_gr_mem; i++ )
+ SAFE_FREE( grp->gr_mem[i] );
+
+ SAFE_FREE( grp->gr_mem );
+
+ return;
+}
+
+/*****************************************************************************
+ Initialise auto-account database.
+*****************************************************************************/
+
+static BOOL winbindd_accountdb_init(void)
+{
+ /* see if we've already opened the tdb */
+
+ if ( account_tdb )
+ return True;
+
+ /* winbindd_idmap.tdb should always be opened by the idmap_init()
+ code first */
+
+ if ( !(account_tdb = idmap_tdb_handle()) ) {
+ DEBUG(0, ("winbindd_accountdb_init: Unable to retreive handle for database\n"));
+ return False;
+ }
+
+ /* yeah! */
+
+ return True;
+}
+
+/**********************************************************************
+ Convert a string in /etc/passwd format to a struct passwd* entry
+**********************************************************************/
+
+static WINBINDD_PW* string2passwd( char *string )
+{
+ static WINBINDD_PW pw;
+ char *p, *str;
+ char *fields[NUM_PW_FIELDS];
+ int i;
+
+ if ( !string )
+ return NULL;
+
+ ZERO_STRUCTP( &pw );
+
+ DEBUG(10,("string2passwd: converting \"%s\"\n", string));
+
+ ZERO_STRUCT( fields );
+
+ for ( i=0, str=string; i<NUM_PW_FIELDS-1; i++ ) {
+ if ( !(p = strchr( str, ':' )) ) {
+ DEBUG(0,("string2passwd: parsing failure\n"));
+ return NULL;
+ }
+ *p = '\0';
+ if ( str )
+ fields[i] = str;
+ str = p + 1;
+ }
+ if ( str )
+ fields[i] = str;
+
+ /* copy fields */
+
+ fstrcpy( pw.pw_name, fields[0] );
+ fstrcpy( pw.pw_passwd, fields[1] );
+ pw.pw_uid = atoi( fields[2] );
+ pw.pw_gid = atoi( fields[3] );
+ fstrcpy( pw.pw_gecos, fields[4] );
+ fstrcpy( pw.pw_dir, fields[5] );
+ fstrcpy( pw.pw_shell, fields[6] );
+
+
+ /* last minute sanity checks */
+
+ if ( pw.pw_uid==0 || pw.pw_gid==0 ) {
+ DEBUG(0,("string2passwd: Failure! uid==%lu, gid==%lu\n",
+ (unsigned long)pw.pw_uid, (unsigned long)pw.pw_gid));
+ return NULL;
+ }
+
+ DEBUG(10,("string2passwd: Success\n"));
+
+ return &pw;
+}
+
+/**********************************************************************
+ Convert a struct passwd* to a string formatted for /etc/passwd
+**********************************************************************/
+
+static char* passwd2string( const WINBINDD_PW *pw )
+{
+ static pstring string;
+ int ret;
+
+ if ( !pw || !pw->pw_name )
+ return NULL;
+
+ DEBUG(10,("passwd2string: converting passwd struct for %s\n",
+ pw->pw_name));
+
+ ret = pstr_sprintf( string, "%s:%s:%lu:%lu:%s:%s:%s",
+ pw->pw_name,
+ pw->pw_passwd ? pw->pw_passwd : "x",
+ (unsigned long)pw->pw_uid,
+ (unsigned long)pw->pw_gid,
+ pw->pw_gecos,
+ pw->pw_dir,
+ pw->pw_shell );
+
+ if ( ret < 0 ) {
+ DEBUG(0,("passwd2string: pstr_sprintf() failed!\n"));
+ return NULL;
+ }
+
+ return string;
+}
+
+/**********************************************************************
+ Convert a string in /etc/group format to a struct group* entry
+**********************************************************************/
+
+static WINBINDD_GR* string2group( char *string )
+{
+ static WINBINDD_GR grp;
+ char *p, *str;
+ char *fields[NUM_GRP_FIELDS];
+ int i;
+ char **gr_members = NULL;
+ int num_gr_members = 0;
+
+ if ( !string )
+ return NULL;
+
+ ZERO_STRUCTP( &grp );
+
+ DEBUG(10,("string2group: converting \"%s\"\n", string));
+
+ ZERO_STRUCT( fields );
+
+ for ( i=0, str=string; i<NUM_GRP_FIELDS-1; i++ ) {
+ if ( !(p = strchr( str, ':' )) ) {
+ DEBUG(0,("string2group: parsing failure\n"));
+ return NULL;
+ }
+ *p = '\0';
+ if ( str )
+ fields[i] = str;
+ str = p + 1;
+ }
+
+ /* group members */
+
+ if ( *str ) {
+ /* we already know we have a non-empty string */
+
+ num_gr_members = count_chars(str, ',') + 1;
+
+ /* if there was at least one comma, then there
+ are n+1 members */
+ if ( num_gr_members ) {
+ fstring buffer;
+
+ gr_members = (char**)smb_xmalloc(sizeof(char*)*(num_gr_members+1));
+
+ i = 0;
+ while ( next_token(&str, buffer, ",", sizeof(buffer)) && i<num_gr_members ) {
+ gr_members[i++] = smb_xstrdup(buffer);
+ }
+
+ gr_members[i] = NULL;
+ }
+ }
+
+
+ /* copy fields */
+
+ fstrcpy( grp.gr_name, fields[0] );
+ fstrcpy( grp.gr_passwd, fields[1] );
+ grp.gr_gid = atoi( fields[2] );
+
+ grp.num_gr_mem = num_gr_members;
+ grp.gr_mem = gr_members;
+
+ /* last minute sanity checks */
+
+ if ( grp.gr_gid == 0 ) {
+ DEBUG(0,("string2group: Failure! gid==%lu\n", (unsigned long)grp.gr_gid));
+ SAFE_FREE( gr_members );
+ return NULL;
+ }
+
+ DEBUG(10,("string2group: Success\n"));
+
+ return &grp;
+}
+
+/**********************************************************************
+ Convert a struct group* to a string formatted for /etc/group
+**********************************************************************/
+
+static char* group2string( const WINBINDD_GR *grp )
+{
+ static pstring string;
+ int ret;
+ char *member, *gr_mem_str;
+ int num_members;
+ int i, size;
+
+ if ( !grp || !grp->gr_name )
+ return NULL;
+
+ DEBUG(10,("group2string: converting passwd struct for %s\n",
+ grp->gr_name));
+
+ if ( grp->num_gr_mem ) {
+ int idx = 0;
+
+ member = grp->gr_mem[0];
+ size = 0;
+ num_members = 0;
+
+ while ( member ) {
+ size += strlen(member) + 1;
+ num_members++;
+ member = grp->gr_mem[num_members];
+ }
+
+ gr_mem_str = smb_xmalloc(size);
+
+ for ( i=0; i<num_members; i++ ) {
+ snprintf( &gr_mem_str[idx], size-idx, "%s,", grp->gr_mem[i] );
+ idx += strlen(grp->gr_mem[i]) + 1;
+ }
+ /* add trailing NULL (also removes trailing ',' */
+ gr_mem_str[size-1] = '\0';
+ }
+ else {
+ /* no members */
+ gr_mem_str = smb_xmalloc(sizeof(fstring));
+ fstrcpy( gr_mem_str, "" );
+ }
+
+ ret = pstr_sprintf( string, "%s:%s:%lu:%s",
+ grp->gr_name,
+ grp->gr_passwd ? grp->gr_passwd : "*",
+ (unsigned long)grp->gr_gid,
+ gr_mem_str );
+
+ SAFE_FREE( gr_mem_str );
+
+ if ( ret < 0 ) {
+ DEBUG(0,("group2string: pstr_sprintf() failed!\n"));
+ return NULL;
+ }
+
+ return string;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+static char* acct_userkey_byname( const char *name )
+{
+ static fstring key;
+
+ fstr_sprintf( key, "%s/NAME/%s", WBKEY_PASSWD, name );
+
+ return key;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+static char* acct_userkey_byuid( uid_t uid )
+{
+ static fstring key;
+
+ fstr_sprintf( key, "%s/UID/%lu", WBKEY_PASSWD, (unsigned long)uid );
+
+ return key;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+static char* acct_groupkey_byname( const char *name )
+{
+ static fstring key;
+
+ fstr_sprintf( key, "%s/NAME/%s", WBKEY_GROUP, name );
+
+ return key;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+static char* acct_groupkey_bygid( gid_t gid )
+{
+ static fstring key;
+
+ fstr_sprintf( key, "%s/GID/%lu", WBKEY_GROUP, (unsigned long)gid );
+
+ return key;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+WINBINDD_PW* wb_getpwnam( const char * name )
+{
+ char *keystr;
+ TDB_DATA data;
+ static WINBINDD_PW *pw;
+
+ if ( !account_tdb && !winbindd_accountdb_init() ) {
+ DEBUG(0,("wb_getpwnam: Failed to open winbindd account db\n"));
+ return NULL;
+ }
+
+
+ keystr = acct_userkey_byname( name );
+
+ data = tdb_fetch_bystring( account_tdb, keystr );
+
+ pw = NULL;
+
+ if ( data.dptr ) {
+ pw = string2passwd( data.dptr );
+ SAFE_FREE( data.dptr );
+ }
+
+ DEBUG(5,("wb_getpwnam: %s user (%s)\n",
+ (pw ? "Found" : "Did not find"), name ));
+
+ return pw;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+WINBINDD_PW* wb_getpwuid( const uid_t uid )
+{
+ char *keystr;
+ TDB_DATA data;
+ static WINBINDD_PW *pw;
+
+ if ( !account_tdb && !winbindd_accountdb_init() ) {
+ DEBUG(0,("wb_getpwuid: Failed to open winbindd account db\n"));
+ return NULL;
+ }
+
+ data = tdb_fetch_bystring( account_tdb, acct_userkey_byuid(uid) );
+ if ( !data.dptr ) {
+ DEBUG(4,("wb_getpwuid: failed to locate uid == %lu\n", (unsigned long)uid));
+ return NULL;
+ }
+ keystr = acct_userkey_byname( data.dptr );
+
+ SAFE_FREE( data.dptr );
+
+ data = tdb_fetch_bystring( account_tdb, keystr );
+
+ pw = NULL;
+
+ if ( data.dptr ) {
+ pw = string2passwd( data.dptr );
+ SAFE_FREE( data.dptr );
+ }
+
+ DEBUG(5,("wb_getpwuid: %s user (uid == %lu)\n",
+ (pw ? "Found" : "Did not find"), (unsigned long)uid ));
+
+ return pw;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+static BOOL wb_storepwnam( const WINBINDD_PW *pw )
+{
+ char *namekey, *uidkey;
+ TDB_DATA data;
+ char *str;
+ int ret = 0;
+ fstring username;
+
+ if ( !account_tdb && !winbindd_accountdb_init() ) {
+ DEBUG(0,("wb_storepwnam: Failed to open winbindd account db\n"));
+ return False;
+ }
+
+ namekey = acct_userkey_byname( pw->pw_name );
+
+ /* lock the main entry first */
+
+ if ( tdb_lock_bystring(account_tdb, namekey, 0) == -1 ) {
+ DEBUG(0,("wb_storepwnam: Failed to lock %s\n", namekey));
+ return False;
+ }
+
+ str = passwd2string( pw );
+
+ data.dptr = str;
+ data.dsize = strlen(str) + 1;
+
+ if ( (tdb_store_bystring(account_tdb, namekey, data, TDB_REPLACE)) == -1 ) {
+ DEBUG(0,("wb_storepwnam: Failed to store \"%s\"\n", str));
+ ret = -1;
+ goto done;
+ }
+
+ /* store the uid index */
+
+ uidkey = acct_userkey_byuid(pw->pw_uid);
+
+ fstrcpy( username, pw->pw_name );
+ data.dptr = username;
+ data.dsize = strlen(username) + 1;
+
+ if ( (tdb_store_bystring(account_tdb, uidkey, data, TDB_REPLACE)) == -1 ) {
+ DEBUG(0,("wb_storepwnam: Failed to store uid key \"%s\"\n", str));
+ tdb_delete_bystring(account_tdb, namekey);
+ ret = -1;
+ goto done;
+ }
+
+ DEBUG(10,("wb_storepwnam: Success -> \"%s\"\n", str));
+
+done:
+ tdb_unlock_bystring( account_tdb, namekey );
+
+ return ( ret == 0 );
+}
+
+/**********************************************************************
+**********************************************************************/
+
+WINBINDD_GR* wb_getgrnam( const char * name )
+{
+ char *keystr;
+ TDB_DATA data;
+ static WINBINDD_GR *grp;
+
+ if ( !account_tdb && !winbindd_accountdb_init() ) {
+ DEBUG(0,("wb_getgrnam: Failed to open winbindd account db\n"));
+ return NULL;
+ }
+
+
+ keystr = acct_groupkey_byname( name );
+
+ data = tdb_fetch_bystring( account_tdb, keystr );
+
+ grp = NULL;
+
+ if ( data.dptr ) {
+ grp = string2group( data.dptr );
+ SAFE_FREE( data.dptr );
+ }
+
+ DEBUG(5,("wb_getgrnam: %s group (%s)\n",
+ (grp ? "Found" : "Did not find"), name ));
+
+ return grp;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+WINBINDD_GR* wb_getgrgid( gid_t gid )
+{
+ char *keystr;
+ TDB_DATA data;
+ static WINBINDD_GR *grp;
+
+ if ( !account_tdb && !winbindd_accountdb_init() ) {
+ DEBUG(0,("wb_getgrgid: Failed to open winbindd account db\n"));
+ return NULL;
+ }
+
+ data = tdb_fetch_bystring( account_tdb, acct_groupkey_bygid(gid) );
+ if ( !data.dptr ) {
+ DEBUG(4,("wb_getgrgid: failed to locate gid == %lu\n",
+ (unsigned long)gid));
+ return NULL;
+ }
+ keystr = acct_groupkey_byname( data.dptr );
+
+ SAFE_FREE( data.dptr );
+
+ data = tdb_fetch_bystring( account_tdb, keystr );
+
+ grp = NULL;
+
+ if ( data.dptr ) {
+ grp = string2group( data.dptr );
+ SAFE_FREE( data.dptr );
+ }
+
+ DEBUG(5,("wb_getgrgid: %s group (gid == %lu)\n",
+ (grp ? "Found" : "Did not find"), (unsigned long)gid ));
+
+ return grp;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+static BOOL wb_storegrnam( const WINBINDD_GR *grp )
+{
+ char *namekey, *gidkey;
+ TDB_DATA data;
+ char *str;
+ int ret = 0;
+ fstring groupname;
+
+ if ( !account_tdb && !winbindd_accountdb_init() ) {
+ DEBUG(0,("wb_storepwnam: Failed to open winbindd account db\n"));
+ return False;
+ }
+
+ namekey = acct_groupkey_byname( grp->gr_name );
+
+ /* lock the main entry first */
+
+ if ( tdb_lock_bystring(account_tdb, namekey, 0) == -1 ) {
+ DEBUG(0,("wb_storegrnam: Failed to lock %s\n", namekey));
+ return False;
+ }
+
+ str = group2string( grp );
+
+ data.dptr = str;
+ data.dsize = strlen(str) + 1;
+
+ if ( (tdb_store_bystring(account_tdb, namekey, data, TDB_REPLACE)) == -1 ) {
+ DEBUG(0,("wb_storegrnam: Failed to store \"%s\"\n", str));
+ ret = -1;
+ goto done;
+ }
+
+ /* store the gid index */
+
+ gidkey = acct_groupkey_bygid(grp->gr_gid);
+
+ fstrcpy( groupname, grp->gr_name );
+ data.dptr = groupname;
+ data.dsize = strlen(groupname) + 1;
+
+ if ( (tdb_store_bystring(account_tdb, gidkey, data, TDB_REPLACE)) == -1 ) {
+ DEBUG(0,("wb_storegrnam: Failed to store gid key \"%s\"\n", str));
+ tdb_delete_bystring(account_tdb, namekey);
+ ret = -1;
+ goto done;
+ }
+
+ DEBUG(10,("wb_storegrnam: Success -> \"%s\"\n", str));
+
+done:
+ tdb_unlock_bystring( account_tdb, namekey );
+
+ return ( ret == 0 );
+}
+
+/**********************************************************************
+**********************************************************************/
+
+static BOOL wb_addgrpmember( WINBINDD_GR *grp, const char *user )
+{
+ int i;
+ char **members;
+
+ if ( !grp || !user )
+ return False;
+
+ for ( i=0; i<grp->num_gr_mem; i++ ) {
+ if ( StrCaseCmp( grp->gr_mem[i], user ) == 0 )
+ return True;
+ }
+
+ /* add one new slot and keep an extra for the terminating NULL */
+ members = Realloc( grp->gr_mem, (grp->num_gr_mem+2)*sizeof(char*) );
+ if ( !members )
+ return False;
+
+ grp->gr_mem = members;
+ grp->gr_mem[grp->num_gr_mem++] = smb_xstrdup(user);
+ grp->gr_mem[grp->num_gr_mem] = NULL;
+
+ return True;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+static BOOL wb_delgrpmember( WINBINDD_GR *grp, const char *user )
+{
+ int i;
+ BOOL found = False;
+
+ if ( !grp || !user )
+ return False;
+
+ for ( i=0; i<grp->num_gr_mem; i++ ) {
+ if ( StrCaseCmp( grp->gr_mem[i], user ) == 0 ) {
+ found = True;
+ break;
+ }
+ }
+
+ if ( !found )
+ return False;
+
+ /* still some remaining members */
+
+ if ( grp->num_gr_mem > 1 ) {
+ SAFE_FREE(grp->gr_mem[i]);
+ grp->num_gr_mem--;
+ grp->gr_mem[i] = grp->gr_mem[grp->num_gr_mem];
+ grp->gr_mem[grp->num_gr_mem] = NULL;
+ }
+ else { /* last one */
+ free_winbindd_gr( grp );
+ grp->gr_mem = NULL;
+ grp->num_gr_mem = 0;
+ }
+
+ return True;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+static int cleangroups_traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
+ void *state)
+{
+ int len;
+ fstring key;
+ char *name = (char*)state;
+
+ fstr_sprintf( key, "%s/NAME", WBKEY_GROUP );
+ len = strlen(key);
+
+ /* if this is a group entry then, check the members */
+
+ if ( (strncmp(kbuf.dptr, key, len) == 0) && dbuf.dptr ) {
+ WINBINDD_GR *grp;
+
+ if ( !(grp = string2group( dbuf.dptr )) ) {
+ DEBUG(0,("cleangroups_traverse_fn: Failure to parse [%s]\n",
+ dbuf.dptr));
+ return 0;
+ }
+
+ /* just try to delete the user and rely on wb_delgrpmember()
+ to tell you whether or not the group changed. This is more
+ effecient than testing group membership first since the
+ checks for deleting a user from a group is essentially the
+ same as checking if he/she is a member */
+
+ if ( wb_delgrpmember( grp, name ) ) {
+ DEBUG(10,("cleanupgroups_traverse_fn: Removed user (%s) from group (%s)\n",
+ name, grp->gr_name));
+ wb_storegrnam( grp );
+ }
+
+ free_winbindd_gr( grp );
+ }
+
+ return 0;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+static BOOL wb_delete_user( WINBINDD_PW *pw)
+{
+ char *namekey;
+ char *uidkey;
+
+ if ( !account_tdb && !winbindd_accountdb_init() ) {
+ DEBUG(0,("wb_delete_user: Failed to open winbindd account db\n"));
+ return False;
+ }
+
+ namekey = acct_userkey_byname( pw->pw_name );
+
+ /* lock the main entry first */
+
+ if ( tdb_lock_bystring(account_tdb, namekey, 0) == -1 ) {
+ DEBUG(0,("wb_delete_user: Failed to lock %s\n", namekey));
+ return False;
+ }
+
+ /* remove user from all groups */
+
+ tdb_traverse(account_tdb, cleangroups_traverse_fn, (void *)pw->pw_name);
+
+ /* remove the user */
+ uidkey = acct_userkey_byuid( pw->pw_uid );
+
+ tdb_delete_bystring( account_tdb, namekey );
+ tdb_delete_bystring( account_tdb, uidkey );
+
+ tdb_unlock_bystring( account_tdb, namekey );
+
+ return True;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+static int isprimarygroup_traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
+ TDB_DATA dbuf, void *params)
+{
+ int len;
+ fstring key;
+ struct _check_primary_grp *check = (struct _check_primary_grp*)params;
+
+ fstr_sprintf( key, "%s/NAME", WBKEY_PASSWD );
+ len = strlen(key);
+
+ /* if this is a group entry then, check the members */
+
+ if ( (strncmp(kbuf.dptr, key, len) == 0) && dbuf.dptr ) {
+ WINBINDD_PW *pw;;
+
+ if ( !(pw = string2passwd( dbuf.dptr )) ) {
+ DEBUG(0,("isprimarygroup_traverse_fn: Failure to parse [%s]\n",
+ dbuf.dptr));
+ return 0;
+ }
+
+ if ( check->gid == pw->pw_gid ) {
+ check->found = True;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+/**********************************************************************
+**********************************************************************/
+
+static BOOL wb_delete_group( WINBINDD_GR *grp )
+{
+ struct _check_primary_grp check;
+ char *namekey;
+ char *gidkey;
+
+ if ( !account_tdb && !winbindd_accountdb_init() ) {
+ DEBUG(0,("wb_delete_group: Failed to open winbindd account db\n"));
+ return False;
+ }
+
+ /* lock the main entry first */
+
+ namekey = acct_groupkey_byname( grp->gr_name );
+ if ( tdb_lock_bystring(account_tdb, namekey, 0) == -1 ) {
+ DEBUG(0,("wb_delete_group: Failed to lock %s\n", namekey));
+ return False;
+ }
+
+ /* is this group the primary group for any user? If
+ so deny delete */
+
+ check.found = False;
+ tdb_traverse(account_tdb, isprimarygroup_traverse_fn, (void *)&check);
+
+ if ( check.found ) {
+ DEBUG(4,("wb_delete_group: Cannot delete group (%s) since it "
+ "is the primary group for some users\n", grp->gr_name));
+ return False;
+ }
+
+ /* We're clear. Delete the group */
+
+ DEBUG(5,("wb_delete_group: Removing group (%s)\n", grp->gr_name));
+
+ gidkey = acct_groupkey_bygid( grp->gr_gid );
+
+ tdb_delete_bystring( account_tdb, namekey );
+ tdb_delete_bystring( account_tdb, gidkey );
+
+ tdb_unlock_bystring( account_tdb, namekey );
+
+ return True;
+}
+
+/**********************************************************************
+ Create a new "UNIX" user for the system given a username
+**********************************************************************/
+
+enum winbindd_result winbindd_create_user(struct winbindd_cli_state *state)
+{
+ char *user, *group;
+ unid_t id;
+ WINBINDD_PW pw, *pw_check;
+ WINBINDD_GR *wb_grp;
+ struct group *unix_grp;
+ gid_t primary_gid;
+ uint32 flags = state->request.flags;
+ uint32 rid;
+
+ if ( !state->privileged ) {
+ DEBUG(2, ("winbindd_create_user: non-privileged access denied!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* Ensure null termination */
+ state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.username)-1]='\0';
+ state->request.data.acct_mgt.groupname[sizeof(state->request.data.acct_mgt.groupname)-1]='\0';
+
+ user = state->request.data.acct_mgt.username;
+ group = state->request.data.acct_mgt.groupname;
+
+ DEBUG(3, ("[%5lu]: create_user: user=>(%s), group=>(%s)\n",
+ (unsigned long)state->pid, user, group));
+
+ if ( (pw_check=wb_getpwnam(user)) != NULL ) {
+ DEBUG(0,("winbindd_create_user: Refusing to create user that already exists (%s)\n",
+ user));
+ return WINBINDD_ERROR;
+ }
+
+
+ if ( !*group )
+ group = lp_template_primary_group();
+
+ /* validate the primary group
+ 1) lookup in local tdb first
+ 2) call getgrnam() as a last resort */
+
+ if ( (wb_grp=wb_getgrnam(group)) != NULL ) {
+ primary_gid = wb_grp->gr_gid;
+ free_winbindd_gr( wb_grp );
+ }
+ else if ( (unix_grp=sys_getgrnam(group)) != NULL ) {
+ primary_gid = unix_grp->gr_gid;
+ }
+ else {
+ DEBUG(2,("winbindd_create_user: Cannot validate gid for group (%s)\n", group));
+ return WINBINDD_ERROR;
+ }
+
+ /* get a new uid */
+
+ if ( !NT_STATUS_IS_OK(idmap_allocate_id( &id, ID_USERID)) ) {
+ DEBUG(0,("winbindd_create_user: idmap_allocate_id() failed!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* The substitution of %U and %D in the 'template homedir' is done
+ by lp_string() calling standard_sub_basic(). */
+
+ fstrcpy( current_user_info.smb_name, user );
+ sub_set_smb_name( user );
+ fstrcpy( current_user_info.domain, get_global_sam_name() );
+
+ /* fill in the passwd struct */
+
+ fstrcpy( pw.pw_name, user );
+ fstrcpy( pw.pw_passwd, "x" );
+ fstrcpy( pw.pw_gecos, user);
+ fstrcpy( pw.pw_dir, lp_template_homedir() );
+ fstrcpy( pw.pw_shell, lp_template_shell() );
+
+ pw.pw_uid = id.uid;
+ pw.pw_gid = primary_gid;
+
+ /* store the new entry */
+
+ if ( !wb_storepwnam(&pw) )
+ return WINBINDD_ERROR;
+
+ /* do we need a new RID? */
+
+ if ( flags & WBFLAG_ALLOCATE_RID ) {
+ if ( !NT_STATUS_IS_OK(idmap_allocate_rid(&rid, USER_RID_TYPE)) ) {
+ DEBUG(0,("winbindd_create_user: RID allocation failure! Cannot create user (%s)\n",
+ user));
+ wb_delete_user( &pw );
+
+ return WINBINDD_ERROR;
+ }
+
+ state->response.data.rid = rid;
+ }
+
+ return WINBINDD_OK;
+}
+
+/**********************************************************************
+ Create a new "UNIX" group for the system given a username
+**********************************************************************/
+
+enum winbindd_result winbindd_create_group(struct winbindd_cli_state *state)
+{
+ char *group;
+ unid_t id;
+ WINBINDD_GR grp, *grp_check;
+ uint32 flags = state->request.flags;
+ uint32 rid;
+
+ if ( !state->privileged ) {
+ DEBUG(2, ("winbindd_create_group: non-privileged access denied!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* Ensure null termination */
+ state->request.data.acct_mgt.groupname[sizeof(state->request.data.acct_mgt.groupname)-1]='\0';
+ group = state->request.data.acct_mgt.groupname;
+
+ DEBUG(3, ("[%5lu]: create_group: (%s)\n", (unsigned long)state->pid, group));
+
+ if ( (grp_check=wb_getgrnam(group)) != NULL ) {
+ DEBUG(0,("winbindd_create_group: Refusing to create group that already exists (%s)\n",
+ group));
+ return WINBINDD_ERROR;
+ }
+
+ /* get a new gid */
+
+ if ( !NT_STATUS_IS_OK(idmap_allocate_id( &id, ID_GROUPID)) ) {
+ DEBUG(0,("winbindd_create_group: idmap_allocate_id() failed!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* fill in the group struct */
+
+ fstrcpy( grp.gr_name, group );
+ fstrcpy( grp.gr_passwd, "*" );
+
+ grp.gr_gid = id.gid;
+ grp.gr_mem = NULL; /* start with no members */
+ grp.num_gr_mem = 0;
+
+ if ( !wb_storegrnam(&grp) )
+ return WINBINDD_ERROR;
+
+ /* do we need a new RID? */
+
+ if ( flags & WBFLAG_ALLOCATE_RID ) {
+ if ( !NT_STATUS_IS_OK(idmap_allocate_rid(&rid, GROUP_RID_TYPE)) ) {
+ DEBUG(0,("winbindd_create_group: RID allocation failure! Cannot create group (%s)\n",
+ group));
+ wb_delete_group( &grp );
+
+ return WINBINDD_ERROR;
+ }
+
+ state->response.data.rid = rid;
+ }
+
+ return WINBINDD_OK;
+}
+
+/**********************************************************************
+ Add a user to the membership for a group.
+**********************************************************************/
+
+enum winbindd_result winbindd_add_user_to_group(struct winbindd_cli_state *state)
+{
+ WINBINDD_PW *pw;
+ WINBINDD_GR *grp;
+ char *user, *group;
+ BOOL ret;
+
+ if ( !state->privileged ) {
+ DEBUG(2, ("winbindd_add_user_to_group: non-privileged access denied!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* Ensure null termination */
+ state->request.data.acct_mgt.groupname[sizeof(state->request.data.acct_mgt.groupname)-1]='\0';
+ state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.username)-1]='\0';
+ group = state->request.data.acct_mgt.groupname;
+ user = state->request.data.acct_mgt.username;
+
+ DEBUG(3, ("[%5lu]: add_user_to_group: add %s to %s\n", (unsigned long)state->pid,
+ user, group));
+
+ /* make sure it is a valid user */
+
+ if ( !(pw = wb_getpwnam( user )) ) {
+ DEBUG(4,("winbindd_add_user_to_group: Cannot add a non-existent user\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* make sure it is a valid group */
+
+ if ( !(grp = wb_getgrnam( group )) ) {
+ DEBUG(4,("winbindd_add_user_to_group: Cannot add a user to a non-extistent group\n"));
+ return WINBINDD_ERROR;
+ }
+
+ if ( !wb_addgrpmember( grp, user ) )
+ return WINBINDD_ERROR;
+
+ ret = wb_storegrnam(grp);
+
+ free_winbindd_gr( grp );
+
+ return ( ret ? WINBINDD_OK : WINBINDD_ERROR );
+}
+
+/**********************************************************************
+ Remove a user from the membership of a group
+**********************************************************************/
+
+enum winbindd_result winbindd_remove_user_from_group(struct winbindd_cli_state *state)
+{
+ WINBINDD_GR *grp;
+ char *user, *group;
+ BOOL ret;
+
+ if ( !state->privileged ) {
+ DEBUG(2, ("winbindd_remove_user_from_group: non-privileged access denied!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* Ensure null termination */
+ state->request.data.acct_mgt.groupname[sizeof(state->request.data.acct_mgt.groupname)-1]='\0';
+ state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.username)-1]='\0';
+ group = state->request.data.acct_mgt.groupname;
+ user = state->request.data.acct_mgt.username;
+
+ DEBUG(3, ("[%5lu]: remove_user_from_group: delete %s from %s\n", (unsigned long)state->pid,
+ user, group));
+
+ /* don't worry about checking the username since we're removing it anyways */
+
+ /* make sure it is a valid group */
+
+ if ( !(grp = wb_getgrnam( group )) ) {
+ DEBUG(4,("winbindd_remove_user_from_group: Cannot remove a user from a non-extistent group\n"));
+ return WINBINDD_ERROR;
+ }
+
+ if ( !wb_delgrpmember( grp, user ) )
+ return WINBINDD_ERROR;
+
+ ret = wb_storegrnam(grp);
+
+ free_winbindd_gr( grp );
+
+ return ( ret ? WINBINDD_OK : WINBINDD_ERROR );
+}
+
+/**********************************************************************
+ Set the primary group membership of a user
+**********************************************************************/
+
+enum winbindd_result winbindd_set_user_primary_group(struct winbindd_cli_state *state)
+{
+ WINBINDD_PW *pw;
+ WINBINDD_GR *grp;
+ char *user, *group;
+
+ if ( !state->privileged ) {
+ DEBUG(2, ("winbindd_set_user_primary_group: non-privileged access denied!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* Ensure null termination */
+ state->request.data.acct_mgt.groupname[sizeof(state->request.data.acct_mgt.groupname)-1]='\0';
+ state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.username)-1]='\0';
+ group = state->request.data.acct_mgt.groupname;
+ user = state->request.data.acct_mgt.username;
+
+ DEBUG(3, ("[%5lu]: set_user_primary_group: group %s for user %s\n",
+ (unsigned long)state->pid, group, user));
+
+ /* make sure it is a valid user */
+
+ if ( !(pw = wb_getpwnam( user )) ) {
+ DEBUG(4,("winbindd_add_user_to_group: Cannot add a non-existent user\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* make sure it is a valid group */
+
+ if ( !(grp = wb_getgrnam( group )) ) {
+ DEBUG(4,("winbindd_add_user_to_group: Cannot add a user to a non-extistent group\n"));
+ return WINBINDD_ERROR;
+ }
+
+ pw->pw_gid = grp->gr_gid;
+
+ free_winbindd_gr( grp );
+
+ return ( wb_storepwnam(pw) ? WINBINDD_OK : WINBINDD_ERROR );
+}
+
+/**********************************************************************
+ Delete a user from the winbindd account tdb.
+**********************************************************************/
+
+enum winbindd_result winbindd_delete_user(struct winbindd_cli_state *state)
+{
+ WINBINDD_PW *pw;
+ char *user;
+
+ if ( !state->privileged ) {
+ DEBUG(2, ("winbindd_delete_user: non-privileged access denied!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* Ensure null termination */
+ state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.username)-1]='\0';
+ user = state->request.data.acct_mgt.username;
+
+ DEBUG(3, ("[%5lu]: delete_user: %s\n", (unsigned long)state->pid, user));
+
+ /* make sure it is a valid user */
+
+ if ( !(pw = wb_getpwnam( user )) ) {
+ DEBUG(4,("winbindd_delete_user: Cannot delete a non-existent user\n"));
+ return WINBINDD_ERROR;
+ }
+
+ return ( wb_delete_user(pw) ? WINBINDD_OK : WINBINDD_ERROR );
+}
+
+/**********************************************************************
+ Delete a group from winbindd's account tdb.
+**********************************************************************/
+
+enum winbindd_result winbindd_delete_group(struct winbindd_cli_state *state)
+{
+ WINBINDD_GR *grp;
+ char *group;
+ BOOL ret;
+
+ if ( !state->privileged ) {
+ DEBUG(2, ("winbindd_delete_group: non-privileged access denied!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* Ensure null termination */
+ state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.groupname)-1]='\0';
+ group = state->request.data.acct_mgt.groupname;
+
+ DEBUG(3, ("[%5lu]: delete_group: %s\n", (unsigned long)state->pid, group));
+
+ /* make sure it is a valid group */
+
+ if ( !(grp = wb_getgrnam( group )) ) {
+ DEBUG(4,("winbindd_delete_group: Cannot delete a non-existent group\n"));
+ return WINBINDD_ERROR;
+ }
+
+ ret = wb_delete_group(grp);
+
+ free_winbindd_gr( grp );
+
+ return ( ret ? WINBINDD_OK : WINBINDD_ERROR );
+}
+
+
+
diff --git a/source/nsswitch/winbindd_ads.c b/source/nsswitch/winbindd_ads.c
new file mode 100644
index 00000000000..cd8b8e0e246
--- /dev/null
+++ b/source/nsswitch/winbindd_ads.c
@@ -0,0 +1,1018 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind ADS backend functions
+
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
+ Copyright (C) Gerald (Jerry) Carter 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+
+#ifdef HAVE_ADS
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/*
+ return our ads connections structure for a domain. We keep the connection
+ open to make things faster
+*/
+static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS status;
+
+ if (domain->private) {
+ ads = (ADS_STRUCT *)domain->private;
+
+ /* check for a valid structure */
+
+ DEBUG(7, ("Current tickets expire at %d\n, time is now %d\n",
+ (uint32) ads->auth.expire, (uint32) time(NULL)));
+ if ( ads->config.realm && (ads->auth.expire > time(NULL))) {
+ return ads;
+ }
+ else {
+ /* we own this ADS_STRUCT so make sure it goes away */
+ ads->is_mine = True;
+ ads_destroy( &ads );
+ ads_kdestroy("MEMORY:winbind_ccache");
+ domain->private = NULL;
+ }
+ }
+
+ /* we don't want this to affect the users ccache */
+ setenv("KRB5CCNAME", "MEMORY:winbind_ccache", 1);
+
+ ads = ads_init(domain->alt_name, domain->name, NULL);
+ if (!ads) {
+ DEBUG(1,("ads_init for domain %s failed\n", domain->name));
+ return NULL;
+ }
+
+ /* the machine acct password might have change - fetch it every time */
+ SAFE_FREE(ads->auth.password);
+ ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
+
+ SAFE_FREE(ads->auth.realm);
+ ads->auth.realm = strdup(lp_realm());
+
+ status = ads_connect(ads);
+ if (!ADS_ERR_OK(status) || !ads->config.realm) {
+ extern struct winbindd_methods msrpc_methods, cache_methods;
+ DEBUG(1,("ads_connect for domain %s failed: %s\n",
+ domain->name, ads_errstr(status)));
+ ads_destroy(&ads);
+
+ /* if we get ECONNREFUSED then it might be a NT4
+ server, fall back to MSRPC */
+ if (status.error_type == ENUM_ADS_ERROR_SYSTEM &&
+ status.err.rc == ECONNREFUSED) {
+ DEBUG(1,("Trying MSRPC methods\n"));
+ if (domain->methods == &cache_methods) {
+ domain->backend = &msrpc_methods;
+ } else {
+ domain->methods = &msrpc_methods;
+ }
+ }
+ return NULL;
+ }
+
+ /* set the flag that says we don't own the memory even
+ though we do so that ads_destroy() won't destroy the
+ structure we pass back by reference */
+
+ ads->is_mine = False;
+
+ domain->private = (void *)ads;
+ return ads;
+}
+
+
+/* Query display info for a realm. This is the basic user list fn */
+static NTSTATUS query_user_list(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ WINBIND_USERINFO **info)
+{
+ ADS_STRUCT *ads = NULL;
+ const char *attrs[] = {"userPrincipalName",
+ "sAMAccountName",
+ "name", "objectSid", "primaryGroupID",
+ "sAMAccountType", NULL};
+ int i, count;
+ ADS_STATUS rc;
+ void *res = NULL;
+ void *msg = NULL;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+
+ *num_entries = 0;
+
+ DEBUG(3,("ads: query_user_list\n"));
+
+ ads = ads_cached_connection(domain);
+
+ if (!ads) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ goto done;
+ }
+
+ rc = ads_search_retry(ads, &res, "(objectClass=user)", attrs);
+ if (!ADS_ERR_OK(rc) || !res) {
+ DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
+ goto done;
+ }
+
+ count = ads_count_replies(ads, res);
+ if (count == 0) {
+ DEBUG(1,("query_user_list: No users found\n"));
+ goto done;
+ }
+
+ (*info) = talloc_zero(mem_ctx, count * sizeof(**info));
+ if (!*info) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ i = 0;
+
+ for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
+ char *name, *gecos;
+ DOM_SID sid;
+ DOM_SID *sid2;
+ DOM_SID *group_sid;
+ uint32 group;
+ uint32 atype;
+
+ if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) ||
+ ads_atype_map(atype) != SID_NAME_USER) {
+ DEBUG(1,("Not a user account? atype=0x%x\n", atype));
+ continue;
+ }
+
+ name = ads_pull_username(ads, mem_ctx, msg);
+ gecos = ads_pull_string(ads, mem_ctx, msg, "name");
+ if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
+ DEBUG(1,("No sid for %s !?\n", name));
+ continue;
+ }
+ if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
+ DEBUG(1,("No primary group for %s !?\n", name));
+ continue;
+ }
+
+ sid2 = talloc(mem_ctx, sizeof(*sid2));
+ if (!sid2) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ sid_copy(sid2, &sid);
+
+ group_sid = rid_to_talloced_sid(domain, mem_ctx, group);
+
+ (*info)[i].acct_name = name;
+ (*info)[i].full_name = gecos;
+ (*info)[i].user_sid = sid2;
+ (*info)[i].group_sid = group_sid;
+ i++;
+ }
+
+ (*num_entries) = i;
+ status = NT_STATUS_OK;
+
+ DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
+
+done:
+ if (res)
+ ads_msgfree(ads, res);
+
+ return status;
+}
+
+/* list all domain groups */
+static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info)
+{
+ ADS_STRUCT *ads = NULL;
+ const char *attrs[] = {"userPrincipalName", "sAMAccountName",
+ "name", "objectSid",
+ "sAMAccountType", NULL};
+ int i, count;
+ ADS_STATUS rc;
+ void *res = NULL;
+ void *msg = NULL;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ uint32 group_flags;
+
+ *num_entries = 0;
+
+ DEBUG(3,("ads: enum_dom_groups\n"));
+
+ ads = ads_cached_connection(domain);
+
+ if (!ads) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ goto done;
+ }
+
+ rc = ads_search_retry(ads, &res, "(objectCategory=group)", attrs);
+ if (!ADS_ERR_OK(rc) || !res) {
+ DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc)));
+ goto done;
+ }
+
+ count = ads_count_replies(ads, res);
+ if (count == 0) {
+ DEBUG(1,("enum_dom_groups: No groups found\n"));
+ goto done;
+ }
+
+ (*info) = talloc_zero(mem_ctx, count * sizeof(**info));
+ if (!*info) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ i = 0;
+
+ group_flags = ATYPE_GLOBAL_GROUP;
+
+ /* only grab domain local groups for our domain */
+ if ( domain->native_mode && strequal(lp_realm(), domain->alt_name) )
+ group_flags |= ATYPE_LOCAL_GROUP;
+
+ for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
+ char *name, *gecos;
+ DOM_SID sid;
+ uint32 rid;
+ uint32 account_type;
+
+ if (!ads_pull_uint32(ads, msg, "sAMAccountType", &account_type) || !(account_type & group_flags) )
+ continue;
+
+ name = ads_pull_username(ads, mem_ctx, msg);
+ gecos = ads_pull_string(ads, mem_ctx, msg, "name");
+ if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
+ DEBUG(1,("No sid for %s !?\n", name));
+ continue;
+ }
+
+ if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
+ DEBUG(1,("No rid for %s !?\n", name));
+ continue;
+ }
+
+ fstrcpy((*info)[i].acct_name, name);
+ fstrcpy((*info)[i].acct_desc, gecos);
+ (*info)[i].rid = rid;
+ i++;
+ }
+
+ (*num_entries) = i;
+
+ status = NT_STATUS_OK;
+
+ DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
+
+done:
+ if (res)
+ ads_msgfree(ads, res);
+
+ return status;
+}
+
+/* list all domain local groups */
+static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info)
+{
+ /*
+ * This is a stub function only as we returned the domain
+ * local groups in enum_dom_groups() if the domain->native field
+ * was true. This is a simple performance optimization when
+ * using LDAP.
+ *
+ * if we ever need to enumerate domain local groups separately,
+ * then this the optimization in enum_dom_groups() will need
+ * to be split out
+ */
+ *num_entries = 0;
+
+ return NT_STATUS_OK;
+}
+
+/* convert a single name to a sid in a domain */
+static NTSTATUS name_to_sid(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *name,
+ DOM_SID *sid,
+ enum SID_NAME_USE *type)
+{
+ ADS_STRUCT *ads;
+
+ DEBUG(3,("ads: name_to_sid\n"));
+
+ ads = ads_cached_connection(domain);
+
+ if (!ads) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return ads_name_to_sid(ads, name, sid, type);
+}
+
+/* convert a sid to a user or group name */
+static NTSTATUS sid_to_name(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *sid,
+ char **name,
+ enum SID_NAME_USE *type)
+{
+ ADS_STRUCT *ads = NULL;
+ DEBUG(3,("ads: sid_to_name\n"));
+
+ ads = ads_cached_connection(domain);
+
+ if (!ads) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return ads_sid_to_name(ads, mem_ctx, sid, name, type);
+}
+
+
+/* convert a DN to a name, SID and name type
+ this might become a major speed bottleneck if groups have
+ lots of users, in which case we could cache the results
+*/
+static BOOL dn_lookup(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
+ const char *dn,
+ char **name, uint32 *name_type, DOM_SID *sid)
+{
+ void *res = NULL;
+ const char *attrs[] = {"userPrincipalName", "sAMAccountName",
+ "objectSid", "sAMAccountType", NULL};
+ ADS_STATUS rc;
+ uint32 atype;
+ DEBUG(3,("ads: dn_lookup\n"));
+
+ rc = ads_search_retry_dn(ads, &res, dn, attrs);
+
+ if (!ADS_ERR_OK(rc) || !res) {
+ goto failed;
+ }
+
+ (*name) = ads_pull_username(ads, mem_ctx, res);
+
+ if (!ads_pull_uint32(ads, res, "sAMAccountType", &atype)) {
+ goto failed;
+ }
+ (*name_type) = ads_atype_map(atype);
+
+ if (!ads_pull_sid(ads, res, "objectSid", sid)) {
+ goto failed;
+ }
+
+ if (res)
+ ads_msgfree(ads, res);
+
+ return True;
+
+failed:
+ if (res)
+ ads_msgfree(ads, res);
+
+ return False;
+}
+
+/* Lookup user information from a rid */
+static NTSTATUS query_user(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *sid,
+ WINBIND_USERINFO *info)
+{
+ ADS_STRUCT *ads = NULL;
+ const char *attrs[] = {"userPrincipalName",
+ "sAMAccountName",
+ "name",
+ "primaryGroupID", NULL};
+ ADS_STATUS rc;
+ int count;
+ void *msg = NULL;
+ char *ldap_exp;
+ char *sidstr;
+ uint32 group_rid;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ DOM_SID *sid2;
+ fstring sid_string;
+
+ DEBUG(3,("ads: query_user\n"));
+
+ ads = ads_cached_connection(domain);
+
+ if (!ads) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ goto done;
+ }
+
+ sidstr = sid_binstring(sid);
+ asprintf(&ldap_exp, "(objectSid=%s)", sidstr);
+ rc = ads_search_retry(ads, &msg, ldap_exp, attrs);
+ free(ldap_exp);
+ free(sidstr);
+ if (!ADS_ERR_OK(rc) || !msg) {
+ DEBUG(1,("query_user(sid=%s) ads_search: %s\n", sid_to_string(sid_string, sid), ads_errstr(rc)));
+ goto done;
+ }
+
+ count = ads_count_replies(ads, msg);
+ if (count != 1) {
+ DEBUG(1,("query_user(sid=%s): Not found\n", sid_to_string(sid_string, sid)));
+ goto done;
+ }
+
+ info->acct_name = ads_pull_username(ads, mem_ctx, msg);
+ info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
+
+ if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group_rid)) {
+ DEBUG(1,("No primary group for %s !?\n", sid_to_string(sid_string, sid)));
+ goto done;
+ }
+
+ sid2 = talloc(mem_ctx, sizeof(*sid2));
+ if (!sid2) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ sid_copy(sid2, sid);
+
+ info->user_sid = sid2;
+
+ info->group_sid = rid_to_talloced_sid(domain, mem_ctx, group_rid);
+
+ status = NT_STATUS_OK;
+
+ DEBUG(3,("ads query_user gave %s\n", info->acct_name));
+done:
+ if (msg)
+ ads_msgfree(ads, msg);
+
+ return status;
+}
+
+/* Lookup groups a user is a member of - alternate method, for when
+ tokenGroups are not available. */
+static NTSTATUS lookup_usergroups_alt(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *user_dn,
+ DOM_SID *primary_group,
+ uint32 *num_groups, DOM_SID ***user_gids)
+{
+ ADS_STATUS rc;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ int count;
+ void *res = NULL;
+ void *msg = NULL;
+ char *ldap_exp;
+ ADS_STRUCT *ads;
+ const char *group_attrs[] = {"objectSid", NULL};
+ char *escaped_dn;
+
+ DEBUG(3,("ads: lookup_usergroups_alt\n"));
+
+ ads = ads_cached_connection(domain);
+
+ if (!ads) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ goto done;
+ }
+
+ if (!(escaped_dn = escape_ldap_string_alloc(user_dn))) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ /* buggy server, no tokenGroups. Instead lookup what groups this user
+ is a member of by DN search on member*/
+
+ if (!(ldap_exp = talloc_asprintf(mem_ctx, "(&(member=%s)(objectClass=group))", escaped_dn))) {
+ DEBUG(1,("lookup_usergroups(dn=%s) asprintf failed!\n", user_dn));
+ SAFE_FREE(escaped_dn);
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ SAFE_FREE(escaped_dn);
+
+ rc = ads_search_retry(ads, &res, ldap_exp, group_attrs);
+
+ if (!ADS_ERR_OK(rc) || !res) {
+ DEBUG(1,("lookup_usergroups ads_search member=%s: %s\n", user_dn, ads_errstr(rc)));
+ return ads_ntstatus(rc);
+ }
+
+ count = ads_count_replies(ads, res);
+ if (count == 0) {
+ DEBUG(5,("lookup_usergroups: No supp groups found\n"));
+
+ status = ads_ntstatus(rc);
+ goto done;
+ }
+
+ (*user_gids) = talloc_zero(mem_ctx, sizeof(**user_gids) * (count + 1));
+ (*user_gids)[0] = primary_group;
+
+ *num_groups = 1;
+
+ for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
+ DOM_SID group_sid;
+
+ if (!ads_pull_sid(ads, msg, "objectSid", &group_sid)) {
+ DEBUG(1,("No sid for this group ?!?\n"));
+ continue;
+ }
+
+ if (sid_equal(&group_sid, primary_group)) continue;
+
+ (*user_gids)[*num_groups] = talloc(mem_ctx, sizeof(***user_gids));
+ if (!(*user_gids)[*num_groups]) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ sid_copy((*user_gids)[*num_groups], &group_sid);
+
+ (*num_groups)++;
+
+ }
+
+ status = NT_STATUS_OK;
+
+ DEBUG(3,("ads lookup_usergroups (alt) for dn=%s\n", user_dn));
+done:
+ if (res)
+ ads_msgfree(ads, res);
+
+ return status;
+}
+
+/* Lookup groups a user is a member of. */
+static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *sid,
+ uint32 *num_groups, DOM_SID ***user_gids)
+{
+ ADS_STRUCT *ads = NULL;
+ const char *attrs[] = {"tokenGroups", "primaryGroupID", NULL};
+ ADS_STATUS rc;
+ int count;
+ LDAPMessage *msg = NULL;
+ char *user_dn;
+ DOM_SID *sids;
+ int i;
+ DOM_SID *primary_group;
+ uint32 primary_group_rid;
+ fstring sid_string;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(3,("ads: lookup_usergroups\n"));
+ *num_groups = 0;
+
+ ads = ads_cached_connection(domain);
+
+ if (!ads) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ status = NT_STATUS_SERVER_DISABLED;
+ goto done;
+ }
+
+ rc = ads_sid_to_dn(ads, mem_ctx, sid, &user_dn);
+ if (!ADS_ERR_OK(rc)) {
+ status = ads_ntstatus(rc);
+ goto done;
+ }
+
+ rc = ads_search_retry_dn(ads, (void**)&msg, user_dn, attrs);
+ if (!ADS_ERR_OK(rc)) {
+ status = ads_ntstatus(rc);
+ DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: %s\n",
+ sid_to_string(sid_string, sid), ads_errstr(rc)));
+ goto done;
+ }
+
+ if (!msg) {
+ DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: NULL msg\n",
+ sid_to_string(sid_string, sid)));
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group_rid)) {
+ DEBUG(1,("%s: No primary group for sid=%s !?\n",
+ domain->name, sid_to_string(sid_string, sid)));
+ goto done;
+ }
+
+ primary_group = rid_to_talloced_sid(domain, mem_ctx, primary_group_rid);
+
+ count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids);
+
+ if (msg)
+ ads_msgfree(ads, msg);
+
+ /* there must always be at least one group in the token,
+ unless we are talking to a buggy Win2k server */
+ if (count == 0) {
+ return lookup_usergroups_alt(domain, mem_ctx, user_dn,
+ primary_group,
+ num_groups, user_gids);
+ }
+
+ (*user_gids) = talloc_zero(mem_ctx, sizeof(**user_gids) * (count + 1));
+ (*user_gids)[0] = primary_group;
+
+ *num_groups = 1;
+
+ for (i=0;i<count;i++) {
+ if (sid_equal(&sids[i], primary_group)) continue;
+
+ (*user_gids)[*num_groups] = talloc(mem_ctx, sizeof(***user_gids));
+ if (!(*user_gids)[*num_groups]) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ sid_copy((*user_gids)[*num_groups], &sids[i]);
+ (*num_groups)++;
+ }
+
+ status = NT_STATUS_OK;
+ DEBUG(3,("ads lookup_usergroups for sid=%s\n", sid_to_string(sid_string, sid)));
+done:
+ return status;
+}
+
+/*
+ find the members of a group, given a group rid and domain
+ */
+static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *group_sid, uint32 *num_names,
+ DOM_SID ***sid_mem, char ***names,
+ uint32 **name_types)
+{
+ ADS_STATUS rc;
+ int count;
+ void *res=NULL;
+ ADS_STRUCT *ads = NULL;
+ char *ldap_exp;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ char *sidstr;
+ char **members;
+ int i, num_members;
+ fstring sid_string;
+ BOOL more_values;
+ const char **attrs;
+ uint32 first_usn;
+ uint32 current_usn;
+ int num_retries = 0;
+
+ DEBUG(10,("ads: lookup_groupmem %s sid=%s\n", domain->name,
+ sid_string_static(group_sid)));
+
+ *num_names = 0;
+
+ ads = ads_cached_connection(domain);
+
+ if (!ads) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ goto done;
+ }
+
+ sidstr = sid_binstring(group_sid);
+
+ /* search for all members of the group */
+ if (!(ldap_exp = talloc_asprintf(mem_ctx, "(objectSid=%s)",sidstr))) {
+ SAFE_FREE(sidstr);
+ DEBUG(1, ("ads: lookup_groupmem: tallloc_asprintf for ldap_exp failed!\n"));
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ SAFE_FREE(sidstr);
+
+ members = NULL;
+ num_members = 0;
+
+ attrs = talloc(mem_ctx, 3 * sizeof(*attrs));
+ attrs[1] = talloc_strdup(mem_ctx, "usnChanged");
+ attrs[2] = NULL;
+
+ do {
+ if (num_members == 0)
+ attrs[0] = talloc_strdup(mem_ctx, "member");
+
+ DEBUG(10, ("Searching for attrs[0] = %s, attrs[1] = %s\n", attrs[0], attrs[1]));
+
+ rc = ads_search_retry(ads, &res, ldap_exp, attrs);
+
+ if (!ADS_ERR_OK(rc) || !res) {
+ DEBUG(1,("ads: lookup_groupmem ads_search: %s\n",
+ ads_errstr(rc)));
+ status = ads_ntstatus(rc);
+ goto done;
+ }
+
+ count = ads_count_replies(ads, res);
+ if (count == 0)
+ break;
+
+ if (num_members == 0) {
+ if (!ads_pull_uint32(ads, res, "usnChanged", &first_usn)) {
+ DEBUG(1, ("ads: lookup_groupmem could not pull usnChanged!\n"));
+ goto done;
+ }
+ }
+
+ if (!ads_pull_uint32(ads, res, "usnChanged", &current_usn)) {
+ DEBUG(1, ("ads: lookup_groupmem could not pull usnChanged!\n"));
+ goto done;
+ }
+
+ if (first_usn != current_usn) {
+ DEBUG(5, ("ads: lookup_groupmem USN on this record changed"
+ " - restarting search\n"));
+ if (num_retries < 5) {
+ num_retries++;
+ num_members = 0;
+ continue;
+ } else {
+ DEBUG(5, ("ads: lookup_groupmem USN on this record changed"
+ " - restarted search too many times, aborting!\n"));
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ }
+
+ members = ads_pull_strings_range(ads, mem_ctx, res,
+ "member",
+ members,
+ &attrs[0],
+ &num_members,
+ &more_values);
+
+ if ((members == NULL) || (num_members == 0))
+ break;
+
+ } while (more_values);
+
+ /* now we need to turn a list of members into rids, names and name types
+ the problem is that the members are in the form of distinguised names
+ */
+
+ (*sid_mem) = talloc_zero(mem_ctx, sizeof(**sid_mem) * num_members);
+ (*name_types) = talloc_zero(mem_ctx, sizeof(**name_types) * num_members);
+ (*names) = talloc_zero(mem_ctx, sizeof(**names) * num_members);
+
+ for (i=0;i<num_members;i++) {
+ uint32 name_type;
+ char *name;
+ DOM_SID sid;
+
+ if (dn_lookup(ads, mem_ctx, members[i], &name, &name_type, &sid)) {
+ (*names)[*num_names] = name;
+ (*name_types)[*num_names] = name_type;
+ (*sid_mem)[*num_names] = talloc(mem_ctx, sizeof(***sid_mem));
+ if (!(*sid_mem)[*num_names]) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ sid_copy((*sid_mem)[*num_names], &sid);
+ (*num_names)++;
+ }
+ }
+
+ status = NT_STATUS_OK;
+ DEBUG(3,("ads lookup_groupmem for sid=%s\n", sid_to_string(sid_string, group_sid)));
+done:
+
+ if (res)
+ ads_msgfree(ads, res);
+
+ return status;
+}
+
+/* find the sequence number for a domain */
+static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
+{
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS rc;
+
+ DEBUG(3,("ads: fetch sequence_number for %s\n", domain->name));
+
+ *seq = DOM_SEQUENCE_NONE;
+
+ ads = ads_cached_connection(domain);
+
+ if (!ads) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ rc = ads_USN(ads, seq);
+
+ if (!ADS_ERR_OK(rc)) {
+
+ /* its a dead connection ; don't destroy it
+ through since ads_USN() has already done
+ that indirectly */
+
+ domain->private = NULL;
+ }
+ return ads_ntstatus(rc);
+}
+
+/* get a list of trusted domains */
+static NTSTATUS trusted_domains(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_domains,
+ char ***names,
+ char ***alt_names,
+ DOM_SID **dom_sids)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ struct ds_domain_trust *domains = NULL;
+ int count = 0;
+ int i;
+ struct cli_state *cli = NULL;
+ /* i think we only need our forest and downlevel trusted domains */
+ uint32 flags = DS_DOMAIN_IN_FOREST | DS_DOMAIN_DIRECT_OUTBOUND;
+
+ DEBUG(3,("ads: trusted_domains\n"));
+
+ *num_domains = 0;
+ *alt_names = NULL;
+ *names = NULL;
+ *dom_sids = NULL;
+
+ if ( !NT_STATUS_IS_OK(result = cm_fresh_connection(domain, PI_NETLOGON, &cli)) ) {
+ DEBUG(5, ("trusted_domains: Could not open a connection to %s for PIPE_NETLOGON (%s)\n",
+ domain->name, nt_errstr(result)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if ( NT_STATUS_IS_OK(result) )
+ result = cli_ds_enum_domain_trusts( cli, mem_ctx, cli->desthost,
+ flags, &domains, (unsigned int *)&count );
+
+ if ( NT_STATUS_IS_OK(result) && count) {
+
+ /* Allocate memory for trusted domain names and sids */
+
+ if ( !(*names = (char **)talloc(mem_ctx, sizeof(char *) * count)) ) {
+ DEBUG(0, ("trusted_domains: out of memory\n"));
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ if ( !(*alt_names = (char **)talloc(mem_ctx, sizeof(char *) * count)) ) {
+ DEBUG(0, ("trusted_domains: out of memory\n"));
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ if ( !(*dom_sids = (DOM_SID *)talloc(mem_ctx, sizeof(DOM_SID) * count)) ) {
+ DEBUG(0, ("trusted_domains: out of memory\n"));
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ /* Copy across names and sids */
+
+ for (i = 0; i < count; i++) {
+ (*names)[i] = domains[i].netbios_domain;
+ (*alt_names)[i] = domains[i].dns_domain;
+
+ sid_copy(&(*dom_sids)[i], &domains[i].sid);
+ }
+
+ *num_domains = count;
+ }
+
+done:
+
+ /* remove connection; This is a special case to the \NETLOGON pipe */
+
+ if ( cli )
+ cli_shutdown( cli );
+
+ return result;
+}
+
+/* find the domain sid for a domain */
+static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+
+ DEBUG(3,("ads: domain_sid\n"));
+
+ ads = ads_cached_connection(domain);
+
+ if (!ads) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ rc = ads_domain_sid(ads, sid);
+
+ if (!ADS_ERR_OK(rc)) {
+
+ /* its a dead connection; don't destroy it though
+ since that has already been done indirectly
+ by ads_domain_sid() */
+
+ domain->private = NULL;
+ }
+
+ return ads_ntstatus(rc);
+}
+
+
+/* find alternate names list for the domain - for ADS this is the
+ netbios name */
+static NTSTATUS alternate_name(struct winbindd_domain *domain)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ TALLOC_CTX *ctx;
+ const char *workgroup;
+
+ DEBUG(3,("ads: alternate_name\n"));
+
+ ads = ads_cached_connection(domain);
+
+ if (!ads) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!(ctx = talloc_init("alternate_name"))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rc = ads_workgroup_name(ads, ctx, &workgroup);
+
+ if (ADS_ERR_OK(rc)) {
+ fstrcpy(domain->name, workgroup);
+ fstrcpy(domain->alt_name, ads->config.realm);
+ strupper_m(domain->alt_name);
+ strupper_m(domain->name);
+ }
+
+ talloc_destroy(ctx);
+
+ return ads_ntstatus(rc);
+}
+
+/* the ADS backend methods are exposed via this structure */
+struct winbindd_methods ads_methods = {
+ True,
+ query_user_list,
+ enum_dom_groups,
+ enum_local_groups,
+ name_to_sid,
+ sid_to_name,
+ query_user,
+ lookup_usergroups,
+ lookup_groupmem,
+ sequence_number,
+ trusted_domains,
+ domain_sid,
+ alternate_name
+};
+
+#endif
diff --git a/source/nsswitch/winbindd_cache.c b/source/nsswitch/winbindd_cache.c
new file mode 100644
index 00000000000..73918d74f7d
--- /dev/null
+++ b/source/nsswitch/winbindd_cache.c
@@ -0,0 +1,1377 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind cache backend functions
+
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Gerald Carter 2003
+
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+struct winbind_cache {
+ TDB_CONTEXT *tdb;
+};
+
+struct cache_entry {
+ NTSTATUS status;
+ uint32 sequence_number;
+ uint8 *data;
+ uint32 len, ofs;
+};
+
+#define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
+
+static struct winbind_cache *wcache;
+
+/* flush the cache */
+void wcache_flush_cache(void)
+{
+ extern BOOL opt_nocache;
+
+ if (!wcache)
+ return;
+ if (wcache->tdb) {
+ tdb_close(wcache->tdb);
+ wcache->tdb = NULL;
+ }
+ if (opt_nocache)
+ return;
+
+ wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000,
+ TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0600);
+
+ if (!wcache->tdb) {
+ DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
+ }
+ DEBUG(10,("wcache_flush_cache success\n"));
+}
+
+void winbindd_check_cache_size(time_t t)
+{
+ static time_t last_check_time;
+ struct stat st;
+
+ if (last_check_time == (time_t)0)
+ last_check_time = t;
+
+ if (t - last_check_time < 60 && t - last_check_time > 0)
+ return;
+
+ if (wcache == NULL || wcache->tdb == NULL) {
+ DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
+ return;
+ }
+
+ if (fstat(wcache->tdb->fd, &st) == -1) {
+ DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
+ return;
+ }
+
+ if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
+ DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
+ (unsigned long)st.st_size,
+ (unsigned long)WINBINDD_MAX_CACHE_SIZE));
+ wcache_flush_cache();
+ }
+}
+
+/* get the winbind_cache structure */
+static struct winbind_cache *get_cache(struct winbindd_domain *domain)
+{
+ struct winbind_cache *ret = wcache;
+
+ if (!domain->backend) {
+ extern struct winbindd_methods msrpc_methods;
+ switch (lp_security()) {
+#ifdef HAVE_ADS
+ case SEC_ADS: {
+ extern struct winbindd_methods ads_methods;
+ /* always obey the lp_security parameter for our domain */
+ if (domain->primary) {
+ domain->backend = &ads_methods;
+ break;
+ }
+
+ /* only use ADS for native modes at the momment.
+ The problem is the correct detection of mixed
+ mode domains from NT4 BDC's --jerry */
+
+ if ( domain->native_mode ) {
+ DEBUG(5,("get_cache: Setting ADS methods for domain %s\n",
+ domain->name));
+ domain->backend = &ads_methods;
+ break;
+ }
+
+ /* fall through */
+ }
+#endif
+ default:
+ DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n",
+ domain->name));
+ domain->backend = &msrpc_methods;
+ }
+ }
+
+ if (ret)
+ return ret;
+
+ ret = smb_xmalloc(sizeof(*ret));
+ ZERO_STRUCTP(ret);
+
+ wcache = ret;
+ wcache_flush_cache();
+
+ return ret;
+}
+
+/*
+ free a centry structure
+*/
+static void centry_free(struct cache_entry *centry)
+{
+ if (!centry)
+ return;
+ SAFE_FREE(centry->data);
+ free(centry);
+}
+
+/*
+ pull a uint32 from a cache entry
+*/
+static uint32 centry_uint32(struct cache_entry *centry)
+{
+ uint32 ret;
+ if (centry->len - centry->ofs < 4) {
+ DEBUG(0,("centry corruption? needed 4 bytes, have %d\n",
+ centry->len - centry->ofs));
+ smb_panic("centry_uint32");
+ }
+ ret = IVAL(centry->data, centry->ofs);
+ centry->ofs += 4;
+ return ret;
+}
+
+/*
+ pull a uint8 from a cache entry
+*/
+static uint8 centry_uint8(struct cache_entry *centry)
+{
+ uint8 ret;
+ if (centry->len - centry->ofs < 1) {
+ DEBUG(0,("centry corruption? needed 1 bytes, have %d\n",
+ centry->len - centry->ofs));
+ smb_panic("centry_uint32");
+ }
+ ret = CVAL(centry->data, centry->ofs);
+ centry->ofs += 1;
+ return ret;
+}
+
+/* pull a string from a cache entry, using the supplied
+ talloc context
+*/
+static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
+{
+ uint32 len;
+ char *ret;
+
+ len = centry_uint8(centry);
+
+ if (len == 0xFF) {
+ /* a deliberate NULL string */
+ return NULL;
+ }
+
+ if (centry->len - centry->ofs < len) {
+ DEBUG(0,("centry corruption? needed %d bytes, have %d\n",
+ len, centry->len - centry->ofs));
+ smb_panic("centry_string");
+ }
+
+ ret = talloc(mem_ctx, len+1);
+ if (!ret) {
+ smb_panic("centry_string out of memory\n");
+ }
+ memcpy(ret,centry->data + centry->ofs, len);
+ ret[len] = 0;
+ centry->ofs += len;
+ return ret;
+}
+
+/* pull a string from a cache entry, using the supplied
+ talloc context
+*/
+static DOM_SID *centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
+{
+ DOM_SID *sid;
+ char *sid_string;
+
+ sid = talloc(mem_ctx, sizeof(*sid));
+ if (!sid)
+ return NULL;
+
+ sid_string = centry_string(centry, mem_ctx);
+ if (!string_to_sid(sid, sid_string)) {
+ return NULL;
+ }
+ return sid;
+}
+
+/* the server is considered down if it can't give us a sequence number */
+static BOOL wcache_server_down(struct winbindd_domain *domain)
+{
+ BOOL ret;
+
+ if (!wcache->tdb)
+ return False;
+
+ ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
+
+ if (ret)
+ DEBUG(10,("wcache_server_down: server for Domain %s down\n",
+ domain->name ));
+ return ret;
+}
+
+static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
+{
+ TDB_DATA data;
+ fstring key;
+ uint32 time_diff;
+
+ if (!wcache->tdb) {
+ DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ fstr_sprintf( key, "SEQNUM/%s", domain->name );
+
+ data = tdb_fetch_bystring( wcache->tdb, key );
+ if ( !data.dptr || data.dsize!=8 ) {
+ DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ domain->sequence_number = IVAL(data.dptr, 0);
+ domain->last_seq_check = IVAL(data.dptr, 4);
+
+ /* have we expired? */
+
+ time_diff = now - domain->last_seq_check;
+ if ( time_diff > lp_winbind_cache_time() ) {
+ DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
+ domain->name, domain->sequence_number,
+ (uint32)domain->last_seq_check));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
+ domain->name, domain->sequence_number,
+ (uint32)domain->last_seq_check));
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
+{
+ TDB_DATA data, key;
+ fstring key_str;
+ char buf[8];
+
+ if (!wcache->tdb) {
+ DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
+ key.dptr = key_str;
+ key.dsize = strlen(key_str)+1;
+
+ SIVAL(buf, 0, domain->sequence_number);
+ SIVAL(buf, 4, domain->last_seq_check);
+ data.dptr = buf;
+ data.dsize = 8;
+
+ if ( tdb_store( wcache->tdb, key, data, TDB_REPLACE) == -1 ) {
+ DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
+ domain->name, domain->sequence_number,
+ (uint32)domain->last_seq_check));
+
+ return NT_STATUS_OK;
+}
+
+/*
+ refresh the domain sequence number. If force is True
+ then always refresh it, no matter how recently we fetched it
+*/
+
+static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
+{
+ NTSTATUS status;
+ unsigned time_diff;
+ time_t t = time(NULL);
+ unsigned cache_time = lp_winbind_cache_time();
+
+ get_cache( domain );
+
+#if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
+ /* trying to reconnect is expensive, don't do it too often */
+ if (domain->sequence_number == DOM_SEQUENCE_NONE) {
+ cache_time *= 8;
+ }
+#endif
+
+ time_diff = t - domain->last_seq_check;
+
+ /* see if we have to refetch the domain sequence number */
+ if (!force && (time_diff < cache_time)) {
+ DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
+ goto done;
+ }
+
+ /* try to get the sequence number from the tdb cache first */
+ /* this will update the timestamp as well */
+
+ status = fetch_cache_seqnum( domain, t );
+ if ( NT_STATUS_IS_OK(status) )
+ goto done;
+
+ status = domain->backend->sequence_number(domain, &domain->sequence_number);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ domain->sequence_number = DOM_SEQUENCE_NONE;
+ }
+
+ domain->last_status = status;
+ domain->last_seq_check = time(NULL);
+
+ /* save the new sequence number ni the cache */
+ store_cache_seqnum( domain );
+
+done:
+ DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
+ domain->name, domain->sequence_number));
+
+ return;
+}
+
+/*
+ decide if a cache entry has expired
+*/
+static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
+{
+ /* if the server is OK and our cache entry came from when it was down then
+ the entry is invalid */
+ if (domain->sequence_number != DOM_SEQUENCE_NONE &&
+ centry->sequence_number == DOM_SEQUENCE_NONE) {
+ DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
+ keystr, domain->name ));
+ return True;
+ }
+
+ /* if the server is down or the cache entry is not older than the
+ current sequence number then it is OK */
+ if (wcache_server_down(domain) ||
+ centry->sequence_number == domain->sequence_number) {
+ DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
+ keystr, domain->name ));
+ return False;
+ }
+
+ DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
+ keystr, domain->name ));
+
+ /* it's expired */
+ return True;
+}
+
+/*
+ fetch an entry from the cache, with a varargs key. auto-fetch the sequence
+ number and return status
+*/
+static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
+ struct winbindd_domain *domain,
+ const char *format, ...) PRINTF_ATTRIBUTE(3,4);
+static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
+ struct winbindd_domain *domain,
+ const char *format, ...)
+{
+ va_list ap;
+ char *kstr;
+ TDB_DATA data;
+ struct cache_entry *centry;
+ TDB_DATA key;
+
+ refresh_sequence_number(domain, False);
+
+ va_start(ap, format);
+ smb_xvasprintf(&kstr, format, ap);
+ va_end(ap);
+
+ key.dptr = kstr;
+ key.dsize = strlen(kstr);
+ data = tdb_fetch(wcache->tdb, key);
+ if (!data.dptr) {
+ /* a cache miss */
+ free(kstr);
+ return NULL;
+ }
+
+ centry = smb_xmalloc(sizeof(*centry));
+ centry->data = (unsigned char *)data.dptr;
+ centry->len = data.dsize;
+ centry->ofs = 0;
+
+ if (centry->len < 8) {
+ /* huh? corrupt cache? */
+ DEBUG(10,("wcache_fetch: Corrupt cache for key %s domain %s (len < 8) ?\n",
+ kstr, domain->name ));
+ centry_free(centry);
+ free(kstr);
+ return NULL;
+ }
+
+ centry->status = NT_STATUS(centry_uint32(centry));
+ centry->sequence_number = centry_uint32(centry);
+
+ if (centry_expired(domain, kstr, centry)) {
+ extern BOOL opt_dual_daemon;
+
+ DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
+ kstr, domain->name ));
+
+ if (opt_dual_daemon) {
+ extern BOOL background_process;
+ background_process = True;
+ DEBUG(10,("wcache_fetch: background processing expired entry %s for domain %s\n",
+ kstr, domain->name ));
+ } else {
+ centry_free(centry);
+ free(kstr);
+ return NULL;
+ }
+ }
+
+ DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
+ kstr, domain->name ));
+
+ free(kstr);
+ return centry;
+}
+
+/*
+ make sure we have at least len bytes available in a centry
+*/
+static void centry_expand(struct cache_entry *centry, uint32 len)
+{
+ uint8 *p;
+ if (centry->len - centry->ofs >= len)
+ return;
+ centry->len *= 2;
+ p = realloc(centry->data, centry->len);
+ if (!p) {
+ DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
+ smb_panic("out of memory in centry_expand");
+ }
+ centry->data = p;
+}
+
+/*
+ push a uint32 into a centry
+*/
+static void centry_put_uint32(struct cache_entry *centry, uint32 v)
+{
+ centry_expand(centry, 4);
+ SIVAL(centry->data, centry->ofs, v);
+ centry->ofs += 4;
+}
+
+/*
+ push a uint8 into a centry
+*/
+static void centry_put_uint8(struct cache_entry *centry, uint8 v)
+{
+ centry_expand(centry, 1);
+ SCVAL(centry->data, centry->ofs, v);
+ centry->ofs += 1;
+}
+
+/*
+ push a string into a centry
+ */
+static void centry_put_string(struct cache_entry *centry, const char *s)
+{
+ int len;
+
+ if (!s) {
+ /* null strings are marked as len 0xFFFF */
+ centry_put_uint8(centry, 0xFF);
+ return;
+ }
+
+ len = strlen(s);
+ /* can't handle more than 254 char strings. Truncating is probably best */
+ if (len > 254)
+ len = 254;
+ centry_put_uint8(centry, len);
+ centry_expand(centry, len);
+ memcpy(centry->data + centry->ofs, s, len);
+ centry->ofs += len;
+}
+
+static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
+{
+ fstring sid_string;
+ centry_put_string(centry, sid_to_string(sid_string, sid));
+}
+
+/*
+ start a centry for output. When finished, call centry_end()
+*/
+struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
+{
+ struct cache_entry *centry;
+
+ if (!wcache->tdb)
+ return NULL;
+
+ centry = smb_xmalloc(sizeof(*centry));
+
+ centry->len = 8192; /* reasonable default */
+ centry->data = smb_xmalloc(centry->len);
+ centry->ofs = 0;
+ centry->sequence_number = domain->sequence_number;
+ centry_put_uint32(centry, NT_STATUS_V(status));
+ centry_put_uint32(centry, centry->sequence_number);
+ return centry;
+}
+
+/*
+ finish a centry and write it to the tdb
+*/
+static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
+static void centry_end(struct cache_entry *centry, const char *format, ...)
+{
+ va_list ap;
+ char *kstr;
+ TDB_DATA key, data;
+
+ va_start(ap, format);
+ smb_xvasprintf(&kstr, format, ap);
+ va_end(ap);
+
+ key.dptr = kstr;
+ key.dsize = strlen(kstr);
+ data.dptr = (char *)centry->data;
+ data.dsize = centry->ofs;
+
+ tdb_store(wcache->tdb, key, data, TDB_REPLACE);
+ free(kstr);
+}
+
+static void wcache_save_name_to_sid(struct winbindd_domain *domain,
+ NTSTATUS status,
+ const char *name, const DOM_SID *sid,
+ enum SID_NAME_USE type)
+{
+ struct cache_entry *centry;
+ fstring uname;
+ fstring sid_string;
+
+ centry = centry_start(domain, status);
+ if (!centry)
+ return;
+ centry_put_sid(centry, sid);
+ fstrcpy(uname, name);
+ strupper_m(uname);
+ centry_end(centry, "NS/%s", sid_to_string(sid_string, sid));
+ DEBUG(10,("wcache_save_name_to_sid: %s -> %s\n", uname, sid_string));
+ centry_free(centry);
+}
+
+static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
+ const DOM_SID *sid, const char *name, enum SID_NAME_USE type)
+{
+ struct cache_entry *centry;
+ fstring sid_string;
+
+ centry = centry_start(domain, status);
+ if (!centry)
+ return;
+ if (NT_STATUS_IS_OK(status)) {
+ centry_put_uint32(centry, type);
+ centry_put_string(centry, name);
+ }
+ centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
+ DEBUG(10,("wcache_save_sid_to_name: %s -> %s\n", sid_string, name));
+ centry_free(centry);
+}
+
+
+static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
+{
+ struct cache_entry *centry;
+ fstring sid_string;
+
+ centry = centry_start(domain, status);
+ if (!centry)
+ return;
+ centry_put_string(centry, info->acct_name);
+ centry_put_string(centry, info->full_name);
+ centry_put_sid(centry, info->user_sid);
+ centry_put_sid(centry, info->group_sid);
+ centry_end(centry, "U/%s", sid_to_string(sid_string, info->user_sid));
+ DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
+ centry_free(centry);
+}
+
+
+/* Query display info. This is the basic user list fn */
+static NTSTATUS query_user_list(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ WINBIND_USERINFO **info)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ unsigned int i, retry;
+
+ if (!cache->tdb)
+ goto do_query;
+
+ centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
+ if (!centry)
+ goto do_query;
+
+ *num_entries = centry_uint32(centry);
+
+ if (*num_entries == 0)
+ goto do_cached;
+
+ (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
+ if (! (*info))
+ smb_panic("query_user_list out of memory");
+ for (i=0; i<(*num_entries); i++) {
+ (*info)[i].acct_name = centry_string(centry, mem_ctx);
+ (*info)[i].full_name = centry_string(centry, mem_ctx);
+ (*info)[i].user_sid = centry_sid(centry, mem_ctx);
+ (*info)[i].group_sid = centry_sid(centry, mem_ctx);
+ }
+
+do_cached:
+ status = centry->status;
+
+ DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status %s\n",
+ domain->name, get_friendly_nt_error_msg(status) ));
+
+ centry_free(centry);
+ return status;
+
+do_query:
+ *num_entries = 0;
+ *info = NULL;
+
+ /* Return status value returned by seq number check */
+
+ if (!NT_STATUS_IS_OK(domain->last_status))
+ return domain->last_status;
+
+ /* Put the query_user_list() in a retry loop. There appears to be
+ * some bug either with Windows 2000 or Samba's handling of large
+ * rpc replies. This manifests itself as sudden disconnection
+ * at a random point in the enumeration of a large (60k) user list.
+ * The retry loop simply tries the operation again. )-: It's not
+ * pretty but an acceptable workaround until we work out what the
+ * real problem is. */
+
+ retry = 0;
+ do {
+
+ DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
+ domain->name ));
+
+ status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
+ if (!NT_STATUS_IS_OK(status))
+ DEBUG(3, ("query_user_list: returned 0x%08x, retrying\n", NT_STATUS_V(status)));
+ if (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL)) {
+ DEBUG(3, ("query_user_list: flushing connection cache\n"));
+ winbindd_cm_flush();
+ }
+
+ } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
+ (retry++ < 5));
+
+ /* and save it */
+ refresh_sequence_number(domain, False);
+ centry = centry_start(domain, status);
+ if (!centry)
+ goto skip_save;
+ centry_put_uint32(centry, *num_entries);
+ for (i=0; i<(*num_entries); i++) {
+ centry_put_string(centry, (*info)[i].acct_name);
+ centry_put_string(centry, (*info)[i].full_name);
+ centry_put_sid(centry, (*info)[i].user_sid);
+ centry_put_sid(centry, (*info)[i].group_sid);
+ if (domain->backend->consistent) {
+ /* when the backend is consistent we can pre-prime some mappings */
+ wcache_save_name_to_sid(domain, NT_STATUS_OK,
+ (*info)[i].acct_name,
+ (*info)[i].user_sid,
+ SID_NAME_USER);
+ wcache_save_sid_to_name(domain, NT_STATUS_OK,
+ (*info)[i].user_sid,
+ (*info)[i].acct_name,
+ SID_NAME_USER);
+ wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
+ }
+ }
+ centry_end(centry, "UL/%s", domain->name);
+ centry_free(centry);
+
+skip_save:
+ return status;
+}
+
+/* list all domain groups */
+static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ unsigned int i;
+
+ if (!cache->tdb)
+ goto do_query;
+
+ centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
+ if (!centry)
+ goto do_query;
+
+ *num_entries = centry_uint32(centry);
+
+ if (*num_entries == 0)
+ goto do_cached;
+
+ (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
+ if (! (*info))
+ smb_panic("enum_dom_groups out of memory");
+ for (i=0; i<(*num_entries); i++) {
+ fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
+ fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
+ (*info)[i].rid = centry_uint32(centry);
+ }
+
+do_cached:
+ status = centry->status;
+
+ DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status %s\n",
+ domain->name, get_friendly_nt_error_msg(status) ));
+
+ centry_free(centry);
+ return status;
+
+do_query:
+ *num_entries = 0;
+ *info = NULL;
+
+ /* Return status value returned by seq number check */
+
+ if (!NT_STATUS_IS_OK(domain->last_status))
+ return domain->last_status;
+
+ DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
+ domain->name ));
+
+ status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
+
+ /* and save it */
+ refresh_sequence_number(domain, False);
+ centry = centry_start(domain, status);
+ if (!centry)
+ goto skip_save;
+ centry_put_uint32(centry, *num_entries);
+ for (i=0; i<(*num_entries); i++) {
+ centry_put_string(centry, (*info)[i].acct_name);
+ centry_put_string(centry, (*info)[i].acct_desc);
+ centry_put_uint32(centry, (*info)[i].rid);
+ }
+ centry_end(centry, "GL/%s/domain", domain->name);
+ centry_free(centry);
+
+skip_save:
+ return status;
+}
+
+/* list all domain groups */
+static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ unsigned int i;
+
+ if (!cache->tdb)
+ goto do_query;
+
+ centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
+ if (!centry)
+ goto do_query;
+
+ *num_entries = centry_uint32(centry);
+
+ if (*num_entries == 0)
+ goto do_cached;
+
+ (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
+ if (! (*info))
+ smb_panic("enum_dom_groups out of memory");
+ for (i=0; i<(*num_entries); i++) {
+ fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
+ fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
+ (*info)[i].rid = centry_uint32(centry);
+ }
+
+do_cached:
+
+ /* If we are returning cached data and the domain controller
+ is down then we don't know whether the data is up to date
+ or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
+ indicate this. */
+
+ if (wcache_server_down(domain)) {
+ DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
+ status = NT_STATUS_MORE_PROCESSING_REQUIRED;
+ } else
+ status = centry->status;
+
+ DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status %s\n",
+ domain->name, get_friendly_nt_error_msg(status) ));
+
+ centry_free(centry);
+ return status;
+
+do_query:
+ *num_entries = 0;
+ *info = NULL;
+
+ /* Return status value returned by seq number check */
+
+ if (!NT_STATUS_IS_OK(domain->last_status))
+ return domain->last_status;
+
+ DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
+ domain->name ));
+
+ status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
+
+ /* and save it */
+ refresh_sequence_number(domain, False);
+ centry = centry_start(domain, status);
+ if (!centry)
+ goto skip_save;
+ centry_put_uint32(centry, *num_entries);
+ for (i=0; i<(*num_entries); i++) {
+ centry_put_string(centry, (*info)[i].acct_name);
+ centry_put_string(centry, (*info)[i].acct_desc);
+ centry_put_uint32(centry, (*info)[i].rid);
+ }
+ centry_end(centry, "GL/%s/local", domain->name);
+ centry_free(centry);
+
+skip_save:
+ return status;
+}
+
+/* convert a single name to a sid in a domain */
+static NTSTATUS name_to_sid(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *name,
+ DOM_SID *sid,
+ enum SID_NAME_USE *type)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ fstring uname;
+ DOM_SID *sid2;
+
+ if (!cache->tdb)
+ goto do_query;
+
+ fstrcpy(uname, name);
+ strupper_m(uname);
+ centry = wcache_fetch(cache, domain, "NS/%s/%s", domain->name, uname);
+ if (!centry)
+ goto do_query;
+ *type = (enum SID_NAME_USE)centry_uint32(centry);
+ sid2 = centry_sid(centry, mem_ctx);
+ if (!sid2) {
+ ZERO_STRUCTP(sid);
+ } else {
+ sid_copy(sid, sid2);
+ }
+
+ status = centry->status;
+
+ DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status %s\n",
+ domain->name, get_friendly_nt_error_msg(status) ));
+
+ centry_free(centry);
+ return status;
+
+do_query:
+ ZERO_STRUCTP(sid);
+
+ /* If the seq number check indicated that there is a problem
+ * with this DC, then return that status... except for
+ * access_denied. This is special because the dc may be in
+ * "restrict anonymous = 1" mode, in which case it will deny
+ * most unauthenticated operations, but *will* allow the LSA
+ * name-to-sid that we try as a fallback. */
+
+ if (!(NT_STATUS_IS_OK(domain->last_status)
+ || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
+ return domain->last_status;
+
+ DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
+ domain->name ));
+
+ status = domain->backend->name_to_sid(domain, mem_ctx, name, sid, type);
+
+ /* and save it */
+ wcache_save_name_to_sid(domain, status, name, sid, *type);
+
+ /* We can't save the sid to name mapping as we don't know the
+ correct case of the name without looking it up */
+
+ return status;
+}
+
+/* convert a sid to a user or group name. The sid is guaranteed to be in the domain
+ given */
+static NTSTATUS sid_to_name(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *sid,
+ char **name,
+ enum SID_NAME_USE *type)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ fstring sid_string;
+
+ if (!cache->tdb)
+ goto do_query;
+
+ centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
+ if (!centry)
+ goto do_query;
+ if (NT_STATUS_IS_OK(centry->status)) {
+ *type = (enum SID_NAME_USE)centry_uint32(centry);
+ *name = centry_string(centry, mem_ctx);
+ }
+ status = centry->status;
+
+ DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status %s\n",
+ domain->name, get_friendly_nt_error_msg(status) ));
+
+ centry_free(centry);
+ return status;
+
+do_query:
+ *name = NULL;
+
+ /* If the seq number check indicated that there is a problem
+ * with this DC, then return that status... except for
+ * access_denied. This is special because the dc may be in
+ * "restrict anonymous = 1" mode, in which case it will deny
+ * most unauthenticated operations, but *will* allow the LSA
+ * sid-to-name that we try as a fallback. */
+
+ if (!(NT_STATUS_IS_OK(domain->last_status)
+ || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
+ return domain->last_status;
+
+ DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
+ domain->name ));
+
+ status = domain->backend->sid_to_name(domain, mem_ctx, sid, name, type);
+
+ /* and save it */
+ refresh_sequence_number(domain, False);
+ wcache_save_sid_to_name(domain, status, sid, *name, *type);
+ wcache_save_name_to_sid(domain, status, *name, sid, *type);
+
+ return status;
+}
+
+
+/* Lookup user information from a rid */
+static NTSTATUS query_user(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *user_sid,
+ WINBIND_USERINFO *info)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+
+ if (!cache->tdb)
+ goto do_query;
+
+ centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
+
+ /* If we have an access denied cache entry and a cached info3 in the
+ samlogon cache then do a query. This will force the rpc back end
+ to return the info3 data. */
+
+ if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
+ netsamlogon_cache_have(user_sid)) {
+ DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
+ domain->last_status = NT_STATUS_OK;
+ centry_free(centry);
+ goto do_query;
+ }
+
+ if (!centry)
+ goto do_query;
+
+ info->acct_name = centry_string(centry, mem_ctx);
+ info->full_name = centry_string(centry, mem_ctx);
+ info->user_sid = centry_sid(centry, mem_ctx);
+ info->group_sid = centry_sid(centry, mem_ctx);
+ status = centry->status;
+
+ DEBUG(10,("query_user: [Cached] - cached info for domain %s status %s\n",
+ domain->name, get_friendly_nt_error_msg(status) ));
+
+ centry_free(centry);
+ return status;
+
+do_query:
+ ZERO_STRUCTP(info);
+
+ /* Return status value returned by seq number check */
+
+ if (!NT_STATUS_IS_OK(domain->last_status))
+ return domain->last_status;
+
+ DEBUG(10,("sid_to_name: [Cached] - doing backend query for info for domain %s\n",
+ domain->name ));
+
+ status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
+
+ /* and save it */
+ refresh_sequence_number(domain, False);
+ wcache_save_user(domain, status, info);
+
+ return status;
+}
+
+
+/* Lookup groups a user is a member of. */
+static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *user_sid,
+ uint32 *num_groups, DOM_SID ***user_gids)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ unsigned int i;
+ fstring sid_string;
+
+ if (!cache->tdb)
+ goto do_query;
+
+ centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
+
+ /* If we have an access denied cache entry and a cached info3 in the
+ samlogon cache then do a query. This will force the rpc back end
+ to return the info3 data. */
+
+ if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
+ netsamlogon_cache_have(user_sid)) {
+ DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
+ domain->last_status = NT_STATUS_OK;
+ centry_free(centry);
+ goto do_query;
+ }
+
+ if (!centry)
+ goto do_query;
+
+ *num_groups = centry_uint32(centry);
+
+ if (*num_groups == 0)
+ goto do_cached;
+
+ (*user_gids) = talloc(mem_ctx, sizeof(**user_gids) * (*num_groups));
+ if (! (*user_gids))
+ smb_panic("lookup_usergroups out of memory");
+ for (i=0; i<(*num_groups); i++) {
+ (*user_gids)[i] = centry_sid(centry, mem_ctx);
+ }
+
+do_cached:
+ status = centry->status;
+
+ DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status %s\n",
+ domain->name, get_friendly_nt_error_msg(status) ));
+
+ centry_free(centry);
+ return status;
+
+do_query:
+ (*num_groups) = 0;
+ (*user_gids) = NULL;
+
+ /* Return status value returned by seq number check */
+
+ if (!NT_STATUS_IS_OK(domain->last_status))
+ return domain->last_status;
+
+ DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
+ domain->name ));
+
+ status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
+
+ /* and save it */
+ refresh_sequence_number(domain, False);
+ centry = centry_start(domain, status);
+ if (!centry)
+ goto skip_save;
+ centry_put_uint32(centry, *num_groups);
+ for (i=0; i<(*num_groups); i++) {
+ centry_put_sid(centry, (*user_gids)[i]);
+ }
+ centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
+ centry_free(centry);
+
+skip_save:
+ return status;
+}
+
+
+static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *group_sid, uint32 *num_names,
+ DOM_SID ***sid_mem, char ***names,
+ uint32 **name_types)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ unsigned int i;
+ fstring sid_string;
+
+ if (!cache->tdb)
+ goto do_query;
+
+ centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
+ if (!centry)
+ goto do_query;
+
+ *num_names = centry_uint32(centry);
+
+ if (*num_names == 0)
+ goto do_cached;
+
+ (*sid_mem) = talloc(mem_ctx, sizeof(**sid_mem) * (*num_names));
+ (*names) = talloc(mem_ctx, sizeof(**names) * (*num_names));
+ (*name_types) = talloc(mem_ctx, sizeof(**name_types) * (*num_names));
+
+ if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
+ smb_panic("lookup_groupmem out of memory");
+ }
+
+ for (i=0; i<(*num_names); i++) {
+ (*sid_mem)[i] = centry_sid(centry, mem_ctx);
+ (*names)[i] = centry_string(centry, mem_ctx);
+ (*name_types)[i] = centry_uint32(centry);
+ }
+
+do_cached:
+ status = centry->status;
+
+ DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status %s\n",
+ domain->name, get_friendly_nt_error_msg(status) ));
+
+ centry_free(centry);
+ return status;
+
+do_query:
+ (*num_names) = 0;
+ (*sid_mem) = NULL;
+ (*names) = NULL;
+ (*name_types) = NULL;
+
+ /* Return status value returned by seq number check */
+
+ if (!NT_STATUS_IS_OK(domain->last_status))
+ return domain->last_status;
+
+ DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
+ domain->name ));
+
+ status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
+ sid_mem, names, name_types);
+
+ /* and save it */
+ refresh_sequence_number(domain, False);
+ centry = centry_start(domain, status);
+ if (!centry)
+ goto skip_save;
+ centry_put_uint32(centry, *num_names);
+ for (i=0; i<(*num_names); i++) {
+ centry_put_sid(centry, (*sid_mem)[i]);
+ centry_put_string(centry, (*names)[i]);
+ centry_put_uint32(centry, (*name_types)[i]);
+ }
+ centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
+ centry_free(centry);
+
+skip_save:
+ return status;
+}
+
+/* find the sequence number for a domain */
+static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
+{
+ refresh_sequence_number(domain, False);
+
+ *seq = domain->sequence_number;
+
+ return NT_STATUS_OK;
+}
+
+/* enumerate trusted domains */
+static NTSTATUS trusted_domains(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_domains,
+ char ***names,
+ char ***alt_names,
+ DOM_SID **dom_sids)
+{
+ get_cache(domain);
+
+ DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
+ domain->name ));
+
+ /* we don't cache this call */
+ return domain->backend->trusted_domains(domain, mem_ctx, num_domains,
+ names, alt_names, dom_sids);
+}
+
+/* find the domain sid */
+static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
+{
+ get_cache(domain);
+
+ DEBUG(10,("domain_sid: [Cached] - doing backend query for info for domain %s\n",
+ domain->name ));
+
+ /* we don't cache this call */
+ return domain->backend->domain_sid(domain, sid);
+}
+
+/* find the alternate names for the domain, if any */
+static NTSTATUS alternate_name(struct winbindd_domain *domain)
+{
+ get_cache(domain);
+
+ DEBUG(10,("alternate_name: [Cached] - doing backend query for info for domain %s\n",
+ domain->name ));
+
+ /* we don't cache this call */
+ return domain->backend->alternate_name(domain);
+}
+
+/* Invalidate cached user and group lists coherently */
+
+static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
+ void *state)
+{
+ if (strncmp(kbuf.dptr, "UL/", 3) == 0 ||
+ strncmp(kbuf.dptr, "GL/", 3) == 0)
+ tdb_delete(the_tdb, kbuf);
+
+ return 0;
+}
+
+/* Invalidate the getpwnam and getgroups entries for a winbindd domain */
+
+void wcache_invalidate_samlogon(struct winbindd_domain *domain,
+ NET_USER_INFO_3 *info3)
+{
+ struct winbind_cache *cache;
+
+ if (!domain)
+ return;
+
+ cache = get_cache(domain);
+ netsamlogon_clear_cached_user(cache->tdb, info3);
+}
+
+void wcache_invalidate_cache(void)
+{
+ struct winbindd_domain *domain;
+
+ for (domain = domain_list(); domain; domain = domain->next) {
+ struct winbind_cache *cache = get_cache(domain);
+
+ DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
+ "entries for %s\n", domain->name));
+ if (cache)
+ tdb_traverse(cache->tdb, traverse_fn, NULL);
+ }
+}
+
+/* the ADS backend methods are exposed via this structure */
+struct winbindd_methods cache_methods = {
+ True,
+ query_user_list,
+ enum_dom_groups,
+ enum_local_groups,
+ name_to_sid,
+ sid_to_name,
+ query_user,
+ lookup_usergroups,
+ lookup_groupmem,
+ sequence_number,
+ trusted_domains,
+ domain_sid,
+ alternate_name
+};
diff --git a/source/nsswitch/winbindd_cm.c b/source/nsswitch/winbindd_cm.c
new file mode 100644
index 00000000000..29b856ec455
--- /dev/null
+++ b/source/nsswitch/winbindd_cm.c
@@ -0,0 +1,773 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon connection manager
+
+ Copyright (C) Tim Potter 2001
+ Copyright (C) Andrew Bartlett 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ We need to manage connections to domain controllers without having to
+ mess up the main winbindd code with other issues. The aim of the
+ connection manager is to:
+
+ - make connections to domain controllers and cache them
+ - re-establish connections when networks or servers go down
+ - centralise the policy on connection timeouts, domain controller
+ selection etc
+ - manage re-entrancy for when winbindd becomes able to handle
+ multiple outstanding rpc requests
+
+ Why not have connection management as part of the rpc layer like tng?
+ Good question. This code may morph into libsmb/rpc_cache.c or something
+ like that but at the moment it's simply staying as part of winbind. I
+ think the TNG architecture of forcing every user of the rpc layer to use
+ the connection caching system is a bad idea. It should be an optional
+ method of using the routines.
+
+ The TNG design is quite good but I disagree with some aspects of the
+ implementation. -tpot
+
+ */
+
+/*
+ TODO:
+
+ - I'm pretty annoyed by all the make_nmb_name() stuff. It should be
+ moved down into another function.
+
+ - Take care when destroying cli_structs as they can be shared between
+ various sam handles.
+
+ */
+
+#include "includes.h"
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* Global list of connections. Initially a DLIST but can become a hash
+ table or whatever later. */
+
+struct winbindd_cm_conn {
+ struct winbindd_cm_conn *prev, *next;
+ fstring domain;
+ fstring controller;
+ fstring pipe_name;
+ size_t mutex_ref_count;
+ struct cli_state *cli;
+ POLICY_HND pol;
+};
+
+static struct winbindd_cm_conn *cm_conns = NULL;
+
+
+/* Choose between anonymous or authenticated connections. We need to use
+ an authenticated connection if DCs have the RestrictAnonymous registry
+ entry set > 0, or the "Additional restrictions for anonymous
+ connections" set in the win2k Local Security Policy.
+
+ Caller to free() result in domain, username, password
+*/
+
+static void cm_get_ipc_userpass(char **username, char **domain, char **password)
+{
+ *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
+ *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
+ *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
+
+ if (*username && **username) {
+
+ if (!*domain || !**domain)
+ *domain = smb_xstrdup(lp_workgroup());
+
+ if (!*password || !**password)
+ *password = smb_xstrdup("");
+
+ DEBUG(3, ("IPC$ connections done by user %s\\%s\n",
+ *domain, *username));
+
+ } else {
+ DEBUG(3, ("IPC$ connections done anonymously\n"));
+ *username = smb_xstrdup("");
+ *domain = smb_xstrdup("");
+ *password = smb_xstrdup("");
+ }
+}
+
+/* Open a connction to the remote server, cache failures for 30 seconds */
+
+static NTSTATUS cm_open_connection(const struct winbindd_domain *domain, const int pipe_index,
+ struct winbindd_cm_conn *new_conn)
+{
+ NTSTATUS result;
+ char *machine_password;
+ char *machine_krb5_principal, *ipc_username, *ipc_domain, *ipc_password;
+ struct in_addr dc_ip;
+ int i;
+ BOOL retry = True;
+
+ ZERO_STRUCT(dc_ip);
+
+ fstrcpy(new_conn->domain, domain->name);
+
+ /* connection failure cache has been moved inside of get_dc_name
+ so we can deal with half dead DC's --jerry */
+
+ if (!get_dc_name(domain->name, domain->alt_name[0] ? domain->alt_name : NULL,
+ new_conn->controller, &dc_ip)) {
+ result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ add_failed_connection_entry(domain->name, "", result);
+ return result;
+ }
+
+ /* Initialise SMB connection */
+ fstrcpy(new_conn->pipe_name, get_pipe_name_from_index(pipe_index));
+
+/* grab stored passwords */
+ machine_password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
+
+ if (asprintf(&machine_krb5_principal, "%s$@%s", global_myname(), lp_realm()) == -1) {
+ SAFE_FREE(machine_password);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
+
+ for (i = 0; retry && (i < 3); i++) {
+ BOOL got_mutex;
+ if (!(got_mutex = secrets_named_mutex(new_conn->controller, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
+ DEBUG(0,("cm_open_connection: mutex grab failed for %s\n", new_conn->controller));
+ result = NT_STATUS_POSSIBLE_DEADLOCK;
+ continue;
+ }
+
+ new_conn->cli = NULL;
+ result = cli_start_connection(&new_conn->cli, global_myname(),
+ new_conn->controller,
+ &dc_ip, 0, Undefined,
+ CLI_FULL_CONNECTION_USE_KERBEROS,
+ &retry);
+
+ if (NT_STATUS_IS_OK(result)) {
+
+ /* reset the error code */
+ result = NT_STATUS_UNSUCCESSFUL;
+
+ /* Krb5 session */
+
+ if ((lp_security() == SEC_ADS)
+ && (new_conn->cli->protocol >= PROTOCOL_NT1 && new_conn->cli->capabilities & CAP_EXTENDED_SECURITY)) {
+ ADS_STATUS ads_status;
+ new_conn->cli->use_kerberos = True;
+ DEBUG(5, ("connecting to %s from %s with kerberos principal [%s]\n",
+ new_conn->controller, global_myname(), machine_krb5_principal));
+
+ ads_status = cli_session_setup_spnego(new_conn->cli, machine_krb5_principal,
+ machine_password,
+ lp_workgroup());
+ if (!ADS_ERR_OK(ads_status)) {
+ DEBUG(4,("failed kerberos session setup with %s\n", ads_errstr(ads_status)));
+ result = ads_ntstatus(ads_status);
+ } else {
+ result = NT_STATUS_OK;
+ }
+ }
+ new_conn->cli->use_kerberos = False;
+
+ /* only do this is we have a username/password for thr IPC$ connection */
+
+ if ( !NT_STATUS_IS_OK(result)
+ && new_conn->cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE
+ && strlen(ipc_username) )
+ {
+ DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n",
+ new_conn->controller, global_myname(), ipc_domain, ipc_username));
+
+ result = NT_STATUS_OK;
+
+ if (!cli_session_setup(new_conn->cli, ipc_username,
+ ipc_password, strlen(ipc_password)+1,
+ ipc_password, strlen(ipc_password)+1,
+ ipc_domain)) {
+ result = cli_nt_error(new_conn->cli);
+ DEBUG(4,("failed authenticated session setup with %s\n", nt_errstr(result)));
+ if (NT_STATUS_IS_OK(result))
+ result = NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ /* anonymous is all that is left if we get to here */
+
+ if (!NT_STATUS_IS_OK(result)) {
+
+ DEBUG(5, ("anonymous connection attempt to %s from %s\n",
+ new_conn->controller, global_myname()));
+
+ result = NT_STATUS_OK;
+
+ if (!cli_session_setup(new_conn->cli, "", NULL, 0, NULL, 0, ""))
+ {
+ result = cli_nt_error(new_conn->cli);
+ DEBUG(4,("failed anonymous session setup with %s\n", nt_errstr(result)));
+ if (NT_STATUS_IS_OK(result))
+ result = NT_STATUS_UNSUCCESSFUL;
+ }
+
+ }
+
+ if (NT_STATUS_IS_OK(result) && !cli_send_tconX(new_conn->cli, "IPC$", "IPC",
+ "", 0)) {
+ result = cli_nt_error(new_conn->cli);
+ DEBUG(1,("failed tcon_X with %s\n", nt_errstr(result)));
+ cli_shutdown(new_conn->cli);
+ if (NT_STATUS_IS_OK(result)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+ }
+
+ if (NT_STATUS_IS_OK(result)) {
+ struct ntuser_creds creds;
+ init_creds(&creds, ipc_username, ipc_domain, ipc_password);
+ cli_init_creds(new_conn->cli, &creds);
+ }
+
+ if (got_mutex)
+ secrets_named_mutex_release(new_conn->controller);
+
+ if (NT_STATUS_IS_OK(result))
+ break;
+ }
+
+ SAFE_FREE(ipc_username);
+ SAFE_FREE(ipc_domain);
+ SAFE_FREE(ipc_password);
+ SAFE_FREE(machine_password);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ add_failed_connection_entry(domain->name, new_conn->controller, result);
+ return result;
+ }
+
+ /* set the domain if empty; needed for schannel connections */
+ if ( !*new_conn->cli->domain )
+ fstrcpy( new_conn->cli->domain, domain->name );
+
+
+ if ( !cli_nt_session_open (new_conn->cli, pipe_index) ) {
+ result = NT_STATUS_PIPE_NOT_AVAILABLE;
+ /*
+ * only cache a failure if we are not trying to open the
+ * **win2k** specific lsarpc UUID. This could be an NT PDC
+ * and therefore a failure is normal. This should probably
+ * be abstracted to a check for 2k specific pipes and wondering
+ * if the PDC is an NT4 box. but since there is only one 2k
+ * specific UUID right now, i'm not going to bother. --jerry
+ */
+ if ( !is_win2k_pipe(pipe_index) )
+ add_failed_connection_entry(domain->name, new_conn->controller, result);
+ cli_shutdown(new_conn->cli);
+ return result;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/************************************************************************
+ Wrapper around statuc cm_open_connection to retreive a freshly
+ setup cli_state struct
+************************************************************************/
+
+NTSTATUS cm_fresh_connection(struct winbindd_domain *domain, const int pipe_index,
+ struct cli_state **cli)
+{
+ NTSTATUS result;
+ struct winbindd_cm_conn conn;
+
+ result = cm_open_connection( domain, pipe_index, &conn );
+
+ if ( NT_STATUS_IS_OK(result) )
+ *cli = conn.cli;
+
+ return result;
+}
+
+/* Return true if a connection is still alive */
+
+static BOOL connection_ok(struct winbindd_cm_conn *conn)
+{
+ if (!conn) {
+ smb_panic("Invalid parameter passed to connection_ok(): conn was NULL!\n");
+ return False;
+ }
+
+ if (!conn->cli) {
+ DEBUG(3, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n",
+ conn->controller, conn->domain, conn->pipe_name));
+ return False;
+ }
+
+ if (!conn->cli->initialised) {
+ DEBUG(3, ("Connection to %s for domain %s (pipe %s) was never initialised!\n",
+ conn->controller, conn->domain, conn->pipe_name));
+ return False;
+ }
+
+ if (conn->cli->fd == -1) {
+ DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n",
+ conn->controller, conn->domain, conn->pipe_name));
+ return False;
+ }
+
+ return True;
+}
+
+/* Search the cache for a connection. If there is a broken one,
+ shut it down properly and return NULL. */
+
+static void find_cm_connection(struct winbindd_domain *domain, const char *pipe_name,
+ struct winbindd_cm_conn **conn_out)
+{
+ struct winbindd_cm_conn *conn;
+
+ for (conn = cm_conns; conn; ) {
+ if (strequal(conn->domain, domain->name) &&
+ strequal(conn->pipe_name, pipe_name)) {
+ if (!connection_ok(conn)) {
+ /* Dead connection - remove it. */
+ struct winbindd_cm_conn *conn_temp = conn->next;
+ if (conn->cli)
+ cli_shutdown(conn->cli);
+ DLIST_REMOVE(cm_conns, conn);
+ SAFE_FREE(conn);
+ conn = conn_temp; /* Keep the loop moving */
+ continue;
+ } else {
+ break;
+ }
+ }
+ conn = conn->next;
+ }
+
+ *conn_out = conn;
+}
+
+/* Initialize a new connection up to the RPC BIND. */
+
+static NTSTATUS new_cm_connection(struct winbindd_domain *domain, const char *pipe_name,
+ struct winbindd_cm_conn **conn_out)
+{
+ struct winbindd_cm_conn *conn;
+ NTSTATUS result;
+
+ if (!(conn = malloc(sizeof(*conn))))
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(conn);
+
+ if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, get_pipe_index(pipe_name), conn))) {
+ DEBUG(3, ("Could not open a connection to %s for %s (%s)\n",
+ domain->name, pipe_name, nt_errstr(result)));
+ SAFE_FREE(conn);
+ return result;
+ }
+ DLIST_ADD(cm_conns, conn);
+
+ *conn_out = conn;
+ return NT_STATUS_OK;
+}
+
+/* Get a connection to the remote DC and open the pipe. If there is already a connection, use that */
+
+static NTSTATUS get_connection_from_cache(struct winbindd_domain *domain, const char *pipe_name,
+ struct winbindd_cm_conn **conn_out)
+{
+ find_cm_connection(domain, pipe_name, conn_out);
+
+ if (*conn_out != NULL)
+ return NT_STATUS_OK;
+
+ return new_cm_connection(domain, pipe_name, conn_out);
+}
+
+/**********************************************************************************
+ We can 'sense' certain things about the DC by it's replies to certain questions.
+
+ This tells us if this particular remote server is Active Directory, and if it is
+ native mode.
+**********************************************************************************/
+
+void set_dc_type_and_flags( struct winbindd_domain *domain )
+{
+ NTSTATUS result;
+ struct winbindd_cm_conn conn;
+ DS_DOMINFO_CTR ctr;
+ TALLOC_CTX *mem_ctx = NULL;
+
+ ZERO_STRUCT( conn );
+ ZERO_STRUCT( ctr );
+
+ domain->native_mode = False;
+ domain->active_directory = False;
+
+ if ( !NT_STATUS_IS_OK(result = cm_open_connection(domain, PI_LSARPC_DS, &conn)) ) {
+ DEBUG(5, ("set_dc_type_and_flags: Could not open a connection to %s for PIPE_LSARPC (%s)\n",
+ domain->name, nt_errstr(result)));
+ return;
+ }
+
+ if ( conn.cli ) {
+ if ( !NT_STATUS_IS_OK(cli_ds_getprimarydominfo( conn.cli,
+ conn.cli->mem_ctx, DsRolePrimaryDomainInfoBasic, &ctr)) ) {
+ goto done;
+ }
+ }
+
+ if ( (ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING)
+ && !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) )
+ domain->native_mode = True;
+
+ /* Cheat - shut down the DS pipe, and open LSA */
+
+ cli_nt_session_close(conn.cli);
+
+ if ( cli_nt_session_open (conn.cli, PI_LSARPC) ) {
+ char *domain_name = NULL;
+ char *dns_name = NULL;
+ DOM_SID *dom_sid = NULL;
+
+ mem_ctx = talloc_init("set_dc_type_and_flags on domain %s\n", domain->name);
+ if (!mem_ctx) {
+ DEBUG(1, ("set_dc_type_and_flags: talloc_init() failed\n"));
+ return;
+ }
+
+ result = cli_lsa_open_policy2(conn.cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &conn.pol);
+
+ if (NT_STATUS_IS_OK(result)) {
+ /* This particular query is exactly what Win2k clients use
+ to determine that the DC is active directory */
+ result = cli_lsa_query_info_policy2(conn.cli, mem_ctx,
+ &conn.pol,
+ 12, &domain_name,
+ &dns_name, NULL,
+ NULL, &dom_sid);
+ }
+
+ if (NT_STATUS_IS_OK(result)) {
+ if (domain_name)
+ fstrcpy(domain->name, domain_name);
+
+ if (dns_name)
+ fstrcpy(domain->alt_name, dns_name);
+
+ if (dom_sid)
+ sid_copy(&domain->sid, dom_sid);
+
+ domain->active_directory = True;
+ } else {
+
+ result = cli_lsa_open_policy(conn.cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &conn.pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_query_info_policy(conn.cli, mem_ctx,
+ &conn.pol, 5, &domain_name,
+ &dom_sid);
+
+ if (NT_STATUS_IS_OK(result)) {
+ if (domain_name)
+ fstrcpy(domain->name, domain_name);
+
+ if (dom_sid)
+ sid_copy(&domain->sid, dom_sid);
+ }
+ }
+ }
+
+done:
+
+ /* close the connection; no other calls use this pipe and it is called only
+ on reestablishing the domain list --jerry */
+
+ if ( conn.cli )
+ cli_shutdown( conn.cli );
+
+ talloc_destroy(mem_ctx);
+
+ return;
+}
+
+
+
+/* Return a LSA policy handle on a domain */
+
+NTSTATUS cm_get_lsa_handle(struct winbindd_domain *domain, CLI_POLICY_HND **return_hnd)
+{
+ struct winbindd_cm_conn *conn;
+ uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+ NTSTATUS result;
+ static CLI_POLICY_HND hnd;
+
+ /* Look for existing connections */
+
+ if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
+ return result;
+
+ /* This *shitty* code needs scrapping ! JRA */
+
+ if (policy_handle_is_valid(&conn->pol)) {
+ hnd.pol = conn->pol;
+ hnd.cli = conn->cli;
+ *return_hnd = &hnd;
+
+ return NT_STATUS_OK;
+ }
+
+ result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
+ des_access, &conn->pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ /* Hit the cache code again. This cleans out the old connection and gets a new one */
+ if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
+ if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
+ return result;
+
+ result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
+ des_access, &conn->pol);
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ cli_shutdown(conn->cli);
+ DLIST_REMOVE(cm_conns, conn);
+ SAFE_FREE(conn);
+ return result;
+ }
+ }
+
+ hnd.pol = conn->pol;
+ hnd.cli = conn->cli;
+
+ *return_hnd = &hnd;
+
+ return NT_STATUS_OK;
+}
+
+/* Return a SAM policy handle on a domain */
+
+NTSTATUS cm_get_sam_handle(struct winbindd_domain *domain, CLI_POLICY_HND **return_hnd)
+{
+ struct winbindd_cm_conn *conn;
+ uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+ NTSTATUS result;
+ static CLI_POLICY_HND hnd;
+
+ /* Look for existing connections */
+
+ if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
+ return result;
+
+ /* This *shitty* code needs scrapping ! JRA */
+
+ if (policy_handle_is_valid(&conn->pol)) {
+ hnd.pol = conn->pol;
+ hnd.cli = conn->cli;
+
+ *return_hnd = &hnd;
+
+ return NT_STATUS_OK;
+ }
+
+ result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
+ des_access, &conn->pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ /* Hit the cache code again. This cleans out the old connection and gets a new one */
+ if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
+
+ if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
+ return result;
+
+ result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
+ des_access, &conn->pol);
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+
+ cli_shutdown(conn->cli);
+ DLIST_REMOVE(cm_conns, conn);
+ SAFE_FREE(conn);
+
+ return result;
+ }
+ }
+
+ hnd.pol = conn->pol;
+ hnd.cli = conn->cli;
+
+ *return_hnd = &hnd;
+
+ return NT_STATUS_OK;
+}
+
+/* Get a handle on a netlogon pipe. This is a bit of a hack to re-use the
+ netlogon pipe as no handle is returned. */
+
+NTSTATUS cm_get_netlogon_cli(struct winbindd_domain *domain,
+ const unsigned char *trust_passwd,
+ uint32 sec_channel_type,
+ BOOL fresh,
+ struct cli_state **cli)
+{
+ NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ struct winbindd_cm_conn *conn;
+ fstring lock_name;
+ BOOL got_mutex;
+
+ if (!cli)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ /* Open an initial conection - keep the mutex. */
+
+ find_cm_connection(domain, PIPE_NETLOGON, &conn);
+
+ if ( fresh && (conn != NULL) ) {
+ cli_shutdown(conn->cli);
+ conn->cli = NULL;
+
+ conn = NULL;
+
+ /* purge connection from cache */
+ find_cm_connection(domain, PIPE_NETLOGON, &conn);
+ if (conn != NULL) {
+ DEBUG(0,("Could not purge connection\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ if (conn != NULL) {
+ *cli = conn->cli;
+ return NT_STATUS_OK;
+ }
+
+ result = new_cm_connection(domain, PIPE_NETLOGON, &conn);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ fstr_sprintf(lock_name, "NETLOGON\\%s", conn->controller);
+
+ if (!(got_mutex = secrets_named_mutex(lock_name, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
+ DEBUG(0,("cm_get_netlogon_cli: mutex grab failed for %s\n", conn->controller));
+ }
+
+ if ( sec_channel_type == SEC_CHAN_DOMAIN )
+ fstr_sprintf(conn->cli->mach_acct, "%s$", lp_workgroup());
+
+ /* This must be the remote domain (not ours) for schannel */
+
+ fstrcpy( conn->cli->domain, domain->name);
+
+ result = cli_nt_establish_netlogon(conn->cli, sec_channel_type, trust_passwd);
+
+ if (got_mutex)
+ secrets_named_mutex_release(lock_name);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ cli_shutdown(conn->cli);
+ DLIST_REMOVE(cm_conns, conn);
+ SAFE_FREE(conn);
+ return result;
+ }
+
+ *cli = conn->cli;
+
+ return result;
+}
+
+/* Dump the current connection status */
+
+static void dump_conn_list(void)
+{
+ struct winbindd_cm_conn *con;
+
+ DEBUG(0, ("\tDomain Controller Pipe\n"));
+
+ for(con = cm_conns; con; con = con->next) {
+ char *msg;
+
+ /* Display pipe info */
+
+ if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
+ DEBUG(0, ("Error: not enough memory!\n"));
+ } else {
+ DEBUG(0, ("%s\n", msg));
+ SAFE_FREE(msg);
+ }
+ }
+}
+
+void winbindd_cm_status(void)
+{
+ /* List open connections */
+
+ DEBUG(0, ("winbindd connection manager status:\n"));
+
+ if (cm_conns)
+ dump_conn_list();
+ else
+ DEBUG(0, ("\tNo active connections\n"));
+}
+
+/* Close all cached connections */
+
+void winbindd_cm_flush(void)
+{
+ struct winbindd_cm_conn *conn, tmp;
+
+ /* Flush connection cache */
+
+ for (conn = cm_conns; conn; conn = conn->next) {
+
+ if (!connection_ok(conn))
+ continue;
+
+ DEBUG(10, ("Closing connection to %s on %s\n",
+ conn->pipe_name, conn->controller));
+
+ if (conn->cli)
+ cli_shutdown(conn->cli);
+
+ tmp.next = conn->next;
+
+ DLIST_REMOVE(cm_conns, conn);
+ SAFE_FREE(conn);
+ conn = &tmp;
+ }
+
+ /* Flush failed connection cache */
+
+ flush_negative_conn_cache();
+}
diff --git a/source/nsswitch/winbindd_dual.c b/source/nsswitch/winbindd_dual.c
new file mode 100644
index 00000000000..a9796afa367
--- /dev/null
+++ b/source/nsswitch/winbindd_dual.c
@@ -0,0 +1,214 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind background daemon
+
+ Copyright (C) Andrew Tridgell 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ the idea of the optional dual daemon mode is ot prevent slow domain
+ responses from clagging up the rest of the system. When in dual
+ daemon mode winbindd always responds to requests from cache if the
+ request is in cache, and if the cached answer is stale then it asks
+ the "dual daemon" to update the cache for that request
+
+ */
+
+#include "includes.h"
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+extern BOOL opt_dual_daemon;
+BOOL background_process = False;
+int dual_daemon_pipe = -1;
+
+
+/* a list of requests ready to be sent to the dual daemon */
+struct dual_list {
+ struct dual_list *next;
+ char *data;
+ int length;
+ int offset;
+};
+
+static struct dual_list *dual_list;
+static struct dual_list *dual_list_end;
+
+/*
+ setup a select() including the dual daemon pipe
+ */
+int dual_select_setup(fd_set *fds, int maxfd)
+{
+ if (dual_daemon_pipe == -1 ||
+ !dual_list) {
+ return maxfd;
+ }
+
+ FD_SET(dual_daemon_pipe, fds);
+ if (dual_daemon_pipe > maxfd) {
+ maxfd = dual_daemon_pipe;
+ }
+ return maxfd;
+}
+
+
+/*
+ a hook called from the main winbindd select() loop to handle writes
+ to the dual daemon pipe
+*/
+void dual_select(fd_set *fds)
+{
+ int n;
+
+ if (dual_daemon_pipe == -1 ||
+ !dual_list ||
+ !FD_ISSET(dual_daemon_pipe, fds)) {
+ return;
+ }
+
+ n = sys_write(dual_daemon_pipe,
+ &dual_list->data[dual_list->offset],
+ dual_list->length - dual_list->offset);
+
+ if (n <= 0) {
+ /* the pipe is dead! fall back to normal operation */
+ dual_daemon_pipe = -1;
+ return;
+ }
+
+ dual_list->offset += n;
+
+ if (dual_list->offset == dual_list->length) {
+ struct dual_list *next;
+ next = dual_list->next;
+ free(dual_list->data);
+ free(dual_list);
+ dual_list = next;
+ if (!dual_list) {
+ dual_list_end = NULL;
+ }
+ }
+}
+
+/*
+ send a request to the background daemon
+ this is called for stale cached entries
+*/
+void dual_send_request(struct winbindd_cli_state *state)
+{
+ struct dual_list *list;
+
+ if (!background_process) return;
+
+ list = malloc(sizeof(*list));
+ if (!list) return;
+
+ list->next = NULL;
+ list->data = memdup(&state->request, sizeof(state->request));
+ list->length = sizeof(state->request);
+ list->offset = 0;
+
+ if (!dual_list_end) {
+ dual_list = list;
+ dual_list_end = list;
+ } else {
+ dual_list_end->next = list;
+ dual_list_end = list;
+ }
+
+ background_process = False;
+}
+
+
+/*
+the main dual daemon
+*/
+void do_dual_daemon(void)
+{
+ int fdpair[2];
+ struct winbindd_cli_state state;
+
+ if (pipe(fdpair) != 0) {
+ return;
+ }
+
+ ZERO_STRUCT(state);
+ state.pid = getpid();
+
+ dual_daemon_pipe = fdpair[1];
+ state.sock = fdpair[0];
+
+ if (fork() != 0) {
+ close(fdpair[0]);
+ return;
+ }
+ close(fdpair[1]);
+
+ /* tdb needs special fork handling */
+ if (tdb_reopen_all() == -1) {
+ DEBUG(0,("tdb_reopen_all failed.\n"));
+ _exit(0);
+ }
+
+ dual_daemon_pipe = -1;
+ opt_dual_daemon = False;
+
+ while (1) {
+ /* free up any talloc memory */
+ lp_talloc_free();
+ main_loop_talloc_free();
+
+ /* fetch a request from the main daemon */
+ winbind_client_read(&state);
+
+ if (state.finished) {
+ /* we lost contact with our parent */
+ exit(0);
+ }
+
+ /* process full rquests */
+ if (state.read_buf_len == sizeof(state.request)) {
+ DEBUG(4,("dual daemon request %d\n", (int)state.request.cmd));
+
+ /* special handling for the stateful requests */
+ switch (state.request.cmd) {
+ case WINBINDD_GETPWENT:
+ winbindd_setpwent(&state);
+ break;
+
+ case WINBINDD_GETGRENT:
+ case WINBINDD_GETGRLST:
+ winbindd_setgrent(&state);
+ break;
+ default:
+ break;
+ }
+
+ winbind_process_packet(&state);
+ SAFE_FREE(state.response.extra_data);
+
+ free_getent_state(state.getpwent_state);
+ free_getent_state(state.getgrent_state);
+ state.getpwent_state = NULL;
+ state.getgrent_state = NULL;
+ }
+ }
+}
+
diff --git a/source/nsswitch/winbindd_group.c b/source/nsswitch/winbindd_group.c
new file mode 100644
index 00000000000..94037e39200
--- /dev/null
+++ b/source/nsswitch/winbindd_group.c
@@ -0,0 +1,1211 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jeremy Allison 2001.
+ Copyright (C) Gerald (Jerry) Carter 2003.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+
+extern BOOL opt_nocache;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/*********************************************************************
+*********************************************************************/
+
+static int gr_mem_buffer( char **buffer, char **members, int num_members )
+{
+ int i;
+ int len = 0;
+ int idx = 0;
+
+ if ( num_members == 0 ) {
+ *buffer = NULL;
+ return 0;
+ }
+
+ for ( i=0; i<num_members; i++ )
+ len += strlen(members[i])+1;
+
+ *buffer = (char*)smb_xmalloc(len);
+ for ( i=0; i<num_members; i++ ) {
+ snprintf( &(*buffer)[idx], len-idx, "%s,", members[i]);
+ idx += strlen(members[i])+1;
+ }
+ /* terminate with NULL */
+ (*buffer)[len-1] = '\0';
+
+ return len;
+}
+
+/***************************************************************
+ Empty static struct for negative caching.
+****************************************************************/
+
+/* Fill a grent structure from various other information */
+
+static BOOL fill_grent(struct winbindd_gr *gr, const char *dom_name,
+ const char *gr_name, gid_t unix_gid)
+{
+ fstring full_group_name;
+ /* Fill in uid/gid */
+ fill_domain_username(full_group_name, dom_name, gr_name);
+
+ gr->gr_gid = unix_gid;
+
+ /* Group name and password */
+
+ safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
+ safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
+
+ return True;
+}
+
+/* Fill in the group membership field of a NT group given by group_sid */
+
+static BOOL fill_grent_mem(struct winbindd_domain *domain,
+ DOM_SID *group_sid,
+ enum SID_NAME_USE group_name_type,
+ int *num_gr_mem, char **gr_mem, int *gr_mem_len)
+{
+ DOM_SID **sid_mem = NULL;
+ uint32 num_names = 0;
+ uint32 *name_types = NULL;
+ unsigned int buf_len, buf_ndx, i;
+ char **names = NULL, *buf;
+ BOOL result = False;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ fstring sid_string;
+
+ if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
+ return False;
+
+ /* Initialise group membership information */
+
+ DEBUG(10, ("group SID %s\n", sid_to_string(sid_string, group_sid)));
+
+ *num_gr_mem = 0;
+
+ /* HACK ALERT!! This whole routine does not cope with group members
+ * from more than one domain, ie aliases. Thus we have to work it out
+ * ourselves in a special routine. */
+
+ if (domain->internal)
+ return fill_passdb_alias_grmem(domain, group_sid,
+ num_gr_mem,
+ gr_mem, gr_mem_len);
+
+ if ( !((group_name_type==SID_NAME_DOM_GRP) ||
+ ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
+ {
+ DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
+ sid_to_string(sid_string, group_sid), domain->name,
+ group_name_type));
+ goto done;
+ }
+
+ /* Lookup group members */
+ status = domain->methods->lookup_groupmem(domain, mem_ctx, group_sid, &num_names,
+ &sid_mem, &names, &name_types);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("could not lookup membership for group rid %s in domain %s (error: %s)\n",
+ sid_to_string(sid_string, group_sid), domain->name, nt_errstr(status)));
+
+ goto done;
+ }
+
+ DEBUG(10, ("looked up %d names\n", num_names));
+
+ if (DEBUGLEVEL >= 10) {
+ for (i = 0; i < num_names; i++)
+ DEBUG(10, ("\t%20s %s %d\n", names[i], sid_to_string(sid_string, sid_mem[i]),
+ name_types[i]));
+ }
+
+ /* Add members to list */
+
+ buf = NULL;
+ buf_len = buf_ndx = 0;
+
+ again:
+
+ for (i = 0; i < num_names; i++) {
+ char *the_name;
+ fstring name;
+ int len;
+
+ the_name = names[i];
+
+ DEBUG(10, ("processing name %s\n", the_name));
+
+ /* FIXME: need to cope with groups within groups. These
+ occur in Universal groups on a Windows 2000 native mode
+ server. */
+
+ /* make sure to allow machine accounts */
+
+ if (name_types[i] != SID_NAME_USER && name_types[i] != SID_NAME_COMPUTER) {
+ DEBUG(3, ("name %s isn't a domain user\n", the_name));
+ continue;
+ }
+
+ /* Append domain name */
+
+ fill_domain_username(name, domain->name, the_name);
+
+ len = strlen(name);
+
+ /* Add to list or calculate buffer length */
+
+ if (!buf) {
+ buf_len += len + 1; /* List is comma separated */
+ (*num_gr_mem)++;
+ DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
+ } else {
+ DEBUG(10, ("appending %s at ndx %d\n", name, len));
+ safe_strcpy(&buf[buf_ndx], name, len);
+ buf_ndx += len;
+ buf[buf_ndx] = ',';
+ buf_ndx++;
+ }
+ }
+
+ /* Allocate buffer */
+
+ if (!buf && buf_len != 0) {
+ if (!(buf = malloc(buf_len))) {
+ DEBUG(1, ("out of memory\n"));
+ result = False;
+ goto done;
+ }
+ memset(buf, 0, buf_len);
+ goto again;
+ }
+
+ if (buf && buf_ndx > 0) {
+ buf[buf_ndx - 1] = '\0';
+ }
+
+ *gr_mem = buf;
+ *gr_mem_len = buf_len;
+
+ DEBUG(10, ("num_mem = %d, len = %d, mem = %s\n", *num_gr_mem,
+ buf_len, *num_gr_mem ? buf : "NULL"));
+ result = True;
+
+done:
+
+ talloc_destroy(mem_ctx);
+
+ DEBUG(10, ("fill_grent_mem returning %d\n", result));
+
+ return result;
+}
+
+/* Return a group structure from a group name */
+
+enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state)
+{
+ DOM_SID group_sid;
+ WINBINDD_GR *grp;
+ struct winbindd_domain *domain;
+ enum SID_NAME_USE name_type;
+ fstring name_domain, name_group;
+ char *tmp, *gr_mem;
+ int gr_mem_len;
+ gid_t gid;
+
+ /* Ensure null termination */
+ state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
+
+ DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
+ state->request.data.groupname));
+
+ /* Parse domain and groupname */
+
+ memset(name_group, 0, sizeof(fstring));
+
+ tmp = state->request.data.groupname;
+
+ parse_domain_user(tmp, name_domain, name_group);
+
+ /* if no domain or our local domain, then do a local tdb search */
+
+ if ( (!*name_domain || strequal(name_domain, get_global_sam_name())) &&
+ ((grp = wb_getgrnam(name_group)) != NULL) ) {
+
+ char *buffer = NULL;
+
+ memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
+
+ gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
+
+ state->response.data.gr.gr_mem_ofs = 0;
+ state->response.length += gr_mem_len;
+ state->response.extra_data = buffer; /* give the memory away */
+
+ return WINBINDD_OK;
+ }
+
+ /* if no domain or our local domain and no local tdb group, default to
+ * our local domain for aliases */
+
+ if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
+ fstrcpy(name_domain, get_global_sam_name());
+ }
+
+ /* Get info for the domain */
+
+ if ((domain = find_domain_from_name(name_domain)) == NULL) {
+ DEBUG(3, ("could not get domain sid for domain %s\n",
+ name_domain));
+ return WINBINDD_ERROR;
+ }
+ /* should we deal with users for our domain? */
+
+ if ( lp_winbind_trusted_domains_only() && domain->primary) {
+ DEBUG(7,("winbindd_getgrnam: My domain -- rejecting getgrnam() for %s\\%s.\n",
+ name_domain, name_group));
+ return WINBINDD_ERROR;
+ }
+
+ /* Get rid and name type from name */
+
+ if (!winbindd_lookup_sid_by_name(domain, name_group, &group_sid,
+ &name_type)) {
+ DEBUG(1, ("group %s in domain %s does not exist\n",
+ name_group, name_domain));
+ return WINBINDD_ERROR;
+ }
+
+ if ( !((name_type==SID_NAME_DOM_GRP) ||
+ ((name_type==SID_NAME_ALIAS) && domain->primary) ||
+ ((name_type==SID_NAME_ALIAS) && domain->internal)) )
+ {
+ DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
+ name_group, name_type));
+ return WINBINDD_ERROR;
+ }
+
+ if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &gid, 0))) {
+ DEBUG(1, ("error converting unix gid to sid\n"));
+ return WINBINDD_ERROR;
+ }
+
+ if (!fill_grent(&state->response.data.gr, name_domain,
+ name_group, gid) ||
+ !fill_grent_mem(domain, &group_sid, name_type,
+ &state->response.data.gr.num_gr_mem,
+ &gr_mem, &gr_mem_len)) {
+ return WINBINDD_ERROR;
+ }
+
+ /* Group membership lives at start of extra data */
+
+ state->response.data.gr.gr_mem_ofs = 0;
+
+ state->response.length += gr_mem_len;
+ state->response.extra_data = gr_mem;
+
+ return WINBINDD_OK;
+}
+
+/* Return a group structure from a gid number */
+
+enum winbindd_result winbindd_getgrgid(struct winbindd_cli_state *state)
+{
+ struct winbindd_domain *domain;
+ WINBINDD_GR *grp;
+ DOM_SID group_sid;
+ enum SID_NAME_USE name_type;
+ fstring dom_name;
+ fstring group_name;
+ int gr_mem_len;
+ char *gr_mem;
+
+ DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid,
+ (unsigned long)state->request.data.gid));
+
+ /* Bug out if the gid isn't in the winbind range */
+
+ if ((state->request.data.gid < server_state.gid_low) ||
+ (state->request.data.gid > server_state.gid_high))
+ return WINBINDD_ERROR;
+
+ /* alway try local tdb lookup first */
+ if ( ( grp=wb_getgrgid(state->request.data.gid)) != NULL ) {
+ char *buffer = NULL;
+
+ memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
+
+ gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
+
+ state->response.data.gr.gr_mem_ofs = 0;
+ state->response.length += gr_mem_len;
+ state->response.extra_data = buffer; /* give away the memory */
+
+ return WINBINDD_OK;
+ }
+
+ /* Get rid from gid */
+ if (!NT_STATUS_IS_OK(idmap_gid_to_sid(&group_sid, state->request.data.gid))) {
+ DEBUG(1, ("could not convert gid %lu to rid\n",
+ (unsigned long)state->request.data.gid));
+ return WINBINDD_ERROR;
+ }
+
+ /* Get name from sid */
+
+ if (!winbindd_lookup_name_by_sid(&group_sid, dom_name, group_name, &name_type)) {
+ DEBUG(1, ("could not lookup sid\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* Fill in group structure */
+
+ domain = find_domain_from_sid(&group_sid);
+
+ if (!domain) {
+ DEBUG(1,("Can't find domain from sid\n"));
+ return WINBINDD_ERROR;
+ }
+
+ if ( !((name_type==SID_NAME_DOM_GRP) ||
+ ((name_type==SID_NAME_ALIAS) && domain->primary) ||
+ ((name_type==SID_NAME_ALIAS) && domain->internal)) )
+ {
+ DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
+ group_name, name_type));
+ return WINBINDD_ERROR;
+ }
+
+ if (!fill_grent(&state->response.data.gr, dom_name, group_name,
+ state->request.data.gid) ||
+ !fill_grent_mem(domain, &group_sid, name_type,
+ &state->response.data.gr.num_gr_mem,
+ &gr_mem, &gr_mem_len))
+ return WINBINDD_ERROR;
+
+ /* Group membership lives at start of extra data */
+
+ state->response.data.gr.gr_mem_ofs = 0;
+
+ state->response.length += gr_mem_len;
+ state->response.extra_data = gr_mem;
+
+ return WINBINDD_OK;
+}
+
+/*
+ * set/get/endgrent functions
+ */
+
+/* "Rewind" file pointer for group database enumeration */
+
+enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state)
+{
+ struct winbindd_domain *domain;
+
+ DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
+
+ /* Check user has enabled this */
+
+ if (!lp_winbind_enum_groups())
+ return WINBINDD_ERROR;
+
+ /* Free old static data if it exists */
+
+ if (state->getgrent_state != NULL) {
+ free_getent_state(state->getgrent_state);
+ state->getgrent_state = NULL;
+ }
+
+ /* Create sam pipes for each domain we know about */
+
+ for (domain = domain_list(); domain != NULL; domain = domain->next) {
+ struct getent_state *domain_state;
+
+
+ /* don't add our domaina if we are a PDC or if we
+ are a member of a Samba domain */
+
+ if ( (IS_DC || lp_winbind_trusted_domains_only())
+ && domain->primary )
+ {
+ continue;
+ }
+
+ /* Create a state record for this domain */
+
+ if ((domain_state = (struct getent_state *)
+ malloc(sizeof(struct getent_state))) == NULL) {
+ DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ ZERO_STRUCTP(domain_state);
+
+ fstrcpy(domain_state->domain_name, domain->name);
+
+ /* Add to list of open domains */
+
+ DLIST_ADD(state->getgrent_state, domain_state);
+ }
+
+ return WINBINDD_OK;
+}
+
+/* Close file pointer to ntdom group database */
+
+enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state)
+{
+ DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
+
+ free_getent_state(state->getgrent_state);
+ state->getgrent_state = NULL;
+
+ return WINBINDD_OK;
+}
+
+/* Get the list of domain groups and domain aliases for a domain. We fill in
+ the sam_entries and num_sam_entries fields with domain group information.
+ The dispinfo_ndx field is incremented to the index of the next group to
+ fetch. Return True if some groups were returned, False otherwise. */
+
+#define MAX_FETCH_SAM_ENTRIES 100
+
+static BOOL get_sam_group_entries(struct getent_state *ent)
+{
+ NTSTATUS status;
+ uint32 num_entries;
+ struct acct_info *name_list = NULL, *tmp_name_list = NULL;
+ TALLOC_CTX *mem_ctx;
+ BOOL result = False;
+ struct acct_info *sam_grp_entries = NULL;
+ struct winbindd_domain *domain;
+
+ if (ent->got_sam_entries)
+ return False;
+
+ if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
+ ent->domain_name))) {
+ DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n"));
+ return False;
+ }
+
+ /* Free any existing group info */
+
+ SAFE_FREE(ent->sam_entries);
+ ent->num_sam_entries = 0;
+ ent->got_sam_entries = True;
+
+ /* Enumerate domain groups */
+
+ num_entries = 0;
+
+ if (!(domain = find_domain_from_name(ent->domain_name))) {
+ DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
+ goto done;
+ }
+
+ /* always get the domain global groups */
+
+ status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
+ result = False;
+ goto done;
+ }
+
+ /* Copy entries into return buffer */
+
+ if (num_entries) {
+ if ( !(name_list = malloc(sizeof(struct acct_info) * num_entries)) ) {
+ DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
+ num_entries));
+ result = False;
+ goto done;
+ }
+ memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
+ }
+
+ ent->num_sam_entries = num_entries;
+
+ /* get the domain local groups if we are a member of a native win2k domain
+ and are not using LDAP to get the groups */
+
+ if ( ( lp_security() != SEC_ADS && domain->native_mode
+ && domain->primary) || domain->internal )
+ {
+ DEBUG(4,("get_sam_group_entries: Native Mode 2k domain; enumerating local groups as well\n"));
+
+ status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
+
+ if ( !NT_STATUS_IS_OK(status) ) {
+ DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
+ num_entries = 0;
+ }
+ else
+ DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
+
+ /* Copy entries into return buffer */
+
+ if ( num_entries ) {
+ if ( !(tmp_name_list = Realloc( name_list, sizeof(struct acct_info) * (ent->num_sam_entries+num_entries))) )
+ {
+ DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n",
+ num_entries));
+ result = False;
+ SAFE_FREE( name_list );
+ goto done;
+ }
+
+ name_list = tmp_name_list;
+
+ memcpy( &name_list[ent->num_sam_entries], sam_grp_entries,
+ num_entries * sizeof(struct acct_info) );
+ }
+
+ ent->num_sam_entries += num_entries;
+ }
+
+
+ /* Fill in remaining fields */
+
+ ent->sam_entries = name_list;
+ ent->sam_entry_index = 0;
+
+ result = (ent->num_sam_entries > 0);
+
+ done:
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+/* Fetch next group entry from ntdom database */
+
+#define MAX_GETGRENT_GROUPS 500
+
+enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state)
+{
+ struct getent_state *ent;
+ struct winbindd_gr *group_list = NULL;
+ int num_groups, group_list_ndx = 0, i, gr_mem_list_len = 0;
+ char *new_extra_data, *gr_mem_list = NULL;
+
+ DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
+
+ /* Check user has enabled this */
+
+ if (!lp_winbind_enum_groups())
+ return WINBINDD_ERROR;
+
+ num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
+
+ if ((state->response.extra_data =
+ malloc(num_groups * sizeof(struct winbindd_gr))) == NULL)
+ return WINBINDD_ERROR;
+
+ state->response.data.num_entries = 0;
+
+ group_list = (struct winbindd_gr *)state->response.extra_data;
+
+ if (!(ent = state->getgrent_state))
+ return WINBINDD_ERROR;
+
+ /* Start sending back groups */
+
+ for (i = 0; i < num_groups; i++) {
+ struct acct_info *name_list = NULL;
+ fstring domain_group_name;
+ uint32 result;
+ gid_t group_gid;
+ int gr_mem_len;
+ char *gr_mem, *new_gr_mem_list;
+ DOM_SID group_sid;
+ struct winbindd_domain *domain;
+
+ /* Do we need to fetch another chunk of groups? */
+
+ tryagain:
+
+ DEBUG(10, ("entry_index = %d, num_entries = %d\n",
+ ent->sam_entry_index, ent->num_sam_entries));
+
+ if (ent->num_sam_entries == ent->sam_entry_index) {
+
+ while(ent && !get_sam_group_entries(ent)) {
+ struct getent_state *next_ent;
+
+ DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name));
+
+ /* Free state information for this domain */
+
+ SAFE_FREE(ent->sam_entries);
+
+ next_ent = ent->next;
+ DLIST_REMOVE(state->getgrent_state, ent);
+
+ SAFE_FREE(ent);
+ ent = next_ent;
+ }
+
+ /* No more domains */
+
+ if (!ent)
+ break;
+ }
+
+ name_list = ent->sam_entries;
+
+ if (!(domain =
+ find_domain_from_name(ent->domain_name))) {
+ DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
+ result = False;
+ goto done;
+ }
+
+ /* Lookup group info */
+
+ sid_copy(&group_sid, &domain->sid);
+ sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
+
+ if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid, 0))) {
+
+ DEBUG(1, ("could not look up gid for group %s\n",
+ name_list[ent->sam_entry_index].acct_name));
+
+ ent->sam_entry_index++;
+ goto tryagain;
+ }
+
+ DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
+ (unsigned long)name_list[ent->sam_entry_index].rid));
+
+ /* Fill in group entry */
+
+ fill_domain_username(domain_group_name, ent->domain_name,
+ name_list[ent->sam_entry_index].acct_name);
+
+ result = fill_grent(&group_list[group_list_ndx],
+ ent->domain_name,
+ name_list[ent->sam_entry_index].acct_name,
+ group_gid);
+
+ /* Fill in group membership entry */
+
+ if (result) {
+ DOM_SID member_sid;
+ group_list[group_list_ndx].num_gr_mem = 0;
+ gr_mem = NULL;
+ gr_mem_len = 0;
+
+ /* Get group membership */
+ if (state->request.cmd == WINBINDD_GETGRLST) {
+ result = True;
+ } else {
+ sid_copy(&member_sid, &domain->sid);
+ sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
+ result = fill_grent_mem(
+ domain,
+ &member_sid,
+ SID_NAME_DOM_GRP,
+ &group_list[group_list_ndx].num_gr_mem,
+ &gr_mem, &gr_mem_len);
+ }
+ }
+
+ if (result) {
+ /* Append to group membership list */
+ new_gr_mem_list = Realloc(
+ gr_mem_list,
+ gr_mem_list_len + gr_mem_len);
+
+ if (!new_gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
+ DEBUG(0, ("out of memory\n"));
+ SAFE_FREE(gr_mem_list);
+ gr_mem_list_len = 0;
+ break;
+ }
+
+ DEBUG(10, ("list_len = %d, mem_len = %d\n",
+ gr_mem_list_len, gr_mem_len));
+
+ gr_mem_list = new_gr_mem_list;
+
+ memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
+ gr_mem_len);
+
+ SAFE_FREE(gr_mem);
+
+ group_list[group_list_ndx].gr_mem_ofs =
+ gr_mem_list_len;
+
+ gr_mem_list_len += gr_mem_len;
+ }
+
+ ent->sam_entry_index++;
+
+ /* Add group to return list */
+
+ if (result) {
+
+ DEBUG(10, ("adding group num_entries = %d\n",
+ state->response.data.num_entries));
+
+ group_list_ndx++;
+ state->response.data.num_entries++;
+
+ state->response.length +=
+ sizeof(struct winbindd_gr);
+
+ } else {
+ DEBUG(0, ("could not lookup domain group %s\n",
+ domain_group_name));
+ }
+ }
+
+ /* Copy the list of group memberships to the end of the extra data */
+
+ if (group_list_ndx == 0)
+ goto done;
+
+ new_extra_data = Realloc(
+ state->response.extra_data,
+ group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
+
+ if (!new_extra_data) {
+ DEBUG(0, ("out of memory\n"));
+ group_list_ndx = 0;
+ SAFE_FREE(state->response.extra_data);
+ SAFE_FREE(gr_mem_list);
+
+ return WINBINDD_ERROR;
+ }
+
+ state->response.extra_data = new_extra_data;
+
+ memcpy(&((char *)state->response.extra_data)
+ [group_list_ndx * sizeof(struct winbindd_gr)],
+ gr_mem_list, gr_mem_list_len);
+
+ SAFE_FREE(gr_mem_list);
+
+ state->response.length += gr_mem_list_len;
+
+ DEBUG(10, ("returning %d groups, length = %d\n",
+ group_list_ndx, gr_mem_list_len));
+
+ /* Out of domains */
+
+ done:
+
+ return (group_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
+}
+
+/* List domain groups without mapping to unix ids */
+
+enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state)
+{
+ uint32 total_entries = 0;
+ struct winbindd_domain *domain;
+ const char *which_domain;
+ char *extra_data = NULL;
+ char *ted = NULL;
+ unsigned int extra_data_len = 0, i;
+
+ DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
+
+ /* Ensure null termination */
+ state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
+ which_domain = state->request.domain_name;
+
+ /* Enumerate over trusted domains */
+
+ for (domain = domain_list(); domain; domain = domain->next) {
+ struct getent_state groups;
+
+ /* if we have a domain name restricting the request and this
+ one in the list doesn't match, then just bypass the remainder
+ of the loop */
+
+ if ( *which_domain && !strequal(which_domain, domain->name) )
+ continue;
+
+ ZERO_STRUCT(groups);
+
+ /* Get list of sam groups */
+
+ fstrcpy(groups.domain_name, domain->name);
+
+ get_sam_group_entries(&groups);
+
+ if (groups.num_sam_entries == 0) {
+ /* this domain is empty or in an error state */
+ continue;
+ }
+
+ /* keep track the of the total number of groups seen so
+ far over all domains */
+ total_entries += groups.num_sam_entries;
+
+ /* Allocate some memory for extra data. Note that we limit
+ account names to sizeof(fstring) = 128 characters. */
+ ted = Realloc(extra_data, sizeof(fstring) * total_entries);
+
+ if (!ted) {
+ DEBUG(0,("failed to enlarge buffer!\n"));
+ SAFE_FREE(extra_data);
+ return WINBINDD_ERROR;
+ } else
+ extra_data = ted;
+
+ /* Pack group list into extra data fields */
+ for (i = 0; i < groups.num_sam_entries; i++) {
+ char *group_name = ((struct acct_info *)
+ groups.sam_entries)[i].acct_name;
+ fstring name;
+
+ fill_domain_username(name, domain->name, group_name);
+ /* Append to extra data */
+ memcpy(&extra_data[extra_data_len], name,
+ strlen(name));
+ extra_data_len += strlen(name);
+ extra_data[extra_data_len++] = ',';
+ }
+
+ SAFE_FREE(groups.sam_entries);
+ }
+
+ /* Assign extra_data fields in response structure */
+ if (extra_data) {
+ extra_data[extra_data_len - 1] = '\0';
+ state->response.extra_data = extra_data;
+ state->response.length += extra_data_len;
+ }
+
+ /* No domains may have responded but that's still OK so don't
+ return an error. */
+
+ return WINBINDD_OK;
+}
+
+static void add_gid_to_array_unique(gid_t gid, gid_t **gids, int *num)
+{
+ int i;
+
+ if ((*num) >= groups_max())
+ return;
+
+ for (i=0; i<*num; i++) {
+ if ((*gids)[i] == gid)
+ return;
+ }
+
+ *gids = Realloc(*gids, (*num+1) * sizeof(gid_t));
+
+ if (*gids == NULL)
+ return;
+
+ (*gids)[*num] = gid;
+ *num += 1;
+}
+
+static void add_gids_from_sid(DOM_SID *sid, gid_t **gids, int *num)
+{
+ gid_t gid;
+ DOM_SID *aliases;
+ int j, num_aliases;
+
+ DEBUG(10, ("Adding gids from SID: %s\n", sid_string_static(sid)));
+
+ if (NT_STATUS_IS_OK(idmap_sid_to_gid(sid, &gid, 0)))
+ add_gid_to_array_unique(gid, gids, num);
+
+ /* Add nested group memberships */
+
+ if (!pdb_enum_alias_memberships(sid, &aliases, &num_aliases))
+ return;
+
+ for (j=0; j<num_aliases; j++) {
+
+ if (!NT_STATUS_IS_OK(sid_to_gid(&aliases[j], &gid)))
+ continue;
+
+ add_gid_to_array_unique(gid, gids, num);
+ }
+ SAFE_FREE(aliases);
+}
+
+/* Get user supplementary groups. This is much quicker than trying to
+ invert the groups database. We merge the groups from the gids and
+ other_sids info3 fields as trusted domain, universal group
+ memberships, and nested groups (win2k native mode only) are not
+ returned by the getgroups RPC call but are present in the info3. */
+
+enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state)
+{
+ fstring name_domain, name_user;
+ DOM_SID user_sid, group_sid;
+ enum SID_NAME_USE name_type;
+ uint32 num_groups = 0;
+ uint32 num_gids = 0;
+ NTSTATUS status;
+ DOM_SID **user_grpsids;
+ struct winbindd_domain *domain;
+ enum winbindd_result result = WINBINDD_ERROR;
+ gid_t *gid_list = NULL;
+ unsigned int i;
+ TALLOC_CTX *mem_ctx;
+ NET_USER_INFO_3 *info3 = NULL;
+
+ /* Ensure null termination */
+ state->request.data.username[sizeof(state->request.data.username)-1]='\0';
+
+ DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
+ state->request.data.username));
+
+ if (!(mem_ctx = talloc_init("winbindd_getgroups(%s)",
+ state->request.data.username)))
+ return WINBINDD_ERROR;
+
+ /* Parse domain and username */
+
+ parse_domain_user(state->request.data.username,
+ name_domain, name_user);
+
+ /* Get info for the domain */
+
+ if ((domain = find_domain_from_name(name_domain)) == NULL) {
+ DEBUG(7, ("could not find domain entry for domain %s\n",
+ name_domain));
+ goto done;
+ }
+
+ if ( domain->primary && lp_winbind_trusted_domains_only()) {
+ DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getgroups() for %s\\%s.\n",
+ name_domain, name_user));
+ return WINBINDD_ERROR;
+ }
+
+ /* Get rid and name type from name. The following costs 1 packet */
+
+ if (!winbindd_lookup_sid_by_name(domain, name_user, &user_sid,
+ &name_type)) {
+ DEBUG(1, ("user '%s' does not exist\n", name_user));
+ goto done;
+ }
+
+ if (name_type != SID_NAME_USER && name_type != SID_NAME_COMPUTER) {
+ DEBUG(1, ("name '%s' is not a user name: %d\n",
+ name_user, name_type));
+ goto done;
+ }
+
+ add_gids_from_sid(&user_sid, &gid_list, &num_gids);
+
+ /* Treat the info3 cache as authoritative as the
+ lookup_usergroups() function may return cached data. */
+
+ if ( !opt_nocache && (info3 = netsamlogon_cache_get(mem_ctx, &user_sid))) {
+
+ DEBUG(10, ("winbindd_getgroups: info3 has %d groups, %d other sids\n",
+ info3->num_groups2, info3->num_other_sids));
+
+ num_groups = info3->num_other_sids + info3->num_groups2;
+
+ /* Go through each other sid and convert it to a gid */
+
+ for (i = 0; i < info3->num_other_sids; i++) {
+ fstring name;
+ fstring dom_name;
+ enum SID_NAME_USE sid_type;
+
+ /* Is this sid known to us? It can either be
+ a trusted domain sid or a foreign sid. */
+
+ if (!winbindd_lookup_name_by_sid( &info3->other_sids[i].sid,
+ dom_name, name, &sid_type))
+ {
+ DEBUG(10, ("winbindd_getgroups: could not lookup name for %s\n",
+ sid_string_static(&info3->other_sids[i].sid)));
+ continue;
+ }
+
+ /* Check it is a domain group or an alias (domain local group)
+ in a win2k native mode domain. */
+
+ if ( !((sid_type==SID_NAME_DOM_GRP) ||
+ ((sid_type==SID_NAME_ALIAS) && domain->primary)) )
+ {
+ DEBUG(10, ("winbindd_getgroups: sid type %d "
+ "for %s is not a domain group\n",
+ sid_type,
+ sid_string_static(
+ &info3->other_sids[i].sid)));
+ continue;
+ }
+
+ add_gids_from_sid(&info3->other_sids[i].sid,
+ &gid_list, &num_gids);
+
+ if (gid_list == NULL)
+ goto done;
+ }
+
+ for (i = 0; i < info3->num_groups2; i++) {
+
+ /* create the group SID */
+
+ sid_copy( &group_sid, &domain->sid );
+ sid_append_rid( &group_sid, info3->gids[i].g_rid );
+
+ add_gids_from_sid(&group_sid, &gid_list, &num_gids);
+
+ if (gid_list == NULL)
+ goto done;
+ }
+
+ SAFE_FREE(info3);
+
+ } else {
+ status = domain->methods->lookup_usergroups(domain, mem_ctx,
+ &user_sid, &num_groups,
+ &user_grpsids);
+ if (!NT_STATUS_IS_OK(status))
+ goto done;
+
+ if (state->response.extra_data)
+ goto done;
+
+ for (i = 0; i < num_groups; i++) {
+ add_gids_from_sid(user_grpsids[i],
+ &gid_list, &num_gids);
+
+ if (gid_list == NULL)
+ goto done;
+ }
+ }
+
+ /* Send data back to client */
+
+ state->response.data.num_entries = num_gids;
+ state->response.extra_data = gid_list;
+ state->response.length += num_gids * sizeof(gid_t);
+
+ result = WINBINDD_OK;
+
+ done:
+
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+
+/* Get user supplementary sids. This is equivalent to the
+ winbindd_getgroups() function but it involves a SID->SIDs mapping
+ rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
+ idmap. This call is designed to be used with applications that need
+ to do ACL evaluation themselves. Note that the cached info3 data is
+ not used
+
+ this function assumes that the SID that comes in is a user SID. If
+ you pass in another type of SID then you may get unpredictable
+ results.
+*/
+enum winbindd_result winbindd_getusersids(struct winbindd_cli_state *state)
+{
+ DOM_SID user_sid;
+ NTSTATUS status;
+ DOM_SID **user_grpsids;
+ struct winbindd_domain *domain;
+ enum winbindd_result result = WINBINDD_ERROR;
+ unsigned int i;
+ TALLOC_CTX *mem_ctx;
+ char *ret = NULL;
+ uint32 num_groups;
+ unsigned ofs, ret_size = 0;
+
+ /* Ensure null termination */
+ state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
+
+ if (!string_to_sid(&user_sid, state->request.data.sid)) {
+ DEBUG(1, ("Could not get convert sid %s from string\n", state->request.data.sid));
+ return WINBINDD_ERROR;
+ }
+
+ if (!(mem_ctx = talloc_init("winbindd_getusersids(%s)",
+ state->request.data.username))) {
+ return WINBINDD_ERROR;
+ }
+
+ /* Get info for the domain */
+ if ((domain = find_domain_from_sid(&user_sid)) == NULL) {
+ DEBUG(0,("could not find domain entry for sid %s\n",
+ sid_string_static(&user_sid)));
+ goto done;
+ }
+
+ status = domain->methods->lookup_usergroups(domain, mem_ctx,
+ &user_sid, &num_groups,
+ &user_grpsids);
+ if (!NT_STATUS_IS_OK(status))
+ goto done;
+
+ if (num_groups == 0) {
+ goto no_groups;
+ }
+
+ /* work out the response size */
+ for (i = 0; i < num_groups; i++) {
+ const char *s = sid_string_static(user_grpsids[i]);
+ ret_size += strlen(s) + 1;
+ }
+
+ /* build the reply */
+ ret = malloc(ret_size);
+ if (!ret) goto done;
+ ofs = 0;
+ for (i = 0; i < num_groups; i++) {
+ const char *s = sid_string_static(user_grpsids[i]);
+ safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
+ ofs += strlen(ret+ofs) + 1;
+ }
+
+no_groups:
+ /* Send data back to client */
+ state->response.data.num_entries = num_groups;
+ state->response.extra_data = ret;
+ state->response.length += ret_size;
+ result = WINBINDD_OK;
+
+ done:
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
diff --git a/source/nsswitch/winbindd_misc.c b/source/nsswitch/winbindd_misc.c
new file mode 100644
index 00000000000..18478992f3e
--- /dev/null
+++ b/source/nsswitch/winbindd_misc.c
@@ -0,0 +1,307 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon - miscellaneous other functions
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Andrew Bartlett 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* Check the machine account password is valid */
+
+enum winbindd_result winbindd_check_machine_acct(struct winbindd_cli_state *state)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uchar trust_passwd[16];
+ int num_retries = 0;
+ struct cli_state *cli;
+ uint32 sec_channel_type;
+ struct winbindd_domain *contact_domain;
+
+ DEBUG(3, ("[%5lu]: check machine account\n", (unsigned long)state->pid));
+
+ /* Get trust account password */
+
+ again:
+ if (!secrets_fetch_trust_account_password(
+ lp_workgroup(), trust_passwd, NULL, &sec_channel_type)) {
+ result = NT_STATUS_INTERNAL_ERROR;
+ goto done;
+ }
+
+
+ contact_domain = find_our_domain();
+ if (!contact_domain) {
+ result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ DEBUG(1, ("Cannot find our own domain!\n"));
+ goto done;
+ }
+
+ /* This call does a cli_nt_setup_creds() which implicitly checks
+ the trust account password. */
+ /* Don't shut this down - it belongs to the connection cache code */
+
+ result = cm_get_netlogon_cli(contact_domain,
+ trust_passwd, sec_channel_type, True, &cli);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
+ goto done;
+ }
+
+ /* There is a race condition between fetching the trust account
+ password and the periodic machine password change. So it's
+ possible that the trust account password has been changed on us.
+ We are returned NT_STATUS_ACCESS_DENIED if this happens. */
+
+#define MAX_RETRIES 8
+
+ if ((num_retries < MAX_RETRIES) &&
+ NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED)) {
+ num_retries++;
+ goto again;
+ }
+
+ /* Pass back result code - zero for success, other values for
+ specific failures. */
+
+ DEBUG(3, ("secret is %s\n", NT_STATUS_IS_OK(result) ?
+ "good" : "bad"));
+
+ done:
+ state->response.data.auth.nt_status = NT_STATUS_V(result);
+ fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
+ fstrcpy(state->response.data.auth.error_string, nt_errstr(result));
+ state->response.data.auth.pam_error = nt_status_to_pam(result);
+
+ DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Checking the trust account password returned %s\n",
+ state->response.data.auth.nt_status_string));
+
+ return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
+}
+
+enum winbindd_result winbindd_list_trusted_domains(struct winbindd_cli_state
+ *state)
+{
+ struct winbindd_domain *domain;
+ int total_entries = 0, extra_data_len = 0;
+ char *ted, *extra_data = NULL;
+
+ DEBUG(3, ("[%5lu]: list trusted domains\n", (unsigned long)state->pid));
+
+ /* We need to refresh the trusted domain list as the domains may
+ have changed since we last looked. There may be a sequence
+ number or something we should use but I haven't found it yet. */
+
+ if (!init_domain_list()) {
+ DEBUG(1, ("winbindd_list_trusted_domains: could not "
+ "refresh trusted domain list\n"));
+ return WINBINDD_ERROR;
+ }
+
+ for(domain = domain_list(); domain; domain = domain->next) {
+
+ /* Skip own domain */
+
+ if (domain->primary) continue;
+
+ /* Add domain to list */
+
+ total_entries++;
+ ted = Realloc(extra_data, sizeof(fstring) *
+ total_entries);
+
+ if (!ted) {
+ DEBUG(0,("winbindd_list_trusted_domains: failed to enlarge buffer!\n"));
+ SAFE_FREE(extra_data);
+ return WINBINDD_ERROR;
+ } else
+ extra_data = ted;
+
+ memcpy(&extra_data[extra_data_len], domain->name,
+ strlen(domain->name));
+
+ extra_data_len += strlen(domain->name);
+ extra_data[extra_data_len++] = ',';
+ }
+
+ if (extra_data) {
+ if (extra_data_len > 1)
+ extra_data[extra_data_len - 1] = '\0';
+ state->response.extra_data = extra_data;
+ state->response.length += extra_data_len;
+ }
+
+ return WINBINDD_OK;
+}
+
+
+enum winbindd_result winbindd_show_sequence(struct winbindd_cli_state *state)
+{
+ struct winbindd_domain *domain;
+ char *extra_data = NULL;
+ const char *which_domain;
+
+ DEBUG(3, ("[%5lu]: show sequence\n", (unsigned long)state->pid));
+
+ /* Ensure null termination */
+ state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
+ which_domain = state->request.domain_name;
+
+ extra_data = strdup("");
+
+ /* this makes for a very simple data format, and is easily parsable as well
+ if that is ever needed */
+ for (domain = domain_list(); domain; domain = domain->next) {
+ char *s;
+
+ /* if we have a domain name restricting the request and this
+ one in the list doesn't match, then just bypass the remainder
+ of the loop */
+
+ if ( *which_domain && !strequal(which_domain, domain->name) )
+ continue;
+
+ domain->methods->sequence_number(domain, &domain->sequence_number);
+
+ if (DOM_SEQUENCE_NONE == (unsigned)domain->sequence_number) {
+ asprintf(&s,"%s%s : DISCONNECTED\n", extra_data,
+ domain->name);
+ } else {
+ asprintf(&s,"%s%s : %u\n", extra_data,
+ domain->name, (unsigned)domain->sequence_number);
+ }
+ free(extra_data);
+ extra_data = s;
+ }
+
+ state->response.extra_data = extra_data;
+ /* must add one to length to copy the 0 for string termination */
+ state->response.length += strlen(extra_data) + 1;
+
+ return WINBINDD_OK;
+}
+
+enum winbindd_result winbindd_domain_info(struct winbindd_cli_state *state)
+{
+ struct winbindd_domain *domain;
+
+ DEBUG(3, ("[%5lu]: domain_info [%s]\n", (unsigned long)state->pid,
+ state->request.domain_name));
+
+ domain = find_domain_from_name(state->request.domain_name);
+
+ if (domain == NULL) {
+ DEBUG(3, ("Did not find domain [%s]\n",
+ state->request.domain_name));
+ return WINBINDD_ERROR;
+ }
+
+ fstrcpy(state->response.data.domain_info.name, domain->name);
+ fstrcpy(state->response.data.domain_info.alt_name, domain->alt_name);
+ fstrcpy(state->response.data.domain_info.sid,
+ sid_string_static(&domain->sid));
+
+ state->response.data.domain_info.native_mode = domain->native_mode;
+ state->response.data.domain_info.active_directory = domain->active_directory;
+ state->response.data.domain_info.primary = domain->primary;
+
+ state->response.data.domain_info.sequence_number =
+ domain->sequence_number;
+
+ return WINBINDD_OK;
+}
+
+enum winbindd_result winbindd_ping(struct winbindd_cli_state
+ *state)
+{
+ DEBUG(3, ("[%5lu]: ping\n", (unsigned long)state->pid));
+
+ return WINBINDD_OK;
+}
+
+/* List various tidbits of information */
+
+enum winbindd_result winbindd_info(struct winbindd_cli_state *state)
+{
+
+ DEBUG(3, ("[%5lu]: request misc info\n", (unsigned long)state->pid));
+
+ state->response.data.info.winbind_separator = *lp_winbind_separator();
+ fstrcpy(state->response.data.info.samba_version, SAMBA_VERSION_STRING);
+
+ return WINBINDD_OK;
+}
+
+/* Tell the client the current interface version */
+
+enum winbindd_result winbindd_interface_version(struct winbindd_cli_state *state)
+{
+
+ DEBUG(3, ("[%5lu]: request interface version\n", (unsigned long)state->pid));
+
+ state->response.data.interface_version = WINBIND_INTERFACE_VERSION;
+
+ return WINBINDD_OK;
+}
+
+/* What domain are we a member of? */
+
+enum winbindd_result winbindd_domain_name(struct winbindd_cli_state *state)
+{
+
+ DEBUG(3, ("[%5lu]: request domain name\n", (unsigned long)state->pid));
+
+ fstrcpy(state->response.data.domain_name, lp_workgroup());
+
+ return WINBINDD_OK;
+}
+
+/* What's my name again? */
+
+enum winbindd_result winbindd_netbios_name(struct winbindd_cli_state *state)
+{
+
+ DEBUG(3, ("[%5lu]: request netbios name\n", (unsigned long)state->pid));
+
+ fstrcpy(state->response.data.netbios_name, global_myname());
+
+ return WINBINDD_OK;
+}
+
+/* Where can I find the privilaged pipe? */
+
+enum winbindd_result winbindd_priv_pipe_dir(struct winbindd_cli_state *state)
+{
+
+ DEBUG(3, ("[%5lu]: request location of privileged pipe\n", (unsigned long)state->pid));
+
+ state->response.extra_data = strdup(get_winbind_priv_pipe_dir());
+ if (!state->response.extra_data)
+ return WINBINDD_ERROR;
+
+ /* must add one to length to copy the 0 for string termination */
+ state->response.length += strlen((char *)state->response.extra_data) + 1;
+
+ return WINBINDD_OK;
+}
diff --git a/source/nsswitch/winbindd_nss.h b/source/nsswitch/winbindd_nss.h
new file mode 100644
index 00000000000..43c9e68cd9f
--- /dev/null
+++ b/source/nsswitch/winbindd_nss.h
@@ -0,0 +1,288 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SAFE_FREE
+#define SAFE_FREE(x) do { if(x) {free(x); x=NULL;} } while(0)
+#endif
+
+#ifndef _WINBINDD_NTDOM_H
+#define _WINBINDD_NTDOM_H
+
+#define WINBINDD_SOCKET_NAME "pipe" /* Name of PF_UNIX socket */
+#define WINBINDD_SOCKET_DIR "/tmp/.winbindd" /* Name of PF_UNIX dir */
+#define WINBINDD_PRIV_SOCKET_SUBDIR "winbindd_privileged" /* name of subdirectory of lp_lockdir() to hold the 'privileged' pipe */
+#define WINBINDD_DOMAIN_ENV "WINBINDD_DOMAIN" /* Environment variables */
+#define WINBINDD_DONT_ENV "_NO_WINBINDD"
+
+/* Update this when you change the interface. */
+
+#define WINBIND_INTERFACE_VERSION 10
+
+/* Socket commands */
+
+enum winbindd_cmd {
+
+ WINBINDD_INTERFACE_VERSION, /* Always a well known value */
+
+ /* Get users and groups */
+
+ WINBINDD_GETPWNAM,
+ WINBINDD_GETPWUID,
+ WINBINDD_GETGRNAM,
+ WINBINDD_GETGRGID,
+ WINBINDD_GETGROUPS,
+
+ /* Enumerate users and groups */
+
+ WINBINDD_SETPWENT,
+ WINBINDD_ENDPWENT,
+ WINBINDD_GETPWENT,
+ WINBINDD_SETGRENT,
+ WINBINDD_ENDGRENT,
+ WINBINDD_GETGRENT,
+
+ /* PAM authenticate and password change */
+
+ WINBINDD_PAM_AUTH,
+ WINBINDD_PAM_AUTH_CRAP,
+ WINBINDD_PAM_CHAUTHTOK,
+
+ /* List various things */
+
+ WINBINDD_LIST_USERS, /* List w/o rid->id mapping */
+ WINBINDD_LIST_GROUPS, /* Ditto */
+ WINBINDD_LIST_TRUSTDOM,
+
+ /* SID conversion */
+
+ WINBINDD_LOOKUPSID,
+ WINBINDD_LOOKUPNAME,
+
+ /* Lookup functions */
+
+ WINBINDD_SID_TO_UID,
+ WINBINDD_SID_TO_GID,
+ WINBINDD_UID_TO_SID,
+ WINBINDD_GID_TO_SID,
+ WINBINDD_ALLOCATE_RID,
+
+ /* Miscellaneous other stuff */
+
+ WINBINDD_CHECK_MACHACC, /* Check machine account pw works */
+ WINBINDD_PING, /* Just tell me winbind is running */
+ WINBINDD_INFO, /* Various bit of info. Currently just tidbits */
+ WINBINDD_DOMAIN_NAME, /* The domain this winbind server is a member of (lp_workgroup()) */
+
+ WINBINDD_DOMAIN_INFO, /* Most of what we know from
+ struct winbindd_domain */
+
+ WINBINDD_SHOW_SEQUENCE, /* display sequence numbers of domains */
+
+ /* WINS commands */
+
+ WINBINDD_WINS_BYIP,
+ WINBINDD_WINS_BYNAME,
+
+ /* account management commands */
+
+ WINBINDD_CREATE_USER,
+ WINBINDD_CREATE_GROUP,
+ WINBINDD_ADD_USER_TO_GROUP,
+ WINBINDD_REMOVE_USER_FROM_GROUP,
+ WINBINDD_SET_USER_PRIMARY_GROUP,
+ WINBINDD_DELETE_USER,
+ WINBINDD_DELETE_GROUP,
+
+ /* this is like GETGRENT but gives an empty group list */
+ WINBINDD_GETGRLST,
+
+ WINBINDD_NETBIOS_NAME, /* The netbios name of the server */
+ /* Placeholder for end of cmd list */
+
+ /* find the location of our privileged pipe */
+ WINBINDD_PRIV_PIPE_DIR,
+
+ /* return a list of group sids for a user sid */
+ WINBINDD_GETUSERSIDS,
+
+ WINBINDD_NUM_CMDS
+};
+
+typedef struct winbindd_pw {
+ fstring pw_name;
+ fstring pw_passwd;
+ uid_t pw_uid;
+ gid_t pw_gid;
+ fstring pw_gecos;
+ fstring pw_dir;
+ fstring pw_shell;
+} WINBINDD_PW;
+
+
+typedef struct winbindd_gr {
+ fstring gr_name;
+ fstring gr_passwd;
+ gid_t gr_gid;
+ int num_gr_mem;
+ int gr_mem_ofs; /* offset to group membership */
+ char **gr_mem;
+} WINBINDD_GR;
+
+
+#define WBFLAG_PAM_INFO3_NDR 0x0001
+#define WBFLAG_PAM_INFO3_TEXT 0x0002
+#define WBFLAG_PAM_NTKEY 0x0004
+#define WBFLAG_PAM_LMKEY 0x0008
+#define WBFLAG_PAM_CONTACT_TRUSTDOM 0x0010
+#define WBFLAG_QUERY_ONLY 0x0020
+#define WBFLAG_ALLOCATE_RID 0x0040
+#define WBFLAG_PAM_UNIX_NAME 0x0080
+#define WBFLAG_PAM_AFS_TOKEN 0x0100
+
+/* Winbind request structure */
+
+struct winbindd_request {
+ uint32 length;
+ enum winbindd_cmd cmd; /* Winbindd command to execute */
+ pid_t pid; /* pid of calling process */
+ uint32 flags; /* flags relavant to a given request */
+ fstring domain_name; /* name of domain for which the request applies */
+
+ union {
+ fstring winsreq; /* WINS request */
+ fstring username; /* getpwnam */
+ fstring groupname; /* getgrnam */
+ uid_t uid; /* getpwuid, uid_to_sid */
+ gid_t gid; /* getgrgid, gid_to_sid */
+ struct {
+ /* We deliberatedly don't split into domain/user to
+ avoid having the client know what the separator
+ character is. */
+ fstring user;
+ fstring pass;
+ } auth; /* pam_winbind auth module */
+ struct {
+ unsigned char chal[8];
+ fstring user;
+ fstring domain;
+ fstring lm_resp;
+ uint16 lm_resp_len;
+ fstring nt_resp;
+ uint16 nt_resp_len;
+ fstring workstation;
+ } auth_crap;
+ struct {
+ fstring user;
+ fstring oldpass;
+ fstring newpass;
+ } chauthtok; /* pam_winbind passwd module */
+ fstring sid; /* lookupsid, sid_to_[ug]id */
+ struct {
+ fstring dom_name; /* lookupname */
+ fstring name;
+ } name;
+ uint32 num_entries; /* getpwent, getgrent */
+ struct {
+ fstring username;
+ fstring groupname;
+ } acct_mgt;
+ } data;
+ char null_term;
+};
+
+/* Response values */
+
+enum winbindd_result {
+ WINBINDD_ERROR,
+ WINBINDD_OK
+};
+
+/* Winbind response structure */
+
+struct winbindd_response {
+
+ /* Header information */
+
+ uint32 length; /* Length of response */
+ enum winbindd_result result; /* Result code */
+
+ /* Fixed length return data */
+
+ union {
+ int interface_version; /* Try to ensure this is always in the same spot... */
+
+ fstring winsresp; /* WINS response */
+
+ /* getpwnam, getpwuid */
+
+ struct winbindd_pw pw;
+
+ /* getgrnam, getgrgid */
+
+ struct winbindd_gr gr;
+
+ uint32 num_entries; /* getpwent, getgrent */
+ struct winbindd_sid {
+ fstring sid; /* lookupname, [ug]id_to_sid */
+ int type;
+ } sid;
+ struct winbindd_name {
+ fstring dom_name; /* lookupsid */
+ fstring name;
+ int type;
+ } name;
+ uid_t uid; /* sid_to_uid */
+ gid_t gid; /* sid_to_gid */
+ struct winbindd_info {
+ char winbind_separator;
+ fstring samba_version;
+ } info;
+ fstring domain_name;
+ fstring netbios_name;
+
+ struct auth_reply {
+ uint32 nt_status;
+ fstring nt_status_string;
+ fstring error_string;
+ int pam_error;
+ char nt_session_key[16];
+ char first_8_lm_hash[8];
+ } auth;
+ uint32 rid; /* create user or group or allocate rid */
+ struct {
+ fstring name;
+ fstring alt_name;
+ fstring sid;
+ BOOL native_mode;
+ BOOL active_directory;
+ BOOL primary;
+ uint32 sequence_number;
+ } domain_info;
+ } data;
+
+ /* Variable length return data */
+
+ void *extra_data; /* getgrnam, getgrgid, getgrent */
+};
+
+#endif
diff --git a/source/nsswitch/winbindd_pam.c b/source/nsswitch/winbindd_pam.c
new file mode 100644
index 00000000000..1d232edfe31
--- /dev/null
+++ b/source/nsswitch/winbindd_pam.c
@@ -0,0 +1,568 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon - pam auth funcions
+
+ Copyright (C) Andrew Tridgell 2000
+ Copyright (C) Tim Potter 2001
+ Copyright (C) Andrew Bartlett 2001-2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+
+static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
+ struct winbindd_cli_state *state,
+ NET_USER_INFO_3 *info3)
+{
+ prs_struct ps;
+ uint32 size;
+ if (!prs_init(&ps, 256 /* Random, non-zero number */, mem_ctx, MARSHALL)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (!net_io_user_info3("", info3, &ps, 1, 3)) {
+ prs_mem_free(&ps);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ size = prs_data_size(&ps);
+ state->response.extra_data = malloc(size);
+ if (!state->response.extra_data) {
+ prs_mem_free(&ps);
+ return NT_STATUS_NO_MEMORY;
+ }
+ prs_copy_all_data_out(state->response.extra_data, &ps);
+ state->response.length += size;
+ prs_mem_free(&ps);
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Authenticate a user with a clear test password
+**********************************************************************/
+
+enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state)
+{
+ NTSTATUS result;
+ fstring name_domain, name_user;
+ unsigned char trust_passwd[16];
+ time_t last_change_time;
+ uint32 sec_channel_type;
+ NET_USER_INFO_3 info3;
+ struct cli_state *cli = NULL;
+ uchar chal[8];
+ TALLOC_CTX *mem_ctx = NULL;
+ DATA_BLOB lm_resp;
+ DATA_BLOB nt_resp;
+ DOM_CRED ret_creds;
+ int attempts = 0;
+ unsigned char local_lm_response[24];
+ unsigned char local_nt_response[24];
+ struct winbindd_domain *contact_domain;
+ BOOL retry;
+
+ /* Ensure null termination */
+ state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0';
+
+ /* Ensure null termination */
+ state->request.data.auth.pass[sizeof(state->request.data.auth.pass)-1]='\0';
+
+ DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid,
+ state->request.data.auth.user));
+
+ if (!(mem_ctx = talloc_init("winbind pam auth for %s", state->request.data.auth.user))) {
+ DEBUG(0, ("winbindd_pam_auth: could not talloc_init()!\n"));
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ /* Parse domain and username */
+
+ parse_domain_user(state->request.data.auth.user, name_domain, name_user);
+
+ /* do password magic */
+
+ generate_random_buffer(chal, 8, False);
+ SMBencrypt(state->request.data.auth.pass, chal, local_lm_response);
+
+ SMBNTencrypt(state->request.data.auth.pass, chal, local_nt_response);
+
+ lm_resp = data_blob_talloc(mem_ctx, local_lm_response, sizeof(local_lm_response));
+ nt_resp = data_blob_talloc(mem_ctx, local_nt_response, sizeof(local_nt_response));
+
+ /* what domain should we contact? */
+
+ if ( IS_DC ) {
+ if (!(contact_domain = find_domain_from_name(name_domain))) {
+ DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
+ state->request.data.auth.user, name_domain, name_user, name_domain));
+ result = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+
+ } else {
+ if (is_myname(name_domain)) {
+ DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
+ result = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+
+ if (!(contact_domain = find_our_domain())) {
+ DEBUG(1, ("Authenticatoin for [%s] -> [%s]\\[%s] in our domain failed - we can't find our domain!\n",
+ state->request.data.auth.user, name_domain, name_user));
+ result = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+ }
+
+ if ( !get_trust_pw(contact_domain->name, trust_passwd, &last_change_time, &sec_channel_type) ) {
+ result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ goto done;
+ }
+
+ /* check authentication loop */
+
+ do {
+ ZERO_STRUCT(info3);
+ ZERO_STRUCT(ret_creds);
+ retry = False;
+
+ /* Don't shut this down - it belongs to the connection cache code */
+ result = cm_get_netlogon_cli(contact_domain, trust_passwd,
+ sec_channel_type, False, &cli);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
+ goto done;
+ }
+
+ result = cli_netlogon_sam_network_logon(cli, mem_ctx,
+ &ret_creds,
+ name_user, name_domain,
+ global_myname(), chal,
+ lm_resp, nt_resp,
+ &info3);
+ attempts += 1;
+
+ /* We have to try a second time as cm_get_netlogon_cli
+ might not yet have noticed that the DC has killed
+ our connection. */
+
+ if ( cli->fd == -1 ) {
+ retry = True;
+ continue;
+ }
+
+ /* if we get access denied, a possible cuase was that we had and open
+ connection to the DC, but someone changed our machine account password
+ out from underneath us using 'net rpc changetrustpw' */
+
+ if ( NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) ) {
+ DEBUG(3,("winbindd_pam_auth: sam_logon returned ACCESS_DENIED. Maybe the trust account "
+ "password was changed and we didn't know it. Killing connections to domain %s\n",
+ name_domain));
+ winbindd_cm_flush();
+ retry = True;
+ cli = NULL;
+ }
+
+ } while ( (attempts < 2) && retry );
+
+ clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), &ret_creds);
+
+ if (NT_STATUS_IS_OK(result)) {
+ netsamlogon_cache_store( cli->mem_ctx, &info3 );
+ wcache_invalidate_samlogon(find_domain_from_name(name_domain), &info3);
+ }
+
+done:
+ /* give us a more useful (more correct?) error code */
+ if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) || (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
+ result = NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ state->response.data.auth.nt_status = NT_STATUS_V(result);
+ fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
+
+ /* we might have given a more useful error above */
+ if (!*state->response.data.auth.error_string)
+ fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
+ state->response.data.auth.pam_error = nt_status_to_pam(result);
+
+ DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
+ state->request.data.auth.user,
+ state->response.data.auth.nt_status_string,
+ state->response.data.auth.pam_error));
+
+ if ( NT_STATUS_IS_OK(result) &&
+ (state->request.flags & WBFLAG_PAM_AFS_TOKEN) ) {
+
+ char *afsname = strdup(lp_afs_username_map());
+ char *cell;
+
+ if (afsname == NULL) goto no_token;
+
+ afsname = realloc_string_sub(afsname, "%D", name_domain);
+ afsname = realloc_string_sub(afsname, "%u", name_user);
+ afsname = realloc_string_sub(afsname, "%U", name_user);
+
+ if (afsname == NULL) goto no_token;
+
+ strlower_m(afsname);
+
+ cell = strchr(afsname, '@');
+
+ if (cell == NULL) goto no_token;
+
+ *cell = '\0';
+ cell += 1;
+
+ /* Append an AFS token string */
+ state->response.extra_data =
+ afs_createtoken_str(afsname, cell);
+
+ if (state->response.extra_data != NULL)
+ state->response.length +=
+ strlen(state->response.extra_data)+1;
+
+ no_token:
+ SAFE_FREE(afsname);
+ }
+
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+
+ return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
+}
+
+/**********************************************************************
+ Challenge Response Authentication Protocol
+**********************************************************************/
+
+enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state)
+{
+ NTSTATUS result;
+ unsigned char trust_passwd[16];
+ time_t last_change_time;
+ uint32 sec_channel_type;
+ NET_USER_INFO_3 info3;
+ struct cli_state *cli = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+ char *name_user = NULL;
+ const char *name_domain = NULL;
+ const char *workstation;
+ struct winbindd_domain *contact_domain;
+ DOM_CRED ret_creds;
+ int attempts = 0;
+ BOOL retry;
+
+ DATA_BLOB lm_resp, nt_resp;
+
+ if (!state->privileged) {
+ char *error_string = NULL;
+ DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access denied. !\n"));
+ DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions on %s are set correctly.\n",
+ get_winbind_priv_pipe_dir()));
+ /* send a better message than ACCESS_DENIED */
+ asprintf(&error_string, "winbind client not authorized to use winbindd_pam_auth_crap. Ensure permissions on %s are set correctly.",
+ get_winbind_priv_pipe_dir());
+ push_utf8_fstring(state->response.data.auth.error_string, error_string);
+ SAFE_FREE(error_string);
+ result = NT_STATUS_ACCESS_DENIED;
+ goto done;
+ }
+
+ /* Ensure null termination */
+ state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]=0;
+ state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]=0;
+
+ if (!(mem_ctx = talloc_init("winbind pam auth crap for (utf8) %s", state->request.data.auth_crap.user))) {
+ DEBUG(0, ("winbindd_pam_auth_crap: could not talloc_init()!\n"));
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ if (pull_utf8_talloc(mem_ctx, &name_user, state->request.data.auth_crap.user) == (size_t)-1) {
+ DEBUG(0, ("winbindd_pam_auth_crap: pull_utf8_talloc failed!\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (*state->request.data.auth_crap.domain) {
+ char *dom = NULL;
+ if (pull_utf8_talloc(mem_ctx, &dom, state->request.data.auth_crap.domain) == (size_t)-1) {
+ DEBUG(0, ("winbindd_pam_auth_crap: pull_utf8_talloc failed!\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ name_domain = dom;
+ } else if (lp_winbind_use_default_domain()) {
+ name_domain = lp_workgroup();
+ } else {
+ DEBUG(5,("no domain specified with username (%s) - failing auth\n",
+ name_user));
+ result = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+
+ DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
+ name_domain, name_user));
+
+ if (*state->request.data.auth_crap.workstation) {
+ char *wrk = NULL;
+ if (pull_utf8_talloc(mem_ctx, &wrk, state->request.data.auth_crap.workstation) == (size_t)-1) {
+ DEBUG(0, ("winbindd_pam_auth_crap: pull_utf8_talloc failed!\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ workstation = wrk;
+ } else {
+ workstation = global_myname();
+ }
+
+ if (state->request.data.auth_crap.lm_resp_len > sizeof(state->request.data.auth_crap.lm_resp)
+ || state->request.data.auth_crap.nt_resp_len > sizeof(state->request.data.auth_crap.nt_resp)) {
+ DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
+ state->request.data.auth_crap.lm_resp_len,
+ state->request.data.auth_crap.nt_resp_len));
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ lm_resp = data_blob_talloc(mem_ctx, state->request.data.auth_crap.lm_resp, state->request.data.auth_crap.lm_resp_len);
+ nt_resp = data_blob_talloc(mem_ctx, state->request.data.auth_crap.nt_resp, state->request.data.auth_crap.nt_resp_len);
+
+
+ /* what domain should we contact? */
+
+ if ( IS_DC ) {
+ if (!(contact_domain = find_domain_from_name(name_domain))) {
+ DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
+ state->request.data.auth.user, name_domain, name_user, name_domain));
+ result = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+
+ } else {
+ if (is_myname(name_domain)) {
+ DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
+ result = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+
+ if (!(contact_domain = find_our_domain())) {
+ DEBUG(1, ("Authenticatoin for [%s] -> [%s]\\[%s] in our domain failed - we can't find our domain!\n",
+ state->request.data.auth.user, name_domain, name_user));
+ result = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+ }
+
+ if ( !get_trust_pw(contact_domain->name, trust_passwd, &last_change_time, &sec_channel_type) ) {
+ result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ goto done;
+ }
+
+ do {
+ ZERO_STRUCT(info3);
+ ZERO_STRUCT(ret_creds);
+ retry = False;
+
+ /* Don't shut this down - it belongs to the connection cache code */
+ result = cm_get_netlogon_cli(contact_domain, trust_passwd, sec_channel_type, False, &cli);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
+ nt_errstr(result)));
+ goto done;
+ }
+
+ result = cli_netlogon_sam_network_logon(cli, mem_ctx,
+ &ret_creds,
+ name_user, name_domain,
+ workstation,
+ state->request.data.auth_crap.chal,
+ lm_resp, nt_resp,
+ &info3);
+
+ attempts += 1;
+
+ /* We have to try a second time as cm_get_netlogon_cli
+ might not yet have noticed that the DC has killed
+ our connection. */
+
+ if ( cli->fd == -1 ) {
+ retry = True;
+ continue;
+ }
+
+ /* if we get access denied, a possible cause was that we had and open
+ connection to the DC, but someone changed our machine account password
+ out from underneath us using 'net rpc changetrustpw' */
+
+ if ( NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) ) {
+ DEBUG(3,("winbindd_pam_auth_crap: sam_logon returned ACCESS_DENIED. Maybe the trust account "
+ "password was changed and we didn't know it. Killing connections to domain %s\n",
+ contact_domain->name));
+ winbindd_cm_flush();
+ retry = True;
+ cli = NULL;
+ }
+
+ } while ( (attempts < 2) && retry );
+
+ clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), &ret_creds);
+
+ if (NT_STATUS_IS_OK(result)) {
+ netsamlogon_cache_store( cli->mem_ctx, &info3 );
+ wcache_invalidate_samlogon(find_domain_from_name(name_domain), &info3);
+
+ if (state->request.flags & WBFLAG_PAM_INFO3_NDR) {
+ result = append_info3_as_ndr(mem_ctx, state, &info3);
+ } else if (state->request.flags & WBFLAG_PAM_UNIX_NAME) {
+ /* ntlm_auth should return the unix username, per
+ 'winbind use default domain' settings and the like */
+
+ fstring username_out;
+ const char *nt_username, *nt_domain;
+ if (!(nt_username = unistr2_tdup(mem_ctx, &(info3.uni_user_name)))) {
+ /* If the server didn't give us one, just use the one we sent them */
+ nt_username = name_user;
+ }
+
+ if (!(nt_domain = unistr2_tdup(mem_ctx, &(info3.uni_logon_dom)))) {
+ /* If the server didn't give us one, just use the one we sent them */
+ nt_domain = name_domain;
+ }
+
+ fill_domain_username(username_out, nt_domain, nt_username);
+
+ DEBUG(5, ("Setting unix username to [%s]\n", username_out));
+
+ /* this interface is in UTF8 */
+ if (push_utf8_allocate((char **)&state->response.extra_data, username_out) == -1) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ state->response.length += strlen(state->response.extra_data)+1;
+ }
+
+ if (state->request.flags & WBFLAG_PAM_NTKEY) {
+ memcpy(state->response.data.auth.nt_session_key, info3.user_sess_key, sizeof(state->response.data.auth.nt_session_key) /* 16 */);
+ }
+ if (state->request.flags & WBFLAG_PAM_LMKEY) {
+ memcpy(state->response.data.auth.first_8_lm_hash, info3.padding, sizeof(state->response.data.auth.first_8_lm_hash) /* 8 */);
+ }
+ }
+
+done:
+ /* give us a more useful (more correct?) error code */
+ if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) || (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
+ result = NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ state->response.data.auth.nt_status = NT_STATUS_V(result);
+ push_utf8_fstring(state->response.data.auth.nt_status_string, nt_errstr(result));
+
+ /* we might have given a more useful error above */
+ if (!*state->response.data.auth.error_string)
+ push_utf8_fstring(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
+ state->response.data.auth.pam_error = nt_status_to_pam(result);
+
+ DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
+ ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
+ name_domain,
+ name_user,
+ state->response.data.auth.nt_status_string,
+ state->response.data.auth.pam_error));
+
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+
+ return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
+}
+
+/* Change a user password */
+
+enum winbindd_result winbindd_pam_chauthtok(struct winbindd_cli_state *state)
+{
+ NTSTATUS result;
+ char *oldpass, *newpass;
+ fstring domain, user;
+ CLI_POLICY_HND *hnd;
+ TALLOC_CTX *mem_ctx;
+ struct winbindd_domain *contact_domain;
+
+ DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
+ state->request.data.chauthtok.user));
+
+ if (!(mem_ctx = talloc_init("winbind password change for (utf8) %s",
+ state->request.data.chauthtok.user))) {
+ DEBUG(0, ("winbindd_pam_auth_crap: could not talloc_init()!\n"));
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ /* Setup crap */
+
+ if (state == NULL)
+ return WINBINDD_ERROR;
+
+ parse_domain_user(state->request.data.chauthtok.user, domain, user);
+
+ if (!(contact_domain = find_domain_from_name(domain))) {
+ DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
+ state->request.data.chauthtok.user, domain, user, domain));
+ result = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+
+ /* Change password */
+
+ oldpass = state->request.data.chauthtok.oldpass;
+ newpass = state->request.data.chauthtok.newpass;
+
+ /* Get sam handle */
+
+ if (!NT_STATUS_IS_OK(result = cm_get_sam_handle(contact_domain, &hnd)) ) {
+ DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
+ goto done;
+ }
+
+ result = cli_samr_chgpasswd_user(hnd->cli, mem_ctx, user, newpass, oldpass);
+
+done:
+ state->response.data.auth.nt_status = NT_STATUS_V(result);
+ fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
+ fstrcpy(state->response.data.auth.error_string, nt_errstr(result));
+ state->response.data.auth.pam_error = nt_status_to_pam(result);
+
+ DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
+ ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
+ domain,
+ user,
+ state->response.data.auth.nt_status_string,
+ state->response.data.auth.pam_error));
+
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+
+ return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
+}
diff --git a/source/nsswitch/winbindd_passdb.c b/source/nsswitch/winbindd_passdb.c
new file mode 100644
index 00000000000..36f5297efeb
--- /dev/null
+++ b/source/nsswitch/winbindd_passdb.c
@@ -0,0 +1,339 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind rpc backend functions
+
+ Copyright (C) Tim Potter 2000-2001,2003
+ Copyright (C) Simo Sorce 2003
+ Copyright (C) Volker Lendecke 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+static void
+add_member(const char *domain, const char *user,
+ char **members, int *num_members)
+{
+ fstring name;
+
+ fill_domain_username(name, domain, user);
+ safe_strcat(name, ",", sizeof(name)-1);
+ string_append(members, name);
+ *num_members += 1;
+}
+
+/**********************************************************************
+ Add member users resulting from sid. Expand if it is a domain group.
+**********************************************************************/
+
+static void
+add_expanded_sid(const DOM_SID *sid, char **members, int *num_members)
+{
+ DOM_SID dom_sid;
+ uint32 rid;
+ struct winbindd_domain *domain;
+ int i;
+
+ char *name = NULL;
+ enum SID_NAME_USE type;
+
+ uint32 num_names;
+ DOM_SID **sid_mem;
+ char **names;
+ uint32 *types;
+
+ NTSTATUS result;
+
+ TALLOC_CTX *mem_ctx = talloc_init("add_expanded_sid");
+
+ if (mem_ctx == NULL) {
+ DEBUG(1, ("talloc_init failed\n"));
+ return;
+ }
+
+ sid_copy(&dom_sid, sid);
+ sid_split_rid(&dom_sid, &rid);
+
+ domain = find_domain_from_sid(&dom_sid);
+
+ if (domain == NULL) {
+ DEBUG(3, ("Could not find domain for sid %s\n",
+ sid_string_static(sid)));
+ goto done;
+ }
+
+ result = domain->methods->sid_to_name(domain, mem_ctx, sid,
+ &name, &type);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(3, ("sid_to_name failed for sid %s\n",
+ sid_string_static(sid)));
+ goto done;
+ }
+
+ DEBUG(10, ("Found name %s, type %d\n", name, type));
+
+ if (type == SID_NAME_USER) {
+ add_member(domain->name, name, members, num_members);
+ goto done;
+ }
+
+ if (type != SID_NAME_DOM_GRP) {
+ DEBUG(10, ("Alias member %s neither user nor group, ignore\n",
+ name));
+ goto done;
+ }
+
+ /* Expand the domain group */
+
+ result = domain->methods->lookup_groupmem(domain, mem_ctx,
+ sid, &num_names,
+ &sid_mem, &names,
+ &types);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10, ("Could not lookup group members for %s: %s\n",
+ name, nt_errstr(result)));
+ goto done;
+ }
+
+ for (i=0; i<num_names; i++) {
+ DEBUG(10, ("Adding group member SID %s\n",
+ sid_string_static(sid_mem[i])));
+
+ if (types[i] != SID_NAME_USER) {
+ DEBUG(1, ("Hmmm. Member %s of group %s is no user. "
+ "Ignoring.\n", names[i], name));
+ continue;
+ }
+
+ add_member(domain->name, names[i], members, num_members);
+ }
+
+ done:
+ talloc_destroy(mem_ctx);
+ return;
+}
+
+BOOL fill_passdb_alias_grmem(struct winbindd_domain *domain,
+ DOM_SID *group_sid,
+ int *num_gr_mem, char **gr_mem, int *gr_mem_len)
+{
+ DOM_SID *members;
+ int i, num_members;
+
+ *num_gr_mem = 0;
+ *gr_mem = NULL;
+ *gr_mem_len = 0;
+
+ if (!pdb_enum_aliasmem(group_sid, &members, &num_members))
+ return True;
+
+ for (i=0; i<num_members; i++) {
+ add_expanded_sid(&members[i], gr_mem, num_gr_mem);
+ }
+
+ SAFE_FREE(members);
+
+ if (*gr_mem != NULL) {
+ int len;
+
+ /* We have at least one member, strip off the last "," */
+ len = strlen(*gr_mem);
+ (*gr_mem)[len-1] = '\0';
+ *gr_mem_len = len;
+ }
+
+ return True;
+}
+
+/* Query display info for a domain. This returns enough information plus a
+ bit extra to give an overview of domain users for the User Manager
+ application. */
+static NTSTATUS query_user_list(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ WINBIND_USERINFO **info)
+{
+ /* We don't have users */
+ *num_entries = 0;
+ *info = NULL;
+ return NT_STATUS_OK;
+}
+
+/* list all domain groups */
+static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info)
+{
+ /* We don't have domain groups */
+ *num_entries = 0;
+ *info = NULL;
+ return NT_STATUS_OK;
+}
+
+/* List all domain groups */
+
+static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info)
+{
+ struct acct_info *talloced_info;
+
+ /* Hmm. One billion aliases should be enough for a start */
+
+ if (!pdb_enum_aliases(&domain->sid, 0, 1000000000,
+ num_entries, info)) {
+ /* Nothing to report, just exit. */
+ return NT_STATUS_OK;
+ }
+
+ talloced_info = (struct acct_info *)
+ talloc_memdup(mem_ctx, *info,
+ *num_entries * sizeof(struct acct_info));
+
+ SAFE_FREE(*info);
+ *info = talloced_info;
+
+ return NT_STATUS_OK;
+}
+
+/* convert a single name to a sid in a domain */
+static NTSTATUS name_to_sid(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *name,
+ DOM_SID *sid,
+ enum SID_NAME_USE *type)
+{
+ DEBUG(10, ("Finding name %s\n", name));
+
+ if (!pdb_find_alias(name, sid))
+ return NT_STATUS_NONE_MAPPED;
+
+ *type = SID_NAME_ALIAS;
+ return NT_STATUS_OK;
+}
+
+/*
+ convert a domain SID to a user or group name
+*/
+static NTSTATUS sid_to_name(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *sid,
+ char **name,
+ enum SID_NAME_USE *type)
+{
+ struct acct_info info;
+
+ DEBUG(10, ("Converting SID %s\n", sid_string_static(sid)));
+
+ if (!pdb_get_aliasinfo(sid, &info))
+ return NT_STATUS_NONE_MAPPED;
+
+ *name = talloc_strdup(mem_ctx, info.acct_name);
+ *type = SID_NAME_ALIAS;
+
+ return NT_STATUS_OK;
+}
+
+/* Lookup user information from a rid or username. */
+static NTSTATUS query_user(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *user_sid,
+ WINBIND_USERINFO *user_info)
+{
+ return NT_STATUS_NO_SUCH_USER;
+}
+
+/* Lookup groups a user is a member of. I wish Unix had a call like this! */
+static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *user_sid,
+ uint32 *num_groups, DOM_SID ***user_gids)
+{
+ return NT_STATUS_NO_SUCH_USER;
+}
+
+
+/* Lookup group membership given a rid. */
+static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *group_sid, uint32 *num_names,
+ DOM_SID ***sid_mem, char ***names,
+ uint32 **name_types)
+{
+ return NT_STATUS_OK;
+}
+
+/* find the sequence number for a domain */
+static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
+{
+ *seq = 1;
+ return NT_STATUS_OK;
+}
+
+/* get a list of trusted domains */
+static NTSTATUS trusted_domains(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_domains,
+ char ***names,
+ char ***alt_names,
+ DOM_SID **dom_sids)
+{
+ return NT_STATUS_OK;
+}
+
+/* find the domain sid for a domain */
+static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
+{
+ sid_copy(sid, &domain->sid);
+ return NT_STATUS_OK;
+}
+
+/* find alternate names list for the domain
+ * should we look for netbios aliases??
+ SSS */
+static NTSTATUS alternate_name(struct winbindd_domain *domain)
+{
+ DEBUG(3,("pdb: alternate_name\n"));
+
+ return NT_STATUS_OK;
+}
+
+
+/* the rpc backend methods are exposed via this structure */
+struct winbindd_methods passdb_methods = {
+ False,
+ query_user_list,
+ enum_dom_groups,
+ enum_local_groups,
+ name_to_sid,
+ sid_to_name,
+ query_user,
+ lookup_usergroups,
+ lookup_groupmem,
+ sequence_number,
+ trusted_domains,
+ domain_sid,
+ alternate_name
+};
diff --git a/source/nsswitch/winbindd_rpc.c b/source/nsswitch/winbindd_rpc.c
new file mode 100644
index 00000000000..25d5f64df67
--- /dev/null
+++ b/source/nsswitch/winbindd_rpc.c
@@ -0,0 +1,1007 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind rpc backend functions
+
+ Copyright (C) Tim Potter 2000-2001,2003
+ Copyright (C) Andrew Tridgell 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+
+/* Query display info for a domain. This returns enough information plus a
+ bit extra to give an overview of domain users for the User Manager
+ application. */
+static NTSTATUS query_user_list(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ WINBIND_USERINFO **info)
+{
+ CLI_POLICY_HND *hnd;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ POLICY_HND dom_pol;
+ BOOL got_dom_pol = False;
+ uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+ unsigned int i, start_idx, retry;
+ uint32 loop_count;
+
+ DEBUG(3,("rpc: query_user_list\n"));
+
+ *num_entries = 0;
+ *info = NULL;
+
+ retry = 0;
+ do {
+ /* Get sam handle */
+
+ if ( !NT_STATUS_IS_OK(result = cm_get_sam_handle(domain, &hnd)) )
+ return result;
+
+ /* Get domain handle */
+
+ result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol,
+ des_access, &domain->sid, &dom_pol);
+
+ } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_dom_pol = True;
+
+ i = start_idx = 0;
+ loop_count = 0;
+
+ do {
+ TALLOC_CTX *ctx2;
+ uint32 num_dom_users, j;
+ uint32 max_entries, max_size;
+ SAM_DISPINFO_CTR ctr;
+ SAM_DISPINFO_1 info1;
+
+ ZERO_STRUCT( ctr );
+ ZERO_STRUCT( info1 );
+ ctr.sam.info1 = &info1;
+
+ if (!(ctx2 = talloc_init("winbindd enum_users"))) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ /* this next bit is copied from net_user_list_internal() */
+
+ get_query_dispinfo_params( loop_count, &max_entries, &max_size );
+
+ result = cli_samr_query_dispinfo(hnd->cli, mem_ctx, &dom_pol,
+ &start_idx, 1, &num_dom_users, max_entries, max_size, &ctr);
+
+ loop_count++;
+
+ *num_entries += num_dom_users;
+
+ *info = talloc_realloc( mem_ctx, *info,
+ (*num_entries) * sizeof(WINBIND_USERINFO));
+
+ if (!(*info)) {
+ result = NT_STATUS_NO_MEMORY;
+ talloc_destroy(ctx2);
+ goto done;
+ }
+
+ for (j = 0; j < num_dom_users; i++, j++) {
+ fstring username, fullname;
+ uint32 rid = ctr.sam.info1->sam[j].rid_user;
+
+ unistr2_to_ascii( username, &(&ctr.sam.info1->str[j])->uni_acct_name, sizeof(username)-1);
+ unistr2_to_ascii( fullname, &(&ctr.sam.info1->str[j])->uni_full_name, sizeof(fullname)-1);
+
+ (*info)[i].acct_name = talloc_strdup(mem_ctx, username );
+ (*info)[i].full_name = talloc_strdup(mem_ctx, fullname );
+ (*info)[i].user_sid = rid_to_talloced_sid(domain, mem_ctx, rid );
+
+ /* For the moment we set the primary group for
+ every user to be the Domain Users group.
+ There are serious problems with determining
+ the actual primary group for large domains.
+ This should really be made into a 'winbind
+ force group' smb.conf parameter or
+ something like that. */
+
+ (*info)[i].group_sid = rid_to_talloced_sid(domain,
+ mem_ctx, DOMAIN_GROUP_RID_USERS);
+ }
+
+ talloc_destroy(ctx2);
+
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+ done:
+
+ if (got_dom_pol)
+ cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+ return result;
+}
+
+/* list all domain groups */
+static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info)
+{
+ uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+ CLI_POLICY_HND *hnd;
+ POLICY_HND dom_pol;
+ NTSTATUS status;
+ uint32 start = 0;
+ int retry;
+ NTSTATUS result;
+
+ *num_entries = 0;
+ *info = NULL;
+
+ DEBUG(3,("rpc: enum_dom_groups\n"));
+
+ retry = 0;
+ do {
+ if (!NT_STATUS_IS_OK(result = cm_get_sam_handle(domain, &hnd)))
+ return result;
+
+ status = cli_samr_open_domain(hnd->cli, mem_ctx,
+ &hnd->pol, des_access, &domain->sid, &dom_pol);
+ } while (!NT_STATUS_IS_OK(status) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+ if (!NT_STATUS_IS_OK(status))
+ return status;
+
+ do {
+ struct acct_info *info2 = NULL;
+ uint32 count = 0;
+ TALLOC_CTX *mem_ctx2;
+
+ mem_ctx2 = talloc_init("enum_dom_groups[rpc]");
+
+ /* start is updated by this call. */
+ status = cli_samr_enum_dom_groups(hnd->cli, mem_ctx2, &dom_pol,
+ &start,
+ 0xFFFF, /* buffer size? */
+ &info2, &count);
+
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+ talloc_destroy(mem_ctx2);
+ break;
+ }
+
+ (*info) = talloc_realloc(mem_ctx, *info,
+ sizeof(**info) * ((*num_entries) + count));
+ if (! *info) {
+ talloc_destroy(mem_ctx2);
+ cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ memcpy(&(*info)[*num_entries], info2, count*sizeof(*info2));
+ (*num_entries) += count;
+ talloc_destroy(mem_ctx2);
+ } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES));
+
+ cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+ return status;
+}
+
+/* List all domain groups */
+
+static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info)
+{
+ uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+ CLI_POLICY_HND *hnd;
+ POLICY_HND dom_pol;
+ NTSTATUS result;
+ int retry;
+
+ *num_entries = 0;
+ *info = NULL;
+
+ retry = 0;
+ do {
+ if ( !NT_STATUS_IS_OK(result = cm_get_sam_handle(domain, &hnd)) )
+ return result;
+
+ result = cli_samr_open_domain( hnd->cli, mem_ctx, &hnd->pol,
+ des_access, &domain->sid, &dom_pol);
+ } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+ if ( !NT_STATUS_IS_OK(result))
+ return result;
+
+ do {
+ struct acct_info *info2 = NULL;
+ uint32 count = 0, start = *num_entries;
+ TALLOC_CTX *mem_ctx2;
+
+ mem_ctx2 = talloc_init("enum_dom_local_groups[rpc]");
+
+ result = cli_samr_enum_als_groups( hnd->cli, mem_ctx2, &dom_pol,
+ &start, 0xFFFF, &info2, &count);
+
+ if ( !NT_STATUS_IS_OK(result)
+ && !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES) )
+ {
+ talloc_destroy(mem_ctx2);
+ break;
+ }
+
+ (*info) = talloc_realloc(mem_ctx, *info,
+ sizeof(**info) * ((*num_entries) + count));
+ if (! *info) {
+ talloc_destroy(mem_ctx2);
+ cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ memcpy(&(*info)[*num_entries], info2, count*sizeof(*info2));
+ (*num_entries) += count;
+ talloc_destroy(mem_ctx2);
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+ cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+ return result;
+}
+
+/* convert a single name to a sid in a domain */
+static NTSTATUS name_to_sid(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *name,
+ DOM_SID *sid,
+ enum SID_NAME_USE *type)
+{
+ CLI_POLICY_HND *hnd;
+ NTSTATUS result;
+ DOM_SID *sids = NULL;
+ uint32 *types = NULL;
+ const char *full_name;
+ int retry;
+
+ DEBUG(3,("rpc: name_to_sid name=%s\n", name));
+
+ full_name = talloc_asprintf(mem_ctx, "%s\\%s", domain->name, name);
+
+ if (!full_name) {
+ DEBUG(0, ("talloc_asprintf failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DEBUG(3,("name_to_sid [rpc] %s for domain %s\n", name, domain->name ));
+
+ retry = 0;
+ do {
+ if (!NT_STATUS_IS_OK(result = cm_get_lsa_handle(domain, &hnd))) {
+ return result;
+ }
+
+ result = cli_lsa_lookup_names(hnd->cli, mem_ctx, &hnd->pol, 1,
+ &full_name, &sids, &types);
+ } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) &&
+ hnd && hnd->cli && hnd->cli->fd == -1);
+
+ /* Return rid and type if lookup successful */
+
+ if (NT_STATUS_IS_OK(result)) {
+ sid_copy(sid, &sids[0]);
+ *type = (enum SID_NAME_USE)types[0];
+ }
+
+ return result;
+}
+
+/*
+ convert a domain SID to a user or group name
+*/
+static NTSTATUS sid_to_name(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *sid,
+ char **name,
+ enum SID_NAME_USE *type)
+{
+ CLI_POLICY_HND *hnd;
+ char **domains;
+ char **names;
+ uint32 *types;
+ NTSTATUS result;
+ int retry;
+
+ DEBUG(3,("sid_to_name [rpc] %s for domain %s\n", sid_string_static(sid),
+ domain->name ));
+
+ retry = 0;
+ do {
+ if (!NT_STATUS_IS_OK(result = cm_get_lsa_handle(domain, &hnd)))
+ return result;
+
+ result = cli_lsa_lookup_sids(hnd->cli, mem_ctx, &hnd->pol,
+ 1, sid, &domains, &names, &types);
+ } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) &&
+ hnd && hnd->cli && hnd->cli->fd == -1);
+
+ if (NT_STATUS_IS_OK(result)) {
+ *type = (enum SID_NAME_USE)types[0];
+ *name = names[0];
+ DEBUG(5,("Mapped sid to [%s]\\[%s]\n", domains[0], *name));
+
+ /* Paranoia */
+ if (!strequal(domain->name, domains[0])) {
+ DEBUG(1, ("domain name from domain param and PDC lookup return differ! (%s vs %s)\n", domain->name, domains[0]));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ return result;
+}
+
+/* Lookup user information from a rid or username. */
+static NTSTATUS query_user(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *user_sid,
+ WINBIND_USERINFO *user_info)
+{
+ CLI_POLICY_HND *hnd = NULL;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ POLICY_HND dom_pol, user_pol;
+ BOOL got_dom_pol = False, got_user_pol = False;
+ SAM_USERINFO_CTR *ctr;
+ int retry;
+ fstring sid_string;
+ uint32 user_rid;
+ NET_USER_INFO_3 *user;
+
+ DEBUG(3,("rpc: query_user rid=%s\n", sid_to_string(sid_string, user_sid)));
+ if (!sid_peek_check_rid(&domain->sid, user_sid, &user_rid)) {
+ goto done;
+ }
+
+ /* try netsamlogon cache first */
+
+ if ( (user = netsamlogon_cache_get( mem_ctx, user_sid )) != NULL )
+ {
+
+ DEBUG(5,("query_user: Cache lookup succeeded for %s\n",
+ sid_string_static(user_sid)));
+
+ user_info->user_sid = rid_to_talloced_sid( domain, mem_ctx, user_rid );
+ user_info->group_sid = rid_to_talloced_sid( domain, mem_ctx, user->group_rid );
+
+ user_info->acct_name = unistr2_tdup(mem_ctx, &user->uni_user_name);
+ user_info->full_name = unistr2_tdup(mem_ctx, &user->uni_full_name);
+
+ SAFE_FREE(user);
+
+ return NT_STATUS_OK;
+ }
+
+ /* no cache; hit the wire */
+
+ retry = 0;
+ do {
+ /* Get sam handle; if we fail here there is no hope */
+
+ if (!NT_STATUS_IS_OK(result = cm_get_sam_handle(domain, &hnd)))
+ goto done;
+
+ /* Get domain handle */
+
+ result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &domain->sid, &dom_pol);
+ } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) &&
+ hnd && hnd->cli && hnd->cli->fd == -1);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_dom_pol = True;
+
+ /* Get user handle */
+ result = cli_samr_open_user(hnd->cli, mem_ctx, &dom_pol,
+ SEC_RIGHTS_MAXIMUM_ALLOWED, user_rid, &user_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_user_pol = True;
+
+ /* Get user info */
+ result = cli_samr_query_userinfo(hnd->cli, mem_ctx, &user_pol,
+ 0x15, &ctr);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ cli_samr_close(hnd->cli, mem_ctx, &user_pol);
+ got_user_pol = False;
+
+ user_info->user_sid = rid_to_talloced_sid(domain, mem_ctx, user_rid);
+ user_info->group_sid = rid_to_talloced_sid(domain, mem_ctx, ctr->info.id21->group_rid);
+ user_info->acct_name = unistr2_tdup(mem_ctx,
+ &ctr->info.id21->uni_user_name);
+ user_info->full_name = unistr2_tdup(mem_ctx,
+ &ctr->info.id21->uni_full_name);
+
+ done:
+ /* Clean up policy handles */
+ if (got_user_pol)
+ cli_samr_close(hnd->cli, mem_ctx, &user_pol);
+
+ if (got_dom_pol)
+ cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+ return result;
+}
+
+/* Lookup groups a user is a member of. I wish Unix had a call like this! */
+static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *user_sid,
+ uint32 *num_groups, DOM_SID ***user_grpsids)
+{
+ CLI_POLICY_HND *hnd;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ POLICY_HND dom_pol, user_pol;
+ uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+ BOOL got_dom_pol = False, got_user_pol = False;
+ DOM_GID *user_groups;
+ unsigned int i;
+ unsigned int retry;
+ fstring sid_string;
+ uint32 user_rid;
+ NET_USER_INFO_3 *user;
+
+ DEBUG(3,("rpc: lookup_usergroups sid=%s\n", sid_to_string(sid_string, user_sid)));
+
+ *num_groups = 0;
+ *user_grpsids = NULL;
+
+ /* so lets see if we have a cached user_info_3 */
+
+ if ( (user = netsamlogon_cache_get( mem_ctx, user_sid )) != NULL )
+ {
+ DEBUG(5,("query_user: Cache lookup succeeded for %s\n",
+ sid_string_static(user_sid)));
+
+ *num_groups = user->num_groups;
+
+ (*user_grpsids) = talloc(mem_ctx, sizeof(DOM_SID*) * (*num_groups));
+ for (i=0;i<(*num_groups);i++) {
+ (*user_grpsids)[i] = rid_to_talloced_sid(domain, mem_ctx, user->gids[i].g_rid);
+ }
+
+ SAFE_FREE(user);
+
+ return NT_STATUS_OK;
+ }
+
+ /* no cache; hit the wire */
+
+ retry = 0;
+ do {
+ /* Get sam handle; if we fail here there is no hope */
+
+ if (!NT_STATUS_IS_OK(result = cm_get_sam_handle(domain, &hnd)))
+ goto done;
+
+ /* Get domain handle */
+
+ result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol,
+ des_access, &domain->sid, &dom_pol);
+ } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) &&
+ hnd && hnd->cli && hnd->cli->fd == -1);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_dom_pol = True;
+
+
+ if (!sid_peek_check_rid(&domain->sid, user_sid, &user_rid)) {
+ goto done;
+ }
+
+ /* Get user handle */
+ result = cli_samr_open_user(hnd->cli, mem_ctx, &dom_pol,
+ des_access, user_rid, &user_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_user_pol = True;
+
+ /* Query user rids */
+ result = cli_samr_query_usergroups(hnd->cli, mem_ctx, &user_pol,
+ num_groups, &user_groups);
+
+ if (!NT_STATUS_IS_OK(result) || (*num_groups) == 0)
+ goto done;
+
+ (*user_grpsids) = talloc(mem_ctx, sizeof(DOM_SID*) * (*num_groups));
+ if (!(*user_grpsids)) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ for (i=0;i<(*num_groups);i++) {
+ (*user_grpsids)[i] = rid_to_talloced_sid(domain, mem_ctx, user_groups[i].g_rid);
+ }
+
+ done:
+ /* Clean up policy handles */
+ if (got_user_pol)
+ cli_samr_close(hnd->cli, mem_ctx, &user_pol);
+
+ if (got_dom_pol)
+ cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+ return result;
+}
+
+
+/* Lookup group membership given a rid. */
+static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *group_sid, uint32 *num_names,
+ DOM_SID ***sid_mem, char ***names,
+ uint32 **name_types)
+{
+ CLI_POLICY_HND *hnd = NULL;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 i, total_names = 0;
+ POLICY_HND dom_pol, group_pol;
+ uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+ BOOL got_dom_pol = False, got_group_pol = False;
+ uint32 *rid_mem = NULL;
+ uint32 group_rid;
+ int retry;
+ unsigned int j;
+ fstring sid_string;
+
+ DEBUG(10,("rpc: lookup_groupmem %s sid=%s\n", domain->name, sid_to_string(sid_string, group_sid)));
+
+ if (!sid_peek_check_rid(&domain->sid, group_sid, &group_rid)) {
+ goto done;
+ }
+
+ *num_names = 0;
+
+ retry = 0;
+ do {
+ /* Get sam handle */
+ if (!NT_STATUS_IS_OK(result = cm_get_sam_handle(domain, &hnd)))
+ goto done;
+
+ /* Get domain handle */
+
+ result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol,
+ des_access, &domain->sid, &dom_pol);
+ } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_dom_pol = True;
+
+ /* Get group handle */
+
+ result = cli_samr_open_group(hnd->cli, mem_ctx, &dom_pol,
+ des_access, group_rid, &group_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_group_pol = True;
+
+ /* Step #1: Get a list of user rids that are the members of the
+ group. */
+
+ result = cli_samr_query_groupmem(hnd->cli, mem_ctx,
+ &group_pol, num_names, &rid_mem,
+ name_types);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ if (!*num_names) {
+ names = NULL;
+ name_types = NULL;
+ sid_mem = NULL;
+ goto done;
+ }
+
+ /* Step #2: Convert list of rids into list of usernames. Do this
+ in bunches of ~1000 to avoid crashing NT4. It looks like there
+ is a buffer overflow or something like that lurking around
+ somewhere. */
+
+#define MAX_LOOKUP_RIDS 900
+
+ *names = talloc_zero(mem_ctx, *num_names * sizeof(char *));
+ *name_types = talloc_zero(mem_ctx, *num_names * sizeof(uint32));
+ *sid_mem = talloc_zero(mem_ctx, *num_names * sizeof(DOM_SID *));
+
+ for (j=0;j<(*num_names);j++) {
+ (*sid_mem)[j] = rid_to_talloced_sid(domain, mem_ctx, (rid_mem)[j]);
+ }
+
+ if (*num_names>0 && (!*names || !*name_types)) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ for (i = 0; i < *num_names; i += MAX_LOOKUP_RIDS) {
+ int num_lookup_rids = MIN(*num_names - i, MAX_LOOKUP_RIDS);
+ uint32 tmp_num_names = 0;
+ char **tmp_names = NULL;
+ uint32 *tmp_types = NULL;
+
+ /* Lookup a chunk of rids */
+
+ result = cli_samr_lookup_rids(hnd->cli, mem_ctx,
+ &dom_pol, 1000, /* flags */
+ num_lookup_rids,
+ &rid_mem[i],
+ &tmp_num_names,
+ &tmp_names, &tmp_types);
+
+ /* see if we have a real error (and yes the STATUS_SOME_UNMAPPED is
+ the one returned from 2k) */
+
+ if (!NT_STATUS_IS_OK(result) && NT_STATUS_V(result) != NT_STATUS_V(STATUS_SOME_UNMAPPED))
+ goto done;
+
+ /* Copy result into array. The talloc system will take
+ care of freeing the temporary arrays later on. */
+
+ memcpy(&(*names)[i], tmp_names, sizeof(char *) *
+ tmp_num_names);
+
+ memcpy(&(*name_types)[i], tmp_types, sizeof(uint32) *
+ tmp_num_names);
+
+ total_names += tmp_num_names;
+ }
+
+ *num_names = total_names;
+
+ result = NT_STATUS_OK;
+
+done:
+ if (got_group_pol)
+ cli_samr_close(hnd->cli, mem_ctx, &group_pol);
+
+ if (got_dom_pol)
+ cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+ return result;
+}
+
+#ifdef HAVE_LDAP
+
+#include <ldap.h>
+
+static SIG_ATOMIC_T gotalarm;
+
+/***************************************************************
+ Signal function to tell us we timed out.
+****************************************************************/
+
+static void gotalarm_sig(void)
+{
+ gotalarm = 1;
+}
+
+static LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
+{
+ LDAP *ldp = NULL;
+
+ /* Setup timeout */
+ gotalarm = 0;
+ CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
+ alarm(to);
+ /* End setup timeout. */
+
+ ldp = ldap_open(server, port);
+
+ /* Teardown timeout. */
+ CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
+ alarm(0);
+
+ return ldp;
+}
+
+static int get_ldap_seq(const char *server, int port, uint32 *seq)
+{
+ int ret = -1;
+ struct timeval to;
+ char *attrs[] = {"highestCommittedUSN", NULL};
+ LDAPMessage *res = NULL;
+ char **values = NULL;
+ LDAP *ldp = NULL;
+
+ *seq = DOM_SEQUENCE_NONE;
+
+ /*
+ * 10 second timeout on open. This is needed as the search timeout
+ * doesn't seem to apply to doing an open as well. JRA.
+ */
+
+ if ((ldp = ldap_open_with_timeout(server, port, 10)) == NULL)
+ return -1;
+
+ /* Timeout if no response within 20 seconds. */
+ to.tv_sec = 10;
+ to.tv_usec = 0;
+
+ if (ldap_search_st(ldp, "", LDAP_SCOPE_BASE, "(objectclass=*)", &attrs[0], 0, &to, &res))
+ goto done;
+
+ if (ldap_count_entries(ldp, res) != 1)
+ goto done;
+
+ values = ldap_get_values(ldp, res, "highestCommittedUSN");
+ if (!values || !values[0])
+ goto done;
+
+ *seq = atoi(values[0]);
+ ret = 0;
+
+ done:
+
+ if (values)
+ ldap_value_free(values);
+ if (res)
+ ldap_msgfree(res);
+ if (ldp)
+ ldap_unbind(ldp);
+ return ret;
+}
+
+/**********************************************************************
+ Get the sequence number for a Windows AD native mode domain using
+ LDAP queries
+**********************************************************************/
+
+static int get_ldap_sequence_number( const char* domain, uint32 *seq)
+{
+ int ret = -1;
+ int i, port = LDAP_PORT;
+ struct ip_service *ip_list = NULL;
+ int count;
+
+ if ( !get_sorted_dc_list(domain, &ip_list, &count, False) ) {
+ DEBUG(3, ("Could not look up dc's for domain %s\n", domain));
+ return False;
+ }
+
+ /* Finally return first DC that we can contact */
+
+ for (i = 0; i < count; i++) {
+ fstring ipstr;
+
+ /* since the is an LDAP lookup, default to the LDAP_PORT is not set */
+ port = (ip_list[i].port!= PORT_NONE) ? ip_list[i].port : LDAP_PORT;
+
+ fstrcpy( ipstr, inet_ntoa(ip_list[i].ip) );
+
+ if (is_zero_ip(ip_list[i].ip))
+ continue;
+
+ if ( (ret = get_ldap_seq( ipstr, port, seq)) == 0 )
+ goto done;
+
+ /* add to failed connection cache */
+ add_failed_connection_entry( domain, ipstr, NT_STATUS_UNSUCCESSFUL );
+ }
+
+done:
+ if ( ret == 0 ) {
+ DEBUG(3, ("get_ldap_sequence_number: Retrieved sequence number for Domain (%s) from DC (%s:%d)\n",
+ domain, inet_ntoa(ip_list[i].ip), port));
+ }
+
+ SAFE_FREE(ip_list);
+
+ return ret;
+}
+
+#endif /* HAVE_LDAP */
+
+/* find the sequence number for a domain */
+static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
+{
+ TALLOC_CTX *mem_ctx;
+ CLI_POLICY_HND *hnd;
+ SAM_UNK_CTR ctr;
+ uint16 switch_value = 2;
+ NTSTATUS result;
+ POLICY_HND dom_pol;
+ BOOL got_dom_pol = False;
+ uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+ int retry;
+
+ DEBUG(10,("rpc: fetch sequence_number for %s\n", domain->name));
+
+ *seq = DOM_SEQUENCE_NONE;
+
+ if (!(mem_ctx = talloc_init("sequence_number[rpc]")))
+ return NT_STATUS_NO_MEMORY;
+
+ retry = 0;
+ do {
+#ifdef HAVE_LDAP
+ if ( domain->native_mode )
+ {
+ DEBUG(8,("using get_ldap_seq() to retrieve the sequence number\n"));
+
+ if ( get_ldap_sequence_number( domain->name, seq ) == 0 ) {
+ result = NT_STATUS_OK;
+ DEBUG(10,("domain_sequence_number: LDAP for domain %s is %u\n",
+ domain->name, *seq));
+ goto done;
+ }
+
+ DEBUG(10,("domain_sequence_number: failed to get LDAP sequence number for domain %s\n",
+ domain->name ));
+ }
+#endif /* HAVE_LDAP */
+ /* Get sam handle */
+ if (!NT_STATUS_IS_OK(result = cm_get_sam_handle(domain, &hnd)))
+ goto done;
+
+ /* Get domain handle */
+ result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol,
+ des_access, &domain->sid, &dom_pol);
+ } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_dom_pol = True;
+
+ /* Query domain info */
+
+ result = cli_samr_query_dom_info(hnd->cli, mem_ctx, &dom_pol,
+ switch_value, &ctr);
+
+ if (NT_STATUS_IS_OK(result)) {
+ *seq = ctr.info.inf2.seq_num;
+ DEBUG(10,("domain_sequence_number: for domain %s is %u\n", domain->name, (unsigned)*seq));
+ } else {
+ DEBUG(10,("domain_sequence_number: failed to get sequence number (%u) for domain %s\n",
+ (unsigned)*seq, domain->name ));
+ }
+
+ done:
+
+ if (got_dom_pol)
+ cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+/* get a list of trusted domains */
+static NTSTATUS trusted_domains(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_domains,
+ char ***names,
+ char ***alt_names,
+ DOM_SID **dom_sids)
+{
+ CLI_POLICY_HND *hnd;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 enum_ctx = 0;
+ int retry;
+
+ DEBUG(3,("rpc: trusted_domains\n"));
+
+ *num_domains = 0;
+ *alt_names = NULL;
+
+ retry = 0;
+ do {
+ if (!NT_STATUS_IS_OK(result = cm_get_lsa_handle(find_our_domain(), &hnd)))
+ goto done;
+
+ result = cli_lsa_enum_trust_dom(hnd->cli, mem_ctx,
+ &hnd->pol, &enum_ctx,
+ num_domains, names, dom_sids);
+ } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+done:
+ return result;
+}
+
+/* find the domain sid for a domain */
+static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ TALLOC_CTX *mem_ctx;
+ CLI_POLICY_HND *hnd;
+ char *level5_dom;
+ DOM_SID *alloc_sid;
+ int retry;
+
+ DEBUG(3,("rpc: domain_sid\n"));
+
+ if (!(mem_ctx = talloc_init("domain_sid[rpc]")))
+ return NT_STATUS_NO_MEMORY;
+
+ retry = 0;
+ do {
+ /* Get lsa handle */
+ if (!NT_STATUS_IS_OK(result = cm_get_lsa_handle(domain, &hnd)))
+ goto done;
+
+ result = cli_lsa_query_info_policy(hnd->cli, mem_ctx,
+ &hnd->pol, 0x05, &level5_dom, &alloc_sid);
+ } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+ if (NT_STATUS_IS_OK(result)) {
+ if (alloc_sid) {
+ sid_copy(sid, alloc_sid);
+ } else {
+ result = NT_STATUS_NO_MEMORY;
+ }
+ }
+
+done:
+ talloc_destroy(mem_ctx);
+ return result;
+}
+
+/* find alternate names list for the domain - none for rpc */
+static NTSTATUS alternate_name(struct winbindd_domain *domain)
+{
+ return NT_STATUS_OK;
+}
+
+
+/* the rpc backend methods are exposed via this structure */
+struct winbindd_methods msrpc_methods = {
+ False,
+ query_user_list,
+ enum_dom_groups,
+ enum_local_groups,
+ name_to_sid,
+ sid_to_name,
+ query_user,
+ lookup_usergroups,
+ lookup_groupmem,
+ sequence_number,
+ trusted_domains,
+ domain_sid,
+ alternate_name
+};
diff --git a/source/nsswitch/winbindd_sid.c b/source/nsswitch/winbindd_sid.c
new file mode 100644
index 00000000000..d4206558c5e
--- /dev/null
+++ b/source/nsswitch/winbindd_sid.c
@@ -0,0 +1,456 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon - sid related functions
+
+ Copyright (C) Tim Potter 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* Convert a string */
+
+enum winbindd_result winbindd_lookupsid(struct winbindd_cli_state *state)
+{
+ enum SID_NAME_USE type;
+ DOM_SID sid;
+ fstring name;
+ fstring dom_name;
+
+ /* Ensure null termination */
+ state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
+
+ DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state->pid,
+ state->request.data.sid));
+
+ /* Lookup sid from PDC using lsa_lookup_sids() */
+
+ if (!string_to_sid(&sid, state->request.data.sid)) {
+ DEBUG(5, ("%s not a SID\n", state->request.data.sid));
+ return WINBINDD_ERROR;
+ }
+
+ /* Lookup the sid */
+
+ if (!winbindd_lookup_name_by_sid(&sid, dom_name, name, &type)) {
+ return WINBINDD_ERROR;
+ }
+
+ fstrcpy(state->response.data.name.dom_name, dom_name);
+ fstrcpy(state->response.data.name.name, name);
+
+ state->response.data.name.type = type;
+
+ return WINBINDD_OK;
+}
+
+
+/**
+ * Look up the SID for a qualified name.
+ **/
+enum winbindd_result winbindd_lookupname(struct winbindd_cli_state *state)
+{
+ enum SID_NAME_USE type;
+ fstring sid_str;
+ char *name_domain, *name_user;
+ DOM_SID sid;
+ struct winbindd_domain *domain;
+ char *p;
+
+ /* Ensure null termination */
+ state->request.data.sid[sizeof(state->request.data.name.dom_name)-1]='\0';
+
+ /* Ensure null termination */
+ state->request.data.sid[sizeof(state->request.data.name.name)-1]='\0';
+
+ /* cope with the name being a fully qualified name */
+ p = strstr(state->request.data.name.name, lp_winbind_separator());
+ if (p) {
+ *p = 0;
+ name_domain = state->request.data.name.name;
+ name_user = p+1;
+ } else {
+ name_domain = state->request.data.name.dom_name;
+ name_user = state->request.data.name.name;
+ }
+
+ DEBUG(3, ("[%5lu]: lookupname %s%s%s\n", (unsigned long)state->pid,
+ name_domain, lp_winbind_separator(), name_user));
+
+ if ((domain = find_domain_from_name(name_domain)) == NULL) {
+ DEBUG(0, ("could not find domain entry for domain %s\n",
+ name_domain));
+ return WINBINDD_ERROR;
+ }
+
+ /* Lookup name from PDC using lsa_lookup_names() */
+ if (!winbindd_lookup_sid_by_name(domain, name_user, &sid, &type)) {
+ return WINBINDD_ERROR;
+ }
+
+ sid_to_string(sid_str, &sid);
+ fstrcpy(state->response.data.sid.sid, sid_str);
+ state->response.data.sid.type = type;
+
+ return WINBINDD_OK;
+}
+
+/* Convert a sid to a uid. We assume we only have one rid attached to the
+ sid. */
+
+enum winbindd_result winbindd_sid_to_uid(struct winbindd_cli_state *state)
+{
+ DOM_SID sid;
+ uint32 flags = 0x0;
+
+ /* Ensure null termination */
+ state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
+
+ DEBUG(3, ("[%5lu]: sid to uid %s\n", (unsigned long)state->pid,
+ state->request.data.sid));
+
+ if (!string_to_sid(&sid, state->request.data.sid)) {
+ DEBUG(1, ("Could not get convert sid %s from string\n", state->request.data.sid));
+ return WINBINDD_ERROR;
+ }
+
+ /* This gets a little tricky. If we assume that usernames are syncd between
+ /etc/passwd and the windows domain (such as a member of a Samba domain),
+ the we need to get the uid from the OS and not alocate one ourselves */
+
+ if ( lp_winbind_trusted_domains_only() ) {
+ struct winbindd_domain *domain = NULL;
+ DOM_SID sid2;
+ uint32 rid;
+
+ domain = find_our_domain();
+ if ( !domain ) {
+ DEBUG(0,("winbindd_sid_to_uid: can't find my own domain!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ sid_copy( &sid2, &sid );
+ sid_split_rid( &sid2, &rid );
+
+ if ( sid_equal( &sid2, &domain->sid ) ) {
+
+ fstring domain_name;
+ fstring user;
+ enum SID_NAME_USE type;
+ struct passwd *pw = NULL;
+ unid_t id;
+
+ /* ok...here's we know that we are dealing with our
+ own domain (the one to which we are joined). And
+ we know that there must be a UNIX account for this user.
+ So we lookup the sid and the call getpwnam().*/
+
+
+ /* But first check and see if we don't already have a mapping */
+
+ flags = ID_QUERY_ONLY;
+ if ( NT_STATUS_IS_OK(idmap_sid_to_uid(&sid, &(state->response.data.uid), flags)) )
+ return WINBINDD_OK;
+
+ /* now fall back to the hard way */
+
+ if ( !winbindd_lookup_name_by_sid(&sid, domain_name, user, &type) )
+ return WINBINDD_ERROR;
+
+ if ( !(pw = getpwnam(user)) ) {
+ DEBUG(0,("winbindd_sid_to_uid: 'winbind trusted domains only' is "
+ "set but this user [%s] doesn't exist!\n", user));
+ return WINBINDD_ERROR;
+ }
+
+ state->response.data.uid = pw->pw_uid;
+
+ id.uid = pw->pw_uid;
+ idmap_set_mapping( &sid, id, ID_USERID );
+
+ return WINBINDD_OK;
+ }
+
+ }
+
+ if ( state->request.flags & WBFLAG_QUERY_ONLY )
+ flags = ID_QUERY_ONLY;
+
+ /* Find uid for this sid and return it */
+
+ if ( !NT_STATUS_IS_OK(idmap_sid_to_uid(&sid, &(state->response.data.uid), flags)) ) {
+ DEBUG(1, ("Could not get uid for sid %s\n", state->request.data.sid));
+ return WINBINDD_ERROR;
+ }
+
+ return WINBINDD_OK;
+}
+
+/* Convert a sid to a gid. We assume we only have one rid attached to the
+ sid.*/
+
+enum winbindd_result winbindd_sid_to_gid(struct winbindd_cli_state *state)
+{
+ DOM_SID sid;
+ uint32 flags = 0x0;
+
+ /* Ensure null termination */
+ state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
+
+ DEBUG(3, ("[%5lu]: sid to gid %s\n", (unsigned long)state->pid,
+ state->request.data.sid));
+
+ if (!string_to_sid(&sid, state->request.data.sid)) {
+ DEBUG(1, ("Could not cvt string to sid %s\n", state->request.data.sid));
+ return WINBINDD_ERROR;
+ }
+
+ /* This gets a little tricky. If we assume that usernames are syncd between
+ /etc/passwd and the windows domain (such as a member of a Samba domain),
+ the we need to get the uid from the OS and not alocate one ourselves */
+
+ if ( lp_winbind_trusted_domains_only() ) {
+ struct winbindd_domain *domain = NULL;
+ DOM_SID sid2;
+ uint32 rid;
+ unid_t id;
+
+ domain = find_our_domain();
+ if ( !domain ) {
+ DEBUG(0,("winbindd_sid_to_uid: can't find my own domain!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ sid_copy( &sid2, &sid );
+ sid_split_rid( &sid2, &rid );
+
+ if ( sid_equal( &sid2, &domain->sid ) ) {
+
+ fstring domain_name;
+ fstring group;
+ enum SID_NAME_USE type;
+ struct group *grp = NULL;
+
+ /* ok...here's we know that we are dealing with our
+ own domain (the one to which we are joined). And
+ we know that there must be a UNIX account for this group.
+ So we lookup the sid and the call getpwnam().*/
+
+ /* But first check and see if we don't already have a mapping */
+
+ flags = ID_QUERY_ONLY;
+ if ( NT_STATUS_IS_OK(idmap_sid_to_gid(&sid, &(state->response.data.gid), flags)) )
+ return WINBINDD_OK;
+
+ /* now fall back to the hard way */
+
+ if ( !winbindd_lookup_name_by_sid(&sid, domain_name, group, &type) )
+ return WINBINDD_ERROR;
+
+ if ( !(grp = getgrnam(group)) ) {
+ DEBUG(0,("winbindd_sid_to_uid: 'winbind trusted domains only' is "
+ "set but this group [%s] doesn't exist!\n", group));
+ return WINBINDD_ERROR;
+ }
+
+ state->response.data.gid = grp->gr_gid;
+
+ id.gid = grp->gr_gid;
+ idmap_set_mapping( &sid, id, ID_GROUPID );
+
+ return WINBINDD_OK;
+ }
+
+ }
+
+ if ( state->request.flags & WBFLAG_QUERY_ONLY )
+ flags = ID_QUERY_ONLY;
+
+ /* Find gid for this sid and return it */
+ if ( !NT_STATUS_IS_OK(idmap_sid_to_gid(&sid, &(state->response.data.gid), flags)) ) {
+ DEBUG(1, ("Could not get gid for sid %s\n", state->request.data.sid));
+ return WINBINDD_ERROR;
+ }
+
+ return WINBINDD_OK;
+}
+
+/* Convert a uid to a sid */
+
+enum winbindd_result winbindd_uid_to_sid(struct winbindd_cli_state *state)
+{
+ DOM_SID sid;
+
+ DEBUG(3, ("[%5lu]: uid to sid %lu\n", (unsigned long)state->pid,
+ (unsigned long)state->request.data.uid));
+
+ if ( (state->request.data.uid < server_state.uid_low )
+ || (state->request.data.uid > server_state.uid_high) )
+ {
+ struct passwd *pw = NULL;
+ enum SID_NAME_USE type;
+ unid_t id;
+ struct winbindd_domain *domain;
+
+ /* SPECIAL CASE FOR MEMBERS OF SAMBA DOMAINS */
+
+ /* if we don't trust /etc/password then when can't know
+ anything about this uid */
+
+ if ( !lp_winbind_trusted_domains_only() )
+ return WINBINDD_ERROR;
+
+
+ /* look for an idmap entry first */
+
+ if ( NT_STATUS_IS_OK(idmap_uid_to_sid(&sid, state->request.data.uid)) )
+ goto done;
+
+ /* if users exist in /etc/passwd, we should try to
+ use that uid. Get the username and the lookup the SID */
+
+ if ( !(pw = getpwuid(state->request.data.uid)) )
+ return WINBINDD_ERROR;
+
+ if ( !(domain = find_our_domain()) ) {
+ DEBUG(0,("winbindd_uid_to_sid: can't find my own domain!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ if ( !winbindd_lookup_sid_by_name(domain, pw->pw_name, &sid, &type) )
+ return WINBINDD_ERROR;
+
+ if ( type != SID_NAME_USER )
+ return WINBINDD_ERROR;
+
+ /* don't fail if we can't store it */
+
+ id.uid = pw->pw_uid;
+ idmap_set_mapping( &sid, id, ID_USERID );
+
+ goto done;
+ }
+
+ /* Lookup rid for this uid */
+
+ if (!NT_STATUS_IS_OK(idmap_uid_to_sid(&sid, state->request.data.uid))) {
+ DEBUG(1, ("Could not convert uid %lu to rid\n",
+ (unsigned long)state->request.data.uid));
+ return WINBINDD_ERROR;
+ }
+
+done:
+ sid_to_string(state->response.data.sid.sid, &sid);
+ state->response.data.sid.type = SID_NAME_USER;
+
+ return WINBINDD_OK;
+}
+
+/* Convert a gid to a sid */
+
+enum winbindd_result winbindd_gid_to_sid(struct winbindd_cli_state *state)
+{
+ DOM_SID sid;
+
+ DEBUG(3, ("[%5lu]: gid to sid %lu\n", (unsigned long)state->pid,
+ (unsigned long)state->request.data.gid));
+
+ if ( (state->request.data.gid < server_state.gid_low)
+ || (state->request.data.gid > server_state.gid_high) )
+ {
+ struct group *grp = NULL;
+ enum SID_NAME_USE type;
+ unid_t id;
+ struct winbindd_domain *domain;
+
+ /* SPECIAL CASE FOR MEMBERS OF SAMBA DOMAINS */
+
+ /* if we don't trust /etc/group then when can't know
+ anything about this gid */
+
+ if ( !lp_winbind_trusted_domains_only() )
+ return WINBINDD_ERROR;
+
+ /* look for an idmap entry first */
+
+ if ( NT_STATUS_IS_OK(idmap_gid_to_sid(&sid, state->request.data.gid)) )
+ goto done;
+
+ /* if users exist in /etc/group, we should try to
+ use that gid. Get the username and the lookup the SID */
+
+ if ( !(grp = getgrgid(state->request.data.gid)) )
+ return WINBINDD_ERROR;
+
+ if ( !(domain = find_our_domain()) ) {
+ DEBUG(0,("winbindd_uid_to_sid: can't find my own domain!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ if ( !winbindd_lookup_sid_by_name(domain, grp->gr_name, &sid, &type) )
+ return WINBINDD_ERROR;
+
+ if ( type!=SID_NAME_DOM_GRP && type!=SID_NAME_ALIAS )
+ return WINBINDD_ERROR;
+
+ /* don't fail if we can't store it */
+
+ id.gid = grp->gr_gid;
+ idmap_set_mapping( &sid, id, ID_GROUPID );
+
+ goto done;
+ }
+
+ /* Lookup sid for this uid */
+
+ if (!NT_STATUS_IS_OK(idmap_gid_to_sid(&sid, state->request.data.gid))) {
+ DEBUG(1, ("Could not convert gid %lu to sid\n",
+ (unsigned long)state->request.data.gid));
+ return WINBINDD_ERROR;
+ }
+
+done:
+ /* Construct sid and return it */
+ sid_to_string(state->response.data.sid.sid, &sid);
+ state->response.data.sid.type = SID_NAME_DOM_GRP;
+
+ return WINBINDD_OK;
+}
+
+enum winbindd_result winbindd_allocate_rid(struct winbindd_cli_state *state)
+{
+ if ( !state->privileged ) {
+ DEBUG(2, ("winbindd_allocate_rid: non-privileged access "
+ "denied!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* We tell idmap to always allocate a user RID. There might be a good
+ * reason to keep RID allocation for users to even and groups to
+ * odd. This needs discussion I think. For now only allocate user
+ * rids. */
+
+ if (!NT_STATUS_IS_OK(idmap_allocate_rid(&state->response.data.rid,
+ USER_RID_TYPE)))
+ return WINBINDD_ERROR;
+
+ return WINBINDD_OK;
+}
diff --git a/source/nsswitch/winbindd_user.c b/source/nsswitch/winbindd_user.c
new file mode 100644
index 00000000000..d08377c888f
--- /dev/null
+++ b/source/nsswitch/winbindd_user.c
@@ -0,0 +1,667 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon - user related functions
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jeremy Allison 2001.
+ Copyright (C) Gerald (Jerry) Carter 2003.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+extern userdom_struct current_user_info;
+
+/* Fill a pwent structure with information we have obtained */
+
+static BOOL winbindd_fill_pwent(char *dom_name, char *user_name,
+ DOM_SID *user_sid, DOM_SID *group_sid,
+ char *full_name, struct winbindd_pw *pw)
+{
+ fstring output_username;
+ char *homedir;
+ char *shell;
+ fstring sid_string;
+
+ if (!pw || !dom_name || !user_name)
+ return False;
+
+ /* Resolve the uid number */
+
+ if (!NT_STATUS_IS_OK(idmap_sid_to_uid(user_sid, &(pw->pw_uid), 0))) {
+ DEBUG(1, ("error getting user id for sid %s\n", sid_to_string(sid_string, user_sid)));
+ return False;
+ }
+
+ /* Resolve the gid number */
+
+ if (!NT_STATUS_IS_OK(idmap_sid_to_gid(group_sid, &(pw->pw_gid), 0))) {
+ DEBUG(1, ("error getting group id for sid %s\n", sid_to_string(sid_string, group_sid)));
+ return False;
+ }
+
+ /* Username */
+
+ fill_domain_username(output_username, dom_name, user_name);
+
+ safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
+
+ /* Full name (gecos) */
+
+ safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1);
+
+ /* Home directory and shell - use template config parameters. The
+ defaults are /tmp for the home directory and /bin/false for
+ shell. */
+
+ /* The substitution of %U and %D in the 'template homedir' is done
+ by alloc_sub_specified() below. */
+
+ fstrcpy(current_user_info.domain, dom_name);
+
+ homedir = alloc_sub_specified(lp_template_homedir(), user_name, dom_name, pw->pw_uid, pw->pw_gid);
+
+ if (!homedir)
+ return False;
+
+ safe_strcpy(pw->pw_dir, homedir, sizeof(pw->pw_dir) - 1);
+
+ SAFE_FREE(homedir);
+
+ shell = alloc_sub_specified(lp_template_shell(), user_name, dom_name, pw->pw_uid, pw->pw_gid);
+
+ if (!shell)
+ return False;
+
+ safe_strcpy(pw->pw_shell, shell,
+ sizeof(pw->pw_shell) - 1);
+
+ /* Password - set to "x" as we can't generate anything useful here.
+ Authentication can be done using the pam_winbind module. */
+
+ safe_strcpy(pw->pw_passwd, "x", sizeof(pw->pw_passwd) - 1);
+
+ return True;
+}
+
+/* Return a password structure from a username. */
+
+enum winbindd_result winbindd_getpwnam(struct winbindd_cli_state *state)
+{
+ WINBIND_USERINFO user_info;
+ WINBINDD_PW *pw;
+ DOM_SID user_sid;
+ NTSTATUS status;
+ fstring name_domain, name_user;
+ enum SID_NAME_USE name_type;
+ struct winbindd_domain *domain;
+ TALLOC_CTX *mem_ctx;
+
+ /* Ensure null termination */
+ state->request.data.username[sizeof(state->request.data.username)-1]='\0';
+
+ DEBUG(3, ("[%5lu]: getpwnam %s\n", (unsigned long)state->pid,
+ state->request.data.username));
+
+ /* Parse domain and username */
+
+ parse_domain_user(state->request.data.username,
+ name_domain, name_user);
+
+ /* if this is our local domain (or no domain), the do a local tdb search */
+
+ if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
+ if ( !(pw = wb_getpwnam(name_user)) ) {
+ DEBUG(5,("winbindd_getpwnam: lookup for %s\\%s failed\n",
+ name_domain, name_user));
+ return WINBINDD_ERROR;
+ }
+ memcpy( &state->response.data.pw, pw, sizeof(WINBINDD_PW) );
+ return WINBINDD_OK;
+ }
+
+ /* should we deal with users for our domain? */
+
+ if ((domain = find_domain_from_name(name_domain)) == NULL) {
+ DEBUG(5, ("no such domain: %s\n", name_domain));
+ return WINBINDD_ERROR;
+ }
+
+ if ( domain->primary && lp_winbind_trusted_domains_only()) {
+ DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getpwnam() for %s\\%s.\n",
+ name_domain, name_user));
+ return WINBINDD_ERROR;
+ }
+
+ /* Get rid and name type from name */
+
+ if (!winbindd_lookup_sid_by_name(domain, name_user, &user_sid, &name_type)) {
+ DEBUG(1, ("user '%s' does not exist\n", name_user));
+ return WINBINDD_ERROR;
+ }
+
+ if (name_type != SID_NAME_USER && name_type != SID_NAME_COMPUTER) {
+ DEBUG(1, ("name '%s' is not a user name: %d\n", name_user,
+ name_type));
+ return WINBINDD_ERROR;
+ }
+
+ /* Get some user info. */
+
+ if (!(mem_ctx = talloc_init("winbindd_getpwnam([%s]\\[%s])",
+ name_domain, name_user))) {
+ DEBUG(1, ("out of memory\n"));
+ return WINBINDD_ERROR;
+ }
+
+ status = domain->methods->query_user(domain, mem_ctx, &user_sid,
+ &user_info);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("error getting user info for user '[%s]\\[%s]'\n",
+ name_domain, name_user));
+ talloc_destroy(mem_ctx);
+ return WINBINDD_ERROR;
+ }
+
+ /* Now take all this information and fill in a passwd structure */
+ if (!winbindd_fill_pwent(name_domain, name_user,
+ user_info.user_sid, user_info.group_sid,
+ user_info.full_name,
+ &state->response.data.pw)) {
+ talloc_destroy(mem_ctx);
+ return WINBINDD_ERROR;
+ }
+
+ talloc_destroy(mem_ctx);
+
+ return WINBINDD_OK;
+}
+
+/* Return a password structure given a uid number */
+
+enum winbindd_result winbindd_getpwuid(struct winbindd_cli_state *state)
+{
+ DOM_SID user_sid;
+ struct winbindd_domain *domain;
+ WINBINDD_PW *pw;
+ fstring dom_name;
+ fstring user_name;
+ enum SID_NAME_USE name_type;
+ WINBIND_USERINFO user_info;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ gid_t gid;
+
+ /* Bug out if the uid isn't in the winbind range */
+
+ if ((state->request.data.uid < server_state.uid_low ) ||
+ (state->request.data.uid > server_state.uid_high))
+ return WINBINDD_ERROR;
+
+ DEBUG(3, ("[%5lu]: getpwuid %lu\n", (unsigned long)state->pid,
+ (unsigned long)state->request.data.uid));
+
+ /* always try local tdb first */
+
+ if ( (pw = wb_getpwuid(state->request.data.uid)) != NULL ) {
+ memcpy( &state->response.data.pw, pw, sizeof(WINBINDD_PW) );
+ return WINBINDD_OK;
+ }
+
+ /* Get rid from uid */
+
+ if (!NT_STATUS_IS_OK(idmap_uid_to_sid(&user_sid, state->request.data.uid))) {
+ DEBUG(1, ("could not convert uid %lu to SID\n",
+ (unsigned long)state->request.data.uid));
+ return WINBINDD_ERROR;
+ }
+
+ /* Get name and name type from rid */
+
+ if (!winbindd_lookup_name_by_sid(&user_sid, dom_name, user_name, &name_type)) {
+ fstring temp;
+
+ sid_to_string(temp, &user_sid);
+ DEBUG(1, ("could not lookup sid %s\n", temp));
+ return WINBINDD_ERROR;
+ }
+
+ domain = find_domain_from_sid(&user_sid);
+
+ if (!domain) {
+ DEBUG(1,("Can't find domain from sid\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* Get some user info */
+
+ if (!(mem_ctx = talloc_init("winbind_getpwuid(%lu)",
+ (unsigned long)state->request.data.uid))) {
+
+ DEBUG(1, ("out of memory\n"));
+ return WINBINDD_ERROR;
+ }
+
+ status = domain->methods->query_user(domain, mem_ctx, &user_sid,
+ &user_info);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("error getting user info for user '%s'\n",
+ user_name));
+ talloc_destroy(mem_ctx);
+ return WINBINDD_ERROR;
+ }
+
+ /* Check group has a gid number */
+
+ if (!NT_STATUS_IS_OK(idmap_sid_to_gid(user_info.group_sid, &gid, 0))) {
+ DEBUG(1, ("error getting group id for user %s\n", user_name));
+ talloc_destroy(mem_ctx);
+ return WINBINDD_ERROR;
+ }
+
+ /* Fill in password structure */
+
+ if (!winbindd_fill_pwent(domain->name, user_name, user_info.user_sid,
+ user_info.group_sid,
+ user_info.full_name, &state->response.data.pw)) {
+ talloc_destroy(mem_ctx);
+ return WINBINDD_ERROR;
+ }
+
+ talloc_destroy(mem_ctx);
+
+ return WINBINDD_OK;
+}
+
+/*
+ * set/get/endpwent functions
+ */
+
+/* Rewind file pointer for ntdom passwd database */
+
+enum winbindd_result winbindd_setpwent(struct winbindd_cli_state *state)
+{
+ struct winbindd_domain *domain;
+
+ DEBUG(3, ("[%5lu]: setpwent\n", (unsigned long)state->pid));
+
+ /* Check user has enabled this */
+
+ if (!lp_winbind_enum_users())
+ return WINBINDD_ERROR;
+
+ /* Free old static data if it exists */
+
+ if (state->getpwent_state != NULL) {
+ free_getent_state(state->getpwent_state);
+ state->getpwent_state = NULL;
+ }
+
+#if 0 /* JERRY */
+ /* add any local users we have */
+
+ if ( (domain_state = (struct getent_state *)malloc(sizeof(struct getent_state))) == NULL )
+ return WINBINDD_ERROR;
+
+ ZERO_STRUCTP(domain_state);
+
+ /* Add to list of open domains */
+
+ DLIST_ADD(state->getpwent_state, domain_state);
+#endif
+
+ /* Create sam pipes for each domain we know about */
+
+ for(domain = domain_list(); domain != NULL; domain = domain->next) {
+ struct getent_state *domain_state;
+
+
+ /* don't add our domaina if we are a PDC or if we
+ are a member of a Samba domain */
+
+ if ( (IS_DC || lp_winbind_trusted_domains_only())
+ && strequal(domain->name, lp_workgroup()) )
+ {
+ continue;
+ }
+
+ /* Create a state record for this domain */
+
+ if ((domain_state = (struct getent_state *)
+ malloc(sizeof(struct getent_state))) == NULL)
+ return WINBINDD_ERROR;
+
+ ZERO_STRUCTP(domain_state);
+
+ fstrcpy(domain_state->domain_name, domain->name);
+
+ /* Add to list of open domains */
+
+ DLIST_ADD(state->getpwent_state, domain_state);
+ }
+
+ return WINBINDD_OK;
+}
+
+/* Close file pointer to ntdom passwd database */
+
+enum winbindd_result winbindd_endpwent(struct winbindd_cli_state *state)
+{
+ DEBUG(3, ("[%5lu]: endpwent\n", (unsigned long)state->pid));
+
+ free_getent_state(state->getpwent_state);
+ state->getpwent_state = NULL;
+
+ return WINBINDD_OK;
+}
+
+/* Get partial list of domain users for a domain. We fill in the sam_entries,
+ and num_sam_entries fields with domain user information. The dispinfo_ndx
+ field is incremented to the index of the next user to fetch. Return True if
+ some users were returned, False otherwise. */
+
+#define MAX_FETCH_SAM_ENTRIES 100
+
+static BOOL get_sam_user_entries(struct getent_state *ent)
+{
+ NTSTATUS status;
+ uint32 num_entries;
+ WINBIND_USERINFO *info;
+ struct getpwent_user *name_list = NULL;
+ BOOL result = False;
+ TALLOC_CTX *mem_ctx;
+ struct winbindd_domain *domain;
+ struct winbindd_methods *methods;
+ unsigned int i;
+
+ if (ent->num_sam_entries)
+ return False;
+
+ if (!(mem_ctx = talloc_init("get_sam_user_entries(%s)",
+ ent->domain_name)))
+ return False;
+
+ if (!(domain = find_domain_from_name(ent->domain_name))) {
+ DEBUG(3, ("no such domain %s in get_sam_user_entries\n",
+ ent->domain_name));
+ return False;
+ }
+
+ methods = domain->methods;
+
+ /* Free any existing user info */
+
+ SAFE_FREE(ent->sam_entries);
+ ent->num_sam_entries = 0;
+
+ /* Call query_user_list to get a list of usernames and user rids */
+
+ num_entries = 0;
+
+ status = methods->query_user_list(domain, mem_ctx, &num_entries,
+ &info);
+
+ if (num_entries) {
+ struct getpwent_user *tnl;
+
+ tnl = (struct getpwent_user *)Realloc(name_list,
+ sizeof(struct getpwent_user) *
+ (ent->num_sam_entries +
+ num_entries));
+
+ if (!tnl) {
+ DEBUG(0,("get_sam_user_entries realloc failed.\n"));
+ SAFE_FREE(name_list);
+ goto done;
+ } else
+ name_list = tnl;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ /* Store account name and gecos */
+ if (!info[i].acct_name) {
+ fstrcpy(name_list[ent->num_sam_entries + i].name, "");
+ } else {
+ fstrcpy(name_list[ent->num_sam_entries + i].name,
+ info[i].acct_name);
+ }
+ if (!info[i].full_name) {
+ fstrcpy(name_list[ent->num_sam_entries + i].gecos, "");
+ } else {
+ fstrcpy(name_list[ent->num_sam_entries + i].gecos,
+ info[i].full_name);
+ }
+
+ /* User and group ids */
+ sid_copy(&name_list[ent->num_sam_entries+i].user_sid, info[i].user_sid);
+ sid_copy(&name_list[ent->num_sam_entries+i].group_sid, info[i].group_sid);
+ }
+
+ ent->num_sam_entries += num_entries;
+
+ /* Fill in remaining fields */
+
+ ent->sam_entries = name_list;
+ ent->sam_entry_index = 0;
+ result = ent->num_sam_entries > 0;
+
+ done:
+
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+/* Fetch next passwd entry from ntdom database */
+
+#define MAX_GETPWENT_USERS 500
+
+enum winbindd_result winbindd_getpwent(struct winbindd_cli_state *state)
+{
+ struct getent_state *ent;
+ struct winbindd_pw *user_list;
+ int num_users, user_list_ndx = 0, i;
+
+ DEBUG(3, ("[%5lu]: getpwent\n", (unsigned long)state->pid));
+
+ /* Check user has enabled this */
+
+ if (!lp_winbind_enum_users())
+ return WINBINDD_ERROR;
+
+ /* Allocate space for returning a chunk of users */
+
+ num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
+
+ if ((state->response.extra_data =
+ malloc(num_users * sizeof(struct winbindd_pw))) == NULL)
+ return WINBINDD_ERROR;
+
+ memset(state->response.extra_data, 0, num_users *
+ sizeof(struct winbindd_pw));
+
+ user_list = (struct winbindd_pw *)state->response.extra_data;
+
+ if (!(ent = state->getpwent_state))
+ return WINBINDD_ERROR;
+
+ /* Start sending back users */
+
+ for (i = 0; i < num_users; i++) {
+ struct getpwent_user *name_list = NULL;
+ uint32 result;
+
+ /* Do we need to fetch another chunk of users? */
+
+ if (ent->num_sam_entries == ent->sam_entry_index) {
+
+ while(ent && !get_sam_user_entries(ent)) {
+ struct getent_state *next_ent;
+
+ /* Free state information for this domain */
+
+ SAFE_FREE(ent->sam_entries);
+
+ next_ent = ent->next;
+ DLIST_REMOVE(state->getpwent_state, ent);
+
+ SAFE_FREE(ent);
+ ent = next_ent;
+ }
+
+ /* No more domains */
+
+ if (!ent)
+ break;
+ }
+
+ name_list = ent->sam_entries;
+
+ /* Lookup user info */
+
+ result = winbindd_fill_pwent(
+ ent->domain_name,
+ name_list[ent->sam_entry_index].name,
+ &name_list[ent->sam_entry_index].user_sid,
+ &name_list[ent->sam_entry_index].group_sid,
+ name_list[ent->sam_entry_index].gecos,
+ &user_list[user_list_ndx]);
+
+ ent->sam_entry_index++;
+
+ /* Add user to return list */
+
+ if (result) {
+
+ user_list_ndx++;
+ state->response.data.num_entries++;
+ state->response.length +=
+ sizeof(struct winbindd_pw);
+
+ } else
+ DEBUG(1, ("could not lookup domain user %s\n",
+ name_list[ent->sam_entry_index].name));
+ }
+
+ /* Out of domains */
+
+ return (user_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
+}
+
+/* List domain users without mapping to unix ids */
+
+enum winbindd_result winbindd_list_users(struct winbindd_cli_state *state)
+{
+ struct winbindd_domain *domain;
+ WINBIND_USERINFO *info;
+ const char *which_domain;
+ uint32 num_entries = 0, total_entries = 0;
+ char *ted, *extra_data = NULL;
+ int extra_data_len = 0;
+ TALLOC_CTX *mem_ctx;
+ enum winbindd_result rv = WINBINDD_ERROR;
+
+ DEBUG(3, ("[%5lu]: list users\n", (unsigned long)state->pid));
+
+ if (!(mem_ctx = talloc_init("winbindd_list_users")))
+ return WINBINDD_ERROR;
+
+ /* Ensure null termination */
+ state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
+ which_domain = state->request.domain_name;
+
+ /* Enumerate over trusted domains */
+
+ for (domain = domain_list(); domain; domain = domain->next) {
+ NTSTATUS status;
+ struct winbindd_methods *methods;
+ unsigned int i;
+
+ /* if we have a domain name restricting the request and this
+ one in the list doesn't match, then just bypass the remainder
+ of the loop */
+
+ if ( *which_domain && !strequal(which_domain, domain->name) )
+ continue;
+
+ methods = domain->methods;
+
+ /* Query display info */
+ status = methods->query_user_list(domain, mem_ctx,
+ &num_entries, &info);
+
+ if (num_entries == 0)
+ continue;
+
+ /* Allocate some memory for extra data */
+ total_entries += num_entries;
+
+ ted = Realloc(extra_data, sizeof(fstring) * total_entries);
+
+ if (!ted) {
+ DEBUG(0,("failed to enlarge buffer!\n"));
+ SAFE_FREE(extra_data);
+ goto done;
+ } else
+ extra_data = ted;
+
+ /* Pack user list into extra data fields */
+
+ for (i = 0; i < num_entries; i++) {
+ fstring acct_name, name;
+
+ if (!info[i].acct_name) {
+ fstrcpy(acct_name, "");
+ } else {
+ fstrcpy(acct_name, info[i].acct_name);
+ }
+
+ fill_domain_username(name, domain->name, acct_name);
+
+ /* Append to extra data */
+ memcpy(&extra_data[extra_data_len], name,
+ strlen(name));
+ extra_data_len += strlen(name);
+ extra_data[extra_data_len++] = ',';
+ }
+ }
+
+ /* Assign extra_data fields in response structure */
+
+ if (extra_data) {
+ extra_data[extra_data_len - 1] = '\0';
+ state->response.extra_data = extra_data;
+ state->response.length += extra_data_len;
+ }
+
+ /* No domains responded but that's still OK so don't return an
+ error. */
+
+ rv = WINBINDD_OK;
+
+ done:
+
+ talloc_destroy(mem_ctx);
+
+ return rv;
+}
diff --git a/source/nsswitch/winbindd_util.c b/source/nsswitch/winbindd_util.c
new file mode 100644
index 00000000000..1aa4923e96f
--- /dev/null
+++ b/source/nsswitch/winbindd_util.c
@@ -0,0 +1,994 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000-2001
+ Copyright (C) 2001 by Martin Pool <mbp@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/**
+ * @file winbindd_util.c
+ *
+ * Winbind daemon for NT domain authentication nss module.
+ **/
+
+
+/**
+ * Used to clobber name fields that have an undefined value.
+ *
+ * Correct code should never look at a field that has this value.
+ **/
+
+static const fstring name_deadbeef = "<deadbeef>";
+
+/* The list of trusted domains. Note that the list can be deleted and
+ recreated using the init_domain_list() function so pointers to
+ individual winbindd_domain structures cannot be made. Keep a copy of
+ the domain name instead. */
+
+static struct winbindd_domain *_domain_list;
+
+/**
+ When was the last scan of trusted domains done?
+
+ 0 == not ever
+*/
+
+static time_t last_trustdom_scan;
+
+struct winbindd_domain *domain_list(void)
+{
+ /* Initialise list */
+
+ if (!_domain_list)
+ if (!init_domain_list())
+ return NULL;
+
+ return _domain_list;
+}
+
+/* Free all entries in the trusted domain list */
+
+void free_domain_list(void)
+{
+ struct winbindd_domain *domain = _domain_list;
+
+ while(domain) {
+ struct winbindd_domain *next = domain->next;
+
+ DLIST_REMOVE(_domain_list, domain);
+ SAFE_FREE(domain);
+ domain = next;
+ }
+}
+
+static BOOL is_internal_domain(const DOM_SID *sid)
+{
+ DOM_SID tmp_sid;
+
+ if (sid_equal(sid, get_global_sam_sid()))
+ return True;
+
+ string_to_sid(&tmp_sid, "S-1-5-32");
+ if (sid_equal(sid, &tmp_sid))
+ return True;
+
+ return False;
+}
+
+
+/* Add a trusted domain to our list of domains */
+static struct winbindd_domain *add_trusted_domain(const char *domain_name, const char *alt_name,
+ struct winbindd_methods *methods,
+ DOM_SID *sid)
+{
+ struct winbindd_domain *domain;
+ const char *alternative_name = NULL;
+ static const DOM_SID null_sid;
+
+ /* ignore alt_name if we are not in an AD domain */
+
+ if ( (lp_security() == SEC_ADS) && alt_name && *alt_name) {
+ alternative_name = alt_name;
+ }
+
+ /* We can't call domain_list() as this function is called from
+ init_domain_list() and we'll get stuck in a loop. */
+ for (domain = _domain_list; domain; domain = domain->next) {
+ if (strequal(domain_name, domain->name) ||
+ strequal(domain_name, domain->alt_name)) {
+ return domain;
+ }
+ if (alternative_name && *alternative_name) {
+ if (strequal(alternative_name, domain->name) ||
+ strequal(alternative_name, domain->alt_name)) {
+ return domain;
+ }
+ }
+ if (sid) {
+ if (sid_equal(sid, &null_sid) ) {
+
+ } else if (sid_equal(sid, &domain->sid)) {
+ return domain;
+ }
+ }
+ }
+
+ /* Create new domain entry */
+
+ if ((domain = (struct winbindd_domain *)malloc(sizeof(*domain))) == NULL)
+ return NULL;
+
+ /* Fill in fields */
+
+ ZERO_STRUCTP(domain);
+
+ /* prioritise the short name */
+ if (strchr_m(domain_name, '.') && alternative_name && *alternative_name) {
+ fstrcpy(domain->name, alternative_name);
+ fstrcpy(domain->alt_name, domain_name);
+ } else {
+ fstrcpy(domain->name, domain_name);
+ if (alternative_name) {
+ fstrcpy(domain->alt_name, alternative_name);
+ }
+ }
+
+ domain->methods = methods;
+ domain->backend = NULL;
+ domain->internal = is_internal_domain(sid);
+ domain->sequence_number = DOM_SEQUENCE_NONE;
+ domain->last_seq_check = 0;
+ if (sid) {
+ sid_copy(&domain->sid, sid);
+ }
+
+ /* set flags about native_mode, active_directory */
+
+ if (!domain->internal)
+ set_dc_type_and_flags( domain );
+
+ DEBUG(3,("add_trusted_domain: %s is an %s %s domain\n", domain->name,
+ domain->active_directory ? "ADS" : "NT4",
+ domain->native_mode ? "native mode" :
+ ((domain->active_directory && !domain->native_mode) ? "mixed mode" : "")));
+
+ /* Link to domain list */
+ DLIST_ADD(_domain_list, domain);
+
+ DEBUG(1,("Added domain %s %s %s\n",
+ domain->name, domain->alt_name,
+ &domain->sid?sid_string_static(&domain->sid):""));
+
+ return domain;
+}
+
+/********************************************************************
+ rescan our domains looking for new trusted domains
+********************************************************************/
+
+static void add_trusted_domains( struct winbindd_domain *domain )
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS result;
+ time_t t;
+ char **names;
+ char **alt_names;
+ int num_domains = 0;
+ DOM_SID *dom_sids, null_sid;
+ int i;
+ struct winbindd_domain *new_domain;
+
+ /* trusted domains might be disabled */
+ if (!lp_allow_trusted_domains()) {
+ return;
+ }
+
+ DEBUG(5, ("scanning trusted domain list\n"));
+
+ if (!(mem_ctx = talloc_init("init_domain_list")))
+ return;
+
+ ZERO_STRUCTP(&null_sid);
+
+ t = time(NULL);
+
+ /* ask the DC what domains it trusts */
+
+ result = domain->methods->trusted_domains(domain, mem_ctx, (unsigned int *)&num_domains,
+ &names, &alt_names, &dom_sids);
+
+ if ( NT_STATUS_IS_OK(result) ) {
+
+ /* Add each domain to the trusted domain list */
+
+ for(i = 0; i < num_domains; i++) {
+ DEBUG(10,("Found domain %s\n", names[i]));
+ add_trusted_domain(names[i], alt_names?alt_names[i]:NULL,
+ domain->methods, &dom_sids[i]);
+
+ /* if the SID was empty, we better set it now */
+
+ if ( sid_equal(&dom_sids[i], &null_sid) ) {
+
+ new_domain = find_domain_from_name(names[i]);
+
+ /* this should never happen */
+ if ( !new_domain ) {
+ DEBUG(0,("rescan_trust_domains: can't find the domain I just added! [%s]\n",
+ names[i]));
+ break;
+ }
+
+ /* call the cache method; which will operate on the winbindd_domain \
+ passed in and choose either rpc or ads as appropriate */
+
+ result = domain->methods->domain_sid( new_domain, &new_domain->sid );
+
+ if ( NT_STATUS_IS_OK(result) )
+ sid_copy( &dom_sids[i], &new_domain->sid );
+ }
+
+ /* store trusted domain in the cache */
+ trustdom_cache_store(names[i], alt_names ? alt_names[i] : NULL,
+ &dom_sids[i], t + WINBINDD_RESCAN_FREQ);
+ }
+ }
+
+ talloc_destroy(mem_ctx);
+}
+
+/********************************************************************
+ Periodically we need to refresh the trusted domain cache for smbd
+********************************************************************/
+
+void rescan_trusted_domains( void )
+{
+ time_t now = time(NULL);
+ struct winbindd_domain *mydomain = NULL;
+
+ /* see if the time has come... */
+
+ if ( (now > last_trustdom_scan) && ((now-last_trustdom_scan) < WINBINDD_RESCAN_FREQ) )
+ return;
+
+ if ( (mydomain = find_our_domain()) == NULL ) {
+ DEBUG(0,("rescan_trusted_domains: Can't find my own domain!\n"));
+ return;
+ }
+
+ /* this will only add new domains we didn't already know about */
+
+ add_trusted_domains( mydomain );
+
+ last_trustdom_scan = now;
+
+ return;
+}
+
+/* Look up global info for the winbind daemon */
+BOOL init_domain_list(void)
+{
+ extern struct winbindd_methods cache_methods;
+ struct winbindd_domain *domain;
+
+ /* Free existing list */
+ free_domain_list();
+
+ /* Add ourselves as the first entry. */
+
+ domain = add_trusted_domain( lp_workgroup(), lp_realm(), &cache_methods, NULL);
+
+ domain->primary = True;
+
+ /* get any alternate name for the primary domain */
+
+ cache_methods.alternate_name(domain);
+
+ /* now we have the correct netbios (short) domain name */
+
+ if ( *domain->name )
+ set_global_myworkgroup( domain->name );
+
+ if (!secrets_fetch_domain_sid(domain->name, &domain->sid)) {
+ DEBUG(1, ("Could not fetch sid for our domain %s\n",
+ domain->name));
+ return False;
+ }
+
+ /* do an initial scan for trusted domains */
+ add_trusted_domains(domain);
+
+ /* Add our local SAM domains */
+ {
+ DOM_SID sid;
+ extern struct winbindd_methods passdb_methods;
+ struct winbindd_domain *dom;
+
+ string_to_sid(&sid, "S-1-5-32");
+
+ dom = add_trusted_domain("BUILTIN", NULL, &passdb_methods,
+ &sid);
+ dom->internal = True;
+
+ dom = add_trusted_domain(get_global_sam_name(), NULL,
+ &passdb_methods,
+ get_global_sam_sid());
+ dom->internal = True;
+ }
+
+ /* avoid rescanning this right away */
+ last_trustdom_scan = time(NULL);
+ return True;
+}
+
+/**
+ * Given a domain name, return the struct winbindd domain info for it
+ *
+ * @note Do *not* pass lp_workgroup() to this function. domain_list
+ * may modify it's value, and free that pointer. Instead, our local
+ * domain may be found by calling find_our_domain().
+ * directly.
+ *
+ *
+ * @return The domain structure for the named domain, if it is working.
+ */
+
+struct winbindd_domain *find_domain_from_name(const char *domain_name)
+{
+ struct winbindd_domain *domain;
+
+ /* Search through list */
+
+ for (domain = domain_list(); domain != NULL; domain = domain->next) {
+ if (strequal(domain_name, domain->name) ||
+ (domain->alt_name[0] && strequal(domain_name, domain->alt_name))) {
+ return domain;
+ }
+ }
+
+ /* Not found */
+
+ return NULL;
+}
+
+/* Given a domain sid, return the struct winbindd domain info for it */
+
+struct winbindd_domain *find_domain_from_sid(DOM_SID *sid)
+{
+ struct winbindd_domain *domain;
+
+ /* Search through list */
+
+ for (domain = domain_list(); domain != NULL; domain = domain->next) {
+ if (sid_compare_domain(sid, &domain->sid) == 0)
+ return domain;
+ }
+
+ /* Not found */
+
+ return NULL;
+}
+
+/* Given a domain sid, return the struct winbindd domain info for it */
+
+struct winbindd_domain *find_our_domain(void)
+{
+ struct winbindd_domain *domain;
+
+ /* Search through list */
+
+ for (domain = domain_list(); domain != NULL; domain = domain->next) {
+ if (domain->primary)
+ return domain;
+ }
+
+ /* Not found */
+
+ return NULL;
+}
+
+/* Lookup a sid in a domain from a name */
+
+BOOL winbindd_lookup_sid_by_name(struct winbindd_domain *domain,
+ const char *name, DOM_SID *sid,
+ enum SID_NAME_USE *type)
+{
+ NTSTATUS result;
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_init("lookup_sid_by_name for %s\n", name);
+ if (!mem_ctx)
+ return False;
+
+ /* Lookup name */
+ result = domain->methods->name_to_sid(domain, mem_ctx, name, sid, type);
+
+ talloc_destroy(mem_ctx);
+
+ /* Return rid and type if lookup successful */
+ if (!NT_STATUS_IS_OK(result)) {
+ *type = SID_NAME_UNKNOWN;
+ }
+
+ return NT_STATUS_IS_OK(result);
+}
+
+/**
+ * @brief Lookup a name in a domain from a sid.
+ *
+ * @param sid Security ID you want to look up.
+ * @param name On success, set to the name corresponding to @p sid.
+ * @param dom_name On success, set to the 'domain name' corresponding to @p sid.
+ * @param type On success, contains the type of name: alias, group or
+ * user.
+ * @retval True if the name exists, in which case @p name and @p type
+ * are set, otherwise False.
+ **/
+BOOL winbindd_lookup_name_by_sid(DOM_SID *sid,
+ fstring dom_name,
+ fstring name,
+ enum SID_NAME_USE *type)
+{
+ char *names;
+ NTSTATUS result;
+ TALLOC_CTX *mem_ctx;
+ BOOL rv = False;
+ struct winbindd_domain *domain;
+
+ domain = find_domain_from_sid(sid);
+
+ if (!domain) {
+ DEBUG(1,("Can't find domain from sid\n"));
+ return False;
+ }
+
+ /* Lookup name */
+
+ if (!(mem_ctx = talloc_init("winbindd_lookup_name_by_sid")))
+ return False;
+
+ result = domain->methods->sid_to_name(domain, mem_ctx, sid, &names, type);
+
+ /* Return name and type if successful */
+
+ if ((rv = NT_STATUS_IS_OK(result))) {
+ fstrcpy(dom_name, domain->name);
+ fstrcpy(name, names);
+ } else {
+ *type = SID_NAME_UNKNOWN;
+ fstrcpy(name, name_deadbeef);
+ }
+
+ talloc_destroy(mem_ctx);
+
+ return rv;
+}
+
+
+/* Free state information held for {set,get,end}{pw,gr}ent() functions */
+
+void free_getent_state(struct getent_state *state)
+{
+ struct getent_state *temp;
+
+ /* Iterate over state list */
+
+ temp = state;
+
+ while(temp != NULL) {
+ struct getent_state *next;
+
+ /* Free sam entries then list entry */
+
+ SAFE_FREE(state->sam_entries);
+ DLIST_REMOVE(state, state);
+ next = temp->next;
+
+ SAFE_FREE(temp);
+ temp = next;
+ }
+}
+
+/* Parse winbindd related parameters */
+
+BOOL winbindd_param_init(void)
+{
+ /* Parse winbind uid and winbind_gid parameters */
+
+ if (!lp_idmap_uid(&server_state.uid_low, &server_state.uid_high)) {
+ DEBUG(0, ("winbindd: idmap uid range missing or invalid\n"));
+ DEBUG(0, ("winbindd: cannot continue, exiting.\n"));
+ return False;
+ }
+
+ if (!lp_idmap_gid(&server_state.gid_low, &server_state.gid_high)) {
+ DEBUG(0, ("winbindd: idmap gid range missing or invalid\n"));
+ DEBUG(0, ("winbindd: cannot continue, exiting.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/* Check if a domain is present in a comma-separated list of domains */
+
+BOOL check_domain_env(char *domain_env, char *domain)
+{
+ fstring name;
+ const char *tmp = domain_env;
+
+ while(next_token(&tmp, name, ",", sizeof(fstring))) {
+ if (strequal(name, domain))
+ return True;
+ }
+
+ return False;
+}
+
+/* Is this a domain which we may assume no DOMAIN\ prefix? */
+
+static BOOL assume_domain(const char *domain) {
+ if ((lp_winbind_use_default_domain()
+ || lp_winbind_trusted_domains_only()) &&
+ strequal(lp_workgroup(), domain))
+ return True;
+
+ if (strequal(get_global_sam_name(), domain))
+ return True;
+
+ return False;
+}
+
+/* Parse a string of the form DOMAIN/user into a domain and a user */
+
+BOOL parse_domain_user(const char *domuser, fstring domain, fstring user)
+{
+ char *p = strchr(domuser,*lp_winbind_separator());
+
+ if ( !p ) {
+ fstrcpy(user, domuser);
+
+ if ( assume_domain(lp_workgroup())) {
+ fstrcpy(domain, lp_workgroup());
+ } else {
+ fstrcpy( domain, get_global_sam_name() );
+ }
+ }
+ else {
+ fstrcpy(user, p+1);
+ fstrcpy(domain, domuser);
+ domain[PTR_DIFF(p, domuser)] = 0;
+ }
+
+ strupper_m(domain);
+
+ return True;
+}
+
+/*
+ Fill DOMAIN\\USERNAME entry accounting 'winbind use default domain' and
+ 'winbind separator' options.
+ This means:
+ - omit DOMAIN when 'winbind use default domain = true' and DOMAIN is
+ lp_workgroup()
+
+ If we are a PDC or BDC, and this is for our domain, do likewise.
+
+ Also, if omit DOMAIN if 'winbind trusted domains only = true', as the
+ username is then unqualified in unix
+
+*/
+void fill_domain_username(fstring name, const char *domain, const char *user)
+{
+ if (assume_domain(domain)) {
+ strlcpy(name, user, sizeof(fstring));
+ } else {
+ slprintf(name, sizeof(fstring) - 1, "%s%s%s",
+ domain, lp_winbind_separator(),
+ user);
+ }
+}
+
+/*
+ * Winbindd socket accessor functions
+ */
+
+char *get_winbind_priv_pipe_dir(void)
+{
+ return lock_path(WINBINDD_PRIV_SOCKET_SUBDIR);
+}
+
+/* Open the winbindd socket */
+
+static int _winbindd_socket = -1;
+static int _winbindd_priv_socket = -1;
+
+int open_winbindd_socket(void)
+{
+ if (_winbindd_socket == -1) {
+ _winbindd_socket = create_pipe_sock(
+ WINBINDD_SOCKET_DIR, WINBINDD_SOCKET_NAME, 0755);
+ DEBUG(10, ("open_winbindd_socket: opened socket fd %d\n",
+ _winbindd_socket));
+ }
+
+ return _winbindd_socket;
+}
+
+int open_winbindd_priv_socket(void)
+{
+ if (_winbindd_priv_socket == -1) {
+ _winbindd_priv_socket = create_pipe_sock(
+ get_winbind_priv_pipe_dir(), WINBINDD_SOCKET_NAME, 0750);
+ DEBUG(10, ("open_winbindd_priv_socket: opened socket fd %d\n",
+ _winbindd_priv_socket));
+ }
+
+ return _winbindd_priv_socket;
+}
+
+/* Close the winbindd socket */
+
+void close_winbindd_socket(void)
+{
+ if (_winbindd_socket != -1) {
+ DEBUG(10, ("close_winbindd_socket: closing socket fd %d\n",
+ _winbindd_socket));
+ close(_winbindd_socket);
+ _winbindd_socket = -1;
+ }
+ if (_winbindd_priv_socket != -1) {
+ DEBUG(10, ("close_winbindd_socket: closing socket fd %d\n",
+ _winbindd_priv_socket));
+ close(_winbindd_priv_socket);
+ _winbindd_priv_socket = -1;
+ }
+}
+
+/*
+ * Client list accessor functions
+ */
+
+static struct winbindd_cli_state *_client_list;
+static int _num_clients;
+
+/* Return list of all connected clients */
+
+struct winbindd_cli_state *winbindd_client_list(void)
+{
+ return _client_list;
+}
+
+/* Add a connection to the list */
+
+void winbindd_add_client(struct winbindd_cli_state *cli)
+{
+ DLIST_ADD(_client_list, cli);
+ _num_clients++;
+}
+
+/* Remove a client from the list */
+
+void winbindd_remove_client(struct winbindd_cli_state *cli)
+{
+ DLIST_REMOVE(_client_list, cli);
+ _num_clients--;
+}
+
+/* Close all open clients */
+
+void winbindd_kill_all_clients(void)
+{
+ struct winbindd_cli_state *cl = winbindd_client_list();
+
+ DEBUG(10, ("winbindd_kill_all_clients: going postal\n"));
+
+ while (cl) {
+ struct winbindd_cli_state *next;
+
+ next = cl->next;
+ winbindd_remove_client(cl);
+ cl = next;
+ }
+}
+
+/* Return number of open clients */
+
+int winbindd_num_clients(void)
+{
+ return _num_clients;
+}
+
+/* Help with RID -> SID conversion */
+
+DOM_SID *rid_to_talloced_sid(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 rid)
+{
+ DOM_SID *sid;
+ sid = talloc(mem_ctx, sizeof(*sid));
+ if (!sid) {
+ smb_panic("rid_to_to_talloced_sid: talloc for DOM_SID failed!\n");
+ }
+ sid_copy(sid, &domain->sid);
+ sid_append_rid(sid, rid);
+ return sid;
+}
+
+/*****************************************************************************
+ For idmap conversion: convert one record to new format
+ Ancient versions (eg 2.2.3a) of winbindd_idmap.tdb mapped DOMAINNAME/rid
+ instead of the SID.
+*****************************************************************************/
+static int convert_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA data, void *state)
+{
+ struct winbindd_domain *domain;
+ char *p;
+ DOM_SID sid;
+ uint32 rid;
+ fstring keystr;
+ fstring dom_name;
+ TDB_DATA key2;
+ BOOL *failed = (BOOL *)state;
+
+ DEBUG(10,("Converting %s\n", key.dptr));
+
+ p = strchr(key.dptr, '/');
+ if (!p)
+ return 0;
+
+ *p = 0;
+ fstrcpy(dom_name, key.dptr);
+ *p++ = '/';
+
+ domain = find_domain_from_name(dom_name);
+ if (domain == NULL) {
+ /* We must delete the old record. */
+ DEBUG(0,("Unable to find domain %s\n", dom_name ));
+ DEBUG(0,("deleting record %s\n", key.dptr ));
+
+ if (tdb_delete(tdb, key) != 0) {
+ DEBUG(0, ("Unable to delete record %s\n", key.dptr));
+ *failed = True;
+ return -1;
+ }
+
+ return 0;
+ }
+
+ rid = atoi(p);
+
+ sid_copy(&sid, &domain->sid);
+ sid_append_rid(&sid, rid);
+
+ sid_to_string(keystr, &sid);
+ key2.dptr = keystr;
+ key2.dsize = strlen(keystr) + 1;
+
+ if (tdb_store(tdb, key2, data, TDB_INSERT) != 0) {
+ DEBUG(0,("Unable to add record %s\n", key2.dptr ));
+ *failed = True;
+ return -1;
+ }
+
+ if (tdb_store(tdb, data, key2, TDB_REPLACE) != 0) {
+ DEBUG(0,("Unable to update record %s\n", data.dptr ));
+ *failed = True;
+ return -1;
+ }
+
+ if (tdb_delete(tdb, key) != 0) {
+ DEBUG(0,("Unable to delete record %s\n", key.dptr ));
+ *failed = True;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* These definitions are from sam/idmap_tdb.c. Replicated here just
+ out of laziness.... :-( */
+
+/* High water mark keys */
+#define HWM_GROUP "GROUP HWM"
+#define HWM_USER "USER HWM"
+
+/* idmap version determines auto-conversion */
+#define IDMAP_VERSION 2
+
+
+/*****************************************************************************
+ Convert the idmap database from an older version.
+*****************************************************************************/
+
+static BOOL idmap_convert(const char *idmap_name)
+{
+ int32 vers;
+ BOOL bigendianheader;
+ BOOL failed = False;
+ TDB_CONTEXT *idmap_tdb;
+
+ if (!(idmap_tdb = tdb_open_log(idmap_name, 0,
+ TDB_DEFAULT, O_RDWR,
+ 0600))) {
+ DEBUG(0, ("idmap_convert: Unable to open idmap database\n"));
+ return False;
+ }
+
+ bigendianheader = (idmap_tdb->flags & TDB_BIGENDIAN) ? True : False;
+
+ vers = tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION");
+
+ if (((vers == -1) && bigendianheader) || (IREV(vers) == IDMAP_VERSION)) {
+ /* Arrggghh ! Bytereversed or old big-endian - make order independent ! */
+ /*
+ * high and low records were created on a
+ * big endian machine and will need byte-reversing.
+ */
+
+ int32 wm;
+
+ wm = tdb_fetch_int32(idmap_tdb, HWM_USER);
+
+ if (wm != -1) {
+ wm = IREV(wm);
+ } else {
+ wm = server_state.uid_low;
+ }
+
+ if (tdb_store_int32(idmap_tdb, HWM_USER, wm) == -1) {
+ DEBUG(0, ("idmap_convert: Unable to byteswap user hwm in idmap database\n"));
+ tdb_close(idmap_tdb);
+ return False;
+ }
+
+ wm = tdb_fetch_int32(idmap_tdb, HWM_GROUP);
+ if (wm != -1) {
+ wm = IREV(wm);
+ } else {
+ wm = server_state.gid_low;
+ }
+
+ if (tdb_store_int32(idmap_tdb, HWM_GROUP, wm) == -1) {
+ DEBUG(0, ("idmap_convert: Unable to byteswap group hwm in idmap database\n"));
+ tdb_close(idmap_tdb);
+ return False;
+ }
+ }
+
+ /* the old format stored as DOMAIN/rid - now we store the SID direct */
+ tdb_traverse(idmap_tdb, convert_fn, &failed);
+
+ if (failed) {
+ DEBUG(0, ("Problem during conversion\n"));
+ tdb_close(idmap_tdb);
+ return False;
+ }
+
+ if (tdb_store_int32(idmap_tdb, "IDMAP_VERSION", IDMAP_VERSION) == -1) {
+ DEBUG(0, ("idmap_convert: Unable to dtore idmap version in databse\n"));
+ tdb_close(idmap_tdb);
+ return False;
+ }
+
+ tdb_close(idmap_tdb);
+ return True;
+}
+
+/*****************************************************************************
+ Convert the idmap database from an older version if necessary
+*****************************************************************************/
+
+BOOL winbindd_upgrade_idmap(void)
+{
+ pstring idmap_name;
+ pstring backup_name;
+ SMB_STRUCT_STAT stbuf;
+ TDB_CONTEXT *idmap_tdb;
+
+ pstrcpy(idmap_name, lock_path("winbindd_idmap.tdb"));
+
+ if (!file_exist(idmap_name, &stbuf)) {
+ /* nothing to convert return */
+ return True;
+ }
+
+ if (!(idmap_tdb = tdb_open_log(idmap_name, 0,
+ TDB_DEFAULT, O_RDWR,
+ 0600))) {
+ DEBUG(0, ("idmap_convert: Unable to open idmap database\n"));
+ return False;
+ }
+
+ if (tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION") == IDMAP_VERSION) {
+ /* nothing to convert return */
+ tdb_close(idmap_tdb);
+ return True;
+ }
+
+ /* backup_tdb expects the tdb not to be open */
+ tdb_close(idmap_tdb);
+
+ DEBUG(0, ("Upgrading winbindd_idmap.tdb from an old version\n"));
+
+ pstrcpy(backup_name, idmap_name);
+ pstrcat(backup_name, ".bak");
+
+ if (backup_tdb(idmap_name, backup_name) != 0) {
+ DEBUG(0, ("Could not backup idmap database\n"));
+ return False;
+ }
+
+ return idmap_convert(idmap_name);
+}
+
+/*******************************************************************
+ wrapper around retrieving the trust account password
+*******************************************************************/
+
+BOOL get_trust_pw(const char *domain, uint8 ret_pwd[16],
+ time_t *pass_last_set_time, uint32 *channel)
+{
+ DOM_SID sid;
+ char *pwd;
+
+ /* if we are a DC and this is not our domain, then lookup an account
+ for the domain trust */
+
+ if ( IS_DC && !strequal(domain, lp_workgroup()) && lp_allow_trusted_domains() )
+ {
+ if ( !secrets_fetch_trusted_domain_password(domain, &pwd, &sid,
+ pass_last_set_time) )
+ {
+ DEBUG(0, ("get_trust_pw: could not fetch trust account "
+ "password for trusted domain %s\n", domain));
+ return False;
+ }
+
+ *channel = SEC_CHAN_DOMAIN;
+ E_md4hash(pwd, ret_pwd);
+ SAFE_FREE(pwd);
+
+ return True;
+ }
+ else /* just get the account for our domain (covers
+ ROLE_DOMAIN_MEMBER as well */
+ {
+ /* get the machine trust account for our domain */
+
+ if ( !secrets_fetch_trust_account_password (lp_workgroup(), ret_pwd,
+ pass_last_set_time, channel) )
+ {
+ DEBUG(0, ("get_trust_pw: could not fetch trust account "
+ "password for my domain %s\n", domain));
+ return False;
+ }
+
+ return True;
+ }
+
+ /* Failure */
+}
+
diff --git a/source/nsswitch/winbindd_wins.c b/source/nsswitch/winbindd_wins.c
new file mode 100644
index 00000000000..a1eef159c0a
--- /dev/null
+++ b/source/nsswitch/winbindd_wins.c
@@ -0,0 +1,226 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon - WINS related functions
+
+ Copyright (C) Andrew Tridgell 1999
+ Copyright (C) Herb Lewis 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* Use our own create socket code so we don't recurse.... */
+
+static int wins_lookup_open_socket_in(void)
+{
+ struct sockaddr_in sock;
+ int val=1;
+ int res;
+
+ memset((char *)&sock,'\0',sizeof(sock));
+
+#ifdef HAVE_SOCK_SIN_LEN
+ sock.sin_len = sizeof(sock);
+#endif
+ sock.sin_port = 0;
+ sock.sin_family = AF_INET;
+ sock.sin_addr.s_addr = interpret_addr("0.0.0.0");
+ res = socket(AF_INET, SOCK_DGRAM, 0);
+ if (res == -1)
+ return -1;
+
+ setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val));
+#ifdef SO_REUSEPORT
+ setsockopt(res,SOL_SOCKET,SO_REUSEPORT,(char *)&val,sizeof(val));
+#endif /* SO_REUSEPORT */
+
+ /* now we've got a socket - we need to bind it */
+
+ if (bind(res, (struct sockaddr * ) &sock,sizeof(sock)) < 0) {
+ close(res);
+ return(-1);
+ }
+
+ set_socket_options(res,"SO_BROADCAST");
+
+ return res;
+}
+
+
+static struct node_status *lookup_byaddr_backend(char *addr, int *count)
+{
+ int fd;
+ struct in_addr ip;
+ struct nmb_name nname;
+ struct node_status *status;
+
+ fd = wins_lookup_open_socket_in();
+ if (fd == -1)
+ return NULL;
+
+ make_nmb_name(&nname, "*", 0);
+ ip = *interpret_addr2(addr);
+ status = node_status_query(fd,&nname,ip, count);
+
+ close(fd);
+ return status;
+}
+
+static struct in_addr *lookup_byname_backend(const char *name, int *count)
+{
+ int fd;
+ struct ip_service *ret = NULL;
+ struct in_addr *return_ip;
+ int j, i, flags = 0;
+
+ *count = 0;
+
+ /* always try with wins first */
+ if (resolve_wins(name,0x20,&ret,count)) {
+ if ( count == 0 )
+ return NULL;
+ if ( (return_ip = (struct in_addr *)malloc((*count)*sizeof(struct in_addr))) == NULL ) {
+ free( ret );
+ return NULL;
+ }
+
+ /* copy the IP addresses */
+ for ( i=0; i<(*count); i++ )
+ return_ip[i] = ret[i].ip;
+
+ return return_ip;
+ }
+
+ fd = wins_lookup_open_socket_in();
+ if (fd == -1) {
+ return NULL;
+ }
+
+ /* uggh, we have to broadcast to each interface in turn */
+ for (j=iface_count() - 1;
+ j >= 0;
+ j--) {
+ struct in_addr *bcast = iface_n_bcast(j);
+ return_ip = name_query(fd,name,0x20,True,True,*bcast,count, &flags, NULL);
+ if (return_ip) break;
+ }
+
+ close(fd);
+ return return_ip;
+}
+
+/* Get hostname from IP */
+
+enum winbindd_result winbindd_wins_byip(struct winbindd_cli_state *state)
+{
+ fstring response;
+ int i, count, maxlen, size;
+ struct node_status *status;
+
+ /* Ensure null termination */
+ state->request.data.winsreq[sizeof(state->request.data.winsreq)-1]='\0';
+
+ DEBUG(3, ("[%5lu]: wins_byip %s\n", (unsigned long)state->pid,
+ state->request.data.winsreq));
+
+ *response = '\0';
+ maxlen = sizeof(response) - 1;
+
+ if ((status = lookup_byaddr_backend(state->request.data.winsreq, &count))){
+ size = strlen(state->request.data.winsreq);
+ if (size > maxlen) {
+ SAFE_FREE(status);
+ return WINBINDD_ERROR;
+ }
+ fstrcat(response,state->request.data.winsreq);
+ fstrcat(response,"\t");
+ for (i = 0; i < count; i++) {
+ /* ignore group names */
+ if (status[i].flags & 0x80) continue;
+ if (status[i].type == 0x20) {
+ size = sizeof(status[i].name) + strlen(response);
+ if (size > maxlen) {
+ SAFE_FREE(status);
+ return WINBINDD_ERROR;
+ }
+ fstrcat(response, status[i].name);
+ fstrcat(response, " ");
+ }
+ }
+ /* make last character a newline */
+ response[strlen(response)-1] = '\n';
+ SAFE_FREE(status);
+ }
+ fstrcpy(state->response.data.winsresp,response);
+ return WINBINDD_OK;
+}
+
+/* Get IP from hostname */
+
+enum winbindd_result winbindd_wins_byname(struct winbindd_cli_state *state)
+{
+ struct in_addr *ip_list;
+ int i, count, maxlen, size;
+ fstring response;
+ char * addr;
+
+ /* Ensure null termination */
+ state->request.data.winsreq[sizeof(state->request.data.winsreq)-1]='\0';
+
+ DEBUG(3, ("[%5lu]: wins_byname %s\n", (unsigned long)state->pid,
+ state->request.data.winsreq));
+
+ *response = '\0';
+ maxlen = sizeof(response) - 1;
+
+ if ((ip_list = lookup_byname_backend(state->request.data.winsreq,&count))){
+ for (i = count; i ; i--) {
+ addr = inet_ntoa(ip_list[i-1]);
+ size = strlen(addr);
+ if (size > maxlen) {
+ SAFE_FREE(ip_list);
+ return WINBINDD_ERROR;
+ }
+ if (i != 0) {
+ /* Clear out the newline character */
+ /* But only if there is something in there,
+ otherwise we clobber something in the stack */
+ if (strlen(response))
+ response[strlen(response)-1] = ' ';
+ }
+ fstrcat(response,addr);
+ fstrcat(response,"\t");
+ }
+ size = strlen(state->request.data.winsreq) + strlen(response);
+ if (size > maxlen) {
+ SAFE_FREE(ip_list);
+ return WINBINDD_ERROR;
+ }
+ fstrcat(response,state->request.data.winsreq);
+ fstrcat(response,"\n");
+ SAFE_FREE(ip_list);
+ } else
+ return WINBINDD_ERROR;
+
+ fstrcpy(state->response.data.winsresp,response);
+
+ return WINBINDD_OK;
+}
diff --git a/source/nsswitch/wins.c b/source/nsswitch/wins.c
new file mode 100644
index 00000000000..100a103924c
--- /dev/null
+++ b/source/nsswitch/wins.c
@@ -0,0 +1,379 @@
+/*
+ Unix SMB/CIFS implementation.
+ a WINS nsswitch module
+ Copyright (C) Andrew Tridgell 1999
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+#ifdef HAVE_NS_API_H
+#undef VOLATILE
+
+#include <ns_daemon.h>
+#endif
+
+#ifndef INADDRSZ
+#define INADDRSZ 4
+#endif
+
+static int initialised;
+
+extern BOOL AllowDebugChange;
+
+/* Use our own create socket code so we don't recurse.... */
+
+static int wins_lookup_open_socket_in(void)
+{
+ struct sockaddr_in sock;
+ int val=1;
+ int res;
+
+ memset((char *)&sock,'\0',sizeof(sock));
+
+#ifdef HAVE_SOCK_SIN_LEN
+ sock.sin_len = sizeof(sock);
+#endif
+ sock.sin_port = 0;
+ sock.sin_family = AF_INET;
+ sock.sin_addr.s_addr = interpret_addr("0.0.0.0");
+ res = socket(AF_INET, SOCK_DGRAM, 0);
+ if (res == -1)
+ return -1;
+
+ setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val));
+#ifdef SO_REUSEPORT
+ setsockopt(res,SOL_SOCKET,SO_REUSEPORT,(char *)&val,sizeof(val));
+#endif /* SO_REUSEPORT */
+
+ /* now we've got a socket - we need to bind it */
+
+ if (bind(res, (struct sockaddr * ) &sock,sizeof(sock)) < 0) {
+ close(res);
+ return(-1);
+ }
+
+ set_socket_options(res,"SO_BROADCAST");
+
+ return res;
+}
+
+
+static void nss_wins_init(void)
+{
+ initialised = 1;
+ DEBUGLEVEL = 0;
+ AllowDebugChange = False;
+
+ TimeInit();
+ setup_logging("nss_wins",False);
+ lp_load(dyn_CONFIGFILE,True,False,False);
+ load_interfaces();
+}
+
+static struct in_addr *lookup_byname_backend(const char *name, int *count)
+{
+ int fd = -1;
+ struct ip_service *address = NULL;
+ struct in_addr *ret;
+ int j, flags = 0;
+
+ if (!initialised) {
+ nss_wins_init();
+ }
+
+ *count = 0;
+
+ /* always try with wins first */
+ if (resolve_wins(name,0x20,&address,count)) {
+ if ( (ret = (struct in_addr *)malloc(sizeof(struct in_addr))) == NULL ) {
+ free( address );
+ return NULL;
+ }
+ *ret = address[0].ip;
+ free( address );
+ return ret;
+ }
+
+ fd = wins_lookup_open_socket_in();
+ if (fd == -1) {
+ return NULL;
+ }
+
+ /* uggh, we have to broadcast to each interface in turn */
+ for (j=iface_count() - 1;j >= 0;j--) {
+ struct in_addr *bcast = iface_n_bcast(j);
+ ret = name_query(fd,name,0x20,True,True,*bcast,count, &flags, NULL);
+ if (ret) break;
+ }
+
+ close(fd);
+ return ret;
+}
+
+#ifdef HAVE_NS_API_H
+
+static struct node_status *lookup_byaddr_backend(char *addr, int *count)
+{
+ int fd;
+ struct in_addr ip;
+ struct nmb_name nname;
+ struct node_status *status;
+
+ if (!initialised) {
+ nss_wins_init();
+ }
+
+ fd = wins_lookup_open_socket_in();
+ if (fd == -1)
+ return NULL;
+
+ make_nmb_name(&nname, "*", 0);
+ ip = *interpret_addr2(addr);
+ status = node_status_query(fd,&nname,ip, count);
+
+ close(fd);
+ return status;
+}
+
+/* IRIX version */
+
+int init(void)
+{
+ nsd_logprintf(NSD_LOG_MIN, "entering init (wins)\n");
+ nss_wins_init();
+ return NSD_OK;
+}
+
+int lookup(nsd_file_t *rq)
+{
+ char *map;
+ char *key;
+ char *addr;
+ struct in_addr *ip_list;
+ struct node_status *status;
+ int i, count, len, size;
+ char response[1024];
+ BOOL found = False;
+
+ nsd_logprintf(NSD_LOG_MIN, "entering lookup (wins)\n");
+ if (! rq)
+ return NSD_ERROR;
+
+ map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
+ if (! map) {
+ rq->f_status = NS_FATAL;
+ return NSD_ERROR;
+ }
+
+ key = nsd_attr_fetch_string(rq->f_attrs, "key", (char*)0);
+ if (! key || ! *key) {
+ rq->f_status = NS_FATAL;
+ return NSD_ERROR;
+ }
+
+ response[0] = '\0';
+ len = sizeof(response) - 2;
+
+ /*
+ * response needs to be a string of the following format
+ * ip_address[ ip_address]*\tname[ alias]*
+ */
+ if (StrCaseCmp(map,"hosts.byaddr") == 0) {
+ if ( status = lookup_byaddr_backend(key, &count)) {
+ size = strlen(key) + 1;
+ if (size > len) {
+ free(status);
+ return NSD_ERROR;
+ }
+ len -= size;
+ strncat(response,key,size);
+ strncat(response,"\t",1);
+ for (i = 0; i < count; i++) {
+ /* ignore group names */
+ if (status[i].flags & 0x80) continue;
+ if (status[i].type == 0x20) {
+ size = sizeof(status[i].name) + 1;
+ if (size > len) {
+ free(status);
+ return NSD_ERROR;
+ }
+ len -= size;
+ strncat(response, status[i].name, size);
+ strncat(response, " ", 1);
+ found = True;
+ }
+ }
+ response[strlen(response)-1] = '\n';
+ free(status);
+ }
+ } else if (StrCaseCmp(map,"hosts.byname") == 0) {
+ if (ip_list = lookup_byname_backend(key, &count)) {
+ for (i = count; i ; i--) {
+ addr = inet_ntoa(ip_list[i-1]);
+ size = strlen(addr) + 1;
+ if (size > len) {
+ free(ip_list);
+ return NSD_ERROR;
+ }
+ len -= size;
+ if (i != 0)
+ response[strlen(response)-1] = ' ';
+ strncat(response,addr,size);
+ strncat(response,"\t",1);
+ }
+ size = strlen(key) + 1;
+ if (size > len) {
+ free(ip_list);
+ return NSD_ERROR;
+ }
+ strncat(response,key,size);
+ strncat(response,"\n",1);
+ found = True;
+ free(ip_list);
+ }
+ }
+
+ if (found) {
+ nsd_logprintf(NSD_LOG_LOW, "lookup (wins %s) %s\n",map,response);
+ nsd_set_result(rq,NS_SUCCESS,response,strlen(response),VOLATILE);
+ return NSD_OK;
+ }
+ nsd_logprintf(NSD_LOG_LOW, "lookup (wins) not found\n");
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+}
+
+#else
+
+/* Allocate some space from the nss static buffer. The buffer and buflen
+ are the pointers passed in by the C library to the _nss_*_*
+ functions. */
+
+static char *get_static(char **buffer, size_t *buflen, int len)
+{
+ char *result;
+
+ /* Error check. We return false if things aren't set up right, or
+ there isn't enough buffer space left. */
+
+ if ((buffer == NULL) || (buflen == NULL) || (*buflen < len)) {
+ return NULL;
+ }
+
+ /* Return an index into the static buffer */
+
+ result = *buffer;
+ *buffer += len;
+ *buflen -= len;
+
+ return result;
+}
+
+/****************************************************************************
+gethostbyname() - we ignore any domain portion of the name and only
+handle names that are at most 15 characters long
+ **************************************************************************/
+NSS_STATUS
+_nss_wins_gethostbyname_r(const char *hostname, struct hostent *he,
+ char *buffer, size_t buflen, int *h_errnop)
+{
+ struct in_addr *ip_list;
+ int i, count;
+ fstring name;
+ size_t namelen;
+
+ memset(he, '\0', sizeof(*he));
+ fstrcpy(name, hostname);
+
+ /* Do lookup */
+
+ ip_list = lookup_byname_backend(name, &count);
+
+ if (!ip_list)
+ return NSS_STATUS_NOTFOUND;
+
+ /* Copy h_name */
+
+ namelen = strlen(name) + 1;
+
+ if ((he->h_name = get_static(&buffer, &buflen, namelen)) == NULL)
+ return NSS_STATUS_TRYAGAIN;
+
+ memcpy(he->h_name, name, namelen);
+
+ /* Copy h_addr_list, align to pointer boundary first */
+
+ if ((i = (unsigned long)(buffer) % sizeof(char*)) != 0)
+ i = sizeof(char*) - i;
+
+ if (get_static(&buffer, &buflen, i) == NULL)
+ return NSS_STATUS_TRYAGAIN;
+
+ if ((he->h_addr_list = (char **)get_static(
+ &buffer, &buflen, (count + 1) * sizeof(char *))) == NULL)
+ return NSS_STATUS_TRYAGAIN;
+
+ for (i = 0; i < count; i++) {
+ if ((he->h_addr_list[i] = get_static(&buffer, &buflen,
+ INADDRSZ)) == NULL)
+ return NSS_STATUS_TRYAGAIN;
+ memcpy(he->h_addr_list[i], &ip_list[i], INADDRSZ);
+ }
+
+ he->h_addr_list[count] = NULL;
+
+ if (ip_list)
+ free(ip_list);
+
+ /* Set h_addr_type and h_length */
+
+ he->h_addrtype = AF_INET;
+ he->h_length = INADDRSZ;
+
+ /* Set h_aliases */
+
+ if ((i = (unsigned long)(buffer) % sizeof(char*)) != 0)
+ i = sizeof(char*) - i;
+
+ if (get_static(&buffer, &buflen, i) == NULL)
+ return NSS_STATUS_TRYAGAIN;
+
+ if ((he->h_aliases = (char **)get_static(
+ &buffer, &buflen, sizeof(char *))) == NULL)
+ return NSS_STATUS_TRYAGAIN;
+
+ he->h_aliases[0] = NULL;
+
+ return NSS_STATUS_SUCCESS;
+}
+
+
+NSS_STATUS
+_nss_wins_gethostbyname2_r(const char *name, int af, struct hostent *he,
+ char *buffer, size_t buflen, int *h_errnop)
+{
+ if(af!=AF_INET) {
+ *h_errnop = NO_DATA;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ return _nss_wins_gethostbyname_r(
+ name, he, buffer, buflen, h_errnop);
+}
+#endif
diff --git a/source/pam_smbpass/.cvsignore b/source/pam_smbpass/.cvsignore
new file mode 100644
index 00000000000..6d609cec52b
--- /dev/null
+++ b/source/pam_smbpass/.cvsignore
@@ -0,0 +1 @@
+*.po
diff --git a/source/pam_smbpass/CHANGELOG b/source/pam_smbpass/CHANGELOG
new file mode 100644
index 00000000000..96ef7840084
--- /dev/null
+++ b/source/pam_smbpass/CHANGELOG
@@ -0,0 +1,31 @@
+version 0.7.5 25 Mar 2001
+ - Use Samba 2.2.0 (alpha) as the target codebase, since it doesn't look
+ like Samba will be offering shared libraries in the near future.
+ - added a Makefile and support scripts to make the build process easier.
+ - imported some Solaris fixes that I've been sitting on.
+
+version 0.7.4 20 Jan 2000
+ - added a 'migrate' option to the authentication code which makes no
+ effort to authenticate the user, or even to ask for a password, but
+ it can be useful for filling in an SMB password db.
+
+version 0.7.3 19 Jan 2000
+ - updated to use the SAMBA_TNG Samba branch, allowing us to dynamically
+ link against Luke's new shared libs (libsamba, libsmb).
+
+version 0.7.2 20 Jul 1999
+ - miscellaneous bugfixes. Cleanup of legacy pam_pwdb code.
+ - fixed return value of pam_sm_setcred function.
+ - fix to autoconf support
+ - clarified some of the messages being logged
+
+version 0.6, 15 Jul 1999
+ - updated to use the new Samba (2.0) password database API.
+ - added autoconf support. May now theoretically compile on more
+ platforms than PAM itself does.
+ - added support for account management functions (i.e., disabled
+ accounts)
+
+version 0.5, 4 Apr 1998
+ - added support for hashed passwords as input. Now capable of serving
+ as an authentication agent for encrypted network transactions.
diff --git a/source/pam_smbpass/INSTALL b/source/pam_smbpass/INSTALL
new file mode 100644
index 00000000000..ae2ba02bbbc
--- /dev/null
+++ b/source/pam_smbpass/INSTALL
@@ -0,0 +1,64 @@
+
+Because pam_smbpass is derived from the Samba smbpasswd utility, recent
+versions of pam_smbpass require a copy of the Samba source code to be
+available on the build system. Version 0.7.5 has been tested against
+Samba 2.2.0-alpha3, and this is the recommended version of Samba to use
+for building pam_smbpass. This only affects /building/ pam_smbpass; you
+can still run any version of the Samba server that you want, although
+clearly it saves some disk space to have only one copy of the source
+code on your system (Samba 2.2.0-alpha3 takes roughly 32MB of disk space
+to build pam_smbpass).
+
+Version 0.7.5 features a new build system to make it easier to build
+pam_smbpass.
+
+
+Using the new build system
+==========================
+
+If you don't have a copy of the Samba source code on your machine, and you
+don't have a preferred Samba version (or mirror site), you can build
+pam_smbpass by just typing 'make'.
+
+If you want to use a version other than 2.2.0-alpha3, or you want to
+download the source code from a faster Samba mirror (see
+<http://us1.samba.org/samba/> for a list of mirror sites), please download
+the source code and unpack it before running make. The build scripts will
+attempt to autodetect your Samba source directory, and if it can't be
+found automatically, you will be given the opportunity to specify an
+alternate directory for the Samba sources.
+
+Feedback is welcome if you try (or succeed!) to build pam_smbpass with
+other versions of Samba.
+
+
+Options to 'make'
+=================
+
+By default, pam_smbpass will configure the Samba build tree with the
+options
+
+ --with-fhs --with-privatedir=/etc --with-configdir=/etc
+
+This will configure pam_smbpass to look for the smbpasswd file as
+/etc/smbpasswd (or /etc/smbpasswd.tdb), and the smb.conf file as
+/etc/smb.conf. You can override these options by setting CONFIGOPTS when
+calling make. E.g., if you have your smb.conf file in /usr/etc and your
+smbpasswd file in /usr/etc/private, you might run
+
+ make CONFIGOPTS="--with-privatedir=/usr/etc/private --with-configdir=/usr/etc"
+
+For a complete list of available configuration options, see
+'./samba/configure --help'
+
+
+Installing the module
+=====================
+
+If all goes well in the build process, the file pam_smbpass.so will be
+created in the current directory. Simply install the module into your
+system's PAM module directory:
+
+ install -m 755 -s bin/pam_smbpass.so /lib/security
+
+and you're all set.
diff --git a/source/pam_smbpass/README b/source/pam_smbpass/README
new file mode 100644
index 00000000000..6cdb76f9c3f
--- /dev/null
+++ b/source/pam_smbpass/README
@@ -0,0 +1,60 @@
+25 Mar 2001
+
+pam_smbpass is a PAM module which can be used on conforming systems to
+keep the smbpasswd (Samba password) database in sync with the unix
+password file. PAM (Pluggable Authentication Modules) is an API supported
+under some Unices, such as Solaris, HPUX and Linux, that provides a
+generic interface to authentication mechanisms.
+
+For more information on PAM, see http://ftp.kernel.org/pub/linux/libs/pam/
+
+This module authenticates a local smbpasswd user database. If you require
+support for authenticating against a remote SMB server, or if you're
+concerned about the presence of suid root binaries on your system, it is
+recommended that you use pam_winbind instead.
+
+Options recognized by this module are as follows:
+
+ debug - log more debugging info
+ audit - like debug, but also logs unknown usernames
+ use_first_pass - don't prompt the user for passwords;
+ take them from PAM_ items instead
+ try_first_pass - try to get the password from a previous
+ PAM module, fall back to prompting the user
+ use_authtok - like try_first_pass, but *fail* if the new
+ PAM_AUTHTOK has not been previously set.
+ (intended for stacking password modules only)
+ not_set_pass - don't make passwords used by this module
+ available to other modules.
+ nodelay - don't insert ~1 second delays on authentication
+ failure.
+ nullok - null passwords are allowed.
+ nonull - null passwords are not allowed. Used to
+ override the Samba configuration.
+ migrate - only meaningful in an "auth" context;
+ used to update smbpasswd file with a
+ password used for successful authentication.
+ smbconf=<file> - specify an alternate path to the smb.conf
+ file.
+
+See the samples/ directory for example PAM configurations using this
+module.
+
+Thanks go to the following people:
+
+* Andrew Morgan <morgan@transmeta.com>, for providing the Linux-PAM
+framework, without which none of this would have happened
+
+* Christian Gafton <gafton@redhat.com> and Andrew Morgan again, for the
+pam_pwdb module upon which pam_smbpass was originally based
+
+* Luke Leighton <lkcl@switchboard.net> for being receptive to the idea,
+and for the occasional good-natured complaint about the project's status
+that keep me working on it :)
+
+* and of course, all the other members of the Samba team
+<http://www.samba.org/samba/team.html>, for creating a great product
+and for giving this project a purpose
+
+---------------------
+Stephen Langasek <vorlon@netexpress.net>
diff --git a/source/pam_smbpass/TODO b/source/pam_smbpass/TODO
new file mode 100644
index 00000000000..20cf4fb0987
--- /dev/null
+++ b/source/pam_smbpass/TODO
@@ -0,0 +1,7 @@
+This is a tentative TODO file which will probably get much longer before
+it gets much shorter.
+
+- Recognizing (and overriding) debug options in the smb.conf file
+- Support for 'name=value' parameters in the PAM config
+- Compliant handling of unrecognized PAM parameters (i.e., fail on error)
+-
diff --git a/source/pam_smbpass/general.h b/source/pam_smbpass/general.h
new file mode 100644
index 00000000000..4f13d601313
--- /dev/null
+++ b/source/pam_smbpass/general.h
@@ -0,0 +1,130 @@
+#ifndef LINUX
+/* This is only needed by modules in the Sun implementation. */
+#include <security/pam_appl.h>
+#endif /* LINUX */
+
+#include <security/pam_modules.h>
+
+#ifndef PAM_AUTHTOK_RECOVER_ERR
+#define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_RECOVERY_ERR
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+/*
+ * here is the string to inform the user that the new passwords they
+ * typed were not the same.
+ */
+
+#define MISTYPED_PASS "Sorry, passwords do not match"
+
+/* type definition for the control options */
+
+typedef struct {
+ const char *token;
+ unsigned int mask; /* shall assume 32 bits of flags */
+ unsigned int flag;
+} SMB_Ctrls;
+
+#ifndef False
+#define False (0)
+#endif
+
+#ifndef True
+#define True (1)
+#endif
+
+/* macro to determine if a given flag is on */
+#define on(x,ctrl) (smb_args[x].flag & ctrl)
+
+/* macro to determine that a given flag is NOT on */
+#define off(x,ctrl) (!on(x,ctrl))
+
+/* macro to turn on/off a ctrl flag manually */
+#define set(x,ctrl) (ctrl = ((ctrl)&smb_args[x].mask)|smb_args[x].flag)
+#define unset(x,ctrl) (ctrl &= ~(smb_args[x].flag))
+
+#ifndef __linux__
+#define strncasecmp(s1,s2,n) StrnCaseCmp(s1,s2,n)
+#endif
+
+/* the generic mask */
+#define _ALL_ON_ (~0U)
+
+/* end of macro definitions definitions for the control flags */
+
+/*
+ * These are the options supported by the smb password module, very
+ * similar to the pwdb options
+ */
+
+#define SMB__OLD_PASSWD 0 /* internal */
+#define SMB__VERIFY_PASSWD 1 /* internal */
+
+#define SMB_AUDIT 2 /* print more things than debug..
+ some information may be sensitive */
+#define SMB_USE_FIRST_PASS 3
+#define SMB_TRY_FIRST_PASS 4
+#define SMB_NOT_SET_PASS 5 /* don't set the AUTHTOK items */
+
+#define SMB__NONULL 6 /* internal */
+#define SMB__QUIET 7 /* internal */
+#define SMB_USE_AUTHTOK 8 /* insist on reading PAM_AUTHTOK */
+#define SMB__NULLOK 9 /* Null token ok */
+#define SMB_DEBUG 10 /* send more info to syslog(3) */
+#define SMB_NODELAY 11 /* admin does not want a fail-delay */
+#define SMB_MIGRATE 12 /* Does no authentication, just
+ updates the smb database. */
+#define SMB_CONF_FILE 13 /* Alternate location of smb.conf */
+
+#define SMB_CTRLS_ 14 /* number of ctrl arguments defined */
+
+static const SMB_Ctrls smb_args[SMB_CTRLS_] = {
+/* symbol token name ctrl mask ctrl *
+ * ------------------ ------------------ -------------- ---------- */
+
+/* SMB__OLD_PASSWD */ { NULL, _ALL_ON_, 01 },
+/* SMB__VERIFY_PASSWD */ { NULL, _ALL_ON_, 02 },
+/* SMB_AUDIT */ { "audit", _ALL_ON_, 04 },
+/* SMB_USE_FIRST_PASS */ { "use_first_pass", _ALL_ON_^(030), 010 },
+/* SMB_TRY_FIRST_PASS */ { "try_first_pass", _ALL_ON_^(030), 020 },
+/* SMB_NOT_SET_PASS */ { "not_set_pass", _ALL_ON_, 040 },
+/* SMB__NONULL */ { "nonull", _ALL_ON_, 0100 },
+/* SMB__QUIET */ { NULL, _ALL_ON_, 0200 },
+/* SMB_USE_AUTHTOK */ { "use_authtok", _ALL_ON_, 0400 },
+/* SMB__NULLOK */ { "nullok", _ALL_ON_^(0100), 0 },
+/* SMB_DEBUG */ { "debug", _ALL_ON_, 01000 },
+/* SMB_NODELAY */ { "nodelay", _ALL_ON_, 02000 },
+/* SMB_MIGRATE */ { "migrate", _ALL_ON_^(0100), 04000 },
+/* SMB_CONF_FILE */ { "smbconf=", _ALL_ON_, 0 },
+};
+
+#define SMB_DEFAULTS (smb_args[SMB__NONULL].flag)
+
+/*
+ * the following is used to keep track of the number of times a user fails
+ * to authenticate themself.
+ */
+
+#define FAIL_PREFIX "-SMB-FAIL-"
+#define SMB_MAX_RETRIES 3
+
+struct _pam_failed_auth {
+ char *user; /* user that's failed to be authenticated */
+ int id; /* uid of requested user */
+ char *agent; /* attempt from user with name */
+ int count; /* number of failures so far */
+};
+
+/*
+ * General use functions go here
+ */
+
+/* from support.c */
+int make_remark(pam_handle_t *, unsigned int, int, const char *);
diff --git a/source/pam_smbpass/pam_smb_acct.c b/source/pam_smbpass/pam_smb_acct.c
new file mode 100644
index 00000000000..2ea7eea7d87
--- /dev/null
+++ b/source/pam_smbpass/pam_smb_acct.c
@@ -0,0 +1,121 @@
+/* Unix NT password database implementation, version 0.7.5.
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 675
+ * Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* indicate the following groups are defined */
+#define PAM_SM_ACCT
+
+#include "includes.h"
+
+#ifndef LINUX
+
+/* This is only used in the Sun implementation. */
+#include <security/pam_appl.h>
+
+#endif /* LINUX */
+
+#include <security/pam_modules.h>
+
+#include "general.h"
+
+#include "support.h"
+
+
+/*
+ * pam_sm_acct_mgmt() verifies whether or not the account is disabled.
+ *
+ */
+
+int pam_sm_acct_mgmt( pam_handle_t *pamh, int flags,
+ int argc, const char **argv )
+{
+ unsigned int ctrl;
+ int retval;
+
+ const char *name;
+ SAM_ACCOUNT *sampass = NULL;
+ void (*oldsig_handler)(int);
+ extern BOOL in_client;
+
+ /* Samba initialization. */
+ setup_logging( "pam_smbpass", False );
+ in_client = True;
+
+ ctrl = set_ctrl( flags, argc, argv );
+
+ /* get the username */
+
+ retval = pam_get_user( pamh, &name, "Username: " );
+ if (retval != PAM_SUCCESS) {
+ if (on( SMB_DEBUG, ctrl )) {
+ _log_err( LOG_DEBUG, "acct: could not identify user" );
+ }
+ return retval;
+ }
+ if (on( SMB_DEBUG, ctrl )) {
+ _log_err( LOG_DEBUG, "acct: username [%s] obtained", name );
+ }
+
+ /* Getting into places that might use LDAP -- protect the app
+ from a SIGPIPE it's not expecting */
+ oldsig_handler = CatchSignal(SIGPIPE, SIGNAL_CAST SIG_IGN);
+ if (!initialize_password_db(True)) {
+ _log_err( LOG_ALERT, "Cannot access samba password database" );
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return PAM_AUTHINFO_UNAVAIL;
+ }
+
+ /* Get the user's record. */
+ pdb_init_sam(&sampass);
+ pdb_getsampwnam(sampass, name );
+
+ if (!sampass) {
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return PAM_USER_UNKNOWN;
+ }
+
+ if (pdb_get_acct_ctrl(sampass) & ACB_DISABLED) {
+ if (on( SMB_DEBUG, ctrl )) {
+ _log_err( LOG_DEBUG
+ , "acct: account %s is administratively disabled", name );
+ }
+ make_remark( pamh, ctrl, PAM_ERROR_MSG
+ , "Your account has been disabled; "
+ "please see your system administrator." );
+
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return PAM_ACCT_EXPIRED;
+ }
+
+ /* TODO: support for expired passwords. */
+
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return PAM_SUCCESS;
+}
+
+/* static module data */
+#ifdef PAM_STATIC
+struct pam_module _pam_smbpass_acct_modstruct = {
+ "pam_smbpass",
+ NULL,
+ NULL,
+ pam_sm_acct_mgmt,
+ NULL,
+ NULL,
+ NULL
+};
+#endif
+
diff --git a/source/pam_smbpass/pam_smb_auth.c b/source/pam_smbpass/pam_smb_auth.c
new file mode 100644
index 00000000000..4452538d32c
--- /dev/null
+++ b/source/pam_smbpass/pam_smb_auth.c
@@ -0,0 +1,256 @@
+/* Unix NT password database implementation, version 0.7.5.
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 675
+ * Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* indicate the following groups are defined */
+#define PAM_SM_AUTH
+
+#include "includes.h"
+#include "debug.h"
+
+#ifndef LINUX
+
+/* This is only used in the Sun implementation. */
+#include <security/pam_appl.h>
+
+#endif /* LINUX */
+
+#include <security/pam_modules.h>
+
+#include "general.h"
+
+#include "support.h"
+
+#define AUTH_RETURN \
+do { \
+ /* Restore application signal handler */ \
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler); \
+ if(ret_data) { \
+ *ret_data = retval; \
+ pam_set_data( pamh, "smb_setcred_return" \
+ , (void *) ret_data, NULL ); \
+ } \
+ return retval; \
+} while (0)
+
+static int _smb_add_user(pam_handle_t *pamh, unsigned int ctrl,
+ const char *name, SAM_ACCOUNT *sampass, BOOL exist);
+
+
+/*
+ * pam_sm_authenticate() authenticates users against the samba password file.
+ *
+ * First, obtain the password from the user. Then use a
+ * routine in 'support.c' to authenticate the user.
+ */
+
+#define _SMB_AUTHTOK "-SMB-PASS"
+
+int pam_sm_authenticate(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ unsigned int ctrl;
+ int retval, *ret_data = NULL;
+ SAM_ACCOUNT *sampass = NULL;
+ extern BOOL in_client;
+ const char *name;
+ void (*oldsig_handler)(int);
+ BOOL found;
+
+ /* Points to memory managed by the PAM library. Do not free. */
+ char *p = NULL;
+
+
+ /* Samba initialization. */
+ setup_logging("pam_smbpass",False);
+ in_client = True;
+
+ ctrl = set_ctrl(flags, argc, argv);
+
+ /* Get a few bytes so we can pass our return value to
+ pam_sm_setcred(). */
+ ret_data = malloc(sizeof(int));
+
+ /* get the username */
+ retval = pam_get_user( pamh, &name, "Username: " );
+ if ( retval != PAM_SUCCESS ) {
+ if (on( SMB_DEBUG, ctrl )) {
+ _log_err(LOG_DEBUG, "auth: could not identify user");
+ }
+ AUTH_RETURN;
+ }
+ if (on( SMB_DEBUG, ctrl )) {
+ _log_err( LOG_DEBUG, "username [%s] obtained", name );
+ }
+
+ /* Getting into places that might use LDAP -- protect the app
+ from a SIGPIPE it's not expecting */
+ oldsig_handler = CatchSignal(SIGPIPE, SIGNAL_CAST SIG_IGN);
+
+ if (!initialize_password_db(True)) {
+ _log_err( LOG_ALERT, "Cannot access samba password database" );
+ retval = PAM_AUTHINFO_UNAVAIL;
+ AUTH_RETURN;
+ }
+
+ pdb_init_sam(&sampass);
+
+ found = pdb_getsampwnam( sampass, name );
+
+ if (on( SMB_MIGRATE, ctrl )) {
+ retval = _smb_add_user(pamh, ctrl, name, sampass, found);
+ pdb_free_sam(&sampass);
+ AUTH_RETURN;
+ }
+
+ if (!found) {
+ _log_err(LOG_ALERT, "Failed to find entry for user %s.", name);
+ retval = PAM_USER_UNKNOWN;
+ pdb_free_sam(&sampass);
+ sampass = NULL;
+ AUTH_RETURN;
+ }
+
+ /* if this user does not have a password... */
+
+ if (_smb_blankpasswd( ctrl, sampass )) {
+ pdb_free_sam(&sampass);
+ retval = PAM_SUCCESS;
+ AUTH_RETURN;
+ }
+
+ /* get this user's authentication token */
+
+ retval = _smb_read_password(pamh, ctrl, NULL, "Password: ", NULL, _SMB_AUTHTOK, &p);
+ if (retval != PAM_SUCCESS ) {
+ _log_err(LOG_CRIT, "auth: no password provided for [%s]"
+ , name);
+ pdb_free_sam(&sampass);
+ AUTH_RETURN;
+ }
+
+ /* verify the password of this user */
+
+ retval = _smb_verify_password( pamh, sampass, p, ctrl );
+ pdb_free_sam(&sampass);
+ p = NULL;
+ AUTH_RETURN;
+}
+
+/*
+ * This function is for setting samba credentials. If anyone comes up
+ * with any credentials they think should be set, let me know.
+ */
+
+int pam_sm_setcred(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ int retval, *pretval = NULL;
+
+ retval = PAM_SUCCESS;
+
+ pam_get_data(pamh, "smb_setcred_return", (const void **) &pretval);
+ if(pretval) {
+ retval = *pretval;
+ SAFE_FREE(pretval);
+ }
+ pam_set_data(pamh, "smb_setcred_return", NULL, NULL);
+
+ return retval;
+}
+
+
+/* Helper function for adding a user to the db. */
+static int _smb_add_user(pam_handle_t *pamh, unsigned int ctrl,
+ const char *name, SAM_ACCOUNT *sampass, BOOL exist)
+{
+ pstring err_str;
+ pstring msg_str;
+ const char *pass = NULL;
+ int retval;
+
+ err_str[0] = '\0';
+ msg_str[0] = '\0';
+
+ /* Get the authtok; if we don't have one, silently fail. */
+ retval = pam_get_item( pamh, PAM_AUTHTOK, (const void **) &pass );
+
+ if (retval != PAM_SUCCESS) {
+ _log_err( LOG_ALERT
+ , "pam_get_item returned error to pam_sm_authenticate" );
+ return PAM_AUTHTOK_RECOVER_ERR;
+ } else if (pass == NULL) {
+ return PAM_AUTHTOK_RECOVER_ERR;
+ }
+
+ /* Add the user to the db if they aren't already there. */
+ if (!exist) {
+ retval = local_password_change( name, LOCAL_ADD_USER|LOCAL_SET_PASSWORD,
+ pass, err_str,
+ sizeof(err_str),
+ msg_str, sizeof(msg_str) );
+ if (!retval && *err_str)
+ {
+ err_str[PSTRING_LEN-1] = '\0';
+ make_remark( pamh, ctrl, PAM_ERROR_MSG, err_str );
+ }
+ else if (*msg_str)
+ {
+ msg_str[PSTRING_LEN-1] = '\0';
+ make_remark( pamh, ctrl, PAM_TEXT_INFO, msg_str );
+ }
+ pass = NULL;
+
+ return PAM_IGNORE;
+ }
+ else {
+ /* Change the user's password IFF it's null. */
+ if ((pdb_get_lanman_passwd(sampass) == NULL) && (pdb_get_acct_ctrl(sampass) & ACB_PWNOTREQ))
+ {
+ retval = local_password_change( name, 0, pass, err_str, sizeof(err_str),
+ msg_str, sizeof(msg_str) );
+ if (!retval && *err_str)
+ {
+ err_str[PSTRING_LEN-1] = '\0';
+ make_remark( pamh, ctrl, PAM_ERROR_MSG, err_str );
+ }
+ else if (*msg_str)
+ {
+ msg_str[PSTRING_LEN-1] = '\0';
+ make_remark( pamh, ctrl, PAM_TEXT_INFO, msg_str );
+ }
+ }
+ }
+
+ pass = NULL;
+
+ return PAM_IGNORE;
+}
+
+
+/* static module data */
+#ifdef PAM_STATIC
+struct pam_module _pam_smbpass_auth_modstruct = {
+ "pam_smbpass",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+#endif
+
diff --git a/source/pam_smbpass/pam_smb_passwd.c b/source/pam_smbpass/pam_smb_passwd.c
new file mode 100644
index 00000000000..bef587a916c
--- /dev/null
+++ b/source/pam_smbpass/pam_smb_passwd.c
@@ -0,0 +1,349 @@
+/*
+ Unix SMB/CIFS implementation.
+ Use PAM to update user passwords in the local SAM
+ Copyright (C) Steve Langasek 1998-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/* indicate the following groups are defined */
+#define PAM_SM_PASSWORD
+
+#include "includes.h"
+
+/* This is only used in the Sun implementation. FIXME: we really
+ want a define here that distinguishes between the Solaris PAM
+ and others (including FreeBSD). */
+
+#ifndef LINUX
+#include <security/pam_appl.h>
+#endif
+
+#include <security/pam_modules.h>
+
+#include "general.h"
+
+#include "support.h"
+
+int smb_update_db( pam_handle_t *pamh, int ctrl, const char *user, const char *pass_new )
+{
+ int retval;
+ pstring err_str;
+ pstring msg_str;
+
+ err_str[0] = '\0';
+ msg_str[0] = '\0';
+
+ retval = local_password_change( user, LOCAL_SET_PASSWORD, pass_new,
+ err_str, sizeof(err_str),
+ msg_str, sizeof(msg_str) );
+
+ if (!retval) {
+ if (*err_str) {
+ err_str[PSTRING_LEN-1] = '\0';
+ make_remark( pamh, ctrl, PAM_ERROR_MSG, err_str );
+ }
+
+ /* FIXME: what value is appropriate here? */
+ retval = PAM_AUTHTOK_ERR;
+ } else {
+ if (*msg_str) {
+ msg_str[PSTRING_LEN-1] = '\0';
+ make_remark( pamh, ctrl, PAM_TEXT_INFO, msg_str );
+ }
+ retval = PAM_SUCCESS;
+ }
+
+ return retval;
+}
+
+
+/* data tokens */
+
+#define _SMB_OLD_AUTHTOK "-SMB-OLD-PASS"
+#define _SMB_NEW_AUTHTOK "-SMB-NEW-PASS"
+
+/*
+ * FUNCTION: pam_sm_chauthtok()
+ *
+ * This function is called twice by the PAM library, once with
+ * PAM_PRELIM_CHECK set, and then again with PAM_UPDATE_AUTHTOK set. With
+ * Linux-PAM, these two passes generally involve first checking the old
+ * token and then changing the token. This is what we do here.
+ *
+ * Having obtained a new password. The function updates the
+ * SMB_PASSWD_FILE file (normally, $(LIBDIR)/smbpasswd).
+ */
+
+int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ unsigned int ctrl;
+ int retval;
+
+ extern BOOL in_client;
+
+ SAM_ACCOUNT *sampass = NULL;
+ void (*oldsig_handler)(int);
+ const char *user;
+ char *pass_old;
+ char *pass_new;
+
+ NTSTATUS nt_status;
+
+ /* Samba initialization. */
+ setup_logging( "pam_smbpass", False );
+ in_client = True;
+
+ ctrl = set_ctrl(flags, argc, argv);
+
+ /*
+ * First get the name of a user. No need to do anything if we can't
+ * determine this.
+ */
+
+ retval = pam_get_user( pamh, &user, "Username: " );
+ if (retval != PAM_SUCCESS) {
+ if (on( SMB_DEBUG, ctrl )) {
+ _log_err( LOG_DEBUG, "password: could not identify user" );
+ }
+ return retval;
+ }
+ if (on( SMB_DEBUG, ctrl )) {
+ _log_err( LOG_DEBUG, "username [%s] obtained", user );
+ }
+
+ /* Getting into places that might use LDAP -- protect the app
+ from a SIGPIPE it's not expecting */
+ oldsig_handler = CatchSignal(SIGPIPE, SIGNAL_CAST SIG_IGN);
+
+ if (!initialize_password_db(True)) {
+ _log_err( LOG_ALERT, "Cannot access samba password database" );
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return PAM_AUTHINFO_UNAVAIL;
+ }
+
+ /* obtain user record */
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam(&sampass))) {
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return nt_status_to_pam(nt_status);
+ }
+
+ if (!pdb_getsampwnam(sampass,user)) {
+ _log_err( LOG_ALERT, "Failed to find entry for user %s.", user );
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return PAM_USER_UNKNOWN;
+ }
+
+ if (flags & PAM_PRELIM_CHECK) {
+ /*
+ * obtain and verify the current password (OLDAUTHTOK) for
+ * the user.
+ */
+
+ char *Announce;
+
+ if (_smb_blankpasswd( ctrl, sampass )) {
+
+ pdb_free_sam(&sampass);
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return PAM_SUCCESS;
+ }
+
+ /* Password change by root, or for an expired token, doesn't
+ require authentication. Is this a good choice? */
+ if (getuid() != 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK)) {
+
+ /* tell user what is happening */
+#define greeting "Changing password for "
+ Announce = (char *) malloc(sizeof(greeting)+strlen(user));
+ if (Announce == NULL) {
+ _log_err(LOG_CRIT, "password: out of memory");
+ pdb_free_sam(&sampass);
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return PAM_BUF_ERR;
+ }
+ strncpy( Announce, greeting, sizeof(greeting) );
+ strncpy( Announce+sizeof(greeting)-1, user, strlen(user)+1 );
+#undef greeting
+
+ set( SMB__OLD_PASSWD, ctrl );
+ retval = _smb_read_password( pamh, ctrl, Announce, "Current SMB password: ",
+ NULL, _SMB_OLD_AUTHTOK, &pass_old );
+ SAFE_FREE( Announce );
+
+ if (retval != PAM_SUCCESS) {
+ _log_err( LOG_NOTICE
+ , "password - (old) token not obtained" );
+ pdb_free_sam(&sampass);
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return retval;
+ }
+
+ /* verify that this is the password for this user */
+
+ retval = _smb_verify_password( pamh, sampass, pass_old, ctrl );
+
+ } else {
+ pass_old = NULL;
+ retval = PAM_SUCCESS; /* root doesn't have to */
+ }
+
+ pass_old = NULL;
+ pdb_free_sam(&sampass);
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return retval;
+
+ } else if (flags & PAM_UPDATE_AUTHTOK) {
+
+ /*
+ * obtain the proposed password
+ */
+
+ /*
+ * get the old token back. NULL was ok only if root [at this
+ * point we assume that this has already been enforced on a
+ * previous call to this function].
+ */
+
+ if (off( SMB_NOT_SET_PASS, ctrl )) {
+ retval = pam_get_item( pamh, PAM_OLDAUTHTOK,
+ (const void **)&pass_old );
+ } else {
+ retval = pam_get_data( pamh, _SMB_OLD_AUTHTOK,
+ (const void **)&pass_old );
+ if (retval == PAM_NO_MODULE_DATA) {
+ pass_old = NULL;
+ retval = PAM_SUCCESS;
+ }
+ }
+
+ if (retval != PAM_SUCCESS) {
+ _log_err( LOG_NOTICE, "password: user not authenticated" );
+ pdb_free_sam(&sampass);
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return retval;
+ }
+
+ /*
+ * use_authtok is to force the use of a previously entered
+ * password -- needed for pluggable password strength checking
+ * or other module stacking
+ */
+
+ if (on( SMB_USE_AUTHTOK, ctrl )) {
+ set( SMB_USE_FIRST_PASS, ctrl );
+ }
+
+ retval = _smb_read_password( pamh, ctrl
+ , NULL
+ , "Enter new SMB password: "
+ , "Retype new SMB password: "
+ , _SMB_NEW_AUTHTOK
+ , &pass_new );
+
+ if (retval != PAM_SUCCESS) {
+ if (on( SMB_DEBUG, ctrl )) {
+ _log_err( LOG_ALERT
+ , "password: new password not obtained" );
+ }
+ pass_old = NULL; /* tidy up */
+ pdb_free_sam(&sampass);
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return retval;
+ }
+
+ /*
+ * At this point we know who the user is and what they
+ * propose as their new password. Verify that the new
+ * password is acceptable.
+ */
+
+ if (pass_new[0] == '\0') { /* "\0" password = NULL */
+ pass_new = NULL;
+ }
+
+ retval = _pam_smb_approve_pass(pamh, ctrl, pass_old, pass_new);
+
+ if (retval != PAM_SUCCESS) {
+ _log_err(LOG_NOTICE, "new password not acceptable");
+ pass_new = pass_old = NULL; /* tidy up */
+ pdb_free_sam(&sampass);
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return retval;
+ }
+
+ /*
+ * By reaching here we have approved the passwords and must now
+ * rebuild the smb password file.
+ */
+
+ /* update the password database */
+
+ retval = smb_update_db(pamh, ctrl, user, pass_new);
+ if (retval == PAM_SUCCESS) {
+ uid_t uid;
+
+ /* password updated */
+ if (!NT_STATUS_IS_OK(sid_to_uid(pdb_get_user_sid(sampass), &uid))) {
+ _log_err( LOG_NOTICE, "Unable to get uid for user %s",
+ pdb_get_username(sampass));
+ _log_err( LOG_NOTICE, "password for (%s) changed by (%s/%d)",
+ user, uidtoname(getuid()), getuid());
+ } else {
+ _log_err( LOG_NOTICE, "password for (%s/%d) changed by (%s/%d)",
+ user, uid, uidtoname(getuid()), getuid());
+ }
+ } else {
+ _log_err( LOG_ERR, "password change failed for user %s", user);
+ }
+
+ pass_old = pass_new = NULL;
+ if (sampass) {
+ pdb_free_sam(&sampass);
+ sampass = NULL;
+ }
+
+ } else { /* something has broken with the library */
+
+ _log_err( LOG_ALERT, "password received unknown request" );
+ retval = PAM_ABORT;
+
+ }
+
+ if (sampass) {
+ pdb_free_sam(&sampass);
+ sampass = NULL;
+ }
+
+ pdb_free_sam(&sampass);
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return retval;
+}
+
+/* static module data */
+#ifdef PAM_STATIC
+struct pam_module _pam_smbpass_passwd_modstruct = {
+ "pam_smbpass",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ pam_sm_chauthtok
+};
+#endif
+
diff --git a/source/pam_smbpass/samples/README b/source/pam_smbpass/samples/README
new file mode 100644
index 00000000000..d77603306f1
--- /dev/null
+++ b/source/pam_smbpass/samples/README
@@ -0,0 +1,3 @@
+This directory contains example configurations demonstrating various uses
+of pam_smbpass. These examples use Linux-style /etc/pam.d syntax, and
+must be modified for use on Solaris systems.
diff --git a/source/pam_smbpass/samples/kdc-pdc b/source/pam_smbpass/samples/kdc-pdc
new file mode 100644
index 00000000000..70f1998f32a
--- /dev/null
+++ b/source/pam_smbpass/samples/kdc-pdc
@@ -0,0 +1,15 @@
+#%PAM-1.0
+# kdc-pdc
+#
+# A sample PAM configuration that shows pam_smbpass used together with
+# pam_krb5. This could be useful on a Samba PDC that is also a member of
+# a Kerberos realm.
+
+auth requisite pam_nologin.so
+auth requisite pam_krb5.so
+auth optional pam_smbpass.so migrate
+account required pam_krb5.so
+password requisite pam_cracklib.so retry=3
+password optional pam_smbpass.so nullok use_authtok try_first_pass
+password required pam_krb5.so use_authtok try_first_pass
+session required pam_krb5.so
diff --git a/source/pam_smbpass/samples/password-mature b/source/pam_smbpass/samples/password-mature
new file mode 100644
index 00000000000..6d73e0906fc
--- /dev/null
+++ b/source/pam_smbpass/samples/password-mature
@@ -0,0 +1,14 @@
+#%PAM-1.0
+# password-mature
+#
+# A sample PAM configuration for a 'mature' smbpasswd installation.
+# private/smbpasswd is fully populated, and we consider it an error if
+# the smbpasswd doesn't exist or doesn't match the Unix password.
+
+auth requisite pam_nologin.so
+auth required pam_unix.so
+account required pam_unix.so
+password requisite pam_cracklib.so retry=3
+password requisite pam_unix.so shadow md5 use_authtok try_first_pass
+password required pam_smbpass.so use_authtok use_first_pass
+session required pam_unix.so
diff --git a/source/pam_smbpass/samples/password-migration b/source/pam_smbpass/samples/password-migration
new file mode 100644
index 00000000000..305cb53858e
--- /dev/null
+++ b/source/pam_smbpass/samples/password-migration
@@ -0,0 +1,18 @@
+#%PAM-1.0
+# password-migration
+#
+# A sample PAM configuration that shows the use of pam_smbpass to migrate
+# from plaintext to encrypted passwords for Samba. Unlike other methods,
+# this can be used for users who have never connected to Samba shares:
+# password migration takes place when users ftp in, login using ssh, pop
+# their mail, etc.
+
+auth requisite pam_nologin.so
+# pam_smbpass is called IFF pam_unix succeeds.
+auth requisite pam_unix.so
+auth optional pam_smbpass.so migrate
+account required pam_unix.so
+password requisite pam_cracklib.so retry=3
+password requisite pam_unix.so shadow md5 use_authtok try_first_pass
+password optional pam_smbpass.so nullok use_authtok try_first_pass
+session required pam_unix.so
diff --git a/source/pam_smbpass/samples/password-sync b/source/pam_smbpass/samples/password-sync
new file mode 100644
index 00000000000..0a950dd2e9a
--- /dev/null
+++ b/source/pam_smbpass/samples/password-sync
@@ -0,0 +1,15 @@
+#%PAM-1.0
+# password-sync
+#
+# A sample PAM configuration that shows the use of pam_smbpass to make
+# sure private/smbpasswd is kept in sync when /etc/passwd (/etc/shadow)
+# is changed. Useful when an expired password might be changed by an
+# application (such as ssh).
+
+auth requisite pam_nologin.so
+auth required pam_unix.so
+account required pam_unix.so
+password requisite pam_cracklib.so retry=3
+password requisite pam_unix.so shadow md5 use_authtok try_first_pass
+password required pam_smbpass.so nullok use_authtok try_first_pass
+session required pam_unix.so
diff --git a/source/pam_smbpass/support.c b/source/pam_smbpass/support.c
new file mode 100644
index 00000000000..8a0432c8550
--- /dev/null
+++ b/source/pam_smbpass/support.c
@@ -0,0 +1,635 @@
+ /* Unix NT password database implementation, version 0.6.
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 675
+ * Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+ #include "includes.h"
+ #include "general.h"
+
+ #include "support.h"
+
+
+ #define _pam_overwrite(x) \
+ do { \
+ register char *__xx__; \
+ if ((__xx__=(x))) \
+ while (*__xx__) \
+ *__xx__++ = '\0'; \
+ } while (0)
+
+ /*
+ * Don't just free it, forget it too.
+ */
+
+ #define _pam_drop(X) \
+ do { \
+ if (X) { \
+ free(X); \
+ X=NULL; \
+ } \
+ } while (0)
+
+ #define _pam_drop_reply(/* struct pam_response * */ reply, /* int */ replies) \
+ do { \
+ int reply_i; \
+ \
+ for (reply_i=0; reply_i<replies; ++reply_i) { \
+ if (reply[reply_i].resp) { \
+ _pam_overwrite(reply[reply_i].resp); \
+ free(reply[reply_i].resp); \
+ } \
+ } \
+ if (reply) \
+ free(reply); \
+ } while (0)
+
+
+ int converse(pam_handle_t *, int, int, struct pam_message **,
+ struct pam_response **);
+ int make_remark(pam_handle_t *, unsigned int, int, const char *);
+ void _cleanup(pam_handle_t *, void *, int);
+ char *_pam_delete(register char *);
+
+ /* default configuration file location */
+
+ char *servicesf = dyn_CONFIGFILE;
+
+ /* syslogging function for errors and other information */
+
+ void _log_err( int err, const char *format, ... )
+ {
+ va_list args;
+
+ va_start( args, format );
+ openlog( "PAM_smbpass", LOG_CONS | LOG_PID, LOG_AUTH );
+ vsyslog( err, format, args );
+ va_end( args );
+ closelog();
+ }
+
+ /* this is a front-end for module-application conversations */
+
+ int converse( pam_handle_t * pamh, int ctrl, int nargs
+ , struct pam_message **message
+ , struct pam_response **response )
+ {
+ int retval;
+ struct pam_conv *conv;
+
+ retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
+ if (retval == PAM_SUCCESS) {
+
+ retval = conv->conv(nargs, (const struct pam_message **) message
+ ,response, conv->appdata_ptr);
+
+ if (retval != PAM_SUCCESS && on(SMB_DEBUG, ctrl)) {
+ _log_err(LOG_DEBUG, "conversation failure [%s]"
+ ,pam_strerror(pamh, retval));
+ }
+ } else {
+ _log_err(LOG_ERR, "couldn't obtain coversation function [%s]"
+ ,pam_strerror(pamh, retval));
+ }
+
+ return retval; /* propagate error status */
+ }
+
+ int make_remark( pam_handle_t * pamh, unsigned int ctrl
+ , int type, const char *text )
+ {
+ if (off(SMB__QUIET, ctrl)) {
+ struct pam_message *pmsg[1], msg[1];
+ struct pam_response *resp;
+
+ pmsg[0] = &msg[0];
+ msg[0].msg = text;
+ msg[0].msg_style = type;
+ resp = NULL;
+
+ return converse(pamh, ctrl, 1, pmsg, &resp);
+ }
+ return PAM_SUCCESS;
+ }
+
+
+ /* set the control flags for the SMB module. */
+
+int set_ctrl( int flags, int argc, const char **argv )
+{
+ int i = 0;
+ const char *service_file = dyn_CONFIGFILE;
+ unsigned int ctrl;
+
+ ctrl = SMB_DEFAULTS; /* the default selection of options */
+
+ /* set some flags manually */
+
+ /* A good, sane default (matches Samba's behavior). */
+ set( SMB__NONULL, ctrl );
+
+ /* initialize service file location */
+ service_file=servicesf;
+
+ if (flags & PAM_SILENT) {
+ set( SMB__QUIET, ctrl );
+ }
+
+ /* Run through the arguments once, looking for an alternate smb config
+ file location */
+ while (i < argc) {
+ int j;
+
+ for (j = 0; j < SMB_CTRLS_; ++j) {
+ if (smb_args[j].token
+ && !strncmp(argv[i], smb_args[j].token, strlen(smb_args[j].token)))
+ {
+ break;
+ }
+ }
+
+ if (j == SMB_CONF_FILE) {
+ service_file = argv[i] + 8;
+ }
+ i++;
+ }
+
+ /* Read some options from the Samba config. Can be overridden by
+ the PAM config. */
+ if(lp_load(service_file,True,False,False) == False) {
+ _log_err( LOG_ERR, "Error loading service file %s", service_file );
+ }
+
+ secrets_init();
+
+ if (lp_null_passwords()) {
+ set( SMB__NULLOK, ctrl );
+ }
+
+ /* now parse the rest of the arguments to this module */
+
+ while (argc-- > 0) {
+ int j;
+
+ for (j = 0; j < SMB_CTRLS_; ++j) {
+ if (smb_args[j].token
+ && !strncmp(*argv, smb_args[j].token, strlen(smb_args[j].token)))
+ {
+ break;
+ }
+ }
+
+ if (j >= SMB_CTRLS_) {
+ _log_err( LOG_ERR, "unrecognized option [%s]", *argv );
+ } else {
+ ctrl &= smb_args[j].mask; /* for turning things off */
+ ctrl |= smb_args[j].flag; /* for turning things on */
+ }
+
+ ++argv; /* step to next argument */
+ }
+
+ /* auditing is a more sensitive version of debug */
+
+ if (on( SMB_AUDIT, ctrl )) {
+ set( SMB_DEBUG, ctrl );
+ }
+ /* return the set of flags */
+
+ return ctrl;
+}
+
+/* use this to free strings. ESPECIALLY password strings */
+
+char * _pam_delete( register char *xx )
+{
+ _pam_overwrite( xx );
+ _pam_drop( xx );
+ return NULL;
+}
+
+void _cleanup( pam_handle_t * pamh, void *x, int error_status )
+{
+ x = _pam_delete( (char *) x );
+}
+
+/* JHT
+ *
+ * Safe duplication of character strings. "Paranoid"; don't leave
+ * evidence of old token around for later stack analysis.
+ *
+ */
+char * smbpXstrDup( const char *x )
+{
+ register char *new = NULL;
+
+ if (x != NULL) {
+ register int i;
+
+ for (i = 0; x[i]; ++i); /* length of string */
+ if ((new = malloc(++i)) == NULL) {
+ i = 0;
+ _log_err( LOG_CRIT, "out of memory in smbpXstrDup" );
+ } else {
+ while (i-- > 0) {
+ new[i] = x[i];
+ }
+ }
+ x = NULL;
+ }
+ return new; /* return the duplicate or NULL on error */
+}
+
+/* ************************************************************** *
+ * Useful non-trivial functions *
+ * ************************************************************** */
+
+void _cleanup_failures( pam_handle_t * pamh, void *fl, int err )
+{
+ int quiet;
+ const char *service = NULL;
+ struct _pam_failed_auth *failure;
+
+#ifdef PAM_DATA_SILENT
+ quiet = err & PAM_DATA_SILENT; /* should we log something? */
+#else
+ quiet = 0;
+#endif
+#ifdef PAM_DATA_REPLACE
+ err &= PAM_DATA_REPLACE; /* are we just replacing data? */
+#endif
+ failure = (struct _pam_failed_auth *) fl;
+
+ if (failure != NULL) {
+
+#ifdef PAM_DATA_SILENT
+ if (!quiet && !err) { /* under advisement from Sun,may go away */
+#else
+ if (!quiet) { /* under advisement from Sun,may go away */
+#endif
+
+ /* log the number of authentication failures */
+ if (failure->count != 0) {
+ pam_get_item( pamh, PAM_SERVICE, (const void **) &service );
+ _log_err( LOG_NOTICE
+ , "%d authentication %s "
+ "from %s for service %s as %s(%d)"
+ , failure->count
+ , failure->count == 1 ? "failure" : "failures"
+ , failure->agent
+ , service == NULL ? "**unknown**" : service
+ , failure->user, failure->id );
+ if (failure->count > SMB_MAX_RETRIES) {
+ _log_err( LOG_ALERT
+ , "service(%s) ignoring max retries; %d > %d"
+ , service == NULL ? "**unknown**" : service
+ , failure->count
+ , SMB_MAX_RETRIES );
+ }
+ }
+ }
+ _pam_delete( failure->agent ); /* tidy up */
+ _pam_delete( failure->user ); /* tidy up */
+ SAFE_FREE( failure );
+ }
+}
+
+int _smb_verify_password( pam_handle_t * pamh, SAM_ACCOUNT *sampass,
+ const char *p, unsigned int ctrl )
+{
+ uchar lm_pw[16];
+ uchar nt_pw[16];
+ int retval = PAM_AUTH_ERR;
+ char *data_name;
+ const char *name;
+
+ if (!sampass)
+ return PAM_ABORT;
+
+ name = pdb_get_username(sampass);
+
+#ifdef HAVE_PAM_FAIL_DELAY
+ if (off( SMB_NODELAY, ctrl )) {
+ (void) pam_fail_delay( pamh, 1000000 ); /* 1 sec delay for on failure */
+ }
+#endif
+
+ if (!pdb_get_lanman_passwd(sampass))
+ {
+ _log_err( LOG_DEBUG, "user %s has null SMB password"
+ , name );
+
+ if (off( SMB__NONULL, ctrl )
+ && (pdb_get_acct_ctrl(sampass) & ACB_PWNOTREQ))
+ { /* this means we've succeeded */
+ return PAM_SUCCESS;
+ } else {
+ const char *service;
+
+ pam_get_item( pamh, PAM_SERVICE, (const void **)&service );
+ _log_err( LOG_NOTICE, "failed auth request by %s for service %s as %s",
+ uidtoname(getuid()), service ? service : "**unknown**", name);
+ return PAM_AUTH_ERR;
+ }
+ }
+
+ data_name = (char *) malloc( sizeof(FAIL_PREFIX) + strlen( name ));
+ if (data_name == NULL) {
+ _log_err( LOG_CRIT, "no memory for data-name" );
+ }
+ strncpy( data_name, FAIL_PREFIX, sizeof(FAIL_PREFIX) );
+ strncpy( data_name + sizeof(FAIL_PREFIX) - 1, name, strlen( name ) + 1 );
+
+ /*
+ * The password we were given wasn't an encrypted password, or it
+ * didn't match the one we have. We encrypt the password now and try
+ * again.
+ */
+
+ nt_lm_owf_gen(p, nt_pw, lm_pw);
+
+ /* the moment of truth -- do we agree with the password? */
+
+ if (!memcmp( nt_pw, pdb_get_nt_passwd(sampass), 16 )) {
+
+ retval = PAM_SUCCESS;
+ if (data_name) { /* reset failures */
+ pam_set_data(pamh, data_name, NULL, _cleanup_failures);
+ }
+ } else {
+
+ const char *service;
+
+ pam_get_item( pamh, PAM_SERVICE, (const void **)&service );
+
+ if (data_name != NULL) {
+ struct _pam_failed_auth *new = NULL;
+ const struct _pam_failed_auth *old = NULL;
+
+ /* get a failure recorder */
+
+ new = (struct _pam_failed_auth *)
+ malloc( sizeof(struct _pam_failed_auth) );
+
+ if (new != NULL) {
+
+ /* any previous failures for this user ? */
+ pam_get_data(pamh, data_name, (const void **) &old);
+
+ if (old != NULL) {
+ new->count = old->count + 1;
+ if (new->count >= SMB_MAX_RETRIES) {
+ retval = PAM_MAXTRIES;
+ }
+ } else {
+ _log_err(LOG_NOTICE,
+ "failed auth request by %s for service %s as %s",
+ uidtoname(getuid()),
+ service ? service : "**unknown**", name);
+ new->count = 1;
+ }
+ if (!NT_STATUS_IS_OK(sid_to_uid(pdb_get_user_sid(sampass), &(new->id)))) {
+ _log_err(LOG_NOTICE,
+ "failed auth request by %s for service %s as %s",
+ uidtoname(getuid()),
+ service ? service : "**unknown**", name);
+ }
+ new->user = smbpXstrDup( name );
+ new->agent = smbpXstrDup( uidtoname( getuid() ) );
+ pam_set_data( pamh, data_name, new, _cleanup_failures );
+
+ } else {
+ _log_err( LOG_CRIT, "no memory for failure recorder" );
+ _log_err(LOG_NOTICE,
+ "failed auth request by %s for service %s as %s(%d)",
+ uidtoname(getuid()),
+ service ? service : "**unknown**", name);
+ }
+ } else {
+ _log_err(LOG_NOTICE,
+ "failed auth request by %s for service %s as %s(%d)",
+ uidtoname(getuid()),
+ service ? service : "**unknown**", name);
+ retval = PAM_AUTH_ERR;
+ }
+ }
+
+ _pam_delete( data_name );
+
+ return retval;
+}
+
+
+/*
+ * _smb_blankpasswd() is a quick check for a blank password
+ *
+ * returns TRUE if user does not have a password
+ * - to avoid prompting for one in such cases (CG)
+ */
+
+int _smb_blankpasswd( unsigned int ctrl, SAM_ACCOUNT *sampass )
+{
+ int retval;
+
+ /*
+ * This function does not have to be too smart if something goes
+ * wrong, return FALSE and let this case to be treated somewhere
+ * else (CG)
+ */
+
+ if (on( SMB__NONULL, ctrl ))
+ return 0; /* will fail but don't let on yet */
+
+ if (pdb_get_lanman_passwd(sampass) == NULL)
+ retval = 1;
+ else
+ retval = 0;
+
+ return retval;
+}
+
+/*
+ * obtain a password from the user
+ */
+
+int _smb_read_password( pam_handle_t * pamh, unsigned int ctrl,
+ const char *comment, const char *prompt1,
+ const char *prompt2, const char *data_name, char **pass )
+{
+ int authtok_flag;
+ int retval;
+ char *item = NULL;
+ char *token;
+
+ struct pam_message msg[3], *pmsg[3];
+ struct pam_response *resp;
+ int i, expect;
+
+
+ /* make sure nothing inappropriate gets returned */
+
+ *pass = token = NULL;
+
+ /* which authentication token are we getting? */
+
+ authtok_flag = on(SMB__OLD_PASSWD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
+
+ /* should we obtain the password from a PAM item ? */
+
+ if (on(SMB_TRY_FIRST_PASS, ctrl) || on(SMB_USE_FIRST_PASS, ctrl)) {
+ retval = pam_get_item( pamh, authtok_flag, (const void **) &item );
+ if (retval != PAM_SUCCESS) {
+ /* very strange. */
+ _log_err( LOG_ALERT
+ , "pam_get_item returned error to smb_read_password" );
+ return retval;
+ } else if (item != NULL) { /* we have a password! */
+ *pass = item;
+ item = NULL;
+ return PAM_SUCCESS;
+ } else if (on( SMB_USE_FIRST_PASS, ctrl )) {
+ return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
+ } else if (on( SMB_USE_AUTHTOK, ctrl )
+ && off( SMB__OLD_PASSWD, ctrl ))
+ {
+ return PAM_AUTHTOK_RECOVER_ERR;
+ }
+ }
+
+ /*
+ * getting here implies we will have to get the password from the
+ * user directly.
+ */
+
+ /* prepare to converse */
+ if (comment != NULL && off(SMB__QUIET, ctrl)) {
+ pmsg[0] = &msg[0];
+ msg[0].msg_style = PAM_TEXT_INFO;
+ msg[0].msg = comment;
+ i = 1;
+ } else {
+ i = 0;
+ }
+
+ pmsg[i] = &msg[i];
+ msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[i++].msg = prompt1;
+
+ if (prompt2 != NULL) {
+ pmsg[i] = &msg[i];
+ msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[i++].msg = prompt2;
+ expect = 2;
+ } else
+ expect = 1;
+
+ resp = NULL;
+
+ retval = converse( pamh, ctrl, i, pmsg, &resp );
+
+ if (resp != NULL) {
+ int j = comment ? 1 : 0;
+ /* interpret the response */
+
+ if (retval == PAM_SUCCESS) { /* a good conversation */
+
+ token = smbpXstrDup(resp[j++].resp);
+ if (token != NULL) {
+ if (expect == 2) {
+ /* verify that password entered correctly */
+ if (!resp[j].resp || strcmp( token, resp[j].resp )) {
+ _pam_delete( token );
+ retval = PAM_AUTHTOK_RECOVER_ERR;
+ make_remark( pamh, ctrl, PAM_ERROR_MSG
+ , MISTYPED_PASS );
+ }
+ }
+ } else {
+ _log_err(LOG_NOTICE, "could not recover authentication token");
+ }
+ }
+
+ /* tidy up */
+ _pam_drop_reply( resp, expect );
+
+ } else {
+ retval = (retval == PAM_SUCCESS) ? PAM_AUTHTOK_RECOVER_ERR : retval;
+ }
+
+ if (retval != PAM_SUCCESS) {
+ if (on( SMB_DEBUG, ctrl ))
+ _log_err( LOG_DEBUG, "unable to obtain a password" );
+ return retval;
+ }
+ /* 'token' is the entered password */
+
+ if (off( SMB_NOT_SET_PASS, ctrl )) {
+
+ /* we store this password as an item */
+
+ retval = pam_set_item( pamh, authtok_flag, (const void *)token );
+ _pam_delete( token ); /* clean it up */
+ if (retval != PAM_SUCCESS
+ || (retval = pam_get_item( pamh, authtok_flag
+ ,(const void **)&item )) != PAM_SUCCESS)
+ {
+ _log_err( LOG_CRIT, "error manipulating password" );
+ return retval;
+ }
+ } else {
+ /*
+ * then store it as data specific to this module. pam_end()
+ * will arrange to clean it up.
+ */
+
+ retval = pam_set_data( pamh, data_name, (void *) token, _cleanup );
+ if (retval != PAM_SUCCESS
+ || (retval = pam_get_data( pamh, data_name, (const void **)&item ))
+ != PAM_SUCCESS)
+ {
+ _log_err( LOG_CRIT, "error manipulating password data [%s]"
+ , pam_strerror( pamh, retval ));
+ _pam_delete( token );
+ item = NULL;
+ return retval;
+ }
+ token = NULL; /* break link to password */
+ }
+
+ *pass = item;
+ item = NULL; /* break link to password */
+
+ return PAM_SUCCESS;
+}
+
+int _pam_smb_approve_pass(pam_handle_t * pamh,
+ unsigned int ctrl,
+ const char *pass_old,
+ const char *pass_new )
+{
+
+ /* Further checks should be handled through module stacking. -SRL */
+ if (pass_new == NULL || (pass_old && !strcmp( pass_old, pass_new )))
+ {
+ if (on(SMB_DEBUG, ctrl)) {
+ _log_err( LOG_DEBUG,
+ "passwd: bad authentication token (null or unchanged)" );
+ }
+ make_remark( pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ?
+ "No password supplied" : "Password unchanged" );
+ return PAM_AUTHTOK_ERR;
+ }
+
+ return PAM_SUCCESS;
+}
diff --git a/source/pam_smbpass/support.h b/source/pam_smbpass/support.h
new file mode 100644
index 00000000000..a13a2d0aeb9
--- /dev/null
+++ b/source/pam_smbpass/support.h
@@ -0,0 +1,50 @@
+/* syslogging function for errors and other information */
+extern void _log_err(int, const char *, ...);
+
+/* set the control flags for the UNIX module. */
+extern int set_ctrl(int, int, const char **);
+
+/* generic function for freeing pam data segments */
+extern void _cleanup(pam_handle_t *, void *, int);
+
+/*
+ * Safe duplication of character strings. "Paranoid"; don't leave
+ * evidence of old token around for later stack analysis.
+ */
+
+extern char *smbpXstrDup(const char *);
+
+/* ************************************************************** *
+ * Useful non-trivial functions *
+ * ************************************************************** */
+
+extern void _cleanup_failures(pam_handle_t *, void *, int);
+
+/* compare 2 strings */
+extern BOOL strequal(const char *, const char *);
+
+extern struct smb_passwd *
+_my_get_smbpwnam(FILE *, const char *, BOOL *, BOOL *, long *);
+
+extern int _smb_verify_password( pam_handle_t *pamh , SAM_ACCOUNT *sampass,
+ const char *p, unsigned int ctrl );
+
+/*
+ * this function obtains the name of the current user and ensures
+ * that the PAM_USER item is set to this value
+ */
+
+extern int _smb_get_user(pam_handle_t *, unsigned int,
+ const char *, const char **);
+
+/* _smb_blankpasswd() is a quick check for a blank password */
+
+extern int _smb_blankpasswd(unsigned int, SAM_ACCOUNT *);
+
+
+/* obtain a password from the user */
+extern int _smb_read_password( pam_handle_t *, unsigned int, const char*,
+ const char *, const char *, const char *, char **);
+
+extern int _pam_smb_approve_pass(pam_handle_t *, unsigned int, const char *,
+ const char *);
diff --git a/source/param/.cvsignore b/source/param/.cvsignore
new file mode 100644
index 00000000000..76e2d7ff3eb
--- /dev/null
+++ b/source/param/.cvsignore
@@ -0,0 +1,3 @@
+*.po32
+*.po
+
diff --git a/source/param/config_ldap.c b/source/param/config_ldap.c
new file mode 100644
index 00000000000..fe4693fb583
--- /dev/null
+++ b/source/param/config_ldap.c
@@ -0,0 +1,351 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ ModConfig LDAP backend
+
+ Copyright (C) Simo Sorce 2003
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+ Copyright (C) Gerald Carter 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_CONFIG
+*/
+
+#include <lber.h>
+#include <ldap.h>
+
+#include "smbldap.h"
+
+#define LDAP_OBJ_SAMBA_CONFIG "sambaConfig"
+#define LDAP_OBJ_SAMBA_SHARE "sambaShare"
+#define LDAP_OBJ_SAMBA_OPTION "sambaConfigOption"
+
+#define LDAP_ATTR_LIST_END 0
+#define LDAP_ATTR_BOOL 1
+#define LDAP_ATTR_INTEGER 2
+#define LDAP_ATTR_STRING 3
+#define LDAP_ATTR_LIST 4
+#define LDAP_ATTR_NAME 5
+
+
+struct ldap_config_state {
+ struct smbldap_state *smbldap_state;
+ TALLOC_CTX *mem_ctx;
+};
+
+ATTRIB_MAP_ENTRY option_attr_list[] = {
+ { LDAP_ATTR_NAME, "sambaOptionName" },
+ { LDAP_ATTR_LIST, "sambaListOption" },
+ { LDAP_ATTR_STRING, "sambaStringOption" },
+ { LDAP_ATTR_INTEGER, "sambaIntegerOption" },
+ { LDAP_ATTR_BOOL, "sambaBoolOption" },
+ { LDAP_ATTR_LIST_END, NULL }
+};
+
+static struct ldap_config_state ldap_state;
+static char *config_base_dn;
+
+static NTSTATUS ldap_config_close(void);
+
+/*
+TODO:
+ search each section
+ start with global, then with others
+ for each section parse all options
+*/
+
+static NTSTATUS parse_section(
+ const char *dn,
+ BOOL (*pfunc)(const char *, const char *))
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ pstring filter;
+ pstring option_name;
+ pstring option_value;
+ char **attr_list = NULL;
+ int rc;
+ int count;
+
+ mem_ctx = talloc_init("parse_section");
+
+ /* search for the options */
+ pstr_sprintf(filter, "objectClass=%s",
+ LDAP_OBJ_SAMBA_OPTION);
+
+ DEBUG(0, ("Searching for:[%s]\n", filter));
+
+ attr_list = get_attr_list(option_attr_list);
+ rc = smbldap_search(ldap_state.smbldap_state,
+ dn, LDAP_SCOPE_ONELEVEL,
+ filter, attr_list, 0, &result);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0,("parse_section: %s object not found\n", LDAP_OBJ_SAMBA_CONFIG));
+ goto done;
+ }
+
+ count = ldap_count_entries(ldap_state.smbldap_state->ldap_struct, result);
+ entry = ldap_first_entry(ldap_state.smbldap_state->ldap_struct, result);
+ while (entry) {
+ int o;
+
+ if (!smbldap_get_single_pstring(ldap_state.smbldap_state->ldap_struct, entry, "sambaOptionName", option_name)) {
+ goto done;
+ }
+
+ option_value[0] = '\0';
+ for (o = 1; option_attr_list[o].name != NULL; o++) {
+ if (smbldap_get_single_pstring(ldap_state.smbldap_state->ldap_struct, entry, option_attr_list[o].name, option_value)) {
+ break;
+ }
+ }
+ if (option_value[0] != '\0') {
+ if (!pfunc(option_name, option_value)) {
+ goto done;
+ }
+ } else {
+ DEBUG(0,("parse_section: Missing value for option: %s\n", option_name));
+ goto done;
+ }
+
+ entry = ldap_next_entry(ldap_state.smbldap_state->ldap_struct, entry);
+ }
+
+ ret = NT_STATUS_OK;
+
+done:
+ talloc_destroy(mem_ctx);
+ free_attr_list(attr_list);
+ if (result) ldap_msgfree(result);
+
+ return ret;
+}
+
+/*****************************************************************************
+ load configuration from ldap
+*****************************************************************************/
+
+static NTSTATUS ldap_config_load(
+ BOOL (*sfunc)(const char *),
+ BOOL (*pfunc)(const char *, const char *))
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ pstring filter;
+ pstring attr_text;
+ char *config_dn = NULL;
+ char *temp;
+ int rc;
+ int count;
+ const char *config_attr_list[] = {"description", NULL};
+ const char *share_attr_list[] = {"sambaShareName", "description", NULL};
+ char **share_dn;
+ char **share_name;
+
+ mem_ctx = talloc_init("ldap_config_load");
+
+ /* search for the base config dn */
+ pstr_sprintf(filter, "objectClass=%s",
+ LDAP_OBJ_SAMBA_CONFIG);
+
+ DEBUG(0, ("Searching for:[%s]\n", filter));
+
+ rc = smbldap_search(ldap_state.smbldap_state,
+ config_base_dn, LDAP_SCOPE_SUBTREE,
+ filter, config_attr_list, 0, &result);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0,("ldap_config_load: %s object not found\n", LDAP_OBJ_SAMBA_CONFIG));
+ goto done;
+ }
+
+ count = ldap_count_entries(ldap_state.smbldap_state->ldap_struct, result);
+ if (count != 1) {
+ DEBUG(0,("ldap_config_load: single %s object not found\n", LDAP_OBJ_SAMBA_CONFIG));
+ goto done;
+ }
+
+ if (!(temp = smbldap_get_dn(ldap_state.smbldap_state->ldap_struct, result))) {
+ goto done;
+ }
+ config_dn = talloc_strdup(mem_ctx, temp);
+ SAFE_FREE(temp);
+ if (!config_dn) {
+ goto done;
+ }
+
+ entry = ldap_first_entry(ldap_state.smbldap_state->ldap_struct, result);
+
+ if (!smbldap_get_single_pstring(ldap_state.smbldap_state->ldap_struct, entry, "description", attr_text)) {
+ DEBUG(0, ("ldap_config_load: no description field in %s object\n", LDAP_OBJ_SAMBA_CONFIG));
+ }
+
+ if (result) ldap_msgfree(result);
+/* TODO: finish up the last section, see loadparm's lp_load()*/
+
+ /* retrive the section list */
+ pstr_sprintf(filter, "objectClass=%s",
+ LDAP_OBJ_SAMBA_SHARE);
+
+ DEBUG(0, ("Searching for:[%s]\n", filter));
+
+ rc = smbldap_search(ldap_state.smbldap_state,
+ config_dn, LDAP_SCOPE_SUBTREE,
+ filter, share_attr_list, 0, &result);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0,("ldap_config_load: %s object not found\n", LDAP_OBJ_SAMBA_CONFIG));
+ goto done;
+ }
+
+ count = ldap_count_entries(ldap_state.smbldap_state->ldap_struct, result);
+ DEBUG(0, ("config_ldap: Found %d shares\n", count));
+ if (count) {
+ int i;
+
+ share_dn = talloc(mem_ctx, (count + 1) * sizeof(char *));
+ share_name = talloc(mem_ctx, (count) * sizeof(char *));
+ if (!share_dn || !share_name) {
+ DEBUG(0,("config_ldap: Out of memory!\n"));
+ goto done;
+ }
+ entry = ldap_first_entry(ldap_state.smbldap_state->ldap_struct, result);
+ i = 0;
+ while (entry) {
+ if (!(temp = smbldap_get_dn(ldap_state.smbldap_state->ldap_struct, entry))) {
+ goto done;
+ }
+ if (!smbldap_get_single_pstring(ldap_state.smbldap_state->ldap_struct, entry, "sambaShareName", attr_text)) {
+ goto done;
+ }
+ share_dn[i] = talloc_strdup(mem_ctx, temp);
+ share_name[i] = talloc_strdup(mem_ctx, attr_text);
+ if (!share_dn[i] || !share_name[i]) {
+ DEBUG(0,("config_ldap: Out of memory!\n"));
+ goto done;
+ }
+
+ DEBUG(0, ("config_ldap: Found share [%s] (%s)\n", attr_text, temp));
+ SAFE_FREE(temp);
+
+ entry = ldap_next_entry(ldap_state.smbldap_state->ldap_struct, entry);
+ i++;
+ if (entry && (count == i)) {
+ DEBUG(0, ("Error too many entryes in ldap result\n"));
+ goto done;
+ }
+ }
+ share_dn[i] = NULL;
+ }
+
+ /* parse global section*/
+ if (!sfunc("global")) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(parse_section(config_dn, pfunc))) {
+ goto done;
+ } else { /* parse shares */
+ int i;
+
+ for (i = 0; share_dn[i] != NULL; i++) {
+ if (!sfunc(share_name[i])) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(parse_section(share_dn[i], pfunc))) {
+ goto done;
+ }
+ }
+ }
+
+done:
+ talloc_destroy(mem_ctx);
+ if (result) ldap_msgfree(result);
+
+ return ret;
+}
+
+/*****************************************************************************
+ Initialise config_ldap module
+*****************************************************************************/
+
+static NTSTATUS ldap_config_init(char *params)
+{
+ NTSTATUS nt_status;
+ const char *location;
+ const char *basedn;
+
+ ldap_state.mem_ctx = talloc_init("config_ldap");
+ if (!ldap_state.mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* we assume only location is passed through an inline parameter
+ * other options go via parametrical options */
+ if (params) {
+ location = params;
+ } else {
+ location = lp_parm_const_string(GLOBAL_SECTION_SNUM, "config_ldap", "url", "ldap://localhost");
+ }
+ DEBUG(0,("config_ldap: location=%s\n", location));
+ basedn = lp_parm_const_string(GLOBAL_SECTION_SNUM, "config_ldap", "basedn", NULL);
+ if (basedn) config_base_dn = smb_xstrdup(basedn);
+
+ if (!NT_STATUS_IS_OK(nt_status =
+ smbldap_init(ldap_state.mem_ctx, location,
+ &ldap_state.smbldap_state))) {
+ talloc_destroy(ldap_state.mem_ctx);
+ DEBUG(0,("config_ldap: smbldap_init failed!\n"));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*****************************************************************************
+ End the LDAP session
+*****************************************************************************/
+
+static NTSTATUS ldap_config_close(void)
+{
+
+ smbldap_free_struct(&(ldap_state).smbldap_state);
+ talloc_destroy(ldap_state.mem_ctx);
+
+ DEBUG(5,("The connection to the LDAP server was closed\n"));
+ /* maybe free the results here --metze */
+
+ return NT_STATUS_OK;
+}
+
+static struct config_functions functions = {
+ ldap_config_init,
+ ldap_config_load,
+ ldap_config_close
+};
+
+NTSTATUS config_ldap_init(void)
+{
+ return smb_register_config(SAMBA_CONFIG_INTERFACE_VERSION, "ldap", &functions);
+}
diff --git a/source/param/loadparm.c b/source/param/loadparm.c
new file mode 100644
index 00000000000..b92fa64ee0c
--- /dev/null
+++ b/source/param/loadparm.c
@@ -0,0 +1,4277 @@
+/*
+ Unix SMB/CIFS implementation.
+ Parameter loading functions
+ Copyright (C) Karl Auer 1993-1998
+
+ Largely re-written by Andrew Tridgell, September 1994
+
+ Copyright (C) Simo Sorce 2001
+ Copyright (C) Alexander Bokovoy 2002
+ Copyright (C) Stefan (metze) Metzmacher 2002
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ * Load parameters.
+ *
+ * This module provides suitable callback functions for the params
+ * module. It builds the internal table of service details which is
+ * then used by the rest of the server.
+ *
+ * To add a parameter:
+ *
+ * 1) add it to the global or service structure definition
+ * 2) add it to the parm_table
+ * 3) add it to the list of available functions (eg: using FN_GLOBAL_STRING())
+ * 4) If it's a global then initialise it in init_globals. If a local
+ * (ie. service) parameter then initialise it in the sDefault structure
+ *
+ *
+ * Notes:
+ * The configuration file is processed sequentially for speed. It is NOT
+ * accessed randomly as happens in 'real' Windows. For this reason, there
+ * is a fair bit of sequence-dependent code here - ie., code which assumes
+ * that certain things happen before others. In particular, the code which
+ * happens at the boundary between sections is delicately poised, so be
+ * careful!
+ *
+ */
+
+#include "includes.h"
+
+BOOL in_client = False; /* Not in the client by default */
+BOOL bLoaded = False;
+
+extern userdom_struct current_user_info;
+extern pstring user_socket_options;
+
+#ifndef GLOBAL_NAME
+#define GLOBAL_NAME "global"
+#endif
+
+#ifndef PRINTERS_NAME
+#define PRINTERS_NAME "printers"
+#endif
+
+#ifndef HOMES_NAME
+#define HOMES_NAME "homes"
+#endif
+
+/* some helpful bits */
+#define LP_SNUM_OK(i) (((i) >= 0) && ((i) < iNumServices) && ServicePtrs[(i)]->valid)
+#define VALID(i) ServicePtrs[i]->valid
+
+int keepalive = DEFAULT_KEEPALIVE;
+BOOL use_getwd_cache = True;
+
+extern int extra_time_offset;
+
+static BOOL defaults_saved = False;
+
+typedef struct _param_opt_struct param_opt_struct;
+struct _param_opt_struct {
+ param_opt_struct *prev, *next;
+ char *key;
+ char *value;
+ char **list;
+};
+
+/*
+ * This structure describes global (ie., server-wide) parameters.
+ */
+typedef struct
+{
+ char *szConfigBackend;
+ char *smb_ports;
+ char *dos_charset;
+ char *unix_charset;
+ char *display_charset;
+ char *szPrintcapname;
+ char *szEnumPortsCommand;
+ char *szAddPrinterCommand;
+ char *szDeletePrinterCommand;
+ char *szOs2DriverMap;
+ char *szLockDir;
+ char *szPidDir;
+ char *szRootdir;
+ char *szDefaultService;
+ char *szDfree;
+ char *szGetQuota;
+ char *szSetQuota;
+ char *szMsgCommand;
+ char *szHostsEquiv;
+ char *szServerString;
+ char *szAutoServices;
+ char *szPasswdProgram;
+ char *szPasswdChat;
+ char *szLogFile;
+ char *szConfigFile;
+ char *szSMBPasswdFile;
+ char *szPrivateDir;
+ char **szPassdbBackend;
+ char *szGumsBackend;
+ char **szPreloadModules;
+ char *szPasswordServer;
+ char *szSocketOptions;
+ char *szRealm;
+ char *szAfsUsernameMap;
+ char *szUsernameMap;
+ char *szLogonScript;
+ char *szLogonPath;
+ char *szLogonDrive;
+ char *szLogonHome;
+ char **szWINSservers;
+ char **szInterfaces;
+ char *szRemoteAnnounce;
+ char *szRemoteBrowseSync;
+ char *szSocketAddress;
+ char *szNISHomeMapName;
+ char *szAnnounceVersion; /* This is initialised in init_globals */
+ char *szWorkgroup;
+ char *szNetbiosName;
+ char **szNetbiosAliases;
+ char *szNetbiosScope;
+ char *szDomainOtherSIDs;
+ char *szNameResolveOrder;
+ char *szPanicAction;
+ char *szAddUserScript;
+ char *szDelUserScript;
+ char *szAddGroupScript;
+ char *szDelGroupScript;
+ char *szAddUserToGroupScript;
+ char *szDelUserFromGroupScript;
+ char *szSetPrimaryGroupScript;
+ char *szAddMachineScript;
+ char *szShutdownScript;
+ char *szAbortShutdownScript;
+ char *szWINSHook;
+ char *szWINSPartners;
+ char *szUtmpDir;
+ char *szWtmpDir;
+ BOOL bUtmp;
+ char *szIdmapUID;
+ char *szIdmapGID;
+ BOOL bEnableRidAlgorithm;
+ int AlgorithmicRidBase;
+ char *szTemplatePrimaryGroup;
+ char *szTemplateHomedir;
+ char *szTemplateShell;
+ char *szWinbindSeparator;
+ BOOL bWinbindEnableLocalAccounts;
+ BOOL bWinbindEnumUsers;
+ BOOL bWinbindEnumGroups;
+ BOOL bWinbindUseDefaultDomain;
+ BOOL bWinbindTrustedDomainsOnly;
+ char *szWinbindBackend;
+ char *szIdmapBackend;
+ char *szAddShareCommand;
+ char *szChangeShareCommand;
+ char *szDeleteShareCommand;
+ char *szGuestaccount;
+ char *szManglingMethod;
+ int mangle_prefix;
+ int max_log_size;
+ char *szLogLevel;
+ int max_xmit;
+ int max_mux;
+ int max_open_files;
+ int pwordlevel;
+ int unamelevel;
+ int deadtime;
+ int maxprotocol;
+ int minprotocol;
+ int security;
+ char **AuthMethods;
+ BOOL paranoid_server_security;
+ int maxdisksize;
+ int lpqcachetime;
+ int iMaxSmbdProcesses;
+ BOOL bDisableSpoolss;
+ int iTotalPrintJobs;
+ int syslog;
+ int os_level;
+ int enhanced_browsing;
+ int max_ttl;
+ int max_wins_ttl;
+ int min_wins_ttl;
+ int lm_announce;
+ int lm_interval;
+ int announce_as; /* This is initialised in init_globals */
+ int machine_password_timeout;
+ int change_notify_timeout;
+ int map_to_guest;
+ int min_passwd_length;
+ BOOL use_cracklib;
+ int oplock_break_wait_time;
+ int winbind_cache_time;
+ int iLockSpinCount;
+ int iLockSpinTime;
+ char *szLdapMachineSuffix;
+ char *szLdapUserSuffix;
+ char *szLdapIdmapSuffix;
+ char *szLdapGroupSuffix;
+ char *szLdapPrivilegeSuffix;
+#ifdef WITH_LDAP_SAMCONFIG
+ int ldap_port;
+ char *szLdapServer;
+#endif
+ int ldap_ssl;
+ char *szLdapSuffix;
+ char *szLdapFilter;
+ char *szLdapAdminDn;
+ char *szAclCompat;
+ int ldap_passwd_sync;
+ int ldap_replication_sleep;
+ BOOL ldap_delete_dn;
+ BOOL bMsAddPrinterWizard;
+ BOOL bDNSproxy;
+ BOOL bWINSsupport;
+ BOOL bWINSproxy;
+ BOOL bLocalMaster;
+ BOOL bPreferredMaster;
+ BOOL bDomainMaster;
+ BOOL bDomainLogons;
+ BOOL bEncryptPasswords;
+ BOOL bUpdateEncrypt;
+ int clientSchannel;
+ int serverSchannel;
+ BOOL bNullPasswords;
+ BOOL bObeyPamRestrictions;
+ BOOL bLoadPrinters;
+ BOOL bLargeReadwrite;
+ BOOL bReadRaw;
+ BOOL bWriteRaw;
+ BOOL bReadbmpx;
+ BOOL bSyslogOnly;
+ BOOL bBrowseList;
+ BOOL bNISHomeMap;
+ BOOL bTimeServer;
+ BOOL bBindInterfacesOnly;
+ BOOL bPamPasswordChange;
+ BOOL bUnixPasswdSync;
+ BOOL bPasswdChatDebug;
+ int iPasswdChatTimeout;
+ BOOL bTimestampLogs;
+ BOOL bNTSmbSupport;
+ BOOL bNTPipeSupport;
+ BOOL bNTStatusSupport;
+ BOOL bStatCache;
+ BOOL bKernelOplocks;
+ BOOL bAllowTrustedDomains;
+ BOOL bLanmanAuth;
+ BOOL bNTLMAuth;
+ BOOL bUseSpnego;
+ BOOL bClientLanManAuth;
+ BOOL bClientNTLMv2Auth;
+ BOOL bClientPlaintextAuth;
+ BOOL bClientUseSpnego;
+ BOOL bDebugHiresTimestamp;
+ BOOL bDebugPid;
+ BOOL bDebugUid;
+ BOOL bHostMSDfs;
+ BOOL bUnicode;
+ BOOL bUseMmap;
+ BOOL bHostnameLookups;
+ BOOL bUnixExtensions;
+ BOOL bDisableNetbios;
+ BOOL bKernelChangeNotify;
+ int restrict_anonymous;
+ int name_cache_timeout;
+ int client_signing;
+ int server_signing;
+ param_opt_struct *param_opt;
+}
+global;
+
+static global Globals;
+
+/*
+ * This structure describes a single service.
+ */
+typedef struct
+{
+ BOOL valid;
+ BOOL autoloaded;
+ char *szService;
+ char *szPath;
+ char *szUsername;
+ char **szInvalidUsers;
+ char **szValidUsers;
+ char **szAdminUsers;
+ char *szCopy;
+ char *szInclude;
+ char *szPreExec;
+ char *szPostExec;
+ char *szRootPreExec;
+ char *szRootPostExec;
+ char *szPrintcommand;
+ char *szLpqcommand;
+ char *szLprmcommand;
+ char *szLppausecommand;
+ char *szLpresumecommand;
+ char *szQueuepausecommand;
+ char *szQueueresumecommand;
+ char *szPrintername;
+ char *szDontdescend;
+ char **szHostsallow;
+ char **szHostsdeny;
+ char *szMagicScript;
+ char *szMagicOutput;
+ char *szMangledMap;
+ char *szVetoFiles;
+ char *szHideFiles;
+ char *szVetoOplockFiles;
+ char *comment;
+ char *force_user;
+ char *force_group;
+ char **readlist;
+ char **writelist;
+ char **printer_admin;
+ char *volume;
+ char *fstype;
+ char **szVfsObjects;
+ char *szMSDfsProxy;
+ int iMinPrintSpace;
+ int iMaxPrintJobs;
+ int iMaxReportedPrintJobs;
+ int iWriteCacheSize;
+ int iCreate_mask;
+ int iCreate_force_mode;
+ int iSecurity_mask;
+ int iSecurity_force_mode;
+ int iDir_mask;
+ int iDir_force_mode;
+ int iDir_Security_mask;
+ int iDir_Security_force_mode;
+ int iMaxConnections;
+ int iDefaultCase;
+ int iPrinting;
+ int iOplockContentionLimit;
+ int iCSCPolicy;
+ int iBlock_size;
+ BOOL bPreexecClose;
+ BOOL bRootpreexecClose;
+ BOOL bCaseSensitive;
+ BOOL bCasePreserve;
+ BOOL bShortCasePreserve;
+ BOOL bCaseMangle;
+ BOOL bHideDotFiles;
+ BOOL bHideSpecialFiles;
+ BOOL bHideUnReadable;
+ BOOL bHideUnWriteableFiles;
+ BOOL bBrowseable;
+ BOOL bAvailable;
+ BOOL bRead_only;
+ BOOL bNo_set_dir;
+ BOOL bGuest_only;
+ BOOL bGuest_ok;
+ BOOL bPrint_ok;
+ BOOL bMap_system;
+ BOOL bMap_hidden;
+ BOOL bMap_archive;
+ BOOL bStoreDosAttributes;
+ BOOL bLocking;
+ BOOL bStrictLocking;
+ BOOL bPosixLocking;
+ BOOL bShareModes;
+ BOOL bOpLocks;
+ BOOL bLevel2OpLocks;
+ BOOL bOnlyUser;
+ BOOL bMangledNames;
+ BOOL bWidelinks;
+ BOOL bSymlinks;
+ BOOL bSyncAlways;
+ BOOL bStrictAllocate;
+ BOOL bStrictSync;
+ char magic_char;
+ BOOL *copymap;
+ BOOL bDeleteReadonly;
+ BOOL bFakeOplocks;
+ BOOL bDeleteVetoFiles;
+ BOOL bDosFilemode;
+ BOOL bDosFiletimes;
+ BOOL bDosFiletimeResolution;
+ BOOL bFakeDirCreateTimes;
+ BOOL bBlockingLocks;
+ BOOL bInheritPerms;
+ BOOL bInheritACLS;
+ BOOL bMSDfsRoot;
+ BOOL bUseClientDriver;
+ BOOL bDefaultDevmode;
+ BOOL bNTAclSupport;
+ BOOL bUseSendfile;
+ BOOL bProfileAcls;
+ BOOL bMap_acl_inherit;
+ BOOL bAfs_Share;
+ BOOL bEASupport;
+ param_opt_struct *param_opt;
+
+ char dummy[3]; /* for alignment */
+}
+service;
+
+
+/* This is a default service used to prime a services structure */
+static service sDefault = {
+ True, /* valid */
+ False, /* not autoloaded */
+ NULL, /* szService */
+ NULL, /* szPath */
+ NULL, /* szUsername */
+ NULL, /* szInvalidUsers */
+ NULL, /* szValidUsers */
+ NULL, /* szAdminUsers */
+ NULL, /* szCopy */
+ NULL, /* szInclude */
+ NULL, /* szPreExec */
+ NULL, /* szPostExec */
+ NULL, /* szRootPreExec */
+ NULL, /* szRootPostExec */
+ NULL, /* szPrintcommand */
+ NULL, /* szLpqcommand */
+ NULL, /* szLprmcommand */
+ NULL, /* szLppausecommand */
+ NULL, /* szLpresumecommand */
+ NULL, /* szQueuepausecommand */
+ NULL, /* szQueueresumecommand */
+ NULL, /* szPrintername */
+ NULL, /* szDontdescend */
+ NULL, /* szHostsallow */
+ NULL, /* szHostsdeny */
+ NULL, /* szMagicScript */
+ NULL, /* szMagicOutput */
+ NULL, /* szMangledMap */
+ NULL, /* szVetoFiles */
+ NULL, /* szHideFiles */
+ NULL, /* szVetoOplockFiles */
+ NULL, /* comment */
+ NULL, /* force user */
+ NULL, /* force group */
+ NULL, /* readlist */
+ NULL, /* writelist */
+ NULL, /* printer admin */
+ NULL, /* volume */
+ NULL, /* fstype */
+ NULL, /* vfs objects */
+ NULL, /* szMSDfsProxy */
+ 0, /* iMinPrintSpace */
+ 1000, /* iMaxPrintJobs */
+ 0, /* iMaxReportedPrintJobs */
+ 0, /* iWriteCacheSize */
+ 0744, /* iCreate_mask */
+ 0000, /* iCreate_force_mode */
+ 0777, /* iSecurity_mask */
+ 0, /* iSecurity_force_mode */
+ 0755, /* iDir_mask */
+ 0000, /* iDir_force_mode */
+ 0777, /* iDir_Security_mask */
+ 0, /* iDir_Security_force_mode */
+ 0, /* iMaxConnections */
+ CASE_LOWER, /* iDefaultCase */
+ DEFAULT_PRINTING, /* iPrinting */
+ 2, /* iOplockContentionLimit */
+ 0, /* iCSCPolicy */
+ 1024, /* iBlock_size */
+ False, /* bPreexecClose */
+ False, /* bRootpreexecClose */
+ False, /* case sensitive */
+ True, /* case preserve */
+ True, /* short case preserve */
+ False, /* case mangle */
+ True, /* bHideDotFiles */
+ False, /* bHideSpecialFiles */
+ False, /* bHideUnReadable */
+ False, /* bHideUnWriteableFiles */
+ True, /* bBrowseable */
+ True, /* bAvailable */
+ True, /* bRead_only */
+ True, /* bNo_set_dir */
+ False, /* bGuest_only */
+ False, /* bGuest_ok */
+ False, /* bPrint_ok */
+ False, /* bMap_system */
+ False, /* bMap_hidden */
+ True, /* bMap_archive */
+ False, /* bStoreDosAttributes */
+ True, /* bLocking */
+ True, /* bStrictLocking */
+ True, /* bPosixLocking */
+ True, /* bShareModes */
+ True, /* bOpLocks */
+ True, /* bLevel2OpLocks */
+ False, /* bOnlyUser */
+ True, /* bMangledNames */
+ True, /* bWidelinks */
+ True, /* bSymlinks */
+ False, /* bSyncAlways */
+ False, /* bStrictAllocate */
+ False, /* bStrictSync */
+ '~', /* magic char */
+ NULL, /* copymap */
+ False, /* bDeleteReadonly */
+ False, /* bFakeOplocks */
+ False, /* bDeleteVetoFiles */
+ False, /* bDosFilemode */
+ False, /* bDosFiletimes */
+ False, /* bDosFiletimeResolution */
+ False, /* bFakeDirCreateTimes */
+ True, /* bBlockingLocks */
+ False, /* bInheritPerms */
+ False, /* bInheritACLS */
+ False, /* bMSDfsRoot */
+ False, /* bUseClientDriver */
+ False, /* bDefaultDevmode */
+ True, /* bNTAclSupport */
+ False, /* bUseSendfile */
+ False, /* bProfileAcls */
+ False, /* bMap_acl_inherit */
+ False, /* bAfs_Share */
+ False, /* bEASupport */
+
+ NULL, /* Parametric options */
+
+ "" /* dummy */
+};
+
+/* local variables */
+static service **ServicePtrs = NULL;
+static int iNumServices = 0;
+static int iServiceIndex = 0;
+static BOOL bInGlobalSection = True;
+static BOOL bGlobalOnly = False;
+static int server_role;
+static int default_server_announce;
+
+#define NUMPARAMETERS (sizeof(parm_table) / sizeof(struct parm_struct))
+
+/* prototypes for the special type handlers */
+static BOOL handle_include(const char *pszParmValue, char **ptr);
+static BOOL handle_copy(const char *pszParmValue, char **ptr);
+static BOOL handle_netbios_name(const char *pszParmValue, char **ptr);
+static BOOL handle_idmap_uid(const char *pszParmValue, char **ptr);
+static BOOL handle_idmap_gid(const char *pszParmValue, char **ptr);
+static BOOL handle_debug_list( const char *pszParmValue, char **ptr );
+static BOOL handle_workgroup( const char *pszParmValue, char **ptr );
+static BOOL handle_netbios_aliases( const char *pszParmValue, char **ptr );
+static BOOL handle_netbios_scope( const char *pszParmValue, char **ptr );
+static BOOL handle_charset( const char *pszParmValue, char **ptr );
+
+static BOOL handle_acl_compatibility(const char *pszParmValue, char **ptr);
+
+static void set_server_role(void);
+static void set_default_server_announce_type(void);
+static void set_allowed_client_auth(void);
+
+static const struct enum_list enum_protocol[] = {
+ {PROTOCOL_NT1, "NT1"},
+ {PROTOCOL_LANMAN2, "LANMAN2"},
+ {PROTOCOL_LANMAN1, "LANMAN1"},
+ {PROTOCOL_CORE, "CORE"},
+ {PROTOCOL_COREPLUS, "COREPLUS"},
+ {PROTOCOL_COREPLUS, "CORE+"},
+ {-1, NULL}
+};
+
+static const struct enum_list enum_security[] = {
+ {SEC_SHARE, "SHARE"},
+ {SEC_USER, "USER"},
+ {SEC_SERVER, "SERVER"},
+ {SEC_DOMAIN, "DOMAIN"},
+#ifdef HAVE_ADS
+ {SEC_ADS, "ADS"},
+#endif
+ {-1, NULL}
+};
+
+static const struct enum_list enum_printing[] = {
+ {PRINT_SYSV, "sysv"},
+ {PRINT_AIX, "aix"},
+ {PRINT_HPUX, "hpux"},
+ {PRINT_BSD, "bsd"},
+ {PRINT_QNX, "qnx"},
+ {PRINT_PLP, "plp"},
+ {PRINT_LPRNG, "lprng"},
+ {PRINT_CUPS, "cups"},
+ {PRINT_LPRNT, "nt"},
+ {PRINT_LPROS2, "os2"},
+#ifdef DEVELOPER
+ {PRINT_TEST, "test"},
+ {PRINT_VLP, "vlp"},
+#endif /* DEVELOPER */
+ {-1, NULL}
+};
+
+static const struct enum_list enum_ldap_ssl[] = {
+#ifdef WITH_LDAP_SAMCONFIG
+ {LDAP_SSL_ON, "Yes"},
+ {LDAP_SSL_ON, "yes"},
+ {LDAP_SSL_ON, "on"},
+ {LDAP_SSL_ON, "On"},
+#endif
+ {LDAP_SSL_OFF, "no"},
+ {LDAP_SSL_OFF, "No"},
+ {LDAP_SSL_OFF, "off"},
+ {LDAP_SSL_OFF, "Off"},
+ {LDAP_SSL_START_TLS, "start tls"},
+ {LDAP_SSL_START_TLS, "Start_tls"},
+ {-1, NULL}
+};
+
+static const struct enum_list enum_ldap_passwd_sync[] = {
+ {LDAP_PASSWD_SYNC_OFF, "no"},
+ {LDAP_PASSWD_SYNC_OFF, "No"},
+ {LDAP_PASSWD_SYNC_OFF, "off"},
+ {LDAP_PASSWD_SYNC_OFF, "Off"},
+ {LDAP_PASSWD_SYNC_ON, "Yes"},
+ {LDAP_PASSWD_SYNC_ON, "yes"},
+ {LDAP_PASSWD_SYNC_ON, "on"},
+ {LDAP_PASSWD_SYNC_ON, "On"},
+ {LDAP_PASSWD_SYNC_ONLY, "Only"},
+ {LDAP_PASSWD_SYNC_ONLY, "only"},
+ {-1, NULL}
+};
+
+/* Types of machine we can announce as. */
+#define ANNOUNCE_AS_NT_SERVER 1
+#define ANNOUNCE_AS_WIN95 2
+#define ANNOUNCE_AS_WFW 3
+#define ANNOUNCE_AS_NT_WORKSTATION 4
+
+static const struct enum_list enum_announce_as[] = {
+ {ANNOUNCE_AS_NT_SERVER, "NT"},
+ {ANNOUNCE_AS_NT_SERVER, "NT Server"},
+ {ANNOUNCE_AS_NT_WORKSTATION, "NT Workstation"},
+ {ANNOUNCE_AS_WIN95, "win95"},
+ {ANNOUNCE_AS_WFW, "WfW"},
+ {-1, NULL}
+};
+
+static const struct enum_list enum_case[] = {
+ {CASE_LOWER, "lower"},
+ {CASE_UPPER, "upper"},
+ {-1, NULL}
+};
+
+static const struct enum_list enum_bool_auto[] = {
+ {False, "No"},
+ {False, "False"},
+ {False, "0"},
+ {True, "Yes"},
+ {True, "True"},
+ {True, "1"},
+ {Auto, "Auto"},
+ {-1, NULL}
+};
+
+/* Client-side offline caching policy types */
+#define CSC_POLICY_MANUAL 0
+#define CSC_POLICY_DOCUMENTS 1
+#define CSC_POLICY_PROGRAMS 2
+#define CSC_POLICY_DISABLE 3
+
+static const struct enum_list enum_csc_policy[] = {
+ {CSC_POLICY_MANUAL, "manual"},
+ {CSC_POLICY_DOCUMENTS, "documents"},
+ {CSC_POLICY_PROGRAMS, "programs"},
+ {CSC_POLICY_DISABLE, "disable"},
+ {-1, NULL}
+};
+
+/* SMB signing types. */
+static const struct enum_list enum_smb_signing_vals[] = {
+ {False, "No"},
+ {False, "False"},
+ {False, "0"},
+ {False, "Off"},
+ {False, "disabled"},
+ {True, "Yes"},
+ {True, "True"},
+ {True, "1"},
+ {True, "On"},
+ {True, "enabled"},
+ {Auto, "auto"},
+ {Required, "required"},
+ {Required, "mandatory"},
+ {Required, "force"},
+ {Required, "forced"},
+ {Required, "enforced"},
+ {-1, NULL}
+};
+
+
+/*
+ Do you want session setups at user level security with a invalid
+ password to be rejected or allowed in as guest? WinNT rejects them
+ but it can be a pain as it means "net view" needs to use a password
+
+ You have 3 choices in the setting of map_to_guest:
+
+ "Never" means session setups with an invalid password
+ are rejected. This is the default.
+
+ "Bad User" means session setups with an invalid password
+ are rejected, unless the username does not exist, in which case it
+ is treated as a guest login
+
+ "Bad Password" means session setups with an invalid password
+ are treated as a guest login
+
+ Note that map_to_guest only has an effect in user or server
+ level security.
+*/
+
+static const struct enum_list enum_map_to_guest[] = {
+ {NEVER_MAP_TO_GUEST, "Never"},
+ {MAP_TO_GUEST_ON_BAD_USER, "Bad User"},
+ {MAP_TO_GUEST_ON_BAD_PASSWORD, "Bad Password"},
+ {-1, NULL}
+};
+
+/* Note: We do not initialise the defaults union - it is not allowed in ANSI C
+ *
+ * The FLAG_HIDE is explicit. Paramters set this way do NOT appear in any edit
+ * screen in SWAT. This is used to exclude parameters as well as to squash all
+ * parameters that have been duplicated by pseudonyms.
+ *
+ * NOTE: To display a parameter in BASIC view set FLAG_BASIC
+ * Any parameter that does NOT have FLAG_ADVANCED will not disply at all
+ * Set FLAG_SHARE and FLAG_PRINT to specifically display parameters in
+ * respective views.
+ *
+ * NOTE2: Handling of duplicated (synonym) paramters:
+ * Only the first occurance of a parameter should be enabled by FLAG_BASIC
+ * and/or FLAG_ADVANCED. All duplicates following the first mention should be
+ * set to FLAG_HIDE. ie: Make you must place the parameter that has the preferred
+ * name first, and all synonyms must follow it with the FLAG_HIDE attribute.
+ */
+
+static struct parm_struct parm_table[] = {
+ {N_("Base Options"), P_SEP, P_SEPARATOR},
+
+ {"config backend", P_STRING, P_GLOBAL, &Globals.szConfigBackend, NULL, NULL, FLAG_ADVANCED},
+ {"dos charset", P_STRING, P_GLOBAL, &Globals.dos_charset, handle_charset, NULL, FLAG_ADVANCED},
+ {"unix charset", P_STRING, P_GLOBAL, &Globals.unix_charset, handle_charset, NULL, FLAG_ADVANCED},
+ {"display charset", P_STRING, P_GLOBAL, &Globals.display_charset, handle_charset, NULL, FLAG_ADVANCED},
+ {"comment", P_STRING, P_LOCAL, &sDefault.comment, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT},
+ {"path", P_STRING, P_LOCAL, &sDefault.szPath, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT},
+ {"directory", P_STRING, P_LOCAL, &sDefault.szPath, NULL, NULL, FLAG_HIDE},
+ {"workgroup", P_USTRING, P_GLOBAL, &Globals.szWorkgroup, handle_workgroup, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD},
+#ifdef WITH_ADS
+ {"realm", P_USTRING, P_GLOBAL, &Globals.szRealm, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD},
+#endif
+ {"netbios name", P_USTRING, P_GLOBAL, &Globals.szNetbiosName, handle_netbios_name, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD},
+ {"netbios aliases", P_LIST, P_GLOBAL, &Globals.szNetbiosAliases, handle_netbios_aliases, NULL, FLAG_ADVANCED},
+ {"netbios scope", P_USTRING, P_GLOBAL, &Globals.szNetbiosScope, handle_netbios_scope, NULL, FLAG_ADVANCED},
+ {"server string", P_STRING, P_GLOBAL, &Globals.szServerString, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED },
+ {"interfaces", P_LIST, P_GLOBAL, &Globals.szInterfaces, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD},
+ {"bind interfaces only", P_BOOL, P_GLOBAL, &Globals.bBindInterfacesOnly, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD},
+
+ {N_("Security Options"), P_SEP, P_SEPARATOR},
+
+ {"security", P_ENUM, P_GLOBAL, &Globals.security, NULL, enum_security, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD},
+ {"auth methods", P_LIST, P_GLOBAL, &Globals.AuthMethods, NULL, NULL, FLAG_ADVANCED},
+ {"encrypt passwords", P_BOOL, P_GLOBAL, &Globals.bEncryptPasswords, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD},
+ {"update encrypted", P_BOOL, P_GLOBAL, &Globals.bUpdateEncrypt, NULL, NULL, FLAG_ADVANCED},
+ {"client schannel", P_ENUM, P_GLOBAL, &Globals.clientSchannel, NULL, enum_bool_auto, FLAG_BASIC | FLAG_ADVANCED},
+ {"server schannel", P_ENUM, P_GLOBAL, &Globals.serverSchannel, NULL, enum_bool_auto, FLAG_BASIC | FLAG_ADVANCED},
+ {"allow trusted domains", P_BOOL, P_GLOBAL, &Globals.bAllowTrustedDomains, NULL, NULL, FLAG_ADVANCED},
+ {"hosts equiv", P_STRING, P_GLOBAL, &Globals.szHostsEquiv, NULL, NULL, FLAG_ADVANCED},
+ {"min passwd length", P_INTEGER, P_GLOBAL, &Globals.min_passwd_length, NULL, NULL, FLAG_ADVANCED},
+ {"min password length", P_INTEGER, P_GLOBAL, &Globals.min_passwd_length, NULL, NULL, FLAG_ADVANCED},
+ {"use cracklib", P_BOOL, P_GLOBAL, &Globals.use_cracklib, NULL, NULL, FLAG_ADVANCED},
+ {"map to guest", P_ENUM, P_GLOBAL, &Globals.map_to_guest, NULL, enum_map_to_guest, FLAG_ADVANCED},
+ {"null passwords", P_BOOL, P_GLOBAL, &Globals.bNullPasswords, NULL, NULL, FLAG_ADVANCED},
+ {"obey pam restrictions", P_BOOL, P_GLOBAL, &Globals.bObeyPamRestrictions, NULL, NULL, FLAG_ADVANCED},
+ {"password server", P_STRING, P_GLOBAL, &Globals.szPasswordServer, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD},
+ {"smb passwd file", P_STRING, P_GLOBAL, &Globals.szSMBPasswdFile, NULL, NULL, FLAG_ADVANCED},
+ {"private dir", P_STRING, P_GLOBAL, &Globals.szPrivateDir, NULL, NULL, FLAG_ADVANCED},
+ {"passdb backend", P_LIST, P_GLOBAL, &Globals.szPassdbBackend, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD},
+ {"gums backend", P_STRING, P_GLOBAL, &Globals.szGumsBackend, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD},
+ {"algorithmic rid base", P_INTEGER, P_GLOBAL, &Globals.AlgorithmicRidBase, NULL, NULL, FLAG_ADVANCED},
+ {"root directory", P_STRING, P_GLOBAL, &Globals.szRootdir, NULL, NULL, FLAG_ADVANCED},
+ {"root dir", P_STRING, P_GLOBAL, &Globals.szRootdir, NULL, NULL, FLAG_HIDE},
+ {"root", P_STRING, P_GLOBAL, &Globals.szRootdir, NULL, NULL, FLAG_HIDE},
+ {"guest account", P_STRING, P_GLOBAL, &Globals.szGuestaccount, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED},
+
+ {"pam password change", P_BOOL, P_GLOBAL, &Globals.bPamPasswordChange, NULL, NULL, FLAG_ADVANCED},
+ {"passwd program", P_STRING, P_GLOBAL, &Globals.szPasswdProgram, NULL, NULL, FLAG_ADVANCED},
+ {"passwd chat", P_STRING, P_GLOBAL, &Globals.szPasswdChat, NULL, NULL, FLAG_ADVANCED},
+ {"passwd chat debug", P_BOOL, P_GLOBAL, &Globals.bPasswdChatDebug, NULL, NULL, FLAG_ADVANCED},
+ {"passwd chat timeout", P_INTEGER, P_GLOBAL, &Globals.iPasswdChatTimeout, NULL, NULL, FLAG_ADVANCED},
+ {"username map", P_STRING, P_GLOBAL, &Globals.szUsernameMap, NULL, NULL, FLAG_ADVANCED},
+ {"password level", P_INTEGER, P_GLOBAL, &Globals.pwordlevel, NULL, NULL, FLAG_ADVANCED},
+ {"username level", P_INTEGER, P_GLOBAL, &Globals.unamelevel, NULL, NULL, FLAG_ADVANCED},
+ {"unix password sync", P_BOOL, P_GLOBAL, &Globals.bUnixPasswdSync, NULL, NULL, FLAG_ADVANCED},
+ {"restrict anonymous", P_INTEGER, P_GLOBAL, &Globals.restrict_anonymous, NULL, NULL, FLAG_ADVANCED},
+ {"lanman auth", P_BOOL, P_GLOBAL, &Globals.bLanmanAuth, NULL, NULL, FLAG_ADVANCED},
+ {"ntlm auth", P_BOOL, P_GLOBAL, &Globals.bNTLMAuth, NULL, NULL, FLAG_ADVANCED},
+ {"client NTLMv2 auth", P_BOOL, P_GLOBAL, &Globals.bClientNTLMv2Auth, NULL, NULL, FLAG_ADVANCED},
+ {"client lanman auth", P_BOOL, P_GLOBAL, &Globals.bClientLanManAuth, NULL, NULL, FLAG_ADVANCED},
+ {"client plaintext auth", P_BOOL, P_GLOBAL, &Globals.bClientPlaintextAuth, NULL, NULL, FLAG_ADVANCED},
+
+ {"username", P_STRING, P_LOCAL, &sDefault.szUsername, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"user", P_STRING, P_LOCAL, &sDefault.szUsername, NULL, NULL, FLAG_HIDE},
+ {"users", P_STRING, P_LOCAL, &sDefault.szUsername, NULL, NULL, FLAG_HIDE},
+
+ {"invalid users", P_LIST, P_LOCAL, &sDefault.szInvalidUsers, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"valid users", P_LIST, P_LOCAL, &sDefault.szValidUsers, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"admin users", P_LIST, P_LOCAL, &sDefault.szAdminUsers, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"read list", P_LIST, P_LOCAL, &sDefault.readlist, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"write list", P_LIST, P_LOCAL, &sDefault.writelist, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"printer admin", P_LIST, P_LOCAL, &sDefault.printer_admin, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_PRINT},
+ {"force user", P_STRING, P_LOCAL, &sDefault.force_user, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"force group", P_STRING, P_LOCAL, &sDefault.force_group, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"group", P_STRING, P_LOCAL, &sDefault.force_group, NULL, NULL, FLAG_ADVANCED},
+
+ {"read only", P_BOOL, P_LOCAL, &sDefault.bRead_only, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE},
+ {"write ok", P_BOOLREV, P_LOCAL, &sDefault.bRead_only, NULL, NULL, FLAG_HIDE},
+ {"writeable", P_BOOLREV, P_LOCAL, &sDefault.bRead_only, NULL, NULL, FLAG_HIDE},
+ {"writable", P_BOOLREV, P_LOCAL, &sDefault.bRead_only, NULL, NULL, FLAG_HIDE},
+
+ {"create mask", P_OCTAL, P_LOCAL, &sDefault.iCreate_mask, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"create mode", P_OCTAL, P_LOCAL, &sDefault.iCreate_mask, NULL, NULL, FLAG_HIDE},
+ {"force create mode", P_OCTAL, P_LOCAL, &sDefault.iCreate_force_mode, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"security mask", P_OCTAL, P_LOCAL, &sDefault.iSecurity_mask, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"force security mode", P_OCTAL, P_LOCAL, &sDefault.iSecurity_force_mode, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"directory mask", P_OCTAL, P_LOCAL, &sDefault.iDir_mask, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"directory mode", P_OCTAL, P_LOCAL, &sDefault.iDir_mask, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL},
+ {"force directory mode", P_OCTAL, P_LOCAL, &sDefault.iDir_force_mode, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"directory security mask", P_OCTAL, P_LOCAL, &sDefault.iDir_Security_mask, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"force directory security mode", P_OCTAL, P_LOCAL, &sDefault.iDir_Security_force_mode, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"inherit permissions", P_BOOL, P_LOCAL, &sDefault.bInheritPerms, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"inherit acls", P_BOOL, P_LOCAL, &sDefault.bInheritACLS, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"guest only", P_BOOL, P_LOCAL, &sDefault.bGuest_only, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"only guest", P_BOOL, P_LOCAL, &sDefault.bGuest_only, NULL, NULL, FLAG_HIDE},
+
+ {"guest ok", P_BOOL, P_LOCAL, &sDefault.bGuest_ok, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT},
+ {"public", P_BOOL, P_LOCAL, &sDefault.bGuest_ok, NULL, NULL, FLAG_HIDE},
+
+ {"only user", P_BOOL, P_LOCAL, &sDefault.bOnlyUser, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_DEPRECATED},
+ {"hosts allow", P_LIST, P_LOCAL, &sDefault.szHostsallow, NULL, NULL, FLAG_GLOBAL | FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT},
+ {"allow hosts", P_LIST, P_LOCAL, &sDefault.szHostsallow, NULL, NULL, FLAG_HIDE},
+ {"hosts deny", P_LIST, P_LOCAL, &sDefault.szHostsdeny, NULL, NULL, FLAG_GLOBAL | FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT},
+ {"deny hosts", P_LIST, P_LOCAL, &sDefault.szHostsdeny, NULL, NULL, FLAG_HIDE},
+ {"preload modules", P_LIST, P_GLOBAL, &Globals.szPreloadModules, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL},
+
+ {N_("Logging Options"), P_SEP, P_SEPARATOR},
+
+ {"log level", P_STRING, P_GLOBAL, &Globals.szLogLevel, handle_debug_list, NULL, FLAG_ADVANCED},
+ {"debuglevel", P_STRING, P_GLOBAL, &Globals.szLogLevel, handle_debug_list, NULL, FLAG_HIDE},
+ {"syslog", P_INTEGER, P_GLOBAL, &Globals.syslog, NULL, NULL, FLAG_ADVANCED},
+ {"syslog only", P_BOOL, P_GLOBAL, &Globals.bSyslogOnly, NULL, NULL, FLAG_ADVANCED},
+ {"log file", P_STRING, P_GLOBAL, &Globals.szLogFile, NULL, NULL, FLAG_ADVANCED},
+
+ {"max log size", P_INTEGER, P_GLOBAL, &Globals.max_log_size, NULL, NULL, FLAG_ADVANCED},
+ {"timestamp logs", P_BOOL, P_GLOBAL, &Globals.bTimestampLogs, NULL, NULL, FLAG_ADVANCED},
+ {"debug timestamp", P_BOOL, P_GLOBAL, &Globals.bTimestampLogs, NULL, NULL, FLAG_ADVANCED},
+ {"debug hires timestamp", P_BOOL, P_GLOBAL, &Globals.bDebugHiresTimestamp, NULL, NULL, FLAG_ADVANCED},
+ {"debug pid", P_BOOL, P_GLOBAL, &Globals.bDebugPid, NULL, NULL, FLAG_ADVANCED},
+ {"debug uid", P_BOOL, P_GLOBAL, &Globals.bDebugUid, NULL, NULL, FLAG_ADVANCED},
+
+ {N_("Protocol Options"), P_SEP, P_SEPARATOR},
+
+ {"smb ports", P_STRING, P_GLOBAL, &Globals.smb_ports, NULL, NULL, FLAG_ADVANCED},
+ {"protocol", P_ENUM, P_GLOBAL, &Globals.maxprotocol, NULL, enum_protocol, FLAG_ADVANCED},
+ {"large readwrite", P_BOOL, P_GLOBAL, &Globals.bLargeReadwrite, NULL, NULL, FLAG_ADVANCED},
+ {"max protocol", P_ENUM, P_GLOBAL, &Globals.maxprotocol, NULL, enum_protocol, FLAG_ADVANCED},
+ {"min protocol", P_ENUM, P_GLOBAL, &Globals.minprotocol, NULL, enum_protocol, FLAG_ADVANCED},
+ {"unicode", P_BOOL, P_GLOBAL, &Globals.bUnicode, NULL, NULL, FLAG_ADVANCED},
+ {"read bmpx", P_BOOL, P_GLOBAL, &Globals.bReadbmpx, NULL, NULL, FLAG_ADVANCED},
+ {"read raw", P_BOOL, P_GLOBAL, &Globals.bReadRaw, NULL, NULL, FLAG_ADVANCED},
+ {"write raw", P_BOOL, P_GLOBAL, &Globals.bWriteRaw, NULL, NULL, FLAG_ADVANCED},
+ {"disable netbios", P_BOOL, P_GLOBAL, &Globals.bDisableNetbios, NULL, NULL, FLAG_ADVANCED},
+
+ {"acl compatibility", P_STRING, P_GLOBAL, &Globals.szAclCompat, handle_acl_compatibility, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"ea support", P_BOOL, P_LOCAL, &sDefault.bEASupport, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"nt acl support", P_BOOL, P_LOCAL, &sDefault.bNTAclSupport, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"nt pipe support", P_BOOL, P_GLOBAL, &Globals.bNTPipeSupport, NULL, NULL, FLAG_ADVANCED},
+ {"nt status support", P_BOOL, P_GLOBAL, &Globals.bNTStatusSupport, NULL, NULL, FLAG_ADVANCED},
+ {"profile acls", P_BOOL, P_LOCAL, &sDefault.bProfileAcls, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+
+ {"announce version", P_STRING, P_GLOBAL, &Globals.szAnnounceVersion, NULL, NULL, FLAG_ADVANCED},
+ {"announce as", P_ENUM, P_GLOBAL, &Globals.announce_as, NULL, enum_announce_as, FLAG_ADVANCED},
+ {"map acl inherit", P_BOOL, P_LOCAL, &sDefault.bMap_acl_inherit, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"afs share", P_BOOL, P_LOCAL, &sDefault.bAfs_Share, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"max mux", P_INTEGER, P_GLOBAL, &Globals.max_mux, NULL, NULL, FLAG_ADVANCED},
+ {"max xmit", P_INTEGER, P_GLOBAL, &Globals.max_xmit, NULL, NULL, FLAG_ADVANCED},
+
+ {"name resolve order", P_STRING, P_GLOBAL, &Globals.szNameResolveOrder, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD},
+ {"max ttl", P_INTEGER, P_GLOBAL, &Globals.max_ttl, NULL, NULL, FLAG_ADVANCED},
+ {"max wins ttl", P_INTEGER, P_GLOBAL, &Globals.max_wins_ttl, NULL, NULL, FLAG_ADVANCED},
+ {"min wins ttl", P_INTEGER, P_GLOBAL, &Globals.min_wins_ttl, NULL, NULL, FLAG_ADVANCED},
+ {"time server", P_BOOL, P_GLOBAL, &Globals.bTimeServer, NULL, NULL, FLAG_ADVANCED},
+ {"unix extensions", P_BOOL, P_GLOBAL, &Globals.bUnixExtensions, NULL, NULL, FLAG_ADVANCED},
+ {"use spnego", P_BOOL, P_GLOBAL, &Globals.bUseSpnego, NULL, NULL, FLAG_ADVANCED},
+ {"client signing", P_ENUM, P_GLOBAL, &Globals.client_signing, NULL, enum_smb_signing_vals, FLAG_ADVANCED},
+ {"server signing", P_ENUM, P_GLOBAL, &Globals.server_signing, NULL, enum_smb_signing_vals, FLAG_ADVANCED},
+ {"client use spnego", P_BOOL, P_GLOBAL, &Globals.bClientUseSpnego, NULL, NULL, FLAG_ADVANCED},
+
+ {N_("Tuning Options"), P_SEP, P_SEPARATOR},
+
+ {"block size", P_INTEGER, P_LOCAL, &sDefault.iBlock_size, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"change notify timeout", P_INTEGER, P_GLOBAL, &Globals.change_notify_timeout, NULL, NULL, FLAG_ADVANCED},
+ {"deadtime", P_INTEGER, P_GLOBAL, &Globals.deadtime, NULL, NULL, FLAG_ADVANCED},
+ {"getwd cache", P_BOOL, P_GLOBAL, &use_getwd_cache, NULL, NULL, FLAG_ADVANCED},
+ {"keepalive", P_INTEGER, P_GLOBAL, &keepalive, NULL, NULL, FLAG_ADVANCED},
+ {"kernel change notify", P_BOOL, P_GLOBAL, &Globals.bKernelChangeNotify, NULL, NULL, FLAG_ADVANCED},
+
+ {"lpq cache time", P_INTEGER, P_GLOBAL, &Globals.lpqcachetime, NULL, NULL, FLAG_ADVANCED},
+ {"max smbd processes", P_INTEGER, P_GLOBAL, &Globals.iMaxSmbdProcesses, NULL, NULL, FLAG_ADVANCED},
+ {"max connections", P_INTEGER, P_LOCAL, &sDefault.iMaxConnections, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"paranoid server security", P_BOOL, P_GLOBAL, &Globals.paranoid_server_security, NULL, NULL, FLAG_ADVANCED},
+ {"max disk size", P_INTEGER, P_GLOBAL, &Globals.maxdisksize, NULL, NULL, FLAG_ADVANCED},
+ {"max open files", P_INTEGER, P_GLOBAL, &Globals.max_open_files, NULL, NULL, FLAG_ADVANCED},
+ {"min print space", P_INTEGER, P_LOCAL, &sDefault.iMinPrintSpace, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT},
+
+ {"socket options", P_GSTRING, P_GLOBAL, user_socket_options, NULL, NULL, FLAG_ADVANCED},
+ {"strict allocate", P_BOOL, P_LOCAL, &sDefault.bStrictAllocate, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"strict sync", P_BOOL, P_LOCAL, &sDefault.bStrictSync, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"sync always", P_BOOL, P_LOCAL, &sDefault.bSyncAlways, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"use mmap", P_BOOL, P_GLOBAL, &Globals.bUseMmap, NULL, NULL, FLAG_ADVANCED},
+ {"use sendfile", P_BOOL, P_LOCAL, &sDefault.bUseSendfile, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"hostname lookups", P_BOOL, P_GLOBAL, &Globals.bHostnameLookups, NULL, NULL, FLAG_ADVANCED},
+ {"write cache size", P_INTEGER, P_LOCAL, &sDefault.iWriteCacheSize, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+
+ {"name cache timeout", P_INTEGER, P_GLOBAL, &Globals.name_cache_timeout, NULL, NULL, FLAG_ADVANCED},
+
+ {N_("Printing Options"), P_SEP, P_SEPARATOR},
+
+ {"max reported print jobs", P_INTEGER, P_LOCAL, &sDefault.iMaxReportedPrintJobs, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT},
+ {"max print jobs", P_INTEGER, P_LOCAL, &sDefault.iMaxPrintJobs, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT},
+ {"load printers", P_BOOL, P_GLOBAL, &Globals.bLoadPrinters, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT},
+ {"printcap name", P_STRING, P_GLOBAL, &Globals.szPrintcapname, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT},
+ {"printcap", P_STRING, P_GLOBAL, &Globals.szPrintcapname, NULL, NULL, FLAG_HIDE},
+ {"printable", P_BOOL, P_LOCAL, &sDefault.bPrint_ok, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT},
+ {"print ok", P_BOOL, P_LOCAL, &sDefault.bPrint_ok, NULL, NULL, FLAG_HIDE},
+ {"printing", P_ENUM, P_LOCAL, &sDefault.iPrinting, NULL, enum_printing, FLAG_ADVANCED | FLAG_PRINT | FLAG_GLOBAL},
+ {"print command", P_STRING, P_LOCAL, &sDefault.szPrintcommand, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT | FLAG_GLOBAL},
+ {"disable spoolss", P_BOOL, P_GLOBAL, &Globals.bDisableSpoolss, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT | FLAG_GLOBAL},
+ {"lpq command", P_STRING, P_LOCAL, &sDefault.szLpqcommand, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT | FLAG_GLOBAL},
+ {"lprm command", P_STRING, P_LOCAL, &sDefault.szLprmcommand, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT | FLAG_GLOBAL},
+ {"lppause command", P_STRING, P_LOCAL, &sDefault.szLppausecommand, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT | FLAG_GLOBAL},
+ {"lpresume command", P_STRING, P_LOCAL, &sDefault.szLpresumecommand, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT | FLAG_GLOBAL},
+ {"queuepause command", P_STRING, P_LOCAL, &sDefault.szQueuepausecommand, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT | FLAG_GLOBAL},
+ {"queueresume command", P_STRING, P_LOCAL, &sDefault.szQueueresumecommand, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT | FLAG_GLOBAL},
+
+ {"enumports command", P_STRING, P_GLOBAL, &Globals.szEnumPortsCommand, NULL, NULL, FLAG_ADVANCED},
+ {"addprinter command", P_STRING, P_GLOBAL, &Globals.szAddPrinterCommand, NULL, NULL, FLAG_ADVANCED},
+ {"deleteprinter command", P_STRING, P_GLOBAL, &Globals.szDeletePrinterCommand, NULL, NULL, FLAG_ADVANCED},
+ {"show add printer wizard", P_BOOL, P_GLOBAL, &Globals.bMsAddPrinterWizard, NULL, NULL, FLAG_ADVANCED},
+ {"os2 driver map", P_STRING, P_GLOBAL, &Globals.szOs2DriverMap, NULL, NULL, FLAG_ADVANCED},
+
+ {"printer name", P_STRING, P_LOCAL, &sDefault.szPrintername, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT},
+ {"printer", P_STRING, P_LOCAL, &sDefault.szPrintername, NULL, NULL, FLAG_HIDE},
+ {"use client driver", P_BOOL, P_LOCAL, &sDefault.bUseClientDriver, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT},
+ {"default devmode", P_BOOL, P_LOCAL, &sDefault.bDefaultDevmode, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT},
+
+ {N_("Filename Handling"), P_SEP, P_SEPARATOR},
+ {"mangling method", P_STRING, P_GLOBAL, &Globals.szManglingMethod, NULL, NULL, FLAG_ADVANCED},
+ {"mangle prefix", P_INTEGER, P_GLOBAL, &Globals.mangle_prefix, NULL, NULL, FLAG_ADVANCED},
+
+ {"default case", P_ENUM, P_LOCAL, &sDefault.iDefaultCase, NULL, enum_case, FLAG_ADVANCED | FLAG_SHARE},
+ {"case sensitive", P_BOOL, P_LOCAL, &sDefault.bCaseSensitive, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"casesignames", P_BOOL, P_LOCAL, &sDefault.bCaseSensitive, NULL, NULL, FLAG_HIDE},
+ {"preserve case", P_BOOL, P_LOCAL, &sDefault.bCasePreserve, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"short preserve case", P_BOOL, P_LOCAL, &sDefault.bShortCasePreserve, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"mangle case", P_BOOL, P_LOCAL, &sDefault.bCaseMangle, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"mangling char", P_CHAR, P_LOCAL, &sDefault.magic_char, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"hide dot files", P_BOOL, P_LOCAL, &sDefault.bHideDotFiles, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"hide special files", P_BOOL, P_LOCAL, &sDefault.bHideSpecialFiles, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"hide unreadable", P_BOOL, P_LOCAL, &sDefault.bHideUnReadable, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"hide unwriteable files", P_BOOL, P_LOCAL, &sDefault.bHideUnWriteableFiles, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"delete veto files", P_BOOL, P_LOCAL, &sDefault.bDeleteVetoFiles, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"veto files", P_STRING, P_LOCAL, &sDefault.szVetoFiles, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL },
+ {"hide files", P_STRING, P_LOCAL, &sDefault.szHideFiles, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL },
+ {"veto oplock files", P_STRING, P_LOCAL, &sDefault.szVetoOplockFiles, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL },
+ {"map system", P_BOOL, P_LOCAL, &sDefault.bMap_system, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"map hidden", P_BOOL, P_LOCAL, &sDefault.bMap_hidden, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"map archive", P_BOOL, P_LOCAL, &sDefault.bMap_archive, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"mangled names", P_BOOL, P_LOCAL, &sDefault.bMangledNames, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"mangled map", P_STRING, P_LOCAL, &sDefault.szMangledMap, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL | FLAG_DEPRECATED },
+ {"stat cache", P_BOOL, P_GLOBAL, &Globals.bStatCache, NULL, NULL, FLAG_ADVANCED},
+ {"store dos attributes", P_BOOL, P_LOCAL, &sDefault.bStoreDosAttributes, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+
+ {N_("Domain Options"), P_SEP, P_SEPARATOR},
+
+ {"machine password timeout", P_INTEGER, P_GLOBAL, &Globals.machine_password_timeout, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD},
+
+ {N_("Logon Options"), P_SEP, P_SEPARATOR},
+
+ {"add user script", P_STRING, P_GLOBAL, &Globals.szAddUserScript, NULL, NULL, FLAG_ADVANCED},
+ {"delete user script", P_STRING, P_GLOBAL, &Globals.szDelUserScript, NULL, NULL, FLAG_ADVANCED},
+ {"add group script", P_STRING, P_GLOBAL, &Globals.szAddGroupScript, NULL, NULL, FLAG_ADVANCED},
+ {"delete group script", P_STRING, P_GLOBAL, &Globals.szDelGroupScript, NULL, NULL, FLAG_ADVANCED},
+ {"add user to group script", P_STRING, P_GLOBAL, &Globals.szAddUserToGroupScript, NULL, NULL, FLAG_ADVANCED},
+ {"delete user from group script", P_STRING, P_GLOBAL, &Globals.szDelUserFromGroupScript, NULL, NULL, FLAG_ADVANCED},
+ {"set primary group script", P_STRING, P_GLOBAL, &Globals.szSetPrimaryGroupScript, NULL, NULL, FLAG_ADVANCED},
+ {"add machine script", P_STRING, P_GLOBAL, &Globals.szAddMachineScript, NULL, NULL, FLAG_ADVANCED},
+ {"shutdown script", P_STRING, P_GLOBAL, &Globals.szShutdownScript, NULL, NULL, FLAG_ADVANCED},
+ {"abort shutdown script", P_STRING, P_GLOBAL, &Globals.szAbortShutdownScript, NULL, NULL, FLAG_ADVANCED},
+
+ {"logon script", P_STRING, P_GLOBAL, &Globals.szLogonScript, NULL, NULL, FLAG_ADVANCED},
+ {"logon path", P_STRING, P_GLOBAL, &Globals.szLogonPath, NULL, NULL, FLAG_ADVANCED},
+ {"logon drive", P_STRING, P_GLOBAL, &Globals.szLogonDrive, NULL, NULL, FLAG_ADVANCED},
+ {"logon home", P_STRING, P_GLOBAL, &Globals.szLogonHome, NULL, NULL, FLAG_ADVANCED},
+ {"domain logons", P_BOOL, P_GLOBAL, &Globals.bDomainLogons, NULL, NULL, FLAG_ADVANCED},
+
+ {N_("Browse Options"), P_SEP, P_SEPARATOR},
+
+ {"os level", P_INTEGER, P_GLOBAL, &Globals.os_level, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED},
+ {"lm announce", P_ENUM, P_GLOBAL, &Globals.lm_announce, NULL, enum_bool_auto, FLAG_ADVANCED},
+ {"lm interval", P_INTEGER, P_GLOBAL, &Globals.lm_interval, NULL, NULL, FLAG_ADVANCED},
+ {"preferred master", P_ENUM, P_GLOBAL, &Globals.bPreferredMaster, NULL, enum_bool_auto, FLAG_BASIC | FLAG_ADVANCED},
+ {"prefered master", P_ENUM, P_GLOBAL, &Globals.bPreferredMaster, NULL, enum_bool_auto, FLAG_HIDE},
+ {"local master", P_BOOL, P_GLOBAL, &Globals.bLocalMaster, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED},
+ {"domain master", P_ENUM, P_GLOBAL, &Globals.bDomainMaster, NULL, enum_bool_auto, FLAG_BASIC | FLAG_ADVANCED},
+ {"browse list", P_BOOL, P_GLOBAL, &Globals.bBrowseList, NULL, NULL, FLAG_ADVANCED},
+ {"browseable", P_BOOL, P_LOCAL, &sDefault.bBrowseable, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT},
+ {"browsable", P_BOOL, P_LOCAL, &sDefault.bBrowseable, NULL, NULL, FLAG_HIDE},
+ {"enhanced browsing", P_BOOL, P_GLOBAL, &Globals.enhanced_browsing, NULL, NULL, FLAG_ADVANCED},
+
+ {N_("WINS Options"), P_SEP, P_SEPARATOR},
+
+ {"dns proxy", P_BOOL, P_GLOBAL, &Globals.bDNSproxy, NULL, NULL, FLAG_ADVANCED},
+ {"wins proxy", P_BOOL, P_GLOBAL, &Globals.bWINSproxy, NULL, NULL, FLAG_ADVANCED},
+
+ {"wins server", P_LIST, P_GLOBAL, &Globals.szWINSservers, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD},
+ {"wins support", P_BOOL, P_GLOBAL, &Globals.bWINSsupport, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD},
+ {"wins hook", P_STRING, P_GLOBAL, &Globals.szWINSHook, NULL, NULL, FLAG_ADVANCED},
+ {"wins partners", P_STRING, P_GLOBAL, &Globals.szWINSPartners, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD},
+
+ {N_("Locking Options"), P_SEP, P_SEPARATOR},
+
+ {"blocking locks", P_BOOL, P_LOCAL, &sDefault.bBlockingLocks, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"csc policy", P_ENUM, P_LOCAL, &sDefault.iCSCPolicy, NULL, enum_csc_policy, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"fake oplocks", P_BOOL, P_LOCAL, &sDefault.bFakeOplocks, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"kernel oplocks", P_BOOL, P_GLOBAL, &Globals.bKernelOplocks, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL},
+ {"locking", P_BOOL, P_LOCAL, &sDefault.bLocking, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"lock spin count", P_INTEGER, P_GLOBAL, &Globals.iLockSpinCount, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL},
+ {"lock spin time", P_INTEGER, P_GLOBAL, &Globals.iLockSpinTime, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL},
+
+ {"oplocks", P_BOOL, P_LOCAL, &sDefault.bOpLocks, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"level2 oplocks", P_BOOL, P_LOCAL, &sDefault.bLevel2OpLocks, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"oplock break wait time", P_INTEGER, P_GLOBAL, &Globals.oplock_break_wait_time, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL},
+ {"oplock contention limit", P_INTEGER, P_LOCAL, &sDefault.iOplockContentionLimit, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"posix locking", P_BOOL, P_LOCAL, &sDefault.bPosixLocking, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"strict locking", P_BOOL, P_LOCAL, &sDefault.bStrictLocking, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"share modes", P_BOOL, P_LOCAL, &sDefault.bShareModes, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+
+ {N_("Ldap Options"), P_SEP, P_SEPARATOR},
+
+#ifdef WITH_LDAP_SAMCONFIG
+ {"ldap server", P_STRING, P_GLOBAL, &Globals.szLdapServer, NULL, NULL, FLAG_ADVANCED},
+ {"ldap port", P_INTEGER, P_GLOBAL, &Globals.ldap_port, NULL, NULL, FLAG_ADVANCED},
+#endif
+ {"ldap suffix", P_STRING, P_GLOBAL, &Globals.szLdapSuffix, NULL, NULL, FLAG_ADVANCED},
+ {"ldap machine suffix", P_STRING, P_GLOBAL, &Globals.szLdapMachineSuffix, NULL, NULL, FLAG_ADVANCED},
+ {"ldap user suffix", P_STRING, P_GLOBAL, &Globals.szLdapUserSuffix, NULL, NULL, FLAG_ADVANCED},
+ {"ldap group suffix", P_STRING, P_GLOBAL, &Globals.szLdapGroupSuffix, NULL, NULL, FLAG_ADVANCED},
+ {"ldap idmap suffix", P_STRING, P_GLOBAL, &Globals.szLdapIdmapSuffix, NULL, NULL, FLAG_ADVANCED},
+ {"ldap privilege suffix", P_STRING, P_GLOBAL, &Globals.szLdapPrivilegeSuffix, NULL, NULL, FLAG_ADVANCED},
+ {"ldap filter", P_STRING, P_GLOBAL, &Globals.szLdapFilter, NULL, NULL, FLAG_ADVANCED},
+ {"ldap admin dn", P_STRING, P_GLOBAL, &Globals.szLdapAdminDn, NULL, NULL, FLAG_ADVANCED},
+ {"ldap ssl", P_ENUM, P_GLOBAL, &Globals.ldap_ssl, NULL, enum_ldap_ssl, FLAG_ADVANCED},
+ {"ldap passwd sync", P_ENUM, P_GLOBAL, &Globals.ldap_passwd_sync, NULL, enum_ldap_passwd_sync, FLAG_ADVANCED},
+ {"ldap delete dn", P_BOOL, P_GLOBAL, &Globals.ldap_delete_dn, NULL, NULL, FLAG_ADVANCED},
+ {"ldap replication sleep", P_INTEGER, P_GLOBAL, &Globals.ldap_replication_sleep, NULL, NULL, FLAG_ADVANCED},
+
+ {N_("Miscellaneous Options"), P_SEP, P_SEPARATOR},
+ {"add share command", P_STRING, P_GLOBAL, &Globals.szAddShareCommand, NULL, NULL, FLAG_ADVANCED},
+ {"change share command", P_STRING, P_GLOBAL, &Globals.szChangeShareCommand, NULL, NULL, FLAG_ADVANCED},
+ {"delete share command", P_STRING, P_GLOBAL, &Globals.szDeleteShareCommand, NULL, NULL, FLAG_ADVANCED},
+
+ {"config file", P_STRING, P_GLOBAL, &Globals.szConfigFile, NULL, NULL, FLAG_HIDE},
+ {"preload", P_STRING, P_GLOBAL, &Globals.szAutoServices, NULL, NULL, FLAG_ADVANCED},
+ {"auto services", P_STRING, P_GLOBAL, &Globals.szAutoServices, NULL, NULL, FLAG_ADVANCED},
+ {"lock directory", P_STRING, P_GLOBAL, &Globals.szLockDir, NULL, NULL, FLAG_ADVANCED},
+ {"lock dir", P_STRING, P_GLOBAL, &Globals.szLockDir, NULL, NULL, FLAG_HIDE},
+ {"pid directory", P_STRING, P_GLOBAL, &Globals.szPidDir, NULL, NULL, FLAG_ADVANCED},
+#ifdef WITH_UTMP
+ {"utmp directory", P_STRING, P_GLOBAL, &Globals.szUtmpDir, NULL, NULL, FLAG_ADVANCED},
+ {"wtmp directory", P_STRING, P_GLOBAL, &Globals.szWtmpDir, NULL, NULL, FLAG_ADVANCED},
+ {"utmp", P_BOOL, P_GLOBAL, &Globals.bUtmp, NULL, NULL, FLAG_ADVANCED},
+#endif
+
+ {"default service", P_STRING, P_GLOBAL, &Globals.szDefaultService, NULL, NULL, FLAG_ADVANCED},
+ {"default", P_STRING, P_GLOBAL, &Globals.szDefaultService, NULL, NULL, FLAG_ADVANCED},
+ {"message command", P_STRING, P_GLOBAL, &Globals.szMsgCommand, NULL, NULL, FLAG_ADVANCED},
+ {"dfree command", P_STRING, P_GLOBAL, &Globals.szDfree, NULL, NULL, FLAG_ADVANCED},
+ {"get quota command", P_STRING, P_GLOBAL, &Globals.szGetQuota, NULL, NULL, FLAG_ADVANCED},
+ {"set quota command", P_STRING, P_GLOBAL, &Globals.szSetQuota, NULL, NULL, FLAG_ADVANCED},
+ {"remote announce", P_STRING, P_GLOBAL, &Globals.szRemoteAnnounce, NULL, NULL, FLAG_ADVANCED},
+ {"remote browse sync", P_STRING, P_GLOBAL, &Globals.szRemoteBrowseSync, NULL, NULL, FLAG_ADVANCED},
+ {"socket address", P_STRING, P_GLOBAL, &Globals.szSocketAddress, NULL, NULL, FLAG_ADVANCED},
+ {"homedir map", P_STRING, P_GLOBAL, &Globals.szNISHomeMapName, NULL, NULL, FLAG_ADVANCED},
+ {"afs username map", P_USTRING, P_GLOBAL, &Globals.szAfsUsernameMap, NULL, NULL, FLAG_ADVANCED},
+ {"time offset", P_INTEGER, P_GLOBAL, &extra_time_offset, NULL, NULL, FLAG_ADVANCED},
+ {"NIS homedir", P_BOOL, P_GLOBAL, &Globals.bNISHomeMap, NULL, NULL, FLAG_ADVANCED},
+ {"-valid", P_BOOL, P_LOCAL, &sDefault.valid, NULL, NULL, FLAG_HIDE},
+
+ {"copy", P_STRING, P_LOCAL, &sDefault.szCopy, handle_copy, NULL, FLAG_HIDE},
+ {"include", P_STRING, P_LOCAL, &sDefault.szInclude, handle_include, NULL, FLAG_HIDE},
+ {"exec", P_STRING, P_LOCAL, &sDefault.szPreExec, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT},
+ {"preexec", P_STRING, P_LOCAL, &sDefault.szPreExec, NULL, NULL, FLAG_ADVANCED},
+
+ {"preexec close", P_BOOL, P_LOCAL, &sDefault.bPreexecClose, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"postexec", P_STRING, P_LOCAL, &sDefault.szPostExec, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT},
+ {"root preexec", P_STRING, P_LOCAL, &sDefault.szRootPreExec, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT},
+ {"root preexec close", P_BOOL, P_LOCAL, &sDefault.bRootpreexecClose, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"root postexec", P_STRING, P_LOCAL, &sDefault.szRootPostExec, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT},
+ {"available", P_BOOL, P_LOCAL, &sDefault.bAvailable, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT},
+ {"volume", P_STRING, P_LOCAL, &sDefault.volume, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE },
+ {"fstype", P_STRING, P_LOCAL, &sDefault.fstype, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"set directory", P_BOOLREV, P_LOCAL, &sDefault.bNo_set_dir, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"wide links", P_BOOL, P_LOCAL, &sDefault.bWidelinks, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"follow symlinks", P_BOOL, P_LOCAL, &sDefault.bSymlinks, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"dont descend", P_STRING, P_LOCAL, &sDefault.szDontdescend, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"magic script", P_STRING, P_LOCAL, &sDefault.szMagicScript, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"magic output", P_STRING, P_LOCAL, &sDefault.szMagicOutput, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"delete readonly", P_BOOL, P_LOCAL, &sDefault.bDeleteReadonly, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"dos filemode", P_BOOL, P_LOCAL, &sDefault.bDosFilemode, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"dos filetimes", P_BOOL, P_LOCAL, &sDefault.bDosFiletimes, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"dos filetime resolution", P_BOOL, P_LOCAL, &sDefault.bDosFiletimeResolution, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+
+ {"fake directory create times", P_BOOL, P_LOCAL, &sDefault.bFakeDirCreateTimes, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"panic action", P_STRING, P_GLOBAL, &Globals.szPanicAction, NULL, NULL, FLAG_ADVANCED},
+
+ {N_("VFS module options"), P_SEP, P_SEPARATOR},
+
+ {"vfs objects", P_LIST, P_LOCAL, &sDefault.szVfsObjects, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"vfs object", P_LIST, P_LOCAL, &sDefault.szVfsObjects, NULL, NULL, FLAG_HIDE},
+
+
+ {"msdfs root", P_BOOL, P_LOCAL, &sDefault.bMSDfsRoot, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"msdfs proxy", P_STRING, P_LOCAL, &sDefault.szMSDfsProxy, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"host msdfs", P_BOOL, P_GLOBAL, &Globals.bHostMSDfs, NULL, NULL, FLAG_ADVANCED},
+
+ {N_("Winbind options"), P_SEP, P_SEPARATOR},
+
+ {"enable rid algorithm", P_BOOL, P_GLOBAL, &Globals.bEnableRidAlgorithm, NULL, NULL, FLAG_DEPRECATED},
+ {"idmap backend", P_STRING, P_GLOBAL, &Globals.szIdmapBackend, NULL, NULL, FLAG_ADVANCED},
+ {"idmap uid", P_STRING, P_GLOBAL, &Globals.szIdmapUID, handle_idmap_uid, NULL, FLAG_ADVANCED},
+ {"winbind uid", P_STRING, P_GLOBAL, &Globals.szIdmapUID, handle_idmap_uid, NULL, FLAG_HIDE},
+ {"idmap gid", P_STRING, P_GLOBAL, &Globals.szIdmapGID, handle_idmap_gid, NULL, FLAG_ADVANCED},
+ {"winbind gid", P_STRING, P_GLOBAL, &Globals.szIdmapGID, handle_idmap_gid, NULL, FLAG_HIDE},
+ {"template primary group", P_STRING, P_GLOBAL, &Globals.szTemplatePrimaryGroup, NULL, NULL, FLAG_ADVANCED},
+ {"template homedir", P_STRING, P_GLOBAL, &Globals.szTemplateHomedir, NULL, NULL, FLAG_ADVANCED},
+ {"template shell", P_STRING, P_GLOBAL, &Globals.szTemplateShell, NULL, NULL, FLAG_ADVANCED},
+ {"winbind separator", P_STRING, P_GLOBAL, &Globals.szWinbindSeparator, NULL, NULL, FLAG_ADVANCED},
+ {"winbind cache time", P_INTEGER, P_GLOBAL, &Globals.winbind_cache_time, NULL, NULL, FLAG_ADVANCED},
+ {"winbind enable local accounts", P_BOOL, P_GLOBAL, &Globals.bWinbindEnableLocalAccounts, NULL, NULL, FLAG_ADVANCED},
+ {"winbind enum users", P_BOOL, P_GLOBAL, &Globals.bWinbindEnumUsers, NULL, NULL, FLAG_ADVANCED},
+ {"winbind enum groups", P_BOOL, P_GLOBAL, &Globals.bWinbindEnumGroups, NULL, NULL, FLAG_ADVANCED},
+ {"winbind use default domain", P_BOOL, P_GLOBAL, &Globals.bWinbindUseDefaultDomain, NULL, NULL, FLAG_ADVANCED},
+ {"winbind trusted domains only", P_BOOL, P_GLOBAL, &Globals.bWinbindTrustedDomainsOnly, NULL, NULL, FLAG_ADVANCED},
+
+ {NULL, P_BOOL, P_NONE, NULL, NULL, NULL, 0}
+};
+
+/***************************************************************************
+ Initialise the sDefault parameter structure for the printer values.
+***************************************************************************/
+
+static void init_printer_values(service *pService)
+{
+ if ( pService == NULL ) {
+ DEBUG(0,("init_printer_values: NULL pointer\n"));
+ return;
+ }
+
+ /* choose defaults depending on the type of printing */
+ switch (pService->iPrinting) {
+ case PRINT_BSD:
+ case PRINT_AIX:
+ case PRINT_LPRNT:
+ case PRINT_LPROS2:
+ string_set(&pService->szLpqcommand, "lpq -P'%p'");
+ string_set(&pService->szLprmcommand, "lprm -P'%p' %j");
+ string_set(&pService->szPrintcommand,
+ "lpr -r -P'%p' %s");
+ break;
+
+ case PRINT_LPRNG:
+ case PRINT_PLP:
+ string_set(&pService->szLpqcommand, "lpq -P'%p'");
+ string_set(&pService->szLprmcommand, "lprm -P'%p' %j");
+ string_set(&pService->szPrintcommand,
+ "lpr -r -P'%p' %s");
+ string_set(&pService->szQueuepausecommand,
+ "lpc stop '%p'");
+ string_set(&pService->szQueueresumecommand,
+ "lpc start '%p'");
+ string_set(&pService->szLppausecommand,
+ "lpc hold '%p' %j");
+ string_set(&pService->szLpresumecommand,
+ "lpc release '%p' %j");
+ break;
+
+ case PRINT_CUPS:
+#ifdef HAVE_CUPS
+ string_set(&pService->szLpqcommand, "");
+ string_set(&pService->szLprmcommand, "");
+ string_set(&pService->szPrintcommand, "");
+ string_set(&pService->szLppausecommand, "");
+ string_set(&pService->szLpresumecommand, "");
+ string_set(&pService->szQueuepausecommand, "");
+ string_set(&pService->szQueueresumecommand, "");
+
+ string_set(&Globals.szPrintcapname, "cups");
+#else
+ string_set(&pService->szLpqcommand,
+ "/usr/bin/lpstat -o '%p'");
+ string_set(&pService->szLprmcommand,
+ "/usr/bin/cancel '%p-%j'");
+ string_set(&pService->szPrintcommand,
+ "/usr/bin/lp -d '%p' %s; rm %s");
+ string_set(&pService->szLppausecommand,
+ "lp -i '%p-%j' -H hold");
+ string_set(&pService->szLpresumecommand,
+ "lp -i '%p-%j' -H resume");
+ string_set(&pService->szQueuepausecommand,
+ "/usr/bin/disable '%p'");
+ string_set(&pService->szQueueresumecommand,
+ "/usr/bin/enable '%p'");
+ string_set(&Globals.szPrintcapname, "lpstat");
+#endif /* HAVE_CUPS */
+ break;
+
+ case PRINT_SYSV:
+ case PRINT_HPUX:
+ string_set(&pService->szLpqcommand, "lpstat -o%p");
+ string_set(&pService->szLprmcommand, "cancel %p-%j");
+ string_set(&pService->szPrintcommand,
+ "lp -c -d%p %s; rm %s");
+ string_set(&pService->szQueuepausecommand,
+ "disable %p");
+ string_set(&pService->szQueueresumecommand,
+ "enable %p");
+#ifndef HPUX
+ string_set(&pService->szLppausecommand,
+ "lp -i %p-%j -H hold");
+ string_set(&pService->szLpresumecommand,
+ "lp -i %p-%j -H resume");
+#endif /* HPUX */
+ break;
+
+ case PRINT_QNX:
+ string_set(&pService->szLpqcommand, "lpq -P%p");
+ string_set(&pService->szLprmcommand, "lprm -P%p %j");
+ string_set(&pService->szPrintcommand, "lp -r -P%p %s");
+ break;
+
+#ifdef DEVELOPER
+ case PRINT_TEST:
+ case PRINT_VLP:
+ string_set(&pService->szPrintcommand, "vlp print %p %s");
+ string_set(&pService->szLpqcommand, "vlp lpq %p");
+ string_set(&pService->szLprmcommand, "vlp lprm %p %j");
+ string_set(&pService->szLppausecommand, "vlp lppause %p %j");
+ string_set(&pService->szLpresumecommand, "vlp lpresum %p %j");
+ string_set(&pService->szQueuepausecommand, "vlp queuepause %p");
+ string_set(&pService->szQueueresumecommand, "vlp queueresume %p");
+ break;
+#endif /* DEVELOPER */
+
+ }
+}
+
+/***************************************************************************
+ Initialise the global parameter structure.
+***************************************************************************/
+
+static void init_globals(void)
+{
+ static BOOL done_init = False;
+ pstring s;
+
+ if (!done_init) {
+ int i;
+ memset((void *)&Globals, '\0', sizeof(Globals));
+
+ for (i = 0; parm_table[i].label; i++)
+ if ((parm_table[i].type == P_STRING ||
+ parm_table[i].type == P_USTRING) &&
+ parm_table[i].ptr)
+ string_set(parm_table[i].ptr, "");
+
+ string_set(&sDefault.fstype, FSTYPE_STRING);
+
+ done_init = True;
+ }
+
+
+ DEBUG(3, ("Initialising global parameters\n"));
+
+ string_set(&Globals.szConfigBackend, NULL);
+
+ string_set(&Globals.szSMBPasswdFile, dyn_SMB_PASSWD_FILE);
+ string_set(&Globals.szPrivateDir, dyn_PRIVATE_DIR);
+
+ /* use the new 'hash2' method by default, with a prefix of 1 */
+ string_set(&Globals.szManglingMethod, "hash2");
+ Globals.mangle_prefix = 1;
+
+ string_set(&Globals.szGuestaccount, GUEST_ACCOUNT);
+
+ /* using UTF8 by default allows us to support all chars */
+ string_set(&Globals.unix_charset, DEFAULT_UNIX_CHARSET);
+
+#if defined(HAVE_NL_LANGINFO) && defined(CODESET)
+ /* If the system supports nl_langinfo(), try to grab the value
+ from the user's locale */
+ string_set(&Globals.display_charset, "LOCALE");
+#else
+ string_set(&Globals.display_charset, DEFAULT_DISPLAY_CHARSET);
+#endif
+
+ /* Use codepage 850 as a default for the dos character set */
+ string_set(&Globals.dos_charset, DEFAULT_DOS_CHARSET);
+
+ /*
+ * Allow the default PASSWD_CHAT to be overridden in local.h.
+ */
+ string_set(&Globals.szPasswdChat, DEFAULT_PASSWD_CHAT);
+
+ set_global_myname(myhostname());
+ string_set(&Globals.szNetbiosName,global_myname());
+
+ set_global_myworkgroup(WORKGROUP);
+ string_set(&Globals.szWorkgroup, lp_workgroup());
+
+ string_set(&Globals.szPasswdProgram, "");
+ string_set(&Globals.szPrintcapname, PRINTCAP_NAME);
+ string_set(&Globals.szPidDir, dyn_PIDDIR);
+ string_set(&Globals.szLockDir, dyn_LOCKDIR);
+ string_set(&Globals.szSocketAddress, "0.0.0.0");
+ pstrcpy(s, "Samba ");
+ pstrcat(s, SAMBA_VERSION_STRING);
+ string_set(&Globals.szServerString, s);
+ slprintf(s, sizeof(s) - 1, "%d.%d", DEFAULT_MAJOR_VERSION,
+ DEFAULT_MINOR_VERSION);
+ string_set(&Globals.szAnnounceVersion, s);
+
+ pstrcpy(user_socket_options, DEFAULT_SOCKET_OPTIONS);
+
+ string_set(&Globals.szLogonDrive, "");
+ /* %N is the NIS auto.home server if -DAUTOHOME is used, else same as %L */
+ string_set(&Globals.szLogonHome, "\\\\%N\\%U");
+ string_set(&Globals.szLogonPath, "\\\\%N\\%U\\profile");
+
+ string_set(&Globals.szNameResolveOrder, "lmhosts wins host bcast");
+ string_set(&Globals.szPasswordServer, "*");
+
+ Globals.AlgorithmicRidBase = BASE_RID;
+
+ Globals.bLoadPrinters = True;
+ /* Was 65535 (0xFFFF). 0x4101 matches W2K and causes major speed improvements... */
+ /* Discovered by 2 days of pain by Don McCall @ HP :-). */
+ Globals.max_xmit = 0x4104;
+ Globals.max_mux = 50; /* This is *needed* for profile support. */
+ Globals.lpqcachetime = 10;
+ Globals.bDisableSpoolss = False;
+ Globals.iMaxSmbdProcesses = 0;/* no limit specified */
+ Globals.iTotalPrintJobs = 0; /* no limit specified */
+ Globals.pwordlevel = 0;
+ Globals.unamelevel = 0;
+ Globals.deadtime = 0;
+ Globals.bLargeReadwrite = True;
+ Globals.max_log_size = 5000;
+ Globals.max_open_files = MAX_OPEN_FILES;
+ Globals.maxprotocol = PROTOCOL_NT1;
+ Globals.minprotocol = PROTOCOL_CORE;
+ Globals.security = SEC_USER;
+ Globals.paranoid_server_security = True;
+ Globals.bEncryptPasswords = True;
+ Globals.bUpdateEncrypt = False;
+ Globals.clientSchannel = Auto;
+ Globals.serverSchannel = Auto;
+ Globals.bReadRaw = True;
+ Globals.bWriteRaw = True;
+ Globals.bReadbmpx = False;
+ Globals.bNullPasswords = False;
+ Globals.bObeyPamRestrictions = False;
+ Globals.syslog = 1;
+ Globals.bSyslogOnly = False;
+ Globals.bTimestampLogs = True;
+ string_set(&Globals.szLogLevel, "0");
+ Globals.bDebugHiresTimestamp = False;
+ Globals.bDebugPid = False;
+ Globals.bDebugUid = False;
+ Globals.max_ttl = 60 * 60 * 24 * 3; /* 3 days default. */
+ Globals.max_wins_ttl = 60 * 60 * 24 * 6; /* 6 days default. */
+ Globals.min_wins_ttl = 60 * 60 * 6; /* 6 hours default. */
+ Globals.machine_password_timeout = 60 * 60 * 24 * 7; /* 7 days default. */
+ Globals.change_notify_timeout = 60; /* 1 minute default. */
+ Globals.bKernelChangeNotify = True; /* On if we have it. */
+ Globals.lm_announce = 2; /* = Auto: send only if LM clients found */
+ Globals.lm_interval = 60;
+ Globals.announce_as = ANNOUNCE_AS_NT_SERVER;
+#if (defined(HAVE_NETGROUP) && defined(WITH_AUTOMOUNT))
+ Globals.bNISHomeMap = False;
+#ifdef WITH_NISPLUS_HOME
+ string_set(&Globals.szNISHomeMapName, "auto_home.org_dir");
+#else
+ string_set(&Globals.szNISHomeMapName, "auto.home");
+#endif
+#endif
+ Globals.bTimeServer = False;
+ Globals.bBindInterfacesOnly = False;
+ Globals.bUnixPasswdSync = False;
+ Globals.bPamPasswordChange = False;
+ Globals.bPasswdChatDebug = False;
+ Globals.iPasswdChatTimeout = 2; /* 2 second default. */
+ Globals.bUnicode = True; /* Do unicode on the wire by default */
+ Globals.bNTPipeSupport = True; /* Do NT pipes by default. */
+ Globals.bNTStatusSupport = True; /* Use NT status by default. */
+ Globals.bStatCache = True; /* use stat cache by default */
+ Globals.restrict_anonymous = 0;
+ Globals.bClientLanManAuth = True; /* Do use the LanMan hash if it is available */
+ Globals.bClientPlaintextAuth = True; /* Do use a plaintext password if is requested by the server */
+ Globals.bLanmanAuth = True; /* Do use the LanMan hash if it is available */
+ Globals.bNTLMAuth = True; /* Do use NTLMv1 if it is available (otherwise NTLMv2) */
+ Globals.bClientNTLMv2Auth = False; /* Client should not use NTLMv2, as we can't tell that the server supports it. */
+ /* Note, that we will use NTLM2 session security (which is different), if it is available */
+
+ Globals.map_to_guest = 0; /* By Default, "Never" */
+ Globals.min_passwd_length = MINPASSWDLENGTH; /* By Default, 5. */
+ Globals.use_cracklib = False;
+ Globals.oplock_break_wait_time = 0; /* By Default, 0 msecs. */
+ Globals.enhanced_browsing = True;
+ Globals.iLockSpinCount = 3; /* Try 3 times. */
+ Globals.iLockSpinTime = 10; /* usec. */
+#ifdef MMAP_BLACKLIST
+ Globals.bUseMmap = False;
+#else
+ Globals.bUseMmap = True;
+#endif
+ Globals.bUnixExtensions = True;
+
+ /* hostname lookups can be very expensive and are broken on
+ a large number of sites (tridge) */
+ Globals.bHostnameLookups = False;
+
+#ifdef WITH_LDAP_SAMCONFIG
+ string_set(&Globals.szLdapServer, "localhost");
+ Globals.ldap_port = 636;
+ Globals.szPassdbBackend = str_list_make("ldapsam_compat", NULL);
+#else
+ Globals.szPassdbBackend = str_list_make("smbpasswd", NULL);
+#endif /* WITH_LDAP_SAMCONFIG */
+ string_set(&Globals.szGumsBackend, "tdbsam2");
+
+ string_set(&Globals.szLdapSuffix, "");
+ string_set(&Globals.szLdapFilter, "(uid=%u)");
+ string_set(&Globals.szLdapMachineSuffix, "");
+ string_set(&Globals.szLdapUserSuffix, "");
+ string_set(&Globals.szLdapGroupSuffix, "");
+ string_set(&Globals.szLdapIdmapSuffix, "");
+ string_set(&Globals.szLdapPrivilegeSuffix, "");
+
+ string_set(&Globals.szLdapAdminDn, "");
+ Globals.ldap_ssl = LDAP_SSL_ON;
+ Globals.ldap_passwd_sync = LDAP_PASSWD_SYNC_OFF;
+ Globals.ldap_delete_dn = False;
+ Globals.ldap_replication_sleep = 1000; /* wait 1 sec for replication */
+
+/* these parameters are set to defaults that are more appropriate
+ for the increasing samba install base:
+
+ as a member of the workgroup, that will possibly become a
+ _local_ master browser (lm = True). this is opposed to a forced
+ local master browser startup (pm = True).
+
+ doesn't provide WINS server service by default (wsupp = False),
+ and doesn't provide domain master browser services by default, either.
+
+*/
+
+ Globals.bMsAddPrinterWizard = True;
+ Globals.bPreferredMaster = Auto; /* depending on bDomainMaster */
+ Globals.os_level = 20;
+ Globals.bLocalMaster = True;
+ Globals.bDomainMaster = Auto; /* depending on bDomainLogons */
+ Globals.bDomainLogons = False;
+ Globals.bBrowseList = True;
+ Globals.bWINSsupport = False;
+ Globals.bWINSproxy = False;
+
+ Globals.bDNSproxy = True;
+
+ /* this just means to use them if they exist */
+ Globals.bKernelOplocks = True;
+
+ Globals.bAllowTrustedDomains = True;
+
+ string_set(&Globals.szTemplateShell, "/bin/false");
+ string_set(&Globals.szTemplateHomedir, "/home/%D/%U");
+ string_set(&Globals.szTemplatePrimaryGroup, "nobody");
+ string_set(&Globals.szWinbindSeparator, "\\");
+ string_set(&Globals.szAclCompat, "");
+
+ Globals.winbind_cache_time = 300; /* 5 minutes */
+ Globals.bWinbindEnableLocalAccounts = True;
+ Globals.bWinbindEnumUsers = True;
+ Globals.bWinbindEnumGroups = True;
+ Globals.bWinbindUseDefaultDomain = False;
+ Globals.bWinbindTrustedDomainsOnly = False;
+
+ Globals.bEnableRidAlgorithm = True;
+
+ Globals.name_cache_timeout = 660; /* In seconds */
+
+ Globals.bUseSpnego = True;
+ Globals.bClientUseSpnego = True;
+
+ Globals.client_signing = Auto;
+ Globals.server_signing = False;
+
+ string_set(&Globals.smb_ports, SMB_PORTS);
+}
+
+static TALLOC_CTX *lp_talloc;
+
+/******************************************************************* a
+ Free up temporary memory - called from the main loop.
+********************************************************************/
+
+void lp_talloc_free(void)
+{
+ if (!lp_talloc)
+ return;
+ talloc_destroy(lp_talloc);
+ lp_talloc = NULL;
+}
+
+/*******************************************************************
+ Convenience routine to grab string parameters into temporary memory
+ and run standard_sub_basic on them. The buffers can be written to by
+ callers without affecting the source string.
+********************************************************************/
+
+static char *lp_string(const char *s)
+{
+ char *ret, *tmpstr;
+
+ /* The follow debug is useful for tracking down memory problems
+ especially if you have an inner loop that is calling a lp_*()
+ function that returns a string. Perhaps this debug should be
+ present all the time? */
+
+#if 0
+ DEBUG(10, ("lp_string(%s)\n", s));
+#endif
+
+ if (!lp_talloc)
+ lp_talloc = talloc_init("lp_talloc");
+
+ tmpstr = alloc_sub_basic(current_user_info.smb_name, s);
+ if (trim_char(tmpstr, '\"', '\"')) {
+ if (strchr(tmpstr,'\"') != NULL) {
+ SAFE_FREE(tmpstr);
+ tmpstr = alloc_sub_basic(current_user_info.smb_name,s);
+ }
+ }
+ ret = talloc_strdup(lp_talloc, tmpstr);
+ SAFE_FREE(tmpstr);
+
+ return (ret);
+}
+
+/*
+ In this section all the functions that are used to access the
+ parameters from the rest of the program are defined
+*/
+
+#define FN_GLOBAL_STRING(fn_name,ptr) \
+ char *fn_name(void) {return(lp_string(*(char **)(ptr) ? *(char **)(ptr) : ""));}
+#define FN_GLOBAL_CONST_STRING(fn_name,ptr) \
+ const char *fn_name(void) {return(*(const char **)(ptr) ? *(const char **)(ptr) : "");}
+#define FN_GLOBAL_LIST(fn_name,ptr) \
+ const char **fn_name(void) {return(*(const char ***)(ptr));}
+#define FN_GLOBAL_BOOL(fn_name,ptr) \
+ BOOL fn_name(void) {return(*(BOOL *)(ptr));}
+#define FN_GLOBAL_CHAR(fn_name,ptr) \
+ char fn_name(void) {return(*(char *)(ptr));}
+#define FN_GLOBAL_INTEGER(fn_name,ptr) \
+ int fn_name(void) {return(*(int *)(ptr));}
+
+#define FN_LOCAL_STRING(fn_name,val) \
+ char *fn_name(int i) {return(lp_string((LP_SNUM_OK(i) && ServicePtrs[(i)]->val) ? ServicePtrs[(i)]->val : sDefault.val));}
+#define FN_LOCAL_CONST_STRING(fn_name,val) \
+ const char *fn_name(int i) {return (const char *)((LP_SNUM_OK(i) && ServicePtrs[(i)]->val) ? ServicePtrs[(i)]->val : sDefault.val);}
+#define FN_LOCAL_LIST(fn_name,val) \
+ const char **fn_name(int i) {return(const char **)(LP_SNUM_OK(i)? ServicePtrs[(i)]->val : sDefault.val);}
+#define FN_LOCAL_BOOL(fn_name,val) \
+ BOOL fn_name(int i) {return(LP_SNUM_OK(i)? ServicePtrs[(i)]->val : sDefault.val);}
+#define FN_LOCAL_CHAR(fn_name,val) \
+ char fn_name(int i) {return(LP_SNUM_OK(i)? ServicePtrs[(i)]->val : sDefault.val);}
+#define FN_LOCAL_INTEGER(fn_name,val) \
+ int fn_name(int i) {return(LP_SNUM_OK(i)? ServicePtrs[(i)]->val : sDefault.val);}
+
+FN_GLOBAL_STRING(lp_config_backend, &Globals.szConfigBackend)
+FN_GLOBAL_STRING(lp_smb_ports, &Globals.smb_ports)
+FN_GLOBAL_STRING(lp_dos_charset, &Globals.dos_charset)
+FN_GLOBAL_STRING(lp_unix_charset, &Globals.unix_charset)
+FN_GLOBAL_STRING(lp_display_charset, &Globals.display_charset)
+FN_GLOBAL_STRING(lp_logfile, &Globals.szLogFile)
+FN_GLOBAL_STRING(lp_configfile, &Globals.szConfigFile)
+FN_GLOBAL_STRING(lp_smb_passwd_file, &Globals.szSMBPasswdFile)
+FN_GLOBAL_STRING(lp_private_dir, &Globals.szPrivateDir)
+FN_GLOBAL_STRING(lp_serverstring, &Globals.szServerString)
+FN_GLOBAL_STRING(lp_printcapname, &Globals.szPrintcapname)
+FN_GLOBAL_STRING(lp_enumports_cmd, &Globals.szEnumPortsCommand)
+FN_GLOBAL_STRING(lp_addprinter_cmd, &Globals.szAddPrinterCommand)
+FN_GLOBAL_STRING(lp_deleteprinter_cmd, &Globals.szDeletePrinterCommand)
+FN_GLOBAL_STRING(lp_os2_driver_map, &Globals.szOs2DriverMap)
+FN_GLOBAL_STRING(lp_lockdir, &Globals.szLockDir)
+FN_GLOBAL_STRING(lp_piddir, &Globals.szPidDir)
+FN_GLOBAL_STRING(lp_mangling_method, &Globals.szManglingMethod)
+FN_GLOBAL_INTEGER(lp_mangle_prefix, &Globals.mangle_prefix)
+FN_GLOBAL_STRING(lp_utmpdir, &Globals.szUtmpDir)
+FN_GLOBAL_STRING(lp_wtmpdir, &Globals.szWtmpDir)
+FN_GLOBAL_BOOL(lp_utmp, &Globals.bUtmp)
+FN_GLOBAL_STRING(lp_rootdir, &Globals.szRootdir)
+FN_GLOBAL_STRING(lp_defaultservice, &Globals.szDefaultService)
+FN_GLOBAL_STRING(lp_msg_command, &Globals.szMsgCommand)
+FN_GLOBAL_STRING(lp_dfree_command, &Globals.szDfree)
+FN_GLOBAL_STRING(lp_get_quota_command, &Globals.szGetQuota)
+FN_GLOBAL_STRING(lp_set_quota_command, &Globals.szSetQuota)
+FN_GLOBAL_STRING(lp_hosts_equiv, &Globals.szHostsEquiv)
+FN_GLOBAL_STRING(lp_auto_services, &Globals.szAutoServices)
+FN_GLOBAL_STRING(lp_passwd_program, &Globals.szPasswdProgram)
+FN_GLOBAL_STRING(lp_passwd_chat, &Globals.szPasswdChat)
+FN_GLOBAL_STRING(lp_passwordserver, &Globals.szPasswordServer)
+FN_GLOBAL_STRING(lp_name_resolve_order, &Globals.szNameResolveOrder)
+FN_GLOBAL_STRING(lp_realm, &Globals.szRealm)
+FN_GLOBAL_STRING(lp_afs_username_map, &Globals.szAfsUsernameMap)
+FN_GLOBAL_STRING(lp_username_map, &Globals.szUsernameMap)
+FN_GLOBAL_CONST_STRING(lp_logon_script, &Globals.szLogonScript)
+FN_GLOBAL_CONST_STRING(lp_logon_path, &Globals.szLogonPath)
+FN_GLOBAL_CONST_STRING(lp_logon_drive, &Globals.szLogonDrive)
+FN_GLOBAL_CONST_STRING(lp_logon_home, &Globals.szLogonHome)
+FN_GLOBAL_STRING(lp_remote_announce, &Globals.szRemoteAnnounce)
+FN_GLOBAL_STRING(lp_remote_browse_sync, &Globals.szRemoteBrowseSync)
+FN_GLOBAL_LIST(lp_wins_server_list, &Globals.szWINSservers)
+FN_GLOBAL_LIST(lp_interfaces, &Globals.szInterfaces)
+FN_GLOBAL_STRING(lp_socket_address, &Globals.szSocketAddress)
+FN_GLOBAL_STRING(lp_nis_home_map_name, &Globals.szNISHomeMapName)
+static FN_GLOBAL_STRING(lp_announce_version, &Globals.szAnnounceVersion)
+FN_GLOBAL_LIST(lp_netbios_aliases, &Globals.szNetbiosAliases)
+FN_GLOBAL_LIST(lp_passdb_backend, &Globals.szPassdbBackend)
+FN_GLOBAL_STRING(lp_gums_backend, &Globals.szGumsBackend)
+FN_GLOBAL_LIST(lp_preload_modules, &Globals.szPreloadModules)
+FN_GLOBAL_STRING(lp_panic_action, &Globals.szPanicAction)
+FN_GLOBAL_STRING(lp_adduser_script, &Globals.szAddUserScript)
+FN_GLOBAL_STRING(lp_deluser_script, &Globals.szDelUserScript)
+
+FN_GLOBAL_CONST_STRING(lp_guestaccount, &Globals.szGuestaccount)
+FN_GLOBAL_STRING(lp_addgroup_script, &Globals.szAddGroupScript)
+FN_GLOBAL_STRING(lp_delgroup_script, &Globals.szDelGroupScript)
+FN_GLOBAL_STRING(lp_addusertogroup_script, &Globals.szAddUserToGroupScript)
+FN_GLOBAL_STRING(lp_deluserfromgroup_script, &Globals.szDelUserFromGroupScript)
+FN_GLOBAL_STRING(lp_setprimarygroup_script, &Globals.szSetPrimaryGroupScript)
+
+FN_GLOBAL_STRING(lp_addmachine_script, &Globals.szAddMachineScript)
+
+FN_GLOBAL_STRING(lp_shutdown_script, &Globals.szShutdownScript)
+FN_GLOBAL_STRING(lp_abort_shutdown_script, &Globals.szAbortShutdownScript)
+
+FN_GLOBAL_STRING(lp_wins_hook, &Globals.szWINSHook)
+FN_GLOBAL_STRING(lp_wins_partners, &Globals.szWINSPartners)
+FN_GLOBAL_STRING(lp_template_primary_group, &Globals.szTemplatePrimaryGroup)
+FN_GLOBAL_CONST_STRING(lp_template_homedir, &Globals.szTemplateHomedir)
+FN_GLOBAL_CONST_STRING(lp_template_shell, &Globals.szTemplateShell)
+FN_GLOBAL_CONST_STRING(lp_winbind_separator, &Globals.szWinbindSeparator)
+FN_GLOBAL_STRING(lp_acl_compatibility, &Globals.szAclCompat)
+FN_GLOBAL_BOOL(lp_winbind_enable_local_accounts, &Globals.bWinbindEnableLocalAccounts)
+FN_GLOBAL_BOOL(lp_winbind_enum_users, &Globals.bWinbindEnumUsers)
+FN_GLOBAL_BOOL(lp_winbind_enum_groups, &Globals.bWinbindEnumGroups)
+FN_GLOBAL_BOOL(lp_winbind_use_default_domain, &Globals.bWinbindUseDefaultDomain)
+FN_GLOBAL_BOOL(lp_winbind_trusted_domains_only, &Globals.bWinbindTrustedDomainsOnly)
+
+FN_GLOBAL_STRING(lp_idmap_backend, &Globals.szIdmapBackend)
+FN_GLOBAL_BOOL(lp_enable_rid_algorithm, &Globals.bEnableRidAlgorithm)
+
+#ifdef WITH_LDAP_SAMCONFIG
+FN_GLOBAL_STRING(lp_ldap_server, &Globals.szLdapServer)
+FN_GLOBAL_INTEGER(lp_ldap_port, &Globals.ldap_port)
+#endif
+FN_GLOBAL_STRING(lp_ldap_suffix, &Globals.szLdapSuffix)
+FN_GLOBAL_STRING(lp_ldap_filter, &Globals.szLdapFilter)
+FN_GLOBAL_STRING(lp_ldap_admin_dn, &Globals.szLdapAdminDn)
+FN_GLOBAL_INTEGER(lp_ldap_ssl, &Globals.ldap_ssl)
+FN_GLOBAL_INTEGER(lp_ldap_passwd_sync, &Globals.ldap_passwd_sync)
+FN_GLOBAL_BOOL(lp_ldap_delete_dn, &Globals.ldap_delete_dn)
+FN_GLOBAL_INTEGER(lp_ldap_replication_sleep, &Globals.ldap_replication_sleep)
+FN_GLOBAL_STRING(lp_add_share_cmd, &Globals.szAddShareCommand)
+FN_GLOBAL_STRING(lp_change_share_cmd, &Globals.szChangeShareCommand)
+FN_GLOBAL_STRING(lp_delete_share_cmd, &Globals.szDeleteShareCommand)
+
+FN_GLOBAL_BOOL(lp_disable_netbios, &Globals.bDisableNetbios)
+FN_GLOBAL_BOOL(lp_ms_add_printer_wizard, &Globals.bMsAddPrinterWizard)
+FN_GLOBAL_BOOL(lp_dns_proxy, &Globals.bDNSproxy)
+FN_GLOBAL_BOOL(lp_wins_support, &Globals.bWINSsupport)
+FN_GLOBAL_BOOL(lp_we_are_a_wins_server, &Globals.bWINSsupport)
+FN_GLOBAL_BOOL(lp_wins_proxy, &Globals.bWINSproxy)
+FN_GLOBAL_BOOL(lp_local_master, &Globals.bLocalMaster)
+FN_GLOBAL_BOOL(lp_domain_logons, &Globals.bDomainLogons)
+FN_GLOBAL_BOOL(lp_load_printers, &Globals.bLoadPrinters)
+FN_GLOBAL_BOOL(lp_readbmpx, &Globals.bReadbmpx)
+FN_GLOBAL_BOOL(lp_readraw, &Globals.bReadRaw)
+FN_GLOBAL_BOOL(lp_large_readwrite, &Globals.bLargeReadwrite)
+FN_GLOBAL_BOOL(lp_writeraw, &Globals.bWriteRaw)
+FN_GLOBAL_BOOL(lp_null_passwords, &Globals.bNullPasswords)
+FN_GLOBAL_BOOL(lp_obey_pam_restrictions, &Globals.bObeyPamRestrictions)
+FN_GLOBAL_BOOL(lp_encrypted_passwords, &Globals.bEncryptPasswords)
+FN_GLOBAL_BOOL(lp_update_encrypted, &Globals.bUpdateEncrypt)
+FN_GLOBAL_INTEGER(lp_client_schannel, &Globals.clientSchannel)
+FN_GLOBAL_INTEGER(lp_server_schannel, &Globals.serverSchannel)
+FN_GLOBAL_BOOL(lp_syslog_only, &Globals.bSyslogOnly)
+FN_GLOBAL_BOOL(lp_timestamp_logs, &Globals.bTimestampLogs)
+FN_GLOBAL_BOOL(lp_debug_hires_timestamp, &Globals.bDebugHiresTimestamp)
+FN_GLOBAL_BOOL(lp_debug_pid, &Globals.bDebugPid)
+FN_GLOBAL_BOOL(lp_debug_uid, &Globals.bDebugUid)
+FN_GLOBAL_BOOL(lp_browse_list, &Globals.bBrowseList)
+FN_GLOBAL_BOOL(lp_nis_home_map, &Globals.bNISHomeMap)
+static FN_GLOBAL_BOOL(lp_time_server, &Globals.bTimeServer)
+FN_GLOBAL_BOOL(lp_bind_interfaces_only, &Globals.bBindInterfacesOnly)
+FN_GLOBAL_BOOL(lp_pam_password_change, &Globals.bPamPasswordChange)
+FN_GLOBAL_BOOL(lp_unix_password_sync, &Globals.bUnixPasswdSync)
+FN_GLOBAL_BOOL(lp_passwd_chat_debug, &Globals.bPasswdChatDebug)
+FN_GLOBAL_INTEGER(lp_passwd_chat_timeout, &Globals.iPasswdChatTimeout)
+FN_GLOBAL_BOOL(lp_unicode, &Globals.bUnicode)
+FN_GLOBAL_BOOL(lp_nt_pipe_support, &Globals.bNTPipeSupport)
+FN_GLOBAL_BOOL(lp_nt_status_support, &Globals.bNTStatusSupport)
+FN_GLOBAL_BOOL(lp_stat_cache, &Globals.bStatCache)
+FN_GLOBAL_BOOL(lp_allow_trusted_domains, &Globals.bAllowTrustedDomains)
+FN_GLOBAL_INTEGER(lp_restrict_anonymous, &Globals.restrict_anonymous)
+FN_GLOBAL_BOOL(lp_lanman_auth, &Globals.bLanmanAuth)
+FN_GLOBAL_BOOL(lp_ntlm_auth, &Globals.bNTLMAuth)
+FN_GLOBAL_BOOL(lp_client_plaintext_auth, &Globals.bClientPlaintextAuth)
+FN_GLOBAL_BOOL(lp_client_lanman_auth, &Globals.bClientLanManAuth)
+FN_GLOBAL_BOOL(lp_client_ntlmv2_auth, &Globals.bClientNTLMv2Auth)
+FN_GLOBAL_BOOL(lp_host_msdfs, &Globals.bHostMSDfs)
+FN_GLOBAL_BOOL(lp_kernel_oplocks, &Globals.bKernelOplocks)
+FN_GLOBAL_BOOL(lp_enhanced_browsing, &Globals.enhanced_browsing)
+FN_GLOBAL_BOOL(lp_use_mmap, &Globals.bUseMmap)
+FN_GLOBAL_BOOL(lp_unix_extensions, &Globals.bUnixExtensions)
+FN_GLOBAL_BOOL(lp_use_spnego, &Globals.bUseSpnego)
+FN_GLOBAL_BOOL(lp_client_use_spnego, &Globals.bClientUseSpnego)
+FN_GLOBAL_BOOL(lp_hostname_lookups, &Globals.bHostnameLookups)
+FN_GLOBAL_BOOL(lp_kernel_change_notify, &Globals.bKernelChangeNotify)
+FN_GLOBAL_INTEGER(lp_os_level, &Globals.os_level)
+FN_GLOBAL_INTEGER(lp_max_ttl, &Globals.max_ttl)
+FN_GLOBAL_INTEGER(lp_max_wins_ttl, &Globals.max_wins_ttl)
+FN_GLOBAL_INTEGER(lp_min_wins_ttl, &Globals.min_wins_ttl)
+FN_GLOBAL_INTEGER(lp_max_log_size, &Globals.max_log_size)
+FN_GLOBAL_INTEGER(lp_max_open_files, &Globals.max_open_files)
+FN_GLOBAL_INTEGER(lp_maxxmit, &Globals.max_xmit)
+FN_GLOBAL_INTEGER(lp_maxmux, &Globals.max_mux)
+FN_GLOBAL_INTEGER(lp_passwordlevel, &Globals.pwordlevel)
+FN_GLOBAL_INTEGER(lp_usernamelevel, &Globals.unamelevel)
+FN_GLOBAL_INTEGER(lp_deadtime, &Globals.deadtime)
+FN_GLOBAL_INTEGER(lp_maxprotocol, &Globals.maxprotocol)
+FN_GLOBAL_INTEGER(lp_minprotocol, &Globals.minprotocol)
+FN_GLOBAL_INTEGER(lp_security, &Globals.security)
+FN_GLOBAL_LIST(lp_auth_methods, &Globals.AuthMethods)
+FN_GLOBAL_BOOL(lp_paranoid_server_security, &Globals.paranoid_server_security)
+FN_GLOBAL_INTEGER(lp_maxdisksize, &Globals.maxdisksize)
+FN_GLOBAL_INTEGER(lp_lpqcachetime, &Globals.lpqcachetime)
+FN_GLOBAL_INTEGER(lp_max_smbd_processes, &Globals.iMaxSmbdProcesses)
+FN_GLOBAL_INTEGER(lp_disable_spoolss, &Globals.bDisableSpoolss)
+FN_GLOBAL_INTEGER(lp_syslog, &Globals.syslog)
+static FN_GLOBAL_INTEGER(lp_announce_as, &Globals.announce_as)
+FN_GLOBAL_INTEGER(lp_lm_announce, &Globals.lm_announce)
+FN_GLOBAL_INTEGER(lp_lm_interval, &Globals.lm_interval)
+FN_GLOBAL_INTEGER(lp_machine_password_timeout, &Globals.machine_password_timeout)
+FN_GLOBAL_INTEGER(lp_change_notify_timeout, &Globals.change_notify_timeout)
+FN_GLOBAL_INTEGER(lp_map_to_guest, &Globals.map_to_guest)
+FN_GLOBAL_INTEGER(lp_min_passwd_length, &Globals.min_passwd_length)
+FN_GLOBAL_BOOL(lp_use_cracklib, &Globals.use_cracklib)
+FN_GLOBAL_INTEGER(lp_oplock_break_wait_time, &Globals.oplock_break_wait_time)
+FN_GLOBAL_INTEGER(lp_lock_spin_count, &Globals.iLockSpinCount)
+FN_GLOBAL_INTEGER(lp_lock_sleep_time, &Globals.iLockSpinTime)
+FN_LOCAL_STRING(lp_preexec, szPreExec)
+FN_LOCAL_STRING(lp_postexec, szPostExec)
+FN_LOCAL_STRING(lp_rootpreexec, szRootPreExec)
+FN_LOCAL_STRING(lp_rootpostexec, szRootPostExec)
+FN_LOCAL_STRING(lp_servicename, szService)
+FN_LOCAL_CONST_STRING(lp_const_servicename, szService)
+FN_LOCAL_STRING(lp_pathname, szPath)
+FN_LOCAL_STRING(lp_dontdescend, szDontdescend)
+FN_LOCAL_STRING(lp_username, szUsername)
+FN_LOCAL_LIST(lp_invalid_users, szInvalidUsers)
+FN_LOCAL_LIST(lp_valid_users, szValidUsers)
+FN_LOCAL_LIST(lp_admin_users, szAdminUsers)
+FN_LOCAL_STRING(lp_printcommand, szPrintcommand)
+FN_LOCAL_STRING(lp_lpqcommand, szLpqcommand)
+FN_LOCAL_STRING(lp_lprmcommand, szLprmcommand)
+FN_LOCAL_STRING(lp_lppausecommand, szLppausecommand)
+FN_LOCAL_STRING(lp_lpresumecommand, szLpresumecommand)
+FN_LOCAL_STRING(lp_queuepausecommand, szQueuepausecommand)
+FN_LOCAL_STRING(lp_queueresumecommand, szQueueresumecommand)
+static FN_LOCAL_STRING(_lp_printername, szPrintername)
+FN_LOCAL_LIST(lp_hostsallow, szHostsallow)
+FN_LOCAL_LIST(lp_hostsdeny, szHostsdeny)
+FN_LOCAL_STRING(lp_magicscript, szMagicScript)
+FN_LOCAL_STRING(lp_magicoutput, szMagicOutput)
+FN_LOCAL_STRING(lp_comment, comment)
+FN_LOCAL_STRING(lp_force_user, force_user)
+FN_LOCAL_STRING(lp_force_group, force_group)
+FN_LOCAL_LIST(lp_readlist, readlist)
+FN_LOCAL_LIST(lp_writelist, writelist)
+FN_LOCAL_LIST(lp_printer_admin, printer_admin)
+FN_LOCAL_STRING(lp_fstype, fstype)
+FN_LOCAL_LIST(lp_vfs_objects, szVfsObjects)
+FN_LOCAL_STRING(lp_msdfs_proxy, szMSDfsProxy)
+static FN_LOCAL_STRING(lp_volume, volume)
+FN_LOCAL_STRING(lp_mangled_map, szMangledMap)
+FN_LOCAL_STRING(lp_veto_files, szVetoFiles)
+FN_LOCAL_STRING(lp_hide_files, szHideFiles)
+FN_LOCAL_STRING(lp_veto_oplocks, szVetoOplockFiles)
+FN_LOCAL_BOOL(lp_msdfs_root, bMSDfsRoot)
+FN_LOCAL_BOOL(lp_autoloaded, autoloaded)
+FN_LOCAL_BOOL(lp_preexec_close, bPreexecClose)
+FN_LOCAL_BOOL(lp_rootpreexec_close, bRootpreexecClose)
+FN_LOCAL_BOOL(lp_casesensitive, bCaseSensitive)
+FN_LOCAL_BOOL(lp_preservecase, bCasePreserve)
+FN_LOCAL_BOOL(lp_shortpreservecase, bShortCasePreserve)
+FN_LOCAL_BOOL(lp_casemangle, bCaseMangle)
+FN_LOCAL_BOOL(lp_hide_dot_files, bHideDotFiles)
+FN_LOCAL_BOOL(lp_hide_special_files, bHideSpecialFiles)
+FN_LOCAL_BOOL(lp_hideunreadable, bHideUnReadable)
+FN_LOCAL_BOOL(lp_hideunwriteable_files, bHideUnWriteableFiles)
+FN_LOCAL_BOOL(lp_browseable, bBrowseable)
+FN_LOCAL_BOOL(lp_readonly, bRead_only)
+FN_LOCAL_BOOL(lp_no_set_dir, bNo_set_dir)
+FN_LOCAL_BOOL(lp_guest_ok, bGuest_ok)
+FN_LOCAL_BOOL(lp_guest_only, bGuest_only)
+FN_LOCAL_BOOL(lp_print_ok, bPrint_ok)
+FN_LOCAL_BOOL(lp_map_hidden, bMap_hidden)
+FN_LOCAL_BOOL(lp_map_archive, bMap_archive)
+FN_LOCAL_BOOL(lp_store_dos_attributes, bStoreDosAttributes)
+FN_LOCAL_BOOL(lp_locking, bLocking)
+FN_LOCAL_BOOL(lp_strict_locking, bStrictLocking)
+FN_LOCAL_BOOL(lp_posix_locking, bPosixLocking)
+FN_LOCAL_BOOL(lp_share_modes, bShareModes)
+FN_LOCAL_BOOL(lp_oplocks, bOpLocks)
+FN_LOCAL_BOOL(lp_level2_oplocks, bLevel2OpLocks)
+FN_LOCAL_BOOL(lp_onlyuser, bOnlyUser)
+FN_LOCAL_BOOL(lp_manglednames, bMangledNames)
+FN_LOCAL_BOOL(lp_widelinks, bWidelinks)
+FN_LOCAL_BOOL(lp_symlinks, bSymlinks)
+FN_LOCAL_BOOL(lp_syncalways, bSyncAlways)
+FN_LOCAL_BOOL(lp_strict_allocate, bStrictAllocate)
+FN_LOCAL_BOOL(lp_strict_sync, bStrictSync)
+FN_LOCAL_BOOL(lp_map_system, bMap_system)
+FN_LOCAL_BOOL(lp_delete_readonly, bDeleteReadonly)
+FN_LOCAL_BOOL(lp_fake_oplocks, bFakeOplocks)
+FN_LOCAL_BOOL(lp_recursive_veto_delete, bDeleteVetoFiles)
+FN_LOCAL_BOOL(lp_dos_filemode, bDosFilemode)
+FN_LOCAL_BOOL(lp_dos_filetimes, bDosFiletimes)
+FN_LOCAL_BOOL(lp_dos_filetime_resolution, bDosFiletimeResolution)
+FN_LOCAL_BOOL(lp_fake_dir_create_times, bFakeDirCreateTimes)
+FN_LOCAL_BOOL(lp_blocking_locks, bBlockingLocks)
+FN_LOCAL_BOOL(lp_inherit_perms, bInheritPerms)
+FN_LOCAL_BOOL(lp_inherit_acls, bInheritACLS)
+FN_LOCAL_BOOL(lp_use_client_driver, bUseClientDriver)
+FN_LOCAL_BOOL(lp_default_devmode, bDefaultDevmode)
+FN_LOCAL_BOOL(lp_nt_acl_support, bNTAclSupport)
+FN_LOCAL_BOOL(lp_ea_support, bEASupport)
+FN_LOCAL_BOOL(_lp_use_sendfile, bUseSendfile)
+FN_LOCAL_BOOL(lp_profile_acls, bProfileAcls)
+FN_LOCAL_BOOL(lp_map_acl_inherit, bMap_acl_inherit)
+FN_LOCAL_BOOL(lp_afs_share, bAfs_Share)
+FN_LOCAL_INTEGER(lp_create_mask, iCreate_mask)
+FN_LOCAL_INTEGER(lp_force_create_mode, iCreate_force_mode)
+FN_LOCAL_INTEGER(lp_security_mask, iSecurity_mask)
+FN_LOCAL_INTEGER(lp_force_security_mode, iSecurity_force_mode)
+FN_LOCAL_INTEGER(lp_dir_mask, iDir_mask)
+FN_LOCAL_INTEGER(lp_force_dir_mode, iDir_force_mode)
+FN_LOCAL_INTEGER(lp_dir_security_mask, iDir_Security_mask)
+FN_LOCAL_INTEGER(lp_force_dir_security_mode, iDir_Security_force_mode)
+FN_LOCAL_INTEGER(lp_max_connections, iMaxConnections)
+FN_LOCAL_INTEGER(lp_defaultcase, iDefaultCase)
+FN_LOCAL_INTEGER(lp_minprintspace, iMinPrintSpace)
+FN_LOCAL_INTEGER(lp_printing, iPrinting)
+FN_LOCAL_INTEGER(lp_max_reported_jobs, iMaxReportedPrintJobs)
+FN_LOCAL_INTEGER(lp_oplock_contention_limit, iOplockContentionLimit)
+FN_LOCAL_INTEGER(lp_csc_policy, iCSCPolicy)
+FN_LOCAL_INTEGER(lp_write_cache_size, iWriteCacheSize)
+FN_LOCAL_INTEGER(lp_block_size, iBlock_size)
+FN_LOCAL_CHAR(lp_magicchar, magic_char)
+FN_GLOBAL_INTEGER(lp_winbind_cache_time, &Globals.winbind_cache_time)
+FN_GLOBAL_INTEGER(lp_algorithmic_rid_base, &Globals.AlgorithmicRidBase)
+FN_GLOBAL_INTEGER(lp_name_cache_timeout, &Globals.name_cache_timeout)
+FN_GLOBAL_INTEGER(lp_client_signing, &Globals.client_signing)
+FN_GLOBAL_INTEGER(lp_server_signing, &Globals.server_signing)
+
+/* local prototypes */
+
+static int map_parameter(const char *pszParmName);
+static BOOL set_boolean(BOOL *pb, const char *pszParmValue);
+static int getservicebyname(const char *pszServiceName,
+ service * pserviceDest);
+static void copy_service(service * pserviceDest,
+ service * pserviceSource, BOOL *pcopymapDest);
+static BOOL service_ok(int iService);
+static BOOL do_parameter(const char *pszParmName, const char *pszParmValue);
+static BOOL do_section(const char *pszSectionName);
+static void init_copymap(service * pservice);
+
+/* This is a helper function for parametrical options support. */
+/* It returns a pointer to parametrical option value if it exists or NULL otherwise */
+/* Actual parametrical functions are quite simple */
+static param_opt_struct *get_parametrics(int snum, const char *type, const char *option)
+{
+ BOOL global_section = False;
+ char* param_key;
+ param_opt_struct *data;
+
+ if (snum >= iNumServices) return NULL;
+
+ if (snum < 0) {
+ data = Globals.param_opt;
+ global_section = True;
+ } else {
+ data = ServicePtrs[snum]->param_opt;
+ }
+
+ asprintf(&param_key, "%s:%s", type, option);
+ if (!param_key) {
+ DEBUG(0,("asprintf failed!\n"));
+ return NULL;
+ }
+
+ while (data) {
+ if (strcmp(data->key, param_key) == 0) {
+ string_free(&param_key);
+ return data;
+ }
+ data = data->next;
+ }
+
+ if (!global_section) {
+ /* Try to fetch the same option but from globals */
+ /* but only if we are not already working with Globals */
+ data = Globals.param_opt;
+ while (data) {
+ if (strcmp(data->key, param_key) == 0) {
+ string_free(&param_key);
+ return data;
+ }
+ data = data->next;
+ }
+ }
+
+ string_free(&param_key);
+
+ return NULL;
+}
+
+
+/*******************************************************************
+convenience routine to return int parameters.
+********************************************************************/
+static int lp_int(const char *s)
+{
+
+ if (!s) {
+ DEBUG(0,("lp_int(%s): is called with NULL!\n",s));
+ return (-1);
+ }
+
+ return atoi(s);
+}
+
+/*******************************************************************
+convenience routine to return unsigned long parameters.
+********************************************************************/
+static int lp_ulong(const char *s)
+{
+
+ if (!s) {
+ DEBUG(0,("lp_int(%s): is called with NULL!\n",s));
+ return (-1);
+ }
+
+ return strtoul(s, NULL, 10);
+}
+
+/*******************************************************************
+convenience routine to return boolean parameters.
+********************************************************************/
+static BOOL lp_bool(const char *s)
+{
+ BOOL ret = False;
+
+ if (!s) {
+ DEBUG(0,("lp_bool(%s): is called with NULL!\n",s));
+ return False;
+ }
+
+ if (!set_boolean(&ret,s)) {
+ DEBUG(0,("lp_bool(%s): value is not boolean!\n",s));
+ return False;
+ }
+
+ return ret;
+}
+
+/*******************************************************************
+convenience routine to return enum parameters.
+********************************************************************/
+static int lp_enum(const char *s,const struct enum_list *_enum)
+{
+ int i;
+
+ if (!s || !_enum) {
+ DEBUG(0,("lp_enum(%s,enum): is called with NULL!\n",s));
+ return (-1);
+ }
+
+ for (i=0; _enum[i].name; i++) {
+ if (strequal(_enum[i].name,s))
+ return _enum[i].value;
+ }
+
+ DEBUG(0,("lp_enum(%s,enum): value is not in enum_list!\n",s));
+ return (-1);
+}
+
+
+/* DO NOT USE lp_parm_string ANYMORE!!!!
+ * use lp_parm_const_string or lp_parm_talloc_string
+ *
+ * lp_parm_string is only used to let old modules find this symbol
+ */
+#undef lp_parm_string
+ char *lp_parm_string(const char *servicename, const char *type, const char *option)
+{
+ return lp_parm_talloc_string(lp_servicenumber(servicename), type, option, NULL);
+}
+
+/* Return parametric option from a given service. Type is a part of option before ':' */
+/* Parametric option has following syntax: 'Type: option = value' */
+/* the returned value is talloced in lp_talloc */
+char *lp_parm_talloc_string(int snum, const char *type, const char *option, const char *def)
+{
+ param_opt_struct *data = get_parametrics(snum, type, option);
+
+ if (data == NULL||data->value==NULL) {
+ if (def) {
+ return lp_string(def);
+ } else {
+ return NULL;
+ }
+ }
+
+ return lp_string(data->value);
+}
+
+/* Return parametric option from a given service. Type is a part of option before ':' */
+/* Parametric option has following syntax: 'Type: option = value' */
+const char *lp_parm_const_string(int snum, const char *type, const char *option, const char *def)
+{
+ param_opt_struct *data = get_parametrics(snum, type, option);
+
+ if (data == NULL||data->value==NULL)
+ return def;
+
+ return data->value;
+}
+
+/* Return parametric option from a given service. Type is a part of option before ':' */
+/* Parametric option has following syntax: 'Type: option = value' */
+
+const char **lp_parm_string_list(int snum, const char *type, const char *option, const char **def)
+{
+ param_opt_struct *data = get_parametrics(snum, type, option);
+
+ if (data == NULL||data->value==NULL)
+ return (const char **)def;
+
+ if (data->list==NULL) {
+ data->list = str_list_make(data->value, NULL);
+ }
+
+ return (const char **)data->list;
+}
+
+/* Return parametric option from a given service. Type is a part of option before ':' */
+/* Parametric option has following syntax: 'Type: option = value' */
+
+int lp_parm_int(int snum, const char *type, const char *option, int def)
+{
+ param_opt_struct *data = get_parametrics(snum, type, option);
+
+ if (data && data->value && *data->value)
+ return lp_int(data->value);
+
+ return def;
+}
+
+/* Return parametric option from a given service. Type is a part of option before ':' */
+/* Parametric option has following syntax: 'Type: option = value' */
+
+unsigned long lp_parm_ulong(int snum, const char *type, const char *option, unsigned long def)
+{
+ param_opt_struct *data = get_parametrics(snum, type, option);
+
+ if (data && data->value && *data->value)
+ return lp_ulong(data->value);
+
+ return def;
+}
+
+/* Return parametric option from a given service. Type is a part of option before ':' */
+/* Parametric option has following syntax: 'Type: option = value' */
+
+BOOL lp_parm_bool(int snum, const char *type, const char *option, BOOL def)
+{
+ param_opt_struct *data = get_parametrics(snum, type, option);
+
+ if (data && data->value && *data->value)
+ return lp_bool(data->value);
+
+ return def;
+}
+
+/* Return parametric option from a given service. Type is a part of option before ':' */
+/* Parametric option has following syntax: 'Type: option = value' */
+
+int lp_parm_enum(int snum, const char *type, const char *option,
+ const struct enum_list *_enum, int def)
+{
+ param_opt_struct *data = get_parametrics(snum, type, option);
+
+ if (data && data->value && *data->value && _enum)
+ return lp_enum(data->value, _enum);
+
+ return def;
+}
+
+
+/***************************************************************************
+ Initialise a service to the defaults.
+***************************************************************************/
+
+static void init_service(service * pservice)
+{
+ memset((char *)pservice, '\0', sizeof(service));
+ copy_service(pservice, &sDefault, NULL);
+}
+
+/***************************************************************************
+ Free the dynamically allocated parts of a service struct.
+***************************************************************************/
+
+static void free_service(service *pservice)
+{
+ int i;
+ param_opt_struct *data, *pdata;
+ if (!pservice)
+ return;
+
+ if (pservice->szService)
+ DEBUG(5, ("free_service: Freeing service %s\n",
+ pservice->szService));
+
+ string_free(&pservice->szService);
+ SAFE_FREE(pservice->copymap);
+
+ for (i = 0; parm_table[i].label; i++) {
+ if ((parm_table[i].type == P_STRING ||
+ parm_table[i].type == P_USTRING) &&
+ parm_table[i].class == P_LOCAL)
+ string_free((char **)
+ (((char *)pservice) +
+ PTR_DIFF(parm_table[i].ptr, &sDefault)));
+ else if (parm_table[i].type == P_LIST &&
+ parm_table[i].class == P_LOCAL)
+ str_list_free((char ***)
+ (((char *)pservice) +
+ PTR_DIFF(parm_table[i].ptr, &sDefault)));
+ }
+
+ data = pservice->param_opt;
+ if (data)
+ DEBUG(5,("Freeing parametrics:\n"));
+ while (data) {
+ DEBUG(5,("[%s = %s]\n", data->key, data->value));
+ string_free(&data->key);
+ string_free(&data->value);
+ str_list_free(&data->list);
+ pdata = data->next;
+ SAFE_FREE(data);
+ data = pdata;
+ }
+
+ ZERO_STRUCTP(pservice);
+}
+
+/***************************************************************************
+ Add a new service to the services array initialising it with the given
+ service.
+***************************************************************************/
+
+static int add_a_service(const service *pservice, const char *name)
+{
+ int i;
+ service tservice;
+ int num_to_alloc = iNumServices + 1;
+ param_opt_struct *data, *pdata;
+
+ tservice = *pservice;
+
+ /* it might already exist */
+ if (name) {
+ i = getservicebyname(name, NULL);
+ if (i >= 0) {
+ /* Clean all parametric options for service */
+ /* They will be added during parsing again */
+ data = ServicePtrs[i]->param_opt;
+ while (data) {
+ string_free(&data->key);
+ string_free(&data->value);
+ str_list_free(&data->list);
+ pdata = data->next;
+ SAFE_FREE(data);
+ data = pdata;
+ }
+ ServicePtrs[i]->param_opt = NULL;
+ return (i);
+ }
+ }
+
+ /* find an invalid one */
+ for (i = 0; i < iNumServices; i++)
+ if (!ServicePtrs[i]->valid)
+ break;
+
+ /* if not, then create one */
+ if (i == iNumServices) {
+ service **tsp;
+
+ tsp = (service **) Realloc(ServicePtrs,
+ sizeof(service *) *
+ num_to_alloc);
+
+ if (!tsp) {
+ DEBUG(0,("add_a_service: failed to enlarge ServicePtrs!\n"));
+ return (-1);
+ }
+ else {
+ ServicePtrs = tsp;
+ ServicePtrs[iNumServices] =
+ (service *) malloc(sizeof(service));
+ }
+ if (!ServicePtrs[iNumServices]) {
+ DEBUG(0,("add_a_service: out of memory!\n"));
+ return (-1);
+ }
+
+ iNumServices++;
+ } else
+ free_service(ServicePtrs[i]);
+
+ ServicePtrs[i]->valid = True;
+
+ init_service(ServicePtrs[i]);
+ copy_service(ServicePtrs[i], &tservice, NULL);
+ if (name)
+ string_set(&ServicePtrs[i]->szService, name);
+ return (i);
+}
+
+/***************************************************************************
+ Add a new home service, with the specified home directory, defaults coming
+ from service ifrom.
+***************************************************************************/
+
+BOOL lp_add_home(const char *pszHomename, int iDefaultService,
+ const char *user, const char *pszHomedir)
+{
+ int i;
+ pstring newHomedir;
+
+ i = add_a_service(ServicePtrs[iDefaultService], pszHomename);
+
+ if (i < 0)
+ return (False);
+
+ if (!(*(ServicePtrs[iDefaultService]->szPath))
+ || strequal(ServicePtrs[iDefaultService]->szPath, lp_pathname(GLOBAL_SECTION_SNUM))) {
+ pstrcpy(newHomedir, pszHomedir);
+ string_set(&ServicePtrs[i]->szPath, newHomedir);
+ }
+
+ if (!(*(ServicePtrs[i]->comment))) {
+ pstring comment;
+ slprintf(comment, sizeof(comment) - 1,
+ "Home directory of %s", user);
+ string_set(&ServicePtrs[i]->comment, comment);
+ }
+
+ /* set the browseable flag from the gloabl default */
+
+ ServicePtrs[i]->bBrowseable = sDefault.bBrowseable;
+
+ DEBUG(3, ("adding home's share [%s] for user '%s' at '%s'\n", pszHomename,
+ user, newHomedir));
+
+ return (True);
+}
+
+/***************************************************************************
+ Add a new service, based on an old one.
+***************************************************************************/
+
+int lp_add_service(const char *pszService, int iDefaultService)
+{
+ return (add_a_service(ServicePtrs[iDefaultService], pszService));
+}
+
+/***************************************************************************
+ Add the IPC service.
+***************************************************************************/
+
+static BOOL lp_add_ipc(const char *ipc_name, BOOL guest_ok)
+{
+ pstring comment;
+ int i = add_a_service(&sDefault, ipc_name);
+
+ if (i < 0)
+ return (False);
+
+ slprintf(comment, sizeof(comment) - 1,
+ "IPC Service (%s)", Globals.szServerString);
+
+ string_set(&ServicePtrs[i]->szPath, tmpdir());
+ string_set(&ServicePtrs[i]->szUsername, "");
+ string_set(&ServicePtrs[i]->comment, comment);
+ string_set(&ServicePtrs[i]->fstype, "IPC");
+ ServicePtrs[i]->iMaxConnections = 0;
+ ServicePtrs[i]->bAvailable = True;
+ ServicePtrs[i]->bRead_only = True;
+ ServicePtrs[i]->bGuest_only = False;
+ ServicePtrs[i]->bGuest_ok = guest_ok;
+ ServicePtrs[i]->bPrint_ok = False;
+ ServicePtrs[i]->bBrowseable = sDefault.bBrowseable;
+
+ DEBUG(3, ("adding IPC service\n"));
+
+ return (True);
+}
+
+/***************************************************************************
+ Add a new printer service, with defaults coming from service iFrom.
+***************************************************************************/
+
+BOOL lp_add_printer(const char *pszPrintername, int iDefaultService)
+{
+ const char *comment = "From Printcap";
+ int i = add_a_service(ServicePtrs[iDefaultService], pszPrintername);
+
+ if (i < 0)
+ return (False);
+
+ /* note that we do NOT default the availability flag to True - */
+ /* we take it from the default service passed. This allows all */
+ /* dynamic printers to be disabled by disabling the [printers] */
+ /* entry (if/when the 'available' keyword is implemented!). */
+
+ /* the printer name is set to the service name. */
+ string_set(&ServicePtrs[i]->szPrintername, pszPrintername);
+ string_set(&ServicePtrs[i]->comment, comment);
+
+ /* set the browseable flag from the gloabl default */
+ ServicePtrs[i]->bBrowseable = sDefault.bBrowseable;
+
+ /* Printers cannot be read_only. */
+ ServicePtrs[i]->bRead_only = False;
+ /* No share modes on printer services. */
+ ServicePtrs[i]->bShareModes = False;
+ /* No oplocks on printer services. */
+ ServicePtrs[i]->bOpLocks = False;
+ /* Printer services must be printable. */
+ ServicePtrs[i]->bPrint_ok = True;
+
+ DEBUG(3, ("adding printer service %s\n", pszPrintername));
+
+ return (True);
+}
+
+/***************************************************************************
+ Map a parameter's string representation to something we can use.
+ Returns False if the parameter string is not recognised, else TRUE.
+***************************************************************************/
+
+static int map_parameter(const char *pszParmName)
+{
+ int iIndex;
+
+ if (*pszParmName == '-')
+ return (-1);
+
+ for (iIndex = 0; parm_table[iIndex].label; iIndex++)
+ if (strwicmp(parm_table[iIndex].label, pszParmName) == 0)
+ return (iIndex);
+
+ /* Warn only if it isn't parametric option */
+ if (strchr(pszParmName, ':') == NULL)
+ DEBUG(0, ("Unknown parameter encountered: \"%s\"\n", pszParmName));
+ /* We do return 'fail' for parametric options as well because they are
+ stored in different storage
+ */
+ return (-1);
+}
+
+/***************************************************************************
+ Set a boolean variable from the text value stored in the passed string.
+ Returns True in success, False if the passed string does not correctly
+ represent a boolean.
+***************************************************************************/
+
+static BOOL set_boolean(BOOL *pb, const char *pszParmValue)
+{
+ BOOL bRetval;
+
+ bRetval = True;
+ if (strwicmp(pszParmValue, "yes") == 0 ||
+ strwicmp(pszParmValue, "true") == 0 ||
+ strwicmp(pszParmValue, "1") == 0)
+ *pb = True;
+ else if (strwicmp(pszParmValue, "no") == 0 ||
+ strwicmp(pszParmValue, "False") == 0 ||
+ strwicmp(pszParmValue, "0") == 0)
+ *pb = False;
+ else {
+ DEBUG(0,
+ ("ERROR: Badly formed boolean in configuration file: \"%s\".\n",
+ pszParmValue));
+ bRetval = False;
+ }
+ return (bRetval);
+}
+
+/***************************************************************************
+Find a service by name. Otherwise works like get_service.
+***************************************************************************/
+
+static int getservicebyname(const char *pszServiceName, service * pserviceDest)
+{
+ int iService;
+
+ for (iService = iNumServices - 1; iService >= 0; iService--)
+ if (VALID(iService) &&
+ strwicmp(ServicePtrs[iService]->szService, pszServiceName) == 0) {
+ if (pserviceDest != NULL)
+ copy_service(pserviceDest, ServicePtrs[iService], NULL);
+ break;
+ }
+
+ return (iService);
+}
+
+/***************************************************************************
+ Copy a service structure to another.
+ If pcopymapDest is NULL then copy all fields
+***************************************************************************/
+
+static void copy_service(service * pserviceDest, service * pserviceSource, BOOL *pcopymapDest)
+{
+ int i;
+ BOOL bcopyall = (pcopymapDest == NULL);
+ param_opt_struct *data, *pdata, *paramo;
+ BOOL not_added;
+
+ for (i = 0; parm_table[i].label; i++)
+ if (parm_table[i].ptr && parm_table[i].class == P_LOCAL &&
+ (bcopyall || pcopymapDest[i])) {
+ void *def_ptr = parm_table[i].ptr;
+ void *src_ptr =
+ ((char *)pserviceSource) + PTR_DIFF(def_ptr,
+ &sDefault);
+ void *dest_ptr =
+ ((char *)pserviceDest) + PTR_DIFF(def_ptr,
+ &sDefault);
+
+ switch (parm_table[i].type) {
+ case P_BOOL:
+ case P_BOOLREV:
+ *(BOOL *)dest_ptr = *(BOOL *)src_ptr;
+ break;
+
+ case P_INTEGER:
+ case P_ENUM:
+ case P_OCTAL:
+ *(int *)dest_ptr = *(int *)src_ptr;
+ break;
+
+ case P_CHAR:
+ *(char *)dest_ptr = *(char *)src_ptr;
+ break;
+
+ case P_STRING:
+ string_set(dest_ptr,
+ *(char **)src_ptr);
+ break;
+
+ case P_USTRING:
+ string_set(dest_ptr,
+ *(char **)src_ptr);
+ strupper_m(*(char **)dest_ptr);
+ break;
+ case P_LIST:
+ str_list_copy((char ***)dest_ptr, *(const char ***)src_ptr);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (bcopyall) {
+ init_copymap(pserviceDest);
+ if (pserviceSource->copymap)
+ memcpy((void *)pserviceDest->copymap,
+ (void *)pserviceSource->copymap,
+ sizeof(BOOL) * NUMPARAMETERS);
+ }
+
+ data = pserviceSource->param_opt;
+ while (data) {
+ not_added = True;
+ pdata = pserviceDest->param_opt;
+ /* Traverse destination */
+ while (pdata) {
+ /* If we already have same option, override it */
+ if (strcmp(pdata->key, data->key) == 0) {
+ string_free(&pdata->value);
+ str_list_free(&data->list);
+ pdata->value = strdup(data->value);
+ not_added = False;
+ break;
+ }
+ pdata = pdata->next;
+ }
+ if (not_added) {
+ paramo = smb_xmalloc(sizeof(param_opt_struct));
+ paramo->key = strdup(data->key);
+ paramo->value = strdup(data->value);
+ paramo->list = NULL;
+ DLIST_ADD(pserviceDest->param_opt, paramo);
+ }
+ data = data->next;
+ }
+}
+
+/***************************************************************************
+Check a service for consistency. Return False if the service is in any way
+incomplete or faulty, else True.
+***************************************************************************/
+
+static BOOL service_ok(int iService)
+{
+ BOOL bRetval;
+
+ bRetval = True;
+ if (ServicePtrs[iService]->szService[0] == '\0') {
+ DEBUG(0, ("The following message indicates an internal error:\n"));
+ DEBUG(0, ("No service name in service entry.\n"));
+ bRetval = False;
+ }
+
+ /* The [printers] entry MUST be printable. I'm all for flexibility, but */
+ /* I can't see why you'd want a non-printable printer service... */
+ if (strwicmp(ServicePtrs[iService]->szService, PRINTERS_NAME) == 0) {
+ if (!ServicePtrs[iService]->bPrint_ok) {
+ DEBUG(0, ("WARNING: [%s] service MUST be printable!\n",
+ ServicePtrs[iService]->szService));
+ ServicePtrs[iService]->bPrint_ok = True;
+ }
+ /* [printers] service must also be non-browsable. */
+ if (ServicePtrs[iService]->bBrowseable)
+ ServicePtrs[iService]->bBrowseable = False;
+ }
+
+ if (ServicePtrs[iService]->szPath[0] == '\0' &&
+ strwicmp(ServicePtrs[iService]->szService, HOMES_NAME) != 0) {
+ DEBUG(0, ("No path in service %s - using %s\n",
+ ServicePtrs[iService]->szService, tmpdir()));
+ string_set(&ServicePtrs[iService]->szPath, tmpdir());
+ }
+
+ /* If a service is flagged unavailable, log the fact at level 0. */
+ if (!ServicePtrs[iService]->bAvailable)
+ DEBUG(1, ("NOTE: Service %s is flagged unavailable.\n",
+ ServicePtrs[iService]->szService));
+
+ return (bRetval);
+}
+
+static struct file_lists {
+ struct file_lists *next;
+ char *name;
+ char *subfname;
+ time_t modtime;
+} *file_lists = NULL;
+
+/*******************************************************************
+ Keep a linked list of all config files so we know when one has changed
+ it's date and needs to be reloaded.
+********************************************************************/
+
+static void add_to_file_list(const char *fname, const char *subfname)
+{
+ struct file_lists *f = file_lists;
+
+ while (f) {
+ if (f->name && !strcmp(f->name, fname))
+ break;
+ f = f->next;
+ }
+
+ if (!f) {
+ f = (struct file_lists *)malloc(sizeof(file_lists[0]));
+ if (!f)
+ return;
+ f->next = file_lists;
+ f->name = strdup(fname);
+ if (!f->name) {
+ SAFE_FREE(f);
+ return;
+ }
+ f->subfname = strdup(subfname);
+ if (!f->subfname) {
+ SAFE_FREE(f);
+ return;
+ }
+ file_lists = f;
+ f->modtime = file_modtime(subfname);
+ } else {
+ time_t t = file_modtime(subfname);
+ if (t)
+ f->modtime = t;
+ }
+}
+
+/*******************************************************************
+ Check if a config file has changed date.
+********************************************************************/
+
+BOOL lp_file_list_changed(void)
+{
+ struct file_lists *f = file_lists;
+ char *username;
+
+ DEBUG(6, ("lp_file_list_changed()\n"));
+
+ /* get the username for substituion -- preference to the current_user_info */
+ if ( strlen( current_user_info.smb_name ) != 0 )
+ username = current_user_info.smb_name;
+ else
+ username = sub_get_smb_name();
+
+
+ while (f) {
+ pstring n2;
+ time_t mod_time;
+
+ pstrcpy(n2, f->name);
+ standard_sub_basic(current_user_info.smb_name, n2,sizeof(n2));
+
+ DEBUGADD(6, ("file %s -> %s last mod_time: %s\n",
+ f->name, n2, ctime(&f->modtime)));
+
+ mod_time = file_modtime(n2);
+
+ if (mod_time && ((f->modtime != mod_time) || (f->subfname == NULL) || (strcmp(n2, f->subfname) != 0))) {
+ DEBUGADD(6,
+ ("file %s modified: %s\n", n2,
+ ctime(&mod_time)));
+ f->modtime = mod_time;
+ SAFE_FREE(f->subfname);
+ f->subfname = strdup(n2);
+ return (True);
+ }
+ f = f->next;
+ }
+ return (False);
+}
+
+/***************************************************************************
+ Run standard_sub_basic on netbios name... needed because global_myname
+ is not accessed through any lp_ macro.
+ Note: We must *NOT* use string_set() here as ptr points to global_myname.
+***************************************************************************/
+
+static BOOL handle_netbios_name(const char *pszParmValue, char **ptr)
+{
+ BOOL ret;
+ pstring netbios_name;
+
+ pstrcpy(netbios_name, pszParmValue);
+
+ standard_sub_basic(current_user_info.smb_name, netbios_name,sizeof(netbios_name));
+
+ ret = set_global_myname(netbios_name);
+ string_set(&Globals.szNetbiosName,global_myname());
+
+ DEBUG(4, ("handle_netbios_name: set global_myname to: %s\n",
+ global_myname()));
+
+ return ret;
+}
+
+static BOOL handle_charset(const char *pszParmValue, char **ptr)
+{
+ if (strcmp(*ptr, pszParmValue) != 0) {
+ string_set(ptr, pszParmValue);
+ init_iconv();
+ }
+ return True;
+}
+
+static BOOL handle_workgroup(const char *pszParmValue, char **ptr)
+{
+ BOOL ret;
+
+ ret = set_global_myworkgroup(pszParmValue);
+ string_set(&Globals.szWorkgroup,lp_workgroup());
+
+ return ret;
+}
+
+static BOOL handle_netbios_scope(const char *pszParmValue, char **ptr)
+{
+ BOOL ret;
+
+ ret = set_global_scope(pszParmValue);
+ string_set(&Globals.szNetbiosScope,global_scope());
+
+ return ret;
+}
+
+static BOOL handle_netbios_aliases(const char *pszParmValue, char **ptr)
+{
+ Globals.szNetbiosAliases = str_list_make(pszParmValue, NULL);
+ return set_netbios_aliases((const char **)Globals.szNetbiosAliases);
+}
+
+/***************************************************************************
+ Handle the include operation.
+***************************************************************************/
+
+static BOOL handle_include(const char *pszParmValue, char **ptr)
+{
+ pstring fname;
+ pstrcpy(fname, pszParmValue);
+
+ standard_sub_basic(current_user_info.smb_name, fname,sizeof(fname));
+
+ add_to_file_list(pszParmValue, fname);
+
+ string_set(ptr, fname);
+
+ if (file_exist(fname, NULL))
+ return (pm_process(fname, do_section, do_parameter));
+
+ DEBUG(2, ("Can't find include file %s\n", fname));
+
+ return (False);
+}
+
+/***************************************************************************
+ Handle the interpretation of the copy parameter.
+***************************************************************************/
+
+static BOOL handle_copy(const char *pszParmValue, char **ptr)
+{
+ BOOL bRetval;
+ int iTemp;
+ service serviceTemp;
+
+ string_set(ptr, pszParmValue);
+
+ init_service(&serviceTemp);
+
+ bRetval = False;
+
+ DEBUG(3, ("Copying service from service %s\n", pszParmValue));
+
+ if ((iTemp = getservicebyname(pszParmValue, &serviceTemp)) >= 0) {
+ if (iTemp == iServiceIndex) {
+ DEBUG(0, ("Can't copy service %s - unable to copy self!\n", pszParmValue));
+ } else {
+ copy_service(ServicePtrs[iServiceIndex],
+ &serviceTemp,
+ ServicePtrs[iServiceIndex]->copymap);
+ bRetval = True;
+ }
+ } else {
+ DEBUG(0, ("Unable to copy service - source not found: %s\n", pszParmValue));
+ bRetval = False;
+ }
+
+ free_service(&serviceTemp);
+ return (bRetval);
+}
+
+/***************************************************************************
+ Handle idmap/non unix account uid and gid allocation parameters. The format of these
+ parameters is:
+
+ [global]
+
+ idmap uid = 1000-1999
+ idmap gid = 700-899
+
+ We only do simple parsing checks here. The strings are parsed into useful
+ structures in the idmap daemon code.
+
+***************************************************************************/
+
+/* Some lp_ routines to return idmap [ug]id information */
+
+static uid_t idmap_uid_low, idmap_uid_high;
+static gid_t idmap_gid_low, idmap_gid_high;
+
+BOOL lp_idmap_uid(uid_t *low, uid_t *high)
+{
+ if (idmap_uid_low == 0 || idmap_uid_high == 0)
+ return False;
+
+ if (low)
+ *low = idmap_uid_low;
+
+ if (high)
+ *high = idmap_uid_high;
+
+ return True;
+}
+
+BOOL lp_idmap_gid(gid_t *low, gid_t *high)
+{
+ if (idmap_gid_low == 0 || idmap_gid_high == 0)
+ return False;
+
+ if (low)
+ *low = idmap_gid_low;
+
+ if (high)
+ *high = idmap_gid_high;
+
+ return True;
+}
+
+/* Do some simple checks on "idmap [ug]id" parameter values */
+
+static BOOL handle_idmap_uid(const char *pszParmValue, char **ptr)
+{
+ uint32 low, high;
+
+ if (sscanf(pszParmValue, "%u-%u", &low, &high) != 2 || high < low)
+ return False;
+
+ /* Parse OK */
+
+ string_set(ptr, pszParmValue);
+
+ idmap_uid_low = low;
+ idmap_uid_high = high;
+
+ return True;
+}
+
+static BOOL handle_idmap_gid(const char *pszParmValue, char **ptr)
+{
+ uint32 low, high;
+
+ if (sscanf(pszParmValue, "%u-%u", &low, &high) != 2 || high < low)
+ return False;
+
+ /* Parse OK */
+
+ string_set(ptr, pszParmValue);
+
+ idmap_gid_low = low;
+ idmap_gid_high = high;
+
+ return True;
+}
+
+/***************************************************************************
+ Handle the DEBUG level list.
+***************************************************************************/
+
+static BOOL handle_debug_list( const char *pszParmValueIn, char **ptr )
+{
+ pstring pszParmValue;
+
+ pstrcpy(pszParmValue, pszParmValueIn);
+ string_set(ptr, pszParmValueIn);
+ return debug_parse_levels( pszParmValue );
+}
+
+/***************************************************************************
+ Handle ldap suffixes - default to ldapsuffix if sub-suffixes are not defined.
+***************************************************************************/
+
+static char* append_ldap_suffix( const char *str )
+{
+ char *suffix_string;
+
+
+ if (!lp_talloc)
+ lp_talloc = talloc_init("lp_talloc");
+
+ suffix_string = talloc_asprintf( lp_talloc, "%s,%s", str, Globals.szLdapSuffix );
+ if ( !suffix_string ) {
+ DEBUG(0,("append_ldap_suffix: talloc_asprintf() failed!\n"));
+ return NULL;
+ }
+
+ return suffix_string;
+}
+
+char *lp_ldap_machine_suffix(void)
+{
+ if (Globals.szLdapMachineSuffix[0])
+ return append_ldap_suffix(Globals.szLdapMachineSuffix);
+
+ return lp_string(Globals.szLdapSuffix);
+}
+
+char *lp_ldap_user_suffix(void)
+{
+ if (Globals.szLdapUserSuffix[0])
+ return append_ldap_suffix(Globals.szLdapUserSuffix);
+
+ return lp_string(Globals.szLdapSuffix);
+}
+
+char *lp_ldap_group_suffix(void)
+{
+ if (Globals.szLdapGroupSuffix[0])
+ return append_ldap_suffix(Globals.szLdapGroupSuffix);
+
+ return lp_string(Globals.szLdapSuffix);
+}
+
+char *lp_ldap_idmap_suffix(void)
+{
+ if (Globals.szLdapIdmapSuffix[0])
+ return append_ldap_suffix(Globals.szLdapIdmapSuffix);
+
+ return lp_string(Globals.szLdapSuffix);
+}
+
+char *lp_ldap_privilege_suffix(void)
+{
+ if (Globals.szLdapPrivilegeSuffix[0])
+ return append_ldap_suffix(Globals.szLdapPrivilegeSuffix);
+
+ return lp_string(Globals.szLdapSuffix);
+}
+
+/***************************************************************************
+***************************************************************************/
+
+static BOOL handle_acl_compatibility(const char *pszParmValue, char **ptr)
+{
+ if (strequal(pszParmValue, "auto"))
+ string_set(ptr, "");
+ else if (strequal(pszParmValue, "winnt"))
+ string_set(ptr, "winnt");
+ else if (strequal(pszParmValue, "win2k"))
+ string_set(ptr, "win2k");
+ else
+ return False;
+
+ return True;
+}
+
+/***************************************************************************
+ Initialise a copymap.
+***************************************************************************/
+
+static void init_copymap(service * pservice)
+{
+ int i;
+ SAFE_FREE(pservice->copymap);
+ pservice->copymap = (BOOL *)malloc(sizeof(BOOL) * NUMPARAMETERS);
+ if (!pservice->copymap)
+ DEBUG(0,
+ ("Couldn't allocate copymap!! (size %d)\n",
+ (int)NUMPARAMETERS));
+ else
+ for (i = 0; i < NUMPARAMETERS; i++)
+ pservice->copymap[i] = True;
+}
+
+/***************************************************************************
+ Return the local pointer to a parameter given the service number and the
+ pointer into the default structure.
+***************************************************************************/
+
+void *lp_local_ptr(int snum, void *ptr)
+{
+ return (void *)(((char *)ServicePtrs[snum]) + PTR_DIFF(ptr, &sDefault));
+}
+
+/***************************************************************************
+ Process a parameter for a particular service number. If snum < 0
+ then assume we are in the globals.
+***************************************************************************/
+
+BOOL lp_do_parameter(int snum, const char *pszParmName, const char *pszParmValue)
+{
+ int parmnum, i, slen;
+ void *parm_ptr = NULL; /* where we are going to store the result */
+ void *def_ptr = NULL;
+ pstring param_key;
+ char *sep;
+ param_opt_struct *paramo, *data;
+ BOOL not_added;
+
+ parmnum = map_parameter(pszParmName);
+
+ if (parmnum < 0) {
+ if ((sep=strchr(pszParmName, ':')) != NULL) {
+ *sep = '\0';
+ ZERO_STRUCT(param_key);
+ pstr_sprintf(param_key, "%s:", pszParmName);
+ slen = strlen(param_key);
+ pstrcat(param_key, sep+1);
+ trim_char(param_key+slen, ' ', ' ');
+ not_added = True;
+ data = (snum < 0) ? Globals.param_opt :
+ ServicePtrs[snum]->param_opt;
+ /* Traverse destination */
+ while (data) {
+ /* If we already have same option, override it */
+ if (strcmp(data->key, param_key) == 0) {
+ string_free(&data->value);
+ str_list_free(&data->list);
+ data->value = strdup(pszParmValue);
+ not_added = False;
+ break;
+ }
+ data = data->next;
+ }
+ if (not_added) {
+ paramo = smb_xmalloc(sizeof(param_opt_struct));
+ paramo->key = strdup(param_key);
+ paramo->value = strdup(pszParmValue);
+ paramo->list = NULL;
+ if (snum < 0) {
+ DLIST_ADD(Globals.param_opt, paramo);
+ } else {
+ DLIST_ADD(ServicePtrs[snum]->param_opt, paramo);
+ }
+ }
+
+ *sep = ':';
+ return (True);
+ }
+ DEBUG(0, ("Ignoring unknown parameter \"%s\"\n", pszParmName));
+ return (True);
+ }
+
+ if (parm_table[parmnum].flags & FLAG_DEPRECATED) {
+ DEBUG(1, ("WARNING: The \"%s\" option is deprecated\n",
+ pszParmName));
+ }
+
+ def_ptr = parm_table[parmnum].ptr;
+
+ /* we might point at a service, the default service or a global */
+ if (snum < 0) {
+ parm_ptr = def_ptr;
+ } else {
+ if (parm_table[parmnum].class == P_GLOBAL) {
+ DEBUG(0,
+ ("Global parameter %s found in service section!\n",
+ pszParmName));
+ return (True);
+ }
+ parm_ptr =
+ ((char *)ServicePtrs[snum]) + PTR_DIFF(def_ptr,
+ &sDefault);
+ }
+
+ if (snum >= 0) {
+ if (!ServicePtrs[snum]->copymap)
+ init_copymap(ServicePtrs[snum]);
+
+ /* this handles the aliases - set the copymap for other entries with
+ the same data pointer */
+ for (i = 0; parm_table[i].label; i++)
+ if (parm_table[i].ptr == parm_table[parmnum].ptr)
+ ServicePtrs[snum]->copymap[i] = False;
+ }
+
+ /* if it is a special case then go ahead */
+ if (parm_table[parmnum].special) {
+ parm_table[parmnum].special(pszParmValue, (char **)parm_ptr);
+ return (True);
+ }
+
+ /* now switch on the type of variable it is */
+ switch (parm_table[parmnum].type)
+ {
+ case P_BOOL:
+ set_boolean(parm_ptr, pszParmValue);
+ break;
+
+ case P_BOOLREV:
+ set_boolean(parm_ptr, pszParmValue);
+ *(BOOL *)parm_ptr = !*(BOOL *)parm_ptr;
+ break;
+
+ case P_INTEGER:
+ *(int *)parm_ptr = atoi(pszParmValue);
+ break;
+
+ case P_CHAR:
+ *(char *)parm_ptr = *pszParmValue;
+ break;
+
+ case P_OCTAL:
+ sscanf(pszParmValue, "%o", (int *)parm_ptr);
+ break;
+
+ case P_LIST:
+ str_list_free(parm_ptr);
+ *(char ***)parm_ptr = str_list_make(pszParmValue, NULL);
+ break;
+
+ case P_STRING:
+ string_set(parm_ptr, pszParmValue);
+ break;
+
+ case P_USTRING:
+ string_set(parm_ptr, pszParmValue);
+ strupper_m(*(char **)parm_ptr);
+ break;
+
+ case P_GSTRING:
+ pstrcpy((char *)parm_ptr, pszParmValue);
+ break;
+
+ case P_UGSTRING:
+ pstrcpy((char *)parm_ptr, pszParmValue);
+ strupper_m((char *)parm_ptr);
+ break;
+
+ case P_ENUM:
+ for (i = 0; parm_table[parmnum].enum_list[i].name; i++) {
+ if (strequal
+ (pszParmValue,
+ parm_table[parmnum].enum_list[i].name)) {
+ *(int *)parm_ptr =
+ parm_table[parmnum].
+ enum_list[i].value;
+ break;
+ }
+ }
+ break;
+ case P_SEP:
+ break;
+ }
+
+ return (True);
+}
+
+/***************************************************************************
+ Process a parameter.
+***************************************************************************/
+
+static BOOL do_parameter(const char *pszParmName, const char *pszParmValue)
+{
+ if (!bInGlobalSection && bGlobalOnly)
+ return (True);
+
+ DEBUGADD(4, ("doing parameter %s = %s\n", pszParmName, pszParmValue));
+
+ return (lp_do_parameter(bInGlobalSection ? -2 : iServiceIndex,
+ pszParmName, pszParmValue));
+}
+
+/***************************************************************************
+ Print a parameter of the specified type.
+***************************************************************************/
+
+static void print_parameter(struct parm_struct *p, void *ptr, FILE * f)
+{
+ int i;
+ switch (p->type)
+ {
+ case P_ENUM:
+ for (i = 0; p->enum_list[i].name; i++) {
+ if (*(int *)ptr == p->enum_list[i].value) {
+ fprintf(f, "%s",
+ p->enum_list[i].name);
+ break;
+ }
+ }
+ break;
+
+ case P_BOOL:
+ fprintf(f, "%s", BOOLSTR(*(BOOL *)ptr));
+ break;
+
+ case P_BOOLREV:
+ fprintf(f, "%s", BOOLSTR(!*(BOOL *)ptr));
+ break;
+
+ case P_INTEGER:
+ fprintf(f, "%d", *(int *)ptr);
+ break;
+
+ case P_CHAR:
+ fprintf(f, "%c", *(char *)ptr);
+ break;
+
+ case P_OCTAL:
+ fprintf(f, "%s", octal_string(*(int *)ptr));
+ break;
+
+ case P_LIST:
+ if ((char ***)ptr && *(char ***)ptr) {
+ char **list = *(char ***)ptr;
+
+ for (; *list; list++) {
+ /* surround strings with whitespace in single quotes */
+ if ( strchr_m( *list, ' ' ) )
+ fprintf(f, "\'%s\'%s", *list, ((*(list+1))?", ":""));
+ else
+ fprintf(f, "%s%s", *list, ((*(list+1))?", ":""));
+ }
+ }
+ break;
+
+ case P_GSTRING:
+ case P_UGSTRING:
+ if ((char *)ptr) {
+ fprintf(f, "%s", (char *)ptr);
+ }
+ break;
+
+ case P_STRING:
+ case P_USTRING:
+ if (*(char **)ptr) {
+ fprintf(f, "%s", *(char **)ptr);
+ }
+ break;
+ case P_SEP:
+ break;
+ }
+}
+
+/***************************************************************************
+ Check if two parameters are equal.
+***************************************************************************/
+
+static BOOL equal_parameter(parm_type type, void *ptr1, void *ptr2)
+{
+ switch (type) {
+ case P_BOOL:
+ case P_BOOLREV:
+ return (*((BOOL *)ptr1) == *((BOOL *)ptr2));
+
+ case P_INTEGER:
+ case P_ENUM:
+ case P_OCTAL:
+ return (*((int *)ptr1) == *((int *)ptr2));
+
+ case P_CHAR:
+ return (*((char *)ptr1) == *((char *)ptr2));
+
+ case P_LIST:
+ return str_list_compare(*(char ***)ptr1, *(char ***)ptr2);
+
+ case P_GSTRING:
+ case P_UGSTRING:
+ {
+ char *p1 = (char *)ptr1, *p2 = (char *)ptr2;
+ if (p1 && !*p1)
+ p1 = NULL;
+ if (p2 && !*p2)
+ p2 = NULL;
+ return (p1 == p2 || strequal(p1, p2));
+ }
+ case P_STRING:
+ case P_USTRING:
+ {
+ char *p1 = *(char **)ptr1, *p2 = *(char **)ptr2;
+ if (p1 && !*p1)
+ p1 = NULL;
+ if (p2 && !*p2)
+ p2 = NULL;
+ return (p1 == p2 || strequal(p1, p2));
+ }
+ case P_SEP:
+ break;
+ }
+ return (False);
+}
+
+/***************************************************************************
+ Initialize any local varients in the sDefault table.
+***************************************************************************/
+
+void init_locals(void)
+{
+ /* None as yet. */
+}
+
+/***************************************************************************
+ Process a new section (service). At this stage all sections are services.
+ Later we'll have special sections that permit server parameters to be set.
+ Returns True on success, False on failure.
+***************************************************************************/
+
+static BOOL do_section(const char *pszSectionName)
+{
+ BOOL bRetval;
+ BOOL isglobal = ((strwicmp(pszSectionName, GLOBAL_NAME) == 0) ||
+ (strwicmp(pszSectionName, GLOBAL_NAME2) == 0));
+ bRetval = False;
+
+ /* if we were in a global section then do the local inits */
+ if (bInGlobalSection && !isglobal)
+ init_locals();
+
+ /* if we've just struck a global section, note the fact. */
+ bInGlobalSection = isglobal;
+
+ /* check for multiple global sections */
+ if (bInGlobalSection) {
+ DEBUG(3, ("Processing section \"[%s]\"\n", pszSectionName));
+ return (True);
+ }
+
+ if (!bInGlobalSection && bGlobalOnly)
+ return (True);
+
+ /* if we have a current service, tidy it up before moving on */
+ bRetval = True;
+
+ if (iServiceIndex >= 0)
+ bRetval = service_ok(iServiceIndex);
+
+ /* if all is still well, move to the next record in the services array */
+ if (bRetval) {
+ /* We put this here to avoid an odd message order if messages are */
+ /* issued by the post-processing of a previous section. */
+ DEBUG(2, ("Processing section \"[%s]\"\n", pszSectionName));
+
+ if ((iServiceIndex = add_a_service(&sDefault, pszSectionName))
+ < 0) {
+ DEBUG(0, ("Failed to add a new service\n"));
+ return (False);
+ }
+ }
+
+ return (bRetval);
+}
+
+
+/***************************************************************************
+ Determine if a partcular base parameter is currentl set to the default value.
+***************************************************************************/
+
+static BOOL is_default(int i)
+{
+ if (!defaults_saved)
+ return False;
+ switch (parm_table[i].type) {
+ case P_LIST:
+ return str_list_compare (parm_table[i].def.lvalue,
+ *(char ***)parm_table[i].ptr);
+ case P_STRING:
+ case P_USTRING:
+ return strequal(parm_table[i].def.svalue,
+ *(char **)parm_table[i].ptr);
+ case P_GSTRING:
+ case P_UGSTRING:
+ return strequal(parm_table[i].def.svalue,
+ (char *)parm_table[i].ptr);
+ case P_BOOL:
+ case P_BOOLREV:
+ return parm_table[i].def.bvalue ==
+ *(BOOL *)parm_table[i].ptr;
+ case P_CHAR:
+ return parm_table[i].def.cvalue ==
+ *(char *)parm_table[i].ptr;
+ case P_INTEGER:
+ case P_OCTAL:
+ case P_ENUM:
+ return parm_table[i].def.ivalue ==
+ *(int *)parm_table[i].ptr;
+ case P_SEP:
+ break;
+ }
+ return False;
+}
+
+/***************************************************************************
+Display the contents of the global structure.
+***************************************************************************/
+
+static void dump_globals(FILE *f)
+{
+ int i;
+ param_opt_struct *data;
+
+ fprintf(f, "# Global parameters\n[global]\n");
+
+ for (i = 0; parm_table[i].label; i++)
+ if (parm_table[i].class == P_GLOBAL &&
+ parm_table[i].ptr &&
+ (i == 0 || (parm_table[i].ptr != parm_table[i - 1].ptr))) {
+ if (defaults_saved && is_default(i))
+ continue;
+ fprintf(f, "\t%s = ", parm_table[i].label);
+ print_parameter(&parm_table[i], parm_table[i].ptr, f);
+ fprintf(f, "\n");
+ }
+ if (Globals.param_opt != NULL) {
+ data = Globals.param_opt;
+ while(data) {
+ fprintf(f, "\t%s = %s\n", data->key, data->value);
+ data = data->next;
+ }
+ }
+
+}
+
+/***************************************************************************
+ Return True if a local parameter is currently set to the global default.
+***************************************************************************/
+
+BOOL lp_is_default(int snum, struct parm_struct *parm)
+{
+ int pdiff = PTR_DIFF(parm->ptr, &sDefault);
+
+ return equal_parameter(parm->type,
+ ((char *)ServicePtrs[snum]) + pdiff,
+ ((char *)&sDefault) + pdiff);
+}
+
+/***************************************************************************
+ Display the contents of a single services record.
+***************************************************************************/
+
+static void dump_a_service(service * pService, FILE * f)
+{
+ int i;
+ param_opt_struct *data;
+
+ if (pService != &sDefault)
+ fprintf(f, "\n[%s]\n", pService->szService);
+
+ for (i = 0; parm_table[i].label; i++)
+ if (parm_table[i].class == P_LOCAL &&
+ parm_table[i].ptr &&
+ (*parm_table[i].label != '-') &&
+ (i == 0 || (parm_table[i].ptr != parm_table[i - 1].ptr))) {
+ int pdiff = PTR_DIFF(parm_table[i].ptr, &sDefault);
+
+ if (pService == &sDefault) {
+ if (defaults_saved && is_default(i))
+ continue;
+ } else {
+ if (equal_parameter(parm_table[i].type,
+ ((char *)pService) +
+ pdiff,
+ ((char *)&sDefault) +
+ pdiff))
+ continue;
+ }
+
+ fprintf(f, "\t%s = ", parm_table[i].label);
+ print_parameter(&parm_table[i],
+ ((char *)pService) + pdiff, f);
+ fprintf(f, "\n");
+ }
+ if (pService->param_opt != NULL) {
+ data = pService->param_opt;
+ while(data) {
+ fprintf(f, "\t%s = %s\n", data->key, data->value);
+ data = data->next;
+ }
+ }
+}
+
+
+/***************************************************************************
+ Return info about the next service in a service. snum==GLOBAL_SECTION_SNUM gives the globals.
+ Return NULL when out of parameters.
+***************************************************************************/
+
+struct parm_struct *lp_next_parameter(int snum, int *i, int allparameters)
+{
+ if (snum < 0) {
+ /* do the globals */
+ for (; parm_table[*i].label; (*i)++) {
+ if (parm_table[*i].class == P_SEPARATOR)
+ return &parm_table[(*i)++];
+
+ if (!parm_table[*i].ptr
+ || (*parm_table[*i].label == '-'))
+ continue;
+
+ if ((*i) > 0
+ && (parm_table[*i].ptr ==
+ parm_table[(*i) - 1].ptr))
+ continue;
+
+ return &parm_table[(*i)++];
+ }
+ } else {
+ service *pService = ServicePtrs[snum];
+
+ for (; parm_table[*i].label; (*i)++) {
+ if (parm_table[*i].class == P_SEPARATOR)
+ return &parm_table[(*i)++];
+
+ if (parm_table[*i].class == P_LOCAL &&
+ parm_table[*i].ptr &&
+ (*parm_table[*i].label != '-') &&
+ ((*i) == 0 ||
+ (parm_table[*i].ptr !=
+ parm_table[(*i) - 1].ptr)))
+ {
+ int pdiff =
+ PTR_DIFF(parm_table[*i].ptr,
+ &sDefault);
+
+ if (allparameters ||
+ !equal_parameter(parm_table[*i].type,
+ ((char *)pService) +
+ pdiff,
+ ((char *)&sDefault) +
+ pdiff))
+ {
+ return &parm_table[(*i)++];
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+
+#if 0
+/***************************************************************************
+ Display the contents of a single copy structure.
+***************************************************************************/
+static void dump_copy_map(BOOL *pcopymap)
+{
+ int i;
+ if (!pcopymap)
+ return;
+
+ printf("\n\tNon-Copied parameters:\n");
+
+ for (i = 0; parm_table[i].label; i++)
+ if (parm_table[i].class == P_LOCAL &&
+ parm_table[i].ptr && !pcopymap[i] &&
+ (i == 0 || (parm_table[i].ptr != parm_table[i - 1].ptr)))
+ {
+ printf("\t\t%s\n", parm_table[i].label);
+ }
+}
+#endif
+
+/***************************************************************************
+ Return TRUE if the passed service number is within range.
+***************************************************************************/
+
+BOOL lp_snum_ok(int iService)
+{
+ return (LP_SNUM_OK(iService) && ServicePtrs[iService]->bAvailable);
+}
+
+/***************************************************************************
+ Auto-load some home services.
+***************************************************************************/
+
+static void lp_add_auto_services(char *str)
+{
+ char *s;
+ char *p;
+ int homes;
+
+ if (!str)
+ return;
+
+ s = strdup(str);
+ if (!s)
+ return;
+
+ homes = lp_servicenumber(HOMES_NAME);
+
+ for (p = strtok(s, LIST_SEP); p; p = strtok(NULL, LIST_SEP)) {
+ char *home = get_user_home_dir(p);
+
+ if (lp_servicenumber(p) >= 0)
+ continue;
+
+ if (home && homes >= 0)
+ lp_add_home(p, homes, p, home);
+ }
+ SAFE_FREE(s);
+}
+
+/***************************************************************************
+ Auto-load one printer.
+***************************************************************************/
+
+void lp_add_one_printer(char *name, char *comment)
+{
+ int printers = lp_servicenumber(PRINTERS_NAME);
+ int i;
+
+ if (lp_servicenumber(name) < 0) {
+ lp_add_printer(name, printers);
+ if ((i = lp_servicenumber(name)) >= 0) {
+ string_set(&ServicePtrs[i]->comment, comment);
+ ServicePtrs[i]->autoloaded = True;
+ }
+ }
+}
+
+/***************************************************************************
+ Have we loaded a services file yet?
+***************************************************************************/
+
+BOOL lp_loaded(void)
+{
+ return (bLoaded);
+}
+
+/***************************************************************************
+ Unload unused services.
+***************************************************************************/
+
+void lp_killunused(BOOL (*snumused) (int))
+{
+ int i;
+ for (i = 0; i < iNumServices; i++) {
+ if (!VALID(i))
+ continue;
+
+ if (!snumused || !snumused(i)) {
+ ServicePtrs[i]->valid = False;
+ free_service(ServicePtrs[i]);
+ }
+ }
+}
+
+/***************************************************************************
+ Unload a service.
+***************************************************************************/
+
+void lp_killservice(int iServiceIn)
+{
+ if (VALID(iServiceIn)) {
+ ServicePtrs[iServiceIn]->valid = False;
+ free_service(ServicePtrs[iServiceIn]);
+ }
+}
+
+/***************************************************************************
+ Save the curent values of all global and sDefault parameters into the
+ defaults union. This allows swat and testparm to show only the
+ changed (ie. non-default) parameters.
+***************************************************************************/
+
+static void lp_save_defaults(void)
+{
+ int i;
+ for (i = 0; parm_table[i].label; i++) {
+ if (i > 0 && parm_table[i].ptr == parm_table[i - 1].ptr)
+ continue;
+ switch (parm_table[i].type) {
+ case P_LIST:
+ str_list_copy(&(parm_table[i].def.lvalue),
+ *(const char ***)parm_table[i].ptr);
+ break;
+ case P_STRING:
+ case P_USTRING:
+ if (parm_table[i].ptr) {
+ parm_table[i].def.svalue = strdup(*(char **)parm_table[i].ptr);
+ } else {
+ parm_table[i].def.svalue = NULL;
+ }
+ break;
+ case P_GSTRING:
+ case P_UGSTRING:
+ if (parm_table[i].ptr) {
+ parm_table[i].def.svalue = strdup((char *)parm_table[i].ptr);
+ } else {
+ parm_table[i].def.svalue = NULL;
+ }
+ break;
+ case P_BOOL:
+ case P_BOOLREV:
+ parm_table[i].def.bvalue =
+ *(BOOL *)parm_table[i].ptr;
+ break;
+ case P_CHAR:
+ parm_table[i].def.cvalue =
+ *(char *)parm_table[i].ptr;
+ break;
+ case P_INTEGER:
+ case P_OCTAL:
+ case P_ENUM:
+ parm_table[i].def.ivalue =
+ *(int *)parm_table[i].ptr;
+ break;
+ case P_SEP:
+ break;
+ }
+ }
+ defaults_saved = True;
+}
+
+/*******************************************************************
+ Set the server type we will announce as via nmbd.
+********************************************************************/
+
+static void set_server_role(void)
+{
+ server_role = ROLE_STANDALONE;
+
+ switch (lp_security()) {
+ case SEC_SHARE:
+ if (lp_domain_logons())
+ DEBUG(0, ("Server's Role (logon server) conflicts with share-level security\n"));
+ break;
+ case SEC_SERVER:
+ if (lp_domain_logons())
+ DEBUG(0, ("Server's Role (logon server) conflicts with server-level security\n"));
+ server_role = ROLE_DOMAIN_MEMBER;
+ break;
+ case SEC_DOMAIN:
+ if (lp_domain_logons()) {
+ DEBUG(1, ("Server's Role (logon server) NOT ADVISED with domain-level security\n"));
+ server_role = ROLE_DOMAIN_BDC;
+ break;
+ }
+ server_role = ROLE_DOMAIN_MEMBER;
+ break;
+ case SEC_ADS:
+ if (lp_domain_logons()) {
+ server_role = ROLE_DOMAIN_PDC;
+ break;
+ }
+ server_role = ROLE_DOMAIN_MEMBER;
+ break;
+ case SEC_USER:
+ if (lp_domain_logons()) {
+
+ if (Globals.bDomainMaster) /* auto or yes */
+ server_role = ROLE_DOMAIN_PDC;
+ else
+ server_role = ROLE_DOMAIN_BDC;
+ }
+ break;
+ default:
+ DEBUG(0, ("Server's Role undefined due to unknown security mode\n"));
+ break;
+ }
+
+ DEBUG(10, ("set_server_role: role = "));
+
+ switch(server_role) {
+ case ROLE_STANDALONE:
+ DEBUGADD(10, ("ROLE_STANDALONE\n"));
+ break;
+ case ROLE_DOMAIN_MEMBER:
+ DEBUGADD(10, ("ROLE_DOMAIN_MEMBER\n"));
+ break;
+ case ROLE_DOMAIN_BDC:
+ DEBUGADD(10, ("ROLE_DOMAIN_BDC\n"));
+ break;
+ case ROLE_DOMAIN_PDC:
+ DEBUGADD(10, ("ROLE_DOMAIN_PDC\n"));
+ break;
+ }
+}
+
+/***********************************************************
+ If we should send plaintext/LANMAN passwords in the clinet
+************************************************************/
+static void set_allowed_client_auth(void)
+{
+ if (Globals.bClientNTLMv2Auth) {
+ Globals.bClientLanManAuth = False;
+ }
+ if (!Globals.bClientLanManAuth) {
+ Globals.bClientPlaintextAuth = False;
+ }
+}
+
+/***************************************************************************
+ Load the services array from the services file. Return True on success,
+ False on failure.
+***************************************************************************/
+
+BOOL lp_load(const char *pszFname, BOOL global_only, BOOL save_defaults,
+ BOOL add_ipc)
+{
+ pstring n2;
+ BOOL bRetval;
+ param_opt_struct *data, *pdata;
+ char *username;
+
+ pstrcpy(n2, pszFname);
+
+ /* get the username for substituion -- preference to the current_user_info */
+
+ if ( strlen( current_user_info.smb_name ) != 0 )
+ username = current_user_info.smb_name;
+ else
+ username = sub_get_smb_name();
+
+ standard_sub_basic( username, n2,sizeof(n2) );
+
+ add_to_file_list(pszFname, n2);
+
+ bRetval = False;
+
+ DEBUG(3, ("lp_load: refreshing parameters\n"));
+
+ bInGlobalSection = True;
+ bGlobalOnly = global_only;
+
+ init_globals();
+ debug_init();
+
+ if (save_defaults)
+ {
+ init_locals();
+ lp_save_defaults();
+ }
+
+ if (Globals.param_opt != NULL) {
+ data = Globals.param_opt;
+ while (data) {
+ string_free(&data->key);
+ string_free(&data->value);
+ str_list_free(&data->list);
+ pdata = data->next;
+ SAFE_FREE(data);
+ data = pdata;
+ }
+ Globals.param_opt = NULL;
+ }
+
+ /* We get sections first, so have to start 'behind' to make up */
+ iServiceIndex = -1;
+ bRetval = pm_process(n2, do_section, do_parameter);
+
+ /* finish up the last section */
+ DEBUG(4, ("pm_process() returned %s\n", BOOLSTR(bRetval)));
+ if (bRetval)
+ if (iServiceIndex >= 0)
+ bRetval = service_ok(iServiceIndex);
+
+ if (*(lp_config_backend())) {
+ modconf_init(lp_config_backend());
+ modconf_load(do_section, do_parameter);
+ }
+
+ lp_add_auto_services(lp_auto_services());
+
+ if (add_ipc) {
+ /* When 'restrict anonymous = 2' guest connections to ipc$
+ are denied */
+ lp_add_ipc("IPC$", (lp_restrict_anonymous() < 2));
+ lp_add_ipc("ADMIN$", False);
+ }
+
+ set_server_role();
+ set_default_server_announce_type();
+ set_allowed_client_auth();
+
+ bLoaded = True;
+
+ /* Now we check bWINSsupport and set szWINSserver to 127.0.0.1 */
+ /* if bWINSsupport is true and we are in the client */
+ if (in_client && Globals.bWINSsupport) {
+ lp_do_parameter(GLOBAL_SECTION_SNUM, "wins server", "127.0.0.1");
+ }
+
+ init_iconv();
+ init_printer_values(&sDefault);
+
+ return (bRetval);
+}
+
+/***************************************************************************
+ Reset the max number of services.
+***************************************************************************/
+
+void lp_resetnumservices(void)
+{
+ iNumServices = 0;
+}
+
+/***************************************************************************
+ Return the max number of services.
+***************************************************************************/
+
+int lp_numservices(void)
+{
+ return (iNumServices);
+}
+
+/***************************************************************************
+Display the contents of the services array in human-readable form.
+***************************************************************************/
+
+void lp_dump(FILE *f, BOOL show_defaults, int maxtoprint)
+{
+ int iService;
+
+ if (show_defaults)
+ defaults_saved = False;
+
+ dump_globals(f);
+
+ dump_a_service(&sDefault, f);
+
+ for (iService = 0; iService < maxtoprint; iService++)
+ lp_dump_one(f, show_defaults, iService);
+}
+
+/***************************************************************************
+Display the contents of one service in human-readable form.
+***************************************************************************/
+
+void lp_dump_one(FILE * f, BOOL show_defaults, int snum)
+{
+ if (VALID(snum)) {
+ if (ServicePtrs[snum]->szService[0] == '\0')
+ return;
+ dump_a_service(ServicePtrs[snum], f);
+ }
+}
+
+/***************************************************************************
+Return the number of the service with the given name, or -1 if it doesn't
+exist. Note that this is a DIFFERENT ANIMAL from the internal function
+getservicebyname()! This works ONLY if all services have been loaded, and
+does not copy the found service.
+***************************************************************************/
+
+int lp_servicenumber(const char *pszServiceName)
+{
+ int iService;
+ fstring serviceName;
+
+ if (!pszServiceName)
+ return GLOBAL_SECTION_SNUM;
+
+ for (iService = iNumServices - 1; iService >= 0; iService--) {
+ if (VALID(iService) && ServicePtrs[iService]->szService) {
+ /*
+ * The substitution here is used to support %U is
+ * service names
+ */
+ fstrcpy(serviceName, ServicePtrs[iService]->szService);
+ standard_sub_basic(current_user_info.smb_name, serviceName,sizeof(serviceName));
+ if (strequal(serviceName, pszServiceName))
+ break;
+ }
+ }
+
+ if (iService < 0) {
+ DEBUG(7,("lp_servicenumber: couldn't find %s\n", pszServiceName));
+ return GLOBAL_SECTION_SNUM;
+ }
+
+ return (iService);
+}
+
+/*******************************************************************
+ A useful volume label function.
+********************************************************************/
+
+char *volume_label(int snum)
+{
+ char *ret = lp_volume(snum);
+ if (!*ret)
+ return lp_servicename(snum);
+ return (ret);
+}
+
+
+/*******************************************************************
+ Set the server type we will announce as via nmbd.
+********************************************************************/
+
+static void set_default_server_announce_type(void)
+{
+ default_server_announce = 0;
+ default_server_announce |= SV_TYPE_WORKSTATION;
+ default_server_announce |= SV_TYPE_SERVER;
+ default_server_announce |= SV_TYPE_SERVER_UNIX;
+
+ /* note that the flag should be set only if we have a
+ printer service but nmbd doesn't actually load the
+ services so we can't tell --jerry */
+
+ default_server_announce |= SV_TYPE_PRINTQ_SERVER;
+
+ switch (lp_announce_as()) {
+ case ANNOUNCE_AS_NT_SERVER:
+ default_server_announce |= SV_TYPE_SERVER_NT;
+ /* fall through... */
+ case ANNOUNCE_AS_NT_WORKSTATION:
+ default_server_announce |= SV_TYPE_NT;
+ break;
+ case ANNOUNCE_AS_WIN95:
+ default_server_announce |= SV_TYPE_WIN95_PLUS;
+ break;
+ case ANNOUNCE_AS_WFW:
+ default_server_announce |= SV_TYPE_WFW;
+ break;
+ default:
+ break;
+ }
+
+ switch (lp_server_role()) {
+ case ROLE_DOMAIN_MEMBER:
+ default_server_announce |= SV_TYPE_DOMAIN_MEMBER;
+ break;
+ case ROLE_DOMAIN_PDC:
+ default_server_announce |= SV_TYPE_DOMAIN_CTRL;
+ break;
+ case ROLE_DOMAIN_BDC:
+ default_server_announce |= SV_TYPE_DOMAIN_BAKCTRL;
+ break;
+ case ROLE_STANDALONE:
+ default:
+ break;
+ }
+ if (lp_time_server())
+ default_server_announce |= SV_TYPE_TIME_SOURCE;
+
+ if (lp_host_msdfs())
+ default_server_announce |= SV_TYPE_DFS_SERVER;
+}
+
+/***********************************************************
+ returns role of Samba server
+************************************************************/
+
+int lp_server_role(void)
+{
+ return server_role;
+}
+
+/***********************************************************
+ If we are PDC then prefer us as DMB
+************************************************************/
+
+BOOL lp_domain_master(void)
+{
+ if (Globals.bDomainMaster == Auto)
+ return (lp_server_role() == ROLE_DOMAIN_PDC);
+
+ return Globals.bDomainMaster;
+}
+
+/***********************************************************
+ If we are DMB then prefer us as LMB
+************************************************************/
+
+BOOL lp_preferred_master(void)
+{
+ if (Globals.bPreferredMaster == Auto)
+ return (lp_local_master() && lp_domain_master());
+
+ return Globals.bPreferredMaster;
+}
+
+/*******************************************************************
+ Remove a service.
+********************************************************************/
+
+void lp_remove_service(int snum)
+{
+ ServicePtrs[snum]->valid = False;
+}
+
+/*******************************************************************
+ Copy a service.
+********************************************************************/
+
+void lp_copy_service(int snum, const char *new_name)
+{
+ do_section(new_name);
+ if (snum >= 0) {
+ snum = lp_servicenumber(new_name);
+ if (snum >= 0)
+ lp_do_parameter(snum, "copy", lp_servicename(snum));
+ }
+}
+
+
+/*******************************************************************
+ Get the default server type we will announce as via nmbd.
+********************************************************************/
+
+int lp_default_server_announce(void)
+{
+ return default_server_announce;
+}
+
+/*******************************************************************
+ Split the announce version into major and minor numbers.
+********************************************************************/
+
+int lp_major_announce_version(void)
+{
+ static BOOL got_major = False;
+ static int major_version = DEFAULT_MAJOR_VERSION;
+ char *vers;
+ char *p;
+
+ if (got_major)
+ return major_version;
+
+ got_major = True;
+ if ((vers = lp_announce_version()) == NULL)
+ return major_version;
+
+ if ((p = strchr_m(vers, '.')) == 0)
+ return major_version;
+
+ *p = '\0';
+ major_version = atoi(vers);
+ return major_version;
+}
+
+int lp_minor_announce_version(void)
+{
+ static BOOL got_minor = False;
+ static int minor_version = DEFAULT_MINOR_VERSION;
+ char *vers;
+ char *p;
+
+ if (got_minor)
+ return minor_version;
+
+ got_minor = True;
+ if ((vers = lp_announce_version()) == NULL)
+ return minor_version;
+
+ if ((p = strchr_m(vers, '.')) == 0)
+ return minor_version;
+
+ p++;
+ minor_version = atoi(p);
+ return minor_version;
+}
+
+/***********************************************************
+ Set the global name resolution order (used in smbclient).
+************************************************************/
+
+void lp_set_name_resolve_order(const char *new_order)
+{
+ string_set(&Globals.szNameResolveOrder, new_order);
+}
+
+const char *lp_printername(int snum)
+{
+ const char *ret = _lp_printername(snum);
+ if (ret == NULL || (ret != NULL && *ret == '\0'))
+ ret = lp_const_servicename(snum);
+
+ return ret;
+}
+
+
+/****************************************************************
+ Compatibility fn. for 2.2.2 code.....
+*****************************************************************/
+
+void get_private_directory(pstring privdir)
+{
+ pstrcpy (privdir, lp_private_dir());
+}
+
+/***********************************************************
+ Allow daemons such as winbindd to fix their logfile name.
+************************************************************/
+
+void lp_set_logfile(const char *name)
+{
+ string_set(&Globals.szLogFile, name);
+ pstrcpy(debugf, name);
+}
+
+/*******************************************************************
+ Return the NetBIOS called name, or my IP - but never global_myname().
+********************************************************************/
+
+const char *get_called_name(void)
+{
+ extern fstring local_machine;
+ static fstring called_name;
+
+ if (!*local_machine) {
+ fstrcpy(called_name, get_my_primary_ip());
+ DEBUG(8,("get_called_name: assuming that client used IP address [%s] as called name.\n",
+ called_name));
+ return called_name;
+ }
+
+ return local_machine;
+}
+
+/*******************************************************************
+ Return the max print jobs per queue.
+********************************************************************/
+
+int lp_maxprintjobs(int snum)
+{
+ int maxjobs = LP_SNUM_OK(snum) ? ServicePtrs[snum]->iMaxPrintJobs : sDefault.iMaxPrintJobs;
+ if (maxjobs <= 0 || maxjobs >= PRINT_MAX_JOBID)
+ maxjobs = PRINT_MAX_JOBID - 1;
+
+ return maxjobs;
+}
+
+/*******************************************************************
+ Ensure we don't use sendfile if server smb signing is active.
+********************************************************************/
+
+BOOL lp_use_sendfile(int snum)
+{
+ return (_lp_use_sendfile(snum) && !srv_is_signing_active());
+}
diff --git a/source/param/modconf.c b/source/param/modconf.c
new file mode 100644
index 00000000000..a9ab6f9b4a2
--- /dev/null
+++ b/source/param/modconf.c
@@ -0,0 +1,96 @@
+/*
+ Unix SMB/CIFS implementation.
+ Configuration Modules Support
+ Copyright (C) Simo Sorce 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+struct modconf_struct {
+ char *name;
+ struct config_functions *fns;
+};
+
+static struct modconf_struct module;
+
+NTSTATUS smb_register_config(int version, const char *name, struct config_functions *fns)
+{
+ if ((version != SAMBA_CONFIG_INTERFACE_VERSION)) {
+ DEBUG(0, ("smb_register_config: Failed to register config module.\n"
+ "The module has been compiled with a different interface version (%d).\n"
+ "The supported version is: %d\n",
+ version, SAMBA_CONFIG_INTERFACE_VERSION));
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ if (!name || !name[0]) {
+ DEBUG(0,("smb_register_config: Name missing!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ module.name = smb_xstrdup(name);
+ module.fns = fns;
+ DEBUG(5, ("smb_register_config: Successfully registeres config backend '%s'\n", name));
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ * Init the configuration module
+ *********************************************************************/
+
+BOOL modconf_init(const char *config_backend)
+{
+ NTSTATUS ret;
+ BOOL bret = False;
+ char *name;
+ char *params;
+
+ /* nothing to do */
+ if (!config_backend)
+ return True;
+
+ name = smb_xstrdup(config_backend);
+ if ((params = strchr(name, ':')) != NULL ) {
+ *params = '\0';
+ params++;
+ }
+
+ ret = smb_probe_module("config", name);
+
+ if (NT_STATUS_IS_OK(ret) && NT_STATUS_IS_OK(module.fns->init(params)))
+ bret = True;
+
+ SAFE_FREE(name);
+ return bret;
+}
+
+BOOL modconf_load(BOOL (*sfunc)(const char *),BOOL (*pfunc)(const char *, const char *))
+{
+ if (module.fns) {
+ if (NT_STATUS_IS_OK(module.fns->load(sfunc, pfunc))) {
+ return True;
+ }
+ }
+ return False;
+}
+
+NTSTATUS modconf_close(void)
+{
+ return module.fns->close();
+}
diff --git a/source/param/params.c b/source/param/params.c
new file mode 100644
index 00000000000..892e5476cc0
--- /dev/null
+++ b/source/param/params.c
@@ -0,0 +1,599 @@
+/* -------------------------------------------------------------------------- **
+ * Microsoft Network Services for Unix, AKA., Andrew Tridgell's SAMBA.
+ *
+ * This module Copyright (C) 1990-1998 Karl Auer
+ *
+ * Rewritten almost completely by Christopher R. Hertel
+ * at the University of Minnesota, September, 1997.
+ * This module Copyright (C) 1997-1998 by the University of Minnesota
+ * -------------------------------------------------------------------------- **
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * Module name: params
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * This module performs lexical analysis and initial parsing of a
+ * Windows-like parameter file. It recognizes and handles four token
+ * types: section-name, parameter-name, parameter-value, and
+ * end-of-file. Comments and line continuation are handled
+ * internally.
+ *
+ * The entry point to the module is function pm_process(). This
+ * function opens the source file, calls the Parse() function to parse
+ * the input, and then closes the file when either the EOF is reached
+ * or a fatal error is encountered.
+ *
+ * A sample parameter file might look like this:
+ *
+ * [section one]
+ * parameter one = value string
+ * parameter two = another value
+ * [section two]
+ * new parameter = some value or t'other
+ *
+ * The parameter file is divided into sections by section headers:
+ * section names enclosed in square brackets (eg. [section one]).
+ * Each section contains parameter lines, each of which consist of a
+ * parameter name and value delimited by an equal sign. Roughly, the
+ * syntax is:
+ *
+ * <file> :== { <section> } EOF
+ *
+ * <section> :== <section header> { <parameter line> }
+ *
+ * <section header> :== '[' NAME ']'
+ *
+ * <parameter line> :== NAME '=' VALUE '\n'
+ *
+ * Blank lines and comment lines are ignored. Comment lines are lines
+ * beginning with either a semicolon (';') or a pound sign ('#').
+ *
+ * All whitespace in section names and parameter names is compressed
+ * to single spaces. Leading and trailing whitespace is stipped from
+ * both names and values.
+ *
+ * Only the first equals sign in a parameter line is significant.
+ * Parameter values may contain equals signs, square brackets and
+ * semicolons. Internal whitespace is retained in parameter values,
+ * with the exception of the '\r' character, which is stripped for
+ * historic reasons. Parameter names may not start with a left square
+ * bracket, an equal sign, a pound sign, or a semicolon, because these
+ * are used to identify other tokens.
+ *
+ * -------------------------------------------------------------------------- **
+ */
+
+#include "includes.h"
+
+/* -------------------------------------------------------------------------- **
+ * Constants...
+ */
+
+#define BUFR_INC 1024
+
+
+/* -------------------------------------------------------------------------- **
+ * Variables...
+ *
+ * DEBUGLEVEL - The ubiquitous DEBUGLEVEL. This determines which DEBUG()
+ * messages will be produced.
+ * bufr - pointer to a global buffer. This is probably a kludge,
+ * but it was the nicest kludge I could think of (for now).
+ * bSize - The size of the global buffer <bufr>.
+ */
+
+static char *bufr = NULL;
+static int bSize = 0;
+
+/* we can't use FILE* due to the 256 fd limit - use this cheap hack
+ instead */
+typedef struct {
+ char *buf;
+ char *p;
+ size_t size;
+} myFILE;
+
+static int mygetc(myFILE *f)
+{
+ if (f->p >= f->buf+f->size) return EOF;
+ /* be sure to return chars >127 as positive values */
+ return (int)( *(f->p++) & 0x00FF );
+}
+
+static void myfile_close(myFILE *f)
+{
+ if (!f) return;
+ SAFE_FREE(f->buf);
+ SAFE_FREE(f);
+}
+
+/* -------------------------------------------------------------------------- **
+ * Functions...
+ */
+
+static int EatWhitespace( myFILE *InFile )
+ /* ------------------------------------------------------------------------ **
+ * Scan past whitespace (see ctype(3C)) and return the first non-whitespace
+ * character, or newline, or EOF.
+ *
+ * Input: InFile - Input source.
+ *
+ * Output: The next non-whitespace character in the input stream.
+ *
+ * Notes: Because the config files use a line-oriented grammar, we
+ * explicitly exclude the newline character from the list of
+ * whitespace characters.
+ * - Note that both EOF (-1) and the nul character ('\0') are
+ * considered end-of-file markers.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int c;
+
+ for( c = mygetc( InFile ); isspace( c ) && ('\n' != c); c = mygetc( InFile ) )
+ ;
+ return( c );
+ } /* EatWhitespace */
+
+static int EatComment( myFILE *InFile )
+ /* ------------------------------------------------------------------------ **
+ * Scan to the end of a comment.
+ *
+ * Input: InFile - Input source.
+ *
+ * Output: The character that marks the end of the comment. Normally,
+ * this will be a newline, but it *might* be an EOF.
+ *
+ * Notes: Because the config files use a line-oriented grammar, we
+ * explicitly exclude the newline character from the list of
+ * whitespace characters.
+ * - Note that both EOF (-1) and the nul character ('\0') are
+ * considered end-of-file markers.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int c;
+
+ for( c = mygetc( InFile ); ('\n'!=c) && (EOF!=c) && (c>0); c = mygetc( InFile ) )
+ ;
+ return( c );
+ } /* EatComment */
+
+/*****************************************************************************
+ * Scan backards within a string to discover if the last non-whitespace
+ * character is a line-continuation character ('\\').
+ *
+ * Input: line - A pointer to a buffer containing the string to be
+ * scanned.
+ * pos - This is taken to be the offset of the end of the
+ * string. This position is *not* scanned.
+ *
+ * Output: The offset of the '\\' character if it was found, or -1 to
+ * indicate that it was not.
+ *
+ *****************************************************************************/
+
+static int Continuation(char *line, int pos )
+{
+ pos--;
+ while( (pos >= 0) && isspace((int)line[pos]))
+ pos--;
+
+ return (((pos >= 0) && ('\\' == line[pos])) ? pos : -1 );
+}
+
+
+static BOOL Section( myFILE *InFile, BOOL (*sfunc)(const char *) )
+ /* ------------------------------------------------------------------------ **
+ * Scan a section name, and pass the name to function sfunc().
+ *
+ * Input: InFile - Input source.
+ * sfunc - Pointer to the function to be called if the section
+ * name is successfully read.
+ *
+ * Output: True if the section name was read and True was returned from
+ * <sfunc>. False if <sfunc> failed or if a lexical error was
+ * encountered.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int c;
+ int i;
+ int end;
+ const char *func = "params.c:Section() -";
+
+ i = 0; /* <i> is the offset of the next free byte in bufr[] and */
+ end = 0; /* <end> is the current "end of string" offset. In most */
+ /* cases these will be the same, but if the last */
+ /* character written to bufr[] is a space, then <end> */
+ /* will be one less than <i>. */
+
+ c = EatWhitespace( InFile ); /* We've already got the '['. Scan */
+ /* past initial white space. */
+
+ while( (EOF != c) && (c > 0) )
+ {
+
+ /* Check that the buffer is big enough for the next character. */
+ if( i > (bSize - 2) )
+ {
+ char *tb;
+
+ tb = Realloc( bufr, bSize +BUFR_INC );
+ if( NULL == tb )
+ {
+ DEBUG(0, ("%s Memory re-allocation failure.", func) );
+ return( False );
+ }
+ bufr = tb;
+ bSize += BUFR_INC;
+ }
+
+ /* Handle a single character. */
+ switch( c )
+ {
+ case ']': /* Found the closing bracket. */
+ bufr[end] = '\0';
+ if( 0 == end ) /* Don't allow an empty name. */
+ {
+ DEBUG(0, ("%s Empty section name in configuration file.\n", func ));
+ return( False );
+ }
+ if( !sfunc(bufr) ) /* Got a valid name. Deal with it. */
+ return( False );
+ (void)EatComment( InFile ); /* Finish off the line. */
+ return( True );
+
+ case '\n': /* Got newline before closing ']'. */
+ i = Continuation( bufr, i ); /* Check for line continuation. */
+ if( i < 0 )
+ {
+ bufr[end] = '\0';
+ DEBUG(0, ("%s Badly formed line in configuration file: %s\n",
+ func, bufr ));
+ return( False );
+ }
+ end = ( (i > 0) && (' ' == bufr[i - 1]) ) ? (i - 1) : (i);
+ c = mygetc( InFile ); /* Continue with next line. */
+ break;
+
+ default: /* All else are a valid name chars. */
+ if( isspace( c ) ) /* One space per whitespace region. */
+ {
+ bufr[end] = ' ';
+ i = end + 1;
+ c = EatWhitespace( InFile );
+ }
+ else /* All others copy verbatim. */
+ {
+ bufr[i++] = c;
+ end = i;
+ c = mygetc( InFile );
+ }
+ }
+ }
+
+ /* We arrive here if we've met the EOF before the closing bracket. */
+ DEBUG(0, ("%s Unexpected EOF in the configuration file: %s\n", func, bufr ));
+ return( False );
+ } /* Section */
+
+static BOOL Parameter( myFILE *InFile, BOOL (*pfunc)(const char *, const char *), int c )
+ /* ------------------------------------------------------------------------ **
+ * Scan a parameter name and value, and pass these two fields to pfunc().
+ *
+ * Input: InFile - The input source.
+ * pfunc - A pointer to the function that will be called to
+ * process the parameter, once it has been scanned.
+ * c - The first character of the parameter name, which
+ * would have been read by Parse(). Unlike a comment
+ * line or a section header, there is no lead-in
+ * character that can be discarded.
+ *
+ * Output: True if the parameter name and value were scanned and processed
+ * successfully, else False.
+ *
+ * Notes: This function is in two parts. The first loop scans the
+ * parameter name. Internal whitespace is compressed, and an
+ * equal sign (=) terminates the token. Leading and trailing
+ * whitespace is discarded. The second loop scans the parameter
+ * value. When both have been successfully identified, they are
+ * passed to pfunc() for processing.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int i = 0; /* Position within bufr. */
+ int end = 0; /* bufr[end] is current end-of-string. */
+ int vstart = 0; /* Starting position of the parameter value. */
+ const char *func = "params.c:Parameter() -";
+
+ /* Read the parameter name. */
+ while( 0 == vstart ) /* Loop until we've found the start of the value. */
+ {
+
+ if( i > (bSize - 2) ) /* Ensure there's space for next char. */
+ {
+ char *tb;
+
+ tb = Realloc( bufr, bSize + BUFR_INC );
+ if( NULL == tb )
+ {
+ DEBUG(0, ("%s Memory re-allocation failure.", func) );
+ return( False );
+ }
+ bufr = tb;
+ bSize += BUFR_INC;
+ }
+
+ switch( c )
+ {
+ case '=': /* Equal sign marks end of param name. */
+ if( 0 == end ) /* Don't allow an empty name. */
+ {
+ DEBUG(0, ("%s Invalid parameter name in config. file.\n", func ));
+ return( False );
+ }
+ bufr[end++] = '\0'; /* Mark end of string & advance. */
+ i = end; /* New string starts here. */
+ vstart = end; /* New string is parameter value. */
+ bufr[i] = '\0'; /* New string is nul, for now. */
+ break;
+
+ case '\n': /* Find continuation char, else error. */
+ i = Continuation( bufr, i );
+ if( i < 0 )
+ {
+ bufr[end] = '\0';
+ DEBUG(1,("%s Ignoring badly formed line in configuration file: %s\n",
+ func, bufr ));
+ return( True );
+ }
+ end = ( (i > 0) && (' ' == bufr[i - 1]) ) ? (i - 1) : (i);
+ c = mygetc( InFile ); /* Read past eoln. */
+ break;
+
+ case '\0': /* Shouldn't have EOF within param name. */
+ case EOF:
+ bufr[i] = '\0';
+ DEBUG(1,("%s Unexpected end-of-file at: %s\n", func, bufr ));
+ return( True );
+
+ default:
+ if( isspace( c ) ) /* One ' ' per whitespace region. */
+ {
+ bufr[end] = ' ';
+ i = end + 1;
+ c = EatWhitespace( InFile );
+ }
+ else /* All others verbatim. */
+ {
+ bufr[i++] = c;
+ end = i;
+ c = mygetc( InFile );
+ }
+ }
+ }
+
+ /* Now parse the value. */
+ c = EatWhitespace( InFile ); /* Again, trim leading whitespace. */
+ while( (EOF !=c) && (c > 0) )
+ {
+
+ if( i > (bSize - 2) ) /* Make sure there's enough room. */
+ {
+ char *tb;
+
+ tb = Realloc( bufr, bSize + BUFR_INC );
+ if( NULL == tb )
+ {
+ DEBUG(0, ("%s Memory re-allocation failure.", func) );
+ return( False );
+ }
+ bufr = tb;
+ bSize += BUFR_INC;
+ }
+
+ switch( c )
+ {
+ case '\r': /* Explicitly remove '\r' because the older */
+ c = mygetc( InFile ); /* version called fgets_slash() which also */
+ break; /* removes them. */
+
+ case '\n': /* Marks end of value unless there's a '\'. */
+ i = Continuation( bufr, i );
+ if( i < 0 )
+ c = 0;
+ else
+ {
+ for( end = i; (end >= 0) && isspace((int)bufr[end]); end-- )
+ ;
+ c = mygetc( InFile );
+ }
+ break;
+
+ default: /* All others verbatim. Note that spaces do */
+ bufr[i++] = c; /* not advance <end>. This allows trimming */
+ if( !isspace( c ) ) /* of whitespace at the end of the line. */
+ end = i;
+ c = mygetc( InFile );
+ break;
+ }
+ }
+ bufr[end] = '\0'; /* End of value. */
+
+ return( pfunc( bufr, &bufr[vstart] ) ); /* Pass name & value to pfunc(). */
+ } /* Parameter */
+
+static BOOL Parse( myFILE *InFile,
+ BOOL (*sfunc)(const char *),
+ BOOL (*pfunc)(const char *, const char *) )
+ /* ------------------------------------------------------------------------ **
+ * Scan & parse the input.
+ *
+ * Input: InFile - Input source.
+ * sfunc - Function to be called when a section name is scanned.
+ * See Section().
+ * pfunc - Function to be called when a parameter is scanned.
+ * See Parameter().
+ *
+ * Output: True if the file was successfully scanned, else False.
+ *
+ * Notes: The input can be viewed in terms of 'lines'. There are four
+ * types of lines:
+ * Blank - May contain whitespace, otherwise empty.
+ * Comment - First non-whitespace character is a ';' or '#'.
+ * The remainder of the line is ignored.
+ * Section - First non-whitespace character is a '['.
+ * Parameter - The default case.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int c;
+
+ c = EatWhitespace( InFile );
+ while( (EOF != c) && (c > 0) )
+ {
+ switch( c )
+ {
+ case '\n': /* Blank line. */
+ c = EatWhitespace( InFile );
+ break;
+
+ case ';': /* Comment line. */
+ case '#':
+ c = EatComment( InFile );
+ break;
+
+ case '[': /* Section Header. */
+ if( !Section( InFile, sfunc ) )
+ return( False );
+ c = EatWhitespace( InFile );
+ break;
+
+ case '\\': /* Bogus backslash. */
+ c = EatWhitespace( InFile );
+ break;
+
+ default: /* Parameter line. */
+ if( !Parameter( InFile, pfunc, c ) )
+ return( False );
+ c = EatWhitespace( InFile );
+ break;
+ }
+ }
+ return( True );
+ } /* Parse */
+
+static myFILE *OpenConfFile( const char *FileName )
+ /* ------------------------------------------------------------------------ **
+ * Open a configuration file.
+ *
+ * Input: FileName - The pathname of the config file to be opened.
+ *
+ * Output: A pointer of type (char **) to the lines of the file
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ const char *func = "params.c:OpenConfFile() -";
+ extern BOOL in_client;
+ int lvl = in_client?1:0;
+ myFILE *ret;
+
+ ret = (myFILE *)malloc(sizeof(*ret));
+ if (!ret) return NULL;
+
+ ret->buf = file_load(FileName, &ret->size);
+ if( NULL == ret->buf )
+ {
+ DEBUG( lvl,
+ ("%s Unable to open configuration file \"%s\":\n\t%s\n",
+ func, FileName, strerror(errno)) );
+ SAFE_FREE(ret);
+ return NULL;
+ }
+
+ ret->p = ret->buf;
+ return( ret );
+ } /* OpenConfFile */
+
+BOOL pm_process( const char *FileName,
+ BOOL (*sfunc)(const char *),
+ BOOL (*pfunc)(const char *, const char *) )
+ /* ------------------------------------------------------------------------ **
+ * Process the named parameter file.
+ *
+ * Input: FileName - The pathname of the parameter file to be opened.
+ * sfunc - A pointer to a function that will be called when
+ * a section name is discovered.
+ * pfunc - A pointer to a function that will be called when
+ * a parameter name and value are discovered.
+ *
+ * Output: TRUE if the file was successfully parsed, else FALSE.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int result;
+ myFILE *InFile;
+ const char *func = "params.c:pm_process() -";
+
+ InFile = OpenConfFile( FileName ); /* Open the config file. */
+ if( NULL == InFile )
+ return( False );
+
+ DEBUG( 3, ("%s Processing configuration file \"%s\"\n", func, FileName) );
+
+ if( NULL != bufr ) /* If we already have a buffer */
+ result = Parse( InFile, sfunc, pfunc ); /* (recursive call), then just */
+ /* use it. */
+
+ else /* If we don't have a buffer */
+ { /* allocate one, then parse, */
+ bSize = BUFR_INC; /* then free. */
+ bufr = (char *)malloc( bSize );
+ if( NULL == bufr )
+ {
+ DEBUG(0,("%s memory allocation failure.\n", func));
+ myfile_close(InFile);
+ return( False );
+ }
+ result = Parse( InFile, sfunc, pfunc );
+ SAFE_FREE( bufr );
+ bufr = NULL;
+ bSize = 0;
+ }
+
+ myfile_close(InFile);
+
+ if( !result ) /* Generic failure. */
+ {
+ DEBUG(0,("%s Failed. Error returned from params.c:parse().\n", func));
+ return( False );
+ }
+
+ return( True ); /* Generic success. */
+ } /* pm_process */
+
+/* -------------------------------------------------------------------------- */
diff --git a/source/passdb/.cvsignore b/source/passdb/.cvsignore
new file mode 100644
index 00000000000..5f2a5c4cf75
--- /dev/null
+++ b/source/passdb/.cvsignore
@@ -0,0 +1,2 @@
+*.po
+*.po32
diff --git a/source/passdb/login_cache.c b/source/passdb/login_cache.c
new file mode 100644
index 00000000000..4b760172ec2
--- /dev/null
+++ b/source/passdb/login_cache.c
@@ -0,0 +1,174 @@
+/*
+ Unix SMB/CIFS implementation.
+ SAM_ACCOUNT local cache for
+ Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2004.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+#define LOGIN_CACHE_FILE "login_cache.tdb"
+
+#define SAM_CACHE_FORMAT "dwwd"
+
+static TDB_CONTEXT *cache;
+
+BOOL login_cache_init(void)
+{
+ char* cache_fname = NULL;
+
+ /* skip file open if it's already opened */
+ if (cache) return True;
+
+ asprintf(&cache_fname, "%s/%s", lp_lockdir(), LOGIN_CACHE_FILE);
+ if (cache_fname)
+ DEBUG(5, ("Opening cache file at %s\n", cache_fname));
+ else {
+ DEBUG(0, ("Filename allocation failed.\n"));
+ return False;
+ }
+
+ cache = tdb_open_log(cache_fname, 0, TDB_DEFAULT,
+ O_RDWR|O_CREAT, 0644);
+
+ if (!cache)
+ DEBUG(5, ("Attempt to open %s failed.\n", cache_fname));
+
+ SAFE_FREE(cache_fname);
+
+ return (cache ? True : False);
+}
+
+BOOL login_cache_shutdown(void)
+{
+ /* tdb_close routine returns -1 on error */
+ if (!cache) return False;
+ DEBUG(5, ("Closing cache file\n"));
+ return tdb_close(cache) != -1;
+}
+
+/* if we can't read the cache, oh well, no need to return anything */
+LOGIN_CACHE * login_cache_read(SAM_ACCOUNT *sampass)
+{
+ TDB_DATA keybuf, databuf;
+ LOGIN_CACHE *entry;
+
+ if (!login_cache_init())
+ return NULL;
+
+ keybuf.dptr = strdup(pdb_get_nt_username(sampass));
+ if (!keybuf.dptr || !strlen(keybuf.dptr)) {
+ SAFE_FREE(keybuf.dptr);
+ return NULL;
+ }
+ keybuf.dsize = strlen(keybuf.dptr) + 1;
+
+ DEBUG(7, ("Looking up login cache for user %s\n",
+ keybuf.dptr));
+ databuf = tdb_fetch(cache, keybuf);
+ SAFE_FREE(keybuf.dptr);
+
+ if (!(entry = malloc(sizeof(LOGIN_CACHE)))) {
+ DEBUG(1, ("Unable to allocate cache entry buffer!\n"));
+ SAFE_FREE(databuf.dptr);
+ return NULL;
+ }
+
+ if (tdb_unpack (databuf.dptr, databuf.dsize, SAM_CACHE_FORMAT,
+ &entry->entry_timestamp, &entry->acct_ctrl,
+ &entry->bad_password_count,
+ &entry->bad_password_time) == -1) {
+ DEBUG(7, ("No cache entry found\n"));
+ SAFE_FREE(databuf.dptr);
+ return NULL;
+ }
+
+ DEBUG(5, ("Found login cache entry: timestamp %12u, flags 0x%x, count %d, time %12u\n",
+ entry->entry_timestamp, entry->acct_ctrl,
+ entry->bad_password_count, entry->bad_password_time));
+ return entry;
+}
+
+BOOL login_cache_write(const SAM_ACCOUNT *sampass, LOGIN_CACHE entry)
+{
+
+ TDB_DATA keybuf, databuf;
+ BOOL ret;
+
+
+ keybuf.dptr = strdup(pdb_get_nt_username(sampass));
+ if (!keybuf.dptr || !strlen(keybuf.dptr)) {
+ SAFE_FREE(keybuf.dptr);
+ return False;
+ }
+ keybuf.dsize = strlen(keybuf.dptr) + 1;
+
+ entry.entry_timestamp = time(NULL);
+
+ databuf.dsize =
+ tdb_pack(NULL, 0, SAM_CACHE_FORMAT,
+ entry.entry_timestamp,
+ entry.acct_ctrl,
+ entry.bad_password_count,
+ entry.bad_password_time);
+ databuf.dptr = malloc(databuf.dsize);
+ if (!databuf.dptr) {
+ SAFE_FREE(keybuf.dptr);
+ return False;
+ }
+
+ if (tdb_pack(databuf.dptr, databuf.dsize, SAM_CACHE_FORMAT,
+ entry.entry_timestamp,
+ entry.acct_ctrl,
+ entry.bad_password_count,
+ entry.bad_password_time)
+ != databuf.dsize) {
+ SAFE_FREE(keybuf.dptr);
+ SAFE_FREE(databuf.dptr);
+ return False;
+ }
+
+ ret = tdb_store(cache, keybuf, databuf, 0);
+ SAFE_FREE(keybuf.dptr);
+ SAFE_FREE(databuf.dptr);
+ return ret == 0;
+}
+
+BOOL login_cache_delentry(const SAM_ACCOUNT *sampass)
+{
+ int ret;
+ TDB_DATA keybuf;
+
+ if (!login_cache_init())
+ return False;
+
+ keybuf.dptr = strdup(pdb_get_nt_username(sampass));
+ if (!keybuf.dptr || !strlen(keybuf.dptr)) {
+ SAFE_FREE(keybuf.dptr);
+ return False;
+ }
+ keybuf.dsize = strlen(keybuf.dptr) + 1;
+ DEBUG(9, ("About to delete entry for %s\n", keybuf.dptr));
+ ret = tdb_delete(cache, keybuf);
+ DEBUG(9, ("tdb_delete returned %d\n", ret));
+
+ SAFE_FREE(keybuf.dptr);
+ return ret == 0;
+}
+
diff --git a/source/passdb/lookup_sid.c b/source/passdb/lookup_sid.c
new file mode 100644
index 00000000000..a365cba0082
--- /dev/null
+++ b/source/passdb/lookup_sid.c
@@ -0,0 +1,529 @@
+/*
+ Unix SMB/CIFS implementation.
+ uid/user handling
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Gerald (Jerry) Carter 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*****************************************************************
+ *THE CANONICAL* convert name to SID function.
+ Tries local lookup first - for local domains - then uses winbind.
+*****************************************************************/
+
+BOOL lookup_name(const char *domain, const char *name, DOM_SID *psid, enum SID_NAME_USE *name_type)
+{
+ fstring sid;
+ BOOL local_lookup = False;
+
+ *name_type = SID_NAME_UNKNOWN;
+
+ /* If we are looking up a domain user, make sure it is
+ for the local machine only */
+
+ if (strequal(domain, get_global_sam_name())) {
+ if (local_lookup_name(name, psid, name_type)) {
+ DEBUG(10,
+ ("lookup_name: (local) [%s]\\[%s] -> SID %s (type %s: %u)\n",
+ domain, name, sid_to_string(sid,psid),
+ sid_type_lookup(*name_type), (unsigned int)*name_type));
+ return True;
+ }
+ } else {
+ /* Remote */
+ if (winbind_lookup_name(domain, name, psid, name_type)) {
+
+ DEBUG(10,("lookup_name (winbindd): [%s]\\[%s] -> SID %s (type %u)\n",
+ domain, name, sid_to_string(sid, psid),
+ (unsigned int)*name_type));
+ return True;
+ }
+ }
+
+ DEBUG(10, ("lookup_name: %s lookup for [%s]\\[%s] failed\n",
+ local_lookup ? "local" : "winbind", domain, name));
+
+ return False;
+}
+
+/*****************************************************************
+ *THE CANONICAL* convert SID to name function.
+ Tries local lookup first - for local sids, then tries winbind.
+*****************************************************************/
+
+BOOL lookup_sid(const DOM_SID *sid, fstring dom_name, fstring name, enum SID_NAME_USE *name_type)
+{
+ if (!name_type)
+ return False;
+
+ *name_type = SID_NAME_UNKNOWN;
+
+ /* Check if this is our own sid. This should perhaps be done by
+ winbind? For the moment handle it here. */
+
+ if (sid->num_auths == 5) {
+ DOM_SID tmp_sid;
+ uint32 rid;
+
+ sid_copy(&tmp_sid, sid);
+ sid_split_rid(&tmp_sid, &rid);
+
+ if (sid_equal(get_global_sam_sid(), &tmp_sid)) {
+
+ return map_domain_sid_to_name(&tmp_sid, dom_name) &&
+ local_lookup_sid(sid, name, name_type);
+ }
+ }
+
+ if (!winbind_lookup_sid(sid, dom_name, name, name_type)) {
+ fstring sid_str;
+ DOM_SID tmp_sid;
+ uint32 rid;
+
+ DEBUG(10,("lookup_sid: winbind lookup for SID %s failed - trying local.\n", sid_to_string(sid_str, sid) ));
+
+ sid_copy(&tmp_sid, sid);
+ sid_split_rid(&tmp_sid, &rid);
+ return map_domain_sid_to_name(&tmp_sid, dom_name) &&
+ lookup_known_rid(&tmp_sid, rid, name, name_type);
+ }
+ return True;
+}
+
+BOOL sid_to_local_user_name(const DOM_SID *sid, fstring username)
+{
+ fstring dom_name;
+ fstring name;
+ enum SID_NAME_USE type;
+
+ if (!sid_check_is_in_our_domain(sid))
+ return False;
+
+ if (!lookup_sid(sid, dom_name, name, &type))
+ return False;
+
+ if (type != SID_NAME_USER)
+ return False;
+
+ fstrcpy(username, name);
+ return True;
+}
+
+BOOL sid_to_local_dom_grp_name(const DOM_SID *sid, fstring groupname)
+{
+ fstring dom_name;
+ fstring name;
+ enum SID_NAME_USE type;
+
+ if (!sid_check_is_in_our_domain(sid))
+ return False;
+
+ if (!lookup_sid(sid, dom_name, name, &type))
+ return False;
+
+ if (type != SID_NAME_DOM_GRP)
+ return False;
+
+ fstrcpy(groupname, name);
+ return True;
+}
+
+
+/*****************************************************************
+ Id mapping cache. This is to avoid Winbind mappings already
+ seen by smbd to be queried too frequently, keeping winbindd
+ busy, and blocking smbd while winbindd is busy with other
+ stuff. Written by Michael Steffens <michael.steffens@hp.com>,
+ modified to use linked lists by jra.
+*****************************************************************/
+
+#define MAX_UID_SID_CACHE_SIZE 100
+#define TURNOVER_UID_SID_CACHE_SIZE 10
+#define MAX_GID_SID_CACHE_SIZE 100
+#define TURNOVER_GID_SID_CACHE_SIZE 10
+
+static size_t n_uid_sid_cache = 0;
+static size_t n_gid_sid_cache = 0;
+
+static struct uid_sid_cache {
+ struct uid_sid_cache *next, *prev;
+ uid_t uid;
+ DOM_SID sid;
+ enum SID_NAME_USE sidtype;
+} *uid_sid_cache_head;
+
+static struct gid_sid_cache {
+ struct gid_sid_cache *next, *prev;
+ gid_t gid;
+ DOM_SID sid;
+ enum SID_NAME_USE sidtype;
+} *gid_sid_cache_head;
+
+/*****************************************************************
+ Find a SID given a uid.
+*****************************************************************/
+
+static BOOL fetch_sid_from_uid_cache(DOM_SID *psid, uid_t uid)
+{
+ struct uid_sid_cache *pc;
+
+ for (pc = uid_sid_cache_head; pc; pc = pc->next) {
+ if (pc->uid == uid) {
+ fstring sid;
+ *psid = pc->sid;
+ DEBUG(3,("fetch sid from uid cache %u -> %s\n",
+ (unsigned int)uid, sid_to_string(sid, psid)));
+ DLIST_PROMOTE(uid_sid_cache_head, pc);
+ return True;
+ }
+ }
+ return False;
+}
+
+/*****************************************************************
+ Find a uid given a SID.
+*****************************************************************/
+
+static BOOL fetch_uid_from_cache( uid_t *puid, const DOM_SID *psid )
+{
+ struct uid_sid_cache *pc;
+
+ for (pc = uid_sid_cache_head; pc; pc = pc->next) {
+ if (sid_compare(&pc->sid, psid) == 0) {
+ fstring sid;
+ *puid = pc->uid;
+ DEBUG(3,("fetch uid from cache %u -> %s\n",
+ (unsigned int)*puid, sid_to_string(sid, psid)));
+ DLIST_PROMOTE(uid_sid_cache_head, pc);
+ return True;
+ }
+ }
+ return False;
+}
+
+/*****************************************************************
+ Store uid to SID mapping in cache.
+*****************************************************************/
+
+static void store_uid_sid_cache(const DOM_SID *psid, uid_t uid)
+{
+ struct uid_sid_cache *pc;
+
+ if (n_uid_sid_cache >= MAX_UID_SID_CACHE_SIZE && n_uid_sid_cache > TURNOVER_UID_SID_CACHE_SIZE) {
+ /* Delete the last TURNOVER_UID_SID_CACHE_SIZE entries. */
+ struct uid_sid_cache *pc_next;
+ size_t i;
+
+ for (i = 0, pc = uid_sid_cache_head; i < (n_uid_sid_cache - TURNOVER_UID_SID_CACHE_SIZE); i++, pc = pc->next)
+ ;
+ for(; pc; pc = pc_next) {
+ pc_next = pc->next;
+ DLIST_REMOVE(uid_sid_cache_head,pc);
+ SAFE_FREE(pc);
+ n_uid_sid_cache--;
+ }
+ }
+
+ pc = (struct uid_sid_cache *)malloc(sizeof(struct uid_sid_cache));
+ if (!pc)
+ return;
+ pc->uid = uid;
+ sid_copy(&pc->sid, psid);
+ DLIST_ADD(uid_sid_cache_head, pc);
+ n_uid_sid_cache++;
+}
+
+/*****************************************************************
+ Find a SID given a gid.
+*****************************************************************/
+
+static BOOL fetch_sid_from_gid_cache(DOM_SID *psid, gid_t gid)
+{
+ struct gid_sid_cache *pc;
+
+ for (pc = gid_sid_cache_head; pc; pc = pc->next) {
+ if (pc->gid == gid) {
+ fstring sid;
+ *psid = pc->sid;
+ DEBUG(3,("fetch sid from gid cache %u -> %s\n",
+ (unsigned int)gid, sid_to_string(sid, psid)));
+ DLIST_PROMOTE(gid_sid_cache_head, pc);
+ return True;
+ }
+ }
+ return False;
+}
+
+/*****************************************************************
+ Find a gid given a SID.
+*****************************************************************/
+
+static BOOL fetch_gid_from_cache(gid_t *pgid, const DOM_SID *psid)
+{
+ struct gid_sid_cache *pc;
+
+ for (pc = gid_sid_cache_head; pc; pc = pc->next) {
+ if (sid_compare(&pc->sid, psid) == 0) {
+ fstring sid;
+ *pgid = pc->gid;
+ DEBUG(3,("fetch uid from cache %u -> %s\n",
+ (unsigned int)*pgid, sid_to_string(sid, psid)));
+ DLIST_PROMOTE(gid_sid_cache_head, pc);
+ return True;
+ }
+ }
+ return False;
+}
+
+/*****************************************************************
+ Store gid to SID mapping in cache.
+*****************************************************************/
+
+static void store_gid_sid_cache(const DOM_SID *psid, gid_t gid)
+{
+ struct gid_sid_cache *pc;
+
+ if (n_gid_sid_cache >= MAX_GID_SID_CACHE_SIZE && n_gid_sid_cache > TURNOVER_GID_SID_CACHE_SIZE) {
+ /* Delete the last TURNOVER_GID_SID_CACHE_SIZE entries. */
+ struct gid_sid_cache *pc_next;
+ size_t i;
+
+ for (i = 0, pc = gid_sid_cache_head; i < (n_gid_sid_cache - TURNOVER_GID_SID_CACHE_SIZE); i++, pc = pc->next)
+ ;
+ for(; pc; pc = pc_next) {
+ pc_next = pc->next;
+ DLIST_REMOVE(gid_sid_cache_head,pc);
+ SAFE_FREE(pc);
+ n_gid_sid_cache--;
+ }
+ }
+
+ pc = (struct gid_sid_cache *)malloc(sizeof(struct gid_sid_cache));
+ if (!pc)
+ return;
+ pc->gid = gid;
+ sid_copy(&pc->sid, psid);
+ DLIST_ADD(gid_sid_cache_head, pc);
+ n_gid_sid_cache++;
+}
+
+/*****************************************************************
+ *THE CANONICAL* convert uid_t to SID function.
+*****************************************************************/
+
+NTSTATUS uid_to_sid(DOM_SID *psid, uid_t uid)
+{
+ fstring sid;
+ uid_t low, high;
+
+ ZERO_STRUCTP(psid);
+
+ if (fetch_sid_from_uid_cache(psid, uid))
+ return ( psid ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL );
+
+ /* DC's never use winbindd to resolve users outside the
+ defined idmap range */
+
+ if ( lp_server_role()==ROLE_DOMAIN_MEMBER
+ || (lp_idmap_uid(&low, &high) && uid >= low && uid <= high) )
+ {
+ if (winbind_uid_to_sid(psid, uid)) {
+
+ DEBUG(10,("uid_to_sid: winbindd %u -> %s\n",
+ (unsigned int)uid, sid_to_string(sid, psid)));
+
+ if (psid)
+ store_uid_sid_cache(psid, uid);
+ return ( psid ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL );
+ }
+ }
+
+ if (!local_uid_to_sid(psid, uid)) {
+ DEBUG(10,("uid_to_sid: local %u failed to map to sid\n", (unsigned int)uid ));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(10,("uid_to_sid: local %u -> %s\n", (unsigned int)uid, sid_to_string(sid, psid)));
+
+ store_uid_sid_cache(psid, uid);
+ return NT_STATUS_OK;
+}
+
+/*****************************************************************
+ *THE CANONICAL* convert gid_t to SID function.
+*****************************************************************/
+
+NTSTATUS gid_to_sid(DOM_SID *psid, gid_t gid)
+{
+ fstring sid;
+ gid_t low, high;
+
+ ZERO_STRUCTP(psid);
+
+ if (fetch_sid_from_gid_cache(psid, gid))
+ return ( psid ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL );
+
+ /* DC's never use winbindd to resolve groups outside the
+ defined idmap range */
+
+ if ( lp_server_role()==ROLE_DOMAIN_MEMBER
+ || (lp_idmap_gid(&low, &high) && gid >= low && gid <= high) )
+ {
+ if (winbind_gid_to_sid(psid, gid)) {
+
+ DEBUG(10,("gid_to_sid: winbindd %u -> %s\n",
+ (unsigned int)gid, sid_to_string(sid, psid)));
+
+ if (psid)
+ store_gid_sid_cache(psid, gid);
+ return ( psid ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL );
+ }
+ }
+
+ if (!local_gid_to_sid(psid, gid)) {
+ DEBUG(10,("gid_to_sid: local %u failed to map to sid\n", (unsigned int)gid ));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(10,("gid_to_sid: local %u -> %s\n", (unsigned int)gid, sid_to_string(sid, psid)));
+
+ store_gid_sid_cache(psid, gid);
+ return NT_STATUS_OK;
+}
+
+/*****************************************************************
+ *THE CANONICAL* convert SID to uid function.
+*****************************************************************/
+
+NTSTATUS sid_to_uid(const DOM_SID *psid, uid_t *puid)
+{
+ fstring dom_name, name, sid_str;
+ enum SID_NAME_USE name_type;
+
+ if (fetch_uid_from_cache(puid, psid))
+ return NT_STATUS_OK;
+
+ /* if this is our SID then go straight to a local lookup */
+
+ if ( sid_compare_domain(get_global_sam_sid(), psid) == 0 ) {
+ DEBUG(10,("sid_to_uid: my domain (%s) - trying local.\n",
+ sid_string_static(psid) ));
+
+ if ( local_sid_to_uid(puid, psid, &name_type) )
+ goto success;
+
+ DEBUG(10,("sid_to_uid: local lookup failed\n"));
+
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* If it is not our local domain, only hope is winbindd */
+
+ if ( !winbind_lookup_sid(psid, dom_name, name, &name_type) ) {
+ DEBUG(10,("sid_to_uid: winbind lookup for non-local sid %s failed\n",
+ sid_string_static(psid) ));
+
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* If winbindd does know the SID, ensure this is a user */
+
+ if (name_type != SID_NAME_USER) {
+ DEBUG(10,("sid_to_uid: winbind lookup succeeded but SID is not a user (%u)\n",
+ (unsigned int)name_type ));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* get the uid. Has to work or else we are dead in the water */
+
+ if ( !winbind_sid_to_uid(puid, psid) ) {
+ DEBUG(10,("sid_to_uid: winbind failed to allocate a new uid for sid %s\n",
+ sid_to_string(sid_str, psid) ));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+success:
+ DEBUG(10,("sid_to_uid: %s -> %u\n", sid_to_string(sid_str, psid),
+ (unsigned int)*puid ));
+
+ store_uid_sid_cache(psid, *puid);
+
+ return NT_STATUS_OK;
+}
+/*****************************************************************
+ *THE CANONICAL* convert SID to gid function.
+ Group mapping is used for gids that maps to Wellknown SIDs
+*****************************************************************/
+
+NTSTATUS sid_to_gid(const DOM_SID *psid, gid_t *pgid)
+{
+ fstring dom_name, name, sid_str;
+ enum SID_NAME_USE name_type;
+
+ if (fetch_gid_from_cache(pgid, psid))
+ return NT_STATUS_OK;
+
+ /*
+ * First we must look up the name and decide if this is a group sid.
+ * Group mapping can deal with foreign SIDs
+ */
+
+ if (!winbind_lookup_sid(psid, dom_name, name, &name_type)) {
+ DEBUG(10,("sid_to_gid: winbind lookup for sid %s failed - trying local.\n",
+ sid_to_string(sid_str, psid) ));
+
+ if ( local_sid_to_gid(pgid, psid, &name_type) )
+ goto success;
+
+ DEBUG(10,("sid_to_gid: no one knows this SID\n"));
+
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* winbindd knows it; Ensure this is a group sid */
+
+ if ((name_type != SID_NAME_DOM_GRP) && (name_type != SID_NAME_ALIAS)
+ && (name_type != SID_NAME_WKN_GRP))
+ {
+ DEBUG(10,("sid_to_gid: winbind lookup succeeded but SID is not a known group (%u)\n",
+ (unsigned int)name_type ));
+
+ /* winbindd is running and knows about this SID. Just the wrong type.
+ Don't fallback to a local lookup here */
+
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* winbindd knows it and it is a type of group; sid_to_gid must succeed
+ or we are dead in the water */
+
+ if ( !winbind_sid_to_gid(pgid, psid) ) {
+ DEBUG(10,("sid_to_uid: winbind failed to allocate a new gid for sid %s\n",
+ sid_to_string(sid_str, psid) ));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+success:
+ DEBUG(10,("sid_to_gid: %s -> %u\n", sid_to_string(sid_str, psid),
+ (unsigned int)*pgid ));
+
+ store_gid_sid_cache(psid, *pgid);
+
+ return NT_STATUS_OK;
+}
+
diff --git a/source/passdb/machine_sid.c b/source/passdb/machine_sid.c
new file mode 100644
index 00000000000..47b9e2d487e
--- /dev/null
+++ b/source/passdb/machine_sid.c
@@ -0,0 +1,213 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Jeremy Allison 1996-2002
+ Copyright (C) Andrew Tridgell 2002
+ Copyright (C) Gerald (Jerry) Carter 2000
+ Copyright (C) Stefan (metze) Metzmacher 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* NOTE! the global_sam_sid is the SID of our local SAM. This is only
+ equal to the domain SID when we are a DC, otherwise its our
+ workstation SID */
+static DOM_SID *global_sam_sid=NULL;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+/****************************************************************************
+ Read a SID from a file. This is for compatibility with the old MACHINE.SID
+ style of SID storage
+****************************************************************************/
+static BOOL read_sid_from_file(const char *fname, DOM_SID *sid)
+{
+ char **lines;
+ int numlines;
+ BOOL ret;
+
+ lines = file_lines_load(fname, &numlines);
+
+ if (!lines || numlines < 1) {
+ if (lines) file_lines_free(lines);
+ return False;
+ }
+
+ ret = string_to_sid(sid, lines[0]);
+ file_lines_free(lines);
+ return ret;
+}
+
+/*
+ generate a random sid - used to build our own sid if we don't have one
+*/
+static void generate_random_sid(DOM_SID *sid)
+{
+ int i;
+ uchar raw_sid_data[12];
+
+ memset((char *)sid, '\0', sizeof(*sid));
+ sid->sid_rev_num = 1;
+ sid->id_auth[5] = 5;
+ sid->num_auths = 0;
+ sid->sub_auths[sid->num_auths++] = 21;
+
+ generate_random_buffer(raw_sid_data, 12, True);
+ for (i = 0; i < 3; i++)
+ sid->sub_auths[sid->num_auths++] = IVAL(raw_sid_data, i*4);
+}
+
+/****************************************************************************
+ Generate the global machine sid.
+****************************************************************************/
+
+static DOM_SID *pdb_generate_sam_sid(void)
+{
+ DOM_SID domain_sid;
+ char *fname = NULL;
+ BOOL is_dc = False;
+ DOM_SID *sam_sid;
+
+ if(!(sam_sid=(DOM_SID *)malloc(sizeof(DOM_SID))))
+ return NULL;
+
+ generate_wellknown_sids();
+
+ switch (lp_server_role()) {
+ case ROLE_DOMAIN_PDC:
+ case ROLE_DOMAIN_BDC:
+ is_dc = True;
+ break;
+ default:
+ is_dc = False;
+ break;
+ }
+
+ if (is_dc) {
+ if (secrets_fetch_domain_sid(lp_workgroup(), &domain_sid)) {
+ sid_copy(sam_sid, &domain_sid);
+ return sam_sid;
+ }
+ }
+
+ if (secrets_fetch_domain_sid(global_myname(), sam_sid)) {
+
+ /* We got our sid. If not a pdc/bdc, we're done. */
+ if (!is_dc)
+ return sam_sid;
+
+ if (!secrets_fetch_domain_sid(lp_workgroup(), &domain_sid)) {
+
+ /* No domain sid and we're a pdc/bdc. Store it */
+
+ if (!secrets_store_domain_sid(lp_workgroup(), sam_sid)) {
+ DEBUG(0,("pdb_generate_sam_sid: Can't store domain SID as a pdc/bdc.\n"));
+ SAFE_FREE(sam_sid);
+ return NULL;
+ }
+ return sam_sid;
+ }
+
+ if (!sid_equal(&domain_sid, sam_sid)) {
+
+ /* Domain name sid doesn't match global sam sid. Re-store domain sid as 'local' sid. */
+
+ DEBUG(0,("pdb_generate_sam_sid: Mismatched SIDs as a pdc/bdc.\n"));
+ if (!secrets_store_domain_sid(global_myname(), &domain_sid)) {
+ DEBUG(0,("pdb_generate_sam_sid: Can't re-store domain SID for local sid as PDC/BDC.\n"));
+ SAFE_FREE(sam_sid);
+ return NULL;
+ }
+ return sam_sid;
+ }
+
+ return sam_sid;
+
+ }
+
+ /* check for an old MACHINE.SID file for backwards compatibility */
+ asprintf(&fname, "%s/MACHINE.SID", lp_private_dir());
+
+ if (read_sid_from_file(fname, sam_sid)) {
+ /* remember it for future reference and unlink the old MACHINE.SID */
+ if (!secrets_store_domain_sid(global_myname(), sam_sid)) {
+ DEBUG(0,("pdb_generate_sam_sid: Failed to store SID from file.\n"));
+ SAFE_FREE(fname);
+ SAFE_FREE(sam_sid);
+ return NULL;
+ }
+ unlink(fname);
+ if (is_dc) {
+ if (!secrets_store_domain_sid(lp_workgroup(), sam_sid)) {
+ DEBUG(0,("pdb_generate_sam_sid: Failed to store domain SID from file.\n"));
+ SAFE_FREE(fname);
+ SAFE_FREE(sam_sid);
+ return NULL;
+ }
+ }
+
+ /* Stored the old sid from MACHINE.SID successfully.*/
+ SAFE_FREE(fname);
+ return sam_sid;
+ }
+
+ SAFE_FREE(fname);
+
+ /* we don't have the SID in secrets.tdb, we will need to
+ generate one and save it */
+ generate_random_sid(sam_sid);
+
+ if (!secrets_store_domain_sid(global_myname(), sam_sid)) {
+ DEBUG(0,("pdb_generate_sam_sid: Failed to store generated machine SID.\n"));
+ SAFE_FREE(sam_sid);
+ return NULL;
+ }
+ if (is_dc) {
+ if (!secrets_store_domain_sid(lp_workgroup(), sam_sid)) {
+ DEBUG(0,("pdb_generate_sam_sid: Failed to store generated domain SID.\n"));
+ SAFE_FREE(sam_sid);
+ return NULL;
+ }
+ }
+
+ return sam_sid;
+}
+
+/* return our global_sam_sid */
+DOM_SID *get_global_sam_sid(void)
+{
+ if (global_sam_sid != NULL)
+ return global_sam_sid;
+
+ /* memory for global_sam_sid is allocated in
+ pdb_generate_sam_sid() as needed */
+
+ if (!(global_sam_sid = pdb_generate_sam_sid())) {
+ smb_panic("Could not generate a machine SID\n");
+ }
+
+ return global_sam_sid;
+}
+
+/**
+ * Force get_global_sam_sid to requery the backends
+ */
+void reset_global_sam_sid(void)
+{
+ SAFE_FREE(global_sam_sid);
+}
diff --git a/source/passdb/passdb.c b/source/passdb/passdb.c
new file mode 100644
index 00000000000..73f613535d9
--- /dev/null
+++ b/source/passdb/passdb.c
@@ -0,0 +1,2388 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Jeremy Allison 1996-2001
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+ Copyright (C) Gerald (Jerry) Carter 2000-2001
+ Copyright (C) Andrew Bartlett 2001-2002
+ Copyright (C) Simo Sorce 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+/******************************************************************
+ get the default domain/netbios name to be used when
+ testing authentication. For example, if you connect
+ to a Windows member server using a bogus domain name, the
+ Windows box will map the BOGUS\user to DOMAIN\user. A
+ standalone box will map to WKS\user.
+******************************************************************/
+
+const char *get_default_sam_name(void)
+{
+ /* standalone servers can only use the local netbios name */
+ if ( lp_server_role() == ROLE_STANDALONE )
+ return global_myname();
+
+ /* Windows domain members default to the DOMAIN
+ name when not specified */
+ return lp_workgroup();
+}
+
+/******************************************************************
+ get the default domain/netbios name to be used when dealing
+ with our passdb list of accounts
+******************************************************************/
+
+const char *get_global_sam_name(void)
+{
+ if ((lp_server_role() == ROLE_DOMAIN_PDC) || (lp_server_role() == ROLE_DOMAIN_BDC)) {
+ return lp_workgroup();
+ }
+ return global_myname();
+}
+
+/************************************************************
+ Fill the SAM_ACCOUNT with default values.
+ ***********************************************************/
+
+void pdb_fill_default_sam(SAM_ACCOUNT *user)
+{
+ ZERO_STRUCT(user->private); /* Don't touch the talloc context */
+
+ /* no initial methods */
+ user->methods = NULL;
+
+ /* Don't change these timestamp settings without a good reason.
+ They are important for NT member server compatibility. */
+
+ user->private.logon_time = (time_t)0;
+ user->private.pass_last_set_time = (time_t)0;
+ user->private.pass_can_change_time = (time_t)0;
+ user->private.logoff_time =
+ user->private.kickoff_time =
+ user->private.pass_must_change_time = get_time_t_max();
+ user->private.fields_present = 0x00ffffff;
+ user->private.logon_divs = 168; /* hours per week */
+ user->private.hours_len = 21; /* 21 times 8 bits = 168 */
+ memset(user->private.hours, 0xff, user->private.hours_len); /* available at all hours */
+ user->private.bad_password_count = 0;
+ user->private.logon_count = 0;
+ user->private.unknown_6 = 0x000004ec; /* don't know */
+
+ /* Some parts of samba strlen their pdb_get...() returns,
+ so this keeps the interface unchanged for now. */
+
+ user->private.username = "";
+ user->private.domain = "";
+ user->private.nt_username = "";
+ user->private.full_name = "";
+ user->private.home_dir = "";
+ user->private.logon_script = "";
+ user->private.profile_path = "";
+ user->private.acct_desc = "";
+ user->private.workstations = "";
+ user->private.unknown_str = "";
+ user->private.munged_dial = "";
+
+ user->private.plaintext_pw = NULL;
+
+ /*
+ Unless we know otherwise have a Account Control Bit
+ value of 'normal user'. This helps User Manager, which
+ asks for a filtered list of users.
+ */
+
+ user->private.acct_ctrl = ACB_NORMAL;
+}
+
+static void destroy_pdb_talloc(SAM_ACCOUNT **user)
+{
+ if (*user) {
+ data_blob_clear_free(&((*user)->private.lm_pw));
+ data_blob_clear_free(&((*user)->private.nt_pw));
+
+ if((*user)->private.plaintext_pw!=NULL)
+ memset((*user)->private.plaintext_pw,'\0',strlen((*user)->private.plaintext_pw));
+ talloc_destroy((*user)->mem_ctx);
+ *user = NULL;
+ }
+}
+
+
+/**********************************************************************
+ Allocates memory and initialises a struct sam_passwd on supplied mem_ctx.
+***********************************************************************/
+
+NTSTATUS pdb_init_sam_talloc(TALLOC_CTX *mem_ctx, SAM_ACCOUNT **user)
+{
+ if (*user != NULL) {
+ DEBUG(0,("pdb_init_sam_talloc: SAM_ACCOUNT was non NULL\n"));
+#if 0
+ smb_panic("non-NULL pointer passed to pdb_init_sam\n");
+#endif
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!mem_ctx) {
+ DEBUG(0,("pdb_init_sam_talloc: mem_ctx was NULL!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ *user=(SAM_ACCOUNT *)talloc(mem_ctx, sizeof(SAM_ACCOUNT));
+
+ if (*user==NULL) {
+ DEBUG(0,("pdb_init_sam_talloc: error while allocating memory\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*user)->mem_ctx = mem_ctx;
+
+ (*user)->free_fn = NULL;
+
+ pdb_fill_default_sam(*user);
+
+ return NT_STATUS_OK;
+}
+
+
+/*************************************************************
+ Allocates memory and initialises a struct sam_passwd.
+ ************************************************************/
+
+NTSTATUS pdb_init_sam(SAM_ACCOUNT **user)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS nt_status;
+
+ mem_ctx = talloc_init("passdb internal SAM_ACCOUNT allocation");
+
+ if (!mem_ctx) {
+ DEBUG(0,("pdb_init_sam: error while doing talloc_init()\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam_talloc(mem_ctx, user))) {
+ talloc_destroy(mem_ctx);
+ return nt_status;
+ }
+
+ (*user)->free_fn = destroy_pdb_talloc;
+
+ return NT_STATUS_OK;
+}
+
+/**************************************************************************
+ * This function will take care of all the steps needed to correctly
+ * allocate and set the user SID, please do use this function to create new
+ * users, messing with SIDs is not good.
+ *
+ * account_data must be provided initialized, pwd may be null.
+ * SSS
+ ***************************************************************************/
+
+static NTSTATUS pdb_set_sam_sids(SAM_ACCOUNT *account_data, const struct passwd *pwd)
+{
+ const char *guest_account = lp_guestaccount();
+ GROUP_MAP map;
+ BOOL ret;
+
+ if (!account_data || !pwd) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* this is a hack this thing should not be set
+ this way --SSS */
+ if (!(guest_account && *guest_account)) {
+ DEBUG(1, ("NULL guest account!?!?\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ } else {
+ /* Ensure this *must* be set right */
+ if (strcmp(pwd->pw_name, guest_account) == 0) {
+ if (!pdb_set_user_sid_from_rid(account_data, DOMAIN_USER_RID_GUEST, PDB_DEFAULT)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ if (!pdb_set_group_sid_from_rid(account_data, DOMAIN_GROUP_RID_GUESTS, PDB_DEFAULT)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ return NT_STATUS_OK;
+ }
+ }
+
+ if (!pdb_set_user_sid_from_rid(account_data, fallback_pdb_uid_to_user_rid(pwd->pw_uid), PDB_SET)) {
+ DEBUG(0,("Can't set User SID from RID!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* call the mapping code here */
+ become_root();
+ ret = pdb_getgrgid(&map, pwd->pw_gid);
+ unbecome_root();
+
+ if( ret ) {
+ if (!pdb_set_group_sid(account_data, &map.sid, PDB_SET)){
+ DEBUG(0,("Can't set Group SID!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+ else {
+ if (!pdb_set_group_sid_from_rid(account_data, pdb_gid_to_group_rid(pwd->pw_gid), PDB_SET)) {
+ DEBUG(0,("Can't set Group SID\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************
+ Initialises a struct sam_passwd with sane values.
+ ************************************************************/
+
+NTSTATUS pdb_fill_sam_pw(SAM_ACCOUNT *sam_account, const struct passwd *pwd)
+{
+ NTSTATUS ret;
+
+ if (!pwd) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ pdb_fill_default_sam(sam_account);
+
+ pdb_set_username(sam_account, pwd->pw_name, PDB_SET);
+ pdb_set_fullname(sam_account, pwd->pw_gecos, PDB_SET);
+
+ pdb_set_unix_homedir(sam_account, pwd->pw_dir, PDB_SET);
+
+ pdb_set_domain (sam_account, get_global_sam_name(), PDB_DEFAULT);
+
+ /* When we get a proper uid -> SID and SID -> uid allocation
+ mechinism, we should call it here.
+
+ We can't just set this to 0 or allow it only to be filled
+ in when added to the backend, because the user's SID
+ may already be in security descriptors etc.
+
+ -- abartlet 11-May-02
+ */
+
+ ret = pdb_set_sam_sids(sam_account, pwd);
+ if (!NT_STATUS_IS_OK(ret)) return ret;
+
+ /* check if this is a user account or a machine account */
+ if (pwd->pw_name[strlen(pwd->pw_name)-1] != '$')
+ {
+ pdb_set_profile_path(sam_account,
+ talloc_sub_specified((sam_account)->mem_ctx,
+ lp_logon_path(),
+ pwd->pw_name, global_myname(),
+ pwd->pw_uid, pwd->pw_gid),
+ PDB_DEFAULT);
+
+ pdb_set_homedir(sam_account,
+ talloc_sub_specified((sam_account)->mem_ctx,
+ lp_logon_home(),
+ pwd->pw_name, global_myname(),
+ pwd->pw_uid, pwd->pw_gid),
+ PDB_DEFAULT);
+
+ pdb_set_dir_drive(sam_account,
+ talloc_sub_specified((sam_account)->mem_ctx,
+ lp_logon_drive(),
+ pwd->pw_name, global_myname(),
+ pwd->pw_uid, pwd->pw_gid),
+ PDB_DEFAULT);
+
+ pdb_set_logon_script(sam_account,
+ talloc_sub_specified((sam_account)->mem_ctx,
+ lp_logon_script(),
+ pwd->pw_name, global_myname(),
+ pwd->pw_uid, pwd->pw_gid),
+ PDB_DEFAULT);
+ if (!pdb_set_acct_ctrl(sam_account, ACB_NORMAL, PDB_DEFAULT)) {
+ DEBUG(1, ("Failed to set 'normal account' flags for user %s.\n", pwd->pw_name));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ } else {
+ if (!pdb_set_acct_ctrl(sam_account, ACB_WSTRUST, PDB_DEFAULT)) {
+ DEBUG(1, ("Failed to set 'trusted workstation account' flags for user %s.\n", pwd->pw_name));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+
+/*************************************************************
+ Initialises a struct sam_passwd with sane values.
+ ************************************************************/
+
+NTSTATUS pdb_init_sam_pw(SAM_ACCOUNT **new_sam_acct, const struct passwd *pwd)
+{
+ NTSTATUS nt_status;
+
+ if (!pwd) {
+ new_sam_acct = NULL;
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam(new_sam_acct))) {
+ new_sam_acct = NULL;
+ return nt_status;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = pdb_fill_sam_pw(*new_sam_acct, pwd))) {
+ pdb_free_sam(new_sam_acct);
+ new_sam_acct = NULL;
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*************************************************************
+ Initialises a SAM_ACCOUNT ready to add a new account, based
+ on the UNIX user. Pass in a RID if you have one
+ ************************************************************/
+
+NTSTATUS pdb_init_sam_new(SAM_ACCOUNT **new_sam_acct, const char *username,
+ uint32 rid)
+{
+ NTSTATUS nt_status = NT_STATUS_NO_MEMORY;
+ struct passwd *pwd;
+ BOOL ret;
+
+ pwd = Get_Pwnam(username);
+
+ if (!pwd)
+ return NT_STATUS_NO_SUCH_USER;
+
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam_pw(new_sam_acct, pwd))) {
+ *new_sam_acct = NULL;
+ return nt_status;
+ }
+
+ /* see if we need to generate a new rid using the 2.2 algorithm */
+ if ( rid == 0 && lp_enable_rid_algorithm() ) {
+ DEBUG(10,("pdb_init_sam_new: no RID specified. Generating one via old algorithm\n"));
+ rid = fallback_pdb_uid_to_user_rid(pwd->pw_uid);
+ }
+
+ /* set the new SID */
+
+ ret = pdb_set_user_sid_from_rid( *new_sam_acct, rid, PDB_SET );
+
+ return (ret ? NT_STATUS_OK : NT_STATUS_NO_SUCH_USER);
+}
+
+
+/**
+ * Free the contets of the SAM_ACCOUNT, but not the structure.
+ *
+ * Also wipes the LM and NT hashes and plaintext password from
+ * memory.
+ *
+ * @param user SAM_ACCOUNT to free members of.
+ **/
+
+static void pdb_free_sam_contents(SAM_ACCOUNT *user)
+{
+
+ /* Kill off sensitive data. Free()ed by the
+ talloc mechinism */
+
+ data_blob_clear_free(&(user->private.lm_pw));
+ data_blob_clear_free(&(user->private.nt_pw));
+ if (user->private.plaintext_pw!=NULL)
+ memset(user->private.plaintext_pw,'\0',strlen(user->private.plaintext_pw));
+
+ if (user->private.backend_private_data && user->private.backend_private_data_free_fn) {
+ user->private.backend_private_data_free_fn(&user->private.backend_private_data);
+ }
+}
+
+
+/************************************************************
+ Reset the SAM_ACCOUNT and free the NT/LM hashes.
+ ***********************************************************/
+
+NTSTATUS pdb_reset_sam(SAM_ACCOUNT *user)
+{
+ if (user == NULL) {
+ DEBUG(0,("pdb_reset_sam: SAM_ACCOUNT was NULL\n"));
+#if 0
+ smb_panic("NULL pointer passed to pdb_free_sam\n");
+#endif
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ pdb_free_sam_contents(user);
+
+ pdb_fill_default_sam(user);
+
+ return NT_STATUS_OK;
+}
+
+
+/************************************************************
+ Free the SAM_ACCOUNT and the member pointers.
+ ***********************************************************/
+
+NTSTATUS pdb_free_sam(SAM_ACCOUNT **user)
+{
+ if (*user == NULL) {
+ DEBUG(0,("pdb_free_sam: SAM_ACCOUNT was NULL\n"));
+#if 0
+ smb_panic("NULL pointer passed to pdb_free_sam\n");
+#endif
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ pdb_free_sam_contents(*user);
+
+ if ((*user)->free_fn) {
+ (*user)->free_fn(user);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************
+ Encode the account control bits into a string.
+ length = length of string to encode into (including terminating
+ null). length *MUST BE MORE THAN 2* !
+ **********************************************************/
+
+char *pdb_encode_acct_ctrl(uint16 acct_ctrl, size_t length)
+{
+ static fstring acct_str;
+
+ size_t i = 0;
+
+ SMB_ASSERT(length <= sizeof(acct_str));
+
+ acct_str[i++] = '[';
+
+ if (acct_ctrl & ACB_PWNOTREQ ) acct_str[i++] = 'N';
+ if (acct_ctrl & ACB_DISABLED ) acct_str[i++] = 'D';
+ if (acct_ctrl & ACB_HOMDIRREQ) acct_str[i++] = 'H';
+ if (acct_ctrl & ACB_TEMPDUP ) acct_str[i++] = 'T';
+ if (acct_ctrl & ACB_NORMAL ) acct_str[i++] = 'U';
+ if (acct_ctrl & ACB_MNS ) acct_str[i++] = 'M';
+ if (acct_ctrl & ACB_WSTRUST ) acct_str[i++] = 'W';
+ if (acct_ctrl & ACB_SVRTRUST ) acct_str[i++] = 'S';
+ if (acct_ctrl & ACB_AUTOLOCK ) acct_str[i++] = 'L';
+ if (acct_ctrl & ACB_PWNOEXP ) acct_str[i++] = 'X';
+ if (acct_ctrl & ACB_DOMTRUST ) acct_str[i++] = 'I';
+
+ for ( ; i < length - 2 ; i++ )
+ acct_str[i] = ' ';
+
+ i = length - 2;
+ acct_str[i++] = ']';
+ acct_str[i++] = '\0';
+
+ return acct_str;
+}
+
+/**********************************************************
+ Decode the account control bits from a string.
+ **********************************************************/
+
+uint16 pdb_decode_acct_ctrl(const char *p)
+{
+ uint16 acct_ctrl = 0;
+ BOOL finished = False;
+
+ /*
+ * Check if the account type bits have been encoded after the
+ * NT password (in the form [NDHTUWSLXI]).
+ */
+
+ if (*p != '[')
+ return 0;
+
+ for (p++; *p && !finished; p++) {
+ switch (*p) {
+ case 'N': { acct_ctrl |= ACB_PWNOTREQ ; break; /* 'N'o password. */ }
+ case 'D': { acct_ctrl |= ACB_DISABLED ; break; /* 'D'isabled. */ }
+ case 'H': { acct_ctrl |= ACB_HOMDIRREQ; break; /* 'H'omedir required. */ }
+ case 'T': { acct_ctrl |= ACB_TEMPDUP ; break; /* 'T'emp account. */ }
+ case 'U': { acct_ctrl |= ACB_NORMAL ; break; /* 'U'ser account (normal). */ }
+ case 'M': { acct_ctrl |= ACB_MNS ; break; /* 'M'NS logon user account. What is this ? */ }
+ case 'W': { acct_ctrl |= ACB_WSTRUST ; break; /* 'W'orkstation account. */ }
+ case 'S': { acct_ctrl |= ACB_SVRTRUST ; break; /* 'S'erver account. */ }
+ case 'L': { acct_ctrl |= ACB_AUTOLOCK ; break; /* 'L'ocked account. */ }
+ case 'X': { acct_ctrl |= ACB_PWNOEXP ; break; /* No 'X'piry on password */ }
+ case 'I': { acct_ctrl |= ACB_DOMTRUST ; break; /* 'I'nterdomain trust account. */ }
+ case ' ': { break; }
+ case ':':
+ case '\n':
+ case '\0':
+ case ']':
+ default: { finished = True; }
+ }
+ }
+
+ return acct_ctrl;
+}
+
+/*************************************************************
+ Routine to set 32 hex password characters from a 16 byte array.
+**************************************************************/
+
+void pdb_sethexpwd(char *p, const unsigned char *pwd, uint16 acct_ctrl)
+{
+ if (pwd != NULL) {
+ int i;
+ for (i = 0; i < 16; i++)
+ slprintf(&p[i*2], 3, "%02X", pwd[i]);
+ } else {
+ if (acct_ctrl & ACB_PWNOTREQ)
+ safe_strcpy(p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", 33);
+ else
+ safe_strcpy(p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 33);
+ }
+}
+
+/*************************************************************
+ Routine to get the 32 hex characters and turn them
+ into a 16 byte array.
+**************************************************************/
+
+BOOL pdb_gethexpwd(const char *p, unsigned char *pwd)
+{
+ int i;
+ unsigned char lonybble, hinybble;
+ const char *hexchars = "0123456789ABCDEF";
+ char *p1, *p2;
+
+ if (!p)
+ return (False);
+
+ for (i = 0; i < 32; i += 2) {
+ hinybble = toupper(p[i]);
+ lonybble = toupper(p[i + 1]);
+
+ p1 = strchr(hexchars, hinybble);
+ p2 = strchr(hexchars, lonybble);
+
+ if (!p1 || !p2)
+ return (False);
+
+ hinybble = PTR_DIFF(p1, hexchars);
+ lonybble = PTR_DIFF(p2, hexchars);
+
+ pwd[i / 2] = (hinybble << 4) | lonybble;
+ }
+ return (True);
+}
+
+int algorithmic_rid_base(void)
+{
+ static int rid_offset = 0;
+
+ if (rid_offset != 0)
+ return rid_offset;
+
+ rid_offset = lp_algorithmic_rid_base();
+
+ if (rid_offset < BASE_RID) {
+ /* Try to prevent admin foot-shooting, we can't put algorithmic
+ rids below 1000, that's the 'well known RIDs' on NT */
+ DEBUG(0, ("'algorithmic rid base' must be equal to or above %ld\n", BASE_RID));
+ rid_offset = BASE_RID;
+ }
+ if (rid_offset & 1) {
+ DEBUG(0, ("algorithmic rid base must be even\n"));
+ rid_offset += 1;
+ }
+ return rid_offset;
+}
+
+/*******************************************************************
+ Converts NT user RID to a UNIX uid.
+ ********************************************************************/
+
+uid_t fallback_pdb_user_rid_to_uid(uint32 user_rid)
+{
+ int rid_offset = algorithmic_rid_base();
+ return (uid_t)(((user_rid & (~USER_RID_TYPE)) - rid_offset)/RID_MULTIPLIER);
+}
+
+/*******************************************************************
+ converts UNIX uid to an NT User RID.
+ ********************************************************************/
+
+uint32 fallback_pdb_uid_to_user_rid(uid_t uid)
+{
+ int rid_offset = algorithmic_rid_base();
+ return (((((uint32)uid)*RID_MULTIPLIER) + rid_offset) | USER_RID_TYPE);
+}
+
+/*******************************************************************
+ Converts NT group RID to a UNIX gid.
+ ********************************************************************/
+
+gid_t pdb_group_rid_to_gid(uint32 group_rid)
+{
+ int rid_offset = algorithmic_rid_base();
+ return (gid_t)(((group_rid & (~GROUP_RID_TYPE))- rid_offset)/RID_MULTIPLIER);
+}
+
+/*******************************************************************
+ converts NT Group RID to a UNIX uid.
+
+ warning: you must not call that function only
+ you must do a call to the group mapping first.
+ there is not anymore a direct link between the gid and the rid.
+ ********************************************************************/
+
+uint32 pdb_gid_to_group_rid(gid_t gid)
+{
+ int rid_offset = algorithmic_rid_base();
+ return (((((uint32)gid)*RID_MULTIPLIER) + rid_offset) | GROUP_RID_TYPE);
+}
+
+/*******************************************************************
+ Decides if a RID is a well known RID.
+ ********************************************************************/
+
+static BOOL pdb_rid_is_well_known(uint32 rid)
+{
+ /* Not using rid_offset here, because this is the actual
+ NT fixed value (1000) */
+
+ return (rid < BASE_RID);
+}
+
+/*******************************************************************
+ Decides if a RID is a user or group RID.
+ ********************************************************************/
+
+BOOL fallback_pdb_rid_is_user(uint32 rid)
+{
+ /* lkcl i understand that NT attaches an enumeration to a RID
+ * such that it can be identified as either a user, group etc
+ * type. there are 5 such categories, and they are documented.
+ */
+ /* However, they are not in the RID, just somthing you can query
+ seperatly. Sorry luke :-) */
+
+ if(pdb_rid_is_well_known(rid)) {
+ /*
+ * The only well known user RIDs are DOMAIN_USER_RID_ADMIN
+ * and DOMAIN_USER_RID_GUEST.
+ */
+ if(rid == DOMAIN_USER_RID_ADMIN || rid == DOMAIN_USER_RID_GUEST)
+ return True;
+ } else if((rid & RID_TYPE_MASK) == USER_RID_TYPE) {
+ return True;
+ }
+ return False;
+}
+
+/*******************************************************************
+ Convert a rid into a name. Used in the lookup SID rpc.
+ ********************************************************************/
+
+BOOL local_lookup_sid(const DOM_SID *sid, char *name, enum SID_NAME_USE *psid_name_use)
+{
+ uint32 rid;
+ SAM_ACCOUNT *sam_account = NULL;
+ GROUP_MAP map;
+ BOOL ret;
+
+ if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid)){
+ DEBUG(0,("local_lookup_sid: sid_peek_check_rid return False! SID: %s\n",
+ sid_string_static(&map.sid)));
+ return False;
+ }
+ *psid_name_use = SID_NAME_UNKNOWN;
+
+ DEBUG(5,("local_lookup_sid: looking up RID %u.\n", (unsigned int)rid));
+
+ if (rid == DOMAIN_USER_RID_ADMIN) {
+ const char **admin_list = lp_admin_users(-1);
+ *psid_name_use = SID_NAME_USER;
+ if (admin_list) {
+ const char *p = *admin_list;
+ if(!next_token(&p, name, NULL, sizeof(fstring)))
+ fstrcpy(name, "Administrator");
+ } else {
+ fstrcpy(name, "Administrator");
+ }
+ return True;
+ }
+
+ if (!NT_STATUS_IS_OK(pdb_init_sam(&sam_account))) {
+ return False;
+ }
+
+ /* see if the passdb can help us with the name of the user */
+
+ /* BEING ROOT BLLOCK */
+ become_root();
+ if (pdb_getsampwsid(sam_account, sid)) {
+ unbecome_root(); /* -----> EXIT BECOME_ROOT() */
+ fstrcpy(name, pdb_get_username(sam_account));
+ *psid_name_use = SID_NAME_USER;
+
+ pdb_free_sam(&sam_account);
+
+ return True;
+ }
+ pdb_free_sam(&sam_account);
+
+ ret = pdb_getgrsid(&map, *sid);
+ unbecome_root();
+ /* END BECOME_ROOT BLOCK */
+
+ if ( ret ) {
+ if (map.gid!=(gid_t)-1) {
+ DEBUG(5,("local_lookup_sid: mapped group %s to gid %u\n", map.nt_name, (unsigned int)map.gid));
+ } else {
+ DEBUG(5,("local_lookup_sid: mapped group %s to no unix gid. Returning name.\n", map.nt_name));
+ }
+
+ fstrcpy(name, map.nt_name);
+ *psid_name_use = map.sid_name_use;
+ return True;
+ }
+
+ if (fallback_pdb_rid_is_user(rid)) {
+ uid_t uid;
+ struct passwd *pw = NULL;
+
+ DEBUG(5, ("assuming RID %u is a user\n", (unsigned)rid));
+
+ uid = fallback_pdb_user_rid_to_uid(rid);
+ pw = sys_getpwuid( uid );
+
+ DEBUG(5,("local_lookup_sid: looking up uid %u %s\n", (unsigned int)uid,
+ pw ? "succeeded" : "failed" ));
+
+ if ( !pw )
+ fstr_sprintf(name, "unix_user.%u", (unsigned int)uid);
+ else
+ fstrcpy( name, pw->pw_name );
+
+ DEBUG(5,("local_lookup_sid: found user %s for rid %u\n", name,
+ (unsigned int)rid ));
+
+ *psid_name_use = SID_NAME_USER;
+
+ return ( pw != NULL );
+ } else {
+ gid_t gid;
+ struct group *gr;
+
+ DEBUG(5, ("assuming RID %u is a group\n", (unsigned)rid));
+
+ gid = pdb_group_rid_to_gid(rid);
+ gr = getgrgid(gid);
+
+ DEBUG(5,("local_lookup_sid: looking up gid %u %s\n", (unsigned int)gid,
+ gr ? "succeeded" : "failed" ));
+
+ if( !gr )
+ fstr_sprintf(name, "unix_group.%u", (unsigned int)gid);
+ else
+ fstrcpy( name, gr->gr_name);
+
+ DEBUG(5,("local_lookup_sid: found group %s for rid %u\n", name,
+ (unsigned int)rid ));
+
+ /* assume fallback groups aer domain global groups */
+
+ *psid_name_use = SID_NAME_DOM_GRP;
+
+ return ( gr != NULL );
+ }
+}
+
+/*******************************************************************
+ Convert a name into a SID. Used in the lookup name rpc.
+ ********************************************************************/
+
+BOOL local_lookup_name(const char *c_user, DOM_SID *psid, enum SID_NAME_USE *psid_name_use)
+{
+ extern DOM_SID global_sid_World_Domain;
+ DOM_SID local_sid;
+ fstring user;
+ SAM_ACCOUNT *sam_account = NULL;
+ struct group *grp;
+ GROUP_MAP map;
+
+ *psid_name_use = SID_NAME_UNKNOWN;
+
+ /*
+ * user may be quoted a const string, and map_username and
+ * friends can modify it. Make a modifiable copy. JRA.
+ */
+
+ fstrcpy(user, c_user);
+
+ sid_copy(&local_sid, get_global_sam_sid());
+
+ /*
+ * Special case for MACHINE\Everyone. Map to the world_sid.
+ */
+
+ if(strequal(user, "Everyone")) {
+ sid_copy( psid, &global_sid_World_Domain);
+ sid_append_rid(psid, 0);
+ *psid_name_use = SID_NAME_ALIAS;
+ return True;
+ }
+
+ (void)map_username(user);
+
+ if (!NT_STATUS_IS_OK(pdb_init_sam(&sam_account))) {
+ return False;
+ }
+
+ /* BEGIN ROOT BLOCK */
+
+ become_root();
+ if (pdb_getsampwnam(sam_account, user)) {
+ unbecome_root();
+ sid_copy(psid, pdb_get_user_sid(sam_account));
+ *psid_name_use = SID_NAME_USER;
+
+ pdb_free_sam(&sam_account);
+ return True;
+ }
+
+ pdb_free_sam(&sam_account);
+
+ /*
+ * Maybe it was a group ?
+ */
+
+ /* check if it's a mapped group */
+ if (pdb_getgrnam(&map, user)) {
+ /* yes it's a mapped group */
+ sid_copy(&local_sid, &map.sid);
+ *psid_name_use = map.sid_name_use;
+ } else {
+ /* it's not a mapped group */
+ grp = getgrnam(user);
+ if(!grp) {
+ unbecome_root(); /* ---> exit form block */
+ return False;
+ }
+
+ /*
+ *check if it's mapped, if it is reply it doesn't exist
+ *
+ * that's to prevent this case:
+ *
+ * unix group ug is mapped to nt group ng
+ * someone does a lookup on ug
+ * we must not reply as it doesn't "exist" anymore
+ * for NT. For NT only ng exists.
+ * JFM, 30/11/2001
+ */
+
+ if (pdb_getgrgid(&map, grp->gr_gid)){
+ unbecome_root(); /* ---> exit form block */
+ return False;
+ }
+
+ sid_append_rid( &local_sid, pdb_gid_to_group_rid(grp->gr_gid));
+ *psid_name_use = SID_NAME_ALIAS;
+ }
+ unbecome_root();
+ /* END ROOT BLOCK */
+
+ sid_copy( psid, &local_sid);
+
+ return True;
+}
+
+/*************************************************************
+ Change a password entry in the local smbpasswd file.
+ *************************************************************/
+
+BOOL local_password_change(const char *user_name, int local_flags,
+ const char *new_passwd,
+ char *err_str, size_t err_str_len,
+ char *msg_str, size_t msg_str_len)
+{
+ SAM_ACCOUNT *sam_pass=NULL;
+ uint16 other_acb;
+
+ *err_str = '\0';
+ *msg_str = '\0';
+
+ /* Get the smb passwd entry for this user */
+ pdb_init_sam(&sam_pass);
+
+ become_root();
+ if(!pdb_getsampwnam(sam_pass, user_name)) {
+ unbecome_root();
+ pdb_free_sam(&sam_pass);
+
+ if ((local_flags & LOCAL_ADD_USER) || (local_flags & LOCAL_DELETE_USER)) {
+ /* Might not exist in /etc/passwd. Use rid algorithm here */
+ if (!NT_STATUS_IS_OK(pdb_init_sam_new(&sam_pass, user_name, 0))) {
+ slprintf(err_str, err_str_len-1, "Failed to initialise SAM_ACCOUNT for user %s.\n", user_name);
+ return False;
+ }
+ } else {
+ slprintf(err_str, err_str_len-1,"Failed to find entry for user %s.\n", user_name);
+ return False;
+ }
+ } else {
+ unbecome_root();
+ /* the entry already existed */
+ local_flags &= ~LOCAL_ADD_USER;
+ }
+
+ /* the 'other' acb bits not being changed here */
+ other_acb = (pdb_get_acct_ctrl(sam_pass) & (!(ACB_WSTRUST|ACB_DOMTRUST|ACB_SVRTRUST|ACB_NORMAL)));
+ if (local_flags & LOCAL_TRUST_ACCOUNT) {
+ if (!pdb_set_acct_ctrl(sam_pass, ACB_WSTRUST | other_acb, PDB_CHANGED) ) {
+ slprintf(err_str, err_str_len - 1, "Failed to set 'trusted workstation account' flags for user %s.\n", user_name);
+ pdb_free_sam(&sam_pass);
+ return False;
+ }
+ } else if (local_flags & LOCAL_INTERDOM_ACCOUNT) {
+ if (!pdb_set_acct_ctrl(sam_pass, ACB_DOMTRUST | other_acb, PDB_CHANGED)) {
+ slprintf(err_str, err_str_len - 1, "Failed to set 'domain trust account' flags for user %s.\n", user_name);
+ pdb_free_sam(&sam_pass);
+ return False;
+ }
+ } else {
+ if (!pdb_set_acct_ctrl(sam_pass, ACB_NORMAL | other_acb, PDB_CHANGED)) {
+ slprintf(err_str, err_str_len - 1, "Failed to set 'normal account' flags for user %s.\n", user_name);
+ pdb_free_sam(&sam_pass);
+ return False;
+ }
+ }
+
+ /*
+ * We are root - just write the new password
+ * and the valid last change time.
+ */
+
+ if (local_flags & LOCAL_DISABLE_USER) {
+ if (!pdb_set_acct_ctrl (sam_pass, pdb_get_acct_ctrl(sam_pass)|ACB_DISABLED, PDB_CHANGED)) {
+ slprintf(err_str, err_str_len-1, "Failed to set 'disabled' flag for user %s.\n", user_name);
+ pdb_free_sam(&sam_pass);
+ return False;
+ }
+ } else if (local_flags & LOCAL_ENABLE_USER) {
+ if (!pdb_set_acct_ctrl (sam_pass, pdb_get_acct_ctrl(sam_pass)&(~ACB_DISABLED), PDB_CHANGED)) {
+ slprintf(err_str, err_str_len-1, "Failed to unset 'disabled' flag for user %s.\n", user_name);
+ pdb_free_sam(&sam_pass);
+ return False;
+ }
+ }
+
+ if (local_flags & LOCAL_SET_NO_PASSWORD) {
+ if (!pdb_set_acct_ctrl (sam_pass, pdb_get_acct_ctrl(sam_pass)|ACB_PWNOTREQ, PDB_CHANGED)) {
+ slprintf(err_str, err_str_len-1, "Failed to set 'no password required' flag for user %s.\n", user_name);
+ pdb_free_sam(&sam_pass);
+ return False;
+ }
+ } else if (local_flags & LOCAL_SET_PASSWORD) {
+ /*
+ * If we're dealing with setting a completely empty user account
+ * ie. One with a password of 'XXXX', but not set disabled (like
+ * an account created from scratch) then if the old password was
+ * 'XX's then getsmbpwent will have set the ACB_DISABLED flag.
+ * We remove that as we're giving this user their first password
+ * and the decision hasn't really been made to disable them (ie.
+ * don't create them disabled). JRA.
+ */
+ if ((pdb_get_lanman_passwd(sam_pass)==NULL) && (pdb_get_acct_ctrl(sam_pass)&ACB_DISABLED)) {
+ if (!pdb_set_acct_ctrl (sam_pass, pdb_get_acct_ctrl(sam_pass)&(~ACB_DISABLED), PDB_CHANGED)) {
+ slprintf(err_str, err_str_len-1, "Failed to unset 'disabled' flag for user %s.\n", user_name);
+ pdb_free_sam(&sam_pass);
+ return False;
+ }
+ }
+ if (!pdb_set_acct_ctrl (sam_pass, pdb_get_acct_ctrl(sam_pass)&(~ACB_PWNOTREQ), PDB_CHANGED)) {
+ slprintf(err_str, err_str_len-1, "Failed to unset 'no password required' flag for user %s.\n", user_name);
+ pdb_free_sam(&sam_pass);
+ return False;
+ }
+
+ if (!pdb_set_plaintext_passwd (sam_pass, new_passwd)) {
+ slprintf(err_str, err_str_len-1, "Failed to set password for user %s.\n", user_name);
+ pdb_free_sam(&sam_pass);
+ return False;
+ }
+ }
+
+ if (local_flags & LOCAL_ADD_USER) {
+ if (pdb_add_sam_account(sam_pass)) {
+ slprintf(msg_str, msg_str_len-1, "Added user %s.\n", user_name);
+ pdb_free_sam(&sam_pass);
+ return True;
+ } else {
+ slprintf(err_str, err_str_len-1, "Failed to add entry for user %s.\n", user_name);
+ pdb_free_sam(&sam_pass);
+ return False;
+ }
+ } else if (local_flags & LOCAL_DELETE_USER) {
+ if (!pdb_delete_sam_account(sam_pass)) {
+ slprintf(err_str,err_str_len-1, "Failed to delete entry for user %s.\n", user_name);
+ pdb_free_sam(&sam_pass);
+ return False;
+ }
+ slprintf(msg_str, msg_str_len-1, "Deleted user %s.\n", user_name);
+ } else {
+ if(!pdb_update_sam_account(sam_pass)) {
+ slprintf(err_str, err_str_len-1, "Failed to modify entry for user %s.\n", user_name);
+ pdb_free_sam(&sam_pass);
+ return False;
+ }
+ if(local_flags & LOCAL_DISABLE_USER)
+ slprintf(msg_str, msg_str_len-1, "Disabled user %s.\n", user_name);
+ else if (local_flags & LOCAL_ENABLE_USER)
+ slprintf(msg_str, msg_str_len-1, "Enabled user %s.\n", user_name);
+ else if (local_flags & LOCAL_SET_NO_PASSWORD)
+ slprintf(msg_str, msg_str_len-1, "User %s password set to none.\n", user_name);
+ }
+
+ pdb_free_sam(&sam_pass);
+ return True;
+}
+
+/****************************************************************************
+ Convert a uid to SID - algorithmic.
+****************************************************************************/
+
+DOM_SID *algorithmic_uid_to_sid(DOM_SID *psid, uid_t uid)
+{
+ if ( !lp_enable_rid_algorithm() )
+ return NULL;
+
+ DEBUG(8,("algorithmic_uid_to_sid: falling back to RID algorithm\n"));
+ sid_copy( psid, get_global_sam_sid() );
+ sid_append_rid( psid, fallback_pdb_uid_to_user_rid(uid) );
+ DEBUG(10,("algorithmic_uid_to_sid: uid (%d) -> SID %s.\n",
+ (unsigned int)uid, sid_string_static(psid) ));
+
+ return psid;
+}
+
+/****************************************************************************
+ Convert a uid to SID - locally.
+****************************************************************************/
+
+DOM_SID *local_uid_to_sid(DOM_SID *psid, uid_t uid)
+{
+ SAM_ACCOUNT *sampw = NULL;
+ struct passwd *unix_pw;
+ BOOL ret;
+
+ unix_pw = sys_getpwuid( uid );
+
+ if ( !unix_pw ) {
+ DEBUG(4,("local_uid_to_sid: host has no idea of uid %lu\n", (unsigned long)uid));
+ return algorithmic_uid_to_sid( psid, uid);
+ }
+
+ if ( !NT_STATUS_IS_OK(pdb_init_sam(&sampw)) ) {
+ DEBUG(0,("local_uid_to_sid: failed to allocate SAM_ACCOUNT object\n"));
+ return NULL;
+ }
+
+ become_root();
+ ret = pdb_getsampwnam( sampw, unix_pw->pw_name );
+ unbecome_root();
+
+ if ( ret )
+ sid_copy( psid, pdb_get_user_sid(sampw) );
+ else {
+ DEBUG(4,("local_uid_to_sid: User %s [uid == %lu] has no samba account\n",
+ unix_pw->pw_name, (unsigned long)uid));
+
+ return algorithmic_uid_to_sid( psid, uid);
+ }
+
+ DEBUG(10,("local_uid_to_sid: uid (%d) -> SID %s (%s).\n",
+ (unsigned int)uid, sid_string_static(psid), unix_pw->pw_name));
+
+ return psid;
+}
+
+/****************************************************************************
+ Convert a SID to uid - locally.
+****************************************************************************/
+
+BOOL local_sid_to_uid(uid_t *puid, const DOM_SID *psid, enum SID_NAME_USE *name_type)
+{
+ SAM_ACCOUNT *sampw = NULL;
+ struct passwd *unix_pw;
+ const char *user_name;
+
+ *name_type = SID_NAME_UNKNOWN;
+
+ /*
+ * We can only convert to a uid if this is our local
+ * Domain SID (ie. we are the controling authority).
+ */
+ if (!sid_check_is_in_our_domain(psid) ) {
+ DEBUG(5,("local_sid_to_uid: this SID (%s) is not from our domain\n", sid_string_static(psid)));
+ return False;
+ }
+
+ /* lookup the user account */
+
+ if ( !NT_STATUS_IS_OK(pdb_init_sam(&sampw)) ) {
+ DEBUG(0,("local_sid_to_uid: Failed to allocate memory for SAM_ACCOUNT object\n"));
+ return False;
+ }
+
+ become_root();
+ if ( !pdb_getsampwsid(sampw, psid) ) {
+ unbecome_root();
+ DEBUG(8,("local_sid_to_uid: Could not find SID %s in passdb\n",
+ sid_string_static(psid)));
+ return False;
+ }
+ unbecome_root();
+
+ user_name = pdb_get_username(sampw);
+
+ unix_pw = sys_getpwnam( user_name );
+
+ if ( !unix_pw ) {
+ DEBUG(0,("local_sid_to_uid: %s found in passdb but getpwnam() return NULL!\n",
+ user_name));
+ pdb_free_sam( &sampw );
+ return False;
+ }
+
+ *puid = unix_pw->pw_uid;
+
+ DEBUG(10,("local_sid_to_uid: SID %s -> uid (%u) (%s).\n", sid_string_static(psid),
+ (unsigned int)*puid, user_name ));
+
+ *name_type = SID_NAME_USER;
+
+ return True;
+}
+
+/****************************************************************************
+ Convert a gid to SID - locally.
+****************************************************************************/
+
+DOM_SID *local_gid_to_sid(DOM_SID *psid, gid_t gid)
+{
+ GROUP_MAP group;
+ BOOL ret;
+
+ /* we don't need to disable winbindd since the gid is stored in
+ the GROUP_MAP object */
+
+ /* done as root since ldap backend requires root to open a connection */
+
+ become_root();
+ ret = pdb_getgrgid( &group, gid );
+ unbecome_root();
+
+ if ( !ret ) {
+
+ /* fallback to rid mapping if enabled */
+
+ if ( lp_enable_rid_algorithm() ) {
+ sid_copy(psid, get_global_sam_sid());
+ sid_append_rid(psid, pdb_gid_to_group_rid(gid));
+
+ DEBUG(10,("local_gid_to_sid: Fall back to algorithmic mapping: %u -> %s\n",
+ (unsigned int)gid, sid_string_static(psid)));
+
+ return psid;
+ }
+ else
+ return NULL;
+ }
+
+ sid_copy( psid, &group.sid );
+
+ DEBUG(10,("local_gid_to_sid: gid (%d) -> SID %s.\n",
+ (unsigned int)gid, sid_string_static(psid)));
+
+ return psid;
+}
+
+/****************************************************************************
+ Convert a SID to gid - locally.
+****************************************************************************/
+
+BOOL local_sid_to_gid(gid_t *pgid, const DOM_SID *psid, enum SID_NAME_USE *name_type)
+{
+ uint32 rid;
+ GROUP_MAP group;
+ BOOL ret;
+
+ *name_type = SID_NAME_UNKNOWN;
+
+ /* This call can enumerate group mappings for foreign sids as well.
+ So don't check for a match against our domain SID */
+
+ /* we don't need to disable winbindd since the gid is stored in
+ the GROUP_MAP object */
+
+ become_root();
+ ret = pdb_getgrsid(&group, *psid);
+ unbecome_root();
+
+ if ( !ret ) {
+
+ /* fallback to rid mapping if enabled */
+
+ if ( lp_enable_rid_algorithm() ) {
+
+ if (!sid_check_is_in_our_domain(psid) ) {
+ DEBUG(5,("local_sid_to_gid: RID algorithm only supported for our domain (%s is not)\n", sid_string_static(psid)));
+ return False;
+ }
+
+ if (!sid_peek_rid(psid, &rid)) {
+ DEBUG(10,("local_sid_to_uid: invalid SID!\n"));
+ return False;
+ }
+
+ DEBUG(10,("local_sid_to_gid: Fall back to algorithmic mapping\n"));
+
+ if (fallback_pdb_rid_is_user(rid)) {
+ DEBUG(3, ("local_sid_to_gid: SID %s is *NOT* a group\n", sid_string_static(psid)));
+ return False;
+ } else {
+ *pgid = pdb_group_rid_to_gid(rid);
+ DEBUG(10,("local_sid_to_gid: mapping: %s -> %u\n", sid_string_static(psid), (unsigned int)(*pgid)));
+ return True;
+ }
+ }
+
+ return False;
+ }
+
+ *pgid = group.gid;
+
+ DEBUG(10,("local_sid_to_gid: SID %s -> gid (%u)\n", sid_string_static(psid),
+ (unsigned int)*pgid));
+
+ return True;
+}
+
+/**********************************************************************
+ Marshall/unmarshall SAM_ACCOUNT structs.
+ *********************************************************************/
+
+#define TDB_FORMAT_STRING_V0 "ddddddBBBBBBBBBBBBddBBwdwdBwwd"
+#define TDB_FORMAT_STRING_V1 "dddddddBBBBBBBBBBBBddBBwdwdBwwd"
+
+/**********************************************************************
+ Intialize a SAM_ACCOUNT struct from a BYTE buffer of size len
+ *********************************************************************/
+
+BOOL init_sam_from_buffer(SAM_ACCOUNT *sampass, uint8 *buf, uint32 buflen)
+{
+ return(init_sam_from_buffer_v1(sampass, buf, buflen));
+}
+
+/**********************************************************************
+ Intialize a BYTE buffer from a SAM_ACCOUNT struct
+ *********************************************************************/
+
+uint32 init_buffer_from_sam (uint8 **buf, const SAM_ACCOUNT *sampass, BOOL size_only)
+{
+ return(init_buffer_from_sam_v1(buf, sampass, size_only));
+}
+
+
+BOOL init_sam_from_buffer_v0(SAM_ACCOUNT *sampass, uint8 *buf, uint32 buflen)
+{
+
+ /* times are stored as 32bit integer
+ take care on system with 64bit wide time_t
+ --SSS */
+ uint32 logon_time,
+ logoff_time,
+ kickoff_time,
+ pass_last_set_time,
+ pass_can_change_time,
+ pass_must_change_time;
+ char *username;
+ char *domain;
+ char *nt_username;
+ char *dir_drive;
+ char *unknown_str;
+ char *munged_dial;
+ char *fullname;
+ char *homedir;
+ char *logon_script;
+ char *profile_path;
+ char *acct_desc;
+ char *workstations;
+ uint32 username_len, domain_len, nt_username_len,
+ dir_drive_len, unknown_str_len, munged_dial_len,
+ fullname_len, homedir_len, logon_script_len,
+ profile_path_len, acct_desc_len, workstations_len;
+
+ uint32 user_rid, group_rid, remove_me, hours_len, unknown_6;
+ uint16 acct_ctrl, logon_divs;
+ uint16 bad_password_count, logon_count;
+ uint8 *hours;
+ static uint8 *lm_pw_ptr, *nt_pw_ptr;
+ uint32 len = 0;
+ uint32 lm_pw_len, nt_pw_len, hourslen;
+ BOOL ret = True;
+
+ if(sampass == NULL || buf == NULL) {
+ DEBUG(0, ("init_sam_from_buffer: NULL parameters found!\n"));
+ return False;
+ }
+
+ /* unpack the buffer into variables */
+ len = tdb_unpack ((char *)buf, buflen, TDB_FORMAT_STRING_V0,
+ &logon_time,
+ &logoff_time,
+ &kickoff_time,
+ &pass_last_set_time,
+ &pass_can_change_time,
+ &pass_must_change_time,
+ &username_len, &username,
+ &domain_len, &domain,
+ &nt_username_len, &nt_username,
+ &fullname_len, &fullname,
+ &homedir_len, &homedir,
+ &dir_drive_len, &dir_drive,
+ &logon_script_len, &logon_script,
+ &profile_path_len, &profile_path,
+ &acct_desc_len, &acct_desc,
+ &workstations_len, &workstations,
+ &unknown_str_len, &unknown_str,
+ &munged_dial_len, &munged_dial,
+ &user_rid,
+ &group_rid,
+ &lm_pw_len, &lm_pw_ptr,
+ &nt_pw_len, &nt_pw_ptr,
+ &acct_ctrl,
+ &remove_me, /* remove on the next TDB_FORMAT upgarde */
+ &logon_divs,
+ &hours_len,
+ &hourslen, &hours,
+ &bad_password_count,
+ &logon_count,
+ &unknown_6);
+
+ if (len == (uint32) -1) {
+ ret = False;
+ goto done;
+ }
+
+ pdb_set_logon_time(sampass, logon_time, PDB_SET);
+ pdb_set_logoff_time(sampass, logoff_time, PDB_SET);
+ pdb_set_kickoff_time(sampass, kickoff_time, PDB_SET);
+ pdb_set_pass_can_change_time(sampass, pass_can_change_time, PDB_SET);
+ pdb_set_pass_must_change_time(sampass, pass_must_change_time, PDB_SET);
+ pdb_set_pass_last_set_time(sampass, pass_last_set_time, PDB_SET);
+
+ pdb_set_username(sampass, username, PDB_SET);
+ pdb_set_domain(sampass, domain, PDB_SET);
+ pdb_set_nt_username(sampass, nt_username, PDB_SET);
+ pdb_set_fullname(sampass, fullname, PDB_SET);
+
+ if (homedir) {
+ pdb_set_homedir(sampass, homedir, PDB_SET);
+ }
+ else {
+ pdb_set_homedir(sampass,
+ talloc_sub_basic(sampass->mem_ctx, username, lp_logon_home()),
+ PDB_DEFAULT);
+ }
+
+ if (dir_drive)
+ pdb_set_dir_drive(sampass, dir_drive, PDB_SET);
+ else {
+ pdb_set_dir_drive(sampass,
+ talloc_sub_basic(sampass->mem_ctx, username, lp_logon_drive()),
+ PDB_DEFAULT);
+ }
+
+ if (logon_script)
+ pdb_set_logon_script(sampass, logon_script, PDB_SET);
+ else {
+ pdb_set_logon_script(sampass,
+ talloc_sub_basic(sampass->mem_ctx, username, lp_logon_script()),
+ PDB_DEFAULT);
+ }
+
+ if (profile_path) {
+ pdb_set_profile_path(sampass, profile_path, PDB_SET);
+ } else {
+ pdb_set_profile_path(sampass,
+ talloc_sub_basic(sampass->mem_ctx, username, lp_logon_path()),
+ PDB_DEFAULT);
+ }
+
+ pdb_set_acct_desc(sampass, acct_desc, PDB_SET);
+ pdb_set_workstations(sampass, workstations, PDB_SET);
+ pdb_set_munged_dial(sampass, munged_dial, PDB_SET);
+
+ if (lm_pw_ptr && lm_pw_len == LM_HASH_LEN) {
+ if (!pdb_set_lanman_passwd(sampass, lm_pw_ptr, PDB_SET)) {
+ ret = False;
+ goto done;
+ }
+ }
+
+ if (nt_pw_ptr && nt_pw_len == NT_HASH_LEN) {
+ if (!pdb_set_nt_passwd(sampass, nt_pw_ptr, PDB_SET)) {
+ ret = False;
+ goto done;
+ }
+ }
+
+ pdb_set_user_sid_from_rid(sampass, user_rid, PDB_SET);
+ pdb_set_group_sid_from_rid(sampass, group_rid, PDB_SET);
+ pdb_set_hours_len(sampass, hours_len, PDB_SET);
+ pdb_set_bad_password_count(sampass, bad_password_count, PDB_SET);
+ pdb_set_logon_count(sampass, logon_count, PDB_SET);
+ pdb_set_unknown_6(sampass, unknown_6, PDB_SET);
+ pdb_set_acct_ctrl(sampass, acct_ctrl, PDB_SET);
+ pdb_set_logon_divs(sampass, logon_divs, PDB_SET);
+ pdb_set_hours(sampass, hours, PDB_SET);
+
+done:
+
+ SAFE_FREE(username);
+ SAFE_FREE(domain);
+ SAFE_FREE(nt_username);
+ SAFE_FREE(fullname);
+ SAFE_FREE(homedir);
+ SAFE_FREE(dir_drive);
+ SAFE_FREE(logon_script);
+ SAFE_FREE(profile_path);
+ SAFE_FREE(acct_desc);
+ SAFE_FREE(workstations);
+ SAFE_FREE(munged_dial);
+ SAFE_FREE(unknown_str);
+ SAFE_FREE(hours);
+
+ return ret;
+}
+
+
+uint32 init_buffer_from_sam_v0 (uint8 **buf, const SAM_ACCOUNT *sampass, BOOL size_only)
+{
+ size_t len, buflen;
+
+ /* times are stored as 32bit integer
+ take care on system with 64bit wide time_t
+ --SSS */
+ uint32 logon_time,
+ logoff_time,
+ kickoff_time,
+ pass_last_set_time,
+ pass_can_change_time,
+ pass_must_change_time;
+
+ uint32 user_rid, group_rid;
+
+ const char *username;
+ const char *domain;
+ const char *nt_username;
+ const char *dir_drive;
+ const char *unknown_str;
+ const char *munged_dial;
+ const char *fullname;
+ const char *homedir;
+ const char *logon_script;
+ const char *profile_path;
+ const char *acct_desc;
+ const char *workstations;
+ uint32 username_len, domain_len, nt_username_len,
+ dir_drive_len, unknown_str_len, munged_dial_len,
+ fullname_len, homedir_len, logon_script_len,
+ profile_path_len, acct_desc_len, workstations_len;
+
+ const uint8 *lm_pw;
+ const uint8 *nt_pw;
+ uint32 lm_pw_len = 16;
+ uint32 nt_pw_len = 16;
+
+ /* do we have a valid SAM_ACCOUNT pointer? */
+ if (sampass == NULL) {
+ DEBUG(0, ("init_buffer_from_sam: SAM_ACCOUNT is NULL!\n"));
+ return -1;
+ }
+
+ *buf = NULL;
+ buflen = 0;
+
+ logon_time = (uint32)pdb_get_logon_time(sampass);
+ logoff_time = (uint32)pdb_get_logoff_time(sampass);
+ kickoff_time = (uint32)pdb_get_kickoff_time(sampass);
+ pass_can_change_time = (uint32)pdb_get_pass_can_change_time(sampass);
+ pass_must_change_time = (uint32)pdb_get_pass_must_change_time(sampass);
+ pass_last_set_time = (uint32)pdb_get_pass_last_set_time(sampass);
+
+ user_rid = pdb_get_user_rid(sampass);
+ group_rid = pdb_get_group_rid(sampass);
+
+ username = pdb_get_username(sampass);
+ if (username)
+ username_len = strlen(username) +1;
+ else
+ username_len = 0;
+
+ domain = pdb_get_domain(sampass);
+ if (domain)
+ domain_len = strlen(domain) +1;
+ else
+ domain_len = 0;
+
+ nt_username = pdb_get_nt_username(sampass);
+ if (nt_username)
+ nt_username_len = strlen(nt_username) +1;
+ else
+ nt_username_len = 0;
+
+ fullname = pdb_get_fullname(sampass);
+ if (fullname)
+ fullname_len = strlen(fullname) +1;
+ else
+ fullname_len = 0;
+
+ /*
+ * Only updates fields which have been set (not defaults from smb.conf)
+ */
+
+ if (!IS_SAM_DEFAULT(sampass, PDB_DRIVE))
+ dir_drive = pdb_get_dir_drive(sampass);
+ else
+ dir_drive = NULL;
+ if (dir_drive)
+ dir_drive_len = strlen(dir_drive) +1;
+ else
+ dir_drive_len = 0;
+
+ if (!IS_SAM_DEFAULT(sampass, PDB_SMBHOME))
+ homedir = pdb_get_homedir(sampass);
+ else
+ homedir = NULL;
+ if (homedir)
+ homedir_len = strlen(homedir) +1;
+ else
+ homedir_len = 0;
+
+ if (!IS_SAM_DEFAULT(sampass, PDB_LOGONSCRIPT))
+ logon_script = pdb_get_logon_script(sampass);
+ else
+ logon_script = NULL;
+ if (logon_script)
+ logon_script_len = strlen(logon_script) +1;
+ else
+ logon_script_len = 0;
+
+ if (!IS_SAM_DEFAULT(sampass, PDB_PROFILE))
+ profile_path = pdb_get_profile_path(sampass);
+ else
+ profile_path = NULL;
+ if (profile_path)
+ profile_path_len = strlen(profile_path) +1;
+ else
+ profile_path_len = 0;
+
+ lm_pw = pdb_get_lanman_passwd(sampass);
+ if (!lm_pw)
+ lm_pw_len = 0;
+
+ nt_pw = pdb_get_nt_passwd(sampass);
+ if (!nt_pw)
+ nt_pw_len = 0;
+
+ acct_desc = pdb_get_acct_desc(sampass);
+ if (acct_desc)
+ acct_desc_len = strlen(acct_desc) +1;
+ else
+ acct_desc_len = 0;
+
+ workstations = pdb_get_workstations(sampass);
+ if (workstations)
+ workstations_len = strlen(workstations) +1;
+ else
+ workstations_len = 0;
+
+ unknown_str = NULL;
+ unknown_str_len = 0;
+
+ munged_dial = pdb_get_munged_dial(sampass);
+ if (munged_dial)
+ munged_dial_len = strlen(munged_dial) +1;
+ else
+ munged_dial_len = 0;
+
+ /* one time to get the size needed */
+ len = tdb_pack(NULL, 0, TDB_FORMAT_STRING_V0,
+ logon_time,
+ logoff_time,
+ kickoff_time,
+ pass_last_set_time,
+ pass_can_change_time,
+ pass_must_change_time,
+ username_len, username,
+ domain_len, domain,
+ nt_username_len, nt_username,
+ fullname_len, fullname,
+ homedir_len, homedir,
+ dir_drive_len, dir_drive,
+ logon_script_len, logon_script,
+ profile_path_len, profile_path,
+ acct_desc_len, acct_desc,
+ workstations_len, workstations,
+ unknown_str_len, unknown_str,
+ munged_dial_len, munged_dial,
+ user_rid,
+ group_rid,
+ lm_pw_len, lm_pw,
+ nt_pw_len, nt_pw,
+ pdb_get_acct_ctrl(sampass),
+ 0, /* was: fileds_present, to be removed on format change */
+ pdb_get_logon_divs(sampass),
+ pdb_get_hours_len(sampass),
+ MAX_HOURS_LEN, pdb_get_hours(sampass),
+ pdb_get_bad_password_count(sampass),
+ pdb_get_logon_count(sampass),
+ pdb_get_unknown_6(sampass));
+
+
+ if (size_only)
+ return buflen;
+
+ /* malloc the space needed */
+ if ( (*buf=(uint8*)malloc(len)) == NULL) {
+ DEBUG(0,("init_buffer_from_sam: Unable to malloc() memory for buffer!\n"));
+ return (-1);
+ }
+
+ /* now for the real call to tdb_pack() */
+ buflen = tdb_pack((char *)*buf, len, TDB_FORMAT_STRING_V0,
+ logon_time,
+ logoff_time,
+ kickoff_time,
+ pass_last_set_time,
+ pass_can_change_time,
+ pass_must_change_time,
+ username_len, username,
+ domain_len, domain,
+ nt_username_len, nt_username,
+ fullname_len, fullname,
+ homedir_len, homedir,
+ dir_drive_len, dir_drive,
+ logon_script_len, logon_script,
+ profile_path_len, profile_path,
+ acct_desc_len, acct_desc,
+ workstations_len, workstations,
+ unknown_str_len, unknown_str,
+ munged_dial_len, munged_dial,
+ user_rid,
+ group_rid,
+ lm_pw_len, lm_pw,
+ nt_pw_len, nt_pw,
+ pdb_get_acct_ctrl(sampass),
+ 0, /* was: fileds_present, to be removed on format change */
+ pdb_get_logon_divs(sampass),
+ pdb_get_hours_len(sampass),
+ MAX_HOURS_LEN, pdb_get_hours(sampass),
+ pdb_get_bad_password_count(sampass),
+ pdb_get_logon_count(sampass),
+ pdb_get_unknown_6(sampass));
+
+
+ /* check to make sure we got it correct */
+ if (buflen != len) {
+ DEBUG(0, ("init_buffer_from_sam: somthing odd is going on here: bufflen (%lu) != len (%lu) in tdb_pack operations!\n",
+ (unsigned long)buflen, (unsigned long)len));
+ /* error */
+ SAFE_FREE (*buf);
+ return (-1);
+ }
+
+ return (buflen);
+}
+
+
+BOOL init_sam_from_buffer_v1(SAM_ACCOUNT *sampass, uint8 *buf, uint32 buflen)
+{
+
+ /* times are stored as 32bit integer
+ take care on system with 64bit wide time_t
+ --SSS */
+ uint32 logon_time,
+ logoff_time,
+ kickoff_time,
+ bad_password_time,
+ pass_last_set_time,
+ pass_can_change_time,
+ pass_must_change_time;
+ char *username;
+ char *domain;
+ char *nt_username;
+ char *dir_drive;
+ char *unknown_str;
+ char *munged_dial;
+ char *fullname;
+ char *homedir;
+ char *logon_script;
+ char *profile_path;
+ char *acct_desc;
+ char *workstations;
+ uint32 username_len, domain_len, nt_username_len,
+ dir_drive_len, unknown_str_len, munged_dial_len,
+ fullname_len, homedir_len, logon_script_len,
+ profile_path_len, acct_desc_len, workstations_len;
+
+ uint32 user_rid, group_rid, remove_me, hours_len, unknown_6;
+ uint16 acct_ctrl, logon_divs;
+ uint16 bad_password_count, logon_count;
+ uint8 *hours;
+ static uint8 *lm_pw_ptr, *nt_pw_ptr;
+ uint32 len = 0;
+ uint32 lm_pw_len, nt_pw_len, hourslen;
+ BOOL ret = True;
+
+ if(sampass == NULL || buf == NULL) {
+ DEBUG(0, ("init_sam_from_buffer: NULL parameters found!\n"));
+ return False;
+ }
+
+ /* unpack the buffer into variables */
+ len = tdb_unpack ((char *)buf, buflen, TDB_FORMAT_STRING_V1,
+ &logon_time,
+ &logoff_time,
+ &kickoff_time,
+ &bad_password_time,
+ &pass_last_set_time,
+ &pass_can_change_time,
+ &pass_must_change_time,
+ &username_len, &username,
+ &domain_len, &domain,
+ &nt_username_len, &nt_username,
+ &fullname_len, &fullname,
+ &homedir_len, &homedir,
+ &dir_drive_len, &dir_drive,
+ &logon_script_len, &logon_script,
+ &profile_path_len, &profile_path,
+ &acct_desc_len, &acct_desc,
+ &workstations_len, &workstations,
+ &unknown_str_len, &unknown_str,
+ &munged_dial_len, &munged_dial,
+ &user_rid,
+ &group_rid,
+ &lm_pw_len, &lm_pw_ptr,
+ &nt_pw_len, &nt_pw_ptr,
+ &acct_ctrl,
+ &remove_me,
+ &logon_divs,
+ &hours_len,
+ &hourslen, &hours,
+ &bad_password_count,
+ &logon_count,
+ &unknown_6);
+
+ if (len == (uint32) -1) {
+ ret = False;
+ goto done;
+ }
+
+ pdb_set_logon_time(sampass, logon_time, PDB_SET);
+ pdb_set_logoff_time(sampass, logoff_time, PDB_SET);
+ pdb_set_kickoff_time(sampass, kickoff_time, PDB_SET);
+ pdb_set_bad_password_time(sampass, bad_password_time, PDB_SET);
+ pdb_set_pass_can_change_time(sampass, pass_can_change_time, PDB_SET);
+ pdb_set_pass_must_change_time(sampass, pass_must_change_time, PDB_SET);
+ pdb_set_pass_last_set_time(sampass, pass_last_set_time, PDB_SET);
+
+ pdb_set_username(sampass, username, PDB_SET);
+ pdb_set_domain(sampass, domain, PDB_SET);
+ pdb_set_nt_username(sampass, nt_username, PDB_SET);
+ pdb_set_fullname(sampass, fullname, PDB_SET);
+
+ if (homedir) {
+ pdb_set_homedir(sampass, homedir, PDB_SET);
+ }
+ else {
+ pdb_set_homedir(sampass,
+ talloc_sub_basic(sampass->mem_ctx, username, lp_logon_home()),
+ PDB_DEFAULT);
+ }
+
+ if (dir_drive)
+ pdb_set_dir_drive(sampass, dir_drive, PDB_SET);
+ else {
+ pdb_set_dir_drive(sampass,
+ talloc_sub_basic(sampass->mem_ctx, username, lp_logon_drive()),
+ PDB_DEFAULT);
+ }
+
+ if (logon_script)
+ pdb_set_logon_script(sampass, logon_script, PDB_SET);
+ else {
+ pdb_set_logon_script(sampass,
+ talloc_sub_basic(sampass->mem_ctx, username, lp_logon_script()),
+ PDB_DEFAULT);
+ }
+
+ if (profile_path) {
+ pdb_set_profile_path(sampass, profile_path, PDB_SET);
+ } else {
+ pdb_set_profile_path(sampass,
+ talloc_sub_basic(sampass->mem_ctx, username, lp_logon_path()),
+ PDB_DEFAULT);
+ }
+
+ pdb_set_acct_desc(sampass, acct_desc, PDB_SET);
+ pdb_set_workstations(sampass, workstations, PDB_SET);
+ pdb_set_munged_dial(sampass, munged_dial, PDB_SET);
+
+ if (lm_pw_ptr && lm_pw_len == LM_HASH_LEN) {
+ if (!pdb_set_lanman_passwd(sampass, lm_pw_ptr, PDB_SET)) {
+ ret = False;
+ goto done;
+ }
+ }
+
+ if (nt_pw_ptr && nt_pw_len == NT_HASH_LEN) {
+ if (!pdb_set_nt_passwd(sampass, nt_pw_ptr, PDB_SET)) {
+ ret = False;
+ goto done;
+ }
+ }
+
+ pdb_set_user_sid_from_rid(sampass, user_rid, PDB_SET);
+ pdb_set_group_sid_from_rid(sampass, group_rid, PDB_SET);
+ pdb_set_hours_len(sampass, hours_len, PDB_SET);
+ pdb_set_bad_password_count(sampass, bad_password_count, PDB_SET);
+ pdb_set_logon_count(sampass, logon_count, PDB_SET);
+ pdb_set_unknown_6(sampass, unknown_6, PDB_SET);
+ pdb_set_acct_ctrl(sampass, acct_ctrl, PDB_SET);
+ pdb_set_logon_divs(sampass, logon_divs, PDB_SET);
+ pdb_set_hours(sampass, hours, PDB_SET);
+
+done:
+
+ SAFE_FREE(lm_pw_ptr);
+ SAFE_FREE(nt_pw_ptr);
+ SAFE_FREE(username);
+ SAFE_FREE(domain);
+ SAFE_FREE(nt_username);
+ SAFE_FREE(fullname);
+ SAFE_FREE(homedir);
+ SAFE_FREE(dir_drive);
+ SAFE_FREE(logon_script);
+ SAFE_FREE(profile_path);
+ SAFE_FREE(acct_desc);
+ SAFE_FREE(workstations);
+ SAFE_FREE(munged_dial);
+ SAFE_FREE(unknown_str);
+ SAFE_FREE(hours);
+
+ return ret;
+}
+
+
+uint32 init_buffer_from_sam_v1 (uint8 **buf, const SAM_ACCOUNT *sampass, BOOL size_only)
+{
+ size_t len, buflen;
+
+ /* times are stored as 32bit integer
+ take care on system with 64bit wide time_t
+ --SSS */
+ uint32 logon_time,
+ logoff_time,
+ kickoff_time,
+ bad_password_time,
+ pass_last_set_time,
+ pass_can_change_time,
+ pass_must_change_time;
+
+ uint32 user_rid, group_rid;
+
+ const char *username;
+ const char *domain;
+ const char *nt_username;
+ const char *dir_drive;
+ const char *unknown_str;
+ const char *munged_dial;
+ const char *fullname;
+ const char *homedir;
+ const char *logon_script;
+ const char *profile_path;
+ const char *acct_desc;
+ const char *workstations;
+ uint32 username_len, domain_len, nt_username_len,
+ dir_drive_len, unknown_str_len, munged_dial_len,
+ fullname_len, homedir_len, logon_script_len,
+ profile_path_len, acct_desc_len, workstations_len;
+
+ const uint8 *lm_pw;
+ const uint8 *nt_pw;
+ uint32 lm_pw_len = 16;
+ uint32 nt_pw_len = 16;
+
+ /* do we have a valid SAM_ACCOUNT pointer? */
+ if (sampass == NULL) {
+ DEBUG(0, ("init_buffer_from_sam: SAM_ACCOUNT is NULL!\n"));
+ return -1;
+ }
+
+ *buf = NULL;
+ buflen = 0;
+
+ logon_time = (uint32)pdb_get_logon_time(sampass);
+ logoff_time = (uint32)pdb_get_logoff_time(sampass);
+ kickoff_time = (uint32)pdb_get_kickoff_time(sampass);
+ bad_password_time = (uint32)pdb_get_bad_password_time(sampass);
+ pass_can_change_time = (uint32)pdb_get_pass_can_change_time(sampass);
+ pass_must_change_time = (uint32)pdb_get_pass_must_change_time(sampass);
+ pass_last_set_time = (uint32)pdb_get_pass_last_set_time(sampass);
+
+ user_rid = pdb_get_user_rid(sampass);
+ group_rid = pdb_get_group_rid(sampass);
+
+ username = pdb_get_username(sampass);
+ if (username)
+ username_len = strlen(username) +1;
+ else
+ username_len = 0;
+
+ domain = pdb_get_domain(sampass);
+ if (domain)
+ domain_len = strlen(domain) +1;
+ else
+ domain_len = 0;
+
+ nt_username = pdb_get_nt_username(sampass);
+ if (nt_username)
+ nt_username_len = strlen(nt_username) +1;
+ else
+ nt_username_len = 0;
+
+ fullname = pdb_get_fullname(sampass);
+ if (fullname)
+ fullname_len = strlen(fullname) +1;
+ else
+ fullname_len = 0;
+
+ /*
+ * Only updates fields which have been set (not defaults from smb.conf)
+ */
+
+ if (!IS_SAM_DEFAULT(sampass, PDB_DRIVE))
+ dir_drive = pdb_get_dir_drive(sampass);
+ else
+ dir_drive = NULL;
+ if (dir_drive)
+ dir_drive_len = strlen(dir_drive) +1;
+ else
+ dir_drive_len = 0;
+
+ if (!IS_SAM_DEFAULT(sampass, PDB_SMBHOME))
+ homedir = pdb_get_homedir(sampass);
+ else
+ homedir = NULL;
+ if (homedir)
+ homedir_len = strlen(homedir) +1;
+ else
+ homedir_len = 0;
+
+ if (!IS_SAM_DEFAULT(sampass, PDB_LOGONSCRIPT))
+ logon_script = pdb_get_logon_script(sampass);
+ else
+ logon_script = NULL;
+ if (logon_script)
+ logon_script_len = strlen(logon_script) +1;
+ else
+ logon_script_len = 0;
+
+ if (!IS_SAM_DEFAULT(sampass, PDB_PROFILE))
+ profile_path = pdb_get_profile_path(sampass);
+ else
+ profile_path = NULL;
+ if (profile_path)
+ profile_path_len = strlen(profile_path) +1;
+ else
+ profile_path_len = 0;
+
+ lm_pw = pdb_get_lanman_passwd(sampass);
+ if (!lm_pw)
+ lm_pw_len = 0;
+
+ nt_pw = pdb_get_nt_passwd(sampass);
+ if (!nt_pw)
+ nt_pw_len = 0;
+
+ acct_desc = pdb_get_acct_desc(sampass);
+ if (acct_desc)
+ acct_desc_len = strlen(acct_desc) +1;
+ else
+ acct_desc_len = 0;
+
+ workstations = pdb_get_workstations(sampass);
+ if (workstations)
+ workstations_len = strlen(workstations) +1;
+ else
+ workstations_len = 0;
+
+ unknown_str = NULL;
+ unknown_str_len = 0;
+
+ munged_dial = pdb_get_munged_dial(sampass);
+ if (munged_dial)
+ munged_dial_len = strlen(munged_dial) +1;
+ else
+ munged_dial_len = 0;
+
+ /* one time to get the size needed */
+ len = tdb_pack(NULL, 0, TDB_FORMAT_STRING_V1,
+ logon_time,
+ logoff_time,
+ kickoff_time,
+ bad_password_time,
+ pass_last_set_time,
+ pass_can_change_time,
+ pass_must_change_time,
+ username_len, username,
+ domain_len, domain,
+ nt_username_len, nt_username,
+ fullname_len, fullname,
+ homedir_len, homedir,
+ dir_drive_len, dir_drive,
+ logon_script_len, logon_script,
+ profile_path_len, profile_path,
+ acct_desc_len, acct_desc,
+ workstations_len, workstations,
+ unknown_str_len, unknown_str,
+ munged_dial_len, munged_dial,
+ user_rid,
+ group_rid,
+ lm_pw_len, lm_pw,
+ nt_pw_len, nt_pw,
+ pdb_get_acct_ctrl(sampass),
+ 0,
+ pdb_get_logon_divs(sampass),
+ pdb_get_hours_len(sampass),
+ MAX_HOURS_LEN, pdb_get_hours(sampass),
+ pdb_get_bad_password_count(sampass),
+ pdb_get_logon_count(sampass),
+ pdb_get_unknown_6(sampass));
+
+
+ if (size_only)
+ return buflen;
+
+ /* malloc the space needed */
+ if ( (*buf=(uint8*)malloc(len)) == NULL) {
+ DEBUG(0,("init_buffer_from_sam: Unable to malloc() memory for buffer!\n"));
+ return (-1);
+ }
+
+ /* now for the real call to tdb_pack() */
+ buflen = tdb_pack((char *)*buf, len, TDB_FORMAT_STRING_V1,
+ logon_time,
+ logoff_time,
+ kickoff_time,
+ bad_password_time,
+ pass_last_set_time,
+ pass_can_change_time,
+ pass_must_change_time,
+ username_len, username,
+ domain_len, domain,
+ nt_username_len, nt_username,
+ fullname_len, fullname,
+ homedir_len, homedir,
+ dir_drive_len, dir_drive,
+ logon_script_len, logon_script,
+ profile_path_len, profile_path,
+ acct_desc_len, acct_desc,
+ workstations_len, workstations,
+ unknown_str_len, unknown_str,
+ munged_dial_len, munged_dial,
+ user_rid,
+ group_rid,
+ lm_pw_len, lm_pw,
+ nt_pw_len, nt_pw,
+ pdb_get_acct_ctrl(sampass),
+ 0,
+ pdb_get_logon_divs(sampass),
+ pdb_get_hours_len(sampass),
+ MAX_HOURS_LEN, pdb_get_hours(sampass),
+ pdb_get_bad_password_count(sampass),
+ pdb_get_logon_count(sampass),
+ pdb_get_unknown_6(sampass));
+
+
+ /* check to make sure we got it correct */
+ if (buflen != len) {
+ DEBUG(0, ("init_buffer_from_sam: somthing odd is going on here: bufflen (%lu) != len (%lu) in tdb_pack operations!\n",
+ (unsigned long)buflen, (unsigned long)len));
+ /* error */
+ SAFE_FREE (*buf);
+ return (-1);
+ }
+
+ return (buflen);
+}
+
+
+/**********************************************************************
+**********************************************************************/
+
+static BOOL get_free_ugid_range(uint32 *low, uint32 *high)
+{
+ uid_t u_low, u_high;
+ gid_t g_low, g_high;
+
+ if (!lp_idmap_uid(&u_low, &u_high) || !lp_idmap_gid(&g_low, &g_high)) {
+ return False;
+ }
+
+ *low = (u_low < g_low) ? u_low : g_low;
+ *high = (u_high < g_high) ? u_high : g_high;
+
+ return True;
+}
+
+/******************************************************************
+ Get the the non-algorithmic RID range if idmap range are defined
+******************************************************************/
+
+BOOL get_free_rid_range(uint32 *low, uint32 *high)
+{
+ uint32 id_low, id_high;
+
+ if (!lp_enable_rid_algorithm()) {
+ *low = BASE_RID;
+ *high = (uint32)-1;
+ }
+
+ if (!get_free_ugid_range(&id_low, &id_high)) {
+ return False;
+ }
+
+ *low = fallback_pdb_uid_to_user_rid(id_low);
+ if (fallback_pdb_user_rid_to_uid((uint32)-1) < id_high) {
+ *high = (uint32)-1;
+ } else {
+ *high = fallback_pdb_uid_to_user_rid(id_high);
+ }
+
+ return True;
+}
+
+/*********************************************************************
+ Update the bad password count checking the AP_RESET_COUNT_TIME
+*********************************************************************/
+
+BOOL pdb_update_bad_password_count(SAM_ACCOUNT *sampass, BOOL *updated)
+{
+ time_t LastBadPassword;
+ uint16 BadPasswordCount;
+ uint32 resettime;
+
+ if (!sampass) return False;
+
+ BadPasswordCount = pdb_get_bad_password_count(sampass);
+ if (!BadPasswordCount) {
+ DEBUG(9, ("No bad password attempts.\n"));
+ return True;
+ }
+
+ if (!account_policy_get(AP_RESET_COUNT_TIME, &resettime)) {
+ DEBUG(0, ("pdb_update_bad_password_count: account_policy_get failed.\n"));
+ return False;
+ }
+
+ /* First, check if there is a reset time to compare */
+ if ((resettime == (uint32) -1) || (resettime == 0)) {
+ DEBUG(9, ("No reset time, can't reset bad pw count\n"));
+ return True;
+ }
+
+ LastBadPassword = pdb_get_bad_password_time(sampass);
+ DEBUG(7, ("LastBadPassword=%d, resettime=%d, current time=%d.\n",
+ (uint32) LastBadPassword, resettime, (uint32)time(NULL)));
+ if (time(NULL) > (LastBadPassword + (time_t)resettime*60)){
+ pdb_set_bad_password_count(sampass, 0, PDB_CHANGED);
+ pdb_set_bad_password_time(sampass, 0, PDB_CHANGED);
+ if (updated) *updated = True;
+ }
+
+ return True;
+}
+
+/*********************************************************************
+ Update the ACB_AUTOLOCK flag checking the AP_LOCK_ACCOUNT_DURATION
+*********************************************************************/
+
+BOOL pdb_update_autolock_flag(SAM_ACCOUNT *sampass, BOOL *updated)
+{
+ uint32 duration;
+ time_t LastBadPassword;
+
+ if (!sampass) return False;
+
+ if (!(pdb_get_acct_ctrl(sampass) & ACB_AUTOLOCK)) {
+ DEBUG(9, ("Account not autolocked, no check needed\n"));
+ return True;
+ }
+
+ if (!account_policy_get(AP_LOCK_ACCOUNT_DURATION, &duration)) {
+ DEBUG(0, ("pdb_update_autolock_flag: account_policy_get failed.\n"));
+ return False;
+ }
+
+ /* First, check if there is a duration to compare */
+ if ((duration == (uint32) -1) || (duration == 0)) {
+ DEBUG(9, ("No reset duration, can't reset autolock\n"));
+ return True;
+ }
+
+ LastBadPassword = pdb_get_bad_password_time(sampass);
+ DEBUG(7, ("LastBadPassword=%d, duration=%d, current time =%d.\n",
+ (uint32)LastBadPassword, duration*60, (uint32)time(NULL)));
+ if ((time(NULL) > (LastBadPassword + (time_t) duration * 60))) {
+ pdb_set_acct_ctrl(sampass,
+ pdb_get_acct_ctrl(sampass) & ~ACB_AUTOLOCK,
+ PDB_CHANGED);
+ pdb_set_bad_password_count(sampass, 0, PDB_CHANGED);
+ pdb_set_bad_password_time(sampass, 0, PDB_CHANGED);
+ if (updated) *updated = True;
+ }
+
+ return True;
+}
+
+/*********************************************************************
+ Increment the bad_password_count
+*********************************************************************/
+
+BOOL pdb_increment_bad_password_count(SAM_ACCOUNT *sampass)
+{
+ uint32 account_policy_lockout;
+ BOOL autolock_updated = False, badpw_updated = False;
+
+ if (!sampass)
+ return False;
+
+ /* Retrieve the account lockout policy */
+ if (!account_policy_get(AP_BAD_ATTEMPT_LOCKOUT,
+ &account_policy_lockout)) {
+ DEBUG(0, ("pdb_increment_bad_password_count: account_policy_get failed.\n"));
+ return False;
+ }
+
+ /* If there is no policy, we don't need to continue checking */
+ if (!account_policy_lockout) {
+ DEBUG(9, ("No lockout policy, don't track bad passwords\n"));
+ return True;
+ }
+
+ /* Check if the autolock needs to be cleared */
+ if (!pdb_update_autolock_flag(sampass, &autolock_updated))
+ return False;
+
+ /* Check if the badpw count needs to be reset */
+ if (!pdb_update_bad_password_count(sampass, &badpw_updated))
+ return False;
+
+ /*
+ Ok, now we can assume that any resetting that needs to be
+ done has been done, and just get on with incrementing
+ and autolocking if necessary
+ */
+
+ pdb_set_bad_password_count(sampass,
+ pdb_get_bad_password_count(sampass)+1,
+ PDB_CHANGED);
+ pdb_set_bad_password_time(sampass, time(NULL), PDB_CHANGED);
+
+
+ if (pdb_get_bad_password_count(sampass) < account_policy_lockout)
+ return True;
+
+ if (!pdb_set_acct_ctrl(sampass,
+ pdb_get_acct_ctrl(sampass) | ACB_AUTOLOCK,
+ PDB_CHANGED)) {
+ DEBUG(1, ("pdb_increment_bad_password_count:failed to set 'autolock' flag. \n"));
+ return False;
+ }
+
+ return True;
+}
+
+BOOL get_sids_from_priv(const char *privname, DOM_SID **sids, int *num)
+{
+ char *sids_string;
+ char *s;
+ fstring tok;
+
+ if (!pdb_get_privilege_entry(privname, &sids_string))
+ return False;
+
+ s = sids_string;
+
+ while (next_token(&s, tok, ",", sizeof(tok))) {
+ DOM_SID sid;
+ DEBUG(10, ("converting SID %s\n", tok));
+
+ if (!string_to_sid(&sid, tok)) {
+ DEBUG(3, ("Could not convert SID\n"));
+ continue;
+ }
+
+ add_sid_to_array(&sid, sids, num);
+ }
+
+ SAFE_FREE(sids_string);
+ return True;
+}
+
+BOOL get_priv_for_sid(const DOM_SID *sid, PRIVILEGE_SET *priv)
+{
+ extern PRIVS privs[];
+ int i;
+ for (i=1; i<PRIV_ALL_INDEX-1; i++) {
+ DOM_SID *sids;
+ int j, num;
+
+ if (!get_sids_from_priv(privs[i].priv, &sids, &num))
+ continue;
+
+ for (j=0; j<num; j++) {
+ if (sid_compare(sid, &sids[j]) == 0)
+ add_privilege_by_name(priv, privs[i].priv);
+ }
+ SAFE_FREE(sids);
+ }
+ return True;
+}
diff --git a/source/passdb/pdb_compat.c b/source/passdb/pdb_compat.c
new file mode 100644
index 00000000000..abd572a7c14
--- /dev/null
+++ b/source/passdb/pdb_compat.c
@@ -0,0 +1,104 @@
+/*
+ Unix SMB/CIFS implementation.
+ SAM_ACCOUNT access routines
+ Copyright (C) Jeremy Allison 1996-2001
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+ Copyright (C) Gerald (Jerry) Carter 2000-2001
+ Copyright (C) Andrew Bartlett 2001-2002
+ Copyright (C) Stefan (metze) Metzmacher 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+uint32 pdb_get_user_rid (const SAM_ACCOUNT *sampass)
+{
+ uint32 u_rid;
+
+ if (sampass)
+ if (sid_peek_check_rid(get_global_sam_sid(), pdb_get_user_sid(sampass),&u_rid))
+ return u_rid;
+
+ return (0);
+}
+
+uint32 pdb_get_group_rid (const SAM_ACCOUNT *sampass)
+{
+ uint32 g_rid;
+
+ if (sampass)
+ if (sid_peek_check_rid(get_global_sam_sid(), pdb_get_group_sid(sampass),&g_rid))
+ return g_rid;
+ return (0);
+}
+
+BOOL pdb_set_user_sid_from_rid (SAM_ACCOUNT *sampass, uint32 rid, enum pdb_value_state flag)
+{
+ DOM_SID u_sid;
+ const DOM_SID *global_sam_sid;
+
+ if (!sampass)
+ return False;
+
+ if (!(global_sam_sid = get_global_sam_sid())) {
+ DEBUG(1, ("pdb_set_user_sid_from_rid: Could not read global sam sid!\n"));
+ return False;
+ }
+
+ sid_copy(&u_sid, global_sam_sid);
+
+ if (!sid_append_rid(&u_sid, rid))
+ return False;
+
+ if (!pdb_set_user_sid(sampass, &u_sid, flag))
+ return False;
+
+ DEBUG(10, ("pdb_set_user_sid_from_rid:\n\tsetting user sid %s from rid %d\n",
+ sid_string_static(&u_sid),rid));
+
+ return True;
+}
+
+BOOL pdb_set_group_sid_from_rid (SAM_ACCOUNT *sampass, uint32 grid, enum pdb_value_state flag)
+{
+ DOM_SID g_sid;
+ const DOM_SID *global_sam_sid;
+
+ if (!sampass)
+ return False;
+
+ if (!(global_sam_sid = get_global_sam_sid())) {
+ DEBUG(1, ("pdb_set_user_sid_from_rid: Could not read global sam sid!\n"));
+ return False;
+ }
+
+ sid_copy(&g_sid, global_sam_sid);
+
+ if (!sid_append_rid(&g_sid, grid))
+ return False;
+
+ if (!pdb_set_group_sid(sampass, &g_sid, flag))
+ return False;
+
+ DEBUG(10, ("pdb_set_group_sid_from_rid:\n\tsetting group sid %s from rid %d\n",
+ sid_string_static(&g_sid), grid));
+
+ return True;
+}
+
diff --git a/source/passdb/pdb_get_set.c b/source/passdb/pdb_get_set.c
new file mode 100644
index 00000000000..908588c8988
--- /dev/null
+++ b/source/passdb/pdb_get_set.c
@@ -0,0 +1,1158 @@
+/*
+ Unix SMB/CIFS implementation.
+ SAM_ACCOUNT access routines
+ Copyright (C) Jeremy Allison 1996-2001
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+ Copyright (C) Gerald (Jerry) Carter 2000-2001
+ Copyright (C) Andrew Bartlett 2001-2002
+ Copyright (C) Stefan (metze) Metzmacher 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+/**
+ * @todo Redefine this to NULL, but this changes the API because
+ * much of samba assumes that the pdb_get...() funtions
+ * return pstrings. (ie not null-pointers).
+ * See also pdb_fill_default_sam().
+ */
+
+#define PDB_NOT_QUITE_NULL ""
+
+/*********************************************************************
+ Collection of get...() functions for SAM_ACCOUNT.
+ ********************************************************************/
+
+uint16 pdb_get_acct_ctrl (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.acct_ctrl);
+ else
+ return (ACB_DISABLED);
+}
+
+time_t pdb_get_logon_time (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.logon_time);
+ else
+ return (0);
+}
+
+time_t pdb_get_logoff_time (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.logoff_time);
+ else
+ return (-1);
+}
+
+time_t pdb_get_kickoff_time (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.kickoff_time);
+ else
+ return (-1);
+}
+
+time_t pdb_get_bad_password_time (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.bad_password_time);
+ else
+ return (-1);
+}
+
+time_t pdb_get_pass_last_set_time (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.pass_last_set_time);
+ else
+ return (-1);
+}
+
+time_t pdb_get_pass_can_change_time (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.pass_can_change_time);
+ else
+ return (-1);
+}
+
+time_t pdb_get_pass_must_change_time (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.pass_must_change_time);
+ else
+ return (-1);
+}
+
+uint16 pdb_get_logon_divs (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.logon_divs);
+ else
+ return (-1);
+}
+
+uint32 pdb_get_hours_len (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.hours_len);
+ else
+ return (-1);
+}
+
+const uint8* pdb_get_hours (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.hours);
+ else
+ return (NULL);
+}
+
+const uint8* pdb_get_nt_passwd (const SAM_ACCOUNT *sampass)
+{
+ if (sampass) {
+ SMB_ASSERT((!sampass->private.nt_pw.data)
+ || sampass->private.nt_pw.length == NT_HASH_LEN);
+ return ((uint8*)sampass->private.nt_pw.data);
+ }
+ else
+ return (NULL);
+}
+
+const uint8* pdb_get_lanman_passwd (const SAM_ACCOUNT *sampass)
+{
+ if (sampass) {
+ SMB_ASSERT((!sampass->private.lm_pw.data)
+ || sampass->private.lm_pw.length == LM_HASH_LEN);
+ return ((uint8*)sampass->private.lm_pw.data);
+ }
+ else
+ return (NULL);
+}
+
+/* Return the plaintext password if known. Most of the time
+ it isn't, so don't assume anything magic about this function.
+
+ Used to pass the plaintext to passdb backends that might
+ want to store more than just the NTLM hashes.
+*/
+const char* pdb_get_plaintext_passwd (const SAM_ACCOUNT *sampass)
+{
+ if (sampass) {
+ return (sampass->private.plaintext_pw);
+ }
+ else
+ return (NULL);
+}
+const DOM_SID *pdb_get_user_sid(const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return &sampass->private.user_sid;
+ else
+ return (NULL);
+}
+
+const DOM_SID *pdb_get_group_sid(const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return &sampass->private.group_sid;
+ else
+ return (NULL);
+}
+
+/**
+ * Get flags showing what is initalised in the SAM_ACCOUNT
+ * @param sampass the SAM_ACCOUNT in question
+ * @return the flags indicating the members initialised in the struct.
+ **/
+
+enum pdb_value_state pdb_get_init_flags (const SAM_ACCOUNT *sampass, enum pdb_elements element)
+{
+ enum pdb_value_state ret = PDB_DEFAULT;
+
+ if (!sampass || !sampass->private.change_flags || !sampass->private.set_flags)
+ return ret;
+
+ if (bitmap_query(sampass->private.set_flags, element)) {
+ DEBUG(11, ("element %d: SET\n", element));
+ ret = PDB_SET;
+ }
+
+ if (bitmap_query(sampass->private.change_flags, element)) {
+ DEBUG(11, ("element %d: CHANGED\n", element));
+ ret = PDB_CHANGED;
+ }
+
+ if (ret == PDB_DEFAULT) {
+ DEBUG(11, ("element %d: DEFAULT\n", element));
+ }
+
+ return ret;
+}
+
+const char* pdb_get_username (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.username);
+ else
+ return (NULL);
+}
+
+const char* pdb_get_domain (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.domain);
+ else
+ return (NULL);
+}
+
+const char* pdb_get_nt_username (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.nt_username);
+ else
+ return (NULL);
+}
+
+const char* pdb_get_fullname (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.full_name);
+ else
+ return (NULL);
+}
+
+const char* pdb_get_homedir (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.home_dir);
+ else
+ return (NULL);
+}
+
+const char* pdb_get_unix_homedir (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.unix_home_dir);
+ else
+ return (NULL);
+}
+
+const char* pdb_get_dir_drive (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.dir_drive);
+ else
+ return (NULL);
+}
+
+const char* pdb_get_logon_script (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.logon_script);
+ else
+ return (NULL);
+}
+
+const char* pdb_get_profile_path (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.profile_path);
+ else
+ return (NULL);
+}
+
+const char* pdb_get_acct_desc (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.acct_desc);
+ else
+ return (NULL);
+}
+
+const char* pdb_get_workstations (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.workstations);
+ else
+ return (NULL);
+}
+
+const char* pdb_get_unknown_str (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.unknown_str);
+ else
+ return (NULL);
+}
+
+const char* pdb_get_munged_dial (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.munged_dial);
+ else
+ return (NULL);
+}
+
+uint16 pdb_get_bad_password_count(const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.bad_password_count);
+ else
+ return 0;
+}
+
+uint16 pdb_get_logon_count(const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.logon_count);
+ else
+ return 0;
+}
+
+uint32 pdb_get_unknown_6 (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.unknown_6);
+ else
+ return (-1);
+}
+
+void *pdb_get_backend_private_data (const SAM_ACCOUNT *sampass, const struct pdb_methods *my_methods)
+{
+ if (sampass && my_methods == sampass->private.backend_private_methods)
+ return sampass->private.backend_private_data;
+ else
+ return NULL;
+}
+
+/*********************************************************************
+ Collection of set...() functions for SAM_ACCOUNT.
+ ********************************************************************/
+
+BOOL pdb_set_acct_ctrl (SAM_ACCOUNT *sampass, uint16 acct_ctrl, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ sampass->private.acct_ctrl = acct_ctrl;
+
+ return pdb_set_init_flags(sampass, PDB_ACCTCTRL, flag);
+}
+
+BOOL pdb_set_logon_time (SAM_ACCOUNT *sampass, time_t mytime, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ sampass->private.logon_time = mytime;
+
+ return pdb_set_init_flags(sampass, PDB_LOGONTIME, flag);
+}
+
+BOOL pdb_set_logoff_time (SAM_ACCOUNT *sampass, time_t mytime, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ sampass->private.logoff_time = mytime;
+
+ return pdb_set_init_flags(sampass, PDB_LOGOFFTIME, flag);
+}
+
+BOOL pdb_set_kickoff_time (SAM_ACCOUNT *sampass, time_t mytime, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ sampass->private.kickoff_time = mytime;
+
+ return pdb_set_init_flags(sampass, PDB_KICKOFFTIME, flag);
+}
+
+BOOL pdb_set_bad_password_time (SAM_ACCOUNT *sampass, time_t mytime,
+ enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ sampass->private.bad_password_time = mytime;
+
+ return pdb_set_init_flags(sampass, PDB_BAD_PASSWORD_TIME, flag);
+}
+
+BOOL pdb_set_pass_can_change_time (SAM_ACCOUNT *sampass, time_t mytime, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ sampass->private.pass_can_change_time = mytime;
+
+ return pdb_set_init_flags(sampass, PDB_CANCHANGETIME, flag);
+}
+
+BOOL pdb_set_pass_must_change_time (SAM_ACCOUNT *sampass, time_t mytime, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ sampass->private.pass_must_change_time = mytime;
+
+ return pdb_set_init_flags(sampass, PDB_MUSTCHANGETIME, flag);
+}
+
+BOOL pdb_set_pass_last_set_time (SAM_ACCOUNT *sampass, time_t mytime, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ sampass->private.pass_last_set_time = mytime;
+
+ return pdb_set_init_flags(sampass, PDB_PASSLASTSET, flag);
+}
+
+BOOL pdb_set_hours_len (SAM_ACCOUNT *sampass, uint32 len, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ sampass->private.hours_len = len;
+
+ return pdb_set_init_flags(sampass, PDB_HOURSLEN, flag);
+}
+
+BOOL pdb_set_logon_divs (SAM_ACCOUNT *sampass, uint16 hours, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ sampass->private.logon_divs = hours;
+
+ return pdb_set_init_flags(sampass, PDB_LOGONDIVS, flag);
+}
+
+/**
+ * Set flags showing what is initalised in the SAM_ACCOUNT
+ * @param sampass the SAM_ACCOUNT in question
+ * @param flag The *new* flag to be set. Old flags preserved
+ * this flag is only added.
+ **/
+
+BOOL pdb_set_init_flags (SAM_ACCOUNT *sampass, enum pdb_elements element, enum pdb_value_state value_flag)
+{
+ if (!sampass || !sampass->mem_ctx)
+ return False;
+
+ if (!sampass->private.set_flags) {
+ if ((sampass->private.set_flags =
+ bitmap_talloc(sampass->mem_ctx,
+ PDB_COUNT))==NULL) {
+ DEBUG(0,("bitmap_talloc failed\n"));
+ return False;
+ }
+ }
+ if (!sampass->private.change_flags) {
+ if ((sampass->private.change_flags =
+ bitmap_talloc(sampass->mem_ctx,
+ PDB_COUNT))==NULL) {
+ DEBUG(0,("bitmap_talloc failed\n"));
+ return False;
+ }
+ }
+
+ switch(value_flag) {
+ case PDB_CHANGED:
+ if (!bitmap_set(sampass->private.change_flags, element)) {
+ DEBUG(0,("Can't set flag: %d in change_flags.\n",element));
+ return False;
+ }
+ if (!bitmap_set(sampass->private.set_flags, element)) {
+ DEBUG(0,("Can't set flag: %d in set_flags.\n",element));
+ return False;
+ }
+ DEBUG(11, ("element %d -> now CHANGED\n", element));
+ break;
+ case PDB_SET:
+ if (!bitmap_clear(sampass->private.change_flags, element)) {
+ DEBUG(0,("Can't set flag: %d in change_flags.\n",element));
+ return False;
+ }
+ if (!bitmap_set(sampass->private.set_flags, element)) {
+ DEBUG(0,("Can't set flag: %d in set_flags.\n",element));
+ return False;
+ }
+ DEBUG(10, ("element %d -> now SET\n", element));
+ break;
+ case PDB_DEFAULT:
+ default:
+ if (!bitmap_clear(sampass->private.change_flags, element)) {
+ DEBUG(0,("Can't set flag: %d in change_flags.\n",element));
+ return False;
+ }
+ if (!bitmap_clear(sampass->private.set_flags, element)) {
+ DEBUG(0,("Can't set flag: %d in set_flags.\n",element));
+ return False;
+ }
+ DEBUG(11, ("element %d -> now DEFAULT\n", element));
+ break;
+ }
+
+ return True;
+}
+
+BOOL pdb_set_user_sid (SAM_ACCOUNT *sampass, const DOM_SID *u_sid, enum pdb_value_state flag)
+{
+ if (!sampass || !u_sid)
+ return False;
+
+ sid_copy(&sampass->private.user_sid, u_sid);
+
+ DEBUG(10, ("pdb_set_user_sid: setting user sid %s\n",
+ sid_string_static(&sampass->private.user_sid)));
+
+ return pdb_set_init_flags(sampass, PDB_USERSID, flag);
+}
+
+BOOL pdb_set_user_sid_from_string (SAM_ACCOUNT *sampass, fstring u_sid, enum pdb_value_state flag)
+{
+ DOM_SID new_sid;
+
+ if (!sampass || !u_sid)
+ return False;
+
+ DEBUG(10, ("pdb_set_user_sid_from_string: setting user sid %s\n",
+ u_sid));
+
+ if (!string_to_sid(&new_sid, u_sid)) {
+ DEBUG(1, ("pdb_set_user_sid_from_string: %s isn't a valid SID!\n", u_sid));
+ return False;
+ }
+
+ if (!pdb_set_user_sid(sampass, &new_sid, flag)) {
+ DEBUG(1, ("pdb_set_user_sid_from_string: could not set sid %s on SAM_ACCOUNT!\n", u_sid));
+ return False;
+ }
+
+ return True;
+}
+
+BOOL pdb_set_group_sid (SAM_ACCOUNT *sampass, const DOM_SID *g_sid, enum pdb_value_state flag)
+{
+ if (!sampass || !g_sid)
+ return False;
+
+ sid_copy(&sampass->private.group_sid, g_sid);
+
+ DEBUG(10, ("pdb_set_group_sid: setting group sid %s\n",
+ sid_string_static(&sampass->private.group_sid)));
+
+ return pdb_set_init_flags(sampass, PDB_GROUPSID, flag);
+}
+
+BOOL pdb_set_group_sid_from_string (SAM_ACCOUNT *sampass, fstring g_sid, enum pdb_value_state flag)
+{
+ DOM_SID new_sid;
+ if (!sampass || !g_sid)
+ return False;
+
+ DEBUG(10, ("pdb_set_group_sid_from_string: setting group sid %s\n",
+ g_sid));
+
+ if (!string_to_sid(&new_sid, g_sid)) {
+ DEBUG(1, ("pdb_set_group_sid_from_string: %s isn't a valid SID!\n", g_sid));
+ return False;
+ }
+
+ if (!pdb_set_group_sid(sampass, &new_sid, flag)) {
+ DEBUG(1, ("pdb_set_group_sid_from_string: could not set sid %s on SAM_ACCOUNT!\n", g_sid));
+ return False;
+ }
+ return True;
+}
+
+/*********************************************************************
+ Set the user's UNIX name.
+ ********************************************************************/
+
+BOOL pdb_set_username(SAM_ACCOUNT *sampass, const char *username, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (username) {
+ DEBUG(10, ("pdb_set_username: setting username %s, was %s\n", username,
+ (sampass->private.username)?(sampass->private.username):"NULL"));
+
+ sampass->private.username = talloc_strdup(sampass->mem_ctx, username);
+
+ if (!sampass->private.username) {
+ DEBUG(0, ("pdb_set_username: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.username = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_USERNAME, flag);
+}
+
+/*********************************************************************
+ Set the domain name.
+ ********************************************************************/
+
+BOOL pdb_set_domain(SAM_ACCOUNT *sampass, const char *domain, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (domain) {
+ DEBUG(10, ("pdb_set_domain: setting domain %s, was %s\n", domain,
+ (sampass->private.domain)?(sampass->private.domain):"NULL"));
+
+ sampass->private.domain = talloc_strdup(sampass->mem_ctx, domain);
+
+ if (!sampass->private.domain) {
+ DEBUG(0, ("pdb_set_domain: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.domain = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_DOMAIN, flag);
+}
+
+/*********************************************************************
+ Set the user's NT name.
+ ********************************************************************/
+
+BOOL pdb_set_nt_username(SAM_ACCOUNT *sampass, const char *nt_username, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (nt_username) {
+ DEBUG(10, ("pdb_set_nt_username: setting nt username %s, was %s\n", nt_username,
+ (sampass->private.nt_username)?(sampass->private.nt_username):"NULL"));
+
+ sampass->private.nt_username = talloc_strdup(sampass->mem_ctx, nt_username);
+
+ if (!sampass->private.nt_username) {
+ DEBUG(0, ("pdb_set_nt_username: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.nt_username = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_NTUSERNAME, flag);
+}
+
+/*********************************************************************
+ Set the user's full name.
+ ********************************************************************/
+
+BOOL pdb_set_fullname(SAM_ACCOUNT *sampass, const char *full_name, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (full_name) {
+ DEBUG(10, ("pdb_set_full_name: setting full name %s, was %s\n", full_name,
+ (sampass->private.full_name)?(sampass->private.full_name):"NULL"));
+
+ sampass->private.full_name = talloc_strdup(sampass->mem_ctx, full_name);
+
+ if (!sampass->private.full_name) {
+ DEBUG(0, ("pdb_set_fullname: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.full_name = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_FULLNAME, flag);
+}
+
+/*********************************************************************
+ Set the user's logon script.
+ ********************************************************************/
+
+BOOL pdb_set_logon_script(SAM_ACCOUNT *sampass, const char *logon_script, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (logon_script) {
+ DEBUG(10, ("pdb_set_logon_script: setting logon script %s, was %s\n", logon_script,
+ (sampass->private.logon_script)?(sampass->private.logon_script):"NULL"));
+
+ sampass->private.logon_script = talloc_strdup(sampass->mem_ctx, logon_script);
+
+ if (!sampass->private.logon_script) {
+ DEBUG(0, ("pdb_set_logon_script: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.logon_script = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_LOGONSCRIPT, flag);
+}
+
+/*********************************************************************
+ Set the user's profile path.
+ ********************************************************************/
+
+BOOL pdb_set_profile_path (SAM_ACCOUNT *sampass, const char *profile_path, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (profile_path) {
+ DEBUG(10, ("pdb_set_profile_path: setting profile path %s, was %s\n", profile_path,
+ (sampass->private.profile_path)?(sampass->private.profile_path):"NULL"));
+
+ sampass->private.profile_path = talloc_strdup(sampass->mem_ctx, profile_path);
+
+ if (!sampass->private.profile_path) {
+ DEBUG(0, ("pdb_set_profile_path: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.profile_path = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_PROFILE, flag);
+}
+
+/*********************************************************************
+ Set the user's directory drive.
+ ********************************************************************/
+
+BOOL pdb_set_dir_drive (SAM_ACCOUNT *sampass, const char *dir_drive, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (dir_drive) {
+ DEBUG(10, ("pdb_set_dir_drive: setting dir drive %s, was %s\n", dir_drive,
+ (sampass->private.dir_drive)?(sampass->private.dir_drive):"NULL"));
+
+ sampass->private.dir_drive = talloc_strdup(sampass->mem_ctx, dir_drive);
+
+ if (!sampass->private.dir_drive) {
+ DEBUG(0, ("pdb_set_dir_drive: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.dir_drive = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_DRIVE, flag);
+}
+
+/*********************************************************************
+ Set the user's home directory.
+ ********************************************************************/
+
+BOOL pdb_set_homedir (SAM_ACCOUNT *sampass, const char *home_dir, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (home_dir) {
+ DEBUG(10, ("pdb_set_homedir: setting home dir %s, was %s\n", home_dir,
+ (sampass->private.home_dir)?(sampass->private.home_dir):"NULL"));
+
+ sampass->private.home_dir = talloc_strdup(sampass->mem_ctx, home_dir);
+
+ if (!sampass->private.home_dir) {
+ DEBUG(0, ("pdb_set_home_dir: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.home_dir = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_SMBHOME, flag);
+}
+
+/*********************************************************************
+ Set the user's unix home directory.
+ ********************************************************************/
+
+BOOL pdb_set_unix_homedir (SAM_ACCOUNT *sampass, const char *unix_home_dir, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (unix_home_dir) {
+ DEBUG(10, ("pdb_set_unix_homedir: setting home dir %s, was %s\n", unix_home_dir,
+ (sampass->private.unix_home_dir)?(sampass->private.unix_home_dir):"NULL"));
+
+ sampass->private.unix_home_dir = talloc_strdup(sampass->mem_ctx,
+ unix_home_dir);
+
+ if (!sampass->private.unix_home_dir) {
+ DEBUG(0, ("pdb_set_unix_home_dir: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.unix_home_dir = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_UNIXHOMEDIR, flag);
+}
+
+/*********************************************************************
+ Set the user's account description.
+ ********************************************************************/
+
+BOOL pdb_set_acct_desc (SAM_ACCOUNT *sampass, const char *acct_desc, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (acct_desc) {
+ sampass->private.acct_desc = talloc_strdup(sampass->mem_ctx, acct_desc);
+
+ if (!sampass->private.acct_desc) {
+ DEBUG(0, ("pdb_set_acct_desc: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.acct_desc = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_ACCTDESC, flag);
+}
+
+/*********************************************************************
+ Set the user's workstation allowed list.
+ ********************************************************************/
+
+BOOL pdb_set_workstations (SAM_ACCOUNT *sampass, const char *workstations, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (workstations) {
+ DEBUG(10, ("pdb_set_workstations: setting workstations %s, was %s\n", workstations,
+ (sampass->private.workstations)?(sampass->private.workstations):"NULL"));
+
+ sampass->private.workstations = talloc_strdup(sampass->mem_ctx, workstations);
+
+ if (!sampass->private.workstations) {
+ DEBUG(0, ("pdb_set_workstations: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.workstations = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_WORKSTATIONS, flag);
+}
+
+/*********************************************************************
+ Set the user's 'unknown_str', whatever the heck this actually is...
+ ********************************************************************/
+
+BOOL pdb_set_unknown_str (SAM_ACCOUNT *sampass, const char *unknown_str, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (unknown_str) {
+ sampass->private.unknown_str = talloc_strdup(sampass->mem_ctx, unknown_str);
+
+ if (!sampass->private.unknown_str) {
+ DEBUG(0, ("pdb_set_unknown_str: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.unknown_str = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_UNKNOWNSTR, flag);
+}
+
+/*********************************************************************
+ Set the user's dial string.
+ ********************************************************************/
+
+BOOL pdb_set_munged_dial (SAM_ACCOUNT *sampass, const char *munged_dial, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (munged_dial) {
+ sampass->private.munged_dial = talloc_strdup(sampass->mem_ctx, munged_dial);
+
+ if (!sampass->private.munged_dial) {
+ DEBUG(0, ("pdb_set_munged_dial: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.munged_dial = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_MUNGEDDIAL, flag);
+}
+
+/*********************************************************************
+ Set the user's NT hash.
+ ********************************************************************/
+
+BOOL pdb_set_nt_passwd (SAM_ACCOUNT *sampass, const uint8 pwd[NT_HASH_LEN], enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ data_blob_clear_free(&sampass->private.nt_pw);
+
+ if (pwd) {
+ sampass->private.nt_pw = data_blob(pwd, NT_HASH_LEN);
+ } else {
+ sampass->private.nt_pw = data_blob(NULL, 0);
+ }
+
+ return pdb_set_init_flags(sampass, PDB_NTPASSWD, flag);
+}
+
+/*********************************************************************
+ Set the user's LM hash.
+ ********************************************************************/
+
+BOOL pdb_set_lanman_passwd (SAM_ACCOUNT *sampass, const uint8 pwd[LM_HASH_LEN], enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ data_blob_clear_free(&sampass->private.lm_pw);
+
+ if (pwd) {
+ sampass->private.lm_pw = data_blob(pwd, LM_HASH_LEN);
+ } else {
+ sampass->private.lm_pw = data_blob(NULL, 0);
+ }
+
+ return pdb_set_init_flags(sampass, PDB_LMPASSWD, flag);
+}
+
+/*********************************************************************
+ Set the user's plaintext password only (base procedure, see helper
+ below)
+ ********************************************************************/
+
+BOOL pdb_set_plaintext_pw_only (SAM_ACCOUNT *sampass, const char *password, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (password) {
+ if (sampass->private.plaintext_pw!=NULL)
+ memset(sampass->private.plaintext_pw,'\0',strlen(sampass->private.plaintext_pw)+1);
+
+ sampass->private.plaintext_pw = talloc_strdup(sampass->mem_ctx, password);
+
+ if (!sampass->private.plaintext_pw) {
+ DEBUG(0, ("pdb_set_unknown_str: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.plaintext_pw = NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_PLAINTEXT_PW, flag);
+}
+
+BOOL pdb_set_bad_password_count(SAM_ACCOUNT *sampass, uint16 bad_password_count, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ sampass->private.bad_password_count = bad_password_count;
+
+ return pdb_set_init_flags(sampass, PDB_BAD_PASSWORD_COUNT, flag);
+}
+
+BOOL pdb_set_logon_count(SAM_ACCOUNT *sampass, uint16 logon_count, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ sampass->private.logon_count = logon_count;
+
+ return pdb_set_init_flags(sampass, PDB_LOGON_COUNT, flag);
+}
+
+BOOL pdb_set_unknown_6 (SAM_ACCOUNT *sampass, uint32 unkn, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ sampass->private.unknown_6 = unkn;
+
+ return pdb_set_init_flags(sampass, PDB_UNKNOWN6, flag);
+}
+
+BOOL pdb_set_hours (SAM_ACCOUNT *sampass, const uint8 *hours, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (!hours) {
+ memset ((char *)sampass->private.hours, 0, MAX_HOURS_LEN);
+ return True;
+ }
+
+ memcpy (sampass->private.hours, hours, MAX_HOURS_LEN);
+
+ return pdb_set_init_flags(sampass, PDB_HOURS, flag);
+}
+
+BOOL pdb_set_backend_private_data (SAM_ACCOUNT *sampass, void *private_data,
+ void (*free_fn)(void **),
+ const struct pdb_methods *my_methods,
+ enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (sampass->private.backend_private_data && sampass->private.backend_private_data_free_fn) {
+ sampass->private.backend_private_data_free_fn(&sampass->private.backend_private_data);
+ }
+
+ sampass->private.backend_private_data = private_data;
+ sampass->private.backend_private_data_free_fn = free_fn;
+ sampass->private.backend_private_methods = my_methods;
+
+ return pdb_set_init_flags(sampass, PDB_BACKEND_PRIVATE_DATA, flag);
+}
+
+
+/* Helpful interfaces to the above */
+
+/*********************************************************************
+ Sets the last changed times and must change times for a normal
+ password change.
+ ********************************************************************/
+
+BOOL pdb_set_pass_changed_now (SAM_ACCOUNT *sampass)
+{
+ uint32 expire;
+ uint32 min_age;
+
+ if (!sampass)
+ return False;
+
+ if (!pdb_set_pass_last_set_time (sampass, time(NULL), PDB_CHANGED))
+ return False;
+
+ if (!account_policy_get(AP_MAX_PASSWORD_AGE, &expire)
+ || (expire==(uint32)-1) || (expire == 0)) {
+ if (!pdb_set_pass_must_change_time (sampass, get_time_t_max(), PDB_CHANGED))
+ return False;
+ } else {
+ if (!pdb_set_pass_must_change_time (sampass,
+ pdb_get_pass_last_set_time(sampass)
+ + expire, PDB_CHANGED))
+ return False;
+ }
+
+ if (!account_policy_get(AP_MIN_PASSWORD_AGE, &min_age)
+ || (min_age==(uint32)-1)) {
+ if (!pdb_set_pass_can_change_time (sampass, 0, PDB_CHANGED))
+ return False;
+ } else {
+ if (!pdb_set_pass_can_change_time (sampass,
+ pdb_get_pass_last_set_time(sampass)
+ + min_age, PDB_CHANGED))
+ return False;
+ }
+ return True;
+}
+
+/*********************************************************************
+ Set the user's PLAINTEXT password. Used as an interface to the above.
+ Also sets the last change time to NOW.
+ ********************************************************************/
+
+BOOL pdb_set_plaintext_passwd (SAM_ACCOUNT *sampass, const char *plaintext)
+{
+ uchar new_lanman_p16[16];
+ uchar new_nt_p16[16];
+
+ if (!sampass || !plaintext)
+ return False;
+
+ /* Calculate the MD4 hash (NT compatible) of the password */
+ E_md4hash(plaintext, new_nt_p16);
+
+ if (!pdb_set_nt_passwd (sampass, new_nt_p16, PDB_CHANGED))
+ return False;
+
+ if (!E_deshash(plaintext, new_lanman_p16)) {
+ /* E_deshash returns false for 'long' passwords (> 14
+ DOS chars). This allows us to match Win2k, which
+ does not store a LM hash for these passwords (which
+ would reduce the effective password length to 14 */
+
+ if (!pdb_set_lanman_passwd (sampass, NULL, PDB_CHANGED))
+ return False;
+ } else {
+ if (!pdb_set_lanman_passwd (sampass, new_lanman_p16, PDB_CHANGED))
+ return False;
+ }
+
+ if (!pdb_set_plaintext_pw_only (sampass, plaintext, PDB_CHANGED))
+ return False;
+
+ if (!pdb_set_pass_changed_now (sampass))
+ return False;
+
+ return True;
+}
+
+/* check for any PDB_SET/CHANGED field and fill the appropriate mask bit */
+uint32 pdb_build_fields_present (SAM_ACCOUNT *sampass)
+{
+ /* value set to all for testing */
+ return 0x00ffffff;
+}
+
diff --git a/source/passdb/pdb_guest.c b/source/passdb/pdb_guest.c
new file mode 100644
index 00000000000..8c1d4c7b0fe
--- /dev/null
+++ b/source/passdb/pdb_guest.c
@@ -0,0 +1,179 @@
+/*
+ * 'Guest' password backend for samba
+ * Copyright (C) Jelmer Vernooij 2002
+ * Copyright (C) Andrew Bartlett 2003
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 675
+ * Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+/******************************************************************
+ Lookup a name in the SAM database
+ ******************************************************************/
+
+static NTSTATUS guestsam_getsampwnam (struct pdb_methods *methods, SAM_ACCOUNT *sam_account, const char *sname)
+{
+ const char *guest_account = lp_guestaccount();
+
+ if (!sam_account || !sname) {
+ DEBUG(0,("invalid name specified"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!(guest_account && *guest_account)) {
+ DEBUG(1, ("NULL guest account!?!?\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!methods) {
+ DEBUG(0,("invalid methods\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ if (!strequal(guest_account, sname)) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ pdb_fill_default_sam(sam_account);
+
+ if (!pdb_set_username(sam_account, guest_account, PDB_SET))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ if (!pdb_set_fullname(sam_account, guest_account, PDB_SET))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ if (!pdb_set_domain(sam_account, get_global_sam_name(), PDB_DEFAULT))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ if (!pdb_set_acct_ctrl(sam_account, ACB_NORMAL, PDB_DEFAULT))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ if (!pdb_set_user_sid_from_rid(sam_account, DOMAIN_USER_RID_GUEST, PDB_SET))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ if (!pdb_set_group_sid_from_rid(sam_account, DOMAIN_GROUP_RID_GUESTS, PDB_DEFAULT))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ return NT_STATUS_OK;
+}
+
+
+/***************************************************************************
+ Search by rid
+ **************************************************************************/
+
+static NTSTATUS guestsam_getsampwrid (struct pdb_methods *methods,
+ SAM_ACCOUNT *sam_account, uint32 rid)
+{
+ if (rid != DOMAIN_USER_RID_GUEST) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (!sam_account) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return guestsam_getsampwnam (methods, sam_account, lp_guestaccount());
+}
+
+static NTSTATUS guestsam_getsampwsid(struct pdb_methods *my_methods, SAM_ACCOUNT * user, const DOM_SID *sid)
+{
+ uint32 rid;
+ if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid))
+ return NT_STATUS_NO_SUCH_USER;
+
+ return guestsam_getsampwrid(my_methods, user, rid);
+}
+
+
+/***************************************************************************
+ Updates a SAM_ACCOUNT
+
+ This isn't a particulary practical option for pdb_guest. We certainly don't
+ want to twidde the filesystem, so what should we do?
+
+ Current plan is to transparently add the account. It should appear
+ as if the pdb_guest version was modified, but its actually stored somehwere.
+ ****************************************************************************/
+
+static NTSTATUS guestsam_update_sam_account (struct pdb_methods *methods, SAM_ACCOUNT *newpwd)
+{
+#if 1 /* JERRY */
+
+ /* apparently thr build farm relies upon this heavior :-( */
+
+ return methods->parent->pdb_add_sam_account(methods->parent, newpwd);
+#else
+ /* I don't think we should allow any modification of
+ the guest account as SID will could messed up with
+ the smbpasswd backend --jerry */
+
+ return NT_STATUS_NOT_IMPLEMENTED;
+#endif
+}
+
+NTSTATUS pdb_init_guestsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
+{
+ NTSTATUS nt_status;
+
+ if (!pdb_context) {
+ DEBUG(0, ("invalid pdb_context specified\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
+ return nt_status;
+ }
+
+ (*pdb_method)->name = "guestsam";
+
+ (*pdb_method)->getsampwnam = guestsam_getsampwnam;
+ (*pdb_method)->getsampwsid = guestsam_getsampwsid;
+ (*pdb_method)->update_sam_account = guestsam_update_sam_account;
+
+ /* we should do no group mapping here */
+ (*pdb_method)->getgrsid = pdb_nop_getgrsid;
+ (*pdb_method)->getgrgid = pdb_nop_getgrgid;
+ (*pdb_method)->getgrnam = pdb_nop_getgrnam;
+ (*pdb_method)->add_group_mapping_entry = pdb_nop_add_group_mapping_entry;
+ (*pdb_method)->update_group_mapping_entry = pdb_nop_update_group_mapping_entry;
+ (*pdb_method)->delete_group_mapping_entry = pdb_nop_delete_group_mapping_entry;
+ (*pdb_method)->enum_group_mapping = pdb_nop_enum_group_mapping;
+
+ /* we do not handle groups in guest backend */
+/* FIXME
+ (*pdb_method)->get_group_info_by_sid = pdb_nop_get_group_info_by_sid;
+ (*pdb_method)->get_group_list = pdb_nop_get_group_list;
+ (*pdb_method)->get_group_sids = pdb_nop_get_group_sids;
+ (*pdb_method)->add_group = pdb_nop_add_group;
+ (*pdb_method)->update_group = pdb_nop_update_group;
+ (*pdb_method)->delete_group = pdb_nop_delete_group;
+ (*pdb_method)->add_sid_to_group = pdb_nop_add_sid_to_group;
+ (*pdb_method)->remove_sid_from_group = pdb_nop_remove_sid_from_group;
+ (*pdb_method)->get_group_info_by_name = pdb_nop_get_group_info_by_name;
+ (*pdb_method)->get_group_info_by_nt_name = pdb_nop_get_group_info_by_nt_name;
+ (*pdb_method)->get_group_uids = pdb_nop_get_group_uids;
+*/
+
+
+ /* There's not very much to initialise here */
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_guest_init(void)
+{
+ return smb_register_passdb(PASSDB_INTERFACE_VERSION, "guest", pdb_init_guestsam);
+}
+
diff --git a/source/passdb/pdb_gums.c b/source/passdb/pdb_gums.c
new file mode 100644
index 00000000000..f34d3a94b5a
--- /dev/null
+++ b/source/passdb/pdb_gums.c
@@ -0,0 +1,464 @@
+/*
+ * GUMS password backend for samba
+ * Copyright (C) Simo Sorce 2003-2004
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 675
+ * Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+#define SET_OR_FAIL(func, label) do { if (!NT_STATUS_IS_OK(func)) { DEBUG(0, ("%s: Setting gums object data failed!\n", FUNCTION_MACRO)); goto label; } } while(0)
+#define BOOL_SET_OR_FAIL(func, label) do { if (!func) { DEBUG(0, ("%s: Setting sam object data failed!\n", FUNCTION_MACRO)); goto label; } } while(0)
+
+struct gums_gw_data {
+ GUMS_FUNCTIONS *fns;
+ void *handle;
+};
+
+static NTSTATUS gums_object_to_sam_account(SAM_ACCOUNT *sa, GUMS_OBJECT *go)
+{
+ NTSTATUS ret;
+ NTTIME nt_time;
+ DATA_BLOB pwd;
+
+ if (!go || !sa)
+ return NT_STATUS_INVALID_PARAMETER;
+/*
+ if (!NT_STATUS_IS_OK(ret = pdb_init_sam(sa))) {
+ DEBUG(0, ("gums_object_to_sam_account: error occurred while creating sam_account object!\n"));
+ goto error;
+ }
+*/
+ if (gums_get_object_type(go) != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ BOOL_SET_OR_FAIL(pdb_set_acct_ctrl(sa, gums_get_user_acct_ctrl(go), PDB_SET), error);
+
+ /* domain */
+ /* unix_homedir ? */
+
+ nt_time = gums_get_user_logon_time(go);
+ BOOL_SET_OR_FAIL(pdb_set_logon_time(sa, nt_time_to_unix(&nt_time), PDB_SET), error);
+ nt_time = gums_get_user_logoff_time(go);
+ BOOL_SET_OR_FAIL(pdb_set_logoff_time(sa, nt_time_to_unix(&nt_time), PDB_SET), error);
+ nt_time = gums_get_user_kickoff_time(go);
+ BOOL_SET_OR_FAIL(pdb_set_kickoff_time(sa, nt_time_to_unix(&nt_time), PDB_SET), error);
+ nt_time = gums_get_user_pass_last_set_time(go);
+ BOOL_SET_OR_FAIL(pdb_set_pass_last_set_time(sa, nt_time_to_unix(&nt_time), PDB_SET), error);
+ nt_time = gums_get_user_pass_can_change_time(go);
+ BOOL_SET_OR_FAIL(pdb_set_pass_can_change_time(sa, nt_time_to_unix(&nt_time), PDB_SET), error);
+ nt_time = gums_get_user_pass_must_change_time(go);
+ BOOL_SET_OR_FAIL(pdb_set_pass_must_change_time(sa, nt_time_to_unix(&nt_time), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_hours_len(sa, gums_get_user_hours_len(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_logon_divs(sa, gums_get_user_logon_divs(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_user_sid(sa, gums_get_object_sid(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_group_sid(sa, gums_get_user_pri_group(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_username(sa, gums_get_object_name(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_nt_username(sa, gums_get_object_name(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_fullname(sa, gums_get_user_fullname(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_logon_script(sa, gums_get_user_logon_script(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_profile_path(sa, gums_get_user_profile_path(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_dir_drive(sa, gums_get_user_dir_drive(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_homedir(sa, gums_get_user_homedir(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_acct_desc(sa, gums_get_object_description(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_workstations(sa, gums_get_user_workstations(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_unknown_str(sa, gums_get_user_unknown_str(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_munged_dial(sa, gums_get_user_munged_dial(go), PDB_SET), error);
+
+ pwd = gums_get_user_nt_pwd(go);
+ if (!pdb_set_nt_passwd(sa, pwd.data, PDB_SET)) {
+ DEBUG(5, ("gums_object_to_sam_account: unable to set nt password"));
+ data_blob_clear_free(&pwd);
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto error;
+ }
+ data_blob_clear_free(&pwd);
+ pwd = gums_get_user_lm_pwd(go);
+ if (!pdb_set_lanman_passwd(sa, pwd.data, PDB_SET)) {
+ DEBUG(5, ("gums_object_to_sam_account: unable to set lanman password"));
+ data_blob_clear_free(&pwd);
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto error;
+ }
+ data_blob_clear_free(&pwd);
+
+ BOOL_SET_OR_FAIL(pdb_set_bad_password_count(sa, gums_get_user_bad_password_count(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_unknown_6(sa, gums_get_user_unknown_6(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_hours(sa, gums_get_user_hours(go), PDB_SET), error);
+
+ return NT_STATUS_OK;
+
+error:
+ if (sa && (sa->free_fn)) {
+ sa->free_fn(&sa);
+ }
+
+ return ret;
+}
+
+static NTSTATUS sam_account_to_gums_object(GUMS_OBJECT *go, SAM_ACCOUNT *sa)
+{
+ NTSTATUS ret;
+ NTTIME nt_time;
+ DATA_BLOB pwd;
+
+ if (!go || !sa)
+ return NT_STATUS_INVALID_PARAMETER;
+
+/*
+ ret = gums_create_object(go, GUMS_OBJ_NORMAL_USER);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0, ("sam_account_to_gums_object: error occurred while creating gums object!\n"));
+ goto error;
+ }
+*/
+
+ /* sec_desc */
+
+ SET_OR_FAIL(gums_set_object_name(go, pdb_get_username(sa)), error);
+
+ SET_OR_FAIL(gums_set_object_sid(go, pdb_get_user_sid(sa)), error);
+ SET_OR_FAIL(gums_set_user_pri_group(go, pdb_get_group_sid(sa)), error);
+
+ if (pdb_get_acct_desc(sa))
+ SET_OR_FAIL(gums_set_object_description(go, pdb_get_acct_desc(sa)), error);
+ if (pdb_get_fullname(sa))
+ SET_OR_FAIL(gums_set_user_fullname(go, pdb_get_fullname(sa)), error);
+ if (pdb_get_homedir(sa))
+ SET_OR_FAIL(gums_set_user_homedir(go, pdb_get_homedir(sa)), error);
+ if (pdb_get_dir_drive(sa))
+ SET_OR_FAIL(gums_set_user_dir_drive(go, pdb_get_dir_drive(sa)), error);
+ if (pdb_get_logon_script(sa))
+ SET_OR_FAIL(gums_set_user_logon_script(go, pdb_get_logon_script(sa)), error);
+ if (pdb_get_profile_path(sa))
+ SET_OR_FAIL(gums_set_user_profile_path(go, pdb_get_profile_path(sa)), error);
+ if (pdb_get_workstations(sa))
+ SET_OR_FAIL(gums_set_user_workstations(go, pdb_get_workstations(sa)), error);
+ if (pdb_get_unknown_str(sa))
+ SET_OR_FAIL(gums_set_user_unknown_str(go, pdb_get_unknown_str(sa)), error);
+ if (pdb_get_munged_dial(sa))
+ SET_OR_FAIL(gums_set_user_munged_dial(go, pdb_get_munged_dial(sa)), error);
+ SET_OR_FAIL(gums_set_user_logon_divs(go, pdb_get_logon_divs(sa)), error);
+ if (pdb_get_hours(sa))
+ SET_OR_FAIL(gums_set_user_hours(go, pdb_get_hours_len(sa), pdb_get_hours(sa)), error);
+ SET_OR_FAIL(gums_set_user_bad_password_count(go, pdb_get_bad_password_count(sa)), error);
+ SET_OR_FAIL(gums_set_user_unknown_6(go, pdb_get_unknown_6(sa)), error);
+
+ unix_to_nt_time(&nt_time, pdb_get_logon_time(sa));
+ SET_OR_FAIL(gums_set_user_logon_time(go, nt_time), error);
+ unix_to_nt_time(&nt_time, pdb_get_logoff_time(sa));
+ SET_OR_FAIL(gums_set_user_logoff_time(go, nt_time), error);
+ unix_to_nt_time(&nt_time, pdb_get_kickoff_time(sa));
+ SET_OR_FAIL(gums_set_user_kickoff_time(go, nt_time), error);
+ unix_to_nt_time(&nt_time, pdb_get_pass_last_set_time(sa));
+ SET_OR_FAIL(gums_set_user_pass_last_set_time(go, nt_time), error);
+ unix_to_nt_time(&nt_time, pdb_get_pass_can_change_time(sa));
+ SET_OR_FAIL(gums_set_user_pass_can_change_time(go, nt_time), error);
+ unix_to_nt_time(&nt_time, pdb_get_pass_must_change_time(sa));
+ SET_OR_FAIL(gums_set_user_pass_must_change_time(go, nt_time), error);
+
+ pwd = data_blob(pdb_get_nt_passwd(sa), NT_HASH_LEN);
+ ret = gums_set_user_nt_pwd(go, pwd);
+ data_blob_clear_free(&pwd);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(5, ("sam_account_to_gums_object: failed to set nt password!\n"));
+ goto error;
+ }
+ pwd = data_blob(pdb_get_lanman_passwd(sa), LM_HASH_LEN);
+ ret = gums_set_user_lm_pwd(go, pwd);
+ data_blob_clear_free(&pwd);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(5, ("sam_account_to_gums_object: failed to set lanman password!\n"));
+ goto error;
+ }
+
+ SET_OR_FAIL(gums_set_user_acct_ctrl(go, pdb_get_acct_ctrl(sa)), error);
+
+ return NT_STATUS_OK;
+
+error:
+ gums_reset_object(go);
+ return ret;
+}
+
+static NTSTATUS gums_setsampwent(struct pdb_methods *methods, BOOL update)
+{
+ struct gums_gw_data *ggwd = (struct gums_gw_data *)(methods->private_data);
+
+ return ggwd->fns->enumerate_objects_start(&(ggwd->handle), NULL, GUMS_OBJ_NORMAL_USER);
+}
+
+static NTSTATUS gums_getsampwent(struct pdb_methods *methods, SAM_ACCOUNT *account)
+{
+ NTSTATUS ret;
+ GUMS_OBJECT *go;
+ struct gums_gw_data *ggwd = (struct gums_gw_data *)(methods->private_data);
+
+ if (!NT_STATUS_IS_OK(ret = ggwd->fns->enumerate_objects_get_next(&go, ggwd->handle))) {
+ return ret;
+ }
+
+ ret = gums_object_to_sam_account(account, go);
+
+ gums_destroy_object(&go);
+ return ret;
+}
+
+static void gums_endsampwent(struct pdb_methods *methods)
+{
+ struct gums_gw_data *ggwd = (struct gums_gw_data *)(methods->private_data);
+
+ ggwd->fns->enumerate_objects_stop(ggwd->handle);
+}
+
+/******************************************************************
+ Lookup a name in the SAM database
+ ******************************************************************/
+
+static NTSTATUS gums_getsampwnam (struct pdb_methods *methods, SAM_ACCOUNT *account, const char *name)
+{
+ NTSTATUS ret;
+ GUMS_OBJECT *go;
+ struct gums_gw_data *ggwd = (struct gums_gw_data *)(methods->private_data);
+
+ if (!account || !name)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = ggwd->fns->get_object_from_name(&go, global_myname(), name, GUMS_OBJ_NORMAL_USER))) {
+ DEBUG(10, ("gums_getsampwnam: unable to find account with name %s", name));
+ return ret;
+ }
+
+ ret = gums_object_to_sam_account(account, go);
+
+ gums_destroy_object(&go);
+ return ret;
+}
+
+/***************************************************************************
+ Search by SID
+ **************************************************************************/
+
+static NTSTATUS gums_getsampwsid(struct pdb_methods *methods, SAM_ACCOUNT *account, const DOM_SID *sid)
+{
+ NTSTATUS ret;
+ GUMS_OBJECT *go;
+ struct gums_gw_data *ggwd = (struct gums_gw_data *)(methods->private_data);
+
+ if (!account || !sid)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = ggwd->fns->get_object_from_sid(&go, sid, GUMS_OBJ_NORMAL_USER))) {
+ DEBUG(10, ("gums_getsampwsid: unable to find account with sid %s", sid_string_static(sid)));
+ return ret;
+ }
+
+ ret = gums_object_to_sam_account(account, go);
+
+ gums_destroy_object(&go);
+ return ret;
+}
+
+/***************************************************************************
+ Search by rid
+ **************************************************************************/
+
+#if 0
+
+static NTSTATUS gums_getsampwrid (struct pdb_methods *methods,
+ SAM_ACCOUNT *account, uint32 rid)
+{
+ DOM_SID sid;
+
+ sid_copy(&sid, get_global_sam_sid());
+ sid_append_rid(&sid, rid);
+ gums_getsampwsid(methods, account, &sid);
+
+ return NT_STATUS_OK;
+}
+
+#endif
+
+/***************************************************************************
+ Updates a SAM_ACCOUNT
+
+ This isn't a particulary practical option for pdb_guest. We certainly don't
+ want to twidde the filesystem, so what should we do?
+
+ Current plan is to transparently add the account. It should appear
+ as if the pdb_guest version was modified, but its actually stored somehwere.
+ ****************************************************************************/
+
+static NTSTATUS gums_add_sam_account (struct pdb_methods *methods, SAM_ACCOUNT *account)
+{
+ NTSTATUS ret;
+ GUMS_OBJECT *go;
+ struct gums_gw_data *ggwd = (struct gums_gw_data *)(methods->private_data);
+
+ if (!account)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = gums_create_object(&go, GUMS_OBJ_NORMAL_USER))) {
+ DEBUG(0, ("gums_add_sam_account: error occurred while creating gums object!\n"));
+ return ret;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = sam_account_to_gums_object(go, account))) {
+ DEBUG(0, ("gums_add_sam_account: error occurred while converting object!\n"));
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = ggwd->fns->set_object(go))) {
+ DEBUG(0, ("gums_add_sam_account: unable to store account!\n"));
+ goto done;
+ }
+
+done:
+ gums_destroy_object(&go);
+ return ret;
+}
+
+static NTSTATUS gums_update_sam_account (struct pdb_methods *methods, SAM_ACCOUNT *account)
+{
+ NTSTATUS ret;
+ GUMS_OBJECT *go;
+ struct gums_gw_data *ggwd = (struct gums_gw_data *)(methods->private_data);
+
+ if (!account)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = ggwd->fns->get_object_from_sid(&go, pdb_get_user_sid(account), GUMS_OBJ_NORMAL_USER))) {
+ DEBUG(0, ("gums_update_sam_account: update on invalid account!\n"));
+ return ret;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = sam_account_to_gums_object(go, account))) {
+ DEBUG(0, ("gums_update_sam_account: error occurred while converting object!\n"));
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = ggwd->fns->set_object(go))) {
+ DEBUG(0, ("gums_update_sam_account: unable to store account!\n"));
+ goto done;
+ }
+
+done:
+ gums_destroy_object(&go);
+ return ret;
+}
+
+static NTSTATUS gums_delete_sam_account (struct pdb_methods *methods, SAM_ACCOUNT *account)
+{
+ NTSTATUS ret;
+ struct gums_gw_data *ggwd = (struct gums_gw_data *)(methods->private_data);
+
+ if (!account)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = ggwd->fns->delete_object(pdb_get_user_sid(account)))) {
+ DEBUG(0, ("gums_add_sam_account: unable to store account!\n"));
+ }
+
+ return ret;
+}
+
+
+static void free_gw_private_data(void **vp)
+{
+ struct gums_gw_data *ggwd = (struct gums_gw_data *)vp;
+ ggwd->fns->free_private_data(&(ggwd->fns->private_data));
+ ggwd->fns = NULL;
+ ggwd->handle = NULL;
+ SAFE_FREE(vp);
+}
+
+NTSTATUS pdb_init_gums_gateway(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
+{
+ NTSTATUS ret;
+ struct gums_gw_data *ggwd;
+
+ if (!pdb_context) {
+ DEBUG(0, ("invalid pdb_context specified\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = gums_setup_backend(lp_gums_backend()))) {
+ DEBUG(0, ("pdb_init_gums_gateway: initialization error!\n"));
+ return ret;
+ }
+
+ ggwd = (struct gums_gw_data *)malloc(sizeof(struct gums_gw_data));
+ if (!ggwd)
+ return NT_STATUS_NO_MEMORY;
+ memset(ggwd, 0, sizeof(struct gums_gw_data));
+
+ if (!NT_STATUS_IS_OK(ret = get_gums_fns(&(ggwd->fns)))) {
+ goto error;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
+ goto error;
+ }
+
+ (*pdb_method)->name = "gums_gateway";
+
+ (*pdb_method)->setsampwent = gums_setsampwent;
+ (*pdb_method)->getsampwent = gums_getsampwent;
+ (*pdb_method)->endsampwent = gums_endsampwent;
+ (*pdb_method)->getsampwnam = gums_getsampwnam;
+ (*pdb_method)->getsampwsid = gums_getsampwsid;
+ (*pdb_method)->add_sam_account = gums_add_sam_account;
+ (*pdb_method)->update_sam_account = gums_update_sam_account;
+ (*pdb_method)->delete_sam_account = gums_delete_sam_account;
+
+ /* we should do no group mapping here */
+/* (*pdb_method)->getgrsid = gums_getgrsid;
+ (*pdb_method)->getgrgid = gums_getgrgid;
+ (*pdb_method)->getgrnam = gums_getgrnam;
+ (*pdb_method)->add_group_mapping_entry = gums_add_group_mapping_entry;
+ (*pdb_method)->update_group_mapping_entry = gums_update_group_mapping_entry;
+ (*pdb_method)->delete_group_mapping_entry = gums_delete_group_mapping_entry;
+ (*pdb_method)->enum_group_mapping = gums_enum_group_mapping;*/
+
+ /* we do not handle groups in guest backend */
+/* FIXME
+ (*pdb_method)->get_group_info_by_sid = gums_get_group_info_by_sid;
+ (*pdb_method)->get_group_list = gums_get_group_list;
+ (*pdb_method)->get_group_sids = gums_get_group_sids;
+ (*pdb_method)->add_group = gums_add_group;
+ (*pdb_method)->update_group = gums_update_group;
+ (*pdb_method)->delete_group = gums_delete_group;
+ (*pdb_method)->add_sid_to_group = gums_add_sid_to_group;
+ (*pdb_method)->remove_sid_from_group = gums_remove_sid_from_group;
+ (*pdb_method)->get_group_info_by_name = gums_get_group_info_by_name;
+ (*pdb_method)->get_group_info_by_nt_name = gums_get_group_info_by_nt_name;
+ (*pdb_method)->get_group_uids = gums_get_group_uids;
+*/
+
+ (*pdb_method)->private_data = ggwd;
+ (*pdb_method)->free_private_data = free_gw_private_data;
+
+ return NT_STATUS_OK;
+
+error:
+ SAFE_FREE(ggwd);
+ return ret;
+}
+
+NTSTATUS pdb_gums_init(void)
+{
+ return smb_register_passdb(PASSDB_INTERFACE_VERSION, "gums", pdb_init_gums_gateway);
+}
+
diff --git a/source/passdb/pdb_interface.c b/source/passdb/pdb_interface.c
new file mode 100644
index 00000000000..b1620aa9eb6
--- /dev/null
+++ b/source/passdb/pdb_interface.c
@@ -0,0 +1,1601 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett 2002
+ Copyright (C) Jelmer Vernooij 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+static struct pdb_init_function_entry *backends = NULL;
+
+static void lazy_initialize_passdb(void)
+{
+ static BOOL initialized = False;
+ if(initialized)return;
+ static_init_pdb;
+ initialized = True;
+}
+
+static struct pdb_init_function_entry *pdb_find_backend_entry(const char *name);
+
+/*******************************************************************
+ Clean up uninitialised passwords. The only way to tell
+ that these values are not 'real' is that they do not
+ have a valid last set time. Instead, the value is fixed at 0.
+ Therefore we use that as the key for 'is this a valid password'.
+ However, it is perfectly valid to have a 'default' last change
+ time, such LDAP with a missing attribute would produce.
+********************************************************************/
+
+static void pdb_force_pw_initialization(SAM_ACCOUNT *pass)
+{
+ const char *lm_pwd, *nt_pwd;
+
+ /* only reset a password if the last set time has been
+ explicitly been set to zero. A default last set time
+ is ignored */
+
+ if ( (pdb_get_init_flags(pass, PDB_PASSLASTSET) != PDB_DEFAULT)
+ && (pdb_get_pass_last_set_time(pass) == 0) )
+ {
+
+ if (pdb_get_init_flags(pass, PDB_LMPASSWD) != PDB_DEFAULT)
+ {
+ lm_pwd = pdb_get_lanman_passwd(pass);
+ if (lm_pwd)
+ pdb_set_lanman_passwd(pass, NULL, PDB_CHANGED);
+ }
+ if (pdb_get_init_flags(pass, PDB_NTPASSWD) != PDB_DEFAULT)
+ {
+ nt_pwd = pdb_get_nt_passwd(pass);
+ if (nt_pwd)
+ pdb_set_nt_passwd(pass, NULL, PDB_CHANGED);
+ }
+ }
+
+ return;
+}
+
+NTSTATUS smb_register_passdb(int version, const char *name, pdb_init_function init)
+{
+ struct pdb_init_function_entry *entry = backends;
+
+ if(version != PASSDB_INTERFACE_VERSION) {
+ DEBUG(0,("Can't register passdb backend!\n"
+ "You tried to register a passdb module with PASSDB_INTERFACE_VERSION %d, "
+ "while this version of samba uses version %d\n",
+ version,PASSDB_INTERFACE_VERSION));
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ if (!name || !init) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(5,("Attempting to register passdb backend %s\n", name));
+
+ /* Check for duplicates */
+ if (pdb_find_backend_entry(name)) {
+ DEBUG(0,("There already is a passdb backend registered with the name %s!\n", name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ entry = smb_xmalloc(sizeof(struct pdb_init_function_entry));
+ entry->name = smb_xstrdup(name);
+ entry->init = init;
+
+ DLIST_ADD(backends, entry);
+ DEBUG(5,("Successfully added passdb backend '%s'\n", name));
+ return NT_STATUS_OK;
+}
+
+static struct pdb_init_function_entry *pdb_find_backend_entry(const char *name)
+{
+ struct pdb_init_function_entry *entry = backends;
+
+ while(entry) {
+ if (strcmp(entry->name, name)==0) return entry;
+ entry = entry->next;
+ }
+
+ return NULL;
+}
+
+static NTSTATUS context_setsampwent(struct pdb_context *context, BOOL update)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if (!context) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ context->pwent_methods = context->pdb_methods;
+
+ if (!context->pwent_methods) {
+ /* No passdbs at all */
+ return ret;
+ }
+
+ while (NT_STATUS_IS_ERR(ret = context->pwent_methods->setsampwent(context->pwent_methods, update))) {
+ context->pwent_methods = context->pwent_methods->next;
+ if (context->pwent_methods == NULL)
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ return ret;
+}
+
+static void context_endsampwent(struct pdb_context *context)
+{
+ if ((!context)){
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return;
+ }
+
+ if (context->pwent_methods && context->pwent_methods->endsampwent)
+ context->pwent_methods->endsampwent(context->pwent_methods);
+
+ /* So we won't get strange data when calling getsampwent now */
+ context->pwent_methods = NULL;
+}
+
+static NTSTATUS context_getsampwent(struct pdb_context *context, SAM_ACCOUNT *user)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pwent_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+ /* Loop until we find something useful */
+ while (NT_STATUS_IS_ERR(ret = context->pwent_methods->getsampwent(context->pwent_methods, user))) {
+
+ context->pwent_methods->endsampwent(context->pwent_methods);
+
+ context->pwent_methods = context->pwent_methods->next;
+
+ /* All methods are checked now. There are no more entries */
+ if (context->pwent_methods == NULL)
+ return ret;
+
+ context->pwent_methods->setsampwent(context->pwent_methods, False);
+ }
+ user->methods = context->pwent_methods;
+ pdb_force_pw_initialization(user);
+ return ret;
+}
+
+static NTSTATUS context_getsampwnam(struct pdb_context *context, SAM_ACCOUNT *sam_acct, const char *username)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ struct pdb_methods *curmethods;
+ if ((!context)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+ curmethods = context->pdb_methods;
+ while (curmethods){
+ if (NT_STATUS_IS_OK(ret = curmethods->getsampwnam(curmethods, sam_acct, username))) {
+ pdb_force_pw_initialization(sam_acct);
+ sam_acct->methods = curmethods;
+ return ret;
+ }
+ curmethods = curmethods->next;
+ }
+
+ return ret;
+}
+
+static NTSTATUS context_getsampwsid(struct pdb_context *context, SAM_ACCOUNT *sam_acct, const DOM_SID *sid)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ struct pdb_methods *curmethods;
+ if ((!context)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ curmethods = context->pdb_methods;
+
+ while (curmethods){
+ if (NT_STATUS_IS_OK(ret = curmethods->getsampwsid(curmethods, sam_acct, sid))) {
+ pdb_force_pw_initialization(sam_acct);
+ sam_acct->methods = curmethods;
+ return ret;
+ }
+ curmethods = curmethods->next;
+ }
+
+ return ret;
+}
+
+static NTSTATUS context_add_sam_account(struct pdb_context *context, SAM_ACCOUNT *sam_acct)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ /** @todo This is where a 're-read on add' should be done */
+ /* We now add a new account to the first database listed.
+ * Should we? */
+
+ return context->pdb_methods->add_sam_account(context->pdb_methods, sam_acct);
+}
+
+static NTSTATUS context_update_sam_account(struct pdb_context *context, SAM_ACCOUNT *sam_acct)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if (!context) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ if (!sam_acct || !sam_acct->methods){
+ DEBUG(0, ("invalid sam_acct specified\n"));
+ return ret;
+ }
+
+ /** @todo This is where a 're-read on update' should be done */
+
+ return sam_acct->methods->update_sam_account(sam_acct->methods, sam_acct);
+}
+
+static NTSTATUS context_delete_sam_account(struct pdb_context *context, SAM_ACCOUNT *sam_acct)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ struct pdb_methods *pdb_selected;
+ if (!context) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ if (!sam_acct->methods){
+ pdb_selected = context->pdb_methods;
+ /* There's no passdb backend specified for this account.
+ * Try to delete it in every passdb available
+ * Needed to delete accounts in smbpasswd that are not
+ * in /etc/passwd.
+ */
+ while (pdb_selected){
+ if (NT_STATUS_IS_OK(ret = pdb_selected->delete_sam_account(pdb_selected, sam_acct))) {
+ return ret;
+ }
+ pdb_selected = pdb_selected->next;
+ }
+ return ret;
+ }
+
+ if (!sam_acct->methods->delete_sam_account){
+ DEBUG(0,("invalid sam_acct->methods->delete_sam_account\n"));
+ return ret;
+ }
+
+ return sam_acct->methods->delete_sam_account(sam_acct->methods, sam_acct);
+}
+
+static NTSTATUS context_getgrsid(struct pdb_context *context,
+ GROUP_MAP *map, DOM_SID sid)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ struct pdb_methods *curmethods;
+ if ((!context)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+ curmethods = context->pdb_methods;
+ while (curmethods){
+ ret = curmethods->getgrsid(curmethods, map, sid);
+ if (NT_STATUS_IS_OK(ret)) {
+ map->methods = curmethods;
+ return ret;
+ }
+ curmethods = curmethods->next;
+ }
+
+ return ret;
+}
+
+static NTSTATUS context_getgrgid(struct pdb_context *context,
+ GROUP_MAP *map, gid_t gid)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ struct pdb_methods *curmethods;
+ if ((!context)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+ curmethods = context->pdb_methods;
+ while (curmethods){
+ ret = curmethods->getgrgid(curmethods, map, gid);
+ if (NT_STATUS_IS_OK(ret)) {
+ map->methods = curmethods;
+ return ret;
+ }
+ curmethods = curmethods->next;
+ }
+
+ return ret;
+}
+
+static NTSTATUS context_getgrnam(struct pdb_context *context,
+ GROUP_MAP *map, const char *name)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ struct pdb_methods *curmethods;
+ if ((!context)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+ curmethods = context->pdb_methods;
+ while (curmethods){
+ ret = curmethods->getgrnam(curmethods, map, name);
+ if (NT_STATUS_IS_OK(ret)) {
+ map->methods = curmethods;
+ return ret;
+ }
+ curmethods = curmethods->next;
+ }
+
+ return ret;
+}
+
+static NTSTATUS context_add_group_mapping_entry(struct pdb_context *context,
+ GROUP_MAP *map)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->pdb_methods->add_group_mapping_entry(context->pdb_methods,
+ map);
+}
+
+static NTSTATUS context_update_group_mapping_entry(struct pdb_context *context,
+ GROUP_MAP *map)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->
+ pdb_methods->update_group_mapping_entry(context->pdb_methods, map);
+}
+
+static NTSTATUS context_delete_group_mapping_entry(struct pdb_context *context,
+ DOM_SID sid)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->
+ pdb_methods->delete_group_mapping_entry(context->pdb_methods, sid);
+}
+
+static NTSTATUS context_enum_group_mapping(struct pdb_context *context,
+ enum SID_NAME_USE sid_name_use,
+ GROUP_MAP **rmap, int *num_entries,
+ BOOL unix_only)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->pdb_methods->enum_group_mapping(context->pdb_methods,
+ sid_name_use, rmap,
+ num_entries, unix_only);
+}
+
+static NTSTATUS context_find_alias(struct pdb_context *context,
+ const char *name, DOM_SID *sid)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->pdb_methods->find_alias(context->pdb_methods,
+ name, sid);
+}
+
+static NTSTATUS context_create_alias(struct pdb_context *context,
+ const char *name, uint32 *rid)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->pdb_methods->create_alias(context->pdb_methods,
+ name, rid);
+}
+
+static NTSTATUS context_delete_alias(struct pdb_context *context,
+ const DOM_SID *sid)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->pdb_methods->delete_alias(context->pdb_methods, sid);
+}
+
+static NTSTATUS context_enum_aliases(struct pdb_context *context,
+ const DOM_SID *sid,
+ uint32 start_idx, uint32 max_entries,
+ uint32 *num_aliases,
+ struct acct_info **info)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->pdb_methods->enum_aliases(context->pdb_methods,
+ sid, start_idx, max_entries,
+ num_aliases, info);
+}
+
+static NTSTATUS context_get_aliasinfo(struct pdb_context *context,
+ const DOM_SID *sid,
+ struct acct_info *info)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->pdb_methods->get_aliasinfo(context->pdb_methods,
+ sid, info);
+}
+
+static NTSTATUS context_set_aliasinfo(struct pdb_context *context,
+ const DOM_SID *sid,
+ struct acct_info *info)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->pdb_methods->set_aliasinfo(context->pdb_methods,
+ sid, info);
+}
+
+static NTSTATUS context_add_aliasmem(struct pdb_context *context,
+ const DOM_SID *alias,
+ const DOM_SID *member)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->pdb_methods->add_aliasmem(context->pdb_methods,
+ alias, member);
+}
+
+static NTSTATUS context_del_aliasmem(struct pdb_context *context,
+ const DOM_SID *alias,
+ const DOM_SID *member)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->pdb_methods->del_aliasmem(context->pdb_methods,
+ alias, member);
+}
+
+static NTSTATUS context_enum_aliasmem(struct pdb_context *context,
+ const DOM_SID *alias, DOM_SID **members,
+ int *num)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->pdb_methods->enum_aliasmem(context->pdb_methods,
+ alias, members, num);
+}
+
+static NTSTATUS context_enum_alias_memberships(struct pdb_context *context,
+ const DOM_SID *sid,
+ DOM_SID **aliases, int *num)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->pdb_methods->
+ enum_alias_memberships(context->pdb_methods, sid, aliases,
+ num);
+}
+
+static NTSTATUS context_settrustpwent(struct pdb_context *context)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ struct pdb_methods *cur_methods;
+
+ if (!context) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ cur_methods = context->pdb_methods;
+
+ while (cur_methods) {
+ ret = cur_methods->settrustpwent(cur_methods);
+ if (NT_STATUS_IS_OK(ret)) {
+ context->pdb_methods = cur_methods;
+ return ret;
+ }
+ cur_methods = cur_methods->next;
+ }
+
+ return ret;
+}
+
+static NTSTATUS context_gettrustpwent(struct pdb_context *context,
+ SAM_TRUST_PASSWD *trust)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ struct pdb_methods *cur_methods;
+
+ if (!context) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ cur_methods = context->pdb_methods;
+
+ while (cur_methods) {
+ ret = cur_methods->gettrustpwent(cur_methods, trust);
+ if (!NT_STATUS_IS_ERR(ret)) {
+ /* prevent from segfaulting when gettrustpwent
+ was called just to rewind enumeration */
+ if (trust) trust->methods = cur_methods;
+ return ret;
+ }
+ cur_methods = cur_methods->next;
+ }
+
+ return ret;
+}
+
+static NTSTATUS context_gettrustpwnam(struct pdb_context *context,
+ SAM_TRUST_PASSWD *trust,
+ const char *name)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ struct pdb_methods *cur_methods;
+
+ if (!context) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ cur_methods = context->pdb_methods;
+
+ while (cur_methods) {
+ ret = cur_methods->gettrustpwnam(cur_methods, trust, name);
+ if (NT_STATUS_IS_OK(ret)) {
+ trust->methods = cur_methods;
+ return ret;
+ }
+ cur_methods = cur_methods->next;
+ }
+
+ return ret;
+}
+
+static NTSTATUS context_gettrustpwsid(struct pdb_context *context,
+ SAM_TRUST_PASSWD *trust,
+ const DOM_SID *sid)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ struct pdb_methods *cur_methods;
+
+ if (!context) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ cur_methods = context->pdb_methods;
+
+ while (cur_methods) {
+ ret = cur_methods->gettrustpwsid(cur_methods, trust, sid);
+ if (NT_STATUS_IS_OK(ret)) {
+ trust->methods = cur_methods;
+ return ret;
+ }
+ cur_methods = cur_methods->next;
+ }
+
+ return ret;
+}
+
+static NTSTATUS context_add_trust_passwd(struct pdb_context *context,
+ SAM_TRUST_PASSWD *trust)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if (!context) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->pdb_methods->add_trust_passwd(context->pdb_methods, trust);
+}
+
+static NTSTATUS context_update_trust_passwd(struct pdb_context *context,
+ SAM_TRUST_PASSWD *trust)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if (!context) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ if (!trust || !trust->methods) {
+ DEBUG(0, ("invalid trust pointer specified!\n"));
+ return ret;
+ }
+
+ return trust->methods->update_trust_passwd(trust->methods, trust);
+}
+
+static NTSTATUS context_delete_trust_passwd(struct pdb_context *context,
+ SAM_TRUST_PASSWD *trust)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if (!context) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ if (!trust || !trust->methods) {
+ DEBUG(0, ("invalid trust pointer specified!\n"));
+ return ret;
+ }
+
+ return trust->methods->delete_trust_passwd(trust->methods, trust);
+}
+
+static NTSTATUS context_add_sid_to_privilege(struct pdb_context *context, const char *priv_name, const DOM_SID *sid)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ struct pdb_methods *curmethods;
+ if ((!context)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+ curmethods = context->pdb_methods;
+ while (curmethods){
+ if (NT_STATUS_IS_OK(ret = curmethods->add_sid_to_privilege(curmethods, priv_name, sid))) {
+ return ret;
+ }
+ curmethods = curmethods->next;
+ }
+
+ return ret;
+}
+
+static NTSTATUS context_remove_sid_from_privilege(struct pdb_context *context, const char *priv_name, const DOM_SID *sid)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ struct pdb_methods *curmethods;
+ if ((!context)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+ curmethods = context->pdb_methods;
+ while (curmethods){
+ if (NT_STATUS_IS_OK(ret = curmethods->remove_sid_from_privilege(curmethods, priv_name, sid))) {
+ return ret;
+ }
+ curmethods = curmethods->next;
+ }
+
+ return ret;
+}
+
+static NTSTATUS context_get_privilege_set(struct pdb_context *context, DOM_SID *sid_list, int num_sids, PRIVILEGE_SET *privset)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ struct pdb_methods *curmethods;
+ if ((!context)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+ curmethods = context->pdb_methods;
+ while (curmethods){
+ if (NT_STATUS_IS_OK(ret = curmethods->get_privilege_set(curmethods, sid_list, num_sids, privset))) {
+ return ret;
+ }
+ curmethods = curmethods->next;
+ }
+
+ return ret;
+}
+
+static NTSTATUS context_get_privilege_entry(struct pdb_context *context, const char *privname, char **sid_list)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ struct pdb_methods *curmethods;
+ if ((!context)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+ curmethods = context->pdb_methods;
+ while (curmethods){
+ if (NT_STATUS_IS_OK(ret = curmethods->get_privilege_entry(curmethods, privname, sid_list))) {
+ return ret;
+ }
+ curmethods = curmethods->next;
+ }
+
+ return ret;
+}
+
+/******************************************************************
+ Free and cleanup a pdb context, any associated data and anything
+ that the attached modules might have associated.
+ *******************************************************************/
+
+static void free_pdb_context(struct pdb_context **context)
+{
+ struct pdb_methods *pdb_selected = (*context)->pdb_methods;
+
+ while (pdb_selected){
+ if(pdb_selected->free_private_data)
+ pdb_selected->free_private_data(&(pdb_selected->private_data));
+ pdb_selected = pdb_selected->next;
+ }
+
+ talloc_destroy((*context)->mem_ctx);
+ *context = NULL;
+}
+
+/******************************************************************
+ Make a pdb_methods from scratch
+ *******************************************************************/
+
+static NTSTATUS make_pdb_methods_name(struct pdb_methods **methods, struct pdb_context *context, const char *selected)
+{
+ char *module_name = smb_xstrdup(selected);
+ char *module_location = NULL, *p;
+ struct pdb_init_function_entry *entry;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+
+ lazy_initialize_passdb();
+
+ p = strchr(module_name, ':');
+
+ if (p) {
+ *p = 0;
+ module_location = p+1;
+ trim_char(module_location, ' ', ' ');
+ }
+
+ trim_char(module_name, ' ', ' ');
+
+
+ DEBUG(5,("Attempting to find an passdb backend to match %s (%s)\n", selected, module_name));
+
+ entry = pdb_find_backend_entry(module_name);
+
+ /* Try to find a module that contains this module */
+ if (!entry) {
+ DEBUG(2,("No builtin backend found, trying to load plugin\n"));
+ if(NT_STATUS_IS_OK(smb_probe_module("pdb", module_name)) && !(entry = pdb_find_backend_entry(module_name))) {
+ DEBUG(0,("Plugin is available, but doesn't register passdb backend %s\n", module_name));
+ SAFE_FREE(module_name);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ /* No such backend found */
+ if(!entry) {
+ DEBUG(0,("No builtin nor plugin backend for %s found\n", module_name));
+ SAFE_FREE(module_name);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(5,("Found pdb backend %s\n", module_name));
+ nt_status = entry->init(context, methods, module_location);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(5,("pdb backend %s has a valid init\n", selected));
+ } else {
+ DEBUG(0,("pdb backend %s did not correctly init (error was %s)\n", selected, nt_errstr(nt_status)));
+ }
+ SAFE_FREE(module_name);
+ return nt_status;
+}
+
+/******************************************************************
+ Make a pdb_context from scratch.
+ *******************************************************************/
+
+static NTSTATUS make_pdb_context(struct pdb_context **context)
+{
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_init("pdb_context internal allocation context");
+
+ if (!mem_ctx) {
+ DEBUG(0, ("make_pdb_context: talloc init failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *context = talloc(mem_ctx, sizeof(**context));
+ if (!*context) {
+ DEBUG(0, ("make_pdb_context: talloc failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCTP(*context);
+
+ (*context)->mem_ctx = mem_ctx;
+
+ (*context)->pdb_setsampwent = context_setsampwent;
+ (*context)->pdb_endsampwent = context_endsampwent;
+ (*context)->pdb_getsampwent = context_getsampwent;
+ (*context)->pdb_getsampwnam = context_getsampwnam;
+ (*context)->pdb_getsampwsid = context_getsampwsid;
+ (*context)->pdb_add_sam_account = context_add_sam_account;
+ (*context)->pdb_update_sam_account = context_update_sam_account;
+ (*context)->pdb_delete_sam_account = context_delete_sam_account;
+ (*context)->pdb_getgrsid = context_getgrsid;
+ (*context)->pdb_getgrgid = context_getgrgid;
+ (*context)->pdb_getgrnam = context_getgrnam;
+ (*context)->pdb_add_group_mapping_entry = context_add_group_mapping_entry;
+ (*context)->pdb_update_group_mapping_entry = context_update_group_mapping_entry;
+ (*context)->pdb_delete_group_mapping_entry = context_delete_group_mapping_entry;
+ (*context)->pdb_enum_group_mapping = context_enum_group_mapping;
+ (*context)->pdb_find_alias = context_find_alias;
+ (*context)->pdb_create_alias = context_create_alias;
+ (*context)->pdb_delete_alias = context_delete_alias;
+ (*context)->pdb_enum_aliases = context_enum_aliases;
+ (*context)->pdb_get_aliasinfo = context_get_aliasinfo;
+ (*context)->pdb_set_aliasinfo = context_set_aliasinfo;
+ (*context)->pdb_add_aliasmem = context_add_aliasmem;
+ (*context)->pdb_del_aliasmem = context_del_aliasmem;
+ (*context)->pdb_enum_aliasmem = context_enum_aliasmem;
+ (*context)->pdb_enum_alias_memberships = context_enum_alias_memberships;
+ (*context)->pdb_settrustpwent = context_settrustpwent;
+ (*context)->pdb_gettrustpwent = context_gettrustpwent;
+ (*context)->pdb_gettrustpwnam = context_gettrustpwnam;
+ (*context)->pdb_gettrustpwsid = context_gettrustpwsid;
+ (*context)->pdb_add_trust_passwd = context_add_trust_passwd;
+ (*context)->pdb_update_trust_passwd = context_update_trust_passwd;
+ (*context)->pdb_delete_trust_passwd = context_delete_trust_passwd;
+ (*context)->pdb_add_sid_to_privilege = context_add_sid_to_privilege;
+ (*context)->pdb_remove_sid_from_privilege = context_remove_sid_from_privilege;
+ (*context)->pdb_get_privilege_set = context_get_privilege_set;
+ (*context)->pdb_get_privilege_entry = context_get_privilege_entry;
+
+ (*context)->free_fn = free_pdb_context;
+
+ return NT_STATUS_OK;
+}
+
+
+/******************************************************************
+ Make a pdb_context, given an array of strings
+ *******************************************************************/
+
+NTSTATUS make_pdb_context_list(struct pdb_context **context, const char **selected)
+{
+ int i = 0;
+ struct pdb_methods *curmethods, *tmpmethods;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ BOOL have_guest = False;
+
+ if (!NT_STATUS_IS_OK(nt_status = make_pdb_context(context))) {
+ return nt_status;
+ }
+
+ if (!selected) {
+ DEBUG(0, ("ERROR: empty passdb backend list!\n"));
+ return nt_status;
+ }
+
+ while (selected[i]){
+ if (strcmp(selected[i], "guest") == 0) {
+ have_guest = True;
+ }
+ /* Try to initialise pdb */
+ DEBUG(5,("Trying to load: %s\n", selected[i]));
+ if (!NT_STATUS_IS_OK(nt_status = make_pdb_methods_name(&curmethods, *context, selected[i]))) {
+ DEBUG(1, ("Loading %s failed!\n", selected[i]));
+ free_pdb_context(context);
+ return nt_status;
+ }
+ curmethods->parent = *context;
+ DLIST_ADD_END((*context)->pdb_methods, curmethods, tmpmethods);
+ i++;
+ }
+
+ if (have_guest)
+ return NT_STATUS_OK;
+
+ if ( (lp_guestaccount() == NULL) ||
+ (*lp_guestaccount() == '\0') ) {
+ /* We explicitly don't want guest access. No idea what
+ else that breaks, but be it that way. */
+ return NT_STATUS_OK;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = make_pdb_methods_name(&curmethods,
+ *context,
+ "guest"))) {
+ DEBUG(1, ("Loading guest module failed!\n"));
+ free_pdb_context(context);
+ return nt_status;
+ }
+
+ curmethods->parent = *context;
+ DLIST_ADD_END((*context)->pdb_methods, curmethods, tmpmethods);
+
+ return NT_STATUS_OK;
+}
+
+/******************************************************************
+ Make a pdb_context, given a text string.
+ *******************************************************************/
+
+NTSTATUS make_pdb_context_string(struct pdb_context **context, const char *selected)
+{
+ NTSTATUS ret;
+ char **newsel = str_list_make(selected, NULL);
+ ret = make_pdb_context_list(context, (const char **)newsel);
+ str_list_free(&newsel);
+ return ret;
+}
+
+/******************************************************************
+ Return an already initialised pdb_context, to facilitate backward
+ compatibility (see functions below).
+*******************************************************************/
+
+static struct pdb_context *pdb_get_static_context(BOOL reload)
+{
+ static struct pdb_context *pdb_context = NULL;
+
+ if ((pdb_context) && (reload)) {
+ pdb_context->free_fn(&pdb_context);
+ if (!NT_STATUS_IS_OK(make_pdb_context_list(&pdb_context, lp_passdb_backend()))) {
+ return NULL;
+ }
+ }
+
+ if (!pdb_context) {
+ if (!NT_STATUS_IS_OK(make_pdb_context_list(&pdb_context, lp_passdb_backend()))) {
+ return NULL;
+ }
+ }
+
+ return pdb_context;
+}
+
+/******************************************************************
+ Backward compatibility functions for the original passdb interface
+*******************************************************************/
+
+BOOL pdb_setsampwent(BOOL update)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->pdb_setsampwent(pdb_context, update));
+}
+
+void pdb_endsampwent(void)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return;
+ }
+
+ pdb_context->pdb_endsampwent(pdb_context);
+}
+
+BOOL pdb_getsampwent(SAM_ACCOUNT *user)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->pdb_getsampwent(pdb_context, user));
+}
+
+BOOL pdb_getsampwnam(SAM_ACCOUNT *sam_acct, const char *username)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->pdb_getsampwnam(pdb_context, sam_acct, username));
+}
+
+BOOL pdb_getsampwsid(SAM_ACCOUNT *sam_acct, const DOM_SID *sid)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->pdb_getsampwsid(pdb_context, sam_acct, sid));
+}
+
+BOOL pdb_add_sam_account(SAM_ACCOUNT *sam_acct)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+ const char *lm_pw, *nt_pw;
+ uint16 acb_flags;
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ /* disable acccounts with no passwords (that has not
+ been allowed by the ACB_PWNOTREQ bit */
+
+ lm_pw = pdb_get_lanman_passwd( sam_acct );
+ nt_pw = pdb_get_nt_passwd( sam_acct );
+ acb_flags = pdb_get_acct_ctrl( sam_acct );
+ if ( !lm_pw && !nt_pw && !(acb_flags&ACB_PWNOTREQ) ) {
+ acb_flags |= ACB_DISABLED;
+ pdb_set_acct_ctrl( sam_acct, acb_flags, PDB_CHANGED );
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->pdb_add_sam_account(pdb_context, sam_acct));
+}
+
+BOOL pdb_update_sam_account(SAM_ACCOUNT *sam_acct)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+ const char *lm_pw, *nt_pw;
+ uint16 acb_flags;
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ /* disable acccounts with no passwords (that has not
+ been allowed by the ACB_PWNOTREQ bit */
+
+ lm_pw = pdb_get_lanman_passwd( sam_acct );
+ nt_pw = pdb_get_nt_passwd( sam_acct );
+ acb_flags = pdb_get_acct_ctrl( sam_acct );
+ if ( !lm_pw && !nt_pw && !(acb_flags&ACB_PWNOTREQ) ) {
+ acb_flags |= ACB_DISABLED;
+ pdb_set_acct_ctrl( sam_acct, acb_flags, PDB_CHANGED );
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->pdb_update_sam_account(pdb_context, sam_acct));
+}
+
+BOOL pdb_delete_sam_account(SAM_ACCOUNT *sam_acct)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->pdb_delete_sam_account(pdb_context, sam_acct));
+}
+
+BOOL pdb_getgrsid(GROUP_MAP *map, DOM_SID sid)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_getgrsid(pdb_context, map, sid));
+}
+
+BOOL pdb_getgrgid(GROUP_MAP *map, gid_t gid)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_getgrgid(pdb_context, map, gid));
+}
+
+BOOL pdb_getgrnam(GROUP_MAP *map, const char *name)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_getgrnam(pdb_context, map, name));
+}
+
+BOOL pdb_add_group_mapping_entry(GROUP_MAP *map)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_add_group_mapping_entry(pdb_context, map));
+}
+
+BOOL pdb_update_group_mapping_entry(GROUP_MAP *map)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_update_group_mapping_entry(pdb_context, map));
+}
+
+BOOL pdb_delete_group_mapping_entry(DOM_SID sid)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_delete_group_mapping_entry(pdb_context, sid));
+}
+
+BOOL pdb_enum_group_mapping(enum SID_NAME_USE sid_name_use, GROUP_MAP **rmap,
+ int *num_entries, BOOL unix_only)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_enum_group_mapping(pdb_context, sid_name_use,
+ rmap, num_entries, unix_only));
+}
+
+BOOL pdb_find_alias(const char *name, DOM_SID *sid)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->pdb_find_alias(pdb_context,
+ name, sid));
+}
+
+BOOL pdb_create_alias(const char *name, uint32 *rid)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->pdb_create_alias(pdb_context,
+ name, rid));
+}
+
+BOOL pdb_delete_alias(const DOM_SID *sid)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->pdb_delete_alias(pdb_context,
+ sid));
+
+}
+
+BOOL pdb_enum_aliases(const DOM_SID *sid, uint32 start_idx, uint32 max_entries,
+ uint32 *num_aliases, struct acct_info **info)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->pdb_enum_aliases(pdb_context, sid,
+ start_idx,
+ max_entries,
+ num_aliases,
+ info));
+}
+
+BOOL pdb_get_aliasinfo(const DOM_SID *sid, struct acct_info *info)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->pdb_get_aliasinfo(pdb_context, sid,
+ info));
+}
+
+BOOL pdb_set_aliasinfo(const DOM_SID *sid, struct acct_info *info)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->pdb_set_aliasinfo(pdb_context, sid,
+ info));
+}
+
+BOOL pdb_add_aliasmem(const DOM_SID *alias, const DOM_SID *member)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_add_aliasmem(pdb_context, alias, member));
+}
+
+BOOL pdb_del_aliasmem(const DOM_SID *alias, const DOM_SID *member)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_del_aliasmem(pdb_context, alias, member));
+}
+
+BOOL pdb_enum_aliasmem(const DOM_SID *alias,
+ DOM_SID **members, int *num_members)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_enum_aliasmem(pdb_context, alias,
+ members, num_members));
+}
+
+BOOL pdb_enum_alias_memberships(const DOM_SID *sid,
+ DOM_SID **aliases, int *num)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_enum_alias_memberships(pdb_context, sid,
+ aliases, num));
+}
+
+BOOL pdb_add_sid_to_privilege(char *priv_name, DOM_SID *sid)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_add_sid_to_privilege(pdb_context, priv_name, sid));
+}
+
+BOOL pdb_remove_sid_from_privilege(char *priv_name, DOM_SID *sid)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_remove_sid_from_privilege(pdb_context, priv_name, sid));
+}
+
+BOOL pdb_get_privilege_set(DOM_SID *sid_list, int num_sids, PRIVILEGE_SET *privset)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_get_privilege_set(pdb_context, sid_list, num_sids, privset));
+}
+
+BOOL pdb_get_privilege_entry(const char *privname, char **sid_list)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_get_privilege_entry(pdb_context, privname, sid_list));
+}
+
+/***************************************************************
+ Initialize the static context (at smbd startup etc).
+
+ If uninitialised, context will auto-init on first use.
+ ***************************************************************/
+
+BOOL initialize_password_db(BOOL reload)
+{
+ return (pdb_get_static_context(reload) != NULL);
+}
+
+
+/***************************************************************************
+ Default implementations of some functions.
+ ****************************************************************************/
+
+static NTSTATUS pdb_default_getsampwnam (struct pdb_methods *methods, SAM_ACCOUNT *user, const char *sname)
+{
+ return NT_STATUS_NO_SUCH_USER;
+}
+
+static NTSTATUS pdb_default_getsampwsid(struct pdb_methods *my_methods, SAM_ACCOUNT * user, const DOM_SID *sid)
+{
+ return NT_STATUS_NO_SUCH_USER;
+}
+
+static NTSTATUS pdb_default_add_sam_account (struct pdb_methods *methods, SAM_ACCOUNT *newpwd)
+{
+ DEBUG(0,("this backend (%s) should not be listed as the first passdb backend! You can't add users to it.\n", methods->name));
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_update_sam_account (struct pdb_methods *methods, SAM_ACCOUNT *newpwd)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_delete_sam_account (struct pdb_methods *methods, SAM_ACCOUNT *pwd)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_setsampwent(struct pdb_methods *methods, BOOL update)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_getsampwent(struct pdb_methods *methods, SAM_ACCOUNT *user)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static void pdb_default_endsampwent(struct pdb_methods *methods)
+{
+ return; /* NT_STATUS_NOT_IMPLEMENTED; */
+}
+
+static NTSTATUS pdb_default_settrustpwent(struct pdb_methods *methods)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_gettrustpwent(struct pdb_methods *methods, SAM_TRUST_PASSWD* trust)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_gettrustpwnam(struct pdb_methods *methods, SAM_TRUST_PASSWD* trust,
+ const char* name)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_gettrustpwsid(struct pdb_methods *methods, SAM_TRUST_PASSWD* trust,
+ const DOM_SID* sid)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_add_trust_passwd(struct pdb_methods *methods, const SAM_TRUST_PASSWD* trust)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_update_trust_passwd(struct pdb_methods *methods, const SAM_TRUST_PASSWD* trust)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_delete_trust_passwd(struct pdb_methods *methods, const SAM_TRUST_PASSWD* trust)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_add_sid_to_privilege(struct pdb_methods *methods, const char *priv_name, const DOM_SID *sid)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_remove_sid_from_privilege(struct pdb_methods *methods, const char *priv_name, const DOM_SID *sid)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_get_privilege_set(struct pdb_methods *methods, DOM_SID *sid_list, int num_sids, PRIVILEGE_SET *privset)
+{
+ /* by default return the empty privilege set as otherwise login will
+ * be denied if a backend does not support privilege sets */
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS pdb_default_get_privilege_entry(struct pdb_methods *methods, const char *privname, char **sid_list)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+NTSTATUS make_pdb_methods(TALLOC_CTX *mem_ctx, PDB_METHODS **methods)
+{
+ *methods = talloc(mem_ctx, sizeof(struct pdb_methods));
+
+ if (!*methods) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCTP(*methods);
+
+ (*methods)->setsampwent = pdb_default_setsampwent;
+ (*methods)->endsampwent = pdb_default_endsampwent;
+ (*methods)->getsampwent = pdb_default_getsampwent;
+ (*methods)->getsampwnam = pdb_default_getsampwnam;
+ (*methods)->getsampwsid = pdb_default_getsampwsid;
+ (*methods)->add_sam_account = pdb_default_add_sam_account;
+ (*methods)->update_sam_account = pdb_default_update_sam_account;
+ (*methods)->delete_sam_account = pdb_default_delete_sam_account;
+
+ (*methods)->getgrsid = pdb_default_getgrsid;
+ (*methods)->getgrgid = pdb_default_getgrgid;
+ (*methods)->getgrnam = pdb_default_getgrnam;
+ (*methods)->add_group_mapping_entry = pdb_default_add_group_mapping_entry;
+ (*methods)->update_group_mapping_entry = pdb_default_update_group_mapping_entry;
+ (*methods)->delete_group_mapping_entry = pdb_default_delete_group_mapping_entry;
+ (*methods)->enum_group_mapping = pdb_default_enum_group_mapping;
+ (*methods)->find_alias = pdb_default_find_alias;
+ (*methods)->create_alias = pdb_default_create_alias;
+ (*methods)->delete_alias = pdb_default_delete_alias;
+ (*methods)->enum_aliases = pdb_default_enum_aliases;
+ (*methods)->get_aliasinfo = pdb_default_get_aliasinfo;
+ (*methods)->set_aliasinfo = pdb_default_set_aliasinfo;
+ (*methods)->add_aliasmem = pdb_default_add_aliasmem;
+ (*methods)->del_aliasmem = pdb_default_del_aliasmem;
+ (*methods)->enum_aliasmem = pdb_default_enum_aliasmem;
+ (*methods)->enum_alias_memberships = pdb_default_alias_memberships;
+
+ (*methods)->settrustpwent = pdb_default_settrustpwent;
+ (*methods)->gettrustpwent = pdb_default_gettrustpwent;
+ (*methods)->gettrustpwnam = pdb_default_gettrustpwnam;
+ (*methods)->gettrustpwsid = pdb_default_gettrustpwsid;
+ (*methods)->add_trust_passwd = pdb_default_add_trust_passwd;
+ (*methods)->update_trust_passwd = pdb_default_update_trust_passwd;
+ (*methods)->delete_trust_passwd = pdb_default_delete_trust_passwd;
+
+ (*methods)->add_sid_to_privilege = pdb_default_add_sid_to_privilege;
+ (*methods)->remove_sid_from_privilege = pdb_default_remove_sid_from_privilege;
+ (*methods)->get_privilege_set = pdb_default_get_privilege_set;
+ (*methods)->get_privilege_entry = pdb_default_get_privilege_entry;
+
+ return NT_STATUS_OK;
+}
diff --git a/source/passdb/pdb_ldap.c b/source/passdb/pdb_ldap.c
new file mode 100644
index 00000000000..23ab0f9965d
--- /dev/null
+++ b/source/passdb/pdb_ldap.c
@@ -0,0 +1,3117 @@
+/*
+ Unix SMB/CIFS mplementation.
+ LDAP protocol helper functions for SAMBA
+ Copyright (C) Jean François Micouleau 1998
+ Copyright (C) Gerald Carter 2001-2003
+ Copyright (C) Shahms King 2001
+ Copyright (C) Andrew Bartlett 2002-2003
+ Copyright (C) Stefan (metze) Metzmacher 2002-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/* TODO:
+* persistent connections: if using NSS LDAP, many connections are made
+* however, using only one within Samba would be nice
+*
+* Clean up SSL stuff, compile on OpenLDAP 1.x, 2.x, and Netscape SDK
+*
+* Other LDAP based login attributes: accountExpires, etc.
+* (should be the domain of Samba proper, but the sam_password/SAM_ACCOUNT
+* structures don't have fields for some of these attributes)
+*
+* SSL is done, but can't get the certificate based authentication to work
+* against on my test platform (Linux 2.4, OpenLDAP 2.x)
+*/
+
+/* NOTE: this will NOT work against an Active Directory server
+* due to the fact that the two password fields cannot be retrieved
+* from a server; recommend using security = domain in this situation
+* and/or winbind
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+#include <lber.h>
+#include <ldap.h>
+
+/*
+ * Work around versions of the LDAP client libs that don't have the OIDs
+ * defined, or have them defined under the old name.
+ * This functionality is really a factor of the server, not the client
+ *
+ */
+
+#if defined(LDAP_EXOP_X_MODIFY_PASSWD) && !defined(LDAP_EXOP_MODIFY_PASSWD)
+#define LDAP_EXOP_MODIFY_PASSWD LDAP_EXOP_X_MODIFY_PASSWD
+#elif !defined(LDAP_EXOP_MODIFY_PASSWD)
+#define LDAP_EXOP_MODIFY_PASSWD "1.3.6.1.4.1.4203.1.11.1"
+#endif
+
+#if defined(LDAP_EXOP_X_MODIFY_PASSWD_ID) && !defined(LDAP_EXOP_MODIFY_PASSWD_ID)
+#define LDAP_TAG_EXOP_MODIFY_PASSWD_ID LDAP_EXOP_X_MODIFY_PASSWD_ID
+#elif !defined(LDAP_EXOP_MODIFY_PASSWD_ID)
+#define LDAP_TAG_EXOP_MODIFY_PASSWD_ID ((ber_tag_t) 0x80U)
+#endif
+
+#if defined(LDAP_EXOP_X_MODIFY_PASSWD_NEW) && !defined(LDAP_EXOP_MODIFY_PASSWD_NEW)
+#define LDAP_TAG_EXOP_MODIFY_PASSWD_NEW LDAP_EXOP_X_MODIFY_PASSWD_NEW
+#elif !defined(LDAP_EXOP_MODIFY_PASSWD_NEW)
+#define LDAP_TAG_EXOP_MODIFY_PASSWD_NEW ((ber_tag_t) 0x82U)
+#endif
+
+
+#ifndef SAM_ACCOUNT
+#define SAM_ACCOUNT struct sam_passwd
+#endif
+
+#include "smbldap.h"
+
+struct ldapsam_privates {
+ struct smbldap_state *smbldap_state;
+
+ /* Former statics */
+ LDAPMessage *result;
+ LDAPMessage *entry;
+ int index;
+
+ const char *domain_name;
+ DOM_SID domain_sid;
+
+ /* configuration items */
+ int schema_ver;
+};
+
+/**********************************************************************
+ Free a LDAPMessage (one is stored on the SAM_ACCOUNT).
+ **********************************************************************/
+
+static void private_data_free_fn(void **result)
+{
+ ldap_msgfree(*result);
+ *result = NULL;
+}
+
+/**********************************************************************
+ Get the attribute name given a user schame version.
+ **********************************************************************/
+
+static const char* get_userattr_key2string( int schema_ver, int key )
+{
+ switch ( schema_ver ) {
+ case SCHEMAVER_SAMBAACCOUNT:
+ return get_attr_key2string( attrib_map_v22, key );
+
+ case SCHEMAVER_SAMBASAMACCOUNT:
+ return get_attr_key2string( attrib_map_v30, key );
+
+ default:
+ DEBUG(0,("get_userattr_key2string: unknown schema version specified\n"));
+ break;
+ }
+ return NULL;
+}
+
+/**********************************************************************
+ Return the list of attribute names given a user schema version.
+**********************************************************************/
+
+static char** get_userattr_list( int schema_ver )
+{
+ switch ( schema_ver ) {
+ case SCHEMAVER_SAMBAACCOUNT:
+ return get_attr_list( attrib_map_v22 );
+
+ case SCHEMAVER_SAMBASAMACCOUNT:
+ return get_attr_list( attrib_map_v30 );
+ default:
+ DEBUG(0,("get_userattr_list: unknown schema version specified!\n"));
+ break;
+ }
+
+ return NULL;
+}
+
+/*******************************************************************
+ Generate the LDAP search filter for the objectclass based on the
+ version of the schema we are using.
+******************************************************************/
+
+static const char* get_objclass_filter( int schema_ver )
+{
+ static fstring objclass_filter;
+
+ switch( schema_ver ) {
+ case SCHEMAVER_SAMBAACCOUNT:
+ fstr_sprintf( objclass_filter, "(objectclass=%s)", LDAP_OBJ_SAMBAACCOUNT );
+ break;
+ case SCHEMAVER_SAMBASAMACCOUNT:
+ fstr_sprintf( objclass_filter, "(objectclass=%s)", LDAP_OBJ_SAMBASAMACCOUNT );
+ break;
+ default:
+ DEBUG(0,("get_objclass_filter: Invalid schema version specified!\n"));
+ break;
+ }
+
+ return objclass_filter;
+}
+
+/*******************************************************************
+ Run the search by name.
+******************************************************************/
+
+static int ldapsam_search_suffix_by_name (struct ldapsam_privates *ldap_state,
+ const char *user,
+ LDAPMessage ** result, char **attr)
+{
+ pstring filter;
+ char *escape_user = escape_ldap_string_alloc(user);
+
+ if (!escape_user) {
+ return LDAP_NO_MEMORY;
+ }
+
+ /*
+ * in the filter expression, replace %u with the real name
+ * so in ldap filter, %u MUST exist :-)
+ */
+ pstr_sprintf(filter, "(&%s%s)", lp_ldap_filter(),
+ get_objclass_filter(ldap_state->schema_ver));
+
+ /*
+ * have to use this here because $ is filtered out
+ * in pstring_sub
+ */
+
+
+ all_string_sub(filter, "%u", escape_user, sizeof(pstring));
+ SAFE_FREE(escape_user);
+
+ return smbldap_search_suffix(ldap_state->smbldap_state, filter, attr, result);
+}
+
+/*******************************************************************
+ Run the search by rid.
+******************************************************************/
+
+static int ldapsam_search_suffix_by_rid (struct ldapsam_privates *ldap_state,
+ uint32 rid, LDAPMessage ** result,
+ char **attr)
+{
+ pstring filter;
+ int rc;
+
+ pstr_sprintf(filter, "(&(rid=%i)%s)", rid,
+ get_objclass_filter(ldap_state->schema_ver));
+
+ rc = smbldap_search_suffix(ldap_state->smbldap_state, filter, attr, result);
+
+ return rc;
+}
+
+/*******************************************************************
+ Run the search by SID.
+******************************************************************/
+
+static int ldapsam_search_suffix_by_sid (struct ldapsam_privates *ldap_state,
+ const DOM_SID *sid, LDAPMessage ** result,
+ char **attr)
+{
+ pstring filter;
+ int rc;
+ fstring sid_string;
+
+ pstr_sprintf(filter, "(&(%s=%s)%s)",
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_SID),
+ sid_to_string(sid_string, sid),
+ get_objclass_filter(ldap_state->schema_ver));
+
+ rc = smbldap_search_suffix(ldap_state->smbldap_state, filter, attr, result);
+
+ return rc;
+}
+
+/*******************************************************************
+ Delete complete object or objectclass and attrs from
+ object found in search_result depending on lp_ldap_delete_dn
+******************************************************************/
+
+static NTSTATUS ldapsam_delete_entry(struct ldapsam_privates *ldap_state,
+ LDAPMessage *result,
+ const char *objectclass,
+ char **attrs)
+{
+ int rc;
+ LDAPMessage *entry = NULL;
+ LDAPMod **mods = NULL;
+ char *name, *dn;
+ BerElement *ptr = NULL;
+
+ rc = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result);
+
+ if (rc != 1) {
+ DEBUG(0, ("ldapsam_delete_entry: Entry must exist exactly once!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, result);
+ dn = smbldap_get_dn(ldap_state->smbldap_state->ldap_struct, entry);
+ if (!dn) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (lp_ldap_delete_dn()) {
+ NTSTATUS ret = NT_STATUS_OK;
+ rc = smbldap_delete(ldap_state->smbldap_state, dn);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0, ("ldapsam_delete_entry: Could not delete object %s\n", dn));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ }
+ SAFE_FREE(dn);
+ return ret;
+ }
+
+ /* Ok, delete only the SAM attributes */
+
+ for (name = ldap_first_attribute(ldap_state->smbldap_state->ldap_struct, entry, &ptr);
+ name != NULL;
+ name = ldap_next_attribute(ldap_state->smbldap_state->ldap_struct, entry, ptr)) {
+ char **attrib;
+
+ /* We are only allowed to delete the attributes that
+ really exist. */
+
+ for (attrib = attrs; *attrib != NULL; attrib++) {
+ if (StrCaseCmp(*attrib, name) == 0) {
+ DEBUG(10, ("ldapsam_delete_entry: deleting attribute %s\n", name));
+ smbldap_set_mod(&mods, LDAP_MOD_DELETE, name, NULL);
+ }
+ }
+
+ ldap_memfree(name);
+ }
+
+ if (ptr != NULL) {
+ ber_free(ptr, 0);
+ }
+
+ smbldap_set_mod(&mods, LDAP_MOD_DELETE, "objectClass", objectclass);
+
+ rc = smbldap_modify(ldap_state->smbldap_state, dn, mods);
+ ldap_mods_free(mods, True);
+
+ if (rc != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(ldap_state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
+ &ld_error);
+
+ DEBUG(0, ("ldapsam_delete_entry: Could not delete attributes for %s, error: %s (%s)\n",
+ dn, ldap_err2string(rc), ld_error?ld_error:"unknown"));
+ SAFE_FREE(ld_error);
+ SAFE_FREE(dn);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ SAFE_FREE(dn);
+ return NT_STATUS_OK;
+}
+
+/* New Interface is being implemented here */
+
+#if 0 /* JERRY - not uesed anymore */
+
+/**********************************************************************
+Initialize SAM_ACCOUNT from an LDAP query (unix attributes only)
+*********************************************************************/
+static BOOL get_unix_attributes (struct ldapsam_privates *ldap_state,
+ SAM_ACCOUNT * sampass,
+ LDAPMessage * entry,
+ gid_t *gid)
+{
+ pstring homedir;
+ pstring temp;
+ char **ldap_values;
+ char **values;
+
+ if ((ldap_values = ldap_get_values (ldap_state->smbldap_state->ldap_struct, entry, "objectClass")) == NULL) {
+ DEBUG (1, ("get_unix_attributes: no objectClass! \n"));
+ return False;
+ }
+
+ for (values=ldap_values;*values;values++) {
+ if (strequal(*values, LDAP_OBJ_POSIXACCOUNT )) {
+ break;
+ }
+ }
+
+ if (!*values) { /*end of array, no posixAccount */
+ DEBUG(10, ("user does not have %s attributes\n", LDAP_OBJ_POSIXACCOUNT));
+ ldap_value_free(ldap_values);
+ return False;
+ }
+ ldap_value_free(ldap_values);
+
+ if ( !smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_UNIX_HOME), homedir) )
+ {
+ return False;
+ }
+
+ if ( !smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_GIDNUMBER), temp) )
+ {
+ return False;
+ }
+
+ *gid = (gid_t)atol(temp);
+
+ pdb_set_unix_homedir(sampass, homedir, PDB_SET);
+
+ DEBUG(10, ("user has %s attributes\n", LDAP_OBJ_POSIXACCOUNT));
+
+ return True;
+}
+
+#endif
+
+static time_t ldapsam_get_entry_timestamp(
+ struct ldapsam_privates *ldap_state,
+ LDAPMessage * entry)
+{
+ pstring temp;
+ struct tm tm;
+
+ if (!smbldap_get_single_pstring(
+ ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_MOD_TIMESTAMP),
+ temp))
+ return (time_t) 0;
+
+ strptime(temp, "%Y%m%d%H%M%SZ", &tm);
+ tzset();
+ return timegm(&tm);
+}
+
+/**********************************************************************
+ Initialize SAM_ACCOUNT from an LDAP query.
+ (Based on init_sam_from_buffer in pdb_tdb.c)
+*********************************************************************/
+
+static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
+ SAM_ACCOUNT * sampass,
+ LDAPMessage * entry)
+{
+ time_t logon_time,
+ logoff_time,
+ kickoff_time,
+ pass_last_set_time,
+ pass_can_change_time,
+ pass_must_change_time,
+ ldap_entry_time,
+ bad_password_time;
+ pstring username,
+ domain,
+ nt_username,
+ fullname,
+ homedir,
+ dir_drive,
+ logon_script,
+ profile_path,
+ acct_desc,
+ workstations;
+ char munged_dial[2048];
+ uint32 user_rid;
+ uint8 smblmpwd[LM_HASH_LEN],
+ smbntpwd[NT_HASH_LEN];
+ uint16 acct_ctrl = 0,
+ logon_divs;
+ uint16 bad_password_count = 0,
+ logon_count = 0;
+ uint32 hours_len;
+ uint8 hours[MAX_HOURS_LEN];
+ pstring temp;
+ LOGIN_CACHE *cache_entry = NULL;
+
+ /*
+ * do a little initialization
+ */
+ username[0] = '\0';
+ domain[0] = '\0';
+ nt_username[0] = '\0';
+ fullname[0] = '\0';
+ homedir[0] = '\0';
+ dir_drive[0] = '\0';
+ logon_script[0] = '\0';
+ profile_path[0] = '\0';
+ acct_desc[0] = '\0';
+ munged_dial[0] = '\0';
+ workstations[0] = '\0';
+
+
+ if (sampass == NULL || ldap_state == NULL || entry == NULL) {
+ DEBUG(0, ("init_sam_from_ldap: NULL parameters found!\n"));
+ return False;
+ }
+
+ if (ldap_state->smbldap_state->ldap_struct == NULL) {
+ DEBUG(0, ("init_sam_from_ldap: ldap_state->smbldap_state->ldap_struct is NULL!\n"));
+ return False;
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry, "uid", username)) {
+ DEBUG(1, ("init_sam_from_ldap: No uid attribute found for this user!\n"));
+ return False;
+ }
+
+ DEBUG(2, ("init_sam_from_ldap: Entry found for user: %s\n", username));
+
+ pstrcpy(nt_username, username);
+
+ pstrcpy(domain, ldap_state->domain_name);
+
+ pdb_set_username(sampass, username, PDB_SET);
+
+ pdb_set_domain(sampass, domain, PDB_DEFAULT);
+ pdb_set_nt_username(sampass, nt_username, PDB_SET);
+
+ /* deal with different attributes between the schema first */
+
+ if ( ldap_state->schema_ver == SCHEMAVER_SAMBASAMACCOUNT ) {
+ if (smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_SID), temp)) {
+ pdb_set_user_sid_from_string(sampass, temp, PDB_SET);
+ }
+
+ if (smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PRIMARY_GROUP_SID), temp)) {
+ pdb_set_group_sid_from_string(sampass, temp, PDB_SET);
+ } else {
+ pdb_set_group_sid_from_rid(sampass, DOMAIN_GROUP_RID_USERS, PDB_DEFAULT);
+ }
+ } else {
+ if (smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_RID), temp)) {
+ user_rid = (uint32)atol(temp);
+ pdb_set_user_sid_from_rid(sampass, user_rid, PDB_SET);
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PRIMARY_GROUP_RID), temp)) {
+ pdb_set_group_sid_from_rid(sampass, DOMAIN_GROUP_RID_USERS, PDB_DEFAULT);
+ } else {
+ uint32 group_rid;
+
+ group_rid = (uint32)atol(temp);
+
+ /* for some reason, we often have 0 as a primary group RID.
+ Make sure that we treat this just as a 'default' value */
+
+ if ( group_rid > 0 )
+ pdb_set_group_sid_from_rid(sampass, group_rid, PDB_SET);
+ else
+ pdb_set_group_sid_from_rid(sampass, DOMAIN_GROUP_RID_USERS, PDB_DEFAULT);
+ }
+ }
+
+ if (pdb_get_init_flags(sampass,PDB_USERSID) == PDB_DEFAULT) {
+ DEBUG(1, ("init_sam_from_ldap: no %s or %s attribute found for this user %s\n",
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_SID),
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_RID),
+ username));
+ return False;
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PWD_LAST_SET), temp)) {
+ /* leave as default */
+ } else {
+ pass_last_set_time = (time_t) atol(temp);
+ pdb_set_pass_last_set_time(sampass, pass_last_set_time, PDB_SET);
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LOGON_TIME), temp)) {
+ /* leave as default */
+ } else {
+ logon_time = (time_t) atol(temp);
+ pdb_set_logon_time(sampass, logon_time, PDB_SET);
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LOGOFF_TIME), temp)) {
+ /* leave as default */
+ } else {
+ logoff_time = (time_t) atol(temp);
+ pdb_set_logoff_time(sampass, logoff_time, PDB_SET);
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_KICKOFF_TIME), temp)) {
+ /* leave as default */
+ } else {
+ kickoff_time = (time_t) atol(temp);
+ pdb_set_kickoff_time(sampass, kickoff_time, PDB_SET);
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PWD_CAN_CHANGE), temp)) {
+ /* leave as default */
+ } else {
+ pass_can_change_time = (time_t) atol(temp);
+ pdb_set_pass_can_change_time(sampass, pass_can_change_time, PDB_SET);
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PWD_MUST_CHANGE), temp)) {
+ /* leave as default */
+ } else {
+ pass_must_change_time = (time_t) atol(temp);
+ pdb_set_pass_must_change_time(sampass, pass_must_change_time, PDB_SET);
+ }
+
+ /* recommend that 'gecos' and 'displayName' should refer to the same
+ * attribute OID. userFullName depreciated, only used by Samba
+ * primary rules of LDAP: don't make a new attribute when one is already defined
+ * that fits your needs; using cn then displayName rather than 'userFullName'
+ */
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_DISPLAY_NAME), fullname)) {
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_CN), fullname)) {
+ /* leave as default */
+ } else {
+ pdb_set_fullname(sampass, fullname, PDB_SET);
+ }
+ } else {
+ pdb_set_fullname(sampass, fullname, PDB_SET);
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_HOME_DRIVE), dir_drive))
+ {
+ pdb_set_dir_drive( sampass,
+ talloc_sub_basic(sampass->mem_ctx, username, lp_logon_drive()),
+ PDB_DEFAULT );
+ } else {
+ pdb_set_dir_drive(sampass, dir_drive, PDB_SET);
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_HOME_PATH), homedir))
+ {
+ pdb_set_homedir( sampass,
+ talloc_sub_basic(sampass->mem_ctx, username, lp_logon_home()),
+ PDB_DEFAULT );
+ } else {
+ pdb_set_homedir(sampass, homedir, PDB_SET);
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LOGON_SCRIPT), logon_script))
+ {
+ pdb_set_logon_script( sampass,
+ talloc_sub_basic(sampass->mem_ctx, username, lp_logon_script()),
+ PDB_DEFAULT );
+ } else {
+ pdb_set_logon_script(sampass, logon_script, PDB_SET);
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PROFILE_PATH), profile_path))
+ {
+ pdb_set_profile_path( sampass,
+ talloc_sub_basic( sampass->mem_ctx, username, lp_logon_path()),
+ PDB_DEFAULT );
+ } else {
+ pdb_set_profile_path(sampass, profile_path, PDB_SET);
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_DESC), acct_desc))
+ {
+ /* leave as default */
+ } else {
+ pdb_set_acct_desc(sampass, acct_desc, PDB_SET);
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_WKS), workstations)) {
+ /* leave as default */;
+ } else {
+ pdb_set_workstations(sampass, workstations, PDB_SET);
+ }
+
+ if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_MUNGED_DIAL), munged_dial, sizeof(munged_dial))) {
+ /* leave as default */;
+ } else {
+ pdb_set_munged_dial(sampass, munged_dial, PDB_SET);
+ }
+
+ /* FIXME: hours stuff should be cleaner */
+
+ logon_divs = 168;
+ hours_len = 21;
+ memset(hours, 0xff, hours_len);
+
+ if (!smbldap_get_single_pstring (ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LMPW), temp)) {
+ /* leave as default */
+ } else {
+ pdb_gethexpwd(temp, smblmpwd);
+ memset((char *)temp, '\0', strlen(temp)+1);
+ if (!pdb_set_lanman_passwd(sampass, smblmpwd, PDB_SET))
+ return False;
+ ZERO_STRUCT(smblmpwd);
+ }
+
+ if (!smbldap_get_single_pstring (ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_NTPW), temp)) {
+ /* leave as default */
+ } else {
+ pdb_gethexpwd(temp, smbntpwd);
+ memset((char *)temp, '\0', strlen(temp)+1);
+ if (!pdb_set_nt_passwd(sampass, smbntpwd, PDB_SET))
+ return False;
+ ZERO_STRUCT(smbntpwd);
+ }
+
+ if (!smbldap_get_single_pstring (ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_ACB_INFO), temp)) {
+ acct_ctrl |= ACB_NORMAL;
+ } else {
+ acct_ctrl = pdb_decode_acct_ctrl(temp);
+
+ if (acct_ctrl == 0)
+ acct_ctrl |= ACB_NORMAL;
+
+ pdb_set_acct_ctrl(sampass, acct_ctrl, PDB_SET);
+ }
+
+ pdb_set_hours_len(sampass, hours_len, PDB_SET);
+ pdb_set_logon_divs(sampass, logon_divs, PDB_SET);
+
+/* pdb_set_munged_dial(sampass, munged_dial, PDB_SET); */
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_BAD_PASSWORD_COUNT), temp)) {
+ /* leave as default */
+ } else {
+ bad_password_count = (uint32) atol(temp);
+ pdb_set_bad_password_count(sampass, bad_password_count, PDB_SET);
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_BAD_PASSWORD_TIME), temp)) {
+ /* leave as default */
+ } else {
+ bad_password_time = (time_t) atol(temp);
+ pdb_set_bad_password_time(sampass, bad_password_time, PDB_SET);
+ }
+
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LOGON_COUNT), temp)) {
+ /* leave as default */
+ } else {
+ logon_count = (uint32) atol(temp);
+ pdb_set_logon_count(sampass, logon_count, PDB_SET);
+ }
+
+ /* pdb_set_unknown_6(sampass, unknown6, PDB_SET); */
+
+ pdb_set_hours(sampass, hours, PDB_SET);
+
+ /* check the timestamp of the cache vs ldap entry */
+ if (!(ldap_entry_time = ldapsam_get_entry_timestamp(ldap_state,
+ entry)))
+ return True;
+
+ /* see if we have newer updates */
+ if (!(cache_entry = login_cache_read(sampass))) {
+ DEBUG (9, ("No cache entry, bad count = %u, bad time = %u\n",
+ (unsigned int)pdb_get_bad_password_count(sampass),
+ (unsigned int)pdb_get_bad_password_time(sampass)));
+ return True;
+ }
+
+ DEBUG(7, ("ldap time is %u, cache time is %u, bad time = %u\n",
+ (unsigned int)ldap_entry_time, (unsigned int)cache_entry->entry_timestamp,
+ (unsigned int)cache_entry->bad_password_time));
+
+ if (ldap_entry_time > cache_entry->entry_timestamp) {
+ /* cache is older than directory , so
+ we need to delete the entry but allow the
+ fields to be written out */
+ login_cache_delentry(sampass);
+ } else {
+ /* read cache in */
+ pdb_set_acct_ctrl(sampass,
+ pdb_get_acct_ctrl(sampass) |
+ (cache_entry->acct_ctrl & ACB_AUTOLOCK),
+ PDB_SET);
+ pdb_set_bad_password_count(sampass,
+ cache_entry->bad_password_count,
+ PDB_SET);
+ pdb_set_bad_password_time(sampass,
+ cache_entry->bad_password_time,
+ PDB_SET);
+ }
+
+ SAFE_FREE(cache_entry);
+ return True;
+}
+
+/**********************************************************************
+ Initialize SAM_ACCOUNT from an LDAP query.
+ (Based on init_buffer_from_sam in pdb_tdb.c)
+*********************************************************************/
+
+static BOOL init_ldap_from_sam (struct ldapsam_privates *ldap_state,
+ LDAPMessage *existing,
+ LDAPMod *** mods, SAM_ACCOUNT * sampass,
+ BOOL (*need_update)(const SAM_ACCOUNT *,
+ enum pdb_elements))
+{
+ pstring temp;
+ uint32 rid;
+
+ if (mods == NULL || sampass == NULL) {
+ DEBUG(0, ("init_ldap_from_sam: NULL parameters found!\n"));
+ return False;
+ }
+
+ *mods = NULL;
+
+ /*
+ * took out adding "objectclass: sambaAccount"
+ * do this on a per-mod basis
+ */
+ if (need_update(sampass, PDB_USERNAME))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ "uid", pdb_get_username(sampass));
+
+ DEBUG(2, ("init_ldap_from_sam: Setting entry for user: %s\n", pdb_get_username(sampass)));
+
+ /* only update the RID if we actually need to */
+ if (need_update(sampass, PDB_USERSID)) {
+ fstring sid_string;
+ fstring dom_sid_string;
+ const DOM_SID *user_sid = pdb_get_user_sid(sampass);
+
+ switch ( ldap_state->schema_ver ) {
+ case SCHEMAVER_SAMBAACCOUNT:
+ if (!sid_peek_check_rid(&ldap_state->domain_sid, user_sid, &rid)) {
+ DEBUG(1, ("init_ldap_from_sam: User's SID (%s) is not for this domain (%s), cannot add to LDAP!\n",
+ sid_to_string(sid_string, user_sid),
+ sid_to_string(dom_sid_string, &ldap_state->domain_sid)));
+ return False;
+ }
+ slprintf(temp, sizeof(temp) - 1, "%i", rid);
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_RID),
+ temp);
+ break;
+
+ case SCHEMAVER_SAMBASAMACCOUNT:
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_SID),
+ sid_to_string(sid_string, user_sid));
+ break;
+
+ default:
+ DEBUG(0,("init_ldap_from_sam: unknown schema version specified\n"));
+ break;
+ }
+ }
+
+ /* we don't need to store the primary group RID - so leaving it
+ 'free' to hang off the unix primary group makes life easier */
+
+ if (need_update(sampass, PDB_GROUPSID)) {
+ fstring sid_string;
+ fstring dom_sid_string;
+ const DOM_SID *group_sid = pdb_get_group_sid(sampass);
+
+ switch ( ldap_state->schema_ver ) {
+ case SCHEMAVER_SAMBAACCOUNT:
+ if (!sid_peek_check_rid(&ldap_state->domain_sid, group_sid, &rid)) {
+ DEBUG(1, ("init_ldap_from_sam: User's Primary Group SID (%s) is not for this domain (%s), cannot add to LDAP!\n",
+ sid_to_string(sid_string, group_sid),
+ sid_to_string(dom_sid_string, &ldap_state->domain_sid)));
+ return False;
+ }
+
+ slprintf(temp, sizeof(temp) - 1, "%i", rid);
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_PRIMARY_GROUP_RID), temp);
+ break;
+
+ case SCHEMAVER_SAMBASAMACCOUNT:
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_PRIMARY_GROUP_SID), sid_to_string(sid_string, group_sid));
+ break;
+
+ default:
+ DEBUG(0,("init_ldap_from_sam: unknown schema version specified\n"));
+ break;
+ }
+
+ }
+
+ /* displayName, cn, and gecos should all be the same
+ * most easily accomplished by giving them the same OID
+ * gecos isn't set here b/c it should be handled by the
+ * add-user script
+ * We change displayName only and fall back to cn if
+ * it does not exist.
+ */
+
+ if (need_update(sampass, PDB_FULLNAME))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_DISPLAY_NAME),
+ pdb_get_fullname(sampass));
+
+ if (need_update(sampass, PDB_ACCTDESC))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_DESC),
+ pdb_get_acct_desc(sampass));
+
+ if (need_update(sampass, PDB_WORKSTATIONS))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_WKS),
+ pdb_get_workstations(sampass));
+
+ if (need_update(sampass, PDB_MUNGEDDIAL))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_MUNGED_DIAL),
+ pdb_get_munged_dial(sampass));
+
+ if (need_update(sampass, PDB_SMBHOME))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_HOME_PATH),
+ pdb_get_homedir(sampass));
+
+ if (need_update(sampass, PDB_DRIVE))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_HOME_DRIVE),
+ pdb_get_dir_drive(sampass));
+
+ if (need_update(sampass, PDB_LOGONSCRIPT))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LOGON_SCRIPT),
+ pdb_get_logon_script(sampass));
+
+ if (need_update(sampass, PDB_PROFILE))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PROFILE_PATH),
+ pdb_get_profile_path(sampass));
+
+ slprintf(temp, sizeof(temp) - 1, "%li", pdb_get_logon_time(sampass));
+ if (need_update(sampass, PDB_LOGONTIME))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LOGON_TIME), temp);
+
+ slprintf(temp, sizeof(temp) - 1, "%li", pdb_get_logoff_time(sampass));
+ if (need_update(sampass, PDB_LOGOFFTIME))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LOGOFF_TIME), temp);
+
+ slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_kickoff_time(sampass));
+ if (need_update(sampass, PDB_KICKOFFTIME))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_KICKOFF_TIME), temp);
+
+ slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_pass_can_change_time(sampass));
+ if (need_update(sampass, PDB_CANCHANGETIME))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PWD_CAN_CHANGE), temp);
+
+ slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_pass_must_change_time(sampass));
+ if (need_update(sampass, PDB_MUSTCHANGETIME))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PWD_MUST_CHANGE), temp);
+
+
+ if ((pdb_get_acct_ctrl(sampass)&(ACB_WSTRUST|ACB_SVRTRUST|ACB_DOMTRUST))
+ || (lp_ldap_passwd_sync()!=LDAP_PASSWD_SYNC_ONLY)) {
+
+ if (need_update(sampass, PDB_LMPASSWD)) {
+ const uchar *lm_pw = pdb_get_lanman_passwd(sampass);
+ if (lm_pw) {
+ pdb_sethexpwd(temp, lm_pw,
+ pdb_get_acct_ctrl(sampass));
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LMPW),
+ temp);
+ } else {
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LMPW),
+ NULL);
+ }
+ }
+ if (need_update(sampass, PDB_NTPASSWD)) {
+ const uchar *nt_pw = pdb_get_nt_passwd(sampass);
+ if (nt_pw) {
+ pdb_sethexpwd(temp, nt_pw,
+ pdb_get_acct_ctrl(sampass));
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_NTPW),
+ temp);
+ } else {
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_NTPW),
+ NULL);
+ }
+ }
+
+ if (need_update(sampass, PDB_PASSLASTSET)) {
+ slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_pass_last_set_time(sampass));
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PWD_LAST_SET),
+ temp);
+ }
+ }
+
+ /* FIXME: Hours stuff goes in LDAP */
+
+ if (need_update(sampass, PDB_ACCTCTRL))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_ACB_INFO),
+ pdb_encode_acct_ctrl (pdb_get_acct_ctrl(sampass), NEW_PW_FORMAT_SPACE_PADDED_LEN));
+
+ /* password lockout cache:
+ - If we are now autolocking or clearing, we write to ldap
+ - If we are clearing, we delete the cache entry
+ - If the count is > 0, we update the cache
+
+ This even means when autolocking, we cache, just in case the
+ update doesn't work, and we have to cache the autolock flag */
+
+ if (need_update(sampass, PDB_BAD_PASSWORD_COUNT)) /* &&
+ need_update(sampass, PDB_BAD_PASSWORD_TIME)) */ {
+ uint16 badcount = pdb_get_bad_password_count(sampass);
+ time_t badtime = pdb_get_bad_password_time(sampass);
+ uint32 pol;
+ account_policy_get(AP_BAD_ATTEMPT_LOCKOUT, &pol);
+
+ DEBUG(3, ("updating bad password fields, policy=%u, count=%u, time=%u\n",
+ (unsigned int)pol, (unsigned int)badcount, (unsigned int)badtime));
+
+ if ((badcount >= pol) || (badcount == 0)) {
+ DEBUG(7, ("making mods to update ldap, count=%u, time=%u\n",
+ (unsigned int)badcount, (unsigned int)badtime));
+ slprintf (temp, sizeof (temp) - 1, "%li", (long)badcount);
+ smbldap_make_mod(
+ ldap_state->smbldap_state->ldap_struct,
+ existing, mods,
+ get_userattr_key2string(
+ ldap_state->schema_ver,
+ LDAP_ATTR_BAD_PASSWORD_COUNT),
+ temp);
+
+ slprintf (temp, sizeof (temp) - 1, "%li", badtime);
+ smbldap_make_mod(
+ ldap_state->smbldap_state->ldap_struct,
+ existing, mods,
+ get_userattr_key2string(
+ ldap_state->schema_ver,
+ LDAP_ATTR_BAD_PASSWORD_TIME),
+ temp);
+ }
+ if (badcount == 0) {
+ DEBUG(7, ("bad password count is reset, deleting login cache entry for %s\n", pdb_get_nt_username(sampass)));
+ login_cache_delentry(sampass);
+ } else {
+ LOGIN_CACHE cache_entry ={time(NULL),
+ pdb_get_acct_ctrl(sampass),
+ badcount, badtime};
+ DEBUG(7, ("Updating bad password count and time in login cache\n"));
+ login_cache_write(sampass, cache_entry);
+ }
+ }
+
+ return True;
+}
+
+/**********************************************************************
+ Connect to LDAP server for password enumeration.
+*********************************************************************/
+
+static NTSTATUS ldapsam_setsampwent(struct pdb_methods *my_methods, BOOL update)
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ int rc;
+ pstring filter;
+ char **attr_list;
+
+ pstr_sprintf( filter, "(&%s%s)", lp_ldap_filter(),
+ get_objclass_filter(ldap_state->schema_ver));
+ all_string_sub(filter, "%u", "*", sizeof(pstring));
+
+ attr_list = get_userattr_list(ldap_state->schema_ver);
+ rc = smbldap_search_suffix(ldap_state->smbldap_state, filter,
+ attr_list, &ldap_state->result);
+ free_attr_list( attr_list );
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0, ("ldapsam_setsampwent: LDAP search failed: %s\n", ldap_err2string(rc)));
+ DEBUG(3, ("ldapsam_setsampwent: Query was: %s, %s\n", lp_ldap_suffix(), filter));
+ ldap_msgfree(ldap_state->result);
+ ldap_state->result = NULL;
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(2, ("ldapsam_setsampwent: %d entries in the base!\n",
+ ldap_count_entries(ldap_state->smbldap_state->ldap_struct,
+ ldap_state->result)));
+
+ ldap_state->entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct,
+ ldap_state->result);
+ ldap_state->index = 0;
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ End enumeration of the LDAP password list.
+*********************************************************************/
+
+static void ldapsam_endsampwent(struct pdb_methods *my_methods)
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ if (ldap_state->result) {
+ ldap_msgfree(ldap_state->result);
+ ldap_state->result = NULL;
+ }
+}
+
+/**********************************************************************
+Get the next entry in the LDAP password database.
+*********************************************************************/
+
+static NTSTATUS ldapsam_getsampwent(struct pdb_methods *my_methods, SAM_ACCOUNT *user)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ BOOL bret = False;
+
+ while (!bret) {
+ if (!ldap_state->entry)
+ return ret;
+
+ ldap_state->index++;
+ bret = init_sam_from_ldap(ldap_state, user, ldap_state->entry);
+
+ ldap_state->entry = ldap_next_entry(ldap_state->smbldap_state->ldap_struct,
+ ldap_state->entry);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+Get SAM_ACCOUNT entry from LDAP by username.
+*********************************************************************/
+
+static NTSTATUS ldapsam_getsampwnam(struct pdb_methods *my_methods, SAM_ACCOUNT *user, const char *sname)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ int count;
+ char ** attr_list;
+ int rc;
+
+ attr_list = get_userattr_list( ldap_state->schema_ver );
+ rc = ldapsam_search_suffix_by_name(ldap_state, sname, &result, attr_list);
+ free_attr_list( attr_list );
+
+ if ( rc != LDAP_SUCCESS )
+ return NT_STATUS_NO_SUCH_USER;
+
+ count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result);
+
+ if (count < 1) {
+ DEBUG(4, ("ldapsam_getsampwnam: Unable to locate user [%s] count=%d\n", sname, count));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_USER;
+ } else if (count > 1) {
+ DEBUG(1, ("ldapsam_getsampwnam: Duplicate entries for this user [%s] Failing. count=%d\n", sname, count));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, result);
+ if (entry) {
+ if (!init_sam_from_ldap(ldap_state, user, entry)) {
+ DEBUG(1,("ldapsam_getsampwnam: init_sam_from_ldap failed for user '%s'!\n", sname));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+ pdb_set_backend_private_data(user, result,
+ private_data_free_fn,
+ my_methods, PDB_CHANGED);
+ ret = NT_STATUS_OK;
+ } else {
+ ldap_msgfree(result);
+ }
+ return ret;
+}
+
+static int ldapsam_get_ldap_user_by_sid(struct ldapsam_privates *ldap_state,
+ const DOM_SID *sid, LDAPMessage **result)
+{
+ int rc = -1;
+ char ** attr_list;
+ uint32 rid;
+
+ switch ( ldap_state->schema_ver ) {
+ case SCHEMAVER_SAMBASAMACCOUNT:
+ attr_list = get_userattr_list(ldap_state->schema_ver);
+ rc = ldapsam_search_suffix_by_sid(ldap_state, sid, result, attr_list);
+ free_attr_list( attr_list );
+
+ if ( rc != LDAP_SUCCESS )
+ return rc;
+ break;
+
+ case SCHEMAVER_SAMBAACCOUNT:
+ if (!sid_peek_check_rid(&ldap_state->domain_sid, sid, &rid)) {
+ return rc;
+ }
+
+ attr_list = get_userattr_list(ldap_state->schema_ver);
+ rc = ldapsam_search_suffix_by_rid(ldap_state, rid, result, attr_list );
+ free_attr_list( attr_list );
+
+ if ( rc != LDAP_SUCCESS )
+ return rc;
+ break;
+ }
+ return rc;
+}
+
+/**********************************************************************
+ Get SAM_ACCOUNT entry from LDAP by SID.
+*********************************************************************/
+
+static NTSTATUS ldapsam_getsampwsid(struct pdb_methods *my_methods, SAM_ACCOUNT * user, const DOM_SID *sid)
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ int count;
+ int rc;
+ fstring sid_string;
+
+ rc = ldapsam_get_ldap_user_by_sid(ldap_state,
+ sid, &result);
+ if (rc != LDAP_SUCCESS)
+ return NT_STATUS_NO_SUCH_USER;
+
+ count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result);
+
+ if (count < 1) {
+ DEBUG(4, ("ldapsam_getsampwsid: Unable to locate SID [%s] count=%d\n", sid_to_string(sid_string, sid),
+ count));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_USER;
+ } else if (count > 1) {
+ DEBUG(1, ("ldapsam_getsampwsid: More than one user with SID [%s]. Failing. count=%d\n", sid_to_string(sid_string, sid),
+ count));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, result);
+ if (!entry) {
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (!init_sam_from_ldap(ldap_state, user, entry)) {
+ DEBUG(1,("ldapsam_getsampwrid: init_sam_from_ldap failed!\n"));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ pdb_set_backend_private_data(user, result,
+ private_data_free_fn,
+ my_methods, PDB_CHANGED);
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+ Do the actual modification - also change a plaintext passord if
+ it it set.
+**********************************************************************/
+
+static NTSTATUS ldapsam_modify_entry(struct pdb_methods *my_methods,
+ SAM_ACCOUNT *newpwd, char *dn,
+ LDAPMod **mods, int ldap_op,
+ BOOL (*need_update)(const SAM_ACCOUNT *, enum pdb_elements))
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ int rc;
+
+ if (!my_methods || !newpwd || !dn) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!mods) {
+ DEBUG(5,("ldapsam_modify_entry: mods is empty: nothing to modify\n"));
+ /* may be password change below however */
+ } else {
+ switch(ldap_op) {
+ case LDAP_MOD_ADD:
+ smbldap_set_mod(&mods, LDAP_MOD_ADD,
+ "objectclass",
+ LDAP_OBJ_ACCOUNT);
+ rc = smbldap_add(ldap_state->smbldap_state,
+ dn, mods);
+ break;
+ case LDAP_MOD_REPLACE:
+ rc = smbldap_modify(ldap_state->smbldap_state,
+ dn ,mods);
+ break;
+ default:
+ DEBUG(0,("ldapsam_modify_entry: Wrong LDAP operation type: %d!\n",
+ ldap_op));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (rc!=LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(ldap_state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
+ &ld_error);
+ DEBUG(1, ("ldapsam_modify_entry: Failed to %s user dn= %s with: %s\n\t%s\n",
+ ldap_op == LDAP_MOD_ADD ? "add" : "modify",
+ dn, ldap_err2string(rc),
+ ld_error?ld_error:"unknown"));
+ SAFE_FREE(ld_error);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ if (!(pdb_get_acct_ctrl(newpwd)&(ACB_WSTRUST|ACB_SVRTRUST|ACB_DOMTRUST)) &&
+ (lp_ldap_passwd_sync() != LDAP_PASSWD_SYNC_OFF) &&
+ need_update(newpwd, PDB_PLAINTEXT_PW) &&
+ (pdb_get_plaintext_passwd(newpwd)!=NULL)) {
+ BerElement *ber;
+ struct berval *bv;
+ char *retoid;
+ struct berval *retdata;
+ char *utf8_password;
+ char *utf8_dn;
+
+ if (push_utf8_allocate(&utf8_password, pdb_get_plaintext_passwd(newpwd)) == (size_t)-1) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (push_utf8_allocate(&utf8_dn, dn) == (size_t)-1) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if ((ber = ber_alloc_t(LBER_USE_DER))==NULL) {
+ DEBUG(0,("ber_alloc_t returns NULL\n"));
+ SAFE_FREE(utf8_password);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ber_printf (ber, "{");
+ ber_printf (ber, "ts", LDAP_TAG_EXOP_MODIFY_PASSWD_ID, utf8_dn);
+ ber_printf (ber, "ts", LDAP_TAG_EXOP_MODIFY_PASSWD_NEW, utf8_password);
+ ber_printf (ber, "N}");
+
+ if ((rc = ber_flatten (ber, &bv))<0) {
+ DEBUG(0,("ldapsam_modify_entry: ber_flatten returns a value <0\n"));
+ ber_free(ber,1);
+ SAFE_FREE(utf8_dn);
+ SAFE_FREE(utf8_password);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ SAFE_FREE(utf8_dn);
+ SAFE_FREE(utf8_password);
+ ber_free(ber, 1);
+
+ if ((rc = smbldap_extended_operation(ldap_state->smbldap_state,
+ LDAP_EXOP_MODIFY_PASSWD,
+ bv, NULL, NULL, &retoid,
+ &retdata)) != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(ldap_state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
+ &ld_error);
+ DEBUG(0,("ldapsam_modify_entry: LDAP Password could not be changed for user %s: %s\n\t%s\n",
+ pdb_get_username(newpwd), ldap_err2string(rc), ld_error?ld_error:"unknown"));
+ SAFE_FREE(ld_error);
+ ber_bvfree(bv);
+ return NT_STATUS_UNSUCCESSFUL;
+ } else {
+ DEBUG(3,("ldapsam_modify_entry: LDAP Password changed for user %s\n",pdb_get_username(newpwd)));
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("ldapsam_modify_entry: LDAP Password changed to %s\n",pdb_get_plaintext_passwd(newpwd)));
+#endif
+ ber_bvfree(retdata);
+ ber_memfree(retoid);
+ }
+ ber_bvfree(bv);
+ }
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Delete entry from LDAP for username.
+*********************************************************************/
+
+static NTSTATUS ldapsam_delete_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT * sam_acct)
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ const char *sname;
+ int rc;
+ LDAPMessage *result = NULL;
+ NTSTATUS ret;
+ char **attr_list;
+ fstring objclass;
+
+ if (!sam_acct) {
+ DEBUG(0, ("ldapsam_delete_sam_account: sam_acct was NULL!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ sname = pdb_get_username(sam_acct);
+
+ DEBUG (3, ("ldapsam_delete_sam_account: Deleting user %s from LDAP.\n", sname));
+
+ attr_list= get_userattr_list( ldap_state->schema_ver );
+ rc = ldapsam_search_suffix_by_name(ldap_state, sname, &result, attr_list);
+
+ if (rc != LDAP_SUCCESS) {
+ free_attr_list( attr_list );
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ switch ( ldap_state->schema_ver ) {
+ case SCHEMAVER_SAMBASAMACCOUNT:
+ fstrcpy( objclass, LDAP_OBJ_SAMBASAMACCOUNT );
+ break;
+
+ case SCHEMAVER_SAMBAACCOUNT:
+ fstrcpy( objclass, LDAP_OBJ_SAMBAACCOUNT );
+ break;
+ default:
+ fstrcpy( objclass, "UNKNOWN" );
+ DEBUG(0,("ldapsam_delete_sam_account: Unknown schema version specified!\n"));
+ break;
+ }
+
+ ret = ldapsam_delete_entry(ldap_state, result, objclass, attr_list );
+ ldap_msgfree(result);
+ free_attr_list( attr_list );
+
+ return ret;
+}
+
+/**********************************************************************
+ Helper function to determine for update_sam_account whether
+ we need LDAP modification.
+*********************************************************************/
+
+static BOOL element_is_changed(const SAM_ACCOUNT *sampass,
+ enum pdb_elements element)
+{
+ return IS_SAM_CHANGED(sampass, element);
+}
+
+/**********************************************************************
+ Update SAM_ACCOUNT.
+*********************************************************************/
+
+static NTSTATUS ldapsam_update_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT * newpwd)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ int rc = 0;
+ char *dn;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ LDAPMod **mods = NULL;
+ char **attr_list;
+
+ result = pdb_get_backend_private_data(newpwd, my_methods);
+ if (!result) {
+ attr_list = get_userattr_list(ldap_state->schema_ver);
+ rc = ldapsam_search_suffix_by_name(ldap_state, pdb_get_username(newpwd), &result, attr_list );
+ free_attr_list( attr_list );
+ if (rc != LDAP_SUCCESS) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ pdb_set_backend_private_data(newpwd, result, private_data_free_fn, my_methods, PDB_CHANGED);
+ }
+
+ if (ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result) == 0) {
+ DEBUG(0, ("ldapsam_update_sam_account: No user to modify!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, result);
+ dn = smbldap_get_dn(ldap_state->smbldap_state->ldap_struct, entry);
+ if (!dn) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(4, ("ldapsam_update_sam_account: user %s to be modified has dn: %s\n", pdb_get_username(newpwd), dn));
+
+ if (!init_ldap_from_sam(ldap_state, entry, &mods, newpwd,
+ element_is_changed)) {
+ DEBUG(0, ("ldapsam_update_sam_account: init_ldap_from_sam failed!\n"));
+ SAFE_FREE(dn);
+ if (mods != NULL)
+ ldap_mods_free(mods,True);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (mods == NULL) {
+ DEBUG(4,("ldapsam_update_sam_account: mods is empty: nothing to update for user: %s\n",
+ pdb_get_username(newpwd)));
+ SAFE_FREE(dn);
+ return NT_STATUS_OK;
+ }
+
+ ret = ldapsam_modify_entry(my_methods,newpwd,dn,mods,LDAP_MOD_REPLACE, element_is_changed);
+ ldap_mods_free(mods,True);
+ SAFE_FREE(dn);
+
+ if (!NT_STATUS_IS_OK(ret)) {
+ char *ld_error = NULL;
+ ldap_get_option(ldap_state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
+ &ld_error);
+ DEBUG(0,("ldapsam_update_sam_account: failed to modify user with uid = %s, error: %s (%s)\n",
+ pdb_get_username(newpwd), ld_error?ld_error:"(unknwon)", ldap_err2string(rc)));
+ SAFE_FREE(ld_error);
+ return ret;
+ }
+
+ DEBUG(2, ("ldapsam_update_sam_account: successfully modified uid = %s in the LDAP database\n",
+ pdb_get_username(newpwd)));
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Helper function to determine for update_sam_account whether
+ we need LDAP modification.
+ *********************************************************************/
+
+static BOOL element_is_set_or_changed(const SAM_ACCOUNT *sampass,
+ enum pdb_elements element)
+{
+ return (IS_SAM_SET(sampass, element) ||
+ IS_SAM_CHANGED(sampass, element));
+}
+
+/**********************************************************************
+ Add SAM_ACCOUNT to LDAP.
+*********************************************************************/
+
+static NTSTATUS ldapsam_add_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT * newpwd)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ int rc;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ pstring dn;
+ LDAPMod **mods = NULL;
+ int ldap_op = LDAP_MOD_REPLACE;
+ uint32 num_result;
+ char **attr_list;
+ char *escape_user;
+ const char *username = pdb_get_username(newpwd);
+ const DOM_SID *sid = pdb_get_user_sid(newpwd);
+ pstring filter;
+ fstring sid_string;
+
+ if (!username || !*username) {
+ DEBUG(0, ("ldapsam_add_sam_account: Cannot add user without a username!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* free this list after the second search or in case we exit on failure */
+ attr_list = get_userattr_list(ldap_state->schema_ver);
+
+ rc = ldapsam_search_suffix_by_name (ldap_state, username, &result, attr_list);
+
+ if (rc != LDAP_SUCCESS) {
+ free_attr_list( attr_list );
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result) != 0) {
+ DEBUG(0,("ldapsam_add_sam_account: User '%s' already in the base, with samba attributes\n",
+ username));
+ ldap_msgfree(result);
+ free_attr_list( attr_list );
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ ldap_msgfree(result);
+ result = NULL;
+
+ if (element_is_set_or_changed(newpwd, PDB_USERSID)) {
+ rc = ldapsam_get_ldap_user_by_sid(ldap_state,
+ sid, &result);
+ if (rc == LDAP_SUCCESS) {
+ if (ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result) != 0) {
+ DEBUG(0,("ldapsam_add_sam_account: SID '%s' already in the base, with samba attributes\n",
+ sid_to_string(sid_string, sid)));
+ free_attr_list( attr_list );
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ ldap_msgfree(result);
+ }
+ }
+
+ /* does the entry already exist but without a samba attributes?
+ we need to return the samba attributes here */
+
+ escape_user = escape_ldap_string_alloc( username );
+ pstrcpy( filter, lp_ldap_filter() );
+ all_string_sub( filter, "%u", escape_user, sizeof(filter) );
+ SAFE_FREE( escape_user );
+
+ rc = smbldap_search_suffix(ldap_state->smbldap_state,
+ filter, attr_list, &result);
+ if ( rc != LDAP_SUCCESS ) {
+ free_attr_list( attr_list );
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ num_result = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result);
+
+ if (num_result > 1) {
+ DEBUG (0, ("ldapsam_add_sam_account: More than one user with that uid exists: bailing out!\n"));
+ free_attr_list( attr_list );
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Check if we need to update an existing entry */
+ if (num_result == 1) {
+ char *tmp;
+
+ DEBUG(3,("ldapsam_add_sam_account: User exists without samba attributes: adding them\n"));
+ ldap_op = LDAP_MOD_REPLACE;
+ entry = ldap_first_entry (ldap_state->smbldap_state->ldap_struct, result);
+ tmp = smbldap_get_dn (ldap_state->smbldap_state->ldap_struct, entry);
+ if (!tmp) {
+ free_attr_list( attr_list );
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ slprintf (dn, sizeof (dn) - 1, "%s", tmp);
+ SAFE_FREE(tmp);
+
+ } else if (ldap_state->schema_ver == SCHEMAVER_SAMBASAMACCOUNT) {
+
+ /* There might be a SID for this account already - say an idmap entry */
+
+ pstr_sprintf(filter, "(&(%s=%s)(|(objectClass=%s)(objectClass=%s)))",
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_SID),
+ sid_to_string(sid_string, sid),
+ LDAP_OBJ_IDMAP_ENTRY,
+ LDAP_OBJ_SID_ENTRY);
+
+ /* free old result before doing a new search */
+ if (result != NULL) {
+ ldap_msgfree(result);
+ result = NULL;
+ }
+ rc = smbldap_search_suffix(ldap_state->smbldap_state,
+ filter, attr_list, &result);
+
+ if ( rc != LDAP_SUCCESS ) {
+ free_attr_list( attr_list );
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ num_result = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result);
+
+ if (num_result > 1) {
+ DEBUG (0, ("ldapsam_add_sam_account: More than one user with that uid exists: bailing out!\n"));
+ free_attr_list( attr_list );
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Check if we need to update an existing entry */
+ if (num_result == 1) {
+ char *tmp;
+
+ DEBUG(3,("ldapsam_add_sam_account: User exists without samba attributes: adding them\n"));
+ ldap_op = LDAP_MOD_REPLACE;
+ entry = ldap_first_entry (ldap_state->smbldap_state->ldap_struct, result);
+ tmp = smbldap_get_dn (ldap_state->smbldap_state->ldap_struct, entry);
+ if (!tmp) {
+ free_attr_list( attr_list );
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ slprintf (dn, sizeof (dn) - 1, "%s", tmp);
+ SAFE_FREE(tmp);
+ }
+ }
+
+ free_attr_list( attr_list );
+
+ if (num_result == 0) {
+ /* Check if we need to add an entry */
+ DEBUG(3,("ldapsam_add_sam_account: Adding new user\n"));
+ ldap_op = LDAP_MOD_ADD;
+ if (username[strlen(username)-1] == '$') {
+ slprintf (dn, sizeof (dn) - 1, "uid=%s,%s", username, lp_ldap_machine_suffix ());
+ } else {
+ slprintf (dn, sizeof (dn) - 1, "uid=%s,%s", username, lp_ldap_user_suffix ());
+ }
+ }
+
+ if (!init_ldap_from_sam(ldap_state, entry, &mods, newpwd,
+ element_is_set_or_changed)) {
+ DEBUG(0, ("ldapsam_add_sam_account: init_ldap_from_sam failed!\n"));
+ ldap_msgfree(result);
+ if (mods != NULL)
+ ldap_mods_free(mods,True);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ldap_msgfree(result);
+
+ if (mods == NULL) {
+ DEBUG(0,("ldapsam_add_sam_account: mods is empty: nothing to add for user: %s\n",pdb_get_username(newpwd)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ switch ( ldap_state->schema_ver ) {
+ case SCHEMAVER_SAMBAACCOUNT:
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectclass", LDAP_OBJ_SAMBAACCOUNT);
+ break;
+ case SCHEMAVER_SAMBASAMACCOUNT:
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectclass", LDAP_OBJ_SAMBASAMACCOUNT);
+ break;
+ default:
+ DEBUG(0,("ldapsam_add_sam_account: invalid schema version specified\n"));
+ break;
+ }
+
+ ret = ldapsam_modify_entry(my_methods,newpwd,dn,mods,ldap_op, element_is_set_or_changed);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("ldapsam_add_sam_account: failed to modify/add user with uid = %s (dn = %s)\n",
+ pdb_get_username(newpwd),dn));
+ ldap_mods_free(mods, True);
+ return ret;
+ }
+
+ DEBUG(2,("ldapsam_add_sam_account: added: uid == %s in the LDAP database\n", pdb_get_username(newpwd)));
+ ldap_mods_free(mods, True);
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static int ldapsam_search_one_group (struct ldapsam_privates *ldap_state,
+ const char *filter,
+ LDAPMessage ** result)
+{
+ int scope = LDAP_SCOPE_SUBTREE;
+ int rc;
+ char **attr_list;
+
+ attr_list = get_attr_list(groupmap_attr_list);
+ rc = smbldap_search(ldap_state->smbldap_state,
+ lp_ldap_group_suffix (), scope,
+ filter, attr_list, 0, result);
+ free_attr_list( attr_list );
+
+ if (rc != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(ldap_state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
+ &ld_error);
+ DEBUG(0, ("ldapsam_search_one_group: "
+ "Problem during the LDAP search: LDAP error: %s (%s)\n",
+ ld_error?ld_error:"(unknown)", ldap_err2string(rc)));
+ DEBUGADD(3, ("ldapsam_search_one_group: Query was: %s, %s\n",
+ lp_ldap_group_suffix(), filter));
+ SAFE_FREE(ld_error);
+ }
+
+ return rc;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static BOOL init_group_from_ldap(struct ldapsam_privates *ldap_state,
+ GROUP_MAP *map, LDAPMessage *entry)
+{
+ pstring temp;
+
+ if (ldap_state == NULL || map == NULL || entry == NULL ||
+ ldap_state->smbldap_state->ldap_struct == NULL) {
+ DEBUG(0, ("init_group_from_ldap: NULL parameters found!\n"));
+ return False;
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_attr_key2string(groupmap_attr_list, LDAP_ATTR_GIDNUMBER), temp)) {
+ DEBUG(0, ("init_group_from_ldap: Mandatory attribute %s not found\n",
+ get_attr_key2string( groupmap_attr_list, LDAP_ATTR_GIDNUMBER)));
+ return False;
+ }
+ DEBUG(2, ("init_group_from_ldap: Entry found for group: %s\n", temp));
+
+ map->gid = (gid_t)atol(temp);
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_attr_key2string( groupmap_attr_list, LDAP_ATTR_GROUP_SID), temp)) {
+ DEBUG(0, ("init_group_from_ldap: Mandatory attribute %s not found\n",
+ get_attr_key2string( groupmap_attr_list, LDAP_ATTR_GROUP_SID)));
+ return False;
+ }
+
+ if (!string_to_sid(&map->sid, temp)) {
+ DEBUG(1, ("SID string [%s] could not be read as a valid SID\n", temp));
+ return False;
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_attr_key2string( groupmap_attr_list, LDAP_ATTR_GROUP_TYPE), temp)) {
+ DEBUG(0, ("init_group_from_ldap: Mandatory attribute %s not found\n",
+ get_attr_key2string( groupmap_attr_list, LDAP_ATTR_GROUP_TYPE)));
+ return False;
+ }
+ map->sid_name_use = (enum SID_NAME_USE)atol(temp);
+
+ if ((map->sid_name_use < SID_NAME_USER) ||
+ (map->sid_name_use > SID_NAME_UNKNOWN)) {
+ DEBUG(0, ("init_group_from_ldap: Unknown Group type: %d\n", map->sid_name_use));
+ return False;
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_attr_key2string( groupmap_attr_list, LDAP_ATTR_DISPLAY_NAME), temp)) {
+ temp[0] = '\0';
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_attr_key2string( groupmap_attr_list, LDAP_ATTR_CN), temp))
+ {
+ DEBUG(0, ("init_group_from_ldap: Attributes cn not found either \
+for gidNumber(%lu)\n",(unsigned long)map->gid));
+ return False;
+ }
+ }
+ fstrcpy(map->nt_name, temp);
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_attr_key2string( groupmap_attr_list, LDAP_ATTR_DESC), temp)) {
+ temp[0] = '\0';
+ }
+ fstrcpy(map->comment, temp);
+
+ return True;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static BOOL init_ldap_from_group(LDAP *ldap_struct,
+ LDAPMessage *existing,
+ LDAPMod ***mods,
+ const GROUP_MAP *map)
+{
+ pstring tmp;
+
+ if (mods == NULL || map == NULL) {
+ DEBUG(0, ("init_ldap_from_group: NULL parameters found!\n"));
+ return False;
+ }
+
+ *mods = NULL;
+
+ sid_to_string(tmp, &map->sid);
+
+ smbldap_make_mod(ldap_struct, existing, mods,
+ get_attr_key2string(groupmap_attr_list, LDAP_ATTR_GROUP_SID), tmp);
+ pstr_sprintf(tmp, "%i", map->sid_name_use);
+ smbldap_make_mod(ldap_struct, existing, mods,
+ get_attr_key2string(groupmap_attr_list, LDAP_ATTR_GROUP_TYPE), tmp);
+
+ smbldap_make_mod(ldap_struct, existing, mods,
+ get_attr_key2string( groupmap_attr_list, LDAP_ATTR_DISPLAY_NAME), map->nt_name);
+ smbldap_make_mod(ldap_struct, existing, mods,
+ get_attr_key2string( groupmap_attr_list, LDAP_ATTR_DESC), map->comment);
+
+ return True;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_getgroup(struct pdb_methods *methods,
+ const char *filter,
+ GROUP_MAP *map)
+{
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ int count;
+
+ if (ldapsam_search_one_group(ldap_state, filter, &result)
+ != LDAP_SUCCESS) {
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result);
+
+ if (count < 1) {
+ DEBUG(4, ("ldapsam_getgroup: Did not find group\n"));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ if (count > 1) {
+ DEBUG(1, ("ldapsam_getgroup: Duplicate entries for filter %s: count=%d\n",
+ filter, count));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, result);
+
+ if (!entry) {
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!init_group_from_ldap(ldap_state, map, entry)) {
+ DEBUG(1, ("ldapsam_getgroup: init_group_from_ldap failed for group filter %s\n",
+ filter));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ ldap_msgfree(result);
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_getgrsid(struct pdb_methods *methods, GROUP_MAP *map,
+ DOM_SID sid)
+{
+ pstring filter;
+
+ pstr_sprintf(filter, "(&(objectClass=%s)(%s=%s))",
+ LDAP_OBJ_GROUPMAP,
+ get_attr_key2string(groupmap_attr_list, LDAP_ATTR_GROUP_SID),
+ sid_string_static(&sid));
+
+ return ldapsam_getgroup(methods, filter, map);
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_getgrgid(struct pdb_methods *methods, GROUP_MAP *map,
+ gid_t gid)
+{
+ pstring filter;
+
+ pstr_sprintf(filter, "(&(objectClass=%s)(%s=%lu))",
+ LDAP_OBJ_GROUPMAP,
+ get_attr_key2string(groupmap_attr_list, LDAP_ATTR_GIDNUMBER),
+ (unsigned long)gid);
+
+ return ldapsam_getgroup(methods, filter, map);
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_getgrnam(struct pdb_methods *methods, GROUP_MAP *map,
+ const char *name)
+{
+ pstring filter;
+ char *escape_name = escape_ldap_string_alloc(name);
+
+ if (!escape_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ pstr_sprintf(filter, "(&(objectClass=%s)(|(%s=%s)(%s=%s)))",
+ LDAP_OBJ_GROUPMAP,
+ get_attr_key2string(groupmap_attr_list, LDAP_ATTR_DISPLAY_NAME), escape_name,
+ get_attr_key2string(groupmap_attr_list, LDAP_ATTR_CN), escape_name);
+
+ SAFE_FREE(escape_name);
+
+ return ldapsam_getgroup(methods, filter, map);
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static int ldapsam_search_one_group_by_gid(struct ldapsam_privates *ldap_state,
+ gid_t gid,
+ LDAPMessage **result)
+{
+ pstring filter;
+
+ pstr_sprintf(filter, "(&(|(objectClass=%s)(objectclass=%s))(%s=%lu))",
+ LDAP_OBJ_POSIXGROUP, LDAP_OBJ_IDMAP_ENTRY,
+ get_attr_key2string(groupmap_attr_list, LDAP_ATTR_GIDNUMBER),
+ (unsigned long)gid);
+
+ return ldapsam_search_one_group(ldap_state, filter, result);
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_add_group_mapping_entry(struct pdb_methods *methods,
+ GROUP_MAP *map)
+{
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+ LDAPMessage *result = NULL;
+ LDAPMod **mods = NULL;
+ int count;
+
+ char *tmp;
+ pstring dn;
+ LDAPMessage *entry;
+
+ GROUP_MAP dummy;
+
+ int rc;
+
+ if (NT_STATUS_IS_OK(ldapsam_getgrgid(methods, &dummy,
+ map->gid))) {
+ DEBUG(0, ("ldapsam_add_group_mapping_entry: Group %ld already exists in LDAP\n", (unsigned long)map->gid));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ rc = ldapsam_search_one_group_by_gid(ldap_state, map->gid, &result);
+ if (rc != LDAP_SUCCESS) {
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result);
+
+ if ( count == 0 ) {
+ /* There's no posixGroup account, let's try to find an
+ * appropriate idmap entry for aliases */
+
+ pstring suffix;
+ pstring filter;
+ char **attr_list;
+
+ ldap_msgfree(result);
+
+ pstrcpy( suffix, lp_ldap_idmap_suffix() );
+ pstr_sprintf(filter, "(&(objectClass=%s)(%s=%u))",
+ LDAP_OBJ_IDMAP_ENTRY, LDAP_ATTRIBUTE_GIDNUMBER,
+ map->gid);
+
+ attr_list = get_attr_list( sidmap_attr_list );
+ rc = smbldap_search(ldap_state->smbldap_state, suffix,
+ LDAP_SCOPE_SUBTREE, filter, attr_list,
+ 0, &result);
+
+ free_attr_list(attr_list);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(3,("Failure looking up entry (%s)\n",
+ ldap_err2string(rc) ));
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result);
+ if ( count == 0 ) {
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (count > 1) {
+ DEBUG(2, ("ldapsam_add_group_mapping_entry: Group %lu must exist exactly once in LDAP\n",
+ (unsigned long)map->gid));
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, result);
+ tmp = smbldap_get_dn(ldap_state->smbldap_state->ldap_struct, entry);
+ if (!tmp) {
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ pstrcpy(dn, tmp);
+ SAFE_FREE(tmp);
+
+ if (!init_ldap_from_group(ldap_state->smbldap_state->ldap_struct,
+ result, &mods, map)) {
+ DEBUG(0, ("ldapsam_add_group_mapping_entry: init_ldap_from_group failed!\n"));
+ ldap_mods_free(mods, True);
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ldap_msgfree(result);
+
+ if (mods == NULL) {
+ DEBUG(0, ("ldapsam_add_group_mapping_entry: mods is empty\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_GROUPMAP );
+
+ rc = smbldap_modify(ldap_state->smbldap_state, dn, mods);
+ ldap_mods_free(mods, True);
+
+ if (rc != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(ldap_state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
+ &ld_error);
+ DEBUG(0, ("ldapsam_add_group_mapping_entry: failed to add group %lu error: %s (%s)\n", (unsigned long)map->gid,
+ ld_error ? ld_error : "(unknown)", ldap_err2string(rc)));
+ SAFE_FREE(ld_error);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(2, ("ldapsam_add_group_mapping_entry: successfully modified group %lu in LDAP\n", (unsigned long)map->gid));
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_update_group_mapping_entry(struct pdb_methods *methods,
+ GROUP_MAP *map)
+{
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+ int rc;
+ char *dn = NULL;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ LDAPMod **mods = NULL;
+
+ rc = ldapsam_search_one_group_by_gid(ldap_state, map->gid, &result);
+
+ if (rc != LDAP_SUCCESS) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result) == 0) {
+ DEBUG(0, ("ldapsam_update_group_mapping_entry: No group to modify!\n"));
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, result);
+
+ if (!init_ldap_from_group(ldap_state->smbldap_state->ldap_struct,
+ result, &mods, map)) {
+ DEBUG(0, ("ldapsam_update_group_mapping_entry: init_ldap_from_group failed\n"));
+ ldap_msgfree(result);
+ if (mods != NULL)
+ ldap_mods_free(mods,True);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (mods == NULL) {
+ DEBUG(4, ("ldapsam_update_group_mapping_entry: mods is empty: nothing to do\n"));
+ ldap_msgfree(result);
+ return NT_STATUS_OK;
+ }
+
+ dn = smbldap_get_dn(ldap_state->smbldap_state->ldap_struct, entry);
+ if (!dn) {
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ rc = smbldap_modify(ldap_state->smbldap_state, dn, mods);
+ SAFE_FREE(dn);
+
+ ldap_mods_free(mods, True);
+ ldap_msgfree(result);
+
+ if (rc != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(ldap_state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
+ &ld_error);
+ DEBUG(0, ("ldapsam_update_group_mapping_entry: failed to modify group %lu error: %s (%s)\n", (unsigned long)map->gid,
+ ld_error ? ld_error : "(unknown)", ldap_err2string(rc)));
+ SAFE_FREE(ld_error);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(2, ("ldapsam_update_group_mapping_entry: successfully modified group %lu in LDAP\n", (unsigned long)map->gid));
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_delete_group_mapping_entry(struct pdb_methods *methods,
+ DOM_SID sid)
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)methods->private_data;
+ pstring sidstring, filter;
+ LDAPMessage *result = NULL;
+ int rc;
+ NTSTATUS ret;
+ char **attr_list;
+
+ sid_to_string(sidstring, &sid);
+
+ pstr_sprintf(filter, "(&(objectClass=%s)(%s=%s))",
+ LDAP_OBJ_GROUPMAP, LDAP_ATTRIBUTE_SID, sidstring);
+
+ rc = ldapsam_search_one_group(ldap_state, filter, &result);
+
+ if (rc != LDAP_SUCCESS) {
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ attr_list = get_attr_list( groupmap_attr_list_to_delete );
+ ret = ldapsam_delete_entry(ldap_state, result, LDAP_OBJ_GROUPMAP, attr_list);
+ free_attr_list ( attr_list );
+
+ ldap_msgfree(result);
+
+ return ret;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_setsamgrent(struct pdb_methods *my_methods, BOOL update)
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ fstring filter;
+ int rc;
+ char **attr_list;
+
+ pstr_sprintf( filter, "(objectclass=%s)", LDAP_OBJ_GROUPMAP);
+ attr_list = get_attr_list( groupmap_attr_list );
+ rc = smbldap_search(ldap_state->smbldap_state, lp_ldap_group_suffix(),
+ LDAP_SCOPE_SUBTREE, filter,
+ attr_list, 0, &ldap_state->result);
+ free_attr_list( attr_list );
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0, ("ldapsam_setsamgrent: LDAP search failed: %s\n", ldap_err2string(rc)));
+ DEBUG(3, ("ldapsam_setsamgrent: Query was: %s, %s\n", lp_ldap_group_suffix(), filter));
+ ldap_msgfree(ldap_state->result);
+ ldap_state->result = NULL;
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(2, ("ldapsam_setsampwent: %d entries in the base!\n",
+ ldap_count_entries(ldap_state->smbldap_state->ldap_struct,
+ ldap_state->result)));
+
+ ldap_state->entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, ldap_state->result);
+ ldap_state->index = 0;
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static void ldapsam_endsamgrent(struct pdb_methods *my_methods)
+{
+ ldapsam_endsampwent(my_methods);
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_getsamgrent(struct pdb_methods *my_methods,
+ GROUP_MAP *map)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ BOOL bret = False;
+
+ while (!bret) {
+ if (!ldap_state->entry)
+ return ret;
+
+ ldap_state->index++;
+ bret = init_group_from_ldap(ldap_state, map, ldap_state->entry);
+
+ ldap_state->entry = ldap_next_entry(ldap_state->smbldap_state->ldap_struct,
+ ldap_state->entry);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_enum_group_mapping(struct pdb_methods *methods,
+ enum SID_NAME_USE sid_name_use,
+ GROUP_MAP **rmap, int *num_entries,
+ BOOL unix_only)
+{
+ GROUP_MAP map;
+ GROUP_MAP *mapt;
+ int entries = 0;
+
+ *num_entries = 0;
+ *rmap = NULL;
+
+ if (!NT_STATUS_IS_OK(ldapsam_setsamgrent(methods, False))) {
+ DEBUG(0, ("ldapsam_enum_group_mapping: Unable to open passdb\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ while (NT_STATUS_IS_OK(ldapsam_getsamgrent(methods, &map))) {
+ if (sid_name_use != SID_NAME_UNKNOWN &&
+ sid_name_use != map.sid_name_use) {
+ DEBUG(11,("ldapsam_enum_group_mapping: group %s is not of the requested type\n", map.nt_name));
+ continue;
+ }
+ if (unix_only==ENUM_ONLY_MAPPED && map.gid==-1) {
+ DEBUG(11,("ldapsam_enum_group_mapping: group %s is non mapped\n", map.nt_name));
+ continue;
+ }
+
+ mapt=(GROUP_MAP *)Realloc((*rmap), (entries+1)*sizeof(GROUP_MAP));
+ if (!mapt) {
+ DEBUG(0,("ldapsam_enum_group_mapping: Unable to enlarge group map!\n"));
+ SAFE_FREE(*rmap);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ else
+ (*rmap) = mapt;
+
+ mapt[entries] = map;
+
+ entries += 1;
+
+ }
+ ldapsam_endsamgrent(methods);
+
+ *num_entries = entries;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ldapsam_modify_aliasmem(struct pdb_methods *methods,
+ const DOM_SID *alias,
+ const DOM_SID *member,
+ int modop)
+{
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+ char *dn;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ int count;
+ LDAPMod **mods = NULL;
+ int rc;
+
+ pstring filter;
+
+ pstr_sprintf(filter, "(&(|(objectClass=%s)(objectclass=%s))(%s=%s))",
+ LDAP_OBJ_GROUPMAP, LDAP_OBJ_IDMAP_ENTRY,
+ get_attr_key2string(groupmap_attr_list,
+ LDAP_ATTR_GROUP_SID),
+ sid_string_static(alias));
+
+ if (ldapsam_search_one_group(ldap_state, filter,
+ &result) != LDAP_SUCCESS)
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct,
+ result);
+
+ if (count < 1) {
+ DEBUG(4, ("ldapsam_add_aliasmem: Did not find alias\n"));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_ALIAS;
+ }
+
+ if (count > 1) {
+ DEBUG(1, ("ldapsam_getgroup: Duplicate entries for filter %s: "
+ "count=%d\n", filter, count));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_ALIAS;
+ }
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct,
+ result);
+
+ if (!entry) {
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ dn = smbldap_get_dn(ldap_state->smbldap_state->ldap_struct, entry);
+ if (!dn) {
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ smbldap_set_mod(&mods, modop,
+ get_attr_key2string(groupmap_attr_list,
+ LDAP_ATTR_SID_LIST),
+ sid_string_static(member));
+
+ rc = smbldap_modify(ldap_state->smbldap_state, dn, mods);
+
+ ldap_mods_free(mods, True);
+ ldap_msgfree(result);
+
+ if (rc != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(ldap_state->smbldap_state->ldap_struct,
+ LDAP_OPT_ERROR_STRING,&ld_error);
+
+ DEBUG(0, ("ldapsam_delete_entry: Could not delete attributes "
+ "for %s, error: %s (%s)\n", dn, ldap_err2string(rc),
+ ld_error?ld_error:"unknown"));
+ SAFE_FREE(ld_error);
+ SAFE_FREE(dn);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ SAFE_FREE(dn);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ldapsam_add_aliasmem(struct pdb_methods *methods,
+ const DOM_SID *alias,
+ const DOM_SID *member)
+{
+ return ldapsam_modify_aliasmem(methods, alias, member, LDAP_MOD_ADD);
+}
+
+static NTSTATUS ldapsam_del_aliasmem(struct pdb_methods *methods,
+ const DOM_SID *alias,
+ const DOM_SID *member)
+{
+ return ldapsam_modify_aliasmem(methods, alias, member,
+ LDAP_MOD_DELETE);
+}
+
+static NTSTATUS ldapsam_enum_aliasmem(struct pdb_methods *methods,
+ const DOM_SID *alias, DOM_SID **members,
+ int *num_members)
+{
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ int count;
+ char **values;
+ int i;
+ pstring filter;
+
+ *members = NULL;
+ *num_members = 0;
+
+ pstr_sprintf(filter, "(&(|(objectClass=%s)(objectclass=%s))(%s=%s))",
+ LDAP_OBJ_GROUPMAP, LDAP_OBJ_IDMAP_ENTRY,
+ get_attr_key2string(groupmap_attr_list,
+ LDAP_ATTR_GROUP_SID),
+ sid_string_static(alias));
+
+ if (ldapsam_search_one_group(ldap_state, filter,
+ &result) != LDAP_SUCCESS)
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct,
+ result);
+
+ if (count < 1) {
+ DEBUG(4, ("ldapsam_add_aliasmem: Did not find alias\n"));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_ALIAS;
+ }
+
+ if (count > 1) {
+ DEBUG(1, ("ldapsam_getgroup: Duplicate entries for filter %s: "
+ "count=%d\n", filter, count));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_ALIAS;
+ }
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct,
+ result);
+
+ if (!entry) {
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ values = ldap_get_values(ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_attr_key2string(groupmap_attr_list,
+ LDAP_ATTR_SID_LIST));
+
+ if (values == NULL) {
+ ldap_msgfree(result);
+ return NT_STATUS_OK;
+ }
+
+ count = ldap_count_values(values);
+
+ for (i=0; i<count; i++) {
+ DOM_SID member;
+
+ if (!string_to_sid(&member, values[i]))
+ continue;
+
+ add_sid_to_array(&member, members, num_members);
+ }
+
+ ldap_value_free(values);
+ ldap_msgfree(result);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ldapsam_alias_memberships(struct pdb_methods *methods,
+ const DOM_SID *sid,
+ DOM_SID **aliases, int *num)
+{
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+
+ fstring sid_string;
+ const char *attrs[] = { LDAP_ATTRIBUTE_SID, NULL };
+
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ int count;
+ int rc;
+ pstring filter;
+
+ sid_to_string(sid_string, sid);
+ pstr_sprintf(filter, "(&(|(objectclass=%s)(objectclass=%s))(%s=%s))",
+ LDAP_OBJ_GROUPMAP, LDAP_OBJ_IDMAP_ENTRY,
+ get_attr_key2string(groupmap_attr_list,
+ LDAP_ATTR_SID_LIST), sid_string);
+
+ rc = smbldap_search(ldap_state->smbldap_state, lp_ldap_group_suffix(),
+ LDAP_SCOPE_SUBTREE, filter, attrs, 0, &result);
+
+ if (rc != LDAP_SUCCESS)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ *aliases = NULL;
+ *num = 0;
+
+ count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct,
+ result);
+
+ if (count < 1) {
+ ldap_msgfree(result);
+ return NT_STATUS_OK;
+ }
+
+
+ for (entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct,
+ result);
+ entry != NULL;
+ entry = ldap_next_entry(ldap_state->smbldap_state->ldap_struct,
+ entry))
+ {
+ DOM_SID alias;
+ char **vals;
+ vals = ldap_get_values(ldap_state->smbldap_state->ldap_struct,
+ entry, LDAP_ATTRIBUTE_SID);
+
+ if (vals == NULL)
+ continue;
+
+ if (vals[0] == NULL) {
+ ldap_value_free(vals);
+ continue;
+ }
+
+ if (!string_to_sid(&alias, vals[0])) {
+ ldap_value_free(vals);
+ continue;
+ }
+
+ add_sid_to_array(&alias, aliases, num);
+ ldap_value_free(vals);
+ }
+
+ ldap_msgfree(result);
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Privileges related functions
+ *********************************************************************/
+
+static NTSTATUS ldapsam_modify_sid_list_for_privilege(struct pdb_methods *my_methods, const char *privname, const DOM_SID *sid, int ldap_op)
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ LDAPMessage *entry = NULL;
+ LDAPMod **mods = NULL;
+ fstring sid_str;
+ fstring filter;
+ char **attr_list, *dn;
+ int rc;
+
+ if ((sid == NULL) || (!sid_to_string(sid_str, sid))) {
+ DEBUG(3, ("ldapsam_modify_sid_list_for_privilege: Invalid SID\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ pstr_sprintf(filter, "(&(objectclass=%s)(sambaPrivName=%s))", LDAP_OBJ_PRIVILEGE, privname);
+ attr_list = get_attr_list(privilege_attr_list);
+ rc = smbldap_search(ldap_state->smbldap_state, lp_ldap_privilege_suffix(),
+ LDAP_SCOPE_SUBTREE, filter,
+ attr_list, 0, &ldap_state->result);
+ free_attr_list(attr_list);
+
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0, ("ldapsam_modify_sid_list_for_privilege: LDAP search failed: %s\n", ldap_err2string(rc)));
+ DEBUG(3, ("ldapsam_modify_sid_list_for_privilege: Query was: %s, %s\n", lp_ldap_privilege_suffix(), filter));
+ ldap_msgfree(ldap_state->result);
+ ldap_state->result = NULL;
+ goto done;
+ }
+
+ if (ldap_count_entries(ldap_state->smbldap_state->ldap_struct, ldap_state->result) == 0) {
+ /* if the privilege does not exist and we are adding then
+ * create it */
+ if (ldap_op == LDAP_MOD_ADD) {
+
+ DEBUG(3, ("Privilege not found on ldap tree, creating a new entry\n"));
+ if (asprintf(&dn, "sambaPrivName=%s,%s", privname, lp_ldap_privilege_suffix()) < 0) {
+ DEBUG(0, ("ldapsam_modify_sid_list_for_privilege: Out of memory\n"));
+ goto done;
+ }
+
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, entry, &mods, "sambaPrivName", privname);
+
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectclass", LDAP_OBJ_PRIVILEGE);
+
+ rc = smbldap_add(ldap_state->smbldap_state, dn, mods);
+
+ if (rc != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+
+ ldap_get_option(ldap_state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING, &ld_error);
+ DEBUG(1,
+ ("ldapsam_modify_sid_list_for_privilege:"
+ "Failed to add privilege (%s) dn= %s with: %s\n\t%s\n",
+ privname,
+ dn, ldap_err2string(rc),
+ ld_error ? ld_error : "unknown")
+ );
+
+ SAFE_FREE(ld_error);
+ goto done;
+ }
+
+ pstr_sprintf(filter, "(&(objectclass=%s)(sambaPrivName=%s))", LDAP_OBJ_PRIVILEGE, privname);
+ attr_list = get_attr_list(privilege_attr_list);
+ rc = smbldap_search(ldap_state->smbldap_state, lp_ldap_privilege_suffix(),
+ LDAP_SCOPE_SUBTREE, filter,
+ attr_list, 0, &ldap_state->result);
+ free_attr_list(attr_list);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0, ("ldapsam_modify_sid_list_for_privilege: LDAP search failed: %s\n", ldap_err2string(rc)));
+ DEBUG(3, ("ldapsam_modify_sid_list_for_privilege: Query was: %s, %s\n", lp_ldap_privilege_suffix(), filter));
+ ldap_msgfree(ldap_state->result);
+ ldap_state->result = NULL;
+ goto done;
+ }
+ } else {
+ goto done;
+ }
+ }
+ /* entry found */
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, ldap_state->result);
+
+ /* retrieve the dn */
+ dn = smbldap_get_dn(ldap_state->smbldap_state->ldap_struct, entry);
+ if (!dn) {
+ goto done;
+ }
+
+ /* prepare the modification */
+ smbldap_set_mod(&mods, ldap_op, "sambaSIDList", sid_str);
+
+ /* modify the privilege */
+ rc = smbldap_modify(ldap_state->smbldap_state, dn, mods);
+
+ /* free used structures */
+ ldap_mods_free(mods, True);
+
+ if (rc != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+
+ ldap_get_option(ldap_state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING, &ld_error);
+ DEBUG(1,
+ ("ldapsam_modify_sid_list_for_privilege:"
+ "Failed to %s sid for privilege (%s) dn= %s with: %s\n\t%s\n",
+ (ldap_op == LDAP_MOD_ADD) ? "add" : "remove",
+ privname,
+ dn, ldap_err2string(rc),
+ ld_error ? ld_error : "unknown")
+ );
+ SAFE_FREE(ld_error);
+ goto done;
+ }
+
+ ret = NT_STATUS_OK;
+
+done:
+ return ret;
+}
+
+static NTSTATUS ldapsam_add_sid_to_privilege(struct pdb_methods *my_methods, const char *privname, const DOM_SID *sid)
+{
+ return ldapsam_modify_sid_list_for_privilege(my_methods, privname, sid, LDAP_MOD_ADD);
+}
+
+static NTSTATUS ldapsam_remove_sid_from_privilege(struct pdb_methods *my_methods, const char *privname, const DOM_SID *sid)
+{
+ return ldapsam_modify_sid_list_for_privilege(my_methods, privname, sid, LDAP_MOD_DELETE);
+}
+
+static NTSTATUS ldapsam_get_privilege_set(struct pdb_methods *my_methods, DOM_SID *user_sids, int num_sids, PRIVILEGE_SET *privset)
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ LDAPMessage *entry = NULL;
+ fstring sid_str;
+ fstring filter;
+ char **sid_list;
+ char **attr_list;
+ int rc, i;
+
+ sid_list = (char **)malloc(sizeof(char *) * (num_sids + 1));
+ for (i = 0; i < num_sids; i++) {
+ sid_to_string(sid_str, &user_sids[i]);
+ sid_list[i] = strdup(sid_str);
+ if ( ! sid_list[i]) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ }
+ sid_list[i] = NULL;
+
+ pstr_sprintf(filter, "(objectclass=%s)", LDAP_OBJ_PRIVILEGE);
+ attr_list = get_attr_list(privilege_attr_list);
+ rc = smbldap_search(ldap_state->smbldap_state, lp_ldap_privilege_suffix(),
+ LDAP_SCOPE_SUBTREE, filter,
+ attr_list, 0, &ldap_state->result);
+ free_attr_list(attr_list);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0, ("ldapsam_get_privilege_set: LDAP search failed: %s\n", ldap_err2string(rc)));
+ DEBUG(3, ("ldapsam_get_privilege_set: Query was: %s, %s\n", lp_ldap_privilege_suffix(), filter));
+ ldap_msgfree(ldap_state->result);
+ ldap_state->result = NULL;
+ goto done;
+ }
+
+ if (ldap_count_entries(ldap_state->smbldap_state->ldap_struct, ldap_state->result) == 0) {
+ DEBUG(3, ("ldapsam_get_privilege_set: No privileges in ldap tree\n"));
+ ret = NT_STATUS_OK;
+ goto done;
+ }
+
+ DEBUG(2, ("ldapsam_get_privilege_set: %d entries in the base!\n",
+ ldap_count_entries(ldap_state->smbldap_state->ldap_struct, ldap_state->result)));
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, ldap_state->result);
+
+ while (entry != NULL) {
+ char **values = NULL;
+
+ for(i=0; sid_list[i] != NULL; i++) {
+ pstring privname;
+ int j;
+
+ if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry, "sambaPrivName", privname, sizeof(pstring))) {
+ goto loop;
+ }
+
+ if ((values = ldap_get_values(ldap_state->smbldap_state->ldap_struct, entry, LDAP_ATTRIBUTE_SID_LIST)) == NULL) {
+ DEBUG(10, ("ldapsam_get_privilege_set: SID List not found skipping privilege\n"));
+ goto loop;
+ }
+
+ j = 0;
+ while (values[j] != 0) {
+ if (strcmp(values[j], sid_list[i]) == 0) {
+ DEBUG(10, ("sid [%s] found in users sid list\n", sid_list[i]));
+ DEBUG(10, ("adding privilege [%s] to the users privilege list\n", privname));
+ add_privilege_by_name(privset, privname);
+ goto loop;
+ }
+ j++;
+ }
+
+ if (values) {
+ ldap_value_free(values);
+ values = NULL;
+ }
+ }
+ loop:
+ if (values) {
+ ldap_value_free(values);
+ }
+
+ entry = ldap_next_entry(ldap_state->smbldap_state->ldap_struct, entry);
+ }
+
+ ret = NT_STATUS_OK;
+
+done:
+ i = 0;
+ while (sid_list[i]) {
+ free(sid_list[i]);
+ i++;
+ }
+ free(sid_list);
+
+ return ret;
+}
+
+static NTSTATUS ldapsam_get_privilege_entry(struct pdb_methods *my_methods, const char *privname,
+ char **sid_list)
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ LDAPMessage *entry = NULL;
+ fstring filter;
+ char **attr_list, **values;
+ int rc, i, len;
+
+ *sid_list = NULL;
+ pstr_sprintf(filter, "(&(objectclass=%s)(sambaPrivName=%s))", LDAP_OBJ_PRIVILEGE, privname);
+ attr_list = get_attr_list(privilege_attr_list);
+ rc = smbldap_search(ldap_state->smbldap_state, lp_ldap_privilege_suffix(),
+ LDAP_SCOPE_SUBTREE, filter,
+ attr_list, 0, &ldap_state->result);
+ free_attr_list(attr_list);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0, ("ldapsam_get_privilege_entry: LDAP search failed: %s\n", ldap_err2string(rc)));
+ DEBUG(3, ("ldapsam_get_privilege_entry: Query was: %s, %s\n", lp_ldap_privilege_suffix(), filter));
+ ldap_msgfree(ldap_state->result);
+ ldap_state->result = NULL;
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (ldap_count_entries(ldap_state->smbldap_state->ldap_struct, ldap_state->result) == 0) {
+ DEBUG(3, ("ldapsam_get_privilege_entry: No such privilege (%s) in ldap tree\n", privname));
+ goto done;
+ }
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, ldap_state->result);
+
+ if ((values = ldap_get_values(ldap_state->smbldap_state->ldap_struct, entry, LDAP_ATTRIBUTE_SID_LIST)) == NULL) {
+ DEBUG(10, ("ldapsam_get_privilege_entry: SID List not found skipping privilege\n"));
+ ret = NT_STATUS_OK;
+ goto done;
+ }
+
+ for (i = 0, len = 0; values[i] != 0; i++ ) {
+ len = len + strlen(values[i]) + 1;
+ }
+
+ *sid_list = (char *)malloc(len);
+ if ((*sid_list) == NULL) {
+ DEBUG(0, ("ldapsam_get_privilege_entry: Out of memory!\n"));
+ ldap_value_free(values);
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ (*sid_list)[0] = '\0';
+
+ for (i = 0; values[i] != 0; i++ ) {
+ if (i != 0) {
+ strlcat(*sid_list, ",", len);
+ }
+ strlcat(*sid_list, values[i], len);
+ }
+
+ ldap_value_free(values);
+ ret = NT_STATUS_OK;
+done:
+ return ret;
+}
+
+
+/**********************************************************************
+ Housekeeping
+ *********************************************************************/
+
+static void free_private_data(void **vp)
+{
+ struct ldapsam_privates **ldap_state = (struct ldapsam_privates **)vp;
+
+ smbldap_free_struct(&(*ldap_state)->smbldap_state);
+
+ if ((*ldap_state)->result != NULL) {
+ ldap_msgfree((*ldap_state)->result);
+ (*ldap_state)->result = NULL;
+ }
+
+ *ldap_state = NULL;
+
+ /* No need to free any further, as it is talloc()ed */
+}
+
+/**********************************************************************
+ Intitalise the parts of the pdb_context that are common to all pdb_ldap modes
+ *********************************************************************/
+
+static NTSTATUS pdb_init_ldapsam_common(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method,
+ const char *location)
+{
+ NTSTATUS nt_status;
+ struct ldapsam_privates *ldap_state;
+
+ if (!NT_STATUS_IS_OK(nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
+ return nt_status;
+ }
+
+ (*pdb_method)->name = "ldapsam";
+
+ (*pdb_method)->setsampwent = ldapsam_setsampwent;
+ (*pdb_method)->endsampwent = ldapsam_endsampwent;
+ (*pdb_method)->getsampwent = ldapsam_getsampwent;
+ (*pdb_method)->getsampwnam = ldapsam_getsampwnam;
+ (*pdb_method)->getsampwsid = ldapsam_getsampwsid;
+ (*pdb_method)->add_sam_account = ldapsam_add_sam_account;
+ (*pdb_method)->update_sam_account = ldapsam_update_sam_account;
+ (*pdb_method)->delete_sam_account = ldapsam_delete_sam_account;
+
+ (*pdb_method)->getgrsid = ldapsam_getgrsid;
+ (*pdb_method)->getgrgid = ldapsam_getgrgid;
+ (*pdb_method)->getgrnam = ldapsam_getgrnam;
+ (*pdb_method)->add_group_mapping_entry = ldapsam_add_group_mapping_entry;
+ (*pdb_method)->update_group_mapping_entry = ldapsam_update_group_mapping_entry;
+ (*pdb_method)->delete_group_mapping_entry = ldapsam_delete_group_mapping_entry;
+ (*pdb_method)->enum_group_mapping = ldapsam_enum_group_mapping;
+
+ (*pdb_method)->add_sid_to_privilege = ldapsam_add_sid_to_privilege;
+ (*pdb_method)->remove_sid_from_privilege = ldapsam_remove_sid_from_privilege;
+ (*pdb_method)->get_privilege_set = ldapsam_get_privilege_set;
+ (*pdb_method)->get_privilege_entry = ldapsam_get_privilege_entry;
+
+ /* TODO: Setup private data and free */
+
+ ldap_state = talloc_zero(pdb_context->mem_ctx, sizeof(*ldap_state));
+ if (!ldap_state) {
+ DEBUG(0, ("pdb_init_ldapsam_common: talloc() failed for ldapsam private_data!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status =
+ smbldap_init(pdb_context->mem_ctx, location,
+ &ldap_state->smbldap_state)));
+
+ ldap_state->domain_name = talloc_strdup(pdb_context->mem_ctx, get_global_sam_name());
+ if (!ldap_state->domain_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*pdb_method)->private_data = ldap_state;
+
+ (*pdb_method)->free_private_data = free_private_data;
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Initialise the 'compat' mode for pdb_ldap
+ *********************************************************************/
+
+static NTSTATUS pdb_init_ldapsam_compat(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
+{
+ NTSTATUS nt_status;
+ struct ldapsam_privates *ldap_state;
+
+#ifdef WITH_LDAP_SAMCONFIG
+ if (!location) {
+ int ldap_port = lp_ldap_port();
+
+ /* remap default port if not using SSL (ie clear or TLS) */
+ if ( (lp_ldap_ssl() != LDAP_SSL_ON) && (ldap_port == 636) ) {
+ ldap_port = 389;
+ }
+
+ location = talloc_asprintf(pdb_context->mem_ctx, "%s://%s:%d", lp_ldap_ssl() == LDAP_SSL_ON ? "ldaps" : "ldap", lp_ldap_server(), ldap_port);
+ if (!location) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+#endif
+
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_ldapsam_common(pdb_context, pdb_method, location))) {
+ return nt_status;
+ }
+
+ (*pdb_method)->name = "ldapsam_compat";
+
+ ldap_state = (*pdb_method)->private_data;
+ ldap_state->schema_ver = SCHEMAVER_SAMBAACCOUNT;
+
+ sid_copy(&ldap_state->domain_sid, get_global_sam_sid());
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Initialise the normal mode for pdb_ldap
+ *********************************************************************/
+
+static NTSTATUS pdb_init_ldapsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
+{
+ NTSTATUS nt_status;
+ struct ldapsam_privates *ldap_state;
+ uint32 alg_rid_base;
+ pstring alg_rid_base_string;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ DOM_SID ldap_domain_sid;
+ DOM_SID secrets_domain_sid;
+ pstring domain_sid_string;
+
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_ldapsam_common(pdb_context, pdb_method, location))) {
+ return nt_status;
+ }
+
+ (*pdb_method)->name = "ldapsam";
+
+ (*pdb_method)->add_aliasmem = ldapsam_add_aliasmem;
+ (*pdb_method)->del_aliasmem = ldapsam_del_aliasmem;
+ (*pdb_method)->enum_aliasmem = ldapsam_enum_aliasmem;
+ (*pdb_method)->enum_alias_memberships = ldapsam_alias_memberships;
+
+ ldap_state = (*pdb_method)->private_data;
+ ldap_state->schema_ver = SCHEMAVER_SAMBASAMACCOUNT;
+
+ /* Try to setup the Domain Name, Domain SID, algorithmic rid base */
+
+ nt_status = smbldap_search_domain_info(ldap_state->smbldap_state, &result,
+ ldap_state->domain_name, True);
+
+ if ( !NT_STATUS_IS_OK(nt_status) ) {
+ DEBUG(2, ("pdb_init_ldapsam: WARNING: Could not get domain info, nor add one to the domain\n"));
+ DEBUGADD(2, ("pdb_init_ldapsam: Continuing on regardless, will be unable to allocate new users/groups, \
+and will risk BDCs having inconsistant SIDs\n"));
+ sid_copy(&ldap_state->domain_sid, get_global_sam_sid());
+ return NT_STATUS_OK;
+ }
+
+ /* Given that the above might fail, everything below this must be optional */
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, result);
+ if (!entry) {
+ DEBUG(0, ("pdb_init_ldapsam: Could not get domain info entry\n"));
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_SID),
+ domain_sid_string)) {
+ BOOL found_sid;
+ if (!string_to_sid(&ldap_domain_sid, domain_sid_string)) {
+ DEBUG(1, ("pdb_init_ldapsam: SID [%s] could not be read as a valid SID\n", domain_sid_string));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ found_sid = secrets_fetch_domain_sid(ldap_state->domain_name, &secrets_domain_sid);
+ if (!found_sid || !sid_equal(&secrets_domain_sid, &ldap_domain_sid)) {
+ fstring new_sid_str, old_sid_str;
+ DEBUG(1, ("pdb_init_ldapsam: Resetting SID for domain %s based on pdb_ldap results %s -> %s\n",
+ ldap_state->domain_name,
+ sid_to_string(old_sid_str, &secrets_domain_sid),
+ sid_to_string(new_sid_str, &ldap_domain_sid)));
+
+ /* reset secrets.tdb sid */
+ secrets_store_domain_sid(ldap_state->domain_name, &ldap_domain_sid);
+ DEBUG(1, ("New global sam SID: %s\n", sid_to_string(new_sid_str, get_global_sam_sid())));
+ }
+ sid_copy(&ldap_state->domain_sid, &ldap_domain_sid);
+ }
+
+ if (smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_ALGORITHMIC_RID_BASE),
+ alg_rid_base_string)) {
+ alg_rid_base = (uint32)atol(alg_rid_base_string);
+ if (alg_rid_base != algorithmic_rid_base()) {
+ DEBUG(0, ("The value of 'algorithmic RID base' has changed since the LDAP\n"
+ "database was initialised. Aborting. \n"));
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+ ldap_msgfree(result);
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_ldap_init(void)
+{
+ NTSTATUS nt_status;
+ if (!NT_STATUS_IS_OK(nt_status = smb_register_passdb(PASSDB_INTERFACE_VERSION, "ldapsam", pdb_init_ldapsam)))
+ return nt_status;
+
+ if (!NT_STATUS_IS_OK(nt_status = smb_register_passdb(PASSDB_INTERFACE_VERSION, "ldapsam_compat", pdb_init_ldapsam_compat)))
+ return nt_status;
+
+ return NT_STATUS_OK;
+}
diff --git a/source/passdb/pdb_mysql.c b/source/passdb/pdb_mysql.c
new file mode 100644
index 00000000000..deed27dbe49
--- /dev/null
+++ b/source/passdb/pdb_mysql.c
@@ -0,0 +1,497 @@
+/*
+ * MySQL password backend for samba
+ * Copyright (C) Jelmer Vernooij 2002-2004
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 675
+ * Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+#include <mysql/mysql.h>
+
+#define CONFIG_HOST_DEFAULT "localhost"
+#define CONFIG_USER_DEFAULT "samba"
+#define CONFIG_PASS_DEFAULT ""
+#define CONFIG_PORT_DEFAULT "3306"
+#define CONFIG_DB_DEFAULT "samba"
+
+static int mysqlsam_debug_level = DBGC_ALL;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS mysqlsam_debug_level
+
+typedef struct pdb_mysql_data {
+ MYSQL *handle;
+ MYSQL_RES *pwent;
+ const char *location;
+} pdb_mysql_data;
+
+#define SET_DATA(data,methods) { \
+ if(!methods){ \
+ DEBUG(0, ("invalid methods!\n")); \
+ return NT_STATUS_INVALID_PARAMETER; \
+ } \
+ data = (struct pdb_mysql_data *)methods->private_data; \
+ if(!data || !(data->handle)){ \
+ DEBUG(0, ("invalid handle!\n")); \
+ return NT_STATUS_INVALID_HANDLE; \
+ } \
+}
+
+#define config_value( data, name, default_value ) \
+ lp_parm_const_string( GLOBAL_SECTION_SNUM, (data)->location, name, default_value )
+
+static long xatol(const char *d)
+{
+ if(!d) return 0;
+ return atol(d);
+}
+
+static NTSTATUS row_to_sam_account(MYSQL_RES * r, SAM_ACCOUNT * u)
+{
+ MYSQL_ROW row;
+ pstring temp;
+ unsigned int num_fields;
+ DOM_SID sid;
+
+ num_fields = mysql_num_fields(r);
+ row = mysql_fetch_row(r);
+ if (!row)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ pdb_set_logon_time(u, xatol(row[0]), PDB_SET);
+ pdb_set_logoff_time(u, xatol(row[1]), PDB_SET);
+ pdb_set_kickoff_time(u, xatol(row[2]), PDB_SET);
+ pdb_set_pass_last_set_time(u, xatol(row[3]), PDB_SET);
+ pdb_set_pass_can_change_time(u, xatol(row[4]), PDB_SET);
+ pdb_set_pass_must_change_time(u, xatol(row[5]), PDB_SET);
+ pdb_set_username(u, row[6], PDB_SET);
+ pdb_set_domain(u, row[7], PDB_SET);
+ pdb_set_nt_username(u, row[8], PDB_SET);
+ pdb_set_fullname(u, row[9], PDB_SET);
+ pdb_set_homedir(u, row[10], PDB_SET);
+ pdb_set_dir_drive(u, row[11], PDB_SET);
+ pdb_set_logon_script(u, row[12], PDB_SET);
+ pdb_set_profile_path(u, row[13], PDB_SET);
+ pdb_set_acct_desc(u, row[14], PDB_SET);
+ pdb_set_workstations(u, row[15], PDB_SET);
+ pdb_set_unknown_str(u, row[16], PDB_SET);
+ pdb_set_munged_dial(u, row[17], PDB_SET);
+
+ if(!row[18] || !string_to_sid(&sid, row[18])) {
+ DEBUG(0,("No user SID retrieved from database!\n"));
+ } else {
+ pdb_set_user_sid(u, &sid, PDB_SET);
+ }
+
+ if(row[19]) {
+ string_to_sid(&sid, row[19]);
+ pdb_set_group_sid(u, &sid, PDB_SET);
+ }
+
+ if (pdb_gethexpwd(row[20], temp))
+ pdb_set_lanman_passwd(u, temp, PDB_SET);
+ if (pdb_gethexpwd(row[21], temp))
+ pdb_set_nt_passwd(u, temp, PDB_SET);
+
+ /* Only use plaintext password storage when lanman and nt are
+ * NOT used */
+ if (!row[20] || !row[21])
+ pdb_set_plaintext_passwd(u, row[22]);
+
+ pdb_set_acct_ctrl(u, xatol(row[23]), PDB_SET);
+ pdb_set_logon_divs(u, xatol(row[25]), PDB_SET);
+ pdb_set_hours_len(u, xatol(row[26]), PDB_SET);
+ pdb_set_bad_password_count(u, xatol(row[27]), PDB_SET);
+ pdb_set_logon_count(u, xatol(row[28]), PDB_SET);
+ pdb_set_unknown_6(u, xatol(row[29]), PDB_SET);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS mysqlsam_setsampwent(struct pdb_methods *methods, BOOL update)
+{
+ struct pdb_mysql_data *data =
+ (struct pdb_mysql_data *) methods->private_data;
+ char *query;
+ int ret;
+
+ if (!data || !(data->handle)) {
+ DEBUG(0, ("invalid handle!\n"));
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ query = sql_account_query_select(data->location, update, SQL_SEARCH_NONE, NULL);
+
+ ret = mysql_query(data->handle, query);
+ SAFE_FREE(query);
+
+ if (ret) {
+ DEBUG(0,
+ ("Error executing MySQL query %s\n", mysql_error(data->handle)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ data->pwent = mysql_store_result(data->handle);
+
+ if (data->pwent == NULL) {
+ DEBUG(0,
+ ("Error storing results: %s\n", mysql_error(data->handle)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(5,
+ ("mysqlsam_setsampwent succeeded(%llu results)!\n",
+ mysql_num_rows(data->pwent)));
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************
+ End enumeration of the passwd list.
+ ****************************************************************/
+
+static void mysqlsam_endsampwent(struct pdb_methods *methods)
+{
+ struct pdb_mysql_data *data =
+ (struct pdb_mysql_data *) methods->private_data;
+
+ if (data == NULL) {
+ DEBUG(0, ("invalid handle!\n"));
+ return;
+ }
+
+ if (data->pwent != NULL)
+ mysql_free_result(data->pwent);
+
+ data->pwent = NULL;
+
+ DEBUG(5, ("mysql_endsampwent called\n"));
+}
+
+/*****************************************************************
+ Get one SAM_ACCOUNT from the list (next in line)
+ *****************************************************************/
+
+static NTSTATUS mysqlsam_getsampwent(struct pdb_methods *methods, SAM_ACCOUNT * user)
+{
+ struct pdb_mysql_data *data;
+
+ SET_DATA(data, methods);
+
+ if (data->pwent == NULL) {
+ DEBUG(0, ("invalid pwent\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return row_to_sam_account(data->pwent, user);
+}
+
+static NTSTATUS mysqlsam_select_by_field(struct pdb_methods * methods, SAM_ACCOUNT * user,
+ enum sql_search_field field, const char *sname)
+{
+ char *esc_sname;
+ char *query;
+ NTSTATUS ret;
+ MYSQL_RES *res;
+ int mysql_ret;
+ struct pdb_mysql_data *data;
+ char *tmp_sname;
+
+ SET_DATA(data, methods);
+
+ esc_sname = malloc(strlen(sname) * 2 + 1);
+ if (!esc_sname) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ tmp_sname = smb_xstrdup(sname);
+
+ /* Escape sname */
+ mysql_real_escape_string(data->handle, esc_sname, tmp_sname,
+ strlen(tmp_sname));
+
+ SAFE_FREE(tmp_sname);
+
+ if (user == NULL) {
+ DEBUG(0, ("pdb_getsampwnam: SAM_ACCOUNT is NULL.\n"));
+ SAFE_FREE(esc_sname);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ query = sql_account_query_select(data->location, True, field, esc_sname);
+
+ SAFE_FREE(esc_sname);
+
+ DEBUG(5, ("Executing query %s\n", query));
+
+ mysql_ret = mysql_query(data->handle, query);
+
+ SAFE_FREE(query);
+
+ if (mysql_ret) {
+ DEBUG(0,
+ ("Error while executing MySQL query %s\n",
+ mysql_error(data->handle)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ res = mysql_store_result(data->handle);
+ if (res == NULL) {
+ DEBUG(0,
+ ("Error storing results: %s\n", mysql_error(data->handle)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ret = row_to_sam_account(res, user);
+ mysql_free_result(res);
+
+ return ret;
+}
+
+/******************************************************************
+ Lookup a name in the SAM database
+ ******************************************************************/
+
+static NTSTATUS mysqlsam_getsampwnam(struct pdb_methods *methods, SAM_ACCOUNT * user,
+ const char *sname)
+{
+ struct pdb_mysql_data *data;
+
+ SET_DATA(data, methods);
+
+ if (!sname) {
+ DEBUG(0, ("invalid name specified"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return mysqlsam_select_by_field(methods, user,
+ SQL_SEARCH_USER_NAME, sname);
+}
+
+
+/***************************************************************************
+ Search by sid
+ **************************************************************************/
+
+static NTSTATUS mysqlsam_getsampwsid(struct pdb_methods *methods, SAM_ACCOUNT * user,
+ const DOM_SID * sid)
+{
+ struct pdb_mysql_data *data;
+ fstring sid_str;
+
+ SET_DATA(data, methods);
+
+ sid_to_string(sid_str, sid);
+
+ return mysqlsam_select_by_field(methods, user, SQL_SEARCH_USER_SID, sid_str);
+}
+
+/***************************************************************************
+ Delete a SAM_ACCOUNT
+ ****************************************************************************/
+
+static NTSTATUS mysqlsam_delete_sam_account(struct pdb_methods *methods,
+ SAM_ACCOUNT * sam_pass)
+{
+ const char *sname = pdb_get_username(sam_pass);
+ char *esc;
+ char *query;
+ int ret;
+ struct pdb_mysql_data *data;
+ char *tmp_sname;
+
+ SET_DATA(data, methods);
+
+ if (!methods) {
+ DEBUG(0, ("invalid methods!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ data = (struct pdb_mysql_data *) methods->private_data;
+ if (!data || !(data->handle)) {
+ DEBUG(0, ("invalid handle!\n"));
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (!sname) {
+ DEBUG(0, ("invalid name specified\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* Escape sname */
+ esc = malloc(strlen(sname) * 2 + 1);
+ if (!esc) {
+ DEBUG(0, ("Can't allocate memory to store escaped name\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ tmp_sname = smb_xstrdup(sname);
+
+ mysql_real_escape_string(data->handle, esc, tmp_sname,
+ strlen(tmp_sname));
+
+ SAFE_FREE(tmp_sname);
+
+ query = sql_account_query_delete(data->location, esc);
+
+ SAFE_FREE(esc);
+
+ ret = mysql_query(data->handle, query);
+
+ SAFE_FREE(query);
+
+ if (ret) {
+ DEBUG(0,
+ ("Error while executing query: %s\n",
+ mysql_error(data->handle)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(5, ("User '%s' deleted\n", sname));
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS mysqlsam_replace_sam_account(struct pdb_methods *methods,
+ const SAM_ACCOUNT * newpwd, char isupdate)
+{
+ struct pdb_mysql_data *data;
+ char *query;
+
+ if (!methods) {
+ DEBUG(0, ("invalid methods!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ data = (struct pdb_mysql_data *) methods->private_data;
+
+ if (data == NULL || data->handle == NULL) {
+ DEBUG(0, ("invalid handle!\n"));
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ query = sql_account_query_update(data->location, newpwd, isupdate);
+
+ /* Execute the query */
+ if (mysql_query(data->handle, query)) {
+ DEBUG(0,
+ ("Error executing %s, %s\n", query,
+ mysql_error(data->handle)));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ SAFE_FREE(query);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS mysqlsam_add_sam_account(struct pdb_methods *methods, SAM_ACCOUNT * newpwd)
+{
+ return mysqlsam_replace_sam_account(methods, newpwd, 0);
+}
+
+static NTSTATUS mysqlsam_update_sam_account(struct pdb_methods *methods,
+ SAM_ACCOUNT * newpwd)
+{
+ return mysqlsam_replace_sam_account(methods, newpwd, 1);
+}
+
+static NTSTATUS mysqlsam_init(struct pdb_context * pdb_context, struct pdb_methods ** pdb_method,
+ const char *location)
+{
+ NTSTATUS nt_status;
+ struct pdb_mysql_data *data;
+
+ mysqlsam_debug_level = debug_add_class("mysqlsam");
+ if (mysqlsam_debug_level == -1) {
+ mysqlsam_debug_level = DBGC_ALL;
+ DEBUG(0,
+ ("mysqlsam: Couldn't register custom debugging class!\n"));
+ }
+
+
+ if (!pdb_context) {
+ DEBUG(0, ("invalid pdb_methods specified\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!NT_STATUS_IS_OK
+ (nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
+ return nt_status;
+ }
+
+ (*pdb_method)->name = "mysqlsam";
+
+ (*pdb_method)->setsampwent = mysqlsam_setsampwent;
+ (*pdb_method)->endsampwent = mysqlsam_endsampwent;
+ (*pdb_method)->getsampwent = mysqlsam_getsampwent;
+ (*pdb_method)->getsampwnam = mysqlsam_getsampwnam;
+ (*pdb_method)->getsampwsid = mysqlsam_getsampwsid;
+ (*pdb_method)->add_sam_account = mysqlsam_add_sam_account;
+ (*pdb_method)->update_sam_account = mysqlsam_update_sam_account;
+ (*pdb_method)->delete_sam_account = mysqlsam_delete_sam_account;
+
+ data = talloc(pdb_context->mem_ctx, sizeof(struct pdb_mysql_data));
+ (*pdb_method)->private_data = data;
+ data->handle = NULL;
+ data->pwent = NULL;
+
+ if (!location) {
+ DEBUG(0, ("No identifier specified. Check the Samba HOWTO Collection for details\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ data->location = smb_xstrdup(location);
+
+ DEBUG(1,
+ ("Connecting to database server, host: %s, user: %s, password: %s, database: %s, port: %ld\n",
+ config_value(data, "mysql host", CONFIG_HOST_DEFAULT),
+ config_value(data, "mysql user", CONFIG_USER_DEFAULT),
+ config_value(data, "mysql password", CONFIG_PASS_DEFAULT),
+ config_value(data, "mysql database", CONFIG_DB_DEFAULT),
+ xatol(config_value(data, "mysql port", CONFIG_PORT_DEFAULT))));
+
+ /* Do the mysql initialization */
+ data->handle = mysql_init(NULL);
+ if (!data->handle) {
+ DEBUG(0, ("Failed to connect to server\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if(!sql_account_config_valid(data->location)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* Process correct entry in $HOME/.my.conf */
+ if (!mysql_real_connect(data->handle,
+ config_value(data, "mysql host", CONFIG_HOST_DEFAULT),
+ config_value(data, "mysql user", CONFIG_USER_DEFAULT),
+ config_value(data, "mysql password", CONFIG_PASS_DEFAULT),
+ config_value(data, "mysql database", CONFIG_DB_DEFAULT),
+ xatol(config_value (data, "mysql port", CONFIG_PORT_DEFAULT)),
+ NULL, 0)) {
+ DEBUG(0,
+ ("Failed to connect to mysql database: error: %s\n",
+ mysql_error(data->handle)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(5, ("Connected to mysql db\n"));
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_mysql_init(void)
+{
+ return smb_register_passdb(PASSDB_INTERFACE_VERSION, "mysql", mysqlsam_init);
+}
diff --git a/source/passdb/pdb_pgsql.c b/source/passdb/pdb_pgsql.c
new file mode 100644
index 00000000000..1731c720a21
--- /dev/null
+++ b/source/passdb/pdb_pgsql.c
@@ -0,0 +1,488 @@
+/*
+ * PostgresSQL password backend for samba
+ * Copyright (C) Hamish Friedlander 2003
+ * Copyright (C) Jelmer Vernooij 2004
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 675
+ * Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+#include <libpq-fe.h>
+
+#define CONFIG_HOST_DEFAULT "localhost"
+#define CONFIG_USER_DEFAULT "samba"
+#define CONFIG_PASS_DEFAULT ""
+#define CONFIG_PORT_DEFAULT "5432"
+#define CONFIG_DB_DEFAULT "samba"
+
+/* handles for doing db transactions */
+typedef struct pdb_pgsql_data {
+ PGconn *handle ;
+ PGresult *pwent ;
+ long currow ;
+
+ const char *location ;
+} pdb_pgsql_data ;
+
+#define SET_DATA(data,methods) { \
+ if(!methods){ \
+ DEBUG(0, ("invalid methods!\n")); \
+ return NT_STATUS_INVALID_PARAMETER; \
+ } \
+ data = (struct pdb_pgsql_data *)methods->private_data; \
+ if(!data || !(data->handle)){ \
+ DEBUG(0, ("invalid handle!\n")); \
+ return NT_STATUS_INVALID_HANDLE; \
+ } \
+}
+
+#define SET_DATA_QUIET(data,methods) { \
+ if(!methods){ \
+ DEBUG(0, ("invalid methods!\n")); \
+ return ; \
+ } \
+ data = (struct pdb_pgsql_data *)methods->private_data; \
+ if(!data || !(data->handle)){ \
+ DEBUG(0, ("invalid handle!\n")); \
+ return ; \
+ } \
+}
+
+
+#define config_value( data, name, default_value ) \
+ lp_parm_const_string( GLOBAL_SECTION_SNUM, (data)->location, name, default_value )
+
+static long PQgetlong( PGresult *r, long row, long col )
+{
+ if ( PQgetisnull( r, row, col ) ) return 0 ;
+
+ return atol( PQgetvalue( r, row, col ) ) ;
+}
+
+static NTSTATUS row_to_sam_account ( PGresult *r, long row, SAM_ACCOUNT *u )
+{
+ pstring temp ;
+ DOM_SID sid ;
+
+ if ( row >= PQntuples( r ) ) return NT_STATUS_INVALID_PARAMETER ;
+
+ pdb_set_logon_time ( u, PQgetlong ( r, row, 0 ), PDB_SET ) ;
+ pdb_set_logoff_time ( u, PQgetlong ( r, row, 1 ), PDB_SET ) ;
+ pdb_set_kickoff_time ( u, PQgetlong ( r, row, 2 ), PDB_SET ) ;
+ pdb_set_pass_last_set_time ( u, PQgetlong ( r, row, 3 ), PDB_SET ) ;
+ pdb_set_pass_can_change_time ( u, PQgetlong ( r, row, 4 ), PDB_SET ) ;
+ pdb_set_pass_must_change_time( u, PQgetlong ( r, row, 5 ), PDB_SET ) ;
+ pdb_set_username ( u, PQgetvalue( r, row, 6 ), PDB_SET ) ;
+ pdb_set_domain ( u, PQgetvalue( r, row, 7 ), PDB_SET ) ;
+ pdb_set_nt_username ( u, PQgetvalue( r, row, 8 ), PDB_SET ) ;
+ pdb_set_fullname ( u, PQgetvalue( r, row, 9 ), PDB_SET ) ;
+ pdb_set_homedir ( u, PQgetvalue( r, row, 10 ), PDB_SET ) ;
+ pdb_set_dir_drive ( u, PQgetvalue( r, row, 11 ), PDB_SET ) ;
+ pdb_set_logon_script ( u, PQgetvalue( r, row, 12 ), PDB_SET ) ;
+ pdb_set_profile_path ( u, PQgetvalue( r, row, 13 ), PDB_SET ) ;
+ pdb_set_acct_desc ( u, PQgetvalue( r, row, 14 ), PDB_SET ) ;
+ pdb_set_workstations ( u, PQgetvalue( r, row, 15 ), PDB_SET ) ;
+ pdb_set_unknown_str ( u, PQgetvalue( r, row, 16 ), PDB_SET ) ;
+ pdb_set_munged_dial ( u, PQgetvalue( r, row, 17 ), PDB_SET ) ;
+
+ pdb_set_acct_ctrl ( u, PQgetlong ( r, row, 23 ), PDB_SET ) ;
+ pdb_set_logon_divs ( u, PQgetlong ( r, row, 25 ), PDB_SET ) ;
+ pdb_set_hours_len ( u, PQgetlong ( r, row, 26 ), PDB_SET ) ;
+ pdb_set_logon_count ( u, PQgetlong ( r, row, 27 ), PDB_SET ) ;
+ pdb_set_unknown_6 ( u, PQgetlong ( r, row, 28 ), PDB_SET ) ;
+
+ if ( !PQgetisnull( r, row, 18 ) ) string_to_sid( &sid, PQgetvalue( r, row, 18 ) ) ;
+ pdb_set_user_sid ( u, &sid, PDB_SET ) ;
+ if ( !PQgetisnull( r, row, 19 ) ) string_to_sid( &sid, PQgetvalue( r, row, 19 ) ) ;
+ pdb_set_group_sid( u, &sid, PDB_SET ) ;
+
+ if ( pdb_gethexpwd( PQgetvalue( r, row, 20 ), temp ), PDB_SET ) pdb_set_lanman_passwd( u, temp, PDB_SET ) ;
+ if ( pdb_gethexpwd( PQgetvalue( r, row, 21 ), temp ), PDB_SET ) pdb_set_nt_passwd ( u, temp, PDB_SET ) ;
+
+ /* Only use plaintext password storage when lanman and nt are NOT used */
+ if ( PQgetisnull( r, row, 20 ) || PQgetisnull( r, row, 21 ) ) pdb_set_plaintext_passwd( u, PQgetvalue( r, row, 22 ) ) ;
+
+ return NT_STATUS_OK ;
+}
+
+static NTSTATUS pgsqlsam_setsampwent(struct pdb_methods *methods, BOOL update)
+{
+ struct pdb_pgsql_data *data ;
+ char *query ;
+ NTSTATUS retval ;
+
+ SET_DATA( data, methods ) ;
+
+ query = sql_account_query_select(data->location, update, SQL_SEARCH_NONE, NULL);
+
+ /* Do it */
+ DEBUG( 5, ("Executing query %s\n", query) ) ;
+ data->pwent = PQexec( data->handle, query ) ;
+ data->currow = 0 ;
+
+ /* Result? */
+ if ( data->pwent == NULL )
+ {
+ DEBUG( 0, ("Error executing %s, %s\n", query, PQerrorMessage( data->handle ) ) ) ;
+ retval = NT_STATUS_UNSUCCESSFUL ;
+ }
+ else if ( PQresultStatus( data->pwent ) != PGRES_TUPLES_OK )
+ {
+ DEBUG( 0, ("Error executing %s, %s\n", query, PQresultErrorMessage( data->pwent ) ) ) ;
+ retval = NT_STATUS_UNSUCCESSFUL ;
+ }
+ else
+ {
+ DEBUG( 5, ("pgsqlsam_setsampwent succeeded(%llu results)!\n", PQntuples(data->pwent)) ) ;
+ retval = NT_STATUS_OK ;
+ }
+
+ SAFE_FREE(query);
+ return retval ;
+}
+
+/***************************************************************
+ End enumeration of the passwd list.
+ ****************************************************************/
+
+static void pgsqlsam_endsampwent(struct pdb_methods *methods)
+{
+ struct pdb_pgsql_data *data ;
+
+ SET_DATA_QUIET( data, methods ) ;
+
+ if (data->pwent != NULL)
+ {
+ PQclear( data->pwent ) ;
+ }
+
+ data->pwent = NULL ;
+ data->currow = 0 ;
+
+ DEBUG( 5, ("pgsql_endsampwent called\n") ) ;
+}
+
+/*****************************************************************
+ Get one SAM_ACCOUNT from the list (next in line)
+ *****************************************************************/
+
+static NTSTATUS pgsqlsam_getsampwent( struct pdb_methods *methods, SAM_ACCOUNT *user )
+{
+ struct pdb_pgsql_data *data;
+ NTSTATUS retval ;
+
+ SET_DATA( data, methods ) ;
+
+ if ( data->pwent == NULL )
+ {
+ DEBUG( 0, ("invalid pwent\n") ) ;
+ return NT_STATUS_INVALID_PARAMETER ;
+ }
+
+ retval = row_to_sam_account( data->pwent, data->currow, user ) ;
+ data->currow++ ;
+
+ return retval ;
+}
+
+static NTSTATUS pgsqlsam_select_by_field ( struct pdb_methods *methods, SAM_ACCOUNT *user, enum sql_search_field field, const char *sname )
+{
+ struct pdb_pgsql_data *data ;
+
+ char *esc ;
+ char *query ;
+
+ PGresult *result ;
+ NTSTATUS retval ;
+
+ SET_DATA(data, methods);
+
+ if ( user == NULL )
+ {
+ DEBUG( 0, ("pdb_getsampwnam: SAM_ACCOUNT is NULL.\n") ) ;
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG( 5, ("pgsqlsam_select_by_field: getting data where %d = %s(nonescaped)\n", field, sname) ) ;
+
+ /* Escape sname */
+ esc = malloc(strlen(sname) * 2 + 1);
+ if ( !esc )
+ {
+ DEBUG(0, ("Can't allocate memory to store escaped name\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ //tmp_sname = smb_xstrdup(sname);
+ PQescapeString( esc, sname, strlen(sname) ) ;
+
+ query = sql_account_query_select(data->location, True, field, esc);
+
+ /* Do it */
+ DEBUG( 5, ("Executing query %s\n", query) ) ;
+ result = PQexec( data->handle, query ) ;
+
+ /* Result? */
+ if ( result == NULL )
+ {
+ DEBUG( 0, ("Error executing %s, %s\n", query, PQerrorMessage( data->handle ) ) ) ;
+ retval = NT_STATUS_UNSUCCESSFUL ;
+ }
+ else if ( PQresultStatus( result ) != PGRES_TUPLES_OK )
+ {
+ DEBUG( 0, ("Error executing %s, %s\n", query, PQresultErrorMessage( result ) ) ) ;
+ retval = NT_STATUS_UNSUCCESSFUL ;
+ }
+ else
+ {
+ retval = row_to_sam_account( result, 0, user ) ;
+ }
+
+ SAFE_FREE( esc ) ;
+ SAFE_FREE( query ) ;
+
+ PQclear( result ) ;
+
+ return retval ;
+}
+
+/******************************************************************
+ Lookup a name in the SAM database
+ ******************************************************************/
+
+static NTSTATUS pgsqlsam_getsampwnam ( struct pdb_methods *methods, SAM_ACCOUNT *user, const char *sname )
+{
+ struct pdb_pgsql_data *data;
+
+ SET_DATA(data, methods);
+
+ if ( !sname )
+ {
+ DEBUG( 0, ("invalid name specified") ) ;
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return pgsqlsam_select_by_field( methods, user, SQL_SEARCH_USER_NAME, sname ) ;
+}
+
+
+/***************************************************************************
+ Search by sid
+ **************************************************************************/
+
+static NTSTATUS pgsqlsam_getsampwsid ( struct pdb_methods *methods, SAM_ACCOUNT *user, const DOM_SID *sid )
+{
+ struct pdb_pgsql_data *data;
+ fstring sid_str;
+
+ SET_DATA( data, methods ) ;
+
+ sid_to_string( sid_str, sid ) ;
+
+ return pgsqlsam_select_by_field( methods, user, SQL_SEARCH_USER_SID, sid_str ) ;
+}
+
+/***************************************************************************
+ Delete a SAM_ACCOUNT
+ ****************************************************************************/
+
+static NTSTATUS pgsqlsam_delete_sam_account( struct pdb_methods *methods, SAM_ACCOUNT *sam_pass )
+{
+ struct pdb_pgsql_data *data ;
+
+ const char *sname = pdb_get_username( sam_pass ) ;
+ char *esc ;
+ char *query ;
+
+ PGresult *result ;
+ NTSTATUS retval ;
+
+ SET_DATA(data, methods);
+
+ if ( !sname )
+ {
+ DEBUG( 0, ("invalid name specified\n") ) ;
+ return NT_STATUS_INVALID_PARAMETER ;
+ }
+
+ /* Escape sname */
+ esc = malloc(strlen(sname) * 2 + 1);
+ if ( !esc )
+ {
+ DEBUG(0, ("Can't allocate memory to store escaped name\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ PQescapeString( esc, sname, strlen(sname) ) ;
+
+ query = sql_account_query_delete(data->location, esc);
+
+ /* Do it */
+ result = PQexec( data->handle, query ) ;
+
+ if ( result == NULL )
+ {
+ DEBUG( 0, ("Error executing %s, %s\n", query, PQerrorMessage( data->handle ) ) ) ;
+ retval = NT_STATUS_UNSUCCESSFUL ;
+ }
+ else if ( PQresultStatus( result ) != PGRES_COMMAND_OK )
+ {
+ DEBUG( 0, ("Error executing %s, %s\n", query, PQresultErrorMessage( result ) ) ) ;
+ retval = NT_STATUS_UNSUCCESSFUL ;
+ }
+ else
+ {
+ DEBUG( 5, ("User '%s' deleted\n", sname) ) ;
+ retval = NT_STATUS_OK ;
+ }
+
+ SAFE_FREE( esc ) ;
+ SAFE_FREE( query ) ;
+
+ return retval ;
+}
+
+static NTSTATUS pgsqlsam_replace_sam_account( struct pdb_methods *methods, const SAM_ACCOUNT *newpwd, char isupdate )
+{
+ struct pdb_pgsql_data *data ;
+ char *query;
+ PGresult *result ;
+
+ if ( !methods )
+ {
+ DEBUG( 0, ("invalid methods!\n") ) ;
+ return NT_STATUS_INVALID_PARAMETER ;
+ }
+
+ data = (struct pdb_pgsql_data *) methods->private_data ;
+
+ if ( data == NULL || data->handle == NULL )
+ {
+ DEBUG( 0, ("invalid handle!\n") ) ;
+ return NT_STATUS_INVALID_HANDLE ;
+ }
+
+ query = sql_account_query_update(data->location, newpwd, isupdate);
+
+ result = PQexec( data->handle, query ) ;
+
+
+ /* Execute the query */
+ if ( result == NULL )
+ {
+ DEBUG( 0, ("Error executing %s, %s\n", query, PQerrorMessage( data->handle ) ) ) ;
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ else if ( PQresultStatus( result ) != PGRES_COMMAND_OK )
+ {
+ DEBUG( 0, ("Error executing %s, %s\n", query, PQresultErrorMessage( result ) ) ) ;
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ SAFE_FREE(query);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS pgsqlsam_add_sam_account ( struct pdb_methods *methods, SAM_ACCOUNT *newpwd )
+{
+ return pgsqlsam_replace_sam_account( methods, newpwd, 0 ) ;
+}
+
+static NTSTATUS pgsqlsam_update_sam_account ( struct pdb_methods *methods, SAM_ACCOUNT *newpwd )
+{
+ return pgsqlsam_replace_sam_account( methods, newpwd, 1 ) ;
+}
+
+static NTSTATUS pgsqlsam_init ( struct pdb_context *pdb_context, struct pdb_methods **pdb_method, const char *location )
+{
+ NTSTATUS nt_status ;
+ struct pdb_pgsql_data *data ;
+
+ if ( !pdb_context )
+ {
+ DEBUG( 0, ("invalid pdb_methods specified\n") ) ;
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!NT_STATUS_IS_OK
+ (nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
+ return nt_status;
+ }
+
+ (*pdb_method)->name = "pgsqlsam" ;
+
+ (*pdb_method)->setsampwent = pgsqlsam_setsampwent ;
+ (*pdb_method)->endsampwent = pgsqlsam_endsampwent ;
+ (*pdb_method)->getsampwent = pgsqlsam_getsampwent ;
+ (*pdb_method)->getsampwnam = pgsqlsam_getsampwnam ;
+ (*pdb_method)->getsampwsid = pgsqlsam_getsampwsid ;
+ (*pdb_method)->add_sam_account = pgsqlsam_add_sam_account ;
+ (*pdb_method)->update_sam_account = pgsqlsam_update_sam_account ;
+ (*pdb_method)->delete_sam_account = pgsqlsam_delete_sam_account ;
+
+ data = talloc( pdb_context->mem_ctx, sizeof( struct pdb_pgsql_data ) ) ;
+ (*pdb_method)->private_data = data ;
+ data->handle = NULL ;
+ data->pwent = NULL ;
+
+ if ( !location )
+ {
+ DEBUG( 0, ("No identifier specified. Check the Samba HOWTO Collection for details\n") ) ;
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ data->location = smb_xstrdup( location ) ;
+
+ if(!sql_account_config_valid(data->location)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG
+ (
+ 1,
+ (
+ "Connecting to database server, host: %s, user: %s, password: XXXXXX, database: %s, port: %s\n",
+ config_value( data, "pgsql host" , CONFIG_HOST_DEFAULT ),
+ config_value( data, "pgsql user" , CONFIG_USER_DEFAULT ),
+ config_value( data, "pgsql database", CONFIG_DB_DEFAULT ),
+ config_value( data, "pgsql port" , CONFIG_PORT_DEFAULT )
+ )
+ ) ;
+
+ /* Do the pgsql initialization */
+ data->handle = PQsetdbLogin(
+ config_value( data, "pgsql host" , CONFIG_HOST_DEFAULT ),
+ config_value( data, "pgsql port" , CONFIG_PORT_DEFAULT ),
+ NULL,
+ NULL,
+ config_value( data, "pgsql database", CONFIG_DB_DEFAULT ),
+ config_value( data, "pgsql user" , CONFIG_USER_DEFAULT ),
+ config_value( data, "pgsql password", CONFIG_PASS_DEFAULT )
+ ) ;
+
+ if ( PQstatus( data->handle ) != CONNECTION_OK )
+ {
+ DEBUG( 0, ("Failed to connect to pgsql database: error: %s\n", PQerrorMessage( data->handle )) ) ;
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG( 5, ("Connected to pgsql database\n") ) ;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_pgsql_init(void)
+{
+ return smb_register_passdb( PASSDB_INTERFACE_VERSION, "pgsql", pgsqlsam_init ) ;
+}
diff --git a/source/passdb/pdb_plugin.c b/source/passdb/pdb_plugin.c
new file mode 100644
index 00000000000..027cd0b5d33
--- /dev/null
+++ b/source/passdb/pdb_plugin.c
@@ -0,0 +1,80 @@
+/*
+ Unix SMB/CIFS implementation.
+ Loadable passdb module interface.
+ Copyright (C) Jelmer Vernooij 2002
+ Copyright (C) Andrew Bartlett 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+NTSTATUS pdb_init_plugin(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
+{
+ void * dl_handle;
+ char *plugin_location, *plugin_name, *p;
+ pdb_init_function plugin_init;
+ int (*plugin_version)(void);
+
+ if (location == NULL) {
+ DEBUG(0, ("The plugin module needs an argument!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ plugin_name = smb_xstrdup(location);
+ p = strchr(plugin_name, ':');
+ if (p) {
+ *p = 0;
+ plugin_location = p+1;
+ trim_char(plugin_location, ' ', ' ');
+ } else {
+ plugin_location = NULL;
+ }
+ trim_char(plugin_name, ' ', ' ');
+
+ DEBUG(5, ("Trying to load sam plugin %s\n", plugin_name));
+ dl_handle = sys_dlopen(plugin_name, RTLD_NOW );
+ if (!dl_handle) {
+ DEBUG(0, ("Failed to load sam plugin %s using sys_dlopen (%s)\n", plugin_name, sys_dlerror()));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ plugin_version = sys_dlsym(dl_handle, "pdb_version");
+ if (!plugin_version) {
+ sys_dlclose(dl_handle);
+ DEBUG(0, ("Failed to find function 'pdb_version' using sys_dlsym in sam plugin %s (%s)\n", plugin_name, sys_dlerror()));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (plugin_version() != PASSDB_INTERFACE_VERSION) {
+ sys_dlclose(dl_handle);
+ DEBUG(0, ("Wrong PASSDB_INTERFACE_VERSION! sam plugin has version %d and version %d is needed! Please update!\n",
+ plugin_version(),PASSDB_INTERFACE_VERSION));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ plugin_init = sys_dlsym(dl_handle, "pdb_init");
+ if (!plugin_init) {
+ sys_dlclose(dl_handle);
+ DEBUG(0, ("Failed to find function 'pdb_init' using sys_dlsym in sam plugin %s (%s)\n", plugin_name, sys_dlerror()));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(5, ("Starting sam plugin %s with location %s\n", plugin_name, plugin_location));
+ return plugin_init(pdb_context, pdb_method, plugin_location);
+}
diff --git a/source/passdb/pdb_smbpasswd.c b/source/passdb/pdb_smbpasswd.c
new file mode 100644
index 00000000000..562d50f89e3
--- /dev/null
+++ b/source/passdb/pdb_smbpasswd.c
@@ -0,0 +1,1517 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * SMB parameters and setup
+ * Copyright (C) Andrew Tridgell 1992-1998
+ * Modified by Jeremy Allison 1995.
+ * Modified by Gerald (Jerry) Carter 2000-2001,2003
+ * Modified by Andrew Bartlett 2002.
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 675
+ * Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+/*
+ smb_passwd is analogous to sam_passwd used everywhere
+ else. However, smb_passwd is limited to the information
+ stored by an smbpasswd entry
+ */
+
+struct smb_passwd
+{
+ uint32 smb_userid; /* this is actually the unix uid_t */
+ const char *smb_name; /* username string */
+
+ const unsigned char *smb_passwd; /* Null if no password */
+ const unsigned char *smb_nt_passwd; /* Null if no password */
+
+ uint16 acct_ctrl; /* account info (ACB_xxxx bit-mask) */
+ time_t pass_last_set_time; /* password last set time */
+};
+
+struct smbpasswd_privates
+{
+ /* used for maintain locks on the smbpasswd file */
+ int pw_file_lock_depth;
+
+ /* Global File pointer */
+ FILE *pw_file;
+
+ /* formerly static variables */
+ struct smb_passwd pw_buf;
+ pstring user_name;
+ unsigned char smbpwd[16];
+ unsigned char smbntpwd[16];
+
+ /* retrive-once info */
+ const char *smbpasswd_file;
+};
+
+enum pwf_access_type { PWF_READ, PWF_UPDATE, PWF_CREATE };
+
+/***************************************************************
+ Lock an fd. Abandon after waitsecs seconds.
+****************************************************************/
+
+static BOOL pw_file_lock(int fd, int type, int secs, int *plock_depth)
+{
+ if (fd < 0)
+ return False;
+
+ if(*plock_depth == 0) {
+ if (!do_file_lock(fd, secs, type)) {
+ DEBUG(10,("pw_file_lock: locking file failed, error = %s.\n",
+ strerror(errno)));
+ return False;
+ }
+ }
+
+ (*plock_depth)++;
+
+ return True;
+}
+
+/***************************************************************
+ Unlock an fd. Abandon after waitsecs seconds.
+****************************************************************/
+
+static BOOL pw_file_unlock(int fd, int *plock_depth)
+{
+ BOOL ret=True;
+
+ if (fd == 0 || *plock_depth == 0) {
+ return True;
+ }
+
+ if(*plock_depth == 1)
+ ret = do_file_lock(fd, 5, F_UNLCK);
+
+ if (*plock_depth > 0)
+ (*plock_depth)--;
+
+ if(!ret)
+ DEBUG(10,("pw_file_unlock: unlocking file failed, error = %s.\n",
+ strerror(errno)));
+ return ret;
+}
+
+
+/**************************************************************
+ Intialize a smb_passwd struct
+ *************************************************************/
+
+static void pdb_init_smb(struct smb_passwd *user)
+{
+ if (user == NULL)
+ return;
+ ZERO_STRUCTP (user);
+
+ user->pass_last_set_time = (time_t)0;
+}
+
+/***************************************************************
+ Internal fn to enumerate the smbpasswd list. Returns a void pointer
+ to ensure no modification outside this module. Checks for atomic
+ rename of smbpasswd file on update or create once the lock has
+ been granted to prevent race conditions. JRA.
+****************************************************************/
+
+static FILE *startsmbfilepwent(const char *pfile, enum pwf_access_type type, int *lock_depth)
+{
+ FILE *fp = NULL;
+ const char *open_mode = NULL;
+ int race_loop = 0;
+ int lock_type = F_RDLCK;
+
+ if (!*pfile) {
+ DEBUG(0, ("startsmbfilepwent: No SMB password file set\n"));
+ return (NULL);
+ }
+
+ switch(type) {
+ case PWF_READ:
+ open_mode = "rb";
+ lock_type = F_RDLCK;
+ break;
+ case PWF_UPDATE:
+ open_mode = "r+b";
+ lock_type = F_WRLCK;
+ break;
+ case PWF_CREATE:
+ /*
+ * Ensure atomic file creation.
+ */
+ {
+ int i, fd = -1;
+
+ for(i = 0; i < 5; i++) {
+ if((fd = sys_open(pfile, O_CREAT|O_TRUNC|O_EXCL|O_RDWR, 0600))!=-1)
+ break;
+ sys_usleep(200); /* Spin, spin... */
+ }
+ if(fd == -1) {
+ DEBUG(0,("startsmbfilepwent_internal: too many race conditions creating file %s\n", pfile));
+ return NULL;
+ }
+ close(fd);
+ open_mode = "r+b";
+ lock_type = F_WRLCK;
+ break;
+ }
+ }
+
+ for(race_loop = 0; race_loop < 5; race_loop++) {
+ DEBUG(10, ("startsmbfilepwent_internal: opening file %s\n", pfile));
+
+ if((fp = sys_fopen(pfile, open_mode)) == NULL) {
+
+ /*
+ * If smbpasswd file doesn't exist, then create new one. This helps to avoid
+ * confusing error msg when adding user account first time.
+ */
+ if (errno == ENOENT) {
+ if ((fp = sys_fopen(pfile, "a+")) != NULL) {
+ DEBUG(0, ("startsmbfilepwent_internal: file %s did not exist. File successfully created.\n", pfile));
+
+ } else {
+ DEBUG(0, ("startsmbfilepwent_internal: file %s did not exist. Couldn't create new one. Error was: %s",
+ pfile, strerror(errno)));
+ return NULL;
+ }
+
+ } else {
+ DEBUG(0, ("startsmbfilepwent_internal: unable to open file %s. Error was: %s\n", pfile, strerror(errno)));
+ return NULL;
+ }
+ }
+
+ if (!pw_file_lock(fileno(fp), lock_type, 5, lock_depth)) {
+ DEBUG(0, ("startsmbfilepwent_internal: unable to lock file %s. Error was %s\n", pfile, strerror(errno) ));
+ fclose(fp);
+ return NULL;
+ }
+
+ /*
+ * Only check for replacement races on update or create.
+ * For read we don't mind if the data is one record out of date.
+ */
+
+ if(type == PWF_READ) {
+ break;
+ } else {
+ SMB_STRUCT_STAT sbuf1, sbuf2;
+
+ /*
+ * Avoid the potential race condition between the open and the lock
+ * by doing a stat on the filename and an fstat on the fd. If the
+ * two inodes differ then someone did a rename between the open and
+ * the lock. Back off and try the open again. Only do this 5 times to
+ * prevent infinate loops. JRA.
+ */
+
+ if (sys_stat(pfile,&sbuf1) != 0) {
+ DEBUG(0, ("startsmbfilepwent_internal: unable to stat file %s. Error was %s\n", pfile, strerror(errno)));
+ pw_file_unlock(fileno(fp), lock_depth);
+ fclose(fp);
+ return NULL;
+ }
+
+ if (sys_fstat(fileno(fp),&sbuf2) != 0) {
+ DEBUG(0, ("startsmbfilepwent_internal: unable to fstat file %s. Error was %s\n", pfile, strerror(errno)));
+ pw_file_unlock(fileno(fp), lock_depth);
+ fclose(fp);
+ return NULL;
+ }
+
+ if( sbuf1.st_ino == sbuf2.st_ino) {
+ /* No race. */
+ break;
+ }
+
+ /*
+ * Race occurred - back off and try again...
+ */
+
+ pw_file_unlock(fileno(fp), lock_depth);
+ fclose(fp);
+ }
+ }
+
+ if(race_loop == 5) {
+ DEBUG(0, ("startsmbfilepwent_internal: too many race conditions opening file %s\n", pfile));
+ return NULL;
+ }
+
+ /* Set a buffer to do more efficient reads */
+ setvbuf(fp, (char *)NULL, _IOFBF, 1024);
+
+ /* Make sure it is only rw by the owner */
+#ifdef HAVE_FCHMOD
+ if(fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1) {
+#else
+ if(chmod(pfile, S_IRUSR|S_IWUSR) == -1) {
+#endif
+ DEBUG(0, ("startsmbfilepwent_internal: failed to set 0600 permissions on password file %s. \
+Error was %s\n.", pfile, strerror(errno) ));
+ pw_file_unlock(fileno(fp), lock_depth);
+ fclose(fp);
+ return NULL;
+ }
+
+ /* We have a lock on the file. */
+ return fp;
+}
+
+/***************************************************************
+ End enumeration of the smbpasswd list.
+****************************************************************/
+static void endsmbfilepwent(FILE *fp, int *lock_depth)
+{
+ if (!fp) {
+ return;
+ }
+
+ pw_file_unlock(fileno(fp), lock_depth);
+ fclose(fp);
+ DEBUG(7, ("endsmbfilepwent_internal: closed password file.\n"));
+}
+
+/*************************************************************************
+ Routine to return the next entry in the smbpasswd list.
+ *************************************************************************/
+
+static struct smb_passwd *getsmbfilepwent(struct smbpasswd_privates *smbpasswd_state, FILE *fp)
+{
+ /* Static buffers we will return. */
+ struct smb_passwd *pw_buf = &smbpasswd_state->pw_buf;
+ char *user_name = smbpasswd_state->user_name;
+ unsigned char *smbpwd = smbpasswd_state->smbpwd;
+ unsigned char *smbntpwd = smbpasswd_state->smbntpwd;
+ char linebuf[256];
+ unsigned char c;
+ unsigned char *p;
+ long uidval;
+ size_t linebuf_len;
+
+ if(fp == NULL) {
+ DEBUG(0,("getsmbfilepwent: Bad password file pointer.\n"));
+ return NULL;
+ }
+
+ pdb_init_smb(pw_buf);
+
+ pw_buf->acct_ctrl = ACB_NORMAL;
+
+ /*
+ * Scan the file, a line at a time and check if the name matches.
+ */
+ while (!feof(fp)) {
+ linebuf[0] = '\0';
+
+ fgets(linebuf, 256, fp);
+ if (ferror(fp)) {
+ return NULL;
+ }
+
+ /*
+ * Check if the string is terminated with a newline - if not
+ * then we must keep reading and discard until we get one.
+ */
+ if ((linebuf_len = strlen(linebuf)) == 0)
+ continue;
+
+ if (linebuf[linebuf_len - 1] != '\n') {
+ c = '\0';
+ while (!ferror(fp) && !feof(fp)) {
+ c = fgetc(fp);
+ if (c == '\n')
+ break;
+ }
+ } else
+ linebuf[linebuf_len - 1] = '\0';
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("getsmbfilepwent: got line |%s|\n", linebuf));
+#endif
+ if ((linebuf[0] == 0) && feof(fp)) {
+ DEBUG(4, ("getsmbfilepwent: end of file reached\n"));
+ break;
+ }
+ /*
+ * The line we have should be of the form :-
+ *
+ * username:uid:32hex bytes:[Account type]:LCT-12345678....other flags presently
+ * ignored....
+ *
+ * or,
+ *
+ * username:uid:32hex bytes:32hex bytes:[Account type]:LCT-12345678....ignored....
+ *
+ * if Windows NT compatible passwords are also present.
+ * [Account type] is an ascii encoding of the type of account.
+ * LCT-(8 hex digits) is the time_t value of the last change time.
+ */
+
+ if (linebuf[0] == '#' || linebuf[0] == '\0') {
+ DEBUG(6, ("getsmbfilepwent: skipping comment or blank line\n"));
+ continue;
+ }
+ p = (unsigned char *) strchr_m(linebuf, ':');
+ if (p == NULL) {
+ DEBUG(0, ("getsmbfilepwent: malformed password entry (no :)\n"));
+ continue;
+ }
+ /*
+ * As 256 is shorter than a pstring we don't need to check
+ * length here - if this ever changes....
+ */
+ SMB_ASSERT(sizeof(pstring) > sizeof(linebuf));
+
+ strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
+ user_name[PTR_DIFF(p, linebuf)] = '\0';
+
+ /* Get smb uid. */
+
+ p++; /* Go past ':' */
+
+ if(*p == '-') {
+ DEBUG(0, ("getsmbfilepwent: uids in the smbpasswd file must not be negative.\n"));
+ continue;
+ }
+
+ if (!isdigit(*p)) {
+ DEBUG(0, ("getsmbfilepwent: malformed password entry (uid not number)\n"));
+ continue;
+ }
+
+ uidval = atoi((char *) p);
+
+ while (*p && isdigit(*p))
+ p++;
+
+ if (*p != ':') {
+ DEBUG(0, ("getsmbfilepwent: malformed password entry (no : after uid)\n"));
+ continue;
+ }
+
+ pw_buf->smb_name = user_name;
+ pw_buf->smb_userid = uidval;
+
+ /*
+ * Now get the password value - this should be 32 hex digits
+ * which are the ascii representations of a 16 byte string.
+ * Get two at a time and put them into the password.
+ */
+
+ /* Skip the ':' */
+ p++;
+
+ if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
+ DEBUG(0, ("getsmbfilepwent: malformed password entry (passwd too short)\n"));
+ continue;
+ }
+
+ if (p[32] != ':') {
+ DEBUG(0, ("getsmbfilepwent: malformed password entry (no terminating :)\n"));
+ continue;
+ }
+
+ if (strnequal((char *) p, "NO PASSWORD", 11)) {
+ pw_buf->smb_passwd = NULL;
+ pw_buf->acct_ctrl |= ACB_PWNOTREQ;
+ } else {
+ if (*p == '*' || *p == 'X') {
+ /* NULL LM password */
+ pw_buf->smb_passwd = NULL;
+ DEBUG(10, ("getsmbfilepwent: LM password for user %s invalidated\n", user_name));
+ } else if (pdb_gethexpwd((char *)p, smbpwd)) {
+ pw_buf->smb_passwd = smbpwd;
+ } else {
+ pw_buf->smb_passwd = NULL;
+ DEBUG(0, ("getsmbfilepwent: Malformed Lanman password entry (non hex chars)\n"));
+ }
+ }
+
+ /*
+ * Now check if the NT compatible password is
+ * available.
+ */
+ pw_buf->smb_nt_passwd = NULL;
+
+ p += 33; /* Move to the first character of the line after
+ the lanman password. */
+ if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
+ if (*p != '*' && *p != 'X') {
+ if(pdb_gethexpwd((char *)p,smbntpwd))
+ pw_buf->smb_nt_passwd = smbntpwd;
+ }
+ p += 33; /* Move to the first character of the line after
+ the NT password. */
+ }
+
+ DEBUG(5,("getsmbfilepwent: returning passwd entry for user %s, uid %ld\n",
+ user_name, uidval));
+
+ if (*p == '[')
+ {
+ unsigned char *end_p = (unsigned char *)strchr_m((char *)p, ']');
+ pw_buf->acct_ctrl = pdb_decode_acct_ctrl((char*)p);
+
+ /* Must have some account type set. */
+ if(pw_buf->acct_ctrl == 0)
+ pw_buf->acct_ctrl = ACB_NORMAL;
+
+ /* Now try and get the last change time. */
+ if(end_p)
+ p = end_p + 1;
+ if(*p == ':') {
+ p++;
+ if(*p && (StrnCaseCmp((char *)p, "LCT-", 4)==0)) {
+ int i;
+ p += 4;
+ for(i = 0; i < 8; i++) {
+ if(p[i] == '\0' || !isxdigit(p[i]))
+ break;
+ }
+ if(i == 8) {
+ /*
+ * p points at 8 characters of hex digits -
+ * read into a time_t as the seconds since
+ * 1970 that the password was last changed.
+ */
+ pw_buf->pass_last_set_time = (time_t)strtol((char *)p, NULL, 16);
+ }
+ }
+ }
+ } else {
+ /* 'Old' style file. Fake up based on user name. */
+ /*
+ * Currently trust accounts are kept in the same
+ * password file as 'normal accounts'. If this changes
+ * we will have to fix this code. JRA.
+ */
+ if(pw_buf->smb_name[strlen(pw_buf->smb_name) - 1] == '$') {
+ pw_buf->acct_ctrl &= ~ACB_NORMAL;
+ pw_buf->acct_ctrl |= ACB_WSTRUST;
+ }
+ }
+
+ return pw_buf;
+ }
+
+ DEBUG(5,("getsmbfilepwent: end of file reached.\n"));
+ return NULL;
+}
+
+/************************************************************************
+ Create a new smbpasswd entry - malloced space returned.
+*************************************************************************/
+
+static char *format_new_smbpasswd_entry(const struct smb_passwd *newpwd)
+{
+ int new_entry_length;
+ char *new_entry;
+ char *p;
+
+ new_entry_length = strlen(newpwd->smb_name) + 1 + 15 + 1 + 32 + 1 + 32 + 1 + NEW_PW_FORMAT_SPACE_PADDED_LEN + 1 + 13 + 2;
+
+ if((new_entry = (char *)malloc( new_entry_length )) == NULL) {
+ DEBUG(0, ("format_new_smbpasswd_entry: Malloc failed adding entry for user %s.\n", newpwd->smb_name ));
+ return NULL;
+ }
+
+ slprintf(new_entry, new_entry_length - 1, "%s:%u:", newpwd->smb_name, (unsigned)newpwd->smb_userid);
+
+ p = new_entry+strlen(new_entry);
+
+ pdb_sethexpwd(p, newpwd->smb_passwd, newpwd->acct_ctrl);
+
+ p+=strlen(p); *p = ':'; p++;
+
+ pdb_sethexpwd(p, newpwd->smb_nt_passwd, newpwd->acct_ctrl);
+
+ p+=strlen(p); *p = ':'; p++;
+
+ /* Add the account encoding and the last change time. */
+ slprintf((char *)p, new_entry_length - 1 - (p - new_entry), "%s:LCT-%08X:\n",
+ pdb_encode_acct_ctrl(newpwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN),
+ (uint32)newpwd->pass_last_set_time);
+
+ return new_entry;
+}
+
+/************************************************************************
+ Routine to add an entry to the smbpasswd file.
+*************************************************************************/
+
+static BOOL add_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, struct smb_passwd *newpwd)
+{
+ const char *pfile = smbpasswd_state->smbpasswd_file;
+ struct smb_passwd *pwd = NULL;
+ FILE *fp = NULL;
+ int wr_len;
+ int fd;
+ size_t new_entry_length;
+ char *new_entry;
+ SMB_OFF_T offpos;
+ uint32 max_found_uid = 0;
+
+ /* Open the smbpassword file - for update. */
+ fp = startsmbfilepwent(pfile, PWF_UPDATE, &(smbpasswd_state->pw_file_lock_depth));
+
+ if (fp == NULL && errno == ENOENT) {
+ /* Try again - create. */
+ fp = startsmbfilepwent(pfile, PWF_CREATE, &(smbpasswd_state->pw_file_lock_depth));
+ }
+
+ if (fp == NULL) {
+ DEBUG(0, ("add_smbfilepwd_entry: unable to open file.\n"));
+ return False;
+ }
+
+ /*
+ * Scan the file, a line at a time and check if the name matches.
+ */
+
+ while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL)
+ {
+ if (strequal(newpwd->smb_name, pwd->smb_name))
+ {
+ DEBUG(0, ("add_smbfilepwd_entry: entry with name %s already exists\n", pwd->smb_name));
+ endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+ return False;
+ }
+
+ /* Look for a free uid for use in non-unix accounts */
+ if (pwd->smb_userid > max_found_uid) {
+ max_found_uid = pwd->smb_userid;
+ }
+ }
+
+ /* Ok - entry doesn't exist. We can add it */
+
+ /* Create a new smb passwd entry and set it to the given password. */
+ /*
+ * The add user write needs to be atomic - so get the fd from
+ * the fp and do a raw write() call.
+ */
+ fd = fileno(fp);
+
+ if((offpos = sys_lseek(fd, 0, SEEK_END)) == -1)
+ {
+ DEBUG(0, ("add_smbfilepwd_entry(sys_lseek): Failed to add entry for user %s to file %s. \
+Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
+ endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+ return False;
+ }
+
+ if((new_entry = format_new_smbpasswd_entry(newpwd)) == NULL)
+ {
+ DEBUG(0, ("add_smbfilepwd_entry(malloc): Failed to add entry for user %s to file %s. \
+Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
+ endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+ return False;
+ }
+
+ new_entry_length = strlen(new_entry);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("add_smbfilepwd_entry(%d): new_entry_len %d made line |%s|",
+ fd, new_entry_length, new_entry));
+#endif
+
+ if ((wr_len = write(fd, new_entry, new_entry_length)) != new_entry_length)
+ {
+ DEBUG(0, ("add_smbfilepwd_entry(write): %d Failed to add entry for user %s to file %s. \
+Error was %s\n", wr_len, newpwd->smb_name, pfile, strerror(errno)));
+
+ /* Remove the entry we just wrote. */
+ if(sys_ftruncate(fd, offpos) == -1)
+ {
+ DEBUG(0, ("add_smbfilepwd_entry: ERROR failed to ftruncate file %s. \
+Error was %s. Password file may be corrupt ! Please examine by hand !\n",
+ newpwd->smb_name, strerror(errno)));
+ }
+
+ endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+ free(new_entry);
+ return False;
+ }
+
+ free(new_entry);
+ endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+ return True;
+}
+
+/************************************************************************
+ Routine to search the smbpasswd file for an entry matching the username.
+ and then modify its password entry. We can't use the startsmbpwent()/
+ getsmbpwent()/endsmbpwent() interfaces here as we depend on looking
+ in the actual file to decide how much room we have to write data.
+ override = False, normal
+ override = True, override XXXXXXXX'd out password or NO PASS
+************************************************************************/
+
+static BOOL mod_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, const struct smb_passwd* pwd)
+{
+ /* Static buffers we will return. */
+ pstring user_name;
+
+ char linebuf[256];
+ char readbuf[1024];
+ unsigned char c;
+ fstring ascii_p16;
+ fstring encode_bits;
+ unsigned char *p = NULL;
+ size_t linebuf_len = 0;
+ FILE *fp;
+ int lockfd;
+ const char *pfile = smbpasswd_state->smbpasswd_file;
+ BOOL found_entry = False;
+ BOOL got_pass_last_set_time = False;
+
+ SMB_OFF_T pwd_seekpos = 0;
+
+ int i;
+ int wr_len;
+ int fd;
+
+ if (!*pfile) {
+ DEBUG(0, ("No SMB password file set\n"));
+ return False;
+ }
+ DEBUG(10, ("mod_smbfilepwd_entry: opening file %s\n", pfile));
+
+ fp = sys_fopen(pfile, "r+");
+
+ if (fp == NULL) {
+ DEBUG(0, ("mod_smbfilepwd_entry: unable to open file %s\n", pfile));
+ return False;
+ }
+ /* Set a buffer to do more efficient reads */
+ setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
+
+ lockfd = fileno(fp);
+
+ if (!pw_file_lock(lockfd, F_WRLCK, 5, &(smbpasswd_state->pw_file_lock_depth))) {
+ DEBUG(0, ("mod_smbfilepwd_entry: unable to lock file %s\n", pfile));
+ fclose(fp);
+ return False;
+ }
+
+ /* Make sure it is only rw by the owner */
+ chmod(pfile, 0600);
+
+ /* We have a write lock on the file. */
+ /*
+ * Scan the file, a line at a time and check if the name matches.
+ */
+ while (!feof(fp)) {
+ pwd_seekpos = sys_ftell(fp);
+
+ linebuf[0] = '\0';
+
+ fgets(linebuf, sizeof(linebuf), fp);
+ if (ferror(fp)) {
+ pw_file_unlock(lockfd, &(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return False;
+ }
+
+ /*
+ * Check if the string is terminated with a newline - if not
+ * then we must keep reading and discard until we get one.
+ */
+ linebuf_len = strlen(linebuf);
+ if (linebuf[linebuf_len - 1] != '\n') {
+ c = '\0';
+ while (!ferror(fp) && !feof(fp)) {
+ c = fgetc(fp);
+ if (c == '\n') {
+ break;
+ }
+ }
+ } else {
+ linebuf[linebuf_len - 1] = '\0';
+ }
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("mod_smbfilepwd_entry: got line |%s|\n", linebuf));
+#endif
+
+ if ((linebuf[0] == 0) && feof(fp)) {
+ DEBUG(4, ("mod_smbfilepwd_entry: end of file reached\n"));
+ break;
+ }
+
+ /*
+ * The line we have should be of the form :-
+ *
+ * username:uid:[32hex bytes]:....other flags presently
+ * ignored....
+ *
+ * or,
+ *
+ * username:uid:[32hex bytes]:[32hex bytes]:[attributes]:LCT-XXXXXXXX:...ignored.
+ *
+ * if Windows NT compatible passwords are also present.
+ */
+
+ if (linebuf[0] == '#' || linebuf[0] == '\0') {
+ DEBUG(6, ("mod_smbfilepwd_entry: skipping comment or blank line\n"));
+ continue;
+ }
+
+ p = (unsigned char *) strchr_m(linebuf, ':');
+
+ if (p == NULL) {
+ DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no :)\n"));
+ continue;
+ }
+
+ /*
+ * As 256 is shorter than a pstring we don't need to check
+ * length here - if this ever changes....
+ */
+
+ SMB_ASSERT(sizeof(user_name) > sizeof(linebuf));
+
+ strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
+ user_name[PTR_DIFF(p, linebuf)] = '\0';
+ if (strequal(user_name, pwd->smb_name)) {
+ found_entry = True;
+ break;
+ }
+ }
+
+ if (!found_entry) {
+ pw_file_unlock(lockfd, &(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+
+ DEBUG(2, ("Cannot update entry for user %s, as they don't exist in the smbpasswd file!\n",
+ pwd->smb_name));
+ return False;
+ }
+
+ DEBUG(6, ("mod_smbfilepwd_entry: entry exists\n"));
+
+ /* User name matches - get uid and password */
+ p++; /* Go past ':' */
+
+ if (!isdigit(*p)) {
+ DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (uid not number)\n"));
+ pw_file_unlock(lockfd, &(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return False;
+ }
+
+ while (*p && isdigit(*p))
+ p++;
+ if (*p != ':') {
+ DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no : after uid)\n"));
+ pw_file_unlock(lockfd, &(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return False;
+ }
+
+ /*
+ * Now get the password value - this should be 32 hex digits
+ * which are the ascii representations of a 16 byte string.
+ * Get two at a time and put them into the password.
+ */
+ p++;
+
+ /* Record exact password position */
+ pwd_seekpos += PTR_DIFF(p, linebuf);
+
+ if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
+ DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n"));
+ pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return (False);
+ }
+
+ if (p[32] != ':') {
+ DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n"));
+ pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return False;
+ }
+
+ /* Now check if the NT compatible password is
+ available. */
+ p += 33; /* Move to the first character of the line after
+ the lanman password. */
+ if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
+ DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n"));
+ pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return (False);
+ }
+
+ if (p[32] != ':') {
+ DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n"));
+ pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return False;
+ }
+
+ /*
+ * Now check if the account info and the password last
+ * change time is available.
+ */
+ p += 33; /* Move to the first character of the line after
+ the NT password. */
+
+ if (*p == '[') {
+
+ i = 0;
+ encode_bits[i++] = *p++;
+ while((linebuf_len > PTR_DIFF(p, linebuf)) && (*p != ']'))
+ encode_bits[i++] = *p++;
+
+ encode_bits[i++] = ']';
+ encode_bits[i++] = '\0';
+
+ if(i == NEW_PW_FORMAT_SPACE_PADDED_LEN) {
+ /*
+ * We are using a new format, space padded
+ * acct ctrl field. Encode the given acct ctrl
+ * bits into it.
+ */
+ fstrcpy(encode_bits, pdb_encode_acct_ctrl(pwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN));
+ } else {
+ DEBUG(0,("mod_smbfilepwd_entry: Using old smbpasswd format. This is no longer supported.!\n"));
+ DEBUG(0,("mod_smbfilepwd_entry: No changes made, failing.!\n"));
+ return False;
+ }
+
+ /* Go past the ']' */
+ if(linebuf_len > PTR_DIFF(p, linebuf))
+ p++;
+
+ if((linebuf_len > PTR_DIFF(p, linebuf)) && (*p == ':')) {
+ p++;
+
+ /* We should be pointing at the LCT entry. */
+ if((linebuf_len > (PTR_DIFF(p, linebuf) + 13)) && (StrnCaseCmp((char *)p, "LCT-", 4) == 0)) {
+
+ p += 4;
+ for(i = 0; i < 8; i++) {
+ if(p[i] == '\0' || !isxdigit(p[i]))
+ break;
+ }
+ if(i == 8) {
+ /*
+ * p points at 8 characters of hex digits -
+ * read into a time_t as the seconds since
+ * 1970 that the password was last changed.
+ */
+ got_pass_last_set_time = True;
+ } /* i == 8 */
+ } /* *p && StrnCaseCmp() */
+ } /* p == ':' */
+ } /* p == '[' */
+
+ /* Entry is correctly formed. */
+
+ /* Create the 32 byte representation of the new p16 */
+ pdb_sethexpwd(ascii_p16, pwd->smb_passwd, pwd->acct_ctrl);
+
+ /* Add on the NT md4 hash */
+ ascii_p16[32] = ':';
+ wr_len = 66;
+ pdb_sethexpwd(ascii_p16+33, pwd->smb_nt_passwd, pwd->acct_ctrl);
+ ascii_p16[65] = ':';
+ ascii_p16[66] = '\0'; /* null-terminate the string so that strlen works */
+
+ /* Add on the account info bits and the time of last
+ password change. */
+
+ if(got_pass_last_set_time) {
+ slprintf(&ascii_p16[strlen(ascii_p16)],
+ sizeof(ascii_p16)-(strlen(ascii_p16)+1),
+ "%s:LCT-%08X:",
+ encode_bits, (uint32)pwd->pass_last_set_time );
+ wr_len = strlen(ascii_p16);
+ }
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("mod_smbfilepwd_entry: "));
+ dump_data(100, ascii_p16, wr_len);
+#endif
+
+ if(wr_len > sizeof(linebuf)) {
+ DEBUG(0, ("mod_smbfilepwd_entry: line to write (%d) is too long.\n", wr_len+1));
+ pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return (False);
+ }
+
+ /*
+ * Do an atomic write into the file at the position defined by
+ * seekpos.
+ */
+
+ /* The mod user write needs to be atomic - so get the fd from
+ the fp and do a raw write() call.
+ */
+
+ fd = fileno(fp);
+
+ if (sys_lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) {
+ DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
+ pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return False;
+ }
+
+ /* Sanity check - ensure the areas we are writing are framed by ':' */
+ if (read(fd, linebuf, wr_len+1) != wr_len+1) {
+ DEBUG(0, ("mod_smbfilepwd_entry: read fail on file %s.\n", pfile));
+ pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return False;
+ }
+
+ if ((linebuf[0] != ':') || (linebuf[wr_len] != ':')) {
+ DEBUG(0, ("mod_smbfilepwd_entry: check on passwd file %s failed.\n", pfile));
+ pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return False;
+ }
+
+ if (sys_lseek(fd, pwd_seekpos, SEEK_SET) != pwd_seekpos) {
+ DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
+ pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return False;
+ }
+
+ if (write(fd, ascii_p16, wr_len) != wr_len) {
+ DEBUG(0, ("mod_smbfilepwd_entry: write failed in passwd file %s\n", pfile));
+ pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return False;
+ }
+
+ pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return True;
+}
+
+/************************************************************************
+ Routine to delete an entry in the smbpasswd file by name.
+*************************************************************************/
+
+static BOOL del_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, const char *name)
+{
+ const char *pfile = smbpasswd_state->smbpasswd_file;
+ pstring pfile2;
+ struct smb_passwd *pwd = NULL;
+ FILE *fp = NULL;
+ FILE *fp_write = NULL;
+ int pfile2_lockdepth = 0;
+
+ slprintf(pfile2, sizeof(pfile2)-1, "%s.%u", pfile, (unsigned)sys_getpid() );
+
+ /*
+ * Open the smbpassword file - for update. It needs to be update
+ * as we need any other processes to wait until we have replaced
+ * it.
+ */
+
+ if((fp = startsmbfilepwent(pfile, PWF_UPDATE, &(smbpasswd_state->pw_file_lock_depth))) == NULL) {
+ DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
+ return False;
+ }
+
+ /*
+ * Create the replacement password file.
+ */
+ if((fp_write = startsmbfilepwent(pfile2, PWF_CREATE, &pfile2_lockdepth)) == NULL) {
+ DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
+ endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+ return False;
+ }
+
+ /*
+ * Scan the file, a line at a time and check if the name matches.
+ */
+
+ while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) {
+ char *new_entry;
+ size_t new_entry_length;
+
+ if (strequal(name, pwd->smb_name)) {
+ DEBUG(10, ("add_smbfilepwd_entry: found entry with name %s - deleting it.\n", name));
+ continue;
+ }
+
+ /*
+ * We need to copy the entry out into the second file.
+ */
+
+ if((new_entry = format_new_smbpasswd_entry(pwd)) == NULL)
+ {
+ DEBUG(0, ("del_smbfilepwd_entry(malloc): Failed to copy entry for user %s to file %s. \
+Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
+ unlink(pfile2);
+ endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+ endsmbfilepwent(fp_write, &pfile2_lockdepth);
+ return False;
+ }
+
+ new_entry_length = strlen(new_entry);
+
+ if(fwrite(new_entry, 1, new_entry_length, fp_write) != new_entry_length)
+ {
+ DEBUG(0, ("del_smbfilepwd_entry(write): Failed to copy entry for user %s to file %s. \
+Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
+ unlink(pfile2);
+ endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+ endsmbfilepwent(fp_write, &pfile2_lockdepth);
+ free(new_entry);
+ return False;
+ }
+
+ free(new_entry);
+ }
+
+ /*
+ * Ensure pfile2 is flushed before rename.
+ */
+
+ if(fflush(fp_write) != 0)
+ {
+ DEBUG(0, ("del_smbfilepwd_entry: Failed to flush file %s. Error was %s\n", pfile2, strerror(errno)));
+ endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+ endsmbfilepwent(fp_write,&pfile2_lockdepth);
+ return False;
+ }
+
+ /*
+ * Do an atomic rename - then release the locks.
+ */
+
+ if(rename(pfile2,pfile) != 0) {
+ unlink(pfile2);
+ }
+
+ endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+ endsmbfilepwent(fp_write,&pfile2_lockdepth);
+ return True;
+}
+
+/*********************************************************************
+ Create a smb_passwd struct from a SAM_ACCOUNT.
+ We will not allocate any new memory. The smb_passwd struct
+ should only stay around as long as the SAM_ACCOUNT does.
+ ********************************************************************/
+static BOOL build_smb_pass (struct smb_passwd *smb_pw, const SAM_ACCOUNT *sampass)
+{
+ uint32 rid;
+
+ if (sampass == NULL)
+ return False;
+ ZERO_STRUCTP(smb_pw);
+
+ if (!IS_SAM_DEFAULT(sampass, PDB_USERSID)) {
+ rid = pdb_get_user_rid(sampass);
+
+ /* If the user specified a RID, make sure its able to be both stored and retreived */
+ if (rid == DOMAIN_USER_RID_GUEST) {
+ struct passwd *passwd = getpwnam_alloc(lp_guestaccount());
+ if (!passwd) {
+ DEBUG(0, ("Could not find gest account via getpwnam()! (%s)\n", lp_guestaccount()));
+ return False;
+ }
+ smb_pw->smb_userid=passwd->pw_uid;
+ passwd_free(&passwd);
+
+ } else if (fallback_pdb_rid_is_user(rid)) {
+ smb_pw->smb_userid=fallback_pdb_user_rid_to_uid(rid);
+ } else {
+ DEBUG(0,("build_sam_pass: Failing attempt to store user with non-uid based user RID. \n"));
+ return False;
+ }
+ }
+
+ smb_pw->smb_name=(const char*)pdb_get_username(sampass);
+
+ smb_pw->smb_passwd=pdb_get_lanman_passwd(sampass);
+ smb_pw->smb_nt_passwd=pdb_get_nt_passwd(sampass);
+
+ smb_pw->acct_ctrl=pdb_get_acct_ctrl(sampass);
+ smb_pw->pass_last_set_time=pdb_get_pass_last_set_time(sampass);
+
+ return True;
+}
+
+/*********************************************************************
+ Create a SAM_ACCOUNT from a smb_passwd struct
+ ********************************************************************/
+static BOOL build_sam_account(struct smbpasswd_privates *smbpasswd_state,
+ SAM_ACCOUNT *sam_pass, const struct smb_passwd *pw_buf)
+{
+ struct passwd *pwfile;
+
+ if (sam_pass==NULL) {
+ DEBUG(5,("build_sam_account: SAM_ACCOUNT is NULL\n"));
+ return False;
+ }
+
+ /* verify the user account exists */
+
+ if ( !(pwfile = getpwnam_alloc(pw_buf->smb_name)) ) {
+ DEBUG(0,("build_sam_account: smbpasswd database is corrupt! username %s with uid "
+ "%u is not in unix passwd database!\n", pw_buf->smb_name, pw_buf->smb_userid));
+ return False;
+ }
+
+ if (!NT_STATUS_IS_OK(pdb_fill_sam_pw(sam_pass, pwfile)))
+ return False;
+
+ passwd_free(&pwfile);
+
+ /* set remaining fields */
+
+ pdb_set_nt_passwd (sam_pass, pw_buf->smb_nt_passwd, PDB_SET);
+ pdb_set_lanman_passwd (sam_pass, pw_buf->smb_passwd, PDB_SET);
+ pdb_set_acct_ctrl (sam_pass, pw_buf->acct_ctrl, PDB_SET);
+ pdb_set_pass_last_set_time (sam_pass, pw_buf->pass_last_set_time, PDB_SET);
+ pdb_set_pass_can_change_time (sam_pass, pw_buf->pass_last_set_time, PDB_SET);
+
+ return True;
+}
+
+/*****************************************************************
+ Functions to be implemented by the new passdb API
+ ****************************************************************/
+static NTSTATUS smbpasswd_setsampwent (struct pdb_methods *my_methods, BOOL update)
+{
+ struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+
+ smbpasswd_state->pw_file = startsmbfilepwent(smbpasswd_state->smbpasswd_file,
+ update ? PWF_UPDATE : PWF_READ,
+ &(smbpasswd_state->pw_file_lock_depth));
+
+ /* did we fail? Should we try to create it? */
+ if (!smbpasswd_state->pw_file && update && errno == ENOENT)
+ {
+ FILE *fp;
+ /* slprintf(msg_str,msg_str_len-1,
+ "smbpasswd file did not exist - attempting to create it.\n"); */
+ DEBUG(0,("smbpasswd file did not exist - attempting to create it.\n"));
+ fp = sys_fopen(smbpasswd_state->smbpasswd_file, "w");
+ if (fp)
+ {
+ fprintf(fp, "# Samba SMB password file\n");
+ fclose(fp);
+ }
+
+ smbpasswd_state->pw_file = startsmbfilepwent(smbpasswd_state->smbpasswd_file,
+ update ? PWF_UPDATE : PWF_READ,
+ &(smbpasswd_state->pw_file_lock_depth));
+ }
+
+ if (smbpasswd_state->pw_file != NULL)
+ return NT_STATUS_OK;
+ else
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+static void smbpasswd_endsampwent (struct pdb_methods *my_methods)
+{
+ struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+ endsmbfilepwent(smbpasswd_state->pw_file, &(smbpasswd_state->pw_file_lock_depth));
+}
+
+/*****************************************************************
+ ****************************************************************/
+static NTSTATUS smbpasswd_getsampwent(struct pdb_methods *my_methods, SAM_ACCOUNT *user)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+ struct smb_passwd *pw_buf=NULL;
+ BOOL done = False;
+ DEBUG(5,("pdb_getsampwent\n"));
+
+ if (user==NULL) {
+ DEBUG(5,("pdb_getsampwent (smbpasswd): user is NULL\n"));
+#if 0
+ smb_panic("NULL pointer passed to getsampwent (smbpasswd)\n");
+#endif
+ return nt_status;
+ }
+
+ while (!done)
+ {
+ /* do we have an entry? */
+ pw_buf = getsmbfilepwent(smbpasswd_state, smbpasswd_state->pw_file);
+ if (pw_buf == NULL)
+ return nt_status;
+
+ /* build the SAM_ACCOUNT entry from the smb_passwd struct.
+ We loop in case the user in the pdb does not exist in
+ the local system password file */
+ if (build_sam_account(smbpasswd_state, user, pw_buf))
+ done = True;
+ }
+
+ DEBUG(5,("getsampwent (smbpasswd): done\n"));
+
+ /* success */
+ return NT_STATUS_OK;
+}
+
+
+/****************************************************************
+ Search smbpasswd file by iterating over the entries. Do not
+ call getpwnam() for unix account information until we have found
+ the correct entry
+ ***************************************************************/
+static NTSTATUS smbpasswd_getsampwnam(struct pdb_methods *my_methods,
+ SAM_ACCOUNT *sam_acct, const char *username)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+ struct smb_passwd *smb_pw;
+ void *fp = NULL;
+
+ DEBUG(10, ("getsampwnam (smbpasswd): search by name: %s\n", username));
+
+ /* startsmbfilepwent() is used here as we don't want to lookup
+ the UNIX account in the local system password file until
+ we have a match. */
+ fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
+
+ if (fp == NULL) {
+ DEBUG(0, ("Unable to open passdb database.\n"));
+ return nt_status;
+ }
+
+ while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL)&& (!strequal(smb_pw->smb_name, username)) )
+ /* do nothing....another loop */ ;
+
+ endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+
+
+ /* did we locate the username in smbpasswd */
+ if (smb_pw == NULL)
+ return nt_status;
+
+ DEBUG(10, ("getsampwnam (smbpasswd): found by name: %s\n", smb_pw->smb_name));
+
+ if (!sam_acct) {
+ DEBUG(10,("getsampwnam (smbpasswd): SAM_ACCOUNT is NULL\n"));
+#if 0
+ smb_panic("NULL pointer passed to pdb_getsampwnam\n");
+#endif
+ return nt_status;
+ }
+
+ /* now build the SAM_ACCOUNT */
+ if (!build_sam_account(smbpasswd_state, sam_acct, smb_pw))
+ return nt_status;
+
+ /* success */
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smbpasswd_getsampwsid(struct pdb_methods *my_methods, SAM_ACCOUNT *sam_acct, const DOM_SID *sid)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+ struct smb_passwd *smb_pw;
+ void *fp = NULL;
+ fstring sid_str;
+ uint32 rid;
+
+ DEBUG(10, ("smbpasswd_getsampwrid: search by sid: %s\n", sid_to_string(sid_str, sid)));
+
+ if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ /* More special case 'guest account' hacks... */
+ if (rid == DOMAIN_USER_RID_GUEST) {
+ const char *guest_account = lp_guestaccount();
+ if (!(guest_account && *guest_account)) {
+ DEBUG(1, ("Guest account not specfied!\n"));
+ return nt_status;
+ }
+ return smbpasswd_getsampwnam(my_methods, sam_acct, guest_account);
+ }
+
+ /* Open the sam password file - not for update. */
+ fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
+
+ if (fp == NULL) {
+ DEBUG(0, ("Unable to open passdb database.\n"));
+ return nt_status;
+ }
+
+ while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL) && (fallback_pdb_uid_to_user_rid(smb_pw->smb_userid) != rid) )
+ /* do nothing */ ;
+
+ endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+
+
+ /* did we locate the username in smbpasswd */
+ if (smb_pw == NULL)
+ return nt_status;
+
+ DEBUG(10, ("getsampwrid (smbpasswd): found by name: %s\n", smb_pw->smb_name));
+
+ if (!sam_acct) {
+ DEBUG(10,("getsampwrid: (smbpasswd) SAM_ACCOUNT is NULL\n"));
+#if 0
+ smb_panic("NULL pointer passed to pdb_getsampwrid\n");
+#endif
+ return nt_status;
+ }
+
+ /* now build the SAM_ACCOUNT */
+ if (!build_sam_account (smbpasswd_state, sam_acct, smb_pw))
+ return nt_status;
+
+ /* build_sam_account might change the SID on us, if the name was for the guest account */
+ if (NT_STATUS_IS_OK(nt_status) && !sid_equal(pdb_get_user_sid(sam_acct), sid)) {
+ fstring sid_string1, sid_string2;
+ DEBUG(1, ("looking for user with sid %s instead returned %s for account %s!?!\n",
+ sid_to_string(sid_string1, sid), sid_to_string(sid_string2, pdb_get_user_sid(sam_acct)), pdb_get_username(sam_acct)));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ /* success */
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smbpasswd_add_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT *sampass)
+{
+ struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+ struct smb_passwd smb_pw;
+
+ /* convert the SAM_ACCOUNT */
+ if (!build_smb_pass(&smb_pw, sampass)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* add the entry */
+ if(!add_smbfilepwd_entry(smbpasswd_state, &smb_pw)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smbpasswd_update_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT *sampass)
+{
+ struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+ struct smb_passwd smb_pw;
+
+ /* convert the SAM_ACCOUNT */
+ if (!build_smb_pass(&smb_pw, sampass)) {
+ DEBUG(0, ("smbpasswd_update_sam_account: build_smb_pass failed!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* update the entry */
+ if(!mod_smbfilepwd_entry(smbpasswd_state, &smb_pw)) {
+ DEBUG(0, ("smbpasswd_update_sam_account: mod_smbfilepwd_entry failed!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smbpasswd_delete_sam_account (struct pdb_methods *my_methods, SAM_ACCOUNT *sampass)
+{
+ struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+
+ const char *username = pdb_get_username(sampass);
+
+ if (del_smbfilepwd_entry(smbpasswd_state, username))
+ return NT_STATUS_OK;
+
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+static void free_private_data(void **vp)
+{
+ struct smbpasswd_privates **privates = (struct smbpasswd_privates**)vp;
+
+ endsmbfilepwent((*privates)->pw_file, &((*privates)->pw_file_lock_depth));
+
+ *privates = NULL;
+ /* No need to free any further, as it is talloc()ed */
+}
+
+static NTSTATUS pdb_init_smbpasswd(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
+{
+ NTSTATUS nt_status;
+ struct smbpasswd_privates *privates;
+
+ if (!NT_STATUS_IS_OK(nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
+ return nt_status;
+ }
+
+ (*pdb_method)->name = "smbpasswd";
+
+ (*pdb_method)->setsampwent = smbpasswd_setsampwent;
+ (*pdb_method)->endsampwent = smbpasswd_endsampwent;
+ (*pdb_method)->getsampwent = smbpasswd_getsampwent;
+ (*pdb_method)->getsampwnam = smbpasswd_getsampwnam;
+ (*pdb_method)->getsampwsid = smbpasswd_getsampwsid;
+ (*pdb_method)->add_sam_account = smbpasswd_add_sam_account;
+ (*pdb_method)->update_sam_account = smbpasswd_update_sam_account;
+ (*pdb_method)->delete_sam_account = smbpasswd_delete_sam_account;
+
+ /* Setup private data and free function */
+
+ privates = talloc_zero(pdb_context->mem_ctx, sizeof(struct smbpasswd_privates));
+
+ if (!privates) {
+ DEBUG(0, ("talloc() failed for smbpasswd private_data!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Store some config details */
+
+ if (location) {
+ privates->smbpasswd_file = talloc_strdup(pdb_context->mem_ctx, location);
+ } else {
+ privates->smbpasswd_file = talloc_strdup(pdb_context->mem_ctx, lp_smb_passwd_file());
+ }
+
+ if (!privates->smbpasswd_file) {
+ DEBUG(0, ("talloc_strdp() failed for storing smbpasswd location!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*pdb_method)->private_data = privates;
+
+ (*pdb_method)->free_private_data = free_private_data;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_smbpasswd_init(void)
+{
+ return smb_register_passdb(PASSDB_INTERFACE_VERSION, "smbpasswd", pdb_init_smbpasswd);
+}
diff --git a/source/passdb/pdb_sql.c b/source/passdb/pdb_sql.c
new file mode 100644
index 00000000000..ffb8313a970
--- /dev/null
+++ b/source/passdb/pdb_sql.c
@@ -0,0 +1,493 @@
+/*
+ * Common PDB SQL backend functions
+ * Copyright (C) Jelmer Vernooij 2003-2004
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 675
+ * Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+#define CONFIG_TABLE_DEFAULT "user"
+#define CONFIG_LOGON_TIME_DEFAULT "logon_time"
+#define CONFIG_LOGOFF_TIME_DEFAULT "logoff_time"
+#define CONFIG_KICKOFF_TIME_DEFAULT "kickoff_time"
+#define CONFIG_PASS_LAST_SET_TIME_DEFAULT "pass_last_set_time"
+#define CONFIG_PASS_CAN_CHANGE_TIME_DEFAULT "pass_can_change_time"
+#define CONFIG_PASS_MUST_CHANGE_TIME_DEFAULT "pass_must_change_time"
+#define CONFIG_USERNAME_DEFAULT "username"
+#define CONFIG_DOMAIN_DEFAULT "domain"
+#define CONFIG_NT_USERNAME_DEFAULT "nt_username"
+#define CONFIG_FULLNAME_DEFAULT "nt_fullname"
+#define CONFIG_HOME_DIR_DEFAULT "home_dir"
+#define CONFIG_DIR_DRIVE_DEFAULT "dir_drive"
+#define CONFIG_LOGON_SCRIPT_DEFAULT "logon_script"
+#define CONFIG_PROFILE_PATH_DEFAULT "profile_path"
+#define CONFIG_ACCT_DESC_DEFAULT "acct_desc"
+#define CONFIG_WORKSTATIONS_DEFAULT "workstations"
+#define CONFIG_UNKNOWN_STR_DEFAULT "unknown_str"
+#define CONFIG_MUNGED_DIAL_DEFAULT "munged_dial"
+#define CONFIG_USER_SID_DEFAULT "user_sid"
+#define CONFIG_GROUP_SID_DEFAULT "group_sid"
+#define CONFIG_LM_PW_DEFAULT "lm_pw"
+#define CONFIG_NT_PW_DEFAULT "nt_pw"
+#define CONFIG_PLAIN_PW_DEFAULT "NULL"
+#define CONFIG_ACCT_CTRL_DEFAULT "acct_ctrl"
+#define CONFIG_LOGON_DIVS_DEFAULT "logon_divs"
+#define CONFIG_HOURS_LEN_DEFAULT "hours_len"
+#define CONFIG_BAD_PASSWORD_COUNT_DEFAULT "bad_password_count"
+#define CONFIG_LOGON_COUNT_DEFAULT "logon_count"
+#define CONFIG_UNKNOWN_6_DEFAULT "unknown_6"
+
+/* Used to construct insert and update queries */
+
+typedef struct pdb_sql_query {
+ char update;
+ TALLOC_CTX *mem_ctx;
+ char *part1;
+ char *part2;
+} pdb_sql_query;
+
+static void pdb_sql_int_field(struct pdb_sql_query *q, const char *name, int value)
+{
+ if (!name || strchr(name, '\''))
+ return; /* This field shouldn't be set by us */
+
+ if (q->update) {
+ q->part1 =
+ talloc_asprintf_append(q->mem_ctx, q->part1,
+ "%s = %d,", name, value);
+ } else {
+ q->part1 =
+ talloc_asprintf_append(q->mem_ctx, q->part1, "%s,", name);
+ q->part2 =
+ talloc_asprintf_append(q->mem_ctx, q->part2, "%d,", value);
+ }
+}
+
+char *sql_escape_string(const char *unesc)
+{
+ char *esc = malloc(strlen(unesc) * 2 + 3);
+ size_t pos_unesc = 0, pos_esc = 0;
+
+ for(pos_unesc = 0; unesc[pos_unesc]; pos_unesc++) {
+ switch(unesc[pos_unesc]) {
+ case '\\':
+ case '\'':
+ case '"':
+ esc[pos_esc] = '\\'; pos_esc++;
+ default:
+ esc[pos_esc] = unesc[pos_unesc]; pos_esc++;
+ break;
+ }
+ }
+
+ esc[pos_esc] = '\0';
+
+ return esc;
+}
+
+static NTSTATUS pdb_sql_string_field(struct pdb_sql_query *q,
+ const char *name, const char *value)
+{
+ char *esc_value;
+
+ if (!name || !value || !strcmp(value, "") || strchr(name, '\''))
+ return NT_STATUS_INVALID_PARAMETER; /* This field shouldn't be set by module */
+
+ esc_value = sql_escape_string(value);
+
+ if (q->update) {
+ q->part1 =
+ talloc_asprintf_append(q->mem_ctx, q->part1,
+ "%s = '%s',", name, esc_value);
+ } else {
+ q->part1 =
+ talloc_asprintf_append(q->mem_ctx, q->part1, "%s,", name);
+ q->part2 =
+ talloc_asprintf_append(q->mem_ctx, q->part2, "'%s',",
+ esc_value);
+ }
+
+ SAFE_FREE(esc_value);
+
+ return NT_STATUS_OK;
+}
+
+#define config_value(data,name,default_value) \
+ lp_parm_const_string(GLOBAL_SECTION_SNUM, data, name, default_value)
+
+static const char * config_value_write(const char *location, const char *name, const char *default_value)
+{
+ char const *v = NULL;
+ char const *swrite = NULL;
+
+ v = lp_parm_const_string(GLOBAL_SECTION_SNUM, location, name, default_value);
+
+ if (!v)
+ return NULL;
+
+ swrite = strrchr(v, ':');
+
+ /* Default to the same field as read field */
+ if (!swrite)
+ return v;
+
+ swrite++;
+
+ /* If the field is 0 chars long, we shouldn't write to it */
+ if (!strlen(swrite) || !strcmp(swrite, "NULL"))
+ return NULL;
+
+ /* Otherwise, use the additionally specified */
+ return swrite;
+}
+
+static const char * config_value_read(const char *location, const char *name, const char *default_value)
+{
+ char *v = NULL;
+ char *swrite;
+
+ v = lp_parm_talloc_string(GLOBAL_SECTION_SNUM, location, name, default_value);
+
+ if (!v)
+ return "NULL";
+
+ swrite = strrchr(v, ':');
+
+ /* If no write is specified, there are no problems */
+ if (!swrite) {
+ if (strlen(v) == 0)
+ return "NULL";
+ return (const char *)v;
+ }
+
+ /* Otherwise, we have to cut the ':write_part' */
+ *swrite = '\0';
+ if (strlen(v) == 0)
+ return "NULL";
+
+ return (const char *)v;
+}
+
+char *sql_account_query_select(const char *data, BOOL update, enum sql_search_field field, const char *value)
+{
+ const char *field_string;
+ char *query;
+
+ switch(field) {
+ case SQL_SEARCH_NONE:
+ field_string = "'1'";
+ value = "1";
+ break;
+
+ case SQL_SEARCH_USER_SID:
+ field_string = config_value_read(data, "user sid column",
+ CONFIG_USER_SID_DEFAULT);
+ break;
+
+ case SQL_SEARCH_USER_NAME:
+ field_string = config_value_read(data, "username column",
+ CONFIG_USERNAME_DEFAULT);
+ break;
+ }
+
+ asprintf(&query,
+ "SELECT %s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s FROM %s WHERE %s = '%s'",
+ config_value_read(data, "logon time column",
+ CONFIG_LOGON_TIME_DEFAULT),
+ config_value_read(data, "logoff time column",
+ CONFIG_LOGOFF_TIME_DEFAULT),
+ config_value_read(data, "kickoff time column",
+ CONFIG_KICKOFF_TIME_DEFAULT),
+ config_value_read(data, "pass last set time column",
+ CONFIG_PASS_LAST_SET_TIME_DEFAULT),
+ config_value_read(data, "pass can change time column",
+ CONFIG_PASS_CAN_CHANGE_TIME_DEFAULT),
+ config_value_read(data, "pass must change time column",
+ CONFIG_PASS_MUST_CHANGE_TIME_DEFAULT),
+ config_value_read(data, "username column",
+ CONFIG_USERNAME_DEFAULT),
+ config_value_read(data, "domain column",
+ CONFIG_DOMAIN_DEFAULT),
+ config_value_read(data, "nt username column",
+ CONFIG_NT_USERNAME_DEFAULT),
+ config_value_read(data, "fullname column",
+ CONFIG_FULLNAME_DEFAULT),
+ config_value_read(data, "home dir column",
+ CONFIG_HOME_DIR_DEFAULT),
+ config_value_read(data, "dir drive column",
+ CONFIG_DIR_DRIVE_DEFAULT),
+ config_value_read(data, "logon script column",
+ CONFIG_LOGON_SCRIPT_DEFAULT),
+ config_value_read(data, "profile path column",
+ CONFIG_PROFILE_PATH_DEFAULT),
+ config_value_read(data, "acct desc column",
+ CONFIG_ACCT_DESC_DEFAULT),
+ config_value_read(data, "workstations column",
+ CONFIG_WORKSTATIONS_DEFAULT),
+ config_value_read(data, "unknown string column",
+ CONFIG_UNKNOWN_STR_DEFAULT),
+ config_value_read(data, "munged dial column",
+ CONFIG_MUNGED_DIAL_DEFAULT),
+ config_value_read(data, "user sid column",
+ CONFIG_USER_SID_DEFAULT),
+ config_value_read(data, "group sid column",
+ CONFIG_GROUP_SID_DEFAULT),
+ config_value_read(data, "lanman pass column",
+ CONFIG_LM_PW_DEFAULT),
+ config_value_read(data, "nt pass column",
+ CONFIG_NT_PW_DEFAULT),
+ config_value_read(data, "plain pass column",
+ CONFIG_PLAIN_PW_DEFAULT),
+ config_value_read(data, "acct ctrl column",
+ CONFIG_ACCT_CTRL_DEFAULT),
+ config_value_read(data, "logon divs column",
+ CONFIG_LOGON_DIVS_DEFAULT),
+ config_value_read(data, "hours len column",
+ CONFIG_HOURS_LEN_DEFAULT),
+ config_value_read(data, "bad password count column",
+ CONFIG_BAD_PASSWORD_COUNT_DEFAULT),
+ config_value_read(data, "logon count column",
+ CONFIG_LOGON_COUNT_DEFAULT),
+ config_value_read(data, "unknown 6 column",
+ CONFIG_UNKNOWN_6_DEFAULT),
+ config_value(data, "table", CONFIG_TABLE_DEFAULT),
+ field_string, value
+ );
+ return query;
+}
+
+char *sql_account_query_delete(const char *data, const char *esc)
+{
+ char *query;
+
+ asprintf(&query, "DELETE FROM %s WHERE %s = '%s'",
+ config_value(data, "table", CONFIG_TABLE_DEFAULT),
+ config_value_read(data, "username column",
+ CONFIG_USERNAME_DEFAULT), esc);
+ return query;
+}
+
+char *sql_account_query_update(const char *location, const SAM_ACCOUNT *newpwd, char isupdate)
+{
+ char *ret;
+ pstring temp;
+ pdb_sql_query query;
+ fstring sid_str;
+
+ query.update = isupdate;
+
+ /* I know this is somewhat overkill but only the talloc
+ * functions have asprint_append and the 'normal' asprintf
+ * is a GNU extension */
+ query.mem_ctx = talloc_init("sql_query_update");
+ query.part2 = talloc_asprintf(query.mem_ctx, "%s", "");
+ if (query.update) {
+ query.part1 =
+ talloc_asprintf(query.mem_ctx, "UPDATE %s SET ",
+ config_value(location, "table",
+ CONFIG_TABLE_DEFAULT));
+ } else {
+ query.part1 =
+ talloc_asprintf(query.mem_ctx, "INSERT INTO %s (",
+ config_value(location, "table",
+ CONFIG_TABLE_DEFAULT));
+ }
+
+ pdb_sql_int_field(&query,
+ config_value_write(location, "acct ctrl column",
+ CONFIG_ACCT_CTRL_DEFAULT),
+ pdb_get_acct_ctrl(newpwd));
+
+ if (pdb_get_init_flags(newpwd, PDB_LOGONTIME) != PDB_DEFAULT) {
+ pdb_sql_int_field(&query,
+ config_value_write(location,
+ "logon time column",
+ CONFIG_LOGON_TIME_DEFAULT),
+ pdb_get_logon_time(newpwd));
+ }
+
+ if (pdb_get_init_flags(newpwd, PDB_LOGOFFTIME) != PDB_DEFAULT) {
+ pdb_sql_int_field(&query,
+ config_value_write(location,
+ "logoff time column",
+ CONFIG_LOGOFF_TIME_DEFAULT),
+ pdb_get_logoff_time(newpwd));
+ }
+
+ if (pdb_get_init_flags(newpwd, PDB_KICKOFFTIME) != PDB_DEFAULT) {
+ pdb_sql_int_field(&query,
+ config_value_write(location,
+ "kickoff time column",
+ CONFIG_KICKOFF_TIME_DEFAULT),
+ pdb_get_kickoff_time(newpwd));
+ }
+
+ if (pdb_get_init_flags(newpwd, PDB_CANCHANGETIME) != PDB_DEFAULT) {
+ pdb_sql_int_field(&query,
+ config_value_write(location,
+ "pass can change time column",
+ CONFIG_PASS_CAN_CHANGE_TIME_DEFAULT),
+ pdb_get_pass_can_change_time(newpwd));
+ }
+
+ if (pdb_get_init_flags(newpwd, PDB_MUSTCHANGETIME) != PDB_DEFAULT) {
+ pdb_sql_int_field(&query,
+ config_value_write(location,
+ "pass must change time column",
+ CONFIG_PASS_MUST_CHANGE_TIME_DEFAULT),
+ pdb_get_pass_must_change_time(newpwd));
+ }
+
+ if (pdb_get_pass_last_set_time(newpwd)) {
+ pdb_sql_int_field(&query,
+ config_value_write(location,
+ "pass last set time column",
+ CONFIG_PASS_LAST_SET_TIME_DEFAULT),
+ pdb_get_pass_last_set_time(newpwd));
+ }
+
+ if (pdb_get_hours_len(newpwd)) {
+ pdb_sql_int_field(&query,
+ config_value_write(location,
+ "hours len column",
+ CONFIG_HOURS_LEN_DEFAULT),
+ pdb_get_hours_len(newpwd));
+ }
+
+ if (pdb_get_logon_divs(newpwd)) {
+ pdb_sql_int_field(&query,
+ config_value_write(location,
+ "logon divs column",
+ CONFIG_LOGON_DIVS_DEFAULT),
+ pdb_get_logon_divs(newpwd));
+ }
+
+ pdb_sql_string_field(&query,
+ config_value_write(location, "user sid column",
+ CONFIG_USER_SID_DEFAULT),
+ sid_to_string(sid_str,
+ pdb_get_user_sid(newpwd)));
+
+ pdb_sql_string_field(&query,
+ config_value_write(location, "group sid column",
+ CONFIG_GROUP_SID_DEFAULT),
+ sid_to_string(sid_str,
+ pdb_get_group_sid(newpwd)));
+
+ pdb_sql_string_field(&query,
+ config_value_write(location, "username column",
+ CONFIG_USERNAME_DEFAULT),
+ pdb_get_username(newpwd));
+
+ pdb_sql_string_field(&query,
+ config_value_write(location, "domain column",
+ CONFIG_DOMAIN_DEFAULT),
+ pdb_get_domain(newpwd));
+
+ pdb_sql_string_field(&query,
+ config_value_write(location,
+ "nt username column",
+ CONFIG_NT_USERNAME_DEFAULT),
+ pdb_get_nt_username(newpwd));
+
+ pdb_sql_string_field(&query,
+ config_value_write(location, "fullname column",
+ CONFIG_FULLNAME_DEFAULT),
+ pdb_get_fullname(newpwd));
+
+ pdb_sql_string_field(&query,
+ config_value_write(location,
+ "logon script column",
+ CONFIG_LOGON_SCRIPT_DEFAULT),
+ pdb_get_logon_script(newpwd));
+
+ pdb_sql_string_field(&query,
+ config_value_write(location,
+ "profile path column",
+ CONFIG_PROFILE_PATH_DEFAULT),
+ pdb_get_profile_path(newpwd));
+
+ pdb_sql_string_field(&query,
+ config_value_write(location, "dir drive column",
+ CONFIG_DIR_DRIVE_DEFAULT),
+ pdb_get_dir_drive(newpwd));
+
+ pdb_sql_string_field(&query,
+ config_value_write(location, "home dir column",
+ CONFIG_HOME_DIR_DEFAULT),
+ pdb_get_homedir(newpwd));
+
+ pdb_sql_string_field(&query,
+ config_value_write(location,
+ "workstations column",
+ CONFIG_WORKSTATIONS_DEFAULT),
+ pdb_get_workstations(newpwd));
+
+ pdb_sql_string_field(&query,
+ config_value_write(location,
+ "unknown string column",
+ CONFIG_UNKNOWN_STR_DEFAULT),
+ pdb_get_workstations(newpwd));
+
+ pdb_sethexpwd(temp, pdb_get_lanman_passwd(newpwd),
+ pdb_get_acct_ctrl(newpwd));
+ pdb_sql_string_field(&query,
+ config_value_write(location,
+ "lanman pass column",
+ CONFIG_LM_PW_DEFAULT), temp);
+
+ pdb_sethexpwd(temp, pdb_get_nt_passwd(newpwd),
+ pdb_get_acct_ctrl(newpwd));
+ pdb_sql_string_field(&query,
+ config_value_write(location, "nt pass column",
+ CONFIG_NT_PW_DEFAULT), temp);
+
+ if (query.update) {
+ query.part1[strlen(query.part1) - 1] = '\0';
+ query.part1 =
+ talloc_asprintf_append(query.mem_ctx, query.part1,
+ " WHERE %s = '%s'",
+ config_value_read(location,
+ "user sid column",
+ CONFIG_USER_SID_DEFAULT),
+ sid_to_string(sid_str, pdb_get_user_sid (newpwd)));
+ } else {
+ query.part2[strlen(query.part2) - 1] = ')';
+ query.part1[strlen(query.part1) - 1] = ')';
+ query.part1 =
+ talloc_asprintf_append(query.mem_ctx, query.part1,
+ " VALUES (%s", query.part2);
+ }
+
+ ret = strdup(query.part1);
+ talloc_destroy(query.mem_ctx);
+ return ret;
+}
+
+BOOL sql_account_config_valid(const char *data)
+{
+ const char *sid_column, *username_column;
+
+ sid_column = config_value_read(data, "user sid column", CONFIG_USER_SID_DEFAULT);
+ username_column = config_value_read(data, "username column", CONFIG_USERNAME_DEFAULT);
+
+ if(!strcmp(sid_column,"NULL") || !strcmp(username_column, "NULL")) {
+ DEBUG(0,("Please specify both a valid 'user sid column' and a valid 'username column' in smb.conf\n"));
+ return False;
+ }
+
+ return True;
+}
diff --git a/source/passdb/pdb_tdb.c b/source/passdb/pdb_tdb.c
new file mode 100644
index 00000000000..2af6609ef6c
--- /dev/null
+++ b/source/passdb/pdb_tdb.c
@@ -0,0 +1,1374 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * SMB parameters and setup
+ * Copyright (C) Andrew Tridgell 1992-1998
+ * Copyright (C) Simo Sorce 2000-2002
+ * Copyright (C) Gerald Carter 2000
+ * Copyright (C) Jeremy Allison 2001
+ * Copyright (C) Andrew Bartlett 2002
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 675
+ * Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+#if 0 /* when made a module use this */
+
+static int tdbsam_debug_level = DBGC_ALL;
+#undef DBGC_CLASS
+#define DBGC_CLASS tdbsam_debug_level
+
+#else
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+#endif
+
+#define TDBSAM_VERSION 1 /* Most recent TDBSAM version */
+#define TDBSAM_VERSION_STRING "INFO/version"
+#define PASSDB_FILE_NAME "passdb.tdb"
+#define USERPREFIX "USER_"
+#define RIDPREFIX "RID_"
+#define PRIVPREFIX "PRIV_"
+#define tdbsamver_t int32
+
+struct tdbsam_privates {
+ TDB_CONTEXT *passwd_tdb;
+
+ /* retrive-once info */
+ const char *tdbsam_location;
+};
+
+struct pwent_list {
+ struct pwent_list *prev, *next;
+ TDB_DATA key;
+};
+static struct pwent_list *tdbsam_pwent_list;
+
+
+/**
+ * Convert old TDBSAM to the latest version.
+ * @param pdb_tdb A pointer to the opened TDBSAM file which must be converted.
+ * This file must be opened with read/write access.
+ * @param from Current version of the TDBSAM file.
+ * @return True if the conversion has been successful, false otherwise.
+ **/
+
+static BOOL tdbsam_convert(TDB_CONTEXT *pdb_tdb, tdbsamver_t from)
+{
+ const char * vstring = TDBSAM_VERSION_STRING;
+ SAM_ACCOUNT *user = NULL;
+ const char *prefix = USERPREFIX;
+ TDB_DATA data, key, old_key;
+ uint8 *buf = NULL;
+ BOOL ret;
+
+ if (pdb_tdb == NULL) {
+ DEBUG(0,("tdbsam_convert: Bad TDB Context pointer.\n"));
+ return False;
+ }
+
+ /* handle a Samba upgrade */
+ tdb_lock_bystring(pdb_tdb, vstring, 0);
+
+ if (!NT_STATUS_IS_OK(pdb_init_sam(&user))) {
+ DEBUG(0,("tdbsam_convert: cannot initialized a SAM_ACCOUNT.\n"));
+ return False;
+ }
+
+ /* Enumerate all records and convert them */
+ key = tdb_firstkey(pdb_tdb);
+
+ while (key.dptr) {
+
+ /* skip all non-USER entries (eg. RIDs) */
+ while ((key.dsize != 0) && (strncmp(key.dptr, prefix, strlen (prefix)))) {
+ old_key = key;
+ /* increment to next in line */
+ key = tdb_nextkey(pdb_tdb, key);
+ SAFE_FREE(old_key.dptr);
+ }
+
+ if (key.dptr) {
+
+ /* read from tdbsam */
+ data = tdb_fetch(pdb_tdb, key);
+ if (!data.dptr) {
+ DEBUG(0,("tdbsam_convert: database entry not found: %s.\n",key.dptr));
+ return False;
+ }
+
+ if (!NT_STATUS_IS_OK(pdb_reset_sam(user))) {
+ DEBUG(0,("tdbsam_convert: cannot reset SAM_ACCOUNT.\n"));
+ SAFE_FREE(data.dptr);
+ return False;
+ }
+
+ /* unpack the buffer from the former format */
+ DEBUG(10,("tdbsam_convert: Try unpacking a record with (key:%s) (version:%d)\n", key.dptr, from));
+ switch (from) {
+ case 0:
+ ret = init_sam_from_buffer_v0(user, (uint8 *)data.dptr, data.dsize);
+ break;
+ case 1:
+ ret = init_sam_from_buffer_v1(user, (uint8 *)data.dptr, data.dsize);
+ break;
+ default:
+ /* unknown tdbsam version */
+ ret = False;
+ }
+ if (!ret) {
+ DEBUG(0,("tdbsam_convert: Bad SAM_ACCOUNT entry returned from TDB (key:%s) (version:%d)\n", key.dptr, from));
+ SAFE_FREE(data.dptr);
+ return False;
+ }
+
+ /* pack from the buffer into the new format */
+ DEBUG(10,("tdbsam_convert: Try packing a record (key:%s) (version:%d)\n", key.dptr, from));
+ if ((data.dsize=init_buffer_from_sam (&buf, user, False)) == -1) {
+ DEBUG(0,("tdbsam_convert: cannot pack the SAM_ACCOUNT into the new format\n"));
+ SAFE_FREE(data.dptr);
+ return False;
+ }
+ data.dptr = (char *)buf;
+
+ /* Store the buffer inside the TDBSAM */
+ if (tdb_store(pdb_tdb, key, data, TDB_MODIFY) != TDB_SUCCESS) {
+ DEBUG(0,("tdbsam_convert: cannot store the SAM_ACCOUNT (key:%s) in new format\n",key.dptr));
+ SAFE_FREE(data.dptr);
+ return False;
+ }
+
+ SAFE_FREE(data.dptr);
+
+ /* increment to next in line */
+ old_key = key;
+ key = tdb_nextkey(pdb_tdb, key);
+ SAFE_FREE(old_key.dptr);
+ }
+
+ }
+
+ pdb_free_sam(&user);
+
+ /* upgrade finished */
+ tdb_store_int32(pdb_tdb, vstring, TDBSAM_VERSION);
+ tdb_unlock_bystring(pdb_tdb, vstring);
+
+ return(True);
+}
+
+/**
+ * Open the TDB passwd database, check version and convert it if needed.
+ * @param name filename of the tdbsam file.
+ * @param open_flags file access mode.
+ * @return a TDB_CONTEXT handle on the tdbsam file.
+ **/
+
+static TDB_CONTEXT * tdbsam_tdbopen (const char *name, int open_flags)
+{
+ TDB_CONTEXT *pdb_tdb;
+ tdbsamver_t version;
+
+ /* Try to open tdb passwd */
+ if (!(pdb_tdb = tdb_open_log(name, 0, TDB_DEFAULT,
+ open_flags, 0600))) {
+ DEBUG(0, ("Unable to open/create TDB passwd\n"));
+ return NULL;
+ }
+
+ /* Check the version */
+ version = (tdbsamver_t) tdb_fetch_int32(pdb_tdb,
+ TDBSAM_VERSION_STRING);
+ if (version == -1)
+ version = 0; /* Version not found, assume version 0 */
+
+ /* Compare the version */
+ if (version > TDBSAM_VERSION) {
+ /* Version more recent than the latest known */
+ DEBUG(0, ("TDBSAM version unknown: %d\n", version));
+ tdb_close(pdb_tdb);
+ pdb_tdb = NULL;
+ }
+ else if (version < TDBSAM_VERSION) {
+ /* Older version, must be converted */
+ DEBUG(1, ("TDBSAM version too old (%d), trying to convert it.\n", version));
+
+ /* Reopen the pdb file with read-write access if needed */
+ if (!(open_flags & O_RDWR)) {
+ DEBUG(10, ("tdbsam_tdbopen: TDB file opened with read only access, reopen it with read-write access.\n"));
+ tdb_close(pdb_tdb);
+ pdb_tdb = tdb_open_log(name, 0, TDB_DEFAULT, (open_flags & 07777770) | O_RDWR, 0600);
+ }
+
+ /* Convert */
+ if (!tdbsam_convert(pdb_tdb, version)){
+ DEBUG(0, ("tdbsam_tdbopen: Error when trying to convert tdbsam: %s\n",name));
+ tdb_close(pdb_tdb);
+ pdb_tdb = NULL;
+ } else {
+ DEBUG(1, ("TDBSAM converted successfully.\n"));
+ }
+
+ /* Reopen the pdb file as it must be */
+ if (!(open_flags & O_RDWR)) {
+ tdb_close(pdb_tdb);
+ pdb_tdb = tdb_open_log(name, 0, TDB_DEFAULT, open_flags, 0600);
+ }
+ }
+
+ return pdb_tdb;
+}
+
+/*****************************************************************************
+ Utility functions to close the tdb sam database
+ ****************************************************************************/
+
+static void tdbsam_tdbclose ( struct tdbsam_privates *state )
+{
+ if ( !state )
+ return;
+
+ if ( state->passwd_tdb ) {
+ tdb_close( state->passwd_tdb );
+ state->passwd_tdb = NULL;
+ }
+
+ return;
+
+}
+
+/****************************************************************************
+ creates a list of user keys
+****************************************************************************/
+
+static int tdbsam_traverse_setpwent(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void *state)
+{
+ const char *prefix = USERPREFIX;
+ int prefixlen = strlen (prefix);
+ struct pwent_list *ptr;
+
+ if ( strncmp(key.dptr, prefix, prefixlen) == 0 ) {
+ if ( !(ptr=(struct pwent_list*)malloc(sizeof(struct pwent_list))) ) {
+ DEBUG(0,("tdbsam_traverse_setpwent: Failed to malloc new entry for list\n"));
+
+ /* just return 0 and let the traversal continue */
+ return 0;
+ }
+ ZERO_STRUCTP(ptr);
+
+ /* save a copy of the key */
+
+ ptr->key.dptr = memdup( key.dptr, key.dsize );
+ ptr->key.dsize = key.dsize;
+
+ DLIST_ADD( tdbsam_pwent_list, ptr );
+
+ }
+
+
+ return 0;
+}
+
+/***************************************************************
+ Open the TDB passwd database for SAM account enumeration.
+ Save a list of user keys for iteration.
+****************************************************************/
+
+static NTSTATUS tdbsam_setsampwent(struct pdb_methods *my_methods, BOOL update)
+{
+ uint32 flags = update ? (O_RDWR|O_CREAT) : O_RDONLY;
+
+ struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+
+ if ( !(tdb_state->passwd_tdb = tdbsam_tdbopen(tdb_state->tdbsam_location, flags )) )
+ return NT_STATUS_UNSUCCESSFUL;
+
+ tdb_traverse( tdb_state->passwd_tdb, tdbsam_traverse_setpwent, NULL );
+
+ return NT_STATUS_OK;
+}
+
+
+/***************************************************************
+ End enumeration of the TDB passwd list.
+****************************************************************/
+
+static void tdbsam_endsampwent(struct pdb_methods *my_methods)
+{
+ struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+ struct pwent_list *ptr, *ptr_next;
+
+ tdbsam_tdbclose( tdb_state );
+
+ /* clear out any remaining entries in the list */
+
+ for ( ptr=tdbsam_pwent_list; ptr; ptr = ptr_next ) {
+ ptr_next = ptr->next;
+ DLIST_REMOVE( tdbsam_pwent_list, ptr );
+ SAFE_FREE( ptr->key.dptr);
+ SAFE_FREE( ptr );
+ }
+
+ DEBUG(7, ("endtdbpwent: closed sam database.\n"));
+}
+
+/*****************************************************************
+ Get one SAM_ACCOUNT from the TDB (next in line)
+*****************************************************************/
+
+static NTSTATUS tdbsam_getsampwent(struct pdb_methods *my_methods, SAM_ACCOUNT *user)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+ TDB_DATA data;
+ struct pwent_list *pkey;
+
+ if ( !user ) {
+ DEBUG(0,("tdbsam_getsampwent: SAM_ACCOUNT is NULL.\n"));
+ return nt_status;
+ }
+
+ if ( !tdbsam_pwent_list ) {
+ DEBUG(4,("tdbsam_getsampwent: end of list\n"));
+ tdbsam_tdbclose( tdb_state );
+ return nt_status;
+ }
+
+ if ( !tdb_state->passwd_tdb ) {
+ if ( !(tdb_state->passwd_tdb = tdbsam_tdbopen(tdb_state->tdbsam_location, O_RDONLY)) )
+ return nt_status;
+ }
+
+ /* pull the next entry */
+
+ pkey = tdbsam_pwent_list;
+ DLIST_REMOVE( tdbsam_pwent_list, pkey );
+
+ data = tdb_fetch(tdb_state->passwd_tdb, pkey->key);
+
+ SAFE_FREE( pkey->key.dptr);
+ SAFE_FREE( pkey);
+
+ if (!data.dptr) {
+ DEBUG(5,("pdb_getsampwent: database entry not found. Was the user deleted?\n"));
+ return nt_status;
+ }
+
+ if (!init_sam_from_buffer(user, (unsigned char *)data.dptr, data.dsize)) {
+ DEBUG(0,("pdb_getsampwent: Bad SAM_ACCOUNT entry returned from TDB!\n"));
+ }
+
+ SAFE_FREE( data.dptr );
+
+
+ return NT_STATUS_OK;
+}
+
+/******************************************************************
+ Lookup a name in the SAM TDB
+******************************************************************/
+
+static NTSTATUS tdbsam_getsampwnam (struct pdb_methods *my_methods, SAM_ACCOUNT *user, const char *sname)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+ TDB_CONTEXT *pwd_tdb;
+ TDB_DATA data, key;
+ fstring keystr;
+ fstring name;
+
+ if ( !user ) {
+ DEBUG(0,("pdb_getsampwnam: SAM_ACCOUNT is NULL.\n"));
+ return nt_status;
+ }
+
+ /* Data is stored in all lower-case */
+ fstrcpy(name, sname);
+ strlower_m(name);
+
+ /* set search key */
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ /* open the accounts TDB */
+ if (!(pwd_tdb = tdbsam_tdbopen(tdb_state->tdbsam_location, O_RDONLY))) {
+
+ if (errno == ENOENT) {
+ /*
+ * TDB file doesn't exist, so try to create new one. This is useful to avoid
+ * confusing error msg when adding user account first time
+ */
+ if (!(pwd_tdb = tdbsam_tdbopen(tdb_state->tdbsam_location, O_CREAT ))) {
+ DEBUG(0, ("pdb_getsampwnam: TDB passwd (%s) did not exist. File successfully created.\n",
+ tdb_state->tdbsam_location));
+ } else {
+ DEBUG(0, ("pdb_getsampwnam: TDB passwd (%s) does not exist. Couldn't create new one. Error was: %s\n",
+ tdb_state->tdbsam_location, strerror(errno)));
+ }
+
+ /* requested user isn't there anyway */
+ nt_status = NT_STATUS_NO_SUCH_USER;
+ return nt_status;
+ }
+ DEBUG(0, ("pdb_getsampwnam: Unable to open TDB passwd (%s)!\n", tdb_state->tdbsam_location));
+ return nt_status;
+ }
+
+ /* get the record */
+ data = tdb_fetch(pwd_tdb, key);
+ if (!data.dptr) {
+ DEBUG(5,("pdb_getsampwnam (TDB): error fetching database.\n"));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(pwd_tdb)));
+ DEBUGADD(5, (" Key: %s\n", keystr));
+ tdb_close(pwd_tdb);
+ return nt_status;
+ }
+
+ /* unpack the buffer */
+ if (!init_sam_from_buffer(user, (unsigned char *)data.dptr, data.dsize)) {
+ DEBUG(0,("pdb_getsampwent: Bad SAM_ACCOUNT entry returned from TDB!\n"));
+ SAFE_FREE(data.dptr);
+ tdb_close(pwd_tdb);
+ return nt_status;
+ }
+ SAFE_FREE(data.dptr);
+
+ /* no further use for database, close it now */
+ tdb_close(pwd_tdb);
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Search by rid
+ **************************************************************************/
+
+static NTSTATUS tdbsam_getsampwrid (struct pdb_methods *my_methods, SAM_ACCOUNT *user, uint32 rid)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+ TDB_CONTEXT *pwd_tdb;
+ TDB_DATA data, key;
+ fstring keystr;
+ fstring name;
+
+ if (user==NULL) {
+ DEBUG(0,("pdb_getsampwrid: SAM_ACCOUNT is NULL.\n"));
+ return nt_status;
+ }
+
+ /* set search key */
+ slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, rid);
+ key.dptr = keystr;
+ key.dsize = strlen (keystr) + 1;
+
+ /* open the accounts TDB */
+ if (!(pwd_tdb = tdbsam_tdbopen(tdb_state->tdbsam_location, O_RDONLY))) {
+ DEBUG(0, ("pdb_getsampwrid: Unable to open TDB rid database!\n"));
+ return nt_status;
+ }
+
+ /* get the record */
+ data = tdb_fetch (pwd_tdb, key);
+ if (!data.dptr) {
+ DEBUG(5,("pdb_getsampwrid (TDB): error looking up RID %d by key %s.\n", rid, keystr));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(pwd_tdb)));
+ tdb_close (pwd_tdb);
+ return nt_status;
+ }
+
+
+ fstrcpy(name, data.dptr);
+ SAFE_FREE(data.dptr);
+
+ tdb_close (pwd_tdb);
+
+ return tdbsam_getsampwnam (my_methods, user, name);
+}
+
+static NTSTATUS tdbsam_getsampwsid(struct pdb_methods *my_methods, SAM_ACCOUNT * user, const DOM_SID *sid)
+{
+ uint32 rid;
+ if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid))
+ return NT_STATUS_UNSUCCESSFUL;
+ return tdbsam_getsampwrid(my_methods, user, rid);
+}
+
+/***************************************************************************
+ Delete a SAM_ACCOUNT
+****************************************************************************/
+
+static NTSTATUS tdbsam_delete_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT *sam_pass)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+ TDB_CONTEXT *pwd_tdb;
+ TDB_DATA key;
+ fstring keystr;
+ uint32 rid;
+ fstring name;
+
+ fstrcpy(name, pdb_get_username(sam_pass));
+ strlower_m(name);
+
+ /* open the TDB */
+ if (!(pwd_tdb = tdbsam_tdbopen(tdb_state->tdbsam_location, O_RDWR))) {
+ DEBUG(0, ("Unable to open TDB passwd!"));
+ return nt_status;
+ }
+
+ /* set the search key */
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
+ key.dptr = keystr;
+ key.dsize = strlen (keystr) + 1;
+
+ rid = pdb_get_user_rid(sam_pass);
+
+ /* it's outaa here! 8^) */
+ if (tdb_delete(pwd_tdb, key) != TDB_SUCCESS) {
+ DEBUG(5, ("Error deleting entry from tdb passwd database!\n"));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(pwd_tdb)));
+ tdb_close(pwd_tdb);
+ return nt_status;
+ }
+
+ /* delete also the RID key */
+
+ /* set the search key */
+ slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, rid);
+ key.dptr = keystr;
+ key.dsize = strlen (keystr) + 1;
+
+ /* it's outaa here! 8^) */
+ if (tdb_delete(pwd_tdb, key) != TDB_SUCCESS) {
+ DEBUG(5, ("Error deleting entry from tdb rid database!\n"));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(pwd_tdb)));
+ tdb_close(pwd_tdb);
+ return nt_status;
+ }
+
+ tdb_close(pwd_tdb);
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Update the TDB SAM
+****************************************************************************/
+
+static BOOL tdb_update_sam(struct pdb_methods *my_methods, SAM_ACCOUNT* newpwd, int flag)
+{
+ struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+ TDB_CONTEXT *pwd_tdb = NULL;
+ TDB_DATA key, data;
+ uint8 *buf = NULL;
+ fstring keystr;
+ fstring name;
+ BOOL ret = True;
+ uint32 user_rid;
+
+ /* invalidate the existing TDB iterator if it is open */
+
+ if (tdb_state->passwd_tdb) {
+ tdb_close(tdb_state->passwd_tdb);
+ tdb_state->passwd_tdb = NULL;
+ }
+
+ /* open the account TDB passwd*/
+
+ pwd_tdb = tdbsam_tdbopen(tdb_state->tdbsam_location, O_RDWR | O_CREAT);
+
+ if (!pwd_tdb) {
+ DEBUG(0, ("tdb_update_sam: Unable to open TDB passwd (%s)!\n",
+ tdb_state->tdbsam_location));
+ return False;
+ }
+
+ if (!pdb_get_group_rid(newpwd)) {
+ DEBUG (0,("tdb_update_sam: Failing to store a SAM_ACCOUNT for [%s] without a primary group RID\n",
+ pdb_get_username(newpwd)));
+ ret = False;
+ goto done;
+ }
+
+ if ( !(user_rid = pdb_get_user_rid(newpwd)) ) {
+ DEBUG(0,("tdb_update_sam: SAM_ACCOUNT (%s) with no RID!\n", pdb_get_username(newpwd)));
+ ret = False;
+ goto done;
+ }
+
+ /* copy the SAM_ACCOUNT struct into a BYTE buffer for storage */
+ if ((data.dsize=init_buffer_from_sam (&buf, newpwd, False)) == -1) {
+ DEBUG(0,("tdb_update_sam: ERROR - Unable to copy SAM_ACCOUNT info BYTE buffer!\n"));
+ ret = False;
+ goto done;
+ }
+ data.dptr = (char *)buf;
+
+ fstrcpy(name, pdb_get_username(newpwd));
+ strlower_m(name);
+
+ DEBUG(5, ("Storing %saccount %s with RID %d\n", flag == TDB_INSERT ? "(new) " : "", name, user_rid));
+
+ /* setup the USER index key */
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ /* add the account */
+ if (tdb_store(pwd_tdb, key, data, flag) != TDB_SUCCESS) {
+ DEBUG(0, ("Unable to modify passwd TDB!"));
+ DEBUGADD(0, (" Error: %s", tdb_errorstr(pwd_tdb)));
+ DEBUGADD(0, (" occured while storing the main record (%s)\n", keystr));
+ ret = False;
+ goto done;
+ }
+
+ /* setup RID data */
+ data.dsize = strlen(name) + 1;
+ data.dptr = name;
+
+ /* setup the RID index key */
+ slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, user_rid);
+ key.dptr = keystr;
+ key.dsize = strlen (keystr) + 1;
+
+ /* add the reference */
+ if (tdb_store(pwd_tdb, key, data, flag) != TDB_SUCCESS) {
+ DEBUG(0, ("Unable to modify TDB passwd !"));
+ DEBUGADD(0, (" Error: %s\n", tdb_errorstr(pwd_tdb)));
+ DEBUGADD(0, (" occured while storing the RID index (%s)\n", keystr));
+ ret = False;
+ goto done;
+ }
+
+done:
+ /* cleanup */
+ tdb_close (pwd_tdb);
+ SAFE_FREE(buf);
+
+ return (ret);
+}
+
+/***************************************************************************
+ Modifies an existing SAM_ACCOUNT
+****************************************************************************/
+
+static NTSTATUS tdbsam_update_sam_account (struct pdb_methods *my_methods, SAM_ACCOUNT *newpwd)
+{
+ if (tdb_update_sam(my_methods, newpwd, TDB_MODIFY))
+ return NT_STATUS_OK;
+ else
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/***************************************************************************
+ Adds an existing SAM_ACCOUNT
+****************************************************************************/
+
+static NTSTATUS tdbsam_add_sam_account (struct pdb_methods *my_methods, SAM_ACCOUNT *newpwd)
+{
+ if (tdb_update_sam(my_methods, newpwd, TDB_INSERT))
+ return NT_STATUS_OK;
+ else
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+static void free_private_data(void **vp)
+{
+ struct tdbsam_privates **tdb_state = (struct tdbsam_privates **)vp;
+ tdbsam_tdbclose(*tdb_state);
+ *tdb_state = NULL;
+
+ /* No need to free any further, as it is talloc()ed */
+}
+
+/**
+ * Start trust passwords enumeration. This function is a simple
+ * wrapper for calling gettrustpwent with null pointer passed.
+ *
+ * @param methods methods belonging in pdb context (module)
+ * @return nt status of performed operation
+ **/
+
+static NTSTATUS tdbsam_settrustpwent(struct pdb_methods *methods)
+{
+ /* rewind enumeration from beginning */
+ return methods->gettrustpwent(methods, NULL);
+}
+
+
+/**
+ * Enumerate across trust passwords (machine and interdomain nt/ads)
+ *
+ * @param methods methods belonging in pdb context (module)
+ * @param trust trust password structure
+ *
+ * @return nt status of performed operation
+ **/
+
+static NTSTATUS tdbsam_gettrustpwent(struct pdb_methods *methods, SAM_TRUST_PASSWD *trust)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct trust_passwd_data t;
+ TALLOC_CTX *mem_ctx;
+
+ TRUSTDOM **trustdom;
+ static int enum_ctx;
+ int num_domains = 0;
+ unsigned int max_domains = 1;
+ char *dom_name, *dom_pass;
+
+ smb_ucs2_t *uni_dom_name;
+ uint8 mach_pass[16];
+ uint32 sec_chan;
+
+ if (!methods) return NT_STATUS_UNSUCCESSFUL;
+
+ /*
+ * NT domain trust passwords
+ */
+
+ /* rewind enumeration when passed NULL pointer as a trust */
+ if (!trust) {
+ enum_ctx = 0;
+ return NT_STATUS_OK;
+ }
+
+ mem_ctx = talloc_init("tdbsam_gettrustpwent: trust password enumeration");
+
+ /* fetch next trusted domain (one at a time) and its full information */
+ nt_status = secrets_get_trusted_domains(mem_ctx, &enum_ctx, max_domains, &num_domains,
+ &trustdom);
+ if (num_domains) {
+ pull_ucs2_talloc(mem_ctx, &dom_name, trustdom[0]->name);
+ if (secrets_fetch_trusted_domain_password(dom_name, &dom_pass, &t.domain_sid,
+ &t.mod_time)) {
+
+ t.uni_name_len = strnlen_w(trustdom[0]->name, 32);
+ strncpy_w(t.uni_name, trustdom[0]->name, t.uni_name_len);
+ safe_strcpy(t.pass, dom_pass, FSTRING_LEN - 1);
+ t.flags = PASS_DOMAIN_TRUST_NT;
+
+ SAFE_FREE(dom_pass);
+ talloc_destroy(mem_ctx);
+ trust->private = t;
+ return nt_status;
+ } else {
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ /*
+ * NT machine trust password
+ */
+
+ if (secrets_lock_trust_account_password(lp_workgroup(), True)) {
+ sec_chan = get_default_sec_channel();
+ if (secrets_fetch_trust_account_password(lp_workgroup(), mach_pass, &t.mod_time,
+ &sec_chan)) {
+
+ t.uni_name_len = strlen(lp_workgroup());
+ push_ucs2_talloc(mem_ctx, &uni_dom_name, lp_workgroup());
+ strncpy_w(t.uni_name, uni_dom_name, t.uni_name_len);
+ safe_strcpy(t.pass, mach_pass, FSTRING_LEN - 1);
+ t.flags = PASS_MACHINE_TRUST_NT;
+ if (!secrets_fetch_domain_sid(lp_workgroup(), &t.domain_sid)) {
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ talloc_destroy(mem_ctx);
+ trust->private = t;
+ return NT_STATUS_NO_MORE_ENTRIES;
+ }
+ secrets_lock_trust_account_password(lp_workgroup(), False);
+ } else {
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /*
+ * ADS machine trust password (TODO)
+ */
+
+
+ /*
+ * if nothing is to be returned then reset domain name
+ * and return "no more entries"
+ */
+ nt_status = NT_STATUS_NO_MORE_ENTRIES;
+ trust->private.uni_name_len = 0;
+ trust->private.uni_name[t.uni_name_len] = 0;
+
+ talloc_destroy(mem_ctx);
+ return nt_status;
+}
+
+
+/**
+ * Get trust password by trusted party name
+ *
+ * @param methods methods belonging to pdb context (module)
+ * @param trust trust password structure
+ * @param sid trusted party name
+ *
+ * @return nt status of performed operation
+ **/
+
+static NTSTATUS tdbsam_gettrustpwnam(struct pdb_methods *methods, SAM_TRUST_PASSWD *trust,
+ const char *name)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ char domain_name[32];
+
+ if (!methods || !trust || !name) return nt_status;
+
+ do {
+ /* get trust password (next in turn) */
+ nt_status = tdbsam_gettrustpwent(methods, trust);
+
+ /* convert unicode name and do case insensitive compare */
+ pull_ucs2(NULL, domain_name, trust->private.uni_name, sizeof(domain_name),
+ trust->private.uni_name_len, STR_TERMINATE);
+ if (!StrnCaseCmp(domain_name, name, sizeof(domain_name)))
+ return NT_STATUS_OK;
+
+ } while (NT_STATUS_EQUAL(nt_status, STATUS_MORE_ENTRIES));
+
+ return nt_status;
+}
+
+
+/**
+ * Get trust password by trusted party sid
+ *
+ * @param methods methods belonging to pdb context (module)
+ * @param trust trust password structure
+ * @param sid trusted party sid
+ *
+ * @return nt status of performed operation
+ **/
+
+static NTSTATUS tdbsam_gettrustpwsid(struct pdb_methods *methods, SAM_TRUST_PASSWD *trust,
+ const DOM_SID *sid)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+
+ if (!methods || !trust || !sid) return nt_status;
+
+ do {
+ nt_status = tdbsam_gettrustpwent(methods, trust);
+
+ if (sid_equal(&trust->private.domain_sid, sid))
+ return NT_STATUS_OK;
+
+ } while (NT_STATUS_EQUAL(nt_status, STATUS_MORE_ENTRIES));
+
+ return nt_status;
+}
+
+
+/**
+ * Add new trust password.
+ *
+ * @param methods methods belonging in pdb context (module)
+ * @param trust trust password structure
+ *
+ * @return nt status of performed operation
+ **/
+
+static NTSTATUS tdbsam_add_trust_passwd(struct pdb_methods *methods, const SAM_TRUST_PASSWD *trust)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ BOOL status = False;
+ TALLOC_CTX *mem_ctx;
+
+ char* domain = NULL;
+ struct trust_passwd_data t = trust->private;
+ uint32 sec_chan;
+
+ mem_ctx = talloc_init("tdbsam_add_trust_passwd: storing new trust password");
+
+ /* convert unicode name to char* (used to form the key) */
+ pull_ucs2_talloc(mem_ctx, &domain, t.uni_name);
+
+ /* add nt machine trust password */
+ if (t.flags & (PASS_MACHINE_TRUST_NT | PASS_SERVER_TRUST_NT)) {
+ sec_chan = (t.flags & PASS_MACHINE_TRUST_NT) ? SEC_CHAN_WKSTA : SEC_CHAN_BDC;
+ status = secrets_store_machine_password(t.pass, domain, sec_chan);
+ if (status)
+ status = secrets_store_domain_sid(domain, &t.domain_sid);
+
+ nt_status = status ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+
+ /* add nt domain trust password */
+ } else if (t.flags & PASS_DOMAIN_TRUST_NT) {
+ status = secrets_store_trusted_domain_password(domain, t.uni_name, t.uni_name_len,
+ t.pass, t.domain_sid);
+ nt_status = status ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+
+ /* add ads machine trust password (TODO) */
+ } else if (t.flags & PASS_MACHINE_TRUST_ADS) {
+ }
+
+ talloc_destroy(mem_ctx);
+ return nt_status;
+}
+
+
+/**
+ * Update trust password.
+ *
+ * @param methods methods belonging in pdb context (module)
+ * @param trust trust password structure
+ *
+ * @return nt status of performed operation
+ **/
+
+static NTSTATUS tdbsam_update_trust_passwd(struct pdb_methods *methods, const SAM_TRUST_PASSWD* trust)
+{
+ NTSTATUS nt_status = NT_STATUS_NOT_IMPLEMENTED;
+ return nt_status;
+}
+
+
+/**
+ * Delete trust password.
+ *
+ * @param methods methods belonging in pdb context (module)
+ * @param trust trust password structure
+ *
+ * @return nt status of performed operation
+ **/
+
+static NTSTATUS tdbsam_delete_trust_passwd(struct pdb_methods *methods, const SAM_TRUST_PASSWD* trust)
+{
+ NTSTATUS nt_status = NT_STATUS_NOT_IMPLEMENTED;
+ return nt_status;
+}
+
+
+/***************************************************************************
+ Add sid to privilege
+****************************************************************************/
+
+static NTSTATUS tdbsam_add_sid_to_privilege(struct pdb_methods *my_methods, const char *priv_name, const DOM_SID *sid)
+{
+ struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+ TDB_CONTEXT *pwd_tdb = NULL;
+ TDB_DATA key, data;
+ fstring keystr;
+ fstring name;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ fstring sid_str;
+ char *sid_list = NULL, *s = NULL;
+ size_t str_size;
+ int flag;
+
+ /* invalidate the existing TDB iterator if it is open */
+
+ if (tdb_state->passwd_tdb) {
+ tdb_close(tdb_state->passwd_tdb);
+ tdb_state->passwd_tdb = NULL;
+ }
+
+ /* open the account TDB passwd*/
+
+ pwd_tdb = tdbsam_tdbopen(tdb_state->tdbsam_location, O_RDWR | O_CREAT);
+
+ if (!pwd_tdb) {
+ DEBUG(0, ("tdb_add_sid_to_privilege: Unable to open TDB passwd (%s)!\n",
+ tdb_state->tdbsam_location));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* setup the PRIV index key */
+ fstrcpy(name, priv_name);
+ strlower_m(name);
+
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", PRIVPREFIX, name);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ /* check if the privilege already exist in the database */
+
+ /* get the record */
+ data = tdb_fetch (pwd_tdb, key);
+
+ if (data.dptr) {
+ /* check the list is not empty */
+ if (*(data.dptr)) {
+ sid_list = strdup(data.dptr);
+ if (!sid_list) {
+ DEBUG(0, ("tdbsam_add_sid_to_privilege: Out of Memory!\n"));
+ goto done;
+ }
+ }
+ SAFE_FREE(data.dptr);
+
+ flag = TDB_MODIFY;
+ } else {
+ /* if privilege does not exist create one */
+ flag = TDB_INSERT;
+ }
+
+ /* add the given sid */
+ sid_to_string(sid_str, sid);
+
+ if (sid_list) {
+ str_size = strlen(sid_list) + strlen(sid_str) + 2;
+ s = realloc(sid_list, str_size);
+ if (!s) {
+ DEBUG(0, ("tdbsam_add_sid_to_privilege: Out of Memory!\n"));
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ sid_list = s;
+ s = &sid_list[strlen(sid_list)];
+ snprintf(s, strlen(sid_str) + 2, ",%s", sid_str);
+
+ } else {
+ sid_list = strdup(sid_str);
+ if (!sid_list) {
+ DEBUG(0, ("tdbsam_add_sid_to_privilege: Out of Memory!\n"));
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ }
+
+ /* copy the PRIVILEGE struct into a BYTE buffer for storage */
+ data.dsize = strlen(sid_list) + 1;
+ data.dptr = sid_list;
+
+ /* add the account */
+ if (tdb_store(pwd_tdb, key, data, flag) != TDB_SUCCESS) {
+ DEBUG(0, ("Unable to modify passwd TDB!"));
+ DEBUGADD(0, (" Error: %s", tdb_errorstr(pwd_tdb)));
+ DEBUGADD(0, (" occured while storing the main record (%s)\n", keystr));
+ goto done;
+ }
+
+ ret = NT_STATUS_OK;
+
+done:
+ /* cleanup */
+ tdb_close (pwd_tdb);
+ SAFE_FREE(sid_list);
+
+ return (ret);
+}
+
+/***************************************************************************
+ Reomve sid to privilege
+****************************************************************************/
+
+static NTSTATUS tdbsam_remove_sid_from_privilege(struct pdb_methods *my_methods, const char *priv_name, const DOM_SID *sid)
+{
+ struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+ TDB_CONTEXT *pwd_tdb = NULL;
+ TDB_DATA key, data;
+ fstring keystr;
+ fstring name;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ fstring sid_str;
+ char *sid_list = NULL, *s = NULL;
+
+ /* invalidate the existing TDB iterator if it is open */
+
+ if (tdb_state->passwd_tdb) {
+ tdb_close(tdb_state->passwd_tdb);
+ tdb_state->passwd_tdb = NULL;
+ }
+
+ /* open the account TDB passwd*/
+
+ pwd_tdb = tdbsam_tdbopen(tdb_state->tdbsam_location, O_RDWR | O_CREAT);
+
+ if (!pwd_tdb) {
+ DEBUG(0, ("tdbsam_remove_sid_from_privilege: Unable to open TDB passwd (%s)!\n",
+ tdb_state->tdbsam_location));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* setup the PRIV index key */
+ fstrcpy(name, priv_name);
+ strlower_m(name);
+
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", PRIVPREFIX, name);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ /* check if the privilege already exist in the database */
+
+ /* get the record */
+ data = tdb_fetch (pwd_tdb, key);
+
+ /* if privilege does not exist, just leave */
+ if (!data.dptr) {
+ ret = NT_STATUS_OK;
+ goto done;
+ }
+
+ if (data.dptr) {
+ sid_list = strdup(data.dptr);
+ if (!sid_list) {
+ DEBUG(0, ("tdbsam_remove_sid_from_privilege: Out of Memory!\n"));
+ goto done;
+ }
+ SAFE_FREE(data.dptr);
+ }
+
+ /* remove the given sid */
+ sid_to_string(sid_str, sid);
+
+ s = strstr(sid_list, sid_str);
+ if (s) {
+ char *p;
+ p = strstr(s, ",");
+ if (p) {
+ size_t l = strlen(sid_list) + 1 - (s - sid_list);
+ memmove(s, ++p, l);
+ } else {
+ if (s != sid_list)
+ s--;
+ *s = '\0';
+ }
+ } else {
+ /* sid not found */
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* copy the PRIVILEGE struct into a BYTE buffer for storage */
+ data.dsize = strlen(sid_list) + 1;
+ data.dptr = sid_list;
+
+ /* add the account */
+ if (tdb_store(pwd_tdb, key, data, TDB_MODIFY) != TDB_SUCCESS) {
+ DEBUG(0, ("Unable to modify passwd TDB!"));
+ DEBUGADD(0, (" Error: %s", tdb_errorstr(pwd_tdb)));
+ DEBUGADD(0, (" occured while storing the main record (%s)\n", keystr));
+ goto done;
+ }
+
+ ret = NT_STATUS_OK;
+
+done:
+ /* cleanup */
+ tdb_close (pwd_tdb);
+ SAFE_FREE(sid_list);
+
+ return (ret);
+}
+
+/***************************************************************************
+ get the privilege list for the given token
+****************************************************************************/
+
+struct priv_traverse {
+ char **sid_list;
+ PRIVILEGE_SET *privset;
+};
+
+static int tdbsam_traverse_privilege(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void *state)
+{
+ struct priv_traverse *pt = (struct priv_traverse *)state;
+ int prefixlen = strlen(PRIVPREFIX);
+
+ if (strncmp(key.dptr, PRIVPREFIX, prefixlen) == 0) {
+
+ /* add to privilege_set if any of the sid in the token
+ * is contained in the privilege */
+ int i;
+
+ for(i=0; pt->sid_list[i] != NULL; i++) {
+ char *c, *s;
+ int len;
+
+ s = data.dptr;
+ while ((c=strchr(s, ',')) !=NULL) {
+ len = MAX((c - s), strlen(pt->sid_list[i]));
+ if (strncmp(s, pt->sid_list[i], len) == 0) {
+ DEBUG(10, ("sid [%s] found in users sid list\n", pt->sid_list[i]));
+ DEBUG(10, ("adding privilege [%s] to the users privilege list\n", &(key.dptr[prefixlen])));
+ add_privilege_by_name(pt->privset, &(key.dptr[prefixlen]));
+ return 0;
+ }
+ s = c + 1;
+ }
+ len = MAX(strlen(s), strlen(pt->sid_list[i]));
+ if (strncmp(s, pt->sid_list[i], len) == 0) {
+ DEBUG(10, ("sid [%s] found in users sid list\n", pt->sid_list[i]));
+ DEBUG(10, ("adding privilege [%s] to the users privilege list\n", &(key.dptr[prefixlen])));
+ add_privilege_by_name(pt->privset, &(key.dptr[prefixlen]));
+ return 0;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static NTSTATUS tdbsam_get_privilege_set(struct pdb_methods *my_methods, DOM_SID *user_sids, int num_sids, PRIVILEGE_SET *privset)
+{
+ struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ TDB_CONTEXT *pwd_tdb = NULL;
+ struct priv_traverse pt;
+ fstring sid_str;
+ char **sid_list;
+ int i;
+
+ if (!(pwd_tdb = tdbsam_tdbopen(tdb_state->tdbsam_location, O_RDONLY )))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ sid_list = (char **)malloc(sizeof(char *) * (num_sids + 1));
+ for (i = 0; i < num_sids; i++) {
+ sid_to_string(sid_str, &user_sids[i]);
+ sid_list[i] = strdup(sid_str);
+ if ( ! sid_list[i]) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ }
+ sid_list[i] = NULL;
+
+ pt.sid_list = sid_list;
+ pt.privset = privset;
+ tdb_traverse(pwd_tdb, tdbsam_traverse_privilege, &pt);
+
+ ret = NT_STATUS_OK;
+
+done:
+ i = 0;
+ while (sid_list[i]) {
+ free(sid_list[i]);
+ i++;
+ }
+ free(sid_list);
+
+ tdb_close(pwd_tdb);
+
+ return ret;
+}
+
+static NTSTATUS tdbsam_get_privilege_entry(struct pdb_methods *my_methods, const char *privname, char **sid_list)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ TDB_CONTEXT *pwd_tdb = NULL;
+ TDB_DATA key, data;
+ fstring name;
+ fstring keystr;
+
+ struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+
+ if (!(pwd_tdb = tdbsam_tdbopen(tdb_state->tdbsam_location, O_RDONLY)))
+ return ret;
+
+ /* setup the PRIV index key */
+ fstrcpy(name, privname);
+ strlower_m(name);
+
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", PRIVPREFIX, name);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ data = tdb_fetch(pwd_tdb, key);
+ if (!data.dptr)
+ goto done;
+
+ *sid_list = strdup(data.dptr);
+ SAFE_FREE(data.dptr);
+
+ if (!*sid_list)
+ goto done;
+
+ ret = NT_STATUS_OK;
+done:
+ tdb_close(pwd_tdb);
+ return ret;
+}
+
+
+
+
+
+
+
+static NTSTATUS pdb_init_tdbsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
+{
+ NTSTATUS nt_status;
+ struct tdbsam_privates *tdb_state;
+
+ if (!NT_STATUS_IS_OK(nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
+ return nt_status;
+ }
+
+ (*pdb_method)->name = "tdbsam";
+
+ (*pdb_method)->setsampwent = tdbsam_setsampwent;
+ (*pdb_method)->endsampwent = tdbsam_endsampwent;
+ (*pdb_method)->getsampwent = tdbsam_getsampwent;
+ (*pdb_method)->getsampwnam = tdbsam_getsampwnam;
+ (*pdb_method)->getsampwsid = tdbsam_getsampwsid;
+ (*pdb_method)->add_sam_account = tdbsam_add_sam_account;
+ (*pdb_method)->update_sam_account = tdbsam_update_sam_account;
+ (*pdb_method)->delete_sam_account = tdbsam_delete_sam_account;
+ (*pdb_method)->settrustpwent = tdbsam_settrustpwent;
+ (*pdb_method)->gettrustpwent = tdbsam_gettrustpwent;
+ (*pdb_method)->gettrustpwnam = tdbsam_gettrustpwnam;
+ (*pdb_method)->gettrustpwsid = tdbsam_gettrustpwsid;
+ (*pdb_method)->add_trust_passwd = tdbsam_add_trust_passwd;
+ (*pdb_method)->update_trust_passwd = tdbsam_update_trust_passwd;
+ (*pdb_method)->delete_trust_passwd = tdbsam_delete_trust_passwd;
+ (*pdb_method)->add_sid_to_privilege = tdbsam_add_sid_to_privilege;
+ (*pdb_method)->remove_sid_from_privilege = tdbsam_remove_sid_from_privilege;
+ (*pdb_method)->get_privilege_set = tdbsam_get_privilege_set;
+ (*pdb_method)->get_privilege_entry = tdbsam_get_privilege_entry;
+
+ tdb_state = talloc_zero(pdb_context->mem_ctx, sizeof(struct tdbsam_privates));
+
+ if (!tdb_state) {
+ DEBUG(0, ("talloc() failed for tdbsam private_data!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (location) {
+ tdb_state->tdbsam_location = talloc_strdup(pdb_context->mem_ctx, location);
+ } else {
+ pstring tdbfile;
+ get_private_directory(tdbfile);
+ pstrcat(tdbfile, "/");
+ pstrcat(tdbfile, PASSDB_FILE_NAME);
+ tdb_state->tdbsam_location = talloc_strdup(pdb_context->mem_ctx, tdbfile);
+ }
+
+ (*pdb_method)->private_data = tdb_state;
+
+ (*pdb_method)->free_private_data = free_private_data;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_tdbsam_init(void)
+{
+ return smb_register_passdb(PASSDB_INTERFACE_VERSION, "tdbsam", pdb_init_tdbsam);
+}
diff --git a/source/passdb/pdb_xml.c b/source/passdb/pdb_xml.c
new file mode 100644
index 00000000000..2738ad40e2a
--- /dev/null
+++ b/source/passdb/pdb_xml.c
@@ -0,0 +1,563 @@
+
+/*
+ * XML password backend for samba
+ * Copyright (C) Jelmer Vernooij 2002
+ * Some parts based on the libxml gjobread example by Daniel Veillard
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 675
+ * Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* FIXME:
+ * - Support stdin input by using '-'
+ * - Be faster. Don't rewrite the whole file when adding a user, but store it in the memory and save it when exiting. Requires changes to samba source.
+ * - Gives the ability to read/write to standard input/output
+ * - Do locking!
+ * - Better names!
+ */
+
+
+#define XML_URL "http://samba.org/~jelmer/sambapdb.dtd"
+
+#include "includes.h"
+
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+
+static int xmlsam_debug_level = DBGC_ALL;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS xmlsam_debug_level
+
+static char * iota(int a) {
+ static char tmp[10];
+
+ snprintf(tmp, 9, "%d", a);
+ return tmp;
+}
+
+static BOOL parsePass(xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur, SAM_ACCOUNT * u)
+{
+ pstring temp;
+
+ cur = cur->xmlChildrenNode;
+ while (cur != NULL) {
+ if (strcmp(cur->name, "crypt"))
+ DEBUG(0, ("Unknown element %s\n", cur->name));
+ else {
+ if (!strcmp(xmlGetProp(cur, "type"), "nt")
+ &&
+ pdb_gethexpwd(xmlNodeListGetString
+ (doc, cur->xmlChildrenNode, 1), temp))
+ pdb_set_nt_passwd(u, temp, PDB_SET);
+ else if (!strcmp(xmlGetProp(cur, "type"), "lanman")
+ &&
+ pdb_gethexpwd(xmlNodeListGetString
+ (doc, cur->xmlChildrenNode, 1), temp))
+ pdb_set_lanman_passwd(u, temp, PDB_SET);
+ else
+ DEBUG(0,
+ ("Unknown crypt type: %s\n",
+ xmlGetProp(cur, "type")));
+ }
+ cur = cur->next;
+ }
+ return True;
+}
+
+static BOOL parseUser(xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur, SAM_ACCOUNT * u)
+{
+ char *tmp;
+ DOM_SID sid;
+
+ tmp = xmlGetProp(cur, "sid");
+ if (tmp){
+ string_to_sid(&sid, tmp);
+ pdb_set_user_sid(u, &sid, PDB_SET);
+ }
+ pdb_set_username(u, xmlGetProp(cur, "name"), PDB_SET);
+ /* We don't care what the top level element name is */
+ cur = cur->xmlChildrenNode;
+ while (cur != NULL) {
+ if ((!strcmp(cur->name, "group")) && (cur->ns == ns)) {
+ tmp = xmlGetProp(cur, "sid");
+ if (tmp){
+ string_to_sid(&sid, tmp);
+ pdb_set_group_sid(u, &sid, PDB_SET);
+ }
+ }
+
+ else if ((!strcmp(cur->name, "domain")) && (cur->ns == ns))
+ pdb_set_domain(u,
+ xmlNodeListGetString(doc, cur->xmlChildrenNode,
+ 1), PDB_SET);
+
+ else if (!strcmp(cur->name, "fullname") && cur->ns == ns)
+ pdb_set_fullname(u,
+ xmlNodeListGetString(doc,
+ cur->xmlChildrenNode,
+ 1), PDB_SET);
+
+ else if (!strcmp(cur->name, "nt_username") && cur->ns == ns)
+ pdb_set_nt_username(u,
+ xmlNodeListGetString(doc,
+ cur->xmlChildrenNode,
+ 1), PDB_SET);
+
+ else if (!strcmp(cur->name, "logon_script") && cur->ns == ns)
+ pdb_set_logon_script(u,
+ xmlNodeListGetString(doc,
+ cur->xmlChildrenNode,
+ 1), PDB_SET);
+
+ else if (!strcmp(cur->name, "profile_path") && cur->ns == ns)
+ pdb_set_profile_path(u,
+ xmlNodeListGetString(doc,
+ cur->xmlChildrenNode,
+ 1), PDB_SET);
+
+ else if (!strcmp(cur->name, "logon_time") && cur->ns == ns)
+ pdb_set_logon_time(u,
+ atol(xmlNodeListGetString
+ (doc, cur->xmlChildrenNode, 1)), PDB_SET);
+
+ else if (!strcmp(cur->name, "logoff_time") && cur->ns == ns)
+ pdb_set_logoff_time(u,
+ atol(xmlNodeListGetString
+ (doc, cur->xmlChildrenNode, 1)),
+ PDB_SET);
+
+ else if (!strcmp(cur->name, "kickoff_time") && cur->ns == ns)
+ pdb_set_kickoff_time(u,
+ atol(xmlNodeListGetString
+ (doc, cur->xmlChildrenNode, 1)),
+ PDB_SET);
+
+ else if (!strcmp(cur->name, "logon_divs") && cur->ns == ns)
+ pdb_set_logon_divs(u,
+ atol(xmlNodeListGetString
+ (doc, cur->xmlChildrenNode, 1)), PDB_SET);
+
+ else if (!strcmp(cur->name, "hours_len") && cur->ns == ns)
+ pdb_set_hours_len(u,
+ atol(xmlNodeListGetString
+ (doc, cur->xmlChildrenNode, 1)), PDB_SET);
+
+ else if (!strcmp(cur->name, "bad_password_count") && cur->ns == ns)
+ pdb_set_bad_password_count(u,
+ atol(xmlNodeListGetString
+ (doc, cur->xmlChildrenNode, 1)), PDB_SET);
+
+ else if (!strcmp(cur->name, "logon_count") && cur->ns == ns)
+ pdb_set_logon_count(u,
+ atol(xmlNodeListGetString
+ (doc, cur->xmlChildrenNode, 1)), PDB_SET);
+
+ else if (!strcmp(cur->name, "unknown_6") && cur->ns == ns)
+ pdb_set_unknown_6(u,
+ atol(xmlNodeListGetString
+ (doc, cur->xmlChildrenNode, 1)), PDB_SET);
+
+ else if (!strcmp(cur->name, "homedir") && cur->ns == ns)
+ pdb_set_homedir(u,
+ xmlNodeListGetString(doc, cur->xmlChildrenNode,
+ 1), PDB_SET);
+
+ else if (!strcmp(cur->name, "unknown_str") && cur->ns == ns)
+ pdb_set_unknown_str(u,
+ xmlNodeListGetString(doc,
+ cur->xmlChildrenNode,
+ 1), PDB_SET);
+
+ else if (!strcmp(cur->name, "dir_drive") && cur->ns == ns)
+ pdb_set_dir_drive(u,
+ xmlNodeListGetString(doc,
+ cur->xmlChildrenNode,
+ 1), PDB_SET);
+
+ else if (!strcmp(cur->name, "munged_dial") && cur->ns == ns)
+ pdb_set_munged_dial(u,
+ xmlNodeListGetString(doc,
+ cur->xmlChildrenNode,
+ 1), PDB_SET);
+
+ else if (!strcmp(cur->name, "acct_desc") && cur->ns == ns)
+ pdb_set_acct_desc(u,
+ xmlNodeListGetString(doc,
+ cur->xmlChildrenNode,
+ 1), PDB_SET);
+
+ else if (!strcmp(cur->name, "acct_ctrl") && cur->ns == ns)
+ pdb_set_acct_ctrl(u,
+ atol(xmlNodeListGetString
+ (doc, cur->xmlChildrenNode, 1)), PDB_SET);
+
+ else if (!strcmp(cur->name, "workstations") && cur->ns == ns)
+ pdb_set_workstations(u,
+ xmlNodeListGetString(doc,
+ cur->xmlChildrenNode,
+ 1), PDB_SET);
+
+ else if ((!strcmp(cur->name, "password")) && (cur->ns == ns)) {
+ tmp = xmlGetProp(cur, "last_set");
+ if (tmp)
+ pdb_set_pass_last_set_time(u, atol(tmp), PDB_SET);
+ tmp = xmlGetProp(cur, "must_change");
+ if (tmp)
+ pdb_set_pass_must_change_time(u, atol(tmp), PDB_SET);
+ tmp = xmlGetProp(cur, "can_change");
+ if (tmp)
+ pdb_set_pass_can_change_time(u, atol(tmp), PDB_SET);
+ parsePass(doc, ns, cur, u);
+ }
+
+ else
+ DEBUG(0, ("Unknown element %s\n", cur->name));
+ cur = cur->next;
+ }
+
+ return True;
+}
+
+typedef struct pdb_xml {
+ char *location;
+ char written;
+ xmlDocPtr doc;
+ xmlNodePtr users;
+ xmlNodePtr pwent;
+ xmlNsPtr ns;
+} pdb_xml;
+
+static xmlNodePtr parseSambaXMLFile(struct pdb_xml *data)
+{
+ xmlNodePtr cur;
+
+ data->doc = xmlParseFile(data->location);
+ if (data->doc == NULL)
+ return NULL;
+
+ cur = xmlDocGetRootElement(data->doc);
+ if (!cur) {
+ DEBUG(0, ("empty document\n"));
+ xmlFreeDoc(data->doc);
+ return NULL;
+ }
+ data->ns = xmlSearchNsByHref(data->doc, cur, XML_URL);
+ if (!data->ns) {
+ DEBUG(0,
+ ("document of the wrong type, samba user namespace not found\n"));
+ xmlFreeDoc(data->doc);
+ return NULL;
+ }
+ if (strcmp(cur->name, "samba")) {
+ DEBUG(0, ("document of the wrong type, root node != samba"));
+ xmlFreeDoc(data->doc);
+ return NULL;
+ }
+
+ cur = cur->xmlChildrenNode;
+ while (cur && xmlIsBlankNode(cur)) {
+ cur = cur->next;
+ }
+ if (!cur)
+ return NULL;
+ if ((strcmp(cur->name, "users")) || (cur->ns != data->ns)) {
+ DEBUG(0, ("document of the wrong type, was '%s', users expected",
+ cur->name));
+ DEBUG(0, ("xmlDocDump follows\n"));
+ xmlDocDump(stderr, data->doc);
+ DEBUG(0, ("xmlDocDump finished\n"));
+ xmlFreeDoc(data->doc);
+ return NULL;
+ }
+ data->users = cur;
+ cur = cur->xmlChildrenNode;
+ return cur;
+}
+
+static NTSTATUS xmlsam_setsampwent(struct pdb_methods *methods, BOOL update)
+{
+ pdb_xml *data;
+
+ if (!methods) {
+ DEBUG(0, ("Invalid methods\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ data = (pdb_xml *) methods->private_data;
+ if (!data) {
+ DEBUG(0, ("Invalid pdb_xml_data\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ data->pwent = parseSambaXMLFile(data);
+ if (!data->pwent)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************
+ End enumeration of the passwd list.
+ ****************************************************************/
+
+static void xmlsam_endsampwent(struct pdb_methods *methods)
+{
+ pdb_xml *data;
+
+ if (!methods) {
+ DEBUG(0, ("Invalid methods\n"));
+ return;
+ }
+
+ data = (pdb_xml *) methods->private_data;
+
+ if (!data) {
+ DEBUG(0, ("Invalid pdb_xml_data\n"));
+ return;
+ }
+
+ xmlFreeDoc(data->doc);
+ data->doc = NULL;
+ data->pwent = NULL;
+}
+
+/*****************************************************************
+ Get one SAM_ACCOUNT from the list (next in line)
+ *****************************************************************/
+
+static NTSTATUS xmlsam_getsampwent(struct pdb_methods *methods, SAM_ACCOUNT * user)
+{
+ pdb_xml *data;
+
+ if (!methods) {
+ DEBUG(0, ("Invalid methods\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ data = (pdb_xml *) methods->private_data;
+
+ if (!data) {
+ DEBUG(0, ("Invalid pdb_xml_data\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ while (data->pwent) {
+ if ((!strcmp(data->pwent->name, "user")) &&
+ (data->pwent->ns == data->ns)) {
+
+ parseUser(data->doc, data->ns, data->pwent, user);
+ data->pwent = data->pwent->next;
+ return NT_STATUS_OK;
+ }
+ data->pwent = data->pwent->next;
+ }
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/***************************************************************************
+ Adds an existing SAM_ACCOUNT
+ ****************************************************************************/
+
+static NTSTATUS xmlsam_add_sam_account(struct pdb_methods *methods, SAM_ACCOUNT * u)
+{
+ pstring temp;
+ fstring sid_str;
+ xmlNodePtr cur, user, pass, root;
+ pdb_xml *data;
+
+ DEBUG(10, ("xmlsam_add_sam_account called!\n"));
+
+ if (!methods) {
+ DEBUG(0, ("Invalid methods\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ data = (pdb_xml *) methods->private_data;
+ if (!data) {
+ DEBUG(0, ("Invalid pdb_xml_data\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* Create a new document if we can't open the current one */
+ if (!parseSambaXMLFile(data)) {
+ DEBUG(0, ("Can't load current XML file, creating a new one\n"));
+ data->doc = xmlNewDoc(XML_DEFAULT_VERSION);
+ root = xmlNewDocNode(data->doc, NULL, "samba", NULL);
+ cur = xmlDocSetRootElement(data->doc, root);
+ data->ns = xmlNewNs(root, XML_URL, "samba");
+ data->users = xmlNewChild(root, data->ns, "users", NULL);
+ }
+
+ user = xmlNewChild(data->users, data->ns, "user", NULL);
+ xmlNewProp(user, "sid",
+ sid_to_string(sid_str, pdb_get_user_sid(u)));
+
+ if (pdb_get_username(u) && strcmp(pdb_get_username(u), ""))
+ xmlNewProp(user, "name", pdb_get_username(u));
+
+ cur = xmlNewChild(user, data->ns, "group", NULL);
+
+ xmlNewProp(cur, "sid",
+ sid_to_string(sid_str, pdb_get_group_sid(u)));
+
+ if (pdb_get_init_flags(u, PDB_LOGONTIME) != PDB_DEFAULT)
+ xmlNewChild(user, data->ns, "logon_time",
+ iota(pdb_get_logon_time(u)));
+
+ if (pdb_get_init_flags(u, PDB_LOGOFFTIME) != PDB_DEFAULT)
+ xmlNewChild(user, data->ns, "logoff_time",
+ iota(pdb_get_logoff_time(u)));
+
+ if (pdb_get_init_flags(u, PDB_KICKOFFTIME) != PDB_DEFAULT)
+ xmlNewChild(user, data->ns, "kickoff_time",
+ iota(pdb_get_kickoff_time(u)));
+
+ if (pdb_get_domain(u) && strcmp(pdb_get_domain(u), ""))
+ xmlNewChild(user, data->ns, "domain", pdb_get_domain(u));
+
+ if (pdb_get_nt_username(u) && strcmp(pdb_get_nt_username(u), ""))
+ xmlNewChild(user, data->ns, "nt_username", pdb_get_nt_username(u));
+
+ if (pdb_get_fullname(u) && strcmp(pdb_get_fullname(u), ""))
+ xmlNewChild(user, data->ns, "fullname", pdb_get_fullname(u));
+
+ if (pdb_get_homedir(u) && strcmp(pdb_get_homedir(u), ""))
+ xmlNewChild(user, data->ns, "homedir", pdb_get_homedir(u));
+
+ if (pdb_get_dir_drive(u) && strcmp(pdb_get_dir_drive(u), ""))
+ xmlNewChild(user, data->ns, "dir_drive", pdb_get_dir_drive(u));
+
+ if (pdb_get_logon_script(u) && strcmp(pdb_get_logon_script(u), ""))
+ xmlNewChild(user, data->ns, "logon_script",
+ pdb_get_logon_script(u));
+
+ if (pdb_get_profile_path(u) && strcmp(pdb_get_profile_path(u), ""))
+ xmlNewChild(user, data->ns, "profile_path",
+ pdb_get_profile_path(u));
+
+ if (pdb_get_acct_desc(u) && strcmp(pdb_get_acct_desc(u), ""))
+ xmlNewChild(user, data->ns, "acct_desc", pdb_get_acct_desc(u));
+
+ if (pdb_get_workstations(u) && strcmp(pdb_get_workstations(u), ""))
+ xmlNewChild(user, data->ns, "workstations",
+ pdb_get_workstations(u));
+
+ if (pdb_get_unknown_str(u) && strcmp(pdb_get_unknown_str(u), ""))
+ xmlNewChild(user, data->ns, "unknown_str", pdb_get_unknown_str(u));
+
+ if (pdb_get_munged_dial(u) && strcmp(pdb_get_munged_dial(u), ""))
+ xmlNewChild(user, data->ns, "munged_dial", pdb_get_munged_dial(u));
+
+
+ /* Password stuff */
+ pass = xmlNewChild(user, data->ns, "password", NULL);
+ if (pdb_get_pass_last_set_time(u))
+ xmlNewProp(pass, "last_set", iota(pdb_get_pass_last_set_time(u)));
+ if (pdb_get_init_flags(u, PDB_CANCHANGETIME) != PDB_DEFAULT)
+ xmlNewProp(pass, "can_change",
+ iota(pdb_get_pass_can_change_time(u)));
+
+ if (pdb_get_init_flags(u, PDB_MUSTCHANGETIME) != PDB_DEFAULT)
+ xmlNewProp(pass, "must_change",
+ iota(pdb_get_pass_must_change_time(u)));
+
+
+ if (pdb_get_lanman_passwd(u)) {
+ pdb_sethexpwd(temp, pdb_get_lanman_passwd(u),
+ pdb_get_acct_ctrl(u));
+ cur = xmlNewChild(pass, data->ns, "crypt", temp);
+ xmlNewProp(cur, "type", "lanman");
+ }
+
+ if (pdb_get_nt_passwd(u)) {
+ pdb_sethexpwd(temp, pdb_get_nt_passwd(u), pdb_get_acct_ctrl(u));
+ cur = xmlNewChild(pass, data->ns, "crypt", temp);
+ xmlNewProp(cur, "type", "nt");
+ }
+
+ xmlNewChild(user, data->ns, "acct_ctrl", iota(pdb_get_acct_ctrl(u)));
+
+ if (pdb_get_logon_divs(u))
+ xmlNewChild(user, data->ns, "logon_divs",
+ iota(pdb_get_logon_divs(u)));
+
+ if (pdb_get_hours_len(u))
+ xmlNewChild(user, data->ns, "hours_len",
+ iota(pdb_get_hours_len(u)));
+
+ xmlNewChild(user, data->ns, "bad_password_count", iota(pdb_get_bad_password_count(u)));
+ xmlNewChild(user, data->ns, "logon_count", iota(pdb_get_logon_count(u)));
+ xmlNewChild(user, data->ns, "unknown_6", iota(pdb_get_unknown_6(u)));
+ xmlSaveFile(data->location, data->doc);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS xmlsam_init(PDB_CONTEXT * pdb_context, PDB_METHODS ** pdb_method,
+ const char *location)
+{
+ NTSTATUS nt_status;
+ pdb_xml *data;
+
+ xmlsam_debug_level = debug_add_class("xmlsam");
+ if (xmlsam_debug_level == -1) {
+ xmlsam_debug_level = DBGC_ALL;
+ DEBUG(0, ("xmlsam: Couldn't register custom debugging class!\n"));
+ }
+
+ if (!pdb_context) {
+ DEBUG(0, ("invalid pdb_methods specified\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!NT_STATUS_IS_OK
+ (nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
+ return nt_status;
+ }
+
+ (*pdb_method)->name = "xmlsam";
+
+ (*pdb_method)->setsampwent = xmlsam_setsampwent;
+ (*pdb_method)->endsampwent = xmlsam_endsampwent;
+ (*pdb_method)->getsampwent = xmlsam_getsampwent;
+ (*pdb_method)->add_sam_account = xmlsam_add_sam_account;
+ (*pdb_method)->getsampwnam = NULL;
+ (*pdb_method)->getsampwsid = NULL;
+ (*pdb_method)->update_sam_account = NULL;
+ (*pdb_method)->delete_sam_account = NULL;
+ (*pdb_method)->get_group_info_by_sid = NULL;
+ (*pdb_method)->get_group_list = NULL;
+ (*pdb_method)->get_group_sids = NULL;
+ (*pdb_method)->add_group = NULL;
+ (*pdb_method)->update_group = NULL;
+ (*pdb_method)->delete_group = NULL;
+ (*pdb_method)->add_sid_to_group = NULL;
+ (*pdb_method)->remove_sid_from_group = NULL;
+ (*pdb_method)->get_group_info_by_name = NULL;
+ (*pdb_method)->get_group_info_by_nt_name = NULL;
+ (*pdb_method)->get_group_uids = NULL;
+
+ data = talloc(pdb_context->mem_ctx, sizeof(pdb_xml));
+ data->location = talloc_strdup(pdb_context->mem_ctx, (location ? location : "passdb.xml"));
+ data->pwent = NULL;
+ data->written = 0;
+ (*pdb_method)->private_data = data;
+
+ LIBXML_TEST_VERSION xmlKeepBlanksDefault(0);
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_xml_init(void)
+{
+ return smb_register_passdb(PASSDB_INTERFACE_VERSION, "xml", xmlsam_init);
+}
diff --git a/source/passdb/privileges.c b/source/passdb/privileges.c
new file mode 100644
index 00000000000..624817e32e0
--- /dev/null
+++ b/source/passdb/privileges.c
@@ -0,0 +1,341 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * default privileges backend for passdb
+ *
+ * Copyright (C) Andrew Tridgell 2003
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 675
+ * Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+/*
+ this is a local implementation of a privileges backend, with
+ privileges stored in a tdb. Most passdb implementations will
+ probably use this backend, although some (such as pdb_ldap) will
+ store the privileges in another manner.
+
+ The basic principle is that the backend should store a list of SIDs
+ associated with each right, where a right is a string name such as
+ 'SeTakeOwnershipPrivilege'. The SIDs can be of any type, and do not
+ need to belong to the local domain.
+
+ The way this is used is that certain places in the code which
+ require access control will ask the privileges backend 'does this
+ user have the following privilege'. The 'user' will be a NT_TOKEN,
+ which is essentially just a list of SIDs. If any of those SIDs are
+ listed in the list of SIDs for that privilege then the answer will
+ be 'yes'. That will usually mean that the user gets unconditional
+ access to that functionality, regradless of any ACLs. In this way
+ privileges act in a similar fashion to unix setuid bits.
+*/
+
+/*
+ The terms 'right' and 'privilege' are used interchangably in this
+ file. This follows MSDN convention where the LSA calls are calls on
+ 'rights', which really means privileges. My apologies for the
+ confusion.
+*/
+
+
+/* 15 seconds seems like an ample time for timeouts on the privileges db */
+#define LOCK_TIMEOUT 15
+
+
+/* the tdb handle for the privileges database */
+static TDB_CONTEXT *tdb;
+
+
+/* initialise the privilege database */
+BOOL privilege_init(void)
+{
+ tdb = tdb_open_log(lock_path("privilege.tdb"), 0, TDB_DEFAULT,
+ O_RDWR|O_CREAT, 0600);
+ if (!tdb) {
+ DEBUG(0,("Failed to open privilege database\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*
+ lock the record for a particular privilege (write lock)
+*/
+static NTSTATUS privilege_lock_right(const char *right)
+{
+ if (tdb_lock_bystring(tdb, right, LOCK_TIMEOUT) != 0) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ unlock the record for a particular privilege (write lock)
+*/
+static void privilege_unlock_right(const char *right)
+{
+ tdb_unlock_bystring(tdb, right);
+}
+
+
+/*
+ return a list of SIDs that have a particular right
+*/
+NTSTATUS privilege_enum_account_with_right(const char *right,
+ uint32 *count,
+ DOM_SID **sids)
+{
+ TDB_DATA data;
+ char *p;
+ int i;
+
+ if (!tdb) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ data = tdb_fetch_bystring(tdb, right);
+ if (!data.dptr) {
+ *count = 0;
+ *sids = NULL;
+ return NT_STATUS_OK;
+ }
+
+ /* count them */
+ for (i=0, p=data.dptr; p<data.dptr+data.dsize; i++) {
+ p += strlen(p) + 1;
+ }
+ *count = i;
+
+ /* allocate and parse */
+ *sids = malloc(sizeof(DOM_SID) * *count);
+ if (! *sids) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0, p=data.dptr; p<data.dptr+data.dsize; i++) {
+ if (!string_to_sid(&(*sids)[i], p)) {
+ free(data.dptr);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ p += strlen(p) + 1;
+ }
+
+ free(data.dptr);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ set what accounts have a given right - this is an internal interface
+*/
+static NTSTATUS privilege_set_accounts_with_right(const char *right,
+ uint32 count,
+ DOM_SID *sids)
+{
+ TDB_DATA data;
+ char *p;
+ int i;
+
+ if (!tdb) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ /* allocate the maximum size that we might use */
+ data.dptr = malloc(count * ((MAXSUBAUTHS*11) + 30));
+ if (!data.dptr) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ p = data.dptr;
+
+ for (i=0;i<count;i++) {
+ sid_to_string(p, &sids[i]);
+ p += strlen(p) + 1;
+ }
+
+ data.dsize = PTR_DIFF(p, data.dptr);
+
+ if (tdb_store_bystring(tdb, right, data, TDB_REPLACE) != 0) {
+ free(data.dptr);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ free(data.dptr);
+ return NT_STATUS_OK;
+}
+
+
+/*
+ add a SID to the list of SIDs for a right
+*/
+NTSTATUS privilege_add_account_right(const char *right,
+ DOM_SID *sid)
+{
+ NTSTATUS status;
+ DOM_SID *current_sids;
+ uint32 current_count;
+ int i;
+
+ status = privilege_lock_right(right);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = privilege_enum_account_with_right(right, &current_count, &current_sids);
+ if (!NT_STATUS_IS_OK(status)) {
+ privilege_unlock_right(right);
+ return status;
+ }
+
+ /* maybe that SID is already listed? this is not an error */
+ for (i=0;i<current_count;i++) {
+ if (sid_equal(&current_sids[i], sid)) {
+ privilege_unlock_right(right);
+ free(current_sids);
+ return NT_STATUS_OK;
+ }
+ }
+
+ /* add it in */
+ current_sids = Realloc(current_sids, sizeof(current_sids[0]) * (current_count+1));
+ if (!current_sids) {
+ privilege_unlock_right(right);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sid_copy(&current_sids[current_count], sid);
+ current_count++;
+
+ status = privilege_set_accounts_with_right(right, current_count, current_sids);
+
+ free(current_sids);
+ privilege_unlock_right(right);
+
+ return status;
+}
+
+
+/*
+ remove a SID from the list of SIDs for a right
+*/
+NTSTATUS privilege_remove_account_right(const char *right,
+ DOM_SID *sid)
+{
+ NTSTATUS status;
+ DOM_SID *current_sids;
+ uint32 current_count;
+ int i;
+
+ status = privilege_lock_right(right);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = privilege_enum_account_with_right(right, &current_count, &current_sids);
+ if (!NT_STATUS_IS_OK(status)) {
+ privilege_unlock_right(right);
+ return status;
+ }
+
+ for (i=0;i<current_count;i++) {
+ if (sid_equal(&current_sids[i], sid)) {
+ /* found it - so remove it */
+ if (current_count-i > 1) {
+ memmove(&current_sids[i], &current_sids[i+1],
+ sizeof(current_sids[0]) * ((current_count-i)-1));
+ }
+ current_count--;
+ status = privilege_set_accounts_with_right(right,
+ current_count,
+ current_sids);
+ free(current_sids);
+ privilege_unlock_right(right);
+ return status;
+ }
+ }
+
+ /* removing a right that you don't have is not an error */
+
+ safe_free(current_sids);
+ privilege_unlock_right(right);
+ return NT_STATUS_OK;
+}
+
+
+/*
+ an internal function for checking if a SID has a right
+*/
+static BOOL privilege_sid_has_right(DOM_SID *sid, const char *right)
+{
+ NTSTATUS status;
+ uint32 count;
+ DOM_SID *sids;
+ int i;
+
+ status = privilege_enum_account_with_right(right, &count, &sids);
+ if (!NT_STATUS_IS_OK(status)) {
+ return False;
+ }
+ for (i=0;i<count;i++) {
+ if (sid_equal(sid, &sids[i])) {
+ free(sids);
+ return True;
+ }
+ }
+
+ safe_free(sids);
+ return False;
+}
+
+/*
+ list the rights for an account. This involves traversing the database
+*/
+NTSTATUS privilege_enum_account_rights(DOM_SID *sid,
+ uint32 *count,
+ char ***rights)
+{
+ TDB_DATA key, nextkey;
+ char *right;
+
+ if (!tdb) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ *rights = NULL;
+ *count = 0;
+
+ for (key = tdb_firstkey(tdb); key.dptr; key = nextkey) {
+ nextkey = tdb_nextkey(tdb, key);
+
+ right = key.dptr;
+
+ if (privilege_sid_has_right(sid, right)) {
+ (*rights) = (char **)Realloc(*rights,sizeof(char *) * ((*count)+1));
+ if (! *rights) {
+ safe_free(nextkey.dptr);
+ free(key.dptr);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*rights)[*count] = strdup(right);
+ (*count)++;
+ }
+
+ free(key.dptr);
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source/passdb/secrets.c b/source/passdb/secrets.c
new file mode 100644
index 00000000000..7531435e84f
--- /dev/null
+++ b/source/passdb/secrets.c
@@ -0,0 +1,824 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) Andrew Tridgell 1992-2001
+ Copyright (C) Andrew Bartlett 2002
+ Copyright (C) Rafal Szczesniak 2002
+ Copyright (C) Tim Potter 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* the Samba secrets database stores any generated, private information
+ such as the local SID and machine trust password */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+static TDB_CONTEXT *tdb;
+
+/* open up the secrets database */
+BOOL secrets_init(void)
+{
+ pstring fname;
+
+ if (tdb)
+ return True;
+
+ pstrcpy(fname, lp_private_dir());
+ pstrcat(fname,"/secrets.tdb");
+
+ tdb = tdb_open_log(fname, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+
+ if (!tdb) {
+ DEBUG(0,("Failed to open %s\n", fname));
+ return False;
+ }
+ return True;
+}
+
+/* read a entry from the secrets database - the caller must free the result
+ if size is non-null then the size of the entry is put in there
+ */
+void *secrets_fetch(const char *key, size_t *size)
+{
+ TDB_DATA kbuf, dbuf;
+ secrets_init();
+ if (!tdb)
+ return NULL;
+ kbuf.dptr = (char *)key;
+ kbuf.dsize = strlen(key);
+ dbuf = tdb_fetch(tdb, kbuf);
+ if (size)
+ *size = dbuf.dsize;
+ return dbuf.dptr;
+}
+
+/* store a secrets entry
+ */
+BOOL secrets_store(const char *key, const void *data, size_t size)
+{
+ TDB_DATA kbuf, dbuf;
+ secrets_init();
+ if (!tdb)
+ return False;
+ kbuf.dptr = (char *)key;
+ kbuf.dsize = strlen(key);
+ dbuf.dptr = (char *)data;
+ dbuf.dsize = size;
+ return tdb_store(tdb, kbuf, dbuf, TDB_REPLACE) == 0;
+}
+
+
+/* delete a secets database entry
+ */
+BOOL secrets_delete(const char *key)
+{
+ TDB_DATA kbuf;
+ secrets_init();
+ if (!tdb)
+ return False;
+ kbuf.dptr = (char *)key;
+ kbuf.dsize = strlen(key);
+ return tdb_delete(tdb, kbuf) == 0;
+}
+
+BOOL secrets_store_domain_sid(const char *domain, const DOM_SID *sid)
+{
+ fstring key;
+ BOOL ret;
+
+ slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_DOMAIN_SID, domain);
+ strupper_m(key);
+ ret = secrets_store(key, sid, sizeof(DOM_SID));
+
+ /* Force a re-query, in case we modified our domain */
+ if (ret)
+ reset_global_sam_sid();
+ return ret;
+}
+
+BOOL secrets_fetch_domain_sid(const char *domain, DOM_SID *sid)
+{
+ DOM_SID *dyn_sid;
+ fstring key;
+ size_t size;
+
+ slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_DOMAIN_SID, domain);
+ strupper_m(key);
+ dyn_sid = (DOM_SID *)secrets_fetch(key, &size);
+
+ if (dyn_sid == NULL)
+ return False;
+
+ if (size != sizeof(DOM_SID))
+ {
+ SAFE_FREE(dyn_sid);
+ return False;
+ }
+
+ *sid = *dyn_sid;
+ SAFE_FREE(dyn_sid);
+ return True;
+}
+
+BOOL secrets_store_domain_guid(const char *domain, struct uuid *guid)
+{
+ fstring key;
+
+ slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_DOMAIN_GUID, domain);
+ strupper_m(key);
+ return secrets_store(key, guid, sizeof(struct uuid));
+}
+
+BOOL secrets_fetch_domain_guid(const char *domain, struct uuid *guid)
+{
+ struct uuid *dyn_guid;
+ fstring key;
+ size_t size;
+ struct uuid new_guid;
+
+ slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_DOMAIN_GUID, domain);
+ strupper_m(key);
+ dyn_guid = (struct uuid *)secrets_fetch(key, &size);
+
+ if ((!dyn_guid) && (lp_server_role() == ROLE_DOMAIN_PDC)) {
+ smb_uuid_generate_random(&new_guid);
+ if (!secrets_store_domain_guid(domain, &new_guid))
+ return False;
+ dyn_guid = (struct uuid *)secrets_fetch(key, &size);
+ if (dyn_guid == NULL)
+ return False;
+ }
+
+ if (size != sizeof(struct uuid))
+ {
+ DEBUG(1,("UUID size %d is wrong!\n", (int)size));
+ SAFE_FREE(dyn_guid);
+ return False;
+ }
+
+ *guid = *dyn_guid;
+ SAFE_FREE(dyn_guid);
+ return True;
+}
+
+/**
+ * Form a key for fetching the machine trust account password
+ *
+ * @param domain domain name
+ *
+ * @return stored password's key
+ **/
+const char *trust_keystr(const char *domain)
+{
+ static fstring keystr;
+
+ slprintf(keystr,sizeof(keystr)-1,"%s/%s",
+ SECRETS_MACHINE_ACCT_PASS, domain);
+ strupper_m(keystr);
+
+ return keystr;
+}
+
+/**
+ * Form a key for fetching a trusted domain password
+ *
+ * @param domain trusted domain name
+ *
+ * @return stored password's key
+ **/
+static char *trustdom_keystr(const char *domain)
+{
+ static pstring keystr;
+
+ pstr_sprintf(keystr, "%s/%s", SECRETS_DOMTRUST_ACCT_PASS, domain);
+ strupper_m(keystr);
+
+ return keystr;
+}
+
+/************************************************************************
+ Lock the trust password entry.
+************************************************************************/
+
+BOOL secrets_lock_trust_account_password(const char *domain, BOOL dolock)
+{
+ if (!tdb)
+ return False;
+
+ if (dolock)
+ return (tdb_lock_bystring(tdb, trust_keystr(domain),0) == 0);
+ else
+ tdb_unlock_bystring(tdb, trust_keystr(domain));
+ return True;
+}
+
+/************************************************************************
+ Routine to get the default secure channel type for trust accounts
+************************************************************************/
+
+uint32 get_default_sec_channel(void)
+{
+ if (lp_server_role() == ROLE_DOMAIN_BDC ||
+ lp_server_role() == ROLE_DOMAIN_PDC) {
+ return SEC_CHAN_BDC;
+ } else {
+ return SEC_CHAN_WKSTA;
+ }
+}
+
+/************************************************************************
+ Routine to get the trust account password for a domain.
+ The user of this function must have locked the trust password file using
+ the above secrets_lock_trust_account_password().
+************************************************************************/
+
+BOOL secrets_fetch_trust_account_password(const char *domain, uint8 ret_pwd[16],
+ time_t *pass_last_set_time,
+ uint32 *channel)
+{
+ struct machine_acct_pass *pass;
+ char *plaintext;
+ size_t size;
+
+ plaintext = secrets_fetch_machine_password(domain, pass_last_set_time,
+ channel);
+ if (plaintext) {
+ DEBUG(4,("Using cleartext machine password\n"));
+ E_md4hash(plaintext, ret_pwd);
+ SAFE_FREE(plaintext);
+ return True;
+ }
+
+ if (!(pass = secrets_fetch(trust_keystr(domain), &size))) {
+ DEBUG(5, ("secrets_fetch failed!\n"));
+ return False;
+ }
+
+ if (size != sizeof(*pass)) {
+ DEBUG(0, ("secrets were of incorrect size!\n"));
+ return False;
+ }
+
+ if (pass_last_set_time) *pass_last_set_time = pass->mod_time;
+ memcpy(ret_pwd, pass->hash, 16);
+ SAFE_FREE(pass);
+
+ if (channel)
+ *channel = get_default_sec_channel();
+
+ return True;
+}
+
+/************************************************************************
+ Routine to get account password to trusted domain
+************************************************************************/
+
+BOOL secrets_fetch_trusted_domain_password(const char *domain, char** pwd,
+ DOM_SID *sid, time_t *pass_last_set_time)
+{
+ struct trusted_dom_pass pass;
+ size_t size;
+
+ /* unpacking structures */
+ char* pass_buf;
+ int pass_len = 0;
+
+ ZERO_STRUCT(pass);
+
+ /* fetching trusted domain password structure */
+ if (!(pass_buf = secrets_fetch(trustdom_keystr(domain), &size))) {
+ DEBUG(5, ("secrets_fetch failed!\n"));
+ return False;
+ }
+
+ /* unpack trusted domain password */
+ pass_len = tdb_trusted_dom_pass_unpack(pass_buf, size, &pass);
+ SAFE_FREE(pass_buf);
+
+ if (pass_len != size) {
+ DEBUG(5, ("Invalid secrets size. Unpacked data doesn't match trusted_dom_pass structure.\n"));
+ return False;
+ }
+
+ /* the trust's password */
+ if (pwd) {
+ *pwd = strdup(pass.pass);
+ if (!*pwd) {
+ return False;
+ }
+ }
+
+ /* last change time */
+ if (pass_last_set_time) *pass_last_set_time = pass.mod_time;
+
+ /* domain sid */
+ sid_copy(sid, &pass.domain_sid);
+
+ return True;
+}
+
+
+/**
+ * Routine to store the password for trusted domain
+ *
+ * @param domain remote domain name
+ * @param pwd plain text password of trust relationship
+ * @param sid remote domain sid
+ *
+ * @return true if succeeded
+ **/
+
+BOOL secrets_store_trusted_domain_password(const char* domain, smb_ucs2_t *uni_dom_name,
+ size_t uni_name_len, const char* pwd,
+ DOM_SID sid)
+{
+ /* packing structures */
+ pstring pass_buf;
+ int pass_len = 0;
+ int pass_buf_len = sizeof(pass_buf);
+
+ struct trusted_dom_pass pass;
+ ZERO_STRUCT(pass);
+
+ /* unicode domain name and its length */
+ if (!uni_dom_name)
+ return False;
+
+ strncpy_w(pass.uni_name, uni_dom_name, sizeof(pass.uni_name) - 1);
+ pass.uni_name_len = uni_name_len;
+
+ /* last change time */
+ pass.mod_time = time(NULL);
+
+ /* password of the trust */
+ pass.pass_len = strlen(pwd);
+ fstrcpy(pass.pass, pwd);
+
+ /* domain sid */
+ sid_copy(&pass.domain_sid, &sid);
+
+ pass_len = tdb_trusted_dom_pass_pack(pass_buf, pass_buf_len, &pass);
+
+ return secrets_store(trustdom_keystr(domain), (void *)&pass_buf, pass_len);
+}
+
+/************************************************************************
+ Routine to set the plaintext machine account password for a realm
+the password is assumed to be a null terminated ascii string
+************************************************************************/
+
+BOOL secrets_store_machine_password(const char *pass, const char *domain, uint32 sec_channel)
+{
+ char *key = NULL;
+ BOOL ret;
+ uint32 last_change_time;
+ uint32 sec_channel_type;
+
+ asprintf(&key, "%s/%s", SECRETS_MACHINE_PASSWORD, domain);
+ if (!key)
+ return False;
+ strupper_m(key);
+
+ ret = secrets_store(key, pass, strlen(pass)+1);
+ SAFE_FREE(key);
+
+ if (!ret)
+ return ret;
+
+ asprintf(&key, "%s/%s", SECRETS_MACHINE_LAST_CHANGE_TIME, domain);
+ if (!key)
+ return False;
+ strupper_m(key);
+
+ SIVAL(&last_change_time, 0, time(NULL));
+ ret = secrets_store(key, &last_change_time, sizeof(last_change_time));
+ SAFE_FREE(key);
+
+ asprintf(&key, "%s/%s", SECRETS_MACHINE_SEC_CHANNEL_TYPE, domain);
+ if (!key)
+ return False;
+ strupper_m(key);
+
+ SIVAL(&sec_channel_type, 0, sec_channel);
+ ret = secrets_store(key, &sec_channel_type, sizeof(sec_channel_type));
+ SAFE_FREE(key);
+
+ return ret;
+}
+
+
+/************************************************************************
+ Routine to fetch the plaintext machine account password for a realm
+the password is assumed to be a null terminated ascii string
+************************************************************************/
+char *secrets_fetch_machine_password(const char *domain,
+ time_t *pass_last_set_time,
+ uint32 *channel)
+{
+ char *key = NULL;
+ char *ret;
+ asprintf(&key, "%s/%s", SECRETS_MACHINE_PASSWORD, domain);
+ strupper_m(key);
+ ret = (char *)secrets_fetch(key, NULL);
+ SAFE_FREE(key);
+
+ if (pass_last_set_time) {
+ size_t size;
+ uint32 *last_set_time;
+ asprintf(&key, "%s/%s", SECRETS_MACHINE_LAST_CHANGE_TIME, domain);
+ strupper_m(key);
+ last_set_time = secrets_fetch(key, &size);
+ if (last_set_time) {
+ *pass_last_set_time = IVAL(last_set_time,0);
+ SAFE_FREE(last_set_time);
+ } else {
+ *pass_last_set_time = 0;
+ }
+ SAFE_FREE(key);
+ }
+
+ if (channel) {
+ size_t size;
+ uint32 *channel_type;
+ asprintf(&key, "%s/%s", SECRETS_MACHINE_SEC_CHANNEL_TYPE, domain);
+ strupper_m(key);
+ channel_type = secrets_fetch(key, &size);
+ if (channel_type) {
+ *channel = IVAL(channel_type,0);
+ SAFE_FREE(channel_type);
+ } else {
+ *channel = get_default_sec_channel();
+ }
+ SAFE_FREE(key);
+ }
+
+ return ret;
+}
+
+
+
+/************************************************************************
+ Routine to delete the machine trust account password file for a domain.
+************************************************************************/
+
+BOOL trust_password_delete(const char *domain)
+{
+ return secrets_delete(trust_keystr(domain));
+}
+
+/************************************************************************
+ Routine to delete the password for trusted domain
+************************************************************************/
+
+BOOL trusted_domain_password_delete(const char *domain)
+{
+ return secrets_delete(trustdom_keystr(domain));
+}
+
+
+/*******************************************************************
+ Reset the 'done' variables so after a client process is created
+ from a fork call these calls will be re-done. This should be
+ expanded if more variables need reseting.
+ ******************************************************************/
+
+void reset_globals_after_fork(void)
+{
+ unsigned char dummy;
+
+ secrets_init();
+
+ /*
+ * Increment the global seed value to ensure every smbd starts
+ * with a new random seed.
+ */
+
+ if (tdb) {
+ uint32 initial_val = sys_getpid();
+ tdb_change_int32_atomic(tdb, "INFO/random_seed", (int *)&initial_val, 1);
+ set_rand_reseed_data((unsigned char *)&initial_val, sizeof(initial_val));
+ }
+
+ /*
+ * Re-seed the random crypto generator, so all smbd's
+ * started from the same parent won't generate the same
+ * sequence.
+ */
+ generate_random_buffer( &dummy, 1, True);
+}
+
+BOOL secrets_store_ldap_pw(const char* dn, char* pw)
+{
+ char *key = NULL;
+ BOOL ret;
+
+ if (asprintf(&key, "%s/%s", SECRETS_LDAP_BIND_PW, dn) < 0) {
+ DEBUG(0, ("secrets_store_ldap_pw: asprintf failed!\n"));
+ return False;
+ }
+
+ ret = secrets_store(key, pw, strlen(pw)+1);
+
+ SAFE_FREE(key);
+ return ret;
+}
+
+
+/**
+ * Get trusted domains info from secrets.tdb.
+ *
+ * The linked list is allocated on the supplied talloc context, caller gets to destroy
+ * when done.
+ *
+ * @param ctx Allocation context
+ * @param enum_ctx Starting index, eg. we can start fetching at third
+ * or sixth trusted domain entry. Zero is the first index.
+ * Value it is set to is the enum context for the next enumeration.
+ * @param num_domains Number of domain entries to fetch at one call
+ * @param domains Pointer to array of trusted domain structs to be filled up
+ *
+ * @return nt status code of rpc response
+ **/
+
+NTSTATUS secrets_get_trusted_domains(TALLOC_CTX* ctx, int* enum_ctx, unsigned int max_num_domains,
+ int *num_domains, TRUSTDOM ***domains)
+{
+ TDB_LIST_NODE *keys, *k;
+ TRUSTDOM *dom = NULL;
+ char *pattern;
+ unsigned int start_idx;
+ uint32 idx = 0;
+ size_t size, packed_size = 0;
+ fstring dom_name;
+ char *packed_pass;
+ struct trusted_dom_pass *pass = talloc_zero(ctx, sizeof(struct trusted_dom_pass));
+ NTSTATUS status;
+
+ if (!secrets_init()) return NT_STATUS_ACCESS_DENIED;
+
+ if (!pass) {
+ DEBUG(0, ("talloc_zero failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *num_domains = 0;
+ start_idx = *enum_ctx;
+
+ /* generate searching pattern */
+ if (!(pattern = talloc_asprintf(ctx, "%s/*", SECRETS_DOMTRUST_ACCT_PASS))) {
+ DEBUG(0, ("secrets_get_trusted_domains: talloc_asprintf() failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DEBUG(5, ("secrets_get_trusted_domains: looking for %d domains, starting at index %d\n",
+ max_num_domains, *enum_ctx));
+
+ *domains = talloc_zero(ctx, sizeof(**domains)*max_num_domains);
+
+ /* fetching trusted domains' data and collecting them in a list */
+ keys = tdb_search_keys(tdb, pattern);
+
+ /*
+ * if there's no keys returned ie. no trusted domain,
+ * return "no more entries" code
+ */
+ status = NT_STATUS_NO_MORE_ENTRIES;
+
+ /* searching for keys in secrets db -- way to go ... */
+ for (k = keys; k; k = k->next) {
+ char *secrets_key;
+
+ /* important: ensure null-termination of the key string */
+ secrets_key = strndup(k->node_key.dptr, k->node_key.dsize);
+ if (!secrets_key) {
+ DEBUG(0, ("strndup failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ packed_pass = secrets_fetch(secrets_key, &size);
+ packed_size = tdb_trusted_dom_pass_unpack(packed_pass, size, pass);
+ /* packed representation isn't needed anymore */
+ SAFE_FREE(packed_pass);
+
+ if (size != packed_size) {
+ DEBUG(2, ("Secrets record %s is invalid!\n", secrets_key));
+ continue;
+ }
+
+ pull_ucs2_fstring(dom_name, pass->uni_name);
+ DEBUG(18, ("Fetched secret record num %d.\nDomain name: %s, SID: %s\n",
+ idx, dom_name, sid_string_static(&pass->domain_sid)));
+
+ SAFE_FREE(secrets_key);
+
+ if (idx >= start_idx && idx < start_idx + max_num_domains) {
+ dom = talloc_zero(ctx, sizeof(*dom));
+ if (!dom) {
+ /* free returned tdb record */
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* copy domain sid */
+ SMB_ASSERT(sizeof(dom->sid) == sizeof(pass->domain_sid));
+ memcpy(&(dom->sid), &(pass->domain_sid), sizeof(dom->sid));
+
+ /* copy unicode domain name */
+ dom->name = talloc_strdup_w(ctx, pass->uni_name);
+
+ (*domains)[idx - start_idx] = dom;
+
+ DEBUG(18, ("Secret record is in required range.\n \
+ start_idx = %d, max_num_domains = %d. Added to returned array.\n",
+ start_idx, max_num_domains));
+
+ *enum_ctx = idx + 1;
+ (*num_domains)++;
+
+ /* set proper status code to return */
+ if (k->next) {
+ /* there are yet some entries to enumerate */
+ status = STATUS_MORE_ENTRIES;
+ } else {
+ /* this is the last entry in the whole enumeration */
+ status = NT_STATUS_OK;
+ }
+ } else {
+ DEBUG(18, ("Secret is outside the required range.\n \
+ start_idx = %d, max_num_domains = %d. Not added to returned array\n",
+ start_idx, max_num_domains));
+ }
+
+ idx++;
+ }
+
+ DEBUG(5, ("secrets_get_trusted_domains: got %d domains\n", *num_domains));
+
+ /* free the results of searching the keys */
+ tdb_search_list_free(keys);
+
+ return status;
+}
+
+/*******************************************************************************
+ Lock the secrets tdb based on a string - this is used as a primitive form of mutex
+ between smbd instances.
+*******************************************************************************/
+
+BOOL secrets_named_mutex(const char *name, unsigned int timeout)
+{
+ int ret = 0;
+
+ if (!message_init())
+ return False;
+
+ ret = tdb_lock_bystring(tdb, name, timeout);
+ if (ret == 0)
+ DEBUG(10,("secrets_named_mutex: got mutex for %s\n", name ));
+
+ return (ret == 0);
+}
+
+/*******************************************************************************
+ Unlock a named mutex.
+*******************************************************************************/
+
+void secrets_named_mutex_release(const char *name)
+{
+ tdb_unlock_bystring(tdb, name);
+ DEBUG(10,("secrets_named_mutex: released mutex for %s\n", name ));
+}
+
+/*********************************************************
+ Check to see if we must talk to the PDC to avoid sam
+ sync delays
+ ********************************************************/
+
+BOOL must_use_pdc( const char *domain )
+{
+ time_t now = time(NULL);
+ time_t last_change_time;
+ unsigned char passwd[16];
+
+ if ( !secrets_fetch_trust_account_password(domain, passwd, &last_change_time, NULL) )
+ return False;
+
+ /*
+ * If the time the machine password has changed
+ * was less than about 15 minutes then we need to contact
+ * the PDC only, as we cannot be sure domain replication
+ * has yet taken place. Bug found by Gerald (way to go
+ * Gerald !). JRA.
+ */
+
+ if ( now - last_change_time < SAM_SYNC_WINDOW )
+ return True;
+
+ return False;
+
+}
+
+/*******************************************************************************
+ Store a complete AFS keyfile into secrets.tdb.
+*******************************************************************************/
+
+BOOL secrets_store_afs_keyfile(const char *cell, const struct afs_keyfile *keyfile)
+{
+ fstring key;
+
+ if ((cell == NULL) || (keyfile == NULL))
+ return False;
+
+ if (ntohl(keyfile->nkeys) > SECRETS_AFS_MAXKEYS)
+ return False;
+
+ slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_AFS_KEYFILE, cell);
+ return secrets_store(key, keyfile, sizeof(struct afs_keyfile));
+}
+
+/*******************************************************************************
+ Fetch the current (highest) AFS key from secrets.tdb
+*******************************************************************************/
+BOOL secrets_fetch_afs_key(const char *cell, struct afs_key *result)
+{
+ fstring key;
+ struct afs_keyfile *keyfile;
+ size_t size;
+ uint32 i;
+
+ slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_AFS_KEYFILE, cell);
+
+ keyfile = (struct afs_keyfile *)secrets_fetch(key, &size);
+
+ if (keyfile == NULL)
+ return False;
+
+ if (size != sizeof(struct afs_keyfile)) {
+ SAFE_FREE(keyfile);
+ return False;
+ }
+
+ i = ntohl(keyfile->nkeys);
+
+ if (i > SECRETS_AFS_MAXKEYS) {
+ SAFE_FREE(keyfile);
+ return False;
+ }
+
+ *result = keyfile->entry[i-1];
+
+ result->kvno = ntohl(result->kvno);
+
+ return True;
+}
+
+/******************************************************************************
+ When kerberos is not available, choose between anonymous or
+ authenticated connections.
+
+ We need to use an authenticated connection if DCs have the
+ RestrictAnonymous registry entry set > 0, or the "Additional
+ restrictions for anonymous connections" set in the win2k Local
+ Security Policy.
+
+ Caller to free() result in domain, username, password
+*******************************************************************************/
+void secrets_fetch_ipc_userpass(char **username, char **domain, char **password)
+{
+ *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
+ *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
+ *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
+
+ if (*username && **username) {
+
+ if (!*domain || !**domain)
+ *domain = smb_xstrdup(lp_workgroup());
+
+ if (!*password || !**password)
+ *password = smb_xstrdup("");
+
+ DEBUG(3, ("IPC$ connections done by user %s\\%s\n",
+ *domain, *username));
+
+ } else {
+ DEBUG(3, ("IPC$ connections done anonymously\n"));
+ *username = smb_xstrdup("");
+ *domain = smb_xstrdup("");
+ *password = smb_xstrdup("");
+ }
+}
+
diff --git a/source/passdb/util_sam_sid.c b/source/passdb/util_sam_sid.c
new file mode 100644
index 00000000000..3617498eec1
--- /dev/null
+++ b/source/passdb/util_sam_sid.c
@@ -0,0 +1,332 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Luke Kenneth Caseson Leighton 1998-1999
+ Copyright (C) Jeremy Allison 1999
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#define MAX_SID_NAMES 7
+
+typedef struct _known_sid_users {
+ uint32 rid;
+ enum SID_NAME_USE sid_name_use;
+ const char *known_user_name;
+} known_sid_users;
+
+static struct sid_name_map_info
+{
+ DOM_SID *sid;
+ const char *name;
+ const known_sid_users *known_users;
+} sid_name_map[MAX_SID_NAMES];
+
+extern DOM_SID global_sid_Builtin; /* Local well-known domain */
+extern DOM_SID global_sid_World_Domain; /* Everyone domain */
+extern DOM_SID global_sid_Creator_Owner_Domain; /* Creator Owner domain */
+extern DOM_SID global_sid_NT_Authority; /* NT Authority */
+
+
+static BOOL sid_name_map_initialized = False;
+/* static known_sid_users no_users[] = {{0, 0, NULL}}; */
+
+static const known_sid_users everyone_users[] = {
+ { 0, SID_NAME_WKN_GRP, "Everyone" },
+ {0, (enum SID_NAME_USE)0, NULL}};
+
+static const known_sid_users creator_owner_users[] = {
+ { 0, SID_NAME_WKN_GRP, "Creator Owner" },
+ { 1, SID_NAME_WKN_GRP, "Creator Group" },
+ {0, (enum SID_NAME_USE)0, NULL}};
+
+static const known_sid_users nt_authority_users[] = {
+ { 1, SID_NAME_ALIAS, "Dialup" },
+ { 2, SID_NAME_ALIAS, "Network"},
+ { 3, SID_NAME_ALIAS, "Batch"},
+ { 4, SID_NAME_ALIAS, "Interactive"},
+ { 6, SID_NAME_ALIAS, "Service"},
+ { 7, SID_NAME_ALIAS, "AnonymousLogon"},
+ { 8, SID_NAME_ALIAS, "Proxy"},
+ { 9, SID_NAME_ALIAS, "ServerLogon"},
+ { 11, SID_NAME_ALIAS, "Authenticated Users"},
+ { 18, SID_NAME_ALIAS, "SYSTEM"},
+ { 0, (enum SID_NAME_USE)0, NULL}};
+
+static const known_sid_users builtin_groups[] = {
+ { BUILTIN_ALIAS_RID_ADMINS, SID_NAME_ALIAS, "Administrators" },
+ { BUILTIN_ALIAS_RID_USERS, SID_NAME_ALIAS, "Users" },
+ { BUILTIN_ALIAS_RID_GUESTS, SID_NAME_ALIAS, "Guests" },
+ { BUILTIN_ALIAS_RID_POWER_USERS, SID_NAME_ALIAS, "Power Users" },
+ { BUILTIN_ALIAS_RID_ACCOUNT_OPS, SID_NAME_ALIAS, "Account Operators" },
+ { BUILTIN_ALIAS_RID_SYSTEM_OPS, SID_NAME_ALIAS, "Server Operators" },
+ { BUILTIN_ALIAS_RID_PRINT_OPS, SID_NAME_ALIAS, "Print Operators" },
+ { BUILTIN_ALIAS_RID_BACKUP_OPS, SID_NAME_ALIAS, "Backup Operators" },
+ { BUILTIN_ALIAS_RID_REPLICATOR, SID_NAME_ALIAS, "Replicator" },
+ { BUILTIN_ALIAS_RID_RAS_SERVERS, SID_NAME_ALIAS, "RAS Servers" },
+ { BUILTIN_ALIAS_RID_PRE_2K_ACCESS, SID_NAME_ALIAS, "Pre-Windows 2000 Compatible Access" },
+ { 0, (enum SID_NAME_USE)0, NULL}};
+
+/**************************************************************************
+ Quick init function.
+*************************************************************************/
+
+static void init_sid_name_map (void)
+{
+ int i = 0;
+
+ if (sid_name_map_initialized) return;
+
+ generate_wellknown_sids();
+
+ if ((lp_security() == SEC_USER) && lp_domain_logons()) {
+ sid_name_map[i].sid = get_global_sam_sid();
+ /* This is not lp_workgroup() for good reason:
+ it must stay around longer than the lp_*()
+ strings do */
+ sid_name_map[i].name = strdup(lp_workgroup());
+ sid_name_map[i].known_users = NULL;
+ i++;
+ sid_name_map[i].sid = get_global_sam_sid();
+ sid_name_map[i].name = strdup(global_myname());
+ sid_name_map[i].known_users = NULL;
+ i++;
+ } else {
+ sid_name_map[i].sid = get_global_sam_sid();
+ sid_name_map[i].name = strdup(global_myname());
+ sid_name_map[i].known_users = NULL;
+ i++;
+ }
+
+ sid_name_map[i].sid = &global_sid_Builtin;
+ sid_name_map[i].name = "BUILTIN";
+ sid_name_map[i].known_users = &builtin_groups[0];
+ i++;
+
+ sid_name_map[i].sid = &global_sid_World_Domain;
+ sid_name_map[i].name = "";
+ sid_name_map[i].known_users = &everyone_users[0];
+ i++;
+
+ sid_name_map[i].sid = &global_sid_Creator_Owner_Domain;
+ sid_name_map[i].name = "";
+ sid_name_map[i].known_users = &creator_owner_users[0];
+ i++;
+
+ sid_name_map[i].sid = &global_sid_NT_Authority;
+ sid_name_map[i].name = "NT Authority";
+ sid_name_map[i].known_users = &nt_authority_users[0];
+ i++;
+
+ /* End of array. */
+ sid_name_map[i].sid = NULL;
+ sid_name_map[i].name = NULL;
+ sid_name_map[i].known_users = NULL;
+
+ sid_name_map_initialized = True;
+
+ return;
+}
+
+/**************************************************************************
+ Turns a domain SID into a name, returned in the nt_domain argument.
+***************************************************************************/
+
+BOOL map_domain_sid_to_name(DOM_SID *sid, fstring nt_domain)
+{
+ fstring sid_str;
+ int i = 0;
+
+ sid_to_string(sid_str, sid);
+
+ if (!sid_name_map_initialized)
+ init_sid_name_map();
+
+ DEBUG(5,("map_domain_sid_to_name: %s\n", sid_str));
+
+ if (nt_domain == NULL)
+ return False;
+
+ while (sid_name_map[i].sid != NULL) {
+ sid_to_string(sid_str, sid_name_map[i].sid);
+ DEBUG(5,("map_domain_sid_to_name: compare: %s\n", sid_str));
+ if (sid_equal(sid_name_map[i].sid, sid)) {
+ fstrcpy(nt_domain, sid_name_map[i].name);
+ DEBUG(5,("map_domain_sid_to_name: found '%s'\n", nt_domain));
+ return True;
+ }
+ i++;
+ }
+
+ DEBUG(5,("map_domain_sid_to_name: mapping for %s not found\n", sid_str));
+
+ return False;
+}
+
+/**************************************************************************
+ Looks up a known username from one of the known domains.
+***************************************************************************/
+
+BOOL lookup_known_rid(DOM_SID *sid, uint32 rid, char *name, enum SID_NAME_USE *psid_name_use)
+{
+ int i = 0;
+ struct sid_name_map_info *psnm;
+
+ if (!sid_name_map_initialized)
+ init_sid_name_map();
+
+ for(i = 0; sid_name_map[i].sid != NULL; i++) {
+ psnm = &sid_name_map[i];
+ if(sid_equal(psnm->sid, sid)) {
+ int j;
+ for(j = 0; psnm->known_users && psnm->known_users[j].known_user_name != NULL; j++) {
+ if(rid == psnm->known_users[j].rid) {
+ DEBUG(5,("lookup_builtin_rid: rid = %u, domain = '%s', user = '%s'\n",
+ (unsigned int)rid, psnm->name, psnm->known_users[j].known_user_name ));
+ fstrcpy( name, psnm->known_users[j].known_user_name);
+ *psid_name_use = psnm->known_users[j].sid_name_use;
+ return True;
+ }
+ }
+ }
+ }
+
+ return False;
+}
+
+/**************************************************************************
+ Turns a domain name into a SID.
+ *** side-effect: if the domain name is NULL, it is set to our domain ***
+***************************************************************************/
+
+BOOL map_domain_name_to_sid(DOM_SID *sid, char *nt_domain)
+{
+ int i = 0;
+
+ if (nt_domain == NULL) {
+ DEBUG(5,("map_domain_name_to_sid: mapping NULL domain to our SID.\n"));
+ sid_copy(sid, get_global_sam_sid());
+ return True;
+ }
+
+ if (nt_domain[0] == 0) {
+ fstrcpy(nt_domain, global_myname());
+ DEBUG(5,("map_domain_name_to_sid: overriding blank name to %s\n", nt_domain));
+ sid_copy(sid, get_global_sam_sid());
+ return True;
+ }
+
+ DEBUG(5,("map_domain_name_to_sid: %s\n", nt_domain));
+
+ if (!sid_name_map_initialized)
+ init_sid_name_map();
+
+ while (sid_name_map[i].name != NULL) {
+ DEBUG(5,("map_domain_name_to_sid: compare: %s\n", sid_name_map[i].name));
+ if (strequal(sid_name_map[i].name, nt_domain)) {
+ fstring sid_str;
+ sid_copy(sid, sid_name_map[i].sid);
+ sid_to_string(sid_str, sid_name_map[i].sid);
+ DEBUG(5,("map_domain_name_to_sid: found %s\n", sid_str));
+ return True;
+ }
+ i++;
+ }
+
+ DEBUG(0,("map_domain_name_to_sid: mapping to %s not found.\n", nt_domain));
+ return False;
+}
+
+/*****************************************************************
+ Check if the SID is our domain SID (S-1-5-21-x-y-z).
+*****************************************************************/
+
+BOOL sid_check_is_domain(const DOM_SID *sid)
+{
+ return sid_equal(sid, get_global_sam_sid());
+}
+
+/*****************************************************************
+ Check if the SID is our domain SID (S-1-5-21-x-y-z).
+*****************************************************************/
+
+BOOL sid_check_is_in_our_domain(const DOM_SID *sid)
+{
+ DOM_SID dom_sid;
+ uint32 rid;
+
+ sid_copy(&dom_sid, sid);
+ sid_split_rid(&dom_sid, &rid);
+
+ return sid_equal(&dom_sid, get_global_sam_sid());
+}
+
+/**************************************************************************
+ Try and map a name to one of the well known SIDs.
+***************************************************************************/
+
+BOOL map_name_to_wellknown_sid(DOM_SID *sid, enum SID_NAME_USE *use, const char *name)
+{
+ int i, j;
+
+ if (!sid_name_map_initialized)
+ init_sid_name_map();
+
+ for (i=0; sid_name_map[i].sid != NULL; i++) {
+ const known_sid_users *users = sid_name_map[i].known_users;
+
+ if (users == NULL)
+ continue;
+
+ for (j=0; users[j].known_user_name != NULL; j++) {
+ if ( strequal(users[j].known_user_name, name) ) {
+ sid_copy(sid, sid_name_map[i].sid);
+ sid_append_rid(sid, users[j].rid);
+ *use = users[j].sid_name_use;
+ return True;
+ }
+ }
+ }
+
+ return False;
+}
+
+void add_sid_to_array(const DOM_SID *sid, DOM_SID **sids, int *num)
+{
+ *sids = Realloc(*sids, ((*num)+1) * sizeof(DOM_SID));
+
+ if (*sids == NULL)
+ return;
+
+ sid_copy(&((*sids)[*num]), sid);
+ *num += 1;
+
+ return;
+}
+
+void add_sid_to_array_unique(const DOM_SID *sid, DOM_SID **sids, int *num)
+{
+ int i;
+
+ for (i=0; i<*num; i++) {
+ if (sid_compare(sid, &(*sids)[i]) == 0)
+ return;
+ }
+
+ add_sid_to_array(sid, sids, num);
+}
diff --git a/source/po/de.msg b/source/po/de.msg
new file mode 100644
index 00000000000..b1b84a21b67
--- /dev/null
+++ b/source/po/de.msg
@@ -0,0 +1,593 @@
+# German messages for international release of SWAT.
+# Copyright (C) 2001 Andreas Moroder
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: i18n_swat\n"
+"POT-Creation-Date: 2003-10-06 05:30+0900\n"
+"PO-Revision-Date: 2000-02-08 14:45+0100\n"
+"Last-Translator: Andreas Moroder\n"
+"Language-Team: (Samba Team) <samba-technical@samba.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../web/swat.c:117
+#, c-format
+msgid "ERROR: Can't open %s"
+msgstr "ERRORE: Kann %s nicht öffnen"
+
+#: ../web/swat.c:200
+msgid "Help"
+msgstr "Hilfe"
+
+#: ../web/swat.c:206 ../web/swat.c:220 ../web/swat.c:235 ../web/swat.c:243 ../web/swat.c:252 ../web/swat.c:261 ../web/swat.c:267 ../web/swat.c:273 ../web/swat.c:286
+msgid "Set Default"
+msgstr "Setze Standardwerte"
+
+#: ../web/swat.c:408
+#, c-format
+msgid "failed to open %s for writing"
+msgstr ""
+
+#: ../web/swat.c:431
+#, c-format
+msgid "Can't reload %s"
+msgstr ""
+
+#: ../web/swat.c:501
+#, c-format
+msgid "Logged in as <b>%s</b>"
+msgstr "Verbunden als <b>%s</b>"
+
+#: ../web/swat.c:505
+msgid "Home"
+msgstr "Home"
+
+#: ../web/swat.c:507
+msgid "Globals"
+msgstr "Globals"
+
+#: ../web/swat.c:508
+msgid "Shares"
+msgstr "Freigaben"
+
+#: ../web/swat.c:509
+msgid "Printers"
+msgstr "Drucker"
+
+#: ../web/swat.c:510
+msgid "Wizard"
+msgstr ""
+
+#: ../web/swat.c:513
+msgid "Status"
+msgstr "Status"
+
+#: ../web/swat.c:514
+msgid "View Config"
+msgstr "Zeige Konfiguration"
+
+#: ../web/swat.c:516
+msgid "Password Management"
+msgstr "Passwortverwaltung"
+
+#: ../web/swat.c:526
+msgid "Current View Is"
+msgstr "Aktuelle Konfiguration"
+
+#: ../web/swat.c:527 ../web/swat.c:530
+msgid "Basic"
+msgstr "Basis Ansicht"
+
+#: ../web/swat.c:528 ../web/swat.c:531
+msgid "Advanced"
+msgstr "Erweiterte Ansicht"
+
+#: ../web/swat.c:529
+msgid "Change View To"
+msgstr "Ändere Passwort"
+
+#: ../web/swat.c:554
+msgid "Current Config"
+msgstr "Aktuelle Konfiguration"
+
+#: ../web/swat.c:558
+msgid "Normal View"
+msgstr "Normale Ansich"
+
+#: ../web/swat.c:560
+msgid "Full View"
+msgstr "Komplette Ansicht"
+
+#. Here we first set and commit all the parameters that were selected
+#. in the previous screen.
+#: ../web/swat.c:579
+msgid "Wizard Parameter Edit Page"
+msgstr ""
+
+#: ../web/swat.c:608
+msgid "Note: smb.conf file has been read and rewritten"
+msgstr ""
+
+#. Here we go ...
+#: ../web/swat.c:716
+msgid "Samba Configuration Wizard"
+msgstr ""
+
+#: ../web/swat.c:720
+msgid "The \"Rewrite smb.conf file\" button will clear the smb.conf file of all default values and of comments."
+msgstr ""
+
+#: ../web/swat.c:721
+msgid "The same will happen if you press the commit button."
+msgstr ""
+
+#: ../web/swat.c:724
+msgid "Rewrite smb.conf file"
+msgstr ""
+
+#: ../web/swat.c:725
+msgid "Commit"
+msgstr "Kommentar"
+
+#: ../web/swat.c:726
+msgid "Edit Parameter Values"
+msgstr "Drucker Parameter"
+
+#: ../web/swat.c:732
+msgid "Server Type"
+msgstr ""
+
+#: ../web/swat.c:733
+msgid "Stand Alone"
+msgstr ""
+
+#: ../web/swat.c:734
+msgid "Domain Member"
+msgstr "Domänen master"
+
+#: ../web/swat.c:735
+msgid "Domain Controller"
+msgstr "Domänen master"
+
+#: ../web/swat.c:738
+msgid "Unusual Type in smb.conf - Please Select New Mode"
+msgstr ""
+
+#: ../web/swat.c:740
+msgid "Configure WINS As"
+msgstr ""
+
+#: ../web/swat.c:741
+msgid "Not Used"
+msgstr "nicht hinabsteigen"
+
+#: ../web/swat.c:742
+msgid "Server for client use"
+msgstr ""
+
+#: ../web/swat.c:743
+msgid "Client of another WINS server"
+msgstr ""
+
+#: ../web/swat.c:745
+msgid "Remote WINS Server"
+msgstr ""
+
+#: ../web/swat.c:756
+msgid "Error: WINS Server Mode and WINS Support both set in smb.conf"
+msgstr ""
+
+#: ../web/swat.c:757
+msgid "Please Select desired WINS mode above."
+msgstr ""
+
+#: ../web/swat.c:759
+msgid "Expose Home Directories"
+msgstr ""
+
+#: ../web/swat.c:774
+msgid "The above configuration options will set multiple parameters and will generally assist with rapid Samba deployment."
+msgstr ""
+
+#: ../web/swat.c:787
+msgid "Global Parameters"
+msgstr "Globale Variablen"
+
+#: ../web/swat.c:815 ../web/swat.c:916 ../web/swat.c:1265
+msgid "Commit Changes"
+msgstr "Speichere Änderungen"
+
+#: ../web/swat.c:819 ../web/swat.c:919 ../web/swat.c:1267
+msgid "Reset Values"
+msgstr "Setze Werte zurück"
+
+#: ../web/swat.c:844
+msgid "Share Parameters"
+msgstr "Parameter der Freigabe"
+
+#: ../web/swat.c:887
+msgid "Choose Share"
+msgstr "Wähle Freigabe"
+
+#: ../web/swat.c:901
+msgid "Delete Share"
+msgstr "Lösche Freigabe"
+
+#: ../web/swat.c:908
+msgid "Create Share"
+msgstr "Erstelle Freigabe"
+
+#: ../web/swat.c:944
+msgid "password change in demo mode rejected"
+msgstr "Änderung des Passworts im Demo modus nicht aktiv"
+
+#: ../web/swat.c:957
+msgid "Can't setup password database vectors."
+msgstr ""
+
+#: ../web/swat.c:983
+msgid " Must specify \"User Name\" "
+msgstr " \"Benutzername\" muss angegeben werden "
+
+#: ../web/swat.c:999
+msgid " Must specify \"Old Password\" "
+msgstr " \"Altes Passwort\" muß angegeben werden "
+
+#: ../web/swat.c:1005
+msgid " Must specify \"Remote Machine\" "
+msgstr " \"Remote Maschine\" muß angegeben werden "
+
+#: ../web/swat.c:1012
+msgid " Must specify \"New, and Re-typed Passwords\" "
+msgstr " \"Neues/Bestätige Passwort\" muß angegeben werden "
+
+#: ../web/swat.c:1018
+msgid " Re-typed password didn't match new password "
+msgstr " Das bestätigte Passwort stimmt nicht mit dem neuen Passwort überein"
+
+#: ../web/swat.c:1048
+#, c-format
+msgid " The passwd for '%s' has been changed."
+msgstr " Das Passwort für '%s' wurde geändert."
+
+#: ../web/swat.c:1051
+#, c-format
+msgid " The passwd for '%s' has NOT been changed."
+msgstr " Das Passwort für '%s' wurde nicht geändert."
+
+#: ../web/swat.c:1076
+msgid "Server Password Management"
+msgstr "Verwaltung des Server Passwortes"
+
+#.
+#. * Create all the dialog boxes for data collection
+#.
+#: ../web/swat.c:1085 ../web/swat.c:1132
+msgid "User Name"
+msgstr "Benutzername"
+
+#: ../web/swat.c:1088 ../web/swat.c:1134
+msgid "Old Password"
+msgstr "Altes Passwort"
+
+#: ../web/swat.c:1091 ../web/swat.c:1136
+msgid "New Password"
+msgstr "Neues Passwort"
+
+#: ../web/swat.c:1093 ../web/swat.c:1138
+msgid "Re-type New Password"
+msgstr "Bestätige neues Passwort"
+
+#: ../web/swat.c:1101 ../web/swat.c:1149
+msgid "Change Password"
+msgstr "Ändere Passwort"
+
+#: ../web/swat.c:1104
+msgid "Add New User"
+msgstr "Füge Benutzer hinzu"
+
+#: ../web/swat.c:1106
+msgid "Delete User"
+msgstr "Lösche Benutzer"
+
+#: ../web/swat.c:1108
+msgid "Disable User"
+msgstr "Desaktiviere Benutzer"
+
+#: ../web/swat.c:1110
+msgid "Enable User"
+msgstr "Aktiviere Benutzer"
+
+#: ../web/swat.c:1123
+msgid "Client/Server Password Management"
+msgstr "Client/Server Passwort Verwaltung"
+
+#: ../web/swat.c:1140
+msgid "Remote Machine"
+msgstr "Remote Maschine"
+
+#: ../web/swat.c:1179
+msgid "Printer Parameters"
+msgstr "Drucker Parameter"
+
+#: ../web/swat.c:1181
+msgid "Important Note:"
+msgstr "Wichtige Hinweise:"
+
+#: ../web/swat.c:1182
+msgid "Printer names marked with [*] in the Choose Printer drop-down box "
+msgstr "Mit [*] gekennzeichnete Druckername in der Druckerauswahlliste"
+
+#: ../web/swat.c:1183
+msgid "are autoloaded printers from "
+msgstr "wurde automatisch geladen von :"
+
+#: ../web/swat.c:1184
+msgid "Printcap Name"
+msgstr "Printcap Name"
+
+#: ../web/swat.c:1185
+msgid "Attempting to delete these printers from SWAT will have no effect."
+msgstr "Der Versuch diese Drucker von SWAT aus zu löschen wird keine Auswirkung haben."
+
+#: ../web/swat.c:1231
+msgid "Choose Printer"
+msgstr "Wähle Drucker"
+
+#: ../web/swat.c:1250
+msgid "Delete Printer"
+msgstr "Lösche Drucker"
+
+#: ../web/swat.c:1257
+msgid "Create Printer"
+msgstr "Ersteller Drucker"
+
+#: ../web/statuspage.c:123
+msgid "RDONLY "
+msgstr ""
+
+#: ../web/statuspage.c:124
+msgid "WRONLY "
+msgstr ""
+
+#: ../web/statuspage.c:125
+msgid "RDWR "
+msgstr ""
+
+#: ../web/statuspage.c:309
+msgid "Server Status"
+msgstr "Server Status"
+
+#: ../web/statuspage.c:314
+msgid "Auto Refresh"
+msgstr "Automatische Aktualisierung"
+
+#: ../web/statuspage.c:315 ../web/statuspage.c:320
+msgid "Refresh Interval: "
+msgstr "Aktualisierungsintervall: "
+
+#: ../web/statuspage.c:319
+msgid "Stop Refreshing"
+msgstr "Stop Aktualisierung"
+
+#: ../web/statuspage.c:334
+msgid "version:"
+msgstr "Version:"
+
+#: ../web/statuspage.c:337
+msgid "smbd:"
+msgstr ""
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "running"
+msgstr "aktiv"
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "not running"
+msgstr "inaktiv"
+
+#: ../web/statuspage.c:341
+msgid "Stop smbd"
+msgstr "Stopp smbd"
+
+#: ../web/statuspage.c:343
+msgid "Start smbd"
+msgstr "Start smbd"
+
+#: ../web/statuspage.c:345
+msgid "Restart smbd"
+msgstr "Neustart smbd"
+
+#: ../web/statuspage.c:350
+msgid "nmbd:"
+msgstr ""
+
+#: ../web/statuspage.c:354
+msgid "Stop nmbd"
+msgstr "Stopp nmbd"
+
+#: ../web/statuspage.c:356
+msgid "Start nmbd"
+msgstr "Start nmbd"
+
+#: ../web/statuspage.c:358
+msgid "Restart nmbd"
+msgstr "Neustart nmbd"
+
+#: ../web/statuspage.c:364
+msgid "winbindd:"
+msgstr ""
+
+#: ../web/statuspage.c:368
+msgid "Stop winbindd"
+msgstr "Stopp winbindd"
+
+#: ../web/statuspage.c:370
+msgid "Start winbindd"
+msgstr "Start winbindd"
+
+#: ../web/statuspage.c:372
+msgid "Restart winbindd"
+msgstr "Neustart winbindd"
+
+#. stop, restart all
+#: ../web/statuspage.c:381
+msgid "Stop All"
+msgstr ""
+
+#: ../web/statuspage.c:382
+msgid "Restart All"
+msgstr "Neustart Alle"
+
+#. start all
+#: ../web/statuspage.c:386
+msgid "Start All"
+msgstr "Start Alle"
+
+#: ../web/statuspage.c:393
+msgid "Active Connections"
+msgstr "Aktive Verbindungen"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "PID"
+msgstr ""
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408
+msgid "Client"
+msgstr ""
+
+#: ../web/statuspage.c:395
+msgid "IP address"
+msgstr "IP Adresse"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "Date"
+msgstr "Datum"
+
+#: ../web/statuspage.c:397
+msgid "Kill"
+msgstr "Kill"
+
+#: ../web/statuspage.c:405
+msgid "Active Shares"
+msgstr "Aktive Freigaben"
+
+#: ../web/statuspage.c:408
+msgid "Share"
+msgstr "Freigabe"
+
+#: ../web/statuspage.c:408
+msgid "User"
+msgstr "Benutzer"
+
+#: ../web/statuspage.c:408
+msgid "Group"
+msgstr "Gruppe"
+
+#: ../web/statuspage.c:414
+msgid "Open Files"
+msgstr "Offene Dateien"
+
+#: ../web/statuspage.c:416
+msgid "Sharing"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "R/W"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "Oplock"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "File"
+msgstr ""
+
+#: ../web/statuspage.c:425
+msgid "Show Client in col 1"
+msgstr ""
+
+#: ../web/statuspage.c:426
+msgid "Show PID in col 1"
+msgstr ""
+
+#: ../param/loadparm.c:755
+msgid "Base Options"
+msgstr "Basisoptionen"
+
+#: ../param/loadparm.c:775
+msgid "Security Options"
+msgstr "Sicherheitsoptionen"
+
+#: ../param/loadparm.c:859
+msgid "Logging Options"
+msgstr "Log Optionen"
+
+#: ../param/loadparm.c:874
+msgid "Protocol Options"
+msgstr "Protokoll Optionen"
+
+#: ../param/loadparm.c:911
+msgid "Tuning Options"
+msgstr "Optimierungsoptionen"
+
+#: ../param/loadparm.c:940
+msgid "Printing Options"
+msgstr "Druckoptionen"
+
+#: ../param/loadparm.c:970
+msgid "Filename Handling"
+msgstr "Verwaltung Dateinamen"
+
+#: ../param/loadparm.c:996
+msgid "Domain Options"
+msgstr "Domänen Optionen"
+
+#: ../param/loadparm.c:1000
+msgid "Logon Options"
+msgstr "Login optionen"
+
+#: ../param/loadparm.c:1019
+msgid "Browse Options"
+msgstr "Browsing Optionen"
+
+#: ../param/loadparm.c:1033
+msgid "WINS Options"
+msgstr "WINS Optionen"
+
+#: ../param/loadparm.c:1043
+msgid "Locking Options"
+msgstr "Locking Optionen"
+
+#: ../param/loadparm.c:1061
+msgid "Ldap Options"
+msgstr "LDAP Optionen"
+
+#: ../param/loadparm.c:1078
+msgid "Miscellaneous Options"
+msgstr "Verschiedene Optionen"
+
+#: ../param/loadparm.c:1138
+msgid "VFS module options"
+msgstr "VFS Optionen"
+
+#: ../param/loadparm.c:1148
+msgid "Winbind options"
+msgstr "Winbind Optionen"
diff --git a/source/po/en.msg b/source/po/en.msg
new file mode 100644
index 00000000000..6a1fcb55bac
--- /dev/null
+++ b/source/po/en.msg
@@ -0,0 +1,593 @@
+# English messages for international release of SWAT.
+# Copyright (C) 2003 TAKAHASHI Motonobu <monyo@samba.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: i18n_swat \n"
+"POT-Creation-Date: 2003-10-06 05:30+0900\n"
+"PO-Revision-Date: 2000-02-08 12:48+09:00\n"
+"Last-Translator: TAKAHASHI Motonobu <monyo@samba.org>\n"
+"Language-Team: (Samba Team) <samba-technical@samba.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=US-ASCII\n"
+"Content-Transfer-Encoding: \n"
+
+#: ../web/swat.c:117
+#, c-format
+msgid "ERROR: Can't open %s"
+msgstr ""
+
+#: ../web/swat.c:200
+msgid "Help"
+msgstr ""
+
+#: ../web/swat.c:206 ../web/swat.c:220 ../web/swat.c:235 ../web/swat.c:243 ../web/swat.c:252 ../web/swat.c:261 ../web/swat.c:267 ../web/swat.c:273 ../web/swat.c:286
+msgid "Set Default"
+msgstr ""
+
+#: ../web/swat.c:408
+#, c-format
+msgid "failed to open %s for writing"
+msgstr ""
+
+#: ../web/swat.c:431
+#, c-format
+msgid "Can't reload %s"
+msgstr ""
+
+#: ../web/swat.c:501
+#, c-format
+msgid "Logged in as <b>%s</b>"
+msgstr ""
+
+#: ../web/swat.c:505
+msgid "Home"
+msgstr ""
+
+#: ../web/swat.c:507
+msgid "Globals"
+msgstr ""
+
+#: ../web/swat.c:508
+msgid "Shares"
+msgstr ""
+
+#: ../web/swat.c:509
+msgid "Printers"
+msgstr ""
+
+#: ../web/swat.c:510
+msgid "Wizard"
+msgstr ""
+
+#: ../web/swat.c:513
+msgid "Status"
+msgstr ""
+
+#: ../web/swat.c:514
+msgid "View Config"
+msgstr ""
+
+#: ../web/swat.c:516
+msgid "Password Management"
+msgstr ""
+
+#: ../web/swat.c:526
+msgid "Current View Is"
+msgstr ""
+
+#: ../web/swat.c:527 ../web/swat.c:530
+msgid "Basic"
+msgstr ""
+
+#: ../web/swat.c:528 ../web/swat.c:531
+msgid "Advanced"
+msgstr ""
+
+#: ../web/swat.c:529
+msgid "Change View To"
+msgstr ""
+
+#: ../web/swat.c:554
+msgid "Current Config"
+msgstr ""
+
+#: ../web/swat.c:558
+msgid "Normal View"
+msgstr ""
+
+#: ../web/swat.c:560
+msgid "Full View"
+msgstr ""
+
+#. Here we first set and commit all the parameters that were selected
+#. in the previous screen.
+#: ../web/swat.c:579
+msgid "Wizard Parameter Edit Page"
+msgstr ""
+
+#: ../web/swat.c:608
+msgid "Note: smb.conf file has been read and rewritten"
+msgstr ""
+
+#. Here we go ...
+#: ../web/swat.c:716
+msgid "Samba Configuration Wizard"
+msgstr ""
+
+#: ../web/swat.c:720
+msgid "The \"Rewrite smb.conf file\" button will clear the smb.conf file of all default values and of comments."
+msgstr ""
+
+#: ../web/swat.c:721
+msgid "The same will happen if you press the commit button."
+msgstr ""
+
+#: ../web/swat.c:724
+msgid "Rewrite smb.conf file"
+msgstr ""
+
+#: ../web/swat.c:725
+msgid "Commit"
+msgstr ""
+
+#: ../web/swat.c:726
+msgid "Edit Parameter Values"
+msgstr ""
+
+#: ../web/swat.c:732
+msgid "Server Type"
+msgstr ""
+
+#: ../web/swat.c:733
+msgid "Stand Alone"
+msgstr ""
+
+#: ../web/swat.c:734
+msgid "Domain Member"
+msgstr ""
+
+#: ../web/swat.c:735
+msgid "Domain Controller"
+msgstr ""
+
+#: ../web/swat.c:738
+msgid "Unusual Type in smb.conf - Please Select New Mode"
+msgstr ""
+
+#: ../web/swat.c:740
+msgid "Configure WINS As"
+msgstr ""
+
+#: ../web/swat.c:741
+msgid "Not Used"
+msgstr ""
+
+#: ../web/swat.c:742
+msgid "Server for client use"
+msgstr ""
+
+#: ../web/swat.c:743
+msgid "Client of another WINS server"
+msgstr ""
+
+#: ../web/swat.c:745
+msgid "Remote WINS Server"
+msgstr ""
+
+#: ../web/swat.c:756
+msgid "Error: WINS Server Mode and WINS Support both set in smb.conf"
+msgstr ""
+
+#: ../web/swat.c:757
+msgid "Please Select desired WINS mode above."
+msgstr ""
+
+#: ../web/swat.c:759
+msgid "Expose Home Directories"
+msgstr ""
+
+#: ../web/swat.c:774
+msgid "The above configuration options will set multiple parameters and will generally assist with rapid Samba deployment."
+msgstr ""
+
+#: ../web/swat.c:787
+msgid "Global Parameters"
+msgstr ""
+
+#: ../web/swat.c:815 ../web/swat.c:916 ../web/swat.c:1265
+msgid "Commit Changes"
+msgstr ""
+
+#: ../web/swat.c:819 ../web/swat.c:919 ../web/swat.c:1267
+msgid "Reset Values"
+msgstr ""
+
+#: ../web/swat.c:844
+msgid "Share Parameters"
+msgstr ""
+
+#: ../web/swat.c:887
+msgid "Choose Share"
+msgstr ""
+
+#: ../web/swat.c:901
+msgid "Delete Share"
+msgstr ""
+
+#: ../web/swat.c:908
+msgid "Create Share"
+msgstr ""
+
+#: ../web/swat.c:944
+msgid "password change in demo mode rejected"
+msgstr ""
+
+#: ../web/swat.c:957
+msgid "Can't setup password database vectors."
+msgstr ""
+
+#: ../web/swat.c:983
+msgid " Must specify \"User Name\" "
+msgstr ""
+
+#: ../web/swat.c:999
+msgid " Must specify \"Old Password\" "
+msgstr ""
+
+#: ../web/swat.c:1005
+msgid " Must specify \"Remote Machine\" "
+msgstr ""
+
+#: ../web/swat.c:1012
+msgid " Must specify \"New, and Re-typed Passwords\" "
+msgstr ""
+
+#: ../web/swat.c:1018
+msgid " Re-typed password didn't match new password "
+msgstr ""
+
+#: ../web/swat.c:1048
+#, c-format
+msgid " The passwd for '%s' has been changed."
+msgstr ""
+
+#: ../web/swat.c:1051
+#, c-format
+msgid " The passwd for '%s' has NOT been changed."
+msgstr ""
+
+#: ../web/swat.c:1076
+msgid "Server Password Management"
+msgstr ""
+
+#.
+#. * Create all the dialog boxes for data collection
+#.
+#: ../web/swat.c:1085 ../web/swat.c:1132
+msgid "User Name"
+msgstr ""
+
+#: ../web/swat.c:1088 ../web/swat.c:1134
+msgid "Old Password"
+msgstr ""
+
+#: ../web/swat.c:1091 ../web/swat.c:1136
+msgid "New Password"
+msgstr ""
+
+#: ../web/swat.c:1093 ../web/swat.c:1138
+msgid "Re-type New Password"
+msgstr ""
+
+#: ../web/swat.c:1101 ../web/swat.c:1149
+msgid "Change Password"
+msgstr ""
+
+#: ../web/swat.c:1104
+msgid "Add New User"
+msgstr ""
+
+#: ../web/swat.c:1106
+msgid "Delete User"
+msgstr ""
+
+#: ../web/swat.c:1108
+msgid "Disable User"
+msgstr ""
+
+#: ../web/swat.c:1110
+msgid "Enable User"
+msgstr ""
+
+#: ../web/swat.c:1123
+msgid "Client/Server Password Management"
+msgstr ""
+
+#: ../web/swat.c:1140
+msgid "Remote Machine"
+msgstr ""
+
+#: ../web/swat.c:1179
+msgid "Printer Parameters"
+msgstr ""
+
+#: ../web/swat.c:1181
+msgid "Important Note:"
+msgstr ""
+
+#: ../web/swat.c:1182
+msgid "Printer names marked with [*] in the Choose Printer drop-down box "
+msgstr ""
+
+#: ../web/swat.c:1183
+msgid "are autoloaded printers from "
+msgstr ""
+
+#: ../web/swat.c:1184
+msgid "Printcap Name"
+msgstr ""
+
+#: ../web/swat.c:1185
+msgid "Attempting to delete these printers from SWAT will have no effect."
+msgstr ""
+
+#: ../web/swat.c:1231
+msgid "Choose Printer"
+msgstr ""
+
+#: ../web/swat.c:1250
+msgid "Delete Printer"
+msgstr ""
+
+#: ../web/swat.c:1257
+msgid "Create Printer"
+msgstr ""
+
+#: ../web/statuspage.c:123
+msgid "RDONLY "
+msgstr ""
+
+#: ../web/statuspage.c:124
+msgid "WRONLY "
+msgstr ""
+
+#: ../web/statuspage.c:125
+msgid "RDWR "
+msgstr ""
+
+#: ../web/statuspage.c:309
+msgid "Server Status"
+msgstr ""
+
+#: ../web/statuspage.c:314
+msgid "Auto Refresh"
+msgstr ""
+
+#: ../web/statuspage.c:315 ../web/statuspage.c:320
+msgid "Refresh Interval: "
+msgstr ""
+
+#: ../web/statuspage.c:319
+msgid "Stop Refreshing"
+msgstr ""
+
+#: ../web/statuspage.c:334
+msgid "version:"
+msgstr ""
+
+#: ../web/statuspage.c:337
+msgid "smbd:"
+msgstr ""
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "running"
+msgstr ""
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "not running"
+msgstr ""
+
+#: ../web/statuspage.c:341
+msgid "Stop smbd"
+msgstr ""
+
+#: ../web/statuspage.c:343
+msgid "Start smbd"
+msgstr ""
+
+#: ../web/statuspage.c:345
+msgid "Restart smbd"
+msgstr ""
+
+#: ../web/statuspage.c:350
+msgid "nmbd:"
+msgstr ""
+
+#: ../web/statuspage.c:354
+msgid "Stop nmbd"
+msgstr ""
+
+#: ../web/statuspage.c:356
+msgid "Start nmbd"
+msgstr ""
+
+#: ../web/statuspage.c:358
+msgid "Restart nmbd"
+msgstr ""
+
+#: ../web/statuspage.c:364
+msgid "winbindd:"
+msgstr ""
+
+#: ../web/statuspage.c:368
+msgid "Stop winbindd"
+msgstr ""
+
+#: ../web/statuspage.c:370
+msgid "Start winbindd"
+msgstr ""
+
+#: ../web/statuspage.c:372
+msgid "Restart winbindd"
+msgstr ""
+
+#. stop, restart all
+#: ../web/statuspage.c:381
+msgid "Stop All"
+msgstr ""
+
+#: ../web/statuspage.c:382
+msgid "Restart All"
+msgstr ""
+
+#. start all
+#: ../web/statuspage.c:386
+msgid "Start All"
+msgstr ""
+
+#: ../web/statuspage.c:393
+msgid "Active Connections"
+msgstr ""
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "PID"
+msgstr ""
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408
+msgid "Client"
+msgstr ""
+
+#: ../web/statuspage.c:395
+msgid "IP address"
+msgstr ""
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "Date"
+msgstr ""
+
+#: ../web/statuspage.c:397
+msgid "Kill"
+msgstr ""
+
+#: ../web/statuspage.c:405
+msgid "Active Shares"
+msgstr ""
+
+#: ../web/statuspage.c:408
+msgid "Share"
+msgstr ""
+
+#: ../web/statuspage.c:408
+msgid "User"
+msgstr ""
+
+#: ../web/statuspage.c:408
+msgid "Group"
+msgstr ""
+
+#: ../web/statuspage.c:414
+msgid "Open Files"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "Sharing"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "R/W"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "Oplock"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "File"
+msgstr ""
+
+#: ../web/statuspage.c:425
+msgid "Show Client in col 1"
+msgstr ""
+
+#: ../web/statuspage.c:426
+msgid "Show PID in col 1"
+msgstr ""
+
+#: ../param/loadparm.c:755
+msgid "Base Options"
+msgstr ""
+
+#: ../param/loadparm.c:775
+msgid "Security Options"
+msgstr ""
+
+#: ../param/loadparm.c:859
+msgid "Logging Options"
+msgstr ""
+
+#: ../param/loadparm.c:874
+msgid "Protocol Options"
+msgstr ""
+
+#: ../param/loadparm.c:911
+msgid "Tuning Options"
+msgstr ""
+
+#: ../param/loadparm.c:940
+msgid "Printing Options"
+msgstr ""
+
+#: ../param/loadparm.c:970
+msgid "Filename Handling"
+msgstr ""
+
+#: ../param/loadparm.c:996
+msgid "Domain Options"
+msgstr ""
+
+#: ../param/loadparm.c:1000
+msgid "Logon Options"
+msgstr ""
+
+#: ../param/loadparm.c:1019
+msgid "Browse Options"
+msgstr ""
+
+#: ../param/loadparm.c:1033
+msgid "WINS Options"
+msgstr ""
+
+#: ../param/loadparm.c:1043
+msgid "Locking Options"
+msgstr ""
+
+#: ../param/loadparm.c:1061
+msgid "Ldap Options"
+msgstr ""
+
+#: ../param/loadparm.c:1078
+msgid "Miscellaneous Options"
+msgstr ""
+
+#: ../param/loadparm.c:1138
+msgid "VFS module options"
+msgstr ""
+
+#: ../param/loadparm.c:1148
+msgid "Winbind options"
+msgstr ""
diff --git a/source/po/fr.msg b/source/po/fr.msg
new file mode 100644
index 00000000000..134f1d6390b
--- /dev/null
+++ b/source/po/fr.msg
@@ -0,0 +1,593 @@
+# French messages for international release of SWAT.
+# Copyright (C) 2001 François Le Lay <fanch@tuxfamily.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: i18n_swat \n"
+"POT-Creation-Date: 2003-10-06 05:30+0900\n"
+"PO-Revision-Date: 2000-02-08 14:45+0100\n"
+"Last-Translator: François Le Lay <fanch@tuxfamily.org>\n"
+"Language-Team: (Samba Team) <samba-technical@samba.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-1\n"
+"Content-Transfer-Encoding: \n"
+
+#: ../web/swat.c:117
+#, c-format
+msgid "ERROR: Can't open %s"
+msgstr ""
+
+#: ../web/swat.c:200
+msgid "Help"
+msgstr "Aide"
+
+#: ../web/swat.c:206 ../web/swat.c:220 ../web/swat.c:235 ../web/swat.c:243 ../web/swat.c:252 ../web/swat.c:261 ../web/swat.c:267 ../web/swat.c:273 ../web/swat.c:286
+msgid "Set Default"
+msgstr "Définir par défaut"
+
+#: ../web/swat.c:408
+#, c-format
+msgid "failed to open %s for writing"
+msgstr ""
+
+#: ../web/swat.c:431
+#, c-format
+msgid "Can't reload %s"
+msgstr ""
+
+#: ../web/swat.c:501
+#, c-format
+msgid "Logged in as <b>%s</b>"
+msgstr "Connecté en tant que <b>%s</b>"
+
+#: ../web/swat.c:505
+msgid "Home"
+msgstr "Home"
+
+#: ../web/swat.c:507
+msgid "Globals"
+msgstr "Paramètres Généraux"
+
+#: ../web/swat.c:508
+msgid "Shares"
+msgstr "Partages"
+
+#: ../web/swat.c:509
+msgid "Printers"
+msgstr "Imprimantes"
+
+#: ../web/swat.c:510
+msgid "Wizard"
+msgstr ""
+
+#: ../web/swat.c:513
+msgid "Status"
+msgstr "Statut"
+
+#: ../web/swat.c:514
+msgid "View Config"
+msgstr "Voir Configuration"
+
+#: ../web/swat.c:516
+msgid "Password Management"
+msgstr "Gestion des mots de passe"
+
+#: ../web/swat.c:526
+msgid "Current View Is"
+msgstr "Configuration Actuelle"
+
+#: ../web/swat.c:527 ../web/swat.c:530
+msgid "Basic"
+msgstr "Vue Basique"
+
+#: ../web/swat.c:528 ../web/swat.c:531
+msgid "Advanced"
+msgstr "Vue Détaillée"
+
+#: ../web/swat.c:529
+msgid "Change View To"
+msgstr "Modifier le mot de passe"
+
+#: ../web/swat.c:554
+msgid "Current Config"
+msgstr "Configuration Actuelle"
+
+#: ../web/swat.c:558
+msgid "Normal View"
+msgstr "Vue Normale"
+
+#: ../web/swat.c:560
+msgid "Full View"
+msgstr "Vue Complète"
+
+#. Here we first set and commit all the parameters that were selected
+#. in the previous screen.
+#: ../web/swat.c:579
+msgid "Wizard Parameter Edit Page"
+msgstr ""
+
+#: ../web/swat.c:608
+msgid "Note: smb.conf file has been read and rewritten"
+msgstr ""
+
+#. Here we go ...
+#: ../web/swat.c:716
+msgid "Samba Configuration Wizard"
+msgstr ""
+
+#: ../web/swat.c:720
+msgid "The \"Rewrite smb.conf file\" button will clear the smb.conf file of all default values and of comments."
+msgstr ""
+
+#: ../web/swat.c:721
+msgid "The same will happen if you press the commit button."
+msgstr ""
+
+#: ../web/swat.c:724
+msgid "Rewrite smb.conf file"
+msgstr ""
+
+#: ../web/swat.c:725
+msgid "Commit"
+msgstr "commentaire"
+
+#: ../web/swat.c:726
+msgid "Edit Parameter Values"
+msgstr "Paramètres Imprimantes"
+
+#: ../web/swat.c:732
+msgid "Server Type"
+msgstr ""
+
+#: ../web/swat.c:733
+msgid "Stand Alone"
+msgstr ""
+
+#: ../web/swat.c:734
+msgid "Domain Member"
+msgstr "master de domaine"
+
+#: ../web/swat.c:735
+msgid "Domain Controller"
+msgstr "master de domaine"
+
+#: ../web/swat.c:738
+msgid "Unusual Type in smb.conf - Please Select New Mode"
+msgstr ""
+
+#: ../web/swat.c:740
+msgid "Configure WINS As"
+msgstr ""
+
+#: ../web/swat.c:741
+msgid "Not Used"
+msgstr "ne pas descendre"
+
+#: ../web/swat.c:742
+msgid "Server for client use"
+msgstr ""
+
+#: ../web/swat.c:743
+msgid "Client of another WINS server"
+msgstr ""
+
+#: ../web/swat.c:745
+msgid "Remote WINS Server"
+msgstr ""
+
+#: ../web/swat.c:756
+msgid "Error: WINS Server Mode and WINS Support both set in smb.conf"
+msgstr ""
+
+#: ../web/swat.c:757
+msgid "Please Select desired WINS mode above."
+msgstr ""
+
+#: ../web/swat.c:759
+msgid "Expose Home Directories"
+msgstr ""
+
+#: ../web/swat.c:774
+msgid "The above configuration options will set multiple parameters and will generally assist with rapid Samba deployment."
+msgstr ""
+
+#: ../web/swat.c:787
+msgid "Global Parameters"
+msgstr "Variables Globales"
+
+#: ../web/swat.c:815 ../web/swat.c:916 ../web/swat.c:1265
+msgid "Commit Changes"
+msgstr "Sauver les modifications"
+
+#: ../web/swat.c:819 ../web/swat.c:919 ../web/swat.c:1267
+msgid "Reset Values"
+msgstr "Réinitialiser Valeurs"
+
+#: ../web/swat.c:844
+msgid "Share Parameters"
+msgstr "Paramètres de partage"
+
+#: ../web/swat.c:887
+msgid "Choose Share"
+msgstr "Choisir un partage"
+
+#: ../web/swat.c:901
+msgid "Delete Share"
+msgstr "Supprimer un partage"
+
+#: ../web/swat.c:908
+msgid "Create Share"
+msgstr "Créer un partage"
+
+#: ../web/swat.c:944
+msgid "password change in demo mode rejected"
+msgstr "changement de mot de passe en mode démo rejeté"
+
+#: ../web/swat.c:957
+msgid "Can't setup password database vectors."
+msgstr ""
+
+#: ../web/swat.c:983
+msgid " Must specify \"User Name\" "
+msgstr " Le champ \"Nom d'utilisateur\" doit être spécifié"
+
+#: ../web/swat.c:999
+msgid " Must specify \"Old Password\" "
+msgstr " Le champ \"Ancien mot de passe\" doît être spécifié"
+
+#: ../web/swat.c:1005
+msgid " Must specify \"Remote Machine\" "
+msgstr " Le champ \"Machine Distante\" doît être spécifié"
+
+#: ../web/swat.c:1012
+msgid " Must specify \"New, and Re-typed Passwords\" "
+msgstr "Les champs \"Nouveau mot de passe\" et \"Confirmation du nouveau mot de passe\" doivent être spécifiés \n"
+
+#: ../web/swat.c:1018
+msgid " Re-typed password didn't match new password "
+msgstr " Echec de la confirmation du nouveau mot de passe"
+
+#: ../web/swat.c:1048
+#, c-format
+msgid " The passwd for '%s' has been changed."
+msgstr " Le mot de passe de '%s' a été modifié. "
+
+#: ../web/swat.c:1051
+#, c-format
+msgid " The passwd for '%s' has NOT been changed."
+msgstr " Le mot de passe de '%s' n'a PAS été modifié. \n"
+
+#: ../web/swat.c:1076
+msgid "Server Password Management"
+msgstr "Gestion des mots de passe serveur"
+
+#.
+#. * Create all the dialog boxes for data collection
+#.
+#: ../web/swat.c:1085 ../web/swat.c:1132
+msgid "User Name"
+msgstr " Nom d'utilisateur : "
+
+#: ../web/swat.c:1088 ../web/swat.c:1134
+msgid "Old Password"
+msgstr " Ancien mot de passe : "
+
+#: ../web/swat.c:1091 ../web/swat.c:1136
+msgid "New Password"
+msgstr " Nouveau mot de passe : "
+
+#: ../web/swat.c:1093 ../web/swat.c:1138
+msgid "Re-type New Password"
+msgstr " Confirmation du nouveau mot de passe : "
+
+#: ../web/swat.c:1101 ../web/swat.c:1149
+msgid "Change Password"
+msgstr "Modifier le mot de passe"
+
+#: ../web/swat.c:1104
+msgid "Add New User"
+msgstr "Nouvel Utilisateur"
+
+#: ../web/swat.c:1106
+msgid "Delete User"
+msgstr "Supprimer Utilisateur"
+
+#: ../web/swat.c:1108
+msgid "Disable User"
+msgstr "Désactiver Utilisateur"
+
+#: ../web/swat.c:1110
+msgid "Enable User"
+msgstr "Activer Utilisateur"
+
+#: ../web/swat.c:1123
+msgid "Client/Server Password Management"
+msgstr "Gestion des mots de passe Client/Serveur"
+
+#: ../web/swat.c:1140
+msgid "Remote Machine"
+msgstr " Machine distante : "
+
+#: ../web/swat.c:1179
+msgid "Printer Parameters"
+msgstr "Paramètres Imprimantes"
+
+#: ../web/swat.c:1181
+msgid "Important Note:"
+msgstr "Note Importante:"
+
+#: ../web/swat.c:1182
+msgid "Printer names marked with [*] in the Choose Printer drop-down box "
+msgstr "Les Noms d'imprimantes marqués du signe [*] dans le menu déroulant Choisir Imprimante"
+
+#: ../web/swat.c:1183
+msgid "are autoloaded printers from "
+msgstr "désignent des imprimantes automatiquement chargées depuis le "
+
+#: ../web/swat.c:1184
+msgid "Printcap Name"
+msgstr "Nom Printcap"
+
+#: ../web/swat.c:1185
+msgid "Attempting to delete these printers from SWAT will have no effect."
+msgstr "Essayer de supprimer ces imprimantes depuis SWAT n'aura aucun effet."
+
+#: ../web/swat.c:1231
+msgid "Choose Printer"
+msgstr "Choisir Imprimante"
+
+#: ../web/swat.c:1250
+msgid "Delete Printer"
+msgstr "Supprimer Imprimante"
+
+#: ../web/swat.c:1257
+msgid "Create Printer"
+msgstr "Créer Imprimante"
+
+#: ../web/statuspage.c:123
+msgid "RDONLY "
+msgstr ""
+
+#: ../web/statuspage.c:124
+msgid "WRONLY "
+msgstr ""
+
+#: ../web/statuspage.c:125
+msgid "RDWR "
+msgstr ""
+
+#: ../web/statuspage.c:309
+msgid "Server Status"
+msgstr "Statut du Serveur"
+
+#: ../web/statuspage.c:314
+msgid "Auto Refresh"
+msgstr "Rafraîchissement Automatique"
+
+#: ../web/statuspage.c:315 ../web/statuspage.c:320
+msgid "Refresh Interval: "
+msgstr "Intervalle de rafraîchissement: "
+
+#: ../web/statuspage.c:319
+msgid "Stop Refreshing"
+msgstr "Stopper Rafraîchissement"
+
+#: ../web/statuspage.c:334
+msgid "version:"
+msgstr "version:"
+
+#: ../web/statuspage.c:337
+msgid "smbd:"
+msgstr ""
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "running"
+msgstr "actif"
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "not running"
+msgstr "non actif"
+
+#: ../web/statuspage.c:341
+msgid "Stop smbd"
+msgstr "Stopper smbd"
+
+#: ../web/statuspage.c:343
+msgid "Start smbd"
+msgstr "Lancer smbd"
+
+#: ../web/statuspage.c:345
+msgid "Restart smbd"
+msgstr "Relancer smbd"
+
+#: ../web/statuspage.c:350
+msgid "nmbd:"
+msgstr ""
+
+#: ../web/statuspage.c:354
+msgid "Stop nmbd"
+msgstr "Stopper nmbd"
+
+#: ../web/statuspage.c:356
+msgid "Start nmbd"
+msgstr "Lancer nmbd"
+
+#: ../web/statuspage.c:358
+msgid "Restart nmbd"
+msgstr "Relancer nmbd"
+
+#: ../web/statuspage.c:364
+msgid "winbindd:"
+msgstr ""
+
+#: ../web/statuspage.c:368
+msgid "Stop winbindd"
+msgstr "Stopper nmbd"
+
+#: ../web/statuspage.c:370
+msgid "Start winbindd"
+msgstr "Lancer nmbd"
+
+#: ../web/statuspage.c:372
+msgid "Restart winbindd"
+msgstr "Relancer nmbd"
+
+#. stop, restart all
+#: ../web/statuspage.c:381
+msgid "Stop All"
+msgstr ""
+
+#: ../web/statuspage.c:382
+msgid "Restart All"
+msgstr "Relancer nmbd"
+
+#. start all
+#: ../web/statuspage.c:386
+msgid "Start All"
+msgstr "Lancer nmbd"
+
+#: ../web/statuspage.c:393
+msgid "Active Connections"
+msgstr "Connections Actives"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "PID"
+msgstr ""
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408
+msgid "Client"
+msgstr ""
+
+#: ../web/statuspage.c:395
+msgid "IP address"
+msgstr "adresse IP"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "Date"
+msgstr "Date"
+
+#: ../web/statuspage.c:397
+msgid "Kill"
+msgstr "Terminer"
+
+#: ../web/statuspage.c:405
+msgid "Active Shares"
+msgstr "Partages Actifs"
+
+#: ../web/statuspage.c:408
+msgid "Share"
+msgstr "Partager"
+
+#: ../web/statuspage.c:408
+msgid "User"
+msgstr "Utilisateur"
+
+#: ../web/statuspage.c:408
+msgid "Group"
+msgstr "Groupe"
+
+#: ../web/statuspage.c:414
+msgid "Open Files"
+msgstr "Fichiers Ouverts"
+
+#: ../web/statuspage.c:416
+msgid "Sharing"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "R/W"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "Oplock"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "File"
+msgstr "Fichier"
+
+#: ../web/statuspage.c:425
+msgid "Show Client in col 1"
+msgstr ""
+
+#: ../web/statuspage.c:426
+msgid "Show PID in col 1"
+msgstr ""
+
+#: ../param/loadparm.c:755
+msgid "Base Options"
+msgstr "Options de base"
+
+#: ../param/loadparm.c:775
+msgid "Security Options"
+msgstr "Options de Sécurité"
+
+#: ../param/loadparm.c:859
+msgid "Logging Options"
+msgstr "Options de Logging"
+
+#: ../param/loadparm.c:874
+msgid "Protocol Options"
+msgstr "Options de Protocole"
+
+#: ../param/loadparm.c:911
+msgid "Tuning Options"
+msgstr "Options de réglage"
+
+#: ../param/loadparm.c:940
+msgid "Printing Options"
+msgstr "Options d'impression"
+
+#: ../param/loadparm.c:970
+msgid "Filename Handling"
+msgstr "Gestion des noms de fichier"
+
+#: ../param/loadparm.c:996
+msgid "Domain Options"
+msgstr "Options de Domaine"
+
+#: ../param/loadparm.c:1000
+msgid "Logon Options"
+msgstr "Options de Logon"
+
+#: ../param/loadparm.c:1019
+msgid "Browse Options"
+msgstr "Options de Navigation"
+
+#: ../param/loadparm.c:1033
+msgid "WINS Options"
+msgstr "Options WINS"
+
+#: ../param/loadparm.c:1043
+msgid "Locking Options"
+msgstr "Options de Verrouillage"
+
+#: ../param/loadparm.c:1061
+msgid "Ldap Options"
+msgstr "Options Ldap"
+
+#: ../param/loadparm.c:1078
+msgid "Miscellaneous Options"
+msgstr "Options Diverses"
+
+#: ../param/loadparm.c:1138
+msgid "VFS module options"
+msgstr "Options VFS"
+
+#: ../param/loadparm.c:1148
+msgid "Winbind options"
+msgstr "Options Winbind"
diff --git a/source/po/genmsg b/source/po/genmsg
new file mode 100755
index 00000000000..08d5bd222f8
--- /dev/null
+++ b/source/po/genmsg
@@ -0,0 +1,40 @@
+#!/bin/sh
+# Copyright (C) 2003 TAKAHASHI Motonobu <monyo@samba.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+FILES='../web/swat.c ../web/statuspage.c ../param/loadparm.c'
+LANGS='en ja tr pl fr de it'
+
+XGETTEXT=xgettext
+MSGMERGE=msgmerge
+
+WIDTH=256
+
+$XGETTEXT --default-domain="i18n_swat" \
+ --add-comments \
+ --keyword=_ --keyword=N_ \
+ --width=${WIDTH} \
+ $FILES
+
+for lang in $LANGS; do
+ echo -n $lang
+ mv ${lang}.msg ${lang}.msg.old
+ $MSGMERGE --width=${WIDTH} ${lang}.msg.old i18n_swat.po -o ${lang}.msg
+done
+
+rm i18n_swat.po
+
diff --git a/source/po/it.msg b/source/po/it.msg
new file mode 100644
index 00000000000..be447a84d47
--- /dev/null
+++ b/source/po/it.msg
@@ -0,0 +1,593 @@
+# Italian messages for international release of SWAT.
+# Copyright (C) 2001 Simo Sorce <idra@samba.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: i18n_swat \n"
+"POT-Creation-Date: 2003-10-06 05:30+0900\n"
+"PO-Revision-Date: 2000-02-08 14:45+0100\n"
+"Last-Translator: Simo Sorce <idra@samba.org>\n"
+"Language-Team: (Samba Team) <samba-technical@samba.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-1\n"
+"Content-Transfer-Encoding: \n"
+
+#: ../web/swat.c:117
+#, c-format
+msgid "ERROR: Can't open %s"
+msgstr ""
+
+#: ../web/swat.c:200
+msgid "Help"
+msgstr "Aiuto"
+
+#: ../web/swat.c:206 ../web/swat.c:220 ../web/swat.c:235 ../web/swat.c:243 ../web/swat.c:252 ../web/swat.c:261 ../web/swat.c:267 ../web/swat.c:273 ../web/swat.c:286
+msgid "Set Default"
+msgstr "Imposta Default"
+
+#: ../web/swat.c:408
+#, c-format
+msgid "failed to open %s for writing"
+msgstr ""
+
+#: ../web/swat.c:431
+#, c-format
+msgid "Can't reload %s"
+msgstr ""
+
+#: ../web/swat.c:501
+#, c-format
+msgid "Logged in as <b>%s</b>"
+msgstr "Connesso come <b>%s</b><p>\n"
+
+#: ../web/swat.c:505
+msgid "Home"
+msgstr "Home"
+
+#: ../web/swat.c:507
+msgid "Globals"
+msgstr "Globali"
+
+#: ../web/swat.c:508
+msgid "Shares"
+msgstr "Condivisioni"
+
+#: ../web/swat.c:509
+msgid "Printers"
+msgstr "Stampanti"
+
+#: ../web/swat.c:510
+msgid "Wizard"
+msgstr ""
+
+#: ../web/swat.c:513
+msgid "Status"
+msgstr "Stato"
+
+#: ../web/swat.c:514
+msgid "View Config"
+msgstr "Visualizza Configurazione"
+
+#: ../web/swat.c:516
+msgid "Password Management"
+msgstr "Gestione Password"
+
+#: ../web/swat.c:526
+msgid "Current View Is"
+msgstr "Configurazione Attuale"
+
+#: ../web/swat.c:527 ../web/swat.c:530
+msgid "Basic"
+msgstr "Vista Semplice"
+
+#: ../web/swat.c:528 ../web/swat.c:531
+msgid "Advanced"
+msgstr "Vista Avanzata"
+
+#: ../web/swat.c:529
+msgid "Change View To"
+msgstr "Cambia Password"
+
+#: ../web/swat.c:554
+msgid "Current Config"
+msgstr "Configurazione Attuale"
+
+#: ../web/swat.c:558
+msgid "Normal View"
+msgstr "Vista Normale"
+
+#: ../web/swat.c:560
+msgid "Full View"
+msgstr "Vista Completa"
+
+#. Here we first set and commit all the parameters that were selected
+#. in the previous screen.
+#: ../web/swat.c:579
+msgid "Wizard Parameter Edit Page"
+msgstr ""
+
+#: ../web/swat.c:608
+msgid "Note: smb.conf file has been read and rewritten"
+msgstr ""
+
+#. Here we go ...
+#: ../web/swat.c:716
+msgid "Samba Configuration Wizard"
+msgstr ""
+
+#: ../web/swat.c:720
+msgid "The \"Rewrite smb.conf file\" button will clear the smb.conf file of all default values and of comments."
+msgstr ""
+
+#: ../web/swat.c:721
+msgid "The same will happen if you press the commit button."
+msgstr ""
+
+#: ../web/swat.c:724
+msgid "Rewrite smb.conf file"
+msgstr ""
+
+#: ../web/swat.c:725
+msgid "Commit"
+msgstr "commento"
+
+#: ../web/swat.c:726
+msgid "Edit Parameter Values"
+msgstr "Parametri Stampante"
+
+#: ../web/swat.c:732
+msgid "Server Type"
+msgstr ""
+
+#: ../web/swat.c:733
+msgid "Stand Alone"
+msgstr ""
+
+#: ../web/swat.c:734
+msgid "Domain Member"
+msgstr "master dominio"
+
+#: ../web/swat.c:735
+msgid "Domain Controller"
+msgstr "master dominio"
+
+#: ../web/swat.c:738
+msgid "Unusual Type in smb.conf - Please Select New Mode"
+msgstr ""
+
+#: ../web/swat.c:740
+msgid "Configure WINS As"
+msgstr ""
+
+#: ../web/swat.c:741
+msgid "Not Used"
+msgstr "non discendere"
+
+#: ../web/swat.c:742
+msgid "Server for client use"
+msgstr ""
+
+#: ../web/swat.c:743
+msgid "Client of another WINS server"
+msgstr ""
+
+#: ../web/swat.c:745
+msgid "Remote WINS Server"
+msgstr ""
+
+#: ../web/swat.c:756
+msgid "Error: WINS Server Mode and WINS Support both set in smb.conf"
+msgstr ""
+
+#: ../web/swat.c:757
+msgid "Please Select desired WINS mode above."
+msgstr ""
+
+#: ../web/swat.c:759
+msgid "Expose Home Directories"
+msgstr ""
+
+#: ../web/swat.c:774
+msgid "The above configuration options will set multiple parameters and will generally assist with rapid Samba deployment."
+msgstr ""
+
+#: ../web/swat.c:787
+msgid "Global Parameters"
+msgstr "Variabili Globali"
+
+#: ../web/swat.c:815 ../web/swat.c:916 ../web/swat.c:1265
+msgid "Commit Changes"
+msgstr "Salva Modifiche"
+
+#: ../web/swat.c:819 ../web/swat.c:919 ../web/swat.c:1267
+msgid "Reset Values"
+msgstr "Resetta Valori"
+
+#: ../web/swat.c:844
+msgid "Share Parameters"
+msgstr "Parametri Condivisioni"
+
+#: ../web/swat.c:887
+msgid "Choose Share"
+msgstr "Scegli Condivisione"
+
+#: ../web/swat.c:901
+msgid "Delete Share"
+msgstr "Cancella Condivisione"
+
+#: ../web/swat.c:908
+msgid "Create Share"
+msgstr "Crea Condivisione"
+
+#: ../web/swat.c:944
+msgid "password change in demo mode rejected"
+msgstr "cambio password in modalita' demo rigettata"
+
+#: ../web/swat.c:957
+msgid "Can't setup password database vectors."
+msgstr ""
+
+#: ../web/swat.c:983
+msgid " Must specify \"User Name\" "
+msgstr " \"Nome Utente\" deve essere specificato "
+
+#: ../web/swat.c:999
+msgid " Must specify \"Old Password\" "
+msgstr " \"Vecchia Password\" deve essere specificato "
+
+#: ../web/swat.c:1005
+msgid " Must specify \"Remote Machine\" "
+msgstr " \"Macchina Remota\" deve essere specificato "
+
+#: ../web/swat.c:1012
+msgid " Must specify \"New, and Re-typed Passwords\" "
+msgstr " \"Nuova/Conferma Password\" devono essere specificati "
+
+#: ../web/swat.c:1018
+msgid " Re-typed password didn't match new password "
+msgstr " la password di conferma non e' uguale alla nuova password "
+
+#: ../web/swat.c:1048
+#, c-format
+msgid " The passwd for '%s' has been changed."
+msgstr " La password per '%s' e' stata cambiata."
+
+#: ../web/swat.c:1051
+#, c-format
+msgid " The passwd for '%s' has NOT been changed."
+msgstr " La password per '%s' non e' stata cambianta."
+
+#: ../web/swat.c:1076
+msgid "Server Password Management"
+msgstr "Gestione Password del Server"
+
+#.
+#. * Create all the dialog boxes for data collection
+#.
+#: ../web/swat.c:1085 ../web/swat.c:1132
+msgid "User Name"
+msgstr " Nome Utente"
+
+#: ../web/swat.c:1088 ../web/swat.c:1134
+msgid "Old Password"
+msgstr " Vecchia Password"
+
+#: ../web/swat.c:1091 ../web/swat.c:1136
+msgid "New Password"
+msgstr " Nuova Password"
+
+#: ../web/swat.c:1093 ../web/swat.c:1138
+msgid "Re-type New Password"
+msgstr " Conferma nuova Password"
+
+#: ../web/swat.c:1101 ../web/swat.c:1149
+msgid "Change Password"
+msgstr "Cambia Password"
+
+#: ../web/swat.c:1104
+msgid "Add New User"
+msgstr "Aggiungi Nuovo Utente"
+
+#: ../web/swat.c:1106
+msgid "Delete User"
+msgstr "Cancella Utente"
+
+#: ../web/swat.c:1108
+msgid "Disable User"
+msgstr "Disabilita Utente"
+
+#: ../web/swat.c:1110
+msgid "Enable User"
+msgstr "Abilita Utente"
+
+#: ../web/swat.c:1123
+msgid "Client/Server Password Management"
+msgstr "Gestione Password Client/Server"
+
+#: ../web/swat.c:1140
+msgid "Remote Machine"
+msgstr " Macchina Remota"
+
+#: ../web/swat.c:1179
+msgid "Printer Parameters"
+msgstr "Parametri Stampante"
+
+#: ../web/swat.c:1181
+msgid "Important Note:"
+msgstr "Nota Importante:"
+
+#: ../web/swat.c:1182
+msgid "Printer names marked with [*] in the Choose Printer drop-down box "
+msgstr "nomi di stampante marcati con [*] nel riquadro a scomparsa Scegli Stampante"
+
+#: ../web/swat.c:1183
+msgid "are autoloaded printers from "
+msgstr "sono stampanti caricate automaticamente da "
+
+#: ../web/swat.c:1184
+msgid "Printcap Name"
+msgstr "Nome Printcap"
+
+#: ../web/swat.c:1185
+msgid "Attempting to delete these printers from SWAT will have no effect."
+msgstr "Il tentativo di cancellare queste stampanti da sWAT non avara' effetto.\n"
+
+#: ../web/swat.c:1231
+msgid "Choose Printer"
+msgstr "Scegli Stampante"
+
+#: ../web/swat.c:1250
+msgid "Delete Printer"
+msgstr "Cancella Stampante"
+
+#: ../web/swat.c:1257
+msgid "Create Printer"
+msgstr "Crea Stampante"
+
+#: ../web/statuspage.c:123
+msgid "RDONLY "
+msgstr ""
+
+#: ../web/statuspage.c:124
+msgid "WRONLY "
+msgstr ""
+
+#: ../web/statuspage.c:125
+msgid "RDWR "
+msgstr ""
+
+#: ../web/statuspage.c:309
+msgid "Server Status"
+msgstr "Stato del Server"
+
+#: ../web/statuspage.c:314
+msgid "Auto Refresh"
+msgstr "Rinfresco Automatico"
+
+#: ../web/statuspage.c:315 ../web/statuspage.c:320
+msgid "Refresh Interval: "
+msgstr "Intervallo Rinfresco: "
+
+#: ../web/statuspage.c:319
+msgid "Stop Refreshing"
+msgstr "Ferma Rinfresco"
+
+#: ../web/statuspage.c:334
+msgid "version:"
+msgstr "versione:"
+
+#: ../web/statuspage.c:337
+msgid "smbd:"
+msgstr ""
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "running"
+msgstr "attivo"
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "not running"
+msgstr "non attivo"
+
+#: ../web/statuspage.c:341
+msgid "Stop smbd"
+msgstr "Ferma smbd"
+
+#: ../web/statuspage.c:343
+msgid "Start smbd"
+msgstr "Lancia smbd"
+
+#: ../web/statuspage.c:345
+msgid "Restart smbd"
+msgstr "Rilancia smbd"
+
+#: ../web/statuspage.c:350
+msgid "nmbd:"
+msgstr ""
+
+#: ../web/statuspage.c:354
+msgid "Stop nmbd"
+msgstr "Ferma nmbd"
+
+#: ../web/statuspage.c:356
+msgid "Start nmbd"
+msgstr "Lancia nmbd"
+
+#: ../web/statuspage.c:358
+msgid "Restart nmbd"
+msgstr "Rilancia nmbd"
+
+#: ../web/statuspage.c:364
+msgid "winbindd:"
+msgstr ""
+
+#: ../web/statuspage.c:368
+msgid "Stop winbindd"
+msgstr "Ferma nmbd"
+
+#: ../web/statuspage.c:370
+msgid "Start winbindd"
+msgstr "Lancia nmbd"
+
+#: ../web/statuspage.c:372
+msgid "Restart winbindd"
+msgstr "Rilancia nmbd"
+
+#. stop, restart all
+#: ../web/statuspage.c:381
+msgid "Stop All"
+msgstr ""
+
+#: ../web/statuspage.c:382
+msgid "Restart All"
+msgstr "Rilancia nmbd"
+
+#. start all
+#: ../web/statuspage.c:386
+msgid "Start All"
+msgstr "Lancia nmbd"
+
+#: ../web/statuspage.c:393
+msgid "Active Connections"
+msgstr "Connessioni Attive"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "PID"
+msgstr ""
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408
+msgid "Client"
+msgstr ""
+
+#: ../web/statuspage.c:395
+msgid "IP address"
+msgstr "indirizzo IP"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "Date"
+msgstr "Data"
+
+#: ../web/statuspage.c:397
+msgid "Kill"
+msgstr "Termina"
+
+#: ../web/statuspage.c:405
+msgid "Active Shares"
+msgstr "Condivisioni Attive"
+
+#: ../web/statuspage.c:408
+msgid "Share"
+msgstr "Condivisione"
+
+#: ../web/statuspage.c:408
+msgid "User"
+msgstr "Utente"
+
+#: ../web/statuspage.c:408
+msgid "Group"
+msgstr "Gruppo"
+
+#: ../web/statuspage.c:414
+msgid "Open Files"
+msgstr "File Aperti"
+
+#: ../web/statuspage.c:416
+msgid "Sharing"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "R/W"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "Oplock"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "File"
+msgstr ""
+
+#: ../web/statuspage.c:425
+msgid "Show Client in col 1"
+msgstr ""
+
+#: ../web/statuspage.c:426
+msgid "Show PID in col 1"
+msgstr ""
+
+#: ../param/loadparm.c:755
+msgid "Base Options"
+msgstr "Opzioni Basilari"
+
+#: ../param/loadparm.c:775
+msgid "Security Options"
+msgstr "Opzioni di Sicurezza"
+
+#: ../param/loadparm.c:859
+msgid "Logging Options"
+msgstr "Opzioni di Log"
+
+#: ../param/loadparm.c:874
+msgid "Protocol Options"
+msgstr "Opzioni Protocollo"
+
+#: ../param/loadparm.c:911
+msgid "Tuning Options"
+msgstr "Opzioni Tuning"
+
+#: ../param/loadparm.c:940
+msgid "Printing Options"
+msgstr "Opzioni di Stampa"
+
+#: ../param/loadparm.c:970
+msgid "Filename Handling"
+msgstr "Gestione Nomi File"
+
+#: ../param/loadparm.c:996
+msgid "Domain Options"
+msgstr "Opzioni Dominio"
+
+#: ../param/loadparm.c:1000
+msgid "Logon Options"
+msgstr "Opzioni di Logon"
+
+#: ../param/loadparm.c:1019
+msgid "Browse Options"
+msgstr "Opzioni Browsing"
+
+#: ../param/loadparm.c:1033
+msgid "WINS Options"
+msgstr "opzioni WINS"
+
+#: ../param/loadparm.c:1043
+msgid "Locking Options"
+msgstr "Opzioni Locking"
+
+#: ../param/loadparm.c:1061
+msgid "Ldap Options"
+msgstr "Opzioni Ldap"
+
+#: ../param/loadparm.c:1078
+msgid "Miscellaneous Options"
+msgstr "Opzioni Generiche"
+
+#: ../param/loadparm.c:1138
+msgid "VFS module options"
+msgstr "Opzioni VFS"
+
+#: ../param/loadparm.c:1148
+msgid "Winbind options"
+msgstr "Opzioni Winbind"
diff --git a/source/po/ja.msg b/source/po/ja.msg
new file mode 100644
index 00000000000..affb2764141
--- /dev/null
+++ b/source/po/ja.msg
@@ -0,0 +1,595 @@
+# Japanese messages for international release of SWAT.
+# Copyright (C) 2003 TAKAHASHI Motonobu <monyo@samba.org>
+# Copyright (C) 2000 Ryo Kawahara <rkawa@lbe.co.jp>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: i18n-swat\n"
+"POT-Creation-Date: 2003-10-06 05:30+0900\n"
+"PO-Revision-Date: 2003-09-23 04:38+900\n"
+"Last-Translator: TAKAHASHI Motonobu <monyo@samba.org>\n"
+"Language-Team: Samba Users Group Japan <sugj-tech@samba.gr.jp>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CP932\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../web/swat.c:117
+#, c-format
+msgid "ERROR: Can't open %s"
+msgstr "%s ‚ðƒI[ƒvƒ“‚Å‚«‚Ü‚¹‚ñ"
+
+#: ../web/swat.c:200
+msgid "Help"
+msgstr "ƒwƒ‹ƒv"
+
+#: ../web/swat.c:206 ../web/swat.c:220 ../web/swat.c:235 ../web/swat.c:243 ../web/swat.c:252 ../web/swat.c:261 ../web/swat.c:267 ../web/swat.c:273 ../web/swat.c:286
+msgid "Set Default"
+msgstr "ƒfƒtƒHƒ‹ƒg’l"
+
+#: ../web/swat.c:408
+#, c-format
+msgid "failed to open %s for writing"
+msgstr "%s ‚ð‘‚«ž‚Ý—p‚ɃI[ƒvƒ“‚Å‚«‚Ü‚¹‚ñ"
+
+#: ../web/swat.c:431
+#, c-format
+msgid "Can't reload %s"
+msgstr "%s ‚ðÄ“Ç‚Ýž‚Ý‚Å‚«‚Ü‚¹‚ñ\n"
+
+# msgid "Logged in as <b>%s</b><p>\n"
+#: ../web/swat.c:501
+#, c-format
+msgid "Logged in as <b>%s</b>"
+msgstr "<b>%s</b>‚Æ‚µ‚ăƒOƒCƒ“"
+
+#: ../web/swat.c:505
+msgid "Home"
+msgstr "ƒz[ƒ€"
+
+#: ../web/swat.c:507
+msgid "Globals"
+msgstr "ƒOƒ[ƒoƒ‹"
+
+#: ../web/swat.c:508
+msgid "Shares"
+msgstr "ƒtƒ@ƒCƒ‹‹¤—L"
+
+#: ../web/swat.c:509
+msgid "Printers"
+msgstr "ˆóü‹¤—L"
+
+#: ../web/swat.c:510
+msgid "Wizard"
+msgstr "ƒEƒBƒU[ƒh"
+
+#: ../web/swat.c:513
+msgid "Status"
+msgstr "ƒT[ƒo‚Ìó‘Ô"
+
+#: ../web/swat.c:514
+msgid "View Config"
+msgstr "Œ»Ý‚ÌÝ’è"
+
+#: ../web/swat.c:516
+msgid "Password Management"
+msgstr "ƒpƒXƒ[ƒhŠÇ—"
+
+#: ../web/swat.c:526
+msgid "Current View Is"
+msgstr "Œ»Ý‚Ì•\Ž¦ƒ‚[ƒh"
+
+#: ../web/swat.c:527 ../web/swat.c:530
+msgid "Basic"
+msgstr "•W€•\Ž¦"
+
+#: ../web/swat.c:528 ../web/swat.c:531
+msgid "Advanced"
+msgstr "Úו\Ž¦"
+
+#: ../web/swat.c:529
+msgid "Change View To"
+msgstr "•\Ž¦ƒ‚[ƒh‚Ì•ÏX"
+
+#: ../web/swat.c:554
+msgid "Current Config"
+msgstr "Œ»Ý‚ÌÝ’è"
+
+#: ../web/swat.c:558
+msgid "Normal View"
+msgstr "•W€•\Ž¦"
+
+#: ../web/swat.c:560
+msgid "Full View"
+msgstr "Š®‘S•\Ž¦"
+
+#. Here we first set and commit all the parameters that were selected
+#. in the previous screen.
+#: ../web/swat.c:579
+msgid "Wizard Parameter Edit Page"
+msgstr "ƒEƒBƒU[ƒh‚É‚æ‚éƒpƒ‰ƒ[ƒ^•ÒWƒy[ƒW"
+
+#: ../web/swat.c:608
+msgid "Note: smb.conf file has been read and rewritten"
+msgstr "smb.conf ƒtƒ@ƒCƒ‹‚ª‘‚«Š·‚¦‚ç‚ê‚Ü‚µ‚½B"
+
+#. Here we go ...
+#: ../web/swat.c:716
+msgid "Samba Configuration Wizard"
+msgstr "Samba Ý’èƒEƒBƒU[ƒh"
+
+#: ../web/swat.c:720
+msgid "The \"Rewrite smb.conf file\" button will clear the smb.conf file of all default values and of comments."
+msgstr "usmb.conf ‚Ì‘Š·vƒ{ƒ^ƒ“‚ð‰Ÿ‚·‚Æ smb.conf ƒtƒ@ƒCƒ‹’†‚Ì‚·‚ׂẴfƒtƒHƒ‹ƒg’l‚âƒRƒƒ“ƒg‚Í휂³‚ê‚Ü‚·B"
+
+#: ../web/swat.c:721
+msgid "The same will happen if you press the commit button."
+msgstr "ucommitvƒ{ƒ^ƒ“‚ð‰Ÿ‚µ‚½ê‡‚É‚à“¯—l‚Ì•ÏX‚ªs‚È‚í‚ê‚Ü‚·B"
+
+#: ../web/swat.c:724
+msgid "Rewrite smb.conf file"
+msgstr "smb.conf ‚Ì‘Š·"
+
+#: ../web/swat.c:725
+msgid "Commit"
+msgstr "Ý’è‚𔽉f"
+
+#: ../web/swat.c:726
+msgid "Edit Parameter Values"
+msgstr "Šeƒpƒ‰ƒ[ƒ^‚Ì•ÒW"
+
+#: ../web/swat.c:732
+msgid "Server Type"
+msgstr "ƒT[ƒoƒ^ƒCƒv"
+
+#: ../web/swat.c:733
+msgid "Stand Alone"
+msgstr "ƒXƒ^ƒ“ƒhƒAƒƒ“"
+
+#: ../web/swat.c:734
+msgid "Domain Member"
+msgstr "ƒhƒƒCƒ“ƒƒ“ƒo"
+
+#: ../web/swat.c:735
+msgid "Domain Controller"
+msgstr "ƒhƒƒCƒ“ƒRƒ“ƒgƒ[ƒ‰"
+
+#: ../web/swat.c:738
+msgid "Unusual Type in smb.conf - Please Select New Mode"
+msgstr "’Êí‚ÌŒ`Ž®‚Å‚Í‚È‚¢ - V‚µ‚¢ƒ‚[ƒh‚ð‘I‘ð‚Ì‚±‚Æ"
+
+#: ../web/swat.c:740
+msgid "Configure WINS As"
+msgstr "WINS"
+
+#: ../web/swat.c:741
+msgid "Not Used"
+msgstr "Žg‚í‚È‚¢"
+
+#: ../web/swat.c:742
+msgid "Server for client use"
+msgstr "ƒT[ƒo‚Æ‚µ‚Ä\¬"
+
+#: ../web/swat.c:743
+msgid "Client of another WINS server"
+msgstr "•Ê‚Ì WINS ƒT[ƒo‚̃Nƒ‰ƒCƒAƒ“ƒg‚Æ‚µ‚Ä\¬"
+
+#: ../web/swat.c:745
+msgid "Remote WINS Server"
+msgstr "•Ê‚Ì WINS ƒT[ƒo"
+
+#: ../web/swat.c:756
+msgid "Error: WINS Server Mode and WINS Support both set in smb.conf"
+msgstr "ƒGƒ‰[: wins server ‚Æ wins support ‚Ì—¼ƒpƒ‰ƒ[ƒ^‚ª smb.conf ‚ÅŽw’肳‚ê‚Ä‚¢‚Ü‚·"
+
+#: ../web/swat.c:757
+msgid "Please Select desired WINS mode above."
+msgstr "‚¢‚¸‚ê‚©‚Ì WINS ƒ‚[ƒh‚ð‘I‘ð‚µ‚Ä‚­‚¾‚³‚¢B"
+
+#: ../web/swat.c:759
+msgid "Expose Home Directories"
+msgstr "ƒz[ƒ€ƒfƒBƒŒƒNƒgƒŠ‚ÌŒöŠJ"
+
+#: ../web/swat.c:774
+msgid "The above configuration options will set multiple parameters and will generally assist with rapid Samba deployment."
+msgstr "ã‹L‚ÌÝ’èƒIƒvƒVƒ‡ƒ“‚É‚æ‚èA•¡”‚̃pƒ‰ƒ[ƒ^‚ª“¯Šú‚µ‚Äݒ肳‚ê‚邽‚ßA Samba ‚̉^—p‚ðv‘¬‚ÉŠJŽn‚·‚éã‚Å‚Ì•‚¯‚Æ‚È‚é‚Å‚µ‚傤B"
+
+#: ../web/swat.c:787
+msgid "Global Parameters"
+msgstr "Global ƒpƒ‰ƒ[ƒ^"
+
+#: ../web/swat.c:815 ../web/swat.c:916 ../web/swat.c:1265
+msgid "Commit Changes"
+msgstr "•ÏX‚𔽉f"
+
+#: ../web/swat.c:819 ../web/swat.c:919 ../web/swat.c:1267
+msgid "Reset Values"
+msgstr "•ÏX‚ðŽæÁ"
+
+#: ../web/swat.c:844
+msgid "Share Parameters"
+msgstr "ƒtƒ@ƒCƒ‹‹¤—L ƒpƒ‰ƒ[ƒ^"
+
+#: ../web/swat.c:887
+msgid "Choose Share"
+msgstr "ƒtƒ@ƒCƒ‹‹¤—L‚Ì‘I‘ð"
+
+#: ../web/swat.c:901
+msgid "Delete Share"
+msgstr "ƒtƒ@ƒCƒ‹‹¤—L‚Ìíœ"
+
+#: ../web/swat.c:908
+msgid "Create Share"
+msgstr "ƒtƒ@ƒCƒ‹‹¤—L‚Ìì¬"
+
+#: ../web/swat.c:944
+msgid "password change in demo mode rejected"
+msgstr "ƒfƒ‚Eƒ‚[ƒh‚ł̃pƒXƒ[ƒh•ÏX‚Í‚Å‚«‚Ü‚¹‚ñ"
+
+#: ../web/swat.c:957
+msgid "Can't setup password database vectors."
+msgstr "ƒpƒXƒ[ƒhEƒf[ƒ^ƒx[ƒX‚ªŒ©‚‚¯‚ç‚ê‚Ü‚¹‚ñ"
+
+#: ../web/swat.c:983
+msgid " Must specify \"User Name\" "
+msgstr "uƒ†[ƒU–¼v—“‚É“ü—Í‚µ‚Ä‚­‚¾‚³‚¢"
+
+#: ../web/swat.c:999
+msgid " Must specify \"Old Password\" "
+msgstr "u‹ŒƒpƒXƒ[ƒhv—“‚É“ü—Í‚µ‚Ä‚­‚¾‚³‚¢"
+
+#: ../web/swat.c:1005
+msgid " Must specify \"Remote Machine\" "
+msgstr "uƒŠƒ‚[ƒgƒ}ƒVƒ“v—“‚É“ü—Í‚µ‚Ä‚­‚¾‚³‚¢"
+
+#: ../web/swat.c:1012
+msgid " Must specify \"New, and Re-typed Passwords\" "
+msgstr "uVƒpƒXƒ[ƒhv—“‚ÆuVƒpƒXƒ[ƒh‚ÌÄ“ü—Ív—“‚É“ü—Í‚µ‚Ä‚­‚¾‚³‚¢"
+
+#: ../web/swat.c:1018
+msgid " Re-typed password didn't match new password "
+msgstr "uVƒpƒXƒ[ƒh‚ÌÄ“ü—Ív—“‚Ì“ü—Í“à—e‚ªuVƒpƒXƒ[ƒhv—“‚Ì“ü—͂ƈê’v‚µ‚Ä‚¢‚Ü‚¹‚ñB"
+
+#: ../web/swat.c:1048
+#, c-format
+msgid " The passwd for '%s' has been changed."
+msgstr " %s ‚̃pƒXƒ[ƒh‚Í•ÏX‚³‚ê‚Ü‚µ‚½B"
+
+#: ../web/swat.c:1051
+#, c-format
+msgid " The passwd for '%s' has NOT been changed."
+msgstr " '%s' ‚̃pƒXƒ[ƒh‚Í•ÏX‚³‚ê‚Ü‚¹‚ñ‚Å‚µ‚½B"
+
+#: ../web/swat.c:1076
+msgid "Server Password Management"
+msgstr "ƒ[ƒJƒ‹ƒ}ƒVƒ“‚̃pƒXƒ[ƒhŠÇ—"
+
+#.
+#. * Create all the dialog boxes for data collection
+#.
+#: ../web/swat.c:1085 ../web/swat.c:1132
+msgid "User Name"
+msgstr "ƒ†[ƒU–¼"
+
+#: ../web/swat.c:1088 ../web/swat.c:1134
+msgid "Old Password"
+msgstr "‹ŒƒpƒXƒ[ƒh"
+
+#: ../web/swat.c:1091 ../web/swat.c:1136
+msgid "New Password"
+msgstr "VƒpƒXƒ[ƒh"
+
+#: ../web/swat.c:1093 ../web/swat.c:1138
+msgid "Re-type New Password"
+msgstr "VƒpƒXƒ[ƒh‚ÌÄ“ü—Í"
+
+#: ../web/swat.c:1101 ../web/swat.c:1149
+msgid "Change Password"
+msgstr "ƒpƒXƒ[ƒh•ÏX"
+
+#: ../web/swat.c:1104
+msgid "Add New User"
+msgstr "V‹Kƒ†[ƒU’ljÁ"
+
+#: ../web/swat.c:1106
+msgid "Delete User"
+msgstr "ƒ†[ƒU‚Ìíœ"
+
+#: ../web/swat.c:1108
+msgid "Disable User"
+msgstr "ƒ†[ƒU‚Ì–³Œø‰»"
+
+#: ../web/swat.c:1110
+msgid "Enable User"
+msgstr "ƒ†[ƒU‚Ì—LŒø‰»"
+
+#: ../web/swat.c:1123
+msgid "Client/Server Password Management"
+msgstr "ƒŠƒ‚[ƒgƒ}ƒVƒ“‚̃pƒXƒ[ƒhŠÇ—"
+
+#: ../web/swat.c:1140
+msgid "Remote Machine"
+msgstr "ƒŠƒ‚[ƒgƒ}ƒVƒ“"
+
+#: ../web/swat.c:1179
+msgid "Printer Parameters"
+msgstr "ˆóü‹¤—L ƒpƒ‰ƒ[ƒ^"
+
+#: ../web/swat.c:1181
+msgid "Important Note:"
+msgstr "*’"
+
+#: ../web/swat.c:1182
+msgid "Printer names marked with [*] in the Choose Printer drop-down box "
+msgstr "–¼‘O‚Ì擪‚É [*] ‚ª‚‚¢‚½ƒvƒŠƒ“ƒ^"
+
+#: ../web/swat.c:1183
+msgid "are autoloaded printers from "
+msgstr "‚Í"
+
+#: ../web/swat.c:1184
+msgid "Printcap Name"
+msgstr "printcap name ƒpƒ‰ƒ[ƒ^"
+
+#: ../web/swat.c:1185
+msgid "Attempting to delete these printers from SWAT will have no effect."
+msgstr "‚©‚玩“®Ý’肳‚ꂽ‚à‚Ì‚Å‚·‚©‚çA휂·‚邱‚Æ‚Í‚Å‚«‚Ü‚¹‚ñB"
+
+#: ../web/swat.c:1231
+msgid "Choose Printer"
+msgstr "ˆóü‹¤—L‚Ì‘I‘ð"
+
+#: ../web/swat.c:1250
+msgid "Delete Printer"
+msgstr "ˆóü‹¤—L‚Ìíœ"
+
+#: ../web/swat.c:1257
+msgid "Create Printer"
+msgstr "ˆóü‹¤—L‚Ìì¬"
+
+#: ../web/statuspage.c:123
+msgid "RDONLY "
+msgstr "ŽQÆ‚Ì‚Ý "
+
+#: ../web/statuspage.c:124
+msgid "WRONLY "
+msgstr "XV‚Ì‚Ý "
+
+#: ../web/statuspage.c:125
+msgid "RDWR "
+msgstr "ŽQÆ/XV "
+
+#: ../web/statuspage.c:309
+msgid "Server Status"
+msgstr "ƒT[ƒo‚Ìó‘Ô"
+
+#: ../web/statuspage.c:314
+msgid "Auto Refresh"
+msgstr "Ž©“®XV‚ÌŠJŽn"
+
+#: ../web/statuspage.c:315 ../web/statuspage.c:320
+msgid "Refresh Interval: "
+msgstr "XVŠÔŠu: "
+
+#: ../web/statuspage.c:319
+msgid "Stop Refreshing"
+msgstr "Ž©“®XV‚Ì’âŽ~"
+
+#: ../web/statuspage.c:334
+msgid "version:"
+msgstr "ƒo[ƒWƒ‡ƒ“"
+
+#: ../web/statuspage.c:337
+msgid "smbd:"
+msgstr ""
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "running"
+msgstr "ŽÀs’†"
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "not running"
+msgstr "’âŽ~’†"
+
+#: ../web/statuspage.c:341
+msgid "Stop smbd"
+msgstr "smbd ‚Ì’âŽ~"
+
+#: ../web/statuspage.c:343
+msgid "Start smbd"
+msgstr "smbd ‚Ì‹N“®"
+
+#: ../web/statuspage.c:345
+msgid "Restart smbd"
+msgstr "smbd ‚ÌÄ‹N“®"
+
+#: ../web/statuspage.c:350
+msgid "nmbd:"
+msgstr ""
+
+#: ../web/statuspage.c:354
+msgid "Stop nmbd"
+msgstr "nmbd ‚Ì’âŽ~"
+
+#: ../web/statuspage.c:356
+msgid "Start nmbd"
+msgstr "nmbd ‚Ì‹N“®"
+
+#: ../web/statuspage.c:358
+msgid "Restart nmbd"
+msgstr "nmbd ‚ÌÄ‹N“®"
+
+#: ../web/statuspage.c:364
+msgid "winbindd:"
+msgstr ""
+
+#: ../web/statuspage.c:368
+msgid "Stop winbindd"
+msgstr "winbindd ‚Ì’âŽ~"
+
+#: ../web/statuspage.c:370
+msgid "Start winbindd"
+msgstr "winbindd ‚Ì‹N“®"
+
+#: ../web/statuspage.c:372
+msgid "Restart winbindd"
+msgstr "winbindd ‚ÌÄ‹N“®"
+
+#. stop, restart all
+#: ../web/statuspage.c:381
+msgid "Stop All"
+msgstr "‚·‚ׂĒâŽ~"
+
+#: ../web/statuspage.c:382
+msgid "Restart All"
+msgstr "‚·‚ׂÄÄ‹N“®"
+
+#. start all
+#: ../web/statuspage.c:386
+msgid "Start All"
+msgstr "‚·‚ׂċN“®"
+
+#: ../web/statuspage.c:393
+msgid "Active Connections"
+msgstr "Ú‘±’†‚̃Nƒ‰ƒCƒAƒ“ƒg"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "PID"
+msgstr ""
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408
+msgid "Client"
+msgstr "ƒNƒ‰ƒCƒAƒ“ƒg"
+
+#: ../web/statuspage.c:395
+msgid "IP address"
+msgstr "IPƒAƒhƒŒƒX"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "Date"
+msgstr "“ú•t"
+
+#: ../web/statuspage.c:397
+msgid "Kill"
+msgstr "Ø’f"
+
+#: ../web/statuspage.c:405
+msgid "Active Shares"
+msgstr "Ú‘±’†‚Ì‹¤—L"
+
+#: ../web/statuspage.c:408
+msgid "Share"
+msgstr "‹¤—L–¼"
+
+#: ../web/statuspage.c:408
+msgid "User"
+msgstr "ƒ†[ƒU"
+
+#: ../web/statuspage.c:408
+msgid "Group"
+msgstr "ƒOƒ‹[ƒv"
+
+#: ../web/statuspage.c:414
+msgid "Open Files"
+msgstr "Žg—p’†‚̃tƒ@ƒCƒ‹"
+
+#: ../web/statuspage.c:416
+msgid "Sharing"
+msgstr "”r‘¼ƒ‚[ƒh"
+
+#: ../web/statuspage.c:416
+msgid "R/W"
+msgstr "ŽQÆ/XV"
+
+#: ../web/statuspage.c:416
+msgid "Oplock"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "File"
+msgstr "ƒtƒ@ƒCƒ‹–¼"
+
+#: ../web/statuspage.c:425
+msgid "Show Client in col 1"
+msgstr "ƒNƒ‰ƒCƒAƒ“ƒg–¼‚ð擪‚É•\Ž¦"
+
+#: ../web/statuspage.c:426
+msgid "Show PID in col 1"
+msgstr "PID‚ð擪‚É•\Ž¦"
+
+#: ../param/loadparm.c:755
+msgid "Base Options"
+msgstr "Šî–{ ƒIƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:775
+msgid "Security Options"
+msgstr "ƒZƒLƒ…ƒŠƒeƒB ƒIƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:859
+msgid "Logging Options"
+msgstr "ƒƒMƒ“ƒO ƒIƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:874
+msgid "Protocol Options"
+msgstr "ƒvƒƒgƒRƒ‹ ƒIƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:911
+msgid "Tuning Options"
+msgstr "ƒ`ƒ…[ƒjƒ“ƒO ƒIƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:940
+msgid "Printing Options"
+msgstr "ˆóü ƒIƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:970
+msgid "Filename Handling"
+msgstr "ƒtƒ@ƒCƒ‹–¼‚̎戵"
+
+#: ../param/loadparm.c:996
+msgid "Domain Options"
+msgstr "ƒhƒƒCƒ“ ƒIƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:1000
+msgid "Logon Options"
+msgstr "ƒƒOƒIƒ“ ƒIƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:1019
+msgid "Browse Options"
+msgstr "ƒuƒ‰ƒEƒWƒ“ƒO ƒIƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:1033
+msgid "WINS Options"
+msgstr "WINS ƒIƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:1043
+msgid "Locking Options"
+msgstr "ƒƒbƒLƒ“ƒO ƒIƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:1061
+msgid "Ldap Options"
+msgstr "LDAP ƒIƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:1078
+msgid "Miscellaneous Options"
+msgstr "‚»‚Ì‘¼‚̃IƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:1138
+msgid "VFS module options"
+msgstr "VFS ƒIƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:1148
+msgid "Winbind options"
+msgstr "Winbind ƒIƒvƒVƒ‡ƒ“"
diff --git a/source/po/nl.msg b/source/po/nl.msg
new file mode 100644
index 00000000000..8d7b050ce90
--- /dev/null
+++ b/source/po/nl.msg
@@ -0,0 +1,593 @@
+# Dutch messages for international release of SWAT.
+# Copyright (C) 2003 Jelmer Vernooij <jelmer@samba.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: i18n_swat \n"
+"POT-Creation-Date: 2003-10-06 05:30+0900\n"
+"PO-Revision-Date: 2000-02-08 12:48+09:00\n"
+"Last-Translator: Jelmer Vernooij <jelmer@samba.org>\n"
+"Language-Team: (Samba Team) <samba-technical@samba.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=US-ASCII\n"
+"Content-Transfer-Encoding: \n"
+
+#: ../web/swat.c:117
+#, c-format
+msgid "ERROR: Can't open %s"
+msgstr "FOUT: Kan %s niet openen"
+
+#: ../web/swat.c:200
+msgid "Help"
+msgstr "Help"
+
+#: ../web/swat.c:206 ../web/swat.c:220 ../web/swat.c:235 ../web/swat.c:243 ../web/swat.c:252 ../web/swat.c:261 ../web/swat.c:267 ../web/swat.c:273 ../web/swat.c:286
+msgid "Set Default"
+msgstr "Stel Standaard In"
+
+#: ../web/swat.c:408
+#, c-format
+msgid "failed to open %s for writing"
+msgstr "kon %s niet openen voor schrijven"
+
+#: ../web/swat.c:431
+#, c-format
+msgid "Can't reload %s"
+msgstr "Kan %s niet herladen"
+
+#: ../web/swat.c:501
+#, c-format
+msgid "Logged in as <b>%s</b>"
+msgstr "Ingelogd als <b>%s</b>"
+
+#: ../web/swat.c:505
+msgid "Home"
+msgstr "Home"
+
+#: ../web/swat.c:507
+msgid "Globals"
+msgstr "Algemene Instellingen"
+
+#: ../web/swat.c:508
+msgid "Shares"
+msgstr "Gedeelde Bronnen"
+
+#: ../web/swat.c:509
+msgid "Printers"
+msgstr "Printers"
+
+#: ../web/swat.c:510
+msgid "Wizard"
+msgstr "Wizard"
+
+#: ../web/swat.c:513
+msgid "Status"
+msgstr "Status"
+
+#: ../web/swat.c:514
+msgid "View Config"
+msgstr "Bekijk Configuratie"
+
+#: ../web/swat.c:516
+msgid "Password Management"
+msgstr "Wachtwoord Beheer"
+
+#: ../web/swat.c:526
+msgid "Current View Is"
+msgstr "Huidige weergave is"
+
+#: ../web/swat.c:527 ../web/swat.c:530
+msgid "Basic"
+msgstr "Basis"
+
+#: ../web/swat.c:528 ../web/swat.c:531
+msgid "Advanced"
+msgstr "Geadvanceerd"
+
+#: ../web/swat.c:529
+msgid "Change View To"
+msgstr "Verander Weergave In"
+
+#: ../web/swat.c:554
+msgid "Current Config"
+msgstr "Huidige Configuratie"
+
+#: ../web/swat.c:558
+msgid "Normal View"
+msgstr "Normale Weergave"
+
+#: ../web/swat.c:560
+msgid "Full View"
+msgstr "Volledige Weergave"
+
+#. Here we first set and commit all the parameters that were selected
+#. in the previous screen.
+#: ../web/swat.c:579
+msgid "Wizard Parameter Edit Page"
+msgstr "Wizard Instellingen Veranderen Pagina"
+
+#: ../web/swat.c:608
+msgid "Note: smb.conf file has been read and rewritten"
+msgstr "N.B.: het smb.conf bestand is gelezen en herschreven"
+
+#. Here we go ...
+#: ../web/swat.c:716
+msgid "Samba Configuration Wizard"
+msgstr "Samba Configuratie Wizard"
+
+#: ../web/swat.c:720
+msgid "The \"Rewrite smb.conf file\" button will clear the smb.conf file of all default values and of comments."
+msgstr "De \"Herschrijf smb.conf bestand\" knop zal alle standaardwaardes en alle commentaar verwijderen."
+
+#: ../web/swat.c:721
+msgid "The same will happen if you press the commit button."
+msgstr "Hetzelfde zal gebeuren als u de \"toepassen\" knop gebruikt."
+
+#: ../web/swat.c:724
+msgid "Rewrite smb.conf file"
+msgstr "Herschrijf smb.conf bestand"
+
+#: ../web/swat.c:725
+msgid "Commit"
+msgstr "Toepassen"
+
+#: ../web/swat.c:726
+msgid "Edit Parameter Values"
+msgstr "Bewerk Configuratie Waardes"
+
+#: ../web/swat.c:732
+msgid "Server Type"
+msgstr "Server Type"
+
+#: ../web/swat.c:733
+msgid "Stand Alone"
+msgstr "Stand Alone"
+
+#: ../web/swat.c:734
+msgid "Domain Member"
+msgstr "Domein Lid"
+
+#: ../web/swat.c:735
+msgid "Domain Controller"
+msgstr "Domein Controller"
+
+#: ../web/swat.c:738
+msgid "Unusual Type in smb.conf - Please Select New Mode"
+msgstr "Ongebruikelijk Type in smb.conf - Selecteer een nieuwe modus"
+
+#: ../web/swat.c:740
+msgid "Configure WINS As"
+msgstr "Configureer WINS Als"
+
+#: ../web/swat.c:741
+msgid "Not Used"
+msgstr "Niet gebruikt"
+
+#: ../web/swat.c:742
+msgid "Server for client use"
+msgstr "Server voor client gebruik"
+
+#: ../web/swat.c:743
+msgid "Client of another WINS server"
+msgstr "Client van een andere WINS server"
+
+#: ../web/swat.c:745
+msgid "Remote WINS Server"
+msgstr "Naam of IP-adres WINS Server"
+
+#: ../web/swat.c:756
+msgid "Error: WINS Server Mode and WINS Support both set in smb.conf"
+msgstr "Fout: WINS Server Modus en WINS Ondersteuning beiden ingesteld in smb.conf"
+
+#: ../web/swat.c:757
+msgid "Please Select desired WINS mode above."
+msgstr "Selecteer hierboven de gewenste WINS modus."
+
+#: ../web/swat.c:759
+msgid "Expose Home Directories"
+msgstr "Stel Home Directories Open"
+
+#: ../web/swat.c:774
+msgid "The above configuration options will set multiple parameters and will generally assist with rapid Samba deployment."
+msgstr "The configuratie hierboven zal meerdere variabelen veranderen en zal over het algemeen zorgen voor snelle installatie van Samba."
+
+#: ../web/swat.c:787
+msgid "Global Parameters"
+msgstr "Algemene Instellingen"
+
+#: ../web/swat.c:815 ../web/swat.c:916 ../web/swat.c:1265
+msgid "Commit Changes"
+msgstr "Pas Veranderingen Toe"
+
+#: ../web/swat.c:819 ../web/swat.c:919 ../web/swat.c:1267
+msgid "Reset Values"
+msgstr "Beginwaarden"
+
+#: ../web/swat.c:844
+msgid "Share Parameters"
+msgstr "Instellingen Gedeelde Bronnen"
+
+#: ../web/swat.c:887
+msgid "Choose Share"
+msgstr "Kies Bron"
+
+#: ../web/swat.c:901
+msgid "Delete Share"
+msgstr "Verwijder Bron"
+
+#: ../web/swat.c:908
+msgid "Create Share"
+msgstr "Maak Bron"
+
+#: ../web/swat.c:944
+msgid "password change in demo mode rejected"
+msgstr "wachtwoord veranderen in demo modus geweigerd"
+
+#: ../web/swat.c:957
+msgid "Can't setup password database vectors."
+msgstr "Kan wachtwoord database vectors niet opzetten."
+
+#: ../web/swat.c:983
+msgid " Must specify \"User Name\" "
+msgstr " \"Gebruikersnaam\" moet opgegeven worden "
+
+#: ../web/swat.c:999
+msgid " Must specify \"Old Password\" "
+msgstr " \"Oude wachtwoord\" moet opgegeven worden "
+
+#: ../web/swat.c:1005
+msgid " Must specify \"Remote Machine\" "
+msgstr " \"Server Naam of IP\" moet opgegeven worden "
+
+#: ../web/swat.c:1012
+msgid " Must specify \"New, and Re-typed Passwords\" "
+msgstr " \"Nieuw, en bevestiging Wachtwoorden\" moeten opgegeven worden "
+
+#: ../web/swat.c:1018
+msgid " Re-typed password didn't match new password "
+msgstr " Bevestigingswachtwoord was anders dan nieuwe wachtwoord "
+
+#: ../web/swat.c:1048
+#, c-format
+msgid " The passwd for '%s' has been changed."
+msgstr " Het wachtwoord voor '%s' is veranderd."
+
+#: ../web/swat.c:1051
+#, c-format
+msgid " The passwd for '%s' has NOT been changed."
+msgstr " Het wachtwoord voor '%s' is niet veranderd."
+
+#: ../web/swat.c:1076
+msgid "Server Password Management"
+msgstr "Server Wachtwoord Beheer"
+
+#.
+#. * Create all the dialog boxes for data collection
+#.
+#: ../web/swat.c:1085 ../web/swat.c:1132
+msgid "User Name"
+msgstr "Gebuikersnaam"
+
+#: ../web/swat.c:1088 ../web/swat.c:1134
+msgid "Old Password"
+msgstr "Oud Wachtwoord"
+
+#: ../web/swat.c:1091 ../web/swat.c:1136
+msgid "New Password"
+msgstr "Nieuw Wachtwoord"
+
+#: ../web/swat.c:1093 ../web/swat.c:1138
+msgid "Re-type New Password"
+msgstr "Bevestiging Nieuw Wachtwoord"
+
+#: ../web/swat.c:1101 ../web/swat.c:1149
+msgid "Change Password"
+msgstr "Verander Wachtwoord"
+
+#: ../web/swat.c:1104
+msgid "Add New User"
+msgstr "Voeg Gebruiker Toe"
+
+#: ../web/swat.c:1106
+msgid "Delete User"
+msgstr "Verwijder Gebruiker"
+
+#: ../web/swat.c:1108
+msgid "Disable User"
+msgstr "Maak gebruiker inactief"
+
+#: ../web/swat.c:1110
+msgid "Enable User"
+msgstr "Maak gebruiker actief"
+
+#: ../web/swat.c:1123
+msgid "Client/Server Password Management"
+msgstr "Client/Server Wachtwoord Beheer"
+
+#: ../web/swat.c:1140
+msgid "Remote Machine"
+msgstr "Naam of IP Server"
+
+#: ../web/swat.c:1179
+msgid "Printer Parameters"
+msgstr "Printer Instellingen"
+
+#: ../web/swat.c:1181
+msgid "Important Note:"
+msgstr "Belangrijk:"
+
+#: ../web/swat.c:1182
+msgid "Printer names marked with [*] in the Choose Printer drop-down box "
+msgstr "Printer namen gemarkeerd met [*] in het Kies Printer veld "
+
+#: ../web/swat.c:1183
+msgid "are autoloaded printers from "
+msgstr "zijn automatisch geladen uit "
+
+#: ../web/swat.c:1184
+msgid "Printcap Name"
+msgstr "Printcap Naam"
+
+#: ../web/swat.c:1185
+msgid "Attempting to delete these printers from SWAT will have no effect."
+msgstr "Proberen deze printers te verwijderen vanuit SWAT zal geen effect hebben."
+
+#: ../web/swat.c:1231
+msgid "Choose Printer"
+msgstr "Kies Printer"
+
+#: ../web/swat.c:1250
+msgid "Delete Printer"
+msgstr "Verwijder Printer"
+
+#: ../web/swat.c:1257
+msgid "Create Printer"
+msgstr "Maak Printer"
+
+#: ../web/statuspage.c:123
+msgid "RDONLY "
+msgstr "RDONLY"
+
+#: ../web/statuspage.c:124
+msgid "WRONLY "
+msgstr "WRONLY"
+
+#: ../web/statuspage.c:125
+msgid "RDWR "
+msgstr "RDWR"
+
+#: ../web/statuspage.c:309
+msgid "Server Status"
+msgstr "Server Status"
+
+#: ../web/statuspage.c:314
+msgid "Auto Refresh"
+msgstr "Automatisch Verversen"
+
+#: ../web/statuspage.c:315 ../web/statuspage.c:320
+msgid "Refresh Interval: "
+msgstr "Ververs Interval:"
+
+#: ../web/statuspage.c:319
+msgid "Stop Refreshing"
+msgstr "Stop met Verversen"
+
+#: ../web/statuspage.c:334
+msgid "version:"
+msgstr "versie:"
+
+#: ../web/statuspage.c:337
+msgid "smbd:"
+msgstr "smbd:"
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "running"
+msgstr "draaiend"
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "not running"
+msgstr "niet draaiend"
+
+#: ../web/statuspage.c:341
+msgid "Stop smbd"
+msgstr "Stop smbd"
+
+#: ../web/statuspage.c:343
+msgid "Start smbd"
+msgstr "Start smbd"
+
+#: ../web/statuspage.c:345
+msgid "Restart smbd"
+msgstr "Herstart smbd"
+
+#: ../web/statuspage.c:350
+msgid "nmbd:"
+msgstr "nmbd:"
+
+#: ../web/statuspage.c:354
+msgid "Stop nmbd"
+msgstr "Stop nmbd"
+
+#: ../web/statuspage.c:356
+msgid "Start nmbd"
+msgstr "Start nmbd"
+
+#: ../web/statuspage.c:358
+msgid "Restart nmbd"
+msgstr "Herstart nmbd"
+
+#: ../web/statuspage.c:364
+msgid "winbindd:"
+msgstr "winbindd:"
+
+#: ../web/statuspage.c:368
+msgid "Stop winbindd"
+msgstr "Stop winbindd"
+
+#: ../web/statuspage.c:370
+msgid "Start winbindd"
+msgstr "Start winbindd"
+
+#: ../web/statuspage.c:372
+msgid "Restart winbindd"
+msgstr "Herstart winbindd"
+
+#. stop, restart all
+#: ../web/statuspage.c:381
+msgid "Stop All"
+msgstr "Stop Alles"
+
+#: ../web/statuspage.c:382
+msgid "Restart All"
+msgstr "Herstart Alles"
+
+#. start all
+#: ../web/statuspage.c:386
+msgid "Start All"
+msgstr "Start Alles"
+
+#: ../web/statuspage.c:393
+msgid "Active Connections"
+msgstr "Actieve Verbindingen"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "PID"
+msgstr "PID"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408
+msgid "Client"
+msgstr "Client"
+
+#: ../web/statuspage.c:395
+msgid "IP address"
+msgstr "IP adres"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "Date"
+msgstr "Datum"
+
+#: ../web/statuspage.c:397
+msgid "Kill"
+msgstr "Kill"
+
+#: ../web/statuspage.c:405
+msgid "Active Shares"
+msgstr "Actieve Bronnen"
+
+#: ../web/statuspage.c:408
+msgid "Share"
+msgstr "Bron"
+
+#: ../web/statuspage.c:408
+msgid "User"
+msgstr "Gebruiker"
+
+#: ../web/statuspage.c:408
+msgid "Group"
+msgstr "Groep"
+
+#: ../web/statuspage.c:414
+msgid "Open Files"
+msgstr "Geopende Bestanden"
+
+#: ../web/statuspage.c:416
+msgid "Sharing"
+msgstr "Gedeeld"
+
+#: ../web/statuspage.c:416
+msgid "R/W"
+msgstr "Lees/Schrijf"
+
+#: ../web/statuspage.c:416
+msgid "Oplock"
+msgstr "Oplock"
+
+#: ../web/statuspage.c:416
+msgid "File"
+msgstr "Bestand"
+
+#: ../web/statuspage.c:425
+msgid "Show Client in col 1"
+msgstr "Toon Client in kolom 1"
+
+#: ../web/statuspage.c:426
+msgid "Show PID in col 1"
+msgstr "Toon PID in kolom 1"
+
+#: ../param/loadparm.c:755
+msgid "Base Options"
+msgstr "Basis Opties"
+
+#: ../param/loadparm.c:775
+msgid "Security Options"
+msgstr "Veiligheidsopties"
+
+#: ../param/loadparm.c:859
+msgid "Logging Options"
+msgstr "Log Opties"
+
+#: ../param/loadparm.c:874
+msgid "Protocol Options"
+msgstr "Protocol Opties"
+
+#: ../param/loadparm.c:911
+msgid "Tuning Options"
+msgstr "Fijntune Opties"
+
+#: ../param/loadparm.c:940
+msgid "Printing Options"
+msgstr "Printer Opties"
+
+#: ../param/loadparm.c:970
+msgid "Filename Handling"
+msgstr "Bestandsnaam Afhandeling"
+
+#: ../param/loadparm.c:996
+msgid "Domain Options"
+msgstr "Domein Opties"
+
+#: ../param/loadparm.c:1000
+msgid "Logon Options"
+msgstr "Logon Opties"
+
+#: ../param/loadparm.c:1019
+msgid "Browse Options"
+msgstr "Verken Opties"
+
+#: ../param/loadparm.c:1033
+msgid "WINS Options"
+msgstr "WINS Opties"
+
+#: ../param/loadparm.c:1043
+msgid "Locking Options"
+msgstr "Locking Opties"
+
+#: ../param/loadparm.c:1061
+msgid "Ldap Options"
+msgstr "LDAP Opties"
+
+#: ../param/loadparm.c:1078
+msgid "Miscellaneous Options"
+msgstr "Verscheidene Opties"
+
+#: ../param/loadparm.c:1138
+msgid "VFS module options"
+msgstr "VFS module opties"
+
+#: ../param/loadparm.c:1148
+msgid "Winbind options"
+msgstr "Winbind opties"
diff --git a/source/po/pl.msg b/source/po/pl.msg
new file mode 100644
index 00000000000..a7e56453bfc
--- /dev/null
+++ b/source/po/pl.msg
@@ -0,0 +1,593 @@
+# Polish messages for international release of SWAT.
+# Copyright (C) 2001 Rafal Szczesniak <mimir@spin.ict.pwr.wroc.pl>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: i18n_swat \n"
+"POT-Creation-Date: 2003-10-06 05:30+0900\n"
+"PO-Revision-Date: 2001-08-15 22:45+02:00\n"
+"Last-Translator: Rafal Szczesniak <mimir@spin.ict.pwr.wroc.pl>\n"
+"Language-Team: pl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-2\n"
+"Content-Transfer-Encoding: \n"
+
+#: ../web/swat.c:117
+#, c-format
+msgid "ERROR: Can't open %s"
+msgstr ""
+
+#: ../web/swat.c:200
+msgid "Help"
+msgstr "Pomoc"
+
+#: ../web/swat.c:206 ../web/swat.c:220 ../web/swat.c:235 ../web/swat.c:243 ../web/swat.c:252 ../web/swat.c:261 ../web/swat.c:267 ../web/swat.c:273 ../web/swat.c:286
+msgid "Set Default"
+msgstr "Ustaw domy¶lnie"
+
+#: ../web/swat.c:408
+#, c-format
+msgid "failed to open %s for writing"
+msgstr ""
+
+#: ../web/swat.c:431
+#, c-format
+msgid "Can't reload %s"
+msgstr ""
+
+#: ../web/swat.c:501
+#, c-format
+msgid "Logged in as <b>%s</b>"
+msgstr "Zalogowany jako <b>%s</b><p>\n"
+
+#: ../web/swat.c:505
+msgid "Home"
+msgstr "Strona domowa"
+
+#: ../web/swat.c:507
+msgid "Globals"
+msgstr "Ustawienia globalne"
+
+#: ../web/swat.c:508
+msgid "Shares"
+msgstr "Wspó³udzia³y"
+
+#: ../web/swat.c:509
+msgid "Printers"
+msgstr "Drukarki"
+
+#: ../web/swat.c:510
+msgid "Wizard"
+msgstr ""
+
+#: ../web/swat.c:513
+msgid "Status"
+msgstr "Status"
+
+#: ../web/swat.c:514
+msgid "View Config"
+msgstr "Przejrzyj Konfiguracjê"
+
+#: ../web/swat.c:516
+msgid "Password Management"
+msgstr "Zarz±dzanie Has³ami"
+
+#: ../web/swat.c:526
+msgid "Current View Is"
+msgstr "Bie¿±ca Konfiguracja"
+
+#: ../web/swat.c:527 ../web/swat.c:530
+msgid "Basic"
+msgstr "Widok Podstawowy"
+
+#: ../web/swat.c:528 ../web/swat.c:531
+msgid "Advanced"
+msgstr "Widok Zaawansowany"
+
+#: ../web/swat.c:529
+msgid "Change View To"
+msgstr "Zmieñ Has³o"
+
+#: ../web/swat.c:554
+msgid "Current Config"
+msgstr "Bie¿±ca Konfiguracja"
+
+#: ../web/swat.c:558
+msgid "Normal View"
+msgstr "Normalny Widok"
+
+#: ../web/swat.c:560
+msgid "Full View"
+msgstr "Pe³ny Widok"
+
+#. Here we first set and commit all the parameters that were selected
+#. in the previous screen.
+#: ../web/swat.c:579
+msgid "Wizard Parameter Edit Page"
+msgstr ""
+
+#: ../web/swat.c:608
+msgid "Note: smb.conf file has been read and rewritten"
+msgstr ""
+
+#. Here we go ...
+#: ../web/swat.c:716
+msgid "Samba Configuration Wizard"
+msgstr ""
+
+#: ../web/swat.c:720
+msgid "The \"Rewrite smb.conf file\" button will clear the smb.conf file of all default values and of comments."
+msgstr ""
+
+#: ../web/swat.c:721
+msgid "The same will happen if you press the commit button."
+msgstr ""
+
+#: ../web/swat.c:724
+msgid "Rewrite smb.conf file"
+msgstr ""
+
+#: ../web/swat.c:725
+msgid "Commit"
+msgstr "Potwierd¼ Zmiany"
+
+#: ../web/swat.c:726
+msgid "Edit Parameter Values"
+msgstr "Parametry Drukarki"
+
+#: ../web/swat.c:732
+msgid "Server Type"
+msgstr ""
+
+#: ../web/swat.c:733
+msgid "Stand Alone"
+msgstr "Uruchom nmbd"
+
+#: ../web/swat.c:734
+msgid "Domain Member"
+msgstr ""
+
+#: ../web/swat.c:735
+msgid "Domain Controller"
+msgstr ""
+
+#: ../web/swat.c:738
+msgid "Unusual Type in smb.conf - Please Select New Mode"
+msgstr ""
+
+#: ../web/swat.c:740
+msgid "Configure WINS As"
+msgstr ""
+
+#: ../web/swat.c:741
+msgid "Not Used"
+msgstr ""
+
+#: ../web/swat.c:742
+msgid "Server for client use"
+msgstr ""
+
+#: ../web/swat.c:743
+msgid "Client of another WINS server"
+msgstr ""
+
+#: ../web/swat.c:745
+msgid "Remote WINS Server"
+msgstr ""
+
+#: ../web/swat.c:756
+msgid "Error: WINS Server Mode and WINS Support both set in smb.conf"
+msgstr ""
+
+#: ../web/swat.c:757
+msgid "Please Select desired WINS mode above."
+msgstr ""
+
+#: ../web/swat.c:759
+msgid "Expose Home Directories"
+msgstr ""
+
+#: ../web/swat.c:774
+msgid "The above configuration options will set multiple parameters and will generally assist with rapid Samba deployment."
+msgstr ""
+
+#: ../web/swat.c:787
+msgid "Global Parameters"
+msgstr "Zmienne Globalne"
+
+#: ../web/swat.c:815 ../web/swat.c:916 ../web/swat.c:1265
+msgid "Commit Changes"
+msgstr "Potwierd¼ Zmiany"
+
+#: ../web/swat.c:819 ../web/swat.c:919 ../web/swat.c:1267
+msgid "Reset Values"
+msgstr "Zresetuj Warto¶ci"
+
+#: ../web/swat.c:844
+msgid "Share Parameters"
+msgstr "Parametry Wspó³udzia³u"
+
+#: ../web/swat.c:887
+msgid "Choose Share"
+msgstr "Wybierz Wspó³udzia³"
+
+#: ../web/swat.c:901
+msgid "Delete Share"
+msgstr "Usuñ Wspó³udzia³"
+
+#: ../web/swat.c:908
+msgid "Create Share"
+msgstr "Utwórz Wspó³udzia³"
+
+#: ../web/swat.c:944
+msgid "password change in demo mode rejected"
+msgstr "zmiana has³a w trybie demo odrzucona\n"
+
+#: ../web/swat.c:957
+msgid "Can't setup password database vectors."
+msgstr ""
+
+#: ../web/swat.c:983
+msgid " Must specify \"User Name\" "
+msgstr " Musisz podaæ \"Nazwê U¿ytkownika\" \n"
+
+#: ../web/swat.c:999
+msgid " Must specify \"Old Password\" "
+msgstr " Musisz podaæ \"Stare Has³o\" \n"
+
+#: ../web/swat.c:1005
+msgid " Must specify \"Remote Machine\" "
+msgstr " Musisz podaæ \"Zdaln± Maszynê\" \n"
+
+#: ../web/swat.c:1012
+msgid " Must specify \"New, and Re-typed Passwords\" "
+msgstr " Musisz podaæ \"Nowe Has³o, i ponownie wpisane Nowe Has³o\" \n"
+
+#: ../web/swat.c:1018
+msgid " Re-typed password didn't match new password "
+msgstr " Ponownie wpisane has³o nie pasuje do nowego has³a\n"
+
+#: ../web/swat.c:1048
+#, c-format
+msgid " The passwd for '%s' has been changed."
+msgstr " Has³o dla '%s' zosta³o zmienione. \n"
+
+#: ../web/swat.c:1051
+#, c-format
+msgid " The passwd for '%s' has NOT been changed."
+msgstr " Has³o dla '%s' NIE zosta³o zmienione. \n"
+
+#: ../web/swat.c:1076
+msgid "Server Password Management"
+msgstr "Zarz±dzanie Has³ami na Serwerze"
+
+#.
+#. * Create all the dialog boxes for data collection
+#.
+#: ../web/swat.c:1085 ../web/swat.c:1132
+msgid "User Name"
+msgstr " Nazwa U¿ytkownika"
+
+#: ../web/swat.c:1088 ../web/swat.c:1134
+msgid "Old Password"
+msgstr " Stare Has³o"
+
+#: ../web/swat.c:1091 ../web/swat.c:1136
+msgid "New Password"
+msgstr " Nowe Has³o"
+
+#: ../web/swat.c:1093 ../web/swat.c:1138
+msgid "Re-type New Password"
+msgstr " Ponownie wpisz Nowe Has³o"
+
+#: ../web/swat.c:1101 ../web/swat.c:1149
+msgid "Change Password"
+msgstr "Zmieñ Has³o"
+
+#: ../web/swat.c:1104
+msgid "Add New User"
+msgstr "Dodaj Nowego U¿ytkownika"
+
+#: ../web/swat.c:1106
+msgid "Delete User"
+msgstr "Usuñ U¿ytkownika"
+
+#: ../web/swat.c:1108
+msgid "Disable User"
+msgstr "Zablokuj U¿ytkownika"
+
+#: ../web/swat.c:1110
+msgid "Enable User"
+msgstr "Odblokuj U¿ytkownika"
+
+#: ../web/swat.c:1123
+msgid "Client/Server Password Management"
+msgstr "Zarz±dzanie Has³ami Klient/Serwer"
+
+#: ../web/swat.c:1140
+msgid "Remote Machine"
+msgstr " Zdalna Maszyna"
+
+#: ../web/swat.c:1179
+msgid "Printer Parameters"
+msgstr "Parametry Drukarki"
+
+#: ../web/swat.c:1181
+msgid "Important Note:"
+msgstr "Wa¿na Informacja:"
+
+#: ../web/swat.c:1182
+msgid "Printer names marked with [*] in the Choose Printer drop-down box "
+msgstr "Nazwy Drukarek zaznaczone [*] w rozwijanym polu Wybierz Drukarkê "
+
+#: ../web/swat.c:1183
+msgid "are autoloaded printers from "
+msgstr "s± drukarkami automatycznie ³adowanymi z "
+
+#: ../web/swat.c:1184
+msgid "Printcap Name"
+msgstr "Nazwa Printcap"
+
+#: ../web/swat.c:1185
+msgid "Attempting to delete these printers from SWAT will have no effect."
+msgstr "Próby usuniêcia tych drukarek ze SWAT nie przynios± efektu.\n"
+
+#: ../web/swat.c:1231
+msgid "Choose Printer"
+msgstr "Wybierz Drukarkê"
+
+#: ../web/swat.c:1250
+msgid "Delete Printer"
+msgstr "Usuñ Drukarkê"
+
+#: ../web/swat.c:1257
+msgid "Create Printer"
+msgstr "Utwórz Drukarkê"
+
+#: ../web/statuspage.c:123
+msgid "RDONLY "
+msgstr ""
+
+#: ../web/statuspage.c:124
+msgid "WRONLY "
+msgstr ""
+
+#: ../web/statuspage.c:125
+msgid "RDWR "
+msgstr ""
+
+#: ../web/statuspage.c:309
+msgid "Server Status"
+msgstr "Status Serwera"
+
+#: ../web/statuspage.c:314
+msgid "Auto Refresh"
+msgstr "Automatyczne Od¶wie¿anie"
+
+#: ../web/statuspage.c:315 ../web/statuspage.c:320
+msgid "Refresh Interval: "
+msgstr "Interwa³ Od¶wie¿ania: "
+
+#: ../web/statuspage.c:319
+msgid "Stop Refreshing"
+msgstr "Zatrzymaj Od¶wie¿anie"
+
+#: ../web/statuspage.c:334
+msgid "version:"
+msgstr "wersja:"
+
+#: ../web/statuspage.c:337
+msgid "smbd:"
+msgstr ""
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "running"
+msgstr "dzia³a"
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "not running"
+msgstr "nie dzia³a"
+
+#: ../web/statuspage.c:341
+msgid "Stop smbd"
+msgstr "Zatrzymaj smbd"
+
+#: ../web/statuspage.c:343
+msgid "Start smbd"
+msgstr "Uruchom smbd"
+
+#: ../web/statuspage.c:345
+msgid "Restart smbd"
+msgstr "Zrestartuj smbd"
+
+#: ../web/statuspage.c:350
+msgid "nmbd:"
+msgstr ""
+
+#: ../web/statuspage.c:354
+msgid "Stop nmbd"
+msgstr "Zatrzymaj nmbd"
+
+#: ../web/statuspage.c:356
+msgid "Start nmbd"
+msgstr "Uruchom nmbd"
+
+#: ../web/statuspage.c:358
+msgid "Restart nmbd"
+msgstr "Zrestartuj nmbd"
+
+#: ../web/statuspage.c:364
+msgid "winbindd:"
+msgstr ""
+
+#: ../web/statuspage.c:368
+msgid "Stop winbindd"
+msgstr "Zatrzymaj nmbd"
+
+#: ../web/statuspage.c:370
+msgid "Start winbindd"
+msgstr "Uruchom nmbd"
+
+#: ../web/statuspage.c:372
+msgid "Restart winbindd"
+msgstr "Zrestartuj nmbd"
+
+#. stop, restart all
+#: ../web/statuspage.c:381
+msgid "Stop All"
+msgstr ""
+
+#: ../web/statuspage.c:382
+msgid "Restart All"
+msgstr "Zrestartuj nmbd"
+
+#. start all
+#: ../web/statuspage.c:386
+msgid "Start All"
+msgstr "Uruchom nmbd"
+
+#: ../web/statuspage.c:393
+msgid "Active Connections"
+msgstr "Aktywne Po³±czenia"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "PID"
+msgstr ""
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408
+msgid "Client"
+msgstr "Klient"
+
+#: ../web/statuspage.c:395
+msgid "IP address"
+msgstr "adres IP"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "Date"
+msgstr "Data"
+
+#: ../web/statuspage.c:397
+msgid "Kill"
+msgstr "Zatrzymaj"
+
+#: ../web/statuspage.c:405
+msgid "Active Shares"
+msgstr "Aktywne Wspó³udzia³y"
+
+#: ../web/statuspage.c:408
+msgid "Share"
+msgstr "Wspó³udzia³"
+
+#: ../web/statuspage.c:408
+msgid "User"
+msgstr "U¿ytkownik"
+
+#: ../web/statuspage.c:408
+msgid "Group"
+msgstr "Grupa"
+
+#: ../web/statuspage.c:414
+msgid "Open Files"
+msgstr "Otwarte Pliki"
+
+#: ../web/statuspage.c:416
+msgid "Sharing"
+msgstr "Wspó³dzielenie"
+
+#: ../web/statuspage.c:416
+msgid "R/W"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "Oplock"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "File"
+msgstr "Plik"
+
+#: ../web/statuspage.c:425
+msgid "Show Client in col 1"
+msgstr ""
+
+#: ../web/statuspage.c:426
+msgid "Show PID in col 1"
+msgstr ""
+
+#: ../param/loadparm.c:755
+msgid "Base Options"
+msgstr "Bazowe Opcje"
+
+#: ../param/loadparm.c:775
+msgid "Security Options"
+msgstr "Opcje Zabezpieczeñ"
+
+#: ../param/loadparm.c:859
+msgid "Logging Options"
+msgstr "Opcje Blokowania"
+
+#: ../param/loadparm.c:874
+msgid "Protocol Options"
+msgstr "Opcje Protoko³u"
+
+#: ../param/loadparm.c:911
+msgid "Tuning Options"
+msgstr "Opcje Dostrajaj±ce"
+
+#: ../param/loadparm.c:940
+msgid "Printing Options"
+msgstr "Opcje Drukowania"
+
+#: ../param/loadparm.c:970
+msgid "Filename Handling"
+msgstr "Obs³uga Nazw Plików"
+
+#: ../param/loadparm.c:996
+msgid "Domain Options"
+msgstr "Opcje Domeny"
+
+#: ../param/loadparm.c:1000
+msgid "Logon Options"
+msgstr "Opcje Logowania"
+
+#: ../param/loadparm.c:1019
+msgid "Browse Options"
+msgstr "Opcje Przegl±dania"
+
+#: ../param/loadparm.c:1033
+msgid "WINS Options"
+msgstr "Opcje WINS"
+
+#: ../param/loadparm.c:1043
+msgid "Locking Options"
+msgstr "Opcje Blokowania"
+
+#: ../param/loadparm.c:1061
+msgid "Ldap Options"
+msgstr "Opcje Ldap"
+
+#: ../param/loadparm.c:1078
+msgid "Miscellaneous Options"
+msgstr "Pozosta³e Opcje"
+
+#: ../param/loadparm.c:1138
+msgid "VFS module options"
+msgstr "Opcje WINS"
+
+#: ../param/loadparm.c:1148
+msgid "Winbind options"
+msgstr "Opcje Drukowania"
diff --git a/source/po/tr.msg b/source/po/tr.msg
new file mode 100644
index 00000000000..8ef551da46a
--- /dev/null
+++ b/source/po/tr.msg
@@ -0,0 +1,594 @@
+# Turkish messages for international release of SWAT.
+# Copyright (C) 2001 Deniz Akkus Kanca <deniz@arayan.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: i18n_swat \n"
+"POT-Creation-Date: 2003-10-06 05:30+0900\n"
+"PO-Revision-Date: 2001-09-20 22:51EEST\n"
+"Last-Translator: Deniz Akkus Kanca <deniz@arayan.com>\n"
+"Language-Team: Turkish <gnu-tr-u12a@lists.sourceforge.net>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-9\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: KBabel 0.9.1\n"
+
+#: ../web/swat.c:117
+#, c-format
+msgid "ERROR: Can't open %s"
+msgstr ""
+
+#: ../web/swat.c:200
+msgid "Help"
+msgstr "Yardým"
+
+#: ../web/swat.c:206 ../web/swat.c:220 ../web/swat.c:235 ../web/swat.c:243 ../web/swat.c:252 ../web/swat.c:261 ../web/swat.c:267 ../web/swat.c:273 ../web/swat.c:286
+msgid "Set Default"
+msgstr "Öntanýmlýya Ayarla"
+
+#: ../web/swat.c:408
+#, c-format
+msgid "failed to open %s for writing"
+msgstr ""
+
+#: ../web/swat.c:431
+#, c-format
+msgid "Can't reload %s"
+msgstr ""
+
+#: ../web/swat.c:501
+#, c-format
+msgid "Logged in as <b>%s</b>"
+msgstr "<b>%s</b> kimliði ile oturum açýlmýþ"
+
+#: ../web/swat.c:505
+msgid "Home"
+msgstr "Ev"
+
+#: ../web/swat.c:507
+msgid "Globals"
+msgstr "Evrenseller"
+
+#: ../web/swat.c:508
+msgid "Shares"
+msgstr "Paylaþýmlar"
+
+#: ../web/swat.c:509
+msgid "Printers"
+msgstr "Yazýcýlar"
+
+#: ../web/swat.c:510
+msgid "Wizard"
+msgstr ""
+
+#: ../web/swat.c:513
+msgid "Status"
+msgstr "Durum"
+
+#: ../web/swat.c:514
+msgid "View Config"
+msgstr "Ayarlara Gözat"
+
+#: ../web/swat.c:516
+msgid "Password Management"
+msgstr "Þifre Yönetimi"
+
+#: ../web/swat.c:526
+msgid "Current View Is"
+msgstr "Þimdiki Ayarlar"
+
+#: ../web/swat.c:527 ../web/swat.c:530
+msgid "Basic"
+msgstr "Temel Görünüm"
+
+#: ../web/swat.c:528 ../web/swat.c:531
+msgid "Advanced"
+msgstr "Geliþmiþ Görünüm"
+
+#: ../web/swat.c:529
+msgid "Change View To"
+msgstr "Þifre Deðiþtir"
+
+#: ../web/swat.c:554
+msgid "Current Config"
+msgstr "Þimdiki Ayarlar"
+
+#: ../web/swat.c:558
+msgid "Normal View"
+msgstr "Normal Görünüm"
+
+#: ../web/swat.c:560
+msgid "Full View"
+msgstr "Tam Görünüm"
+
+#. Here we first set and commit all the parameters that were selected
+#. in the previous screen.
+#: ../web/swat.c:579
+msgid "Wizard Parameter Edit Page"
+msgstr ""
+
+#: ../web/swat.c:608
+msgid "Note: smb.conf file has been read and rewritten"
+msgstr ""
+
+#. Here we go ...
+#: ../web/swat.c:716
+msgid "Samba Configuration Wizard"
+msgstr ""
+
+#: ../web/swat.c:720
+msgid "The \"Rewrite smb.conf file\" button will clear the smb.conf file of all default values and of comments."
+msgstr ""
+
+#: ../web/swat.c:721
+msgid "The same will happen if you press the commit button."
+msgstr ""
+
+#: ../web/swat.c:724
+msgid "Rewrite smb.conf file"
+msgstr ""
+
+#: ../web/swat.c:725
+msgid "Commit"
+msgstr "açýklama"
+
+#: ../web/swat.c:726
+msgid "Edit Parameter Values"
+msgstr "Yazýcý Bilgileri"
+
+#: ../web/swat.c:732
+msgid "Server Type"
+msgstr ""
+
+#: ../web/swat.c:733
+msgid "Stand Alone"
+msgstr "Nmbd'yi çalýþtýr"
+
+#: ../web/swat.c:734
+msgid "Domain Member"
+msgstr "alan sunucusu"
+
+#: ../web/swat.c:735
+msgid "Domain Controller"
+msgstr "alan sunucusu"
+
+#: ../web/swat.c:738
+msgid "Unusual Type in smb.conf - Please Select New Mode"
+msgstr ""
+
+#: ../web/swat.c:740
+msgid "Configure WINS As"
+msgstr ""
+
+#: ../web/swat.c:741
+msgid "Not Used"
+msgstr "dont descend"
+
+#: ../web/swat.c:742
+msgid "Server for client use"
+msgstr ""
+
+#: ../web/swat.c:743
+msgid "Client of another WINS server"
+msgstr ""
+
+#: ../web/swat.c:745
+msgid "Remote WINS Server"
+msgstr ""
+
+#: ../web/swat.c:756
+msgid "Error: WINS Server Mode and WINS Support both set in smb.conf"
+msgstr ""
+
+#: ../web/swat.c:757
+msgid "Please Select desired WINS mode above."
+msgstr ""
+
+#: ../web/swat.c:759
+msgid "Expose Home Directories"
+msgstr ""
+
+#: ../web/swat.c:774
+msgid "The above configuration options will set multiple parameters and will generally assist with rapid Samba deployment."
+msgstr ""
+
+#: ../web/swat.c:787
+msgid "Global Parameters"
+msgstr "Genel Deðiþkenler"
+
+#: ../web/swat.c:815 ../web/swat.c:916 ../web/swat.c:1265
+msgid "Commit Changes"
+msgstr "Deðiþiklikleri Kaydet"
+
+#: ../web/swat.c:819 ../web/swat.c:919 ../web/swat.c:1267
+msgid "Reset Values"
+msgstr "Deðerleri Ýlk Haline Getir"
+
+#: ../web/swat.c:844
+msgid "Share Parameters"
+msgstr "Paylaþým Parametreleri"
+
+#: ../web/swat.c:887
+msgid "Choose Share"
+msgstr "Paylaþým Seçin"
+
+#: ../web/swat.c:901
+msgid "Delete Share"
+msgstr "Paylaþým Kaldýr"
+
+#: ../web/swat.c:908
+msgid "Create Share"
+msgstr "Paylaþým Oluþtur"
+
+#: ../web/swat.c:944
+msgid "password change in demo mode rejected"
+msgstr "demo kipinde þifre deðiþikliði kabul edilmedi\n"
+
+#: ../web/swat.c:957
+msgid "Can't setup password database vectors."
+msgstr ""
+
+#: ../web/swat.c:983
+msgid " Must specify \"User Name\" "
+msgstr " \"Kullanýcý Adý\" belirtilmeli \n"
+
+#: ../web/swat.c:999
+msgid " Must specify \"Old Password\" "
+msgstr " \"Eski Þifre\" belirtilmeli \n"
+
+#: ../web/swat.c:1005
+msgid " Must specify \"Remote Machine\" "
+msgstr " \"Uzak Makina\" belirtilmeli \n"
+
+#: ../web/swat.c:1012
+msgid " Must specify \"New, and Re-typed Passwords\" "
+msgstr " \"Yeni ve Tekrar Girilmiþ Þifreler\" belirtilmeli \n"
+
+#: ../web/swat.c:1018
+msgid " Re-typed password didn't match new password "
+msgstr " Tekrar girilen þifre yeni þifre ile eþleþmedi\n"
+
+#: ../web/swat.c:1048
+#, c-format
+msgid " The passwd for '%s' has been changed."
+msgstr " '%s' için þifre deðiþtirildi."
+
+#: ../web/swat.c:1051
+#, c-format
+msgid " The passwd for '%s' has NOT been changed."
+msgstr " '%s' için þifre DEÐÝÞTÝRÝLMEDÝ."
+
+#: ../web/swat.c:1076
+msgid "Server Password Management"
+msgstr "Sunucu Þifre Yönetimi"
+
+#.
+#. * Create all the dialog boxes for data collection
+#.
+#: ../web/swat.c:1085 ../web/swat.c:1132
+msgid "User Name"
+msgstr " Kullanýcý Adý"
+
+#: ../web/swat.c:1088 ../web/swat.c:1134
+msgid "Old Password"
+msgstr " Eski Þifre"
+
+#: ../web/swat.c:1091 ../web/swat.c:1136
+msgid "New Password"
+msgstr " Yeni Þifre"
+
+#: ../web/swat.c:1093 ../web/swat.c:1138
+msgid "Re-type New Password"
+msgstr " Yeni Þifre Tekrarý"
+
+#: ../web/swat.c:1101 ../web/swat.c:1149
+msgid "Change Password"
+msgstr "Þifre Deðiþtir"
+
+#: ../web/swat.c:1104
+msgid "Add New User"
+msgstr "Kull. Ekle"
+
+#: ../web/swat.c:1106
+msgid "Delete User"
+msgstr "Kull. Sil"
+
+#: ../web/swat.c:1108
+msgid "Disable User"
+msgstr "Kull. Etkisizleþtir"
+
+#: ../web/swat.c:1110
+msgid "Enable User"
+msgstr "Kull. Etkinleþtir"
+
+#: ../web/swat.c:1123
+msgid "Client/Server Password Management"
+msgstr "Ýstemci/Sunucu Þifre Yönetimi"
+
+#: ../web/swat.c:1140
+msgid "Remote Machine"
+msgstr " Uzak Makina"
+
+#: ../web/swat.c:1179
+msgid "Printer Parameters"
+msgstr "Yazýcý Bilgileri"
+
+#: ../web/swat.c:1181
+msgid "Important Note:"
+msgstr "Önemli Not:"
+
+#: ../web/swat.c:1182
+msgid "Printer names marked with [*] in the Choose Printer drop-down box "
+msgstr "Yazýcý Seç kutusunda [*] ile iþaretlenmiþ yazýcý isimleri "
+
+#: ../web/swat.c:1183
+msgid "are autoloaded printers from "
+msgstr "otomatik yüklenen yazýcýlar "
+
+#: ../web/swat.c:1184
+msgid "Printcap Name"
+msgstr "Printcap Adý"
+
+#: ../web/swat.c:1185
+msgid "Attempting to delete these printers from SWAT will have no effect."
+msgstr "Bu yazýcýlarý SWAT'dan silmek etkisiz olacaktýr.\n"
+
+#: ../web/swat.c:1231
+msgid "Choose Printer"
+msgstr "Yazýcý Seç"
+
+#: ../web/swat.c:1250
+msgid "Delete Printer"
+msgstr "Yazýcý Sil"
+
+#: ../web/swat.c:1257
+msgid "Create Printer"
+msgstr "Yazýcý Oluþtur"
+
+#: ../web/statuspage.c:123
+msgid "RDONLY "
+msgstr "SALTOKUNUR "
+
+#: ../web/statuspage.c:124
+msgid "WRONLY "
+msgstr "SALTYAZILIR "
+
+#: ../web/statuspage.c:125
+msgid "RDWR "
+msgstr "O/Y "
+
+#: ../web/statuspage.c:309
+msgid "Server Status"
+msgstr "Sunucu Durumu"
+
+#: ../web/statuspage.c:314
+msgid "Auto Refresh"
+msgstr "Oto Tazele"
+
+#: ../web/statuspage.c:315 ../web/statuspage.c:320
+msgid "Refresh Interval: "
+msgstr "Tazeleme Aralýðý: "
+
+#: ../web/statuspage.c:319
+msgid "Stop Refreshing"
+msgstr "Tazelemeyi Durdur"
+
+#: ../web/statuspage.c:334
+msgid "version:"
+msgstr "sürüm:"
+
+#: ../web/statuspage.c:337
+msgid "smbd:"
+msgstr "smbd:"
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "running"
+msgstr "çalýþýyor"
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "not running"
+msgstr "çalýþmýyor"
+
+#: ../web/statuspage.c:341
+msgid "Stop smbd"
+msgstr "Smbd'yi durdur"
+
+#: ../web/statuspage.c:343
+msgid "Start smbd"
+msgstr "Smbd'yi çalýþtýr"
+
+#: ../web/statuspage.c:345
+msgid "Restart smbd"
+msgstr "Smbd'yi yeniden çalýþtýr"
+
+#: ../web/statuspage.c:350
+msgid "nmbd:"
+msgstr "nmbd:"
+
+#: ../web/statuspage.c:354
+msgid "Stop nmbd"
+msgstr "Nmbd'yi durdur"
+
+#: ../web/statuspage.c:356
+msgid "Start nmbd"
+msgstr "Nmbd'yi çalýþtýr"
+
+#: ../web/statuspage.c:358
+msgid "Restart nmbd"
+msgstr "Nmbd'yi yeniden çalýþtýr"
+
+#: ../web/statuspage.c:364
+msgid "winbindd:"
+msgstr "winbind uid"
+
+#: ../web/statuspage.c:368
+msgid "Stop winbindd"
+msgstr "Nmbd'yi durdur"
+
+#: ../web/statuspage.c:370
+msgid "Start winbindd"
+msgstr "Nmbd'yi çalýþtýr"
+
+#: ../web/statuspage.c:372
+msgid "Restart winbindd"
+msgstr "Nmbd'yi yeniden çalýþtýr"
+
+#. stop, restart all
+#: ../web/statuspage.c:381
+msgid "Stop All"
+msgstr ""
+
+#: ../web/statuspage.c:382
+msgid "Restart All"
+msgstr "Nmbd'yi yeniden çalýþtýr"
+
+#. start all
+#: ../web/statuspage.c:386
+msgid "Start All"
+msgstr "Nmbd'yi çalýþtýr"
+
+#: ../web/statuspage.c:393
+msgid "Active Connections"
+msgstr "Aktif Baðlantýlar"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "PID"
+msgstr "PID"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408
+msgid "Client"
+msgstr "Ýstemci"
+
+#: ../web/statuspage.c:395
+msgid "IP address"
+msgstr "IP numarasý"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "Date"
+msgstr "Tarih"
+
+#: ../web/statuspage.c:397
+msgid "Kill"
+msgstr "Kapat"
+
+#: ../web/statuspage.c:405
+msgid "Active Shares"
+msgstr "Aktif Paylaþýmlar"
+
+#: ../web/statuspage.c:408
+msgid "Share"
+msgstr "Paylaþým"
+
+#: ../web/statuspage.c:408
+msgid "User"
+msgstr "Kullanýcý"
+
+#: ../web/statuspage.c:408
+msgid "Group"
+msgstr "Grup"
+
+#: ../web/statuspage.c:414
+msgid "Open Files"
+msgstr "Açýk Dosyalar"
+
+#: ../web/statuspage.c:416
+msgid "Sharing"
+msgstr "Paylaþýlýyor"
+
+#: ../web/statuspage.c:416
+msgid "R/W"
+msgstr "O/Y"
+
+#: ../web/statuspage.c:416
+msgid "Oplock"
+msgstr "Oplock"
+
+#: ../web/statuspage.c:416
+msgid "File"
+msgstr "Dosya"
+
+#: ../web/statuspage.c:425
+msgid "Show Client in col 1"
+msgstr ""
+
+#: ../web/statuspage.c:426
+msgid "Show PID in col 1"
+msgstr ""
+
+#: ../param/loadparm.c:755
+msgid "Base Options"
+msgstr "Temel Seçenekler"
+
+#: ../param/loadparm.c:775
+msgid "Security Options"
+msgstr "Güvenlik Seçenekleri"
+
+#: ../param/loadparm.c:859
+msgid "Logging Options"
+msgstr "Günlük Kaydý Seçenekleri"
+
+#: ../param/loadparm.c:874
+msgid "Protocol Options"
+msgstr "Protokol Seçenekleri"
+
+#: ../param/loadparm.c:911
+msgid "Tuning Options"
+msgstr "Ayar Seçenekleri"
+
+#: ../param/loadparm.c:940
+msgid "Printing Options"
+msgstr "Yazdýrma Seçenekleri"
+
+#: ../param/loadparm.c:970
+msgid "Filename Handling"
+msgstr "Dosyaadý Ýþlenmesi"
+
+#: ../param/loadparm.c:996
+msgid "Domain Options"
+msgstr "Alan Seçenekleri"
+
+#: ../param/loadparm.c:1000
+msgid "Logon Options"
+msgstr "Sistem Giriþ Seçenekleri"
+
+#: ../param/loadparm.c:1019
+msgid "Browse Options"
+msgstr "Gözatma Seçenekleri"
+
+#: ../param/loadparm.c:1033
+msgid "WINS Options"
+msgstr "WINS Seçenekleri"
+
+#: ../param/loadparm.c:1043
+msgid "Locking Options"
+msgstr "Kilitleme Seçenekleri"
+
+#: ../param/loadparm.c:1061
+msgid "Ldap Options"
+msgstr "Ldap Seçenekleri"
+
+#: ../param/loadparm.c:1078
+msgid "Miscellaneous Options"
+msgstr "Diðer Seçenekler"
+
+#: ../param/loadparm.c:1138
+msgid "VFS module options"
+msgstr "VFS Seçenekleri"
+
+#: ../param/loadparm.c:1148
+msgid "Winbind options"
+msgstr "Winbind seçenekleri"
diff --git a/source/popt/.cvsignore b/source/popt/.cvsignore
new file mode 100644
index 00000000000..86b08b58d2c
--- /dev/null
+++ b/source/popt/.cvsignore
@@ -0,0 +1,8 @@
+ID
+Makefile
+config.cache
+config.h
+config.log
+config.status
+rsync
+zlib/dummy
diff --git a/source/popt/CHANGES b/source/popt/CHANGES
new file mode 100644
index 00000000000..b6ab2aa3088
--- /dev/null
+++ b/source/popt/CHANGES
@@ -0,0 +1,43 @@
+1.3 ->
+ - heavy dose of const's
+ - poptParseArgvString() now NULL terminates the list
+
+1.2.3 -> 1.3
+ - added support for single -
+ - misc bug fixes
+ - portability improvements
+
+1.2.2 -> 1.2.3
+ - fixed memset() in help message generation (Dale Hawkins)
+ - added extern "C" stuff to popt.h for C++ compilers (Dale Hawkins)
+ - const'ified poptParseArgvString (Jeff Garzik)
+
+1.2.1 -> 1.2.2
+ - fixed bug in chaind alias happens which seems to have only
+ affected --triggers in rpm
+ - added POPT_ARG_VAL
+ - popt.3 installed by default
+
+1.2 -> 1.2.1
+ - added POPT_ARG_INTL_DOMAIN (Elliot Lee)
+ - updated Makefile's to be more GNUish (Elliot Lee)
+
+1.1 -> 1.2
+ - added popt.3 man page (Robert Lynch)
+ - don't use mmap anymore (its lack of portability isn't worth the
+ trouble)
+ - added test script
+ - added support for exec
+ - removed support for *_POPT_ALIASES env variable -- it was a bad
+ idea
+ - reorganized into multiple source files
+ - added automatic help generation, POPT_AUTOHELP
+ - added table callbacks
+ - added table inclusion
+ - updated man page for new features
+ - added test scripts
+
+1.0 -> 1.1
+ - moved to autoconf (Fred Fish)
+ - added STRERROR replacement (Norbert Warmuth)
+ - added const keywords (Bruce Perens)
diff --git a/source/popt/COPYING b/source/popt/COPYING
new file mode 100644
index 00000000000..b4c7ca876c6
--- /dev/null
+++ b/source/popt/COPYING
@@ -0,0 +1,22 @@
+Copyright (c) 1998 Red Hat Software
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
diff --git a/source/popt/README b/source/popt/README
new file mode 100644
index 00000000000..7fccc836ffa
--- /dev/null
+++ b/source/popt/README
@@ -0,0 +1,18 @@
+This is the popt command line option parsing library. While it is similiar
+to getopt(3), it contains a number of enhancements, including:
+
+ 1) popt is fully reentrant
+ 2) popt can parse arbitrary argv[] style arrays while
+ getopt(2) makes this quite difficult
+ 3) popt allows users to alias command line arguments
+ 4) popt provides convience functions for parsting strings
+ into argv[] style arrays
+
+popt is used by rpm, the Red Hat install program, and many other Red Hat
+utilities, all of which provide excellent examples of how to use popt.
+Complete documentation on popt is available in popt.ps (included in this
+tarball), which is excerpted with permission from the book "Linux
+Application Development" by Michael K. Johnson and Erik Troan (availble
+from Addison Wesley in May, 1998).
+
+Comments on popt should be addressed to ewt@redhat.com.
diff --git a/source/popt/dummy.in b/source/popt/dummy.in
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/source/popt/dummy.in
diff --git a/source/popt/findme.c b/source/popt/findme.c
new file mode 100644
index 00000000000..f2ad05bb3fb
--- /dev/null
+++ b/source/popt/findme.c
@@ -0,0 +1,46 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.redhat.com/pub/code/popt */
+
+#include "system.h"
+#include "findme.h"
+
+const char * findProgramPath(const char * argv0) {
+ char * path = getenv("PATH");
+ char * pathbuf;
+ char * start, * chptr;
+ char * buf, *local = NULL;
+
+ /* If there is a / in the argv[0], it has to be an absolute
+ path */
+ if (strchr(argv0, '/'))
+ return xstrdup(argv0);
+
+ if (!path) return NULL;
+
+ local = start = pathbuf = malloc(strlen(path) + 1);
+ buf = malloc(strlen(path) + strlen(argv0) + 2);
+ strcpy(pathbuf, path);
+
+ chptr = NULL;
+ do {
+ if ((chptr = strchr(start, ':')))
+ *chptr = '\0';
+ sprintf(buf, "%s/%s", start, argv0);
+
+ if (!access(buf, X_OK)) {
+ if (local) free(local);
+ return buf;
+ }
+
+ if (chptr)
+ start = chptr + 1;
+ else
+ start = NULL;
+ } while (start && *start);
+
+ free(buf);
+ if (local) free(local);
+
+ return NULL;
+}
diff --git a/source/popt/findme.h b/source/popt/findme.h
new file mode 100644
index 00000000000..5e93963d603
--- /dev/null
+++ b/source/popt/findme.h
@@ -0,0 +1,10 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.redhat.com/pub/code/popt */
+
+#ifndef H_FINDME
+#define H_FINDME
+
+const char * findProgramPath(const char * argv0);
+
+#endif
diff --git a/source/popt/popt.c b/source/popt/popt.c
new file mode 100644
index 00000000000..9fa8650312c
--- /dev/null
+++ b/source/popt/popt.c
@@ -0,0 +1,782 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.redhat.com/pub/code/popt */
+
+#include "system.h"
+#include "findme.h"
+#include "poptint.h"
+
+#ifndef HAVE_STRERROR
+static char * strerror(int errno) {
+ extern int sys_nerr;
+ extern char * sys_errlist[];
+
+ if ((0 <= errno) && (errno < sys_nerr))
+ return sys_errlist[errno];
+ else
+ return POPT_("unknown errno");
+}
+#endif
+
+void poptSetExecPath(poptContext con, const char * path, int allowAbsolute) {
+ if (con->execPath) xfree(con->execPath);
+ con->execPath = xstrdup(path);
+ con->execAbsolute = allowAbsolute;
+}
+
+static void invokeCallbacks(poptContext con, const struct poptOption * table,
+ int post) {
+ const struct poptOption * opt = table;
+ poptCallbackType cb;
+
+ while (opt->longName || opt->shortName || opt->arg) {
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+ invokeCallbacks(con, opt->arg, post);
+ } else if (((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) &&
+ ((!post && (opt->argInfo & POPT_CBFLAG_PRE)) ||
+ ( post && (opt->argInfo & POPT_CBFLAG_POST)))) {
+ cb = (poptCallbackType)opt->arg;
+ cb(con, post ? POPT_CALLBACK_REASON_POST : POPT_CALLBACK_REASON_PRE,
+ NULL, NULL, opt->descrip);
+ }
+ opt++;
+ }
+}
+
+poptContext poptGetContext(const char * name, int argc, const char ** argv,
+ const struct poptOption * options, int flags) {
+ poptContext con = malloc(sizeof(*con));
+
+ memset(con, 0, sizeof(*con));
+
+ con->os = con->optionStack;
+ con->os->argc = argc;
+ con->os->argv = argv;
+ con->os->argb = NULL;
+
+ if (!(flags & POPT_CONTEXT_KEEP_FIRST))
+ con->os->next = 1; /* skip argv[0] */
+
+ con->leftovers = calloc( (argc + 1), sizeof(char *) );
+ con->options = options;
+ con->aliases = NULL;
+ con->numAliases = 0;
+ con->flags = flags;
+ con->execs = NULL;
+ con->numExecs = 0;
+ con->finalArgvAlloced = argc * 2;
+ con->finalArgv = calloc( con->finalArgvAlloced, sizeof(*con->finalArgv) );
+ con->execAbsolute = 1;
+ con->arg_strip = NULL;
+
+ if (getenv("POSIXLY_CORRECT") || getenv("POSIX_ME_HARDER"))
+ con->flags |= POPT_CONTEXT_POSIXMEHARDER;
+
+ if (name)
+ con->appName = strcpy(malloc(strlen(name) + 1), name);
+
+ invokeCallbacks(con, con->options, 0);
+
+ return con;
+}
+
+static void cleanOSE(struct optionStackEntry *os)
+{
+ if (os->nextArg) {
+ xfree(os->nextArg);
+ os->nextArg = NULL;
+ }
+ if (os->argv) {
+ xfree(os->argv);
+ os->argv = NULL;
+ }
+ if (os->argb) {
+ PBM_FREE(os->argb);
+ os->argb = NULL;
+ }
+}
+
+void poptResetContext(poptContext con) {
+ int i;
+
+ while (con->os > con->optionStack) {
+ cleanOSE(con->os--);
+ }
+ if (con->os->argb) {
+ PBM_FREE(con->os->argb);
+ con->os->argb = NULL;
+ }
+ con->os->currAlias = NULL;
+ con->os->nextCharArg = NULL;
+ con->os->nextArg = NULL;
+ con->os->next = 1; /* skip argv[0] */
+
+ con->numLeftovers = 0;
+ con->nextLeftover = 0;
+ con->restLeftover = 0;
+ con->doExec = NULL;
+
+ for (i = 0; i < con->finalArgvCount; i++) {
+ if (con->finalArgv[i]) {
+ xfree(con->finalArgv[i]);
+ con->finalArgv[i] = NULL;
+ }
+ }
+
+ con->finalArgvCount = 0;
+
+ if (con->arg_strip) {
+ PBM_FREE(con->arg_strip);
+ con->arg_strip = NULL;
+ }
+}
+
+/* Only one of longName, shortName may be set at a time */
+static int handleExec(poptContext con, char * longName, char shortName) {
+ int i;
+
+ i = con->numExecs - 1;
+ if (longName) {
+ while (i >= 0 && (!con->execs[i].longName ||
+ strcmp(con->execs[i].longName, longName))) i--;
+ } else {
+ while (i >= 0 &&
+ con->execs[i].shortName != shortName) i--;
+ }
+
+ if (i < 0) return 0;
+
+ if (con->flags & POPT_CONTEXT_NO_EXEC)
+ return 1;
+
+ if (con->doExec == NULL) {
+ con->doExec = con->execs + i;
+ return 1;
+ }
+
+ /* We already have an exec to do; remember this option for next
+ time 'round */
+ if ((con->finalArgvCount + 1) >= (con->finalArgvAlloced)) {
+ con->finalArgvAlloced += 10;
+ con->finalArgv = realloc(con->finalArgv,
+ sizeof(*con->finalArgv) * con->finalArgvAlloced);
+ }
+
+ i = con->finalArgvCount++;
+ { char *s = malloc((longName ? strlen(longName) : 0) + 3);
+ if (longName)
+ sprintf(s, "--%s", longName);
+ else
+ sprintf(s, "-%c", shortName);
+ con->finalArgv[i] = s;
+ }
+
+ return 1;
+}
+
+/* Only one of longName, shortName may be set at a time */
+static int handleAlias(poptContext con, const char * longName, char shortName,
+ /*@keep@*/ const char * nextCharArg) {
+ int i;
+
+ if (con->os->currAlias && con->os->currAlias->longName && longName &&
+ !strcmp(con->os->currAlias->longName, longName))
+ return 0;
+ if (con->os->currAlias && shortName &&
+ shortName == con->os->currAlias->shortName)
+ return 0;
+
+ i = con->numAliases - 1;
+ if (longName) {
+ while (i >= 0 && (!con->aliases[i].longName ||
+ strcmp(con->aliases[i].longName, longName))) i--;
+ } else {
+ while (i >= 0 &&
+ con->aliases[i].shortName != shortName) i--;
+ }
+
+ if (i < 0) return 0;
+
+ if ((con->os - con->optionStack + 1) == POPT_OPTION_DEPTH)
+ return POPT_ERROR_OPTSTOODEEP;
+
+ if (nextCharArg && *nextCharArg)
+ con->os->nextCharArg = nextCharArg;
+
+ con->os++;
+ con->os->next = 0;
+ con->os->stuffed = 0;
+ con->os->nextArg = NULL;
+ con->os->nextCharArg = NULL;
+ con->os->currAlias = con->aliases + i;
+ poptDupArgv(con->os->currAlias->argc, con->os->currAlias->argv,
+ &con->os->argc, &con->os->argv);
+ con->os->argb = NULL;
+
+ return 1;
+}
+
+static void execCommand(poptContext con) {
+ const char ** argv;
+ int pos = 0;
+ const char * script = con->doExec->script;
+
+ argv = malloc(sizeof(*argv) *
+ (6 + con->numLeftovers + con->finalArgvCount));
+
+ if (!con->execAbsolute && strchr(script, '/')) return;
+
+ if (!strchr(script, '/') && con->execPath) {
+ char *s = malloc(strlen(con->execPath) + strlen(script) + 2);
+ sprintf(s, "%s/%s", con->execPath, script);
+ argv[pos] = s;
+ } else {
+ argv[pos] = script;
+ }
+ pos++;
+
+ argv[pos] = findProgramPath(con->os->argv[0]);
+ if (argv[pos]) pos++;
+ argv[pos++] = ";";
+
+ memcpy(argv + pos, con->finalArgv, sizeof(*argv) * con->finalArgvCount);
+ pos += con->finalArgvCount;
+
+ if (con->numLeftovers) {
+ argv[pos++] = "--";
+ memcpy(argv + pos, con->leftovers, sizeof(*argv) * con->numLeftovers);
+ pos += con->numLeftovers;
+ }
+
+ argv[pos++] = NULL;
+
+#ifdef __hpux
+ setresuid(getuid(), getuid(),-1);
+#else
+/*
+ * XXX " ... on BSD systems setuid() should be preferred over setreuid()"
+ * XXX sez' Timur Bakeyev <mc@bat.ru>
+ * XXX from Norbert Warmuth <nwarmuth@privat.circular.de>
+ */
+#if defined(HAVE_SETUID)
+ setuid(getuid());
+#elif defined (HAVE_SETREUID)
+ setreuid(getuid(), getuid()); /*hlauer: not portable to hpux9.01 */
+#else
+ ; /* Can't drop privileges */
+#endif
+#endif
+
+ execvp(argv[0], (char *const *)argv);
+}
+
+/*@observer@*/ static const struct poptOption *
+findOption(const struct poptOption * table, const char * longName,
+ char shortName,
+ /*@out@*/ poptCallbackType * callback, /*@out@*/ const void ** callbackData,
+ int singleDash)
+{
+ const struct poptOption * opt = table;
+ const struct poptOption * opt2;
+ const struct poptOption * cb = NULL;
+
+ /* This happens when a single - is given */
+ if (singleDash && !shortName && !*longName)
+ shortName = '-';
+
+ while (opt->longName || opt->shortName || opt->arg) {
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+ opt2 = findOption(opt->arg, longName, shortName, callback,
+ callbackData, singleDash);
+ if (opt2) {
+ if (*callback && !*callbackData)
+ *callbackData = opt->descrip;
+ return opt2;
+ }
+ } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) {
+ cb = opt;
+ } else if (longName && opt->longName &&
+ (!singleDash || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) &&
+ !strcmp(longName, opt->longName)) {
+ break;
+ } else if (shortName && shortName == opt->shortName) {
+ break;
+ }
+ opt++;
+ }
+
+ if (!opt->longName && !opt->shortName) return NULL;
+ *callbackData = NULL;
+ *callback = NULL;
+ if (cb) {
+ *callback = (poptCallbackType)cb->arg;
+ if (!(cb->argInfo & POPT_CBFLAG_INC_DATA))
+ *callbackData = cb->descrip;
+ }
+
+ return opt;
+}
+
+static const char *findNextArg(poptContext con, unsigned argx, int delete)
+{
+ struct optionStackEntry * os = con->os;
+ const char * arg;
+
+ do {
+ int i;
+ arg = NULL;
+ while (os->next == os->argc && os > con->optionStack) os--;
+ if (os->next == os->argc && os == con->optionStack) break;
+ for (i = os->next; i < os->argc; i++) {
+ if (os->argb && PBM_ISSET(i, os->argb)) continue;
+ if (*os->argv[i] == '-') continue;
+ if (--argx > 0) continue;
+ arg = os->argv[i];
+ if (delete) {
+ if (os->argb == NULL) os->argb = PBM_ALLOC(os->argc);
+ PBM_SET(i, os->argb);
+ }
+ break;
+ }
+ if (os > con->optionStack) os--;
+ } while (arg == NULL);
+ return arg;
+}
+
+static /*@only@*/ const char * expandNextArg(poptContext con, const char * s)
+{
+ const char *a;
+ size_t alen;
+ char *t, *te;
+ size_t tn = strlen(s) + 1;
+ char c;
+
+ te = t = malloc(tn);;
+ while ((c = *s++) != '\0') {
+ switch (c) {
+#if 0 /* XXX can't do this */
+ case '\\': /* escape */
+ c = *s++;
+ break;
+#endif
+ case '!':
+ if (!(s[0] == '#' && s[1] == ':' && s[2] == '+'))
+ break;
+ if ((a = findNextArg(con, 1, 1)) == NULL)
+ break;
+ s += 3;
+
+ alen = strlen(a);
+ tn += alen;
+ *te = '\0';
+ t = realloc(t, tn);
+ te = t + strlen(t);
+ strncpy(te, a, alen); te += alen;
+ continue;
+ /*@notreached@*/ break;
+ default:
+ break;
+ }
+ *te++ = c;
+ }
+ *te = '\0';
+ t = realloc(t, strlen(t)+1); /* XXX memory leak, hard to plug */
+ return t;
+}
+
+static void poptStripArg(poptContext con, int which)
+{
+ if(con->arg_strip == NULL) {
+ con->arg_strip = PBM_ALLOC(con->optionStack[0].argc);
+ }
+ PBM_SET(which, con->arg_strip);
+}
+
+/* returns 'val' element, -1 on last item, POPT_ERROR_* on error */
+int poptGetNextOpt(poptContext con)
+{
+ const struct poptOption * opt = NULL;
+ int done = 0;
+
+ /* looks a bit tricky to get rid of alloca properly in this fn */
+#if HAVE_ALLOCA_H
+#define ALLOCA(x) alloca(x)
+#else
+#define ALLOCA(x) malloc(x)
+#endif
+
+
+ while (!done) {
+ const char * origOptString = NULL;
+ poptCallbackType cb = NULL;
+ const void * cbData = NULL;
+ const char * longArg = NULL;
+ int canstrip = 0;
+
+ while (!con->os->nextCharArg && con->os->next == con->os->argc
+ && con->os > con->optionStack) {
+ cleanOSE(con->os--);
+ }
+ if (!con->os->nextCharArg && con->os->next == con->os->argc) {
+ invokeCallbacks(con, con->options, 1);
+ if (con->doExec) execCommand(con);
+ return -1;
+ }
+
+ /* Process next long option */
+ if (!con->os->nextCharArg) {
+ char * localOptString, * optString;
+ int thisopt;
+
+ if (con->os->argb && PBM_ISSET(con->os->next, con->os->argb)) {
+ con->os->next++;
+ continue;
+ }
+ thisopt=con->os->next;
+ origOptString = con->os->argv[con->os->next++];
+
+ if (con->restLeftover || *origOptString != '-') {
+ con->leftovers[con->numLeftovers++] = origOptString;
+ if (con->flags & POPT_CONTEXT_POSIXMEHARDER)
+ con->restLeftover = 1;
+ continue;
+ }
+
+ /* Make a copy we can hack at */
+ localOptString = optString =
+ strcpy(ALLOCA(strlen(origOptString) + 1),
+ origOptString);
+
+ if (!optString[0])
+ return POPT_ERROR_BADOPT;
+
+ if (optString[1] == '-' && !optString[2]) {
+ con->restLeftover = 1;
+ continue;
+ } else {
+ char *oe;
+ int singleDash;
+
+ optString++;
+ if (*optString == '-')
+ singleDash = 0, optString++;
+ else
+ singleDash = 1;
+
+ /* XXX aliases with arg substitution need "--alias=arg" */
+ if (handleAlias(con, optString, '\0', NULL))
+ continue;
+ if (handleExec(con, optString, '\0'))
+ continue;
+
+ /* Check for "--long=arg" option. */
+ for (oe = optString; *oe && *oe != '='; oe++)
+ ;
+ if (*oe == '=') {
+ *oe++ = '\0';
+ /* XXX longArg is mapped back to persistent storage. */
+ longArg = origOptString + (oe - localOptString);
+ }
+
+ opt = findOption(con->options, optString, '\0', &cb, &cbData,
+ singleDash);
+ if (!opt && !singleDash)
+ return POPT_ERROR_BADOPT;
+ }
+
+ if (!opt) {
+ con->os->nextCharArg = origOptString + 1;
+ } else {
+ if(con->os == con->optionStack &&
+ opt->argInfo & POPT_ARGFLAG_STRIP) {
+ canstrip = 1;
+ poptStripArg(con, thisopt);
+ }
+ }
+ }
+
+ /* Process next short option */
+ if (con->os->nextCharArg) {
+ origOptString = con->os->nextCharArg;
+
+ con->os->nextCharArg = NULL;
+
+ if (handleAlias(con, NULL, *origOptString,
+ origOptString + 1)) {
+ origOptString++;
+ continue;
+ }
+ if (handleExec(con, NULL, *origOptString))
+ continue;
+
+ opt = findOption(con->options, NULL, *origOptString, &cb,
+ &cbData, 0);
+ if (!opt)
+ return POPT_ERROR_BADOPT;
+
+ origOptString++;
+ if (*origOptString)
+ con->os->nextCharArg = origOptString;
+ }
+
+ if (opt->arg && (opt->argInfo & POPT_ARG_MASK) == POPT_ARG_NONE) {
+ *((int *)opt->arg) = 1;
+ } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_VAL) {
+ if (opt->arg)
+ *((int *) opt->arg) = opt->val;
+ } else if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE) {
+ if (con->os->nextArg) {
+ xfree(con->os->nextArg);
+ con->os->nextArg = NULL;
+ }
+ if (longArg) {
+ con->os->nextArg = expandNextArg(con, longArg);
+ } else if (con->os->nextCharArg) {
+ con->os->nextArg = expandNextArg(con, con->os->nextCharArg);
+ con->os->nextCharArg = NULL;
+ } else {
+ while (con->os->next == con->os->argc &&
+ con->os > con->optionStack) {
+ cleanOSE(con->os--);
+ }
+ if (con->os->next == con->os->argc)
+ return POPT_ERROR_NOARG;
+
+ /* make sure this isn't part of a short arg or the
+ result of an alias expansion */
+ if(con->os == con->optionStack &&
+ opt->argInfo & POPT_ARGFLAG_STRIP &&
+ canstrip) {
+ poptStripArg(con, con->os->next);
+ }
+
+ con->os->nextArg = expandNextArg(con, con->os->argv[con->os->next++]);
+ }
+
+ if (opt->arg) {
+ long aLong;
+ char *end;
+
+ switch (opt->argInfo & POPT_ARG_MASK) {
+ case POPT_ARG_STRING:
+ /* XXX memory leak, hard to plug */
+ *((const char **) opt->arg) = xstrdup(con->os->nextArg);
+ break;
+
+ case POPT_ARG_INT:
+ case POPT_ARG_LONG:
+ aLong = strtol(con->os->nextArg, &end, 0);
+ if (!(end && *end == '\0'))
+ return POPT_ERROR_BADNUMBER;
+
+ if (aLong == LONG_MIN || aLong == LONG_MAX)
+ return POPT_ERROR_OVERFLOW;
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_LONG) {
+ *((long *) opt->arg) = aLong;
+ } else {
+ if (aLong > INT_MAX || aLong < INT_MIN)
+ return POPT_ERROR_OVERFLOW;
+ *((int *) opt->arg) = aLong;
+ }
+ break;
+
+ default:
+ fprintf(stdout, POPT_("option type (%d) not implemented in popt\n"),
+ opt->argInfo & POPT_ARG_MASK);
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ if (cb)
+ cb(con, POPT_CALLBACK_REASON_OPTION, opt, con->os->nextArg, cbData);
+ else if (opt->val && ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL))
+ done = 1;
+
+ if ((con->finalArgvCount + 2) >= (con->finalArgvAlloced)) {
+ con->finalArgvAlloced += 10;
+ con->finalArgv = realloc(con->finalArgv,
+ sizeof(*con->finalArgv) * con->finalArgvAlloced);
+ }
+
+ { char *s = malloc((opt->longName ? strlen(opt->longName) : 0) + 3);
+ if (opt->longName)
+ sprintf(s, "--%s", opt->longName);
+ else
+ sprintf(s, "-%c", opt->shortName);
+ con->finalArgv[con->finalArgvCount++] = s;
+ }
+
+ if (opt->arg && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE
+ && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL) {
+ con->finalArgv[con->finalArgvCount++] = xstrdup(con->os->nextArg);
+ }
+ }
+
+ return opt->val;
+}
+
+const char * poptGetOptArg(poptContext con) {
+ const char * ret = con->os->nextArg;
+ con->os->nextArg = NULL;
+ return ret;
+}
+
+const char * poptGetArg(poptContext con) {
+ if (con->numLeftovers == con->nextLeftover) return NULL;
+ return con->leftovers[con->nextLeftover++];
+}
+
+const char * poptPeekArg(poptContext con) {
+ if (con->numLeftovers == con->nextLeftover) return NULL;
+ return con->leftovers[con->nextLeftover];
+}
+
+const char ** poptGetArgs(poptContext con) {
+ if (con->numLeftovers == con->nextLeftover) return NULL;
+
+ /* some apps like [like RPM ;-) ] need this NULL terminated */
+ con->leftovers[con->numLeftovers] = NULL;
+
+ return (con->leftovers + con->nextLeftover);
+}
+
+void poptFreeContext(poptContext con) {
+ int i;
+
+ poptResetContext(con);
+ if (con->os->argb) free(con->os->argb);
+
+ for (i = 0; i < con->numAliases; i++) {
+ if (con->aliases[i].longName) xfree(con->aliases[i].longName);
+ free(con->aliases[i].argv);
+ }
+
+ for (i = 0; i < con->numExecs; i++) {
+ if (con->execs[i].longName) xfree(con->execs[i].longName);
+ xfree(con->execs[i].script);
+ }
+ if (con->execs) xfree(con->execs);
+
+ free(con->leftovers);
+ free(con->finalArgv);
+ if (con->appName) xfree(con->appName);
+ if (con->aliases) free(con->aliases);
+ if (con->otherHelp) xfree(con->otherHelp);
+ if (con->execPath) xfree(con->execPath);
+ if (con->arg_strip) PBM_FREE(con->arg_strip);
+
+ free(con);
+}
+
+int poptAddAlias(poptContext con, struct poptAlias newAlias,
+ /*@unused@*/ int flags)
+{
+ int aliasNum = con->numAliases++;
+ struct poptAlias * alias;
+
+ /* SunOS won't realloc(NULL, ...) */
+ if (!con->aliases)
+ con->aliases = malloc(sizeof(newAlias) * con->numAliases);
+ else
+ con->aliases = realloc(con->aliases,
+ sizeof(newAlias) * con->numAliases);
+ alias = con->aliases + aliasNum;
+
+ alias->longName = (newAlias.longName)
+ ? strcpy(malloc(strlen(newAlias.longName) + 1), newAlias.longName)
+ : NULL;
+ alias->shortName = newAlias.shortName;
+ alias->argc = newAlias.argc;
+ alias->argv = newAlias.argv;
+
+ return 0;
+}
+
+const char * poptBadOption(poptContext con, int flags) {
+ struct optionStackEntry * os;
+
+ if (flags & POPT_BADOPTION_NOALIAS)
+ os = con->optionStack;
+ else
+ os = con->os;
+
+ return os->argv[os->next - 1];
+}
+
+#define POPT_ERROR_NOARG -10
+#define POPT_ERROR_BADOPT -11
+#define POPT_ERROR_OPTSTOODEEP -13
+#define POPT_ERROR_BADQUOTE -15 /* only from poptParseArgString() */
+#define POPT_ERROR_ERRNO -16 /* only from poptParseArgString() */
+
+const char *poptStrerror(const int error) {
+ switch (error) {
+ case POPT_ERROR_NOARG:
+ return POPT_("missing argument");
+ case POPT_ERROR_BADOPT:
+ return POPT_("unknown option");
+ case POPT_ERROR_OPTSTOODEEP:
+ return POPT_("aliases nested too deeply");
+ case POPT_ERROR_BADQUOTE:
+ return POPT_("error in paramter quoting");
+ case POPT_ERROR_BADNUMBER:
+ return POPT_("invalid numeric value");
+ case POPT_ERROR_OVERFLOW:
+ return POPT_("number too large or too small");
+ case POPT_ERROR_ERRNO:
+ return strerror(errno);
+ default:
+ return POPT_("unknown error");
+ }
+}
+
+int poptStuffArgs(poptContext con, const char ** argv) {
+ int argc;
+
+ if ((con->os - con->optionStack) == POPT_OPTION_DEPTH)
+ return POPT_ERROR_OPTSTOODEEP;
+
+ for (argc = 0; argv[argc]; argc++)
+ ;
+
+ con->os++;
+ con->os->next = 0;
+ con->os->nextArg = NULL;
+ con->os->nextCharArg = NULL;
+ con->os->currAlias = NULL;
+ poptDupArgv(argc, argv, &con->os->argc, &con->os->argv);
+ con->os->argb = NULL;
+ con->os->stuffed = 1;
+
+ return 0;
+}
+
+const char * poptGetInvocationName(poptContext con) {
+ return con->os->argv[0];
+}
+
+int poptStrippedArgv(poptContext con, int argc, char **argv)
+{
+ int i,j=1, numargs=argc;
+
+ for(i=1; i<argc; i++) {
+ if(PBM_ISSET(i, con->arg_strip)) {
+ numargs--;
+ }
+ }
+
+ for(i=1; i<argc; i++) {
+ if(PBM_ISSET(i, con->arg_strip)) {
+ continue;
+ } else {
+ if(j<numargs) {
+ argv[j++]=argv[i];
+ } else {
+ argv[j++]='\0';
+ }
+ }
+ }
+
+ return(numargs);
+}
diff --git a/source/popt/popt.h b/source/popt/popt.h
new file mode 100644
index 00000000000..c33cedaec99
--- /dev/null
+++ b/source/popt/popt.h
@@ -0,0 +1,130 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.redhat.com/pub/code/popt */
+
+#ifndef H_POPT
+#define H_POPT
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h> /* for FILE * */
+
+#define POPT_OPTION_DEPTH 10
+
+#define POPT_ARG_NONE 0
+#define POPT_ARG_STRING 1
+#define POPT_ARG_INT 2
+#define POPT_ARG_LONG 3
+#define POPT_ARG_INCLUDE_TABLE 4 /* arg points to table */
+#define POPT_ARG_CALLBACK 5 /* table-wide callback... must be
+ set first in table; arg points
+ to callback, descrip points to
+ callback data to pass */
+#define POPT_ARG_INTL_DOMAIN 6 /* set the translation domain
+ for this table and any
+ included tables; arg points
+ to the domain string */
+#define POPT_ARG_VAL 7 /* arg should take value val */
+#define POPT_ARG_MASK 0x0000FFFF
+#define POPT_ARGFLAG_ONEDASH 0x80000000 /* allow -longoption */
+#define POPT_ARGFLAG_DOC_HIDDEN 0x40000000 /* don't show in help/usage */
+#define POPT_ARGFLAG_STRIP 0x20000000 /* strip this arg from argv (only applies to long args) */
+#define POPT_CBFLAG_PRE 0x80000000 /* call the callback before parse */
+#define POPT_CBFLAG_POST 0x40000000 /* call the callback after parse */
+#define POPT_CBFLAG_INC_DATA 0x20000000 /* use data from the include line,
+ not the subtable */
+
+#define POPT_ERROR_NOARG -10
+#define POPT_ERROR_BADOPT -11
+#define POPT_ERROR_OPTSTOODEEP -13
+#define POPT_ERROR_BADQUOTE -15 /* only from poptParseArgString() */
+#define POPT_ERROR_ERRNO -16 /* only from poptParseArgString() */
+#define POPT_ERROR_BADNUMBER -17
+#define POPT_ERROR_OVERFLOW -18
+
+/* poptBadOption() flags */
+#define POPT_BADOPTION_NOALIAS (1 << 0) /* don't go into an alias */
+
+/* poptGetContext() flags */
+#define POPT_CONTEXT_NO_EXEC (1 << 0) /* ignore exec expansions */
+#define POPT_CONTEXT_KEEP_FIRST (1 << 1) /* pay attention to argv[0] */
+#define POPT_CONTEXT_POSIXMEHARDER (1 << 2) /* options can't follow args */
+
+struct poptOption {
+ /*@observer@*/ /*@null@*/ const char * longName; /* may be NULL */
+ char shortName; /* may be '\0' */
+ int argInfo;
+ /*@shared@*/ /*@null@*/ void * arg; /* depends on argInfo */
+ int val; /* 0 means don't return, just update flag */
+ /*@shared@*/ /*@null@*/ const char * descrip; /* description for autohelp -- may be NULL */
+ /*@shared@*/ /*@null@*/ const char * argDescrip; /* argument description for autohelp */
+};
+
+struct poptAlias {
+ /*@owned@*/ /*@null@*/ const char * longName; /* may be NULL */
+ char shortName; /* may be '\0' */
+ int argc;
+ /*@owned@*/ const char ** argv; /* must be free()able */
+};
+
+extern struct poptOption poptHelpOptions[];
+#define POPT_AUTOHELP { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptHelpOptions, \
+ 0, "Help options", NULL },
+
+typedef struct poptContext_s * poptContext;
+#ifndef __cplusplus
+typedef struct poptOption * poptOption;
+#endif
+
+enum poptCallbackReason { POPT_CALLBACK_REASON_PRE,
+ POPT_CALLBACK_REASON_POST,
+ POPT_CALLBACK_REASON_OPTION };
+typedef void (*poptCallbackType)(poptContext con,
+ enum poptCallbackReason reason,
+ const struct poptOption * opt,
+ const char * arg, const void * data);
+
+/*@only@*/ poptContext poptGetContext(/*@keep@*/ const char * name,
+ int argc, /*@keep@*/ const char ** argv,
+ /*@keep@*/ const struct poptOption * options, int flags);
+void poptResetContext(poptContext con);
+
+/* returns 'val' element, -1 on last item, POPT_ERROR_* on error */
+int poptGetNextOpt(poptContext con);
+/* returns NULL if no argument is available */
+/*@observer@*/ /*@null@*/ const char * poptGetOptArg(poptContext con);
+/* returns NULL if no more options are available */
+/*@observer@*/ /*@null@*/ const char * poptGetArg(poptContext con);
+/*@observer@*/ /*@null@*/ const char * poptPeekArg(poptContext con);
+/*@observer@*/ /*@null@*/ const char ** poptGetArgs(poptContext con);
+/* returns the option which caused the most recent error */
+/*@observer@*/ const char * poptBadOption(poptContext con, int flags);
+void poptFreeContext( /*@only@*/ poptContext con);
+int poptStuffArgs(poptContext con, /*@keep@*/ const char ** argv);
+int poptAddAlias(poptContext con, struct poptAlias alias, int flags);
+int poptReadConfigFile(poptContext con, const char * fn);
+/* like above, but reads /etc/popt and $HOME/.popt along with environment
+ vars */
+int poptReadDefaultConfig(poptContext con, int useEnv);
+/* argv should be freed -- this allows ', ", and \ quoting, but ' is treated
+ the same as " and both may include \ quotes */
+int poptDupArgv(int argc, const char **argv,
+ /*@out@*/ int * argcPtr, /*@out@*/ const char *** argvPtr);
+int poptParseArgvString(const char * s,
+ /*@out@*/ int * argcPtr, /*@out@*/ const char *** argvPtr);
+/*@observer@*/ const char *poptStrerror(const int error);
+void poptSetExecPath(poptContext con, const char * path, int allowAbsolute);
+void poptPrintHelp(poptContext con, FILE * f, int flags);
+void poptPrintUsage(poptContext con, FILE * f, int flags);
+void poptSetOtherOptionHelp(poptContext con, const char * text);
+/*@observer@*/ const char * poptGetInvocationName(poptContext con);
+/* shuffles argv pointers to remove stripped args, returns new argc */
+int poptStrippedArgv(poptContext con, int argc, char **argv);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/source/popt/poptconfig.c b/source/popt/poptconfig.c
new file mode 100644
index 00000000000..eb769413630
--- /dev/null
+++ b/source/popt/poptconfig.c
@@ -0,0 +1,142 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.redhat.com/pub/code/popt */
+
+#include "system.h"
+#include "poptint.h"
+
+static void configLine(poptContext con, char * line) {
+ int nameLength = strlen(con->appName);
+ char * opt;
+ struct poptAlias alias;
+ char * entryType;
+ char * longName = NULL;
+ char shortName = '\0';
+
+ if (strncmp(line, con->appName, nameLength)) return;
+ line += nameLength;
+ if (!*line || !isspace(*line)) return;
+ while (*line && isspace(*line)) line++;
+ entryType = line;
+
+ while (!*line || !isspace(*line)) line++;
+ *line++ = '\0';
+ while (*line && isspace(*line)) line++;
+ if (!*line) return;
+ opt = line;
+
+ while (!*line || !isspace(*line)) line++;
+ *line++ = '\0';
+ while (*line && isspace(*line)) line++;
+ if (!*line) return;
+
+ if (opt[0] == '-' && opt[1] == '-')
+ longName = opt + 2;
+ else if (opt[0] == '-' && !opt[2])
+ shortName = opt[1];
+
+ if (!strcmp(entryType, "alias")) {
+ if (poptParseArgvString(line, &alias.argc, &alias.argv)) return;
+ alias.longName = longName, alias.shortName = shortName;
+ poptAddAlias(con, alias, 0);
+ } else if (!strcmp(entryType, "exec")) {
+ con->execs = realloc(con->execs,
+ sizeof(*con->execs) * (con->numExecs + 1));
+ if (longName)
+ con->execs[con->numExecs].longName = xstrdup(longName);
+ else
+ con->execs[con->numExecs].longName = NULL;
+
+ con->execs[con->numExecs].shortName = shortName;
+ con->execs[con->numExecs].script = xstrdup(line);
+
+ con->numExecs++;
+ }
+}
+
+int poptReadConfigFile(poptContext con, const char * fn) {
+ char * file=NULL, * chptr, * end;
+ char * buf=NULL, * dst;
+ int fd, rc;
+ int fileLength;
+
+ fd = open(fn, O_RDONLY);
+ if (fd < 0) {
+ if (errno == ENOENT)
+ return 0;
+ else
+ return POPT_ERROR_ERRNO;
+ }
+
+ fileLength = lseek(fd, 0, SEEK_END);
+ (void) lseek(fd, 0, 0);
+
+ file = malloc(fileLength + 1);
+ if (read(fd, file, fileLength) != fileLength) {
+ rc = errno;
+ close(fd);
+ errno = rc;
+ if (file) free(file);
+ return POPT_ERROR_ERRNO;
+ }
+ close(fd);
+
+ dst = buf = malloc(fileLength + 1);
+
+ chptr = file;
+ end = (file + fileLength);
+ while (chptr < end) {
+ switch (*chptr) {
+ case '\n':
+ *dst = '\0';
+ dst = buf;
+ while (*dst && isspace(*dst)) dst++;
+ if (*dst && *dst != '#') {
+ configLine(con, dst);
+ }
+ chptr++;
+ break;
+ case '\\':
+ *dst++ = *chptr++;
+ if (chptr < end) {
+ if (*chptr == '\n')
+ dst--, chptr++;
+ /* \ at the end of a line does not insert a \n */
+ else
+ *dst++ = *chptr++;
+ }
+ break;
+ default:
+ *dst++ = *chptr++;
+ break;
+ }
+ }
+
+ free(file);
+ free(buf);
+
+ return 0;
+}
+
+int poptReadDefaultConfig(poptContext con, /*@unused@*/ int useEnv) {
+ char * fn, * home;
+ int rc;
+
+ if (!con->appName) return 0;
+
+ rc = poptReadConfigFile(con, "/etc/popt");
+ if (rc) return rc;
+ if (getuid() != geteuid()) return 0;
+
+ if ((home = getenv("HOME"))) {
+ fn = malloc(strlen(home) + 20);
+ strcpy(fn, home);
+ strcat(fn, "/.popt");
+ rc = poptReadConfigFile(con, fn);
+ free(fn);
+ if (rc) return rc;
+ }
+
+ return 0;
+}
+
diff --git a/source/popt/popthelp.c b/source/popt/popthelp.c
new file mode 100644
index 00000000000..6b790a63e78
--- /dev/null
+++ b/source/popt/popthelp.c
@@ -0,0 +1,301 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.redhat.com/pub/code/popt */
+
+#include "system.h"
+#include "poptint.h"
+
+static void displayArgs(poptContext con,
+ /*@unused@*/ enum poptCallbackReason foo,
+ struct poptOption * key,
+ /*@unused@*/ const char * arg, /*@unused@*/ void * data) {
+ if (key->shortName== '?')
+ poptPrintHelp(con, stdout, 0);
+ else
+ poptPrintUsage(con, stdout, 0);
+ exit(0);
+}
+
+struct poptOption poptHelpOptions[] = {
+ { NULL, '\0', POPT_ARG_CALLBACK, (void *)&displayArgs, '\0', NULL, NULL },
+ { "help", '?', 0, NULL, '?', N_("Show this help message"), NULL },
+ { "usage", '\0', 0, NULL, 'u', N_("Display brief usage message"), NULL },
+ { NULL, '\0', 0, NULL, 0, NULL, NULL }
+} ;
+
+
+/*@observer@*/ /*@null@*/ static const char *
+getTableTranslationDomain(const struct poptOption *table)
+{
+ const struct poptOption *opt;
+
+ for(opt = table;
+ opt->longName || opt->shortName || opt->arg;
+ opt++) {
+ if(opt->argInfo == POPT_ARG_INTL_DOMAIN)
+ return opt->arg;
+ }
+
+ return NULL;
+}
+
+/*@observer@*/ /*@null@*/ static const char *
+getArgDescrip(const struct poptOption * opt, const char *translation_domain)
+{
+ if (!(opt->argInfo & POPT_ARG_MASK)) return NULL;
+
+ if (opt == (poptHelpOptions + 1) || opt == (poptHelpOptions + 2))
+ if (opt->argDescrip) return POPT_(opt->argDescrip);
+
+ if (opt->argDescrip) return D_(translation_domain, opt->argDescrip);
+ return POPT_("ARG");
+}
+
+static void singleOptionHelp(FILE * f, int maxLeftCol,
+ const struct poptOption * opt,
+ const char *translation_domain) {
+ int indentLength = maxLeftCol + 5;
+ int lineLength = 79 - indentLength;
+ const char * help = D_(translation_domain, opt->descrip);
+ int helpLength;
+ const char * ch;
+ char format[10];
+ char * left;
+ const char * argDescrip = getArgDescrip(opt, translation_domain);
+
+ left = malloc(maxLeftCol + 1);
+ *left = '\0';
+
+ if (opt->longName && opt->shortName)
+ sprintf(left, "-%c, --%s", opt->shortName, opt->longName);
+ else if (opt->shortName)
+ sprintf(left, "-%c", opt->shortName);
+ else if (opt->longName)
+ sprintf(left, "--%s", opt->longName);
+ if (!*left) return ;
+ if (argDescrip) {
+ strcat(left, "=");
+ strcat(left, argDescrip);
+ }
+
+ if (help)
+ fprintf(f," %-*s ", maxLeftCol, left);
+ else {
+ fprintf(f," %s\n", left);
+ goto out;
+ }
+
+ helpLength = strlen(help);
+ while (helpLength > lineLength) {
+ ch = help + lineLength - 1;
+ while (ch > help && !isspace(*ch)) ch--;
+ if (ch == help) break; /* give up */
+ while (ch > (help + 1) && isspace(*ch)) ch--;
+ ch++;
+
+ sprintf(format, "%%.%ds\n%%%ds", (int) (ch - help), indentLength);
+ fprintf(f, format, help, " ");
+ help = ch;
+ while (isspace(*help) && *help) help++;
+ helpLength = strlen(help);
+ }
+
+ if (helpLength) fprintf(f, "%s\n", help);
+
+out:
+ free(left);
+}
+
+static int maxArgWidth(const struct poptOption * opt,
+ const char * translation_domain) {
+ int max = 0;
+ int this;
+ const char * s;
+
+ while (opt->longName || opt->shortName || opt->arg) {
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+ this = maxArgWidth(opt->arg, translation_domain);
+ if (this > max) max = this;
+ } else if (!(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
+ this = opt->shortName ? 2 : 0;
+ if (opt->longName) {
+ if (this) this += 2;
+ this += strlen(opt->longName) + 2;
+ }
+
+ s = getArgDescrip(opt, translation_domain);
+ if (s)
+ this += strlen(s) + 1;
+ if (this > max) max = this;
+ }
+
+ opt++;
+ }
+
+ return max;
+}
+
+static void singleTableHelp(FILE * f, const struct poptOption * table,
+ int left,
+ const char *translation_domain) {
+ const struct poptOption * opt;
+ const char *sub_transdom;
+
+ opt = table;
+ while (opt->longName || opt->shortName || opt->arg) {
+ if ((opt->longName || opt->shortName) &&
+ !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
+ singleOptionHelp(f, left, opt, translation_domain);
+ opt++;
+ }
+
+ opt = table;
+ while (opt->longName || opt->shortName || opt->arg) {
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+ sub_transdom = getTableTranslationDomain(opt->arg);
+ if(!sub_transdom)
+ sub_transdom = translation_domain;
+
+ if (opt->descrip)
+ fprintf(f, "\n%s\n", D_(sub_transdom, opt->descrip));
+
+ singleTableHelp(f, opt->arg, left, sub_transdom);
+ }
+ opt++;
+ }
+}
+
+static int showHelpIntro(poptContext con, FILE * f) {
+ int len = 6;
+ const char * fn;
+
+ fprintf(f, POPT_("Usage:"));
+ if (!(con->flags & POPT_CONTEXT_KEEP_FIRST)) {
+ fn = con->optionStack->argv[0];
+ if (strchr(fn, '/')) fn = strchr(fn, '/') + 1;
+ fprintf(f, " %s", fn);
+ len += strlen(fn) + 1;
+ }
+
+ return len;
+}
+
+void poptPrintHelp(poptContext con, FILE * f, /*@unused@*/ int flags) {
+ int leftColWidth;
+
+ showHelpIntro(con, f);
+ if (con->otherHelp)
+ fprintf(f, " %s\n", con->otherHelp);
+ else
+ fprintf(f, " %s\n", POPT_("[OPTION...]"));
+
+ leftColWidth = maxArgWidth(con->options, NULL);
+ singleTableHelp(f, con->options, leftColWidth, NULL);
+}
+
+static int singleOptionUsage(FILE * f, int cursor,
+ const struct poptOption * opt,
+ const char *translation_domain) {
+ int len = 3;
+ char shortStr[2] = { '\0', '\0' };
+ const char * item = shortStr;
+ const char * argDescrip = getArgDescrip(opt, translation_domain);
+
+ if (opt->shortName) {
+ if (!(opt->argInfo & POPT_ARG_MASK))
+ return cursor; /* we did these already */
+ len++;
+ *shortStr = opt->shortName;
+ shortStr[1] = '\0';
+ } else if (opt->longName) {
+ len += 1 + strlen(opt->longName);
+ item = opt->longName;
+ }
+
+ if (len == 3) return cursor;
+
+ if (argDescrip)
+ len += strlen(argDescrip) + 1;
+
+ if ((cursor + len) > 79) {
+ fprintf(f, "\n ");
+ cursor = 7;
+ }
+
+ fprintf(f, " [-%s%s%s%s]", opt->shortName ? "" : "-", item,
+ argDescrip ? (opt->shortName ? " " : "=") : "",
+ argDescrip ? argDescrip : "");
+
+ return cursor + len + 1;
+}
+
+static int singleTableUsage(FILE * f, int cursor, const struct poptOption * table,
+ const char *translation_domain) {
+ const struct poptOption * opt;
+
+ opt = table;
+ while (opt->longName || opt->shortName || opt->arg) {
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN)
+ translation_domain = (const char *)opt->arg;
+ else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE)
+ cursor = singleTableUsage(f, cursor, opt->arg,
+ translation_domain);
+ else if ((opt->longName || opt->shortName) &&
+ !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
+ cursor = singleOptionUsage(f, cursor, opt, translation_domain);
+
+ opt++;
+ }
+
+ return cursor;
+}
+
+static int showShortOptions(const struct poptOption * opt, FILE * f,
+ char * str) {
+ char s[300]; /* this is larger then the ascii set, so
+ it should do just fine */
+
+ s[0] = '\0';
+ if (str == NULL) {
+ memset(s, 0, sizeof(s));
+ str = s;
+ }
+
+ while (opt->longName || opt->shortName || opt->arg) {
+ if (opt->shortName && !(opt->argInfo & POPT_ARG_MASK))
+ str[strlen(str)] = opt->shortName;
+ else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE)
+ showShortOptions(opt->arg, f, str);
+
+ opt++;
+ }
+
+ if (s != str || !*s)
+ return 0;
+
+ fprintf(f, " [-%s]", s);
+ return strlen(s) + 4;
+}
+
+void poptPrintUsage(poptContext con, FILE * f, /*@unused@*/ int flags) {
+ int cursor;
+
+ cursor = showHelpIntro(con, f);
+ cursor += showShortOptions(con->options, f, NULL);
+ singleTableUsage(f, cursor, con->options, NULL);
+
+ if (con->otherHelp) {
+ cursor += strlen(con->otherHelp) + 1;
+ if (cursor > 79) fprintf(f, "\n ");
+ fprintf(f, " %s", con->otherHelp);
+ }
+
+ fprintf(f, "\n");
+}
+
+void poptSetOtherOptionHelp(poptContext con, const char * text) {
+ if (con->otherHelp) xfree(con->otherHelp);
+ con->otherHelp = xstrdup(text);
+}
diff --git a/source/popt/poptint.h b/source/popt/poptint.h
new file mode 100644
index 00000000000..1847ffafe67
--- /dev/null
+++ b/source/popt/poptint.h
@@ -0,0 +1,71 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.redhat.com/pub/code/popt */
+
+#ifndef H_POPTINT
+#define H_POPTINT
+
+/* Bit mask macros. */
+typedef unsigned int __pbm_bits;
+#define __PBM_NBITS (8 * sizeof (__pbm_bits))
+#define __PBM_IX(d) ((d) / __PBM_NBITS)
+#define __PBM_MASK(d) ((__pbm_bits) 1 << ((d) % __PBM_NBITS))
+typedef struct {
+ __pbm_bits bits[1];
+} pbm_set;
+#define __PBM_BITS(set) ((set)->bits)
+
+#define PBM_ALLOC(d) calloc(__PBM_IX (d) + 1, sizeof(__pbm_bits))
+#define PBM_FREE(s) free(s);
+#define PBM_SET(d, s) (__PBM_BITS (s)[__PBM_IX (d)] |= __PBM_MASK (d))
+#define PBM_CLR(d, s) (__PBM_BITS (s)[__PBM_IX (d)] &= ~__PBM_MASK (d))
+#define PBM_ISSET(d, s) ((__PBM_BITS (s)[__PBM_IX (d)] & __PBM_MASK (d)) != 0)
+
+struct optionStackEntry {
+ int argc;
+ /*@only@*/ const char ** argv;
+ /*@only@*/ pbm_set * argb;
+ int next;
+ /*@only@*/ const char * nextArg;
+ /*@keep@*/ const char * nextCharArg;
+ /*@dependent@*/ struct poptAlias * currAlias;
+ int stuffed;
+};
+
+struct execEntry {
+ const char * longName;
+ char shortName;
+ const char * script;
+};
+
+struct poptContext_s {
+ struct optionStackEntry optionStack[POPT_OPTION_DEPTH];
+ /*@dependent@*/ struct optionStackEntry * os;
+ /*@owned@*/ const char ** leftovers;
+ int numLeftovers;
+ int nextLeftover;
+ /*@keep@*/ const struct poptOption * options;
+ int restLeftover;
+ /*@only@*/ const char * appName;
+ /*@only@*/ struct poptAlias * aliases;
+ int numAliases;
+ int flags;
+ struct execEntry * execs;
+ int numExecs;
+ /*@only@*/ const char ** finalArgv;
+ int finalArgvCount;
+ int finalArgvAlloced;
+ /*@dependent@*/ struct execEntry * doExec;
+ /*@only@*/ const char * execPath;
+ int execAbsolute;
+ /*@only@*/ const char * otherHelp;
+ pbm_set * arg_strip;
+};
+
+#define xfree(_a) free((void *)_a)
+
+#define POPT_(foo) (foo)
+#define D_(dom, str) (str)
+#define N_(foo) (foo)
+
+#endif
diff --git a/source/popt/poptparse.c b/source/popt/poptparse.c
new file mode 100644
index 00000000000..8f00769be9f
--- /dev/null
+++ b/source/popt/poptparse.c
@@ -0,0 +1,102 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.redhat.com/pub/code/popt */
+
+#include "system.h"
+
+#define POPT_ARGV_ARRAY_GROW_DELTA 5
+
+int poptDupArgv(int argc, const char **argv,
+ int * argcPtr, const char *** argvPtr)
+{
+ size_t nb = (argc + 1) * sizeof(*argv);
+ const char ** argv2;
+ char * dst;
+ int i;
+
+ for (i = 0; i < argc; i++) {
+ if (argv[i] == NULL)
+ return POPT_ERROR_NOARG;
+ nb += strlen(argv[i]) + 1;
+ }
+
+ dst = malloc(nb);
+ argv2 = (void *) dst;
+ dst += (argc + 1) * sizeof(*argv);
+
+ for (i = 0; i < argc; i++) {
+ argv2[i] = dst;
+ dst += strlen(strcpy(dst, argv[i])) + 1;
+ }
+ argv2[argc] = NULL;
+
+ *argvPtr = argv2;
+ *argcPtr = argc;
+ return 0;
+}
+
+int poptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
+{
+ const char * src;
+ char quote = '\0';
+ int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA;
+ const char ** argv = malloc(sizeof(*argv) * argvAlloced);
+ int argc = 0;
+ int buflen = strlen(s) + 1;
+ char *buf0 = calloc(buflen, 1);
+ char *buf = buf0;
+
+ argv[argc] = buf;
+
+ for (src = s; *src; src++) {
+ if (quote == *src) {
+ quote = '\0';
+ } else if (quote) {
+ if (*src == '\\') {
+ src++;
+ if (!*src) {
+ free(argv);
+ free(buf0);
+ return POPT_ERROR_BADQUOTE;
+ }
+ if (*src != quote) *buf++ = '\\';
+ }
+ *buf++ = *src;
+ } else if (isspace(*src)) {
+ if (*argv[argc]) {
+ buf++, argc++;
+ if (argc == argvAlloced) {
+ argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA;
+ argv = realloc(argv, sizeof(*argv) * argvAlloced);
+ }
+ argv[argc] = buf;
+ }
+ } else switch (*src) {
+ case '"':
+ case '\'':
+ quote = *src;
+ break;
+ case '\\':
+ src++;
+ if (!*src) {
+ free(argv);
+ free(buf0);
+ return POPT_ERROR_BADQUOTE;
+ }
+ /*@fallthrough@*/
+ default:
+ *buf++ = *src;
+ break;
+ }
+ }
+
+ if (strlen(argv[argc])) {
+ argc++, buf++;
+ }
+
+ (void) poptDupArgv(argc, argv, argcPtr, argvPtr);
+
+ free(argv);
+ free(buf0);
+ return 0;
+}
diff --git a/source/popt/system.h b/source/popt/system.h
new file mode 100644
index 00000000000..059c0458176
--- /dev/null
+++ b/source/popt/system.h
@@ -0,0 +1,53 @@
+#include "config.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#if HAVE_MCHECK_H
+#include <mcheck.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef __NeXT
+/* access macros are not declared in non posix mode in unistd.h -
+ don't try to use posix on NeXTstep 3.3 ! */
+#include <libc.h>
+#endif
+
+/* AIX requires this to be the first thing in the file. */
+#ifndef __GNUC__
+# if HAVE_ALLOCA_H
+# include <alloca.h>
+# else
+# ifdef _AIX
+#pragma alloca
+# else
+# ifndef alloca /* predefined by HP cc +Olibcalls */
+char *alloca ();
+# endif
+# endif
+# endif
+#elif defined(__GNUC__) && defined(__STRICT_ANSI__)
+#define alloca __builtin_alloca
+#endif
+
+/*@only@*/ char * xstrdup (const char *str);
+
+#if HAVE_MCHECK_H && defined(__GNUC__)
+#define vmefail() (fprintf(stderr, "virtual memory exhausted.\n"), exit(EXIT_FAILURE), NULL)
+#define xstrdup(_str) (strcpy((malloc(strlen(_str)+1) ? : vmefail()), (_str)))
+#else
+#define xstrdup(_str) strdup(_str)
+#endif /* HAVE_MCHECK_H && defined(__GNUC__) */
+
+
+#include "popt.h"
diff --git a/source/printing/.cvsignore b/source/printing/.cvsignore
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/source/printing/.cvsignore
diff --git a/source/printing/load.c b/source/printing/load.c
new file mode 100644
index 00000000000..cd90cbb6f33
--- /dev/null
+++ b/source/printing/load.c
@@ -0,0 +1,73 @@
+/*
+ Unix SMB/CIFS implementation.
+ load printer lists
+ Copyright (C) Andrew Tridgell 1992-2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+
+/***************************************************************************
+auto-load printer services
+***************************************************************************/
+void add_all_printers(void)
+{
+ int printers = lp_servicenumber(PRINTERS_NAME);
+
+ if (printers < 0) return;
+
+ pcap_printer_fn(lp_add_one_printer);
+}
+
+/***************************************************************************
+auto-load some homes and printer services
+***************************************************************************/
+static void add_auto_printers(void)
+{
+ const char *p;
+ int printers;
+ char *str = strdup(lp_auto_services());
+
+ if (!str) return;
+
+ printers = lp_servicenumber(PRINTERS_NAME);
+
+ if (printers < 0) {
+ SAFE_FREE(str);
+ return;
+ }
+
+ for (p=strtok(str,LIST_SEP);p;p=strtok(NULL,LIST_SEP)) {
+ if (lp_servicenumber(p) >= 0) continue;
+
+ if (pcap_printername_ok(p,NULL)) {
+ lp_add_printer(p,printers);
+ }
+ }
+
+ SAFE_FREE(str);
+}
+
+/***************************************************************************
+load automatic printer services
+***************************************************************************/
+void load_printers(void)
+{
+ add_auto_printers();
+ if (lp_load_printers())
+ add_all_printers();
+}
diff --git a/source/printing/lpq_parse.c b/source/printing/lpq_parse.c
new file mode 100644
index 00000000000..b7e41964f1b
--- /dev/null
+++ b/source/printing/lpq_parse.c
@@ -0,0 +1,1016 @@
+/*
+ Unix SMB/CIFS implementation.
+ lpq parsing routines
+ Copyright (C) Andrew Tridgell 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static const char *Months[13] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Err"};
+
+
+/*******************************************************************
+process time fields
+********************************************************************/
+static time_t EntryTime(fstring tok[], int ptr, int count, int minimum)
+{
+ time_t jobtime,jobtime1;
+
+ jobtime = time(NULL); /* default case: take current time */
+ if (count >= minimum) {
+ struct tm *t;
+ int i, day, hour, min, sec;
+ char *c;
+
+ for (i=0; i<13; i++) if (!strncmp(tok[ptr], Months[i],3)) break; /* Find month */
+ if (i<12) {
+ t = localtime(&jobtime);
+ day = atoi(tok[ptr+1]);
+ c=(char *)(tok[ptr+2]);
+ *(c+2)=0;
+ hour = atoi(c);
+ *(c+5)=0;
+ min = atoi(c+3);
+ if(*(c+6) != 0)sec = atoi(c+6);
+ else sec=0;
+
+ if ((t->tm_mon < i)||
+ ((t->tm_mon == i)&&
+ ((t->tm_mday < day)||
+ ((t->tm_mday == day)&&
+ (t->tm_hour*60+t->tm_min < hour*60+min)))))
+ t->tm_year--; /* last year's print job */
+
+ t->tm_mon = i;
+ t->tm_mday = day;
+ t->tm_hour = hour;
+ t->tm_min = min;
+ t->tm_sec = sec;
+ jobtime1 = mktime(t);
+ if (jobtime1 != (time_t)-1)
+ jobtime = jobtime1;
+ }
+ }
+ return jobtime;
+}
+
+
+/****************************************************************************
+parse a lpq line
+
+here is an example of lpq output under bsd
+
+Warning: no daemon present
+Rank Owner Job Files Total Size
+1st tridge 148 README 8096 bytes
+
+here is an example of lpq output under osf/1
+
+Warning: no daemon present
+Rank Pri Owner Job Files Total Size
+1st 0 tridge 148 README 8096 bytes
+
+
+<allan@umich.edu> June 30, 1998.
+Modified to handle file names with spaces, like the parse_lpq_lprng code
+further below.
+****************************************************************************/
+static BOOL parse_lpq_bsd(char *line,print_queue_struct *buf,BOOL first)
+{
+#ifdef OSF1
+#define RANKTOK 0
+#define PRIOTOK 1
+#define USERTOK 2
+#define JOBTOK 3
+#define FILETOK 4
+#define TOTALTOK (count - 2)
+#define NTOK 6
+#define MAXTOK 128
+#else /* OSF1 */
+#define RANKTOK 0
+#define USERTOK 1
+#define JOBTOK 2
+#define FILETOK 3
+#define TOTALTOK (count - 2)
+#define NTOK 5
+#define MAXTOK 128
+#endif /* OSF1 */
+
+ char *tok[MAXTOK];
+ int count = 0;
+ pstring line2;
+
+ pstrcpy(line2,line);
+
+#ifdef OSF1
+ {
+ size_t length;
+ length = strlen(line2);
+ if (line2[length-3] == ':')
+ return(False);
+ }
+#endif /* OSF1 */
+
+ /* FIXME: Use next_token rather than strtok! */
+ tok[0] = strtok(line2," \t");
+ count++;
+
+ while (((tok[count] = strtok(NULL," \t")) != NULL) && (count < MAXTOK)) {
+ count++;
+ }
+
+ /* we must get at least NTOK tokens */
+ if (count < NTOK)
+ return(False);
+
+ /* the Job and Total columns must be integer */
+ if (!isdigit((int)*tok[JOBTOK]) || !isdigit((int)*tok[TOTALTOK])) return(False);
+
+ buf->job = atoi(tok[JOBTOK]);
+ buf->size = atoi(tok[TOTALTOK]);
+ buf->status = strequal(tok[RANKTOK],"active")?LPQ_PRINTING:LPQ_QUEUED;
+ buf->time = time(NULL);
+ fstrcpy(buf->fs_user,tok[USERTOK]);
+ fstrcpy(buf->fs_file,tok[FILETOK]);
+
+ if ((FILETOK + 1) != TOTALTOK) {
+ int i;
+
+ for (i = (FILETOK + 1); i < TOTALTOK; i++) {
+ /* FIXME: Using fstrcat rather than other means is a bit
+ * inefficient; this might be a problem for enormous queues with
+ * many fields. */
+ fstrcat(buf->fs_file, " ");
+ fstrcat(buf->fs_file, tok[i]);
+ }
+ /* Ensure null termination. */
+ fstrterminate(buf->fs_file);
+ }
+
+#ifdef PRIOTOK
+ buf->priority = atoi(tok[PRIOTOK]);
+#else
+ buf->priority = 1;
+#endif
+ return(True);
+}
+
+/*
+<magnus@hum.auc.dk>
+LPRng_time modifies the current date by inserting the hour and minute from
+the lpq output. The lpq time looks like "23:15:07"
+
+<allan@umich.edu> June 30, 1998.
+Modified to work with the re-written parse_lpq_lprng routine.
+
+<J.P.M.v.Itegem@tue.nl> Dec 17,1999
+Modified to work with lprng 3.16
+With lprng 3.16 The lpq time looks like
+ "23:15:07"
+ "23:15:07.100"
+ "1999-12-16-23:15:07"
+ "1999-12-16-23:15:07.100"
+
+*/
+static time_t LPRng_time(char *time_string)
+{
+ time_t jobtime;
+ struct tm t;
+
+ jobtime = time(NULL); /* default case: take current time */
+ t = *localtime(&jobtime);
+
+ if ( atoi(time_string) < 24 ){
+ t.tm_hour = atoi(time_string);
+ t.tm_min = atoi(time_string+3);
+ t.tm_sec = atoi(time_string+6);
+ } else {
+ t.tm_year = atoi(time_string)-1900;
+ t.tm_mon = atoi(time_string+5)-1;
+ t.tm_mday = atoi(time_string+8);
+ t.tm_hour = atoi(time_string+11);
+ t.tm_min = atoi(time_string+14);
+ t.tm_sec = atoi(time_string+17);
+ }
+ jobtime = mktime(&t);
+
+ return jobtime;
+}
+
+
+/****************************************************************************
+ parse a lprng lpq line
+ <allan@umich.edu> June 30, 1998.
+ Re-wrote this to handle file names with spaces, multiple file names on one
+ lpq line, etc;
+****************************************************************************/
+static BOOL parse_lpq_lprng(char *line,print_queue_struct *buf,BOOL first)
+{
+#define LPRNG_RANKTOK 0
+#define LPRNG_USERTOK 1
+#define LPRNG_PRIOTOK 2
+#define LPRNG_JOBTOK 3
+#define LPRNG_FILETOK 4
+#define LPRNG_TOTALTOK (num_tok - 2)
+#define LPRNG_TIMETOK (num_tok - 1)
+#define LPRNG_NTOK 7
+#define LPRNG_MAXTOK 128 /* PFMA just to keep us from running away. */
+
+ fstring tokarr[LPRNG_MAXTOK];
+ const char *cptr;
+ char *ptr;
+ int num_tok = 0;
+
+ cptr = line;
+ while(next_token( &cptr, tokarr[num_tok], " \t", sizeof(fstring)) && (num_tok < LPRNG_MAXTOK))
+ num_tok++;
+
+ /* We must get at least LPRNG_NTOK tokens. */
+ if (num_tok < LPRNG_NTOK) {
+ return(False);
+ }
+
+ if (!isdigit((int)*tokarr[LPRNG_JOBTOK]) || !isdigit((int)*tokarr[LPRNG_TOTALTOK])) {
+ return(False);
+ }
+
+ buf->job = atoi(tokarr[LPRNG_JOBTOK]);
+ buf->size = atoi(tokarr[LPRNG_TOTALTOK]);
+
+ if (strequal(tokarr[LPRNG_RANKTOK],"active")) {
+ buf->status = LPQ_PRINTING;
+ } else if (strequal(tokarr[LPRNG_RANKTOK],"done")) {
+ buf->status = LPQ_PRINTED;
+ } else if (isdigit((int)*tokarr[LPRNG_RANKTOK])) {
+ buf->status = LPQ_QUEUED;
+ } else {
+ buf->status = LPQ_PAUSED;
+ }
+
+ buf->priority = *tokarr[LPRNG_PRIOTOK] -'A';
+
+ buf->time = LPRng_time(tokarr[LPRNG_TIMETOK]);
+
+ fstrcpy(buf->fs_user,tokarr[LPRNG_USERTOK]);
+
+ /* The '@hostname' prevents windows from displaying the printing icon
+ * for the current user on the taskbar. Plop in a null.
+ */
+
+ if ((ptr = strchr_m(buf->fs_user,'@')) != NULL) {
+ *ptr = '\0';
+ }
+
+ fstrcpy(buf->fs_file,tokarr[LPRNG_FILETOK]);
+
+ if ((LPRNG_FILETOK + 1) != LPRNG_TOTALTOK) {
+ int i;
+
+ for (i = (LPRNG_FILETOK + 1); i < LPRNG_TOTALTOK; i++) {
+ /* FIXME: Using fstrcat rather than other means is a bit
+ * inefficient; this might be a problem for enormous queues with
+ * many fields. */
+ fstrcat(buf->fs_file, " ");
+ fstrcat(buf->fs_file, tokarr[i]);
+ }
+ /* Ensure null termination. */
+ fstrterminate(buf->fs_file);
+ }
+
+ return(True);
+}
+
+
+
+/*******************************************************************
+parse lpq on an aix system
+
+Queue Dev Status Job Files User PP % Blks Cp Rnk
+------- ----- --------- --- ------------------ ---------- ---- -- ----- --- ---
+lazer lazer READY
+lazer lazer RUNNING 537 6297doc.A kvintus@IE 0 10 2445 1 1
+ QUEUED 538 C.ps root@IEDVB 124 1 2
+ QUEUED 539 E.ps root@IEDVB 28 1 3
+ QUEUED 540 L.ps root@IEDVB 172 1 4
+ QUEUED 541 P.ps root@IEDVB 22 1 5
+********************************************************************/
+static BOOL parse_lpq_aix(char *line,print_queue_struct *buf,BOOL first)
+{
+ fstring tok[11];
+ int count=0;
+ const char *cline = line;
+
+ /* handle the case of "(standard input)" as a filename */
+ string_sub(line,"standard input","STDIN",0);
+ all_string_sub(line,"(","\"",0);
+ all_string_sub(line,")","\"",0);
+
+ for (count=0;
+ count<10 &&
+ next_token(&cline,tok[count],NULL, sizeof(tok[count]));
+ count++) ;
+
+ /* we must get 6 tokens */
+ if (count < 10)
+ {
+ if ((count == 7) && ((strcmp(tok[0],"QUEUED") == 0) || (strcmp(tok[0],"HELD") == 0)))
+ {
+ /* the 2nd and 5th columns must be integer */
+ if (!isdigit((int)*tok[1]) || !isdigit((int)*tok[4])) return(False);
+ buf->size = atoi(tok[4]) * 1024;
+ /* if the fname contains a space then use STDIN */
+ if (strchr_m(tok[2],' '))
+ fstrcpy(tok[2],"STDIN");
+
+ /* only take the last part of the filename */
+ {
+ fstring tmp;
+ char *p = strrchr_m(tok[2],'/');
+ if (p)
+ {
+ fstrcpy(tmp,p+1);
+ fstrcpy(tok[2],tmp);
+ }
+ }
+
+
+ buf->job = atoi(tok[1]);
+ buf->status = strequal(tok[0],"HELD")?LPQ_PAUSED:LPQ_QUEUED;
+ buf->priority = 0;
+ buf->time = time(NULL);
+ fstrcpy(buf->fs_user,tok[3]);
+ fstrcpy(buf->fs_file,tok[2]);
+ }
+ else
+ {
+ DEBUG(6,("parse_lpq_aix count=%d\n", count));
+ return(False);
+ }
+ }
+ else
+ {
+ /* the 4th and 9th columns must be integer */
+ if (!isdigit((int)*tok[3]) || !isdigit((int)*tok[8])) return(False);
+ buf->size = atoi(tok[8]) * 1024;
+ /* if the fname contains a space then use STDIN */
+ if (strchr_m(tok[4],' '))
+ fstrcpy(tok[4],"STDIN");
+
+ /* only take the last part of the filename */
+ {
+ fstring tmp;
+ char *p = strrchr_m(tok[4],'/');
+ if (p)
+ {
+ fstrcpy(tmp,p+1);
+ fstrcpy(tok[4],tmp);
+ }
+ }
+
+
+ buf->job = atoi(tok[3]);
+ buf->status = strequal(tok[2],"RUNNING")?LPQ_PRINTING:LPQ_QUEUED;
+ buf->priority = 0;
+ buf->time = time(NULL);
+ fstrcpy(buf->fs_user,tok[5]);
+ fstrcpy(buf->fs_file,tok[4]);
+ }
+
+
+ return(True);
+}
+
+
+/****************************************************************************
+parse a lpq line
+here is an example of lpq output under hpux; note there's no space after -o !
+$> lpstat -oljplus
+ljplus-2153 user priority 0 Jan 19 08:14 on ljplus
+ util.c 125697 bytes
+ server.c 110712 bytes
+ljplus-2154 user priority 0 Jan 19 08:14 from client
+ (standard input) 7551 bytes
+****************************************************************************/
+static BOOL parse_lpq_hpux(char *line, print_queue_struct *buf, BOOL first)
+{
+ /* must read two lines to process, therefore keep some values static */
+ static BOOL header_line_ok=False, base_prio_reset=False;
+ static fstring jobuser;
+ static int jobid;
+ static int jobprio;
+ static time_t jobtime;
+ static int jobstat=LPQ_QUEUED;
+ /* to store minimum priority to print, lpstat command should be invoked
+ with -p option first, to work */
+ static int base_prio;
+ int count;
+ char htab = '\011';
+ const char *cline = line;
+ fstring tok[12];
+
+ /* If a line begins with a horizontal TAB, it is a subline type */
+
+ if (line[0] == htab) { /* subline */
+ /* check if it contains the base priority */
+ if (!strncmp(line,"\tfence priority : ",18)) {
+ base_prio=atoi(&line[18]);
+ DEBUG(4, ("fence priority set at %d\n", base_prio));
+ }
+ if (!header_line_ok) return (False); /* incorrect header line */
+ /* handle the case of "(standard input)" as a filename */
+ string_sub(line,"standard input","STDIN",0);
+ all_string_sub(line,"(","\"",0);
+ all_string_sub(line,")","\"",0);
+
+ for (count=0; count<2 && next_token(&cline,tok[count],NULL,sizeof(tok[count])); count++) ;
+ /* we must get 2 tokens */
+ if (count < 2) return(False);
+
+ /* the 2nd column must be integer */
+ if (!isdigit((int)*tok[1])) return(False);
+
+ /* if the fname contains a space then use STDIN */
+ if (strchr_m(tok[0],' '))
+ fstrcpy(tok[0],"STDIN");
+
+ buf->size = atoi(tok[1]);
+ fstrcpy(buf->fs_file,tok[0]);
+
+ /* fill things from header line */
+ buf->time = jobtime;
+ buf->job = jobid;
+ buf->status = jobstat;
+ buf->priority = jobprio;
+ fstrcpy(buf->fs_user,jobuser);
+
+ return(True);
+ }
+ else { /* header line */
+ header_line_ok=False; /* reset it */
+ if (first) {
+ if (!base_prio_reset) {
+ base_prio=0; /* reset it */
+ base_prio_reset=True;
+ }
+ }
+ else if (base_prio) base_prio_reset=False;
+
+ /* handle the dash in the job id */
+ string_sub(line,"-"," ",0);
+
+ for (count=0; count<12 && next_token(&cline,tok[count],NULL,sizeof(tok[count])); count++) ;
+
+ /* we must get 8 tokens */
+ if (count < 8) return(False);
+
+ /* first token must be printer name (cannot check ?) */
+ /* the 2nd, 5th & 7th column must be integer */
+ if (!isdigit((int)*tok[1]) || !isdigit((int)*tok[4]) || !isdigit((int)*tok[6])) return(False);
+ jobid = atoi(tok[1]);
+ fstrcpy(jobuser,tok[2]);
+ jobprio = atoi(tok[4]);
+
+ /* process time */
+ jobtime=EntryTime(tok, 5, count, 8);
+ if (jobprio < base_prio) {
+ jobstat = LPQ_PAUSED;
+ DEBUG (4, ("job %d is paused: prio %d < %d; jobstat=%d\n", jobid, jobprio, base_prio, jobstat));
+ }
+ else {
+ jobstat = LPQ_QUEUED;
+ if ((count >8) && (((strequal(tok[8],"on")) ||
+ ((strequal(tok[8],"from")) &&
+ ((count > 10)&&(strequal(tok[10],"on")))))))
+ jobstat = LPQ_PRINTING;
+ }
+
+ header_line_ok=True; /* information is correct */
+ return(False); /* need subline info to include into queuelist */
+ }
+}
+
+
+/****************************************************************************
+parse a lpstat line
+
+here is an example of "lpstat -o dcslw" output under sysv
+
+dcslw-896 tridge 4712 Dec 20 10:30:30 on dcslw
+dcslw-897 tridge 4712 Dec 20 10:30:30 being held
+
+****************************************************************************/
+static BOOL parse_lpq_sysv(char *line,print_queue_struct *buf,BOOL first)
+{
+ fstring tok[9];
+ int count=0;
+ char *p;
+ const char *cline = line;
+
+ /*
+ * Handle the dash in the job id, but make sure that we skip over
+ * the printer name in case we have a dash in that.
+ * Patch from Dom.Mitchell@palmerharvey.co.uk.
+ */
+
+ /*
+ * Move to the first space.
+ */
+ for (p = line ; !isspace(*p) && *p; p++)
+ ;
+
+ /*
+ * Back up until the last '-' character or
+ * start of line.
+ */
+ for (; (p >= line) && (*p != '-'); p--)
+ ;
+
+ if((p >= line) && (*p == '-'))
+ *p = ' ';
+
+ for (count=0; count<9 && next_token(&cline,tok[count],NULL,sizeof(tok[count])); count++)
+ ;
+
+ /* we must get 7 tokens */
+ if (count < 7)
+ return(False);
+
+ /* the 2nd and 4th, 6th columns must be integer */
+ if (!isdigit((int)*tok[1]) || !isdigit((int)*tok[3]))
+ return(False);
+ if (!isdigit((int)*tok[5]))
+ return(False);
+
+ /* if the user contains a ! then trim the first part of it */
+ if ((p=strchr_m(tok[2],'!'))) {
+ fstring tmp;
+ fstrcpy(tmp,p+1);
+ fstrcpy(tok[2],tmp);
+ }
+
+ buf->job = atoi(tok[1]);
+ buf->size = atoi(tok[3]);
+ if (count > 7 && strequal(tok[7],"on"))
+ buf->status = LPQ_PRINTING;
+ else if (count > 8 && strequal(tok[7],"being") && strequal(tok[8],"held"))
+ buf->status = LPQ_PAUSED;
+ else
+ buf->status = LPQ_QUEUED;
+ buf->priority = 0;
+ buf->time = EntryTime(tok, 4, count, 7);
+ fstrcpy(buf->fs_user,tok[2]);
+ fstrcpy(buf->fs_file,tok[2]);
+ return(True);
+}
+
+/****************************************************************************
+parse a lpq line
+
+here is an example of lpq output under qnx
+Spooler: /qnx/spooler, on node 1
+Printer: txt (ready)
+0000: root [job #1 ] active 1146 bytes /etc/profile
+0001: root [job #2 ] ready 2378 bytes /etc/install
+0002: root [job #3 ] ready 1146 bytes -- standard input --
+****************************************************************************/
+static BOOL parse_lpq_qnx(char *line,print_queue_struct *buf,BOOL first)
+{
+ fstring tok[7];
+ int count=0;
+ const char *cline = line;
+
+ DEBUG(4,("antes [%s]\n", line));
+
+ /* handle the case of "-- standard input --" as a filename */
+ string_sub(line,"standard input","STDIN",0);
+ DEBUG(4,("despues [%s]\n", line));
+ all_string_sub(line,"-- ","\"",0);
+ all_string_sub(line," --","\"",0);
+ DEBUG(4,("despues 1 [%s]\n", line));
+
+ string_sub(line,"[job #","",0);
+ string_sub(line,"]","",0);
+ DEBUG(4,("despues 2 [%s]\n", line));
+
+ for (count=0; count<7 && next_token(&cline,tok[count],NULL,sizeof(tok[count])); count++) ;
+
+ /* we must get 7 tokens */
+ if (count < 7)
+ return(False);
+
+ /* the 3rd and 5th columns must be integer */
+ if (!isdigit((int)*tok[2]) || !isdigit((int)*tok[4])) return(False);
+
+ /* only take the last part of the filename */
+ {
+ fstring tmp;
+ char *p = strrchr_m(tok[6],'/');
+ if (p)
+ {
+ fstrcpy(tmp,p+1);
+ fstrcpy(tok[6],tmp);
+ }
+ }
+
+
+ buf->job = atoi(tok[2]);
+ buf->size = atoi(tok[4]);
+ buf->status = strequal(tok[3],"active")?LPQ_PRINTING:LPQ_QUEUED;
+ buf->priority = 0;
+ buf->time = time(NULL);
+ fstrcpy(buf->fs_user,tok[1]);
+ fstrcpy(buf->fs_file,tok[6]);
+ return(True);
+}
+
+
+/****************************************************************************
+ parse a lpq line for the plp printing system
+ Bertrand Wallrich <Bertrand.Wallrich@loria.fr>
+
+redone by tridge. Here is a sample queue:
+
+Local Printer 'lp2' (fjall):
+ Printing (started at Jun 15 13:33:58, attempt 1).
+ Rank Owner Pr Opt Job Host Files Size Date
+ active tridge X - 6 fjall /etc/hosts 739 Jun 15 13:33
+ 3rd tridge X - 7 fjall /etc/hosts 739 Jun 15 13:33
+
+****************************************************************************/
+static BOOL parse_lpq_plp(char *line,print_queue_struct *buf,BOOL first)
+{
+ fstring tok[11];
+ int count=0;
+ const char *cline = line;
+
+ /* handle the case of "(standard input)" as a filename */
+ string_sub(line,"stdin","STDIN",0);
+ all_string_sub(line,"(","\"",0);
+ all_string_sub(line,")","\"",0);
+
+ for (count=0; count<11 && next_token(&cline,tok[count],NULL,sizeof(tok[count])); count++) ;
+
+ /* we must get 11 tokens */
+ if (count < 11)
+ return(False);
+
+ /* the first must be "active" or begin with an integer */
+ if (strcmp(tok[0],"active") && !isdigit((int)tok[0][0]))
+ return(False);
+
+ /* the 5th and 8th must be integer */
+ if (!isdigit((int)*tok[4]) || !isdigit((int)*tok[7]))
+ return(False);
+
+ /* if the fname contains a space then use STDIN */
+ if (strchr_m(tok[6],' '))
+ fstrcpy(tok[6],"STDIN");
+
+ /* only take the last part of the filename */
+ {
+ fstring tmp;
+ char *p = strrchr_m(tok[6],'/');
+ if (p)
+ {
+ fstrcpy(tmp,p+1);
+ fstrcpy(tok[6],tmp);
+ }
+ }
+
+
+ buf->job = atoi(tok[4]);
+
+ buf->size = atoi(tok[7]);
+ if (strchr_m(tok[7],'K'))
+ buf->size *= 1024;
+ if (strchr_m(tok[7],'M'))
+ buf->size *= 1024*1024;
+
+ buf->status = strequal(tok[0],"active")?LPQ_PRINTING:LPQ_QUEUED;
+ buf->priority = 0;
+ buf->time = time(NULL);
+ fstrcpy(buf->fs_user,tok[1]);
+ fstrcpy(buf->fs_file,tok[6]);
+ return(True);
+}
+
+/*******************************************************************
+parse lpq on an NT system
+
+ Windows 2000 LPD Server
+ Printer \\10.0.0.2\NP17PCL (Paused)
+
+Owner Status Jobname Job-Id Size Pages Priority
+----------------------------------------------------------------------------
+root (9.99. Printing /usr/lib/rhs/rhs-pr 3 625 0 1
+root (9.99. Paused /usr/lib/rhs/rhs-pr 4 625 0 1
+jmcd Waiting Re: Samba Open Sour 26 32476 1 1
+
+********************************************************************/
+static BOOL parse_lpq_nt(char *line,print_queue_struct *buf,BOOL first)
+{
+#define LPRNT_OWNSIZ 11
+#define LPRNT_STATSIZ 9
+#define LPRNT_JOBSIZ 19
+#define LPRNT_IDSIZ 6
+#define LPRNT_SIZSIZ 9
+ typedef struct
+ {
+ char owner[LPRNT_OWNSIZ];
+ char space1;
+ char status[LPRNT_STATSIZ];
+ char space2;
+ char jobname[LPRNT_JOBSIZ];
+ char space3;
+ char jobid[LPRNT_IDSIZ];
+ char space4;
+ char size[LPRNT_SIZSIZ];
+ char terminator;
+ } nt_lpq_line;
+
+ nt_lpq_line parse_line;
+#define LPRNT_PRINTING "Printing"
+#define LPRNT_WAITING "Waiting"
+#define LPRNT_PAUSED "Paused"
+
+ memset(&parse_line, '\0', sizeof(parse_line));
+ strncpy((char *) &parse_line, line, sizeof(parse_line) -1);
+
+ if (strlen((char *) &parse_line) != sizeof(parse_line) - 1)
+ return(False);
+
+ /* Just want the first word in the owner field - the username */
+ if (strchr_m(parse_line.owner, ' '))
+ *(strchr_m(parse_line.owner, ' ')) = '\0';
+ else
+ parse_line.space1 = '\0';
+
+ /* Make sure we have an owner */
+ if (!strlen(parse_line.owner))
+ return(False);
+
+ /* Make sure the status is valid */
+ parse_line.space2 = '\0';
+ trim_char(parse_line.status, '\0', ' ');
+ if (!strequal(parse_line.status, LPRNT_PRINTING) &&
+ !strequal(parse_line.status, LPRNT_PAUSED) &&
+ !strequal(parse_line.status, LPRNT_WAITING))
+ return(False);
+
+ parse_line.space3 = '\0';
+ trim_char(parse_line.jobname, '\0', ' ');
+
+ buf->job = atoi(parse_line.jobid);
+ buf->priority = 0;
+ buf->size = atoi(parse_line.size);
+ buf->time = time(NULL);
+ fstrcpy(buf->fs_user, parse_line.owner);
+ fstrcpy(buf->fs_file, parse_line.jobname);
+ if (strequal(parse_line.status, LPRNT_PRINTING))
+ buf->status = LPQ_PRINTING;
+ else if (strequal(parse_line.status, LPRNT_PAUSED))
+ buf->status = LPQ_PAUSED;
+ else
+ buf->status = LPQ_QUEUED;
+
+ return(True);
+}
+
+/*******************************************************************
+parse lpq on an OS2 system
+
+JobID File Name Rank Size Status Comment
+----- --------------- ------ -------- ------------ ------------
+ 3 Control 1 68 Queued root@psflinu
+ 4 /etc/motd 2 11666 Queued root@psflinu
+
+********************************************************************/
+static BOOL parse_lpq_os2(char *line,print_queue_struct *buf,BOOL first)
+{
+#define LPROS2_IDSIZ 5
+#define LPROS2_JOBSIZ 15
+#define LPROS2_SIZSIZ 8
+#define LPROS2_STATSIZ 12
+#define LPROS2_OWNSIZ 12
+ typedef struct
+ {
+ char jobid[LPROS2_IDSIZ];
+ char space1[2];
+ char jobname[LPROS2_JOBSIZ];
+ char space2[14];
+ char size[LPROS2_SIZSIZ];
+ char space3[4];
+ char status[LPROS2_STATSIZ];
+ char space4[4];
+ char owner[LPROS2_OWNSIZ];
+ char terminator;
+ } os2_lpq_line;
+
+ os2_lpq_line parse_line;
+#define LPROS2_PRINTING "Printing"
+#define LPROS2_WAITING "Queued"
+#define LPROS2_PAUSED "Paused"
+
+ memset(&parse_line, '\0', sizeof(parse_line));
+ strncpy((char *) &parse_line, line, sizeof(parse_line) -1);
+
+ if (strlen((char *) &parse_line) != sizeof(parse_line) - 1)
+ return(False);
+
+ /* Get the jobid */
+ buf->job = atoi(parse_line.jobid);
+
+ /* Get the job name */
+ parse_line.space2[0] = '\0';
+ trim_char(parse_line.jobname, '\0', ' ');
+ fstrcpy(buf->fs_file, parse_line.jobname);
+
+ buf->priority = 0;
+ buf->size = atoi(parse_line.size);
+ buf->time = time(NULL);
+
+ /* Make sure we have an owner */
+ if (!strlen(parse_line.owner))
+ return(False);
+
+ /* Make sure we have a valid status */
+ parse_line.space4[0] = '\0';
+ trim_char(parse_line.status, '\0', ' ');
+ if (!strequal(parse_line.status, LPROS2_PRINTING) &&
+ !strequal(parse_line.status, LPROS2_PAUSED) &&
+ !strequal(parse_line.status, LPROS2_WAITING))
+ return(False);
+
+ fstrcpy(buf->fs_user, parse_line.owner);
+ if (strequal(parse_line.status, LPROS2_PRINTING))
+ buf->status = LPQ_PRINTING;
+ else if (strequal(parse_line.status, LPROS2_PAUSED))
+ buf->status = LPQ_PAUSED;
+ else
+ buf->status = LPQ_QUEUED;
+
+ return(True);
+}
+
+static const char *stat0_strings[] = { "enabled", "online", "idle", "no entries", "free", "ready", NULL };
+static const char *stat1_strings[] = { "offline", "disabled", "down", "off", "waiting", "no daemon", NULL };
+static const char *stat2_strings[] = { "jam", "paper", "error", "responding", "not accepting", "not running", "turned off", NULL };
+
+#ifdef DEVELOPER
+
+/****************************************************************************
+parse a vlp line
+****************************************************************************/
+static BOOL parse_lpq_vlp(char *line,print_queue_struct *buf,BOOL first)
+{
+ int toknum = 0;
+ fstring tok;
+ const char *cline = line;
+
+ /* First line is printer status */
+
+ if (!isdigit(line[0])) return False;
+
+ /* Parse a print job entry */
+
+ while(next_token(&cline, tok, NULL, sizeof(fstring))) {
+ switch (toknum) {
+ case 0:
+ buf->job = atoi(tok);
+ break;
+ case 1:
+ buf->size = atoi(tok);
+ break;
+ case 2:
+ buf->status = atoi(tok);
+ break;
+ case 3:
+ buf->time = atoi(tok);
+ break;
+ case 4:
+ fstrcpy(buf->fs_user, tok);
+ break;
+ case 5:
+ fstrcpy(buf->fs_file, tok);
+ break;
+ }
+ toknum++;
+ }
+
+ return True;
+}
+
+#endif /* DEVELOPER */
+
+/****************************************************************************
+parse a lpq line. Choose printing style
+****************************************************************************/
+
+BOOL parse_lpq_entry(int snum,char *line,
+ print_queue_struct *buf,
+ print_status_struct *status,BOOL first)
+{
+ BOOL ret;
+
+ switch (lp_printing(snum))
+ {
+ case PRINT_SYSV:
+ ret = parse_lpq_sysv(line,buf,first);
+ break;
+ case PRINT_AIX:
+ ret = parse_lpq_aix(line,buf,first);
+ break;
+ case PRINT_HPUX:
+ ret = parse_lpq_hpux(line,buf,first);
+ break;
+ case PRINT_QNX:
+ ret = parse_lpq_qnx(line,buf,first);
+ break;
+ case PRINT_LPRNG:
+ ret = parse_lpq_lprng(line,buf,first);
+ break;
+ case PRINT_PLP:
+ ret = parse_lpq_plp(line,buf,first);
+ break;
+ case PRINT_LPRNT:
+ ret = parse_lpq_nt(line,buf,first);
+ break;
+ case PRINT_LPROS2:
+ ret = parse_lpq_os2(line,buf,first);
+ break;
+#ifdef DEVELOPER
+ case PRINT_VLP:
+ case PRINT_TEST:
+ ret = parse_lpq_vlp(line,buf,first);
+ break;
+#endif /* DEVELOPER */
+ default:
+ ret = parse_lpq_bsd(line,buf,first);
+ break;
+ }
+
+ /* We don't want the newline in the status message. */
+ {
+ char *p = strchr_m(line,'\n');
+ if (p) *p = 0;
+ }
+
+ /* in the LPRNG case, we skip lines starting by a space.*/
+ if (line && !ret && (lp_printing(snum)==PRINT_LPRNG) )
+ {
+ if (line[0]==' ')
+ return ret;
+ }
+
+
+ if (status && !ret)
+ {
+ /* a few simple checks to see if the line might be a
+ printer status line:
+ handle them so that most severe condition is shown */
+ int i;
+ strlower_m(line);
+
+ switch (status->status) {
+ case LPSTAT_OK:
+ for (i=0; stat0_strings[i]; i++)
+ if (strstr_m(line,stat0_strings[i])) {
+ fstrcpy(status->message,line);
+ status->status=LPSTAT_OK;
+ return ret;
+ }
+ case LPSTAT_STOPPED:
+ for (i=0; stat1_strings[i]; i++)
+ if (strstr_m(line,stat1_strings[i])) {
+ fstrcpy(status->message,line);
+ status->status=LPSTAT_STOPPED;
+ return ret;
+ }
+ case LPSTAT_ERROR:
+ for (i=0; stat2_strings[i]; i++)
+ if (strstr_m(line,stat2_strings[i])) {
+ fstrcpy(status->message,line);
+ status->status=LPSTAT_ERROR;
+ return ret;
+ }
+ break;
+ }
+ }
+
+ return(ret);
+}
diff --git a/source/printing/notify.c b/source/printing/notify.c
new file mode 100644
index 00000000000..7750239630c
--- /dev/null
+++ b/source/printing/notify.c
@@ -0,0 +1,555 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ printing backend routines
+ Copyright (C) Tim Potter, 2002
+ Copyright (C) Gerald Carter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "printing.h"
+
+static TALLOC_CTX *send_ctx;
+
+static unsigned int num_messages;
+
+static struct notify_queue {
+ struct notify_queue *next, *prev;
+ struct spoolss_notify_msg *msg;
+ struct timeval tv;
+ char *buf;
+ size_t buflen;
+} *notify_queue_head = NULL;
+
+
+static BOOL create_send_ctx(void)
+{
+ if (!send_ctx)
+ send_ctx = talloc_init("print notify queue");
+
+ if (!send_ctx)
+ return False;
+
+ return True;
+}
+
+/****************************************************************************
+ Turn a queue name into a snum.
+****************************************************************************/
+
+int print_queue_snum(const char *qname)
+{
+ int snum = lp_servicenumber(qname);
+ if (snum == -1 || !lp_print_ok(snum))
+ return -1;
+ return snum;
+}
+
+/*******************************************************************
+ Used to decide if we need a short select timeout.
+*******************************************************************/
+
+BOOL print_notify_messages_pending(void)
+{
+ return (notify_queue_head != NULL);
+}
+
+/*******************************************************************
+ Flatten data into a message.
+*******************************************************************/
+
+static BOOL flatten_message(struct notify_queue *q)
+{
+ struct spoolss_notify_msg *msg = q->msg;
+ char *buf = NULL;
+ size_t buflen = 0, len;
+
+again:
+ len = 0;
+
+ /* Pack header */
+
+ len += tdb_pack(buf + len, buflen - len, "f", msg->printer);
+
+ len += tdb_pack(buf + len, buflen - len, "ddddddd",
+ (uint32)q->tv.tv_sec, (uint32)q->tv.tv_usec,
+ msg->type, msg->field, msg->id, msg->len, msg->flags);
+
+ /* Pack data */
+
+ if (msg->len == 0)
+ len += tdb_pack(buf + len, buflen - len, "dd",
+ msg->notify.value[0], msg->notify.value[1]);
+ else
+ len += tdb_pack(buf + len, buflen - len, "B",
+ msg->len, msg->notify.data);
+
+ if (buflen != len) {
+ buf = talloc_realloc(send_ctx, buf, len);
+ if (!buf)
+ return False;
+ buflen = len;
+ goto again;
+ }
+
+ q->buf = buf;
+ q->buflen = buflen;
+
+ return True;
+}
+
+/*******************************************************************
+ Send the batched messages - on a per-printer basis.
+*******************************************************************/
+
+static void print_notify_send_messages_to_printer(const char *printer, unsigned int timeout)
+{
+ char *buf;
+ struct notify_queue *pq, *pq_next;
+ size_t msg_count = 0, offset = 0;
+ size_t num_pids = 0;
+ size_t i;
+ pid_t *pid_list = NULL;
+
+ /* Count the space needed to send the messages. */
+ for (pq = notify_queue_head; pq; pq = pq->next) {
+ if (strequal(printer, pq->msg->printer)) {
+ if (!flatten_message(pq)) {
+ DEBUG(0,("print_notify_send_messages: Out of memory\n"));
+ talloc_destroy_pool(send_ctx);
+ num_messages = 0;
+ return;
+ }
+ offset += (pq->buflen + 4);
+ msg_count++;
+ }
+ }
+ offset += 4; /* For count. */
+
+ buf = talloc(send_ctx, offset);
+ if (!buf) {
+ DEBUG(0,("print_notify_send_messages: Out of memory\n"));
+ talloc_destroy_pool(send_ctx);
+ num_messages = 0;
+ return;
+ }
+
+ offset = 0;
+ SIVAL(buf,offset,msg_count);
+ offset += 4;
+ for (pq = notify_queue_head; pq; pq = pq_next) {
+ pq_next = pq->next;
+
+ if (strequal(printer, pq->msg->printer)) {
+ SIVAL(buf,offset,pq->buflen);
+ offset += 4;
+ memcpy(buf + offset, pq->buf, pq->buflen);
+ offset += pq->buflen;
+
+ /* Remove from list. */
+ DLIST_REMOVE(notify_queue_head, pq);
+ }
+ }
+
+ DEBUG(5, ("print_notify_send_messages_to_printer: sending %lu print notify message%s to printer %s\n",
+ (unsigned long)msg_count, msg_count != 1 ? "s" : "", printer));
+
+ /*
+ * Get the list of PID's to send to.
+ */
+
+ if (!print_notify_pid_list(printer, send_ctx, &num_pids, &pid_list))
+ return;
+
+ for (i = 0; i < num_pids; i++) {
+ unsigned int q_len = messages_pending_for_pid(pid_list[i]);
+ if (q_len > 1000) {
+ DEBUG(5, ("print_notify_send_messages_to_printer: discarding notify to printer %s as queue length = %u\n",
+ printer, q_len ));
+ continue;
+ }
+ message_send_pid_with_timeout(pid_list[i], MSG_PRINTER_NOTIFY2, buf, offset, True, timeout);
+ }
+}
+
+/*******************************************************************
+ Actually send the batched messages.
+*******************************************************************/
+
+void print_notify_send_messages(unsigned int timeout)
+{
+ if (!print_notify_messages_pending())
+ return;
+
+ if (!create_send_ctx())
+ return;
+
+ while (print_notify_messages_pending())
+ print_notify_send_messages_to_printer(notify_queue_head->msg->printer, timeout);
+
+ talloc_destroy_pool(send_ctx);
+ num_messages = 0;
+}
+
+/**********************************************************************
+ deep copy a SPOOLSS_NOTIFY_MSG structure using a TALLOC_CTX
+ *********************************************************************/
+
+static BOOL copy_notify2_msg( SPOOLSS_NOTIFY_MSG *to, SPOOLSS_NOTIFY_MSG *from )
+{
+
+ if ( !to || !from )
+ return False;
+
+ memcpy( to, from, sizeof(SPOOLSS_NOTIFY_MSG) );
+
+ if ( from->len ) {
+ to->notify.data = talloc_memdup(send_ctx, from->notify.data, from->len );
+ if ( !to->notify.data ) {
+ DEBUG(0,("copy_notify2_msg: talloc_memdup() of size [%d] failed!\n", from->len ));
+ return False;
+ }
+ }
+
+
+ return True;
+}
+
+/*******************************************************************
+ Batch up print notify messages.
+*******************************************************************/
+
+static void send_spoolss_notify2_msg(SPOOLSS_NOTIFY_MSG *msg)
+{
+ struct notify_queue *pnqueue, *tmp_ptr;
+
+ /*
+ * Ensure we only have one job total_bytes and job total_pages for
+ * each job. There is no point in sending multiple messages that match
+ * as they will just cause flickering updates in the client.
+ */
+
+ if ((num_messages < 100) && (msg->type == JOB_NOTIFY_TYPE) &&
+ (msg->field == JOB_NOTIFY_TOTAL_BYTES || msg->field == JOB_NOTIFY_TOTAL_PAGES)) {
+
+ for (tmp_ptr = notify_queue_head; tmp_ptr; tmp_ptr = tmp_ptr->next) {
+ if (tmp_ptr->msg->type == msg->type &&
+ tmp_ptr->msg->field == msg->field &&
+ tmp_ptr->msg->id == msg->id &&
+ tmp_ptr->msg->flags == msg->flags &&
+ strequal(tmp_ptr->msg->printer, msg->printer)) {
+
+ DEBUG(5, ("send_spoolss_notify2_msg: replacing message 0x%02x/0x%02x for printer %s \
+in notify_queue\n", msg->type, msg->field, msg->printer));
+
+ tmp_ptr->msg = msg;
+ return;
+ }
+ }
+ }
+
+ /* Store the message on the pending queue. */
+
+ pnqueue = talloc(send_ctx, sizeof(*pnqueue));
+ if (!pnqueue) {
+ DEBUG(0,("send_spoolss_notify2_msg: Out of memory.\n"));
+ return;
+ }
+
+ /* allocate a new msg structure and copy the fields */
+
+ if ( !(pnqueue->msg = (SPOOLSS_NOTIFY_MSG*)talloc(send_ctx, sizeof(SPOOLSS_NOTIFY_MSG))) ) {
+ DEBUG(0,("send_spoolss_notify2_msg: talloc() of size [%lu] failed!\n",
+ (unsigned long)sizeof(SPOOLSS_NOTIFY_MSG)));
+ return;
+ }
+ copy_notify2_msg(pnqueue->msg, msg);
+ gettimeofday(&pnqueue->tv, NULL);
+ pnqueue->buf = NULL;
+ pnqueue->buflen = 0;
+
+ DEBUG(5, ("send_spoolss_notify2_msg: appending message 0x%02x/0x%02x for printer %s \
+to notify_queue_head\n", msg->type, msg->field, msg->printer));
+
+ /*
+ * Note we add to the end of the list to ensure
+ * the messages are sent in the order they were received. JRA.
+ */
+
+ DLIST_ADD_END(notify_queue_head, pnqueue, tmp_ptr);
+ num_messages++;
+}
+
+static void send_notify_field_values(const char *printer_name, uint32 type,
+ uint32 field, uint32 id, uint32 value1,
+ uint32 value2, uint32 flags)
+{
+ struct spoolss_notify_msg *msg;
+
+ if (lp_disable_spoolss())
+ return;
+
+ if (!create_send_ctx())
+ return;
+
+ msg = (struct spoolss_notify_msg *)talloc(send_ctx, sizeof(struct spoolss_notify_msg));
+ if (!msg)
+ return;
+
+ ZERO_STRUCTP(msg);
+
+ fstrcpy(msg->printer, printer_name);
+ msg->type = type;
+ msg->field = field;
+ msg->id = id;
+ msg->notify.value[0] = value1;
+ msg->notify.value[1] = value2;
+ msg->flags = flags;
+
+ send_spoolss_notify2_msg(msg);
+}
+
+static void send_notify_field_buffer(const char *printer_name, uint32 type,
+ uint32 field, uint32 id, uint32 len,
+ char *buffer)
+{
+ struct spoolss_notify_msg *msg;
+
+ if (lp_disable_spoolss())
+ return;
+
+ if (!create_send_ctx())
+ return;
+
+ msg = (struct spoolss_notify_msg *)talloc(send_ctx, sizeof(struct spoolss_notify_msg));
+ if (!msg)
+ return;
+
+ ZERO_STRUCTP(msg);
+
+ fstrcpy(msg->printer, printer_name);
+ msg->type = type;
+ msg->field = field;
+ msg->id = id;
+ msg->len = len;
+ msg->notify.data = buffer;
+
+ send_spoolss_notify2_msg(msg);
+}
+
+/* Send a message that the printer status has changed */
+
+void notify_printer_status_byname(const char *printer_name, uint32 status)
+{
+ /* Printer status stored in value1 */
+
+ send_notify_field_values(printer_name, PRINTER_NOTIFY_TYPE,
+ PRINTER_NOTIFY_STATUS, 0,
+ status, 0, 0);
+}
+
+void notify_printer_status(int snum, uint32 status)
+{
+ const char *printer_name = SERVICE(snum);
+
+ if (printer_name)
+ notify_printer_status_byname(printer_name, status);
+}
+
+void notify_job_status_byname(const char *printer_name, uint32 jobid, uint32 status,
+ uint32 flags)
+{
+ /* Job id stored in id field, status in value1 */
+
+ send_notify_field_values(printer_name, JOB_NOTIFY_TYPE,
+ JOB_NOTIFY_STATUS, jobid,
+ status, 0, flags);
+}
+
+void notify_job_status(int snum, uint32 jobid, uint32 status)
+{
+ const char *printer_name = SERVICE(snum);
+
+ notify_job_status_byname(printer_name, jobid, status, 0);
+}
+
+void notify_job_total_bytes(int snum, uint32 jobid, uint32 size)
+{
+ const char *printer_name = SERVICE(snum);
+
+ /* Job id stored in id field, status in value1 */
+
+ send_notify_field_values(printer_name, JOB_NOTIFY_TYPE,
+ JOB_NOTIFY_TOTAL_BYTES, jobid,
+ size, 0, 0);
+}
+
+void notify_job_total_pages(int snum, uint32 jobid, uint32 pages)
+{
+ const char *printer_name = SERVICE(snum);
+
+ /* Job id stored in id field, status in value1 */
+
+ send_notify_field_values(printer_name, JOB_NOTIFY_TYPE,
+ JOB_NOTIFY_TOTAL_PAGES, jobid,
+ pages, 0, 0);
+}
+
+void notify_job_username(int snum, uint32 jobid, char *name)
+{
+ const char *printer_name = SERVICE(snum);
+
+ send_notify_field_buffer(
+ printer_name, JOB_NOTIFY_TYPE, JOB_NOTIFY_USER_NAME,
+ jobid, strlen(name) + 1, name);
+}
+
+void notify_job_name(int snum, uint32 jobid, char *name)
+{
+ const char *printer_name = SERVICE(snum);
+
+ send_notify_field_buffer(
+ printer_name, JOB_NOTIFY_TYPE, JOB_NOTIFY_DOCUMENT,
+ jobid, strlen(name) + 1, name);
+}
+
+void notify_job_submitted(int snum, uint32 jobid, time_t submitted)
+{
+ const char *printer_name = SERVICE(snum);
+
+ send_notify_field_buffer(
+ printer_name, JOB_NOTIFY_TYPE, JOB_NOTIFY_SUBMITTED,
+ jobid, sizeof(submitted), (char *)&submitted);
+}
+
+void notify_printer_driver(int snum, char *driver_name)
+{
+ const char *printer_name = SERVICE(snum);
+
+ send_notify_field_buffer(
+ printer_name, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DRIVER_NAME,
+ snum, strlen(driver_name) + 1, driver_name);
+}
+
+void notify_printer_comment(int snum, char *comment)
+{
+ const char *printer_name = SERVICE(snum);
+
+ send_notify_field_buffer(
+ printer_name, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_COMMENT,
+ snum, strlen(comment) + 1, comment);
+}
+
+void notify_printer_sharename(int snum, char *share_name)
+{
+ const char *printer_name = SERVICE(snum);
+
+ send_notify_field_buffer(
+ printer_name, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SHARE_NAME,
+ snum, strlen(share_name) + 1, share_name);
+}
+
+void notify_printer_port(int snum, char *port_name)
+{
+ const char *printer_name = SERVICE(snum);
+
+ send_notify_field_buffer(
+ printer_name, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PORT_NAME,
+ snum, strlen(port_name) + 1, port_name);
+}
+
+void notify_printer_location(int snum, char *location)
+{
+ const char *printer_name = SERVICE(snum);
+
+ send_notify_field_buffer(
+ printer_name, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_LOCATION,
+ snum, strlen(location) + 1, location);
+}
+
+void notify_printer_byname( const char *printername, uint32 change, char *value )
+{
+ int snum = print_queue_snum(printername);
+ int type = PRINTER_NOTIFY_TYPE;
+
+ if ( snum == -1 )
+ return;
+
+ send_notify_field_buffer( printername, type, change, snum, strlen(value)+1, value );
+}
+
+
+/****************************************************************************
+ Return a malloced list of pid_t's that are interested in getting update
+ messages on this print queue. Used in printing/notify to send the messages.
+****************************************************************************/
+
+BOOL print_notify_pid_list(const char *printername, TALLOC_CTX *mem_ctx, size_t *p_num_pids, pid_t **pp_pid_list)
+{
+ struct tdb_print_db *pdb = NULL;
+ TDB_CONTEXT *tdb = NULL;
+ TDB_DATA data;
+ BOOL ret = True;
+ size_t i, num_pids, offset;
+ pid_t *pid_list;
+
+ *p_num_pids = 0;
+ *pp_pid_list = NULL;
+
+ pdb = get_print_db_byname(printername);
+ if (!pdb)
+ return False;
+ tdb = pdb->tdb;
+
+ if (tdb_read_lock_bystring(tdb, NOTIFY_PID_LIST_KEY, 10) == -1) {
+ DEBUG(0,("print_notify_pid_list: Failed to lock printer %s database\n",
+ printername));
+ if (pdb)
+ release_print_db(pdb);
+ return False;
+ }
+
+ data = get_printer_notify_pid_list( tdb, printername, True );
+
+ if (!data.dptr) {
+ ret = True;
+ goto done;
+ }
+
+ num_pids = data.dsize / 8;
+
+ if ((pid_list = (pid_t *)talloc(mem_ctx, sizeof(pid_t) * num_pids)) == NULL) {
+ ret = False;
+ goto done;
+ }
+
+ for( i = 0, offset = 0; offset < data.dsize; offset += 8, i++)
+ pid_list[i] = (pid_t)IVAL(data.dptr, offset);
+
+ *pp_pid_list = pid_list;
+ *p_num_pids = num_pids;
+
+ ret = True;
+
+ done:
+
+ tdb_read_unlock_bystring(tdb, NOTIFY_PID_LIST_KEY);
+ if (pdb)
+ release_print_db(pdb);
+ SAFE_FREE(data.dptr);
+ return ret;
+}
diff --git a/source/printing/nt_printing.c b/source/printing/nt_printing.c
new file mode 100644
index 00000000000..3c860fc5650
--- /dev/null
+++ b/source/printing/nt_printing.c
@@ -0,0 +1,4990 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-2000,
+ * Copyright (C) Jean François Micouleau 1998-2000.
+ * Copyright (C) Gerald Carter 2002-2003.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+extern DOM_SID global_sid_World;
+
+static TDB_CONTEXT *tdb_forms; /* used for forms files */
+static TDB_CONTEXT *tdb_drivers; /* used for driver files */
+static TDB_CONTEXT *tdb_printers; /* used for printers files */
+
+#define FORMS_PREFIX "FORMS/"
+#define DRIVERS_PREFIX "DRIVERS/"
+#define DRIVER_INIT_PREFIX "DRIVER_INIT/"
+#define PRINTERS_PREFIX "PRINTERS/"
+#define SECDESC_PREFIX "SECDESC/"
+#define GLOBAL_C_SETPRINTER "GLOBALS/c_setprinter"
+
+#define NTDRIVERS_DATABASE_VERSION_1 1
+#define NTDRIVERS_DATABASE_VERSION_2 2
+#define NTDRIVERS_DATABASE_VERSION_3 3 /* little endian version of v2 */
+
+#define NTDRIVERS_DATABASE_VERSION NTDRIVERS_DATABASE_VERSION_3
+
+/* Map generic permissions to printer object specific permissions */
+
+GENERIC_MAPPING printer_generic_mapping = {
+ PRINTER_READ,
+ PRINTER_WRITE,
+ PRINTER_EXECUTE,
+ PRINTER_ALL_ACCESS
+};
+
+STANDARD_MAPPING printer_std_mapping = {
+ PRINTER_READ,
+ PRINTER_WRITE,
+ PRINTER_EXECUTE,
+ PRINTER_ALL_ACCESS
+};
+
+/* Map generic permissions to print server object specific permissions */
+
+GENERIC_MAPPING printserver_generic_mapping = {
+ SERVER_READ,
+ SERVER_WRITE,
+ SERVER_EXECUTE,
+ SERVER_ALL_ACCESS
+};
+
+STANDARD_MAPPING printserver_std_mapping = {
+ SERVER_READ,
+ SERVER_WRITE,
+ SERVER_EXECUTE,
+ SERVER_ALL_ACCESS
+};
+
+/* We need one default form to support our default printer. Msoft adds the
+forms it wants and in the ORDER it wants them (note: DEVMODE papersize is an
+array index). Letter is always first, so (for the current code) additions
+always put things in the correct order. */
+static const nt_forms_struct default_forms[] = {
+ {"Letter",0x1,0x34b5c,0x44368,0x0,0x0,0x34b5c,0x44368},
+ {"Letter Small",0x1,0x34b5c,0x44368,0x0,0x0,0x34b5c,0x44368},
+ {"Tabloid",0x1,0x44368,0x696b8,0x0,0x0,0x44368,0x696b8},
+ {"Ledger",0x1,0x696b8,0x44368,0x0,0x0,0x696b8,0x44368},
+ {"Legal",0x1,0x34b5c,0x56d10,0x0,0x0,0x34b5c,0x56d10},
+ {"Statement",0x1,0x221b4,0x34b5c,0x0,0x0,0x221b4,0x34b5c},
+ {"Executive",0x1,0x2cf56,0x411cc,0x0,0x0,0x2cf56,0x411cc},
+ {"A3",0x1,0x48828,0x668a0,0x0,0x0,0x48828,0x668a0},
+ {"A4",0x1,0x33450,0x48828,0x0,0x0,0x33450,0x48828},
+ {"A4 Small",0x1,0x33450,0x48828,0x0,0x0,0x33450,0x48828},
+ {"A5",0x1,0x24220,0x33450,0x0,0x0,0x24220,0x33450},
+ {"B4 (JIS)",0x1,0x3ebe8,0x58de0,0x0,0x0,0x3ebe8,0x58de0},
+ {"B5 (JIS)",0x1,0x2c6f0,0x3ebe8,0x0,0x0,0x2c6f0,0x3ebe8},
+ {"Folio",0x1,0x34b5c,0x509d8,0x0,0x0,0x34b5c,0x509d8},
+ {"Quarto",0x1,0x347d8,0x43238,0x0,0x0,0x347d8,0x43238},
+ {"10x14",0x1,0x3e030,0x56d10,0x0,0x0,0x3e030,0x56d10},
+ {"11x17",0x1,0x44368,0x696b8,0x0,0x0,0x44368,0x696b8},
+ {"Note",0x1,0x34b5c,0x44368,0x0,0x0,0x34b5c,0x44368},
+ {"Envelope #9",0x1,0x18079,0x37091,0x0,0x0,0x18079,0x37091},
+ {"Envelope #10",0x1,0x19947,0x3ae94,0x0,0x0,0x19947,0x3ae94},
+ {"Envelope #11",0x1,0x1be7c,0x40565,0x0,0x0,0x1be7c,0x40565},
+ {"Envelope #12",0x1,0x1d74a,0x44368,0x0,0x0,0x1d74a,0x44368},
+ {"Envelope #14",0x1,0x1f018,0x47504,0x0,0x0,0x1f018,0x47504},
+ {"C size sheet",0x1,0x696b8,0x886d0,0x0,0x0,0x696b8,0x886d0},
+ {"D size sheet",0x1,0x886d0,0xd2d70,0x0,0x0,0x886d0,0xd2d70},
+ {"E size sheet",0x1,0xd2d70,0x110da0,0x0,0x0,0xd2d70,0x110da0},
+ {"Envelope DL",0x1,0x1adb0,0x35b60,0x0,0x0,0x1adb0,0x35b60},
+ {"Envelope C5",0x1,0x278d0,0x37e88,0x0,0x0,0x278d0,0x37e88},
+ {"Envelope C3",0x1,0x4f1a0,0x6fd10,0x0,0x0,0x4f1a0,0x6fd10},
+ {"Envelope C4",0x1,0x37e88,0x4f1a0,0x0,0x0,0x37e88,0x4f1a0},
+ {"Envelope C6",0x1,0x1bd50,0x278d0,0x0,0x0,0x1bd50,0x278d0},
+ {"Envelope C65",0x1,0x1bd50,0x37e88,0x0,0x0,0x1bd50,0x37e88},
+ {"Envelope B4",0x1,0x3d090,0x562e8,0x0,0x0,0x3d090,0x562e8},
+ {"Envelope B5",0x1,0x2af80,0x3d090,0x0,0x0,0x2af80,0x3d090},
+ {"Envelope B6",0x1,0x2af80,0x1e848,0x0,0x0,0x2af80,0x1e848},
+ {"Envelope",0x1,0x1adb0,0x38270,0x0,0x0,0x1adb0,0x38270},
+ {"Envelope Monarch",0x1,0x18079,0x2e824,0x0,0x0,0x18079,0x2e824},
+ {"6 3/4 Envelope",0x1,0x167ab,0x284ec,0x0,0x0,0x167ab,0x284ec},
+ {"US Std Fanfold",0x1,0x5c3e1,0x44368,0x0,0x0,0x5c3e1,0x44368},
+ {"German Std Fanfold",0x1,0x34b5c,0x4a6a0,0x0,0x0,0x34b5c,0x4a6a0},
+ {"German Legal Fanfold",0x1,0x34b5c,0x509d8,0x0,0x0,0x34b5c,0x509d8},
+ {"B4 (ISO)",0x1,0x3d090,0x562e8,0x0,0x0,0x3d090,0x562e8},
+ {"Japanese Postcard",0x1,0x186a0,0x24220,0x0,0x0,0x186a0,0x24220},
+ {"9x11",0x1,0x37cf8,0x44368,0x0,0x0,0x37cf8,0x44368},
+ {"10x11",0x1,0x3e030,0x44368,0x0,0x0,0x3e030,0x44368},
+ {"15x11",0x1,0x5d048,0x44368,0x0,0x0,0x5d048,0x44368},
+ {"Envelope Invite",0x1,0x35b60,0x35b60,0x0,0x0,0x35b60,0x35b60},
+ {"Reserved48",0x1,0x1,0x1,0x0,0x0,0x1,0x1},
+ {"Reserved49",0x1,0x1,0x1,0x0,0x0,0x1,0x1},
+ {"Letter Extra",0x1,0x3ae94,0x4a6a0,0x0,0x0,0x3ae94,0x4a6a0},
+ {"Legal Extra",0x1,0x3ae94,0x5d048,0x0,0x0,0x3ae94,0x5d048},
+ {"Tabloid Extra",0x1,0x4a6a0,0x6f9f0,0x0,0x0,0x4a6a0,0x6f9f0},
+ {"A4 Extra",0x1,0x397c2,0x4eb16,0x0,0x0,0x397c2,0x4eb16},
+ {"Letter Transverse",0x1,0x34b5c,0x44368,0x0,0x0,0x34b5c,0x44368},
+ {"A4 Transverse",0x1,0x33450,0x48828,0x0,0x0,0x33450,0x48828},
+ {"Letter Extra Transverse",0x1,0x3ae94,0x4a6a0,0x0,0x0,0x3ae94,0x4a6a0},
+ {"Super A",0x1,0x376b8,0x56ea0,0x0,0x0,0x376b8,0x56ea0},
+ {"Super B",0x1,0x4a768,0x76e58,0x0,0x0,0x4a768,0x76e58},
+ {"Letter Plus",0x1,0x34b5c,0x4eb16,0x0,0x0,0x34b5c,0x4eb16},
+ {"A4 Plus",0x1,0x33450,0x50910,0x0,0x0,0x33450,0x50910},
+ {"A5 Transverse",0x1,0x24220,0x33450,0x0,0x0,0x24220,0x33450},
+ {"B5 (JIS) Transverse",0x1,0x2c6f0,0x3ebe8,0x0,0x0,0x2c6f0,0x3ebe8},
+ {"A3 Extra",0x1,0x4e9d0,0x6ca48,0x0,0x0,0x4e9d0,0x6ca48},
+ {"A5 Extra",0x1,0x2a7b0,0x395f8,0x0,0x0,0x2a7b0,0x395f8},
+ {"B5 (ISO) Extra",0x1,0x31128,0x43620,0x0,0x0,0x31128,0x43620},
+ {"A2",0x1,0x668a0,0x91050,0x0,0x0,0x668a0,0x91050},
+ {"A3 Transverse",0x1,0x48828,0x668a0,0x0,0x0,0x48828,0x668a0},
+ {"A3 Extra Transverse",0x1,0x4e9d0,0x6ca48,0x0,0x0,0x4e9d0,0x6ca48},
+ {"Japanese Double Postcard",0x1,0x30d40,0x24220,0x0,0x0,0x30d40,0x24220},
+ {"A6",0x1,0x19a28,0x24220,0x0,0x0,0x19a28,0x24220},
+ {"Japanese Envelope Kaku #2",0x1,0x3a980,0x510e0,0x0,0x0,0x3a980,0x510e0},
+ {"Japanese Envelope Kaku #3",0x1,0x34bc0,0x43a08,0x0,0x0,0x34bc0,0x43a08},
+ {"Japanese Envelope Chou #3",0x1,0x1d4c0,0x395f8,0x0,0x0,0x1d4c0,0x395f8},
+ {"Japanese Envelope Chou #4",0x1,0x15f90,0x320c8,0x0,0x0,0x15f90,0x320c8},
+ {"Letter Rotated",0x1,0x44368,0x34b5c,0x0,0x0,0x44368,0x34b5c},
+ {"A3 Rotated",0x1,0x668a0,0x48828,0x0,0x0,0x668a0,0x48828},
+ {"A4 Rotated",0x1,0x48828,0x33450,0x0,0x0,0x48828,0x33450},
+ {"A5 Rotated",0x1,0x33450,0x24220,0x0,0x0,0x33450,0x24220},
+ {"B4 (JIS) Rotated",0x1,0x58de0,0x3ebe8,0x0,0x0,0x58de0,0x3ebe8},
+ {"B5 (JIS) Rotated",0x1,0x3ebe8,0x2c6f0,0x0,0x0,0x3ebe8,0x2c6f0},
+ {"Japanese Postcard Rotated",0x1,0x24220,0x186a0,0x0,0x0,0x24220,0x186a0},
+ {"Double Japan Postcard Rotated",0x1,0x24220,0x30d40,0x0,0x0,0x24220,0x30d40},
+ {"A6 Rotated",0x1,0x24220,0x19a28,0x0,0x0,0x24220,0x19a28},
+ {"Japan Envelope Kaku #2 Rotated",0x1,0x510e0,0x3a980,0x0,0x0,0x510e0,0x3a980},
+ {"Japan Envelope Kaku #3 Rotated",0x1,0x43a08,0x34bc0,0x0,0x0,0x43a08, 0x34bc0},
+ {"Japan Envelope Chou #3 Rotated",0x1,0x395f8,0x1d4c0,0x0,0x0,0x395f8,0x1d4c0},
+ {"Japan Envelope Chou #4 Rotated",0x1,0x320c8,0x15f90,0x0,0x0,0x320c8,0x15f90},
+ {"B6 (JIS)",0x1,0x1f400,0x2c6f0,0x0,0x0,0x1f400,0x2c6f0},
+ {"B6 (JIS) Rotated",0x1,0x2c6f0,0x1f400,0x0,0x0,0x2c6f0,0x1f400},
+ {"12x11",0x1,0x4a724,0x443e1,0x0,0x0,0x4a724,0x443e1},
+ {"Japan Envelope You #4",0x1,0x19a28,0x395f8,0x0,0x0,0x19a28,0x395f8},
+ {"Japan Envelope You #4 Rotated",0x1,0x395f8,0x19a28,0x0,0x0,0x395f8,0x19a28},
+ {"PRC 16K",0x1,0x2de60,0x3f7a0,0x0,0x0,0x2de60,0x3f7a0},
+ {"PRC 32K",0x1,0x1fbd0,0x2cec0,0x0,0x0,0x1fbd0,0x2cec0},
+ {"PRC 32K(Big)",0x1,0x222e0,0x318f8,0x0,0x0,0x222e0,0x318f8},
+ {"PRC Envelope #1",0x1,0x18e70,0x28488,0x0,0x0,0x18e70,0x28488},
+ {"PRC Envelope #2",0x1,0x18e70,0x2af80,0x0,0x0,0x18e70,0x2af80},
+ {"PRC Envelope #3",0x1,0x1e848,0x2af80,0x0,0x0,0x1e848,0x2af80},
+ {"PRC Envelope #4",0x1,0x1adb0,0x32c80,0x0,0x0,0x1adb0,0x32c80},
+ {"PRC Envelope #5",0x1,0x1adb0,0x35b60,0x0,0x0,0x1adb0,0x35b60},
+ {"PRC Envelope #6",0x1,0x1d4c0,0x38270,0x0,0x0,0x1d4c0,0x38270},
+ {"PRC Envelope #7",0x1,0x27100,0x38270,0x0,0x0,0x27100,0x38270},
+ {"PRC Envelope #8",0x1,0x1d4c0,0x4b708,0x0,0x0,0x1d4c0,0x4b708},
+ {"PRC Envelope #9",0x1,0x37e88,0x4f1a0,0x0,0x0,0x37e88,0x4f1a0},
+ {"PRC Envelope #10",0x1,0x4f1a0,0x6fd10,0x0,0x0,0x4f1a0,0x6fd10},
+ {"PRC 16K Rotated",0x1,0x3f7a0,0x2de60,0x0,0x0,0x3f7a0,0x2de60},
+ {"PRC 32K Rotated",0x1,0x2cec0,0x1fbd0,0x0,0x0,0x2cec0,0x1fbd0},
+ {"PRC 32K(Big) Rotated",0x1,0x318f8,0x222e0,0x0,0x0,0x318f8,0x222e0},
+ {"PRC Envelope #1 Rotated",0x1,0x28488,0x18e70,0x0,0x0,0x28488,0x18e70},
+ {"PRC Envelope #2 Rotated",0x1,0x2af80,0x18e70,0x0,0x0,0x2af80,0x18e70},
+ {"PRC Envelope #3 Rotated",0x1,0x2af80,0x1e848,0x0,0x0,0x2af80,0x1e848},
+ {"PRC Envelope #4 Rotated",0x1,0x32c80,0x1adb0,0x0,0x0,0x32c80,0x1adb0},
+ {"PRC Envelope #5 Rotated",0x1,0x35b60,0x1adb0,0x0,0x0,0x35b60,0x1adb0},
+ {"PRC Envelope #6 Rotated",0x1,0x38270,0x1d4c0,0x0,0x0,0x38270,0x1d4c0},
+ {"PRC Envelope #7 Rotated",0x1,0x38270,0x27100,0x0,0x0,0x38270,0x27100},
+ {"PRC Envelope #8 Rotated",0x1,0x4b708,0x1d4c0,0x0,0x0,0x4b708,0x1d4c0},
+ {"PRC Envelope #9 Rotated",0x1,0x4f1a0,0x37e88,0x0,0x0,0x4f1a0,0x37e88},
+ {"PRC Envelope #10 Rotated",0x1,0x6fd10,0x4f1a0,0x0,0x0,0x6fd10,0x4f1a0}
+};
+
+struct table_node {
+ const char *long_archi;
+ const char *short_archi;
+ int version;
+};
+
+#define SPL_ARCH_WIN40 "WIN40"
+#define SPL_ARCH_W32X86 "W32X86"
+#define SPL_ARCH_W32MIPS "W32MIPS"
+#define SPL_ARCH_W32ALPHA "W32ALPHA"
+#define SPL_ARCH_W32PPC "W32PPC"
+
+static const struct table_node archi_table[]= {
+
+ {"Windows 4.0", SPL_ARCH_WIN40, 0 },
+ {"Windows NT x86", SPL_ARCH_W32X86, 2 },
+ {"Windows NT R4000", SPL_ARCH_W32MIPS, 2 },
+ {"Windows NT Alpha_AXP", SPL_ARCH_W32ALPHA, 2 },
+ {"Windows NT PowerPC", SPL_ARCH_W32PPC, 2 },
+ {NULL, "", -1 }
+};
+
+static BOOL upgrade_to_version_3(void)
+{
+ TDB_DATA kbuf, newkey, dbuf;
+
+ DEBUG(0,("upgrade_to_version_3: upgrading print tdb's to version 3\n"));
+
+ for (kbuf = tdb_firstkey(tdb_drivers); kbuf.dptr;
+ newkey = tdb_nextkey(tdb_drivers, kbuf), safe_free(kbuf.dptr), kbuf=newkey) {
+
+ dbuf = tdb_fetch(tdb_drivers, kbuf);
+
+ if (strncmp(kbuf.dptr, FORMS_PREFIX, strlen(FORMS_PREFIX)) == 0) {
+ DEBUG(0,("upgrade_to_version_3:moving form\n"));
+ if (tdb_store(tdb_forms, kbuf, dbuf, TDB_REPLACE) != 0) {
+ SAFE_FREE(dbuf.dptr);
+ DEBUG(0,("upgrade_to_version_3: failed to move form. Error (%s).\n", tdb_errorstr(tdb_forms)));
+ return False;
+ }
+ if (tdb_delete(tdb_drivers, kbuf) != 0) {
+ SAFE_FREE(dbuf.dptr);
+ DEBUG(0,("upgrade_to_version_3: failed to delete form. Error (%s)\n", tdb_errorstr(tdb_drivers)));
+ return False;
+ }
+ }
+
+ if (strncmp(kbuf.dptr, PRINTERS_PREFIX, strlen(PRINTERS_PREFIX)) == 0) {
+ DEBUG(0,("upgrade_to_version_3:moving printer\n"));
+ if (tdb_store(tdb_printers, kbuf, dbuf, TDB_REPLACE) != 0) {
+ SAFE_FREE(dbuf.dptr);
+ DEBUG(0,("upgrade_to_version_3: failed to move printer. Error (%s)\n", tdb_errorstr(tdb_printers)));
+ return False;
+ }
+ if (tdb_delete(tdb_drivers, kbuf) != 0) {
+ SAFE_FREE(dbuf.dptr);
+ DEBUG(0,("upgrade_to_version_3: failed to delete printer. Error (%s)\n", tdb_errorstr(tdb_drivers)));
+ return False;
+ }
+ }
+
+ if (strncmp(kbuf.dptr, SECDESC_PREFIX, strlen(SECDESC_PREFIX)) == 0) {
+ DEBUG(0,("upgrade_to_version_3:moving secdesc\n"));
+ if (tdb_store(tdb_printers, kbuf, dbuf, TDB_REPLACE) != 0) {
+ SAFE_FREE(dbuf.dptr);
+ DEBUG(0,("upgrade_to_version_3: failed to move secdesc. Error (%s)\n", tdb_errorstr(tdb_printers)));
+ return False;
+ }
+ if (tdb_delete(tdb_drivers, kbuf) != 0) {
+ SAFE_FREE(dbuf.dptr);
+ DEBUG(0,("upgrade_to_version_3: failed to delete secdesc. Error (%s)\n", tdb_errorstr(tdb_drivers)));
+ return False;
+ }
+ }
+
+ SAFE_FREE(dbuf.dptr);
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Open the NT printing tdbs. Done once before fork().
+****************************************************************************/
+
+BOOL nt_printing_init(void)
+{
+ static pid_t local_pid;
+ const char *vstring = "INFO/version";
+
+ if (tdb_drivers && tdb_printers && tdb_forms && local_pid == sys_getpid())
+ return True;
+
+ if (tdb_drivers)
+ tdb_close(tdb_drivers);
+ tdb_drivers = tdb_open_log(lock_path("ntdrivers.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+ if (!tdb_drivers) {
+ DEBUG(0,("nt_printing_init: Failed to open nt drivers database %s (%s)\n",
+ lock_path("ntdrivers.tdb"), strerror(errno) ));
+ return False;
+ }
+
+ if (tdb_printers)
+ tdb_close(tdb_printers);
+ tdb_printers = tdb_open_log(lock_path("ntprinters.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+ if (!tdb_printers) {
+ DEBUG(0,("nt_printing_init: Failed to open nt printers database %s (%s)\n",
+ lock_path("ntprinters.tdb"), strerror(errno) ));
+ return False;
+ }
+
+ if (tdb_forms)
+ tdb_close(tdb_forms);
+ tdb_forms = tdb_open_log(lock_path("ntforms.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+ if (!tdb_forms) {
+ DEBUG(0,("nt_printing_init: Failed to open nt forms database %s (%s)\n",
+ lock_path("ntforms.tdb"), strerror(errno) ));
+ return False;
+ }
+
+ local_pid = sys_getpid();
+
+ /* handle a Samba upgrade */
+ tdb_lock_bystring(tdb_drivers, vstring, 0);
+ {
+ int32 vers_id;
+
+ /* Cope with byte-reversed older versions of the db. */
+ vers_id = tdb_fetch_int32(tdb_drivers, vstring);
+ if ((vers_id == NTDRIVERS_DATABASE_VERSION_2) || (IREV(vers_id) == NTDRIVERS_DATABASE_VERSION_2)) {
+ /* Written on a bigendian machine with old fetch_int code. Save as le. */
+ /* The only upgrade between V2 and V3 is to save the version in little-endian. */
+ tdb_store_int32(tdb_drivers, vstring, NTDRIVERS_DATABASE_VERSION);
+ vers_id = NTDRIVERS_DATABASE_VERSION;
+ }
+
+ if (vers_id != NTDRIVERS_DATABASE_VERSION) {
+
+ if ((vers_id == NTDRIVERS_DATABASE_VERSION_1) || (IREV(vers_id) == NTDRIVERS_DATABASE_VERSION_1)) {
+ if (!upgrade_to_version_3())
+ return False;
+ } else
+ tdb_traverse(tdb_drivers, tdb_traverse_delete_fn, NULL);
+
+ tdb_store_int32(tdb_drivers, vstring, NTDRIVERS_DATABASE_VERSION);
+ }
+ }
+ tdb_unlock_bystring(tdb_drivers, vstring);
+
+ update_c_setprinter(True);
+
+ /*
+ * register callback to handle updating printers as new
+ * drivers are installed
+ */
+
+ message_register( MSG_PRINTER_DRVUPGRADE, do_drv_upgrade_printer );
+
+ /*
+ * register callback to handle updating printer data
+ * when a driver is initialized
+ */
+
+ message_register( MSG_PRINTERDATA_INIT_RESET, reset_all_printerdata );
+
+
+ return True;
+}
+
+/*******************************************************************
+ tdb traversal function for counting printers.
+********************************************************************/
+
+static int traverse_counting_printers(TDB_CONTEXT *t, TDB_DATA key,
+ TDB_DATA data, void *context)
+{
+ int *printer_count = (int*)context;
+
+ if (memcmp(PRINTERS_PREFIX, key.dptr, sizeof(PRINTERS_PREFIX)-1) == 0) {
+ (*printer_count)++;
+ DEBUG(10,("traverse_counting_printers: printer = [%s] printer_count = %d\n", key.dptr, *printer_count));
+ }
+
+ return 0;
+}
+
+/*******************************************************************
+ Update the spooler global c_setprinter. This variable is initialized
+ when the parent smbd starts with the number of existing printers. It
+ is monotonically increased by the current number of printers *after*
+ each add or delete printer RPC. Only Microsoft knows why... JRR020119
+********************************************************************/
+
+uint32 update_c_setprinter(BOOL initialize)
+{
+ int32 c_setprinter;
+ int32 printer_count = 0;
+
+ tdb_lock_bystring(tdb_printers, GLOBAL_C_SETPRINTER, 0);
+
+ /* Traverse the tdb, counting the printers */
+ tdb_traverse(tdb_printers, traverse_counting_printers, (void *)&printer_count);
+
+ /* If initializing, set c_setprinter to current printers count
+ * otherwise, bump it by the current printer count
+ */
+ if (!initialize)
+ c_setprinter = tdb_fetch_int32(tdb_printers, GLOBAL_C_SETPRINTER) + printer_count;
+ else
+ c_setprinter = printer_count;
+
+ DEBUG(10,("update_c_setprinter: c_setprinter = %u\n", (unsigned int)c_setprinter));
+ tdb_store_int32(tdb_printers, GLOBAL_C_SETPRINTER, c_setprinter);
+
+ tdb_unlock_bystring(tdb_printers, GLOBAL_C_SETPRINTER);
+
+ return (uint32)c_setprinter;
+}
+
+/*******************************************************************
+ Get the spooler global c_setprinter, accounting for initialization.
+********************************************************************/
+
+uint32 get_c_setprinter(void)
+{
+ int32 c_setprinter = tdb_fetch_int32(tdb_printers, GLOBAL_C_SETPRINTER);
+
+ if (c_setprinter == (int32)-1)
+ c_setprinter = update_c_setprinter(True);
+
+ DEBUG(10,("get_c_setprinter: c_setprinter = %d\n", c_setprinter));
+
+ return (uint32)c_setprinter;
+}
+
+/****************************************************************************
+ Get builtin form struct list.
+****************************************************************************/
+
+int get_builtin_ntforms(nt_forms_struct **list)
+{
+ *list = (nt_forms_struct *)memdup(&default_forms[0], sizeof(default_forms));
+ return sizeof(default_forms) / sizeof(default_forms[0]);
+}
+
+/****************************************************************************
+ get a builtin form struct
+****************************************************************************/
+
+BOOL get_a_builtin_ntform(UNISTR2 *uni_formname,nt_forms_struct *form)
+{
+ int i,count;
+ fstring form_name;
+ unistr2_to_ascii(form_name, uni_formname, sizeof(form_name)-1);
+ DEBUGADD(6,("Looking for builtin form %s \n", form_name));
+ count = sizeof(default_forms) / sizeof(default_forms[0]);
+ for (i=0;i<count;i++) {
+ if (strequal(form_name,default_forms[i].name)) {
+ DEBUGADD(6,("Found builtin form %s \n", form_name));
+ memcpy(form,&default_forms[i],sizeof(*form));
+ break;
+ }
+ }
+
+ return (i !=count);
+}
+
+/****************************************************************************
+get a form struct list
+****************************************************************************/
+int get_ntforms(nt_forms_struct **list)
+{
+ TDB_DATA kbuf, newkey, dbuf;
+ nt_forms_struct *tl;
+ nt_forms_struct form;
+ int ret;
+ int i;
+ int n = 0;
+
+ for (kbuf = tdb_firstkey(tdb_forms);
+ kbuf.dptr;
+ newkey = tdb_nextkey(tdb_forms, kbuf), safe_free(kbuf.dptr), kbuf=newkey)
+ {
+ if (strncmp(kbuf.dptr, FORMS_PREFIX, strlen(FORMS_PREFIX)) != 0)
+ continue;
+
+ dbuf = tdb_fetch(tdb_forms, kbuf);
+ if (!dbuf.dptr)
+ continue;
+
+ fstrcpy(form.name, kbuf.dptr+strlen(FORMS_PREFIX));
+ ret = tdb_unpack(dbuf.dptr, dbuf.dsize, "dddddddd",
+ &i, &form.flag, &form.width, &form.length, &form.left,
+ &form.top, &form.right, &form.bottom);
+ SAFE_FREE(dbuf.dptr);
+ if (ret != dbuf.dsize)
+ continue;
+
+ tl = Realloc(*list, sizeof(nt_forms_struct)*(n+1));
+ if (!tl) {
+ DEBUG(0,("get_ntforms: Realloc fail.\n"));
+ return 0;
+ }
+ *list = tl;
+ (*list)[n] = form;
+ n++;
+ }
+
+
+ return n;
+}
+
+/****************************************************************************
+write a form struct list
+****************************************************************************/
+int write_ntforms(nt_forms_struct **list, int number)
+{
+ pstring buf, key;
+ int len;
+ TDB_DATA kbuf,dbuf;
+ int i;
+
+ for (i=0;i<number;i++) {
+ /* save index, so list is rebuilt in correct order */
+ len = tdb_pack(buf, sizeof(buf), "dddddddd",
+ i, (*list)[i].flag, (*list)[i].width, (*list)[i].length,
+ (*list)[i].left, (*list)[i].top, (*list)[i].right,
+ (*list)[i].bottom);
+ if (len > sizeof(buf)) break;
+ slprintf(key, sizeof(key)-1, "%s%s", FORMS_PREFIX, (*list)[i].name);
+ kbuf.dsize = strlen(key)+1;
+ kbuf.dptr = key;
+ dbuf.dsize = len;
+ dbuf.dptr = buf;
+ if (tdb_store(tdb_forms, kbuf, dbuf, TDB_REPLACE) != 0) break;
+ }
+
+ return i;
+}
+
+/****************************************************************************
+add a form struct at the end of the list
+****************************************************************************/
+BOOL add_a_form(nt_forms_struct **list, const FORM *form, int *count)
+{
+ int n=0;
+ BOOL update;
+ fstring form_name;
+ nt_forms_struct *tl;
+
+ /*
+ * NT tries to add forms even when
+ * they are already in the base
+ * only update the values if already present
+ */
+
+ update=False;
+
+ unistr2_to_ascii(form_name, &form->name, sizeof(form_name)-1);
+ for (n=0; n<*count; n++) {
+ if (!strncmp((*list)[n].name, form_name, strlen(form_name))) {
+ DEBUG(103, ("NT workaround, [%s] already exists\n", form_name));
+ update=True;
+ break;
+ }
+ }
+
+ if (update==False) {
+ if((tl=Realloc(*list, (n+1)*sizeof(nt_forms_struct))) == NULL) {
+ DEBUG(0,("add_a_form: failed to enlarge forms list!\n"));
+ return False;
+ }
+ *list = tl;
+ unistr2_to_ascii((*list)[n].name, &form->name, sizeof((*list)[n].name)-1);
+ (*count)++;
+ }
+
+ (*list)[n].flag=form->flags;
+ (*list)[n].width=form->size_x;
+ (*list)[n].length=form->size_y;
+ (*list)[n].left=form->left;
+ (*list)[n].top=form->top;
+ (*list)[n].right=form->right;
+ (*list)[n].bottom=form->bottom;
+
+ return True;
+}
+
+/****************************************************************************
+ Delete a named form struct.
+****************************************************************************/
+
+BOOL delete_a_form(nt_forms_struct **list, UNISTR2 *del_name, int *count, WERROR *ret)
+{
+ pstring key;
+ TDB_DATA kbuf;
+ int n=0;
+ fstring form_name;
+
+ *ret = WERR_OK;
+
+ unistr2_to_ascii(form_name, del_name, sizeof(form_name)-1);
+
+ for (n=0; n<*count; n++) {
+ if (!strncmp((*list)[n].name, form_name, strlen(form_name))) {
+ DEBUG(103, ("delete_a_form, [%s] in list\n", form_name));
+ break;
+ }
+ }
+
+ if (n == *count) {
+ DEBUG(10,("delete_a_form, [%s] not found\n", form_name));
+ *ret = WERR_INVALID_PARAM;
+ return False;
+ }
+
+ slprintf(key, sizeof(key)-1, "%s%s", FORMS_PREFIX, (*list)[n].name);
+ kbuf.dsize = strlen(key)+1;
+ kbuf.dptr = key;
+ if (tdb_delete(tdb_forms, kbuf) != 0) {
+ *ret = WERR_NOMEM;
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Update a form struct.
+****************************************************************************/
+
+void update_a_form(nt_forms_struct **list, const FORM *form, int count)
+{
+ int n=0;
+ fstring form_name;
+ unistr2_to_ascii(form_name, &(form->name), sizeof(form_name)-1);
+
+ DEBUG(106, ("[%s]\n", form_name));
+ for (n=0; n<count; n++) {
+ DEBUGADD(106, ("n [%d]:[%s]\n", n, (*list)[n].name));
+ if (!strncmp((*list)[n].name, form_name, strlen(form_name)))
+ break;
+ }
+
+ if (n==count) return;
+
+ (*list)[n].flag=form->flags;
+ (*list)[n].width=form->size_x;
+ (*list)[n].length=form->size_y;
+ (*list)[n].left=form->left;
+ (*list)[n].top=form->top;
+ (*list)[n].right=form->right;
+ (*list)[n].bottom=form->bottom;
+}
+
+/****************************************************************************
+ Get the nt drivers list.
+ Traverse the database and look-up the matching names.
+****************************************************************************/
+int get_ntdrivers(fstring **list, const char *architecture, uint32 version)
+{
+ int total=0;
+ const char *short_archi;
+ fstring *fl;
+ pstring key;
+ TDB_DATA kbuf, newkey;
+
+ short_archi = get_short_archi(architecture);
+ slprintf(key, sizeof(key)-1, "%s%s/%d/", DRIVERS_PREFIX, short_archi, version);
+
+ for (kbuf = tdb_firstkey(tdb_drivers);
+ kbuf.dptr;
+ newkey = tdb_nextkey(tdb_drivers, kbuf), safe_free(kbuf.dptr), kbuf=newkey) {
+
+ if (strncmp(kbuf.dptr, key, strlen(key)) != 0)
+ continue;
+
+ if((fl = Realloc(*list, sizeof(fstring)*(total+1))) == NULL) {
+ DEBUG(0,("get_ntdrivers: failed to enlarge list!\n"));
+ return -1;
+ }
+ else *list = fl;
+
+ fstrcpy((*list)[total], kbuf.dptr+strlen(key));
+ total++;
+ }
+
+ return(total);
+}
+
+/****************************************************************************
+function to do the mapping between the long architecture name and
+the short one.
+****************************************************************************/
+const char *get_short_archi(const char *long_archi)
+{
+ int i=-1;
+
+ DEBUG(107,("Getting architecture dependant directory\n"));
+ do {
+ i++;
+ } while ( (archi_table[i].long_archi!=NULL ) &&
+ StrCaseCmp(long_archi, archi_table[i].long_archi) );
+
+ if (archi_table[i].long_archi==NULL) {
+ DEBUGADD(10,("Unknown architecture [%s] !\n", long_archi));
+ return NULL;
+ }
+
+ /* this might be client code - but shouldn't this be an fstrcpy etc? */
+
+
+ DEBUGADD(108,("index: [%d]\n", i));
+ DEBUGADD(108,("long architecture: [%s]\n", archi_table[i].long_archi));
+ DEBUGADD(108,("short architecture: [%s]\n", archi_table[i].short_archi));
+
+ return archi_table[i].short_archi;
+}
+
+/****************************************************************************
+ Version information in Microsoft files is held in a VS_VERSION_INFO structure.
+ There are two case to be covered here: PE (Portable Executable) and NE (New
+ Executable) files. Both files support the same INFO structure, but PE files
+ store the signature in unicode, and NE files store it as !unicode.
+ returns -1 on error, 1 on version info found, and 0 on no version info found.
+****************************************************************************/
+
+static int get_file_version(files_struct *fsp, char *fname,uint32 *major, uint32 *minor)
+{
+ int i;
+ char *buf = NULL;
+ ssize_t byte_count;
+
+ if ((buf=malloc(PE_HEADER_SIZE)) == NULL) {
+ DEBUG(0,("get_file_version: PE file [%s] PE Header malloc failed bytes = %d\n",
+ fname, PE_HEADER_SIZE));
+ goto error_exit;
+ }
+
+ /* Note: DOS_HEADER_SIZE < malloc'ed PE_HEADER_SIZE */
+ if ((byte_count = vfs_read_data(fsp, buf, DOS_HEADER_SIZE)) < DOS_HEADER_SIZE) {
+ DEBUG(3,("get_file_version: File [%s] DOS header too short, bytes read = %lu\n",
+ fname, (unsigned long)byte_count));
+ goto no_version_info;
+ }
+
+ /* Is this really a DOS header? */
+ if (SVAL(buf,DOS_HEADER_MAGIC_OFFSET) != DOS_HEADER_MAGIC) {
+ DEBUG(6,("get_file_version: File [%s] bad DOS magic = 0x%x\n",
+ fname, SVAL(buf,DOS_HEADER_MAGIC_OFFSET)));
+ goto no_version_info;
+ }
+
+ /* Skip OEM header (if any) and the DOS stub to start of Windows header */
+ if (SMB_VFS_LSEEK(fsp, fsp->fd, SVAL(buf,DOS_HEADER_LFANEW_OFFSET), SEEK_SET) == (SMB_OFF_T)-1) {
+ DEBUG(3,("get_file_version: File [%s] too short, errno = %d\n",
+ fname, errno));
+ /* Assume this isn't an error... the file just looks sort of like a PE/NE file */
+ goto no_version_info;
+ }
+
+ if ((byte_count = vfs_read_data(fsp, buf, PE_HEADER_SIZE)) < PE_HEADER_SIZE) {
+ DEBUG(3,("get_file_version: File [%s] Windows header too short, bytes read = %lu\n",
+ fname, (unsigned long)byte_count));
+ /* Assume this isn't an error... the file just looks sort of like a PE/NE file */
+ goto no_version_info;
+ }
+
+ /* The header may be a PE (Portable Executable) or an NE (New Executable) */
+ if (IVAL(buf,PE_HEADER_SIGNATURE_OFFSET) == PE_HEADER_SIGNATURE) {
+ unsigned int num_sections;
+ unsigned int section_table_bytes;
+
+ if (SVAL(buf,PE_HEADER_MACHINE_OFFSET) != PE_HEADER_MACHINE_I386) {
+ DEBUG(3,("get_file_version: PE file [%s] wrong machine = 0x%x\n",
+ fname, SVAL(buf,PE_HEADER_MACHINE_OFFSET)));
+ /* At this point, we assume the file is in error. It still could be somthing
+ * else besides a PE file, but it unlikely at this point.
+ */
+ goto error_exit;
+ }
+
+ /* get the section table */
+ num_sections = SVAL(buf,PE_HEADER_NUMBER_OF_SECTIONS);
+ section_table_bytes = num_sections * PE_HEADER_SECT_HEADER_SIZE;
+ if (section_table_bytes == 0)
+ goto error_exit;
+
+ SAFE_FREE(buf);
+ if ((buf=malloc(section_table_bytes)) == NULL) {
+ DEBUG(0,("get_file_version: PE file [%s] section table malloc failed bytes = %d\n",
+ fname, section_table_bytes));
+ goto error_exit;
+ }
+
+ if ((byte_count = vfs_read_data(fsp, buf, section_table_bytes)) < section_table_bytes) {
+ DEBUG(3,("get_file_version: PE file [%s] Section header too short, bytes read = %lu\n",
+ fname, (unsigned long)byte_count));
+ goto error_exit;
+ }
+
+ /* Iterate the section table looking for the resource section ".rsrc" */
+ for (i = 0; i < num_sections; i++) {
+ int sec_offset = i * PE_HEADER_SECT_HEADER_SIZE;
+
+ if (strcmp(".rsrc", &buf[sec_offset+PE_HEADER_SECT_NAME_OFFSET]) == 0) {
+ unsigned int section_pos = IVAL(buf,sec_offset+PE_HEADER_SECT_PTR_DATA_OFFSET);
+ unsigned int section_bytes = IVAL(buf,sec_offset+PE_HEADER_SECT_SIZE_DATA_OFFSET);
+
+ if (section_bytes == 0)
+ goto error_exit;
+
+ SAFE_FREE(buf);
+ if ((buf=malloc(section_bytes)) == NULL) {
+ DEBUG(0,("get_file_version: PE file [%s] version malloc failed bytes = %d\n",
+ fname, section_bytes));
+ goto error_exit;
+ }
+
+ /* Seek to the start of the .rsrc section info */
+ if (SMB_VFS_LSEEK(fsp, fsp->fd, section_pos, SEEK_SET) == (SMB_OFF_T)-1) {
+ DEBUG(3,("get_file_version: PE file [%s] too short for section info, errno = %d\n",
+ fname, errno));
+ goto error_exit;
+ }
+
+ if ((byte_count = vfs_read_data(fsp, buf, section_bytes)) < section_bytes) {
+ DEBUG(3,("get_file_version: PE file [%s] .rsrc section too short, bytes read = %lu\n",
+ fname, (unsigned long)byte_count));
+ goto error_exit;
+ }
+
+ if (section_bytes < VS_VERSION_INFO_UNICODE_SIZE)
+ goto error_exit;
+
+ for (i=0; i<section_bytes-VS_VERSION_INFO_UNICODE_SIZE; i++) {
+ /* Scan for 1st 3 unicoded bytes followed by word aligned magic value */
+ if (buf[i] == 'V' && buf[i+1] == '\0' && buf[i+2] == 'S') {
+ /* Align to next long address */
+ int pos = (i + sizeof(VS_SIGNATURE)*2 + 3) & 0xfffffffc;
+
+ if (IVAL(buf,pos) == VS_MAGIC_VALUE) {
+ *major = IVAL(buf,pos+VS_MAJOR_OFFSET);
+ *minor = IVAL(buf,pos+VS_MINOR_OFFSET);
+
+ DEBUG(6,("get_file_version: PE file [%s] Version = %08x:%08x (%d.%d.%d.%d)\n",
+ fname, *major, *minor,
+ (*major>>16)&0xffff, *major&0xffff,
+ (*minor>>16)&0xffff, *minor&0xffff));
+ SAFE_FREE(buf);
+ return 1;
+ }
+ }
+ }
+ }
+ }
+
+ /* Version info not found, fall back to origin date/time */
+ DEBUG(10,("get_file_version: PE file [%s] has no version info\n", fname));
+ SAFE_FREE(buf);
+ return 0;
+
+ } else if (SVAL(buf,NE_HEADER_SIGNATURE_OFFSET) == NE_HEADER_SIGNATURE) {
+ if (CVAL(buf,NE_HEADER_TARGET_OS_OFFSET) != NE_HEADER_TARGOS_WIN ) {
+ DEBUG(3,("get_file_version: NE file [%s] wrong target OS = 0x%x\n",
+ fname, CVAL(buf,NE_HEADER_TARGET_OS_OFFSET)));
+ /* At this point, we assume the file is in error. It still could be somthing
+ * else besides a NE file, but it unlikely at this point. */
+ goto error_exit;
+ }
+
+ /* Allocate a bit more space to speed up things */
+ SAFE_FREE(buf);
+ if ((buf=malloc(VS_NE_BUF_SIZE)) == NULL) {
+ DEBUG(0,("get_file_version: NE file [%s] malloc failed bytes = %d\n",
+ fname, PE_HEADER_SIZE));
+ goto error_exit;
+ }
+
+ /* This is a HACK! I got tired of trying to sort through the messy
+ * 'NE' file format. If anyone wants to clean this up please have at
+ * it, but this works. 'NE' files will eventually fade away. JRR */
+ while((byte_count = vfs_read_data(fsp, buf, VS_NE_BUF_SIZE)) > 0) {
+ /* Cover case that should not occur in a well formed 'NE' .dll file */
+ if (byte_count-VS_VERSION_INFO_SIZE <= 0) break;
+
+ for(i=0; i<byte_count; i++) {
+ /* Fast skip past data that can't possibly match */
+ if (buf[i] != 'V') continue;
+
+ /* Potential match data crosses buf boundry, move it to beginning
+ * of buf, and fill the buf with as much as it will hold. */
+ if (i>byte_count-VS_VERSION_INFO_SIZE) {
+ int bc;
+
+ memcpy(buf, &buf[i], byte_count-i);
+ if ((bc = vfs_read_data(fsp, &buf[byte_count-i], VS_NE_BUF_SIZE-
+ (byte_count-i))) < 0) {
+
+ DEBUG(0,("get_file_version: NE file [%s] Read error, errno=%d\n",
+ fname, errno));
+ goto error_exit;
+ }
+
+ byte_count = bc + (byte_count - i);
+ if (byte_count<VS_VERSION_INFO_SIZE) break;
+
+ i = 0;
+ }
+
+ /* Check that the full signature string and the magic number that
+ * follows exist (not a perfect solution, but the chances that this
+ * occurs in code is, well, remote. Yes I know I'm comparing the 'V'
+ * twice, as it is simpler to read the code. */
+ if (strcmp(&buf[i], VS_SIGNATURE) == 0) {
+ /* Compute skip alignment to next long address */
+ int skip = -(SMB_VFS_LSEEK(fsp, fsp->fd, 0, SEEK_CUR) - (byte_count - i) +
+ sizeof(VS_SIGNATURE)) & 3;
+ if (IVAL(buf,i+sizeof(VS_SIGNATURE)+skip) != 0xfeef04bd) continue;
+
+ *major = IVAL(buf,i+sizeof(VS_SIGNATURE)+skip+VS_MAJOR_OFFSET);
+ *minor = IVAL(buf,i+sizeof(VS_SIGNATURE)+skip+VS_MINOR_OFFSET);
+ DEBUG(6,("get_file_version: NE file [%s] Version = %08x:%08x (%d.%d.%d.%d)\n",
+ fname, *major, *minor,
+ (*major>>16)&0xffff, *major&0xffff,
+ (*minor>>16)&0xffff, *minor&0xffff));
+ SAFE_FREE(buf);
+ return 1;
+ }
+ }
+ }
+
+ /* Version info not found, fall back to origin date/time */
+ DEBUG(0,("get_file_version: NE file [%s] Version info not found\n", fname));
+ SAFE_FREE(buf);
+ return 0;
+
+ } else
+ /* Assume this isn't an error... the file just looks sort of like a PE/NE file */
+ DEBUG(3,("get_file_version: File [%s] unknown file format, signature = 0x%x\n",
+ fname, IVAL(buf,PE_HEADER_SIGNATURE_OFFSET)));
+
+ no_version_info:
+ SAFE_FREE(buf);
+ return 0;
+
+ error_exit:
+ SAFE_FREE(buf);
+ return -1;
+}
+
+/****************************************************************************
+Drivers for Microsoft systems contain multiple files. Often, multiple drivers
+share one or more files. During the MS installation process files are checked
+to insure that only a newer version of a shared file is installed over an
+older version. There are several possibilities for this comparison. If there
+is no previous version, the new one is newer (obviously). If either file is
+missing the version info structure, compare the creation date (on Unix use
+the modification date). Otherwise chose the numerically larger version number.
+****************************************************************************/
+
+static int file_version_is_newer(connection_struct *conn, fstring new_file, fstring old_file)
+{
+ BOOL use_version = True;
+ pstring filepath;
+
+ uint32 new_major;
+ uint32 new_minor;
+ time_t new_create_time;
+
+ uint32 old_major;
+ uint32 old_minor;
+ time_t old_create_time;
+
+ int access_mode;
+ int action;
+ files_struct *fsp = NULL;
+ SMB_STRUCT_STAT st;
+ SMB_STRUCT_STAT stat_buf;
+ BOOL bad_path;
+
+ ZERO_STRUCT(st);
+ ZERO_STRUCT(stat_buf);
+ new_create_time = (time_t)0;
+ old_create_time = (time_t)0;
+
+ /* Get file version info (if available) for previous file (if it exists) */
+ pstrcpy(filepath, old_file);
+
+ unix_convert(filepath,conn,NULL,&bad_path,&stat_buf);
+
+ fsp = open_file_shared(conn, filepath, &stat_buf,
+ SET_OPEN_MODE(DOS_OPEN_RDONLY),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
+ FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &action);
+ if (!fsp) {
+ /* Old file not found, so by definition new file is in fact newer */
+ DEBUG(10,("file_version_is_newer: Can't open old file [%s], errno = %d\n",
+ filepath, errno));
+ return True;
+
+ } else {
+ int ret = get_file_version(fsp, old_file, &old_major, &old_minor);
+ if (ret == -1) goto error_exit;
+
+ if (!ret) {
+ DEBUG(6,("file_version_is_newer: Version info not found [%s], use mod time\n",
+ old_file));
+ use_version = False;
+ if (SMB_VFS_FSTAT(fsp, fsp->fd, &st) == -1) goto error_exit;
+ old_create_time = st.st_mtime;
+ DEBUGADD(6,("file_version_is_newer: mod time = %ld sec\n", old_create_time));
+ }
+ }
+ close_file(fsp, True);
+
+ /* Get file version info (if available) for new file */
+ pstrcpy(filepath, new_file);
+ unix_convert(filepath,conn,NULL,&bad_path,&stat_buf);
+
+ fsp = open_file_shared(conn, filepath, &stat_buf,
+ SET_OPEN_MODE(DOS_OPEN_RDONLY),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
+ FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &action);
+ if (!fsp) {
+ /* New file not found, this shouldn't occur if the caller did its job */
+ DEBUG(3,("file_version_is_newer: Can't open new file [%s], errno = %d\n",
+ filepath, errno));
+ goto error_exit;
+
+ } else {
+ int ret = get_file_version(fsp, new_file, &new_major, &new_minor);
+ if (ret == -1) goto error_exit;
+
+ if (!ret) {
+ DEBUG(6,("file_version_is_newer: Version info not found [%s], use mod time\n",
+ new_file));
+ use_version = False;
+ if (SMB_VFS_FSTAT(fsp, fsp->fd, &st) == -1) goto error_exit;
+ new_create_time = st.st_mtime;
+ DEBUGADD(6,("file_version_is_newer: mod time = %ld sec\n", new_create_time));
+ }
+ }
+ close_file(fsp, True);
+
+ if (use_version && (new_major != old_major || new_minor != old_minor)) {
+ /* Compare versions and choose the larger version number */
+ if (new_major > old_major ||
+ (new_major == old_major && new_minor > old_minor)) {
+
+ DEBUG(6,("file_version_is_newer: Replacing [%s] with [%s]\n", old_file, new_file));
+ return True;
+ }
+ else {
+ DEBUG(6,("file_version_is_newer: Leaving [%s] unchanged\n", old_file));
+ return False;
+ }
+
+ } else {
+ /* Compare modification time/dates and choose the newest time/date */
+ if (new_create_time > old_create_time) {
+ DEBUG(6,("file_version_is_newer: Replacing [%s] with [%s]\n", old_file, new_file));
+ return True;
+ }
+ else {
+ DEBUG(6,("file_version_is_newer: Leaving [%s] unchanged\n", old_file));
+ return False;
+ }
+ }
+
+ error_exit:
+ if(fsp)
+ close_file(fsp, True);
+ return -1;
+}
+
+/****************************************************************************
+Determine the correct cVersion associated with an architecture and driver
+****************************************************************************/
+static uint32 get_correct_cversion(const char *architecture, fstring driverpath_in,
+ struct current_user *user, WERROR *perr)
+{
+ int cversion;
+ int access_mode;
+ int action;
+ NTSTATUS nt_status;
+ pstring driverpath;
+ DATA_BLOB null_pw;
+ fstring res_type;
+ files_struct *fsp = NULL;
+ BOOL bad_path;
+ SMB_STRUCT_STAT st;
+ connection_struct *conn;
+
+ ZERO_STRUCT(st);
+
+ *perr = WERR_INVALID_PARAM;
+
+ /* If architecture is Windows 95/98/ME, the version is always 0. */
+ if (strcmp(architecture, "WIN40") == 0) {
+ DEBUG(10,("get_correct_cversion: Driver is Win9x, cversion = 0\n"));
+ *perr = WERR_OK;
+ return 0;
+ }
+
+ /*
+ * Connect to the print$ share under the same account as the user connected
+ * to the rpc pipe. Note we must still be root to do this.
+ */
+
+ /* Null password is ok - we are already an authenticated user... */
+ null_pw = data_blob(NULL, 0);
+ fstrcpy(res_type, "A:");
+ become_root();
+ conn = make_connection_with_chdir("print$", null_pw, res_type, user->vuid, &nt_status);
+ unbecome_root();
+
+ if (conn == NULL) {
+ DEBUG(0,("get_correct_cversion: Unable to connect\n"));
+ *perr = ntstatus_to_werror(nt_status);
+ return -1;
+ }
+
+ /* We are temporarily becoming the connection user. */
+ if (!become_user(conn, user->vuid)) {
+ DEBUG(0,("get_correct_cversion: Can't become user!\n"));
+ *perr = WERR_ACCESS_DENIED;
+ return -1;
+ }
+
+ /* Open the driver file (Portable Executable format) and determine the
+ * deriver the cversion. */
+ slprintf(driverpath, sizeof(driverpath)-1, "%s/%s", architecture, driverpath_in);
+
+ unix_convert(driverpath,conn,NULL,&bad_path,&st);
+
+ fsp = open_file_shared(conn, driverpath, &st,
+ SET_OPEN_MODE(DOS_OPEN_RDONLY),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
+ FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &action);
+ if (!fsp) {
+ DEBUG(3,("get_correct_cversion: Can't open file [%s], errno = %d\n",
+ driverpath, errno));
+ *perr = WERR_ACCESS_DENIED;
+ goto error_exit;
+ }
+ else {
+ uint32 major;
+ uint32 minor;
+ int ret = get_file_version(fsp, driverpath, &major, &minor);
+ if (ret == -1) goto error_exit;
+
+ if (!ret) {
+ DEBUG(6,("get_correct_cversion: Version info not found [%s]\n", driverpath));
+ goto error_exit;
+ }
+
+ /*
+ * This is a Microsoft'ism. See references in MSDN to VER_FILEVERSION
+ * for more details. Version in this case is not just the version of the
+ * file, but the version in the sense of kernal mode (2) vs. user mode
+ * (3) drivers. Other bits of the version fields are the version info.
+ * JRR 010716
+ */
+ cversion = major & 0x0000ffff;
+ switch (cversion) {
+ case 2: /* WinNT drivers */
+ case 3: /* Win2K drivers */
+ break;
+
+ default:
+ DEBUG(6,("get_correct_cversion: cversion invalid [%s] cversion = %d\n",
+ driverpath, cversion));
+ goto error_exit;
+ }
+
+ DEBUG(10,("get_correct_cversion: Version info found [%s] major = 0x%x minor = 0x%x\n",
+ driverpath, major, minor));
+ }
+
+ DEBUG(10,("get_correct_cversion: Driver file [%s] cversion = %d\n",
+ driverpath, cversion));
+
+ close_file(fsp, True);
+ close_cnum(conn, user->vuid);
+ unbecome_user();
+ *perr = WERR_OK;
+ return cversion;
+
+
+ error_exit:
+
+ if(fsp)
+ close_file(fsp, True);
+
+ close_cnum(conn, user->vuid);
+ unbecome_user();
+ return -1;
+}
+
+/****************************************************************************
+****************************************************************************/
+static WERROR clean_up_driver_struct_level_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver,
+ struct current_user *user)
+{
+ const char *architecture;
+ fstring new_name;
+ char *p;
+ int i;
+ WERROR err;
+
+ /* clean up the driver name.
+ * we can get .\driver.dll
+ * or worse c:\windows\system\driver.dll !
+ */
+ /* using an intermediate string to not have overlaping memcpy()'s */
+ if ((p = strrchr(driver->driverpath,'\\')) != NULL) {
+ fstrcpy(new_name, p+1);
+ fstrcpy(driver->driverpath, new_name);
+ }
+
+ if ((p = strrchr(driver->datafile,'\\')) != NULL) {
+ fstrcpy(new_name, p+1);
+ fstrcpy(driver->datafile, new_name);
+ }
+
+ if ((p = strrchr(driver->configfile,'\\')) != NULL) {
+ fstrcpy(new_name, p+1);
+ fstrcpy(driver->configfile, new_name);
+ }
+
+ if ((p = strrchr(driver->helpfile,'\\')) != NULL) {
+ fstrcpy(new_name, p+1);
+ fstrcpy(driver->helpfile, new_name);
+ }
+
+ if (driver->dependentfiles) {
+ for (i=0; *driver->dependentfiles[i]; i++) {
+ if ((p = strrchr(driver->dependentfiles[i],'\\')) != NULL) {
+ fstrcpy(new_name, p+1);
+ fstrcpy(driver->dependentfiles[i], new_name);
+ }
+ }
+ }
+
+ architecture = get_short_archi(driver->environment);
+
+ /* jfm:7/16/2000 the client always sends the cversion=0.
+ * The server should check which version the driver is by reading
+ * the PE header of driver->driverpath.
+ *
+ * For Windows 95/98 the version is 0 (so the value sent is correct)
+ * For Windows NT (the architecture doesn't matter)
+ * NT 3.1: cversion=0
+ * NT 3.5/3.51: cversion=1
+ * NT 4: cversion=2
+ * NT2K: cversion=3
+ */
+ if ((driver->cversion = get_correct_cversion( architecture,
+ driver->driverpath, user, &err)) == -1)
+ return err;
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+static WERROR clean_up_driver_struct_level_6(NT_PRINTER_DRIVER_INFO_LEVEL_6 *driver, struct current_user *user)
+{
+ const char *architecture;
+ fstring new_name;
+ char *p;
+ int i;
+ WERROR err;
+
+ /* clean up the driver name.
+ * we can get .\driver.dll
+ * or worse c:\windows\system\driver.dll !
+ */
+ /* using an intermediate string to not have overlaping memcpy()'s */
+ if ((p = strrchr(driver->driverpath,'\\')) != NULL) {
+ fstrcpy(new_name, p+1);
+ fstrcpy(driver->driverpath, new_name);
+ }
+
+ if ((p = strrchr(driver->datafile,'\\')) != NULL) {
+ fstrcpy(new_name, p+1);
+ fstrcpy(driver->datafile, new_name);
+ }
+
+ if ((p = strrchr(driver->configfile,'\\')) != NULL) {
+ fstrcpy(new_name, p+1);
+ fstrcpy(driver->configfile, new_name);
+ }
+
+ if ((p = strrchr(driver->helpfile,'\\')) != NULL) {
+ fstrcpy(new_name, p+1);
+ fstrcpy(driver->helpfile, new_name);
+ }
+
+ if (driver->dependentfiles) {
+ for (i=0; *driver->dependentfiles[i]; i++) {
+ if ((p = strrchr(driver->dependentfiles[i],'\\')) != NULL) {
+ fstrcpy(new_name, p+1);
+ fstrcpy(driver->dependentfiles[i], new_name);
+ }
+ }
+ }
+
+ architecture = get_short_archi(driver->environment);
+
+ /* jfm:7/16/2000 the client always sends the cversion=0.
+ * The server should check which version the driver is by reading
+ * the PE header of driver->driverpath.
+ *
+ * For Windows 95/98 the version is 0 (so the value sent is correct)
+ * For Windows NT (the architecture doesn't matter)
+ * NT 3.1: cversion=0
+ * NT 3.5/3.51: cversion=1
+ * NT 4: cversion=2
+ * NT2K: cversion=3
+ */
+ if ((driver->version = get_correct_cversion(architecture, driver->driverpath, user, &err)) == -1)
+ return err;
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+WERROR clean_up_driver_struct(NT_PRINTER_DRIVER_INFO_LEVEL driver_abstract,
+ uint32 level, struct current_user *user)
+{
+ switch (level) {
+ case 3:
+ {
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver;
+ driver=driver_abstract.info_3;
+ return clean_up_driver_struct_level_3(driver, user);
+ }
+ case 6:
+ {
+ NT_PRINTER_DRIVER_INFO_LEVEL_6 *driver;
+ driver=driver_abstract.info_6;
+ return clean_up_driver_struct_level_6(driver, user);
+ }
+ default:
+ return WERR_INVALID_PARAM;
+ }
+}
+
+/****************************************************************************
+ This function sucks and should be replaced. JRA.
+****************************************************************************/
+
+static void convert_level_6_to_level3(NT_PRINTER_DRIVER_INFO_LEVEL_3 *dst, NT_PRINTER_DRIVER_INFO_LEVEL_6 *src)
+{
+ dst->cversion = src->version;
+
+ fstrcpy( dst->name, src->name);
+ fstrcpy( dst->environment, src->environment);
+ fstrcpy( dst->driverpath, src->driverpath);
+ fstrcpy( dst->datafile, src->datafile);
+ fstrcpy( dst->configfile, src->configfile);
+ fstrcpy( dst->helpfile, src->helpfile);
+ fstrcpy( dst->monitorname, src->monitorname);
+ fstrcpy( dst->defaultdatatype, src->defaultdatatype);
+ dst->dependentfiles = src->dependentfiles;
+}
+
+#if 0 /* Debugging function */
+
+static char* ffmt(unsigned char *c){
+ int i;
+ static char ffmt_str[17];
+
+ for (i=0; i<16; i++) {
+ if ((c[i] < ' ') || (c[i] > '~'))
+ ffmt_str[i]='.';
+ else
+ ffmt_str[i]=c[i];
+ }
+ ffmt_str[16]='\0';
+ return ffmt_str;
+}
+
+#endif
+
+/****************************************************************************
+****************************************************************************/
+BOOL move_driver_to_download_area(NT_PRINTER_DRIVER_INFO_LEVEL driver_abstract, uint32 level,
+ struct current_user *user, WERROR *perr)
+{
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver;
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 converted_driver;
+ const char *architecture;
+ pstring new_dir;
+ pstring old_name;
+ pstring new_name;
+ DATA_BLOB null_pw;
+ connection_struct *conn;
+ NTSTATUS nt_status;
+ pstring inbuf;
+ pstring outbuf;
+ fstring res_type;
+ int ver = 0;
+ int i;
+
+ memset(inbuf, '\0', sizeof(inbuf));
+ memset(outbuf, '\0', sizeof(outbuf));
+ *perr = WERR_OK;
+
+ if (level==3)
+ driver=driver_abstract.info_3;
+ else if (level==6) {
+ convert_level_6_to_level3(&converted_driver, driver_abstract.info_6);
+ driver = &converted_driver;
+ } else {
+ DEBUG(0,("move_driver_to_download_area: Unknown info level (%u)\n", (unsigned int)level ));
+ return False;
+ }
+
+ architecture = get_short_archi(driver->environment);
+
+ /*
+ * Connect to the print$ share under the same account as the user connected to the rpc pipe.
+ * Note we must be root to do this.
+ */
+
+ null_pw = data_blob(NULL, 0);
+ fstrcpy(res_type, "A:");
+ become_root();
+ conn = make_connection_with_chdir("print$", null_pw, res_type, user->vuid, &nt_status);
+ unbecome_root();
+
+ if (conn == NULL) {
+ DEBUG(0,("move_driver_to_download_area: Unable to connect\n"));
+ *perr = ntstatus_to_werror(nt_status);
+ return False;
+ }
+
+ /*
+ * Save who we are - we are temporarily becoming the connection user.
+ */
+
+ if (!become_user(conn, conn->vuid)) {
+ DEBUG(0,("move_driver_to_download_area: Can't become user!\n"));
+ return False;
+ }
+
+ /*
+ * make the directories version and version\driver_name
+ * under the architecture directory.
+ */
+ DEBUG(5,("Creating first directory\n"));
+ slprintf(new_dir, sizeof(new_dir)-1, "%s/%d", architecture, driver->cversion);
+ mkdir_internal(conn, new_dir);
+
+ /* For each driver file, archi\filexxx.yyy, if there is a duplicate file
+ * listed for this driver which has already been moved, skip it (note:
+ * drivers may list the same file name several times. Then check if the
+ * file already exists in archi\cversion\, if so, check that the version
+ * info (or time stamps if version info is unavailable) is newer (or the
+ * date is later). If it is, move it to archi\cversion\filexxx.yyy.
+ * Otherwise, delete the file.
+ *
+ * If a file is not moved to archi\cversion\ because of an error, all the
+ * rest of the 'unmoved' driver files are removed from archi\. If one or
+ * more of the driver's files was already moved to archi\cversion\, it
+ * potentially leaves the driver in a partially updated state. Version
+ * trauma will most likely occur if an client attempts to use any printer
+ * bound to the driver. Perhaps a rewrite to make sure the moves can be
+ * done is appropriate... later JRR
+ */
+
+ DEBUG(5,("Moving files now !\n"));
+
+ if (driver->driverpath && strlen(driver->driverpath)) {
+ slprintf(new_name, sizeof(new_name)-1, "%s/%s", architecture, driver->driverpath);
+ slprintf(old_name, sizeof(old_name)-1, "%s/%s", new_dir, driver->driverpath);
+ if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) {
+ NTSTATUS status;
+ status = rename_internals(conn, new_name, old_name, 0, True);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n",
+ new_name, old_name));
+ *perr = ntstatus_to_werror(status);
+ unlink_internals(conn, 0, new_name);
+ ver = -1;
+ }
+ }
+ else
+ unlink_internals(conn, 0, new_name);
+ }
+
+ if (driver->datafile && strlen(driver->datafile)) {
+ if (!strequal(driver->datafile, driver->driverpath)) {
+ slprintf(new_name, sizeof(new_name)-1, "%s/%s", architecture, driver->datafile);
+ slprintf(old_name, sizeof(old_name)-1, "%s/%s", new_dir, driver->datafile);
+ if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) {
+ NTSTATUS status;
+ status = rename_internals(conn, new_name, old_name, 0, True);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n",
+ new_name, old_name));
+ *perr = ntstatus_to_werror(status);
+ unlink_internals(conn, 0, new_name);
+ ver = -1;
+ }
+ }
+ else
+ unlink_internals(conn, 0, new_name);
+ }
+ }
+
+ if (driver->configfile && strlen(driver->configfile)) {
+ if (!strequal(driver->configfile, driver->driverpath) &&
+ !strequal(driver->configfile, driver->datafile)) {
+ slprintf(new_name, sizeof(new_name)-1, "%s/%s", architecture, driver->configfile);
+ slprintf(old_name, sizeof(old_name)-1, "%s/%s", new_dir, driver->configfile);
+ if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) {
+ NTSTATUS status;
+ status = rename_internals(conn, new_name, old_name, 0, True);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n",
+ new_name, old_name));
+ *perr = ntstatus_to_werror(status);
+ unlink_internals(conn, 0, new_name);
+ ver = -1;
+ }
+ }
+ else
+ unlink_internals(conn, 0, new_name);
+ }
+ }
+
+ if (driver->helpfile && strlen(driver->helpfile)) {
+ if (!strequal(driver->helpfile, driver->driverpath) &&
+ !strequal(driver->helpfile, driver->datafile) &&
+ !strequal(driver->helpfile, driver->configfile)) {
+ slprintf(new_name, sizeof(new_name)-1, "%s/%s", architecture, driver->helpfile);
+ slprintf(old_name, sizeof(old_name)-1, "%s/%s", new_dir, driver->helpfile);
+ if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) {
+ NTSTATUS status;
+ status = rename_internals(conn, new_name, old_name, 0, True);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n",
+ new_name, old_name));
+ *perr = ntstatus_to_werror(status);
+ unlink_internals(conn, 0, new_name);
+ ver = -1;
+ }
+ }
+ else
+ unlink_internals(conn, 0, new_name);
+ }
+ }
+
+ if (driver->dependentfiles) {
+ for (i=0; *driver->dependentfiles[i]; i++) {
+ if (!strequal(driver->dependentfiles[i], driver->driverpath) &&
+ !strequal(driver->dependentfiles[i], driver->datafile) &&
+ !strequal(driver->dependentfiles[i], driver->configfile) &&
+ !strequal(driver->dependentfiles[i], driver->helpfile)) {
+ int j;
+ for (j=0; j < i; j++) {
+ if (strequal(driver->dependentfiles[i], driver->dependentfiles[j])) {
+ goto NextDriver;
+ }
+ }
+
+ slprintf(new_name, sizeof(new_name)-1, "%s/%s", architecture, driver->dependentfiles[i]);
+ slprintf(old_name, sizeof(old_name)-1, "%s/%s", new_dir, driver->dependentfiles[i]);
+ if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) {
+ NTSTATUS status;
+ status = rename_internals(conn, new_name, old_name, 0, True);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n",
+ new_name, old_name));
+ *perr = ntstatus_to_werror(status);
+ unlink_internals(conn, 0, new_name);
+ ver = -1;
+ }
+ }
+ else
+ unlink_internals(conn, 0, new_name);
+ }
+ NextDriver: ;
+ }
+ }
+
+ close_cnum(conn, user->vuid);
+ unbecome_user();
+
+ return ver == -1 ? False : True;
+}
+
+/****************************************************************************
+****************************************************************************/
+static uint32 add_a_printer_driver_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver)
+{
+ int len, buflen;
+ const char *architecture;
+ pstring directory;
+ fstring temp_name;
+ pstring key;
+ char *buf;
+ int i, ret;
+ TDB_DATA kbuf, dbuf;
+
+ architecture = get_short_archi(driver->environment);
+
+ /* The names are relative. We store them in the form: \print$\arch\version\driver.xxx
+ * \\server is added in the rpc server layer.
+ * It does make sense to NOT store the server's name in the printer TDB.
+ */
+
+ slprintf(directory, sizeof(directory)-1, "\\print$\\%s\\%d\\", architecture, driver->cversion);
+
+ /* .inf files do not always list a file for each of the four standard files.
+ * Don't prepend a path to a null filename, or client claims:
+ * "The server on which the printer resides does not have a suitable
+ * <printer driver name> printer driver installed. Click OK if you
+ * wish to install the driver on your local machine."
+ */
+ if (strlen(driver->driverpath)) {
+ fstrcpy(temp_name, driver->driverpath);
+ slprintf(driver->driverpath, sizeof(driver->driverpath)-1, "%s%s", directory, temp_name);
+ }
+
+ if (strlen(driver->datafile)) {
+ fstrcpy(temp_name, driver->datafile);
+ slprintf(driver->datafile, sizeof(driver->datafile)-1, "%s%s", directory, temp_name);
+ }
+
+ if (strlen(driver->configfile)) {
+ fstrcpy(temp_name, driver->configfile);
+ slprintf(driver->configfile, sizeof(driver->configfile)-1, "%s%s", directory, temp_name);
+ }
+
+ if (strlen(driver->helpfile)) {
+ fstrcpy(temp_name, driver->helpfile);
+ slprintf(driver->helpfile, sizeof(driver->helpfile)-1, "%s%s", directory, temp_name);
+ }
+
+ if (driver->dependentfiles) {
+ for (i=0; *driver->dependentfiles[i]; i++) {
+ fstrcpy(temp_name, driver->dependentfiles[i]);
+ slprintf(driver->dependentfiles[i], sizeof(driver->dependentfiles[i])-1, "%s%s", directory, temp_name);
+ }
+ }
+
+ slprintf(key, sizeof(key)-1, "%s%s/%d/%s", DRIVERS_PREFIX, architecture, driver->cversion, driver->name);
+
+ DEBUG(5,("add_a_printer_driver_3: Adding driver with key %s\n", key ));
+
+ buf = NULL;
+ len = buflen = 0;
+
+ again:
+ len = 0;
+ len += tdb_pack(buf+len, buflen-len, "dffffffff",
+ driver->cversion,
+ driver->name,
+ driver->environment,
+ driver->driverpath,
+ driver->datafile,
+ driver->configfile,
+ driver->helpfile,
+ driver->monitorname,
+ driver->defaultdatatype);
+
+ if (driver->dependentfiles) {
+ for (i=0; *driver->dependentfiles[i]; i++) {
+ len += tdb_pack(buf+len, buflen-len, "f",
+ driver->dependentfiles[i]);
+ }
+ }
+
+ if (len != buflen) {
+ char *tb;
+
+ tb = (char *)Realloc(buf, len);
+ if (!tb) {
+ DEBUG(0,("add_a_printer_driver_3: failed to enlarge buffer\n!"));
+ ret = -1;
+ goto done;
+ }
+ else buf = tb;
+ buflen = len;
+ goto again;
+ }
+
+
+ kbuf.dptr = key;
+ kbuf.dsize = strlen(key)+1;
+ dbuf.dptr = buf;
+ dbuf.dsize = len;
+
+ ret = tdb_store(tdb_drivers, kbuf, dbuf, TDB_REPLACE);
+
+done:
+ if (ret)
+ DEBUG(0,("add_a_printer_driver_3: Adding driver with key %s failed.\n", key ));
+
+ SAFE_FREE(buf);
+ return ret;
+}
+
+/****************************************************************************
+****************************************************************************/
+static uint32 add_a_printer_driver_6(NT_PRINTER_DRIVER_INFO_LEVEL_6 *driver)
+{
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 info3;
+
+ ZERO_STRUCT(info3);
+ info3.cversion = driver->version;
+ fstrcpy(info3.name,driver->name);
+ fstrcpy(info3.environment,driver->environment);
+ fstrcpy(info3.driverpath,driver->driverpath);
+ fstrcpy(info3.datafile,driver->datafile);
+ fstrcpy(info3.configfile,driver->configfile);
+ fstrcpy(info3.helpfile,driver->helpfile);
+ fstrcpy(info3.monitorname,driver->monitorname);
+ fstrcpy(info3.defaultdatatype,driver->defaultdatatype);
+ info3.dependentfiles = driver->dependentfiles;
+
+ return add_a_printer_driver_3(&info3);
+}
+
+
+/****************************************************************************
+****************************************************************************/
+static WERROR get_a_printer_driver_3_default(NT_PRINTER_DRIVER_INFO_LEVEL_3 **info_ptr, const char *driver, const char *arch)
+{
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 info;
+
+ ZERO_STRUCT(info);
+
+ fstrcpy(info.name, driver);
+ fstrcpy(info.defaultdatatype, "RAW");
+
+ fstrcpy(info.driverpath, "");
+ fstrcpy(info.datafile, "");
+ fstrcpy(info.configfile, "");
+ fstrcpy(info.helpfile, "");
+
+ if ((info.dependentfiles=(fstring *)malloc(2*sizeof(fstring))) == NULL)
+ return WERR_NOMEM;
+
+ memset(info.dependentfiles, '\0', 2*sizeof(fstring));
+ fstrcpy(info.dependentfiles[0], "");
+
+ *info_ptr = memdup(&info, sizeof(info));
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+static WERROR get_a_printer_driver_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 **info_ptr, fstring drivername, const char *arch, uint32 version)
+{
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 driver;
+ TDB_DATA kbuf, dbuf;
+ const char *architecture;
+ int len = 0;
+ int i;
+ pstring key;
+
+ ZERO_STRUCT(driver);
+
+ architecture = get_short_archi(arch);
+
+ /* Windows 4.0 (i.e. win9x) should always use a version of 0 */
+
+ if ( strcmp( architecture, SPL_ARCH_WIN40 ) == 0 )
+ version = 0;
+
+ DEBUG(8,("get_a_printer_driver_3: [%s%s/%d/%s]\n", DRIVERS_PREFIX, architecture, version, drivername));
+
+ slprintf(key, sizeof(key)-1, "%s%s/%d/%s", DRIVERS_PREFIX, architecture, version, drivername);
+
+ kbuf.dptr = key;
+ kbuf.dsize = strlen(key)+1;
+
+ dbuf = tdb_fetch(tdb_drivers, kbuf);
+ if (!dbuf.dptr)
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+
+ len += tdb_unpack(dbuf.dptr, dbuf.dsize, "dffffffff",
+ &driver.cversion,
+ driver.name,
+ driver.environment,
+ driver.driverpath,
+ driver.datafile,
+ driver.configfile,
+ driver.helpfile,
+ driver.monitorname,
+ driver.defaultdatatype);
+
+ i=0;
+ while (len < dbuf.dsize) {
+ fstring *tddfs;
+
+ tddfs = (fstring *)Realloc(driver.dependentfiles,
+ sizeof(fstring)*(i+2));
+ if (tddfs == NULL) {
+ DEBUG(0,("get_a_printer_driver_3: failed to enlarge buffer!\n"));
+ break;
+ }
+ else driver.dependentfiles = tddfs;
+
+ len += tdb_unpack(dbuf.dptr+len, dbuf.dsize-len, "f",
+ &driver.dependentfiles[i]);
+ i++;
+ }
+
+ if (driver.dependentfiles != NULL)
+ fstrcpy(driver.dependentfiles[i], "");
+
+ SAFE_FREE(dbuf.dptr);
+
+ if (len != dbuf.dsize) {
+ SAFE_FREE(driver.dependentfiles);
+
+ return get_a_printer_driver_3_default(info_ptr, drivername, arch);
+ }
+
+ *info_ptr = (NT_PRINTER_DRIVER_INFO_LEVEL_3 *)memdup(&driver, sizeof(driver));
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ Debugging function, dump at level 6 the struct in the logs.
+****************************************************************************/
+
+static uint32 dump_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL driver, uint32 level)
+{
+ uint32 result;
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 *info3;
+ int i;
+
+ DEBUG(20,("Dumping printer driver at level [%d]\n", level));
+
+ switch (level)
+ {
+ case 3:
+ {
+ if (driver.info_3 == NULL)
+ result=5;
+ else {
+ info3=driver.info_3;
+
+ DEBUGADD(20,("version:[%d]\n", info3->cversion));
+ DEBUGADD(20,("name:[%s]\n", info3->name));
+ DEBUGADD(20,("environment:[%s]\n", info3->environment));
+ DEBUGADD(20,("driverpath:[%s]\n", info3->driverpath));
+ DEBUGADD(20,("datafile:[%s]\n", info3->datafile));
+ DEBUGADD(20,("configfile:[%s]\n", info3->configfile));
+ DEBUGADD(20,("helpfile:[%s]\n", info3->helpfile));
+ DEBUGADD(20,("monitorname:[%s]\n", info3->monitorname));
+ DEBUGADD(20,("defaultdatatype:[%s]\n", info3->defaultdatatype));
+
+ for (i=0; info3->dependentfiles &&
+ *info3->dependentfiles[i]; i++) {
+ DEBUGADD(20,("dependentfile:[%s]\n",
+ info3->dependentfiles[i]));
+ }
+ result=0;
+ }
+ break;
+ }
+ default:
+ DEBUGADD(20,("dump_a_printer_driver: Level %u not implemented\n", (unsigned int)level));
+ result=1;
+ break;
+ }
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+int pack_devicemode(NT_DEVICEMODE *nt_devmode, char *buf, int buflen)
+{
+ int len = 0;
+
+ len += tdb_pack(buf+len, buflen-len, "p", nt_devmode);
+
+ if (!nt_devmode)
+ return len;
+
+ len += tdb_pack(buf+len, buflen-len, "ffwwwwwwwwwwwwwwwwwwddddddddddddddp",
+ nt_devmode->devicename,
+ nt_devmode->formname,
+
+ nt_devmode->specversion,
+ nt_devmode->driverversion,
+ nt_devmode->size,
+ nt_devmode->driverextra,
+ nt_devmode->orientation,
+ nt_devmode->papersize,
+ nt_devmode->paperlength,
+ nt_devmode->paperwidth,
+ nt_devmode->scale,
+ nt_devmode->copies,
+ nt_devmode->defaultsource,
+ nt_devmode->printquality,
+ nt_devmode->color,
+ nt_devmode->duplex,
+ nt_devmode->yresolution,
+ nt_devmode->ttoption,
+ nt_devmode->collate,
+ nt_devmode->logpixels,
+
+ nt_devmode->fields,
+ nt_devmode->bitsperpel,
+ nt_devmode->pelswidth,
+ nt_devmode->pelsheight,
+ nt_devmode->displayflags,
+ nt_devmode->displayfrequency,
+ nt_devmode->icmmethod,
+ nt_devmode->icmintent,
+ nt_devmode->mediatype,
+ nt_devmode->dithertype,
+ nt_devmode->reserved1,
+ nt_devmode->reserved2,
+ nt_devmode->panningwidth,
+ nt_devmode->panningheight,
+ nt_devmode->private);
+
+
+ if (nt_devmode->private) {
+ len += tdb_pack(buf+len, buflen-len, "B",
+ nt_devmode->driverextra,
+ nt_devmode->private);
+ }
+
+ DEBUG(8,("Packed devicemode [%s]\n", nt_devmode->formname));
+
+ return len;
+}
+
+/****************************************************************************
+ Pack all values in all printer keys
+ ***************************************************************************/
+
+static int pack_values(NT_PRINTER_DATA *data, char *buf, int buflen)
+{
+ int len = 0;
+ int i, j;
+ REGISTRY_VALUE *val;
+ REGVAL_CTR *val_ctr;
+ pstring path;
+ int num_values;
+
+ if ( !data )
+ return 0;
+
+ /* loop over all keys */
+
+ for ( i=0; i<data->num_keys; i++ ) {
+ val_ctr = &data->keys[i].values;
+ num_values = regval_ctr_numvals( val_ctr );
+
+ /* loop over all values */
+
+ for ( j=0; j<num_values; j++ ) {
+ /* pathname should be stored as <key>\<value> */
+
+ val = regval_ctr_specific_value( val_ctr, j );
+ pstrcpy( path, data->keys[i].name );
+ pstrcat( path, "\\" );
+ pstrcat( path, regval_name(val) );
+
+ len += tdb_pack(buf+len, buflen-len, "pPdB",
+ val,
+ path,
+ regval_type(val),
+ regval_size(val),
+ regval_data_p(val) );
+ }
+
+ }
+
+ /* terminator */
+
+ len += tdb_pack(buf+len, buflen-len, "p", NULL);
+
+ return len;
+}
+
+
+/****************************************************************************
+ Delete a printer - this just deletes the printer info file, any open
+ handles are not affected.
+****************************************************************************/
+
+uint32 del_a_printer(char *sharename)
+{
+ pstring key;
+ TDB_DATA kbuf;
+
+ slprintf(key, sizeof(key)-1, "%s%s", PRINTERS_PREFIX, sharename);
+
+ kbuf.dptr=key;
+ kbuf.dsize=strlen(key)+1;
+
+ tdb_delete(tdb_printers, kbuf);
+ return 0;
+}
+
+/* FIXME!!! Reorder so this forward declaration is not necessary --jerry */
+static WERROR get_a_printer_2(NT_PRINTER_INFO_LEVEL_2 **, const char* sharename);
+static void free_nt_printer_info_level_2(NT_PRINTER_INFO_LEVEL_2 **);
+/****************************************************************************
+****************************************************************************/
+static WERROR update_a_printer_2(NT_PRINTER_INFO_LEVEL_2 *info)
+{
+ pstring key;
+ char *buf;
+ int buflen, len;
+ WERROR ret;
+ TDB_DATA kbuf, dbuf;
+
+ /*
+ * in addprinter: no servername and the printer is the name
+ * in setprinter: servername is \\server
+ * and printer is \\server\\printer
+ *
+ * Samba manages only local printers.
+ * we currently don't support things like path=\\other_server\printer
+ */
+
+ if (info->servername[0]!='\0') {
+ trim_string(info->printername, info->servername, NULL);
+ trim_char(info->printername, '\\', '\0');
+ info->servername[0]='\0';
+ }
+
+ /*
+ * JFM: one day I'll forget.
+ * below that's info->portname because that's the SAMBA sharename
+ * and I made NT 'thinks' it's the portname
+ * the info->sharename is the thing you can name when you add a printer
+ * that's the short-name when you create shared printer for 95/98
+ * So I've made a limitation in SAMBA: you can only have 1 printer model
+ * behind a SAMBA share.
+ */
+
+ buf = NULL;
+ buflen = 0;
+
+ again:
+ len = 0;
+ len += tdb_pack(buf+len, buflen-len, "dddddddddddfffffPfffff",
+ info->attributes,
+ info->priority,
+ info->default_priority,
+ info->starttime,
+ info->untiltime,
+ info->status,
+ info->cjobs,
+ info->averageppm,
+ info->changeid,
+ info->c_setprinter,
+ info->setuptime,
+ info->servername,
+ info->printername,
+ info->sharename,
+ info->portname,
+ info->drivername,
+ info->comment,
+ info->location,
+ info->sepfile,
+ info->printprocessor,
+ info->datatype,
+ info->parameters);
+
+ len += pack_devicemode(info->devmode, buf+len, buflen-len);
+
+ len += pack_values( &info->data, buf+len, buflen-len );
+
+ if (buflen != len) {
+ char *tb;
+
+ tb = (char *)Realloc(buf, len);
+ if (!tb) {
+ DEBUG(0,("update_a_printer_2: failed to enlarge buffer!\n"));
+ ret = WERR_NOMEM;
+ goto done;
+ }
+ else buf = tb;
+ buflen = len;
+ goto again;
+ }
+
+
+ slprintf(key, sizeof(key)-1, "%s%s", PRINTERS_PREFIX, info->sharename);
+
+ kbuf.dptr = key;
+ kbuf.dsize = strlen(key)+1;
+ dbuf.dptr = buf;
+ dbuf.dsize = len;
+
+ ret = (tdb_store(tdb_printers, kbuf, dbuf, TDB_REPLACE) == 0? WERR_OK : WERR_NOMEM);
+
+done:
+ if (!W_ERROR_IS_OK(ret))
+ DEBUG(8, ("error updating printer to tdb on disk\n"));
+
+ SAFE_FREE(buf);
+
+ DEBUG(8,("packed printer [%s] with driver [%s] portname=[%s] len=%d\n",
+ info->sharename, info->drivername, info->portname, len));
+
+ return ret;
+}
+
+
+/****************************************************************************
+ Malloc and return an NT devicemode.
+****************************************************************************/
+
+NT_DEVICEMODE *construct_nt_devicemode(const fstring default_devicename)
+{
+
+ char adevice[MAXDEVICENAME];
+ NT_DEVICEMODE *nt_devmode = (NT_DEVICEMODE *)malloc(sizeof(NT_DEVICEMODE));
+
+ if (nt_devmode == NULL) {
+ DEBUG(0,("construct_nt_devicemode: malloc fail.\n"));
+ return NULL;
+ }
+
+ ZERO_STRUCTP(nt_devmode);
+
+ safe_strcpy(adevice, default_devicename, sizeof(adevice)-1);
+ fstrcpy(nt_devmode->devicename, adevice);
+
+ fstrcpy(nt_devmode->formname, "Letter");
+
+ nt_devmode->specversion = 0x0401;
+ nt_devmode->driverversion = 0x0400;
+ nt_devmode->size = 0x00DC;
+ nt_devmode->driverextra = 0x0000;
+ nt_devmode->fields = FORMNAME | TTOPTION | PRINTQUALITY |
+ DEFAULTSOURCE | COPIES | SCALE |
+ PAPERSIZE | ORIENTATION;
+ nt_devmode->orientation = 1;
+ nt_devmode->papersize = PAPER_LETTER;
+ nt_devmode->paperlength = 0;
+ nt_devmode->paperwidth = 0;
+ nt_devmode->scale = 0x64;
+ nt_devmode->copies = 1;
+ nt_devmode->defaultsource = BIN_FORMSOURCE;
+ nt_devmode->printquality = RES_HIGH; /* 0x0258 */
+ nt_devmode->color = COLOR_MONOCHROME;
+ nt_devmode->duplex = DUP_SIMPLEX;
+ nt_devmode->yresolution = 0;
+ nt_devmode->ttoption = TT_SUBDEV;
+ nt_devmode->collate = COLLATE_FALSE;
+ nt_devmode->icmmethod = 0;
+ nt_devmode->icmintent = 0;
+ nt_devmode->mediatype = 0;
+ nt_devmode->dithertype = 0;
+
+ /* non utilisés par un driver d'imprimante */
+ nt_devmode->logpixels = 0;
+ nt_devmode->bitsperpel = 0;
+ nt_devmode->pelswidth = 0;
+ nt_devmode->pelsheight = 0;
+ nt_devmode->displayflags = 0;
+ nt_devmode->displayfrequency = 0;
+ nt_devmode->reserved1 = 0;
+ nt_devmode->reserved2 = 0;
+ nt_devmode->panningwidth = 0;
+ nt_devmode->panningheight = 0;
+
+ nt_devmode->private = NULL;
+ return nt_devmode;
+}
+
+/****************************************************************************
+ Deepcopy an NT devicemode.
+****************************************************************************/
+
+NT_DEVICEMODE *dup_nt_devicemode(NT_DEVICEMODE *nt_devicemode)
+{
+ NT_DEVICEMODE *new_nt_devicemode = NULL;
+
+ if ( !nt_devicemode )
+ return NULL;
+
+ if ((new_nt_devicemode = (NT_DEVICEMODE *)memdup(nt_devicemode, sizeof(NT_DEVICEMODE))) == NULL) {
+ DEBUG(0,("dup_nt_devicemode: malloc fail.\n"));
+ return NULL;
+ }
+
+ new_nt_devicemode->private = NULL;
+ if (nt_devicemode->private != NULL) {
+ if ((new_nt_devicemode->private = memdup(nt_devicemode->private, nt_devicemode->driverextra)) == NULL) {
+ SAFE_FREE(new_nt_devicemode);
+ DEBUG(0,("dup_nt_devicemode: malloc fail.\n"));
+ return NULL;
+ }
+ }
+
+ return new_nt_devicemode;
+}
+
+/****************************************************************************
+ Clean up and deallocate a (maybe partially) allocated NT_DEVICEMODE.
+****************************************************************************/
+
+void free_nt_devicemode(NT_DEVICEMODE **devmode_ptr)
+{
+ NT_DEVICEMODE *nt_devmode = *devmode_ptr;
+
+ if(nt_devmode == NULL)
+ return;
+
+ DEBUG(106,("free_nt_devicemode: deleting DEVMODE\n"));
+
+ SAFE_FREE(nt_devmode->private);
+ SAFE_FREE(*devmode_ptr);
+}
+
+/****************************************************************************
+ Clean up and deallocate a (maybe partially) allocated NT_PRINTER_INFO_LEVEL_2.
+****************************************************************************/
+static void free_nt_printer_info_level_2(NT_PRINTER_INFO_LEVEL_2 **info_ptr)
+{
+ NT_PRINTER_INFO_LEVEL_2 *info = *info_ptr;
+ NT_PRINTER_DATA *data;
+ int i;
+
+ if ( !info )
+ return;
+
+ DEBUG(106,("free_nt_printer_info_level_2: deleting info\n"));
+
+ free_nt_devicemode(&info->devmode);
+
+ /* clean up all registry keys */
+
+ data = &info->data;
+ for ( i=0; i<data->num_keys; i++ ) {
+ SAFE_FREE( data->keys[i].name );
+ regval_ctr_destroy( &data->keys[i].values );
+ }
+ SAFE_FREE( data->keys );
+
+ /* finally the top level structure */
+
+ SAFE_FREE( *info_ptr );
+}
+
+
+/****************************************************************************
+****************************************************************************/
+int unpack_devicemode(NT_DEVICEMODE **nt_devmode, char *buf, int buflen)
+{
+ int len = 0;
+ int extra_len = 0;
+ NT_DEVICEMODE devmode;
+
+ ZERO_STRUCT(devmode);
+
+ len += tdb_unpack(buf+len, buflen-len, "p", nt_devmode);
+
+ if (!*nt_devmode) return len;
+
+ len += tdb_unpack(buf+len, buflen-len, "ffwwwwwwwwwwwwwwwwwwddddddddddddddp",
+ devmode.devicename,
+ devmode.formname,
+
+ &devmode.specversion,
+ &devmode.driverversion,
+ &devmode.size,
+ &devmode.driverextra,
+ &devmode.orientation,
+ &devmode.papersize,
+ &devmode.paperlength,
+ &devmode.paperwidth,
+ &devmode.scale,
+ &devmode.copies,
+ &devmode.defaultsource,
+ &devmode.printquality,
+ &devmode.color,
+ &devmode.duplex,
+ &devmode.yresolution,
+ &devmode.ttoption,
+ &devmode.collate,
+ &devmode.logpixels,
+
+ &devmode.fields,
+ &devmode.bitsperpel,
+ &devmode.pelswidth,
+ &devmode.pelsheight,
+ &devmode.displayflags,
+ &devmode.displayfrequency,
+ &devmode.icmmethod,
+ &devmode.icmintent,
+ &devmode.mediatype,
+ &devmode.dithertype,
+ &devmode.reserved1,
+ &devmode.reserved2,
+ &devmode.panningwidth,
+ &devmode.panningheight,
+ &devmode.private);
+
+ if (devmode.private) {
+ /* the len in tdb_unpack is an int value and
+ * devmode.driverextra is only a short
+ */
+ len += tdb_unpack(buf+len, buflen-len, "B", &extra_len, &devmode.private);
+ devmode.driverextra=(uint16)extra_len;
+
+ /* check to catch an invalid TDB entry so we don't segfault */
+ if (devmode.driverextra == 0) {
+ devmode.private = NULL;
+ }
+ }
+
+ *nt_devmode = (NT_DEVICEMODE *)memdup(&devmode, sizeof(devmode));
+
+ DEBUG(8,("Unpacked devicemode [%s](%s)\n", devmode.devicename, devmode.formname));
+ if (devmode.private)
+ DEBUG(8,("with a private section of %d bytes\n", devmode.driverextra));
+
+ return len;
+}
+
+/****************************************************************************
+ Allocate and initialize a new slot.
+***************************************************************************/
+
+static int add_new_printer_key( NT_PRINTER_DATA *data, const char *name )
+{
+ NT_PRINTER_KEY *d;
+ int key_index;
+
+ if ( !data || !name )
+ return -1;
+
+ /* allocate another slot in the NT_PRINTER_KEY array */
+
+ d = Realloc( data->keys, sizeof(NT_PRINTER_KEY)*(data->num_keys+1) );
+ if ( d )
+ data->keys = d;
+
+ key_index = data->num_keys;
+
+ /* initialze new key */
+
+ data->num_keys++;
+ data->keys[key_index].name = strdup( name );
+
+ ZERO_STRUCTP( &data->keys[key_index].values );
+
+ regval_ctr_init( &data->keys[key_index].values );
+
+ DEBUG(10,("add_new_printer_key: Inserted new data key [%s]\n", name ));
+
+ return key_index;
+}
+
+/****************************************************************************
+ search for a registry key name in the existing printer data
+ ***************************************************************************/
+
+int lookup_printerkey( NT_PRINTER_DATA *data, const char *name )
+{
+ int key_index = -1;
+ int i;
+
+ if ( !data || !name )
+ return -1;
+
+ DEBUG(12,("lookup_printerkey: Looking for [%s]\n", name));
+
+ /* loop over all existing keys */
+
+ for ( i=0; i<data->num_keys; i++ ) {
+ if ( strequal(data->keys[i].name, name) ) {
+ DEBUG(12,("lookup_printerkey: Found [%s]!\n", name));
+ key_index = i;
+ break;
+
+ }
+ }
+
+ return key_index;
+}
+
+/****************************************************************************
+ ***************************************************************************/
+
+uint32 get_printer_subkeys( NT_PRINTER_DATA *data, const char* key, fstring **subkeys )
+{
+ int i, j;
+ int key_len;
+ int num_subkeys = 0;
+ char *p;
+ fstring *ptr, *subkeys_ptr = NULL;
+ fstring subkeyname;
+
+ if ( !data )
+ return 0;
+
+ for ( i=0; i<data->num_keys; i++ ) {
+ if ( StrnCaseCmp(data->keys[i].name, key, strlen(key)) == 0 ) {
+ /* match sure it is a subkey and not the key itself */
+
+ key_len = strlen( key );
+ if ( strlen(data->keys[i].name) == key_len )
+ continue;
+
+ /* get subkey path */
+
+ p = data->keys[i].name + key_len;
+ if ( *p == '\\' )
+ p++;
+ fstrcpy( subkeyname, p );
+ if ( (p = strchr( subkeyname, '\\' )) )
+ *p = '\0';
+
+ /* don't add a key more than once */
+
+ for ( j=0; j<num_subkeys; j++ ) {
+ if ( strequal( subkeys_ptr[j], subkeyname ) )
+ break;
+ }
+
+ if ( j != num_subkeys )
+ continue;
+
+ /* found a match, so allocate space and copy the name */
+
+ if ( !(ptr = Realloc( subkeys_ptr, (num_subkeys+2)*sizeof(fstring))) ) {
+ DEBUG(0,("get_printer_subkeys: Realloc failed for [%d] entries!\n",
+ num_subkeys+1));
+ SAFE_FREE( subkeys );
+ return 0;
+ }
+
+ subkeys_ptr = ptr;
+ fstrcpy( subkeys_ptr[num_subkeys], subkeyname );
+ num_subkeys++;
+ }
+
+ }
+
+ /* tag of the end */
+
+ if (num_subkeys)
+ fstrcpy(subkeys_ptr[num_subkeys], "" );
+
+ *subkeys = subkeys_ptr;
+
+ return num_subkeys;
+}
+
+#ifdef HAVE_ADS
+static void map_sz_into_ctr(REGVAL_CTR *ctr, const char *val_name,
+ const char *sz)
+{
+ smb_ucs2_t conv_str[1024];
+ size_t str_size;
+
+ regval_ctr_delvalue(ctr, val_name);
+ str_size = push_ucs2(NULL, conv_str, sz, sizeof(conv_str),
+ STR_TERMINATE | STR_NOALIGN);
+ regval_ctr_addvalue(ctr, val_name, REG_SZ,
+ (char *) conv_str, str_size);
+}
+
+static void map_dword_into_ctr(REGVAL_CTR *ctr, const char *val_name,
+ uint32 dword)
+{
+ regval_ctr_delvalue(ctr, val_name);
+ regval_ctr_addvalue(ctr, val_name, REG_DWORD,
+ (char *) &dword, sizeof(dword));
+}
+
+static void map_bool_into_ctr(REGVAL_CTR *ctr, const char *val_name,
+ BOOL b)
+{
+ uint8 bin_bool = (b ? 1 : 0);
+ regval_ctr_delvalue(ctr, val_name);
+ regval_ctr_addvalue(ctr, val_name, REG_BINARY,
+ (char *) &bin_bool, sizeof(bin_bool));
+}
+
+static void map_single_multi_sz_into_ctr(REGVAL_CTR *ctr, const char *val_name,
+ const char *multi_sz)
+{
+ smb_ucs2_t *conv_strs = NULL;
+ size_t str_size;
+
+ /* a multi-sz has to have a null string terminator, i.e., the last
+ string must be followed by two nulls */
+ str_size = (strlen(multi_sz) + 2) * sizeof(smb_ucs2_t);
+ conv_strs = calloc(str_size, 1);
+
+ push_ucs2(NULL, conv_strs, multi_sz, str_size,
+ STR_TERMINATE | STR_NOALIGN);
+
+ regval_ctr_delvalue(ctr, val_name);
+ regval_ctr_addvalue(ctr, val_name, REG_MULTI_SZ,
+ (char *) conv_strs, str_size);
+ safe_free(conv_strs);
+
+}
+
+/****************************************************************************
+ * Map the NT_PRINTER_INFO_LEVEL_2 data into DsSpooler keys for publishing.
+ *
+ * @param info2 NT_PRINTER_INFO_LEVEL_2 describing printer - gets modified
+ * @return BOOL indicating success or failure
+ ***************************************************************************/
+
+static BOOL map_nt_printer_info2_to_dsspooler(NT_PRINTER_INFO_LEVEL_2 *info2)
+{
+ REGVAL_CTR *ctr = NULL;
+ fstring longname;
+ char *allocated_string = NULL;
+ const char *ascii_str;
+ int i;
+
+ if ((i = lookup_printerkey(&info2->data, SPOOL_DSSPOOLER_KEY)) < 0)
+ i = add_new_printer_key(&info2->data, SPOOL_DSSPOOLER_KEY);
+ ctr = &info2->data.keys[i].values;
+
+ map_sz_into_ctr(ctr, SPOOL_REG_PRINTERNAME, info2->sharename);
+ map_sz_into_ctr(ctr, SPOOL_REG_SHORTSERVERNAME, global_myname());
+
+ get_mydnsfullname(longname);
+ map_sz_into_ctr(ctr, SPOOL_REG_SERVERNAME, longname);
+
+ asprintf(&allocated_string, "\\\\%s\\%s", longname, info2->sharename);
+ map_sz_into_ctr(ctr, SPOOL_REG_UNCNAME, allocated_string);
+ SAFE_FREE(allocated_string);
+
+ map_dword_into_ctr(ctr, SPOOL_REG_VERSIONNUMBER, 4);
+ map_sz_into_ctr(ctr, SPOOL_REG_DRIVERNAME, info2->drivername);
+ map_sz_into_ctr(ctr, SPOOL_REG_LOCATION, info2->location);
+ map_sz_into_ctr(ctr, SPOOL_REG_DESCRIPTION, info2->comment);
+ map_single_multi_sz_into_ctr(ctr, SPOOL_REG_PORTNAME, info2->portname);
+ map_sz_into_ctr(ctr, SPOOL_REG_PRINTSEPARATORFILE, info2->sepfile);
+ map_dword_into_ctr(ctr, SPOOL_REG_PRINTSTARTTIME, info2->starttime);
+ map_dword_into_ctr(ctr, SPOOL_REG_PRINTENDTIME, info2->untiltime);
+ map_dword_into_ctr(ctr, SPOOL_REG_PRIORITY, info2->priority);
+
+ map_bool_into_ctr(ctr, SPOOL_REG_PRINTKEEPPRINTEDJOBS,
+ (info2->attributes &
+ PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS));
+
+ switch (info2->attributes & 0x3) {
+ case 0:
+ ascii_str = SPOOL_REGVAL_PRINTWHILESPOOLING;
+ break;
+ case 1:
+ ascii_str = SPOOL_REGVAL_PRINTAFTERSPOOLED;
+ break;
+ case 2:
+ ascii_str = SPOOL_REGVAL_PRINTDIRECT;
+ break;
+ default:
+ ascii_str = "unknown";
+ }
+ map_sz_into_ctr(ctr, SPOOL_REG_PRINTSPOOLING, ascii_str);
+
+ return True;
+}
+
+static void store_printer_guid(NT_PRINTER_INFO_LEVEL_2 *info2,
+ struct uuid guid)
+{
+ int i;
+ REGVAL_CTR *ctr=NULL;
+
+ /* find the DsSpooler key */
+ if ((i = lookup_printerkey(&info2->data, SPOOL_DSSPOOLER_KEY)) < 0)
+ i = add_new_printer_key(&info2->data, SPOOL_DSSPOOLER_KEY);
+ ctr = &info2->data.keys[i].values;
+
+ regval_ctr_delvalue(ctr, "objectGUID");
+ regval_ctr_addvalue(ctr, "objectGUID", REG_BINARY,
+ (char *) &guid, sizeof(struct uuid));
+}
+
+static WERROR publish_it(NT_PRINTER_INFO_LEVEL *printer)
+{
+ ADS_STATUS ads_rc;
+ TALLOC_CTX *ctx = talloc_init("publish_it");
+ ADS_MODLIST mods = ads_init_mods(ctx);
+ char *prt_dn = NULL, *srv_dn, *srv_cn_0;
+ char *srv_dn_utf8, **srv_cn_utf8;
+ void *res = NULL;
+ ADS_STRUCT *ads;
+ const char *attrs[] = {"objectGUID", NULL};
+ struct uuid guid;
+ WERROR win_rc = WERR_OK;
+
+ ZERO_STRUCT(guid);
+ /* set the DsSpooler info and attributes */
+ if (!(map_nt_printer_info2_to_dsspooler(printer->info_2)))
+ return WERR_NOMEM;
+ printer->info_2->attributes |= PRINTER_ATTRIBUTE_PUBLISHED;
+ win_rc = mod_a_printer(*printer, 2);
+ if (!W_ERROR_IS_OK(win_rc)) {
+ DEBUG(3, ("err %d saving data\n",
+ W_ERROR_V(win_rc)));
+ return win_rc;
+ }
+
+ /* Build the ads mods */
+ get_local_printer_publishing_data(ctx, &mods,
+ &printer->info_2->data);
+ ads_mod_str(ctx, &mods, SPOOL_REG_PRINTERNAME,
+ printer->info_2->sharename);
+
+ /* initial ads structure */
+
+ ads = ads_init(NULL, NULL, NULL);
+ if (!ads) {
+ DEBUG(3, ("ads_init() failed\n"));
+ return WERR_SERVER_UNAVAILABLE;
+ }
+ setenv(KRB5_ENV_CCNAME, "MEMORY:prtpub_cache", 1);
+ SAFE_FREE(ads->auth.password);
+ ads->auth.password = secrets_fetch_machine_password(lp_workgroup(),
+ NULL, NULL);
+
+ /* ads_connect() will find the DC for us */
+ ads_rc = ads_connect(ads);
+ if (!ADS_ERR_OK(ads_rc)) {
+ DEBUG(3, ("ads_connect failed: %s\n", ads_errstr(ads_rc)));
+ ads_destroy(&ads);
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* figure out where to publish */
+ ads_find_machine_acct(ads, &res, global_myname());
+
+ /* We use ldap_get_dn here as we need the answer
+ * in utf8 to call ldap_explode_dn(). JRA. */
+
+ srv_dn_utf8 = ldap_get_dn(ads->ld, res);
+ if (!srv_dn_utf8) {
+ ads_destroy(&ads);
+ return WERR_SERVER_UNAVAILABLE;
+ }
+ ads_msgfree(ads, res);
+ srv_cn_utf8 = ldap_explode_dn(srv_dn_utf8, 1);
+ if (!srv_cn_utf8) {
+ ldap_memfree(srv_dn_utf8);
+ ads_destroy(&ads);
+ return WERR_SERVER_UNAVAILABLE;
+ }
+ /* Now convert to CH_UNIX. */
+ if (pull_utf8_allocate(&srv_dn, srv_dn_utf8) == (size_t)-1) {
+ ldap_memfree(srv_dn_utf8);
+ ldap_memfree(srv_cn_utf8);
+ ads_destroy(&ads);
+ return WERR_SERVER_UNAVAILABLE;
+ }
+ if (pull_utf8_allocate(&srv_cn_0, srv_cn_utf8[0]) == (size_t)-1) {
+ ldap_memfree(srv_dn_utf8);
+ ldap_memfree(srv_cn_utf8);
+ ads_destroy(&ads);
+ SAFE_FREE(srv_dn);
+ return WERR_SERVER_UNAVAILABLE;
+ }
+
+ ldap_memfree(srv_dn_utf8);
+ ldap_memfree(srv_cn_utf8);
+
+ asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn_0,
+ printer->info_2->sharename, srv_dn);
+
+ SAFE_FREE(srv_dn);
+ SAFE_FREE(srv_cn_0);
+
+ /* publish it */
+ ads_rc = ads_add_printer_entry(ads, prt_dn, ctx, &mods);
+ if (LDAP_ALREADY_EXISTS == ads_rc.err.rc)
+ ads_rc = ads_mod_printer_entry(ads, prt_dn, ctx,&mods);
+
+ /* retreive the guid and store it locally */
+ if (ADS_ERR_OK(ads_search_dn(ads, &res, prt_dn, attrs))) {
+ ads_memfree(ads, prt_dn);
+ ads_pull_guid(ads, res, &guid);
+ ads_msgfree(ads, res);
+ store_printer_guid(printer->info_2, guid);
+ win_rc = mod_a_printer(*printer, 2);
+ }
+
+ safe_free(prt_dn);
+ ads_destroy(&ads);
+
+ return WERR_OK;
+}
+
+WERROR unpublish_it(NT_PRINTER_INFO_LEVEL *printer)
+{
+ ADS_STATUS ads_rc;
+ ADS_STRUCT *ads;
+ void *res;
+ char *prt_dn = NULL;
+ WERROR win_rc;
+
+ printer->info_2->attributes ^= PRINTER_ATTRIBUTE_PUBLISHED;
+ win_rc = mod_a_printer(*printer, 2);
+ if (!W_ERROR_IS_OK(win_rc)) {
+ DEBUG(3, ("err %d saving data\n",
+ W_ERROR_V(win_rc)));
+ return win_rc;
+ }
+
+ ads = ads_init(NULL, NULL, NULL);
+ if (!ads) {
+ DEBUG(3, ("ads_init() failed\n"));
+ return WERR_SERVER_UNAVAILABLE;
+ }
+ setenv(KRB5_ENV_CCNAME, "MEMORY:prtpub_cache", 1);
+ SAFE_FREE(ads->auth.password);
+ ads->auth.password = secrets_fetch_machine_password(lp_workgroup(),
+ NULL, NULL);
+
+ /* ads_connect() will find the DC for us */
+ ads_rc = ads_connect(ads);
+ if (!ADS_ERR_OK(ads_rc)) {
+ DEBUG(3, ("ads_connect failed: %s\n", ads_errstr(ads_rc)));
+ ads_destroy(&ads);
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* remove the printer from the directory */
+ ads_rc = ads_find_printer_on_server(ads, &res,
+ printer->info_2->sharename, global_myname());
+ if (ADS_ERR_OK(ads_rc) && ads_count_replies(ads, res)) {
+ prt_dn = ads_get_dn(ads, res);
+ ads_msgfree(ads, res);
+ ads_rc = ads_del_dn(ads, prt_dn);
+ ads_memfree(ads, prt_dn);
+ }
+
+ ads_destroy(&ads);
+ return WERR_OK;
+}
+
+/****************************************************************************
+ * Publish a printer in the directory
+ *
+ * @param snum describing printer service
+ * @return WERROR indicating status of publishing
+ ***************************************************************************/
+
+WERROR nt_printer_publish(Printer_entry *print_hnd, int snum, int action)
+{
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ WERROR win_rc;
+
+ win_rc = get_a_printer(print_hnd, &printer, 2, lp_servicename(snum));
+ if (!W_ERROR_IS_OK(win_rc))
+ return win_rc;
+
+ switch(action) {
+ case SPOOL_DS_PUBLISH:
+ case SPOOL_DS_UPDATE:
+ win_rc = publish_it(printer);
+ break;
+ case SPOOL_DS_UNPUBLISH:
+ win_rc = unpublish_it(printer);
+ break;
+ default:
+ win_rc = WERR_NOT_SUPPORTED;
+ }
+
+
+ free_a_printer(&printer, 2);
+ return win_rc;
+}
+
+BOOL is_printer_published(Printer_entry *print_hnd, int snum,
+ struct uuid *guid)
+{
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ REGVAL_CTR *ctr;
+ REGISTRY_VALUE *guid_val;
+ WERROR win_rc;
+ int i;
+
+
+ win_rc = get_a_printer(print_hnd, &printer, 2, lp_servicename(snum));
+ if (!W_ERROR_IS_OK(win_rc))
+ return False;
+
+ if (!(printer->info_2->attributes & PRINTER_ATTRIBUTE_PUBLISHED))
+ return False;
+
+ if ((i = lookup_printerkey(&printer->info_2->data,
+ SPOOL_DSSPOOLER_KEY)) < 0)
+ return False;
+
+ if (!(ctr = &printer->info_2->data.keys[i].values)) {
+ return False;
+ }
+
+ if (!(guid_val = regval_ctr_getvalue(ctr, "objectGUID"))) {
+ return False;
+ }
+
+ if (regval_size(guid_val) == sizeof(struct uuid))
+ memcpy(guid, regval_data_p(guid_val), sizeof(struct uuid));
+
+ return True;
+}
+
+#else
+WERROR nt_printer_publish(Printer_entry *print_hnd, int snum, int action)
+{
+ return WERR_OK;
+}
+BOOL is_printer_published(Printer_entry *print_hnd, int snum,
+ struct uuid *guid)
+{
+ return False;
+}
+#endif
+/****************************************************************************
+ ***************************************************************************/
+
+WERROR delete_all_printer_data( NT_PRINTER_INFO_LEVEL_2 *p2, const char *key )
+{
+ NT_PRINTER_DATA *data;
+ int i;
+ int removed_keys = 0;
+ int empty_slot;
+
+ data = &p2->data;
+ empty_slot = data->num_keys;
+
+ if ( !key )
+ return WERR_INVALID_PARAM;
+
+ /* remove all keys */
+
+ if ( !strlen(key) ) {
+ for ( i=0; i<data->num_keys; i++ ) {
+ DEBUG(8,("delete_all_printer_data: Removed all Printer Data from key [%s]\n",
+ data->keys[i].name));
+
+ SAFE_FREE( data->keys[i].name );
+ regval_ctr_destroy( &data->keys[i].values );
+ }
+
+ DEBUG(8,("delete_all_printer_data: Removed all Printer Data from printer [%s]\n",
+ p2->printername ));
+
+ SAFE_FREE( data->keys );
+ ZERO_STRUCTP( data );
+
+ return WERR_OK;
+ }
+
+ /* remove a specific key (and all subkeys) */
+
+ for ( i=0; i<data->num_keys; i++ ) {
+ if ( StrnCaseCmp( data->keys[i].name, key, strlen(key)) == 0 ) {
+ DEBUG(8,("delete_all_printer_data: Removed all Printer Data from key [%s]\n",
+ data->keys[i].name));
+
+ SAFE_FREE( data->keys[i].name );
+ regval_ctr_destroy( &data->keys[i].values );
+
+ /* mark the slot as empty */
+
+ ZERO_STRUCTP( &data->keys[i] );
+ }
+ }
+
+ /* find the first empty slot */
+
+ for ( i=0; i<data->num_keys; i++ ) {
+ if ( !data->keys[i].name ) {
+ empty_slot = i;
+ removed_keys++;
+ break;
+ }
+ }
+
+ if ( i == data->num_keys )
+ /* nothing was removed */
+ return WERR_INVALID_PARAM;
+
+ /* move everything down */
+
+ for ( i=empty_slot+1; i<data->num_keys; i++ ) {
+ if ( data->keys[i].name ) {
+ memcpy( &data->keys[empty_slot], &data->keys[i], sizeof(NT_PRINTER_KEY) );
+ ZERO_STRUCTP( &data->keys[i] );
+ empty_slot++;
+ removed_keys++;
+ }
+ }
+
+ /* update count */
+
+ data->num_keys -= removed_keys;
+
+ /* sanity check to see if anything is left */
+
+ if ( !data->num_keys ) {
+ DEBUG(8,("delete_all_printer_data: No keys left for printer [%s]\n", p2->printername ));
+
+ SAFE_FREE( data->keys );
+ ZERO_STRUCTP( data );
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ ***************************************************************************/
+
+WERROR delete_printer_data( NT_PRINTER_INFO_LEVEL_2 *p2, const char *key, const char *value )
+{
+ WERROR result = WERR_OK;
+ int key_index;
+
+ /* we must have names on non-zero length */
+
+ if ( !key || !*key|| !value || !*value )
+ return WERR_INVALID_NAME;
+
+ /* find the printer key first */
+
+ key_index = lookup_printerkey( &p2->data, key );
+ if ( key_index == -1 )
+ return WERR_OK;
+
+ /* make sure the value exists so we can return the correct error code */
+
+ if ( !regval_ctr_getvalue( &p2->data.keys[key_index].values, value ) )
+ return WERR_BADFILE;
+
+ regval_ctr_delvalue( &p2->data.keys[key_index].values, value );
+
+ DEBUG(8,("delete_printer_data: Removed key => [%s], value => [%s]\n",
+ key, value ));
+
+ return result;
+}
+
+/****************************************************************************
+ ***************************************************************************/
+
+WERROR add_printer_data( NT_PRINTER_INFO_LEVEL_2 *p2, const char *key, const char *value,
+ uint32 type, uint8 *data, int real_len )
+{
+ WERROR result = WERR_OK;
+ int key_index;
+
+ /* we must have names on non-zero length */
+
+ if ( !key || !*key|| !value || !*value )
+ return WERR_INVALID_NAME;
+
+ /* find the printer key first */
+
+ key_index = lookup_printerkey( &p2->data, key );
+ if ( key_index == -1 )
+ key_index = add_new_printer_key( &p2->data, key );
+
+ if ( key_index == -1 )
+ return WERR_NOMEM;
+
+ regval_ctr_addvalue( &p2->data.keys[key_index].values, value,
+ type, (const char *)data, real_len );
+
+ DEBUG(8,("add_printer_data: Added key => [%s], value => [%s], type=> [%d], size => [%d]\n",
+ key, value, type, real_len ));
+
+ return result;
+}
+
+/****************************************************************************
+ ***************************************************************************/
+
+REGISTRY_VALUE* get_printer_data( NT_PRINTER_INFO_LEVEL_2 *p2, const char *key, const char *value )
+{
+ int key_index;
+
+ if ( (key_index = lookup_printerkey( &p2->data, key )) == -1 )
+ return NULL;
+
+ DEBUG(8,("get_printer_data: Attempting to lookup key => [%s], value => [%s]\n",
+ key, value ));
+
+ return regval_ctr_getvalue( &p2->data.keys[key_index].values, value );
+}
+
+/****************************************************************************
+ Unpack a list of registry values frem the TDB
+ ***************************************************************************/
+
+static int unpack_values(NT_PRINTER_DATA *printer_data, char *buf, int buflen)
+{
+ int len = 0;
+ uint32 type;
+ pstring string, valuename, keyname;
+ char *str;
+ int size;
+ uint8 *data_p;
+ REGISTRY_VALUE *regval_p;
+ int key_index;
+
+ /* add the "PrinterDriverData" key first for performance reasons */
+
+ add_new_printer_key( printer_data, SPOOL_PRINTERDATA_KEY );
+
+ /* loop and unpack the rest of the registry values */
+
+ while ( True ) {
+
+ /* check to see if there are any more registry values */
+
+ len += tdb_unpack(buf+len, buflen-len, "p", &regval_p);
+ if ( !regval_p )
+ break;
+
+ /* unpack the next regval */
+
+ len += tdb_unpack(buf+len, buflen-len, "fdB",
+ string,
+ &type,
+ &size,
+ &data_p);
+
+ /*
+ * break of the keyname from the value name.
+ * Should only be one '\' in the string returned.
+ */
+
+ str = strrchr( string, '\\');
+
+ /* Put in "PrinterDriverData" is no key specified */
+
+ if ( !str ) {
+ pstrcpy( keyname, SPOOL_PRINTERDATA_KEY );
+ pstrcpy( valuename, string );
+ }
+ else {
+ *str = '\0';
+ pstrcpy( keyname, string );
+ pstrcpy( valuename, str+1 );
+ }
+
+ /* see if we need a new key */
+
+ if ( (key_index=lookup_printerkey( printer_data, keyname )) == -1 )
+ key_index = add_new_printer_key( printer_data, keyname );
+
+ if ( key_index == -1 ) {
+ DEBUG(0,("unpack_values: Failed to allocate a new key [%s]!\n",
+ keyname));
+ break;
+ }
+
+ /* add the new value */
+
+ regval_ctr_addvalue( &printer_data->keys[key_index].values, valuename, type, (const char *)data_p, size );
+
+ SAFE_FREE(data_p); /* 'B' option to tdbpack does a malloc() */
+
+ DEBUG(8,("specific: [%s:%s], len: %d\n", keyname, valuename, size));
+ }
+
+ return len;
+}
+
+/****************************************************************************
+ ***************************************************************************/
+
+static void map_to_os2_driver(fstring drivername)
+{
+ static BOOL initialised=False;
+ static fstring last_from,last_to;
+ char *mapfile = lp_os2_driver_map();
+ char **lines = NULL;
+ int numlines = 0;
+ int i;
+
+ if (!strlen(drivername))
+ return;
+
+ if (!*mapfile)
+ return;
+
+ if (!initialised) {
+ *last_from = *last_to = 0;
+ initialised = True;
+ }
+
+ if (strequal(drivername,last_from)) {
+ DEBUG(3,("Mapped Windows driver %s to OS/2 driver %s\n",drivername,last_to));
+ fstrcpy(drivername,last_to);
+ return;
+ }
+
+ lines = file_lines_load(mapfile, &numlines);
+ if (numlines == 0) {
+ DEBUG(0,("No entries in OS/2 driver map %s\n",mapfile));
+ return;
+ }
+
+ DEBUG(4,("Scanning OS/2 driver map %s\n",mapfile));
+
+ for( i = 0; i < numlines; i++) {
+ char *nt_name = lines[i];
+ char *os2_name = strchr(nt_name,'=');
+
+ if (!os2_name)
+ continue;
+
+ *os2_name++ = 0;
+
+ while (isspace(*nt_name))
+ nt_name++;
+
+ if (!*nt_name || strchr("#;",*nt_name))
+ continue;
+
+ {
+ int l = strlen(nt_name);
+ while (l && isspace(nt_name[l-1])) {
+ nt_name[l-1] = 0;
+ l--;
+ }
+ }
+
+ while (isspace(*os2_name))
+ os2_name++;
+
+ {
+ int l = strlen(os2_name);
+ while (l && isspace(os2_name[l-1])) {
+ os2_name[l-1] = 0;
+ l--;
+ }
+ }
+
+ if (strequal(nt_name,drivername)) {
+ DEBUG(3,("Mapped windows driver %s to os2 driver%s\n",drivername,os2_name));
+ fstrcpy(last_from,drivername);
+ fstrcpy(last_to,os2_name);
+ fstrcpy(drivername,os2_name);
+ file_lines_free(lines);
+ return;
+ }
+ }
+
+ file_lines_free(lines);
+}
+
+/****************************************************************************
+ Get a default printer info 2 struct.
+****************************************************************************/
+static WERROR get_a_printer_2_default(NT_PRINTER_INFO_LEVEL_2 **info_ptr, const char *sharename)
+{
+ int snum;
+ NT_PRINTER_INFO_LEVEL_2 info;
+
+ ZERO_STRUCT(info);
+
+ snum = lp_servicenumber(sharename);
+
+ slprintf(info.servername, sizeof(info.servername)-1, "\\\\%s", get_called_name());
+ slprintf(info.printername, sizeof(info.printername)-1, "\\\\%s\\%s",
+ get_called_name(), sharename);
+ fstrcpy(info.sharename, sharename);
+ fstrcpy(info.portname, SAMBA_PRINTER_PORT_NAME);
+
+ /* by setting the driver name to an empty string, a local NT admin
+ can now run the **local** APW to install a local printer driver
+ for a Samba shared printer in 2.2. Without this, drivers **must** be
+ installed on the Samba server for NT clients --jerry */
+#if 0 /* JERRY --do not uncomment-- */
+ if (!*info.drivername)
+ fstrcpy(info.drivername, "NO DRIVER AVAILABLE FOR THIS PRINTER");
+#endif
+
+
+ DEBUG(10,("get_a_printer_2_default: driver name set to [%s]\n", info.drivername));
+
+ pstrcpy(info.comment, "");
+ fstrcpy(info.printprocessor, "winprint");
+ fstrcpy(info.datatype, "RAW");
+
+ info.attributes = PRINTER_ATTRIBUTE_SAMBA;
+
+ info.starttime = 0; /* Minutes since 12:00am GMT */
+ info.untiltime = 0; /* Minutes since 12:00am GMT */
+ info.priority = 1;
+ info.default_priority = 1;
+ info.setuptime = (uint32)time(NULL);
+
+ /*
+ * I changed this as I think it is better to have a generic
+ * DEVMODE than to crash Win2k explorer.exe --jerry
+ * See the HP Deskjet 990c Win2k drivers for an example.
+ *
+ * However the default devmode appears to cause problems
+ * with the HP CLJ 8500 PCL driver. Hence the addition of
+ * the "default devmode" parameter --jerry 22/01/2002
+ */
+
+ if (lp_default_devmode(snum)) {
+ if ((info.devmode = construct_nt_devicemode(info.printername)) == NULL)
+ goto fail;
+ }
+ else {
+ info.devmode = NULL;
+ }
+
+ /* This will get the current RPC talloc context, but we should be
+ passing this as a parameter... fixme... JRA ! */
+
+ if (!nt_printing_getsec(get_talloc_ctx(), sharename, &info.secdesc_buf))
+ goto fail;
+
+ *info_ptr = (NT_PRINTER_INFO_LEVEL_2 *)memdup(&info, sizeof(info));
+ if (! *info_ptr) {
+ DEBUG(0,("get_a_printer_2_default: malloc fail.\n"));
+ goto fail;
+ }
+
+ return WERR_OK;
+
+ fail:
+ if (info.devmode)
+ free_nt_devicemode(&info.devmode);
+ return WERR_ACCESS_DENIED;
+}
+
+/****************************************************************************
+****************************************************************************/
+static WERROR get_a_printer_2(NT_PRINTER_INFO_LEVEL_2 **info_ptr, const char *sharename)
+{
+ pstring key;
+ NT_PRINTER_INFO_LEVEL_2 info;
+ int len = 0;
+ TDB_DATA kbuf, dbuf;
+ fstring printername;
+ char adevice[MAXDEVICENAME];
+
+ ZERO_STRUCT(info);
+
+ slprintf(key, sizeof(key)-1, "%s%s", PRINTERS_PREFIX, sharename);
+
+ kbuf.dptr = key;
+ kbuf.dsize = strlen(key)+1;
+
+ dbuf = tdb_fetch(tdb_printers, kbuf);
+ if (!dbuf.dptr)
+ return get_a_printer_2_default(info_ptr, sharename);
+
+ len += tdb_unpack(dbuf.dptr+len, dbuf.dsize-len, "dddddddddddfffffPfffff",
+ &info.attributes,
+ &info.priority,
+ &info.default_priority,
+ &info.starttime,
+ &info.untiltime,
+ &info.status,
+ &info.cjobs,
+ &info.averageppm,
+ &info.changeid,
+ &info.c_setprinter,
+ &info.setuptime,
+ info.servername,
+ info.printername,
+ info.sharename,
+ info.portname,
+ info.drivername,
+ info.comment,
+ info.location,
+ info.sepfile,
+ info.printprocessor,
+ info.datatype,
+ info.parameters);
+
+ /* Samba has to have shared raw drivers. */
+ info.attributes = PRINTER_ATTRIBUTE_SAMBA;
+
+ /* Restore the stripped strings. */
+ slprintf(info.servername, sizeof(info.servername)-1, "\\\\%s", get_called_name());
+ slprintf(printername, sizeof(printername)-1, "\\\\%s\\%s", get_called_name(),
+ info.printername);
+ fstrcpy(info.printername, printername);
+
+ len += unpack_devicemode(&info.devmode,dbuf.dptr+len, dbuf.dsize-len);
+
+ /*
+ * Some client drivers freak out if there is a NULL devmode
+ * (probably the driver is not checking before accessing
+ * the devmode pointer) --jerry
+ *
+ * See comments in get_a_printer_2_default()
+ */
+
+ if (lp_default_devmode(lp_servicenumber(sharename)) && !info.devmode) {
+ DEBUG(8,("get_a_printer_2: Constructing a default device mode for [%s]\n",
+ printername));
+ info.devmode = construct_nt_devicemode(printername);
+ }
+
+ safe_strcpy(adevice, info.printername, sizeof(adevice)-1);
+ if (info.devmode) {
+ fstrcpy(info.devmode->devicename, adevice);
+ }
+
+ len += unpack_values( &info.data, dbuf.dptr+len, dbuf.dsize-len );
+
+ /* This will get the current RPC talloc context, but we should be
+ passing this as a parameter... fixme... JRA ! */
+
+ nt_printing_getsec(get_talloc_ctx(), sharename, &info.secdesc_buf);
+
+ /* Fix for OS/2 drivers. */
+
+ if (get_remote_arch() == RA_OS2)
+ map_to_os2_driver(info.drivername);
+
+ SAFE_FREE(dbuf.dptr);
+ *info_ptr=memdup(&info, sizeof(info));
+
+ DEBUG(9,("Unpacked printer [%s] name [%s] running driver [%s]\n",
+ sharename, info.printername, info.drivername));
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ Debugging function, dump at level 6 the struct in the logs.
+****************************************************************************/
+static uint32 dump_a_printer(NT_PRINTER_INFO_LEVEL printer, uint32 level)
+{
+ uint32 result;
+ NT_PRINTER_INFO_LEVEL_2 *info2;
+
+ DEBUG(106,("Dumping printer at level [%d]\n", level));
+
+ switch (level) {
+ case 2:
+ {
+ if (printer.info_2 == NULL)
+ result=5;
+ else
+ {
+ info2=printer.info_2;
+
+ DEBUGADD(106,("attributes:[%d]\n", info2->attributes));
+ DEBUGADD(106,("priority:[%d]\n", info2->priority));
+ DEBUGADD(106,("default_priority:[%d]\n", info2->default_priority));
+ DEBUGADD(106,("starttime:[%d]\n", info2->starttime));
+ DEBUGADD(106,("untiltime:[%d]\n", info2->untiltime));
+ DEBUGADD(106,("status:[%d]\n", info2->status));
+ DEBUGADD(106,("cjobs:[%d]\n", info2->cjobs));
+ DEBUGADD(106,("averageppm:[%d]\n", info2->averageppm));
+ DEBUGADD(106,("changeid:[%d]\n", info2->changeid));
+ DEBUGADD(106,("c_setprinter:[%d]\n", info2->c_setprinter));
+ DEBUGADD(106,("setuptime:[%d]\n", info2->setuptime));
+
+ DEBUGADD(106,("servername:[%s]\n", info2->servername));
+ DEBUGADD(106,("printername:[%s]\n", info2->printername));
+ DEBUGADD(106,("sharename:[%s]\n", info2->sharename));
+ DEBUGADD(106,("portname:[%s]\n", info2->portname));
+ DEBUGADD(106,("drivername:[%s]\n", info2->drivername));
+ DEBUGADD(106,("comment:[%s]\n", info2->comment));
+ DEBUGADD(106,("location:[%s]\n", info2->location));
+ DEBUGADD(106,("sepfile:[%s]\n", info2->sepfile));
+ DEBUGADD(106,("printprocessor:[%s]\n", info2->printprocessor));
+ DEBUGADD(106,("datatype:[%s]\n", info2->datatype));
+ DEBUGADD(106,("parameters:[%s]\n", info2->parameters));
+ result=0;
+ }
+ break;
+ }
+ default:
+ DEBUGADD(106,("dump_a_printer: Level %u not implemented\n", (unsigned int)level ));
+ result=1;
+ break;
+ }
+
+ return result;
+}
+
+/****************************************************************************
+ Update the changeid time.
+ This is SO NASTY as some drivers need this to change, others need it
+ static. This value will change every second, and I must hope that this
+ is enough..... DON'T CHANGE THIS CODE WITHOUT A TEST MATRIX THE SIZE OF
+ UTAH ! JRA.
+****************************************************************************/
+
+static uint32 rev_changeid(void)
+{
+ struct timeval tv;
+
+ get_process_uptime(&tv);
+
+#if 1 /* JERRY */
+ /* Return changeid as msec since spooler restart */
+ return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+#else
+ /*
+ * This setting seems to work well but is too untested
+ * to replace the above calculation. Left in for experiementation
+ * of the reader --jerry (Tue Mar 12 09:15:05 CST 2002)
+ */
+ return tv.tv_sec * 10 + tv.tv_usec / 100000;
+#endif
+}
+
+/*
+ * The function below are the high level ones.
+ * only those ones must be called from the spoolss code.
+ * JFM.
+ */
+
+/****************************************************************************
+ Modify a printer. This is called from SETPRINTERDATA/DELETEPRINTERDATA.
+****************************************************************************/
+
+WERROR mod_a_printer(NT_PRINTER_INFO_LEVEL printer, uint32 level)
+{
+ WERROR result;
+
+ dump_a_printer(printer, level);
+
+ /*
+ * invalidate cache for all open handles to this printer.
+ * cache for a given handle will be updated on the next
+ * get_a_printer()
+ */
+
+ invalidate_printer_hnd_cache( printer.info_2->sharename );
+
+ switch (level) {
+ case 2:
+ {
+ /*
+ * Update the changestamp. Emperical tests show that the
+ * ChangeID is always updated,but c_setprinter is
+ * global spooler variable (not per printer).
+ */
+
+ /* ChangeID **must** be increasing over the lifetime
+ of client's spoolss service in order for the
+ client's cache to show updates */
+
+ printer.info_2->changeid = rev_changeid();
+
+ /*
+ * Because one day someone will ask:
+ * NT->NT An admin connection to a remote
+ * printer show changes imeediately in
+ * the properities dialog
+ *
+ * A non-admin connection will only show the
+ * changes after viewing the properites page
+ * 2 times. Seems to be related to a
+ * race condition in the client between the spooler
+ * updating the local cache and the Explorer.exe GUI
+ * actually displaying the properties.
+ *
+ * This is fixed in Win2k. admin/non-admin
+ * connections both display changes immediately.
+ *
+ * 14/12/01 --jerry
+ */
+
+ result=update_a_printer_2(printer.info_2);
+
+ break;
+ }
+ default:
+ result=WERR_UNKNOWN_LEVEL;
+ break;
+ }
+
+ return result;
+}
+
+/****************************************************************************
+ Initialize printer devmode & data with previously saved driver init values.
+****************************************************************************/
+
+static BOOL set_driver_init_2( NT_PRINTER_INFO_LEVEL_2 *info_ptr )
+{
+ int len = 0;
+ pstring key;
+ TDB_DATA kbuf, dbuf;
+ NT_PRINTER_INFO_LEVEL_2 info;
+
+
+ ZERO_STRUCT(info);
+
+ /*
+ * Delete any printer data 'values' already set. When called for driver
+ * replace, there will generally be some, but during an add printer, there
+ * should not be any (if there are delete them).
+ */
+
+ delete_all_printer_data( info_ptr, "" );
+
+ slprintf(key, sizeof(key)-1, "%s%s", DRIVER_INIT_PREFIX, info_ptr->drivername);
+
+ kbuf.dptr = key;
+ kbuf.dsize = strlen(key)+1;
+
+ dbuf = tdb_fetch(tdb_drivers, kbuf);
+ if (!dbuf.dptr) {
+ /*
+ * When changing to a driver that has no init info in the tdb, remove
+ * the previous drivers init info and leave the new on blank.
+ */
+ free_nt_devicemode(&info_ptr->devmode);
+ return False;
+ }
+
+ /*
+ * Get the saved DEVMODE..
+ */
+
+ len += unpack_devicemode(&info.devmode,dbuf.dptr+len, dbuf.dsize-len);
+
+ /*
+ * The saved DEVMODE contains the devicename from the printer used during
+ * the initialization save. Change it to reflect the new printer.
+ */
+
+ if ( info.devmode ) {
+ ZERO_STRUCT(info.devmode->devicename);
+ fstrcpy(info.devmode->devicename, info_ptr->printername);
+ }
+
+ /*
+ * NT/2k does not change out the entire DeviceMode of a printer
+ * when changing the driver. Only the driverextra, private, &
+ * driverversion fields. --jerry (Thu Mar 14 08:58:43 CST 2002)
+ *
+ * Later examination revealed that Windows NT/2k does reset the
+ * the printer's device mode, bit **only** when you change a
+ * property of the device mode such as the page orientation.
+ * --jerry
+ */
+
+
+ /* Bind the saved DEVMODE to the new the printer */
+
+ free_nt_devicemode(&info_ptr->devmode);
+ info_ptr->devmode = info.devmode;
+
+ DEBUG(10,("set_driver_init_2: Set printer [%s] init %s DEVMODE for driver [%s]\n",
+ info_ptr->printername, info_ptr->devmode?"VALID":"NULL", info_ptr->drivername));
+
+ /* Add the printer data 'values' to the new printer */
+
+ len += unpack_values( &info_ptr->data, dbuf.dptr+len, dbuf.dsize-len );
+
+
+ SAFE_FREE(dbuf.dptr);
+
+ return True;
+}
+
+/****************************************************************************
+ Initialize printer devmode & data with previously saved driver init values.
+ When a printer is created using AddPrinter, the drivername bound to the
+ printer is used to lookup previously saved driver initialization info, which
+ is bound to the new printer.
+****************************************************************************/
+
+BOOL set_driver_init(NT_PRINTER_INFO_LEVEL *printer, uint32 level)
+{
+ BOOL result = False;
+
+ switch (level) {
+ case 2:
+ result = set_driver_init_2(printer->info_2);
+ break;
+
+ default:
+ DEBUG(0,("set_driver_init: Programmer's error! Unknown driver_init level [%d]\n",
+ level));
+ break;
+ }
+
+ return result;
+}
+
+/****************************************************************************
+ Delete driver init data stored for a specified driver
+****************************************************************************/
+
+BOOL del_driver_init(char *drivername)
+{
+ pstring key;
+ TDB_DATA kbuf;
+
+ if (!drivername || !*drivername) {
+ DEBUG(3,("del_driver_init: No drivername specified!\n"));
+ return False;
+ }
+
+ slprintf(key, sizeof(key)-1, "%s%s", DRIVER_INIT_PREFIX, drivername);
+
+ kbuf.dptr = key;
+ kbuf.dsize = strlen(key)+1;
+
+ DEBUG(6,("del_driver_init: Removing driver init data for [%s]\n", drivername));
+
+ return (tdb_delete(tdb_drivers, kbuf) == 0);
+}
+
+/****************************************************************************
+ Pack up the DEVMODE and values for a printer into a 'driver init' entry
+ in the tdb. Note: this is different from the driver entry and the printer
+ entry. There should be a single driver init entry for each driver regardless
+ of whether it was installed from NT or 2K. Technically, they should be
+ different, but they work out to the same struct.
+****************************************************************************/
+
+static uint32 update_driver_init_2(NT_PRINTER_INFO_LEVEL_2 *info)
+{
+ pstring key;
+ char *buf;
+ int buflen, len, ret;
+ TDB_DATA kbuf, dbuf;
+
+ buf = NULL;
+ buflen = 0;
+
+ again:
+ len = 0;
+ len += pack_devicemode(info->devmode, buf+len, buflen-len);
+
+ len += pack_values( &info->data, buf+len, buflen-len );
+
+ if (buflen != len) {
+ char *tb;
+
+ tb = (char *)Realloc(buf, len);
+ if (!tb) {
+ DEBUG(0, ("update_driver_init_2: failed to enlarge buffer!\n"));
+ ret = -1;
+ goto done;
+ }
+ else
+ buf = tb;
+ buflen = len;
+ goto again;
+ }
+
+ slprintf(key, sizeof(key)-1, "%s%s", DRIVER_INIT_PREFIX, info->drivername);
+
+ kbuf.dptr = key;
+ kbuf.dsize = strlen(key)+1;
+ dbuf.dptr = buf;
+ dbuf.dsize = len;
+
+ ret = tdb_store(tdb_drivers, kbuf, dbuf, TDB_REPLACE);
+
+done:
+ if (ret == -1)
+ DEBUG(8, ("update_driver_init_2: error updating printer init to tdb on disk\n"));
+
+ SAFE_FREE(buf);
+
+ DEBUG(10,("update_driver_init_2: Saved printer [%s] init DEVMODE & values for driver [%s]\n",
+ info->sharename, info->drivername));
+
+ return ret;
+}
+
+/****************************************************************************
+ Update (i.e. save) the driver init info (DEVMODE and values) for a printer
+****************************************************************************/
+
+uint32 update_driver_init(NT_PRINTER_INFO_LEVEL printer, uint32 level)
+{
+ uint32 result;
+
+ dump_a_printer(printer, level);
+
+ switch (level) {
+ case 2:
+ result = update_driver_init_2(printer.info_2);
+ break;
+ default:
+ result = 1;
+ break;
+ }
+
+ return result;
+}
+
+/****************************************************************************
+ Convert the printer data value, a REG_BINARY array, into an initialization
+ DEVMODE. Note: the array must be parsed as if it was a DEVMODE in an rpc...
+ got to keep the endians happy :).
+****************************************************************************/
+
+static BOOL convert_driver_init( TALLOC_CTX *ctx, NT_DEVICEMODE *nt_devmode, uint8 *data, uint32 data_len )
+{
+ BOOL result = False;
+ prs_struct ps;
+ DEVICEMODE devmode;
+
+ ZERO_STRUCT(devmode);
+
+ prs_init(&ps, 0, ctx, UNMARSHALL);
+ ps.data_p = (char *)data;
+ ps.buffer_size = data_len;
+
+ if (spoolss_io_devmode("phantom DEVMODE", &ps, 0, &devmode))
+ result = convert_devicemode("", &devmode, &nt_devmode);
+ else
+ DEBUG(10,("convert_driver_init: error parsing DEVMODE\n"));
+
+ return result;
+}
+
+/****************************************************************************
+ Set the DRIVER_INIT info in the tdb. Requires Win32 client code that:
+
+ 1. Use the driver's config DLL to this UNC printername and:
+ a. Call DrvPrintEvent with PRINTER_EVENT_INITIALIZE
+ b. Call DrvConvertDevMode with CDM_DRIVER_DEFAULT to get default DEVMODE
+ 2. Call SetPrinterData with the 'magic' key and the DEVMODE as data.
+
+ The last step triggers saving the "driver initialization" information for
+ this printer into the tdb. Later, new printers that use this driver will
+ have this initialization information bound to them. This simulates the
+ driver initialization, as if it had run on the Samba server (as it would
+ have done on NT).
+
+ The Win32 client side code requirement sucks! But until we can run arbitrary
+ Win32 printer driver code on any Unix that Samba runs on, we are stuck with it.
+
+ It would have been easier to use SetPrinter because all the UNMARSHALLING of
+ the DEVMODE is done there, but 2K/XP clients do not set the DEVMODE... think
+ about it and you will realize why. JRR 010720
+****************************************************************************/
+
+static WERROR save_driver_init_2(NT_PRINTER_INFO_LEVEL *printer, uint8 *data, uint32 data_len )
+{
+ WERROR status = WERR_OK;
+ TALLOC_CTX *ctx = NULL;
+ NT_DEVICEMODE *nt_devmode = NULL;
+ NT_DEVICEMODE *tmp_devmode = printer->info_2->devmode;
+
+ /*
+ * When the DEVMODE is already set on the printer, don't try to unpack it.
+ */
+ DEBUG(8,("save_driver_init_2: Enter...\n"));
+
+ if ( !printer->info_2->devmode && data_len ) {
+ /*
+ * Set devmode on printer info, so entire printer initialization can be
+ * saved to tdb.
+ */
+
+ if ((ctx = talloc_init("save_driver_init_2")) == NULL)
+ return WERR_NOMEM;
+
+ if ((nt_devmode = (NT_DEVICEMODE*)malloc(sizeof(NT_DEVICEMODE))) == NULL) {
+ status = WERR_NOMEM;
+ goto done;
+ }
+
+ ZERO_STRUCTP(nt_devmode);
+
+ /*
+ * The DEVMODE is held in the 'data' component of the param in raw binary.
+ * Convert it to to a devmode structure
+ */
+ if ( !convert_driver_init( ctx, nt_devmode, data, data_len )) {
+ DEBUG(10,("save_driver_init_2: error converting DEVMODE\n"));
+ status = WERR_INVALID_PARAM;
+ goto done;
+ }
+
+ printer->info_2->devmode = nt_devmode;
+ }
+
+ /*
+ * Pack up and add (or update) the DEVMODE and any current printer data to
+ * a 'driver init' element in the tdb
+ *
+ */
+
+ if ( update_driver_init(*printer, 2) != 0 ) {
+ DEBUG(10,("save_driver_init_2: error updating DEVMODE\n"));
+ status = WERR_NOMEM;
+ goto done;
+ }
+
+ /*
+ * If driver initialization info was successfully saved, set the current
+ * printer to match it. This allows initialization of the current printer
+ * as well as the driver.
+ */
+ status = mod_a_printer(*printer, 2);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(10,("save_driver_init_2: error setting DEVMODE on printer [%s]\n",
+ printer->info_2->printername));
+ }
+
+ done:
+ talloc_destroy(ctx);
+ free_nt_devicemode( &nt_devmode );
+
+ printer->info_2->devmode = tmp_devmode;
+
+ return status;
+}
+
+/****************************************************************************
+ Update the driver init info (DEVMODE and specifics) for a printer
+****************************************************************************/
+
+WERROR save_driver_init(NT_PRINTER_INFO_LEVEL *printer, uint32 level, uint8 *data, uint32 data_len)
+{
+ WERROR status = WERR_OK;
+
+ switch (level) {
+ case 2:
+ status = save_driver_init_2( printer, data, data_len );
+ break;
+ default:
+ status = WERR_UNKNOWN_LEVEL;
+ break;
+ }
+
+ return status;
+}
+
+/****************************************************************************
+ Deep copy a NT_PRINTER_DATA
+****************************************************************************/
+
+static NTSTATUS copy_printer_data( NT_PRINTER_DATA *dst, NT_PRINTER_DATA *src )
+{
+ int i, j, num_vals, new_key_index;
+ REGVAL_CTR *src_key, *dst_key;
+
+ if ( !dst || !src )
+ return NT_STATUS_NO_MEMORY;
+
+ for ( i=0; i<src->num_keys; i++ ) {
+
+ /* create a new instance of the printerkey in the destination
+ printer_data object */
+
+ new_key_index = add_new_printer_key( dst, src->keys[i].name );
+ dst_key = &dst->keys[new_key_index].values;
+
+ src_key = &src->keys[i].values;
+ num_vals = regval_ctr_numvals( src_key );
+
+ /* dup the printer entire printer key */
+
+ for ( j=0; j<num_vals; j++ ) {
+ regval_ctr_copyvalue( dst_key, regval_ctr_specific_value(src_key, j) );
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Deep copy a NT_PRINTER_INFO_LEVEL_2 structure using malloc()'d memeory
+ Caller must free.
+****************************************************************************/
+
+NT_PRINTER_INFO_LEVEL_2* dup_printer_2( TALLOC_CTX *ctx, NT_PRINTER_INFO_LEVEL_2 *printer )
+{
+ NT_PRINTER_INFO_LEVEL_2 *copy;
+
+ if ( !printer )
+ return NULL;
+
+ if ( !(copy = (NT_PRINTER_INFO_LEVEL_2 *)malloc(sizeof(NT_PRINTER_INFO_LEVEL_2))) )
+ return NULL;
+
+ memcpy( copy, printer, sizeof(NT_PRINTER_INFO_LEVEL_2) );
+
+ /* malloc()'d members copied here */
+
+ copy->devmode = dup_nt_devicemode( printer->devmode );
+
+ ZERO_STRUCT( copy->data );
+ copy_printer_data( &copy->data, &printer->data );
+
+ /* this is talloc()'d; very ugly that we have a structure that
+ is half malloc()'d and half talloc()'d but that is the way
+ that the PRINTER_INFO stuff is written right now. --jerry */
+
+ copy->secdesc_buf = dup_sec_desc_buf( ctx, printer->secdesc_buf );
+
+ return copy;
+}
+
+/****************************************************************************
+ Get a NT_PRINTER_INFO_LEVEL struct. It returns malloced memory.
+
+ Previously the code had a memory allocation problem because it always
+ used the TALLOC_CTX from the Printer_entry*. This context lasts
+ as a long as the original handle is open. So if the client made a lot
+ of getprinter[data]() calls, the memory usage would climb. Now we use
+ a short lived TALLOC_CTX for printer_info_2 objects returned. We
+ still use the Printer_entry->ctx for maintaining the cache copy though
+ since that object must live as long as the handle by definition.
+ --jerry
+
+****************************************************************************/
+
+WERROR get_a_printer( Printer_entry *print_hnd, NT_PRINTER_INFO_LEVEL **pp_printer, uint32 level,
+ const char *sharename)
+{
+ WERROR result;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ *pp_printer = NULL;
+
+ DEBUG(10,("get_a_printer: [%s] level %u\n", sharename, (unsigned int)level));
+
+ switch (level) {
+ case 2:
+ if ((printer = (NT_PRINTER_INFO_LEVEL *)malloc(sizeof(NT_PRINTER_INFO_LEVEL))) == NULL) {
+ DEBUG(0,("get_a_printer: malloc fail.\n"));
+ return WERR_NOMEM;
+ }
+ ZERO_STRUCTP(printer);
+
+ /*
+ * check for cache first. A Printer handle cannot changed
+ * to another printer object so we only check that the printer
+ * is actually for a printer and that the printer_info pointer
+ * is valid
+ */
+ if ( print_hnd
+ && (print_hnd->printer_type==PRINTER_HANDLE_IS_PRINTER)
+ && print_hnd->printer_info )
+ {
+ /* get_talloc_ctx() works here because we need a short
+ lived talloc context */
+
+ if ( !(printer->info_2 = dup_printer_2(get_talloc_ctx(), print_hnd->printer_info->info_2)) )
+ {
+ DEBUG(0,("get_a_printer: unable to copy cached printer info!\n"));
+
+ SAFE_FREE(printer);
+ return WERR_NOMEM;
+ }
+
+ DEBUG(10,("get_a_printer: using cached copy of printer_info_2\n"));
+
+ *pp_printer = printer;
+ result = WERR_OK;
+
+ break;
+ }
+
+ /* no cache for this handle; see if we can match one from another handle.
+ Make sure to use a short lived talloc ctx */
+
+ if ( print_hnd )
+ result = find_printer_in_print_hnd_cache(get_talloc_ctx(), &printer->info_2, sharename);
+
+ /* fail to disk if we don't have it with any open handle */
+
+ if ( !print_hnd || !W_ERROR_IS_OK(result) )
+ result = get_a_printer_2(&printer->info_2, sharename);
+
+ /* we have a new printer now. Save it with this handle */
+
+ if ( W_ERROR_IS_OK(result) ) {
+ dump_a_printer(*printer, level);
+
+ /* save a copy in cache */
+ if ( print_hnd && (print_hnd->printer_type==PRINTER_HANDLE_IS_PRINTER)) {
+ if ( !print_hnd->printer_info )
+ print_hnd->printer_info = (NT_PRINTER_INFO_LEVEL *)malloc(sizeof(NT_PRINTER_INFO_LEVEL));
+
+ if ( print_hnd->printer_info ) {
+ /* make sure to use the handle's talloc ctx here since
+ the printer_2 object must last until the handle is closed */
+
+ print_hnd->printer_info->info_2 = dup_printer_2(print_hnd->ctx, printer->info_2);
+
+ /* don't fail the lookup just because the cache update failed */
+ if ( !print_hnd->printer_info->info_2 )
+ DEBUG(0,("get_a_printer: unable to copy new printer info!\n"));
+ }
+ }
+ *pp_printer = printer;
+ }
+ else
+ SAFE_FREE(printer);
+
+ break;
+
+ default:
+ result=WERR_UNKNOWN_LEVEL;
+ break;
+ }
+
+ DEBUG(10,("get_a_printer: [%s] level %u returning %s\n", sharename, (unsigned int)level, dos_errstr(result)));
+
+ return result;
+}
+
+/****************************************************************************
+ Deletes a NT_PRINTER_INFO_LEVEL struct.
+****************************************************************************/
+
+uint32 free_a_printer(NT_PRINTER_INFO_LEVEL **pp_printer, uint32 level)
+{
+ uint32 result;
+ NT_PRINTER_INFO_LEVEL *printer = *pp_printer;
+
+ DEBUG(104,("freeing a printer at level [%d]\n", level));
+
+ if (printer == NULL)
+ return 0;
+
+ switch (level) {
+ case 2:
+ if (printer->info_2 != NULL) {
+ free_nt_printer_info_level_2(&printer->info_2);
+ result=0;
+ } else
+ result=4;
+ break;
+
+ default:
+ result=1;
+ break;
+ }
+
+ SAFE_FREE(*pp_printer);
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+uint32 add_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL driver, uint32 level)
+{
+ uint32 result;
+ DEBUG(104,("adding a printer at level [%d]\n", level));
+ dump_a_printer_driver(driver, level);
+
+ switch (level) {
+ case 3:
+ result=add_a_printer_driver_3(driver.info_3);
+ break;
+
+ case 6:
+ result=add_a_printer_driver_6(driver.info_6);
+ break;
+
+ default:
+ result=1;
+ break;
+ }
+
+ return result;
+}
+/****************************************************************************
+****************************************************************************/
+
+WERROR get_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL *driver, uint32 level,
+ fstring drivername, const char *architecture, uint32 version)
+{
+ WERROR result;
+
+ switch (level) {
+ case 3:
+ /* Sometime we just want any version of the driver */
+
+ if ( version == DRIVER_ANY_VERSION ) {
+ /* look for Win2k first and then for NT4 */
+ result = get_a_printer_driver_3(&driver->info_3, drivername,
+ architecture, 3);
+
+ if ( !W_ERROR_IS_OK(result) ) {
+ result = get_a_printer_driver_3( &driver->info_3,
+ drivername, architecture, 2 );
+ }
+ } else {
+ result = get_a_printer_driver_3(&driver->info_3, drivername,
+ architecture, version);
+ }
+ break;
+
+ default:
+ result=W_ERROR(1);
+ break;
+ }
+
+ if (W_ERROR_IS_OK(result))
+ dump_a_printer_driver(*driver, level);
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+uint32 free_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL driver, uint32 level)
+{
+ uint32 result;
+
+ switch (level) {
+ case 3:
+ {
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 *info3;
+ if (driver.info_3 != NULL)
+ {
+ info3=driver.info_3;
+ SAFE_FREE(info3->dependentfiles);
+ ZERO_STRUCTP(info3);
+ SAFE_FREE(info3);
+ result=0;
+ } else {
+ result=4;
+ }
+ break;
+ }
+ case 6:
+ {
+ NT_PRINTER_DRIVER_INFO_LEVEL_6 *info6;
+ if (driver.info_6 != NULL) {
+ info6=driver.info_6;
+ SAFE_FREE(info6->dependentfiles);
+ SAFE_FREE(info6->previousnames);
+ ZERO_STRUCTP(info6);
+ SAFE_FREE(info6);
+ result=0;
+ } else {
+ result=4;
+ }
+ break;
+ }
+ default:
+ result=1;
+ break;
+ }
+ return result;
+}
+
+
+/****************************************************************************
+ Determine whether or not a particular driver is currently assigned
+ to a printer
+****************************************************************************/
+
+BOOL printer_driver_in_use ( NT_PRINTER_DRIVER_INFO_LEVEL_3 *info_3 )
+{
+ int snum;
+ int n_services = lp_numservices();
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ if ( !info_3 )
+ return False;
+
+ DEBUG(5,("printer_driver_in_use: Beginning search through ntprinters.tdb...\n"));
+
+ /* loop through the printers.tdb and check for the drivername */
+
+ for (snum=0; snum<n_services; snum++) {
+ if ( !(lp_snum_ok(snum) && lp_print_ok(snum) ) )
+ continue;
+
+ if ( !W_ERROR_IS_OK(get_a_printer(NULL, &printer, 2, lp_servicename(snum))) )
+ continue;
+
+ if ( !StrCaseCmp(info_3->name, printer->info_2->drivername) ) {
+ free_a_printer( &printer, 2 );
+ return True;
+ }
+
+ free_a_printer( &printer, 2 );
+ }
+
+ DEBUG(5,("printer_driver_in_use: Completed search through ntprinters.tdb...\n"));
+
+ /* report that the driver is not in use by default */
+
+ return False;
+}
+
+
+/**********************************************************************
+ Check to see if a ogiven file is in use by *info
+ *********************************************************************/
+
+static BOOL drv_file_in_use( char* file, NT_PRINTER_DRIVER_INFO_LEVEL_3 *info )
+{
+ int i = 0;
+
+ if ( !info )
+ return False;
+
+ if ( strequal(file, info->driverpath) )
+ return True;
+
+ if ( strequal(file, info->datafile) )
+ return True;
+
+ if ( strequal(file, info->configfile) )
+ return True;
+
+ if ( strequal(file, info->helpfile) )
+ return True;
+
+ /* see of there are any dependent files to examine */
+
+ if ( !info->dependentfiles )
+ return False;
+
+ while ( *info->dependentfiles[i] ) {
+ if ( strequal(file, info->dependentfiles[i]) )
+ return True;
+ i++;
+ }
+
+ return False;
+
+}
+
+/**********************************************************************
+ Utility function to remove the dependent file pointed to by the
+ input parameter from the list
+ *********************************************************************/
+
+static void trim_dependent_file( fstring files[], int idx )
+{
+
+ /* bump everything down a slot */
+
+ while( *files[idx+1] ) {
+ fstrcpy( files[idx], files[idx+1] );
+ idx++;
+ }
+
+ *files[idx] = '\0';
+
+ return;
+}
+
+/**********************************************************************
+ Check if any of the files used by src are also used by drv
+ *********************************************************************/
+
+static BOOL trim_overlap_drv_files( NT_PRINTER_DRIVER_INFO_LEVEL_3 *src,
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 *drv )
+{
+ BOOL in_use = False;
+ int i = 0;
+
+ if ( !src || !drv )
+ return False;
+
+ /* check each file. Remove it from the src structure if it overlaps */
+
+ if ( drv_file_in_use(src->driverpath, drv) ) {
+ in_use = True;
+ DEBUG(10,("Removing driverfile [%s] from list\n", src->driverpath));
+ fstrcpy( src->driverpath, "" );
+ }
+
+ if ( drv_file_in_use(src->datafile, drv) ) {
+ in_use = True;
+ DEBUG(10,("Removing datafile [%s] from list\n", src->datafile));
+ fstrcpy( src->datafile, "" );
+ }
+
+ if ( drv_file_in_use(src->configfile, drv) ) {
+ in_use = True;
+ DEBUG(10,("Removing configfile [%s] from list\n", src->configfile));
+ fstrcpy( src->configfile, "" );
+ }
+
+ if ( drv_file_in_use(src->helpfile, drv) ) {
+ in_use = True;
+ DEBUG(10,("Removing helpfile [%s] from list\n", src->helpfile));
+ fstrcpy( src->helpfile, "" );
+ }
+
+ /* are there any dependentfiles to examine? */
+
+ if ( !src->dependentfiles )
+ return in_use;
+
+ while ( *src->dependentfiles[i] ) {
+ if ( drv_file_in_use(src->dependentfiles[i], drv) ) {
+ in_use = True;
+ DEBUG(10,("Removing [%s] from dependent file list\n", src->dependentfiles[i]));
+ trim_dependent_file( src->dependentfiles, i );
+ } else
+ i++;
+ }
+
+ return in_use;
+}
+
+/****************************************************************************
+ Determine whether or not a particular driver files are currently being
+ used by any other driver.
+
+ Return value is True if any files were in use by other drivers
+ and False otherwise.
+
+ Upon return, *info has been modified to only contain the driver files
+ which are not in use
+****************************************************************************/
+
+BOOL printer_driver_files_in_use ( NT_PRINTER_DRIVER_INFO_LEVEL_3 *info )
+{
+ int i;
+ int ndrivers;
+ uint32 version;
+ fstring *list = NULL;
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+
+ if ( !info )
+ return False;
+
+ version = info->cversion;
+
+ /* loop over all driver versions */
+
+ DEBUG(5,("printer_driver_files_in_use: Beginning search through ntdrivers.tdb...\n"));
+
+ /* get the list of drivers */
+
+ list = NULL;
+ ndrivers = get_ntdrivers(&list, info->environment, version);
+
+ DEBUGADD(4,("we have:[%d] drivers in environment [%s] and version [%d]\n",
+ ndrivers, info->environment, version));
+
+ /* check each driver for overlap in files */
+
+ for (i=0; i<ndrivers; i++) {
+ DEBUGADD(5,("\tdriver: [%s]\n", list[i]));
+
+ ZERO_STRUCT(driver);
+
+ if ( !W_ERROR_IS_OK(get_a_printer_driver(&driver, 3, list[i], info->environment, version)) ) {
+ SAFE_FREE(list);
+ return True;
+ }
+
+ /* check if d2 uses any files from d1 */
+ /* only if this is a different driver than the one being deleted */
+
+ if ( !strequal(info->name, driver.info_3->name) ) {
+ if ( trim_overlap_drv_files(info, driver.info_3) ) {
+ free_a_printer_driver(driver, 3);
+ SAFE_FREE( list );
+ return True;
+ }
+ }
+
+ free_a_printer_driver(driver, 3);
+ }
+
+ SAFE_FREE(list);
+
+ DEBUG(5,("printer_driver_files_in_use: Completed search through ntdrivers.tdb...\n"));
+
+ driver.info_3 = info;
+
+ if ( DEBUGLEVEL >= 20 )
+ dump_a_printer_driver( driver, 3 );
+
+ return False;
+}
+
+/****************************************************************************
+ Actually delete the driver files. Make sure that
+ printer_driver_files_in_use() return False before calling
+ this.
+****************************************************************************/
+
+static BOOL delete_driver_files( NT_PRINTER_DRIVER_INFO_LEVEL_3 *info_3, struct current_user *user )
+{
+ int i = 0;
+ char *s;
+ connection_struct *conn;
+ DATA_BLOB null_pw;
+ NTSTATUS nt_status;
+ fstring res_type;
+
+ if ( !info_3 )
+ return False;
+
+ DEBUG(6,("delete_driver_files: deleting driver [%s] - version [%d]\n", info_3->name, info_3->cversion));
+
+ /*
+ * Connect to the print$ share under the same account as the
+ * user connected to the rpc pipe. Note we must be root to
+ * do this.
+ */
+
+ null_pw = data_blob( NULL, 0 );
+ fstrcpy(res_type, "A:");
+ become_root();
+ conn = make_connection_with_chdir( "print$", null_pw, res_type, user->vuid, &nt_status );
+ unbecome_root();
+
+ if ( !conn ) {
+ DEBUG(0,("delete_driver_files: Unable to connect\n"));
+ return False;
+ }
+
+ /* Save who we are - we are temporarily becoming the connection user. */
+
+ if ( !become_user(conn, conn->vuid) ) {
+ DEBUG(0,("delete_driver_files: Can't become user!\n"));
+ return False;
+ }
+
+ /* now delete the files; must strip the '\print$' string from
+ fron of path */
+
+ if ( *info_3->driverpath ) {
+ if ( (s = strchr( &info_3->driverpath[1], '\\' )) != NULL ) {
+ DEBUG(10,("deleting driverfile [%s]\n", s));
+ unlink_internals(conn, 0, s);
+ }
+ }
+
+ if ( *info_3->configfile ) {
+ if ( (s = strchr( &info_3->configfile[1], '\\' )) != NULL ) {
+ DEBUG(10,("deleting configfile [%s]\n", s));
+ unlink_internals(conn, 0, s);
+ }
+ }
+
+ if ( *info_3->datafile ) {
+ if ( (s = strchr( &info_3->datafile[1], '\\' )) != NULL ) {
+ DEBUG(10,("deleting datafile [%s]\n", s));
+ unlink_internals(conn, 0, s);
+ }
+ }
+
+ if ( *info_3->helpfile ) {
+ if ( (s = strchr( &info_3->helpfile[1], '\\' )) != NULL ) {
+ DEBUG(10,("deleting helpfile [%s]\n", s));
+ unlink_internals(conn, 0, s);
+ }
+ }
+
+ /* check if we are done removing files */
+
+ if ( info_3->dependentfiles ) {
+ while ( *info_3->dependentfiles[i] ) {
+ char *file;
+
+ /* bypass the "\print$" portion of the path */
+
+ if ( (file = strchr( info_3->dependentfiles[i]+1, '\\' )) != NULL ) {
+ DEBUG(10,("deleting dependent file [%s]\n", file));
+ unlink_internals(conn, 0, file );
+ }
+
+ i++;
+ }
+ }
+
+ unbecome_user();
+
+ return True;
+}
+
+/****************************************************************************
+ Remove a printer driver from the TDB. This assumes that the the driver was
+ previously looked up.
+ ***************************************************************************/
+
+WERROR delete_printer_driver( NT_PRINTER_DRIVER_INFO_LEVEL_3 *info_3, struct current_user *user,
+ uint32 version, BOOL delete_files )
+{
+ pstring key;
+ const char *arch;
+ TDB_DATA kbuf, dbuf;
+ NT_PRINTER_DRIVER_INFO_LEVEL ctr;
+
+ /* delete the tdb data first */
+
+ arch = get_short_archi(info_3->environment);
+ slprintf(key, sizeof(key)-1, "%s%s/%d/%s", DRIVERS_PREFIX,
+ arch, version, info_3->name);
+
+ DEBUG(5,("delete_printer_driver: key = [%s] delete_files = %s\n",
+ key, delete_files ? "TRUE" : "FALSE" ));
+
+ ctr.info_3 = info_3;
+ dump_a_printer_driver( ctr, 3 );
+
+ kbuf.dptr=key;
+ kbuf.dsize=strlen(key)+1;
+
+ /* check if the driver actually exists for this environment */
+
+ dbuf = tdb_fetch( tdb_drivers, kbuf );
+ if ( !dbuf.dptr ) {
+ DEBUG(8,("delete_printer_driver: Driver unknown [%s]\n", key));
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+ }
+
+ SAFE_FREE( dbuf.dptr );
+
+ /* ok... the driver exists so the delete should return success */
+
+ if (tdb_delete(tdb_drivers, kbuf) == -1) {
+ DEBUG (0,("delete_printer_driver: fail to delete %s!\n", key));
+ return WERR_ACCESS_DENIED;
+ }
+
+ /*
+ * now delete any associated files if delete_files == True
+ * even if this part failes, we return succes because the
+ * driver doesn not exist any more
+ */
+
+ if ( delete_files )
+ delete_driver_files( info_3, user );
+
+
+ DEBUG(5,("delete_printer_driver: driver delete successful [%s]\n", key));
+
+ return WERR_OK;
+ }
+
+/****************************************************************************
+ Store a security desc for a printer.
+****************************************************************************/
+
+WERROR nt_printing_setsec(const char *printername, SEC_DESC_BUF *secdesc_ctr)
+{
+ SEC_DESC_BUF *new_secdesc_ctr = NULL;
+ SEC_DESC_BUF *old_secdesc_ctr = NULL;
+ prs_struct ps;
+ TALLOC_CTX *mem_ctx = NULL;
+ fstring key;
+ WERROR status;
+
+ mem_ctx = talloc_init("nt_printing_setsec");
+ if (mem_ctx == NULL)
+ return WERR_NOMEM;
+
+ /* The old owner and group sids of the security descriptor are not
+ present when new ACEs are added or removed by changing printer
+ permissions through NT. If they are NULL in the new security
+ descriptor then copy them over from the old one. */
+
+ if (!secdesc_ctr->sec->owner_sid || !secdesc_ctr->sec->grp_sid) {
+ DOM_SID *owner_sid, *group_sid;
+ SEC_ACL *dacl, *sacl;
+ SEC_DESC *psd = NULL;
+ size_t size;
+
+ nt_printing_getsec(mem_ctx, printername, &old_secdesc_ctr);
+
+ /* Pick out correct owner and group sids */
+
+ owner_sid = secdesc_ctr->sec->owner_sid ?
+ secdesc_ctr->sec->owner_sid :
+ old_secdesc_ctr->sec->owner_sid;
+
+ group_sid = secdesc_ctr->sec->grp_sid ?
+ secdesc_ctr->sec->grp_sid :
+ old_secdesc_ctr->sec->grp_sid;
+
+ dacl = secdesc_ctr->sec->dacl ?
+ secdesc_ctr->sec->dacl :
+ old_secdesc_ctr->sec->dacl;
+
+ sacl = secdesc_ctr->sec->sacl ?
+ secdesc_ctr->sec->sacl :
+ old_secdesc_ctr->sec->sacl;
+
+ /* Make a deep copy of the security descriptor */
+
+ psd = make_sec_desc(mem_ctx, secdesc_ctr->sec->revision, secdesc_ctr->sec->type,
+ owner_sid, group_sid,
+ sacl,
+ dacl,
+ &size);
+
+ new_secdesc_ctr = make_sec_desc_buf(mem_ctx, size, psd);
+ }
+
+ if (!new_secdesc_ctr) {
+ new_secdesc_ctr = secdesc_ctr;
+ }
+
+ /* Store the security descriptor in a tdb */
+
+ prs_init(&ps, (uint32)sec_desc_size(new_secdesc_ctr->sec) +
+ sizeof(SEC_DESC_BUF), mem_ctx, MARSHALL);
+
+ if (!sec_io_desc_buf("nt_printing_setsec", &new_secdesc_ctr,
+ &ps, 1)) {
+ status = WERR_BADFUNC;
+ goto out;
+ }
+
+ slprintf(key, sizeof(key)-1, "SECDESC/%s", printername);
+
+ if (tdb_prs_store(tdb_printers, key, &ps)==0) {
+ status = WERR_OK;
+ } else {
+ DEBUG(1,("Failed to store secdesc for %s\n", printername));
+ status = WERR_BADFUNC;
+ }
+
+ /* Free malloc'ed memory */
+
+ out:
+
+ prs_mem_free(&ps);
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+ return status;
+}
+
+/****************************************************************************
+ Construct a default security descriptor buffer for a printer.
+****************************************************************************/
+
+static SEC_DESC_BUF *construct_default_printer_sdb(TALLOC_CTX *ctx)
+{
+ SEC_ACE ace[3];
+ SEC_ACCESS sa;
+ SEC_ACL *psa = NULL;
+ SEC_DESC_BUF *sdb = NULL;
+ SEC_DESC *psd = NULL;
+ DOM_SID owner_sid;
+ size_t sd_size;
+
+ /* Create an ACE where Everyone is allowed to print */
+
+ init_sec_access(&sa, PRINTER_ACE_PRINT);
+ init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED,
+ sa, SEC_ACE_FLAG_CONTAINER_INHERIT);
+
+ /* Make the security descriptor owned by the Administrators group
+ on the PDC of the domain. */
+
+ if (secrets_fetch_domain_sid(lp_workgroup(), &owner_sid)) {
+ sid_append_rid(&owner_sid, DOMAIN_USER_RID_ADMIN);
+ } else {
+
+ /* Backup plan - make printer owned by admins.
+ This should emulate a lanman printer as security
+ settings can't be changed. */
+
+ sid_copy(&owner_sid, get_global_sam_sid());
+ sid_append_rid(&owner_sid, DOMAIN_USER_RID_ADMIN);
+ }
+
+ init_sec_access(&sa, PRINTER_ACE_FULL_CONTROL);
+ init_sec_ace(&ace[1], &owner_sid, SEC_ACE_TYPE_ACCESS_ALLOWED,
+ sa, SEC_ACE_FLAG_OBJECT_INHERIT |
+ SEC_ACE_FLAG_INHERIT_ONLY);
+
+ init_sec_access(&sa, PRINTER_ACE_FULL_CONTROL);
+ init_sec_ace(&ace[2], &owner_sid, SEC_ACE_TYPE_ACCESS_ALLOWED,
+ sa, SEC_ACE_FLAG_CONTAINER_INHERIT);
+
+ /* The ACL revision number in rpc_secdesc.h differs from the one
+ created by NT when setting ACE entries in printer
+ descriptors. NT4 complains about the property being edited by a
+ NT5 machine. */
+
+ if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 3, ace)) != NULL) {
+ psd = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE,
+ &owner_sid, NULL,
+ NULL, psa, &sd_size);
+ }
+
+ if (!psd) {
+ DEBUG(0,("construct_default_printer_sd: Failed to make SEC_DESC.\n"));
+ return NULL;
+ }
+
+ sdb = make_sec_desc_buf(ctx, sd_size, psd);
+
+ DEBUG(4,("construct_default_printer_sdb: size = %u.\n",
+ (unsigned int)sd_size));
+
+ return sdb;
+}
+
+/****************************************************************************
+ Get a security desc for a printer.
+****************************************************************************/
+
+BOOL nt_printing_getsec(TALLOC_CTX *ctx, const char *printername, SEC_DESC_BUF **secdesc_ctr)
+{
+ prs_struct ps;
+ fstring key;
+ char *temp;
+
+ if (strlen(printername) > 2 && (temp = strchr(printername + 2, '\\'))) {
+ printername = temp + 1;
+ }
+
+ /* Fetch security descriptor from tdb */
+
+ slprintf(key, sizeof(key)-1, "SECDESC/%s", printername);
+
+ if (tdb_prs_fetch(tdb_printers, key, &ps, ctx)!=0 ||
+ !sec_io_desc_buf("nt_printing_getsec", secdesc_ctr, &ps, 1)) {
+
+ DEBUG(4,("using default secdesc for %s\n", printername));
+
+ if (!(*secdesc_ctr = construct_default_printer_sdb(ctx))) {
+ return False;
+ }
+
+ /* Save default security descriptor for later */
+
+ prs_init(&ps, (uint32)sec_desc_size((*secdesc_ctr)->sec) +
+ sizeof(SEC_DESC_BUF), ctx, MARSHALL);
+
+ if (sec_io_desc_buf("nt_printing_getsec", secdesc_ctr, &ps, 1))
+ tdb_prs_store(tdb_printers, key, &ps);
+
+ prs_mem_free(&ps);
+
+ return True;
+ }
+
+ /* If security descriptor is owned by S-1-1-0 and winbindd is up,
+ this security descriptor has been created when winbindd was
+ down. Take ownership of security descriptor. */
+
+ if (sid_equal((*secdesc_ctr)->sec->owner_sid, &global_sid_World)) {
+ DOM_SID owner_sid;
+
+ /* Change sd owner to workgroup administrator */
+
+ if (secrets_fetch_domain_sid(lp_workgroup(), &owner_sid)) {
+ SEC_DESC_BUF *new_secdesc_ctr = NULL;
+ SEC_DESC *psd = NULL;
+ size_t size;
+
+ /* Create new sd */
+
+ sid_append_rid(&owner_sid, DOMAIN_USER_RID_ADMIN);
+
+ psd = make_sec_desc(ctx, (*secdesc_ctr)->sec->revision, (*secdesc_ctr)->sec->type,
+ &owner_sid,
+ (*secdesc_ctr)->sec->grp_sid,
+ (*secdesc_ctr)->sec->sacl,
+ (*secdesc_ctr)->sec->dacl,
+ &size);
+
+ new_secdesc_ctr = make_sec_desc_buf(ctx, size, psd);
+
+ /* Swap with other one */
+
+ *secdesc_ctr = new_secdesc_ctr;
+
+ /* Set it */
+
+ nt_printing_setsec(printername, *secdesc_ctr);
+ }
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ SEC_ACL *the_acl = (*secdesc_ctr)->sec->dacl;
+ int i;
+
+ DEBUG(10, ("secdesc_ctr for %s has %d aces:\n",
+ printername, the_acl->num_aces));
+
+ for (i = 0; i < the_acl->num_aces; i++) {
+ fstring sid_str;
+
+ sid_to_string(sid_str, &the_acl->ace[i].trustee);
+
+ DEBUG(10, ("%s %d %d 0x%08x\n", sid_str,
+ the_acl->ace[i].type, the_acl->ace[i].flags,
+ the_acl->ace[i].info.mask));
+ }
+ }
+
+ prs_mem_free(&ps);
+ return True;
+}
+
+/* error code:
+ 0: everything OK
+ 1: level not implemented
+ 2: file doesn't exist
+ 3: can't allocate memory
+ 4: can't free memory
+ 5: non existant struct
+*/
+
+/*
+ A printer and a printer driver are 2 different things.
+ NT manages them separatelly, Samba does the same.
+ Why ? Simply because it's easier and it makes sense !
+
+ Now explanation: You have 3 printers behind your samba server,
+ 2 of them are the same make and model (laser A and B). But laser B
+ has an 3000 sheet feeder and laser A doesn't such an option.
+ Your third printer is an old dot-matrix model for the accounting :-).
+
+ If the /usr/local/samba/lib directory (default dir), you will have
+ 5 files to describe all of this.
+
+ 3 files for the printers (1 by printer):
+ NTprinter_laser A
+ NTprinter_laser B
+ NTprinter_accounting
+ 2 files for the drivers (1 for the laser and 1 for the dot matrix)
+ NTdriver_printer model X
+ NTdriver_printer model Y
+
+jfm: I should use this comment for the text file to explain
+ same thing for the forms BTW.
+ Je devrais mettre mes commentaires en francais, ca serait mieux :-)
+
+*/
+
+/* Convert generic access rights to printer object specific access rights.
+ It turns out that NT4 security descriptors use generic access rights and
+ NT5 the object specific ones. */
+
+void map_printer_permissions(SEC_DESC *sd)
+{
+ int i;
+
+ for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
+ se_map_generic(&sd->dacl->ace[i].info.mask,
+ &printer_generic_mapping);
+ }
+}
+
+/****************************************************************************
+ Check a user has permissions to perform the given operation. We use the
+ permission constants defined in include/rpc_spoolss.h to check the various
+ actions we perform when checking printer access.
+
+ PRINTER_ACCESS_ADMINISTER:
+ print_queue_pause, print_queue_resume, update_printer_sec,
+ update_printer, spoolss_addprinterex_level_2,
+ _spoolss_setprinterdata
+
+ PRINTER_ACCESS_USE:
+ print_job_start
+
+ JOB_ACCESS_ADMINISTER:
+ print_job_delete, print_job_pause, print_job_resume,
+ print_queue_purge
+
+ ****************************************************************************/
+BOOL print_access_check(struct current_user *user, int snum, int access_type)
+{
+ SEC_DESC_BUF *secdesc = NULL;
+ uint32 access_granted;
+ NTSTATUS status;
+ BOOL result;
+ const char *pname;
+ TALLOC_CTX *mem_ctx = NULL;
+ extern struct current_user current_user;
+
+ /* If user is NULL then use the current_user structure */
+
+ if (!user)
+ user = &current_user;
+
+ /* Always allow root or printer admins to do anything */
+
+ if (user->uid == 0 ||
+ user_in_list(uidtoname(user->uid), lp_printer_admin(snum), user->groups, user->ngroups)) {
+ return True;
+ }
+
+ /* Get printer name */
+
+ pname = PRINTERNAME(snum);
+
+ if (!pname || !*pname) {
+ errno = EACCES;
+ return False;
+ }
+
+ /* Get printer security descriptor */
+
+ if(!(mem_ctx = talloc_init("print_access_check"))) {
+ errno = ENOMEM;
+ return False;
+ }
+
+ nt_printing_getsec(mem_ctx, pname, &secdesc);
+
+ if (access_type == JOB_ACCESS_ADMINISTER) {
+ SEC_DESC_BUF *parent_secdesc = secdesc;
+
+ /* Create a child security descriptor to check permissions
+ against. This is because print jobs are child objects
+ objects of a printer. */
+
+ secdesc = se_create_child_secdesc(mem_ctx, parent_secdesc->sec, False);
+
+ /* Now this is the bit that really confuses me. The access
+ type needs to be changed from JOB_ACCESS_ADMINISTER to
+ PRINTER_ACCESS_ADMINISTER for this to work. Something
+ to do with the child (job) object becoming like a
+ printer?? -tpot */
+
+ access_type = PRINTER_ACCESS_ADMINISTER;
+ }
+
+ /* Check access */
+
+ map_printer_permissions(secdesc->sec);
+
+ result = se_access_check(secdesc->sec, user->nt_user_token, access_type,
+ &access_granted, &status);
+
+ DEBUG(4, ("access check was %s\n", result ? "SUCCESS" : "FAILURE"));
+
+ talloc_destroy(mem_ctx);
+
+ if (!result)
+ errno = EACCES;
+
+ return result;
+}
+
+/****************************************************************************
+ Check the time parameters allow a print operation.
+*****************************************************************************/
+
+BOOL print_time_access_check(int snum)
+{
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ BOOL ok = False;
+ time_t now = time(NULL);
+ struct tm *t;
+ uint32 mins;
+
+ if (!W_ERROR_IS_OK(get_a_printer(NULL, &printer, 2, lp_servicename(snum))))
+ return False;
+
+ if (printer->info_2->starttime == 0 && printer->info_2->untiltime == 0)
+ ok = True;
+
+ t = gmtime(&now);
+ mins = (uint32)t->tm_hour*60 + (uint32)t->tm_min;
+
+ if (mins >= printer->info_2->starttime && mins <= printer->info_2->untiltime)
+ ok = True;
+
+ free_a_printer(&printer, 2);
+
+ if (!ok)
+ errno = EACCES;
+
+ return ok;
+}
+
diff --git a/source/printing/pcap.c b/source/printing/pcap.c
new file mode 100644
index 00000000000..a5fb53a320d
--- /dev/null
+++ b/source/printing/pcap.c
@@ -0,0 +1,413 @@
+/*
+ Unix SMB/CIFS implementation.
+ printcap parsing
+ Copyright (C) Karl Auer 1993-1998
+
+ Re-working by Martin Kiff, 1994
+
+ Re-written again by Andrew Tridgell
+
+ Modified for SVID support by Norm Jacobs, 1997
+
+ Modified for CUPS support by Michael Sweet, 1999
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ * Parse printcap file.
+ *
+ * This module does exactly one thing - it looks into the printcap file
+ * and tells callers if a specified string appears as a printer name.
+ *
+ * The way this module looks at the printcap file is very simplistic.
+ * Only the local printcap file is inspected (no searching of NIS
+ * databases etc).
+ *
+ * There are assumed to be one or more printer names per record, held
+ * as a set of sub-fields separated by vertical bar symbols ('|') in the
+ * first field of the record. The field separator is assumed to be a colon
+ * ':' and the record separator a newline.
+ *
+ * Lines ending with a backspace '\' are assumed to flag that the following
+ * line is a continuation line so that a set of lines can be read as one
+ * printcap entry.
+ *
+ * A line stating with a hash '#' is assumed to be a comment and is ignored
+ * Comments are discarded before the record is strung together from the
+ * set of continuation lines.
+ *
+ * Opening a pipe for "lpc status" and reading that would probably
+ * be pretty effective. Code to do this already exists in the freely
+ * distributable PCNFS server code.
+ *
+ * Modified to call SVID/XPG4 support if printcap name is set to "lpstat"
+ * in smb.conf under Solaris.
+ *
+ * Modified to call CUPS support if printcap name is set to "cups"
+ * in smb.conf.
+ */
+
+#include "includes.h"
+
+#ifdef AIX
+/* ******************************************
+ Extend for AIX system and qconfig file
+ from 'boulard@univ-rennes1.fr
+ ****************************************** */
+static int strlocate(char *xpLine,char *xpS)
+{
+ int iS,iL,iRet;
+ char *p;
+ iS = strlen(xpS);
+ iL = strlen(xpLine);
+
+ iRet = 0;
+ p = xpLine;
+ while (iL >= iS)
+ {
+ if (strncmp(p,xpS,iS) == 0) {iRet =1;break;};
+ p++;
+ iL--;
+ }
+ /*DEBUG(3,(" strlocate %s in line '%s',ret=%d\n",xpS,xpLine,iRet));*/
+
+ return(iRet);
+}
+
+
+/* ******************************************************************* */
+/* * Scan qconfig and search all virtual printer (device printer) * */
+/* ******************************************************************* */
+static void ScanQconfig_fn(char *psz,void (*fn)(char *, char *))
+{
+ int iEtat;
+ XFILE *pfile;
+ char *line,*p;
+ pstring name,comment;
+ line = NULL;
+ *name = 0;
+ *comment = 0;
+
+ if ((pfile = x_fopen(psz, O_RDONLY, 0)) == NULL)
+ {
+ DEBUG(0,( "Unable to open qconfig file %s for read!\n", psz));
+ return;
+ }
+
+ iEtat = 0;
+ /* scan qconfig file for searching <printername>: */
+ for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); safe_free(line))
+ {
+ if (*line == '*' || *line == 0)
+ continue;
+ switch (iEtat)
+ {
+ case 0: /* locate an entry */
+ if (*line == '\t' || *line == ' ') continue;
+ if ((p=strchr_m(line,':')))
+ {
+ *p = '\0';
+ p = strtok(line,":");
+ if (strcmp(p,"bsh")!=0)
+ {
+ pstrcpy(name,p);
+ iEtat = 1;
+ continue;
+ }
+ }
+ break;
+ case 1: /* scanning device stanza */
+ if (*line == '*' || *line == 0) continue;
+ if (*line != '\t' && *line != ' ')
+ {
+ /* name is found without stanza device */
+ /* probably a good printer ??? */
+ fn(name,comment);
+ iEtat = 0;
+ continue;
+ }
+
+ if (strlocate(line,"backend"))
+ {
+ /* it's a device, not a virtual printer*/
+ iEtat = 0;
+ }
+ else if (strlocate(line,"device"))
+ {
+ /* it's a good virtual printer */
+ fn(name,comment);
+ iEtat = 0;
+ continue;
+ }
+ break;
+ }
+ }
+ x_fclose(pfile);
+}
+
+/* Scan qconfig file and locate de printername */
+
+static BOOL ScanQconfig(char *psz,char *pszPrintername)
+{
+ int iLg,iEtat;
+ XFILE *pfile;
+ char *pName;
+ char *line;
+
+ pName = NULL;
+ line = NULL;
+ if ((pszPrintername!= NULL) && ((iLg = strlen(pszPrintername)) > 0))
+ pName = malloc(iLg+10);
+ if (pName == NULL)
+ {
+ DEBUG(0,(" Unable to allocate memory for printer %s\n",pszPrintername));
+ return(False);
+ }
+ if ((pfile = x_fopen(psz, O_RDONLY, 0)) == NULL)
+ {
+ DEBUG(0,( "Unable to open qconfig file %s for read!\n", psz));
+ SAFE_FREE(pName);
+ return(False);
+ }
+ slprintf(pName, iLg + 9, "%s:",pszPrintername);
+ iLg = strlen(pName);
+ /*DEBUG(3,( " Looking for entry %s\n",pName));*/
+ iEtat = 0;
+ /* scan qconfig file for searching <printername>: */
+ for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); safe_free(line))
+ {
+ if (*line == '*' || *line == 0)
+ continue;
+ switch (iEtat)
+ {
+ case 0: /* scanning entry */
+ if (strncmp(line,pName,iLg) == 0)
+ {
+ iEtat = 1;
+ continue;
+ }
+ break;
+ case 1: /* scanning device stanza */
+ if (*line == '*' || *line == 0) continue;
+ if (*line != '\t' && *line != ' ')
+ {
+ /* name is found without stanza device */
+ /* probably a good printer ??? */
+ free (line);
+ SAFE_FREE(pName);
+ x_fclose(pfile);
+ return(True);
+ }
+
+ if (strlocate(line,"backend"))
+ {
+ /* it's a device, not a virtual printer*/
+ iEtat = 0;
+ }
+ else if (strlocate(line,"device"))
+ {
+ /* it's a good virtual printer */
+ free (line);
+ SAFE_FREE(pName);
+ x_fclose(pfile);
+ return(True);
+ }
+ break;
+ }
+ }
+ free (pName);
+ x_fclose(pfile);
+ return(False);
+}
+#endif /* AIX */
+
+
+/***************************************************************************
+Scan printcap file pszPrintcapname for a printer called pszPrintername.
+Return True if found, else False. Returns False on error, too, after logging
+the error at level 0. For generality, the printcap name may be passed - if
+passed as NULL, the configuration will be queried for the name.
+***************************************************************************/
+BOOL pcap_printername_ok(const char *pszPrintername, const char *pszPrintcapname)
+{
+ char *line=NULL;
+ const char *psz;
+ char *p,*q;
+ XFILE *pfile;
+
+ if (pszPrintername == NULL || pszPrintername[0] == '\0')
+ {
+ DEBUG(0,( "Attempt to locate null printername! Internal error?\n"));
+ return(False);
+ }
+
+ /* only go looking if no printcap name supplied */
+ if ((psz = pszPrintcapname) == NULL || psz[0] == '\0')
+ if (((psz = lp_printcapname()) == NULL) || (psz[0] == '\0'))
+ {
+ DEBUG(0,( "No printcap file name configured!\n"));
+ return(False);
+ }
+
+#ifdef HAVE_CUPS
+ if (strequal(psz, "cups"))
+ return (cups_printername_ok(pszPrintername));
+#endif /* HAVE_CUPS */
+
+#ifdef SYSV
+ if (strequal(psz, "lpstat"))
+ return (sysv_printername_ok(pszPrintername));
+#endif
+
+#ifdef AIX
+ if (strlocate(psz,"/qconfig"))
+ return(ScanQconfig(psz,pszPrintername));
+#endif
+
+ if ((pfile = x_fopen(psz, O_RDONLY, 0)) == NULL)
+ {
+ DEBUG(0,( "Unable to open printcap file %s for read!\n", psz));
+ return(False);
+ }
+
+ for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); safe_free(line))
+ {
+ if (*line == '#' || *line == 0)
+ continue;
+
+ /* now we have a real printer line - cut it off at the first : */
+ p = strchr_m(line,':');
+ if (p) *p = 0;
+
+ /* now just check if the name is in the list */
+ /* NOTE: I avoid strtok as the fn calling this one may be using it */
+ for (p=line; p; p=q)
+ {
+ if ((q = strchr_m(p,'|'))) *q++ = 0;
+
+ if (strequal(p,pszPrintername))
+ {
+ SAFE_FREE(line);
+ x_fclose(pfile);
+ return(True);
+ }
+ p = q;
+ }
+ }
+
+ x_fclose(pfile);
+ return(False);
+}
+
+
+/***************************************************************************
+run a function on each printer name in the printcap file. The function is
+passed the primary name and the comment (if possible). Note the fn() takes
+strings in DOS codepage. This means the xxx_printer_fn() calls must be fixed
+to return DOS codepage. FIXME !! JRA.
+***************************************************************************/
+void pcap_printer_fn(void (*fn)(char *, char *))
+{
+ pstring name,comment;
+ char *line;
+ char *psz;
+ char *p,*q;
+ XFILE *pfile;
+
+ /* only go looking if no printcap name supplied */
+ if (((psz = lp_printcapname()) == NULL) || (psz[0] == '\0'))
+ {
+ DEBUG(0,( "No printcap file name configured!\n"));
+ return;
+ }
+
+#ifdef HAVE_CUPS
+ if (strequal(psz, "cups")) {
+ cups_printer_fn(fn);
+ return;
+ }
+#endif /* HAVE_CUPS */
+
+#ifdef SYSV
+ if (strequal(psz, "lpstat")) {
+ sysv_printer_fn(fn);
+ return;
+ }
+#endif
+
+#ifdef AIX
+ if (strlocate(psz,"/qconfig"))
+ {
+ ScanQconfig_fn(psz,fn);
+ return;
+ }
+#endif
+
+ if ((pfile = x_fopen(psz, O_RDONLY, 0)) == NULL)
+ {
+ DEBUG(0,( "Unable to open printcap file %s for read!\n", psz));
+ return;
+ }
+
+ for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); safe_free(line))
+ {
+ if (*line == '#' || *line == 0)
+ continue;
+
+ /* now we have a real printer line - cut it off at the first : */
+ p = strchr_m(line,':');
+ if (p) *p = 0;
+
+ /* now find the most likely printer name and comment
+ this is pure guesswork, but it's better than nothing */
+ *name = 0;
+ *comment = 0;
+ for (p=line; p; p=q)
+ {
+ BOOL has_punctuation;
+ if ((q = strchr_m(p,'|'))) *q++ = 0;
+
+ has_punctuation = (strchr_m(p,' ') || strchr_m(p,'\t') || strchr_m(p,'(') || strchr_m(p,')'));
+
+ if (strlen(p)>strlen(comment) && has_punctuation)
+ {
+ pstrcpy(comment,p);
+ continue;
+ }
+
+ if (strlen(p) <= MAXPRINTERLEN && strlen(p)>strlen(name) && !has_punctuation)
+ {
+ if (!*comment) pstrcpy(comment,name);
+ pstrcpy(name,p);
+ continue;
+ }
+
+ if (!strchr_m(comment,' ') &&
+ strlen(p) > strlen(comment))
+ {
+ pstrcpy(comment,p);
+ continue;
+ }
+ }
+
+ comment[60] = 0;
+ name[MAXPRINTERLEN] = 0;
+
+ if (*name)
+ fn(name,comment);
+ }
+ x_fclose(pfile);
+}
diff --git a/source/printing/print_cups.c b/source/printing/print_cups.c
new file mode 100644
index 00000000000..f0096a17c2c
--- /dev/null
+++ b/source/printing/print_cups.c
@@ -0,0 +1,1303 @@
+/*
+ * Support code for the Common UNIX Printing System ("CUPS")
+ *
+ * Copyright 1999-2003 by Michael R Sweet.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+#include "printing.h"
+
+#ifdef HAVE_CUPS
+#include <cups/cups.h>
+#include <cups/language.h>
+
+
+/*
+ * CUPS printing interface definitions...
+ */
+
+static int cups_job_delete(int snum, struct printjob *pjob);
+static int cups_job_pause(int snum, struct printjob *pjob);
+static int cups_job_resume(int snum, struct printjob *pjob);
+static int cups_job_submit(int snum, struct printjob *pjob);
+static int cups_queue_get(int snum, print_queue_struct **q,
+ print_status_struct *status);
+static int cups_queue_pause(int snum);
+static int cups_queue_resume(int snum);
+
+
+struct printif cups_printif =
+ {
+ cups_queue_get,
+ cups_queue_pause,
+ cups_queue_resume,
+ cups_job_delete,
+ cups_job_pause,
+ cups_job_resume,
+ cups_job_submit,
+ };
+
+/*
+ * 'cups_passwd_cb()' - The CUPS password callback...
+ */
+
+static const char * /* O - Password or NULL */
+cups_passwd_cb(const char *prompt) /* I - Prompt */
+{
+ /*
+ * Always return NULL to indicate that no password is available...
+ */
+
+ return (NULL);
+}
+
+
+/*
+ * 'cups_printer_fn()' - Call a function for every printer known to the
+ * system.
+ */
+
+void cups_printer_fn(void (*fn)(char *, char *))
+{
+ /* I - Function to call */
+ http_t *http; /* HTTP connection to server */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ cups_lang_t *language; /* Default language */
+ char *name, /* printer-name attribute */
+ *make_model, /* printer-make-and-model attribute */
+ *info; /* printer-info attribute */
+ static const char *requested[] =/* Requested attributes */
+ {
+ "printer-name",
+ "printer-make-and-model",
+ "printer-info"
+ };
+
+
+ DEBUG(5,("cups_printer_fn(%p)\n", fn));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(cups_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+ {
+ DEBUG(0,("Unable to connect to CUPS server %s - %s\n",
+ cupsServer(), strerror(errno)));
+ return;
+ }
+
+ /*
+ * Build a CUPS_GET_PRINTERS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * requested-attributes
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_GET_PRINTERS;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requested-attributes",
+ (sizeof(requested) / sizeof(requested[0])),
+ NULL, requested);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/")) == NULL)
+ {
+ DEBUG(0,("Unable to get printer list - %s\n",
+ ippErrorString(cupsLastError())));
+ httpClose(http);
+ return;
+ }
+
+ for (attr = response->attrs; attr != NULL;)
+ {
+ /*
+ * Skip leading attributes until we hit a printer...
+ */
+
+ while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
+ attr = attr->next;
+
+ if (attr == NULL)
+ break;
+
+ /*
+ * Pull the needed attributes from this printer...
+ */
+
+ name = NULL;
+ make_model = NULL;
+ info = NULL;
+
+ while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
+ {
+ if (strcmp(attr->name, "printer-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ name = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "printer-make-and-model") == 0 &&
+ attr->value_tag == IPP_TAG_TEXT)
+ make_model = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "printer-info") == 0 &&
+ attr->value_tag == IPP_TAG_TEXT)
+ info = attr->values[0].string.text;
+
+ attr = attr->next;
+ }
+
+ /*
+ * See if we have everything needed...
+ */
+
+ if (name == NULL)
+ break;
+
+ if (info == NULL || !info[0])
+ (*fn)(name, make_model);
+ else
+ (*fn)(name, info);
+
+
+ }
+
+ ippDelete(response);
+
+
+ /*
+ * Build a CUPS_GET_CLASSES request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * requested-attributes
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_GET_CLASSES;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requested-attributes",
+ (sizeof(requested) / sizeof(requested[0])),
+ NULL, requested);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/")) == NULL)
+ {
+ DEBUG(0,("Unable to get printer list - %s\n",
+ ippErrorString(cupsLastError())));
+ httpClose(http);
+ return;
+ }
+
+ for (attr = response->attrs; attr != NULL;)
+ {
+ /*
+ * Skip leading attributes until we hit a printer...
+ */
+
+ while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
+ attr = attr->next;
+
+ if (attr == NULL)
+ break;
+
+ /*
+ * Pull the needed attributes from this printer...
+ */
+
+ name = NULL;
+ make_model = NULL;
+ info = NULL;
+
+ while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
+ {
+ if (strcmp(attr->name, "printer-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ name = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "printer-make-and-model") == 0 &&
+ attr->value_tag == IPP_TAG_TEXT)
+ make_model = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "printer-info") == 0 &&
+ attr->value_tag == IPP_TAG_TEXT)
+ info = attr->values[0].string.text;
+
+ attr = attr->next;
+ }
+
+ /*
+ * See if we have everything needed...
+ */
+
+ if (name == NULL)
+ break;
+
+ if (info == NULL || !info[0])
+ (*fn)(name, make_model);
+ else
+ (*fn)(name, info);
+
+
+ }
+
+ ippDelete(response);
+
+ /*
+ * Close the connection to the server...
+ */
+
+ httpClose(http);
+}
+
+
+/*
+ * 'cups_printername_ok()' - Provide the equivalent of pcap_printername_ok()
+ * for CUPS.
+ * O - 1 if printer name OK
+ * I - Name of printer
+ */
+int cups_printername_ok(const char *name)
+{
+ http_t *http; /* HTTP connection to server */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ cups_lang_t *language; /* Default language */
+ char uri[HTTP_MAX_URI]; /* printer-uri attribute */
+
+
+ DEBUG(5,("cups_printername_ok(\"%s\")\n", name));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(cups_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+ {
+ DEBUG(3,("Unable to connect to CUPS server %s - %s\n",
+ cupsServer(), strerror(errno)));
+ return (0);
+ }
+
+ /*
+ * Build an IPP_GET_PRINTER_ATTRS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * requested-attributes
+ * printer-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requested-attributes", NULL, "printer-uri");
+
+ slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s", name);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/")) == NULL)
+ {
+ DEBUG(3,("Unable to get printer status for %s - %s\n", name,
+ ippErrorString(cupsLastError())));
+ httpClose(http);
+ return (0);
+ }
+
+ httpClose(http);
+
+ if (response->request.status.status_code >= IPP_OK_CONFLICT)
+ {
+ DEBUG(3,("Unable to get printer status for %s - %s\n", name,
+ ippErrorString(response->request.status.status_code)));
+ ippDelete(response);
+ return (0);
+ }
+ else
+ {
+ ippDelete(response);
+ return (1);
+ }
+}
+
+
+/*
+ * 'cups_job_delete()' - Delete a job.
+ */
+
+static int
+cups_job_delete(int snum, struct printjob *pjob)
+{
+ int ret; /* Return value */
+ http_t *http; /* HTTP connection to server */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ cups_lang_t *language; /* Default language */
+ char uri[HTTP_MAX_URI]; /* printer-uri attribute */
+
+
+ DEBUG(5,("cups_job_delete(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(cups_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+ {
+ DEBUG(0,("Unable to connect to CUPS server %s - %s\n",
+ cupsServer(), strerror(errno)));
+ return (1);
+ }
+
+ /*
+ * Build an IPP_CANCEL_JOB request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * job-uri
+ * requesting-user-name
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_CANCEL_JOB;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ slprintf(uri, sizeof(uri) - 1, "ipp://localhost/jobs/%d", pjob->sysjob);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, pjob->user);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ ret = 1;
+
+ if ((response = cupsDoRequest(http, request, "/jobs")) != NULL)
+ {
+ if (response->request.status.status_code >= IPP_OK_CONFLICT)
+ DEBUG(0,("Unable to cancel job %d - %s\n", pjob->sysjob,
+ ippErrorString(cupsLastError())));
+ else
+ ret = 0;
+
+ ippDelete(response);
+ }
+ else
+ DEBUG(0,("Unable to cancel job %d - %s\n", pjob->sysjob,
+ ippErrorString(cupsLastError())));
+
+ httpClose(http);
+
+ return (ret);
+}
+
+
+/*
+ * 'cups_job_pause()' - Pause a job.
+ */
+
+static int
+cups_job_pause(int snum, struct printjob *pjob)
+{
+ int ret; /* Return value */
+ http_t *http; /* HTTP connection to server */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ cups_lang_t *language; /* Default language */
+ char uri[HTTP_MAX_URI]; /* printer-uri attribute */
+
+
+ DEBUG(5,("cups_job_pause(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(cups_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+ {
+ DEBUG(0,("Unable to connect to CUPS server %s - %s\n",
+ cupsServer(), strerror(errno)));
+ return (1);
+ }
+
+ /*
+ * Build an IPP_HOLD_JOB request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * job-uri
+ * requesting-user-name
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_HOLD_JOB;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ slprintf(uri, sizeof(uri) - 1, "ipp://localhost/jobs/%d", pjob->sysjob);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, pjob->user);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ ret = 1;
+
+ if ((response = cupsDoRequest(http, request, "/jobs")) != NULL)
+ {
+ if (response->request.status.status_code >= IPP_OK_CONFLICT)
+ DEBUG(0,("Unable to hold job %d - %s\n", pjob->sysjob,
+ ippErrorString(cupsLastError())));
+ else
+ ret = 0;
+
+ ippDelete(response);
+ }
+ else
+ DEBUG(0,("Unable to hold job %d - %s\n", pjob->sysjob,
+ ippErrorString(cupsLastError())));
+
+ httpClose(http);
+
+ return (ret);
+}
+
+
+/*
+ * 'cups_job_resume()' - Resume a paused job.
+ */
+
+static int
+cups_job_resume(int snum, struct printjob *pjob)
+{
+ int ret; /* Return value */
+ http_t *http; /* HTTP connection to server */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ cups_lang_t *language; /* Default language */
+ char uri[HTTP_MAX_URI]; /* printer-uri attribute */
+
+
+ DEBUG(5,("cups_job_resume(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(cups_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+ {
+ DEBUG(0,("Unable to connect to CUPS server %s - %s\n",
+ cupsServer(), strerror(errno)));
+ return (1);
+ }
+
+ /*
+ * Build an IPP_RELEASE_JOB request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * job-uri
+ * requesting-user-name
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_RELEASE_JOB;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ slprintf(uri, sizeof(uri) - 1, "ipp://localhost/jobs/%d", pjob->sysjob);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, pjob->user);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ ret = 1;
+
+ if ((response = cupsDoRequest(http, request, "/jobs")) != NULL)
+ {
+ if (response->request.status.status_code >= IPP_OK_CONFLICT)
+ DEBUG(0,("Unable to release job %d - %s\n", pjob->sysjob,
+ ippErrorString(cupsLastError())));
+ else
+ ret = 0;
+
+ ippDelete(response);
+ }
+ else
+ DEBUG(0,("Unable to release job %d - %s\n", pjob->sysjob,
+ ippErrorString(cupsLastError())));
+
+ httpClose(http);
+
+ return (ret);
+}
+
+
+/*
+ * 'cups_job_submit()' - Submit a job for printing.
+ */
+
+static int
+cups_job_submit(int snum, struct printjob *pjob)
+{
+ int ret; /* Return value */
+ http_t *http; /* HTTP connection to server */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ cups_lang_t *language; /* Default language */
+ char uri[HTTP_MAX_URI]; /* printer-uri attribute */
+ char *clientname; /* hostname of client for job-originating-host attribute */
+ pstring new_jobname;
+
+ DEBUG(5,("cups_job_submit(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(cups_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+ {
+ DEBUG(0,("Unable to connect to CUPS server %s - %s\n",
+ cupsServer(), strerror(errno)));
+ return (1);
+ }
+
+ /*
+ * Build an IPP_PRINT_JOB request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * requesting-user-name
+ * [document-data]
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_PRINT_JOB;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s",
+ PRINTERNAME(snum));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, pjob->user);
+
+ clientname = client_name();
+ if (strcmp(clientname, "UNKNOWN") == 0) {
+ clientname = client_addr();
+ }
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "job-originating-host-name", NULL,
+ clientname);
+
+ pstr_sprintf(new_jobname,"%s%.8u %s", PRINT_SPOOL_PREFIX,
+ (unsigned int)pjob->smbjob, pjob->jobname);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
+ new_jobname);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ slprintf(uri, sizeof(uri) - 1, "/printers/%s", PRINTERNAME(snum));
+
+ ret = 1;
+ if ((response = cupsDoFileRequest(http, request, uri,
+ pjob->filename)) != NULL)
+ {
+ if (response->request.status.status_code >= IPP_OK_CONFLICT)
+ DEBUG(0,("Unable to print file to %s - %s\n", PRINTERNAME(snum),
+ ippErrorString(cupsLastError())));
+ else
+ ret = 0;
+
+ ippDelete(response);
+ }
+ else
+ DEBUG(0,("Unable to print file to `%s' - %s\n", PRINTERNAME(snum),
+ ippErrorString(cupsLastError())));
+
+ httpClose(http);
+
+ if ( ret == 0 )
+ unlink(pjob->filename);
+ /* else print_job_end will do it for us */
+
+ return (ret);
+}
+
+
+/*
+ * 'cups_queue_get()' - Get all the jobs in the print queue.
+ */
+
+static int
+cups_queue_get(int snum, print_queue_struct **q, print_status_struct *status)
+{
+ http_t *http; /* HTTP connection to server */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ cups_lang_t *language; /* Default language */
+ char uri[HTTP_MAX_URI]; /* printer-uri attribute */
+ int qcount, /* Number of active queue entries */
+ qalloc; /* Number of queue entries allocated */
+ print_queue_struct *queue, /* Queue entries */
+ *temp; /* Temporary pointer for queue */
+ const char *user_name, /* job-originating-user-name attribute */
+ *job_name; /* job-name attribute */
+ int job_id; /* job-id attribute */
+ int job_k_octets; /* job-k-octets attribute */
+ time_t job_time; /* time-at-creation attribute */
+ ipp_jstate_t job_status; /* job-status attribute */
+ int job_priority; /* job-priority attribute */
+ static const char *jattrs[] = /* Requested job attributes */
+ {
+ "job-id",
+ "job-k-octets",
+ "job-name",
+ "job-originating-user-name",
+ "job-priority",
+ "job-state",
+ "time-at-creation",
+ };
+ static const char *pattrs[] = /* Requested printer attributes */
+ {
+ "printer-state",
+ "printer-state-message"
+ };
+
+
+ DEBUG(5,("cups_queue_get(%d, %p, %p)\n", snum, q, status));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(cups_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+ {
+ DEBUG(0,("Unable to connect to CUPS server %s - %s\n",
+ cupsServer(), strerror(errno)));
+ return (0);
+ }
+
+ /*
+ * Generate the printer URI...
+ */
+
+ slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s",
+ PRINTERNAME(snum));
+
+ /*
+ * Build an IPP_GET_JOBS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * requested-attributes
+ * printer-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_GET_JOBS;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requested-attributes",
+ (sizeof(jattrs) / sizeof(jattrs[0])),
+ NULL, jattrs);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/")) == NULL)
+ {
+ DEBUG(0,("Unable to get jobs for %s - %s\n", uri,
+ ippErrorString(cupsLastError())));
+ httpClose(http);
+ return (0);
+ }
+
+ if (response->request.status.status_code >= IPP_OK_CONFLICT)
+ {
+ DEBUG(0,("Unable to get jobs for %s - %s\n", uri,
+ ippErrorString(response->request.status.status_code)));
+ ippDelete(response);
+ httpClose(http);
+
+ return (0);
+ }
+
+ /*
+ * Process the jobs...
+ */
+
+ qcount = 0;
+ qalloc = 0;
+ queue = NULL;
+
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ {
+ /*
+ * Skip leading attributes until we hit a job...
+ */
+
+ while (attr != NULL && attr->group_tag != IPP_TAG_JOB)
+ attr = attr->next;
+
+ if (attr == NULL)
+ break;
+
+ /*
+ * Allocate memory as needed...
+ */
+ if (qcount >= qalloc)
+ {
+ qalloc += 16;
+
+ temp = Realloc(queue, sizeof(print_queue_struct) * qalloc);
+
+ if (temp == NULL)
+ {
+ DEBUG(0,("cups_queue_get: Not enough memory!"));
+ ippDelete(response);
+ httpClose(http);
+
+ SAFE_FREE(queue);
+ return (0);
+ }
+
+ queue = temp;
+ }
+
+ temp = queue + qcount;
+ memset(temp, 0, sizeof(print_queue_struct));
+
+ /*
+ * Pull the needed attributes from this job...
+ */
+
+ job_id = 0;
+ job_priority = 50;
+ job_status = IPP_JOB_PENDING;
+ job_time = 0;
+ job_k_octets = 0;
+ user_name = NULL;
+ job_name = NULL;
+
+ while (attr != NULL && attr->group_tag == IPP_TAG_JOB)
+ {
+ if (attr->name == NULL)
+ {
+ attr = attr->next;
+ break;
+ }
+
+ if (strcmp(attr->name, "job-id") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ job_id = attr->values[0].integer;
+
+ if (strcmp(attr->name, "job-k-octets") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ job_k_octets = attr->values[0].integer;
+
+ if (strcmp(attr->name, "job-priority") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ job_priority = attr->values[0].integer;
+
+ if (strcmp(attr->name, "job-state") == 0 &&
+ attr->value_tag == IPP_TAG_ENUM)
+ job_status = (ipp_jstate_t)(attr->values[0].integer);
+
+ if (strcmp(attr->name, "time-at-creation") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ job_time = attr->values[0].integer;
+
+ if (strcmp(attr->name, "job-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ job_name = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "job-originating-user-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ user_name = attr->values[0].string.text;
+
+ attr = attr->next;
+ }
+
+ /*
+ * See if we have everything needed...
+ */
+
+ if (user_name == NULL || job_name == NULL || job_id == 0)
+ {
+ if (attr == NULL)
+ break;
+ else
+ continue;
+ }
+
+ temp->job = job_id;
+ temp->size = job_k_octets * 1024;
+ temp->status = job_status == IPP_JOB_PENDING ? LPQ_QUEUED :
+ job_status == IPP_JOB_STOPPED ? LPQ_PAUSED :
+ job_status == IPP_JOB_HELD ? LPQ_PAUSED :
+ LPQ_PRINTING;
+ temp->priority = job_priority;
+ temp->time = job_time;
+ strncpy(temp->fs_user, user_name, sizeof(temp->fs_user) - 1);
+ strncpy(temp->fs_file, job_name, sizeof(temp->fs_file) - 1);
+
+ qcount ++;
+
+ if (attr == NULL)
+ break;
+ }
+
+ ippDelete(response);
+
+ /*
+ * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
+ * following attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * requested-attributes
+ * printer-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requested-attributes",
+ (sizeof(pattrs) / sizeof(pattrs[0])),
+ NULL, pattrs);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/")) == NULL)
+ {
+ DEBUG(0,("Unable to get printer status for %s - %s\n", PRINTERNAME(snum),
+ ippErrorString(cupsLastError())));
+ httpClose(http);
+ *q = queue;
+ return (qcount);
+ }
+
+ if (response->request.status.status_code >= IPP_OK_CONFLICT)
+ {
+ DEBUG(0,("Unable to get printer status for %s - %s\n", PRINTERNAME(snum),
+ ippErrorString(response->request.status.status_code)));
+ ippDelete(response);
+ httpClose(http);
+ *q = queue;
+ return (qcount);
+ }
+
+ /*
+ * Get the current printer status and convert it to the SAMBA values.
+ */
+
+ if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL)
+ {
+ if (attr->values[0].integer == IPP_PRINTER_STOPPED)
+ status->status = LPSTAT_STOPPED;
+ else
+ status->status = LPSTAT_OK;
+ }
+
+ if ((attr = ippFindAttribute(response, "printer-state-message",
+ IPP_TAG_TEXT)) != NULL)
+ fstrcpy(status->message, attr->values[0].string.text);
+
+ ippDelete(response);
+
+ /*
+ * Return the job queue...
+ */
+
+ httpClose(http);
+
+ *q = queue;
+ return (qcount);
+}
+
+
+/*
+ * 'cups_queue_pause()' - Pause a print queue.
+ */
+
+static int
+cups_queue_pause(int snum)
+{
+ extern userdom_struct current_user_info;
+ int ret; /* Return value */
+ http_t *http; /* HTTP connection to server */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ cups_lang_t *language; /* Default language */
+ char uri[HTTP_MAX_URI]; /* printer-uri attribute */
+
+
+ DEBUG(5,("cups_queue_pause(%d)\n", snum));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(cups_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+ {
+ DEBUG(0,("Unable to connect to CUPS server %s - %s\n",
+ cupsServer(), strerror(errno)));
+ return (1);
+ }
+
+ /*
+ * Build an IPP_PAUSE_PRINTER request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * requesting-user-name
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_PAUSE_PRINTER;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s",
+ PRINTERNAME(snum));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, current_user_info.unix_name);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ ret = 1;
+
+ if ((response = cupsDoRequest(http, request, "/admin/")) != NULL)
+ {
+ if (response->request.status.status_code >= IPP_OK_CONFLICT)
+ DEBUG(0,("Unable to pause printer %s - %s\n", PRINTERNAME(snum),
+ ippErrorString(cupsLastError())));
+ else
+ ret = 0;
+
+ ippDelete(response);
+ }
+ else
+ DEBUG(0,("Unable to pause printer %s - %s\n", PRINTERNAME(snum),
+ ippErrorString(cupsLastError())));
+
+ httpClose(http);
+
+ return (ret);
+}
+
+
+/*
+ * 'cups_queue_resume()' - Restart a print queue.
+ */
+
+static int
+cups_queue_resume(int snum)
+{
+ extern userdom_struct current_user_info;
+ int ret; /* Return value */
+ http_t *http; /* HTTP connection to server */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ cups_lang_t *language; /* Default language */
+ char uri[HTTP_MAX_URI]; /* printer-uri attribute */
+
+
+ DEBUG(5,("cups_queue_resume(%d)\n", snum));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(cups_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+ {
+ DEBUG(0,("Unable to connect to CUPS server %s - %s\n",
+ cupsServer(), strerror(errno)));
+ return (1);
+ }
+
+ /*
+ * Build an IPP_RESUME_PRINTER request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * requesting-user-name
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_RESUME_PRINTER;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s",
+ PRINTERNAME(snum));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, current_user_info.unix_name);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ ret = 1;
+
+ if ((response = cupsDoRequest(http, request, "/admin/")) != NULL)
+ {
+ if (response->request.status.status_code >= IPP_OK_CONFLICT)
+ DEBUG(0,("Unable to resume printer %s - %s\n", PRINTERNAME(snum),
+ ippErrorString(cupsLastError())));
+ else
+ ret = 0;
+
+ ippDelete(response);
+ }
+ else
+ DEBUG(0,("Unable to resume printer %s - %s\n", PRINTERNAME(snum),
+ ippErrorString(cupsLastError())));
+
+ httpClose(http);
+
+ return (ret);
+}
+
+
+#else
+ /* this keeps fussy compilers happy */
+ void print_cups_dummy(void) {}
+#endif /* HAVE_CUPS */
diff --git a/source/printing/print_generic.c b/source/printing/print_generic.c
new file mode 100644
index 00000000000..9e0ea85bb9a
--- /dev/null
+++ b/source/printing/print_generic.c
@@ -0,0 +1,247 @@
+/*
+ Unix SMB/CIFS implementation.
+ printing command routines
+ Copyright (C) Andrew Tridgell 1992-2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "printing.h"
+
+
+/*
+ * Generic printing interface definitions...
+ */
+
+static int generic_job_delete(int snum, struct printjob *pjob);
+static int generic_job_pause(int snum, struct printjob *pjob);
+static int generic_job_resume(int snum, struct printjob *pjob);
+static int generic_job_submit(int snum, struct printjob *pjob);
+static int generic_queue_get(int snum, print_queue_struct **q,
+ print_status_struct *status);
+static int generic_queue_pause(int snum);
+static int generic_queue_resume(int snum);
+
+
+struct printif generic_printif =
+ {
+ generic_queue_get,
+ generic_queue_pause,
+ generic_queue_resume,
+ generic_job_delete,
+ generic_job_pause,
+ generic_job_resume,
+ generic_job_submit,
+ };
+
+/****************************************************************************
+run a given print command
+a null terminated list of value/substitute pairs is provided
+for local substitution strings
+****************************************************************************/
+static int print_run_command(int snum,char *command, int *outfd, ...)
+{
+
+ pstring syscmd;
+ char *arg;
+ int ret;
+ va_list ap;
+ va_start(ap, outfd);
+
+ if (!command || !*command) return -1;
+
+ if (!VALID_SNUM(snum)) {
+ DEBUG(0,("Invalid snum %d for command %s\n", snum, command));
+ return -1;
+ }
+
+ pstrcpy(syscmd, command);
+
+ while ((arg = va_arg(ap, char *))) {
+ char *value = va_arg(ap,char *);
+ pstring_sub(syscmd, arg, value);
+ }
+ va_end(ap);
+
+ pstring_sub(syscmd, "%p", PRINTERNAME(snum));
+ standard_sub_snum(snum,syscmd,sizeof(syscmd));
+
+ ret = smbrun(syscmd,outfd);
+
+ DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
+
+ return ret;
+}
+
+
+/****************************************************************************
+delete a print job
+****************************************************************************/
+static int generic_job_delete(int snum, struct printjob *pjob)
+{
+ fstring jobstr;
+
+ /* need to delete the spooled entry */
+ slprintf(jobstr, sizeof(jobstr)-1, "%d", pjob->sysjob);
+ return print_run_command(
+ snum,
+ lp_lprmcommand(snum), NULL,
+ "%j", jobstr,
+ "%T", http_timestring(pjob->starttime),
+ NULL);
+}
+
+/****************************************************************************
+pause a job
+****************************************************************************/
+static int generic_job_pause(int snum, struct printjob *pjob)
+{
+ fstring jobstr;
+
+ /* need to pause the spooled entry */
+ slprintf(jobstr, sizeof(jobstr)-1, "%d", pjob->sysjob);
+ return print_run_command(snum,
+ lp_lppausecommand(snum), NULL,
+ "%j", jobstr,
+ NULL);
+}
+
+/****************************************************************************
+resume a job
+****************************************************************************/
+static int generic_job_resume(int snum, struct printjob *pjob)
+{
+ fstring jobstr;
+
+ /* need to pause the spooled entry */
+ slprintf(jobstr, sizeof(jobstr)-1, "%d", pjob->sysjob);
+ return print_run_command(snum,
+ lp_lpresumecommand(snum), NULL,
+ "%j", jobstr,
+ NULL);
+}
+
+/****************************************************************************
+ Submit a file for printing - called from print_job_end()
+****************************************************************************/
+
+static int generic_job_submit(int snum, struct printjob *pjob)
+{
+ int ret;
+ pstring current_directory;
+ pstring print_directory;
+ char *wd, *p;
+ pstring jobname;
+ fstring job_page_count, job_size;
+
+ /* we print from the directory path to give the best chance of
+ parsing the lpq output */
+ wd = sys_getwd(current_directory);
+ if (!wd)
+ return 0;
+
+ pstrcpy(print_directory, pjob->filename);
+ p = strrchr_m(print_directory,'/');
+ if (!p)
+ return 0;
+ *p++ = 0;
+
+ if (chdir(print_directory) != 0)
+ return 0;
+
+ pstrcpy(jobname, pjob->jobname);
+ pstring_sub(jobname, "'", "_");
+ slprintf(job_page_count, sizeof(job_page_count)-1, "%d", pjob->page_count);
+ slprintf(job_size, sizeof(job_size)-1, "%lu", (unsigned long)pjob->size);
+
+ /* send it to the system spooler */
+ ret = print_run_command(snum,
+ lp_printcommand(snum), NULL,
+ "%s", p,
+ "%J", jobname,
+ "%f", p,
+ "%z", job_size,
+ "%c", job_page_count,
+ NULL);
+
+ chdir(wd);
+
+ return ret;
+}
+
+
+/****************************************************************************
+get the current list of queued jobs
+****************************************************************************/
+static int generic_queue_get(int snum, print_queue_struct **q, print_status_struct *status)
+{
+ char **qlines;
+ int fd;
+ int numlines, i, qcount;
+ print_queue_struct *queue = NULL;
+ fstring printer_name;
+
+ fstrcpy(printer_name, lp_servicename(snum));
+
+ print_run_command(snum, lp_lpqcommand(snum), &fd, NULL);
+
+ if (fd == -1) {
+ DEBUG(5,("generic_queue_get: Can't read print queue status for printer %s\n",
+ printer_name ));
+ return 0;
+ }
+
+ numlines = 0;
+ qlines = fd_lines_load(fd, &numlines);
+ close(fd);
+
+ /* turn the lpq output into a series of job structures */
+ qcount = 0;
+ ZERO_STRUCTP(status);
+ if (numlines)
+ queue = (print_queue_struct *)malloc(sizeof(print_queue_struct)*(numlines+1));
+
+ if (queue) {
+ memset(queue, '\0', sizeof(print_queue_struct)*(numlines+1));
+ for (i=0; i<numlines; i++) {
+ /* parse the line */
+ if (parse_lpq_entry(snum,qlines[i],
+ &queue[qcount],status,qcount==0)) {
+ qcount++;
+ }
+ }
+ }
+ file_lines_free(qlines);
+
+ *q = queue;
+ return qcount;
+}
+
+/****************************************************************************
+ pause a queue
+****************************************************************************/
+static int generic_queue_pause(int snum)
+{
+ return print_run_command(snum, lp_queuepausecommand(snum), NULL, NULL);
+}
+
+/****************************************************************************
+ resume a queue
+****************************************************************************/
+static int generic_queue_resume(int snum)
+{
+ return print_run_command(snum, lp_queueresumecommand(snum), NULL, NULL);
+}
diff --git a/source/printing/print_svid.c b/source/printing/print_svid.c
new file mode 100644
index 00000000000..07b157bcd95
--- /dev/null
+++ b/source/printing/print_svid.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 1997-1998 by Norm Jacobs, Colorado Springs, Colorado, USA
+ * Copyright (C) 1997-1998 by Sun Microsystem, Inc.
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * This module implements support for gathering and comparing available
+ * printer information on a SVID or XPG4 compliant system. It does this
+ * through the use of the SVID/XPG4 command "lpstat(1)".
+ *
+ * The expectations is that execution of the command "lpstat -v" will
+ * generate responses in the form of:
+ *
+ * device for serial: /dev/term/b
+ * system for fax: server
+ * system for color: server (as printer chroma)
+ */
+
+
+#include "includes.h"
+
+#ifdef SYSV
+
+typedef struct printer {
+ char *name;
+ struct printer *next;
+} printer_t;
+static printer_t *printers = NULL;
+
+static void populate_printers(void)
+{
+ char **lines;
+ int i;
+
+ lines = file_lines_pload("/usr/bin/lpstat -v", NULL);
+ if (!lines) return;
+
+ for (i=0;lines[i];i++) {
+ printer_t *ptmp;
+ char *name, *tmp;
+ char *buf = lines[i];
+
+ /* eat "system/device for " */
+ if (((tmp = strchr_m(buf, ' ')) == NULL) ||
+ ((tmp = strchr_m(++tmp, ' ')) == NULL))
+ continue;
+
+ /*
+ * In case we're only at the "for ".
+ */
+
+ if(!strncmp("for ",++tmp,4)) {
+ tmp=strchr_m(tmp, ' ');
+ tmp++;
+ }
+
+ /* Eat whitespace. */
+
+ while(*tmp == ' ')
+ ++tmp;
+
+ /*
+ * On HPUX there is an extra line that can be ignored.
+ * d.thibadeau 2001/08/09
+ */
+ if(!strncmp("remote to",tmp,9))
+ continue;
+
+ name = tmp;
+
+ /* truncate the ": ..." */
+ if ((tmp = strchr_m(name, ':')) != NULL)
+ *tmp = '\0';
+
+ /* add it to the cache */
+ if ((ptmp = malloc(sizeof (*ptmp))) != NULL) {
+ ZERO_STRUCTP(ptmp);
+ if((ptmp->name = strdup(name)) == NULL)
+ DEBUG(0,("populate_printers: malloc fail in strdup !\n"));
+ ptmp->next = printers;
+ printers = ptmp;
+ } else {
+ DEBUG(0,("populate_printers: malloc fail for ptmp\n"));
+ }
+ }
+
+ file_lines_free(lines);
+}
+
+
+/*
+ * provide the equivalent of pcap_printer_fn() for SVID/XPG4 conforming
+ * systems. It was unclear why pcap_printer_fn() was tossing names longer
+ * than 8 characters. I suspect that its a protocol limit, but amazingly
+ * names longer than 8 characters appear to work with my test
+ * clients (Win95/NT).
+ */
+void sysv_printer_fn(void (*fn)(char *, char *))
+{
+ printer_t *tmp;
+
+ if (printers == NULL)
+ populate_printers();
+ for (tmp = printers; tmp != NULL; tmp = tmp->next)
+ (fn)(tmp->name, "");
+}
+
+
+/*
+ * provide the equivalent of pcap_printername_ok() for SVID/XPG4 conforming
+ * systems.
+ */
+int sysv_printername_ok(const char *name)
+{
+ printer_t *tmp;
+
+ if (printers == NULL)
+ populate_printers();
+ for (tmp = printers; tmp != NULL; tmp = tmp->next)
+ if (strcmp(tmp->name, name) == 0)
+ return (True);
+ return (False);
+}
+
+#else
+/* this keeps fussy compilers happy */
+ void print_svid_dummy(void);
+ void print_svid_dummy(void) {}
+#endif
diff --git a/source/printing/printfsp.c b/source/printing/printfsp.c
new file mode 100644
index 00000000000..0b6d4fdbe1c
--- /dev/null
+++ b/source/printing/printfsp.c
@@ -0,0 +1,120 @@
+/*
+ Unix SMB/CIFS implementation.
+ printing backend routines for smbd - using files_struct rather
+ than only snum
+ Copyright (C) Andrew Tridgell 1992-2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/***************************************************************************
+open a print file and setup a fsp for it. This is a wrapper around
+print_job_start().
+***************************************************************************/
+
+files_struct *print_fsp_open(connection_struct *conn, char *fname)
+{
+ int jobid;
+ SMB_STRUCT_STAT sbuf;
+ extern struct current_user current_user;
+ files_struct *fsp = file_new(conn);
+ fstring name;
+
+ if(!fsp)
+ return NULL;
+
+ fstrcpy( name, "Remote Downlevel Document");
+ if (fname) {
+ char *p = strrchr(fname, '/');
+ fstrcat(name, " ");
+ if (!p)
+ p = fname;
+ fstrcat(name, p);
+ }
+
+ jobid = print_job_start(&current_user, SNUM(conn), name, NULL);
+ if (jobid == -1) {
+ file_free(fsp);
+ return NULL;
+ }
+
+ /* Convert to RAP id. */
+ fsp->rap_print_jobid = pjobid_to_rap(SNUM(conn), jobid);
+ if (fsp->rap_print_jobid == 0) {
+ /* We need to delete the entry in the tdb. */
+ pjob_delete(SNUM(conn), jobid);
+ file_free(fsp);
+ return NULL;
+ }
+
+ /* setup a full fsp */
+ fsp->fd = print_job_fd(SNUM(conn),jobid);
+ GetTimeOfDay(&fsp->open_time);
+ fsp->vuid = current_user.vuid;
+ fsp->size = 0;
+ fsp->pos = -1;
+ fsp->can_lock = True;
+ fsp->can_read = False;
+ fsp->can_write = True;
+ fsp->share_mode = 0;
+ fsp->print_file = True;
+ fsp->modified = False;
+ fsp->oplock_type = NO_OPLOCK;
+ fsp->sent_oplock_break = NO_BREAK_SENT;
+ fsp->is_directory = False;
+ fsp->directory_delete_on_close = False;
+ string_set(&fsp->fsp_name,print_job_fname(SNUM(conn),jobid));
+ fsp->wbmpx_ptr = NULL;
+ fsp->wcp = NULL;
+ SMB_VFS_FSTAT(fsp,fsp->fd, &sbuf);
+ fsp->mode = sbuf.st_mode;
+ fsp->inode = sbuf.st_ino;
+ fsp->dev = sbuf.st_dev;
+
+ conn->num_files_open++;
+
+ return fsp;
+}
+
+/****************************************************************************
+print a file - called on closing the file
+****************************************************************************/
+void print_fsp_end(files_struct *fsp, BOOL normal_close)
+{
+ uint32 jobid;
+ int snum;
+
+ if (fsp->share_mode == FILE_DELETE_ON_CLOSE) {
+ /*
+ * Truncate the job. print_job_end will take
+ * care of deleting it for us. JRA.
+ */
+ sys_ftruncate(fsp->fd, 0);
+ }
+
+ if (fsp->fsp_name) {
+ string_free(&fsp->fsp_name);
+ }
+
+ if (!rap_to_pjobid(fsp->rap_print_jobid, &snum, &jobid)) {
+ DEBUG(3,("print_fsp_end: Unable to convert RAP jobid %u to print jobid.\n",
+ (unsigned int)fsp->rap_print_jobid ));
+ return;
+ }
+
+ print_job_end(SNUM(fsp->conn),jobid, normal_close);
+}
diff --git a/source/printing/printing.c b/source/printing/printing.c
new file mode 100644
index 00000000000..e4ef1f52d0e
--- /dev/null
+++ b/source/printing/printing.c
@@ -0,0 +1,2420 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ printing backend routines
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Jeremy Allison 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "printing.h"
+
+/* Current printer interface */
+static BOOL remove_from_jobs_changed(int snum, uint32 jobid);
+
+/*
+ the printing backend revolves around a tdb database that stores the
+ SMB view of the print queue
+
+ The key for this database is a jobid - a internally generated number that
+ uniquely identifies a print job
+
+ reading the print queue involves two steps:
+ - possibly running lpq and updating the internal database from that
+ - reading entries from the database
+
+ jobids are assigned when a job starts spooling.
+*/
+
+/***************************************************************************
+ Nightmare. LANMAN jobid's are 16 bit numbers..... We must map them to 32
+ bit RPC jobids.... JRA.
+***************************************************************************/
+
+static TDB_CONTEXT *rap_tdb;
+static uint16 next_rap_jobid;
+
+uint16 pjobid_to_rap(int snum, uint32 jobid)
+{
+ uint16 rap_jobid;
+ TDB_DATA data, key;
+ char jinfo[8];
+
+ DEBUG(10,("pjobid_to_rap: called.\n"));
+
+ if (!rap_tdb) {
+ /* Create the in-memory tdb. */
+ rap_tdb = tdb_open_log(NULL, 0, TDB_INTERNAL, (O_RDWR|O_CREAT), 0644);
+ if (!rap_tdb)
+ return 0;
+ }
+
+ SIVAL(&jinfo,0,(int32)snum);
+ SIVAL(&jinfo,4,jobid);
+
+ key.dptr = (char *)&jinfo;
+ key.dsize = sizeof(jinfo);
+ data = tdb_fetch(rap_tdb, key);
+ if (data.dptr && data.dsize == sizeof(uint16)) {
+ rap_jobid = SVAL(data.dptr, 0);
+ SAFE_FREE(data.dptr);
+ DEBUG(10,("pjobid_to_rap: jobid %u maps to RAP jobid %u\n",
+ (unsigned int)jobid,
+ (unsigned int)rap_jobid));
+ return rap_jobid;
+ }
+ SAFE_FREE(data.dptr);
+ /* Not found - create and store mapping. */
+ rap_jobid = ++next_rap_jobid;
+ if (rap_jobid == 0)
+ rap_jobid = ++next_rap_jobid;
+ data.dptr = (char *)&rap_jobid;
+ data.dsize = sizeof(rap_jobid);
+ tdb_store(rap_tdb, key, data, TDB_REPLACE);
+ tdb_store(rap_tdb, data, key, TDB_REPLACE);
+
+ DEBUG(10,("pjobid_to_rap: created jobid %u maps to RAP jobid %u\n",
+ (unsigned int)jobid,
+ (unsigned int)rap_jobid));
+ return rap_jobid;
+}
+
+BOOL rap_to_pjobid(uint16 rap_jobid, int *psnum, uint32 *pjobid)
+{
+ TDB_DATA data, key;
+
+ DEBUG(10,("rap_to_pjobid called.\n"));
+
+ if (!rap_tdb)
+ return False;
+
+ key.dptr = (char *)&rap_jobid;
+ key.dsize = sizeof(rap_jobid);
+ data = tdb_fetch(rap_tdb, key);
+ if (data.dptr && data.dsize == 8) {
+ *psnum = IVAL(data.dptr,0);
+ *pjobid = IVAL(data.dptr,4);
+ DEBUG(10,("rap_to_pjobid: jobid %u maps to RAP jobid %u\n",
+ (unsigned int)*pjobid,
+ (unsigned int)rap_jobid));
+ SAFE_FREE(data.dptr);
+ return True;
+ }
+
+ DEBUG(10,("rap_to_pjobid: Failed to lookup RAP jobid %u\n",
+ (unsigned int)rap_jobid));
+ SAFE_FREE(data.dptr);
+ return False;
+}
+
+static void rap_jobid_delete(int snum, uint32 jobid)
+{
+ TDB_DATA key, data;
+ uint16 rap_jobid;
+ char jinfo[8];
+
+ DEBUG(10,("rap_jobid_delete: called.\n"));
+
+ if (!rap_tdb)
+ return;
+
+ SIVAL(&jinfo,0,(int32)snum);
+ SIVAL(&jinfo,4,jobid);
+
+ key.dptr = (char *)&jinfo;
+ key.dsize = sizeof(jinfo);
+ data = tdb_fetch(rap_tdb, key);
+ if (!data.dptr || (data.dsize != sizeof(uint16))) {
+ DEBUG(10,("rap_jobid_delete: cannot find jobid %u\n",
+ (unsigned int)jobid ));
+ SAFE_FREE(data.dptr);
+ return;
+ }
+
+ DEBUG(10,("rap_jobid_delete: deleting jobid %u\n",
+ (unsigned int)jobid ));
+
+ rap_jobid = SVAL(data.dptr, 0);
+ SAFE_FREE(data.dptr);
+ data.dptr = (char *)&rap_jobid;
+ data.dsize = sizeof(rap_jobid);
+ tdb_delete(rap_tdb, key);
+ tdb_delete(rap_tdb, data);
+}
+
+static pid_t local_pid;
+
+static int get_queue_status(int, print_status_struct *);
+
+/****************************************************************************
+ Initialise the printing backend. Called once at startup before the fork().
+****************************************************************************/
+
+BOOL print_backend_init(void)
+{
+ const char *sversion = "INFO/version";
+ pstring printing_path;
+ int services = lp_numservices();
+ int snum;
+
+ if (local_pid == sys_getpid())
+ return True;
+
+ unlink(lock_path("printing.tdb"));
+ pstrcpy(printing_path,lock_path("printing"));
+ mkdir(printing_path,0755);
+
+ local_pid = sys_getpid();
+
+ /* handle a Samba upgrade */
+
+ for (snum = 0; snum < services; snum++) {
+ struct tdb_print_db *pdb;
+ if (!lp_print_ok(snum))
+ continue;
+
+ pdb = get_print_db_byname(lp_const_servicename(snum));
+ if (!pdb)
+ continue;
+ if (tdb_lock_bystring(pdb->tdb, sversion, 0) == -1) {
+ DEBUG(0,("print_backend_init: Failed to open printer %s database\n", lp_const_servicename(snum) ));
+ release_print_db(pdb);
+ return False;
+ }
+ if (tdb_fetch_int32(pdb->tdb, sversion) != PRINT_DATABASE_VERSION) {
+ tdb_traverse(pdb->tdb, tdb_traverse_delete_fn, NULL);
+ tdb_store_int32(pdb->tdb, sversion, PRINT_DATABASE_VERSION);
+ }
+ tdb_unlock_bystring(pdb->tdb, sversion);
+ release_print_db(pdb);
+ }
+
+ close_all_print_db(); /* Don't leave any open. */
+
+ /* do NT print initialization... */
+ return nt_printing_init();
+}
+
+/****************************************************************************
+ Shut down printing backend. Called once at shutdown to close the tdb.
+****************************************************************************/
+
+void printing_end(void)
+{
+ close_all_print_db(); /* Don't leave any open. */
+}
+
+/****************************************************************************
+ Retrieve the set of printing functions for a given service. This allows
+ us to set the printer function table based on the value of the 'printing'
+ service parameter.
+
+ Use the generic interface as the default and only use cups interface only
+ when asked for (and only when supported)
+****************************************************************************/
+
+static struct printif *get_printer_fns( int snum )
+{
+ struct printif *printer_fns = &generic_printif;
+
+#ifdef HAVE_CUPS
+ if ( lp_printing(snum) == PRINT_CUPS ) {
+ printer_fns = &cups_printif;
+ }
+#endif /* HAVE_CUPS */
+
+ return printer_fns;
+}
+
+/****************************************************************************
+ Useful function to generate a tdb key.
+****************************************************************************/
+
+static TDB_DATA print_key(uint32 jobid)
+{
+ static uint32 j;
+ TDB_DATA ret;
+
+ j = jobid;
+ ret.dptr = (void *)&j;
+ ret.dsize = sizeof(j);
+ return ret;
+}
+
+/***********************************************************************
+ unpack a pjob from a tdb buffer
+***********************************************************************/
+
+int unpack_pjob( char* buf, int buflen, struct printjob *pjob )
+{
+ int len = 0;
+ int used;
+ uint32 pjpid, pjsysjob, pjfd, pjstarttime, pjstatus;
+ uint32 pjsize, pjpage_count, pjspooled, pjsmbjob;
+
+ if ( !buf || !pjob )
+ return -1;
+
+ len += tdb_unpack(buf+len, buflen-len, "dddddddddffff",
+ &pjpid,
+ &pjsysjob,
+ &pjfd,
+ &pjstarttime,
+ &pjstatus,
+ &pjsize,
+ &pjpage_count,
+ &pjspooled,
+ &pjsmbjob,
+ pjob->filename,
+ pjob->jobname,
+ pjob->user,
+ pjob->queuename);
+
+ if ( len == -1 )
+ return -1;
+
+ if ( (used = unpack_devicemode(&pjob->nt_devmode, buf+len, buflen-len)) == -1 )
+ return -1;
+
+ len += used;
+
+ pjob->pid = pjpid;
+ pjob->sysjob = pjsysjob;
+ pjob->fd = pjfd;
+ pjob->starttime = pjstarttime;
+ pjob->status = pjstatus;
+ pjob->size = pjsize;
+ pjob->page_count = pjpage_count;
+ pjob->spooled = pjspooled;
+ pjob->smbjob = pjsmbjob;
+
+ return len;
+
+}
+
+/****************************************************************************
+ Useful function to find a print job in the database.
+****************************************************************************/
+
+static struct printjob *print_job_find(int snum, uint32 jobid)
+{
+ static struct printjob pjob;
+ TDB_DATA ret;
+ struct tdb_print_db *pdb = get_print_db_byname(lp_const_servicename(snum));
+
+
+ if (!pdb)
+ return NULL;
+
+ ret = tdb_fetch(pdb->tdb, print_key(jobid));
+ release_print_db(pdb);
+
+ if (!ret.dptr)
+ return NULL;
+
+ if ( pjob.nt_devmode )
+ free_nt_devicemode( &pjob.nt_devmode );
+
+ ZERO_STRUCT( pjob );
+
+ if ( unpack_pjob( ret.dptr, ret.dsize, &pjob ) == -1 ) {
+ SAFE_FREE(ret.dptr);
+ return NULL;
+ }
+
+ SAFE_FREE(ret.dptr);
+ return &pjob;
+}
+
+/* Convert a unix jobid to a smb jobid */
+
+static uint32 sysjob_to_jobid_value;
+
+static int unixjob_traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA key,
+ TDB_DATA data, void *state)
+{
+ struct printjob *pjob;
+ int *sysjob = (int *)state;
+
+ if (!data.dptr || data.dsize == 0)
+ return 0;
+
+ pjob = (struct printjob *)data.dptr;
+ if (key.dsize != sizeof(uint32))
+ return 0;
+
+ if (*sysjob == pjob->sysjob) {
+ uint32 *jobid = (uint32 *)key.dptr;
+
+ sysjob_to_jobid_value = *jobid;
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ This is a *horribly expensive call as we have to iterate through all the
+ current printer tdb's. Don't do this often ! JRA.
+****************************************************************************/
+
+uint32 sysjob_to_jobid(int unix_jobid)
+{
+ int services = lp_numservices();
+ int snum;
+
+ sysjob_to_jobid_value = (uint32)-1;
+
+ for (snum = 0; snum < services; snum++) {
+ struct tdb_print_db *pdb;
+ if (!lp_print_ok(snum))
+ continue;
+ pdb = get_print_db_byname(lp_const_servicename(snum));
+ if (pdb)
+ tdb_traverse(pdb->tdb, unixjob_traverse_fn, &unix_jobid);
+ release_print_db(pdb);
+ if (sysjob_to_jobid_value != (uint32)-1)
+ return sysjob_to_jobid_value;
+ }
+ return (uint32)-1;
+}
+
+/****************************************************************************
+ Send notifications based on what has changed after a pjob_store.
+****************************************************************************/
+
+static struct {
+ uint32 lpq_status;
+ uint32 spoolss_status;
+} lpq_to_spoolss_status_map[] = {
+ { LPQ_QUEUED, JOB_STATUS_QUEUED },
+ { LPQ_PAUSED, JOB_STATUS_PAUSED },
+ { LPQ_SPOOLING, JOB_STATUS_SPOOLING },
+ { LPQ_PRINTING, JOB_STATUS_PRINTING },
+ { LPQ_DELETING, JOB_STATUS_DELETING },
+ { LPQ_OFFLINE, JOB_STATUS_OFFLINE },
+ { LPQ_PAPEROUT, JOB_STATUS_PAPEROUT },
+ { LPQ_PRINTED, JOB_STATUS_PRINTED },
+ { LPQ_DELETED, JOB_STATUS_DELETED },
+ { LPQ_BLOCKED, JOB_STATUS_BLOCKED },
+ { LPQ_USER_INTERVENTION, JOB_STATUS_USER_INTERVENTION },
+ { -1, 0 }
+};
+
+/* Convert a lpq status value stored in printing.tdb into the
+ appropriate win32 API constant. */
+
+static uint32 map_to_spoolss_status(uint32 lpq_status)
+{
+ int i = 0;
+
+ while (lpq_to_spoolss_status_map[i].lpq_status != -1) {
+ if (lpq_to_spoolss_status_map[i].lpq_status == lpq_status)
+ return lpq_to_spoolss_status_map[i].spoolss_status;
+ i++;
+ }
+
+ return 0;
+}
+
+static void pjob_store_notify(int snum, uint32 jobid, struct printjob *old_data,
+ struct printjob *new_data)
+{
+ BOOL new_job = False;
+
+ if (!old_data)
+ new_job = True;
+
+ /* Notify the job name first */
+
+ if (new_job || !strequal(old_data->jobname, new_data->jobname))
+ notify_job_name(snum, jobid, new_data->jobname);
+
+ /* Job attributes that can't be changed. We only send
+ notification for these on a new job. */
+
+ if (new_job) {
+ notify_job_submitted(snum, jobid, new_data->starttime);
+ notify_job_username(snum, jobid, new_data->user);
+ }
+
+ /* Job attributes of a new job or attributes that can be
+ modified. */
+
+ if (new_job || old_data->status != new_data->status)
+ notify_job_status(snum, jobid, map_to_spoolss_status(new_data->status));
+
+ if (new_job || old_data->size != new_data->size)
+ notify_job_total_bytes(snum, jobid, new_data->size);
+
+ if (new_job || old_data->page_count != new_data->page_count)
+ notify_job_total_pages(snum, jobid, new_data->page_count);
+}
+
+/****************************************************************************
+ Store a job structure back to the database.
+****************************************************************************/
+
+static BOOL pjob_store(int snum, uint32 jobid, struct printjob *pjob)
+{
+ TDB_DATA old_data, new_data;
+ BOOL ret = False;
+ struct tdb_print_db *pdb = get_print_db_byname(lp_const_servicename(snum));
+ char *buf = NULL;
+ int len, newlen, buflen;
+
+
+ if (!pdb)
+ return False;
+
+ /* Get old data */
+
+ old_data = tdb_fetch(pdb->tdb, print_key(jobid));
+
+ /* Doh! Now we have to pack/unpack data since the NT_DEVICEMODE was added */
+
+ newlen = 0;
+
+ do {
+ len = 0;
+ buflen = newlen;
+ len += tdb_pack(buf+len, buflen-len, "dddddddddffff",
+ (uint32)pjob->pid,
+ (uint32)pjob->sysjob,
+ (uint32)pjob->fd,
+ (uint32)pjob->starttime,
+ (uint32)pjob->status,
+ (uint32)pjob->size,
+ (uint32)pjob->page_count,
+ (uint32)pjob->spooled,
+ (uint32)pjob->smbjob,
+ pjob->filename,
+ pjob->jobname,
+ pjob->user,
+ pjob->queuename);
+
+ len += pack_devicemode(pjob->nt_devmode, buf+len, buflen-len);
+
+ if (buflen != len) {
+ char *tb;
+
+ tb = (char *)Realloc(buf, len);
+ if (!tb) {
+ DEBUG(0,("pjob_store: failed to enlarge buffer!\n"));
+ goto done;
+ }
+ else
+ buf = tb;
+ newlen = len;
+ }
+ } while ( buflen != len );
+
+
+ /* Store new data */
+
+ new_data.dptr = buf;
+ new_data.dsize = len;
+ ret = (tdb_store(pdb->tdb, print_key(jobid), new_data, TDB_REPLACE) == 0);
+
+ release_print_db(pdb);
+
+ /* Send notify updates for what has changed */
+
+ if ( ret ) {
+ struct printjob old_pjob;
+
+ if ( old_data.dsize )
+ {
+ if ( unpack_pjob( old_data.dptr, old_data.dsize, &old_pjob ) != -1 )
+ {
+ pjob_store_notify( snum, jobid, &old_pjob , pjob );
+ free_nt_devicemode( &old_pjob.nt_devmode );
+ }
+ }
+ else {
+ /* new job */
+ pjob_store_notify( snum, jobid, NULL, pjob );
+ }
+ }
+
+done:
+ SAFE_FREE( old_data.dptr );
+ SAFE_FREE( buf );
+
+ return ret;
+}
+
+/****************************************************************************
+ Remove a job structure from the database.
+****************************************************************************/
+
+void pjob_delete(int snum, uint32 jobid)
+{
+ struct printjob *pjob = print_job_find(snum, jobid);
+ uint32 job_status = 0;
+ struct tdb_print_db *pdb = get_print_db_byname(lp_const_servicename(snum));
+
+ if (!pdb)
+ return;
+
+ if (!pjob) {
+ DEBUG(5, ("pjob_delete(): we were asked to delete nonexistent job %u\n",
+ (unsigned int)jobid));
+ release_print_db(pdb);
+ return;
+ }
+
+ /* Send a notification that a job has been deleted */
+
+ job_status = map_to_spoolss_status(pjob->status);
+
+ /* We must cycle through JOB_STATUS_DELETING and
+ JOB_STATUS_DELETED for the port monitor to delete the job
+ properly. */
+
+ job_status |= JOB_STATUS_DELETING;
+ notify_job_status(snum, jobid, job_status);
+
+ job_status |= JOB_STATUS_DELETED;
+ notify_job_status(snum, jobid, job_status);
+
+ /* Remove from printing.tdb */
+
+ tdb_delete(pdb->tdb, print_key(jobid));
+ release_print_db(pdb);
+ rap_jobid_delete(snum, jobid);
+}
+
+/****************************************************************************
+ Parse a file name from the system spooler to generate a jobid.
+****************************************************************************/
+
+static uint32 print_parse_jobid(char *fname)
+{
+ int jobid;
+
+ if (strncmp(fname,PRINT_SPOOL_PREFIX,strlen(PRINT_SPOOL_PREFIX)) != 0)
+ return (uint32)-1;
+ fname += strlen(PRINT_SPOOL_PREFIX);
+
+ jobid = atoi(fname);
+ if (jobid <= 0)
+ return (uint32)-1;
+
+ return (uint32)jobid;
+}
+
+/****************************************************************************
+ List a unix job in the print database.
+****************************************************************************/
+
+static void print_unix_job(int snum, print_queue_struct *q, uint32 jobid)
+{
+ struct printjob pj, *old_pj;
+
+ if (jobid == (uint32)-1)
+ jobid = q->job + UNIX_JOB_START;
+
+ /* Preserve the timestamp on an existing unix print job */
+
+ old_pj = print_job_find(snum, jobid);
+
+ ZERO_STRUCT(pj);
+
+ pj.pid = (pid_t)-1;
+ pj.sysjob = q->job;
+ pj.fd = -1;
+ pj.starttime = old_pj ? old_pj->starttime : q->time;
+ pj.status = q->status;
+ pj.size = q->size;
+ pj.spooled = True;
+ fstrcpy(pj.filename, old_pj ? old_pj->filename : "");
+ if (jobid < UNIX_JOB_START) {
+ pj.smbjob = True;
+ fstrcpy(pj.jobname, old_pj ? old_pj->jobname : "Remote Downlevel Document");
+ } else {
+ pj.smbjob = False;
+ fstrcpy(pj.jobname, old_pj ? old_pj->jobname : q->fs_file);
+ }
+ fstrcpy(pj.user, old_pj ? old_pj->user : q->fs_user);
+ fstrcpy(pj.queuename, old_pj ? old_pj->queuename : lp_const_servicename(snum));
+
+ pjob_store(snum, jobid, &pj);
+}
+
+
+struct traverse_struct {
+ print_queue_struct *queue;
+ int qcount, snum, maxcount, total_jobs;
+ time_t lpq_time;
+};
+
+/****************************************************************************
+ Utility fn to delete any jobs that are no longer active.
+****************************************************************************/
+
+static int traverse_fn_delete(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void *state)
+{
+ struct traverse_struct *ts = (struct traverse_struct *)state;
+ struct printjob pjob;
+ uint32 jobid;
+ int i = 0;
+
+ if ( key.dsize != sizeof(jobid) )
+ return 0;
+
+ jobid = IVAL(key.dptr, 0);
+ if ( unpack_pjob( data.dptr, data.dsize, &pjob ) == -1 )
+ return 0;
+ free_nt_devicemode( &pjob.nt_devmode );
+
+
+ if (ts->snum != lp_servicenumber(pjob.queuename)) {
+ /* this isn't for the queue we are looking at - this cannot happen with the split tdb's. JRA */
+ return 0;
+ }
+
+ if (!pjob.smbjob) {
+ /* remove a unix job if it isn't in the system queue any more */
+
+ for (i=0;i<ts->qcount;i++) {
+ uint32 u_jobid = (ts->queue[i].job + UNIX_JOB_START);
+ if (jobid == u_jobid)
+ break;
+ }
+ if (i == ts->qcount) {
+ DEBUG(10,("traverse_fn_delete: pjob %u deleted due to !smbjob\n",
+ (unsigned int)jobid ));
+ pjob_delete(ts->snum, jobid);
+ return 0;
+ }
+
+ /* need to continue the the bottom of the function to
+ save the correct attributes */
+ }
+
+ /* maybe it hasn't been spooled yet */
+ if (!pjob.spooled) {
+ /* if a job is not spooled and the process doesn't
+ exist then kill it. This cleans up after smbd
+ deaths */
+ if (!process_exists(pjob.pid)) {
+ DEBUG(10,("traverse_fn_delete: pjob %u deleted due to !process_exists (%u)\n",
+ (unsigned int)jobid, (unsigned int)pjob.pid ));
+ pjob_delete(ts->snum, jobid);
+ } else
+ ts->total_jobs++;
+ return 0;
+ }
+
+ /* this check only makes sense for jobs submitted from Windows clients */
+
+ if ( pjob.smbjob ) {
+ for (i=0;i<ts->qcount;i++) {
+ uint32 curr_jobid = print_parse_jobid(ts->queue[i].fs_file);
+ if (jobid == curr_jobid)
+ break;
+ }
+ }
+
+ /* The job isn't in the system queue - we have to assume it has
+ completed, so delete the database entry. */
+
+ if (i == ts->qcount) {
+
+ /* A race can occur between the time a job is spooled and
+ when it appears in the lpq output. This happens when
+ the job is added to printing.tdb when another smbd
+ running print_queue_update() has completed a lpq and
+ is currently traversing the printing tdb and deleting jobs.
+ Don't delete the job if it was submitted after the lpq_time. */
+
+ if (pjob.starttime < ts->lpq_time) {
+ DEBUG(10,("traverse_fn_delete: pjob %u deleted due to pjob.starttime (%u) < ts->lpq_time (%u)\n",
+ (unsigned int)jobid,
+ (unsigned int)pjob.starttime,
+ (unsigned int)ts->lpq_time ));
+ pjob_delete(ts->snum, jobid);
+ } else
+ ts->total_jobs++;
+ return 0;
+ }
+
+ /* Save the pjob attributes we will store. */
+ /* FIXME!!! This is the only place where queue->job
+ represents the SMB jobid --jerry */
+ ts->queue[i].job = jobid;
+ ts->queue[i].size = pjob.size;
+ ts->queue[i].page_count = pjob.page_count;
+ ts->queue[i].status = pjob.status;
+ ts->queue[i].priority = 1;
+ ts->queue[i].time = pjob.starttime;
+ fstrcpy(ts->queue[i].fs_user, pjob.user);
+ fstrcpy(ts->queue[i].fs_file, pjob.jobname);
+
+ ts->total_jobs++;
+
+ return 0;
+}
+
+/****************************************************************************
+ Check if the print queue has been updated recently enough.
+****************************************************************************/
+
+static void print_cache_flush(int snum)
+{
+ fstring key;
+ const char *printername = lp_const_servicename(snum);
+ struct tdb_print_db *pdb = get_print_db_byname(printername);
+
+ if (!pdb)
+ return;
+ slprintf(key, sizeof(key)-1, "CACHE/%s", printername);
+ tdb_store_int32(pdb->tdb, key, -1);
+ release_print_db(pdb);
+}
+
+/****************************************************************************
+ Check if someone already thinks they are doing the update.
+****************************************************************************/
+
+static pid_t get_updating_pid(fstring printer_name)
+{
+ fstring keystr;
+ TDB_DATA data, key;
+ pid_t updating_pid;
+ struct tdb_print_db *pdb = get_print_db_byname(printer_name);
+
+ if (!pdb)
+ return (pid_t)-1;
+ slprintf(keystr, sizeof(keystr)-1, "UPDATING/%s", printer_name);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr);
+
+ data = tdb_fetch(pdb->tdb, key);
+ release_print_db(pdb);
+ if (!data.dptr || data.dsize != sizeof(pid_t)) {
+ SAFE_FREE(data.dptr);
+ return (pid_t)-1;
+ }
+
+ updating_pid = IVAL(data.dptr, 0);
+ SAFE_FREE(data.dptr);
+
+ if (process_exists(updating_pid))
+ return updating_pid;
+
+ return (pid_t)-1;
+}
+
+/****************************************************************************
+ Set the fact that we're doing the update, or have finished doing the update
+ in the tdb.
+****************************************************************************/
+
+static void set_updating_pid(const fstring printer_name, BOOL delete)
+{
+ fstring keystr;
+ TDB_DATA key;
+ TDB_DATA data;
+ pid_t updating_pid = sys_getpid();
+ uint8 buffer[4];
+
+ struct tdb_print_db *pdb = get_print_db_byname(printer_name);
+
+ if (!pdb)
+ return;
+
+ slprintf(keystr, sizeof(keystr)-1, "UPDATING/%s", printer_name);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr);
+
+ if (delete) {
+ tdb_delete(pdb->tdb, key);
+ release_print_db(pdb);
+ return;
+ }
+
+ SIVAL( buffer, 0, updating_pid);
+ data.dptr = (void *)buffer;
+ data.dsize = 4; /* we always assume this is a 4 byte value */
+
+ tdb_store(pdb->tdb, key, data, TDB_REPLACE);
+ release_print_db(pdb);
+}
+
+/****************************************************************************
+ Sort print jobs by submittal time.
+****************************************************************************/
+
+static int printjob_comp(print_queue_struct *j1, print_queue_struct *j2)
+{
+ /* Silly cases */
+
+ if (!j1 && !j2)
+ return 0;
+ if (!j1)
+ return -1;
+ if (!j2)
+ return 1;
+
+ /* Sort on job start time */
+
+ if (j1->time == j2->time)
+ return 0;
+ return (j1->time > j2->time) ? 1 : -1;
+}
+
+/****************************************************************************
+ Store the sorted queue representation for later portmon retrieval.
+****************************************************************************/
+
+static void store_queue_struct(struct tdb_print_db *pdb, struct traverse_struct *pts)
+{
+ TDB_DATA data, key;
+ int max_reported_jobs = lp_max_reported_jobs(pts->snum);
+ print_queue_struct *queue = pts->queue;
+ size_t len;
+ size_t i;
+ uint qcount;
+
+ if (max_reported_jobs && (max_reported_jobs < pts->qcount))
+ pts->qcount = max_reported_jobs;
+ qcount = pts->qcount;
+
+ /* Work out the size. */
+ data.dsize = 0;
+ data.dsize += tdb_pack(NULL, 0, "d", qcount);
+
+ for (i = 0; i < pts->qcount; i++) {
+ data.dsize += tdb_pack(NULL, 0, "ddddddff",
+ (uint32)queue[i].job,
+ (uint32)queue[i].size,
+ (uint32)queue[i].page_count,
+ (uint32)queue[i].status,
+ (uint32)queue[i].priority,
+ (uint32)queue[i].time,
+ queue[i].fs_user,
+ queue[i].fs_file);
+ }
+
+ if ((data.dptr = malloc(data.dsize)) == NULL)
+ return;
+
+ len = 0;
+ len += tdb_pack(data.dptr + len, data.dsize - len, "d", qcount);
+ for (i = 0; i < pts->qcount; i++) {
+ len += tdb_pack(data.dptr + len, data.dsize - len, "ddddddff",
+ (uint32)queue[i].job,
+ (uint32)queue[i].size,
+ (uint32)queue[i].page_count,
+ (uint32)queue[i].status,
+ (uint32)queue[i].priority,
+ (uint32)queue[i].time,
+ queue[i].fs_user,
+ queue[i].fs_file);
+ }
+
+ key.dptr = "INFO/linear_queue_array";
+ key.dsize = strlen(key.dptr);
+ tdb_store(pdb->tdb, key, data, TDB_REPLACE);
+ SAFE_FREE(data.dptr);
+ return;
+}
+
+static TDB_DATA get_jobs_changed_data(struct tdb_print_db *pdb)
+{
+ TDB_DATA data, key;
+
+ key.dptr = "INFO/jobs_changed";
+ key.dsize = strlen(key.dptr);
+ ZERO_STRUCT(data);
+
+ data = tdb_fetch(pdb->tdb, key);
+ if (data.dptr == NULL || data.dsize == 0 || (data.dsize % 4 != 0)) {
+ SAFE_FREE(data.dptr);
+ ZERO_STRUCT(data);
+ }
+
+ return data;
+}
+
+static void check_job_changed(int snum, TDB_DATA data, uint32 jobid)
+{
+ unsigned int i;
+ unsigned int job_count = data.dsize / 4;
+
+ for (i = 0; i < job_count; i++) {
+ uint32 ch_jobid;
+
+ ch_jobid = IVAL(data.dptr, i*4);
+ if (ch_jobid == jobid)
+ remove_from_jobs_changed(snum, jobid);
+ }
+}
+
+/****************************************************************************
+ Update the internal database from the system print queue for a queue.
+****************************************************************************/
+
+static void print_queue_update(int snum)
+{
+ int i, qcount;
+ print_queue_struct *queue = NULL;
+ print_status_struct status;
+ print_status_struct old_status;
+ struct printjob *pjob;
+ struct traverse_struct tstruct;
+ fstring keystr, printer_name, cachestr;
+ TDB_DATA data, key;
+ TDB_DATA jcdata;
+ struct tdb_print_db *pdb;
+ struct printif *current_printif = get_printer_fns( snum );
+
+ fstrcpy(printer_name, lp_const_servicename(snum));
+ pdb = get_print_db_byname(printer_name);
+ if (!pdb)
+ return;
+
+ /*
+ * Check to see if someone else is doing this update.
+ * This is essentially a mutex on the update.
+ */
+
+ if (get_updating_pid(printer_name) != -1) {
+ release_print_db(pdb);
+ return;
+ }
+
+ /* Lock the queue for the database update */
+
+ slprintf(keystr, sizeof(keystr) - 1, "LOCK/%s", printer_name);
+ /* Only wait 10 seconds for this. */
+ if (tdb_lock_bystring(pdb->tdb, keystr, 10) == -1) {
+ DEBUG(0,("print_queue_update: Failed to lock printer %s database\n", printer_name));
+ release_print_db(pdb);
+ return;
+ }
+
+ /*
+ * Ensure that no one else got in here.
+ * If the updating pid is still -1 then we are
+ * the winner.
+ */
+
+ if (get_updating_pid(printer_name) != -1) {
+ /*
+ * Someone else is doing the update, exit.
+ */
+ tdb_unlock_bystring(pdb->tdb, keystr);
+ release_print_db(pdb);
+ return;
+ }
+
+ /*
+ * We're going to do the update ourselves.
+ */
+
+ /* Tell others we're doing the update. */
+ set_updating_pid(printer_name, False);
+
+ /*
+ * Allow others to enter and notice we're doing
+ * the update.
+ */
+
+ tdb_unlock_bystring(pdb->tdb, keystr);
+
+ /*
+ * Update the cache time FIRST ! Stops others even
+ * attempting to get the lock and doing this
+ * if the lpq takes a long time.
+ */
+
+ slprintf(cachestr, sizeof(cachestr)-1, "CACHE/%s", printer_name);
+ tdb_store_int32(pdb->tdb, cachestr, (int)time(NULL));
+
+ /* get the current queue using the appropriate interface */
+ ZERO_STRUCT(status);
+
+ qcount = (*(current_printif->queue_get))(snum, &queue, &status);
+
+ DEBUG(3, ("%d job%s in queue for %s\n", qcount, (qcount != 1) ?
+ "s" : "", printer_name));
+
+ /* Sort the queue by submission time otherwise they are displayed
+ in hash order. */
+
+ qsort(queue, qcount, sizeof(print_queue_struct),
+ QSORT_CAST(printjob_comp));
+
+ /*
+ any job in the internal database that is marked as spooled
+ and doesn't exist in the system queue is considered finished
+ and removed from the database
+
+ any job in the system database but not in the internal database
+ is added as a unix job
+
+ fill in any system job numbers as we go
+ */
+
+ jcdata = get_jobs_changed_data(pdb);
+
+ for (i=0; i<qcount; i++) {
+ uint32 jobid = print_parse_jobid(queue[i].fs_file);
+
+ if (jobid == (uint32)-1) {
+ /* assume its a unix print job */
+ print_unix_job(snum, &queue[i], jobid);
+ continue;
+ }
+
+ /* we have an active SMB print job - update its status */
+ pjob = print_job_find(snum, jobid);
+ if (!pjob) {
+ /* err, somethings wrong. Probably smbd was restarted
+ with jobs in the queue. All we can do is treat them
+ like unix jobs. Pity. */
+ print_unix_job(snum, &queue[i], jobid);
+ continue;
+ }
+
+ pjob->sysjob = queue[i].job;
+ pjob->status = queue[i].status;
+ pjob_store(snum, jobid, pjob);
+ check_job_changed(snum, jcdata, jobid);
+ }
+
+ SAFE_FREE(jcdata.dptr);
+
+ /* now delete any queued entries that don't appear in the
+ system queue */
+ tstruct.queue = queue;
+ tstruct.qcount = qcount;
+ tstruct.snum = snum;
+ tstruct.total_jobs = 0;
+ tstruct.lpq_time = time(NULL);
+
+ tdb_traverse(pdb->tdb, traverse_fn_delete, (void *)&tstruct);
+
+ /* Store the linearised queue, max jobs only. */
+ store_queue_struct(pdb, &tstruct);
+
+ SAFE_FREE(tstruct.queue);
+
+ DEBUG(10,("print_queue_update: printer %s INFO/total_jobs = %d\n",
+ printer_name, tstruct.total_jobs ));
+
+ tdb_store_int32(pdb->tdb, "INFO/total_jobs", tstruct.total_jobs);
+
+ get_queue_status(snum, &old_status);
+ if (old_status.qcount != qcount)
+ DEBUG(10,("print_queue_update: queue status change %d jobs -> %d jobs for printer %s\n",
+ old_status.qcount, qcount, printer_name ));
+
+ /* store the new queue status structure */
+ slprintf(keystr, sizeof(keystr)-1, "STATUS/%s", printer_name);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr);
+
+ status.qcount = qcount;
+ data.dptr = (void *)&status;
+ data.dsize = sizeof(status);
+ tdb_store(pdb->tdb, key, data, TDB_REPLACE);
+
+ /*
+ * Update the cache time again. We want to do this call
+ * as little as possible...
+ */
+
+ slprintf(keystr, sizeof(keystr)-1, "CACHE/%s", printer_name);
+ tdb_store_int32(pdb->tdb, keystr, (int32)time(NULL));
+
+ /* Delete our pid from the db. */
+ set_updating_pid(printer_name, True);
+ release_print_db(pdb);
+}
+
+/****************************************************************************
+ Create/Update an entry in the print tdb that will allow us to send notify
+ updates only to interested smbd's.
+****************************************************************************/
+
+BOOL print_notify_register_pid(int snum)
+{
+ TDB_DATA data;
+ struct tdb_print_db *pdb = NULL;
+ TDB_CONTEXT *tdb = NULL;
+ const char *printername;
+ uint32 mypid = (uint32)sys_getpid();
+ BOOL ret = False;
+ size_t i;
+
+ /* if (snum == -1), then the change notify request was
+ on a print server handle and we need to register on
+ all print queus */
+
+ if (snum == -1)
+ {
+ int num_services = lp_numservices();
+ int idx;
+
+ for ( idx=0; idx<num_services; idx++ ) {
+ if (lp_snum_ok(idx) && lp_print_ok(idx) )
+ print_notify_register_pid(idx);
+ }
+
+ return True;
+ }
+ else /* register for a specific printer */
+ {
+ printername = lp_const_servicename(snum);
+ pdb = get_print_db_byname(printername);
+ if (!pdb)
+ return False;
+ tdb = pdb->tdb;
+ }
+
+ if (tdb_lock_bystring(tdb, NOTIFY_PID_LIST_KEY, 10) == -1) {
+ DEBUG(0,("print_notify_register_pid: Failed to lock printer %s\n",
+ printername));
+ if (pdb)
+ release_print_db(pdb);
+ return False;
+ }
+
+ data = get_printer_notify_pid_list( tdb, printername, True );
+
+ /* Add ourselves and increase the refcount. */
+
+ for (i = 0; i < data.dsize; i += 8) {
+ if (IVAL(data.dptr,i) == mypid) {
+ uint32 new_refcount = IVAL(data.dptr, i+4) + 1;
+ SIVAL(data.dptr, i+4, new_refcount);
+ break;
+ }
+ }
+
+ if (i == data.dsize) {
+ /* We weren't in the list. Realloc. */
+ data.dptr = Realloc(data.dptr, data.dsize + 8);
+ if (!data.dptr) {
+ DEBUG(0,("print_notify_register_pid: Relloc fail for printer %s\n",
+ printername));
+ goto done;
+ }
+ data.dsize += 8;
+ SIVAL(data.dptr,data.dsize - 8,mypid);
+ SIVAL(data.dptr,data.dsize - 4,1); /* Refcount. */
+ }
+
+ /* Store back the record. */
+ if (tdb_store_bystring(tdb, NOTIFY_PID_LIST_KEY, data, TDB_REPLACE) == -1) {
+ DEBUG(0,("print_notify_register_pid: Failed to update pid \
+list for printer %s\n", printername));
+ goto done;
+ }
+
+ ret = True;
+
+ done:
+
+ tdb_unlock_bystring(tdb, NOTIFY_PID_LIST_KEY);
+ if (pdb)
+ release_print_db(pdb);
+ SAFE_FREE(data.dptr);
+ return ret;
+}
+
+/****************************************************************************
+ Update an entry in the print tdb that will allow us to send notify
+ updates only to interested smbd's.
+****************************************************************************/
+
+BOOL print_notify_deregister_pid(int snum)
+{
+ TDB_DATA data;
+ struct tdb_print_db *pdb = NULL;
+ TDB_CONTEXT *tdb = NULL;
+ const char *printername;
+ uint32 mypid = (uint32)sys_getpid();
+ size_t i;
+ BOOL ret = False;
+
+ /* if ( snum == -1 ), we are deregister a print server handle
+ which means to deregister on all print queues */
+
+ if (snum == -1)
+ {
+ int num_services = lp_numservices();
+ int idx;
+
+ for ( idx=0; idx<num_services; idx++ ) {
+ if ( lp_snum_ok(idx) && lp_print_ok(idx) )
+ print_notify_deregister_pid(idx);
+ }
+
+ return True;
+ }
+ else /* deregister a specific printer */
+ {
+ printername = lp_const_servicename(snum);
+ pdb = get_print_db_byname(printername);
+ if (!pdb)
+ return False;
+ tdb = pdb->tdb;
+ }
+
+ if (tdb_lock_bystring(tdb, NOTIFY_PID_LIST_KEY, 10) == -1) {
+ DEBUG(0,("print_notify_register_pid: Failed to lock \
+printer %s database\n", printername));
+ if (pdb)
+ release_print_db(pdb);
+ return False;
+ }
+
+ data = get_printer_notify_pid_list( tdb, printername, True );
+
+ /* Reduce refcount. Remove ourselves if zero. */
+
+ for (i = 0; i < data.dsize; ) {
+ if (IVAL(data.dptr,i) == mypid) {
+ uint32 refcount = IVAL(data.dptr, i+4);
+
+ refcount--;
+
+ if (refcount == 0) {
+ if (data.dsize - i > 8)
+ memmove( &data.dptr[i], &data.dptr[i+8], data.dsize - i - 8);
+ data.dsize -= 8;
+ continue;
+ }
+ SIVAL(data.dptr, i+4, refcount);
+ }
+
+ i += 8;
+ }
+
+ if (data.dsize == 0)
+ SAFE_FREE(data.dptr);
+
+ /* Store back the record. */
+ if (tdb_store_bystring(tdb, NOTIFY_PID_LIST_KEY, data, TDB_REPLACE) == -1) {
+ DEBUG(0,("print_notify_register_pid: Failed to update pid \
+list for printer %s\n", printername));
+ goto done;
+ }
+
+ ret = True;
+
+ done:
+
+ tdb_unlock_bystring(tdb, NOTIFY_PID_LIST_KEY);
+ if (pdb)
+ release_print_db(pdb);
+ SAFE_FREE(data.dptr);
+ return ret;
+}
+
+/****************************************************************************
+ Check if a jobid is valid. It is valid if it exists in the database.
+****************************************************************************/
+
+BOOL print_job_exists(int snum, uint32 jobid)
+{
+ struct tdb_print_db *pdb = get_print_db_byname(lp_const_servicename(snum));
+ BOOL ret;
+
+ if (!pdb)
+ return False;
+ ret = tdb_exists(pdb->tdb, print_key(jobid));
+ release_print_db(pdb);
+ return ret;
+}
+
+/****************************************************************************
+ Give the fd used for a jobid.
+****************************************************************************/
+
+int print_job_fd(int snum, uint32 jobid)
+{
+ struct printjob *pjob = print_job_find(snum, jobid);
+ if (!pjob)
+ return -1;
+ /* don't allow another process to get this info - it is meaningless */
+ if (pjob->pid != local_pid)
+ return -1;
+ return pjob->fd;
+}
+
+/****************************************************************************
+ Give the filename used for a jobid.
+ Only valid for the process doing the spooling and when the job
+ has not been spooled.
+****************************************************************************/
+
+char *print_job_fname(int snum, uint32 jobid)
+{
+ struct printjob *pjob = print_job_find(snum, jobid);
+ if (!pjob || pjob->spooled || pjob->pid != local_pid)
+ return NULL;
+ return pjob->filename;
+}
+
+
+/****************************************************************************
+ Give the filename used for a jobid.
+ Only valid for the process doing the spooling and when the job
+ has not been spooled.
+****************************************************************************/
+
+NT_DEVICEMODE *print_job_devmode(int snum, uint32 jobid)
+{
+ struct printjob *pjob = print_job_find(snum, jobid);
+
+ if ( !pjob )
+ return NULL;
+
+ return pjob->nt_devmode;
+}
+
+/****************************************************************************
+ Set the place in the queue for a job.
+****************************************************************************/
+
+BOOL print_job_set_place(int snum, uint32 jobid, int place)
+{
+ DEBUG(2,("print_job_set_place not implemented yet\n"));
+ return False;
+}
+
+/****************************************************************************
+ Set the name of a job. Only possible for owner.
+****************************************************************************/
+
+BOOL print_job_set_name(int snum, uint32 jobid, char *name)
+{
+ struct printjob *pjob = print_job_find(snum, jobid);
+ if (!pjob || pjob->pid != local_pid)
+ return False;
+
+ fstrcpy(pjob->jobname, name);
+ return pjob_store(snum, jobid, pjob);
+}
+
+/***************************************************************************
+ Remove a jobid from the 'jobs changed' list.
+***************************************************************************/
+
+static BOOL remove_from_jobs_changed(int snum, uint32 jobid)
+{
+ const char *printername = lp_const_servicename(snum);
+ struct tdb_print_db *pdb = get_print_db_byname(printername);
+ TDB_DATA data, key;
+ size_t job_count, i;
+ BOOL ret = False;
+ BOOL gotlock = False;
+
+ key.dptr = "INFO/jobs_changed";
+ key.dsize = strlen(key.dptr);
+ ZERO_STRUCT(data);
+
+ if (tdb_chainlock_with_timeout(pdb->tdb, key, 5) == -1)
+ goto out;
+
+ gotlock = True;
+
+ data = tdb_fetch(pdb->tdb, key);
+
+ if (data.dptr == NULL || data.dsize == 0 || (data.dsize % 4 != 0))
+ goto out;
+
+ job_count = data.dsize / 4;
+ for (i = 0; i < job_count; i++) {
+ uint32 ch_jobid;
+
+ ch_jobid = IVAL(data.dptr, i*4);
+ if (ch_jobid == jobid) {
+ if (i < job_count -1 )
+ memmove(data.dptr + (i*4), data.dptr + (i*4) + 4, (job_count - i - 1)*4 );
+ data.dsize -= 4;
+ if (tdb_store(pdb->tdb, key, data, TDB_REPLACE) == -1)
+ goto out;
+ break;
+ }
+ }
+
+ ret = True;
+ out:
+
+ if (gotlock)
+ tdb_chainunlock(pdb->tdb, key);
+ SAFE_FREE(data.dptr);
+ release_print_db(pdb);
+ if (ret)
+ DEBUG(10,("remove_from_jobs_changed: removed jobid %u\n", (unsigned int)jobid ));
+ else
+ DEBUG(10,("remove_from_jobs_changed: Failed to remove jobid %u\n", (unsigned int)jobid ));
+ return ret;
+}
+
+/****************************************************************************
+ Delete a print job - don't update queue.
+****************************************************************************/
+
+static BOOL print_job_delete1(int snum, uint32 jobid)
+{
+ struct printjob *pjob = print_job_find(snum, jobid);
+ int result = 0;
+ struct printif *current_printif = get_printer_fns( snum );
+
+ if (!pjob)
+ return False;
+
+ /*
+ * If already deleting just return.
+ */
+
+ if (pjob->status == LPQ_DELETING)
+ return True;
+
+ /* Hrm - we need to be able to cope with deleting a job before it
+ has reached the spooler. */
+
+ if (pjob->sysjob == -1) {
+ DEBUG(5, ("attempt to delete job %u not seen by lpr\n", (unsigned int)jobid));
+ }
+
+ /* Set the tdb entry to be deleting. */
+
+ pjob->status = LPQ_DELETING;
+ pjob_store(snum, jobid, pjob);
+
+ if (pjob->spooled && pjob->sysjob != -1)
+ result = (*(current_printif->job_delete))(snum, pjob);
+ else
+ remove_from_jobs_changed(snum, jobid);
+
+ /* Delete the tdb entry if the delete succeeded or the job hasn't
+ been spooled. */
+
+ if (result == 0) {
+ const char *printername = lp_const_servicename(snum);
+ struct tdb_print_db *pdb = get_print_db_byname(printername);
+ int njobs = 1;
+
+ if (!pdb)
+ return False;
+ pjob_delete(snum, jobid);
+ /* Ensure we keep a rough count of the number of total jobs... */
+ tdb_change_int32_atomic(pdb->tdb, "INFO/total_jobs", &njobs, -1);
+ release_print_db(pdb);
+ }
+
+ return (result == 0);
+}
+
+/****************************************************************************
+ Return true if the current user owns the print job.
+****************************************************************************/
+
+static BOOL is_owner(struct current_user *user, int snum, uint32 jobid)
+{
+ struct printjob *pjob = print_job_find(snum, jobid);
+ user_struct *vuser;
+
+ if (!pjob || !user)
+ return False;
+
+ if ((vuser = get_valid_user_struct(user->vuid)) != NULL) {
+ return strequal(pjob->user, vuser->user.smb_name);
+ } else {
+ return strequal(pjob->user, uidtoname(user->uid));
+ }
+}
+
+/****************************************************************************
+ Delete a print job.
+****************************************************************************/
+
+BOOL print_job_delete(struct current_user *user, int snum, uint32 jobid, WERROR *errcode)
+{
+ BOOL owner, deleted;
+ char *fname;
+
+ *errcode = WERR_OK;
+
+ owner = is_owner(user, snum, jobid);
+
+ /* Check access against security descriptor or whether the user
+ owns their job. */
+
+ if (!owner &&
+ !print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) {
+ DEBUG(3, ("delete denied by security descriptor\n"));
+ *errcode = WERR_ACCESS_DENIED;
+
+ /* BEGIN_ADMIN_LOG */
+ sys_adminlog( LOG_ERR,
+ "Permission denied-- user not allowed to delete, \
+pause, or resume print job. User name: %s. Printer name: %s.",
+ uidtoname(user->uid), PRINTERNAME(snum) );
+ /* END_ADMIN_LOG */
+
+ return False;
+ }
+
+ /*
+ * get the spooled filename of the print job
+ * if this works, then the file has not been spooled
+ * to the underlying print system. Just delete the
+ * spool file & return.
+ */
+
+ if ( (fname = print_job_fname( snum, jobid )) != NULL )
+ {
+ /* remove the spool file */
+ DEBUG(10,("print_job_delete: Removing spool file [%s]\n", fname ));
+ if ( unlink( fname ) == -1 ) {
+ *errcode = map_werror_from_unix(errno);
+ return False;
+ }
+
+ return True;
+ }
+
+ if (!print_job_delete1(snum, jobid)) {
+ *errcode = WERR_ACCESS_DENIED;
+ return False;
+ }
+
+ /* force update the database and say the delete failed if the
+ job still exists */
+
+ print_queue_update(snum);
+
+ deleted = !print_job_exists(snum, jobid);
+ if ( !deleted )
+ *errcode = WERR_ACCESS_DENIED;
+
+ return deleted;
+}
+
+/****************************************************************************
+ Pause a job.
+****************************************************************************/
+
+BOOL print_job_pause(struct current_user *user, int snum, uint32 jobid, WERROR *errcode)
+{
+ struct printjob *pjob = print_job_find(snum, jobid);
+ int ret = -1;
+ struct printif *current_printif = get_printer_fns( snum );
+
+ if (!pjob || !user)
+ return False;
+
+ if (!pjob->spooled || pjob->sysjob == -1)
+ return False;
+
+ if (!is_owner(user, snum, jobid) &&
+ !print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) {
+ DEBUG(3, ("pause denied by security descriptor\n"));
+
+ /* BEGIN_ADMIN_LOG */
+ sys_adminlog( LOG_ERR,
+ "Permission denied-- user not allowed to delete, \
+pause, or resume print job. User name: %s. Printer name: %s.",
+ uidtoname(user->uid), PRINTERNAME(snum) );
+ /* END_ADMIN_LOG */
+
+ *errcode = WERR_ACCESS_DENIED;
+ return False;
+ }
+
+ /* need to pause the spooled entry */
+ ret = (*(current_printif->job_pause))(snum, pjob);
+
+ if (ret != 0) {
+ *errcode = WERR_INVALID_PARAM;
+ return False;
+ }
+
+ /* force update the database */
+ print_cache_flush(snum);
+
+ /* Send a printer notify message */
+
+ notify_job_status(snum, jobid, JOB_STATUS_PAUSED);
+
+ /* how do we tell if this succeeded? */
+
+ return True;
+}
+
+/****************************************************************************
+ Resume a job.
+****************************************************************************/
+
+BOOL print_job_resume(struct current_user *user, int snum, uint32 jobid, WERROR *errcode)
+{
+ struct printjob *pjob = print_job_find(snum, jobid);
+ int ret;
+ struct printif *current_printif = get_printer_fns( snum );
+
+ if (!pjob || !user)
+ return False;
+
+ if (!pjob->spooled || pjob->sysjob == -1)
+ return False;
+
+ if (!is_owner(user, snum, jobid) &&
+ !print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) {
+ DEBUG(3, ("resume denied by security descriptor\n"));
+ *errcode = WERR_ACCESS_DENIED;
+
+ /* BEGIN_ADMIN_LOG */
+ sys_adminlog( LOG_ERR,
+ "Permission denied-- user not allowed to delete, \
+pause, or resume print job. User name: %s. Printer name: %s.",
+ uidtoname(user->uid), PRINTERNAME(snum) );
+ /* END_ADMIN_LOG */
+ return False;
+ }
+
+ ret = (*(current_printif->job_resume))(snum, pjob);
+
+ if (ret != 0) {
+ *errcode = WERR_INVALID_PARAM;
+ return False;
+ }
+
+ /* force update the database */
+ print_cache_flush(snum);
+
+ /* Send a printer notify message */
+
+ notify_job_status(snum, jobid, JOB_STATUS_QUEUED);
+
+ return True;
+}
+
+/****************************************************************************
+ Write to a print file.
+****************************************************************************/
+
+int print_job_write(int snum, uint32 jobid, const char *buf, int size)
+{
+ int return_code;
+ struct printjob *pjob = print_job_find(snum, jobid);
+
+ if (!pjob)
+ return -1;
+ /* don't allow another process to get this info - it is meaningless */
+ if (pjob->pid != local_pid)
+ return -1;
+
+ return_code = write(pjob->fd, buf, size);
+ if (return_code>0) {
+ pjob->size += size;
+ pjob_store(snum, jobid, pjob);
+ }
+ return return_code;
+}
+
+/****************************************************************************
+ Check if the print queue has been updated recently enough.
+****************************************************************************/
+
+static BOOL print_cache_expired(int snum)
+{
+ fstring key;
+ time_t last_qscan_time, time_now = time(NULL);
+ const char *printername = lp_const_servicename(snum);
+ struct tdb_print_db *pdb = get_print_db_byname(printername);
+
+ if (!pdb)
+ return False;
+
+ slprintf(key, sizeof(key), "CACHE/%s", printername);
+ last_qscan_time = (time_t)tdb_fetch_int32(pdb->tdb, key);
+
+ /*
+ * Invalidate the queue for 3 reasons.
+ * (1). last queue scan time == -1.
+ * (2). Current time - last queue scan time > allowed cache time.
+ * (3). last queue scan time > current time + MAX_CACHE_VALID_TIME (1 hour by default).
+ * This last test picks up machines for which the clock has been moved
+ * forward, an lpq scan done and then the clock moved back. Otherwise
+ * that last lpq scan would stay around for a loooong loooong time... :-). JRA.
+ */
+
+ if (last_qscan_time == ((time_t)-1) || (time_now - last_qscan_time) >= lp_lpqcachetime() ||
+ last_qscan_time > (time_now + MAX_CACHE_VALID_TIME)) {
+ DEBUG(3, ("print cache expired for queue %s \
+(last_qscan_time = %d, time now = %d, qcachetime = %d)\n", printername,
+ (int)last_qscan_time, (int)time_now, (int)lp_lpqcachetime() ));
+ release_print_db(pdb);
+ return True;
+ }
+ release_print_db(pdb);
+ return False;
+}
+
+/****************************************************************************
+ Get the queue status - do not update if db is out of date.
+****************************************************************************/
+
+static int get_queue_status(int snum, print_status_struct *status)
+{
+ fstring keystr;
+ TDB_DATA data, key;
+ const char *printername = lp_const_servicename(snum);
+ struct tdb_print_db *pdb = get_print_db_byname(printername);
+ int len;
+
+ if (!pdb)
+ return 0;
+
+ if (status) {
+ ZERO_STRUCTP(status);
+ slprintf(keystr, sizeof(keystr)-1, "STATUS/%s", printername);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr);
+ data = tdb_fetch(pdb->tdb, key);
+ if (data.dptr) {
+ if (data.dsize == sizeof(print_status_struct))
+ /* this memcpy is ok since the status struct was
+ not packed before storing it in the tdb */
+ memcpy(status, data.dptr, sizeof(print_status_struct));
+ SAFE_FREE(data.dptr);
+ }
+ }
+ len = tdb_fetch_int32(pdb->tdb, "INFO/total_jobs");
+ release_print_db(pdb);
+ return (len == -1 ? 0 : len);
+}
+
+/****************************************************************************
+ Determine the number of jobs in a queue.
+****************************************************************************/
+
+int print_queue_length(int snum, print_status_struct *pstatus)
+{
+ print_status_struct status;
+ int len;
+
+ /* make sure the database is up to date */
+ if (print_cache_expired(snum))
+ print_queue_update(snum);
+
+ /* also fetch the queue status */
+ memset(&status, 0, sizeof(status));
+ len = get_queue_status(snum, &status);
+
+ if (pstatus)
+ *pstatus = status;
+
+ return len;
+}
+
+/***************************************************************************
+ Allocate a jobid. Hold the lock for as short a time as possible.
+***************************************************************************/
+
+static BOOL allocate_print_jobid(struct tdb_print_db *pdb, int snum, const char *printername, uint32 *pjobid)
+{
+ int i;
+ uint32 jobid;
+
+ *pjobid = (uint32)-1;
+
+ for (i = 0; i < 3; i++) {
+ /* Lock the database - only wait 20 seconds. */
+ if (tdb_lock_bystring(pdb->tdb, "INFO/nextjob", 20) == -1) {
+ DEBUG(0,("allocate_print_jobid: failed to lock printing database %s\n", printername ));
+ return False;
+ }
+
+ if (!tdb_fetch_uint32(pdb->tdb, "INFO/nextjob", &jobid)) {
+ if (tdb_error(pdb->tdb) != TDB_ERR_NOEXIST) {
+ DEBUG(0, ("allocate_print_jobid: failed to fetch INFO/nextjob for print queue %s\n",
+ printername ));
+ return False;
+ }
+ jobid = 0;
+ }
+
+ jobid = NEXT_JOBID(jobid);
+
+ if (tdb_store_int32(pdb->tdb, "INFO/nextjob", jobid)==-1) {
+ DEBUG(3, ("allocate_print_jobid: failed to store INFO/nextjob.\n"));
+ tdb_unlock_bystring(pdb->tdb, "INFO/nextjob");
+ return False;
+ }
+
+ /* We've finished with the INFO/nextjob lock. */
+ tdb_unlock_bystring(pdb->tdb, "INFO/nextjob");
+
+ if (!print_job_exists(snum, jobid))
+ break;
+ }
+
+ if (i > 2) {
+ DEBUG(0, ("allocate_print_jobid: failed to allocate a print job for queue %s\n",
+ printername ));
+ /* Probably full... */
+ errno = ENOSPC;
+ return False;
+ }
+
+ /* Store a dummy placeholder. */
+ {
+ TDB_DATA dum;
+ dum.dptr = NULL;
+ dum.dsize = 0;
+ if (tdb_store(pdb->tdb, print_key(jobid), dum, TDB_INSERT) == -1) {
+ DEBUG(3, ("allocate_print_jobid: jobid (%d) failed to store placeholder.\n",
+ jobid ));
+ return False;
+ }
+ }
+
+ *pjobid = jobid;
+ return True;
+}
+
+/***************************************************************************
+ Append a jobid to the 'jobs changed' list.
+***************************************************************************/
+
+static BOOL add_to_jobs_changed(struct tdb_print_db *pdb, uint32 jobid)
+{
+ TDB_DATA data, key;
+
+ key.dptr = "INFO/jobs_changed";
+ key.dsize = strlen(key.dptr);
+ data.dptr = (char *)&jobid;
+ data.dsize = 4;
+
+ DEBUG(10,("add_to_jobs_changed: Added jobid %u\n", (unsigned int)jobid ));
+
+ return (tdb_append(pdb->tdb, key, data) == 0);
+}
+
+/***************************************************************************
+ Start spooling a job - return the jobid.
+***************************************************************************/
+
+uint32 print_job_start(struct current_user *user, int snum, char *jobname, NT_DEVICEMODE *nt_devmode )
+{
+ uint32 jobid;
+ char *path;
+ struct printjob pjob;
+ user_struct *vuser;
+ const char *printername = lp_const_servicename(snum);
+ struct tdb_print_db *pdb = get_print_db_byname(printername);
+ int njobs;
+
+ errno = 0;
+
+ if (!pdb)
+ return (uint32)-1;
+
+ if (!print_access_check(user, snum, PRINTER_ACCESS_USE)) {
+ DEBUG(3, ("print_job_start: job start denied by security descriptor\n"));
+ release_print_db(pdb);
+ return (uint32)-1;
+ }
+
+ if (!print_time_access_check(snum)) {
+ DEBUG(3, ("print_job_start: job start denied by time check\n"));
+ release_print_db(pdb);
+ return (uint32)-1;
+ }
+
+ path = lp_pathname(snum);
+
+ /* see if we have sufficient disk space */
+ if (lp_minprintspace(snum)) {
+ SMB_BIG_UINT dspace, dsize;
+ if (sys_fsusage(path, &dspace, &dsize) == 0 &&
+ dspace < 2*(SMB_BIG_UINT)lp_minprintspace(snum)) {
+ DEBUG(3, ("print_job_start: disk space check failed.\n"));
+ release_print_db(pdb);
+ errno = ENOSPC;
+ return (uint32)-1;
+ }
+ }
+
+ /* for autoloaded printers, check that the printcap entry still exists */
+ if (lp_autoloaded(snum) && !pcap_printername_ok(lp_const_servicename(snum), NULL)) {
+ DEBUG(3, ("print_job_start: printer name %s check failed.\n", lp_const_servicename(snum) ));
+ release_print_db(pdb);
+ errno = ENOENT;
+ return (uint32)-1;
+ }
+
+ /* Insure the maximum queue size is not violated */
+ if ((njobs = print_queue_length(snum,NULL)) > lp_maxprintjobs(snum)) {
+ DEBUG(3, ("print_job_start: Queue %s number of jobs (%d) larger than max printjobs per queue (%d).\n",
+ printername, njobs, lp_maxprintjobs(snum) ));
+ release_print_db(pdb);
+ errno = ENOSPC;
+ return (uint32)-1;
+ }
+
+ DEBUG(10,("print_job_start: Queue %s number of jobs (%d), max printjobs = %d\n",
+ printername, njobs, lp_maxprintjobs(snum) ));
+
+ if (!allocate_print_jobid(pdb, snum, printername, &jobid))
+ goto fail;
+
+ /* create the database entry */
+
+ ZERO_STRUCT(pjob);
+
+ pjob.pid = local_pid;
+ pjob.sysjob = -1;
+ pjob.fd = -1;
+ pjob.starttime = time(NULL);
+ pjob.status = LPQ_SPOOLING;
+ pjob.size = 0;
+ pjob.spooled = False;
+ pjob.smbjob = True;
+ pjob.nt_devmode = nt_devmode;
+
+ fstrcpy(pjob.jobname, jobname);
+
+ if ((vuser = get_valid_user_struct(user->vuid)) != NULL) {
+ fstrcpy(pjob.user, vuser->user.smb_name);
+ } else {
+ fstrcpy(pjob.user, uidtoname(user->uid));
+ }
+
+ fstrcpy(pjob.queuename, lp_const_servicename(snum));
+
+ /* we have a job entry - now create the spool file */
+ slprintf(pjob.filename, sizeof(pjob.filename)-1, "%s/%s%.8u.XXXXXX",
+ path, PRINT_SPOOL_PREFIX, (unsigned int)jobid);
+ pjob.fd = smb_mkstemp(pjob.filename);
+
+ if (pjob.fd == -1) {
+ if (errno == EACCES) {
+ /* Common setup error, force a report. */
+ DEBUG(0, ("print_job_start: insufficient permissions \
+to open spool file %s.\n", pjob.filename));
+ } else {
+ /* Normal case, report at level 3 and above. */
+ DEBUG(3, ("print_job_start: can't open spool file %s,\n", pjob.filename));
+ DEBUGADD(3, ("errno = %d (%s).\n", errno, strerror(errno)));
+ }
+ goto fail;
+ }
+
+ pjob_store(snum, jobid, &pjob);
+
+ /* Update the 'jobs changed' entry used by print_queue_status. */
+ add_to_jobs_changed(pdb, jobid);
+
+ /* Ensure we keep a rough count of the number of total jobs... */
+ tdb_change_int32_atomic(pdb->tdb, "INFO/total_jobs", &njobs, 1);
+
+ release_print_db(pdb);
+
+ return jobid;
+
+ fail:
+ if (jobid != -1)
+ pjob_delete(snum, jobid);
+
+ release_print_db(pdb);
+
+ DEBUG(3, ("print_job_start: returning fail. Error = %s\n", strerror(errno) ));
+ return (uint32)-1;
+}
+
+/****************************************************************************
+ Update the number of pages spooled to jobid
+****************************************************************************/
+
+void print_job_endpage(int snum, uint32 jobid)
+{
+ struct printjob *pjob = print_job_find(snum, jobid);
+ if (!pjob)
+ return;
+ /* don't allow another process to get this info - it is meaningless */
+ if (pjob->pid != local_pid)
+ return;
+
+ pjob->page_count++;
+ pjob_store(snum, jobid, pjob);
+}
+
+/****************************************************************************
+ Print a file - called on closing the file. This spools the job.
+ If normal close is false then we're tearing down the jobs - treat as an
+ error.
+****************************************************************************/
+
+BOOL print_job_end(int snum, uint32 jobid, BOOL normal_close)
+{
+ struct printjob *pjob = print_job_find(snum, jobid);
+ int ret;
+ SMB_STRUCT_STAT sbuf;
+ struct printif *current_printif = get_printer_fns( snum );
+
+ if (!pjob)
+ return False;
+
+ if (pjob->spooled || pjob->pid != local_pid)
+ return False;
+
+ if (normal_close && (sys_fstat(pjob->fd, &sbuf) == 0)) {
+ pjob->size = sbuf.st_size;
+ close(pjob->fd);
+ pjob->fd = -1;
+ } else {
+
+ /*
+ * Not a normal close or we couldn't stat the job file,
+ * so something has gone wrong. Cleanup.
+ */
+ close(pjob->fd);
+ pjob->fd = -1;
+ DEBUG(3,("print_job_end: failed to stat file for jobid %d\n", jobid ));
+ goto fail;
+ }
+
+ /* Technically, this is not quite right. If the printer has a separator
+ * page turned on, the NT spooler prints the separator page even if the
+ * print job is 0 bytes. 010215 JRR */
+ if (pjob->size == 0 || pjob->status == LPQ_DELETING) {
+ /* don't bother spooling empty files or something being deleted. */
+ DEBUG(5,("print_job_end: canceling spool of %s (%s)\n",
+ pjob->filename, pjob->size ? "deleted" : "zero length" ));
+ unlink(pjob->filename);
+ pjob_delete(snum, jobid);
+ return True;
+ }
+
+ pjob->smbjob = jobid;
+
+ ret = (*(current_printif->job_submit))(snum, pjob);
+
+ if (ret)
+ goto fail;
+
+ /* The print job has been sucessfully handed over to the back-end */
+
+ pjob->spooled = True;
+ pjob->status = LPQ_QUEUED;
+ pjob_store(snum, jobid, pjob);
+
+ /* make sure the database is up to date */
+ if (print_cache_expired(snum))
+ print_queue_update(snum);
+
+ return True;
+
+fail:
+
+ /* The print job was not succesfully started. Cleanup */
+ /* Still need to add proper error return propagation! 010122:JRR */
+ unlink(pjob->filename);
+ pjob_delete(snum, jobid);
+ remove_from_jobs_changed(snum, jobid);
+ return False;
+}
+
+/****************************************************************************
+ Get a snapshot of jobs in the system without traversing.
+****************************************************************************/
+
+static BOOL get_stored_queue_info(struct tdb_print_db *pdb, int snum, int *pcount, print_queue_struct **ppqueue)
+{
+ TDB_DATA data, key, cgdata;
+ print_queue_struct *queue = NULL;
+ uint32 qcount = 0;
+ uint32 extra_count = 0;
+ int total_count = 0;
+ size_t len = 0;
+ uint32 i;
+ int max_reported_jobs = lp_max_reported_jobs(snum);
+ BOOL ret = False;
+
+ /* make sure the database is up to date */
+ if (print_cache_expired(snum))
+ print_queue_update(snum);
+
+ *pcount = 0;
+ *ppqueue = NULL;
+
+ ZERO_STRUCT(data);
+ ZERO_STRUCT(cgdata);
+ key.dptr = "INFO/linear_queue_array";
+ key.dsize = strlen(key.dptr);
+
+ /* Get the stored queue data. */
+ data = tdb_fetch(pdb->tdb, key);
+
+ if (data.dptr && data.dsize >= sizeof(qcount))
+ len += tdb_unpack(data.dptr + len, data.dsize - len, "d", &qcount);
+
+ /* Get the changed jobs list. */
+ key.dptr = "INFO/jobs_changed";
+ key.dsize = strlen(key.dptr);
+
+ cgdata = tdb_fetch(pdb->tdb, key);
+ if (cgdata.dptr != NULL && (cgdata.dsize % 4 == 0))
+ extra_count = cgdata.dsize/4;
+
+ DEBUG(5,("get_stored_queue_info: qcount = %u, extra_count = %u\n", (unsigned int)qcount, (unsigned int)extra_count));
+
+ /* Allocate the queue size. */
+ if (qcount == 0 && extra_count == 0)
+ goto out;
+
+ if ((queue = (print_queue_struct *)malloc(sizeof(print_queue_struct)*(qcount + extra_count))) == NULL)
+ goto out;
+
+ /* Retrieve the linearised queue data. */
+
+ for( i = 0; i < qcount; i++) {
+ uint32 qjob, qsize, qpage_count, qstatus, qpriority, qtime;
+ len += tdb_unpack(data.dptr + len, data.dsize - len, "ddddddff",
+ &qjob,
+ &qsize,
+ &qpage_count,
+ &qstatus,
+ &qpriority,
+ &qtime,
+ queue[i].fs_user,
+ queue[i].fs_file);
+ queue[i].job = qjob;
+ queue[i].size = qsize;
+ queue[i].page_count = qpage_count;
+ queue[i].status = qstatus;
+ queue[i].priority = qpriority;
+ queue[i].time = qtime;
+ }
+
+ total_count = qcount;
+
+ /* Add in the changed jobids. */
+ for( i = 0; i < extra_count; i++) {
+ uint32 jobid;
+ struct printjob *pjob;
+
+ jobid = IVAL(cgdata.dptr, i*4);
+ DEBUG(5,("get_stored_queue_info: changed job = %u\n", (unsigned int)jobid));
+ pjob = print_job_find(snum, jobid);
+ if (!pjob) {
+ DEBUG(5,("get_stored_queue_info: failed to find changed job = %u\n", (unsigned int)jobid));
+ remove_from_jobs_changed(snum, jobid);
+ continue;
+ }
+
+ queue[total_count].job = jobid;
+ queue[total_count].size = pjob->size;
+ queue[total_count].page_count = pjob->page_count;
+ queue[total_count].status = pjob->status;
+ queue[total_count].priority = 1;
+ queue[total_count].time = pjob->starttime;
+ fstrcpy(queue[total_count].fs_user, pjob->user);
+ fstrcpy(queue[total_count].fs_file, pjob->jobname);
+ total_count++;
+ }
+
+ /* Sort the queue by submission time otherwise they are displayed
+ in hash order. */
+
+ qsort(queue, total_count, sizeof(print_queue_struct), QSORT_CAST(printjob_comp));
+
+ DEBUG(5,("get_stored_queue_info: total_count = %u\n", (unsigned int)total_count));
+
+ if (max_reported_jobs && total_count > max_reported_jobs)
+ total_count = max_reported_jobs;
+
+ *ppqueue = queue;
+ *pcount = total_count;
+
+ ret = True;
+
+ out:
+
+ SAFE_FREE(data.dptr);
+ SAFE_FREE(cgdata.dptr);
+ return ret;
+}
+
+/****************************************************************************
+ Get a printer queue listing.
+ set queue = NULL and status = NULL if you just want to update the cache
+****************************************************************************/
+
+int print_queue_status(int snum,
+ print_queue_struct **ppqueue,
+ print_status_struct *status)
+{
+ fstring keystr;
+ TDB_DATA data, key;
+ const char *printername;
+ struct tdb_print_db *pdb;
+ int count = 0;
+
+ /* make sure the database is up to date */
+
+ if (print_cache_expired(snum))
+ print_queue_update(snum);
+
+ /* return if we are done */
+ if ( !ppqueue || !status )
+ return 0;
+
+ *ppqueue = NULL;
+ printername = lp_const_servicename(snum);
+ pdb = get_print_db_byname(printername);
+
+ if (!pdb)
+ return 0;
+
+ /*
+ * Fetch the queue status. We must do this first, as there may
+ * be no jobs in the queue.
+ */
+
+ ZERO_STRUCTP(status);
+ slprintf(keystr, sizeof(keystr)-1, "STATUS/%s", printername);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr);
+ data = tdb_fetch(pdb->tdb, key);
+ if (data.dptr) {
+ if (data.dsize == sizeof(*status)) {
+ /* this memcpy is ok since the status struct was
+ not packed before storing it in the tdb */
+ memcpy(status, data.dptr, sizeof(*status));
+ }
+ SAFE_FREE(data.dptr);
+ }
+
+ /*
+ * Now, fetch the print queue information. We first count the number
+ * of entries, and then only retrieve the queue if necessary.
+ */
+
+ if (!get_stored_queue_info(pdb, snum, &count, ppqueue)) {
+ release_print_db(pdb);
+ return 0;
+ }
+
+ release_print_db(pdb);
+ return count;
+}
+
+/****************************************************************************
+ Pause a queue.
+****************************************************************************/
+
+BOOL print_queue_pause(struct current_user *user, int snum, WERROR *errcode)
+{
+ int ret;
+ struct printif *current_printif = get_printer_fns( snum );
+
+ if (!print_access_check(user, snum, PRINTER_ACCESS_ADMINISTER)) {
+ *errcode = WERR_ACCESS_DENIED;
+ return False;
+ }
+
+ ret = (*(current_printif->queue_pause))(snum);
+
+ if (ret != 0) {
+ *errcode = WERR_INVALID_PARAM;
+ return False;
+ }
+
+ /* force update the database */
+ print_cache_flush(snum);
+
+ /* Send a printer notify message */
+
+ notify_printer_status(snum, PRINTER_STATUS_PAUSED);
+
+ return True;
+}
+
+/****************************************************************************
+ Resume a queue.
+****************************************************************************/
+
+BOOL print_queue_resume(struct current_user *user, int snum, WERROR *errcode)
+{
+ int ret;
+ struct printif *current_printif = get_printer_fns( snum );
+
+ if (!print_access_check(user, snum, PRINTER_ACCESS_ADMINISTER)) {
+ *errcode = WERR_ACCESS_DENIED;
+ return False;
+ }
+
+ ret = (*(current_printif->queue_resume))(snum);
+
+ if (ret != 0) {
+ *errcode = WERR_INVALID_PARAM;
+ return False;
+ }
+
+ /* make sure the database is up to date */
+ if (print_cache_expired(snum))
+ print_queue_update(snum);
+
+ /* Send a printer notify message */
+
+ notify_printer_status(snum, PRINTER_STATUS_OK);
+
+ return True;
+}
+
+/****************************************************************************
+ Purge a queue - implemented by deleting all jobs that we can delete.
+****************************************************************************/
+
+BOOL print_queue_purge(struct current_user *user, int snum, WERROR *errcode)
+{
+ print_queue_struct *queue;
+ print_status_struct status;
+ int njobs, i;
+ BOOL can_job_admin;
+
+ /* Force and update so the count is accurate (i.e. not a cached count) */
+ print_queue_update(snum);
+
+ can_job_admin = print_access_check(user, snum, JOB_ACCESS_ADMINISTER);
+ njobs = print_queue_status(snum, &queue, &status);
+
+ for (i=0;i<njobs;i++) {
+ BOOL owner = is_owner(user, snum, queue[i].job);
+
+ if (owner || can_job_admin) {
+ print_job_delete1(snum, queue[i].job);
+ }
+ }
+
+ SAFE_FREE(queue);
+
+ return True;
+}
diff --git a/source/printing/printing_db.c b/source/printing/printing_db.c
new file mode 100644
index 00000000000..d402aa366f4
--- /dev/null
+++ b/source/printing/printing_db.c
@@ -0,0 +1,206 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ printing backend routines
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Jeremy Allison 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "printing.h"
+
+static struct tdb_print_db *print_db_head;
+
+/****************************************************************************
+ Function to find or create the printer specific job tdb given a printername.
+ Limits the number of tdb's open to MAX_PRINT_DBS_OPEN.
+****************************************************************************/
+
+struct tdb_print_db *get_print_db_byname(const char *printername)
+{
+ struct tdb_print_db *p = NULL, *last_entry = NULL;
+ int num_open = 0;
+ pstring printdb_path;
+ BOOL done_become_root = False;
+
+ for (p = print_db_head, last_entry = print_db_head; p; p = p->next) {
+ /* Ensure the list terminates... JRA. */
+ SMB_ASSERT(p->next != print_db_head);
+
+ if (p->tdb && strequal(p->printer_name, printername)) {
+ DLIST_PROMOTE(print_db_head, p);
+ p->ref_count++;
+ return p;
+ }
+ num_open++;
+ last_entry = p;
+ }
+
+ /* Not found. */
+ if (num_open >= MAX_PRINT_DBS_OPEN) {
+ /* Try and recycle the last entry. */
+ DLIST_PROMOTE(print_db_head, last_entry);
+
+ for (p = print_db_head; p; p = p->next) {
+ if (p->ref_count)
+ continue;
+ if (p->tdb) {
+ if (tdb_close(print_db_head->tdb)) {
+ DEBUG(0,("get_print_db: Failed to close tdb for printer %s\n",
+ print_db_head->printer_name ));
+ return NULL;
+ }
+ }
+ p->tdb = NULL;
+ p->ref_count = 0;
+ memset(p->printer_name, '\0', sizeof(p->printer_name));
+ break;
+ }
+ if (p) {
+ DLIST_PROMOTE(print_db_head, p);
+ p = print_db_head;
+ }
+ }
+
+ if (!p) {
+ /* Create one. */
+ p = (struct tdb_print_db *)malloc(sizeof(struct tdb_print_db));
+ if (!p) {
+ DEBUG(0,("get_print_db: malloc fail !\n"));
+ return NULL;
+ }
+ ZERO_STRUCTP(p);
+ DLIST_ADD(print_db_head, p);
+ }
+
+ pstrcpy(printdb_path, lock_path("printing/"));
+ pstrcat(printdb_path, printername);
+ pstrcat(printdb_path, ".tdb");
+
+ if (geteuid() != 0) {
+ become_root();
+ done_become_root = True;
+ }
+
+ p->tdb = tdb_open_ex(printdb_path, 5000, TDB_DEFAULT, O_RDWR|O_CREAT,
+ 0600, smbd_tdb_log);
+
+ if (done_become_root)
+ unbecome_root();
+
+ if (!p->tdb) {
+ DEBUG(0,("get_print_db: Failed to open printer backend database %s.\n",
+ printdb_path ));
+ DLIST_REMOVE(print_db_head, p);
+ SAFE_FREE(p);
+ return NULL;
+ }
+ fstrcpy(p->printer_name, printername);
+ p->ref_count++;
+ return p;
+}
+
+/***************************************************************************
+ Remove a reference count.
+****************************************************************************/
+
+void release_print_db( struct tdb_print_db *pdb)
+{
+ pdb->ref_count--;
+ SMB_ASSERT(pdb->ref_count >= 0);
+}
+
+/***************************************************************************
+ Close all open print db entries.
+****************************************************************************/
+
+void close_all_print_db(void)
+{
+ struct tdb_print_db *p = NULL, *next_p = NULL;
+
+ for (p = print_db_head; p; p = next_p) {
+ next_p = p->next;
+
+ if (p->tdb)
+ tdb_close(p->tdb);
+ DLIST_REMOVE(print_db_head, p);
+ ZERO_STRUCTP(p);
+ SAFE_FREE(p);
+ }
+}
+
+
+/****************************************************************************
+ Fetch and clean the pid_t record list for all pids interested in notify
+ messages. data needs freeing on exit.
+****************************************************************************/
+
+TDB_DATA get_printer_notify_pid_list(TDB_CONTEXT *tdb, const char *printer_name, BOOL cleanlist)
+{
+ TDB_DATA data;
+ size_t i;
+
+ ZERO_STRUCT(data);
+
+ data = tdb_fetch_bystring( tdb, NOTIFY_PID_LIST_KEY );
+
+ if (!data.dptr) {
+ ZERO_STRUCT(data);
+ return data;
+ }
+
+ if (data.dsize % 8) {
+ DEBUG(0,("get_printer_notify_pid_list: Size of record for printer %s not a multiple of 8 !\n", printer_name ));
+ tdb_delete_bystring(tdb, NOTIFY_PID_LIST_KEY );
+ SAFE_FREE(data.dptr);
+ ZERO_STRUCT(data);
+ return data;
+ }
+
+ if (!cleanlist)
+ return data;
+
+ /*
+ * Weed out all dead entries.
+ */
+
+ for( i = 0; i < data.dsize; i += 8) {
+ pid_t pid = (pid_t)IVAL(data.dptr, i);
+
+ if (pid == sys_getpid())
+ continue;
+
+ /* Entry is dead if process doesn't exist or refcount is zero. */
+
+ while ((i < data.dsize) && ((IVAL(data.dptr, i + 4) == 0) || !process_exists(pid))) {
+
+ /* Refcount == zero is a logic error and should never happen. */
+ if (IVAL(data.dptr, i + 4) == 0) {
+ DEBUG(0,("get_printer_notify_pid_list: Refcount == 0 for pid = %u printer %s !\n",
+ (unsigned int)pid, printer_name ));
+ }
+
+ if (data.dsize - i > 8)
+ memmove( &data.dptr[i], &data.dptr[i+8], data.dsize - i - 8);
+ data.dsize -= 8;
+ }
+ }
+
+ return data;
+}
+
+
diff --git a/source/profile/profile.c b/source/profile/profile.c
new file mode 100644
index 00000000000..689f67da997
--- /dev/null
+++ b/source/profile/profile.c
@@ -0,0 +1,166 @@
+/*
+ Unix SMB/CIFS implementation.
+ store smbd profiling information in shared memory
+ Copyright (C) Andrew Tridgell 1999
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+
+#ifdef WITH_PROFILE
+#define IPC_PERMS ((SHM_R | SHM_W) | (SHM_R>>3) | (SHM_R>>6))
+#endif /* WITH_PROFILE */
+
+#ifdef WITH_PROFILE
+static int shm_id;
+static BOOL read_only;
+#endif
+
+struct profile_header *profile_h;
+struct profile_stats *profile_p;
+
+BOOL do_profile_flag = False;
+BOOL do_profile_times = False;
+
+struct timeval profile_starttime;
+struct timeval profile_endtime;
+struct timeval profile_starttime_nested;
+struct timeval profile_endtime_nested;
+
+/****************************************************************************
+receive a set profile level message
+****************************************************************************/
+void profile_message(int msg_type, pid_t src, void *buf, size_t len)
+{
+ int level;
+
+ memcpy(&level, buf, sizeof(int));
+#ifdef WITH_PROFILE
+ switch (level) {
+ case 0: /* turn off profiling */
+ do_profile_flag = False;
+ do_profile_times = False;
+ DEBUG(1,("INFO: Profiling turned OFF from pid %d\n", (int)src));
+ break;
+ case 1: /* turn on counter profiling only */
+ do_profile_flag = True;
+ do_profile_times = False;
+ DEBUG(1,("INFO: Profiling counts turned ON from pid %d\n", (int)src));
+ break;
+ case 2: /* turn on complete profiling */
+ do_profile_flag = True;
+ do_profile_times = True;
+ DEBUG(1,("INFO: Full profiling turned ON from pid %d\n", (int)src));
+ break;
+ case 3: /* reset profile values */
+ memset((char *)profile_p, 0, sizeof(*profile_p));
+ DEBUG(1,("INFO: Profiling values cleared from pid %d\n", (int)src));
+ break;
+ }
+#else /* WITH_PROFILE */
+ DEBUG(1,("INFO: Profiling support unavailable in this build.\n"));
+#endif /* WITH_PROFILE */
+}
+
+/****************************************************************************
+receive a request profile level message
+****************************************************************************/
+void reqprofile_message(int msg_type, pid_t src, void *buf, size_t len)
+{
+ int level;
+
+#ifdef WITH_PROFILE
+ level = 1 + (do_profile_flag?2:0) + (do_profile_times?4:0);
+#else
+ level = 0;
+#endif
+ DEBUG(1,("INFO: Received REQ_PROFILELEVEL message from PID %u\n",(unsigned int)src));
+ message_send_pid(src, MSG_PROFILELEVEL, &level, sizeof(int), True);
+}
+
+/*******************************************************************
+ open the profiling shared memory area
+ ******************************************************************/
+#ifdef WITH_PROFILE
+BOOL profile_setup(BOOL rdonly)
+{
+ struct shmid_ds shm_ds;
+
+ read_only = rdonly;
+
+ again:
+ /* try to use an existing key */
+ shm_id = shmget(PROF_SHMEM_KEY, 0, 0);
+
+ /* if that failed then create one. There is a race condition here
+ if we are running from inetd. Bad luck. */
+ if (shm_id == -1) {
+ if (read_only) return False;
+ shm_id = shmget(PROF_SHMEM_KEY, sizeof(*profile_h),
+ IPC_CREAT | IPC_EXCL | IPC_PERMS);
+ }
+
+ if (shm_id == -1) {
+ DEBUG(0,("Can't create or use IPC area. Error was %s\n",
+ strerror(errno)));
+ return False;
+ }
+
+
+ profile_h = (struct profile_header *)shmat(shm_id, 0,
+ read_only?SHM_RDONLY:0);
+ if ((long)profile_p == -1) {
+ DEBUG(0,("Can't attach to IPC area. Error was %s\n",
+ strerror(errno)));
+ return False;
+ }
+
+ /* find out who created this memory area */
+ if (shmctl(shm_id, IPC_STAT, &shm_ds) != 0) {
+ DEBUG(0,("ERROR shmctl : can't IPC_STAT. Error was %s\n",
+ strerror(errno)));
+ return False;
+ }
+
+ if (shm_ds.shm_perm.cuid != sec_initial_uid() || shm_ds.shm_perm.cgid != sec_initial_gid()) {
+ DEBUG(0,("ERROR: we did not create the shmem (owned by another user)\n"));
+ return False;
+ }
+
+ if (shm_ds.shm_segsz != sizeof(*profile_h)) {
+ DEBUG(0,("WARNING: profile size is %d (expected %d). Deleting\n",
+ (int)shm_ds.shm_segsz, sizeof(*profile_h)));
+ if (shmctl(shm_id, IPC_RMID, &shm_ds) == 0) {
+ goto again;
+ } else {
+ return False;
+ }
+ }
+
+ if (!read_only && (shm_ds.shm_nattch == 1)) {
+ memset((char *)profile_h, 0, sizeof(*profile_h));
+ profile_h->prof_shm_magic = PROF_SHM_MAGIC;
+ profile_h->prof_shm_version = PROF_SHM_VERSION;
+ DEBUG(3,("Initialised profile area\n"));
+ }
+
+ profile_p = &profile_h->stats;
+ message_register(MSG_PROFILE, profile_message);
+ message_register(MSG_REQ_PROFILELEVEL, reqprofile_message);
+ return True;
+}
+#endif /* WITH_PROFILE */
diff --git a/source/python/.cvsignore b/source/python/.cvsignore
new file mode 100644
index 00000000000..7e99e367f84
--- /dev/null
+++ b/source/python/.cvsignore
@@ -0,0 +1 @@
+*.pyc \ No newline at end of file
diff --git a/source/python/README b/source/python/README
new file mode 100644
index 00000000000..04f794215ab
--- /dev/null
+++ b/source/python/README
@@ -0,0 +1,28 @@
+This directory contains Python bindings to allow you to access various
+aspects of Samba. At the moment their status is "experimental" and
+they are not built by default.
+
+In order to be able to compile samba-python you need to have python
+and the python-dev packages installed.
+
+Python libraries are always built for a particular version of Python
+(2.2, 2.1, etc), and libraries built for one version will not be seen
+by another. By default Samba's libraries are built for whatever is
+installed as "python" on your $PATH, but you can override this using
+the --with-python option. For example
+
+ $ ./configure --with-python=python2.2
+
+To build:
+
+$ autoconf
+$ ./configure
+$ make python_ext
+
+Now, you can install the modules:
+
+$ cp build/lib.*/*.so /usr/lib/python2.1/lib-dynload/
+
+(the directory /usr/lib/python2.1 may vary, depending on your installation)
+
+Samba-python should work now!
diff --git a/source/python/examples/spoolss/changeid.py b/source/python/examples/spoolss/changeid.py
new file mode 100755
index 00000000000..85fe0efe8a4
--- /dev/null
+++ b/source/python/examples/spoolss/changeid.py
@@ -0,0 +1,34 @@
+#!/usr/bin/python
+#
+# Display the changeid for a list of printers given on the command line
+#
+# Sample usage:
+#
+# changeid.py '\\win2kdc1\magpie'
+#
+
+import sys
+from samba import spoolss
+
+if len(sys.argv) == 1:
+ print "Usage: changeid.py <printername>"
+ sys.exit(1)
+
+for printer in sys.argv[1:]:
+
+ # Open printer handle
+
+ try:
+ hnd = spoolss.openprinter(printer)
+ except:
+ print "error opening printer %s" % printer
+ sys.exit(1)
+
+ # Fetch and display changeid
+
+ info = hnd.getprinter(level = 0)
+ print info["change_id"]
+
+ # Clean up
+
+ spoolss.closeprinter(hnd)
diff --git a/source/python/examples/spoolss/enumprinters.py b/source/python/examples/spoolss/enumprinters.py
new file mode 100755
index 00000000000..478c46bc24c
--- /dev/null
+++ b/source/python/examples/spoolss/enumprinters.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+#
+# Display information on all printers on a print server. Defaults to
+# printer info level 1.
+#
+# Example: enumprinters.py win2kdc1
+#
+
+import sys
+from samba import spoolss
+
+if len(sys.argv) < 2 or len(sys.argv) > 3:
+ print "Usage: enumprinters.py <servername> [infolevel]"
+ sys.exit(1)
+
+printserver = sys.argv[1]
+
+level = 1
+if len(sys.argv) == 3:
+ level = int(sys.argv[2])
+
+# Get list of printers
+
+try:
+ printer_list = spoolss.enumprinters("\\\\%s" % printserver)
+except:
+ print "error enumerating printers on %s" % printserver
+ sys.exit(1)
+
+# Display basic info
+
+for printer in printer_list:
+ h = spoolss.openprinter("\\\\%s\\%s" % (printserver, printer))
+ info = h.getprinter(level = level)
+ print "Printer info %d for %s: %s" % (level, printer, info)
+ print
diff --git a/source/python/examples/spoolss/psec.py b/source/python/examples/spoolss/psec.py
new file mode 100755
index 00000000000..498a0ef1744
--- /dev/null
+++ b/source/python/examples/spoolss/psec.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+#
+# Get or set the security descriptor on a printer
+#
+
+import sys, re, string
+from samba import spoolss
+
+if len(sys.argv) != 3:
+ print "Usage: psec.py getsec|setsec printername"
+ sys.exit(1)
+
+op = sys.argv[1]
+printername = sys.argv[2]
+
+# Display security descriptor
+
+if op == "getsec":
+
+ try:
+ hnd = spoolss.openprinter(printername)
+ except:
+ print "error opening printer %s" % printername
+ sys.exit(1)
+
+ secdesc = hnd.getprinter(level = 3)["security_descriptor"]
+
+ print secdesc["owner_sid"]
+ print secdesc["group_sid"]
+
+ for acl in secdesc["dacl"]["ace_list"]:
+ print "%d %d 0x%08x %s" % (acl["type"], acl["flags"],
+ acl["mask"], acl["trustee"])
+
+ spoolss.closeprinter(hnd)
+
+ sys.exit(0)
+
+# Set security descriptor
+
+if op == "setsec":
+
+ # Open printer
+
+ try:
+ hnd = spoolss.openprinter(printername,
+ creds = {"domain": "NPSD-TEST2",
+ "username": "Administrator",
+ "password": "penguin"})
+ except:
+ print "error opening printer %s" % printername
+ sys.exit(1)
+
+ # Read lines from standard input and build security descriptor
+
+ lines = sys.stdin.readlines()
+
+ secdesc = {}
+
+ secdesc["owner_sid"] = lines[0]
+ secdesc["group_sid"] = lines[1]
+
+ secdesc["revision"] = 1
+ secdesc["dacl"] = {}
+ secdesc["dacl"]["revision"] = 2
+ secdesc["dacl"]["ace_list"] = []
+
+ for acl in lines[2:]:
+ match = re.match("(\d+) (\d+) (0[xX][\dA-Fa-f]+) (\S+)", acl)
+ secdesc["dacl"]["ace_list"].append(
+ {"type": int(match.group(1)), "flags": int(match.group(2)),
+ "mask": string.atoi(match.group(3), 0), "trustee": match.group(4)})
+
+ # Build info3 structure
+
+ info3 = {}
+
+ info3["flags"] = 0x8004 # self-relative, dacl present
+ info3["level"] = 3
+ info3["security_descriptor"] = secdesc
+
+ hnd.setprinter(info3)
+
+ spoolss.closeprinter(hnd)
+ sys.exit(0)
+
+print "invalid operation %s" % op
+sys.exit(1)
diff --git a/source/python/examples/tdbpack/.cvsignore b/source/python/examples/tdbpack/.cvsignore
new file mode 100644
index 00000000000..52e4e61140d
--- /dev/null
+++ b/source/python/examples/tdbpack/.cvsignore
@@ -0,0 +1,2 @@
+*.pyc
+*.pyo
diff --git a/source/python/examples/tdbpack/oldtdbutil.py b/source/python/examples/tdbpack/oldtdbutil.py
new file mode 100644
index 00000000000..ac435b8bacf
--- /dev/null
+++ b/source/python/examples/tdbpack/oldtdbutil.py
@@ -0,0 +1,144 @@
+#!/usr/bin/python
+#############################################################
+# tdbutil
+#
+# Purpose:
+# Contains functions that are used to pack and unpack data
+# from Samba's tdb databases. Samba sometimes represents complex
+# data structures as a single value in a database. These functions
+# allow other python scripts to package data types into a single python
+# string and unpackage them.
+#
+#
+# XXXXX: This code is no longer used; it's just here for testing
+# compatibility with the new (much faster) C implementation.
+#
+##############################################################
+import string
+
+def pack(format,list):
+ retstring = ''
+ listind = 0
+
+ # Cycle through format entries
+ for type in format:
+ # Null Terminated String
+ if (type == 'f' or type == 'P'):
+ retstring = retstring + list[listind] + "\000"
+ # 4 Byte Number
+ if (type == 'd'):
+ retstring = retstring + PackNum(list[listind],4)
+ # 2 Byte Number
+ if (type == 'w'):
+ retstring = retstring + PackNum(list[listind],2)
+ # Pointer Value
+ if (type == 'p'):
+ if (list[listind]):
+ retstring = retstring + PackNum(1,4)
+ else:
+ retstring = retstring + PackNum(0,4)
+ # Buffer and Length
+ if (type == 'B'):
+ # length
+ length = list[listind]
+ retstring = retstring + PackNum(length,4)
+ length = int(length)
+ listind = listind + 1
+ # buffer
+ retstring = retstring + list[listind][:length]
+
+ listind = listind + 1
+
+ return retstring
+
+def unpack(format,buffer):
+ retlist = []
+ bufind = 0
+
+ lasttype = ""
+ for type in format:
+ # Pointer Value
+ if (type == 'p'):
+ newvalue = UnpackNum(buffer[bufind:bufind+4])
+ bufind = bufind + 4
+ if (newvalue):
+ newvalue = 1L
+ else:
+ newvalue = 0L
+ retlist.append(newvalue)
+ # Previous character till end of data
+ elif (type == '$'):
+ if (lasttype == 'f'):
+ while (bufind < len(buffer)):
+ newstring = ''
+ while (buffer[bufind] != '\000'):
+ newstring = newstring + buffer[bufind]
+ bufind = bufind + 1
+ bufind = bufind + 1
+ retlist.append(newstring)
+ # Null Terminated String
+ elif (type == 'f' or type == 'P'):
+ newstring = ''
+ while (buffer[bufind] != '\000'):
+ newstring = newstring + buffer[bufind]
+ bufind = bufind + 1
+ bufind = bufind + 1
+ retlist.append(newstring)
+ # 4 Byte Number
+ elif (type == 'd'):
+ newvalue = UnpackNum(buffer[bufind:bufind+4])
+ bufind = bufind + 4
+ retlist.append(newvalue)
+ # 2 Byte Number
+ elif (type == 'w'):
+ newvalue = UnpackNum(buffer[bufind:bufind+2])
+ bufind = bufind + 2
+ retlist.append(newvalue)
+ # Length and Buffer
+ elif (type == 'B'):
+ # Length
+ length = UnpackNum(buffer[bufind:bufind+4])
+ bufind = bufind + 4
+ retlist.append(length)
+ length = int(length)
+ # Buffer
+ retlist.append(buffer[bufind:bufind+length])
+ bufind = bufind + length
+
+ lasttype = type
+
+ return ((retlist,buffer[bufind:]))
+
+def PackNum(myint,size):
+ retstring = ''
+ size = size * 2
+ hint = hex(myint)[2:]
+
+ # Check for long notation
+ if (hint[-1:] == 'L'):
+ hint = hint[:-1]
+
+ addon = size - len(hint)
+ for i in range(0,addon):
+ hint = '0' + hint
+
+ while (size > 0):
+ val = string.atoi(hint[size-2:size],16)
+ retstring = retstring + chr(val)
+ size = size - 2
+
+ return retstring
+
+def UnpackNum(buffer):
+ size = len(buffer)
+ mystring = ''
+
+ for i in range(size-1,-1,-1):
+ val = hex(ord(buffer[i]))[2:]
+ if (len(val) == 1):
+ val = '0' + val
+ mystring = mystring + val
+ if (len(mystring) > 4):
+ return string.atol(mystring,16)
+ else:
+ return string.atoi(mystring,16)
diff --git a/source/python/examples/tdbpack/tdbtimetrial.py b/source/python/examples/tdbpack/tdbtimetrial.py
new file mode 100755
index 00000000000..be6404899d8
--- /dev/null
+++ b/source/python/examples/tdbpack/tdbtimetrial.py
@@ -0,0 +1,12 @@
+#! /usr/bin/python2.2
+
+def run_trial():
+ # import tdbutil
+ from samba.tdbpack import pack
+
+ for i in xrange(500000):
+ pack("ddffd", (10, 2, "mbp", "martin", 0))
+ #s = "\n\0\0\0" + "\x02\0\0\0" + "mbp\0" + "martin\0" + "\0\0\0\0"
+
+if __name__ == '__main__':
+ run_trial()
diff --git a/source/python/examples/tdbpack/test_tdbpack.py b/source/python/examples/tdbpack/test_tdbpack.py
new file mode 100755
index 00000000000..837600f789e
--- /dev/null
+++ b/source/python/examples/tdbpack/test_tdbpack.py
@@ -0,0 +1,253 @@
+#! /usr/bin/env python2.2
+
+__doc__ = """test case for samba.tdbpack functions
+
+tdbpack provides a means of pickling values into binary formats
+compatible with that used by the samba tdbpack()/tdbunpack()
+functions.
+
+Numbers are always stored in little-endian format; strings are stored
+in either DOS or Unix codepage as appropriate.
+
+The format for any particular element is encoded as a short ASCII
+string, with one character per field."""
+
+# Copyright (C) 2002 Hewlett-Packard.
+
+__author__ = 'Martin Pool <mbp@sourcefrog.net>'
+
+import unittest
+import oldtdbutil
+import samba.tdbpack
+
+both_unpackers = (samba.tdbpack.unpack, oldtdbutil.unpack)
+both_packers = (samba.tdbpack.pack, oldtdbutil.pack)
+
+
+
+# # ('B', [10, 'hello'], '\x0a\0\0\0hello'),
+# ('BB', [11, 'hello\0world', 3, 'now'],
+# '\x0b\0\0\0hello\0world\x03\0\0\0now'),
+# ('pd', [1, 10], '\x01\0\0\0\x0a\0\0\0'),
+# ('BBB', [5, 'hello', 0, '', 5, 'world'],
+# '\x05\0\0\0hello\0\0\0\0\x05\0\0\0world'),
+
+ # strings are sequences in Python, there's no getting away
+ # from it
+# ('ffff', 'evil', 'e\0v\0i\0l\0'),
+# ('BBBB', 'evil',
+# '\x01\0\0\0e'
+# '\x01\0\0\0v'
+# '\x01\0\0\0i'
+# '\x01\0\0\0l'),
+
+# ('', [], ''),
+
+# # exercise some long strings
+# ('PP', ['hello' * 255, 'world' * 255],
+# 'hello' * 255 + '\0' + 'world' * 255 + '\0'),
+# ('PP', ['hello' * 40000, 'world' * 50000],
+# 'hello' * 40000 + '\0' + 'world' * 50000 + '\0'),
+# ('B', [(5*51), 'hello' * 51], '\xff\0\0\0' + 'hello' * 51),
+# ('BB', [(5 * 40000), 'hello' * 40000,
+# (5 * 50000), 'world' * 50000],
+# '\x40\x0d\x03\0' + 'hello' * 40000 + '\x90\xd0\x03\x00' + 'world' * 50000),
+
+
+class PackTests(unittest.TestCase):
+ symm_cases = [
+ ('w', [42], '\x2a\0'),
+ ('www', [42, 2, 69], '\x2a\0\x02\0\x45\0'),
+ ('wd', [42, 256], '\x2a\0\0\x01\0\0'),
+ ('w', [0], '\0\0'),
+ ('w', [255], '\xff\0'),
+ ('w', [256], '\0\x01'),
+ ('w', [0xdead], '\xad\xde'),
+ ('w', [0xffff], '\xff\xff'),
+ ('p', [0], '\0\0\0\0'),
+ ('p', [1], '\x01\0\0\0'),
+ ('d', [0x01020304], '\x04\x03\x02\x01'),
+ ('d', [0x7fffffff], '\xff\xff\xff\x7f'),
+ ('d', [0x80000000L], '\x00\x00\x00\x80'),
+ ('d', [0x80000069L], '\x69\x00\x00\x80'),
+ ('d', [0xffffffffL], '\xff\xff\xff\xff'),
+ ('d', [0xffffff00L], '\x00\xff\xff\xff'),
+ ('ddd', [1, 10, 50], '\x01\0\0\0\x0a\0\0\0\x32\0\0\0'),
+ ('ff', ['hello', 'world'], 'hello\0world\0'),
+ ('fP', ['hello', 'world'], 'hello\0world\0'),
+ ('PP', ['hello', 'world'], 'hello\0world\0'),
+ ('B', [0, ''], '\0\0\0\0'),
+# old implementation is wierd when string is not the right length
+# ('B', [2, 'hello'], '\x0a\0\0\0hello'),
+ ('B', [5, 'hello'], '\x05\0\0\0hello'),
+ ]
+
+ def test_symmetric(self):
+ """Cookbook of symmetric pack/unpack tests
+ """
+ for packer in [samba.tdbpack.pack]: # both_packers:
+ for unpacker in both_unpackers:
+ for format, values, expected in self.symm_cases:
+ out_packed = packer(format, values)
+ self.assertEquals(out_packed, expected)
+ out, rest = unpacker(format, expected)
+ self.assertEquals(rest, '')
+ self.assertEquals(list(values), list(out))
+
+ def test_large(self):
+ """Test large pack/unpack strings"""
+ large_cases = [('w' * 1000, xrange(1000)), ]
+ for packer in both_packers:
+ for unpacker in both_unpackers:
+ for format, values in large_cases:
+ packed = packer(format, values)
+ out, rest = unpacker(format, packed)
+ self.assertEquals(rest, '')
+ self.assertEquals(list(values), list(out))
+
+
+ def test_pack(self):
+ """Cookbook of expected pack values
+
+ These can't be used for the symmetric test because the unpacked value is
+ not "canonical".
+ """
+ cases = [('w', (42,), '\x2a\0'),
+ ]
+
+ for packer in both_packers:
+ for format, values, expected in cases:
+ self.assertEquals(packer(format, values), expected)
+
+ def test_unpack_extra(self):
+ # Test leftover data
+ for unpacker in both_unpackers:
+ for format, values, packed in self.symm_cases:
+ out, rest = unpacker(format, packed + 'hello sailor!')
+ self.assertEquals(rest, 'hello sailor!')
+ self.assertEquals(list(values), list(out))
+
+
+ def test_pack_extra(self):
+ """Leftover values when packing"""
+ cases = [
+ ('d', [10, 20], [10]),
+ ('d', [10, 'hello'], [10]),
+ ('ff', ['hello', 'world', 'sailor'], ['hello', 'world']),
+ ]
+ for unpacker in both_unpackers:
+ for packer in both_packers:
+ for format, values, chopped in cases:
+ bin = packer(format, values)
+ out, rest = unpacker(format, bin)
+ self.assertEquals(list(out), list(chopped))
+ self.assertEquals(rest, '')
+
+
+ def test_unpack(self):
+ """Cookbook of tricky unpack tests"""
+ cases = [
+ # Apparently I couldn't think of any tests that weren't
+ # symmetric :-/
+ ]
+ for unpacker in both_unpackers:
+ for format, values, expected in cases:
+ out, rest = unpacker(format, expected)
+ self.assertEquals(rest, '')
+ self.assertEquals(list(values), list(out))
+
+
+ def test_pack_failures(self):
+ """Expected errors for incorrect packing"""
+ cases = [('w', []),
+# ('w', ()),
+# ('w', {}),
+ ('ww', [2]),
+ ('w', 2),
+# ('w', None),
+ ('wwwwwwwwwwww', []),
+# ('w', [0x60A15EC5L]),
+# ('w', [None]),
+ ('d', []),
+ ('p', []),
+ ('f', [2]),
+ ('P', [None]),
+ ('P', ()),
+ ('f', [hex]),
+ ('fw', ['hello']),
+# ('f', [u'hello']),
+ ('B', [2]),
+ (None, [2, 3, 4]),
+ (ord('f'), [20]),
+ # old code doesn't distinguish string from seq-of-char
+# (['w', 'w'], [2, 2]),
+ # old code just ignores invalid characters
+# ('Q', [2]),
+# ('fQ', ['2', 3]),
+# ('fQ', ['2']),
+ (2, [2]),
+ # old code doesn't typecheck format
+# ({}, {})
+ ]
+ for packer in both_packers:
+ for format, values in cases:
+ try:
+ packer(format, values)
+ except StandardError:
+ pass
+ else:
+ raise AssertionError("didn't get exception: format %s, values %s, packer %s"
+ % (`format`, `values`, `packer`))
+
+
+ def test_unpack_failures(self):
+ """Expected errors for incorrect unpacking"""
+ cases = [
+# This ought to be illegal, but the old code doesn't prohibit it
+# ('$', '', ValueError),
+# ('Q', '', ValueError),
+# ('Q$', '', ValueError),
+ ('f', '', IndexError),
+ ('d', '', IndexError),
+# This is an illegal packing, but the old code doesn't trap
+# ('d', '2', IndexError),
+# ('d', '22', IndexError),
+# ('d', '222', IndexError),
+# ('p', '\x01\0', IndexError),
+# ('w', '2', IndexError),
+# ('B', '\xff\0\0\0hello', IndexError),
+# ('B', '\xff\0', IndexError),
+ ('w', '', IndexError),
+ ('f', 'hello', IndexError),
+ ('f', '', IndexError),
+# ('B', '\x01\0\0\0', IndexError),
+# ('B', '\x05\0\0\0hell', IndexError),
+ ('B', '\xff\xff\xff\xff', ValueError),
+# ('B', 'foobar', IndexError),
+# ('BB', '\x01\0\0\0a\x01', IndexError),
+ ]
+
+ for unpacker in both_unpackers:
+ for format, values, throwable_class in cases:
+ try:
+ unpacker(format, values)
+ except StandardError:
+ pass
+ else:
+ raise AssertionError("didn't get exception: format %s, values %s, unpacker %s"
+ % (`format`, `values`, `unpacker`))
+
+ def test_unpack_repeated(self):
+ cases = [(('df$',
+ '\x00\x00\x00\x00HP C LaserJet 4500-PS\x00Windows 4.0\x00\\print$\\WIN40\\0\\PSCRIPT.DRV\x00\\print$\\WIN40\\0\\PSCRIPT.DRV\x00\\print$\\WIN40\\0\\PSCRIPT.DRV\x00\\print$\\WIN40\\0\\PSCRIPT.HLP\x00\x00RAW\x00\\print$\\WIN40\\0\\readme.wri\x00\\print$\\WIN40\\0\\pscript.drv\x00\\print$\\WIN40\\0\\pscript.hlp\x00'),
+ ([0L, 'HP C LaserJet 4500-PS', 'Windows 4.0', '\\print$\\WIN40\\0\\PSCRIPT.DRV', '\\print$\\WIN40\\0\\PSCRIPT.DRV', '\\print$\\WIN40\\0\\PSCRIPT.DRV', '\\print$\\WIN40\\0\\PSCRIPT.HLP', '', 'RAW', '\\print$\\WIN40\\0\\readme.wri', '\\print$\\WIN40\\0\\pscript.drv', '\\print$\\WIN40\\0\\pscript.hlp'], ''))]
+ for unpacker in both_unpackers:
+ for input, expected in cases:
+ result = apply(unpacker, input)
+ if result != expected:
+ raise AssertionError("%s:\n input: %s\n output: %s\n expected: %s" % (`unpacker`, `input`, `result`, `expected`))
+
+
+if __name__ == '__main__':
+ unittest.main()
+
diff --git a/source/python/gprinterdata b/source/python/gprinterdata
new file mode 100755
index 00000000000..cd062076c0b
--- /dev/null
+++ b/source/python/gprinterdata
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+
+import sys
+from gtkdictbrowser import GtkDictBrowser, hex_string
+import gtk
+from samba import spoolss
+import string
+import printerdata
+
+# Initialise printerdata dictionary
+
+if len(sys.argv) < 2 or len(sys.argv) > 3:
+ print "Usage: gprinterdata [--ex] <printer>"
+ print "where <printer> is a UNC printer name."
+ sys.exit(1)
+
+try:
+ host = string.replace(sys.argv[len(sys.argv) - 1], "/", "\\")
+ if sys.argv[1] == "--ex":
+ t = printerdata.printerdata_ex(host)
+ else:
+ t = printerdata.printerdata(host)
+except:
+ print "gprinterdata: error opening %s" % sys.argv[len(sys.argv) - 1]
+ sys.exit(1)
+
+# Create interface
+
+db = GtkDictBrowser(t)
+db.register_get_value_text_fn("", hex_string)
+db.build_ui('gprinterdata')
+
+# Override Python's handling of ctrl-c so we can break out of the
+# gui from the command line.
+
+import signal
+signal.signal(signal.SIGINT, signal.SIG_DFL)
+
+gtk.mainloop()
diff --git a/source/python/gtdbtool b/source/python/gtdbtool
new file mode 100755
index 00000000000..129f4fe0e2e
--- /dev/null
+++ b/source/python/gtdbtool
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+
+import sys
+from gtkdictbrowser import GtkDictBrowser
+import gtk
+from samba import tdb
+import string
+
+# Open handle on tdb
+
+if len(sys.argv) != 2:
+ print "Usage: gdbtool <tdbfile>"
+ sys.exit(1)
+
+try:
+ t = tdb.open(sys.argv[1])
+except tdb.error, t:
+ print "gtdbtool: error opening %s: %s" % (sys.argv[1], t)
+ sys.exit(1)
+
+# Create interface
+
+db = GtkDictBrowser(t)
+
+def display_key_x00(key):
+ """Remove \x00 from all keys as they mucks up GTK."""
+ return string.replace(key, "\x00", "")
+
+db.register_get_key_text_fn(display_key_x00)
+
+db.build_ui('gtdbtool')
+
+# Override Python's handling of ctrl-c so we can break out of the
+# gui from the command line.
+
+import signal
+signal.signal(signal.SIGINT, signal.SIG_DFL)
+
+gtk.mainloop()
diff --git a/source/python/gtkdictbrowser.py b/source/python/gtkdictbrowser.py
new file mode 100755
index 00000000000..dd8bed8f478
--- /dev/null
+++ b/source/python/gtkdictbrowser.py
@@ -0,0 +1,272 @@
+#!/usr/bin/python
+#
+# Browse a Python dictionary in a two pane graphical interface written
+# in GTK.
+#
+# The GtkDictBrowser class is supposed to be generic enough to allow
+# applications to override enough methods and produce a
+# domain-specific browser provided the information is presented as a
+# Python dictionary.
+#
+# Possible applications:
+#
+# - Windows registry browser
+# - SPOOLSS printerdata browser
+# - tdb file browser
+#
+
+from gtk import *
+import string, re
+
+class GtkDictBrowser:
+
+ def __init__(self, dict):
+ self.dict = dict
+
+ # This variable stores a list of (regexp, function) used to
+ # convert the raw value data to a displayable string.
+
+ self.get_value_text_fns = []
+ self.get_key_text = lambda x: x
+
+ # We can filter the list of keys displayed using a regex
+
+ self.filter_regex = ""
+
+ # Create and configure user interface widgets. A string argument is
+ # used to set the window title.
+
+ def build_ui(self, title):
+ win = GtkWindow()
+ win.set_title(title)
+
+ win.connect("destroy", mainquit)
+
+ hpaned = GtkHPaned()
+ win.add(hpaned)
+ hpaned.set_border_width(5)
+ hpaned.show()
+
+ vbox = GtkVBox()
+ hpaned.add1(vbox)
+ vbox.show()
+
+ scrolled_win = GtkScrolledWindow()
+ scrolled_win.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
+ vbox.pack_start(scrolled_win)
+ scrolled_win.show()
+
+ hbox = GtkHBox()
+ vbox.pack_end(hbox, expand = 0, padding = 5)
+ hbox.show()
+
+ label = GtkLabel("Filter:")
+ hbox.pack_start(label, expand = 0, padding = 5)
+ label.show()
+
+ self.entry = GtkEntry()
+ hbox.pack_end(self.entry, padding = 5)
+ self.entry.show()
+
+ self.entry.connect("activate", self.filter_activated)
+
+ self.list = GtkList()
+ self.list.set_selection_mode(SELECTION_MULTIPLE)
+ self.list.set_selection_mode(SELECTION_BROWSE)
+ scrolled_win.add_with_viewport(self.list)
+ self.list.show()
+
+ self.list.connect("select_child", self.key_selected)
+
+ scrolled_win = GtkScrolledWindow()
+ scrolled_win.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
+ hpaned.add2(scrolled_win)
+ scrolled_win.set_usize(500,400)
+ scrolled_win.show()
+
+ self.text = GtkText()
+ self.text.set_editable(FALSE)
+ scrolled_win.add_with_viewport(self.text)
+ self.text.show()
+
+ self.text.connect("event", self.event_handler)
+
+ self.menu = GtkMenu()
+ self.menu.show()
+
+ self.font = load_font("fixed")
+
+ self.update_keylist()
+
+ win.show()
+
+ # Add a key to the left hand side of the user interface
+
+ def add_key(self, key):
+ display_key = self.get_key_text(key)
+ list_item = GtkListItem(display_key)
+ list_item.set_data("raw_key", key) # Store raw key in item data
+ self.list.add(list_item)
+ list_item.show()
+
+ # Event handler registered by build_ui()
+
+ def event_handler(self, event, menu):
+ return FALSE
+
+ # Set the text to appear in the right hand side of the user interface
+
+ def set_value_text(self, item):
+
+ # Clear old old value in text window
+
+ self.text.delete_text(0, self.text.get_length())
+
+ if type(item) == str:
+
+ # The text widget has trouble inserting text containing NULL
+ # characters.
+
+ item = string.replace(item, "\x00", ".")
+
+ self.text.insert(self.font, None, None, item)
+
+ else:
+
+ # A non-text item
+
+ self.text.insert(self.font, None, None, repr(item))
+
+ # This function is called when a key is selected in the left hand side
+ # of the user interface.
+
+ def key_selected(self, list, list_item):
+ key = list_item.children()[0].get()
+
+ # Look for a match in the value display function list
+
+ text = self.dict[list_item.get_data("raw_key")]
+
+ for entry in self.get_value_text_fns:
+ if re.match(entry[0], key):
+ text = entry[1](text)
+ break
+
+ self.set_value_text(text)
+
+ # Refresh the key list by removing all items and re-inserting them.
+ # Items are only inserted if they pass through the filter regexp.
+
+ def update_keylist(self):
+ self.list.remove_items(self.list.children())
+ self.set_value_text("")
+ for k in self.dict.keys():
+ if re.match(self.filter_regex, k):
+ self.add_key(k)
+
+ # Invoked when the user hits return in the filter text entry widget.
+
+ def filter_activated(self, entry):
+ self.filter_regex = entry.get_text()
+ self.update_keylist()
+
+ # Register a key display function
+
+ def register_get_key_text_fn(self, fn):
+ self.get_key_text = fn
+
+ # Register a value display function
+
+ def register_get_value_text_fn(self, regexp, fn):
+ self.get_value_text_fns.append((regexp, fn))
+
+#
+# A utility function to convert a string to the standard hex + ascii format.
+# To display all values in hex do:
+# register_get_value_text_fn("", gtkdictbrowser.hex_string)
+#
+
+def hex_string(data):
+ """Return a hex dump of a string as a string.
+
+ The output produced is in the standard 16 characters per line hex +
+ ascii format:
+
+ 00000000: 40 00 00 00 00 00 00 00 40 00 00 00 01 00 04 80 @....... @.......
+ 00000010: 01 01 00 00 00 00 00 01 00 00 00 00 ........ ....
+ """
+
+ pos = 0 # Position in data
+ line = 0 # Line of data
+
+ hex = "" # Hex display
+ ascii = "" # ASCII display
+
+ result = ""
+
+ while pos < len(data):
+
+ # Start with header
+
+ if pos % 16 == 0:
+ hex = "%08x: " % (line * 16)
+ ascii = ""
+
+ # Add character
+
+ hex = hex + "%02x " % (ord(data[pos]))
+
+ if ord(data[pos]) < 32 or ord(data[pos]) > 176:
+ ascii = ascii + '.'
+ else:
+ ascii = ascii + data[pos]
+
+ pos = pos + 1
+
+ # Add separator if half way
+
+ if pos % 16 == 8:
+ hex = hex + " "
+ ascii = ascii + " "
+
+ # End of line
+
+ if pos % 16 == 0:
+ result = result + "%s %s\n" % (hex, ascii)
+ line = line + 1
+
+ # Leftover bits
+
+ if pos % 16 != 0:
+
+ # Pad hex string
+
+ for i in range(0, (16 - (pos % 16))):
+ hex = hex + " "
+
+ # Half way separator
+
+ if (pos % 16) < 8:
+ hex = hex + " "
+
+ result = result + "%s %s\n" % (hex, ascii)
+
+ return result
+
+# For testing purposes, create a fixed dictionary to browse with
+
+if __name__ == "__main__":
+
+ dict = {"chicken": "ham", "spam": "fun", "subdict": {"a": "b", "c": "d"}}
+
+ db = GtkDictBrowser(dict)
+
+ db.build_ui("GtkDictBrowser")
+
+ # Override Python's handling of ctrl-c so we can break out of the
+ # gui from the command line.
+
+ import signal
+ signal.signal(signal.SIGINT, signal.SIG_DFL)
+
+ mainloop()
diff --git a/source/python/py_common.c b/source/python/py_common.c
new file mode 100644
index 00000000000..02d22bbdab5
--- /dev/null
+++ b/source/python/py_common.c
@@ -0,0 +1,259 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "python/py_common.h"
+
+/* Return a tuple of (error code, error string) from a WERROR */
+
+PyObject *py_werror_tuple(WERROR werror)
+{
+ return Py_BuildValue("[is]", W_ERROR_V(werror),
+ dos_errstr(werror));
+}
+
+/* Return a tuple of (error code, error string) from a WERROR */
+
+PyObject *py_ntstatus_tuple(NTSTATUS ntstatus)
+{
+ return Py_BuildValue("[is]", NT_STATUS_V(ntstatus),
+ nt_errstr(ntstatus));
+}
+
+/* Initialise samba client routines */
+
+static BOOL initialised;
+
+void py_samba_init(void)
+{
+ if (initialised)
+ return;
+
+ /* Load configuration file */
+
+ if (!lp_load(dyn_CONFIGFILE, True, False, False))
+ fprintf(stderr, "Can't load %s\n", dyn_CONFIGFILE);
+
+ /* Misc other stuff */
+
+ load_interfaces();
+ init_names();
+
+ initialised = True;
+}
+
+/* Debuglevel routines */
+
+PyObject *get_debuglevel(PyObject *self, PyObject *args)
+{
+ PyObject *debuglevel;
+
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+
+ debuglevel = PyInt_FromLong(DEBUGLEVEL);
+
+ return debuglevel;
+}
+
+PyObject *set_debuglevel(PyObject *self, PyObject *args)
+{
+ int debuglevel;
+
+ if (!PyArg_ParseTuple(args, "i", &debuglevel))
+ return NULL;
+
+ DEBUGLEVEL = debuglevel;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* Initialise logging */
+
+PyObject *py_setup_logging(PyObject *self, PyObject *args, PyObject *kw)
+{
+ BOOL interactive = False;
+ char *logfilename = NULL;
+ static char *kwlist[] = {"interactive", "logfilename", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "|is", kwlist, &interactive, &logfilename))
+ return NULL;
+
+ if (interactive && logfilename) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "can't be interactive and set log file name");
+ return NULL;
+ }
+
+ if (interactive)
+ setup_logging("spoolss", True);
+
+ if (logfilename) {
+ lp_set_logfile(logfilename);
+ setup_logging(logfilename, False);
+ reopen_logs();
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* Parse credentials from a python dictionary. The dictionary can
+ only have the keys "username", "domain" and "password". Return
+ True for valid credentials in which case the username, domain and
+ password are set to pointers to their values from the dicationary.
+ If returns False, the errstr is set to point at some mallocated
+ memory describing the error. */
+
+BOOL py_parse_creds(PyObject *creds, char **username, char **domain,
+ char **password, char **errstr)
+{
+ /* Initialise anonymous credentials */
+
+ *username = "";
+ *domain = "";
+ *password = "";
+
+ if (creds && PyDict_Size(creds) > 0) {
+ PyObject *username_obj, *password_obj, *domain_obj;
+ PyObject *key, *value;
+ int i;
+
+ /* Check for presence of required fields */
+
+ username_obj = PyDict_GetItemString(creds, "username");
+ domain_obj = PyDict_GetItemString(creds, "domain");
+ password_obj = PyDict_GetItemString(creds, "password");
+
+ if (!username_obj) {
+ *errstr = strdup("no username field in credential");
+ return False;
+ }
+
+ if (!domain_obj) {
+ *errstr = strdup("no domain field in credential");
+ return False;
+ }
+
+ if (!password_obj) {
+ *errstr = strdup("no password field in credential");
+ return False;
+ }
+
+ /* Check type of required fields */
+
+ if (!PyString_Check(username_obj)) {
+ *errstr = strdup("username field is not string type");
+ return False;
+ }
+
+ if (!PyString_Check(domain_obj)) {
+ *errstr = strdup("domain field is not string type");
+ return False;
+ }
+
+ if (!PyString_Check(password_obj)) {
+ *errstr = strdup("password field is not string type");
+ return False;
+ }
+
+ /* Look for any extra fields */
+
+ i = 0;
+
+ while (PyDict_Next(creds, &i, &key, &value)) {
+ if (strcmp(PyString_AsString(key), "domain") != 0 &&
+ strcmp(PyString_AsString(key), "username") != 0 &&
+ strcmp(PyString_AsString(key), "password") != 0) {
+ asprintf(errstr,
+ "creds contain extra field '%s'",
+ PyString_AsString(key));
+ return False;
+ }
+ }
+
+ /* Assign values */
+
+ *username = PyString_AsString(username_obj);
+ *domain = PyString_AsString(domain_obj);
+ *password = PyString_AsString(password_obj);
+ }
+
+ *errstr = NULL;
+
+ return True;
+}
+
+/* Return a cli_state to a RPC pipe on the given server. Use the
+ credentials passed if not NULL. If an error occurs errstr is set to a
+ string describing the error and NULL is returned. If set, errstr must
+ be freed by calling free(). */
+
+struct cli_state *open_pipe_creds(char *server, PyObject *creds,
+ int pipe_idx, char **errstr)
+{
+ char *username, *password, *domain;
+ struct cli_state *cli;
+ NTSTATUS result;
+
+ /* Extract credentials from the python dictionary */
+
+ if (!py_parse_creds(creds, &username, &domain, &password, errstr))
+ return NULL;
+
+ /* Now try to connect */
+
+ result = cli_full_connection(
+ &cli, NULL, server, NULL, 0, "IPC$", "IPC",
+ username, domain, password, 0, Undefined, NULL);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ *errstr = strdup("error connecting to IPC$ pipe");
+ return NULL;
+ }
+
+ if (!cli_nt_session_open(cli, pipe_idx)) {
+ cli_shutdown(cli);
+ asprintf(errstr, "error opening pipe index %d", pipe_idx);
+ return NULL;
+ }
+
+ *errstr = NULL;
+
+ return cli;
+}
+
+/* Return true if a dictionary contains a "level" key with an integer
+ value. Set the value if so. */
+
+BOOL get_level_value(PyObject *dict, uint32 *level)
+{
+ PyObject *obj;
+
+ if (!(obj = PyDict_GetItemString(dict, "level")) ||
+ !PyInt_Check(obj))
+ return False;
+
+ if (level)
+ *level = PyInt_AsLong(obj);
+
+ return True;
+}
diff --git a/source/python/py_common.h b/source/python/py_common.h
new file mode 100644
index 00000000000..2bbd148ff4b
--- /dev/null
+++ b/source/python/py_common.h
@@ -0,0 +1,67 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _PY_COMMON_H
+#define _PY_COMMON_H
+
+#include "includes.h"
+
+/* This symbol is used in both includes.h and Python.h which causes an
+ annoying compiler warning. */
+
+#ifdef HAVE_FSTAT
+#undef HAVE_FSTAT
+#endif
+
+#include "Python.h"
+
+/* Return a cli_state struct opened on the specified pipe. If credentials
+ are passed use them. */
+
+typedef struct cli_state *(cli_pipe_fn)(
+ struct cli_state *cli, char *system_name,
+ struct ntuser_creds *creds);
+
+/* The following definitions come from python/py_common.c */
+
+PyObject *py_werror_tuple(WERROR werror);
+PyObject *py_ntstatus_tuple(NTSTATUS ntstatus);
+void py_samba_init(void);
+PyObject *get_debuglevel(PyObject *self, PyObject *args);
+PyObject *set_debuglevel(PyObject *self, PyObject *args);
+PyObject *py_setup_logging(PyObject *self, PyObject *args, PyObject *kw);
+BOOL py_parse_creds(PyObject *creds, char **username, char **domain,
+ char **password, char **errstr);
+struct cli_state *open_pipe_creds(char *server, PyObject *creds,
+ int pipe_idx, char **errstr);
+BOOL get_level_value(PyObject *dict, uint32 *level);
+
+/* The following definitions come from python/py_ntsec.c */
+
+BOOL py_from_SID(PyObject **obj, DOM_SID *sid);
+BOOL py_to_SID(DOM_SID *sid, PyObject *obj);
+BOOL py_from_ACE(PyObject **dict, SEC_ACE *ace);
+BOOL py_to_ACE(SEC_ACE *ace, PyObject *dict);
+BOOL py_from_ACL(PyObject **dict, SEC_ACL *acl);
+BOOL py_to_ACL(SEC_ACL *acl, PyObject *dict, TALLOC_CTX *mem_ctx);
+BOOL py_from_SECDESC(PyObject **dict, SEC_DESC *sd);
+BOOL py_to_SECDESC(SEC_DESC **sd, PyObject *dict, TALLOC_CTX *mem_ctx);
+
+#endif /* _PY_COMMON_H */
diff --git a/source/python/py_conv.c b/source/python/py_conv.c
new file mode 100644
index 00000000000..d0a2d78aabd
--- /dev/null
+++ b/source/python/py_conv.c
@@ -0,0 +1,223 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "py_conv.h"
+
+/* Helper for rpcstr_pull() function */
+
+static void fstr_pull(fstring str, UNISTR *uni)
+{
+ rpcstr_pull(str, uni->buffer, sizeof(fstring), -1, STR_TERMINATE);
+}
+
+static void fstr_pull2(fstring str, UNISTR2 *uni)
+{
+ rpcstr_pull(str, uni->buffer, sizeof(fstring), -1, STR_TERMINATE);
+}
+
+/* Convert a structure to a Python dict */
+
+PyObject *from_struct(void *s, struct pyconv *conv)
+{
+ PyObject *obj, *item;
+ int i;
+
+ obj = PyDict_New();
+
+ for (i = 0; conv[i].name; i++) {
+ switch (conv[i].type) {
+ case PY_UNISTR: {
+ UNISTR *u = (UNISTR *)((char *)s + conv[i].offset);
+ fstring str = "";
+
+ if (u->buffer)
+ fstr_pull(str, u);
+
+ item = PyString_FromString(str);
+ PyDict_SetItemString(obj, conv[i].name, item);
+
+ break;
+ }
+ case PY_UNISTR2: {
+ UNISTR2 *u = (UNISTR2 *)((char *)s + conv[i].offset);
+ fstring str = "";
+
+ if (u->buffer)
+ fstr_pull2(str, u);
+
+ item = PyString_FromString(str);
+ PyDict_SetItemString(obj, conv[i].name, item);
+
+ break;
+ }
+ case PY_UINT32: {
+ uint32 *u = (uint32 *)((char *)s + conv[i].offset);
+
+ item = PyInt_FromLong(*u);
+ PyDict_SetItemString(obj, conv[i].name, item);
+
+ break;
+ }
+ case PY_UINT16: {
+ uint16 *u = (uint16 *)((char *)s + conv[i].offset);
+
+ item = PyInt_FromLong(*u);
+ PyDict_SetItemString(obj, conv[i].name, item);
+
+ break;
+ }
+ case PY_STRING: {
+ char *str = (char *)s + conv[i].offset;
+
+ item = PyString_FromString(str);
+ PyDict_SetItemString(obj, conv[i].name, item);
+
+ break;
+ }
+ case PY_UID: {
+ uid_t *uid = (uid_t *)((char *)s + conv[i].offset);
+
+ item = PyInt_FromLong(*uid);
+ PyDict_SetItemString(obj, conv[i].name, item);
+
+ break;
+ }
+ case PY_GID: {
+ gid_t *gid = (gid_t *)((char *)s + conv[i].offset);
+
+ item = PyInt_FromLong(*gid);
+ PyDict_SetItemString(obj, conv[i].name, item);
+
+ break;
+ }
+ default:
+
+ break;
+ }
+ }
+
+ return obj;
+}
+
+/* Convert a Python dict to a structure */
+
+BOOL to_struct(void *s, PyObject *dict, struct pyconv *conv)
+{
+ PyObject *visited, *key, *value;
+ BOOL result = False;
+ int i;
+
+ visited = PyDict_New();
+
+ for (i = 0; conv[i].name; i++) {
+ PyObject *obj;
+
+ obj = PyDict_GetItemString(dict, conv[i].name);
+
+ if (!obj)
+ goto done;
+
+ switch (conv[i].type) {
+ case PY_UNISTR: {
+ UNISTR *u = (UNISTR *)((char *)s + conv[i].offset);
+ char *str = "";
+
+ if (!PyString_Check(obj))
+ goto done;
+
+ str = PyString_AsString(obj);
+ init_unistr(u, str);
+
+ break;
+ }
+ case PY_UINT32: {
+ uint32 *u = (uint32 *)((char *)s + conv[i].offset);
+
+ if (!PyInt_Check(obj))
+ goto done;
+
+ *u = PyInt_AsLong(obj);
+
+ break;
+ }
+ case PY_UINT16: {
+ uint16 *u = (uint16 *)((char *)s + conv[i].offset);
+
+ if (!PyInt_Check(obj))
+ goto done;
+
+ *u = PyInt_AsLong(obj);
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Mark as visited */
+
+ PyDict_SetItemString(visited, conv[i].name,
+ PyInt_FromLong(1));
+ }
+
+ /* Iterate over each item in the input dictionary and see if it was
+ visited. If it wasn't then the user has added some extra crap
+ to the dictionary. */
+
+ i = 0;
+
+ while (PyDict_Next(dict, &i, &key, &value)) {
+ if (!PyDict_GetItem(visited, key))
+ goto done;
+ }
+
+ result = True;
+
+done:
+ /* We must decrement the reference count here or the visited
+ dictionary will not be freed. */
+
+ Py_DECREF(visited);
+
+ return result;
+}
+
+/* Convert a NULL terminated list of NULL terminated unicode strings
+ to a list of (char *) strings */
+
+PyObject *from_unistr_list(uint16 *dependentfiles)
+{
+ PyObject *list;
+ int offset = 0;
+
+ list = PyList_New(0);
+
+ while (*(dependentfiles + offset) != 0) {
+ fstring name;
+ int len;
+
+ len = rpcstr_pull(name, dependentfiles + offset,
+ sizeof(fstring), -1, STR_TERMINATE);
+
+ offset += len / 2;
+ PyList_Append(list, PyString_FromString(name));
+ }
+
+ return list;
+}
diff --git a/source/python/py_conv.h b/source/python/py_conv.h
new file mode 100644
index 00000000000..798661c3a0e
--- /dev/null
+++ b/source/python/py_conv.h
@@ -0,0 +1,44 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _PY_CONV_H
+#define _PY_CONV_H
+
+#include "python/py_common.h"
+
+enum pyconv_types { PY_UNISTR, PY_UNISTR2, PY_UINT32, PY_UINT16, PY_STRING,
+ PY_UID, PY_GID };
+
+struct pyconv {
+ char *name; /* Name of member */
+ enum pyconv_types type; /* Type */
+ size_t offset; /* Offset into structure */
+};
+
+PyObject *from_struct(void *s, struct pyconv *conv);
+BOOL to_struct(void *s, PyObject *dict, struct pyconv *conv);
+PyObject *from_unistr_list(uint16 *dependentfiles);
+
+/* Another version of offsetof (-: */
+
+#undef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+#endif /* _PY_CONV_H */
diff --git a/source/python/py_lsa.c b/source/python/py_lsa.c
new file mode 100644
index 00000000000..07191be8682
--- /dev/null
+++ b/source/python/py_lsa.c
@@ -0,0 +1,484 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "python/py_lsa.h"
+
+PyObject *new_lsa_policy_hnd_object(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol)
+{
+ lsa_policy_hnd_object *o;
+
+ o = PyObject_New(lsa_policy_hnd_object, &lsa_policy_hnd_type);
+
+ o->cli = cli;
+ o->mem_ctx = mem_ctx;
+ memcpy(&o->pol, pol, sizeof(POLICY_HND));
+
+ return (PyObject*)o;
+}
+
+/*
+ * Exceptions raised by this module
+ */
+
+PyObject *lsa_error; /* This indicates a non-RPC related error
+ such as name lookup failure */
+
+PyObject *lsa_ntstatus; /* This exception is raised when a RPC call
+ returns a status code other than
+ NT_STATUS_OK */
+
+/*
+ * Open/close lsa handles
+ */
+
+static PyObject *lsa_open_policy(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ static char *kwlist[] = { "servername", "creds", "access", NULL };
+ char *server, *errstr;
+ PyObject *creds = NULL, *result = NULL;
+ uint32 desired_access = MAXIMUM_ALLOWED_ACCESS;
+ struct cli_state *cli = NULL;
+ NTSTATUS ntstatus;
+ TALLOC_CTX *mem_ctx = NULL;
+ POLICY_HND hnd;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "s|Oi", kwlist, &server, &creds, &desired_access))
+ return NULL;
+
+ if (creds && creds != Py_None && !PyDict_Check(creds)) {
+ PyErr_SetString(PyExc_TypeError,
+ "credentials must be dictionary or None");
+ return NULL;
+ }
+
+ if (server[0] != '\\' || server[1] != '\\') {
+ PyErr_SetString(PyExc_ValueError, "UNC name required");
+ return NULL;
+ }
+
+ server += 2;
+
+ if (!(cli = open_pipe_creds(server, creds, PI_LSARPC, &errstr))) {
+ PyErr_SetString(lsa_error, errstr);
+ free(errstr);
+ return NULL;
+ }
+
+ if (!(mem_ctx = talloc_init("lsa_open_policy"))) {
+ PyErr_SetString(lsa_error, "unable to init talloc context\n");
+ goto done;
+ }
+
+ ntstatus = cli_lsa_open_policy(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED, &hnd);
+
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ PyErr_SetObject(lsa_ntstatus, py_ntstatus_tuple(ntstatus));
+ goto done;
+ }
+
+ result = new_lsa_policy_hnd_object(cli, mem_ctx, &hnd);
+
+done:
+ if (!result) {
+ if (cli)
+ cli_shutdown(cli);
+
+ talloc_destroy(mem_ctx);
+ }
+
+ return result;
+}
+
+static PyObject *lsa_close(PyObject *self, PyObject *args, PyObject *kw)
+{
+ PyObject *po;
+ lsa_policy_hnd_object *hnd;
+ NTSTATUS result;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTuple(args, "O!", &lsa_policy_hnd_type, &po))
+ return NULL;
+
+ hnd = (lsa_policy_hnd_object *)po;
+
+ /* Call rpc function */
+
+ result = cli_lsa_close(hnd->cli, hnd->mem_ctx, &hnd->pol);
+
+ /* Cleanup samba stuff */
+
+ cli_shutdown(hnd->cli);
+ talloc_destroy(hnd->mem_ctx);
+
+ /* Return value */
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *lsa_lookup_names(PyObject *self, PyObject *args)
+{
+ PyObject *py_names, *result = NULL;
+ NTSTATUS ntstatus;
+ lsa_policy_hnd_object *hnd = (lsa_policy_hnd_object *)self;
+ int num_names, i;
+ const char **names;
+ DOM_SID *sids;
+ TALLOC_CTX *mem_ctx = NULL;
+ uint32 *name_types;
+
+ if (!PyArg_ParseTuple(args, "O", &py_names))
+ return NULL;
+
+ if (!PyList_Check(py_names) && !PyString_Check(py_names)) {
+ PyErr_SetString(PyExc_TypeError, "must be list or string");
+ return NULL;
+ }
+
+ if (!(mem_ctx = talloc_init("lsa_lookup_names"))) {
+ PyErr_SetString(lsa_error, "unable to init talloc context\n");
+ goto done;
+ }
+
+ if (PyList_Check(py_names)) {
+
+ /* Convert list to char ** array */
+
+ num_names = PyList_Size(py_names);
+ names = (const char **)talloc(mem_ctx, num_names * sizeof(char *));
+
+ for (i = 0; i < num_names; i++) {
+ PyObject *obj = PyList_GetItem(py_names, i);
+
+ names[i] = talloc_strdup(mem_ctx, PyString_AsString(obj));
+ }
+
+ } else {
+
+ /* Just a single element */
+
+ num_names = 1;
+ names = (const char **)talloc(mem_ctx, sizeof(char *));
+
+ names[0] = PyString_AsString(py_names);
+ }
+
+ ntstatus = cli_lsa_lookup_names(hnd->cli, mem_ctx, &hnd->pol,
+ num_names, names, &sids, &name_types);
+
+ if (!NT_STATUS_IS_OK(ntstatus) && NT_STATUS_V(ntstatus) != 0x107) {
+ PyErr_SetObject(lsa_ntstatus, py_ntstatus_tuple(ntstatus));
+ goto done;
+ }
+
+ result = PyList_New(num_names);
+
+ for (i = 0; i < num_names; i++) {
+ PyObject *sid_obj, *obj;
+
+ py_from_SID(&sid_obj, &sids[i]);
+
+ obj = Py_BuildValue("(Ni)", sid_obj, name_types[i]);
+
+ PyList_SetItem(result, i, obj);
+ }
+
+ done:
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+static PyObject *lsa_lookup_sids(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ PyObject *py_sids, *result = NULL;
+ NTSTATUS ntstatus;
+ int num_sids, i;
+ char **domains, **names;
+ uint32 *types;
+ lsa_policy_hnd_object *hnd = (lsa_policy_hnd_object *)self;
+ TALLOC_CTX *mem_ctx = NULL;
+ DOM_SID *sids;
+
+ if (!PyArg_ParseTuple(args, "O", &py_sids))
+ return NULL;
+
+ if (!PyList_Check(py_sids) && !PyString_Check(py_sids)) {
+ PyErr_SetString(PyExc_TypeError, "must be list or string");
+ return NULL;
+ }
+
+ if (!(mem_ctx = talloc_init("lsa_lookup_sids"))) {
+ PyErr_SetString(lsa_error, "unable to init talloc context\n");
+ goto done;
+ }
+
+ if (PyList_Check(py_sids)) {
+
+ /* Convert dictionary to char ** array */
+
+ num_sids = PyList_Size(py_sids);
+ sids = (DOM_SID *)talloc(mem_ctx, num_sids * sizeof(DOM_SID));
+
+ memset(sids, 0, num_sids * sizeof(DOM_SID));
+
+ for (i = 0; i < num_sids; i++) {
+ PyObject *obj = PyList_GetItem(py_sids, i);
+
+ if (!string_to_sid(&sids[i], PyString_AsString(obj))) {
+ PyErr_SetString(PyExc_ValueError, "string_to_sid failed");
+ goto done;
+ }
+ }
+
+ } else {
+
+ /* Just a single element */
+
+ num_sids = 1;
+ sids = (DOM_SID *)talloc(mem_ctx, sizeof(DOM_SID));
+
+ if (!string_to_sid(&sids[0], PyString_AsString(py_sids))) {
+ PyErr_SetString(PyExc_ValueError, "string_to_sid failed");
+ goto done;
+ }
+ }
+
+ ntstatus = cli_lsa_lookup_sids(hnd->cli, mem_ctx, &hnd->pol,
+ num_sids, sids, &domains, &names,
+ &types);
+
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ PyErr_SetObject(lsa_ntstatus, py_ntstatus_tuple(ntstatus));
+ goto done;
+ }
+
+ result = PyList_New(num_sids);
+
+ for (i = 0; i < num_sids; i++) {
+ PyObject *obj;
+
+ obj = Py_BuildValue("{sssssi}", "username", names[i],
+ "domain", domains[i], "name_type",
+ types[i]);
+
+ PyList_SetItem(result, i, obj);
+ }
+
+ done:
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+static PyObject *lsa_enum_trust_dom(PyObject *self, PyObject *args)
+{
+ lsa_policy_hnd_object *hnd = (lsa_policy_hnd_object *)self;
+ NTSTATUS ntstatus;
+ uint32 enum_ctx = 0, num_domains, i;
+ char **domain_names;
+ DOM_SID *domain_sids;
+ PyObject *result;
+
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+
+ ntstatus = cli_lsa_enum_trust_dom(
+ hnd->cli, hnd->mem_ctx, &hnd->pol, &enum_ctx,
+ &num_domains, &domain_names, &domain_sids);
+
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ PyErr_SetObject(lsa_ntstatus, py_ntstatus_tuple(ntstatus));
+ return NULL;
+ }
+
+ result = PyList_New(num_domains);
+
+ for (i = 0; i < num_domains; i++) {
+ fstring sid_str;
+
+ sid_to_string(sid_str, &domain_sids[i]);
+ PyList_SetItem(
+ result, i,
+ Py_BuildValue("(ss)", domain_names[i], sid_str));
+ }
+
+ return result;
+}
+
+/*
+ * Method dispatch tables
+ */
+
+static PyMethodDef lsa_hnd_methods[] = {
+
+ /* SIDs<->names */
+
+ { "lookup_sids", (PyCFunction)lsa_lookup_sids,
+ METH_VARARGS | METH_KEYWORDS,
+ "Convert sids to names." },
+
+ { "lookup_names", (PyCFunction)lsa_lookup_names,
+ METH_VARARGS | METH_KEYWORDS,
+ "Convert names to sids." },
+
+ /* Trusted domains */
+
+ { "enum_trusted_domains", (PyCFunction)lsa_enum_trust_dom,
+ METH_VARARGS,
+ "Enumerate trusted domains." },
+
+ { NULL }
+};
+
+static void py_lsa_policy_hnd_dealloc(PyObject* self)
+{
+ PyObject_Del(self);
+}
+
+static PyObject *py_lsa_policy_hnd_getattr(PyObject *self, char *attrname)
+{
+ return Py_FindMethod(lsa_hnd_methods, self, attrname);
+}
+
+PyTypeObject lsa_policy_hnd_type = {
+ PyObject_HEAD_INIT(NULL)
+ 0,
+ "LSA Policy Handle",
+ sizeof(lsa_policy_hnd_object),
+ 0,
+ py_lsa_policy_hnd_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ py_lsa_policy_hnd_getattr, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+};
+
+static PyMethodDef lsa_methods[] = {
+
+ /* Open/close lsa handles */
+
+ { "open_policy", (PyCFunction)lsa_open_policy,
+ METH_VARARGS | METH_KEYWORDS,
+ "Open a policy handle" },
+
+ { "close", (PyCFunction)lsa_close,
+ METH_VARARGS,
+ "Close a policy handle" },
+
+ /* Other stuff - this should really go into a samba config module
+ but for the moment let's leave it here. */
+
+ { "setup_logging", (PyCFunction)py_setup_logging,
+ METH_VARARGS | METH_KEYWORDS,
+ "Set up debug logging.\n"
+"\n"
+"Initialises Samba's debug logging system. One argument is expected which\n"
+"is a boolean specifying whether debugging is interactive and sent to stdout\n"
+"or logged to a file.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> lsa.setup_logging(interactive = 1)" },
+
+ { "get_debuglevel", (PyCFunction)get_debuglevel,
+ METH_VARARGS,
+ "Set the current debug level.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> lsa.get_debuglevel()\n"
+"0" },
+
+ { "set_debuglevel", (PyCFunction)set_debuglevel,
+ METH_VARARGS,
+ "Get the current debug level.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> lsa.set_debuglevel(10)" },
+
+ { NULL }
+};
+
+static struct const_vals {
+ char *name;
+ uint32 value;
+} module_const_vals[] = {
+ { NULL }
+};
+
+static void const_init(PyObject *dict)
+{
+ struct const_vals *tmp;
+ PyObject *obj;
+
+ for (tmp = module_const_vals; tmp->name; tmp++) {
+ obj = PyInt_FromLong(tmp->value);
+ PyDict_SetItemString(dict, tmp->name, obj);
+ Py_DECREF(obj);
+ }
+}
+
+/*
+ * Module initialisation
+ */
+
+void initlsa(void)
+{
+ PyObject *module, *dict;
+
+ /* Initialise module */
+
+ module = Py_InitModule("lsa", lsa_methods);
+ dict = PyModule_GetDict(module);
+
+ lsa_error = PyErr_NewException("lsa.error", NULL, NULL);
+ PyDict_SetItemString(dict, "error", lsa_error);
+
+ lsa_ntstatus = PyErr_NewException("lsa.ntstatus", NULL, NULL);
+ PyDict_SetItemString(dict, "ntstatus", lsa_ntstatus);
+
+ /* Initialise policy handle object */
+
+ lsa_policy_hnd_type.ob_type = &PyType_Type;
+
+ /* Initialise constants */
+
+ const_init(dict);
+
+ /* Do samba initialisation */
+
+ py_samba_init();
+
+ setup_logging("lsa", True);
+ DEBUGLEVEL = 10;
+}
diff --git a/source/python/py_lsa.h b/source/python/py_lsa.h
new file mode 100644
index 00000000000..99f3de50b1f
--- /dev/null
+++ b/source/python/py_lsa.h
@@ -0,0 +1,41 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _PY_LSA_H
+#define _PY_LSA_H
+
+#include "python/py_common.h"
+
+/* LSA policy handle object */
+
+typedef struct {
+ PyObject_HEAD
+ struct cli_state *cli;
+ TALLOC_CTX *mem_ctx;
+ POLICY_HND pol;
+} lsa_policy_hnd_object;
+
+/* Exceptions raised by this module */
+
+extern PyTypeObject lsa_policy_hnd_type;
+
+extern PyObject *lsa_error;
+
+#endif /* _PY_LSA_H */
diff --git a/source/python/py_ntsec.c b/source/python/py_ntsec.c
new file mode 100644
index 00000000000..5ce5e8fc1be
--- /dev/null
+++ b/source/python/py_ntsec.c
@@ -0,0 +1,288 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "python/py_common.h"
+
+/* Convert a SID to a Python dict */
+
+BOOL py_from_SID(PyObject **obj, DOM_SID *sid)
+{
+ fstring sidstr;
+
+ if (!sid) {
+ Py_INCREF(Py_None);
+ *obj = Py_None;
+ return True;
+ }
+
+ if (!sid_to_string(sidstr, sid))
+ return False;
+
+ *obj = PyString_FromString(sidstr);
+
+ return True;
+}
+
+BOOL py_to_SID(DOM_SID *sid, PyObject *obj)
+{
+ if (!PyString_Check(obj))
+ return False;
+
+ return string_to_sid(sid, PyString_AsString(obj));
+}
+
+BOOL py_from_ACE(PyObject **dict, SEC_ACE *ace)
+{
+ PyObject *obj;
+
+ if (!ace) {
+ Py_INCREF(Py_None);
+ *dict = Py_None;
+ return True;
+ }
+
+ *dict = Py_BuildValue("{sisisi}", "type", ace->type,
+ "flags", ace->flags,
+ "mask", ace->info.mask);
+
+ if (py_from_SID(&obj, &ace->trustee)) {
+ PyDict_SetItemString(*dict, "trustee", obj);
+ Py_DECREF(obj);
+ }
+
+ return True;
+}
+
+BOOL py_to_ACE(SEC_ACE *ace, PyObject *dict)
+{
+ PyObject *obj;
+ uint8 ace_type, ace_flags;
+ DOM_SID trustee;
+ SEC_ACCESS sec_access;
+
+ if (!PyDict_Check(dict))
+ return False;
+
+ if (!(obj = PyDict_GetItemString(dict, "type")) ||
+ !PyInt_Check(obj))
+ return False;
+
+ ace_type = PyInt_AsLong(obj);
+
+ if (!(obj = PyDict_GetItemString(dict, "flags")) ||
+ !PyInt_Check(obj))
+ return False;
+
+ ace_flags = PyInt_AsLong(obj);
+
+ if (!(obj = PyDict_GetItemString(dict, "trustee")) ||
+ !PyString_Check(obj))
+ return False;
+
+ if (!py_to_SID(&trustee, obj))
+ return False;
+
+ if (!(obj = PyDict_GetItemString(dict, "mask")) ||
+ !PyInt_Check(obj))
+ return False;
+
+ sec_access.mask = PyInt_AsLong(obj);
+
+ init_sec_ace(ace, &trustee, ace_type, sec_access, ace_flags);
+
+ /* Fill in size field */
+
+ ace->size = SEC_ACE_HEADER_SIZE + sid_size(&trustee);
+
+ return True;
+}
+
+BOOL py_from_ACL(PyObject **dict, SEC_ACL *acl)
+{
+ PyObject *ace_list;
+ int i;
+
+ if (!acl) {
+ Py_INCREF(Py_None);
+ *dict = Py_None;
+ return True;
+ }
+
+ ace_list = PyList_New(acl->num_aces);
+
+ for (i = 0; i < acl->num_aces; i++) {
+ PyObject *obj;
+
+ if (py_from_ACE(&obj, &acl->ace[i]))
+ PyList_SetItem(ace_list, i, obj);
+ }
+
+ *dict = Py_BuildValue("{sisN}", "revision", acl->revision,
+ "ace_list", ace_list);
+
+ return True;
+}
+
+BOOL py_to_ACL(SEC_ACL *acl, PyObject *dict, TALLOC_CTX *mem_ctx)
+{
+ PyObject *obj;
+ uint32 i;
+
+ if (!(obj = PyDict_GetItemString(dict, "revision")) ||
+ !PyInt_Check(obj))
+ return False;
+
+ acl->revision = PyInt_AsLong(obj);
+
+ if (!(obj = PyDict_GetItemString(dict, "ace_list")) ||
+ !PyList_Check(obj))
+ return False;
+
+ acl->num_aces = PyList_Size(obj);
+
+ acl->ace = talloc(mem_ctx, acl->num_aces * sizeof(SEC_ACE));
+ acl->size = SEC_ACL_HEADER_SIZE;
+
+ for (i = 0; i < acl->num_aces; i++) {
+ PyObject *py_ace = PyList_GetItem(obj, i);
+
+ if (!py_to_ACE(&acl->ace[i], py_ace))
+ return False;
+
+ acl->size += acl->ace[i].size;
+ }
+
+ return True;
+}
+
+BOOL py_from_SECDESC(PyObject **dict, SEC_DESC *sd)
+{
+ PyObject *obj;
+
+ *dict = PyDict_New();
+
+ obj = PyInt_FromLong(sd->revision);
+ PyDict_SetItemString(*dict, "revision", obj);
+ Py_DECREF(obj);
+
+ if (py_from_SID(&obj, sd->owner_sid)) {
+ PyDict_SetItemString(*dict, "owner_sid", obj);
+ Py_DECREF(obj);
+ }
+
+ if (py_from_SID(&obj, sd->grp_sid)) {
+ PyDict_SetItemString(*dict, "group_sid", obj);
+ Py_DECREF(obj);
+ }
+
+ if (py_from_ACL(&obj, sd->dacl)) {
+ PyDict_SetItemString(*dict, "dacl", obj);
+ Py_DECREF(obj);
+ }
+
+ if (py_from_ACL(&obj, sd->sacl)) {
+ PyDict_SetItemString(*dict, "sacl", obj);
+ Py_DECREF(obj);
+ }
+
+ return True;
+}
+
+BOOL py_to_SECDESC(SEC_DESC **sd, PyObject *dict, TALLOC_CTX *mem_ctx)
+{
+ PyObject *obj;
+ uint16 revision;
+ DOM_SID owner_sid, group_sid;
+ SEC_ACL sacl, dacl;
+ BOOL got_dacl = False, got_sacl = False;
+ BOOL got_owner_sid = False, got_group_sid = False;
+
+ ZERO_STRUCT(dacl); ZERO_STRUCT(sacl);
+ ZERO_STRUCT(owner_sid); ZERO_STRUCT(group_sid);
+
+ if (!(obj = PyDict_GetItemString(dict, "revision")))
+ return False;
+
+ revision = PyInt_AsLong(obj);
+
+ if ((obj = PyDict_GetItemString(dict, "owner_sid"))) {
+
+ if (obj != Py_None) {
+
+ if (!py_to_SID(&owner_sid, obj))
+ return False;
+
+ got_owner_sid = True;
+ }
+ }
+
+ if ((obj = PyDict_GetItemString(dict, "group_sid"))) {
+
+ if (obj != Py_None) {
+
+ if (!py_to_SID(&group_sid, obj))
+ return False;
+
+ got_group_sid = True;
+ }
+ }
+
+ if ((obj = PyDict_GetItemString(dict, "dacl"))) {
+
+ if (obj != Py_None) {
+
+ if (!py_to_ACL(&dacl, obj, mem_ctx))
+ return False;
+
+ got_dacl = True;
+ }
+ }
+
+ if ((obj = PyDict_GetItemString(dict, "sacl"))) {
+
+ if (obj != Py_None) {
+
+ if (!py_to_ACL(&sacl, obj, mem_ctx))
+ return False;
+
+ got_sacl = True;
+ }
+ }
+
+#if 0 /* For new secdesc code */
+ *sd = make_sec_desc(mem_ctx, revision,
+ got_owner_sid ? &owner_sid : NULL,
+ got_group_sid ? &group_sid : NULL,
+ got_sacl ? &sacl : NULL,
+ got_dacl ? &dacl : NULL);
+#else
+ {
+ size_t sd_size;
+
+ *sd = make_sec_desc(mem_ctx, revision, SEC_DESC_SELF_RELATIVE,
+ got_owner_sid ? &owner_sid : NULL,
+ got_group_sid ? &group_sid : NULL,
+ got_sacl ? &sacl : NULL,
+ got_dacl ? &dacl : NULL, &sd_size);
+ }
+#endif
+
+ return True;
+}
diff --git a/source/python/py_samr.c b/source/python/py_samr.c
new file mode 100644
index 00000000000..57acd74bedb
--- /dev/null
+++ b/source/python/py_samr.c
@@ -0,0 +1,676 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "python/py_samr.h"
+
+/*
+ * Exceptions raised by this module
+ */
+
+PyObject *samr_error; /* This indicates a non-RPC related error
+ such as name lookup failure */
+
+PyObject *samr_ntstatus; /* This exception is raised when a RPC call
+ returns a status code other than
+ NT_STATUS_OK */
+
+/* SAMR group handle object */
+
+static void py_samr_group_hnd_dealloc(PyObject* self)
+{
+ PyObject_Del(self);
+}
+
+static PyMethodDef samr_group_methods[] = {
+ { NULL }
+};
+
+static PyObject *py_samr_group_hnd_getattr(PyObject *self, char *attrname)
+{
+ return Py_FindMethod(samr_group_methods, self, attrname);
+}
+
+PyTypeObject samr_group_hnd_type = {
+ PyObject_HEAD_INIT(NULL)
+ 0,
+ "SAMR Group Handle",
+ sizeof(samr_group_hnd_object),
+ 0,
+ py_samr_group_hnd_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ py_samr_group_hnd_getattr, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+};
+
+PyObject *new_samr_group_hnd_object(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol)
+{
+ samr_group_hnd_object *o;
+
+ o = PyObject_New(samr_group_hnd_object, &samr_group_hnd_type);
+
+ o->cli = cli;
+ o->mem_ctx = mem_ctx;
+ memcpy(&o->group_pol, pol, sizeof(POLICY_HND));
+
+ return (PyObject*)o;
+}
+
+/* Alias handle object */
+
+static void py_samr_alias_hnd_dealloc(PyObject* self)
+{
+ PyObject_Del(self);
+}
+
+static PyMethodDef samr_alias_methods[] = {
+ { NULL }
+};
+
+static PyObject *py_samr_alias_hnd_getattr(PyObject *self, char *attrname)
+{
+ return Py_FindMethod(samr_alias_methods, self, attrname);
+}
+
+PyTypeObject samr_alias_hnd_type = {
+ PyObject_HEAD_INIT(NULL)
+ 0,
+ "SAMR Alias Handle",
+ sizeof(samr_alias_hnd_object),
+ 0,
+ py_samr_alias_hnd_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ py_samr_alias_hnd_getattr, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+};
+
+PyObject *new_samr_alias_hnd_object(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol)
+{
+ samr_alias_hnd_object *o;
+
+ o = PyObject_New(samr_alias_hnd_object, &samr_alias_hnd_type);
+
+ o->cli = cli;
+ o->mem_ctx = mem_ctx;
+ memcpy(&o->alias_pol, pol, sizeof(POLICY_HND));
+
+ return (PyObject*)o;
+}
+
+/* SAMR user handle object */
+
+static void py_samr_user_hnd_dealloc(PyObject* self)
+{
+ PyObject_Del(self);
+}
+
+static PyObject *samr_set_user_info2(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ samr_user_hnd_object *user_hnd = (samr_user_hnd_object *)self;
+ static char *kwlist[] = { "dict", NULL };
+ PyObject *info, *result = NULL;
+ SAM_USERINFO_CTR ctr;
+ TALLOC_CTX *mem_ctx;
+ uchar sess_key[16];
+ NTSTATUS ntstatus;
+ int level;
+ union {
+ SAM_USER_INFO_10 id10;
+ SAM_USER_INFO_21 id21;
+ } pinfo;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "O!", kwlist, &PyDict_Type, &info))
+ return NULL;
+
+ if (!get_level_value(info, &level)) {
+ PyErr_SetString(samr_error, "invalid info level");
+ return NULL;
+ }
+
+ ZERO_STRUCT(ctr);
+
+ ctr.switch_value = level;
+
+ switch(level) {
+ case 0x10:
+ ctr.info.id10 = &pinfo.id10;
+
+ if (!py_to_SAM_USER_INFO_10(ctr.info.id10, info)) {
+ PyErr_SetString(
+ samr_error, "error converting user info");
+ goto done;
+ }
+
+ break;
+ case 21:
+ ctr.info.id21 = &pinfo.id21;
+
+ if (!py_to_SAM_USER_INFO_21(ctr.info.id21, info)) {
+ PyErr_SetString(
+ samr_error, "error converting user info");
+ goto done;
+ }
+
+ break;
+ default:
+ PyErr_SetString(samr_error, "unsupported info level");
+ goto done;
+ }
+
+ /* Call RPC function */
+
+ if (!(mem_ctx = talloc_init("samr_set_user_info2"))) {
+ PyErr_SetString(
+ samr_error, "unable to init talloc context\n");
+ goto done;
+ }
+
+ ntstatus = cli_samr_set_userinfo2(
+ user_hnd->cli, mem_ctx, &user_hnd->user_pol, level,
+ sess_key, &ctr);
+
+ talloc_destroy(mem_ctx);
+
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ PyErr_SetObject(samr_ntstatus, py_ntstatus_tuple(ntstatus));
+ goto done;
+ }
+
+ Py_INCREF(Py_None);
+ result = Py_None;
+
+done:
+ return result;
+}
+
+static PyObject *samr_delete_dom_user(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ samr_user_hnd_object *user_hnd = (samr_user_hnd_object *)self;
+ static char *kwlist[] = { NULL };
+ NTSTATUS ntstatus;
+ TALLOC_CTX *mem_ctx;
+ PyObject *result = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "", kwlist))
+ return NULL;
+
+ if (!(mem_ctx = talloc_init("samr_delete_dom_user"))) {
+ PyErr_SetString(samr_error, "unable to init talloc context");
+ return NULL;
+ }
+
+ ntstatus = cli_samr_delete_dom_user(
+ user_hnd->cli, mem_ctx, &user_hnd->user_pol);
+
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ PyErr_SetObject(samr_ntstatus, py_ntstatus_tuple(ntstatus));
+ goto done;
+ }
+
+ Py_INCREF(Py_None);
+ result = Py_None;
+
+done:
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+static PyMethodDef samr_user_methods[] = {
+ { "delete_domain_user", (PyCFunction)samr_delete_dom_user,
+ METH_VARARGS | METH_KEYWORDS,
+ "Delete domain user." },
+ { "set_user_info2", (PyCFunction)samr_set_user_info2,
+ METH_VARARGS | METH_KEYWORDS,
+ "Set user info 2" },
+ { NULL }
+};
+
+static PyObject *py_samr_user_hnd_getattr(PyObject *self, char *attrname)
+{
+ return Py_FindMethod(samr_user_methods, self, attrname);
+}
+
+PyTypeObject samr_user_hnd_type = {
+ PyObject_HEAD_INIT(NULL)
+ 0,
+ "SAMR User Handle",
+ sizeof(samr_user_hnd_object),
+ 0,
+ py_samr_user_hnd_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ py_samr_user_hnd_getattr, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+};
+
+PyObject *new_samr_user_hnd_object(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol)
+{
+ samr_user_hnd_object *o;
+
+ o = PyObject_New(samr_user_hnd_object, &samr_user_hnd_type);
+
+ o->cli = cli;
+ o->mem_ctx = mem_ctx;
+ memcpy(&o->user_pol, pol, sizeof(POLICY_HND));
+
+ return (PyObject*)o;
+}
+
+/* SAMR connect handle object */
+
+static void py_samr_connect_hnd_dealloc(PyObject* self)
+{
+ PyObject_Del(self);
+}
+
+PyObject *new_samr_domain_hnd_object(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol)
+{
+ samr_domain_hnd_object *o;
+
+ o = PyObject_New(samr_domain_hnd_object, &samr_domain_hnd_type);
+
+ o->cli = cli;
+ o->mem_ctx = mem_ctx;
+ memcpy(&o->domain_pol, pol, sizeof(POLICY_HND));
+
+ return (PyObject*)o;
+}
+
+static PyObject *samr_open_domain(PyObject *self, PyObject *args, PyObject *kw)
+{
+ samr_connect_hnd_object *connect_hnd = (samr_connect_hnd_object *)self;
+ static char *kwlist[] = { "sid", "access", NULL };
+ uint32 desired_access = MAXIMUM_ALLOWED_ACCESS;
+ char *sid_str;
+ DOM_SID sid;
+ TALLOC_CTX *mem_ctx = NULL;
+ POLICY_HND domain_pol;
+ NTSTATUS ntstatus;
+ PyObject *result = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "s|i", kwlist, &sid_str, &desired_access))
+ return NULL;
+
+ if (!string_to_sid(&sid, sid_str)) {
+ PyErr_SetString(PyExc_TypeError, "string is not a sid");
+ return NULL;
+ }
+
+ if (!(mem_ctx = talloc_init("samr_open_domain"))) {
+ PyErr_SetString(samr_error, "unable to init talloc context");
+ return NULL;
+ }
+
+ ntstatus = cli_samr_open_domain(
+ connect_hnd->cli, mem_ctx, &connect_hnd->connect_pol,
+ desired_access, &sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ PyErr_SetObject(samr_ntstatus, py_ntstatus_tuple(ntstatus));
+ goto done;
+ }
+
+ result = new_samr_domain_hnd_object(
+ connect_hnd->cli, mem_ctx, &domain_pol);
+
+done:
+ if (!result) {
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+ }
+
+ return result;
+}
+
+static PyMethodDef samr_connect_methods[] = {
+ { "open_domain", (PyCFunction)samr_open_domain,
+ METH_VARARGS | METH_KEYWORDS,
+ "Open a handle on a domain" },
+
+ { NULL }
+};
+
+static PyObject *py_samr_connect_hnd_getattr(PyObject *self, char *attrname)
+{
+ return Py_FindMethod(samr_connect_methods, self, attrname);
+}
+
+PyTypeObject samr_connect_hnd_type = {
+ PyObject_HEAD_INIT(NULL)
+ 0,
+ "SAMR Connect Handle",
+ sizeof(samr_connect_hnd_object),
+ 0,
+ py_samr_connect_hnd_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ py_samr_connect_hnd_getattr, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+};
+
+PyObject *new_samr_connect_hnd_object(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol)
+{
+ samr_connect_hnd_object *o;
+
+ o = PyObject_New(samr_connect_hnd_object, &samr_connect_hnd_type);
+
+ o->cli = cli;
+ o->mem_ctx = mem_ctx;
+ memcpy(&o->connect_pol, pol, sizeof(POLICY_HND));
+
+ return (PyObject*)o;
+}
+
+/* SAMR domain handle object */
+
+static void py_samr_domain_hnd_dealloc(PyObject* self)
+{
+ PyObject_Del(self);
+}
+
+static PyObject *samr_enum_dom_groups(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ samr_domain_hnd_object *domain_hnd = (samr_domain_hnd_object *)self;
+ static char *kwlist[] = { NULL };
+ TALLOC_CTX *mem_ctx;
+/* uint32 desired_access = MAXIMUM_ALLOWED_ACCESS; */
+ uint32 start_idx, size, num_dom_groups;
+ struct acct_info *dom_groups;
+ NTSTATUS result;
+ PyObject *py_result = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "", kwlist))
+ return NULL;
+
+ if (!(mem_ctx = talloc_init("samr_enum_dom_groups"))) {
+ PyErr_SetString(samr_error, "unable to init talloc context");
+ return NULL;
+ }
+
+ start_idx = 0;
+ size = 0xffff;
+
+ do {
+ result = cli_samr_enum_dom_groups(
+ domain_hnd->cli, mem_ctx, &domain_hnd->domain_pol,
+ &start_idx, size, &dom_groups, &num_dom_groups);
+
+ if (NT_STATUS_IS_OK(result) ||
+ NT_STATUS_V(result) == NT_STATUS_V(STATUS_MORE_ENTRIES)) {
+ py_from_acct_info(&py_result, dom_groups,
+ num_dom_groups);
+ }
+
+ } while (NT_STATUS_V(result) == NT_STATUS_V(STATUS_MORE_ENTRIES));
+
+ return py_result;
+}
+
+static PyObject *samr_create_dom_user(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ samr_domain_hnd_object *domain_hnd = (samr_domain_hnd_object *)self;
+ static char *kwlist[] = { "account_name", "acb_info", NULL };
+ char *account_name;
+ NTSTATUS ntstatus;
+ uint32 unknown = 0xe005000b; /* Access mask? */
+ uint32 user_rid;
+ PyObject *result = NULL;
+ TALLOC_CTX *mem_ctx;
+ uint16 acb_info = ACB_NORMAL;
+ POLICY_HND user_pol;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "s|i", kwlist, &account_name, &acb_info))
+ return NULL;
+
+ if (!(mem_ctx = talloc_init("samr_create_dom_user"))) {
+ PyErr_SetString(samr_error, "unable to init talloc context");
+ return NULL;
+ }
+
+ ntstatus = cli_samr_create_dom_user(
+ domain_hnd->cli, mem_ctx, &domain_hnd->domain_pol,
+ account_name, acb_info, unknown, &user_pol, &user_rid);
+
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ PyErr_SetObject(samr_ntstatus, py_ntstatus_tuple(ntstatus));
+ talloc_destroy(mem_ctx);
+ goto done;
+ }
+
+ result = new_samr_user_hnd_object(
+ domain_hnd->cli, mem_ctx, &user_pol);
+
+done:
+
+ return result;
+}
+
+static PyMethodDef samr_domain_methods[] = {
+ { "enum_domain_groups", (PyCFunction)samr_enum_dom_groups,
+ METH_VARARGS | METH_KEYWORDS, "Enumerate domain groups" },
+ { "create_domain_user", (PyCFunction)samr_create_dom_user,
+ METH_VARARGS | METH_KEYWORDS, "Create domain user" },
+ { NULL }
+};
+
+static PyObject *py_samr_domain_hnd_getattr(PyObject *self, char *attrname)
+{
+ return Py_FindMethod(samr_domain_methods, self, attrname);
+}
+
+PyTypeObject samr_domain_hnd_type = {
+ PyObject_HEAD_INIT(NULL)
+ 0,
+ "SAMR Domain Handle",
+ sizeof(samr_domain_hnd_object),
+ 0,
+ py_samr_domain_hnd_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ py_samr_domain_hnd_getattr, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+};
+
+static PyObject *samr_connect(PyObject *self, PyObject *args, PyObject *kw)
+{
+ static char *kwlist[] = { "server", "creds", "access", NULL };
+ uint32 desired_access = MAXIMUM_ALLOWED_ACCESS;
+ char *server, *errstr;
+ struct cli_state *cli = NULL;
+ POLICY_HND hnd;
+ TALLOC_CTX *mem_ctx = NULL;
+ PyObject *result = NULL, *creds = NULL;
+ NTSTATUS ntstatus;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "s|Oi", kwlist, &server, &creds,
+ &desired_access))
+ return NULL;
+
+ if (server[0] != '\\' || server[1] != '\\') {
+ PyErr_SetString(PyExc_ValueError, "UNC name required");
+ return NULL;
+ }
+
+ server += 2;
+
+ if (creds && creds != Py_None && !PyDict_Check(creds)) {
+ PyErr_SetString(PyExc_TypeError,
+ "credentials must be dictionary or None");
+ return NULL;
+ }
+
+ if (!(cli = open_pipe_creds(server, creds, PI_SAMR, &errstr))) {
+ PyErr_SetString(samr_error, errstr);
+ free(errstr);
+ return NULL;
+ }
+
+ if (!(mem_ctx = talloc_init("samr_connect"))) {
+ PyErr_SetString(samr_ntstatus,
+ "unable to init talloc context\n");
+ goto done;
+ }
+
+ ntstatus = cli_samr_connect(cli, mem_ctx, desired_access, &hnd);
+
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ cli_shutdown(cli);
+ PyErr_SetObject(samr_ntstatus, py_ntstatus_tuple(ntstatus));
+ goto done;
+ }
+
+ result = new_samr_connect_hnd_object(cli, mem_ctx, &hnd);
+
+done:
+ if (!result) {
+ if (cli)
+ cli_shutdown(cli);
+
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+ }
+
+ return result;
+}
+
+/*
+ * Module initialisation
+ */
+
+static PyMethodDef samr_methods[] = {
+
+ /* Open/close samr connect handles */
+
+ { "connect", (PyCFunction)samr_connect,
+ METH_VARARGS | METH_KEYWORDS,
+ "Open a connect handle" },
+
+ { NULL }
+};
+
+static struct const_vals {
+ char *name;
+ uint32 value;
+} module_const_vals[] = {
+
+ /* Account control bits */
+
+ { "ACB_DISABLED", 0x0001 },
+ { "ACB_HOMDIRREQ", 0x0002 },
+ { "ACB_PWNOTREQ", 0x0004 },
+ { "ACB_TEMPDUP", 0x0008 },
+ { "ACB_NORMAL", 0x0010 },
+ { "ACB_MNS", 0x0020 },
+ { "ACB_DOMTRUST", 0x0040 },
+ { "ACB_WSTRUST", 0x0080 },
+ { "ACB_SVRTRUST", 0x0100 },
+ { "ACB_PWNOEXP", 0x0200 },
+ { "ACB_AUTOLOCK", 0x0400 },
+
+ { NULL }
+};
+
+static void const_init(PyObject *dict)
+{
+ struct const_vals *tmp;
+ PyObject *obj;
+
+ for (tmp = module_const_vals; tmp->name; tmp++) {
+ obj = PyInt_FromLong(tmp->value);
+ PyDict_SetItemString(dict, tmp->name, obj);
+ Py_DECREF(obj);
+ }
+}
+
+void initsamr(void)
+{
+ PyObject *module, *dict;
+
+ /* Initialise module */
+
+ module = Py_InitModule("samr", samr_methods);
+ dict = PyModule_GetDict(module);
+
+ samr_error = PyErr_NewException("samr.error", NULL, NULL);
+ PyDict_SetItemString(dict, "error", samr_error);
+
+ samr_ntstatus = PyErr_NewException("samr.ntstatus", NULL, NULL);
+ PyDict_SetItemString(dict, "ntstatus", samr_ntstatus);
+
+ /* Initialise policy handle object */
+
+ samr_connect_hnd_type.ob_type = &PyType_Type;
+ samr_domain_hnd_type.ob_type = &PyType_Type;
+ samr_user_hnd_type.ob_type = &PyType_Type;
+ samr_group_hnd_type.ob_type = &PyType_Type;
+ samr_alias_hnd_type.ob_type = &PyType_Type;
+
+ /* Initialise constants */
+
+ const_init(dict);
+
+ /* Do samba initialisation */
+
+ py_samba_init();
+
+ setup_logging("samr", True);
+ DEBUGLEVEL = 10;
+}
diff --git a/source/python/py_samr.h b/source/python/py_samr.h
new file mode 100644
index 00000000000..4d2b0675b47
--- /dev/null
+++ b/source/python/py_samr.h
@@ -0,0 +1,86 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _PY_SAMR_H
+#define _PY_SAMR_H
+
+#include "python/py_common.h"
+
+/* SAMR connect policy handle object */
+
+typedef struct {
+ PyObject_HEAD
+ struct cli_state *cli;
+ TALLOC_CTX *mem_ctx;
+ POLICY_HND connect_pol;
+} samr_connect_hnd_object;
+
+/* SAMR domain policy handle object */
+
+typedef struct {
+ PyObject_HEAD
+ struct cli_state *cli;
+ TALLOC_CTX *mem_ctx;
+ POLICY_HND domain_pol;
+} samr_domain_hnd_object;
+
+/* SAMR user policy handle object */
+
+typedef struct {
+ PyObject_HEAD
+ struct cli_state *cli;
+ TALLOC_CTX *mem_ctx;
+ POLICY_HND user_pol;
+} samr_user_hnd_object;
+
+/* SAMR group policy handle object */
+
+typedef struct {
+ PyObject_HEAD
+ struct cli_state *cli;
+ TALLOC_CTX *mem_ctx;
+ POLICY_HND group_pol;
+} samr_group_hnd_object;
+
+/* SAMR alias policy handle object */
+
+typedef struct {
+ PyObject_HEAD
+ struct cli_state *cli;
+ TALLOC_CTX *mem_ctx;
+ POLICY_HND alias_pol;
+} samr_alias_hnd_object;
+
+extern PyTypeObject samr_connect_hnd_type, samr_domain_hnd_type,
+ samr_user_hnd_type, samr_group_hnd_type, samr_alias_hnd_type;
+
+/* Exceptions raised by this module */
+
+extern PyObject *samr_error;
+
+/* The following definitions are from py_samr_conv.c */
+
+BOOL py_from_acct_info(PyObject **array, struct acct_info *info, int num_accts);
+BOOL py_from_SAM_USER_INFO_10(PyObject **dict, SAM_USER_INFO_10 *info);
+BOOL py_to_SAM_USER_INFO_10(SAM_USER_INFO_10 *info, PyObject *dict);
+BOOL py_from_SAM_USER_INFO_21(PyObject **dict, SAM_USER_INFO_21 *info);
+BOOL py_to_SAM_USER_INFO_21(SAM_USER_INFO_21 *info, PyObject *dict);
+
+#endif /* _PY_SAMR_H */
diff --git a/source/python/py_samr_conv.c b/source/python/py_samr_conv.c
new file mode 100644
index 00000000000..7523ee7dfcb
--- /dev/null
+++ b/source/python/py_samr_conv.c
@@ -0,0 +1,131 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "python/py_samr.h"
+#include "python/py_conv.h"
+
+/*
+ * Convert between SAM_USER_INFO_10 and Python
+ */
+
+struct pyconv py_SAM_USER_INFO_10[] = {
+ { "acb_info", PY_UINT32, offsetof(SAM_USER_INFO_10, acb_info) },
+ { NULL }
+};
+
+BOOL py_from_SAM_USER_INFO_10(PyObject **dict, SAM_USER_INFO_10 *info)
+{
+ *dict = from_struct(info, py_SAM_USER_INFO_10);
+ PyDict_SetItemString(*dict, "level", PyInt_FromLong(0x10));
+ return True;
+}
+
+BOOL py_to_SAM_USER_INFO_10(SAM_USER_INFO_10 *info, PyObject *dict)
+{
+ PyObject *obj, *dict_copy = PyDict_Copy(dict);
+ BOOL result = False;
+
+ if (!(obj = PyDict_GetItemString(dict_copy, "level")) ||
+ !PyInt_Check(obj))
+ goto done;
+
+ PyDict_DelItemString(dict_copy, "level");
+
+ if (!to_struct(info, dict_copy, py_SAM_USER_INFO_10))
+ goto done;
+
+ result = True;
+
+done:
+ Py_DECREF(dict_copy);
+ return result;
+}
+
+/*
+ * Convert between SAM_USER_INFO_21 and Python
+ */
+
+struct pyconv py_SAM_USER_INFO_21[] = {
+ { NULL }
+};
+
+BOOL py_from_SAM_USER_INFO_21(PyObject **dict, SAM_USER_INFO_21 *info)
+{
+ *dict = from_struct(info, py_SAM_USER_INFO_21);
+ PyDict_SetItemString(*dict, "level", PyInt_FromLong(21));
+ return True;
+}
+
+BOOL py_to_SAM_USER_INFO_21(SAM_USER_INFO_21 *info, PyObject *dict)
+{
+ PyObject *obj, *dict_copy = PyDict_Copy(dict);
+ BOOL result = False;
+
+ if (!(obj = PyDict_GetItemString(dict_copy, "level")) ||
+ !PyInt_Check(obj))
+ goto done;
+
+ PyDict_DelItemString(dict_copy, "level");
+
+ if (!to_struct(info, dict_copy, py_SAM_USER_INFO_21))
+ goto done;
+
+ result = True;
+
+done:
+ Py_DECREF(dict_copy);
+ return result;
+}
+
+/*
+ * Convert between acct_info and Python
+ */
+
+BOOL py_from_acct_info(PyObject **array, struct acct_info *info, int num_accts)
+{
+ int i;
+
+ *array = PyList_New(num_accts);
+
+ for (i = 0; i < num_accts; i++) {
+ PyObject *obj;
+
+ obj = PyDict_New();
+
+ PyDict_SetItemString(
+ obj, "name", PyString_FromString(info[i].acct_name));
+
+ PyDict_SetItemString(
+ obj, "description",
+ PyString_FromString(info[i].acct_desc));
+
+ PyDict_SetItemString(obj, "rid", PyInt_FromLong(info[i].rid));
+
+ PyList_SetItem(*array, i, obj);
+ }
+
+ return True;
+}
+
+BOOL py_to_acct_info(PRINTER_INFO_3 *info, PyObject *dict,
+ TALLOC_CTX *mem_ctx)
+{
+ return False;
+}
diff --git a/source/python/py_smb.c b/source/python/py_smb.c
new file mode 100644
index 00000000000..e5e65061965
--- /dev/null
+++ b/source/python/py_smb.c
@@ -0,0 +1,442 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "python/py_smb.h"
+
+/* Create a new cli_state python object */
+
+PyObject *new_cli_state_object(struct cli_state *cli)
+{
+ cli_state_object *o;
+
+ o = PyObject_New(cli_state_object, &cli_state_type);
+
+ o->cli = cli;
+
+ return (PyObject*)o;
+}
+
+static PyObject *py_smb_connect(PyObject *self, PyObject *args, PyObject *kw)
+{
+ static char *kwlist[] = { "server", NULL };
+ struct cli_state *cli;
+ char *server;
+ struct in_addr ip;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "s", kwlist, &server))
+ return NULL;
+
+ if (!(cli = cli_initialise(NULL)))
+ return NULL;
+
+ ZERO_STRUCT(ip);
+
+ if (!cli_connect(cli, server, &ip))
+ return NULL;
+
+ return new_cli_state_object(cli);
+}
+
+static PyObject *py_smb_session_request(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ cli_state_object *cli = (cli_state_object *)self;
+ static char *kwlist[] = { "called", "calling", NULL };
+ char *calling_name = NULL, *called_name;
+ struct nmb_name calling, called;
+ BOOL result;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "s|s", kwlist, &called_name,
+ &calling_name))
+ return NULL;
+
+ if (!calling_name)
+ calling_name = global_myname();
+
+ make_nmb_name(&calling, calling_name, 0x00);
+ make_nmb_name(&called, called_name, 0x20);
+
+ result = cli_session_request(cli->cli, &calling, &called);
+
+ return Py_BuildValue("i", result);
+}
+
+static PyObject *py_smb_negprot(PyObject *self, PyObject *args, PyObject *kw)
+{
+ cli_state_object *cli = (cli_state_object *)self;
+ static char *kwlist[] = { NULL };
+ BOOL result;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "", kwlist))
+ return NULL;
+
+ result = cli_negprot(cli->cli);
+
+ return Py_BuildValue("i", result);
+}
+
+static PyObject *py_smb_session_setup(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ cli_state_object *cli = (cli_state_object *)self;
+ static char *kwlist[] = { "creds", NULL };
+ PyObject *creds;
+ char *username, *domain, *password, *errstr;
+ BOOL result;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "|O", kwlist, &creds))
+ return NULL;
+
+ if (!py_parse_creds(creds, &username, &domain, &password, &errstr)) {
+ free(errstr);
+ return NULL;
+ }
+
+ result = cli_session_setup(
+ cli->cli, username, password, strlen(password) + 1,
+ password, strlen(password) + 1, domain);
+
+ if (cli_is_error(cli->cli)) {
+ PyErr_SetString(PyExc_RuntimeError, "session setup failed");
+ return NULL;
+ }
+
+ return Py_BuildValue("i", result);
+}
+
+static PyObject *py_smb_tconx(PyObject *self, PyObject *args, PyObject *kw)
+{
+ cli_state_object *cli = (cli_state_object *)self;
+ static char *kwlist[] = { "service", NULL };
+ char *service;
+ BOOL result;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "s", kwlist, &service))
+ return NULL;
+
+ result = cli_send_tconX(
+ cli->cli, service, strequal(service, "IPC$") ? "IPC" :
+ "?????", "", 1);
+
+ if (cli_is_error(cli->cli)) {
+ PyErr_SetString(PyExc_RuntimeError, "tconx failed");
+ return NULL;
+ }
+
+ return Py_BuildValue("i", result);
+}
+
+static PyObject *py_smb_nt_create_andx(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ cli_state_object *cli = (cli_state_object *)self;
+ static char *kwlist[] = { "filename", "desired_access",
+ "file_attributes", "share_access",
+ "create_disposition", NULL };
+ char *filename;
+ uint32 desired_access, file_attributes = 0,
+ share_access = FILE_SHARE_READ | FILE_SHARE_WRITE,
+ create_disposition = FILE_EXISTS_OPEN, create_options = 0;
+ int result;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "si|iii", kwlist, &filename, &desired_access,
+ &file_attributes, &share_access, &create_disposition,
+ &create_options))
+ return NULL;
+
+ result = cli_nt_create_full(
+ cli->cli, filename, 0, desired_access, file_attributes,
+ share_access, create_disposition, create_options, 0);
+
+ if (cli_is_error(cli->cli)) {
+ PyErr_SetString(PyExc_RuntimeError, "nt_create_andx failed");
+ return NULL;
+ }
+
+ /* Return FID */
+
+ return PyInt_FromLong(result);
+}
+
+static PyObject *py_smb_close(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ cli_state_object *cli = (cli_state_object *)self;
+ static char *kwlist[] = { "fnum", NULL };
+ BOOL result;
+ int fnum;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "i", kwlist, &fnum))
+ return NULL;
+
+ result = cli_close(cli->cli, fnum);
+
+ return PyInt_FromLong(result);
+}
+
+static PyObject *py_smb_unlink(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ cli_state_object *cli = (cli_state_object *)self;
+ static char *kwlist[] = { "filename", NULL };
+ char *filename;
+ BOOL result;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "s", kwlist, &filename))
+ return NULL;
+
+ result = cli_unlink(cli->cli, filename);
+
+ return PyInt_FromLong(result);
+}
+
+static PyObject *py_smb_query_secdesc(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ cli_state_object *cli = (cli_state_object *)self;
+ static char *kwlist[] = { "fnum", NULL };
+ PyObject *result = NULL;
+ SEC_DESC *secdesc = NULL;
+ int fnum;
+ TALLOC_CTX *mem_ctx = NULL;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "i", kwlist, &fnum))
+ return NULL;
+
+ mem_ctx = talloc_init("py_smb_query_secdesc");
+
+ secdesc = cli_query_secdesc(cli->cli, fnum, mem_ctx);
+
+ if (cli_is_error(cli->cli)) {
+ PyErr_SetString(PyExc_RuntimeError, "query_secdesc failed");
+ goto done;
+ }
+
+ if (!secdesc) {
+ Py_INCREF(Py_None);
+ result = Py_None;
+ goto done;
+ }
+
+ if (!py_from_SECDESC(&result, secdesc)) {
+ PyErr_SetString(
+ PyExc_TypeError,
+ "Invalid security descriptor returned");
+ goto done;
+ }
+
+ done:
+ talloc_destroy(mem_ctx);
+
+ return result;
+
+}
+
+static PyObject *py_smb_set_secdesc(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ cli_state_object *cli = (cli_state_object *)self;
+ static char *kwlist[] = { "fnum", "security_descriptor", NULL };
+ PyObject *result = NULL;
+ PyObject *py_secdesc;
+ SEC_DESC *secdesc;
+ TALLOC_CTX *mem_ctx = NULL;
+ int fnum;
+ BOOL err;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "iO", kwlist, &fnum, &py_secdesc))
+ return NULL;
+
+ mem_ctx = talloc_init("py_smb_set_secdesc");
+
+ if (!py_to_SECDESC(&secdesc, py_secdesc, mem_ctx)) {
+ PyErr_SetString(PyExc_TypeError,
+ "Invalid security descriptor");
+ goto done;
+ }
+
+ err = cli_set_secdesc(cli->cli, fnum, secdesc);
+
+ if (cli_is_error(cli->cli)) {
+ PyErr_SetString(PyExc_RuntimeError, "set_secdesc failed");
+ goto done;
+ }
+
+ result = PyInt_FromLong(err);
+ done:
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+static PyMethodDef smb_hnd_methods[] = {
+
+ /* Session and connection handling */
+
+ { "session_request", (PyCFunction)py_smb_session_request,
+ METH_VARARGS | METH_KEYWORDS, "Request a session" },
+
+ { "negprot", (PyCFunction)py_smb_negprot,
+ METH_VARARGS | METH_KEYWORDS, "Protocol negotiation" },
+
+ { "session_setup", (PyCFunction)py_smb_session_setup,
+ METH_VARARGS | METH_KEYWORDS, "Session setup" },
+
+ { "tconx", (PyCFunction)py_smb_tconx,
+ METH_VARARGS | METH_KEYWORDS, "Tree connect" },
+
+ /* File operations */
+
+ { "nt_create_andx", (PyCFunction)py_smb_nt_create_andx,
+ METH_VARARGS | METH_KEYWORDS, "NT Create&X" },
+
+ { "close", (PyCFunction)py_smb_close,
+ METH_VARARGS | METH_KEYWORDS, "Close" },
+
+ { "unlink", (PyCFunction)py_smb_unlink,
+ METH_VARARGS | METH_KEYWORDS, "Unlink" },
+
+ /* Security descriptors */
+
+ { "query_secdesc", (PyCFunction)py_smb_query_secdesc,
+ METH_VARARGS | METH_KEYWORDS, "Query security descriptor" },
+
+ { "set_secdesc", (PyCFunction)py_smb_set_secdesc,
+ METH_VARARGS | METH_KEYWORDS, "Set security descriptor" },
+
+ { NULL }
+};
+
+/*
+ * Method dispatch tables
+ */
+
+static PyMethodDef smb_methods[] = {
+
+ { "connect", (PyCFunction)py_smb_connect, METH_VARARGS | METH_KEYWORDS,
+ "Connect to a host" },
+
+ /* Other stuff - this should really go into a samba config module
+ but for the moment let's leave it here. */
+
+ { "setup_logging", (PyCFunction)py_setup_logging,
+ METH_VARARGS | METH_KEYWORDS,
+ "Set up debug logging.\n"
+"\n"
+"Initialises Samba's debug logging system. One argument is expected which\n"
+"is a boolean specifying whether debugging is interactive and sent to stdout\n"
+"or logged to a file.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> smb.setup_logging(interactive = 1)" },
+
+ { "get_debuglevel", (PyCFunction)get_debuglevel,
+ METH_VARARGS,
+ "Set the current debug level.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> smb.get_debuglevel()\n"
+"0" },
+
+ { "set_debuglevel", (PyCFunction)set_debuglevel,
+ METH_VARARGS,
+ "Get the current debug level.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> smb.set_debuglevel(10)" },
+
+ { NULL }
+};
+
+static void py_cli_state_dealloc(PyObject* self)
+{
+ cli_state_object *cli = (cli_state_object *)self;
+
+ if (cli->cli)
+ cli_shutdown(cli->cli);
+
+ PyObject_Del(self);
+}
+
+static PyObject *py_cli_state_getattr(PyObject *self, char *attrname)
+{
+ return Py_FindMethod(smb_hnd_methods, self, attrname);
+}
+
+PyTypeObject cli_state_type = {
+ PyObject_HEAD_INIT(NULL)
+ 0,
+ "SMB client connection",
+ sizeof(cli_state_object),
+ 0,
+ py_cli_state_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ py_cli_state_getattr, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+};
+
+/*
+ * Module initialisation
+ */
+
+void initsmb(void)
+{
+ PyObject *module, *dict;
+
+ /* Initialise module */
+
+ module = Py_InitModule("smb", smb_methods);
+ dict = PyModule_GetDict(module);
+
+ /* Initialise policy handle object */
+
+ cli_state_type.ob_type = &PyType_Type;
+
+ /* Do samba initialisation */
+
+ py_samba_init();
+
+ setup_logging("smb", True);
+ DEBUGLEVEL = 3;
+}
diff --git a/source/python/py_smb.h b/source/python/py_smb.h
new file mode 100644
index 00000000000..31bcf4aab2e
--- /dev/null
+++ b/source/python/py_smb.h
@@ -0,0 +1,39 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _PY_SMB_H
+#define _PY_SMB_H
+
+#include "python/py_common.h"
+
+/* cli_state handle object */
+
+typedef struct {
+ PyObject_HEAD
+ struct cli_state *cli;
+} cli_state_object;
+
+/* Exceptions raised by this module */
+
+extern PyTypeObject cli_state_type;
+
+extern PyObject *smb_ntstatus;
+
+#endif /* _PY_SMB_H */
diff --git a/source/python/py_spoolss.c b/source/python/py_spoolss.c
new file mode 100644
index 00000000000..b8df5cbf113
--- /dev/null
+++ b/source/python/py_spoolss.c
@@ -0,0 +1,485 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "python/py_spoolss.h"
+
+/* Exceptions this module can raise */
+
+PyObject *spoolss_error, *spoolss_werror;
+
+/*
+ * Method dispatch table
+ */
+
+static PyMethodDef spoolss_methods[] = {
+
+ /* Open/close printer handles */
+
+ { "openprinter", (PyCFunction)spoolss_openprinter, METH_VARARGS | METH_KEYWORDS,
+ "Open a printer by name in UNC format.\n"
+"\n"
+"Optionally a dictionary of (domain, username, password) may be given in\n"
+"which case they are used when opening the RPC pipe. An access mask may\n"
+"also be given which defaults to MAXIMUM_ALLOWED_ACCESS.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> hnd = spoolss.openprinter(\"\\\\\\\\NPSD-PDC2\\\\meanie\")"},
+
+ { "closeprinter", spoolss_closeprinter, METH_VARARGS,
+ "Close a printer handle opened with openprinter or addprinter.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> spoolss.closeprinter(hnd)"},
+
+ { "addprinterex", (PyCFunction)spoolss_addprinterex, METH_VARARGS,
+ "addprinterex()"},
+
+ /* Server enumeratation functions */
+
+ { "enumprinters", (PyCFunction)spoolss_enumprinters,
+ METH_VARARGS | METH_KEYWORDS,
+ "Enumerate printers on a print server.\n"
+"\n"
+"Return a list of printers on a print server. The credentials, info level\n"
+"and flags may be specified as keyword arguments.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> print spoolss.enumprinters(\"\\\\\\\\npsd-pdc2\")\n"
+"[{'comment': 'i am a comment', 'printer_name': 'meanie', 'flags': 8388608, \n"
+" 'description': 'meanie,Generic / Text Only,i am a location'}, \n"
+" {'comment': '', 'printer_name': 'fileprint', 'flags': 8388608, \n"
+" 'description': 'fileprint,Generic / Text Only,'}]"},
+
+ { "enumports", (PyCFunction)spoolss_enumports,
+ METH_VARARGS | METH_KEYWORDS,
+ "Enumerate ports on a print server.\n"
+"\n"
+"Return a list of ports on a print server.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> print spoolss.enumports(\"\\\\\\\\npsd-pdc2\")\n"
+"[{'name': 'LPT1:'}, {'name': 'LPT2:'}, {'name': 'COM1:'}, \n"
+"{'name': 'COM2:'}, {'name': 'FILE:'}, {'name': '\\\\nautilus1\\zpekt3r'}]"},
+
+ { "enumprinterdrivers", (PyCFunction)spoolss_enumprinterdrivers,
+ METH_VARARGS | METH_KEYWORDS,
+ "Enumerate printer drivers on a print server.\n"
+"\n"
+"Return a list of printer drivers."},
+
+ /* Miscellaneous other commands */
+
+ { "getprinterdriverdir", (PyCFunction)spoolss_getprinterdriverdir,
+ METH_VARARGS | METH_KEYWORDS,
+ "Return printer driver directory.\n"
+"\n"
+"Return the printer driver directory for a given architecture. The\n"
+"architecture defaults to \"Windows NT x86\"."},
+
+ /* Other stuff - this should really go into a samba config module
+ but for the moment let's leave it here. */
+
+ { "setup_logging", (PyCFunction)py_setup_logging,
+ METH_VARARGS | METH_KEYWORDS,
+ "Set up debug logging.\n"
+"\n"
+"Initialises Samba's debug logging system. One argument is expected which\n"
+"is a boolean specifying whether debugging is interactive and sent to stdout\n"
+"or logged to a file.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> spoolss.setup_logging(interactive = 1)" },
+
+ { "get_debuglevel", (PyCFunction)get_debuglevel,
+ METH_VARARGS,
+ "Set the current debug level.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> spoolss.get_debuglevel()\n"
+"0" },
+
+ { "set_debuglevel", (PyCFunction)set_debuglevel,
+ METH_VARARGS,
+ "Get the current debug level.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> spoolss.set_debuglevel(10)" },
+
+ /* Printer driver routines */
+
+ { "addprinterdriver", (PyCFunction)spoolss_addprinterdriver,
+ METH_VARARGS | METH_KEYWORDS,
+ "Add a printer driver." },
+
+ { "addprinterdriverex", (PyCFunction)spoolss_addprinterdriverex,
+ METH_VARARGS | METH_KEYWORDS,
+ "Add a printer driver." },
+
+ { "deleteprinterdriver", (PyCFunction)spoolss_deleteprinterdriver,
+ METH_VARARGS | METH_KEYWORDS,
+ "Delete a printer driver." },
+
+ { "deleteprinterdriverex", (PyCFunction)spoolss_deleteprinterdriverex,
+ METH_VARARGS | METH_KEYWORDS,
+ "Delete a printer driver." },
+
+ { NULL }
+};
+
+/* Methods attached to a spoolss handle object */
+
+static PyMethodDef spoolss_hnd_methods[] = {
+
+ /* Printer info */
+
+ { "getprinter", (PyCFunction)spoolss_hnd_getprinter,
+ METH_VARARGS | METH_KEYWORDS,
+ "Get printer information.\n"
+"\n"
+"Return a dictionary of print information. The info level defaults to 1.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> hnd.getprinter()\n"
+"{'comment': 'i am a comment', 'printer_name': '\\\\NPSD-PDC2\\meanie',\n"
+" 'description': '\\\\NPSD-PDC2\\meanie,Generic / Text Only,i am a location',\n"
+" 'flags': 8388608}"},
+
+ { "setprinter", (PyCFunction)spoolss_hnd_setprinter,
+ METH_VARARGS | METH_KEYWORDS,
+ "Set printer information."},
+
+ /* Printer drivers */
+
+ { "getprinterdriver", (PyCFunction)spoolss_hnd_getprinterdriver,
+ METH_VARARGS | METH_KEYWORDS,
+ "Return printer driver information.\n"
+"\n"
+"Return a dictionary of printer driver information for the printer driver\n"
+"bound to this printer."},
+
+ /* Forms */
+
+ { "enumforms", (PyCFunction)spoolss_hnd_enumforms,
+ METH_VARARGS | METH_KEYWORDS,
+ "Enumerate supported forms.\n"
+"\n"
+"Return a list of forms supported by this printer or print server."},
+
+ { "setform", (PyCFunction)spoolss_hnd_setform,
+ METH_VARARGS | METH_KEYWORDS,
+ "Set form data.\n"
+"\n"
+"Set the form given by the dictionary argument."},
+
+ { "addform", (PyCFunction)spoolss_hnd_addform,
+ METH_VARARGS | METH_KEYWORDS,
+ "Add a new form." },
+
+ { "getform", (PyCFunction)spoolss_hnd_getform,
+ METH_VARARGS | METH_KEYWORDS,
+ "Get form properties." },
+
+ { "deleteform", (PyCFunction)spoolss_hnd_deleteform,
+ METH_VARARGS | METH_KEYWORDS,
+ "Delete a form." },
+
+ /* Job related methods */
+
+ { "enumjobs", (PyCFunction)spoolss_hnd_enumjobs,
+ METH_VARARGS | METH_KEYWORDS,
+ "Enumerate jobs." },
+
+ { "setjob", (PyCFunction)spoolss_hnd_setjob,
+ METH_VARARGS | METH_KEYWORDS,
+ "Set job information." },
+
+ { "getjob", (PyCFunction)spoolss_hnd_getjob,
+ METH_VARARGS | METH_KEYWORDS,
+ "Get job information." },
+
+ { "startpageprinter", (PyCFunction)spoolss_hnd_startpageprinter,
+ METH_VARARGS | METH_KEYWORDS,
+ "Notify spooler that a page is about to be printed." },
+
+ { "endpageprinter", (PyCFunction)spoolss_hnd_endpageprinter,
+ METH_VARARGS | METH_KEYWORDS,
+ "Notify spooler that a page is about to be printed." },
+
+ { "startdocprinter", (PyCFunction)spoolss_hnd_startdocprinter,
+ METH_VARARGS | METH_KEYWORDS,
+ "Notify spooler that a document is about to be printed." },
+
+ { "enddocprinter", (PyCFunction)spoolss_hnd_enddocprinter,
+ METH_VARARGS | METH_KEYWORDS,
+ "Notify spooler that a document is about to be printed." },
+
+ { "writeprinter", (PyCFunction)spoolss_hnd_writeprinter,
+ METH_VARARGS | METH_KEYWORDS,
+ "Write job data to a printer." },
+
+ { "addjob", (PyCFunction)spoolss_hnd_addjob,
+ METH_VARARGS | METH_KEYWORDS,
+ "Add a job to the list of print jobs." },
+
+ /* Printer data */
+
+ { "getprinterdata", (PyCFunction)spoolss_hnd_getprinterdata,
+ METH_VARARGS | METH_KEYWORDS,
+ "Get printer data." },
+
+ { "setprinterdata", (PyCFunction)spoolss_hnd_setprinterdata,
+ METH_VARARGS | METH_KEYWORDS,
+ "Set printer data." },
+
+ { "enumprinterdata", (PyCFunction)spoolss_hnd_enumprinterdata,
+ METH_VARARGS | METH_KEYWORDS,
+ "Enumerate printer data." },
+
+ { "deleteprinterdata", (PyCFunction)spoolss_hnd_deleteprinterdata,
+ METH_VARARGS | METH_KEYWORDS,
+ "Delete printer data." },
+
+ { "getprinterdataex", (PyCFunction)spoolss_hnd_getprinterdataex,
+ METH_VARARGS | METH_KEYWORDS,
+ "Get printer data." },
+
+ { "setprinterdataex", (PyCFunction)spoolss_hnd_setprinterdataex,
+ METH_VARARGS | METH_KEYWORDS,
+ "Set printer data." },
+
+ { "enumprinterdataex", (PyCFunction)spoolss_hnd_enumprinterdataex,
+ METH_VARARGS | METH_KEYWORDS,
+ "Enumerate printer data." },
+
+ { "deleteprinterdataex", (PyCFunction)spoolss_hnd_deleteprinterdataex,
+ METH_VARARGS | METH_KEYWORDS,
+ "Delete printer data." },
+
+ { "enumprinterkey", (PyCFunction)spoolss_hnd_enumprinterkey,
+ METH_VARARGS | METH_KEYWORDS,
+ "Enumerate printer key." },
+
+#if 0
+ /* Not implemented */
+
+ { "deleteprinterkey", (PyCFunction)spoolss_hnd_deleteprinterkey,
+ METH_VARARGS | METH_KEYWORDS,
+ "Delete printer key." },
+#endif
+
+ { NULL }
+
+};
+
+static void py_policy_hnd_dealloc(PyObject* self)
+{
+ spoolss_policy_hnd_object *hnd;
+
+ /* Close down policy handle and free talloc context */
+
+ hnd = (spoolss_policy_hnd_object*)self;
+
+ cli_shutdown(hnd->cli);
+ talloc_destroy(hnd->mem_ctx);
+
+ PyObject_Del(self);
+}
+
+static PyObject *py_policy_hnd_getattr(PyObject *self, char *attrname)
+{
+ return Py_FindMethod(spoolss_hnd_methods, self, attrname);
+}
+
+static char spoolss_type_doc[] =
+"Python wrapper for Windows NT SPOOLSS rpc pipe.";
+
+PyTypeObject spoolss_policy_hnd_type = {
+ PyObject_HEAD_INIT(NULL)
+ 0,
+ "spoolss.hnd",
+ sizeof(spoolss_policy_hnd_object),
+ 0,
+ py_policy_hnd_dealloc, /* tp_dealloc*/
+ 0, /* tp_print*/
+ py_policy_hnd_getattr, /* tp_getattr*/
+ 0, /* tp_setattr*/
+ 0, /* tp_compare*/
+ 0, /* tp_repr*/
+ 0, /* tp_as_number*/
+ 0, /* tp_as_sequence*/
+ 0, /* tp_as_mapping*/
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ spoolss_type_doc, /* tp_doc */
+};
+
+/* Initialise constants */
+
+static struct const_vals {
+ char *name;
+ uint32 value;
+} module_const_vals[] = {
+
+ /* Access permissions */
+
+ { "MAXIMUM_ALLOWED_ACCESS", MAXIMUM_ALLOWED_ACCESS },
+ { "SERVER_ALL_ACCESS", SERVER_ALL_ACCESS },
+ { "SERVER_READ", SERVER_READ },
+ { "SERVER_WRITE", SERVER_WRITE },
+ { "SERVER_EXECUTE", SERVER_EXECUTE },
+ { "SERVER_ACCESS_ADMINISTER", SERVER_ACCESS_ADMINISTER },
+ { "SERVER_ACCESS_ENUMERATE", SERVER_ACCESS_ENUMERATE },
+ { "PRINTER_ALL_ACCESS", PRINTER_ALL_ACCESS },
+ { "PRINTER_READ", PRINTER_READ },
+ { "PRINTER_WRITE", PRINTER_WRITE },
+ { "PRINTER_EXECUTE", PRINTER_EXECUTE },
+ { "PRINTER_ACCESS_ADMINISTER", PRINTER_ACCESS_ADMINISTER },
+ { "PRINTER_ACCESS_USE", PRINTER_ACCESS_USE },
+ { "JOB_ACCESS_ADMINISTER", JOB_ACCESS_ADMINISTER },
+ { "JOB_ALL_ACCESS", JOB_ALL_ACCESS },
+ { "JOB_READ", JOB_READ },
+ { "JOB_WRITE", JOB_WRITE },
+ { "JOB_EXECUTE", JOB_EXECUTE },
+ { "STANDARD_RIGHTS_ALL_ACCESS", STANDARD_RIGHTS_ALL_ACCESS },
+ { "STANDARD_RIGHTS_EXECUTE_ACCESS", STANDARD_RIGHTS_EXECUTE_ACCESS },
+ { "STANDARD_RIGHTS_READ_ACCESS", STANDARD_RIGHTS_READ_ACCESS },
+ { "STANDARD_RIGHTS_REQUIRED_ACCESS", STANDARD_RIGHTS_REQUIRED_ACCESS },
+ { "STANDARD_RIGHTS_WRITE_ACCESS", STANDARD_RIGHTS_WRITE_ACCESS },
+
+ /* Printer enumeration flags */
+
+ { "PRINTER_ENUM_DEFAULT", PRINTER_ENUM_DEFAULT },
+ { "PRINTER_ENUM_LOCAL", PRINTER_ENUM_LOCAL },
+ { "PRINTER_ENUM_CONNECTIONS", PRINTER_ENUM_CONNECTIONS },
+ { "PRINTER_ENUM_FAVORITE", PRINTER_ENUM_FAVORITE },
+ { "PRINTER_ENUM_NAME", PRINTER_ENUM_NAME },
+ { "PRINTER_ENUM_REMOTE", PRINTER_ENUM_REMOTE },
+ { "PRINTER_ENUM_SHARED", PRINTER_ENUM_SHARED },
+ { "PRINTER_ENUM_NETWORK", PRINTER_ENUM_NETWORK },
+
+ /* Form types */
+
+ { "FORM_USER", FORM_USER },
+ { "FORM_BUILTIN", FORM_BUILTIN },
+ { "FORM_PRINTER", FORM_PRINTER },
+
+ /* WERRORs */
+
+ { "WERR_OK", 0 },
+ { "WERR_BADFILE", 2 },
+ { "WERR_ACCESS_DENIED", 5 },
+ { "WERR_BADFID", 6 },
+ { "WERR_BADFUNC", 1 },
+ { "WERR_INSUFFICIENT_BUFFER", 122 },
+ { "WERR_NO_SUCH_SHARE", 67 },
+ { "WERR_ALREADY_EXISTS", 80 },
+ { "WERR_INVALID_PARAM", 87 },
+ { "WERR_NOT_SUPPORTED", 50 },
+ { "WERR_BAD_PASSWORD", 86 },
+ { "WERR_NOMEM", 8 },
+ { "WERR_INVALID_NAME", 123 },
+ { "WERR_UNKNOWN_LEVEL", 124 },
+ { "WERR_OBJECT_PATH_INVALID", 161 },
+ { "WERR_NO_MORE_ITEMS", 259 },
+ { "WERR_MORE_DATA", 234 },
+ { "WERR_UNKNOWN_PRINTER_DRIVER", 1797 },
+ { "WERR_INVALID_PRINTER_NAME", 1801 },
+ { "WERR_PRINTER_ALREADY_EXISTS", 1802 },
+ { "WERR_INVALID_DATATYPE", 1804 },
+ { "WERR_INVALID_ENVIRONMENT", 1805 },
+ { "WERR_INVALID_FORM_NAME", 1902 },
+ { "WERR_INVALID_FORM_SIZE", 1903 },
+ { "WERR_BUF_TOO_SMALL", 2123 },
+ { "WERR_JOB_NOT_FOUND", 2151 },
+ { "WERR_DEST_NOT_FOUND", 2152 },
+ { "WERR_NOT_LOCAL_DOMAIN", 2320 },
+ { "WERR_PRINTER_DRIVER_IN_USE", 3001 },
+ { "WERR_STATUS_MORE_ENTRIES ", 0x0105 },
+
+ /* Job control constants */
+
+ { "JOB_CONTROL_PAUSE", JOB_CONTROL_PAUSE },
+ { "JOB_CONTROL_RESUME", JOB_CONTROL_RESUME },
+ { "JOB_CONTROL_CANCEL", JOB_CONTROL_CANCEL },
+ { "JOB_CONTROL_RESTART", JOB_CONTROL_RESTART },
+ { "JOB_CONTROL_DELETE", JOB_CONTROL_DELETE },
+
+ { NULL },
+};
+
+static void const_init(PyObject *dict)
+{
+ struct const_vals *tmp;
+ PyObject *obj;
+
+ for (tmp = module_const_vals; tmp->name; tmp++) {
+ obj = PyInt_FromLong(tmp->value);
+ PyDict_SetItemString(dict, tmp->name, obj);
+ Py_DECREF(obj);
+ }
+}
+
+/* Module initialisation */
+
+void initspoolss(void)
+{
+ PyObject *module, *dict;
+
+ /* Initialise module */
+
+ module = Py_InitModule("spoolss", spoolss_methods);
+ dict = PyModule_GetDict(module);
+
+ /* Exceptions we can raise */
+
+ spoolss_error = PyErr_NewException("spoolss.error", NULL, NULL);
+ PyDict_SetItemString(dict, "error", spoolss_error);
+
+ spoolss_werror = PyErr_NewException("spoolss.werror", NULL, NULL);
+ PyDict_SetItemString(dict, "werror", spoolss_werror);
+
+ /* Initialise policy handle object */
+
+ spoolss_policy_hnd_type.ob_type = &PyType_Type;
+
+ PyDict_SetItemString(dict, "spoolss.hnd",
+ (PyObject *)&spoolss_policy_hnd_type);
+
+ /* Initialise constants */
+
+ const_init(dict);
+
+ /* Do samba initialisation */
+
+ py_samba_init();
+}
diff --git a/source/python/py_spoolss.h b/source/python/py_spoolss.h
new file mode 100644
index 00000000000..34b48190cd1
--- /dev/null
+++ b/source/python/py_spoolss.h
@@ -0,0 +1,160 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _PY_SPOOLSS_H
+#define _PY_SPOOLSS_H
+
+#include "python/py_common.h"
+
+/* Spoolss policy handle object */
+
+typedef struct {
+ PyObject_HEAD
+ struct cli_state *cli;
+ TALLOC_CTX *mem_ctx;
+ POLICY_HND pol;
+} spoolss_policy_hnd_object;
+
+/* Exceptions raised by this module */
+
+extern PyTypeObject spoolss_policy_hnd_type;
+
+extern PyObject *spoolss_error, *spoolss_werror;
+
+/* The following definitions come from python/py_spoolss_common.c */
+
+PyObject *new_spoolss_policy_hnd_object(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, POLICY_HND *pol);
+
+/* The following definitions come from python/py_spoolss_drivers.c */
+
+PyObject *spoolss_enumprinterdrivers(PyObject *self, PyObject *args,
+ PyObject *kw);
+PyObject *spoolss_hnd_getprinterdriver(PyObject *self, PyObject *args,
+ PyObject *kw);
+PyObject *spoolss_getprinterdriverdir(PyObject *self, PyObject *args,
+ PyObject *kw);
+PyObject *spoolss_addprinterdriver(PyObject *self, PyObject *args,
+ PyObject *kw);
+PyObject *spoolss_addprinterdriverex(PyObject *self, PyObject *args,
+ PyObject *kw);
+PyObject *spoolss_deleteprinterdriver(PyObject *self, PyObject *args,
+ PyObject *kw);
+PyObject *spoolss_deleteprinterdriverex(PyObject *self, PyObject *args,
+ PyObject *kw);
+
+/* The following definitions come from python/py_spoolss_drivers_conv.c */
+
+BOOL py_from_DRIVER_INFO_1(PyObject **dict, DRIVER_INFO_1 *info);
+BOOL py_to_DRIVER_INFO_1(DRIVER_INFO_1 *info, PyObject *dict);
+BOOL py_from_DRIVER_INFO_2(PyObject **dict, DRIVER_INFO_2 *info);
+BOOL py_to_DRIVER_INFO_2(DRIVER_INFO_2 *info, PyObject *dict);
+BOOL py_from_DRIVER_INFO_3(PyObject **dict, DRIVER_INFO_3 *info);
+BOOL py_to_DRIVER_INFO_3(DRIVER_INFO_3 *info, PyObject *dict);
+BOOL py_from_DRIVER_INFO_6(PyObject **dict, DRIVER_INFO_6 *info);
+BOOL py_to_DRIVER_INFO_6(DRIVER_INFO_6 *info, PyObject *dict);
+BOOL py_from_DRIVER_DIRECTORY_1(PyObject **dict, DRIVER_DIRECTORY_1 *info);
+BOOL py_to_DRIVER_DIRECTORY_1(DRIVER_DIRECTORY_1 *info, PyObject *dict);
+
+/* The following definitions come from python/py_spoolss_forms.c */
+
+PyObject *spoolss_hnd_addform(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_getform(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_setform(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_deleteform(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_enumforms(PyObject *self, PyObject *args, PyObject *kw);
+
+/* The following definitions come from python/py_spoolss_forms_conv.c */
+
+BOOL py_from_FORM_1(PyObject **dict, FORM_1 *form);
+BOOL py_to_FORM(FORM *form, PyObject *dict);
+
+/* The following definitions come from python/py_spoolss_jobs.c */
+
+PyObject *spoolss_hnd_enumjobs(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_setjob(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_getjob(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_startpageprinter(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_endpageprinter(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_startdocprinter(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_enddocprinter(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_writeprinter(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_addjob(PyObject *self, PyObject *args, PyObject *kw);
+
+/* The following definitions come from python/py_spoolss_jobs_conv.c */
+
+BOOL py_from_JOB_INFO_1(PyObject **dict, JOB_INFO_1 *info);
+BOOL py_to_JOB_INFO_1(JOB_INFO_1 *info, PyObject *dict);
+BOOL py_from_JOB_INFO_2(PyObject **dict, JOB_INFO_2 *info);
+BOOL py_to_JOB_INFO_2(JOB_INFO_2 *info, PyObject *dict);
+BOOL py_from_DOC_INFO_1(PyObject **dict, DOC_INFO_1 *info);
+BOOL py_to_DOC_INFO_1(DOC_INFO_1 *info, PyObject *dict);
+
+/* The following definitions come from python/py_spoolss_ports.c */
+
+PyObject *spoolss_enumports(PyObject *self, PyObject *args, PyObject *kw);
+
+/* The following definitions come from python/py_spoolss_ports_conv.c */
+
+BOOL py_from_PORT_INFO_1(PyObject **dict, PORT_INFO_1 *info);
+BOOL py_to_PORT_INFO_1(PORT_INFO_1 *info, PyObject *dict);
+BOOL py_from_PORT_INFO_2(PyObject **dict, PORT_INFO_2 *info);
+BOOL py_to_PORT_INFO_2(PORT_INFO_2 *info, PyObject *dict);
+
+/* The following definitions come from python/py_spoolss_printerdata.c */
+
+PyObject *spoolss_hnd_getprinterdata(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_setprinterdata(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_enumprinterdata(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_deleteprinterdata(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_getprinterdataex(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_setprinterdataex(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_enumprinterdataex(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_deleteprinterdataex(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_enumprinterkey(PyObject *self, PyObject *args,
+ PyObject *kw);
+PyObject *spoolss_hnd_deleteprinterkey(PyObject *self, PyObject *args,
+ PyObject *kw);
+
+/* The following definitions come from python/py_spoolss_printers.c */
+
+PyObject *spoolss_openprinter(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_closeprinter(PyObject *self, PyObject *args);
+PyObject *spoolss_hnd_getprinter(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_setprinter(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_enumprinters(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_addprinterex(PyObject *self, PyObject *args, PyObject *kw);
+
+/* The following definitions come from python/py_spoolss_printers_conv.c */
+
+BOOL py_from_DEVICEMODE(PyObject **dict, DEVICEMODE *devmode);
+BOOL py_to_DEVICEMODE(DEVICEMODE *devmode, PyObject *dict);
+BOOL py_from_PRINTER_INFO_0(PyObject **dict, PRINTER_INFO_0 *info);
+BOOL py_to_PRINTER_INFO_0(PRINTER_INFO_0 *info, PyObject *dict);
+BOOL py_from_PRINTER_INFO_1(PyObject **dict, PRINTER_INFO_1 *info);
+BOOL py_to_PRINTER_INFO_1(PRINTER_INFO_1 *info, PyObject *dict);
+BOOL py_from_PRINTER_INFO_2(PyObject **dict, PRINTER_INFO_2 *info);
+BOOL py_to_PRINTER_INFO_2(PRINTER_INFO_2 *info, PyObject *dict,
+ TALLOC_CTX *mem_ctx);
+BOOL py_from_PRINTER_INFO_3(PyObject **dict, PRINTER_INFO_3 *info);
+BOOL py_to_PRINTER_INFO_3(PRINTER_INFO_3 *info, PyObject *dict,
+ TALLOC_CTX *mem_ctx);
+
+#endif /* _PY_SPOOLSS_H */
diff --git a/source/python/py_spoolss_common.c b/source/python/py_spoolss_common.c
new file mode 100644
index 00000000000..f34d2ac6c7e
--- /dev/null
+++ b/source/python/py_spoolss_common.c
@@ -0,0 +1,35 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "python/py_spoolss.h"
+
+PyObject *new_spoolss_policy_hnd_object(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, POLICY_HND *pol)
+{
+ spoolss_policy_hnd_object *o;
+
+ o = PyObject_New(spoolss_policy_hnd_object, &spoolss_policy_hnd_type);
+
+ o->cli = cli;
+ o->mem_ctx = mem_ctx;
+ memcpy(&o->pol, pol, sizeof(POLICY_HND));
+
+ return (PyObject*)o;
+}
diff --git a/source/python/py_spoolss_drivers.c b/source/python/py_spoolss_drivers.c
new file mode 100644
index 00000000000..12190519ecc
--- /dev/null
+++ b/source/python/py_spoolss_drivers.c
@@ -0,0 +1,422 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "python/py_spoolss.h"
+
+/* Enumerate printer drivers */
+
+PyObject *spoolss_enumprinterdrivers(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ WERROR werror;
+ PyObject *result = NULL, *creds = NULL;
+ PRINTER_DRIVER_CTR ctr;
+ int level = 1, i;
+ uint32 needed, num_drivers;
+ char *arch = "Windows NT x86", *server, *errstr;
+ static char *kwlist[] = {"server", "level", "creds", "arch", NULL};
+ struct cli_state *cli = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "s|iOs", kwlist, &server, &level, &creds,
+ &arch))
+ return NULL;
+
+ if (server[0] != '\\' || server[1] != '\\') {
+ PyErr_SetString(PyExc_ValueError, "UNC name required");
+ return NULL;
+ }
+
+ server += 2;
+
+ if (creds && creds != Py_None && !PyDict_Check(creds)) {
+ PyErr_SetString(PyExc_TypeError,
+ "credentials must be dictionary or None");
+ return NULL;
+ }
+
+ /* Call rpc function */
+
+ if (!(cli = open_pipe_creds(server, creds, PI_SPOOLSS, &errstr))) {
+ PyErr_SetString(spoolss_error, errstr);
+ free(errstr);
+ goto done;
+ }
+
+ if (!(mem_ctx = talloc_init("spoolss_enumprinterdrivers"))) {
+ PyErr_SetString(
+ spoolss_error, "unable to init talloc context\n");
+ goto done;
+ }
+
+ werror = cli_spoolss_enumprinterdrivers(
+ cli, mem_ctx, 0, &needed, level, arch,
+ &num_drivers, &ctr);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+ werror = cli_spoolss_enumprinterdrivers(
+ cli, mem_ctx, needed, NULL, level, arch,
+ &num_drivers, &ctr);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ goto done;
+ }
+
+ /* Return value */
+
+ switch (level) {
+ case 1:
+ result = PyDict_New();
+
+ for (i = 0; i < num_drivers; i++) {
+ PyObject *value;
+ fstring name;
+
+ rpcstr_pull(name, ctr.info1[i].name.buffer,
+ sizeof(fstring), -1, STR_TERMINATE);
+
+ py_from_DRIVER_INFO_1(&value, &ctr.info1[i]);
+
+ PyDict_SetItemString(result, name, value);
+ }
+
+ break;
+ case 2:
+ result = PyDict_New();
+
+ for(i = 0; i < num_drivers; i++) {
+ PyObject *value;
+ fstring name;
+
+ rpcstr_pull(name, ctr.info2[i].name.buffer,
+ sizeof(fstring), -1, STR_TERMINATE);
+
+ py_from_DRIVER_INFO_2(&value, &ctr.info2[i]);
+
+ PyDict_SetItemString(result, name, value);
+ }
+
+ break;
+ case 3:
+ result = PyDict_New();
+
+ for(i = 0; i < num_drivers; i++) {
+ PyObject *value;
+ fstring name;
+
+ rpcstr_pull(name, ctr.info3[i].name.buffer,
+ sizeof(fstring), -1, STR_TERMINATE);
+
+ py_from_DRIVER_INFO_3(&value, &ctr.info3[i]);
+
+ PyDict_SetItemString(result, name, value);
+ }
+
+ break;
+ case 6:
+ result = PyDict_New();
+
+ for(i = 0; i < num_drivers; i++) {
+ PyObject *value;
+ fstring name;
+
+ rpcstr_pull(name, ctr.info6[i].name.buffer,
+ sizeof(fstring), -1, STR_TERMINATE);
+
+ py_from_DRIVER_INFO_6(&value, &ctr.info6[i]);
+
+ PyList_SetItem(result, i, value);
+ }
+
+ break;
+ default:
+ PyErr_SetString(spoolss_error, "unknown info level");
+ goto done;
+ }
+
+ done:
+ if (cli)
+ cli_shutdown(cli);
+
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+/* Fetch printer driver */
+
+PyObject *spoolss_hnd_getprinterdriver(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ PyObject *result = Py_None;
+ PRINTER_DRIVER_CTR ctr;
+ int level = 1;
+ uint32 needed;
+ char *arch = "Windows NT x86";
+ int version = 2;
+ static char *kwlist[] = {"level", "arch", NULL};
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "|is", kwlist, &level, &arch))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_getprinterdriver(
+ hnd->cli, hnd->mem_ctx, 0, &needed, &hnd->pol, level,
+ version, arch, &ctr);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+ werror = cli_spoolss_getprinterdriver(
+ hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol,
+ level, version, arch, &ctr);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ /* Return value */
+
+ switch (level) {
+ case 1:
+ py_from_DRIVER_INFO_1(&result, ctr.info1);
+ break;
+ case 2:
+ py_from_DRIVER_INFO_2(&result, ctr.info2);
+ break;
+ case 3:
+ py_from_DRIVER_INFO_3(&result, ctr.info3);
+ break;
+ case 6:
+ py_from_DRIVER_INFO_6(&result, ctr.info6);
+ break;
+ default:
+ PyErr_SetString(spoolss_error, "unsupported info level");
+ return NULL;
+ }
+
+ Py_INCREF(result);
+ return result;
+}
+
+/* Fetch printer driver directory */
+
+PyObject *spoolss_getprinterdriverdir(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ WERROR werror;
+ PyObject *result = NULL, *creds = NULL;
+ DRIVER_DIRECTORY_CTR ctr;
+ uint32 needed, level = 1;
+ char *arch = "Windows NT x86", *server, *errstr;
+ static char *kwlist[] = {"server", "level", "arch", "creds", NULL};
+ struct cli_state *cli = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "s|isO", kwlist, &server, &level,
+ &arch, &creds))
+ return NULL;
+
+ if (server[0] != '\\' || server[1] != '\\') {
+ PyErr_SetString(PyExc_ValueError, "UNC name required");
+ return NULL;
+ }
+
+ server += 2;
+
+ if (creds && creds != Py_None && !PyDict_Check(creds)) {
+ PyErr_SetString(PyExc_TypeError,
+ "credentials must be dictionary or None");
+ return NULL;
+ }
+
+ /* Call rpc function */
+
+ if (!(cli = open_pipe_creds(server, creds, PI_SPOOLSS, &errstr))) {
+ PyErr_SetString(spoolss_error, errstr);
+ free(errstr);
+ goto done;
+ }
+
+ if (!(mem_ctx = talloc_init("spoolss_getprinterdriverdir"))) {
+ PyErr_SetString(
+ spoolss_error, "unable to init talloc context\n");
+ goto done;
+ }
+
+ werror = cli_spoolss_getprinterdriverdir(
+ cli, mem_ctx, 0, &needed, level, arch, &ctr);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+ werror = cli_spoolss_getprinterdriverdir(
+ cli, mem_ctx, needed, NULL, level, arch, &ctr);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ goto done;
+ }
+
+ /* Return value */
+
+ switch (level) {
+ case 1:
+ py_from_DRIVER_DIRECTORY_1(&result, ctr.info1);
+ break;
+ default:
+ PyErr_SetString(spoolss_error, "unknown info level");
+ goto done;
+ }
+
+ done:
+ if (cli)
+ cli_shutdown(cli);
+
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+PyObject *spoolss_addprinterdriver(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ static char *kwlist[] = { "server", "info", "creds", NULL };
+ char *server, *errstr;
+ uint32 level;
+ PyObject *info, *result = NULL, *creds = NULL;
+ WERROR werror;
+ TALLOC_CTX *mem_ctx = NULL;
+ struct cli_state *cli = NULL;
+ PRINTER_DRIVER_CTR ctr;
+ union {
+ DRIVER_INFO_3 driver_3;
+ } dinfo;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "sO!|O", kwlist, &server, &PyDict_Type,
+ &info, &creds))
+ return NULL;
+
+ if (server[0] == '\\' || server[1] == '\\')
+ server += 2;
+
+ if (creds && creds != Py_None && !PyDict_Check(creds)) {
+ PyErr_SetString(PyExc_TypeError,
+ "credentials must be dictionary or None");
+ return NULL;
+ }
+
+ if (!(mem_ctx = talloc_init("spoolss_addprinterdriver"))) {
+ PyErr_SetString(
+ spoolss_error, "unable to init talloc context\n");
+ return NULL;
+ }
+
+ if (!(cli = open_pipe_creds(server, creds, PI_SPOOLSS, &errstr))) {
+ PyErr_SetString(spoolss_error, errstr);
+ free(errstr);
+ goto done;
+ }
+
+ if (!get_level_value(info, &level)) {
+ PyErr_SetString(spoolss_error, "invalid info level");
+ goto done;
+ }
+
+ if (level != 3) {
+ PyErr_SetString(spoolss_error, "unsupported info level");
+ goto done;
+ }
+
+ ZERO_STRUCT(ctr);
+ ZERO_STRUCT(dinfo);
+
+ switch(level) {
+ case 3:
+ ctr.info3 = &dinfo.driver_3;
+
+ if (!py_to_DRIVER_INFO_3(&dinfo.driver_3, info)) {
+ PyErr_SetString(spoolss_error,
+ "error converting to driver info 3");
+ goto done;
+ }
+
+ break;
+ default:
+ PyErr_SetString(spoolss_error, "unsupported info level");
+ goto done;
+ }
+
+ werror = cli_spoolss_addprinterdriver(cli, mem_ctx, level, &ctr);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ goto done;
+ }
+
+ Py_INCREF(Py_None);
+ result = Py_None;
+
+done:
+ if (cli)
+ cli_shutdown(cli);
+
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+
+ return result;
+
+}
+
+PyObject *spoolss_addprinterdriverex(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ /* Not supported by Samba server */
+
+ PyErr_SetString(spoolss_error, "Not implemented");
+ return NULL;
+}
+
+PyObject *spoolss_deleteprinterdriver(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ PyErr_SetString(spoolss_error, "Not implemented");
+ return NULL;
+}
+
+PyObject *spoolss_deleteprinterdriverex(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ PyErr_SetString(spoolss_error, "Not implemented");
+ return NULL;
+}
diff --git a/source/python/py_spoolss_drivers_conv.c b/source/python/py_spoolss_drivers_conv.c
new file mode 100644
index 00000000000..9bc84080529
--- /dev/null
+++ b/source/python/py_spoolss_drivers_conv.c
@@ -0,0 +1,179 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "python/py_spoolss.h"
+#include "python/py_conv.h"
+
+/* Structure/hash conversions */
+
+struct pyconv py_DRIVER_INFO_1[] = {
+ { "name", PY_UNISTR, offsetof(DRIVER_INFO_1, name) },
+ { NULL }
+};
+
+struct pyconv py_DRIVER_INFO_2[] = {
+ { "version", PY_UINT32, offsetof(DRIVER_INFO_2, version) },
+ { "name", PY_UNISTR, offsetof(DRIVER_INFO_2, name) },
+ { "architecture", PY_UNISTR, offsetof(DRIVER_INFO_2, architecture) },
+ { "driver_path", PY_UNISTR, offsetof(DRIVER_INFO_2, driverpath) },
+ { "data_file", PY_UNISTR, offsetof(DRIVER_INFO_2, datafile) },
+ { "config_file", PY_UNISTR, offsetof(DRIVER_INFO_2, configfile) },
+ { NULL }
+};
+
+struct pyconv py_DRIVER_INFO_3[] = {
+ { "version", PY_UINT32, offsetof(DRIVER_INFO_3, version) },
+ { "name", PY_UNISTR, offsetof(DRIVER_INFO_3, name) },
+ { "architecture", PY_UNISTR, offsetof(DRIVER_INFO_3, architecture) },
+ { "driver_path", PY_UNISTR, offsetof(DRIVER_INFO_3, driverpath) },
+ { "data_file", PY_UNISTR, offsetof(DRIVER_INFO_3, datafile) },
+ { "config_file", PY_UNISTR, offsetof(DRIVER_INFO_3, configfile) },
+ { "help_file", PY_UNISTR, offsetof(DRIVER_INFO_3, helpfile) },
+ { "monitor_name", PY_UNISTR, offsetof(DRIVER_INFO_3, monitorname) },
+ { "default_datatype", PY_UNISTR, offsetof(DRIVER_INFO_3, defaultdatatype) },
+ { NULL }
+};
+
+struct pyconv py_DRIVER_INFO_6[] = {
+ { "version", PY_UINT32, offsetof(DRIVER_INFO_6, version) },
+ { "name", PY_UNISTR, offsetof(DRIVER_INFO_6, name) },
+ { "architecture", PY_UNISTR, offsetof(DRIVER_INFO_6, architecture) },
+ { "driver_path", PY_UNISTR, offsetof(DRIVER_INFO_6, driverpath) },
+ { "data_file", PY_UNISTR, offsetof(DRIVER_INFO_6, datafile) },
+ { "config_file", PY_UNISTR, offsetof(DRIVER_INFO_6, configfile) },
+ { "help_file", PY_UNISTR, offsetof(DRIVER_INFO_6, helpfile) },
+ { "monitor_name", PY_UNISTR, offsetof(DRIVER_INFO_6, monitorname) },
+ { "default_datatype", PY_UNISTR, offsetof(DRIVER_INFO_6, defaultdatatype) },
+ /* driver_date */
+ { "padding", PY_UINT32, offsetof(DRIVER_INFO_6, padding) },
+ { "driver_version_low", PY_UINT32, offsetof(DRIVER_INFO_6, driver_version_low) },
+ { "driver_version_high", PY_UINT32, offsetof(DRIVER_INFO_6, driver_version_high) },
+ { "mfg_name", PY_UNISTR, offsetof(DRIVER_INFO_6, mfgname) },
+ { "oem_url", PY_UNISTR, offsetof(DRIVER_INFO_6, oem_url) },
+ { "hardware_id", PY_UNISTR, offsetof(DRIVER_INFO_6, hardware_id) },
+ { "provider", PY_UNISTR, offsetof(DRIVER_INFO_6, provider) },
+
+ { NULL }
+};
+
+struct pyconv py_DRIVER_DIRECTORY_1[] = {
+ { "name", PY_UNISTR, offsetof(DRIVER_DIRECTORY_1, name) },
+ { NULL }
+};
+
+static uint16 *to_dependentfiles(PyObject *dict)
+{
+ return (uint16 *)"abcd\0";
+}
+
+BOOL py_from_DRIVER_INFO_1(PyObject **dict, DRIVER_INFO_1 *info)
+{
+ *dict = from_struct(info, py_DRIVER_INFO_1);
+ PyDict_SetItemString(*dict, "level", PyInt_FromLong(1));
+
+ return True;
+}
+
+BOOL py_to_DRIVER_INFO_1(DRIVER_INFO_1 *info, PyObject *dict)
+{
+ return False;
+}
+
+BOOL py_from_DRIVER_INFO_2(PyObject **dict, DRIVER_INFO_2 *info)
+{
+ *dict = from_struct(info, py_DRIVER_INFO_2);
+ PyDict_SetItemString(*dict, "level", PyInt_FromLong(2));
+
+ return True;
+}
+
+BOOL py_to_DRIVER_INFO_2(DRIVER_INFO_2 *info, PyObject *dict)
+{
+ return False;
+}
+
+BOOL py_from_DRIVER_INFO_3(PyObject **dict, DRIVER_INFO_3 *info)
+{
+ *dict = from_struct(info, py_DRIVER_INFO_3);
+
+ PyDict_SetItemString(*dict, "level", PyInt_FromLong(3));
+
+ PyDict_SetItemString(
+ *dict, "dependent_files",
+ from_unistr_list(info->dependentfiles));
+
+ return True;
+}
+
+BOOL py_to_DRIVER_INFO_3(DRIVER_INFO_3 *info, PyObject *dict)
+{
+ PyObject *obj, *dict_copy = PyDict_Copy(dict);
+ BOOL result = False;
+
+ if (!(obj = PyDict_GetItemString(dict_copy, "dependent_files")) ||
+ !PyList_Check(obj))
+ goto done;
+
+ info->dependentfiles = to_dependentfiles(obj);
+
+ PyDict_DelItemString(dict_copy, "dependent_files");
+
+ if (!(obj = PyDict_GetItemString(dict_copy, "level")) ||
+ !PyInt_Check(obj))
+ goto done;
+
+ PyDict_DelItemString(dict_copy, "level");
+
+ if (!to_struct(info, dict_copy, py_DRIVER_INFO_3))
+ goto done;
+
+ result = True;
+
+done:
+ Py_DECREF(dict_copy);
+ return result;
+}
+
+BOOL py_from_DRIVER_INFO_6(PyObject **dict, DRIVER_INFO_6 *info)
+{
+ *dict = from_struct(info, py_DRIVER_INFO_6);
+ PyDict_SetItemString(*dict, "level", PyInt_FromLong(6));
+ PyDict_SetItemString(
+ *dict, "dependent_files",
+ from_unistr_list(info->dependentfiles));
+ return True;
+}
+
+BOOL py_to_DRIVER_INFO_6(DRIVER_INFO_6 *info, PyObject *dict)
+{
+ return False;
+}
+
+BOOL py_from_DRIVER_DIRECTORY_1(PyObject **dict, DRIVER_DIRECTORY_1 *info)
+{
+ *dict = from_struct(info, py_DRIVER_DIRECTORY_1);
+ PyDict_SetItemString(*dict, "level", PyInt_FromLong(1));
+ return True;
+}
+
+BOOL py_to_DRIVER_DIRECTORY_1(DRIVER_DIRECTORY_1 *info, PyObject *dict)
+{
+ return False;
+}
diff --git a/source/python/py_spoolss_forms.c b/source/python/py_spoolss_forms.c
new file mode 100644
index 00000000000..66a6540e074
--- /dev/null
+++ b/source/python/py_spoolss_forms.c
@@ -0,0 +1,266 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "python/py_spoolss.h"
+
+/* Add a form */
+
+PyObject *spoolss_hnd_addform(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ PyObject *info;
+ FORM form;
+ int level;
+ static char *kwlist[] = {"form", NULL};
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "O!", kwlist, &PyDict_Type, &info))
+ return NULL;
+
+ /* Call rpc function */
+
+ if (!py_to_FORM(&form, info)) {
+ PyErr_SetString(spoolss_error, "invalid form");
+ return NULL;
+ }
+
+ if (!get_level_value(info, &level)) {
+ PyErr_SetString(spoolss_error, "invalid info level");
+ return NULL;
+ }
+
+ if (level != 1) {
+ PyErr_SetString(spoolss_error, "unsupported info level");
+ return NULL;
+ }
+
+ switch (level) {
+ case 1: {
+ PyObject *obj = PyDict_GetItemString(info, "name");
+ char *form_name = PyString_AsString(obj);
+
+ init_unistr2(&form.name, form_name, UNI_STR_TERMINATE);
+ break;
+ }
+ default:
+ PyErr_SetString(spoolss_error, "unsupported info level");
+ return NULL;
+ }
+
+ werror = cli_spoolss_addform(hnd->cli, hnd->mem_ctx, &hnd->pol,
+ level, &form);
+
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* Get form properties */
+
+PyObject *spoolss_hnd_getform(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ PyObject *result;
+ char *form_name;
+ int level = 1;
+ static char *kwlist[] = {"form_name", "level", NULL};
+ uint32 needed;
+ FORM_1 form;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "s|i", kwlist, &form_name, &level))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_getform(hnd->cli, hnd->mem_ctx, 0, &needed,
+ &hnd->pol, form_name, level, &form);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+ werror = cli_spoolss_getform(
+ hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol,
+ form_name, 1, &form);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ result = Py_None;
+
+ switch(level) {
+ case 1:
+ py_from_FORM_1(&result, &form);
+ break;
+ }
+
+ Py_INCREF(result);
+ return result;
+}
+
+/* Set form properties */
+
+PyObject *spoolss_hnd_setform(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ PyObject *info, *form_name;
+ int level;
+ static char *kwlist[] = { "form", NULL};
+ FORM form;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "O!", kwlist, &PyDict_Type, &info))
+ return NULL;
+
+ if (!get_level_value(info, &level)) {
+ PyErr_SetString(spoolss_error, "invalid info level");
+ return NULL;
+ }
+
+ if (level != 1) {
+ PyErr_SetString(spoolss_error, "unsupported info level");
+ return NULL;
+ }
+
+ /* Call rpc function */
+
+ if (!py_to_FORM(&form, info)) {
+ PyErr_SetString(spoolss_error, "invalid form");
+ return NULL;
+ }
+
+ form_name = PyDict_GetItemString(info, "name");
+
+ werror = cli_spoolss_setform(
+ hnd->cli, hnd->mem_ctx, &hnd->pol, level,
+ PyString_AsString(form_name), &form);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* Delete a form */
+
+PyObject *spoolss_hnd_deleteform(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ static char *kwlist[] = {"form_name", NULL};
+ char *form_name;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "s", kwlist, &form_name))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_deleteform(
+ hnd->cli, hnd->mem_ctx, &hnd->pol, form_name);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* Enumerate forms */
+
+PyObject *spoolss_hnd_enumforms(PyObject *self, PyObject *args, PyObject *kw)
+{
+ PyObject *result;
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ uint32 level = 1, num_forms, needed, i;
+ static char *kwlist[] = {"level", NULL};
+ FORM_1 *forms;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "|i", kwlist, &level))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_enumforms(
+ hnd->cli, hnd->mem_ctx, 0, &needed, &hnd->pol, level,
+ &num_forms, &forms);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+ werror = cli_spoolss_enumforms(
+ hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol, level,
+ &num_forms, &forms);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ switch(level) {
+ case 1:
+ result = PyDict_New();
+
+ for (i = 0; i < num_forms; i++) {
+ PyObject *value;
+ fstring name;
+
+ rpcstr_pull(name, forms[i].name.buffer,
+ sizeof(fstring), -1, STR_TERMINATE);
+
+ py_from_FORM_1(&value, &forms[i]);
+
+ PyDict_SetItemString(
+ value, "level", PyInt_FromLong(1));
+
+ PyDict_SetItemString(result, name, value);
+ }
+
+ break;
+ default:
+ PyErr_SetString(spoolss_error, "unknown info level");
+ return NULL;
+ }
+
+ return result;
+}
diff --git a/source/python/py_spoolss_forms_conv.c b/source/python/py_spoolss_forms_conv.c
new file mode 100644
index 00000000000..ede729cad33
--- /dev/null
+++ b/source/python/py_spoolss_forms_conv.c
@@ -0,0 +1,91 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "python/py_spoolss.h"
+#include "python/py_conv.h"
+
+struct pyconv py_FORM[] = {
+ { "flags", PY_UINT32, offsetof(FORM, flags) },
+ { "width", PY_UINT32, offsetof(FORM, size_x) },
+ { "length", PY_UINT32, offsetof(FORM, size_y) },
+ { "top", PY_UINT32, offsetof(FORM, top) },
+ { "left", PY_UINT32, offsetof(FORM, left) },
+ { "right", PY_UINT32, offsetof(FORM, right) },
+ { "bottom", PY_UINT32, offsetof(FORM, bottom) },
+ { NULL }
+};
+
+struct pyconv py_FORM_1[] = {
+ { "flags", PY_UINT32, offsetof(FORM_1, flag) },
+ { "width", PY_UINT32, offsetof(FORM_1, width) },
+ { "length", PY_UINT32, offsetof(FORM_1, length) },
+ { "top", PY_UINT32, offsetof(FORM_1, top) },
+ { "left", PY_UINT32, offsetof(FORM_1, left) },
+ { "right", PY_UINT32, offsetof(FORM_1, right) },
+ { "bottom", PY_UINT32, offsetof(FORM_1, bottom) },
+ { "name", PY_UNISTR, offsetof(FORM_1, name) },
+ { NULL }
+};
+
+BOOL py_from_FORM_1(PyObject **dict, FORM_1 *form)
+{
+ *dict = from_struct(form, py_FORM_1);
+
+ PyDict_SetItemString(*dict, "level", PyInt_FromLong(1));
+
+ return True;
+}
+
+BOOL py_to_FORM(FORM *form, PyObject *dict)
+{
+ PyObject *obj, *dict_copy = PyDict_Copy(dict);
+ char *name;
+ BOOL result = False;
+
+ if (!(obj = PyDict_GetItemString(dict_copy, "name")) ||
+ !PyString_Check(obj))
+ goto done;
+
+ PyDict_DelItemString(dict_copy, "name");
+
+ if (!(obj = PyDict_GetItemString(dict_copy, "level")) ||
+ !PyInt_Check(obj))
+ goto done;
+
+ PyDict_DelItemString(dict_copy, "level");
+
+ if (!to_struct(form, dict_copy, py_FORM))
+ goto done;
+
+ /* Careful! We can't call PyString_AsString(obj) then delete
+ obj and still expect to have our pointer pointing somewhere
+ useful. */
+
+ obj = PyDict_GetItemString(dict, "name");
+ name = PyString_AsString(obj);
+
+ init_unistr2(&form->name, name, UNI_STR_TERMINATE);
+
+ result = True;
+
+done:
+ Py_DECREF(dict_copy);
+ return result;
+}
diff --git a/source/python/py_spoolss_jobs.c b/source/python/py_spoolss_jobs.c
new file mode 100644
index 00000000000..59754bd36dd
--- /dev/null
+++ b/source/python/py_spoolss_jobs.c
@@ -0,0 +1,377 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "python/py_spoolss.h"
+
+/* Enumerate jobs */
+
+PyObject *spoolss_hnd_enumjobs(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ PyObject *result;
+ int level = 1;
+ uint32 i, needed, num_jobs;
+ static char *kwlist[] = {"level", NULL};
+ JOB_INFO_CTR ctr;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "|i", kwlist, &level))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_enumjobs(
+ hnd->cli, hnd->mem_ctx, 0, &needed, &hnd->pol, level, 0,
+ 1000, &num_jobs, &ctr);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+ werror = cli_spoolss_enumjobs(
+ hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol,
+ level, 0, 1000, &num_jobs, &ctr);
+
+ /* Return value */
+
+ result = Py_None;
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ goto done;
+ }
+
+ result = PyList_New(num_jobs);
+
+ switch (level) {
+ case 1:
+ for (i = 0; i < num_jobs; i++) {
+ PyObject *value;
+
+ py_from_JOB_INFO_1(&value, &ctr.job.job_info_1[i]);
+
+ PyList_SetItem(result, i, value);
+ }
+
+ break;
+ case 2:
+ for(i = 0; i < num_jobs; i++) {
+ PyObject *value;
+
+ py_from_JOB_INFO_2(&value, &ctr.job.job_info_2[i]);
+
+ PyList_SetItem(result, i, value);
+ }
+
+ break;
+ }
+
+ done:
+ Py_INCREF(result);
+ return result;
+}
+
+/* Set job command */
+
+PyObject *spoolss_hnd_setjob(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ uint32 level = 0, command, jobid;
+ static char *kwlist[] = {"jobid", "command", "level", NULL};
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "ii|i", kwlist, &jobid, &command, &level))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_setjob(hnd->cli, hnd->mem_ctx, &hnd->pol,
+ jobid, level, command);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* Get job */
+
+PyObject *spoolss_hnd_getjob(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ PyObject *result;
+ uint32 level = 1, jobid, needed;
+ static char *kwlist[] = {"jobid", "level", NULL};
+ JOB_INFO_CTR ctr;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "i|i", kwlist, &jobid, &level))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_getjob(hnd->cli, hnd->mem_ctx, 0, &needed,
+ &hnd->pol, jobid, level, &ctr);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+ werror = cli_spoolss_getjob(
+ hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol,
+ jobid, level, &ctr);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ switch(level) {
+ case 1:
+ py_from_JOB_INFO_1(&result, &ctr.job.job_info_1[0]);
+ break;
+ case 2:
+ py_from_JOB_INFO_2(&result, &ctr.job.job_info_2[0]);
+ break;
+ }
+
+ return result;
+}
+
+/* Start page printer. This notifies the spooler that a page is about to be
+ printed on the specified printer. */
+
+PyObject *spoolss_hnd_startpageprinter(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ static char *kwlist[] = { NULL };
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "", kwlist))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_startpageprinter(
+ hnd->cli, hnd->mem_ctx, &hnd->pol);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* End page printer. This notifies the spooler that a page has finished
+ being printed on the specified printer. */
+
+PyObject *spoolss_hnd_endpageprinter(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ static char *kwlist[] = { NULL };
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "", kwlist))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_endpageprinter(
+ hnd->cli, hnd->mem_ctx, &hnd->pol);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* Start doc printer. This notifies the spooler that a document is about to be
+ printed on the specified printer. */
+
+PyObject *spoolss_hnd_startdocprinter(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ static char *kwlist[] = { "document_info", NULL };
+ PyObject *info, *obj;
+ uint32 level, jobid;
+ char *document_name = NULL, *output_file = NULL, *data_type = NULL;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "O!", kwlist, &PyDict_Type, &info))
+ return NULL;
+
+ /* Check document_info parameter */
+
+ if (!get_level_value(info, &level)) {
+ PyErr_SetString(spoolss_error, "invalid info level");
+ return NULL;
+ }
+
+ if (level != 1) {
+ PyErr_SetString(spoolss_error, "unsupported info level");
+ return NULL;
+ }
+
+ if ((obj = PyDict_GetItemString(info, "document_name"))) {
+
+ if (!PyString_Check(obj) && obj != Py_None) {
+ PyErr_SetString(spoolss_error,
+ "document_name not a string");
+ return NULL;
+ }
+
+ if (PyString_Check(obj))
+ document_name = PyString_AsString(obj);
+
+ } else {
+ PyErr_SetString(spoolss_error, "no document_name present");
+ return NULL;
+ }
+
+ if ((obj = PyDict_GetItemString(info, "output_file"))) {
+
+ if (!PyString_Check(obj) && obj != Py_None) {
+ PyErr_SetString(spoolss_error,
+ "output_file not a string");
+ return NULL;
+ }
+
+ if (PyString_Check(obj))
+ output_file = PyString_AsString(obj);
+
+ } else {
+ PyErr_SetString(spoolss_error, "no output_file present");
+ return NULL;
+ }
+
+ if ((obj = PyDict_GetItemString(info, "data_type"))) {
+
+ if (!PyString_Check(obj) && obj != Py_None) {
+ PyErr_SetString(spoolss_error,
+ "data_type not a string");
+ return NULL;
+ }
+
+ if (PyString_Check(obj))
+ data_type = PyString_AsString(obj);
+
+ } else {
+ PyErr_SetString(spoolss_error, "no data_type present");
+ return NULL;
+ }
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_startdocprinter(
+ hnd->cli, hnd->mem_ctx, &hnd->pol, document_name,
+ output_file, data_type, &jobid);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ /* The return value is zero for an error (where does the status
+ code come from now??) and the return value is the jobid
+ allocated for the new job. */
+
+ return Py_BuildValue("i", jobid);
+}
+
+/* End doc printer. This notifies the spooler that a document has finished
+ being printed on the specified printer. */
+
+PyObject *spoolss_hnd_enddocprinter(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ static char *kwlist[] = { NULL };
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "", kwlist))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_enddocprinter(hnd->cli, hnd->mem_ctx, &hnd->pol);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* Write data to a printer */
+
+PyObject *spoolss_hnd_writeprinter(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ static char *kwlist[] = { "data", NULL };
+ PyObject *data;
+ uint32 num_written;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "O!", kwlist, &PyString_Type, &data))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_writeprinter(
+ hnd->cli, hnd->mem_ctx, &hnd->pol, PyString_Size(data),
+ PyString_AsString(data), &num_written);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyObject *spoolss_hnd_addjob(PyObject *self, PyObject *args, PyObject *kw)
+{
+ PyErr_SetString(spoolss_error, "Not implemented");
+ return NULL;
+}
diff --git a/source/python/py_spoolss_jobs_conv.c b/source/python/py_spoolss_jobs_conv.c
new file mode 100644
index 00000000000..cb04ec6713c
--- /dev/null
+++ b/source/python/py_spoolss_jobs_conv.c
@@ -0,0 +1,102 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "python/py_spoolss.h"
+#include "python/py_conv.h"
+
+struct pyconv py_JOB_INFO_1[] = {
+ { "jobid", PY_UINT32, offsetof(JOB_INFO_1, jobid) },
+ { "printer_name", PY_UNISTR, offsetof(JOB_INFO_1, printername) },
+ { "server_name", PY_UNISTR, offsetof(JOB_INFO_1, machinename) },
+ { "user_name", PY_UNISTR, offsetof(JOB_INFO_1, username) },
+ { "document_name", PY_UNISTR, offsetof(JOB_INFO_1, document) },
+ { "data_type", PY_UNISTR, offsetof(JOB_INFO_1, datatype) },
+ { "text_status", PY_UNISTR, offsetof(JOB_INFO_1, text_status) },
+ { "status", PY_UINT32, offsetof(JOB_INFO_1, status) },
+ { "priority", PY_UINT32, offsetof(JOB_INFO_1, priority) },
+ { "position", PY_UINT32, offsetof(JOB_INFO_1, position) },
+ { "total_pages", PY_UINT32, offsetof(JOB_INFO_1, totalpages) },
+ { "pages_printed", PY_UINT32, offsetof(JOB_INFO_1, pagesprinted) },
+ { NULL }
+};
+
+struct pyconv py_JOB_INFO_2[] = {
+ { "jobid", PY_UINT32, offsetof(JOB_INFO_2, jobid) },
+ { "printer_name", PY_UNISTR, offsetof(JOB_INFO_2, printername) },
+ { "server_name", PY_UNISTR, offsetof(JOB_INFO_2, machinename) },
+ { "user_name", PY_UNISTR, offsetof(JOB_INFO_2, username) },
+ { "document_name", PY_UNISTR, offsetof(JOB_INFO_2, document) },
+ { "notify_name", PY_UNISTR, offsetof(JOB_INFO_2, notifyname) },
+ { "data_type", PY_UNISTR, offsetof(JOB_INFO_2, datatype) },
+ { "print_processor", PY_UNISTR, offsetof(JOB_INFO_2, printprocessor) },
+ { "parameters", PY_UNISTR, offsetof(JOB_INFO_2, parameters) },
+ { "driver_name", PY_UNISTR, offsetof(JOB_INFO_2, drivername) },
+ { "text_status", PY_UNISTR, offsetof(JOB_INFO_2, text_status) },
+ { "status", PY_UINT32, offsetof(JOB_INFO_2, status) },
+ { "priority", PY_UINT32, offsetof(JOB_INFO_2, priority) },
+ { "position", PY_UINT32, offsetof(JOB_INFO_2, position) },
+ { "start_time", PY_UINT32, offsetof(JOB_INFO_2, starttime) },
+ { "until_time", PY_UINT32, offsetof(JOB_INFO_2, untiltime) },
+ { "total_pages", PY_UINT32, offsetof(JOB_INFO_2, totalpages) },
+ { "size", PY_UINT32, offsetof(JOB_INFO_2, size) },
+ { "time_elapsed", PY_UINT32, offsetof(JOB_INFO_2, timeelapsed) },
+ { "pages_printed", PY_UINT32, offsetof(JOB_INFO_2, pagesprinted) },
+ { NULL }
+};
+
+struct pyconv py_DOC_INFO_1[] = {
+ { "document_name", PY_UNISTR, offsetof(DOC_INFO_1, docname) },
+ { "output_file", PY_UNISTR, offsetof(DOC_INFO_1, outputfile) },
+ { "data_type", PY_UNISTR, offsetof(DOC_INFO_1, datatype) },
+ { NULL }
+};
+
+BOOL py_from_JOB_INFO_1(PyObject **dict, JOB_INFO_1 *info)
+{
+ *dict = from_struct(info, py_JOB_INFO_1);
+ return True;
+}
+
+BOOL py_to_JOB_INFO_1(JOB_INFO_1 *info, PyObject *dict)
+{
+ return False;
+}
+
+BOOL py_from_JOB_INFO_2(PyObject **dict, JOB_INFO_2 *info)
+{
+ *dict = from_struct(info, py_JOB_INFO_2);
+ return True;
+}
+
+BOOL py_to_JOB_INFO_2(JOB_INFO_2 *info, PyObject *dict)
+{
+ return False;
+}
+
+BOOL py_from_DOC_INFO_1(PyObject **dict, DOC_INFO_1 *info)
+{
+ *dict = from_struct(info, py_DOC_INFO_1);
+ return True;
+}
+
+BOOL py_to_DOC_INFO_1(DOC_INFO_1 *info, PyObject *dict)
+{
+ return to_struct(info, dict, py_DOC_INFO_1);
+}
diff --git a/source/python/py_spoolss_ports.c b/source/python/py_spoolss_ports.c
new file mode 100644
index 00000000000..ddc8868f0f5
--- /dev/null
+++ b/source/python/py_spoolss_ports.c
@@ -0,0 +1,137 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "python/py_spoolss.h"
+
+/* Enumerate ports */
+
+PyObject *spoolss_enumports(PyObject *self, PyObject *args, PyObject *kw)
+{
+ WERROR werror;
+ PyObject *result = NULL, *creds = NULL;
+ uint32 level = 1;
+ uint32 i, needed, num_ports;
+ static char *kwlist[] = {"server", "level", "creds", NULL};
+ TALLOC_CTX *mem_ctx = NULL;
+ struct cli_state *cli = NULL;
+ char *server, *errstr;
+ PORT_INFO_CTR ctr;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "s|iO", kwlist, &server, &level, &creds))
+ return NULL;
+
+ if (server[0] != '\\' || server[1] != '\\') {
+ PyErr_SetString(PyExc_ValueError, "UNC name required");
+ return NULL;
+ }
+
+ server += 2;
+
+ if (creds && creds != Py_None && !PyDict_Check(creds)) {
+ PyErr_SetString(PyExc_TypeError,
+ "credentials must be dictionary or None");
+ return NULL;
+ }
+
+ if (!(cli = open_pipe_creds(server, creds, PI_SPOOLSS, &errstr))) {
+ PyErr_SetString(spoolss_error, errstr);
+ free(errstr);
+ goto done;
+ }
+
+ if (!(mem_ctx = talloc_init("spoolss_enumports"))) {
+ PyErr_SetString(
+ spoolss_error, "unable to init talloc context\n");
+ goto done;
+ }
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_enum_ports(
+ cli, mem_ctx, 0, &needed, level, &num_ports, &ctr);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+ werror = cli_spoolss_enum_ports(
+ cli, mem_ctx, needed, NULL, level,
+ &num_ports, &ctr);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ goto done;
+ }
+
+ /* Return value */
+
+ switch (level) {
+ case 1:
+ result = PyDict_New();
+
+ for (i = 0; i < num_ports; i++) {
+ PyObject *value;
+ fstring name;
+
+ rpcstr_pull(name, ctr.port.info_1[i].port_name.buffer,
+ sizeof(fstring), -1, STR_TERMINATE);
+
+ py_from_PORT_INFO_1(&value, &ctr.port.info_1[i]);
+
+ PyDict_SetItemString(
+ value, "level", PyInt_FromLong(1));
+
+ PyDict_SetItemString(result, name, value);
+ }
+
+ break;
+ case 2:
+ result = PyDict_New();
+
+ for(i = 0; i < num_ports; i++) {
+ PyObject *value;
+ fstring name;
+
+ rpcstr_pull(name, ctr.port.info_2[i].port_name.buffer,
+ sizeof(fstring), -1, STR_TERMINATE);
+
+ py_from_PORT_INFO_2(&value, &ctr.port.info_2[i]);
+
+ PyDict_SetItemString(
+ value, "level", PyInt_FromLong(2));
+
+ PyDict_SetItemString(result, name, value);
+ }
+
+ break;
+ default:
+ PyErr_SetString(spoolss_error, "unknown info level");
+ goto done;
+ }
+
+ done:
+ if (cli)
+ cli_shutdown(cli);
+
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
diff --git a/source/python/py_spoolss_ports_conv.c b/source/python/py_spoolss_ports_conv.c
new file mode 100644
index 00000000000..3f6d94bf7e7
--- /dev/null
+++ b/source/python/py_spoolss_ports_conv.c
@@ -0,0 +1,58 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "python/py_spoolss.h"
+#include "python/py_conv.h"
+
+struct pyconv py_PORT_INFO_1[] = {
+ { "name", PY_UNISTR, offsetof(PORT_INFO_1, port_name) },
+ { NULL }
+};
+
+struct pyconv py_PORT_INFO_2[] = {
+ { "name", PY_UNISTR, offsetof(PORT_INFO_2, port_name) },
+ { "monitor_name", PY_UNISTR, offsetof(PORT_INFO_2, monitor_name) },
+ { "description", PY_UNISTR, offsetof(PORT_INFO_2, description) },
+ { "reserved", PY_UINT32, offsetof(PORT_INFO_2, reserved) },
+ { "type", PY_UINT32, offsetof(PORT_INFO_2, port_type) },
+ { NULL }
+};
+
+BOOL py_from_PORT_INFO_1(PyObject **dict, PORT_INFO_1 *info)
+{
+ *dict = from_struct(info, py_PORT_INFO_1);
+ return True;
+}
+
+BOOL py_to_PORT_INFO_1(PORT_INFO_1 *info, PyObject *dict)
+{
+ return False;
+}
+
+BOOL py_from_PORT_INFO_2(PyObject **dict, PORT_INFO_2 *info)
+{
+ *dict = from_struct(info, py_PORT_INFO_2);
+ return True;
+}
+
+BOOL py_to_PORT_INFO_2(PORT_INFO_2 *info, PyObject *dict)
+{
+ return False;
+}
diff --git a/source/python/py_spoolss_printerdata.c b/source/python/py_spoolss_printerdata.c
new file mode 100644
index 00000000000..f165475b080
--- /dev/null
+++ b/source/python/py_spoolss_printerdata.c
@@ -0,0 +1,450 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "python/py_spoolss.h"
+#include "python/py_conv.h"
+
+static BOOL py_from_printerdata(PyObject **dict, char *key, char *value,
+ uint16 data_type, uint8 *data,
+ uint32 data_size)
+{
+ *dict = PyDict_New();
+
+ PyDict_SetItemString(*dict, "key", Py_BuildValue("s", key ? key : ""));
+ PyDict_SetItemString(*dict, "value", Py_BuildValue("s", value));
+ PyDict_SetItemString(*dict, "type", Py_BuildValue("i", data_type));
+
+ PyDict_SetItemString(*dict, "data",
+ Py_BuildValue("s#", data, data_size));
+
+ return True;
+}
+
+static BOOL py_to_printerdata(char **key, char **value, uint16 *data_type,
+ uint8 **data, uint32 *data_size,
+ PyObject *dict)
+{
+ PyObject *obj;
+
+ if ((obj = PyDict_GetItemString(dict, "key"))) {
+
+ if (!PyString_Check(obj)) {
+ PyErr_SetString(spoolss_error,
+ "key not a string");
+ return False;
+ }
+
+ if (key) {
+ *key = PyString_AsString(obj);
+
+ if (!key[0])
+ *key = NULL;
+ }
+ } else
+ *key = NULL;
+
+ if ((obj = PyDict_GetItemString(dict, "value"))) {
+
+ if (!PyString_Check(obj)) {
+ PyErr_SetString(spoolss_error,
+ "value not a string");
+ return False;
+ }
+
+ *value = PyString_AsString(obj);
+ } else {
+ PyErr_SetString(spoolss_error, "no value present");
+ return False;
+ }
+
+ if ((obj = PyDict_GetItemString(dict, "type"))) {
+
+ if (!PyInt_Check(obj)) {
+ PyErr_SetString(spoolss_error,
+ "type not an integer");
+ return False;
+ }
+
+ *data_type = PyInt_AsLong(obj);
+ } else {
+ PyErr_SetString(spoolss_error, "no type present");
+ return False;
+ }
+
+ if ((obj = PyDict_GetItemString(dict, "data"))) {
+
+ if (!PyString_Check(obj)) {
+ PyErr_SetString(spoolss_error,
+ "data not a string");
+ return False;
+ }
+
+ *data = PyString_AsString(obj);
+ *data_size = PyString_Size(obj);
+ } else {
+ PyErr_SetString(spoolss_error, "no data present");
+ return False;
+ }
+
+ return True;
+}
+
+PyObject *spoolss_hnd_getprinterdata(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ static char *kwlist[] = { "value", NULL };
+ char *valuename;
+ WERROR werror;
+ uint32 needed;
+ PyObject *result;
+ REGISTRY_VALUE value;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "s", kwlist, &valuename))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_getprinterdata(
+ hnd->cli, hnd->mem_ctx, 0, &needed, &hnd->pol, valuename,
+ &value);
+
+ if (W_ERROR_V(werror) == ERRmoredata)
+ werror = cli_spoolss_getprinterdata(
+ hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol,
+ valuename, &value);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ py_from_printerdata(
+ &result, NULL, valuename, value.type, value.data_p,
+ value.size);
+
+ return result;
+}
+
+PyObject *spoolss_hnd_setprinterdata(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ static char *kwlist[] = { "data", NULL };
+ PyObject *py_data;
+ char *valuename;
+ WERROR werror;
+ REGISTRY_VALUE value;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "O!", kwlist, &PyDict_Type, &py_data))
+ return NULL;
+
+ if (!py_to_printerdata(
+ NULL, &valuename, &value.type, &value.data_p,
+ &value.size, py_data))
+ return NULL;
+
+ fstrcpy(value.valuename, valuename);
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_setprinterdata(
+ hnd->cli, hnd->mem_ctx, &hnd->pol, &value);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyObject *spoolss_hnd_enumprinterdata(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ static char *kwlist[] = { NULL };
+ uint32 data_needed, value_needed, ndx = 0;
+ WERROR werror;
+ PyObject *result;
+ REGISTRY_VALUE value;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "", kwlist))
+ return NULL;
+
+ /* Get max buffer sizes for value and data */
+
+ werror = cli_spoolss_enumprinterdata(
+ hnd->cli, hnd->mem_ctx, &hnd->pol, ndx, 0, 0,
+ &value_needed, &data_needed, NULL);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ /* Iterate over all printerdata */
+
+ result = PyDict_New();
+
+ while (W_ERROR_IS_OK(werror)) {
+ PyObject *obj;
+
+ werror = cli_spoolss_enumprinterdata(
+ hnd->cli, hnd->mem_ctx, &hnd->pol, ndx,
+ value_needed, data_needed, NULL, NULL, &value);
+
+ if (py_from_printerdata(
+ &obj, NULL, value.valuename, value.type,
+ value.data_p, value.size))
+ PyDict_SetItemString(result, value.valuename, obj);
+
+ ndx++;
+ }
+
+ return result;
+}
+
+PyObject *spoolss_hnd_deleteprinterdata(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ static char *kwlist[] = { "value", NULL };
+ char *value;
+ WERROR werror;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "s", kwlist, &value))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_deleteprinterdata(
+ hnd->cli, hnd->mem_ctx, &hnd->pol, value);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyObject *spoolss_hnd_getprinterdataex(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ static char *kwlist[] = { "key", "value", NULL };
+ char *key, *valuename;
+ WERROR werror;
+ uint32 needed;
+ PyObject *result;
+ REGISTRY_VALUE value;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "ss", kwlist, &key, &valuename))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_getprinterdataex(
+ hnd->cli, hnd->mem_ctx, 0, &needed, &hnd->pol, key,
+ valuename, &value);
+
+ if (W_ERROR_V(werror) == ERRmoredata)
+ werror = cli_spoolss_getprinterdataex(
+ hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol, key,
+ valuename, &value);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ py_from_printerdata(
+ &result, key, valuename, value.type, value.data_p, value.size);
+
+ return result;
+}
+
+PyObject *spoolss_hnd_setprinterdataex(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ static char *kwlist[] = { "data", NULL };
+ PyObject *py_data;
+ char *keyname, *valuename;
+ WERROR werror;
+ REGISTRY_VALUE value;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "O!", kwlist, &PyDict_Type, &py_data))
+ return NULL;
+
+ if (!py_to_printerdata(
+ &keyname, &valuename, &value.type, &value.data_p, &value.size, py_data))
+ return NULL;
+
+ fstrcpy(value.valuename, valuename);
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_setprinterdataex(
+ hnd->cli, hnd->mem_ctx, &hnd->pol, keyname, &value);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyObject *spoolss_hnd_enumprinterdataex(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ static char *kwlist[] = { "key", NULL };
+ uint32 needed, i;
+ char *key;
+ WERROR werror;
+ PyObject *result;
+ REGVAL_CTR ctr;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "s", kwlist, &key))
+ return NULL;
+
+ /* Get max buffer sizes for value and data */
+
+ werror = cli_spoolss_enumprinterdataex(
+ hnd->cli, hnd->mem_ctx, 0, &needed, &hnd->pol, key, &ctr);
+
+ if (W_ERROR_V(werror) == ERRmoredata)
+ werror = cli_spoolss_enumprinterdataex(
+ hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol, key,
+ &ctr);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ /* Iterate over all printerdata */
+
+ result = PyDict_New();
+
+ for (i = 0; i < regval_ctr_numvals(&ctr); i++) {
+ REGISTRY_VALUE *value;
+ PyObject *item;
+
+ item = PyDict_New();
+ value = regval_ctr_specific_value(&ctr, i);
+
+ if (py_from_printerdata(
+ &item, key, value->valuename, value->type,
+ value->data_p, value->size))
+ PyDict_SetItemString(result, value->valuename, item);
+ }
+
+ return result;
+}
+
+PyObject *spoolss_hnd_deleteprinterdataex(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ static char *kwlist[] = { "key", "value", NULL };
+ char *key, *value;
+ WERROR werror;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "ss", kwlist, &key, &value))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_deleteprinterdataex(
+ hnd->cli, hnd->mem_ctx, &hnd->pol, key, value);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyObject *spoolss_hnd_enumprinterkey(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ static char *kwlist[] = { "key", NULL };
+ char *keyname;
+ WERROR werror;
+ uint32 needed, keylist_len;
+ uint16 *keylist;
+ PyObject *result;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "s", kwlist, &keyname))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_enumprinterkey(
+ hnd->cli, hnd->mem_ctx, 0, &needed, &hnd->pol,
+ keyname, &keylist, &keylist_len);
+
+ if (W_ERROR_V(werror) == ERRmoredata)
+ werror = cli_spoolss_enumprinterkey(
+ hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol,
+ keyname, &keylist, &keylist_len);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ result = from_unistr_list(keylist);
+
+ return result;
+}
+
+#if 0
+
+PyObject *spoolss_hnd_deleteprinterkey(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ static char *kwlist[] = { "key", NULL };
+ char *keyname;
+ WERROR werror;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "s", kwlist, &keyname))
+ return NULL;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+#endif
diff --git a/source/python/py_spoolss_printers.c b/source/python/py_spoolss_printers.c
new file mode 100644
index 00000000000..d011681acc2
--- /dev/null
+++ b/source/python/py_spoolss_printers.c
@@ -0,0 +1,475 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "python/py_spoolss.h"
+
+/* Open a printer */
+
+PyObject *spoolss_openprinter(PyObject *self, PyObject *args, PyObject *kw)
+{
+ char *unc_name, *server, *errstr;
+ TALLOC_CTX *mem_ctx = NULL;
+ POLICY_HND hnd;
+ WERROR werror;
+ PyObject *result = NULL, *creds = NULL;
+ static char *kwlist[] = { "printername", "creds", "access", NULL };
+ uint32 desired_access = MAXIMUM_ALLOWED_ACCESS;
+ struct cli_state *cli;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "s|Oi", kwlist, &unc_name, &creds,
+ &desired_access))
+ return NULL;
+
+ if (unc_name[0] != '\\' || unc_name[1] != '\\') {
+ PyErr_SetString(PyExc_ValueError, "UNC name required");
+ return NULL;
+ }
+
+ server = strdup(unc_name + 2);
+
+ if (strchr(server, '\\')) {
+ char *c = strchr(server, '\\');
+ *c = 0;
+ }
+
+ if (creds && creds != Py_None && !PyDict_Check(creds)) {
+ PyErr_SetString(PyExc_TypeError,
+ "credentials must be dictionary or None");
+ return NULL;
+ }
+
+ if (!(cli = open_pipe_creds(server, creds, PI_SPOOLSS, &errstr))) {
+ PyErr_SetString(spoolss_error, errstr);
+ free(errstr);
+ goto done;
+ }
+
+ if (!(mem_ctx = talloc_init("spoolss_openprinter"))) {
+ PyErr_SetString(spoolss_error,
+ "unable to init talloc context\n");
+ goto done;
+ }
+
+ werror = cli_spoolss_open_printer_ex(
+ cli, mem_ctx, unc_name, "", desired_access, server,
+ "", &hnd);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ goto done;
+ }
+
+ result = new_spoolss_policy_hnd_object(cli, mem_ctx, &hnd);
+
+ done:
+ if (!result) {
+ if (cli)
+ cli_shutdown(cli);
+
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+ }
+
+ SAFE_FREE(server);
+
+ return result;
+}
+
+/* Close a printer */
+
+PyObject *spoolss_closeprinter(PyObject *self, PyObject *args)
+{
+ PyObject *po;
+ spoolss_policy_hnd_object *hnd;
+ WERROR result;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTuple(args, "O!", &spoolss_policy_hnd_type, &po))
+ return NULL;
+
+ hnd = (spoolss_policy_hnd_object *)po;
+
+ /* Call rpc function */
+
+ result = cli_spoolss_close_printer(hnd->cli, hnd->mem_ctx, &hnd->pol);
+
+ /* Return value */
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* Fetch printer information */
+
+PyObject *spoolss_hnd_getprinter(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ PyObject *result = NULL;
+ PRINTER_INFO_CTR ctr;
+ int level = 1;
+ uint32 needed;
+ static char *kwlist[] = {"level", NULL};
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "|i", kwlist, &level))
+ return NULL;
+
+ ZERO_STRUCT(ctr);
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_getprinter(
+ hnd->cli, hnd->mem_ctx, 0, &needed, &hnd->pol, level, &ctr);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+ werror = cli_spoolss_getprinter(
+ hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol,
+ level, &ctr);
+
+ /* Return value */
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ result = Py_None;
+
+ switch (level) {
+
+ case 0:
+ py_from_PRINTER_INFO_0(&result, ctr.printers_0);
+ break;
+
+ case 1:
+ py_from_PRINTER_INFO_1(&result, ctr.printers_1);
+ break;
+
+ case 2:
+ py_from_PRINTER_INFO_2(&result, ctr.printers_2);
+ break;
+
+ case 3:
+ py_from_PRINTER_INFO_3(&result, ctr.printers_3);
+ break;
+ }
+
+ Py_INCREF(result);
+ return result;
+}
+
+/* Set printer information */
+
+PyObject *spoolss_hnd_setprinter(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ PyObject *info;
+ PRINTER_INFO_CTR ctr;
+ uint32 level;
+ static char *kwlist[] = {"dict", NULL};
+ union {
+ PRINTER_INFO_1 printers_1;
+ PRINTER_INFO_2 printers_2;
+ PRINTER_INFO_3 printers_3;
+ } pinfo;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "O!", kwlist, &PyDict_Type, &info))
+ return NULL;
+
+ if (!get_level_value(info, &level)) {
+ PyErr_SetString(spoolss_error, "invalid info level");
+ return NULL;
+ }
+
+ if (level < 1 && level > 3) {
+ PyErr_SetString(spoolss_error, "unsupported info level");
+ return NULL;
+ }
+
+ /* Fill in printer info */
+
+ ZERO_STRUCT(ctr);
+
+ switch (level) {
+ case 1:
+ ctr.printers_1 = &pinfo.printers_1;
+
+ if (!py_to_PRINTER_INFO_1(ctr.printers_1, info)){
+ PyErr_SetString(spoolss_error,
+ "error converting printer to info 1");
+ return NULL;
+ }
+
+ break;
+ case 2:
+ ctr.printers_2 = &pinfo.printers_2;
+
+ if (!py_to_PRINTER_INFO_2(ctr.printers_2, info,
+ hnd->mem_ctx)){
+ PyErr_SetString(spoolss_error,
+ "error converting printer to info 2");
+ return NULL;
+ }
+
+ break;
+ case 3:
+ ctr.printers_3 = &pinfo.printers_3;
+
+ if (!py_to_PRINTER_INFO_3(ctr.printers_3, info,
+ hnd->mem_ctx)) {
+ PyErr_SetString(spoolss_error,
+ "error converting to printer info 3");
+ return NULL;
+ }
+
+ break;
+ default:
+ PyErr_SetString(spoolss_error, "unsupported info level");
+ return NULL;
+ }
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_setprinter(hnd->cli, hnd->mem_ctx, &hnd->pol,
+ level, &ctr, 0);
+
+ /* Return value */
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* Enumerate printers */
+
+PyObject *spoolss_enumprinters(PyObject *self, PyObject *args, PyObject *kw)
+{
+ WERROR werror;
+ PyObject *result = NULL, *creds = NULL;
+ PRINTER_INFO_CTR ctr;
+ int level = 1, flags = PRINTER_ENUM_LOCAL, i;
+ uint32 needed, num_printers;
+ static char *kwlist[] = {"server", "name", "level", "flags",
+ "creds", NULL};
+ TALLOC_CTX *mem_ctx = NULL;
+ struct cli_state *cli = NULL;
+ char *server, *errstr, *name = NULL;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "s|siiO", kwlist, &server, &name, &level,
+ &flags, &creds))
+ return NULL;
+
+ if (server[0] != '\\' || server[1] != '\\') {
+ PyErr_SetString(PyExc_ValueError, "UNC name required");
+ return NULL;
+ }
+
+ server += 2;
+
+ if (creds && creds != Py_None && !PyDict_Check(creds)) {
+ PyErr_SetString(PyExc_TypeError,
+ "credentials must be dictionary or None");
+ return NULL;
+ }
+
+ if (!(cli = open_pipe_creds(server, creds, PI_SPOOLSS, &errstr))) {
+ PyErr_SetString(spoolss_error, errstr);
+ free(errstr);
+ goto done;
+ }
+
+ if (!(mem_ctx = talloc_init("spoolss_enumprinters"))) {
+ PyErr_SetString(
+ spoolss_error, "unable to init talloc context\n");
+ goto done;
+ }
+
+ /* This RPC is weird. By setting the server name to different
+ values we can get different behaviour. If however the server
+ name is not specified, we default it to being the full server
+ name as this is probably what the caller intended. To pass a
+ NULL name, pass a value of "" */
+
+ if (!name)
+ name = server;
+ else {
+ if (!name[0])
+ name = NULL;
+ }
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_enum_printers(
+ cli, mem_ctx, 0, &needed, name, flags, level,
+ &num_printers, &ctr);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+ werror = cli_spoolss_enum_printers(
+ cli, mem_ctx, needed, NULL, name, flags,
+ level, &num_printers, &ctr);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ goto done;
+ }
+
+ /* Return value */
+
+ switch (level) {
+ case 0:
+ result = PyDict_New();
+
+ for (i = 0; i < num_printers; i++) {
+ PyObject *value;
+ fstring s;
+
+ rpcstr_pull(s, ctr.printers_0[i].printername.buffer,
+ sizeof(fstring), -1, STR_TERMINATE);
+
+ py_from_PRINTER_INFO_0(&value, &ctr.printers_0[i]);
+
+ PyDict_SetItemString(
+ value, "level", PyInt_FromLong(0));
+
+ PyDict_SetItemString(result, s, value);
+ }
+
+ break;
+ case 1:
+ result = PyDict_New();
+
+ for(i = 0; i < num_printers; i++) {
+ PyObject *value;
+ fstring s;
+
+ rpcstr_pull(s, ctr.printers_1[i].name.buffer,
+ sizeof(fstring), -1, STR_TERMINATE);
+
+ py_from_PRINTER_INFO_1(&value, &ctr.printers_1[i]);
+
+ PyDict_SetItemString(
+ value, "level", PyInt_FromLong(1));
+
+ PyDict_SetItemString(result, s, value);
+ }
+
+ break;
+ case 2:
+ result = PyDict_New();
+
+ for(i = 0; i < num_printers; i++) {
+ PyObject *value;
+ fstring s;
+
+ rpcstr_pull(s, ctr.printers_2[i].printername.buffer,
+ sizeof(fstring), -1, STR_TERMINATE);
+
+ py_from_PRINTER_INFO_2(&value, &ctr.printers_2[i]);
+
+ PyDict_SetItemString(
+ value, "level", PyInt_FromLong(2));
+
+ PyDict_SetItemString(result, s, value);
+ }
+
+ break;
+ default:
+ PyErr_SetString(spoolss_error, "unknown info level");
+ goto done;
+ }
+
+done:
+ if (cli)
+ cli_shutdown(cli);
+
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+/* Add a printer */
+
+PyObject *spoolss_addprinterex(PyObject *self, PyObject *args, PyObject *kw)
+{
+ static char *kwlist[] = { "server", "printername", "info", "creds",
+ NULL};
+ char *printername, *server, *errstr;
+ PyObject *info, *result = NULL, *creds = NULL;
+ struct cli_state *cli = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+ PRINTER_INFO_CTR ctr;
+ PRINTER_INFO_2 info2;
+ WERROR werror;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "ssO!|O!", kwlist, &server, &printername,
+ &PyDict_Type, &info, &PyDict_Type, &creds))
+ return NULL;
+
+ if (!(cli = open_pipe_creds(server, creds, PI_SPOOLSS, &errstr))) {
+ PyErr_SetString(spoolss_error, errstr);
+ free(errstr);
+ goto done;
+ }
+
+ if (!(mem_ctx = talloc_init("spoolss_addprinterex"))) {
+ PyErr_SetString(
+ spoolss_error, "unable to init talloc context\n");
+ goto done;
+ }
+
+ if (!py_to_PRINTER_INFO_2(&info2, info, mem_ctx)) {
+ PyErr_SetString(spoolss_error,
+ "error converting to printer info 2");
+ goto done;
+ }
+
+ ctr.printers_2 = &info2;
+
+ werror = cli_spoolss_addprinterex(cli, mem_ctx, 2, &ctr);
+
+ Py_INCREF(Py_None);
+ result = Py_None;
+
+done:
+ if (cli)
+ cli_shutdown(cli);
+
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
diff --git a/source/python/py_spoolss_printers_conv.c b/source/python/py_spoolss_printers_conv.c
new file mode 100644
index 00000000000..f7b2f516df5
--- /dev/null
+++ b/source/python/py_spoolss_printers_conv.c
@@ -0,0 +1,354 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "python/py_spoolss.h"
+#include "python/py_conv.h"
+
+struct pyconv py_PRINTER_INFO_0[] = {
+ { "name", PY_UNISTR, offsetof(PRINTER_INFO_0, printername) },
+ { "server_name", PY_UNISTR, offsetof(PRINTER_INFO_0, servername) },
+
+ { "cjobs", PY_UINT32, offsetof(PRINTER_INFO_0, cjobs) },
+ { "total_jobs", PY_UINT32, offsetof(PRINTER_INFO_0, total_jobs) },
+ { "total_bytes", PY_UINT32, offsetof(PRINTER_INFO_0, total_bytes) },
+
+ { "year", PY_UINT16, offsetof(PRINTER_INFO_0, year) },
+ { "month", PY_UINT16, offsetof(PRINTER_INFO_0, month) },
+ { "day_of_week", PY_UINT16, offsetof(PRINTER_INFO_0, dayofweek) },
+ { "day", PY_UINT16, offsetof(PRINTER_INFO_0, day) },
+ { "hour", PY_UINT16, offsetof(PRINTER_INFO_0, hour) },
+ { "minute", PY_UINT16, offsetof(PRINTER_INFO_0, minute) },
+ { "second", PY_UINT16, offsetof(PRINTER_INFO_0, second) },
+ { "milliseconds", PY_UINT16, offsetof(PRINTER_INFO_0, milliseconds) },
+
+ { "global_counter", PY_UINT32, offsetof(PRINTER_INFO_0, global_counter) },
+ { "total_pages", PY_UINT32, offsetof(PRINTER_INFO_0, total_pages) },
+
+ { "major_version", PY_UINT16, offsetof(PRINTER_INFO_0, major_version) },
+ { "build_version", PY_UINT16, offsetof(PRINTER_INFO_0, build_version) },
+
+ { "unknown7", PY_UINT32, offsetof(PRINTER_INFO_0, unknown7) },
+ { "unknown8", PY_UINT32, offsetof(PRINTER_INFO_0, unknown8) },
+ { "unknown9", PY_UINT32, offsetof(PRINTER_INFO_0, unknown9) },
+ { "session_counter", PY_UINT32, offsetof(PRINTER_INFO_0, session_counter)},
+ { "unknown11", PY_UINT32, offsetof(PRINTER_INFO_0, unknown11) },
+ { "printer_errors", PY_UINT32, offsetof(PRINTER_INFO_0, printer_errors) },
+ { "unknown13", PY_UINT32, offsetof(PRINTER_INFO_0, unknown13) },
+ { "unknown14", PY_UINT32, offsetof(PRINTER_INFO_0, unknown14) },
+ { "unknown15", PY_UINT32, offsetof(PRINTER_INFO_0, unknown15) },
+ { "unknown16", PY_UINT32, offsetof(PRINTER_INFO_0, unknown16) },
+ { "change_id", PY_UINT32, offsetof(PRINTER_INFO_0, change_id) },
+ { "unknown18", PY_UINT32, offsetof(PRINTER_INFO_0, unknown18) },
+ { "status", PY_UINT32, offsetof(PRINTER_INFO_0, status) },
+ { "unknown20", PY_UINT32, offsetof(PRINTER_INFO_0, unknown20) },
+ { "c_setprinter", PY_UINT32, offsetof(PRINTER_INFO_0, c_setprinter) },
+ { "unknown22", PY_UINT32, offsetof(PRINTER_INFO_0, unknown22) },
+ { "unknown23", PY_UINT32, offsetof(PRINTER_INFO_0, unknown23) },
+ { "unknown24", PY_UINT32, offsetof(PRINTER_INFO_0, unknown24) },
+ { "unknown25", PY_UINT32, offsetof(PRINTER_INFO_0, unknown25) },
+ { "unknown26", PY_UINT32, offsetof(PRINTER_INFO_0, unknown26) },
+ { "unknown27", PY_UINT32, offsetof(PRINTER_INFO_0, unknown27) },
+ { "unknown28", PY_UINT32, offsetof(PRINTER_INFO_0, unknown28) },
+ { "unknown29", PY_UINT32, offsetof(PRINTER_INFO_0, unknown29) },
+
+ { NULL }
+};
+
+struct pyconv py_PRINTER_INFO_1[] = {
+ { "name", PY_UNISTR, offsetof(PRINTER_INFO_1, name) },
+ { "description", PY_UNISTR, offsetof(PRINTER_INFO_1, description) },
+ { "comment", PY_UNISTR, offsetof(PRINTER_INFO_1, comment) },
+ { "flags", PY_UINT32, offsetof(PRINTER_INFO_1, flags) },
+ { NULL }
+};
+
+struct pyconv py_PRINTER_INFO_2[] = {
+ { "server_name", PY_UNISTR, offsetof(PRINTER_INFO_2, servername) },
+ { "name", PY_UNISTR, offsetof(PRINTER_INFO_2, printername) },
+ { "share_name", PY_UNISTR, offsetof(PRINTER_INFO_2, sharename) },
+ { "port_name", PY_UNISTR, offsetof(PRINTER_INFO_2, portname) },
+ { "driver_name", PY_UNISTR, offsetof(PRINTER_INFO_2, drivername) },
+ { "comment", PY_UNISTR, offsetof(PRINTER_INFO_2, comment) },
+ { "location", PY_UNISTR, offsetof(PRINTER_INFO_2, location) },
+ { "datatype", PY_UNISTR, offsetof(PRINTER_INFO_2, datatype) },
+ { "sepfile", PY_UNISTR, offsetof(PRINTER_INFO_2, sepfile) },
+ { "print_processor", PY_UNISTR, offsetof(PRINTER_INFO_2, printprocessor) },
+ { "parameters", PY_UNISTR, offsetof(PRINTER_INFO_2, parameters) },
+ { "attributes", PY_UINT32, offsetof(PRINTER_INFO_2, attributes) },
+ { "default_priority", PY_UINT32, offsetof(PRINTER_INFO_2, defaultpriority) },
+ { "priority", PY_UINT32, offsetof(PRINTER_INFO_2, priority) },
+ { "start_time", PY_UINT32, offsetof(PRINTER_INFO_2, starttime) },
+ { "until_time", PY_UINT32, offsetof(PRINTER_INFO_2, untiltime) },
+ { "status", PY_UINT32, offsetof(PRINTER_INFO_2, status) },
+ { "cjobs", PY_UINT32, offsetof(PRINTER_INFO_2, cjobs) },
+ { "average_ppm", PY_UINT32, offsetof(PRINTER_INFO_2, averageppm) },
+ { NULL }
+};
+
+struct pyconv py_PRINTER_INFO_3[] = {
+ { "flags", PY_UINT32, offsetof(PRINTER_INFO_3, flags) },
+ { NULL }
+};
+
+struct pyconv py_DEVICEMODE[] = {
+ { "device_name", PY_UNISTR, offsetof(DEVICEMODE, devicename) },
+ { "spec_version", PY_UINT16, offsetof(DEVICEMODE, specversion) },
+ { "driver_version", PY_UINT16, offsetof(DEVICEMODE, driverversion) },
+ { "size", PY_UINT16, offsetof(DEVICEMODE, size) },
+ { "fields", PY_UINT16, offsetof(DEVICEMODE, fields) },
+ { "orientation", PY_UINT16, offsetof(DEVICEMODE, orientation) },
+ { "paper_size", PY_UINT16, offsetof(DEVICEMODE, papersize) },
+ { "paper_width", PY_UINT16, offsetof(DEVICEMODE, paperwidth) },
+ { "paper_length", PY_UINT16, offsetof(DEVICEMODE, paperlength) },
+ { "scale", PY_UINT16, offsetof(DEVICEMODE, scale) },
+ { "copies", PY_UINT16, offsetof(DEVICEMODE, copies) },
+ { "default_source", PY_UINT16, offsetof(DEVICEMODE, defaultsource) },
+ { "print_quality", PY_UINT16, offsetof(DEVICEMODE, printquality) },
+ { "color", PY_UINT16, offsetof(DEVICEMODE, color) },
+ { "duplex", PY_UINT16, offsetof(DEVICEMODE, duplex) },
+ { "y_resolution", PY_UINT16, offsetof(DEVICEMODE, yresolution) },
+ { "tt_option", PY_UINT16, offsetof(DEVICEMODE, ttoption) },
+ { "collate", PY_UINT16, offsetof(DEVICEMODE, collate) },
+ { "form_name", PY_UNISTR, offsetof(DEVICEMODE, formname) },
+ { "log_pixels", PY_UINT16, offsetof(DEVICEMODE, logpixels) },
+ { "bits_per_pel", PY_UINT32, offsetof(DEVICEMODE, bitsperpel) },
+ { "pels_width", PY_UINT32, offsetof(DEVICEMODE, pelswidth) },
+ { "pels_height", PY_UINT32, offsetof(DEVICEMODE, pelsheight) },
+ { "display_flags", PY_UINT32, offsetof(DEVICEMODE, displayflags) },
+ { "display_frequency", PY_UINT32, offsetof(DEVICEMODE, displayfrequency) },
+ { "icm_method", PY_UINT32, offsetof(DEVICEMODE, icmmethod) },
+ { "icm_intent", PY_UINT32, offsetof(DEVICEMODE, icmintent) },
+ { "media_type", PY_UINT32, offsetof(DEVICEMODE, mediatype) },
+ { "dither_type", PY_UINT32, offsetof(DEVICEMODE, dithertype) },
+ { "reserved1", PY_UINT32, offsetof(DEVICEMODE, reserved1) },
+ { "reserved2", PY_UINT32, offsetof(DEVICEMODE, reserved2) },
+ { "panning_width", PY_UINT32, offsetof(DEVICEMODE, panningwidth) },
+ { "panning_height", PY_UINT32, offsetof(DEVICEMODE, panningheight) },
+ { NULL }
+};
+
+/*
+ * Convert between DEVICEMODE and Python
+ */
+
+BOOL py_from_DEVICEMODE(PyObject **dict, DEVICEMODE *devmode)
+{
+ *dict = from_struct(devmode, py_DEVICEMODE);
+
+ PyDict_SetItemString(*dict, "private",
+ PyString_FromStringAndSize(
+ devmode->private, devmode->driverextra));
+
+ return True;
+}
+
+BOOL py_to_DEVICEMODE(DEVICEMODE *devmode, PyObject *dict)
+{
+ PyObject *obj, *dict_copy = PyDict_Copy(dict);
+ BOOL result = False;
+
+ if (!(obj = PyDict_GetItemString(dict_copy, "private")))
+ goto done;
+
+ if (!PyString_Check(obj))
+ goto done;
+
+ devmode->private = PyString_AsString(obj);
+ devmode->driverextra = PyString_Size(obj);
+
+ PyDict_DelItemString(dict_copy, "private");
+
+ if (!to_struct(devmode, dict_copy, py_DEVICEMODE))
+ goto done;
+
+ result = True;
+
+done:
+ Py_DECREF(dict_copy);
+ return result;
+}
+
+/*
+ * Convert between PRINTER_INFO_0 and Python
+ */
+
+BOOL py_from_PRINTER_INFO_0(PyObject **dict, PRINTER_INFO_0 *info)
+{
+ *dict = from_struct(info, py_PRINTER_INFO_0);
+ PyDict_SetItemString(*dict, "level", PyInt_FromLong(0));
+ return True;
+}
+
+BOOL py_to_PRINTER_INFO_0(PRINTER_INFO_0 *info, PyObject *dict)
+{
+ return False;
+}
+
+/*
+ * Convert between PRINTER_INFO_1 and Python
+ */
+
+BOOL py_from_PRINTER_INFO_1(PyObject **dict, PRINTER_INFO_1 *info)
+{
+ *dict = from_struct(info, py_PRINTER_INFO_1);
+ PyDict_SetItemString(*dict, "level", PyInt_FromLong(1));
+ return True;
+}
+
+BOOL py_to_PRINTER_INFO_1(PRINTER_INFO_1 *info, PyObject *dict)
+{
+ PyObject *obj, *dict_copy = PyDict_Copy(dict);
+ BOOL result = False;
+
+ if (!(obj = PyDict_GetItemString(dict_copy, "level")) ||
+ !PyInt_Check(obj))
+ goto done;
+
+ PyDict_DelItemString(dict_copy, "level");
+
+ if (!to_struct(info, dict_copy, py_PRINTER_INFO_1))
+ goto done;
+
+ result = True;
+
+done:
+ Py_DECREF(dict_copy);
+ return result;
+}
+
+/*
+ * Convert between PRINTER_INFO_2 and Python
+ */
+
+BOOL py_from_PRINTER_INFO_2(PyObject **dict, PRINTER_INFO_2 *info)
+{
+ PyObject *obj;
+
+ *dict = from_struct(info, py_PRINTER_INFO_2);
+
+ /* The security descriptor could be NULL */
+
+ if (info->secdesc) {
+ if (py_from_SECDESC(&obj, info->secdesc))
+ PyDict_SetItemString(*dict, "security_descriptor", obj);
+ }
+
+ /* Bong! The devmode could be NULL */
+
+ if (info->devmode)
+ py_from_DEVICEMODE(&obj, info->devmode);
+ else
+ obj = PyDict_New();
+
+ PyDict_SetItemString(*dict, "device_mode", obj);
+
+ PyDict_SetItemString(*dict, "level", PyInt_FromLong(2));
+
+ return True;
+}
+
+BOOL py_to_PRINTER_INFO_2(PRINTER_INFO_2 *info, PyObject *dict,
+ TALLOC_CTX *mem_ctx)
+{
+ PyObject *obj, *dict_copy = PyDict_Copy(dict);
+ BOOL result = False;
+
+ /* Convert security descriptor - may be NULL */
+
+ info->secdesc = NULL;
+
+ if ((obj = PyDict_GetItemString(dict_copy, "security_descriptor"))) {
+
+ if (!PyDict_Check(obj))
+ goto done;
+
+ if (!py_to_SECDESC(&info->secdesc, obj, mem_ctx))
+ goto done;
+
+ PyDict_DelItemString(dict_copy, "security_descriptor");
+ }
+
+ /* Convert device mode */
+
+ if (!(obj = PyDict_GetItemString(dict_copy, "device_mode"))
+ || !PyDict_Check(obj))
+ goto done;
+
+ info->devmode = talloc(mem_ctx, sizeof(DEVICEMODE));
+
+ if (!py_to_DEVICEMODE(info->devmode, obj))
+ goto done;
+
+ PyDict_DelItemString(dict_copy, "device_mode");
+
+ /* Check info level */
+
+ if (!(obj = PyDict_GetItemString(dict_copy, "level")) ||
+ !PyInt_Check(obj))
+ goto done;
+
+ PyDict_DelItemString(dict_copy, "level");
+
+ /* Convert remaining elements of dictionary */
+
+ if (!to_struct(info, dict_copy, py_PRINTER_INFO_2))
+ goto done;
+
+ result = True;
+
+done:
+ Py_DECREF(dict_copy);
+ return result;
+}
+
+/*
+ * Convert between PRINTER_INFO_1 and Python
+ */
+
+BOOL py_from_PRINTER_INFO_3(PyObject **dict, PRINTER_INFO_3 *info)
+{
+ PyObject *obj;
+
+ *dict = from_struct(info, py_PRINTER_INFO_3);
+
+ if (py_from_SECDESC(&obj, info->secdesc))
+ PyDict_SetItemString(*dict, "security_descriptor", obj);
+
+ PyDict_SetItemString(*dict, "level", PyInt_FromLong(3));
+
+ return True;
+}
+
+BOOL py_to_PRINTER_INFO_3(PRINTER_INFO_3 *info, PyObject *dict,
+ TALLOC_CTX *mem_ctx)
+{
+ PyObject *obj;
+
+ if (!to_struct(info, dict, py_PRINTER_INFO_3))
+ return False;
+
+ if (!(obj = PyDict_GetItemString(dict, "security_descriptor")))
+ return False;
+
+ if (!py_to_SECDESC(&info->secdesc, obj, mem_ctx))
+ return False;
+
+ return True;
+}
diff --git a/source/python/py_srvsvc.c b/source/python/py_srvsvc.c
new file mode 100644
index 00000000000..3e5a42be234
--- /dev/null
+++ b/source/python/py_srvsvc.c
@@ -0,0 +1,215 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "python/py_srvsvc.h"
+
+/* Exceptions this module can raise */
+
+PyObject *srvsvc_error, *srvsvc_werror;
+
+static struct const_vals {
+ char *name;
+ uint32 value;
+} module_const_vals[] = {
+ { "SV_TYPE_WORKSTATION", SV_TYPE_WORKSTATION },
+ { "SV_TYPE_SERVER", SV_TYPE_SERVER },
+ { "SV_TYPE_SQLSERVER", SV_TYPE_SQLSERVER },
+ { "SV_TYPE_DOMAIN_CTRL", SV_TYPE_DOMAIN_CTRL },
+ { "SV_TYPE_DOMAIN_BAKCTRL", SV_TYPE_DOMAIN_BAKCTRL },
+ { "SV_TYPE_TIME_SOURCE", SV_TYPE_TIME_SOURCE },
+ { "SV_TYPE_AFP", SV_TYPE_AFP },
+ { "SV_TYPE_NOVELL", SV_TYPE_NOVELL },
+ { "SV_TYPE_DOMAIN_MEMBER", SV_TYPE_DOMAIN_MEMBER },
+ { "SV_TYPE_PRINTQ_SERVER", SV_TYPE_PRINTQ_SERVER },
+ { "SV_TYPE_DIALIN_SERVER", SV_TYPE_DIALIN_SERVER },
+ { "SV_TYPE_SERVER_UNIX", SV_TYPE_SERVER_UNIX },
+ { "SV_TYPE_NT", SV_TYPE_NT },
+ { "SV_TYPE_WFW", SV_TYPE_WFW },
+ { "SV_TYPE_SERVER_MFPN", SV_TYPE_SERVER_MFPN },
+ { "SV_TYPE_SERVER_NT", SV_TYPE_SERVER_NT },
+ { "SV_TYPE_POTENTIAL_BROWSER", SV_TYPE_POTENTIAL_BROWSER },
+ { "SV_TYPE_BACKUP_BROWSER", SV_TYPE_BACKUP_BROWSER },
+ { "SV_TYPE_MASTER_BROWSER", SV_TYPE_MASTER_BROWSER },
+ { "SV_TYPE_DOMAIN_MASTER", SV_TYPE_DOMAIN_MASTER },
+ { "SV_TYPE_SERVER_OSF", SV_TYPE_SERVER_OSF },
+ { "SV_TYPE_SERVER_VMS", SV_TYPE_SERVER_VMS },
+ { "SV_TYPE_WIN95_PLUS", SV_TYPE_WIN95_PLUS },
+ { "SV_TYPE_DFS_SERVER", SV_TYPE_DFS_SERVER },
+ { "SV_TYPE_ALTERNATE_XPORT", SV_TYPE_ALTERNATE_XPORT },
+ { "SV_TYPE_LOCAL_LIST_ONLY", SV_TYPE_LOCAL_LIST_ONLY },
+ { "SV_TYPE_DOMAIN_ENUM", SV_TYPE_DOMAIN_ENUM },
+ { NULL },
+};
+
+static void const_init(PyObject *dict)
+{
+ struct const_vals *tmp;
+ PyObject *obj;
+
+ for (tmp = module_const_vals; tmp->name; tmp++) {
+ obj = PyInt_FromLong(tmp->value);
+ PyDict_SetItemString(dict, tmp->name, obj);
+ Py_DECREF(obj);
+ }
+}
+
+/* NetServerGetInfo */
+
+PyObject *srvsvc_netservergetinfo(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ static char *kwlist[] = { "server", "level", "creds", NULL };
+ char *unc_name, *server, *errstr;
+ PyObject *creds = NULL, *result = NULL;
+ struct cli_state *cli;
+ TALLOC_CTX *mem_ctx = NULL;
+ uint32 level;
+ SRV_INFO_CTR ctr;
+ WERROR status;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "si|O", kwlist, &unc_name, &level, &creds))
+ return NULL;
+
+ if (unc_name[0] != '\\' || unc_name[1] != '\\') {
+ PyErr_SetString(PyExc_ValueError, "UNC name required");
+ return NULL;
+ }
+
+ server = strdup(unc_name + 2);
+
+ if (strchr(server, '\\')) {
+ char *c = strchr(server, '\\');
+ *c = 0;
+ }
+
+ if (creds && creds != Py_None && !PyDict_Check(creds)) {
+ PyErr_SetString(PyExc_TypeError,
+ "credentials must be dictionary or None");
+ return NULL;
+ }
+
+ if (!(cli = open_pipe_creds(server, creds, PI_SRVSVC, &errstr))) {
+ PyErr_SetString(srvsvc_error, errstr);
+ free(errstr);
+ goto done;
+ }
+
+ if (!(mem_ctx = talloc_init("srvsvc_netservergetinfo"))) {
+ PyErr_SetString(srvsvc_error,
+ "unable to init talloc context\n");
+ goto done;
+ }
+
+ ZERO_STRUCT(ctr);
+
+ status = cli_srvsvc_net_srv_get_info(cli, mem_ctx, level, &ctr);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetObject(srvsvc_error, py_werror_tuple(status));
+ goto done;
+ }
+
+ if (level != ctr.switch_value) {
+ PyErr_SetString(srvsvc_error, "container level value wrong");
+ goto done;
+ }
+
+ switch(level) {
+ case 101:
+ py_from_SRV_INFO_101(&result, &ctr.srv.sv101);
+ break;
+ }
+
+ Py_INCREF(result);
+
+done:
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+/*
+ * Module initialisation
+ */
+
+static PyMethodDef srvsvc_methods[] = {
+ { "netservergetinfo", (PyCFunction)srvsvc_netservergetinfo,
+ METH_VARARGS | METH_KEYWORDS,
+ "Retrieve information about a particular server." },
+
+ { "setup_logging", (PyCFunction)py_setup_logging,
+ METH_VARARGS | METH_KEYWORDS,
+ "Set up debug logging.\n"
+"\n"
+"Initialises Samba's debug logging system. One argument is expected which\n"
+"is a boolean specifying whether debugging is interactive and sent to stdout\n"
+"or logged to a file.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> srvsvc.setup_logging(interactive = 1)" },
+
+ { "get_debuglevel", (PyCFunction)get_debuglevel,
+ METH_VARARGS,
+ "Set the current debug level.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> srvsvc.get_debuglevel()\n"
+"0" },
+
+ { "set_debuglevel", (PyCFunction)set_debuglevel,
+ METH_VARARGS,
+ "Get the current debug level.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> srvsvc.set_debuglevel(10)" },
+
+ { NULL }
+};
+
+void initsrvsvc(void)
+{
+ PyObject *module, *dict;
+
+ /* Initialise module */
+
+ module = Py_InitModule("srvsvc", srvsvc_methods);
+ dict = PyModule_GetDict(module);
+
+ /* Exceptions we can raise */
+
+ srvsvc_error = PyErr_NewException("srvsvc.error", NULL, NULL);
+ PyDict_SetItemString(dict, "error", srvsvc_error);
+
+ srvsvc_werror = PyErr_NewException("srvsvc.werror", NULL, NULL);
+ PyDict_SetItemString(dict, "werror", srvsvc_werror);
+
+ /* Initialise constants */
+
+ const_init(dict);
+
+ /* Do samba initialisation */
+
+ py_samba_init();
+}
diff --git a/source/python/py_srvsvc.h b/source/python/py_srvsvc.h
new file mode 100644
index 00000000000..c5e71cb90f5
--- /dev/null
+++ b/source/python/py_srvsvc.h
@@ -0,0 +1,30 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _PY_SRVSVC_H
+#define _PY_SRVSVC_H
+
+#include "python/py_common.h"
+
+/* The following definitions come from python/py_srvsv.c */
+
+BOOL py_from_SRV_INFO_101(PyObject **dict, SRV_INFO_101 *info);
+
+#endif /* _PY_SRVSVC_H */
diff --git a/source/python/py_srvsvc_conv.c b/source/python/py_srvsvc_conv.c
new file mode 100644
index 00000000000..86c3761d0f4
--- /dev/null
+++ b/source/python/py_srvsvc_conv.c
@@ -0,0 +1,41 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "python/py_srvsvc.h"
+#include "python/py_conv.h"
+
+static struct pyconv py_SRV_INFO_101[] = {
+ { "platform_id", PY_UINT32, offsetof(SRV_INFO_101, platform_id) },
+ { "major_version", PY_UINT32, offsetof(SRV_INFO_101, ver_major) },
+ { "minor_version", PY_UINT32, offsetof(SRV_INFO_101, ver_minor) },
+ { "server_type", PY_UINT32, offsetof(SRV_INFO_101, srv_type) },
+ { "name", PY_UNISTR2, offsetof(SRV_INFO_101, uni_name) },
+ { "comment", PY_UNISTR2, offsetof(SRV_INFO_101, uni_comment) },
+ { NULL }
+};
+
+BOOL py_from_SRV_INFO_101(PyObject **dict, SRV_INFO_101 *info)
+{
+ *dict = from_struct(info, py_SRV_INFO_101);
+
+ PyDict_SetItemString(*dict, "level", PyInt_FromLong(101));
+
+ return True;
+}
diff --git a/source/python/py_tdb.c b/source/python/py_tdb.c
new file mode 100644
index 00000000000..37f64ce7802
--- /dev/null
+++ b/source/python/py_tdb.c
@@ -0,0 +1,702 @@
+/*
+ Python wrappers for TDB module
+
+ Copyright (C) Tim Potter, 2002-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ NOTE: Since tdb is licenced under the GPL any program that uses these bindings
+ must be distributed under the GPL license terms since this is what
+ the GPL requires.
+
+ http://www.gnu.org/licenses/gpl-faq.html#IfInterpreterIsGPL
+*/
+
+#include "includes.h"
+
+/* This symbol is used in both includes.h and Python.h which causes an
+ annoying compiler warning. */
+
+#ifdef HAVE_FSTAT
+#undef HAVE_FSTAT
+#endif
+
+#include "Python.h"
+
+/* Tdb exception */
+
+PyObject *py_tdb_error;
+
+/* tdb handle object */
+
+typedef struct {
+ PyObject_HEAD
+ TDB_CONTEXT *tdb;
+} tdb_hnd_object;
+
+PyTypeObject tdb_hnd_type;
+
+PyObject *new_tdb_hnd_object(TDB_CONTEXT *tdb)
+{
+ tdb_hnd_object *obj;
+
+ obj = PyObject_New(tdb_hnd_object, &tdb_hnd_type);
+ obj->tdb = tdb;
+
+ return (PyObject *)obj;
+}
+
+PyObject *py_tdb_close(PyObject *self, PyObject *args)
+{
+ tdb_hnd_object *obj;
+
+ if (!PyArg_ParseTuple(args, "O!", &tdb_hnd_type, &obj))
+ return NULL;
+
+ if (tdb_close(obj->tdb) == -1) {
+ obj->tdb = NULL;
+ PyErr_SetString(py_tdb_error, strerror(errno));
+ return NULL;
+ }
+
+ obj->tdb = NULL;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyObject *py_tdb_open(PyObject *self, PyObject *args, PyObject *kw)
+{
+ static char *kwlist[] = { "name", "hash_size", "tdb_flags",
+ "open_flags", "mode", NULL };
+ char *name;
+ int hash_size = 0, flags = TDB_DEFAULT, open_flags = -1, open_mode = 0600;
+ TDB_CONTEXT *tdb;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "s|iiii", kwlist, &name, &hash_size, &flags,
+ &open_flags, &open_mode))
+ return NULL;
+
+ /* Default open_flags to read/write */
+
+ if (open_flags == -1) {
+ if (access(name, W_OK) == -1)
+ open_flags = O_RDONLY;
+ else
+ open_flags = O_RDWR;
+ }
+
+ if (!(tdb = tdb_open(name, hash_size, flags, open_flags, open_mode))) {
+ PyErr_SetString(py_tdb_error, strerror(errno));
+ return NULL;
+ }
+
+ return new_tdb_hnd_object(tdb);
+}
+
+/*
+ * Allow a tdb to act as a python mapping (dictionary)
+ */
+
+static int tdb_traverse_count(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA value,
+ void *state)
+{
+ /* Do nothing - tdb_traverse will return the number of records
+ traversed. */
+
+ return 0;
+}
+
+static int tdb_hnd_length(tdb_hnd_object *obj)
+{
+ int result;
+
+ result = tdb_traverse(obj->tdb, tdb_traverse_count, NULL);
+
+ return result;
+}
+
+static PyObject *tdb_hnd_subscript(tdb_hnd_object *obj, PyObject *key)
+{
+ TDB_DATA drec, krec;
+ PyObject *result;
+
+ if (!PyArg_Parse(key, "s#", &krec.dptr, &krec.dsize))
+ return NULL;
+
+ drec = tdb_fetch(obj->tdb, krec);
+
+ if (!drec.dptr) {
+ PyErr_SetString(PyExc_KeyError,
+ PyString_AsString(key));
+ return NULL;
+ }
+
+ result = PyString_FromStringAndSize(drec.dptr, drec.dsize);
+ free(drec.dptr);
+
+ return result;
+}
+
+static int tdb_ass_subscript(tdb_hnd_object *obj, PyObject *key, PyObject *value)
+{
+ TDB_DATA krec, drec;
+
+ if (!PyArg_Parse(key, "s#", &krec.dptr, &krec.dsize)) {
+ PyErr_SetString(PyExc_TypeError,
+ "tdb mappings have string indices only");
+ return -1;
+ }
+
+ if (!obj->tdb) {
+ PyErr_SetString(
+ py_tdb_error, "tdb object has been closed");
+ return -1;
+ }
+
+ if (!value) {
+
+ /* Delete value */
+
+ if (tdb_delete(obj->tdb, krec) == -1) {
+ PyErr_SetString(PyExc_KeyError,
+ PyString_AsString(value));
+ return -1;
+ }
+
+ } else {
+
+ /* Set value */
+
+ if (!PyArg_Parse(value, "s#", &drec.dptr, &drec.dsize)) {
+ PyErr_SetString(PyExc_TypeError,
+ "tdb mappings have string elements only");
+ return -1;
+ }
+
+ errno = 0;
+
+ if (tdb_store(obj->tdb, krec, drec, 0) < 0 ) {
+ if (errno != 0)
+ PyErr_SetFromErrno(py_tdb_error);
+ else
+ PyErr_SetString(
+ py_tdb_error,
+ (char *)tdb_errorstr(obj->tdb));
+
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static PyMappingMethods tdb_mapping = {
+ (inquiry) tdb_hnd_length,
+ (binaryfunc) tdb_hnd_subscript,
+ (objobjargproc) tdb_ass_subscript
+};
+
+/*
+ * Utility methods
+ */
+
+/* Return non-zero if a given key exists in the tdb */
+
+PyObject *py_tdb_hnd_has_key(PyObject *self, PyObject *args)
+{
+ tdb_hnd_object *obj = (tdb_hnd_object *)self;
+ TDB_DATA key;
+
+ if (!PyArg_ParseTuple(args, "s#", &key.dptr, &key.dsize))
+ return NULL;
+
+ if (!obj->tdb) {
+ PyErr_SetString(
+ py_tdb_error, "tdb object has been closed");
+ return NULL;
+ }
+
+ return PyInt_FromLong(tdb_exists(obj->tdb, key));
+}
+
+/* Return a list of keys in the tdb */
+
+static int tdb_traverse_keys(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA value,
+ void *state)
+{
+ PyObject *key_list = (PyObject *)state;
+
+ PyList_Append(key_list,
+ PyString_FromStringAndSize(key.dptr, key.dsize));
+
+ return 0;
+}
+
+PyObject *py_tdb_hnd_keys(PyObject *self, PyObject *args)
+{
+ tdb_hnd_object *obj = (tdb_hnd_object *)self;
+ PyObject *key_list = PyList_New(0);
+
+ if (!obj->tdb) {
+ PyErr_SetString(py_tdb_error, "tdb object has been closed");
+ return NULL;
+ }
+
+ if (tdb_traverse(obj->tdb, tdb_traverse_keys, key_list) == -1) {
+ PyErr_SetString(py_tdb_error, "error traversing tdb");
+ Py_DECREF(key_list);
+ return NULL;
+ }
+
+ return key_list;
+}
+
+PyObject *py_tdb_hnd_first_key(PyObject *self, PyObject *args)
+{
+ tdb_hnd_object *obj = (tdb_hnd_object *)self;
+ TDB_DATA key;
+
+ if (!obj->tdb) {
+ PyErr_SetString(py_tdb_error, "tdb object has been closed");
+ return NULL;
+ }
+
+ key = tdb_firstkey(obj->tdb);
+
+ return Py_BuildValue("s#", key.dptr, key.dsize);
+}
+
+PyObject *py_tdb_hnd_next_key(PyObject *self, PyObject *py_oldkey)
+{
+ tdb_hnd_object *obj = (tdb_hnd_object *)self;
+ TDB_DATA key, oldkey;
+
+ if (!obj->tdb) {
+ PyErr_SetString(py_tdb_error, "tdb object has been closed");
+ return NULL;
+ }
+
+ if (!PyArg_Parse(py_oldkey, "s#", &oldkey.dptr, &oldkey.dsize))
+ return NULL;
+
+ key = tdb_nextkey(obj->tdb, oldkey);
+
+ return Py_BuildValue("s#", key.dptr, key.dsize);
+}
+
+/*
+ * Locking routines
+ */
+
+PyObject *py_tdb_hnd_lock_all(PyObject *self, PyObject *args)
+{
+ tdb_hnd_object *obj = (tdb_hnd_object *)self;
+ int result;
+
+ if (!obj->tdb) {
+ PyErr_SetString(py_tdb_error, "tdb object has been closed");
+ return NULL;
+ }
+
+ result = tdb_lockall(obj->tdb);
+
+ return PyInt_FromLong(result != -1);
+}
+
+PyObject *py_tdb_hnd_unlock_all(PyObject *self, PyObject *args)
+{
+ tdb_hnd_object *obj = (tdb_hnd_object *)self;
+
+ if (!obj->tdb) {
+ PyErr_SetString(py_tdb_error, "tdb object has been closed");
+ return NULL;
+ }
+
+ tdb_unlockall(obj->tdb);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* Return an array of keys from a python object which must be a string or a
+ list of strings. */
+
+static BOOL make_lock_list(PyObject *py_keys, TDB_DATA **keys, int *num_keys)
+{
+ /* Are we a list or a string? */
+
+ if (!PyList_Check(py_keys) && !PyString_Check(py_keys)) {
+ PyErr_SetString(PyExc_TypeError, "arg must be list of string");
+ return False;
+ }
+
+ if (PyList_Check(py_keys)) {
+ int i;
+
+ /* Turn python list into array of keys */
+
+ *num_keys = PyList_Size(py_keys);
+ *keys = (TDB_DATA *)malloc(sizeof(TDB_DATA) * (*num_keys));
+
+ for (i = 0; i < *num_keys; i++) {
+ PyObject *key = PyList_GetItem(py_keys, i);
+
+ if (!PyString_Check(key)) {
+ PyErr_SetString(
+ PyExc_TypeError,
+ "list elements must be strings");
+ return False;
+ }
+
+ PyArg_Parse(key, "s#", &(*keys)[i].dptr,
+ &(*keys)[i].dsize);
+ }
+
+ } else {
+
+ /* Turn python string into a single key */
+
+ *keys = (TDB_DATA *)malloc(sizeof(TDB_DATA));
+ *num_keys = 1;
+ PyArg_Parse(py_keys, "s#", &(*keys)->dptr, &(*keys)->dsize);
+ }
+
+ return True;
+}
+
+PyObject *py_tdb_hnd_lock(PyObject *self, PyObject *args)
+{
+ tdb_hnd_object *obj = (tdb_hnd_object *)self;
+ PyObject *py_keys;
+ TDB_DATA *keys;
+ int num_keys, result;
+
+ if (!obj->tdb) {
+ PyErr_SetString(py_tdb_error, "tdb object has been closed");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "O", &py_keys))
+ return NULL;
+
+ if (!make_lock_list(py_keys, &keys, &num_keys))
+ return NULL;
+
+ result = tdb_lockkeys(obj->tdb, num_keys, keys);
+
+ free(keys);
+
+ return PyInt_FromLong(result != -1);
+}
+
+PyObject *py_tdb_hnd_unlock(PyObject *self, PyObject *args)
+{
+ tdb_hnd_object *obj = (tdb_hnd_object *)self;
+
+ if (!obj->tdb) {
+ PyErr_SetString(py_tdb_error, "tdb object has been closed");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+
+ tdb_unlockkeys(obj->tdb);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/*
+ * tdb traversal
+ */
+
+struct traverse_info {
+ PyObject *callback;
+ PyObject *state;
+};
+
+static int tdb_traverse_traverse(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA value,
+ void *state)
+{
+ struct traverse_info *info = state;
+ PyObject *arglist, *py_result;
+ int result;
+
+ arglist = Py_BuildValue("(s#s#O)", key.dptr, key.dsize, value.dptr,
+ value.dsize, info->state);
+
+ py_result = PyEval_CallObject(info->callback, arglist);
+
+ Py_DECREF(arglist);
+
+ if (!PyInt_Check(py_result)) {
+ result = 1; /* Hmm - non-integer object returned by callback */
+ goto done;
+ }
+
+ result = PyInt_AsLong(py_result);
+
+done:
+ Py_DECREF(py_result);
+ return result;
+}
+
+PyObject *py_tdb_hnd_traverse(PyObject *self, PyObject *args, PyObject *kw)
+{
+ tdb_hnd_object *obj = (tdb_hnd_object *)self;
+ static char *kwlist[] = { "traverse_fn", "state", NULL };
+ PyObject *state = Py_None, *callback;
+ struct traverse_info info;
+ int result;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "O|O", kwlist, &callback, &state))
+ return NULL;
+
+ if (!PyCallable_Check(callback)) {
+ PyErr_SetString(PyExc_TypeError, "parameter must be callable");
+ return NULL;
+ }
+
+ Py_INCREF(callback);
+ Py_INCREF(state);
+
+ info.callback = callback;
+ info.state = state;
+
+ result = tdb_traverse(obj->tdb, tdb_traverse_traverse, &info);
+
+ Py_DECREF(callback);
+ Py_DECREF(state);
+
+ return PyInt_FromLong(result);
+}
+
+PyObject *py_tdb_hnd_chainlock(PyObject *self, PyObject *args)
+{
+ tdb_hnd_object *obj = (tdb_hnd_object *)self;
+ TDB_DATA key;
+ int result;
+
+ if (!obj->tdb) {
+ PyErr_SetString(py_tdb_error, "tdb object has been closed");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "s#", &key.dptr, &key.dsize))
+ return NULL;
+
+ result = tdb_chainlock(obj->tdb, key);
+
+ return PyInt_FromLong(result != -1);
+}
+
+PyObject *py_tdb_hnd_chainunlock(PyObject *self, PyObject *args)
+{
+ tdb_hnd_object *obj = (tdb_hnd_object *)self;
+ TDB_DATA key;
+ int result;
+
+ if (!obj->tdb) {
+ PyErr_SetString(py_tdb_error, "tdb object has been closed");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "s#", &key.dptr, &key.dsize))
+ return NULL;
+
+ result = tdb_chainunlock(obj->tdb, key);
+
+ return PyInt_FromLong(result != -1);
+}
+
+PyObject *py_tdb_hnd_lock_bystring(PyObject *self, PyObject *args)
+{
+ tdb_hnd_object *obj = (tdb_hnd_object *)self;
+ int result, timeout = 30;
+ char *s;
+
+ if (!obj->tdb) {
+ PyErr_SetString(py_tdb_error, "tdb object has been closed");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "s|i", &s, &timeout))
+ return NULL;
+
+ result = tdb_lock_bystring(obj->tdb, s, timeout);
+
+ return PyInt_FromLong(result != -1);
+}
+
+PyObject *py_tdb_hnd_unlock_bystring(PyObject *self, PyObject *args)
+{
+ tdb_hnd_object *obj = (tdb_hnd_object *)self;
+ char *s;
+
+ if (!obj->tdb) {
+ PyErr_SetString(py_tdb_error, "tdb object has been closed");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "s", &s))
+ return NULL;
+
+ tdb_unlock_bystring(obj->tdb, s);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/*
+ * Method dispatch table for this module
+ */
+
+static PyMethodDef tdb_methods[] = {
+ { "open", (PyCFunction)py_tdb_open, METH_VARARGS | METH_KEYWORDS },
+ { "close", (PyCFunction)py_tdb_close, METH_VARARGS },
+ { NULL }
+};
+
+/*
+ * Methods on a tdb object
+ */
+
+static PyMethodDef tdb_hnd_methods[] = {
+ { "keys", (PyCFunction)py_tdb_hnd_keys, METH_VARARGS },
+ { "has_key", (PyCFunction)py_tdb_hnd_has_key, METH_VARARGS },
+ { "first_key", (PyCFunction)py_tdb_hnd_first_key, METH_VARARGS },
+ { "next_key", (PyCFunction)py_tdb_hnd_next_key, METH_VARARGS },
+ { "lock_all", (PyCFunction)py_tdb_hnd_lock_all, METH_VARARGS },
+ { "unlock_all", (PyCFunction)py_tdb_hnd_unlock_all, METH_VARARGS },
+ { "lock", (PyCFunction)py_tdb_hnd_lock, METH_VARARGS },
+ { "unlock", (PyCFunction)py_tdb_hnd_unlock, METH_VARARGS },
+ { "traverse", (PyCFunction)py_tdb_hnd_traverse, METH_VARARGS | METH_KEYWORDS },
+ { "chainlock", (PyCFunction)py_tdb_hnd_chainlock, METH_VARARGS | METH_KEYWORDS },
+ { "chainunlock", (PyCFunction)py_tdb_hnd_chainunlock, METH_VARARGS | METH_KEYWORDS },
+ { "lock_bystring", (PyCFunction)py_tdb_hnd_lock_bystring, METH_VARARGS | METH_KEYWORDS },
+ { "unlock_bystring", (PyCFunction)py_tdb_hnd_unlock_bystring, METH_VARARGS | METH_KEYWORDS },
+ { NULL }
+};
+
+/* Deallocate a tdb handle object */
+
+static void tdb_hnd_dealloc(PyObject* self)
+{
+ tdb_hnd_object *hnd = (tdb_hnd_object *)self;
+
+ if (hnd->tdb) {
+ tdb_close(hnd->tdb);
+ hnd->tdb = NULL;
+ }
+}
+
+/* Return tdb handle attributes */
+
+static PyObject *tdb_hnd_getattr(PyObject *self, char *attrname)
+{
+ return Py_FindMethod(tdb_hnd_methods, self, attrname);
+}
+
+static char tdb_hnd_type_doc[] =
+"Python wrapper for tdb.";
+
+PyTypeObject tdb_hnd_type = {
+ PyObject_HEAD_INIT(NULL)
+ 0,
+ "tdb",
+ sizeof(tdb_hnd_object),
+ 0,
+ tdb_hnd_dealloc, /* tp_dealloc*/
+ 0, /* tp_print*/
+ tdb_hnd_getattr, /* tp_getattr*/
+ 0, /* tp_setattr*/
+ 0, /* tp_compare*/
+ 0, /* tp_repr*/
+ 0, /* tp_as_number*/
+ 0, /* tp_as_sequence*/
+ &tdb_mapping, /* tp_as_mapping*/
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ tdb_hnd_type_doc, /* tp_doc */
+};
+
+/* Constants */
+
+static struct const_vals {
+ char *name;
+ uint32 value;
+} module_const_vals[] = {
+
+ /* Flags for tdb_open() */
+
+ { "TDB_DEFAULT", TDB_DEFAULT },
+ { "TDB_CLEAR_IF_FIRST", TDB_CLEAR_IF_FIRST },
+ { "TDB_INTERNAL", TDB_INTERNAL },
+ { "TDB_NOLOCK", TDB_NOLOCK },
+ { "TDB_NOMMAP", TDB_NOMMAP },
+ { "TDB_CONVERT", TDB_CONVERT },
+ { "TDB_BIGENDIAN", TDB_BIGENDIAN },
+
+ { NULL },
+};
+
+static void const_init(PyObject *dict)
+{
+ struct const_vals *tmp;
+ PyObject *obj;
+
+ for (tmp = module_const_vals; tmp->name; tmp++) {
+ obj = PyInt_FromLong(tmp->value);
+ PyDict_SetItemString(dict, tmp->name, obj);
+ Py_DECREF(obj);
+ }
+}
+
+/* Module initialisation */
+
+void inittdb(void)
+{
+ PyObject *module, *dict;
+
+ /* Initialise module */
+
+ module = Py_InitModule("tdb", tdb_methods);
+ dict = PyModule_GetDict(module);
+
+ py_tdb_error = PyErr_NewException("tdb.error", NULL, NULL);
+ PyDict_SetItemString(dict, "error", py_tdb_error);
+
+ /* Initialise policy handle object */
+
+ tdb_hnd_type.ob_type = &PyType_Type;
+
+ PyDict_SetItemString(dict, "tdb.hnd",
+ (PyObject *)&tdb_hnd_type);
+
+ /* Initialise constants */
+
+ const_init(dict);
+}
diff --git a/source/python/py_tdb.h b/source/python/py_tdb.h
new file mode 100644
index 00000000000..69f251c8c1f
--- /dev/null
+++ b/source/python/py_tdb.h
@@ -0,0 +1,26 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _PY_TDB_H
+#define _PY_TDB_H
+
+#include "python/py_common.h"
+
+#endif /* _PY_TDB_H */
diff --git a/source/python/py_tdbpack.c b/source/python/py_tdbpack.c
new file mode 100644
index 00000000000..4fa97af8a3c
--- /dev/null
+++ b/source/python/py_tdbpack.c
@@ -0,0 +1,721 @@
+/* -*- c-file-style: "python"; indent-tabs-mode: nil; -*-
+
+ Python wrapper for Samba tdb pack/unpack functions
+ Copyright (C) Martin Pool 2002, 2003
+
+
+ NOTE PYTHON STYLE GUIDE
+ http://www.python.org/peps/pep-0007.html
+
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "Python.h"
+
+/* This symbol is used in both config.h and Python.h which causes an
+ annoying compiler warning. */
+
+#ifdef HAVE_FSTAT
+#undef HAVE_FSTAT
+#endif
+
+/* This module is supposed to be standalone, however for portability
+ it would be good to use the FUNCTION_MACRO preprocessor define. */
+
+#include "include/config.h"
+
+#ifdef HAVE_FUNCTION_MACRO
+#define FUNCTION_MACRO (__FUNCTION__)
+#else
+#define FUNCTION_MACRO (__FILE__)
+#endif
+
+static PyObject * pytdbpack_number(char ch, PyObject *val_iter, PyObject *packed_list);
+static PyObject * pytdbpack_str(char ch,
+ PyObject *val_iter, PyObject *packed_list,
+ const char *encoding);
+static PyObject * pytdbpack_buffer(PyObject *val_iter, PyObject *packed_list);
+
+static PyObject *pytdbunpack_item(char, char **pbuf, int *plen, PyObject *);
+
+static PyObject *pytdbpack_data(const char *format_str,
+ PyObject *val_seq,
+ PyObject *val_list);
+
+static PyObject *
+pytdbunpack_string(char **pbuf, int *plen, const char *encoding);
+
+static void pack_le_uint32(unsigned long val_long, unsigned char *pbuf);
+
+
+static PyObject *pytdbpack_bad_type(char ch,
+ const char *expected,
+ PyObject *val_obj);
+
+static const char * pytdbpack_docstring =
+"Convert between Python values and Samba binary encodings.\n"
+"\n"
+"This module is conceptually similar to the standard 'struct' module, but it\n"
+"uses both a different binary format and a different description string.\n"
+"\n"
+"Samba's encoding is based on that used inside DCE-RPC and SMB: a\n"
+"little-endian, unpadded, non-self-describing binary format. It is intended\n"
+"that these functions be as similar as possible to the routines in Samba's\n"
+"tdb/tdbutil module, with appropriate adjustments for Python datatypes.\n"
+"\n"
+"Python strings are used to specify the format of data to be packed or\n"
+"unpacked.\n"
+"\n"
+"String encodings are implied by the database format: they may be either DOS\n"
+"codepage (currently hardcoded to 850), or Unix codepage (currently hardcoded\n"
+"to be the same as the default Python encoding).\n"
+"\n"
+"tdbpack format strings:\n"
+"\n"
+" 'f': NUL-terminated string in codepage iso8859-1\n"
+" \n"
+" 'P': same as 'f'\n"
+"\n"
+" 'F': NUL-terminated string in iso-8859-1\n"
+"\n"
+" 'd': 4 byte little-endian unsigned number\n"
+"\n"
+" 'w': 2 byte little-endian unsigned number\n"
+"\n"
+" 'P': \"Pointer\" value -- in the subset of DCERPC used by Samba, this is\n"
+" really just an \"exists\" or \"does not exist\" flag. The boolean\n"
+" value of the Python object is used.\n"
+" \n"
+" 'B': 4-byte LE length, followed by that many bytes of binary data.\n"
+" Corresponds to a Python integer giving the length, followed by a byte\n"
+" string of the appropriate length.\n"
+"\n"
+" '$': Special flag indicating that the preceding format code should be\n"
+" repeated while data remains. This is only supported for unpacking.\n"
+"\n"
+" Every code corresponds to a single Python object, except 'B' which\n"
+" corresponds to two values (length and contents), and '$', which produces\n"
+" however many make sense.\n";
+
+static char const pytdbpack_doc[] =
+"pack(format, values) -> buffer\n"
+"Pack Python objects into Samba binary format according to format string.\n"
+"\n"
+"arguments:\n"
+" format -- string of tdbpack format characters\n"
+" values -- sequence of value objects corresponding 1:1 to format characters\n"
+"\n"
+"returns:\n"
+" buffer -- string containing packed data\n"
+"\n"
+"raises:\n"
+" IndexError -- if there are too few values for the format\n"
+" ValueError -- if any of the format characters is illegal\n"
+" TypeError -- if the format is not a string, or values is not a sequence,\n"
+" or any of the values is of the wrong type for the corresponding\n"
+" format character\n"
+"\n"
+"notes:\n"
+" For historical reasons, it is not an error to pass more values than are consumed\n"
+" by the format.\n";
+
+
+static char const pytdbunpack_doc[] =
+"unpack(format, buffer) -> (values, rest)\n"
+"Unpack Samba binary data according to format string.\n"
+"\n"
+"arguments:\n"
+" format -- string of tdbpack characters\n"
+" buffer -- string of packed binary data\n"
+"\n"
+"returns:\n"
+" 2-tuple of:\n"
+" values -- sequence of values corresponding 1:1 to format characters\n"
+" rest -- string containing data that was not decoded, or '' if the\n"
+" whole string was consumed\n"
+"\n"
+"raises:\n"
+" IndexError -- if there is insufficient data in the buffer for the\n"
+" format (or if the data is corrupt and contains a variable-length\n"
+" field extending past the end)\n"
+" ValueError -- if any of the format characters is illegal\n"
+"\n"
+"notes:\n"
+" Because unconsumed data is returned, you can feed it back in to the\n"
+" unpacker to extract further fields. Alternatively, if you wish to modify\n"
+" some fields near the start of the data, you may be able to save time by\n"
+" only unpacking and repacking the necessary part.\n";
+
+
+const char *pytdb_dos_encoding = "cp850";
+
+/* NULL, meaning that the Samba default encoding *must* be the same as the
+ Python default encoding. */
+const char *pytdb_unix_encoding = NULL;
+
+
+/*
+ * Pack objects to bytes.
+ *
+ * All objects are first individually encoded onto a list, and then the list
+ * of strings is concatenated. This is faster than concatenating strings,
+ * and reasonably simple to code.
+ */
+static PyObject *
+pytdbpack(PyObject *self,
+ PyObject *args)
+{
+ char *format_str;
+ PyObject *val_seq, *val_iter = NULL,
+ *packed_list = NULL, *packed_str = NULL,
+ *empty_str = NULL;
+
+ /* TODO: Test passing wrong types or too many arguments */
+ if (!PyArg_ParseTuple(args, "sO", &format_str, &val_seq))
+ return NULL;
+
+ if (!(val_iter = PyObject_GetIter(val_seq)))
+ goto out;
+
+ /* Create list to hold strings until we're done, then join them all. */
+ if (!(packed_list = PyList_New(0)))
+ goto out;
+
+ if (!pytdbpack_data(format_str, val_iter, packed_list))
+ goto out;
+
+ /* this function is not officially documented but it works */
+ if (!(empty_str = PyString_InternFromString("")))
+ goto out;
+
+ packed_str = _PyString_Join(empty_str, packed_list);
+
+ out:
+ Py_XDECREF(empty_str);
+ Py_XDECREF(val_iter);
+ Py_XDECREF(packed_list);
+
+ return packed_str;
+}
+
+
+/*
+ Pack data according to FORMAT_STR from the elements of VAL_SEQ into
+ PACKED_BUF.
+
+ The string has already been checked out, so we know that VAL_SEQ is large
+ enough to hold the packed data, and that there are enough value items.
+ (However, their types may not have been thoroughly checked yet.)
+
+ In addition, val_seq is a Python Fast sequence.
+
+ Returns NULL for error (with exception set), or None.
+*/
+PyObject *
+pytdbpack_data(const char *format_str,
+ PyObject *val_iter,
+ PyObject *packed_list)
+{
+ int format_i, val_i = 0;
+
+ for (format_i = 0, val_i = 0; format_str[format_i]; format_i++) {
+ char ch = format_str[format_i];
+
+ switch (ch) {
+ /* dispatch to the appropriate packer for this type,
+ which should pull things off the iterator, and
+ append them to the packed_list */
+ case 'w':
+ case 'd':
+ case 'p':
+ if (!(packed_list = pytdbpack_number(ch, val_iter, packed_list)))
+ return NULL;
+ break;
+
+ case 'f':
+ case 'P':
+ if (!(packed_list = pytdbpack_str(ch, val_iter, packed_list, pytdb_unix_encoding)))
+ return NULL;
+ break;
+
+ case 'B':
+ if (!(packed_list = pytdbpack_buffer(val_iter, packed_list)))
+ return NULL;
+ break;
+
+ default:
+ PyErr_Format(PyExc_ValueError,
+ "%s: format character '%c' is not supported",
+ FUNCTION_MACRO, ch);
+ return NULL;
+ }
+ }
+
+ return packed_list;
+}
+
+
+static PyObject *
+pytdbpack_number(char ch, PyObject *val_iter, PyObject *packed_list)
+{
+ unsigned long val_long;
+ PyObject *val_obj = NULL, *long_obj = NULL, *result_obj = NULL;
+ PyObject *new_list = NULL;
+ unsigned char pack_buf[4];
+
+ if (!(val_obj = PyIter_Next(val_iter)))
+ goto out;
+
+ if (!(long_obj = PyNumber_Long(val_obj))) {
+ pytdbpack_bad_type(ch, "Number", val_obj);
+ goto out;
+ }
+
+ val_long = PyLong_AsUnsignedLong(long_obj);
+ pack_le_uint32(val_long, pack_buf);
+
+ /* pack as 32-bit; if just packing a 'w' 16-bit word then only take
+ the first two bytes. */
+
+ if (!(result_obj = PyString_FromStringAndSize(pack_buf, ch == 'w' ? 2 : 4)))
+ goto out;
+
+ if (PyList_Append(packed_list, result_obj) != -1)
+ new_list = packed_list;
+
+ out:
+ Py_XDECREF(val_obj);
+ Py_XDECREF(long_obj);
+ Py_XDECREF(result_obj);
+
+ return new_list;
+}
+
+
+/*
+ * Take one string from the iterator val_iter, convert it to 8-bit, and return
+ * it.
+ *
+ * If the input is neither a string nor Unicode, an exception is raised.
+ *
+ * If the input is Unicode, then it is converted to the appropriate encoding.
+ *
+ * If the input is a String, and encoding is not null, then it is converted to
+ * Unicode using the default decoding method, and then converted to the
+ * encoding. If the encoding is NULL, then the string is written out as-is --
+ * this is used when the default Python encoding is the same as the Samba
+ * encoding.
+ *
+ * I hope this approach avoids being too fragile w.r.t. being passed either
+ * Unicode or String objects.
+ */
+static PyObject *
+pytdbpack_str(char ch,
+ PyObject *val_iter, PyObject *packed_list, const char *encoding)
+{
+ PyObject *val_obj = NULL;
+ PyObject *unicode_obj = NULL;
+ PyObject *coded_str = NULL;
+ PyObject *nul_str = NULL;
+ PyObject *new_list = NULL;
+
+ if (!(val_obj = PyIter_Next(val_iter)))
+ goto out;
+
+ if (PyUnicode_Check(val_obj)) {
+ if (!(coded_str = PyUnicode_AsEncodedString(val_obj, encoding, NULL)))
+ goto out;
+ }
+ else if (PyString_Check(val_obj) && !encoding) {
+ /* For efficiency, we assume that the Python interpreter has
+ the same default string encoding as Samba's native string
+ encoding. On the PSA, both are always 8859-1. */
+ coded_str = val_obj;
+ Py_INCREF(coded_str);
+ }
+ else if (PyString_Check(val_obj)) {
+ /* String, but needs to be converted */
+ if (!(unicode_obj = PyString_AsDecodedObject(val_obj, NULL, NULL)))
+ goto out;
+ if (!(coded_str = PyUnicode_AsEncodedString(unicode_obj, encoding, NULL)))
+ goto out;
+ }
+ else {
+ pytdbpack_bad_type(ch, "String or Unicode", val_obj);
+ goto out;
+ }
+
+ if (!nul_str)
+ /* this is constant and often-used; hold it forever */
+ if (!(nul_str = PyString_FromStringAndSize("", 1)))
+ goto out;
+
+ if ((PyList_Append(packed_list, coded_str) != -1)
+ && (PyList_Append(packed_list, nul_str) != -1))
+ new_list = packed_list;
+
+ out:
+ Py_XDECREF(val_obj);
+ Py_XDECREF(unicode_obj);
+ Py_XDECREF(coded_str);
+
+ return new_list;
+}
+
+
+/*
+ * Pack (LENGTH, BUFFER) pair onto the list.
+ *
+ * The buffer must already be a String, not Unicode, because it contains 8-bit
+ * untranslated data. In some cases it will actually be UTF_16_LE data.
+ */
+static PyObject *
+pytdbpack_buffer(PyObject *val_iter, PyObject *packed_list)
+{
+ PyObject *val_obj;
+ PyObject *new_list = NULL;
+
+ /* pull off integer and stick onto list */
+ if (!(packed_list = pytdbpack_number('d', val_iter, packed_list)))
+ return NULL;
+
+ /* this assumes that the string is the right length; the old code did
+ the same. */
+ if (!(val_obj = PyIter_Next(val_iter)))
+ return NULL;
+
+ if (!PyString_Check(val_obj)) {
+ pytdbpack_bad_type('B', "String", val_obj);
+ goto out;
+ }
+
+ if (PyList_Append(packed_list, val_obj) != -1)
+ new_list = packed_list;
+
+ out:
+ Py_XDECREF(val_obj);
+ return new_list;
+}
+
+
+static PyObject *pytdbpack_bad_type(char ch,
+ const char *expected,
+ PyObject *val_obj)
+{
+ PyObject *r = PyObject_Repr(val_obj);
+ if (!r)
+ return NULL;
+ PyErr_Format(PyExc_TypeError,
+ "tdbpack: format '%c' requires %s, not %s",
+ ch, expected, PyString_AS_STRING(r));
+ Py_DECREF(r);
+ return val_obj;
+}
+
+
+/*
+ XXX: glib and Samba have quicker macro for doing the endianness conversions,
+ but I don't know of one in plain libc, and it's probably not a big deal. I
+ realize this is kind of dumb because we'll almost always be on x86, but
+ being safe is important.
+*/
+static void pack_le_uint32(unsigned long val_long, unsigned char *pbuf)
+{
+ pbuf[0] = val_long & 0xff;
+ pbuf[1] = (val_long >> 8) & 0xff;
+ pbuf[2] = (val_long >> 16) & 0xff;
+ pbuf[3] = (val_long >> 24) & 0xff;
+}
+
+
+static void pack_bytes(long len, const char *from,
+ unsigned char **pbuf)
+{
+ memcpy(*pbuf, from, len);
+ (*pbuf) += len;
+}
+
+
+
+static PyObject *
+pytdbunpack(PyObject *self,
+ PyObject *args)
+{
+ char *format_str, *packed_str, *ppacked;
+ PyObject *val_list = NULL, *ret_tuple = NULL;
+ PyObject *rest_string = NULL;
+ int format_len, packed_len;
+ char last_format = '#'; /* invalid */
+ int i;
+
+ /* get arguments */
+ if (!PyArg_ParseTuple(args, "ss#", &format_str, &packed_str, &packed_len))
+ return NULL;
+
+ format_len = strlen(format_str);
+
+ /* Allocate list to hold results. Initially empty, and we append
+ results as we go along. */
+ val_list = PyList_New(0);
+ if (!val_list)
+ goto failed;
+ ret_tuple = PyTuple_New(2);
+ if (!ret_tuple)
+ goto failed;
+
+ /* For every object, unpack. */
+ for (ppacked = packed_str, i = 0; i < format_len && format_str[i] != '$'; i++) {
+ last_format = format_str[i];
+ /* packed_len is reduced in place */
+ if (!pytdbunpack_item(format_str[i], &ppacked, &packed_len, val_list))
+ goto failed;
+ }
+
+ /* If the last character was '$', keep going until out of space */
+ if (format_str[i] == '$') {
+ if (i == 0) {
+ PyErr_Format(PyExc_ValueError,
+ "%s: '$' may not be first character in format",
+ FUNCTION_MACRO);
+ return NULL;
+ }
+ while (packed_len > 0)
+ if (!pytdbunpack_item(last_format, &ppacked, &packed_len, val_list))
+ goto failed;
+ }
+
+ /* save leftovers for next time */
+ rest_string = PyString_FromStringAndSize(ppacked, packed_len);
+ if (!rest_string)
+ goto failed;
+
+ /* return (values, rest) tuple; give up references to them */
+ PyTuple_SET_ITEM(ret_tuple, 0, val_list);
+ val_list = NULL;
+ PyTuple_SET_ITEM(ret_tuple, 1, rest_string);
+ val_list = NULL;
+ return ret_tuple;
+
+ failed:
+ /* handle failure: deallocate anything. XDECREF forms handle NULL
+ pointers for objects that haven't been allocated yet. */
+ Py_XDECREF(val_list);
+ Py_XDECREF(ret_tuple);
+ Py_XDECREF(rest_string);
+ return NULL;
+}
+
+
+static void
+pytdbunpack_err_too_short(void)
+{
+ PyErr_Format(PyExc_IndexError,
+ "%s: data too short for unpack format", FUNCTION_MACRO);
+}
+
+
+static PyObject *
+pytdbunpack_uint32(char **pbuf, int *plen)
+{
+ unsigned long v;
+ unsigned char *b;
+
+ if (*plen < 4) {
+ pytdbunpack_err_too_short();
+ return NULL;
+ }
+
+ b = *pbuf;
+ v = b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24;
+
+ (*pbuf) += 4;
+ (*plen) -= 4;
+
+ return PyLong_FromUnsignedLong(v);
+}
+
+
+static PyObject *pytdbunpack_int16(char **pbuf, int *plen)
+{
+ long v;
+ unsigned char *b;
+
+ if (*plen < 2) {
+ pytdbunpack_err_too_short();
+ return NULL;
+ }
+
+ b = *pbuf;
+ v = b[0] | b[1]<<8;
+
+ (*pbuf) += 2;
+ (*plen) -= 2;
+
+ return PyInt_FromLong(v);
+}
+
+
+static PyObject *
+pytdbunpack_string(char **pbuf, int *plen, const char *encoding)
+{
+ int len;
+ char *nul_ptr, *start;
+
+ start = *pbuf;
+
+ nul_ptr = memchr(start, '\0', *plen);
+ if (!nul_ptr) {
+ pytdbunpack_err_too_short();
+ return NULL;
+ }
+
+ len = nul_ptr - start;
+
+ *pbuf += len + 1; /* skip \0 */
+ *plen -= len + 1;
+
+ return PyString_Decode(start, len, encoding, NULL);
+}
+
+
+static PyObject *
+pytdbunpack_buffer(char **pbuf, int *plen, PyObject *val_list)
+{
+ /* first get 32-bit len */
+ long slen;
+ unsigned char *b;
+ unsigned char *start;
+ PyObject *str_obj = NULL, *len_obj = NULL;
+
+ if (*plen < 4) {
+ pytdbunpack_err_too_short();
+ return NULL;
+ }
+
+ b = *pbuf;
+ slen = b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24;
+
+ if (slen < 0) { /* surely you jest */
+ PyErr_Format(PyExc_ValueError,
+ "%s: buffer seems to have negative length", FUNCTION_MACRO);
+ return NULL;
+ }
+
+ (*pbuf) += 4;
+ (*plen) -= 4;
+ start = *pbuf;
+
+ if (*plen < slen) {
+ PyErr_Format(PyExc_IndexError,
+ "%s: not enough data to unpack buffer: "
+ "need %d bytes, have %d", FUNCTION_MACRO,
+ (int) slen, *plen);
+ return NULL;
+ }
+
+ (*pbuf) += slen;
+ (*plen) -= slen;
+
+ if (!(len_obj = PyInt_FromLong(slen)))
+ goto failed;
+
+ if (PyList_Append(val_list, len_obj) == -1)
+ goto failed;
+
+ if (!(str_obj = PyString_FromStringAndSize(start, slen)))
+ goto failed;
+
+ if (PyList_Append(val_list, str_obj) == -1)
+ goto failed;
+
+ Py_DECREF(len_obj);
+ Py_DECREF(str_obj);
+
+ return val_list;
+
+ failed:
+ Py_XDECREF(len_obj); /* handles NULL */
+ Py_XDECREF(str_obj);
+ return NULL;
+}
+
+
+/* Unpack a single field from packed data, according to format character CH.
+ Remaining data is at *PBUF, of *PLEN.
+
+ *PBUF is advanced, and *PLEN reduced to reflect the amount of data that has
+ been consumed.
+
+ Returns a reference to None, or NULL for failure.
+*/
+static PyObject *pytdbunpack_item(char ch,
+ char **pbuf,
+ int *plen,
+ PyObject *val_list)
+{
+ PyObject *unpacked;
+
+ if (ch == 'w') { /* 16-bit int */
+ unpacked = pytdbunpack_int16(pbuf, plen);
+ }
+ else if (ch == 'd' || ch == 'p') { /* 32-bit int */
+ /* pointers can just come through as integers */
+ unpacked = pytdbunpack_uint32(pbuf, plen);
+ }
+ else if (ch == 'f' || ch == 'P') { /* nul-term string */
+ unpacked = pytdbunpack_string(pbuf, plen, pytdb_unix_encoding);
+ }
+ else if (ch == 'B') { /* length, buffer */
+ return pytdbunpack_buffer(pbuf, plen, val_list);
+ }
+ else {
+ PyErr_Format(PyExc_ValueError,
+ "%s: format character '%c' is not supported",
+ FUNCTION_MACRO, ch);
+
+ return NULL;
+ }
+
+ /* otherwise OK */
+ if (!unpacked)
+ return NULL;
+
+ if (PyList_Append(val_list, unpacked) == -1)
+ val_list = NULL;
+
+ /* PyList_Append takes a new reference to the inserted object.
+ Therefore, we no longer need the original reference. */
+ Py_DECREF(unpacked);
+
+ return val_list;
+}
+
+
+
+
+
+
+static PyMethodDef pytdbpack_methods[] = {
+ { "pack", pytdbpack, METH_VARARGS, (char *) pytdbpack_doc },
+ { "unpack", pytdbunpack, METH_VARARGS, (char *) pytdbunpack_doc },
+};
+
+DL_EXPORT(void)
+inittdbpack(void)
+{
+ Py_InitModule3("tdbpack", pytdbpack_methods,
+ (char *) pytdbpack_docstring);
+}
diff --git a/source/python/py_winbind.c b/source/python/py_winbind.c
new file mode 100644
index 00000000000..130f78d7e12
--- /dev/null
+++ b/source/python/py_winbind.c
@@ -0,0 +1,800 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Python wrapper for winbind client functions.
+
+ Copyright (C) Tim Potter 2002-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "py_winbind.h"
+
+/*
+ * Exceptions raised by this module
+ */
+
+PyObject *winbind_error; /* A winbind call returned WINBINDD_ERROR */
+
+/* Prototypes from common.h */
+
+NSS_STATUS winbindd_request(int req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response);
+
+/*
+ * Name <-> SID conversion
+ */
+
+/* Convert a name to a sid */
+
+static PyObject *py_name_to_sid(PyObject *self, PyObject *args)
+
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ PyObject *result;
+ char *name, *p;
+ const char *sep;
+
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return NULL;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ sep = lp_winbind_separator();
+
+ if ((p = strchr(name, sep[0]))) {
+ *p = 0;
+ fstrcpy(request.data.name.dom_name, name);
+ fstrcpy(request.data.name.name, p + 1);
+ } else {
+ fstrcpy(request.data.name.dom_name, lp_workgroup());
+ fstrcpy(request.data.name.name, name);
+ }
+
+ if (winbindd_request(WINBINDD_LOOKUPNAME, &request, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ result = PyString_FromString(response.data.sid.sid);
+
+ return result;
+}
+
+/* Convert a sid to a name */
+
+static PyObject *py_sid_to_name(PyObject *self, PyObject *args)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ PyObject *result;
+ char *sid, *name;
+
+ if (!PyArg_ParseTuple(args, "s", &sid))
+ return NULL;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.data.sid, sid);
+
+ if (winbindd_request(WINBINDD_LOOKUPSID, &request, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ asprintf(&name, "%s%s%s", response.data.name.dom_name,
+ lp_winbind_separator(), response.data.name.name);
+
+ result = PyString_FromString(name);
+
+ free(name);
+
+ return result;
+}
+
+/*
+ * Enumerate users/groups
+ */
+
+/* Enumerate domain users */
+
+static PyObject *py_enum_domain_users(PyObject *self, PyObject *args)
+{
+ struct winbindd_response response;
+ PyObject *result;
+
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+
+ ZERO_STRUCT(response);
+
+ if (winbindd_request(WINBINDD_LIST_USERS, NULL, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ result = PyList_New(0);
+
+ if (response.extra_data) {
+ const char *extra_data = response.extra_data;
+ fstring name;
+
+ while (next_token(&extra_data, name, ",", sizeof(fstring)))
+ PyList_Append(result, PyString_FromString(name));
+ }
+
+ return result;
+}
+
+/* Enumerate domain groups */
+
+static PyObject *py_enum_domain_groups(PyObject *self, PyObject *args)
+{
+ struct winbindd_response response;
+ PyObject *result = NULL;
+
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+
+ ZERO_STRUCT(response);
+
+ if (winbindd_request(WINBINDD_LIST_GROUPS, NULL, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ result = PyList_New(0);
+
+ if (response.extra_data) {
+ const char *extra_data = response.extra_data;
+ fstring name;
+
+ while (next_token(&extra_data, name, ",", sizeof(fstring)))
+ PyList_Append(result, PyString_FromString(name));
+ }
+
+ return result;
+}
+
+/*
+ * Miscellaneous domain related
+ */
+
+/* Enumerate domain groups */
+
+static PyObject *py_enum_trust_dom(PyObject *self, PyObject *args)
+{
+ struct winbindd_response response;
+ PyObject *result = NULL;
+
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+
+ ZERO_STRUCT(response);
+
+ if (winbindd_request(WINBINDD_LIST_TRUSTDOM, NULL, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ result = PyList_New(0);
+
+ if (response.extra_data) {
+ const char *extra_data = response.extra_data;
+ fstring name;
+
+ while (next_token(&extra_data, name, ",", sizeof(fstring)))
+ PyList_Append(result, PyString_FromString(name));
+ }
+
+ return result;
+}
+
+/* Check machine account password */
+
+static PyObject *py_check_secret(PyObject *self, PyObject *args)
+{
+ struct winbindd_response response;
+
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+
+ ZERO_STRUCT(response);
+
+ if (winbindd_request(WINBINDD_CHECK_MACHACC, NULL, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ return PyInt_FromLong(response.data.num_entries);
+}
+
+/*
+ * Return a dictionary consisting of all the winbind related smb.conf
+ * parameters. This is stored in the module object.
+ */
+
+static PyObject *py_config_dict(void)
+{
+ PyObject *result;
+ uid_t ulow, uhi;
+ gid_t glow, ghi;
+
+ if (!(result = PyDict_New()))
+ return NULL;
+
+ /* Various string parameters */
+
+ PyDict_SetItemString(result, "workgroup",
+ PyString_FromString(lp_workgroup()));
+
+ PyDict_SetItemString(result, "separator",
+ PyString_FromString(lp_winbind_separator()));
+
+ PyDict_SetItemString(result, "template_homedir",
+ PyString_FromString(lp_template_homedir()));
+
+ PyDict_SetItemString(result, "template_shell",
+ PyString_FromString(lp_template_shell()));
+
+ /* idmap uid/gid range */
+
+ if (lp_idmap_uid(&ulow, &uhi)) {
+ PyDict_SetItemString(result, "uid_low", PyInt_FromLong(ulow));
+ PyDict_SetItemString(result, "uid_high", PyInt_FromLong(uhi));
+ }
+
+ if (lp_idmap_gid(&glow, &ghi)) {
+ PyDict_SetItemString(result, "gid_low", PyInt_FromLong(glow));
+ PyDict_SetItemString(result, "gid_high", PyInt_FromLong(ghi));
+ }
+
+ return result;
+}
+
+/*
+ * ID mapping
+ */
+
+/* Convert a uid to a SID */
+
+static PyObject *py_uid_to_sid(PyObject *self, PyObject *args)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int id;
+
+ if (!PyArg_ParseTuple(args, "i", &id))
+ return NULL;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.data.uid = id;
+
+ if (winbindd_request(WINBINDD_UID_TO_SID, &request, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ return PyString_FromString(response.data.sid.sid);
+}
+
+/* Convert a gid to a SID */
+
+static PyObject *py_gid_to_sid(PyObject *self, PyObject *args)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int id;
+
+ if (!PyArg_ParseTuple(args, "i", &id))
+ return NULL;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.data.gid = id;
+
+ if (winbindd_request(WINBINDD_GID_TO_SID, &request, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ return PyString_FromString(response.data.sid.sid);
+}
+
+/* Convert a sid to a uid */
+
+static PyObject *py_sid_to_uid(PyObject *self, PyObject *args)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ char *sid;
+
+ if (!PyArg_ParseTuple(args, "s", &sid))
+ return NULL;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.data.sid, sid);
+
+ if (winbindd_request(WINBINDD_SID_TO_UID, &request, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ return PyInt_FromLong(response.data.uid);
+}
+
+/* Convert a sid to a gid */
+
+static PyObject *py_sid_to_gid(PyObject *self, PyObject *args)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ char *sid;
+
+ if (!PyArg_ParseTuple(args, "s", &sid))
+ return NULL;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.data.sid, sid);
+
+ if (winbindd_request(WINBINDD_SID_TO_GID, &request, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ return PyInt_FromLong(response.data.gid);
+}
+
+/*
+ * PAM authentication functions
+ */
+
+/* Plaintext authentication */
+
+static PyObject *py_auth_plaintext(PyObject *self, PyObject *args)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ char *username, *password;
+
+ if (!PyArg_ParseTuple(args, "ss", &username, &password))
+ return NULL;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.data.auth.user, username);
+ fstrcpy(request.data.auth.pass, password);
+
+ if (winbindd_request(WINBINDD_PAM_AUTH, &request, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ return PyInt_FromLong(response.data.auth.nt_status);
+}
+
+/* Challenge/response authentication */
+
+static PyObject *py_auth_crap(PyObject *self, PyObject *args, PyObject *kw)
+{
+ static char *kwlist[] =
+ {"username", "password", "use_lm_hash", "use_nt_hash", NULL };
+ struct winbindd_request request;
+ struct winbindd_response response;
+ char *username, *password;
+ int use_lm_hash = 1, use_nt_hash = 1;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "ss|ii", kwlist, &username, &password,
+ &use_lm_hash, &use_nt_hash))
+ return NULL;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (push_utf8_fstring(request.data.auth_crap.user, username) == -1) {
+ PyErr_SetString(winbind_error, "unable to create utf8 string");
+ return NULL;
+ }
+
+ generate_random_buffer(request.data.auth_crap.chal, 8, False);
+
+ if (use_lm_hash) {
+ SMBencrypt((uchar *)password, request.data.auth_crap.chal,
+ (uchar *)request.data.auth_crap.lm_resp);
+ request.data.auth_crap.lm_resp_len = 24;
+ }
+
+ if (use_nt_hash) {
+ SMBNTencrypt((uchar *)password, request.data.auth_crap.chal,
+ (uchar *)request.data.auth_crap.nt_resp);
+ request.data.auth_crap.nt_resp_len = 24;
+ }
+
+ if (winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ return PyInt_FromLong(response.data.auth.nt_status);
+}
+
+#if 0 /* Include when auth_smbd merged to HEAD */
+
+/* Challenge/response authentication, with secret */
+
+static PyObject *py_auth_smbd(PyObject *self, PyObject *args, PyObject *kw)
+{
+ static char *kwlist[] =
+ {"username", "password", "use_lm_hash", "use_nt_hash", NULL };
+ struct winbindd_request request;
+ struct winbindd_response response;
+ char *username, *password;
+ int use_lm_hash = 1, use_nt_hash = 1;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "ss|ii", kwlist, &username, &password,
+ &use_lm_hash, &use_nt_hash))
+ return NULL;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (push_utf8_fstring(request.data.auth_crap.user, username) == -1) {
+ PyErr_SetString("unable to create utf8 string");
+ return NULL;
+ }
+
+ generate_random_buffer(request.data.smbd_auth_crap.chal, 8, False);
+
+ if (use_lm_hash) {
+ SMBencrypt((uchar *)password,
+ request.data.smbd_auth_crap.chal,
+ (uchar *)request.data.smbd_auth_crap.lm_resp);
+ request.data.smbd_auth_crap.lm_resp_len = 24;
+ }
+
+ if (use_nt_hash) {
+ SMBNTencrypt((uchar *)password,
+ request.data.smbd_auth_crap.chal,
+ (uchar *)request.data.smbd_auth_crap.nt_resp);
+ request.data.smbd_auth_crap.nt_resp_len = 24;
+ }
+
+ if (!secrets_fetch_trust_account_password(
+ lp_workgroup(), request.data.smbd_auth_crap.proof, NULL)) {
+ PyErr_SetString(
+ winbind_error, "unable to fetch domain secret");
+ return NULL;
+ }
+
+
+
+ if (winbindd_request(WINBINDD_SMBD_AUTH_CRAP, &request, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ return PyInt_FromLong(response.data.auth.nt_status);
+}
+
+#endif /* 0 */
+
+/* Get user info from name */
+
+static PyObject *py_getpwnam(PyObject *self, PyObject *args)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ char *username;
+ PyObject *result;
+
+ if (!PyArg_ParseTuple(args, "s", &username))
+ return NULL;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.data.username, username);
+
+ if (winbindd_request(WINBINDD_GETPWNAM, &request, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ if (!py_from_winbind_passwd(&result, &response)) {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+
+ return result;
+}
+
+/* Get user info from uid */
+
+static PyObject *py_getpwuid(PyObject *self, PyObject *args)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ uid_t uid;
+ PyObject *result;
+
+ if (!PyArg_ParseTuple(args, "i", &uid))
+ return NULL;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.data.uid = uid;
+
+ if (winbindd_request(WINBINDD_GETPWUID, &request, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ if (!py_from_winbind_passwd(&result, &response)) {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+
+ return result;
+}
+
+/*
+ * Method dispatch table
+ */
+
+static PyMethodDef winbind_methods[] = {
+
+ { "getpwnam", (PyCFunction)py_getpwnam, METH_VARARGS, "getpwnam(3)" },
+ { "getpwuid", (PyCFunction)py_getpwuid, METH_VARARGS, "getpwuid(3)" },
+
+ /* Name <-> SID conversion */
+
+ { "name_to_sid", (PyCFunction)py_name_to_sid, METH_VARARGS,
+ "name_to_sid(s) -> string\n"
+"\n"
+"Return the SID for a name.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> winbind.name_to_sid('FOO/Administrator')\n"
+"'S-1-5-21-406022937-1377575209-526660263-500' " },
+
+ { "sid_to_name", (PyCFunction)py_sid_to_name, METH_VARARGS,
+ "sid_to_name(s) -> string\n"
+"\n"
+"Return the name for a SID.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> import winbind\n"
+">>> winbind.sid_to_name('S-1-5-21-406022937-1377575209-526660263-500')\n"
+"'FOO/Administrator' " },
+
+ /* Enumerate users/groups */
+
+ { "enum_domain_users", (PyCFunction)py_enum_domain_users, METH_VARARGS,
+ "enum_domain_users() -> list of strings\n"
+"\n"
+"Return a list of domain users.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> winbind.enum_domain_users()\n"
+"['FOO/Administrator', 'FOO/anna', 'FOO/Anne Elk', 'FOO/build', \n"
+"'FOO/foo', 'FOO/foo2', 'FOO/foo3', 'FOO/Guest', 'FOO/user1', \n"
+"'FOO/whoops-ptang'] " },
+
+ { "enum_domain_groups", (PyCFunction)py_enum_domain_groups,
+ METH_VARARGS,
+ "enum_domain_groups() -> list of strings\n"
+"\n"
+"Return a list of domain groups.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> winbind.enum_domain_groups()\n"
+"['FOO/cows', 'FOO/Domain Admins', 'FOO/Domain Guests', \n"
+"'FOO/Domain Users'] " },
+
+ /* ID mapping */
+
+ { "uid_to_sid", (PyCFunction)py_uid_to_sid, METH_VARARGS,
+ "uid_to_sid(int) -> string\n"
+"\n"
+"Return the SID for a UNIX uid.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> winbind.uid_to_sid(10000) \n"
+"'S-1-5-21-406022937-1377575209-526660263-500' " },
+
+ { "gid_to_sid", (PyCFunction)py_gid_to_sid, METH_VARARGS,
+ "gid_to_sid(int) -> string\n"
+"\n"
+"Return the UNIX gid for a SID.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> winbind.gid_to_sid(10001)\n"
+"'S-1-5-21-406022937-1377575209-526660263-512' " },
+
+ { "sid_to_uid", (PyCFunction)py_sid_to_uid, METH_VARARGS,
+ "sid_to_uid(string) -> int\n"
+"\n"
+"Return the UNIX uid for a SID.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> winbind.sid_to_uid('S-1-5-21-406022937-1377575209-526660263-500')\n"
+"10000 " },
+
+ { "sid_to_gid", (PyCFunction)py_sid_to_gid, METH_VARARGS,
+ "sid_to_gid(string) -> int\n"
+"\n"
+"Return the UNIX gid corresponding to a SID.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> winbind.sid_to_gid('S-1-5-21-406022937-1377575209-526660263-512')\n"
+"10001 " },
+
+ /* Miscellaneous */
+
+ { "check_secret", (PyCFunction)py_check_secret, METH_VARARGS,
+ "check_secret() -> int\n"
+"\n"
+"Check the machine trust account password. The NT status is returned\n"
+"with zero indicating success. " },
+
+ { "enum_trust_dom", (PyCFunction)py_enum_trust_dom, METH_VARARGS,
+ "enum_trust_dom() -> list of strings\n"
+"\n"
+"Return a list of trusted domains. The domain the server is a member \n"
+"of is not included.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> winbind.enum_trust_dom()\n"
+"['NPSD-TEST2', 'SP2NDOM'] " },
+
+ /* PAM authorisation functions */
+
+ { "auth_plaintext", (PyCFunction)py_auth_plaintext, METH_VARARGS,
+ "auth_plaintext(s, s) -> int\n"
+"\n"
+"Authenticate a username and password using plaintext authentication.\n"
+"The NT status code is returned with zero indicating success." },
+
+ { "auth_crap", (PyCFunction)py_auth_crap, METH_VARARGS,
+ "auth_crap(s, s) -> int\n"
+"\n"
+"Authenticate a username and password using the challenge/response\n"
+"protocol. The NT status code is returned with zero indicating\n"
+"success." },
+
+#if 0 /* Include when smbd_auth merged to HEAD */
+
+ { "auth_smbd", (PyCFunction)py_auth_crap, METH_VARARGS,
+ "auth_smbd(s, s) -> int\n"
+"\n"
+"Authenticate a username and password using the challenge/response\n"
+"protocol but using the domain secret to prove we are root. The NT \n"
+"status code is returned with zero indicating success." },
+
+#endif
+
+ { NULL }
+};
+
+static struct const_vals {
+ char *name;
+ uint32 value;
+ char *docstring;
+} module_const_vals[] = {
+
+ /* Well known RIDs */
+
+ { "DOMAIN_USER_RID_ADMIN", DOMAIN_USER_RID_ADMIN,
+ "Well-known RID for Administrator user" },
+
+ { "DOMAIN_USER_RID_GUEST", DOMAIN_USER_RID_GUEST,
+ "Well-known RID for Guest user" },
+
+ { "DOMAIN_GROUP_RID_ADMINS", DOMAIN_GROUP_RID_ADMINS,
+ "Well-known RID for Domain Admins group" },
+
+ { "DOMAIN_GROUP_RID_USERS", DOMAIN_GROUP_RID_USERS,
+ "Well-known RID for Domain Users group" },
+
+ { "DOMAIN_GROUP_RID_GUESTS", DOMAIN_GROUP_RID_GUESTS,
+ "Well-known RID for Domain Guests group" },
+
+ { NULL }
+};
+
+static void const_init(PyObject *dict)
+{
+ struct const_vals *tmp;
+ PyObject *obj;
+
+ for (tmp = module_const_vals; tmp->name; tmp++) {
+ obj = PyInt_FromLong(tmp->value);
+ PyDict_SetItemString(dict, tmp->name, obj);
+ Py_DECREF(obj);
+ }
+}
+
+/*
+ * Module initialisation
+ */
+
+static char winbind_module__doc__[] =
+"A python extension to winbind client functions.";
+
+void initwinbind(void)
+{
+ PyObject *module, *dict;
+
+ /* Initialise module */
+
+ module = Py_InitModule3("winbind", winbind_methods,
+ winbind_module__doc__);
+
+ dict = PyModule_GetDict(module);
+
+ winbind_error = PyErr_NewException("winbind.error", NULL, NULL);
+ PyDict_SetItemString(dict, "error", winbind_error);
+
+ /* Do samba initialisation */
+
+ py_samba_init();
+
+ /* Initialise constants */
+
+ const_init(dict);
+
+ /* Insert configuration dictionary */
+
+ PyDict_SetItemString(dict, "config", py_config_dict());
+}
diff --git a/source/python/py_winbind.h b/source/python/py_winbind.h
new file mode 100644
index 00000000000..10927ea6c8f
--- /dev/null
+++ b/source/python/py_winbind.h
@@ -0,0 +1,30 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _PY_WINBIND_H
+#define _PY_WINBIND_H
+
+#include "python/py_common.h"
+
+/* The following definitions are from py_winbind_conv.c */
+
+BOOL py_from_winbind_passwd(PyObject **dict, struct winbindd_response *response);
+
+#endif /* _PY_WINBIND_H */
diff --git a/source/python/py_winbind_conv.c b/source/python/py_winbind_conv.c
new file mode 100644
index 00000000000..6e2eab59414
--- /dev/null
+++ b/source/python/py_winbind_conv.c
@@ -0,0 +1,41 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "python/py_common.h"
+#include "python/py_conv.h"
+
+/* Convert a struct passwd to a dictionary */
+
+static struct pyconv py_passwd[] = {
+ { "pw_name", PY_STRING, offsetof(struct winbindd_response, data.pw.pw_name) },
+ { "pw_passwd", PY_STRING, offsetof(struct winbindd_response, data.pw.pw_passwd) },
+ { "pw_uid", PY_UID, offsetof(struct winbindd_response, data.pw.pw_uid) },
+ { "pw_guid", PY_GID, offsetof(struct winbindd_response, data.pw.pw_gid) },
+ { "pw_gecos", PY_STRING, offsetof(struct winbindd_response, data.pw.pw_gecos) },
+ { "pw_dir", PY_STRING, offsetof(struct winbindd_response, data.pw.pw_dir) },
+ { "pw_shell", PY_STRING, offsetof(struct winbindd_response, data.pw.pw_shell) },
+ { NULL}
+};
+
+BOOL py_from_winbind_passwd(PyObject **dict, struct winbindd_response *response)
+{
+ *dict = from_struct(response, py_passwd);
+ return True;
+}
diff --git a/source/python/py_winreg.c b/source/python/py_winreg.c
new file mode 100644
index 00000000000..ce27f5c533b
--- /dev/null
+++ b/source/python/py_winreg.c
@@ -0,0 +1,82 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "python/py_winreg.h"
+
+static struct const_vals {
+ char *name;
+ uint32 value;
+} module_const_vals[] = {
+
+ /* Registry value types */
+
+ { "REG_NONE", REG_NONE },
+ { "REG_SZ", REG_SZ },
+ { "REG_EXPAND_SZ", REG_EXPAND_SZ },
+ { "REG_BINARY", REG_BINARY },
+ { "REG_DWORD", REG_DWORD },
+ { "REG_DWORD_LE", REG_DWORD_LE },
+ { "REG_DWORD_BE", REG_DWORD_BE },
+ { "REG_LINK", REG_LINK },
+ { "REG_MULTI_SZ", REG_MULTI_SZ },
+ { "REG_RESOURCE_LIST", REG_RESOURCE_LIST },
+ { "REG_FULL_RESOURCE_DESCRIPTOR", REG_FULL_RESOURCE_DESCRIPTOR },
+ { "REG_RESOURCE_REQUIREMENTS_LIST", REG_RESOURCE_REQUIREMENTS_LIST },
+
+ { NULL },
+};
+
+static void const_init(PyObject *dict)
+{
+ struct const_vals *tmp;
+ PyObject *obj;
+
+ for (tmp = module_const_vals; tmp->name; tmp++) {
+ obj = PyInt_FromLong(tmp->value);
+ PyDict_SetItemString(dict, tmp->name, obj);
+ Py_DECREF(obj);
+ }
+}
+
+/*
+ * Module initialisation
+ */
+
+static PyMethodDef winreg_methods[] = {
+ { NULL }
+};
+
+void initwinreg(void)
+{
+ PyObject *module, *dict;
+
+ /* Initialise module */
+
+ module = Py_InitModule("winreg", winreg_methods);
+ dict = PyModule_GetDict(module);
+
+ /* Initialise constants */
+
+ const_init(dict);
+
+ /* Do samba initialisation */
+
+ py_samba_init();
+}
diff --git a/source/python/py_winreg.h b/source/python/py_winreg.h
new file mode 100644
index 00000000000..95d5fc6ea95
--- /dev/null
+++ b/source/python/py_winreg.h
@@ -0,0 +1,26 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _PY_WINREG_H
+#define _PY_WINREG_H
+
+#include "python/py_common.h"
+
+#endif /* _PY_WINREG_H */
diff --git a/source/python/samba/.cvsignore b/source/python/samba/.cvsignore
new file mode 100644
index 00000000000..0d20b6487c6
--- /dev/null
+++ b/source/python/samba/.cvsignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/source/python/samba/__init__.py b/source/python/samba/__init__.py
new file mode 100644
index 00000000000..c818ca3e044
--- /dev/null
+++ b/source/python/samba/__init__.py
@@ -0,0 +1,7 @@
+"""samba
+
+Various Python modules for interfacing to Samba.
+
+Try using help() to examine their documentation.
+"""
+
diff --git a/source/python/samba/printerdata.py b/source/python/samba/printerdata.py
new file mode 100644
index 00000000000..0b53a3dfb52
--- /dev/null
+++ b/source/python/samba/printerdata.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+
+#
+# A python module that maps printerdata to a dictionary. We define
+# two classes. The printerdata class maps to Get/Set/Enum/DeletePrinterData
+# and the printerdata_ex class maps to Get/Set/Enum/DeletePrinterDataEx
+#
+
+#
+# TODO:
+#
+# - Implement __delitem__
+#
+
+from samba import spoolss
+
+class printerdata:
+ def __init__(self, host, creds = {}, access = 0x02000000):
+ # For read access, use MAXIMUM_ALLOWED_ACCESS = 0x02000000
+ # For write access, use PRINTER_ACCESS_ADMINISTER = 0x00000004
+ self.hnd = spoolss.openprinter(host, creds = creds, access = access)
+
+ def keys(self):
+ return self.hnd.enumprinterdata().keys()
+
+ def __getitem__(self, key):
+ return self.hnd.getprinterdata(key)['data']
+
+ def __setitem__(self, key, value):
+ # Store as REG_BINARY for now
+ self.hnd.setprinterdata({"key": "", "value": key, "type": 3,
+ "data": value})
+
+class printerdata_ex:
+ def __init__(self, host, creds = {}, access = 0x02000000):
+ # For read access, use MAXIMUM_ALLOWED_ACCESS = 0x02000000
+ # For write access, use PRINTER_ACCESS_ADMINISTER = 0x00000004
+ self.host = host
+ self.top_level_keys = ["PrinterDriverData", "DsSpooler", "DsDriver",
+ "DsUser"]
+ self.creds = creds
+ self.access = access
+
+ def keys(self):
+ return self.top_level_keys
+
+ def has_key(self, key):
+ for k in self.top_level_keys:
+ if k == key:
+ return 1
+ return 0
+
+ class printerdata_ex_subkey:
+ def __init__(self, host, key, creds, access):
+ self.hnd = spoolss.openprinter(host, creds, access)
+ self.key = key
+
+ def keys(self):
+ return self.hnd.enumprinterdataex(self.key).keys()
+
+ def __getitem__(self, key):
+ return self.hnd.getprinterdataex(self.key, key)['data']
+
+ def __getitem__(self, key):
+ return self.printerdata_ex_subkey(self.host, key, creds, access)
diff --git a/source/python/setup.py b/source/python/setup.py
new file mode 100755
index 00000000000..4a4f6ad3f81
--- /dev/null
+++ b/source/python/setup.py
@@ -0,0 +1,200 @@
+# -*- mode: python -*-
+#
+# Unix SMB/CIFS implementation.
+# Module packaging setup for Samba python extensions
+#
+# Copyright (C) Tim Potter, 2002-2003
+# Copyright (C) Martin Pool, 2002
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+from distutils.core import setup
+from distutils.extension import Extension
+
+import sys, string, os
+
+# The Makefile passes in environment variable $PYTHON_OBJ as being the
+# list of Samba objects. This kind of goes against the distutils.cmd
+# method of adding setup commands and will also confuse people who are
+# familiar with the python Distutils module.
+
+samba_objs = os.environ.get("PYTHON_OBJS", "")
+
+samba_cflags = os.environ.get("PYTHON_CFLAGS", "")
+
+samba_srcdir = os.environ.get("SRCDIR", "")
+
+# These variables are filled in by configure
+
+samba_libs = os.environ.get("LIBS", "")
+
+obj_list = string.split(samba_objs)
+
+# Unfortunately the samba_libs variable contains both shared libraries
+# and linker flags. The python distutils doesn't like this so we have
+# to split $samba_libs into a flags component and a library component.
+
+libraries = []
+library_dirs = []
+
+for lib in string.split(samba_libs):
+ if lib[0:2] == "-l":
+ libraries.append(lib[2:])
+ continue
+ if lib[0:2] == "-L":
+ library_dirs.append(lib[2:])
+ continue
+ if lib[0:2] == "-W":
+ # Skip linker flags
+ continue
+ print "Unknown entry '%s' in $LIBS variable passed to setup.py" % lib
+ sys.exit(1)
+
+flags_list = string.split(samba_cflags)
+
+# Invoke distutils.setup
+
+setup(
+
+ # Overview information
+
+ name = "Samba Python Extensions",
+ version = "0.1",
+ author = "Tim Potter",
+ author_email = "tpot@samba.org",
+ license = "GPL",
+
+ # Get the "samba" directory of Python source. At the moment this
+ # just contains the __init__ file that makes it work as a
+ # subpackage. This is needed even though everything else is an
+ # extension module.
+ package_dir = {"samba": os.path.join(samba_srcdir, "python", "samba")},
+ packages = ["samba"],
+
+ # Module list
+ ext_package = "samba",
+ ext_modules = [
+
+ # SPOOLSS pipe module
+
+ Extension(name = "spoolss",
+ sources = [samba_srcdir + "python/py_spoolss.c",
+ samba_srcdir + "python/py_common.c",
+ samba_srcdir + "python/py_conv.c",
+ samba_srcdir + "python/py_ntsec.c",
+ samba_srcdir + "python/py_spoolss_common.c",
+ samba_srcdir + "python/py_spoolss_forms.c",
+ samba_srcdir + "python/py_spoolss_forms_conv.c",
+ samba_srcdir + "python/py_spoolss_drivers.c",
+ samba_srcdir + "python/py_spoolss_drivers_conv.c",
+ samba_srcdir + "python/py_spoolss_printers.c",
+ samba_srcdir + "python/py_spoolss_printers_conv.c",
+ samba_srcdir + "python/py_spoolss_printerdata.c",
+ samba_srcdir + "python/py_spoolss_ports.c",
+ samba_srcdir + "python/py_spoolss_ports_conv.c",
+ samba_srcdir + "python/py_spoolss_jobs.c",
+ samba_srcdir + "python/py_spoolss_jobs_conv.c",
+ ],
+ libraries = libraries,
+ library_dirs = ["/usr/kerberos/lib"] + library_dirs,
+ extra_compile_args = flags_list,
+ extra_objects = obj_list),
+
+ # LSA pipe module
+
+ Extension(name = "lsa",
+ sources = [samba_srcdir + "python/py_lsa.c",
+ samba_srcdir + "python/py_common.c",
+ samba_srcdir + "python/py_ntsec.c"],
+ libraries = libraries,
+ library_dirs = ["/usr/kerberos/lib"] + library_dirs,
+ extra_compile_args = flags_list,
+ extra_objects = obj_list),
+
+ # SAMR pipe module
+
+ Extension(name = "samr",
+ sources = [samba_srcdir + "python/py_samr.c",
+ samba_srcdir + "python/py_conv.c",
+ samba_srcdir + "python/py_samr_conv.c",
+ samba_srcdir + "python/py_common.c"],
+ libraries = libraries,
+ library_dirs = ["/usr/kerberos/lib"] + library_dirs,
+ extra_compile_args = flags_list,
+ extra_objects = obj_list),
+
+ # winbind client module
+
+ Extension(name = "winbind",
+ sources = [samba_srcdir + "python/py_winbind.c",
+ samba_srcdir + "python/py_winbind_conv.c",
+ samba_srcdir + "python/py_conv.c",
+ samba_srcdir + "python/py_common.c"],
+ libraries = libraries,
+ library_dirs = ["/usr/kerberos/lib"] + library_dirs,
+ extra_compile_args = flags_list,
+ extra_objects = obj_list),
+
+ # WINREG pipe module
+
+ Extension(name = "winreg",
+ sources = [samba_srcdir + "python/py_winreg.c",
+ samba_srcdir + "python/py_common.c"],
+ libraries = libraries,
+ library_dirs = ["/usr/kerberos/lib"] + library_dirs,
+ extra_compile_args = flags_list,
+ extra_objects = obj_list),
+
+ # SRVSVC pipe module
+
+ Extension(name = "srvsvc",
+ sources = [samba_srcdir + "python/py_srvsvc.c",
+ samba_srcdir + "python/py_conv.c",
+ samba_srcdir + "python/py_srvsvc_conv.c",
+ samba_srcdir + "python/py_common.c"],
+ libraries = libraries,
+ library_dirs = ["/usr/kerberos/lib"] + library_dirs,
+ extra_compile_args = flags_list,
+ extra_objects = obj_list),
+
+ # tdb module
+
+ Extension(name = "tdb",
+ sources = [samba_srcdir + "python/py_tdb.c"],
+ libraries = libraries,
+ library_dirs = ["/usr/kerberos/lib"] + library_dirs,
+ extra_compile_args = flags_list,
+ extra_objects = obj_list),
+
+ # libsmb module
+
+ Extension(name = "smb",
+ sources = [samba_srcdir + "python/py_smb.c",
+ samba_srcdir + "python/py_common.c",
+ samba_srcdir + "python/py_ntsec.c"],
+ libraries = libraries,
+ library_dirs = ["/usr/kerberos/lib"] + library_dirs,
+ extra_compile_args = flags_list,
+ extra_objects = obj_list),
+
+ # tdbpack/unpack extensions. Does not actually link to any Samba
+ # code, although it implements a compatible data format.
+
+ Extension(name = "tdbpack",
+ sources = [os.path.join(samba_srcdir, "python", "py_tdbpack.c")],
+ extra_compile_args = ["-I."])
+ ],
+)
diff --git a/source/registry/.cvsignore b/source/registry/.cvsignore
new file mode 100644
index 00000000000..5f2a5c4cf75
--- /dev/null
+++ b/source/registry/.cvsignore
@@ -0,0 +1,2 @@
+*.po
+*.po32
diff --git a/source/registry/reg_cachehook.c b/source/registry/reg_cachehook.c
new file mode 100644
index 00000000000..547eed392d8
--- /dev/null
+++ b/source/registry/reg_cachehook.c
@@ -0,0 +1,112 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Gerald Carter 2002.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Implementation of registry hook cache tree */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+static SORTED_TREE *cache_tree;
+extern REGISTRY_OPS regdb_ops; /* these are the default */
+static REGISTRY_HOOK default_hook = { KEY_TREE_ROOT, &regdb_ops };
+
+/**********************************************************************
+ Initialize the cache tree
+ *********************************************************************/
+
+BOOL reghook_cache_init( void )
+{
+ cache_tree = sorted_tree_init( &default_hook, NULL, NULL );
+
+ return ( cache_tree == NULL );
+}
+
+/**********************************************************************
+ Add a new REGISTRY_HOOK to the cache. Note that the keyname
+ is not in the exact format that a SORTED_TREE expects.
+ *********************************************************************/
+
+BOOL reghook_cache_add( REGISTRY_HOOK *hook )
+{
+ pstring key;
+
+ if ( !hook )
+ return False;
+
+ pstrcpy( key, "\\");
+ pstrcat( key, hook->keyname );
+
+ pstring_sub( key, "\\", "/" );
+
+ DEBUG(10,("reghook_cache_add: Adding key [%s]\n", key));
+
+ return sorted_tree_add( cache_tree, key, hook );
+}
+
+/**********************************************************************
+ Initialize the cache tree
+ *********************************************************************/
+
+REGISTRY_HOOK* reghook_cache_find( char *keyname )
+{
+ char *key;
+ int len;
+ REGISTRY_HOOK *hook;
+
+ if ( !keyname )
+ return NULL;
+
+ /* prepend the string with a '\' character */
+
+ len = strlen( keyname );
+ if ( !(key = malloc( len + 2 )) ) {
+ DEBUG(0,("reghook_cache_find: malloc failed for string [%s] !?!?!\n",
+ keyname));
+ return NULL;
+ }
+
+ *key = '\\';
+ strncpy( key+1, keyname, len+1);
+
+ /* swap to a form understood by the SORTED_TREE */
+
+ string_sub( key, "\\", "/", 0 );
+
+ DEBUG(10,("reghook_cache_find: Searching for keyname [%s]\n", key));
+
+ hook = sorted_tree_find( cache_tree, key ) ;
+
+ SAFE_FREE( key );
+
+ return hook;
+}
+
+/**********************************************************************
+ Initialize the cache tree
+ *********************************************************************/
+
+void reghook_dump_cache( int debuglevel )
+{
+ DEBUG(debuglevel,("reghook_dump_cache: Starting cache dump now...\n"));
+
+ sorted_tree_print_keys( cache_tree, debuglevel );
+}
diff --git a/source/registry/reg_db.c b/source/registry/reg_db.c
new file mode 100644
index 00000000000..cd5ec18f021
--- /dev/null
+++ b/source/registry/reg_db.c
@@ -0,0 +1,311 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Gerald Carter 2002.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Implementation of internal registry database functions. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+static TDB_CONTEXT *tdb_reg;
+
+
+/***********************************************************************
+ Open the registry data in the tdb
+ ***********************************************************************/
+
+static BOOL init_registry_data( void )
+{
+ pstring keyname;
+ REGSUBKEY_CTR subkeys;
+
+ ZERO_STRUCTP( &subkeys );
+
+ /* HKEY_LOCAL_MACHINE */
+
+ regsubkey_ctr_init( &subkeys );
+ pstrcpy( keyname, KEY_HKLM );
+ regsubkey_ctr_addkey( &subkeys, "SYSTEM" );
+ if ( !regdb_store_reg_keys( keyname, &subkeys ))
+ return False;
+ regsubkey_ctr_destroy( &subkeys );
+
+ regsubkey_ctr_init( &subkeys );
+ pstrcpy( keyname, KEY_HKLM );
+ pstrcat( keyname, "/SYSTEM" );
+ regsubkey_ctr_addkey( &subkeys, "CurrentControlSet" );
+ if ( !regdb_store_reg_keys( keyname, &subkeys ))
+ return False;
+ regsubkey_ctr_destroy( &subkeys );
+
+ regsubkey_ctr_init( &subkeys );
+ pstrcpy( keyname, KEY_HKLM );
+ pstrcat( keyname, "/SYSTEM/CurrentControlSet" );
+ regsubkey_ctr_addkey( &subkeys, "Control" );
+ regsubkey_ctr_addkey( &subkeys, "Services" );
+ if ( !regdb_store_reg_keys( keyname, &subkeys ))
+ return False;
+ regsubkey_ctr_destroy( &subkeys );
+
+ regsubkey_ctr_init( &subkeys );
+ pstrcpy( keyname, KEY_HKLM );
+ pstrcat( keyname, "/SYSTEM/CurrentControlSet/Control" );
+ regsubkey_ctr_addkey( &subkeys, "Print" );
+ regsubkey_ctr_addkey( &subkeys, "ProductOptions" );
+ if ( !regdb_store_reg_keys( keyname, &subkeys ))
+ return False;
+ regsubkey_ctr_destroy( &subkeys );
+
+ pstrcpy( keyname, KEY_HKLM );
+ pstrcat( keyname, "/SYSTEM/CurrentControlSet/Control/ProductOptions" );
+ if ( !regdb_store_reg_keys( keyname, &subkeys ))
+ return False;
+
+ regsubkey_ctr_init( &subkeys );
+ pstrcpy( keyname, KEY_HKLM );
+ pstrcat( keyname, "/SYSTEM/CurrentControlSet/Services" );
+ regsubkey_ctr_addkey( &subkeys, "Netlogon" );
+ if ( !regdb_store_reg_keys( keyname, &subkeys ))
+ return False;
+ regsubkey_ctr_destroy( &subkeys );
+
+ regsubkey_ctr_init( &subkeys );
+ pstrcpy( keyname, KEY_HKLM );
+ pstrcat( keyname, "/SYSTEM/CurrentControlSet/Services/Netlogon" );
+ regsubkey_ctr_addkey( &subkeys, "Parameters" );
+ if ( !regdb_store_reg_keys( keyname, &subkeys ))
+ return False;
+ regsubkey_ctr_destroy( &subkeys );
+
+ pstrcpy( keyname, KEY_HKLM );
+ pstrcat( keyname, "/SYSTEM/CurrentControlSet/Services/Netlogon/Parameters" );
+ if ( !regdb_store_reg_keys( keyname, &subkeys ))
+ return False;
+
+ /* HKEY_USER */
+
+ pstrcpy( keyname, KEY_HKU );
+ if ( !regdb_store_reg_keys( keyname, &subkeys ) )
+ return False;
+
+ /* HKEY_CLASSES_ROOT*/
+
+ pstrcpy( keyname, KEY_HKCR );
+ if ( !regdb_store_reg_keys( keyname, &subkeys ) )
+ return False;
+
+ return True;
+}
+
+/***********************************************************************
+ Open the registry database
+ ***********************************************************************/
+
+BOOL init_registry_db( void )
+{
+ static pid_t local_pid;
+
+ if (tdb_reg && local_pid == sys_getpid())
+ return True;
+
+ /*
+ * try to open first without creating so we can determine
+ * if we need to init the data in the registry
+ */
+
+ tdb_reg = tdb_open_log(lock_path("registry.tdb"), 0, TDB_DEFAULT, O_RDWR, 0600);
+ if ( !tdb_reg )
+ {
+ tdb_reg = tdb_open_log(lock_path("registry.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+ if ( !tdb_reg ) {
+ DEBUG(0,("init_registry: Failed to open registry %s (%s)\n",
+ lock_path("registry.tdb"), strerror(errno) ));
+ return False;
+ }
+
+ DEBUG(10,("init_registry: Successfully created registry tdb\n"));
+
+ /* create the registry here */
+ if ( !init_registry_data() ) {
+ DEBUG(0,("init_registry: Failed to initiailize data in registry!\n"));
+ return False;
+ }
+ }
+
+ local_pid = sys_getpid();
+
+ return True;
+}
+
+
+
+/***********************************************************************
+ Add subkey strings to the registry tdb under a defined key
+ fmt is the same format as tdb_pack except this function only supports
+ fstrings
+
+ The full path to the registry key is used as database after the
+ \'s are converted to /'s. Key string is also normalized to UPPER
+ case.
+ ***********************************************************************/
+
+BOOL regdb_store_reg_keys( char *keyname, REGSUBKEY_CTR *ctr )
+{
+ TDB_DATA kbuf, dbuf;
+ char *buffer, *tmpbuf;
+ int i = 0;
+ uint32 len, buflen;
+ BOOL ret = True;
+ uint32 num_subkeys = regsubkey_ctr_numkeys( ctr );
+
+ if ( !keyname )
+ return False;
+
+ strupper_m( keyname );
+
+ /* allocate some initial memory */
+
+ buffer = malloc(sizeof(pstring));
+ buflen = sizeof(pstring);
+ len = 0;
+
+ /* store the number of subkeys */
+
+ len += tdb_pack(buffer+len, buflen-len, "d", num_subkeys );
+
+ /* pack all the strings */
+
+ for (i=0; i<num_subkeys; i++) {
+ len += tdb_pack( buffer+len, buflen-len, "f", regsubkey_ctr_specific_key(ctr, i) );
+ if ( len > buflen ) {
+ /* allocate some extra space */
+ if ((tmpbuf = Realloc( buffer, len*2 )) == NULL) {
+ DEBUG(0,("regdb_store_reg_keys: Failed to realloc memory of size [%d]\n", len*2));
+ ret = False;
+ goto done;
+ }
+ buffer = tmpbuf;
+ buflen = len*2;
+
+ len = tdb_pack( buffer+len, buflen-len, "f", regsubkey_ctr_specific_key(ctr, i) );
+ }
+ }
+
+ /* finally write out the data */
+
+ kbuf.dptr = keyname;
+ kbuf.dsize = strlen(keyname)+1;
+ dbuf.dptr = buffer;
+ dbuf.dsize = len;
+ if ( tdb_store( tdb_reg, kbuf, dbuf, TDB_REPLACE ) == -1) {
+ ret = False;
+ goto done;
+ }
+
+done:
+ SAFE_FREE( buffer );
+
+ return ret;
+}
+
+/***********************************************************************
+ Retrieve an array of strings containing subkeys. Memory should be
+ released by the caller. The subkeys are stored in a catenated string
+ of null terminated character strings
+ ***********************************************************************/
+
+int regdb_fetch_reg_keys( char* key, REGSUBKEY_CTR *ctr )
+{
+ pstring path;
+ uint32 num_items;
+ TDB_DATA dbuf;
+ char *buf;
+ uint32 buflen, len;
+ int i;
+ fstring subkeyname;
+
+ DEBUG(10,("regdb_fetch_reg_keys: Enter key => [%s]\n", key ? key : "NULL"));
+
+ pstrcpy( path, key );
+
+ /* convert to key format */
+ pstring_sub( path, "\\", "/" );
+ strupper_m( path );
+
+ dbuf = tdb_fetch_bystring( tdb_reg, path );
+
+ buf = dbuf.dptr;
+ buflen = dbuf.dsize;
+
+ if ( !buf ) {
+ DEBUG(5,("regdb_fetch_reg_keys: tdb lookup failed to locate key [%s]\n", key));
+ return -1;
+ }
+
+ len = tdb_unpack( buf, buflen, "d", &num_items);
+
+ for (i=0; i<num_items; i++) {
+ len += tdb_unpack( buf+len, buflen-len, "f", subkeyname );
+ regsubkey_ctr_addkey( ctr, subkeyname );
+ }
+
+ SAFE_FREE( dbuf.dptr );
+
+ DEBUG(10,("regdb_fetch_reg_keys: Exit [%d] items\n", num_items));
+
+ return num_items;
+}
+
+
+/***********************************************************************
+ Retrieve an array of strings containing subkeys. Memory should be
+ released by the caller. The subkeys are stored in a catenated string
+ of null terminated character strings
+ ***********************************************************************/
+
+int regdb_fetch_reg_values( char* key, REGVAL_CTR *val )
+{
+ return 0;
+}
+
+/***********************************************************************
+ Stub function since we do not currently support storing registry
+ values in the registry.tdb
+ ***********************************************************************/
+
+BOOL regdb_store_reg_values( char *key, REGVAL_CTR *val )
+{
+ return False;
+}
+
+
+/*
+ * Table of function pointers for default access
+ */
+
+REGISTRY_OPS regdb_ops = {
+ regdb_fetch_reg_keys,
+ regdb_fetch_reg_values,
+ regdb_store_reg_keys,
+ regdb_store_reg_values
+};
+
+
diff --git a/source/registry/reg_frontend.c b/source/registry/reg_frontend.c
new file mode 100644
index 00000000000..a9dfb52f011
--- /dev/null
+++ b/source/registry/reg_frontend.c
@@ -0,0 +1,260 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Gerald Carter 2002.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Implementation of registry frontend view functions. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+extern REGISTRY_OPS printing_ops;
+extern REGISTRY_OPS regdb_ops; /* these are the default */
+
+/* array of REGISTRY_HOOK's which are read into a tree for easy access */
+
+
+REGISTRY_HOOK reg_hooks[] = {
+ { KEY_PRINTING, &printing_ops },
+ { NULL, NULL }
+};
+
+
+/***********************************************************************
+ Open the registry database and initialize the REGISTRY_HOOK cache
+ ***********************************************************************/
+
+BOOL init_registry( void )
+{
+ int i;
+
+ if ( !init_registry_db() ) {
+ DEBUG(0,("init_registry: failed to initialize the registry tdb!\n"));
+ return False;
+ }
+
+ /* build the cache tree of registry hooks */
+
+ reghook_cache_init();
+
+ for ( i=0; reg_hooks[i].keyname; i++ ) {
+ if ( !reghook_cache_add(&reg_hooks[i]) )
+ return False;
+ }
+
+ if ( DEBUGLEVEL >= 20 )
+ reghook_dump_cache(20);
+
+ return True;
+}
+
+
+
+
+/***********************************************************************
+ High level wrapper function for storing registry subkeys
+ ***********************************************************************/
+
+BOOL store_reg_keys( REGISTRY_KEY *key, REGSUBKEY_CTR *subkeys )
+{
+ if ( key->hook && key->hook->ops && key->hook->ops->store_subkeys_fn )
+ return key->hook->ops->store_subkeys_fn( key->name, subkeys );
+ else
+ return False;
+
+}
+
+/***********************************************************************
+ High level wrapper function for storing registry values
+ ***********************************************************************/
+
+BOOL store_reg_values( REGISTRY_KEY *key, REGVAL_CTR *val )
+{
+ if ( key->hook && key->hook->ops && key->hook->ops->store_values_fn )
+ return key->hook->ops->store_values_fn( key->name, val );
+ else
+ return False;
+}
+
+
+/***********************************************************************
+ High level wrapper function for enumerating registry subkeys
+ Initialize the TALLOC_CTX if necessary
+ ***********************************************************************/
+
+int fetch_reg_keys( REGISTRY_KEY *key, REGSUBKEY_CTR *subkey_ctr )
+{
+ int result = -1;
+
+ if ( key->hook && key->hook->ops && key->hook->ops->subkey_fn )
+ result = key->hook->ops->subkey_fn( key->name, subkey_ctr );
+
+ return result;
+}
+
+/***********************************************************************
+ retreive a specific subkey specified by index. Caller is
+ responsible for freeing memory
+ ***********************************************************************/
+
+BOOL fetch_reg_keys_specific( REGISTRY_KEY *key, char** subkey, uint32 key_index )
+{
+ static REGSUBKEY_CTR ctr;
+ static pstring save_path;
+ static BOOL ctr_init = False;
+ char *s;
+
+ *subkey = NULL;
+
+ /* simple caching for performance; very basic heuristic */
+
+ if ( !ctr_init ) {
+ DEBUG(8,("fetch_reg_keys_specific: Initializing cache of subkeys for [%s]\n", key->name));
+ ZERO_STRUCTP( &ctr );
+ regsubkey_ctr_init( &ctr );
+
+ pstrcpy( save_path, key->name );
+
+ if ( fetch_reg_keys( key, &ctr) == -1 )
+ return False;
+
+ ctr_init = True;
+ }
+ /* clear the cache when key_index == 0 or the path has changed */
+ else if ( !key_index || StrCaseCmp( save_path, key->name) ) {
+
+ DEBUG(8,("fetch_reg_keys_specific: Updating cache of subkeys for [%s]\n", key->name));
+
+ regsubkey_ctr_destroy( &ctr );
+ regsubkey_ctr_init( &ctr );
+
+ pstrcpy( save_path, key->name );
+
+ if ( fetch_reg_keys( key, &ctr) == -1 )
+ return False;
+ }
+
+ if ( !(s = regsubkey_ctr_specific_key( &ctr, key_index )) )
+ return False;
+
+ *subkey = strdup( s );
+
+ return True;
+}
+
+
+/***********************************************************************
+ High level wrapper function for enumerating registry values
+ Initialize the TALLOC_CTX if necessary
+ ***********************************************************************/
+
+int fetch_reg_values( REGISTRY_KEY *key, REGVAL_CTR *val )
+{
+ int result = -1;
+
+ if ( key->hook && key->hook->ops && key->hook->ops->value_fn )
+ result = key->hook->ops->value_fn( key->name, val );
+
+ return result;
+}
+
+
+/***********************************************************************
+ retreive a specific subkey specified by index. Caller is
+ responsible for freeing memory
+ ***********************************************************************/
+
+BOOL fetch_reg_values_specific( REGISTRY_KEY *key, REGISTRY_VALUE **val, uint32 val_index )
+{
+ static REGVAL_CTR ctr;
+ static pstring save_path;
+ static BOOL ctr_init = False;
+ REGISTRY_VALUE *v;
+
+ *val = NULL;
+
+ /* simple caching for performance; very basic heuristic */
+
+ if ( !ctr_init ) {
+ DEBUG(8,("fetch_reg_values_specific: Initializing cache of values for [%s]\n", key->name));
+
+ ZERO_STRUCTP( &ctr );
+ regval_ctr_init( &ctr );
+
+ pstrcpy( save_path, key->name );
+
+ if ( fetch_reg_values( key, &ctr) == -1 )
+ return False;
+
+ ctr_init = True;
+ }
+ /* clear the cache when val_index == 0 or the path has changed */
+ else if ( !val_index || StrCaseCmp(save_path, key->name) ) {
+
+ DEBUG(8,("fetch_reg_values_specific: Updating cache of values for [%s]\n", key->name));
+
+ regval_ctr_destroy( &ctr );
+ regval_ctr_init( &ctr );
+
+ pstrcpy( save_path, key->name );
+
+ if ( fetch_reg_values( key, &ctr) == -1 )
+ return False;
+ }
+
+ if ( !(v = regval_ctr_specific_value( &ctr, val_index )) )
+ return False;
+
+ *val = dup_registry_value( v );
+
+ return True;
+}
+
+/***********************************************************************
+ Utility function for splitting the base path of a registry path off
+ by setting base and new_path to the apprapriate offsets withing the
+ path.
+
+ WARNING!! Does modify the original string!
+ ***********************************************************************/
+
+BOOL reg_split_path( char *path, char **base, char **new_path )
+{
+ char *p;
+
+ *new_path = *base = NULL;
+
+ if ( !path)
+ return False;
+
+ *base = path;
+
+ p = strchr( path, '\\' );
+
+ if ( p ) {
+ *p = '\0';
+ *new_path = p+1;
+ }
+
+ return True;
+}
+
+
+
diff --git a/source/registry/reg_objects.c b/source/registry/reg_objects.c
new file mode 100644
index 00000000000..9cfeb7faa97
--- /dev/null
+++ b/source/registry/reg_objects.c
@@ -0,0 +1,374 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Gerald Carter 2002.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Implementation of registry frontend view functions. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+
+/***********************************************************************
+ Init the talloc context held by a REGSUBKEY_CTR structure
+ **********************************************************************/
+
+void regsubkey_ctr_init( REGSUBKEY_CTR *ctr )
+{
+ if ( !ctr->ctx )
+ ctr->ctx = talloc_init("regsubkey_ctr_init for ctr %p", ctr);
+}
+
+/***********************************************************************
+ Add a new key to the array
+ **********************************************************************/
+
+int regsubkey_ctr_addkey( REGSUBKEY_CTR *ctr, const char *keyname )
+{
+ uint32 len;
+ char **pp;
+
+ if ( keyname )
+ {
+ len = strlen( keyname );
+
+ /* allocate a space for the char* in the array */
+
+ if ( ctr->subkeys == 0 )
+ ctr->subkeys = talloc( ctr->ctx, sizeof(char*) );
+ else {
+ pp = talloc_realloc( ctr->ctx, ctr->subkeys, sizeof(char*)*(ctr->num_subkeys+1) );
+ if ( pp )
+ ctr->subkeys = pp;
+ }
+
+ /* allocate the string and save it in the array */
+
+ ctr->subkeys[ctr->num_subkeys] = talloc( ctr->ctx, len+1 );
+ strncpy( ctr->subkeys[ctr->num_subkeys], keyname, len+1 );
+ ctr->num_subkeys++;
+ }
+
+ return ctr->num_subkeys;
+}
+
+/***********************************************************************
+ How many keys does the container hold ?
+ **********************************************************************/
+
+int regsubkey_ctr_numkeys( REGSUBKEY_CTR *ctr )
+{
+ return ctr->num_subkeys;
+}
+
+/***********************************************************************
+ Retreive a specific key string
+ **********************************************************************/
+
+char* regsubkey_ctr_specific_key( REGSUBKEY_CTR *ctr, uint32 key_index )
+{
+ if ( ! (key_index < ctr->num_subkeys) )
+ return NULL;
+
+ return ctr->subkeys[key_index];
+}
+
+/***********************************************************************
+ free memory held by a REGSUBKEY_CTR structure
+ **********************************************************************/
+
+void regsubkey_ctr_destroy( REGSUBKEY_CTR *ctr )
+{
+ if ( ctr ) {
+ talloc_destroy( ctr->ctx );
+ ZERO_STRUCTP( ctr );
+ }
+}
+
+
+/*
+ * Utility functions for REGVAL_CTR
+ */
+
+/***********************************************************************
+ Init the talloc context held by a REGSUBKEY_CTR structure
+ **********************************************************************/
+
+void regval_ctr_init( REGVAL_CTR *ctr )
+{
+ if ( !ctr->ctx )
+ ctr->ctx = talloc_init("regval_ctr_init for ctr %p", ctr);
+}
+
+/***********************************************************************
+ How many keys does the container hold ?
+ **********************************************************************/
+
+int regval_ctr_numvals( REGVAL_CTR *ctr )
+{
+ return ctr->num_values;
+}
+
+/***********************************************************************
+ allocate memory for and duplicate a REGISTRY_VALUE.
+ This is malloc'd memory so the caller should free it when done
+ **********************************************************************/
+
+REGISTRY_VALUE* dup_registry_value( REGISTRY_VALUE *val )
+{
+ REGISTRY_VALUE *copy = NULL;
+
+ if ( !val )
+ return NULL;
+
+ if ( !(copy = malloc( sizeof(REGISTRY_VALUE) )) ) {
+ DEBUG(0,("dup_registry_value: malloc() failed!\n"));
+ return NULL;
+ }
+
+ /* copy all the non-pointer initial data */
+
+ memcpy( copy, val, sizeof(REGISTRY_VALUE) );
+ if ( val->data_p )
+ {
+ if ( !(copy->data_p = memdup( val->data_p, val->size )) ) {
+ DEBUG(0,("dup_registry_value: memdup() failed for [%d] bytes!\n",
+ val->size));
+ SAFE_FREE( copy );
+ }
+ }
+
+ return copy;
+}
+
+/**********************************************************************
+ free the memory allocated to a REGISTRY_VALUE
+ *********************************************************************/
+
+void free_registry_value( REGISTRY_VALUE *val )
+{
+ if ( !val )
+ return;
+
+ SAFE_FREE( val->data_p );
+ SAFE_FREE( val );
+
+ return;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+uint8* regval_data_p( REGISTRY_VALUE *val )
+{
+ return val->data_p;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+int regval_size( REGISTRY_VALUE *val )
+{
+ return val->size;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+char* regval_name( REGISTRY_VALUE *val )
+{
+ return val->valuename;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+uint32 regval_type( REGISTRY_VALUE *val )
+{
+ return val->type;
+}
+
+/***********************************************************************
+ Retreive a pointer to a specific value. Caller shoud dup the structure
+ since this memory may go away with a regval_ctr_destroy()
+ **********************************************************************/
+
+REGISTRY_VALUE* regval_ctr_specific_value( REGVAL_CTR *ctr, uint32 idx )
+{
+ if ( !(idx < ctr->num_values) )
+ return NULL;
+
+ return ctr->values[idx];
+}
+
+/***********************************************************************
+ Retrive the TALLOC_CTX associated with a REGISTRY_VALUE
+ **********************************************************************/
+
+TALLOC_CTX* regval_ctr_getctx( REGVAL_CTR *val )
+{
+ if ( !val )
+ return NULL;
+
+ return val->ctx;
+}
+
+/***********************************************************************
+ Add a new registry value to the array
+ **********************************************************************/
+
+int regval_ctr_addvalue( REGVAL_CTR *ctr, const char *name, uint16 type,
+ const char *data_p, size_t size )
+{
+ REGISTRY_VALUE **ppreg;
+
+ if ( name )
+ {
+ /* allocate a slot in the array of pointers */
+
+ if ( ctr->num_values == 0 )
+ ctr->values = talloc( ctr->ctx, sizeof(REGISTRY_VALUE*) );
+ else {
+ ppreg = talloc_realloc( ctr->ctx, ctr->values, sizeof(REGISTRY_VALUE*)*(ctr->num_values+1) );
+ if ( ppreg )
+ ctr->values = ppreg;
+ }
+
+ /* allocate a new value and store the pointer in the arrya */
+
+ ctr->values[ctr->num_values] = talloc( ctr->ctx, sizeof(REGISTRY_VALUE) );
+
+ /* init the value */
+
+ fstrcpy( ctr->values[ctr->num_values]->valuename, name );
+ ctr->values[ctr->num_values]->type = type;
+ ctr->values[ctr->num_values]->data_p = talloc_memdup( ctr->ctx, data_p, size );
+ ctr->values[ctr->num_values]->size = size;
+ ctr->num_values++;
+ }
+
+ return ctr->num_values;
+}
+
+/***********************************************************************
+ Add a new registry value to the array
+ **********************************************************************/
+
+int regval_ctr_copyvalue( REGVAL_CTR *ctr, REGISTRY_VALUE *val )
+{
+ REGISTRY_VALUE **ppreg;
+
+ if ( val )
+ {
+ /* allocate a slot in the array of pointers */
+
+ if ( ctr->num_values == 0 )
+ ctr->values = talloc( ctr->ctx, sizeof(REGISTRY_VALUE*) );
+ else {
+ ppreg = talloc_realloc( ctr->ctx, ctr->values, sizeof(REGISTRY_VALUE*)*(ctr->num_values+1) );
+ if ( ppreg )
+ ctr->values = ppreg;
+ }
+
+ /* allocate a new value and store the pointer in the arrya */
+
+ ctr->values[ctr->num_values] = talloc( ctr->ctx, sizeof(REGISTRY_VALUE) );
+
+ /* init the value */
+
+ fstrcpy( ctr->values[ctr->num_values]->valuename, val->valuename );
+ ctr->values[ctr->num_values]->type = val->type;
+ ctr->values[ctr->num_values]->data_p = talloc_memdup( ctr->ctx, val->data_p, val->size );
+ ctr->values[ctr->num_values]->size = val->size;
+ ctr->num_values++;
+ }
+
+ return ctr->num_values;
+}
+
+/***********************************************************************
+ Delete a single value from the registry container.
+ No need to free memory since it is talloc'd.
+ **********************************************************************/
+
+int regval_ctr_delvalue( REGVAL_CTR *ctr, const char *name )
+{
+ int i;
+
+ /* search for the value */
+ if (!(ctr->num_values))
+ return 0;
+
+ for ( i=0; i<ctr->num_values; i++ ) {
+ if ( strcmp( ctr->values[i]->valuename, name ) == 0)
+ break;
+ }
+
+ /* just return if we don't find it */
+
+ if ( i == ctr->num_values )
+ return ctr->num_values;
+
+ /* just shift everything down one */
+
+ for ( /* use previous i */; i<(ctr->num_values-1); i++ )
+ memcpy( ctr->values[i], ctr->values[i+1], sizeof(REGISTRY_VALUE) );
+
+ /* paranoia */
+
+ ZERO_STRUCTP( ctr->values[i] );
+
+ ctr->num_values--;
+
+ return ctr->num_values;
+}
+
+/***********************************************************************
+ Delete a single value from the registry container.
+ No need to free memory since it is talloc'd.
+ **********************************************************************/
+
+REGISTRY_VALUE* regval_ctr_getvalue( REGVAL_CTR *ctr, const char *name )
+{
+ int i;
+
+ /* search for the value */
+
+ for ( i=0; i<ctr->num_values; i++ ) {
+ if ( strequal( ctr->values[i]->valuename, name ) )
+ return ctr->values[i];
+ }
+
+ return NULL;
+}
+
+/***********************************************************************
+ free memory held by a REGVAL_CTR structure
+ **********************************************************************/
+
+void regval_ctr_destroy( REGVAL_CTR *ctr )
+{
+ if ( ctr ) {
+ talloc_destroy( ctr->ctx );
+ ZERO_STRUCTP( ctr );
+ }
+}
+
+
diff --git a/source/registry/reg_printing.c b/source/registry/reg_printing.c
new file mode 100644
index 00000000000..e50a5f4d4fd
--- /dev/null
+++ b/source/registry/reg_printing.c
@@ -0,0 +1,826 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Gerald Carter 2002.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Implementation of registry virtual views for printing information */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#define MAX_TOP_LEVEL_KEYS 3
+
+/* some symbolic indexes into the top_level_keys */
+
+#define KEY_INDEX_ENVIR 0
+#define KEY_INDEX_FORMS 1
+#define KEY_INDEX_PRINTER 2
+
+static const char *top_level_keys[MAX_TOP_LEVEL_KEYS] = {
+ "Environments",
+ "Forms",
+ "Printers"
+};
+
+
+/**********************************************************************
+ It is safe to assume that every registry path passed into on of
+ the exported functions here begins with KEY_PRINTING else
+ these functions would have never been called. This is a small utility
+ function to strip the beginning of the path and make a copy that the
+ caller can modify. Note that the caller is responsible for releasing
+ the memory allocated here.
+ **********************************************************************/
+
+static char* trim_reg_path( char *path )
+{
+ char *p;
+ uint16 key_len = strlen(KEY_PRINTING);
+
+ /*
+ * sanity check...this really should never be True.
+ * It is only here to prevent us from accessing outside
+ * the path buffer in the extreme case.
+ */
+
+ if ( strlen(path) < key_len ) {
+ DEBUG(0,("trim_reg_path: Registry path too short! [%s]\n", path));
+ DEBUG(0,("trim_reg_path: KEY_PRINTING => [%s]!\n", KEY_PRINTING));
+ return NULL;
+ }
+
+
+ p = path + strlen( KEY_PRINTING );
+
+ if ( *p == '\\' )
+ p++;
+
+ if ( *p )
+ return strdup(p);
+ else
+ return NULL;
+}
+
+/**********************************************************************
+ handle enumeration of subkeys below KEY_PRINTING\Environments
+ *********************************************************************/
+
+static int print_subpath_environments( char *key, REGSUBKEY_CTR *subkeys )
+{
+ const char *environments[] = {
+ "Windows 4.0",
+ "Windows NT x86",
+ "Windows NT R4000",
+ "Windows NT Alpha_AXP",
+ "Windows NT PowerPC",
+ NULL };
+ fstring *drivers = NULL;
+ int i, env_index, num_drivers;
+ BOOL valid_env = False;
+ char *base, *new_path;
+ char *keystr;
+ char *key2 = NULL;
+ int num_subkeys = -1;
+
+ DEBUG(10,("print_subpath_environments: key=>[%s]\n", key ? key : "NULL" ));
+
+ /* listed architectures of installed drivers */
+
+ if ( !key )
+ {
+ /* Windows 9x drivers */
+
+ if ( get_ntdrivers( &drivers, environments[0], 0 ) )
+ regsubkey_ctr_addkey( subkeys, environments[0] );
+ SAFE_FREE( drivers );
+
+ /* Windows NT/2k intel drivers */
+
+ if ( get_ntdrivers( &drivers, environments[1], 2 )
+ || get_ntdrivers( &drivers, environments[1], 3 ) )
+ {
+ regsubkey_ctr_addkey( subkeys, environments[1] );
+ }
+ SAFE_FREE( drivers );
+
+ /* Windows NT 4.0; non-intel drivers */
+ for ( i=2; environments[i]; i++ ) {
+ if ( get_ntdrivers( &drivers, environments[i], 2 ) )
+ regsubkey_ctr_addkey( subkeys, environments[i] );
+
+ }
+ SAFE_FREE( drivers );
+
+ num_subkeys = regsubkey_ctr_numkeys( subkeys );
+ goto done;
+ }
+
+ /* we are dealing with a subkey of "Environments */
+
+ key2 = strdup( key );
+ keystr = key2;
+ reg_split_path( keystr, &base, &new_path );
+
+ /* sanity check */
+
+ for ( env_index=0; environments[env_index]; env_index++ ) {
+ if ( StrCaseCmp( environments[env_index], base ) == 0 ) {
+ valid_env = True;
+ break;
+ }
+ }
+
+ if ( !valid_env )
+ return -1;
+
+ /* enumerate driver versions; environment is environments[env_index] */
+
+ if ( !new_path ) {
+ switch ( env_index ) {
+ case 0: /* Win9x */
+ if ( get_ntdrivers( &drivers, environments[0], 0 ) ) {
+ regsubkey_ctr_addkey( subkeys, "0" );
+ SAFE_FREE( drivers );
+ }
+ break;
+ case 1: /* Windows NT/2k - intel */
+ if ( get_ntdrivers( &drivers, environments[1], 2 ) ) {
+ regsubkey_ctr_addkey( subkeys, "2" );
+ SAFE_FREE( drivers );
+ }
+ if ( get_ntdrivers( &drivers, environments[1], 3 ) ) {
+ regsubkey_ctr_addkey( subkeys, "3" );
+ SAFE_FREE( drivers );
+ }
+ break;
+ default: /* Windows NT - nonintel */
+ if ( get_ntdrivers( &drivers, environments[env_index], 2 ) ) {
+ regsubkey_ctr_addkey( subkeys, "2" );
+ SAFE_FREE( drivers );
+ }
+
+ }
+
+ num_subkeys = regsubkey_ctr_numkeys( subkeys );
+ goto done;
+ }
+
+ /* we finally get to enumerate the drivers */
+
+ keystr = new_path;
+ reg_split_path( keystr, &base, &new_path );
+
+ if ( !new_path ) {
+ num_drivers = get_ntdrivers( &drivers, environments[env_index], atoi(base) );
+ for ( i=0; i<num_drivers; i++ )
+ regsubkey_ctr_addkey( subkeys, drivers[i] );
+
+ num_subkeys = regsubkey_ctr_numkeys( subkeys );
+ goto done;
+ }
+
+done:
+ SAFE_FREE( key2 );
+
+ return num_subkeys;
+}
+
+/***********************************************************************
+ simple function to prune a pathname down to the basename of a file
+ **********************************************************************/
+
+static char* dos_basename ( char *path )
+{
+ char *p;
+
+ p = strrchr( path, '\\' );
+ if ( p )
+ p++;
+ else
+ p = path;
+
+ return p;
+}
+
+/**********************************************************************
+ handle enumeration of values below
+ KEY_PRINTING\Environments\<arch>\<version>\<drivername>
+ *********************************************************************/
+
+static int print_subpath_values_environments( char *key, REGVAL_CTR *val )
+{
+ char *keystr;
+ char *key2 = NULL;
+ char *base, *new_path;
+ fstring env;
+ fstring driver;
+ int version;
+ NT_PRINTER_DRIVER_INFO_LEVEL driver_ctr;
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 *info3;
+ WERROR w_result;
+ char *buffer = NULL;
+ char *buffer2 = NULL;
+ int buffer_size = 0;
+ int i, length;
+ char *filename;
+ UNISTR2 data;;
+
+ DEBUG(8,("print_subpath_values_environments: Enter key => [%s]\n", key ? key : "NULL"));
+
+ if ( !key )
+ return 0;
+
+ /*
+ * The only key below KEY_PRINTING\Environments that
+ * posseses values is each specific printer driver
+ * First get the arch, version, & driver name
+ */
+
+ /* env */
+
+ key2 = strdup( key );
+ keystr = key2;
+ reg_split_path( keystr, &base, &new_path );
+ if ( !base || !new_path )
+ return 0;
+ fstrcpy( env, base );
+
+ /* version */
+
+ keystr = new_path;
+ reg_split_path( keystr, &base, &new_path );
+ if ( !base || !new_path )
+ return 0;
+ version = atoi( base );
+
+ /* printer driver name */
+
+ keystr = new_path;
+ reg_split_path( keystr, &base, &new_path );
+ /* new_path should be NULL here since this must be the last key */
+ if ( !base || new_path )
+ return 0;
+ fstrcpy( driver, base );
+
+ w_result = get_a_printer_driver( &driver_ctr, 3, driver, env, version );
+
+ if ( !W_ERROR_IS_OK(w_result) )
+ return -1;
+
+ /* build the values out of the driver information */
+ info3 = driver_ctr.info_3;
+
+ filename = dos_basename( info3->driverpath );
+ init_unistr2( &data, filename, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Driver", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+
+ filename = dos_basename( info3->configfile );
+ init_unistr2( &data, filename, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Configuration File", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+
+ filename = dos_basename( info3->datafile );
+ init_unistr2( &data, filename, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Data File", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+
+ filename = dos_basename( info3->helpfile );
+ init_unistr2( &data, filename, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Help File", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+
+ init_unistr2( &data, info3->defaultdatatype, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Data Type", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+
+ regval_ctr_addvalue( val, "Version", REG_DWORD, (char*)&info3->cversion, sizeof(info3->cversion) );
+
+ if ( info3->dependentfiles ) {
+ /* place the list of dependent files in a single
+ character buffer, separating each file name by
+ a NULL */
+
+ for ( i=0; strcmp(info3->dependentfiles[i], ""); i++ ) {
+ /* strip the path to only the file's base name */
+
+ filename = dos_basename( info3->dependentfiles[i] );
+
+ length = strlen(filename);
+
+ buffer2 = Realloc( buffer, buffer_size + (length + 1)*sizeof(uint16) );
+ if ( !buffer2 )
+ break;
+ buffer = buffer2;
+
+ init_unistr2( &data, filename, UNI_STR_TERMINATE);
+ memcpy( buffer+buffer_size, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+
+ buffer_size += (length + 1)*sizeof(uint16);
+ }
+
+ /* terminated by double NULL. Add the final one here */
+
+ buffer2 = Realloc( buffer, buffer_size + 2 );
+ if ( !buffer2 ) {
+ SAFE_FREE( buffer );
+ buffer_size = 0;
+ } else {
+ buffer = buffer2;
+ buffer[buffer_size++] = '\0';
+ buffer[buffer_size++] = '\0';
+ }
+ }
+
+ regval_ctr_addvalue( val, "Dependent Files", REG_MULTI_SZ, buffer, buffer_size );
+
+ free_a_printer_driver( driver_ctr, 3 );
+
+ SAFE_FREE( key2 );
+ SAFE_FREE( buffer );
+
+ DEBUG(8,("print_subpath_values_environments: Exit\n"));
+
+ return regval_ctr_numvals( val );
+}
+
+
+/**********************************************************************
+ handle enumeration of subkeys below KEY_PRINTING\Forms
+ Really just a stub function, but left here in case it needs to
+ be expanded later on
+ *********************************************************************/
+
+static int print_subpath_forms( char *key, REGSUBKEY_CTR *subkeys )
+{
+ DEBUG(10,("print_subpath_forms: key=>[%s]\n", key ? key : "NULL" ));
+
+ /* there are no subkeys */
+
+ if ( key )
+ return -1;
+
+ return 0;
+}
+
+/**********************************************************************
+ handle enumeration of values below KEY_PRINTING\Forms
+ *********************************************************************/
+
+static int print_subpath_values_forms( char *key, REGVAL_CTR *val )
+{
+ int num_values = 0;
+ uint32 data[8];
+ int form_index = 1;
+
+ DEBUG(10,("print_values_forms: key=>[%s]\n", key ? key : "NULL" ));
+
+ /* handle ..\Forms\ */
+
+ if ( !key )
+ {
+ nt_forms_struct *forms_list = NULL;
+ nt_forms_struct *form = NULL;
+ int i;
+
+ if ( (num_values = get_ntforms( &forms_list )) == 0 )
+ return 0;
+
+ DEBUG(10,("print_subpath_values_forms: [%d] user defined forms returned\n",
+ num_values));
+
+ /* handle user defined forms */
+
+ for ( i=0; i<num_values; i++ )
+ {
+ form = &forms_list[i];
+
+ data[0] = form->width;
+ data[1] = form->length;
+ data[2] = form->left;
+ data[3] = form->top;
+ data[4] = form->right;
+ data[5] = form->bottom;
+ data[6] = form_index++;
+ data[7] = form->flag;
+
+ regval_ctr_addvalue( val, form->name, REG_BINARY, (char*)data, sizeof(data) );
+
+ }
+
+ SAFE_FREE( forms_list );
+ forms_list = NULL;
+
+ /* handle built-on forms */
+
+ if ( (num_values = get_builtin_ntforms( &forms_list )) == 0 )
+ return 0;
+
+ DEBUG(10,("print_subpath_values_forms: [%d] built-in forms returned\n",
+ num_values));
+
+ for ( i=0; i<num_values; i++ )
+ {
+ form = &forms_list[i];
+
+ data[0] = form->width;
+ data[1] = form->length;
+ data[2] = form->left;
+ data[3] = form->top;
+ data[4] = form->right;
+ data[5] = form->bottom;
+ data[6] = form_index++;
+ data[7] = form->flag;
+
+ regval_ctr_addvalue( val, form->name, REG_BINARY, (char*)data, sizeof(data) );
+ }
+
+ SAFE_FREE( forms_list );
+ }
+
+ return num_values;
+}
+
+/**********************************************************************
+ handle enumeration of subkeys below KEY_PRINTING\Printers
+ *********************************************************************/
+
+static int print_subpath_printers( char *key, REGSUBKEY_CTR *subkeys )
+{
+ int n_services = lp_numservices();
+ int snum;
+ fstring sname;
+ int i;
+ int num_subkeys = 0;
+ char *keystr, *key2 = NULL;
+ char *base, *new_path;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ fstring *subkey_names = NULL;
+
+ DEBUG(10,("print_subpath_printers: key=>[%s]\n", key ? key : "NULL" ));
+
+ if ( !key )
+ {
+ /* enumerate all printers */
+
+ for (snum=0; snum<n_services; snum++) {
+ if ( !(lp_snum_ok(snum) && lp_print_ok(snum) ) )
+ continue;
+
+ fstrcpy( sname, lp_servicename(snum) );
+
+ regsubkey_ctr_addkey( subkeys, sname );
+ }
+
+ num_subkeys = regsubkey_ctr_numkeys( subkeys );
+ goto done;
+ }
+
+ /* get information for a specific printer */
+
+ key2 = strdup( key );
+ keystr = key2;
+ reg_split_path( keystr, &base, &new_path );
+
+ if ( !W_ERROR_IS_OK( get_a_printer(NULL, &printer, 2, base) ) )
+ goto done;
+
+ num_subkeys = get_printer_subkeys( &printer->info_2->data, new_path?new_path:"", &subkey_names );
+
+ for ( i=0; i<num_subkeys; i++ )
+ regsubkey_ctr_addkey( subkeys, subkey_names[i] );
+
+ free_a_printer( &printer, 2 );
+
+ /* no other subkeys below here */
+
+done:
+ SAFE_FREE( key2 );
+ SAFE_FREE( subkey_names );
+
+ return num_subkeys;
+}
+
+/**********************************************************************
+ handle enumeration of values below KEY_PRINTING\Printers
+ *********************************************************************/
+
+static int print_subpath_values_printers( char *key, REGVAL_CTR *val )
+{
+ int num_values = 0;
+ char *keystr, *key2 = NULL;
+ char *base, *new_path;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ NT_PRINTER_INFO_LEVEL_2 *info2;
+ DEVICEMODE *devmode;
+ prs_struct prs;
+ uint32 offset;
+ int snum;
+ fstring printername;
+ NT_PRINTER_DATA *p_data;
+ int i, key_index;
+ UNISTR2 data;
+
+ /*
+ * Theres are tw cases to deal with here
+ * (1) enumeration of printer_info_2 values
+ * (2) enumeration of the PrinterDriverData subney
+ */
+
+ if ( !key ) {
+ /* top level key has no values */
+ goto done;
+ }
+
+ key2 = strdup( key );
+ keystr = key2;
+ reg_split_path( keystr, &base, &new_path );
+
+ fstrcpy( printername, base );
+
+ if ( !new_path )
+ {
+ /* we are dealing with the printer itself */
+
+ if ( !W_ERROR_IS_OK( get_a_printer(NULL, &printer, 2, printername) ) )
+ goto done;
+
+ info2 = printer->info_2;
+
+
+ regval_ctr_addvalue( val, "Attributes", REG_DWORD, (char*)&info2->attributes, sizeof(info2->attributes) );
+ regval_ctr_addvalue( val, "Priority", REG_DWORD, (char*)&info2->priority, sizeof(info2->attributes) );
+ regval_ctr_addvalue( val, "ChangeID", REG_DWORD, (char*)&info2->changeid, sizeof(info2->changeid) );
+ regval_ctr_addvalue( val, "Default Priority", REG_DWORD, (char*)&info2->default_priority, sizeof(info2->default_priority) );
+ regval_ctr_addvalue( val, "Status", REG_DWORD, (char*)&info2->status, sizeof(info2->status) );
+ regval_ctr_addvalue( val, "StartTime", REG_DWORD, (char*)&info2->starttime, sizeof(info2->starttime) );
+ regval_ctr_addvalue( val, "UntilTime", REG_DWORD, (char*)&info2->untiltime, sizeof(info2->untiltime) );
+ regval_ctr_addvalue( val, "cjobs", REG_DWORD, (char*)&info2->cjobs, sizeof(info2->cjobs) );
+ regval_ctr_addvalue( val, "AveragePPM", REG_DWORD, (char*)&info2->averageppm, sizeof(info2->averageppm) );
+
+ init_unistr2( &data, info2->printername, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Name", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+ init_unistr2( &data, info2->location, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Location", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+ init_unistr2( &data, info2->comment, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Comment", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+ init_unistr2( &data, info2->parameters, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Parameters", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+ init_unistr2( &data, info2->portname, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Port", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+ init_unistr2( &data, info2->servername, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Server", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+ init_unistr2( &data, info2->sharename, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Share", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+ init_unistr2( &data, info2->drivername, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Driver", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+ init_unistr2( &data, info2->sepfile, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Separator File", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+ init_unistr2( &data, "winprint", UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Print Processor", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+
+
+ /* use a prs_struct for converting the devmode and security
+ descriptor to REG_BIARY */
+
+ prs_init( &prs, MAX_PDU_FRAG_LEN, regval_ctr_getctx(val), MARSHALL);
+
+ /* stream the device mode */
+
+ snum = lp_servicenumber(info2->sharename);
+ if ( (devmode = construct_dev_mode( snum )) != NULL )
+ {
+ if ( spoolss_io_devmode( "devmode", &prs, 0, devmode ) ) {
+
+ offset = prs_offset( &prs );
+
+ regval_ctr_addvalue( val, "Default Devmode", REG_BINARY, prs_data_p(&prs), offset );
+ }
+
+
+ }
+
+ prs_mem_clear( &prs );
+ prs_set_offset( &prs, 0 );
+
+ if ( info2->secdesc_buf && info2->secdesc_buf->len )
+ {
+ if ( sec_io_desc("sec_desc", &info2->secdesc_buf->sec, &prs, 0 ) ) {
+
+ offset = prs_offset( &prs );
+
+ regval_ctr_addvalue( val, "Security", REG_BINARY, prs_data_p(&prs), offset );
+ }
+ }
+
+ prs_mem_free( &prs );
+
+ num_values = regval_ctr_numvals( val );
+
+ goto done;
+
+ }
+
+ /* now enumerate the key */
+
+ if ( !W_ERROR_IS_OK( get_a_printer(NULL, &printer, 2, printername) ) )
+ goto done;
+
+ /* iterate over all printer data and fill the regval container */
+
+ p_data = &printer->info_2->data;
+ if ( (key_index = lookup_printerkey( p_data, new_path )) == -1 ) {
+ DEBUG(10,("print_subpath_values_printer: Unknown keyname [%s]\n", new_path));
+ goto done;
+ }
+
+ num_values = regval_ctr_numvals( &p_data->keys[key_index].values );
+
+ for ( i=0; i<num_values; i++ )
+ regval_ctr_copyvalue( val, regval_ctr_specific_value(&p_data->keys[key_index].values, i) );
+
+
+done:
+ if ( printer )
+ free_a_printer( &printer, 2 );
+
+ SAFE_FREE( key2 );
+
+ return num_values;
+}
+
+/**********************************************************************
+ Routine to handle enumeration of subkeys and values
+ below KEY_PRINTING (depending on whether or not subkeys/val are
+ valid pointers.
+ *********************************************************************/
+
+static int handle_printing_subpath( char *key, REGSUBKEY_CTR *subkeys, REGVAL_CTR *val )
+{
+ int result = 0;
+ char *p, *base;
+ int i;
+
+ DEBUG(10,("handle_printing_subpath: key=>[%s]\n", key ));
+
+ /*
+ * break off the first part of the path
+ * topmost base **must** be one of the strings
+ * in top_level_keys[]
+ */
+
+ reg_split_path( key, &base, &p);
+
+ for ( i=0; i<MAX_TOP_LEVEL_KEYS; i++ ) {
+ if ( StrCaseCmp( top_level_keys[i], base ) == 0 )
+ break;
+ }
+
+ DEBUG(10,("handle_printing_subpath: base=>[%s], i==[%d]\n", base, i));
+
+ if ( !(i < MAX_TOP_LEVEL_KEYS) )
+ return -1;
+
+ /* Call routine to handle each top level key */
+ switch ( i )
+ {
+ case KEY_INDEX_ENVIR:
+ if ( subkeys )
+ print_subpath_environments( p, subkeys );
+ if ( val )
+ print_subpath_values_environments( p, val );
+ break;
+
+ case KEY_INDEX_FORMS:
+ if ( subkeys )
+ print_subpath_forms( p, subkeys );
+ if ( val )
+ print_subpath_values_forms( p, val );
+ break;
+
+ case KEY_INDEX_PRINTER:
+ if ( subkeys )
+ print_subpath_printers( p, subkeys );
+ if ( val )
+ print_subpath_values_printers( p, val );
+ break;
+
+ /* default case for top level key that has no handler */
+
+ default:
+ break;
+ }
+
+
+
+ return result;
+
+}
+/**********************************************************************
+ Enumerate registry subkey names given a registry path.
+ Caller is responsible for freeing memory to **subkeys
+ *********************************************************************/
+
+int printing_subkey_info( char *key, REGSUBKEY_CTR *subkey_ctr )
+{
+ char *path;
+ BOOL top_level = False;
+ int num_subkeys = 0;
+
+ DEBUG(10,("printing_subkey_info: key=>[%s]\n", key));
+
+ path = trim_reg_path( key );
+
+ /* check to see if we are dealing with the top level key */
+
+ if ( !path )
+ top_level = True;
+
+ if ( top_level ) {
+ for ( num_subkeys=0; num_subkeys<MAX_TOP_LEVEL_KEYS; num_subkeys++ )
+ regsubkey_ctr_addkey( subkey_ctr, top_level_keys[num_subkeys] );
+ }
+ else
+ num_subkeys = handle_printing_subpath( path, subkey_ctr, NULL );
+
+ SAFE_FREE( path );
+
+ return num_subkeys;
+}
+
+/**********************************************************************
+ Enumerate registry values given a registry path.
+ Caller is responsible for freeing memory
+ *********************************************************************/
+
+int printing_value_info( char *key, REGVAL_CTR *val )
+{
+ char *path;
+ BOOL top_level = False;
+ int num_values = 0;
+
+ DEBUG(10,("printing_value_info: key=>[%s]\n", key));
+
+ path = trim_reg_path( key );
+
+ /* check to see if we are dealing with the top level key */
+
+ if ( !path )
+ top_level = True;
+
+ /* fill in values from the getprinterdata_printer_server() */
+ if ( top_level )
+ num_values = 0;
+ else
+ num_values = handle_printing_subpath( path, NULL, val );
+
+
+ return num_values;
+}
+
+/**********************************************************************
+ Stub function which always returns failure since we don't want
+ people storing printing information directly via regostry calls
+ (for now at least)
+ *********************************************************************/
+
+BOOL printing_store_subkey( char *key, REGSUBKEY_CTR *subkeys )
+{
+ return False;
+}
+
+/**********************************************************************
+ Stub function which always returns failure since we don't want
+ people storing printing information directly via regostry calls
+ (for now at least)
+ *********************************************************************/
+
+BOOL printing_store_value( char *key, REGVAL_CTR *val )
+{
+ return False;
+}
+
+/*
+ * Table of function pointers for accessing printing data
+ */
+
+REGISTRY_OPS printing_ops = {
+ printing_subkey_info,
+ printing_value_info,
+ printing_store_subkey,
+ printing_store_value
+};
+
+
diff --git a/source/rpc_client/.cvsignore b/source/rpc_client/.cvsignore
new file mode 100644
index 00000000000..07da2225c72
--- /dev/null
+++ b/source/rpc_client/.cvsignore
@@ -0,0 +1,3 @@
+*.po
+*.po32
+
diff --git a/source/rpc_client/cli_dfs.c b/source/rpc_client/cli_dfs.c
new file mode 100644
index 00000000000..2136b69df07
--- /dev/null
+++ b/source/rpc_client/cli_dfs.c
@@ -0,0 +1,247 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+ Copyright (C) Tim Potter 2000-2001,
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* Query DFS support */
+
+NTSTATUS cli_dfs_exist(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ BOOL *dfs_exists)
+{
+ prs_struct qbuf, rbuf;
+ DFS_Q_DFS_EXIST q;
+ DFS_R_DFS_EXIST r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_dfs_q_dfs_exist(&q);
+
+ if (!dfs_io_q_dfs_exist("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, DFS_EXIST, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!dfs_io_r_dfs_exist("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return result */
+
+ *dfs_exists = (r.status != 0);
+
+ result = NT_STATUS_OK;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+NTSTATUS cli_dfs_add(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const char *entrypath, const char *servername,
+ const char *sharename, const char *comment, uint32 flags)
+{
+ prs_struct qbuf, rbuf;
+ DFS_Q_DFS_ADD q;
+ DFS_R_DFS_ADD r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_dfs_q_dfs_add(&q, entrypath, servername, sharename, comment,
+ flags);
+
+ if (!dfs_io_q_dfs_add("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, DFS_ADD, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!dfs_io_r_dfs_add("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return result */
+
+ result = werror_to_ntstatus(r.status);
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+NTSTATUS cli_dfs_remove(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const char *entrypath, const char *servername,
+ const char *sharename)
+{
+ prs_struct qbuf, rbuf;
+ DFS_Q_DFS_REMOVE q;
+ DFS_R_DFS_REMOVE r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_dfs_q_dfs_remove(&q, entrypath, servername, sharename);
+
+ if (!dfs_io_q_dfs_remove("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, DFS_REMOVE, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!dfs_io_r_dfs_remove("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return result */
+
+ result = werror_to_ntstatus(r.status);
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+NTSTATUS cli_dfs_get_info(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const char *entrypath, const char *servername,
+ const char *sharename, uint32 info_level,
+ DFS_INFO_CTR *ctr)
+
+{
+ prs_struct qbuf, rbuf;
+ DFS_Q_DFS_GET_INFO q;
+ DFS_R_DFS_GET_INFO r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_dfs_q_dfs_get_info(&q, entrypath, servername, sharename,
+ info_level);
+
+ if (!dfs_io_q_dfs_get_info("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, DFS_GET_INFO, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!dfs_io_r_dfs_get_info("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return result */
+
+ result = werror_to_ntstatus(r.status);
+ *ctr = r.ctr;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Enumerate dfs shares */
+
+NTSTATUS cli_dfs_enum(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 info_level, DFS_INFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ DFS_Q_DFS_ENUM q;
+ DFS_R_DFS_ENUM r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_dfs_q_dfs_enum(&q, info_level, ctr);
+
+ if (!dfs_io_q_dfs_enum("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, DFS_ENUM, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ r.ctr = ctr;
+
+ if (!dfs_io_r_dfs_enum("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return result */
+
+ result = werror_to_ntstatus(r.status);
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
diff --git a/source/rpc_client/cli_ds.c b/source/rpc_client/cli_ds.c
new file mode 100644
index 00000000000..09e63a47147
--- /dev/null
+++ b/source/rpc_client/cli_ds.c
@@ -0,0 +1,157 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+ Copyright (C) Gerald Carter 2002,
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* implementations of client side DsXXX() functions */
+
+/********************************************************************
+ Get information about the server and directory services
+********************************************************************/
+
+NTSTATUS cli_ds_getprimarydominfo(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint16 level, DS_DOMINFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ DS_Q_GETPRIMDOMINFO q;
+ DS_R_GETPRIMDOMINFO r;
+ NTSTATUS result;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ q.level = level;
+
+ if (!ds_io_q_getprimdominfo("", &qbuf, 0, &q)
+ || !rpc_api_pipe_req(cli, DS_GETPRIMDOMINFO, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!ds_io_r_getprimdominfo("", &rbuf, 0, &r)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Return basic info - if we are requesting at info != 1 then
+ there could be trouble. */
+
+ result = r.status;
+
+ if ( r.ptr && ctr ) {
+ ctr->basic = talloc(mem_ctx, sizeof(DSROLE_PRIMARY_DOMAIN_INFO_BASIC));
+ if (!ctr->basic)
+ goto done;
+ memcpy(ctr->basic, r.info.basic, sizeof(DSROLE_PRIMARY_DOMAIN_INFO_BASIC));
+ }
+
+done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/********************************************************************
+ Enumerate trusted domains in an AD forest
+********************************************************************/
+
+NTSTATUS cli_ds_enum_domain_trusts(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const char *server, uint32 flags,
+ struct ds_domain_trust **trusts, uint32 *num_domains)
+{
+ prs_struct qbuf, rbuf;
+ DS_Q_ENUM_DOM_TRUSTS q;
+ DS_R_ENUM_DOM_TRUSTS r;
+ NTSTATUS result;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ init_q_ds_enum_domain_trusts( &q, server, flags );
+
+ if (!ds_io_q_enum_domain_trusts("", &qbuf, 0, &q)
+ || !rpc_api_pipe_req(cli, DS_ENUM_DOM_TRUSTS, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!ds_io_r_enum_domain_trusts("", &rbuf, 0, &r)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ result = r.status;
+
+ if ( NT_STATUS_IS_OK(result) ) {
+ int i;
+
+ *num_domains = r.num_domains;
+ *trusts = (struct ds_domain_trust*)talloc(mem_ctx, r.num_domains*sizeof(**trusts));
+
+ for ( i=0; i< *num_domains; i++ ) {
+ (*trusts)[i].flags = r.domains.trusts[i].flags;
+ (*trusts)[i].parent_index = r.domains.trusts[i].parent_index;
+ (*trusts)[i].trust_type = r.domains.trusts[i].trust_type;
+ (*trusts)[i].trust_attributes = r.domains.trusts[i].trust_attributes;
+ (*trusts)[i].guid = r.domains.trusts[i].guid;
+
+ if (r.domains.trusts[i].sid_ptr) {
+ sid_copy(&(*trusts)[i].sid, &r.domains.trusts[i].sid.sid);
+ } else {
+ ZERO_STRUCT((*trusts)[i].sid);
+ }
+
+ if (r.domains.trusts[i].netbios_ptr) {
+ (*trusts)[i].netbios_domain = unistr2_tdup( mem_ctx, &r.domains.trusts[i].netbios_domain );
+ } else {
+ (*trusts)[i].netbios_domain = NULL;
+ }
+
+ if (r.domains.trusts[i].dns_ptr) {
+ (*trusts)[i].dns_domain = unistr2_tdup( mem_ctx, &r.domains.trusts[i].dns_domain );
+ } else {
+ (*trusts)[i].dns_domain = NULL;
+ }
+ }
+ }
+
+done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+
diff --git a/source/rpc_client/cli_echo.c b/source/rpc_client/cli_echo.c
new file mode 100644
index 00000000000..03a4ab36ee0
--- /dev/null
+++ b/source/rpc_client/cli_echo.c
@@ -0,0 +1,187 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ RPC pipe client
+
+ Copyright (C) Tim Potter 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+NTSTATUS cli_echo_add_one(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 request, uint32 *response)
+{
+ prs_struct qbuf, rbuf;
+ ECHO_Q_ADD_ONE q;
+ ECHO_R_ADD_ONE r;
+ BOOL result = False;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_echo_q_add_one(&q, request);
+
+ if (!echo_io_q_add_one("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, ECHO_ADD_ONE, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!echo_io_r_add_one("", &r, &rbuf, 0))
+ goto done;
+
+ if (response)
+ *response = r.response;
+
+ result = True;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS cli_echo_data(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 size, char *in_data, char **out_data)
+{
+ prs_struct qbuf, rbuf;
+ ECHO_Q_ECHO_DATA q;
+ ECHO_R_ECHO_DATA r;
+ BOOL result = False;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_echo_q_echo_data(&q, size, in_data);
+
+ if (!echo_io_q_echo_data("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, ECHO_DATA, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!echo_io_r_echo_data("", &r, &rbuf, 0))
+ goto done;
+
+ result = True;
+
+ if (out_data) {
+ *out_data = talloc(mem_ctx, size);
+ memcpy(*out_data, r.data, size);
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS cli_echo_sink_data(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 size, char *in_data)
+{
+ prs_struct qbuf, rbuf;
+ ECHO_Q_SINK_DATA q;
+ ECHO_R_SINK_DATA r;
+ BOOL result = False;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_echo_q_sink_data(&q, size, in_data);
+
+ if (!echo_io_q_sink_data("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, ECHO_SINK_DATA, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!echo_io_r_sink_data("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ result = True;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS cli_echo_source_data(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 size, char **out_data)
+{
+ prs_struct qbuf, rbuf;
+ ECHO_Q_SOURCE_DATA q;
+ ECHO_R_SOURCE_DATA r;
+ BOOL result = False;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_echo_q_source_data(&q, size);
+
+ if (!echo_io_q_source_data("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, ECHO_SOURCE_DATA, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!echo_io_r_source_data("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ result = True;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
diff --git a/source/rpc_client/cli_epmapper.c b/source/rpc_client/cli_epmapper.c
new file mode 100644
index 00000000000..66362f16209
--- /dev/null
+++ b/source/rpc_client/cli_epmapper.c
@@ -0,0 +1,61 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+NTSTATUS cli_epm_map(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ EPM_HANDLE *handle, EPM_TOWER **tower,
+ EPM_HANDLE *entry_handle, uint32 *num_towers)
+{
+ prs_struct qbuf, rbuf;
+ EPM_Q_MAP q;
+ EPM_R_MAP r;
+ BOOL result = False;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_epm_q_map(mem_ctx, &q, *tower, *num_towers);
+
+ if (!epm_io_q_map("map_query", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, EPM_MAP_PIPE_NAME, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!epm_io_r_map("map_reply", &r, &rbuf, 0))
+ goto done;
+
+ result = True;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
diff --git a/source/rpc_client/cli_lsarpc.c b/source/rpc_client/cli_lsarpc.c
new file mode 100644
index 00000000000..980a681387f
--- /dev/null
+++ b/source/rpc_client/cli_lsarpc.c
@@ -0,0 +1,1466 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+ Copyright (C) Tim Potter 2000-2001,
+ Copyright (C) Andrew Tridgell 1992-1997,2000,
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1997,2000,
+ Copyright (C) Paul Ashton 1997,2000,
+ Copyright (C) Elrond 2000,
+ Copyright (C) Rafal Szczesniak 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/** @defgroup lsa LSA - Local Security Architecture
+ * @ingroup rpc_client
+ *
+ * @{
+ **/
+
+/**
+ * @file cli_lsarpc.c
+ *
+ * RPC client routines for the LSA RPC pipe. LSA means "local
+ * security authority", which is half of a password database.
+ **/
+
+/** Open a LSA policy handle
+ *
+ * @param cli Handle on an initialised SMB connection */
+
+NTSTATUS cli_lsa_open_policy(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ BOOL sec_qos, uint32 des_access, POLICY_HND *pol)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_OPEN_POL q;
+ LSA_R_OPEN_POL r;
+ LSA_SEC_QOS qos;
+ NTSTATUS result;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ if (sec_qos) {
+ init_lsa_sec_qos(&qos, 2, 1, 0);
+ init_q_open_pol(&q, '\\', 0, des_access, &qos);
+ } else {
+ init_q_open_pol(&q, '\\', 0, des_access, NULL);
+ }
+
+ /* Marshall data and send request */
+
+ if (!lsa_io_q_open_pol("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_OPENPOLICY, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_open_pol("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *pol = r.pol;
+#ifdef __INSURE__
+ pol->marker = malloc(1);
+#endif
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Open a LSA policy handle
+ *
+ * @param cli Handle on an initialised SMB connection
+ */
+
+NTSTATUS cli_lsa_open_policy2(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ BOOL sec_qos, uint32 des_access, POLICY_HND *pol)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_OPEN_POL2 q;
+ LSA_R_OPEN_POL2 r;
+ LSA_SEC_QOS qos;
+ NTSTATUS result;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ if (sec_qos) {
+ init_lsa_sec_qos(&qos, 2, 1, 0);
+ init_q_open_pol2(&q, cli->srv_name_slash, 0, des_access,
+ &qos);
+ } else {
+ init_q_open_pol2(&q, cli->srv_name_slash, 0, des_access,
+ NULL);
+ }
+
+ /* Marshall data and send request */
+
+ if (!lsa_io_q_open_pol2("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_OPENPOLICY2, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_open_pol2("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *pol = r.pol;
+#ifdef __INSURE__
+ pol->marker = (char *)malloc(1);
+#endif
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Close a LSA policy handle */
+
+NTSTATUS cli_lsa_close(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_CLOSE q;
+ LSA_R_CLOSE r;
+ NTSTATUS result;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_lsa_q_close(&q, pol);
+
+ if (!lsa_io_q_close("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_CLOSE, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_close("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+#ifdef __INSURE__
+ SAFE_FREE(pol->marker);
+#endif
+ *pol = r.pol;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Lookup a list of sids */
+
+NTSTATUS cli_lsa_lookup_sids(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, int num_sids, const DOM_SID *sids,
+ char ***domains, char ***names, uint32 **types)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_LOOKUP_SIDS q;
+ LSA_R_LOOKUP_SIDS r;
+ DOM_R_REF ref;
+ LSA_TRANS_NAME_ENUM t_names;
+ NTSTATUS result;
+ int i;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_q_lookup_sids(mem_ctx, &q, pol, num_sids, sids, 1);
+
+ if (!lsa_io_q_lookup_sids("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_LOOKUPSIDS, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ ZERO_STRUCT(ref);
+ ZERO_STRUCT(t_names);
+
+ r.dom_ref = &ref;
+ r.names = &t_names;
+
+ if (!lsa_io_r_lookup_sids("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ result = r.status;
+
+ if (!NT_STATUS_IS_OK(result) &&
+ NT_STATUS_V(result) != NT_STATUS_V(STATUS_SOME_UNMAPPED)) {
+
+ /* An actual error occured */
+
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (r.mapped_count == 0) {
+ result = NT_STATUS_NONE_MAPPED;
+ goto done;
+ }
+
+ if (!((*domains) = (char **)talloc(mem_ctx, sizeof(char *) *
+ num_sids))) {
+ DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!((*names) = (char **)talloc(mem_ctx, sizeof(char *) *
+ num_sids))) {
+ DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!((*types) = (uint32 *)talloc(mem_ctx, sizeof(uint32) *
+ num_sids))) {
+ DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ for (i = 0; i < num_sids; i++) {
+ fstring name, dom_name;
+ uint32 dom_idx = t_names.name[i].domain_idx;
+
+ /* Translate optimised name through domain index array */
+
+ if (dom_idx != 0xffffffff) {
+
+ rpcstr_pull_unistr2_fstring(
+ dom_name, &ref.ref_dom[dom_idx].uni_dom_name);
+ rpcstr_pull_unistr2_fstring(
+ name, &t_names.uni_name[i]);
+
+ (*names)[i] = talloc_strdup(mem_ctx, name);
+ (*domains)[i] = talloc_strdup(mem_ctx, dom_name);
+ (*types)[i] = t_names.name[i].sid_name_use;
+
+ if (((*names)[i] == NULL) || ((*domains)[i] == NULL)) {
+ DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ } else {
+ (*names)[i] = NULL;
+ (*domains)[i] = NULL;
+ (*types)[i] = SID_NAME_UNKNOWN;
+ }
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Lookup a list of names */
+
+NTSTATUS cli_lsa_lookup_names(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, int num_names,
+ const char **names, DOM_SID **sids,
+ uint32 **types)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_LOOKUP_NAMES q;
+ LSA_R_LOOKUP_NAMES r;
+ DOM_R_REF ref;
+ NTSTATUS result;
+ int i;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_q_lookup_names(mem_ctx, &q, pol, num_names, names);
+
+ if (!lsa_io_q_lookup_names("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_LOOKUPNAMES, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ ZERO_STRUCT(ref);
+ r.dom_ref = &ref;
+
+ if (!lsa_io_r_lookup_names("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ result = r.status;
+
+ if (!NT_STATUS_IS_OK(result) && NT_STATUS_V(result) !=
+ NT_STATUS_V(STATUS_SOME_UNMAPPED)) {
+
+ /* An actual error occured */
+
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (r.mapped_count == 0) {
+ result = NT_STATUS_NONE_MAPPED;
+ goto done;
+ }
+
+ if (!((*sids = (DOM_SID *)talloc(mem_ctx, sizeof(DOM_SID) *
+ num_names)))) {
+ DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!((*types = (uint32 *)talloc(mem_ctx, sizeof(uint32) *
+ num_names)))) {
+ DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ for (i = 0; i < num_names; i++) {
+ DOM_RID2 *t_rids = r.dom_rid;
+ uint32 dom_idx = t_rids[i].rid_idx;
+ uint32 dom_rid = t_rids[i].rid;
+ DOM_SID *sid = &(*sids)[i];
+
+ /* Translate optimised sid through domain index array */
+
+ if (dom_idx != 0xffffffff) {
+
+ sid_copy(sid, &ref.ref_dom[dom_idx].ref_dom.sid);
+
+ if (dom_rid != 0xffffffff) {
+ sid_append_rid(sid, dom_rid);
+ }
+
+ (*types)[i] = t_rids[i].type;
+ } else {
+ ZERO_STRUCTP(sid);
+ (*types)[i] = SID_NAME_UNKNOWN;
+ }
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Query info policy
+ *
+ * @param domain_sid - returned remote server's domain sid */
+
+NTSTATUS cli_lsa_query_info_policy(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint16 info_class,
+ char **domain_name, DOM_SID **domain_sid)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_QUERY_INFO q;
+ LSA_R_QUERY_INFO r;
+ NTSTATUS result;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_q_query(&q, pol, info_class);
+
+ if (!lsa_io_q_query("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_QUERYINFOPOLICY, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_query("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ switch (info_class) {
+
+ case 3:
+ if (domain_name && (r.dom.id3.buffer_dom_name != 0)) {
+ *domain_name = unistr2_tdup(mem_ctx,
+ &r.dom.id3.
+ uni_domain_name);
+ }
+
+ if (domain_sid && (r.dom.id3.buffer_dom_sid != 0)) {
+ *domain_sid = talloc(mem_ctx, sizeof(**domain_sid));
+ if (*domain_sid) {
+ sid_copy(*domain_sid, &r.dom.id3.dom_sid.sid);
+ }
+ }
+
+ break;
+
+ case 5:
+
+ if (domain_name && (r.dom.id5.buffer_dom_name != 0)) {
+ *domain_name = unistr2_tdup(mem_ctx,
+ &r.dom.id5.
+ uni_domain_name);
+ }
+
+ if (domain_sid && (r.dom.id5.buffer_dom_sid != 0)) {
+ *domain_sid = talloc(mem_ctx, sizeof(**domain_sid));
+ if (*domain_sid) {
+ sid_copy(*domain_sid, &r.dom.id5.dom_sid.sid);
+ }
+ }
+ break;
+
+ default:
+ DEBUG(3, ("unknown info class %d\n", info_class));
+ break;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Query info policy2
+ *
+ * @param domain_name - returned remote server's domain name
+ * @param dns_name - returned remote server's dns domain name
+ * @param forest_name - returned remote server's forest name
+ * @param domain_guid - returned remote server's domain guid
+ * @param domain_sid - returned remote server's domain sid */
+
+NTSTATUS cli_lsa_query_info_policy2(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint16 info_class,
+ char **domain_name, char **dns_name,
+ char **forest_name, struct uuid **domain_guid,
+ DOM_SID **domain_sid)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_QUERY_INFO2 q;
+ LSA_R_QUERY_INFO2 r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (info_class != 12)
+ goto done;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_q_query2(&q, pol, info_class);
+
+ if (!lsa_io_q_query_info2("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_QUERYINFO2, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_query_info2("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ ZERO_STRUCTP(domain_guid);
+
+ if (domain_name && r.info.dns_dom_info.hdr_nb_dom_name.buffer) {
+ *domain_name = unistr2_tdup(mem_ctx,
+ &r.info.dns_dom_info
+ .uni_nb_dom_name);
+ }
+ if (dns_name && r.info.dns_dom_info.hdr_dns_dom_name.buffer) {
+ *dns_name = unistr2_tdup(mem_ctx,
+ &r.info.dns_dom_info
+ .uni_dns_dom_name);
+ }
+ if (forest_name && r.info.dns_dom_info.hdr_forest_name.buffer) {
+ *forest_name = unistr2_tdup(mem_ctx,
+ &r.info.dns_dom_info
+ .uni_forest_name);
+ }
+
+ if (domain_guid) {
+ *domain_guid = talloc(mem_ctx, sizeof(**domain_guid));
+ memcpy(*domain_guid,
+ &r.info.dns_dom_info.dom_guid,
+ sizeof(struct uuid));
+ }
+
+ if (domain_sid && r.info.dns_dom_info.ptr_dom_sid != 0) {
+ *domain_sid = talloc(mem_ctx, sizeof(**domain_sid));
+ if (*domain_sid) {
+ sid_copy(*domain_sid,
+ &r.info.dns_dom_info.dom_sid.sid);
+ }
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/**
+ * Enumerate list of trusted domains
+ *
+ * @param cli client state (cli_state) structure of the connection
+ * @param mem_ctx memory context
+ * @param pol opened lsa policy handle
+ * @param enum_ctx enumeration context ie. index of first returned domain entry
+ * @param pref_num_domains preferred max number of entries returned in one response
+ * @param num_domains total number of trusted domains returned by response
+ * @param domain_names returned trusted domain names
+ * @param domain_sids returned trusted domain sids
+ *
+ * @return nt status code of response
+ **/
+
+NTSTATUS cli_lsa_enum_trust_dom(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint32 *enum_ctx,
+ uint32 *num_domains,
+ char ***domain_names, DOM_SID **domain_sids)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_ENUM_TRUST_DOM q;
+ LSA_R_ENUM_TRUST_DOM r;
+ NTSTATUS result;
+ int i;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ /* 64k is enough for about 2000 trusted domains */
+ init_q_enum_trust_dom(&q, pol, *enum_ctx, 0x10000);
+
+ if (!lsa_io_q_enum_trust_dom("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_ENUMTRUSTDOM, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_enum_trust_dom("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ result = r.status;
+
+ if (!NT_STATUS_IS_OK(result) &&
+ !NT_STATUS_EQUAL(result, NT_STATUS_NO_MORE_ENTRIES) &&
+ !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) {
+
+ /* An actual error ocured */
+
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (r.num_domains) {
+
+ /* Allocate memory for trusted domain names and sids */
+
+ *domain_names = (char **)talloc(mem_ctx, sizeof(char *) *
+ r.num_domains);
+
+ if (!*domain_names) {
+ DEBUG(0, ("cli_lsa_enum_trust_dom(): out of memory\n"));
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ *domain_sids = (DOM_SID *)talloc(mem_ctx, sizeof(DOM_SID) *
+ r.num_domains);
+ if (!domain_sids) {
+ DEBUG(0, ("cli_lsa_enum_trust_dom(): out of memory\n"));
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ /* Copy across names and sids */
+
+ for (i = 0; i < r.num_domains; i++) {
+ fstring tmp;
+
+ unistr2_to_ascii(tmp, &r.uni_domain_name[i],
+ sizeof(tmp) - 1);
+ (*domain_names)[i] = talloc_strdup(mem_ctx, tmp);
+ sid_copy(&(*domain_sids)[i], &r.domain_sid[i].sid);
+ }
+ }
+
+ *num_domains = r.num_domains;
+ *enum_ctx = r.enum_context;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+
+/** Enumerate privileges*/
+
+NTSTATUS cli_lsa_enum_privilege(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint32 *enum_context, uint32 pref_max_length,
+ uint32 *count, char ***privs_name, uint32 **privs_high, uint32 **privs_low)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_ENUM_PRIVS q;
+ LSA_R_ENUM_PRIVS r;
+ NTSTATUS result;
+ int i;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_q_enum_privs(&q, pol, *enum_context, pref_max_length);
+
+ if (!lsa_io_q_enum_privs("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_ENUM_PRIVS, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_enum_privs("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ *enum_context = r.enum_context;
+ *count = r.count;
+
+ if (!((*privs_name = (char **)talloc(mem_ctx, sizeof(char *) * r.count)))) {
+ DEBUG(0, ("(cli_lsa_enum_privilege): out of memory\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!((*privs_high = (uint32 *)talloc(mem_ctx, sizeof(uint32) * r.count)))) {
+ DEBUG(0, ("(cli_lsa_enum_privilege): out of memory\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!((*privs_low = (uint32 *)talloc(mem_ctx, sizeof(uint32) * r.count)))) {
+ DEBUG(0, ("(cli_lsa_enum_privilege): out of memory\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ for (i = 0; i < r.count; i++) {
+ fstring name;
+
+ rpcstr_pull_unistr2_fstring( name, &r.privs[i].name);
+
+ (*privs_name)[i] = talloc_strdup(mem_ctx, name);
+
+ (*privs_high)[i] = r.privs[i].luid_high;
+ (*privs_low)[i] = r.privs[i].luid_low;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Get privilege name */
+
+NTSTATUS cli_lsa_get_dispname(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, const char *name,
+ uint16 lang_id, uint16 lang_id_sys,
+ fstring description, uint16 *lang_id_desc)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_PRIV_GET_DISPNAME q;
+ LSA_R_PRIV_GET_DISPNAME r;
+ NTSTATUS result;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_lsa_priv_get_dispname(&q, pol, name, lang_id, lang_id_sys);
+
+ if (!lsa_io_q_priv_get_dispname("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_PRIV_GET_DISPNAME, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_priv_get_dispname("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ rpcstr_pull_unistr2_fstring(description , &r.desc);
+ *lang_id_desc = r.lang_id;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Enumerate list of SIDs */
+
+NTSTATUS cli_lsa_enum_sids(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint32 *enum_ctx, uint32 pref_max_length,
+ uint32 *num_sids, DOM_SID **sids)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_ENUM_ACCOUNTS q;
+ LSA_R_ENUM_ACCOUNTS r;
+ NTSTATUS result;
+ int i;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_lsa_q_enum_accounts(&q, pol, *enum_ctx, pref_max_length);
+
+ if (!lsa_io_q_enum_accounts("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_ENUM_ACCOUNTS, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_enum_accounts("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ result = r.status;
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ if (r.sids.num_entries==0)
+ goto done;
+
+ /* Return output parameters */
+
+ *sids = (DOM_SID *)talloc(mem_ctx, sizeof(DOM_SID) * r.sids.num_entries);
+ if (!*sids) {
+ DEBUG(0, ("(cli_lsa_enum_sids): out of memory\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Copy across names and sids */
+
+ for (i = 0; i < r.sids.num_entries; i++) {
+ sid_copy(&(*sids)[i], &r.sids.sid[i].sid);
+ }
+
+ *num_sids= r.sids.num_entries;
+ *enum_ctx = r.enum_context;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Create a LSA user handle
+ *
+ * @param cli Handle on an initialised SMB connection
+ *
+ * FIXME: The code is actually identical to open account
+ * TODO: Check and code what the function should exactly do
+ *
+ * */
+
+NTSTATUS cli_lsa_create_account(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *dom_pol, DOM_SID *sid, uint32 desired_access,
+ POLICY_HND *user_pol)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_CREATEACCOUNT q;
+ LSA_R_CREATEACCOUNT r;
+ NTSTATUS result;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ init_lsa_q_create_account(&q, dom_pol, sid, desired_access);
+
+ /* Marshall data and send request */
+
+ if (!lsa_io_q_create_account("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_CREATEACCOUNT, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_create_account("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *user_pol = r.pol;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Open a LSA user handle
+ *
+ * @param cli Handle on an initialised SMB connection */
+
+NTSTATUS cli_lsa_open_account(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *dom_pol, DOM_SID *sid, uint32 des_access,
+ POLICY_HND *user_pol)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_OPENACCOUNT q;
+ LSA_R_OPENACCOUNT r;
+ NTSTATUS result;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ init_lsa_q_open_account(&q, dom_pol, sid, des_access);
+
+ /* Marshall data and send request */
+
+ if (!lsa_io_q_open_account("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_OPENACCOUNT, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_open_account("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *user_pol = r.pol;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Enumerate user privileges
+ *
+ * @param cli Handle on an initialised SMB connection */
+
+NTSTATUS cli_lsa_enum_privsaccount(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint32 *count, LUID_ATTR **set)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_ENUMPRIVSACCOUNT q;
+ LSA_R_ENUMPRIVSACCOUNT r;
+ NTSTATUS result;
+ int i;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ init_lsa_q_enum_privsaccount(&q, pol);
+
+ /* Marshall data and send request */
+
+ if (!lsa_io_q_enum_privsaccount("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_ENUMPRIVSACCOUNT, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_enum_privsaccount("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ if (r.count == 0)
+ goto done;
+
+ if (!((*set = (LUID_ATTR *)talloc(mem_ctx, sizeof(LUID_ATTR) * r.count)))) {
+ DEBUG(0, ("(cli_lsa_enum_privsaccount): out of memory\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ for (i=0; i<r.count; i++) {
+ (*set)[i].luid.low = r.set->set[i].luid.low;
+ (*set)[i].luid.high = r.set->set[i].luid.high;
+ (*set)[i].attr = r.set->set[i].attr;
+ }
+
+ *count=r.count;
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Get a privilege value given its name */
+
+NTSTATUS cli_lsa_lookupprivvalue(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, const char *name, LUID *luid)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_LOOKUPPRIVVALUE q;
+ LSA_R_LOOKUPPRIVVALUE r;
+ NTSTATUS result;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_lsa_q_lookupprivvalue(&q, pol, name);
+
+ if (!lsa_io_q_lookupprivvalue("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_LOOKUPPRIVVALUE, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_lookupprivvalue("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ (*luid).low=r.luid.low;
+ (*luid).high=r.luid.high;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Query LSA security object */
+
+NTSTATUS cli_lsa_query_secobj(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint32 sec_info,
+ SEC_DESC_BUF **psdb)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_QUERY_SEC_OBJ q;
+ LSA_R_QUERY_SEC_OBJ r;
+ NTSTATUS result;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_q_query_sec_obj(&q, pol, sec_info);
+
+ if (!lsa_io_q_query_sec_obj("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_QUERYSECOBJ, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_query_sec_obj("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (psdb)
+ *psdb = r.buf;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+
+/* Enumerate account rights This is similar to enum_privileges but
+ takes a SID directly, avoiding the open_account call.
+*/
+
+NTSTATUS cli_lsa_enum_account_rights(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, DOM_SID sid,
+ uint32 *count, char ***privs_name)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_ENUM_ACCT_RIGHTS q;
+ LSA_R_ENUM_ACCT_RIGHTS r;
+ NTSTATUS result;
+ int i;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+ init_q_enum_acct_rights(&q, pol, 2, &sid);
+
+ if (!lsa_io_q_enum_acct_rights("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_ENUMACCTRIGHTS, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!lsa_io_r_enum_acct_rights("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ *count = r.count;
+ if (! *count) {
+ goto done;
+ }
+
+ *privs_name = (char **)talloc(mem_ctx, (*count) * sizeof(char **));
+ for (i=0;i<*count;i++) {
+ pull_ucs2_talloc(mem_ctx, &(*privs_name)[i], r.rights.strings[i].string.buffer);
+ }
+
+done:
+
+ return result;
+}
+
+
+
+/* add account rights to an account. */
+
+NTSTATUS cli_lsa_add_account_rights(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, DOM_SID sid,
+ uint32 count, const char **privs_name)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_ADD_ACCT_RIGHTS q;
+ LSA_R_ADD_ACCT_RIGHTS r;
+ NTSTATUS result;
+
+ ZERO_STRUCT(q);
+
+ /* Initialise parse structures */
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+ init_q_add_acct_rights(&q, pol, &sid, count, privs_name);
+
+ if (!lsa_io_q_add_acct_rights("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_ADDACCTRIGHTS, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_add_acct_rights("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+done:
+
+ return result;
+}
+
+
+/* remove account rights for an account. */
+
+NTSTATUS cli_lsa_remove_account_rights(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, DOM_SID sid, BOOL removeall,
+ uint32 count, const char **privs_name)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_REMOVE_ACCT_RIGHTS q;
+ LSA_R_REMOVE_ACCT_RIGHTS r;
+ NTSTATUS result;
+
+ ZERO_STRUCT(q);
+
+ /* Initialise parse structures */
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+ init_q_remove_acct_rights(&q, pol, &sid, removeall?1:0, count, privs_name);
+
+ if (!lsa_io_q_remove_acct_rights("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_REMOVEACCTRIGHTS, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_remove_acct_rights("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+done:
+
+ return result;
+}
+
+
+#if 0
+
+/** An example of how to use the routines in this file. Fetch a DOMAIN
+ sid. Does complete cli setup / teardown anonymously. */
+
+BOOL fetch_domain_sid( char *domain, char *remote_machine, DOM_SID *psid)
+{
+ extern pstring global_myname;
+ struct cli_state cli;
+ NTSTATUS result;
+ POLICY_HND lsa_pol;
+ BOOL ret = False;
+
+ ZERO_STRUCT(cli);
+ if(cli_initialise(&cli) == False) {
+ DEBUG(0,("fetch_domain_sid: unable to initialize client connection.\n"));
+ return False;
+ }
+
+ if(!resolve_name( remote_machine, &cli.dest_ip, 0x20)) {
+ DEBUG(0,("fetch_domain_sid: Can't resolve address for %s\n", remote_machine));
+ goto done;
+ }
+
+ if (!cli_connect(&cli, remote_machine, &cli.dest_ip)) {
+ DEBUG(0,("fetch_domain_sid: unable to connect to SMB server on \
+machine %s. Error was : %s.\n", remote_machine, cli_errstr(&cli) ));
+ goto done;
+ }
+
+ if (!attempt_netbios_session_request(&cli, global_myname, remote_machine, &cli.dest_ip)) {
+ DEBUG(0,("fetch_domain_sid: machine %s rejected the NetBIOS session request.\n",
+ remote_machine));
+ goto done;
+ }
+
+ cli.protocol = PROTOCOL_NT1;
+
+ if (!cli_negprot(&cli)) {
+ DEBUG(0,("fetch_domain_sid: machine %s rejected the negotiate protocol. \
+Error was : %s.\n", remote_machine, cli_errstr(&cli) ));
+ goto done;
+ }
+
+ if (cli.protocol != PROTOCOL_NT1) {
+ DEBUG(0,("fetch_domain_sid: machine %s didn't negotiate NT protocol.\n",
+ remote_machine));
+ goto done;
+ }
+
+ /*
+ * Do an anonymous session setup.
+ */
+
+ if (!cli_session_setup(&cli, "", "", 0, "", 0, "")) {
+ DEBUG(0,("fetch_domain_sid: machine %s rejected the session setup. \
+Error was : %s.\n", remote_machine, cli_errstr(&cli) ));
+ goto done;
+ }
+
+ if (!(cli.sec_mode & NEGOTIATE_SECURITY_USER_LEVEL)) {
+ DEBUG(0,("fetch_domain_sid: machine %s isn't in user level security mode\n",
+ remote_machine));
+ goto done;
+ }
+
+ if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) {
+ DEBUG(0,("fetch_domain_sid: machine %s rejected the tconX on the IPC$ share. \
+Error was : %s.\n", remote_machine, cli_errstr(&cli) ));
+ goto done;
+ }
+
+ /* Fetch domain sid */
+
+ if (!cli_nt_session_open(&cli, PI_LSARPC)) {
+ DEBUG(0, ("fetch_domain_sid: Error connecting to SAM pipe\n"));
+ goto done;
+ }
+
+ result = cli_lsa_open_policy(&cli, cli.mem_ctx, True, SEC_RIGHTS_QUERY_VALUE, &lsa_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0, ("fetch_domain_sid: Error opening lsa policy handle. %s\n",
+ nt_errstr(result) ));
+ goto done;
+ }
+
+ result = cli_lsa_query_info_policy(&cli, cli.mem_ctx, &lsa_pol, 5, domain, psid);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0, ("fetch_domain_sid: Error querying lsa policy handle. %s\n",
+ nt_errstr(result) ));
+ goto done;
+ }
+
+ ret = True;
+
+ done:
+
+ cli_shutdown(&cli);
+ return ret;
+}
+
+#endif
+
+/** @} **/
diff --git a/source/rpc_client/cli_netlogon.c b/source/rpc_client/cli_netlogon.c
new file mode 100644
index 00000000000..f6d88a19501
--- /dev/null
+++ b/source/rpc_client/cli_netlogon.c
@@ -0,0 +1,802 @@
+/*
+ Unix SMB/CIFS implementation.
+ NT Domain Authentication SMB / MSRPC client
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Tim Potter 2001
+ Copyright (C) Paul Ashton 1997.
+ Copyright (C) Jeremy Allison 1998.
+ Copyright (C) Andrew Bartlett 2001.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* LSA Request Challenge. Sends our challenge to server, then gets
+ server response. These are used to generate the credentials. */
+
+NTSTATUS cli_net_req_chal(struct cli_state *cli, DOM_CHAL *clnt_chal,
+ DOM_CHAL *srv_chal)
+{
+ prs_struct qbuf, rbuf;
+ NET_Q_REQ_CHAL q;
+ NET_R_REQ_CHAL r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, cli->mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, cli->mem_ctx, UNMARSHALL);
+
+ /* create and send a MSRPC command with api NET_REQCHAL */
+
+ DEBUG(4,("cli_net_req_chal: LSA Request Challenge from %s to %s: %s\n",
+ global_myname(), cli->desthost, credstr(clnt_chal->data)));
+
+ /* store the parameters */
+ init_q_req_chal(&q, cli->srv_name_slash, global_myname(), clnt_chal);
+
+ /* Marshall data and send request */
+
+ if (!net_io_q_req_chal("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, NET_REQCHAL, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarhall response */
+
+ if (!net_io_r_req_chal("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ result = r.status;
+
+ /* Return result */
+
+ if (NT_STATUS_IS_OK(result)) {
+ memcpy(srv_chal, r.srv_chal.data, sizeof(srv_chal->data));
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/****************************************************************************
+LSA Authenticate 2
+
+Send the client credential, receive back a server credential.
+Ensure that the server credential returned matches the session key
+encrypt of the server challenge originally received. JRA.
+****************************************************************************/
+
+NTSTATUS cli_net_auth2(struct cli_state *cli,
+ uint16 sec_chan,
+ uint32 *neg_flags, DOM_CHAL *srv_chal)
+{
+ prs_struct qbuf, rbuf;
+ NET_Q_AUTH_2 q;
+ NET_R_AUTH_2 r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, cli->mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, cli->mem_ctx, UNMARSHALL);
+
+ /* create and send a MSRPC command with api NET_AUTH2 */
+
+ DEBUG(4,("cli_net_auth2: srv:%s acct:%s sc:%x mc: %s chal %s neg: %x\n",
+ cli->srv_name_slash, cli->mach_acct, sec_chan, global_myname(),
+ credstr(cli->clnt_cred.challenge.data), *neg_flags));
+
+ /* store the parameters */
+ init_q_auth_2(&q, cli->srv_name_slash, cli->mach_acct,
+ sec_chan, global_myname(), &cli->clnt_cred.challenge,
+ *neg_flags);
+
+ /* turn parameters into data stream */
+
+ if (!net_io_q_auth_2("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, NET_AUTH2, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!net_io_r_auth_2("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ result = r.status;
+
+ if (NT_STATUS_IS_OK(result)) {
+ UTIME zerotime;
+
+ /*
+ * Check the returned value using the initial
+ * server received challenge.
+ */
+
+ zerotime.time = 0;
+ if (cred_assert( &r.srv_chal, cli->sess_key, srv_chal,
+ zerotime) == 0) {
+
+ /*
+ * Server replied with bad credential. Fail.
+ */
+ DEBUG(0,("cli_net_auth2: server %s replied with bad credential (bad machine \
+password ?).\n", cli->desthost ));
+ result = NT_STATUS_ACCESS_DENIED;
+ goto done;
+ }
+ *neg_flags = r.srv_flgs.neg_flags;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/****************************************************************************
+LSA Authenticate 3
+
+Send the client credential, receive back a server credential.
+Ensure that the server credential returned matches the session key
+encrypt of the server challenge originally received. JRA.
+****************************************************************************/
+
+NTSTATUS cli_net_auth3(struct cli_state *cli,
+ uint16 sec_chan,
+ uint32 *neg_flags, DOM_CHAL *srv_chal)
+{
+ prs_struct qbuf, rbuf;
+ NET_Q_AUTH_3 q;
+ NET_R_AUTH_3 r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, cli->mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, cli->mem_ctx, UNMARSHALL);
+
+ /* create and send a MSRPC command with api NET_AUTH2 */
+
+ DEBUG(4,("cli_net_auth3: srv:%s acct:%s sc:%x mc: %s chal %s neg: %x\n",
+ cli->srv_name_slash, cli->mach_acct, sec_chan, global_myname(),
+ credstr(cli->clnt_cred.challenge.data), *neg_flags));
+
+ /* store the parameters */
+ init_q_auth_3(&q, cli->srv_name_slash, cli->mach_acct,
+ sec_chan, global_myname(), &cli->clnt_cred.challenge,
+ *neg_flags);
+
+ /* turn parameters into data stream */
+
+ if (!net_io_q_auth_3("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, NET_AUTH3, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!net_io_r_auth_3("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ result = r.status;
+
+ if (NT_STATUS_IS_OK(result)) {
+ UTIME zerotime;
+
+ /*
+ * Check the returned value using the initial
+ * server received challenge.
+ */
+
+ zerotime.time = 0;
+ if (cred_assert( &r.srv_chal, cli->sess_key, srv_chal,
+ zerotime) == 0) {
+
+ /*
+ * Server replied with bad credential. Fail.
+ */
+ DEBUG(0,("cli_net_auth3: server %s replied with bad credential (bad machine \
+password ?).\n", cli->desthost ));
+ result = NT_STATUS_ACCESS_DENIED;
+ goto done;
+ }
+ *neg_flags = r.srv_flgs.neg_flags;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Initialize domain session credentials */
+
+NTSTATUS cli_nt_setup_creds(struct cli_state *cli,
+ uint16 sec_chan,
+ const unsigned char mach_pwd[16], uint32 *neg_flags, int level)
+{
+ DOM_CHAL clnt_chal;
+ DOM_CHAL srv_chal;
+ UTIME zerotime;
+ NTSTATUS result;
+
+ /******************* Request Challenge ********************/
+
+ generate_random_buffer(clnt_chal.data, 8, False);
+
+ /* send a client challenge; receive a server challenge */
+ result = cli_net_req_chal(cli, &clnt_chal, &srv_chal);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0,("cli_nt_setup_creds: request challenge failed\n"));
+ return result;
+ }
+
+ /**************** Long-term Session key **************/
+
+ /* calculate the session key */
+ cred_session_key(&clnt_chal, &srv_chal, mach_pwd,
+ cli->sess_key);
+ memset((char *)cli->sess_key+8, '\0', 8);
+
+ /******************* Authenticate 2/3 ********************/
+
+ /* calculate auth-2/3 credentials */
+ zerotime.time = 0;
+ cred_create(cli->sess_key, &clnt_chal, zerotime, &cli->clnt_cred.challenge);
+
+ /*
+ * Send client auth-2/3 challenge.
+ * Receive an auth-2/3 challenge response and check it.
+ */
+ switch (level) {
+ case 2:
+ result = cli_net_auth2(cli, sec_chan, neg_flags, &srv_chal);
+ break;
+ case 3:
+ result = cli_net_auth3(cli, sec_chan, neg_flags, &srv_chal);
+ break;
+ default:
+ DEBUG(1,("cli_nt_setup_creds: unsupported auth level: %d\n", level));
+ break;
+ }
+
+ if (!NT_STATUS_IS_OK(result))
+ DEBUG(3,("cli_nt_setup_creds: auth%d challenge failed %s\n", level, nt_errstr(result)));
+
+ return result;
+}
+
+/* Logon Control 2 */
+
+NTSTATUS cli_netlogon_logon_ctrl2(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 query_level)
+{
+ prs_struct qbuf, rbuf;
+ NET_Q_LOGON_CTRL2 q;
+ NET_R_LOGON_CTRL2 r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ init_net_q_logon_ctrl2(&q, cli->srv_name_slash, query_level);
+
+ /* Marshall data and send request */
+
+ if (!net_io_q_logon_ctrl2("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, NET_LOGON_CTRL2, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!net_io_r_logon_ctrl2("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* GetDCName */
+
+NTSTATUS cli_netlogon_getdcname(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const char *domainname, fstring dcname)
+{
+ prs_struct qbuf, rbuf;
+ NET_Q_GETDCNAME q;
+ NET_R_GETDCNAME r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ init_net_q_getdcname(&q, cli->srv_name_slash, domainname);
+
+ /* Marshall data and send request */
+
+ if (!net_io_q_getdcname("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, NET_GETDCNAME, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!net_io_r_getdcname("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ result = r.status;
+
+ if (NT_STATUS_IS_OK(result))
+ rpcstr_pull_unistr2_fstring(dcname, &r.uni_dcname);
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/****************************************************************************
+Generate the next creds to use.
+****************************************************************************/
+
+static void gen_next_creds( struct cli_state *cli, DOM_CRED *new_clnt_cred)
+{
+ /*
+ * Create the new client credentials.
+ */
+
+ cli->clnt_cred.timestamp.time = time(NULL);
+
+ memcpy(new_clnt_cred, &cli->clnt_cred, sizeof(*new_clnt_cred));
+
+ /* Calculate the new credentials. */
+ cred_create(cli->sess_key, &(cli->clnt_cred.challenge),
+ new_clnt_cred->timestamp, &(new_clnt_cred->challenge));
+}
+
+/* Sam synchronisation */
+
+NTSTATUS cli_netlogon_sam_sync(struct cli_state *cli, TALLOC_CTX *mem_ctx, DOM_CRED *ret_creds,
+ uint32 database_id, uint32 next_rid, uint32 *num_deltas,
+ SAM_DELTA_HDR **hdr_deltas,
+ SAM_DELTA_CTR **deltas)
+{
+ prs_struct qbuf, rbuf;
+ NET_Q_SAM_SYNC q;
+ NET_R_SAM_SYNC r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ DOM_CRED clnt_creds;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ gen_next_creds(cli, &clnt_creds);
+
+ init_net_q_sam_sync(&q, cli->srv_name_slash, cli->clnt_name_slash + 2,
+ &clnt_creds, ret_creds, database_id, next_rid);
+
+ /* Marshall data and send request */
+
+ if (!net_io_q_sam_sync("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, NET_SAM_SYNC, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!net_io_r_sam_sync("", cli->sess_key, &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Return results */
+
+ result = r.status;
+ *num_deltas = r.num_deltas2;
+ *hdr_deltas = r.hdr_deltas;
+ *deltas = r.deltas;
+
+ memcpy(ret_creds, &r.srv_creds, sizeof(*ret_creds));
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Sam synchronisation */
+
+NTSTATUS cli_netlogon_sam_deltas(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 database_id, UINT64_S seqnum,
+ uint32 *num_deltas,
+ SAM_DELTA_HDR **hdr_deltas,
+ SAM_DELTA_CTR **deltas)
+{
+ prs_struct qbuf, rbuf;
+ NET_Q_SAM_DELTAS q;
+ NET_R_SAM_DELTAS r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ DOM_CRED clnt_creds;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ gen_next_creds(cli, &clnt_creds);
+
+ init_net_q_sam_deltas(&q, cli->srv_name_slash,
+ cli->clnt_name_slash + 2, &clnt_creds,
+ database_id, seqnum);
+
+ /* Marshall data and send request */
+
+ if (!net_io_q_sam_deltas("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, NET_SAM_DELTAS, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!net_io_r_sam_deltas("", cli->sess_key, &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Return results */
+
+ result = r.status;
+ *num_deltas = r.num_deltas2;
+ *hdr_deltas = r.hdr_deltas;
+ *deltas = r.deltas;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Logon domain user */
+
+NTSTATUS cli_netlogon_sam_logon(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ DOM_CRED *ret_creds,
+ const char *username, const char *password,
+ int logon_type)
+{
+ prs_struct qbuf, rbuf;
+ NET_Q_SAM_LOGON q;
+ NET_R_SAM_LOGON r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ DOM_CRED clnt_creds, dummy_rtn_creds;
+ NET_ID_INFO_CTR ctr;
+ NET_USER_INFO_3 user;
+ int validation_level = 3;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+ ZERO_STRUCT(dummy_rtn_creds);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ gen_next_creds(cli, &clnt_creds);
+
+ q.validation_level = validation_level;
+
+ if (ret_creds == NULL)
+ ret_creds = &dummy_rtn_creds;
+
+ ctr.switch_value = logon_type;
+
+ switch (logon_type) {
+ case INTERACTIVE_LOGON_TYPE: {
+ unsigned char lm_owf_user_pwd[16], nt_owf_user_pwd[16];
+
+ nt_lm_owf_gen(password, nt_owf_user_pwd, lm_owf_user_pwd);
+
+ init_id_info1(&ctr.auth.id1, lp_workgroup(),
+ 0, /* param_ctrl */
+ 0xdead, 0xbeef, /* LUID? */
+ username, cli->clnt_name_slash,
+ (const char *)cli->sess_key, lm_owf_user_pwd,
+ nt_owf_user_pwd);
+
+ break;
+ }
+ case NET_LOGON_TYPE: {
+ uint8 chal[8];
+ unsigned char local_lm_response[24];
+ unsigned char local_nt_response[24];
+
+ generate_random_buffer(chal, 8, False);
+
+ SMBencrypt(password, chal, local_lm_response);
+ SMBNTencrypt(password, chal, local_nt_response);
+
+ init_id_info2(&ctr.auth.id2, lp_workgroup(),
+ 0, /* param_ctrl */
+ 0xdead, 0xbeef, /* LUID? */
+ username, cli->clnt_name_slash, chal,
+ local_lm_response, 24, local_nt_response, 24);
+ break;
+ }
+ default:
+ DEBUG(0, ("switch value %d not supported\n",
+ ctr.switch_value));
+ goto done;
+ }
+
+ init_sam_info(&q.sam_id, cli->srv_name_slash, global_myname(),
+ &clnt_creds, ret_creds, logon_type,
+ &ctr);
+
+ /* Marshall data and send request */
+
+ if (!net_io_q_sam_logon("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, NET_SAMLOGON, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ r.user = &user;
+
+ if (!net_io_r_sam_logon("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return results */
+
+ result = r.status;
+ memcpy(ret_creds, &r.srv_creds, sizeof(*ret_creds));
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+
+/**
+ * Logon domain user with an 'network' SAM logon
+ *
+ * @param info3 Pointer to a NET_USER_INFO_3 already allocated by the caller.
+ **/
+
+NTSTATUS cli_netlogon_sam_network_logon(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ DOM_CRED *ret_creds,
+ const char *username, const char *domain, const char *workstation,
+ const uint8 chal[8],
+ DATA_BLOB lm_response, DATA_BLOB nt_response,
+ NET_USER_INFO_3 *info3)
+
+{
+ prs_struct qbuf, rbuf;
+ NET_Q_SAM_LOGON q;
+ NET_R_SAM_LOGON r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ DOM_CRED clnt_creds, dummy_rtn_creds;
+ NET_ID_INFO_CTR ctr;
+ int validation_level = 3;
+ char *workstation_name_slash;
+ uint8 netlogon_sess_key[16];
+ static uint8 zeros[16];
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+ ZERO_STRUCT(dummy_rtn_creds);
+
+ workstation_name_slash = talloc_asprintf(mem_ctx, "\\\\%s", workstation);
+
+ if (!workstation_name_slash) {
+ DEBUG(0, ("talloc_asprintf failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ gen_next_creds(cli, &clnt_creds);
+
+ q.validation_level = validation_level;
+
+ if (ret_creds == NULL)
+ ret_creds = &dummy_rtn_creds;
+
+ ctr.switch_value = NET_LOGON_TYPE;
+
+ init_id_info2(&ctr.auth.id2, domain,
+ 0, /* param_ctrl */
+ 0xdead, 0xbeef, /* LUID? */
+ username, workstation_name_slash, (const uchar*)chal,
+ lm_response.data, lm_response.length, nt_response.data, nt_response.length);
+
+ init_sam_info(&q.sam_id, cli->srv_name_slash, global_myname(),
+ &clnt_creds, ret_creds, NET_LOGON_TYPE,
+ &ctr);
+
+ /* Marshall data and send request */
+
+ if (!net_io_q_sam_logon("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, NET_SAMLOGON, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ r.user = info3;
+
+ if (!net_io_r_sam_logon("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ ZERO_STRUCT(netlogon_sess_key);
+ memcpy(netlogon_sess_key, cli->sess_key, 8);
+
+ if (memcmp(zeros, info3->user_sess_key, 16) != 0) {
+ SamOEMhash(info3->user_sess_key, netlogon_sess_key, 16);
+ } else {
+ memset(info3->user_sess_key, '\0', 16);
+ }
+
+ if (memcmp(zeros, info3->padding, 16) != 0) {
+ SamOEMhash(info3->padding, netlogon_sess_key, 16);
+ } else {
+ memset(info3->padding, '\0', 16);
+ }
+
+ /* Return results */
+
+ result = r.status;
+ memcpy(ret_creds, &r.srv_creds, sizeof(*ret_creds));
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/***************************************************************************
+LSA Server Password Set.
+****************************************************************************/
+
+NTSTATUS cli_net_srv_pwset(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const char *machine_name, uint8 hashed_mach_pwd[16])
+{
+ prs_struct rbuf;
+ prs_struct qbuf;
+ DOM_CRED new_clnt_cred;
+ NET_Q_SRV_PWSET q_s;
+ uint16 sec_chan_type = 2;
+ NTSTATUS nt_status;
+
+ gen_next_creds( cli, &new_clnt_cred);
+
+ prs_init(&qbuf , 1024, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ DEBUG(4,("cli_net_srv_pwset: srv:%s acct:%s sc: %d mc: %s clnt %s %x\n",
+ cli->srv_name_slash, cli->mach_acct, sec_chan_type, machine_name,
+ credstr(new_clnt_cred.challenge.data), new_clnt_cred.timestamp.time));
+
+ /* store the parameters */
+ init_q_srv_pwset(&q_s, cli->srv_name_slash, (const char *)cli->sess_key,
+ cli->mach_acct, sec_chan_type, machine_name,
+ &new_clnt_cred, hashed_mach_pwd);
+
+ /* turn parameters into data stream */
+ if(!net_io_q_srv_pwset("", &q_s, &qbuf, 0)) {
+ DEBUG(0,("cli_net_srv_pwset: Error : failed to marshall NET_Q_SRV_PWSET struct.\n"));
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* send the data on \PIPE\ */
+ if (rpc_api_pipe_req(cli, NET_SRVPWSET, &qbuf, &rbuf))
+ {
+ NET_R_SRV_PWSET r_s;
+
+ if (!net_io_r_srv_pwset("", &r_s, &rbuf, 0)) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ nt_status = r_s.status;
+
+ if (!NT_STATUS_IS_OK(r_s.status))
+ {
+ /* report error code */
+ DEBUG(0,("cli_net_srv_pwset: %s\n", nt_errstr(nt_status)));
+ goto done;
+ }
+
+ /* Update the credentials. */
+ if (!clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), &(r_s.srv_cred)))
+ {
+ /*
+ * Server replied with bad credential. Fail.
+ */
+ DEBUG(0,("cli_net_srv_pwset: server %s replied with bad credential (bad machine \
+password ?).\n", cli->desthost ));
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return nt_status;
+}
+
diff --git a/source/rpc_client/cli_pipe.c b/source/rpc_client/cli_pipe.c
new file mode 100644
index 00000000000..df0d37a4631
--- /dev/null
+++ b/source/rpc_client/cli_pipe.c
@@ -0,0 +1,1651 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1998,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ * Copyright (C) Paul Ashton 1998.
+ * Copyright (C) Jeremy Allison 1999.
+ * Copyright (C) Andrew Bartlett 2003.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_CLI
+
+extern struct pipe_id_info pipe_names[];
+
+/* convert pipe auth flags into the RPC auth type and level */
+
+void get_auth_type_level(int pipe_auth_flags, int *auth_type, int *auth_level)
+{
+ *auth_type = 0;
+ *auth_level = 0;
+ if (pipe_auth_flags & AUTH_PIPE_SEAL) {
+ *auth_level = RPC_PIPE_AUTH_SEAL_LEVEL;
+ } else if (pipe_auth_flags & AUTH_PIPE_SIGN) {
+ *auth_level = RPC_PIPE_AUTH_SIGN_LEVEL;
+ }
+
+ if (pipe_auth_flags & AUTH_PIPE_NETSEC) {
+ *auth_type = NETSEC_AUTH_TYPE;
+ } else if (pipe_auth_flags & AUTH_PIPE_NTLMSSP) {
+ *auth_type = NTLMSSP_AUTH_TYPE;
+ }
+}
+
+/********************************************************************
+ Rpc pipe call id.
+ ********************************************************************/
+
+static uint32 get_rpc_call_id(void)
+{
+ static uint32 call_id = 0;
+ return ++call_id;
+}
+
+/*******************************************************************
+ Use SMBreadX to get rest of one fragment's worth of rpc data.
+ ********************************************************************/
+
+static BOOL rpc_read(struct cli_state *cli, prs_struct *rdata, uint32 data_to_read, uint32 *rdata_offset)
+{
+ size_t size = (size_t)cli->max_recv_frag;
+ int stream_offset = 0;
+ int num_read;
+ char *pdata;
+ int extra_data_size = ((int)*rdata_offset) + ((int)data_to_read) - (int)prs_data_size(rdata);
+
+ DEBUG(5,("rpc_read: data_to_read: %u rdata offset: %u extra_data_size: %d\n",
+ (int)data_to_read, (unsigned int)*rdata_offset, extra_data_size));
+
+ /*
+ * Grow the buffer if needed to accommodate the data to be read.
+ */
+
+ if (extra_data_size > 0) {
+ if(!prs_force_grow(rdata, (uint32)extra_data_size)) {
+ DEBUG(0,("rpc_read: Failed to grow parse struct by %d bytes.\n", extra_data_size ));
+ return False;
+ }
+ DEBUG(5,("rpc_read: grew buffer by %d bytes to %u\n", extra_data_size, prs_data_size(rdata) ));
+ }
+
+ pdata = prs_data_p(rdata) + *rdata_offset;
+
+ do /* read data using SMBreadX */
+ {
+ uint32 ecode;
+ uint8 eclass;
+
+ if (size > (size_t)data_to_read)
+ size = (size_t)data_to_read;
+
+ num_read = (int)cli_read(cli, cli->nt_pipe_fnum, pdata, (off_t)stream_offset, size);
+
+ DEBUG(5,("rpc_read: num_read = %d, read offset: %d, to read: %d\n",
+ num_read, stream_offset, data_to_read));
+
+ if (cli_is_dos_error(cli)) {
+ cli_dos_error(cli, &eclass, &ecode);
+ if (eclass != ERRDOS && ecode != ERRmoredata) {
+ DEBUG(0,("rpc_read: Error %d/%u in cli_read\n",
+ eclass, (unsigned int)ecode));
+ return False;
+ }
+ }
+
+ data_to_read -= num_read;
+ stream_offset += num_read;
+ pdata += num_read;
+
+ } while (num_read > 0 && data_to_read > 0);
+ /* && err == (0x80000000 | STATUS_BUFFER_OVERFLOW)); */
+
+ /*
+ * Update the current offset into rdata by the amount read.
+ */
+ *rdata_offset += stream_offset;
+
+ return True;
+}
+
+/****************************************************************************
+ Checks the header. This will set the endian bit in the rdata prs_struct. JRA.
+ ****************************************************************************/
+
+static BOOL rpc_check_hdr(prs_struct *rdata, RPC_HDR *rhdr,
+ BOOL *first, BOOL *last, uint32 *len)
+{
+ DEBUG(5,("rpc_check_hdr: rdata->data_size = %u\n", (uint32)prs_data_size(rdata) ));
+
+ /* Next call sets endian bit. */
+
+ if(!smb_io_rpc_hdr("rpc_hdr ", rhdr, rdata, 0)) {
+ DEBUG(0,("rpc_check_hdr: Failed to unmarshall RPC_HDR.\n"));
+ return False;
+ }
+
+ if (prs_offset(rdata) != RPC_HEADER_LEN) {
+ DEBUG(0,("rpc_check_hdr: offset was %x, should be %x.\n", prs_offset(rdata), RPC_HEADER_LEN));
+ return False;
+ }
+
+ (*first) = ((rhdr->flags & RPC_FLG_FIRST) != 0);
+ (*last) = ((rhdr->flags & RPC_FLG_LAST ) != 0);
+ (*len) = (uint32)rhdr->frag_len - prs_data_size(rdata);
+
+ return (rhdr->pkt_type != RPC_FAULT);
+}
+
+/****************************************************************************
+ Verify data on an rpc pipe.
+ The VERIFY & SEAL code is only executed on packets that look like this :
+
+ Request/Response PDU's look like the following...
+
+ |<------------------PDU len----------------------------------------------->|
+ |<-HDR_LEN-->|<--REQ LEN------>|.............|<-AUTH_HDRLEN->|<-AUTH_LEN-->|
+
+ +------------+-----------------+-------------+---------------+-------------+
+ | RPC HEADER | REQ/RESP HEADER | DATA ...... | AUTH_HDR | AUTH DATA |
+ +------------+-----------------+-------------+---------------+-------------+
+
+ Never on bind requests/responses.
+ ****************************************************************************/
+
+static BOOL rpc_auth_pipe(struct cli_state *cli, prs_struct *rdata,
+ uint32 fragment_start, int len, int auth_len, uint8 pkt_type,
+ int *pauth_padding_len)
+{
+
+ /*
+ * The following is that length of the data we must sign or seal.
+ * This doesn't include the RPC headers or the auth_len or the RPC_HDR_AUTH_LEN
+ * preceeding the auth_data.
+ */
+
+ int data_len = len - RPC_HEADER_LEN - RPC_HDR_RESP_LEN - RPC_HDR_AUTH_LEN - auth_len;
+
+ /*
+ * The start of the data to sign/seal is just after the RPC headers.
+ */
+ char *reply_data = prs_data_p(rdata) + fragment_start + RPC_HEADER_LEN + RPC_HDR_REQ_LEN;
+
+ RPC_HDR_AUTH rhdr_auth;
+
+ char *dp = prs_data_p(rdata) + fragment_start + len -
+ RPC_HDR_AUTH_LEN - auth_len;
+ prs_struct auth_verf;
+
+ *pauth_padding_len = 0;
+
+ if (auth_len == 0) {
+ if (cli->pipe_auth_flags == 0) {
+ /* move along, nothing to see here */
+ return True;
+ }
+
+ DEBUG(2, ("No authenticaton header recienved on reply, but this pipe is authenticated\n"));
+ return False;
+ }
+
+ DEBUG(5,("rpc_auth_pipe: pkt_type: %d len: %d auth_len: %d NTLMSSP %s schannel %s sign %s seal %s \n",
+ pkt_type, len, auth_len,
+ BOOLSTR(cli->pipe_auth_flags & AUTH_PIPE_NTLMSSP),
+ BOOLSTR(cli->pipe_auth_flags & AUTH_PIPE_NETSEC),
+ BOOLSTR(cli->pipe_auth_flags & AUTH_PIPE_SIGN),
+ BOOLSTR(cli->pipe_auth_flags & AUTH_PIPE_SEAL)));
+
+ if (dp - prs_data_p(rdata) > prs_data_size(rdata)) {
+ DEBUG(0,("rpc_auth_pipe: schannel auth data > data size !\n"));
+ return False;
+ }
+
+ DEBUG(10,("rpc_auth_pipe: packet:\n"));
+ dump_data(100, dp, auth_len);
+
+ prs_init(&auth_verf, 0, cli->mem_ctx, UNMARSHALL);
+
+ /* The endinness must be preserved. JRA. */
+ prs_set_endian_data( &auth_verf, rdata->bigendian_data);
+
+ /* Point this new parse struct at the auth section of the main
+ parse struct - rather than copying it. Avoids needing to
+ free it on every error
+ */
+ prs_give_memory(&auth_verf, dp, RPC_HDR_AUTH_LEN + auth_len, False /* not dynamic */);
+ prs_set_offset(&auth_verf, 0);
+
+ {
+ int auth_type;
+ int auth_level;
+ if (!smb_io_rpc_hdr_auth("auth_hdr", &rhdr_auth, &auth_verf, 0)) {
+ DEBUG(0, ("rpc_auth_pipe: Could not parse auth header\n"));
+ return False;
+ }
+
+ /* Let the caller know how much padding at the end of the data */
+ *pauth_padding_len = rhdr_auth.padding;
+
+ /* Check it's the type of reply we were expecting to decode */
+
+ get_auth_type_level(cli->pipe_auth_flags, &auth_type, &auth_level);
+ if (rhdr_auth.auth_type != auth_type) {
+ DEBUG(0, ("BAD auth type %d (should be %d)\n",
+ rhdr_auth.auth_type, auth_type));
+ return False;
+ }
+
+ if (rhdr_auth.auth_level != auth_level) {
+ DEBUG(0, ("BAD auth level %d (should be %d)\n",
+ rhdr_auth.auth_level, auth_level));
+ return False;
+ }
+ }
+
+ if (pkt_type == RPC_BINDACK) {
+ if (cli->pipe_auth_flags & AUTH_PIPE_NTLMSSP) {
+ /* copy the next auth_len bytes into a buffer for
+ later use */
+
+ DATA_BLOB ntlmssp_verf = data_blob(NULL, auth_len);
+ BOOL store_ok;
+
+ /* save the reply away, for use a little later */
+ prs_copy_data_out((char *)ntlmssp_verf.data, &auth_verf, auth_len);
+
+ store_ok = (NT_STATUS_IS_OK(ntlmssp_store_response(cli->ntlmssp_pipe_state,
+ ntlmssp_verf)));
+
+ data_blob_free(&ntlmssp_verf);
+ return store_ok;
+ }
+ else if (cli->pipe_auth_flags & AUTH_PIPE_NETSEC) {
+ /* nothing to do here - we don't seem to be able to
+ validate the bindack based on VL's comments */
+ return True;
+ }
+ }
+
+ if (cli->pipe_auth_flags & AUTH_PIPE_NTLMSSP) {
+ NTSTATUS nt_status;
+ DATA_BLOB sig;
+ if ((cli->pipe_auth_flags & AUTH_PIPE_SIGN) ||
+ (cli->pipe_auth_flags & AUTH_PIPE_SEAL)) {
+ if (auth_len != RPC_AUTH_NTLMSSP_CHK_LEN) {
+ DEBUG(0,("rpc_auth_pipe: wrong ntlmssp auth len %d\n", auth_len));
+ return False;
+ }
+ sig = data_blob(NULL, auth_len);
+ prs_copy_data_out((char *)sig.data, &auth_verf, auth_len);
+ }
+
+ /*
+ * Unseal any sealed data in the PDU, not including the
+ * 8 byte auth_header or the auth_data.
+ */
+
+ /*
+ * Now unseal and check the auth verifier in the auth_data at
+ * the end of the packet.
+ */
+
+ if (cli->pipe_auth_flags & AUTH_PIPE_SEAL) {
+ if (data_len < 0) {
+ DEBUG(1, ("Can't unseal - data_len < 0!!\n"));
+ return False;
+ }
+ nt_status = ntlmssp_unseal_packet(cli->ntlmssp_pipe_state,
+ (unsigned char *)reply_data, data_len,
+ &sig);
+ }
+ else if (cli->pipe_auth_flags & AUTH_PIPE_SIGN) {
+ nt_status = ntlmssp_check_packet(cli->ntlmssp_pipe_state,
+ (const unsigned char *)reply_data, data_len,
+ &sig);
+ }
+
+ data_blob_free(&sig);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("rpc_auth_pipe: could not validate "
+ "incoming NTLMSSP packet!\n"));
+ return False;
+ }
+ }
+
+ if (cli->pipe_auth_flags & AUTH_PIPE_NETSEC) {
+ RPC_AUTH_NETSEC_CHK chk;
+
+ if (auth_len != RPC_AUTH_NETSEC_CHK_LEN) {
+ DEBUG(0,("rpc_auth_pipe: wrong schannel auth len %d\n", auth_len));
+ return False;
+ }
+
+ if (!smb_io_rpc_auth_netsec_chk("schannel_auth_sign",
+ &chk, &auth_verf, 0)) {
+ DEBUG(0, ("rpc_auth_pipe: schannel unmarshalling "
+ "RPC_AUTH_NETSECK_CHK failed\n"));
+ return False;
+ }
+
+ if (!netsec_decode(&cli->auth_info,
+ cli->pipe_auth_flags,
+ SENDER_IS_ACCEPTOR,
+ &chk, reply_data, data_len)) {
+ DEBUG(0, ("rpc_auth_pipe: Could not decode schannel\n"));
+ return False;
+ }
+
+ cli->auth_info.seq_num++;
+
+ }
+ return True;
+}
+
+
+/****************************************************************************
+ Send data on an rpc pipe via trans, which *must* be the last fragment.
+ receive response data from an rpc pipe, which may be large...
+
+ Read the first fragment: unfortunately have to use SMBtrans for the first
+ bit, then SMBreadX for subsequent bits.
+
+ If first fragment received also wasn't the last fragment, continue
+ getting fragments until we _do_ receive the last fragment.
+
+ Request/Response PDU's look like the following...
+
+ |<------------------PDU len----------------------------------------------->|
+ |<-HDR_LEN-->|<--REQ LEN------>|.............|<-AUTH_HDRLEN->|<-AUTH_LEN-->|
+
+ +------------+-----------------+-------------+---------------+-------------+
+ | RPC HEADER | REQ/RESP HEADER | DATA ...... | AUTH_HDR | AUTH DATA |
+ +------------+-----------------+-------------+---------------+-------------+
+
+ Where the presence of the AUTH_HDR and AUTH are dependent on the
+ signing & sealing being negotiated.
+
+ ****************************************************************************/
+
+static BOOL rpc_api_pipe(struct cli_state *cli, prs_struct *data, prs_struct *rdata,
+ uint8 expected_pkt_type)
+{
+ uint32 len;
+ char *rparam = NULL;
+ uint32 rparam_len = 0;
+ uint16 setup[2];
+ BOOL first = True;
+ BOOL last = True;
+ RPC_HDR rhdr;
+ char *pdata = data ? prs_data_p(data) : NULL;
+ uint32 data_len = data ? prs_offset(data) : 0;
+ char *prdata = NULL;
+ uint32 rdata_len = 0;
+ uint32 current_offset = 0;
+ uint32 fragment_start = 0;
+ uint32 max_data = cli->max_xmit_frag ? cli->max_xmit_frag : 1024;
+ int auth_padding_len = 0;
+
+ /* Create setup parameters - must be in native byte order. */
+
+ setup[0] = TRANSACT_DCERPCCMD;
+ setup[1] = cli->nt_pipe_fnum; /* Pipe file handle. */
+
+ DEBUG(5,("rpc_api_pipe: fnum:%x\n", (int)cli->nt_pipe_fnum));
+
+ /* Send the RPC request and receive a response. For short RPC
+ calls (about 1024 bytes or so) the RPC request and response
+ appears in a SMBtrans request and response. Larger RPC
+ responses are received further on. */
+
+ if (!cli_api_pipe(cli, "\\PIPE\\",
+ setup, 2, 0, /* Setup, length, max */
+ NULL, 0, 0, /* Params, length, max */
+ pdata, data_len, max_data, /* data, length, max */
+ &rparam, &rparam_len, /* return params, len */
+ &prdata, &rdata_len)) /* return data, len */
+ {
+ DEBUG(0, ("cli_pipe: return critical error. Error was %s\n", cli_errstr(cli)));
+ return False;
+ }
+
+ /* Throw away returned params - we know we won't use them. */
+
+ SAFE_FREE(rparam);
+
+ if (prdata == NULL) {
+ DEBUG(0,("rpc_api_pipe: pipe %x failed to return data.\n",
+ (int)cli->nt_pipe_fnum));
+ return False;
+ }
+
+ /*
+ * Give this memory as dynamically allocated to the return parse
+ * struct.
+ */
+
+ prs_give_memory(rdata, prdata, rdata_len, True);
+ current_offset = rdata_len;
+
+ /* This next call sets the endian bit correctly in rdata. */
+
+ if (!rpc_check_hdr(rdata, &rhdr, &first, &last, &len)) {
+ prs_mem_free(rdata);
+ return False;
+ }
+
+ if (rhdr.pkt_type == RPC_BINDACK) {
+ if (!last && !first) {
+ DEBUG(5,("rpc_api_pipe: bug in server (AS/U?), setting fragment first/last ON.\n"));
+ first = True;
+ last = True;
+ }
+ }
+
+ if (rhdr.pkt_type == RPC_BINDNACK) {
+ DEBUG(3, ("Bind NACK received on pipe %x!\n", (int)cli->nt_pipe_fnum));
+ prs_mem_free(rdata);
+ return False;
+ }
+
+ if (rhdr.pkt_type == RPC_RESPONSE) {
+ RPC_HDR_RESP rhdr_resp;
+ if(!smb_io_rpc_hdr_resp("rpc_hdr_resp", &rhdr_resp, rdata, 0)) {
+ DEBUG(5,("rpc_api_pipe: failed to unmarshal RPC_HDR_RESP.\n"));
+ prs_mem_free(rdata);
+ return False;
+ }
+ }
+
+ if (rhdr.pkt_type != expected_pkt_type) {
+ DEBUG(3, ("Connection to pipe %x got an unexpected RPC packet type - %d, not %d\n", (int)cli->nt_pipe_fnum, rhdr.pkt_type, expected_pkt_type));
+ prs_mem_free(rdata);
+ return False;
+ }
+
+ DEBUG(5,("rpc_api_pipe: len left: %u smbtrans read: %u\n",
+ (unsigned int)len, (unsigned int)rdata_len ));
+
+ /* check if data to be sent back was too large for one SMBtrans */
+ /* err status is only informational: the _real_ check is on the
+ length */
+
+ if (len > 0) {
+ /* || err == (0x80000000 | STATUS_BUFFER_OVERFLOW)) */
+
+ /* Read the remaining part of the first response fragment */
+
+ if (!rpc_read(cli, rdata, len, &current_offset)) {
+ prs_mem_free(rdata);
+ return False;
+ }
+ }
+
+ /*
+ * Now we have a complete PDU, check the auth struct if any was sent.
+ */
+
+ if(!rpc_auth_pipe(cli, rdata, fragment_start, rhdr.frag_len,
+ rhdr.auth_len, rhdr.pkt_type, &auth_padding_len)) {
+ prs_mem_free(rdata);
+ return False;
+ }
+
+ if (rhdr.auth_len != 0) {
+ /*
+ * Drop the auth footers from the current offset.
+ * We need this if there are more fragments.
+ * The auth footers consist of the auth_data and the
+ * preceeding 8 byte auth_header.
+ */
+ current_offset -= (auth_padding_len + RPC_HDR_AUTH_LEN + rhdr.auth_len);
+ }
+
+ /*
+ * Only one rpc fragment, and it has been read.
+ */
+
+ if (first && last) {
+ DEBUG(6,("rpc_api_pipe: fragment first and last both set\n"));
+ return True;
+ }
+
+ /*
+ * Read more fragments using SMBreadX until we get one with the
+ * last bit set.
+ */
+
+ while (!last) {
+ RPC_HDR_RESP rhdr_resp;
+ int num_read;
+ char hdr_data[RPC_HEADER_LEN+RPC_HDR_RESP_LEN];
+ prs_struct hps;
+ uint8 eclass;
+ uint32 ecode;
+
+ /*
+ * First read the header of the next PDU.
+ */
+
+ prs_init(&hps, 0, cli->mem_ctx, UNMARSHALL);
+ prs_give_memory(&hps, hdr_data, sizeof(hdr_data), False);
+
+ num_read = cli_read(cli, cli->nt_pipe_fnum, hdr_data, 0, RPC_HEADER_LEN+RPC_HDR_RESP_LEN);
+ if (cli_is_dos_error(cli)) {
+ cli_dos_error(cli, &eclass, &ecode);
+ if (eclass != ERRDOS && ecode != ERRmoredata) {
+ DEBUG(0,("rpc_api_pipe: cli_read error : %d/%d\n", eclass, ecode));
+ return False;
+ }
+ }
+
+ DEBUG(5,("rpc_api_pipe: read header (size:%d)\n", num_read));
+
+ if (num_read != RPC_HEADER_LEN+RPC_HDR_RESP_LEN) {
+ DEBUG(0,("rpc_api_pipe: Error : requested %d bytes, got %d.\n",
+ RPC_HEADER_LEN+RPC_HDR_RESP_LEN, num_read ));
+ return False;
+ }
+
+ /* This call sets the endianness in hps. */
+
+ if (!rpc_check_hdr(&hps, &rhdr, &first, &last, &len))
+ return False;
+
+ /* Ensure the endianness in rdata is set correctly - must be same as hps. */
+
+ if (hps.bigendian_data != rdata->bigendian_data) {
+ DEBUG(0,("rpc_api_pipe: Error : Endianness changed from %s to %s\n",
+ rdata->bigendian_data ? "big" : "little",
+ hps.bigendian_data ? "big" : "little" ));
+ return False;
+ }
+
+ if(!smb_io_rpc_hdr_resp("rpc_hdr_resp", &rhdr_resp, &hps, 0)) {
+ DEBUG(0,("rpc_api_pipe: Error in unmarshalling RPC_HDR_RESP.\n"));
+ return False;
+ }
+
+ if (first) {
+ DEBUG(0,("rpc_api_pipe: secondary PDU rpc header has 'first' set !\n"));
+ return False;
+ }
+
+ /*
+ * Now read the rest of the PDU.
+ */
+
+ if (!rpc_read(cli, rdata, len, &current_offset)) {
+ prs_mem_free(rdata);
+ return False;
+ }
+
+ fragment_start = current_offset - len - RPC_HEADER_LEN - RPC_HDR_RESP_LEN;
+
+ /*
+ * Verify any authentication footer.
+ */
+
+
+ if(!rpc_auth_pipe(cli, rdata, fragment_start, rhdr.frag_len,
+ rhdr.auth_len, rhdr.pkt_type, &auth_padding_len)) {
+ prs_mem_free(rdata);
+ return False;
+ }
+
+ if (rhdr.auth_len != 0 ) {
+
+ /*
+ * Drop the auth footers from the current offset.
+ * The auth footers consist of the auth_data and the
+ * preceeding 8 byte auth_header.
+ * We need this if there are more fragments.
+ */
+ current_offset -= (auth_padding_len + RPC_HDR_AUTH_LEN + rhdr.auth_len);
+ }
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ creates a DCE/RPC bind request
+
+ - initialises the parse structure.
+ - dynamically allocates the header data structure
+ - caller is expected to free the header data structure once used.
+
+ ********************************************************************/
+
+static NTSTATUS create_rpc_bind_req(struct cli_state *cli, prs_struct *rpc_out,
+ uint32 rpc_call_id,
+ RPC_IFACE *abstract, RPC_IFACE *transfer,
+ const char *my_name, const char *domain)
+{
+ RPC_HDR hdr;
+ RPC_HDR_RB hdr_rb;
+ RPC_HDR_AUTH hdr_auth;
+ int auth_len = 0;
+ int auth_type, auth_level;
+ size_t saved_hdr_offset = 0;
+
+ prs_struct auth_info;
+ prs_init(&auth_info, RPC_HDR_AUTH_LEN, /* we will need at least this much */
+ prs_get_mem_context(rpc_out), MARSHALL);
+
+ if (cli->pipe_auth_flags) {
+ get_auth_type_level(cli->pipe_auth_flags, &auth_type, &auth_level);
+
+ /*
+ * Create the auth structs we will marshall.
+ */
+
+ init_rpc_hdr_auth(&hdr_auth, auth_type, auth_level, 0x00, 1);
+
+ /*
+ * Now marshall the data into the temporary parse_struct.
+ */
+
+ if(!smb_io_rpc_hdr_auth("hdr_auth", &hdr_auth, &auth_info, 0)) {
+ DEBUG(0,("create_rpc_bind_req: failed to marshall RPC_HDR_AUTH.\n"));
+ prs_mem_free(&auth_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+ saved_hdr_offset = prs_offset(&auth_info);
+ }
+
+ if (cli->pipe_auth_flags & AUTH_PIPE_NTLMSSP) {
+
+ NTSTATUS nt_status;
+ DATA_BLOB null_blob = data_blob(NULL, 0);
+ DATA_BLOB request;
+
+ DEBUG(5, ("Processing NTLMSSP Negotiate\n"));
+ nt_status = ntlmssp_update(cli->ntlmssp_pipe_state,
+ null_blob,
+ &request);
+
+ if (!NT_STATUS_EQUAL(nt_status,
+ NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ prs_mem_free(&auth_info);
+ return nt_status;
+ }
+
+ /* Auth len in the rpc header doesn't include auth_header. */
+ auth_len = request.length;
+ prs_copy_data_in(&auth_info, (char *)request.data, request.length);
+
+ DEBUG(5, ("NTLMSSP Negotiate:\n"));
+ dump_data(5, (const char *)request.data, request.length);
+
+ data_blob_free(&request);
+
+ } else if (cli->pipe_auth_flags & AUTH_PIPE_NETSEC) {
+ RPC_AUTH_NETSEC_NEG netsec_neg;
+
+ /* Use lp_workgroup() if domain not specified */
+
+ if (!domain || !domain[0]) {
+ DEBUG(10,("create_rpc_bind_req: no domain; assuming my own\n"));
+ domain = lp_workgroup();
+ }
+
+ init_rpc_auth_netsec_neg(&netsec_neg, domain, my_name);
+
+ /*
+ * Now marshall the data into the temporary parse_struct.
+ */
+
+ if(!smb_io_rpc_auth_netsec_neg("netsec_neg",
+ &netsec_neg, &auth_info, 0)) {
+ DEBUG(0,("Failed to marshall RPC_AUTH_NETSEC_NEG.\n"));
+ prs_mem_free(&auth_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Auth len in the rpc header doesn't include auth_header. */
+ auth_len = prs_offset(&auth_info) - saved_hdr_offset;
+ }
+
+ /* Create the request RPC_HDR */
+ init_rpc_hdr(&hdr, RPC_BIND, 0x3, rpc_call_id,
+ RPC_HEADER_LEN + RPC_HDR_RB_LEN + prs_offset(&auth_info),
+ auth_len);
+
+ if(!smb_io_rpc_hdr("hdr" , &hdr, rpc_out, 0)) {
+ DEBUG(0,("create_rpc_bind_req: failed to marshall RPC_HDR.\n"));
+ prs_mem_free(&auth_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* create the bind request RPC_HDR_RB */
+ init_rpc_hdr_rb(&hdr_rb, MAX_PDU_FRAG_LEN, MAX_PDU_FRAG_LEN, 0x0,
+ 0x1, 0x0, 0x1, abstract, transfer);
+
+ /* Marshall the bind request data */
+ if(!smb_io_rpc_hdr_rb("", &hdr_rb, rpc_out, 0)) {
+ DEBUG(0,("create_rpc_bind_req: failed to marshall RPC_HDR_RB.\n"));
+ prs_mem_free(&auth_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * Grow the outgoing buffer to store any auth info.
+ */
+
+ if(auth_len != 0) {
+ if(!prs_append_prs_data( rpc_out, &auth_info)) {
+ DEBUG(0,("create_rpc_bind_req: failed to grow parse struct to add auth.\n"));
+ prs_mem_free(&auth_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ prs_mem_free(&auth_info);
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Creates a DCE/RPC bind authentication response.
+ This is the packet that is sent back to the server once we
+ have received a BIND-ACK, to finish the third leg of
+ the authentication handshake.
+ ********************************************************************/
+
+static NTSTATUS create_rpc_bind_resp(struct cli_state *cli,
+ uint32 rpc_call_id,
+ prs_struct *rpc_out)
+{
+ NTSTATUS nt_status;
+ RPC_HDR hdr;
+ RPC_HDR_AUTHA hdr_autha;
+ DATA_BLOB ntlmssp_null_response = data_blob(NULL, 0);
+ DATA_BLOB ntlmssp_reply;
+ int auth_type, auth_level;
+
+ /* The response is picked up from the internal cache,
+ where it was placed by the rpc_auth_pipe() code */
+ nt_status = ntlmssp_update(cli->ntlmssp_pipe_state,
+ ntlmssp_null_response,
+ &ntlmssp_reply);
+
+ if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ return nt_status;
+ }
+
+ /* Create the request RPC_HDR */
+ init_rpc_hdr(&hdr, RPC_BINDRESP, 0x0, rpc_call_id,
+ RPC_HEADER_LEN + RPC_HDR_AUTHA_LEN + ntlmssp_reply.length,
+ ntlmssp_reply.length );
+
+ /* Marshall it. */
+ if(!smb_io_rpc_hdr("hdr", &hdr, rpc_out, 0)) {
+ DEBUG(0,("create_rpc_bind_resp: failed to marshall RPC_HDR.\n"));
+ data_blob_free(&ntlmssp_reply);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ get_auth_type_level(cli->pipe_auth_flags, &auth_type, &auth_level);
+
+ /* Create the request RPC_HDR_AUTHA */
+ init_rpc_hdr_autha(&hdr_autha, MAX_PDU_FRAG_LEN, MAX_PDU_FRAG_LEN,
+ auth_type, auth_level, 0x00);
+
+ if(!smb_io_rpc_hdr_autha("hdr_autha", &hdr_autha, rpc_out, 0)) {
+ DEBUG(0,("create_rpc_bind_resp: failed to marshall RPC_HDR_AUTHA.\n"));
+ data_blob_free(&ntlmssp_reply);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * Append the auth data to the outgoing buffer.
+ */
+
+ if(!prs_copy_data_in(rpc_out, (char *)ntlmssp_reply.data, ntlmssp_reply.length)) {
+ DEBUG(0,("create_rpc_bind_req: failed to grow parse struct to add auth.\n"));
+ data_blob_free(&ntlmssp_reply);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ data_blob_free(&ntlmssp_reply);
+ return NT_STATUS_OK;
+}
+
+
+/*******************************************************************
+ Creates a DCE/RPC request.
+ ********************************************************************/
+
+static uint32 create_rpc_request(prs_struct *rpc_out, uint8 op_num, int data_len, int auth_len, uint8 flags, uint32 oldid, uint32 data_left)
+{
+ uint32 alloc_hint;
+ RPC_HDR hdr;
+ RPC_HDR_REQ hdr_req;
+ uint32 callid = oldid ? oldid : get_rpc_call_id();
+
+ DEBUG(5,("create_rpc_request: opnum: 0x%x data_len: 0x%x\n", op_num, data_len));
+
+ /* create the rpc header RPC_HDR */
+ init_rpc_hdr(&hdr, RPC_REQUEST, flags,
+ callid, data_len, auth_len);
+
+ /*
+ * The alloc hint should be the amount of data, not including
+ * RPC headers & footers.
+ */
+
+ if (auth_len != 0)
+ alloc_hint = data_len - RPC_HEADER_LEN - RPC_HDR_AUTH_LEN - auth_len;
+ else
+ alloc_hint = data_len - RPC_HEADER_LEN;
+
+ DEBUG(10,("create_rpc_request: data_len: %x auth_len: %x alloc_hint: %x\n",
+ data_len, auth_len, alloc_hint));
+
+ /* Create the rpc request RPC_HDR_REQ */
+ init_rpc_hdr_req(&hdr_req, alloc_hint, op_num);
+
+ /* stream-time... */
+ if(!smb_io_rpc_hdr("hdr ", &hdr, rpc_out, 0))
+ return 0;
+
+ if(!smb_io_rpc_hdr_req("hdr_req", &hdr_req, rpc_out, 0))
+ return 0;
+
+ if (prs_offset(rpc_out) != RPC_HEADER_LEN + RPC_HDR_REQ_LEN)
+ return 0;
+
+ return callid;
+}
+
+/*******************************************************************
+ Puts an auth header into an rpc request.
+ ********************************************************************/
+
+static BOOL create_auth_hdr(prs_struct *outgoing_packet,
+ int auth_type,
+ int auth_level, int padding)
+{
+ RPC_HDR_AUTH hdr_auth;
+
+ init_rpc_hdr_auth(&hdr_auth, auth_type, auth_level,
+ padding, 1);
+ if(!smb_io_rpc_hdr_auth("hdr_auth", &hdr_auth,
+ outgoing_packet, 0)) {
+ DEBUG(0,("create_auth_hdr:Failed to marshal RPC_HDR_AUTH.\n"));
+ return False;
+ }
+ return True;
+}
+
+/**
+ * Send a request on an RPC pipe and get a response.
+ *
+ * @param data NDR contents of the request to be sent.
+ * @param rdata Unparsed NDR response data.
+**/
+
+BOOL rpc_api_pipe_req(struct cli_state *cli, uint8 op_num,
+ prs_struct *data, prs_struct *rdata)
+{
+ uint32 auth_len, real_auth_len, auth_hdr_len, max_data, data_left, data_sent;
+ NTSTATUS nt_status;
+ BOOL ret = False;
+ uint32 callid = 0;
+ fstring dump_name;
+
+ auth_len = 0;
+ real_auth_len = 0;
+ auth_hdr_len = 0;
+
+ if (cli->pipe_auth_flags & AUTH_PIPE_SIGN) {
+ if (cli->pipe_auth_flags & AUTH_PIPE_NTLMSSP) {
+ auth_len = RPC_AUTH_NTLMSSP_CHK_LEN;
+ }
+ if (cli->pipe_auth_flags & AUTH_PIPE_NETSEC) {
+ auth_len = RPC_AUTH_NETSEC_CHK_LEN;
+ }
+ auth_hdr_len = RPC_HDR_AUTH_LEN;
+ }
+
+ /*
+ * calc how much actual data we can send in a PDU fragment
+ */
+ max_data = cli->max_xmit_frag - RPC_HEADER_LEN - RPC_HDR_REQ_LEN -
+ auth_hdr_len - auth_len - 8;
+
+ for (data_left = prs_offset(data), data_sent = 0; data_left > 0;) {
+ prs_struct outgoing_packet;
+ prs_struct sec_blob;
+ uint32 data_len, send_size;
+ uint8 flags = 0;
+ uint32 auth_padding = 0;
+ DATA_BLOB sign_blob;
+
+ /*
+ * how much will we send this time
+ */
+ send_size = MIN(data_left, max_data);
+
+ if (!prs_init(&sec_blob, send_size, /* will need at least this much */
+ cli->mem_ctx, MARSHALL)) {
+ DEBUG(0,("Could not malloc %u bytes",
+ send_size+auth_padding));
+ return False;
+ }
+
+ if(!prs_append_some_prs_data(&sec_blob, data,
+ data_sent, send_size)) {
+ DEBUG(0,("Failed to append data to netsec blob\n"));
+ prs_mem_free(&sec_blob);
+ return False;
+ }
+
+ /*
+ * NT expects the data that is sealed to be 8-byte
+ * aligned. The padding must be encrypted as well and
+ * taken into account when generating the
+ * authentication verifier. The amount of padding must
+ * be stored in the auth header.
+ */
+
+ if (cli->pipe_auth_flags) {
+ size_t data_and_padding_size;
+ int auth_type;
+ int auth_level;
+ prs_align_uint64(&sec_blob);
+
+ get_auth_type_level(cli->pipe_auth_flags, &auth_type, &auth_level);
+
+ data_and_padding_size = prs_offset(&sec_blob);
+ auth_padding = data_and_padding_size - send_size;
+
+ /* insert the auth header */
+
+ if(!create_auth_hdr(&sec_blob, auth_type, auth_level, auth_padding)) {
+ prs_mem_free(&sec_blob);
+ return False;
+ }
+
+ /* create an NTLMSSP signature */
+ if (cli->pipe_auth_flags & AUTH_PIPE_NTLMSSP) {
+ /*
+ * Seal the outgoing data if requested.
+ */
+ if (cli->pipe_auth_flags & AUTH_PIPE_SEAL) {
+
+ nt_status = ntlmssp_seal_packet(cli->ntlmssp_pipe_state,
+ (unsigned char*)prs_data_p(&sec_blob),
+ data_and_padding_size,
+ &sign_blob);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ prs_mem_free(&sec_blob);
+ return False;
+ }
+ }
+ else if (cli->pipe_auth_flags & AUTH_PIPE_SIGN) {
+
+ nt_status = ntlmssp_sign_packet(cli->ntlmssp_pipe_state,
+ (unsigned char*)prs_data_p(&sec_blob),
+ data_and_padding_size, &sign_blob);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ prs_mem_free(&sec_blob);
+ return False;
+ }
+ }
+
+
+ /* write auth footer onto the packet */
+ real_auth_len = sign_blob.length;
+
+ prs_copy_data_in(&sec_blob, (char *)sign_blob.data, sign_blob.length);
+ data_blob_free(&sign_blob);
+
+ }
+ else if (cli->pipe_auth_flags & AUTH_PIPE_NETSEC) {
+ size_t parse_offset_marker;
+ RPC_AUTH_NETSEC_CHK verf;
+ DEBUG(10,("SCHANNEL seq_num=%d\n", cli->auth_info.seq_num));
+
+ netsec_encode(&cli->auth_info,
+ cli->pipe_auth_flags,
+ SENDER_IS_INITIATOR,
+ &verf,
+ prs_data_p(&sec_blob),
+ data_and_padding_size);
+
+ cli->auth_info.seq_num++;
+
+ /* write auth footer onto the packet */
+
+ parse_offset_marker = prs_offset(&sec_blob);
+ if (!smb_io_rpc_auth_netsec_chk("", &verf,
+ &sec_blob, 0)) {
+ prs_mem_free(&sec_blob);
+ return False;
+ }
+ real_auth_len = prs_offset(&sec_blob) - parse_offset_marker;
+ }
+ }
+
+ data_len = RPC_HEADER_LEN + RPC_HDR_REQ_LEN + prs_offset(&sec_blob);
+
+ /*
+ * Malloc parse struct to hold it (and enough for alignments).
+ */
+ if(!prs_init(&outgoing_packet, data_len + 8,
+ cli->mem_ctx, MARSHALL)) {
+ DEBUG(0,("rpc_api_pipe_req: Failed to malloc %u bytes.\n", (unsigned int)data_len ));
+ return False;
+ }
+
+ if (data_left == prs_offset(data))
+ flags |= RPC_FLG_FIRST;
+
+ if (data_left <= max_data)
+ flags |= RPC_FLG_LAST;
+ /*
+ * Write out the RPC header and the request header.
+ */
+ if(!(callid = create_rpc_request(&outgoing_packet, op_num,
+ data_len, real_auth_len, flags,
+ callid, data_left))) {
+ DEBUG(0,("rpc_api_pipe_req: Failed to create RPC request.\n"));
+ prs_mem_free(&outgoing_packet);
+ prs_mem_free(&sec_blob);
+ return False;
+ }
+
+ prs_append_prs_data(&outgoing_packet, &sec_blob);
+ prs_mem_free(&sec_blob);
+
+ DEBUG(100,("data_len: %x data_calc_len: %x\n", data_len,
+ prs_offset(&outgoing_packet)));
+
+ if (flags & RPC_FLG_LAST)
+ ret = rpc_api_pipe(cli, &outgoing_packet,
+ rdata, RPC_RESPONSE);
+ else {
+ cli_write(cli, cli->nt_pipe_fnum, 0x0008,
+ prs_data_p(&outgoing_packet),
+ data_sent, data_len);
+ }
+ prs_mem_free(&outgoing_packet);
+ data_sent += send_size;
+ data_left -= send_size;
+ }
+ /* Also capture received data */
+ slprintf(dump_name, sizeof(dump_name) - 1, "reply_%s",
+ cli_pipe_get_name(cli));
+ prs_dump(dump_name, op_num, rdata);
+
+ return ret;
+}
+
+/****************************************************************************
+ Set the handle state.
+****************************************************************************/
+
+static BOOL rpc_pipe_set_hnd_state(struct cli_state *cli, const char *pipe_name, uint16 device_state)
+{
+ BOOL state_set = False;
+ char param[2];
+ uint16 setup[2]; /* only need 2 uint16 setup parameters */
+ char *rparam = NULL;
+ char *rdata = NULL;
+ uint32 rparam_len, rdata_len;
+
+ if (pipe_name == NULL)
+ return False;
+
+ DEBUG(5,("Set Handle state Pipe[%x]: %s - device state:%x\n",
+ cli->nt_pipe_fnum, pipe_name, device_state));
+
+ /* create parameters: device state */
+ SSVAL(param, 0, device_state);
+
+ /* create setup parameters. */
+ setup[0] = 0x0001;
+ setup[1] = cli->nt_pipe_fnum; /* pipe file handle. got this from an SMBOpenX. */
+
+ /* send the data on \PIPE\ */
+ if (cli_api_pipe(cli, "\\PIPE\\",
+ setup, 2, 0, /* setup, length, max */
+ param, 2, 0, /* param, length, max */
+ NULL, 0, 1024, /* data, length, max */
+ &rparam, &rparam_len, /* return param, length */
+ &rdata, &rdata_len)) /* return data, length */
+ {
+ DEBUG(5, ("Set Handle state: return OK\n"));
+ state_set = True;
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return state_set;
+}
+
+/****************************************************************************
+ check the rpc bind acknowledge response
+****************************************************************************/
+
+int get_pipe_index( const char *pipe_name )
+{
+ int pipe_idx = 0;
+
+ while (pipe_names[pipe_idx].client_pipe != NULL) {
+ if (strequal(pipe_name, pipe_names[pipe_idx].client_pipe ))
+ return pipe_idx;
+ pipe_idx++;
+ };
+
+ return -1;
+}
+
+
+/****************************************************************************
+ check the rpc bind acknowledge response
+****************************************************************************/
+
+const char* get_pipe_name_from_index( const int pipe_index )
+{
+
+ if ( (pipe_index < 0) || (pipe_index >= PI_MAX_PIPES) )
+ return NULL;
+
+ return pipe_names[pipe_index].client_pipe;
+}
+
+/****************************************************************************
+ Check to see if this pipe index points to one of
+ the pipes only supported by Win2k
+ ****************************************************************************/
+
+BOOL is_win2k_pipe( const int pipe_idx )
+{
+ switch ( pipe_idx )
+ {
+ case PI_LSARPC_DS:
+ return True;
+ }
+
+ return False;
+}
+
+/****************************************************************************
+ check the rpc bind acknowledge response
+****************************************************************************/
+
+static BOOL valid_pipe_name(const int pipe_idx, RPC_IFACE *abstract, RPC_IFACE *transfer)
+{
+ if ( pipe_idx >= PI_MAX_PIPES ) {
+ DEBUG(0,("valid_pipe_name: Programmer error! Invalid pipe index [%d]\n",
+ pipe_idx));
+ return False;
+ }
+
+ DEBUG(5,("Bind Abstract Syntax: "));
+ dump_data(5, (char*)&(pipe_names[pipe_idx].abstr_syntax),
+ sizeof(pipe_names[pipe_idx].abstr_syntax));
+ DEBUG(5,("Bind Transfer Syntax: "));
+ dump_data(5, (char*)&(pipe_names[pipe_idx].trans_syntax),
+ sizeof(pipe_names[pipe_idx].trans_syntax));
+
+ /* copy the required syntaxes out so we can do the right bind */
+
+ *transfer = pipe_names[pipe_idx].trans_syntax;
+ *abstract = pipe_names[pipe_idx].abstr_syntax;
+
+ return True;
+}
+
+/****************************************************************************
+ check the rpc bind acknowledge response
+****************************************************************************/
+
+static BOOL check_bind_response(RPC_HDR_BA *hdr_ba, const int pipe_idx, RPC_IFACE *transfer)
+{
+# if 0 /* JERRY -- apparently ASU forgets to fill in the server pipe name sometimes */
+ if ( hdr_ba->addr.len <= 0)
+ return False;
+
+ if ( !strequal(hdr_ba->addr.str, pipe_names[pipe_idx].client_pipe) &&
+ !strequal(hdr_ba->addr.str, pipe_names[pipe_idx].server_pipe) )
+ {
+ DEBUG(4,("bind_rpc_pipe: pipe_name %s != expected pipe %s. oh well!\n",
+ pipe_names[i].server_pipe ,hdr_ba->addr.str));
+ return False;
+ }
+
+ DEBUG(5,("bind_rpc_pipe: server pipe_name found: %s\n", pipe_names[i].server_pipe ));
+
+ if (pipe_names[pipe_idx].server_pipe == NULL) {
+ DEBUG(2,("bind_rpc_pipe: pipe name %s unsupported\n", hdr_ba->addr.str));
+ return False;
+ }
+#endif /* JERRY */
+
+ /* check the transfer syntax */
+ if ((hdr_ba->transfer.version != transfer->version) ||
+ (memcmp(&hdr_ba->transfer.uuid, &transfer->uuid, sizeof(transfer->uuid)) !=0)) {
+ DEBUG(2,("bind_rpc_pipe: transfer syntax differs\n"));
+ return False;
+ }
+
+ /* lkclXXXX only accept one result: check the result(s) */
+ if (hdr_ba->res.num_results != 0x1 || hdr_ba->res.result != 0) {
+ DEBUG(2,("bind_rpc_pipe: bind denied results: %d reason: %x\n",
+ hdr_ba->res.num_results, hdr_ba->res.reason));
+ }
+
+ DEBUG(5,("bind_rpc_pipe: accepted!\n"));
+ return True;
+}
+
+/****************************************************************************
+ Create and send the third packet in an RPC auth.
+****************************************************************************/
+
+static BOOL rpc_send_auth_reply(struct cli_state *cli, prs_struct *rdata, uint32 rpc_call_id)
+{
+ prs_struct rpc_out;
+ ssize_t ret;
+
+ prs_init(&rpc_out, RPC_HEADER_LEN + RPC_HDR_AUTHA_LEN, /* need at least this much */
+ cli->mem_ctx, MARSHALL);
+
+ if (!NT_STATUS_IS_OK(create_rpc_bind_resp(cli, rpc_call_id,
+ &rpc_out))) {
+ return False;
+ }
+
+ if ((ret = cli_write(cli, cli->nt_pipe_fnum, 0x8, prs_data_p(&rpc_out),
+ 0, (size_t)prs_offset(&rpc_out))) != (ssize_t)prs_offset(&rpc_out)) {
+ DEBUG(0,("rpc_send_auth_reply: cli_write failed. Return was %d\n", (int)ret));
+ prs_mem_free(&rpc_out);
+ return False;
+ }
+
+ prs_mem_free(&rpc_out);
+ return True;
+}
+
+/****************************************************************************
+ Do an rpc bind.
+****************************************************************************/
+
+static BOOL rpc_pipe_bind(struct cli_state *cli, int pipe_idx, const char *my_name)
+{
+ RPC_IFACE abstract;
+ RPC_IFACE transfer;
+ prs_struct rpc_out;
+ prs_struct rdata;
+ uint32 rpc_call_id;
+ char buffer[MAX_PDU_FRAG_LEN];
+
+ if ( (pipe_idx < 0) || (pipe_idx >= PI_MAX_PIPES) )
+ return False;
+
+ DEBUG(5,("Bind RPC Pipe[%x]: %s\n", cli->nt_pipe_fnum, pipe_names[pipe_idx].client_pipe));
+
+ if (!valid_pipe_name(pipe_idx, &abstract, &transfer))
+ return False;
+
+ prs_init(&rpc_out, 0, cli->mem_ctx, MARSHALL);
+
+ /*
+ * Use the MAX_PDU_FRAG_LEN buffer to store the bind request.
+ */
+
+ prs_give_memory( &rpc_out, buffer, sizeof(buffer), False);
+
+ rpc_call_id = get_rpc_call_id();
+
+ if (cli->pipe_auth_flags & AUTH_PIPE_NTLMSSP) {
+ NTSTATUS nt_status;
+ fstring password;
+
+ DEBUG(5, ("NTLMSSP authenticated pipe selected\n"));
+
+ nt_status = ntlmssp_client_start(&cli->ntlmssp_pipe_state);
+
+ if (!NT_STATUS_IS_OK(nt_status))
+ return False;
+
+ /* Currently the NTLMSSP code does not implement NTLM2 correctly for signing or sealing */
+
+ cli->ntlmssp_pipe_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
+
+ nt_status = ntlmssp_set_username(cli->ntlmssp_pipe_state,
+ cli->user_name);
+ if (!NT_STATUS_IS_OK(nt_status))
+ return False;
+
+ nt_status = ntlmssp_set_domain(cli->ntlmssp_pipe_state,
+ cli->domain);
+ if (!NT_STATUS_IS_OK(nt_status))
+ return False;
+
+ if (cli->pwd.null_pwd) {
+ nt_status = ntlmssp_set_password(cli->ntlmssp_pipe_state,
+ NULL);
+ if (!NT_STATUS_IS_OK(nt_status))
+ return False;
+ } else {
+ pwd_get_cleartext(&cli->pwd, password);
+ nt_status = ntlmssp_set_password(cli->ntlmssp_pipe_state,
+ password);
+ if (!NT_STATUS_IS_OK(nt_status))
+ return False;
+ }
+
+ if (cli->pipe_auth_flags & AUTH_PIPE_SIGN) {
+ cli->ntlmssp_pipe_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN;
+ }
+
+ if (cli->pipe_auth_flags & AUTH_PIPE_SEAL) {
+ cli->ntlmssp_pipe_state->neg_flags |= NTLMSSP_NEGOTIATE_SEAL;
+ }
+ } else if (cli->pipe_auth_flags & AUTH_PIPE_NETSEC) {
+ cli->auth_info.seq_num = 0;
+ }
+
+ /* Marshall the outgoing data. */
+ create_rpc_bind_req(cli, &rpc_out, rpc_call_id,
+ &abstract, &transfer,
+ global_myname(), cli->domain);
+
+ /* Initialize the incoming data struct. */
+ prs_init(&rdata, 0, cli->mem_ctx, UNMARSHALL);
+
+ /* send data on \PIPE\. receive a response */
+ if (rpc_api_pipe(cli, &rpc_out, &rdata, RPC_BINDACK)) {
+ RPC_HDR_BA hdr_ba;
+
+ DEBUG(5, ("rpc_pipe_bind: rpc_api_pipe returned OK.\n"));
+
+ if(!smb_io_rpc_hdr_ba("", &hdr_ba, &rdata, 0)) {
+ DEBUG(0,("rpc_pipe_bind: Failed to unmarshall RPC_HDR_BA.\n"));
+ prs_mem_free(&rdata);
+ return False;
+ }
+
+ if(!check_bind_response(&hdr_ba, pipe_idx, &transfer)) {
+ DEBUG(2,("rpc_pipe_bind: check_bind_response failed.\n"));
+ prs_mem_free(&rdata);
+ return False;
+ }
+
+ cli->max_xmit_frag = hdr_ba.bba.max_tsize;
+ cli->max_recv_frag = hdr_ba.bba.max_rsize;
+
+ /*
+ * If we're doing NTLMSSP auth we need to send a reply to
+ * the bind-ack to complete the 3-way challenge response
+ * handshake.
+ */
+
+ if ((cli->pipe_auth_flags & AUTH_PIPE_NTLMSSP)
+ && !rpc_send_auth_reply(cli, &rdata, rpc_call_id)) {
+ DEBUG(0,("rpc_pipe_bind: rpc_send_auth_reply failed.\n"));
+ prs_mem_free(&rdata);
+ return False;
+ }
+ prs_mem_free(&rdata);
+ return True;
+ }
+
+ return False;
+}
+
+/****************************************************************************
+ Open a session.
+ ****************************************************************************/
+
+BOOL cli_nt_session_open(struct cli_state *cli, const int pipe_idx)
+{
+ int fnum;
+
+ /* At the moment we can't have more than one pipe open over
+ a cli connection. )-: */
+
+ SMB_ASSERT(cli->nt_pipe_fnum == 0);
+
+ /* The pipe index must fall within our array */
+
+ SMB_ASSERT((pipe_idx >= 0) && (pipe_idx < PI_MAX_PIPES));
+
+ if (cli->capabilities & CAP_NT_SMBS) {
+ if ((fnum = cli_nt_create(cli, &pipe_names[pipe_idx].client_pipe[5], DESIRED_ACCESS_PIPE)) == -1) {
+ DEBUG(0,("cli_nt_session_open: cli_nt_create failed on pipe %s to machine %s. Error was %s\n",
+ &pipe_names[pipe_idx].client_pipe[5], cli->desthost, cli_errstr(cli)));
+ return False;
+ }
+
+ cli->nt_pipe_fnum = (uint16)fnum;
+ } else {
+ if ((fnum = cli_open(cli, pipe_names[pipe_idx].client_pipe, O_CREAT|O_RDWR, DENY_NONE)) == -1) {
+ DEBUG(1,("cli_nt_session_open: cli_open failed on pipe %s to machine %s. Error was %s\n",
+ pipe_names[pipe_idx].client_pipe, cli->desthost, cli_errstr(cli)));
+ return False;
+ }
+
+ cli->nt_pipe_fnum = (uint16)fnum;
+
+ /**************** Set Named Pipe State ***************/
+ if (!rpc_pipe_set_hnd_state(cli, pipe_names[pipe_idx].client_pipe, 0x4300)) {
+ DEBUG(0,("cli_nt_session_open: pipe hnd state failed. Error was %s\n",
+ cli_errstr(cli)));
+ cli_close(cli, cli->nt_pipe_fnum);
+ cli->nt_pipe_fnum = 0;
+ return False;
+ }
+ }
+
+ /******************* bind request on pipe *****************/
+
+ if (!rpc_pipe_bind(cli, pipe_idx, global_myname())) {
+ DEBUG(2,("cli_nt_session_open: rpc bind to %s failed\n",
+ get_pipe_name_from_index(pipe_idx)));
+ cli_close(cli, cli->nt_pipe_fnum);
+ cli->nt_pipe_fnum = 0;
+ return False;
+ }
+
+ cli->pipe_idx = pipe_idx;
+
+ /*
+ * Setup the remote server name prefixed by \ and the machine account name.
+ */
+
+ fstrcpy(cli->srv_name_slash, "\\\\");
+ fstrcat(cli->srv_name_slash, cli->desthost);
+ strupper_m(cli->srv_name_slash);
+
+ fstrcpy(cli->clnt_name_slash, "\\\\");
+ fstrcat(cli->clnt_name_slash, global_myname());
+ strupper_m(cli->clnt_name_slash);
+
+ fstrcpy(cli->mach_acct, global_myname());
+ fstrcat(cli->mach_acct, "$");
+ strupper_m(cli->mach_acct);
+
+ /* Remember which pipe we're talking to */
+ fstrcpy(cli->pipe_name, pipe_names[pipe_idx].client_pipe);
+
+ return True;
+}
+
+
+/****************************************************************************
+ Open a session to the NETLOGON pipe using schannel.
+
+ (Assumes that the netlogon pipe is already open)
+ ****************************************************************************/
+
+NTSTATUS cli_nt_establish_netlogon(struct cli_state *cli, int sec_chan,
+ const uchar trust_password[16])
+{
+ NTSTATUS result;
+ uint32 neg_flags = NETLOGON_NEG_AUTH2_FLAGS;
+ int fnum;
+
+ cli_nt_netlogon_netsec_session_close(cli);
+
+ if (lp_client_schannel() != False)
+ neg_flags |= NETLOGON_NEG_SCHANNEL;
+
+ result = cli_nt_setup_creds(cli, sec_chan, trust_password,
+ &neg_flags, 2);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ cli_nt_session_close(cli);
+ return result;
+ }
+
+ if ((lp_client_schannel() == True) &&
+ ((neg_flags & NETLOGON_NEG_SCHANNEL) == 0)) {
+
+ DEBUG(3, ("Server did not offer schannel\n"));
+ cli_nt_session_close(cli);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if ((lp_client_schannel() == False) ||
+ ((neg_flags & NETLOGON_NEG_SCHANNEL) == 0)) {
+ return NT_STATUS_OK;
+
+ /* keep the existing connection to NETLOGON open */
+
+ }
+
+ /* Server offered schannel, so try it. */
+
+ memcpy(cli->auth_info.sess_key, cli->sess_key,
+ sizeof(cli->auth_info.sess_key));
+
+ cli->saved_netlogon_pipe_fnum = cli->nt_pipe_fnum;
+
+ cli->pipe_auth_flags = AUTH_PIPE_NETSEC;
+ cli->pipe_auth_flags |= AUTH_PIPE_SIGN;
+ cli->pipe_auth_flags |= AUTH_PIPE_SEAL;
+
+ if (cli->capabilities & CAP_NT_SMBS) {
+
+ /* The secure channel connection must be opened on the same
+ session (TCP connection) as the one the challenge was
+ requested from. */
+ if ((fnum = cli_nt_create(cli, PIPE_NETLOGON_PLAIN,
+ DESIRED_ACCESS_PIPE)) == -1) {
+ DEBUG(0,("cli_nt_create failed to %s machine %s. "
+ "Error was %s\n",
+ PIPE_NETLOGON, cli->desthost,
+ cli_errstr(cli)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ cli->nt_pipe_fnum = (uint16)fnum;
+ } else {
+ if ((fnum = cli_open(cli, PIPE_NETLOGON,
+ O_CREAT|O_RDWR, DENY_NONE)) == -1) {
+ DEBUG(0,("cli_open failed on pipe %s to machine %s. "
+ "Error was %s\n",
+ PIPE_NETLOGON, cli->desthost,
+ cli_errstr(cli)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ cli->nt_pipe_fnum = (uint16)fnum;
+
+ /**************** Set Named Pipe State ***************/
+ if (!rpc_pipe_set_hnd_state(cli, PIPE_NETLOGON, 0x4300)) {
+ DEBUG(0,("Pipe hnd state failed. Error was %s\n",
+ cli_errstr(cli)));
+ cli_close(cli, cli->nt_pipe_fnum);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ if (!rpc_pipe_bind(cli, PI_NETLOGON, global_myname())) {
+ DEBUG(2,("rpc bind to %s failed\n", PIPE_NETLOGON));
+ cli_close(cli, cli->nt_pipe_fnum);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+NTSTATUS cli_nt_setup_netsec(struct cli_state *cli, int sec_chan, int auth_flags,
+ const uchar trust_password[16])
+{
+ NTSTATUS result;
+ uint32 neg_flags = NETLOGON_NEG_AUTH2_FLAGS;
+ cli->pipe_auth_flags = 0;
+
+ if (lp_client_schannel() == False) {
+ return NT_STATUS_OK;
+ }
+
+ if (!cli_nt_session_open(cli, PI_NETLOGON)) {
+ DEBUG(0, ("Could not initialise %s\n",
+ get_pipe_name_from_index(PI_NETLOGON)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (lp_client_schannel() != False)
+ neg_flags |= NETLOGON_NEG_SCHANNEL;
+
+ neg_flags |= NETLOGON_NEG_SCHANNEL;
+
+ result = cli_nt_setup_creds(cli, sec_chan, trust_password,
+ &neg_flags, 2);
+
+ if (!(neg_flags & NETLOGON_NEG_SCHANNEL)
+ && lp_client_schannel() == True) {
+ DEBUG(1, ("Could not negotiate SCHANNEL with the DC!\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ ZERO_STRUCT(cli->auth_info.sess_key);
+ ZERO_STRUCT(cli->sess_key);
+ cli->pipe_auth_flags = 0;
+ cli_nt_session_close(cli);
+ return result;
+ }
+
+ memcpy(cli->auth_info.sess_key, cli->sess_key,
+ sizeof(cli->auth_info.sess_key));
+
+ cli->saved_netlogon_pipe_fnum = cli->nt_pipe_fnum;
+ cli->nt_pipe_fnum = 0;
+
+ /* doing schannel, not per-user auth */
+ cli->pipe_auth_flags = auth_flags;
+
+ return NT_STATUS_OK;
+}
+
+const char *cli_pipe_get_name(struct cli_state *cli)
+{
+ return cli->pipe_name;
+}
+
+
diff --git a/source/rpc_client/cli_reg.c b/source/rpc_client/cli_reg.c
new file mode 100644
index 00000000000..5cfbf68fb34
--- /dev/null
+++ b/source/rpc_client/cli_reg.c
@@ -0,0 +1,103 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC Pipe client
+
+ Copyright (C) Andrew Tridgell 1992-1998,
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ Copyright (C) Paul Ashton 1997-1998.
+ Copyright (C) Jeremy Allison 1999.
+ Copyright (C) Simo Sorce 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* Shutdown a server */
+
+NTSTATUS cli_reg_shutdown(struct cli_state * cli, TALLOC_CTX *mem_ctx,
+ const char *msg, uint32 timeout, BOOL do_reboot,
+ BOOL force)
+{
+ prs_struct qbuf;
+ prs_struct rbuf;
+ REG_Q_SHUTDOWN q_s;
+ REG_R_SHUTDOWN r_s;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (msg == NULL) return NT_STATUS_INVALID_PARAMETER;
+
+ ZERO_STRUCT (q_s);
+ ZERO_STRUCT (r_s);
+
+ prs_init(&qbuf , MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_reg_q_shutdown(&q_s, msg, timeout, do_reboot, force);
+
+ if (!reg_io_q_shutdown("", &q_s, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, REG_SHUTDOWN, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if(reg_io_r_shutdown("", &r_s, &rbuf, 0))
+ result = r_s.status;
+
+done:
+ prs_mem_free(&rbuf);
+ prs_mem_free(&qbuf);
+
+ return result;
+}
+
+
+/* Abort a server shutdown */
+
+NTSTATUS cli_reg_abort_shutdown(struct cli_state * cli, TALLOC_CTX *mem_ctx)
+{
+ prs_struct rbuf;
+ prs_struct qbuf;
+ REG_Q_ABORT_SHUTDOWN q_s;
+ REG_R_ABORT_SHUTDOWN r_s;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ ZERO_STRUCT (q_s);
+ ZERO_STRUCT (r_s);
+
+ prs_init(&qbuf , MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_reg_q_abort_shutdown(&q_s);
+
+ if (!reg_io_q_abort_shutdown("", &q_s, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, REG_ABORT_SHUTDOWN, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (reg_io_r_abort_shutdown("", &r_s, &rbuf, 0))
+ result = r_s.status;
+
+done:
+ prs_mem_free(&rbuf);
+ prs_mem_free(&qbuf );
+
+ return result;
+}
diff --git a/source/rpc_client/cli_samr.c b/source/rpc_client/cli_samr.c
new file mode 100644
index 00000000000..d534745d25a
--- /dev/null
+++ b/source/rpc_client/cli_samr.c
@@ -0,0 +1,2102 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+ Copyright (C) Tim Potter 2000-2001,
+ Copyright (C) Andrew Tridgell 1992-1997,2000,
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1997,2000,
+ Copyright (C) Paul Ashton 1997,2000,
+ Copyright (C) Elrond 2000,
+ Copyright (C) Rafal Szczesniak 2002.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* Connect to SAMR database */
+
+NTSTATUS cli_samr_connect(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 access_mask, POLICY_HND *connect_pol)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_CONNECT q;
+ SAMR_R_CONNECT r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_connect to %s\n", cli->desthost));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_connect(&q, cli->desthost, access_mask);
+
+ if (!samr_io_q_connect("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_CONNECT, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_connect("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *connect_pol = r.connect_pol;
+#ifdef __INSURE__
+ connect_pol->marker = malloc(1);
+#endif
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Connect to SAMR database */
+
+NTSTATUS cli_samr_connect4(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 access_mask, POLICY_HND *connect_pol)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_CONNECT4 q;
+ SAMR_R_CONNECT4 r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_connect4 to %s\n", cli->desthost));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_connect4(&q, cli->desthost, access_mask);
+
+ if (!samr_io_q_connect4("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_CONNECT4, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_connect4("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *connect_pol = r.connect_pol;
+#ifdef __INSURE__
+ connect_pol->marker = malloc(1);
+#endif
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Close SAMR handle */
+
+NTSTATUS cli_samr_close(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *connect_pol)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_CLOSE_HND q;
+ SAMR_R_CLOSE_HND r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_close\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_close_hnd(&q, connect_pol);
+
+ if (!samr_io_q_close_hnd("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_CLOSE_HND, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_close_hnd("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+#ifdef __INSURE__
+ SAFE_FREE(connect_pol->marker);
+#endif
+ *connect_pol = r.pol;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Open handle on a domain */
+
+NTSTATUS cli_samr_open_domain(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *connect_pol, uint32 access_mask,
+ const DOM_SID *domain_sid, POLICY_HND *domain_pol)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_OPEN_DOMAIN q;
+ SAMR_R_OPEN_DOMAIN r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_open_domain with sid %s\n", sid_string_static(domain_sid) ));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_open_domain(&q, connect_pol, access_mask, domain_sid);
+
+ if (!samr_io_q_open_domain("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_OPEN_DOMAIN, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_open_domain("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *domain_pol = r.domain_pol;
+#ifdef __INSURE__
+ domain_pol->marker = malloc(1);
+#endif
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Open handle on a user */
+
+NTSTATUS cli_samr_open_user(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *domain_pol, uint32 access_mask,
+ uint32 user_rid, POLICY_HND *user_pol)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_OPEN_USER q;
+ SAMR_R_OPEN_USER r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_open_user with rid 0x%x\n", user_rid ));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_open_user(&q, domain_pol, access_mask, user_rid);
+
+ if (!samr_io_q_open_user("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_OPEN_USER, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_open_user("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *user_pol = r.user_pol;
+#ifdef __INSURE__
+ user_pol->marker = malloc(1);
+#endif
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Open handle on a group */
+
+NTSTATUS cli_samr_open_group(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *domain_pol, uint32 access_mask,
+ uint32 group_rid, POLICY_HND *group_pol)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_OPEN_GROUP q;
+ SAMR_R_OPEN_GROUP r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_open_group with rid 0x%x\n", group_rid ));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_open_group(&q, domain_pol, access_mask, group_rid);
+
+ if (!samr_io_q_open_group("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_OPEN_GROUP, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_open_group("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *group_pol = r.pol;
+#ifdef __INSURE__
+ group_pol->marker = malloc(1);
+#endif
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Create domain group */
+
+NTSTATUS cli_samr_create_dom_group(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *domain_pol,
+ const char *group_name,
+ uint32 access_mask, POLICY_HND *group_pol)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_CREATE_DOM_GROUP q;
+ SAMR_R_CREATE_DOM_GROUP r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_create_dom_group\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_create_dom_group(&q, domain_pol, group_name, access_mask);
+
+ if (!samr_io_q_create_dom_group("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_CREATE_DOM_GROUP, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_create_dom_group("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (NT_STATUS_IS_OK(result))
+ *group_pol = r.pol;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Add a domain group member */
+
+NTSTATUS cli_samr_add_groupmem(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *group_pol, uint32 rid)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_ADD_GROUPMEM q;
+ SAMR_R_ADD_GROUPMEM r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_add_groupmem\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_add_groupmem(&q, group_pol, rid);
+
+ if (!samr_io_q_add_groupmem("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_ADD_GROUPMEM, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_add_groupmem("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Delete a domain group member */
+
+NTSTATUS cli_samr_del_groupmem(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *group_pol, uint32 rid)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_DEL_GROUPMEM q;
+ SAMR_R_DEL_GROUPMEM r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_del_groupmem\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_del_groupmem(&q, group_pol, rid);
+
+ if (!samr_io_q_del_groupmem("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_DEL_GROUPMEM, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_del_groupmem("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Query user info */
+
+NTSTATUS cli_samr_query_userinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *user_pol, uint16 switch_value,
+ SAM_USERINFO_CTR **ctr)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_QUERY_USERINFO q;
+ SAMR_R_QUERY_USERINFO r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_query_userinfo\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_query_userinfo(&q, user_pol, switch_value);
+
+ if (!samr_io_q_query_userinfo("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_QUERY_USERINFO, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_query_userinfo("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+ *ctr = r.ctr;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Set group info */
+
+NTSTATUS cli_samr_set_groupinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *group_pol, GROUP_INFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_SET_GROUPINFO q;
+ SAMR_R_SET_GROUPINFO r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_set_groupinfo\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_set_groupinfo(&q, group_pol, ctr);
+
+ if (!samr_io_q_set_groupinfo("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_SET_GROUPINFO, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_set_groupinfo("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Query group info */
+
+NTSTATUS cli_samr_query_groupinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *group_pol, uint32 info_level,
+ GROUP_INFO_CTR **ctr)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_QUERY_GROUPINFO q;
+ SAMR_R_QUERY_GROUPINFO r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_query_groupinfo\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_query_groupinfo(&q, group_pol, info_level);
+
+ if (!samr_io_q_query_groupinfo("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_QUERY_GROUPINFO, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_query_groupinfo("", &r, &rbuf, 0))
+ goto done;
+
+ *ctr = r.ctr;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Query user groups */
+
+NTSTATUS cli_samr_query_usergroups(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *user_pol, uint32 *num_groups,
+ DOM_GID **gid)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_QUERY_USERGROUPS q;
+ SAMR_R_QUERY_USERGROUPS r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_query_usergroups\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_query_usergroups(&q, user_pol);
+
+ if (!samr_io_q_query_usergroups("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_QUERY_USERGROUPS, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_query_usergroups("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *num_groups = r.num_entries;
+ *gid = r.gid;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Set alias info */
+
+NTSTATUS cli_samr_set_aliasinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *alias_pol, ALIAS_INFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_SET_ALIASINFO q;
+ SAMR_R_SET_ALIASINFO r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_set_aliasinfo\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_set_aliasinfo(&q, alias_pol, ctr);
+
+ if (!samr_io_q_set_aliasinfo("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_SET_ALIASINFO, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_set_aliasinfo("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Query user aliases */
+
+NTSTATUS cli_samr_query_useraliases(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *user_pol, uint32 num_sids, DOM_SID2 *sid,
+ uint32 *num_aliases, uint32 **als_rids)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_QUERY_USERALIASES q;
+ SAMR_R_QUERY_USERALIASES r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ unsigned int ptr=1;
+
+ DEBUG(10,("cli_samr_query_useraliases\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_query_useraliases(&q, user_pol, num_sids, &ptr, sid);
+
+ if (!samr_io_q_query_useraliases("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_QUERY_USERALIASES, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_query_useraliases("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *num_aliases = r.num_entries;
+ *als_rids = r.rid;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Query user groups */
+
+NTSTATUS cli_samr_query_groupmem(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *group_pol, uint32 *num_mem,
+ uint32 **rid, uint32 **attr)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_QUERY_GROUPMEM q;
+ SAMR_R_QUERY_GROUPMEM r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_query_groupmem\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_query_groupmem(&q, group_pol);
+
+ if (!samr_io_q_query_groupmem("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_QUERY_GROUPMEM, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_query_groupmem("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *num_mem = r.num_entries;
+ *rid = r.rid;
+ *attr = r.attr;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/**
+ * Enumerate domain users
+ *
+ * @param cli client state structure
+ * @param mem_ctx talloc context
+ * @param pol opened domain policy handle
+ * @param start_idx starting index of enumeration, returns context for
+ next enumeration
+ * @param acb_mask account control bit mask (to enumerate some particular
+ * kind of accounts)
+ * @param size max acceptable size of response
+ * @param dom_users returned array of domain user names
+ * @param rids returned array of domain user RIDs
+ * @param num_dom_users numer returned entries
+ *
+ * @return NTSTATUS returned in rpc response
+ **/
+NTSTATUS cli_samr_enum_dom_users(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint32 *start_idx, uint16 acb_mask,
+ uint32 size, char ***dom_users, uint32 **rids,
+ uint32 *num_dom_users)
+{
+ prs_struct qbuf;
+ prs_struct rbuf;
+ SAMR_Q_ENUM_DOM_USERS q;
+ SAMR_R_ENUM_DOM_USERS r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ int i;
+
+ DEBUG(10,("cli_samr_enum_dom_users starting at index %u\n", (unsigned int)*start_idx));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* always init this */
+ *num_dom_users = 0;
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Fill query structure with parameters */
+
+ init_samr_q_enum_dom_users(&q, pol, *start_idx, acb_mask, 0, size);
+
+ if (!samr_io_q_enum_dom_users("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_ENUM_DOM_USERS, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* unpack received stream */
+
+ if(!samr_io_r_enum_dom_users("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (!NT_STATUS_IS_OK(result) &&
+ NT_STATUS_V(result) != NT_STATUS_V(STATUS_MORE_ENTRIES))
+ goto done;
+
+ *start_idx = r.next_idx;
+ *num_dom_users = r.num_entries2;
+
+ if (r.num_entries2) {
+ /* allocate memory needed to return received data */
+ *rids = (uint32*)talloc(mem_ctx, sizeof(uint32) * r.num_entries2);
+ if (!*rids) {
+ DEBUG(0, ("Error in cli_samr_enum_dom_users(): out of memory\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *dom_users = (char**)talloc(mem_ctx, sizeof(char*) * r.num_entries2);
+ if (!*dom_users) {
+ DEBUG(0, ("Error in cli_samr_enum_dom_users(): out of memory\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* fill output buffers with rpc response */
+ for (i = 0; i < r.num_entries2; i++) {
+ fstring conv_buf;
+
+ (*rids)[i] = r.sam[i].rid;
+ unistr2_to_ascii(conv_buf, &(r.uni_acct_name[i]), sizeof(conv_buf) - 1);
+ (*dom_users)[i] = talloc_strdup(mem_ctx, conv_buf);
+ }
+ }
+
+done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Enumerate domain groups */
+
+NTSTATUS cli_samr_enum_dom_groups(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint32 *start_idx,
+ uint32 size, struct acct_info **dom_groups,
+ uint32 *num_dom_groups)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_ENUM_DOM_GROUPS q;
+ SAMR_R_ENUM_DOM_GROUPS r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 name_idx, i;
+
+ DEBUG(10,("cli_samr_enum_dom_groups starting at index %u\n", (unsigned int)*start_idx));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_enum_dom_groups(&q, pol, *start_idx, size);
+
+ if (!samr_io_q_enum_dom_groups("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_ENUM_DOM_GROUPS, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_enum_dom_groups("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (!NT_STATUS_IS_OK(result) &&
+ NT_STATUS_V(result) != NT_STATUS_V(STATUS_MORE_ENTRIES))
+ goto done;
+
+ *num_dom_groups = r.num_entries2;
+
+ if (*num_dom_groups == 0)
+ goto done;
+
+ if (!((*dom_groups) = (struct acct_info *)
+ talloc(mem_ctx, sizeof(struct acct_info) * *num_dom_groups))) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ memset(*dom_groups, 0, sizeof(struct acct_info) * (*num_dom_groups));
+
+ name_idx = 0;
+
+ for (i = 0; i < *num_dom_groups; i++) {
+
+ (*dom_groups)[i].rid = r.sam[i].rid;
+
+ if (r.sam[i].hdr_name.buffer) {
+ unistr2_to_ascii((*dom_groups)[i].acct_name,
+ &r.uni_grp_name[name_idx],
+ sizeof(fstring) - 1);
+ name_idx++;
+ }
+
+ *start_idx = r.next_idx;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Enumerate domain groups */
+
+NTSTATUS cli_samr_enum_als_groups(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint32 *start_idx,
+ uint32 size, struct acct_info **dom_aliases,
+ uint32 *num_dom_aliases)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_ENUM_DOM_ALIASES q;
+ SAMR_R_ENUM_DOM_ALIASES r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 name_idx, i;
+
+ DEBUG(10,("cli_samr_enum_als_groups starting at index %u\n", (unsigned int)*start_idx));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_enum_dom_aliases(&q, pol, *start_idx, size);
+
+ if (!samr_io_q_enum_dom_aliases("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_ENUM_DOM_ALIASES, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_enum_dom_aliases("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (!NT_STATUS_IS_OK(result) &&
+ NT_STATUS_V(result) != NT_STATUS_V(STATUS_MORE_ENTRIES)) {
+ goto done;
+ }
+
+ *num_dom_aliases = r.num_entries2;
+
+ if (*num_dom_aliases == 0)
+ goto done;
+
+ if (!((*dom_aliases) = (struct acct_info *)
+ talloc(mem_ctx, sizeof(struct acct_info) * *num_dom_aliases))) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ memset(*dom_aliases, 0, sizeof(struct acct_info) * *num_dom_aliases);
+
+ name_idx = 0;
+
+ for (i = 0; i < *num_dom_aliases; i++) {
+
+ (*dom_aliases)[i].rid = r.sam[i].rid;
+
+ if (r.sam[i].hdr_name.buffer) {
+ unistr2_to_ascii((*dom_aliases)[i].acct_name,
+ &r.uni_grp_name[name_idx],
+ sizeof(fstring) - 1);
+ name_idx++;
+ }
+
+ *start_idx = r.next_idx;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Query alias members */
+
+NTSTATUS cli_samr_query_aliasmem(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *alias_pol, uint32 *num_mem,
+ DOM_SID **sids)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_QUERY_ALIASMEM q;
+ SAMR_R_QUERY_ALIASMEM r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 i;
+
+ DEBUG(10,("cli_samr_query_aliasmem\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_query_aliasmem(&q, alias_pol);
+
+ if (!samr_io_q_query_aliasmem("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_QUERY_ALIASMEM, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_query_aliasmem("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ *num_mem = r.num_sids;
+
+ if (*num_mem == 0) {
+ *sids = NULL;
+ result = NT_STATUS_OK;
+ goto done;
+ }
+
+ if (!(*sids = talloc(mem_ctx, sizeof(DOM_SID) * *num_mem))) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ for (i = 0; i < *num_mem; i++) {
+ (*sids)[i] = r.sid[i].sid;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Open handle on an alias */
+
+NTSTATUS cli_samr_open_alias(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *domain_pol, uint32 access_mask,
+ uint32 alias_rid, POLICY_HND *alias_pol)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_OPEN_ALIAS q;
+ SAMR_R_OPEN_ALIAS r;
+ NTSTATUS result;
+
+ DEBUG(10,("cli_samr_open_alias with rid 0x%x\n", alias_rid));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_open_alias(&q, domain_pol, access_mask, alias_rid);
+
+ if (!samr_io_q_open_alias("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_OPEN_ALIAS, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_open_alias("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *alias_pol = r.pol;
+#ifdef __INSURE__
+ alias_pol->marker = malloc(1);
+#endif
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Create an alias */
+
+NTSTATUS cli_samr_create_dom_alias(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *domain_pol, const char *name,
+ POLICY_HND *alias_pol)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_CREATE_DOM_ALIAS q;
+ SAMR_R_CREATE_DOM_ALIAS r;
+ NTSTATUS result;
+
+ DEBUG(10,("cli_samr_create_dom_alias named %s\n", name));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_create_dom_alias(&q, domain_pol, name);
+
+ if (!samr_io_q_create_dom_alias("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_CREATE_DOM_ALIAS, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_create_dom_alias("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *alias_pol = r.alias_pol;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Add an alias member */
+
+NTSTATUS cli_samr_add_aliasmem(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *alias_pol, DOM_SID *member)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_ADD_ALIASMEM q;
+ SAMR_R_ADD_ALIASMEM r;
+ NTSTATUS result;
+
+ DEBUG(10,("cli_samr_add_aliasmem"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_add_aliasmem(&q, alias_pol, member);
+
+ if (!samr_io_q_add_aliasmem("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_ADD_ALIASMEM, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_add_aliasmem("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Delete an alias member */
+
+NTSTATUS cli_samr_del_aliasmem(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *alias_pol, DOM_SID *member)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_DEL_ALIASMEM q;
+ SAMR_R_DEL_ALIASMEM r;
+ NTSTATUS result;
+
+ DEBUG(10,("cli_samr_del_aliasmem"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_del_aliasmem(&q, alias_pol, member);
+
+ if (!samr_io_q_del_aliasmem("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_DEL_ALIASMEM, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_del_aliasmem("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Query alias info */
+
+NTSTATUS cli_samr_query_alias_info(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *alias_pol, uint16 switch_value,
+ ALIAS_INFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_QUERY_ALIASINFO q;
+ SAMR_R_QUERY_ALIASINFO r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_query_dom_info\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_query_aliasinfo(&q, alias_pol, switch_value);
+
+ if (!samr_io_q_query_aliasinfo("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_QUERY_ALIASINFO, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_query_aliasinfo("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ *ctr = r.ctr;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Query domain info */
+
+NTSTATUS cli_samr_query_dom_info(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *domain_pol, uint16 switch_value,
+ SAM_UNK_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_QUERY_DOMAIN_INFO q;
+ SAMR_R_QUERY_DOMAIN_INFO r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_query_dom_info\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_query_dom_info(&q, domain_pol, switch_value);
+
+ if (!samr_io_q_query_dom_info("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_QUERY_DOMAIN_INFO, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ r.ctr = ctr;
+
+ if (!samr_io_r_query_dom_info("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* User change password */
+
+NTSTATUS cli_samr_chgpasswd_user(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const char *username,
+ const char *newpassword,
+ const char *oldpassword )
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_CHGPASSWD_USER q;
+ SAMR_R_CHGPASSWD_USER r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ uchar new_nt_password[516];
+ uchar new_lm_password[516];
+ uchar old_nt_hash[16];
+ uchar old_lanman_hash[16];
+ uchar old_nt_hash_enc[16];
+ uchar old_lanman_hash_enc[16];
+
+ uchar new_nt_hash[16];
+ uchar new_lanman_hash[16];
+
+ DEBUG(10,("cli_samr_query_dom_info\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Calculate the MD4 hash (NT compatible) of the password */
+ E_md4hash(oldpassword, old_nt_hash);
+ E_md4hash(newpassword, new_nt_hash);
+
+ if (lp_client_lanman_auth()
+ && E_deshash(newpassword, new_lanman_hash)
+ && E_deshash(oldpassword, old_lanman_hash)) {
+ /* E_deshash returns false for 'long' passwords (> 14
+ DOS chars). This allows us to match Win2k, which
+ does not store a LM hash for these passwords (which
+ would reduce the effective password length to 14) */
+
+ encode_pw_buffer(new_lm_password, newpassword, STR_UNICODE);
+
+ SamOEMhash( new_lm_password, old_nt_hash, 516);
+ E_old_pw_hash( new_nt_hash, old_lanman_hash, old_lanman_hash_enc);
+ } else {
+ ZERO_STRUCT(new_lm_password);
+ ZERO_STRUCT(old_lanman_hash_enc);
+ }
+
+ encode_pw_buffer(new_nt_password, newpassword, STR_UNICODE);
+
+ SamOEMhash( new_nt_password, old_nt_hash, 516);
+ E_old_pw_hash( new_nt_hash, old_nt_hash, old_nt_hash_enc);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_chgpasswd_user(&q, cli->srv_name_slash, username,
+ new_nt_password,
+ old_nt_hash_enc,
+ new_lm_password,
+ old_lanman_hash_enc);
+
+ if (!samr_io_q_chgpasswd_user("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_CHGPASSWD_USER, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_chgpasswd_user("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* This function returns the bizzare set of (max_entries, max_size) required
+ for the QueryDisplayInfo RPC to actually work against a domain controller
+ with large (10k and higher) numbers of users. These values were
+ obtained by inspection using ethereal and NT4 running User Manager. */
+
+void get_query_dispinfo_params(int loop_count, uint32 *max_entries,
+ uint32 *max_size)
+{
+ switch(loop_count) {
+ case 0:
+ *max_entries = 512;
+ *max_size = 16383;
+ break;
+ case 1:
+ *max_entries = 1024;
+ *max_size = 32766;
+ break;
+ case 2:
+ *max_entries = 2048;
+ *max_size = 65532;
+ break;
+ case 3:
+ *max_entries = 4096;
+ *max_size = 131064;
+ break;
+ default: /* loop_count >= 4 */
+ *max_entries = 4096;
+ *max_size = 131071;
+ break;
+ }
+}
+
+/* Query display info */
+
+NTSTATUS cli_samr_query_dispinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *domain_pol, uint32 *start_idx,
+ uint16 switch_value, uint32 *num_entries,
+ uint32 max_entries, uint32 max_size,
+ SAM_DISPINFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_QUERY_DISPINFO q;
+ SAMR_R_QUERY_DISPINFO r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_query_dispinfo for start_idx = %u\n", *start_idx));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ *num_entries = 0;
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_query_dispinfo(&q, domain_pol, switch_value,
+ *start_idx, max_entries, max_size);
+
+ if (!samr_io_q_query_dispinfo("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_QUERY_DISPINFO, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ r.ctr = ctr;
+
+ if (!samr_io_r_query_dispinfo("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (!NT_STATUS_IS_OK(result) &&
+ NT_STATUS_V(result) != NT_STATUS_V(STATUS_MORE_ENTRIES)) {
+ goto done;
+ }
+
+ *num_entries = r.num_entries;
+ *start_idx += r.num_entries; /* No next_idx in this structure! */
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Lookup rids. Note that NT4 seems to crash if more than ~1000 rids are
+ looked up in one packet. */
+
+NTSTATUS cli_samr_lookup_rids(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *domain_pol, uint32 flags,
+ uint32 num_rids, uint32 *rids,
+ uint32 *num_names, char ***names,
+ uint32 **name_types)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_LOOKUP_RIDS q;
+ SAMR_R_LOOKUP_RIDS r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 i;
+
+ DEBUG(10,("cli_samr_lookup_rids\n"));
+
+ if (num_rids > 1000) {
+ DEBUG(2, ("cli_samr_lookup_rids: warning: NT4 can crash if "
+ "more than ~1000 rids are looked up at once.\n"));
+ }
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_lookup_rids(mem_ctx, &q, domain_pol, flags,
+ num_rids, rids);
+
+ if (!samr_io_q_lookup_rids("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_LOOKUP_RIDS, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_lookup_rids("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ if (r.num_names1 == 0) {
+ *num_names = 0;
+ *names = NULL;
+ goto done;
+ }
+
+ *num_names = r.num_names1;
+ *names = talloc(mem_ctx, sizeof(char *) * r.num_names1);
+ *name_types = talloc(mem_ctx, sizeof(uint32) * r.num_names1);
+
+ for (i = 0; i < r.num_names1; i++) {
+ fstring tmp;
+
+ unistr2_to_ascii(tmp, &r.uni_name[i], sizeof(tmp) - 1);
+ (*names)[i] = talloc_strdup(mem_ctx, tmp);
+ (*name_types)[i] = r.type[i];
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Lookup names */
+
+NTSTATUS cli_samr_lookup_names(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *domain_pol, uint32 flags,
+ uint32 num_names, const char **names,
+ uint32 *num_rids, uint32 **rids,
+ uint32 **rid_types)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_LOOKUP_NAMES q;
+ SAMR_R_LOOKUP_NAMES r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 i;
+
+ DEBUG(10,("cli_samr_lookup_names\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_lookup_names(mem_ctx, &q, domain_pol, flags,
+ num_names, names);
+
+ if (!samr_io_q_lookup_names("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_LOOKUP_NAMES, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_lookup_names("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ if (r.num_rids1 == 0) {
+ *num_rids = 0;
+ goto done;
+ }
+
+ *num_rids = r.num_rids1;
+ *rids = talloc(mem_ctx, sizeof(uint32) * r.num_rids1);
+ *rid_types = talloc(mem_ctx, sizeof(uint32) * r.num_rids1);
+
+ for (i = 0; i < r.num_rids1; i++) {
+ (*rids)[i] = r.rids[i];
+ (*rid_types)[i] = r.types[i];
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Create a domain user */
+
+NTSTATUS cli_samr_create_dom_user(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *domain_pol, const char *acct_name,
+ uint32 acb_info, uint32 unknown,
+ POLICY_HND *user_pol, uint32 *rid)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_CREATE_USER q;
+ SAMR_R_CREATE_USER r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_create_dom_user %s\n", acct_name));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_create_user(&q, domain_pol, acct_name, acb_info, unknown);
+
+ if (!samr_io_q_create_user("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_CREATE_USER, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_create_user("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ if (user_pol)
+ *user_pol = r.user_pol;
+
+ if (rid)
+ *rid = r.user_rid;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Set userinfo */
+
+NTSTATUS cli_samr_set_userinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *user_pol, uint16 switch_value,
+ DATA_BLOB *sess_key, SAM_USERINFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_SET_USERINFO q;
+ SAMR_R_SET_USERINFO r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_set_userinfo\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ if (!sess_key->length) {
+ DEBUG(1, ("No user session key\n"));
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ q.ctr = ctr;
+
+ init_samr_q_set_userinfo(&q, user_pol, sess_key, switch_value,
+ ctr->info.id);
+
+ if (!samr_io_q_set_userinfo("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_SET_USERINFO, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_set_userinfo("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Set userinfo2 */
+
+NTSTATUS cli_samr_set_userinfo2(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *user_pol, uint16 switch_value,
+ DATA_BLOB *sess_key, SAM_USERINFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_SET_USERINFO2 q;
+ SAMR_R_SET_USERINFO2 r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_set_userinfo2\n"));
+
+ if (!sess_key->length) {
+ DEBUG(1, ("No user session key\n"));
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_set_userinfo2(&q, user_pol, sess_key, switch_value, ctr);
+
+ if (!samr_io_q_set_userinfo2("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_SET_USERINFO2, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_set_userinfo2("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Delete domain user */
+
+NTSTATUS cli_samr_delete_dom_user(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *user_pol)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_DELETE_DOM_USER q;
+ SAMR_R_DELETE_DOM_USER r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_delete_dom_user\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_delete_dom_user(&q, user_pol);
+
+ if (!samr_io_q_delete_dom_user("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_DELETE_DOM_USER, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_delete_dom_user("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Query user security object */
+
+NTSTATUS cli_samr_query_sec_obj(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *user_pol, uint16 switch_value,
+ TALLOC_CTX *ctx, SEC_DESC_BUF **sec_desc_buf)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_QUERY_SEC_OBJ q;
+ SAMR_R_QUERY_SEC_OBJ r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_query_sec_obj\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_query_sec_obj(&q, user_pol, switch_value);
+
+ if (!samr_io_q_query_sec_obj("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_QUERY_SEC_OBJECT, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_query_sec_obj("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ result = r.status;
+ *sec_desc_buf=dup_sec_desc_buf(ctx, r.buf);
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Get domain password info */
+
+NTSTATUS cli_samr_get_dom_pwinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint16 *unk_0, uint16 *unk_1, uint16 *unk_2)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_GET_DOM_PWINFO q;
+ SAMR_R_GET_DOM_PWINFO r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_get_dom_pwinfo\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_get_dom_pwinfo(&q, cli->desthost);
+
+ if (!samr_io_q_get_dom_pwinfo("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_GET_DOM_PWINFO, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_get_dom_pwinfo("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (NT_STATUS_IS_OK(result)) {
+ if (unk_0)
+ *unk_0 = r.unk_0;
+ if (unk_1)
+ *unk_1 = r.unk_1;
+ if (unk_2)
+ *unk_2 = r.unk_2;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Lookup Domain Name */
+
+NTSTATUS cli_samr_lookup_domain(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *user_pol, char *domain_name,
+ DOM_SID *sid)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_LOOKUP_DOMAIN q;
+ SAMR_R_LOOKUP_DOMAIN r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_lookup_domain\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_lookup_domain(&q, user_pol, domain_name);
+
+ if (!samr_io_q_lookup_domain("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_LOOKUP_DOMAIN, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_lookup_domain("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (NT_STATUS_IS_OK(result))
+ sid_copy(sid, &r.dom_sid.sid);
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
diff --git a/source/rpc_client/cli_shutdown.c b/source/rpc_client/cli_shutdown.c
new file mode 100644
index 00000000000..0bf6e90ad27
--- /dev/null
+++ b/source/rpc_client/cli_shutdown.c
@@ -0,0 +1,104 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC Pipe client
+
+ Copyright (C) Andrew Tridgell 1992-1998,
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ Copyright (C) Paul Ashton 1997-1998.
+ Copyright (C) Jeremy Allison 1999,
+ Copyright (C) Simo Sorce 2001,
+ Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* Shutdown a server */
+
+NTSTATUS cli_shutdown_init(struct cli_state * cli, TALLOC_CTX *mem_ctx,
+ const char *msg, uint32 timeout, BOOL do_reboot,
+ BOOL force)
+{
+ prs_struct qbuf;
+ prs_struct rbuf;
+ SHUTDOWN_Q_INIT q_s;
+ SHUTDOWN_R_INIT r_s;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (msg == NULL) return NT_STATUS_INVALID_PARAMETER;
+
+ ZERO_STRUCT (q_s);
+ ZERO_STRUCT (r_s);
+
+ prs_init(&qbuf , MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_shutdown_q_init(&q_s, msg, timeout, do_reboot, force);
+
+ if (!shutdown_io_q_init("", &q_s, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SHUTDOWN_INIT, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if(shutdown_io_r_init("", &r_s, &rbuf, 0))
+ result = r_s.status;
+
+done:
+ prs_mem_free(&rbuf);
+ prs_mem_free(&qbuf);
+
+ return result;
+}
+
+
+/* Abort a server shutdown */
+
+NTSTATUS cli_shutdown_abort(struct cli_state * cli, TALLOC_CTX *mem_ctx)
+{
+ prs_struct rbuf;
+ prs_struct qbuf;
+ SHUTDOWN_Q_ABORT q_s;
+ SHUTDOWN_R_ABORT r_s;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ ZERO_STRUCT (q_s);
+ ZERO_STRUCT (r_s);
+
+ prs_init(&qbuf , MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_shutdown_q_abort(&q_s);
+
+ if (!shutdown_io_q_abort("", &q_s, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SHUTDOWN_ABORT, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (shutdown_io_r_abort("", &r_s, &rbuf, 0))
+ result = r_s.status;
+
+done:
+ prs_mem_free(&rbuf);
+ prs_mem_free(&qbuf );
+
+ return result;
+}
diff --git a/source/rpc_client/cli_spoolss.c b/source/rpc_client/cli_spoolss.c
new file mode 100644
index 00000000000..8f5f2413dee
--- /dev/null
+++ b/source/rpc_client/cli_spoolss.c
@@ -0,0 +1,2466 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ Copyright (C) Gerald Carter 2001-2002,
+ Copyright (C) Tim Potter 2000-2002,
+ Copyright (C) Andrew Tridgell 1994-2000,
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+ Copyright (C) Jean-Francois Micouleau 1999-2000.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/** @defgroup spoolss SPOOLSS - NT printing routines
+ * @ingroup rpc_client
+ *
+ * @{
+ **/
+
+/**********************************************************************
+ Initialize a new spoolss buff for use by a client rpc
+**********************************************************************/
+static void init_buffer(NEW_BUFFER *buffer, uint32 size, TALLOC_CTX *ctx)
+{
+ buffer->ptr = (size != 0);
+ buffer->size = size;
+ buffer->string_at_end = size;
+ prs_init(&buffer->prs, size, ctx, MARSHALL);
+ buffer->struct_start = prs_offset(&buffer->prs);
+}
+
+/*********************************************************************
+ Decode various spoolss rpc's and info levels
+ ********************************************************************/
+
+/**********************************************************************
+**********************************************************************/
+static void decode_printer_info_0(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+ uint32 returned, PRINTER_INFO_0 **info)
+{
+ uint32 i;
+ PRINTER_INFO_0 *inf;
+
+ inf=(PRINTER_INFO_0 *)talloc(mem_ctx, returned*sizeof(PRINTER_INFO_0));
+ memset(inf, 0, returned*sizeof(PRINTER_INFO_0));
+
+ prs_set_offset(&buffer->prs,0);
+
+ for (i=0; i<returned; i++) {
+ smb_io_printer_info_0("", buffer, &inf[i], 0);
+ }
+
+ *info=inf;
+}
+
+/**********************************************************************
+**********************************************************************/
+static void decode_printer_info_1(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+ uint32 returned, PRINTER_INFO_1 **info)
+{
+ uint32 i;
+ PRINTER_INFO_1 *inf;
+
+ inf=(PRINTER_INFO_1 *)talloc(mem_ctx, returned*sizeof(PRINTER_INFO_1));
+ memset(inf, 0, returned*sizeof(PRINTER_INFO_1));
+
+ prs_set_offset(&buffer->prs,0);
+
+ for (i=0; i<returned; i++) {
+ smb_io_printer_info_1("", buffer, &inf[i], 0);
+ }
+
+ *info=inf;
+}
+
+/**********************************************************************
+**********************************************************************/
+static void decode_printer_info_2(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+ uint32 returned, PRINTER_INFO_2 **info)
+{
+ uint32 i;
+ PRINTER_INFO_2 *inf;
+
+ inf=(PRINTER_INFO_2 *)talloc(mem_ctx, returned*sizeof(PRINTER_INFO_2));
+ memset(inf, 0, returned*sizeof(PRINTER_INFO_2));
+
+ prs_set_offset(&buffer->prs,0);
+
+ for (i=0; i<returned; i++) {
+ /* a little initialization as we go */
+ inf[i].secdesc = NULL;
+ smb_io_printer_info_2("", buffer, &inf[i], 0);
+ }
+
+ *info=inf;
+}
+
+/**********************************************************************
+**********************************************************************/
+static void decode_printer_info_3(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+ uint32 returned, PRINTER_INFO_3 **info)
+{
+ uint32 i;
+ PRINTER_INFO_3 *inf;
+
+ inf=(PRINTER_INFO_3 *)talloc(mem_ctx, returned*sizeof(PRINTER_INFO_3));
+ memset(inf, 0, returned*sizeof(PRINTER_INFO_3));
+
+ prs_set_offset(&buffer->prs,0);
+
+ for (i=0; i<returned; i++) {
+ inf[i].secdesc = NULL;
+ smb_io_printer_info_3("", buffer, &inf[i], 0);
+ }
+
+ *info=inf;
+}
+
+/**********************************************************************
+**********************************************************************/
+static void decode_port_info_1(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+ uint32 returned, PORT_INFO_1 **info)
+{
+ uint32 i;
+ PORT_INFO_1 *inf;
+
+ inf=(PORT_INFO_1*)talloc(mem_ctx, returned*sizeof(PORT_INFO_1));
+ memset(inf, 0, returned*sizeof(PORT_INFO_1));
+
+ prs_set_offset(&buffer->prs, 0);
+
+ for (i=0; i<returned; i++) {
+ smb_io_port_info_1("", buffer, &(inf[i]), 0);
+ }
+
+ *info=inf;
+}
+
+/**********************************************************************
+**********************************************************************/
+static void decode_port_info_2(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+ uint32 returned, PORT_INFO_2 **info)
+{
+ uint32 i;
+ PORT_INFO_2 *inf;
+
+ inf=(PORT_INFO_2*)talloc(mem_ctx, returned*sizeof(PORT_INFO_2));
+ memset(inf, 0, returned*sizeof(PORT_INFO_2));
+
+ prs_set_offset(&buffer->prs, 0);
+
+ for (i=0; i<returned; i++) {
+ smb_io_port_info_2("", buffer, &(inf[i]), 0);
+ }
+
+ *info=inf;
+}
+
+/**********************************************************************
+**********************************************************************/
+static void decode_printer_driver_1(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+ uint32 returned, DRIVER_INFO_1 **info)
+{
+ uint32 i;
+ DRIVER_INFO_1 *inf;
+
+ inf=(DRIVER_INFO_1 *)talloc(mem_ctx, returned*sizeof(DRIVER_INFO_1));
+ memset(inf, 0, returned*sizeof(DRIVER_INFO_1));
+
+ prs_set_offset(&buffer->prs,0);
+
+ for (i=0; i<returned; i++) {
+ smb_io_printer_driver_info_1("", buffer, &(inf[i]), 0);
+ }
+
+ *info=inf;
+}
+
+/**********************************************************************
+**********************************************************************/
+static void decode_printer_driver_2(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+ uint32 returned, DRIVER_INFO_2 **info)
+{
+ uint32 i;
+ DRIVER_INFO_2 *inf;
+
+ inf=(DRIVER_INFO_2 *)talloc(mem_ctx, returned*sizeof(DRIVER_INFO_2));
+ memset(inf, 0, returned*sizeof(DRIVER_INFO_2));
+
+ prs_set_offset(&buffer->prs,0);
+
+ for (i=0; i<returned; i++) {
+ smb_io_printer_driver_info_2("", buffer, &(inf[i]), 0);
+ }
+
+ *info=inf;
+}
+
+/**********************************************************************
+**********************************************************************/
+static void decode_printer_driver_3(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+ uint32 returned, DRIVER_INFO_3 **info)
+{
+ uint32 i;
+ DRIVER_INFO_3 *inf;
+
+ inf=(DRIVER_INFO_3 *)talloc(mem_ctx, returned*sizeof(DRIVER_INFO_3));
+ memset(inf, 0, returned*sizeof(DRIVER_INFO_3));
+
+ prs_set_offset(&buffer->prs,0);
+
+ for (i=0; i<returned; i++) {
+ smb_io_printer_driver_info_3("", buffer, &(inf[i]), 0);
+ }
+
+ *info=inf;
+}
+
+/**********************************************************************
+**********************************************************************/
+static void decode_printerdriverdir_1 (TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+ uint32 returned, DRIVER_DIRECTORY_1 **info
+)
+{
+ DRIVER_DIRECTORY_1 *inf;
+
+ inf=(DRIVER_DIRECTORY_1 *)talloc(mem_ctx, sizeof(DRIVER_DIRECTORY_1));
+ memset(inf, 0, sizeof(DRIVER_DIRECTORY_1));
+
+ prs_set_offset(&buffer->prs, 0);
+
+ smb_io_driverdir_1("", buffer, inf, 0);
+
+ *info=inf;
+}
+
+/** Return a handle to the specified printer or print server.
+ *
+ * @param cli Pointer to client state structure which is open
+ * on the SPOOLSS pipe.
+ *
+ * @param mem_ctx Pointer to an initialised talloc context.
+ *
+ * @param printername The name of the printer or print server to be
+ * opened in UNC format.
+ *
+ * @param datatype Specifies the default data type for the printer.
+ *
+ * @param access_required The access rights requested on the printer or
+ * print server.
+ *
+ * @param station The UNC name of the requesting workstation.
+ *
+ * @param username The name of the user requesting the open.
+ *
+ * @param pol Returned policy handle.
+ */
+
+/*********************************************************************************
+ Win32 API - OpenPrinter()
+ ********************************************************************************/
+
+WERROR cli_spoolss_open_printer_ex(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const char *printername, const char *datatype, uint32 access_required,
+ const char *station, const char *username, POLICY_HND *pol)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_OPEN_PRINTER_EX q;
+ SPOOL_R_OPEN_PRINTER_EX r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_open_printer_ex(&q, printername, datatype,
+ access_required, station, username);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_open_printer_ex("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_OPENPRINTEREX, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_open_printer_ex("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (W_ERROR_IS_OK(result))
+ *pol = r.handle;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Close a printer handle
+ *
+ * @param cli Pointer to client state structure which is open
+ * on the SPOOLSS pipe.
+ *
+ * @param mem_ctx Pointer to an initialised talloc context.
+ *
+ * @param pol Policy handle of printer or print server to close.
+ */
+/*********************************************************************************
+ Win32 API - ClosePrinter()
+ ********************************************************************************/
+
+WERROR cli_spoolss_close_printer(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_CLOSEPRINTER q;
+ SPOOL_R_CLOSEPRINTER r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_closeprinter(&q, pol);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_closeprinter("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_CLOSEPRINTER, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_closeprinter("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (W_ERROR_IS_OK(result))
+ *pol = r.handle;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Enumerate printers on a print server.
+ *
+ * @param cli Pointer to client state structure which is open
+ * on the SPOOLSS pipe.
+ * @param mem_ctx Pointer to an initialised talloc context.
+ *
+ * @param offered Buffer size offered in the request.
+ * @param needed Number of bytes needed to complete the request.
+ * may be NULL.
+ *
+ * @param flags Selected from PRINTER_ENUM_* flags.
+ * @param level Request information level.
+ *
+ * @param num_printers Pointer to number of printers returned. May be
+ * NULL.
+ * @param ctr Return structure for printer information. May
+ * be NULL.
+ */
+/*********************************************************************************
+ Win32 API - EnumPrinters()
+ ********************************************************************************/
+
+WERROR cli_spoolss_enum_printers(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ char *name, uint32 flags, uint32 level,
+ uint32 *num_printers, PRINTER_INFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ENUMPRINTERS q;
+ SPOOL_R_ENUMPRINTERS r;
+ NEW_BUFFER buffer;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise input parameters */
+
+ init_buffer(&buffer, offered, mem_ctx);
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ make_spoolss_q_enumprinters(&q, flags, name, level, &buffer,
+ offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_enumprinters("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_ENUMPRINTERS, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (spoolss_io_r_enumprinters("", &r, &rbuf, 0)) {
+ if (needed)
+ *needed = r.needed;
+ }
+
+ result = r.status;
+
+ /* Return output parameters */
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ if (num_printers)
+ *num_printers = r.returned;
+
+ if (!ctr)
+ goto done;
+
+ switch (level) {
+ case 0:
+ decode_printer_info_0(mem_ctx, r.buffer, r.returned,
+ &ctr->printers_0);
+ break;
+ case 1:
+ decode_printer_info_1(mem_ctx, r.buffer, r.returned,
+ &ctr->printers_1);
+ break;
+ case 2:
+ decode_printer_info_2(mem_ctx, r.buffer, r.returned,
+ &ctr->printers_2);
+ break;
+ case 3:
+ decode_printer_info_3(mem_ctx, r.buffer, r.returned,
+ &ctr->printers_3);
+ break;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/*********************************************************************************
+ Win32 API - EnumPorts()
+ ********************************************************************************/
+/** Enumerate printer ports on a print server.
+ *
+ * @param cli Pointer to client state structure which is open
+ * on the SPOOLSS pipe.
+ * @param mem_ctx Pointer to an initialised talloc context.
+ *
+ * @param offered Buffer size offered in the request.
+ * @param needed Number of bytes needed to complete the request.
+ * May be NULL.
+ *
+ * @param level Requested information level.
+ *
+ * @param num_ports Pointer to number of ports returned. May be NULL.
+ * @param ctr Pointer to structure holding port information.
+ * May be NULL.
+ */
+
+WERROR cli_spoolss_enum_ports(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ uint32 level, uint32 *num_ports, PORT_INFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ENUMPORTS q;
+ SPOOL_R_ENUMPORTS r;
+ NEW_BUFFER buffer;
+ WERROR result = W_ERROR(ERRgeneral);
+ fstring server;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ slprintf(server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+ strupper_m(server);
+
+ /* Initialise input parameters */
+
+ init_buffer(&buffer, offered, mem_ctx);
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ make_spoolss_q_enumports(&q, server, level, &buffer, offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_enumports("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_ENUMPORTS, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (spoolss_io_r_enumports("", &r, &rbuf, 0)) {
+ if (needed)
+ *needed = r.needed;
+ }
+
+ result = r.status;
+
+ /* Return output parameters */
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ if (num_ports)
+ *num_ports = r.returned;
+
+ if (!ctr)
+ goto done;
+
+ switch (level) {
+ case 1:
+ decode_port_info_1(mem_ctx, r.buffer, r.returned,
+ &ctr->port.info_1);
+ break;
+ case 2:
+ decode_port_info_2(mem_ctx, r.buffer, r.returned,
+ &ctr->port.info_2);
+ break;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/*********************************************************************************
+ Win32 API - GetPrinter()
+ ********************************************************************************/
+
+WERROR cli_spoolss_getprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ POLICY_HND *pol, uint32 level,
+ PRINTER_INFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_GETPRINTER q;
+ SPOOL_R_GETPRINTER r;
+ NEW_BUFFER buffer;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise input parameters */
+
+ init_buffer(&buffer, offered, mem_ctx);
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ make_spoolss_q_getprinter(mem_ctx, &q, pol, level, &buffer, offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_getprinter("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_GETPRINTER, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_getprinter("", &r, &rbuf, 0))
+ goto done;
+
+ if (needed)
+ *needed = r.needed;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (W_ERROR_IS_OK(result)) {
+ switch (level) {
+ case 0:
+ decode_printer_info_0(mem_ctx, r.buffer, 1, &ctr->printers_0);
+ break;
+ case 1:
+ decode_printer_info_1(mem_ctx, r.buffer, 1, &ctr->printers_1);
+ break;
+ case 2:
+ decode_printer_info_2(mem_ctx, r.buffer, 1, &ctr->printers_2);
+ break;
+ case 3:
+ decode_printer_info_3(mem_ctx, r.buffer, 1, &ctr->printers_3);
+ break;
+ }
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/*********************************************************************************
+ Win32 API - SetPrinter()
+ ********************************************************************************/
+/** Set printer info
+ *
+ * @param cli Pointer to client state structure which is open
+ * on the SPOOLSS pipe.
+ * @param mem_ctx Pointer to an initialised talloc context.
+ *
+ * @param pol Policy handle on printer to set info.
+ * @param level Information level to set.
+ * @param ctr Pointer to structure holding printer information.
+ * @param command Specifies the action performed. See
+ * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/prntspol_13ua.asp
+ * for details.
+ *
+ */
+
+WERROR cli_spoolss_setprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint32 level,
+ PRINTER_INFO_CTR *ctr, uint32 command)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_SETPRINTER q;
+ SPOOL_R_SETPRINTER r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise input parameters */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ if (!make_spoolss_q_setprinter(mem_ctx, &q, pol, level, ctr, command))
+ goto done;
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_setprinter("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_SETPRINTER, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_setprinter("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/*********************************************************************************
+ Win32 API - GetPrinterDriver()
+ ********************************************************************************/
+/** Get installed printer drivers for a given printer
+ *
+ * @param cli Pointer to client state structure which is open
+ * on the SPOOLSS pipe.
+ *
+ * @param mem_ctx Pointer to an initialised talloc context.
+ *
+ * @param offered Buffer size offered in the request.
+ * @param needed Number of bytes needed to complete the request.
+ * may be NULL.
+ *
+ * @param pol Pointer to an open policy handle for the printer
+ * opened with cli_spoolss_open_printer_ex().
+ * @param level Requested information level.
+ * @param env The print environment or archictecture. This is
+ * "Windows NT x86" for NT4.
+ * @param ctr Returned printer driver information.
+ */
+
+WERROR cli_spoolss_getprinterdriver(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ POLICY_HND *pol, uint32 level,
+ const char *env, int version, PRINTER_DRIVER_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_GETPRINTERDRIVER2 q;
+ SPOOL_R_GETPRINTERDRIVER2 r;
+ NEW_BUFFER buffer;
+ WERROR result = W_ERROR(ERRgeneral);
+ fstring server;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ fstrcpy(server, cli->desthost);
+ strupper_m(server);
+
+ /* Initialise input parameters */
+
+ init_buffer(&buffer, offered, mem_ctx);
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ make_spoolss_q_getprinterdriver2(&q, pol, env, level, version, 2,
+ &buffer, offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_getprinterdriver2 ("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req (cli, SPOOLSS_GETPRINTERDRIVER2, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (spoolss_io_r_getprinterdriver2 ("", &r, &rbuf, 0)) {
+ if (needed)
+ *needed = r.needed;
+ }
+
+ result = r.status;
+
+ /* Return output parameters */
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ if (!ctr)
+ goto done;
+
+ switch (level) {
+ case 1:
+ decode_printer_driver_1(mem_ctx, r.buffer, 1, &ctr->info1);
+ break;
+ case 2:
+ decode_printer_driver_2(mem_ctx, r.buffer, 1, &ctr->info2);
+ break;
+ case 3:
+ decode_printer_driver_3(mem_ctx, r.buffer, 1, &ctr->info3);
+ break;
+ default:
+ DEBUG(10, ("cli_spoolss_getprinterdriver: unknown info level %d", level));
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/*********************************************************************************
+ Win32 API - EnumPrinterDrivers()
+ ********************************************************************************/
+/**********************************************************************
+ * Get installed printer drivers for a given printer
+ */
+WERROR cli_spoolss_enumprinterdrivers (struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ uint32 level, const char *env,
+ uint32 *num_drivers,
+ PRINTER_DRIVER_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ENUMPRINTERDRIVERS q;
+ SPOOL_R_ENUMPRINTERDRIVERS r;
+ NEW_BUFFER buffer;
+ WERROR result = W_ERROR(ERRgeneral);
+ fstring server;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ slprintf(server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+ strupper_m(server);
+
+ /* Initialise input parameters */
+
+ init_buffer(&buffer, offered, mem_ctx);
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Write the request */
+
+ make_spoolss_q_enumprinterdrivers(&q, server, env, level, &buffer,
+ offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_enumprinterdrivers ("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req (cli, SPOOLSS_ENUMPRINTERDRIVERS, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_enumprinterdrivers ("", &r, &rbuf, 0))
+ goto done;
+
+ if (needed)
+ *needed = r.needed;
+
+ if (num_drivers)
+ *num_drivers = r.returned;
+
+ result = r.status;
+
+ /* Return output parameters */
+
+ if (W_ERROR_IS_OK(result) && (r.returned != 0)) {
+ *num_drivers = r.returned;
+
+ switch (level) {
+ case 1:
+ decode_printer_driver_1(mem_ctx, r.buffer, r.returned, &ctr->info1);
+ break;
+ case 2:
+ decode_printer_driver_2(mem_ctx, r.buffer, r.returned, &ctr->info2);
+ break;
+ case 3:
+ decode_printer_driver_3(mem_ctx, r.buffer, r.returned, &ctr->info3);
+ break;
+ default:
+ DEBUG(10, ("cli_spoolss_enumprinterdrivers: unknown info level %d\n",
+ level));
+ return WERR_UNKNOWN_LEVEL;
+ }
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+
+/*********************************************************************************
+ Win32 API - GetPrinterDriverDirectory()
+ ********************************************************************************/
+/**********************************************************************
+ * Get installed printer drivers for a given printer
+ */
+WERROR cli_spoolss_getprinterdriverdir (struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ uint32 level, char *env,
+ DRIVER_DIRECTORY_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_GETPRINTERDRIVERDIR q;
+ SPOOL_R_GETPRINTERDRIVERDIR r;
+ NEW_BUFFER buffer;
+ WERROR result = W_ERROR(ERRgeneral);
+ fstring server;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ slprintf(server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+ strupper_m(server);
+
+ /* Initialise input parameters */
+
+ init_buffer(&buffer, offered, mem_ctx);
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Write the request */
+
+ make_spoolss_q_getprinterdriverdir(&q, server, env, level, &buffer,
+ offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_getprinterdriverdir ("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req (cli, SPOOLSS_GETPRINTERDRIVERDIRECTORY,
+ &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (spoolss_io_r_getprinterdriverdir ("", &r, &rbuf, 0)) {
+ if (needed)
+ *needed = r.needed;
+ }
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (W_ERROR_IS_OK(result)) {
+ switch (level) {
+ case 1:
+ decode_printerdriverdir_1(mem_ctx, r.buffer, 1,
+ &ctr->info1);
+ break;
+ }
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/*********************************************************************************
+ Win32 API - AddPrinterDriver()
+ ********************************************************************************/
+/**********************************************************************
+ * Install a printer driver
+ */
+WERROR cli_spoolss_addprinterdriver (struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, uint32 level,
+ PRINTER_DRIVER_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ADDPRINTERDRIVER q;
+ SPOOL_R_ADDPRINTERDRIVER r;
+ WERROR result = W_ERROR(ERRgeneral);
+ fstring server;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ slprintf(server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+ strupper_m(server);
+
+ /* Initialise input parameters */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Write the request */
+
+ make_spoolss_q_addprinterdriver (mem_ctx, &q, server, level, ctr);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_addprinterdriver ("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req (cli, SPOOLSS_ADDPRINTERDRIVER, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_addprinterdriver ("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/*********************************************************************************
+ Win32 API - AddPrinter()
+ ********************************************************************************/
+/**********************************************************************
+ * Install a printer
+ */
+WERROR cli_spoolss_addprinterex (struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 level, PRINTER_INFO_CTR*ctr)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ADDPRINTEREX q;
+ SPOOL_R_ADDPRINTEREX r;
+ WERROR result = W_ERROR(ERRgeneral);
+ fstring server,
+ client,
+ user;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ slprintf(client, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+ strupper_m(client);
+ slprintf(server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+ strupper_m(server);
+ fstrcpy (user, cli->user_name);
+
+ /* Initialise input parameters */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Write the request */
+
+ make_spoolss_q_addprinterex (mem_ctx, &q, server, client, user,
+ level, ctr);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_addprinterex ("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req (cli, SPOOLSS_ADDPRINTEREX, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_addprinterex ("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/*********************************************************************************
+ Win32 API - DeltePrinterDriver()
+ ********************************************************************************/
+/**********************************************************************
+ * Delete a Printer Driver from the server (does not remove
+ * the driver files
+ */
+WERROR cli_spoolss_deleteprinterdriver (struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, const char *arch,
+ const char *driver)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_DELETEPRINTERDRIVER q;
+ SPOOL_R_DELETEPRINTERDRIVER r;
+ WERROR result = W_ERROR(ERRgeneral);
+ fstring server;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+
+ /* Initialise input parameters */
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ slprintf(server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+ strupper_m(server);
+
+ /* Write the request */
+
+ make_spoolss_q_deleteprinterdriver(mem_ctx, &q, server, arch, driver);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_deleteprinterdriver ("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req (cli,SPOOLSS_DELETEPRINTERDRIVER , &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_deleteprinterdriver ("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/*********************************************************************************
+ Win32 API - GetPrinterProcessorDirectory()
+ ********************************************************************************/
+
+WERROR cli_spoolss_getprintprocessordirectory(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ char *name, char *environment,
+ fstring procdir)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_GETPRINTPROCESSORDIRECTORY q;
+ SPOOL_R_GETPRINTPROCESSORDIRECTORY r;
+ int level = 1;
+ WERROR result = W_ERROR(ERRgeneral);
+ NEW_BUFFER buffer;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ init_buffer(&buffer, offered, mem_ctx);
+
+ make_spoolss_q_getprintprocessordirectory(
+ &q, name, environment, level, &buffer, offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_getprintprocessordirectory("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_GETPRINTPROCESSORDIRECTORY,
+ &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_getprintprocessordirectory("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (needed)
+ *needed = r.needed;
+
+ if (W_ERROR_IS_OK(result))
+ fstrcpy(procdir, "Not implemented!");
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Add a form to a printer.
+ *
+ * @param cli Pointer to client state structure which is open
+ * on the SPOOLSS pipe.
+ * @param mem_ctx Pointer to an initialised talloc context.
+ *
+ * @param handle Policy handle opened with cli_spoolss_open_printer_ex
+ * or cli_spoolss_addprinterex.
+ * @param level Form info level to add - should always be 1.
+ * @param form A pointer to the form to be added.
+ *
+ */
+
+WERROR cli_spoolss_addform(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *handle, uint32 level, FORM *form)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ADDFORM q;
+ SPOOL_R_ADDFORM r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_addform(&q, handle, level, form);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_addform("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_ADDFORM, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_addform("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Set a form on a printer.
+ *
+ * @param cli Pointer to client state structure which is open
+ * on the SPOOLSS pipe.
+ * @param mem_ctx Pointer to an initialised talloc context.
+ *
+ * @param handle Policy handle opened with cli_spoolss_open_printer_ex
+ * or cli_spoolss_addprinterex.
+ * @param level Form info level to set - should always be 1.
+ * @param form A pointer to the form to be set.
+ *
+ */
+
+WERROR cli_spoolss_setform(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *handle, uint32 level,
+ const char *form_name, FORM *form)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_SETFORM q;
+ SPOOL_R_SETFORM r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_setform(&q, handle, level, form_name, form);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_setform("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_SETFORM, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_setform("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Get a form on a printer.
+ *
+ * @param cli Pointer to client state structure which is open
+ * on the SPOOLSS pipe.
+ * @param mem_ctx Pointer to an initialised talloc context.
+ *
+ * @param handle Policy handle opened with cli_spoolss_open_printer_ex
+ * or cli_spoolss_addprinterex.
+ * @param formname Name of the form to get
+ * @param level Form info level to get - should always be 1.
+ *
+ */
+
+WERROR cli_spoolss_getform(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ POLICY_HND *handle, const char *formname,
+ uint32 level, FORM_1 *form)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_GETFORM q;
+ SPOOL_R_GETFORM r;
+ WERROR result = W_ERROR(ERRgeneral);
+ NEW_BUFFER buffer;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ init_buffer(&buffer, offered, mem_ctx);
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_getform(&q, handle, formname, level, &buffer, offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_getform("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_GETFORM, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_getform("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (needed)
+ *needed = r.needed;
+
+ if (W_ERROR_IS_OK(result)) {
+ switch(level) {
+ case 1:
+ smb_io_form_1("", r.buffer, form, 0);
+ break;
+ default:
+ DEBUG(10, ("cli_spoolss_getform: unknown info level %d", level));
+ return WERR_UNKNOWN_LEVEL;
+ }
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Delete a form on a printer.
+ *
+ * @param cli Pointer to client state structure which is open
+ * on the SPOOLSS pipe.
+ * @param mem_ctx Pointer to an initialised talloc context.
+ *
+ * @param handle Policy handle opened with cli_spoolss_open_printer_ex
+ * or cli_spoolss_addprinterex.
+ * @param form The name of the form to delete.
+ *
+ */
+
+WERROR cli_spoolss_deleteform(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *handle, const char *form_name)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_DELETEFORM q;
+ SPOOL_R_DELETEFORM r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_deleteform(&q, handle, form_name);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_deleteform("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_DELETEFORM, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_deleteform("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+static void decode_forms_1(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+ uint32 num_forms, FORM_1 **forms)
+{
+ int i;
+
+ *forms = (FORM_1 *)talloc(mem_ctx, num_forms * sizeof(FORM_1));
+ prs_set_offset(&buffer->prs,0);
+
+ for (i = 0; i < num_forms; i++)
+ smb_io_form_1("", buffer, &((*forms)[i]), 0);
+}
+
+/** Enumerate forms
+ *
+ * @param cli Pointer to client state structure which is open
+ * on the SPOOLSS pipe.
+ * @param mem_ctx Pointer to an initialised talloc context.
+ *
+ * @param offered Buffer size offered in the request.
+ * @param needed Number of bytes needed to complete the request.
+ * may be NULL.
+ * or cli_spoolss_addprinterex.
+ * @param level Form info level to get - should always be 1.
+ * @param handle Open policy handle
+ *
+ */
+
+WERROR cli_spoolss_enumforms(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ POLICY_HND *handle, int level, uint32 *num_forms,
+ FORM_1 **forms)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ENUMFORMS q;
+ SPOOL_R_ENUMFORMS r;
+ WERROR result = W_ERROR(ERRgeneral);
+ NEW_BUFFER buffer;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ init_buffer(&buffer, offered, mem_ctx);
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_enumforms(&q, handle, level, &buffer, offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_enumforms("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_ENUMFORMS, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_enumforms("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (needed)
+ *needed = r.needed;
+
+ if (num_forms)
+ *num_forms = r.numofforms;
+
+ decode_forms_1(mem_ctx, r.buffer, *num_forms, forms);
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+static void decode_jobs_1(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+ uint32 num_jobs, JOB_INFO_1 **jobs)
+{
+ uint32 i;
+
+ *jobs = (JOB_INFO_1 *)talloc(mem_ctx, num_jobs * sizeof(JOB_INFO_1));
+ prs_set_offset(&buffer->prs,0);
+
+ for (i = 0; i < num_jobs; i++)
+ smb_io_job_info_1("", buffer, &((*jobs)[i]), 0);
+}
+
+static void decode_jobs_2(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+ uint32 num_jobs, JOB_INFO_2 **jobs)
+{
+ uint32 i;
+
+ *jobs = (JOB_INFO_2 *)talloc(mem_ctx, num_jobs * sizeof(JOB_INFO_2));
+ prs_set_offset(&buffer->prs,0);
+
+ for (i = 0; i < num_jobs; i++)
+ smb_io_job_info_2("", buffer, &((*jobs)[i]), 0);
+}
+
+/* Enumerate jobs */
+
+WERROR cli_spoolss_enumjobs(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ POLICY_HND *hnd, uint32 level, uint32 firstjob,
+ uint32 num_jobs, uint32 *returned, JOB_INFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ENUMJOBS q;
+ SPOOL_R_ENUMJOBS r;
+ WERROR result = W_ERROR(ERRgeneral);
+ NEW_BUFFER buffer;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ init_buffer(&buffer, offered, mem_ctx);
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_enumjobs(&q, hnd, firstjob, num_jobs, level, &buffer,
+ offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_enumjobs("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_ENUMJOBS, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_enumjobs("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (needed)
+ *needed = r.needed;
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ *returned = r.returned;
+
+ switch(level) {
+ case 1:
+ decode_jobs_1(mem_ctx, r.buffer, r.returned,
+ &ctr->job.job_info_1);
+ break;
+ case 2:
+ decode_jobs_2(mem_ctx, r.buffer, r.returned,
+ &ctr->job.job_info_2);
+ break;
+ default:
+ DEBUG(3, ("unsupported info level %d", level));
+ break;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Set job */
+
+WERROR cli_spoolss_setjob(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd, uint32 jobid, uint32 level,
+ uint32 command)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_SETJOB q;
+ SPOOL_R_SETJOB r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_setjob(&q, hnd, jobid, level, command);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_setjob("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_SETJOB, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_setjob("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Get job */
+
+WERROR cli_spoolss_getjob(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ POLICY_HND *hnd, uint32 jobid, uint32 level,
+ JOB_INFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_GETJOB q;
+ SPOOL_R_GETJOB r;
+ WERROR result = W_ERROR(ERRgeneral);
+ NEW_BUFFER buffer;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ init_buffer(&buffer, offered, mem_ctx);
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_getjob(&q, hnd, jobid, level, &buffer, offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_getjob("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_GETJOB, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_getjob("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (needed)
+ *needed = r.needed;
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ switch(level) {
+ case 1:
+ decode_jobs_1(mem_ctx, r.buffer, 1, &ctr->job.job_info_1);
+ break;
+ case 2:
+ decode_jobs_2(mem_ctx, r.buffer, 1, &ctr->job.job_info_2);
+ break;
+ default:
+ DEBUG(3, ("unsupported info level %d", level));
+ break;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Startpageprinter. Sent to notify the spooler when a page is about to be
+ sent to a printer. */
+
+WERROR cli_spoolss_startpageprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_STARTPAGEPRINTER q;
+ SPOOL_R_STARTPAGEPRINTER r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_startpageprinter(&q, hnd);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_startpageprinter("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_STARTPAGEPRINTER, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_startpageprinter("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Endpageprinter. Sent to notify the spooler when a page has finished
+ being sent to a printer. */
+
+WERROR cli_spoolss_endpageprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ENDPAGEPRINTER q;
+ SPOOL_R_ENDPAGEPRINTER r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_endpageprinter(&q, hnd);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_endpageprinter("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_ENDPAGEPRINTER, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_endpageprinter("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Startdocprinter. Sent to notify the spooler that a document is about
+ to be spooled for printing. */
+
+WERROR cli_spoolss_startdocprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd, char *docname,
+ char *outputfile, char *datatype,
+ uint32 *jobid)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_STARTDOCPRINTER q;
+ SPOOL_R_STARTDOCPRINTER r;
+ WERROR result = W_ERROR(ERRgeneral);
+ uint32 level = 1;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_startdocprinter(&q, hnd, level, docname, outputfile,
+ datatype);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_startdocprinter("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_STARTDOCPRINTER, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_startdocprinter("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (W_ERROR_IS_OK(result))
+ *jobid = r.jobid;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Enddocprinter. Sent to notify the spooler that a document has finished
+ being spooled. */
+
+WERROR cli_spoolss_enddocprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ENDDOCPRINTER q;
+ SPOOL_R_ENDDOCPRINTER r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_enddocprinter(&q, hnd);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_enddocprinter("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_ENDDOCPRINTER, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_enddocprinter("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Get printer data */
+
+WERROR cli_spoolss_getprinterdata(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ POLICY_HND *hnd, const char *valuename,
+ REGISTRY_VALUE *value)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_GETPRINTERDATA q;
+ SPOOL_R_GETPRINTERDATA r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_getprinterdata(&q, hnd, valuename, offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_getprinterdata("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_GETPRINTERDATA, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_getprinterdata("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (needed)
+ *needed = r.needed;
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ /* Return output parameters */
+
+ value->data_p = talloc_memdup(mem_ctx, r.data, r.needed);
+ value->type = r.type;
+ value->size = r.size;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+WERROR cli_spoolss_getprinterdataex(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ POLICY_HND *hnd, const char *keyname,
+ const char *valuename,
+ REGISTRY_VALUE *value)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_GETPRINTERDATAEX q;
+ SPOOL_R_GETPRINTERDATAEX r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_getprinterdataex(&q, hnd, keyname, valuename, offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_getprinterdataex("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_GETPRINTERDATAEX, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_getprinterdataex("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (needed)
+ *needed = r.needed;
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ /* Return output parameters */
+
+ value->data_p = talloc_memdup(mem_ctx, r.data, r.needed);
+ value->type = r.type;
+ value->size = r.needed;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Set printer data */
+
+WERROR cli_spoolss_setprinterdata(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd, REGISTRY_VALUE *value)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_SETPRINTERDATA q;
+ SPOOL_R_SETPRINTERDATA r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_setprinterdata(
+ &q, hnd, value->valuename, value->type, (char *)value->data_p, value->size);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_setprinterdata("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_SETPRINTERDATA, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_setprinterdata("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+WERROR cli_spoolss_setprinterdataex(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd, char *keyname,
+ REGISTRY_VALUE *value)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_SETPRINTERDATAEX q;
+ SPOOL_R_SETPRINTERDATAEX r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_setprinterdataex(
+ &q, hnd, keyname, value->valuename, value->type, (char *)value->data_p,
+ value->size);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_setprinterdataex("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_SETPRINTERDATAEX, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_setprinterdataex("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Enum printer data */
+
+WERROR cli_spoolss_enumprinterdata(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd, uint32 ndx,
+ uint32 value_offered, uint32 data_offered,
+ uint32 *value_needed, uint32 *data_needed,
+ REGISTRY_VALUE *value)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ENUMPRINTERDATA q;
+ SPOOL_R_ENUMPRINTERDATA r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_enumprinterdata(&q, hnd, ndx, value_offered, data_offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_enumprinterdata("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_ENUMPRINTERDATA, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_enumprinterdata("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ /* Return data */
+
+ if (value_needed)
+ *value_needed = r.realvaluesize;
+
+ if (data_needed)
+ *data_needed = r.realdatasize;
+
+ if (value) {
+ rpcstr_pull(value->valuename, r.value, sizeof(value->valuename), -1,
+ STR_TERMINATE);
+ value->data_p = talloc_memdup(mem_ctx, r.data, r.realdatasize);
+ value->type = r.type;
+ value->size = r.realdatasize;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+WERROR cli_spoolss_enumprinterdataex(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ POLICY_HND *hnd, const char *keyname,
+ REGVAL_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ENUMPRINTERDATAEX q;
+ SPOOL_R_ENUMPRINTERDATAEX r;
+ WERROR result = W_ERROR(ERRgeneral);
+ int i;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_enumprinterdataex(&q, hnd, keyname, offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_enumprinterdataex("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_ENUMPRINTERDATAEX, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_enumprinterdataex("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (needed)
+ *needed = r.needed;
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ /* Return data */
+
+ ZERO_STRUCTP(ctr);
+ regval_ctr_init(ctr);
+
+ for (i = 0; i < r.returned; i++) {
+ PRINTER_ENUM_VALUES *v = &r.ctr.values[i];
+ fstring name;
+
+ rpcstr_pull(name, v->valuename.buffer, sizeof(name), -1,
+ STR_TERMINATE);
+ regval_ctr_addvalue(ctr, name, v->type, (const char *)v->data, v->data_len);
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Write data to printer */
+
+WERROR cli_spoolss_writeprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd, uint32 data_size, char *data,
+ uint32 *num_written)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_WRITEPRINTER q;
+ SPOOL_R_WRITEPRINTER r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_writeprinter(&q, hnd, data_size, data);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_writeprinter("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_WRITEPRINTER, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_writeprinter("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ if (num_written)
+ *num_written = r.buffer_written;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Delete printer data */
+
+WERROR cli_spoolss_deleteprinterdata(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd, char *valuename)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_DELETEPRINTERDATA q;
+ SPOOL_R_DELETEPRINTERDATA r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_deleteprinterdata(&q, hnd, valuename);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_deleteprinterdata("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_DELETEPRINTERDATA, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_deleteprinterdata("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+WERROR cli_spoolss_deleteprinterdataex(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd, char *keyname,
+ char *valuename)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_DELETEPRINTERDATAEX q;
+ SPOOL_R_DELETEPRINTERDATAEX r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_deleteprinterdataex(&q, hnd, keyname, valuename);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_deleteprinterdataex("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_DELETEPRINTERDATAEX, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_deleteprinterdataex("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+WERROR cli_spoolss_enumprinterkey(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ POLICY_HND *hnd, const char *keyname,
+ uint16 **keylist, uint32 *len)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ENUMPRINTERKEY q;
+ SPOOL_R_ENUMPRINTERKEY r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_enumprinterkey(&q, hnd, keyname, offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_enumprinterkey("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_ENUMPRINTERKEY, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_enumprinterkey("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (needed)
+ *needed = r.needed;
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ /* Copy results */
+
+ if (keylist) {
+ *keylist = (uint16 *)malloc(r.keys.buf_len * 2);
+ memcpy(*keylist, r.keys.buffer, r.keys.buf_len * 2);
+ if (len)
+ *len = r.keys.buf_len * 2;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+WERROR cli_spoolss_deleteprinterkey(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd, char *keyname)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_DELETEPRINTERKEY q;
+ SPOOL_R_DELETEPRINTERKEY r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_deleteprinterkey(&q, hnd, keyname);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_deleteprinterkey("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_DELETEPRINTERKEY, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_deleteprinterkey("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** @} **/
diff --git a/source/rpc_client/cli_spoolss_notify.c b/source/rpc_client/cli_spoolss_notify.c
new file mode 100644
index 00000000000..f4eda332bb1
--- /dev/null
+++ b/source/rpc_client/cli_spoolss_notify.c
@@ -0,0 +1,272 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ Copyright (C) Gerald Carter 2001-2002,
+ Copyright (C) Tim Potter 2000-2002,
+ Copyright (C) Andrew Tridgell 1994-2000,
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+ Copyright (C) Jean-Francois Micouleau 1999-2000.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*
+ * SPOOLSS Client RPC's used by servers as the notification
+ * back channel.
+ */
+
+/* Send a ReplyOpenPrinter request. This rpc is made by the printer
+ server to the printer client in response to a rffpcnex request.
+ The rrfpcnex request names a printer and a handle (the printerlocal
+ value) and this rpc establishes a back-channel over which printer
+ notifications are performed. */
+
+WERROR cli_spoolss_reply_open_printer(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const char *printer, uint32 printerlocal, uint32 type,
+ POLICY_HND *handle)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_REPLYOPENPRINTER q;
+ SPOOL_R_REPLYOPENPRINTER r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ /* Initialise input parameters */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ make_spoolss_q_replyopenprinter(&q, printer, printerlocal, type);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_replyopenprinter("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req (cli, SPOOLSS_REPLYOPENPRINTER, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_replyopenprinter("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return result */
+
+ memcpy(handle, &r.handle, sizeof(r.handle));
+ result = r.status;
+
+done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Close a back-channel notification connection */
+
+WERROR cli_spoolss_reply_close_printer(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *handle)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_REPLYCLOSEPRINTER q;
+ SPOOL_R_REPLYCLOSEPRINTER r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ /* Initialise input parameters */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ make_spoolss_q_reply_closeprinter(&q, handle);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_replycloseprinter("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req (cli, SPOOLSS_REPLYCLOSEPRINTER, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_replycloseprinter("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return result */
+
+ result = r.status;
+
+done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/*********************************************************************
+ This SPOOLSS_ROUTERREPLYPRINTER function is used to send a change
+ notification event when the registration **did not** use
+ SPOOL_NOTIFY_OPTION_TYPE structure to specify the events to monitor.
+ Also see cli_spolss_reply_rrpcn()
+ *********************************************************************/
+
+WERROR cli_spoolss_routerreplyprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint32 condition, uint32 change_id)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ROUTERREPLYPRINTER q;
+ SPOOL_R_ROUTERREPLYPRINTER r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ /* Initialise input parameters */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ make_spoolss_q_routerreplyprinter(&q, pol, condition, change_id);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_routerreplyprinter("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req (cli, SPOOLSS_ROUTERREPLYPRINTER, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_routerreplyprinter("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/*********************************************************************
+ This SPOOLSS_REPLY_RRPCN function is used to send a change
+ notification event when the registration **did** use
+ SPOOL_NOTIFY_OPTION_TYPE structure to specify the events to monitor
+ Also see cli_spoolss_routereplyprinter()
+ *********************************************************************/
+
+WERROR cli_spoolss_rrpcn(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint32 notify_data_len,
+ SPOOL_NOTIFY_INFO_DATA *notify_data,
+ uint32 change_low, uint32 change_high)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_REPLY_RRPCN q;
+ SPOOL_R_REPLY_RRPCN r;
+ WERROR result = W_ERROR(ERRgeneral);
+ SPOOL_NOTIFY_INFO notify_info;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ ZERO_STRUCT(notify_info);
+
+ /* Initialise input parameters */
+
+ notify_info.version = 0x2;
+ notify_info.flags = 0x00020000; /* ?? */
+ notify_info.count = notify_data_len;
+ notify_info.data = notify_data;
+
+ /* create and send a MSRPC command with api */
+ /* store the parameters */
+
+ make_spoolss_q_reply_rrpcn(&q, pol, change_low, change_high,
+ &notify_info);
+
+ /* Marshall data and send request */
+
+ if(!spoolss_io_q_reply_rrpcn("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_RRPCN, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if(!spoolss_io_r_reply_rrpcn("", &r, &rbuf, 0))
+ goto done;
+
+ if (r.unknown0 == 0x00080000)
+ DEBUG(8,("cli_spoolss_reply_rrpcn: I think the spooler resonded that the notification was ignored.\n"));
+ else if ( r.unknown0 != 0x0 )
+ DEBUG(8,("cli_spoolss_reply_rrpcn: unknown0 is non-zero [0x%x]\n", r.unknown0));
+
+ result = r.status;
+
+done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/*********************************************************************
+ *********************************************************************/
+
+WERROR cli_spoolss_rffpcnex(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint32 flags, uint32 options,
+ const char *localmachine, uint32 printerlocal,
+ SPOOL_NOTIFY_OPTION *option)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_RFFPCNEX q;
+ SPOOL_R_RFFPCNEX r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_rffpcnex(
+ &q, pol, flags, options, localmachine, printerlocal,
+ option);
+
+ /* Marshall data and send request */
+
+ if(!spoolss_io_q_rffpcnex("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_RFFPCNEX, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if(!spoolss_io_r_rffpcnex("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
diff --git a/source/rpc_client/cli_srvsvc.c b/source/rpc_client/cli_srvsvc.c
new file mode 100644
index 00000000000..555703cf4d8
--- /dev/null
+++ b/source/rpc_client/cli_srvsvc.c
@@ -0,0 +1,442 @@
+/*
+ Unix SMB/CIFS implementation.
+ NT Domain Authentication SMB / MSRPC client
+ Copyright (C) Andrew Tridgell 1994-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Tim Potter 2001
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+WERROR cli_srvsvc_net_srv_get_info(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ uint32 switch_value, SRV_INFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SRV_Q_NET_SRV_GET_INFO q;
+ SRV_R_NET_SRV_GET_INFO r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ init_srv_q_net_srv_get_info(&q, cli->srv_name_slash, switch_value);
+
+ /* Marshall data and send request */
+
+ if (!srv_io_q_net_srv_get_info("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SRV_NET_SRV_GET_INFO, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ r.ctr = ctr;
+
+ if (!srv_io_r_net_srv_get_info("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+WERROR cli_srvsvc_net_share_enum(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 info_level, SRV_SHARE_INFO_CTR *ctr,
+ int preferred_len, ENUM_HND *hnd)
+{
+ prs_struct qbuf, rbuf;
+ SRV_Q_NET_SHARE_ENUM q;
+ SRV_R_NET_SHARE_ENUM r;
+ WERROR result = W_ERROR(ERRgeneral);
+ int i;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ init_srv_q_net_share_enum(
+ &q, cli->srv_name_slash, info_level, preferred_len, hnd);
+
+ /* Marshall data and send request */
+
+ if (!srv_io_q_net_share_enum("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SRV_NET_SHARE_ENUM_ALL, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!srv_io_r_net_share_enum("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ /* Oh yuck yuck yuck - we have to copy all the info out of the
+ SRV_SHARE_INFO_CTR in the SRV_R_NET_SHARE_ENUM as when we do a
+ prs_mem_free() it will all be invalidated. The various share
+ info structures suck badly too. This really is gross. */
+
+ ZERO_STRUCTP(ctr);
+
+ if (!r.ctr.num_entries)
+ goto done;
+
+ ctr->info_level = info_level;
+ ctr->num_entries = r.ctr.num_entries;
+
+ switch(info_level) {
+ case 1:
+ ctr->share.info1 = (SRV_SHARE_INFO_1 *)talloc(
+ mem_ctx, sizeof(SRV_SHARE_INFO_1) * ctr->num_entries);
+
+ memset(ctr->share.info1, 0, sizeof(SRV_SHARE_INFO_1));
+
+ for (i = 0; i < ctr->num_entries; i++) {
+ SRV_SHARE_INFO_1 *info1 = &ctr->share.info1[i];
+ char *s;
+
+ /* Copy pointer crap */
+
+ memcpy(&info1->info_1, &r.ctr.share.info1[i].info_1,
+ sizeof(SH_INFO_1));
+
+ /* Duplicate strings */
+
+ s = unistr2_tdup(mem_ctx, &r.ctr.share.info1[i].info_1_str.uni_netname);
+ if (s)
+ init_unistr2(&info1->info_1_str.uni_netname, s, UNI_STR_TERMINATE);
+
+ s = unistr2_tdup(mem_ctx, &r.ctr.share.info1[i].info_1_str.uni_remark);
+ if (s)
+ init_unistr2(&info1->info_1_str.uni_remark, s, UNI_STR_TERMINATE);
+
+ }
+
+ break;
+ case 2:
+ ctr->share.info2 = (SRV_SHARE_INFO_2 *)talloc(
+ mem_ctx, sizeof(SRV_SHARE_INFO_2) * ctr->num_entries);
+
+ memset(ctr->share.info2, 0, sizeof(SRV_SHARE_INFO_2));
+
+ for (i = 0; i < ctr->num_entries; i++) {
+ SRV_SHARE_INFO_2 *info2 = &ctr->share.info2[i];
+ char *s;
+
+ /* Copy pointer crap */
+
+ memcpy(&info2->info_2, &r.ctr.share.info2[i].info_2,
+ sizeof(SH_INFO_2));
+
+ /* Duplicate strings */
+
+ s = unistr2_tdup(mem_ctx, &r.ctr.share.info2[i].info_2_str.uni_netname);
+ if (s)
+ init_unistr2(&info2->info_2_str.uni_netname, s, UNI_STR_TERMINATE);
+
+ s = unistr2_tdup(mem_ctx, &r.ctr.share.info2[i].info_2_str.uni_remark);
+ if (s)
+ init_unistr2(&info2->info_2_str.uni_remark, s, UNI_STR_TERMINATE);
+
+ s = unistr2_tdup(mem_ctx, &r.ctr.share.info2[i].info_2_str.uni_path);
+ if (s)
+ init_unistr2(&info2->info_2_str.uni_path, s, UNI_STR_TERMINATE);
+
+ s = unistr2_tdup(mem_ctx, &r.ctr.share.info2[i].info_2_str.uni_passwd);
+ if (s)
+ init_unistr2(&info2->info_2_str.uni_passwd, s, UNI_STR_TERMINATE);
+ }
+ break;
+ }
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+WERROR cli_srvsvc_net_share_del(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const char *sharename)
+{
+ prs_struct qbuf, rbuf;
+ SRV_Q_NET_SHARE_DEL q;
+ SRV_R_NET_SHARE_DEL r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ init_srv_q_net_share_del(&q, cli->srv_name_slash, sharename);
+
+ /* Marshall data and send request */
+
+ if (!srv_io_q_net_share_del("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SRV_NET_SHARE_DEL, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!srv_io_r_net_share_del("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+WERROR cli_srvsvc_net_share_add(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const char *netname, uint32 type,
+ const char *remark, uint32 perms,
+ uint32 max_uses, uint32 num_uses,
+ const char *path, const char *passwd)
+{
+ prs_struct qbuf, rbuf;
+ SRV_Q_NET_SHARE_ADD q;
+ SRV_R_NET_SHARE_ADD r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ init_srv_q_net_share_add(&q,cli->srv_name_slash, netname, type, remark,
+ perms, max_uses, num_uses, path, passwd);
+
+ /* Marshall data and send request */
+
+ if (!srv_io_q_net_share_add("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SRV_NET_SHARE_ADD, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!srv_io_r_net_share_add("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+WERROR cli_srvsvc_net_remote_tod(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ char *server, TIME_OF_DAY_INFO *tod)
+{
+ prs_struct qbuf, rbuf;
+ SRV_Q_NET_REMOTE_TOD q;
+ SRV_R_NET_REMOTE_TOD r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ init_srv_q_net_remote_tod(&q, cli->srv_name_slash);
+
+ /* Marshall data and send request */
+
+ if (!srv_io_q_net_remote_tod("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SRV_NET_REMOTE_TOD, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ r.tod = tod;
+
+ if (!srv_io_r_net_remote_tod("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+WERROR cli_srvsvc_net_file_enum(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 file_level, const char *user_name,
+ SRV_FILE_INFO_CTR *ctr, int preferred_len,
+ ENUM_HND *hnd)
+{
+ prs_struct qbuf, rbuf;
+ SRV_Q_NET_FILE_ENUM q;
+ SRV_R_NET_FILE_ENUM r;
+ WERROR result = W_ERROR(ERRgeneral);
+ int i;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ init_srv_q_net_file_enum(&q, cli->srv_name_slash, NULL, user_name,
+ file_level, ctr, preferred_len, hnd);
+
+ /* Marshall data and send request */
+
+ if (!srv_io_q_net_file_enum("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SRV_NET_FILE_ENUM, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!srv_io_r_net_file_enum("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ /* copy the data over to the ctr */
+
+ ZERO_STRUCTP(ctr);
+
+ ctr->switch_value = file_level;
+
+ ctr->num_entries = ctr->num_entries2 = r.ctr.num_entries;
+
+ switch(file_level) {
+ case 3:
+ ctr->file.info3 = (SRV_FILE_INFO_3 *)talloc(
+ mem_ctx, sizeof(SRV_FILE_INFO_3) * ctr->num_entries);
+
+ memset(ctr->file.info3, 0,
+ sizeof(SRV_FILE_INFO_3) * ctr->num_entries);
+
+ for (i = 0; i < r.ctr.num_entries; i++) {
+ SRV_FILE_INFO_3 *info3 = &ctr->file.info3[i];
+ char *s;
+
+ /* Copy pointer crap */
+
+ memcpy(&info3->info_3, &r.ctr.file.info3[i].info_3,
+ sizeof(FILE_INFO_3));
+
+ /* Duplicate strings */
+
+ s = unistr2_tdup(mem_ctx, &r.ctr.file.info3[i].info_3_str.uni_path_name);
+ if (s)
+ init_unistr2(&info3->info_3_str.uni_path_name, s, UNI_STR_TERMINATE);
+
+ s = unistr2_tdup(mem_ctx, &r.ctr.file.info3[i].info_3_str.uni_user_name);
+ if (s)
+ init_unistr2(&info3->info_3_str.uni_user_name, s, UNI_STR_TERMINATE);
+
+ }
+
+ break;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+WERROR cli_srvsvc_net_file_close(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 file_id)
+{
+ prs_struct qbuf, rbuf;
+ SRV_Q_NET_FILE_CLOSE q;
+ SRV_R_NET_FILE_CLOSE r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ init_srv_q_net_file_close(&q, cli->srv_name_slash, file_id);
+
+ /* Marshall data and send request */
+
+ if (!srv_io_q_net_file_close("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SRV_NET_FILE_CLOSE, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!srv_io_r_net_file_close("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+ return result;
+}
diff --git a/source/rpc_client/cli_wkssvc.c b/source/rpc_client/cli_wkssvc.c
new file mode 100644
index 00000000000..97b948bf628
--- /dev/null
+++ b/source/rpc_client/cli_wkssvc.c
@@ -0,0 +1,93 @@
+/*
+ Unix SMB/CIFS implementation.
+ NT Domain Authentication SMB / MSRPC client
+ Copyright (C) Andrew Tridgell 1994-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Tim Potter 2001
+ Copytight (C) Rafal Szczesniak 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/**
+ * WksQueryInfo rpc call (like query for server's capabilities)
+ *
+ * @param initialised client structure with \PIPE\wkssvc opened
+ * @param mem_ctx memory context assigned to this rpc binding
+ * @param wks100 WksQueryInfo structure
+ *
+ * @return NTSTATUS of rpc call
+ */
+
+NTSTATUS cli_wks_query_info(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ WKS_INFO_100 *wks100)
+{
+ prs_struct buf;
+ prs_struct rbuf;
+ WKS_Q_QUERY_INFO q_o;
+ WKS_R_QUERY_INFO r_o;
+
+ if (cli == NULL || wks100 == NULL)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ /* init rpc parse structures */
+ prs_init(&buf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ DEBUG(4, ("WksQueryInfo\n"));
+
+ /* init query structure with rpc call arguments */
+ init_wks_q_query_info(&q_o, cli->desthost, 100);
+
+ /* marshall data */
+ if (!wks_io_q_query_info("", &q_o, &buf, 0)) {
+ prs_mem_free(&buf);
+ prs_mem_free(&rbuf);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* actual rpc call over \PIPE\wkssvc */
+ if (!rpc_api_pipe_req(cli, WKS_QUERY_INFO, &buf, &rbuf)) {
+ prs_mem_free(&buf);
+ prs_mem_free(&rbuf);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ prs_mem_free(&buf);
+
+ r_o.wks100 = wks100;
+
+ /* get call results from response buffer */
+ if (!wks_io_r_query_info("", &r_o, &rbuf, 0)) {
+ prs_mem_free(&rbuf);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* check returnet status code */
+ if (NT_STATUS_IS_ERR(r_o.status)) {
+ /* report the error */
+ DEBUG(0,("WKS_R_QUERY_INFO: %s\n", nt_errstr(r_o.status)));
+ prs_mem_free(&rbuf);
+ return r_o.status;
+ }
+
+ /* do clean up */
+ prs_mem_free(&rbuf);
+
+ return NT_STATUS_OK;
+}
+
diff --git a/source/rpc_parse/.cvsignore b/source/rpc_parse/.cvsignore
new file mode 100644
index 00000000000..5f2a5c4cf75
--- /dev/null
+++ b/source/rpc_parse/.cvsignore
@@ -0,0 +1,2 @@
+*.po
+*.po32
diff --git a/source/rpc_parse/parse_dfs.c b/source/rpc_parse/parse_dfs.c
new file mode 100644
index 00000000000..0d0ce557b22
--- /dev/null
+++ b/source/rpc_parse/parse_dfs.c
@@ -0,0 +1,546 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * MSDfs RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-2000,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+ * Copyright (C) Shirish Kalele 2000.
+ * Copyright (C) Jeremy Allison 2001.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+#include "nterr.h"
+#include "rpc_parse.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+Make a DFS_Q_DFS_QUERY structure
+*******************************************************************/
+
+void init_dfs_q_dfs_exist(DFS_Q_DFS_EXIST *q_d)
+{
+ q_d->dummy = 0;
+}
+
+/*************************************************************
+ Read/write a DFS_Q_DFS_EXIST structure - dummy...
+ ************************************************************/
+
+BOOL dfs_io_q_dfs_exist(const char *desc, DFS_Q_DFS_EXIST *q_d, prs_struct *ps, int depth)
+{
+ if(q_d == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "dfs_io_q_dfs_exist");
+
+ return True;
+}
+
+/*************************************************************
+ Read/write a DFS_R_DFS_EXIST structure
+ ************************************************************/
+
+BOOL dfs_io_r_dfs_exist(const char *desc, DFS_R_DFS_EXIST *q_d, prs_struct *ps, int depth)
+{
+ if(q_d == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "dfs_io_r_dfs_exist");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("exist flag", ps, 0, &q_d->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+Make a DFS_Q_DFS_REMOVE structure
+*******************************************************************/
+
+BOOL init_dfs_q_dfs_remove(DFS_Q_DFS_REMOVE *q_d, const char *entrypath,
+ const char *servername, const char *sharename)
+{
+ DEBUG(5,("init_dfs_q_dfs_remove\n"));
+ init_unistr2(&q_d->DfsEntryPath, entrypath, UNI_STR_TERMINATE);
+ init_unistr2(&q_d->ServerName, servername, UNI_STR_TERMINATE);
+ init_unistr2(&q_d->ShareName, sharename, UNI_STR_TERMINATE);
+ q_d->ptr_ServerName = q_d->ptr_ShareName = 1;
+ return True;
+}
+
+/*******************************************************************
+Read/write a DFS_Q_DFS_REMOVE structure
+*******************************************************************/
+
+BOOL dfs_io_q_dfs_remove(const char *desc, DFS_Q_DFS_REMOVE *q_d, prs_struct *ps, int depth)
+{
+ if(q_d == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "dfs_io_q_dfs_remove");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("DfsEntryPath",&q_d->DfsEntryPath, 1, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_ServerName", ps, depth, &q_d->ptr_ServerName))
+ return False;
+ if(q_d->ptr_ServerName)
+ if (!smb_io_unistr2("ServerName",&q_d->ServerName, q_d->ptr_ServerName, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_ShareName", ps, depth, &q_d->ptr_ShareName))
+ return False;
+ if(q_d->ptr_ShareName)
+ if (!smb_io_unistr2("ShareName",&q_d->ShareName, q_d->ptr_ShareName, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+Read/write a DFS_R_DFS_REMOVE structure
+*******************************************************************/
+
+BOOL dfs_io_r_dfs_remove(const char *desc, DFS_R_DFS_REMOVE *r_d, prs_struct *ps, int depth)
+{
+ if(r_d == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "dfs_io_r_dfs_remove");
+ depth++;
+
+ if(!prs_werror("status", ps, depth, &r_d->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+Make a DFS_Q_DFS_ADD structure
+*******************************************************************/
+
+BOOL init_dfs_q_dfs_add(DFS_Q_DFS_ADD *q_d, const char *entrypath,
+ const char *servername, const char *sharename,
+ const char *comment, uint32 flags)
+{
+ DEBUG(5,("init_dfs_q_dfs_add\n"));
+ q_d->ptr_DfsEntryPath = q_d->ptr_ServerName = q_d->ptr_ShareName = 1;
+ init_unistr2(&q_d->DfsEntryPath, entrypath, UNI_STR_TERMINATE);
+ init_unistr2(&q_d->ServerName, servername, UNI_STR_TERMINATE);
+ init_unistr2(&q_d->ShareName, sharename, UNI_STR_TERMINATE);
+ if(comment != NULL) {
+ init_unistr2(&q_d->Comment, comment,UNI_STR_TERMINATE);
+ q_d->ptr_Comment = 1;
+ } else {
+ q_d->ptr_Comment = 0;
+ }
+
+ q_d->Flags = flags;
+ return True;
+}
+
+/************************************************************
+ Read/write a DFS_Q_DFS_ADD structure
+ ************************************************************/
+
+BOOL dfs_io_q_dfs_add(const char *desc, DFS_Q_DFS_ADD *q_d, prs_struct *ps, int depth)
+{
+ if(q_d == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "dfs_io_q_dfs_add");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("DfsEntryPath",&q_d->DfsEntryPath, 1, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("ServerName",&q_d->ServerName, 1, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_ShareName", ps, depth, &q_d->ptr_ShareName))
+ return False;
+ if(!smb_io_unistr2("ShareName",&q_d->ShareName, 1, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_Comment", ps, depth, &q_d->ptr_Comment))
+ return False;
+ if(!smb_io_unistr2("",&q_d->Comment, q_d->ptr_Comment , ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("Flags", ps, depth, &q_d->Flags))
+ return True;
+
+ return True;
+}
+
+/************************************************************
+ Read/write a DFS_R_DFS_ADD structure
+ ************************************************************/
+
+BOOL dfs_io_r_dfs_add(const char *desc, DFS_R_DFS_ADD *r_d, prs_struct *ps, int depth)
+{
+ if(r_d == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "dfs_io_r_dfs_add");
+ depth++;
+
+ if(!prs_werror("status", ps, depth, &r_d->status))
+ return False;
+
+ return True;
+}
+
+BOOL init_dfs_q_dfs_get_info(DFS_Q_DFS_GET_INFO *q_d, const char *entrypath,
+ const char *servername, const char *sharename,
+ uint32 info_level)
+{
+ DEBUG(5,("init_dfs_q2_get_info\n"));
+ init_unistr2(&q_d->uni_path, entrypath, UNI_STR_TERMINATE);
+ init_unistr2(&q_d->uni_server, servername, UNI_STR_TERMINATE);
+ init_unistr2(&q_d->uni_share, sharename, UNI_STR_TERMINATE);
+ q_d->level = info_level;
+ q_d->ptr_server = q_d->ptr_share = 1;
+ return True;
+}
+
+/************************************************************
+ Read/write a DFS_Q_GET_INFO structure
+ ************************************************************/
+
+BOOL dfs_io_q_dfs_get_info(const char *desc, DFS_Q_DFS_GET_INFO* q_i, prs_struct* ps, int depth)
+{
+ if(q_i == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "dfs_io_q_dfs_get_info");
+ depth++;
+
+ if(!smb_io_unistr2("",&q_i->uni_path, 1, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_server", ps, depth, &q_i->ptr_server))
+ return False;
+
+ if(q_i->ptr_server)
+ if (!smb_io_unistr2("",&q_i->uni_server, q_i->ptr_server, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_share", ps, depth, &q_i->ptr_share))
+ return False;
+ if(q_i->ptr_share)
+ if(!smb_io_unistr2("", &q_i->uni_share, q_i->ptr_share, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("level", ps, depth, &q_i->level))
+ return False;
+ return True;
+}
+
+/************************************************************
+ Read/write a DFS_R_GET_INFO structure
+ ************************************************************/
+
+BOOL dfs_io_r_dfs_get_info(const char *desc, DFS_R_DFS_GET_INFO* r_i, prs_struct* ps, int depth)
+{
+ if(r_i == NULL)
+ return False;
+
+ if(!prs_uint32("level", ps, depth, &r_i->level))
+ return False;
+ if(!prs_uint32("ptr_ctr", ps, depth, &r_i->ptr_ctr))
+ return False;
+
+ if(!dfs_io_dfs_info_ctr("", &r_i->ctr, 1, r_i->level, ps, depth))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_i->status))
+ return False;
+ return True;
+}
+
+/************************************************************
+ Make a DFS_Q_DFS_ENUM structure
+ ************************************************************/
+BOOL init_dfs_q_dfs_enum(DFS_Q_DFS_ENUM *q_d, uint32 level, DFS_INFO_CTR *ctr)
+{
+ q_d->level = level;
+ q_d->maxpreflen = -1;
+ q_d->ptr_buffer = 1;
+ q_d->level2 = level;
+
+ q_d->ptr_num_entries = 1;
+ q_d->num_entries = 0;
+ q_d->num_entries2 = 0;
+ q_d->reshnd.ptr_hnd = 1;
+ q_d->reshnd.handle = 0;
+ return True;
+}
+
+/************************************************************
+ Read or write the DFS_Q_DFS_ENUM structure
+ ************************************************************/
+
+BOOL dfs_io_q_dfs_enum(const char *desc, DFS_Q_DFS_ENUM *q_d, prs_struct *ps, int depth)
+{
+ if(q_d == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "dfs_io_q_dfs_enum");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("level", ps, depth, &q_d->level))
+ return False;
+ if(!prs_uint32("maxpreflen", ps, depth, &q_d->maxpreflen))
+ return False;
+ if(!prs_uint32("ptr_buffer", ps, depth, &q_d->ptr_buffer))
+ return False;
+ if(!prs_uint32("level2", ps, depth, &q_d->level2))
+ return False;
+ if(!prs_uint32("level3", ps, depth, &q_d->level2))
+ return False;
+
+ if(!prs_uint32("ptr_num_entries", ps, depth, &q_d->ptr_num_entries))
+ return False;
+ if(!prs_uint32("num_entries", ps, depth, &q_d->num_entries))
+ return False;
+ if(!prs_uint32("num_entries2", ps, depth, &q_d->num_entries2))
+ return False;
+ if(!smb_io_enum_hnd("resume_hnd",&q_d->reshnd, ps, depth))
+ return False;
+ return True;
+}
+
+/************************************************************
+ Read/write a DFS_INFO_CTR structure
+ ************************************************************/
+
+BOOL dfs_io_dfs_info_ctr(const char *desc, DFS_INFO_CTR* ctr, uint32 num_entries, uint32 level, prs_struct* ps, int depth)
+{
+ int i=0;
+
+ switch(level) {
+ case 1:
+ depth++;
+ /* should depend on whether marshalling or unmarshalling! */
+ if(UNMARSHALLING(ps)) {
+ ctr->dfs.info1 = (DFS_INFO_1 *)prs_alloc_mem(ps, sizeof(DFS_INFO_1)*num_entries);
+ if (!ctr->dfs.info1)
+ return False;
+ }
+
+ for(i=0;i<num_entries;i++) {
+ if(!prs_uint32("ptr_entrypath",ps, depth, &ctr->dfs.info1[i].ptr_entrypath))
+ return False;
+ }
+ for(i=0;i<num_entries;i++) {
+ if(!smb_io_unistr2("", &ctr->dfs.info1[i].entrypath, ctr->dfs.info1[i].ptr_entrypath, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ }
+ depth--;
+ break;
+ case 2:
+ depth++;
+ if(UNMARSHALLING(ps)) {
+ ctr->dfs.info2 = (DFS_INFO_2 *)prs_alloc_mem(ps, num_entries*sizeof(DFS_INFO_2));
+ if (!ctr->dfs.info2)
+ return False;
+ }
+
+ for(i=0;i<num_entries;i++) {
+ if(!prs_uint32("ptr_entrypath", ps, depth, &ctr->dfs.info2[i].ptr_entrypath))
+ return False;
+ if(!prs_uint32("ptr_comment", ps, depth, &ctr->dfs.info2[i].ptr_comment))
+ return False;
+ if(!prs_uint32("state", ps, depth, &ctr->dfs.info2[i].state))
+ return False;
+ if(!prs_uint32("num_storages", ps, depth, &ctr->dfs.info2[i].num_storages))
+ return False;
+ }
+ for(i=0;i<num_entries;i++) {
+ if(!smb_io_unistr2("", &ctr->dfs.info2[i].entrypath, ctr->dfs.info2[i].ptr_entrypath, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unistr2("",&ctr->dfs.info2[i].comment, ctr->dfs.info2[i].ptr_comment, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ }
+ depth--;
+ break;
+ case 3:
+ depth++;
+ if(UNMARSHALLING(ps)) {
+ ctr->dfs.info3 = (DFS_INFO_3 *)prs_alloc_mem(ps, num_entries*sizeof(DFS_INFO_3));
+ if (!ctr->dfs.info3)
+ return False;
+ }
+
+ for(i=0;i<num_entries;i++) {
+ if(!prs_uint32("ptr_entrypath", ps, depth, &ctr->dfs.info3[i].ptr_entrypath))
+ return False;
+ if(!prs_uint32("ptr_comment", ps, depth, &ctr->dfs.info3[i].ptr_comment))
+ return False;
+ if(!prs_uint32("state", ps, depth, &ctr->dfs.info3[i].state))
+ return False;
+ if(!prs_uint32("num_storages", ps, depth, &ctr->dfs.info3[i].num_storages))
+ return False;
+ if(!prs_uint32("ptr_storages", ps, depth, &ctr->dfs.info3[i].ptr_storages))
+ return False;
+ }
+ for(i=0;i<num_entries;i++) {
+ if(!smb_io_unistr2("", &ctr->dfs.info3[i].entrypath, ctr->dfs.info3[i].ptr_entrypath, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unistr2("", &ctr->dfs.info3[i].comment, ctr->dfs.info3[i].ptr_comment, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("num_storage_infos", ps, depth, &ctr->dfs.info3[i].num_storage_infos))
+ return False;
+
+ if(!dfs_io_dfs_storage_info("storage_info", &ctr->dfs.info3[i], ps, depth))
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/************************************************************
+ Read/write a DFS_R_DFS_ENUM structure
+ ************************************************************/
+
+BOOL dfs_io_r_dfs_enum(const char *desc, DFS_R_DFS_ENUM *q_d, prs_struct *ps, int depth)
+{
+ DFS_INFO_CTR *ctr;
+ if(q_d == NULL)
+ return False;
+ ctr = q_d->ctr;
+ if(ctr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "dfs_io_r_dfs_enum");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_buffer", ps, depth, &q_d->ptr_buffer))
+ return False;
+ if(!prs_uint32("level", ps, depth, &q_d->level))
+ return False;
+ if(!prs_uint32("level2", ps, depth, &ctr->switch_value))
+ return False;
+ if(!prs_uint32("ptr_num_entries", ps, depth, &q_d->ptr_num_entries))
+ return False;
+ if(q_d->ptr_num_entries)
+ if(!prs_uint32("num_entries", ps, depth, &q_d->num_entries))
+ return False;
+ if(!prs_uint32("ptr_num_entries2", ps, depth, &q_d->ptr_num_entries2))
+ return False;
+ if(q_d->ptr_num_entries2)
+ if(!prs_uint32("num_entries2", ps, depth, &ctr->num_entries))
+ return False;
+
+ if(!dfs_io_dfs_info_ctr("", ctr, q_d->num_entries, q_d->level, ps, depth))
+ return False;
+
+ if(!smb_io_enum_hnd("resume_hnd", &q_d->reshnd, ps, depth))
+ return False;
+ if(!prs_werror("status", ps, depth, &q_d->status))
+ return False;
+ return True;
+}
+
+BOOL dfs_io_dfs_storage_info(const char *desc, DFS_INFO_3* info3, prs_struct *ps, int depth)
+{
+ int i=0;
+ if(info3 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_dfs_storage_info");
+ depth++;
+
+ if(UNMARSHALLING(ps)) {
+ info3->storages = (DFS_STORAGE_INFO *)prs_alloc_mem(ps, info3->num_storage_infos*sizeof(DFS_STORAGE_INFO));
+ if (!info3->storages)
+ return False;
+ }
+
+ for(i=0;i<info3->num_storage_infos;i++) {
+ if(!prs_uint32("storage_state", ps, depth, &info3->storages[i].state))
+ return False;
+ if(!prs_uint32("ptr_servername", ps, depth, &info3->storages[i].ptr_servername))
+ return False;
+ if(!prs_uint32("ptr_sharename", ps, depth, &info3->storages[i].ptr_sharename))
+ return False;
+ }
+
+ for(i=0;i<info3->num_storage_infos;i++) {
+ if(!smb_io_unistr2("servername", &info3->storages[i].servername, info3->storages[i].ptr_servername, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unistr2("sharename", &info3->storages[i].sharename, info3->storages[i].ptr_sharename, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ }
+
+ return True;
+}
diff --git a/source/rpc_parse/parse_ds.c b/source/rpc_parse/parse_ds.c
new file mode 100644
index 00000000000..8d894b6c6ad
--- /dev/null
+++ b/source/rpc_parse/parse_ds.c
@@ -0,0 +1,302 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+
+ * Copyright (C) Gerald Carter 2002-2003
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+/************************************************************************
+************************************************************************/
+
+static BOOL ds_io_dominfobasic( const char *desc, prs_struct *ps, int depth, DSROLE_PRIMARY_DOMAIN_INFO_BASIC **basic)
+{
+ DSROLE_PRIMARY_DOMAIN_INFO_BASIC *p = *basic;
+
+ if ( UNMARSHALLING(ps) )
+ p = *basic = (DSROLE_PRIMARY_DOMAIN_INFO_BASIC *)prs_alloc_mem(ps, sizeof(DSROLE_PRIMARY_DOMAIN_INFO_BASIC));
+
+ if ( !p )
+ return False;
+
+ if ( !prs_uint16("machine_role", ps, depth, &p->machine_role) )
+ return False;
+ if ( !prs_uint16("unknown", ps, depth, &p->unknown) )
+ return False;
+
+ if ( !prs_uint32("flags", ps, depth, &p->flags) )
+ return False;
+
+ if ( !prs_uint32("netbios_ptr", ps, depth, &p->netbios_ptr) )
+ return False;
+ if ( !prs_uint32("dnsname_ptr", ps, depth, &p->dnsname_ptr) )
+ return False;
+ if ( !prs_uint32("forestname_ptr", ps, depth, &p->forestname_ptr) )
+ return False;
+
+ if ( !smb_io_uuid("domain_guid", &p->domain_guid, ps, depth) )
+ return False;
+
+ if ( !smb_io_unistr2( "netbios_domain", &p->netbios_domain, p->netbios_ptr, ps, depth) )
+ return False;
+ if ( !prs_align(ps) )
+ return False;
+
+ if ( !smb_io_unistr2( "dns_domain", &p->dns_domain, p->dnsname_ptr, ps, depth) )
+ return False;
+ if ( !prs_align(ps) )
+ return False;
+
+ if ( !smb_io_unistr2( "forest_domain", &p->forest_domain, p->forestname_ptr, ps, depth) )
+ return False;
+ if ( !prs_align(ps) )
+ return False;
+
+
+ return True;
+
+}
+
+/************************************************************************
+************************************************************************/
+
+BOOL ds_io_q_getprimdominfo( const char *desc, prs_struct *ps, int depth, DS_Q_GETPRIMDOMINFO *q_u)
+{
+ prs_debug(ps, depth, desc, "ds_io_q_getprimdominfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if ( !prs_uint16( "level", ps, depth, &q_u->level ) )
+ return False;
+
+ return True;
+}
+
+/************************************************************************
+************************************************************************/
+
+BOOL ds_io_r_getprimdominfo( const char *desc, prs_struct *ps, int depth, DS_R_GETPRIMDOMINFO *r_u)
+{
+ prs_debug(ps, depth, desc, "ds_io_r_getprimdominfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if ( !prs_uint32( "ptr", ps, depth, &r_u->ptr ) )
+ return False;
+
+ if ( r_u->ptr )
+ {
+ if ( !prs_uint16( "level", ps, depth, &r_u->level ) )
+ return False;
+
+ if ( !prs_uint16( "unknown0", ps, depth, &r_u->unknown0 ) )
+ return False;
+
+ switch ( r_u->level )
+ {
+ case DsRolePrimaryDomainInfoBasic:
+ if ( !ds_io_dominfobasic( "dominfobasic", ps, depth, &r_u->info.basic ) )
+ return False;
+ break;
+ default:
+ return False;
+ }
+ }
+
+ if ( !prs_align(ps) )
+ return False;
+
+ if ( !prs_ntstatus("status", ps, depth, &r_u->status ) )
+ return False;
+
+ return True;
+}
+
+/************************************************************************
+ initialize a DS_ENUM_DOM_TRUSTS structure
+************************************************************************/
+
+BOOL init_q_ds_enum_domain_trusts( DS_Q_ENUM_DOM_TRUSTS *q, const char *server,
+ uint32 flags )
+{
+ q->flags = flags;
+
+ if ( server && *server )
+ q->server_ptr = 1;
+ else
+ q->server_ptr = 0;
+
+ init_unistr2( &q->server, server, UNI_STR_TERMINATE);
+
+ return True;
+}
+
+/************************************************************************
+************************************************************************/
+
+static BOOL ds_io_domain_trusts( const char *desc, prs_struct *ps, int depth, DS_DOMAIN_TRUSTS *trust)
+{
+ prs_debug(ps, depth, desc, "ds_io_dom_trusts_ctr");
+ depth++;
+
+ if ( !prs_uint32( "netbios_ptr", ps, depth, &trust->netbios_ptr ) )
+ return False;
+
+ if ( !prs_uint32( "dns_ptr", ps, depth, &trust->dns_ptr ) )
+ return False;
+
+ if ( !prs_uint32( "flags", ps, depth, &trust->flags ) )
+ return False;
+
+ if ( !prs_uint32( "parent_index", ps, depth, &trust->parent_index ) )
+ return False;
+
+ if ( !prs_uint32( "trust_type", ps, depth, &trust->trust_type ) )
+ return False;
+
+ if ( !prs_uint32( "trust_attributes", ps, depth, &trust->trust_attributes ) )
+ return False;
+
+ if ( !prs_uint32( "sid_ptr", ps, depth, &trust->sid_ptr ) )
+ return False;
+
+ if ( !smb_io_uuid("guid", &trust->guid, ps, depth) )
+ return False;
+
+ return True;
+}
+
+/************************************************************************
+************************************************************************/
+
+static BOOL ds_io_dom_trusts_ctr( const char *desc, prs_struct *ps, int depth, DS_DOMAIN_TRUSTS_CTR *ctr)
+{
+ int i;
+
+ prs_debug(ps, depth, desc, "ds_io_dom_trusts_ctr");
+ depth++;
+
+ if ( !prs_uint32( "ptr", ps, depth, &ctr->ptr ) )
+ return False;
+
+ if ( !prs_uint32( "max_count", ps, depth, &ctr->max_count ) )
+ return False;
+
+ /* are we done? */
+
+ if ( ctr->max_count == 0 )
+ return True;
+
+ /* allocate the domain trusts array are parse it */
+
+ ctr->trusts = (DS_DOMAIN_TRUSTS*)talloc(ps->mem_ctx, sizeof(DS_DOMAIN_TRUSTS)*ctr->max_count);
+
+ if ( !ctr->trusts )
+ return False;
+
+ /* this stinks; the static portion o fthe structure is read here and then
+ we need another loop to read the UNISTR2's and SID's */
+
+ for ( i=0; i<ctr->max_count;i++ ) {
+ if ( !ds_io_domain_trusts("domain_trusts", ps, depth, &ctr->trusts[i] ) )
+ return False;
+ }
+
+ for ( i=0; i<ctr->max_count; i++ ) {
+
+ if ( !smb_io_unistr2("netbios_domain", &ctr->trusts[i].netbios_domain, ctr->trusts[i].netbios_ptr, ps, depth) )
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if ( !smb_io_unistr2("dns_domain", &ctr->trusts[i].dns_domain, ctr->trusts[i].dns_ptr, ps, depth) )
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if ( ctr->trusts[i].sid_ptr ) {
+ if ( !smb_io_dom_sid2("sid", &ctr->trusts[i].sid, ps, depth ) )
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/************************************************************************
+ initialize a DS_ENUM_DOM_TRUSTS request
+************************************************************************/
+
+BOOL ds_io_q_enum_domain_trusts( const char *desc, prs_struct *ps, int depth, DS_Q_ENUM_DOM_TRUSTS *q_u)
+{
+ prs_debug(ps, depth, desc, "ds_io_q_enum_domain_trusts");
+ depth++;
+
+ if ( !prs_align(ps) )
+ return False;
+
+ if ( !prs_uint32( "server_ptr", ps, depth, &q_u->server_ptr ) )
+ return False;
+
+ if ( !smb_io_unistr2("server", &q_u->server, q_u->server_ptr, ps, depth) )
+ return False;
+
+ if ( !prs_align(ps) )
+ return False;
+
+ if ( !prs_uint32( "flags", ps, depth, &q_u->flags ) )
+ return False;
+
+ return True;
+}
+
+/************************************************************************
+************************************************************************/
+
+BOOL ds_io_r_enum_domain_trusts( const char *desc, prs_struct *ps, int depth, DS_R_ENUM_DOM_TRUSTS *r_u)
+{
+ prs_debug(ps, depth, desc, "ds_io_r_enum_domain_trusts");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if ( !prs_uint32( "num_domains", ps, depth, &r_u->num_domains ) )
+ return False;
+
+ if ( r_u->num_domains ) {
+ if ( !ds_io_dom_trusts_ctr("domains", ps, depth, &r_u->domains ) )
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if ( !prs_ntstatus("status", ps, depth, &r_u->status ) )
+ return False;
+
+ return True;
+}
+
+
diff --git a/source/rpc_parse/parse_echo.c b/source/rpc_parse/parse_echo.c
new file mode 100644
index 00000000000..4b1ff1f4d54
--- /dev/null
+++ b/source/rpc_parse/parse_echo.c
@@ -0,0 +1,166 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * RPC Pipe client / server routines
+ *
+ * Copyright (C) Tim Potter 2003
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+#include "nterr.h"
+#include "rpc_parse.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+void init_echo_q_add_one(ECHO_Q_ADD_ONE *q_d, uint32 request)
+{
+ q_d->request = request;
+}
+
+BOOL echo_io_q_add_one(const char *desc, ECHO_Q_ADD_ONE *q_d,
+ prs_struct *ps, int depth)
+{
+ if (!prs_uint32("request", ps, 0, &q_d->request))
+ return False;
+
+ return True;
+}
+
+BOOL echo_io_r_add_one(const char *desc, ECHO_R_ADD_ONE *q_d,
+ prs_struct *ps, int depth)
+{
+ if (!prs_uint32("response", ps, 0, &q_d->response))
+ return False;
+
+ return True;
+}
+
+
+void init_echo_q_echo_data(ECHO_Q_ECHO_DATA *q_d, uint32 size, char *data)
+{
+ q_d->size = size;
+ q_d->data = data;
+}
+
+BOOL echo_io_q_echo_data(const char *desc, ECHO_Q_ECHO_DATA *q_d,
+ prs_struct *ps, int depth)
+{
+ if (!prs_uint32("size", ps, depth, &q_d->size))
+ return False;
+
+ if (!prs_uint32("size", ps, depth, &q_d->size))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ q_d->data = prs_alloc_mem(ps, q_d->size);
+
+ if (!q_d->data)
+ return False;
+ }
+
+ if (!prs_uint8s(False, "data", ps, depth, (unsigned char *)q_d->data, q_d->size))
+ return False;
+
+ return True;
+}
+
+BOOL echo_io_r_echo_data(const char *desc, ECHO_R_ECHO_DATA *q_d,
+ prs_struct *ps, int depth)
+{
+ if (!prs_uint32("size", ps, 0, &q_d->size))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ q_d->data = prs_alloc_mem(ps, q_d->size);
+
+ if (!q_d->data)
+ return False;
+ }
+
+ if (!prs_uint8s(False, "data", ps, depth, (unsigned char *)q_d->data, q_d->size))
+ return False;
+
+ return True;
+}
+
+void init_echo_q_sink_data(ECHO_Q_SINK_DATA *q_d, uint32 size, char *data)
+{
+ q_d->size = size;
+ q_d->data = data;
+}
+
+BOOL echo_io_q_sink_data(const char *desc, ECHO_Q_SINK_DATA *q_d,
+ prs_struct *ps, int depth)
+{
+ if (!prs_uint32("size", ps, depth, &q_d->size))
+ return False;
+
+ if (!prs_uint32("size", ps, depth, &q_d->size))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ q_d->data = prs_alloc_mem(ps, q_d->size);
+
+ if (!q_d->data)
+ return False;
+ }
+
+ if (!prs_uint8s(False, "data", ps, depth, (unsigned char *)q_d->data, q_d->size))
+ return False;
+
+ return True;
+}
+
+BOOL echo_io_r_sink_data(const char *desc, ECHO_R_SINK_DATA *q_d,
+ prs_struct *ps, int depth)
+{
+ return True;
+}
+
+void init_echo_q_source_data(ECHO_Q_SOURCE_DATA *q_d, uint32 size)
+{
+ q_d->size = size;
+}
+
+BOOL echo_io_q_source_data(const char *desc, ECHO_Q_SOURCE_DATA *q_d,
+ prs_struct *ps, int depth)
+{
+ if (!prs_uint32("size", ps, depth, &q_d->size))
+ return False;
+
+ return True;
+}
+
+BOOL echo_io_r_source_data(const char *desc, ECHO_R_SOURCE_DATA *q_d,
+ prs_struct *ps, int depth)
+{
+ if (!prs_uint32("size", ps, 0, &q_d->size))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ q_d->data = prs_alloc_mem(ps, q_d->size);
+
+ if (!q_d->data)
+ return False;
+ }
+
+ if (!prs_uint8s(False, "data", ps, depth, (unsigned char *)q_d->data, q_d->size))
+ return False;
+
+ return True;
+}
diff --git a/source/rpc_parse/parse_epmapper.c b/source/rpc_parse/parse_epmapper.c
new file mode 100644
index 00000000000..bc2cd175034
--- /dev/null
+++ b/source/rpc_parse/parse_epmapper.c
@@ -0,0 +1,482 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba end point mapper functions
+ Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+static uint32 internal_referent_id = 0;
+
+
+/*******************************************************************
+ Reads or writes a handle.
+********************************************************************/
+BOOL epm_io_handle(const char *desc, EPM_HANDLE *handle, prs_struct *ps,
+ int depth)
+{
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint8s(False, "data", ps, depth, handle->data,
+ sizeof(handle->data)))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ inits an EPM_FLOOR structure.
+********************************************************************/
+NTSTATUS init_epm_floor(EPM_FLOOR *efloor, uint8 protocol)
+{
+ /* handle lhs */
+ efloor->lhs.protocol = protocol;
+ efloor->lhs.length = sizeof(efloor->lhs.protocol);
+
+ switch(efloor->lhs.protocol) {
+ case EPM_FLOOR_UUID:
+ efloor->lhs.length += sizeof(efloor->lhs.uuid.uuid);
+ efloor->lhs.length += sizeof(efloor->lhs.uuid.version);
+ break;
+ default:
+ break;
+ }
+
+ /* handle rhs */
+ switch(efloor->lhs.protocol) {
+ case EPM_FLOOR_RPC:
+ case EPM_FLOOR_UUID:
+ efloor->rhs.length = sizeof(efloor->rhs.unknown);
+ break;
+ case EPM_FLOOR_TCP:
+ efloor->rhs.length = sizeof(efloor->rhs.tcp.port);
+ break;
+ case EPM_FLOOR_IP:
+ efloor->rhs.length = sizeof(efloor->rhs.ip.addr);
+ break;
+ case EPM_FLOOR_NMPIPES:
+ case EPM_FLOOR_LRPC:
+ case EPM_FLOOR_NETBIOS:
+ efloor->rhs.length = strlen(efloor->rhs.string) + 1;
+ break;
+ default:
+ break;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ inits an EPM_FLOOR structure with a UUID
+********************************************************************/
+NTSTATUS init_epm_floor_uuid(EPM_FLOOR *efloor,
+ const struct uuid uuid, uint16 version)
+{
+ memcpy(&efloor->lhs.uuid.uuid, &uuid, sizeof(uuid));
+ efloor->lhs.uuid.version = version;
+ efloor->rhs.unknown = 0;
+ return init_epm_floor(efloor, EPM_FLOOR_UUID);
+}
+
+/*******************************************************************
+ inits an EPM_FLOOR structure for RPC
+********************************************************************/
+NTSTATUS init_epm_floor_rpc(EPM_FLOOR *efloor)
+{
+ efloor->rhs.unknown = 0;
+ return init_epm_floor(efloor, EPM_FLOOR_RPC);
+}
+
+/*******************************************************************
+ inits an EPM_FLOOR structure for TCP
+********************************************************************/
+NTSTATUS init_epm_floor_tcp(EPM_FLOOR *efloor, uint16 port)
+{
+ efloor->rhs.tcp.port = htons(port);
+ return init_epm_floor(efloor, EPM_FLOOR_TCP);
+}
+
+/*******************************************************************
+ inits an EPM_FLOOR structure for IP
+********************************************************************/
+NTSTATUS init_epm_floor_ip(EPM_FLOOR *efloor, uint8 addr[4])
+{
+ memcpy(&efloor->rhs.ip.addr, addr, sizeof(addr));
+ return init_epm_floor(efloor, EPM_FLOOR_IP);
+}
+
+/*******************************************************************
+ inits an EPM_FLOOR structure for named pipe
+********************************************************************/
+NTSTATUS init_epm_floor_np(EPM_FLOOR *efloor, const char *pipe_name)
+{
+ safe_strcpy(efloor->rhs.string, pipe_name, sizeof(efloor->rhs.string)-1);
+ return init_epm_floor(efloor, EPM_FLOOR_NMPIPES);
+}
+
+/*******************************************************************
+ inits an EPM_FLOOR structure for named pipe
+********************************************************************/
+NTSTATUS init_epm_floor_lrpc(EPM_FLOOR *efloor, const char *pipe_name)
+{
+ safe_strcpy(efloor->rhs.string, pipe_name, sizeof(efloor->rhs.string)-1);
+ return init_epm_floor(efloor, EPM_FLOOR_LRPC);
+}
+
+/*******************************************************************
+ inits an EPM_FLOOR structure for named pipe
+********************************************************************/
+NTSTATUS init_epm_floor_nb(EPM_FLOOR *efloor, char *host_name)
+{
+ safe_strcpy(efloor->rhs.string, host_name, sizeof(efloor->rhs.string)-1);
+ return init_epm_floor(efloor, EPM_FLOOR_NETBIOS);
+}
+
+/*******************************************************************
+ reads and writes EPM_FLOOR.
+********************************************************************/
+BOOL epm_io_floor(const char *desc, EPM_FLOOR *efloor,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "epm_io_floor");
+ depth++;
+
+ if (!prs_uint16("lhs_length", ps, depth, &efloor->lhs.length))
+ return False;
+ if (!prs_uint8("protocol", ps, depth, &efloor->lhs.protocol))
+ return False;
+
+ switch (efloor->lhs.protocol) {
+ case EPM_FLOOR_UUID:
+ if (!smb_io_uuid("uuid", &efloor->lhs.uuid.uuid, ps, depth))
+ return False;
+ if (!prs_uint16("version", ps, depth,
+ &efloor->lhs.uuid.version))
+ return False;
+ break;
+ }
+
+ if (!prs_uint16("rhs_length", ps, depth, &efloor->rhs.length))
+ return False;
+
+ switch (efloor->lhs.protocol) {
+ case EPM_FLOOR_UUID:
+ case EPM_FLOOR_RPC:
+ if (!prs_uint16("unknown", ps, depth, &efloor->rhs.unknown))
+ return False;
+ break;
+ case EPM_FLOOR_TCP:
+ if (!prs_uint16("tcp_port", ps, depth, &efloor->rhs.tcp.port))
+ return False;
+ break;
+ case EPM_FLOOR_IP:
+ if (!prs_uint8s(False, "ip_addr", ps, depth,
+ efloor->rhs.ip.addr,
+ sizeof(efloor->rhs.ip.addr)))
+ return False;
+ break;
+ case EPM_FLOOR_NMPIPES:
+ case EPM_FLOOR_LRPC:
+ case EPM_FLOOR_NETBIOS:
+ if (!prs_uint8s(False, "string", ps, depth,
+ efloor->rhs.string,
+ efloor->rhs.length))
+ return False;
+ break;
+ default:
+ break;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a EPM_TOWER structure.
+********************************************************************/
+NTSTATUS init_epm_tower(TALLOC_CTX *ctx, EPM_TOWER *tower,
+ const EPM_FLOOR *floors, int num_floors)
+{
+ int size = 0;
+ int i;
+
+ DEBUG(5, ("init_epm_tower\n"));
+
+ size += sizeof(uint16); /* number of floors is in tower length */
+ for (i = 0; i < num_floors; i++) {
+ size += (sizeof(uint16) * 2);
+ size += floors[i].lhs.length;
+ size += floors[i].rhs.length;
+ }
+
+ tower->max_length = tower->length = size;
+ tower->num_floors = num_floors;
+ tower->floors = talloc(ctx, sizeof(EPM_FLOOR) * num_floors);
+ if (!tower->floors) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ memcpy(tower->floors, floors, sizeof(EPM_FLOOR) * num_floors);
+ tower->unknown = 0x7e;
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Reads or writes an EPM_TOWER structure.
+********************************************************************/
+BOOL epm_io_tower(const char *desc, EPM_TOWER *tower,
+ prs_struct *ps, int depth)
+{
+ int i;
+
+ prs_debug(ps, depth, desc, "epm_io_tower");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("max_length", ps, depth, &tower->max_length))
+ return False;
+ if (!prs_uint32("length", ps, depth, &tower->length))
+ return False;
+ if (!prs_uint16("num_floors", ps, depth, &tower->num_floors))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ tower->floors = talloc(ps->mem_ctx,
+ sizeof(EPM_FLOOR) * tower->num_floors);
+ if (!tower->floors)
+ return False;
+ }
+
+ for (i = 0; i < tower->num_floors; i++) {
+ if (!epm_io_floor("floor", tower->floors + i, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Initialize an EPM_TOWER_ARRAY structure
+********************************************************************/
+NTSTATUS init_epm_tower_array(TALLOC_CTX *ctx, EPM_TOWER_ARRAY *array,
+ const EPM_TOWER *towers, int num_towers)
+{
+ int i;
+
+ array->max_count = num_towers;
+ array->offset = 0;
+ array->count = num_towers;
+ array->tower_ref_ids = talloc(ctx, sizeof(uint32) * num_towers);
+ if (!array->tower_ref_ids) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0;i<num_towers;i++)
+ array->tower_ref_ids[i] = ++internal_referent_id;
+
+ array->towers = talloc(ctx, sizeof(EPM_TOWER) * num_towers);
+ if (!array->towers) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ memcpy(array->towers, towers, sizeof(EPM_TOWER) * num_towers);
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Reads or writes an EPM_TOWER_ARRAY structure.
+********************************************************************/
+BOOL epm_io_tower_array(const char *desc, EPM_TOWER_ARRAY *array,
+ prs_struct *ps, int depth)
+{
+ int i;
+
+ prs_debug(ps, depth, desc, "epm_io_tower_array");
+ depth++;
+
+ if (!prs_uint32("max_count", ps, depth, &array->max_count))
+ return False;
+ if (!prs_uint32("offset", ps, depth, &array->offset))
+ return False;
+ if (!prs_uint32("count", ps, depth, &array->count))
+ return False;
+
+
+ if (UNMARSHALLING(ps)) {
+ array->tower_ref_ids = talloc(ps->mem_ctx,
+ sizeof(uint32) * array->count);
+ if (!array->tower_ref_ids) {
+ return False;
+ }
+ }
+ for (i=0; i < array->count; i++) {
+ if (!prs_uint32("ref_id", ps, depth, &array->tower_ref_ids[i])) {
+ return False;
+ } else {
+ if (array->tower_ref_ids[i] > internal_referent_id) {
+ internal_referent_id = array->tower_ref_ids[i];
+ }
+ }
+ }
+
+
+
+ if (!prs_set_offset(ps, prs_offset(ps) + array->offset))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ array->towers = talloc(ps->mem_ctx,
+ sizeof(EPM_TOWER) * array->count);
+ if (!array->towers) {
+ return False;
+ }
+ }
+
+ for (i = 0; i < array->count; i++) {
+ if (!epm_io_tower("tower", &array->towers[i], ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Initialize EPM_R_MAP structure
+******************************************************************/
+NTSTATUS init_epm_r_map(TALLOC_CTX *ctx, EPM_R_MAP *r_map,
+ const EPM_HANDLE *handle, const EPM_TOWER_ARRAY *array,
+ int num_elements, uint32 status)
+{
+ memcpy(&r_map->handle, handle, sizeof(*handle));
+ r_map->num_results = num_elements;
+ r_map->results = talloc(ctx, sizeof(EPM_TOWER_ARRAY) * num_elements);
+ if (!r_map->results) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ memcpy(r_map->results, array, sizeof(EPM_TOWER_ARRAY) * num_elements);
+ r_map->status = status;
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ Inits a EPM_Q_MAP structure.
+**************************************************************************
+* We attempt to hide the ugliness of the wire format by taking a EPM_TOWER
+* array with a defined size
+**************************************************************************/
+NTSTATUS init_epm_q_map(TALLOC_CTX *ctx, EPM_Q_MAP *q_map,
+ const EPM_TOWER *towers, int num_towers)
+{
+ static uint32 handle = 1;
+
+ ZERO_STRUCTP(q_map);
+
+ DEBUG(5, ("init_epm_q_map\n"));
+ q_map->handle.data[0] = (handle >> 0) & 0xFF;
+ q_map->handle.data[1] = (handle >> 8) & 0xFF;
+ q_map->handle.data[2] = (handle >> 16) & 0xFF;
+ q_map->handle.data[3] = (handle >> 24) & 0xFF;
+
+ q_map->tower = talloc(ctx, sizeof(EPM_TOWER) * (num_towers + 1));
+ if (!q_map->tower) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ memcpy(q_map->tower, towers, sizeof(EPM_TOWER) * num_towers);
+
+ ZERO_STRUCT(q_map->tower[num_towers]);
+
+ /* For now let's not take more than 4 towers per result */
+ q_map->max_towers = num_towers * 4;
+
+ q_map->tower_ref_id = ++internal_referent_id;
+
+ handle++;
+
+ return NT_STATUS_OK;
+}
+
+/*****************************************************************
+ epm_io_q_map - read or write EPM_Q_MAP structure
+******************************************************************/
+BOOL epm_io_q_map(const char *desc, EPM_Q_MAP *io_map, prs_struct *ps,
+ int depth)
+{
+ prs_debug(ps, depth, desc, "epm_io_q_map");
+ depth++;
+
+ if (!epm_io_handle("handle", &io_map->handle, ps, depth))
+ return False;
+
+ if (!prs_uint32("referent_id", ps, 0, &io_map->tower_ref_id))
+ return False;
+ if (io_map->tower_ref_id > internal_referent_id)
+ internal_referent_id = io_map->tower_ref_id;
+
+ /* HACK: We need a more elegant way of doing this */
+ if (UNMARSHALLING(ps)) {
+ io_map->tower = talloc(ps->mem_ctx, sizeof(EPM_TOWER));
+ if (!io_map->tower)
+ return False;
+ }
+ if (!epm_io_tower("tower", io_map->tower, ps, depth))
+ return False;
+ if (!epm_io_handle("term_handle", &io_map->term_handle, ps, depth))
+ return False;
+
+ if (!prs_uint32("max_towers", ps, 0, &io_map->max_towers))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ epm_io_r_map - Read/Write EPM_R_MAP structure
+******************************************************************/
+BOOL epm_io_r_map(const char *desc, EPM_R_MAP *io_map,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "epm_io_r_map");
+ depth++;
+
+ if (!epm_io_handle("handle", &io_map->handle, ps, depth))
+ return False;
+ if (!prs_uint32("num_results", ps, depth, &io_map->num_results))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ io_map->results = talloc(ps->mem_ctx,
+ sizeof(EPM_TOWER_ARRAY) *
+ io_map->num_results);
+ if (!io_map->results)
+ return False;
+ }
+ if (!epm_io_tower_array("results", io_map->results, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("status", ps, depth, &io_map->status))
+ return False;
+
+ return True;
+}
diff --git a/source/rpc_parse/parse_lsa.c b/source/rpc_parse/parse_lsa.c
new file mode 100644
index 00000000000..50fd3beb48e
--- /dev/null
+++ b/source/rpc_parse/parse_lsa.c
@@ -0,0 +1,2474 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997,
+ * Copyright (C) Andrew Bartlett 2002,
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+static BOOL lsa_io_trans_names(const char *desc, LSA_TRANS_NAME_ENUM *trn, prs_struct *ps, int depth);
+
+/*******************************************************************
+ Inits a LSA_TRANS_NAME structure.
+********************************************************************/
+
+void init_lsa_trans_name(LSA_TRANS_NAME *trn, UNISTR2 *uni_name,
+ uint16 sid_name_use, const char *name, uint32 idx)
+{
+ trn->sid_name_use = sid_name_use;
+ init_unistr2(uni_name, name, UNI_FLAGS_NONE);
+ init_uni_hdr(&trn->hdr_name, uni_name);
+ trn->domain_idx = idx;
+}
+
+/*******************************************************************
+ Reads or writes a LSA_TRANS_NAME structure.
+********************************************************************/
+
+static BOOL lsa_io_trans_name(const char *desc, LSA_TRANS_NAME *trn, prs_struct *ps,
+ int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_trans_name");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint16("sid_name_use", ps, depth, &trn->sid_name_use))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unihdr ("hdr_name", &trn->hdr_name, ps, depth))
+ return False;
+ if(!prs_uint32("domain_idx ", ps, depth, &trn->domain_idx))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_R_REF structure.
+********************************************************************/
+
+static BOOL lsa_io_dom_r_ref(const char *desc, DOM_R_REF *r_r, prs_struct *ps,
+ int depth)
+{
+ unsigned int i;
+
+ prs_debug(ps, depth, desc, "lsa_io_dom_r_ref");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_ref_doms_1", ps, depth, &r_r->num_ref_doms_1)) /* num referenced domains? */
+ return False;
+ if(!prs_uint32("ptr_ref_dom ", ps, depth, &r_r->ptr_ref_dom)) /* undocumented buffer pointer. */
+ return False;
+ if(!prs_uint32("max_entries ", ps, depth, &r_r->max_entries)) /* 32 - max number of entries */
+ return False;
+
+ SMB_ASSERT_ARRAY(r_r->hdr_ref_dom, r_r->num_ref_doms_1);
+
+ if (r_r->ptr_ref_dom != 0) {
+
+ if(!prs_uint32("num_ref_doms_2", ps, depth, &r_r->num_ref_doms_2)) /* 4 - num referenced domains? */
+ return False;
+
+ SMB_ASSERT_ARRAY(r_r->ref_dom, r_r->num_ref_doms_2);
+
+ for (i = 0; i < r_r->num_ref_doms_1; i++) {
+ fstring t;
+
+ slprintf(t, sizeof(t) - 1, "dom_ref[%d] ", i);
+ if(!smb_io_unihdr(t, &r_r->hdr_ref_dom[i].hdr_dom_name, ps, depth))
+ return False;
+
+ slprintf(t, sizeof(t) - 1, "sid_ptr[%d] ", i);
+ if(!prs_uint32(t, ps, depth, &r_r->hdr_ref_dom[i].ptr_dom_sid))
+ return False;
+ }
+
+ for (i = 0; i < r_r->num_ref_doms_2; i++) {
+ fstring t;
+
+ if (r_r->hdr_ref_dom[i].hdr_dom_name.buffer != 0) {
+ slprintf(t, sizeof(t) - 1, "dom_ref[%d] ", i);
+ if(!smb_io_unistr2(t, &r_r->ref_dom[i].uni_dom_name, True, ps, depth)) /* domain name unicode string */
+ return False;
+ if(!prs_align(ps))
+ return False;
+ }
+
+ if (r_r->hdr_ref_dom[i].ptr_dom_sid != 0) {
+ slprintf(t, sizeof(t) - 1, "sid_ptr[%d] ", i);
+ if(!smb_io_dom_sid2(t, &r_r->ref_dom[i].ref_dom, ps, depth)) /* referenced domain SIDs */
+ return False;
+ }
+ }
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an LSA_SEC_QOS structure.
+********************************************************************/
+
+void init_lsa_sec_qos(LSA_SEC_QOS *qos, uint16 imp_lev, uint8 ctxt, uint8 eff)
+{
+ DEBUG(5, ("init_lsa_sec_qos\n"));
+
+ qos->len = 0x0c; /* length of quality of service block, in bytes */
+ qos->sec_imp_level = imp_lev;
+ qos->sec_ctxt_mode = ctxt;
+ qos->effective_only = eff;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_SEC_QOS structure.
+********************************************************************/
+
+static BOOL lsa_io_sec_qos(const char *desc, LSA_SEC_QOS *qos, prs_struct *ps,
+ int depth)
+{
+ uint32 start;
+
+ prs_debug(ps, depth, desc, "lsa_io_obj_qos");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ start = prs_offset(ps);
+
+ /* these pointers had _better_ be zero, because we don't know
+ what they point to!
+ */
+ if(!prs_uint32("len ", ps, depth, &qos->len)) /* 0x18 - length (in bytes) inc. the length field. */
+ return False;
+ if(!prs_uint16("sec_imp_level ", ps, depth, &qos->sec_imp_level ))
+ return False;
+ if(!prs_uint8 ("sec_ctxt_mode ", ps, depth, &qos->sec_ctxt_mode ))
+ return False;
+ if(!prs_uint8 ("effective_only", ps, depth, &qos->effective_only))
+ return False;
+
+ if (qos->len != prs_offset(ps) - start) {
+ DEBUG(3,("lsa_io_sec_qos: length %x does not match size %x\n",
+ qos->len, prs_offset(ps) - start));
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an LSA_OBJ_ATTR structure.
+********************************************************************/
+
+static void init_lsa_obj_attr(LSA_OBJ_ATTR *attr, uint32 attributes, LSA_SEC_QOS *qos)
+{
+ DEBUG(5, ("init_lsa_obj_attr\n"));
+
+ attr->len = 0x18; /* length of object attribute block, in bytes */
+ attr->ptr_root_dir = 0;
+ attr->ptr_obj_name = 0;
+ attr->attributes = attributes;
+ attr->ptr_sec_desc = 0;
+
+ if (qos != NULL) {
+ attr->ptr_sec_qos = 1;
+ attr->sec_qos = qos;
+ } else {
+ attr->ptr_sec_qos = 0;
+ attr->sec_qos = NULL;
+ }
+}
+
+/*******************************************************************
+ Reads or writes an LSA_OBJ_ATTR structure.
+********************************************************************/
+
+static BOOL lsa_io_obj_attr(const char *desc, LSA_OBJ_ATTR *attr, prs_struct *ps,
+ int depth)
+{
+ uint32 start;
+
+ prs_debug(ps, depth, desc, "lsa_io_obj_attr");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ start = prs_offset(ps);
+
+ /* these pointers had _better_ be zero, because we don't know
+ what they point to!
+ */
+ if(!prs_uint32("len ", ps, depth, &attr->len)) /* 0x18 - length (in bytes) inc. the length field. */
+ return False;
+ if(!prs_uint32("ptr_root_dir", ps, depth, &attr->ptr_root_dir)) /* 0 - root directory (pointer) */
+ return False;
+ if(!prs_uint32("ptr_obj_name", ps, depth, &attr->ptr_obj_name)) /* 0 - object name (pointer) */
+ return False;
+ if(!prs_uint32("attributes ", ps, depth, &attr->attributes)) /* 0 - attributes (undocumented) */
+ return False;
+ if(!prs_uint32("ptr_sec_desc", ps, depth, &attr->ptr_sec_desc)) /* 0 - security descriptior (pointer) */
+ return False;
+ if(!prs_uint32("ptr_sec_qos ", ps, depth, &attr->ptr_sec_qos )) /* security quality of service (pointer) */
+ return False;
+
+ /* code commented out as it's not necessary true (tested with hyena). JFM, 11/22/2001 */
+#if 0
+ if (attr->len != prs_offset(ps) - start) {
+ DEBUG(3,("lsa_io_obj_attr: length %x does not match size %x\n",
+ attr->len, prs_offset(ps) - start));
+ return False;
+ }
+#endif
+
+ if (attr->ptr_sec_qos != 0) {
+ if (UNMARSHALLING(ps))
+ if (!(attr->sec_qos = (LSA_SEC_QOS *)prs_alloc_mem(ps,sizeof(LSA_SEC_QOS))))
+ return False;
+
+ if(!lsa_io_sec_qos("sec_qos", attr->sec_qos, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+
+/*******************************************************************
+ Inits an LSA_Q_OPEN_POL structure.
+********************************************************************/
+
+void init_q_open_pol(LSA_Q_OPEN_POL *r_q, uint16 system_name,
+ uint32 attributes, uint32 desired_access,
+ LSA_SEC_QOS *qos)
+{
+ DEBUG(5, ("init_open_pol: attr:%d da:%d\n", attributes,
+ desired_access));
+
+ r_q->ptr = 1; /* undocumented pointer */
+
+ r_q->des_access = desired_access;
+
+ r_q->system_name = system_name;
+ init_lsa_obj_attr(&r_q->attr, attributes, qos);
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_OPEN_POL structure.
+********************************************************************/
+
+BOOL lsa_io_q_open_pol(const char *desc, LSA_Q_OPEN_POL *r_q, prs_struct *ps,
+ int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_open_pol");
+ depth++;
+
+ if(!prs_uint32("ptr ", ps, depth, &r_q->ptr))
+ return False;
+ if(!prs_uint16("system_name", ps, depth, &r_q->system_name))
+ return False;
+ if(!prs_align( ps ))
+ return False;
+
+ if(!lsa_io_obj_attr("", &r_q->attr, ps, depth))
+ return False;
+
+ if(!prs_uint32("des_access", ps, depth, &r_q->des_access))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_OPEN_POL structure.
+********************************************************************/
+
+BOOL lsa_io_r_open_pol(const char *desc, LSA_R_OPEN_POL *r_p, prs_struct *ps,
+ int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_open_pol");
+ depth++;
+
+ if(!smb_io_pol_hnd("", &r_p->pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_p->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an LSA_Q_OPEN_POL2 structure.
+********************************************************************/
+
+void init_q_open_pol2(LSA_Q_OPEN_POL2 *r_q, const char *server_name,
+ uint32 attributes, uint32 desired_access,
+ LSA_SEC_QOS *qos)
+{
+ DEBUG(5, ("init_q_open_pol2: attr:%d da:%d\n", attributes,
+ desired_access));
+
+ r_q->ptr = 1; /* undocumented pointer */
+
+ r_q->des_access = desired_access;
+
+ init_unistr2(&r_q->uni_server_name, server_name, UNI_STR_TERMINATE);
+
+ init_lsa_obj_attr(&r_q->attr, attributes, qos);
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_OPEN_POL2 structure.
+********************************************************************/
+
+BOOL lsa_io_q_open_pol2(const char *desc, LSA_Q_OPEN_POL2 *r_q, prs_struct *ps,
+ int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_open_pol2");
+ depth++;
+
+ if(!prs_uint32("ptr ", ps, depth, &r_q->ptr))
+ return False;
+
+ if(!smb_io_unistr2 ("", &r_q->uni_server_name, r_q->ptr, ps, depth))
+ return False;
+ if(!lsa_io_obj_attr("", &r_q->attr, ps, depth))
+ return False;
+
+ if(!prs_uint32("des_access", ps, depth, &r_q->des_access))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_OPEN_POL2 structure.
+********************************************************************/
+
+BOOL lsa_io_r_open_pol2(const char *desc, LSA_R_OPEN_POL2 *r_p, prs_struct *ps,
+ int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_open_pol2");
+ depth++;
+
+ if(!smb_io_pol_hnd("", &r_p->pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_p->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+makes an LSA_Q_QUERY_SEC_OBJ structure.
+********************************************************************/
+
+void init_q_query_sec_obj(LSA_Q_QUERY_SEC_OBJ *q_q, const POLICY_HND *hnd,
+ uint32 sec_info)
+{
+ DEBUG(5, ("init_q_query_sec_obj\n"));
+
+ q_q->pol = *hnd;
+ q_q->sec_info = sec_info;
+
+ return;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_QUERY_SEC_OBJ structure.
+********************************************************************/
+
+BOOL lsa_io_q_query_sec_obj(const char *desc, LSA_Q_QUERY_SEC_OBJ *q_q,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_query_sec_obj");
+ depth++;
+
+ if (!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+ return False;
+
+ if (!prs_uint32("sec_info", ps, depth, &q_q->sec_info))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a LSA_R_QUERY_SEC_OBJ structure.
+********************************************************************/
+
+BOOL lsa_io_r_query_sec_obj(const char *desc, LSA_R_QUERY_SEC_OBJ *r_u,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_query_sec_obj");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("ptr", ps, depth, &r_u->ptr))
+ return False;
+
+ if (r_u->ptr != 0) {
+ if (!sec_io_desc_buf("sec", &r_u->buf, ps, depth))
+ return False;
+ }
+
+ if (!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an LSA_Q_QUERY_INFO structure.
+********************************************************************/
+
+void init_q_query(LSA_Q_QUERY_INFO *q_q, POLICY_HND *hnd, uint16 info_class)
+{
+ DEBUG(5, ("init_q_query\n"));
+
+ memcpy(&q_q->pol, hnd, sizeof(q_q->pol));
+
+ q_q->info_class = info_class;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_QUERY_INFO structure.
+********************************************************************/
+
+BOOL lsa_io_q_query(const char *desc, LSA_Q_QUERY_INFO *q_q, prs_struct *ps,
+ int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_query");
+ depth++;
+
+ if(!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+ return False;
+
+ if(!prs_uint16("info_class", ps, depth, &q_q->info_class))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+makes an LSA_Q_ENUM_TRUST_DOM structure.
+********************************************************************/
+BOOL init_q_enum_trust_dom(LSA_Q_ENUM_TRUST_DOM * q_e, POLICY_HND *pol,
+ uint32 enum_context, uint32 preferred_len)
+{
+ DEBUG(5, ("init_q_enum_trust_dom\n"));
+
+ q_e->pol = *pol;
+ q_e->enum_context = enum_context;
+ q_e->preferred_len = preferred_len;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_ENUM_TRUST_DOM structure.
+********************************************************************/
+
+BOOL lsa_io_q_enum_trust_dom(const char *desc, LSA_Q_ENUM_TRUST_DOM *q_e,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_enum_trust_dom");
+ depth++;
+
+ if(!smb_io_pol_hnd("", &q_e->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("enum_context ", ps, depth, &q_e->enum_context))
+ return False;
+ if(!prs_uint32("preferred_len", ps, depth, &q_e->preferred_len))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an LSA_R_ENUM_TRUST_DOM structure.
+********************************************************************/
+
+void init_r_enum_trust_dom(TALLOC_CTX *ctx, LSA_R_ENUM_TRUST_DOM *r_e, uint32 enum_context,
+ uint32 req_num_domains, uint32 num_domains, TRUSTDOM **td)
+{
+ unsigned int i;
+
+ DEBUG(5, ("init_r_enum_trust_dom\n"));
+
+ r_e->enum_context = enum_context;
+ r_e->num_domains = num_domains;
+ r_e->ptr_enum_domains = 0;
+ r_e->num_domains2 = num_domains;
+
+ if (num_domains != 0) {
+
+ /*
+ * allocating empty arrays of unicode headers, strings
+ * and sids of enumerated trusted domains
+ */
+ if (!(r_e->hdr_domain_name = (UNIHDR2 *)talloc(ctx,sizeof(UNIHDR2) * num_domains))) {
+ r_e->status = NT_STATUS_NO_MEMORY;
+ return;
+ }
+
+ if (!(r_e->uni_domain_name = (UNISTR2 *)talloc(ctx,sizeof(UNISTR2) * num_domains))) {
+ r_e->status = NT_STATUS_NO_MEMORY;
+ return;
+ }
+
+ if (!(r_e->domain_sid = (DOM_SID2 *)talloc(ctx,sizeof(DOM_SID2) * num_domains))) {
+ r_e->status = NT_STATUS_NO_MEMORY;
+ return;
+ }
+
+ for (i = 0; i < num_domains; i++) {
+
+ /* don't know what actually is this for */
+ r_e->ptr_enum_domains = 1;
+
+ init_dom_sid2(&r_e->domain_sid[i], &(td[i])->sid);
+
+ init_unistr2_w(ctx, &r_e->uni_domain_name[i], (td[i])->name);
+ init_uni_hdr2(&r_e->hdr_domain_name[i], &r_e->uni_domain_name[i]);
+
+ };
+ }
+
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_ENUM_TRUST_DOM structure.
+********************************************************************/
+
+BOOL lsa_io_r_enum_trust_dom(const char *desc, LSA_R_ENUM_TRUST_DOM *r_e,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_enum_trust_dom");
+ depth++;
+
+ if(!prs_uint32("enum_context ", ps, depth, &r_e->enum_context))
+ return False;
+ if(!prs_uint32("num_domains ", ps, depth, &r_e->num_domains))
+ return False;
+ if(!prs_uint32("ptr_enum_domains", ps, depth, &r_e->ptr_enum_domains))
+ return False;
+
+ if (r_e->ptr_enum_domains) {
+ int i, num_domains;
+
+ if(!prs_uint32("num_domains2", ps, depth, &r_e->num_domains2))
+ return False;
+
+ num_domains = r_e->num_domains2;
+
+ if (UNMARSHALLING(ps)) {
+ if (!(r_e->hdr_domain_name = (UNIHDR2 *)prs_alloc_mem(ps,sizeof(UNIHDR2) * num_domains)))
+ return False;
+
+ if (!(r_e->uni_domain_name = (UNISTR2 *)prs_alloc_mem(ps,sizeof(UNISTR2) * num_domains)))
+ return False;
+
+ if (!(r_e->domain_sid = (DOM_SID2 *)prs_alloc_mem(ps,sizeof(DOM_SID2) * num_domains)))
+ return False;
+ }
+
+ for (i = 0; i < num_domains; i++) {
+ if(!smb_io_unihdr2 ("", &r_e->hdr_domain_name[i], ps,
+ depth))
+ return False;
+ }
+
+ for (i = 0; i < num_domains; i++) {
+ if(!smb_io_unistr2 ("", &r_e->uni_domain_name[i],
+ r_e->hdr_domain_name[i].buffer,
+ ps, depth))
+ return False;
+ if(!smb_io_dom_sid2("", &r_e->domain_sid[i], ps,
+ depth))
+ return False;
+ }
+ }
+
+ if(!prs_ntstatus("status", ps, depth, &r_e->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a dom query structure.
+********************************************************************/
+
+static BOOL lsa_io_dom_query(const char *desc, DOM_QUERY *d_q, prs_struct *ps, int depth)
+{
+ if (d_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "lsa_io_dom_query");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint16("uni_dom_max_len", ps, depth, &d_q->uni_dom_max_len)) /* domain name string length * 2 */
+ return False;
+ if(!prs_uint16("uni_dom_str_len", ps, depth, &d_q->uni_dom_str_len)) /* domain name string length * 2 */
+ return False;
+
+ if(!prs_uint32("buffer_dom_name", ps, depth, &d_q->buffer_dom_name)) /* undocumented domain name string buffer pointer */
+ return False;
+ if(!prs_uint32("buffer_dom_sid ", ps, depth, &d_q->buffer_dom_sid)) /* undocumented domain SID string buffer pointer */
+ return False;
+
+ if(!smb_io_unistr2("unistr2", &d_q->uni_domain_name, d_q->buffer_dom_name, ps, depth)) /* domain name (unicode string) */
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (d_q->buffer_dom_sid != 0) {
+ if(!smb_io_dom_sid2("", &d_q->dom_sid, ps, depth)) /* domain SID */
+ return False;
+ } else {
+ memset((char *)&d_q->dom_sid, '\0', sizeof(d_q->dom_sid));
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL lsa_io_dom_query_2(const char *desc, DOM_QUERY_2 *d_q, prs_struct *ps, int depth)
+{
+ uint32 ptr = 1;
+
+ if (d_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "lsa_io_dom_query_2");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("auditing_enabled", ps, depth, &d_q->auditing_enabled))
+ return False;
+ if (!prs_uint32("ptr ", ps, depth, &ptr))
+ return False;
+ if (!prs_uint32("count1", ps, depth, &d_q->count1))
+ return False;
+ if (!prs_uint32("count2", ps, depth, &d_q->count2))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ d_q->auditsettings = (uint32 *)talloc_zero(ps->mem_ctx, d_q->count2 * sizeof(uint32));
+ }
+
+ if (d_q->auditsettings == NULL) {
+ DEBUG(1, ("lsa_io_dom_query_2: NULL auditsettings!\n"));
+ return False;
+ }
+
+ if (!prs_uint32s(False, "auditsettings", ps, depth, d_q->auditsettings, d_q->count2))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a dom query structure.
+********************************************************************/
+
+static BOOL lsa_io_dom_query_3(const char *desc, DOM_QUERY_3 *d_q, prs_struct *ps, int depth)
+{
+ return lsa_io_dom_query("", d_q, ps, depth);
+}
+
+/*******************************************************************
+ Reads or writes a dom query structure.
+********************************************************************/
+
+static BOOL lsa_io_dom_query_5(const char *desc, DOM_QUERY_5 *d_q, prs_struct *ps, int depth)
+{
+ return lsa_io_dom_query("", d_q, ps, depth);
+}
+
+/*******************************************************************
+ Reads or writes a dom query structure.
+********************************************************************/
+
+static BOOL lsa_io_dom_query_6(const char *desc, DOM_QUERY_6 *d_q, prs_struct *ps, int depth)
+{
+ if (d_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "lsa_io_dom_query_6");
+ depth++;
+
+ if (!prs_uint16("server_role", ps, depth, &d_q->server_role))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_QUERY_INFO structure.
+********************************************************************/
+
+BOOL lsa_io_r_query(const char *desc, LSA_R_QUERY_INFO *r_q, prs_struct *ps,
+ int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_query");
+ depth++;
+
+ if(!prs_uint32("undoc_buffer", ps, depth, &r_q->undoc_buffer))
+ return False;
+
+ if (r_q->undoc_buffer != 0) {
+ if(!prs_uint16("info_class", ps, depth, &r_q->info_class))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ switch (r_q->info_class) {
+ case 2:
+ if(!lsa_io_dom_query_2("", &r_q->dom.id2, ps, depth))
+ return False;
+ break;
+ case 3:
+ if(!lsa_io_dom_query_3("", &r_q->dom.id3, ps, depth))
+ return False;
+ break;
+ case 5:
+ if(!lsa_io_dom_query_5("", &r_q->dom.id5, ps, depth))
+ return False;
+ break;
+ case 6:
+ if(!lsa_io_dom_query_6("", &r_q->dom.id6, ps, depth))
+ return False;
+ break;
+ default:
+ /* PANIC! */
+ break;
+ }
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_q->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a LSA_SID_ENUM structure.
+********************************************************************/
+
+static void init_lsa_sid_enum(TALLOC_CTX *mem_ctx, LSA_SID_ENUM *sen,
+ int num_entries, const DOM_SID *sids)
+{
+ int i;
+
+ DEBUG(5, ("init_lsa_sid_enum\n"));
+
+ sen->num_entries = num_entries;
+ sen->ptr_sid_enum = (num_entries != 0);
+ sen->num_entries2 = num_entries;
+
+ /* Allocate memory for sids and sid pointers */
+
+ if (num_entries == 0) return;
+
+ if ((sen->ptr_sid = (uint32 *)talloc_zero(mem_ctx, num_entries *
+ sizeof(uint32))) == NULL) {
+ DEBUG(3, ("init_lsa_sid_enum(): out of memory for ptr_sid\n"));
+ return;
+ }
+
+ if ((sen->sid = (DOM_SID2 *)talloc_zero(mem_ctx, num_entries *
+ sizeof(DOM_SID2))) == NULL) {
+ DEBUG(3, ("init_lsa_sid_enum(): out of memory for sids\n"));
+ return;
+ }
+
+ /* Copy across SIDs and SID pointers */
+
+ for (i = 0; i < num_entries; i++) {
+ sen->ptr_sid[i] = 1;
+ init_dom_sid2(&sen->sid[i], &sids[i]);
+ }
+}
+
+/*******************************************************************
+ Reads or writes a LSA_SID_ENUM structure.
+********************************************************************/
+
+static BOOL lsa_io_sid_enum(const char *desc, LSA_SID_ENUM *sen, prs_struct *ps,
+ int depth)
+{
+ unsigned int i;
+
+ prs_debug(ps, depth, desc, "lsa_io_sid_enum");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_entries ", ps, depth, &sen->num_entries))
+ return False;
+ if(!prs_uint32("ptr_sid_enum", ps, depth, &sen->ptr_sid_enum))
+ return False;
+
+ /*
+ if the ptr is NULL, leave here. checked from a real w2k trace.
+ JFM, 11/23/2001
+ */
+
+ if (sen->ptr_sid_enum==0)
+ return True;
+
+ if(!prs_uint32("num_entries2", ps, depth, &sen->num_entries2))
+ return False;
+
+ /* Mallocate memory if we're unpacking from the wire */
+
+ if (UNMARSHALLING(ps)) {
+ if ((sen->ptr_sid = (uint32 *)prs_alloc_mem( ps,
+ sen->num_entries * sizeof(uint32))) == NULL) {
+ DEBUG(3, ("init_lsa_sid_enum(): out of memory for "
+ "ptr_sid\n"));
+ return False;
+ }
+
+ if ((sen->sid = (DOM_SID2 *)prs_alloc_mem( ps,
+ sen->num_entries * sizeof(DOM_SID2))) == NULL) {
+ DEBUG(3, ("init_lsa_sid_enum(): out of memory for "
+ "sids\n"));
+ return False;
+ }
+ }
+
+ for (i = 0; i < sen->num_entries; i++) {
+ fstring temp;
+
+ slprintf(temp, sizeof(temp) - 1, "ptr_sid[%d]", i);
+ if(!prs_uint32(temp, ps, depth, &sen->ptr_sid[i])) {
+ return False;
+ }
+ }
+
+ for (i = 0; i < sen->num_entries; i++) {
+ fstring temp;
+
+ slprintf(temp, sizeof(temp) - 1, "sid[%d]", i);
+ if(!smb_io_dom_sid2(temp, &sen->sid[i], ps, depth)) {
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an LSA_R_ENUM_TRUST_DOM structure.
+********************************************************************/
+
+void init_q_lookup_sids(TALLOC_CTX *mem_ctx, LSA_Q_LOOKUP_SIDS *q_l,
+ POLICY_HND *hnd, int num_sids, const DOM_SID *sids,
+ uint16 level)
+{
+ DEBUG(5, ("init_q_lookup_sids\n"));
+
+ ZERO_STRUCTP(q_l);
+
+ memcpy(&q_l->pol, hnd, sizeof(q_l->pol));
+ init_lsa_sid_enum(mem_ctx, &q_l->sids, num_sids, sids);
+
+ q_l->level.value = level;
+}
+
+/*******************************************************************
+ Reads or writes a LSA_Q_LOOKUP_SIDS structure.
+********************************************************************/
+
+BOOL lsa_io_q_lookup_sids(const char *desc, LSA_Q_LOOKUP_SIDS *q_s, prs_struct *ps,
+ int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_lookup_sids");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol_hnd", &q_s->pol, ps, depth)) /* policy handle */
+ return False;
+ if(!lsa_io_sid_enum("sids ", &q_s->sids, ps, depth)) /* sids to be looked up */
+ return False;
+ if(!lsa_io_trans_names("names ", &q_s->names, ps, depth)) /* translated names */
+ return False;
+ if(!smb_io_lookup_level("switch ", &q_s->level, ps, depth)) /* lookup level */
+ return False;
+
+ if(!prs_uint32("mapped_count", ps, depth, &q_s->mapped_count))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL lsa_io_trans_names(const char *desc, LSA_TRANS_NAME_ENUM *trn,
+ prs_struct *ps, int depth)
+{
+ unsigned int i;
+
+ prs_debug(ps, depth, desc, "lsa_io_trans_names");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_entries ", ps, depth, &trn->num_entries))
+ return False;
+ if(!prs_uint32("ptr_trans_names", ps, depth, &trn->ptr_trans_names))
+ return False;
+
+ if (trn->ptr_trans_names != 0) {
+ if(!prs_uint32("num_entries2 ", ps, depth,
+ &trn->num_entries2))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ if ((trn->name = (LSA_TRANS_NAME *)
+ prs_alloc_mem(ps, trn->num_entries *
+ sizeof(LSA_TRANS_NAME))) == NULL) {
+ return False;
+ }
+
+ if ((trn->uni_name = (UNISTR2 *)
+ prs_alloc_mem(ps, trn->num_entries *
+ sizeof(UNISTR2))) == NULL) {
+ return False;
+ }
+ }
+
+ for (i = 0; i < trn->num_entries2; i++) {
+ fstring t;
+ slprintf(t, sizeof(t) - 1, "name[%d] ", i);
+
+ if(!lsa_io_trans_name(t, &trn->name[i], ps, depth)) /* translated name */
+ return False;
+ }
+
+ for (i = 0; i < trn->num_entries2; i++) {
+ fstring t;
+ slprintf(t, sizeof(t) - 1, "name[%d] ", i);
+
+ if(!smb_io_unistr2(t, &trn->uni_name[i], trn->name[i].hdr_name.buffer, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL lsa_io_r_lookup_sids(const char *desc, LSA_R_LOOKUP_SIDS *r_s,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_lookup_sids");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_dom_ref", ps, depth, &r_s->ptr_dom_ref))
+ return False;
+
+ if (r_s->ptr_dom_ref != 0)
+ if(!lsa_io_dom_r_ref ("dom_ref", r_s->dom_ref, ps, depth)) /* domain reference info */
+ return False;
+
+ if(!lsa_io_trans_names("names ", r_s->names, ps, depth)) /* translated names */
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("mapped_count", ps, depth, &r_s->mapped_count))
+ return False;
+
+ if(!prs_ntstatus("status ", ps, depth, &r_s->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+makes a structure.
+********************************************************************/
+
+void init_q_lookup_names(TALLOC_CTX *mem_ctx, LSA_Q_LOOKUP_NAMES *q_l,
+ POLICY_HND *hnd, int num_names, const char **names)
+{
+ unsigned int i;
+
+ DEBUG(5, ("init_q_lookup_names\n"));
+
+ ZERO_STRUCTP(q_l);
+
+ q_l->pol = *hnd;
+ q_l->num_entries = num_names;
+ q_l->num_entries2 = num_names;
+ q_l->lookup_level = 1;
+
+ if ((q_l->uni_name = (UNISTR2 *)talloc_zero(
+ mem_ctx, num_names * sizeof(UNISTR2))) == NULL) {
+ DEBUG(3, ("init_q_lookup_names(): out of memory\n"));
+ return;
+ }
+
+ if ((q_l->hdr_name = (UNIHDR *)talloc_zero(
+ mem_ctx, num_names * sizeof(UNIHDR))) == NULL) {
+ DEBUG(3, ("init_q_lookup_names(): out of memory\n"));
+ return;
+ }
+
+ for (i = 0; i < num_names; i++) {
+ init_unistr2(&q_l->uni_name[i], names[i], UNI_FLAGS_NONE);
+ init_uni_hdr(&q_l->hdr_name[i], &q_l->uni_name[i]);
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL lsa_io_q_lookup_names(const char *desc, LSA_Q_LOOKUP_NAMES *q_r,
+ prs_struct *ps, int depth)
+{
+ unsigned int i;
+
+ prs_debug(ps, depth, desc, "lsa_io_q_lookup_names");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &q_r->pol, ps, depth)) /* policy handle */
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("num_entries ", ps, depth, &q_r->num_entries))
+ return False;
+ if(!prs_uint32("num_entries2 ", ps, depth, &q_r->num_entries2))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ if (q_r->num_entries) {
+ if ((q_r->hdr_name = (UNIHDR *)prs_alloc_mem(ps,
+ q_r->num_entries * sizeof(UNIHDR))) == NULL)
+ return False;
+ if ((q_r->uni_name = (UNISTR2 *)prs_alloc_mem(ps,
+ q_r->num_entries * sizeof(UNISTR2))) == NULL)
+ return False;
+ }
+ }
+
+ for (i = 0; i < q_r->num_entries; i++) {
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unihdr("hdr_name", &q_r->hdr_name[i], ps, depth)) /* pointer names */
+ return False;
+ }
+
+ for (i = 0; i < q_r->num_entries; i++) {
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unistr2("dom_name", &q_r->uni_name[i], q_r->hdr_name[i].buffer, ps, depth)) /* names to be looked up */
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("num_trans_entries ", ps, depth, &q_r->num_trans_entries))
+ return False;
+ if(!prs_uint32("ptr_trans_sids ", ps, depth, &q_r->ptr_trans_sids))
+ return False;
+ if(!prs_uint32("lookup_level ", ps, depth, &q_r->lookup_level))
+ return False;
+ if(!prs_uint32("mapped_count ", ps, depth, &q_r->mapped_count))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL lsa_io_r_lookup_names(const char *desc, LSA_R_LOOKUP_NAMES *r_r,
+ prs_struct *ps, int depth)
+{
+ unsigned int i;
+
+ prs_debug(ps, depth, desc, "lsa_io_r_lookup_names");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_dom_ref", ps, depth, &r_r->ptr_dom_ref))
+ return False;
+
+ if (r_r->ptr_dom_ref != 0)
+ if(!lsa_io_dom_r_ref("", r_r->dom_ref, ps, depth))
+ return False;
+
+ if(!prs_uint32("num_entries", ps, depth, &r_r->num_entries))
+ return False;
+ if(!prs_uint32("ptr_entries", ps, depth, &r_r->ptr_entries))
+ return False;
+
+ if (r_r->ptr_entries != 0) {
+ if(!prs_uint32("num_entries2", ps, depth, &r_r->num_entries2))
+ return False;
+
+ if (r_r->num_entries2 != r_r->num_entries) {
+ /* RPC fault */
+ return False;
+ }
+
+ if (UNMARSHALLING(ps)) {
+ if ((r_r->dom_rid = (DOM_RID2 *)prs_alloc_mem(ps, r_r->num_entries2 * sizeof(DOM_RID2)))
+ == NULL) {
+ DEBUG(3, ("lsa_io_r_lookup_names(): out of memory\n"));
+ return False;
+ }
+ }
+
+ for (i = 0; i < r_r->num_entries2; i++)
+ if(!smb_io_dom_rid2("", &r_r->dom_rid[i], ps, depth)) /* domain RIDs being looked up */
+ return False;
+ }
+
+ if(!prs_uint32("mapped_count", ps, depth, &r_r->mapped_count))
+ return False;
+
+ if(!prs_ntstatus("status ", ps, depth, &r_r->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Inits an LSA_Q_CLOSE structure.
+********************************************************************/
+
+void init_lsa_q_close(LSA_Q_CLOSE *q_c, POLICY_HND *hnd)
+{
+ DEBUG(5, ("init_lsa_q_close\n"));
+
+ memcpy(&q_c->pol, hnd, sizeof(q_c->pol));
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_CLOSE structure.
+********************************************************************/
+
+BOOL lsa_io_q_close(const char *desc, LSA_Q_CLOSE *q_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_close");
+ depth++;
+
+ if(!smb_io_pol_hnd("", &q_c->pol, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_CLOSE structure.
+********************************************************************/
+
+BOOL lsa_io_r_close(const char *desc, LSA_R_CLOSE *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_close");
+ depth++;
+
+ if(!smb_io_pol_hnd("", &r_c->pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_OPEN_SECRET structure.
+********************************************************************/
+
+BOOL lsa_io_q_open_secret(const char *desc, LSA_Q_OPEN_SECRET *q_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_open_secret");
+ depth++;
+
+ /* Don't bother to read or write at present... */
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_OPEN_SECRET structure.
+********************************************************************/
+
+BOOL lsa_io_r_open_secret(const char *desc, LSA_R_OPEN_SECRET *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_open_secret");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("dummy1", ps, depth, &r_c->dummy1))
+ return False;
+ if(!prs_uint32("dummy2", ps, depth, &r_c->dummy2))
+ return False;
+ if(!prs_uint32("dummy3", ps, depth, &r_c->dummy3))
+ return False;
+ if(!prs_uint32("dummy4", ps, depth, &r_c->dummy4))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an LSA_Q_ENUM_PRIVS structure.
+********************************************************************/
+
+void init_q_enum_privs(LSA_Q_ENUM_PRIVS *q_q, POLICY_HND *hnd, uint32 enum_context, uint32 pref_max_length)
+{
+ DEBUG(5, ("init_q_enum_privs\n"));
+
+ memcpy(&q_q->pol, hnd, sizeof(q_q->pol));
+
+ q_q->enum_context = enum_context;
+ q_q->pref_max_length = pref_max_length;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL lsa_io_q_enum_privs(const char *desc, LSA_Q_ENUM_PRIVS *q_q, prs_struct *ps, int depth)
+{
+ if (q_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "lsa_io_q_enum_privs");
+ depth++;
+
+ if (!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("enum_context ", ps, depth, &q_q->enum_context))
+ return False;
+ if(!prs_uint32("pref_max_length", ps, depth, &q_q->pref_max_length))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL lsa_io_priv_entries(const char *desc, LSA_PRIV_ENTRY *entries, uint32 count, prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ if (entries == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "lsa_io_priv_entries");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ for (i = 0; i < count; i++) {
+ if (!smb_io_unihdr("", &entries[i].hdr_name, ps, depth))
+ return False;
+ if(!prs_uint32("luid_low ", ps, depth, &entries[i].luid_low))
+ return False;
+ if(!prs_uint32("luid_high", ps, depth, &entries[i].luid_high))
+ return False;
+ }
+
+ for (i = 0; i < count; i++)
+ if (!smb_io_unistr2("", &entries[i].name, entries[i].hdr_name.buffer, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an LSA_R_ENUM_PRIVS structure.
+********************************************************************/
+
+void init_lsa_r_enum_privs(LSA_R_ENUM_PRIVS *r_u, uint32 enum_context,
+ uint32 count, LSA_PRIV_ENTRY *entries)
+{
+ DEBUG(5, ("init_lsa_r_enum_privs\n"));
+
+ r_u->enum_context=enum_context;
+ r_u->count=count;
+
+ if (entries!=NULL) {
+ r_u->ptr=1;
+ r_u->count1=count;
+ r_u->privs=entries;
+ } else {
+ r_u->ptr=0;
+ r_u->count1=0;
+ r_u->privs=NULL;
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL lsa_io_r_enum_privs(const char *desc, LSA_R_ENUM_PRIVS *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "lsa_io_r_enum_privs");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("enum_context", ps, depth, &r_q->enum_context))
+ return False;
+ if(!prs_uint32("count", ps, depth, &r_q->count))
+ return False;
+ if(!prs_uint32("ptr", ps, depth, &r_q->ptr))
+ return False;
+
+ if (r_q->ptr) {
+ if(!prs_uint32("count1", ps, depth, &r_q->count1))
+ return False;
+
+ if (UNMARSHALLING(ps))
+ if (!(r_q->privs = (LSA_PRIV_ENTRY *)prs_alloc_mem(ps, sizeof(LSA_PRIV_ENTRY) * r_q->count1)))
+ return False;
+
+ if (!lsa_io_priv_entries("", r_q->privs, r_q->count1, ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_q->status))
+ return False;
+
+ return True;
+}
+
+void init_lsa_priv_get_dispname(LSA_Q_PRIV_GET_DISPNAME *trn, POLICY_HND *hnd, const char *name, uint16 lang_id, uint16 lang_id_sys)
+{
+ memcpy(&trn->pol, hnd, sizeof(trn->pol));
+
+ init_unistr2(&trn->name, name, UNI_FLAGS_NONE);
+ init_uni_hdr(&trn->hdr_name, &trn->name);
+ trn->lang_id = lang_id;
+ trn->lang_id_sys = lang_id_sys;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL lsa_io_q_priv_get_dispname(const char *desc, LSA_Q_PRIV_GET_DISPNAME *q_q, prs_struct *ps, int depth)
+{
+ if (q_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "lsa_io_q_priv_get_dispname");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+ return False;
+
+ if (!smb_io_unihdr("hdr_name", &q_q->hdr_name, ps, depth))
+ return False;
+
+ if (!smb_io_unistr2("name", &q_q->name, q_q->hdr_name.buffer, ps, depth))
+ return False;
+
+ if(!prs_uint16("lang_id ", ps, depth, &q_q->lang_id))
+ return False;
+ if(!prs_uint16("lang_id_sys", ps, depth, &q_q->lang_id_sys))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL lsa_io_r_priv_get_dispname(const char *desc, LSA_R_PRIV_GET_DISPNAME *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "lsa_io_r_priv_get_dispname");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("ptr_info", ps, depth, &r_q->ptr_info))
+ return False;
+
+ if (r_q->ptr_info){
+ if (!smb_io_unihdr("hdr_name", &r_q->hdr_desc, ps, depth))
+ return False;
+
+ if (!smb_io_unistr2("desc", &r_q->desc, r_q->hdr_desc.buffer, ps, depth))
+ return False;
+ }
+/*
+ if(!prs_align(ps))
+ return False;
+*/
+ if(!prs_uint16("lang_id", ps, depth, &r_q->lang_id))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_q->status))
+ return False;
+
+ return True;
+}
+
+/*
+ initialise a LSA_Q_ENUM_ACCOUNTS structure
+*/
+void init_lsa_q_enum_accounts(LSA_Q_ENUM_ACCOUNTS *trn, POLICY_HND *hnd, uint32 enum_context, uint32 pref_max_length)
+{
+ memcpy(&trn->pol, hnd, sizeof(trn->pol));
+
+ trn->enum_context = enum_context;
+ trn->pref_max_length = pref_max_length;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL lsa_io_q_enum_accounts(const char *desc, LSA_Q_ENUM_ACCOUNTS *q_q, prs_struct *ps, int depth)
+{
+ if (q_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "lsa_io_q_enum_accounts");
+ depth++;
+
+ if (!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("enum_context ", ps, depth, &q_q->enum_context))
+ return False;
+ if(!prs_uint32("pref_max_length", ps, depth, &q_q->pref_max_length))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Inits an LSA_R_ENUM_PRIVS structure.
+********************************************************************/
+
+void init_lsa_r_enum_accounts(LSA_R_ENUM_ACCOUNTS *r_u, uint32 enum_context)
+{
+ DEBUG(5, ("init_lsa_r_enum_accounts\n"));
+
+ r_u->enum_context=enum_context;
+ if (r_u->enum_context!=0) {
+ r_u->sids.num_entries=enum_context;
+ r_u->sids.ptr_sid_enum=1;
+ r_u->sids.num_entries2=enum_context;
+ } else {
+ r_u->sids.num_entries=0;
+ r_u->sids.ptr_sid_enum=0;
+ r_u->sids.num_entries2=0;
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL lsa_io_r_enum_accounts(const char *desc, LSA_R_ENUM_ACCOUNTS *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "lsa_io_r_enum_accounts");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("enum_context", ps, depth, &r_q->enum_context))
+ return False;
+
+ if (!lsa_io_sid_enum("sids", &r_q->sids, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_q->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Reads or writes an LSA_Q_UNK_GET_CONNUSER structure.
+********************************************************************/
+
+BOOL lsa_io_q_unk_get_connuser(const char *desc, LSA_Q_UNK_GET_CONNUSER *q_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_unk_get_connuser");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srvname", ps, depth, &q_c->ptr_srvname))
+ return False;
+
+ if(!smb_io_unistr2("uni2_srvname", &q_c->uni2_srvname, q_c->ptr_srvname, ps, depth)) /* server name to be looked up */
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("unk1", ps, depth, &q_c->unk1))
+ return False;
+ if(!prs_uint32("unk2", ps, depth, &q_c->unk2))
+ return False;
+ if(!prs_uint32("unk3", ps, depth, &q_c->unk3))
+ return False;
+
+ /* Don't bother to read or write at present... */
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_UNK_GET_CONNUSER structure.
+********************************************************************/
+
+BOOL lsa_io_r_unk_get_connuser(const char *desc, LSA_R_UNK_GET_CONNUSER *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_unk_get_connuser");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_user_name", ps, depth, &r_c->ptr_user_name))
+ return False;
+ if(!smb_io_unihdr("hdr_user_name", &r_c->hdr_user_name, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni2_user_name", &r_c->uni2_user_name, r_c->ptr_user_name, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("unk1", ps, depth, &r_c->unk1))
+ return False;
+
+ if(!prs_uint32("ptr_dom_name", ps, depth, &r_c->ptr_dom_name))
+ return False;
+ if(!smb_io_unihdr("hdr_dom_name", &r_c->hdr_dom_name, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni2_dom_name", &r_c->uni2_dom_name, r_c->ptr_dom_name, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+void init_lsa_q_create_account(LSA_Q_CREATEACCOUNT *trn, POLICY_HND *hnd, DOM_SID *sid, uint32 desired_access)
+{
+ memcpy(&trn->pol, hnd, sizeof(trn->pol));
+
+ init_dom_sid2(&trn->sid, sid);
+ trn->access = desired_access;
+}
+
+
+/*******************************************************************
+ Reads or writes an LSA_Q_CREATEACCOUNT structure.
+********************************************************************/
+
+BOOL lsa_io_q_create_account(const char *desc, LSA_Q_CREATEACCOUNT *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_create_account");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+ return False;
+
+ if(!smb_io_dom_sid2("sid", &r_c->sid, ps, depth)) /* domain SID */
+ return False;
+
+ if(!prs_uint32("access", ps, depth, &r_c->access))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_CREATEACCOUNT structure.
+********************************************************************/
+
+BOOL lsa_io_r_create_account(const char *desc, LSA_R_CREATEACCOUNT *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_open_account");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+
+void init_lsa_q_open_account(LSA_Q_OPENACCOUNT *trn, POLICY_HND *hnd, DOM_SID *sid, uint32 desired_access)
+{
+ memcpy(&trn->pol, hnd, sizeof(trn->pol));
+
+ init_dom_sid2(&trn->sid, sid);
+ trn->access = desired_access;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_OPENACCOUNT structure.
+********************************************************************/
+
+BOOL lsa_io_q_open_account(const char *desc, LSA_Q_OPENACCOUNT *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_open_account");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+ return False;
+
+ if(!smb_io_dom_sid2("sid", &r_c->sid, ps, depth)) /* domain SID */
+ return False;
+
+ if(!prs_uint32("access", ps, depth, &r_c->access))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_OPENACCOUNT structure.
+********************************************************************/
+
+BOOL lsa_io_r_open_account(const char *desc, LSA_R_OPENACCOUNT *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_open_account");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+
+void init_lsa_q_enum_privsaccount(LSA_Q_ENUMPRIVSACCOUNT *trn, POLICY_HND *hnd)
+{
+ memcpy(&trn->pol, hnd, sizeof(trn->pol));
+
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_ENUMPRIVSACCOUNT structure.
+********************************************************************/
+
+BOOL lsa_io_q_enum_privsaccount(const char *desc, LSA_Q_ENUMPRIVSACCOUNT *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_enum_privsaccount");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LUID structure.
+********************************************************************/
+
+static BOOL lsa_io_luid(const char *desc, LUID *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_luid");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("low", ps, depth, &r_c->low))
+ return False;
+
+ if(!prs_uint32("high", ps, depth, &r_c->high))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LUID_ATTR structure.
+********************************************************************/
+
+static BOOL lsa_io_luid_attr(const char *desc, LUID_ATTR *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_luid_attr");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (!lsa_io_luid(desc, &r_c->luid, ps, depth))
+ return False;
+
+ if(!prs_uint32("attr", ps, depth, &r_c->attr))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an PRIVILEGE_SET structure.
+********************************************************************/
+
+static BOOL lsa_io_privilege_set(const char *desc, PRIVILEGE_SET *r_c, prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ prs_debug(ps, depth, desc, "lsa_io_privilege_set");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("count", ps, depth, &r_c->count))
+ return False;
+ if(!prs_uint32("control", ps, depth, &r_c->control))
+ return False;
+
+ for (i=0; i<r_c->count; i++) {
+ if (!lsa_io_luid_attr(desc, &r_c->set[i], ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+NTSTATUS init_lsa_r_enum_privsaccount(TALLOC_CTX *mem_ctx, LSA_R_ENUMPRIVSACCOUNT *r_u, LUID_ATTR *set, uint32 count, uint32 control)
+{
+ NTSTATUS ret = NT_STATUS_OK;
+
+ r_u->ptr = 1;
+ r_u->count = count;
+
+ if (!NT_STATUS_IS_OK(ret = init_priv_with_ctx(mem_ctx, &(r_u->set))))
+ return ret;
+
+ r_u->set->count = count;
+
+ if (!NT_STATUS_IS_OK(ret = dupalloc_luid_attr(r_u->set->mem_ctx, &(r_u->set->set), set, count)))
+ return ret;
+
+ DEBUG(10,("init_lsa_r_enum_privsaccount: %d privileges\n", r_u->count));
+
+ return ret;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_ENUMPRIVSACCOUNT structure.
+********************************************************************/
+
+BOOL lsa_io_r_enum_privsaccount(const char *desc, LSA_R_ENUMPRIVSACCOUNT *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_enum_privsaccount");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr", ps, depth, &r_c->ptr))
+ return False;
+
+ if (r_c->ptr!=0) {
+ if(!prs_uint32("count", ps, depth, &r_c->count))
+ return False;
+
+ /* malloc memory if unmarshalling here */
+
+ if (UNMARSHALLING(ps) && r_c->count != 0) {
+ if (!NT_STATUS_IS_OK(init_priv_with_ctx(ps->mem_ctx, &(r_c->set))))
+ return False;
+
+ if (!(r_c->set->set = (LUID_ATTR *)prs_alloc_mem(ps,sizeof(LUID_ATTR) * r_c->count)))
+ return False;
+
+ }
+
+ if(!lsa_io_privilege_set(desc, r_c->set, ps, depth))
+ return False;
+ }
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+
+
+/*******************************************************************
+ Reads or writes an LSA_Q_GETSYSTEMACCOUNTstructure.
+********************************************************************/
+
+BOOL lsa_io_q_getsystemaccount(const char *desc, LSA_Q_GETSYSTEMACCOUNT *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_getsystemaccount");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_GETSYSTEMACCOUNTstructure.
+********************************************************************/
+
+BOOL lsa_io_r_getsystemaccount(const char *desc, LSA_R_GETSYSTEMACCOUNT *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_getsystemaccount");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("access", ps, depth, &r_c->access))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Reads or writes an LSA_Q_SETSYSTEMACCOUNT structure.
+********************************************************************/
+
+BOOL lsa_io_q_setsystemaccount(const char *desc, LSA_Q_SETSYSTEMACCOUNT *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_setsystemaccount");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("access", ps, depth, &r_c->access))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_SETSYSTEMACCOUNT structure.
+********************************************************************/
+
+BOOL lsa_io_r_setsystemaccount(const char *desc, LSA_R_SETSYSTEMACCOUNT *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_setsystemaccount");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+
+void init_lsa_q_lookupprivvalue(LSA_Q_LOOKUPPRIVVALUE *trn, POLICY_HND *hnd, const char *name)
+{
+ memcpy(&trn->pol, hnd, sizeof(trn->pol));
+ init_unistr2(&trn->uni2_right, name, UNI_FLAGS_NONE);
+ init_uni_hdr(&trn->hdr_right, &trn->uni2_right);
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_LOOKUPPRIVVALUE structure.
+********************************************************************/
+
+BOOL lsa_io_q_lookupprivvalue(const char *desc, LSA_Q_LOOKUPPRIVVALUE *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_lookupprivvalue");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+ return False;
+ if(!smb_io_unihdr ("hdr_name", &r_c->hdr_right, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni2_right", &r_c->uni2_right, r_c->hdr_right.buffer, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_LOOKUPPRIVVALUE structure.
+********************************************************************/
+
+BOOL lsa_io_r_lookupprivvalue(const char *desc, LSA_R_LOOKUPPRIVVALUE *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_lookupprivvalue");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!lsa_io_luid("luid", &r_c->luid, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Reads or writes an LSA_Q_ADDPRIVS structure.
+********************************************************************/
+
+BOOL lsa_io_q_addprivs(const char *desc, LSA_Q_ADDPRIVS *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_addprivs");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("count", ps, depth, &r_c->count))
+ return False;
+
+ if (UNMARSHALLING(ps) && r_c->count!=0) {
+ if (!NT_STATUS_IS_OK(init_priv_with_ctx(ps->mem_ctx, &(r_c->set))))
+ return False;
+
+ if (!(r_c->set->set = (LUID_ATTR *)prs_alloc_mem(ps, sizeof(LUID_ATTR) * r_c->count)))
+ return False;
+ }
+
+ if(!lsa_io_privilege_set(desc, r_c->set, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_ADDPRIVS structure.
+********************************************************************/
+
+BOOL lsa_io_r_addprivs(const char *desc, LSA_R_ADDPRIVS *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_addprivs");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_REMOVEPRIVS structure.
+********************************************************************/
+
+BOOL lsa_io_q_removeprivs(const char *desc, LSA_Q_REMOVEPRIVS *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_removeprivs");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("allrights", ps, depth, &r_c->allrights))
+ return False;
+
+ if(!prs_uint32("ptr", ps, depth, &r_c->ptr))
+ return False;
+
+ /*
+ * JFM: I'm not sure at all if the count is inside the ptr
+ * never seen one with ptr=0
+ */
+
+ if (r_c->ptr!=0) {
+ if(!prs_uint32("count", ps, depth, &r_c->count))
+ return False;
+
+ if (UNMARSHALLING(ps) && r_c->count!=0) {
+ if (!NT_STATUS_IS_OK(init_priv_with_ctx(ps->mem_ctx, &(r_c->set))))
+ return False;
+
+ if (!(r_c->set->set = (LUID_ATTR *)prs_alloc_mem(ps, sizeof(LUID_ATTR) * r_c->count)))
+ return False;
+ }
+
+ if(!lsa_io_privilege_set(desc, r_c->set, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_REMOVEPRIVS structure.
+********************************************************************/
+
+BOOL lsa_io_r_removeprivs(const char *desc, LSA_R_REMOVEPRIVS *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_removeprivs");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+BOOL policy_handle_is_valid(const POLICY_HND *hnd)
+{
+ POLICY_HND zero_pol;
+
+ ZERO_STRUCT(zero_pol);
+ return ((memcmp(&zero_pol, hnd, sizeof(POLICY_HND)) == 0) ? False : True );
+}
+
+/*******************************************************************
+ Reads or writes an LSA_DNS_DOM_INFO structure.
+********************************************************************/
+
+BOOL lsa_io_dns_dom_info(const char *desc, LSA_DNS_DOM_INFO *info,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_dns_dom_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unihdr("nb_name", &info->hdr_nb_dom_name, ps, depth))
+ return False;
+ if(!smb_io_unihdr("dns_name", &info->hdr_dns_dom_name, ps, depth))
+ return False;
+ if(!smb_io_unihdr("forest", &info->hdr_forest_name, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if ( !smb_io_uuid("dom_guid", &info->dom_guid, ps, depth) )
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("dom_sid", ps, depth, &info->ptr_dom_sid))
+ return False;
+
+ if(!smb_io_unistr2("nb_name", &info->uni_nb_dom_name,
+ info->hdr_nb_dom_name.buffer, ps, depth))
+ return False;
+ if(!smb_io_unistr2("dns_name", &info->uni_dns_dom_name,
+ info->hdr_dns_dom_name.buffer, ps, depth))
+ return False;
+ if(!smb_io_unistr2("forest", &info->uni_forest_name,
+ info->hdr_forest_name.buffer, ps, depth))
+ return False;
+
+ if(!smb_io_dom_sid2("dom_sid", &info->dom_sid, ps, depth))
+ return False;
+
+ return True;
+
+}
+
+/*******************************************************************
+ Inits an LSA_Q_QUERY_INFO2 structure.
+********************************************************************/
+
+void init_q_query2(LSA_Q_QUERY_INFO2 *q_q, POLICY_HND *hnd, uint16 info_class)
+{
+ DEBUG(5, ("init_q_query2\n"));
+
+ memcpy(&q_q->pol, hnd, sizeof(q_q->pol));
+
+ q_q->info_class = info_class;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_QUERY_DNSDOMINFO structure.
+********************************************************************/
+
+BOOL lsa_io_q_query_info2(const char *desc, LSA_Q_QUERY_INFO2 *q_c,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_query_info2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_c->pol, ps, depth))
+ return False;
+
+ if(!prs_uint16("info_class", ps, depth, &q_c->info_class))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_QUERY_DNSDOMINFO structure.
+********************************************************************/
+
+BOOL lsa_io_r_query_info2(const char *desc, LSA_R_QUERY_INFO2 *r_c,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_query_info2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr", ps, depth, &r_c->ptr))
+ return False;
+ if(!prs_uint16("info_class", ps, depth, &r_c->info_class))
+ return False;
+ switch(r_c->info_class) {
+ case 0x000c:
+ if (!lsa_io_dns_dom_info("info12", &r_c->info.dns_dom_info,
+ ps, depth))
+ return False;
+ break;
+ default:
+ DEBUG(0,("lsa_io_r_query_info2: unknown info class %d\n",
+ r_c->info_class));
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Inits an LSA_Q_ENUM_ACCT_RIGHTS structure.
+********************************************************************/
+void init_q_enum_acct_rights(LSA_Q_ENUM_ACCT_RIGHTS *q_q,
+ POLICY_HND *hnd,
+ uint32 count,
+ DOM_SID *sid)
+{
+ DEBUG(5, ("init_q_enum_acct_rights\n"));
+
+ q_q->pol = *hnd;
+ init_dom_sid2(&q_q->sid, sid);
+}
+
+/*******************************************************************
+reads or writes a LSA_Q_ENUM_ACCT_RIGHTS structure.
+********************************************************************/
+BOOL lsa_io_q_enum_acct_rights(const char *desc, LSA_Q_ENUM_ACCT_RIGHTS *q_q, prs_struct *ps, int depth)
+{
+
+ if (q_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "lsa_io_q_enum_acct_rights");
+ depth++;
+
+ if (!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+ return False;
+
+ if(!smb_io_dom_sid2("sid", &q_q->sid, ps, depth))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+reads or writes a LSA_R_ENUM_ACCT_RIGHTS structure.
+********************************************************************/
+BOOL lsa_io_r_enum_acct_rights(const char *desc, LSA_R_ENUM_ACCT_RIGHTS *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_enum_acct_rights");
+ depth++;
+
+ if(!prs_uint32("count ", ps, depth, &r_c->count))
+ return False;
+
+ if(!smb_io_unistr2_array("rights", &r_c->rights, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Inits an LSA_Q_ADD_ACCT_RIGHTS structure.
+********************************************************************/
+void init_q_add_acct_rights(LSA_Q_ADD_ACCT_RIGHTS *q_q,
+ POLICY_HND *hnd,
+ DOM_SID *sid,
+ uint32 count,
+ const char **rights)
+{
+ DEBUG(5, ("init_q_add_acct_rights\n"));
+
+ q_q->pol = *hnd;
+ init_dom_sid2(&q_q->sid, sid);
+ init_unistr2_array(&q_q->rights, count, rights);
+ q_q->count = 5;
+}
+
+
+/*******************************************************************
+reads or writes a LSA_Q_ADD_ACCT_RIGHTS structure.
+********************************************************************/
+BOOL lsa_io_q_add_acct_rights(const char *desc, LSA_Q_ADD_ACCT_RIGHTS *q_q, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_add_acct_rights");
+ depth++;
+
+ if (!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+ return False;
+
+ if(!smb_io_dom_sid2("sid", &q_q->sid, ps, depth))
+ return False;
+
+ if(!prs_uint32("count", ps, depth, &q_q->rights.count))
+ return False;
+
+ if(!smb_io_unistr2_array("rights", &q_q->rights, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a LSA_R_ENUM_ACCT_RIGHTS structure.
+********************************************************************/
+BOOL lsa_io_r_add_acct_rights(const char *desc, LSA_R_ADD_ACCT_RIGHTS *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_add_acct_rights");
+ depth++;
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Inits an LSA_Q_REMOVE_ACCT_RIGHTS structure.
+********************************************************************/
+void init_q_remove_acct_rights(LSA_Q_REMOVE_ACCT_RIGHTS *q_q,
+ POLICY_HND *hnd,
+ DOM_SID *sid,
+ uint32 removeall,
+ uint32 count,
+ const char **rights)
+{
+ DEBUG(5, ("init_q_remove_acct_rights\n"));
+
+ q_q->pol = *hnd;
+ init_dom_sid2(&q_q->sid, sid);
+ q_q->removeall = removeall;
+ init_unistr2_array(&q_q->rights, count, rights);
+ q_q->count = 5;
+}
+
+
+/*******************************************************************
+reads or writes a LSA_Q_REMOVE_ACCT_RIGHTS structure.
+********************************************************************/
+BOOL lsa_io_q_remove_acct_rights(const char *desc, LSA_Q_REMOVE_ACCT_RIGHTS *q_q, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_remove_acct_rights");
+ depth++;
+
+ if (!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+ return False;
+
+ if(!smb_io_dom_sid2("sid", &q_q->sid, ps, depth))
+ return False;
+
+ if(!prs_uint32("removeall", ps, depth, &q_q->removeall))
+ return False;
+
+ if(!prs_uint32("count", ps, depth, &q_q->rights.count))
+ return False;
+
+ if(!smb_io_unistr2_array("rights", &q_q->rights, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a LSA_R_ENUM_ACCT_RIGHTS structure.
+********************************************************************/
+BOOL lsa_io_r_remove_acct_rights(const char *desc, LSA_R_REMOVE_ACCT_RIGHTS *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_remove_acct_rights");
+ depth++;
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
diff --git a/source/rpc_parse/parse_misc.c b/source/rpc_parse/parse_misc.c
new file mode 100644
index 00000000000..cea31c88a80
--- /dev/null
+++ b/source/rpc_parse/parse_misc.c
@@ -0,0 +1,1765 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/****************************************************************************
+ A temporary TALLOC context for things like unistrs, that is valid for
+ the life of a complete RPC call.
+****************************************************************************/
+
+static TALLOC_CTX *current_rpc_talloc = NULL;
+
+static TALLOC_CTX *get_current_rpc_talloc(void)
+{
+ return current_rpc_talloc;
+}
+
+void set_current_rpc_talloc( TALLOC_CTX *ctx)
+{
+ current_rpc_talloc = ctx;
+}
+
+static TALLOC_CTX *main_loop_talloc = NULL;
+
+/*******************************************************************
+free up temporary memory - called from the main loop
+********************************************************************/
+
+void main_loop_talloc_free(void)
+{
+ if (!main_loop_talloc)
+ return;
+ talloc_destroy(main_loop_talloc);
+ main_loop_talloc = NULL;
+}
+
+/*******************************************************************
+ Get a talloc context that is freed in the main loop...
+********************************************************************/
+
+TALLOC_CTX *main_loop_talloc_get(void)
+{
+ if (!main_loop_talloc) {
+ main_loop_talloc = talloc_init("main loop talloc (mainly parse_misc)");
+ if (!main_loop_talloc)
+ smb_panic("main_loop_talloc: malloc fail\n");
+ }
+
+ return main_loop_talloc;
+}
+
+/*******************************************************************
+ Try and get a talloc context. Get the rpc one if possible, else
+ get the main loop one. The main loop one is more dangerous as it
+ goes away between packets, the rpc one will stay around for as long
+ as a current RPC lasts.
+********************************************************************/
+
+TALLOC_CTX *get_talloc_ctx(void)
+{
+ TALLOC_CTX *tc = get_current_rpc_talloc();
+
+ if (tc)
+ return tc;
+ return main_loop_talloc_get();
+}
+
+/*******************************************************************
+ Reads or writes a UTIME type.
+********************************************************************/
+
+static BOOL smb_io_utime(const char *desc, UTIME *t, prs_struct *ps, int depth)
+{
+ if (t == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_utime");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32 ("time", ps, depth, &t->time))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an NTTIME structure.
+********************************************************************/
+
+BOOL smb_io_time(const char *desc, NTTIME *nttime, prs_struct *ps, int depth)
+{
+ if (nttime == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_time");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("low ", ps, depth, &nttime->low)) /* low part */
+ return False;
+ if(!prs_uint32("high", ps, depth, &nttime->high)) /* high part */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a LOOKUP_LEVEL structure.
+********************************************************************/
+
+BOOL smb_io_lookup_level(const char *desc, LOOKUP_LEVEL *level, prs_struct *ps, int depth)
+{
+ if (level == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_lookup_level");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint16("value", ps, depth, &level->value))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Gets an enumeration handle from an ENUM_HND structure.
+********************************************************************/
+
+uint32 get_enum_hnd(ENUM_HND *enh)
+{
+ return (enh && enh->ptr_hnd != 0) ? enh->handle : 0;
+}
+
+/*******************************************************************
+ Inits an ENUM_HND structure.
+********************************************************************/
+
+void init_enum_hnd(ENUM_HND *enh, uint32 hnd)
+{
+ DEBUG(5,("smb_io_enum_hnd\n"));
+
+ enh->ptr_hnd = (hnd != 0) ? 1 : 0;
+ enh->handle = hnd;
+}
+
+/*******************************************************************
+ Reads or writes an ENUM_HND structure.
+********************************************************************/
+
+BOOL smb_io_enum_hnd(const char *desc, ENUM_HND *hnd, prs_struct *ps, int depth)
+{
+ if (hnd == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_enum_hnd");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_hnd", ps, depth, &hnd->ptr_hnd)) /* pointer */
+ return False;
+
+ if (hnd->ptr_hnd != 0) {
+ if(!prs_uint32("handle ", ps, depth, &hnd->handle )) /* enum handle */
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_SID structure.
+********************************************************************/
+
+BOOL smb_io_dom_sid(const char *desc, DOM_SID *sid, prs_struct *ps, int depth)
+{
+ int i;
+
+ if (sid == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_dom_sid");
+ depth++;
+
+ if(!prs_uint8 ("sid_rev_num", ps, depth, &sid->sid_rev_num))
+ return False;
+
+ if(!prs_uint8 ("num_auths ", ps, depth, &sid->num_auths))
+ return False;
+
+ for (i = 0; i < 6; i++)
+ {
+ fstring tmp;
+ slprintf(tmp, sizeof(tmp) - 1, "id_auth[%d] ", i);
+ if(!prs_uint8 (tmp, ps, depth, &sid->id_auth[i]))
+ return False;
+ }
+
+ /* oops! XXXX should really issue a warning here... */
+ if (sid->num_auths > MAXSUBAUTHS)
+ sid->num_auths = MAXSUBAUTHS;
+
+ if(!prs_uint32s(False, "sub_auths ", ps, depth, sid->sub_auths, sid->num_auths))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a DOM_SID structure.
+
+ BIG NOTE: this function only does SIDS where the identauth is not >= 2^32
+ identauth >= 2^32 can be detected because it will be specified in hex
+********************************************************************/
+
+void init_dom_sid(DOM_SID *sid, const char *str_sid)
+{
+ pstring domsid;
+ int identauth;
+ char *p;
+
+ if (str_sid == NULL) {
+ DEBUG(4,("netlogon domain SID: none\n"));
+ sid->sid_rev_num = 0;
+ sid->num_auths = 0;
+ return;
+ }
+
+ pstrcpy(domsid, str_sid);
+
+ DEBUG(4,("init_dom_sid %d SID: %s\n", __LINE__, domsid));
+
+ /* assume, but should check, that domsid starts "S-" */
+ p = strtok(domsid+2,"-");
+ sid->sid_rev_num = atoi(p);
+
+ /* identauth in decimal should be < 2^32 */
+ /* identauth in hex should be >= 2^32 */
+ identauth = atoi(strtok(0,"-"));
+
+ DEBUG(4,("netlogon rev %d\n", sid->sid_rev_num));
+ DEBUG(4,("netlogon %s ia %d\n", p, identauth));
+
+ sid->id_auth[0] = 0;
+ sid->id_auth[1] = 0;
+ sid->id_auth[2] = (identauth & 0xff000000) >> 24;
+ sid->id_auth[3] = (identauth & 0x00ff0000) >> 16;
+ sid->id_auth[4] = (identauth & 0x0000ff00) >> 8;
+ sid->id_auth[5] = (identauth & 0x000000ff);
+
+ sid->num_auths = 0;
+
+ while ((p = strtok(0, "-")) != NULL && sid->num_auths < MAXSUBAUTHS)
+ sid->sub_auths[sid->num_auths++] = atoi(p);
+
+ DEBUG(4,("init_dom_sid: %d SID: %s\n", __LINE__, domsid));
+}
+
+/*******************************************************************
+ Inits a DOM_SID2 structure.
+********************************************************************/
+
+void init_dom_sid2(DOM_SID2 *sid2, const DOM_SID *sid)
+{
+ sid2->sid = *sid;
+ sid2->num_auths = sid2->sid.num_auths;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_SID2 structure.
+********************************************************************/
+
+BOOL smb_io_dom_sid2(const char *desc, DOM_SID2 *sid, prs_struct *ps, int depth)
+{
+ if (sid == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_dom_sid2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_auths", ps, depth, &sid->num_auths))
+ return False;
+
+ if(!smb_io_dom_sid("sid", &sid->sid, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a struct uuid
+********************************************************************/
+
+BOOL smb_io_uuid(const char *desc, struct uuid *uuid,
+ prs_struct *ps, int depth)
+{
+ if (uuid == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_uuid");
+ depth++;
+
+ if(!prs_uint32 ("data ", ps, depth, &uuid->time_low))
+ return False;
+ if(!prs_uint16 ("data ", ps, depth, &uuid->time_mid))
+ return False;
+ if(!prs_uint16 ("data ", ps, depth, &uuid->time_hi_and_version))
+ return False;
+
+ if(!prs_uint8s (False, "data ", ps, depth, uuid->clock_seq, sizeof(uuid->clock_seq)))
+ return False;
+ if(!prs_uint8s (False, "data ", ps, depth, uuid->node, sizeof(uuid->node)))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+creates a STRHDR structure.
+********************************************************************/
+
+void init_str_hdr(STRHDR *hdr, int max_len, int len, uint32 buffer)
+{
+ hdr->str_max_len = max_len;
+ hdr->str_str_len = len;
+ hdr->buffer = buffer;
+}
+
+/*******************************************************************
+ Reads or writes a STRHDR structure.
+********************************************************************/
+
+BOOL smb_io_strhdr(const char *desc, STRHDR *hdr, prs_struct *ps, int depth)
+{
+ if (hdr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_strhdr");
+ depth++;
+
+ prs_align(ps);
+
+ if(!prs_uint16("str_str_len", ps, depth, &hdr->str_str_len))
+ return False;
+ if(!prs_uint16("str_max_len", ps, depth, &hdr->str_max_len))
+ return False;
+ if(!prs_uint32("buffer ", ps, depth, &hdr->buffer))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a UNIHDR structure.
+********************************************************************/
+
+void init_uni_hdr(UNIHDR *hdr, UNISTR2 *str2)
+{
+ hdr->uni_str_len = 2 * (str2->uni_str_len);
+ hdr->uni_max_len = 2 * (str2->uni_max_len);
+ hdr->buffer = (str2->uni_str_len != 0) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a UNIHDR structure.
+********************************************************************/
+
+BOOL smb_io_unihdr(const char *desc, UNIHDR *hdr, prs_struct *ps, int depth)
+{
+ if (hdr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_unihdr");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint16("uni_str_len", ps, depth, &hdr->uni_str_len))
+ return False;
+ if(!prs_uint16("uni_max_len", ps, depth, &hdr->uni_max_len))
+ return False;
+ if(!prs_uint32("buffer ", ps, depth, &hdr->buffer))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a BUFHDR structure.
+********************************************************************/
+
+void init_buf_hdr(BUFHDR *hdr, int max_len, int len)
+{
+ hdr->buf_max_len = max_len;
+ hdr->buf_len = len;
+}
+
+/*******************************************************************
+ prs_uint16 wrapper. Call this and it sets up a pointer to where the
+ uint16 should be stored, or gets the size if reading.
+ ********************************************************************/
+
+BOOL smb_io_hdrbuf_pre(const char *desc, BUFHDR *hdr, prs_struct *ps, int depth, uint32 *offset)
+{
+ (*offset) = prs_offset(ps);
+ if (ps->io) {
+
+ /* reading. */
+
+ if(!smb_io_hdrbuf(desc, hdr, ps, depth))
+ return False;
+
+ } else {
+
+ /* writing. */
+
+ if(!prs_set_offset(ps, prs_offset(ps) + (sizeof(uint32) * 2)))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ smb_io_hdrbuf wrapper. Call this and it retrospectively stores the size.
+ Does nothing on reading, as that is already handled by ...._pre()
+ ********************************************************************/
+
+BOOL smb_io_hdrbuf_post(const char *desc, BUFHDR *hdr, prs_struct *ps, int depth,
+ uint32 ptr_hdrbuf, uint32 max_len, uint32 len)
+{
+ if (!ps->io) {
+ /* writing: go back and do a retrospective job. i hate this */
+
+ uint32 old_offset = prs_offset(ps);
+
+ init_buf_hdr(hdr, max_len, len);
+ if(!prs_set_offset(ps, ptr_hdrbuf))
+ return False;
+ if(!smb_io_hdrbuf(desc, hdr, ps, depth))
+ return False;
+
+ if(!prs_set_offset(ps, old_offset))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a BUFHDR structure.
+********************************************************************/
+
+BOOL smb_io_hdrbuf(const char *desc, BUFHDR *hdr, prs_struct *ps, int depth)
+{
+ if (hdr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_hdrbuf");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("buf_max_len", ps, depth, &hdr->buf_max_len))
+ return False;
+ if(!prs_uint32("buf_len ", ps, depth, &hdr->buf_len))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+creates a UNIHDR2 structure.
+********************************************************************/
+
+void init_uni_hdr2(UNIHDR2 *hdr, UNISTR2 *str2)
+{
+ init_uni_hdr(&hdr->unihdr, str2);
+ hdr->buffer = (str2->uni_str_len > 0) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a UNIHDR2 structure.
+********************************************************************/
+
+BOOL smb_io_unihdr2(const char *desc, UNIHDR2 *hdr2, prs_struct *ps, int depth)
+{
+ if (hdr2 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_unihdr2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unihdr("hdr", &hdr2->unihdr, ps, depth))
+ return False;
+ if(!prs_uint32("buffer", ps, depth, &hdr2->buffer))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a UNISTR structure.
+********************************************************************/
+
+void init_unistr(UNISTR *str, const char *buf)
+{
+ size_t len;
+
+ if (buf == NULL) {
+ str->buffer = NULL;
+ return;
+ }
+
+
+ len = strlen(buf) + 1;
+
+ if (len < MAX_UNISTRLEN)
+ len = MAX_UNISTRLEN;
+ len *= sizeof(uint16);
+
+ str->buffer = (uint16 *)talloc_zero(get_talloc_ctx(), len);
+ if (str->buffer == NULL)
+ smb_panic("init_unistr: malloc fail\n");
+
+ rpcstr_push(str->buffer, buf, len, STR_TERMINATE);
+}
+
+/*******************************************************************
+reads or writes a UNISTR structure.
+XXXX NOTE: UNISTR structures NEED to be null-terminated.
+********************************************************************/
+
+BOOL smb_io_unistr(const char *desc, UNISTR *uni, prs_struct *ps, int depth)
+{
+ if (uni == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_unistr");
+ depth++;
+
+ if(!prs_unistr("unistr", ps, depth, uni))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Allocate the BUFFER3 memory.
+********************************************************************/
+
+static void create_buffer3(BUFFER3 *str, size_t len)
+{
+ if (len < MAX_BUFFERLEN)
+ len = MAX_BUFFERLEN;
+
+ str->buffer = talloc_zero(get_talloc_ctx(), len);
+ if (str->buffer == NULL)
+ smb_panic("create_buffer3: talloc fail\n");
+
+}
+
+/*******************************************************************
+ Inits a BUFFER3 structure from a uint32
+********************************************************************/
+
+void init_buffer3_uint32(BUFFER3 *str, uint32 val)
+{
+ ZERO_STRUCTP(str);
+
+ /* set up string lengths. */
+ str->buf_max_len = sizeof(uint32);
+ str->buf_len = sizeof(uint32);
+
+ create_buffer3(str, sizeof(uint32));
+ SIVAL(str->buffer, 0, val);
+}
+
+/*******************************************************************
+ Inits a BUFFER3 structure.
+********************************************************************/
+
+void init_buffer3_str(BUFFER3 *str, const char *buf, int len)
+{
+ ZERO_STRUCTP(str);
+
+ /* set up string lengths. */
+ str->buf_max_len = len * 2;
+ str->buf_len = len * 2;
+
+ create_buffer3(str, str->buf_max_len);
+
+ rpcstr_push(str->buffer, buf, str->buf_max_len, STR_TERMINATE);
+
+}
+
+/*******************************************************************
+ Inits a BUFFER3 structure from a hex string.
+********************************************************************/
+
+void init_buffer3_hex(BUFFER3 *str, const char *buf)
+{
+ ZERO_STRUCTP(str);
+ create_buffer3(str, strlen(buf));
+ str->buf_max_len = str->buf_len = strhex_to_str((char *)str->buffer, sizeof(str->buffer), buf);
+}
+
+/*******************************************************************
+ Inits a BUFFER3 structure.
+********************************************************************/
+
+void init_buffer3_bytes(BUFFER3 *str, uint8 *buf, int len)
+{
+ ZERO_STRUCTP(str);
+
+ /* max buffer size (allocated size) */
+ str->buf_max_len = len;
+ if (buf != NULL) {
+ create_buffer3(str, len);
+ memcpy(str->buffer, buf, len);
+ }
+ str->buf_len = buf != NULL ? len : 0;
+}
+
+/*******************************************************************
+ Reads or writes a BUFFER3 structure.
+ the uni_max_len member tells you how large the buffer is.
+ the uni_str_len member tells you how much of the buffer is really used.
+********************************************************************/
+
+BOOL smb_io_buffer3(const char *desc, BUFFER3 *buf3, prs_struct *ps, int depth)
+{
+ if (buf3 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_buffer3");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("uni_max_len", ps, depth, &buf3->buf_max_len))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ buf3->buffer = (unsigned char *)prs_alloc_mem(ps, buf3->buf_max_len);
+ if (buf3->buffer == NULL)
+ return False;
+ }
+
+ if(!prs_uint8s(True, "buffer ", ps, depth, buf3->buffer, buf3->buf_max_len))
+ return False;
+
+ if(!prs_uint32("buf_len ", ps, depth, &buf3->buf_len))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a BUFFER5 structure.
+the buf_len member tells you how large the buffer is.
+********************************************************************/
+BOOL smb_io_buffer5(const char *desc, BUFFER5 *buf5, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "smb_io_buffer5");
+ depth++;
+
+ if (buf5 == NULL) return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("buf_len", ps, depth, &buf5->buf_len))
+ return False;
+
+ if(buf5->buf_len) {
+ if(!prs_buffer5(True, "buffer" , ps, depth, buf5))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a BUFFER2 structure.
+********************************************************************/
+
+void init_buffer2(BUFFER2 *str, const uint8 *buf, size_t len)
+{
+ ZERO_STRUCTP(str);
+
+ /* max buffer size (allocated size) */
+ str->buf_max_len = len;
+ str->offset = 0;
+ str->buf_len = buf != NULL ? len : 0;
+
+ if (buf != NULL) {
+ if (len < MAX_BUFFERLEN)
+ len = MAX_BUFFERLEN;
+ str->buffer = talloc_zero(get_talloc_ctx(), len);
+ if (str->buffer == NULL)
+ smb_panic("init_buffer2: talloc fail\n");
+ memcpy(str->buffer, buf, MIN(str->buf_len, len));
+ }
+}
+
+/*******************************************************************
+ Reads or writes a BUFFER2 structure.
+ the uni_max_len member tells you how large the buffer is.
+ the uni_str_len member tells you how much of the buffer is really used.
+********************************************************************/
+
+BOOL smb_io_buffer2(const char *desc, BUFFER2 *buf2, uint32 buffer, prs_struct *ps, int depth)
+{
+ if (buf2 == NULL)
+ return False;
+
+ if (buffer) {
+
+ prs_debug(ps, depth, desc, "smb_io_buffer2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("uni_max_len", ps, depth, &buf2->buf_max_len))
+ return False;
+ if(!prs_uint32("offset ", ps, depth, &buf2->offset))
+ return False;
+ if(!prs_uint32("buf_len ", ps, depth, &buf2->buf_len))
+ return False;
+
+ /* buffer advanced by indicated length of string
+ NOT by searching for null-termination */
+
+ if(!prs_buffer2(True, "buffer ", ps, depth, buf2))
+ return False;
+
+ } else {
+
+ prs_debug(ps, depth, desc, "smb_io_buffer2 - NULL");
+ depth++;
+ memset((char *)buf2, '\0', sizeof(*buf2));
+
+ }
+ return True;
+}
+
+/*******************************************************************
+creates a UNISTR2 structure: sets up the buffer, too
+********************************************************************/
+
+void init_buf_unistr2(UNISTR2 *str, uint32 *ptr, const char *buf)
+{
+ if (buf != NULL) {
+ *ptr = 1;
+ init_unistr2(str, buf, UNI_STR_TERMINATE);
+ } else {
+ *ptr = 0;
+ init_unistr2(str, NULL, UNI_FLAGS_NONE);
+
+ }
+}
+
+/*******************************************************************
+ Copies a UNISTR2 structure.
+********************************************************************/
+
+void copy_unistr2(UNISTR2 *str, const UNISTR2 *from)
+{
+ str->uni_max_len = from->uni_max_len;
+ str->offset = from->offset;
+ str->uni_str_len = from->uni_str_len;
+
+ if (from->buffer == NULL)
+ return;
+
+ /* the string buffer is allocated to the maximum size
+ (the the length of the source string) to prevent
+ reallocation of memory. */
+ if (str->buffer == NULL) {
+ size_t len = from->uni_max_len * sizeof(uint16);
+
+ if (len < MAX_UNISTRLEN)
+ len = MAX_UNISTRLEN;
+ len *= sizeof(uint16);
+
+ str->buffer = (uint16 *)talloc_zero(get_talloc_ctx(), len);
+ if ((str->buffer == NULL) && (len > 0 )) {
+ smb_panic("copy_unistr2: talloc fail\n");
+ return;
+ }
+ }
+
+ /* copy the string */
+ memcpy(str->buffer, from->buffer, from->uni_max_len*sizeof(uint16));
+}
+
+/*******************************************************************
+ Creates a STRING2 structure.
+********************************************************************/
+
+void init_string2(STRING2 *str, const char *buf, int max_len, int str_len)
+{
+ int alloc_len = 0;
+
+ /* set up string lengths. */
+ str->str_max_len = max_len;
+ str->offset = 0;
+ str->str_str_len = str_len;
+
+ /* store the string */
+ if(str_len != 0) {
+ if (str_len < MAX_STRINGLEN)
+ alloc_len = MAX_STRINGLEN;
+ str->buffer = talloc_zero(get_talloc_ctx(), alloc_len);
+ if (str->buffer == NULL)
+ smb_panic("init_string2: malloc fail\n");
+ memcpy(str->buffer, buf, str_len);
+ }
+}
+
+/*******************************************************************
+ Reads or writes a STRING2 structure.
+ XXXX NOTE: STRING2 structures need NOT be null-terminated.
+ the str_str_len member tells you how long the string is;
+ the str_max_len member tells you how large the buffer is.
+********************************************************************/
+
+BOOL smb_io_string2(const char *desc, STRING2 *str2, uint32 buffer, prs_struct *ps, int depth)
+{
+ if (str2 == NULL)
+ return False;
+
+ if (buffer) {
+
+ prs_debug(ps, depth, desc, "smb_io_string2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("str_max_len", ps, depth, &str2->str_max_len))
+ return False;
+ if(!prs_uint32("offset ", ps, depth, &str2->offset))
+ return False;
+ if(!prs_uint32("str_str_len", ps, depth, &str2->str_str_len))
+ return False;
+
+ /* buffer advanced by indicated length of string
+ NOT by searching for null-termination */
+ if(!prs_string2(True, "buffer ", ps, depth, str2))
+ return False;
+
+ } else {
+
+ prs_debug(ps, depth, desc, "smb_io_string2 - NULL");
+ depth++;
+ memset((char *)str2, '\0', sizeof(*str2));
+
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a UNISTR2 structure.
+********************************************************************/
+
+void init_unistr2(UNISTR2 *str, const char *buf, enum unistr2_term_codes flags)
+{
+ size_t len = 0;
+ uint32 num_chars = 0;
+
+ if (buf) {
+ /* We always null terminate the copy. */
+ len = strlen(buf) + 1;
+ }
+
+ if (len < MAX_UNISTRLEN)
+ len = MAX_UNISTRLEN;
+ len *= sizeof(uint16);
+
+ str->buffer = (uint16 *)talloc_zero(get_talloc_ctx(), len);
+ if ((str->buffer == NULL) && (len > 0)) {
+ smb_panic("init_unistr2: malloc fail\n");
+ return;
+ }
+
+ /*
+ * The UNISTR2 must be initialized !!!
+ * jfm, 7/7/2001.
+ */
+ if (buf) {
+ rpcstr_push((char *)str->buffer, buf, len, STR_TERMINATE);
+ num_chars = strlen_w(str->buffer);
+ if (flags == STR_TERMINATE || flags == UNI_MAXLEN_TERMINATE) {
+ num_chars++;
+ }
+ }
+
+ str->uni_max_len = num_chars;
+ str->offset = 0;
+ str->uni_str_len = num_chars;
+ if ( num_chars && ((flags == UNI_MAXLEN_TERMINATE) || (flags == UNI_BROKEN_NON_NULL)) )
+ str->uni_max_len++;
+}
+
+/**
+ * Inits a UNISTR2 structure.
+ * @param ctx talloc context to allocate string on
+ * @param str pointer to string to create
+ * @param buf UCS2 null-terminated buffer to init from
+*/
+
+void init_unistr2_w(TALLOC_CTX *ctx, UNISTR2 *str, const smb_ucs2_t *buf)
+{
+ uint32 len = strlen_w(buf);
+ uint32 max_len = len;
+ uint32 alloc_len;
+
+ ZERO_STRUCTP(str);
+
+ /* set up string lengths. */
+ str->uni_max_len = len;
+ str->offset = 0;
+ str->uni_str_len = len;
+
+ if (max_len < MAX_UNISTRLEN)
+ max_len = MAX_UNISTRLEN;
+
+ alloc_len = (max_len + 1) * sizeof(uint16);
+
+ str->buffer = (uint16 *)talloc_zero(ctx, alloc_len);
+ if ((str->buffer == NULL) && (alloc_len > 0)) {
+ smb_panic("init_unistr2_w: malloc fail\n");
+ return;
+ }
+
+ /*
+ * don't move this test above ! The UNISTR2 must be initialized !!!
+ * jfm, 7/7/2001.
+ */
+ if (buf==NULL)
+ return;
+
+ /* Yes, this is a strncpy( foo, bar, strlen(bar)) - but as
+ long as the buffer above is talloc()ed correctly then this
+ is the correct thing to do */
+ strncpy_w(str->buffer, buf, len + 1);
+}
+
+/*******************************************************************
+ Inits a UNISTR2 structure from a UNISTR
+********************************************************************/
+
+void init_unistr2_from_unistr(UNISTR2 *to, const UNISTR *from)
+{
+ uint32 i;
+
+ /* the destination UNISTR2 should never be NULL.
+ if it is it is a programming error */
+
+ /* if the source UNISTR is NULL, then zero out
+ the destination string and return */
+ ZERO_STRUCTP (to);
+ if ((from == NULL) || (from->buffer == NULL))
+ return;
+
+ /* get the length; UNISTR must be NULL terminated */
+ i = 0;
+ while ((from->buffer)[i]!='\0')
+ i++;
+ i++; /* one more to catch the terminating NULL */
+ /* is this necessary -- jerry? I need to think */
+
+ /* set up string lengths; uni_max_len is set to i+1
+ because we need to account for the final NULL termination */
+ to->uni_max_len = i;
+ to->offset = 0;
+ to->uni_str_len = i;
+
+ /* allocate the space and copy the string buffer */
+ to->buffer = (uint16 *)talloc_zero(get_talloc_ctx(), sizeof(uint16)*(to->uni_str_len));
+ if (to->buffer == NULL)
+ smb_panic("init_unistr2_from_unistr: malloc fail\n");
+ memcpy(to->buffer, from->buffer, to->uni_max_len*sizeof(uint16));
+ return;
+}
+
+/*******************************************************************
+ Inits a UNISTR2 structure from a DATA_BLOB.
+ The length of the data_blob must count the bytes of the buffer.
+ Copies the blob data.
+********************************************************************/
+
+void init_unistr2_from_datablob(UNISTR2 *str, DATA_BLOB *blob)
+{
+ /* Allocs the unistring */
+ init_unistr2(str, NULL, UNI_FLAGS_NONE);
+
+ /* Sets the values */
+ str->uni_str_len = blob->length / sizeof(uint16);
+ str->uni_max_len = str->uni_str_len;
+ str->offset = 0;
+ if (blob->length) {
+ str->buffer = (uint16 *) memdup(blob->data, blob->length);
+ } else {
+ str->buffer = NULL;
+ }
+ if ((str->buffer == NULL) && (blob->length > 0)) {
+ smb_panic("init_unistr2_from_datablob: malloc fail\n");
+ }
+}
+
+/*******************************************************************
+ Reads or writes a UNISTR2 structure.
+ XXXX NOTE: UNISTR2 structures need NOT be null-terminated.
+ the uni_str_len member tells you how long the string is;
+ the uni_max_len member tells you how large the buffer is.
+********************************************************************/
+
+BOOL smb_io_unistr2(const char *desc, UNISTR2 *uni2, uint32 buffer, prs_struct *ps, int depth)
+{
+ if (uni2 == NULL)
+ return False;
+
+ if (buffer) {
+
+ prs_debug(ps, depth, desc, "smb_io_unistr2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("uni_max_len", ps, depth, &uni2->uni_max_len))
+ return False;
+ if(!prs_uint32("offset ", ps, depth, &uni2->offset))
+ return False;
+ if(!prs_uint32("uni_str_len", ps, depth, &uni2->uni_str_len))
+ return False;
+
+ /* buffer advanced by indicated length of string
+ NOT by searching for null-termination */
+ if(!prs_unistr2(True, "buffer ", ps, depth, uni2))
+ return False;
+
+ } else {
+
+ prs_debug(ps, depth, desc, "smb_io_unistr2 - NULL");
+ depth++;
+ memset((char *)uni2, '\0', sizeof(*uni2));
+
+ }
+
+ return True;
+}
+
+
+/*
+ initialise a UNISTR_ARRAY from a char**
+*/
+BOOL init_unistr2_array(UNISTR2_ARRAY *array,
+ uint32 count, const char **strings)
+{
+ unsigned int i;
+
+ array->count = count;
+ array->ref_id = count?1:0;
+ if (array->count == 0) {
+ return True;
+ }
+
+ array->strings = (UNISTR2_ARRAY_EL *)talloc_zero(get_talloc_ctx(), count * sizeof(UNISTR2_ARRAY_EL));
+ if (!array->strings) {
+ return False;
+ }
+
+ for (i=0;i<count;i++) {
+ init_unistr2(&array->strings[i].string, strings[i], UNI_FLAGS_NONE);
+ array->strings[i].size = array->strings[i].string.uni_max_len*2;
+ array->strings[i].length = array->strings[i].size;
+ array->strings[i].ref_id = 1;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a UNISTR2_ARRAY structure.
+********************************************************************/
+BOOL smb_io_unistr2_array(const char *desc, UNISTR2_ARRAY *array, prs_struct *ps, int depth)
+{
+ unsigned int i;
+
+ prs_debug(ps, depth, desc, "smb_io_unistr2_array");
+ depth++;
+
+ if(!prs_uint32("ref_id", ps, depth, &array->ref_id))
+ return False;
+
+ if (! array->ref_id) {
+ return True;
+ }
+
+ if(!prs_uint32("count", ps, depth, &array->count))
+ return False;
+
+ if (array->count == 0) {
+ return True;
+ }
+
+ if (UNMARSHALLING(ps)) {
+ array->strings = talloc_zero(get_talloc_ctx(), array->count * sizeof(array->strings[0]));
+ }
+ if (! array->strings) {
+ return False;
+ }
+
+ for (i=0;i<array->count;i++) {
+ if(!prs_uint16("length", ps, depth, &array->strings[i].length))
+ return False;
+ if(!prs_uint16("size", ps, depth, &array->strings[i].size))
+ return False;
+ if(!prs_uint32("ref_id", ps, depth, &array->strings[i].ref_id))
+ return False;
+ }
+
+ for (i=0;i<array->count;i++) {
+ if (! smb_io_unistr2("string", &array->strings[i].string, array->strings[i].ref_id, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+
+/*******************************************************************
+ Inits a DOM_RID2 structure.
+********************************************************************/
+
+void init_dom_rid2(DOM_RID2 *rid2, uint32 rid, uint8 type, uint32 idx)
+{
+ rid2->type = type;
+ rid2->rid = rid;
+ rid2->rid_idx = idx;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_RID2 structure.
+********************************************************************/
+
+BOOL smb_io_dom_rid2(const char *desc, DOM_RID2 *rid2, prs_struct *ps, int depth)
+{
+ if (rid2 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_dom_rid2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint8("type ", ps, depth, &rid2->type))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("rid ", ps, depth, &rid2->rid))
+ return False;
+ if(!prs_uint32("rid_idx", ps, depth, &rid2->rid_idx))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+creates a DOM_RID3 structure.
+********************************************************************/
+
+void init_dom_rid3(DOM_RID3 *rid3, uint32 rid, uint8 type)
+{
+ rid3->rid = rid;
+ rid3->type1 = type;
+ rid3->ptr_type = 0x1; /* non-zero, basically. */
+ rid3->type2 = 0x1;
+ rid3->unk = type;
+}
+
+/*******************************************************************
+reads or writes a DOM_RID3 structure.
+********************************************************************/
+
+BOOL smb_io_dom_rid3(const char *desc, DOM_RID3 *rid3, prs_struct *ps, int depth)
+{
+ if (rid3 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_dom_rid3");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("rid ", ps, depth, &rid3->rid))
+ return False;
+ if(!prs_uint32("type1 ", ps, depth, &rid3->type1))
+ return False;
+ if(!prs_uint32("ptr_type", ps, depth, &rid3->ptr_type))
+ return False;
+ if(!prs_uint32("type2 ", ps, depth, &rid3->type2))
+ return False;
+ if(!prs_uint32("unk ", ps, depth, &rid3->unk))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a DOM_RID4 structure.
+********************************************************************/
+
+void init_dom_rid4(DOM_RID4 *rid4, uint16 unknown, uint16 attr, uint32 rid)
+{
+ rid4->unknown = unknown;
+ rid4->attr = attr;
+ rid4->rid = rid;
+}
+
+/*******************************************************************
+ Inits a DOM_CLNT_SRV structure.
+********************************************************************/
+
+static void init_clnt_srv(DOM_CLNT_SRV *logcln, const char *logon_srv, const char *comp_name)
+{
+ DEBUG(5,("init_clnt_srv: %d\n", __LINE__));
+
+ if (logon_srv != NULL) {
+ logcln->undoc_buffer = 1;
+ init_unistr2(&logcln->uni_logon_srv, logon_srv, UNI_STR_TERMINATE);
+ } else {
+ logcln->undoc_buffer = 0;
+ }
+
+ if (comp_name != NULL) {
+ logcln->undoc_buffer2 = 1;
+ init_unistr2(&logcln->uni_comp_name, comp_name, UNI_STR_TERMINATE);
+ } else {
+ logcln->undoc_buffer2 = 0;
+ }
+}
+
+/*******************************************************************
+ Inits or writes a DOM_CLNT_SRV structure.
+********************************************************************/
+
+static BOOL smb_io_clnt_srv(const char *desc, DOM_CLNT_SRV *logcln, prs_struct *ps, int depth)
+{
+ if (logcln == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_clnt_srv");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("undoc_buffer ", ps, depth, &logcln->undoc_buffer))
+ return False;
+
+ if (logcln->undoc_buffer != 0) {
+ if(!smb_io_unistr2("unistr2", &logcln->uni_logon_srv, logcln->undoc_buffer, ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("undoc_buffer2", ps, depth, &logcln->undoc_buffer2))
+ return False;
+
+ if (logcln->undoc_buffer2 != 0) {
+ if(!smb_io_unistr2("unistr2", &logcln->uni_comp_name, logcln->undoc_buffer2, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a DOM_LOG_INFO structure.
+********************************************************************/
+
+void init_log_info(DOM_LOG_INFO *loginfo, const char *logon_srv, const char *acct_name,
+ uint16 sec_chan, const char *comp_name)
+{
+ DEBUG(5,("make_log_info %d\n", __LINE__));
+
+ loginfo->undoc_buffer = 1;
+
+ init_unistr2(&loginfo->uni_logon_srv, logon_srv, UNI_STR_TERMINATE);
+ init_unistr2(&loginfo->uni_acct_name, acct_name, UNI_STR_TERMINATE);
+
+ loginfo->sec_chan = sec_chan;
+
+ init_unistr2(&loginfo->uni_comp_name, comp_name, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes a DOM_LOG_INFO structure.
+********************************************************************/
+
+BOOL smb_io_log_info(const char *desc, DOM_LOG_INFO *loginfo, prs_struct *ps, int depth)
+{
+ if (loginfo == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_log_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("undoc_buffer", ps, depth, &loginfo->undoc_buffer))
+ return False;
+
+ if(!smb_io_unistr2("unistr2", &loginfo->uni_logon_srv, True, ps, depth))
+ return False;
+ if(!smb_io_unistr2("unistr2", &loginfo->uni_acct_name, True, ps, depth))
+ return False;
+
+ if(!prs_uint16("sec_chan", ps, depth, &loginfo->sec_chan))
+ return False;
+
+ if(!smb_io_unistr2("unistr2", &loginfo->uni_comp_name, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_CHAL structure.
+********************************************************************/
+
+BOOL smb_io_chal(const char *desc, DOM_CHAL *chal, prs_struct *ps, int depth)
+{
+ if (chal == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_chal");
+ depth++;
+
+ if(!prs_uint8s (False, "data", ps, depth, chal->data, 8))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_CRED structure.
+********************************************************************/
+
+BOOL smb_io_cred(const char *desc, DOM_CRED *cred, prs_struct *ps, int depth)
+{
+ if (cred == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_cred");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_chal ("", &cred->challenge, ps, depth))
+ return False;
+
+ if(!smb_io_utime("", &cred->timestamp, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a DOM_CLNT_INFO2 structure.
+********************************************************************/
+
+void init_clnt_info2(DOM_CLNT_INFO2 *clnt,
+ const char *logon_srv, const char *comp_name,
+ const DOM_CRED *clnt_cred)
+{
+ DEBUG(5,("make_clnt_info: %d\n", __LINE__));
+
+ init_clnt_srv(&clnt->login, logon_srv, comp_name);
+
+ if (clnt_cred != NULL) {
+ clnt->ptr_cred = 1;
+ memcpy(&clnt->cred, clnt_cred, sizeof(clnt->cred));
+ } else {
+ clnt->ptr_cred = 0;
+ }
+}
+
+/*******************************************************************
+ Reads or writes a DOM_CLNT_INFO2 structure.
+********************************************************************/
+
+BOOL smb_io_clnt_info2(const char *desc, DOM_CLNT_INFO2 *clnt, prs_struct *ps, int depth)
+{
+ if (clnt == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_clnt_info2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_clnt_srv("", &clnt->login, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_cred", ps, depth, &clnt->ptr_cred))
+ return False;
+ if(!smb_io_cred("", &clnt->cred, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a DOM_CLNT_INFO structure.
+********************************************************************/
+
+void init_clnt_info(DOM_CLNT_INFO *clnt,
+ const char *logon_srv, const char *acct_name,
+ uint16 sec_chan, const char *comp_name,
+ const DOM_CRED *cred)
+{
+ DEBUG(5,("make_clnt_info\n"));
+
+ init_log_info(&clnt->login, logon_srv, acct_name, sec_chan, comp_name);
+ memcpy(&clnt->cred, cred, sizeof(clnt->cred));
+}
+
+/*******************************************************************
+ Reads or writes a DOM_CLNT_INFO structure.
+********************************************************************/
+
+BOOL smb_io_clnt_info(const char *desc, DOM_CLNT_INFO *clnt, prs_struct *ps, int depth)
+{
+ if (clnt == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_clnt_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_log_info("", &clnt->login, ps, depth))
+ return False;
+ if(!smb_io_cred("", &clnt->cred, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a DOM_LOGON_ID structure.
+********************************************************************/
+
+void init_logon_id(DOM_LOGON_ID *logonid, uint32 log_id_low, uint32 log_id_high)
+{
+ DEBUG(5,("make_logon_id: %d\n", __LINE__));
+
+ logonid->low = log_id_low;
+ logonid->high = log_id_high;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_LOGON_ID structure.
+********************************************************************/
+
+BOOL smb_io_logon_id(const char *desc, DOM_LOGON_ID *logonid, prs_struct *ps, int depth)
+{
+ if (logonid == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_logon_id");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("low ", ps, depth, &logonid->low ))
+ return False;
+ if(!prs_uint32("high", ps, depth, &logonid->high))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an OWF_INFO structure.
+********************************************************************/
+
+void init_owf_info(OWF_INFO *hash, const uint8 data[16])
+{
+ DEBUG(5,("init_owf_info: %d\n", __LINE__));
+
+ if (data != NULL)
+ memcpy(hash->data, data, sizeof(hash->data));
+ else
+ memset((char *)hash->data, '\0', sizeof(hash->data));
+}
+
+/*******************************************************************
+ Reads or writes an OWF_INFO structure.
+********************************************************************/
+
+BOOL smb_io_owf_info(const char *desc, OWF_INFO *hash, prs_struct *ps, int depth)
+{
+ if (hash == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_owf_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint8s (False, "data", ps, depth, hash->data, 16))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_GID structure.
+********************************************************************/
+
+BOOL smb_io_gid(const char *desc, DOM_GID *gid, prs_struct *ps, int depth)
+{
+ if (gid == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_gid");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("g_rid", ps, depth, &gid->g_rid))
+ return False;
+ if(!prs_uint32("attr ", ps, depth, &gid->attr))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an POLICY_HND structure.
+********************************************************************/
+
+BOOL smb_io_pol_hnd(const char *desc, POLICY_HND *pol, prs_struct *ps, int depth)
+{
+ if (pol == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_pol_hnd");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(UNMARSHALLING(ps))
+ ZERO_STRUCTP(pol);
+
+ if (!prs_uint32("data1", ps, depth, &pol->data1))
+ return False;
+ if (!prs_uint32("data2", ps, depth, &pol->data2))
+ return False;
+ if (!prs_uint16("data3", ps, depth, &pol->data3))
+ return False;
+ if (!prs_uint16("data4", ps, depth, &pol->data4))
+ return False;
+ if(!prs_uint8s (False, "data5", ps, depth, pol->data5, sizeof(pol->data5)))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Create a UNISTR3.
+********************************************************************/
+
+void init_unistr3(UNISTR3 *str, const char *buf)
+{
+ size_t len;
+
+ if (buf == NULL) {
+ str->uni_str_len=0;
+ str->str.buffer = NULL;
+ return;
+ }
+
+ len = strlen(buf) + 1;
+
+ str->uni_str_len=len;
+
+ if (len < MAX_UNISTRLEN)
+ len = MAX_UNISTRLEN;
+
+ len *= sizeof(uint16);
+
+ str->str.buffer = (uint16 *)talloc_zero(get_talloc_ctx(), len);
+ if (str->str.buffer == NULL)
+ smb_panic("init_unistr3: malloc fail\n");
+
+ rpcstr_push((char *)str->str.buffer, buf, len, STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes a UNISTR3 structure.
+********************************************************************/
+
+BOOL smb_io_unistr3(const char *desc, UNISTR3 *name, prs_struct *ps, int depth)
+{
+ if (name == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_unistr3");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("uni_str_len", ps, depth, &name->uni_str_len))
+ return False;
+
+ /* don't know if len is specified by uni_str_len member... */
+ /* assume unicode string is unicode-null-terminated, instead */
+
+ if(!prs_unistr3(True, "unistr", name, ps, depth))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Stream a uint64_struct
+ ********************************************************************/
+BOOL prs_uint64(const char *name, prs_struct *ps, int depth, UINT64_S *data64)
+{
+ return prs_uint32(name, ps, depth+1, &data64->low) &&
+ prs_uint32(name, ps, depth+1, &data64->high);
+}
+
+/*******************************************************************
+reads or writes a BUFHDR2 structure.
+********************************************************************/
+BOOL smb_io_bufhdr2(const char *desc, BUFHDR2 *hdr, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "smb_io_bufhdr2");
+ depth++;
+
+ prs_align(ps);
+ prs_uint32("info_level", ps, depth, &(hdr->info_level));
+ prs_uint32("length ", ps, depth, &(hdr->length ));
+ prs_uint32("buffer ", ps, depth, &(hdr->buffer ));
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a BUFFER4 structure.
+********************************************************************/
+BOOL smb_io_buffer4(const char *desc, BUFFER4 *buf4, uint32 buffer, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "smb_io_buffer4");
+ depth++;
+
+ prs_align(ps);
+ prs_uint32("buf_len", ps, depth, &(buf4->buf_len));
+
+ if (buf4->buf_len > MAX_BUFFERLEN)
+ {
+ buf4->buf_len = MAX_BUFFERLEN;
+ }
+
+ prs_uint8s(True, "buffer", ps, depth, buf4->buffer, buf4->buf_len);
+
+ return True;
+}
+
+/*******************************************************************
+creates a UNIHDR structure.
+********************************************************************/
+
+BOOL make_uni_hdr(UNIHDR *hdr, int len)
+{
+ if (hdr == NULL)
+ {
+ return False;
+ }
+ hdr->uni_str_len = 2 * len;
+ hdr->uni_max_len = 2 * len;
+ hdr->buffer = len != 0 ? 1 : 0;
+
+ return True;
+}
+
+/*******************************************************************
+creates a BUFHDR2 structure.
+********************************************************************/
+BOOL make_bufhdr2(BUFHDR2 *hdr, uint32 info_level, uint32 length, uint32 buffer)
+{
+ hdr->info_level = info_level;
+ hdr->length = length;
+ hdr->buffer = buffer;
+
+ return True;
+}
diff --git a/source/rpc_parse/parse_net.c b/source/rpc_parse/parse_net.c
new file mode 100644
index 00000000000..a98738b51f0
--- /dev/null
+++ b/source/rpc_parse/parse_net.c
@@ -0,0 +1,3048 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997.
+ * Copyright (C) Jean François Micouleau 2002.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL net_io_neg_flags(const char *desc, NEG_FLAGS *neg, prs_struct *ps, int depth)
+{
+ if (neg == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_neg_flags");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("neg_flags", ps, depth, &neg->neg_flags))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a NETLOGON_INFO_3 structure.
+********************************************************************/
+
+static void init_netinfo_3(NETLOGON_INFO_3 *info, uint32 flags, uint32 logon_attempts)
+{
+ info->flags = flags;
+ info->logon_attempts = logon_attempts;
+ info->reserved_1 = 0x0;
+ info->reserved_2 = 0x0;
+ info->reserved_3 = 0x0;
+ info->reserved_4 = 0x0;
+ info->reserved_5 = 0x0;
+}
+
+/*******************************************************************
+ Reads or writes a NETLOGON_INFO_3 structure.
+********************************************************************/
+
+static BOOL net_io_netinfo_3(const char *desc, NETLOGON_INFO_3 *info, prs_struct *ps, int depth)
+{
+ if (info == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_netinfo_3");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("flags ", ps, depth, &info->flags))
+ return False;
+ if(!prs_uint32("logon_attempts", ps, depth, &info->logon_attempts))
+ return False;
+ if(!prs_uint32("reserved_1 ", ps, depth, &info->reserved_1))
+ return False;
+ if(!prs_uint32("reserved_2 ", ps, depth, &info->reserved_2))
+ return False;
+ if(!prs_uint32("reserved_3 ", ps, depth, &info->reserved_3))
+ return False;
+ if(!prs_uint32("reserved_4 ", ps, depth, &info->reserved_4))
+ return False;
+ if(!prs_uint32("reserved_5 ", ps, depth, &info->reserved_5))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Inits a NETLOGON_INFO_1 structure.
+********************************************************************/
+
+static void init_netinfo_1(NETLOGON_INFO_1 *info, uint32 flags, uint32 pdc_status)
+{
+ info->flags = flags;
+ info->pdc_status = pdc_status;
+}
+
+/*******************************************************************
+ Reads or writes a NETLOGON_INFO_1 structure.
+********************************************************************/
+
+static BOOL net_io_netinfo_1(const char *desc, NETLOGON_INFO_1 *info, prs_struct *ps, int depth)
+{
+ if (info == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_netinfo_1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("flags ", ps, depth, &info->flags))
+ return False;
+ if(!prs_uint32("pdc_status", ps, depth, &info->pdc_status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a NETLOGON_INFO_2 structure.
+********************************************************************/
+
+static void init_netinfo_2(NETLOGON_INFO_2 *info, uint32 flags, uint32 pdc_status,
+ uint32 tc_status, const char *trusted_dc_name)
+{
+ info->flags = flags;
+ info->pdc_status = pdc_status;
+ info->ptr_trusted_dc_name = 1;
+ info->tc_status = tc_status;
+
+ if (trusted_dc_name != NULL)
+ init_unistr2(&info->uni_trusted_dc_name, trusted_dc_name, UNI_STR_TERMINATE);
+ else
+ init_unistr2(&info->uni_trusted_dc_name, "", UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes a NETLOGON_INFO_2 structure.
+********************************************************************/
+
+static BOOL net_io_netinfo_2(const char *desc, NETLOGON_INFO_2 *info, prs_struct *ps, int depth)
+{
+ if (info == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_netinfo_2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("flags ", ps, depth, &info->flags))
+ return False;
+ if(!prs_uint32("pdc_status ", ps, depth, &info->pdc_status))
+ return False;
+ if(!prs_uint32("ptr_trusted_dc_name", ps, depth, &info->ptr_trusted_dc_name))
+ return False;
+ if(!prs_uint32("tc_status ", ps, depth, &info->tc_status))
+ return False;
+
+ if (info->ptr_trusted_dc_name != 0) {
+ if(!smb_io_unistr2("unistr2", &info->uni_trusted_dc_name, info->ptr_trusted_dc_name, ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an NET_Q_LOGON_CTRL2 structure.
+********************************************************************/
+
+BOOL net_io_q_logon_ctrl2(const char *desc, NET_Q_LOGON_CTRL2 *q_l, prs_struct *ps, int depth)
+{
+ if (q_l == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_q_logon_ctrl2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr ", ps, depth, &q_l->ptr))
+ return False;
+
+ if(!smb_io_unistr2 ("", &q_l->uni_server_name, q_l->ptr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("function_code", ps, depth, &q_l->function_code))
+ return False;
+ if(!prs_uint32("query_level ", ps, depth, &q_l->query_level))
+ return False;
+ if(!prs_uint32("switch_value ", ps, depth, &q_l->switch_value))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an NET_Q_LOGON_CTRL2 structure.
+********************************************************************/
+
+void init_net_q_logon_ctrl2(NET_Q_LOGON_CTRL2 *q_l, const char *srv_name,
+ uint32 query_level)
+{
+ DEBUG(5,("init_q_logon_ctrl2\n"));
+
+ q_l->function_code = 0x01;
+ q_l->query_level = query_level;
+ q_l->switch_value = 0x01;
+
+ init_unistr2(&q_l->uni_server_name, srv_name, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Inits an NET_R_LOGON_CTRL2 structure.
+********************************************************************/
+
+void init_net_r_logon_ctrl2(NET_R_LOGON_CTRL2 *r_l, uint32 query_level,
+ uint32 flags, uint32 pdc_status,
+ uint32 logon_attempts, uint32 tc_status,
+ const char *trusted_domain_name)
+{
+ DEBUG(5,("init_r_logon_ctrl2\n"));
+
+ r_l->switch_value = query_level; /* should only be 0x1 */
+
+ switch (query_level) {
+ case 1:
+ r_l->ptr = 1; /* undocumented pointer */
+ init_netinfo_1(&r_l->logon.info1, flags, pdc_status);
+ r_l->status = NT_STATUS_OK;
+ break;
+ case 2:
+ r_l->ptr = 1; /* undocumented pointer */
+ init_netinfo_2(&r_l->logon.info2, flags, pdc_status,
+ tc_status, trusted_domain_name);
+ r_l->status = NT_STATUS_OK;
+ break;
+ case 3:
+ r_l->ptr = 1; /* undocumented pointer */
+ init_netinfo_3(&r_l->logon.info3, flags, logon_attempts);
+ r_l->status = NT_STATUS_OK;
+ break;
+ default:
+ DEBUG(2,("init_r_logon_ctrl2: unsupported switch value %d\n",
+ r_l->switch_value));
+ r_l->ptr = 0; /* undocumented pointer */
+
+ /* take a guess at an error code... */
+ r_l->status = NT_STATUS_INVALID_INFO_CLASS;
+ break;
+ }
+}
+
+/*******************************************************************
+ Reads or writes an NET_R_LOGON_CTRL2 structure.
+********************************************************************/
+
+BOOL net_io_r_logon_ctrl2(const char *desc, NET_R_LOGON_CTRL2 *r_l, prs_struct *ps, int depth)
+{
+ if (r_l == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_r_logon_ctrl2");
+ depth++;
+
+ if(!prs_uint32("switch_value ", ps, depth, &r_l->switch_value))
+ return False;
+ if(!prs_uint32("ptr ", ps, depth, &r_l->ptr))
+ return False;
+
+ if (r_l->ptr != 0) {
+ switch (r_l->switch_value) {
+ case 1:
+ if(!net_io_netinfo_1("", &r_l->logon.info1, ps, depth))
+ return False;
+ break;
+ case 2:
+ if(!net_io_netinfo_2("", &r_l->logon.info2, ps, depth))
+ return False;
+ break;
+ case 3:
+ if(!net_io_netinfo_3("", &r_l->logon.info3, ps, depth))
+ return False;
+ break;
+ default:
+ DEBUG(2,("net_io_r_logon_ctrl2: unsupported switch value %d\n",
+ r_l->switch_value));
+ break;
+ }
+ }
+
+ if(!prs_ntstatus("status ", ps, depth, &r_l->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an NET_Q_LOGON_CTRL structure.
+********************************************************************/
+
+BOOL net_io_q_logon_ctrl(const char *desc, NET_Q_LOGON_CTRL *q_l, prs_struct *ps,
+ int depth)
+{
+ prs_debug(ps, depth, desc, "net_io_q_logon_ctrl");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr ", ps, depth, &q_l->ptr))
+ return False;
+
+ if(!smb_io_unistr2 ("", &q_l->uni_server_name, q_l->ptr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("function_code", ps, depth, &q_l->function_code))
+ return False;
+ if(!prs_uint32("query_level ", ps, depth, &q_l->query_level))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an NET_Q_LOGON_CTRL structure.
+********************************************************************/
+
+void init_net_q_logon_ctrl(NET_Q_LOGON_CTRL *q_l, const char *srv_name,
+ uint32 query_level)
+{
+ DEBUG(5,("init_q_logon_ctrl\n"));
+
+ q_l->function_code = 0x01; /* ??? */
+ q_l->query_level = query_level;
+
+ init_unistr2(&q_l->uni_server_name, srv_name, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Inits an NET_R_LOGON_CTRL structure.
+********************************************************************/
+
+void init_net_r_logon_ctrl(NET_R_LOGON_CTRL *r_l, uint32 query_level,
+ uint32 flags, uint32 pdc_status)
+{
+ DEBUG(5,("init_r_logon_ctrl\n"));
+
+ r_l->switch_value = query_level; /* should only be 0x1 */
+
+ switch (query_level) {
+ case 1:
+ r_l->ptr = 1; /* undocumented pointer */
+ init_netinfo_1(&r_l->logon.info1, flags, pdc_status);
+ r_l->status = NT_STATUS_OK;
+ break;
+ default:
+ DEBUG(2,("init_r_logon_ctrl: unsupported switch value %d\n",
+ r_l->switch_value));
+ r_l->ptr = 0; /* undocumented pointer */
+
+ /* take a guess at an error code... */
+ r_l->status = NT_STATUS_INVALID_INFO_CLASS;
+ break;
+ }
+}
+
+/*******************************************************************
+ Reads or writes an NET_R_LOGON_CTRL structure.
+********************************************************************/
+
+BOOL net_io_r_logon_ctrl(const char *desc, NET_R_LOGON_CTRL *r_l, prs_struct *ps,
+ int depth)
+{
+ prs_debug(ps, depth, desc, "net_io_r_logon_ctrl");
+ depth++;
+
+ if(!prs_uint32("switch_value ", ps, depth, &r_l->switch_value))
+ return False;
+ if(!prs_uint32("ptr ", ps, depth, &r_l->ptr))
+ return False;
+
+ if (r_l->ptr != 0) {
+ switch (r_l->switch_value) {
+ case 1:
+ if(!net_io_netinfo_1("", &r_l->logon.info1, ps, depth))
+ return False;
+ break;
+ default:
+ DEBUG(2,("net_io_r_logon_ctrl: unsupported switch value %d\n",
+ r_l->switch_value));
+ break;
+ }
+ }
+
+ if(!prs_ntstatus("status ", ps, depth, &r_l->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an NET_R_GETDCNAME structure.
+********************************************************************/
+void init_net_q_getdcname(NET_Q_GETDCNAME *r_t, const char *logon_server,
+ const char *domainname)
+{
+ DEBUG(5,("init_r_getdcname\n"));
+
+ r_t->ptr_logon_server = (logon_server != NULL);
+ init_unistr2(&r_t->uni_logon_server, logon_server, UNI_STR_TERMINATE);
+ r_t->ptr_domainname = (domainname != NULL);
+ init_unistr2(&r_t->uni_domainname, domainname, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes an NET_Q_GETDCNAME structure.
+********************************************************************/
+
+BOOL net_io_q_getdcname(const char *desc, NET_Q_GETDCNAME *r_t, prs_struct *ps,
+ int depth)
+{
+ if (r_t == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_q_getdcname");
+ depth++;
+
+ if (!prs_uint32("ptr_logon_server", ps, depth, &r_t->ptr_logon_server))
+ return False;
+
+ if (!smb_io_unistr2("logon_server", &r_t->uni_logon_server,
+ r_t->ptr_logon_server, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("ptr_domainname", ps, depth, &r_t->ptr_domainname))
+ return False;
+
+ if (!smb_io_unistr2("domainname", &r_t->uni_domainname,
+ r_t->ptr_domainname, ps, depth))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Inits an NET_R_GETDCNAME structure.
+********************************************************************/
+void init_net_r_getdcname(NET_R_GETDCNAME *r_t, const char *dcname)
+{
+ DEBUG(5,("init_r_getdcname\n"));
+
+ init_unistr2(&r_t->uni_dcname, dcname, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes an NET_R_GETDCNAME structure.
+********************************************************************/
+
+BOOL net_io_r_getdcname(const char *desc, NET_R_GETDCNAME *r_t, prs_struct *ps,
+ int depth)
+{
+ if (r_t == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_r_getdcname");
+ depth++;
+
+ if (!prs_uint32("ptr_dcname", ps, depth, &r_t->ptr_dcname))
+ return False;
+
+ if (!smb_io_unistr2("dcname", &r_t->uni_dcname,
+ r_t->ptr_dcname, ps, depth))
+ return False;
+
+ if (!prs_ntstatus("status", ps, depth, &r_t->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an NET_R_TRUST_DOM_LIST structure.
+********************************************************************/
+
+void init_r_trust_dom(NET_R_TRUST_DOM_LIST *r_t,
+ uint32 num_doms, const char *dom_name)
+{
+ unsigned int i = 0;
+
+ DEBUG(5,("init_r_trust_dom\n"));
+
+ for (i = 0; i < MAX_TRUST_DOMS; i++) {
+ r_t->uni_trust_dom_name[i].uni_str_len = 0;
+ r_t->uni_trust_dom_name[i].uni_max_len = 0;
+ }
+ if (num_doms > MAX_TRUST_DOMS)
+ num_doms = MAX_TRUST_DOMS;
+
+ for (i = 0; i < num_doms; i++) {
+ fstring domain_name;
+ fstrcpy(domain_name, dom_name);
+ strupper_m(domain_name);
+ init_unistr2(&r_t->uni_trust_dom_name[i], domain_name, UNI_STR_TERMINATE);
+ /* the use of UNISTR2 here is non-standard. */
+ r_t->uni_trust_dom_name[i].offset = 0x1;
+ }
+
+ r_t->status = NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Reads or writes an NET_R_TRUST_DOM_LIST structure.
+********************************************************************/
+
+BOOL net_io_r_trust_dom(const char *desc, NET_R_TRUST_DOM_LIST *r_t, prs_struct *ps, int depth)
+{
+ uint32 value;
+
+ if (r_t == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_r_trust_dom");
+ depth++;
+
+ /* temporary code to give a valid response */
+ value=2;
+ if(!prs_uint32("status", ps, depth, &value))
+ return False;
+
+ value=1;
+ if(!prs_uint32("status", ps, depth, &value))
+ return False;
+ value=2;
+ if(!prs_uint32("status", ps, depth, &value))
+ return False;
+
+ value=0;
+ if(!prs_uint32("status", ps, depth, &value))
+ return False;
+
+ value=0;
+ if(!prs_uint32("status", ps, depth, &value))
+ return False;
+
+/* old non working code */
+#if 0
+ int i;
+
+ for (i = 0; i < MAX_TRUST_DOMS; i++) {
+ if (r_t->uni_trust_dom_name[i].uni_str_len == 0)
+ break;
+ if(!smb_io_unistr2("", &r_t->uni_trust_dom_name[i], True, ps, depth))
+ return False;
+ }
+
+ if(!prs_ntstatus("status", ps, depth, &r_t->status))
+ return False;
+#endif
+ return True;
+}
+
+
+/*******************************************************************
+ Reads or writes an NET_Q_TRUST_DOM_LIST structure.
+********************************************************************/
+
+BOOL net_io_q_trust_dom(const char *desc, NET_Q_TRUST_DOM_LIST *q_l, prs_struct *ps, int depth)
+{
+ if (q_l == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_q_trust_dom");
+ depth++;
+
+ if(!prs_uint32("ptr ", ps, depth, &q_l->ptr))
+ return False;
+ if(!smb_io_unistr2 ("", &q_l->uni_server_name, q_l->ptr, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an NET_Q_REQ_CHAL structure.
+********************************************************************/
+
+void init_q_req_chal(NET_Q_REQ_CHAL *q_c,
+ const char *logon_srv, const char *logon_clnt,
+ DOM_CHAL *clnt_chal)
+{
+ DEBUG(5,("init_q_req_chal: %d\n", __LINE__));
+
+ q_c->undoc_buffer = 1; /* don't know what this buffer is */
+
+ init_unistr2(&q_c->uni_logon_srv, logon_srv , UNI_STR_TERMINATE);
+ init_unistr2(&q_c->uni_logon_clnt, logon_clnt, UNI_STR_TERMINATE);
+
+ memcpy(q_c->clnt_chal.data, clnt_chal->data, sizeof(clnt_chal->data));
+
+ DEBUG(5,("init_q_req_chal: %d\n", __LINE__));
+}
+
+/*******************************************************************
+ Reads or writes an NET_Q_REQ_CHAL structure.
+********************************************************************/
+
+BOOL net_io_q_req_chal(const char *desc, NET_Q_REQ_CHAL *q_c, prs_struct *ps, int depth)
+{
+ if (q_c == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_q_req_chal");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("undoc_buffer", ps, depth, &q_c->undoc_buffer))
+ return False;
+
+ if(!smb_io_unistr2("", &q_c->uni_logon_srv, True, ps, depth)) /* logon server unicode string */
+ return False;
+ if(!smb_io_unistr2("", &q_c->uni_logon_clnt, True, ps, depth)) /* logon client unicode string */
+ return False;
+
+ if(!smb_io_chal("", &q_c->clnt_chal, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_r_req_chal(const char *desc, NET_R_REQ_CHAL *r_c, prs_struct *ps, int depth)
+{
+ if (r_c == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_r_req_chal");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_chal("", &r_c->srv_chal, ps, depth)) /* server challenge */
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_q_auth(const char *desc, NET_Q_AUTH *q_a, prs_struct *ps, int depth)
+{
+ if (q_a == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_q_auth");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_log_info ("", &q_a->clnt_id, ps, depth)) /* client identification info */
+ return False;
+ if(!smb_io_chal("", &q_a->clnt_chal, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_r_auth(const char *desc, NET_R_AUTH *r_a, prs_struct *ps, int depth)
+{
+ if (r_a == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_r_auth");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_chal("", &r_a->srv_chal, ps, depth)) /* server challenge */
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_a->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a NET_Q_AUTH_2 struct.
+********************************************************************/
+
+void init_q_auth_2(NET_Q_AUTH_2 *q_a,
+ const char *logon_srv, const char *acct_name, uint16 sec_chan, const char *comp_name,
+ DOM_CHAL *clnt_chal, uint32 clnt_flgs)
+{
+ DEBUG(5,("init_q_auth_2: %d\n", __LINE__));
+
+ init_log_info(&q_a->clnt_id, logon_srv, acct_name, sec_chan, comp_name);
+ memcpy(q_a->clnt_chal.data, clnt_chal->data, sizeof(clnt_chal->data));
+ q_a->clnt_flgs.neg_flags = clnt_flgs;
+
+ DEBUG(5,("init_q_auth_2: %d\n", __LINE__));
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_q_auth_2(const char *desc, NET_Q_AUTH_2 *q_a, prs_struct *ps, int depth)
+{
+ if (q_a == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_q_auth_2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_log_info ("", &q_a->clnt_id, ps, depth)) /* client identification info */
+ return False;
+ if(!smb_io_chal("", &q_a->clnt_chal, ps, depth))
+ return False;
+ if(!net_io_neg_flags("", &q_a->clnt_flgs, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_r_auth_2(const char *desc, NET_R_AUTH_2 *r_a, prs_struct *ps, int depth)
+{
+ if (r_a == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_r_auth_2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_chal("", &r_a->srv_chal, ps, depth)) /* server challenge */
+ return False;
+ if(!net_io_neg_flags("", &r_a->srv_flgs, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_a->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a NET_Q_AUTH_3 struct.
+********************************************************************/
+
+void init_q_auth_3(NET_Q_AUTH_3 *q_a,
+ const char *logon_srv, const char *acct_name, uint16 sec_chan, const char *comp_name,
+ DOM_CHAL *clnt_chal, uint32 clnt_flgs)
+{
+ DEBUG(5,("init_q_auth_3: %d\n", __LINE__));
+
+ init_log_info(&q_a->clnt_id, logon_srv, acct_name, sec_chan, comp_name);
+ memcpy(q_a->clnt_chal.data, clnt_chal->data, sizeof(clnt_chal->data));
+ q_a->clnt_flgs.neg_flags = clnt_flgs;
+
+ DEBUG(5,("init_q_auth_3: %d\n", __LINE__));
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_q_auth_3(const char *desc, NET_Q_AUTH_3 *q_a, prs_struct *ps, int depth)
+{
+ if (q_a == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_q_auth_3");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_log_info ("", &q_a->clnt_id, ps, depth)) /* client identification info */
+ return False;
+ if(!smb_io_chal("", &q_a->clnt_chal, ps, depth))
+ return False;
+ if(!net_io_neg_flags("", &q_a->clnt_flgs, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_r_auth_3(const char *desc, NET_R_AUTH_3 *r_a, prs_struct *ps, int depth)
+{
+ if (r_a == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_r_auth_3");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_chal("srv_chal", &r_a->srv_chal, ps, depth)) /* server challenge */
+ return False;
+ if(!net_io_neg_flags("srv_flgs", &r_a->srv_flgs, ps, depth))
+ return False;
+ if (!prs_uint32("unknown", ps, depth, &r_a->unknown))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_a->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Inits a NET_Q_SRV_PWSET.
+********************************************************************/
+
+void init_q_srv_pwset(NET_Q_SRV_PWSET *q_s,
+ const char *logon_srv, const char *sess_key, const char *acct_name,
+ uint16 sec_chan, const char *comp_name,
+ DOM_CRED *cred, uchar hashed_mach_pwd[16])
+{
+ unsigned char nt_cypher[16];
+
+ DEBUG(5,("init_q_srv_pwset\n"));
+
+ /* Process the new password. */
+ cred_hash3( nt_cypher, hashed_mach_pwd, (const unsigned char *)sess_key, 1);
+
+ init_clnt_info(&q_s->clnt_id, logon_srv, acct_name, sec_chan, comp_name, cred);
+
+ memcpy(q_s->pwd, nt_cypher, sizeof(q_s->pwd));
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_q_srv_pwset(const char *desc, NET_Q_SRV_PWSET *q_s, prs_struct *ps, int depth)
+{
+ if (q_s == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_q_srv_pwset");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_clnt_info("", &q_s->clnt_id, ps, depth)) /* client identification/authentication info */
+ return False;
+ if(!prs_uint8s (False, "pwd", ps, depth, q_s->pwd, 16)) /* new password - undocumented */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_r_srv_pwset(const char *desc, NET_R_SRV_PWSET *r_s, prs_struct *ps, int depth)
+{
+ if (r_s == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_r_srv_pwset");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_cred("", &r_s->srv_cred, ps, depth)) /* server challenge */
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_s->status))
+ return False;
+
+ return True;
+}
+
+/*************************************************************************
+ Init DOM_SID2 array from a string containing multiple sids
+ *************************************************************************/
+
+static int init_dom_sid2s(TALLOC_CTX *ctx, const char *sids_str, DOM_SID2 **ppsids)
+{
+ const char *ptr;
+ pstring s2;
+ int count = 0;
+
+ DEBUG(4,("init_dom_sid2s: %s\n", sids_str ? sids_str:""));
+
+ *ppsids = NULL;
+
+ if(sids_str) {
+ int number;
+ DOM_SID2 *sids;
+
+ /* Count the number of valid SIDs. */
+ for (count = 0, ptr = sids_str; next_token(&ptr, s2, NULL, sizeof(s2)); ) {
+ DOM_SID tmpsid;
+ if (string_to_sid(&tmpsid, s2))
+ count++;
+ }
+
+ /* Now allocate space for them. */
+ *ppsids = (DOM_SID2 *)talloc_zero(ctx, count * sizeof(DOM_SID2));
+ if (*ppsids == NULL)
+ return 0;
+
+ sids = *ppsids;
+
+ for (number = 0, ptr = sids_str; next_token(&ptr, s2, NULL, sizeof(s2)); ) {
+ DOM_SID tmpsid;
+ if (string_to_sid(&tmpsid, s2)) {
+ /* count only valid sids */
+ init_dom_sid2(&sids[number], &tmpsid);
+ number++;
+ }
+ }
+ }
+
+ return count;
+}
+
+/*******************************************************************
+ Inits a NET_ID_INFO_1 structure.
+********************************************************************/
+
+void init_id_info1(NET_ID_INFO_1 *id, const char *domain_name,
+ uint32 param_ctrl, uint32 log_id_low, uint32 log_id_high,
+ const char *user_name, const char *wksta_name,
+ const char *sess_key,
+ unsigned char lm_cypher[16], unsigned char nt_cypher[16])
+{
+ unsigned char lm_owf[16];
+ unsigned char nt_owf[16];
+
+ DEBUG(5,("init_id_info1: %d\n", __LINE__));
+
+ id->ptr_id_info1 = 1;
+
+ id->param_ctrl = param_ctrl;
+ init_logon_id(&id->logon_id, log_id_low, log_id_high);
+
+
+ if (lm_cypher && nt_cypher) {
+ unsigned char key[16];
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("lm cypher:"));
+ dump_data(100, (char *)lm_cypher, 16);
+
+ DEBUG(100,("nt cypher:"));
+ dump_data(100, (char *)nt_cypher, 16);
+#endif
+
+ memset(key, 0, 16);
+ memcpy(key, sess_key, 8);
+
+ memcpy(lm_owf, lm_cypher, 16);
+ SamOEMhash(lm_owf, key, 16);
+ memcpy(nt_owf, nt_cypher, 16);
+ SamOEMhash(nt_owf, key, 16);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("encrypt of lm owf password:"));
+ dump_data(100, (char *)lm_owf, 16);
+
+ DEBUG(100,("encrypt of nt owf password:"));
+ dump_data(100, (char *)nt_owf, 16);
+#endif
+ /* set up pointers to cypher blocks */
+ lm_cypher = lm_owf;
+ nt_cypher = nt_owf;
+ }
+
+ init_owf_info(&id->lm_owf, lm_cypher);
+ init_owf_info(&id->nt_owf, nt_cypher);
+
+ init_unistr2(&id->uni_domain_name, domain_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&id->hdr_domain_name, &id->uni_domain_name);
+ init_unistr2(&id->uni_user_name, user_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&id->hdr_user_name, &id->uni_user_name);
+ init_unistr2(&id->uni_wksta_name, wksta_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&id->hdr_wksta_name, &id->uni_wksta_name);
+}
+
+/*******************************************************************
+ Reads or writes an NET_ID_INFO_1 structure.
+********************************************************************/
+
+static BOOL net_io_id_info1(const char *desc, NET_ID_INFO_1 *id, prs_struct *ps, int depth)
+{
+ if (id == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_id_info1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_id_info1", ps, depth, &id->ptr_id_info1))
+ return False;
+
+ if (id->ptr_id_info1 != 0) {
+ if(!smb_io_unihdr("unihdr", &id->hdr_domain_name, ps, depth))
+ return False;
+
+ if(!prs_uint32("param_ctrl", ps, depth, &id->param_ctrl))
+ return False;
+ if(!smb_io_logon_id("", &id->logon_id, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr("unihdr", &id->hdr_user_name, ps, depth))
+ return False;
+ if(!smb_io_unihdr("unihdr", &id->hdr_wksta_name, ps, depth))
+ return False;
+
+ if(!smb_io_owf_info("", &id->lm_owf, ps, depth))
+ return False;
+ if(!smb_io_owf_info("", &id->nt_owf, ps, depth))
+ return False;
+
+ if(!smb_io_unistr2("unistr2", &id->uni_domain_name,
+ id->hdr_domain_name.buffer, ps, depth))
+ return False;
+ if(!smb_io_unistr2("unistr2", &id->uni_user_name,
+ id->hdr_user_name.buffer, ps, depth))
+ return False;
+ if(!smb_io_unistr2("unistr2", &id->uni_wksta_name,
+ id->hdr_wksta_name.buffer, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+Inits a NET_ID_INFO_2 structure.
+
+This is a network logon packet. The log_id parameters
+are what an NT server would generate for LUID once the
+user is logged on. I don't think we care about them.
+
+Note that this has no access to the NT and LM hashed passwords,
+so it forwards the challenge, and the NT and LM responses (24
+bytes each) over the secure channel to the Domain controller
+for it to say yea or nay. This is the preferred method of
+checking for a logon as it doesn't export the password
+hashes to anyone who has compromised the secure channel. JRA.
+********************************************************************/
+
+void init_id_info2(NET_ID_INFO_2 * id, const char *domain_name,
+ uint32 param_ctrl,
+ uint32 log_id_low, uint32 log_id_high,
+ const char *user_name, const char *wksta_name,
+ const uchar lm_challenge[8],
+ const uchar * lm_chal_resp, size_t lm_chal_resp_len,
+ const uchar * nt_chal_resp, size_t nt_chal_resp_len)
+{
+ unsigned char lm_owf[24];
+ unsigned char nt_owf[128];
+
+ DEBUG(5,("init_id_info2: %d\n", __LINE__));
+
+ id->ptr_id_info2 = 1;
+
+
+ id->param_ctrl = param_ctrl;
+ init_logon_id(&id->logon_id, log_id_low, log_id_high);
+
+ if (nt_chal_resp) {
+ /* oops. can only send what-ever-it-is direct */
+ memcpy(nt_owf, nt_chal_resp, MIN(sizeof(nt_owf), nt_chal_resp_len));
+ nt_chal_resp = nt_owf;
+ }
+ if (lm_chal_resp) {
+ /* oops. can only send what-ever-it-is direct */
+ memcpy(lm_owf, lm_chal_resp, MIN(sizeof(lm_owf), lm_chal_resp_len));
+ lm_chal_resp = lm_owf;
+ }
+
+ memcpy(id->lm_chal, lm_challenge, sizeof(id->lm_chal));
+ init_str_hdr(&id->hdr_nt_chal_resp, nt_chal_resp_len, nt_chal_resp_len, (nt_chal_resp != NULL) ? 1 : 0);
+ init_str_hdr(&id->hdr_lm_chal_resp, lm_chal_resp_len, lm_chal_resp_len, (lm_chal_resp != NULL) ? 1 : 0);
+
+ init_unistr2(&id->uni_domain_name, domain_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&id->hdr_domain_name, &id->uni_domain_name);
+ init_unistr2(&id->uni_user_name, user_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&id->hdr_user_name, &id->uni_user_name);
+ init_unistr2(&id->uni_wksta_name, wksta_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&id->hdr_wksta_name, &id->uni_wksta_name);
+
+ init_string2(&id->nt_chal_resp, (const char *)nt_chal_resp, nt_chal_resp_len, nt_chal_resp_len);
+ init_string2(&id->lm_chal_resp, (const char *)lm_chal_resp, lm_chal_resp_len, lm_chal_resp_len);
+
+}
+
+/*******************************************************************
+ Reads or writes an NET_ID_INFO_2 structure.
+********************************************************************/
+
+static BOOL net_io_id_info2(const char *desc, NET_ID_INFO_2 *id, prs_struct *ps, int depth)
+{
+ if (id == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_id_info2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_id_info2", ps, depth, &id->ptr_id_info2))
+ return False;
+
+ if (id->ptr_id_info2 != 0) {
+ if(!smb_io_unihdr("unihdr", &id->hdr_domain_name, ps, depth))
+ return False;
+
+ if(!prs_uint32("param_ctrl", ps, depth, &id->param_ctrl))
+ return False;
+ if(!smb_io_logon_id("", &id->logon_id, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr("unihdr", &id->hdr_user_name, ps, depth))
+ return False;
+ if(!smb_io_unihdr("unihdr", &id->hdr_wksta_name, ps, depth))
+ return False;
+
+ if(!prs_uint8s (False, "lm_chal", ps, depth, id->lm_chal, 8)) /* lm 8 byte challenge */
+ return False;
+
+ if(!smb_io_strhdr("hdr_nt_chal_resp", &id->hdr_nt_chal_resp, ps, depth))
+ return False;
+ if(!smb_io_strhdr("hdr_lm_chal_resp", &id->hdr_lm_chal_resp, ps, depth))
+ return False;
+
+ if(!smb_io_unistr2("uni_domain_name", &id->uni_domain_name,
+ id->hdr_domain_name.buffer, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_user_name ", &id->uni_user_name,
+ id->hdr_user_name.buffer, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_wksta_name ", &id->uni_wksta_name,
+ id->hdr_wksta_name.buffer, ps, depth))
+ return False;
+ if(!smb_io_string2("nt_chal_resp", &id->nt_chal_resp,
+ id->hdr_nt_chal_resp.buffer, ps, depth))
+ return False;
+ if(!smb_io_string2("lm_chal_resp", &id->lm_chal_resp,
+ id->hdr_lm_chal_resp.buffer, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+
+/*******************************************************************
+ Inits a DOM_SAM_INFO structure.
+********************************************************************/
+
+void init_sam_info(DOM_SAM_INFO *sam,
+ const char *logon_srv, const char *comp_name,
+ DOM_CRED *clnt_cred,
+ DOM_CRED *rtn_cred, uint16 logon_level,
+ NET_ID_INFO_CTR *ctr)
+{
+ DEBUG(5,("init_sam_info: %d\n", __LINE__));
+
+ init_clnt_info2(&sam->client, logon_srv, comp_name, clnt_cred);
+
+ if (rtn_cred != NULL) {
+ sam->ptr_rtn_cred = 1;
+ memcpy(&sam->rtn_cred, rtn_cred, sizeof(sam->rtn_cred));
+ } else {
+ sam->ptr_rtn_cred = 0;
+ }
+
+ sam->logon_level = logon_level;
+ sam->ctr = ctr;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_SAM_INFO structure.
+********************************************************************/
+
+static BOOL net_io_id_info_ctr(const char *desc, NET_ID_INFO_CTR **pp_ctr, prs_struct *ps, int depth)
+{
+ NET_ID_INFO_CTR *ctr = *pp_ctr;
+
+ prs_debug(ps, depth, desc, "smb_io_sam_info");
+ depth++;
+
+ if (UNMARSHALLING(ps)) {
+ ctr = *pp_ctr = (NET_ID_INFO_CTR *)prs_alloc_mem(ps, sizeof(NET_ID_INFO_CTR));
+ if (ctr == NULL)
+ return False;
+ }
+
+ if (ctr == NULL)
+ return False;
+
+ /* don't 4-byte align here! */
+
+ if(!prs_uint16("switch_value ", ps, depth, &ctr->switch_value))
+ return False;
+
+ switch (ctr->switch_value) {
+ case 1:
+ if(!net_io_id_info1("", &ctr->auth.id1, ps, depth))
+ return False;
+ break;
+ case 2:
+ if(!net_io_id_info2("", &ctr->auth.id2, ps, depth))
+ return False;
+ break;
+ default:
+ /* PANIC! */
+ DEBUG(4,("smb_io_sam_info: unknown switch_value!\n"));
+ break;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_SAM_INFO structure.
+ ********************************************************************/
+
+static BOOL smb_io_sam_info(const char *desc, DOM_SAM_INFO *sam, prs_struct *ps, int depth)
+{
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_sam_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_clnt_info2("", &sam->client, ps, depth))
+ return False;
+
+ if(!prs_uint32("ptr_rtn_cred ", ps, depth, &sam->ptr_rtn_cred))
+ return False;
+ if(!smb_io_cred("", &sam->rtn_cred, ps, depth))
+ return False;
+
+ if(!prs_uint16("logon_level ", ps, depth, &sam->logon_level))
+ return False;
+
+ if (sam->logon_level != 0) {
+ if(!net_io_id_info_ctr("logon_info", &sam->ctr, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*************************************************************************
+ Inits a NET_USER_INFO_3 structure.
+
+ This is a network logon reply packet, and contains much information about
+ the user. This information is passed as a (very long) paramater list
+ to avoid having to link in the PASSDB code to every program that deals
+ with this file.
+ *************************************************************************/
+
+void init_net_user_info3(TALLOC_CTX *ctx, NET_USER_INFO_3 *usr,
+ uint32 user_rid,
+ uint32 group_rid,
+
+ const char* user_name,
+ const char* full_name,
+ const char* home_dir,
+ const char* dir_drive,
+ const char* logon_script,
+ const char* profile_path,
+
+ time_t unix_logon_time,
+ time_t unix_logoff_time,
+ time_t unix_kickoff_time,
+ time_t unix_pass_last_set_time,
+ time_t unix_pass_can_change_time,
+ time_t unix_pass_must_change_time,
+
+ uint16 logon_count, uint16 bad_pw_count,
+ uint32 num_groups, const DOM_GID *gids,
+ uint32 user_flgs, uchar nt_session_key[16],
+ uchar lm_session_key[16],
+ const char *logon_srv, const char *logon_dom,
+ const DOM_SID *dom_sid, const char *other_sids)
+{
+ /* only cope with one "other" sid, right now. */
+ /* need to count the number of space-delimited sids */
+ unsigned int i;
+ int num_other_sids = 0;
+
+ NTTIME logon_time, logoff_time, kickoff_time,
+ pass_last_set_time, pass_can_change_time,
+ pass_must_change_time;
+
+ ZERO_STRUCTP(usr);
+
+ usr->ptr_user_info = 1; /* yes, we're bothering to put USER_INFO data here */
+
+ /* Create NTTIME structs */
+ unix_to_nt_time (&logon_time, unix_logon_time);
+ unix_to_nt_time (&logoff_time, unix_logoff_time);
+ unix_to_nt_time (&kickoff_time, unix_kickoff_time);
+ unix_to_nt_time (&pass_last_set_time, unix_pass_last_set_time);
+ unix_to_nt_time (&pass_can_change_time, unix_pass_can_change_time);
+ unix_to_nt_time (&pass_must_change_time, unix_pass_must_change_time);
+
+ usr->logon_time = logon_time;
+ usr->logoff_time = logoff_time;
+ usr->kickoff_time = kickoff_time;
+ usr->pass_last_set_time = pass_last_set_time;
+ usr->pass_can_change_time = pass_can_change_time;
+ usr->pass_must_change_time = pass_must_change_time;
+
+ usr->logon_count = logon_count;
+ usr->bad_pw_count = bad_pw_count;
+
+ usr->user_rid = user_rid;
+ usr->group_rid = group_rid;
+ usr->num_groups = num_groups;
+
+ usr->buffer_groups = 1; /* indicates fill in groups, below, even if there are none */
+ usr->user_flgs = user_flgs;
+
+ if (nt_session_key != NULL)
+ memcpy(usr->user_sess_key, nt_session_key, sizeof(usr->user_sess_key));
+ else
+ memset((char *)usr->user_sess_key, '\0', sizeof(usr->user_sess_key));
+
+ usr->buffer_dom_id = dom_sid ? 1 : 0; /* yes, we're bothering to put a domain SID in */
+
+ memset((char *)usr->padding, '\0', sizeof(usr->padding));
+
+#if 0 /* JRATEST - exchange auth test. */
+ if (lm_session_key != NULL)
+ memcpy(usr->padding, lm_session_key, sizeof(usr->user_sess_key));
+#endif
+
+ num_other_sids = init_dom_sid2s(ctx, other_sids, &usr->other_sids);
+
+ usr->num_other_sids = num_other_sids;
+ usr->buffer_other_sids = (num_other_sids != 0) ? 1 : 0;
+
+ init_unistr2(&usr->uni_user_name, user_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_user_name, &usr->uni_user_name);
+ init_unistr2(&usr->uni_full_name, full_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_full_name, &usr->uni_full_name);
+ init_unistr2(&usr->uni_logon_script, logon_script, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_logon_script, &usr->uni_logon_script);
+ init_unistr2(&usr->uni_profile_path, profile_path, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_profile_path, &usr->uni_profile_path);
+ init_unistr2(&usr->uni_home_dir, home_dir, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_home_dir, &usr->uni_home_dir);
+ init_unistr2(&usr->uni_dir_drive, dir_drive, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_dir_drive, &usr->uni_dir_drive);
+
+ usr->num_groups2 = num_groups;
+
+ usr->gids = (DOM_GID *)talloc_zero(ctx,sizeof(DOM_GID) * (num_groups));
+ if (usr->gids == NULL && num_groups>0)
+ return;
+
+ for (i = 0; i < num_groups; i++)
+ usr->gids[i] = gids[i];
+
+ init_unistr2(&usr->uni_logon_srv, logon_srv, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_logon_srv, &usr->uni_logon_srv);
+ init_unistr2(&usr->uni_logon_dom, logon_dom, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_logon_dom, &usr->uni_logon_dom);
+
+ init_dom_sid2(&usr->dom_sid, dom_sid);
+ /* "other" sids are set up above */
+}
+
+/*******************************************************************
+ This code has been modified to cope with a NET_USER_INFO_2 - which is
+ exactly the same as a NET_USER_INFO_3, minus the other sids parameters.
+ We use validation level to determine if we're marshalling a info 2 or
+ INFO_3 - be we always return an INFO_3. Based on code donated by Marc
+ Jacobsen at HP. JRA.
+********************************************************************/
+
+BOOL net_io_user_info3(const char *desc, NET_USER_INFO_3 *usr, prs_struct *ps,
+ int depth, uint16 validation_level)
+{
+ unsigned int i;
+
+ if (usr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_user_info3");
+ depth++;
+
+ if (UNMARSHALLING(ps))
+ ZERO_STRUCTP(usr);
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_user_info ", ps, depth, &usr->ptr_user_info))
+ return False;
+
+ if (usr->ptr_user_info == 0)
+ return True;
+
+ if(!smb_io_time("logon time", &usr->logon_time, ps, depth)) /* logon time */
+ return False;
+ if(!smb_io_time("logoff time", &usr->logoff_time, ps, depth)) /* logoff time */
+ return False;
+ if(!smb_io_time("kickoff time", &usr->kickoff_time, ps, depth)) /* kickoff time */
+ return False;
+ if(!smb_io_time("last set time", &usr->pass_last_set_time, ps, depth)) /* password last set time */
+ return False;
+ if(!smb_io_time("can change time", &usr->pass_can_change_time , ps, depth)) /* password can change time */
+ return False;
+ if(!smb_io_time("must change time", &usr->pass_must_change_time, ps, depth)) /* password must change time */
+ return False;
+
+ if(!smb_io_unihdr("hdr_user_name", &usr->hdr_user_name, ps, depth)) /* username unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_full_name", &usr->hdr_full_name, ps, depth)) /* user's full name unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_logon_script", &usr->hdr_logon_script, ps, depth)) /* logon script unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_profile_path", &usr->hdr_profile_path, ps, depth)) /* profile path unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_home_dir", &usr->hdr_home_dir, ps, depth)) /* home directory unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_dir_drive", &usr->hdr_dir_drive, ps, depth)) /* home directory drive unicode string header */
+ return False;
+
+ if(!prs_uint16("logon_count ", ps, depth, &usr->logon_count)) /* logon count */
+ return False;
+ if(!prs_uint16("bad_pw_count ", ps, depth, &usr->bad_pw_count)) /* bad password count */
+ return False;
+
+ if(!prs_uint32("user_rid ", ps, depth, &usr->user_rid)) /* User RID */
+ return False;
+ if(!prs_uint32("group_rid ", ps, depth, &usr->group_rid)) /* Group RID */
+ return False;
+ if(!prs_uint32("num_groups ", ps, depth, &usr->num_groups)) /* num groups */
+ return False;
+ if(!prs_uint32("buffer_groups ", ps, depth, &usr->buffer_groups)) /* undocumented buffer pointer to groups. */
+ return False;
+ if(!prs_uint32("user_flgs ", ps, depth, &usr->user_flgs)) /* user flags */
+ return False;
+
+ if(!prs_uint8s(False, "user_sess_key", ps, depth, usr->user_sess_key, 16)) /* user session key */
+ return False;
+
+ if(!smb_io_unihdr("hdr_logon_srv", &usr->hdr_logon_srv, ps, depth)) /* logon server unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_logon_dom", &usr->hdr_logon_dom, ps, depth)) /* logon domain unicode string header */
+ return False;
+
+ if(!prs_uint32("buffer_dom_id ", ps, depth, &usr->buffer_dom_id)) /* undocumented logon domain id pointer */
+ return False;
+ if(!prs_uint8s (False, "padding ", ps, depth, usr->padding, 40)) /* unused padding bytes? */
+ return False;
+
+ if (validation_level == 3) {
+ if(!prs_uint32("num_other_sids", ps, depth, &usr->num_other_sids)) /* 0 - num_sids */
+ return False;
+ if(!prs_uint32("buffer_other_sids", ps, depth, &usr->buffer_other_sids)) /* NULL - undocumented pointer to SIDs. */
+ return False;
+ } else {
+ if (UNMARSHALLING(ps)) {
+ usr->num_other_sids = 0;
+ usr->buffer_other_sids = 0;
+ }
+ }
+
+ if(!smb_io_unistr2("uni_user_name", &usr->uni_user_name, usr->hdr_user_name.buffer, ps, depth)) /* username unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_full_name", &usr->uni_full_name, usr->hdr_full_name.buffer, ps, depth)) /* user's full name unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_logon_script", &usr->uni_logon_script, usr->hdr_logon_script.buffer, ps, depth)) /* logon script unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_profile_path", &usr->uni_profile_path, usr->hdr_profile_path.buffer, ps, depth)) /* profile path unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_home_dir", &usr->uni_home_dir, usr->hdr_home_dir.buffer, ps, depth)) /* home directory unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_dir_drive", &usr->uni_dir_drive, usr->hdr_dir_drive.buffer, ps, depth)) /* home directory drive unicode string */
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("num_groups2 ", ps, depth, &usr->num_groups2)) /* num groups */
+ return False;
+
+ if (UNMARSHALLING(ps) && usr->num_groups2 > 0) {
+ usr->gids = (DOM_GID *)prs_alloc_mem(ps, sizeof(DOM_GID)*usr->num_groups2);
+ if (usr->gids == NULL)
+ return False;
+ }
+
+ for (i = 0; i < usr->num_groups2; i++) {
+ if(!smb_io_gid("", &usr->gids[i], ps, depth)) /* group info */
+ return False;
+ }
+
+ if(!smb_io_unistr2("uni_logon_srv", &usr->uni_logon_srv, usr->hdr_logon_srv.buffer, ps, depth)) /* logon server unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_logon_dom", &usr->uni_logon_dom, usr->hdr_logon_srv.buffer, ps, depth)) /* logon domain unicode string */
+ return False;
+
+ if(!smb_io_dom_sid2("", &usr->dom_sid, ps, depth)) /* domain SID */
+ return False;
+
+ if (usr->num_other_sids) {
+
+ if (UNMARSHALLING(ps)) {
+ usr->other_sids = (DOM_SID2 *)prs_alloc_mem(ps, sizeof(DOM_SID2)*usr->num_other_sids);
+ if (usr->other_sids == NULL)
+ return False;
+ }
+
+ if(!prs_uint32("num_other_groups", ps, depth, &usr->num_other_groups))
+ return False;
+
+ if (UNMARSHALLING(ps) && usr->num_other_groups > 0) {
+ usr->other_gids = (DOM_GID *)prs_alloc_mem(ps, sizeof(DOM_GID)*usr->num_other_groups);
+ if (usr->other_gids == NULL)
+ return False;
+ }
+
+ for (i = 0; i < usr->num_other_groups; i++) {
+ if(!smb_io_gid("", &usr->other_gids[i], ps, depth)) /* other GIDs */
+ return False;
+ }
+ for (i = 0; i < usr->num_other_sids; i++) {
+ if(!smb_io_dom_sid2("", &usr->other_sids[i], ps, depth)) /* other domain SIDs */
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_q_sam_logon(const char *desc, NET_Q_SAM_LOGON *q_l, prs_struct *ps, int depth)
+{
+ if (q_l == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_q_sam_logon");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_sam_info("", &q_l->sam_id, ps, depth))
+ return False;
+
+ if(!prs_align_uint16(ps))
+ return False;
+
+ if(!prs_uint16("validation_level", ps, depth, &q_l->validation_level))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_r_sam_logon(const char *desc, NET_R_SAM_LOGON *r_l, prs_struct *ps, int depth)
+{
+ if (r_l == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_r_sam_logon");
+ depth++;
+
+ if(!prs_uint32("buffer_creds", ps, depth, &r_l->buffer_creds)) /* undocumented buffer pointer */
+ return False;
+ if(!smb_io_cred("", &r_l->srv_creds, ps, depth)) /* server credentials. server time stamp appears to be ignored. */
+ return False;
+
+ if(!prs_uint16("switch_value", ps, depth, &r_l->switch_value))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+#if 1 /* W2k always needs this - even for bad passwd. JRA */
+ if(!net_io_user_info3("", r_l->user, ps, depth, r_l->switch_value))
+ return False;
+#else
+ if (r_l->switch_value != 0) {
+ if(!net_io_user_info3("", r_l->user, ps, depth, r_l->switch_value))
+ return False;
+ }
+#endif
+
+ if(!prs_uint32("auth_resp ", ps, depth, &r_l->auth_resp)) /* 1 - Authoritative response; 0 - Non-Auth? */
+ return False;
+
+ if(!prs_ntstatus("status ", ps, depth, &r_l->status))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_q_sam_logoff(const char *desc, NET_Q_SAM_LOGOFF *q_l, prs_struct *ps, int depth)
+{
+ if (q_l == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_q_sam_logoff");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_sam_info("", &q_l->sam_id, ps, depth)) /* domain SID */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_r_sam_logoff(const char *desc, NET_R_SAM_LOGOFF *r_l, prs_struct *ps, int depth)
+{
+ if (r_l == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_r_sam_logoff");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("buffer_creds", ps, depth, &r_l->buffer_creds)) /* undocumented buffer pointer */
+ return False;
+ if(!smb_io_cred("", &r_l->srv_creds, ps, depth)) /* server credentials. server time stamp appears to be ignored. */
+ return False;
+
+ if(!prs_ntstatus("status ", ps, depth, &r_l->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+makes a NET_Q_SAM_SYNC structure.
+********************************************************************/
+BOOL init_net_q_sam_sync(NET_Q_SAM_SYNC * q_s, const char *srv_name,
+ const char *cli_name, DOM_CRED *cli_creds,
+ DOM_CRED *ret_creds, uint32 database_id,
+ uint32 next_rid)
+{
+ DEBUG(5, ("init_q_sam_sync\n"));
+
+ init_unistr2(&q_s->uni_srv_name, srv_name, UNI_STR_TERMINATE);
+ init_unistr2(&q_s->uni_cli_name, cli_name, UNI_STR_TERMINATE);
+
+ if (cli_creds)
+ memcpy(&q_s->cli_creds, cli_creds, sizeof(q_s->cli_creds));
+
+ if (cli_creds)
+ memcpy(&q_s->ret_creds, ret_creds, sizeof(q_s->ret_creds));
+ else
+ memset(&q_s->ret_creds, 0, sizeof(q_s->ret_creds));
+
+ q_s->database_id = database_id;
+ q_s->restart_state = 0;
+ q_s->sync_context = next_rid;
+ q_s->max_size = 0xffff;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL net_io_q_sam_sync(const char *desc, NET_Q_SAM_SYNC * q_s, prs_struct *ps,
+ int depth)
+{
+ prs_debug(ps, depth, desc, "net_io_q_sam_sync");
+ depth++;
+
+ if (!smb_io_unistr2("", &q_s->uni_srv_name, True, ps, depth))
+ return False;
+ if (!smb_io_unistr2("", &q_s->uni_cli_name, True, ps, depth))
+ return False;
+
+ if (!smb_io_cred("", &q_s->cli_creds, ps, depth))
+ return False;
+ if (!smb_io_cred("", &q_s->ret_creds, ps, depth))
+ return False;
+
+ if (!prs_uint32("database_id ", ps, depth, &q_s->database_id))
+ return False;
+ if (!prs_uint32("restart_state", ps, depth, &q_s->restart_state))
+ return False;
+ if (!prs_uint32("sync_context ", ps, depth, &q_s->sync_context))
+ return False;
+
+ if (!prs_uint32("max_size", ps, depth, &q_s->max_size))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_delta_hdr(const char *desc, SAM_DELTA_HDR * delta,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "net_io_sam_delta_hdr");
+ depth++;
+
+ if (!prs_uint16("type", ps, depth, &delta->type))
+ return False;
+ if (!prs_uint16("type2", ps, depth, &delta->type2))
+ return False;
+ if (!prs_uint32("target_rid", ps, depth, &delta->target_rid))
+ return False;
+
+ if (!prs_uint32("type3", ps, depth, &delta->type3))
+ return False;
+
+ /* Not sure why we need this but it seems to be necessary to get
+ sam deltas working. */
+
+ if (delta->type != 0x16) {
+ if (!prs_uint32("ptr_delta", ps, depth, &delta->ptr_delta))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_delta_mod_count(const char *desc, SAM_DELTA_MOD_COUNT *info,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "net_io_sam_delta_stamp");
+ depth++;
+
+ if (!prs_uint32("seqnum", ps, depth, &info->seqnum))
+ return False;
+ if (!prs_uint32("dom_mod_count_ptr", ps, depth,
+ &info->dom_mod_count_ptr))
+ return False;
+
+ if (info->dom_mod_count_ptr) {
+ if (!prs_uint64("dom_mod_count", ps, depth,
+ &info->dom_mod_count))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_domain_info(const char *desc, SAM_DOMAIN_INFO * info,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "net_io_sam_domain_info");
+ depth++;
+
+ if (!smb_io_unihdr("hdr_dom_name", &info->hdr_dom_name, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_oem_info", &info->hdr_oem_info, ps, depth))
+ return False;
+
+ if (!prs_uint64("force_logoff", ps, depth, &info->force_logoff))
+ return False;
+ if (!prs_uint16("min_pwd_len", ps, depth, &info->min_pwd_len))
+ return False;
+ if (!prs_uint16("pwd_history_len", ps, depth, &info->pwd_history_len))
+ return False;
+ if (!prs_uint64("max_pwd_age", ps, depth, &info->max_pwd_age))
+ return False;
+ if (!prs_uint64("min_pwd_age", ps, depth, &info->min_pwd_age))
+ return False;
+ if (!prs_uint64("dom_mod_count", ps, depth, &info->dom_mod_count))
+ return False;
+ if (!smb_io_time("creation_time", &info->creation_time, ps, depth))
+ return False;
+
+ if (!smb_io_bufhdr2("hdr_sec_desc", &info->hdr_sec_desc, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_unknown", &info->hdr_unknown, ps, depth))
+ return False;
+
+ if (ps->data_offset + 40 > ps->buffer_size)
+ return False;
+ ps->data_offset += 40;
+
+ if (!smb_io_unistr2("uni_dom_name", &info->uni_dom_name,
+ info->hdr_dom_name.buffer, ps, depth))
+ return False;
+ if (!smb_io_unistr2("buf_oem_info", &info->buf_oem_info,
+ info->hdr_oem_info.buffer, ps, depth))
+ return False;
+
+ if (!smb_io_buffer4("buf_sec_desc", &info->buf_sec_desc,
+ info->hdr_sec_desc.buffer, ps, depth))
+ return False;
+ if (!smb_io_unistr2("buf_unknown", &info->buf_unknown,
+ info->hdr_unknown.buffer, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_group_info(const char *desc, SAM_GROUP_INFO * info,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "net_io_sam_group_info");
+ depth++;
+
+ if (!smb_io_unihdr("hdr_grp_name", &info->hdr_grp_name, ps, depth))
+ return False;
+ if (!smb_io_gid("gid", &info->gid, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_grp_desc", &info->hdr_grp_desc, ps, depth))
+ return False;
+ if (!smb_io_bufhdr2("hdr_sec_desc", &info->hdr_sec_desc, ps, depth))
+ return False;
+
+ if (ps->data_offset + 48 > ps->buffer_size)
+ return False;
+ ps->data_offset += 48;
+
+ if (!smb_io_unistr2("uni_grp_name", &info->uni_grp_name,
+ info->hdr_grp_name.buffer, ps, depth))
+ return False;
+ if (!smb_io_unistr2("uni_grp_desc", &info->uni_grp_desc,
+ info->hdr_grp_desc.buffer, ps, depth))
+ return False;
+ if (!smb_io_buffer4("buf_sec_desc", &info->buf_sec_desc,
+ info->hdr_sec_desc.buffer, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_passwd_info(const char *desc, SAM_PWD * pwd,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "net_io_sam_passwd_info");
+ depth++;
+
+ if (!prs_uint32("unk_0 ", ps, depth, &pwd->unk_0))
+ return False;
+
+ if (!smb_io_unihdr("hdr_lm_pwd", &pwd->hdr_lm_pwd, ps, depth))
+ return False;
+ if (!prs_uint8s(False, "buf_lm_pwd", ps, depth, pwd->buf_lm_pwd, 16))
+ return False;
+
+ if (!smb_io_unihdr("hdr_nt_pwd", &pwd->hdr_nt_pwd, ps, depth))
+ return False;
+ if (!prs_uint8s(False, "buf_nt_pwd", ps, depth, pwd->buf_nt_pwd, 16))
+ return False;
+
+ if (!smb_io_unihdr("", &pwd->hdr_empty_lm, ps, depth))
+ return False;
+ if (!smb_io_unihdr("", &pwd->hdr_empty_nt, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+makes a SAM_ACCOUNT_INFO structure.
+********************************************************************/
+BOOL make_sam_account_info(SAM_ACCOUNT_INFO * info,
+ const UNISTR2 *user_name,
+ const UNISTR2 *full_name,
+ uint32 user_rid, uint32 group_rid,
+ const UNISTR2 *home_dir,
+ const UNISTR2 *dir_drive,
+ const UNISTR2 *log_scr,
+ const UNISTR2 *desc,
+ uint32 acb_info,
+ const UNISTR2 *prof_path,
+ const UNISTR2 *wkstas,
+ const UNISTR2 *unk_str, const UNISTR2 *mung_dial)
+{
+ int len_user_name = user_name != NULL ? user_name->uni_str_len : 0;
+ int len_full_name = full_name != NULL ? full_name->uni_str_len : 0;
+ int len_home_dir = home_dir != NULL ? home_dir->uni_str_len : 0;
+ int len_dir_drive = dir_drive != NULL ? dir_drive->uni_str_len : 0;
+ int len_logon_script = log_scr != NULL ? log_scr->uni_str_len : 0;
+ int len_profile_path = prof_path != NULL ? prof_path->uni_str_len : 0;
+ int len_description = desc != NULL ? desc->uni_str_len : 0;
+ int len_workstations = wkstas != NULL ? wkstas->uni_str_len : 0;
+ int len_unknown_str = unk_str != NULL ? unk_str->uni_str_len : 0;
+ int len_munged_dial = mung_dial != NULL ? mung_dial->uni_str_len : 0;
+
+ DEBUG(5, ("make_sam_account_info\n"));
+
+ make_uni_hdr(&info->hdr_acct_name, len_user_name);
+ make_uni_hdr(&info->hdr_full_name, len_full_name);
+ make_uni_hdr(&info->hdr_home_dir, len_home_dir);
+ make_uni_hdr(&info->hdr_dir_drive, len_dir_drive);
+ make_uni_hdr(&info->hdr_logon_script, len_logon_script);
+ make_uni_hdr(&info->hdr_profile, len_profile_path);
+ make_uni_hdr(&info->hdr_acct_desc, len_description);
+ make_uni_hdr(&info->hdr_workstations, len_workstations);
+ make_uni_hdr(&info->hdr_comment, len_unknown_str);
+ make_uni_hdr(&info->hdr_parameters, len_munged_dial);
+
+ /* not present */
+ make_bufhdr2(&info->hdr_sec_desc, 0, 0, 0);
+
+ info->user_rid = user_rid;
+ info->group_rid = group_rid;
+
+ init_nt_time(&info->logon_time);
+ init_nt_time(&info->logoff_time);
+ init_nt_time(&info->pwd_last_set_time);
+ init_nt_time(&info->acct_expiry_time);
+
+ info->logon_divs = 0xA8;
+ info->ptr_logon_hrs = 0; /* Don't care right now */
+
+ info->bad_pwd_count = 0;
+ info->logon_count = 0;
+ info->acb_info = acb_info;
+ info->nt_pwd_present = 0;
+ info->lm_pwd_present = 0;
+ info->pwd_expired = 0;
+ info->country = 0;
+ info->codepage = 0;
+
+ info->unknown1 = 0x4EC;
+ info->unknown2 = 0;
+
+ copy_unistr2(&info->uni_acct_name, user_name);
+ copy_unistr2(&info->uni_full_name, full_name);
+ copy_unistr2(&info->uni_home_dir, home_dir);
+ copy_unistr2(&info->uni_dir_drive, dir_drive);
+ copy_unistr2(&info->uni_logon_script, log_scr);
+ copy_unistr2(&info->uni_profile, prof_path);
+ copy_unistr2(&info->uni_acct_desc, desc);
+ copy_unistr2(&info->uni_workstations, wkstas);
+ copy_unistr2(&info->uni_comment, unk_str);
+ copy_unistr2(&info->uni_parameters, mung_dial);
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_account_info(const char *desc, uint8 sess_key[16],
+ SAM_ACCOUNT_INFO * info, prs_struct *ps,
+ int depth)
+{
+ BUFHDR2 hdr_priv_data;
+ uint32 i;
+
+ prs_debug(ps, depth, desc, "net_io_sam_account_info");
+ depth++;
+
+ if (!smb_io_unihdr("hdr_acct_name", &info->hdr_acct_name, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_full_name", &info->hdr_full_name, ps, depth))
+ return False;
+
+ if (!prs_uint32("user_rid ", ps, depth, &info->user_rid))
+ return False;
+ if (!prs_uint32("group_rid", ps, depth, &info->group_rid))
+ return False;
+
+ if (!smb_io_unihdr("hdr_home_dir ", &info->hdr_home_dir, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_dir_drive", &info->hdr_dir_drive, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_logon_script", &info->hdr_logon_script, ps,
+ depth))
+ return False;
+
+ if (!smb_io_unihdr("hdr_acct_desc", &info->hdr_acct_desc, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_workstations", &info->hdr_workstations, ps,
+ depth))
+ return False;
+
+ if (!smb_io_time("logon_time", &info->logon_time, ps, depth))
+ return False;
+ if (!smb_io_time("logoff_time", &info->logoff_time, ps, depth))
+ return False;
+
+ if (!prs_uint32("logon_divs ", ps, depth, &info->logon_divs))
+ return False;
+ if (!prs_uint32("ptr_logon_hrs", ps, depth, &info->ptr_logon_hrs))
+ return False;
+
+ if (!prs_uint16("bad_pwd_count", ps, depth, &info->bad_pwd_count))
+ return False;
+ if (!prs_uint16("logon_count", ps, depth, &info->logon_count))
+ return False;
+ if (!smb_io_time("pwd_last_set_time", &info->pwd_last_set_time, ps,
+ depth))
+ return False;
+ if (!smb_io_time("acct_expiry_time", &info->acct_expiry_time, ps,
+ depth))
+ return False;
+
+ if (!prs_uint32("acb_info", ps, depth, &info->acb_info))
+ return False;
+ if (!prs_uint8s(False, "nt_pwd", ps, depth, info->nt_pwd, 16))
+ return False;
+ if (!prs_uint8s(False, "lm_pwd", ps, depth, info->lm_pwd, 16))
+ return False;
+ if (!prs_uint8("lm_pwd_present", ps, depth, &info->lm_pwd_present))
+ return False;
+ if (!prs_uint8("nt_pwd_present", ps, depth, &info->nt_pwd_present))
+ return False;
+ if (!prs_uint8("pwd_expired", ps, depth, &info->pwd_expired))
+ return False;
+
+ if (!smb_io_unihdr("hdr_comment", &info->hdr_comment, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_parameters", &info->hdr_parameters, ps,
+ depth))
+ return False;
+ if (!prs_uint16("country", ps, depth, &info->country))
+ return False;
+ if (!prs_uint16("codepage", ps, depth, &info->codepage))
+ return False;
+
+ if (!smb_io_bufhdr2("hdr_priv_data", &hdr_priv_data, ps, depth))
+ return False;
+ if (!smb_io_bufhdr2("hdr_sec_desc", &info->hdr_sec_desc, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_profile", &info->hdr_profile, ps, depth))
+ return False;
+
+ for (i = 0; i < 3; i++)
+ {
+ if (!smb_io_unihdr("hdr_reserved", &info->hdr_reserved[i],
+ ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ if (!prs_uint32("dw_reserved", ps, depth,
+ &info->dw_reserved[i]))
+ return False;
+ }
+
+ if (!smb_io_unistr2("uni_acct_name", &info->uni_acct_name,
+ info->hdr_acct_name.buffer, ps, depth))
+ return False;
+ prs_align(ps);
+ if (!smb_io_unistr2("uni_full_name", &info->uni_full_name,
+ info->hdr_full_name.buffer, ps, depth))
+ return False;
+ prs_align(ps);
+ if (!smb_io_unistr2("uni_home_dir ", &info->uni_home_dir,
+ info->hdr_home_dir.buffer, ps, depth))
+ return False;
+ prs_align(ps);
+ if (!smb_io_unistr2("uni_dir_drive", &info->uni_dir_drive,
+ info->hdr_dir_drive.buffer, ps, depth))
+ return False;
+ prs_align(ps);
+ if (!smb_io_unistr2("uni_logon_script", &info->uni_logon_script,
+ info->hdr_logon_script.buffer, ps, depth))
+ return False;
+ prs_align(ps);
+ if (!smb_io_unistr2("uni_acct_desc", &info->uni_acct_desc,
+ info->hdr_acct_desc.buffer, ps, depth))
+ return False;
+ prs_align(ps);
+ if (!smb_io_unistr2("uni_workstations", &info->uni_workstations,
+ info->hdr_workstations.buffer, ps, depth))
+ return False;
+ prs_align(ps);
+
+ if (!prs_uint32("unknown1", ps, depth, &info->unknown1))
+ return False;
+ if (!prs_uint32("unknown2", ps, depth, &info->unknown2))
+ return False;
+
+ if (!smb_io_buffer4("buf_logon_hrs", &info->buf_logon_hrs,
+ info->ptr_logon_hrs, ps, depth))
+ return False;
+ prs_align(ps);
+ if (!smb_io_unistr2("uni_comment", &info->uni_comment,
+ info->hdr_comment.buffer, ps, depth))
+ return False;
+ prs_align(ps);
+ if (!smb_io_unistr2("uni_parameters", &info->uni_parameters,
+ info->hdr_parameters.buffer, ps, depth))
+ return False;
+ prs_align(ps);
+ if (hdr_priv_data.buffer != 0)
+ {
+ int old_offset = 0;
+ uint32 len = 0x44;
+ if (!prs_uint32("pwd_len", ps, depth, &len))
+ return False;
+ old_offset = ps->data_offset;
+ if (len > 0)
+ {
+ if (ps->io)
+ {
+ /* reading */
+ if (!prs_hash1(ps, ps->data_offset, sess_key, len))
+ return False;
+ }
+ if (!net_io_sam_passwd_info("pass", &info->pass,
+ ps, depth))
+ return False;
+
+ if (!ps->io)
+ {
+ /* writing */
+ if (!prs_hash1(ps, old_offset, sess_key, len))
+ return False;
+ }
+ }
+ if (old_offset + len > ps->buffer_size)
+ return False;
+ ps->data_offset = old_offset + len;
+ }
+ if (!smb_io_buffer4("buf_sec_desc", &info->buf_sec_desc,
+ info->hdr_sec_desc.buffer, ps, depth))
+ return False;
+ prs_align(ps);
+ if (!smb_io_unistr2("uni_profile", &info->uni_profile,
+ info->hdr_profile.buffer, ps, depth))
+ return False;
+
+ prs_align(ps);
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_group_mem_info(const char *desc, SAM_GROUP_MEM_INFO * info,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+ fstring tmp;
+
+ prs_debug(ps, depth, desc, "net_io_sam_group_mem_info");
+ depth++;
+
+ prs_align(ps);
+ if (!prs_uint32("ptr_rids ", ps, depth, &info->ptr_rids))
+ return False;
+ if (!prs_uint32("ptr_attribs", ps, depth, &info->ptr_attribs))
+ return False;
+ if (!prs_uint32("num_members", ps, depth, &info->num_members))
+ return False;
+
+ if (ps->data_offset + 16 > ps->buffer_size)
+ return False;
+ ps->data_offset += 16;
+
+ if (info->ptr_rids != 0)
+ {
+ if (!prs_uint32("num_members2", ps, depth,
+ &info->num_members2))
+ return False;
+
+ if (info->num_members2 != info->num_members)
+ {
+ /* RPC fault */
+ return False;
+ }
+
+ info->rids = talloc(ps->mem_ctx, sizeof(uint32) *
+ info->num_members2);
+
+ if (info->rids == NULL) {
+ DEBUG(0, ("out of memory allocating %d rids\n",
+ info->num_members2));
+ return False;
+ }
+
+ for (i = 0; i < info->num_members2; i++)
+ {
+ slprintf(tmp, sizeof(tmp) - 1, "rids[%02d]", i);
+ if (!prs_uint32(tmp, ps, depth, &info->rids[i]))
+ return False;
+ }
+ }
+
+ if (info->ptr_attribs != 0)
+ {
+ if (!prs_uint32("num_members3", ps, depth,
+ &info->num_members3))
+ return False;
+ if (info->num_members3 != info->num_members)
+ {
+ /* RPC fault */
+ return False;
+ }
+
+ info->attribs = talloc(ps->mem_ctx, sizeof(uint32) *
+ info->num_members3);
+
+ if (info->attribs == NULL) {
+ DEBUG(0, ("out of memory allocating %d attribs\n",
+ info->num_members3));
+ return False;
+ }
+
+ for (i = 0; i < info->num_members3; i++)
+ {
+ slprintf(tmp, sizeof(tmp) - 1, "attribs[%02d]", i);
+ if (!prs_uint32(tmp, ps, depth, &info->attribs[i]))
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_alias_info(const char *desc, SAM_ALIAS_INFO * info,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "net_io_sam_alias_info");
+ depth++;
+
+ if (!smb_io_unihdr("hdr_als_name", &info->hdr_als_name, ps, depth))
+ return False;
+ if (!prs_uint32("als_rid", ps, depth, &info->als_rid))
+ return False;
+ if (!smb_io_bufhdr2("hdr_sec_desc", &info->hdr_sec_desc, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_als_desc", &info->hdr_als_desc, ps, depth))
+ return False;
+
+ if (ps->data_offset + 40 > ps->buffer_size)
+ return False;
+ ps->data_offset += 40;
+
+ if (!smb_io_unistr2("uni_als_name", &info->uni_als_name,
+ info->hdr_als_name.buffer, ps, depth))
+ return False;
+ if (!smb_io_buffer4("buf_sec_desc", &info->buf_sec_desc,
+ info->hdr_sec_desc.buffer, ps, depth))
+ return False;
+
+ if (info->hdr_als_desc.buffer != 0) {
+ if (!smb_io_unistr2("uni_als_desc", &info->uni_als_desc,
+ info->hdr_als_name.buffer, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_alias_mem_info(const char *desc, SAM_ALIAS_MEM_INFO * info,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+ fstring tmp;
+
+ prs_debug(ps, depth, desc, "net_io_sam_alias_mem_info");
+ depth++;
+
+ prs_align(ps);
+ if (!prs_uint32("num_members", ps, depth, &info->num_members))
+ return False;
+ if (!prs_uint32("ptr_members", ps, depth, &info->ptr_members))
+ return False;
+
+ if (ps->data_offset + 16 > ps->buffer_size)
+ return False;
+ ps->data_offset += 16;
+
+ if (info->ptr_members != 0)
+ {
+ if (!prs_uint32("num_sids", ps, depth, &info->num_sids))
+ return False;
+ if (info->num_sids != info->num_members)
+ {
+ /* RPC fault */
+ return False;
+ }
+
+ info->ptr_sids = talloc(ps->mem_ctx, sizeof(uint32) *
+ info->num_sids);
+
+ if (info->ptr_sids == NULL) {
+ DEBUG(0, ("out of memory allocating %d ptr_sids\n",
+ info->num_sids));
+ return False;
+ }
+
+ for (i = 0; i < info->num_sids; i++)
+ {
+ slprintf(tmp, sizeof(tmp) - 1, "ptr_sids[%02d]", i);
+ if (!prs_uint32(tmp, ps, depth, &info->ptr_sids[i]))
+ return False;
+ }
+
+ info->sids = talloc(ps->mem_ctx, sizeof(DOM_SID2) *
+ info->num_sids);
+
+ if (info->sids == NULL) {
+ DEBUG(0, ("error allocating %d sids\n",
+ info->num_sids));
+ return False;
+ }
+
+ for (i = 0; i < info->num_sids; i++)
+ {
+ if (info->ptr_sids[i] != 0)
+ {
+ slprintf(tmp, sizeof(tmp) - 1, "sids[%02d]",
+ i);
+ if (!smb_io_dom_sid2(tmp, &info->sids[i],
+ ps, depth))
+ return False;
+ }
+ }
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_policy_info(const char *desc, SAM_DELTA_POLICY *info,
+ prs_struct *ps, int depth)
+{
+ unsigned int i;
+ prs_debug(ps, depth, desc, "net_io_sam_policy_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("max_log_size", ps, depth, &info->max_log_size))
+ return False;
+ if (!prs_uint64("audit_retention_period", ps, depth,
+ &info->audit_retention_period))
+ return False;
+ if (!prs_uint32("auditing_mode", ps, depth, &info->auditing_mode))
+ return False;
+ if (!prs_uint32("num_events", ps, depth, &info->num_events))
+ return False;
+ if (!prs_uint32("ptr_events", ps, depth, &info->ptr_events))
+ return False;
+
+ if (!smb_io_unihdr("hdr_dom_name", &info->hdr_dom_name, ps, depth))
+ return False;
+
+ if (!prs_uint32("sid_ptr", ps, depth, &info->sid_ptr))
+ return False;
+
+ if (!prs_uint32("paged_pool_limit", ps, depth, &info->paged_pool_limit))
+ return False;
+ if (!prs_uint32("non_paged_pool_limit", ps, depth,
+ &info->non_paged_pool_limit))
+ return False;
+ if (!prs_uint32("min_workset_size", ps, depth, &info->min_workset_size))
+ return False;
+ if (!prs_uint32("max_workset_size", ps, depth, &info->max_workset_size))
+ return False;
+ if (!prs_uint32("page_file_limit", ps, depth, &info->page_file_limit))
+ return False;
+ if (!prs_uint64("time_limit", ps, depth, &info->time_limit))
+ return False;
+ if (!smb_io_time("modify_time", &info->modify_time, ps, depth))
+ return False;
+ if (!smb_io_time("create_time", &info->create_time, ps, depth))
+ return False;
+ if (!smb_io_bufhdr2("hdr_sec_desc", &info->hdr_sec_desc, ps, depth))
+ return False;
+
+ for (i=0; i<4; i++) {
+ UNIHDR dummy;
+ if (!smb_io_unihdr("dummy", &dummy, ps, depth))
+ return False;
+ }
+
+ for (i=0; i<4; i++) {
+ uint32 reserved;
+ if (!prs_uint32("reserved", ps, depth, &reserved))
+ return False;
+ }
+
+ if (!prs_uint32("num_event_audit_options", ps, depth,
+ &info->num_event_audit_options))
+ return False;
+
+ for (i=0; i<info->num_event_audit_options; i++)
+ if (!prs_uint32("event_audit_option", ps, depth,
+ &info->event_audit_option))
+ return False;
+
+ if (!smb_io_unistr2("domain_name", &info->domain_name, True, ps, depth))
+ return False;
+
+ if(!smb_io_dom_sid2("domain_sid", &info->domain_sid, ps, depth))
+ return False;
+
+ if (!smb_io_buffer4("buf_sec_desc", &info->buf_sec_desc,
+ info->hdr_sec_desc.buffer, ps, depth))
+
+ return False;
+
+ return True;
+}
+
+#if 0
+
+/* This function is pretty broken - see bug #334 */
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_trustdoms_info(const char *desc, SAM_DELTA_TRUSTDOMS *info,
+ prs_struct *ps, int depth)
+{
+ int i;
+
+ prs_debug(ps, depth, desc, "net_io_sam_trustdoms_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("buf_size", ps, depth, &info->buf_size))
+ return False;
+
+ if(!sec_io_desc("sec_desc", &info->sec_desc, ps, depth))
+ return False;
+
+ if(!smb_io_dom_sid2("sid", &info->sid, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr("hdr_domain", &info->hdr_domain, ps, depth))
+ return False;
+
+ if(!prs_uint32("unknown0", ps, depth, &info->unknown0))
+ return False;
+ if(!prs_uint32("unknown1", ps, depth, &info->unknown1))
+ return False;
+ if(!prs_uint32("unknown2", ps, depth, &info->unknown2))
+ return False;
+
+ if(!prs_uint32("buf_size2", ps, depth, &info->buf_size2))
+ return False;
+ if(!prs_uint32("ptr", ps, depth, &info->ptr))
+ return False;
+
+ for (i=0; i<12; i++)
+ if(!prs_uint32("unknown3", ps, depth, &info->unknown3))
+ return False;
+
+ if (!smb_io_unistr2("domain", &info->domain, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+#endif
+
+#if 0
+
+/* This function doesn't work - see bug #334 */
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_secret_info(const char *desc, SAM_DELTA_SECRET *info,
+ prs_struct *ps, int depth)
+{
+ int i;
+
+ prs_debug(ps, depth, desc, "net_io_sam_secret_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("buf_size", ps, depth, &info->buf_size))
+ return False;
+
+ if(!sec_io_desc("sec_desc", &info->sec_desc, ps, depth))
+ return False;
+
+ if (!smb_io_unistr2("secret", &info->secret, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("count1", ps, depth, &info->count1))
+ return False;
+ if(!prs_uint32("count2", ps, depth, &info->count2))
+ return False;
+ if(!prs_uint32("ptr", ps, depth, &info->ptr))
+ return False;
+
+
+ if(!smb_io_time("time1", &info->time1, ps, depth)) /* logon time */
+ return False;
+ if(!prs_uint32("count3", ps, depth, &info->count3))
+ return False;
+ if(!prs_uint32("count4", ps, depth, &info->count4))
+ return False;
+ if(!prs_uint32("ptr2", ps, depth, &info->ptr2))
+ return False;
+ if(!smb_io_time("time2", &info->time2, ps, depth)) /* logon time */
+ return False;
+ if(!prs_uint32("unknow1", ps, depth, &info->unknow1))
+ return False;
+
+
+ if(!prs_uint32("buf_size2", ps, depth, &info->buf_size2))
+ return False;
+ if(!prs_uint32("ptr3", ps, depth, &info->ptr3))
+ return False;
+ for(i=0; i<12; i++)
+ if(!prs_uint32("unknow2", ps, depth, &info->unknow2))
+ return False;
+
+ if(!prs_uint32("chal_len", ps, depth, &info->chal_len))
+ return False;
+ if(!prs_uint32("reserved1", ps, depth, &info->reserved1))
+ return False;
+ if(!prs_uint32("chal_len2", ps, depth, &info->chal_len2))
+ return False;
+
+ if(!prs_uint8s (False, "chal", ps, depth, info->chal, info->chal_len2))
+ return False;
+
+ if(!prs_uint32("key_len", ps, depth, &info->key_len))
+ return False;
+ if(!prs_uint32("reserved2", ps, depth, &info->reserved2))
+ return False;
+ if(!prs_uint32("key_len2", ps, depth, &info->key_len2))
+ return False;
+
+ if(!prs_uint8s (False, "key", ps, depth, info->key, info->key_len2))
+ return False;
+
+
+ if(!prs_uint32("buf_size3", ps, depth, &info->buf_size3))
+ return False;
+
+ if(!sec_io_desc("sec_desc2", &info->sec_desc2, ps, depth))
+ return False;
+
+
+ return True;
+}
+
+#endif
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_privs_info(const char *desc, SAM_DELTA_PRIVS *info,
+ prs_struct *ps, int depth)
+{
+ unsigned int i;
+
+ prs_debug(ps, depth, desc, "net_io_sam_privs_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_dom_sid2("sid", &info->sid, ps, depth))
+ return False;
+
+ if(!prs_uint32("priv_count", ps, depth, &info->priv_count))
+ return False;
+ if(!prs_uint32("priv_control", ps, depth, &info->priv_control))
+ return False;
+
+ if(!prs_uint32("priv_attr_ptr", ps, depth, &info->priv_attr_ptr))
+ return False;
+ if(!prs_uint32("priv_name_ptr", ps, depth, &info->priv_name_ptr))
+ return False;
+
+ if (!prs_uint32("paged_pool_limit", ps, depth, &info->paged_pool_limit))
+ return False;
+ if (!prs_uint32("non_paged_pool_limit", ps, depth,
+ &info->non_paged_pool_limit))
+ return False;
+ if (!prs_uint32("min_workset_size", ps, depth, &info->min_workset_size))
+ return False;
+ if (!prs_uint32("max_workset_size", ps, depth, &info->max_workset_size))
+ return False;
+ if (!prs_uint32("page_file_limit", ps, depth, &info->page_file_limit))
+ return False;
+ if (!prs_uint64("time_limit", ps, depth, &info->time_limit))
+ return False;
+ if (!prs_uint32("system_flags", ps, depth, &info->system_flags))
+ return False;
+ if (!smb_io_bufhdr2("hdr_sec_desc", &info->hdr_sec_desc, ps, depth))
+ return False;
+
+ for (i=0; i<4; i++) {
+ UNIHDR dummy;
+ if (!smb_io_unihdr("dummy", &dummy, ps, depth))
+ return False;
+ }
+
+ for (i=0; i<4; i++) {
+ uint32 reserved;
+ if (!prs_uint32("reserved", ps, depth, &reserved))
+ return False;
+ }
+
+ if(!prs_uint32("attribute_count", ps, depth, &info->attribute_count))
+ return False;
+
+ info->attributes = talloc(ps->mem_ctx, sizeof(uint32) * info->attribute_count);
+
+ for (i=0; i<info->attribute_count; i++)
+ if(!prs_uint32("attributes", ps, depth, &info->attributes[i]))
+ return False;
+
+ if(!prs_uint32("privlist_count", ps, depth, &info->privlist_count))
+ return False;
+
+ info->hdr_privslist = talloc(ps->mem_ctx, sizeof(UNIHDR) * info->privlist_count);
+ info->uni_privslist = talloc(ps->mem_ctx, sizeof(UNISTR2) * info->privlist_count);
+
+ for (i=0; i<info->privlist_count; i++)
+ if(!smb_io_unihdr("hdr_privslist", &info->hdr_privslist[i], ps, depth))
+ return False;
+
+ for (i=0; i<info->privlist_count; i++)
+ if (!smb_io_unistr2("uni_privslist", &info->uni_privslist[i], True, ps, depth))
+ return False;
+
+ if (!smb_io_buffer4("buf_sec_desc", &info->buf_sec_desc,
+ info->hdr_sec_desc.buffer, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_delta_ctr(const char *desc, uint8 sess_key[16],
+ SAM_DELTA_CTR * delta, uint16 type,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "net_io_sam_delta_ctr");
+ depth++;
+
+ switch (type) {
+ /* Seen in sam deltas */
+ case SAM_DELTA_MODIFIED_COUNT:
+ if (!net_io_sam_delta_mod_count("", &delta->mod_count, ps, depth))
+ return False;
+ break;
+
+ case SAM_DELTA_DOMAIN_INFO:
+ if (!net_io_sam_domain_info("", &delta->domain_info, ps, depth))
+ return False;
+ break;
+
+ case SAM_DELTA_GROUP_INFO:
+ if (!net_io_sam_group_info("", &delta->group_info, ps, depth))
+ return False;
+ break;
+
+ case SAM_DELTA_ACCOUNT_INFO:
+ if (!net_io_sam_account_info("", sess_key, &delta->account_info, ps, depth))
+ return False;
+ break;
+
+ case SAM_DELTA_GROUP_MEM:
+ if (!net_io_sam_group_mem_info("", &delta->grp_mem_info, ps, depth))
+ return False;
+ break;
+
+ case SAM_DELTA_ALIAS_INFO:
+ if (!net_io_sam_alias_info("", &delta->alias_info, ps, depth))
+ return False;
+ break;
+
+ case SAM_DELTA_POLICY_INFO:
+ if (!net_io_sam_policy_info("", &delta->policy_info, ps, depth))
+ return False;
+ break;
+
+ case SAM_DELTA_ALIAS_MEM:
+ if (!net_io_sam_alias_mem_info("", &delta->als_mem_info, ps, depth))
+ return False;
+ break;
+
+ case SAM_DELTA_PRIVS_INFO:
+ if (!net_io_sam_privs_info("", &delta->privs_info, ps, depth))
+ return False;
+ break;
+
+ /* These guys are implemented but broken */
+
+ case SAM_DELTA_TRUST_DOMS:
+ case SAM_DELTA_SECRET_INFO:
+ break;
+
+ /* These guys are not implemented yet */
+
+ case SAM_DELTA_RENAME_GROUP:
+ case SAM_DELTA_RENAME_USER:
+ case SAM_DELTA_RENAME_ALIAS:
+ case SAM_DELTA_DELETE_GROUP:
+ case SAM_DELTA_DELETE_USER:
+ default:
+ DEBUG(0, ("Replication error: Unknown delta type 0x%x\n", type));
+ break;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL net_io_r_sam_sync(const char *desc, uint8 sess_key[16],
+ NET_R_SAM_SYNC * r_s, prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ prs_debug(ps, depth, desc, "net_io_r_sam_sync");
+ depth++;
+
+ if (!smb_io_cred("srv_creds", &r_s->srv_creds, ps, depth))
+ return False;
+ if (!prs_uint32("sync_context", ps, depth, &r_s->sync_context))
+ return False;
+
+ if (!prs_uint32("ptr_deltas", ps, depth, &r_s->ptr_deltas))
+ return False;
+ if (r_s->ptr_deltas != 0)
+ {
+ if (!prs_uint32("num_deltas ", ps, depth, &r_s->num_deltas))
+ return False;
+ if (!prs_uint32("ptr_deltas2", ps, depth, &r_s->ptr_deltas2))
+ return False;
+ if (r_s->ptr_deltas2 != 0)
+ {
+ if (!prs_uint32("num_deltas2", ps, depth,
+ &r_s->num_deltas2))
+ return False;
+
+ if (r_s->num_deltas2 != r_s->num_deltas)
+ {
+ /* RPC fault */
+ return False;
+ }
+
+ if (r_s->num_deltas2 > 0) {
+ r_s->hdr_deltas = (SAM_DELTA_HDR *)
+ talloc(ps->mem_ctx, r_s->num_deltas2 *
+ sizeof(SAM_DELTA_HDR));
+
+ if (r_s->hdr_deltas == NULL) {
+ DEBUG(0, ("error tallocating memory "
+ "for %d delta headers\n",
+ r_s->num_deltas2));
+ return False;
+ }
+ }
+
+ for (i = 0; i < r_s->num_deltas2; i++)
+ {
+ if (!net_io_sam_delta_hdr("",
+ &r_s->hdr_deltas[i],
+ ps, depth))
+ return False;
+ }
+
+ if (r_s->num_deltas2 > 0) {
+ r_s->deltas = (SAM_DELTA_CTR *)
+ talloc(ps->mem_ctx, r_s->num_deltas2 *
+ sizeof(SAM_DELTA_CTR));
+
+ if (r_s->deltas == NULL) {
+ DEBUG(0, ("error tallocating memory "
+ "for %d deltas\n",
+ r_s->num_deltas2));
+ return False;
+ }
+ }
+
+ for (i = 0; i < r_s->num_deltas2; i++)
+ {
+ if (!net_io_sam_delta_ctr(
+ "", sess_key, &r_s->deltas[i],
+ r_s->hdr_deltas[i].type3,
+ ps, depth)) {
+ DEBUG(0, ("hmm, failed on i=%d\n", i));
+ return False;
+ }
+ }
+ }
+ }
+
+ prs_align(ps);
+ if (!prs_ntstatus("status", ps, depth, &(r_s->status)))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+makes a NET_Q_SAM_DELTAS structure.
+********************************************************************/
+BOOL init_net_q_sam_deltas(NET_Q_SAM_DELTAS *q_s, const char *srv_name,
+ const char *cli_name, DOM_CRED *cli_creds,
+ uint32 database_id, UINT64_S dom_mod_count)
+{
+ DEBUG(5, ("init_net_q_sam_deltas\n"));
+
+ init_unistr2(&q_s->uni_srv_name, srv_name, UNI_STR_TERMINATE);
+ init_unistr2(&q_s->uni_cli_name, cli_name, UNI_STR_TERMINATE);
+
+ memcpy(&q_s->cli_creds, cli_creds, sizeof(q_s->cli_creds));
+ memset(&q_s->ret_creds, 0, sizeof(q_s->ret_creds));
+
+ q_s->database_id = database_id;
+ q_s->dom_mod_count.low = dom_mod_count.low;
+ q_s->dom_mod_count.high = dom_mod_count.high;
+ q_s->max_size = 0xffff;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL net_io_q_sam_deltas(const char *desc, NET_Q_SAM_DELTAS *q_s, prs_struct *ps,
+ int depth)
+{
+ prs_debug(ps, depth, desc, "net_io_q_sam_deltas");
+ depth++;
+
+ if (!smb_io_unistr2("", &q_s->uni_srv_name, True, ps, depth))
+ return False;
+ if (!smb_io_unistr2("", &q_s->uni_cli_name, True, ps, depth))
+ return False;
+
+ if (!smb_io_cred("", &q_s->cli_creds, ps, depth))
+ return False;
+ if (!smb_io_cred("", &q_s->ret_creds, ps, depth))
+ return False;
+
+ if (!prs_uint32("database_id ", ps, depth, &q_s->database_id))
+ return False;
+ if (!prs_uint64("dom_mod_count", ps, depth, &q_s->dom_mod_count))
+ return False;
+ if (!prs_uint32("max_size", ps, depth, &q_s->max_size))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL net_io_r_sam_deltas(const char *desc, uint8 sess_key[16],
+ NET_R_SAM_DELTAS *r_s, prs_struct *ps, int depth)
+{
+ unsigned int i;
+
+ prs_debug(ps, depth, desc, "net_io_r_sam_deltas");
+ depth++;
+
+ if (!smb_io_cred("srv_creds", &r_s->srv_creds, ps, depth))
+ return False;
+ if (!prs_uint64("dom_mod_count", ps, depth, &r_s->dom_mod_count))
+ return False;
+
+ if (!prs_uint32("ptr_deltas", ps, depth, &r_s->ptr_deltas))
+ return False;
+ if (!prs_uint32("num_deltas", ps, depth, &r_s->num_deltas))
+ return False;
+ if (!prs_uint32("ptr_deltas2", ps, depth, &r_s->num_deltas2))
+ return False;
+
+ if (r_s->num_deltas2 != 0)
+ {
+ if (!prs_uint32("num_deltas2 ", ps, depth, &r_s->num_deltas2))
+ return False;
+
+ if (r_s->ptr_deltas != 0)
+ {
+ if (r_s->num_deltas > 0) {
+ r_s->hdr_deltas = (SAM_DELTA_HDR *)
+ talloc(ps->mem_ctx, r_s->num_deltas *
+ sizeof(SAM_DELTA_HDR));
+ if (r_s->hdr_deltas == NULL) {
+ DEBUG(0, ("error tallocating memory "
+ "for %d delta headers\n",
+ r_s->num_deltas));
+ return False;
+ }
+ }
+
+ for (i = 0; i < r_s->num_deltas; i++)
+ {
+ net_io_sam_delta_hdr("", &r_s->hdr_deltas[i],
+ ps, depth);
+ }
+
+ if (r_s->num_deltas > 0) {
+ r_s->deltas = (SAM_DELTA_CTR *)
+ talloc(ps->mem_ctx, r_s->num_deltas *
+ sizeof(SAM_DELTA_CTR));
+
+ if (r_s->deltas == NULL) {
+ DEBUG(0, ("error tallocating memory "
+ "for %d deltas\n",
+ r_s->num_deltas));
+ return False;
+ }
+ }
+
+ for (i = 0; i < r_s->num_deltas; i++)
+ {
+ if (!net_io_sam_delta_ctr(
+ "", sess_key,
+ &r_s->deltas[i],
+ r_s->hdr_deltas[i].type2,
+ ps, depth))
+
+ return False;
+ }
+ }
+ }
+
+ prs_align(ps);
+ if (!prs_ntstatus("status", ps, depth, &r_s->status))
+ return False;
+
+ return True;
+}
diff --git a/source/rpc_parse/parse_prs.c b/source/rpc_parse/parse_prs.c
new file mode 100644
index 00000000000..0e5a25fe8c2
--- /dev/null
+++ b/source/rpc_parse/parse_prs.c
@@ -0,0 +1,1624 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba memory buffer functions
+ Copyright (C) Andrew Tridgell 1992-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+ Copyright (C) Jeremy Allison 1999
+ Copyright (C) Andrew Bartlett 2003.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/**
+ * Dump a prs to a file: from the current location through to the end.
+ **/
+void prs_dump(char *name, int v, prs_struct *ps)
+{
+ prs_dump_region(name, v, ps, ps->data_offset, ps->buffer_size);
+}
+
+
+/**
+ * Dump from the start of the prs to the current location.
+ **/
+void prs_dump_before(char *name, int v, prs_struct *ps)
+{
+ prs_dump_region(name, v, ps, 0, ps->data_offset);
+}
+
+
+/**
+ * Dump everything from the start of the prs up to the current location.
+ **/
+void prs_dump_region(char *name, int v, prs_struct *ps,
+ int from_off, int to_off)
+{
+ int fd, i;
+ pstring fname;
+ if (DEBUGLEVEL < 50) return;
+ for (i=1;i<100;i++) {
+ if (v != -1) {
+ slprintf(fname,sizeof(fname)-1, "/tmp/%s_%d.%d.prs", name, v, i);
+ } else {
+ slprintf(fname,sizeof(fname)-1, "/tmp/%s.%d.prs", name, i);
+ }
+ fd = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0644);
+ if (fd != -1 || errno != EEXIST) break;
+ }
+ if (fd != -1) {
+ write(fd, ps->data_p + from_off, to_off - from_off);
+ close(fd);
+ DEBUG(0,("created %s\n", fname));
+ }
+}
+
+
+
+/*******************************************************************
+ debug output for parsing info.
+
+ XXXX side-effect of this function is to increase the debug depth XXXX
+
+ ********************************************************************/
+void prs_debug(prs_struct *ps, int depth, const char *desc, const char *fn_name)
+{
+ DEBUG(5+depth, ("%s%06x %s %s\n", tab_depth(depth), ps->data_offset, fn_name, desc));
+}
+
+
+/**
+ * Initialise an expandable parse structure.
+ *
+ * @param size Initial buffer size. If >0, a new buffer will be
+ * created with malloc().
+ *
+ * @return False if allocation fails, otherwise True.
+ **/
+BOOL prs_init(prs_struct *ps, uint32 size, TALLOC_CTX *ctx, BOOL io)
+{
+ ZERO_STRUCTP(ps);
+ ps->io = io;
+ ps->bigendian_data = RPC_LITTLE_ENDIAN;
+ ps->align = RPC_PARSE_ALIGN;
+ ps->is_dynamic = False;
+ ps->data_offset = 0;
+ ps->buffer_size = 0;
+ ps->data_p = NULL;
+ ps->mem_ctx = ctx;
+
+ if (size != 0) {
+ ps->buffer_size = size;
+ if((ps->data_p = (char *)malloc((size_t)size)) == NULL) {
+ DEBUG(0,("prs_init: malloc fail for %u bytes.\n", (unsigned int)size));
+ return False;
+ }
+ memset(ps->data_p, '\0', (size_t)size);
+ ps->is_dynamic = True; /* We own this memory. */
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Delete the memory in a parse structure - if we own it.
+ ********************************************************************/
+
+void prs_mem_free(prs_struct *ps)
+{
+ if(ps->is_dynamic)
+ SAFE_FREE(ps->data_p);
+ ps->is_dynamic = False;
+ ps->buffer_size = 0;
+ ps->data_offset = 0;
+}
+
+/*******************************************************************
+ Clear the memory in a parse structure.
+ ********************************************************************/
+
+void prs_mem_clear(prs_struct *ps)
+{
+ if (ps->buffer_size)
+ memset(ps->data_p, '\0', (size_t)ps->buffer_size);
+}
+
+/*******************************************************************
+ Allocate memory when unmarshalling... Always zero clears.
+ ********************************************************************/
+
+char *prs_alloc_mem(prs_struct *ps, size_t size)
+{
+ char *ret = NULL;
+
+ if (size) {
+ ret = talloc(ps->mem_ctx, size);
+ if (ret)
+ memset(ret, '\0', size);
+ }
+ return ret;
+}
+
+/*******************************************************************
+ Return the current talloc context we're using.
+ ********************************************************************/
+
+TALLOC_CTX *prs_get_mem_context(prs_struct *ps)
+{
+ return ps->mem_ctx;
+}
+
+/*******************************************************************
+ Hand some already allocated memory to a prs_struct.
+ ********************************************************************/
+
+void prs_give_memory(prs_struct *ps, char *buf, uint32 size, BOOL is_dynamic)
+{
+ ps->is_dynamic = is_dynamic;
+ ps->data_p = buf;
+ ps->buffer_size = size;
+}
+
+/*******************************************************************
+ Take some memory back from a prs_struct.
+ ********************************************************************/
+
+char *prs_take_memory(prs_struct *ps, uint32 *psize)
+{
+ char *ret = ps->data_p;
+ if(psize)
+ *psize = ps->buffer_size;
+ ps->is_dynamic = False;
+ prs_mem_free(ps);
+ return ret;
+}
+
+/*******************************************************************
+ Set a prs_struct to exactly a given size. Will grow or tuncate if neccessary.
+ ********************************************************************/
+
+BOOL prs_set_buffer_size(prs_struct *ps, uint32 newsize)
+{
+ if (newsize > ps->buffer_size)
+ return prs_force_grow(ps, newsize - ps->buffer_size);
+
+ if (newsize < ps->buffer_size) {
+ char *new_data_p = Realloc(ps->data_p, newsize);
+ /* if newsize is zero, Realloc acts like free() & returns NULL*/
+ if (new_data_p == NULL && newsize != 0) {
+ DEBUG(0,("prs_set_buffer_size: Realloc failure for size %u.\n",
+ (unsigned int)newsize));
+ DEBUG(0,("prs_set_buffer_size: Reason %s\n",strerror(errno)));
+ return False;
+ }
+ ps->data_p = new_data_p;
+ ps->buffer_size = newsize;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Attempt, if needed, to grow a data buffer.
+ Also depends on the data stream mode (io).
+ ********************************************************************/
+
+BOOL prs_grow(prs_struct *ps, uint32 extra_space)
+{
+ uint32 new_size;
+ char *new_data;
+
+ ps->grow_size = MAX(ps->grow_size, ps->data_offset + extra_space);
+
+ if(ps->data_offset + extra_space <= ps->buffer_size)
+ return True;
+
+ /*
+ * We cannot grow the buffer if we're not reading
+ * into the prs_struct, or if we don't own the memory.
+ */
+
+ if(UNMARSHALLING(ps) || !ps->is_dynamic) {
+ DEBUG(0,("prs_grow: Buffer overflow - unable to expand buffer by %u bytes.\n",
+ (unsigned int)extra_space));
+ return False;
+ }
+
+ /*
+ * Decide how much extra space we really need.
+ */
+
+ extra_space -= (ps->buffer_size - ps->data_offset);
+ if(ps->buffer_size == 0) {
+ /*
+ * Ensure we have at least a PDU's length, or extra_space, whichever
+ * is greater.
+ */
+
+ new_size = MAX(MAX_PDU_FRAG_LEN,extra_space);
+
+ if((new_data = malloc(new_size)) == NULL) {
+ DEBUG(0,("prs_grow: Malloc failure for size %u.\n", (unsigned int)new_size));
+ return False;
+ }
+ memset(new_data, '\0', (size_t)new_size );
+ } else {
+ /*
+ * If the current buffer size is bigger than the space needed, just
+ * double it, else add extra_space.
+ */
+ new_size = MAX(ps->buffer_size*2, ps->buffer_size + extra_space);
+
+ if ((new_data = Realloc(ps->data_p, new_size)) == NULL) {
+ DEBUG(0,("prs_grow: Realloc failure for size %u.\n",
+ (unsigned int)new_size));
+ return False;
+ }
+
+ memset(&new_data[ps->buffer_size], '\0', (size_t)(new_size - ps->buffer_size));
+ }
+ ps->buffer_size = new_size;
+ ps->data_p = new_data;
+
+ return True;
+}
+
+/*******************************************************************
+ Attempt to force a data buffer to grow by len bytes.
+ This is only used when appending more data onto a prs_struct
+ when reading an rpc reply, before unmarshalling it.
+ ********************************************************************/
+
+BOOL prs_force_grow(prs_struct *ps, uint32 extra_space)
+{
+ uint32 new_size = ps->buffer_size + extra_space;
+ char *new_data;
+
+ if(!UNMARSHALLING(ps) || !ps->is_dynamic) {
+ DEBUG(0,("prs_force_grow: Buffer overflow - unable to expand buffer by %u bytes.\n",
+ (unsigned int)extra_space));
+ return False;
+ }
+
+ if((new_data = Realloc(ps->data_p, new_size)) == NULL) {
+ DEBUG(0,("prs_force_grow: Realloc failure for size %u.\n",
+ (unsigned int)new_size));
+ return False;
+ }
+
+ memset(&new_data[ps->buffer_size], '\0', (size_t)(new_size - ps->buffer_size));
+
+ ps->buffer_size = new_size;
+ ps->data_p = new_data;
+
+ return True;
+}
+
+/*******************************************************************
+ Get the data pointer (external interface).
+********************************************************************/
+
+char *prs_data_p(prs_struct *ps)
+{
+ return ps->data_p;
+}
+
+/*******************************************************************
+ Get the current data size (external interface).
+ ********************************************************************/
+
+uint32 prs_data_size(prs_struct *ps)
+{
+ return ps->buffer_size;
+}
+
+/*******************************************************************
+ Fetch the current offset (external interface).
+ ********************************************************************/
+
+uint32 prs_offset(prs_struct *ps)
+{
+ return ps->data_offset;
+}
+
+/*******************************************************************
+ Set the current offset (external interface).
+ ********************************************************************/
+
+BOOL prs_set_offset(prs_struct *ps, uint32 offset)
+{
+ if(offset <= ps->data_offset) {
+ ps->data_offset = offset;
+ return True;
+ }
+
+ if(!prs_grow(ps, offset - ps->data_offset))
+ return False;
+
+ ps->data_offset = offset;
+ return True;
+}
+
+/*******************************************************************
+ Append the data from one parse_struct into another.
+ ********************************************************************/
+
+BOOL prs_append_prs_data(prs_struct *dst, prs_struct *src)
+{
+ if (prs_offset(src) == 0)
+ return True;
+
+ if(!prs_grow(dst, prs_offset(src)))
+ return False;
+
+ memcpy(&dst->data_p[dst->data_offset], src->data_p, (size_t)prs_offset(src));
+ dst->data_offset += prs_offset(src);
+
+ return True;
+}
+
+/*******************************************************************
+ Append some data from one parse_struct into another.
+ ********************************************************************/
+
+BOOL prs_append_some_prs_data(prs_struct *dst, prs_struct *src, int32 start, uint32 len)
+{
+ if (len == 0)
+ return True;
+
+ if(!prs_grow(dst, len))
+ return False;
+
+ memcpy(&dst->data_p[dst->data_offset], src->data_p + start, (size_t)len);
+ dst->data_offset += len;
+
+ return True;
+}
+
+/*******************************************************************
+ Append the data from a buffer into a parse_struct.
+ ********************************************************************/
+
+BOOL prs_copy_data_in(prs_struct *dst, char *src, uint32 len)
+{
+ if (len == 0)
+ return True;
+
+ if(!prs_grow(dst, len))
+ return False;
+
+ memcpy(&dst->data_p[dst->data_offset], src, (size_t)len);
+ dst->data_offset += len;
+
+ return True;
+}
+
+/*******************************************************************
+ Copy some data from a parse_struct into a buffer.
+ ********************************************************************/
+
+BOOL prs_copy_data_out(char *dst, prs_struct *src, uint32 len)
+{
+ if (len == 0)
+ return True;
+
+ if(!prs_mem_get(src, len))
+ return False;
+
+ memcpy(dst, &src->data_p[src->data_offset], (size_t)len);
+ src->data_offset += len;
+
+ return True;
+}
+
+/*******************************************************************
+ Copy all the data from a parse_struct into a buffer.
+ ********************************************************************/
+
+BOOL prs_copy_all_data_out(char *dst, prs_struct *src)
+{
+ uint32 len = prs_offset(src);
+
+ if (!len)
+ return True;
+
+ prs_set_offset(src, 0);
+ return prs_copy_data_out(dst, src, len);
+}
+
+/*******************************************************************
+ Set the data as X-endian (external interface).
+ ********************************************************************/
+
+void prs_set_endian_data(prs_struct *ps, BOOL endian)
+{
+ ps->bigendian_data = endian;
+}
+
+/*******************************************************************
+ Align a the data_len to a multiple of align bytes - filling with
+ zeros.
+ ********************************************************************/
+
+BOOL prs_align(prs_struct *ps)
+{
+ uint32 mod = ps->data_offset & (ps->align-1);
+
+ if (ps->align != 0 && mod != 0) {
+ uint32 extra_space = (ps->align - mod);
+ if(!prs_grow(ps, extra_space))
+ return False;
+ memset(&ps->data_p[ps->data_offset], '\0', (size_t)extra_space);
+ ps->data_offset += extra_space;
+ }
+
+ return True;
+}
+
+/******************************************************************
+ Align on a 2 byte boundary
+ *****************************************************************/
+
+BOOL prs_align_uint16(prs_struct *ps)
+{
+ BOOL ret;
+ uint8 old_align = ps->align;
+
+ ps->align = 2;
+ ret = prs_align(ps);
+ ps->align = old_align;
+
+ return ret;
+}
+
+/******************************************************************
+ Align on a 8 byte boundary
+ *****************************************************************/
+
+BOOL prs_align_uint64(prs_struct *ps)
+{
+ BOOL ret;
+ uint8 old_align = ps->align;
+
+ ps->align = 8;
+ ret = prs_align(ps);
+ ps->align = old_align;
+
+ return ret;
+}
+
+/*******************************************************************
+ Align only if required (for the unistr2 string mainly)
+ ********************************************************************/
+
+BOOL prs_align_needed(prs_struct *ps, uint32 needed)
+{
+ if (needed==0)
+ return True;
+ else
+ return prs_align(ps);
+}
+
+/*******************************************************************
+ Ensure we can read/write to a given offset.
+ ********************************************************************/
+
+char *prs_mem_get(prs_struct *ps, uint32 extra_size)
+{
+ if(UNMARSHALLING(ps)) {
+ /*
+ * If reading, ensure that we can read the requested size item.
+ */
+ if (ps->data_offset + extra_size > ps->buffer_size) {
+ DEBUG(0,("prs_mem_get: reading data of size %u would overrun buffer.\n",
+ (unsigned int)extra_size ));
+ return NULL;
+ }
+ } else {
+ /*
+ * Writing - grow the buffer if needed.
+ */
+ if(!prs_grow(ps, extra_size))
+ return NULL;
+ }
+ return &ps->data_p[ps->data_offset];
+}
+
+/*******************************************************************
+ Change the struct type.
+ ********************************************************************/
+
+void prs_switch_type(prs_struct *ps, BOOL io)
+{
+ if ((ps->io ^ io) == True)
+ ps->io=io;
+}
+
+/*******************************************************************
+ Force a prs_struct to be dynamic even when it's size is 0.
+ ********************************************************************/
+
+void prs_force_dynamic(prs_struct *ps)
+{
+ ps->is_dynamic=True;
+}
+
+/*******************************************************************
+ Stream a uint8.
+ ********************************************************************/
+
+BOOL prs_uint8(const char *name, prs_struct *ps, int depth, uint8 *data8)
+{
+ char *q = prs_mem_get(ps, 1);
+ if (q == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps))
+ *data8 = CVAL(q,0);
+ else
+ SCVAL(q,0,*data8);
+
+ DEBUG(5,("%s%04x %s: %02x\n", tab_depth(depth), ps->data_offset, name, *data8));
+
+ ps->data_offset += 1;
+
+ return True;
+}
+
+/*******************************************************************
+ Stream a uint16.
+ ********************************************************************/
+
+BOOL prs_uint16(const char *name, prs_struct *ps, int depth, uint16 *data16)
+{
+ char *q = prs_mem_get(ps, sizeof(uint16));
+ if (q == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ if (ps->bigendian_data)
+ *data16 = RSVAL(q,0);
+ else
+ *data16 = SVAL(q,0);
+ } else {
+ if (ps->bigendian_data)
+ RSSVAL(q,0,*data16);
+ else
+ SSVAL(q,0,*data16);
+ }
+
+ DEBUG(5,("%s%04x %s: %04x\n", tab_depth(depth), ps->data_offset, name, *data16));
+
+ ps->data_offset += sizeof(uint16);
+
+ return True;
+}
+
+/*******************************************************************
+ Stream a uint32.
+ ********************************************************************/
+
+BOOL prs_uint32(const char *name, prs_struct *ps, int depth, uint32 *data32)
+{
+ char *q = prs_mem_get(ps, sizeof(uint32));
+ if (q == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ if (ps->bigendian_data)
+ *data32 = RIVAL(q,0);
+ else
+ *data32 = IVAL(q,0);
+ } else {
+ if (ps->bigendian_data)
+ RSIVAL(q,0,*data32);
+ else
+ SIVAL(q,0,*data32);
+ }
+
+ DEBUG(5,("%s%04x %s: %08x\n", tab_depth(depth), ps->data_offset, name, *data32));
+
+ ps->data_offset += sizeof(uint32);
+
+ return True;
+}
+
+/*******************************************************************
+ Stream a NTSTATUS
+ ********************************************************************/
+
+BOOL prs_ntstatus(const char *name, prs_struct *ps, int depth, NTSTATUS *status)
+{
+ char *q = prs_mem_get(ps, sizeof(uint32));
+ if (q == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ if (ps->bigendian_data)
+ *status = NT_STATUS(RIVAL(q,0));
+ else
+ *status = NT_STATUS(IVAL(q,0));
+ } else {
+ if (ps->bigendian_data)
+ RSIVAL(q,0,NT_STATUS_V(*status));
+ else
+ SIVAL(q,0,NT_STATUS_V(*status));
+ }
+
+ DEBUG(5,("%s%04x %s: %s\n", tab_depth(depth), ps->data_offset, name,
+ nt_errstr(*status)));
+
+ ps->data_offset += sizeof(uint32);
+
+ return True;
+}
+
+/*******************************************************************
+ Stream a WERROR
+ ********************************************************************/
+
+BOOL prs_werror(const char *name, prs_struct *ps, int depth, WERROR *status)
+{
+ char *q = prs_mem_get(ps, sizeof(uint32));
+ if (q == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ if (ps->bigendian_data)
+ *status = W_ERROR(RIVAL(q,0));
+ else
+ *status = W_ERROR(IVAL(q,0));
+ } else {
+ if (ps->bigendian_data)
+ RSIVAL(q,0,W_ERROR_V(*status));
+ else
+ SIVAL(q,0,W_ERROR_V(*status));
+ }
+
+ DEBUG(5,("%s%04x %s: %s\n", tab_depth(depth), ps->data_offset, name,
+ dos_errstr(*status)));
+
+ ps->data_offset += sizeof(uint32);
+
+ return True;
+}
+
+
+/******************************************************************
+ Stream an array of uint8s. Length is number of uint8s.
+ ********************************************************************/
+
+BOOL prs_uint8s(BOOL charmode, const char *name, prs_struct *ps, int depth, uint8 *data8s, int len)
+{
+ int i;
+ char *q = prs_mem_get(ps, len);
+ if (q == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ for (i = 0; i < len; i++)
+ data8s[i] = CVAL(q,i);
+ } else {
+ for (i = 0; i < len; i++)
+ SCVAL(q, i, data8s[i]);
+ }
+
+ DEBUG(5,("%s%04x %s: ", tab_depth(depth), ps->data_offset ,name));
+ if (charmode)
+ print_asc(5, (unsigned char*)data8s, len);
+ else {
+ for (i = 0; i < len; i++)
+ DEBUG(5,("%02x ", data8s[i]));
+ }
+ DEBUG(5,("\n"));
+
+ ps->data_offset += len;
+
+ return True;
+}
+
+/******************************************************************
+ Stream an array of uint16s. Length is number of uint16s.
+ ********************************************************************/
+
+BOOL prs_uint16s(BOOL charmode, const char *name, prs_struct *ps, int depth, uint16 *data16s, int len)
+{
+ int i;
+ char *q = prs_mem_get(ps, len * sizeof(uint16));
+ if (q == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ if (ps->bigendian_data) {
+ for (i = 0; i < len; i++)
+ data16s[i] = RSVAL(q, 2*i);
+ } else {
+ for (i = 0; i < len; i++)
+ data16s[i] = SVAL(q, 2*i);
+ }
+ } else {
+ if (ps->bigendian_data) {
+ for (i = 0; i < len; i++)
+ RSSVAL(q, 2*i, data16s[i]);
+ } else {
+ for (i = 0; i < len; i++)
+ SSVAL(q, 2*i, data16s[i]);
+ }
+ }
+
+ DEBUG(5,("%s%04x %s: ", tab_depth(depth), ps->data_offset, name));
+ if (charmode)
+ print_asc(5, (unsigned char*)data16s, 2*len);
+ else {
+ for (i = 0; i < len; i++)
+ DEBUG(5,("%04x ", data16s[i]));
+ }
+ DEBUG(5,("\n"));
+
+ ps->data_offset += (len * sizeof(uint16));
+
+ return True;
+}
+
+/******************************************************************
+ Start using a function for streaming unicode chars. If unmarshalling,
+ output must be little-endian, if marshalling, input must be little-endian.
+ ********************************************************************/
+
+static void dbg_rw_punival(BOOL charmode, const char *name, int depth, prs_struct *ps,
+ char *in_buf, char *out_buf, int len)
+{
+ int i;
+
+ if (UNMARSHALLING(ps)) {
+ if (ps->bigendian_data) {
+ for (i = 0; i < len; i++)
+ SSVAL(out_buf,2*i,RSVAL(in_buf, 2*i));
+ } else {
+ for (i = 0; i < len; i++)
+ SSVAL(out_buf, 2*i, SVAL(in_buf, 2*i));
+ }
+ } else {
+ if (ps->bigendian_data) {
+ for (i = 0; i < len; i++)
+ RSSVAL(in_buf, 2*i, SVAL(out_buf,2*i));
+ } else {
+ for (i = 0; i < len; i++)
+ SSVAL(in_buf, 2*i, SVAL(out_buf,2*i));
+ }
+ }
+
+ DEBUG(5,("%s%04x %s: ", tab_depth(depth), ps->data_offset, name));
+ if (charmode)
+ print_asc(5, (unsigned char*)out_buf, 2*len);
+ else {
+ for (i = 0; i < len; i++)
+ DEBUG(5,("%04x ", out_buf[i]));
+ }
+ DEBUG(5,("\n"));
+}
+
+/******************************************************************
+ Stream a unistr. Always little endian.
+ ********************************************************************/
+
+BOOL prs_uint16uni(BOOL charmode, const char *name, prs_struct *ps, int depth, uint16 *data16s, int len)
+{
+ char *q = prs_mem_get(ps, len * sizeof(uint16));
+ if (q == NULL)
+ return False;
+
+ dbg_rw_punival(charmode, name, depth, ps, q, (char *)data16s, len);
+ ps->data_offset += (len * sizeof(uint16));
+
+ return True;
+}
+
+/******************************************************************
+ Stream an array of uint32s. Length is number of uint32s.
+ ********************************************************************/
+
+BOOL prs_uint32s(BOOL charmode, const char *name, prs_struct *ps, int depth, uint32 *data32s, int len)
+{
+ int i;
+ char *q = prs_mem_get(ps, len * sizeof(uint32));
+ if (q == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ if (ps->bigendian_data) {
+ for (i = 0; i < len; i++)
+ data32s[i] = RIVAL(q, 4*i);
+ } else {
+ for (i = 0; i < len; i++)
+ data32s[i] = IVAL(q, 4*i);
+ }
+ } else {
+ if (ps->bigendian_data) {
+ for (i = 0; i < len; i++)
+ RSIVAL(q, 4*i, data32s[i]);
+ } else {
+ for (i = 0; i < len; i++)
+ SIVAL(q, 4*i, data32s[i]);
+ }
+ }
+
+ DEBUG(5,("%s%04x %s: ", tab_depth(depth), ps->data_offset, name));
+ if (charmode)
+ print_asc(5, (unsigned char*)data32s, 4*len);
+ else {
+ for (i = 0; i < len; i++)
+ DEBUG(5,("%08x ", data32s[i]));
+ }
+ DEBUG(5,("\n"));
+
+ ps->data_offset += (len * sizeof(uint32));
+
+ return True;
+}
+
+/******************************************************************
+ Stream an array of unicode string, length/buffer specified separately,
+ in uint16 chars. The unicode string is already in little-endian format.
+ ********************************************************************/
+
+BOOL prs_buffer5(BOOL charmode, const char *name, prs_struct *ps, int depth, BUFFER5 *str)
+{
+ char *p;
+ char *q = prs_mem_get(ps, str->buf_len * sizeof(uint16));
+ if (q == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ str->buffer = (uint16 *)prs_alloc_mem(ps,str->buf_len * sizeof(uint16));
+ if (str->buffer == NULL)
+ return False;
+ }
+
+ /* If the string is empty, we don't have anything to stream */
+ if (str->buf_len==0)
+ return True;
+
+ p = (char *)str->buffer;
+
+ dbg_rw_punival(charmode, name, depth, ps, q, p, str->buf_len);
+
+ ps->data_offset += (str->buf_len * sizeof(uint16));
+
+ return True;
+}
+
+/******************************************************************
+ Stream a "not" unicode string, length/buffer specified separately,
+ in byte chars. String is in little-endian format.
+ ********************************************************************/
+
+BOOL prs_buffer2(BOOL charmode, const char *name, prs_struct *ps, int depth, BUFFER2 *str)
+{
+ char *p;
+ char *q = prs_mem_get(ps, str->buf_len);
+ if (q == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ if ( str->buf_len ) {
+ str->buffer = (uint16 *)prs_alloc_mem(ps,str->buf_len);
+ if ( str->buffer == NULL )
+ return False;
+ }
+ }
+
+ p = (char *)str->buffer;
+
+ dbg_rw_punival(charmode, name, depth, ps, q, p, str->buf_len/2);
+ ps->data_offset += str->buf_len;
+
+ return True;
+}
+
+/******************************************************************
+ Stream a string, length/buffer specified separately,
+ in uint8 chars.
+ ********************************************************************/
+
+BOOL prs_string2(BOOL charmode, const char *name, prs_struct *ps, int depth, STRING2 *str)
+{
+ unsigned int i;
+ char *q = prs_mem_get(ps, str->str_max_len);
+ if (q == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ str->buffer = (unsigned char *)prs_alloc_mem(ps,str->str_max_len);
+ if (str->buffer == NULL)
+ return False;
+ }
+
+ if (UNMARSHALLING(ps)) {
+ for (i = 0; i < str->str_str_len; i++)
+ str->buffer[i] = CVAL(q,i);
+ } else {
+ for (i = 0; i < str->str_str_len; i++)
+ SCVAL(q, i, str->buffer[i]);
+ }
+
+ DEBUG(5,("%s%04x %s: ", tab_depth(depth), ps->data_offset, name));
+ if (charmode)
+ print_asc(5, (unsigned char*)str->buffer, str->str_str_len);
+ else {
+ for (i = 0; i < str->str_str_len; i++)
+ DEBUG(5,("%02x ", str->buffer[i]));
+ }
+ DEBUG(5,("\n"));
+
+ ps->data_offset += str->str_str_len;
+
+ return True;
+}
+
+/******************************************************************
+ Stream a unicode string, length/buffer specified separately,
+ in uint16 chars. The unicode string is already in little-endian format.
+ ********************************************************************/
+
+BOOL prs_unistr2(BOOL charmode, const char *name, prs_struct *ps, int depth, UNISTR2 *str)
+{
+ char *p;
+ char *q = prs_mem_get(ps, str->uni_str_len * sizeof(uint16));
+ if (q == NULL)
+ return False;
+
+ /* If the string is empty, we don't have anything to stream */
+ if (str->uni_str_len==0)
+ return True;
+
+ if (UNMARSHALLING(ps)) {
+ str->buffer = (uint16 *)prs_alloc_mem(ps,str->uni_max_len * sizeof(uint16));
+ if (str->buffer == NULL)
+ return False;
+ }
+
+ p = (char *)str->buffer;
+
+ dbg_rw_punival(charmode, name, depth, ps, q, p, str->uni_str_len);
+
+ ps->data_offset += (str->uni_str_len * sizeof(uint16));
+
+ return True;
+}
+
+/******************************************************************
+ Stream a unicode string, length/buffer specified separately,
+ in uint16 chars. The unicode string is already in little-endian format.
+ ********************************************************************/
+
+BOOL prs_unistr3(BOOL charmode, const char *name, UNISTR3 *str, prs_struct *ps, int depth)
+{
+ char *p;
+ char *q = prs_mem_get(ps, str->uni_str_len * sizeof(uint16));
+ if (q == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ str->str.buffer = (uint16 *)prs_alloc_mem(ps,str->uni_str_len * sizeof(uint16));
+ if (str->str.buffer == NULL)
+ return False;
+ }
+
+ p = (char *)str->str.buffer;
+
+ dbg_rw_punival(charmode, name, depth, ps, q, p, str->uni_str_len);
+ ps->data_offset += (str->uni_str_len * sizeof(uint16));
+
+ return True;
+}
+
+/*******************************************************************
+ Stream a unicode null-terminated string. As the string is already
+ in little-endian format then do it as a stream of bytes.
+ ********************************************************************/
+
+BOOL prs_unistr(const char *name, prs_struct *ps, int depth, UNISTR *str)
+{
+ unsigned int len = 0;
+ unsigned char *p = (unsigned char *)str->buffer;
+ uint8 *start;
+ char *q;
+ uint32 max_len;
+ uint16* ptr;
+
+ if (MARSHALLING(ps)) {
+
+ for(len = 0; str->buffer[len] != 0; len++)
+ ;
+
+ q = prs_mem_get(ps, (len+1)*2);
+ if (q == NULL)
+ return False;
+
+ start = (uint8*)q;
+
+ for(len = 0; str->buffer[len] != 0; len++)
+ {
+ if(ps->bigendian_data)
+ {
+ /* swap bytes - p is little endian, q is big endian. */
+ q[0] = (char)p[1];
+ q[1] = (char)p[0];
+ p += 2;
+ q += 2;
+ }
+ else
+ {
+ q[0] = (char)p[0];
+ q[1] = (char)p[1];
+ p += 2;
+ q += 2;
+ }
+ }
+
+ /*
+ * even if the string is 'empty' (only an \0 char)
+ * at this point the leading \0 hasn't been parsed.
+ * so parse it now
+ */
+
+ q[0] = 0;
+ q[1] = 0;
+ q += 2;
+
+ len++;
+
+ DEBUG(5,("%s%04x %s: ", tab_depth(depth), ps->data_offset, name));
+ print_asc(5, (unsigned char*)start, 2*len);
+ DEBUG(5, ("\n"));
+ }
+ else { /* unmarshalling */
+
+ uint32 alloc_len = 0;
+ q = ps->data_p + prs_offset(ps);
+
+ /*
+ * Work out how much space we need and talloc it.
+ */
+ max_len = (ps->buffer_size - ps->data_offset)/sizeof(uint16);
+
+ /* the test of the value of *ptr helps to catch the circumstance
+ where we have an emtpty (non-existent) string in the buffer */
+ for ( ptr = (uint16 *)q; *ptr && (alloc_len <= max_len); alloc_len++)
+ /* do nothing */
+ ;
+
+ /* should we allocate anything at all? */
+ str->buffer = (uint16 *)prs_alloc_mem(ps,alloc_len * sizeof(uint16));
+ if ((str->buffer == NULL) && (alloc_len > 0))
+ return False;
+
+ p = (unsigned char *)str->buffer;
+
+ len = 0;
+ /* the (len < alloc_len) test is to prevent us from overwriting
+ memory that is not ours...if we get that far, we have a non-null
+ terminated string in the buffer and have messed up somewhere */
+ while ((len < alloc_len) && (*(uint16 *)q != 0))
+ {
+ if(ps->bigendian_data)
+ {
+ /* swap bytes - q is big endian, p is little endian. */
+ p[0] = (unsigned char)q[1];
+ p[1] = (unsigned char)q[0];
+ p += 2;
+ q += 2;
+ } else {
+
+ p[0] = (unsigned char)q[0];
+ p[1] = (unsigned char)q[1];
+ p += 2;
+ q += 2;
+ }
+
+ len++;
+ }
+ if (len < alloc_len)
+ {
+ /* NULL terminate the UNISTR */
+ str->buffer[len++] = '\0';
+ }
+
+ DEBUG(5,("%s%04x %s: ", tab_depth(depth), ps->data_offset, name));
+ print_asc(5, (unsigned char*)str->buffer, 2*len);
+ DEBUG(5, ("\n"));
+ }
+
+ /* set the offset in the prs_struct; 'len' points to the
+ terminiating NULL in the UNISTR so we need to go one more
+ uint16 */
+ ps->data_offset += (len)*2;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Stream a null-terminated string. len is strlen, and therefore does
+ not include the null-termination character.
+ ********************************************************************/
+
+BOOL prs_string(const char *name, prs_struct *ps, int depth, char *str, int max_buf_size)
+{
+ char *q;
+ int i;
+ int len;
+
+ if (UNMARSHALLING(ps))
+ len = strlen(&ps->data_p[ps->data_offset]);
+ else
+ len = strlen(str);
+
+ len = MIN(len, (max_buf_size-1));
+
+ q = prs_mem_get(ps, len+1);
+ if (q == NULL)
+ return False;
+
+ for(i = 0; i < len; i++) {
+ if (UNMARSHALLING(ps))
+ str[i] = q[i];
+ else
+ q[i] = str[i];
+ }
+
+ /* The terminating null. */
+ str[i] = '\0';
+
+ if (MARSHALLING(ps)) {
+ q[i] = '\0';
+ }
+
+ ps->data_offset += len+1;
+
+ dump_data(5+depth, q, len);
+
+ return True;
+}
+
+/*******************************************************************
+ prs_uint16 wrapper. Call this and it sets up a pointer to where the
+ uint16 should be stored, or gets the size if reading.
+ ********************************************************************/
+
+BOOL prs_uint16_pre(const char *name, prs_struct *ps, int depth, uint16 *data16, uint32 *offset)
+{
+ *offset = ps->data_offset;
+ if (UNMARSHALLING(ps)) {
+ /* reading. */
+ return prs_uint16(name, ps, depth, data16);
+ } else {
+ char *q = prs_mem_get(ps, sizeof(uint16));
+ if(q ==NULL)
+ return False;
+ ps->data_offset += sizeof(uint16);
+ }
+ return True;
+}
+
+/*******************************************************************
+ prs_uint16 wrapper. call this and it retrospectively stores the size.
+ does nothing on reading, as that is already handled by ...._pre()
+ ********************************************************************/
+
+BOOL prs_uint16_post(const char *name, prs_struct *ps, int depth, uint16 *data16,
+ uint32 ptr_uint16, uint32 start_offset)
+{
+ if (MARSHALLING(ps)) {
+ /*
+ * Writing - temporarily move the offset pointer.
+ */
+ uint16 data_size = ps->data_offset - start_offset;
+ uint32 old_offset = ps->data_offset;
+
+ ps->data_offset = ptr_uint16;
+ if(!prs_uint16(name, ps, depth, &data_size)) {
+ ps->data_offset = old_offset;
+ return False;
+ }
+ ps->data_offset = old_offset;
+ } else {
+ ps->data_offset = start_offset + (uint32)(*data16);
+ }
+ return True;
+}
+
+/*******************************************************************
+ prs_uint32 wrapper. Call this and it sets up a pointer to where the
+ uint32 should be stored, or gets the size if reading.
+ ********************************************************************/
+
+BOOL prs_uint32_pre(const char *name, prs_struct *ps, int depth, uint32 *data32, uint32 *offset)
+{
+ *offset = ps->data_offset;
+ if (UNMARSHALLING(ps) && (data32 != NULL)) {
+ /* reading. */
+ return prs_uint32(name, ps, depth, data32);
+ } else {
+ ps->data_offset += sizeof(uint32);
+ }
+ return True;
+}
+
+/*******************************************************************
+ prs_uint32 wrapper. call this and it retrospectively stores the size.
+ does nothing on reading, as that is already handled by ...._pre()
+ ********************************************************************/
+
+BOOL prs_uint32_post(const char *name, prs_struct *ps, int depth, uint32 *data32,
+ uint32 ptr_uint32, uint32 data_size)
+{
+ if (MARSHALLING(ps)) {
+ /*
+ * Writing - temporarily move the offset pointer.
+ */
+ uint32 old_offset = ps->data_offset;
+ ps->data_offset = ptr_uint32;
+ if(!prs_uint32(name, ps, depth, &data_size)) {
+ ps->data_offset = old_offset;
+ return False;
+ }
+ ps->data_offset = old_offset;
+ }
+ return True;
+}
+
+/* useful function to store a structure in rpc wire format */
+int tdb_prs_store(TDB_CONTEXT *tdb, char *keystr, prs_struct *ps)
+{
+ TDB_DATA kbuf, dbuf;
+ kbuf.dptr = keystr;
+ kbuf.dsize = strlen(keystr)+1;
+ dbuf.dptr = ps->data_p;
+ dbuf.dsize = prs_offset(ps);
+ return tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
+}
+
+/* useful function to fetch a structure into rpc wire format */
+int tdb_prs_fetch(TDB_CONTEXT *tdb, char *keystr, prs_struct *ps, TALLOC_CTX *mem_ctx)
+{
+ TDB_DATA kbuf, dbuf;
+ kbuf.dptr = keystr;
+ kbuf.dsize = strlen(keystr)+1;
+
+ dbuf = tdb_fetch(tdb, kbuf);
+ if (!dbuf.dptr)
+ return -1;
+
+ prs_init(ps, 0, mem_ctx, UNMARSHALL);
+ prs_give_memory(ps, dbuf.dptr, dbuf.dsize, True);
+
+ return 0;
+}
+
+/*******************************************************************
+ hash a stream.
+ ********************************************************************/
+BOOL prs_hash1(prs_struct *ps, uint32 offset, uint8 sess_key[16], int len)
+{
+ char *q;
+
+ q = ps->data_p;
+ q = &q[offset];
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("prs_hash1\n"));
+ dump_data(100, sess_key, 16);
+ dump_data(100, q, len);
+#endif
+ SamOEMhash((uchar *) q, sess_key, len);
+
+#ifdef DEBUG_PASSWORD
+ dump_data(100, q, len);
+#endif
+
+ return True;
+}
+
+
+/*******************************************************************
+ Create a digest over the entire packet (including the data), and
+ MD5 it with the session key.
+ ********************************************************************/
+static void netsec_digest(struct netsec_auth_struct *a,
+ int auth_flags,
+ RPC_AUTH_NETSEC_CHK * verf,
+ char *data, size_t data_len,
+ uchar digest_final[16])
+{
+ uchar whole_packet_digest[16];
+ static uchar zeros[4];
+ struct MD5Context ctx3;
+
+ /* verfiy the signature on the packet by MD5 over various bits */
+ MD5Init(&ctx3);
+ /* use our sequence number, which ensures the packet is not
+ out of order */
+ MD5Update(&ctx3, zeros, sizeof(zeros));
+ MD5Update(&ctx3, verf->sig, sizeof(verf->sig));
+ if (auth_flags & AUTH_PIPE_SEAL) {
+ MD5Update(&ctx3, verf->confounder, sizeof(verf->confounder));
+ }
+ MD5Update(&ctx3, (const unsigned char *)data, data_len);
+ MD5Final(whole_packet_digest, &ctx3);
+ dump_data_pw("whole_packet_digest:\n", whole_packet_digest, sizeof(whole_packet_digest));
+
+ /* MD5 this result and the session key, to prove that
+ only a valid client could had produced this */
+ hmac_md5(a->sess_key, whole_packet_digest, sizeof(whole_packet_digest), digest_final);
+}
+
+/*******************************************************************
+ Calculate the key with which to encode the data payload
+ ********************************************************************/
+static void netsec_get_sealing_key(struct netsec_auth_struct *a,
+ RPC_AUTH_NETSEC_CHK *verf,
+ uchar sealing_key[16])
+{
+ static uchar zeros[4];
+ uchar digest2[16];
+ uchar sess_kf0[16];
+ int i;
+
+ for (i = 0; i < sizeof(sess_kf0); i++) {
+ sess_kf0[i] = a->sess_key[i] ^ 0xf0;
+ }
+
+ dump_data_pw("sess_kf0:\n", sess_kf0, sizeof(sess_kf0));
+
+ /* MD5 of sess_kf0 and 4 zero bytes */
+ hmac_md5(sess_kf0, zeros, 0x4, digest2);
+ dump_data_pw("digest2:\n", digest2, sizeof(digest2));
+
+ /* MD5 of the above result, plus 8 bytes of sequence number */
+ hmac_md5(digest2, verf->seq_num, sizeof(verf->seq_num), sealing_key);
+ dump_data_pw("sealing_key:\n", sealing_key, 16);
+}
+
+/*******************************************************************
+ Encode or Decode the sequence number (which is symmetric)
+ ********************************************************************/
+static void netsec_deal_with_seq_num(struct netsec_auth_struct *a,
+ RPC_AUTH_NETSEC_CHK *verf)
+{
+ static uchar zeros[4];
+ uchar sequence_key[16];
+ uchar digest1[16];
+
+ hmac_md5(a->sess_key, zeros, sizeof(zeros), digest1);
+ dump_data_pw("(sequence key) digest1:\n", digest1, sizeof(digest1));
+
+ hmac_md5(digest1, verf->packet_digest, 8, sequence_key);
+
+ dump_data_pw("sequence_key:\n", sequence_key, sizeof(sequence_key));
+
+ dump_data_pw("seq_num (before):\n", verf->seq_num, sizeof(verf->seq_num));
+ SamOEMhash(verf->seq_num, sequence_key, 8);
+ dump_data_pw("seq_num (after):\n", verf->seq_num, sizeof(verf->seq_num));
+}
+
+/*******************************************************************
+creates an RPC_AUTH_NETSEC_CHK structure.
+********************************************************************/
+static BOOL init_rpc_auth_netsec_chk(RPC_AUTH_NETSEC_CHK * chk,
+ const uchar sig[8],
+ const uchar packet_digest[8],
+ const uchar seq_num[8], const uchar confounder[8])
+{
+ if (chk == NULL)
+ return False;
+
+ memcpy(chk->sig, sig, sizeof(chk->sig));
+ memcpy(chk->packet_digest, packet_digest, sizeof(chk->packet_digest));
+ memcpy(chk->seq_num, seq_num, sizeof(chk->seq_num));
+ memcpy(chk->confounder, confounder, sizeof(chk->confounder));
+
+ return True;
+}
+
+
+/*******************************************************************
+ Encode a blob of data using the netsec (schannel) alogrithm, also produceing
+ a checksum over the original data. We currently only support
+ signing and sealing togeather - the signing-only code is close, but not
+ quite compatible with what MS does.
+ ********************************************************************/
+void netsec_encode(struct netsec_auth_struct *a, int auth_flags,
+ enum netsec_direction direction,
+ RPC_AUTH_NETSEC_CHK * verf,
+ char *data, size_t data_len)
+{
+ uchar digest_final[16];
+ uchar confounder[8];
+ uchar seq_num[8];
+ static const uchar nullbytes[8];
+
+ static const uchar netsec_seal_sig[8] = NETSEC_SEAL_SIGNATURE;
+ static const uchar netsec_sign_sig[8] = NETSEC_SIGN_SIGNATURE;
+ const uchar *netsec_sig = NULL;
+
+ DEBUG(10,("SCHANNEL: netsec_encode seq_num=%d data_len=%lu\n", a->seq_num, (unsigned long)data_len));
+
+ if (auth_flags & AUTH_PIPE_SEAL) {
+ netsec_sig = netsec_seal_sig;
+ } else if (auth_flags & AUTH_PIPE_SIGN) {
+ netsec_sig = netsec_sign_sig;
+ }
+
+ /* fill the 'confounder' with random data */
+ generate_random_buffer(confounder, sizeof(confounder), False);
+
+ dump_data_pw("a->sess_key:\n", a->sess_key, sizeof(a->sess_key));
+
+ RSIVAL(seq_num, 0, a->seq_num);
+
+ switch (direction) {
+ case SENDER_IS_INITIATOR:
+ SIVAL(seq_num, 4, 0x80);
+ break;
+ case SENDER_IS_ACCEPTOR:
+ SIVAL(seq_num, 4, 0x0);
+ break;
+ }
+
+ dump_data_pw("verf->seq_num:\n", seq_num, sizeof(verf->seq_num));
+
+ init_rpc_auth_netsec_chk(verf, netsec_sig, nullbytes,
+ seq_num, confounder);
+
+ /* produce a digest of the packet to prove it's legit (before we seal it) */
+ netsec_digest(a, auth_flags, verf, data, data_len, digest_final);
+ memcpy(verf->packet_digest, digest_final, sizeof(verf->packet_digest));
+
+ if (auth_flags & AUTH_PIPE_SEAL) {
+ uchar sealing_key[16];
+
+ /* get the key to encode the data with */
+ netsec_get_sealing_key(a, verf, sealing_key);
+
+ /* encode the verification data */
+ dump_data_pw("verf->confounder:\n", verf->confounder, sizeof(verf->confounder));
+ SamOEMhash(verf->confounder, sealing_key, 8);
+
+ dump_data_pw("verf->confounder_enc:\n", verf->confounder, sizeof(verf->confounder));
+
+ /* encode the packet payload */
+ dump_data_pw("data:\n", (const unsigned char *)data, data_len);
+ SamOEMhash((unsigned char *)data, sealing_key, data_len);
+ dump_data_pw("data_enc:\n", (const unsigned char *)data, data_len);
+ }
+
+ /* encode the sequence number (key based on packet digest) */
+ /* needs to be done after the sealing, as the original version
+ is used in the sealing stuff... */
+ netsec_deal_with_seq_num(a, verf);
+
+ return;
+}
+
+/*******************************************************************
+ Decode a blob of data using the netsec (schannel) alogrithm, also verifiying
+ a checksum over the original data. We currently can verify signed messages,
+ as well as decode sealed messages
+ ********************************************************************/
+
+BOOL netsec_decode(struct netsec_auth_struct *a, int auth_flags,
+ enum netsec_direction direction,
+ RPC_AUTH_NETSEC_CHK * verf, char *data, size_t data_len)
+{
+ uchar digest_final[16];
+
+ static const uchar netsec_seal_sig[8] = NETSEC_SEAL_SIGNATURE;
+ static const uchar netsec_sign_sig[8] = NETSEC_SIGN_SIGNATURE;
+ const uchar *netsec_sig = NULL;
+
+ uchar seq_num[8];
+
+ DEBUG(10,("SCHANNEL: netsec_encode seq_num=%d data_len=%lu\n", a->seq_num, (unsigned long)data_len));
+
+ if (auth_flags & AUTH_PIPE_SEAL) {
+ netsec_sig = netsec_seal_sig;
+ } else if (auth_flags & AUTH_PIPE_SIGN) {
+ netsec_sig = netsec_sign_sig;
+ }
+
+ /* Create the expected sequence number for comparison */
+ RSIVAL(seq_num, 0, a->seq_num);
+
+ switch (direction) {
+ case SENDER_IS_INITIATOR:
+ SIVAL(seq_num, 4, 0x80);
+ break;
+ case SENDER_IS_ACCEPTOR:
+ SIVAL(seq_num, 4, 0x0);
+ break;
+ }
+
+ DEBUG(10,("SCHANNEL: netsec_decode seq_num=%d data_len=%lu\n", a->seq_num, (unsigned long)data_len));
+ dump_data_pw("a->sess_key:\n", a->sess_key, sizeof(a->sess_key));
+
+ dump_data_pw("seq_num:\n", seq_num, sizeof(seq_num));
+
+ /* extract the sequence number (key based on supplied packet digest) */
+ /* needs to be done before the sealing, as the original version
+ is used in the sealing stuff... */
+ netsec_deal_with_seq_num(a, verf);
+
+ if (memcmp(verf->seq_num, seq_num, sizeof(seq_num))) {
+ /* don't even bother with the below if the sequence number is out */
+ /* The sequence number is MD5'ed with a key based on the whole-packet
+ digest, as supplied by the client. We check that it's a valid
+ checksum after the decode, below
+ */
+ DEBUG(2, ("netsec_decode: FAILED: packet sequence number:\n"));
+ dump_data(2, (const char*)verf->seq_num, sizeof(verf->seq_num));
+ DEBUG(2, ("should be:\n"));
+ dump_data(2, (const char*)seq_num, sizeof(seq_num));
+
+ return False;
+ }
+
+ if (memcmp(verf->sig, netsec_sig, sizeof(verf->sig))) {
+ /* Validate that the other end sent the expected header */
+ DEBUG(2, ("netsec_decode: FAILED: packet header:\n"));
+ dump_data(2, (const char*)verf->sig, sizeof(verf->sig));
+ DEBUG(2, ("should be:\n"));
+ dump_data(2, (const char*)netsec_sig, sizeof(netsec_sig));
+ return False;
+ }
+
+ if (auth_flags & AUTH_PIPE_SEAL) {
+ uchar sealing_key[16];
+
+ /* get the key to extract the data with */
+ netsec_get_sealing_key(a, verf, sealing_key);
+
+ /* extract the verification data */
+ dump_data_pw("verf->confounder:\n", verf->confounder,
+ sizeof(verf->confounder));
+ SamOEMhash(verf->confounder, sealing_key, 8);
+
+ dump_data_pw("verf->confounder_dec:\n", verf->confounder,
+ sizeof(verf->confounder));
+
+ /* extract the packet payload */
+ dump_data_pw("data :\n", (const unsigned char *)data, data_len);
+ SamOEMhash((unsigned char *)data, sealing_key, data_len);
+ dump_data_pw("datadec:\n", (const unsigned char *)data, data_len);
+ }
+
+ /* digest includes 'data' after unsealing */
+ netsec_digest(a, auth_flags, verf, data, data_len, digest_final);
+
+ dump_data_pw("Calculated digest:\n", digest_final,
+ sizeof(digest_final));
+ dump_data_pw("verf->packet_digest:\n", verf->packet_digest,
+ sizeof(verf->packet_digest));
+
+ /* compare - if the client got the same result as us, then
+ it must know the session key */
+ return (memcmp(digest_final, verf->packet_digest,
+ sizeof(verf->packet_digest)) == 0);
+}
diff --git a/source/rpc_parse/parse_reg.c b/source/rpc_parse/parse_reg.c
new file mode 100644
index 00000000000..69c0dfc7548
--- /dev/null
+++ b/source/rpc_parse/parse_reg.c
@@ -0,0 +1,1856 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997.
+ * Copyright (C) Marc Jacobsen 1999.
+ * Copyright (C) Simo Sorce 2000.
+ * Copyright (C) Gerald Carter 2002.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+ Fill in a BUFFER2 for the data given a REGISTRY_VALUE
+ *******************************************************************/
+
+static uint32 reg_init_buffer2( BUFFER2 *buf2, REGISTRY_VALUE *val )
+{
+ uint32 real_size = 0;
+
+ if ( !buf2 || !val )
+ return 0;
+
+ real_size = regval_size(val);
+ init_buffer2( buf2, (unsigned char*)regval_data_p(val), real_size );
+
+ return real_size;
+}
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_q_open_hkcr(REG_Q_OPEN_HKCR *q_o,
+ uint16 unknown_0, uint32 level)
+{
+ q_o->ptr = 1;
+ q_o->unknown_0 = unknown_0;
+ q_o->unknown_1 = 0x0; /* random - changes */
+ q_o->level = level;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_open_hkcr(const char *desc, REG_Q_OPEN_HKCR *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_open_hkcr");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr ", ps, depth, &r_q->ptr))
+ return False;
+
+ if (r_q->ptr != 0) {
+ if(!prs_uint16("unknown_0", ps, depth, &r_q->unknown_0))
+ return False;
+ if(!prs_uint16("unknown_1", ps, depth, &r_q->unknown_1))
+ return False;
+ if(!prs_uint32("level ", ps, depth, &r_q->level))
+ return False;
+ }
+
+ return True;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_open_hkcr(const char *desc, REG_R_OPEN_HKCR *r_r, prs_struct *ps, int depth)
+{
+ if (r_r == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_open_hkcr");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_r->pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_r->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_q_open_hklm(REG_Q_OPEN_HKLM * q_o,
+ uint16 unknown_0, uint32 access_mask)
+{
+ q_o->ptr = 1;
+ q_o->unknown_0 = unknown_0;
+ q_o->unknown_1 = 0x0; /* random - changes */
+ q_o->access_mask = access_mask;
+
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL reg_io_q_open_hklm(const char *desc, REG_Q_OPEN_HKLM * r_q, prs_struct *ps,
+ int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_open_hklm");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("ptr ", ps, depth, &(r_q->ptr)))
+ return False;
+ if (r_q->ptr != 0)
+ {
+ if (!prs_uint16("unknown_0", ps, depth, &(r_q->unknown_0)))
+ return False;
+ if (!prs_uint16("unknown_1", ps, depth, &(r_q->unknown_1)))
+ return False;
+ if (!prs_uint32("access_mask", ps, depth, &(r_q->access_mask)))
+ return False;
+ }
+
+ return True;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL reg_io_r_open_hklm(const char *desc, REG_R_OPEN_HKLM * r_r, prs_struct *ps,
+ int depth)
+{
+ if (r_r == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_open_hklm");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!smb_io_pol_hnd("", &r_r->pol, ps, depth))
+ return False;
+
+ if (!prs_ntstatus("status", ps, depth, &r_r->status))
+ return False;
+
+ return True;
+}
+
+
+
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_q_flush_key(REG_Q_FLUSH_KEY *q_u, POLICY_HND *pol)
+{
+ memcpy(&q_u->pol, pol, sizeof(q_u->pol));
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_flush_key(const char *desc, REG_Q_FLUSH_KEY *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_flush_key");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_q->pol, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_flush_key(const char *desc, REG_R_FLUSH_KEY *r_r, prs_struct *ps, int depth)
+{
+ if (r_r == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_flush_key");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_r->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes SEC_DESC_BUF and SEC_DATA structures.
+********************************************************************/
+
+static BOOL reg_io_hdrbuf_sec(uint32 ptr, uint32 *ptr3, BUFHDR *hdr_sec, SEC_DESC_BUF *data, prs_struct *ps, int depth)
+{
+ if (ptr != 0) {
+ uint32 hdr_offset;
+ uint32 old_offset;
+ if(!smb_io_hdrbuf_pre("hdr_sec", hdr_sec, ps, depth, &hdr_offset))
+ return False;
+
+ old_offset = prs_offset(ps);
+
+ if (ptr3 != NULL) {
+ if(!prs_uint32("ptr3", ps, depth, ptr3))
+ return False;
+ }
+
+ if (ptr3 == NULL || *ptr3 != 0) {
+ if(!sec_io_desc_buf("data ", &data, ps, depth)) /* JRA - this line is probably wrong... */
+ return False;
+ }
+
+ if(!smb_io_hdrbuf_post("hdr_sec", hdr_sec, ps, depth, hdr_offset,
+ data->max_len, data->len))
+ return False;
+ if(!prs_set_offset(ps, old_offset + data->len + sizeof(uint32) * ((ptr3 != NULL) ? 5 : 3)))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_q_create_key(REG_Q_CREATE_KEY *q_c, POLICY_HND *hnd,
+ char *name, char *class, SEC_ACCESS *sam_access,
+ SEC_DESC_BUF *sec_buf)
+{
+ ZERO_STRUCTP(q_c);
+
+ memcpy(&q_c->pnt_pol, hnd, sizeof(q_c->pnt_pol));
+
+ init_unistr2(&q_c->uni_name, name, UNI_STR_TERMINATE);
+ init_uni_hdr(&q_c->hdr_name, &q_c->uni_name);
+
+ init_unistr2(&q_c->uni_class, class, UNI_STR_TERMINATE);
+ init_uni_hdr(&q_c->hdr_class, &q_c->uni_class);
+
+ q_c->reserved = 0x00000000;
+ memcpy(&q_c->sam_access, sam_access, sizeof(q_c->sam_access));
+
+ q_c->ptr1 = 1;
+ q_c->sec_info = DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION;
+
+ q_c->data = sec_buf;
+ q_c->ptr2 = 1;
+ init_buf_hdr(&q_c->hdr_sec, sec_buf->len, sec_buf->len);
+ q_c->ptr3 = 1;
+ q_c->unknown_2 = 0x00000000;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_create_key(const char *desc, REG_Q_CREATE_KEY *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_create_key");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_q->pnt_pol, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr ("", &r_q->hdr_name, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &r_q->uni_name, r_q->hdr_name.buffer, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unihdr ("", &r_q->hdr_class, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &r_q->uni_class, r_q->hdr_class.buffer, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("reserved", ps, depth, &r_q->reserved))
+ return False;
+ if(!sec_io_access("sam_access", &r_q->sam_access, ps, depth))
+ return False;
+
+ if(!prs_uint32("ptr1", ps, depth, &r_q->ptr1))
+ return False;
+
+ if (r_q->ptr1 != 0) {
+ if(!prs_uint32("sec_info", ps, depth, &r_q->sec_info))
+ return False;
+ }
+
+ if(!prs_uint32("ptr2", ps, depth, &r_q->ptr2))
+ return False;
+ if(!reg_io_hdrbuf_sec(r_q->ptr2, &r_q->ptr3, &r_q->hdr_sec, r_q->data, ps, depth))
+ return False;
+
+ if(!prs_uint32("unknown_2", ps, depth, &r_q->unknown_2))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_create_key(const char *desc, REG_R_CREATE_KEY *r_r, prs_struct *ps, int depth)
+{
+ if (r_r == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_create_key");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_r->key_pol, ps, depth))
+ return False;
+ if(!prs_uint32("unknown", ps, depth, &r_r->unknown))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_r->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_q_delete_val(REG_Q_DELETE_VALUE *q_c, POLICY_HND *hnd,
+ char *name)
+{
+ ZERO_STRUCTP(q_c);
+
+ memcpy(&q_c->pnt_pol, hnd, sizeof(q_c->pnt_pol));
+
+ init_unistr2(&q_c->uni_name, name, UNI_STR_TERMINATE);
+ init_uni_hdr(&q_c->hdr_name, &q_c->uni_name);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_delete_val(const char *desc, REG_Q_DELETE_VALUE *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_delete_val");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_q->pnt_pol, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr ("", &r_q->hdr_name, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &r_q->uni_name, r_q->hdr_name.buffer, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_delete_val(const char *desc, REG_R_DELETE_VALUE *r_r, prs_struct *ps, int depth)
+{
+ if (r_r == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_delete_val");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_r->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_q_delete_key(REG_Q_DELETE_KEY *q_c, POLICY_HND *hnd,
+ char *name)
+{
+ ZERO_STRUCTP(q_c);
+
+ memcpy(&q_c->pnt_pol, hnd, sizeof(q_c->pnt_pol));
+
+ init_unistr2(&q_c->uni_name, name, UNI_STR_TERMINATE);
+ init_uni_hdr(&q_c->hdr_name, &q_c->uni_name);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_delete_key(const char *desc, REG_Q_DELETE_KEY *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_delete_key");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_q->pnt_pol, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr ("", &r_q->hdr_name, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &r_q->uni_name, r_q->hdr_name.buffer, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_delete_key(const char *desc, REG_R_DELETE_KEY *r_r, prs_struct *ps, int depth)
+{
+ if (r_r == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_delete_key");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_r->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_q_query_key(REG_Q_QUERY_KEY *q_o, POLICY_HND *hnd, UNISTR2 *uni2)
+{
+ ZERO_STRUCTP(q_o);
+
+ memcpy(&q_o->pol, hnd, sizeof(q_o->pol));
+ init_uni_hdr(&q_o->hdr_class, uni2);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_query_key(const char *desc, REG_Q_QUERY_KEY *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_query_key");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_q->pol, ps, depth))
+ return False;
+ if(!smb_io_unihdr ("", &r_q->hdr_class, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &r_q->uni_class, r_q->hdr_class.buffer, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_query_key(const char *desc, REG_R_QUERY_KEY *r_r, prs_struct *ps, int depth)
+{
+ if (r_r == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_query_key");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unihdr ("", &r_r->hdr_class, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &r_r->uni_class, r_r->hdr_class.buffer, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_subkeys ", ps, depth, &r_r->num_subkeys))
+ return False;
+ if(!prs_uint32("max_subkeylen ", ps, depth, &r_r->max_subkeylen))
+ return False;
+ if(!prs_uint32("reserved ", ps, depth, &r_r->reserved))
+ return False;
+ if(!prs_uint32("num_values ", ps, depth, &r_r->num_values))
+ return False;
+ if(!prs_uint32("max_valnamelen", ps, depth, &r_r->max_valnamelen))
+ return False;
+ if(!prs_uint32("max_valbufsize", ps, depth, &r_r->max_valbufsize))
+ return False;
+ if(!prs_uint32("sec_desc ", ps, depth, &r_r->sec_desc))
+ return False;
+ if(!smb_io_time("mod_time ", &r_r->mod_time, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_r->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_q_unknown_1a(REG_Q_UNKNOWN_1A *q_o, POLICY_HND *hnd)
+{
+ memcpy(&q_o->pol, hnd, sizeof(q_o->pol));
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_unknown_1a(const char *desc, REG_Q_UNKNOWN_1A *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_unknown_1a");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_q->pol, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_unknown_1a(const char *desc, REG_R_UNKNOWN_1A *r_r, prs_struct *ps, int depth)
+{
+ if (r_r == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_unknown_1a");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("unknown", ps, depth, &r_r->unknown))
+ return False;
+ if(!prs_ntstatus("status" , ps, depth, &r_r->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_save_key(const char *desc, REG_Q_SAVE_KEY *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_save_key");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_q->pol, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr ("hdr_file", &r_q->hdr_file, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_file", &r_q->uni_file, r_q->hdr_file.buffer, ps, depth))
+ return False;
+
+ if(!prs_uint32("unknown", ps, depth, &r_q->unknown))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_save_key(const char *desc, REG_R_SAVE_KEY *r_r, prs_struct *ps, int depth)
+{
+ if (r_r == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_save_key");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status" , ps, depth, &r_r->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_q_open_hku(REG_Q_OPEN_HKU *q_o,
+ uint16 unknown_0, uint32 access_mask)
+{
+ q_o->ptr = 1;
+ q_o->unknown_0 = unknown_0;
+ q_o->unknown_1 = 0x0; /* random - changes */
+ q_o->access_mask = access_mask;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_open_hku(const char *desc, REG_Q_OPEN_HKU *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_open_hku");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr ", ps, depth, &r_q->ptr))
+ return False;
+ if (r_q->ptr != 0) {
+ if(!prs_uint16("unknown_0 ", ps, depth, &r_q->unknown_0))
+ return False;
+ if(!prs_uint16("unknown_1 ", ps, depth, &r_q->unknown_1))
+ return False;
+ if(!prs_uint32("access_mask ", ps, depth, &r_q->access_mask))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_open_hku(const char *desc, REG_R_OPEN_HKU *r_r, prs_struct *ps, int depth)
+{
+ if (r_r == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_open_hku");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_r->pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_r->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an REG_Q_CLOSE structure.
+********************************************************************/
+
+void init_reg_q_close(REG_Q_CLOSE *q_c, POLICY_HND *hnd)
+{
+ DEBUG(5,("init_reg_q_close\n"));
+
+ memcpy(&q_c->pol, hnd, sizeof(q_c->pol));
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_close(const char *desc, REG_Q_CLOSE *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_close");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &q_u->pol, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_close(const char *desc, REG_R_CLOSE *r_u, prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_close");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_u->pol, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+makes a structure.
+********************************************************************/
+
+void init_reg_q_set_key_sec(REG_Q_SET_KEY_SEC *q_i, POLICY_HND *pol, SEC_DESC_BUF *sec_desc_buf)
+{
+ memcpy(&q_i->pol, pol, sizeof(q_i->pol));
+
+ q_i->sec_info = DACL_SECURITY_INFORMATION;
+
+ q_i->ptr = 1;
+ init_buf_hdr(&q_i->hdr_sec, sec_desc_buf->len, sec_desc_buf->len);
+ q_i->data = sec_desc_buf;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_set_key_sec(const char *desc, REG_Q_SET_KEY_SEC *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_set_key_sec");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_q->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("sec_info", ps, depth, &r_q->sec_info))
+ return False;
+ if(!prs_uint32("ptr ", ps, depth, &r_q->ptr))
+ return False;
+
+ if(!reg_io_hdrbuf_sec(r_q->ptr, NULL, &r_q->hdr_sec, r_q->data, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_set_key_sec(const char *desc, REG_R_SET_KEY_SEC *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_set_key_sec");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_q->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+makes a structure.
+********************************************************************/
+
+void init_reg_q_get_key_sec(REG_Q_GET_KEY_SEC *q_i, POLICY_HND *pol,
+ uint32 sec_buf_size, SEC_DESC_BUF *psdb)
+{
+ memcpy(&q_i->pol, pol, sizeof(q_i->pol));
+
+ q_i->sec_info = OWNER_SECURITY_INFORMATION |
+ GROUP_SECURITY_INFORMATION |
+ DACL_SECURITY_INFORMATION;
+
+ q_i->ptr = psdb != NULL ? 1 : 0;
+ q_i->data = psdb;
+
+ init_buf_hdr(&q_i->hdr_sec, sec_buf_size, 0);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_get_key_sec(const char *desc, REG_Q_GET_KEY_SEC *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_get_key_sec");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_q->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("sec_info", ps, depth, &r_q->sec_info))
+ return False;
+ if(!prs_uint32("ptr ", ps, depth, &r_q->ptr))
+ return False;
+
+ if(!reg_io_hdrbuf_sec(r_q->ptr, NULL, &r_q->hdr_sec, r_q->data, ps, depth))
+ return False;
+
+ return True;
+}
+
+#if 0
+/*******************************************************************
+makes a structure.
+********************************************************************/
+ void init_reg_r_get_key_sec(REG_R_GET_KEY_SEC *r_i, POLICY_HND *pol,
+ uint32 buf_len, uint8 *buf,
+ NTSTATUS status)
+{
+ r_i->ptr = 1;
+ init_buf_hdr(&r_i->hdr_sec, buf_len, buf_len);
+ init_sec_desc_buf(r_i->data, buf_len, 1);
+
+ r_i->status = status; /* 0x0000 0000 or 0x0000 007a */
+}
+#endif
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_get_key_sec(const char *desc, REG_R_GET_KEY_SEC *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_get_key_sec");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr ", ps, depth, &r_q->ptr))
+ return False;
+
+ if (r_q->ptr != 0) {
+ if(!smb_io_hdrbuf("", &r_q->hdr_sec, ps, depth))
+ return False;
+ if(!sec_io_desc_buf("", &r_q->data, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ }
+
+ if(!prs_ntstatus("status", ps, depth, &r_q->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+makes a structure.
+********************************************************************/
+
+BOOL init_reg_q_info(REG_Q_INFO *q_i, POLICY_HND *pol, char* val_name)
+{
+ if (q_i == NULL)
+ return False;
+
+ q_i->pol = *pol;
+
+ init_unistr2(&q_i->uni_type, val_name, UNI_STR_TERMINATE);
+ init_uni_hdr(&q_i->hdr_type, &q_i->uni_type);
+
+ q_i->ptr_reserved = 1;
+ q_i->ptr_buf = 1;
+
+ q_i->ptr_bufsize = 1;
+ q_i->bufsize = 0;
+ q_i->buf_unk = 0;
+
+ q_i->unk1 = 0;
+ q_i->ptr_buflen = 1;
+ q_i->buflen = 0;
+
+ q_i->ptr_buflen2 = 1;
+ q_i->buflen2 = 0;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_info(const char *desc, REG_Q_INFO *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_q->pol, ps, depth))
+ return False;
+ if(!smb_io_unihdr ("", &r_q->hdr_type, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &r_q->uni_type, r_q->hdr_type.buffer, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_reserved", ps, depth, &(r_q->ptr_reserved)))
+ return False;
+
+ if(!prs_uint32("ptr_buf", ps, depth, &(r_q->ptr_buf)))
+ return False;
+
+ if(r_q->ptr_buf) {
+ if(!prs_uint32("ptr_bufsize", ps, depth, &(r_q->ptr_bufsize)))
+ return False;
+ if(!prs_uint32("bufsize", ps, depth, &(r_q->bufsize)))
+ return False;
+ if(!prs_uint32("buf_unk", ps, depth, &(r_q->buf_unk)))
+ return False;
+ }
+
+ if(!prs_uint32("unk1", ps, depth, &(r_q->unk1)))
+ return False;
+
+ if(!prs_uint32("ptr_buflen", ps, depth, &(r_q->ptr_buflen)))
+ return False;
+
+ if (r_q->ptr_buflen) {
+ if(!prs_uint32("buflen", ps, depth, &(r_q->buflen)))
+ return False;
+ if(!prs_uint32("ptr_buflen2", ps, depth, &(r_q->ptr_buflen2)))
+ return False;
+ if(!prs_uint32("buflen2", ps, depth, &(r_q->buflen2)))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a structure.
+ New version to replace older init_reg_r_info()
+********************************************************************/
+
+BOOL new_init_reg_r_info(uint32 include_keyval, REG_R_INFO *r_r,
+ REGISTRY_VALUE *val, NTSTATUS status)
+{
+ uint32 buf_len = 0;
+ BUFFER2 buf2;
+
+ if(r_r == NULL)
+ return False;
+
+ if ( !val )
+ return False;
+
+ r_r->ptr_type = 1;
+ r_r->type = val->type;
+
+ /* if include_keyval is not set, don't send the key value, just
+ the buflen data. probably used by NT5 to allocate buffer space - SK */
+
+ if ( include_keyval ) {
+ r_r->ptr_uni_val = 1;
+ buf_len = reg_init_buffer2( &r_r->uni_val, val );
+
+ }
+ else {
+ /* dummy buffer used so we can get the size */
+ r_r->ptr_uni_val = 0;
+ buf_len = reg_init_buffer2( &buf2, val );
+ }
+
+ r_r->ptr_max_len = 1;
+ r_r->buf_max_len = buf_len;
+
+ r_r->ptr_len = 1;
+ r_r->buf_len = buf_len;
+
+ r_r->status = status;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+BOOL init_reg_r_info(uint32 include_keyval, REG_R_INFO *r_r,
+ BUFFER2* buf, uint32 type, NTSTATUS status)
+{
+ if(r_r == NULL)
+ return False;
+
+ r_r->ptr_type = 1;
+ r_r->type = type;
+
+ /* if include_keyval is not set, don't send the key value, just
+ the buflen data. probably used by NT5 to allocate buffer space - SK */
+
+ r_r->ptr_uni_val = include_keyval ? 1:0;
+ r_r->uni_val = *buf;
+
+ r_r->ptr_max_len = 1;
+ r_r->buf_max_len = r_r->uni_val.buf_max_len;
+
+ r_r->ptr_len = 1;
+ r_r->buf_len = r_r->uni_val.buf_len;
+
+ r_r->status = status;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_info(const char *desc, REG_R_INFO *r_r, prs_struct *ps, int depth)
+{
+ if (r_r == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_type", ps, depth, &(r_r->ptr_type)))
+ return False;
+
+ if (r_r->ptr_type != 0) {
+ if(!prs_uint32("type", ps, depth, &r_r->type))
+ return False;
+ }
+
+ if(!prs_uint32("ptr_uni_val", ps, depth, &(r_r->ptr_uni_val)))
+ return False;
+
+ if(r_r->ptr_uni_val != 0) {
+ if(!smb_io_buffer2("uni_val", &r_r->uni_val, r_r->ptr_uni_val, ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_max_len", ps, depth, &(r_r->ptr_max_len)))
+ return False;
+
+ if (r_r->ptr_max_len != 0) {
+ if(!prs_uint32("buf_max_len", ps, depth, &(r_r->buf_max_len)))
+ return False;
+ }
+
+ if(!prs_uint32("ptr_len", ps, depth, &(r_r->ptr_len)))
+ return False;
+ if (r_r->ptr_len != 0) {
+ if(!prs_uint32("buf_len", ps, depth, &(r_r->buf_len)))
+ return False;
+ }
+
+ if(!prs_ntstatus("status", ps, depth, &r_r->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+makes a structure.
+********************************************************************/
+
+void init_reg_q_enum_val(REG_Q_ENUM_VALUE *q_i, POLICY_HND *pol,
+ uint32 val_idx, UNISTR2 *uni2,
+ uint32 max_buf_len)
+{
+ ZERO_STRUCTP(q_i);
+
+ memcpy(&q_i->pol, pol, sizeof(q_i->pol));
+
+ q_i->val_index = val_idx;
+ init_uni_hdr(&q_i->hdr_name, uni2);
+
+ q_i->ptr_type = 1;
+ q_i->type = 0x0;
+
+ q_i->ptr_value = 1;
+ q_i->buf_value.buf_max_len = max_buf_len;
+
+ q_i->ptr1 = 1;
+ q_i->len_value1 = max_buf_len;
+
+ q_i->ptr2 = 1;
+ q_i->len_value2 = 0;
+}
+
+/*******************************************************************
+makes a structure.
+********************************************************************/
+
+void init_reg_r_enum_val(REG_R_ENUM_VALUE *r_u, REGISTRY_VALUE *val )
+{
+ uint32 real_size;
+
+ DEBUG(8,("init_reg_r_enum_val: Enter\n"));
+
+ ZERO_STRUCTP(r_u);
+
+ /* value name */
+
+ DEBUG(10,("init_reg_r_enum_val: Valuename => [%s]\n", val->valuename));
+
+ init_unistr2( &r_u->uni_name, val->valuename, UNI_STR_TERMINATE);
+ init_uni_hdr( &r_u->hdr_name, &r_u->uni_name);
+
+ /* type */
+
+ r_u->ptr_type = 1;
+ r_u->type = val->type;
+
+ /* REG_SZ & REG_MULTI_SZ must be converted to UNICODE */
+
+ r_u->ptr_value = 1;
+ real_size = reg_init_buffer2( &r_u->buf_value, val );
+
+ /* lengths */
+
+ r_u->ptr1 = 1;
+ r_u->len_value1 = real_size;
+
+ r_u->ptr2 = 1;
+ r_u->len_value2 = real_size;
+
+ DEBUG(8,("init_reg_r_enum_val: Exit\n"));
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_enum_val(const char *desc, REG_Q_ENUM_VALUE *q_q, prs_struct *ps, int depth)
+{
+ if (q_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_enum_val");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("val_index", ps, depth, &q_q->val_index))
+ return False;
+
+ if(!smb_io_unihdr ("hdr_name", &q_q->hdr_name, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_name", &q_q->uni_name, q_q->hdr_name.buffer, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_type", ps, depth, &q_q->ptr_type))
+ return False;
+
+ if (q_q->ptr_type != 0) {
+ if(!prs_uint32("type", ps, depth, &q_q->type))
+ return False;
+ }
+
+ if(!prs_uint32("ptr_value", ps, depth, &q_q->ptr_value))
+ return False;
+ if(!smb_io_buffer2("buf_value", &q_q->buf_value, q_q->ptr_value, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr1", ps, depth, &q_q->ptr1))
+ return False;
+ if (q_q->ptr1 != 0) {
+ if(!prs_uint32("len_value1", ps, depth, &q_q->len_value1))
+ return False;
+ }
+ if(!prs_uint32("ptr2", ps, depth, &q_q->ptr2))
+ return False;
+ if (q_q->ptr2 != 0) {
+ if(!prs_uint32("len_value2", ps, depth, &q_q->len_value2))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_enum_val(const char *desc, REG_R_ENUM_VALUE *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_enum_val");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unihdr ("hdr_name", &r_q->hdr_name, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_name", &r_q->uni_name, r_q->hdr_name.buffer, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_type", ps, depth, &r_q->ptr_type))
+ return False;
+
+ if (r_q->ptr_type != 0) {
+ if(!prs_uint32("type", ps, depth, &r_q->type))
+ return False;
+ }
+
+ if(!prs_uint32("ptr_value", ps, depth, &r_q->ptr_value))
+ return False;
+ if(!smb_io_buffer2("buf_value", &r_q->buf_value, r_q->ptr_value, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr1", ps, depth, &r_q->ptr1))
+ return False;
+ if (r_q->ptr1 != 0) {
+ if(!prs_uint32("len_value1", ps, depth, &r_q->len_value1))
+ return False;
+ }
+
+ if(!prs_uint32("ptr2", ps, depth, &r_q->ptr2))
+ return False;
+ if (r_q->ptr2 != 0) {
+ if(!prs_uint32("len_value2", ps, depth, &r_q->len_value2))
+ return False;
+ }
+
+ if(!prs_ntstatus("status", ps, depth, &r_q->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+makes a structure.
+********************************************************************/
+
+void init_reg_q_create_val(REG_Q_CREATE_VALUE *q_i, POLICY_HND *pol,
+ char *val_name, uint32 type,
+ BUFFER3 *val)
+{
+ ZERO_STRUCTP(q_i);
+
+ memcpy(&q_i->pol, pol, sizeof(q_i->pol));
+
+ init_unistr2(&q_i->uni_name, val_name, UNI_STR_TERMINATE);
+ init_uni_hdr(&q_i->hdr_name, &q_i->uni_name);
+
+ q_i->type = type;
+ q_i->buf_value = val;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_create_val(const char *desc, REG_Q_CREATE_VALUE *q_q, prs_struct *ps, int depth)
+{
+ if (q_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_create_val");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr ("hdr_name", &q_q->hdr_name, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_name", &q_q->uni_name, q_q->hdr_name.buffer, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("type", ps, depth, &q_q->type))
+ return False;
+ if(!smb_io_buffer3("buf_value", q_q->buf_value, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_create_val(const char *desc, REG_R_CREATE_VALUE *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_create_val");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_q->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+makes a structure.
+********************************************************************/
+
+void init_reg_q_enum_key(REG_Q_ENUM_KEY *q_i, POLICY_HND *pol, uint32 key_idx)
+{
+ memcpy(&q_i->pol, pol, sizeof(q_i->pol));
+
+ q_i->key_index = key_idx;
+ q_i->key_name_len = 0;
+ q_i->unknown_1 = 0x0414;
+
+ q_i->ptr1 = 1;
+ q_i->unknown_2 = 0x0000020A;
+ memset(q_i->pad1, 0, sizeof(q_i->pad1));
+
+ q_i->ptr2 = 1;
+ memset(q_i->pad2, 0, sizeof(q_i->pad2));
+
+ q_i->ptr3 = 1;
+ unix_to_nt_time(&q_i->time, 0); /* current time? */
+}
+
+/*******************************************************************
+makes a reply structure.
+********************************************************************/
+
+void init_reg_r_enum_key(REG_R_ENUM_KEY *r_u, char *subkey, uint32 unknown_1,
+ uint32 unknown_2)
+{
+ if ( !r_u )
+ return;
+
+ r_u->unknown_1 = unknown_1;
+ r_u->unknown_2 = unknown_2;
+ r_u->unknown_3 = 0x0;
+
+ r_u->key_name_len = (strlen(subkey)+1) * 2;
+ if (r_u->key_name_len)
+ r_u->ptr1 = 0x1;
+ init_unistr3( &r_u->key_name, subkey );
+
+ r_u->ptr2 = 0x1;
+ r_u->ptr3 = 0x1;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_enum_key(const char *desc, REG_Q_ENUM_KEY *q_q, prs_struct *ps, int depth)
+{
+ if (q_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_enum_key");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("key_index", ps, depth, &q_q->key_index))
+ return False;
+ if(!prs_uint16("key_name_len", ps, depth, &q_q->key_name_len))
+ return False;
+ if(!prs_uint16("unknown_1", ps, depth, &q_q->unknown_1))
+ return False;
+
+ if(!prs_uint32("ptr1", ps, depth, &q_q->ptr1))
+ return False;
+
+ if (q_q->ptr1 != 0) {
+ if(!prs_uint32("unknown_2", ps, depth, &q_q->unknown_2))
+ return False;
+ if(!prs_uint8s(False, "pad1", ps, depth, q_q->pad1, sizeof(q_q->pad1)))
+ return False;
+ }
+
+ if(!prs_uint32("ptr2", ps, depth, &q_q->ptr2))
+ return False;
+
+ if (q_q->ptr2 != 0) {
+ if(!prs_uint8s(False, "pad2", ps, depth, q_q->pad2, sizeof(q_q->pad2)))
+ return False;
+ }
+
+ if(!prs_uint32("ptr3", ps, depth, &q_q->ptr3))
+ return False;
+
+ if (q_q->ptr3 != 0) {
+ if(!smb_io_time("", &q_q->time, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_enum_key(const char *desc, REG_R_ENUM_KEY *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_enum_key");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint16("key_name_len", ps, depth, &r_q->key_name_len))
+ return False;
+ if(!prs_uint16("unknown_1", ps, depth, &r_q->unknown_1))
+ return False;
+
+ if(!prs_uint32("ptr1", ps, depth, &r_q->ptr1))
+ return False;
+
+ if (r_q->ptr1 != 0) {
+ if(!prs_uint32("unknown_2", ps, depth, &r_q->unknown_2))
+ return False;
+ if(!prs_uint32("unknown_3", ps, depth, &r_q->unknown_3))
+ return False;
+ if(!smb_io_unistr3("key_name", &r_q->key_name, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ }
+
+ if(!prs_uint32("ptr2", ps, depth, &r_q->ptr2))
+ return False;
+
+ if (r_q->ptr2 != 0) {
+ if(!prs_uint8s(False, "pad2", ps, depth, r_q->pad2, sizeof(r_q->pad2)))
+ return False;
+ }
+
+ if(!prs_uint32("ptr3", ps, depth, &r_q->ptr3))
+ return False;
+
+ if (r_q->ptr3 != 0) {
+ if(!smb_io_time("", &r_q->time, ps, depth))
+ return False;
+ }
+
+ if(!prs_ntstatus("status", ps, depth, &r_q->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+makes a structure.
+********************************************************************/
+
+void init_reg_q_open_entry(REG_Q_OPEN_ENTRY *r_q, POLICY_HND *pol,
+ char *key_name, uint32 access_desired)
+{
+ memcpy(&r_q->pol, pol, sizeof(r_q->pol));
+
+ init_unistr2(&r_q->uni_name, key_name, UNI_STR_TERMINATE);
+ init_uni_hdr(&r_q->hdr_name, &r_q->uni_name);
+
+ r_q->unknown_0 = 0x00000000;
+ r_q->access_desired = access_desired;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_open_entry(const char *desc, REG_Q_OPEN_ENTRY *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_entry");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_q->pol, ps, depth))
+ return False;
+ if(!smb_io_unihdr ("", &r_q->hdr_name, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &r_q->uni_name, r_q->hdr_name.buffer, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("unknown_0 ", ps, depth, &r_q->unknown_0))
+ return False;
+ if(!prs_uint32("access_desired ", ps, depth, &r_q->access_desired))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_r_open_entry(REG_R_OPEN_ENTRY *r_r,
+ POLICY_HND *pol, NTSTATUS status)
+{
+ if (NT_STATUS_IS_OK(status)) {
+ memcpy(&r_r->pol, pol, sizeof(r_r->pol));
+ } else {
+ ZERO_STRUCT(r_r->pol);
+ }
+ r_r->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_open_entry(const char *desc, REG_R_OPEN_ENTRY *r_r, prs_struct *ps, int depth)
+{
+ if (r_r == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_open_entry");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_r->pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_r->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+Inits a structure.
+********************************************************************/
+
+void init_reg_q_shutdown(REG_Q_SHUTDOWN * q_s, const char *msg,
+ uint32 timeout, BOOL do_reboot, BOOL force)
+{
+ q_s->ptr_0 = 1;
+ q_s->ptr_1 = 1;
+ q_s->ptr_2 = 1;
+
+ init_unistr2(&q_s->uni_msg, msg, UNI_FLAGS_NONE);
+ init_uni_hdr(&q_s->hdr_msg, &q_s->uni_msg);
+
+ q_s->timeout = timeout;
+
+ q_s->reboot = do_reboot ? 1 : 0;
+ q_s->force = force ? 1 : 0;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_shutdown(const char *desc, REG_Q_SHUTDOWN * q_s, prs_struct *ps,
+ int depth)
+{
+ if (q_s == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_shutdown");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("ptr_0", ps, depth, &(q_s->ptr_0)))
+ return False;
+ if (!prs_uint32("ptr_1", ps, depth, &(q_s->ptr_1)))
+ return False;
+ if (!prs_uint32("ptr_2", ps, depth, &(q_s->ptr_2)))
+ return False;
+
+ if (!smb_io_unihdr("hdr_msg", &(q_s->hdr_msg), ps, depth))
+ return False;
+ if (!smb_io_unistr2("uni_msg", &(q_s->uni_msg), q_s->hdr_msg.buffer, ps, depth))
+ return False;
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("timeout", ps, depth, &(q_s->timeout)))
+ return False;
+ if (!prs_uint8("force ", ps, depth, &(q_s->force)))
+ return False;
+ if (!prs_uint8("reboot ", ps, depth, &(q_s->reboot)))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL reg_io_r_shutdown(const char *desc, REG_R_SHUTDOWN * r_s, prs_struct *ps,
+ int depth)
+{
+ if (r_s == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_shutdown");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_s->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+Inits a structure.
+********************************************************************/
+void init_reg_q_abort_shutdown(REG_Q_ABORT_SHUTDOWN * q_s)
+{
+
+ q_s->ptr_server = 0;
+
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL reg_io_q_abort_shutdown(const char *desc, REG_Q_ABORT_SHUTDOWN * q_s,
+ prs_struct *ps, int depth)
+{
+ if (q_s == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_abort_shutdown");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("ptr_server", ps, depth, &(q_s->ptr_server)))
+ return False;
+ if (q_s->ptr_server != 0)
+ if (!prs_uint16("server", ps, depth, &(q_s->server)))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL reg_io_r_abort_shutdown(const char *desc, REG_R_ABORT_SHUTDOWN * r_s,
+ prs_struct *ps, int depth)
+{
+ if (r_s == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_abort_shutdown");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_ntstatus("status", ps, depth, &r_s->status))
+ return False;
+
+ return True;
+}
diff --git a/source/rpc_parse/parse_rpc.c b/source/rpc_parse/parse_rpc.c
new file mode 100644
index 00000000000..696f258e5de
--- /dev/null
+++ b/source/rpc_parse/parse_rpc.c
@@ -0,0 +1,1219 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997.
+ * Copyright (C) Jeremy Allison 1999.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+interface/version dce/rpc pipe identification
+********************************************************************/
+
+#define TRANS_SYNT_V2 \
+{ \
+ { \
+ 0x8a885d04, 0x1ceb, 0x11c9, \
+ { 0x9f, 0xe8 }, \
+ { 0x08, 0x00, \
+ 0x2b, 0x10, 0x48, 0x60 } \
+ }, 0x02 \
+}
+
+#define SYNT_NETLOGON_V2 \
+{ \
+ { \
+ 0x8a885d04, 0x1ceb, 0x11c9, \
+ { 0x9f, 0xe8 }, \
+ { 0x08, 0x00, \
+ 0x2b, 0x10, 0x48, 0x60 } \
+ }, 0x02 \
+}
+
+#define SYNT_WKSSVC_V1 \
+{ \
+ { \
+ 0x6bffd098, 0xa112, 0x3610, \
+ { 0x98, 0x33 }, \
+ { 0x46, 0xc3, \
+ 0xf8, 0x7e, 0x34, 0x5a } \
+ }, 0x01 \
+}
+
+#define SYNT_SRVSVC_V3 \
+{ \
+ { \
+ 0x4b324fc8, 0x1670, 0x01d3, \
+ { 0x12, 0x78 }, \
+ { 0x5a, 0x47, \
+ 0xbf, 0x6e, 0xe1, 0x88 } \
+ }, 0x03 \
+}
+
+#define SYNT_LSARPC_V0 \
+{ \
+ { \
+ 0x12345778, 0x1234, 0xabcd, \
+ { 0xef, 0x00 }, \
+ { 0x01, 0x23, \
+ 0x45, 0x67, 0x89, 0xab } \
+ }, 0x00 \
+}
+
+#define SYNT_LSARPC_V0_DS \
+{ \
+ { \
+ 0x3919286a, 0xb10c, 0x11d0, \
+ { 0x9b, 0xa8 }, \
+ { 0x00, 0xc0, \
+ 0x4f, 0xd9, 0x2e, 0xf5 } \
+ }, 0x00 \
+}
+
+#define SYNT_SAMR_V1 \
+{ \
+ { \
+ 0x12345778, 0x1234, 0xabcd, \
+ { 0xef, 0x00 }, \
+ { 0x01, 0x23, \
+ 0x45, 0x67, 0x89, 0xac } \
+ }, 0x01 \
+}
+
+#define SYNT_NETLOGON_V1 \
+{ \
+ { \
+ 0x12345678, 0x1234, 0xabcd, \
+ { 0xef, 0x00 }, \
+ { 0x01, 0x23, \
+ 0x45, 0x67, 0xcf, 0xfb } \
+ }, 0x01 \
+}
+
+#define SYNT_WINREG_V1 \
+{ \
+ { \
+ 0x338cd001, 0x2244, 0x31f1, \
+ { 0xaa, 0xaa }, \
+ { 0x90, 0x00, \
+ 0x38, 0x00, 0x10, 0x03 } \
+ }, 0x01 \
+}
+
+#define SYNT_SPOOLSS_V1 \
+{ \
+ { \
+ 0x12345678, 0x1234, 0xabcd, \
+ { 0xef, 0x00 }, \
+ { 0x01, 0x23, \
+ 0x45, 0x67, 0x89, 0xab } \
+ }, 0x01 \
+}
+
+#define SYNT_NONE_V0 \
+{ \
+ { \
+ 0x0, 0x0, 0x0, \
+ { 0x00, 0x00 }, \
+ { 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00 } \
+ }, 0x00 \
+}
+
+#define SYNT_NETDFS_V3 \
+{ \
+ { \
+ 0x4fc742e0, 0x4a10, 0x11cf, \
+ { 0x82, 0x73 }, \
+ { 0x00, 0xaa, \
+ 0x00, 0x4a, 0xe6, 0x73 } \
+ }, 0x03 \
+}
+
+#define SYNT_ECHO_V1 \
+{ \
+ { \
+ 0x60a15ec5, 0x4de8, 0x11d7, \
+ { 0xa6, 0x37 }, \
+ { 0x00, 0x50, \
+ 0x56, 0xa2, 0x01, 0x82 } \
+ }, 0x01 \
+}
+
+#define SYNT_SHUTDOWN_V1 \
+{ \
+ { \
+ 0x894de0c0, 0x0d55, 0x11d3, \
+ { 0xa3, 0x22 }, \
+ { 0x00, 0xc0, \
+ 0x4f, 0xa3, 0x21, 0xa1 } \
+ }, 0x01 \
+}
+
+#define SYNT_EPM_V3 \
+{ \
+ { \
+ 0xe1af8308, 0x5d1f, 0x11c9, \
+ { 0x91, 0xa4 }, \
+ { 0x08, 0x00, \
+ 0x2b, 0x14, 0xa0, 0xfa } \
+ }, 0x03 \
+}
+
+/*
+ * IMPORTANT!! If you update this structure, make sure to
+ * update the index #defines in smb.h.
+ */
+
+const struct pipe_id_info pipe_names [] =
+{
+ /* client pipe , abstract syntax , server pipe , transfer syntax */
+ { PIPE_LSARPC , SYNT_LSARPC_V0 , PIPE_LSASS , TRANS_SYNT_V2 },
+ { PIPE_LSARPC , SYNT_LSARPC_V0_DS , PIPE_LSASS , TRANS_SYNT_V2 },
+ { PIPE_SAMR , SYNT_SAMR_V1 , PIPE_LSASS , TRANS_SYNT_V2 },
+ { PIPE_NETLOGON, SYNT_NETLOGON_V1 , PIPE_LSASS , TRANS_SYNT_V2 },
+ { PIPE_SRVSVC , SYNT_SRVSVC_V3 , PIPE_NTSVCS , TRANS_SYNT_V2 },
+ { PIPE_WKSSVC , SYNT_WKSSVC_V1 , PIPE_NTSVCS , TRANS_SYNT_V2 },
+ { PIPE_WINREG , SYNT_WINREG_V1 , PIPE_WINREG , TRANS_SYNT_V2 },
+ { PIPE_SPOOLSS , SYNT_SPOOLSS_V1 , PIPE_SPOOLSS , TRANS_SYNT_V2 },
+ { PIPE_NETDFS , SYNT_NETDFS_V3 , PIPE_NETDFS , TRANS_SYNT_V2 },
+ { PIPE_ECHO , SYNT_ECHO_V1 , PIPE_ECHO , TRANS_SYNT_V2 },
+ { PIPE_SHUTDOWN, SYNT_SHUTDOWN_V1 , PIPE_SHUTDOWN , TRANS_SYNT_V2 },
+ { PIPE_EPM , SYNT_EPM_V3 , PIPE_EPM , TRANS_SYNT_V2 },
+ { NULL , SYNT_NONE_V0 , NULL , SYNT_NONE_V0 }
+};
+
+/*******************************************************************
+ Inits an RPC_HDR structure.
+********************************************************************/
+
+void init_rpc_hdr(RPC_HDR *hdr, enum RPC_PKT_TYPE pkt_type, uint8 flags,
+ uint32 call_id, int data_len, int auth_len)
+{
+ hdr->major = 5; /* RPC version 5 */
+ hdr->minor = 0; /* minor version 0 */
+ hdr->pkt_type = pkt_type; /* RPC packet type */
+ hdr->flags = flags; /* dce/rpc flags */
+ hdr->pack_type[0] = 0x10; /* little-endian data representation */
+ hdr->pack_type[1] = 0; /* packed data representation */
+ hdr->pack_type[2] = 0; /* packed data representation */
+ hdr->pack_type[3] = 0; /* packed data representation */
+ hdr->frag_len = data_len; /* fragment length, fill in later */
+ hdr->auth_len = auth_len; /* authentication length */
+ hdr->call_id = call_id; /* call identifier - match incoming RPC */
+}
+
+/*******************************************************************
+ Reads or writes an RPC_HDR structure.
+********************************************************************/
+
+BOOL smb_io_rpc_hdr(const char *desc, RPC_HDR *rpc, prs_struct *ps, int depth)
+{
+ if (rpc == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_hdr");
+ depth++;
+
+ if(!prs_uint8 ("major ", ps, depth, &rpc->major))
+ return False;
+
+ if(!prs_uint8 ("minor ", ps, depth, &rpc->minor))
+ return False;
+ if(!prs_uint8 ("pkt_type ", ps, depth, &rpc->pkt_type))
+ return False;
+ if(!prs_uint8 ("flags ", ps, depth, &rpc->flags))
+ return False;
+
+ /* We always marshall in little endian format. */
+ if (MARSHALLING(ps))
+ rpc->pack_type[0] = 0x10;
+
+ if(!prs_uint8("pack_type0", ps, depth, &rpc->pack_type[0]))
+ return False;
+ if(!prs_uint8("pack_type1", ps, depth, &rpc->pack_type[1]))
+ return False;
+ if(!prs_uint8("pack_type2", ps, depth, &rpc->pack_type[2]))
+ return False;
+ if(!prs_uint8("pack_type3", ps, depth, &rpc->pack_type[3]))
+ return False;
+
+ /*
+ * If reading and pack_type[0] == 0 then the data is in big-endian
+ * format. Set the flag in the prs_struct to specify reverse-endainness.
+ */
+
+ if (UNMARSHALLING(ps) && rpc->pack_type[0] == 0) {
+ DEBUG(10,("smb_io_rpc_hdr: PDU data format is big-endian. Setting flag.\n"));
+ prs_set_endian_data(ps, RPC_BIG_ENDIAN);
+ }
+
+ if(!prs_uint16("frag_len ", ps, depth, &rpc->frag_len))
+ return False;
+ if(!prs_uint16("auth_len ", ps, depth, &rpc->auth_len))
+ return False;
+ if(!prs_uint32("call_id ", ps, depth, &rpc->call_id))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an RPC_IFACE structure.
+********************************************************************/
+
+static BOOL smb_io_rpc_iface(const char *desc, RPC_IFACE *ifc, prs_struct *ps, int depth)
+{
+ if (ifc == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_iface");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!smb_io_uuid( "uuid", &ifc->uuid, ps, depth))
+ return False;
+
+ if(!prs_uint32 ("version", ps, depth, &ifc->version))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an RPC_ADDR_STR structure.
+********************************************************************/
+
+static void init_rpc_addr_str(RPC_ADDR_STR *str, const char *name)
+{
+ str->len = strlen(name) + 1;
+ fstrcpy(str->str, name);
+}
+
+/*******************************************************************
+ Reads or writes an RPC_ADDR_STR structure.
+********************************************************************/
+
+static BOOL smb_io_rpc_addr_str(const char *desc, RPC_ADDR_STR *str, prs_struct *ps, int depth)
+{
+ if (str == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_addr_str");
+ depth++;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint16 ( "len", ps, depth, &str->len))
+ return False;
+ if(!prs_uint8s (True, "str", ps, depth, (uchar*)str->str, MIN(str->len, sizeof(str->str)) ))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+ Inits an RPC_HDR_BBA structure.
+********************************************************************/
+
+static void init_rpc_hdr_bba(RPC_HDR_BBA *bba, uint16 max_tsize, uint16 max_rsize, uint32 assoc_gid)
+{
+ bba->max_tsize = max_tsize; /* maximum transmission fragment size (0x1630) */
+ bba->max_rsize = max_rsize; /* max receive fragment size (0x1630) */
+ bba->assoc_gid = assoc_gid; /* associated group id (0x0) */
+}
+
+/*******************************************************************
+ Reads or writes an RPC_HDR_BBA structure.
+********************************************************************/
+
+static BOOL smb_io_rpc_hdr_bba(const char *desc, RPC_HDR_BBA *rpc, prs_struct *ps, int depth)
+{
+ if (rpc == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_hdr_bba");
+ depth++;
+
+ if(!prs_uint16("max_tsize", ps, depth, &rpc->max_tsize))
+ return False;
+ if(!prs_uint16("max_rsize", ps, depth, &rpc->max_rsize))
+ return False;
+ if(!prs_uint32("assoc_gid", ps, depth, &rpc->assoc_gid))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+ Inits an RPC_HDR_RB structure.
+********************************************************************/
+
+void init_rpc_hdr_rb(RPC_HDR_RB *rpc,
+ uint16 max_tsize, uint16 max_rsize, uint32 assoc_gid,
+ uint32 num_elements, uint16 context_id, uint8 num_syntaxes,
+ RPC_IFACE *abstract, RPC_IFACE *transfer)
+{
+ init_rpc_hdr_bba(&rpc->bba, max_tsize, max_rsize, assoc_gid);
+
+ rpc->num_elements = num_elements ; /* the number of elements (0x1) */
+ rpc->context_id = context_id ; /* presentation context identifier (0x0) */
+ rpc->num_syntaxes = num_syntaxes ; /* the number of syntaxes (has always been 1?)(0x1) */
+
+ /* num and vers. of interface client is using */
+ rpc->abstract = *abstract;
+
+ /* num and vers. of interface to use for replies */
+ rpc->transfer = *transfer;
+}
+
+/*******************************************************************
+ Reads or writes an RPC_HDR_RB structure.
+********************************************************************/
+
+BOOL smb_io_rpc_hdr_rb(const char *desc, RPC_HDR_RB *rpc, prs_struct *ps, int depth)
+{
+ if (rpc == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_hdr_rb");
+ depth++;
+
+ if(!smb_io_rpc_hdr_bba("", &rpc->bba, ps, depth))
+ return False;
+
+ if(!prs_uint32("num_elements", ps, depth, &rpc->num_elements))
+ return False;
+ if(!prs_uint16("context_id ", ps, depth, &rpc->context_id ))
+ return False;
+ if(!prs_uint8 ("num_syntaxes", ps, depth, &rpc->num_syntaxes))
+ return False;
+
+ if(!smb_io_rpc_iface("", &rpc->abstract, ps, depth))
+ return False;
+ if(!smb_io_rpc_iface("", &rpc->transfer, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an RPC_RESULTS structure.
+
+ lkclXXXX only one reason at the moment!
+********************************************************************/
+
+static void init_rpc_results(RPC_RESULTS *res,
+ uint8 num_results, uint16 result, uint16 reason)
+{
+ res->num_results = num_results; /* the number of results (0x01) */
+ res->result = result ; /* result (0x00 = accept) */
+ res->reason = reason ; /* reason (0x00 = no reason specified) */
+}
+
+/*******************************************************************
+ Reads or writes an RPC_RESULTS structure.
+
+ lkclXXXX only one reason at the moment!
+********************************************************************/
+
+static BOOL smb_io_rpc_results(const char *desc, RPC_RESULTS *res, prs_struct *ps, int depth)
+{
+ if (res == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_results");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint8 ("num_results", ps, depth, &res->num_results))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint16("result ", ps, depth, &res->result))
+ return False;
+ if(!prs_uint16("reason ", ps, depth, &res->reason))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+ Init an RPC_HDR_BA structure.
+
+ lkclXXXX only one reason at the moment!
+
+********************************************************************/
+
+void init_rpc_hdr_ba(RPC_HDR_BA *rpc,
+ uint16 max_tsize, uint16 max_rsize, uint32 assoc_gid,
+ const char *pipe_addr,
+ uint8 num_results, uint16 result, uint16 reason,
+ RPC_IFACE *transfer)
+{
+ init_rpc_hdr_bba (&rpc->bba, max_tsize, max_rsize, assoc_gid);
+ init_rpc_addr_str(&rpc->addr, pipe_addr);
+ init_rpc_results (&rpc->res, num_results, result, reason);
+
+ /* the transfer syntax from the request */
+ memcpy(&rpc->transfer, transfer, sizeof(rpc->transfer));
+}
+
+/*******************************************************************
+ Reads or writes an RPC_HDR_BA structure.
+********************************************************************/
+
+BOOL smb_io_rpc_hdr_ba(const char *desc, RPC_HDR_BA *rpc, prs_struct *ps, int depth)
+{
+ if (rpc == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_hdr_ba");
+ depth++;
+
+ if(!smb_io_rpc_hdr_bba("", &rpc->bba, ps, depth))
+ return False;
+ if(!smb_io_rpc_addr_str("", &rpc->addr, ps, depth))
+ return False;
+ if(!smb_io_rpc_results("", &rpc->res, ps, depth))
+ return False;
+ if(!smb_io_rpc_iface("", &rpc->transfer, ps, depth))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+ Init an RPC_HDR_REQ structure.
+********************************************************************/
+
+void init_rpc_hdr_req(RPC_HDR_REQ *hdr, uint32 alloc_hint, uint16 opnum)
+{
+ hdr->alloc_hint = alloc_hint; /* allocation hint */
+ hdr->context_id = 0; /* presentation context identifier */
+ hdr->opnum = opnum; /* opnum */
+}
+
+/*******************************************************************
+ Reads or writes an RPC_HDR_REQ structure.
+********************************************************************/
+
+BOOL smb_io_rpc_hdr_req(const char *desc, RPC_HDR_REQ *rpc, prs_struct *ps, int depth)
+{
+ if (rpc == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_hdr_req");
+ depth++;
+
+ if(!prs_uint32("alloc_hint", ps, depth, &rpc->alloc_hint))
+ return False;
+ if(!prs_uint16("context_id", ps, depth, &rpc->context_id))
+ return False;
+ if(!prs_uint16("opnum ", ps, depth, &rpc->opnum))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an RPC_HDR_RESP structure.
+********************************************************************/
+
+BOOL smb_io_rpc_hdr_resp(const char *desc, RPC_HDR_RESP *rpc, prs_struct *ps, int depth)
+{
+ if (rpc == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_hdr_resp");
+ depth++;
+
+ if(!prs_uint32("alloc_hint", ps, depth, &rpc->alloc_hint))
+ return False;
+ if(!prs_uint16("context_id", ps, depth, &rpc->context_id))
+ return False;
+ if(!prs_uint8 ("cancel_ct ", ps, depth, &rpc->cancel_count))
+ return False;
+ if(!prs_uint8 ("reserved ", ps, depth, &rpc->reserved))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an RPC_HDR_FAULT structure.
+********************************************************************/
+
+BOOL smb_io_rpc_hdr_fault(const char *desc, RPC_HDR_FAULT *rpc, prs_struct *ps, int depth)
+{
+ if (rpc == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_hdr_fault");
+ depth++;
+
+ if(!prs_ntstatus("status ", ps, depth, &rpc->status))
+ return False;
+ if(!prs_uint32("reserved", ps, depth, &rpc->reserved))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Init an RPC_HDR_AUTHA structure.
+********************************************************************/
+
+void init_rpc_hdr_autha(RPC_HDR_AUTHA *rai,
+ uint16 max_tsize, uint16 max_rsize,
+ uint8 auth_type, uint8 auth_level,
+ uint8 stub_type_len)
+{
+ rai->max_tsize = max_tsize; /* maximum transmission fragment size (0x1630) */
+ rai->max_rsize = max_rsize; /* max receive fragment size (0x1630) */
+
+ rai->auth_type = auth_type; /* nt lm ssp 0x0a */
+ rai->auth_level = auth_level; /* 0x06 */
+ rai->stub_type_len = stub_type_len; /* 0x00 */
+ rai->padding = 0; /* padding 0x00 */
+
+ rai->unknown = 0x0014a0c0; /* non-zero pointer to something */
+}
+
+/*******************************************************************
+ Reads or writes an RPC_HDR_AUTHA structure.
+********************************************************************/
+
+BOOL smb_io_rpc_hdr_autha(const char *desc, RPC_HDR_AUTHA *rai, prs_struct *ps, int depth)
+{
+ if (rai == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_hdr_autha");
+ depth++;
+
+ if(!prs_uint16("max_tsize ", ps, depth, &rai->max_tsize))
+ return False;
+ if(!prs_uint16("max_rsize ", ps, depth, &rai->max_rsize))
+ return False;
+
+ if(!prs_uint8 ("auth_type ", ps, depth, &rai->auth_type)) /* 0x0a nt lm ssp */
+ return False;
+ if(!prs_uint8 ("auth_level ", ps, depth, &rai->auth_level)) /* 0x06 */
+ return False;
+ if(!prs_uint8 ("stub_type_len", ps, depth, &rai->stub_type_len))
+ return False;
+ if(!prs_uint8 ("padding ", ps, depth, &rai->padding))
+ return False;
+
+ if(!prs_uint32("unknown ", ps, depth, &rai->unknown)) /* 0x0014a0c0 */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an RPC_HDR_AUTH structure.
+********************************************************************/
+
+void init_rpc_hdr_auth(RPC_HDR_AUTH *rai,
+ uint8 auth_type, uint8 auth_level,
+ uint8 padding,
+ uint32 ptr)
+{
+ rai->auth_type = auth_type; /* nt lm ssp 0x0a */
+ rai->auth_level = auth_level; /* 0x06 */
+ rai->padding = padding;
+ rai->reserved = 0;
+
+ rai->auth_context = ptr; /* non-zero pointer to something */
+}
+
+/*******************************************************************
+ Reads or writes an RPC_HDR_AUTH structure.
+********************************************************************/
+
+BOOL smb_io_rpc_hdr_auth(const char *desc, RPC_HDR_AUTH *rai, prs_struct *ps, int depth)
+{
+ if (rai == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_hdr_auth");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint8 ("auth_type ", ps, depth, &rai->auth_type)) /* 0x0a nt lm ssp */
+ return False;
+ if(!prs_uint8 ("auth_level ", ps, depth, &rai->auth_level)) /* 0x06 */
+ return False;
+ if(!prs_uint8 ("padding ", ps, depth, &rai->padding))
+ return False;
+ if(!prs_uint8 ("reserved ", ps, depth, &rai->reserved))
+ return False;
+ if(!prs_uint32("auth_context ", ps, depth, &rai->auth_context))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Checks an RPC_AUTH_VERIFIER structure.
+********************************************************************/
+
+BOOL rpc_auth_verifier_chk(RPC_AUTH_VERIFIER *rav,
+ const char *signature, uint32 msg_type)
+{
+ return (strequal(rav->signature, signature) && rav->msg_type == msg_type);
+}
+
+/*******************************************************************
+ Inits an RPC_AUTH_VERIFIER structure.
+********************************************************************/
+
+void init_rpc_auth_verifier(RPC_AUTH_VERIFIER *rav,
+ const char *signature, uint32 msg_type)
+{
+ fstrcpy(rav->signature, signature); /* "NTLMSSP" */
+ rav->msg_type = msg_type; /* NTLMSSP_MESSAGE_TYPE */
+}
+
+/*******************************************************************
+ Reads or writes an RPC_AUTH_VERIFIER structure.
+********************************************************************/
+
+BOOL smb_io_rpc_auth_verifier(const char *desc, RPC_AUTH_VERIFIER *rav, prs_struct *ps, int depth)
+{
+ if (rav == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_auth_verifier");
+ depth++;
+
+ /* "NTLMSSP" */
+ if(!prs_string("signature", ps, depth, rav->signature,
+ sizeof(rav->signature)))
+ return False;
+ if(!prs_uint32("msg_type ", ps, depth, &rav->msg_type)) /* NTLMSSP_MESSAGE_TYPE */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ This parses an RPC_AUTH_VERIFIER for NETLOGON schannel. I think
+ assuming "NTLMSSP" in sm_io_rpc_auth_verifier is somewhat wrong.
+ I have to look at that later...
+********************************************************************/
+
+BOOL smb_io_rpc_netsec_verifier(const char *desc, RPC_AUTH_VERIFIER *rav, prs_struct *ps, int depth)
+{
+ if (rav == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_auth_verifier");
+ depth++;
+
+ if(!prs_string("signature", ps, depth, rav->signature, sizeof(rav->signature)))
+ return False;
+ if(!prs_uint32("msg_type ", ps, depth, &rav->msg_type))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an RPC_AUTH_NTLMSSP_NEG structure.
+********************************************************************/
+
+void init_rpc_auth_ntlmssp_neg(RPC_AUTH_NTLMSSP_NEG *neg,
+ uint32 neg_flgs,
+ const char *myname, const char *domain)
+{
+ int len_myname = strlen(myname);
+ int len_domain = strlen(domain);
+
+ neg->neg_flgs = neg_flgs ; /* 0x00b2b3 */
+
+ init_str_hdr(&neg->hdr_domain, len_domain, len_domain, 0x20 + len_myname);
+ init_str_hdr(&neg->hdr_myname, len_myname, len_myname, 0x20);
+
+ fstrcpy(neg->myname, myname);
+ fstrcpy(neg->domain, domain);
+}
+
+/*******************************************************************
+ Reads or writes an RPC_AUTH_NTLMSSP_NEG structure.
+
+ *** lkclXXXX HACK ALERT! ***
+********************************************************************/
+
+BOOL smb_io_rpc_auth_ntlmssp_neg(const char *desc, RPC_AUTH_NTLMSSP_NEG *neg, prs_struct *ps, int depth)
+{
+ uint32 start_offset = prs_offset(ps);
+ if (neg == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_auth_ntlmssp_neg");
+ depth++;
+
+ if(!prs_uint32("neg_flgs ", ps, depth, &neg->neg_flgs))
+ return False;
+
+ if (ps->io) {
+ uint32 old_offset;
+ uint32 old_neg_flags = neg->neg_flgs;
+
+ /* reading */
+
+ ZERO_STRUCTP(neg);
+
+ neg->neg_flgs = old_neg_flags;
+
+ if(!smb_io_strhdr("hdr_domain", &neg->hdr_domain, ps, depth))
+ return False;
+ if(!smb_io_strhdr("hdr_myname", &neg->hdr_myname, ps, depth))
+ return False;
+
+ old_offset = prs_offset(ps);
+
+ if(!prs_set_offset(ps, neg->hdr_myname.buffer + start_offset - 12))
+ return False;
+
+ if(!prs_uint8s(True, "myname", ps, depth, (uint8*)neg->myname,
+ MIN(neg->hdr_myname.str_str_len, sizeof(neg->myname))))
+ return False;
+
+ old_offset += neg->hdr_myname.str_str_len;
+
+ if(!prs_set_offset(ps, neg->hdr_domain.buffer + start_offset - 12))
+ return False;
+
+ if(!prs_uint8s(True, "domain", ps, depth, (uint8*)neg->domain,
+ MIN(neg->hdr_domain.str_str_len, sizeof(neg->domain ))))
+ return False;
+
+ old_offset += neg->hdr_domain .str_str_len;
+
+ if(!prs_set_offset(ps, old_offset))
+ return False;
+ } else {
+ /* writing */
+ if(!smb_io_strhdr("hdr_domain", &neg->hdr_domain, ps, depth))
+ return False;
+ if(!smb_io_strhdr("hdr_myname", &neg->hdr_myname, ps, depth))
+ return False;
+
+ if(!prs_uint8s(True, "myname", ps, depth, (uint8*)neg->myname,
+ MIN(neg->hdr_myname.str_str_len, sizeof(neg->myname))))
+ return False;
+ if(!prs_uint8s(True, "domain", ps, depth, (uint8*)neg->domain,
+ MIN(neg->hdr_domain.str_str_len, sizeof(neg->domain ))))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+creates an RPC_AUTH_NTLMSSP_CHAL structure.
+********************************************************************/
+
+void init_rpc_auth_ntlmssp_chal(RPC_AUTH_NTLMSSP_CHAL *chl,
+ uint32 neg_flags,
+ uint8 challenge[8])
+{
+ chl->unknown_1 = 0x0;
+ chl->unknown_2 = 0x00000028;
+ chl->neg_flags = neg_flags; /* 0x0082b1 */
+
+ memcpy(chl->challenge, challenge, sizeof(chl->challenge));
+ memset((char *)chl->reserved , '\0', sizeof(chl->reserved));
+}
+
+/*******************************************************************
+ Reads or writes an RPC_AUTH_NTLMSSP_CHAL structure.
+********************************************************************/
+
+BOOL smb_io_rpc_auth_ntlmssp_chal(const char *desc, RPC_AUTH_NTLMSSP_CHAL *chl, prs_struct *ps, int depth)
+{
+ if (chl == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_auth_ntlmssp_chal");
+ depth++;
+
+ if(!prs_uint32("unknown_1", ps, depth, &chl->unknown_1)) /* 0x0000 0000 */
+ return False;
+ if(!prs_uint32("unknown_2", ps, depth, &chl->unknown_2)) /* 0x0000 b2b3 */
+ return False;
+ if(!prs_uint32("neg_flags", ps, depth, &chl->neg_flags)) /* 0x0000 82b1 */
+ return False;
+
+ if(!prs_uint8s (False, "challenge", ps, depth, chl->challenge, sizeof(chl->challenge)))
+ return False;
+ if(!prs_uint8s (False, "reserved ", ps, depth, chl->reserved , sizeof(chl->reserved )))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an RPC_AUTH_NTLMSSP_RESP structure.
+
+ *** lkclXXXX FUDGE! HAVE TO MANUALLY SPECIFY OFFSET HERE (0x1c bytes) ***
+ *** lkclXXXX the actual offset is at the start of the auth verifier ***
+********************************************************************/
+
+void init_rpc_auth_ntlmssp_resp(RPC_AUTH_NTLMSSP_RESP *rsp,
+ uchar lm_resp[24], uchar nt_resp[24],
+ const char *domain, const char *user, const char *wks,
+ uint32 neg_flags)
+{
+ uint32 offset;
+ int dom_len = strlen(domain);
+ int wks_len = strlen(wks);
+ int usr_len = strlen(user);
+ int lm_len = (lm_resp != NULL) ? 24 : 0;
+ int nt_len = (nt_resp != NULL) ? 24 : 0;
+
+ DEBUG(5,("make_rpc_auth_ntlmssp_resp\n"));
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("lm_resp\n"));
+ dump_data(100, (char *)lm_resp, 24);
+ DEBUG(100,("nt_resp\n"));
+ dump_data(100, (char *)nt_resp, 24);
+#endif
+
+ DEBUG(6,("dom: %s user: %s wks: %s neg_flgs: 0x%x\n",
+ domain, user, wks, neg_flags));
+
+ offset = 0x40;
+
+ if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) {
+ dom_len *= 2;
+ wks_len *= 2;
+ usr_len *= 2;
+ }
+
+ init_str_hdr(&rsp->hdr_domain, dom_len, dom_len, offset);
+ offset += dom_len;
+
+ init_str_hdr(&rsp->hdr_usr, usr_len, usr_len, offset);
+ offset += usr_len;
+
+ init_str_hdr(&rsp->hdr_wks, wks_len, wks_len, offset);
+ offset += wks_len;
+
+ init_str_hdr(&rsp->hdr_lm_resp, lm_len, lm_len, offset);
+ offset += lm_len;
+
+ init_str_hdr(&rsp->hdr_nt_resp, nt_len, nt_len, offset);
+ offset += nt_len;
+
+ init_str_hdr(&rsp->hdr_sess_key, 0, 0, offset);
+
+ rsp->neg_flags = neg_flags;
+
+ memcpy(rsp->lm_resp, lm_resp, 24);
+ memcpy(rsp->nt_resp, nt_resp, 24);
+
+ if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) {
+ rpcstr_push(rsp->domain, domain, sizeof(rsp->domain), 0);
+ rpcstr_push(rsp->user, user, sizeof(rsp->user), 0);
+ rpcstr_push(rsp->wks, wks, sizeof(rsp->wks), 0);
+ } else {
+ fstrcpy(rsp->domain, domain);
+ fstrcpy(rsp->user, user);
+ fstrcpy(rsp->wks, wks);
+ }
+
+ rsp->sess_key[0] = 0;
+}
+
+/*******************************************************************
+ Reads or writes an RPC_AUTH_NTLMSSP_RESP structure.
+
+ *** lkclXXXX FUDGE! HAVE TO MANUALLY SPECIFY OFFSET HERE (0x1c bytes) ***
+ *** lkclXXXX the actual offset is at the start of the auth verifier ***
+********************************************************************/
+
+BOOL smb_io_rpc_auth_ntlmssp_resp(const char *desc, RPC_AUTH_NTLMSSP_RESP *rsp, prs_struct *ps, int depth)
+{
+ if (rsp == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_auth_ntlmssp_resp");
+ depth++;
+
+ if (ps->io) {
+ uint32 old_offset;
+
+ /* reading */
+
+ ZERO_STRUCTP(rsp);
+
+ if(!smb_io_strhdr("hdr_lm_resp ", &rsp->hdr_lm_resp, ps, depth))
+ return False;
+ if(!smb_io_strhdr("hdr_nt_resp ", &rsp->hdr_nt_resp, ps, depth))
+ return False;
+ if(!smb_io_strhdr("hdr_domain ", &rsp->hdr_domain, ps, depth))
+ return False;
+ if(!smb_io_strhdr("hdr_user ", &rsp->hdr_usr, ps, depth))
+ return False;
+ if(!smb_io_strhdr("hdr_wks ", &rsp->hdr_wks, ps, depth))
+ return False;
+ if(!smb_io_strhdr("hdr_sess_key", &rsp->hdr_sess_key, ps, depth))
+ return False;
+
+ if(!prs_uint32("neg_flags", ps, depth, &rsp->neg_flags)) /* 0x0000 82b1 */
+ return False;
+
+ old_offset = prs_offset(ps);
+
+ if(!prs_set_offset(ps, rsp->hdr_domain.buffer + 0xc))
+ return False;
+
+ if(!prs_uint8s(True , "domain ", ps, depth, (uint8*)rsp->domain,
+ MIN(rsp->hdr_domain.str_str_len, sizeof(rsp->domain))))
+ return False;
+
+ old_offset += rsp->hdr_domain.str_str_len;
+
+ if(!prs_set_offset(ps, rsp->hdr_usr.buffer + 0xc))
+ return False;
+
+ if(!prs_uint8s(True , "user ", ps, depth, (uint8*)rsp->user,
+ MIN(rsp->hdr_usr.str_str_len, sizeof(rsp->user))))
+ return False;
+
+ old_offset += rsp->hdr_usr.str_str_len;
+
+ if(!prs_set_offset(ps, rsp->hdr_wks.buffer + 0xc))
+ return False;
+
+ if(!prs_uint8s(True, "wks ", ps, depth, (uint8*)rsp->wks,
+ MIN(rsp->hdr_wks.str_str_len, sizeof(rsp->wks))))
+ return False;
+
+ old_offset += rsp->hdr_wks.str_str_len;
+
+ if(!prs_set_offset(ps, rsp->hdr_lm_resp.buffer + 0xc))
+ return False;
+
+ if(!prs_uint8s(False, "lm_resp ", ps, depth, (uint8*)rsp->lm_resp,
+ MIN(rsp->hdr_lm_resp.str_str_len, sizeof(rsp->lm_resp ))))
+ return False;
+
+ old_offset += rsp->hdr_lm_resp.str_str_len;
+
+ if(!prs_set_offset(ps, rsp->hdr_nt_resp.buffer + 0xc))
+ return False;
+
+ if(!prs_uint8s(False, "nt_resp ", ps, depth, (uint8*)rsp->nt_resp,
+ MIN(rsp->hdr_nt_resp.str_str_len, sizeof(rsp->nt_resp ))))
+ return False;
+
+ old_offset += rsp->hdr_nt_resp.str_str_len;
+
+ if (rsp->hdr_sess_key.str_str_len != 0) {
+
+ if(!prs_set_offset(ps, rsp->hdr_sess_key.buffer + 0x10))
+ return False;
+
+ old_offset += rsp->hdr_sess_key.str_str_len;
+
+ if(!prs_uint8s(False, "sess_key", ps, depth, (uint8*)rsp->sess_key,
+ MIN(rsp->hdr_sess_key.str_str_len, sizeof(rsp->sess_key))))
+ return False;
+ }
+
+ if(!prs_set_offset(ps, old_offset))
+ return False;
+ } else {
+ /* writing */
+ if(!smb_io_strhdr("hdr_lm_resp ", &rsp->hdr_lm_resp, ps, depth))
+ return False;
+ if(!smb_io_strhdr("hdr_nt_resp ", &rsp->hdr_nt_resp, ps, depth))
+ return False;
+ if(!smb_io_strhdr("hdr_domain ", &rsp->hdr_domain, ps, depth))
+ return False;
+ if(!smb_io_strhdr("hdr_user ", &rsp->hdr_usr, ps, depth))
+ return False;
+ if(!smb_io_strhdr("hdr_wks ", &rsp->hdr_wks, ps, depth))
+ return False;
+ if(!smb_io_strhdr("hdr_sess_key", &rsp->hdr_sess_key, ps, depth))
+ return False;
+
+ if(!prs_uint32("neg_flags", ps, depth, &rsp->neg_flags)) /* 0x0000 82b1 */
+ return False;
+
+ if(!prs_uint8s(True , "domain ", ps, depth, (uint8*)rsp->domain,
+ MIN(rsp->hdr_domain.str_str_len, sizeof(rsp->domain))))
+ return False;
+
+ if(!prs_uint8s(True , "user ", ps, depth, (uint8*)rsp->user,
+ MIN(rsp->hdr_usr.str_str_len, sizeof(rsp->user))))
+ return False;
+
+ if(!prs_uint8s(True , "wks ", ps, depth, (uint8*)rsp->wks,
+ MIN(rsp->hdr_wks.str_str_len, sizeof(rsp->wks))))
+ return False;
+ if(!prs_uint8s(False, "lm_resp ", ps, depth, (uint8*)rsp->lm_resp,
+ MIN(rsp->hdr_lm_resp .str_str_len, sizeof(rsp->lm_resp))))
+ return False;
+ if(!prs_uint8s(False, "nt_resp ", ps, depth, (uint8*)rsp->nt_resp,
+ MIN(rsp->hdr_nt_resp .str_str_len, sizeof(rsp->nt_resp ))))
+ return False;
+ if(!prs_uint8s(False, "sess_key", ps, depth, (uint8*)rsp->sess_key,
+ MIN(rsp->hdr_sess_key.str_str_len, sizeof(rsp->sess_key))))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Checks an RPC_AUTH_NTLMSSP_CHK structure.
+********************************************************************/
+
+BOOL rpc_auth_ntlmssp_chk(RPC_AUTH_NTLMSSP_CHK *chk, uint32 crc32, uint32 seq_num)
+{
+ if (chk == NULL)
+ return False;
+
+ if (chk->crc32 != crc32 ||
+ chk->ver != NTLMSSP_SIGN_VERSION ||
+ chk->seq_num != seq_num)
+ {
+ DEBUG(5,("verify failed - crc %x ver %x seq %d\n",
+ chk->crc32, chk->ver, chk->seq_num));
+
+ DEBUG(5,("verify expect - crc %x ver %x seq %d\n",
+ crc32, NTLMSSP_SIGN_VERSION, seq_num));
+ return False;
+ }
+ return True;
+}
+
+/*******************************************************************
+ Inits an RPC_AUTH_NTLMSSP_CHK structure.
+********************************************************************/
+
+void init_rpc_auth_ntlmssp_chk(RPC_AUTH_NTLMSSP_CHK *chk,
+ uint32 ver, uint32 crc32, uint32 seq_num)
+{
+ chk->ver = ver;
+ chk->reserved = 0x0;
+ chk->crc32 = crc32;
+ chk->seq_num = seq_num;
+}
+
+/*******************************************************************
+ Reads or writes an RPC_AUTH_NTLMSSP_CHK structure.
+********************************************************************/
+
+BOOL smb_io_rpc_auth_ntlmssp_chk(const char *desc, RPC_AUTH_NTLMSSP_CHK *chk, prs_struct *ps, int depth)
+{
+ if (chk == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_auth_ntlmssp_chk");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ver ", ps, depth, &chk->ver))
+ return False;
+ if(!prs_uint32("reserved", ps, depth, &chk->reserved))
+ return False;
+ if(!prs_uint32("crc32 ", ps, depth, &chk->crc32))
+ return False;
+ if(!prs_uint32("seq_num ", ps, depth, &chk->seq_num))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+creates an RPC_AUTH_NETSEC_NEG structure.
+********************************************************************/
+void init_rpc_auth_netsec_neg(RPC_AUTH_NETSEC_NEG *neg,
+ const char *domain, const char *myname)
+{
+ neg->type1 = 0;
+ neg->type2 = 0x3;
+ fstrcpy(neg->domain, domain);
+ fstrcpy(neg->myname, myname);
+}
+
+/*******************************************************************
+ Reads or writes an RPC_AUTH_NETSEC_NEG structure.
+********************************************************************/
+
+BOOL smb_io_rpc_auth_netsec_neg(const char *desc, RPC_AUTH_NETSEC_NEG *neg,
+ prs_struct *ps, int depth)
+{
+ if (neg == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_auth_netsec_neg");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("type1", ps, depth, &neg->type1))
+ return False;
+ if(!prs_uint32("type2", ps, depth, &neg->type2))
+ return False;
+ if(!prs_string("domain ", ps, depth, neg->domain, sizeof(neg->domain)))
+ return False;
+ if(!prs_string("myname ", ps, depth, neg->myname, sizeof(neg->myname)))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes an RPC_AUTH_NETSEC_CHK structure.
+********************************************************************/
+BOOL smb_io_rpc_auth_netsec_chk(const char *desc, RPC_AUTH_NETSEC_CHK * chk,
+ prs_struct *ps, int depth)
+{
+ if (chk == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_auth_netsec_chk");
+ depth++;
+
+ prs_uint8s(False, "sig ", ps, depth, chk->sig, sizeof(chk->sig));
+ prs_uint8s(False, "seq_num", ps, depth, chk->seq_num, sizeof(chk->seq_num));
+ prs_uint8s(False, "packet_digest", ps, depth, chk->packet_digest, sizeof(chk->packet_digest));
+ prs_uint8s(False, "confounder", ps, depth, chk->confounder, sizeof(chk->confounder));
+
+ return True;
+}
+
diff --git a/source/rpc_parse/parse_samr.c b/source/rpc_parse/parse_samr.c
new file mode 100644
index 00000000000..287dc3bd7f2
--- /dev/null
+++ b/source/rpc_parse/parse_samr.c
@@ -0,0 +1,7387 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-2000,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+ * Copyright (C) Paul Ashton 1997-2000,
+ * Copyright (C) Elrond 2000,
+ * Copyright (C) Jeremy Allison 2001,
+ * Copyright (C) Jean François Micouleau 1998-2001,
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+#include "rpc_parse.h"
+#include "nterr.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+inits a SAMR_Q_CLOSE_HND structure.
+********************************************************************/
+
+void init_samr_q_close_hnd(SAMR_Q_CLOSE_HND * q_c, POLICY_HND *hnd)
+{
+ DEBUG(5, ("init_samr_q_close_hnd\n"));
+
+ q_c->pol = *hnd;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_close_hnd(const char *desc, SAMR_Q_CLOSE_HND * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_close_hnd");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ return smb_io_pol_hnd("pol", &q_u->pol, ps, depth);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_close_hnd(const char *desc, SAMR_R_CLOSE_HND * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_close_hnd");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_u->pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_LOOKUP_DOMAIN structure.
+********************************************************************/
+
+void init_samr_q_lookup_domain(SAMR_Q_LOOKUP_DOMAIN * q_u,
+ POLICY_HND *pol, char *dom_name)
+{
+ DEBUG(5, ("init_samr_q_lookup_domain\n"));
+
+ q_u->connect_pol = *pol;
+
+ init_unistr2(&q_u->uni_domain, dom_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&q_u->hdr_domain, &q_u->uni_domain);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL samr_io_q_lookup_domain(const char *desc, SAMR_Q_LOOKUP_DOMAIN * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_lookup_domain");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("connect_pol", &q_u->connect_pol, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr("hdr_domain", &q_u->hdr_domain, ps, depth))
+ return False;
+
+ if(!smb_io_unistr2("uni_domain", &q_u->uni_domain, q_u->hdr_domain.buffer, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_LOOKUP_DOMAIN structure.
+********************************************************************/
+
+void init_samr_r_lookup_domain(SAMR_R_LOOKUP_DOMAIN * r_u,
+ DOM_SID *dom_sid, NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_lookup_domain\n"));
+
+ r_u->status = status;
+ r_u->ptr_sid = 0;
+ if (NT_STATUS_IS_OK(status)) {
+ r_u->ptr_sid = 1;
+ init_dom_sid2(&r_u->dom_sid, dom_sid);
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_lookup_domain(const char *desc, SAMR_R_LOOKUP_DOMAIN * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_lookup_domain");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr", ps, depth, &r_u->ptr_sid))
+ return False;
+
+ if (r_u->ptr_sid != 0) {
+ if(!smb_io_dom_sid2("sid", &r_u->dom_sid, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ }
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+void init_samr_q_remove_sid_foreign_domain(SAMR_Q_REMOVE_SID_FOREIGN_DOMAIN * q_u, POLICY_HND *dom_pol, DOM_SID *sid)
+{
+ DEBUG(5, ("samr_init_samr_q_remove_sid_foreign_domain\n"));
+
+ q_u->dom_pol = *dom_pol;
+ init_dom_sid2(&q_u->sid, sid);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_remove_sid_foreign_domain(const char *desc, SAMR_Q_REMOVE_SID_FOREIGN_DOMAIN * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_remove_sid_foreign_domain");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("domain_pol", &q_u->dom_pol, ps, depth))
+ return False;
+
+ if(!smb_io_dom_sid2("sid", &q_u->sid, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_remove_sid_foreign_domain(const char *desc, SAMR_R_REMOVE_SID_FOREIGN_DOMAIN * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_remove_sid_foreign_domain");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+void init_samr_q_open_domain(SAMR_Q_OPEN_DOMAIN * q_u,
+ POLICY_HND *pol, uint32 flags,
+ const DOM_SID *sid)
+{
+ DEBUG(5, ("samr_init_samr_q_open_domain\n"));
+
+ q_u->pol = *pol;
+ q_u->flags = flags;
+ init_dom_sid2(&q_u->dom_sid, sid);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_open_domain(const char *desc, SAMR_Q_OPEN_DOMAIN * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_open_domain");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_u->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("flags", ps, depth, &q_u->flags))
+ return False;
+
+ if(!smb_io_dom_sid2("sid", &q_u->dom_sid, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_open_domain(const char *desc, SAMR_R_OPEN_DOMAIN * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_open_domain");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("domain_pol", &r_u->domain_pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+void init_samr_q_get_usrdom_pwinfo(SAMR_Q_GET_USRDOM_PWINFO * q_u,
+ POLICY_HND *user_pol)
+{
+ DEBUG(5, ("samr_init_samr_q_get_usrdom_pwinfo\n"));
+
+ q_u->user_pol = *user_pol;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_get_usrdom_pwinfo(const char *desc, SAMR_Q_GET_USRDOM_PWINFO * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_get_usrdom_pwinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ return smb_io_pol_hnd("user_pol", &q_u->user_pol, ps, depth);
+}
+
+/*******************************************************************
+ Init.
+********************************************************************/
+
+void init_samr_r_get_usrdom_pwinfo(SAMR_R_GET_USRDOM_PWINFO *r_u, NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_get_usrdom_pwinfo\n"));
+
+ r_u->unknown_0 = 0x0000;
+
+ /*
+ * used to be
+ * r_u->unknown_1 = 0x0015;
+ * but for trusts.
+ */
+ r_u->unknown_1 = 0x01D1;
+ r_u->unknown_1 = 0x0015;
+
+ r_u->unknown_2 = 0x00000000;
+
+ r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_get_usrdom_pwinfo(const char *desc, SAMR_R_GET_USRDOM_PWINFO * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_get_usrdom_pwinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint16("unknown_0", ps, depth, &r_u->unknown_0))
+ return False;
+ if(!prs_uint16("unknown_1", ps, depth, &r_u->unknown_1))
+ return False;
+ if(!prs_uint32("unknown_2", ps, depth, &r_u->unknown_2))
+ return False;
+ if(!prs_ntstatus("status ", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_set_sec_obj(const char *desc, SAMR_Q_SET_SEC_OBJ * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_set_sec_obj");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_u->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("sec_info", ps, depth, &q_u->sec_info))
+ return False;
+
+ if(!sec_io_desc_buf("sec_desc", &q_u->buf, ps, depth))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+void init_samr_q_query_sec_obj(SAMR_Q_QUERY_SEC_OBJ * q_u,
+ POLICY_HND *user_pol, uint32 sec_info)
+{
+ DEBUG(5, ("samr_init_samr_q_query_sec_obj\n"));
+
+ q_u->user_pol = *user_pol;
+ q_u->sec_info = sec_info;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_query_sec_obj(const char *desc, SAMR_Q_QUERY_SEC_OBJ * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_query_sec_obj");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("user_pol", &q_u->user_pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("sec_info", ps, depth, &q_u->sec_info))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+void init_samr_q_query_dom_info(SAMR_Q_QUERY_DOMAIN_INFO * q_u,
+ POLICY_HND *domain_pol, uint16 switch_value)
+{
+ DEBUG(5, ("samr_init_samr_q_query_dom_info\n"));
+
+ q_u->domain_pol = *domain_pol;
+ q_u->switch_value = switch_value;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_query_dom_info(const char *desc, SAMR_Q_QUERY_DOMAIN_INFO * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_query_dom_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("domain_pol", &q_u->domain_pol, ps, depth))
+ return False;
+
+ if(!prs_uint16("switch_value", ps, depth, &q_u->switch_value))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+inits a structure.
+********************************************************************/
+
+void init_unk_info3(SAM_UNK_INFO_3 *u_3, NTTIME nt_logout)
+{
+ u_3->logout.low = nt_logout.low;
+ u_3->logout.high = nt_logout.high;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_unk_info3(const char *desc, SAM_UNK_INFO_3 * u_3,
+ prs_struct *ps, int depth)
+{
+ if (u_3 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_unk_info3");
+ depth++;
+
+ if(!smb_io_time("logout", &u_3->logout, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a structure.
+********************************************************************/
+
+void init_unk_info6(SAM_UNK_INFO_6 * u_6)
+{
+ u_6->unknown_0 = 0x00000000;
+ u_6->ptr_0 = 1;
+ memset(u_6->padding, 0, sizeof(u_6->padding)); /* 12 bytes zeros */
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_unk_info6(const char *desc, SAM_UNK_INFO_6 * u_6,
+ prs_struct *ps, int depth)
+{
+ if (u_6 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_unk_info6");
+ depth++;
+
+ if(!prs_uint32("unknown_0", ps, depth, &u_6->unknown_0)) /* 0x0000 0000 */
+ return False;
+ if(!prs_uint32("ptr_0", ps, depth, &u_6->ptr_0)) /* pointer to unknown structure */
+ return False;
+ if(!prs_uint8s(False, "padding", ps, depth, u_6->padding, sizeof(u_6->padding))) /* 12 bytes zeros */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a structure.
+********************************************************************/
+
+void init_unk_info7(SAM_UNK_INFO_7 * u_7)
+{
+ u_7->unknown_0 = 0x0003;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_unk_info7(const char *desc, SAM_UNK_INFO_7 * u_7,
+ prs_struct *ps, int depth)
+{
+ if (u_7 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_unk_info7");
+ depth++;
+
+ if(!prs_uint16("unknown_0", ps, depth, &u_7->unknown_0)) /* 0x0003 */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a structure.
+********************************************************************/
+
+void init_unk_info12(SAM_UNK_INFO_12 * u_12, NTTIME nt_lock_duration, NTTIME nt_reset_time, uint16 lockout)
+{
+ u_12->duration.low = nt_lock_duration.low;
+ u_12->duration.high = nt_lock_duration.high;
+ u_12->reset_count.low = nt_reset_time.low;
+ u_12->reset_count.high = nt_reset_time.high;
+
+ u_12->bad_attempt_lockout = lockout;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_unk_info12(const char *desc, SAM_UNK_INFO_12 * u_12,
+ prs_struct *ps, int depth)
+{
+ if (u_12 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_unk_info12");
+ depth++;
+
+ if(!smb_io_time("duration", &u_12->duration, ps, depth))
+ return False;
+ if(!smb_io_time("reset_count", &u_12->reset_count, ps, depth))
+ return False;
+ if(!prs_uint16("bad_attempt_lockout", ps, depth, &u_12->bad_attempt_lockout))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a structure.
+********************************************************************/
+
+void init_unk_info5(SAM_UNK_INFO_5 * u_5,const char *server)
+{
+ init_unistr2(&u_5->uni_server, server, UNI_FLAGS_NONE);
+ init_uni_hdr(&u_5->hdr_server, &u_5->uni_server);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_unk_info5(const char *desc, SAM_UNK_INFO_5 * u_5,
+ prs_struct *ps, int depth)
+{
+ if (u_5 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_unk_info5");
+ depth++;
+
+ if(!smb_io_unihdr("hdr_server", &u_5->hdr_server, ps, depth))
+ return False;
+
+ if(!smb_io_unistr2("uni_server", &u_5->uni_server, u_5->hdr_server.buffer, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a structure.
+********************************************************************/
+
+void init_unk_info2(SAM_UNK_INFO_2 * u_2,
+ const char *domain, const char *server,
+ uint32 seq_num, uint32 num_users, uint32 num_groups, uint32 num_alias)
+{
+ u_2->unknown_0 = 0x00000000;
+ u_2->unknown_1 = 0x80000000;
+ u_2->unknown_2 = 0x00000000;
+
+ u_2->ptr_0 = 1;
+
+ u_2->seq_num = seq_num;
+ u_2->unknown_3 = 0x00000000;
+
+ u_2->unknown_4 = 0x00000001;
+ u_2->unknown_5 = 0x00000003;
+ u_2->unknown_6 = 0x00000001;
+ u_2->num_domain_usrs = num_users;
+ u_2->num_domain_grps = num_groups;
+ u_2->num_local_grps = num_alias;
+
+ memset(u_2->padding, 0, sizeof(u_2->padding)); /* 12 bytes zeros */
+
+ init_unistr2(&u_2->uni_domain, domain, UNI_FLAGS_NONE);
+ init_uni_hdr(&u_2->hdr_domain, &u_2->uni_domain);
+ init_unistr2(&u_2->uni_server, server, UNI_FLAGS_NONE);
+ init_uni_hdr(&u_2->hdr_server, &u_2->uni_server);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_unk_info2(const char *desc, SAM_UNK_INFO_2 * u_2,
+ prs_struct *ps, int depth)
+{
+ if (u_2 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_unk_info2");
+ depth++;
+
+ if(!prs_uint32("unknown_0", ps, depth, &u_2->unknown_0)) /* 0x0000 0000 */
+ return False;
+ if(!prs_uint32("unknown_1", ps, depth, &u_2->unknown_1)) /* 0x8000 0000 */
+ return False;
+ if(!prs_uint32("unknown_2", ps, depth, &u_2->unknown_2)) /* 0x0000 0000 */
+ return False;
+
+ if(!prs_uint32("ptr_0", ps, depth, &u_2->ptr_0))
+ return False;
+ if(!smb_io_unihdr("hdr_domain", &u_2->hdr_domain, ps, depth))
+ return False;
+ if(!smb_io_unihdr("hdr_server", &u_2->hdr_server, ps, depth))
+ return False;
+
+ /* put all the data in here, at the moment, including what the above
+ pointer is referring to
+ */
+
+ if(!prs_uint32("seq_num ", ps, depth, &u_2->seq_num)) /* 0x0000 0099 or 0x1000 0000 */
+ return False;
+ if(!prs_uint32("unknown_3 ", ps, depth, &u_2->unknown_3)) /* 0x0000 0000 */
+ return False;
+
+ if(!prs_uint32("unknown_4 ", ps, depth, &u_2->unknown_4)) /* 0x0000 0001 */
+ return False;
+ if(!prs_uint32("unknown_5 ", ps, depth, &u_2->unknown_5)) /* 0x0000 0003 */
+ return False;
+ if(!prs_uint32("unknown_6 ", ps, depth, &u_2->unknown_6)) /* 0x0000 0001 */
+ return False;
+ if(!prs_uint32("num_domain_usrs ", ps, depth, &u_2->num_domain_usrs))
+ return False;
+ if(!prs_uint32("num_domain_grps", ps, depth, &u_2->num_domain_grps))
+ return False;
+ if(!prs_uint32("num_local_grps", ps, depth, &u_2->num_local_grps))
+ return False;
+
+ if (u_2->ptr_0) {
+ /* this was originally marked as 'padding'. It isn't
+ padding, it is some sort of optional 12 byte
+ structure. When it is present it contains zeros
+ !? */
+ if(!prs_uint8s(False, "unknown", ps, depth, u_2->padding,sizeof(u_2->padding)))
+ return False;
+ }
+
+ if(!smb_io_unistr2("uni_domain", &u_2->uni_domain, u_2->hdr_domain.buffer, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_server", &u_2->uni_server, u_2->hdr_server.buffer, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a structure.
+********************************************************************/
+
+void init_unk_info1(SAM_UNK_INFO_1 *u_1, uint16 min_pass_len, uint16 pass_hist,
+ uint32 flag, NTTIME nt_expire, NTTIME nt_min_age)
+{
+ u_1->min_length_password = min_pass_len;
+ u_1->password_history = pass_hist;
+ u_1->flag = flag;
+
+ /* password never expire */
+ u_1->expire.high = nt_expire.high;
+ u_1->expire.low = nt_expire.low;
+
+ /* can change the password now */
+ u_1->min_passwordage.high = nt_min_age.high;
+ u_1->min_passwordage.low = nt_min_age.low;
+
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_unk_info1(const char *desc, SAM_UNK_INFO_1 * u_1,
+ prs_struct *ps, int depth)
+{
+ if (u_1 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_unk_info1");
+ depth++;
+
+ if(!prs_uint16("min_length_password", ps, depth, &u_1->min_length_password))
+ return False;
+ if(!prs_uint16("password_history", ps, depth, &u_1->password_history))
+ return False;
+ if(!prs_uint32("flag", ps, depth, &u_1->flag))
+ return False;
+ if(!smb_io_time("expire", &u_1->expire, ps, depth))
+ return False;
+ if(!smb_io_time("min_passwordage", &u_1->min_passwordage, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_DOMAIN_INFO structure.
+********************************************************************/
+
+void init_samr_r_query_dom_info(SAMR_R_QUERY_DOMAIN_INFO * r_u,
+ uint16 switch_value, SAM_UNK_CTR * ctr,
+ NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_query_dom_info\n"));
+
+ r_u->ptr_0 = 0;
+ r_u->switch_value = 0;
+ r_u->status = status; /* return status */
+
+ if (NT_STATUS_IS_OK(status)) {
+ r_u->switch_value = switch_value;
+ r_u->ptr_0 = 1;
+ r_u->ctr = ctr;
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_query_dom_info(const char *desc, SAMR_R_QUERY_DOMAIN_INFO * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_query_dom_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_0 ", ps, depth, &r_u->ptr_0))
+ return False;
+
+ if (r_u->ptr_0 != 0 && r_u->ctr != NULL) {
+ if(!prs_uint16("switch_value", ps, depth, &r_u->switch_value))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ switch (r_u->switch_value) {
+ case 0x0c:
+ if(!sam_io_unk_info12("unk_inf12", &r_u->ctr->info.inf12, ps, depth))
+ return False;
+ break;
+ case 0x07:
+ if(!sam_io_unk_info7("unk_inf7",&r_u->ctr->info.inf7, ps,depth))
+ return False;
+ break;
+ case 0x06:
+ if(!sam_io_unk_info6("unk_inf6",&r_u->ctr->info.inf6, ps,depth))
+ return False;
+ break;
+ case 0x05:
+ if(!sam_io_unk_info5("unk_inf5",&r_u->ctr->info.inf5, ps,depth))
+ return False;
+ break;
+ case 0x03:
+ if(!sam_io_unk_info3("unk_inf3",&r_u->ctr->info.inf3, ps,depth))
+ return False;
+ break;
+ case 0x02:
+ if(!sam_io_unk_info2("unk_inf2",&r_u->ctr->info.inf2, ps,depth))
+ return False;
+ break;
+ case 0x01:
+ if(!sam_io_unk_info1("unk_inf1",&r_u->ctr->info.inf1, ps,depth))
+ return False;
+ break;
+ default:
+ DEBUG(0, ("samr_io_r_query_dom_info: unknown switch level 0x%x\n",
+ r_u->switch_value));
+ r_u->status = NT_STATUS_INVALID_INFO_CLASS;
+ return False;
+ }
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a SAMR_R_SET_SEC_OBJ structure.
+********************************************************************/
+
+BOOL samr_io_r_set_sec_obj(const char *desc, SAMR_R_SET_SEC_OBJ * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_set_sec_obj");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a SAMR_R_QUERY_SEC_OBJ structure.
+********************************************************************/
+
+BOOL samr_io_r_query_sec_obj(const char *desc, SAMR_R_QUERY_SEC_OBJ * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_query_sec_obj");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr", ps, depth, &r_u->ptr))
+ return False;
+ if (r_u->ptr != 0) {
+ if(!sec_io_desc_buf("sec", &r_u->buf, ps, depth))
+ return False;
+ }
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a SAM_STR1 structure.
+********************************************************************/
+
+static BOOL sam_io_sam_str1(const char *desc, SAM_STR1 * sam, uint32 acct_buf,
+ uint32 name_buf, uint32 desc_buf,
+ prs_struct *ps, int depth)
+{
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_str1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if (!smb_io_unistr2("name", &sam->uni_acct_name, acct_buf, ps, depth))
+ return False;
+
+ if (!smb_io_unistr2("desc", &sam->uni_acct_desc, desc_buf, ps, depth))
+ return False;
+
+ if (!smb_io_unistr2("full", &sam->uni_full_name, name_buf, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_ENTRY1 structure.
+********************************************************************/
+
+static void init_sam_entry1(SAM_ENTRY1 *sam, uint32 user_idx,
+ UNISTR2 *sam_name, UNISTR2 *sam_full,
+ UNISTR2 *sam_desc, uint32 rid_user,
+ uint16 acb_info)
+{
+ DEBUG(5, ("init_sam_entry1\n"));
+
+ ZERO_STRUCTP(sam);
+
+ sam->user_idx = user_idx;
+ sam->rid_user = rid_user;
+ sam->acb_info = acb_info;
+
+ init_uni_hdr(&sam->hdr_acct_name, sam_name);
+ init_uni_hdr(&sam->hdr_user_name, sam_full);
+ init_uni_hdr(&sam->hdr_user_desc, sam_desc);
+}
+
+/*******************************************************************
+reads or writes a SAM_ENTRY1 structure.
+********************************************************************/
+
+static BOOL sam_io_sam_entry1(const char *desc, SAM_ENTRY1 * sam,
+ prs_struct *ps, int depth)
+{
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_entry1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("user_idx ", ps, depth, &sam->user_idx))
+ return False;
+
+ if(!prs_uint32("rid_user ", ps, depth, &sam->rid_user))
+ return False;
+ if(!prs_uint16("acb_info ", ps, depth, &sam->acb_info))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (!smb_io_unihdr("hdr_acct_name", &sam->hdr_acct_name, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_user_desc", &sam->hdr_user_desc, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_user_name", &sam->hdr_user_name, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a SAM_STR2 structure.
+********************************************************************/
+
+static BOOL sam_io_sam_str2(const char *desc, SAM_STR2 * sam, uint32 acct_buf,
+ uint32 desc_buf, prs_struct *ps, int depth)
+{
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_str2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("uni_srv_name", &sam->uni_srv_name, acct_buf, ps, depth)) /* account name unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_srv_desc", &sam->uni_srv_desc, desc_buf, ps, depth)) /* account desc unicode string */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_ENTRY2 structure.
+********************************************************************/
+static void init_sam_entry2(SAM_ENTRY2 * sam, uint32 user_idx,
+ UNISTR2 *sam_name, UNISTR2 *sam_desc,
+ uint32 rid_user, uint16 acb_info)
+{
+ DEBUG(5, ("init_sam_entry2\n"));
+
+ sam->user_idx = user_idx;
+ sam->rid_user = rid_user;
+ sam->acb_info = acb_info;
+
+ init_uni_hdr(&sam->hdr_srv_name, sam_name);
+ init_uni_hdr(&sam->hdr_srv_desc, sam_desc);
+}
+
+/*******************************************************************
+reads or writes a SAM_ENTRY2 structure.
+********************************************************************/
+
+static BOOL sam_io_sam_entry2(const char *desc, SAM_ENTRY2 * sam,
+ prs_struct *ps, int depth)
+{
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_entry2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("user_idx ", ps, depth, &sam->user_idx))
+ return False;
+
+ if(!prs_uint32("rid_user ", ps, depth, &sam->rid_user))
+ return False;
+ if(!prs_uint16("acb_info ", ps, depth, &sam->acb_info))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unihdr("unihdr", &sam->hdr_srv_name, ps, depth)) /* account name unicode string header */
+ return False;
+ if(!smb_io_unihdr("unihdr", &sam->hdr_srv_desc, ps, depth)) /* account name unicode string header */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a SAM_STR3 structure.
+********************************************************************/
+
+static BOOL sam_io_sam_str3(const char *desc, SAM_STR3 * sam, uint32 acct_buf,
+ uint32 desc_buf, prs_struct *ps, int depth)
+{
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_str3");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("uni_grp_name", &sam->uni_grp_name, acct_buf, ps, depth)) /* account name unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_grp_desc", &sam->uni_grp_desc, desc_buf, ps, depth)) /* account desc unicode string */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_ENTRY3 structure.
+********************************************************************/
+
+static void init_sam_entry3(SAM_ENTRY3 * sam, uint32 grp_idx,
+ UNISTR2 *grp_name, UNISTR2 *grp_desc,
+ uint32 rid_grp)
+{
+ DEBUG(5, ("init_sam_entry3\n"));
+
+ sam->grp_idx = grp_idx;
+ sam->rid_grp = rid_grp;
+ sam->attr = 0x07; /* group rid attributes - gets ignored by nt 4.0 */
+
+ init_uni_hdr(&sam->hdr_grp_name, grp_name);
+ init_uni_hdr(&sam->hdr_grp_desc, grp_desc);
+}
+
+/*******************************************************************
+reads or writes a SAM_ENTRY3 structure.
+********************************************************************/
+
+static BOOL sam_io_sam_entry3(const char *desc, SAM_ENTRY3 * sam,
+ prs_struct *ps, int depth)
+{
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_entry3");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("grp_idx", ps, depth, &sam->grp_idx))
+ return False;
+
+ if(!prs_uint32("rid_grp", ps, depth, &sam->rid_grp))
+ return False;
+ if(!prs_uint32("attr ", ps, depth, &sam->attr))
+ return False;
+
+ if(!smb_io_unihdr("unihdr", &sam->hdr_grp_name, ps, depth)) /* account name unicode string header */
+ return False;
+ if(!smb_io_unihdr("unihdr", &sam->hdr_grp_desc, ps, depth)) /* account name unicode string header */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_ENTRY4 structure.
+********************************************************************/
+
+static void init_sam_entry4(SAM_ENTRY4 * sam, uint32 user_idx,
+ uint32 len_acct_name)
+{
+ DEBUG(5, ("init_sam_entry4\n"));
+
+ sam->user_idx = user_idx;
+ init_str_hdr(&sam->hdr_acct_name, len_acct_name+1, len_acct_name, len_acct_name != 0);
+}
+
+/*******************************************************************
+reads or writes a SAM_ENTRY4 structure.
+********************************************************************/
+
+static BOOL sam_io_sam_entry4(const char *desc, SAM_ENTRY4 * sam,
+ prs_struct *ps, int depth)
+{
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_entry4");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("user_idx", ps, depth, &sam->user_idx))
+ return False;
+ if(!smb_io_strhdr("strhdr", &sam->hdr_acct_name, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_ENTRY5 structure.
+********************************************************************/
+
+static void init_sam_entry5(SAM_ENTRY5 * sam, uint32 grp_idx,
+ uint32 len_grp_name)
+{
+ DEBUG(5, ("init_sam_entry5\n"));
+
+ sam->grp_idx = grp_idx;
+ init_str_hdr(&sam->hdr_grp_name, len_grp_name, len_grp_name,
+ len_grp_name != 0);
+}
+
+/*******************************************************************
+reads or writes a SAM_ENTRY5 structure.
+********************************************************************/
+
+static BOOL sam_io_sam_entry5(const char *desc, SAM_ENTRY5 * sam,
+ prs_struct *ps, int depth)
+{
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_entry5");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("grp_idx", ps, depth, &sam->grp_idx))
+ return False;
+ if(!smb_io_strhdr("strhdr", &sam->hdr_grp_name, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_ENTRY structure.
+********************************************************************/
+
+void init_sam_entry(SAM_ENTRY *sam, UNISTR2 *uni2, uint32 rid)
+{
+ DEBUG(10, ("init_sam_entry: %d\n", rid));
+
+ sam->rid = rid;
+ init_uni_hdr(&sam->hdr_name, uni2);
+}
+
+/*******************************************************************
+reads or writes a SAM_ENTRY structure.
+********************************************************************/
+
+static BOOL sam_io_sam_entry(const char *desc, SAM_ENTRY * sam,
+ prs_struct *ps, int depth)
+{
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_entry");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("rid", ps, depth, &sam->rid))
+ return False;
+ if(!smb_io_unihdr("unihdr", &sam->hdr_name, ps, depth)) /* account name unicode string header */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_ENUM_DOM_USERS structure.
+********************************************************************/
+
+void init_samr_q_enum_dom_users(SAMR_Q_ENUM_DOM_USERS * q_e, POLICY_HND *pol,
+ uint32 start_idx,
+ uint16 acb_mask, uint16 unk_1, uint32 size)
+{
+ DEBUG(5, ("init_samr_q_enum_dom_users\n"));
+
+ q_e->pol = *pol;
+
+ q_e->start_idx = start_idx; /* zero indicates lots */
+ q_e->acb_mask = acb_mask;
+ q_e->unknown_1 = unk_1;
+ q_e->max_size = size;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_enum_dom_users(const char *desc, SAMR_Q_ENUM_DOM_USERS * q_e,
+ prs_struct *ps, int depth)
+{
+ if (q_e == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_enum_dom_users");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("domain_pol", &q_e->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("start_idx", ps, depth, &q_e->start_idx))
+ return False;
+ if(!prs_uint16("acb_mask ", ps, depth, &q_e->acb_mask))
+ return False;
+ if(!prs_uint16("unknown_1", ps, depth, &q_e->unknown_1))
+ return False;
+
+ if(!prs_uint32("max_size ", ps, depth, &q_e->max_size))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+inits a SAMR_R_ENUM_DOM_USERS structure.
+********************************************************************/
+
+void init_samr_r_enum_dom_users(SAMR_R_ENUM_DOM_USERS * r_u,
+ uint32 next_idx, uint32 num_sam_entries)
+{
+ DEBUG(5, ("init_samr_r_enum_dom_users\n"));
+
+ r_u->next_idx = next_idx;
+
+ if (num_sam_entries != 0) {
+ r_u->ptr_entries1 = 1;
+ r_u->ptr_entries2 = 1;
+ r_u->num_entries2 = num_sam_entries;
+ r_u->num_entries3 = num_sam_entries;
+
+ r_u->num_entries4 = num_sam_entries;
+ } else {
+ r_u->ptr_entries1 = 0;
+ r_u->num_entries2 = num_sam_entries;
+ r_u->ptr_entries2 = 1;
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_enum_dom_users(const char *desc, SAMR_R_ENUM_DOM_USERS * r_u,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_enum_dom_users");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("next_idx ", ps, depth, &r_u->next_idx))
+ return False;
+ if(!prs_uint32("ptr_entries1", ps, depth, &r_u->ptr_entries1))
+ return False;
+
+ if (r_u->ptr_entries1 != 0) {
+ if(!prs_uint32("num_entries2", ps, depth, &r_u->num_entries2))
+ return False;
+ if(!prs_uint32("ptr_entries2", ps, depth, &r_u->ptr_entries2))
+ return False;
+ if(!prs_uint32("num_entries3", ps, depth, &r_u->num_entries3))
+ return False;
+
+ if (UNMARSHALLING(ps) && (r_u->num_entries2 != 0)) {
+ r_u->sam = (SAM_ENTRY *)prs_alloc_mem(ps,sizeof(SAM_ENTRY)*r_u->num_entries2);
+ r_u->uni_acct_name = (UNISTR2 *)prs_alloc_mem(ps,sizeof(UNISTR2)*r_u->num_entries2);
+ }
+
+ if ((r_u->sam == NULL || r_u->uni_acct_name == NULL) && r_u->num_entries2 != 0) {
+ DEBUG(0,("NULL pointers in SAMR_R_ENUM_DOM_USERS\n"));
+ r_u->num_entries4 = 0;
+ r_u->status = NT_STATUS_MEMORY_NOT_ALLOCATED;
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_entries2; i++) {
+ if(!sam_io_sam_entry("", &r_u->sam[i], ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_entries2; i++) {
+ if(!smb_io_unistr2("", &r_u->uni_acct_name[i],r_u->sam[i].hdr_name.buffer, ps,depth))
+ return False;
+ }
+
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_entries4", ps, depth, &r_u->num_entries4))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_QUERY_DISPINFO structure.
+********************************************************************/
+
+void init_samr_q_query_dispinfo(SAMR_Q_QUERY_DISPINFO * q_e, POLICY_HND *pol,
+ uint16 switch_level, uint32 start_idx,
+ uint32 max_entries, uint32 max_size)
+{
+ DEBUG(5, ("init_samr_q_query_dispinfo\n"));
+
+ q_e->domain_pol = *pol;
+
+ q_e->switch_level = switch_level;
+
+ q_e->start_idx = start_idx;
+ q_e->max_entries = max_entries;
+ q_e->max_size = max_size;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_query_dispinfo(const char *desc, SAMR_Q_QUERY_DISPINFO * q_e,
+ prs_struct *ps, int depth)
+{
+ if (q_e == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_query_dispinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("domain_pol", &q_e->domain_pol, ps, depth))
+ return False;
+
+ if(!prs_uint16("switch_level", ps, depth, &q_e->switch_level))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("start_idx ", ps, depth, &q_e->start_idx))
+ return False;
+ if(!prs_uint32("max_entries ", ps, depth, &q_e->max_entries))
+ return False;
+ if(!prs_uint32("max_size ", ps, depth, &q_e->max_size))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_DISPINFO_1 structure.
+********************************************************************/
+
+NTSTATUS init_sam_dispinfo_1(TALLOC_CTX *ctx, SAM_DISPINFO_1 *sam, uint32 num_entries,
+ uint32 start_idx, SAM_ACCOUNT *disp_user_info,
+ DOM_SID *domain_sid)
+{
+ uint32 i;
+
+ SAM_ACCOUNT *pwd = NULL;
+ ZERO_STRUCTP(sam);
+
+ DEBUG(10, ("init_sam_dispinfo_1: num_entries: %d\n", num_entries));
+
+ if (num_entries==0)
+ return NT_STATUS_OK;
+
+ sam->sam=(SAM_ENTRY1 *)talloc(ctx, num_entries*sizeof(SAM_ENTRY1));
+ if (!sam->sam)
+ return NT_STATUS_NO_MEMORY;
+
+ sam->str=(SAM_STR1 *)talloc(ctx, num_entries*sizeof(SAM_STR1));
+ if (!sam->str)
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(sam->sam);
+ ZERO_STRUCTP(sam->str);
+
+ for (i = 0; i < num_entries ; i++) {
+ const char *username;
+ const char *fullname;
+ const char *acct_desc;
+ uint32 user_rid;
+ const DOM_SID *user_sid;
+ fstring user_sid_string, domain_sid_string;
+
+ DEBUG(11, ("init_sam_dispinfo_1: entry: %d\n",i));
+
+ pwd=&disp_user_info[i+start_idx];
+
+ username = pdb_get_username(pwd);
+ fullname = pdb_get_fullname(pwd);
+ acct_desc = pdb_get_acct_desc(pwd);
+
+ if (!username)
+ username = "";
+
+ if (!fullname)
+ fullname = "";
+
+ if (!acct_desc)
+ acct_desc = "";
+
+ user_sid = pdb_get_user_sid(pwd);
+
+ if (!sid_peek_check_rid(domain_sid, user_sid, &user_rid)) {
+ DEBUG(0, ("init_sam_dispinfo_1: User %s has SID %s, which conflicts with "
+ "the domain sid %s. Failing operation.\n",
+ username,
+ sid_to_string(user_sid_string, user_sid),
+ sid_to_string(domain_sid_string, domain_sid)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ init_unistr2(&sam->str[i].uni_acct_name, pdb_get_username(pwd), UNI_FLAGS_NONE);
+ init_unistr2(&sam->str[i].uni_full_name, pdb_get_fullname(pwd), UNI_FLAGS_NONE);
+ init_unistr2(&sam->str[i].uni_acct_desc, pdb_get_acct_desc(pwd), UNI_FLAGS_NONE);
+
+ init_sam_entry1(&sam->sam[i], start_idx + i + 1,
+ &sam->str[i].uni_acct_name, &sam->str[i].uni_full_name, &sam->str[i].uni_acct_desc,
+ user_rid, pdb_get_acct_ctrl(pwd));
+
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_sam_dispinfo_1(const char *desc, SAM_DISPINFO_1 * sam,
+ uint32 num_entries,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_dispinfo_1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (UNMARSHALLING(ps) && num_entries > 0) {
+
+ if ((sam->sam = (SAM_ENTRY1 *)
+ prs_alloc_mem(ps, sizeof(SAM_ENTRY1) *
+ num_entries)) == NULL) {
+ DEBUG(0, ("out of memory allocating SAM_ENTRY1\n"));
+ return False;
+ }
+
+ if ((sam->str = (SAM_STR1 *)
+ prs_alloc_mem(ps, sizeof(SAM_STR1) *
+ num_entries)) == NULL) {
+ DEBUG(0, ("out of memory allocating SAM_STR1\n"));
+ return False;
+ }
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!sam_io_sam_entry1("", &sam->sam[i], ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!sam_io_sam_str1("", &sam->str[i],
+ sam->sam[i].hdr_acct_name.buffer,
+ sam->sam[i].hdr_user_name.buffer,
+ sam->sam[i].hdr_user_desc.buffer, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_DISPINFO_2 structure.
+********************************************************************/
+
+NTSTATUS init_sam_dispinfo_2(TALLOC_CTX *ctx, SAM_DISPINFO_2 *sam, uint32 num_entries,
+ uint32 start_idx, SAM_ACCOUNT *disp_user_info,
+ DOM_SID *domain_sid )
+{
+ uint32 i;
+
+ SAM_ACCOUNT *pwd = NULL;
+ ZERO_STRUCTP(sam);
+
+ DEBUG(10, ("init_sam_dispinfo_2: num_entries: %d\n", num_entries));
+
+ if (num_entries==0)
+ return NT_STATUS_OK;
+
+ if (!(sam->sam=(SAM_ENTRY2 *)talloc(ctx, num_entries*sizeof(SAM_ENTRY2))))
+ return NT_STATUS_NO_MEMORY;
+
+ if (!(sam->str=(SAM_STR2 *)talloc(ctx, num_entries*sizeof(SAM_STR2))))
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(sam->sam);
+ ZERO_STRUCTP(sam->str);
+
+ for (i = 0; i < num_entries; i++) {
+ uint32 user_rid;
+ const DOM_SID *user_sid;
+ const char *username;
+ const char *acct_desc;
+ fstring user_sid_string, domain_sid_string;
+
+ DEBUG(11, ("init_sam_dispinfo_2: entry: %d\n",i));
+ pwd=&disp_user_info[i+start_idx];
+
+ username = pdb_get_username(pwd);
+ acct_desc = pdb_get_acct_desc(pwd);
+ user_sid = pdb_get_user_sid(pwd);
+
+ if (!sid_peek_check_rid(domain_sid, user_sid, &user_rid)) {
+ DEBUG(0, ("init_sam_dispinfo_2: User %s has SID %s, which conflicts with "
+ "the domain sid %s. Failing operation.\n",
+ username,
+ sid_to_string(user_sid_string, user_sid),
+ sid_to_string(domain_sid_string, domain_sid)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ init_unistr2(&sam->str[i].uni_srv_name, username, UNI_FLAGS_NONE);
+ init_unistr2(&sam->str[i].uni_srv_desc, pdb_get_acct_desc(pwd), UNI_FLAGS_NONE);
+
+ init_sam_entry2(&sam->sam[i], start_idx + i + 1,
+ &sam->str[i].uni_srv_name, &sam->str[i].uni_srv_desc,
+ user_rid, pdb_get_acct_ctrl(pwd));
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_sam_dispinfo_2(const char *desc, SAM_DISPINFO_2 * sam,
+ uint32 num_entries,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_dispinfo_2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (UNMARSHALLING(ps) && num_entries > 0) {
+
+ if ((sam->sam = (SAM_ENTRY2 *)
+ prs_alloc_mem(ps, sizeof(SAM_ENTRY2) *
+ num_entries)) == NULL) {
+ DEBUG(0, ("out of memory allocating SAM_ENTRY2\n"));
+ return False;
+ }
+
+ if ((sam->str = (SAM_STR2 *)
+ prs_alloc_mem(ps, sizeof(SAM_STR2) *
+ num_entries)) == NULL) {
+ DEBUG(0, ("out of memory allocating SAM_STR2\n"));
+ return False;
+ }
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!sam_io_sam_entry2("", &sam->sam[i], ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!sam_io_sam_str2("", &sam->str[i],
+ sam->sam[i].hdr_srv_name.buffer,
+ sam->sam[i].hdr_srv_desc.buffer, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_DISPINFO_3 structure.
+********************************************************************/
+
+NTSTATUS init_sam_dispinfo_3(TALLOC_CTX *ctx, SAM_DISPINFO_3 *sam, uint32 num_entries,
+ uint32 start_idx, DOMAIN_GRP *disp_group_info)
+{
+ uint32 i;
+
+ ZERO_STRUCTP(sam);
+
+ DEBUG(5, ("init_sam_dispinfo_3: num_entries: %d\n", num_entries));
+
+ if (num_entries==0)
+ return NT_STATUS_OK;
+
+ if (!(sam->sam=(SAM_ENTRY3 *)talloc(ctx, num_entries*sizeof(SAM_ENTRY3))))
+ return NT_STATUS_NO_MEMORY;
+
+ if (!(sam->str=(SAM_STR3 *)talloc(ctx, num_entries*sizeof(SAM_STR3))))
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(sam->sam);
+ ZERO_STRUCTP(sam->str);
+
+ for (i = 0; i < num_entries; i++) {
+ DOMAIN_GRP *grp = &disp_group_info[i+start_idx];
+
+ DEBUG(11, ("init_sam_dispinfo_3: entry: %d\n",i));
+
+ init_unistr2(&sam->str[i].uni_grp_name, grp->name, UNI_FLAGS_NONE);
+ init_unistr2(&sam->str[i].uni_grp_desc, grp->comment, UNI_FLAGS_NONE);
+
+ init_sam_entry3(&sam->sam[i], start_idx + i + 1, &sam->str[i].uni_grp_name,
+ &sam->str[i].uni_grp_desc, grp->rid);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_sam_dispinfo_3(const char *desc, SAM_DISPINFO_3 * sam,
+ uint32 num_entries,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_dispinfo_3");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (UNMARSHALLING(ps) && num_entries > 0) {
+
+ if ((sam->sam = (SAM_ENTRY3 *)
+ prs_alloc_mem(ps, sizeof(SAM_ENTRY3) *
+ num_entries)) == NULL) {
+ DEBUG(0, ("out of memory allocating SAM_ENTRY3\n"));
+ return False;
+ }
+
+ if ((sam->str = (SAM_STR3 *)
+ prs_alloc_mem(ps, sizeof(SAM_STR3) *
+ num_entries)) == NULL) {
+ DEBUG(0, ("out of memory allocating SAM_STR3\n"));
+ return False;
+ }
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!sam_io_sam_entry3("", &sam->sam[i], ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!sam_io_sam_str3("", &sam->str[i],
+ sam->sam[i].hdr_grp_name.buffer,
+ sam->sam[i].hdr_grp_desc.buffer, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_DISPINFO_4 structure.
+********************************************************************/
+
+NTSTATUS init_sam_dispinfo_4(TALLOC_CTX *ctx, SAM_DISPINFO_4 *sam, uint32 num_entries,
+ uint32 start_idx, SAM_ACCOUNT *disp_user_info)
+{
+ uint32 len_sam_name;
+ uint32 i;
+
+ SAM_ACCOUNT *pwd = NULL;
+ ZERO_STRUCTP(sam);
+
+ DEBUG(5, ("init_sam_dispinfo_4: num_entries: %d\n", num_entries));
+
+ if (num_entries==0)
+ return NT_STATUS_OK;
+
+ if (!(sam->sam=(SAM_ENTRY4 *)talloc(ctx, num_entries*sizeof(SAM_ENTRY4))))
+ return NT_STATUS_NO_MEMORY;
+
+ if (!(sam->str=(SAM_STR4 *)talloc(ctx, num_entries*sizeof(SAM_STR4))))
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(sam->sam);
+ ZERO_STRUCTP(sam->str);
+
+ for (i = 0; i < num_entries; i++) {
+ DEBUG(11, ("init_sam_dispinfo_2: entry: %d\n",i));
+ pwd=&disp_user_info[i+start_idx];
+
+ len_sam_name = strlen(pdb_get_username(pwd));
+
+ init_sam_entry4(&sam->sam[i], start_idx + i + 1, len_sam_name);
+
+ init_string2(&sam->str[i].acct_name, pdb_get_username(pwd), len_sam_name+1, len_sam_name);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_sam_dispinfo_4(const char *desc, SAM_DISPINFO_4 * sam,
+ uint32 num_entries,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_dispinfo_4");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (UNMARSHALLING(ps) && num_entries > 0) {
+
+ if ((sam->sam = (SAM_ENTRY4 *)
+ prs_alloc_mem(ps, sizeof(SAM_ENTRY4) *
+ num_entries)) == NULL) {
+ DEBUG(0, ("out of memory allocating SAM_ENTRY4\n"));
+ return False;
+ }
+
+ if ((sam->str = (SAM_STR4 *)
+ prs_alloc_mem(ps, sizeof(SAM_STR4) *
+ num_entries)) == NULL) {
+ DEBUG(0, ("out of memory allocating SAM_STR4\n"));
+ return False;
+ }
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!sam_io_sam_entry4("", &sam->sam[i], ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!smb_io_string2("acct_name", &sam->str[i].acct_name,
+ sam->sam[i].hdr_acct_name.buffer, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_DISPINFO_5 structure.
+********************************************************************/
+
+NTSTATUS init_sam_dispinfo_5(TALLOC_CTX *ctx, SAM_DISPINFO_5 *sam, uint32 num_entries,
+ uint32 start_idx, DOMAIN_GRP *disp_group_info)
+{
+ uint32 len_sam_name;
+ uint32 i;
+
+ ZERO_STRUCTP(sam);
+
+ DEBUG(5, ("init_sam_dispinfo_5: num_entries: %d\n", num_entries));
+
+ if (num_entries==0)
+ return NT_STATUS_OK;
+
+ if (!(sam->sam=(SAM_ENTRY5 *)talloc(ctx, num_entries*sizeof(SAM_ENTRY5))))
+ return NT_STATUS_NO_MEMORY;
+
+ if (!(sam->str=(SAM_STR5 *)talloc(ctx, num_entries*sizeof(SAM_STR5))))
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(sam->sam);
+ ZERO_STRUCTP(sam->str);
+
+ for (i = 0; i < num_entries; i++) {
+ DOMAIN_GRP *grp = &disp_group_info[i+start_idx];
+
+ DEBUG(11, ("init_sam_dispinfo_5: entry: %d\n",i));
+
+ len_sam_name = strlen(grp->name);
+
+ init_sam_entry5(&sam->sam[i], start_idx + i + 1, len_sam_name);
+ init_string2(&sam->str[i].grp_name, grp->name, len_sam_name+1, len_sam_name);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_sam_dispinfo_5(const char *desc, SAM_DISPINFO_5 * sam,
+ uint32 num_entries,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_dispinfo_5");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (UNMARSHALLING(ps) && num_entries > 0) {
+
+ if ((sam->sam = (SAM_ENTRY5 *)
+ prs_alloc_mem(ps, sizeof(SAM_ENTRY5) *
+ num_entries)) == NULL) {
+ DEBUG(0, ("out of memory allocating SAM_ENTRY5\n"));
+ return False;
+ }
+
+ if ((sam->str = (SAM_STR5 *)
+ prs_alloc_mem(ps, sizeof(SAM_STR5) *
+ num_entries)) == NULL) {
+ DEBUG(0, ("out of memory allocating SAM_STR5\n"));
+ return False;
+ }
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!sam_io_sam_entry5("", &sam->sam[i], ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!smb_io_string2("grp_name", &sam->str[i].grp_name,
+ sam->sam[i].hdr_grp_name.buffer, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_DISPINFO structure.
+********************************************************************/
+
+void init_samr_r_query_dispinfo(SAMR_R_QUERY_DISPINFO * r_u,
+ uint32 num_entries, uint32 total_size, uint32 data_size,
+ uint16 switch_level, SAM_DISPINFO_CTR * ctr,
+ NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_query_dispinfo: level %d\n", switch_level));
+
+ r_u->total_size = total_size;
+
+ r_u->data_size = data_size;
+
+ r_u->switch_level = switch_level;
+ r_u->num_entries = num_entries;
+
+ if (num_entries==0)
+ r_u->ptr_entries = 0;
+ else
+ r_u->ptr_entries = 1;
+
+ r_u->num_entries2 = num_entries;
+ r_u->ctr = ctr;
+
+ r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_query_dispinfo(const char *desc, SAMR_R_QUERY_DISPINFO * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_query_dispinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("total_size ", ps, depth, &r_u->total_size))
+ return False;
+ if(!prs_uint32("data_size ", ps, depth, &r_u->data_size))
+ return False;
+ if(!prs_uint16("switch_level", ps, depth, &r_u->switch_level))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_entries ", ps, depth, &r_u->num_entries))
+ return False;
+ if(!prs_uint32("ptr_entries ", ps, depth, &r_u->ptr_entries))
+ return False;
+
+ if (r_u->ptr_entries==0) {
+ if(!prs_align(ps))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+ }
+
+ if(!prs_uint32("num_entries2", ps, depth, &r_u->num_entries2))
+ return False;
+
+ switch (r_u->switch_level) {
+ case 0x1:
+ if(!sam_io_sam_dispinfo_1("users", r_u->ctr->sam.info1,
+ r_u->num_entries, ps, depth))
+ return False;
+ break;
+ case 0x2:
+ if(!sam_io_sam_dispinfo_2("servers", r_u->ctr->sam.info2,
+ r_u->num_entries, ps, depth))
+ return False;
+ break;
+ case 0x3:
+ if(!sam_io_sam_dispinfo_3("groups", r_u->ctr->sam.info3,
+ r_u->num_entries, ps, depth))
+ return False;
+ break;
+ case 0x4:
+ if(!sam_io_sam_dispinfo_4("user list",
+ r_u->ctr->sam.info4,
+ r_u->num_entries, ps, depth))
+ return False;
+ break;
+ case 0x5:
+ if(!sam_io_sam_dispinfo_5("group list",
+ r_u->ctr->sam.info5,
+ r_u->num_entries, ps, depth))
+ return False;
+ break;
+ default:
+ DEBUG(0,("samr_io_r_query_dispinfo: unknown switch value\n"));
+ break;
+ }
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_OPEN_GROUP structure.
+********************************************************************/
+
+void init_samr_q_open_group(SAMR_Q_OPEN_GROUP * q_c,
+ POLICY_HND *hnd,
+ uint32 access_mask, uint32 rid)
+{
+ DEBUG(5, ("init_samr_q_open_group\n"));
+
+ q_c->domain_pol = *hnd;
+ q_c->access_mask = access_mask;
+ q_c->rid_group = rid;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_open_group(const char *desc, SAMR_Q_OPEN_GROUP * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_open_group");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("domain_pol", &q_u->domain_pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("access_mask", ps, depth, &q_u->access_mask))
+ return False;
+ if(!prs_uint32("rid_group", ps, depth, &q_u->rid_group))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_open_group(const char *desc, SAMR_R_OPEN_GROUP * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_open_group");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_u->pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a GROUP_INFO1 structure.
+********************************************************************/
+
+void init_samr_group_info1(GROUP_INFO1 * gr1,
+ char *acct_name, char *acct_desc,
+ uint32 num_members)
+{
+ DEBUG(5, ("init_samr_group_info1\n"));
+
+ gr1->unknown_1 = 0x3;
+ gr1->num_members = num_members;
+
+ init_unistr2(&gr1->uni_acct_name, acct_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&gr1->hdr_acct_name, &gr1->uni_acct_name);
+ init_unistr2(&gr1->uni_acct_desc, acct_desc, UNI_FLAGS_NONE);
+ init_uni_hdr(&gr1->hdr_acct_desc, &gr1->uni_acct_desc);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_group_info1(const char *desc, GROUP_INFO1 * gr1,
+ prs_struct *ps, int depth)
+{
+ if (gr1 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_group_info1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unihdr("hdr_acct_name", &gr1->hdr_acct_name, ps, depth))
+ return False;
+
+ if(!prs_uint32("unknown_1", ps, depth, &gr1->unknown_1))
+ return False;
+ if(!prs_uint32("num_members", ps, depth, &gr1->num_members))
+ return False;
+
+ if(!smb_io_unihdr("hdr_acct_desc", &gr1->hdr_acct_desc, ps, depth))
+ return False;
+
+ if(!smb_io_unistr2("uni_acct_name", &gr1->uni_acct_name,
+ gr1->hdr_acct_name.buffer, ps, depth))
+ return False;
+
+ if(!smb_io_unistr2("uni_acct_desc", &gr1->uni_acct_desc,
+ gr1->hdr_acct_desc.buffer, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a GROUP_INFO3 structure.
+********************************************************************/
+
+void init_samr_group_info3(GROUP_INFO3 *gr3)
+{
+ DEBUG(5, ("init_samr_group_info3\n"));
+
+ gr3->unknown_1 = 0x3;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_group_info3(const char *desc, GROUP_INFO3 *gr3, prs_struct *ps, int depth)
+{
+ if (gr3 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_group_info3");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("unknown_1", ps, depth, &gr3->unknown_1))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a GROUP_INFO4 structure.
+********************************************************************/
+
+void init_samr_group_info4(GROUP_INFO4 * gr4, const char *acct_desc)
+{
+ DEBUG(5, ("init_samr_group_info4\n"));
+
+ gr4->level = 4;
+ init_unistr2(&gr4->uni_acct_desc, acct_desc, UNI_FLAGS_NONE);
+ init_uni_hdr(&gr4->hdr_acct_desc, &gr4->uni_acct_desc);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_group_info4(const char *desc, GROUP_INFO4 * gr4,
+ prs_struct *ps, int depth)
+{
+ if (gr4 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_group_info4");
+ depth++;
+
+ if(!prs_uint16("hdr_level", ps, depth, &gr4->level))
+ return False;
+ if(!smb_io_unihdr("hdr_acct_desc", &gr4->hdr_acct_desc, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_acct_desc", &gr4->uni_acct_desc,
+ gr4->hdr_acct_desc.buffer, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL samr_group_info_ctr(const char *desc, GROUP_INFO_CTR **ctr,
+ prs_struct *ps, int depth)
+{
+ if (UNMARSHALLING(ps))
+ *ctr = (GROUP_INFO_CTR *)prs_alloc_mem(ps,sizeof(GROUP_INFO_CTR));
+
+ if (*ctr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_group_info_ctr");
+ depth++;
+
+ if(!prs_uint16("switch_value1", ps, depth, &(*ctr)->switch_value1))
+ return False;
+
+ switch ((*ctr)->switch_value1) {
+ case 1:
+ if(!samr_io_group_info1("group_info1", &(*ctr)->group.info1, ps, depth))
+ return False;
+ break;
+ case 3:
+ if(!samr_io_group_info3("group_info3", &(*ctr)->group.info3, ps, depth))
+ return False;
+ break;
+ case 4:
+ if(!samr_io_group_info4("group_info4", &(*ctr)->group.info4, ps, depth))
+ return False;
+ break;
+ default:
+ DEBUG(0,("samr_group_info_ctr: unsupported switch level\n"));
+ break;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_CREATE_DOM_GROUP structure.
+********************************************************************/
+
+void init_samr_q_create_dom_group(SAMR_Q_CREATE_DOM_GROUP * q_e,
+ POLICY_HND *pol, const char *acct_desc,
+ uint32 access_mask)
+{
+ DEBUG(5, ("init_samr_q_create_dom_group\n"));
+
+ q_e->pol = *pol;
+
+ init_unistr2(&q_e->uni_acct_desc, acct_desc, UNI_FLAGS_NONE);
+ init_uni_hdr(&q_e->hdr_acct_desc, &q_e->uni_acct_desc);
+
+ q_e->access_mask = access_mask;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_create_dom_group(const char *desc, SAMR_Q_CREATE_DOM_GROUP * q_e,
+ prs_struct *ps, int depth)
+{
+ if (q_e == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_create_dom_group");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_e->pol, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr("hdr_acct_desc", &q_e->hdr_acct_desc, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_acct_desc", &q_e->uni_acct_desc,
+ q_e->hdr_acct_desc.buffer, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("access", ps, depth, &q_e->access_mask))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_create_dom_group(const char *desc, SAMR_R_CREATE_DOM_GROUP * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_create_dom_group");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_u->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("rid ", ps, depth, &r_u->rid))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_DELETE_DOM_GROUP structure.
+********************************************************************/
+
+void init_samr_q_delete_dom_group(SAMR_Q_DELETE_DOM_GROUP * q_c,
+ POLICY_HND *hnd)
+{
+ DEBUG(5, ("init_samr_q_delete_dom_group\n"));
+
+ q_c->group_pol = *hnd;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_delete_dom_group(const char *desc, SAMR_Q_DELETE_DOM_GROUP * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_delete_dom_group");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("group_pol", &q_u->group_pol, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_delete_dom_group(const char *desc, SAMR_R_DELETE_DOM_GROUP * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_delete_dom_group");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_u->pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_DEL_GROUPMEM structure.
+********************************************************************/
+
+void init_samr_q_del_groupmem(SAMR_Q_DEL_GROUPMEM * q_e,
+ POLICY_HND *pol, uint32 rid)
+{
+ DEBUG(5, ("init_samr_q_del_groupmem\n"));
+
+ q_e->pol = *pol;
+ q_e->rid = rid;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_del_groupmem(const char *desc, SAMR_Q_DEL_GROUPMEM * q_e,
+ prs_struct *ps, int depth)
+{
+ if (q_e == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_del_groupmem");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_e->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("rid", ps, depth, &q_e->rid))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_DEL_GROUPMEM structure.
+********************************************************************/
+
+void init_samr_r_del_groupmem(SAMR_R_DEL_GROUPMEM * r_u, POLICY_HND *pol,
+ NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_del_groupmem\n"));
+
+ r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_del_groupmem(const char *desc, SAMR_R_DEL_GROUPMEM * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_del_groupmem");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_ADD_GROUPMEM structure.
+********************************************************************/
+
+void init_samr_q_add_groupmem(SAMR_Q_ADD_GROUPMEM * q_e,
+ POLICY_HND *pol, uint32 rid)
+{
+ DEBUG(5, ("init_samr_q_add_groupmem\n"));
+
+ q_e->pol = *pol;
+ q_e->rid = rid;
+ q_e->unknown = 0x0005;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_add_groupmem(const char *desc, SAMR_Q_ADD_GROUPMEM * q_e,
+ prs_struct *ps, int depth)
+{
+ if (q_e == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_add_groupmem");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_e->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("rid ", ps, depth, &q_e->rid))
+ return False;
+ if(!prs_uint32("unknown", ps, depth, &q_e->unknown))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_ADD_GROUPMEM structure.
+********************************************************************/
+
+void init_samr_r_add_groupmem(SAMR_R_ADD_GROUPMEM * r_u, POLICY_HND *pol,
+ NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_add_groupmem\n"));
+
+ r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_add_groupmem(const char *desc, SAMR_R_ADD_GROUPMEM * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_add_groupmem");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_SET_GROUPINFO structure.
+********************************************************************/
+
+void init_samr_q_set_groupinfo(SAMR_Q_SET_GROUPINFO * q_e,
+ POLICY_HND *pol, GROUP_INFO_CTR * ctr)
+{
+ DEBUG(5, ("init_samr_q_set_groupinfo\n"));
+
+ q_e->pol = *pol;
+ q_e->ctr = ctr;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_set_groupinfo(const char *desc, SAMR_Q_SET_GROUPINFO * q_e,
+ prs_struct *ps, int depth)
+{
+ if (q_e == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_set_groupinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_e->pol, ps, depth))
+ return False;
+
+ if(!samr_group_info_ctr("ctr", &q_e->ctr, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_SET_GROUPINFO structure.
+********************************************************************/
+
+void init_samr_r_set_groupinfo(SAMR_R_SET_GROUPINFO * r_u, NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_set_groupinfo\n"));
+
+ r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_set_groupinfo(const char *desc, SAMR_R_SET_GROUPINFO * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_set_groupinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_QUERY_GROUPINFO structure.
+********************************************************************/
+
+void init_samr_q_query_groupinfo(SAMR_Q_QUERY_GROUPINFO * q_e,
+ POLICY_HND *pol, uint16 switch_level)
+{
+ DEBUG(5, ("init_samr_q_query_groupinfo\n"));
+
+ q_e->pol = *pol;
+
+ q_e->switch_level = switch_level;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_query_groupinfo(const char *desc, SAMR_Q_QUERY_GROUPINFO * q_e,
+ prs_struct *ps, int depth)
+{
+ if (q_e == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_query_groupinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_e->pol, ps, depth))
+ return False;
+
+ if(!prs_uint16("switch_level", ps, depth, &q_e->switch_level))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_GROUPINFO structure.
+********************************************************************/
+
+void init_samr_r_query_groupinfo(SAMR_R_QUERY_GROUPINFO * r_u,
+ GROUP_INFO_CTR * ctr, NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_query_groupinfo\n"));
+
+ r_u->ptr = (NT_STATUS_IS_OK(status) && ctr != NULL) ? 1 : 0;
+ r_u->ctr = ctr;
+ r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_query_groupinfo(const char *desc, SAMR_R_QUERY_GROUPINFO * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_query_groupinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr", ps, depth, &r_u->ptr))
+ return False;
+
+ if (r_u->ptr != 0) {
+ if(!samr_group_info_ctr("ctr", &r_u->ctr, ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_QUERY_GROUPMEM structure.
+********************************************************************/
+
+void init_samr_q_query_groupmem(SAMR_Q_QUERY_GROUPMEM * q_c, POLICY_HND *hnd)
+{
+ DEBUG(5, ("init_samr_q_query_groupmem\n"));
+
+ q_c->group_pol = *hnd;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_query_groupmem(const char *desc, SAMR_Q_QUERY_GROUPMEM * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_query_groupmem");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("group_pol", &q_u->group_pol, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_GROUPMEM structure.
+********************************************************************/
+
+void init_samr_r_query_groupmem(SAMR_R_QUERY_GROUPMEM * r_u,
+ uint32 num_entries, uint32 *rid,
+ uint32 *attr, NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_query_groupmem\n"));
+
+ if (NT_STATUS_IS_OK(status)) {
+ r_u->ptr = 1;
+ r_u->num_entries = num_entries;
+
+ r_u->ptr_attrs = attr != NULL ? 1 : 0;
+ r_u->ptr_rids = rid != NULL ? 1 : 0;
+
+ r_u->num_rids = num_entries;
+ r_u->rid = rid;
+
+ r_u->num_attrs = num_entries;
+ r_u->attr = attr;
+ } else {
+ r_u->ptr = 0;
+ r_u->num_entries = 0;
+ }
+
+ r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_query_groupmem(const char *desc, SAMR_R_QUERY_GROUPMEM * r_u,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ if (r_u == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps))
+ ZERO_STRUCTP(r_u);
+
+ prs_debug(ps, depth, desc, "samr_io_r_query_groupmem");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr", ps, depth, &r_u->ptr))
+ return False;
+ if(!prs_uint32("num_entries ", ps, depth, &r_u->num_entries))
+ return False;
+
+ if (r_u->ptr != 0) {
+ if(!prs_uint32("ptr_rids ", ps, depth, &r_u->ptr_rids))
+ return False;
+ if(!prs_uint32("ptr_attrs", ps, depth, &r_u->ptr_attrs))
+ return False;
+
+ if (r_u->ptr_rids != 0) {
+ if(!prs_uint32("num_rids", ps, depth, &r_u->num_rids))
+ return False;
+ if (UNMARSHALLING(ps) && r_u->num_rids != 0) {
+ r_u->rid = (uint32 *)prs_alloc_mem(ps,sizeof(r_u->rid[0])*r_u->num_rids);
+ if (r_u->rid == NULL)
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_rids; i++) {
+ if(!prs_uint32("", ps, depth, &r_u->rid[i]))
+ return False;
+ }
+ }
+
+ if (r_u->ptr_attrs != 0) {
+ if(!prs_uint32("num_attrs", ps, depth, &r_u->num_attrs))
+ return False;
+
+ if (UNMARSHALLING(ps) && r_u->num_attrs != 0) {
+ r_u->attr = (uint32 *)prs_alloc_mem(ps,sizeof(r_u->attr[0])*r_u->num_attrs);
+ if (r_u->attr == NULL)
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_attrs; i++) {
+ if(!prs_uint32("", ps, depth, &r_u->attr[i]))
+ return False;
+ }
+ }
+ }
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_QUERY_USERGROUPS structure.
+********************************************************************/
+
+void init_samr_q_query_usergroups(SAMR_Q_QUERY_USERGROUPS * q_u,
+ POLICY_HND *hnd)
+{
+ DEBUG(5, ("init_samr_q_query_usergroups\n"));
+
+ q_u->pol = *hnd;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_query_usergroups(const char *desc, SAMR_Q_QUERY_USERGROUPS * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_query_usergroups");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_u->pol, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_USERGROUPS structure.
+********************************************************************/
+
+void init_samr_r_query_usergroups(SAMR_R_QUERY_USERGROUPS * r_u,
+ uint32 num_gids, DOM_GID * gid,
+ NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_query_usergroups\n"));
+
+ if (NT_STATUS_IS_OK(status)) {
+ r_u->ptr_0 = 1;
+ r_u->num_entries = num_gids;
+ r_u->ptr_1 = (num_gids != 0) ? 1 : 0;
+ r_u->num_entries2 = num_gids;
+
+ r_u->gid = gid;
+ } else {
+ r_u->ptr_0 = 0;
+ r_u->num_entries = 0;
+ r_u->ptr_1 = 0;
+ r_u->gid = NULL;
+ }
+
+ r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_gids(const char *desc, uint32 *num_gids, DOM_GID ** gid,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+ if (gid == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_gids");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_gids", ps, depth, num_gids))
+ return False;
+
+ if ((*num_gids) != 0) {
+ if (UNMARSHALLING(ps)) {
+ (*gid) = (DOM_GID *)prs_alloc_mem(ps,sizeof(DOM_GID)*(*num_gids));
+ }
+
+ if ((*gid) == NULL) {
+ return False;
+ }
+
+ for (i = 0; i < (*num_gids); i++) {
+ if(!smb_io_gid("gids", &(*gid)[i], ps, depth))
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_query_usergroups(const char *desc, SAMR_R_QUERY_USERGROUPS * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_query_usergroups");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_0 ", ps, depth, &r_u->ptr_0))
+ return False;
+
+ if (r_u->ptr_0 != 0) {
+ if(!prs_uint32("num_entries ", ps, depth, &r_u->num_entries))
+ return False;
+ if(!prs_uint32("ptr_1 ", ps, depth, &r_u->ptr_1))
+ return False;
+
+ if (r_u->num_entries != 0 && r_u->ptr_1 != 0) {
+ if(!samr_io_gids("gids", &r_u->num_entries2, &r_u->gid, ps, depth))
+ return False;
+ }
+ }
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_ENUM_DOMAINS structure.
+********************************************************************/
+
+void init_samr_q_enum_domains(SAMR_Q_ENUM_DOMAINS * q_e,
+ POLICY_HND *pol,
+ uint32 start_idx, uint32 size)
+{
+ DEBUG(5, ("init_samr_q_enum_domains\n"));
+
+ q_e->pol = *pol;
+
+ q_e->start_idx = start_idx;
+ q_e->max_size = size;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_enum_domains(const char *desc, SAMR_Q_ENUM_DOMAINS * q_e,
+ prs_struct *ps, int depth)
+{
+ if (q_e == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_enum_domains");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_e->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("start_idx", ps, depth, &q_e->start_idx))
+ return False;
+ if(!prs_uint32("max_size ", ps, depth, &q_e->max_size))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_ENUM_DOMAINS structure.
+********************************************************************/
+
+void init_samr_r_enum_domains(SAMR_R_ENUM_DOMAINS * r_u,
+ uint32 next_idx, uint32 num_sam_entries)
+{
+ DEBUG(5, ("init_samr_r_enum_domains\n"));
+
+ r_u->next_idx = next_idx;
+
+ if (num_sam_entries != 0) {
+ r_u->ptr_entries1 = 1;
+ r_u->ptr_entries2 = 1;
+ r_u->num_entries2 = num_sam_entries;
+ r_u->num_entries3 = num_sam_entries;
+
+ r_u->num_entries4 = num_sam_entries;
+ } else {
+ r_u->ptr_entries1 = 0;
+ r_u->num_entries2 = num_sam_entries;
+ r_u->ptr_entries2 = 1;
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_enum_domains(const char *desc, SAMR_R_ENUM_DOMAINS * r_u,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_enum_domains");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("next_idx ", ps, depth, &r_u->next_idx))
+ return False;
+ if(!prs_uint32("ptr_entries1", ps, depth, &r_u->ptr_entries1))
+ return False;
+
+ if (r_u->ptr_entries1 != 0) {
+ if(!prs_uint32("num_entries2", ps, depth, &r_u->num_entries2))
+ return False;
+ if(!prs_uint32("ptr_entries2", ps, depth, &r_u->ptr_entries2))
+ return False;
+ if(!prs_uint32("num_entries3", ps, depth, &r_u->num_entries3))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ r_u->sam = (SAM_ENTRY *)prs_alloc_mem(ps,sizeof(SAM_ENTRY)*r_u->num_entries2);
+ r_u->uni_dom_name = (UNISTR2 *)prs_alloc_mem(ps,sizeof(UNISTR2)*r_u->num_entries2);
+ }
+
+ if ((r_u->sam == NULL || r_u->uni_dom_name == NULL) && r_u->num_entries2 != 0) {
+ DEBUG(0, ("NULL pointers in SAMR_R_ENUM_DOMAINS\n"));
+ r_u->num_entries4 = 0;
+ r_u->status = NT_STATUS_MEMORY_NOT_ALLOCATED;
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_entries2; i++) {
+ fstring tmp;
+ slprintf(tmp, sizeof(tmp) - 1, "dom[%d]", i);
+ if(!sam_io_sam_entry(tmp, &r_u->sam[i], ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_entries2; i++) {
+ fstring tmp;
+ slprintf(tmp, sizeof(tmp) - 1, "dom[%d]", i);
+ if(!smb_io_unistr2(tmp, &r_u->uni_dom_name[i],
+ r_u->sam[i].hdr_name.buffer, ps,
+ depth))
+ return False;
+ }
+
+ }
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("num_entries4", ps, depth, &r_u->num_entries4))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_ENUM_DOM_GROUPS structure.
+********************************************************************/
+
+void init_samr_q_enum_dom_groups(SAMR_Q_ENUM_DOM_GROUPS * q_e,
+ POLICY_HND *pol,
+ uint32 start_idx, uint32 size)
+{
+ DEBUG(5, ("init_samr_q_enum_dom_groups\n"));
+
+ q_e->pol = *pol;
+
+ q_e->start_idx = start_idx;
+ q_e->max_size = size;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_enum_dom_groups(const char *desc, SAMR_Q_ENUM_DOM_GROUPS * q_e,
+ prs_struct *ps, int depth)
+{
+ if (q_e == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_enum_dom_groups");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &(q_e->pol), ps, depth))
+ return False;
+
+ if(!prs_uint32("start_idx", ps, depth, &q_e->start_idx))
+ return False;
+ if(!prs_uint32("max_size ", ps, depth, &q_e->max_size))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_ENUM_DOM_GROUPS structure.
+********************************************************************/
+
+void init_samr_r_enum_dom_groups(SAMR_R_ENUM_DOM_GROUPS * r_u,
+ uint32 next_idx, uint32 num_sam_entries)
+{
+ DEBUG(5, ("init_samr_r_enum_dom_groups\n"));
+
+ r_u->next_idx = next_idx;
+
+ if (num_sam_entries != 0) {
+ r_u->ptr_entries1 = 1;
+ r_u->ptr_entries2 = 1;
+ r_u->num_entries2 = num_sam_entries;
+ r_u->num_entries3 = num_sam_entries;
+
+ r_u->num_entries4 = num_sam_entries;
+ } else {
+ r_u->ptr_entries1 = 0;
+ r_u->num_entries2 = num_sam_entries;
+ r_u->ptr_entries2 = 1;
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_enum_dom_groups(const char *desc, SAMR_R_ENUM_DOM_GROUPS * r_u,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_enum_dom_groups");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("next_idx ", ps, depth, &r_u->next_idx))
+ return False;
+ if(!prs_uint32("ptr_entries1", ps, depth, &r_u->ptr_entries1))
+ return False;
+
+ if (r_u->ptr_entries1 != 0) {
+ if(!prs_uint32("num_entries2", ps, depth, &r_u->num_entries2))
+ return False;
+ if(!prs_uint32("ptr_entries2", ps, depth, &r_u->ptr_entries2))
+ return False;
+ if(!prs_uint32("num_entries3", ps, depth, &r_u->num_entries3))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ r_u->sam = (SAM_ENTRY *)prs_alloc_mem(ps,sizeof(SAM_ENTRY)*r_u->num_entries2);
+ r_u->uni_grp_name = (UNISTR2 *)prs_alloc_mem(ps,sizeof(UNISTR2)*r_u->num_entries2);
+ }
+
+ if ((r_u->sam == NULL || r_u->uni_grp_name == NULL) && r_u->num_entries2 != 0) {
+ DEBUG(0,
+ ("NULL pointers in SAMR_R_ENUM_DOM_GROUPS\n"));
+ r_u->num_entries4 = 0;
+ r_u->status = NT_STATUS_MEMORY_NOT_ALLOCATED;
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_entries2; i++) {
+ if(!sam_io_sam_entry("", &r_u->sam[i], ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_entries2; i++) {
+ if(!smb_io_unistr2("", &r_u->uni_grp_name[i],
+ r_u->sam[i].hdr_name.buffer, ps, depth))
+ return False;
+ }
+ }
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("num_entries4", ps, depth, &r_u->num_entries4))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_ENUM_DOM_ALIASES structure.
+********************************************************************/
+
+void init_samr_q_enum_dom_aliases(SAMR_Q_ENUM_DOM_ALIASES * q_e,
+ POLICY_HND *pol, uint32 start_idx,
+ uint32 size)
+{
+ DEBUG(5, ("init_samr_q_enum_dom_aliases\n"));
+
+ q_e->pol = *pol;
+
+ q_e->start_idx = start_idx;
+ q_e->max_size = size;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_enum_dom_aliases(const char *desc, SAMR_Q_ENUM_DOM_ALIASES * q_e,
+ prs_struct *ps, int depth)
+{
+ if (q_e == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_enum_dom_aliases");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_e->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("start_idx", ps, depth, &q_e->start_idx))
+ return False;
+ if(!prs_uint32("max_size ", ps, depth, &q_e->max_size))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_ENUM_DOM_ALIASES structure.
+********************************************************************/
+
+void init_samr_r_enum_dom_aliases(SAMR_R_ENUM_DOM_ALIASES *r_u, uint32 next_idx, uint32 num_sam_entries)
+{
+ DEBUG(5, ("init_samr_r_enum_dom_aliases\n"));
+
+ r_u->next_idx = next_idx;
+
+ if (num_sam_entries != 0) {
+ r_u->ptr_entries1 = 1;
+ r_u->ptr_entries2 = 1;
+ r_u->num_entries2 = num_sam_entries;
+ r_u->num_entries3 = num_sam_entries;
+
+ r_u->num_entries4 = num_sam_entries;
+ } else {
+ r_u->ptr_entries1 = 0;
+ r_u->num_entries2 = num_sam_entries;
+ r_u->ptr_entries2 = 1;
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_enum_dom_aliases(const char *desc, SAMR_R_ENUM_DOM_ALIASES * r_u,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_enum_dom_aliases");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("next_idx ", ps, depth, &r_u->next_idx))
+ return False;
+ if(!prs_uint32("ptr_entries1", ps, depth, &r_u->ptr_entries1))
+ return False;
+
+ if (r_u->ptr_entries1 != 0) {
+ if(!prs_uint32("num_entries2", ps, depth, &r_u->num_entries2))
+ return False;
+ if(!prs_uint32("ptr_entries2", ps, depth, &r_u->ptr_entries2))
+ return False;
+ if(!prs_uint32("num_entries3", ps, depth, &r_u->num_entries3))
+ return False;
+
+ if (UNMARSHALLING(ps) && (r_u->num_entries2 > 0)) {
+ r_u->sam = (SAM_ENTRY *)prs_alloc_mem(ps,sizeof(SAM_ENTRY)*r_u->num_entries2);
+ r_u->uni_grp_name = (UNISTR2 *)prs_alloc_mem(ps,sizeof(UNISTR2)*r_u->num_entries2);
+ }
+
+ if (r_u->num_entries2 != 0 &&
+ (r_u->sam == NULL || r_u->uni_grp_name == NULL)) {
+ DEBUG(0,("NULL pointers in SAMR_R_ENUM_DOM_ALIASES\n"));
+ r_u->num_entries4 = 0;
+ r_u->status = NT_STATUS_MEMORY_NOT_ALLOCATED;
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_entries2; i++) {
+ if(!sam_io_sam_entry("", &r_u->sam[i], ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_entries2; i++) {
+ if(!smb_io_unistr2("", &r_u->uni_grp_name[i],
+ r_u->sam[i].hdr_name.buffer, ps,
+ depth))
+ return False;
+ }
+ }
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("num_entries4", ps, depth, &r_u->num_entries4))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a ALIAS_INFO1 structure.
+********************************************************************/
+
+void init_samr_alias_info1(ALIAS_INFO1 * al1, char *acct_name, uint32 num_member, char *acct_desc)
+{
+ DEBUG(5, ("init_samr_alias_info1\n"));
+
+ init_unistr2(&al1->uni_acct_name, acct_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&al1->hdr_acct_name, &al1->uni_acct_name);
+
+ al1->num_member=num_member;
+
+ init_unistr2(&al1->uni_acct_desc, acct_desc, UNI_FLAGS_NONE);
+ init_uni_hdr(&al1->hdr_acct_desc, &al1->uni_acct_name);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_alias_info1(const char *desc, ALIAS_INFO1 * al1,
+ prs_struct *ps, int depth)
+{
+ if (al1 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_alias_info1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unihdr("hdr_acct_name", &al1->hdr_acct_name, ps, depth))
+ return False;
+ if(!prs_uint32("num_member", ps, depth, &al1->num_member))
+ return False;
+ if(!smb_io_unihdr("hdr_acct_desc", &al1->hdr_acct_desc, ps, depth))
+ return False;
+
+ if(!smb_io_unistr2("uni_acct_name", &al1->uni_acct_name,
+ al1->hdr_acct_name.buffer, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("uni_acct_desc", &al1->uni_acct_desc,
+ al1->hdr_acct_desc.buffer, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a ALIAS_INFO3 structure.
+********************************************************************/
+
+void init_samr_alias_info3(ALIAS_INFO3 * al3, const char *acct_desc)
+{
+ DEBUG(5, ("init_samr_alias_info3\n"));
+
+ init_unistr2(&al3->uni_acct_desc, acct_desc, UNI_FLAGS_NONE);
+ init_uni_hdr(&al3->hdr_acct_desc, &al3->uni_acct_desc);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_alias_info3(const char *desc, ALIAS_INFO3 * al3,
+ prs_struct *ps, int depth)
+{
+ if (al3 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_alias_info3");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unihdr("hdr_acct_desc", &al3->hdr_acct_desc, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_acct_desc", &al3->uni_acct_desc,
+ al3->hdr_acct_desc.buffer, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_alias_info_ctr(const char *desc, ALIAS_INFO_CTR * ctr,
+ prs_struct *ps, int depth)
+{
+ if (ctr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_alias_info_ctr");
+ depth++;
+
+ if(!prs_uint16("switch_value1", ps, depth, &ctr->switch_value1))
+ return False;
+ if(!prs_uint16("switch_value2", ps, depth, &ctr->switch_value2))
+ return False;
+
+ switch (ctr->switch_value1) {
+ case 1:
+ if(!samr_io_alias_info1("alias_info1", &ctr->alias.info1, ps, depth))
+ return False;
+ break;
+ case 3:
+ if(!samr_io_alias_info3("alias_info3", &ctr->alias.info3, ps, depth))
+ return False;
+ break;
+ default:
+ DEBUG(0,("samr_alias_info_ctr: unsupported switch level\n"));
+ break;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_QUERY_ALIASINFO structure.
+********************************************************************/
+
+void init_samr_q_query_aliasinfo(SAMR_Q_QUERY_ALIASINFO * q_e,
+ POLICY_HND *pol, uint16 switch_level)
+{
+ DEBUG(5, ("init_samr_q_query_aliasinfo\n"));
+
+ q_e->pol = *pol;
+ q_e->switch_level = switch_level;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_query_aliasinfo(const char *desc, SAMR_Q_QUERY_ALIASINFO * q_e,
+ prs_struct *ps, int depth)
+{
+ if (q_e == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_query_aliasinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &(q_e->pol), ps, depth))
+ return False;
+
+ if(!prs_uint16("switch_level", ps, depth, &q_e->switch_level))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_ALIASINFO structure.
+********************************************************************/
+
+void init_samr_r_query_aliasinfo(SAMR_R_QUERY_ALIASINFO * r_u,
+ ALIAS_INFO_CTR * ctr, NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_query_aliasinfo\n"));
+
+ r_u->ptr = (NT_STATUS_IS_OK(status) && ctr != NULL) ? 1 : 0;
+ r_u->ctr = *ctr;
+ r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_query_aliasinfo(const char *desc, SAMR_R_QUERY_ALIASINFO * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_query_aliasinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr", ps, depth, &r_u->ptr))
+ return False;
+
+ if (r_u->ptr != 0) {
+ if(!samr_alias_info_ctr("ctr", &r_u->ctr, ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_SET_ALIASINFO structure.
+********************************************************************/
+
+void init_samr_q_set_aliasinfo(SAMR_Q_SET_ALIASINFO * q_u,
+ POLICY_HND *hnd, ALIAS_INFO_CTR * ctr)
+{
+ DEBUG(5, ("init_samr_q_set_aliasinfo\n"));
+
+ q_u->alias_pol = *hnd;
+ q_u->ctr = *ctr;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_set_aliasinfo(const char *desc, SAMR_Q_SET_ALIASINFO * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_set_aliasinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("alias_pol", &q_u->alias_pol, ps, depth))
+ return False;
+ if(!samr_alias_info_ctr("ctr", &q_u->ctr, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_set_aliasinfo(const char *desc, SAMR_R_SET_ALIASINFO * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_set_aliasinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_QUERY_USERALIASES structure.
+********************************************************************/
+
+void init_samr_q_query_useraliases(SAMR_Q_QUERY_USERALIASES * q_u,
+ POLICY_HND *hnd,
+ uint32 num_sids,
+ uint32 *ptr_sid, DOM_SID2 * sid)
+{
+ DEBUG(5, ("init_samr_q_query_useraliases\n"));
+
+ q_u->pol = *hnd;
+
+ q_u->num_sids1 = num_sids;
+ q_u->ptr = 1;
+ q_u->num_sids2 = num_sids;
+
+ q_u->ptr_sid = ptr_sid;
+ q_u->sid = sid;
+}
+
+/*******************************************************************
+reads or writes a SAMR_Q_QUERY_USERALIASES structure.
+********************************************************************/
+
+BOOL samr_io_q_query_useraliases(const char *desc, SAMR_Q_QUERY_USERALIASES * q_u,
+ prs_struct *ps, int depth)
+{
+ fstring tmp;
+ uint32 i;
+
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_query_useraliases");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_u->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("num_sids1", ps, depth, &q_u->num_sids1))
+ return False;
+ if(!prs_uint32("ptr ", ps, depth, &q_u->ptr))
+ return False;
+
+ if (q_u->ptr==0)
+ return True;
+
+ if(!prs_uint32("num_sids2", ps, depth, &q_u->num_sids2))
+ return False;
+
+ if (UNMARSHALLING(ps) && (q_u->num_sids2 != 0)) {
+ q_u->ptr_sid = (uint32 *)prs_alloc_mem(ps,sizeof(q_u->ptr_sid[0])*q_u->num_sids2);
+ if (q_u->ptr_sid == NULL)
+ return False;
+
+ q_u->sid = (DOM_SID2 *)prs_alloc_mem(ps, sizeof(q_u->sid[0]) * q_u->num_sids2);
+ if (q_u->sid == NULL)
+ return False;
+ }
+
+ for (i = 0; i < q_u->num_sids2; i++) {
+ slprintf(tmp, sizeof(tmp) - 1, "ptr[%02d]", i);
+ if(!prs_uint32(tmp, ps, depth, &q_u->ptr_sid[i]))
+ return False;
+ }
+
+ for (i = 0; i < q_u->num_sids2; i++) {
+ if (q_u->ptr_sid[i] != 0) {
+ slprintf(tmp, sizeof(tmp) - 1, "sid[%02d]", i);
+ if(!smb_io_dom_sid2(tmp, &q_u->sid[i], ps, depth))
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_USERALIASES structure.
+********************************************************************/
+
+void init_samr_r_query_useraliases(SAMR_R_QUERY_USERALIASES * r_u,
+ uint32 num_rids, uint32 *rid,
+ NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_query_useraliases\n"));
+
+ if (NT_STATUS_IS_OK(status)) {
+ r_u->num_entries = num_rids;
+ r_u->ptr = 1;
+ r_u->num_entries2 = num_rids;
+
+ r_u->rid = rid;
+ } else {
+ r_u->num_entries = 0;
+ r_u->ptr = 0;
+ r_u->num_entries2 = 0;
+ }
+
+ r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_rids(const char *desc, uint32 *num_rids, uint32 **rid,
+ prs_struct *ps, int depth)
+{
+ fstring tmp;
+ uint32 i;
+ if (rid == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_rids");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_rids", ps, depth, num_rids))
+ return False;
+
+ if ((*num_rids) != 0) {
+ if (UNMARSHALLING(ps)) {
+ /* reading */
+ (*rid) = (uint32 *)prs_alloc_mem(ps,sizeof(uint32)*(*num_rids));
+ }
+ if ((*rid) == NULL)
+ return False;
+
+ for (i = 0; i < (*num_rids); i++) {
+ slprintf(tmp, sizeof(tmp) - 1, "rid[%02d]", i);
+ if(!prs_uint32(tmp, ps, depth, &((*rid)[i])))
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_query_useraliases(const char *desc, SAMR_R_QUERY_USERALIASES * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_query_useraliases");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_entries", ps, depth, &r_u->num_entries))
+ return False;
+ if(!prs_uint32("ptr ", ps, depth, &r_u->ptr))
+ return False;
+
+ if (r_u->ptr != 0) {
+ if(!samr_io_rids("rids", &r_u->num_entries2, &r_u->rid, ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_OPEN_ALIAS structure.
+********************************************************************/
+
+void init_samr_q_open_alias(SAMR_Q_OPEN_ALIAS * q_u, POLICY_HND *pol,
+ uint32 access_mask, uint32 rid)
+{
+ DEBUG(5, ("init_samr_q_open_alias\n"));
+
+ q_u->dom_pol = *pol;
+ q_u->access_mask = access_mask;
+ q_u->rid_alias = rid;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_open_alias(const char *desc, SAMR_Q_OPEN_ALIAS * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_open_alias");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("domain_pol", &q_u->dom_pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("access_mask", ps, depth, &q_u->access_mask))
+ return False;
+ if(!prs_uint32("rid_alias", ps, depth, &q_u->rid_alias))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_open_alias(const char *desc, SAMR_R_OPEN_ALIAS * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_open_alias");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_u->pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_LOOKUP_RIDS structure.
+********************************************************************/
+
+void init_samr_q_lookup_rids(TALLOC_CTX *ctx, SAMR_Q_LOOKUP_RIDS * q_u,
+ POLICY_HND *pol, uint32 flags,
+ uint32 num_rids, uint32 *rid)
+{
+ DEBUG(5, ("init_samr_q_lookup_rids\n"));
+
+ q_u->pol = *pol;
+
+ q_u->num_rids1 = num_rids;
+ q_u->flags = flags;
+ q_u->ptr = 0;
+ q_u->num_rids2 = num_rids;
+ q_u->rid = (uint32 *)talloc_zero(ctx, num_rids * sizeof(q_u->rid[0]));
+ if (q_u->rid == NULL) {
+ q_u->num_rids1 = 0;
+ q_u->num_rids2 = 0;
+ } else {
+ memcpy(q_u->rid, rid, num_rids * sizeof(q_u->rid[0]));
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_lookup_rids(const char *desc, SAMR_Q_LOOKUP_RIDS * q_u,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+ fstring tmp;
+
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_lookup_rids");
+ depth++;
+
+ if (UNMARSHALLING(ps))
+ ZERO_STRUCTP(q_u);
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_u->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("num_rids1", ps, depth, &q_u->num_rids1))
+ return False;
+ if(!prs_uint32("flags ", ps, depth, &q_u->flags))
+ return False;
+ if(!prs_uint32("ptr ", ps, depth, &q_u->ptr))
+ return False;
+ if(!prs_uint32("num_rids2", ps, depth, &q_u->num_rids2))
+ return False;
+
+ if (UNMARSHALLING(ps) && (q_u->num_rids2 != 0)) {
+ q_u->rid = (uint32 *)prs_alloc_mem(ps, sizeof(q_u->rid[0])*q_u->num_rids2);
+ if (q_u->rid == NULL)
+ return False;
+ }
+
+ for (i = 0; i < q_u->num_rids2; i++) {
+ slprintf(tmp, sizeof(tmp) - 1, "rid[%02d] ", i);
+ if(!prs_uint32(tmp, ps, depth, &q_u->rid[i]))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_LOOKUP_RIDS structure.
+********************************************************************/
+
+void init_samr_r_lookup_rids(SAMR_R_LOOKUP_RIDS * r_u,
+ uint32 num_names, UNIHDR * hdr_name,
+ UNISTR2 *uni_name, uint32 *type)
+{
+ DEBUG(5, ("init_samr_r_lookup_rids\n"));
+
+ r_u->hdr_name = NULL;
+ r_u->uni_name = NULL;
+ r_u->type = NULL;
+
+ if (num_names != 0) {
+ r_u->num_names1 = num_names;
+ r_u->ptr_names = 1;
+ r_u->num_names2 = num_names;
+
+ r_u->num_types1 = num_names;
+ r_u->ptr_types = 1;
+ r_u->num_types2 = num_names;
+
+ r_u->hdr_name = hdr_name;
+ r_u->uni_name = uni_name;
+ r_u->type = type;
+ } else {
+ r_u->num_names1 = num_names;
+ r_u->ptr_names = 0;
+ r_u->num_names2 = num_names;
+
+ r_u->num_types1 = num_names;
+ r_u->ptr_types = 0;
+ r_u->num_types2 = num_names;
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_lookup_rids(const char *desc, SAMR_R_LOOKUP_RIDS * r_u,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+ fstring tmp;
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_lookup_rids");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_names1", ps, depth, &r_u->num_names1))
+ return False;
+ if(!prs_uint32("ptr_names ", ps, depth, &r_u->ptr_names))
+ return False;
+
+ if (r_u->ptr_names != 0) {
+
+ if(!prs_uint32("num_names2", ps, depth, &r_u->num_names2))
+ return False;
+
+
+ if (UNMARSHALLING(ps) && (r_u->num_names2 != 0)) {
+ r_u->hdr_name = (UNIHDR *) prs_alloc_mem(ps, r_u->num_names2 * sizeof(r_u->hdr_name[0]));
+ if (r_u->hdr_name == NULL)
+ return False;
+
+ r_u->uni_name = (UNISTR2 *)prs_alloc_mem(ps, r_u->num_names2 * sizeof(r_u->uni_name[0]));
+ if (r_u->uni_name == NULL)
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_names2; i++) {
+ slprintf(tmp, sizeof(tmp) - 1, "hdr[%02d] ", i);
+ if(!smb_io_unihdr("", &r_u->hdr_name[i], ps, depth))
+ return False;
+ }
+ for (i = 0; i < r_u->num_names2; i++) {
+ slprintf(tmp, sizeof(tmp) - 1, "str[%02d] ", i);
+ if(!smb_io_unistr2("", &r_u->uni_name[i], r_u->hdr_name[i].buffer, ps, depth))
+ return False;
+ }
+
+ }
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("num_types1", ps, depth, &r_u->num_types1))
+ return False;
+ if(!prs_uint32("ptr_types ", ps, depth, &r_u->ptr_types))
+ return False;
+
+ if (r_u->ptr_types != 0) {
+
+ if(!prs_uint32("num_types2", ps, depth, &r_u->num_types2))
+ return False;
+
+ if (UNMARSHALLING(ps) && (r_u->num_types2 != 0)) {
+ r_u->type = (uint32 *)prs_alloc_mem(ps, r_u->num_types2 * sizeof(r_u->type[0]));
+ if (r_u->type == NULL)
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_types2; i++) {
+ slprintf(tmp, sizeof(tmp) - 1, "type[%02d] ", i);
+ if(!prs_uint32(tmp, ps, depth, &r_u->type[i]))
+ return False;
+ }
+ }
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_OPEN_ALIAS structure.
+********************************************************************/
+
+void init_samr_q_delete_alias(SAMR_Q_DELETE_DOM_ALIAS * q_u, POLICY_HND *hnd)
+{
+ DEBUG(5, ("init_samr_q_delete_alias\n"));
+
+ q_u->alias_pol = *hnd;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_delete_alias(const char *desc, SAMR_Q_DELETE_DOM_ALIAS * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_delete_alias");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("alias_pol", &q_u->alias_pol, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_delete_alias(const char *desc, SAMR_R_DELETE_DOM_ALIAS * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_delete_alias");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_u->pol, ps, depth))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_CREATE_DOM_ALIAS structure.
+********************************************************************/
+
+void init_samr_q_create_dom_alias(SAMR_Q_CREATE_DOM_ALIAS * q_u,
+ POLICY_HND *hnd, const char *acct_desc)
+{
+ DEBUG(5, ("init_samr_q_create_dom_alias\n"));
+
+ q_u->dom_pol = *hnd;
+
+ init_unistr2(&q_u->uni_acct_desc, acct_desc, UNI_FLAGS_NONE);
+ init_uni_hdr(&q_u->hdr_acct_desc, &q_u->uni_acct_desc);
+
+ q_u->access_mask = MAXIMUM_ALLOWED_ACCESS;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_create_dom_alias(const char *desc, SAMR_Q_CREATE_DOM_ALIAS * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_create_dom_alias");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("dom_pol", &q_u->dom_pol, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr("hdr_acct_desc", &q_u->hdr_acct_desc, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_acct_desc", &q_u->uni_acct_desc,
+ q_u->hdr_acct_desc.buffer, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("access_mask", ps, depth, &q_u->access_mask))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_create_dom_alias(const char *desc, SAMR_R_CREATE_DOM_ALIAS * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_create_dom_alias");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("alias_pol", &r_u->alias_pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("rid", ps, depth, &r_u->rid))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_ADD_ALIASMEM structure.
+********************************************************************/
+
+void init_samr_q_add_aliasmem(SAMR_Q_ADD_ALIASMEM * q_u, POLICY_HND *hnd,
+ DOM_SID *sid)
+{
+ DEBUG(5, ("init_samr_q_add_aliasmem\n"));
+
+ q_u->alias_pol = *hnd;
+ init_dom_sid2(&q_u->sid, sid);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_add_aliasmem(const char *desc, SAMR_Q_ADD_ALIASMEM * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_add_aliasmem");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("alias_pol", &q_u->alias_pol, ps, depth))
+ return False;
+ if(!smb_io_dom_sid2("sid ", &q_u->sid, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_add_aliasmem(const char *desc, SAMR_R_ADD_ALIASMEM * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_add_aliasmem");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_DEL_ALIASMEM structure.
+********************************************************************/
+
+void init_samr_q_del_aliasmem(SAMR_Q_DEL_ALIASMEM * q_u, POLICY_HND *hnd,
+ DOM_SID *sid)
+{
+ DEBUG(5, ("init_samr_q_del_aliasmem\n"));
+
+ q_u->alias_pol = *hnd;
+ init_dom_sid2(&q_u->sid, sid);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_del_aliasmem(const char *desc, SAMR_Q_DEL_ALIASMEM * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_del_aliasmem");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("alias_pol", &q_u->alias_pol, ps, depth))
+ return False;
+ if(!smb_io_dom_sid2("sid ", &q_u->sid, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_del_aliasmem(const char *desc, SAMR_R_DEL_ALIASMEM * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_del_aliasmem");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_DELETE_DOM_ALIAS structure.
+********************************************************************/
+
+void init_samr_q_delete_dom_alias(SAMR_Q_DELETE_DOM_ALIAS * q_c,
+ POLICY_HND *hnd)
+{
+ DEBUG(5, ("init_samr_q_delete_dom_alias\n"));
+
+ q_c->alias_pol = *hnd;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_delete_dom_alias(const char *desc, SAMR_Q_DELETE_DOM_ALIAS * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_delete_dom_alias");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("alias_pol", &q_u->alias_pol, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_DELETE_DOM_ALIAS structure.
+********************************************************************/
+
+void init_samr_r_delete_dom_alias(SAMR_R_DELETE_DOM_ALIAS * r_u,
+ NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_delete_dom_alias\n"));
+
+ r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_delete_dom_alias(const char *desc, SAMR_R_DELETE_DOM_ALIAS * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_delete_dom_alias");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_QUERY_ALIASMEM structure.
+********************************************************************/
+
+void init_samr_q_query_aliasmem(SAMR_Q_QUERY_ALIASMEM * q_c,
+ POLICY_HND *hnd)
+{
+ DEBUG(5, ("init_samr_q_query_aliasmem\n"));
+
+ q_c->alias_pol = *hnd;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_query_aliasmem(const char *desc, SAMR_Q_QUERY_ALIASMEM * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_query_aliasmem");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("alias_pol", &q_u->alias_pol, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_ALIASMEM structure.
+********************************************************************/
+
+void init_samr_r_query_aliasmem(SAMR_R_QUERY_ALIASMEM * r_u,
+ uint32 num_sids, DOM_SID2 * sid,
+ NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_query_aliasmem\n"));
+
+ if (NT_STATUS_IS_OK(status)) {
+ r_u->num_sids = num_sids;
+ r_u->ptr = (num_sids != 0) ? 1 : 0;
+ r_u->num_sids1 = num_sids;
+
+ r_u->sid = sid;
+ } else {
+ r_u->ptr = 0;
+ r_u->num_sids = 0;
+ }
+
+ r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_query_aliasmem(const char *desc, SAMR_R_QUERY_ALIASMEM * r_u,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_query_aliasmem");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_sids ", ps, depth, &r_u->num_sids))
+ return False;
+ if(!prs_uint32("ptr", ps, depth, &r_u->ptr))
+ return False;
+
+ if (r_u->ptr != 0 && r_u->num_sids != 0) {
+ uint32 *ptr_sid = NULL;
+
+ if(!prs_uint32("num_sids1", ps, depth, &r_u->num_sids1))
+ return False;
+
+ ptr_sid = talloc(ps->mem_ctx, sizeof(uint32) * r_u->num_sids1);
+ if (!ptr_sid) {
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_sids1; i++) {
+ ptr_sid[i] = 1;
+ if(!prs_uint32("ptr_sid", ps, depth, &ptr_sid[i]))
+ return False;
+ }
+
+ if (UNMARSHALLING(ps)) {
+ r_u->sid = talloc(ps->mem_ctx, r_u->num_sids1 * sizeof(DOM_SID2));
+ }
+
+ for (i = 0; i < r_u->num_sids1; i++) {
+ if (ptr_sid[i] != 0) {
+ if(!smb_io_dom_sid2("sid", &r_u->sid[i], ps, depth))
+ return False;
+ }
+ }
+ }
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_LOOKUP_NAMES structure.
+********************************************************************/
+
+NTSTATUS init_samr_q_lookup_names(TALLOC_CTX *ctx, SAMR_Q_LOOKUP_NAMES * q_u,
+ POLICY_HND *pol, uint32 flags,
+ uint32 num_names, const char **name)
+{
+ uint32 i;
+
+ DEBUG(5, ("init_samr_q_lookup_names\n"));
+
+ q_u->pol = *pol;
+
+ q_u->num_names1 = num_names;
+ q_u->flags = flags;
+ q_u->ptr = 0;
+ q_u->num_names2 = num_names;
+
+ if (!(q_u->hdr_name = (UNIHDR *)talloc_zero(ctx, num_names * sizeof(UNIHDR))))
+ return NT_STATUS_NO_MEMORY;
+
+ if (!(q_u->uni_name = (UNISTR2 *)talloc_zero(ctx, num_names * sizeof(UNISTR2))))
+ return NT_STATUS_NO_MEMORY;
+
+ for (i = 0; i < num_names; i++) {
+ init_unistr2(&q_u->uni_name[i], name[i], UNI_FLAGS_NONE); /* unicode string for machine account */
+ init_uni_hdr(&q_u->hdr_name[i], &q_u->uni_name[i]); /* unicode header for user_name */
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_lookup_names(const char *desc, SAMR_Q_LOOKUP_NAMES * q_u,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_lookup_names");
+ depth++;
+
+ if (UNMARSHALLING(ps))
+ ZERO_STRUCTP(q_u);
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_u->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("num_names1", ps, depth, &q_u->num_names1))
+ return False;
+ if(!prs_uint32("flags ", ps, depth, &q_u->flags))
+ return False;
+ if(!prs_uint32("ptr ", ps, depth, &q_u->ptr))
+ return False;
+ if(!prs_uint32("num_names2", ps, depth, &q_u->num_names2))
+ return False;
+
+ if (UNMARSHALLING(ps) && (q_u->num_names2 != 0)) {
+ q_u->hdr_name = (UNIHDR *)prs_alloc_mem(ps, sizeof(UNIHDR) *
+ q_u->num_names2);
+ q_u->uni_name = (UNISTR2 *)prs_alloc_mem(ps, sizeof(UNISTR2) *
+ q_u->num_names2);
+ if (!q_u->hdr_name || !q_u->uni_name)
+ return False;
+ }
+
+ for (i = 0; i < q_u->num_names2; i++) {
+ if(!smb_io_unihdr("", &q_u->hdr_name[i], ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < q_u->num_names2; i++) {
+ if(!smb_io_unistr2("", &q_u->uni_name[i], q_u->hdr_name[i].buffer, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_LOOKUP_NAMES structure.
+********************************************************************/
+
+NTSTATUS init_samr_r_lookup_names(TALLOC_CTX *ctx, SAMR_R_LOOKUP_NAMES * r_u,
+ uint32 num_rids,
+ uint32 *rid, uint32 *type,
+ NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_lookup_names\n"));
+
+ if (NT_STATUS_IS_OK(status) && (num_rids != 0)) {
+ uint32 i;
+
+ r_u->num_types1 = num_rids;
+ r_u->ptr_types = 1;
+ r_u->num_types2 = num_rids;
+
+ r_u->num_rids1 = num_rids;
+ r_u->ptr_rids = 1;
+ r_u->num_rids2 = num_rids;
+
+ if (!(r_u->rids = (uint32 *)talloc_zero(ctx, sizeof(uint32)*num_rids)))
+ return NT_STATUS_NO_MEMORY;
+ if (!(r_u->types = (uint32 *)talloc_zero(ctx, sizeof(uint32)*num_rids)))
+ return NT_STATUS_NO_MEMORY;
+
+ if (!r_u->rids || !r_u->types)
+ goto empty;
+
+ for (i = 0; i < num_rids; i++) {
+ r_u->rids[i] = rid[i];
+ r_u->types[i] = type[i];
+ }
+ } else {
+
+ empty:
+ r_u->num_types1 = 0;
+ r_u->ptr_types = 0;
+ r_u->num_types2 = 0;
+
+ r_u->num_rids1 = 0;
+ r_u->ptr_rids = 0;
+ r_u->num_rids2 = 0;
+
+ r_u->rids = NULL;
+ r_u->types = NULL;
+ }
+
+ r_u->status = status;
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_lookup_names(const char *desc, SAMR_R_LOOKUP_NAMES * r_u,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+ fstring tmp;
+
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_lookup_names");
+ depth++;
+
+ if (UNMARSHALLING(ps))
+ ZERO_STRUCTP(r_u);
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_rids1", ps, depth, &r_u->num_rids1))
+ return False;
+ if(!prs_uint32("ptr_rids ", ps, depth, &r_u->ptr_rids))
+ return False;
+
+ if (r_u->ptr_rids != 0) {
+ if(!prs_uint32("num_rids2", ps, depth, &r_u->num_rids2))
+ return False;
+
+ if (r_u->num_rids2 != r_u->num_rids1) {
+ /* RPC fault */
+ return False;
+ }
+
+ if (UNMARSHALLING(ps))
+ r_u->rids = (uint32 *)prs_alloc_mem(ps, sizeof(uint32)*r_u->num_rids2);
+
+ if (!r_u->rids) {
+ DEBUG(0, ("NULL rids in samr_io_r_lookup_names\n"));
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_rids2; i++) {
+ slprintf(tmp, sizeof(tmp) - 1, "rid[%02d] ", i);
+ if(!prs_uint32(tmp, ps, depth, &r_u->rids[i]))
+ return False;
+ }
+ }
+
+ if(!prs_uint32("num_types1", ps, depth, &r_u->num_types1))
+ return False;
+ if(!prs_uint32("ptr_types ", ps, depth, &r_u->ptr_types))
+ return False;
+
+ if (r_u->ptr_types != 0) {
+ if(!prs_uint32("num_types2", ps, depth, &r_u->num_types2))
+ return False;
+
+ if (r_u->num_types2 != r_u->num_types1) {
+ /* RPC fault */
+ return False;
+ }
+
+ if (UNMARSHALLING(ps))
+ r_u->types = (uint32 *)prs_alloc_mem(ps, sizeof(uint32)*r_u->num_types2);
+
+ if (!r_u->types) {
+ DEBUG(0, ("NULL types in samr_io_r_lookup_names\n"));
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_types2; i++) {
+ slprintf(tmp, sizeof(tmp) - 1, "type[%02d] ", i);
+ if(!prs_uint32(tmp, ps, depth, &r_u->types[i]))
+ return False;
+ }
+ }
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_DELETE_DOM_USER structure.
+********************************************************************/
+
+void init_samr_q_delete_dom_user(SAMR_Q_DELETE_DOM_USER * q_c,
+ POLICY_HND *hnd)
+{
+ DEBUG(5, ("init_samr_q_delete_dom_user\n"));
+
+ q_c->user_pol = *hnd;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_delete_dom_user(const char *desc, SAMR_Q_DELETE_DOM_USER * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_delete_dom_user");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("user_pol", &q_u->user_pol, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_delete_dom_user(const char *desc, SAMR_R_DELETE_DOM_USER * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_delete_dom_user");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_u->pol, ps, depth))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+void init_samr_q_open_user(SAMR_Q_OPEN_USER * q_u,
+ POLICY_HND *pol,
+ uint32 access_mask, uint32 rid)
+{
+ DEBUG(5, ("samr_init_samr_q_open_user\n"));
+
+ q_u->domain_pol = *pol;
+ q_u->access_mask = access_mask;
+ q_u->user_rid = rid;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_open_user(const char *desc, SAMR_Q_OPEN_USER * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_open_user");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("domain_pol", &q_u->domain_pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("access_mask", ps, depth, &q_u->access_mask))
+ return False;
+ if(!prs_uint32("user_rid ", ps, depth, &q_u->user_rid))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_open_user(const char *desc, SAMR_R_OPEN_USER * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_open_user");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("user_pol", &r_u->user_pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+void init_samr_q_create_user(SAMR_Q_CREATE_USER * q_u,
+ POLICY_HND *pol,
+ const char *name,
+ uint32 acb_info, uint32 access_mask)
+{
+ DEBUG(5, ("samr_init_samr_q_create_user\n"));
+
+ q_u->domain_pol = *pol;
+
+ init_unistr2(&q_u->uni_name, name, UNI_FLAGS_NONE);
+ init_uni_hdr(&q_u->hdr_name, &q_u->uni_name);
+
+ q_u->acb_info = acb_info;
+ q_u->access_mask = access_mask;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_create_user(const char *desc, SAMR_Q_CREATE_USER * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_create_user");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("domain_pol", &q_u->domain_pol, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr("hdr_name", &q_u->hdr_name, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_name", &q_u->uni_name, q_u->hdr_name.buffer, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("acb_info ", ps, depth, &q_u->acb_info))
+ return False;
+ if(!prs_uint32("access_mask", ps, depth, &q_u->access_mask))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_create_user(const char *desc, SAMR_R_CREATE_USER * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_create_user");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("user_pol", &r_u->user_pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("access_granted", ps, depth, &r_u->access_granted))
+ return False;
+ if(!prs_uint32("user_rid ", ps, depth, &r_u->user_rid))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_QUERY_USERINFO structure.
+********************************************************************/
+
+void init_samr_q_query_userinfo(SAMR_Q_QUERY_USERINFO * q_u,
+ POLICY_HND *hnd, uint16 switch_value)
+{
+ DEBUG(5, ("init_samr_q_query_userinfo\n"));
+
+ q_u->pol = *hnd;
+ q_u->switch_value = switch_value;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_query_userinfo(const char *desc, SAMR_Q_QUERY_USERINFO * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_query_userinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_u->pol, ps, depth))
+ return False;
+
+ if(!prs_uint16("switch_value", ps, depth, &q_u->switch_value)) /* 0x0015 or 0x0011 */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a LOGON_HRS structure.
+********************************************************************/
+
+static BOOL sam_io_logon_hrs(const char *desc, LOGON_HRS * hrs,
+ prs_struct *ps, int depth)
+{
+ if (hrs == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_logon_hrs");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("len ", ps, depth, &hrs->len))
+ return False;
+
+ if (hrs->len > sizeof(hrs->hours)) {
+ DEBUG(3, ("sam_io_logon_hrs: truncating length from %d\n", hrs->len));
+ hrs->len = sizeof(hrs->hours);
+ }
+
+ if(!prs_uint8s(False, "hours", ps, depth, hrs->hours, hrs->len))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_USER_INFO_12 structure.
+********************************************************************/
+
+void init_sam_user_info12(SAM_USER_INFO_12 * usr,
+ const uint8 lm_pwd[16], const uint8 nt_pwd[16])
+{
+ DEBUG(5, ("init_sam_user_info12\n"));
+
+ usr->lm_pwd_active =
+ memcpy(usr->lm_pwd, lm_pwd, sizeof(usr->lm_pwd)) ? 1 : 0;
+ usr->nt_pwd_active =
+ memcpy(usr->nt_pwd, nt_pwd, sizeof(usr->nt_pwd)) ? 1 : 0;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_user_info12(const char *desc, SAM_USER_INFO_12 * u,
+ prs_struct *ps, int depth)
+{
+ if (u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_user_info12");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint8s(False, "lm_pwd", ps, depth, u->lm_pwd, sizeof(u->lm_pwd)))
+ return False;
+ if(!prs_uint8s(False, "nt_pwd", ps, depth, u->nt_pwd, sizeof(u->nt_pwd)))
+ return False;
+
+ if(!prs_uint8("lm_pwd_active", ps, depth, &u->lm_pwd_active))
+ return False;
+ if(!prs_uint8("nt_pwd_active", ps, depth, &u->nt_pwd_active))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_USER_INFO_10 structure.
+********************************************************************/
+
+void init_sam_user_info10(SAM_USER_INFO_10 * usr, uint32 acb_info)
+{
+ DEBUG(5, ("init_sam_user_info10\n"));
+
+ usr->acb_info = acb_info;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_user_info10(const char *desc, SAM_USER_INFO_10 * usr,
+ prs_struct *ps, int depth)
+{
+ if (usr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_user_info10");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("acb_info", ps, depth, &usr->acb_info))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_USER_INFO_11 structure.
+********************************************************************/
+
+void init_sam_user_info11(SAM_USER_INFO_11 * usr,
+ NTTIME * expiry,
+ char *mach_acct,
+ uint32 rid_user, uint32 rid_group, uint16 acct_ctrl)
+{
+ DEBUG(5, ("init_sam_user_info11\n"));
+
+ memcpy(&usr->expiry, expiry, sizeof(usr->expiry)); /* expiry time or something? */
+ ZERO_STRUCT(usr->padding_1); /* 0 - padding 24 bytes */
+
+ usr->padding_2 = 0; /* 0 - padding 4 bytes */
+
+ usr->ptr_1 = 1; /* pointer */
+ ZERO_STRUCT(usr->padding_3); /* 0 - padding 32 bytes */
+ usr->padding_4 = 0; /* 0 - padding 4 bytes */
+
+ usr->ptr_2 = 1; /* pointer */
+ usr->padding_5 = 0; /* 0 - padding 4 bytes */
+
+ usr->ptr_3 = 1; /* pointer */
+ ZERO_STRUCT(usr->padding_6); /* 0 - padding 32 bytes */
+
+ usr->rid_user = rid_user;
+ usr->rid_group = rid_group;
+
+ usr->acct_ctrl = acct_ctrl;
+ usr->unknown_3 = 0x0000;
+
+ usr->unknown_4 = 0x003f; /* 0x003f - 16 bit unknown */
+ usr->unknown_5 = 0x003c; /* 0x003c - 16 bit unknown */
+
+ ZERO_STRUCT(usr->padding_7); /* 0 - padding 16 bytes */
+ usr->padding_8 = 0; /* 0 - padding 4 bytes */
+
+ init_unistr2(&usr->uni_mach_acct, mach_acct, UNI_FLAGS_NONE); /* unicode string for machine account */
+ init_uni_hdr(&usr->hdr_mach_acct, &usr->uni_mach_acct); /* unicode header for machine account */
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_user_info11(const char *desc, SAM_USER_INFO_11 * usr,
+ prs_struct *ps, int depth)
+{
+ if (usr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_unknown_11");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint8s(False, "padding_0", ps, depth, usr->padding_0, sizeof(usr->padding_0)))
+ return False;
+
+ if(!smb_io_time("time", &usr->expiry, ps, depth))
+ return False;
+
+ if(!prs_uint8s(False, "padding_1", ps, depth, usr->padding_1, sizeof(usr->padding_1)))
+ return False;
+
+ if(!smb_io_unihdr("unihdr", &usr->hdr_mach_acct, ps, depth))
+ return False;
+
+ if(!prs_uint32("padding_2", ps, depth, &usr->padding_2))
+ return False;
+
+ if(!prs_uint32("ptr_1 ", ps, depth, &usr->ptr_1))
+ return False;
+ if(!prs_uint8s(False, "padding_3", ps, depth, usr->padding_3, sizeof(usr->padding_3)))
+ return False;
+
+ if(!prs_uint32("padding_4", ps, depth, &usr->padding_4))
+ return False;
+
+ if(!prs_uint32("ptr_2 ", ps, depth, &usr->ptr_2))
+ return False;
+ if(!prs_uint32("padding_5", ps, depth, &usr->padding_5))
+ return False;
+
+ if(!prs_uint32("ptr_3 ", ps, depth, &usr->ptr_3))
+ return False;
+ if(!prs_uint8s(False, "padding_6", ps, depth, usr->padding_6,sizeof(usr->padding_6)))
+ return False;
+
+ if(!prs_uint32("rid_user ", ps, depth, &usr->rid_user))
+ return False;
+ if(!prs_uint32("rid_group", ps, depth, &usr->rid_group))
+ return False;
+ if(!prs_uint16("acct_ctrl", ps, depth, &usr->acct_ctrl))
+ return False;
+ if(!prs_uint16("unknown_3", ps, depth, &usr->unknown_3))
+ return False;
+ if(!prs_uint16("unknown_4", ps, depth, &usr->unknown_4))
+ return False;
+ if(!prs_uint16("unknown_5", ps, depth, &usr->unknown_5))
+ return False;
+
+ if(!prs_uint8s(False, "padding_7", ps, depth, usr->padding_7, sizeof(usr->padding_7)))
+ return False;
+
+ if(!prs_uint32("padding_8", ps, depth, &(usr->padding_8)))
+ return False;
+
+ if(!smb_io_unistr2("unistr2", &usr->uni_mach_acct, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint8s(False, "padding_9", ps, depth, usr->padding_9, sizeof(usr->padding_9)))
+ return False;
+
+ return True;
+}
+
+/*************************************************************************
+ init_sam_user_infoa
+ *************************************************************************/
+
+void init_sam_user_info24(SAM_USER_INFO_24 * usr, char newpass[516], uint16 pw_len)
+{
+ DEBUG(10, ("init_sam_user_info24:\n"));
+ memcpy(usr->pass, newpass, sizeof(usr->pass));
+ usr->pw_len = pw_len;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_user_info24(const char *desc, SAM_USER_INFO_24 * usr,
+ prs_struct *ps, int depth)
+{
+ if (usr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_user_info24");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint8s(False, "password", ps, depth, usr->pass,
+ sizeof(usr->pass)))
+ return False;
+
+ if (MARSHALLING(ps) && (usr->pw_len != 0)) {
+ if (!prs_uint16("pw_len", ps, depth, &usr->pw_len))
+ return False;
+ }
+ if(!prs_align(ps))
+ return False;
+
+ return True;
+}
+
+/*************************************************************************
+ init_sam_user_info23
+
+ unknown_6 = 0x0000 04ec
+
+ *************************************************************************/
+
+void init_sam_user_info23W(SAM_USER_INFO_23 * usr, NTTIME * logon_time, /* all zeros */
+ NTTIME * logoff_time, /* all zeros */
+ NTTIME * kickoff_time, /* all zeros */
+ NTTIME * pass_last_set_time, /* all zeros */
+ NTTIME * pass_can_change_time, /* all zeros */
+ NTTIME * pass_must_change_time, /* all zeros */
+ UNISTR2 *user_name,
+ UNISTR2 *full_name,
+ UNISTR2 *home_dir,
+ UNISTR2 *dir_drive,
+ UNISTR2 *log_scr,
+ UNISTR2 *prof_path,
+ UNISTR2 *desc,
+ UNISTR2 *wkstas,
+ UNISTR2 *unk_str,
+ UNISTR2 *mung_dial,
+ uint32 user_rid, /* 0x0000 0000 */
+ uint32 group_rid,
+ uint32 acb_info,
+ uint32 fields_present,
+ uint16 logon_divs,
+ LOGON_HRS * hrs,
+ uint16 bad_password_count,
+ uint16 logon_count,
+ char newpass[516], uint32 unknown_6)
+{
+ usr->logon_time = *logon_time; /* all zeros */
+ usr->logoff_time = *logoff_time; /* all zeros */
+ usr->kickoff_time = *kickoff_time; /* all zeros */
+ usr->pass_last_set_time = *pass_last_set_time; /* all zeros */
+ usr->pass_can_change_time = *pass_can_change_time; /* all zeros */
+ usr->pass_must_change_time = *pass_must_change_time; /* all zeros */
+
+ ZERO_STRUCT(usr->nt_pwd);
+ ZERO_STRUCT(usr->lm_pwd);
+
+ usr->user_rid = user_rid; /* 0x0000 0000 */
+ usr->group_rid = group_rid;
+ usr->acb_info = acb_info;
+ usr->fields_present = fields_present; /* 09f8 27fa */
+
+ usr->logon_divs = logon_divs; /* should be 168 (hours/week) */
+ usr->ptr_logon_hrs = hrs ? 1 : 0;
+
+ if (nt_time_is_zero(pass_must_change_time)) {
+ usr->passmustchange=PASS_MUST_CHANGE_AT_NEXT_LOGON;
+ } else {
+ usr->passmustchange=0;
+ }
+
+ ZERO_STRUCT(usr->padding1);
+ ZERO_STRUCT(usr->padding2);
+
+ usr->bad_password_count = bad_password_count;
+ usr->logon_count = logon_count;
+
+ memcpy(usr->pass, newpass, sizeof(usr->pass));
+
+ copy_unistr2(&usr->uni_user_name, user_name);
+ init_uni_hdr(&usr->hdr_user_name, &usr->uni_user_name);
+
+ copy_unistr2(&usr->uni_full_name, full_name);
+ init_uni_hdr(&usr->hdr_full_name, &usr->uni_full_name);
+
+ copy_unistr2(&usr->uni_home_dir, home_dir);
+ init_uni_hdr(&usr->hdr_home_dir, &usr->uni_home_dir);
+
+ copy_unistr2(&usr->uni_dir_drive, dir_drive);
+ init_uni_hdr(&usr->hdr_dir_drive, &usr->uni_dir_drive);
+
+ copy_unistr2(&usr->uni_logon_script, log_scr);
+ init_uni_hdr(&usr->hdr_logon_script, &usr->uni_logon_script);
+
+ copy_unistr2(&usr->uni_profile_path, prof_path);
+ init_uni_hdr(&usr->hdr_profile_path, &usr->uni_profile_path);
+
+ copy_unistr2(&usr->uni_acct_desc, desc);
+ init_uni_hdr(&usr->hdr_acct_desc, &usr->uni_acct_desc);
+
+ copy_unistr2(&usr->uni_workstations, wkstas);
+ init_uni_hdr(&usr->hdr_workstations, &usr->uni_workstations);
+
+ copy_unistr2(&usr->uni_unknown_str, unk_str);
+ init_uni_hdr(&usr->hdr_unknown_str, &usr->uni_unknown_str);
+
+ copy_unistr2(&usr->uni_munged_dial, mung_dial);
+ init_uni_hdr(&usr->hdr_munged_dial, &usr->uni_munged_dial);
+
+ usr->unknown_6 = unknown_6; /* 0x0000 04ec */
+ usr->padding4 = 0;
+
+ memcpy(&usr->logon_hrs, hrs, sizeof(usr->logon_hrs));
+}
+
+/*************************************************************************
+ init_sam_user_info23
+
+ unknown_6 = 0x0000 04ec
+
+ *************************************************************************/
+
+void init_sam_user_info23A(SAM_USER_INFO_23 * usr, NTTIME * logon_time, /* all zeros */
+ NTTIME * logoff_time, /* all zeros */
+ NTTIME * kickoff_time, /* all zeros */
+ NTTIME * pass_last_set_time, /* all zeros */
+ NTTIME * pass_can_change_time, /* all zeros */
+ NTTIME * pass_must_change_time, /* all zeros */
+ char *user_name, /* NULL */
+ char *full_name,
+ char *home_dir, char *dir_drive, char *log_scr,
+ char *prof_path, const char *desc, char *wkstas,
+ char *unk_str, char *mung_dial, uint32 user_rid, /* 0x0000 0000 */
+ uint32 group_rid, uint32 acb_info,
+ uint32 fields_present, uint16 logon_divs,
+ LOGON_HRS * hrs, uint16 bad_password_count, uint16 logon_count,
+ char newpass[516], uint32 unknown_6)
+{
+ DATA_BLOB blob = base64_decode_data_blob(mung_dial);
+
+ usr->logon_time = *logon_time; /* all zeros */
+ usr->logoff_time = *logoff_time; /* all zeros */
+ usr->kickoff_time = *kickoff_time; /* all zeros */
+ usr->pass_last_set_time = *pass_last_set_time; /* all zeros */
+ usr->pass_can_change_time = *pass_can_change_time; /* all zeros */
+ usr->pass_must_change_time = *pass_must_change_time; /* all zeros */
+
+ ZERO_STRUCT(usr->nt_pwd);
+ ZERO_STRUCT(usr->lm_pwd);
+
+ usr->user_rid = user_rid; /* 0x0000 0000 */
+ usr->group_rid = group_rid;
+ usr->acb_info = acb_info;
+ usr->fields_present = fields_present; /* 09f8 27fa */
+
+ usr->logon_divs = logon_divs; /* should be 168 (hours/week) */
+ usr->ptr_logon_hrs = hrs ? 1 : 0;
+
+ if (nt_time_is_zero(pass_must_change_time)) {
+ usr->passmustchange=PASS_MUST_CHANGE_AT_NEXT_LOGON;
+ } else {
+ usr->passmustchange=0;
+ }
+
+ ZERO_STRUCT(usr->padding1);
+ ZERO_STRUCT(usr->padding2);
+
+ usr->bad_password_count = bad_password_count;
+ usr->logon_count = logon_count;
+
+ memcpy(usr->pass, newpass, sizeof(usr->pass));
+
+ init_unistr2(&usr->uni_user_name, user_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_user_name, &usr->uni_user_name);
+
+ init_unistr2(&usr->uni_full_name, full_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_full_name, &usr->uni_full_name);
+
+ init_unistr2(&usr->uni_home_dir, home_dir, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_home_dir, &usr->uni_home_dir);
+
+ init_unistr2(&usr->uni_dir_drive, dir_drive, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_dir_drive, &usr->uni_dir_drive);
+
+ init_unistr2(&usr->uni_logon_script, log_scr, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_logon_script, &usr->uni_logon_script);
+
+ init_unistr2(&usr->uni_profile_path, prof_path, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_profile_path, &usr->uni_profile_path);
+
+ init_unistr2(&usr->uni_acct_desc, desc, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_acct_desc, &usr->uni_acct_desc);
+
+ init_unistr2(&usr->uni_workstations, wkstas, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_workstations, &usr->uni_workstations);
+
+ init_unistr2(&usr->uni_unknown_str, unk_str, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_unknown_str, &usr->uni_unknown_str);
+
+ init_unistr2_from_datablob(&usr->uni_munged_dial, &blob);
+ init_uni_hdr(&usr->hdr_munged_dial, &usr->uni_munged_dial);
+
+ data_blob_free(&blob);
+
+ usr->unknown_6 = unknown_6; /* 0x0000 04ec */
+ usr->padding4 = 0;
+
+ memcpy(&usr->logon_hrs, hrs, sizeof(usr->logon_hrs));
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_user_info23(const char *desc, SAM_USER_INFO_23 * usr,
+ prs_struct *ps, int depth)
+{
+ if (usr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_user_info23");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_time("logon_time ", &usr->logon_time, ps, depth))
+ return False;
+ if(!smb_io_time("logoff_time ", &usr->logoff_time, ps, depth))
+ return False;
+ if(!smb_io_time("kickoff_time ", &usr->kickoff_time, ps, depth))
+ return False;
+ if(!smb_io_time("pass_last_set_time ", &usr->pass_last_set_time, ps, depth))
+ return False;
+ if(!smb_io_time("pass_can_change_time ", &usr->pass_can_change_time, ps, depth))
+ return False;
+ if(!smb_io_time("pass_must_change_time", &usr->pass_must_change_time, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr("hdr_user_name ", &usr->hdr_user_name, ps, depth)) /* username unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_full_name ", &usr->hdr_full_name, ps, depth)) /* user's full name unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_home_dir ", &usr->hdr_home_dir, ps, depth)) /* home directory unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_dir_drive ", &usr->hdr_dir_drive, ps, depth)) /* home directory drive */
+ return False;
+ if(!smb_io_unihdr("hdr_logon_script", &usr->hdr_logon_script, ps, depth)) /* logon script unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_profile_path", &usr->hdr_profile_path, ps, depth)) /* profile path unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_acct_desc ", &usr->hdr_acct_desc, ps, depth)) /* account desc */
+ return False;
+ if(!smb_io_unihdr("hdr_workstations", &usr->hdr_workstations, ps, depth)) /* wkstas user can log on from */
+ return False;
+ if(!smb_io_unihdr("hdr_unknown_str ", &usr->hdr_unknown_str, ps, depth)) /* unknown string */
+ return False;
+ if(!smb_io_unihdr("hdr_munged_dial ", &usr->hdr_munged_dial, ps, depth)) /* wkstas user can log on from */
+ return False;
+
+ if(!prs_uint8s(False, "lm_pwd ", ps, depth, usr->lm_pwd, sizeof(usr->lm_pwd)))
+ return False;
+ if(!prs_uint8s(False, "nt_pwd ", ps, depth, usr->nt_pwd, sizeof(usr->nt_pwd)))
+ return False;
+
+ if(!prs_uint32("user_rid ", ps, depth, &usr->user_rid)) /* User ID */
+ return False;
+ if(!prs_uint32("group_rid ", ps, depth, &usr->group_rid)) /* Group ID */
+ return False;
+ if(!prs_uint32("acb_info ", ps, depth, &usr->acb_info))
+ return False;
+
+ if(!prs_uint32("fields_present ", ps, depth, &usr->fields_present))
+ return False;
+ if(!prs_uint16("logon_divs ", ps, depth, &usr->logon_divs)) /* logon divisions per week */
+ return False;
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("ptr_logon_hrs ", ps, depth, &usr->ptr_logon_hrs))
+ return False;
+
+ if(!prs_uint16("bad_password_count ", ps, depth, &usr->bad_password_count))
+ return False;
+ if(!prs_uint16("logon_count ", ps, depth, &usr->logon_count))
+ return False;
+
+ if(!prs_uint8s(False, "padding1 ", ps, depth, usr->padding1, sizeof(usr->padding1)))
+ return False;
+ if(!prs_uint8("passmustchange ", ps, depth, &usr->passmustchange))
+ return False;
+ if(!prs_uint8("padding2 ", ps, depth, &usr->padding2))
+ return False;
+
+
+ if(!prs_uint8s(False, "password ", ps, depth, usr->pass, sizeof(usr->pass)))
+ return False;
+
+ /* here begins pointed-to data */
+
+ if(!smb_io_unistr2("uni_user_name ", &usr->uni_user_name, usr->hdr_user_name.buffer, ps, depth)) /* username unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_full_name ", &usr->uni_full_name, usr->hdr_full_name.buffer, ps, depth)) /* user's full name unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_home_dir ", &usr->uni_home_dir, usr->hdr_home_dir.buffer, ps, depth)) /* home directory unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_dir_drive ", &usr->uni_dir_drive, usr->hdr_dir_drive.buffer, ps, depth)) /* home directory drive unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_logon_script", &usr->uni_logon_script, usr->hdr_logon_script.buffer, ps, depth)) /* logon script unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_profile_path", &usr->uni_profile_path, usr->hdr_profile_path.buffer, ps, depth)) /* profile path unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_acct_desc ", &usr->uni_acct_desc, usr->hdr_acct_desc.buffer, ps, depth)) /* user desc unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_workstations", &usr->uni_workstations, usr->hdr_workstations.buffer, ps, depth)) /* worksations user can log on from */
+ return False;
+
+ if(!smb_io_unistr2("uni_unknown_str ", &usr->uni_unknown_str, usr->hdr_unknown_str.buffer, ps, depth)) /* unknown string */
+ return False;
+
+ if(!smb_io_unistr2("uni_munged_dial ", &usr->uni_munged_dial, usr->hdr_munged_dial.buffer, ps, depth))
+ return False;
+
+ /* ok, this is only guess-work (as usual) */
+ if (usr->ptr_logon_hrs) {
+ if(!prs_uint32("unknown_6 ", ps, depth, &usr->unknown_6))
+ return False;
+ if(!prs_uint32("padding4 ", ps, depth, &usr->padding4))
+ return False;
+ if(!sam_io_logon_hrs("logon_hrs", &usr->logon_hrs, ps, depth))
+ return False;
+ } else if (UNMARSHALLING(ps)) {
+ usr->unknown_6 = 0;
+ usr->padding4 = 0;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ reads or writes a structure.
+ NB. This structure is *definately* incorrect. It's my best guess
+ currently for W2K SP2. The password field is encrypted in a different
+ way than normal... And there are definately other problems. JRA.
+********************************************************************/
+
+static BOOL sam_io_user_info25(const char *desc, SAM_USER_INFO_25 * usr, prs_struct *ps, int depth)
+{
+ if (usr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_user_info25");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_time("logon_time ", &usr->logon_time, ps, depth))
+ return False;
+ if(!smb_io_time("logoff_time ", &usr->logoff_time, ps, depth))
+ return False;
+ if(!smb_io_time("kickoff_time ", &usr->kickoff_time, ps, depth))
+ return False;
+ if(!smb_io_time("pass_last_set_time ", &usr->pass_last_set_time, ps, depth))
+ return False;
+ if(!smb_io_time("pass_can_change_time ", &usr->pass_can_change_time, ps, depth))
+ return False;
+ if(!smb_io_time("pass_must_change_time", &usr->pass_must_change_time, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr("hdr_user_name ", &usr->hdr_user_name, ps, depth)) /* username unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_full_name ", &usr->hdr_full_name, ps, depth)) /* user's full name unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_home_dir ", &usr->hdr_home_dir, ps, depth)) /* home directory unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_dir_drive ", &usr->hdr_dir_drive, ps, depth)) /* home directory drive */
+ return False;
+ if(!smb_io_unihdr("hdr_logon_script", &usr->hdr_logon_script, ps, depth)) /* logon script unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_profile_path", &usr->hdr_profile_path, ps, depth)) /* profile path unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_acct_desc ", &usr->hdr_acct_desc, ps, depth)) /* account desc */
+ return False;
+ if(!smb_io_unihdr("hdr_workstations", &usr->hdr_workstations, ps, depth)) /* wkstas user can log on from */
+ return False;
+ if(!smb_io_unihdr("hdr_unknown_str ", &usr->hdr_unknown_str, ps, depth)) /* unknown string */
+ return False;
+ if(!smb_io_unihdr("hdr_munged_dial ", &usr->hdr_munged_dial, ps, depth)) /* wkstas user can log on from */
+ return False;
+
+ if(!prs_uint8s(False, "lm_pwd ", ps, depth, usr->lm_pwd, sizeof(usr->lm_pwd)))
+ return False;
+ if(!prs_uint8s(False, "nt_pwd ", ps, depth, usr->nt_pwd, sizeof(usr->nt_pwd)))
+ return False;
+
+ if(!prs_uint32("user_rid ", ps, depth, &usr->user_rid)) /* User ID */
+ return False;
+ if(!prs_uint32("group_rid ", ps, depth, &usr->group_rid)) /* Group ID */
+ return False;
+ if(!prs_uint32("acb_info ", ps, depth, &usr->acb_info))
+ return False;
+
+ if(!prs_uint32s(False, "unknown_6 ", ps, depth, usr->unknown_6, 6))
+ return False;
+
+ if(!prs_uint8s(False, "password ", ps, depth, usr->pass, sizeof(usr->pass)))
+ return False;
+
+ /* here begins pointed-to data */
+
+ if(!smb_io_unistr2("uni_user_name ", &usr->uni_user_name, usr->hdr_user_name.buffer, ps, depth)) /* username unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_full_name ", &usr->uni_full_name, usr->hdr_full_name.buffer, ps, depth)) /* user's full name unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_home_dir ", &usr->uni_home_dir, usr->hdr_home_dir.buffer, ps, depth)) /* home directory unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_dir_drive ", &usr->uni_dir_drive, usr->hdr_dir_drive.buffer, ps, depth)) /* home directory drive unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_logon_script", &usr->uni_logon_script, usr->hdr_logon_script.buffer, ps, depth)) /* logon script unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_profile_path", &usr->uni_profile_path, usr->hdr_profile_path.buffer, ps, depth)) /* profile path unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_acct_desc ", &usr->uni_acct_desc, usr->hdr_acct_desc.buffer, ps, depth)) /* user desc unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_workstations", &usr->uni_workstations, usr->hdr_workstations.buffer, ps, depth)) /* worksations user can log on from */
+ return False;
+
+ if(!smb_io_unistr2("uni_unknown_str ", &usr->uni_unknown_str, usr->hdr_unknown_str.buffer, ps, depth)) /* unknown string */
+ return False;
+
+ if(!smb_io_unistr2("uni_munged_dial ", &usr->uni_munged_dial, usr->hdr_munged_dial.buffer, ps, depth))
+ return False;
+
+#if 0 /* JRA - unknown... */
+ /* ok, this is only guess-work (as usual) */
+ if (usr->ptr_logon_hrs) {
+ if(!prs_uint32("unknown_6 ", ps, depth, &usr->unknown_6))
+ return False;
+ if(!prs_uint32("padding4 ", ps, depth, &usr->padding4))
+ return False;
+ if(!sam_io_logon_hrs("logon_hrs", &usr->logon_hrs, ps, depth))
+ return False;
+ } else if (UNMARSHALLING(ps)) {
+ usr->unknown_6 = 0;
+ usr->padding4 = 0;
+ }
+#endif
+
+ return True;
+}
+
+
+/*************************************************************************
+ init_sam_user_info21W
+
+ unknown_6 = 0x0000 04ec
+
+ *************************************************************************/
+
+void init_sam_user_info21W(SAM_USER_INFO_21 * usr,
+ NTTIME * logon_time,
+ NTTIME * logoff_time,
+ NTTIME * kickoff_time,
+ NTTIME * pass_last_set_time,
+ NTTIME * pass_can_change_time,
+ NTTIME * pass_must_change_time,
+ UNISTR2 *user_name,
+ UNISTR2 *full_name,
+ UNISTR2 *home_dir,
+ UNISTR2 *dir_drive,
+ UNISTR2 *log_scr,
+ UNISTR2 *prof_path,
+ UNISTR2 *desc,
+ UNISTR2 *wkstas,
+ UNISTR2 *unk_str,
+ UNISTR2 *mung_dial,
+ uchar lm_pwd[16],
+ uchar nt_pwd[16],
+ uint32 user_rid,
+ uint32 group_rid,
+ uint32 acb_info,
+ uint32 fields_present,
+ uint16 logon_divs,
+ LOGON_HRS * hrs,
+ uint16 bad_password_count,
+ uint16 logon_count,
+ uint32 unknown_6)
+{
+ usr->logon_time = *logon_time;
+ usr->logoff_time = *logoff_time;
+ usr->kickoff_time = *kickoff_time;
+ usr->pass_last_set_time = *pass_last_set_time;
+ usr->pass_can_change_time = *pass_can_change_time;
+ usr->pass_must_change_time = *pass_must_change_time;
+
+ memcpy(usr->lm_pwd, lm_pwd, sizeof(usr->lm_pwd));
+ memcpy(usr->nt_pwd, nt_pwd, sizeof(usr->nt_pwd));
+
+ usr->user_rid = user_rid;
+ usr->group_rid = group_rid;
+ usr->acb_info = acb_info;
+ usr->fields_present = fields_present; /* 0x00ff ffff */
+
+ usr->logon_divs = logon_divs; /* should be 168 (hours/week) */
+ usr->ptr_logon_hrs = hrs ? 1 : 0;
+ usr->bad_password_count = bad_password_count;
+ usr->logon_count = logon_count;
+
+ if (nt_time_is_zero(pass_must_change_time)) {
+ usr->passmustchange=PASS_MUST_CHANGE_AT_NEXT_LOGON;
+ } else {
+ usr->passmustchange=0;
+ }
+
+ ZERO_STRUCT(usr->padding1);
+ ZERO_STRUCT(usr->padding2);
+
+ copy_unistr2(&usr->uni_user_name, user_name);
+ init_uni_hdr(&usr->hdr_user_name, &usr->uni_user_name);
+
+ copy_unistr2(&usr->uni_full_name, full_name);
+ init_uni_hdr(&usr->hdr_full_name, &usr->uni_full_name);
+
+ copy_unistr2(&usr->uni_home_dir, home_dir);
+ init_uni_hdr(&usr->hdr_home_dir, &usr->uni_home_dir);
+
+ copy_unistr2(&usr->uni_dir_drive, dir_drive);
+ init_uni_hdr(&usr->hdr_dir_drive, &usr->uni_dir_drive);
+
+ copy_unistr2(&usr->uni_logon_script, log_scr);
+ init_uni_hdr(&usr->hdr_logon_script, &usr->uni_logon_script);
+
+ copy_unistr2(&usr->uni_profile_path, prof_path);
+ init_uni_hdr(&usr->hdr_profile_path, &usr->uni_profile_path);
+
+ copy_unistr2(&usr->uni_acct_desc, desc);
+ init_uni_hdr(&usr->hdr_acct_desc, &usr->uni_acct_desc);
+
+ copy_unistr2(&usr->uni_workstations, wkstas);
+ init_uni_hdr(&usr->hdr_workstations, &usr->uni_workstations);
+
+ copy_unistr2(&usr->uni_unknown_str, unk_str);
+ init_uni_hdr(&usr->hdr_unknown_str, &usr->uni_unknown_str);
+
+ copy_unistr2(&usr->uni_munged_dial, mung_dial);
+ init_uni_hdr(&usr->hdr_munged_dial, &usr->uni_munged_dial);
+
+ usr->unknown_6 = unknown_6; /* 0x0000 04ec */
+ usr->padding4 = 0;
+
+ memcpy(&usr->logon_hrs, hrs, sizeof(usr->logon_hrs));
+}
+
+/*************************************************************************
+ init_sam_user_info21
+
+ unknown_6 = 0x0000 04ec
+
+ *************************************************************************/
+
+NTSTATUS init_sam_user_info21A(SAM_USER_INFO_21 *usr, SAM_ACCOUNT *pw, DOM_SID *domain_sid)
+{
+ NTTIME logon_time, logoff_time, kickoff_time,
+ pass_last_set_time, pass_can_change_time,
+ pass_must_change_time;
+
+ const char* user_name = pdb_get_username(pw);
+ const char* full_name = pdb_get_fullname(pw);
+ const char* home_dir = pdb_get_homedir(pw);
+ const char* dir_drive = pdb_get_dir_drive(pw);
+ const char* logon_script = pdb_get_logon_script(pw);
+ const char* profile_path = pdb_get_profile_path(pw);
+ const char* description = pdb_get_acct_desc(pw);
+ const char* workstations = pdb_get_workstations(pw);
+ const char* munged_dial = pdb_get_munged_dial(pw);
+ DATA_BLOB munged_dial_blob;
+
+ uint32 user_rid;
+ const DOM_SID *user_sid;
+
+ uint32 group_rid;
+ const DOM_SID *group_sid;
+
+ if (munged_dial) {
+ munged_dial_blob = base64_decode_data_blob(munged_dial);
+ } else {
+ munged_dial_blob = data_blob(NULL, 0);
+ }
+
+ /* Create NTTIME structs */
+ unix_to_nt_time (&logon_time, pdb_get_logon_time(pw));
+ unix_to_nt_time (&logoff_time, pdb_get_logoff_time(pw));
+ unix_to_nt_time (&kickoff_time, pdb_get_kickoff_time(pw));
+ unix_to_nt_time (&pass_last_set_time, pdb_get_pass_last_set_time(pw));
+ unix_to_nt_time (&pass_can_change_time, pdb_get_pass_can_change_time(pw));
+ unix_to_nt_time (&pass_must_change_time,pdb_get_pass_must_change_time(pw));
+
+ /* structure assignment */
+ usr->logon_time = logon_time;
+ usr->logoff_time = logoff_time;
+ usr->kickoff_time = kickoff_time;
+ usr->pass_last_set_time = pass_last_set_time;
+ usr->pass_can_change_time = pass_can_change_time;
+ usr->pass_must_change_time = pass_must_change_time;
+
+ ZERO_STRUCT(usr->nt_pwd);
+ ZERO_STRUCT(usr->lm_pwd);
+
+ user_sid = pdb_get_user_sid(pw);
+
+ if (!sid_peek_check_rid(domain_sid, user_sid, &user_rid)) {
+ fstring user_sid_string;
+ fstring domain_sid_string;
+ DEBUG(0, ("init_sam_user_info_21A: User %s has SID %s, \nwhich conflicts with "
+ "the domain sid %s. Failing operation.\n",
+ user_name,
+ sid_to_string(user_sid_string, user_sid),
+ sid_to_string(domain_sid_string, domain_sid)));
+ data_blob_free(&munged_dial_blob);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ group_sid = pdb_get_group_sid(pw);
+
+ if (!sid_peek_check_rid(domain_sid, group_sid, &group_rid)) {
+ fstring group_sid_string;
+ fstring domain_sid_string;
+ DEBUG(0, ("init_sam_user_info_21A: User %s has Primary Group SID %s, \n"
+ "which conflicts with the domain sid %s. Failing operation.\n",
+ user_name,
+ sid_to_string(group_sid_string, group_sid),
+ sid_to_string(domain_sid_string, domain_sid)));
+ data_blob_free(&munged_dial_blob);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ usr->user_rid = user_rid;
+ usr->group_rid = group_rid;
+ usr->acb_info = pdb_get_acct_ctrl(pw);
+
+ /*
+ Look at a user on a real NT4 PDC with usrmgr, press
+ 'ok'. Then you will see that fields_present is set to
+ 0x08f827fa. Look at the user immediately after that again,
+ and you will see that 0x00fffff is returned. This solves
+ the problem that you get access denied after having looked
+ at the user.
+ -- Volker
+ */
+ usr->fields_present = pdb_build_fields_present(pw);
+
+ usr->logon_divs = pdb_get_logon_divs(pw);
+ usr->ptr_logon_hrs = pdb_get_hours(pw) ? 1 : 0;
+ usr->bad_password_count = pdb_get_bad_password_count(pw);
+ usr->logon_count = pdb_get_logon_count(pw);
+
+ if (pdb_get_pass_must_change_time(pw) == 0) {
+ usr->passmustchange=PASS_MUST_CHANGE_AT_NEXT_LOGON;
+ } else {
+ usr->passmustchange=0;
+ }
+
+ ZERO_STRUCT(usr->padding1);
+ ZERO_STRUCT(usr->padding2);
+
+ init_unistr2(&usr->uni_user_name, user_name, UNI_STR_TERMINATE);
+ init_uni_hdr(&usr->hdr_user_name, &usr->uni_user_name);
+
+ init_unistr2(&usr->uni_full_name, full_name, UNI_STR_TERMINATE);
+ init_uni_hdr(&usr->hdr_full_name, &usr->uni_full_name);
+
+ init_unistr2(&usr->uni_home_dir, home_dir, UNI_STR_TERMINATE);
+ init_uni_hdr(&usr->hdr_home_dir, &usr->uni_home_dir);
+
+ init_unistr2(&usr->uni_dir_drive, dir_drive, UNI_STR_TERMINATE);
+ init_uni_hdr(&usr->hdr_dir_drive, &usr->uni_dir_drive);
+
+ init_unistr2(&usr->uni_logon_script, logon_script, UNI_STR_TERMINATE);
+ init_uni_hdr(&usr->hdr_logon_script, &usr->uni_logon_script);
+
+ init_unistr2(&usr->uni_profile_path, profile_path, UNI_STR_TERMINATE);
+ init_uni_hdr(&usr->hdr_profile_path, &usr->uni_profile_path);
+
+ init_unistr2(&usr->uni_acct_desc, description, UNI_STR_TERMINATE);
+ init_uni_hdr(&usr->hdr_acct_desc, &usr->uni_acct_desc);
+
+ init_unistr2(&usr->uni_workstations, workstations, UNI_STR_TERMINATE);
+ init_uni_hdr(&usr->hdr_workstations, &usr->uni_workstations);
+
+ init_unistr2(&usr->uni_unknown_str, NULL, UNI_STR_TERMINATE);
+ init_uni_hdr(&usr->hdr_unknown_str, &usr->uni_unknown_str);
+
+ init_unistr2_from_datablob(&usr->uni_munged_dial, &munged_dial_blob);
+ init_uni_hdr(&usr->hdr_munged_dial, &usr->uni_munged_dial);
+ data_blob_free(&munged_dial_blob);
+
+ usr->unknown_6 = pdb_get_unknown_6(pw);
+ usr->padding4 = 0;
+
+ if (pdb_get_hours(pw)) {
+ usr->logon_hrs.len = pdb_get_hours_len(pw);
+ memcpy(&usr->logon_hrs.hours, pdb_get_hours(pw), MAX_HOURS_LEN);
+ } else
+ memset(&usr->logon_hrs, 0xff, sizeof(usr->logon_hrs));
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_user_info21(const char *desc, SAM_USER_INFO_21 * usr,
+ prs_struct *ps, int depth)
+{
+ if (usr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_user_info21");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_time("logon_time ", &usr->logon_time, ps, depth))
+ return False;
+ if(!smb_io_time("logoff_time ", &usr->logoff_time, ps, depth))
+ return False;
+ if(!smb_io_time("pass_last_set_time ", &usr->pass_last_set_time, ps,depth))
+ return False;
+ if(!smb_io_time("kickoff_time ", &usr->kickoff_time, ps, depth))
+ return False;
+ if(!smb_io_time("pass_can_change_time ", &usr->pass_can_change_time, ps,depth))
+ return False;
+ if(!smb_io_time("pass_must_change_time", &usr->pass_must_change_time, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr("hdr_user_name ", &usr->hdr_user_name, ps, depth)) /* username unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_full_name ", &usr->hdr_full_name, ps, depth)) /* user's full name unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_home_dir ", &usr->hdr_home_dir, ps, depth)) /* home directory unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_dir_drive ", &usr->hdr_dir_drive, ps, depth)) /* home directory drive */
+ return False;
+ if(!smb_io_unihdr("hdr_logon_script", &usr->hdr_logon_script, ps, depth)) /* logon script unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_profile_path", &usr->hdr_profile_path, ps, depth)) /* profile path unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_acct_desc ", &usr->hdr_acct_desc, ps, depth)) /* account desc */
+ return False;
+ if(!smb_io_unihdr("hdr_workstations", &usr->hdr_workstations, ps, depth)) /* wkstas user can log on from */
+ return False;
+ if(!smb_io_unihdr("hdr_unknown_str ", &usr->hdr_unknown_str, ps, depth)) /* unknown string */
+ return False;
+ if(!smb_io_unihdr("hdr_munged_dial ", &usr->hdr_munged_dial, ps, depth)) /* wkstas user can log on from */
+ return False;
+
+ if(!prs_uint8s(False, "lm_pwd ", ps, depth, usr->lm_pwd, sizeof(usr->lm_pwd)))
+ return False;
+ if(!prs_uint8s(False, "nt_pwd ", ps, depth, usr->nt_pwd, sizeof(usr->nt_pwd)))
+ return False;
+
+ if(!prs_uint32("user_rid ", ps, depth, &usr->user_rid)) /* User ID */
+ return False;
+ if(!prs_uint32("group_rid ", ps, depth, &usr->group_rid)) /* Group ID */
+ return False;
+ if(!prs_uint32("acb_info ", ps, depth, &usr->acb_info))
+ return False;
+
+ if(!prs_uint32("fields_present ", ps, depth, &usr->fields_present))
+ return False;
+ if(!prs_uint16("logon_divs ", ps, depth, &usr->logon_divs)) /* logon divisions per week */
+ return False;
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("ptr_logon_hrs ", ps, depth, &usr->ptr_logon_hrs))
+ return False;
+
+ if(!prs_uint16("bad_password_count ", ps, depth, &usr->bad_password_count))
+ return False;
+ if(!prs_uint16("logon_count ", ps, depth, &usr->logon_count))
+ return False;
+
+ if(!prs_uint8s(False, "padding1 ", ps, depth, usr->padding1, sizeof(usr->padding1)))
+ return False;
+ if(!prs_uint8("passmustchange ", ps, depth, &usr->passmustchange))
+ return False;
+ if(!prs_uint8("padding2 ", ps, depth, &usr->padding2))
+ return False;
+
+ /* here begins pointed-to data */
+
+ if(!smb_io_unistr2("uni_user_name ", &usr->uni_user_name,usr->hdr_user_name.buffer, ps, depth)) /* username unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_full_name ", &usr->uni_full_name, usr->hdr_full_name.buffer, ps, depth)) /* user's full name unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_home_dir ", &usr->uni_home_dir, usr->hdr_home_dir.buffer, ps, depth)) /* home directory unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_dir_drive ", &usr->uni_dir_drive, usr->hdr_dir_drive.buffer, ps, depth)) /* home directory drive unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_logon_script", &usr->uni_logon_script, usr->hdr_logon_script.buffer, ps, depth)) /* logon script unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_profile_path", &usr->uni_profile_path, usr->hdr_profile_path.buffer, ps, depth)) /* profile path unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_acct_desc ", &usr->uni_acct_desc, usr->hdr_acct_desc.buffer, ps, depth)) /* user desc unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_workstations", &usr->uni_workstations, usr->hdr_workstations.buffer, ps, depth)) /* worksations user can log on from */
+ return False;
+ if(!smb_io_unistr2("uni_unknown_str ", &usr->uni_unknown_str, usr->hdr_unknown_str.buffer, ps, depth)) /* unknown string */
+ return False;
+ if(!smb_io_unistr2("uni_munged_dial ", &usr->uni_munged_dial,usr->hdr_munged_dial.buffer, ps, depth)) /* worksations user can log on from */
+ return False;
+
+ /* ok, this is only guess-work (as usual) */
+ if (usr->ptr_logon_hrs) {
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("unknown_6 ", ps, depth, &usr->unknown_6))
+ return False;
+ if(!prs_uint32("padding4 ", ps, depth, &usr->padding4))
+ return False;
+ if(!sam_io_logon_hrs("logon_hrs", &usr->logon_hrs, ps, depth))
+ return False;
+ } else if (UNMARSHALLING(ps)) {
+ usr->unknown_6 = 0;
+ usr->padding4 = 0;
+ }
+
+ return True;
+}
+
+void init_sam_user_info20A(SAM_USER_INFO_20 *usr, SAM_ACCOUNT *pw)
+{
+ const char *munged_dial = pdb_get_munged_dial(pw);
+ DATA_BLOB blob = base64_decode_data_blob(munged_dial);
+
+ init_unistr2_from_datablob(&usr->uni_munged_dial, &blob);
+ init_uni_hdr(&usr->hdr_munged_dial, &usr->uni_munged_dial);
+ data_blob_free(&blob);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_user_info20(const char *desc, SAM_USER_INFO_20 *usr,
+ prs_struct *ps, int depth)
+{
+ if (usr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_user_info20");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unihdr("hdr_munged_dial ", &usr->hdr_munged_dial, ps, depth)) /* wkstas user can log on from */
+ return False;
+
+ if(!smb_io_unistr2("uni_munged_dial ", &usr->uni_munged_dial,usr->hdr_munged_dial.buffer, ps, depth)) /* worksations user can log on from */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_USERINFO_CTR structure.
+********************************************************************/
+
+NTSTATUS make_samr_userinfo_ctr_usr21(TALLOC_CTX *ctx, SAM_USERINFO_CTR * ctr,
+ uint16 switch_value,
+ SAM_USER_INFO_21 * usr)
+{
+ DEBUG(5, ("init_samr_userinfo_ctr\n"));
+
+ ctr->switch_value = switch_value;
+ ctr->info.id = NULL;
+
+ switch (switch_value) {
+ case 0x10:
+ ctr->info.id10 = (SAM_USER_INFO_10 *)talloc_zero(ctx,sizeof(SAM_USER_INFO_10));
+ if (ctr->info.id10 == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ init_sam_user_info10(ctr->info.id10, usr->acb_info);
+ break;
+#if 0
+/* whoops - got this wrong. i think. or don't understand what's happening. */
+ case 0x11:
+ {
+ NTTIME expire;
+ info = (void *)&id11;
+
+ expire.low = 0xffffffff;
+ expire.high = 0x7fffffff;
+
+ ctr->info.id = (SAM_USER_INFO_11 *) talloc_zero(ctx,sizeof(*ctr->info.id11));
+ init_sam_user_info11(ctr->info.id11, &expire,
+ "BROOKFIELDS$", /* name */
+ 0x03ef, /* user rid */
+ 0x201, /* group rid */
+ 0x0080); /* acb info */
+
+ break;
+ }
+#endif
+ case 0x12:
+ ctr->info.id12 = (SAM_USER_INFO_12 *)talloc_zero(ctx,sizeof(SAM_USER_INFO_12));
+ if (ctr->info.id12 == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ init_sam_user_info12(ctr->info.id12, usr->lm_pwd, usr->nt_pwd);
+ break;
+ case 21:
+ {
+ SAM_USER_INFO_21 *cusr;
+ cusr = (SAM_USER_INFO_21 *)talloc_zero(ctx,sizeof(SAM_USER_INFO_21));
+ ctr->info.id21 = cusr;
+ if (ctr->info.id21 == NULL)
+ return NT_STATUS_NO_MEMORY;
+ memcpy(cusr, usr, sizeof(*usr));
+ memset(cusr->lm_pwd, 0, sizeof(cusr->lm_pwd));
+ memset(cusr->nt_pwd, 0, sizeof(cusr->nt_pwd));
+ break;
+ }
+ default:
+ DEBUG(4,("make_samr_userinfo_ctr: unsupported info\n"));
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+inits a SAM_USERINFO_CTR structure.
+********************************************************************/
+
+static void init_samr_userinfo_ctr(SAM_USERINFO_CTR * ctr, DATA_BLOB *sess_key,
+ uint16 switch_value, void *info)
+{
+ DEBUG(5, ("init_samr_userinfo_ctr\n"));
+
+ ctr->switch_value = switch_value;
+ ctr->info.id = info;
+
+ switch (switch_value) {
+ case 0x18:
+ SamOEMhashBlob(ctr->info.id24->pass, 516, sess_key);
+ dump_data(100, (char *)sess_key->data, sess_key->length);
+ dump_data(100, (char *)ctr->info.id24->pass, 516);
+ break;
+ case 0x17:
+ SamOEMhashBlob(ctr->info.id23->pass, 516, sess_key);
+ dump_data(100, (char *)sess_key->data, sess_key->length);
+ dump_data(100, (char *)ctr->info.id23->pass, 516);
+ break;
+ default:
+ DEBUG(4,("init_samr_userinfo_ctr: unsupported switch level\n"));
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL samr_io_userinfo_ctr(const char *desc, SAM_USERINFO_CTR **ppctr,
+ prs_struct *ps, int depth)
+{
+ BOOL ret;
+ SAM_USERINFO_CTR *ctr;
+
+ prs_debug(ps, depth, desc, "samr_io_userinfo_ctr");
+ depth++;
+
+ if (UNMARSHALLING(ps)) {
+ ctr = (SAM_USERINFO_CTR *)prs_alloc_mem(ps,sizeof(SAM_USERINFO_CTR));
+ if (ctr == NULL)
+ return False;
+ *ppctr = ctr;
+ } else {
+ ctr = *ppctr;
+ }
+
+ /* lkclXXXX DO NOT ALIGN BEFORE READING SWITCH VALUE! */
+
+ if(!prs_uint16("switch_value", ps, depth, &ctr->switch_value))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ ret = False;
+
+ switch (ctr->switch_value) {
+ case 0x10:
+ if (UNMARSHALLING(ps))
+ ctr->info.id10 = (SAM_USER_INFO_10 *)prs_alloc_mem(ps,sizeof(SAM_USER_INFO_10));
+ if (ctr->info.id10 == NULL) {
+ DEBUG(2,("samr_io_userinfo_ctr: info pointer not initialised\n"));
+ return False;
+ }
+ ret = sam_io_user_info10("", ctr->info.id10, ps, depth);
+ break;
+ case 0x11:
+ if (UNMARSHALLING(ps))
+ ctr->info.id11 = (SAM_USER_INFO_11 *)prs_alloc_mem(ps,sizeof(SAM_USER_INFO_11));
+
+ if (ctr->info.id11 == NULL) {
+ DEBUG(2,("samr_io_userinfo_ctr: info pointer not initialised\n"));
+ return False;
+ }
+ ret = sam_io_user_info11("", ctr->info.id11, ps, depth);
+ break;
+ case 0x12:
+ if (UNMARSHALLING(ps))
+ ctr->info.id12 = (SAM_USER_INFO_12 *)prs_alloc_mem(ps,sizeof(SAM_USER_INFO_12));
+
+ if (ctr->info.id12 == NULL) {
+ DEBUG(2,("samr_io_userinfo_ctr: info pointer not initialised\n"));
+ return False;
+ }
+ ret = sam_io_user_info12("", ctr->info.id12, ps, depth);
+ break;
+ case 20:
+ if (UNMARSHALLING(ps))
+ ctr->info.id20 = (SAM_USER_INFO_20 *)prs_alloc_mem(ps,sizeof(SAM_USER_INFO_20));
+
+ if (ctr->info.id20 == NULL) {
+ DEBUG(2,("samr_io_userinfo_ctr: info pointer not initialised\n"));
+ return False;
+ }
+ ret = sam_io_user_info20("", ctr->info.id20, ps, depth);
+ break;
+ case 21:
+ if (UNMARSHALLING(ps))
+ ctr->info.id21 = (SAM_USER_INFO_21 *)prs_alloc_mem(ps,sizeof(SAM_USER_INFO_21));
+
+ if (ctr->info.id21 == NULL) {
+ DEBUG(2,("samr_io_userinfo_ctr: info pointer not initialised\n"));
+ return False;
+ }
+ ret = sam_io_user_info21("", ctr->info.id21, ps, depth);
+ break;
+ case 23:
+ if (UNMARSHALLING(ps))
+ ctr->info.id23 = (SAM_USER_INFO_23 *)prs_alloc_mem(ps,sizeof(SAM_USER_INFO_23));
+
+ if (ctr->info.id23 == NULL) {
+ DEBUG(2,("samr_io_userinfo_ctr: info pointer not initialised\n"));
+ return False;
+ }
+ ret = sam_io_user_info23("", ctr->info.id23, ps, depth);
+ break;
+ case 24:
+ if (UNMARSHALLING(ps))
+ ctr->info.id24 = (SAM_USER_INFO_24 *)prs_alloc_mem(ps,sizeof(SAM_USER_INFO_24));
+
+ if (ctr->info.id24 == NULL) {
+ DEBUG(2,("samr_io_userinfo_ctr: info pointer not initialised\n"));
+ return False;
+ }
+ ret = sam_io_user_info24("", ctr->info.id24, ps, depth);
+ break;
+ case 25:
+ if (UNMARSHALLING(ps))
+ ctr->info.id25 = (SAM_USER_INFO_25 *)prs_alloc_mem(ps,sizeof(SAM_USER_INFO_25));
+
+ if (ctr->info.id25 == NULL) {
+ DEBUG(2,("samr_io_userinfo_ctr: info pointer not initialised\n"));
+ return False;
+ }
+ ret = sam_io_user_info25("", ctr->info.id25, ps, depth);
+ break;
+ default:
+ DEBUG(2, ("samr_io_userinfo_ctr: unknown switch level 0x%x\n", ctr->switch_value));
+ ret = False;
+ break;
+ }
+
+ return ret;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_USERINFO structure.
+********************************************************************/
+
+void init_samr_r_query_userinfo(SAMR_R_QUERY_USERINFO * r_u,
+ SAM_USERINFO_CTR * ctr, NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_query_userinfo\n"));
+
+ r_u->ptr = 0;
+ r_u->ctr = NULL;
+
+ if (NT_STATUS_IS_OK(status)) {
+ r_u->ptr = 1;
+ r_u->ctr = ctr;
+ }
+
+ r_u->status = status; /* return status */
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_query_userinfo(const char *desc, SAMR_R_QUERY_USERINFO * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_query_userinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr", ps, depth, &r_u->ptr))
+ return False;
+
+ if (r_u->ptr != 0) {
+ if(!samr_io_userinfo_ctr("ctr", &r_u->ctr, ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_SET_USERINFO structure.
+********************************************************************/
+
+void init_samr_q_set_userinfo(SAMR_Q_SET_USERINFO * q_u,
+ POLICY_HND *hnd, DATA_BLOB *sess_key,
+ uint16 switch_value, void *info)
+{
+ DEBUG(5, ("init_samr_q_set_userinfo\n"));
+
+ q_u->pol = *hnd;
+ q_u->switch_value = switch_value;
+ init_samr_userinfo_ctr(q_u->ctr, sess_key, switch_value, info);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_set_userinfo(const char *desc, SAMR_Q_SET_USERINFO * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_set_userinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ smb_io_pol_hnd("pol", &(q_u->pol), ps, depth);
+
+ if(!prs_uint16("switch_value", ps, depth, &q_u->switch_value))
+ return False;
+ if(!samr_io_userinfo_ctr("ctr", &q_u->ctr, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_SET_USERINFO structure.
+********************************************************************/
+
+void init_samr_r_set_userinfo(SAMR_R_SET_USERINFO * r_u, NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_set_userinfo\n"));
+
+ r_u->status = status; /* return status */
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_set_userinfo(const char *desc, SAMR_R_SET_USERINFO * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_set_userinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_SET_USERINFO2 structure.
+********************************************************************/
+
+void init_samr_q_set_userinfo2(SAMR_Q_SET_USERINFO2 * q_u,
+ POLICY_HND *hnd, DATA_BLOB *sess_key,
+ uint16 switch_value, SAM_USERINFO_CTR * ctr)
+{
+ DEBUG(5, ("init_samr_q_set_userinfo2\n"));
+
+ q_u->pol = *hnd;
+ q_u->switch_value = switch_value;
+ q_u->ctr = ctr;
+
+ if (q_u->ctr != NULL)
+ q_u->ctr->switch_value = switch_value;
+
+ switch (switch_value) {
+ case 0x12:
+ SamOEMhashBlob(ctr->info.id12->lm_pwd, 16, sess_key);
+ SamOEMhashBlob(ctr->info.id12->nt_pwd, 16, sess_key);
+ dump_data(100, (char *)sess_key->data, sess_key->length);
+ dump_data(100, (char *)ctr->info.id12->lm_pwd, 16);
+ dump_data(100, (char *)ctr->info.id12->nt_pwd, 16);
+ break;
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_set_userinfo2(const char *desc, SAMR_Q_SET_USERINFO2 * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_set_userinfo2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_u->pol, ps, depth))
+ return False;
+
+ if(!prs_uint16("switch_value", ps, depth, &q_u->switch_value))
+ return False;
+ if(!samr_io_userinfo_ctr("ctr", &q_u->ctr, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_SET_USERINFO2 structure.
+********************************************************************/
+
+void init_samr_r_set_userinfo2(SAMR_R_SET_USERINFO2 * r_u, NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_set_userinfo2\n"));
+
+ r_u->status = status; /* return status */
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_set_userinfo2(const char *desc, SAMR_R_SET_USERINFO2 * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_set_userinfo2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_CONNECT structure.
+********************************************************************/
+
+void init_samr_q_connect(SAMR_Q_CONNECT * q_u,
+ char *srv_name, uint32 access_mask)
+{
+ DEBUG(5, ("init_samr_q_connect\n"));
+
+ /* make PDC server name \\server */
+ q_u->ptr_srv_name = (srv_name != NULL && *srv_name) ? 1 : 0;
+ init_unistr2(&q_u->uni_srv_name, srv_name, UNI_STR_TERMINATE);
+
+ /* example values: 0x0000 0002 */
+ q_u->access_mask = access_mask;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_connect(const char *desc, SAMR_Q_CONNECT * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_connect");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_u->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_u->uni_srv_name, q_u->ptr_srv_name, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("access_mask", ps, depth, &q_u->access_mask))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_connect(const char *desc, SAMR_R_CONNECT * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_connect");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("connect_pol", &r_u->connect_pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_CONNECT4 structure.
+********************************************************************/
+
+void init_samr_q_connect4(SAMR_Q_CONNECT4 * q_u,
+ char *srv_name, uint32 access_mask)
+{
+ DEBUG(5, ("init_samr_q_connect\n"));
+
+ /* make PDC server name \\server */
+ q_u->ptr_srv_name = (srv_name != NULL && *srv_name) ? 1 : 0;
+ init_unistr2(&q_u->uni_srv_name, srv_name, UNI_STR_TERMINATE);
+
+ /* Only value we've seen, possibly an address type ? */
+ q_u->unk_0 = 2;
+
+ /* example values: 0x0000 0002 */
+ q_u->access_mask = access_mask;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_connect4(const char *desc, SAMR_Q_CONNECT4 * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_connect4");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_u->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_u->uni_srv_name, q_u->ptr_srv_name, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("unk_0", ps, depth, &q_u->unk_0))
+ return False;
+ if(!prs_uint32("access_mask", ps, depth, &q_u->access_mask))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_connect4(const char *desc, SAMR_R_CONNECT4 * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_connect4");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("connect_pol", &r_u->connect_pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_CONNECT_ANON structure.
+********************************************************************/
+
+void init_samr_q_connect_anon(SAMR_Q_CONNECT_ANON * q_u)
+{
+ DEBUG(5, ("init_samr_q_connect_anon\n"));
+
+ q_u->ptr = 1;
+ q_u->unknown_0 = 0x5c; /* server name (?!!) */
+ q_u->unknown_1 = 0x01;
+ q_u->access_mask = 0x20;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_connect_anon(const char *desc, SAMR_Q_CONNECT_ANON * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_connect_anon");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr ", ps, depth, &q_u->ptr))
+ return False;
+ if(!prs_uint16("unknown_0", ps, depth, &q_u->unknown_0))
+ return False;
+ if(!prs_uint16("unknown_1", ps, depth, &q_u->unknown_1))
+ return False;
+ if(!prs_uint32("access_mask", ps, depth, &q_u->access_mask))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_connect_anon(const char *desc, SAMR_R_CONNECT_ANON * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_connect_anon");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("connect_pol", &r_u->connect_pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_GET_DOM_PWINFO structure.
+********************************************************************/
+
+void init_samr_q_get_dom_pwinfo(SAMR_Q_GET_DOM_PWINFO * q_u,
+ char *srv_name)
+{
+ DEBUG(5, ("init_samr_q_get_dom_pwinfo\n"));
+
+ q_u->ptr = 1;
+ init_unistr2(&q_u->uni_srv_name, srv_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&q_u->hdr_srv_name, &q_u->uni_srv_name);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_get_dom_pwinfo(const char *desc, SAMR_Q_GET_DOM_PWINFO * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_get_dom_pwinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr", ps, depth, &q_u->ptr))
+ return False;
+ if (q_u->ptr != 0) {
+ if(!smb_io_unihdr("", &q_u->hdr_srv_name, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &q_u->uni_srv_name, q_u->hdr_srv_name.buffer, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_get_dom_pwinfo(const char *desc, SAMR_R_GET_DOM_PWINFO * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_get_dom_pwinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ /*
+ * We need 16 bytes here according to tests. Don't know
+ * what they are, but the length is important for the singing
+ */
+
+ if(!prs_uint32("unk_0", ps, depth, &r_u->unk_0))
+ return False;
+ if(!prs_uint32("unk_1", ps, depth, &r_u->unk_1))
+ return False;
+ if(!prs_uint32("unk_2", ps, depth, &r_u->unk_2))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+make a SAMR_ENC_PASSWD structure.
+********************************************************************/
+
+void init_enc_passwd(SAMR_ENC_PASSWD * pwd, const char pass[512])
+{
+ ZERO_STRUCTP(pwd);
+
+ if (pass == NULL) {
+ pwd->ptr = 0;
+ } else {
+ pwd->ptr = 1;
+ memcpy(pwd->pass, pass, sizeof(pwd->pass));
+ }
+}
+
+/*******************************************************************
+reads or writes a SAMR_ENC_PASSWD structure.
+********************************************************************/
+
+BOOL samr_io_enc_passwd(const char *desc, SAMR_ENC_PASSWD * pwd,
+ prs_struct *ps, int depth)
+{
+ if (pwd == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_enc_passwd");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr", ps, depth, &pwd->ptr))
+ return False;
+
+ if (pwd->ptr != 0) {
+ if(!prs_uint8s(False, "pwd", ps, depth, pwd->pass, sizeof(pwd->pass)))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_ENC_HASH structure.
+********************************************************************/
+
+void init_enc_hash(SAMR_ENC_HASH * hsh, const uchar hash[16])
+{
+ ZERO_STRUCTP(hsh);
+
+ if (hash == NULL) {
+ hsh->ptr = 0;
+ } else {
+ hsh->ptr = 1;
+ memcpy(hsh->hash, hash, sizeof(hsh->hash));
+ }
+}
+
+/*******************************************************************
+reads or writes a SAMR_ENC_HASH structure.
+********************************************************************/
+
+BOOL samr_io_enc_hash(const char *desc, SAMR_ENC_HASH * hsh,
+ prs_struct *ps, int depth)
+{
+ if (hsh == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_enc_hash");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr ", ps, depth, &hsh->ptr))
+ return False;
+ if (hsh->ptr != 0) {
+ if(!prs_uint8s(False, "hash", ps, depth, hsh->hash,sizeof(hsh->hash)))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_GET_DOM_PWINFO structure.
+********************************************************************/
+
+void init_samr_q_chgpasswd_user(SAMR_Q_CHGPASSWD_USER * q_u,
+ const char *dest_host, const char *user_name,
+ const char nt_newpass[516],
+ const uchar nt_oldhash[16],
+ const char lm_newpass[516],
+ const uchar lm_oldhash[16])
+{
+ DEBUG(5, ("init_samr_q_chgpasswd_user\n"));
+
+ q_u->ptr_0 = 1;
+ init_unistr2(&q_u->uni_dest_host, dest_host, UNI_FLAGS_NONE);
+ init_uni_hdr(&q_u->hdr_dest_host, &q_u->uni_dest_host);
+
+ init_unistr2(&q_u->uni_user_name, user_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&q_u->hdr_user_name, &q_u->uni_user_name);
+
+ init_enc_passwd(&q_u->nt_newpass, nt_newpass);
+ init_enc_hash(&q_u->nt_oldhash, nt_oldhash);
+
+ q_u->unknown = 0x01;
+
+ init_enc_passwd(&q_u->lm_newpass, lm_newpass);
+ init_enc_hash(&q_u->lm_oldhash, lm_oldhash);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_chgpasswd_user(const char *desc, SAMR_Q_CHGPASSWD_USER * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_chgpasswd_user");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_0", ps, depth, &q_u->ptr_0))
+ return False;
+
+ if(!smb_io_unihdr("", &q_u->hdr_dest_host, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &q_u->uni_dest_host, q_u->hdr_dest_host.buffer, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unihdr("", &q_u->hdr_user_name, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &q_u->uni_user_name, q_u->hdr_user_name.buffer,ps, depth))
+ return False;
+
+ if(!samr_io_enc_passwd("nt_newpass", &q_u->nt_newpass, ps, depth))
+ return False;
+ if(!samr_io_enc_hash("nt_oldhash", &q_u->nt_oldhash, ps, depth))
+ return False;
+
+ if(!prs_uint32("unknown", ps, depth, &q_u->unknown))
+ return False;
+
+ if(!samr_io_enc_passwd("lm_newpass", &q_u->lm_newpass, ps, depth))
+ return False;
+ if(!samr_io_enc_hash("lm_oldhash", &q_u->lm_oldhash, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_CHGPASSWD_USER structure.
+********************************************************************/
+
+void init_samr_r_chgpasswd_user(SAMR_R_CHGPASSWD_USER * r_u, NTSTATUS status)
+{
+ DEBUG(5, ("init_r_chgpasswd_user\n"));
+
+ r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_chgpasswd_user(const char *desc, SAMR_R_CHGPASSWD_USER * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_chgpasswd_user");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+void init_samr_q_unknown_2e(SAMR_Q_UNKNOWN_2E *q_u,
+ POLICY_HND *domain_pol, uint16 switch_value)
+{
+ DEBUG(5, ("init_samr_q_unknown_2e\n"));
+
+ q_u->domain_pol = *domain_pol;
+ q_u->switch_value = switch_value;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_unknown_2e(const char *desc, SAMR_Q_UNKNOWN_2E *q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_unknown_2e");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("domain_pol", &q_u->domain_pol, ps, depth))
+ return False;
+
+ if(!prs_uint16("switch_value", ps, depth, &q_u->switch_value))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_DOMAIN_INFO structure.
+********************************************************************/
+
+void init_samr_r_samr_unknown_2e(SAMR_R_UNKNOWN_2E * r_u,
+ uint16 switch_value, SAM_UNK_CTR * ctr,
+ NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_samr_unknown_2e\n"));
+
+ r_u->ptr_0 = 0;
+ r_u->switch_value = 0;
+ r_u->status = status; /* return status */
+
+ if (NT_STATUS_IS_OK(status)) {
+ r_u->switch_value = switch_value;
+ r_u->ptr_0 = 1;
+ r_u->ctr = ctr;
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_samr_unknown_2e(const char *desc, SAMR_R_UNKNOWN_2E * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_samr_unknown_2e");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_0 ", ps, depth, &r_u->ptr_0))
+ return False;
+
+ if (r_u->ptr_0 != 0 && r_u->ctr != NULL) {
+ if(!prs_uint16("switch_value", ps, depth, &r_u->switch_value))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ switch (r_u->switch_value) {
+ case 0x0c:
+ if(!sam_io_unk_info12("unk_inf12", &r_u->ctr->info.inf12, ps, depth))
+ return False;
+ break;
+ case 0x07:
+ if(!sam_io_unk_info7("unk_inf7",&r_u->ctr->info.inf7, ps,depth))
+ return False;
+ break;
+ case 0x06:
+ if(!sam_io_unk_info6("unk_inf6",&r_u->ctr->info.inf6, ps,depth))
+ return False;
+ break;
+ case 0x05:
+ if(!sam_io_unk_info5("unk_inf5",&r_u->ctr->info.inf5, ps,depth))
+ return False;
+ break;
+ case 0x03:
+ if(!sam_io_unk_info3("unk_inf3",&r_u->ctr->info.inf3, ps,depth))
+ return False;
+ break;
+ case 0x02:
+ if(!sam_io_unk_info2("unk_inf2",&r_u->ctr->info.inf2, ps,depth))
+ return False;
+ break;
+ case 0x01:
+ if(!sam_io_unk_info1("unk_inf1",&r_u->ctr->info.inf1, ps,depth))
+ return False;
+ break;
+ default:
+ DEBUG(0, ("samr_io_r_samr_unknown_2e: unknown switch level 0x%x\n",
+ r_u->switch_value));
+ r_u->status = NT_STATUS_INVALID_INFO_CLASS;
+ return False;
+ }
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+void init_samr_q_set_domain_info(SAMR_Q_SET_DOMAIN_INFO *q_u,
+ POLICY_HND *domain_pol, uint16 switch_value, SAM_UNK_CTR *ctr)
+{
+ DEBUG(5, ("init_samr_q_set_domain_info\n"));
+
+ q_u->domain_pol = *domain_pol;
+ q_u->switch_value0 = switch_value;
+
+ q_u->switch_value = switch_value;
+ q_u->ctr = ctr;
+
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_set_domain_info(const char *desc, SAMR_Q_SET_DOMAIN_INFO *q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_set_domain_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("domain_pol", &q_u->domain_pol, ps, depth))
+ return False;
+
+ if(!prs_uint16("switch_value0", ps, depth, &q_u->switch_value0))
+ return False;
+
+ if(!prs_uint16("switch_value", ps, depth, &q_u->switch_value))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if ((q_u->ctr = (SAM_UNK_CTR *)prs_alloc_mem(ps, sizeof(SAM_UNK_CTR))) == NULL)
+ return False;
+
+ switch (q_u->switch_value) {
+
+ case 0x0c:
+ if(!sam_io_unk_info12("unk_inf12", &q_u->ctr->info.inf12, ps, depth))
+ return False;
+ break;
+ case 0x07:
+ if(!sam_io_unk_info7("unk_inf7",&q_u->ctr->info.inf7, ps,depth))
+ return False;
+ break;
+ case 0x06:
+ if(!sam_io_unk_info6("unk_inf6",&q_u->ctr->info.inf6, ps,depth))
+ return False;
+ break;
+ case 0x05:
+ if(!sam_io_unk_info5("unk_inf5",&q_u->ctr->info.inf5, ps,depth))
+ return False;
+ break;
+ case 0x03:
+ if(!sam_io_unk_info3("unk_inf3",&q_u->ctr->info.inf3, ps,depth))
+ return False;
+ break;
+ case 0x02:
+ if(!sam_io_unk_info2("unk_inf2",&q_u->ctr->info.inf2, ps,depth))
+ return False;
+ break;
+ case 0x01:
+ if(!sam_io_unk_info1("unk_inf1",&q_u->ctr->info.inf1, ps,depth))
+ return False;
+ break;
+ default:
+ DEBUG(0, ("samr_io_r_samr_unknown_2e: unknown switch level 0x%x\n",
+ q_u->switch_value));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_DOMAIN_INFO structure.
+********************************************************************/
+
+void init_samr_r_set_domain_info(SAMR_R_SET_DOMAIN_INFO * r_u, NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_set_domain_info\n"));
+
+ r_u->status = status; /* return status */
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_set_domain_info(const char *desc, SAMR_R_SET_DOMAIN_INFO * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_samr_unknown_2e");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
diff --git a/source/rpc_parse/parse_sec.c b/source/rpc_parse/parse_sec.c
new file mode 100644
index 00000000000..a78627650ad
--- /dev/null
+++ b/source/rpc_parse/parse_sec.c
@@ -0,0 +1,366 @@
+/*
+ * Unix SMB/Netbios implementation.
+ * Version 1.9.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1998,
+ * Copyright (C) Jeremy R. Allison 1995-2003.
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ * Copyright (C) Paul Ashton 1997-1998.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+ Reads or writes a SEC_ACCESS structure.
+********************************************************************/
+
+BOOL sec_io_access(const char *desc, SEC_ACCESS *t, prs_struct *ps, int depth)
+{
+ if (t == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sec_io_access");
+ depth++;
+
+ if(!prs_uint32("mask", ps, depth, &t->mask))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a SEC_ACE structure.
+********************************************************************/
+
+BOOL sec_io_ace(const char *desc, SEC_ACE *psa, prs_struct *ps, int depth)
+{
+ uint32 old_offset;
+ uint32 offset_ace_size;
+
+ if (psa == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sec_io_ace");
+ depth++;
+
+ old_offset = prs_offset(ps);
+
+ if(!prs_uint8("type ", ps, depth, &psa->type))
+ return False;
+
+ if(!prs_uint8("flags", ps, depth, &psa->flags))
+ return False;
+
+ if(!prs_uint16_pre("size ", ps, depth, &psa->size, &offset_ace_size))
+ return False;
+
+ if(!sec_io_access("info ", &psa->info, ps, depth))
+ return False;
+
+ /* check whether object access is present */
+ if (!sec_ace_object(psa->type)) {
+ if (!smb_io_dom_sid("trustee ", &psa->trustee , ps, depth))
+ return False;
+ } else {
+ if (!prs_uint32("obj_flags", ps, depth, &psa->obj_flags))
+ return False;
+
+ if (psa->obj_flags & SEC_ACE_OBJECT_PRESENT)
+ if (!smb_io_uuid("obj_guid", &psa->obj_guid, ps,depth))
+ return False;
+
+ if (psa->obj_flags & SEC_ACE_OBJECT_INHERITED_PRESENT)
+ if (!smb_io_uuid("inh_guid", &psa->inh_guid, ps,depth))
+ return False;
+
+ if(!smb_io_dom_sid("trustee ", &psa->trustee , ps, depth))
+ return False;
+ }
+
+ if(!prs_uint16_post("size ", ps, depth, &psa->size, offset_ace_size, old_offset))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a SEC_ACL structure.
+
+ First of the xx_io_xx functions that allocates its data structures
+ for you as it reads them.
+********************************************************************/
+
+BOOL sec_io_acl(const char *desc, SEC_ACL **ppsa, prs_struct *ps, int depth)
+{
+ unsigned int i;
+ uint32 old_offset;
+ uint32 offset_acl_size;
+ SEC_ACL *psa;
+
+ /*
+ * Note that the size is always a multiple of 4 bytes due to the
+ * nature of the data structure. Therefore the prs_align() calls
+ * have been removed as they through us off when doing two-layer
+ * marshalling such as in the printing code (NEW_BUFFER). --jerry
+ */
+
+ if (ppsa == NULL)
+ return False;
+
+ psa = *ppsa;
+
+ if(UNMARSHALLING(ps) && psa == NULL) {
+ /*
+ * This is a read and we must allocate the stuct to read into.
+ */
+ if((psa = (SEC_ACL *)prs_alloc_mem(ps, sizeof(SEC_ACL))) == NULL)
+ return False;
+ *ppsa = psa;
+ }
+
+ prs_debug(ps, depth, desc, "sec_io_acl");
+ depth++;
+
+ old_offset = prs_offset(ps);
+
+ if(!prs_uint16("revision", ps, depth, &psa->revision))
+ return False;
+
+ if(!prs_uint16_pre("size ", ps, depth, &psa->size, &offset_acl_size))
+ return False;
+
+ if(!prs_uint32("num_aces ", ps, depth, &psa->num_aces))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ /*
+ * Even if the num_aces is zero, allocate memory as there's a difference
+ * between a non-present DACL (allow all access) and a DACL with no ACE's
+ * (allow no access).
+ */
+ if((psa->ace = (SEC_ACE *)prs_alloc_mem(ps,sizeof(psa->ace[0]) * (psa->num_aces+1))) == NULL)
+ return False;
+ }
+
+ for (i = 0; i < psa->num_aces; i++) {
+ fstring tmp;
+ slprintf(tmp, sizeof(tmp)-1, "ace_list[%02d]: ", i);
+ if(!sec_io_ace(tmp, &psa->ace[i], ps, depth))
+ return False;
+ }
+
+ if(!prs_uint16_post("size ", ps, depth, &psa->size, offset_acl_size, old_offset))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a SEC_DESC structure.
+ If reading and the *ppsd = NULL, allocates the structure.
+********************************************************************/
+
+BOOL sec_io_desc(const char *desc, SEC_DESC **ppsd, prs_struct *ps, int depth)
+{
+ uint32 old_offset;
+ uint32 max_offset = 0; /* after we're done, move offset to end */
+ uint32 tmp_offset = 0;
+
+ SEC_DESC *psd;
+
+ if (ppsd == NULL)
+ return False;
+
+ psd = *ppsd;
+
+ if (psd == NULL) {
+ if(UNMARSHALLING(ps)) {
+ if((psd = (SEC_DESC *)prs_alloc_mem(ps,sizeof(SEC_DESC))) == NULL)
+ return False;
+ *ppsd = psd;
+ } else {
+ /* Marshalling - just ignore. */
+ return True;
+ }
+ }
+
+ prs_debug(ps, depth, desc, "sec_io_desc");
+ depth++;
+
+#if 0
+ /*
+ * if alignment is needed, should be done by the the
+ * caller. Not here. This caused me problems when marshalling
+ * printer info into a buffer. --jerry
+ */
+ if(!prs_align(ps))
+ return False;
+#endif
+
+ /* start of security descriptor stored for back-calc offset purposes */
+ old_offset = prs_offset(ps);
+
+ if(!prs_uint16("revision ", ps, depth, &psd->revision))
+ return False;
+
+ if(!prs_uint16("type ", ps, depth, &psd->type))
+ return False;
+
+ if(!prs_uint32("off_owner_sid", ps, depth, &psd->off_owner_sid))
+ return False;
+
+ if(!prs_uint32("off_grp_sid ", ps, depth, &psd->off_grp_sid))
+ return False;
+
+ if(!prs_uint32("off_sacl ", ps, depth, &psd->off_sacl))
+ return False;
+
+ if(!prs_uint32("off_dacl ", ps, depth, &psd->off_dacl))
+ return False;
+
+ max_offset = MAX(max_offset, prs_offset(ps));
+
+ if (psd->off_owner_sid != 0) {
+
+ tmp_offset = prs_offset(ps);
+ if(!prs_set_offset(ps, old_offset + psd->off_owner_sid))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ /* reading */
+ if((psd->owner_sid = (DOM_SID *)prs_alloc_mem(ps,sizeof(*psd->owner_sid))) == NULL)
+ return False;
+ }
+
+ if(!smb_io_dom_sid("owner_sid ", psd->owner_sid , ps, depth))
+ return False;
+
+ max_offset = MAX(max_offset, prs_offset(ps));
+
+ if (!prs_set_offset(ps,tmp_offset))
+ return False;
+ }
+
+ if (psd->off_grp_sid != 0) {
+
+ tmp_offset = prs_offset(ps);
+ if(!prs_set_offset(ps, old_offset + psd->off_grp_sid))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ /* reading */
+ if((psd->grp_sid = (DOM_SID *)prs_alloc_mem(ps,sizeof(*psd->grp_sid))) == NULL)
+ return False;
+ }
+
+ if(!smb_io_dom_sid("grp_sid", psd->grp_sid, ps, depth))
+ return False;
+
+ max_offset = MAX(max_offset, prs_offset(ps));
+
+ if (!prs_set_offset(ps,tmp_offset))
+ return False;
+ }
+
+ if ((psd->type & SEC_DESC_SACL_PRESENT) && psd->off_sacl) {
+ tmp_offset = prs_offset(ps);
+ if(!prs_set_offset(ps, old_offset + psd->off_sacl))
+ return False;
+ if(!sec_io_acl("sacl", &psd->sacl, ps, depth))
+ return False;
+ max_offset = MAX(max_offset, prs_offset(ps));
+ if (!prs_set_offset(ps,tmp_offset))
+ return False;
+ }
+
+
+ if ((psd->type & SEC_DESC_DACL_PRESENT) && psd->off_dacl != 0) {
+ tmp_offset = prs_offset(ps);
+ if(!prs_set_offset(ps, old_offset + psd->off_dacl))
+ return False;
+ if(!sec_io_acl("dacl", &psd->dacl, ps, depth))
+ return False;
+ max_offset = MAX(max_offset, prs_offset(ps));
+ if (!prs_set_offset(ps,tmp_offset))
+ return False;
+ }
+
+ if(!prs_set_offset(ps, max_offset))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a SEC_DESC_BUF structure.
+********************************************************************/
+
+BOOL sec_io_desc_buf(const char *desc, SEC_DESC_BUF **ppsdb, prs_struct *ps, int depth)
+{
+ uint32 off_len;
+ uint32 off_max_len;
+ uint32 old_offset;
+ uint32 size;
+ SEC_DESC_BUF *psdb;
+
+ if (ppsdb == NULL)
+ return False;
+
+ psdb = *ppsdb;
+
+ if (UNMARSHALLING(ps) && psdb == NULL) {
+ if((psdb = (SEC_DESC_BUF *)prs_alloc_mem(ps,sizeof(SEC_DESC_BUF))) == NULL)
+ return False;
+ *ppsdb = psdb;
+ }
+
+ prs_debug(ps, depth, desc, "sec_io_desc_buf");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32_pre("max_len", ps, depth, &psdb->max_len, &off_max_len))
+ return False;
+
+ if(!prs_uint32 ("ptr ", ps, depth, &psdb->ptr))
+ return False;
+
+ if(!prs_uint32_pre("len ", ps, depth, &psdb->len, &off_len))
+ return False;
+
+ old_offset = prs_offset(ps);
+
+ /* reading, length is non-zero; writing, descriptor is non-NULL */
+ if ((UNMARSHALLING(ps) && psdb->len != 0) || (MARSHALLING(ps) && psdb->sec != NULL)) {
+ if(!sec_io_desc("sec ", &psdb->sec, ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ size = prs_offset(ps) - old_offset;
+ if(!prs_uint32_post("max_len", ps, depth, &psdb->max_len, off_max_len, size == 0 ? psdb->max_len : size))
+ return False;
+
+ if(!prs_uint32_post("len ", ps, depth, &psdb->len, off_len, size))
+ return False;
+
+ return True;
+}
diff --git a/source/rpc_parse/parse_shutdown.c b/source/rpc_parse/parse_shutdown.c
new file mode 100644
index 00000000000..ad2d6e1a028
--- /dev/null
+++ b/source/rpc_parse/parse_shutdown.c
@@ -0,0 +1,163 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+Inits a structure.
+********************************************************************/
+
+void init_shutdown_q_init(SHUTDOWN_Q_INIT *q_s, const char *msg,
+ uint32 timeout, BOOL do_reboot, BOOL force)
+{
+ q_s->ptr_server = 1;
+ q_s->server = 1;
+ q_s->ptr_msg = 1;
+
+ init_unistr2(&q_s->uni_msg, msg, UNI_FLAGS_NONE);
+ init_uni_hdr(&q_s->hdr_msg, &q_s->uni_msg);
+
+ q_s->timeout = timeout;
+
+ q_s->reboot = do_reboot ? 1 : 0;
+ q_s->force = force ? 1 : 0;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL shutdown_io_q_init(const char *desc, SHUTDOWN_Q_INIT *q_s, prs_struct *ps,
+ int depth)
+{
+ if (q_s == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "shutdown_io_q_init");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("ptr_server", ps, depth, &(q_s->ptr_server)))
+ return False;
+ if (!prs_uint16("server", ps, depth, &(q_s->server)))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("ptr_msg", ps, depth, &(q_s->ptr_msg)))
+ return False;
+
+ if (!smb_io_unihdr("hdr_msg", &(q_s->hdr_msg), ps, depth))
+ return False;
+ if (!smb_io_unistr2("uni_msg", &(q_s->uni_msg), q_s->hdr_msg.buffer, ps, depth))
+ return False;
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("timeout", ps, depth, &(q_s->timeout)))
+ return False;
+ if (!prs_uint8("force ", ps, depth, &(q_s->force)))
+ return False;
+ if (!prs_uint8("reboot ", ps, depth, &(q_s->reboot)))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL shutdown_io_r_init(const char *desc, SHUTDOWN_R_INIT* r_s, prs_struct *ps,
+ int depth)
+{
+ if (r_s == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "shutdown_io_r_init");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_s->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+Inits a structure.
+********************************************************************/
+void init_shutdown_q_abort(SHUTDOWN_Q_ABORT *q_s)
+{
+
+ q_s->ptr_server = 0;
+
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL shutdown_io_q_abort(const char *desc, SHUTDOWN_Q_ABORT *q_s,
+ prs_struct *ps, int depth)
+{
+ if (q_s == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "shutdown_io_q_abort");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("ptr_server", ps, depth, &(q_s->ptr_server)))
+ return False;
+ if (q_s->ptr_server != 0)
+ if (!prs_uint16("server", ps, depth, &(q_s->server)))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL shutdown_io_r_abort(const char *desc, SHUTDOWN_R_ABORT *r_s,
+ prs_struct *ps, int depth)
+{
+ if (r_s == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "shutdown_io_r_abort");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_ntstatus("status", ps, depth, &r_s->status))
+ return False;
+
+ return True;
+}
diff --git a/source/rpc_parse/parse_spoolss.c b/source/rpc_parse/parse_spoolss.c
new file mode 100644
index 00000000000..ae087c7f774
--- /dev/null
+++ b/source/rpc_parse/parse_spoolss.c
@@ -0,0 +1,7741 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-2000,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+ * Copyright (C) Jean François Micouleau 1998-2000,
+ * Copyright (C) Gerald Carter 2000-2002,
+ * Copyright (C) Tim Potter 2001-2002.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+return the length of a UNISTR string.
+********************************************************************/
+
+static uint32 str_len_uni(UNISTR *source)
+{
+ uint32 i=0;
+
+ if (!source->buffer)
+ return 0;
+
+ while (source->buffer[i])
+ i++;
+
+ return i;
+}
+
+/*******************************************************************
+This should be moved in a more generic lib.
+********************************************************************/
+
+BOOL spoolss_io_system_time(const char *desc, prs_struct *ps, int depth, SYSTEMTIME *systime)
+{
+ if(!prs_uint16("year", ps, depth, &systime->year))
+ return False;
+ if(!prs_uint16("month", ps, depth, &systime->month))
+ return False;
+ if(!prs_uint16("dayofweek", ps, depth, &systime->dayofweek))
+ return False;
+ if(!prs_uint16("day", ps, depth, &systime->day))
+ return False;
+ if(!prs_uint16("hour", ps, depth, &systime->hour))
+ return False;
+ if(!prs_uint16("minute", ps, depth, &systime->minute))
+ return False;
+ if(!prs_uint16("second", ps, depth, &systime->second))
+ return False;
+ if(!prs_uint16("milliseconds", ps, depth, &systime->milliseconds))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL make_systemtime(SYSTEMTIME *systime, struct tm *unixtime)
+{
+ systime->year=unixtime->tm_year+1900;
+ systime->month=unixtime->tm_mon+1;
+ systime->dayofweek=unixtime->tm_wday;
+ systime->day=unixtime->tm_mday;
+ systime->hour=unixtime->tm_hour;
+ systime->minute=unixtime->tm_min;
+ systime->second=unixtime->tm_sec;
+ systime->milliseconds=0;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes an DOC_INFO structure.
+********************************************************************/
+
+static BOOL smb_io_doc_info_1(const char *desc, DOC_INFO_1 *info_1, prs_struct *ps, int depth)
+{
+ if (info_1 == NULL) return False;
+
+ prs_debug(ps, depth, desc, "smb_io_doc_info_1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("p_docname", ps, depth, &info_1->p_docname))
+ return False;
+ if(!prs_uint32("p_outputfile", ps, depth, &info_1->p_outputfile))
+ return False;
+ if(!prs_uint32("p_datatype", ps, depth, &info_1->p_datatype))
+ return False;
+
+ if(!smb_io_unistr2("", &info_1->docname, info_1->p_docname, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &info_1->outputfile, info_1->p_outputfile, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &info_1->datatype, info_1->p_datatype, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes an DOC_INFO structure.
+********************************************************************/
+
+static BOOL smb_io_doc_info(const char *desc, DOC_INFO *info, prs_struct *ps, int depth)
+{
+ uint32 useless_ptr=0;
+
+ if (info == NULL) return False;
+
+ prs_debug(ps, depth, desc, "smb_io_doc_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("switch_value", ps, depth, &info->switch_value))
+ return False;
+
+ if(!prs_uint32("doc_info_X ptr", ps, depth, &useless_ptr))
+ return False;
+
+ switch (info->switch_value)
+ {
+ case 1:
+ if(!smb_io_doc_info_1("",&info->doc_info_1, ps, depth))
+ return False;
+ break;
+ case 2:
+ /*
+ this is just a placeholder
+
+ MSDN July 1998 says doc_info_2 is only on
+ Windows 95, and as Win95 doesn't do RPC to print
+ this case is nearly impossible
+
+ Maybe one day with Windows for dishwasher 2037 ...
+
+ */
+ /* smb_io_doc_info_2("",&info->doc_info_2, ps, depth); */
+ break;
+ default:
+ DEBUG(0,("Something is obviously wrong somewhere !\n"));
+ break;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes an DOC_INFO_CONTAINER structure.
+********************************************************************/
+
+static BOOL smb_io_doc_info_container(const char *desc, DOC_INFO_CONTAINER *cont, prs_struct *ps, int depth)
+{
+ if (cont == NULL) return False;
+
+ prs_debug(ps, depth, desc, "smb_io_doc_info_container");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("level", ps, depth, &cont->level))
+ return False;
+
+ if(!smb_io_doc_info("",&cont->docinfo, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes an NOTIFY OPTION TYPE structure.
+********************************************************************/
+
+/* NOTIFY_OPTION_TYPE and NOTIFY_OPTION_TYPE_DATA are really one
+ structure. The _TYPE structure is really the deferred referrants (i.e
+ the notify fields array) of the _TYPE structure. -tpot */
+
+static BOOL smb_io_notify_option_type(const char *desc, SPOOL_NOTIFY_OPTION_TYPE *type, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "smb_io_notify_option_type");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!prs_uint16("type", ps, depth, &type->type))
+ return False;
+ if(!prs_uint16("reserved0", ps, depth, &type->reserved0))
+ return False;
+ if(!prs_uint32("reserved1", ps, depth, &type->reserved1))
+ return False;
+ if(!prs_uint32("reserved2", ps, depth, &type->reserved2))
+ return False;
+ if(!prs_uint32("count", ps, depth, &type->count))
+ return False;
+ if(!prs_uint32("fields_ptr", ps, depth, &type->fields_ptr))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes an NOTIFY OPTION TYPE DATA.
+********************************************************************/
+
+static BOOL smb_io_notify_option_type_data(const char *desc, SPOOL_NOTIFY_OPTION_TYPE *type, prs_struct *ps, int depth)
+{
+ int i;
+
+ prs_debug(ps, depth, desc, "smb_io_notify_option_type_data");
+ depth++;
+
+ /* if there are no fields just return */
+ if (type->fields_ptr==0)
+ return True;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("count2", ps, depth, &type->count2))
+ return False;
+
+ if (type->count2 != type->count)
+ DEBUG(4,("What a mess, count was %x now is %x !\n", type->count, type->count2));
+
+ /* parse the option type data */
+ for(i=0;i<type->count2;i++)
+ if(!prs_uint16("fields",ps,depth,&type->fields[i]))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+reads or writes an NOTIFY OPTION structure.
+********************************************************************/
+
+static BOOL smb_io_notify_option_type_ctr(const char *desc, SPOOL_NOTIFY_OPTION_TYPE_CTR *ctr , prs_struct *ps, int depth)
+{
+ int i;
+
+ prs_debug(ps, depth, desc, "smb_io_notify_option_type_ctr");
+ depth++;
+
+ if(!prs_uint32("count", ps, depth, &ctr->count))
+ return False;
+
+ /* reading */
+ if (UNMARSHALLING(ps))
+ if((ctr->type=(SPOOL_NOTIFY_OPTION_TYPE *)prs_alloc_mem(ps,ctr->count*sizeof(SPOOL_NOTIFY_OPTION_TYPE))) == NULL)
+ return False;
+
+ /* the option type struct */
+ for(i=0;i<ctr->count;i++)
+ if(!smb_io_notify_option_type("", &ctr->type[i] , ps, depth))
+ return False;
+
+ /* the type associated with the option type struct */
+ for(i=0;i<ctr->count;i++)
+ if(!smb_io_notify_option_type_data("", &ctr->type[i] , ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes an NOTIFY OPTION structure.
+********************************************************************/
+
+static BOOL smb_io_notify_option(const char *desc, SPOOL_NOTIFY_OPTION *option, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "smb_io_notify_option");
+ depth++;
+
+ if(!prs_uint32("version", ps, depth, &option->version))
+ return False;
+ if(!prs_uint32("flags", ps, depth, &option->flags))
+ return False;
+ if(!prs_uint32("count", ps, depth, &option->count))
+ return False;
+ if(!prs_uint32("option_type_ptr", ps, depth, &option->option_type_ptr))
+ return False;
+
+ /* marshalling or unmarshalling, that would work */
+ if (option->option_type_ptr!=0) {
+ if(!smb_io_notify_option_type_ctr("", &option->ctr ,ps, depth))
+ return False;
+ }
+ else {
+ option->ctr.type=NULL;
+ option->ctr.count=0;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes an NOTIFY INFO DATA structure.
+********************************************************************/
+
+static BOOL smb_io_notify_info_data(const char *desc,SPOOL_NOTIFY_INFO_DATA *data, prs_struct *ps, int depth)
+{
+ uint32 useless_ptr=0x0FF0ADDE;
+
+ prs_debug(ps, depth, desc, "smb_io_notify_info_data");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint16("type", ps, depth, &data->type))
+ return False;
+ if(!prs_uint16("field", ps, depth, &data->field))
+ return False;
+
+ if(!prs_uint32("how many words", ps, depth, &data->size))
+ return False;
+ if(!prs_uint32("id", ps, depth, &data->id))
+ return False;
+ if(!prs_uint32("how many words", ps, depth, &data->size))
+ return False;
+
+ switch (data->enc_type) {
+
+ /* One and two value data has two uint32 values */
+
+ case NOTIFY_ONE_VALUE:
+ case NOTIFY_TWO_VALUE:
+
+ if(!prs_uint32("value[0]", ps, depth, &data->notify_data.value[0]))
+ return False;
+ if(!prs_uint32("value[1]", ps, depth, &data->notify_data.value[1]))
+ return False;
+ break;
+
+ /* Pointers and strings have a string length and a
+ pointer. For a string the length is expressed as
+ the number of uint16 characters plus a trailing
+ \0\0. */
+
+ case NOTIFY_POINTER:
+
+ if(!prs_uint32("string length", ps, depth, &data->notify_data.data.length ))
+ return False;
+ if(!prs_uint32("pointer", ps, depth, &useless_ptr))
+ return False;
+
+ break;
+
+ case NOTIFY_STRING:
+
+ if(!prs_uint32("string length", ps, depth, &data->notify_data.data.length))
+ return False;
+
+ if(!prs_uint32("pointer", ps, depth, &useless_ptr))
+ return False;
+
+ break;
+
+ case NOTIFY_SECDESC:
+ if( !prs_uint32( "sd size", ps, depth, &data->notify_data.sd.size ) )
+ return False;
+ if( !prs_uint32( "pointer", ps, depth, &useless_ptr ) )
+ return False;
+
+ break;
+
+ default:
+ DEBUG(3, ("invalid enc_type %d for smb_io_notify_info_data\n",
+ data->enc_type));
+ break;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes an NOTIFY INFO DATA structure.
+********************************************************************/
+
+BOOL smb_io_notify_info_data_strings(const char *desc,SPOOL_NOTIFY_INFO_DATA *data,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "smb_io_notify_info_data_strings");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ switch(data->enc_type) {
+
+ /* No data for values */
+
+ case NOTIFY_ONE_VALUE:
+ case NOTIFY_TWO_VALUE:
+
+ break;
+
+ /* Strings start with a length in uint16s */
+
+ case NOTIFY_STRING:
+
+ if (UNMARSHALLING(ps)) {
+ data->notify_data.data.string =
+ (uint16 *)prs_alloc_mem(ps, data->notify_data.data.length);
+
+ if (!data->notify_data.data.string)
+ return False;
+ }
+
+ if (MARSHALLING(ps))
+ data->notify_data.data.length /= 2;
+
+ if(!prs_uint32("string length", ps, depth, &data->notify_data.data.length))
+ return False;
+
+ if (!prs_uint16uni(True, "string", ps, depth, data->notify_data.data.string,
+ data->notify_data.data.length))
+ return False;
+
+ if (MARSHALLING(ps))
+ data->notify_data.data.length *= 2;
+
+ break;
+
+ case NOTIFY_POINTER:
+
+ if (UNMARSHALLING(ps)) {
+ data->notify_data.data.string =
+ (uint16 *)prs_alloc_mem(ps, data->notify_data.data.length);
+
+ if (!data->notify_data.data.string)
+ return False;
+ }
+
+ if(!prs_uint8s(True,"buffer",ps,depth,(uint8*)data->notify_data.data.string,data->notify_data.data.length))
+ return False;
+
+ break;
+
+ case NOTIFY_SECDESC:
+ if( !prs_uint32("secdesc size ", ps, depth, &data->notify_data.sd.size ) )
+ return False;
+ if ( !sec_io_desc( "sec_desc", &data->notify_data.sd.desc, ps, depth ) )
+ return False;
+ break;
+
+ default:
+ DEBUG(3, ("invalid enc_type %d for smb_io_notify_info_data_strings\n",
+ data->enc_type));
+ break;
+ }
+
+#if 0
+ if (isvalue==False) {
+
+ /* length of string in unicode include \0 */
+ x=data->notify_data.data.length+1;
+
+ if (data->field != 16)
+ if(!prs_uint32("string length", ps, depth, &x ))
+ return False;
+
+ if (MARSHALLING(ps)) {
+ /* These are already in little endian format. Don't byte swap. */
+ if (x == 1) {
+
+ /* No memory allocated for this string
+ therefore following the data.string
+ pointer is a bad idea. Use a pointer to
+ the uint32 length union member to
+ provide a source for a unicode NULL */
+
+ if(!prs_uint8s(True,"string",ps,depth, (uint8 *)&data->notify_data.data.length,x*2))
+ return False;
+ } else {
+
+ if (data->field == 16)
+ x /= 2;
+
+ if(!prs_uint16uni(True,"string",ps,depth,data->notify_data.data.string,x))
+ return False;
+ }
+ } else {
+
+ /* Tallocate memory for string */
+
+ data->notify_data.data.string = (uint16 *)prs_alloc_mem(ps, x * 2);
+ if (!data->notify_data.data.string)
+ return False;
+
+ if(!prs_uint16uni(True,"string",ps,depth,data->notify_data.data.string,x))
+ return False;
+ }
+ }
+
+#endif
+
+#if 0 /* JERRY */
+ /* Win2k does not seem to put this parse align here */
+ if(!prs_align(ps))
+ return False;
+#endif
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes an NOTIFY INFO structure.
+********************************************************************/
+
+static BOOL smb_io_notify_info(const char *desc, SPOOL_NOTIFY_INFO *info, prs_struct *ps, int depth)
+{
+ int i;
+
+ prs_debug(ps, depth, desc, "smb_io_notify_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("count", ps, depth, &info->count))
+ return False;
+ if(!prs_uint32("version", ps, depth, &info->version))
+ return False;
+ if(!prs_uint32("flags", ps, depth, &info->flags))
+ return False;
+ if(!prs_uint32("count", ps, depth, &info->count))
+ return False;
+
+ for (i=0;i<info->count;i++) {
+ if(!smb_io_notify_info_data(desc, &info->data[i], ps, depth))
+ return False;
+ }
+
+ /* now do the strings at the end of the stream */
+ for (i=0;i<info->count;i++) {
+ if(!smb_io_notify_info_data_strings(desc, &info->data[i], ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+static BOOL spool_io_user_level_1(const char *desc, SPOOL_USER_1 *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "");
+ depth++;
+
+ /* reading */
+ if (UNMARSHALLING(ps))
+ ZERO_STRUCTP(q_u);
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("size", ps, depth, &q_u->size))
+ return False;
+ if (!prs_uint32("client_name_ptr", ps, depth, &q_u->client_name_ptr))
+ return False;
+ if (!prs_uint32("user_name_ptr", ps, depth, &q_u->user_name_ptr))
+ return False;
+ if (!prs_uint32("build", ps, depth, &q_u->build))
+ return False;
+ if (!prs_uint32("major", ps, depth, &q_u->major))
+ return False;
+ if (!prs_uint32("minor", ps, depth, &q_u->minor))
+ return False;
+ if (!prs_uint32("processor", ps, depth, &q_u->processor))
+ return False;
+
+ if (!smb_io_unistr2("", &q_u->client_name, q_u->client_name_ptr, ps, depth))
+ return False;
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_unistr2("", &q_u->user_name, q_u->user_name_ptr, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+static BOOL spool_io_user_level(const char *desc, SPOOL_USER_CTR *q_u, prs_struct *ps, int depth)
+{
+ if (q_u==NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "spool_io_user_level");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ /* From looking at many captures in ethereal, it looks like
+ the level and ptr fields should be transposed. -tpot */
+
+ if (!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+ if (!prs_uint32("ptr", ps, depth, &q_u->ptr))
+ return False;
+
+ switch (q_u->level) {
+ case 1:
+ if (!spool_io_user_level_1("", &q_u->user1, ps, depth))
+ return False;
+ break;
+ default:
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ * read or write a DEVICEMODE struct.
+ * on reading allocate memory for the private member
+ ********************************************************************/
+
+#define DM_NUM_OPTIONAL_FIELDS 8
+
+BOOL spoolss_io_devmode(const char *desc, prs_struct *ps, int depth, DEVICEMODE *devmode)
+{
+ int available_space; /* size of the device mode left to parse */
+ /* only important on unmarshalling */
+ int i = 0;
+
+ struct optional_fields {
+ fstring name;
+ uint32* field;
+ } opt_fields[DM_NUM_OPTIONAL_FIELDS] = {
+ { "icmmethod", NULL },
+ { "icmintent", NULL },
+ { "mediatype", NULL },
+ { "dithertype", NULL },
+ { "reserved1", NULL },
+ { "reserved2", NULL },
+ { "panningwidth", NULL },
+ { "panningheight", NULL }
+ };
+
+ /* assign at run time to keep non-gcc compilers happy */
+
+ opt_fields[0].field = &devmode->icmmethod;
+ opt_fields[1].field = &devmode->icmintent;
+ opt_fields[2].field = &devmode->mediatype;
+ opt_fields[3].field = &devmode->dithertype;
+ opt_fields[4].field = &devmode->reserved1;
+ opt_fields[5].field = &devmode->reserved2;
+ opt_fields[6].field = &devmode->panningwidth;
+ opt_fields[7].field = &devmode->panningheight;
+
+
+ prs_debug(ps, depth, desc, "spoolss_io_devmode");
+ depth++;
+
+ if (UNMARSHALLING(ps)) {
+ devmode->devicename.buffer = (uint16 *)prs_alloc_mem(ps, 32 * sizeof(uint16) );
+ if (devmode->devicename.buffer == NULL)
+ return False;
+ }
+
+ if (!prs_uint16uni(True,"devicename", ps, depth, devmode->devicename.buffer, MAXDEVICENAME))
+ return False;
+
+ if (!prs_uint16("specversion", ps, depth, &devmode->specversion))
+ return False;
+
+ /* Sanity Check - look for unknown specversions, but don't fail if we see one.
+ Let the size determine that */
+
+ switch (devmode->specversion) {
+ /* list of observed spec version's */
+ case 0x0320:
+ case 0x0400:
+ case 0x0401:
+ case 0x040d:
+ break;
+
+ default:
+ DEBUG(0,("spoolss_io_devmode: Unknown specversion in devicemode [0x%x]\n",
+ devmode->specversion));
+ DEBUG(0,("spoolss_io_devmode: please report to samba-technical@samba.org!\n"));
+ break;
+ }
+
+
+ if (!prs_uint16("driverversion", ps, depth, &devmode->driverversion))
+ return False;
+ if (!prs_uint16("size", ps, depth, &devmode->size))
+ return False;
+ if (!prs_uint16("driverextra", ps, depth, &devmode->driverextra))
+ return False;
+ if (!prs_uint32("fields", ps, depth, &devmode->fields))
+ return False;
+ if (!prs_uint16("orientation", ps, depth, &devmode->orientation))
+ return False;
+ if (!prs_uint16("papersize", ps, depth, &devmode->papersize))
+ return False;
+ if (!prs_uint16("paperlength", ps, depth, &devmode->paperlength))
+ return False;
+ if (!prs_uint16("paperwidth", ps, depth, &devmode->paperwidth))
+ return False;
+ if (!prs_uint16("scale", ps, depth, &devmode->scale))
+ return False;
+ if (!prs_uint16("copies", ps, depth, &devmode->copies))
+ return False;
+ if (!prs_uint16("defaultsource", ps, depth, &devmode->defaultsource))
+ return False;
+ if (!prs_uint16("printquality", ps, depth, &devmode->printquality))
+ return False;
+ if (!prs_uint16("color", ps, depth, &devmode->color))
+ return False;
+ if (!prs_uint16("duplex", ps, depth, &devmode->duplex))
+ return False;
+ if (!prs_uint16("yresolution", ps, depth, &devmode->yresolution))
+ return False;
+ if (!prs_uint16("ttoption", ps, depth, &devmode->ttoption))
+ return False;
+ if (!prs_uint16("collate", ps, depth, &devmode->collate))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ devmode->formname.buffer = (uint16 *)prs_alloc_mem(ps, 32 * sizeof(uint16) );
+ if (devmode->formname.buffer == NULL)
+ return False;
+ }
+
+ if (!prs_uint16uni(True, "formname", ps, depth, devmode->formname.buffer, 32))
+ return False;
+ if (!prs_uint16("logpixels", ps, depth, &devmode->logpixels))
+ return False;
+ if (!prs_uint32("bitsperpel", ps, depth, &devmode->bitsperpel))
+ return False;
+ if (!prs_uint32("pelswidth", ps, depth, &devmode->pelswidth))
+ return False;
+ if (!prs_uint32("pelsheight", ps, depth, &devmode->pelsheight))
+ return False;
+ if (!prs_uint32("displayflags", ps, depth, &devmode->displayflags))
+ return False;
+ if (!prs_uint32("displayfrequency", ps, depth, &devmode->displayfrequency))
+ return False;
+ /*
+ * every device mode I've ever seen on the wire at least has up
+ * to the displayfrequency field. --jerry (05-09-2002)
+ */
+
+ /* add uint32's + uint16's + two UNICODE strings */
+
+ available_space = devmode->size - (sizeof(uint32)*6 + sizeof(uint16)*18 + sizeof(uint16)*64);
+
+ /* Sanity check - we only have uint32's left tp parse */
+
+ if ( available_space && ((available_space % sizeof(uint32)) != 0) ) {
+ DEBUG(0,("spoolss_io_devmode: available_space [%d] no in multiple of 4 bytes (size = %d)!\n",
+ available_space, devmode->size));
+ DEBUG(0,("spoolss_io_devmode: please report to samba-technical@samba.org!\n"));
+ return False;
+ }
+
+ /*
+ * Conditional parsing. Assume that the DeviceMode has been
+ * zero'd by the caller.
+ */
+
+ while ((available_space > 0) && (i < DM_NUM_OPTIONAL_FIELDS))
+ {
+ DEBUG(11, ("spoolss_io_devmode: [%d] bytes left to parse in devmode\n", available_space));
+ if (!prs_uint32(opt_fields[i].name, ps, depth, opt_fields[i].field))
+ return False;
+ available_space -= sizeof(uint32);
+ i++;
+ }
+
+ /* Sanity Check - we should no available space at this point unless
+ MS changes the device mode structure */
+
+ if (available_space) {
+ DEBUG(0,("spoolss_io_devmode: I've parsed all I know and there is still stuff left|\n"));
+ DEBUG(0,("spoolss_io_devmode: available_space = [%d], devmode_size = [%d]!\n",
+ available_space, devmode->size));
+ DEBUG(0,("spoolss_io_devmode: please report to samba-technical@samba.org!\n"));
+ return False;
+ }
+
+
+ if (devmode->driverextra!=0) {
+ if (UNMARSHALLING(ps)) {
+ devmode->private=(uint8 *)prs_alloc_mem(ps, devmode->driverextra*sizeof(uint8));
+ if(devmode->private == NULL)
+ return False;
+ DEBUG(7,("spoolss_io_devmode: allocated memory [%d] for private\n",devmode->driverextra));
+ }
+
+ DEBUG(7,("spoolss_io_devmode: parsing [%d] bytes of private\n",devmode->driverextra));
+ if (!prs_uint8s(False, "private", ps, depth,
+ devmode->private, devmode->driverextra))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Read or write a DEVICEMODE container
+********************************************************************/
+
+static BOOL spoolss_io_devmode_cont(const char *desc, DEVMODE_CTR *dm_c, prs_struct *ps, int depth)
+{
+ if (dm_c==NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_devmode_cont");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("size", ps, depth, &dm_c->size))
+ return False;
+
+ if (!prs_uint32("devmode_ptr", ps, depth, &dm_c->devmode_ptr))
+ return False;
+
+ if (dm_c->size==0 || dm_c->devmode_ptr==0) {
+ if (UNMARSHALLING(ps))
+ /* if while reading there is no DEVMODE ... */
+ dm_c->devmode=NULL;
+ return True;
+ }
+
+ /* so we have a DEVICEMODE to follow */
+ if (UNMARSHALLING(ps)) {
+ DEBUG(9,("Allocating memory for spoolss_io_devmode\n"));
+ dm_c->devmode=(DEVICEMODE *)prs_alloc_mem(ps,sizeof(DEVICEMODE));
+ if(dm_c->devmode == NULL)
+ return False;
+ }
+
+ /* this is bad code, shouldn't be there */
+ if (!prs_uint32("size", ps, depth, &dm_c->size))
+ return False;
+
+ if (!spoolss_io_devmode(desc, ps, depth, dm_c->devmode))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+static BOOL spoolss_io_printer_default(const char *desc, PRINTER_DEFAULT *pd, prs_struct *ps, int depth)
+{
+ if (pd==NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_printer_default");
+ depth++;
+
+ if (!prs_uint32("datatype_ptr", ps, depth, &pd->datatype_ptr))
+ return False;
+
+ if (!smb_io_unistr2("datatype", &pd->datatype, pd->datatype_ptr, ps,depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_devmode_cont("", &pd->devmode_cont, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("access_required", ps, depth, &pd->access_required))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_open_printer_ex(SPOOL_Q_OPEN_PRINTER_EX *q_u,
+ const fstring printername,
+ const fstring datatype,
+ uint32 access_required,
+ const fstring clientname,
+ const fstring user_name)
+{
+ DEBUG(5,("make_spoolss_q_open_printer_ex\n"));
+ q_u->printername_ptr = (printername!=NULL)?1:0;
+ init_unistr2(&q_u->printername, printername, UNI_STR_TERMINATE);
+
+ q_u->printer_default.datatype_ptr = 0;
+/*
+ q_u->printer_default.datatype_ptr = (datatype!=NULL)?1:0;
+ init_unistr2(&q_u->printer_default.datatype, datatype, UNI_FLAGS_NONE);
+*/
+ q_u->printer_default.devmode_cont.size=0;
+ q_u->printer_default.devmode_cont.devmode_ptr=0;
+ q_u->printer_default.devmode_cont.devmode=NULL;
+ q_u->printer_default.access_required=access_required;
+ q_u->user_switch=1;
+ q_u->user_ctr.level=1;
+ q_u->user_ctr.ptr=1;
+ q_u->user_ctr.user1.size=strlen(clientname)+strlen(user_name)+10;
+ q_u->user_ctr.user1.client_name_ptr = (clientname!=NULL)?1:0;
+ q_u->user_ctr.user1.user_name_ptr = (user_name!=NULL)?1:0;
+ q_u->user_ctr.user1.build=1381;
+ q_u->user_ctr.user1.major=2;
+ q_u->user_ctr.user1.minor=0;
+ q_u->user_ctr.user1.processor=0;
+ init_unistr2(&q_u->user_ctr.user1.client_name, clientname, UNI_STR_TERMINATE);
+ init_unistr2(&q_u->user_ctr.user1.user_name, user_name, UNI_STR_TERMINATE);
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_addprinterex(
+ TALLOC_CTX *mem_ctx,
+ SPOOL_Q_ADDPRINTEREX *q_u,
+ const char *srv_name,
+ const char* clientname,
+ const char* user_name,
+ uint32 level,
+ PRINTER_INFO_CTR *ctr)
+{
+ DEBUG(5,("make_spoolss_q_addprinterex\n"));
+
+ if (!ctr) return False;
+
+ ZERO_STRUCTP(q_u);
+
+ q_u->server_name_ptr = (srv_name!=NULL)?1:0;
+ init_unistr2(&q_u->server_name, srv_name, UNI_FLAGS_NONE);
+
+ q_u->level = level;
+
+ q_u->info.level = level;
+ q_u->info.info_ptr = (ctr->printers_2!=NULL)?1:0;
+ switch (level) {
+ case 2:
+ /* init q_u->info.info2 from *info */
+ if (!make_spoolss_printer_info_2(mem_ctx, &q_u->info.info_2, ctr->printers_2)) {
+ DEBUG(0,("make_spoolss_q_addprinterex: Unable to fill SPOOL_Q_ADDPRINTEREX struct!\n"));
+ return False;
+ }
+ break;
+ default :
+ break;
+ }
+
+ q_u->user_switch=1;
+
+ q_u->user_ctr.level=1;
+ q_u->user_ctr.ptr=1;
+ q_u->user_ctr.user1.client_name_ptr = (clientname!=NULL)?1:0;
+ q_u->user_ctr.user1.user_name_ptr = (user_name!=NULL)?1:0;
+ q_u->user_ctr.user1.build=1381;
+ q_u->user_ctr.user1.major=2;
+ q_u->user_ctr.user1.minor=0;
+ q_u->user_ctr.user1.processor=0;
+ init_unistr2(&q_u->user_ctr.user1.client_name, clientname, UNI_STR_TERMINATE);
+ init_unistr2(&q_u->user_ctr.user1.user_name, user_name, UNI_STR_TERMINATE);
+ q_u->user_ctr.user1.size=q_u->user_ctr.user1.user_name.uni_str_len +
+ q_u->user_ctr.user1.client_name.uni_str_len + 2;
+
+ return True;
+}
+
+/*******************************************************************
+create a SPOOL_PRINTER_INFO_2 stuct from a PRINTER_INFO_2 struct
+*******************************************************************/
+
+BOOL make_spoolss_printer_info_2(TALLOC_CTX *mem_ctx, SPOOL_PRINTER_INFO_LEVEL_2 **spool_info2,
+ PRINTER_INFO_2 *info)
+{
+
+ SPOOL_PRINTER_INFO_LEVEL_2 *inf;
+
+ /* allocate the necessary memory */
+ if (!(inf=(SPOOL_PRINTER_INFO_LEVEL_2*)talloc(mem_ctx, sizeof(SPOOL_PRINTER_INFO_LEVEL_2)))) {
+ DEBUG(0,("make_spoolss_printer_info_2: Unable to allocate SPOOL_PRINTER_INFO_LEVEL_2 sruct!\n"));
+ return False;
+ }
+
+ inf->servername_ptr = (info->servername.buffer!=NULL)?1:0;
+ inf->printername_ptr = (info->printername.buffer!=NULL)?1:0;
+ inf->sharename_ptr = (info->sharename.buffer!=NULL)?1:0;
+ inf->portname_ptr = (info->portname.buffer!=NULL)?1:0;
+ inf->drivername_ptr = (info->drivername.buffer!=NULL)?1:0;
+ inf->comment_ptr = (info->comment.buffer!=NULL)?1:0;
+ inf->location_ptr = (info->location.buffer!=NULL)?1:0;
+ inf->devmode_ptr = (info->devmode!=NULL)?1:0;
+ inf->sepfile_ptr = (info->sepfile.buffer!=NULL)?1:0;
+ inf->printprocessor_ptr = (info->printprocessor.buffer!=NULL)?1:0;
+ inf->datatype_ptr = (info->datatype.buffer!=NULL)?1:0;
+ inf->parameters_ptr = (info->parameters.buffer!=NULL)?1:0;
+ inf->secdesc_ptr = (info->secdesc!=NULL)?1:0;
+ inf->attributes = info->attributes;
+ inf->priority = info->priority;
+ inf->default_priority = info->defaultpriority;
+ inf->starttime = info->starttime;
+ inf->untiltime = info->untiltime;
+ inf->cjobs = info->cjobs;
+ inf->averageppm = info->averageppm;
+ init_unistr2_from_unistr(&inf->servername, &info->servername);
+ init_unistr2_from_unistr(&inf->printername, &info->printername);
+ init_unistr2_from_unistr(&inf->sharename, &info->sharename);
+ init_unistr2_from_unistr(&inf->portname, &info->portname);
+ init_unistr2_from_unistr(&inf->drivername, &info->drivername);
+ init_unistr2_from_unistr(&inf->comment, &info->comment);
+ init_unistr2_from_unistr(&inf->location, &info->location);
+ init_unistr2_from_unistr(&inf->sepfile, &info->sepfile);
+ init_unistr2_from_unistr(&inf->printprocessor, &info->printprocessor);
+ init_unistr2_from_unistr(&inf->datatype, &info->datatype);
+ init_unistr2_from_unistr(&inf->parameters, &info->parameters);
+ init_unistr2_from_unistr(&inf->datatype, &info->datatype);
+
+ *spool_info2 = inf;
+
+ return True;
+}
+
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_open_printer_ex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_open_printer(const char *desc, SPOOL_Q_OPEN_PRINTER *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_open_printer");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("printername_ptr", ps, depth, &q_u->printername_ptr))
+ return False;
+ if (!smb_io_unistr2("", &q_u->printername, q_u->printername_ptr, ps,depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_printer_default("", &q_u->printer_default, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from static spoolss_r_open_printer_ex (srv_spoolss.c)
+ * called from spoolss_open_printer_ex (cli_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_open_printer(const char *desc, SPOOL_R_OPEN_PRINTER *r_u, prs_struct *ps, int depth)
+{
+ if (r_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_r_open_printer");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!smb_io_pol_hnd("printer handle",&(r_u->handle),ps,depth))
+ return False;
+
+ if (!prs_werror("status code", ps, depth, &(r_u->status)))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_open_printer_ex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_open_printer_ex(const char *desc, SPOOL_Q_OPEN_PRINTER_EX *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_open_printer_ex");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("printername_ptr", ps, depth, &q_u->printername_ptr))
+ return False;
+ if (!smb_io_unistr2("", &q_u->printername, q_u->printername_ptr, ps,depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_printer_default("", &q_u->printer_default, ps, depth))
+ return False;
+
+ if (!prs_uint32("user_switch", ps, depth, &q_u->user_switch))
+ return False;
+ if (!spool_io_user_level("", &q_u->user_ctr, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from static spoolss_r_open_printer_ex (srv_spoolss.c)
+ * called from spoolss_open_printer_ex (cli_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_open_printer_ex(const char *desc, SPOOL_R_OPEN_PRINTER_EX *r_u, prs_struct *ps, int depth)
+{
+ if (r_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_r_open_printer_ex");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!smb_io_pol_hnd("printer handle",&(r_u->handle),ps,depth))
+ return False;
+
+ if (!prs_werror("status code", ps, depth, &(r_u->status)))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+BOOL make_spoolss_q_deleteprinterdriver(
+ TALLOC_CTX *mem_ctx,
+ SPOOL_Q_DELETEPRINTERDRIVER *q_u,
+ const char *server,
+ const char* arch,
+ const char* driver
+)
+{
+ DEBUG(5,("make_spoolss_q_deleteprinterdriver\n"));
+
+ q_u->server_ptr = (server!=NULL)?1:0;
+
+ /* these must be NULL terminated or else NT4 will
+ complain about invalid parameters --jerry */
+ init_unistr2(&q_u->server, server, UNI_STR_TERMINATE);
+ init_unistr2(&q_u->arch, arch, UNI_STR_TERMINATE);
+ init_unistr2(&q_u->driver, driver, UNI_STR_TERMINATE);
+
+ return True;
+}
+
+/*******************************************************************
+ * make a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_getprinterdata(SPOOL_Q_GETPRINTERDATA *q_u,
+ const POLICY_HND *handle,
+ const char *valuename, uint32 size)
+{
+ if (q_u == NULL) return False;
+
+ DEBUG(5,("make_spoolss_q_getprinterdata\n"));
+
+ q_u->handle = *handle;
+ init_unistr2(&q_u->valuename, valuename, UNI_STR_TERMINATE);
+ q_u->size = size;
+
+ return True;
+}
+
+/*******************************************************************
+ * make a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_getprinterdataex(SPOOL_Q_GETPRINTERDATAEX *q_u,
+ const POLICY_HND *handle,
+ const char *keyname,
+ const char *valuename, uint32 size)
+{
+ if (q_u == NULL) return False;
+
+ DEBUG(5,("make_spoolss_q_getprinterdataex\n"));
+
+ q_u->handle = *handle;
+ init_unistr2(&q_u->valuename, valuename, UNI_STR_TERMINATE);
+ init_unistr2(&q_u->keyname, keyname, UNI_STR_TERMINATE);
+ q_u->size = size;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_getprinterdata (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_getprinterdata(const char *desc, SPOOL_Q_GETPRINTERDATA *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_getprinterdata");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_unistr2("valuename", &q_u->valuename,True,ps,depth))
+ return False;
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("size", ps, depth, &q_u->size))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_deleteprinterdata (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_deleteprinterdata(const char *desc, SPOOL_Q_DELETEPRINTERDATA *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_deleteprinterdata");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_unistr2("valuename", &q_u->valuename,True,ps,depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_deleteprinterdata (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_deleteprinterdata(const char *desc, SPOOL_R_DELETEPRINTERDATA *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_deleteprinterdata");
+ depth++;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_deleteprinterdataex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_deleteprinterdataex(const char *desc, SPOOL_Q_DELETEPRINTERDATAEX *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_deleteprinterdataex");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+
+ if (!smb_io_unistr2("keyname ", &q_u->keyname, True, ps, depth))
+ return False;
+ if (!smb_io_unistr2("valuename", &q_u->valuename, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_deleteprinterdataex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_deleteprinterdataex(const char *desc, SPOOL_R_DELETEPRINTERDATAEX *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_deleteprinterdataex");
+ depth++;
+
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_getprinterdata (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_getprinterdata(const char *desc, SPOOL_R_GETPRINTERDATA *r_u, prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_r_getprinterdata");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("type", ps, depth, &r_u->type))
+ return False;
+ if (!prs_uint32("size", ps, depth, &r_u->size))
+ return False;
+
+ if (UNMARSHALLING(ps) && r_u->size) {
+ r_u->data = (unsigned char *)prs_alloc_mem(ps, r_u->size);
+ if(!r_u->data)
+ return False;
+ }
+
+ if (!prs_uint8s( False, "data", ps, depth, r_u->data, r_u->size ))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * make a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_closeprinter(SPOOL_Q_CLOSEPRINTER *q_u, POLICY_HND *hnd)
+{
+ if (q_u == NULL) return False;
+
+ DEBUG(5,("make_spoolss_q_closeprinter\n"));
+
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from static spoolss_q_abortprinter (srv_spoolss.c)
+ * called from spoolss_abortprinter (cli_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_abortprinter(const char *desc, SPOOL_Q_ABORTPRINTER *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_abortprinter");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_abortprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_abortprinter(const char *desc, SPOOL_R_ABORTPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_abortprinter");
+ depth++;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from static spoolss_q_deleteprinter (srv_spoolss.c)
+ * called from spoolss_deleteprinter (cli_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_deleteprinter(const char *desc, SPOOL_Q_DELETEPRINTER *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_deleteprinter");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from static spoolss_r_deleteprinter (srv_spoolss.c)
+ * called from spoolss_deleteprinter (cli_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_deleteprinter(const char *desc, SPOOL_R_DELETEPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_deleteprinter");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!smb_io_pol_hnd("printer handle",&r_u->handle,ps,depth))
+ return False;
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ * read a structure.
+ * called from api_spoolss_deleteprinterdriver (srv_spoolss.c)
+ * called from spoolss_deleteprinterdriver (cli_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_deleteprinterdriver(const char *desc, SPOOL_Q_DELETEPRINTERDRIVER *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_deleteprinterdriver");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("server_ptr", ps, depth, &q_u->server_ptr))
+ return False;
+ if(!smb_io_unistr2("server", &q_u->server, q_u->server_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("arch", &q_u->arch, True, ps, depth))
+ return False;
+ if(!smb_io_unistr2("driver", &q_u->driver, True, ps, depth))
+ return False;
+
+
+ return True;
+}
+
+
+/*******************************************************************
+ * write a structure.
+ ********************************************************************/
+BOOL spoolss_io_r_deleteprinterdriver(const char *desc, SPOOL_R_DELETEPRINTERDRIVER *r_u, prs_struct *ps, int depth)
+{
+ if (r_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_r_deleteprinterdriver");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ * read a structure.
+ * called from api_spoolss_deleteprinterdriver (srv_spoolss.c)
+ * called from spoolss_deleteprinterdriver (cli_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_deleteprinterdriverex(const char *desc, SPOOL_Q_DELETEPRINTERDRIVEREX *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_deleteprinterdriverex");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("server_ptr", ps, depth, &q_u->server_ptr))
+ return False;
+ if(!smb_io_unistr2("server", &q_u->server, q_u->server_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("arch", &q_u->arch, True, ps, depth))
+ return False;
+ if(!smb_io_unistr2("driver", &q_u->driver, True, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("delete_flags ", ps, depth, &q_u->delete_flags))
+ return False;
+ if(!prs_uint32("version ", ps, depth, &q_u->version))
+ return False;
+
+
+ return True;
+}
+
+
+/*******************************************************************
+ * write a structure.
+ ********************************************************************/
+BOOL spoolss_io_r_deleteprinterdriverex(const char *desc, SPOOL_R_DELETEPRINTERDRIVEREX *r_u, prs_struct *ps, int depth)
+{
+ if (r_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_r_deleteprinterdriverex");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+
+
+/*******************************************************************
+ * read a structure.
+ * called from static spoolss_q_closeprinter (srv_spoolss.c)
+ * called from spoolss_closeprinter (cli_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_closeprinter(const char *desc, SPOOL_Q_CLOSEPRINTER *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_closeprinter");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from static spoolss_r_closeprinter (srv_spoolss.c)
+ * called from spoolss_closeprinter (cli_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_closeprinter(const char *desc, SPOOL_R_CLOSEPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_closeprinter");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!smb_io_pol_hnd("printer handle",&r_u->handle,ps,depth))
+ return False;
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_startdocprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_startdocprinter(const char *desc, SPOOL_Q_STARTDOCPRINTER *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_startdocprinter");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+
+ if(!smb_io_doc_info_container("",&q_u->doc_info_container, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_startdocprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_startdocprinter(const char *desc, SPOOL_R_STARTDOCPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_startdocprinter");
+ depth++;
+ if(!prs_uint32("jobid", ps, depth, &r_u->jobid))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_enddocprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_enddocprinter(const char *desc, SPOOL_Q_ENDDOCPRINTER *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_enddocprinter");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_enddocprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_enddocprinter(const char *desc, SPOOL_R_ENDDOCPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_enddocprinter");
+ depth++;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_startpageprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_startpageprinter(const char *desc, SPOOL_Q_STARTPAGEPRINTER *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_startpageprinter");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_startpageprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_startpageprinter(const char *desc, SPOOL_R_STARTPAGEPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_startpageprinter");
+ depth++;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_endpageprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_endpageprinter(const char *desc, SPOOL_Q_ENDPAGEPRINTER *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_endpageprinter");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_endpageprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_endpageprinter(const char *desc, SPOOL_R_ENDPAGEPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_endpageprinter");
+ depth++;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_writeprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_writeprinter(const char *desc, SPOOL_Q_WRITEPRINTER *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_writeprinter");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+ if(!prs_uint32("buffer_size", ps, depth, &q_u->buffer_size))
+ return False;
+
+ if (q_u->buffer_size!=0)
+ {
+ if (UNMARSHALLING(ps))
+ q_u->buffer=(uint8 *)prs_alloc_mem(ps,q_u->buffer_size*sizeof(uint8));
+ if(q_u->buffer == NULL)
+ return False;
+ if(!prs_uint8s(True, "buffer", ps, depth, q_u->buffer, q_u->buffer_size))
+ return False;
+ }
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("buffer_size2", ps, depth, &q_u->buffer_size2))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_writeprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_writeprinter(const char *desc, SPOOL_R_WRITEPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_writeprinter");
+ depth++;
+ if(!prs_uint32("buffer_written", ps, depth, &r_u->buffer_written))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_rffpcnex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_rffpcnex(const char *desc, SPOOL_Q_RFFPCNEX *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_rffpcnex");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+ if(!prs_uint32("flags", ps, depth, &q_u->flags))
+ return False;
+ if(!prs_uint32("options", ps, depth, &q_u->options))
+ return False;
+ if(!prs_uint32("localmachine_ptr", ps, depth, &q_u->localmachine_ptr))
+ return False;
+ if(!smb_io_unistr2("localmachine", &q_u->localmachine, q_u->localmachine_ptr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("printerlocal", ps, depth, &q_u->printerlocal))
+ return False;
+
+ if(!prs_uint32("option_ptr", ps, depth, &q_u->option_ptr))
+ return False;
+
+ if (q_u->option_ptr!=0) {
+
+ if (UNMARSHALLING(ps))
+ if((q_u->option=(SPOOL_NOTIFY_OPTION *)prs_alloc_mem(ps,sizeof(SPOOL_NOTIFY_OPTION))) == NULL)
+ return False;
+
+ if(!smb_io_notify_option("notify option", q_u->option, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_rffpcnex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_rffpcnex(const char *desc, SPOOL_R_RFFPCNEX *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_rffpcnex");
+ depth++;
+
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_rfnpcnex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_rfnpcnex(const char *desc, SPOOL_Q_RFNPCNEX *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_rfnpcnex");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+
+ if(!prs_uint32("change", ps, depth, &q_u->change))
+ return False;
+
+ if(!prs_uint32("option_ptr", ps, depth, &q_u->option_ptr))
+ return False;
+
+ if (q_u->option_ptr!=0) {
+
+ if (UNMARSHALLING(ps))
+ if((q_u->option=(SPOOL_NOTIFY_OPTION *)prs_alloc_mem(ps,sizeof(SPOOL_NOTIFY_OPTION))) == NULL)
+ return False;
+
+ if(!smb_io_notify_option("notify option", q_u->option, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_rfnpcnex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_rfnpcnex(const char *desc, SPOOL_R_RFNPCNEX *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_rfnpcnex");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("info_ptr", ps, depth, &r_u->info_ptr))
+ return False;
+
+ if(!smb_io_notify_info("notify info", &r_u->info ,ps,depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * return the length of a uint16 (obvious, but the code is clean)
+ ********************************************************************/
+
+static uint32 size_of_uint16(uint16 *value)
+{
+ return (sizeof(*value));
+}
+
+/*******************************************************************
+ * return the length of a uint32 (obvious, but the code is clean)
+ ********************************************************************/
+
+static uint32 size_of_uint32(uint32 *value)
+{
+ return (sizeof(*value));
+}
+
+/*******************************************************************
+ * return the length of a NTTIME (obvious, but the code is clean)
+ ********************************************************************/
+
+static uint32 size_of_nttime(NTTIME *value)
+{
+ return (sizeof(*value));
+}
+
+/*******************************************************************
+ * return the length of a UNICODE string in number of char, includes:
+ * - the leading zero
+ * - the relative pointer size
+ ********************************************************************/
+
+static uint32 size_of_relative_string(UNISTR *string)
+{
+ uint32 size=0;
+
+ size=str_len_uni(string); /* the string length */
+ size=size+1; /* add the trailing zero */
+ size=size*2; /* convert in char */
+ size=size+4; /* add the size of the ptr */
+
+#if 0 /* JERRY */
+ /*
+ * Do not include alignment as Win2k does not align relative
+ * strings within a buffer --jerry
+ */
+ /* Ensure size is 4 byte multiple (prs_align is being called...). */
+ /* size += ((4 - (size & 3)) & 3); */
+#endif
+
+ return size;
+}
+
+/*******************************************************************
+ * return the length of a uint32 (obvious, but the code is clean)
+ ********************************************************************/
+
+static uint32 size_of_device_mode(DEVICEMODE *devmode)
+{
+ if (devmode==NULL)
+ return (4);
+ else
+ return (4+devmode->size+devmode->driverextra);
+}
+
+/*******************************************************************
+ * return the length of a uint32 (obvious, but the code is clean)
+ ********************************************************************/
+
+static uint32 size_of_systemtime(SYSTEMTIME *systime)
+{
+ if (systime==NULL)
+ return (4);
+ else
+ return (sizeof(SYSTEMTIME) +4);
+}
+
+/*******************************************************************
+ * write a UNICODE string and its relative pointer.
+ * used by all the RPC structs passing a buffer
+ *
+ * As I'm a nice guy, I'm forcing myself to explain this code.
+ * MS did a good job in the overall spoolss code except in some
+ * functions where they are passing the API buffer directly in the
+ * RPC request/reply. That's to maintain compatiility at the API level.
+ * They could have done it the good way the first time.
+ *
+ * So what happen is: the strings are written at the buffer's end,
+ * in the reverse order of the original structure. Some pointers to
+ * the strings are also in the buffer. Those are relative to the
+ * buffer's start.
+ *
+ * If you don't understand or want to change that function,
+ * first get in touch with me: jfm@samba.org
+ *
+ ********************************************************************/
+
+static BOOL smb_io_relstr(const char *desc, NEW_BUFFER *buffer, int depth, UNISTR *string)
+{
+ prs_struct *ps=&buffer->prs;
+
+ if (MARSHALLING(ps)) {
+ uint32 struct_offset = prs_offset(ps);
+ uint32 relative_offset;
+
+ buffer->string_at_end -= (size_of_relative_string(string) - 4);
+ if(!prs_set_offset(ps, buffer->string_at_end))
+ return False;
+#if 0 /* JERRY */
+ /*
+ * Win2k does not align strings in a buffer
+ * Tested against WinNT 4.0 SP 6a & 2k SP2 --jerry
+ */
+ if (!prs_align(ps))
+ return False;
+#endif
+ buffer->string_at_end = prs_offset(ps);
+
+ /* write the string */
+ if (!smb_io_unistr(desc, string, ps, depth))
+ return False;
+
+ if(!prs_set_offset(ps, struct_offset))
+ return False;
+
+ relative_offset=buffer->string_at_end - buffer->struct_start;
+ /* write its offset */
+ if (!prs_uint32("offset", ps, depth, &relative_offset))
+ return False;
+ }
+ else {
+ uint32 old_offset;
+
+ /* read the offset */
+ if (!prs_uint32("offset", ps, depth, &(buffer->string_at_end)))
+ return False;
+
+ if (buffer->string_at_end == 0)
+ return True;
+
+ old_offset = prs_offset(ps);
+ if(!prs_set_offset(ps, buffer->string_at_end+buffer->struct_start))
+ return False;
+
+ /* read the string */
+ if (!smb_io_unistr(desc, string, ps, depth))
+ return False;
+
+ if(!prs_set_offset(ps, old_offset))
+ return False;
+ }
+ return True;
+}
+
+/*******************************************************************
+ * write a array of UNICODE strings and its relative pointer.
+ * used by 2 RPC structs
+ ********************************************************************/
+
+static BOOL smb_io_relarraystr(const char *desc, NEW_BUFFER *buffer, int depth, uint16 **string)
+{
+ UNISTR chaine;
+
+ prs_struct *ps=&buffer->prs;
+
+ if (MARSHALLING(ps)) {
+ uint32 struct_offset = prs_offset(ps);
+ uint32 relative_offset;
+ uint16 *p;
+ uint16 *q;
+ uint16 zero=0;
+ p=*string;
+ q=*string;
+
+ /* first write the last 0 */
+ buffer->string_at_end -= 2;
+ if(!prs_set_offset(ps, buffer->string_at_end))
+ return False;
+
+ if(!prs_uint16("leading zero", ps, depth, &zero))
+ return False;
+
+ while (p && (*p!=0)) {
+ while (*q!=0)
+ q++;
+
+ /* Yes this should be malloc not talloc. Don't change. */
+
+ chaine.buffer = malloc((q-p+1)*sizeof(uint16));
+ if (chaine.buffer == NULL)
+ return False;
+
+ memcpy(chaine.buffer, p, (q-p+1)*sizeof(uint16));
+
+ buffer->string_at_end -= (q-p+1)*sizeof(uint16);
+
+ if(!prs_set_offset(ps, buffer->string_at_end)) {
+ SAFE_FREE(chaine.buffer);
+ return False;
+ }
+
+ /* write the string */
+ if (!smb_io_unistr(desc, &chaine, ps, depth)) {
+ SAFE_FREE(chaine.buffer);
+ return False;
+ }
+ q++;
+ p=q;
+
+ SAFE_FREE(chaine.buffer);
+ }
+
+ if(!prs_set_offset(ps, struct_offset))
+ return False;
+
+ relative_offset=buffer->string_at_end - buffer->struct_start;
+ /* write its offset */
+ if (!prs_uint32("offset", ps, depth, &relative_offset))
+ return False;
+
+ } else {
+
+ /* UNMARSHALLING */
+
+ uint32 old_offset;
+ uint16 *chaine2=NULL;
+ int l_chaine=0;
+ int l_chaine2=0;
+ size_t realloc_size = 0;
+
+ *string=NULL;
+
+ /* read the offset */
+ if (!prs_uint32("offset", ps, depth, &buffer->string_at_end))
+ return False;
+
+ old_offset = prs_offset(ps);
+ if(!prs_set_offset(ps, buffer->string_at_end + buffer->struct_start))
+ return False;
+
+ do {
+ if (!smb_io_unistr(desc, &chaine, ps, depth))
+ return False;
+
+ l_chaine=str_len_uni(&chaine);
+
+ /* we're going to add two more bytes here in case this
+ is the last string in the array and we need to add
+ an extra NULL for termination */
+ if (l_chaine > 0)
+ {
+ uint16 *tc2;
+
+ realloc_size = (l_chaine2+l_chaine+2)*sizeof(uint16);
+
+ /* Yes this should be realloc - it's freed below. JRA */
+
+ if((tc2=(uint16 *)Realloc(chaine2, realloc_size)) == NULL) {
+ SAFE_FREE(chaine2);
+ return False;
+ }
+ else chaine2 = tc2;
+ memcpy(chaine2+l_chaine2, chaine.buffer, (l_chaine+1)*sizeof(uint16));
+ l_chaine2+=l_chaine+1;
+ }
+
+ } while(l_chaine!=0);
+
+ /* the end should be bould NULL terminated so add
+ the second one here */
+ if (chaine2)
+ {
+ chaine2[l_chaine2] = '\0';
+ *string=(uint16 *)talloc_memdup(prs_get_mem_context(ps),chaine2,realloc_size);
+ SAFE_FREE(chaine2);
+ }
+
+ if(!prs_set_offset(ps, old_offset))
+ return False;
+ }
+ return True;
+}
+
+/*******************************************************************
+ Parse a DEVMODE structure and its relative pointer.
+********************************************************************/
+
+static BOOL smb_io_relsecdesc(const char *desc, NEW_BUFFER *buffer, int depth, SEC_DESC **secdesc)
+{
+ prs_struct *ps= &buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_relsecdesc");
+ depth++;
+
+ if (MARSHALLING(ps)) {
+ uint32 struct_offset = prs_offset(ps);
+ uint32 relative_offset;
+
+ if (! *secdesc) {
+ relative_offset = 0;
+ if (!prs_uint32("offset", ps, depth, &relative_offset))
+ return False;
+ return True;
+ }
+
+ if (*secdesc != NULL) {
+ buffer->string_at_end -= sec_desc_size(*secdesc);
+
+ if(!prs_set_offset(ps, buffer->string_at_end))
+ return False;
+ /* write the secdesc */
+ if (!sec_io_desc(desc, secdesc, ps, depth))
+ return False;
+
+ if(!prs_set_offset(ps, struct_offset))
+ return False;
+ }
+
+ relative_offset=buffer->string_at_end - buffer->struct_start;
+ /* write its offset */
+
+ if (!prs_uint32("offset", ps, depth, &relative_offset))
+ return False;
+ } else {
+ uint32 old_offset;
+
+ /* read the offset */
+ if (!prs_uint32("offset", ps, depth, &buffer->string_at_end))
+ return False;
+
+ old_offset = prs_offset(ps);
+ if(!prs_set_offset(ps, buffer->string_at_end + buffer->struct_start))
+ return False;
+
+ /* read the sd */
+ if (!sec_io_desc(desc, secdesc, ps, depth))
+ return False;
+
+ if(!prs_set_offset(ps, old_offset))
+ return False;
+ }
+ return True;
+}
+
+/*******************************************************************
+ Parse a DEVMODE structure and its relative pointer.
+********************************************************************/
+
+static BOOL smb_io_reldevmode(const char *desc, NEW_BUFFER *buffer, int depth, DEVICEMODE **devmode)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_reldevmode");
+ depth++;
+
+ if (MARSHALLING(ps)) {
+ uint32 struct_offset = prs_offset(ps);
+ uint32 relative_offset;
+
+ if (*devmode == NULL) {
+ relative_offset=0;
+ if (!prs_uint32("offset", ps, depth, &relative_offset))
+ return False;
+ DEBUG(8, ("boing, the devmode was NULL\n"));
+
+ return True;
+ }
+
+ buffer->string_at_end -= ((*devmode)->size + (*devmode)->driverextra);
+
+ if(!prs_set_offset(ps, buffer->string_at_end))
+ return False;
+
+ /* write the DEVMODE */
+ if (!spoolss_io_devmode(desc, ps, depth, *devmode))
+ return False;
+
+ if(!prs_set_offset(ps, struct_offset))
+ return False;
+
+ relative_offset=buffer->string_at_end - buffer->struct_start;
+ /* write its offset */
+ if (!prs_uint32("offset", ps, depth, &relative_offset))
+ return False;
+ }
+ else {
+ uint32 old_offset;
+
+ /* read the offset */
+ if (!prs_uint32("offset", ps, depth, &buffer->string_at_end))
+ return False;
+ if (buffer->string_at_end == 0) {
+ *devmode = NULL;
+ return True;
+ }
+
+ old_offset = prs_offset(ps);
+ if(!prs_set_offset(ps, buffer->string_at_end + buffer->struct_start))
+ return False;
+
+ /* read the string */
+ if((*devmode=(DEVICEMODE *)prs_alloc_mem(ps,sizeof(DEVICEMODE))) == NULL)
+ return False;
+ if (!spoolss_io_devmode(desc, ps, depth, *devmode))
+ return False;
+
+ if(!prs_set_offset(ps, old_offset))
+ return False;
+ }
+ return True;
+}
+
+/*******************************************************************
+ Parse a PRINTER_INFO_0 structure.
+********************************************************************/
+
+BOOL smb_io_printer_info_0(const char *desc, NEW_BUFFER *buffer, PRINTER_INFO_0 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printer_info_0");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!smb_io_relstr("printername", buffer, depth, &info->printername))
+ return False;
+ if (!smb_io_relstr("servername", buffer, depth, &info->servername))
+ return False;
+
+ if(!prs_uint32("cjobs", ps, depth, &info->cjobs))
+ return False;
+ if(!prs_uint32("total_jobs", ps, depth, &info->total_jobs))
+ return False;
+ if(!prs_uint32("total_bytes", ps, depth, &info->total_bytes))
+ return False;
+
+ if(!prs_uint16("year", ps, depth, &info->year))
+ return False;
+ if(!prs_uint16("month", ps, depth, &info->month))
+ return False;
+ if(!prs_uint16("dayofweek", ps, depth, &info->dayofweek))
+ return False;
+ if(!prs_uint16("day", ps, depth, &info->day))
+ return False;
+ if(!prs_uint16("hour", ps, depth, &info->hour))
+ return False;
+ if(!prs_uint16("minute", ps, depth, &info->minute))
+ return False;
+ if(!prs_uint16("second", ps, depth, &info->second))
+ return False;
+ if(!prs_uint16("milliseconds", ps, depth, &info->milliseconds))
+ return False;
+
+ if(!prs_uint32("global_counter", ps, depth, &info->global_counter))
+ return False;
+ if(!prs_uint32("total_pages", ps, depth, &info->total_pages))
+ return False;
+
+ if(!prs_uint16("major_version", ps, depth, &info->major_version))
+ return False;
+ if(!prs_uint16("build_version", ps, depth, &info->build_version))
+ return False;
+ if(!prs_uint32("unknown7", ps, depth, &info->unknown7))
+ return False;
+ if(!prs_uint32("unknown8", ps, depth, &info->unknown8))
+ return False;
+ if(!prs_uint32("unknown9", ps, depth, &info->unknown9))
+ return False;
+ if(!prs_uint32("session_counter", ps, depth, &info->session_counter))
+ return False;
+ if(!prs_uint32("unknown11", ps, depth, &info->unknown11))
+ return False;
+ if(!prs_uint32("printer_errors", ps, depth, &info->printer_errors))
+ return False;
+ if(!prs_uint32("unknown13", ps, depth, &info->unknown13))
+ return False;
+ if(!prs_uint32("unknown14", ps, depth, &info->unknown14))
+ return False;
+ if(!prs_uint32("unknown15", ps, depth, &info->unknown15))
+ return False;
+ if(!prs_uint32("unknown16", ps, depth, &info->unknown16))
+ return False;
+ if(!prs_uint32("change_id", ps, depth, &info->change_id))
+ return False;
+ if(!prs_uint32("unknown18", ps, depth, &info->unknown18))
+ return False;
+ if(!prs_uint32("status" , ps, depth, &info->status))
+ return False;
+ if(!prs_uint32("unknown20", ps, depth, &info->unknown20))
+ return False;
+ if(!prs_uint32("c_setprinter", ps, depth, &info->c_setprinter))
+ return False;
+ if(!prs_uint16("unknown22", ps, depth, &info->unknown22))
+ return False;
+ if(!prs_uint16("unknown23", ps, depth, &info->unknown23))
+ return False;
+ if(!prs_uint16("unknown24", ps, depth, &info->unknown24))
+ return False;
+ if(!prs_uint16("unknown25", ps, depth, &info->unknown25))
+ return False;
+ if(!prs_uint16("unknown26", ps, depth, &info->unknown26))
+ return False;
+ if(!prs_uint16("unknown27", ps, depth, &info->unknown27))
+ return False;
+ if(!prs_uint16("unknown28", ps, depth, &info->unknown28))
+ return False;
+ if(!prs_uint16("unknown29", ps, depth, &info->unknown29))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a PRINTER_INFO_1 structure.
+********************************************************************/
+
+BOOL smb_io_printer_info_1(const char *desc, NEW_BUFFER *buffer, PRINTER_INFO_1 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printer_info_1");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!prs_uint32("flags", ps, depth, &info->flags))
+ return False;
+ if (!smb_io_relstr("description", buffer, depth, &info->description))
+ return False;
+ if (!smb_io_relstr("name", buffer, depth, &info->name))
+ return False;
+ if (!smb_io_relstr("comment", buffer, depth, &info->comment))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a PRINTER_INFO_2 structure.
+********************************************************************/
+
+BOOL smb_io_printer_info_2(const char *desc, NEW_BUFFER *buffer, PRINTER_INFO_2 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+ uint32 dm_offset, sd_offset, current_offset;
+ uint32 dummy_value = 0, has_secdesc = 0;
+
+ prs_debug(ps, depth, desc, "smb_io_printer_info_2");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!smb_io_relstr("servername", buffer, depth, &info->servername))
+ return False;
+ if (!smb_io_relstr("printername", buffer, depth, &info->printername))
+ return False;
+ if (!smb_io_relstr("sharename", buffer, depth, &info->sharename))
+ return False;
+ if (!smb_io_relstr("portname", buffer, depth, &info->portname))
+ return False;
+ if (!smb_io_relstr("drivername", buffer, depth, &info->drivername))
+ return False;
+ if (!smb_io_relstr("comment", buffer, depth, &info->comment))
+ return False;
+ if (!smb_io_relstr("location", buffer, depth, &info->location))
+ return False;
+
+ /* save current offset and wind forwared by a uint32 */
+ dm_offset = prs_offset(ps);
+ if (!prs_uint32("devmode", ps, depth, &dummy_value))
+ return False;
+
+ if (!smb_io_relstr("sepfile", buffer, depth, &info->sepfile))
+ return False;
+ if (!smb_io_relstr("printprocessor", buffer, depth, &info->printprocessor))
+ return False;
+ if (!smb_io_relstr("datatype", buffer, depth, &info->datatype))
+ return False;
+ if (!smb_io_relstr("parameters", buffer, depth, &info->parameters))
+ return False;
+
+ /* save current offset for the sec_desc */
+ sd_offset = prs_offset(ps);
+ if (!prs_uint32("sec_desc", ps, depth, &has_secdesc))
+ return False;
+
+
+ /* save current location so we can pick back up here */
+ current_offset = prs_offset(ps);
+
+ /* parse the devmode */
+ if (!prs_set_offset(ps, dm_offset))
+ return False;
+ if (!smb_io_reldevmode("devmode", buffer, depth, &info->devmode))
+ return False;
+
+ /* parse the sec_desc */
+ if (info->secdesc) {
+ if (!prs_set_offset(ps, sd_offset))
+ return False;
+ if (!smb_io_relsecdesc("secdesc", buffer, depth, &info->secdesc))
+ return False;
+ }
+
+ /* pick up where we left off */
+ if (!prs_set_offset(ps, current_offset))
+ return False;
+
+ if (!prs_uint32("attributes", ps, depth, &info->attributes))
+ return False;
+ if (!prs_uint32("priority", ps, depth, &info->priority))
+ return False;
+ if (!prs_uint32("defpriority", ps, depth, &info->defaultpriority))
+ return False;
+ if (!prs_uint32("starttime", ps, depth, &info->starttime))
+ return False;
+ if (!prs_uint32("untiltime", ps, depth, &info->untiltime))
+ return False;
+ if (!prs_uint32("status", ps, depth, &info->status))
+ return False;
+ if (!prs_uint32("jobs", ps, depth, &info->cjobs))
+ return False;
+ if (!prs_uint32("averageppm", ps, depth, &info->averageppm))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a PRINTER_INFO_3 structure.
+********************************************************************/
+
+BOOL smb_io_printer_info_3(const char *desc, NEW_BUFFER *buffer, PRINTER_INFO_3 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printer_info_3");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!prs_uint32("flags", ps, depth, &info->flags))
+ return False;
+ if (!sec_io_desc("sec_desc", &info->secdesc, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a PRINTER_INFO_4 structure.
+********************************************************************/
+
+BOOL smb_io_printer_info_4(const char *desc, NEW_BUFFER *buffer, PRINTER_INFO_4 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printer_info_4");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!smb_io_relstr("printername", buffer, depth, &info->printername))
+ return False;
+ if (!smb_io_relstr("servername", buffer, depth, &info->servername))
+ return False;
+ if (!prs_uint32("attributes", ps, depth, &info->attributes))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+ Parse a PRINTER_INFO_5 structure.
+********************************************************************/
+
+BOOL smb_io_printer_info_5(const char *desc, NEW_BUFFER *buffer, PRINTER_INFO_5 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printer_info_5");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!smb_io_relstr("printername", buffer, depth, &info->printername))
+ return False;
+ if (!smb_io_relstr("portname", buffer, depth, &info->portname))
+ return False;
+ if (!prs_uint32("attributes", ps, depth, &info->attributes))
+ return False;
+ if (!prs_uint32("device_not_selected_timeout", ps, depth, &info->device_not_selected_timeout))
+ return False;
+ if (!prs_uint32("transmission_retry_timeout", ps, depth, &info->transmission_retry_timeout))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+ Parse a PRINTER_INFO_7 structure.
+********************************************************************/
+
+BOOL smb_io_printer_info_7(const char *desc, NEW_BUFFER *buffer, PRINTER_INFO_7 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printer_info_7");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!smb_io_relstr("guid", buffer, depth, &info->guid))
+ return False;
+ if (!prs_uint32("action", ps, depth, &info->action))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+ Parse a PORT_INFO_1 structure.
+********************************************************************/
+
+BOOL smb_io_port_info_1(const char *desc, NEW_BUFFER *buffer, PORT_INFO_1 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_port_info_1");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!smb_io_relstr("port_name", buffer, depth, &info->port_name))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a PORT_INFO_2 structure.
+********************************************************************/
+
+BOOL smb_io_port_info_2(const char *desc, NEW_BUFFER *buffer, PORT_INFO_2 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_port_info_2");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!smb_io_relstr("port_name", buffer, depth, &info->port_name))
+ return False;
+ if (!smb_io_relstr("monitor_name", buffer, depth, &info->monitor_name))
+ return False;
+ if (!smb_io_relstr("description", buffer, depth, &info->description))
+ return False;
+ if (!prs_uint32("port_type", ps, depth, &info->port_type))
+ return False;
+ if (!prs_uint32("reserved", ps, depth, &info->reserved))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a DRIVER_INFO_1 structure.
+********************************************************************/
+
+BOOL smb_io_printer_driver_info_1(const char *desc, NEW_BUFFER *buffer, DRIVER_INFO_1 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printer_driver_info_1");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!smb_io_relstr("name", buffer, depth, &info->name))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a DRIVER_INFO_2 structure.
+********************************************************************/
+
+BOOL smb_io_printer_driver_info_2(const char *desc, NEW_BUFFER *buffer, DRIVER_INFO_2 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printer_driver_info_2");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!prs_uint32("version", ps, depth, &info->version))
+ return False;
+ if (!smb_io_relstr("name", buffer, depth, &info->name))
+ return False;
+ if (!smb_io_relstr("architecture", buffer, depth, &info->architecture))
+ return False;
+ if (!smb_io_relstr("driverpath", buffer, depth, &info->driverpath))
+ return False;
+ if (!smb_io_relstr("datafile", buffer, depth, &info->datafile))
+ return False;
+ if (!smb_io_relstr("configfile", buffer, depth, &info->configfile))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a DRIVER_INFO_3 structure.
+********************************************************************/
+
+BOOL smb_io_printer_driver_info_3(const char *desc, NEW_BUFFER *buffer, DRIVER_INFO_3 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printer_driver_info_3");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!prs_uint32("version", ps, depth, &info->version))
+ return False;
+ if (!smb_io_relstr("name", buffer, depth, &info->name))
+ return False;
+ if (!smb_io_relstr("architecture", buffer, depth, &info->architecture))
+ return False;
+ if (!smb_io_relstr("driverpath", buffer, depth, &info->driverpath))
+ return False;
+ if (!smb_io_relstr("datafile", buffer, depth, &info->datafile))
+ return False;
+ if (!smb_io_relstr("configfile", buffer, depth, &info->configfile))
+ return False;
+ if (!smb_io_relstr("helpfile", buffer, depth, &info->helpfile))
+ return False;
+
+ if (!smb_io_relarraystr("dependentfiles", buffer, depth, &info->dependentfiles))
+ return False;
+
+ if (!smb_io_relstr("monitorname", buffer, depth, &info->monitorname))
+ return False;
+ if (!smb_io_relstr("defaultdatatype", buffer, depth, &info->defaultdatatype))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a DRIVER_INFO_6 structure.
+********************************************************************/
+
+BOOL smb_io_printer_driver_info_6(const char *desc, NEW_BUFFER *buffer, DRIVER_INFO_6 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printer_driver_info_6");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!prs_uint32("version", ps, depth, &info->version))
+ return False;
+ if (!smb_io_relstr("name", buffer, depth, &info->name))
+ return False;
+ if (!smb_io_relstr("architecture", buffer, depth, &info->architecture))
+ return False;
+ if (!smb_io_relstr("driverpath", buffer, depth, &info->driverpath))
+ return False;
+ if (!smb_io_relstr("datafile", buffer, depth, &info->datafile))
+ return False;
+ if (!smb_io_relstr("configfile", buffer, depth, &info->configfile))
+ return False;
+ if (!smb_io_relstr("helpfile", buffer, depth, &info->helpfile))
+ return False;
+
+ if (!smb_io_relarraystr("dependentfiles", buffer, depth, &info->dependentfiles))
+ return False;
+
+ if (!smb_io_relstr("monitorname", buffer, depth, &info->monitorname))
+ return False;
+ if (!smb_io_relstr("defaultdatatype", buffer, depth, &info->defaultdatatype))
+ return False;
+
+ if (!smb_io_relarraystr("previousdrivernames", buffer, depth, &info->previousdrivernames))
+ return False;
+
+ if (!prs_uint32("date.low", ps, depth, &info->driver_date.low))
+ return False;
+ if (!prs_uint32("date.high", ps, depth, &info->driver_date.high))
+ return False;
+
+ if (!prs_uint32("padding", ps, depth, &info->padding))
+ return False;
+
+ if (!prs_uint32("driver_version_low", ps, depth, &info->driver_version_low))
+ return False;
+
+ if (!prs_uint32("driver_version_high", ps, depth, &info->driver_version_high))
+ return False;
+
+ if (!smb_io_relstr("mfgname", buffer, depth, &info->mfgname))
+ return False;
+ if (!smb_io_relstr("oem_url", buffer, depth, &info->oem_url))
+ return False;
+ if (!smb_io_relstr("hardware_id", buffer, depth, &info->hardware_id))
+ return False;
+ if (!smb_io_relstr("provider", buffer, depth, &info->provider))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a JOB_INFO_1 structure.
+********************************************************************/
+
+BOOL smb_io_job_info_1(const char *desc, NEW_BUFFER *buffer, JOB_INFO_1 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_job_info_1");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!prs_uint32("jobid", ps, depth, &info->jobid))
+ return False;
+ if (!smb_io_relstr("printername", buffer, depth, &info->printername))
+ return False;
+ if (!smb_io_relstr("machinename", buffer, depth, &info->machinename))
+ return False;
+ if (!smb_io_relstr("username", buffer, depth, &info->username))
+ return False;
+ if (!smb_io_relstr("document", buffer, depth, &info->document))
+ return False;
+ if (!smb_io_relstr("datatype", buffer, depth, &info->datatype))
+ return False;
+ if (!smb_io_relstr("text_status", buffer, depth, &info->text_status))
+ return False;
+ if (!prs_uint32("status", ps, depth, &info->status))
+ return False;
+ if (!prs_uint32("priority", ps, depth, &info->priority))
+ return False;
+ if (!prs_uint32("position", ps, depth, &info->position))
+ return False;
+ if (!prs_uint32("totalpages", ps, depth, &info->totalpages))
+ return False;
+ if (!prs_uint32("pagesprinted", ps, depth, &info->pagesprinted))
+ return False;
+ if (!spoolss_io_system_time("submitted", ps, depth, &info->submitted))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a JOB_INFO_2 structure.
+********************************************************************/
+
+BOOL smb_io_job_info_2(const char *desc, NEW_BUFFER *buffer, JOB_INFO_2 *info, int depth)
+{
+ uint32 pipo=0;
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_job_info_2");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!prs_uint32("jobid",ps, depth, &info->jobid))
+ return False;
+ if (!smb_io_relstr("printername", buffer, depth, &info->printername))
+ return False;
+ if (!smb_io_relstr("machinename", buffer, depth, &info->machinename))
+ return False;
+ if (!smb_io_relstr("username", buffer, depth, &info->username))
+ return False;
+ if (!smb_io_relstr("document", buffer, depth, &info->document))
+ return False;
+ if (!smb_io_relstr("notifyname", buffer, depth, &info->notifyname))
+ return False;
+ if (!smb_io_relstr("datatype", buffer, depth, &info->datatype))
+ return False;
+
+ if (!smb_io_relstr("printprocessor", buffer, depth, &info->printprocessor))
+ return False;
+ if (!smb_io_relstr("parameters", buffer, depth, &info->parameters))
+ return False;
+ if (!smb_io_relstr("drivername", buffer, depth, &info->drivername))
+ return False;
+ if (!smb_io_reldevmode("devmode", buffer, depth, &info->devmode))
+ return False;
+ if (!smb_io_relstr("text_status", buffer, depth, &info->text_status))
+ return False;
+
+/* SEC_DESC sec_desc;*/
+ if (!prs_uint32("Hack! sec desc", ps, depth, &pipo))
+ return False;
+
+ if (!prs_uint32("status",ps, depth, &info->status))
+ return False;
+ if (!prs_uint32("priority",ps, depth, &info->priority))
+ return False;
+ if (!prs_uint32("position",ps, depth, &info->position))
+ return False;
+ if (!prs_uint32("starttime",ps, depth, &info->starttime))
+ return False;
+ if (!prs_uint32("untiltime",ps, depth, &info->untiltime))
+ return False;
+ if (!prs_uint32("totalpages",ps, depth, &info->totalpages))
+ return False;
+ if (!prs_uint32("size",ps, depth, &info->size))
+ return False;
+ if (!spoolss_io_system_time("submitted", ps, depth, &info->submitted) )
+ return False;
+ if (!prs_uint32("timeelapsed",ps, depth, &info->timeelapsed))
+ return False;
+ if (!prs_uint32("pagesprinted",ps, depth, &info->pagesprinted))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL smb_io_form_1(const char *desc, NEW_BUFFER *buffer, FORM_1 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_form_1");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!prs_uint32("flag", ps, depth, &info->flag))
+ return False;
+
+ if (!smb_io_relstr("name", buffer, depth, &info->name))
+ return False;
+
+ if (!prs_uint32("width", ps, depth, &info->width))
+ return False;
+ if (!prs_uint32("length", ps, depth, &info->length))
+ return False;
+ if (!prs_uint32("left", ps, depth, &info->left))
+ return False;
+ if (!prs_uint32("top", ps, depth, &info->top))
+ return False;
+ if (!prs_uint32("right", ps, depth, &info->right))
+ return False;
+ if (!prs_uint32("bottom", ps, depth, &info->bottom))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Read/write a BUFFER struct.
+********************************************************************/
+
+static BOOL spoolss_io_buffer(const char *desc, prs_struct *ps, int depth, NEW_BUFFER **pp_buffer)
+{
+ NEW_BUFFER *buffer = *pp_buffer;
+
+ prs_debug(ps, depth, desc, "spoolss_io_buffer");
+ depth++;
+
+ if (UNMARSHALLING(ps))
+ buffer = *pp_buffer = (NEW_BUFFER *)prs_alloc_mem(ps, sizeof(NEW_BUFFER));
+
+ if (buffer == NULL)
+ return False;
+
+ if (!prs_uint32("ptr", ps, depth, &buffer->ptr))
+ return False;
+
+ /* reading */
+ if (UNMARSHALLING(ps)) {
+ buffer->size=0;
+ buffer->string_at_end=0;
+
+ if (buffer->ptr==0) {
+ /*
+ * JRA. I'm not sure if the data in here is in big-endian format if
+ * the client is big-endian. Leave as default (little endian) for now.
+ */
+
+ if (!prs_init(&buffer->prs, 0, prs_get_mem_context(ps), UNMARSHALL))
+ return False;
+ return True;
+ }
+
+ if (!prs_uint32("size", ps, depth, &buffer->size))
+ return False;
+
+ /*
+ * JRA. I'm not sure if the data in here is in big-endian format if
+ * the client is big-endian. Leave as default (little endian) for now.
+ */
+
+ if (!prs_init(&buffer->prs, buffer->size, prs_get_mem_context(ps), UNMARSHALL))
+ return False;
+
+ if (!prs_append_some_prs_data(&buffer->prs, ps, prs_offset(ps), buffer->size))
+ return False;
+
+ if (!prs_set_offset(&buffer->prs, 0))
+ return False;
+
+ if (!prs_set_offset(ps, buffer->size+prs_offset(ps)))
+ return False;
+
+ buffer->string_at_end=buffer->size;
+
+ return True;
+ }
+ else {
+ BOOL ret = False;
+
+ /* writing */
+ if (buffer->ptr==0) {
+ /* We have finished with the data in buffer->prs - free it. */
+ prs_mem_free(&buffer->prs);
+ return True;
+ }
+
+ if (!prs_uint32("size", ps, depth, &buffer->size))
+ goto out;
+
+ if (!prs_append_some_prs_data(ps, &buffer->prs, 0, buffer->size))
+ goto out;
+
+ ret = True;
+ out:
+
+ /* We have finished with the data in buffer->prs - free it. */
+ prs_mem_free(&buffer->prs);
+
+ return ret;
+ }
+}
+
+/*******************************************************************
+ move a BUFFER from the query to the reply.
+ As the data pointers in NEW_BUFFER are malloc'ed, not talloc'ed,
+ this is ok. This is an OPTIMIZATION and is not strictly neccessary.
+ Clears the memory to zero also.
+********************************************************************/
+
+void spoolss_move_buffer(NEW_BUFFER *src, NEW_BUFFER **dest)
+{
+ prs_switch_type(&src->prs, MARSHALL);
+ if(!prs_set_offset(&src->prs, 0))
+ return;
+ prs_force_dynamic(&src->prs);
+ prs_mem_clear(&src->prs);
+ *dest=src;
+}
+
+/*******************************************************************
+ Get the size of a BUFFER struct.
+********************************************************************/
+
+uint32 new_get_buffer_size(NEW_BUFFER *buffer)
+{
+ return (buffer->size);
+}
+
+/*******************************************************************
+ Parse a DRIVER_DIRECTORY_1 structure.
+********************************************************************/
+
+BOOL smb_io_driverdir_1(const char *desc, NEW_BUFFER *buffer, DRIVER_DIRECTORY_1 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_driverdir_1");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!smb_io_unistr(desc, &info->name, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a PORT_INFO_1 structure.
+********************************************************************/
+
+BOOL smb_io_port_1(const char *desc, NEW_BUFFER *buffer, PORT_INFO_1 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_port_1");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if(!smb_io_relstr("port_name", buffer, depth, &info->port_name))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a PORT_INFO_2 structure.
+********************************************************************/
+
+BOOL smb_io_port_2(const char *desc, NEW_BUFFER *buffer, PORT_INFO_2 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_port_2");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if(!smb_io_relstr("port_name", buffer, depth, &info->port_name))
+ return False;
+ if(!smb_io_relstr("monitor_name", buffer, depth, &info->monitor_name))
+ return False;
+ if(!smb_io_relstr("description", buffer, depth, &info->description))
+ return False;
+ if(!prs_uint32("port_type", ps, depth, &info->port_type))
+ return False;
+ if(!prs_uint32("reserved", ps, depth, &info->reserved))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL smb_io_printprocessor_info_1(const char *desc, NEW_BUFFER *buffer, PRINTPROCESSOR_1 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printprocessor_info_1");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (smb_io_relstr("name", buffer, depth, &info->name))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL smb_io_printprocdatatype_info_1(const char *desc, NEW_BUFFER *buffer, PRINTPROCDATATYPE_1 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printprocdatatype_info_1");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (smb_io_relstr("name", buffer, depth, &info->name))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL smb_io_printmonitor_info_1(const char *desc, NEW_BUFFER *buffer, PRINTMONITOR_1 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printmonitor_info_1");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!smb_io_relstr("name", buffer, depth, &info->name))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL smb_io_printmonitor_info_2(const char *desc, NEW_BUFFER *buffer, PRINTMONITOR_2 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printmonitor_info_2");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!smb_io_relstr("name", buffer, depth, &info->name))
+ return False;
+ if (!smb_io_relstr("environment", buffer, depth, &info->environment))
+ return False;
+ if (!smb_io_relstr("dll_name", buffer, depth, &info->dll_name))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_info_0(PRINTER_INFO_0 *info)
+{
+ int size=0;
+
+ size+=size_of_relative_string( &info->printername );
+ size+=size_of_relative_string( &info->servername );
+
+ size+=size_of_uint32( &info->cjobs);
+ size+=size_of_uint32( &info->total_jobs);
+ size+=size_of_uint32( &info->total_bytes);
+
+ size+=size_of_uint16( &info->year);
+ size+=size_of_uint16( &info->month);
+ size+=size_of_uint16( &info->dayofweek);
+ size+=size_of_uint16( &info->day);
+ size+=size_of_uint16( &info->hour);
+ size+=size_of_uint16( &info->minute);
+ size+=size_of_uint16( &info->second);
+ size+=size_of_uint16( &info->milliseconds);
+
+ size+=size_of_uint32( &info->global_counter);
+ size+=size_of_uint32( &info->total_pages);
+
+ size+=size_of_uint16( &info->major_version);
+ size+=size_of_uint16( &info->build_version);
+
+ size+=size_of_uint32( &info->unknown7);
+ size+=size_of_uint32( &info->unknown8);
+ size+=size_of_uint32( &info->unknown9);
+ size+=size_of_uint32( &info->session_counter);
+ size+=size_of_uint32( &info->unknown11);
+ size+=size_of_uint32( &info->printer_errors);
+ size+=size_of_uint32( &info->unknown13);
+ size+=size_of_uint32( &info->unknown14);
+ size+=size_of_uint32( &info->unknown15);
+ size+=size_of_uint32( &info->unknown16);
+ size+=size_of_uint32( &info->change_id);
+ size+=size_of_uint32( &info->unknown18);
+ size+=size_of_uint32( &info->status);
+ size+=size_of_uint32( &info->unknown20);
+ size+=size_of_uint32( &info->c_setprinter);
+
+ size+=size_of_uint16( &info->unknown22);
+ size+=size_of_uint16( &info->unknown23);
+ size+=size_of_uint16( &info->unknown24);
+ size+=size_of_uint16( &info->unknown25);
+ size+=size_of_uint16( &info->unknown26);
+ size+=size_of_uint16( &info->unknown27);
+ size+=size_of_uint16( &info->unknown28);
+ size+=size_of_uint16( &info->unknown29);
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_info_1(PRINTER_INFO_1 *info)
+{
+ int size=0;
+
+ size+=size_of_uint32( &info->flags );
+ size+=size_of_relative_string( &info->description );
+ size+=size_of_relative_string( &info->name );
+ size+=size_of_relative_string( &info->comment );
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_info_2(PRINTER_INFO_2 *info)
+{
+ uint32 size=0;
+
+ size += 4;
+
+ size += sec_desc_size( info->secdesc );
+
+ size+=size_of_device_mode( info->devmode );
+
+ size+=size_of_relative_string( &info->servername );
+ size+=size_of_relative_string( &info->printername );
+ size+=size_of_relative_string( &info->sharename );
+ size+=size_of_relative_string( &info->portname );
+ size+=size_of_relative_string( &info->drivername );
+ size+=size_of_relative_string( &info->comment );
+ size+=size_of_relative_string( &info->location );
+
+ size+=size_of_relative_string( &info->sepfile );
+ size+=size_of_relative_string( &info->printprocessor );
+ size+=size_of_relative_string( &info->datatype );
+ size+=size_of_relative_string( &info->parameters );
+
+ size+=size_of_uint32( &info->attributes );
+ size+=size_of_uint32( &info->priority );
+ size+=size_of_uint32( &info->defaultpriority );
+ size+=size_of_uint32( &info->starttime );
+ size+=size_of_uint32( &info->untiltime );
+ size+=size_of_uint32( &info->status );
+ size+=size_of_uint32( &info->cjobs );
+ size+=size_of_uint32( &info->averageppm );
+
+ /*
+ * add any adjustments for alignment. This is
+ * not optimal since we could be calling this
+ * function from a loop (e.g. enumprinters), but
+ * it is easier to maintain the calculation here and
+ * not place the burden on the caller to remember. --jerry
+ */
+ if ((size % 4) != 0)
+ size += 4 - (size % 4);
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_info_4(PRINTER_INFO_4 *info)
+{
+ uint32 size=0;
+
+ size+=size_of_relative_string( &info->printername );
+ size+=size_of_relative_string( &info->servername );
+
+ size+=size_of_uint32( &info->attributes );
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_info_5(PRINTER_INFO_5 *info)
+{
+ uint32 size=0;
+
+ size+=size_of_relative_string( &info->printername );
+ size+=size_of_relative_string( &info->portname );
+
+ size+=size_of_uint32( &info->attributes );
+ size+=size_of_uint32( &info->device_not_selected_timeout );
+ size+=size_of_uint32( &info->transmission_retry_timeout );
+ return size;
+}
+
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_info_3(PRINTER_INFO_3 *info)
+{
+ /* The 4 is for the self relative pointer.. */
+ /* JRA !!!! TESTME - WHAT ABOUT prs_align.... !!! */
+ return 4 + (uint32)sec_desc_size( info->secdesc );
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_info_7(PRINTER_INFO_7 *info)
+{
+ uint32 size=0;
+
+ size+=size_of_relative_string( &info->guid );
+ size+=size_of_uint32( &info->action );
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_driver_info_1(DRIVER_INFO_1 *info)
+{
+ int size=0;
+ size+=size_of_relative_string( &info->name );
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_driver_info_2(DRIVER_INFO_2 *info)
+{
+ int size=0;
+ size+=size_of_uint32( &info->version );
+ size+=size_of_relative_string( &info->name );
+ size+=size_of_relative_string( &info->architecture );
+ size+=size_of_relative_string( &info->driverpath );
+ size+=size_of_relative_string( &info->datafile );
+ size+=size_of_relative_string( &info->configfile );
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a string array.
+********************************************************************/
+
+uint32 spoolss_size_string_array(uint16 *string)
+{
+ uint32 i = 0;
+
+ if (string) {
+ for (i=0; (string[i]!=0x0000) || (string[i+1]!=0x0000); i++);
+ }
+ i=i+2; /* to count all chars including the leading zero */
+ i=2*i; /* because we need the value in bytes */
+ i=i+4; /* the offset pointer size */
+
+ return i;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_driver_info_3(DRIVER_INFO_3 *info)
+{
+ int size=0;
+
+ size+=size_of_uint32( &info->version );
+ size+=size_of_relative_string( &info->name );
+ size+=size_of_relative_string( &info->architecture );
+ size+=size_of_relative_string( &info->driverpath );
+ size+=size_of_relative_string( &info->datafile );
+ size+=size_of_relative_string( &info->configfile );
+ size+=size_of_relative_string( &info->helpfile );
+ size+=size_of_relative_string( &info->monitorname );
+ size+=size_of_relative_string( &info->defaultdatatype );
+
+ size+=spoolss_size_string_array(info->dependentfiles);
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_driver_info_6(DRIVER_INFO_6 *info)
+{
+ uint32 size=0;
+
+ size+=size_of_uint32( &info->version );
+ size+=size_of_relative_string( &info->name );
+ size+=size_of_relative_string( &info->architecture );
+ size+=size_of_relative_string( &info->driverpath );
+ size+=size_of_relative_string( &info->datafile );
+ size+=size_of_relative_string( &info->configfile );
+ size+=size_of_relative_string( &info->helpfile );
+
+ size+=spoolss_size_string_array(info->dependentfiles);
+
+ size+=size_of_relative_string( &info->monitorname );
+ size+=size_of_relative_string( &info->defaultdatatype );
+
+ size+=spoolss_size_string_array(info->previousdrivernames);
+
+ size+=size_of_nttime(&info->driver_date);
+ size+=size_of_uint32( &info->padding );
+ size+=size_of_uint32( &info->driver_version_low );
+ size+=size_of_uint32( &info->driver_version_high );
+ size+=size_of_relative_string( &info->mfgname );
+ size+=size_of_relative_string( &info->oem_url );
+ size+=size_of_relative_string( &info->hardware_id );
+ size+=size_of_relative_string( &info->provider );
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_job_info_1(JOB_INFO_1 *info)
+{
+ int size=0;
+ size+=size_of_uint32( &info->jobid );
+ size+=size_of_relative_string( &info->printername );
+ size+=size_of_relative_string( &info->machinename );
+ size+=size_of_relative_string( &info->username );
+ size+=size_of_relative_string( &info->document );
+ size+=size_of_relative_string( &info->datatype );
+ size+=size_of_relative_string( &info->text_status );
+ size+=size_of_uint32( &info->status );
+ size+=size_of_uint32( &info->priority );
+ size+=size_of_uint32( &info->position );
+ size+=size_of_uint32( &info->totalpages );
+ size+=size_of_uint32( &info->pagesprinted );
+ size+=size_of_systemtime( &info->submitted );
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_job_info_2(JOB_INFO_2 *info)
+{
+ int size=0;
+
+ size+=4; /* size of sec desc ptr */
+
+ size+=size_of_uint32( &info->jobid );
+ size+=size_of_relative_string( &info->printername );
+ size+=size_of_relative_string( &info->machinename );
+ size+=size_of_relative_string( &info->username );
+ size+=size_of_relative_string( &info->document );
+ size+=size_of_relative_string( &info->notifyname );
+ size+=size_of_relative_string( &info->datatype );
+ size+=size_of_relative_string( &info->printprocessor );
+ size+=size_of_relative_string( &info->parameters );
+ size+=size_of_relative_string( &info->drivername );
+ size+=size_of_device_mode( info->devmode );
+ size+=size_of_relative_string( &info->text_status );
+/* SEC_DESC sec_desc;*/
+ size+=size_of_uint32( &info->status );
+ size+=size_of_uint32( &info->priority );
+ size+=size_of_uint32( &info->position );
+ size+=size_of_uint32( &info->starttime );
+ size+=size_of_uint32( &info->untiltime );
+ size+=size_of_uint32( &info->totalpages );
+ size+=size_of_uint32( &info->size );
+ size+=size_of_systemtime( &info->submitted );
+ size+=size_of_uint32( &info->timeelapsed );
+ size+=size_of_uint32( &info->pagesprinted );
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_form_1(FORM_1 *info)
+{
+ int size=0;
+
+ size+=size_of_uint32( &info->flag );
+ size+=size_of_relative_string( &info->name );
+ size+=size_of_uint32( &info->width );
+ size+=size_of_uint32( &info->length );
+ size+=size_of_uint32( &info->left );
+ size+=size_of_uint32( &info->top );
+ size+=size_of_uint32( &info->right );
+ size+=size_of_uint32( &info->bottom );
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_port_info_1(PORT_INFO_1 *info)
+{
+ int size=0;
+
+ size+=size_of_relative_string( &info->port_name );
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_driverdir_info_1(DRIVER_DIRECTORY_1 *info)
+{
+ int size=0;
+
+ size=str_len_uni(&info->name); /* the string length */
+ size=size+1; /* add the leading zero */
+ size=size*2; /* convert in char */
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printprocessordirectory_info_1(PRINTPROCESSOR_DIRECTORY_1 *info)
+{
+ int size=0;
+
+ size=str_len_uni(&info->name); /* the string length */
+ size=size+1; /* add the leading zero */
+ size=size*2; /* convert in char */
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_port_info_2(PORT_INFO_2 *info)
+{
+ int size=0;
+
+ size+=size_of_relative_string( &info->port_name );
+ size+=size_of_relative_string( &info->monitor_name );
+ size+=size_of_relative_string( &info->description );
+
+ size+=size_of_uint32( &info->port_type );
+ size+=size_of_uint32( &info->reserved );
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printprocessor_info_1(PRINTPROCESSOR_1 *info)
+{
+ int size=0;
+ size+=size_of_relative_string( &info->name );
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printprocdatatype_info_1(PRINTPROCDATATYPE_1 *info)
+{
+ int size=0;
+ size+=size_of_relative_string( &info->name );
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+uint32 spoolss_size_printer_enum_values(PRINTER_ENUM_VALUES *p)
+{
+ uint32 size = 0;
+
+ if (!p)
+ return 0;
+
+ /* uint32(offset) + uint32(length) + length) */
+ size += (size_of_uint32(&p->value_len)*2) + p->value_len;
+ size += (size_of_uint32(&p->data_len)*2) + p->data_len + (p->data_len%2) ;
+
+ size += size_of_uint32(&p->type);
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printmonitor_info_1(PRINTMONITOR_1 *info)
+{
+ int size=0;
+ size+=size_of_relative_string( &info->name );
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printmonitor_info_2(PRINTMONITOR_2 *info)
+{
+ int size=0;
+ size+=size_of_relative_string( &info->name);
+ size+=size_of_relative_string( &info->environment);
+ size+=size_of_relative_string( &info->dll_name);
+
+ return size;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_getprinterdriver2(SPOOL_Q_GETPRINTERDRIVER2 *q_u,
+ const POLICY_HND *hnd,
+ const fstring architecture,
+ uint32 level, uint32 clientmajor, uint32 clientminor,
+ NEW_BUFFER *buffer, uint32 offered)
+{
+ if (q_u == NULL)
+ return False;
+
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+
+ init_buf_unistr2(&q_u->architecture, &q_u->architecture_ptr, architecture);
+
+ q_u->level=level;
+ q_u->clientmajorversion=clientmajor;
+ q_u->clientminorversion=clientminor;
+
+ q_u->buffer=buffer;
+ q_u->offered=offered;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_getprinterdriver2 (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_getprinterdriver2(const char *desc, SPOOL_Q_GETPRINTERDRIVER2 *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_getprinterdriver2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+ if(!prs_uint32("architecture_ptr", ps, depth, &q_u->architecture_ptr))
+ return False;
+ if(!smb_io_unistr2("architecture", &q_u->architecture, q_u->architecture_ptr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if(!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ if(!prs_uint32("clientmajorversion", ps, depth, &q_u->clientmajorversion))
+ return False;
+ if(!prs_uint32("clientminorversion", ps, depth, &q_u->clientminorversion))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_getprinterdriver2 (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_getprinterdriver2(const char *desc, SPOOL_R_GETPRINTERDRIVER2 *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_getprinterdriver2");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+ if (!prs_uint32("servermajorversion", ps, depth, &r_u->servermajorversion))
+ return False;
+ if (!prs_uint32("serverminorversion", ps, depth, &r_u->serverminorversion))
+ return False;
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_enumprinters(
+ SPOOL_Q_ENUMPRINTERS *q_u,
+ uint32 flags,
+ char *servername,
+ uint32 level,
+ NEW_BUFFER *buffer,
+ uint32 offered
+)
+{
+ q_u->flags=flags;
+
+ q_u->servername_ptr = (servername != NULL) ? 1 : 0;
+ init_buf_unistr2(&q_u->servername, &q_u->servername_ptr, servername);
+
+ q_u->level=level;
+ q_u->buffer=buffer;
+ q_u->offered=offered;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_enumports(SPOOL_Q_ENUMPORTS *q_u,
+ fstring servername, uint32 level,
+ NEW_BUFFER *buffer, uint32 offered)
+{
+ q_u->name_ptr = (servername != NULL) ? 1 : 0;
+ init_buf_unistr2(&q_u->name, &q_u->name_ptr, servername);
+
+ q_u->level=level;
+ q_u->buffer=buffer;
+ q_u->offered=offered;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_enumprinters (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_enumprinters(const char *desc, SPOOL_Q_ENUMPRINTERS *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_enumprinters");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("flags", ps, depth, &q_u->flags))
+ return False;
+ if (!prs_uint32("servername_ptr", ps, depth, &q_u->servername_ptr))
+ return False;
+
+ if (!smb_io_unistr2("", &q_u->servername, q_u->servername_ptr, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_R_ENUMPRINTERS structure.
+ ********************************************************************/
+
+BOOL spoolss_io_r_enumprinters(const char *desc, SPOOL_R_ENUMPRINTERS *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_enumprinters");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if (!prs_uint32("returned", ps, depth, &r_u->returned))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_enum_printers (srv_spoolss.c)
+ *
+ ********************************************************************/
+
+BOOL spoolss_io_r_getprinter(const char *desc, SPOOL_R_GETPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_getprinter");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_getprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_getprinter(const char *desc, SPOOL_Q_GETPRINTER *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_getprinter");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+ if (!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_getprinter(
+ TALLOC_CTX *mem_ctx,
+ SPOOL_Q_GETPRINTER *q_u,
+ const POLICY_HND *hnd,
+ uint32 level,
+ NEW_BUFFER *buffer,
+ uint32 offered
+)
+{
+ if (q_u == NULL)
+ {
+ return False;
+ }
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+
+ q_u->level=level;
+ q_u->buffer=buffer;
+ q_u->offered=offered;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+BOOL make_spoolss_q_setprinter(TALLOC_CTX *mem_ctx, SPOOL_Q_SETPRINTER *q_u,
+ const POLICY_HND *hnd, uint32 level, PRINTER_INFO_CTR *info,
+ uint32 command)
+{
+ SEC_DESC *secdesc;
+ DEVICEMODE *devmode;
+
+ if (q_u == NULL)
+ return False;
+
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+
+ q_u->level = level;
+ q_u->info.level = level;
+ q_u->info.info_ptr = (info != NULL) ? 1 : 0;
+ switch (level) {
+
+ /* There's no such thing as a setprinter level 1 */
+
+ case 2:
+ secdesc = info->printers_2->secdesc;
+ devmode = info->printers_2->devmode;
+
+ make_spoolss_printer_info_2 (mem_ctx, &q_u->info.info_2, info->printers_2);
+#if 1 /* JERRY TEST */
+ q_u->secdesc_ctr = (SEC_DESC_BUF*)malloc(sizeof(SEC_DESC_BUF));
+ if (!q_u->secdesc_ctr)
+ return False;
+ q_u->secdesc_ctr->ptr = (secdesc != NULL) ? 1: 0;
+ q_u->secdesc_ctr->max_len = (secdesc) ? sizeof(SEC_DESC) + (2*sizeof(uint32)) : 0;
+ q_u->secdesc_ctr->len = (secdesc) ? sizeof(SEC_DESC) + (2*sizeof(uint32)) : 0;
+ q_u->secdesc_ctr->sec = secdesc;
+
+ q_u->devmode_ctr.devmode_ptr = (devmode != NULL) ? 1 : 0;
+ q_u->devmode_ctr.size = (devmode != NULL) ? sizeof(DEVICEMODE) + (3*sizeof(uint32)) : 0;
+ q_u->devmode_ctr.devmode = devmode;
+#else
+ q_u->secdesc_ctr = NULL;
+
+ q_u->devmode_ctr.devmode_ptr = 0;
+ q_u->devmode_ctr.size = 0;
+ q_u->devmode_ctr.devmode = NULL;
+#endif
+ break;
+ default:
+ DEBUG(0,("make_spoolss_q_setprinter: Unknown info level [%d]\n", level));
+ break;
+ }
+
+
+ q_u->command = command;
+
+ return True;
+}
+
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_setprinter(const char *desc, SPOOL_R_SETPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_setprinter");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Marshall/unmarshall a SPOOL_Q_SETPRINTER struct.
+********************************************************************/
+
+BOOL spoolss_io_q_setprinter(const char *desc, SPOOL_Q_SETPRINTER *q_u, prs_struct *ps, int depth)
+{
+ uint32 ptr_sec_desc = 0;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_setprinter");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle", &q_u->handle ,ps, depth))
+ return False;
+ if(!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if(!spool_io_printer_info_level("", &q_u->info, ps, depth))
+ return False;
+
+ if (!spoolss_io_devmode_cont(desc, &q_u->devmode_ctr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ switch (q_u->level)
+ {
+ case 2:
+ {
+ ptr_sec_desc = q_u->info.info_2->secdesc_ptr;
+ break;
+ }
+ case 3:
+ {
+ ptr_sec_desc = q_u->info.info_3->secdesc_ptr;
+ break;
+ }
+ }
+ if (ptr_sec_desc)
+ {
+ if (!sec_io_desc_buf(desc, &q_u->secdesc_ctr, ps, depth))
+ return False;
+ } else {
+ uint32 dummy = 0;
+
+ /* Parse a NULL security descriptor. This should really
+ happen inside the sec_io_desc_buf() function. */
+
+ prs_debug(ps, depth, "", "sec_io_desc_buf");
+ if (!prs_uint32("size", ps, depth + 1, &dummy))
+ return False;
+ if (!prs_uint32("ptr", ps, depth + 1, &dummy)) return
+ False;
+ }
+
+ if(!prs_uint32("command", ps, depth, &q_u->command))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_fcpn(const char *desc, SPOOL_R_FCPN *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_fcpn");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_fcpn(const char *desc, SPOOL_Q_FCPN *q_u, prs_struct *ps, int depth)
+{
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_fcpn");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_addjob(const char *desc, SPOOL_R_ADDJOB *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_addjob(const char *desc, SPOOL_Q_ADDJOB *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+ if(!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if(!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_enumjobs(const char *desc, SPOOL_R_ENUMJOBS *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_enumjobs");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if (!prs_uint32("returned", ps, depth, &r_u->returned))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL make_spoolss_q_enumjobs(SPOOL_Q_ENUMJOBS *q_u, const POLICY_HND *hnd,
+ uint32 firstjob,
+ uint32 numofjobs,
+ uint32 level,
+ NEW_BUFFER *buffer,
+ uint32 offered)
+{
+ if (q_u == NULL)
+ {
+ return False;
+ }
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+ q_u->firstjob = firstjob;
+ q_u->numofjobs = numofjobs;
+ q_u->level = level;
+ q_u->buffer= buffer;
+ q_u->offered = offered;
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_enumjobs(const char *desc, SPOOL_Q_ENUMJOBS *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_enumjobs");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!smb_io_pol_hnd("printer handle",&q_u->handle, ps, depth))
+ return False;
+
+ if (!prs_uint32("firstjob", ps, depth, &q_u->firstjob))
+ return False;
+ if (!prs_uint32("numofjobs", ps, depth, &q_u->numofjobs))
+ return False;
+ if (!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_schedulejob(const char *desc, SPOOL_R_SCHEDULEJOB *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_schedulejob");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_schedulejob(const char *desc, SPOOL_Q_SCHEDULEJOB *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_schedulejob");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+ if(!prs_uint32("jobid", ps, depth, &q_u->jobid))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_setjob(const char *desc, SPOOL_R_SETJOB *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_setjob");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_setjob(const char *desc, SPOOL_Q_SETJOB *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_setjob");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+ if(!prs_uint32("jobid", ps, depth, &q_u->jobid))
+ return False;
+ /*
+ * level is usually 0. If (level!=0) then I'm in trouble !
+ * I will try to generate setjob command with level!=0, one day.
+ */
+ if(!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+ if(!prs_uint32("command", ps, depth, &q_u->command))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_R_ENUMPRINTERDRIVERS structure.
+********************************************************************/
+
+BOOL spoolss_io_r_enumprinterdrivers(const char *desc, SPOOL_R_ENUMPRINTERDRIVERS *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_enumprinterdrivers");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if (!prs_uint32("returned", ps, depth, &r_u->returned))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_enumprinterdrivers(SPOOL_Q_ENUMPRINTERDRIVERS *q_u,
+ const char *name,
+ const char *environment,
+ uint32 level,
+ NEW_BUFFER *buffer, uint32 offered)
+{
+ init_buf_unistr2(&q_u->name, &q_u->name_ptr, name);
+ init_buf_unistr2(&q_u->environment, &q_u->environment_ptr, environment);
+
+ q_u->level=level;
+ q_u->buffer=buffer;
+ q_u->offered=offered;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_Q_ENUMPRINTERDRIVERS structure.
+********************************************************************/
+
+BOOL spoolss_io_q_enumprinterdrivers(const char *desc, SPOOL_Q_ENUMPRINTERDRIVERS *q_u, prs_struct *ps, int depth)
+{
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_enumprinterdrivers");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("name_ptr", ps, depth, &q_u->name_ptr))
+ return False;
+ if (!smb_io_unistr2("", &q_u->name, q_u->name_ptr,ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("environment_ptr", ps, depth, &q_u->environment_ptr))
+ return False;
+ if (!smb_io_unistr2("", &q_u->environment, q_u->environment_ptr, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_enumforms(const char *desc, SPOOL_Q_ENUMFORMS *q_u, prs_struct *ps, int depth)
+{
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_enumforms");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+ if (!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_enumforms(const char *desc, SPOOL_R_ENUMFORMS *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_enumforms");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("size of buffer needed", ps, depth, &r_u->needed))
+ return False;
+
+ if (!prs_uint32("numofforms", ps, depth, &r_u->numofforms))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_getform(const char *desc, SPOOL_Q_GETFORM *q_u, prs_struct *ps, int depth)
+{
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_getform");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+ if (!smb_io_unistr2("", &q_u->formname,True,ps,depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_getform(const char *desc, SPOOL_R_GETFORM *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_getform");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("size of buffer needed", ps, depth, &r_u->needed))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_R_ENUMPORTS structure.
+********************************************************************/
+
+BOOL spoolss_io_r_enumports(const char *desc, SPOOL_R_ENUMPORTS *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_enumports");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if (!prs_uint32("returned", ps, depth, &r_u->returned))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_enumports(const char *desc, SPOOL_Q_ENUMPORTS *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("", ps, depth, &q_u->name_ptr))
+ return False;
+ if (!smb_io_unistr2("", &q_u->name,True,ps,depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_PRINTER_INFO_LEVEL_1 structure.
+********************************************************************/
+
+BOOL spool_io_printer_info_level_1(const char *desc, SPOOL_PRINTER_INFO_LEVEL_1 *il, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spool_io_printer_info_level_1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("flags", ps, depth, &il->flags))
+ return False;
+ if(!prs_uint32("description_ptr", ps, depth, &il->description_ptr))
+ return False;
+ if(!prs_uint32("name_ptr", ps, depth, &il->name_ptr))
+ return False;
+ if(!prs_uint32("comment_ptr", ps, depth, &il->comment_ptr))
+ return False;
+
+ if(!smb_io_unistr2("description", &il->description, il->description_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("name", &il->name, il->name_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("comment", &il->comment, il->comment_ptr, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_PRINTER_INFO_LEVEL_3 structure.
+********************************************************************/
+
+BOOL spool_io_printer_info_level_3(const char *desc, SPOOL_PRINTER_INFO_LEVEL_3 *il, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spool_io_printer_info_level_3");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("secdesc_ptr", ps, depth, &il->secdesc_ptr))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_PRINTER_INFO_LEVEL_2 structure.
+********************************************************************/
+
+BOOL spool_io_printer_info_level_2(const char *desc, SPOOL_PRINTER_INFO_LEVEL_2 *il, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spool_io_printer_info_level_2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("servername_ptr", ps, depth, &il->servername_ptr))
+ return False;
+ if(!prs_uint32("printername_ptr", ps, depth, &il->printername_ptr))
+ return False;
+ if(!prs_uint32("sharename_ptr", ps, depth, &il->sharename_ptr))
+ return False;
+ if(!prs_uint32("portname_ptr", ps, depth, &il->portname_ptr))
+ return False;
+
+ if(!prs_uint32("drivername_ptr", ps, depth, &il->drivername_ptr))
+ return False;
+ if(!prs_uint32("comment_ptr", ps, depth, &il->comment_ptr))
+ return False;
+ if(!prs_uint32("location_ptr", ps, depth, &il->location_ptr))
+ return False;
+ if(!prs_uint32("devmode_ptr", ps, depth, &il->devmode_ptr))
+ return False;
+ if(!prs_uint32("sepfile_ptr", ps, depth, &il->sepfile_ptr))
+ return False;
+ if(!prs_uint32("printprocessor_ptr", ps, depth, &il->printprocessor_ptr))
+ return False;
+ if(!prs_uint32("datatype_ptr", ps, depth, &il->datatype_ptr))
+ return False;
+ if(!prs_uint32("parameters_ptr", ps, depth, &il->parameters_ptr))
+ return False;
+ if(!prs_uint32("secdesc_ptr", ps, depth, &il->secdesc_ptr))
+ return False;
+
+ if(!prs_uint32("attributes", ps, depth, &il->attributes))
+ return False;
+ if(!prs_uint32("priority", ps, depth, &il->priority))
+ return False;
+ if(!prs_uint32("default_priority", ps, depth, &il->default_priority))
+ return False;
+ if(!prs_uint32("starttime", ps, depth, &il->starttime))
+ return False;
+ if(!prs_uint32("untiltime", ps, depth, &il->untiltime))
+ return False;
+ if(!prs_uint32("status", ps, depth, &il->status))
+ return False;
+ if(!prs_uint32("cjobs", ps, depth, &il->cjobs))
+ return False;
+ if(!prs_uint32("averageppm", ps, depth, &il->averageppm))
+ return False;
+
+ if(!smb_io_unistr2("servername", &il->servername, il->servername_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("printername", &il->printername, il->printername_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("sharename", &il->sharename, il->sharename_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("portname", &il->portname, il->portname_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("drivername", &il->drivername, il->drivername_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("comment", &il->comment, il->comment_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("location", &il->location, il->location_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("sepfile", &il->sepfile, il->sepfile_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("printprocessor", &il->printprocessor, il->printprocessor_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("datatype", &il->datatype, il->datatype_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("parameters", &il->parameters, il->parameters_ptr, ps, depth))
+ return False;
+
+ return True;
+}
+
+BOOL spool_io_printer_info_level_7(const char *desc, SPOOL_PRINTER_INFO_LEVEL_7 *il, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spool_io_printer_info_level_7");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("guid_ptr", ps, depth, &il->guid_ptr))
+ return False;
+ if(!prs_uint32("action", ps, depth, &il->action))
+ return False;
+
+ if(!smb_io_unistr2("servername", &il->guid, il->guid_ptr, ps, depth))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spool_io_printer_info_level(const char *desc, SPOOL_PRINTER_INFO_LEVEL *il, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spool_io_printer_info_level");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("level", ps, depth, &il->level))
+ return False;
+ if(!prs_uint32("info_ptr", ps, depth, &il->info_ptr))
+ return False;
+
+ /* if no struct inside just return */
+ if (il->info_ptr==0) {
+ if (UNMARSHALLING(ps)) {
+ il->info_1=NULL;
+ il->info_2=NULL;
+ }
+ return True;
+ }
+
+ switch (il->level) {
+ /*
+ * level 0 is used by setprinter when managing the queue
+ * (hold, stop, start a queue)
+ */
+ case 0:
+ break;
+ /* DOCUMENT ME!!! What is level 1 used for? */
+ case 1:
+ {
+ if (UNMARSHALLING(ps)) {
+ if ((il->info_1=(SPOOL_PRINTER_INFO_LEVEL_1 *)prs_alloc_mem(ps,sizeof(SPOOL_PRINTER_INFO_LEVEL_1))) == NULL)
+ return False;
+ }
+ if (!spool_io_printer_info_level_1("", il->info_1, ps, depth))
+ return False;
+ break;
+ }
+ /*
+ * level 2 is used by addprinter
+ * and by setprinter when updating printer's info
+ */
+ case 2:
+ if (UNMARSHALLING(ps)) {
+ if ((il->info_2=(SPOOL_PRINTER_INFO_LEVEL_2 *)prs_alloc_mem(ps,sizeof(SPOOL_PRINTER_INFO_LEVEL_2))) == NULL)
+ return False;
+ }
+ if (!spool_io_printer_info_level_2("", il->info_2, ps, depth))
+ return False;
+ break;
+ /* DOCUMENT ME!!! What is level 3 used for? */
+ case 3:
+ {
+ if (UNMARSHALLING(ps)) {
+ if ((il->info_3=(SPOOL_PRINTER_INFO_LEVEL_3 *)prs_alloc_mem(ps,sizeof(SPOOL_PRINTER_INFO_LEVEL_3))) == NULL)
+ return False;
+ }
+ if (!spool_io_printer_info_level_3("", il->info_3, ps, depth))
+ return False;
+ break;
+ }
+ case 7:
+ if (UNMARSHALLING(ps))
+ if ((il->info_7=(SPOOL_PRINTER_INFO_LEVEL_7 *)prs_alloc_mem(ps,sizeof(SPOOL_PRINTER_INFO_LEVEL_7))) == NULL)
+ return False;
+ if (!spool_io_printer_info_level_7("", il->info_7, ps, depth))
+ return False;
+ break;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_addprinterex(const char *desc, SPOOL_Q_ADDPRINTEREX *q_u, prs_struct *ps, int depth)
+{
+ uint32 ptr_sec_desc = 0;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_addprinterex");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("", ps, depth, &q_u->server_name_ptr))
+ return False;
+ if(!smb_io_unistr2("", &q_u->server_name, q_u->server_name_ptr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("info_level", ps, depth, &q_u->level))
+ return False;
+
+ if(!spool_io_printer_info_level("", &q_u->info, ps, depth))
+ return False;
+
+ if (!spoolss_io_devmode_cont(desc, &q_u->devmode_ctr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ switch (q_u->level) {
+ case 2:
+ ptr_sec_desc = q_u->info.info_2->secdesc_ptr;
+ break;
+ case 3:
+ ptr_sec_desc = q_u->info.info_3->secdesc_ptr;
+ break;
+ }
+ if (ptr_sec_desc) {
+ if (!sec_io_desc_buf(desc, &q_u->secdesc_ctr, ps, depth))
+ return False;
+ } else {
+ uint32 dummy;
+
+ /* Parse a NULL security descriptor. This should really
+ happen inside the sec_io_desc_buf() function. */
+
+ prs_debug(ps, depth, "", "sec_io_desc_buf");
+ if (!prs_uint32("size", ps, depth + 1, &dummy))
+ return False;
+ if (!prs_uint32("ptr", ps, depth + 1, &dummy))
+ return False;
+ }
+
+ if(!prs_uint32("user_switch", ps, depth, &q_u->user_switch))
+ return False;
+ if(!spool_io_user_level("", &q_u->user_ctr, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_addprinterex(const char *desc, SPOOL_R_ADDPRINTEREX *r_u,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_addprinterex");
+ depth++;
+
+ if(!smb_io_pol_hnd("printer handle",&r_u->handle,ps,depth))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spool_io_printer_driver_info_level_3(const char *desc, SPOOL_PRINTER_DRIVER_INFO_LEVEL_3 **q_u,
+ prs_struct *ps, int depth)
+{
+ SPOOL_PRINTER_DRIVER_INFO_LEVEL_3 *il;
+
+ prs_debug(ps, depth, desc, "spool_io_printer_driver_info_level_3");
+ depth++;
+
+ /* reading */
+ if (UNMARSHALLING(ps)) {
+ il=(SPOOL_PRINTER_DRIVER_INFO_LEVEL_3 *)prs_alloc_mem(ps,sizeof(SPOOL_PRINTER_DRIVER_INFO_LEVEL_3));
+ if(il == NULL)
+ return False;
+ *q_u=il;
+ }
+ else {
+ il=*q_u;
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("cversion", ps, depth, &il->cversion))
+ return False;
+ if(!prs_uint32("name", ps, depth, &il->name_ptr))
+ return False;
+ if(!prs_uint32("environment", ps, depth, &il->environment_ptr))
+ return False;
+ if(!prs_uint32("driverpath", ps, depth, &il->driverpath_ptr))
+ return False;
+ if(!prs_uint32("datafile", ps, depth, &il->datafile_ptr))
+ return False;
+ if(!prs_uint32("configfile", ps, depth, &il->configfile_ptr))
+ return False;
+ if(!prs_uint32("helpfile", ps, depth, &il->helpfile_ptr))
+ return False;
+ if(!prs_uint32("monitorname", ps, depth, &il->monitorname_ptr))
+ return False;
+ if(!prs_uint32("defaultdatatype", ps, depth, &il->defaultdatatype_ptr))
+ return False;
+ if(!prs_uint32("dependentfilessize", ps, depth, &il->dependentfilessize))
+ return False;
+ if(!prs_uint32("dependentfiles", ps, depth, &il->dependentfiles_ptr))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("name", &il->name, il->name_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("environment", &il->environment, il->environment_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("driverpath", &il->driverpath, il->driverpath_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("datafile", &il->datafile, il->datafile_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("configfile", &il->configfile, il->configfile_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("helpfile", &il->helpfile, il->helpfile_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("monitorname", &il->monitorname, il->monitorname_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("defaultdatatype", &il->defaultdatatype, il->defaultdatatype_ptr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (il->dependentfiles_ptr)
+ smb_io_buffer5("", &il->dependentfiles, ps, depth);
+
+ return True;
+}
+
+/*******************************************************************
+parse a SPOOL_PRINTER_DRIVER_INFO_LEVEL_6 structure
+********************************************************************/
+
+BOOL spool_io_printer_driver_info_level_6(const char *desc, SPOOL_PRINTER_DRIVER_INFO_LEVEL_6 **q_u,
+ prs_struct *ps, int depth)
+{
+ SPOOL_PRINTER_DRIVER_INFO_LEVEL_6 *il;
+
+ prs_debug(ps, depth, desc, "spool_io_printer_driver_info_level_6");
+ depth++;
+
+ /* reading */
+ if (UNMARSHALLING(ps)) {
+ il=(SPOOL_PRINTER_DRIVER_INFO_LEVEL_6 *)prs_alloc_mem(ps,sizeof(SPOOL_PRINTER_DRIVER_INFO_LEVEL_6));
+ if(il == NULL)
+ return False;
+ *q_u=il;
+ }
+ else {
+ il=*q_u;
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ /*
+ * I know this seems weird, but I have no other explanation.
+ * This is observed behavior on both NT4 and 2K servers.
+ * --jerry
+ */
+
+ if (!prs_align_uint64(ps))
+ return False;
+
+ /* parse the main elements the packet */
+
+ if(!prs_uint32("cversion ", ps, depth, &il->version))
+ return False;
+ if(!prs_uint32("name ", ps, depth, &il->name_ptr))
+ return False;
+ if(!prs_uint32("environment ", ps, depth, &il->environment_ptr))
+ return False;
+ if(!prs_uint32("driverpath ", ps, depth, &il->driverpath_ptr))
+ return False;
+ if(!prs_uint32("datafile ", ps, depth, &il->datafile_ptr))
+ return False;
+ if(!prs_uint32("configfile ", ps, depth, &il->configfile_ptr))
+ return False;
+ if(!prs_uint32("helpfile ", ps, depth, &il->helpfile_ptr))
+ return False;
+ if(!prs_uint32("monitorname ", ps, depth, &il->monitorname_ptr))
+ return False;
+ if(!prs_uint32("defaultdatatype", ps, depth, &il->defaultdatatype_ptr))
+ return False;
+ if(!prs_uint32("dependentfiles ", ps, depth, &il->dependentfiles_len))
+ return False;
+ if(!prs_uint32("dependentfiles ", ps, depth, &il->dependentfiles_ptr))
+ return False;
+ if(!prs_uint32("previousnames ", ps, depth, &il->previousnames_len))
+ return False;
+ if(!prs_uint32("previousnames ", ps, depth, &il->previousnames_ptr))
+ return False;
+ if(!smb_io_time("driverdate ", &il->driverdate, ps, depth))
+ return False;
+ if(!prs_uint32("dummy4 ", ps, depth, &il->dummy4))
+ return False;
+ if(!prs_uint64("driverversion ", ps, depth, &il->driverversion))
+ return False;
+ if(!prs_uint32("mfgname ", ps, depth, &il->mfgname_ptr))
+ return False;
+ if(!prs_uint32("oemurl ", ps, depth, &il->oemurl_ptr))
+ return False;
+ if(!prs_uint32("hardwareid ", ps, depth, &il->hardwareid_ptr))
+ return False;
+ if(!prs_uint32("provider ", ps, depth, &il->provider_ptr))
+ return False;
+
+ /* parse the structures in the packet */
+
+ if(!smb_io_unistr2("name", &il->name, il->name_ptr, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("environment", &il->environment, il->environment_ptr, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("driverpath", &il->driverpath, il->driverpath_ptr, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("datafile", &il->datafile, il->datafile_ptr, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("configfile", &il->configfile, il->configfile_ptr, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("helpfile", &il->helpfile, il->helpfile_ptr, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("monitorname", &il->monitorname, il->monitorname_ptr, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("defaultdatatype", &il->defaultdatatype, il->defaultdatatype_ptr, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ if (il->dependentfiles_ptr) {
+ if(!smb_io_buffer5("dependentfiles", &il->dependentfiles, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ }
+ if (il->previousnames_ptr) {
+ if(!smb_io_buffer5("previousnames", &il->previousnames, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ }
+ if(!smb_io_unistr2("mfgname", &il->mfgname, il->mfgname_ptr, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unistr2("oemurl", &il->oemurl, il->oemurl_ptr, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unistr2("hardwareid", &il->hardwareid, il->hardwareid_ptr, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unistr2("provider", &il->provider, il->provider_ptr, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ convert a buffer of UNICODE strings null terminated
+ the buffer is terminated by a NULL
+
+ convert to an dos codepage array (null terminated)
+
+ dynamically allocate memory
+
+********************************************************************/
+static BOOL uniarray_2_dosarray(BUFFER5 *buf5, fstring **ar)
+{
+ fstring f, *tar;
+ int n = 0;
+ char *src;
+
+ if (buf5==NULL)
+ return False;
+
+ src = (char *)buf5->buffer;
+ *ar = NULL;
+
+ while (src < ((char *)buf5->buffer) + buf5->buf_len*2) {
+ rpcstr_pull(f, src, sizeof(f)-1, -1, STR_TERMINATE);
+ src = skip_unibuf(src, 2*buf5->buf_len - PTR_DIFF(src,buf5->buffer));
+ tar = (fstring *)Realloc(*ar, sizeof(fstring)*(n+2));
+ if (!tar)
+ return False;
+ else
+ *ar = tar;
+ fstrcpy((*ar)[n], f);
+ n++;
+ }
+ fstrcpy((*ar)[n], "");
+
+ return True;
+}
+
+
+
+
+/*******************************************************************
+ read a UNICODE array with null terminated strings
+ and null terminated array
+ and size of array at beginning
+********************************************************************/
+
+BOOL smb_io_unibuffer(const char *desc, UNISTR2 *buffer, prs_struct *ps, int depth)
+{
+ if (buffer==NULL) return False;
+
+ buffer->offset=0;
+ buffer->uni_str_len=buffer->uni_max_len;
+
+ if(!prs_uint32("buffer_size", ps, depth, &buffer->uni_max_len))
+ return False;
+
+ if(!prs_unistr2(True, "buffer ", ps, depth, buffer))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spool_io_printer_driver_info_level(const char *desc, SPOOL_PRINTER_DRIVER_INFO_LEVEL *il, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spool_io_printer_driver_info_level");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("level", ps, depth, &il->level))
+ return False;
+ if(!prs_uint32("ptr", ps, depth, &il->ptr))
+ return False;
+
+ if (il->ptr==0)
+ return True;
+
+ switch (il->level) {
+ case 3:
+ if(!spool_io_printer_driver_info_level_3("", &il->info_3, ps, depth))
+ return False;
+ break;
+ case 6:
+ if(!spool_io_printer_driver_info_level_6("", &il->info_6, ps, depth))
+ return False;
+ break;
+ default:
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ init a SPOOL_Q_ADDPRINTERDRIVER struct
+ ******************************************************************/
+
+BOOL make_spoolss_q_addprinterdriver(TALLOC_CTX *mem_ctx,
+ SPOOL_Q_ADDPRINTERDRIVER *q_u, const char* srv_name,
+ uint32 level, PRINTER_DRIVER_CTR *info)
+{
+ DEBUG(5,("make_spoolss_q_addprinterdriver\n"));
+
+ q_u->server_name_ptr = (srv_name!=NULL)?1:0;
+ init_unistr2(&q_u->server_name, srv_name, UNI_STR_TERMINATE);
+
+ q_u->level = level;
+
+ q_u->info.level = level;
+ q_u->info.ptr = (info!=NULL)?1:0;
+ switch (level)
+ {
+ /* info level 3 is supported by Windows 95/98, WinNT and Win2k */
+ case 3 :
+ make_spoolss_driver_info_3(mem_ctx, &q_u->info.info_3, info->info3);
+ break;
+
+ default:
+ DEBUG(0,("make_spoolss_q_addprinterdriver: Unknown info level [%d]\n", level));
+ break;
+ }
+
+ return True;
+}
+
+BOOL make_spoolss_driver_info_3(TALLOC_CTX *mem_ctx,
+ SPOOL_PRINTER_DRIVER_INFO_LEVEL_3 **spool_drv_info,
+ DRIVER_INFO_3 *info3)
+{
+ uint32 len = 0;
+ uint16 *ptr = info3->dependentfiles;
+ BOOL done = False;
+ BOOL null_char = False;
+ SPOOL_PRINTER_DRIVER_INFO_LEVEL_3 *inf;
+
+ if (!(inf=(SPOOL_PRINTER_DRIVER_INFO_LEVEL_3*)talloc_zero(mem_ctx, sizeof(SPOOL_PRINTER_DRIVER_INFO_LEVEL_3))))
+ return False;
+
+ inf->cversion = info3->version;
+ inf->name_ptr = (info3->name.buffer!=NULL)?1:0;
+ inf->environment_ptr = (info3->architecture.buffer!=NULL)?1:0;
+ inf->driverpath_ptr = (info3->driverpath.buffer!=NULL)?1:0;
+ inf->datafile_ptr = (info3->datafile.buffer!=NULL)?1:0;
+ inf->configfile_ptr = (info3->configfile.buffer!=NULL)?1:0;
+ inf->helpfile_ptr = (info3->helpfile.buffer!=NULL)?1:0;
+ inf->monitorname_ptr = (info3->monitorname.buffer!=NULL)?1:0;
+ inf->defaultdatatype_ptr = (info3->defaultdatatype.buffer!=NULL)?1:0;
+
+ init_unistr2_from_unistr(&inf->name, &info3->name);
+ init_unistr2_from_unistr(&inf->environment, &info3->architecture);
+ init_unistr2_from_unistr(&inf->driverpath, &info3->driverpath);
+ init_unistr2_from_unistr(&inf->datafile, &info3->datafile);
+ init_unistr2_from_unistr(&inf->configfile, &info3->configfile);
+ init_unistr2_from_unistr(&inf->helpfile, &info3->helpfile);
+ init_unistr2_from_unistr(&inf->monitorname, &info3->monitorname);
+ init_unistr2_from_unistr(&inf->defaultdatatype, &info3->defaultdatatype);
+
+ while (!done)
+ {
+ switch (*ptr)
+ {
+ case 0:
+ /* the null_char BOOL is used to help locate
+ two '\0's back to back */
+ if (null_char)
+ done = True;
+ else
+ null_char = True;
+ break;
+
+ default:
+ null_char = False;
+ ;;
+ break;
+ }
+ len++;
+ ptr++;
+ }
+ inf->dependentfiles_ptr = (info3->dependentfiles != NULL) ? 1 : 0;
+ inf->dependentfilessize = len;
+ if(!make_spoolss_buffer5(mem_ctx, &inf->dependentfiles, len, info3->dependentfiles))
+ {
+ SAFE_FREE(inf);
+ return False;
+ }
+
+ *spool_drv_info = inf;
+
+ return True;
+}
+
+/*******************************************************************
+ make a BUFFER5 struct from a uint16*
+ ******************************************************************/
+BOOL make_spoolss_buffer5(TALLOC_CTX *mem_ctx, BUFFER5 *buf5, uint32 len, uint16 *src)
+{
+
+ buf5->buf_len = len;
+ if((buf5->buffer=(uint16*)talloc_memdup(mem_ctx, src, sizeof(uint16)*len)) == NULL)
+ {
+ DEBUG(0,("make_spoolss_buffer5: Unable to malloc memory for buffer!\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ fill in the prs_struct for a ADDPRINTERDRIVER request PDU
+ ********************************************************************/
+
+BOOL spoolss_io_q_addprinterdriver(const char *desc, SPOOL_Q_ADDPRINTERDRIVER *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_addprinterdriver");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("server_name_ptr", ps, depth, &q_u->server_name_ptr))
+ return False;
+ if(!smb_io_unistr2("server_name", &q_u->server_name, q_u->server_name_ptr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("info_level", ps, depth, &q_u->level))
+ return False;
+
+ if(!spool_io_printer_driver_info_level("", &q_u->info, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_addprinterdriver(const char *desc, SPOOL_R_ADDPRINTERDRIVER *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_addprinterdriver");
+ depth++;
+
+ if(!prs_werror("status", ps, depth, &q_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ fill in the prs_struct for a ADDPRINTERDRIVER request PDU
+ ********************************************************************/
+
+BOOL spoolss_io_q_addprinterdriverex(const char *desc, SPOOL_Q_ADDPRINTERDRIVEREX *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_addprinterdriverex");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("server_name_ptr", ps, depth, &q_u->server_name_ptr))
+ return False;
+ if(!smb_io_unistr2("server_name", &q_u->server_name, q_u->server_name_ptr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("info_level", ps, depth, &q_u->level))
+ return False;
+
+ if(!spool_io_printer_driver_info_level("", &q_u->info, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("copy flags", ps, depth, &q_u->copy_flags))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_addprinterdriverex(const char *desc, SPOOL_R_ADDPRINTERDRIVEREX *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_addprinterdriverex");
+ depth++;
+
+ if(!prs_werror("status", ps, depth, &q_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL uni_2_asc_printer_driver_3(SPOOL_PRINTER_DRIVER_INFO_LEVEL_3 *uni,
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 **asc)
+{
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 *d;
+
+ DEBUG(7,("uni_2_asc_printer_driver_3: Converting from UNICODE to ASCII\n"));
+
+ if (*asc==NULL)
+ {
+ *asc=(NT_PRINTER_DRIVER_INFO_LEVEL_3 *)malloc(sizeof(NT_PRINTER_DRIVER_INFO_LEVEL_3));
+ if(*asc == NULL)
+ return False;
+ ZERO_STRUCTP(*asc);
+ }
+
+ d=*asc;
+
+ d->cversion=uni->cversion;
+
+ unistr2_to_ascii(d->name, &uni->name, sizeof(d->name)-1);
+ unistr2_to_ascii(d->environment, &uni->environment, sizeof(d->environment)-1);
+ unistr2_to_ascii(d->driverpath, &uni->driverpath, sizeof(d->driverpath)-1);
+ unistr2_to_ascii(d->datafile, &uni->datafile, sizeof(d->datafile)-1);
+ unistr2_to_ascii(d->configfile, &uni->configfile, sizeof(d->configfile)-1);
+ unistr2_to_ascii(d->helpfile, &uni->helpfile, sizeof(d->helpfile)-1);
+ unistr2_to_ascii(d->monitorname, &uni->monitorname, sizeof(d->monitorname)-1);
+ unistr2_to_ascii(d->defaultdatatype, &uni->defaultdatatype, sizeof(d->defaultdatatype)-1);
+
+ DEBUGADD(8,( "version: %d\n", d->cversion));
+ DEBUGADD(8,( "name: %s\n", d->name));
+ DEBUGADD(8,( "environment: %s\n", d->environment));
+ DEBUGADD(8,( "driverpath: %s\n", d->driverpath));
+ DEBUGADD(8,( "datafile: %s\n", d->datafile));
+ DEBUGADD(8,( "configfile: %s\n", d->configfile));
+ DEBUGADD(8,( "helpfile: %s\n", d->helpfile));
+ DEBUGADD(8,( "monitorname: %s\n", d->monitorname));
+ DEBUGADD(8,( "defaultdatatype: %s\n", d->defaultdatatype));
+
+ if (uniarray_2_dosarray(&uni->dependentfiles, &d->dependentfiles ))
+ return True;
+
+ SAFE_FREE(*asc);
+ return False;
+}
+
+/*******************************************************************
+********************************************************************/
+BOOL uni_2_asc_printer_driver_6(SPOOL_PRINTER_DRIVER_INFO_LEVEL_6 *uni,
+ NT_PRINTER_DRIVER_INFO_LEVEL_6 **asc)
+{
+ NT_PRINTER_DRIVER_INFO_LEVEL_6 *d;
+
+ DEBUG(7,("uni_2_asc_printer_driver_6: Converting from UNICODE to ASCII\n"));
+
+ if (*asc==NULL)
+ {
+ *asc=(NT_PRINTER_DRIVER_INFO_LEVEL_6 *)malloc(sizeof(NT_PRINTER_DRIVER_INFO_LEVEL_6));
+ if(*asc == NULL)
+ return False;
+ ZERO_STRUCTP(*asc);
+ }
+
+ d=*asc;
+
+ d->version=uni->version;
+
+ unistr2_to_ascii(d->name, &uni->name, sizeof(d->name)-1);
+ unistr2_to_ascii(d->environment, &uni->environment, sizeof(d->environment)-1);
+ unistr2_to_ascii(d->driverpath, &uni->driverpath, sizeof(d->driverpath)-1);
+ unistr2_to_ascii(d->datafile, &uni->datafile, sizeof(d->datafile)-1);
+ unistr2_to_ascii(d->configfile, &uni->configfile, sizeof(d->configfile)-1);
+ unistr2_to_ascii(d->helpfile, &uni->helpfile, sizeof(d->helpfile)-1);
+ unistr2_to_ascii(d->monitorname, &uni->monitorname, sizeof(d->monitorname)-1);
+ unistr2_to_ascii(d->defaultdatatype, &uni->defaultdatatype, sizeof(d->defaultdatatype)-1);
+
+ DEBUGADD(8,( "version: %d\n", d->version));
+ DEBUGADD(8,( "name: %s\n", d->name));
+ DEBUGADD(8,( "environment: %s\n", d->environment));
+ DEBUGADD(8,( "driverpath: %s\n", d->driverpath));
+ DEBUGADD(8,( "datafile: %s\n", d->datafile));
+ DEBUGADD(8,( "configfile: %s\n", d->configfile));
+ DEBUGADD(8,( "helpfile: %s\n", d->helpfile));
+ DEBUGADD(8,( "monitorname: %s\n", d->monitorname));
+ DEBUGADD(8,( "defaultdatatype: %s\n", d->defaultdatatype));
+
+ if (!uniarray_2_dosarray(&uni->dependentfiles, &d->dependentfiles ))
+ goto error;
+ if (!uniarray_2_dosarray(&uni->previousnames, &d->previousnames ))
+ goto error;
+
+ return True;
+
+error:
+ SAFE_FREE(*asc);
+ return False;
+}
+
+BOOL uni_2_asc_printer_info_2(const SPOOL_PRINTER_INFO_LEVEL_2 *uni,
+ NT_PRINTER_INFO_LEVEL_2 **asc)
+{
+ NT_PRINTER_INFO_LEVEL_2 *d;
+ time_t time_unix;
+
+ DEBUG(7,("Converting from UNICODE to ASCII\n"));
+ time_unix=time(NULL);
+
+ if (*asc==NULL) {
+ DEBUGADD(8,("allocating memory\n"));
+
+ *asc=(NT_PRINTER_INFO_LEVEL_2 *)malloc(sizeof(NT_PRINTER_INFO_LEVEL_2));
+ if(*asc == NULL)
+ return False;
+ ZERO_STRUCTP(*asc);
+
+ /* we allocate memory iff called from
+ * addprinter(ex) so we can do one time stuff here.
+ */
+ (*asc)->setuptime=time_unix;
+
+ }
+ DEBUGADD(8,("start converting\n"));
+
+ d=*asc;
+
+ d->attributes=uni->attributes;
+ d->priority=uni->priority;
+ d->default_priority=uni->default_priority;
+ d->starttime=uni->starttime;
+ d->untiltime=uni->untiltime;
+ d->status=uni->status;
+ d->cjobs=uni->cjobs;
+
+ unistr2_to_ascii(d->servername, &uni->servername, sizeof(d->servername)-1);
+ unistr2_to_ascii(d->printername, &uni->printername, sizeof(d->printername)-1);
+ unistr2_to_ascii(d->sharename, &uni->sharename, sizeof(d->sharename)-1);
+ unistr2_to_ascii(d->portname, &uni->portname, sizeof(d->portname)-1);
+ unistr2_to_ascii(d->drivername, &uni->drivername, sizeof(d->drivername)-1);
+ unistr2_to_ascii(d->comment, &uni->comment, sizeof(d->comment)-1);
+ unistr2_to_ascii(d->location, &uni->location, sizeof(d->location)-1);
+ unistr2_to_ascii(d->sepfile, &uni->sepfile, sizeof(d->sepfile)-1);
+ unistr2_to_ascii(d->printprocessor, &uni->printprocessor, sizeof(d->printprocessor)-1);
+ unistr2_to_ascii(d->datatype, &uni->datatype, sizeof(d->datatype)-1);
+ unistr2_to_ascii(d->parameters, &uni->parameters, sizeof(d->parameters)-1);
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_getprinterdriverdir(SPOOL_Q_GETPRINTERDRIVERDIR *q_u,
+ fstring servername, fstring env_name, uint32 level,
+ NEW_BUFFER *buffer, uint32 offered)
+{
+ init_buf_unistr2(&q_u->name, &q_u->name_ptr, servername);
+ init_buf_unistr2(&q_u->environment, &q_u->environment_ptr, env_name);
+
+ q_u->level=level;
+ q_u->buffer=buffer;
+ q_u->offered=offered;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_Q_GETPRINTERDRIVERDIR structure.
+********************************************************************/
+
+BOOL spoolss_io_q_getprinterdriverdir(const char *desc, SPOOL_Q_GETPRINTERDRIVERDIR *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_getprinterdriverdir");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("name_ptr", ps, depth, &q_u->name_ptr))
+ return False;
+ if(!smb_io_unistr2("", &q_u->name, q_u->name_ptr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("", ps, depth, &q_u->environment_ptr))
+ return False;
+ if(!smb_io_unistr2("", &q_u->environment, q_u->environment_ptr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if(!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_R_GETPRINTERDRIVERDIR structure.
+********************************************************************/
+
+BOOL spoolss_io_r_getprinterdriverdir(const char *desc, SPOOL_R_GETPRINTERDRIVERDIR *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_getprinterdriverdir");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_enumprintprocessors(const char *desc, SPOOL_R_ENUMPRINTPROCESSORS *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_enumprintprocessors");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if (!prs_uint32("returned", ps, depth, &r_u->returned))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_enumprintprocessors(const char *desc, SPOOL_Q_ENUMPRINTPROCESSORS *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_enumprintprocessors");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("name_ptr", ps, depth, &q_u->name_ptr))
+ return False;
+ if (!smb_io_unistr2("name", &q_u->name, True, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("", ps, depth, &q_u->environment_ptr))
+ return False;
+ if (!smb_io_unistr2("", &q_u->environment, q_u->environment_ptr, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if(!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_addprintprocessor(const char *desc, SPOOL_Q_ADDPRINTPROCESSOR *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_addprintprocessor");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("server_ptr", ps, depth, &q_u->server_ptr))
+ return False;
+ if (!smb_io_unistr2("server", &q_u->server, q_u->server_ptr, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_unistr2("environment", &q_u->environment, True, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_unistr2("path", &q_u->path, True, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_unistr2("name", &q_u->name, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_addprintprocessor(const char *desc, SPOOL_R_ADDPRINTPROCESSOR *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_addprintproicessor");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_enumprintprocdatatypes(const char *desc, SPOOL_R_ENUMPRINTPROCDATATYPES *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_enumprintprocdatatypes");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if (!prs_uint32("returned", ps, depth, &r_u->returned))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_enumprintprocdatatypes(const char *desc, SPOOL_Q_ENUMPRINTPROCDATATYPES *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_enumprintprocdatatypes");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("name_ptr", ps, depth, &q_u->name_ptr))
+ return False;
+ if (!smb_io_unistr2("name", &q_u->name, True, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("processor_ptr", ps, depth, &q_u->processor_ptr))
+ return False;
+ if (!smb_io_unistr2("processor", &q_u->processor, q_u->processor_ptr, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if(!spoolss_io_buffer("buffer", ps, depth, &q_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_Q_ENUMPRINTMONITORS structure.
+********************************************************************/
+
+BOOL spoolss_io_q_enumprintmonitors(const char *desc, SPOOL_Q_ENUMPRINTMONITORS *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_enumprintmonitors");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("name_ptr", ps, depth, &q_u->name_ptr))
+ return False;
+ if (!smb_io_unistr2("name", &q_u->name, True, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if(!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_enumprintmonitors(const char *desc, SPOOL_R_ENUMPRINTMONITORS *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_enumprintmonitors");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if (!prs_uint32("returned", ps, depth, &r_u->returned))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_enumprinterdata(const char *desc, SPOOL_R_ENUMPRINTERDATA *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_enumprinterdata");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("valuesize", ps, depth, &r_u->valuesize))
+ return False;
+
+ if (UNMARSHALLING(ps) && r_u->valuesize) {
+ r_u->value = (uint16 *)prs_alloc_mem(ps, r_u->valuesize * 2);
+ if (!r_u->value) {
+ DEBUG(0, ("spoolss_io_r_enumprinterdata: out of memory for printerdata value\n"));
+ return False;
+ }
+ }
+
+ if(!prs_uint16uni(False, "value", ps, depth, r_u->value, r_u->valuesize ))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("realvaluesize", ps, depth, &r_u->realvaluesize))
+ return False;
+
+ if(!prs_uint32("type", ps, depth, &r_u->type))
+ return False;
+
+ if(!prs_uint32("datasize", ps, depth, &r_u->datasize))
+ return False;
+
+ if (UNMARSHALLING(ps) && r_u->datasize) {
+ r_u->data = (uint8 *)prs_alloc_mem(ps, r_u->datasize);
+ if (!r_u->data) {
+ DEBUG(0, ("spoolss_io_r_enumprinterdata: out of memory for printerdata data\n"));
+ return False;
+ }
+ }
+
+ if(!prs_uint8s(False, "data", ps, depth, r_u->data, r_u->datasize))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("realdatasize", ps, depth, &r_u->realdatasize))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_enumprinterdata(const char *desc, SPOOL_Q_ENUMPRINTERDATA *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_enumprinterdata");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+ if(!prs_uint32("index", ps, depth, &q_u->index))
+ return False;
+ if(!prs_uint32("valuesize", ps, depth, &q_u->valuesize))
+ return False;
+ if(!prs_uint32("datasize", ps, depth, &q_u->datasize))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL make_spoolss_q_enumprinterdata(SPOOL_Q_ENUMPRINTERDATA *q_u,
+ const POLICY_HND *hnd,
+ uint32 idx, uint32 valuelen, uint32 datalen)
+{
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+ q_u->index=idx;
+ q_u->valuesize=valuelen;
+ q_u->datasize=datalen;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL make_spoolss_q_enumprinterdataex(SPOOL_Q_ENUMPRINTERDATAEX *q_u,
+ const POLICY_HND *hnd, const char *key,
+ uint32 size)
+{
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+ init_unistr2(&q_u->key, key, UNI_STR_TERMINATE);
+ q_u->size = size;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+BOOL make_spoolss_q_setprinterdata(SPOOL_Q_SETPRINTERDATA *q_u, const POLICY_HND *hnd,
+ char* value, uint32 data_type, char* data, uint32 data_size)
+{
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+ q_u->type = data_type;
+ init_unistr2(&q_u->value, value, UNI_STR_TERMINATE);
+
+ q_u->max_len = q_u->real_len = data_size;
+ q_u->data = (unsigned char *)data;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+BOOL make_spoolss_q_setprinterdataex(SPOOL_Q_SETPRINTERDATAEX *q_u, const POLICY_HND *hnd,
+ char *key, char* value, uint32 data_type, char* data,
+ uint32 data_size)
+{
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+ q_u->type = data_type;
+ init_unistr2(&q_u->value, value, UNI_STR_TERMINATE);
+ init_unistr2(&q_u->key, key, UNI_STR_TERMINATE);
+
+ q_u->max_len = q_u->real_len = data_size;
+ q_u->data = (unsigned char *)data;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_setprinterdata(const char *desc, SPOOL_Q_SETPRINTERDATA *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_setprinterdata");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &q_u->value, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("type", ps, depth, &q_u->type))
+ return False;
+
+ if(!prs_uint32("max_len", ps, depth, &q_u->max_len))
+ return False;
+
+ switch (q_u->type)
+ {
+ case REG_SZ:
+ case REG_BINARY:
+ case REG_DWORD:
+ case REG_MULTI_SZ:
+ if (q_u->max_len) {
+ if (UNMARSHALLING(ps))
+ q_u->data=(uint8 *)prs_alloc_mem(ps, q_u->max_len * sizeof(uint8));
+ if(q_u->data == NULL)
+ return False;
+ if(!prs_uint8s(False,"data", ps, depth, q_u->data, q_u->max_len))
+ return False;
+ }
+ if(!prs_align(ps))
+ return False;
+ break;
+ }
+
+ if(!prs_uint32("real_len", ps, depth, &q_u->real_len))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_setprinterdata(const char *desc, SPOOL_R_SETPRINTERDATA *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_setprinterdata");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+BOOL spoolss_io_q_resetprinter(const char *desc, SPOOL_Q_RESETPRINTER *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_resetprinter");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+
+ if (!prs_uint32("datatype_ptr", ps, depth, &q_u->datatype_ptr))
+ return False;
+
+ if (q_u->datatype_ptr) {
+ if (!smb_io_unistr2("datatype", &q_u->datatype, q_u->datatype_ptr?True:False, ps, depth))
+ return False;
+ }
+
+ if (!spoolss_io_devmode_cont(desc, &q_u->devmode_ctr, ps, depth))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+********************************************************************/
+BOOL spoolss_io_r_resetprinter(const char *desc, SPOOL_R_RESETPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_resetprinter");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+static BOOL spoolss_io_addform(const char *desc, FORM *f, uint32 ptr, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_addform");
+ depth++;
+ if(!prs_align(ps))
+ return False;
+
+ if (ptr!=0)
+ {
+ if(!prs_uint32("flags", ps, depth, &f->flags))
+ return False;
+ if(!prs_uint32("name_ptr", ps, depth, &f->name_ptr))
+ return False;
+ if(!prs_uint32("size_x", ps, depth, &f->size_x))
+ return False;
+ if(!prs_uint32("size_y", ps, depth, &f->size_y))
+ return False;
+ if(!prs_uint32("left", ps, depth, &f->left))
+ return False;
+ if(!prs_uint32("top", ps, depth, &f->top))
+ return False;
+ if(!prs_uint32("right", ps, depth, &f->right))
+ return False;
+ if(!prs_uint32("bottom", ps, depth, &f->bottom))
+ return False;
+
+ if(!smb_io_unistr2("", &f->name, f->name_ptr, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_deleteform(const char *desc, SPOOL_Q_DELETEFORM *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_deleteform");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+ if(!smb_io_unistr2("form name", &q_u->name, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_deleteform(const char *desc, SPOOL_R_DELETEFORM *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_deleteform");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_addform(const char *desc, SPOOL_Q_ADDFORM *q_u, prs_struct *ps, int depth)
+{
+ uint32 useless_ptr=1;
+ prs_debug(ps, depth, desc, "spoolss_io_q_addform");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+ if(!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+ if(!prs_uint32("level2", ps, depth, &q_u->level2))
+ return False;
+
+ if (q_u->level==1)
+ {
+ if(!prs_uint32("useless_ptr", ps, depth, &useless_ptr))
+ return False;
+ if(!spoolss_io_addform("", &q_u->form, useless_ptr, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_addform(const char *desc, SPOOL_R_ADDFORM *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_addform");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_setform(const char *desc, SPOOL_Q_SETFORM *q_u, prs_struct *ps, int depth)
+{
+ uint32 useless_ptr=1;
+ prs_debug(ps, depth, desc, "spoolss_io_q_setform");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &q_u->name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+ if(!prs_uint32("level2", ps, depth, &q_u->level2))
+ return False;
+
+ if (q_u->level==1)
+ {
+ if(!prs_uint32("useless_ptr", ps, depth, &useless_ptr))
+ return False;
+ if(!spoolss_io_addform("", &q_u->form, useless_ptr, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_setform(const char *desc, SPOOL_R_SETFORM *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_setform");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_R_GETJOB structure.
+********************************************************************/
+
+BOOL spoolss_io_r_getjob(const char *desc, SPOOL_R_GETJOB *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_getjob");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_Q_GETJOB structure.
+********************************************************************/
+
+BOOL spoolss_io_q_getjob(const char *desc, SPOOL_Q_GETJOB *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+ if(!prs_uint32("jobid", ps, depth, &q_u->jobid))
+ return False;
+ if(!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if(!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+void free_devmode(DEVICEMODE *devmode)
+{
+ if (devmode!=NULL) {
+ SAFE_FREE(devmode->private);
+ SAFE_FREE(devmode);
+ }
+}
+
+void free_printer_info_1(PRINTER_INFO_1 *printer)
+{
+ SAFE_FREE(printer);
+}
+
+void free_printer_info_2(PRINTER_INFO_2 *printer)
+{
+ if (printer!=NULL) {
+ free_devmode(printer->devmode);
+ printer->devmode = NULL;
+ SAFE_FREE(printer);
+ }
+}
+
+void free_printer_info_3(PRINTER_INFO_3 *printer)
+{
+ SAFE_FREE(printer);
+}
+
+void free_printer_info_4(PRINTER_INFO_4 *printer)
+{
+ SAFE_FREE(printer);
+}
+
+void free_printer_info_5(PRINTER_INFO_5 *printer)
+{
+ SAFE_FREE(printer);
+}
+
+void free_printer_info_7(PRINTER_INFO_7 *printer)
+{
+ SAFE_FREE(printer);
+}
+
+void free_job_info_2(JOB_INFO_2 *job)
+{
+ if (job!=NULL)
+ free_devmode(job->devmode);
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_replyopenprinter(SPOOL_Q_REPLYOPENPRINTER *q_u,
+ const fstring string, uint32 printer, uint32 type)
+{
+ if (q_u == NULL)
+ return False;
+
+ init_unistr2(&q_u->string, string, UNI_STR_TERMINATE);
+
+ q_u->printer=printer;
+ q_u->type=type;
+
+ q_u->unknown0=0x0;
+ q_u->unknown1=0x0;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_Q_REPLYOPENPRINTER structure.
+********************************************************************/
+
+BOOL spoolss_io_q_replyopenprinter(const char *desc, SPOOL_Q_REPLYOPENPRINTER *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_replyopenprinter");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("", &q_u->string, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("printer", ps, depth, &q_u->printer))
+ return False;
+ if(!prs_uint32("type", ps, depth, &q_u->type))
+ return False;
+
+ if(!prs_uint32("unknown0", ps, depth, &q_u->unknown0))
+ return False;
+ if(!prs_uint32("unknown1", ps, depth, &q_u->unknown1))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_R_REPLYOPENPRINTER structure.
+********************************************************************/
+
+BOOL spoolss_io_r_replyopenprinter(const char *desc, SPOOL_R_REPLYOPENPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_replyopenprinter");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&r_u->handle,ps,depth))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+BOOL make_spoolss_q_routerreplyprinter(SPOOL_Q_ROUTERREPLYPRINTER *q_u, POLICY_HND *hnd,
+ uint32 condition, uint32 change_id)
+{
+
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+
+ q_u->condition = condition;
+ q_u->change_id = change_id;
+
+ /* magic values */
+ q_u->unknown1 = 0x1;
+ memset(q_u->unknown2, 0x0, 5);
+ q_u->unknown2[0] = 0x1;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_Q_ROUTERREPLYPRINTER structure.
+********************************************************************/
+BOOL spoolss_io_q_routerreplyprinter (const char *desc, SPOOL_Q_ROUTERREPLYPRINTER *q_u, prs_struct *ps, int depth)
+{
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_routerreplyprinter");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+
+ if (!prs_uint32("condition", ps, depth, &q_u->condition))
+ return False;
+
+ if (!prs_uint32("unknown1", ps, depth, &q_u->unknown1))
+ return False;
+
+ if (!prs_uint32("change_id", ps, depth, &q_u->change_id))
+ return False;
+
+ if (!prs_uint8s(False, "private", ps, depth, q_u->unknown2, 5))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_R_ROUTERREPLYPRINTER structure.
+********************************************************************/
+BOOL spoolss_io_r_routerreplyprinter (const char *desc, SPOOL_R_ROUTERREPLYPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_routerreplyprinter");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_reply_closeprinter(SPOOL_Q_REPLYCLOSEPRINTER *q_u, POLICY_HND *hnd)
+{
+ if (q_u == NULL)
+ return False;
+
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_Q_REPLYCLOSEPRINTER structure.
+********************************************************************/
+
+BOOL spoolss_io_q_replycloseprinter(const char *desc, SPOOL_Q_REPLYCLOSEPRINTER *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_replycloseprinter");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_R_REPLYCLOSEPRINTER structure.
+********************************************************************/
+
+BOOL spoolss_io_r_replycloseprinter(const char *desc, SPOOL_R_REPLYCLOSEPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_replycloseprinter");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&r_u->handle,ps,depth))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+#if 0 /* JERRY - not currently used but could be :-) */
+
+/*******************************************************************
+ Deep copy a SPOOL_NOTIFY_INFO_DATA structure
+ ******************************************************************/
+static BOOL copy_spool_notify_info_data(SPOOL_NOTIFY_INFO_DATA *dst,
+ SPOOL_NOTIFY_INFO_DATA *src, int n)
+{
+ int i;
+
+ memcpy(dst, src, sizeof(SPOOL_NOTIFY_INFO_DATA)*n);
+
+ for (i=0; i<n; i++) {
+ int len;
+ uint16 *s = NULL;
+
+ if (src->size != POINTER)
+ continue;
+ len = src->notify_data.data.length;
+ s = malloc(sizeof(uint16)*len);
+ if (s == NULL) {
+ DEBUG(0,("copy_spool_notify_info_data: malloc() failed!\n"));
+ return False;
+ }
+
+ memcpy(s, src->notify_data.data.string, len*2);
+ dst->notify_data.data.string = s;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Deep copy a SPOOL_NOTIFY_INFO structure
+ ******************************************************************/
+static BOOL copy_spool_notify_info(SPOOL_NOTIFY_INFO *dst, SPOOL_NOTIFY_INFO *src)
+{
+ if (!dst) {
+ DEBUG(0,("copy_spool_notify_info: NULL destination pointer!\n"));
+ return False;
+ }
+
+ dst->version = src->version;
+ dst->flags = src->flags;
+ dst->count = src->count;
+
+ if (dst->count)
+ {
+ dst->data = malloc(dst->count * sizeof(SPOOL_NOTIFY_INFO_DATA));
+
+ DEBUG(10,("copy_spool_notify_info: allocating space for [%d] PRINTER_NOTIFY_INFO_DATA entries\n",
+ dst->count));
+
+ if (dst->data == NULL) {
+ DEBUG(0,("copy_spool_notify_info: malloc() failed for [%d] entries!\n",
+ dst->count));
+ return False;
+ }
+
+ return (copy_spool_notify_info_data(dst->data, src->data, src->count));
+ }
+
+ return True;
+}
+#endif /* JERRY */
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_reply_rrpcn(SPOOL_Q_REPLY_RRPCN *q_u, POLICY_HND *hnd,
+ uint32 change_low, uint32 change_high,
+ SPOOL_NOTIFY_INFO *info)
+{
+ if (q_u == NULL)
+ return False;
+
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+
+ q_u->change_low=change_low;
+ q_u->change_high=change_high;
+
+ q_u->unknown0=0x0;
+ q_u->unknown1=0x0;
+
+ q_u->info_ptr=0x0FF0ADDE;
+
+ q_u->info.version=2;
+
+ if (info->count) {
+ DEBUG(10,("make_spoolss_q_reply_rrpcn: [%d] PRINTER_NOTIFY_INFO_DATA\n",
+ info->count));
+ q_u->info.version = info->version;
+ q_u->info.flags = info->flags;
+ q_u->info.count = info->count;
+ /* pointer field - be careful! */
+ q_u->info.data = info->data;
+ }
+ else {
+ q_u->info.flags=PRINTER_NOTIFY_INFO_DISCARDED;
+ q_u->info.count=0;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_Q_REPLY_RRPCN structure.
+********************************************************************/
+
+BOOL spoolss_io_q_reply_rrpcn(const char *desc, SPOOL_Q_REPLY_RRPCN *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_reply_rrpcn");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+
+ if (!prs_uint32("change_low", ps, depth, &q_u->change_low))
+ return False;
+
+ if (!prs_uint32("change_high", ps, depth, &q_u->change_high))
+ return False;
+
+ if (!prs_uint32("unknown0", ps, depth, &q_u->unknown0))
+ return False;
+
+ if (!prs_uint32("unknown1", ps, depth, &q_u->unknown1))
+ return False;
+
+ if (!prs_uint32("info_ptr", ps, depth, &q_u->info_ptr))
+ return False;
+
+ if(q_u->info_ptr!=0)
+ if(!smb_io_notify_info(desc, &q_u->info, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_R_REPLY_RRPCN structure.
+********************************************************************/
+
+BOOL spoolss_io_r_reply_rrpcn(const char *desc, SPOOL_R_REPLY_RRPCN *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_reply_rrpcn");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("unknown0", ps, depth, &r_u->unknown0))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_getprinterdataex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_getprinterdataex(const char *desc, SPOOL_Q_GETPRINTERDATAEX *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_getprinterdataex");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_unistr2("keyname", &q_u->keyname,True,ps,depth))
+ return False;
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_unistr2("valuename", &q_u->valuename,True,ps,depth))
+ return False;
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("size", ps, depth, &q_u->size))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_getprinterdataex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_getprinterdataex(const char *desc, SPOOL_R_GETPRINTERDATAEX *r_u, prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_r_getprinterdataex");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("type", ps, depth, &r_u->type))
+ return False;
+ if (!prs_uint32("size", ps, depth, &r_u->size))
+ return False;
+
+ if (UNMARSHALLING(ps) && r_u->size) {
+ r_u->data = (unsigned char *)prs_alloc_mem(ps, r_u->size);
+ if(!r_u->data)
+ return False;
+ }
+
+ if (!prs_uint8s(False,"data", ps, depth, r_u->data, r_u->size))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ ********************************************************************/
+
+BOOL spoolss_io_q_setprinterdataex(const char *desc, SPOOL_Q_SETPRINTERDATAEX *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_setprinterdataex");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &q_u->key, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("", &q_u->value, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("type", ps, depth, &q_u->type))
+ return False;
+
+ if(!prs_uint32("max_len", ps, depth, &q_u->max_len))
+ return False;
+
+ switch (q_u->type)
+ {
+ case 0x1:
+ case 0x3:
+ case 0x4:
+ case 0x7:
+ if (q_u->max_len) {
+ if (UNMARSHALLING(ps))
+ q_u->data=(uint8 *)prs_alloc_mem(ps, q_u->max_len * sizeof(uint8));
+ if(q_u->data == NULL)
+ return False;
+ if(!prs_uint8s(False,"data", ps, depth, q_u->data, q_u->max_len))
+ return False;
+ }
+ if(!prs_align(ps))
+ return False;
+ break;
+ }
+
+ if(!prs_uint32("real_len", ps, depth, &q_u->real_len))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ ********************************************************************/
+
+BOOL spoolss_io_r_setprinterdataex(const char *desc, SPOOL_R_SETPRINTERDATAEX *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_setprinterdataex");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ ********************************************************************/
+BOOL make_spoolss_q_enumprinterkey(SPOOL_Q_ENUMPRINTERKEY *q_u,
+ POLICY_HND *hnd, const char *key,
+ uint32 size)
+{
+ DEBUG(5,("make_spoolss_q_enumprinterkey\n"));
+
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+ init_unistr2(&q_u->key, key, UNI_STR_TERMINATE);
+ q_u->size = size;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ ********************************************************************/
+
+BOOL spoolss_io_q_enumprinterkey(const char *desc, SPOOL_Q_ENUMPRINTERKEY *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_enumprinterkey");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+
+ if(!smb_io_unistr2("", &q_u->key, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("size", ps, depth, &q_u->size))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ ********************************************************************/
+
+BOOL spoolss_io_r_enumprinterkey(const char *desc, SPOOL_R_ENUMPRINTERKEY *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_enumprinterkey");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (!smb_io_buffer5("", &r_u->keys, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_deleteprinterkey(SPOOL_Q_DELETEPRINTERKEY *q_u,
+ POLICY_HND *hnd, char *keyname)
+{
+ DEBUG(5,("make_spoolss_q_deleteprinterkey\n"));
+
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+ init_unistr2(&q_u->keyname, keyname, UNI_STR_TERMINATE);
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ ********************************************************************/
+
+BOOL spoolss_io_q_deleteprinterkey(const char *desc, SPOOL_Q_DELETEPRINTERKEY *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_deleteprinterkey");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+
+ if(!smb_io_unistr2("", &q_u->keyname, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ ********************************************************************/
+
+BOOL spoolss_io_r_deleteprinterkey(const char *desc, SPOOL_R_DELETEPRINTERKEY *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_deleteprinterkey");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ * read a structure.
+ ********************************************************************/
+
+BOOL spoolss_io_q_enumprinterdataex(const char *desc, SPOOL_Q_ENUMPRINTERDATAEX *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_enumprinterdataex");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+
+ if(!smb_io_unistr2("", &q_u->key, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("size", ps, depth, &q_u->size))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+static BOOL spoolss_io_printer_enum_values_ctr(const char *desc, prs_struct *ps,
+ PRINTER_ENUM_VALUES_CTR *ctr, int depth)
+{
+ int i;
+ uint32 valuename_offset,
+ data_offset,
+ current_offset;
+ const uint32 basic_unit = 20; /* size of static portion of enum_values */
+
+ prs_debug(ps, depth, desc, "spoolss_io_printer_enum_values_ctr");
+ depth++;
+
+ /*
+ * offset data begins at 20 bytes per structure * size_of_array.
+ * Don't forget the uint32 at the beginning
+ * */
+
+ current_offset = basic_unit * ctr->size_of_array;
+
+ /* first loop to write basic enum_value information */
+
+ if (UNMARSHALLING(ps)) {
+ ctr->values = (PRINTER_ENUM_VALUES *)prs_alloc_mem(
+ ps, ctr->size_of_array * sizeof(PRINTER_ENUM_VALUES));
+ if (!ctr->values)
+ return False;
+ }
+
+ for (i=0; i<ctr->size_of_array; i++) {
+ valuename_offset = current_offset;
+ if (!prs_uint32("valuename_offset", ps, depth, &valuename_offset))
+ return False;
+
+ if (!prs_uint32("value_len", ps, depth, &ctr->values[i].value_len))
+ return False;
+
+ if (!prs_uint32("type", ps, depth, &ctr->values[i].type))
+ return False;
+
+ data_offset = ctr->values[i].value_len + valuename_offset;
+
+ if (!prs_uint32("data_offset", ps, depth, &data_offset))
+ return False;
+
+ if (!prs_uint32("data_len", ps, depth, &ctr->values[i].data_len))
+ return False;
+
+ current_offset = data_offset + ctr->values[i].data_len - basic_unit;
+ /* account for 2 byte alignment */
+ current_offset += (current_offset % 2);
+ }
+
+ /*
+ * loop #2 for writing the dynamically size objects; pay
+ * attention to 2-byte alignment here....
+ */
+
+ for (i=0; i<ctr->size_of_array; i++) {
+
+ if (!prs_unistr("valuename", ps, depth, &ctr->values[i].valuename))
+ return False;
+
+ if ( ctr->values[i].data_len ) {
+ if ( UNMARSHALLING(ps) ) {
+ ctr->values[i].data = (uint8 *)prs_alloc_mem(
+ ps, ctr->values[i].data_len);
+ if (!ctr->values[i].data)
+ return False;
+ }
+ if (!prs_uint8s(False, "data", ps, depth, ctr->values[i].data, ctr->values[i].data_len))
+ return False;
+ }
+
+ if ( !prs_align_uint16(ps) )
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ ********************************************************************/
+
+BOOL spoolss_io_r_enumprinterdataex(const char *desc, SPOOL_R_ENUMPRINTERDATAEX *r_u, prs_struct *ps, int depth)
+{
+ uint32 data_offset, end_offset;
+ prs_debug(ps, depth, desc, "spoolss_io_r_enumprinterdataex");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("size", ps, depth, &r_u->ctr.size))
+ return False;
+
+ data_offset = prs_offset(ps);
+
+ if (!prs_set_offset(ps, data_offset + r_u->ctr.size))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if(!prs_uint32("returned", ps, depth, &r_u->returned))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ r_u->ctr.size_of_array = r_u->returned;
+
+ end_offset = prs_offset(ps);
+
+ if (!prs_set_offset(ps, data_offset))
+ return False;
+
+ if (r_u->ctr.size)
+ if (!spoolss_io_printer_enum_values_ctr("", ps, &r_u->ctr, depth ))
+ return False;
+
+ if (!prs_set_offset(ps, end_offset))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ ********************************************************************/
+
+/*
+ uint32 GetPrintProcessorDirectory(
+ [in] unistr2 *name,
+ [in] unistr2 *environment,
+ [in] uint32 level,
+ [in,out] NEW_BUFFER buffer,
+ [in] uint32 offered,
+ [out] uint32 needed,
+ [out] uint32 returned
+ );
+
+*/
+
+BOOL make_spoolss_q_getprintprocessordirectory(SPOOL_Q_GETPRINTPROCESSORDIRECTORY *q_u, const char *name, char *environment, int level, NEW_BUFFER *buffer, uint32 offered)
+{
+ DEBUG(5,("make_spoolss_q_getprintprocessordirectory\n"));
+
+ init_unistr2(&q_u->name, name, UNI_STR_TERMINATE);
+ init_unistr2(&q_u->environment, environment, UNI_STR_TERMINATE);
+
+ q_u->level = level;
+
+ q_u->buffer = buffer;
+ q_u->offered = offered;
+
+ return True;
+}
+
+BOOL spoolss_io_q_getprintprocessordirectory(const char *desc, SPOOL_Q_GETPRINTPROCESSORDIRECTORY *q_u, prs_struct *ps, int depth)
+{
+ uint32 ptr;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_getprintprocessordirectory");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("ptr", ps, depth, &ptr))
+ return False;
+
+ if (ptr) {
+ if(!smb_io_unistr2("name", &q_u->name, True, ps, depth))
+ return False;
+ }
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("ptr", ps, depth, &ptr))
+ return False;
+
+ if (ptr) {
+ if(!smb_io_unistr2("environment", &q_u->environment, True,
+ ps, depth))
+ return False;
+ }
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if(!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ ********************************************************************/
+
+BOOL spoolss_io_r_getprintprocessordirectory(const char *desc, SPOOL_R_GETPRINTPROCESSORDIRECTORY *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_getprintprocessordirectory");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+BOOL smb_io_printprocessordirectory_1(const char *desc, NEW_BUFFER *buffer, PRINTPROCESSOR_DIRECTORY_1 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printprocessordirectory_1");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!smb_io_unistr(desc, &info->name, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_addform(SPOOL_Q_ADDFORM *q_u, POLICY_HND *handle,
+ int level, FORM *form)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+ q_u->level = level;
+ q_u->level2 = level;
+ memcpy(&q_u->form, form, sizeof(FORM));
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_setform(SPOOL_Q_SETFORM *q_u, POLICY_HND *handle,
+ int level, const char *form_name, FORM *form)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+ q_u->level = level;
+ q_u->level2 = level;
+ memcpy(&q_u->form, form, sizeof(FORM));
+ init_unistr2(&q_u->name, form_name, UNI_STR_TERMINATE);
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_deleteform(SPOOL_Q_DELETEFORM *q_u, POLICY_HND *handle,
+ const char *form)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+ init_unistr2(&q_u->name, form, UNI_STR_TERMINATE);
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_getform(SPOOL_Q_GETFORM *q_u, POLICY_HND *handle,
+ const char *formname, uint32 level,
+ NEW_BUFFER *buffer, uint32 offered)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+ q_u->level = level;
+ init_unistr2(&q_u->formname, formname, UNI_STR_TERMINATE);
+ q_u->buffer=buffer;
+ q_u->offered=offered;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_enumforms(SPOOL_Q_ENUMFORMS *q_u, POLICY_HND *handle,
+ uint32 level, NEW_BUFFER *buffer,
+ uint32 offered)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+ q_u->level = level;
+ q_u->buffer=buffer;
+ q_u->offered=offered;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_setjob(SPOOL_Q_SETJOB *q_u, POLICY_HND *handle,
+ uint32 jobid, uint32 level, uint32 command)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+ q_u->jobid = jobid;
+ q_u->level = level;
+
+ /* Hmm - the SPOOL_Q_SETJOB structure has a JOB_INFO ctr in it but
+ the server side code has it marked as unused. */
+
+ q_u->command = command;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_getjob(SPOOL_Q_GETJOB *q_u, POLICY_HND *handle,
+ uint32 jobid, uint32 level, NEW_BUFFER *buffer,
+ uint32 offered)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+ q_u->jobid = jobid;
+ q_u->level = level;
+ q_u->buffer = buffer;
+ q_u->offered = offered;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_startpageprinter(SPOOL_Q_STARTPAGEPRINTER *q_u,
+ POLICY_HND *handle)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_endpageprinter(SPOOL_Q_ENDPAGEPRINTER *q_u,
+ POLICY_HND *handle)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_startdocprinter(SPOOL_Q_STARTDOCPRINTER *q_u,
+ POLICY_HND *handle, uint32 level,
+ char *docname, char *outputfile,
+ char *datatype)
+{
+ DOC_INFO_CONTAINER *ctr = &q_u->doc_info_container;
+
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+
+ ctr->level = level;
+
+ switch (level) {
+ case 1:
+ ctr->docinfo.switch_value = level;
+
+ ctr->docinfo.doc_info_1.p_docname = docname ? 1 : 0;
+ ctr->docinfo.doc_info_1.p_outputfile = outputfile ? 1 : 0;
+ ctr->docinfo.doc_info_1.p_datatype = datatype ? 1 : 0;
+
+ init_unistr2(&ctr->docinfo.doc_info_1.docname, docname, UNI_STR_TERMINATE);
+ init_unistr2(&ctr->docinfo.doc_info_1.outputfile, outputfile, UNI_STR_TERMINATE);
+ init_unistr2(&ctr->docinfo.doc_info_1.datatype, datatype, UNI_STR_TERMINATE);
+
+ break;
+ case 2:
+ /* DOC_INFO_2 is only used by Windows 9x and since it
+ doesn't do printing over RPC we don't have to worry
+ about it. */
+ default:
+ DEBUG(3, ("unsupported info level %d\n", level));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_enddocprinter(SPOOL_Q_ENDDOCPRINTER *q_u,
+ POLICY_HND *handle)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_writeprinter(SPOOL_Q_WRITEPRINTER *q_u,
+ POLICY_HND *handle, uint32 data_size,
+ char *data)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+ q_u->buffer_size = q_u->buffer_size2 = data_size;
+ q_u->buffer = (unsigned char *)data;
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_deleteprinterdata(SPOOL_Q_DELETEPRINTERDATA *q_u,
+ POLICY_HND *handle, char *valuename)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+ init_unistr2(&q_u->valuename, valuename, UNI_STR_TERMINATE);
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_deleteprinterdataex(SPOOL_Q_DELETEPRINTERDATAEX *q_u,
+ POLICY_HND *handle, char *key,
+ char *value)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+ init_unistr2(&q_u->valuename, value, UNI_STR_TERMINATE);
+ init_unistr2(&q_u->keyname, key, UNI_STR_TERMINATE);
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_rffpcnex(SPOOL_Q_RFFPCNEX *q_u, POLICY_HND *handle,
+ uint32 flags, uint32 options, const char *localmachine,
+ uint32 printerlocal, SPOOL_NOTIFY_OPTION *option)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+
+ q_u->flags = flags;
+ q_u->options = options;
+
+ q_u->localmachine_ptr = 1;
+
+ init_unistr2(&q_u->localmachine, localmachine, UNI_STR_TERMINATE);
+
+ q_u->printerlocal = printerlocal;
+
+ if (option)
+ q_u->option_ptr = 1;
+
+ q_u->option = option;
+
+ return True;
+}
diff --git a/source/rpc_parse/parse_srv.c b/source/rpc_parse/parse_srv.c
new file mode 100644
index 00000000000..6349fc16325
--- /dev/null
+++ b/source/rpc_parse/parse_srv.c
@@ -0,0 +1,3575 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997,
+ * Copyright (C) Jeremy Allison 1999,
+ * Copyright (C) Nigel Williams 2001,
+ * Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2002.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+ Inits a SH_INFO_0_STR structure
+********************************************************************/
+
+void init_srv_share_info0_str(SH_INFO_0_STR *sh0, const char *net_name)
+{
+ DEBUG(5,("init_srv_share_info0_str\n"));
+
+ init_unistr2(&sh0->uni_netname, net_name, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info0_str(const char *desc, SH_INFO_0_STR *sh0, prs_struct *ps, int depth)
+{
+ if (sh0 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info0_str");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(sh0->ptrs->ptr_netname)
+ if(!smb_io_unistr2("", &sh0->uni_netname, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ makes a SH_INFO_0 structure
+********************************************************************/
+
+void init_srv_share_info0(SH_INFO_0 *sh0, const char *net_name)
+{
+ DEBUG(5,("init_srv_share_info0: %s\n", net_name));
+
+ sh0->ptr_netname = (net_name != NULL) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info0(const char *desc, SH_INFO_0 *sh0, prs_struct *ps, int depth)
+{
+ if (sh0 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info0");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_netname", ps, depth, &sh0->ptr_netname))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SH_INFO_1_STR structure
+********************************************************************/
+
+void init_srv_share_info1_str(SH_INFO_1_STR *sh1, const char *net_name, const char *remark)
+{
+ DEBUG(5,("init_srv_share_info1_str\n"));
+
+ init_unistr2(&sh1->uni_netname, net_name, UNI_STR_TERMINATE);
+ init_unistr2(&sh1->uni_remark, remark, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info1_str(const char *desc, SH_INFO_1_STR *sh1, prs_struct *ps, int depth)
+{
+ if (sh1 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info1_str");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(sh1->ptrs->ptr_netname)
+ if(!smb_io_unistr2("", &sh1->uni_netname, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(sh1->ptrs->ptr_remark)
+ if(!smb_io_unistr2("", &sh1->uni_remark, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ makes a SH_INFO_1 structure
+********************************************************************/
+
+void init_srv_share_info1(SH_INFO_1 *sh1, const char *net_name, uint32 type, const char *remark)
+{
+ DEBUG(5,("init_srv_share_info1: %s %8x %s\n", net_name, type, remark));
+
+ sh1->ptr_netname = (net_name != NULL) ? 1 : 0;
+ sh1->type = type;
+ sh1->ptr_remark = (remark != NULL) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info1(const char *desc, SH_INFO_1 *sh1, prs_struct *ps, int depth)
+{
+ if (sh1 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_netname", ps, depth, &sh1->ptr_netname))
+ return False;
+ if(!prs_uint32("type ", ps, depth, &sh1->type))
+ return False;
+ if(!prs_uint32("ptr_remark ", ps, depth, &sh1->ptr_remark))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SH_INFO_2_STR structure
+********************************************************************/
+
+void init_srv_share_info2_str(SH_INFO_2_STR *sh2,
+ const char *net_name, const char *remark,
+ const char *path, const char *passwd)
+{
+ DEBUG(5,("init_srv_share_info2_str\n"));
+
+ init_unistr2(&sh2->uni_netname, net_name, UNI_STR_TERMINATE);
+ init_unistr2(&sh2->uni_remark, remark, UNI_STR_TERMINATE);
+ init_unistr2(&sh2->uni_path, path, UNI_STR_TERMINATE);
+ init_unistr2(&sh2->uni_passwd, passwd, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info2_str(const char *desc, SH_INFO_2 *sh, SH_INFO_2_STR *sh2, prs_struct *ps, int depth)
+{
+ if (sh2 == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps))
+ ZERO_STRUCTP(sh2);
+
+ prs_debug(ps, depth, desc, "srv_io_share_info2_str");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (sh->ptr_netname)
+ if(!smb_io_unistr2("", &sh2->uni_netname, True, ps, depth))
+ return False;
+
+ if (sh->ptr_remark)
+ if(!smb_io_unistr2("", &sh2->uni_remark, True, ps, depth))
+ return False;
+
+ if (sh->ptr_netname)
+ if(!smb_io_unistr2("", &sh2->uni_path, True, ps, depth))
+ return False;
+
+ if (sh->ptr_passwd)
+ if(!smb_io_unistr2("", &sh2->uni_passwd, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SH_INFO_2 structure
+********************************************************************/
+
+void init_srv_share_info2(SH_INFO_2 *sh2,
+ const char *net_name, uint32 type, const char *remark,
+ uint32 perms, uint32 max_uses, uint32 num_uses,
+ const char *path, const char *passwd)
+{
+ DEBUG(5,("init_srv_share_info2: %s %8x %s\n", net_name, type, remark));
+
+ sh2->ptr_netname = (net_name != NULL) ? 1 : 0;
+ sh2->type = type;
+ sh2->ptr_remark = (remark != NULL) ? 1 : 0;
+ sh2->perms = perms;
+ sh2->max_uses = max_uses;
+ sh2->num_uses = num_uses;
+ sh2->ptr_path = (path != NULL) ? 1 : 0;
+ sh2->ptr_passwd = (passwd != NULL) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info2(const char *desc, SH_INFO_2 *sh2, prs_struct *ps, int depth)
+{
+ if (sh2 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_netname", ps, depth, &sh2->ptr_netname))
+ return False;
+ if(!prs_uint32("type ", ps, depth, &sh2->type))
+ return False;
+ if(!prs_uint32("ptr_remark ", ps, depth, &sh2->ptr_remark))
+ return False;
+ if(!prs_uint32("perms ", ps, depth, &sh2->perms))
+ return False;
+ if(!prs_uint32("max_uses ", ps, depth, &sh2->max_uses))
+ return False;
+ if(!prs_uint32("num_uses ", ps, depth, &sh2->num_uses))
+ return False;
+ if(!prs_uint32("ptr_path ", ps, depth, &sh2->ptr_path))
+ return False;
+ if(!prs_uint32("ptr_passwd ", ps, depth, &sh2->ptr_passwd))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SH_INFO_501_STR structure
+********************************************************************/
+
+void init_srv_share_info501_str(SH_INFO_501_STR *sh501,
+ const char *net_name, const char *remark)
+{
+ DEBUG(5,("init_srv_share_info501_str\n"));
+
+ init_unistr2(&sh501->uni_netname, net_name, UNI_STR_TERMINATE);
+ init_unistr2(&sh501->uni_remark, remark, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Inits a SH_INFO_2 structure
+*******************************************************************/
+
+void init_srv_share_info501(SH_INFO_501 *sh501, const char *net_name, uint32 type, const char *remark, uint32 csc_policy)
+{
+ DEBUG(5,("init_srv_share_info501: %s %8x %s %08x\n", net_name, type,
+ remark, csc_policy));
+
+ ZERO_STRUCTP(sh501);
+
+ sh501->ptr_netname = (net_name != NULL) ? 1 : 0;
+ sh501->type = type;
+ sh501->ptr_remark = (remark != NULL) ? 1 : 0;
+ sh501->csc_policy = csc_policy;
+}
+
+/*******************************************************************
+ Reads of writes a structure.
+*******************************************************************/
+
+static BOOL srv_io_share_info501(const char *desc, SH_INFO_501 *sh501, prs_struct *ps, int depth)
+{
+ if (sh501 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info501");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("ptr_netname", ps, depth, &sh501->ptr_netname))
+ return False;
+ if (!prs_uint32("type ", ps, depth, &sh501->type))
+ return False;
+ if (!prs_uint32("ptr_remark ", ps, depth, &sh501->ptr_remark))
+ return False;
+ if (!prs_uint32("csc_policy ", ps, depth, &sh501->csc_policy))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info501_str(const char *desc, SH_INFO_501_STR *sh501, prs_struct *ps, int depth)
+{
+ if (sh501 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info501_str");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unistr2("", &sh501->uni_netname, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unistr2("", &sh501->uni_remark, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SH_INFO_502 structure
+********************************************************************/
+
+void init_srv_share_info502(SH_INFO_502 *sh502,
+ const char *net_name, uint32 type, const char *remark,
+ uint32 perms, uint32 max_uses, uint32 num_uses,
+ const char *path, const char *passwd, SEC_DESC *psd, size_t sd_size)
+{
+ DEBUG(5,("init_srv_share_info502: %s %8x %s\n", net_name, type, remark));
+
+ ZERO_STRUCTP(sh502);
+
+ sh502->ptr_netname = (net_name != NULL) ? 1 : 0;
+ sh502->type = type;
+ sh502->ptr_remark = (remark != NULL) ? 1 : 0;
+ sh502->perms = perms;
+ sh502->max_uses = max_uses;
+ sh502->num_uses = num_uses;
+ sh502->ptr_path = (path != NULL) ? 1 : 0;
+ sh502->ptr_passwd = (passwd != NULL) ? 1 : 0;
+ sh502->reserved = 0; /* actual size within rpc */
+ sh502->sd_size = (uint32)sd_size;
+ sh502->ptr_sd = (psd != NULL) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info502(const char *desc, SH_INFO_502 *sh502, prs_struct *ps, int depth)
+{
+ if (sh502 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info502");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_netname", ps, depth, &sh502->ptr_netname))
+ return False;
+ if(!prs_uint32("type ", ps, depth, &sh502->type))
+ return False;
+ if(!prs_uint32("ptr_remark ", ps, depth, &sh502->ptr_remark))
+ return False;
+ if(!prs_uint32("perms ", ps, depth, &sh502->perms))
+ return False;
+ if(!prs_uint32("max_uses ", ps, depth, &sh502->max_uses))
+ return False;
+ if(!prs_uint32("num_uses ", ps, depth, &sh502->num_uses))
+ return False;
+ if(!prs_uint32("ptr_path ", ps, depth, &sh502->ptr_path))
+ return False;
+ if(!prs_uint32("ptr_passwd ", ps, depth, &sh502->ptr_passwd))
+ return False;
+ if(!prs_uint32_pre("reserved ", ps, depth, &sh502->reserved, &sh502->reserved_offset))
+ return False;
+ if(!prs_uint32("ptr_sd ", ps, depth, &sh502->ptr_sd))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SH_INFO_502_STR structure
+********************************************************************/
+
+void init_srv_share_info502_str(SH_INFO_502_STR *sh502str,
+ const char *net_name, const char *remark,
+ const char *path, const char *passwd, SEC_DESC *psd, size_t sd_size)
+{
+ DEBUG(5,("init_srv_share_info502_str\n"));
+
+ init_unistr2(&sh502str->uni_netname, net_name, UNI_STR_TERMINATE);
+ init_unistr2(&sh502str->uni_remark, remark, UNI_STR_TERMINATE);
+ init_unistr2(&sh502str->uni_path, path, UNI_STR_TERMINATE);
+ init_unistr2(&sh502str->uni_passwd, passwd, UNI_STR_TERMINATE);
+ sh502str->sd = psd;
+ sh502str->reserved = 0;
+ sh502str->sd_size = sd_size;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info502_str(const char *desc, SH_INFO_502_STR *sh502, prs_struct *ps, int depth)
+{
+ if (sh502 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info502_str");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(sh502->ptrs->ptr_netname) {
+ if(!smb_io_unistr2("", &sh502->uni_netname, True, ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(sh502->ptrs->ptr_remark) {
+ if(!smb_io_unistr2("", &sh502->uni_remark, True, ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(sh502->ptrs->ptr_path) {
+ if(!smb_io_unistr2("", &sh502->uni_path, True, ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(sh502->ptrs->ptr_passwd) {
+ if(!smb_io_unistr2("", &sh502->uni_passwd, True, ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(sh502->ptrs->ptr_sd) {
+ uint32 old_offset;
+ uint32 reserved_offset;
+
+ if(!prs_uint32_pre("reserved ", ps, depth, &sh502->reserved, &reserved_offset))
+ return False;
+
+ old_offset = prs_offset(ps);
+
+ if (!sec_io_desc(desc, &sh502->sd, ps, depth))
+ return False;
+
+ if(UNMARSHALLING(ps)) {
+
+ sh502->ptrs->sd_size = sh502->sd_size = sec_desc_size(sh502->sd);
+
+ prs_set_offset(ps, old_offset + sh502->reserved);
+ }
+
+ prs_align(ps);
+
+ if(MARSHALLING(ps)) {
+
+ sh502->ptrs->reserved = sh502->reserved = prs_offset(ps) - old_offset;
+ }
+
+ if(!prs_uint32_post("reserved ", ps, depth,
+ &sh502->reserved, reserved_offset, sh502->reserved))
+ return False;
+ if(!prs_uint32_post("reserved ", ps, depth,
+ &sh502->ptrs->reserved, sh502->ptrs->reserved_offset, sh502->ptrs->reserved))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SH_INFO_1004_STR structure
+********************************************************************/
+
+void init_srv_share_info1004_str(SH_INFO_1004_STR *sh1004, const char *remark)
+{
+ DEBUG(5,("init_srv_share_info1004_str\n"));
+
+ init_unistr2(&sh1004->uni_remark, remark, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info1004_str(const char *desc, SH_INFO_1004_STR *sh1004, prs_struct *ps, int depth)
+{
+ if (sh1004 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info1004_str");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(sh1004->ptrs->ptr_remark)
+ if(!smb_io_unistr2("", &sh1004->uni_remark, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ makes a SH_INFO_1004 structure
+********************************************************************/
+
+void init_srv_share_info1004(SH_INFO_1004 *sh1004, const char *remark)
+{
+ DEBUG(5,("init_srv_share_info1004: %s\n", remark));
+
+ sh1004->ptr_remark = (remark != NULL) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info1004(const char *desc, SH_INFO_1004 *sh1004, prs_struct *ps, int depth)
+{
+ if (sh1004 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info1004");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_remark", ps, depth, &sh1004->ptr_remark))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info1005(const char* desc, SRV_SHARE_INFO_1005* sh1005, prs_struct* ps, int depth)
+{
+ if(sh1005 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info1005");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("share_info_flags", ps, depth,
+ &sh1005->share_info_flags))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info1006(const char* desc, SRV_SHARE_INFO_1006* sh1006, prs_struct* ps, int depth)
+{
+ if(sh1006 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info1006");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("max uses ", ps, depth, &sh1006->max_uses))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SH_INFO_1007_STR structure
+********************************************************************/
+
+void init_srv_share_info1007_str(SH_INFO_1007_STR *sh1007, const char *alternate_directory_name)
+{
+ DEBUG(5,("init_srv_share_info1007_str\n"));
+
+ init_unistr2(&sh1007->uni_AlternateDirectoryName, alternate_directory_name, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info1007_str(const char *desc, SH_INFO_1007_STR *sh1007, prs_struct *ps, int depth)
+{
+ if (sh1007 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info1007_str");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(sh1007->ptrs->ptr_AlternateDirectoryName)
+ if(!smb_io_unistr2("", &sh1007->uni_AlternateDirectoryName, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ makes a SH_INFO_1007 structure
+********************************************************************/
+
+void init_srv_share_info1007(SH_INFO_1007 *sh1007, uint32 flags, const char *alternate_directory_name)
+{
+ DEBUG(5,("init_srv_share_info1007: %s\n", alternate_directory_name));
+
+ sh1007->flags = flags;
+ sh1007->ptr_AlternateDirectoryName = (alternate_directory_name != NULL) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info1007(const char *desc, SH_INFO_1007 *sh1007, prs_struct *ps, int depth)
+{
+ if (sh1007 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info1007");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("flags ", ps, depth, &sh1007->flags))
+ return False;
+ if(!prs_uint32("ptr_Alter..", ps, depth, &sh1007->ptr_AlternateDirectoryName))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info1501(const char* desc, SRV_SHARE_INFO_1501* sh1501,
+ prs_struct* ps, int depth)
+{
+ if(sh1501 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info1501");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (!sec_io_desc_buf(desc, &sh1501->sdb, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_srv_share_ctr(const char *desc, SRV_SHARE_INFO_CTR *ctr, prs_struct *ps, int depth)
+{
+ if (ctr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_srv_share_ctr");
+ depth++;
+
+ if (UNMARSHALLING(ps)) {
+ memset(ctr, '\0', sizeof(SRV_SHARE_INFO_CTR));
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("info_level", ps, depth, &ctr->info_level))
+ return False;
+
+ if(!prs_uint32("switch_value", ps, depth, &ctr->switch_value))
+ return False;
+ if(!prs_uint32("ptr_share_info", ps, depth, &ctr->ptr_share_info))
+ return False;
+
+ if (ctr->ptr_share_info == 0)
+ return True;
+
+ if(!prs_uint32("num_entries", ps, depth, &ctr->num_entries))
+ return False;
+ if(!prs_uint32("ptr_entries", ps, depth, &ctr->ptr_entries))
+ return False;
+
+ if (ctr->ptr_entries == 0) {
+ if (ctr->num_entries == 0)
+ return True;
+ else
+ return False;
+ }
+
+ if(!prs_uint32("num_entries2", ps, depth, &ctr->num_entries2))
+ return False;
+
+ if (ctr->num_entries2 != ctr->num_entries)
+ return False;
+
+ switch (ctr->switch_value) {
+
+ case 0:
+ {
+ SRV_SHARE_INFO_0 *info0 = ctr->share.info0;
+ int num_entries = ctr->num_entries;
+ int i;
+
+ if (UNMARSHALLING(ps)) {
+ if (!(info0 = (SRV_SHARE_INFO_0 *)prs_alloc_mem(ps, num_entries * sizeof(SRV_SHARE_INFO_0))))
+ return False;
+ ctr->share.info0 = info0;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_share_info0("", &info0[i].info_0, ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ info0[i].info_0_str.ptrs = &info0[i].info_0;
+ if(!srv_io_share_info0_str("", &info0[i].info_0_str, ps, depth))
+ return False;
+ }
+
+ break;
+ }
+
+ case 1:
+ {
+ SRV_SHARE_INFO_1 *info1 = ctr->share.info1;
+ int num_entries = ctr->num_entries;
+ int i;
+
+ if (UNMARSHALLING(ps)) {
+ if (!(info1 = (SRV_SHARE_INFO_1 *)prs_alloc_mem(ps, num_entries * sizeof(SRV_SHARE_INFO_1))))
+ return False;
+ ctr->share.info1 = info1;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_share_info1("", &info1[i].info_1, ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ info1[i].info_1_str.ptrs = &info1[i].info_1;
+ if(!srv_io_share_info1_str("", &info1[i].info_1_str, ps, depth))
+ return False;
+ }
+
+ break;
+ }
+
+ case 2:
+ {
+ SRV_SHARE_INFO_2 *info2 = ctr->share.info2;
+ int num_entries = ctr->num_entries;
+ int i;
+
+ if (UNMARSHALLING(ps)) {
+ if (!(info2 = (SRV_SHARE_INFO_2 *)prs_alloc_mem(ps,num_entries * sizeof(SRV_SHARE_INFO_2))))
+ return False;
+ ctr->share.info2 = info2;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_share_info2("", &info2[i].info_2, ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_share_info2_str("", &info2[i].info_2, &info2[i].info_2_str, ps, depth))
+ return False;
+ }
+
+ break;
+ }
+
+ case 501:
+ {
+ SRV_SHARE_INFO_501 *info501 = ctr->share.info501;
+ int num_entries = ctr->num_entries;
+ int i;
+
+ if (UNMARSHALLING(ps)) {
+ if (!(info501 = (SRV_SHARE_INFO_501 *) prs_alloc_mem(ps, num_entries *
+ sizeof (SRV_SHARE_INFO_501))))
+ return False;
+ ctr->share.info501 = info501;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if (!srv_io_share_info501("", &info501[i].info_501, ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if (!srv_io_share_info501_str("", &info501[i].info_501_str, ps, depth))
+ return False;
+ }
+
+ break;
+ }
+
+ case 502:
+ {
+ SRV_SHARE_INFO_502 *info502 = ctr->share.info502;
+ int num_entries = ctr->num_entries;
+ int i;
+
+ if (UNMARSHALLING(ps)) {
+ if (!(info502 = (SRV_SHARE_INFO_502 *)prs_alloc_mem(ps,num_entries * sizeof(SRV_SHARE_INFO_502))))
+ return False;
+ ctr->share.info502 = info502;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_share_info502("", &info502[i].info_502, ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ info502[i].info_502_str.ptrs = &info502[i].info_502;
+ if(!srv_io_share_info502_str("", &info502[i].info_502_str, ps, depth))
+ return False;
+ }
+
+ break;
+ }
+
+ case 1004:
+ {
+ SRV_SHARE_INFO_1004 *info1004 = ctr->share.info1004;
+ int num_entries = ctr->num_entries;
+ int i;
+
+ if (UNMARSHALLING(ps)) {
+ if (!(info1004 = (SRV_SHARE_INFO_1004 *)prs_alloc_mem(ps,num_entries * sizeof(SRV_SHARE_INFO_1004))))
+ return False;
+ ctr->share.info1004 = info1004;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_share_info1004("", &info1004[i].info_1004, ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ info1004[i].info_1004_str.ptrs = &info1004[i].info_1004;
+ if(!srv_io_share_info1004_str("", &info1004[i].info_1004_str, ps, depth))
+ return False;
+ }
+
+ break;
+ }
+
+ case 1005:
+ {
+ SRV_SHARE_INFO_1005 *info1005 = ctr->share.info1005;
+ int num_entries = ctr->num_entries;
+ int i;
+
+ if (UNMARSHALLING(ps)) {
+ if (!(info1005 = (SRV_SHARE_INFO_1005 *)prs_alloc_mem(ps,num_entries * sizeof(SRV_SHARE_INFO_1005))))
+ return False;
+ ctr->share.info1005 = info1005;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_share_info1005("", &info1005[i], ps, depth))
+ return False;
+ }
+
+ break;
+ }
+
+ case 1006:
+ {
+ SRV_SHARE_INFO_1006 *info1006 = ctr->share.info1006;
+ int num_entries = ctr->num_entries;
+ int i;
+
+ if (UNMARSHALLING(ps)) {
+ if (!(info1006 = (SRV_SHARE_INFO_1006 *)prs_alloc_mem(ps,num_entries * sizeof(SRV_SHARE_INFO_1006))))
+ return False;
+ ctr->share.info1006 = info1006;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_share_info1006("", &info1006[i], ps, depth))
+ return False;
+ }
+
+ break;
+ }
+
+ case 1007:
+ {
+ SRV_SHARE_INFO_1007 *info1007 = ctr->share.info1007;
+ int num_entries = ctr->num_entries;
+ int i;
+
+ if (UNMARSHALLING(ps)) {
+ if (!(info1007 = (SRV_SHARE_INFO_1007 *)prs_alloc_mem(ps,num_entries * sizeof(SRV_SHARE_INFO_1007))))
+ return False;
+ ctr->share.info1007 = info1007;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_share_info1007("", &info1007[i].info_1007, ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ info1007[i].info_1007_str.ptrs = &info1007[i].info_1007;
+ if(!srv_io_share_info1007_str("", &info1007[i].info_1007_str, ps, depth))
+ return False;
+ }
+
+ break;
+ }
+
+ case 1501:
+ {
+ SRV_SHARE_INFO_1501 *info1501 = ctr->share.info1501;
+ int num_entries = ctr->num_entries;
+ int i;
+
+ if (UNMARSHALLING(ps)) {
+ if (!(info1501 = (SRV_SHARE_INFO_1501 *)prs_alloc_mem(ps,num_entries * sizeof(SRV_SHARE_INFO_1501))))
+ return False;
+ ctr->share.info1501 = info1501;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_share_info1501("", &info1501[i], ps, depth))
+ return False;
+ }
+
+ break;
+ }
+
+ default:
+ DEBUG(5,("%s no share info at switch_value %d\n",
+ tab_depth(depth), ctr->switch_value));
+ break;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SRV_Q_NET_SHARE_ENUM structure.
+********************************************************************/
+
+void init_srv_q_net_share_enum(SRV_Q_NET_SHARE_ENUM *q_n,
+ const char *srv_name, uint32 info_level,
+ uint32 preferred_len, ENUM_HND *hnd)
+{
+
+ DEBUG(5,("init_q_net_share_enum\n"));
+
+ init_buf_unistr2(&q_n->uni_srv_name, &q_n->ptr_srv_name, srv_name);
+
+ q_n->ctr.info_level = q_n->ctr.switch_value = info_level;
+ q_n->ctr.ptr_share_info = 1;
+ q_n->ctr.num_entries = 0;
+ q_n->ctr.ptr_entries = 0;
+ q_n->ctr.num_entries2 = 0;
+ q_n->preferred_len = preferred_len;
+
+ memcpy(&q_n->enum_hnd, hnd, sizeof(*hnd));
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_share_enum(const char *desc, SRV_Q_NET_SHARE_ENUM *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_share_enum");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!srv_io_srv_share_ctr("share_ctr", &q_n->ctr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("preferred_len", ps, depth, &q_n->preferred_len))
+ return False;
+
+ if(!smb_io_enum_hnd("enum_hnd", &q_n->enum_hnd, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_share_enum(const char *desc, SRV_R_NET_SHARE_ENUM *r_n, prs_struct *ps, int depth)
+{
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_share_enum");
+ depth++;
+
+ if(!srv_io_srv_share_ctr("share_ctr", &r_n->ctr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("total_entries", ps, depth, &r_n->total_entries))
+ return False;
+
+ if(!smb_io_enum_hnd("enum_hnd", &r_n->enum_hnd, ps, depth))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ initialises a structure.
+********************************************************************/
+
+BOOL init_srv_q_net_share_get_info(SRV_Q_NET_SHARE_GET_INFO *q_n, const char *srv_name, const char *share_name, uint32 info_level)
+{
+
+ uint32 ptr_share_name;
+
+ DEBUG(5,("init_srv_q_net_share_get_info\n"));
+
+ init_buf_unistr2(&q_n->uni_srv_name, &q_n->ptr_srv_name, srv_name);
+ init_buf_unistr2(&q_n->uni_share_name, &ptr_share_name, share_name);
+
+ q_n->info_level = info_level;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_share_get_info(const char *desc, SRV_Q_NET_SHARE_GET_INFO *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_share_get_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!smb_io_unistr2("", &q_n->uni_share_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("info_level", ps, depth, &q_n->info_level))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_srv_share_info(const char *desc, prs_struct *ps, int depth, SRV_SHARE_INFO *r_n)
+{
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_srv_share_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("switch_value ", ps, depth, &r_n->switch_value ))
+ return False;
+
+ if(!prs_uint32("ptr_share_ctr", ps, depth, &r_n->ptr_share_ctr))
+ return False;
+
+ if (r_n->ptr_share_ctr != 0) {
+ switch (r_n->switch_value) {
+ case 0:
+ if(!srv_io_share_info0("", &r_n->share.info0.info_0, ps, depth))
+ return False;
+
+ /* allow access to pointers in the str part. */
+ r_n->share.info0.info_0_str.ptrs = &r_n->share.info0.info_0;
+
+ if(!srv_io_share_info0_str("", &r_n->share.info0.info_0_str, ps, depth))
+ return False;
+
+ break;
+ case 1:
+ if(!srv_io_share_info1("", &r_n->share.info1.info_1, ps, depth))
+ return False;
+
+ /* allow access to pointers in the str part. */
+ r_n->share.info1.info_1_str.ptrs = &r_n->share.info1.info_1;
+
+ if(!srv_io_share_info1_str("", &r_n->share.info1.info_1_str, ps, depth))
+ return False;
+
+ break;
+ case 2:
+ if(!srv_io_share_info2("", &r_n->share.info2.info_2, ps, depth))
+ return False;
+
+ if(!srv_io_share_info2_str("", &r_n->share.info2.info_2, &r_n->share.info2.info_2_str, ps, depth))
+ return False;
+
+ break;
+ case 501:
+ if (!srv_io_share_info501("", &r_n->share.info501.info_501, ps, depth))
+ return False;
+ if (!srv_io_share_info501_str("", &r_n->share.info501.info_501_str, ps, depth))
+ return False;
+ break;
+
+ case 502:
+ if(!srv_io_share_info502("", &r_n->share.info502.info_502, ps, depth))
+ return False;
+
+ /* allow access to pointers in the str part. */
+ r_n->share.info502.info_502_str.ptrs = &r_n->share.info502.info_502;
+
+ if(!srv_io_share_info502_str("", &r_n->share.info502.info_502_str, ps, depth))
+ return False;
+ break;
+ case 1004:
+ if(!srv_io_share_info1004("", &r_n->share.info1004.info_1004, ps, depth))
+ return False;
+
+ /* allow access to pointers in the str part. */
+ r_n->share.info1004.info_1004_str.ptrs = &r_n->share.info1004.info_1004;
+
+ if(!srv_io_share_info1004_str("", &r_n->share.info1004.info_1004_str, ps, depth))
+ return False;
+ break;
+ case 1005:
+ if(!srv_io_share_info1005("", &r_n->share.info1005, ps, depth))
+ return False;
+ break;
+ case 1006:
+ if(!srv_io_share_info1006("", &r_n->share.info1006, ps, depth))
+ return False;
+ break;
+ case 1007:
+ if(!srv_io_share_info1007("", &r_n->share.info1007.info_1007, ps, depth))
+ return False;
+
+ /* allow access to pointers in the str part. */
+ r_n->share.info1007.info_1007_str.ptrs = &r_n->share.info1007.info_1007;
+
+ if(!srv_io_share_info1007_str("", &r_n->share.info1007.info_1007_str, ps, depth))
+ return False;
+ break;
+ case 1501:
+ if (!srv_io_share_info1501("", &r_n->share.info1501, ps, depth))
+ return False;
+ default:
+ DEBUG(5,("%s no share info at switch_value %d\n",
+ tab_depth(depth), r_n->switch_value));
+ break;
+ }
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_share_get_info(const char *desc, SRV_R_NET_SHARE_GET_INFO *r_n, prs_struct *ps, int depth)
+{
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_share_get_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!srv_io_srv_share_info("info ", ps, depth, &r_n->info))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ intialises a structure.
+********************************************************************/
+
+BOOL init_srv_q_net_share_set_info(SRV_Q_NET_SHARE_SET_INFO *q_n,
+ const char *srv_name,
+ const char *share_name,
+ uint32 info_level,
+ const SRV_SHARE_INFO *info)
+{
+
+ uint32 ptr_share_name;
+
+ DEBUG(5,("init_srv_q_net_share_set_info\n"));
+
+ init_buf_unistr2(&q_n->uni_srv_name, &q_n->ptr_srv_name, srv_name);
+ init_buf_unistr2(&q_n->uni_share_name, &ptr_share_name, share_name);
+
+ q_n->info_level = info_level;
+
+ q_n->info = *info;
+
+ q_n->ptr_parm_error = 1;
+ q_n->parm_error = 0;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_share_set_info(const char *desc, SRV_Q_NET_SHARE_SET_INFO *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_share_set_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!smb_io_unistr2("", &q_n->uni_share_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("info_level", ps, depth, &q_n->info_level))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!srv_io_srv_share_info("info ", ps, depth, &q_n->info))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("ptr_parm_error", ps, depth, &q_n->ptr_parm_error))
+ return False;
+ if(q_n->ptr_parm_error!=0) {
+ if(!prs_uint32("parm_error", ps, depth, &q_n->parm_error))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_share_set_info(const char *desc, SRV_R_NET_SHARE_SET_INFO *r_n, prs_struct *ps, int depth)
+{
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_share_set_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_parm_error ", ps, depth, &r_n->ptr_parm_error))
+ return False;
+
+ if(r_n->ptr_parm_error) {
+
+ if(!prs_uint32("parm_error ", ps, depth, &r_n->parm_error))
+ return False;
+ }
+
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_share_add(const char *desc, SRV_Q_NET_SHARE_ADD *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_share_add");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("info_level", ps, depth, &q_n->info_level))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!srv_io_srv_share_info("info ", ps, depth, &q_n->info))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_err_index", ps, depth, &q_n->ptr_err_index))
+ return False;
+ if (q_n->ptr_err_index)
+ if (!prs_uint32("err_index", ps, depth, &q_n->err_index))
+ return False;
+
+ return True;
+}
+
+void init_srv_q_net_share_add(SRV_Q_NET_SHARE_ADD *q, const char *srvname,
+ const char *netname, uint32 type, const char *remark,
+ uint32 perms, uint32 max_uses, uint32 num_uses,
+ const char *path, const char *passwd)
+{
+ q->ptr_srv_name = 1;
+ init_unistr2(&q->uni_srv_name, srvname, UNI_STR_TERMINATE);
+ q->info.switch_value = q->info_level = 2;
+
+ q->info.ptr_share_ctr = 1;
+ init_srv_share_info2(&q->info.share.info2.info_2, netname, type,
+ remark, perms, max_uses, num_uses, path, passwd);
+ init_srv_share_info2_str(&q->info.share.info2.info_2_str, netname,
+ remark, path, passwd);
+ q->ptr_err_index = 1;
+ q->err_index = 0;
+}
+
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_share_add(const char *desc, SRV_R_NET_SHARE_ADD *r_n, prs_struct *ps, int depth)
+{
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_share_add");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_parm_error", ps, depth, &r_n->ptr_parm_error))
+ return False;
+
+ if(r_n->ptr_parm_error) {
+
+ if(!prs_uint32("parm_error", ps, depth, &r_n->parm_error))
+ return False;
+ }
+
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ initialises a structure.
+********************************************************************/
+
+void init_srv_q_net_share_del(SRV_Q_NET_SHARE_DEL *del, const char *srvname,
+ const char *sharename)
+{
+ del->ptr_srv_name = 1;
+ init_unistr2(&del->uni_srv_name, srvname, UNI_STR_TERMINATE);
+ init_unistr2(&del->uni_share_name, sharename, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_share_del(const char *desc, SRV_Q_NET_SHARE_DEL *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_share_del");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!smb_io_unistr2("", &q_n->uni_share_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("reserved", ps, depth, &q_n->reserved))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_share_del(const char *desc, SRV_R_NET_SHARE_DEL *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_share_del");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &q_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SESS_INFO_0_STR structure
+********************************************************************/
+
+void init_srv_sess_info0_str(SESS_INFO_0_STR *ss0, const char *name)
+{
+ DEBUG(5,("init_srv_sess_info0_str\n"));
+
+ init_unistr2(&ss0->uni_name, name, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_sess_info0_str(const char *desc, SESS_INFO_0_STR *ss0, prs_struct *ps, int depth)
+{
+ if (ss0 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_sess_info0_str");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("", &ss0->uni_name, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SESS_INFO_0 structure
+********************************************************************/
+
+void init_srv_sess_info0(SESS_INFO_0 *ss0, const char *name)
+{
+ DEBUG(5,("init_srv_sess_info0: %s\n", name));
+
+ ss0->ptr_name = (name != NULL) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_sess_info0(const char *desc, SESS_INFO_0 *ss0, prs_struct *ps, int depth)
+{
+ if (ss0 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_sess_info0");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_name", ps, depth, &ss0->ptr_name))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_srv_sess_info_0(const char *desc, SRV_SESS_INFO_0 *ss0, prs_struct *ps, int depth)
+{
+ if (ss0 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_srv_sess_info_0");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_entries_read", ps, depth, &ss0->num_entries_read))
+ return False;
+ if(!prs_uint32("ptr_sess_info", ps, depth, &ss0->ptr_sess_info))
+ return False;
+
+ if (ss0->ptr_sess_info != 0) {
+ uint32 i;
+ uint32 num_entries = ss0->num_entries_read;
+
+ if (num_entries > MAX_SESS_ENTRIES) {
+ num_entries = MAX_SESS_ENTRIES; /* report this! */
+ }
+
+ if(!prs_uint32("num_entries_read2", ps, depth, &ss0->num_entries_read2))
+ return False;
+
+ SMB_ASSERT_ARRAY(ss0->info_0, num_entries);
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_sess_info0("", &ss0->info_0[i], ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_sess_info0_str("", &ss0->info_0_str[i], ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SESS_INFO_1_STR structure
+********************************************************************/
+
+void init_srv_sess_info1_str(SESS_INFO_1_STR *ss1, const char *name, const char *user)
+{
+ DEBUG(5,("init_srv_sess_info1_str\n"));
+
+ init_unistr2(&ss1->uni_name, name, UNI_STR_TERMINATE);
+ init_unistr2(&ss1->uni_user, user, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_sess_info1_str(const char *desc, SESS_INFO_1_STR *ss1, prs_struct *ps, int depth)
+{
+ if (ss1 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_sess_info1_str");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("", &ss1->uni_name, True, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &(ss1->uni_user), True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SESS_INFO_1 structure
+********************************************************************/
+
+void init_srv_sess_info1(SESS_INFO_1 *ss1,
+ const char *name, const char *user,
+ uint32 num_opens, uint32 open_time, uint32 idle_time,
+ uint32 user_flags)
+{
+ DEBUG(5,("init_srv_sess_info1: %s\n", name));
+
+ ss1->ptr_name = (name != NULL) ? 1 : 0;
+ ss1->ptr_user = (user != NULL) ? 1 : 0;
+
+ ss1->num_opens = num_opens;
+ ss1->open_time = open_time;
+ ss1->idle_time = idle_time;
+ ss1->user_flags = user_flags;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_sess_info1(const char *desc, SESS_INFO_1 *ss1, prs_struct *ps, int depth)
+{
+ if (ss1 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_sess_info1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_name ", ps, depth, &ss1->ptr_name))
+ return False;
+ if(!prs_uint32("ptr_user ", ps, depth, &ss1->ptr_user))
+ return False;
+
+ if(!prs_uint32("num_opens ", ps, depth, &ss1->num_opens))
+ return False;
+ if(!prs_uint32("open_time ", ps, depth, &ss1->open_time))
+ return False;
+ if(!prs_uint32("idle_time ", ps, depth, &ss1->idle_time))
+ return False;
+ if(!prs_uint32("user_flags", ps, depth, &ss1->user_flags))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_srv_sess_info_1(const char *desc, SRV_SESS_INFO_1 *ss1, prs_struct *ps, int depth)
+{
+ if (ss1 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_srv_sess_info_1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_entries_read", ps, depth, &ss1->num_entries_read))
+ return False;
+ if(!prs_uint32("ptr_sess_info", ps, depth, &ss1->ptr_sess_info))
+ return False;
+
+ if (ss1->ptr_sess_info != 0) {
+ uint32 i;
+ uint32 num_entries = ss1->num_entries_read;
+
+ if (num_entries > MAX_SESS_ENTRIES) {
+ num_entries = MAX_SESS_ENTRIES; /* report this! */
+ }
+
+ if(!prs_uint32("num_entries_read2", ps, depth, &ss1->num_entries_read2))
+ return False;
+
+ SMB_ASSERT_ARRAY(ss1->info_1, num_entries);
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_sess_info1("", &ss1->info_1[i], ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_sess_info1_str("", &ss1->info_1_str[i], ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_srv_sess_ctr(const char *desc, SRV_SESS_INFO_CTR **pp_ctr, prs_struct *ps, int depth)
+{
+ SRV_SESS_INFO_CTR *ctr = *pp_ctr;
+
+ prs_debug(ps, depth, desc, "srv_io_srv_sess_ctr");
+ depth++;
+
+ if(UNMARSHALLING(ps)) {
+ ctr = *pp_ctr = (SRV_SESS_INFO_CTR *)prs_alloc_mem(ps, sizeof(SRV_SESS_INFO_CTR));
+ if (ctr == NULL)
+ return False;
+ }
+
+ if (ctr == NULL)
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("switch_value", ps, depth, &ctr->switch_value))
+ return False;
+ if(!prs_uint32("ptr_sess_ctr", ps, depth, &ctr->ptr_sess_ctr))
+ return False;
+
+ if (ctr->ptr_sess_ctr != 0) {
+ switch (ctr->switch_value) {
+ case 0:
+ if(!srv_io_srv_sess_info_0("", &ctr->sess.info0, ps, depth))
+ return False;
+ break;
+ case 1:
+ if(!srv_io_srv_sess_info_1("", &ctr->sess.info1, ps, depth))
+ return False;
+ break;
+ default:
+ DEBUG(5,("%s no session info at switch_value %d\n",
+ tab_depth(depth), ctr->switch_value));
+ break;
+ }
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SRV_Q_NET_SESS_ENUM structure.
+********************************************************************/
+
+void init_srv_q_net_sess_enum(SRV_Q_NET_SESS_ENUM *q_n,
+ const char *srv_name, const char *qual_name,
+ const char *user_name, uint32 sess_level,
+ SRV_SESS_INFO_CTR *ctr, uint32 preferred_len,
+ ENUM_HND *hnd)
+{
+ q_n->ctr = ctr;
+
+ DEBUG(5,("init_q_net_sess_enum\n"));
+
+ init_buf_unistr2(&q_n->uni_srv_name, &q_n->ptr_srv_name, srv_name);
+ init_buf_unistr2(&q_n->uni_qual_name, &q_n->ptr_qual_name, qual_name);
+ init_buf_unistr2(&q_n->uni_user_name, &q_n->ptr_user_name, user_name);
+
+ q_n->sess_level = sess_level;
+ q_n->preferred_len = preferred_len;
+
+ memcpy(&q_n->enum_hnd, hnd, sizeof(*hnd));
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_sess_enum(const char *desc, SRV_Q_NET_SESS_ENUM *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_sess_enum");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_qual_name", ps, depth, &q_n->ptr_qual_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_qual_name, q_n->ptr_qual_name, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("ptr_user_name", ps, depth, &q_n->ptr_user_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_user_name, q_n->ptr_user_name, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("sess_level", ps, depth, &q_n->sess_level))
+ return False;
+
+ if (q_n->sess_level != (uint32)-1) {
+ if(!srv_io_srv_sess_ctr("sess_ctr", &q_n->ctr, ps, depth))
+ return False;
+ }
+
+ if(!prs_uint32("preferred_len", ps, depth, &q_n->preferred_len))
+ return False;
+
+ if(!smb_io_enum_hnd("enum_hnd", &q_n->enum_hnd, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_sess_enum(const char *desc, SRV_R_NET_SESS_ENUM *r_n, prs_struct *ps, int depth)
+{
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_sess_enum");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("sess_level", ps, depth, &r_n->sess_level))
+ return False;
+
+ if (r_n->sess_level != (uint32)-1) {
+ if(!srv_io_srv_sess_ctr("sess_ctr", &r_n->ctr, ps, depth))
+ return False;
+ }
+
+ if(!prs_uint32("total_entries", ps, depth, &r_n->total_entries))
+ return False;
+ if(!smb_io_enum_hnd("enum_hnd", &r_n->enum_hnd, ps, depth))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a CONN_INFO_0 structure
+********************************************************************/
+
+void init_srv_conn_info0(CONN_INFO_0 *ss0, uint32 id)
+{
+ DEBUG(5,("init_srv_conn_info0\n"));
+
+ ss0->id = id;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_conn_info0(const char *desc, CONN_INFO_0 *ss0, prs_struct *ps, int depth)
+{
+ if (ss0 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_conn_info0");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("id", ps, depth, &ss0->id))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_srv_conn_info_0(const char *desc, SRV_CONN_INFO_0 *ss0, prs_struct *ps, int depth)
+{
+ if (ss0 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_srv_conn_info_0");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_entries_read", ps, depth, &ss0->num_entries_read))
+ return False;
+ if(!prs_uint32("ptr_conn_info", ps, depth, &ss0->ptr_conn_info))
+ return False;
+
+ if (ss0->ptr_conn_info != 0) {
+ int i;
+ int num_entries = ss0->num_entries_read;
+
+ if (num_entries > MAX_CONN_ENTRIES) {
+ num_entries = MAX_CONN_ENTRIES; /* report this! */
+ }
+
+ if(!prs_uint32("num_entries_read2", ps, depth, &ss0->num_entries_read2))
+ return False;
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_conn_info0("", &ss0->info_0[i], ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a CONN_INFO_1_STR structure
+********************************************************************/
+
+void init_srv_conn_info1_str(CONN_INFO_1_STR *ss1, const char *usr_name, const char *net_name)
+{
+ DEBUG(5,("init_srv_conn_info1_str\n"));
+
+ init_unistr2(&ss1->uni_usr_name, usr_name, UNI_STR_TERMINATE);
+ init_unistr2(&ss1->uni_net_name, net_name, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_conn_info1_str(const char *desc, CONN_INFO_1_STR *ss1, prs_struct *ps, int depth)
+{
+ if (ss1 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_conn_info1_str");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("", &ss1->uni_usr_name, True, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &ss1->uni_net_name, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a CONN_INFO_1 structure
+********************************************************************/
+
+void init_srv_conn_info1(CONN_INFO_1 *ss1,
+ uint32 id, uint32 type,
+ uint32 num_opens, uint32 num_users, uint32 open_time,
+ const char *usr_name, const char *net_name)
+{
+ DEBUG(5,("init_srv_conn_info1: %s %s\n", usr_name, net_name));
+
+ ss1->id = id ;
+ ss1->type = type ;
+ ss1->num_opens = num_opens ;
+ ss1->num_users = num_users;
+ ss1->open_time = open_time;
+
+ ss1->ptr_usr_name = (usr_name != NULL) ? 1 : 0;
+ ss1->ptr_net_name = (net_name != NULL) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_conn_info1(const char *desc, CONN_INFO_1 *ss1, prs_struct *ps, int depth)
+{
+ if (ss1 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_conn_info1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("id ", ps, depth, &ss1->id))
+ return False;
+ if(!prs_uint32("type ", ps, depth, &ss1->type))
+ return False;
+ if(!prs_uint32("num_opens ", ps, depth, &ss1->num_opens))
+ return False;
+ if(!prs_uint32("num_users ", ps, depth, &ss1->num_users))
+ return False;
+ if(!prs_uint32("open_time ", ps, depth, &ss1->open_time))
+ return False;
+
+ if(!prs_uint32("ptr_usr_name", ps, depth, &ss1->ptr_usr_name))
+ return False;
+ if(!prs_uint32("ptr_net_name", ps, depth, &ss1->ptr_net_name))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_srv_conn_info_1(const char *desc, SRV_CONN_INFO_1 *ss1, prs_struct *ps, int depth)
+{
+ if (ss1 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_srv_conn_info_1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_entries_read", ps, depth, &ss1->num_entries_read))
+ return False;
+ if(!prs_uint32("ptr_conn_info", ps, depth, &ss1->ptr_conn_info))
+ return False;
+
+ if (ss1->ptr_conn_info != 0) {
+ int i;
+ int num_entries = ss1->num_entries_read;
+
+ if (num_entries > MAX_CONN_ENTRIES) {
+ num_entries = MAX_CONN_ENTRIES; /* report this! */
+ }
+
+ if(!prs_uint32("num_entries_read2", ps, depth, &ss1->num_entries_read2))
+ return False;
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_conn_info1("", &ss1->info_1[i], ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_conn_info1_str("", &ss1->info_1_str[i], ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_srv_conn_ctr(const char *desc, SRV_CONN_INFO_CTR **pp_ctr, prs_struct *ps, int depth)
+{
+ SRV_CONN_INFO_CTR *ctr = *pp_ctr;
+
+ prs_debug(ps, depth, desc, "srv_io_srv_conn_ctr");
+ depth++;
+
+ if (UNMARSHALLING(ps)) {
+ ctr = *pp_ctr = (SRV_CONN_INFO_CTR *)prs_alloc_mem(ps, sizeof(SRV_CONN_INFO_CTR));
+ if (ctr == NULL)
+ return False;
+ }
+
+ if (ctr == NULL)
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("switch_value", ps, depth, &ctr->switch_value))
+ return False;
+ if(!prs_uint32("ptr_conn_ctr", ps, depth, &ctr->ptr_conn_ctr))
+ return False;
+
+ if (ctr->ptr_conn_ctr != 0) {
+ switch (ctr->switch_value) {
+ case 0:
+ if(!srv_io_srv_conn_info_0("", &ctr->conn.info0, ps, depth))
+ return False;
+ break;
+ case 1:
+ if(!srv_io_srv_conn_info_1("", &ctr->conn.info1, ps, depth))
+ return False;
+ break;
+ default:
+ DEBUG(5,("%s no connection info at switch_value %d\n",
+ tab_depth(depth), ctr->switch_value));
+ break;
+ }
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+void init_srv_q_net_conn_enum(SRV_Q_NET_CONN_ENUM *q_n,
+ const char *srv_name, const char *qual_name,
+ uint32 conn_level, SRV_CONN_INFO_CTR *ctr,
+ uint32 preferred_len,
+ ENUM_HND *hnd)
+{
+ DEBUG(5,("init_q_net_conn_enum\n"));
+
+ q_n->ctr = ctr;
+
+ init_buf_unistr2(&q_n->uni_srv_name, &q_n->ptr_srv_name, srv_name );
+ init_buf_unistr2(&q_n->uni_qual_name, &q_n->ptr_qual_name, qual_name);
+
+ q_n->conn_level = conn_level;
+ q_n->preferred_len = preferred_len;
+
+ memcpy(&q_n->enum_hnd, hnd, sizeof(*hnd));
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_conn_enum(const char *desc, SRV_Q_NET_CONN_ENUM *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_conn_enum");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name ", ps, depth, &q_n->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, q_n->ptr_srv_name, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_qual_name", ps, depth, &q_n->ptr_qual_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_qual_name, q_n->ptr_qual_name, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("conn_level", ps, depth, &q_n->conn_level))
+ return False;
+
+ if (q_n->conn_level != (uint32)-1) {
+ if(!srv_io_srv_conn_ctr("conn_ctr", &q_n->ctr, ps, depth))
+ return False;
+ }
+
+ if(!prs_uint32("preferred_len", ps, depth, &q_n->preferred_len))
+ return False;
+
+ if(!smb_io_enum_hnd("enum_hnd", &q_n->enum_hnd, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_conn_enum(const char *desc, SRV_R_NET_CONN_ENUM *r_n, prs_struct *ps, int depth)
+{
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_conn_enum");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("conn_level", ps, depth, &r_n->conn_level))
+ return False;
+
+ if (r_n->conn_level != (uint32)-1) {
+ if(!srv_io_srv_conn_ctr("conn_ctr", &r_n->ctr, ps, depth))
+ return False;
+ }
+
+ if(!prs_uint32("total_entries", ps, depth, &r_n->total_entries))
+ return False;
+ if(!smb_io_enum_hnd("enum_hnd", &r_n->enum_hnd, ps, depth))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a FILE_INFO_3_STR structure
+********************************************************************/
+
+void init_srv_file_info3_str(FILE_INFO_3_STR *fi3, const char *user_name, const char *path_name)
+{
+ DEBUG(5,("init_srv_file_info3_str\n"));
+
+ init_unistr2(&fi3->uni_path_name, path_name, UNI_STR_TERMINATE);
+ init_unistr2(&fi3->uni_user_name, user_name, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_file_info3_str(const char *desc, FILE_INFO_3_STR *sh1, prs_struct *ps, int depth)
+{
+ if (sh1 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_file_info3_str");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("", &sh1->uni_path_name, True, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &sh1->uni_user_name, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a FILE_INFO_3 structure
+********************************************************************/
+
+void init_srv_file_info3(FILE_INFO_3 *fl3,
+ uint32 id, uint32 perms, uint32 num_locks,
+ const char *path_name, const char *user_name)
+{
+ DEBUG(5,("init_srv_file_info3: %s %s\n", path_name, user_name));
+
+ fl3->id = id;
+ fl3->perms = perms;
+ fl3->num_locks = num_locks;
+
+ fl3->ptr_path_name = (path_name != NULL) ? 1 : 0;
+ fl3->ptr_user_name = (user_name != NULL) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_file_info3(const char *desc, FILE_INFO_3 *fl3, prs_struct *ps, int depth)
+{
+ if (fl3 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_file_info3");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("id ", ps, depth, &fl3->id))
+ return False;
+ if(!prs_uint32("perms ", ps, depth, &fl3->perms))
+ return False;
+ if(!prs_uint32("num_locks ", ps, depth, &fl3->num_locks))
+ return False;
+ if(!prs_uint32("ptr_path_name", ps, depth, &fl3->ptr_path_name))
+ return False;
+ if(!prs_uint32("ptr_user_name", ps, depth, &fl3->ptr_user_name))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_srv_file_ctr(const char *desc, SRV_FILE_INFO_CTR *ctr, prs_struct *ps, int depth)
+{
+ if (ctr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_srv_file_ctr");
+ depth++;
+
+ if (UNMARSHALLING(ps)) {
+ memset(ctr, '\0', sizeof(SRV_FILE_INFO_CTR));
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("switch_value", ps, depth, &ctr->switch_value))
+ return False;
+ if (ctr->switch_value != 3) {
+ DEBUG(5,("%s File info %d level not supported\n",
+ tab_depth(depth), ctr->switch_value));
+ }
+ if(!prs_uint32("ptr_file_info", ps, depth, &ctr->ptr_file_info))
+ return False;
+ if(!prs_uint32("num_entries", ps, depth, &ctr->num_entries))
+ return False;
+ if(!prs_uint32("ptr_entries", ps, depth, &ctr->ptr_entries))
+ return False;
+ if (ctr->ptr_entries == 0)
+ return True;
+ if(!prs_uint32("num_entries2", ps, depth,
+ &ctr->num_entries2))
+ return False;
+
+ switch (ctr->switch_value) {
+ case 3: {
+ SRV_FILE_INFO_3 *info3 = ctr->file.info3;
+ int num_entries = ctr->num_entries;
+ int i;
+
+ if (UNMARSHALLING(ps)) {
+ if (!(info3 = (SRV_FILE_INFO_3 *)prs_alloc_mem(ps, num_entries * sizeof(SRV_FILE_INFO_3))))
+ return False;
+ ctr->file.info3 = info3;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_file_info3("", &ctr->file.info3[i].info_3, ps, depth))
+ return False;
+ }
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_file_info3_str("", &ctr->file.info3[i].info_3_str, ps, depth))
+ return False;
+ }
+ break;
+ }
+ default:
+ DEBUG(5,("%s no file info at switch_value %d\n",
+ tab_depth(depth), ctr->switch_value));
+ break;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SRV_Q_NET_FILE_ENUM structure.
+********************************************************************/
+
+void init_srv_q_net_file_enum(SRV_Q_NET_FILE_ENUM *q_n,
+ const char *srv_name, const char *qual_name,
+ const char *user_name,
+ uint32 file_level, SRV_FILE_INFO_CTR *ctr,
+ uint32 preferred_len,
+ ENUM_HND *hnd)
+{
+ DEBUG(5,("init_q_net_file_enum\n"));
+
+ init_buf_unistr2(&q_n->uni_srv_name, &q_n->ptr_srv_name, srv_name);
+ init_buf_unistr2(&q_n->uni_qual_name, &q_n->ptr_qual_name, qual_name);
+ init_buf_unistr2(&q_n->uni_user_name, &q_n->ptr_user_name, user_name);
+
+ q_n->file_level = q_n->ctr.switch_value = file_level;
+ q_n->preferred_len = preferred_len;
+ q_n->ctr.ptr_file_info = 1;
+ q_n->ctr.num_entries = 0;
+ q_n->ctr.num_entries2 = 0;
+
+ memcpy(&q_n->enum_hnd, hnd, sizeof(*hnd));
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_file_enum(const char *desc, SRV_Q_NET_FILE_ENUM *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_file_enum");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_qual_name", ps, depth, &q_n->ptr_qual_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_qual_name, q_n->ptr_qual_name, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_user_name", ps, depth, &q_n->ptr_user_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_user_name, q_n->ptr_user_name, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("file_level", ps, depth, &q_n->file_level))
+ return False;
+
+ if (q_n->file_level != (uint32)-1) {
+ if(!srv_io_srv_file_ctr("file_ctr", &q_n->ctr, ps, depth))
+ return False;
+ }
+
+ if(!prs_uint32("preferred_len", ps, depth, &q_n->preferred_len))
+ return False;
+
+ if(!smb_io_enum_hnd("enum_hnd", &q_n->enum_hnd, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_file_enum(const char *desc, SRV_R_NET_FILE_ENUM *r_n, prs_struct *ps, int depth)
+{
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_file_enum");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("file_level", ps, depth, &r_n->file_level))
+ return False;
+
+ if (r_n->file_level != 0) {
+ if(!srv_io_srv_file_ctr("file_ctr", &r_n->ctr, ps, depth))
+ return False;
+ }
+
+ if(!prs_uint32("total_entries", ps, depth, &r_n->total_entries))
+ return False;
+ if(!smb_io_enum_hnd("enum_hnd", &r_n->enum_hnd, ps, depth))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Initialize a net file close request
+********************************************************************/
+void init_srv_q_net_file_close(SRV_Q_NET_FILE_CLOSE *q_n, const char *server,
+ uint32 file_id)
+{
+ q_n->ptr_srv_name = 1;
+ init_unistr2(&q_n->uni_srv_name, server, UNI_STR_TERMINATE);
+ q_n->file_id = file_id;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+BOOL srv_io_q_net_file_close(const char *desc, SRV_Q_NET_FILE_CLOSE *q_n,
+ prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_file_close");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("file_id", ps, depth, &q_n->file_id))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_file_close(const char *desc, SRV_R_NET_FILE_CLOSE *q_n,
+ prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_file_close");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &q_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SRV_INFO_100 structure.
+ ********************************************************************/
+
+void init_srv_info_100(SRV_INFO_100 *sv100, uint32 platform_id, const char *name)
+{
+ DEBUG(5,("init_srv_info_100\n"));
+
+ sv100->platform_id = platform_id;
+ init_buf_unistr2(&sv100->uni_name, &sv100->ptr_name, name);
+}
+
+/*******************************************************************
+ Reads or writes a SRV_INFO_101 structure.
+ ********************************************************************/
+
+static BOOL srv_io_info_100(const char *desc, SRV_INFO_100 *sv100, prs_struct *ps, int depth)
+{
+ if (sv100 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_info_100");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("platform_id ", ps, depth, &sv100->platform_id))
+ return False;
+ if(!prs_uint32("ptr_name ", ps, depth, &sv100->ptr_name))
+ return False;
+
+ if(!smb_io_unistr2("uni_name ", &sv100->uni_name, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Inits a SRV_INFO_101 structure.
+ ********************************************************************/
+
+void init_srv_info_101(SRV_INFO_101 *sv101, uint32 platform_id, const char *name,
+ uint32 ver_major, uint32 ver_minor,
+ uint32 srv_type, const char *comment)
+{
+ DEBUG(5,("init_srv_info_101\n"));
+
+ sv101->platform_id = platform_id;
+ init_buf_unistr2(&sv101->uni_name, &sv101->ptr_name, name);
+ sv101->ver_major = ver_major;
+ sv101->ver_minor = ver_minor;
+ sv101->srv_type = srv_type;
+ init_buf_unistr2(&sv101->uni_comment, &sv101->ptr_comment, comment);
+}
+
+/*******************************************************************
+ Reads or writes a SRV_INFO_101 structure.
+ ********************************************************************/
+
+static BOOL srv_io_info_101(const char *desc, SRV_INFO_101 *sv101, prs_struct *ps, int depth)
+{
+ if (sv101 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_info_101");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("platform_id ", ps, depth, &sv101->platform_id))
+ return False;
+ if(!prs_uint32("ptr_name ", ps, depth, &sv101->ptr_name))
+ return False;
+ if(!prs_uint32("ver_major ", ps, depth, &sv101->ver_major))
+ return False;
+ if(!prs_uint32("ver_minor ", ps, depth, &sv101->ver_minor))
+ return False;
+ if(!prs_uint32("srv_type ", ps, depth, &sv101->srv_type))
+ return False;
+ if(!prs_uint32("ptr_comment ", ps, depth, &sv101->ptr_comment))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("uni_name ", &sv101->uni_name, True, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_comment ", &sv101->uni_comment, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SRV_INFO_102 structure.
+ ********************************************************************/
+
+void init_srv_info_102(SRV_INFO_102 *sv102, uint32 platform_id, const char *name,
+ const char *comment, uint32 ver_major, uint32 ver_minor,
+ uint32 srv_type, uint32 users, uint32 disc, uint32 hidden,
+ uint32 announce, uint32 ann_delta, uint32 licenses,
+ const char *usr_path)
+{
+ DEBUG(5,("init_srv_info_102\n"));
+
+ sv102->platform_id = platform_id;
+ init_buf_unistr2(&sv102->uni_name, &sv102->ptr_name, name);
+ sv102->ver_major = ver_major;
+ sv102->ver_minor = ver_minor;
+ sv102->srv_type = srv_type;
+ init_buf_unistr2(&sv102->uni_comment, &sv102->ptr_comment, comment);
+
+ /* same as 101 up to here */
+
+ sv102->users = users;
+ sv102->disc = disc;
+ sv102->hidden = hidden;
+ sv102->announce = announce;
+ sv102->ann_delta = ann_delta;
+ sv102->licenses = licenses;
+ init_buf_unistr2(&sv102->uni_usr_path, &sv102->ptr_usr_path, usr_path);
+}
+
+
+/*******************************************************************
+ Reads or writes a SRV_INFO_102 structure.
+ ********************************************************************/
+
+static BOOL srv_io_info_102(const char *desc, SRV_INFO_102 *sv102, prs_struct *ps, int depth)
+{
+ if (sv102 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_info102");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("platform_id ", ps, depth, &sv102->platform_id))
+ return False;
+ if(!prs_uint32("ptr_name ", ps, depth, &sv102->ptr_name))
+ return False;
+ if(!prs_uint32("ver_major ", ps, depth, &sv102->ver_major))
+ return False;
+ if(!prs_uint32("ver_minor ", ps, depth, &sv102->ver_minor))
+ return False;
+ if(!prs_uint32("srv_type ", ps, depth, &sv102->srv_type))
+ return False;
+ if(!prs_uint32("ptr_comment ", ps, depth, &sv102->ptr_comment))
+ return False;
+
+ /* same as 101 up to here */
+
+ if(!prs_uint32("users ", ps, depth, &sv102->users))
+ return False;
+ if(!prs_uint32("disc ", ps, depth, &sv102->disc))
+ return False;
+ if(!prs_uint32("hidden ", ps, depth, &sv102->hidden))
+ return False;
+ if(!prs_uint32("announce ", ps, depth, &sv102->announce))
+ return False;
+ if(!prs_uint32("ann_delta ", ps, depth, &sv102->ann_delta))
+ return False;
+ if(!prs_uint32("licenses ", ps, depth, &sv102->licenses))
+ return False;
+ if(!prs_uint32("ptr_usr_path", ps, depth, &sv102->ptr_usr_path))
+ return False;
+
+ if(!smb_io_unistr2("uni_name ", &sv102->uni_name, True, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unistr2("uni_comment ", &sv102->uni_comment, True, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unistr2("uni_usr_path", &sv102->uni_usr_path, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a SRV_INFO_102 structure.
+ ********************************************************************/
+
+static BOOL srv_io_info_ctr(const char *desc, SRV_INFO_CTR *ctr, prs_struct *ps, int depth)
+{
+ if (ctr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_info_ctr");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("switch_value", ps, depth, &ctr->switch_value))
+ return False;
+ if(!prs_uint32("ptr_srv_ctr ", ps, depth, &ctr->ptr_srv_ctr))
+ return False;
+
+ if (ctr->ptr_srv_ctr != 0 && ctr->switch_value != 0 && ctr != NULL) {
+ switch (ctr->switch_value) {
+ case 100:
+ if(!srv_io_info_100("sv100", &ctr->srv.sv100, ps, depth))
+ return False;
+ break;
+ case 101:
+ if(!srv_io_info_101("sv101", &ctr->srv.sv101, ps, depth))
+ return False;
+ break;
+ case 102:
+ if(!srv_io_info_102("sv102", &ctr->srv.sv102, ps, depth))
+ return False;
+ break;
+ default:
+ DEBUG(5,("%s no server info at switch_value %d\n",
+ tab_depth(depth), ctr->switch_value));
+ break;
+ }
+ if(!prs_align(ps))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SRV_Q_NET_SRV_GET_INFO structure.
+ ********************************************************************/
+
+void init_srv_q_net_srv_get_info(SRV_Q_NET_SRV_GET_INFO *srv,
+ const char *server_name, uint32 switch_value)
+{
+ DEBUG(5,("init_srv_q_net_srv_get_info\n"));
+
+ init_buf_unistr2(&srv->uni_srv_name, &srv->ptr_srv_name, server_name);
+
+ srv->switch_value = switch_value;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_srv_get_info(const char *desc, SRV_Q_NET_SRV_GET_INFO *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_srv_get_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name ", ps, depth, &q_n->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("switch_value ", ps, depth, &q_n->switch_value))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SRV_R_NET_SRV_GET_INFO structure.
+ ********************************************************************/
+
+void init_srv_r_net_srv_get_info(SRV_R_NET_SRV_GET_INFO *srv,
+ uint32 switch_value, SRV_INFO_CTR *ctr, WERROR status)
+{
+ DEBUG(5,("init_srv_r_net_srv_get_info\n"));
+
+ srv->ctr = ctr;
+
+ if (W_ERROR_IS_OK(status)) {
+ srv->ctr->switch_value = switch_value;
+ srv->ctr->ptr_srv_ctr = 1;
+ } else {
+ srv->ctr->switch_value = 0;
+ srv->ctr->ptr_srv_ctr = 0;
+ }
+
+ srv->status = status;
+}
+
+/*******************************************************************
+ Inits a SRV_R_NET_SRV_SET_INFO structure.
+ ********************************************************************/
+
+void init_srv_r_net_srv_set_info(SRV_R_NET_SRV_SET_INFO *srv,
+ uint32 switch_value, WERROR status)
+{
+ DEBUG(5,("init_srv_r_net_srv_set_info\n"));
+
+ srv->switch_value = switch_value;
+ srv->status = status;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_srv_set_info(const char *desc, SRV_Q_NET_SRV_SET_INFO *q_n,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "srv_io_q_net_srv_set_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name ", ps, depth, &q_n->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("switch_value ", ps, depth, &q_n->switch_value))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ q_n->ctr = (SRV_INFO_CTR *)
+ prs_alloc_mem(ps, sizeof(SRV_INFO_CTR));
+
+ if (!q_n->ctr)
+ return False;
+ }
+
+ if(!srv_io_info_ctr("ctr", q_n->ctr, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+ ********************************************************************/
+
+BOOL srv_io_r_net_srv_get_info(const char *desc, SRV_R_NET_SRV_GET_INFO *r_n, prs_struct *ps, int depth)
+{
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_srv_get_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!srv_io_info_ctr("ctr", r_n->ctr, ps, depth))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+ ********************************************************************/
+
+BOOL srv_io_r_net_srv_set_info(const char *desc, SRV_R_NET_SRV_SET_INFO *r_n,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "srv_io_r_net_srv_set_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("switch value ", ps, depth, &r_n->switch_value))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+ ********************************************************************/
+
+BOOL srv_io_q_net_remote_tod(const char *desc, SRV_Q_NET_REMOTE_TOD *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_remote_tod");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name ", ps, depth, &q_n->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a TIME_OF_DAY_INFO structure.
+ ********************************************************************/
+
+static BOOL srv_io_time_of_day_info(const char *desc, TIME_OF_DAY_INFO *tod, prs_struct *ps, int depth)
+{
+ if (tod == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_time_of_day_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("elapsedt ", ps, depth, &tod->elapsedt))
+ return False;
+ if(!prs_uint32("msecs ", ps, depth, &tod->msecs))
+ return False;
+ if(!prs_uint32("hours ", ps, depth, &tod->hours))
+ return False;
+ if(!prs_uint32("mins ", ps, depth, &tod->mins))
+ return False;
+ if(!prs_uint32("secs ", ps, depth, &tod->secs))
+ return False;
+ if(!prs_uint32("hunds ", ps, depth, &tod->hunds))
+ return False;
+ if(!prs_uint32("timezone ", ps, depth, &tod->zone))
+ return False;
+ if(!prs_uint32("tintervals ", ps, depth, &tod->tintervals))
+ return False;
+ if(!prs_uint32("day ", ps, depth, &tod->day))
+ return False;
+ if(!prs_uint32("month ", ps, depth, &tod->month))
+ return False;
+ if(!prs_uint32("year ", ps, depth, &tod->year))
+ return False;
+ if(!prs_uint32("weekday ", ps, depth, &tod->weekday))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a TIME_OF_DAY_INFO structure.
+ ********************************************************************/
+
+void init_time_of_day_info(TIME_OF_DAY_INFO *tod, uint32 elapsedt, uint32 msecs,
+ uint32 hours, uint32 mins, uint32 secs, uint32 hunds,
+ uint32 zone, uint32 tintervals, uint32 day,
+ uint32 month, uint32 year, uint32 weekday)
+{
+ DEBUG(5,("init_time_of_day_info\n"));
+
+ tod->elapsedt = elapsedt;
+ tod->msecs = msecs;
+ tod->hours = hours;
+ tod->mins = mins;
+ tod->secs = secs;
+ tod->hunds = hunds;
+ tod->zone = zone;
+ tod->tintervals = tintervals;
+ tod->day = day;
+ tod->month = month;
+ tod->year = year;
+ tod->weekday = weekday;
+}
+
+
+/*******************************************************************
+ Reads or writes a structure.
+ ********************************************************************/
+
+BOOL srv_io_r_net_remote_tod(const char *desc, SRV_R_NET_REMOTE_TOD *r_n, prs_struct *ps, int depth)
+{
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_remote_tod");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_tod ", ps, depth, &r_n->ptr_srv_tod))
+ return False;
+
+ if(!srv_io_time_of_day_info("tod", r_n->tod, ps, depth))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ initialises a structure.
+ ********************************************************************/
+
+BOOL init_srv_q_net_disk_enum(SRV_Q_NET_DISK_ENUM *q_n,
+ const char *srv_name,
+ uint32 preferred_len,
+ ENUM_HND *enum_hnd
+ )
+{
+
+
+ DEBUG(5,("init_srv_q_net_srv_disk_enum\n"));
+
+ init_buf_unistr2(&q_n->uni_srv_name, &q_n->ptr_srv_name, srv_name);
+
+ q_n->disk_enum_ctr.level = 0;
+ q_n->disk_enum_ctr.disk_info_ptr = 0;
+
+ q_n->preferred_len = preferred_len;
+ memcpy(&q_n->enum_hnd, enum_hnd, sizeof(*enum_hnd));
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+ ********************************************************************/
+
+BOOL srv_io_q_net_disk_enum(const char *desc, SRV_Q_NET_DISK_ENUM *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_disk_enum");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+ return False;
+
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("level", ps, depth, &q_n->disk_enum_ctr.level))
+ return False;
+
+ if(!prs_uint32("entries_read", ps, depth, &q_n->disk_enum_ctr.entries_read))
+ return False;
+
+ if(!prs_uint32("buffer", ps, depth, &q_n->disk_enum_ctr.disk_info_ptr))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("preferred_len", ps, depth, &q_n->preferred_len))
+ return False;
+ if(!smb_io_enum_hnd("enum_hnd", &q_n->enum_hnd, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+ ********************************************************************/
+
+BOOL srv_io_r_net_disk_enum(const char *desc, SRV_R_NET_DISK_ENUM *r_n, prs_struct *ps, int depth)
+{
+
+ unsigned int i;
+ uint32 entries_read, entries_read2, entries_read3;
+
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_disk_enum");
+ depth++;
+
+ entries_read = entries_read2 = entries_read3 = r_n->disk_enum_ctr.entries_read;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("entries_read", ps, depth, &entries_read))
+ return False;
+ if(!prs_uint32("ptr_disk_info", ps, depth, &r_n->disk_enum_ctr.disk_info_ptr))
+ return False;
+
+ /*this may be max, unknown, actual?*/
+
+ if(!prs_uint32("max_elements", ps, depth, &entries_read2))
+ return False;
+ if(!prs_uint32("unknown", ps, depth, &r_n->disk_enum_ctr.unknown))
+ return False;
+ if(!prs_uint32("actual_elements", ps, depth, &entries_read3))
+ return False;
+
+ r_n->disk_enum_ctr.entries_read = entries_read3;
+
+ if(UNMARSHALLING(ps)) {
+
+ DISK_INFO *dinfo;
+
+ if(!(dinfo = (DISK_INFO *)prs_alloc_mem(ps, sizeof(*dinfo) * entries_read3)))
+ return False;
+ r_n->disk_enum_ctr.disk_info = dinfo;
+ }
+
+ for(i=0; i < r_n->disk_enum_ctr.entries_read; i++) {
+
+ if(!prs_uint32("unknown", ps, depth, &r_n->disk_enum_ctr.disk_info[i].unknown))
+ return False;
+
+ if(!smb_io_unistr3("disk_name", &r_n->disk_enum_ctr.disk_info[i].disk_name, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ }
+
+ if(!prs_uint32("total_entries", ps, depth, &r_n->total_entries))
+ return False;
+
+ if(!smb_io_enum_hnd("enum_hnd", &r_n->enum_hnd, ps, depth))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ initialises a structure.
+ ********************************************************************/
+
+BOOL init_srv_q_net_name_validate(SRV_Q_NET_NAME_VALIDATE *q_n, const char *srv_name, const char *share_name, int type)
+{
+ uint32 ptr_share_name;
+
+ DEBUG(5,("init_srv_q_net_name_validate\n"));
+
+ init_buf_unistr2(&q_n->uni_srv_name, &q_n->ptr_srv_name, srv_name);
+ init_buf_unistr2(&q_n->uni_name, &ptr_share_name, share_name);
+
+ q_n->type = type;
+ q_n->flags = 0;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+ ********************************************************************/
+
+BOOL srv_io_q_net_name_validate(const char *desc, SRV_Q_NET_NAME_VALIDATE *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_name_validate");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+ return False;
+
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("", &q_n->uni_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("type", ps, depth, &q_n->type))
+ return False;
+
+ if(!prs_uint32("flags", ps, depth, &q_n->flags))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+ ********************************************************************/
+
+BOOL srv_io_r_net_name_validate(const char *desc, SRV_R_NET_NAME_VALIDATE *r_n, prs_struct *ps, int depth)
+{
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_name_validate");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_file_query_secdesc(const char *desc, SRV_Q_NET_FILE_QUERY_SECDESC *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_file_query_secdesc");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+ return False;
+
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_qual_name", ps, depth, &q_n->ptr_qual_name))
+ return False;
+
+ if(!smb_io_unistr2("", &q_n->uni_qual_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("", &q_n->uni_file_name, True, ps, depth))
+ return False;
+
+ if(!prs_uint32("unknown1", ps, depth, &q_n->unknown1))
+ return False;
+
+ if(!prs_uint32("unknown2", ps, depth, &q_n->unknown2))
+ return False;
+
+ if(!prs_uint32("unknown3", ps, depth, &q_n->unknown3))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_file_query_secdesc(const char *desc, SRV_R_NET_FILE_QUERY_SECDESC *r_n, prs_struct *ps, int depth)
+{
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_file_query_secdesc");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_response", ps, depth, &r_n->ptr_response))
+ return False;
+
+ if(!prs_uint32("size_response", ps, depth, &r_n->size_response))
+ return False;
+
+ if(!prs_uint32("ptr_secdesc", ps, depth, &r_n->ptr_secdesc))
+ return False;
+
+ if(!prs_uint32("size_secdesc", ps, depth, &r_n->size_secdesc))
+ return False;
+
+ if(!sec_io_desc("sec_desc", &r_n->sec_desc, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_file_set_secdesc(const char *desc, SRV_Q_NET_FILE_SET_SECDESC *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_file_set_secdesc");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+ return False;
+
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_qual_name", ps, depth, &q_n->ptr_qual_name))
+ return False;
+
+ if(!smb_io_unistr2("", &q_n->uni_qual_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("", &q_n->uni_file_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("sec_info", ps, depth, &q_n->sec_info))
+ return False;
+
+ if(!prs_uint32("size_set", ps, depth, &q_n->size_set))
+ return False;
+
+ if(!prs_uint32("ptr_secdesc", ps, depth, &q_n->ptr_secdesc))
+ return False;
+
+ if(!prs_uint32("size_secdesc", ps, depth, &q_n->size_secdesc))
+ return False;
+
+ if(!sec_io_desc("sec_desc", &q_n->sec_desc, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_file_set_secdesc(const char *desc, SRV_R_NET_FILE_SET_SECDESC *r_n, prs_struct *ps, int depth)
+{
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_file_set_secdesc");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a structure
+********************************************************************/
+
+void init_srv_q_net_remote_tod(SRV_Q_NET_REMOTE_TOD *q_u, const char *server)
+{
+ q_u->ptr_srv_name = 1;
+ init_unistr2(&q_u->uni_srv_name, server, UNI_STR_TERMINATE);
+}
diff --git a/source/rpc_parse/parse_wks.c b/source/rpc_parse/parse_wks.c
new file mode 100644
index 00000000000..b6de058652b
--- /dev/null
+++ b/source/rpc_parse/parse_wks.c
@@ -0,0 +1,178 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+ Init
+ ********************************************************************/
+
+void init_wks_q_query_info(WKS_Q_QUERY_INFO *q_u,
+ char *server, uint16 switch_value)
+{
+ DEBUG(5,("init_wks_q_query_info\n"));
+
+ init_buf_unistr2(&q_u->uni_srv_name, &q_u->ptr_srv_name, server);
+ q_u->switch_value = switch_value;
+}
+
+/*******************************************************************
+ Reads or writes a WKS_Q_QUERY_INFO structure.
+********************************************************************/
+
+BOOL wks_io_q_query_info(const char *desc, WKS_Q_QUERY_INFO *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "wks_io_q_query_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_u->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_u->uni_srv_name, q_u->ptr_srv_name, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint16("switch_value", ps, depth, &q_u->switch_value))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ wks_info_100
+ ********************************************************************/
+
+void init_wks_info_100(WKS_INFO_100 *inf,
+ uint32 platform_id, uint32 ver_major, uint32 ver_minor,
+ char *my_name, char *domain_name)
+{
+ DEBUG(5,("Init WKS_INFO_100: %d\n", __LINE__));
+
+ inf->platform_id = platform_id; /* 0x0000 01f4 - unknown */
+ inf->ver_major = ver_major; /* os major version */
+ inf->ver_minor = ver_minor; /* os minor version */
+
+ init_buf_unistr2(&inf->uni_compname, &inf->ptr_compname, my_name );
+ init_buf_unistr2(&inf->uni_lan_grp, &inf->ptr_lan_grp, domain_name);
+}
+
+/*******************************************************************
+ Reads or writes a WKS_INFO_100 structure.
+********************************************************************/
+
+static BOOL wks_io_wks_info_100(const char *desc, WKS_INFO_100 *inf, prs_struct *ps, int depth)
+{
+ if (inf == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "wks_io_wks_info_100");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("platform_id ", ps, depth, &inf->platform_id)) /* 0x0000 01f4 - unknown */
+ return False;
+ if(!prs_uint32("ptr_compname", ps, depth, &inf->ptr_compname)) /* pointer to computer name */
+ return False;
+ if(!prs_uint32("ptr_lan_grp ", ps, depth, &inf->ptr_lan_grp)) /* pointer to LAN group name */
+ return False;
+ if(!prs_uint32("ver_major ", ps, depth, &inf->ver_major)) /* 4 - major os version */
+ return False;
+ if(!prs_uint32("ver_minor ", ps, depth, &inf->ver_minor)) /* 0 - minor os version */
+ return False;
+
+ if(!smb_io_unistr2("", &inf->uni_compname, inf->ptr_compname, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("", &inf->uni_lan_grp, inf->ptr_lan_grp , ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits WKS_R_QUERY_INFO.
+
+ only supports info level 100 at the moment.
+
+ ********************************************************************/
+
+void init_wks_r_query_info(WKS_R_QUERY_INFO *r_u,
+ uint32 switch_value, WKS_INFO_100 *wks100,
+ NTSTATUS status)
+{
+ DEBUG(5,("init_wks_r_unknown_0: %d\n", __LINE__));
+
+ r_u->switch_value = switch_value; /* same as in request */
+
+ r_u->ptr_1 = 1; /* pointer 1 */
+ r_u->wks100 = wks100;
+
+ r_u->status = status;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL wks_io_r_query_info(const char *desc, WKS_R_QUERY_INFO *r_u, prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "wks_io_r_query_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint16("switch_value", ps, depth, &r_u->switch_value)) /* level 100 (0x64) */
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_1 ", ps, depth, &r_u->ptr_1)) /* pointer 1 */
+ return False;
+ if(!wks_io_wks_info_100("inf", r_u->wks100, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status ", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
diff --git a/source/rpc_server/.cvsignore b/source/rpc_server/.cvsignore
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/source/rpc_server/.cvsignore
diff --git a/source/rpc_server/srv_dfs.c b/source/rpc_server/srv_dfs.c
new file mode 100644
index 00000000000..6c35917e618
--- /dev/null
+++ b/source/rpc_server/srv_dfs.c
@@ -0,0 +1,179 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines for Dfs
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Shirish Kalele 2000,
+ * Copyright (C) Jeremy Allison 2001,
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This is the interface to the dfs pipe. */
+
+#include "includes.h"
+#include "nterr.h"
+
+#define MAX_MSDFS_JUNCTIONS 256
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/**********************************************************************
+ api_dfs_exist
+ **********************************************************************/
+
+static BOOL api_dfs_exist(pipes_struct *p)
+{
+ DFS_Q_DFS_EXIST q_u;
+ DFS_R_DFS_EXIST r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ if(!dfs_io_q_dfs_exist("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _dfs_exist(p, &q_u, &r_u);
+
+ if (!dfs_io_r_dfs_exist("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*****************************************************************
+ api_dfs_add
+ *****************************************************************/
+
+static BOOL api_dfs_add(pipes_struct *p)
+{
+ DFS_Q_DFS_ADD q_u;
+ DFS_R_DFS_ADD r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!dfs_io_q_dfs_add("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _dfs_add(p, &q_u, &r_u);
+
+ if (!dfs_io_r_dfs_add("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*****************************************************************
+ api_dfs_remove
+ *****************************************************************/
+
+static BOOL api_dfs_remove(pipes_struct *p)
+{
+ DFS_Q_DFS_REMOVE q_u;
+ DFS_R_DFS_REMOVE r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!dfs_io_q_dfs_remove("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _dfs_remove(p, &q_u, &r_u);
+
+ if (!dfs_io_r_dfs_remove("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_dfs_get_info
+ *******************************************************************/
+
+static BOOL api_dfs_get_info(pipes_struct *p)
+{
+ DFS_Q_DFS_GET_INFO q_u;
+ DFS_R_DFS_GET_INFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!dfs_io_q_dfs_get_info("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _dfs_get_info(p, &q_u, &r_u);
+
+ if(!dfs_io_r_dfs_get_info("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_dfs_enum
+ *******************************************************************/
+
+static BOOL api_dfs_enum(pipes_struct *p)
+{
+ DFS_Q_DFS_ENUM q_u;
+ DFS_R_DFS_ENUM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!dfs_io_q_dfs_enum("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _dfs_enum(p, &q_u, &r_u);
+
+ if(!dfs_io_r_dfs_enum("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+\pipe\netdfs commands
+********************************************************************/
+static struct api_struct api_netdfs_cmds[] =
+{
+ {"DFS_EXIST", DFS_EXIST, api_dfs_exist },
+ {"DFS_ADD", DFS_ADD, api_dfs_add },
+ {"DFS_REMOVE", DFS_REMOVE, api_dfs_remove },
+ {"DFS_GET_INFO", DFS_GET_INFO, api_dfs_get_info },
+ {"DFS_ENUM", DFS_ENUM, api_dfs_enum }
+};
+
+void netdfs_get_pipe_fns( struct api_struct **fns, int *n_fns )
+{
+ *fns = api_netdfs_cmds;
+ *n_fns = sizeof(api_netdfs_cmds) / sizeof(struct api_struct);
+}
+
+NTSTATUS rpc_dfs_init(void)
+{
+ return rpc_pipe_register_commands(SMB_RPC_INTERFACE_VERSION, "netdfs", "netdfs", api_netdfs_cmds,
+ sizeof(api_netdfs_cmds) / sizeof(struct api_struct));
+}
diff --git a/source/rpc_server/srv_dfs_nt.c b/source/rpc_server/srv_dfs_nt.c
new file mode 100644
index 00000000000..a3b06bb6e10
--- /dev/null
+++ b/source/rpc_server/srv_dfs_nt.c
@@ -0,0 +1,377 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines for Dfs
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Shirish Kalele 2000.
+ * Copyright (C) Jeremy Allison 2001.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This is the implementation of the dfs pipe. */
+
+#include "includes.h"
+#include "nterr.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#define MAX_MSDFS_JUNCTIONS 256
+
+/* This function does not return a WERROR or NTSTATUS code but rather 1 if
+ dfs exists, or 0 otherwise. */
+
+uint32 _dfs_exist(pipes_struct *p, DFS_Q_DFS_EXIST *q_u, DFS_R_DFS_EXIST *r_u)
+{
+ if(lp_host_msdfs())
+ return 1;
+ else
+ return 0;
+}
+
+WERROR _dfs_add(pipes_struct *p, DFS_Q_DFS_ADD* q_u, DFS_R_DFS_ADD *r_u)
+{
+ struct current_user user;
+ struct junction_map jn;
+ struct referral* old_referral_list = NULL;
+ BOOL exists = False;
+
+ pstring dfspath, servername, sharename;
+ pstring altpath;
+
+ get_current_user(&user,p);
+
+ if (user.uid != 0) {
+ DEBUG(10,("_dfs_add: uid != 0. Access denied.\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ unistr2_to_ascii(dfspath, &q_u->DfsEntryPath, sizeof(dfspath)-1);
+ unistr2_to_ascii(servername, &q_u->ServerName, sizeof(servername)-1);
+ unistr2_to_ascii(sharename, &q_u->ShareName, sizeof(sharename)-1);
+
+ DEBUG(5,("init_reply_dfs_add: Request to add %s -> %s\\%s.\n",
+ dfspath, servername, sharename));
+
+ pstrcpy(altpath, servername);
+ pstrcat(altpath, "\\");
+ pstrcat(altpath, sharename);
+
+ /* The following call can change the cwd. */
+ if(get_referred_path(dfspath, &jn, NULL, NULL)) {
+ exists = True;
+ jn.referral_count += 1;
+ old_referral_list = jn.referral_list;
+ } else {
+ jn.referral_count = 1;
+ }
+
+ vfs_ChDir(p->conn,p->conn->connectpath);
+
+ jn.referral_list = (struct referral*) talloc(p->mem_ctx, jn.referral_count
+ * sizeof(struct referral));
+
+ if(jn.referral_list == NULL) {
+ DEBUG(0,("init_reply_dfs_add: talloc failed for referral list!\n"));
+ return WERR_DFS_INTERNAL_ERROR;
+ }
+
+ if(old_referral_list) {
+ memcpy(jn.referral_list, old_referral_list, sizeof(struct referral)*jn.referral_count-1);
+ SAFE_FREE(old_referral_list);
+ }
+
+ jn.referral_list[jn.referral_count-1].proximity = 0;
+ jn.referral_list[jn.referral_count-1].ttl = REFERRAL_TTL;
+
+ pstrcpy(jn.referral_list[jn.referral_count-1].alternate_path, altpath);
+
+ if(!create_msdfs_link(&jn, exists)) {
+ vfs_ChDir(p->conn,p->conn->connectpath);
+ return WERR_DFS_CANT_CREATE_JUNCT;
+ }
+ vfs_ChDir(p->conn,p->conn->connectpath);
+
+ return WERR_OK;
+}
+
+WERROR _dfs_remove(pipes_struct *p, DFS_Q_DFS_REMOVE *q_u,
+ DFS_R_DFS_REMOVE *r_u)
+{
+ struct current_user user;
+ struct junction_map jn;
+ BOOL found = False;
+
+ pstring dfspath, servername, sharename;
+ pstring altpath;
+
+ get_current_user(&user,p);
+
+ if (user.uid != 0) {
+ DEBUG(10,("_dfs_remove: uid != 0. Access denied.\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ unistr2_to_ascii(dfspath, &q_u->DfsEntryPath, sizeof(dfspath)-1);
+ if(q_u->ptr_ServerName) {
+ unistr2_to_ascii(servername, &q_u->ServerName, sizeof(servername)-1);
+ }
+
+ if(q_u->ptr_ShareName) {
+ unistr2_to_ascii(sharename, &q_u->ShareName, sizeof(sharename)-1);
+ }
+
+ if(q_u->ptr_ServerName && q_u->ptr_ShareName) {
+ pstrcpy(altpath, servername);
+ pstrcat(altpath, "\\");
+ pstrcat(altpath, sharename);
+ strlower_m(altpath);
+ }
+
+ DEBUG(5,("init_reply_dfs_remove: Request to remove %s -> %s\\%s.\n",
+ dfspath, servername, sharename));
+
+ if(!get_referred_path(dfspath, &jn, NULL, NULL)) {
+ return WERR_DFS_NO_SUCH_VOL;
+ }
+
+ /* if no server-share pair given, remove the msdfs link completely */
+ if(!q_u->ptr_ServerName && !q_u->ptr_ShareName) {
+ if(!remove_msdfs_link(&jn)) {
+ vfs_ChDir(p->conn,p->conn->connectpath);
+ return WERR_DFS_NO_SUCH_VOL;
+ }
+ vfs_ChDir(p->conn,p->conn->connectpath);
+ } else {
+ int i=0;
+ /* compare each referral in the list with the one to remove */
+ DEBUG(10,("altpath: .%s. refcnt: %d\n", altpath, jn.referral_count));
+ for(i=0;i<jn.referral_count;i++) {
+ pstring refpath;
+ pstrcpy(refpath,jn.referral_list[i].alternate_path);
+ trim_char(refpath, '\\', '\\');
+ DEBUG(10,("_dfs_remove: refpath: .%s.\n", refpath));
+ if(strequal(refpath, altpath)) {
+ *(jn.referral_list[i].alternate_path)='\0';
+ DEBUG(10,("_dfs_remove: Removal request matches referral %s\n",
+ refpath));
+ found = True;
+ }
+ }
+
+ if(!found) {
+ return WERR_DFS_NO_SUCH_SHARE;
+ }
+
+ /* Only one referral, remove it */
+ if(jn.referral_count == 1) {
+ if(!remove_msdfs_link(&jn)) {
+ vfs_ChDir(p->conn,p->conn->connectpath);
+ return WERR_DFS_NO_SUCH_VOL;
+ }
+ } else {
+ if(!create_msdfs_link(&jn, True)) {
+ vfs_ChDir(p->conn,p->conn->connectpath);
+ return WERR_DFS_CANT_CREATE_JUNCT;
+ }
+ }
+ vfs_ChDir(p->conn,p->conn->connectpath);
+ }
+
+ return WERR_OK;
+}
+
+static BOOL init_reply_dfs_info_1(struct junction_map* j, DFS_INFO_1* dfs1, int num_j)
+{
+ int i=0;
+ for(i=0;i<num_j;i++) {
+ pstring str;
+ dfs1[i].ptr_entrypath = 1;
+ slprintf(str, sizeof(pstring)-1, "\\\\%s\\%s\\%s", global_myname(),
+ j[i].service_name, j[i].volume_name);
+ DEBUG(5,("init_reply_dfs_info_1: %d) initing entrypath: %s\n",i,str));
+ init_unistr2(&dfs1[i].entrypath,str,UNI_STR_TERMINATE);
+ }
+ return True;
+}
+
+static BOOL init_reply_dfs_info_2(struct junction_map* j, DFS_INFO_2* dfs2, int num_j)
+{
+ int i=0;
+ for(i=0;i<num_j;i++) {
+ pstring str;
+ dfs2[i].ptr_entrypath = 1;
+ slprintf(str, sizeof(pstring)-1, "\\\\%s\\%s\\%s", global_myname(),
+ j[i].service_name, j[i].volume_name);
+ init_unistr2(&dfs2[i].entrypath, str, UNI_STR_TERMINATE);
+ dfs2[i].ptr_comment = 0;
+ dfs2[i].state = 1; /* set up state of dfs junction as OK */
+ dfs2[i].num_storages = j[i].referral_count;
+ }
+ return True;
+}
+
+static BOOL init_reply_dfs_info_3(TALLOC_CTX *ctx, struct junction_map* j, DFS_INFO_3* dfs3, int num_j)
+{
+ int i=0,ii=0;
+ for(i=0;i<num_j;i++) {
+ pstring str;
+ dfs3[i].ptr_entrypath = 1;
+ if (j[i].volume_name[0] == '\0')
+ slprintf(str, sizeof(pstring)-1, "\\\\%s\\%s",
+ global_myname(), j[i].service_name);
+ else
+ slprintf(str, sizeof(pstring)-1, "\\\\%s\\%s\\%s", global_myname(),
+ j[i].service_name, j[i].volume_name);
+
+ init_unistr2(&dfs3[i].entrypath, str, UNI_STR_TERMINATE);
+ dfs3[i].ptr_comment = 1;
+ init_unistr2(&dfs3[i].comment, "", UNI_STR_TERMINATE);
+ dfs3[i].state = 1;
+ dfs3[i].num_storages = dfs3[i].num_storage_infos = j[i].referral_count;
+ dfs3[i].ptr_storages = 1;
+
+ /* also enumerate the storages */
+ dfs3[i].storages = (DFS_STORAGE_INFO*) talloc(ctx, j[i].referral_count *
+ sizeof(DFS_STORAGE_INFO));
+ if (!dfs3[i].storages)
+ return False;
+
+ memset(dfs3[i].storages, '\0', j[i].referral_count * sizeof(DFS_STORAGE_INFO));
+
+ for(ii=0;ii<j[i].referral_count;ii++) {
+ char* p;
+ pstring path;
+ DFS_STORAGE_INFO* stor = &(dfs3[i].storages[ii]);
+ struct referral* ref = &(j[i].referral_list[ii]);
+
+ pstrcpy(path, ref->alternate_path);
+ trim_char(path,'\\','\0');
+ p = strrchr_m(path,'\\');
+ if(p==NULL) {
+ DEBUG(4,("init_reply_dfs_info_3: invalid path: no \\ found in %s\n",path));
+ continue;
+ }
+ *p = '\0';
+ DEBUG(5,("storage %d: %s.%s\n",ii,path,p+1));
+ stor->state = 2; /* set all storages as ONLINE */
+ init_unistr2(&stor->servername, path, UNI_STR_TERMINATE);
+ init_unistr2(&stor->sharename, p+1, UNI_STR_TERMINATE);
+ stor->ptr_servername = stor->ptr_sharename = 1;
+ }
+ }
+ return True;
+}
+
+static WERROR init_reply_dfs_ctr(TALLOC_CTX *ctx, uint32 level,
+ DFS_INFO_CTR* ctr, struct junction_map* jn,
+ int num_jn)
+{
+ /* do the levels */
+ switch(level) {
+ case 1:
+ {
+ DFS_INFO_1* dfs1;
+ dfs1 = (DFS_INFO_1*) talloc(ctx, num_jn * sizeof(DFS_INFO_1));
+ if (!dfs1)
+ return WERR_NOMEM;
+ init_reply_dfs_info_1(jn, dfs1, num_jn);
+ ctr->dfs.info1 = dfs1;
+ break;
+ }
+ case 2:
+ {
+ DFS_INFO_2* dfs2;
+ dfs2 = (DFS_INFO_2*) talloc(ctx, num_jn * sizeof(DFS_INFO_2));
+ if (!dfs2)
+ return WERR_NOMEM;
+ init_reply_dfs_info_2(jn, dfs2, num_jn);
+ ctr->dfs.info2 = dfs2;
+ break;
+ }
+ case 3:
+ {
+ DFS_INFO_3* dfs3;
+ dfs3 = (DFS_INFO_3*) talloc(ctx, num_jn * sizeof(DFS_INFO_3));
+ if (!dfs3)
+ return WERR_NOMEM;
+ init_reply_dfs_info_3(ctx, jn, dfs3, num_jn);
+ ctr->dfs.info3 = dfs3;
+ break;
+ }
+ default:
+ return WERR_INVALID_PARAM;
+ }
+ return WERR_OK;
+}
+
+WERROR _dfs_enum(pipes_struct *p, DFS_Q_DFS_ENUM *q_u, DFS_R_DFS_ENUM *r_u)
+{
+ uint32 level = q_u->level;
+ struct junction_map jn[MAX_MSDFS_JUNCTIONS];
+ int num_jn = 0;
+
+ num_jn = enum_msdfs_links(jn);
+ vfs_ChDir(p->conn,p->conn->connectpath);
+
+ DEBUG(5,("make_reply_dfs_enum: %d junctions found in Dfs, doing level %d\n", num_jn, level));
+
+ r_u->ptr_buffer = level;
+ r_u->level = r_u->level2 = level;
+ r_u->ptr_num_entries = r_u->ptr_num_entries2 = 1;
+ r_u->num_entries = r_u->num_entries2 = num_jn;
+ r_u->reshnd.ptr_hnd = 1;
+ r_u->reshnd.handle = num_jn;
+
+ r_u->ctr = (DFS_INFO_CTR*)talloc(p->mem_ctx, sizeof(DFS_INFO_CTR));
+ if (!r_u->ctr)
+ return WERR_NOMEM;
+ ZERO_STRUCTP(r_u->ctr);
+ r_u->ctr->switch_value = level;
+ r_u->ctr->num_entries = num_jn;
+ r_u->ctr->ptr_dfs_ctr = 1;
+
+ r_u->status = init_reply_dfs_ctr(p->mem_ctx, level, r_u->ctr, jn, num_jn);
+
+ return r_u->status;
+}
+
+WERROR _dfs_get_info(pipes_struct *p, DFS_Q_DFS_GET_INFO *q_u,
+ DFS_R_DFS_GET_INFO *r_u)
+{
+ UNISTR2* uni_path = &q_u->uni_path;
+ uint32 level = q_u->level;
+ int consumedcnt = sizeof(pstring);
+ pstring path;
+ struct junction_map jn;
+
+ unistr2_to_ascii(path, uni_path, sizeof(path)-1);
+ if(!create_junction(path, &jn))
+ return WERR_DFS_NO_SUCH_SERVER;
+
+ /* The following call can change the cwd. */
+ if(!get_referred_path(path, &jn, &consumedcnt, NULL) || consumedcnt < strlen(path)) {
+ vfs_ChDir(p->conn,p->conn->connectpath);
+ return WERR_DFS_NO_SUCH_VOL;
+ }
+
+ vfs_ChDir(p->conn,p->conn->connectpath);
+ r_u->level = level;
+ r_u->ptr_ctr = 1;
+ r_u->status = init_reply_dfs_ctr(p->mem_ctx, level, &r_u->ctr, &jn, 1);
+
+ return r_u->status;
+}
diff --git a/source/rpc_server/srv_echo.c b/source/rpc_server/srv_echo.c
new file mode 100644
index 00000000000..c6cfde07c15
--- /dev/null
+++ b/source/rpc_server/srv_echo.c
@@ -0,0 +1,150 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines for rpcecho
+ * Copyright (C) Tim Potter 2003.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This is the interface to the rpcecho pipe. */
+
+#include "includes.h"
+#include "nterr.h"
+
+#ifdef DEVELOPER
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+static BOOL api_add_one(pipes_struct *p)
+{
+ ECHO_Q_ADD_ONE q_u;
+ ECHO_R_ADD_ONE r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!echo_io_q_add_one("", &q_u, data, 0))
+ return False;
+
+ _echo_add_one(p, &q_u, &r_u);
+
+ if(!echo_io_r_add_one("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+static BOOL api_echo_data(pipes_struct *p)
+{
+ ECHO_Q_ECHO_DATA q_u;
+ ECHO_R_ECHO_DATA r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!echo_io_q_echo_data("", &q_u, data, 0))
+ return False;
+
+ _echo_data(p, &q_u, &r_u);
+
+ if(!echo_io_r_echo_data("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+static BOOL api_source_data(pipes_struct *p)
+{
+ ECHO_Q_SOURCE_DATA q_u;
+ ECHO_R_SOURCE_DATA r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!echo_io_q_source_data("", &q_u, data, 0))
+ return False;
+
+ _source_data(p, &q_u, &r_u);
+
+ if(!echo_io_r_source_data("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+static BOOL api_sink_data(pipes_struct *p)
+{
+ ECHO_Q_SINK_DATA q_u;
+ ECHO_R_SINK_DATA r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!echo_io_q_sink_data("", &q_u, data, 0))
+ return False;
+
+ _sink_data(p, &q_u, &r_u);
+
+ if(!echo_io_r_sink_data("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+\pipe\rpcecho commands
+********************************************************************/
+
+struct api_struct api_echo_cmds[] = {
+ {"ADD_ONE", ECHO_ADD_ONE, api_add_one },
+ {"ECHO_DATA", ECHO_DATA, api_echo_data },
+ {"SOURCE_DATA", ECHO_SOURCE_DATA, api_source_data },
+ {"SINK_DATA", ECHO_SINK_DATA, api_sink_data },
+};
+
+
+void echo_get_pipe_fns( struct api_struct **fns, int *n_fns )
+{
+ *fns = api_echo_cmds;
+ *n_fns = sizeof(api_echo_cmds) / sizeof(struct api_struct);
+}
+
+NTSTATUS rpc_echo_init(void)
+{
+ return rpc_pipe_register_commands(SMB_RPC_INTERFACE_VERSION,
+ "rpcecho", "rpcecho", api_echo_cmds,
+ sizeof(api_echo_cmds) / sizeof(struct api_struct));
+}
+
+#else /* DEVELOPER */
+
+NTSTATUS rpc_echo_init(void)
+{
+ return NT_STATUS_OK;
+}
+#endif /* DEVELOPER */
diff --git a/source/rpc_server/srv_echo_nt.c b/source/rpc_server/srv_echo_nt.c
new file mode 100644
index 00000000000..ddb76b3a21e
--- /dev/null
+++ b/source/rpc_server/srv_echo_nt.c
@@ -0,0 +1,78 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines for rpcecho
+ * Copyright (C) Tim Potter 2003.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This is the interface to the rpcecho pipe. */
+
+#include "includes.h"
+#include "nterr.h"
+
+#ifdef DEVELOPER
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/* Add one to the input and return it */
+
+void _echo_add_one(pipes_struct *p, ECHO_Q_ADD_ONE *q_u, ECHO_R_ADD_ONE *r_u)
+{
+ DEBUG(10, ("_echo_add_one\n"));
+
+ r_u->response = q_u->request + 1;
+}
+
+/* Echo back an array of data */
+
+void _echo_data(pipes_struct *p, ECHO_Q_ECHO_DATA *q_u,
+ ECHO_R_ECHO_DATA *r_u)
+{
+ DEBUG(10, ("_echo_data\n"));
+
+ r_u->data = talloc(p->mem_ctx, q_u->size);
+ r_u->size = q_u->size;
+ memcpy(r_u->data, q_u->data, q_u->size);
+}
+
+/* Sink an array of data */
+
+void _sink_data(pipes_struct *p, ECHO_Q_SINK_DATA *q_u,
+ ECHO_R_SINK_DATA *r_u)
+{
+ DEBUG(10, ("_sink_data\n"));
+
+ /* My that was some yummy data! */
+}
+
+/* Source an array of data */
+
+void _source_data(pipes_struct *p, ECHO_Q_SOURCE_DATA *q_u,
+ ECHO_R_SOURCE_DATA *r_u)
+{
+ uint32 i;
+
+ DEBUG(10, ("_source_data\n"));
+
+ r_u->data = talloc(p->mem_ctx, q_u->size);
+ r_u->size = q_u->size;
+
+ for (i = 0; i < r_u->size; i++)
+ r_u->data[i] = i & 0xff;
+}
+
+#endif /* DEVELOPER */
diff --git a/source/rpc_server/srv_epmapper.c b/source/rpc_server/srv_epmapper.c
new file mode 100644
index 00000000000..70de092850b
--- /dev/null
+++ b/source/rpc_server/srv_epmapper.c
@@ -0,0 +1,88 @@
+
+/*
+ Unix SMB/CIFS implementation.
+ Samba end point mapper utility and mapping functions
+ Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*****************************************************************
+ api_handle_map_req - handles standard epm mapping request
+******************************************************************/
+static BOOL api_handle_map_req(pipes_struct * p)
+{
+
+ EPM_Q_MAP q_in;
+ EPM_R_MAP q_out;
+
+ prs_struct *in_data = &p->in_data.data;
+ prs_struct *ret_data = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_in);
+ ZERO_STRUCT(q_out);
+
+ /* process input request and parse packet */
+
+ if (!epm_io_q_map("", &q_in, in_data, 0)) {
+ DEBUG(0,
+ ("api_handle_map_request: unable to unmarshall EPMD_MAP\n"));
+ return False;
+ }
+
+ _epm_map(p, &q_in, &q_out);
+
+ if (!epm_io_r_map("", &q_out, ret_data, 0)) {
+ DEBUG(0,
+ ("api_handle_map_req: unable to marshall EPMD_MAP\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************/
+/* \pipe\epmapper commands */
+/*******************************************************************/
+/* opnum is 3 on map request */
+
+struct api_struct api_epmapper_cmds[] = {
+ {"MAP_PIPE_NAME", EPM_MAP_PIPE_NAME, api_handle_map_req},
+};
+
+/*******************************************************************/
+/* */
+/*******************************************************************/
+
+void epm_get_pipe_fns(struct api_struct **funcs, int *n_funcs)
+{
+ *funcs = api_epmapper_cmds;
+ *n_funcs = sizeof(api_epmapper_cmds) / sizeof(struct api_struct);
+}
+
+/*******************************************************************/
+/* */
+/*******************************************************************/
+
+NTSTATUS rpc_epmapper_init(void)
+{
+ return rpc_pipe_register_commands(SMB_RPC_INTERFACE_VERSION,
+ EPM_PIPE_NM, EPM_PIPE_NM,
+ api_epmapper_cmds,
+ sizeof(api_epmapper_cmds) /
+ sizeof(struct api_struct));
+}
diff --git a/source/rpc_server/srv_epmapper_nt.c b/source/rpc_server/srv_epmapper_nt.c
new file mode 100644
index 00000000000..e82484af4af
--- /dev/null
+++ b/source/rpc_server/srv_epmapper_nt.c
@@ -0,0 +1,70 @@
+
+/*
+ Unix SMB/CIFS implementation.
+ Samba end point mapper utility and mapping functions
+ Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*******************************************************************/
+/* _epm_map - fill out mapping on input and output structs */
+/*******************************************************************/
+void _epm_map(pipes_struct *ps, const EPM_Q_MAP *q_u, EPM_R_MAP *r_u)
+{
+ int i;
+ uint8 target_address[] = { 9, 53, 95, 27 };
+ EPM_FLOOR *floors = talloc(ps->mem_ctx, sizeof(EPM_FLOOR) *
+ q_u->tower->num_floors);
+ EPM_TOWER *towers = talloc(ps->mem_ctx,
+ sizeof(EPM_TOWER) * MAX_TOWERS);
+ EPM_TOWER_ARRAY array;
+
+ if (!floors || !towers) {
+ DEBUG(0, ("_epm_map: talloc failed!\n"));
+ return;
+ }
+
+ for (i = 0; i < q_u->tower->num_floors; i++) {
+ switch (q_u->tower->floors[i].lhs.protocol) {
+ case EPM_FLOOR_UUID:
+ init_epm_floor_uuid(&floors[i],
+ q_u->tower->floors[i].
+ lhs.uuid.uuid,
+ q_u->tower->floors[i].
+ lhs.uuid.version);
+ break;
+ case EPM_FLOOR_RPC:
+ init_epm_floor_rpc(&floors[i]);
+ break;
+ case EPM_FLOOR_TCP:
+ /* for now map all requests to port 135 */
+ init_epm_floor_tcp(&floors[i], 135);
+ break;
+ case EPM_FLOOR_IP:
+ init_epm_floor_ip(&floors[i], target_address);
+ break;
+ }
+ }
+
+ init_epm_tower(ps->mem_ctx, &towers[0], floors, 5);
+ init_epm_tower_array(ps->mem_ctx, &array, towers, 1);
+ init_epm_r_map(ps->mem_ctx, r_u, &q_u->term_handle, &array, 1, 0);
+
+ return;
+
+}
diff --git a/source/rpc_server/srv_lsa.c b/source/rpc_server/srv_lsa.c
new file mode 100644
index 00000000000..63e74ec8911
--- /dev/null
+++ b/source/rpc_server/srv_lsa.c
@@ -0,0 +1,734 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997,
+ * Copyright (C) Jeremy Allison 2001,
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002-2003.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This is the interface to the lsa server code. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/***************************************************************************
+ api_lsa_open_policy2
+ ***************************************************************************/
+
+static BOOL api_lsa_open_policy2(pipes_struct *p)
+{
+ LSA_Q_OPEN_POL2 q_u;
+ LSA_R_OPEN_POL2 r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the server, object attributes and desired access flag...*/
+ if(!lsa_io_q_open_pol2("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_open_policy2: unable to unmarshall LSA_Q_OPEN_POL2.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_open_policy2(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_open_pol2("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_open_policy2: unable to marshall LSA_R_OPEN_POL2.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+api_lsa_open_policy
+ ***************************************************************************/
+
+static BOOL api_lsa_open_policy(pipes_struct *p)
+{
+ LSA_Q_OPEN_POL q_u;
+ LSA_R_OPEN_POL r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the server, object attributes and desired access flag...*/
+ if(!lsa_io_q_open_pol("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_open_policy: unable to unmarshall LSA_Q_OPEN_POL.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_open_policy(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_open_pol("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_open_policy: unable to marshall LSA_R_OPEN_POL.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_enum_trust_dom
+ ***************************************************************************/
+
+static BOOL api_lsa_enum_trust_dom(pipes_struct *p)
+{
+ LSA_Q_ENUM_TRUST_DOM q_u;
+ LSA_R_ENUM_TRUST_DOM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the enum trust domain context etc. */
+ if(!lsa_io_q_enum_trust_dom("", &q_u, data, 0))
+ return False;
+
+ /* get required trusted domains information */
+ r_u.status = _lsa_enum_trust_dom(p, &q_u, &r_u);
+
+ /* prepare the response */
+ if(!lsa_io_r_enum_trust_dom("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_query_info
+ ***************************************************************************/
+
+static BOOL api_lsa_query_info(pipes_struct *p)
+{
+ LSA_Q_QUERY_INFO q_u;
+ LSA_R_QUERY_INFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the info class and policy handle */
+ if(!lsa_io_q_query("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_query_info: failed to unmarshall LSA_Q_QUERY_INFO.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_query_info(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_query("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_query_info: failed to marshall LSA_R_QUERY_INFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_lookup_sids
+ ***************************************************************************/
+
+static BOOL api_lsa_lookup_sids(pipes_struct *p)
+{
+ LSA_Q_LOOKUP_SIDS q_u;
+ LSA_R_LOOKUP_SIDS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the info class and policy handle */
+ if(!lsa_io_q_lookup_sids("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_lookup_sids: failed to unmarshall LSA_Q_LOOKUP_SIDS.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_lookup_sids(p, &q_u, &r_u);
+
+ if(!lsa_io_r_lookup_sids("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_lookup_sids: Failed to marshall LSA_R_LOOKUP_SIDS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_lookup_names
+ ***************************************************************************/
+
+static BOOL api_lsa_lookup_names(pipes_struct *p)
+{
+ LSA_Q_LOOKUP_NAMES q_u;
+ LSA_R_LOOKUP_NAMES r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the info class and policy handle */
+ if(!lsa_io_q_lookup_names("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_lookup_names: failed to unmarshall LSA_Q_LOOKUP_NAMES.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_lookup_names(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_lookup_names("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_lookup_names: Failed to marshall LSA_R_LOOKUP_NAMES.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_close.
+ ***************************************************************************/
+
+static BOOL api_lsa_close(pipes_struct *p)
+{
+ LSA_Q_CLOSE q_u;
+ LSA_R_CLOSE r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!lsa_io_q_close("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_close: lsa_io_q_close failed.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_close(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if (!lsa_io_r_close("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_close: lsa_io_r_close failed.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_open_secret.
+ ***************************************************************************/
+
+static BOOL api_lsa_open_secret(pipes_struct *p)
+{
+ LSA_Q_OPEN_SECRET q_u;
+ LSA_R_OPEN_SECRET r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_open_secret("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_open_secret: failed to unmarshall LSA_Q_OPEN_SECRET.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_open_secret(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_open_secret("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_open_secret: Failed to marshall LSA_R_OPEN_SECRET.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_open_secret.
+ ***************************************************************************/
+
+static BOOL api_lsa_enum_privs(pipes_struct *p)
+{
+ LSA_Q_ENUM_PRIVS q_u;
+ LSA_R_ENUM_PRIVS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_enum_privs("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_enum_privs: failed to unmarshall LSA_Q_ENUM_PRIVS.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_enum_privs(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_enum_privs("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_enum_privs: Failed to marshall LSA_R_ENUM_PRIVS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_open_secret.
+ ***************************************************************************/
+
+static BOOL api_lsa_priv_get_dispname(pipes_struct *p)
+{
+ LSA_Q_PRIV_GET_DISPNAME q_u;
+ LSA_R_PRIV_GET_DISPNAME r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_priv_get_dispname("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_priv_get_dispname: failed to unmarshall LSA_Q_PRIV_GET_DISPNAME.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_priv_get_dispname(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_priv_get_dispname("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_priv_get_dispname: Failed to marshall LSA_R_PRIV_GET_DISPNAME.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_open_secret.
+ ***************************************************************************/
+
+static BOOL api_lsa_enum_accounts(pipes_struct *p)
+{
+ LSA_Q_ENUM_ACCOUNTS q_u;
+ LSA_R_ENUM_ACCOUNTS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_enum_accounts("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_enum_accounts: failed to unmarshall LSA_Q_ENUM_ACCOUNTS.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_enum_accounts(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_enum_accounts("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_enum_accounts: Failed to marshall LSA_R_ENUM_ACCOUNTS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_UNK_GET_CONNUSER
+ ***************************************************************************/
+
+static BOOL api_lsa_unk_get_connuser(pipes_struct *p)
+{
+ LSA_Q_UNK_GET_CONNUSER q_u;
+ LSA_R_UNK_GET_CONNUSER r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_unk_get_connuser("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_unk_get_connuser: failed to unmarshall LSA_Q_UNK_GET_CONNUSER.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_unk_get_connuser(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_unk_get_connuser("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_unk_get_connuser: Failed to marshall LSA_R_UNK_GET_CONNUSER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_create_user
+ ***************************************************************************/
+
+static BOOL api_lsa_create_account(pipes_struct *p)
+{
+ LSA_Q_CREATEACCOUNT q_u;
+ LSA_R_CREATEACCOUNT r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_create_account("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_create_account: failed to unmarshall LSA_Q_CREATEACCOUNT.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_create_account(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_create_account("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_create_account: Failed to marshall LSA_R_CREATEACCOUNT.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_open_user
+ ***************************************************************************/
+
+static BOOL api_lsa_open_account(pipes_struct *p)
+{
+ LSA_Q_OPENACCOUNT q_u;
+ LSA_R_OPENACCOUNT r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_open_account("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_open_account: failed to unmarshall LSA_Q_OPENACCOUNT.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_open_account(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_open_account("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_open_account: Failed to marshall LSA_R_OPENACCOUNT.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_get_privs
+ ***************************************************************************/
+
+static BOOL api_lsa_enum_privsaccount(pipes_struct *p)
+{
+ LSA_Q_ENUMPRIVSACCOUNT q_u;
+ LSA_R_ENUMPRIVSACCOUNT r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_enum_privsaccount("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_enum_privsaccount: failed to unmarshall LSA_Q_ENUMPRIVSACCOUNT.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_enum_privsaccount(p, rdata, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_enum_privsaccount("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_enum_privsaccount: Failed to marshall LSA_R_ENUMPRIVSACCOUNT.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_getsystemaccount
+ ***************************************************************************/
+
+static BOOL api_lsa_getsystemaccount(pipes_struct *p)
+{
+ LSA_Q_GETSYSTEMACCOUNT q_u;
+ LSA_R_GETSYSTEMACCOUNT r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_getsystemaccount("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_getsystemaccount: failed to unmarshall LSA_Q_GETSYSTEMACCOUNT.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_getsystemaccount(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_getsystemaccount("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_getsystemaccount: Failed to marshall LSA_R_GETSYSTEMACCOUNT.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/***************************************************************************
+ api_lsa_setsystemaccount
+ ***************************************************************************/
+
+static BOOL api_lsa_setsystemaccount(pipes_struct *p)
+{
+ LSA_Q_SETSYSTEMACCOUNT q_u;
+ LSA_R_SETSYSTEMACCOUNT r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_setsystemaccount("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_setsystemaccount: failed to unmarshall LSA_Q_SETSYSTEMACCOUNT.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_setsystemaccount(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_setsystemaccount("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_setsystemaccount: Failed to marshall LSA_R_SETSYSTEMACCOUNT.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_addprivs
+ ***************************************************************************/
+
+static BOOL api_lsa_addprivs(pipes_struct *p)
+{
+ LSA_Q_ADDPRIVS q_u;
+ LSA_R_ADDPRIVS r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_addprivs("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_addprivs: failed to unmarshall LSA_Q_ADDPRIVS.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_addprivs(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_addprivs("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_addprivs: Failed to marshall LSA_R_ADDPRIVS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_removeprivs
+ ***************************************************************************/
+
+static BOOL api_lsa_removeprivs(pipes_struct *p)
+{
+ LSA_Q_REMOVEPRIVS q_u;
+ LSA_R_REMOVEPRIVS r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_removeprivs("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_removeprivs: failed to unmarshall LSA_Q_REMOVEPRIVS.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_removeprivs(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_removeprivs("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_removeprivs: Failed to marshall LSA_R_REMOVEPRIVS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_query_secobj
+ ***************************************************************************/
+
+static BOOL api_lsa_query_secobj(pipes_struct *p)
+{
+ LSA_Q_QUERY_SEC_OBJ q_u;
+ LSA_R_QUERY_SEC_OBJ r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_query_sec_obj("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_query_secobj: failed to unmarshall LSA_Q_QUERY_SEC_OBJ.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_query_secobj(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_query_sec_obj("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_query_secobj: Failed to marshall LSA_R_QUERY_SEC_OBJ.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_query_dnsdomainfo
+ ***************************************************************************/
+
+static BOOL api_lsa_query_info2(pipes_struct *p)
+{
+ LSA_Q_QUERY_INFO2 q_u;
+ LSA_R_QUERY_INFO2 r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_query_info2("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_query_info2: failed to unmarshall LSA_Q_QUERY_INFO2.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_query_info2(p, &q_u, &r_u);
+
+ if (!lsa_io_r_query_info2("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_query_info2: failed to marshall LSA_R_QUERY_INFO2.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/***************************************************************************
+ \PIPE\ntlsa commands
+ ***************************************************************************/
+
+static struct api_struct api_lsa_cmds[] =
+{
+ { "LSA_OPENPOLICY2" , LSA_OPENPOLICY2 , api_lsa_open_policy2 },
+ { "LSA_OPENPOLICY" , LSA_OPENPOLICY , api_lsa_open_policy },
+ { "LSA_QUERYINFOPOLICY" , LSA_QUERYINFOPOLICY , api_lsa_query_info },
+ { "LSA_ENUMTRUSTDOM" , LSA_ENUMTRUSTDOM , api_lsa_enum_trust_dom },
+ { "LSA_CLOSE" , LSA_CLOSE , api_lsa_close },
+ { "LSA_OPENSECRET" , LSA_OPENSECRET , api_lsa_open_secret },
+ { "LSA_LOOKUPSIDS" , LSA_LOOKUPSIDS , api_lsa_lookup_sids },
+ { "LSA_LOOKUPNAMES" , LSA_LOOKUPNAMES , api_lsa_lookup_names },
+ { "LSA_ENUM_PRIVS" , LSA_ENUM_PRIVS , api_lsa_enum_privs },
+ { "LSA_PRIV_GET_DISPNAME",LSA_PRIV_GET_DISPNAME,api_lsa_priv_get_dispname},
+ { "LSA_ENUM_ACCOUNTS" , LSA_ENUM_ACCOUNTS , api_lsa_enum_accounts },
+ { "LSA_UNK_GET_CONNUSER", LSA_UNK_GET_CONNUSER, api_lsa_unk_get_connuser },
+ { "LSA_CREATEACCOUNT" , LSA_CREATEACCOUNT , api_lsa_create_account },
+ { "LSA_OPENACCOUNT" , LSA_OPENACCOUNT , api_lsa_open_account },
+ { "LSA_ENUMPRIVSACCOUNT", LSA_ENUMPRIVSACCOUNT, api_lsa_enum_privsaccount},
+ { "LSA_GETSYSTEMACCOUNT", LSA_GETSYSTEMACCOUNT, api_lsa_getsystemaccount },
+ { "LSA_SETSYSTEMACCOUNT", LSA_SETSYSTEMACCOUNT, api_lsa_setsystemaccount },
+ { "LSA_ADDPRIVS" , LSA_ADDPRIVS , api_lsa_addprivs },
+ { "LSA_REMOVEPRIVS" , LSA_REMOVEPRIVS , api_lsa_removeprivs },
+ { "LSA_QUERYSECOBJ" , LSA_QUERYSECOBJ , api_lsa_query_secobj },
+ /* be careful of the adding of new RPC's. See commentrs below about
+ ADS DC capabilities */
+ { "LSA_QUERYINFO2" , LSA_QUERYINFO2 , api_lsa_query_info2 }
+};
+
+static int count_fns(void)
+{
+ int funcs = sizeof(api_lsa_cmds) / sizeof(struct api_struct);
+
+ /*
+ * NOTE: Certain calls can not be enabled if we aren't an ADS DC. Make sure
+ * these calls are always last and that you decrement by the amount of calls
+ * to disable.
+ */
+ if (!(SEC_ADS == lp_security() && ROLE_DOMAIN_PDC == lp_server_role())) {
+ funcs -= 1;
+ }
+
+ return funcs;
+}
+void lsa_get_pipe_fns( struct api_struct **fns, int *n_fns )
+{
+ *fns = api_lsa_cmds;
+ *n_fns = count_fns();
+}
+
+
+NTSTATUS rpc_lsa_init(void)
+{
+ int funcs = count_fns();
+
+ return rpc_pipe_register_commands(SMB_RPC_INTERFACE_VERSION, "lsarpc", "lsass", api_lsa_cmds,
+ funcs);
+}
diff --git a/source/rpc_server/srv_lsa_ds.c b/source/rpc_server/srv_lsa_ds.c
new file mode 100644
index 00000000000..1e75175c2cd
--- /dev/null
+++ b/source/rpc_server/srv_lsa_ds.c
@@ -0,0 +1,93 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Gerald Carter 2003
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This is the interface for the registry functions. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#if 0 /* disabled */
+/*******************************************************************
+ api_reg_open_entry
+ ********************************************************************/
+
+static BOOL api_dsrole_get_primary_dominfo(pipes_struct *p)
+{
+ DS_Q_GETPRIMDOMINFO q_u;
+ DS_R_GETPRIMDOMINFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the request */
+ if ( !ds_io_q_getprimdominfo("", data, 0, &q_u) )
+ return False;
+
+ /* construct reply. */
+ r_u.status = _dsrole_get_primary_dominfo( p, &q_u, &r_u );
+
+ if ( !ds_io_r_getprimdominfo("", rdata, 0, &r_u) )
+ return False;
+
+ return True;
+}
+#endif
+
+/*******************************************************************
+ stub functions for unimplemented RPC
+*******************************************************************/
+
+static BOOL api_dsrole_stub( pipes_struct *p )
+{
+ DEBUG(0,("api_dsrole_stub: Hmmm....didn't know this RPC existed...\n"));
+
+ return False;
+}
+
+
+/*******************************************************************
+ array of \PIPE\lsass (new windows 2000 UUID) operations
+********************************************************************/
+static struct api_struct api_lsa_ds_cmds[] = {
+ { "DS_NOP", DS_NOP, api_dsrole_stub }
+
+#if 0 /* disabled due to breakage with viewing domain users and groups
+ on a Samba PDC from win2k clients --jerry CIFS 2003 */
+ { "DS_GETPRIMDOMINFO", DS_GETPRIMDOMINFO, api_dsrole_get_primary_dominfo }
+#endif
+
+};
+
+void lsa_ds_get_pipe_fns( struct api_struct **fns, int *n_fns )
+{
+ *fns = api_lsa_ds_cmds;
+ *n_fns = sizeof(api_lsa_ds_cmds) / sizeof(struct api_struct);
+}
+
+
+NTSTATUS rpc_lsa_ds_init(void)
+{
+ return rpc_pipe_register_commands(SMB_RPC_INTERFACE_VERSION, "lsa_ds", "lsa_ds", api_lsa_ds_cmds,
+ sizeof(api_lsa_ds_cmds) / sizeof(struct api_struct));
+}
diff --git a/source/rpc_server/srv_lsa_ds_nt.c b/source/rpc_server/srv_lsa_ds_nt.c
new file mode 100644
index 00000000000..f6e8eed9a97
--- /dev/null
+++ b/source/rpc_server/srv_lsa_ds_nt.c
@@ -0,0 +1,127 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997.
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997.
+ * Copyright (C) Paul Ashton 1997.
+ * Copyright (C) Jeremy Allison 2001.
+ * Copyright (C) Gerald Carter 2002.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Implementation of registry functions. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/********************************************************************
+ Fill in a DS_DOMINFO_CTR structure
+ ********************************************************************/
+
+static NTSTATUS fill_dsrole_dominfo_basic(TALLOC_CTX *ctx, DSROLE_PRIMARY_DOMAIN_INFO_BASIC **info)
+{
+ DSROLE_PRIMARY_DOMAIN_INFO_BASIC *basic;
+ const char *netbios_domain;
+ fstring dnsdomain;
+
+ DEBUG(10,("fill_dsrole_dominfo_basic: enter\n"));
+
+ if ( !(basic = talloc_zero(ctx, sizeof(DSROLE_PRIMARY_DOMAIN_INFO_BASIC))) ) {
+ DEBUG(0,("fill_dsrole_dominfo_basic: FATAL error! talloc_xero() failed\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch ( lp_server_role() ) {
+ case ROLE_STANDALONE:
+ basic->machine_role = DSROLE_STANDALONE_SRV;
+ break;
+ case ROLE_DOMAIN_MEMBER:
+ basic->machine_role = DSROLE_DOMAIN_MEMBER_SRV;
+ break;
+ case ROLE_DOMAIN_BDC:
+ basic->machine_role = DSROLE_BDC;
+ basic->flags = DSROLE_PRIMARY_DS_RUNNING|DSROLE_PRIMARY_DS_MIXED_MODE;
+ if ( secrets_fetch_domain_guid( lp_workgroup(), &basic->domain_guid ) )
+ basic->flags |= DSROLE_PRIMARY_DOMAIN_GUID_PRESENT;
+ get_mydnsdomname(dnsdomain);
+ strlower_m(dnsdomain);
+ break;
+ case ROLE_DOMAIN_PDC:
+ basic->machine_role = DSROLE_PDC;
+ basic->flags = DSROLE_PRIMARY_DS_RUNNING|DSROLE_PRIMARY_DS_MIXED_MODE;
+ if ( secrets_fetch_domain_guid( lp_workgroup(), &basic->domain_guid ) )
+ basic->flags |= DSROLE_PRIMARY_DOMAIN_GUID_PRESENT;
+ get_mydnsdomname(dnsdomain);
+ strlower_m(dnsdomain);
+ break;
+ }
+
+ basic->unknown = 0x6173; /* seen on the wire; maybe padding */
+
+ /* always set netbios name */
+
+ basic->netbios_ptr = 1;
+ netbios_domain = get_global_sam_name();
+ init_unistr2( &basic->netbios_domain, netbios_domain, UNI_FLAGS_NONE);
+
+ basic->dnsname_ptr = 1;
+ init_unistr2( &basic->dns_domain, dnsdomain, UNI_FLAGS_NONE);
+ basic->forestname_ptr = 1;
+ init_unistr2( &basic->forest_domain, dnsdomain, UNI_FLAGS_NONE);
+
+
+ /* fill in some additional fields if we are a member of an AD domain */
+
+ if ( lp_security() == SEC_ADS ) {
+ /* TODO */
+ ;;
+ }
+
+ *info = basic;
+
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+ Implement the DsroleGetPrimaryDomainInfo() call
+ ********************************************************************/
+
+NTSTATUS _dsrole_get_primary_dominfo(pipes_struct *p, DS_Q_GETPRIMDOMINFO *q_u, DS_R_GETPRIMDOMINFO *r_u)
+{
+ NTSTATUS result = NT_STATUS_OK;
+ uint32 level = q_u->level;
+
+ switch ( level ) {
+
+ case DsRolePrimaryDomainInfoBasic:
+ r_u->level = DsRolePrimaryDomainInfoBasic;
+ r_u->ptr = 1;
+ result = fill_dsrole_dominfo_basic( p->mem_ctx, &r_u->info.basic );
+ break;
+
+ default:
+ DEBUG(0,("_dsrole_get_primary_dominfo: Unsupported info level [%d]!\n",
+ level));
+ result = NT_STATUS_INVALID_LEVEL;
+ }
+
+ return result;
+}
+
+
+
diff --git a/source/rpc_server/srv_lsa_hnd.c b/source/rpc_server/srv_lsa_hnd.c
new file mode 100644
index 00000000000..2ec62e2c57a
--- /dev/null
+++ b/source/rpc_server/srv_lsa_hnd.c
@@ -0,0 +1,265 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Jeremy Allison 2001.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/* This is the max handles across all instances of a pipe name. */
+#ifndef MAX_OPEN_POLS
+#define MAX_OPEN_POLS 1024
+#endif
+
+/****************************************************************************
+ Hack as handles need to be persisant over lsa pipe closes so long as a samr
+ pipe is open. JRA.
+****************************************************************************/
+
+static BOOL is_samr_lsa_pipe(const char *pipe_name)
+{
+ return (strstr(pipe_name, "samr") || strstr(pipe_name, "lsa"));
+}
+
+/****************************************************************************
+ Initialise a policy handle list on a pipe. Handle list is shared between all
+ pipes of the same name.
+****************************************************************************/
+
+BOOL init_pipe_handle_list(pipes_struct *p, char *pipe_name)
+{
+ pipes_struct *plist = get_first_internal_pipe();
+ struct handle_list *hl = NULL;
+
+ for (plist = get_first_internal_pipe(); plist; plist = get_next_internal_pipe(plist)) {
+ if (strequal( plist->name, pipe_name) ||
+ (is_samr_lsa_pipe(plist->name) && is_samr_lsa_pipe(pipe_name))) {
+ if (!plist->pipe_handles) {
+ pstring msg;
+ slprintf(msg, sizeof(msg)-1, "init_pipe_handles: NULL pipe_handle pointer in pipe %s",
+ pipe_name );
+ smb_panic(msg);
+ }
+ hl = plist->pipe_handles;
+ break;
+ }
+ }
+
+ if (!hl) {
+ /*
+ * No handle list for this pipe (first open of pipe).
+ * Create list.
+ */
+
+ if ((hl = (struct handle_list *)malloc(sizeof(struct handle_list))) == NULL)
+ return False;
+ ZERO_STRUCTP(hl);
+
+ DEBUG(10,("init_pipe_handles: created handle list for pipe %s\n", pipe_name ));
+ }
+
+ /*
+ * One more pipe is using this list.
+ */
+
+ hl->pipe_ref_count++;
+
+ /*
+ * Point this pipe at this list.
+ */
+
+ p->pipe_handles = hl;
+
+ DEBUG(10,("init_pipe_handles: pipe_handles ref count = %lu for pipe %s\n",
+ (unsigned long)p->pipe_handles->pipe_ref_count, pipe_name ));
+
+ return True;
+}
+
+/****************************************************************************
+ find first available policy slot. creates a policy handle for you.
+****************************************************************************/
+
+BOOL create_policy_hnd(pipes_struct *p, POLICY_HND *hnd, void (*free_fn)(void *), void *data_ptr)
+{
+ static uint32 pol_hnd_low = 0;
+ static uint32 pol_hnd_high = 0;
+
+ struct policy *pol;
+
+ if (p->pipe_handles->count > MAX_OPEN_POLS) {
+ DEBUG(0,("create_policy_hnd: ERROR: too many handles (%d) on this pipe.\n",
+ (int)p->pipe_handles->count));
+ return False;
+ }
+
+ pol = (struct policy *)malloc(sizeof(*p));
+ if (!pol) {
+ DEBUG(0,("create_policy_hnd: ERROR: out of memory!\n"));
+ return False;
+ }
+
+ ZERO_STRUCTP(pol);
+
+ pol->data_ptr = data_ptr;
+ pol->free_fn = free_fn;
+
+ pol_hnd_low++;
+ if (pol_hnd_low == 0)
+ (pol_hnd_high)++;
+
+ SIVAL(&pol->pol_hnd.data1, 0 , 0); /* first bit must be null */
+ SIVAL(&pol->pol_hnd.data2, 0 , pol_hnd_low ); /* second bit is incrementing */
+ SSVAL(&pol->pol_hnd.data3, 0 , pol_hnd_high); /* second bit is incrementing */
+ SSVAL(&pol->pol_hnd.data4, 0 , (pol_hnd_high>>16)); /* second bit is incrementing */
+ SIVAL(pol->pol_hnd.data5, 0, time(NULL)); /* something random */
+ SIVAL(pol->pol_hnd.data5, 4, sys_getpid()); /* something more random */
+
+ DLIST_ADD(p->pipe_handles->Policy, pol);
+ p->pipe_handles->count++;
+
+ *hnd = pol->pol_hnd;
+
+ DEBUG(4,("Opened policy hnd[%d] ", (int)p->pipe_handles->count));
+ dump_data(4, (char *)hnd, sizeof(*hnd));
+
+ return True;
+}
+
+/****************************************************************************
+ find policy by handle - internal version.
+****************************************************************************/
+
+static struct policy *find_policy_by_hnd_internal(pipes_struct *p, POLICY_HND *hnd, void **data_p)
+{
+ struct policy *pol;
+ size_t i;
+
+ if (data_p)
+ *data_p = NULL;
+
+ for (i = 0, pol=p->pipe_handles->Policy;pol;pol=pol->next, i++) {
+ if (memcmp(&pol->pol_hnd, hnd, sizeof(*hnd)) == 0) {
+ DEBUG(4,("Found policy hnd[%d] ", (int)i));
+ dump_data(4, (char *)hnd, sizeof(*hnd));
+ if (data_p)
+ *data_p = pol->data_ptr;
+ return pol;
+ }
+ }
+
+ DEBUG(4,("Policy not found: "));
+ dump_data(4, (char *)hnd, sizeof(*hnd));
+
+ p->bad_handle_fault_state = True;
+
+ return NULL;
+}
+
+/****************************************************************************
+ find policy by handle
+****************************************************************************/
+
+BOOL find_policy_by_hnd(pipes_struct *p, POLICY_HND *hnd, void **data_p)
+{
+ return find_policy_by_hnd_internal(p, hnd, data_p) == NULL ? False : True;
+}
+
+/****************************************************************************
+ Close a policy.
+****************************************************************************/
+
+BOOL close_policy_hnd(pipes_struct *p, POLICY_HND *hnd)
+{
+ struct policy *pol = find_policy_by_hnd_internal(p, hnd, NULL);
+
+ if (!pol) {
+ DEBUG(3,("Error closing policy\n"));
+ return False;
+ }
+
+ DEBUG(3,("Closed policy\n"));
+
+ if (pol->free_fn && pol->data_ptr)
+ (*pol->free_fn)(pol->data_ptr);
+
+ p->pipe_handles->count--;
+
+ DLIST_REMOVE(p->pipe_handles->Policy, pol);
+
+ ZERO_STRUCTP(pol);
+
+ SAFE_FREE(pol);
+
+ return True;
+}
+
+/****************************************************************************
+ Close a pipe - free the handle list if it was the last pipe reference.
+****************************************************************************/
+
+void close_policy_by_pipe(pipes_struct *p)
+{
+ p->pipe_handles->pipe_ref_count--;
+
+ if (p->pipe_handles->pipe_ref_count == 0) {
+ /*
+ * Last pipe open on this list - free the list.
+ */
+ while (p->pipe_handles->Policy)
+ close_policy_hnd(p, &p->pipe_handles->Policy->pol_hnd);
+
+ p->pipe_handles->Policy = NULL;
+ p->pipe_handles->count = 0;
+
+ SAFE_FREE(p->pipe_handles);
+ DEBUG(10,("close_policy_by_pipe: deleted handle list for pipe %s\n", p->name ));
+ }
+}
+
+/*******************************************************************
+Shall we allow access to this rpc? Currently this function
+implements the 'restrict anonymous' setting by denying access to
+anonymous users if the restrict anonymous level is > 0. Further work
+will be checking a security descriptor to determine whether a user
+token has enough access to access the pipe.
+********************************************************************/
+
+BOOL pipe_access_check(pipes_struct *p)
+{
+ /* Don't let anonymous users access this RPC if restrict
+ anonymous > 0 */
+
+ if (lp_restrict_anonymous() > 0) {
+ user_struct *user = get_valid_user_struct(p->vuid);
+
+ if (!user) {
+ DEBUG(3, ("invalid vuid %d\n", p->vuid));
+ return False;
+ }
+
+ if (user->guest)
+ return False;
+ }
+
+ return True;
+}
diff --git a/source/rpc_server/srv_lsa_nt.c b/source/rpc_server/srv_lsa_nt.c
new file mode 100644
index 00000000000..f2fe3235a60
--- /dev/null
+++ b/source/rpc_server/srv_lsa_nt.c
@@ -0,0 +1,1342 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997,
+ * Copyright (C) Jeremy Allison 2001,
+ * Copyright (C) Rafal Szczesniak 2002,
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This is the implementation of the lsa server code. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+extern PRIVS privs[];
+
+struct lsa_info {
+ DOM_SID sid;
+ uint32 access;
+};
+
+struct generic_mapping lsa_generic_mapping = {
+ POLICY_READ,
+ POLICY_WRITE,
+ POLICY_EXECUTE,
+ POLICY_ALL_ACCESS
+};
+
+/*******************************************************************
+ Function to free the per handle data.
+ ********************************************************************/
+
+static void free_lsa_info(void *ptr)
+{
+ struct lsa_info *lsa = (struct lsa_info *)ptr;
+
+ SAFE_FREE(lsa);
+}
+
+/***************************************************************************
+Init dom_query
+ ***************************************************************************/
+
+static void init_dom_query(DOM_QUERY *d_q, const char *dom_name, DOM_SID *dom_sid)
+{
+ d_q->buffer_dom_name = (dom_name != NULL) ? 1 : 0; /* domain buffer pointer */
+ d_q->buffer_dom_sid = (dom_sid != NULL) ? 1 : 0; /* domain sid pointer */
+
+ /* this string is supposed to be non-null terminated. */
+ /* But the maxlen in this UNISTR2 must include the terminating null. */
+ init_unistr2(&d_q->uni_domain_name, dom_name, UNI_BROKEN_NON_NULL);
+
+ /*
+ * I'm not sure why this really odd combination of length
+ * values works, but it does appear to. I need to look at
+ * this *much* more closely - but at the moment leave alone
+ * until it's understood. This allows a W2k client to join
+ * a domain with both odd and even length names... JRA.
+ */
+
+ /*
+ * IMPORTANT NOTE !!!!
+ * The two fields below probably are reversed in meaning, ie.
+ * the first field is probably the str_len, the second the max
+ * len. Both are measured in bytes anyway.
+ */
+
+ d_q->uni_dom_str_len = d_q->uni_domain_name.uni_max_len * 2;
+ d_q->uni_dom_max_len = d_q->uni_domain_name.uni_str_len * 2;
+
+ if (dom_sid != NULL)
+ init_dom_sid2(&d_q->dom_sid, dom_sid);
+}
+
+/***************************************************************************
+ init_dom_ref - adds a domain if it's not already in, returns the index.
+***************************************************************************/
+
+static int init_dom_ref(DOM_R_REF *ref, char *dom_name, DOM_SID *dom_sid)
+{
+ int num = 0;
+
+ if (dom_name != NULL) {
+ for (num = 0; num < ref->num_ref_doms_1; num++) {
+ fstring domname;
+ rpcstr_pull(domname, &ref->ref_dom[num].uni_dom_name, sizeof(domname), -1, 0);
+ if (strequal(domname, dom_name))
+ return num;
+ }
+ } else {
+ num = ref->num_ref_doms_1;
+ }
+
+ if (num >= MAX_REF_DOMAINS) {
+ /* index not found, already at maximum domain limit */
+ return -1;
+ }
+
+ ref->num_ref_doms_1 = num+1;
+ ref->ptr_ref_dom = 1;
+ ref->max_entries = MAX_REF_DOMAINS;
+ ref->num_ref_doms_2 = num+1;
+
+ ref->hdr_ref_dom[num].ptr_dom_sid = dom_sid != NULL ? 1 : 0;
+
+ init_unistr2(&ref->ref_dom[num].uni_dom_name, dom_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&ref->hdr_ref_dom[num].hdr_dom_name, &ref->ref_dom[num].uni_dom_name);
+
+ init_dom_sid2(&ref->ref_dom[num].ref_dom, dom_sid );
+
+ return num;
+}
+
+/***************************************************************************
+ init_lsa_rid2s
+ ***************************************************************************/
+
+static void init_lsa_rid2s(DOM_R_REF *ref, DOM_RID2 *rid2,
+ int num_entries, UNISTR2 *name,
+ uint32 *mapped_count, BOOL endian)
+{
+ int i;
+ int total = 0;
+ *mapped_count = 0;
+
+ SMB_ASSERT(num_entries <= MAX_LOOKUP_SIDS);
+
+ become_root(); /* lookup_name can require root privs */
+
+ for (i = 0; i < num_entries; i++) {
+ BOOL status = False;
+ DOM_SID sid;
+ uint32 rid = 0xffffffff;
+ int dom_idx = -1;
+ pstring full_name;
+ fstring dom_name, user;
+ enum SID_NAME_USE name_type = SID_NAME_UNKNOWN;
+
+ /* Split name into domain and user component */
+
+ unistr2_to_ascii(full_name, &name[i], sizeof(full_name));
+ split_domain_name(full_name, dom_name, user);
+
+ /* Lookup name */
+
+ DEBUG(5, ("init_lsa_rid2s: looking up name %s\n", full_name));
+
+ status = lookup_name(dom_name, user, &sid, &name_type);
+
+ if (name_type == SID_NAME_WKN_GRP) {
+ /* BUILTIN aliases are still aliases :-) */
+ name_type = SID_NAME_ALIAS;
+ }
+
+ DEBUG(5, ("init_lsa_rid2s: %s\n", status ? "found" :
+ "not found"));
+
+ if (status && name_type != SID_NAME_UNKNOWN) {
+ sid_split_rid(&sid, &rid);
+ dom_idx = init_dom_ref(ref, dom_name, &sid);
+ (*mapped_count)++;
+ } else {
+ dom_idx = -1;
+ rid = 0;
+ name_type = SID_NAME_UNKNOWN;
+ }
+
+ init_dom_rid2(&rid2[total], rid, name_type, dom_idx);
+ total++;
+ }
+
+ unbecome_root();
+}
+
+/***************************************************************************
+ init_reply_lookup_names
+ ***************************************************************************/
+
+static void init_reply_lookup_names(LSA_R_LOOKUP_NAMES *r_l,
+ DOM_R_REF *ref, uint32 num_entries,
+ DOM_RID2 *rid2, uint32 mapped_count)
+{
+ r_l->ptr_dom_ref = 1;
+ r_l->dom_ref = ref;
+
+ r_l->num_entries = num_entries;
+ r_l->ptr_entries = 1;
+ r_l->num_entries2 = num_entries;
+ r_l->dom_rid = rid2;
+
+ r_l->mapped_count = mapped_count;
+}
+
+/***************************************************************************
+ Init lsa_trans_names.
+ ***************************************************************************/
+
+static void init_lsa_trans_names(TALLOC_CTX *ctx, DOM_R_REF *ref, LSA_TRANS_NAME_ENUM *trn,
+ int num_entries, DOM_SID2 *sid,
+ uint32 *mapped_count)
+{
+ int i;
+ int total = 0;
+ *mapped_count = 0;
+
+ /* Allocate memory for list of names */
+
+ if (num_entries > 0) {
+ if (!(trn->name = (LSA_TRANS_NAME *)talloc(ctx, sizeof(LSA_TRANS_NAME) *
+ num_entries))) {
+ DEBUG(0, ("init_lsa_trans_names(): out of memory\n"));
+ return;
+ }
+
+ if (!(trn->uni_name = (UNISTR2 *)talloc(ctx, sizeof(UNISTR2) *
+ num_entries))) {
+ DEBUG(0, ("init_lsa_trans_names(): out of memory\n"));
+ return;
+ }
+ }
+
+ become_root(); /* Need root to get to passdb to for local sids */
+
+ for (i = 0; i < num_entries; i++) {
+ BOOL status = False;
+ DOM_SID find_sid = sid[i].sid;
+ uint32 rid = 0xffffffff;
+ int dom_idx = -1;
+ fstring name, dom_name;
+ enum SID_NAME_USE sid_name_use = (enum SID_NAME_USE)0;
+
+ sid_to_string(name, &find_sid);
+ DEBUG(5, ("init_lsa_trans_names: looking up sid %s\n", name));
+
+ /* Lookup sid from winbindd */
+
+ status = lookup_sid(&find_sid, dom_name, name, &sid_name_use);
+
+ DEBUG(5, ("init_lsa_trans_names: %s\n", status ? "found" :
+ "not found"));
+
+ if (!status) {
+ sid_name_use = SID_NAME_UNKNOWN;
+ memset(dom_name, '\0', sizeof(dom_name));
+ sid_to_string(name, &find_sid);
+ dom_idx = -1;
+
+ DEBUG(10,("init_lsa_trans_names: added unknown user '%s' to "
+ "referenced list.\n", name ));
+ } else {
+ (*mapped_count)++;
+ /* Store domain sid in ref array */
+ if (find_sid.num_auths == 5) {
+ sid_split_rid(&find_sid, &rid);
+ }
+ dom_idx = init_dom_ref(ref, dom_name, &find_sid);
+
+ DEBUG(10,("init_lsa_trans_names: added user '%s\\%s' to "
+ "referenced list.\n", dom_name, name ));
+
+ }
+
+ init_lsa_trans_name(&trn->name[total], &trn->uni_name[total],
+ sid_name_use, name, dom_idx);
+ total++;
+ }
+
+ unbecome_root();
+
+ trn->num_entries = total;
+ trn->ptr_trans_names = 1;
+ trn->num_entries2 = total;
+}
+
+/***************************************************************************
+ Init_reply_lookup_sids.
+ ***************************************************************************/
+
+static void init_reply_lookup_sids(LSA_R_LOOKUP_SIDS *r_l,
+ DOM_R_REF *ref, LSA_TRANS_NAME_ENUM *names,
+ uint32 mapped_count)
+{
+ r_l->ptr_dom_ref = 1;
+ r_l->dom_ref = ref;
+ r_l->names = names;
+ r_l->mapped_count = mapped_count;
+}
+
+static NTSTATUS lsa_get_generic_sd(TALLOC_CTX *mem_ctx, SEC_DESC **sd, size_t *sd_size)
+{
+ extern DOM_SID global_sid_World;
+ extern DOM_SID global_sid_Builtin;
+ DOM_SID local_adm_sid;
+ DOM_SID adm_sid;
+
+ SEC_ACE ace[3];
+ SEC_ACCESS mask;
+
+ SEC_ACL *psa = NULL;
+
+ init_sec_access(&mask, POLICY_EXECUTE);
+ init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ sid_copy(&adm_sid, get_global_sam_sid());
+ sid_append_rid(&adm_sid, DOMAIN_GROUP_RID_ADMINS);
+ init_sec_access(&mask, POLICY_ALL_ACCESS);
+ init_sec_ace(&ace[1], &adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ sid_copy(&local_adm_sid, &global_sid_Builtin);
+ sid_append_rid(&local_adm_sid, BUILTIN_ALIAS_RID_ADMINS);
+ init_sec_access(&mask, POLICY_ALL_ACCESS);
+ init_sec_ace(&ace[2], &local_adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ if((psa = make_sec_acl(mem_ctx, NT4_ACL_REVISION, 3, ace)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if((*sd = make_sec_desc(mem_ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, &adm_sid, NULL, NULL, psa, sd_size)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Init_dns_dom_info.
+***************************************************************************/
+
+static void init_dns_dom_info(LSA_DNS_DOM_INFO *r_l, const char *nb_name,
+ const char *dns_name, const char *forest_name,
+ struct uuid *dom_guid, DOM_SID *dom_sid)
+{
+ if (nb_name && *nb_name) {
+ init_unistr2(&r_l->uni_nb_dom_name, nb_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&r_l->hdr_nb_dom_name, &r_l->uni_nb_dom_name);
+ r_l->hdr_nb_dom_name.uni_max_len += 2;
+ r_l->uni_nb_dom_name.uni_max_len += 1;
+ }
+
+ if (dns_name && *dns_name) {
+ init_unistr2(&r_l->uni_dns_dom_name, dns_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&r_l->hdr_dns_dom_name, &r_l->uni_dns_dom_name);
+ r_l->hdr_dns_dom_name.uni_max_len += 2;
+ r_l->uni_dns_dom_name.uni_max_len += 1;
+ }
+
+ if (forest_name && *forest_name) {
+ init_unistr2(&r_l->uni_forest_name, forest_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&r_l->hdr_forest_name, &r_l->uni_forest_name);
+ r_l->hdr_forest_name.uni_max_len += 2;
+ r_l->uni_forest_name.uni_max_len += 1;
+ }
+
+ /* how do we init the guid ? probably should write an init fn */
+ if (dom_guid) {
+ memcpy(&r_l->dom_guid, dom_guid, sizeof(struct uuid));
+ }
+
+ if (dom_sid) {
+ r_l->ptr_dom_sid = 1;
+ init_dom_sid2(&r_l->dom_sid, dom_sid);
+ }
+}
+
+/***************************************************************************
+ _lsa_open_policy2.
+ ***************************************************************************/
+
+NTSTATUS _lsa_open_policy2(pipes_struct *p, LSA_Q_OPEN_POL2 *q_u, LSA_R_OPEN_POL2 *r_u)
+{
+ struct lsa_info *info;
+ SEC_DESC *psd = NULL;
+ size_t sd_size;
+ uint32 des_access=q_u->des_access;
+ uint32 acc_granted;
+ NTSTATUS status;
+
+
+ /* map the generic bits to the lsa policy ones */
+ se_map_generic(&des_access, &lsa_generic_mapping);
+
+ /* get the generic lsa policy SD until we store it */
+ lsa_get_generic_sd(p->mem_ctx, &psd, &sd_size);
+
+ if(!se_access_check(psd, p->pipe_user.nt_user_token, des_access, &acc_granted, &status)) {
+ if (geteuid() != 0) {
+ return status;
+ }
+ DEBUG(4,("ACCESS should be DENIED (granted: %#010x; required: %#010x)\n",
+ acc_granted, des_access));
+ DEBUGADD(4,("but overwritten by euid == 0\n"));
+ }
+
+ /* This is needed for lsa_open_account and rpcclient .... :-) */
+
+ if (geteuid() == 0)
+ acc_granted = POLICY_ALL_ACCESS;
+
+ /* associate the domain SID with the (unique) handle. */
+ if ((info = (struct lsa_info *)malloc(sizeof(struct lsa_info))) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(info);
+ sid_copy(&info->sid,get_global_sam_sid());
+ info->access = acc_granted;
+
+ /* set up the LSA QUERY INFO response */
+ if (!create_policy_hnd(p, &r_u->pol, free_lsa_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_open_policy
+ ***************************************************************************/
+
+NTSTATUS _lsa_open_policy(pipes_struct *p, LSA_Q_OPEN_POL *q_u, LSA_R_OPEN_POL *r_u)
+{
+ struct lsa_info *info;
+ SEC_DESC *psd = NULL;
+ size_t sd_size;
+ uint32 des_access=q_u->des_access;
+ uint32 acc_granted;
+ NTSTATUS status;
+
+
+ /* map the generic bits to the lsa policy ones */
+ se_map_generic(&des_access, &lsa_generic_mapping);
+
+ /* get the generic lsa policy SD until we store it */
+ lsa_get_generic_sd(p->mem_ctx, &psd, &sd_size);
+
+ if(!se_access_check(psd, p->pipe_user.nt_user_token, des_access, &acc_granted, &status)) {
+ if (geteuid() != 0) {
+ return status;
+ }
+ DEBUG(4,("ACCESS should be DENIED (granted: %#010x; required: %#010x)\n",
+ acc_granted, des_access));
+ DEBUGADD(4,("but overwritten by euid == 0\n"));
+ acc_granted = des_access;
+ }
+
+ /* associate the domain SID with the (unique) handle. */
+ if ((info = (struct lsa_info *)malloc(sizeof(struct lsa_info))) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(info);
+ sid_copy(&info->sid,get_global_sam_sid());
+ info->access = acc_granted;
+
+ /* set up the LSA QUERY INFO response */
+ if (!create_policy_hnd(p, &r_u->pol, free_lsa_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_enum_trust_dom - this needs fixing to do more than return NULL ! JRA.
+ ufff, done :) mimir
+ ***************************************************************************/
+
+NTSTATUS _lsa_enum_trust_dom(pipes_struct *p, LSA_Q_ENUM_TRUST_DOM *q_u, LSA_R_ENUM_TRUST_DOM *r_u)
+{
+ struct lsa_info *info;
+ uint32 enum_context = q_u->enum_context;
+
+ /*
+ * preferred length is set to 5 as a "our" preferred length
+ * nt sets this parameter to 2
+ * update (20.08.2002): it's not preferred length, but preferred size!
+ * it needs further investigation how to optimally choose this value
+ */
+ uint32 max_num_domains = q_u->preferred_len < 5 ? q_u->preferred_len : 10;
+ TRUSTDOM **trust_doms;
+ uint32 num_domains;
+ NTSTATUS nt_status;
+
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ /* check if the user have enough rights */
+ if (!(info->access & POLICY_VIEW_LOCAL_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ nt_status = secrets_get_trusted_domains(p->mem_ctx, (int *)&enum_context, max_num_domains, (int *)&num_domains, &trust_doms);
+
+ if (!NT_STATUS_IS_OK(nt_status) &&
+ !NT_STATUS_EQUAL(nt_status, STATUS_MORE_ENTRIES) &&
+ !NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_MORE_ENTRIES)) {
+ return nt_status;
+ } else {
+ r_u->status = nt_status;
+ }
+
+ /* set up the lsa_enum_trust_dom response */
+ init_r_enum_trust_dom(p->mem_ctx, r_u, enum_context, max_num_domains, num_domains, trust_doms);
+
+ return r_u->status;
+}
+
+/***************************************************************************
+ _lsa_query_info. See the POLICY_INFOMATION_CLASS docs at msdn.
+ ***************************************************************************/
+
+NTSTATUS _lsa_query_info(pipes_struct *p, LSA_Q_QUERY_INFO *q_u, LSA_R_QUERY_INFO *r_u)
+{
+ struct lsa_info *handle;
+ LSA_INFO_UNION *info = &r_u->dom;
+ DOM_SID domain_sid;
+ const char *name;
+ DOM_SID *sid = NULL;
+
+ r_u->status = NT_STATUS_OK;
+
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle))
+ return NT_STATUS_INVALID_HANDLE;
+
+ switch (q_u->info_class) {
+ case 0x02:
+ {
+ unsigned int i;
+ /* check if the user have enough rights */
+ if (!(handle->access & POLICY_VIEW_AUDIT_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* fake info: We audit everything. ;) */
+ info->id2.auditing_enabled = 1;
+ info->id2.count1 = 7;
+ info->id2.count2 = 7;
+ if ((info->id2.auditsettings = (uint32 *)talloc(p->mem_ctx,7*sizeof(uint32))) == NULL)
+ return NT_STATUS_NO_MEMORY;
+ for (i = 0; i < 7; i++)
+ info->id2.auditsettings[i] = 3;
+ break;
+ }
+ case 0x03:
+ /* check if the user have enough rights */
+ if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* Request PolicyPrimaryDomainInformation. */
+ switch (lp_server_role()) {
+ case ROLE_DOMAIN_PDC:
+ case ROLE_DOMAIN_BDC:
+ name = get_global_sam_name();
+ sid = get_global_sam_sid();
+ break;
+ case ROLE_DOMAIN_MEMBER:
+ name = lp_workgroup();
+ /* We need to return the Domain SID here. */
+ if (secrets_fetch_domain_sid(lp_workgroup(), &domain_sid))
+ sid = &domain_sid;
+ else
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ break;
+ case ROLE_STANDALONE:
+ name = lp_workgroup();
+ sid = NULL;
+ break;
+ default:
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+ init_dom_query(&r_u->dom.id3, name, sid);
+ break;
+ case 0x05:
+ /* check if the user have enough rights */
+ if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* Request PolicyAccountDomainInformation. */
+ name = get_global_sam_name();
+ sid = get_global_sam_sid();
+ init_dom_query(&r_u->dom.id5, name, sid);
+ break;
+ case 0x06:
+ /* check if the user have enough rights */
+ if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ switch (lp_server_role()) {
+ case ROLE_DOMAIN_BDC:
+ /*
+ * only a BDC is a backup controller
+ * of the domain, it controls.
+ */
+ info->id6.server_role = 2;
+ break;
+ default:
+ /*
+ * any other role is a primary
+ * of the domain, it controls.
+ */
+ info->id6.server_role = 3;
+ break;
+ }
+ break;
+ default:
+ DEBUG(0,("_lsa_query_info: unknown info level in Lsa Query: %d\n", q_u->info_class));
+ r_u->status = NT_STATUS_INVALID_INFO_CLASS;
+ break;
+ }
+
+ if (NT_STATUS_IS_OK(r_u->status)) {
+ r_u->undoc_buffer = 0x22000000; /* bizarre */
+ r_u->info_class = q_u->info_class;
+ }
+
+ return r_u->status;
+}
+
+/***************************************************************************
+ _lsa_lookup_sids
+ ***************************************************************************/
+
+NTSTATUS _lsa_lookup_sids(pipes_struct *p, LSA_Q_LOOKUP_SIDS *q_u, LSA_R_LOOKUP_SIDS *r_u)
+{
+ struct lsa_info *handle;
+ DOM_SID2 *sid = q_u->sids.sid;
+ int num_entries = q_u->sids.num_entries;
+ DOM_R_REF *ref = NULL;
+ LSA_TRANS_NAME_ENUM *names = NULL;
+ uint32 mapped_count = 0;
+
+ if (num_entries > MAX_LOOKUP_SIDS) {
+ num_entries = MAX_LOOKUP_SIDS;
+ DEBUG(5,("_lsa_lookup_sids: truncating SID lookup list to %d\n", num_entries));
+ }
+
+ ref = (DOM_R_REF *)talloc_zero(p->mem_ctx, sizeof(DOM_R_REF));
+ names = (LSA_TRANS_NAME_ENUM *)talloc_zero(p->mem_ctx, sizeof(LSA_TRANS_NAME_ENUM));
+
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle)) {
+ r_u->status = NT_STATUS_INVALID_HANDLE;
+ goto done;
+ }
+
+ /* check if the user have enough rights */
+ if (!(handle->access & POLICY_LOOKUP_NAMES)) {
+ r_u->status = NT_STATUS_ACCESS_DENIED;
+ goto done;
+ }
+ if (!ref || !names)
+ return NT_STATUS_NO_MEMORY;
+
+done:
+
+ /* set up the LSA Lookup SIDs response */
+ init_lsa_trans_names(p->mem_ctx, ref, names, num_entries, sid, &mapped_count);
+ if (mapped_count == 0)
+ r_u->status = NT_STATUS_NONE_MAPPED;
+ else if (mapped_count != num_entries)
+ r_u->status = STATUS_SOME_UNMAPPED;
+ else
+ r_u->status = NT_STATUS_OK;
+ init_reply_lookup_sids(r_u, ref, names, mapped_count);
+
+ return r_u->status;
+}
+
+/***************************************************************************
+lsa_reply_lookup_names
+ ***************************************************************************/
+
+NTSTATUS _lsa_lookup_names(pipes_struct *p,LSA_Q_LOOKUP_NAMES *q_u, LSA_R_LOOKUP_NAMES *r_u)
+{
+ struct lsa_info *handle;
+ UNISTR2 *names = q_u->uni_name;
+ int num_entries = q_u->num_entries;
+ DOM_R_REF *ref;
+ DOM_RID2 *rids;
+ uint32 mapped_count = 0;
+
+ if (num_entries > MAX_LOOKUP_SIDS) {
+ num_entries = MAX_LOOKUP_SIDS;
+ DEBUG(5,("_lsa_lookup_names: truncating name lookup list to %d\n", num_entries));
+ }
+
+ ref = (DOM_R_REF *)talloc_zero(p->mem_ctx, sizeof(DOM_R_REF));
+ rids = (DOM_RID2 *)talloc_zero(p->mem_ctx, sizeof(DOM_RID2)*num_entries);
+
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle)) {
+ r_u->status = NT_STATUS_INVALID_HANDLE;
+ goto done;
+ }
+
+ /* check if the user have enough rights */
+ if (!(handle->access & POLICY_LOOKUP_NAMES)) {
+ r_u->status = NT_STATUS_ACCESS_DENIED;
+ goto done;
+ }
+
+ if (!ref || !rids)
+ return NT_STATUS_NO_MEMORY;
+
+done:
+
+ /* set up the LSA Lookup RIDs response */
+ init_lsa_rid2s(ref, rids, num_entries, names, &mapped_count, p->endian);
+ if (mapped_count == 0)
+ r_u->status = NT_STATUS_NONE_MAPPED;
+ else if (mapped_count != num_entries)
+ r_u->status = STATUS_SOME_UNMAPPED;
+ else
+ r_u->status = NT_STATUS_OK;
+ init_reply_lookup_names(r_u, ref, num_entries, rids, mapped_count);
+
+ return r_u->status;
+}
+
+/***************************************************************************
+ _lsa_close. Also weird - needs to check if lsa handle is correct. JRA.
+ ***************************************************************************/
+
+NTSTATUS _lsa_close(pipes_struct *p, LSA_Q_CLOSE *q_u, LSA_R_CLOSE *r_u)
+{
+ if (!find_policy_by_hnd(p, &q_u->pol, NULL))
+ return NT_STATUS_INVALID_HANDLE;
+
+ close_policy_hnd(p, &q_u->pol);
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ "No more secrets Marty...." :-).
+ ***************************************************************************/
+
+NTSTATUS _lsa_open_secret(pipes_struct *p, LSA_Q_OPEN_SECRET *q_u, LSA_R_OPEN_SECRET *r_u)
+{
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+}
+
+/***************************************************************************
+_lsa_enum_privs.
+ ***************************************************************************/
+
+NTSTATUS _lsa_enum_privs(pipes_struct *p, LSA_Q_ENUM_PRIVS *q_u, LSA_R_ENUM_PRIVS *r_u)
+{
+ struct lsa_info *handle;
+ uint32 i;
+
+ uint32 enum_context=q_u->enum_context;
+ LSA_PRIV_ENTRY *entry;
+ LSA_PRIV_ENTRY *entries=NULL;
+
+ if (enum_context >= PRIV_ALL_INDEX-2)
+ return NT_STATUS_NO_MORE_ENTRIES;
+
+ entries = (LSA_PRIV_ENTRY *)talloc_zero(p->mem_ctx, sizeof(LSA_PRIV_ENTRY) * (PRIV_ALL_INDEX));
+ if (entries==NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle))
+ return NT_STATUS_INVALID_HANDLE;
+
+ /* check if the user have enough rights */
+
+ /*
+ * I don't know if it's the right one. not documented.
+ */
+ if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ entry = entries;
+
+ DEBUG(10,("_lsa_enum_privs: enum_context:%d total entries:%d\n", enum_context, PRIV_ALL_INDEX));
+
+ for (i = 1; i < PRIV_ALL_INDEX-1; i++, entry++) {
+ if( i<enum_context) {
+ init_unistr2(&entry->name, NULL, UNI_FLAGS_NONE);
+ init_uni_hdr(&entry->hdr_name, &entry->name);
+ entry->luid_low = 0;
+ entry->luid_high = 0;
+ } else {
+ init_unistr2(&entry->name, privs[i].priv, UNI_FLAGS_NONE);
+ init_uni_hdr(&entry->hdr_name, &entry->name);
+ entry->luid_low = privs[i].se_priv;
+ entry->luid_high = 0;
+ }
+ }
+
+ enum_context = PRIV_ALL_INDEX-2;
+ init_lsa_r_enum_privs(r_u, enum_context, PRIV_ALL_INDEX-2, entries);
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+_lsa_priv_get_dispname.
+ ***************************************************************************/
+
+NTSTATUS _lsa_priv_get_dispname(pipes_struct *p, LSA_Q_PRIV_GET_DISPNAME *q_u, LSA_R_PRIV_GET_DISPNAME *r_u)
+{
+ struct lsa_info *handle;
+ fstring name_asc;
+ int i=1;
+
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle))
+ return NT_STATUS_INVALID_HANDLE;
+
+ /* check if the user have enough rights */
+
+ /*
+ * I don't know if it's the right one. not documented.
+ */
+ if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ unistr2_to_ascii(name_asc, &q_u->name, sizeof(name_asc));
+
+ DEBUG(10,("_lsa_priv_get_dispname: %s", name_asc));
+
+ while (privs[i].se_priv!=SE_ALL_PRIVS && strcmp(name_asc, privs[i].priv))
+ i++;
+
+ if (privs[i].se_priv!=SE_ALL_PRIVS) {
+ DEBUG(10,(": %s\n", privs[i].description));
+ init_unistr2(&r_u->desc, privs[i].description, UNI_FLAGS_NONE);
+ init_uni_hdr(&r_u->hdr_desc, &r_u->desc);
+
+ r_u->ptr_info=0xdeadbeef;
+ r_u->lang_id=q_u->lang_id;
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(10,("_lsa_priv_get_dispname: doesn't exist\n"));
+ r_u->ptr_info=0;
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+}
+
+/***************************************************************************
+_lsa_enum_accounts.
+ ***************************************************************************/
+
+NTSTATUS _lsa_enum_accounts(pipes_struct *p, LSA_Q_ENUM_ACCOUNTS *q_u, LSA_R_ENUM_ACCOUNTS *r_u)
+{
+ struct lsa_info *handle;
+ DOM_SID *sid_list;
+ int i, j, num_entries;
+ LSA_SID_ENUM *sids=&r_u->sids;
+
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ sid_list = NULL;
+ num_entries = 0;
+
+ /* The only way we can currently find out all the SIDs that have been
+ privileged is to scan all privileges */
+
+ for (i=1; i<PRIV_ALL_INDEX-1; i++) {
+ DOM_SID *priv_sids = NULL;
+ int num_priv_sids = 0;
+
+ if (!get_sids_from_priv(privs[i].priv, &priv_sids,
+ &num_priv_sids))
+ continue;
+
+ for (j=0; j<num_priv_sids; j++) {
+ add_sid_to_array_unique(&priv_sids[j], &sid_list,
+ &num_entries);
+ }
+ SAFE_FREE(priv_sids);
+ }
+
+ if (q_u->enum_context >= num_entries)
+ return NT_STATUS_NO_MORE_ENTRIES;
+
+ sids->ptr_sid = (uint32 *)talloc_zero(p->mem_ctx, (num_entries-q_u->enum_context)*sizeof(uint32));
+ sids->sid = (DOM_SID2 *)talloc_zero(p->mem_ctx, (num_entries-q_u->enum_context)*sizeof(DOM_SID2));
+
+ if (sids->ptr_sid==NULL || sids->sid==NULL) {
+ SAFE_FREE(sid_list);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=q_u->enum_context, j=0; i<num_entries; i++) {
+ init_dom_sid2( &(*sids).sid[j], &sid_list[i]);
+ (*sids).ptr_sid[j]=1;
+ j++;
+ }
+
+ SAFE_FREE(sid_list);
+
+ init_lsa_r_enum_accounts(r_u, num_entries);
+
+ return NT_STATUS_OK;
+}
+
+
+NTSTATUS _lsa_unk_get_connuser(pipes_struct *p, LSA_Q_UNK_GET_CONNUSER *q_u, LSA_R_UNK_GET_CONNUSER *r_u)
+{
+ fstring username, domname;
+ user_struct *vuser = get_valid_user_struct(p->vuid);
+
+ if (vuser == NULL)
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+
+ fstrcpy(username, vuser->user.smb_name);
+ fstrcpy(domname, vuser->user.domain);
+
+ r_u->ptr_user_name = 1;
+ init_unistr2(&r_u->uni2_user_name, username, UNI_STR_TERMINATE);
+ init_uni_hdr(&r_u->hdr_user_name, &r_u->uni2_user_name);
+
+ r_u->unk1 = 1;
+
+ r_u->ptr_dom_name = 1;
+ init_unistr2(&r_u->uni2_dom_name, domname, UNI_STR_TERMINATE);
+ init_uni_hdr(&r_u->hdr_dom_name, &r_u->uni2_dom_name);
+
+ r_u->status = NT_STATUS_OK;
+
+ return r_u->status;
+}
+
+/***************************************************************************
+ Lsa Create Account
+
+ FIXME: Actually the code is just a copy of lsa_open_account
+ TODO: Check and code what this function should exactly do
+ ***************************************************************************/
+
+NTSTATUS _lsa_create_account(pipes_struct *p, LSA_Q_CREATEACCOUNT *q_u, LSA_R_CREATEACCOUNT *r_u)
+{
+ struct lsa_info *handle;
+ struct lsa_info *info;
+
+ r_u->status = NT_STATUS_OK;
+
+ /* find the connection policy handle. */
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle))
+ return NT_STATUS_INVALID_HANDLE;
+
+ /* check if the user have enough rights */
+
+ /*
+ * I don't know if it's the right one. not documented.
+ * but guessed with rpcclient.
+ */
+ if (!(handle->access & POLICY_GET_PRIVATE_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* associate the user/group SID with the (unique) handle. */
+ if ((info = (struct lsa_info *)malloc(sizeof(struct lsa_info))) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(info);
+ info->sid = q_u->sid.sid;
+ info->access = q_u->access;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, &r_u->pol, free_lsa_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ return r_u->status;
+}
+
+
+/***************************************************************************
+ Lsa Open Account
+ ***************************************************************************/
+
+NTSTATUS _lsa_open_account(pipes_struct *p, LSA_Q_OPENACCOUNT *q_u, LSA_R_OPENACCOUNT *r_u)
+{
+ struct lsa_info *handle;
+ struct lsa_info *info;
+
+ r_u->status = NT_STATUS_OK;
+
+ /* find the connection policy handle. */
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle))
+ return NT_STATUS_INVALID_HANDLE;
+
+ /* check if the user have enough rights */
+
+ /*
+ * I don't know if it's the right one. not documented.
+ * but guessed with rpcclient.
+ */
+ if (!(handle->access & POLICY_GET_PRIVATE_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* associate the user/group SID with the (unique) handle. */
+ if ((info = (struct lsa_info *)malloc(sizeof(struct lsa_info))) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(info);
+ info->sid = q_u->sid.sid;
+ info->access = q_u->access;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, &r_u->pol, free_lsa_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ return r_u->status;
+}
+
+/***************************************************************************
+ For a given SID, enumerate all the privilege this account has.
+ ***************************************************************************/
+
+NTSTATUS _lsa_enum_privsaccount(pipes_struct *p, prs_struct *ps, LSA_Q_ENUMPRIVSACCOUNT *q_u, LSA_R_ENUMPRIVSACCOUNT *r_u)
+{
+ struct lsa_info *info=NULL;
+ LUID_ATTR *set=NULL;
+ PRIVILEGE_SET *priv;
+
+ r_u->status = NT_STATUS_OK;
+
+ /* find the connection policy handle. */
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ init_privilege(&priv);
+
+ if (!get_priv_for_sid(&info->sid, priv)) {
+ /* This is probably wrong... */
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ DEBUG(10,("_lsa_enum_privsaccount: %d privileges\n", priv->count));
+
+ if (priv->count > 0) {
+ int i;
+ set=(LUID_ATTR *)talloc(ps->mem_ctx,
+ priv->count*sizeof(LUID_ATTR));
+ if (set == NULL) {
+ destroy_privilege(&priv);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < priv->count; i++) {
+ set[i].luid.low = priv->set[i].luid.low;
+ set[i].luid.high = priv->set[i].luid.high;
+ set[i].attr = priv->set[i].attr;
+ DEBUG(10,("_lsa_enum_privsaccount: %d: %d:%d:%d\n", i,
+ set[i].luid.high, set[i].luid.low,
+ set[i].attr));
+ }
+ }
+
+ init_lsa_r_enum_privsaccount(ps->mem_ctx, r_u, set, priv->count, 0);
+ destroy_privilege(&priv);
+
+ return r_u->status;
+}
+
+/***************************************************************************
+
+ ***************************************************************************/
+
+NTSTATUS _lsa_getsystemaccount(pipes_struct *p, LSA_Q_GETSYSTEMACCOUNT *q_u, LSA_R_GETSYSTEMACCOUNT *r_u)
+{
+ struct lsa_info *info=NULL;
+ r_u->status = NT_STATUS_OK;
+ fstring name, dom_name;
+ enum SID_NAME_USE type;
+
+ /* find the connection policy handle. */
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!lookup_sid(&info->sid, dom_name, name, &type))
+ return NT_STATUS_INVALID_HANDLE;
+
+ /*
+ 0x01 -> Log on locally
+ 0x02 -> Access this computer from network
+ 0x04 -> Log on as a batch job
+ 0x10 -> Log on as a service
+
+ they can be ORed together
+ */
+
+ r_u->access = PR_LOG_ON_LOCALLY | PR_ACCESS_FROM_NETWORK;
+
+ return r_u->status;
+}
+
+/***************************************************************************
+ update the systemaccount information
+ ***************************************************************************/
+
+NTSTATUS _lsa_setsystemaccount(pipes_struct *p, LSA_Q_SETSYSTEMACCOUNT *q_u, LSA_R_SETSYSTEMACCOUNT *r_u)
+{
+ struct lsa_info *info=NULL;
+ GROUP_MAP map;
+ r_u->status = NT_STATUS_OK;
+
+ /* find the connection policy handle. */
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!pdb_getgrsid(&map, info->sid))
+ return NT_STATUS_NO_SUCH_GROUP;
+
+ if(!pdb_update_group_mapping_entry(&map))
+ return NT_STATUS_NO_SUCH_GROUP;
+
+ return r_u->status;
+}
+
+/***************************************************************************
+ For a given SID, add some privileges.
+ ***************************************************************************/
+
+NTSTATUS _lsa_addprivs(pipes_struct *p, LSA_Q_ADDPRIVS *q_u, LSA_R_ADDPRIVS *r_u)
+{
+#if 0
+ struct lsa_info *info = NULL;
+ GROUP_MAP map;
+ int i = 0;
+ LUID_ATTR *luid_attr = NULL;
+ PRIVILEGE_SET *set = NULL;
+#endif
+
+ r_u->status = NT_STATUS_OK;
+
+#if 0 /* privileges are not implemented */
+ /* find the connection policy handle. */
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!pdb_getgrsid(&map, info->sid))
+ return NT_STATUS_NO_SUCH_GROUP;
+
+ set = &q_u->set;
+
+ for (i = 0; i < set->count; i++) {
+ luid_attr = &set->set[i];
+
+ /* check if the privilege is already there */
+ if (check_priv_in_privilege(map.priv_set, *luid_attr)){
+ destroy_privilege(&map.priv_set);
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ add_privilege(map.priv_set, *luid_attr);
+ }
+
+ if(!pdb_update_group_mapping_entry(&map))
+ return NT_STATUS_NO_SUCH_GROUP;
+
+ destroy_privilege(&map.priv_set);
+
+#endif
+ return r_u->status;
+}
+
+/***************************************************************************
+ For a given SID, remove some privileges.
+ ***************************************************************************/
+
+NTSTATUS _lsa_removeprivs(pipes_struct *p, LSA_Q_REMOVEPRIVS *q_u, LSA_R_REMOVEPRIVS *r_u)
+{
+#if 0
+ struct lsa_info *info = NULL;
+ GROUP_MAP map;
+ int i=0;
+ LUID_ATTR *luid_attr = NULL;
+ PRIVILEGE_SET *set = NULL;
+#endif
+
+ r_u->status = NT_STATUS_OK;
+
+#if 0 /* privileges are not implemented */
+ /* find the connection policy handle. */
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!pdb_getgrsid(&map, info->sid))
+ return NT_STATUS_NO_SUCH_GROUP;
+
+ if (q_u->allrights != 0) {
+ /* log it and return, until I see one myself don't do anything */
+ DEBUG(5,("_lsa_removeprivs: trying to remove all privileges ?\n"));
+ return NT_STATUS_OK;
+ }
+
+ if (q_u->ptr == 0) {
+ /* log it and return, until I see one myself don't do anything */
+ DEBUG(5,("_lsa_removeprivs: no privileges to remove ?\n"));
+ return NT_STATUS_OK;
+ }
+
+ set = &q_u->set;
+
+ for (i = 0; i < set->count; i++) {
+ luid_attr = &set->set[i];
+
+ /* if we don't have the privilege, we're trying to remove, give up */
+ /* what else can we do ??? JFM. */
+ if (!check_priv_in_privilege(map.priv_set, *luid_attr)){
+ destroy_privilege(&map.priv_set);
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ remove_privilege(map.priv_set, *luid_attr);
+ }
+
+ if(!pdb_update_group_mapping_entry(&map))
+ return NT_STATUS_NO_SUCH_GROUP;
+
+ destroy_privilege(&map.priv_set);
+#endif
+ return r_u->status;
+}
+
+/***************************************************************************
+ For a given SID, remove some privileges.
+ ***************************************************************************/
+
+NTSTATUS _lsa_query_secobj(pipes_struct *p, LSA_Q_QUERY_SEC_OBJ *q_u, LSA_R_QUERY_SEC_OBJ *r_u)
+{
+ struct lsa_info *handle=NULL;
+ SEC_DESC *psd = NULL;
+ size_t sd_size;
+ NTSTATUS status;
+
+ r_u->status = NT_STATUS_OK;
+
+ /* find the connection policy handle. */
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle))
+ return NT_STATUS_INVALID_HANDLE;
+
+ /* check if the user have enough rights */
+ if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+
+ switch (q_u->sec_info) {
+ case 1:
+ /* SD contains only the owner */
+
+ status=lsa_get_generic_sd(p->mem_ctx, &psd, &sd_size);
+ if(!NT_STATUS_IS_OK(status))
+ return NT_STATUS_NO_MEMORY;
+
+
+ if((r_u->buf = make_sec_desc_buf(p->mem_ctx, sd_size, psd)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+ break;
+ case 4:
+ /* SD contains only the ACL */
+
+ status=lsa_get_generic_sd(p->mem_ctx, &psd, &sd_size);
+ if(!NT_STATUS_IS_OK(status))
+ return NT_STATUS_NO_MEMORY;
+
+ if((r_u->buf = make_sec_desc_buf(p->mem_ctx, sd_size, psd)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+ break;
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ r_u->ptr=1;
+
+ return r_u->status;
+}
+
+
+NTSTATUS _lsa_query_info2(pipes_struct *p, LSA_Q_QUERY_INFO2 *q_u, LSA_R_QUERY_INFO2 *r_u)
+{
+ struct lsa_info *handle;
+ const char *nb_name;
+ char *dns_name = NULL;
+ char *forest_name = NULL;
+ DOM_SID *sid = NULL;
+ struct uuid guid;
+ fstring dnsdomname;
+
+ ZERO_STRUCT(guid);
+ r_u->status = NT_STATUS_OK;
+
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle))
+ return NT_STATUS_INVALID_HANDLE;
+
+ switch (q_u->info_class) {
+ case 0x0c:
+ /* check if the user have enough rights */
+ if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* Request PolicyPrimaryDomainInformation. */
+ switch (lp_server_role()) {
+ case ROLE_DOMAIN_PDC:
+ case ROLE_DOMAIN_BDC:
+ nb_name = get_global_sam_name();
+ /* ugly temp hack for these next two */
+
+ /* This should be a 'netbios domain -> DNS domain' mapping */
+ dnsdomname[0] = '\0';
+ get_mydnsdomname(dnsdomname);
+ strlower_m(dnsdomname);
+
+ dns_name = dnsdomname;
+ forest_name = dnsdomname;
+
+ sid = get_global_sam_sid();
+ secrets_fetch_domain_guid(lp_workgroup(), &guid);
+ break;
+ default:
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+ init_dns_dom_info(&r_u->info.dns_dom_info, nb_name, dns_name,
+ forest_name,&guid,sid);
+ break;
+ default:
+ DEBUG(0,("_lsa_query_info2: unknown info level in Lsa Query: %d\n", q_u->info_class));
+ r_u->status = NT_STATUS_INVALID_INFO_CLASS;
+ break;
+ }
+
+ if (NT_STATUS_IS_OK(r_u->status)) {
+ r_u->ptr = 0x1;
+ r_u->info_class = q_u->info_class;
+ }
+
+ return r_u->status;
+}
diff --git a/source/rpc_server/srv_netlog.c b/source/rpc_server/srv_netlog.c
new file mode 100644
index 00000000000..f06a2002e3c
--- /dev/null
+++ b/source/rpc_server/srv_netlog.c
@@ -0,0 +1,385 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997,
+ * Copyright (C) Jeremy Allison 1998-2001,
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This is the interface to the netlogon pipe. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*************************************************************************
+ api_net_req_chal:
+ *************************************************************************/
+
+static BOOL api_net_req_chal(pipes_struct *p)
+{
+ NET_Q_REQ_CHAL q_u;
+ NET_R_REQ_CHAL r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the challenge... */
+ if(!net_io_q_req_chal("", &q_u, data, 0)) {
+ DEBUG(0,("api_net_req_chal: Failed to unmarshall NET_Q_REQ_CHAL.\n"));
+ return False;
+ }
+
+ r_u.status = _net_req_chal(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!net_io_r_req_chal("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_net_req_chal: Failed to marshall NET_R_REQ_CHAL.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*************************************************************************
+ api_net_auth:
+ *************************************************************************/
+
+static BOOL api_net_auth(pipes_struct *p)
+{
+ NET_Q_AUTH q_u;
+ NET_R_AUTH r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the challenge... */
+ if(!net_io_q_auth("", &q_u, data, 0)) {
+ DEBUG(0,("api_net_auth: Failed to unmarshall NET_Q_AUTH.\n"));
+ return False;
+ }
+
+ r_u.status = _net_auth(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!net_io_r_auth("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_net_auth: Failed to marshall NET_R_AUTH.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*************************************************************************
+ api_net_auth_2:
+ *************************************************************************/
+
+static BOOL api_net_auth_2(pipes_struct *p)
+{
+ NET_Q_AUTH_2 q_u;
+ NET_R_AUTH_2 r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the challenge... */
+ if(!net_io_q_auth_2("", &q_u, data, 0)) {
+ DEBUG(0,("api_net_auth_2: Failed to unmarshall NET_Q_AUTH_2.\n"));
+ return False;
+ }
+
+ r_u.status = _net_auth_2(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!net_io_r_auth_2("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_net_auth_2: Failed to marshall NET_R_AUTH_2.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*************************************************************************
+ api_net_srv_pwset:
+ *************************************************************************/
+
+static BOOL api_net_srv_pwset(pipes_struct *p)
+{
+ NET_Q_SRV_PWSET q_u;
+ NET_R_SRV_PWSET r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the challenge and encrypted password ... */
+ if(!net_io_q_srv_pwset("", &q_u, data, 0)) {
+ DEBUG(0,("api_net_srv_pwset: Failed to unmarshall NET_Q_SRV_PWSET.\n"));
+ return False;
+ }
+
+ r_u.status = _net_srv_pwset(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!net_io_r_srv_pwset("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_net_srv_pwset: Failed to marshall NET_R_SRV_PWSET.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*************************************************************************
+ api_net_sam_logoff:
+ *************************************************************************/
+
+static BOOL api_net_sam_logoff(pipes_struct *p)
+{
+ NET_Q_SAM_LOGOFF q_u;
+ NET_R_SAM_LOGOFF r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!net_io_q_sam_logoff("", &q_u, data, 0)) {
+ DEBUG(0,("api_net_sam_logoff: Failed to unmarshall NET_Q_SAM_LOGOFF.\n"));
+ return False;
+ }
+
+ r_u.status = _net_sam_logoff(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!net_io_r_sam_logoff("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_net_sam_logoff: Failed to marshall NET_R_SAM_LOGOFF.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*************************************************************************
+ api_net_sam_logon:
+ *************************************************************************/
+
+static BOOL api_net_sam_logon(pipes_struct *p)
+{
+ NET_Q_SAM_LOGON q_u;
+ NET_R_SAM_LOGON r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!net_io_q_sam_logon("", &q_u, data, 0)) {
+ DEBUG(0, ("api_net_sam_logon: Failed to unmarshall NET_Q_SAM_LOGON.\n"));
+ return False;
+ }
+
+ r_u.status = _net_sam_logon(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!net_io_r_sam_logon("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_net_sam_logon: Failed to marshall NET_R_SAM_LOGON.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*************************************************************************
+ api_net_trust_dom_list:
+ *************************************************************************/
+
+static BOOL api_net_trust_dom_list(pipes_struct *p)
+{
+ NET_Q_TRUST_DOM_LIST q_u;
+ NET_R_TRUST_DOM_LIST r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ DEBUG(6,("api_net_trust_dom_list: %d\n", __LINE__));
+
+ /* grab the lsa trusted domain list query... */
+ if(!net_io_q_trust_dom("", &q_u, data, 0)) {
+ DEBUG(0,("api_net_trust_dom_list: Failed to unmarshall NET_Q_TRUST_DOM_LIST.\n"));
+ return False;
+ }
+
+ /* construct reply. */
+ r_u.status = _net_trust_dom_list(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!net_io_r_trust_dom("", &r_u, rdata, 0)) {
+ DEBUG(0,("net_reply_trust_dom_list: Failed to marshall NET_R_TRUST_DOM_LIST.\n"));
+ return False;
+ }
+
+ DEBUG(6,("api_net_trust_dom_list: %d\n", __LINE__));
+
+ return True;
+}
+
+/*************************************************************************
+ api_net_logon_ctrl2:
+ *************************************************************************/
+
+static BOOL api_net_logon_ctrl2(pipes_struct *p)
+{
+ NET_Q_LOGON_CTRL2 q_u;
+ NET_R_LOGON_CTRL2 r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ DEBUG(6,("api_net_logon_ctrl2: %d\n", __LINE__));
+
+ /* grab the lsa netlogon ctrl2 query... */
+ if(!net_io_q_logon_ctrl2("", &q_u, data, 0)) {
+ DEBUG(0,("api_net_logon_ctrl2: Failed to unmarshall NET_Q_LOGON_CTRL2.\n"));
+ return False;
+ }
+
+ r_u.status = _net_logon_ctrl2(p, &q_u, &r_u);
+
+ if(!net_io_r_logon_ctrl2("", &r_u, rdata, 0)) {
+ DEBUG(0,("net_reply_logon_ctrl2: Failed to marshall NET_R_LOGON_CTRL2.\n"));
+ return False;
+ }
+
+ DEBUG(6,("api_net_logon_ctrl2: %d\n", __LINE__));
+
+ return True;
+}
+
+/*************************************************************************
+ api_net_logon_ctrl:
+ *************************************************************************/
+
+static BOOL api_net_logon_ctrl(pipes_struct *p)
+{
+ NET_Q_LOGON_CTRL q_u;
+ NET_R_LOGON_CTRL r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ DEBUG(6,("api_net_logon_ctrl: %d\n", __LINE__));
+
+ /* grab the lsa netlogon ctrl query... */
+ if(!net_io_q_logon_ctrl("", &q_u, data, 0)) {
+ DEBUG(0,("api_net_logon_ctrl: Failed to unmarshall NET_Q_LOGON_CTRL.\n"));
+ return False;
+ }
+
+ r_u.status = _net_logon_ctrl(p, &q_u, &r_u);
+
+ if(!net_io_r_logon_ctrl("", &r_u, rdata, 0)) {
+ DEBUG(0,("net_reply_logon_ctrl2: Failed to marshall NET_R_LOGON_CTRL2.\n"));
+ return False;
+ }
+
+ DEBUG(6,("api_net_logon_ctrl2: %d\n", __LINE__));
+
+ return True;
+}
+
+/*************************************************************************
+ api_ds_enum_dom_trusts:
+ *************************************************************************/
+
+#if 0 /* JERRY */
+static BOOL api_ds_enum_dom_trusts(pipes_struct *p)
+{
+ DS_Q_ENUM_DOM_TRUSTS q_u;
+ DS_R_ENUM_DOM_TRUSTS r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ DEBUG(6,("api_ds_enum_dom_trusts\n"));
+
+ if ( !ds_io_q_enum_domain_trusts("", data, 0, &q_u) ) {
+ DEBUG(0,("api_ds_enum_domain_trusts: Failed to unmarshall DS_Q_ENUM_DOM_TRUSTS.\n"));
+ return False;
+ }
+
+ r_u.status = _ds_enum_dom_trusts(p, &q_u, &r_u);
+
+ if ( !ds_io_r_enum_domain_trusts("", rdata, 0, &r_u) ) {
+ DEBUG(0,("api_ds_enum_domain_trusts: Failed to marshall DS_R_ENUM_DOM_TRUSTS.\n"));
+ return False;
+ }
+
+ DEBUG(6,("api_ds_enum_dom_trusts\n"));
+
+ return True;
+}
+#endif /* JERRY */
+
+/*******************************************************************
+ array of \PIPE\NETLOGON operations
+ ********************************************************************/
+static struct api_struct api_net_cmds [] =
+ {
+ { "NET_REQCHAL" , NET_REQCHAL , api_net_req_chal },
+ { "NET_AUTH" , NET_AUTH , api_net_auth },
+ { "NET_AUTH2" , NET_AUTH2 , api_net_auth_2 },
+ { "NET_SRVPWSET" , NET_SRVPWSET , api_net_srv_pwset },
+ { "NET_SAMLOGON" , NET_SAMLOGON , api_net_sam_logon },
+ { "NET_SAMLOGOFF" , NET_SAMLOGOFF , api_net_sam_logoff },
+ { "NET_LOGON_CTRL2" , NET_LOGON_CTRL2 , api_net_logon_ctrl2 },
+ { "NET_TRUST_DOM_LIST", NET_TRUST_DOM_LIST, api_net_trust_dom_list },
+ { "NET_LOGON_CTRL" , NET_LOGON_CTRL , api_net_logon_ctrl },
+#if 0 /* JERRY */
+ { "DS_ENUM_DOM_TRUSTS", DS_ENUM_DOM_TRUSTS, api_ds_enum_dom_trusts }
+#endif /* JERRY */
+ };
+
+void netlog_get_pipe_fns( struct api_struct **fns, int *n_fns )
+{
+ *fns = api_net_cmds;
+ *n_fns = sizeof(api_net_cmds) / sizeof(struct api_struct);
+}
+
+NTSTATUS rpc_net_init(void)
+{
+ return rpc_pipe_register_commands(SMB_RPC_INTERFACE_VERSION, "NETLOGON", "lsass", api_net_cmds,
+ sizeof(api_net_cmds) / sizeof(struct api_struct));
+}
diff --git a/source/rpc_server/srv_netlog_nt.c b/source/rpc_server/srv_netlog_nt.c
new file mode 100644
index 00000000000..51ed79980c7
--- /dev/null
+++ b/source/rpc_server/srv_netlog_nt.c
@@ -0,0 +1,795 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997.
+ * Copyright (C) Jeremy Allison 1998-2001.
+ * Copyright (C) Andrew Bartlett 2001.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This is the implementation of the netlogon pipe. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*************************************************************************
+ init_net_r_req_chal:
+ *************************************************************************/
+
+static void init_net_r_req_chal(NET_R_REQ_CHAL *r_c,
+ DOM_CHAL *srv_chal, NTSTATUS status)
+{
+ DEBUG(6,("init_net_r_req_chal: %d\n", __LINE__));
+ memcpy(r_c->srv_chal.data, srv_chal->data, sizeof(srv_chal->data));
+ r_c->status = status;
+}
+
+/*************************************************************************
+ error messages cropping up when using nltest.exe...
+ *************************************************************************/
+
+#define ERROR_NO_SUCH_DOMAIN 0x54b
+#define ERROR_NO_LOGON_SERVERS 0x51f
+
+/*************************************************************************
+ net_reply_logon_ctrl:
+ *************************************************************************/
+
+/* Some flag values reverse engineered from NLTEST.EXE */
+
+#define LOGON_CTRL_IN_SYNC 0x00
+#define LOGON_CTRL_REPL_NEEDED 0x01
+#define LOGON_CTRL_REPL_IN_PROGRESS 0x02
+
+NTSTATUS _net_logon_ctrl(pipes_struct *p, NET_Q_LOGON_CTRL *q_u,
+ NET_R_LOGON_CTRL *r_u)
+{
+ uint32 flags = 0x0;
+ uint32 pdc_connection_status = 0x00; /* Maybe a win32 error code? */
+
+ /* Setup the Logon Control response */
+
+ init_net_r_logon_ctrl(r_u, q_u->query_level, flags,
+ pdc_connection_status);
+
+ return r_u->status;
+}
+
+/****************************************************************************
+Send a message to smbd to do a sam synchronisation
+**************************************************************************/
+static void send_sync_message(void)
+{
+ TDB_CONTEXT *tdb;
+
+ tdb = tdb_open_log(lock_path("connections.tdb"), 0,
+ TDB_DEFAULT, O_RDONLY, 0);
+
+ if (!tdb) {
+ DEBUG(3, ("send_sync_message(): failed to open connections "
+ "database\n"));
+ return;
+ }
+
+ DEBUG(3, ("sending sam synchronisation message\n"));
+
+ message_send_all(tdb, MSG_SMB_SAM_SYNC, NULL, 0, False, NULL);
+
+ tdb_close(tdb);
+}
+
+/*************************************************************************
+ net_reply_logon_ctrl2:
+ *************************************************************************/
+
+NTSTATUS _net_logon_ctrl2(pipes_struct *p, NET_Q_LOGON_CTRL2 *q_u, NET_R_LOGON_CTRL2 *r_u)
+{
+ uint32 flags = 0x0;
+ uint32 pdc_connection_status = 0x0;
+ uint32 logon_attempts = 0x0;
+ uint32 tc_status = ERROR_NO_LOGON_SERVERS;
+ const char *trusted_domain = "test_domain";
+
+ DEBUG(0, ("*** net long ctrl2 %d, %d, %d\n",
+ q_u->function_code, q_u->query_level, q_u->switch_value));
+
+ DEBUG(6,("_net_logon_ctrl2: %d\n", __LINE__));
+
+
+ /* set up the Logon Control2 response */
+ init_net_r_logon_ctrl2(r_u, q_u->query_level,
+ flags, pdc_connection_status, logon_attempts,
+ tc_status, trusted_domain);
+
+ if (lp_server_role() == ROLE_DOMAIN_BDC)
+ send_sync_message();
+
+ DEBUG(6,("_net_logon_ctrl2: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*************************************************************************
+ net_reply_trust_dom_list:
+ *************************************************************************/
+
+NTSTATUS _net_trust_dom_list(pipes_struct *p, NET_Q_TRUST_DOM_LIST *q_u, NET_R_TRUST_DOM_LIST *r_u)
+{
+ const char *trusted_domain = "test_domain";
+ uint32 num_trust_domains = 1;
+
+ DEBUG(6,("_net_trust_dom_list: %d\n", __LINE__));
+
+ /* set up the Trusted Domain List response */
+ init_r_trust_dom(r_u, num_trust_domains, trusted_domain);
+
+ DEBUG(6,("_net_trust_dom_list: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/***********************************************************************************
+ init_net_r_srv_pwset:
+ ***********************************************************************************/
+
+static void init_net_r_srv_pwset(NET_R_SRV_PWSET *r_s,
+ DOM_CRED *srv_cred, NTSTATUS status)
+{
+ DEBUG(5,("init_net_r_srv_pwset: %d\n", __LINE__));
+
+ memcpy(&r_s->srv_cred, srv_cred, sizeof(r_s->srv_cred));
+ r_s->status = status;
+
+ DEBUG(5,("init_net_r_srv_pwset: %d\n", __LINE__));
+}
+
+/******************************************************************
+ gets a machine password entry. checks access rights of the host.
+ ******************************************************************/
+
+static BOOL get_md4pw(char *md4pw, char *mach_acct)
+{
+ SAM_ACCOUNT *sampass = NULL;
+ const uint8 *pass;
+ BOOL ret;
+ uint32 acct_ctrl;
+
+#if 0
+ /*
+ * Currently this code is redundent as we already have a filter
+ * by hostname list. What this code really needs to do is to
+ * get a hosts allowed/hosts denied list from the SAM database
+ * on a per user basis, and make the access decision there.
+ * I will leave this code here for now as a reminder to implement
+ * this at a later date. JRA.
+ */
+
+ if (!allow_access(lp_domain_hostsdeny(), lp_domain_hostsallow(),
+ client_name(), client_addr()))
+ {
+ DEBUG(0,("get_md4pw: Workstation %s denied access to domain\n", mach_acct));
+ return False;
+ }
+#endif /* 0 */
+
+ if(!NT_STATUS_IS_OK(pdb_init_sam(&sampass)))
+ return False;
+
+ /* JRA. This is ok as it is only used for generating the challenge. */
+ become_root();
+ ret=pdb_getsampwnam(sampass, mach_acct);
+ unbecome_root();
+
+ if (ret==False) {
+ DEBUG(0,("get_md4pw: Workstation %s: no account in domain\n", mach_acct));
+ pdb_free_sam(&sampass);
+ return False;
+ }
+
+ acct_ctrl = pdb_get_acct_ctrl(sampass);
+ if (!(acct_ctrl & ACB_DISABLED) &&
+ ((acct_ctrl & ACB_DOMTRUST) ||
+ (acct_ctrl & ACB_WSTRUST) ||
+ (acct_ctrl & ACB_SVRTRUST)) &&
+ ((pass=pdb_get_nt_passwd(sampass)) != NULL)) {
+ memcpy(md4pw, pass, 16);
+ dump_data(5, md4pw, 16);
+ pdb_free_sam(&sampass);
+ return True;
+ }
+
+ DEBUG(0,("get_md4pw: Workstation %s: no account in domain\n", mach_acct));
+ pdb_free_sam(&sampass);
+ return False;
+
+}
+
+/*************************************************************************
+ _net_req_chal
+ *************************************************************************/
+
+NTSTATUS _net_req_chal(pipes_struct *p, NET_Q_REQ_CHAL *q_u, NET_R_REQ_CHAL *r_u)
+{
+ NTSTATUS status = NT_STATUS_OK;
+
+ rpcstr_pull(p->dc.remote_machine,q_u->uni_logon_clnt.buffer,sizeof(fstring),q_u->uni_logon_clnt.uni_str_len*2,0);
+
+ /* create a server challenge for the client */
+ /* Set these to random values. */
+ generate_random_buffer(p->dc.srv_chal.data, 8, False);
+
+ memcpy(p->dc.srv_cred.challenge.data, p->dc.srv_chal.data, 8);
+
+ memcpy(p->dc.clnt_chal.data , q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data));
+ memcpy(p->dc.clnt_cred.challenge.data, q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data));
+
+ memset((char *)p->dc.sess_key, '\0', sizeof(p->dc.sess_key));
+
+ p->dc.challenge_sent = True;
+ /* set up the LSA REQUEST CHALLENGE response */
+ init_net_r_req_chal(r_u, &p->dc.srv_chal, status);
+
+ return status;
+}
+
+/*************************************************************************
+ init_net_r_auth:
+ *************************************************************************/
+
+static void init_net_r_auth(NET_R_AUTH *r_a, DOM_CHAL *resp_cred, NTSTATUS status)
+{
+ memcpy(r_a->srv_chal.data, resp_cred->data, sizeof(resp_cred->data));
+ r_a->status = status;
+}
+
+/*************************************************************************
+ _net_auth
+ *************************************************************************/
+
+NTSTATUS _net_auth(pipes_struct *p, NET_Q_AUTH *q_u, NET_R_AUTH *r_u)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ DOM_CHAL srv_cred;
+ UTIME srv_time;
+ fstring mach_acct;
+
+ srv_time.time = 0;
+
+ rpcstr_pull(mach_acct, q_u->clnt_id.uni_acct_name.buffer,sizeof(fstring),q_u->clnt_id.uni_acct_name.uni_str_len*2,0);
+
+ if (p->dc.challenge_sent && get_md4pw((char *)p->dc.md4pw, mach_acct)) {
+
+ /* from client / server challenges and md4 password, generate sess key */
+ cred_session_key(&p->dc.clnt_chal, &p->dc.srv_chal,
+ p->dc.md4pw, p->dc.sess_key);
+
+ /* check that the client credentials are valid */
+ if (cred_assert(&q_u->clnt_chal, p->dc.sess_key, &p->dc.clnt_cred.challenge, srv_time)) {
+
+ /* create server challenge for inclusion in the reply */
+ cred_create(p->dc.sess_key, &p->dc.srv_cred.challenge, srv_time, &srv_cred);
+
+ /* copy the received client credentials for use next time */
+ memcpy(p->dc.clnt_cred.challenge.data, q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data));
+ memcpy(p->dc.srv_cred .challenge.data, q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data));
+
+ /* Save the machine account name. */
+ fstrcpy(p->dc.mach_acct, mach_acct);
+
+ p->dc.authenticated = True;
+
+ } else {
+ status = NT_STATUS_ACCESS_DENIED;
+ }
+ } else {
+ status = NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* set up the LSA AUTH response */
+ init_net_r_auth(r_u, &srv_cred, status);
+
+ return r_u->status;
+}
+
+/*************************************************************************
+ init_net_r_auth_2:
+ *************************************************************************/
+
+static void init_net_r_auth_2(NET_R_AUTH_2 *r_a,
+ DOM_CHAL *resp_cred, NEG_FLAGS *flgs, NTSTATUS status)
+{
+ memcpy(r_a->srv_chal.data, resp_cred->data, sizeof(resp_cred->data));
+ memcpy(&r_a->srv_flgs, flgs, sizeof(r_a->srv_flgs));
+ r_a->status = status;
+}
+
+/*************************************************************************
+ _net_auth_2
+ *************************************************************************/
+
+NTSTATUS _net_auth_2(pipes_struct *p, NET_Q_AUTH_2 *q_u, NET_R_AUTH_2 *r_u)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ DOM_CHAL srv_cred;
+ UTIME srv_time;
+ NEG_FLAGS srv_flgs;
+ fstring mach_acct;
+
+ srv_time.time = 0;
+
+ if ( (lp_server_schannel() == True) &&
+ ((q_u->clnt_flgs.neg_flags & NETLOGON_NEG_SCHANNEL) == 0) ) {
+
+ /* schannel must be used, but client did not offer it. */
+ status = NT_STATUS_ACCESS_DENIED;
+ }
+
+ rpcstr_pull(mach_acct, q_u->clnt_id.uni_acct_name.buffer,sizeof(fstring),q_u->clnt_id.uni_acct_name.uni_str_len*2,0);
+
+ if (p->dc.challenge_sent && get_md4pw((char *)p->dc.md4pw, mach_acct)) {
+
+ /* from client / server challenges and md4 password, generate sess key */
+ cred_session_key(&p->dc.clnt_chal, &p->dc.srv_chal,
+ p->dc.md4pw, p->dc.sess_key);
+
+ /* check that the client credentials are valid */
+ if (cred_assert(&q_u->clnt_chal, p->dc.sess_key, &p->dc.clnt_cred.challenge, srv_time)) {
+
+ /* create server challenge for inclusion in the reply */
+ cred_create(p->dc.sess_key, &p->dc.srv_cred.challenge, srv_time, &srv_cred);
+
+ /* copy the received client credentials for use next time */
+ memcpy(p->dc.clnt_cred.challenge.data, q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data));
+ memcpy(p->dc.srv_cred .challenge.data, q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data));
+
+ /* Save the machine account name. */
+ fstrcpy(p->dc.mach_acct, mach_acct);
+
+ p->dc.authenticated = True;
+
+ } else {
+ status = NT_STATUS_ACCESS_DENIED;
+ }
+ } else {
+ status = NT_STATUS_ACCESS_DENIED;
+ }
+
+ srv_flgs.neg_flags = 0x000001ff;
+
+ if (lp_server_schannel() != False) {
+ srv_flgs.neg_flags |= NETLOGON_NEG_SCHANNEL;
+ }
+
+ /* set up the LSA AUTH 2 response */
+ init_net_r_auth_2(r_u, &srv_cred, &srv_flgs, status);
+
+ if (NT_STATUS_IS_OK(status)) {
+ extern struct dcinfo last_dcinfo;
+ last_dcinfo = p->dc;
+ }
+
+ return r_u->status;
+}
+
+/*************************************************************************
+ _net_srv_pwset
+ *************************************************************************/
+
+NTSTATUS _net_srv_pwset(pipes_struct *p, NET_Q_SRV_PWSET *q_u, NET_R_SRV_PWSET *r_u)
+{
+ NTSTATUS status = NT_STATUS_ACCESS_DENIED;
+ DOM_CRED srv_cred;
+ pstring workstation;
+ SAM_ACCOUNT *sampass=NULL;
+ BOOL ret = False;
+ unsigned char pwd[16];
+ int i;
+ uint32 acct_ctrl;
+
+ /* checks and updates credentials. creates reply credentials */
+ if (!(p->dc.authenticated && deal_with_creds(p->dc.sess_key, &p->dc.clnt_cred, &q_u->clnt_id.cred, &srv_cred)))
+ return NT_STATUS_INVALID_HANDLE;
+
+ memcpy(&p->dc.srv_cred, &p->dc.clnt_cred, sizeof(p->dc.clnt_cred));
+
+ DEBUG(5,("_net_srv_pwset: %d\n", __LINE__));
+
+ rpcstr_pull(workstation,q_u->clnt_id.login.uni_comp_name.buffer,
+ sizeof(workstation),q_u->clnt_id.login.uni_comp_name.uni_str_len*2,0);
+
+ DEBUG(3,("Server Password Set by Wksta:[%s] on account [%s]\n", workstation, p->dc.mach_acct));
+
+ pdb_init_sam(&sampass);
+
+ become_root();
+ ret=pdb_getsampwnam(sampass, p->dc.mach_acct);
+ unbecome_root();
+
+ /* Ensure the account exists and is a machine account. */
+
+ acct_ctrl = pdb_get_acct_ctrl(sampass);
+
+ if (!(ret
+ && (acct_ctrl & ACB_WSTRUST ||
+ acct_ctrl & ACB_SVRTRUST ||
+ acct_ctrl & ACB_DOMTRUST))) {
+ pdb_free_sam(&sampass);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (pdb_get_acct_ctrl(sampass) & ACB_DISABLED) {
+ pdb_free_sam(&sampass);
+ return NT_STATUS_ACCOUNT_DISABLED;
+ }
+
+ DEBUG(100,("Server password set : new given value was :\n"));
+ for(i = 0; i < 16; i++)
+ DEBUG(100,("%02X ", q_u->pwd[i]));
+ DEBUG(100,("\n"));
+
+ cred_hash3( pwd, q_u->pwd, p->dc.sess_key, 0);
+
+ /* lies! nt and lm passwords are _not_ the same: don't care */
+ if (!pdb_set_lanman_passwd (sampass, pwd, PDB_CHANGED)) {
+ pdb_free_sam(&sampass);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_nt_passwd (sampass, pwd, PDB_CHANGED)) {
+ pdb_free_sam(&sampass);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_pass_changed_now (sampass)) {
+ pdb_free_sam(&sampass);
+ /* Not quite sure what this one qualifies as, but this will do */
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ become_root();
+ ret = pdb_update_sam_account (sampass);
+ unbecome_root();
+
+ if (ret)
+ status = NT_STATUS_OK;
+
+ /* set up the LSA Server Password Set response */
+ init_net_r_srv_pwset(r_u, &srv_cred, status);
+
+ pdb_free_sam(&sampass);
+ return r_u->status;
+}
+
+
+/*************************************************************************
+ _net_sam_logoff:
+ *************************************************************************/
+
+NTSTATUS _net_sam_logoff(pipes_struct *p, NET_Q_SAM_LOGOFF *q_u, NET_R_SAM_LOGOFF *r_u)
+{
+ DOM_CRED srv_cred;
+
+ if (!get_valid_user_struct(p->vuid))
+ return NT_STATUS_NO_SUCH_USER;
+
+ /* checks and updates credentials. creates reply credentials */
+ if (!(p->dc.authenticated && deal_with_creds(p->dc.sess_key, &p->dc.clnt_cred,
+ &q_u->sam_id.client.cred, &srv_cred)))
+ return NT_STATUS_INVALID_HANDLE;
+
+ memcpy(&p->dc.srv_cred, &p->dc.clnt_cred, sizeof(p->dc.clnt_cred));
+
+ /* XXXX maybe we want to say 'no', reject the client's credentials */
+ r_u->buffer_creds = 1; /* yes, we have valid server credentials */
+ memcpy(&r_u->srv_creds, &srv_cred, sizeof(r_u->srv_creds));
+
+ r_u->status = NT_STATUS_OK;
+
+ return r_u->status;
+}
+
+
+/*************************************************************************
+ _net_sam_logon
+ *************************************************************************/
+
+NTSTATUS _net_sam_logon(pipes_struct *p, NET_Q_SAM_LOGON *q_u, NET_R_SAM_LOGON *r_u)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ NET_USER_INFO_3 *usr_info = NULL;
+ NET_ID_INFO_CTR *ctr = q_u->sam_id.ctr;
+ DOM_CRED srv_cred;
+ UNISTR2 *uni_samlogon_user = NULL;
+ UNISTR2 *uni_samlogon_domain = NULL;
+ UNISTR2 *uni_samlogon_workstation = NULL;
+ fstring nt_username, nt_domain, nt_workstation;
+ auth_usersupplied_info *user_info = NULL;
+ auth_serversupplied_info *server_info = NULL;
+ extern userdom_struct current_user_info;
+ SAM_ACCOUNT *sampw;
+ struct auth_context *auth_context = NULL;
+
+ usr_info = (NET_USER_INFO_3 *)talloc(p->mem_ctx, sizeof(NET_USER_INFO_3));
+ if (!usr_info)
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(usr_info);
+
+ /* store the user information, if there is any. */
+ r_u->user = usr_info;
+ r_u->switch_value = 0; /* indicates no info */
+ r_u->auth_resp = 1; /* authoritative response */
+ r_u->switch_value = 3; /* indicates type of validation user info */
+
+ if (!get_valid_user_struct(p->vuid))
+ return NT_STATUS_NO_SUCH_USER;
+
+
+ if ( (lp_server_schannel() == True) && (!p->netsec_auth_validated) ) {
+ /* 'server schannel = yes' should enforce use of
+ schannel, the client did offer it in auth2, but
+ obviously did not use it. */
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* checks and updates credentials. creates reply credentials */
+ if (!(p->dc.authenticated && deal_with_creds(p->dc.sess_key, &p->dc.clnt_cred, &q_u->sam_id.client.cred, &srv_cred)))
+ return NT_STATUS_INVALID_HANDLE;
+
+ memcpy(&p->dc.srv_cred, &p->dc.clnt_cred, sizeof(p->dc.clnt_cred));
+
+ r_u->buffer_creds = 1; /* yes, we have valid server credentials */
+ memcpy(&r_u->srv_creds, &srv_cred, sizeof(r_u->srv_creds));
+
+ /* find the username */
+
+ switch (q_u->sam_id.logon_level) {
+ case INTERACTIVE_LOGON_TYPE:
+ uni_samlogon_user = &ctr->auth.id1.uni_user_name;
+ uni_samlogon_domain = &ctr->auth.id1.uni_domain_name;
+
+ uni_samlogon_workstation = &ctr->auth.id1.uni_wksta_name;
+
+ DEBUG(3,("SAM Logon (Interactive). Domain:[%s]. ", lp_workgroup()));
+ break;
+ case NET_LOGON_TYPE:
+ uni_samlogon_user = &ctr->auth.id2.uni_user_name;
+ uni_samlogon_domain = &ctr->auth.id2.uni_domain_name;
+ uni_samlogon_workstation = &ctr->auth.id2.uni_wksta_name;
+
+ DEBUG(3,("SAM Logon (Network). Domain:[%s]. ", lp_workgroup()));
+ break;
+ default:
+ DEBUG(2,("SAM Logon: unsupported switch value\n"));
+ return NT_STATUS_INVALID_INFO_CLASS;
+ } /* end switch */
+
+ rpcstr_pull(nt_username,uni_samlogon_user->buffer,sizeof(nt_username),uni_samlogon_user->uni_str_len*2,0);
+ rpcstr_pull(nt_domain,uni_samlogon_domain->buffer,sizeof(nt_domain),uni_samlogon_domain->uni_str_len*2,0);
+ rpcstr_pull(nt_workstation,uni_samlogon_workstation->buffer,sizeof(nt_workstation),uni_samlogon_workstation->uni_str_len*2,0);
+
+ DEBUG(3,("User:[%s@%s] Requested Domain:[%s]\n", nt_username,
+ nt_workstation, nt_domain));
+
+ fstrcpy(current_user_info.smb_name, nt_username);
+ sub_set_smb_name(nt_username);
+
+ DEBUG(5,("Attempting validation level %d for unmapped username %s.\n", q_u->sam_id.ctr->switch_value, nt_username));
+
+ status = NT_STATUS_OK;
+
+ switch (ctr->switch_value) {
+ case NET_LOGON_TYPE:
+ {
+ const char *wksname = nt_workstation;
+
+ if (!NT_STATUS_IS_OK(status = make_auth_context_fixed(&auth_context, ctr->auth.id2.lm_chal))) {
+ return status;
+ }
+
+ /* For a network logon, the workstation name comes in with two
+ * backslashes in the front. Strip them if they are there. */
+
+ if (*wksname == '\\') wksname++;
+ if (*wksname == '\\') wksname++;
+
+ /* Standard challenge/response authenticaion */
+ if (!make_user_info_netlogon_network(&user_info,
+ nt_username, nt_domain,
+ wksname,
+ ctr->auth.id2.lm_chal_resp.buffer,
+ ctr->auth.id2.lm_chal_resp.str_str_len,
+ ctr->auth.id2.nt_chal_resp.buffer,
+ ctr->auth.id2.nt_chal_resp.str_str_len)) {
+ status = NT_STATUS_NO_MEMORY;
+ }
+ break;
+ }
+ case INTERACTIVE_LOGON_TYPE:
+ /* 'Interactive' autheticaion, supplies the password in its
+ MD4 form, encrypted with the session key. We will
+ convert this to chellange/responce for the auth
+ subsystem to chew on */
+ {
+ const uint8 *chal;
+
+ if (!NT_STATUS_IS_OK(status = make_auth_context_subsystem(&auth_context))) {
+ return status;
+ }
+
+ chal = auth_context->get_ntlm_challenge(auth_context);
+
+ if (!make_user_info_netlogon_interactive(&user_info,
+ nt_username, nt_domain,
+ nt_workstation, chal,
+ ctr->auth.id1.lm_owf.data,
+ ctr->auth.id1.nt_owf.data,
+ p->dc.sess_key)) {
+ status = NT_STATUS_NO_MEMORY;
+ }
+ break;
+ }
+ default:
+ DEBUG(2,("SAM Logon: unsupported switch value\n"));
+ return NT_STATUS_INVALID_INFO_CLASS;
+ } /* end switch */
+
+ if ( NT_STATUS_IS_OK(status) ) {
+ status = auth_context->check_ntlm_password(auth_context,
+ user_info, &server_info);
+ }
+
+ (auth_context->free)(&auth_context);
+ free_user_info(&user_info);
+
+ DEBUG(5, ("_net_sam_logon: check_password returned status %s\n",
+ nt_errstr(status)));
+
+ /* Check account and password */
+
+ if (!NT_STATUS_IS_OK(status)) {
+ free_server_info(&server_info);
+ return status;
+ }
+
+ if (server_info->guest) {
+ /* We don't like guest domain logons... */
+ DEBUG(5,("_net_sam_logon: Attempted domain logon as GUEST denied.\n"));
+ free_server_info(&server_info);
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ /* This is the point at which, if the login was successful, that
+ the SAM Local Security Authority should record that the user is
+ logged in to the domain. */
+
+ {
+ DOM_GID *gids = NULL;
+ const DOM_SID *user_sid = NULL;
+ const DOM_SID *group_sid = NULL;
+ DOM_SID domain_sid;
+ uint32 user_rid, group_rid;
+
+ int num_gids = 0;
+ pstring my_name;
+ fstring user_sid_string;
+ fstring group_sid_string;
+ uchar nt_session_key[16];
+ uchar lm_session_key[16];
+ uchar netlogon_sess_key[16];
+
+ sampw = server_info->sam_account;
+
+ /* set up pointer indicating user/password failed to be found */
+ usr_info->ptr_user_info = 0;
+
+ user_sid = pdb_get_user_sid(sampw);
+ group_sid = pdb_get_group_sid(sampw);
+
+ sid_copy(&domain_sid, user_sid);
+ sid_split_rid(&domain_sid, &user_rid);
+
+ if (!sid_peek_check_rid(&domain_sid, group_sid, &group_rid)) {
+ DEBUG(1, ("_net_sam_logon: user %s\\%s has user sid %s\n but group sid %s.\nThe conflicting domain portions are not supported for NETLOGON calls\n",
+ pdb_get_domain(sampw), pdb_get_username(sampw),
+ sid_to_string(user_sid_string, user_sid),
+ sid_to_string(group_sid_string, group_sid)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ pstrcpy(my_name, global_myname());
+
+ if (!NT_STATUS_IS_OK(status
+ = nt_token_to_group_list(p->mem_ctx,
+ &domain_sid,
+ server_info->ptok,
+ &num_gids,
+ &gids))) {
+ return status;
+ }
+
+ ZERO_STRUCT(netlogon_sess_key);
+ memcpy(netlogon_sess_key, p->dc.sess_key, 8);
+ if (server_info->nt_session_key.length) {
+ memcpy(nt_session_key, server_info->nt_session_key.data,
+ MIN(sizeof(nt_session_key), server_info->nt_session_key.length));
+ SamOEMhash(nt_session_key, netlogon_sess_key, 16);
+ }
+ if (server_info->lm_session_key.length) {
+ memcpy(lm_session_key, server_info->lm_session_key.data,
+ MIN(sizeof(lm_session_key), server_info->lm_session_key.length));
+ SamOEMhash(lm_session_key, netlogon_sess_key, 16);
+ }
+ ZERO_STRUCT(netlogon_sess_key);
+
+ init_net_user_info3(p->mem_ctx, usr_info,
+ user_rid,
+ group_rid,
+ pdb_get_username(sampw),
+ pdb_get_fullname(sampw),
+ pdb_get_homedir(sampw),
+ pdb_get_dir_drive(sampw),
+ pdb_get_logon_script(sampw),
+ pdb_get_profile_path(sampw),
+ pdb_get_logon_time(sampw),
+ get_time_t_max(),
+ get_time_t_max(),
+ pdb_get_pass_last_set_time(sampw),
+ pdb_get_pass_can_change_time(sampw),
+ pdb_get_pass_must_change_time(sampw),
+
+ 0, /* logon_count */
+ 0, /* bad_pw_count */
+ num_gids, /* uint32 num_groups */
+ gids , /* DOM_GID *gids */
+ 0x20 , /* uint32 user_flgs (?) */
+ server_info->nt_session_key.length ? nt_session_key : NULL,
+ server_info->lm_session_key.length ? lm_session_key : NULL,
+ my_name , /* char *logon_srv */
+ pdb_get_domain(sampw),
+ &domain_sid, /* DOM_SID *dom_sid */
+ /* Should be users domain sid, not servers - for trusted domains */
+
+ NULL); /* char *other_sids */
+ ZERO_STRUCT(nt_session_key);
+ ZERO_STRUCT(lm_session_key);
+ }
+ free_server_info(&server_info);
+ return status;
+}
+
+/*************************************************************************
+ _ds_enum_dom_trusts
+ *************************************************************************/
+#if 0 /* JERRY -- not correct */
+NTSTATUS _ds_enum_dom_trusts(pipes_struct *p, DS_Q_ENUM_DOM_TRUSTS *q_u,
+ DS_R_ENUM_DOM_TRUSTS *r_u)
+{
+ NTSTATUS status = NT_STATUS_OK;
+
+ /* TODO: According to MSDN, the can only be executed against a
+ DC or domain member running Windows 2000 or later. Need
+ to test against a standalone 2k server and see what it
+ does. A windows 2000 DC includes its own domain in the
+ list. --jerry */
+
+ return status;
+}
+#endif /* JERRY */
diff --git a/source/rpc_server/srv_pipe.c b/source/rpc_server/srv_pipe.c
new file mode 100644
index 00000000000..90c20a97fa6
--- /dev/null
+++ b/source/rpc_server/srv_pipe.c
@@ -0,0 +1,1625 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1998
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ * Copyright (C) Paul Ashton 1997-1998,
+ * Copyright (C) Jeremy Allison 1999,
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* this module apparently provides an implementation of DCE/RPC over a
+ * named pipe (IPC$ connection using SMBtrans). details of DCE/RPC
+ * documentation are available (in on-line form) from the X-Open group.
+ *
+ * this module should provide a level of abstraction between SMB
+ * and DCE/RPC, while minimising the amount of mallocs, unnecessary
+ * data copies, and network traffic.
+ *
+ * in this version, which takes a "let's learn what's going on and
+ * get something running" approach, there is additional network
+ * traffic generated, but the code should be easier to understand...
+ *
+ * ... if you read the docs. or stare at packets for weeks on end.
+ *
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*************************************************************
+ HACK Alert!
+ We need to transfer the session key from one rpc bind to the
+ next. This is the way the netlogon schannel works.
+**************************************************************/
+struct dcinfo last_dcinfo;
+
+static void NTLMSSPcalc_p( pipes_struct *p, unsigned char *data, int len)
+{
+ unsigned char *hash = p->ntlmssp_hash;
+ unsigned char index_i = hash[256];
+ unsigned char index_j = hash[257];
+ int ind;
+
+ for( ind = 0; ind < len; ind++) {
+ unsigned char tc;
+ unsigned char t;
+
+ index_i++;
+ index_j += hash[index_i];
+
+ tc = hash[index_i];
+ hash[index_i] = hash[index_j];
+ hash[index_j] = tc;
+
+ t = hash[index_i] + hash[index_j];
+ data[ind] = data[ind] ^ hash[t];
+ }
+
+ hash[256] = index_i;
+ hash[257] = index_j;
+}
+
+/*******************************************************************
+ Generate the next PDU to be returned from the data in p->rdata.
+ We cheat here as this function doesn't handle the special auth
+ footers of the authenticated bind response reply.
+ ********************************************************************/
+
+BOOL create_next_pdu(pipes_struct *p)
+{
+ RPC_HDR_RESP hdr_resp;
+ BOOL auth_verify = ((p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SIGN) != 0);
+ BOOL auth_seal = ((p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SEAL) != 0);
+ uint32 data_len;
+ uint32 data_space_available;
+ uint32 data_len_left;
+ prs_struct outgoing_pdu;
+ uint32 data_pos;
+
+ /*
+ * If we're in the fault state, keep returning fault PDU's until
+ * the pipe gets closed. JRA.
+ */
+
+ if(p->fault_state) {
+ setup_fault_pdu(p, NT_STATUS(0x1c010002));
+ return True;
+ }
+
+ memset((char *)&hdr_resp, '\0', sizeof(hdr_resp));
+
+ /* Change the incoming request header to a response. */
+ p->hdr.pkt_type = RPC_RESPONSE;
+
+ /* Set up rpc header flags. */
+ if (p->out_data.data_sent_length == 0)
+ p->hdr.flags = RPC_FLG_FIRST;
+ else
+ p->hdr.flags = 0;
+
+ /*
+ * Work out how much we can fit in a single PDU.
+ */
+
+ data_space_available = sizeof(p->out_data.current_pdu) - RPC_HEADER_LEN - RPC_HDR_RESP_LEN;
+ if(p->ntlmssp_auth_validated)
+ data_space_available -= (RPC_HDR_AUTH_LEN + RPC_AUTH_NTLMSSP_CHK_LEN);
+
+ if(p->netsec_auth_validated)
+ data_space_available -= (RPC_HDR_AUTH_LEN + RPC_AUTH_NETSEC_CHK_LEN);
+
+ /*
+ * The amount we send is the minimum of the available
+ * space and the amount left to send.
+ */
+
+ data_len_left = prs_offset(&p->out_data.rdata) - p->out_data.data_sent_length;
+
+ /*
+ * Ensure there really is data left to send.
+ */
+
+ if(!data_len_left) {
+ DEBUG(0,("create_next_pdu: no data left to send !\n"));
+ return False;
+ }
+
+ data_len = MIN(data_len_left, data_space_available);
+
+ /*
+ * Set up the alloc hint. This should be the data left to
+ * send.
+ */
+
+ hdr_resp.alloc_hint = data_len_left;
+
+ /*
+ * Set up the header lengths.
+ */
+
+ if (p->ntlmssp_auth_validated) {
+ p->hdr.frag_len = RPC_HEADER_LEN + RPC_HDR_RESP_LEN + data_len +
+ RPC_HDR_AUTH_LEN + RPC_AUTH_NTLMSSP_CHK_LEN;
+ p->hdr.auth_len = RPC_AUTH_NTLMSSP_CHK_LEN;
+ } else if (p->netsec_auth_validated) {
+ p->hdr.frag_len = RPC_HEADER_LEN + RPC_HDR_RESP_LEN + data_len +
+ RPC_HDR_AUTH_LEN + RPC_AUTH_NETSEC_CHK_LEN;
+ p->hdr.auth_len = RPC_AUTH_NETSEC_CHK_LEN;
+ } else {
+ p->hdr.frag_len = RPC_HEADER_LEN + RPC_HDR_RESP_LEN + data_len;
+ p->hdr.auth_len = 0;
+ }
+
+ /*
+ * Work out if this PDU will be the last.
+ */
+
+ if(p->out_data.data_sent_length + data_len >= prs_offset(&p->out_data.rdata))
+ p->hdr.flags |= RPC_FLG_LAST;
+
+ /*
+ * Init the parse struct to point at the outgoing
+ * data.
+ */
+
+ prs_init( &outgoing_pdu, 0, p->mem_ctx, MARSHALL);
+ prs_give_memory( &outgoing_pdu, (char *)p->out_data.current_pdu, sizeof(p->out_data.current_pdu), False);
+
+ /* Store the header in the data stream. */
+ if(!smb_io_rpc_hdr("hdr", &p->hdr, &outgoing_pdu, 0)) {
+ DEBUG(0,("create_next_pdu: failed to marshall RPC_HDR.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ if(!smb_io_rpc_hdr_resp("resp", &hdr_resp, &outgoing_pdu, 0)) {
+ DEBUG(0,("create_next_pdu: failed to marshall RPC_HDR_RESP.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ /* Store the current offset. */
+ data_pos = prs_offset(&outgoing_pdu);
+
+ /* Copy the data into the PDU. */
+
+ if(!prs_append_some_prs_data(&outgoing_pdu, &p->out_data.rdata, p->out_data.data_sent_length, data_len)) {
+ DEBUG(0,("create_next_pdu: failed to copy %u bytes of data.\n", (unsigned int)data_len));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ if (p->ntlmssp_auth_validated) {
+ uint32 crc32 = 0;
+ char *data;
+
+ DEBUG(5,("create_next_pdu: sign: %s seal: %s data %d auth %d\n",
+ BOOLSTR(auth_verify), BOOLSTR(auth_seal), data_len, p->hdr.auth_len));
+
+ /*
+ * Set data to point to where we copied the data into.
+ */
+
+ data = prs_data_p(&outgoing_pdu) + data_pos;
+
+ if (auth_seal) {
+ crc32 = crc32_calc_buffer(data, data_len);
+ NTLMSSPcalc_p(p, (uchar*)data, data_len);
+ }
+
+ if (auth_seal || auth_verify) {
+ RPC_HDR_AUTH auth_info;
+
+ init_rpc_hdr_auth(&auth_info, NTLMSSP_AUTH_TYPE, RPC_PIPE_AUTH_SEAL_LEVEL,
+ (auth_verify ? RPC_HDR_AUTH_LEN : 0), (auth_verify ? 1 : 0));
+ if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, &outgoing_pdu, 0)) {
+ DEBUG(0,("create_next_pdu: failed to marshall RPC_HDR_AUTH.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+ }
+
+ if (auth_verify) {
+ RPC_AUTH_NTLMSSP_CHK ntlmssp_chk;
+ char *auth_data = prs_data_p(&outgoing_pdu);
+
+ p->ntlmssp_seq_num++;
+ init_rpc_auth_ntlmssp_chk(&ntlmssp_chk, NTLMSSP_SIGN_VERSION,
+ crc32, p->ntlmssp_seq_num++);
+ auth_data = prs_data_p(&outgoing_pdu) + prs_offset(&outgoing_pdu) + 4;
+ if(!smb_io_rpc_auth_ntlmssp_chk("auth_sign", &ntlmssp_chk, &outgoing_pdu, 0)) {
+ DEBUG(0,("create_next_pdu: failed to marshall RPC_AUTH_NTLMSSP_CHK.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+ NTLMSSPcalc_p(p, (uchar*)auth_data, RPC_AUTH_NTLMSSP_CHK_LEN - 4);
+ }
+ }
+
+ if (p->netsec_auth_validated) {
+ int auth_type, auth_level;
+ char *data;
+ RPC_HDR_AUTH auth_info;
+
+ RPC_AUTH_NETSEC_CHK verf;
+ prs_struct rverf;
+ prs_struct rauth;
+
+ data = prs_data_p(&outgoing_pdu) + data_pos;
+ /* Check it's the type of reply we were expecting to decode */
+
+ get_auth_type_level(p->netsec_auth.auth_flags, &auth_type, &auth_level);
+ init_rpc_hdr_auth(&auth_info, auth_type, auth_level,
+ RPC_HDR_AUTH_LEN, 1);
+
+ if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, &outgoing_pdu, 0)) {
+ DEBUG(0,("create_next_pdu: failed to marshall RPC_HDR_AUTH.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ prs_init(&rverf, 0, p->mem_ctx, MARSHALL);
+ prs_init(&rauth, 0, p->mem_ctx, MARSHALL);
+
+ netsec_encode(&p->netsec_auth,
+ p->netsec_auth.auth_flags,
+ SENDER_IS_ACCEPTOR,
+ &verf, data, data_len);
+
+ smb_io_rpc_auth_netsec_chk("", &verf, &outgoing_pdu, 0);
+
+ p->netsec_auth.seq_num++;
+ }
+
+ /*
+ * Setup the counts for this PDU.
+ */
+
+ p->out_data.data_sent_length += data_len;
+ p->out_data.current_pdu_len = p->hdr.frag_len;
+ p->out_data.current_pdu_sent = 0;
+
+ prs_mem_free(&outgoing_pdu);
+ return True;
+}
+
+/*******************************************************************
+ Process an NTLMSSP authentication response.
+ If this function succeeds, the user has been authenticated
+ and their domain, name and calling workstation stored in
+ the pipe struct.
+ The initial challenge is stored in p->challenge.
+ *******************************************************************/
+
+static BOOL api_pipe_ntlmssp_verify(pipes_struct *p, RPC_AUTH_NTLMSSP_RESP *ntlmssp_resp)
+{
+ uchar lm_owf[24];
+ uchar nt_owf[128];
+ int nt_pw_len;
+ int lm_pw_len;
+ fstring user_name;
+ fstring domain;
+ fstring wks;
+
+ NTSTATUS nt_status;
+
+ struct auth_context *auth_context = NULL;
+ auth_usersupplied_info *user_info = NULL;
+ auth_serversupplied_info *server_info = NULL;
+
+ DEBUG(5,("api_pipe_ntlmssp_verify: checking user details\n"));
+
+ memset(p->user_name, '\0', sizeof(p->user_name));
+ memset(p->pipe_user_name, '\0', sizeof(p->pipe_user_name));
+ memset(p->domain, '\0', sizeof(p->domain));
+ memset(p->wks, '\0', sizeof(p->wks));
+
+ /* Set up for non-authenticated user. */
+ delete_nt_token(&p->pipe_user.nt_user_token);
+ p->pipe_user.ngroups = 0;
+ SAFE_FREE( p->pipe_user.groups);
+
+ /*
+ * Setup an empty password for a guest user.
+ */
+
+ /*
+ * We always negotiate UNICODE.
+ */
+
+ if (p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_UNICODE) {
+ rpcstr_pull(user_name, ntlmssp_resp->user, sizeof(fstring), ntlmssp_resp->hdr_usr.str_str_len*2, 0 );
+ rpcstr_pull(domain, ntlmssp_resp->domain, sizeof(fstring), ntlmssp_resp->hdr_domain.str_str_len*2, 0);
+ rpcstr_pull(wks, ntlmssp_resp->wks, sizeof(fstring), ntlmssp_resp->hdr_wks.str_str_len*2, 0);
+ } else {
+ pull_ascii_fstring(user_name, ntlmssp_resp->user);
+ pull_ascii_fstring(domain, ntlmssp_resp->domain);
+ pull_ascii_fstring(wks, ntlmssp_resp->wks);
+ }
+
+ DEBUG(5,("user: %s domain: %s wks: %s\n", user_name, domain, wks));
+
+ nt_pw_len = MIN(sizeof(nt_owf), ntlmssp_resp->hdr_nt_resp.str_str_len);
+ lm_pw_len = MIN(sizeof(lm_owf), ntlmssp_resp->hdr_lm_resp.str_str_len);
+
+ memcpy(lm_owf, ntlmssp_resp->lm_resp, sizeof(lm_owf));
+ memcpy(nt_owf, ntlmssp_resp->nt_resp, nt_pw_len);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("lm, nt owfs, chal\n"));
+ dump_data(100, (char *)lm_owf, sizeof(lm_owf));
+ dump_data(100, (char *)nt_owf, nt_pw_len);
+ dump_data(100, (char *)p->challenge, 8);
+#endif
+
+ /*
+ * Allow guest access. Patch from Shirish Kalele <kalele@veritas.com>.
+ */
+
+ if (*user_name) {
+
+ /*
+ * Do the length checking only if user is not NULL.
+ */
+
+ if (ntlmssp_resp->hdr_lm_resp.str_str_len == 0)
+ return False;
+ if (ntlmssp_resp->hdr_nt_resp.str_str_len == 0)
+ return False;
+ if (ntlmssp_resp->hdr_usr.str_str_len == 0)
+ return False;
+ if (ntlmssp_resp->hdr_domain.str_str_len == 0)
+ return False;
+ if (ntlmssp_resp->hdr_wks.str_str_len == 0)
+ return False;
+
+ }
+
+ make_auth_context_fixed(&auth_context, (uchar*)p->challenge);
+
+ if (!make_user_info_netlogon_network(&user_info,
+ user_name, domain, wks,
+ lm_owf, lm_pw_len,
+ nt_owf, nt_pw_len)) {
+ DEBUG(0,("make_user_info_netlogon_network failed! Failing authenticaion.\n"));
+ return False;
+ }
+
+ nt_status = auth_context->check_ntlm_password(auth_context, user_info, &server_info);
+
+ (auth_context->free)(&auth_context);
+ free_user_info(&user_info);
+
+ p->ntlmssp_auth_validated = NT_STATUS_IS_OK(nt_status);
+
+ if (!p->ntlmssp_auth_validated) {
+ DEBUG(1,("api_pipe_ntlmssp_verify: User [%s]\\[%s] from machine %s \
+failed authentication on named pipe %s.\n", domain, user_name, wks, p->name ));
+ free_server_info(&server_info);
+ return False;
+ }
+
+ /*
+ * Set up the sign/seal data.
+ */
+
+ if (server_info->lm_session_key.length != 16) {
+ DEBUG(1,("api_pipe_ntlmssp_verify: User [%s]\\[%s] from machine %s \
+succeeded authentication on named pipe %s, but session key was of incorrect length [%u].\n",
+ domain, user_name, wks, p->name, server_info->lm_session_key.length));
+ free_server_info(&server_info);
+ return False;
+ } else {
+ uchar p24[24];
+ NTLMSSPOWFencrypt(server_info->lm_session_key.data, lm_owf, p24);
+ {
+ unsigned char j = 0;
+ int ind;
+
+ unsigned char k2[8];
+
+ memcpy(k2, p24, 5);
+ k2[5] = 0xe5;
+ k2[6] = 0x38;
+ k2[7] = 0xb0;
+
+ for (ind = 0; ind < 256; ind++)
+ p->ntlmssp_hash[ind] = (unsigned char)ind;
+
+ for( ind = 0; ind < 256; ind++) {
+ unsigned char tc;
+
+ j += (p->ntlmssp_hash[ind] + k2[ind%8]);
+
+ tc = p->ntlmssp_hash[ind];
+ p->ntlmssp_hash[ind] = p->ntlmssp_hash[j];
+ p->ntlmssp_hash[j] = tc;
+ }
+
+ p->ntlmssp_hash[256] = 0;
+ p->ntlmssp_hash[257] = 0;
+ }
+
+ dump_data_pw("NTLMSSP hash (v1)\n", p->ntlmssp_hash,
+ sizeof(p->ntlmssp_hash));
+
+/* NTLMSSPhash(p->ntlmssp_hash, p24); */
+ p->ntlmssp_seq_num = 0;
+
+ }
+
+ fstrcpy(p->user_name, user_name);
+ fstrcpy(p->pipe_user_name, server_info->unix_name);
+ fstrcpy(p->domain, domain);
+ fstrcpy(p->wks, wks);
+
+ /*
+ * Store the UNIX credential data (uid/gid pair) in the pipe structure.
+ */
+
+ p->session_key = data_blob(server_info->lm_session_key.data, server_info->lm_session_key.length);
+
+ p->pipe_user.uid = server_info->uid;
+ p->pipe_user.gid = server_info->gid;
+
+ p->pipe_user.ngroups = server_info->n_groups;
+ if (p->pipe_user.ngroups) {
+ if (!(p->pipe_user.groups = memdup(server_info->groups, sizeof(gid_t) * p->pipe_user.ngroups))) {
+ DEBUG(0,("failed to memdup group list to p->pipe_user.groups\n"));
+ free_server_info(&server_info);
+ return False;
+ }
+ }
+
+ if (server_info->ptok)
+ p->pipe_user.nt_user_token = dup_nt_token(server_info->ptok);
+ else {
+ DEBUG(1,("Error: Authmodule failed to provide nt_user_token\n"));
+ p->pipe_user.nt_user_token = NULL;
+ free_server_info(&server_info);
+ return False;
+ }
+
+ p->ntlmssp_auth_validated = True;
+
+ free_server_info(&server_info);
+ return True;
+}
+
+/*******************************************************************
+ The switch table for the pipe names and the functions to handle them.
+ *******************************************************************/
+
+struct rpc_table
+{
+ struct
+ {
+ const char *clnt;
+ const char *srv;
+ } pipe;
+ struct api_struct *cmds;
+ int n_cmds;
+};
+
+static struct rpc_table *rpc_lookup;
+static int rpc_lookup_size;
+
+/*******************************************************************
+ This is the client reply to our challenge for an authenticated
+ bind request. The challenge we sent is in p->challenge.
+*******************************************************************/
+
+BOOL api_pipe_bind_auth_resp(pipes_struct *p, prs_struct *rpc_in_p)
+{
+ RPC_HDR_AUTHA autha_info;
+ RPC_AUTH_VERIFIER auth_verifier;
+ RPC_AUTH_NTLMSSP_RESP ntlmssp_resp;
+
+ DEBUG(5,("api_pipe_bind_auth_resp: decode request. %d\n", __LINE__));
+
+ if (p->hdr.auth_len == 0) {
+ DEBUG(0,("api_pipe_bind_auth_resp: No auth field sent !\n"));
+ return False;
+ }
+
+ /*
+ * Decode the authentication verifier response.
+ */
+
+ if(!smb_io_rpc_hdr_autha("", &autha_info, rpc_in_p, 0)) {
+ DEBUG(0,("api_pipe_bind_auth_resp: unmarshall of RPC_HDR_AUTHA failed.\n"));
+ return False;
+ }
+
+ if (autha_info.auth_type != NTLMSSP_AUTH_TYPE || autha_info.auth_level != RPC_PIPE_AUTH_SEAL_LEVEL) {
+ DEBUG(0,("api_pipe_bind_auth_resp: incorrect auth type (%d) or level (%d).\n",
+ (int)autha_info.auth_type, (int)autha_info.auth_level ));
+ return False;
+ }
+
+ if(!smb_io_rpc_auth_verifier("", &auth_verifier, rpc_in_p, 0)) {
+ DEBUG(0,("api_pipe_bind_auth_resp: unmarshall of RPC_AUTH_VERIFIER failed.\n"));
+ return False;
+ }
+
+ /*
+ * Ensure this is a NTLMSSP_AUTH packet type.
+ */
+
+ if (!rpc_auth_verifier_chk(&auth_verifier, "NTLMSSP", NTLMSSP_AUTH)) {
+ DEBUG(0,("api_pipe_bind_auth_resp: rpc_auth_verifier_chk failed.\n"));
+ return False;
+ }
+
+ if(!smb_io_rpc_auth_ntlmssp_resp("", &ntlmssp_resp, rpc_in_p, 0)) {
+ DEBUG(0,("api_pipe_bind_auth_resp: Failed to unmarshall RPC_AUTH_NTLMSSP_RESP.\n"));
+ return False;
+ }
+
+ /*
+ * The following call actually checks the challenge/response data.
+ * for correctness against the given DOMAIN\user name.
+ */
+
+ if (!api_pipe_ntlmssp_verify(p, &ntlmssp_resp))
+ return False;
+
+ p->pipe_bound = True
+;
+ return True;
+}
+
+/*******************************************************************
+ Marshall a bind_nak pdu.
+*******************************************************************/
+
+static BOOL setup_bind_nak(pipes_struct *p)
+{
+ prs_struct outgoing_rpc;
+ RPC_HDR nak_hdr;
+ uint16 zero = 0;
+
+ /* Free any memory in the current return data buffer. */
+ prs_mem_free(&p->out_data.rdata);
+
+ /*
+ * Marshall directly into the outgoing PDU space. We
+ * must do this as we need to set to the bind response
+ * header and are never sending more than one PDU here.
+ */
+
+ prs_init( &outgoing_rpc, 0, p->mem_ctx, MARSHALL);
+ prs_give_memory( &outgoing_rpc, (char *)p->out_data.current_pdu, sizeof(p->out_data.current_pdu), False);
+
+
+ /*
+ * Initialize a bind_nak header.
+ */
+
+ init_rpc_hdr(&nak_hdr, RPC_BINDNACK, RPC_FLG_FIRST | RPC_FLG_LAST,
+ p->hdr.call_id, RPC_HEADER_LEN + sizeof(uint16), 0);
+
+ /*
+ * Marshall the header into the outgoing PDU.
+ */
+
+ if(!smb_io_rpc_hdr("", &nak_hdr, &outgoing_rpc, 0)) {
+ DEBUG(0,("setup_bind_nak: marshalling of RPC_HDR failed.\n"));
+ prs_mem_free(&outgoing_rpc);
+ return False;
+ }
+
+ /*
+ * Now add the reject reason.
+ */
+
+ if(!prs_uint16("reject code", &outgoing_rpc, 0, &zero)) {
+ prs_mem_free(&outgoing_rpc);
+ return False;
+ }
+
+ p->out_data.data_sent_length = 0;
+ p->out_data.current_pdu_len = prs_offset(&outgoing_rpc);
+ p->out_data.current_pdu_sent = 0;
+
+ p->pipe_bound = False;
+
+ return True;
+}
+
+/*******************************************************************
+ Marshall a fault pdu.
+*******************************************************************/
+
+BOOL setup_fault_pdu(pipes_struct *p, NTSTATUS status)
+{
+ prs_struct outgoing_pdu;
+ RPC_HDR fault_hdr;
+ RPC_HDR_RESP hdr_resp;
+ RPC_HDR_FAULT fault_resp;
+
+ /* Free any memory in the current return data buffer. */
+ prs_mem_free(&p->out_data.rdata);
+
+ /*
+ * Marshall directly into the outgoing PDU space. We
+ * must do this as we need to set to the bind response
+ * header and are never sending more than one PDU here.
+ */
+
+ prs_init( &outgoing_pdu, 0, p->mem_ctx, MARSHALL);
+ prs_give_memory( &outgoing_pdu, (char *)p->out_data.current_pdu, sizeof(p->out_data.current_pdu), False);
+
+ /*
+ * Initialize a fault header.
+ */
+
+ init_rpc_hdr(&fault_hdr, RPC_FAULT, RPC_FLG_FIRST | RPC_FLG_LAST | RPC_FLG_NOCALL,
+ p->hdr.call_id, RPC_HEADER_LEN + RPC_HDR_RESP_LEN + RPC_HDR_FAULT_LEN, 0);
+
+ /*
+ * Initialize the HDR_RESP and FAULT parts of the PDU.
+ */
+
+ memset((char *)&hdr_resp, '\0', sizeof(hdr_resp));
+
+ fault_resp.status = status;
+ fault_resp.reserved = 0;
+
+ /*
+ * Marshall the header into the outgoing PDU.
+ */
+
+ if(!smb_io_rpc_hdr("", &fault_hdr, &outgoing_pdu, 0)) {
+ DEBUG(0,("setup_fault_pdu: marshalling of RPC_HDR failed.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ if(!smb_io_rpc_hdr_resp("resp", &hdr_resp, &outgoing_pdu, 0)) {
+ DEBUG(0,("setup_fault_pdu: failed to marshall RPC_HDR_RESP.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ if(!smb_io_rpc_hdr_fault("fault", &fault_resp, &outgoing_pdu, 0)) {
+ DEBUG(0,("setup_fault_pdu: failed to marshall RPC_HDR_FAULT.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ p->out_data.data_sent_length = 0;
+ p->out_data.current_pdu_len = prs_offset(&outgoing_pdu);
+ p->out_data.current_pdu_sent = 0;
+
+ prs_mem_free(&outgoing_pdu);
+ return True;
+}
+
+/*******************************************************************
+ Ensure a bind request has the correct abstract & transfer interface.
+ Used to reject unknown binds from Win2k.
+*******************************************************************/
+
+BOOL check_bind_req(struct pipes_struct *p, RPC_IFACE* abstract,
+ RPC_IFACE* transfer, uint32 context_id)
+{
+ extern struct pipe_id_info pipe_names[];
+ char *pipe_name = p->name;
+ int i=0;
+ fstring pname;
+
+ fstrcpy(pname,"\\PIPE\\");
+ fstrcat(pname,pipe_name);
+
+ DEBUG(3,("check_bind_req for %s\n", pname));
+
+ /* we have to check all now since win2k introduced a new UUID on the lsaprpc pipe */
+
+ for ( i=0; pipe_names[i].client_pipe; i++ )
+ {
+ if ( strequal(pipe_names[i].client_pipe, pname)
+ && (abstract->version == pipe_names[i].abstr_syntax.version)
+ && (memcmp(&abstract->uuid, &pipe_names[i].abstr_syntax.uuid, sizeof(struct uuid)) == 0)
+ && (transfer->version == pipe_names[i].trans_syntax.version)
+ && (memcmp(&transfer->uuid, &pipe_names[i].trans_syntax.uuid, sizeof(struct uuid)) == 0) )
+ {
+ struct api_struct *fns = NULL;
+ int n_fns = 0;
+ PIPE_RPC_FNS *context_fns;
+
+ if ( !(context_fns = malloc(sizeof(PIPE_RPC_FNS))) ) {
+ DEBUG(0,("check_bind_req: malloc() failed!\n"));
+ return False;
+ }
+
+ /* save the RPC function table associated with this bind */
+
+ get_pipe_fns(i, &fns, &n_fns);
+
+ context_fns->cmds = fns;
+ context_fns->n_cmds = n_fns;
+ context_fns->context_id = context_id;
+
+ /* add to the list of open contexts */
+
+ DLIST_ADD( p->contexts, context_fns );
+
+ break;
+ }
+ }
+
+ if(pipe_names[i].client_pipe == NULL)
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Register commands to an RPC pipe
+*******************************************************************/
+NTSTATUS rpc_pipe_register_commands(int version, const char *clnt, const char *srv, const struct api_struct *cmds, int size)
+{
+ struct rpc_table *rpc_entry;
+
+ if (!clnt || !srv || !cmds) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (version != SMB_RPC_INTERFACE_VERSION) {
+ DEBUG(0,("Can't register rpc commands!\n"
+ "You tried to register a rpc module with SMB_RPC_INTERFACE_VERSION %d"
+ ", while this version of samba uses version %d!\n",
+ version,SMB_RPC_INTERFACE_VERSION));
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ /* TODO:
+ *
+ * we still need to make sure that don't register the same commands twice!!!
+ *
+ * --metze
+ */
+
+ /* We use a temporary variable because this call can fail and
+ rpc_lookup will still be valid afterwards. It could then succeed if
+ called again later */
+ rpc_entry = realloc(rpc_lookup,
+ ++rpc_lookup_size*sizeof(struct rpc_table));
+ if (NULL == rpc_entry) {
+ rpc_lookup_size--;
+ DEBUG(0, ("rpc_pipe_register_commands: memory allocation failed\n"));
+ return NT_STATUS_NO_MEMORY;
+ } else {
+ rpc_lookup = rpc_entry;
+ }
+
+ rpc_entry = rpc_lookup + (rpc_lookup_size - 1);
+ ZERO_STRUCTP(rpc_entry);
+ rpc_entry->pipe.clnt = strdup(clnt);
+ rpc_entry->pipe.srv = strdup(srv);
+ rpc_entry->cmds = realloc(rpc_entry->cmds,
+ (rpc_entry->n_cmds + size) *
+ sizeof(struct api_struct));
+ memcpy(rpc_entry->cmds + rpc_entry->n_cmds, cmds,
+ size * sizeof(struct api_struct));
+ rpc_entry->n_cmds += size;
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Respond to a pipe bind request.
+*******************************************************************/
+
+BOOL api_pipe_bind_req(pipes_struct *p, prs_struct *rpc_in_p)
+{
+ RPC_HDR_BA hdr_ba;
+ RPC_HDR_RB hdr_rb;
+ RPC_HDR_AUTH auth_info;
+ uint16 assoc_gid;
+ fstring ack_pipe_name;
+ prs_struct out_hdr_ba;
+ prs_struct out_auth;
+ prs_struct outgoing_rpc;
+ int i = 0;
+ int auth_len = 0;
+ enum RPC_PKT_TYPE reply_pkt_type;
+
+ p->ntlmssp_auth_requested = False;
+ p->netsec_auth_validated = False;
+
+ DEBUG(5,("api_pipe_bind_req: decode request. %d\n", __LINE__));
+
+ /*
+ * Try and find the correct pipe name to ensure
+ * that this is a pipe name we support.
+ */
+
+
+ for (i = 0; i < rpc_lookup_size; i++) {
+ if (strequal(rpc_lookup[i].pipe.clnt, p->name)) {
+ DEBUG(3, ("api_pipe_bind_req: \\PIPE\\%s -> \\PIPE\\%s\n",
+ rpc_lookup[i].pipe.clnt, rpc_lookup[i].pipe.srv));
+ fstrcpy(p->pipe_srv_name, rpc_lookup[i].pipe.srv);
+ break;
+ }
+ }
+
+ if (i == rpc_lookup_size) {
+ if (NT_STATUS_IS_ERR(smb_probe_module("rpc", p->name))) {
+ DEBUG(3,("api_pipe_bind_req: Unknown pipe name %s in bind request.\n",
+ p->name ));
+ if(!setup_bind_nak(p))
+ return False;
+ return True;
+ }
+
+ for (i = 0; i < rpc_lookup_size; i++) {
+ if (strequal(rpc_lookup[i].pipe.clnt, p->name)) {
+ DEBUG(3, ("api_pipe_bind_req: \\PIPE\\%s -> \\PIPE\\%s\n",
+ rpc_lookup[i].pipe.clnt, rpc_lookup[i].pipe.srv));
+ fstrcpy(p->pipe_srv_name, rpc_lookup[i].pipe.srv);
+ break;
+ }
+ }
+
+ if (i == rpc_lookup_size) {
+ DEBUG(0, ("module %s doesn't provide functions for pipe %s!\n", p->name, p->name));
+ return False;
+ }
+ }
+
+ /* decode the bind request */
+ if(!smb_io_rpc_hdr_rb("", &hdr_rb, rpc_in_p, 0)) {
+ DEBUG(0,("api_pipe_bind_req: unable to unmarshall RPC_HDR_RB struct.\n"));
+ return False;
+ }
+
+ /*
+ * Check if this is an authenticated request.
+ */
+
+ if (p->hdr.auth_len != 0) {
+ RPC_AUTH_VERIFIER auth_verifier;
+ RPC_AUTH_NTLMSSP_NEG ntlmssp_neg;
+
+ /*
+ * Decode the authentication verifier.
+ */
+
+ if(!smb_io_rpc_hdr_auth("", &auth_info, rpc_in_p, 0)) {
+ DEBUG(0,("api_pipe_bind_req: unable to unmarshall RPC_HDR_AUTH struct.\n"));
+ return False;
+ }
+
+ if(auth_info.auth_type == NTLMSSP_AUTH_TYPE) {
+
+ if(!smb_io_rpc_auth_verifier("", &auth_verifier, rpc_in_p, 0)) {
+ DEBUG(0,("api_pipe_bind_req: unable to "
+ "unmarshall RPC_HDR_AUTH struct.\n"));
+ return False;
+ }
+
+ if(!strequal(auth_verifier.signature, "NTLMSSP")) {
+ DEBUG(0,("api_pipe_bind_req: "
+ "auth_verifier.signature != NTLMSSP\n"));
+ return False;
+ }
+
+ if(auth_verifier.msg_type != NTLMSSP_NEGOTIATE) {
+ DEBUG(0,("api_pipe_bind_req: "
+ "auth_verifier.msg_type (%d) != NTLMSSP_NEGOTIATE\n",
+ auth_verifier.msg_type));
+ return False;
+ }
+
+ if(!smb_io_rpc_auth_ntlmssp_neg("", &ntlmssp_neg, rpc_in_p, 0)) {
+ DEBUG(0,("api_pipe_bind_req: "
+ "Failed to unmarshall RPC_AUTH_NTLMSSP_NEG.\n"));
+ return False;
+ }
+
+ p->ntlmssp_chal_flags = SMBD_NTLMSSP_NEG_FLAGS;
+ p->ntlmssp_auth_requested = True;
+
+ } else if (auth_info.auth_type == NETSEC_AUTH_TYPE) {
+
+ RPC_AUTH_NETSEC_NEG neg;
+ struct netsec_auth_struct *a = &(p->netsec_auth);
+
+ if (!smb_io_rpc_auth_netsec_neg("", &neg, rpc_in_p, 0)) {
+ DEBUG(0,("api_pipe_bind_req: "
+ "Could not unmarshal SCHANNEL auth neg\n"));
+ return False;
+ }
+
+ p->netsec_auth_validated = True;
+
+ memset(a->sess_key, 0, sizeof(a->sess_key));
+ memcpy(a->sess_key, last_dcinfo.sess_key, sizeof(last_dcinfo.sess_key));
+
+ a->seq_num = 0;
+
+ DEBUG(10,("schannel auth: domain [%s] myname [%s]\n",
+ neg.domain, neg.myname));
+
+ } else {
+ DEBUG(0,("api_pipe_bind_req: unknown auth type %x requested.\n",
+ auth_info.auth_type ));
+ return False;
+ }
+ }
+
+ switch(p->hdr.pkt_type) {
+ case RPC_BIND:
+ /* name has to be \PIPE\xxxxx */
+ fstrcpy(ack_pipe_name, "\\PIPE\\");
+ fstrcat(ack_pipe_name, p->pipe_srv_name);
+ reply_pkt_type = RPC_BINDACK;
+ break;
+ case RPC_ALTCONT:
+ /* secondary address CAN be NULL
+ * as the specs say it's ignored.
+ * It MUST NULL to have the spoolss working.
+ */
+ fstrcpy(ack_pipe_name,"");
+ reply_pkt_type = RPC_ALTCONTRESP;
+ break;
+ default:
+ return False;
+ }
+
+ DEBUG(5,("api_pipe_bind_req: make response. %d\n", __LINE__));
+
+ /*
+ * Marshall directly into the outgoing PDU space. We
+ * must do this as we need to set to the bind response
+ * header and are never sending more than one PDU here.
+ */
+
+ prs_init( &outgoing_rpc, 0, p->mem_ctx, MARSHALL);
+ prs_give_memory( &outgoing_rpc, (char *)p->out_data.current_pdu, sizeof(p->out_data.current_pdu), False);
+
+ /*
+ * Setup the memory to marshall the ba header, and the
+ * auth footers.
+ */
+
+ if(!prs_init(&out_hdr_ba, 1024, p->mem_ctx, MARSHALL)) {
+ DEBUG(0,("api_pipe_bind_req: malloc out_hdr_ba failed.\n"));
+ prs_mem_free(&outgoing_rpc);
+ return False;
+ }
+
+ if(!prs_init(&out_auth, 1024, p->mem_ctx, MARSHALL)) {
+ DEBUG(0,("pi_pipe_bind_req: malloc out_auth failed.\n"));
+ prs_mem_free(&outgoing_rpc);
+ prs_mem_free(&out_hdr_ba);
+ return False;
+ }
+
+ if (p->ntlmssp_auth_requested)
+ assoc_gid = 0x7a77;
+ else
+ assoc_gid = hdr_rb.bba.assoc_gid ? hdr_rb.bba.assoc_gid : 0x53f0;
+
+ /*
+ * Create the bind response struct.
+ */
+
+ /* If the requested abstract synt uuid doesn't match our client pipe,
+ reject the bind_ack & set the transfer interface synt to all 0's,
+ ver 0 (observed when NT5 attempts to bind to abstract interfaces
+ unknown to NT4)
+ Needed when adding entries to a DACL from NT5 - SK */
+
+ if(check_bind_req(p, &hdr_rb.abstract, &hdr_rb.transfer, hdr_rb.context_id ))
+ {
+ init_rpc_hdr_ba(&hdr_ba,
+ MAX_PDU_FRAG_LEN,
+ MAX_PDU_FRAG_LEN,
+ assoc_gid,
+ ack_pipe_name,
+ 0x1, 0x0, 0x0,
+ &hdr_rb.transfer);
+ } else {
+ RPC_IFACE null_interface;
+ ZERO_STRUCT(null_interface);
+ /* Rejection reason: abstract syntax not supported */
+ init_rpc_hdr_ba(&hdr_ba, MAX_PDU_FRAG_LEN,
+ MAX_PDU_FRAG_LEN, assoc_gid,
+ ack_pipe_name, 0x1, 0x2, 0x1,
+ &null_interface);
+ }
+
+ /*
+ * and marshall it.
+ */
+
+ if(!smb_io_rpc_hdr_ba("", &hdr_ba, &out_hdr_ba, 0)) {
+ DEBUG(0,("api_pipe_bind_req: marshalling of RPC_HDR_BA failed.\n"));
+ goto err_exit;
+ }
+
+ /*
+ * Now the authentication.
+ */
+
+ if (p->ntlmssp_auth_requested) {
+ RPC_AUTH_VERIFIER auth_verifier;
+ RPC_AUTH_NTLMSSP_CHAL ntlmssp_chal;
+
+ generate_random_buffer(p->challenge, 8, False);
+
+ /*** Authentication info ***/
+
+ init_rpc_hdr_auth(&auth_info, NTLMSSP_AUTH_TYPE, RPC_PIPE_AUTH_SEAL_LEVEL, RPC_HDR_AUTH_LEN, 1);
+ if(!smb_io_rpc_hdr_auth("", &auth_info, &out_auth, 0)) {
+ DEBUG(0,("api_pipe_bind_req: marshalling of RPC_HDR_AUTH failed.\n"));
+ goto err_exit;
+ }
+
+ /*** NTLMSSP verifier ***/
+
+ init_rpc_auth_verifier(&auth_verifier, "NTLMSSP", NTLMSSP_CHALLENGE);
+ if(!smb_io_rpc_auth_verifier("", &auth_verifier, &out_auth, 0)) {
+ DEBUG(0,("api_pipe_bind_req: marshalling of RPC_AUTH_VERIFIER failed.\n"));
+ goto err_exit;
+ }
+
+ /* NTLMSSP challenge ***/
+
+ init_rpc_auth_ntlmssp_chal(&ntlmssp_chal, p->ntlmssp_chal_flags, p->challenge);
+ if(!smb_io_rpc_auth_ntlmssp_chal("", &ntlmssp_chal, &out_auth, 0)) {
+ DEBUG(0,("api_pipe_bind_req: marshalling of RPC_AUTH_NTLMSSP_CHAL failed.\n"));
+ goto err_exit;
+ }
+
+ /* Auth len in the rpc header doesn't include auth_header. */
+ auth_len = prs_offset(&out_auth) - RPC_HDR_AUTH_LEN;
+ }
+
+ if (p->netsec_auth_validated) {
+ RPC_AUTH_VERIFIER auth_verifier;
+ uint32 flags;
+
+ /* The client opens a second RPC NETLOGON pipe without
+ doing a auth2. The credentials for the schannel are
+ re-used from the auth2 the client did before. */
+ p->dc = last_dcinfo;
+
+ init_rpc_hdr_auth(&auth_info, NETSEC_AUTH_TYPE, RPC_PIPE_AUTH_SEAL_LEVEL, RPC_HDR_AUTH_LEN, 1);
+ if(!smb_io_rpc_hdr_auth("", &auth_info, &out_auth, 0)) {
+ DEBUG(0,("api_pipe_bind_req: marshalling of RPC_HDR_AUTH failed.\n"));
+ goto err_exit;
+ }
+
+ /*** NETSEC verifier ***/
+
+ init_rpc_auth_verifier(&auth_verifier, "\001", 0x0);
+ if(!smb_io_rpc_netsec_verifier("", &auth_verifier, &out_auth, 0)) {
+ DEBUG(0,("api_pipe_bind_req: marshalling of RPC_AUTH_VERIFIER failed.\n"));
+ goto err_exit;
+ }
+
+ prs_align(&out_auth);
+
+ flags = 5;
+ if(!prs_uint32("flags ", &out_auth, 0, &flags))
+ goto err_exit;
+
+ auth_len = prs_offset(&out_auth) - RPC_HDR_AUTH_LEN;
+ }
+
+ /*
+ * Create the header, now we know the length.
+ */
+
+ init_rpc_hdr(&p->hdr, reply_pkt_type, RPC_FLG_FIRST | RPC_FLG_LAST,
+ p->hdr.call_id,
+ RPC_HEADER_LEN + prs_offset(&out_hdr_ba) + prs_offset(&out_auth),
+ auth_len);
+
+ /*
+ * Marshall the header into the outgoing PDU.
+ */
+
+ if(!smb_io_rpc_hdr("", &p->hdr, &outgoing_rpc, 0)) {
+ DEBUG(0,("pi_pipe_bind_req: marshalling of RPC_HDR failed.\n"));
+ goto err_exit;
+ }
+
+ /*
+ * Now add the RPC_HDR_BA and any auth needed.
+ */
+
+ if(!prs_append_prs_data( &outgoing_rpc, &out_hdr_ba)) {
+ DEBUG(0,("api_pipe_bind_req: append of RPC_HDR_BA failed.\n"));
+ goto err_exit;
+ }
+
+ if((p->ntlmssp_auth_requested|p->netsec_auth_validated) &&
+ !prs_append_prs_data( &outgoing_rpc, &out_auth)) {
+ DEBUG(0,("api_pipe_bind_req: append of auth info failed.\n"));
+ goto err_exit;
+ }
+
+ if(!p->ntlmssp_auth_requested)
+ p->pipe_bound = True;
+
+ /*
+ * Setup the lengths for the initial reply.
+ */
+
+ p->out_data.data_sent_length = 0;
+ p->out_data.current_pdu_len = prs_offset(&outgoing_rpc);
+ p->out_data.current_pdu_sent = 0;
+
+ prs_mem_free(&out_hdr_ba);
+ prs_mem_free(&out_auth);
+
+ return True;
+
+ err_exit:
+
+ prs_mem_free(&outgoing_rpc);
+ prs_mem_free(&out_hdr_ba);
+ prs_mem_free(&out_auth);
+ return False;
+}
+
+/****************************************************************************
+ Deal with sign & seal processing on an RPC request.
+****************************************************************************/
+
+BOOL api_pipe_auth_process(pipes_struct *p, prs_struct *rpc_in)
+{
+ /*
+ * We always negotiate the following two bits....
+ */
+ BOOL auth_verify = ((p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SIGN) != 0);
+ BOOL auth_seal = ((p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SEAL) != 0);
+ int data_len;
+ int auth_len;
+ uint32 old_offset;
+ uint32 crc32 = 0;
+
+ auth_len = p->hdr.auth_len;
+
+ if ((auth_len != RPC_AUTH_NTLMSSP_CHK_LEN) && auth_verify) {
+ DEBUG(0,("api_pipe_auth_process: Incorrect auth_len %d.\n", auth_len ));
+ return False;
+ }
+
+ /*
+ * The following is that length of the data we must verify or unseal.
+ * This doesn't include the RPC headers or the auth_len or the RPC_HDR_AUTH_LEN
+ * preceeding the auth_data.
+ */
+
+ data_len = p->hdr.frag_len - RPC_HEADER_LEN - RPC_HDR_REQ_LEN -
+ (auth_verify ? RPC_HDR_AUTH_LEN : 0) - auth_len;
+
+ DEBUG(5,("api_pipe_auth_process: sign: %s seal: %s data %d auth %d\n",
+ BOOLSTR(auth_verify), BOOLSTR(auth_seal), data_len, auth_len));
+
+ if (auth_seal) {
+ /*
+ * The data in rpc_in doesn't contain the RPC_HEADER as this
+ * has already been consumed.
+ */
+ char *data = prs_data_p(rpc_in) + RPC_HDR_REQ_LEN;
+ dump_data_pw("NTLMSSP hash (v1)\n", p->ntlmssp_hash,
+ sizeof(p->ntlmssp_hash));
+
+ dump_data_pw("Incoming RPC PDU (NTLMSSP sealed)\n",
+ (const unsigned char *)data, data_len);
+ NTLMSSPcalc_p(p, (uchar*)data, data_len);
+ dump_data_pw("Incoming RPC PDU (NTLMSSP unsealed)\n",
+ (const unsigned char *)data, data_len);
+ crc32 = crc32_calc_buffer(data, data_len);
+ }
+
+ old_offset = prs_offset(rpc_in);
+
+ if (auth_seal || auth_verify) {
+ RPC_HDR_AUTH auth_info;
+
+ if(!prs_set_offset(rpc_in, old_offset + data_len)) {
+ DEBUG(0,("api_pipe_auth_process: cannot move offset to %u.\n",
+ (unsigned int)old_offset + data_len ));
+ return False;
+ }
+
+ if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, rpc_in, 0)) {
+ DEBUG(0,("api_pipe_auth_process: failed to unmarshall RPC_HDR_AUTH.\n"));
+ return False;
+ }
+ }
+
+ if (auth_verify) {
+ RPC_AUTH_NTLMSSP_CHK ntlmssp_chk;
+ char *req_data = prs_data_p(rpc_in) + prs_offset(rpc_in) + 4;
+
+ DEBUG(5,("api_pipe_auth_process: auth %d\n", prs_offset(rpc_in) + 4));
+
+ /*
+ * Ensure we have RPC_AUTH_NTLMSSP_CHK_LEN - 4 more bytes in the
+ * incoming buffer.
+ */
+ if(prs_mem_get(rpc_in, RPC_AUTH_NTLMSSP_CHK_LEN - 4) == NULL) {
+ DEBUG(0,("api_pipe_auth_process: missing %d bytes in buffer.\n",
+ RPC_AUTH_NTLMSSP_CHK_LEN - 4 ));
+ return False;
+ }
+
+ NTLMSSPcalc_p(p, (uchar*)req_data, RPC_AUTH_NTLMSSP_CHK_LEN - 4);
+ if(!smb_io_rpc_auth_ntlmssp_chk("auth_sign", &ntlmssp_chk, rpc_in, 0)) {
+ DEBUG(0,("api_pipe_auth_process: failed to unmarshall RPC_AUTH_NTLMSSP_CHK.\n"));
+ return False;
+ }
+
+ if (!rpc_auth_ntlmssp_chk(&ntlmssp_chk, crc32, p->ntlmssp_seq_num)) {
+ DEBUG(0,("api_pipe_auth_process: NTLMSSP check failed.\n"));
+ return False;
+ }
+ }
+
+ /*
+ * Return the current pointer to the data offset.
+ */
+
+ if(!prs_set_offset(rpc_in, old_offset)) {
+ DEBUG(0,("api_pipe_auth_process: failed to set offset back to %u\n",
+ (unsigned int)old_offset ));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Deal with schannel processing on an RPC request.
+****************************************************************************/
+BOOL api_pipe_netsec_process(pipes_struct *p, prs_struct *rpc_in)
+{
+ /*
+ * We always negotiate the following two bits....
+ */
+ int data_len;
+ int auth_len;
+ uint32 old_offset;
+ RPC_HDR_AUTH auth_info;
+ RPC_AUTH_NETSEC_CHK netsec_chk;
+
+
+ auth_len = p->hdr.auth_len;
+
+ if (auth_len != RPC_AUTH_NETSEC_CHK_LEN) {
+ DEBUG(0,("Incorrect auth_len %d.\n", auth_len ));
+ return False;
+ }
+
+ /*
+ * The following is that length of the data we must verify or unseal.
+ * This doesn't include the RPC headers or the auth_len or the RPC_HDR_AUTH_LEN
+ * preceeding the auth_data.
+ */
+
+ data_len = p->hdr.frag_len - RPC_HEADER_LEN - RPC_HDR_REQ_LEN -
+ RPC_HDR_AUTH_LEN - auth_len;
+
+ DEBUG(5,("data %d auth %d\n", data_len, auth_len));
+
+ old_offset = prs_offset(rpc_in);
+
+ if(!prs_set_offset(rpc_in, old_offset + data_len)) {
+ DEBUG(0,("cannot move offset to %u.\n",
+ (unsigned int)old_offset + data_len ));
+ return False;
+ }
+
+ if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, rpc_in, 0)) {
+ DEBUG(0,("failed to unmarshall RPC_HDR_AUTH.\n"));
+ return False;
+ }
+
+ if (auth_info.auth_type != NETSEC_AUTH_TYPE) {
+ DEBUG(0,("Invalid auth info %d on schannel\n",
+ auth_info.auth_type));
+ return False;
+ }
+
+ if (auth_info.auth_level == RPC_PIPE_AUTH_SEAL_LEVEL) {
+ p->netsec_auth.auth_flags = AUTH_PIPE_NETSEC|AUTH_PIPE_SIGN|AUTH_PIPE_SEAL;
+ } else if (auth_info.auth_level == RPC_PIPE_AUTH_SIGN_LEVEL) {
+ p->netsec_auth.auth_flags = AUTH_PIPE_NETSEC|AUTH_PIPE_SIGN;
+ } else {
+ DEBUG(0,("Invalid auth level %d on schannel\n",
+ auth_info.auth_level));
+ return False;
+ }
+
+ if(!smb_io_rpc_auth_netsec_chk("", &netsec_chk, rpc_in, 0)) {
+ DEBUG(0,("failed to unmarshal RPC_AUTH_NETSEC_CHK.\n"));
+ return False;
+ }
+
+ if (!netsec_decode(&p->netsec_auth,
+ p->netsec_auth.auth_flags,
+ SENDER_IS_INITIATOR,
+ &netsec_chk,
+ prs_data_p(rpc_in)+old_offset, data_len)) {
+ DEBUG(0,("failed to decode PDU\n"));
+ return False;
+ }
+
+ /*
+ * Return the current pointer to the data offset.
+ */
+
+ if(!prs_set_offset(rpc_in, old_offset)) {
+ DEBUG(0,("failed to set offset back to %u\n",
+ (unsigned int)old_offset ));
+ return False;
+ }
+
+ /* The sequence number gets incremented on both send and receive. */
+ p->netsec_auth.seq_num++;
+
+ return True;
+}
+
+/****************************************************************************
+ Return a user struct for a pipe user.
+****************************************************************************/
+
+struct current_user *get_current_user(struct current_user *user, pipes_struct *p)
+{
+ if (p->ntlmssp_auth_validated) {
+ memcpy(user, &p->pipe_user, sizeof(struct current_user));
+ } else {
+ extern struct current_user current_user;
+ memcpy(user, &current_user, sizeof(struct current_user));
+ }
+
+ return user;
+}
+
+/****************************************************************************
+ Find the set of RPC functions associated with this context_id
+****************************************************************************/
+
+static PIPE_RPC_FNS* find_pipe_fns_by_context( PIPE_RPC_FNS *list, uint32 context_id )
+{
+ PIPE_RPC_FNS *fns = NULL;
+ PIPE_RPC_FNS *tmp = NULL;
+
+ if ( !list ) {
+ DEBUG(0,("find_pipe_fns_by_context: ERROR! No context list for pipe!\n"));
+ return NULL;
+ }
+
+ for (tmp=list; tmp; tmp=tmp->next ) {
+ if ( tmp->context_id == context_id )
+ break;
+ }
+
+ fns = tmp;
+
+ return fns;
+}
+
+/****************************************************************************
+ memory cleanup
+****************************************************************************/
+
+void free_pipe_rpc_context( PIPE_RPC_FNS *list )
+{
+ PIPE_RPC_FNS *tmp = list;
+ PIPE_RPC_FNS *tmp2;
+
+ while (tmp) {
+ tmp2 = tmp->next;
+ SAFE_FREE(tmp);
+ tmp = tmp2;
+ }
+
+ return;
+}
+
+/****************************************************************************
+ Find the correct RPC function to call for this request.
+ If the pipe is authenticated then become the correct UNIX user
+ before doing the call.
+****************************************************************************/
+
+BOOL api_pipe_request(pipes_struct *p)
+{
+ BOOL ret = False;
+ PIPE_RPC_FNS *pipe_fns;
+
+ if (p->ntlmssp_auth_validated) {
+
+ if(!become_authenticated_pipe_user(p)) {
+ prs_mem_free(&p->out_data.rdata);
+ return False;
+ }
+ }
+
+ DEBUG(5, ("Requested \\PIPE\\%s\n", p->name));
+
+ /* get the set of RPC functions for this context */
+
+ pipe_fns = find_pipe_fns_by_context(p->contexts, p->hdr_req.context_id);
+
+ if ( pipe_fns ) {
+ set_current_rpc_talloc(p->mem_ctx);
+ ret = api_rpcTNP(p, p->name, pipe_fns->cmds, pipe_fns->n_cmds);
+ set_current_rpc_talloc(NULL);
+ }
+ else {
+ DEBUG(0,("api_pipe_request: No rpc function table associated with context [%d] on pipe [%s]\n",
+ p->hdr_req.context_id, p->name));
+ }
+
+ if(p->ntlmssp_auth_validated)
+ unbecome_authenticated_pipe_user();
+
+ return ret;
+}
+
+/*******************************************************************
+ Calls the underlying RPC function for a named pipe.
+ ********************************************************************/
+
+BOOL api_rpcTNP(pipes_struct *p, const char *rpc_name,
+ const struct api_struct *api_rpc_cmds, int n_cmds)
+{
+ int fn_num;
+ fstring name;
+ uint32 offset1, offset2;
+
+ /* interpret the command */
+ DEBUG(4,("api_rpcTNP: %s op 0x%x - ", rpc_name, p->hdr_req.opnum));
+
+ slprintf(name, sizeof(name)-1, "in_%s", rpc_name);
+ prs_dump(name, p->hdr_req.opnum, &p->in_data.data);
+
+ for (fn_num = 0; fn_num < n_cmds; fn_num++) {
+ if (api_rpc_cmds[fn_num].opnum == p->hdr_req.opnum && api_rpc_cmds[fn_num].fn != NULL) {
+ DEBUG(3,("api_rpcTNP: rpc command: %s\n", api_rpc_cmds[fn_num].name));
+ break;
+ }
+ }
+
+ if (fn_num == n_cmds) {
+ /*
+ * For an unknown RPC just return a fault PDU but
+ * return True to allow RPC's on the pipe to continue
+ * and not put the pipe into fault state. JRA.
+ */
+ DEBUG(4, ("unknown\n"));
+ setup_fault_pdu(p, NT_STATUS(0x1c010002));
+ return True;
+ }
+
+ offset1 = prs_offset(&p->out_data.rdata);
+
+ DEBUG(6, ("api_rpc_cmds[%d].fn == %p\n",
+ fn_num, api_rpc_cmds[fn_num].fn));
+ /* do the actual command */
+ if(!api_rpc_cmds[fn_num].fn(p)) {
+ DEBUG(0,("api_rpcTNP: %s: %s failed.\n", rpc_name, api_rpc_cmds[fn_num].name));
+ prs_mem_free(&p->out_data.rdata);
+ return False;
+ }
+
+ if (p->bad_handle_fault_state) {
+ DEBUG(4,("api_rpcTNP: bad handle fault return.\n"));
+ p->bad_handle_fault_state = False;
+ setup_fault_pdu(p, NT_STATUS(0x1C00001A));
+ return True;
+ }
+
+ slprintf(name, sizeof(name)-1, "out_%s", rpc_name);
+ offset2 = prs_offset(&p->out_data.rdata);
+ prs_set_offset(&p->out_data.rdata, offset1);
+ prs_dump(name, p->hdr_req.opnum, &p->out_data.rdata);
+ prs_set_offset(&p->out_data.rdata, offset2);
+
+ DEBUG(5,("api_rpcTNP: called %s successfully\n", rpc_name));
+
+ /* Check for buffer underflow in rpc parsing */
+
+ if ((DEBUGLEVEL >= 10) &&
+ (prs_offset(&p->in_data.data) != prs_data_size(&p->in_data.data))) {
+ size_t data_len = prs_data_size(&p->in_data.data) - prs_offset(&p->in_data.data);
+ char *data;
+
+ data = malloc(data_len);
+
+ DEBUG(10, ("api_rpcTNP: rpc input buffer underflow (parse error?)\n"));
+ if (data) {
+ prs_uint8s(False, "", &p->in_data.data, 0, (unsigned char *)data, (uint32)data_len);
+ SAFE_FREE(data);
+ }
+
+ }
+
+ return True;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+void get_pipe_fns( int idx, struct api_struct **fns, int *n_fns )
+{
+ struct api_struct *cmds = NULL;
+ int n_cmds = 0;
+
+ switch ( idx ) {
+ case PI_LSARPC:
+ lsa_get_pipe_fns( &cmds, &n_cmds );
+ break;
+ case PI_LSARPC_DS:
+ lsa_ds_get_pipe_fns( &cmds, &n_cmds );
+ break;
+ case PI_SAMR:
+ samr_get_pipe_fns( &cmds, &n_cmds );
+ break;
+ case PI_NETLOGON:
+ netlog_get_pipe_fns( &cmds, &n_cmds );
+ break;
+ case PI_SRVSVC:
+ srvsvc_get_pipe_fns( &cmds, &n_cmds );
+ break;
+ case PI_WKSSVC:
+ wkssvc_get_pipe_fns( &cmds, &n_cmds );
+ break;
+ case PI_WINREG:
+ reg_get_pipe_fns( &cmds, &n_cmds );
+ break;
+ case PI_SPOOLSS:
+ spoolss_get_pipe_fns( &cmds, &n_cmds );
+ break;
+ case PI_NETDFS:
+ netdfs_get_pipe_fns( &cmds, &n_cmds );
+ break;
+#ifdef DEVELOPER
+ case PI_ECHO:
+ echo_get_pipe_fns( &cmds, &n_cmds );
+ break;
+#endif
+ case PI_EPM:
+ epm_get_pipe_fns( &cmds, &n_cmds );
+ break;
+ default:
+ DEBUG(0,("get_pipe_fns: Unknown pipe index! [%d]\n", idx));
+ }
+
+ *fns = cmds;
+ *n_fns = n_cmds;
+
+ return;
+}
+
+
diff --git a/source/rpc_server/srv_pipe_hnd.c b/source/rpc_server/srv_pipe_hnd.c
new file mode 100644
index 00000000000..64ca8388d77
--- /dev/null
+++ b/source/rpc_server/srv_pipe_hnd.c
@@ -0,0 +1,1169 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1998,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ * Copyright (C) Jeremy Allison 1999.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#define PIPE "\\PIPE\\"
+#define PIPELEN strlen(PIPE)
+
+static smb_np_struct *chain_p;
+static int pipes_open;
+
+/*
+ * Sometimes I can't decide if I hate Windows printer driver
+ * writers more than I hate the Windows spooler service driver
+ * writers. This gets around a combination of bugs in the spooler
+ * and the HP 8500 PCL driver that causes a spooler spin. JRA.
+ *
+ * bumped up from 20 -> 64 after viewing traffic from WordPerfect
+ * 2002 running on NT 4.- SP6
+ * bumped up from 64 -> 256 after viewing traffic from con2prt
+ * for lots of printers on a WinNT 4.x SP6 box.
+ */
+
+#ifndef MAX_OPEN_SPOOLSS_PIPES
+#define MAX_OPEN_SPOOLSS_PIPES 256
+#endif
+static int current_spoolss_pipes_open;
+
+static smb_np_struct *Pipes;
+static pipes_struct *InternalPipes;
+static struct bitmap *bmap;
+
+/* TODO
+ * the following prototypes are declared here to avoid
+ * code being moved about too much for a patch to be
+ * disrupted / less obvious.
+ *
+ * these functions, and associated functions that they
+ * call, should be moved behind a .so module-loading
+ * system _anyway_. so that's the next step...
+ */
+
+static ssize_t read_from_internal_pipe(void *np_conn, char *data, size_t n,
+ BOOL *is_data_outstanding);
+static ssize_t write_to_internal_pipe(void *np_conn, char *data, size_t n);
+static BOOL close_internal_rpc_pipe_hnd(void *np_conn);
+static void *make_internal_rpc_pipe_p(char *pipe_name,
+ connection_struct *conn, uint16 vuid);
+
+/****************************************************************************
+ Pipe iterator functions.
+****************************************************************************/
+
+smb_np_struct *get_first_pipe(void)
+{
+ return Pipes;
+}
+
+smb_np_struct *get_next_pipe(smb_np_struct *p)
+{
+ return p->next;
+}
+
+/****************************************************************************
+ Internal Pipe iterator functions.
+****************************************************************************/
+
+pipes_struct *get_first_internal_pipe(void)
+{
+ return InternalPipes;
+}
+
+pipes_struct *get_next_internal_pipe(pipes_struct *p)
+{
+ return p->next;
+}
+
+/* this must be larger than the sum of the open files and directories */
+static int pipe_handle_offset;
+
+/****************************************************************************
+ Set the pipe_handle_offset. Called from smbd/files.c
+****************************************************************************/
+
+void set_pipe_handle_offset(int max_open_files)
+{
+ if(max_open_files < 0x7000)
+ pipe_handle_offset = 0x7000;
+ else
+ pipe_handle_offset = max_open_files + 10; /* For safety. :-) */
+}
+
+/****************************************************************************
+ Reset pipe chain handle number.
+****************************************************************************/
+
+void reset_chain_p(void)
+{
+ chain_p = NULL;
+}
+
+/****************************************************************************
+ Initialise pipe handle states.
+****************************************************************************/
+
+void init_rpc_pipe_hnd(void)
+{
+ bmap = bitmap_allocate(MAX_OPEN_PIPES);
+ if (!bmap)
+ exit_server("out of memory in init_rpc_pipe_hnd");
+}
+
+/****************************************************************************
+ Initialise an outgoing packet.
+****************************************************************************/
+
+static BOOL pipe_init_outgoing_data(pipes_struct *p)
+{
+ output_data *o_data = &p->out_data;
+
+ /* Reset the offset counters. */
+ o_data->data_sent_length = 0;
+ o_data->current_pdu_len = 0;
+ o_data->current_pdu_sent = 0;
+
+ memset(o_data->current_pdu, '\0', sizeof(o_data->current_pdu));
+
+ /* Free any memory in the current return data buffer. */
+ prs_mem_free(&o_data->rdata);
+
+ /*
+ * Initialize the outgoing RPC data buffer.
+ * we will use this as the raw data area for replying to rpc requests.
+ */
+ if(!prs_init(&o_data->rdata, MAX_PDU_FRAG_LEN, p->mem_ctx, MARSHALL)) {
+ DEBUG(0,("pipe_init_outgoing_data: malloc fail.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Find first available pipe slot.
+****************************************************************************/
+
+smb_np_struct *open_rpc_pipe_p(char *pipe_name,
+ connection_struct *conn, uint16 vuid)
+{
+ int i;
+ smb_np_struct *p, *p_it;
+ static int next_pipe;
+ BOOL is_spoolss_pipe = False;
+
+ DEBUG(4,("Open pipe requested %s (pipes_open=%d)\n",
+ pipe_name, pipes_open));
+
+ if (strstr(pipe_name, "spoolss"))
+ is_spoolss_pipe = True;
+
+ if (is_spoolss_pipe && current_spoolss_pipes_open >= MAX_OPEN_SPOOLSS_PIPES) {
+ DEBUG(10,("open_rpc_pipe_p: spooler bug workaround. Denying open on pipe %s\n",
+ pipe_name ));
+ return NULL;
+ }
+
+ /* not repeating pipe numbers makes it easier to track things in
+ log files and prevents client bugs where pipe numbers are reused
+ over connection restarts */
+ if (next_pipe == 0)
+ next_pipe = (sys_getpid() ^ time(NULL)) % MAX_OPEN_PIPES;
+
+ i = bitmap_find(bmap, next_pipe);
+
+ if (i == -1) {
+ DEBUG(0,("ERROR! Out of pipe structures\n"));
+ return NULL;
+ }
+
+ next_pipe = (i+1) % MAX_OPEN_PIPES;
+
+ for (p = Pipes; p; p = p->next)
+ DEBUG(5,("open_rpc_pipe_p: name %s pnum=%x\n", p->name, p->pnum));
+
+ p = (smb_np_struct *)malloc(sizeof(*p));
+
+ if (!p) {
+ DEBUG(0,("ERROR! no memory for pipes_struct!\n"));
+ return NULL;
+ }
+
+ ZERO_STRUCTP(p);
+
+ /* add a dso mechanism instead of this, here */
+
+ p->namedpipe_create = make_internal_rpc_pipe_p;
+ p->namedpipe_read = read_from_internal_pipe;
+ p->namedpipe_write = write_to_internal_pipe;
+ p->namedpipe_close = close_internal_rpc_pipe_hnd;
+
+ p->np_state = p->namedpipe_create(pipe_name, conn, vuid);
+
+ if (p->np_state == NULL) {
+ DEBUG(0,("open_rpc_pipe_p: make_internal_rpc_pipe_p failed.\n"));
+ SAFE_FREE(p);
+ return NULL;
+ }
+
+ DLIST_ADD(Pipes, p);
+
+ /*
+ * Initialize the incoming RPC data buffer with one PDU worth of memory.
+ * We cheat here and say we're marshalling, as we intend to add incoming
+ * data directly into the prs_struct and we want it to auto grow. We will
+ * change the type to UNMARSALLING before processing the stream.
+ */
+
+ bitmap_set(bmap, i);
+ i += pipe_handle_offset;
+
+ pipes_open++;
+
+ p->pnum = i;
+
+ p->open = True;
+ p->device_state = 0;
+ p->priority = 0;
+ p->conn = conn;
+ p->vuid = vuid;
+
+ p->max_trans_reply = 0;
+
+ fstrcpy(p->name, pipe_name);
+
+ DEBUG(4,("Opened pipe %s with handle %x (pipes_open=%d)\n",
+ pipe_name, i, pipes_open));
+
+ chain_p = p;
+
+ /* Iterate over p_it as a temp variable, to display all open pipes */
+ for (p_it = Pipes; p_it; p_it = p_it->next)
+ DEBUG(5,("open pipes: name %s pnum=%x\n", p_it->name, p_it->pnum));
+
+ return chain_p;
+}
+
+/****************************************************************************
+ Make an internal namedpipes structure
+****************************************************************************/
+
+static void *make_internal_rpc_pipe_p(char *pipe_name,
+ connection_struct *conn, uint16 vuid)
+{
+ pipes_struct *p;
+ user_struct *vuser = get_valid_user_struct(vuid);
+
+ DEBUG(4,("Create pipe requested %s\n", pipe_name));
+
+ if (!vuser && vuid != UID_FIELD_INVALID) {
+ DEBUG(0,("ERROR! vuid %d did not map to a valid vuser struct!\n", vuid));
+ return NULL;
+ }
+
+ p = (pipes_struct *)malloc(sizeof(*p));
+
+ if (!p)
+ {
+ DEBUG(0,("ERROR! no memory for pipes_struct!\n"));
+ return NULL;
+ }
+
+ ZERO_STRUCTP(p);
+
+ if ((p->mem_ctx = talloc_init("pipe %s %p", pipe_name, p)) == NULL) {
+ DEBUG(0,("open_rpc_pipe_p: talloc_init failed.\n"));
+ SAFE_FREE(p);
+ return NULL;
+ }
+
+ if (!init_pipe_handle_list(p, pipe_name)) {
+ DEBUG(0,("open_rpc_pipe_p: init_pipe_handles failed.\n"));
+ talloc_destroy(p->mem_ctx);
+ SAFE_FREE(p);
+ return NULL;
+ }
+
+ /*
+ * Initialize the incoming RPC data buffer with one PDU worth of memory.
+ * We cheat here and say we're marshalling, as we intend to add incoming
+ * data directly into the prs_struct and we want it to auto grow. We will
+ * change the type to UNMARSALLING before processing the stream.
+ */
+
+ if(!prs_init(&p->in_data.data, MAX_PDU_FRAG_LEN, p->mem_ctx, MARSHALL)) {
+ DEBUG(0,("open_rpc_pipe_p: malloc fail for in_data struct.\n"));
+ return NULL;
+ }
+
+ DLIST_ADD(InternalPipes, p);
+
+ p->conn = conn;
+
+ /* Ensure the connection isn't idled whilst this pipe is open. */
+ p->conn->num_files_open++;
+
+ p->vuid = vuid;
+
+ p->ntlmssp_chal_flags = 0;
+ p->ntlmssp_auth_validated = False;
+ p->ntlmssp_auth_requested = False;
+
+ p->pipe_bound = False;
+ p->fault_state = False;
+ p->endian = RPC_LITTLE_ENDIAN;
+
+ ZERO_STRUCT(p->pipe_user);
+
+ p->pipe_user.uid = (uid_t)-1;
+ p->pipe_user.gid = (gid_t)-1;
+
+ /* Store the session key and NT_TOKEN */
+ if (vuser) {
+ p->session_key = data_blob(vuser->session_key.data, vuser->session_key.length);
+ p->pipe_user.nt_user_token = dup_nt_token(vuser->nt_user_token);
+ init_privilege(&p->pipe_user.privs);
+ dup_priv_set(p->pipe_user.privs, vuser->privs);
+ }
+
+ /*
+ * Initialize the incoming RPC struct.
+ */
+
+ p->in_data.pdu_needed_len = 0;
+ p->in_data.pdu_received_len = 0;
+
+ /*
+ * Initialize the outgoing RPC struct.
+ */
+
+ p->out_data.current_pdu_len = 0;
+ p->out_data.current_pdu_sent = 0;
+ p->out_data.data_sent_length = 0;
+
+ /*
+ * Initialize the outgoing RPC data buffer with no memory.
+ */
+ prs_init(&p->out_data.rdata, 0, p->mem_ctx, MARSHALL);
+
+ fstrcpy(p->name, pipe_name);
+
+ DEBUG(4,("Created internal pipe %s (pipes_open=%d)\n",
+ pipe_name, pipes_open));
+
+ return (void*)p;
+}
+
+/****************************************************************************
+ Sets the fault state on incoming packets.
+****************************************************************************/
+
+static void set_incoming_fault(pipes_struct *p)
+{
+ prs_mem_free(&p->in_data.data);
+ p->in_data.pdu_needed_len = 0;
+ p->in_data.pdu_received_len = 0;
+ p->fault_state = True;
+ DEBUG(10,("set_incoming_fault: Setting fault state on pipe %s : vuid = 0x%x\n",
+ p->name, p->vuid ));
+}
+
+/****************************************************************************
+ Ensures we have at least RPC_HEADER_LEN amount of data in the incoming buffer.
+****************************************************************************/
+
+static ssize_t fill_rpc_header(pipes_struct *p, char *data, size_t data_to_copy)
+{
+ size_t len_needed_to_complete_hdr = MIN(data_to_copy, RPC_HEADER_LEN - p->in_data.pdu_received_len);
+
+ DEBUG(10,("fill_rpc_header: data_to_copy = %u, len_needed_to_complete_hdr = %u, receive_len = %u\n",
+ (unsigned int)data_to_copy, (unsigned int)len_needed_to_complete_hdr,
+ (unsigned int)p->in_data.pdu_received_len ));
+
+ memcpy((char *)&p->in_data.current_in_pdu[p->in_data.pdu_received_len], data, len_needed_to_complete_hdr);
+ p->in_data.pdu_received_len += len_needed_to_complete_hdr;
+
+ return (ssize_t)len_needed_to_complete_hdr;
+}
+
+/****************************************************************************
+ Unmarshalls a new PDU header. Assumes the raw header data is in current_in_pdu.
+****************************************************************************/
+
+static ssize_t unmarshall_rpc_header(pipes_struct *p)
+{
+ /*
+ * Unmarshall the header to determine the needed length.
+ */
+
+ prs_struct rpc_in;
+
+ if(p->in_data.pdu_received_len != RPC_HEADER_LEN) {
+ DEBUG(0,("unmarshall_rpc_header: assert on rpc header length failed.\n"));
+ set_incoming_fault(p);
+ return -1;
+ }
+
+ prs_init( &rpc_in, 0, p->mem_ctx, UNMARSHALL);
+ prs_set_endian_data( &rpc_in, p->endian);
+
+ prs_give_memory( &rpc_in, (char *)&p->in_data.current_in_pdu[0],
+ p->in_data.pdu_received_len, False);
+
+ /*
+ * Unmarshall the header as this will tell us how much
+ * data we need to read to get the complete pdu.
+ * This also sets the endian flag in rpc_in.
+ */
+
+ if(!smb_io_rpc_hdr("", &p->hdr, &rpc_in, 0)) {
+ DEBUG(0,("unmarshall_rpc_header: failed to unmarshall RPC_HDR.\n"));
+ set_incoming_fault(p);
+ prs_mem_free(&rpc_in);
+ return -1;
+ }
+
+ /*
+ * Validate the RPC header.
+ */
+
+ if(p->hdr.major != 5 && p->hdr.minor != 0) {
+ DEBUG(0,("unmarshall_rpc_header: invalid major/minor numbers in RPC_HDR.\n"));
+ set_incoming_fault(p);
+ prs_mem_free(&rpc_in);
+ return -1;
+ }
+
+ /*
+ * If there's not data in the incoming buffer this should be the start of a new RPC.
+ */
+
+ if(prs_offset(&p->in_data.data) == 0) {
+
+ /*
+ * AS/U doesn't set FIRST flag in a BIND packet it seems.
+ */
+
+ if ((p->hdr.pkt_type == RPC_REQUEST) && !(p->hdr.flags & RPC_FLG_FIRST)) {
+ /*
+ * Ensure that the FIRST flag is set. If not then we have
+ * a stream missmatch.
+ */
+
+ DEBUG(0,("unmarshall_rpc_header: FIRST flag not set in first PDU !\n"));
+ set_incoming_fault(p);
+ prs_mem_free(&rpc_in);
+ return -1;
+ }
+
+ /*
+ * If this is the first PDU then set the endianness
+ * flag in the pipe. We will need this when parsing all
+ * data in this RPC.
+ */
+
+ p->endian = rpc_in.bigendian_data;
+
+ DEBUG(5,("unmarshall_rpc_header: using %sendian RPC\n",
+ p->endian == RPC_LITTLE_ENDIAN ? "little-" : "big-" ));
+
+ } else {
+
+ /*
+ * If this is *NOT* the first PDU then check the endianness
+ * flag in the pipe is the same as that in the PDU.
+ */
+
+ if (p->endian != rpc_in.bigendian_data) {
+ DEBUG(0,("unmarshall_rpc_header: FIRST endianness flag (%d) different in next PDU !\n", (int)p->endian));
+ set_incoming_fault(p);
+ prs_mem_free(&rpc_in);
+ return -1;
+ }
+ }
+
+ /*
+ * Ensure that the pdu length is sane.
+ */
+
+ if((p->hdr.frag_len < RPC_HEADER_LEN) || (p->hdr.frag_len > MAX_PDU_FRAG_LEN)) {
+ DEBUG(0,("unmarshall_rpc_header: assert on frag length failed.\n"));
+ set_incoming_fault(p);
+ prs_mem_free(&rpc_in);
+ return -1;
+ }
+
+ DEBUG(10,("unmarshall_rpc_header: type = %u, flags = %u\n", (unsigned int)p->hdr.pkt_type,
+ (unsigned int)p->hdr.flags ));
+
+ /*
+ * Adjust for the header we just ate.
+ */
+ p->in_data.pdu_received_len = 0;
+ p->in_data.pdu_needed_len = (uint32)p->hdr.frag_len - RPC_HEADER_LEN;
+
+ /*
+ * Null the data we just ate.
+ */
+
+ memset((char *)&p->in_data.current_in_pdu[0], '\0', RPC_HEADER_LEN);
+
+ prs_mem_free(&rpc_in);
+
+ return 0; /* No extra data processed. */
+}
+
+/****************************************************************************
+ Call this to free any talloc'ed memory. Do this before and after processing
+ a complete PDU.
+****************************************************************************/
+
+void free_pipe_context(pipes_struct *p)
+{
+ if (p->mem_ctx) {
+ DEBUG(3,("free_pipe_context: destroying talloc pool of size %lu\n", (unsigned long)talloc_pool_size(p->mem_ctx) ));
+ talloc_destroy_pool(p->mem_ctx);
+ } else {
+ p->mem_ctx = talloc_init("pipe %s %p", p->name, p);
+ if (p->mem_ctx == NULL)
+ p->fault_state = True;
+ }
+}
+
+/****************************************************************************
+ Processes a request pdu. This will do auth processing if needed, and
+ appends the data into the complete stream if the LAST flag is not set.
+****************************************************************************/
+
+static BOOL process_request_pdu(pipes_struct *p, prs_struct *rpc_in_p)
+{
+ BOOL auth_verify = ((p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SIGN) != 0);
+ size_t data_len = p->hdr.frag_len - RPC_HEADER_LEN - RPC_HDR_REQ_LEN -
+ (auth_verify ? RPC_HDR_AUTH_LEN : 0) - p->hdr.auth_len;
+
+ if(!p->pipe_bound) {
+ DEBUG(0,("process_request_pdu: rpc request with no bind.\n"));
+ set_incoming_fault(p);
+ return False;
+ }
+
+ /*
+ * Check if we need to do authentication processing.
+ * This is only done on requests, not binds.
+ */
+
+ /*
+ * Read the RPC request header.
+ */
+
+ if(!smb_io_rpc_hdr_req("req", &p->hdr_req, rpc_in_p, 0)) {
+ DEBUG(0,("process_request_pdu: failed to unmarshall RPC_HDR_REQ.\n"));
+ set_incoming_fault(p);
+ return False;
+ }
+
+ if(p->ntlmssp_auth_validated && !api_pipe_auth_process(p, rpc_in_p)) {
+ DEBUG(0,("process_request_pdu: failed to do auth processing.\n"));
+ set_incoming_fault(p);
+ return False;
+ }
+
+ if (p->ntlmssp_auth_requested && !p->ntlmssp_auth_validated) {
+
+ /*
+ * Authentication _was_ requested and it already failed.
+ */
+
+ DEBUG(0,("process_request_pdu: RPC request received on pipe %s "
+ "where authentication failed. Denying the request.\n",
+ p->name));
+ set_incoming_fault(p);
+ return False;
+ }
+
+ if (p->netsec_auth_validated && !api_pipe_netsec_process(p, rpc_in_p)) {
+ DEBUG(0,("process_request_pdu: failed to do schannel processing.\n"));
+ set_incoming_fault(p);
+ return False;
+ }
+
+ /*
+ * Check the data length doesn't go over the 15Mb limit.
+ * increased after observing a bug in the Windows NT 4.0 SP6a
+ * spoolsv.exe when the response to a GETPRINTERDRIVER2 RPC
+ * will not fit in the initial buffer of size 0x1068 --jerry 22/01/2002
+ */
+
+ if(prs_offset(&p->in_data.data) + data_len > 15*1024*1024) {
+ DEBUG(0,("process_request_pdu: rpc data buffer too large (%u) + (%u)\n",
+ (unsigned int)prs_data_size(&p->in_data.data), (unsigned int)data_len ));
+ set_incoming_fault(p);
+ return False;
+ }
+
+ /*
+ * Append the data portion into the buffer and return.
+ */
+
+ if(!prs_append_some_prs_data(&p->in_data.data, rpc_in_p, prs_offset(rpc_in_p), data_len)) {
+ DEBUG(0,("process_request_pdu: Unable to append data size %u to parse buffer of size %u.\n",
+ (unsigned int)data_len, (unsigned int)prs_data_size(&p->in_data.data) ));
+ set_incoming_fault(p);
+ return False;
+ }
+
+ if(p->hdr.flags & RPC_FLG_LAST) {
+ BOOL ret = False;
+ /*
+ * Ok - we finally have a complete RPC stream.
+ * Call the rpc command to process it.
+ */
+
+ /*
+ * Ensure the internal prs buffer size is *exactly* the same
+ * size as the current offset.
+ */
+
+ if(!prs_set_buffer_size(&p->in_data.data, prs_offset(&p->in_data.data)))
+ {
+ DEBUG(0,("process_request_pdu: Call to prs_set_buffer_size failed!\n"));
+ set_incoming_fault(p);
+ return False;
+ }
+
+ /*
+ * Set the parse offset to the start of the data and set the
+ * prs_struct to UNMARSHALL.
+ */
+
+ prs_set_offset(&p->in_data.data, 0);
+ prs_switch_type(&p->in_data.data, UNMARSHALL);
+
+ /*
+ * Process the complete data stream here.
+ */
+
+ free_pipe_context(p);
+
+ if(pipe_init_outgoing_data(p))
+ ret = api_pipe_request(p);
+
+ free_pipe_context(p);
+
+ /*
+ * We have consumed the whole data stream. Set back to
+ * marshalling and set the offset back to the start of
+ * the buffer to re-use it (we could also do a prs_mem_free()
+ * and then re_init on the next start of PDU. Not sure which
+ * is best here.... JRA.
+ */
+
+ prs_switch_type(&p->in_data.data, MARSHALL);
+ prs_set_offset(&p->in_data.data, 0);
+ return ret;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Processes a finished PDU stored in current_in_pdu. The RPC_HEADER has
+ already been parsed and stored in p->hdr.
+****************************************************************************/
+
+static ssize_t process_complete_pdu(pipes_struct *p)
+{
+ prs_struct rpc_in;
+ size_t data_len = p->in_data.pdu_received_len;
+ char *data_p = (char *)&p->in_data.current_in_pdu[0];
+ BOOL reply = False;
+
+ if(p->fault_state) {
+ DEBUG(10,("process_complete_pdu: pipe %s in fault state.\n",
+ p->name ));
+ set_incoming_fault(p);
+ setup_fault_pdu(p, NT_STATUS(0x1c010002));
+ return (ssize_t)data_len;
+ }
+
+ prs_init( &rpc_in, 0, p->mem_ctx, UNMARSHALL);
+
+ /*
+ * Ensure we're using the corrent endianness for both the
+ * RPC header flags and the raw data we will be reading from.
+ */
+
+ prs_set_endian_data( &rpc_in, p->endian);
+ prs_set_endian_data( &p->in_data.data, p->endian);
+
+ prs_give_memory( &rpc_in, data_p, (uint32)data_len, False);
+
+ DEBUG(10,("process_complete_pdu: processing packet type %u\n",
+ (unsigned int)p->hdr.pkt_type ));
+
+ switch (p->hdr.pkt_type) {
+ case RPC_BIND:
+ case RPC_ALTCONT:
+ /*
+ * We assume that a pipe bind is only in one pdu.
+ */
+ if(pipe_init_outgoing_data(p))
+ reply = api_pipe_bind_req(p, &rpc_in);
+ break;
+ case RPC_BINDRESP:
+ /*
+ * We assume that a pipe bind_resp is only in one pdu.
+ */
+ if(pipe_init_outgoing_data(p))
+ reply = api_pipe_bind_auth_resp(p, &rpc_in);
+ break;
+ case RPC_REQUEST:
+ reply = process_request_pdu(p, &rpc_in);
+ break;
+ default:
+ DEBUG(0,("process_complete_pdu: Unknown rpc type = %u received.\n", (unsigned int)p->hdr.pkt_type ));
+ break;
+ }
+
+ /* Reset to little endian. Probably don't need this but it won't hurt. */
+ prs_set_endian_data( &p->in_data.data, RPC_LITTLE_ENDIAN);
+
+ if (!reply) {
+ DEBUG(3,("process_complete_pdu: DCE/RPC fault sent on pipe %s\n", p->pipe_srv_name));
+ set_incoming_fault(p);
+ setup_fault_pdu(p, NT_STATUS(0x1c010002));
+ prs_mem_free(&rpc_in);
+ } else {
+ /*
+ * Reset the lengths. We're ready for a new pdu.
+ */
+ p->in_data.pdu_needed_len = 0;
+ p->in_data.pdu_received_len = 0;
+ }
+
+ prs_mem_free(&rpc_in);
+ return (ssize_t)data_len;
+}
+
+/****************************************************************************
+ Accepts incoming data on an rpc pipe. Processes the data in pdu sized units.
+****************************************************************************/
+
+static ssize_t process_incoming_data(pipes_struct *p, char *data, size_t n)
+{
+ size_t data_to_copy = MIN(n, MAX_PDU_FRAG_LEN - p->in_data.pdu_received_len);
+ size_t old_pdu_received_len = p->in_data.pdu_received_len;
+
+ DEBUG(10,("process_incoming_data: Start: pdu_received_len = %u, pdu_needed_len = %u, incoming data = %u\n",
+ (unsigned int)p->in_data.pdu_received_len, (unsigned int)p->in_data.pdu_needed_len,
+ (unsigned int)n ));
+
+ if(data_to_copy == 0) {
+ /*
+ * This is an error - data is being received and there is no
+ * space in the PDU. Free the received data and go into the fault state.
+ */
+ DEBUG(0,("process_incoming_data: No space in incoming pdu buffer. Current size = %u \
+incoming data size = %u\n", (unsigned int)p->in_data.pdu_received_len, (unsigned int)n ));
+ set_incoming_fault(p);
+ return -1;
+ }
+
+ /*
+ * If we have no data already, wait until we get at least a RPC_HEADER_LEN
+ * number of bytes before we can do anything.
+ */
+
+ if((p->in_data.pdu_needed_len == 0) && (p->in_data.pdu_received_len < RPC_HEADER_LEN)) {
+ /*
+ * Always return here. If we have more data then the RPC_HEADER
+ * will be processed the next time around the loop.
+ */
+ return fill_rpc_header(p, data, data_to_copy);
+ }
+
+ /*
+ * At this point we know we have at least an RPC_HEADER_LEN amount of data
+ * stored in current_in_pdu.
+ */
+
+ /*
+ * If pdu_needed_len is zero this is a new pdu.
+ * Unmarshall the header so we know how much more
+ * data we need, then loop again.
+ */
+
+ if(p->in_data.pdu_needed_len == 0)
+ return unmarshall_rpc_header(p);
+
+ /*
+ * Ok - at this point we have a valid RPC_HEADER in p->hdr.
+ * Keep reading until we have a full pdu.
+ */
+
+ data_to_copy = MIN(data_to_copy, p->in_data.pdu_needed_len);
+
+ /*
+ * Copy as much of the data as we need into the current_in_pdu buffer.
+ */
+
+ memcpy( (char *)&p->in_data.current_in_pdu[p->in_data.pdu_received_len], data, data_to_copy);
+ p->in_data.pdu_received_len += data_to_copy;
+
+ /*
+ * Do we have a complete PDU ?
+ * (return the nym of bytes handled in the call)
+ */
+
+ if(p->in_data.pdu_received_len == p->in_data.pdu_needed_len)
+ return process_complete_pdu(p) - old_pdu_received_len;
+
+ DEBUG(10,("process_incoming_data: not a complete PDU yet. pdu_received_len = %u, pdu_needed_len = %u\n",
+ (unsigned int)p->in_data.pdu_received_len, (unsigned int)p->in_data.pdu_needed_len ));
+
+ return (ssize_t)data_to_copy;
+
+}
+
+/****************************************************************************
+ Accepts incoming data on an rpc pipe.
+****************************************************************************/
+
+ssize_t write_to_pipe(smb_np_struct *p, char *data, size_t n)
+{
+ DEBUG(6,("write_to_pipe: %x", p->pnum));
+
+ DEBUG(6,(" name: %s open: %s len: %d\n",
+ p->name, BOOLSTR(p->open), (int)n));
+
+ dump_data(50, data, n);
+
+ return p->namedpipe_write(p->np_state, data, n);
+}
+
+/****************************************************************************
+ Accepts incoming data on an internal rpc pipe.
+****************************************************************************/
+
+static ssize_t write_to_internal_pipe(void *np_conn, char *data, size_t n)
+{
+ pipes_struct *p = (pipes_struct*)np_conn;
+ size_t data_left = n;
+
+ while(data_left) {
+ ssize_t data_used;
+
+ DEBUG(10,("write_to_pipe: data_left = %u\n", (unsigned int)data_left ));
+
+ data_used = process_incoming_data(p, data, data_left);
+
+ DEBUG(10,("write_to_pipe: data_used = %d\n", (int)data_used ));
+
+ if(data_used < 0)
+ return -1;
+
+ data_left -= data_used;
+ data += data_used;
+ }
+
+ return n;
+}
+
+/****************************************************************************
+ Replies to a request to read data from a pipe.
+
+ Headers are interspersed with the data at PDU intervals. By the time
+ this function is called, the start of the data could possibly have been
+ read by an SMBtrans (file_offset != 0).
+
+ Calling create_rpc_reply() here is a hack. The data should already
+ have been prepared into arrays of headers + data stream sections.
+****************************************************************************/
+
+ssize_t read_from_pipe(smb_np_struct *p, char *data, size_t n,
+ BOOL *is_data_outstanding)
+{
+ if (!p || !p->open) {
+ DEBUG(0,("read_from_pipe: pipe not open\n"));
+ return -1;
+ }
+
+ DEBUG(6,("read_from_pipe: %x", p->pnum));
+
+ return p->namedpipe_read(p->np_state, data, n, is_data_outstanding);
+}
+
+/****************************************************************************
+ Replies to a request to read data from a pipe.
+
+ Headers are interspersed with the data at PDU intervals. By the time
+ this function is called, the start of the data could possibly have been
+ read by an SMBtrans (file_offset != 0).
+
+ Calling create_rpc_reply() here is a hack. The data should already
+ have been prepared into arrays of headers + data stream sections.
+****************************************************************************/
+
+static ssize_t read_from_internal_pipe(void *np_conn, char *data, size_t n,
+ BOOL *is_data_outstanding)
+{
+ pipes_struct *p = (pipes_struct*)np_conn;
+ uint32 pdu_remaining = 0;
+ ssize_t data_returned = 0;
+
+ if (!p) {
+ DEBUG(0,("read_from_pipe: pipe not open\n"));
+ return -1;
+ }
+
+ DEBUG(6,(" name: %s len: %u\n", p->name, (unsigned int)n));
+
+ /*
+ * We cannot return more than one PDU length per
+ * read request.
+ */
+
+ /*
+ * This condition should result in the connection being closed.
+ * Netapp filers seem to set it to 0xffff which results in domain
+ * authentications failing. Just ignore it so things work.
+ */
+
+ if(n > MAX_PDU_FRAG_LEN) {
+ DEBUG(5,("read_from_pipe: too large read (%u) requested on \
+pipe %s. We can only service %d sized reads.\n", (unsigned int)n, p->name, MAX_PDU_FRAG_LEN ));
+ }
+
+ /*
+ * Determine if there is still data to send in the
+ * pipe PDU buffer. Always send this first. Never
+ * send more than is left in the current PDU. The
+ * client should send a new read request for a new
+ * PDU.
+ */
+
+ if((pdu_remaining = p->out_data.current_pdu_len - p->out_data.current_pdu_sent) > 0) {
+ data_returned = (ssize_t)MIN(n, pdu_remaining);
+
+ DEBUG(10,("read_from_pipe: %s: current_pdu_len = %u, current_pdu_sent = %u \
+returning %d bytes.\n", p->name, (unsigned int)p->out_data.current_pdu_len,
+ (unsigned int)p->out_data.current_pdu_sent, (int)data_returned));
+
+ memcpy( data, &p->out_data.current_pdu[p->out_data.current_pdu_sent], (size_t)data_returned);
+ p->out_data.current_pdu_sent += (uint32)data_returned;
+ goto out;
+ }
+
+ /*
+ * At this point p->current_pdu_len == p->current_pdu_sent (which
+ * may of course be zero if this is the first return fragment.
+ */
+
+ DEBUG(10,("read_from_pipe: %s: fault_state = %d : data_sent_length \
+= %u, prs_offset(&p->out_data.rdata) = %u.\n",
+ p->name, (int)p->fault_state, (unsigned int)p->out_data.data_sent_length, (unsigned int)prs_offset(&p->out_data.rdata) ));
+
+ if(p->out_data.data_sent_length >= prs_offset(&p->out_data.rdata)) {
+ /*
+ * We have sent all possible data, return 0.
+ */
+ data_returned = 0;
+ goto out;
+ }
+
+ /*
+ * We need to create a new PDU from the data left in p->rdata.
+ * Create the header/data/footers. This also sets up the fields
+ * p->current_pdu_len, p->current_pdu_sent, p->data_sent_length
+ * and stores the outgoing PDU in p->current_pdu.
+ */
+
+ if(!create_next_pdu(p)) {
+ DEBUG(0,("read_from_pipe: %s: create_next_pdu failed.\n", p->name));
+ return -1;
+ }
+
+ data_returned = MIN(n, p->out_data.current_pdu_len);
+
+ memcpy( data, p->out_data.current_pdu, (size_t)data_returned);
+ p->out_data.current_pdu_sent += (uint32)data_returned;
+
+ out:
+
+ (*is_data_outstanding) = p->out_data.current_pdu_len > n;
+ return data_returned;
+}
+
+/****************************************************************************
+ Wait device state on a pipe. Exactly what this is for is unknown...
+****************************************************************************/
+
+BOOL wait_rpc_pipe_hnd_state(smb_np_struct *p, uint16 priority)
+{
+ if (p == NULL)
+ return False;
+
+ if (p->open) {
+ DEBUG(3,("wait_rpc_pipe_hnd_state: Setting pipe wait state priority=%x on pipe (name=%s)\n",
+ priority, p->name));
+
+ p->priority = priority;
+
+ return True;
+ }
+
+ DEBUG(3,("wait_rpc_pipe_hnd_state: Error setting pipe wait state priority=%x (name=%s)\n",
+ priority, p->name));
+ return False;
+}
+
+
+/****************************************************************************
+ Set device state on a pipe. Exactly what this is for is unknown...
+****************************************************************************/
+
+BOOL set_rpc_pipe_hnd_state(smb_np_struct *p, uint16 device_state)
+{
+ if (p == NULL)
+ return False;
+
+ if (p->open) {
+ DEBUG(3,("set_rpc_pipe_hnd_state: Setting pipe device state=%x on pipe (name=%s)\n",
+ device_state, p->name));
+
+ p->device_state = device_state;
+
+ return True;
+ }
+
+ DEBUG(3,("set_rpc_pipe_hnd_state: Error setting pipe device state=%x (name=%s)\n",
+ device_state, p->name));
+ return False;
+}
+
+
+/****************************************************************************
+ Close an rpc pipe.
+****************************************************************************/
+
+BOOL close_rpc_pipe_hnd(smb_np_struct *p)
+{
+ if (!p) {
+ DEBUG(0,("Invalid pipe in close_rpc_pipe_hnd\n"));
+ return False;
+ }
+
+ p->namedpipe_close(p->np_state);
+
+ bitmap_clear(bmap, p->pnum - pipe_handle_offset);
+
+ pipes_open--;
+
+ DEBUG(4,("closed pipe name %s pnum=%x (pipes_open=%d)\n",
+ p->name, p->pnum, pipes_open));
+
+ DLIST_REMOVE(Pipes, p);
+
+ ZERO_STRUCTP(p);
+
+ SAFE_FREE(p);
+
+ return True;
+}
+
+/****************************************************************************
+ Close an rpc pipe.
+****************************************************************************/
+
+static BOOL close_internal_rpc_pipe_hnd(void *np_conn)
+{
+ pipes_struct *p = (pipes_struct *)np_conn;
+ if (!p) {
+ DEBUG(0,("Invalid pipe in close_internal_rpc_pipe_hnd\n"));
+ return False;
+ }
+
+ prs_mem_free(&p->out_data.rdata);
+ prs_mem_free(&p->in_data.data);
+
+ if (p->mem_ctx)
+ talloc_destroy(p->mem_ctx);
+
+ free_pipe_rpc_context( p->contexts );
+
+ /* Free the handles database. */
+ close_policy_by_pipe(p);
+
+ delete_nt_token(&p->pipe_user.nt_user_token);
+ SAFE_FREE(p->pipe_user.groups);
+
+ DLIST_REMOVE(InternalPipes, p);
+
+ p->conn->num_files_open--;
+
+ ZERO_STRUCTP(p);
+
+ SAFE_FREE(p);
+
+ return True;
+}
+
+/****************************************************************************
+ Find an rpc pipe given a pipe handle in a buffer and an offset.
+****************************************************************************/
+
+smb_np_struct *get_rpc_pipe_p(char *buf, int where)
+{
+ int pnum = SVAL(buf,where);
+
+ if (chain_p)
+ return chain_p;
+
+ return get_rpc_pipe(pnum);
+}
+
+/****************************************************************************
+ Find an rpc pipe given a pipe handle.
+****************************************************************************/
+
+smb_np_struct *get_rpc_pipe(int pnum)
+{
+ smb_np_struct *p;
+
+ DEBUG(4,("search for pipe pnum=%x\n", pnum));
+
+ for (p=Pipes;p;p=p->next)
+ DEBUG(5,("pipe name %s pnum=%x (pipes_open=%d)\n",
+ p->name, p->pnum, pipes_open));
+
+ for (p=Pipes;p;p=p->next) {
+ if (p->pnum == pnum) {
+ chain_p = p;
+ return p;
+ }
+ }
+
+ return NULL;
+}
diff --git a/source/rpc_server/srv_reg.c b/source/rpc_server/srv_reg.c
new file mode 100644
index 00000000000..b780be0aff3
--- /dev/null
+++ b/source/rpc_server/srv_reg.c
@@ -0,0 +1,402 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997,
+ * Copyright (C) Marc Jacobsen 2000,
+ * Copyright (C) Jeremy Allison 2001,
+ * Copyright (C) Gerald Carter 2002,
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This is the interface for the registry functions. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*******************************************************************
+ api_reg_close
+ ********************************************************************/
+
+static BOOL api_reg_close(pipes_struct *p)
+{
+ REG_Q_CLOSE q_u;
+ REG_R_CLOSE r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the reg unknown 1 */
+ if(!reg_io_q_close("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _reg_close(p, &q_u, &r_u);
+
+ if(!reg_io_r_close("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_reg_open_khlm
+ ********************************************************************/
+
+static BOOL api_reg_open_hklm(pipes_struct *p)
+{
+ REG_Q_OPEN_HKLM q_u;
+ REG_R_OPEN_HKLM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the reg open */
+ if(!reg_io_q_open_hklm("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _reg_open_hklm(p, &q_u, &r_u);
+
+ if(!reg_io_r_open_hklm("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_reg_open_khu
+ ********************************************************************/
+
+static BOOL api_reg_open_hku(pipes_struct *p)
+{
+ REG_Q_OPEN_HKU q_u;
+ REG_R_OPEN_HKU r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the reg open */
+ if(!reg_io_q_open_hku("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _reg_open_hku(p, &q_u, &r_u);
+
+ if(!reg_io_r_open_hku("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_reg_open_khcr
+ ********************************************************************/
+
+static BOOL api_reg_open_hkcr(pipes_struct *p)
+{
+ REG_Q_OPEN_HKCR q_u;
+ REG_R_OPEN_HKCR r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the reg open */
+ if(!reg_io_q_open_hkcr("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _reg_open_hkcr(p, &q_u, &r_u);
+
+ if(!reg_io_r_open_hkcr("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ api_reg_open_entry
+ ********************************************************************/
+
+static BOOL api_reg_open_entry(pipes_struct *p)
+{
+ REG_Q_OPEN_ENTRY q_u;
+ REG_R_OPEN_ENTRY r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the reg open entry */
+ if(!reg_io_q_open_entry("", &q_u, data, 0))
+ return False;
+
+ /* construct reply. */
+ r_u.status = _reg_open_entry(p, &q_u, &r_u);
+
+ if(!reg_io_r_open_entry("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_reg_info
+ ********************************************************************/
+
+static BOOL api_reg_info(pipes_struct *p)
+{
+ REG_Q_INFO q_u;
+ REG_R_INFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the reg unknown 0x11*/
+ if(!reg_io_q_info("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _reg_info(p, &q_u, &r_u);
+
+ if(!reg_io_r_info("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_reg_shutdown
+ ********************************************************************/
+
+static BOOL api_reg_shutdown(pipes_struct *p)
+{
+ REG_Q_SHUTDOWN q_u;
+ REG_R_SHUTDOWN r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the reg shutdown */
+ if(!reg_io_q_shutdown("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _reg_shutdown(p, &q_u, &r_u);
+
+ if(!reg_io_r_shutdown("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_reg_abort_shutdown
+ ********************************************************************/
+
+static BOOL api_reg_abort_shutdown(pipes_struct *p)
+{
+ REG_Q_ABORT_SHUTDOWN q_u;
+ REG_R_ABORT_SHUTDOWN r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the reg shutdown */
+ if(!reg_io_q_abort_shutdown("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _reg_abort_shutdown(p, &q_u, &r_u);
+
+ if(!reg_io_r_abort_shutdown("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ api_reg_query_key
+ ********************************************************************/
+
+static BOOL api_reg_query_key(pipes_struct *p)
+{
+ REG_Q_QUERY_KEY q_u;
+ REG_R_QUERY_KEY r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!reg_io_q_query_key("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _reg_query_key(p, &q_u, &r_u);
+
+ if(!reg_io_r_query_key("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_reg_unknown_1a
+ ********************************************************************/
+
+static BOOL api_reg_unknown_1a(pipes_struct *p)
+{
+ REG_Q_UNKNOWN_1A q_u;
+ REG_R_UNKNOWN_1A r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!reg_io_q_unknown_1a("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _reg_unknown_1a(p, &q_u, &r_u);
+
+ if(!reg_io_r_unknown_1a("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_reg_enum_key
+ ********************************************************************/
+
+static BOOL api_reg_enum_key(pipes_struct *p)
+{
+ REG_Q_ENUM_KEY q_u;
+ REG_R_ENUM_KEY r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!reg_io_q_enum_key("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _reg_enum_key(p, &q_u, &r_u);
+
+ if(!reg_io_r_enum_key("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_reg_enum_value
+ ********************************************************************/
+
+static BOOL api_reg_enum_value(pipes_struct *p)
+{
+ REG_Q_ENUM_VALUE q_u;
+ REG_R_ENUM_VALUE r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!reg_io_q_enum_val("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _reg_enum_value(p, &q_u, &r_u);
+
+ if(!reg_io_r_enum_val("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_reg_save_key
+ ********************************************************************/
+
+static BOOL api_reg_save_key(pipes_struct *p)
+{
+ REG_Q_SAVE_KEY q_u;
+ REG_R_SAVE_KEY r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!reg_io_q_save_key("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _reg_save_key(p, &q_u, &r_u);
+
+ if(!reg_io_r_save_key("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ array of \PIPE\reg operations
+ ********************************************************************/
+
+static struct api_struct api_reg_cmds[] =
+{
+ { "REG_CLOSE" , REG_CLOSE , api_reg_close },
+ { "REG_OPEN_ENTRY" , REG_OPEN_ENTRY , api_reg_open_entry },
+ { "REG_OPEN_HKCR" , REG_OPEN_HKCR , api_reg_open_hkcr },
+ { "REG_OPEN_HKLM" , REG_OPEN_HKLM , api_reg_open_hklm },
+ { "REG_OPEN_HKU" , REG_OPEN_HKU , api_reg_open_hku },
+ { "REG_ENUM_KEY" , REG_ENUM_KEY , api_reg_enum_key },
+ { "REG_ENUM_VALUE" , REG_ENUM_VALUE , api_reg_enum_value },
+ { "REG_QUERY_KEY" , REG_QUERY_KEY , api_reg_query_key },
+ { "REG_INFO" , REG_INFO , api_reg_info },
+ { "REG_SHUTDOWN" , REG_SHUTDOWN , api_reg_shutdown },
+ { "REG_ABORT_SHUTDOWN" , REG_ABORT_SHUTDOWN , api_reg_abort_shutdown },
+ { "REG_UNKNOWN_1A" , REG_UNKNOWN_1A , api_reg_unknown_1a },
+ { "REG_SAVE_KEY" , REG_SAVE_KEY , api_reg_save_key }
+};
+
+void reg_get_pipe_fns( struct api_struct **fns, int *n_fns )
+{
+ *fns = api_reg_cmds;
+ *n_fns = sizeof(api_reg_cmds) / sizeof(struct api_struct);
+}
+
+NTSTATUS rpc_reg_init(void)
+{
+
+ return rpc_pipe_register_commands(SMB_RPC_INTERFACE_VERSION, "winreg", "winreg", api_reg_cmds,
+ sizeof(api_reg_cmds) / sizeof(struct api_struct));
+}
diff --git a/source/rpc_server/srv_reg_nt.c b/source/rpc_server/srv_reg_nt.c
new file mode 100644
index 00000000000..a4e3638be60
--- /dev/null
+++ b/source/rpc_server/srv_reg_nt.c
@@ -0,0 +1,664 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997.
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997.
+ * Copyright (C) Paul Ashton 1997.
+ * Copyright (C) Jeremy Allison 2001.
+ * Copyright (C) Gerald Carter 2002.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Implementation of registry functions. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#define REGSTR_PRODUCTTYPE "ProductType"
+#define REG_PT_WINNT "WinNT"
+#define REG_PT_LANMANNT "LanmanNT"
+#define REG_PT_SERVERNT "ServerNT"
+
+#define OUR_HANDLE(hnd) (((hnd)==NULL)?"NULL":(IVAL((hnd)->data5,4)==(uint32)sys_getpid()?"OURS":"OTHER")), \
+((unsigned int)IVAL((hnd)->data5,4)),((unsigned int)sys_getpid())
+
+
+static REGISTRY_KEY *regkeys_list;
+
+
+/******************************************************************
+ free() function for REGISTRY_KEY
+ *****************************************************************/
+
+static void free_regkey_info(void *ptr)
+{
+ REGISTRY_KEY *info = (REGISTRY_KEY*)ptr;
+
+ DLIST_REMOVE(regkeys_list, info);
+
+ SAFE_FREE(info);
+}
+
+/******************************************************************
+ Find a registry key handle and return a REGISTRY_KEY
+ *****************************************************************/
+
+static REGISTRY_KEY *find_regkey_index_by_hnd(pipes_struct *p, POLICY_HND *hnd)
+{
+ REGISTRY_KEY *regkey = NULL;
+
+ if(!find_policy_by_hnd(p,hnd,(void **)&regkey)) {
+ DEBUG(2,("find_regkey_index_by_hnd: Registry Key not found: "));
+ return NULL;
+ }
+
+ return regkey;
+}
+
+
+/*******************************************************************
+ Function for open a new registry handle and creating a handle
+ Note that P should be valid & hnd should already have space
+
+ When we open a key, we store the full path to the key as
+ HK[LM|U]\<key>\<key>\...
+ *******************************************************************/
+
+static NTSTATUS open_registry_key(pipes_struct *p, POLICY_HND *hnd, REGISTRY_KEY *parent,
+ const char *subkeyname, uint32 access_granted )
+{
+ REGISTRY_KEY *regkey = NULL;
+ NTSTATUS result = NT_STATUS_OK;
+ REGSUBKEY_CTR subkeys;
+ pstring subkeyname2;
+ int subkey_len;
+
+ DEBUG(7,("open_registry_key: name = [%s][%s]\n",
+ parent ? parent->name : "NULL", subkeyname));
+
+ /* strip any trailing '\'s */
+ pstrcpy( subkeyname2, subkeyname );
+ subkey_len = strlen ( subkeyname2 );
+ if ( subkey_len && subkeyname2[subkey_len-1] == '\\' )
+ subkeyname2[subkey_len-1] = '\0';
+
+ if ((regkey=(REGISTRY_KEY*)malloc(sizeof(REGISTRY_KEY))) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP( regkey );
+
+ /*
+ * very crazy, but regedit.exe on Win2k will attempt to call
+ * REG_OPEN_ENTRY with a keyname of "". We should return a new
+ * (second) handle here on the key->name. regedt32.exe does
+ * not do this stupidity. --jerry
+ */
+
+ if ( !subkey_len ) {
+ pstrcpy( regkey->name, parent->name );
+ }
+ else {
+ pstrcpy( regkey->name, "" );
+ if ( parent ) {
+ pstrcat( regkey->name, parent->name );
+ pstrcat( regkey->name, "\\" );
+ }
+ pstrcat( regkey->name, subkeyname2 );
+ }
+
+ /* Look up the table of registry I/O operations */
+
+ if ( !(regkey->hook = reghook_cache_find( regkey->name )) ) {
+ DEBUG(0,("open_registry_key: Failed to assigned a REGISTRY_HOOK to [%s]\n",
+ regkey->name ));
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+
+ /* check if the path really exists; failed is indicated by -1 */
+ /* if the subkey count failed, bail out */
+
+ ZERO_STRUCTP( &subkeys );
+
+ regsubkey_ctr_init( &subkeys );
+
+ if ( fetch_reg_keys( regkey, &subkeys ) == -1 ) {
+
+ /* don't really know what to return here */
+ result = NT_STATUS_NO_SUCH_FILE;
+ }
+ else {
+ /*
+ * This would previously return NT_STATUS_TOO_MANY_SECRETS
+ * that doesn't sound quite right to me --jerry
+ */
+
+ if ( !create_policy_hnd( p, hnd, free_regkey_info, regkey ) )
+ result = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ /* clean up */
+
+ regsubkey_ctr_destroy( &subkeys );
+
+ if ( ! NT_STATUS_IS_OK(result) )
+ SAFE_FREE( regkey );
+ else
+ DLIST_ADD( regkeys_list, regkey );
+
+
+ DEBUG(7,("open_registry_key: exit\n"));
+
+ return result;
+}
+
+/*******************************************************************
+ Function for open a new registry handle and creating a handle
+ Note that P should be valid & hnd should already have space
+ *******************************************************************/
+
+static BOOL close_registry_key(pipes_struct *p, POLICY_HND *hnd)
+{
+ REGISTRY_KEY *regkey = find_regkey_index_by_hnd(p, hnd);
+
+ if ( !regkey ) {
+ DEBUG(2,("close_registry_key: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(hnd)));
+ return False;
+ }
+
+ close_policy_hnd(p, hnd);
+
+ return True;
+}
+
+/********************************************************************
+ retrieve information about the subkeys
+ *******************************************************************/
+
+static BOOL get_subkey_information( REGISTRY_KEY *key, uint32 *maxnum, uint32 *maxlen )
+{
+ int num_subkeys, i;
+ uint32 max_len;
+ REGSUBKEY_CTR subkeys;
+ uint32 len;
+
+ if ( !key )
+ return False;
+
+ ZERO_STRUCTP( &subkeys );
+
+ regsubkey_ctr_init( &subkeys );
+
+ if ( fetch_reg_keys( key, &subkeys ) == -1 )
+ return False;
+
+ /* find the longest string */
+
+ max_len = 0;
+ num_subkeys = regsubkey_ctr_numkeys( &subkeys );
+
+ for ( i=0; i<num_subkeys; i++ ) {
+ len = strlen( regsubkey_ctr_specific_key(&subkeys, i) );
+ max_len = MAX(max_len, len);
+ }
+
+ *maxnum = num_subkeys;
+ *maxlen = max_len*2;
+
+ regsubkey_ctr_destroy( &subkeys );
+
+ return True;
+}
+
+/********************************************************************
+ retrieve information about the values. We don't store values
+ here. The registry tdb is intended to be a frontend to oether
+ Samba tdb's (such as ntdrivers.tdb).
+ *******************************************************************/
+
+static BOOL get_value_information( REGISTRY_KEY *key, uint32 *maxnum,
+ uint32 *maxlen, uint32 *maxsize )
+{
+ REGVAL_CTR values;
+ REGISTRY_VALUE *val;
+ uint32 sizemax, lenmax;
+ int i, num_values;
+
+ if ( !key )
+ return False;
+
+
+ ZERO_STRUCTP( &values );
+
+ regval_ctr_init( &values );
+
+ if ( fetch_reg_values( key, &values ) == -1 )
+ return False;
+
+ lenmax = sizemax = 0;
+ num_values = regval_ctr_numvals( &values );
+
+ val = regval_ctr_specific_value( &values, 0 );
+
+ for ( i=0; i<num_values && val; i++ )
+ {
+ lenmax = MAX(lenmax, strlen(val->valuename)+1 );
+ sizemax = MAX(sizemax, val->size );
+
+ val = regval_ctr_specific_value( &values, i );
+ }
+
+ *maxnum = num_values;
+ *maxlen = lenmax;
+ *maxsize = sizemax;
+
+ regval_ctr_destroy( &values );
+
+ return True;
+}
+
+
+/********************************************************************
+ reg_close
+ ********************************************************************/
+
+NTSTATUS _reg_close(pipes_struct *p, REG_Q_CLOSE *q_u, REG_R_CLOSE *r_u)
+{
+ /* set up the REG unknown_1 response */
+ ZERO_STRUCT(r_u->pol);
+
+ /* close the policy handle */
+ if (!close_registry_key(p, &q_u->pol))
+ return NT_STATUS_OBJECT_NAME_INVALID;
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+NTSTATUS _reg_open_hklm(pipes_struct *p, REG_Q_OPEN_HKLM *q_u, REG_R_OPEN_HKLM *r_u)
+{
+ return open_registry_key( p, &r_u->pol, NULL, KEY_HKLM, 0x0 );
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+NTSTATUS _reg_open_hkcr(pipes_struct *p, REG_Q_OPEN_HKCR *q_u, REG_R_OPEN_HKCR *r_u)
+{
+ return open_registry_key( p, &r_u->pol, NULL, KEY_HKCR, 0x0 );
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+NTSTATUS _reg_open_hku(pipes_struct *p, REG_Q_OPEN_HKU *q_u, REG_R_OPEN_HKU *r_u)
+{
+ return open_registry_key( p, &r_u->pol, NULL, KEY_HKU, 0x0 );
+}
+
+/*******************************************************************
+ reg_reply_open_entry
+ ********************************************************************/
+
+NTSTATUS _reg_open_entry(pipes_struct *p, REG_Q_OPEN_ENTRY *q_u, REG_R_OPEN_ENTRY *r_u)
+{
+ POLICY_HND pol;
+ fstring name;
+ REGISTRY_KEY *key = find_regkey_index_by_hnd(p, &q_u->pol);
+ NTSTATUS result;
+
+ DEBUG(5,("reg_open_entry: Enter\n"));
+
+ if ( !key )
+ return NT_STATUS_INVALID_HANDLE;
+
+ rpcstr_pull(name,q_u->uni_name.buffer,sizeof(name),q_u->uni_name.uni_str_len*2,0);
+
+ result = open_registry_key( p, &pol, key, name, 0x0 );
+
+ init_reg_r_open_entry( r_u, &pol, result );
+
+ DEBUG(5,("reg_open_entry: Exit\n"));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ reg_reply_info
+ ********************************************************************/
+
+NTSTATUS _reg_info(pipes_struct *p, REG_Q_INFO *q_u, REG_R_INFO *r_u)
+{
+ NTSTATUS status = NT_STATUS_NO_SUCH_FILE;
+ fstring name;
+ const char *value_ascii = "";
+ fstring value;
+ int value_length;
+ REGISTRY_KEY *regkey = find_regkey_index_by_hnd( p, &q_u->pol );
+ REGISTRY_VALUE *val = NULL;
+ REGVAL_CTR regvals;
+ int i;
+
+ DEBUG(5,("_reg_info: Enter\n"));
+
+ if ( !regkey )
+ return NT_STATUS_INVALID_HANDLE;
+
+ DEBUG(7,("_reg_info: policy key name = [%s]\n", regkey->name));
+
+ rpcstr_pull(name, q_u->uni_type.buffer, sizeof(name), q_u->uni_type.uni_str_len*2, 0);
+
+ DEBUG(5,("reg_info: looking up value: [%s]\n", name));
+
+ ZERO_STRUCTP( &regvals );
+
+ regval_ctr_init( &regvals );
+
+ /* couple of hard coded registry values */
+
+ if ( strequal(name, "RefusePasswordChange") ) {
+ if ( (val = (REGISTRY_VALUE*)malloc(sizeof(REGISTRY_VALUE))) == NULL ) {
+ DEBUG(0,("_reg_info: malloc() failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ ZERO_STRUCTP( val );
+
+ goto out;
+ }
+
+ if ( strequal(name, REGSTR_PRODUCTTYPE) ) {
+ /* This makes the server look like a member server to clients */
+ /* which tells clients that we have our own local user and */
+ /* group databases and helps with ACL support. */
+
+ switch (lp_server_role()) {
+ case ROLE_DOMAIN_PDC:
+ case ROLE_DOMAIN_BDC:
+ value_ascii = REG_PT_LANMANNT;
+ break;
+ case ROLE_STANDALONE:
+ value_ascii = REG_PT_SERVERNT;
+ break;
+ case ROLE_DOMAIN_MEMBER:
+ value_ascii = REG_PT_WINNT;
+ break;
+ }
+ value_length = push_ucs2(value, value, value_ascii,
+ sizeof(value),
+ STR_TERMINATE|STR_NOALIGN);
+ regval_ctr_addvalue(&regvals, REGSTR_PRODUCTTYPE, REG_SZ,
+ value, value_length);
+
+ val = dup_registry_value( regval_ctr_specific_value( &regvals, 0 ) );
+
+ status = NT_STATUS_OK;
+
+ goto out;
+ }
+
+ /* else fall back to actually looking up the value */
+
+ for ( i=0; fetch_reg_values_specific(regkey, &val, i); i++ )
+ {
+ DEBUG(10,("_reg_info: Testing value [%s]\n", val->valuename));
+ if ( StrCaseCmp( val->valuename, name ) == 0 ) {
+ DEBUG(10,("_reg_info: Found match for value [%s]\n", name));
+ status = NT_STATUS_OK;
+ break;
+ }
+
+ free_registry_value( val );
+ }
+
+
+out:
+ new_init_reg_r_info(q_u->ptr_buf, r_u, val, status);
+
+ regval_ctr_destroy( &regvals );
+ free_registry_value( val );
+
+ DEBUG(5,("_reg_info: Exit\n"));
+
+ return status;
+}
+
+
+/*****************************************************************************
+ Implementation of REG_QUERY_KEY
+ ****************************************************************************/
+
+NTSTATUS _reg_query_key(pipes_struct *p, REG_Q_QUERY_KEY *q_u, REG_R_QUERY_KEY *r_u)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ REGISTRY_KEY *regkey = find_regkey_index_by_hnd( p, &q_u->pol );
+
+ DEBUG(5,("_reg_query_key: Enter\n"));
+
+ if ( !regkey )
+ return NT_STATUS_INVALID_HANDLE;
+
+ if ( !get_subkey_information( regkey, &r_u->num_subkeys, &r_u->max_subkeylen ) )
+ return NT_STATUS_ACCESS_DENIED;
+
+ if ( !get_value_information( regkey, &r_u->num_values, &r_u->max_valnamelen, &r_u->max_valbufsize ) )
+ return NT_STATUS_ACCESS_DENIED;
+
+
+ r_u->sec_desc = 0x00000078; /* size for key's sec_desc */
+
+ /* Win9x set this to 0x0 since it does not keep timestamps.
+ Doing the same here for simplicity --jerry */
+
+ ZERO_STRUCT(r_u->mod_time);
+
+ DEBUG(5,("_reg_query_key: Exit\n"));
+
+ return status;
+}
+
+
+/*****************************************************************************
+ Implementation of REG_UNKNOWN_1A
+ ****************************************************************************/
+
+NTSTATUS _reg_unknown_1a(pipes_struct *p, REG_Q_UNKNOWN_1A *q_u, REG_R_UNKNOWN_1A *r_u)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ REGISTRY_KEY *regkey = find_regkey_index_by_hnd( p, &q_u->pol );
+
+ DEBUG(5,("_reg_unknown_1a: Enter\n"));
+
+ if ( !regkey )
+ return NT_STATUS_INVALID_HANDLE;
+
+ r_u->unknown = 0x00000005; /* seems to be consistent...no idea what it means */
+
+ DEBUG(5,("_reg_unknown_1a: Exit\n"));
+
+ return status;
+}
+
+
+/*****************************************************************************
+ Implementation of REG_ENUM_KEY
+ ****************************************************************************/
+
+NTSTATUS _reg_enum_key(pipes_struct *p, REG_Q_ENUM_KEY *q_u, REG_R_ENUM_KEY *r_u)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ REGISTRY_KEY *regkey = find_regkey_index_by_hnd( p, &q_u->pol );
+ char *subkey = NULL;
+
+
+ DEBUG(5,("_reg_enum_key: Enter\n"));
+
+ if ( !regkey )
+ return NT_STATUS_INVALID_HANDLE;
+
+ DEBUG(8,("_reg_enum_key: enumerating key [%s]\n", regkey->name));
+
+ if ( !fetch_reg_keys_specific( regkey, &subkey, q_u->key_index ) )
+ {
+ status = NT_STATUS_NO_MORE_ENTRIES;
+ goto done;
+ }
+
+ DEBUG(10,("_reg_enum_key: retrieved subkey named [%s]\n", subkey));
+
+ /* subkey has the string name now */
+
+ init_reg_r_enum_key( r_u, subkey, q_u->unknown_1, q_u->unknown_2 );
+
+ DEBUG(5,("_reg_enum_key: Exit\n"));
+
+done:
+ SAFE_FREE( subkey );
+ return status;
+}
+
+/*****************************************************************************
+ Implementation of REG_ENUM_VALUE
+ ****************************************************************************/
+
+NTSTATUS _reg_enum_value(pipes_struct *p, REG_Q_ENUM_VALUE *q_u, REG_R_ENUM_VALUE *r_u)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ REGISTRY_KEY *regkey = find_regkey_index_by_hnd( p, &q_u->pol );
+ REGISTRY_VALUE *val;
+
+
+ DEBUG(5,("_reg_enum_value: Enter\n"));
+
+ if ( !regkey )
+ return NT_STATUS_INVALID_HANDLE;
+
+ DEBUG(8,("_reg_enum_key: enumerating values for key [%s]\n", regkey->name));
+
+ if ( !fetch_reg_values_specific( regkey, &val, q_u->val_index ) )
+ {
+ status = NT_STATUS_NO_MORE_ENTRIES;
+ goto done;
+ }
+
+ DEBUG(10,("_reg_enum_value: retrieved value named [%s]\n", val->valuename));
+
+ /* subkey has the string name now */
+
+ init_reg_r_enum_val( r_u, val );
+
+
+ DEBUG(5,("_reg_enum_value: Exit\n"));
+
+done:
+ free_registry_value( val );
+
+ return status;
+}
+
+
+/*******************************************************************
+ reg_shutdwon
+ ********************************************************************/
+
+#define SHUTDOWN_R_STRING "-r"
+#define SHUTDOWN_F_STRING "-f"
+
+
+NTSTATUS _reg_shutdown(pipes_struct *p, REG_Q_SHUTDOWN *q_u, REG_R_SHUTDOWN *r_u)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ pstring shutdown_script;
+ UNISTR2 unimsg = q_u->uni_msg;
+ pstring message;
+ pstring chkmsg;
+ fstring timeout;
+ fstring r;
+ fstring f;
+
+ /* message */
+ rpcstr_pull (message, unimsg.buffer, sizeof(message), unimsg.uni_str_len*2,0);
+ /* security check */
+ alpha_strcpy (chkmsg, message, NULL, sizeof(message));
+ /* timeout */
+ fstr_sprintf(timeout, "%d", q_u->timeout);
+ /* reboot */
+ fstr_sprintf(r, (q_u->reboot) ? SHUTDOWN_R_STRING : "");
+ /* force */
+ fstr_sprintf(f, (q_u->force) ? SHUTDOWN_F_STRING : "");
+
+ pstrcpy(shutdown_script, lp_shutdown_script());
+
+ if(*shutdown_script) {
+ int shutdown_ret;
+ all_string_sub(shutdown_script, "%m", chkmsg, sizeof(shutdown_script));
+ all_string_sub(shutdown_script, "%t", timeout, sizeof(shutdown_script));
+ all_string_sub(shutdown_script, "%r", r, sizeof(shutdown_script));
+ all_string_sub(shutdown_script, "%f", f, sizeof(shutdown_script));
+ shutdown_ret = smbrun(shutdown_script,NULL);
+ DEBUG(3,("_reg_shutdown: Running the command `%s' gave %d\n",shutdown_script,shutdown_ret));
+ }
+
+ return status;
+}
+
+/*******************************************************************
+ reg_abort_shutdwon
+ ********************************************************************/
+
+NTSTATUS _reg_abort_shutdown(pipes_struct *p, REG_Q_ABORT_SHUTDOWN *q_u, REG_R_ABORT_SHUTDOWN *r_u)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ pstring abort_shutdown_script;
+
+ pstrcpy(abort_shutdown_script, lp_abort_shutdown_script());
+
+ if(*abort_shutdown_script) {
+ int abort_shutdown_ret;
+ abort_shutdown_ret = smbrun(abort_shutdown_script,NULL);
+ DEBUG(3,("_reg_abort_shutdown: Running the command `%s' gave %d\n",abort_shutdown_script,abort_shutdown_ret));
+ }
+
+ return status;
+}
+
+/*******************************************************************
+ REG_SAVE_KEY (0x14)
+ ********************************************************************/
+
+NTSTATUS _reg_save_key(pipes_struct *p, REG_Q_SAVE_KEY *q_u, REG_R_SAVE_KEY *r_u)
+{
+ REGISTRY_KEY *regkey = find_regkey_index_by_hnd( p, &q_u->pol );
+
+ DEBUG(5,("_reg_save_key: Enter\n"));
+
+ /*
+ * basically this is a no op function which just gverifies
+ * that the client gave us a valid registry key handle
+ */
+
+ if ( !regkey )
+ return NT_STATUS_INVALID_HANDLE;
+
+ DEBUG(8,("_reg_save_key: berifying backup of key [%s]\n", regkey->name));
+
+
+ return NT_STATUS_OK;
+}
+
+
diff --git a/source/rpc_server/srv_samr.c b/source/rpc_server/srv_samr.c
new file mode 100644
index 00000000000..971f5ed40ce
--- /dev/null
+++ b/source/rpc_server/srv_samr.c
@@ -0,0 +1,1508 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997,
+ * Copyright (C) Marc Jacobsen 1999,
+ * Copyright (C) Jean François Micouleau 1998-2001,
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002-2003.
+ *
+ * Split into interface and implementation modules by,
+ *
+ * Copyright (C) Jeremy Allison 2001.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * This is the interface to the SAMR code.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*******************************************************************
+ api_samr_close_hnd
+ ********************************************************************/
+
+static BOOL api_samr_close_hnd(pipes_struct *p)
+{
+ SAMR_Q_CLOSE_HND q_u;
+ SAMR_R_CLOSE_HND r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!samr_io_q_close_hnd("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_close_hnd: unable to unmarshall SAMR_Q_CLOSE_HND.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_close_hnd(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_close_hnd("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_close_hnd: unable to marshall SAMR_R_CLOSE_HND.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_open_domain
+ ********************************************************************/
+
+static BOOL api_samr_open_domain(pipes_struct *p)
+{
+ SAMR_Q_OPEN_DOMAIN q_u;
+ SAMR_R_OPEN_DOMAIN r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!samr_io_q_open_domain("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_open_domain: unable to unmarshall SAMR_Q_OPEN_DOMAIN.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_open_domain(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_open_domain("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_open_domain: unable to marshall SAMR_R_OPEN_DOMAIN.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_get_usrdom_pwinfo
+ ********************************************************************/
+
+static BOOL api_samr_get_usrdom_pwinfo(pipes_struct *p)
+{
+ SAMR_Q_GET_USRDOM_PWINFO q_u;
+ SAMR_R_GET_USRDOM_PWINFO r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!samr_io_q_get_usrdom_pwinfo("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_get_usrdom_pwinfo: unable to unmarshall SAMR_Q_GET_USRDOM_PWINFO.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_get_usrdom_pwinfo(p, &q_u, &r_u);
+
+ if(!samr_io_r_get_usrdom_pwinfo("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_get_usrdom_pwinfo: unable to marshall SAMR_R_GET_USRDOM_PWINFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_set_sec_obj
+ ********************************************************************/
+
+static BOOL api_samr_set_sec_obj(pipes_struct *p)
+{
+ SAMR_Q_SET_SEC_OBJ q_u;
+ SAMR_R_SET_SEC_OBJ r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!samr_io_q_set_sec_obj("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_set_sec_obj: unable to unmarshall SAMR_Q_SET_SEC_OBJ.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_set_sec_obj(p, &q_u, &r_u);
+
+ if(!samr_io_r_set_sec_obj("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_set_sec_obj: unable to marshall SAMR_R_SET_SEC_OBJ.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_query_sec_obj
+ ********************************************************************/
+
+static BOOL api_samr_query_sec_obj(pipes_struct *p)
+{
+ SAMR_Q_QUERY_SEC_OBJ q_u;
+ SAMR_R_QUERY_SEC_OBJ r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!samr_io_q_query_sec_obj("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_query_sec_obj: unable to unmarshall SAMR_Q_QUERY_SEC_OBJ.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_query_sec_obj(p, &q_u, &r_u);
+
+ if(!samr_io_r_query_sec_obj("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_query_sec_obj: unable to marshall SAMR_R_QUERY_SEC_OBJ.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_enum_dom_users
+ ********************************************************************/
+
+static BOOL api_samr_enum_dom_users(pipes_struct *p)
+{
+ SAMR_Q_ENUM_DOM_USERS q_u;
+ SAMR_R_ENUM_DOM_USERS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the samr open */
+ if(!samr_io_q_enum_dom_users("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_enum_dom_users: unable to unmarshall SAMR_Q_ENUM_DOM_USERS.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_enum_dom_users(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_enum_dom_users("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_enum_dom_users: unable to marshall SAMR_R_ENUM_DOM_USERS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_enum_dom_groups
+ ********************************************************************/
+
+static BOOL api_samr_enum_dom_groups(pipes_struct *p)
+{
+ SAMR_Q_ENUM_DOM_GROUPS q_u;
+ SAMR_R_ENUM_DOM_GROUPS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the samr open */
+ if(!samr_io_q_enum_dom_groups("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_enum_dom_groups: unable to unmarshall SAMR_Q_ENUM_DOM_GROUPS.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_enum_dom_groups(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_enum_dom_groups("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_enum_dom_groups: unable to marshall SAMR_R_ENUM_DOM_GROUPS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_enum_dom_aliases
+ ********************************************************************/
+
+static BOOL api_samr_enum_dom_aliases(pipes_struct *p)
+{
+ SAMR_Q_ENUM_DOM_ALIASES q_u;
+ SAMR_R_ENUM_DOM_ALIASES r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the samr open */
+ if(!samr_io_q_enum_dom_aliases("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_enum_dom_aliases: unable to unmarshall SAMR_Q_ENUM_DOM_ALIASES.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_enum_dom_aliases(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_enum_dom_aliases("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_enum_dom_aliases: unable to marshall SAMR_R_ENUM_DOM_ALIASES.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_query_dispinfo
+ ********************************************************************/
+
+static BOOL api_samr_query_dispinfo(pipes_struct *p)
+{
+ SAMR_Q_QUERY_DISPINFO q_u;
+ SAMR_R_QUERY_DISPINFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!samr_io_q_query_dispinfo("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_query_dispinfo: unable to unmarshall SAMR_Q_QUERY_DISPINFO.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_query_dispinfo(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_query_dispinfo("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_query_dispinfo: unable to marshall SAMR_R_QUERY_DISPINFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_query_aliasinfo
+ ********************************************************************/
+
+static BOOL api_samr_query_aliasinfo(pipes_struct *p)
+{
+ SAMR_Q_QUERY_ALIASINFO q_u;
+ SAMR_R_QUERY_ALIASINFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the samr open */
+ if(!samr_io_q_query_aliasinfo("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_query_aliasinfo: unable to unmarshall SAMR_Q_QUERY_ALIASINFO.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_query_aliasinfo(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_query_aliasinfo("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_query_aliasinfo: unable to marshall SAMR_R_QUERY_ALIASINFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_lookup_names
+ ********************************************************************/
+
+static BOOL api_samr_lookup_names(pipes_struct *p)
+{
+ SAMR_Q_LOOKUP_NAMES q_u;
+ SAMR_R_LOOKUP_NAMES r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the samr lookup names */
+ if(!samr_io_q_lookup_names("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_lookup_names: unable to unmarshall SAMR_Q_LOOKUP_NAMES.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_lookup_names(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_lookup_names("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_lookup_names: unable to marshall SAMR_R_LOOKUP_NAMES.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_chgpasswd_user
+ ********************************************************************/
+
+static BOOL api_samr_chgpasswd_user(pipes_struct *p)
+{
+ SAMR_Q_CHGPASSWD_USER q_u;
+ SAMR_R_CHGPASSWD_USER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* change password request */
+ if (!samr_io_q_chgpasswd_user("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_chgpasswd_user: Failed to unmarshall SAMR_Q_CHGPASSWD_USER.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_chgpasswd_user(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_chgpasswd_user("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_chgpasswd_user: Failed to marshall SAMR_R_CHGPASSWD_USER.\n" ));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_lookup_rids
+ ********************************************************************/
+
+static BOOL api_samr_lookup_rids(pipes_struct *p)
+{
+ SAMR_Q_LOOKUP_RIDS q_u;
+ SAMR_R_LOOKUP_RIDS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the samr lookup names */
+ if(!samr_io_q_lookup_rids("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_lookup_rids: unable to unmarshall SAMR_Q_LOOKUP_RIDS.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_lookup_rids(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_lookup_rids("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_lookup_rids: unable to marshall SAMR_R_LOOKUP_RIDS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_open_user
+ ********************************************************************/
+
+static BOOL api_samr_open_user(pipes_struct *p)
+{
+ SAMR_Q_OPEN_USER q_u;
+ SAMR_R_OPEN_USER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!samr_io_q_open_user("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_open_user: unable to unmarshall SAMR_Q_OPEN_USER.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_open_user(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_open_user("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_open_user: unable to marshall SAMR_R_OPEN_USER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_query_userinfo
+ ********************************************************************/
+
+static BOOL api_samr_query_userinfo(pipes_struct *p)
+{
+ SAMR_Q_QUERY_USERINFO q_u;
+ SAMR_R_QUERY_USERINFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!samr_io_q_query_userinfo("", &q_u, data, 0)){
+ DEBUG(0,("api_samr_query_userinfo: unable to unmarshall SAMR_Q_QUERY_USERINFO.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_query_userinfo(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_query_userinfo("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_query_userinfo: unable to marshall SAMR_R_QUERY_USERINFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_query_usergroups
+ ********************************************************************/
+
+static BOOL api_samr_query_usergroups(pipes_struct *p)
+{
+ SAMR_Q_QUERY_USERGROUPS q_u;
+ SAMR_R_QUERY_USERGROUPS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!samr_io_q_query_usergroups("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_query_usergroups: unable to unmarshall SAMR_Q_QUERY_USERGROUPS.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_query_usergroups(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_query_usergroups("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_query_usergroups: unable to marshall SAMR_R_QUERY_USERGROUPS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_query_dom_info
+ ********************************************************************/
+
+static BOOL api_samr_query_dom_info(pipes_struct *p)
+{
+ SAMR_Q_QUERY_DOMAIN_INFO q_u;
+ SAMR_R_QUERY_DOMAIN_INFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!samr_io_q_query_dom_info("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_query_dom_info: unable to unmarshall SAMR_Q_QUERY_DOMAIN_INFO.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_query_dom_info(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_query_dom_info("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_query_dom_info: unable to marshall SAMR_R_QUERY_DOMAIN_INFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_create_user
+ ********************************************************************/
+
+static BOOL api_samr_create_user(pipes_struct *p)
+{
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ SAMR_Q_CREATE_USER q_u;
+ SAMR_R_CREATE_USER r_u;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the samr create user */
+ if (!samr_io_q_create_user("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_create_user: Unable to unmarshall SAMR_Q_CREATE_USER.\n"));
+ return False;
+ }
+
+ r_u.status=_samr_create_user(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_create_user("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_create_user: Unable to marshall SAMR_R_CREATE_USER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_connect_anon
+ ********************************************************************/
+
+static BOOL api_samr_connect_anon(pipes_struct *p)
+{
+ SAMR_Q_CONNECT_ANON q_u;
+ SAMR_R_CONNECT_ANON r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the samr open policy */
+ if(!samr_io_q_connect_anon("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_connect_anon: unable to unmarshall SAMR_Q_CONNECT_ANON.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_connect_anon(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_connect_anon("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_connect_anon: unable to marshall SAMR_R_CONNECT_ANON.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_connect
+ ********************************************************************/
+
+static BOOL api_samr_connect(pipes_struct *p)
+{
+ SAMR_Q_CONNECT q_u;
+ SAMR_R_CONNECT r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the samr open policy */
+ if(!samr_io_q_connect("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_connect: unable to unmarshall SAMR_Q_CONNECT.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_connect(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_connect("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_connect: unable to marshall SAMR_R_CONNECT.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_connect4
+ ********************************************************************/
+
+static BOOL api_samr_connect4(pipes_struct *p)
+{
+ SAMR_Q_CONNECT4 q_u;
+ SAMR_R_CONNECT4 r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the samr open policy */
+ if(!samr_io_q_connect4("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_connect4: unable to unmarshall SAMR_Q_CONNECT4.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_connect4(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_connect4("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_connect4: unable to marshall SAMR_R_CONNECT4.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/**********************************************************************
+ api_samr_lookup_domain
+ **********************************************************************/
+
+static BOOL api_samr_lookup_domain(pipes_struct *p)
+{
+ SAMR_Q_LOOKUP_DOMAIN q_u;
+ SAMR_R_LOOKUP_DOMAIN r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!samr_io_q_lookup_domain("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_lookup_domain: Unable to unmarshall SAMR_Q_LOOKUP_DOMAIN.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_lookup_domain(p, &q_u, &r_u);
+
+ if(!samr_io_r_lookup_domain("", &r_u, rdata, 0)){
+ DEBUG(0,("api_samr_lookup_domain: Unable to marshall SAMR_R_LOOKUP_DOMAIN.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/**********************************************************************
+ api_samr_enum_domains
+ **********************************************************************/
+
+static BOOL api_samr_enum_domains(pipes_struct *p)
+{
+ SAMR_Q_ENUM_DOMAINS q_u;
+ SAMR_R_ENUM_DOMAINS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!samr_io_q_enum_domains("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_enum_domains: Unable to unmarshall SAMR_Q_ENUM_DOMAINS.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_enum_domains(p, &q_u, &r_u);
+
+ if(!samr_io_r_enum_domains("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_enum_domains: Unable to marshall SAMR_R_ENUM_DOMAINS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_open_alias
+ ********************************************************************/
+
+static BOOL api_samr_open_alias(pipes_struct *p)
+{
+ SAMR_Q_OPEN_ALIAS q_u;
+ SAMR_R_OPEN_ALIAS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the samr open policy */
+ if(!samr_io_q_open_alias("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_open_alias: Unable to unmarshall SAMR_Q_OPEN_ALIAS.\n"));
+ return False;
+ }
+
+ r_u.status=_samr_open_alias(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_open_alias("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_open_alias: Unable to marshall SAMR_R_OPEN_ALIAS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_set_userinfo
+ ********************************************************************/
+
+static BOOL api_samr_set_userinfo(pipes_struct *p)
+{
+ SAMR_Q_SET_USERINFO q_u;
+ SAMR_R_SET_USERINFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_set_userinfo("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_set_userinfo: Unable to unmarshall SAMR_Q_SET_USERINFO.\n"));
+ /* Fix for W2K SP2 */
+ if (q_u.switch_value == 0x1a) {
+ setup_fault_pdu(p, NT_STATUS(0x1c000006));
+ return True;
+ }
+ return False;
+ }
+
+ r_u.status = _samr_set_userinfo(p, &q_u, &r_u);
+
+ if(!samr_io_r_set_userinfo("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_set_userinfo: Unable to marshall SAMR_R_SET_USERINFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_set_userinfo2
+ ********************************************************************/
+
+static BOOL api_samr_set_userinfo2(pipes_struct *p)
+{
+ SAMR_Q_SET_USERINFO2 q_u;
+ SAMR_R_SET_USERINFO2 r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_set_userinfo2("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_set_userinfo2: Unable to unmarshall SAMR_Q_SET_USERINFO2.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_set_userinfo2(p, &q_u, &r_u);
+
+ if(!samr_io_r_set_userinfo2("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_set_userinfo2: Unable to marshall SAMR_R_SET_USERINFO2.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_query_useraliases
+ ********************************************************************/
+
+static BOOL api_samr_query_useraliases(pipes_struct *p)
+{
+ SAMR_Q_QUERY_USERALIASES q_u;
+ SAMR_R_QUERY_USERALIASES r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_query_useraliases("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_query_useraliases: Unable to unmarshall SAMR_Q_QUERY_USERALIASES.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_query_useraliases(p, &q_u, &r_u);
+
+ if (! samr_io_r_query_useraliases("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_query_useraliases: Unable to nmarshall SAMR_R_QUERY_USERALIASES.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_query_aliasmem
+ ********************************************************************/
+
+static BOOL api_samr_query_aliasmem(pipes_struct *p)
+{
+ SAMR_Q_QUERY_ALIASMEM q_u;
+ SAMR_R_QUERY_ALIASMEM r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_query_aliasmem("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_query_aliasmem: unable to unmarshall SAMR_Q_QUERY_ALIASMEM.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_query_aliasmem(p, &q_u, &r_u);
+
+ if (!samr_io_r_query_aliasmem("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_query_aliasmem: unable to marshall SAMR_R_QUERY_ALIASMEM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_query_groupmem
+ ********************************************************************/
+
+static BOOL api_samr_query_groupmem(pipes_struct *p)
+{
+ SAMR_Q_QUERY_GROUPMEM q_u;
+ SAMR_R_QUERY_GROUPMEM r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_query_groupmem("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_query_groupmem: unable to unmarshall SAMR_Q_QUERY_GROUPMEM.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_query_groupmem(p, &q_u, &r_u);
+
+ if (!samr_io_r_query_groupmem("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_query_groupmem: unable to marshall SAMR_R_QUERY_GROUPMEM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_add_aliasmem
+ ********************************************************************/
+
+static BOOL api_samr_add_aliasmem(pipes_struct *p)
+{
+ SAMR_Q_ADD_ALIASMEM q_u;
+ SAMR_R_ADD_ALIASMEM r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_add_aliasmem("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_add_aliasmem: unable to unmarshall SAMR_Q_ADD_ALIASMEM.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_add_aliasmem(p, &q_u, &r_u);
+
+ if (!samr_io_r_add_aliasmem("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_add_aliasmem: unable to marshall SAMR_R_ADD_ALIASMEM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_del_aliasmem
+ ********************************************************************/
+
+static BOOL api_samr_del_aliasmem(pipes_struct *p)
+{
+ SAMR_Q_DEL_ALIASMEM q_u;
+ SAMR_R_DEL_ALIASMEM r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_del_aliasmem("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_del_aliasmem: unable to unmarshall SAMR_Q_DEL_ALIASMEM.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_del_aliasmem(p, &q_u, &r_u);
+
+ if (!samr_io_r_del_aliasmem("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_del_aliasmem: unable to marshall SAMR_R_DEL_ALIASMEM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_add_groupmem
+ ********************************************************************/
+
+static BOOL api_samr_add_groupmem(pipes_struct *p)
+{
+ SAMR_Q_ADD_GROUPMEM q_u;
+ SAMR_R_ADD_GROUPMEM r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_add_groupmem("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_add_groupmem: unable to unmarshall SAMR_Q_ADD_GROUPMEM.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_add_groupmem(p, &q_u, &r_u);
+
+ if (!samr_io_r_add_groupmem("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_add_groupmem: unable to marshall SAMR_R_ADD_GROUPMEM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_del_groupmem
+ ********************************************************************/
+
+static BOOL api_samr_del_groupmem(pipes_struct *p)
+{
+ SAMR_Q_DEL_GROUPMEM q_u;
+ SAMR_R_DEL_GROUPMEM r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_del_groupmem("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_del_groupmem: unable to unmarshall SAMR_Q_DEL_GROUPMEM.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_del_groupmem(p, &q_u, &r_u);
+
+ if (!samr_io_r_del_groupmem("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_del_groupmem: unable to marshall SAMR_R_DEL_GROUPMEM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_delete_dom_user
+ ********************************************************************/
+
+static BOOL api_samr_delete_dom_user(pipes_struct *p)
+{
+ SAMR_Q_DELETE_DOM_USER q_u;
+ SAMR_R_DELETE_DOM_USER r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_delete_dom_user("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_delete_dom_user: unable to unmarshall SAMR_Q_DELETE_DOM_USER.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_delete_dom_user(p, &q_u, &r_u);
+
+ if (!samr_io_r_delete_dom_user("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_delete_dom_user: unable to marshall SAMR_R_DELETE_DOM_USER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_delete_dom_group
+ ********************************************************************/
+
+static BOOL api_samr_delete_dom_group(pipes_struct *p)
+{
+ SAMR_Q_DELETE_DOM_GROUP q_u;
+ SAMR_R_DELETE_DOM_GROUP r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_delete_dom_group("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_delete_dom_group: unable to unmarshall SAMR_Q_DELETE_DOM_GROUP.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_delete_dom_group(p, &q_u, &r_u);
+
+ if (!samr_io_r_delete_dom_group("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_delete_dom_group: unable to marshall SAMR_R_DELETE_DOM_GROUP.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_delete_dom_alias
+ ********************************************************************/
+
+static BOOL api_samr_delete_dom_alias(pipes_struct *p)
+{
+ SAMR_Q_DELETE_DOM_ALIAS q_u;
+ SAMR_R_DELETE_DOM_ALIAS r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_delete_dom_alias("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_delete_dom_alias: unable to unmarshall SAMR_Q_DELETE_DOM_ALIAS.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_delete_dom_alias(p, &q_u, &r_u);
+
+ if (!samr_io_r_delete_dom_alias("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_delete_dom_alias: unable to marshall SAMR_R_DELETE_DOM_ALIAS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_create_dom_group
+ ********************************************************************/
+
+static BOOL api_samr_create_dom_group(pipes_struct *p)
+{
+ SAMR_Q_CREATE_DOM_GROUP q_u;
+ SAMR_R_CREATE_DOM_GROUP r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_create_dom_group("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_create_dom_group: unable to unmarshall SAMR_Q_CREATE_DOM_GROUP.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_create_dom_group(p, &q_u, &r_u);
+
+ if (!samr_io_r_create_dom_group("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_create_dom_group: unable to marshall SAMR_R_CREATE_DOM_GROUP.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_create_dom_alias
+ ********************************************************************/
+
+static BOOL api_samr_create_dom_alias(pipes_struct *p)
+{
+ SAMR_Q_CREATE_DOM_ALIAS q_u;
+ SAMR_R_CREATE_DOM_ALIAS r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_create_dom_alias("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_create_dom_alias: unable to unmarshall SAMR_Q_CREATE_DOM_ALIAS.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_create_dom_alias(p, &q_u, &r_u);
+
+ if (!samr_io_r_create_dom_alias("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_create_dom_alias: unable to marshall SAMR_R_CREATE_DOM_ALIAS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_query_groupinfo
+ ********************************************************************/
+
+static BOOL api_samr_query_groupinfo(pipes_struct *p)
+{
+ SAMR_Q_QUERY_GROUPINFO q_u;
+ SAMR_R_QUERY_GROUPINFO r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_query_groupinfo("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_query_groupinfo: unable to unmarshall SAMR_Q_QUERY_GROUPINFO.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_query_groupinfo(p, &q_u, &r_u);
+
+ if (!samr_io_r_query_groupinfo("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_query_groupinfo: unable to marshall SAMR_R_QUERY_GROUPINFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_set_groupinfo
+ ********************************************************************/
+
+static BOOL api_samr_set_groupinfo(pipes_struct *p)
+{
+ SAMR_Q_SET_GROUPINFO q_u;
+ SAMR_R_SET_GROUPINFO r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_set_groupinfo("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_set_groupinfo: unable to unmarshall SAMR_Q_SET_GROUPINFO.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_set_groupinfo(p, &q_u, &r_u);
+
+ if (!samr_io_r_set_groupinfo("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_set_groupinfo: unable to marshall SAMR_R_SET_GROUPINFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_set_aliasinfo
+ ********************************************************************/
+
+static BOOL api_samr_set_aliasinfo(pipes_struct *p)
+{
+ SAMR_Q_SET_ALIASINFO q_u;
+ SAMR_R_SET_ALIASINFO r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_set_aliasinfo("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_set_aliasinfo: unable to unmarshall SAMR_Q_SET_ALIASINFO.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_set_aliasinfo(p, &q_u, &r_u);
+
+ if (!samr_io_r_set_aliasinfo("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_set_aliasinfo: unable to marshall SAMR_R_SET_ALIASINFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_get_dom_pwinfo
+ ********************************************************************/
+
+static BOOL api_samr_get_dom_pwinfo(pipes_struct *p)
+{
+ SAMR_Q_GET_DOM_PWINFO q_u;
+ SAMR_R_GET_DOM_PWINFO r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_get_dom_pwinfo("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_get_dom_pwinfo: unable to unmarshall SAMR_Q_GET_DOM_PWINFO.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_get_dom_pwinfo(p, &q_u, &r_u);
+
+ if (!samr_io_r_get_dom_pwinfo("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_get_dom_pwinfo: unable to marshall SAMR_R_GET_DOM_PWINFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_open_group
+ ********************************************************************/
+
+static BOOL api_samr_open_group(pipes_struct *p)
+{
+ SAMR_Q_OPEN_GROUP q_u;
+ SAMR_R_OPEN_GROUP r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_open_group("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_open_group: unable to unmarshall SAMR_Q_OPEN_GROUP.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_open_group(p, &q_u, &r_u);
+
+ if (!samr_io_r_open_group("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_open_group: unable to marshall SAMR_R_OPEN_GROUP.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_remove_sid_foreign_domain
+ ********************************************************************/
+
+static BOOL api_samr_remove_sid_foreign_domain(pipes_struct *p)
+{
+ SAMR_Q_REMOVE_SID_FOREIGN_DOMAIN q_u;
+ SAMR_R_REMOVE_SID_FOREIGN_DOMAIN r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_remove_sid_foreign_domain("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_remove_sid_foreign_domain: unable to unmarshall SAMR_Q_REMOVE_SID_FOREIGN_DOMAIN.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_remove_sid_foreign_domain(p, &q_u, &r_u);
+
+ if (!samr_io_r_remove_sid_foreign_domain("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_remove_sid_foreign_domain: unable to marshall SAMR_R_REMOVE_SID_FOREIGN_DOMAIN.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_query_dom_info
+ ********************************************************************/
+
+static BOOL api_samr_unknown_2e(pipes_struct *p)
+{
+ SAMR_Q_UNKNOWN_2E q_u;
+ SAMR_R_UNKNOWN_2E r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the samr unknown 8 command */
+ if(!samr_io_q_unknown_2e("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_unknown_2e: unable to unmarshall SAMR_Q_UNKNOWN_2E.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_unknown_2e(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_samr_unknown_2e("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_unknown_2e: unable to marshall SAMR_R_UNKNOWN_2E.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_set_dom_info
+ ********************************************************************/
+
+static BOOL api_samr_set_dom_info(pipes_struct *p)
+{
+ SAMR_Q_SET_DOMAIN_INFO q_u;
+ SAMR_R_SET_DOMAIN_INFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!samr_io_q_set_domain_info("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_set_dom_info: unable to unmarshall SAMR_Q_SET_DOMAIN_INFO.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_set_dom_info(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_set_domain_info("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_set_dom_info: unable to marshall SAMR_R_SET_DOMAIN_INFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ array of \PIPE\samr operations
+ ********************************************************************/
+
+static struct api_struct api_samr_cmds [] =
+{
+ {"SAMR_CLOSE_HND" , SAMR_CLOSE_HND , api_samr_close_hnd },
+ {"SAMR_CONNECT" , SAMR_CONNECT , api_samr_connect },
+ {"SAMR_CONNECT_ANON" , SAMR_CONNECT_ANON , api_samr_connect_anon },
+ {"SAMR_ENUM_DOMAINS" , SAMR_ENUM_DOMAINS , api_samr_enum_domains },
+ {"SAMR_ENUM_DOM_USERS" , SAMR_ENUM_DOM_USERS , api_samr_enum_dom_users },
+
+ {"SAMR_ENUM_DOM_GROUPS" , SAMR_ENUM_DOM_GROUPS , api_samr_enum_dom_groups },
+ {"SAMR_ENUM_DOM_ALIASES" , SAMR_ENUM_DOM_ALIASES , api_samr_enum_dom_aliases },
+ {"SAMR_QUERY_USERALIASES" , SAMR_QUERY_USERALIASES, api_samr_query_useraliases},
+ {"SAMR_QUERY_ALIASMEM" , SAMR_QUERY_ALIASMEM , api_samr_query_aliasmem },
+ {"SAMR_QUERY_GROUPMEM" , SAMR_QUERY_GROUPMEM , api_samr_query_groupmem },
+ {"SAMR_ADD_ALIASMEM" , SAMR_ADD_ALIASMEM , api_samr_add_aliasmem },
+ {"SAMR_DEL_ALIASMEM" , SAMR_DEL_ALIASMEM , api_samr_del_aliasmem },
+ {"SAMR_ADD_GROUPMEM" , SAMR_ADD_GROUPMEM , api_samr_add_groupmem },
+ {"SAMR_DEL_GROUPMEM" , SAMR_DEL_GROUPMEM , api_samr_del_groupmem },
+
+ {"SAMR_DELETE_DOM_USER" , SAMR_DELETE_DOM_USER , api_samr_delete_dom_user },
+ {"SAMR_DELETE_DOM_GROUP" , SAMR_DELETE_DOM_GROUP , api_samr_delete_dom_group },
+ {"SAMR_DELETE_DOM_ALIAS" , SAMR_DELETE_DOM_ALIAS , api_samr_delete_dom_alias },
+ {"SAMR_CREATE_DOM_GROUP" , SAMR_CREATE_DOM_GROUP , api_samr_create_dom_group },
+ {"SAMR_CREATE_DOM_ALIAS" , SAMR_CREATE_DOM_ALIAS , api_samr_create_dom_alias },
+ {"SAMR_LOOKUP_NAMES" , SAMR_LOOKUP_NAMES , api_samr_lookup_names },
+ {"SAMR_OPEN_USER" , SAMR_OPEN_USER , api_samr_open_user },
+ {"SAMR_QUERY_USERINFO" , SAMR_QUERY_USERINFO , api_samr_query_userinfo },
+ {"SAMR_SET_USERINFO" , SAMR_SET_USERINFO , api_samr_set_userinfo },
+ {"SAMR_SET_USERINFO2" , SAMR_SET_USERINFO2 , api_samr_set_userinfo2 },
+
+ {"SAMR_QUERY_DOMAIN_INFO" , SAMR_QUERY_DOMAIN_INFO, api_samr_query_dom_info },
+ {"SAMR_QUERY_USERGROUPS" , SAMR_QUERY_USERGROUPS , api_samr_query_usergroups },
+ {"SAMR_QUERY_DISPINFO" , SAMR_QUERY_DISPINFO , api_samr_query_dispinfo },
+ {"SAMR_QUERY_DISPINFO3" , SAMR_QUERY_DISPINFO3 , api_samr_query_dispinfo },
+ {"SAMR_QUERY_DISPINFO4" , SAMR_QUERY_DISPINFO4 , api_samr_query_dispinfo },
+
+ {"SAMR_QUERY_ALIASINFO" , SAMR_QUERY_ALIASINFO , api_samr_query_aliasinfo },
+ {"SAMR_QUERY_GROUPINFO" , SAMR_QUERY_GROUPINFO , api_samr_query_groupinfo },
+ {"SAMR_SET_GROUPINFO" , SAMR_SET_GROUPINFO , api_samr_set_groupinfo },
+ {"SAMR_SET_ALIASINFO" , SAMR_SET_ALIASINFO , api_samr_set_aliasinfo },
+ {"SAMR_CREATE_USER" , SAMR_CREATE_USER , api_samr_create_user },
+ {"SAMR_LOOKUP_RIDS" , SAMR_LOOKUP_RIDS , api_samr_lookup_rids },
+ {"SAMR_GET_DOM_PWINFO" , SAMR_GET_DOM_PWINFO , api_samr_get_dom_pwinfo },
+ {"SAMR_CHGPASSWD_USER" , SAMR_CHGPASSWD_USER , api_samr_chgpasswd_user },
+ {"SAMR_OPEN_ALIAS" , SAMR_OPEN_ALIAS , api_samr_open_alias },
+ {"SAMR_OPEN_GROUP" , SAMR_OPEN_GROUP , api_samr_open_group },
+ {"SAMR_OPEN_DOMAIN" , SAMR_OPEN_DOMAIN , api_samr_open_domain },
+ {"SAMR_REMOVE_SID_FOREIGN_DOMAIN" , SAMR_REMOVE_SID_FOREIGN_DOMAIN , api_samr_remove_sid_foreign_domain },
+ {"SAMR_LOOKUP_DOMAIN" , SAMR_LOOKUP_DOMAIN , api_samr_lookup_domain },
+
+ {"SAMR_QUERY_SEC_OBJECT" , SAMR_QUERY_SEC_OBJECT , api_samr_query_sec_obj },
+ {"SAMR_SET_SEC_OBJECT" , SAMR_SET_SEC_OBJECT , api_samr_set_sec_obj },
+ {"SAMR_GET_USRDOM_PWINFO" , SAMR_GET_USRDOM_PWINFO, api_samr_get_usrdom_pwinfo},
+ {"SAMR_UNKNOWN_2E" , SAMR_UNKNOWN_2E , api_samr_unknown_2e },
+ {"SAMR_SET_DOMAIN_INFO" , SAMR_SET_DOMAIN_INFO , api_samr_set_dom_info },
+ {"SAMR_CONNECT4" , SAMR_CONNECT4 , api_samr_connect4 }
+};
+
+void samr_get_pipe_fns( struct api_struct **fns, int *n_fns )
+{
+ *fns = api_samr_cmds;
+ *n_fns = sizeof(api_samr_cmds) / sizeof(struct api_struct);
+}
+
+
+NTSTATUS rpc_samr_init(void)
+{
+ return rpc_pipe_register_commands(SMB_RPC_INTERFACE_VERSION, "samr", "lsass", api_samr_cmds,
+ sizeof(api_samr_cmds) / sizeof(struct api_struct));
+}
diff --git a/source/rpc_server/srv_samr_nt.c b/source/rpc_server/srv_samr_nt.c
new file mode 100644
index 00000000000..642c10e26fe
--- /dev/null
+++ b/source/rpc_server/srv_samr_nt.c
@@ -0,0 +1,4476 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997,
+ * Copyright (C) Marc Jacobsen 1999,
+ * Copyright (C) Jeremy Allison 2001-2002,
+ * Copyright (C) Jean François Micouleau 1998-2001,
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002.
+ * Copyright (C) Gerald (Jerry) Carter 2003.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * This is the implementation of the SAMR code.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+extern DOM_SID global_sid_Builtin;
+
+extern rid_name domain_group_rids[];
+extern rid_name domain_alias_rids[];
+extern rid_name builtin_alias_rids[];
+
+extern PRIVS privs[];
+
+typedef struct _disp_info {
+ BOOL user_dbloaded;
+ uint32 num_user_account;
+ SAM_ACCOUNT *disp_user_info;
+ BOOL group_dbloaded;
+ uint32 num_group_account;
+ DOMAIN_GRP *disp_group_info;
+} DISP_INFO;
+
+struct samr_info {
+ /* for use by the \PIPE\samr policy */
+ DOM_SID sid;
+ uint32 status; /* some sort of flag. best to record it. comes from opnum 0x39 */
+ uint32 acc_granted;
+ uint16 acb_mask;
+ BOOL all_machines;
+ DISP_INFO disp_info;
+
+ TALLOC_CTX *mem_ctx;
+};
+
+struct generic_mapping sam_generic_mapping = {GENERIC_RIGHTS_SAM_READ, GENERIC_RIGHTS_SAM_WRITE, GENERIC_RIGHTS_SAM_EXECUTE, GENERIC_RIGHTS_SAM_ALL_ACCESS};
+struct generic_mapping dom_generic_mapping = {GENERIC_RIGHTS_DOMAIN_READ, GENERIC_RIGHTS_DOMAIN_WRITE, GENERIC_RIGHTS_DOMAIN_EXECUTE, GENERIC_RIGHTS_DOMAIN_ALL_ACCESS};
+struct generic_mapping usr_generic_mapping = {GENERIC_RIGHTS_USER_READ, GENERIC_RIGHTS_USER_WRITE, GENERIC_RIGHTS_USER_EXECUTE, GENERIC_RIGHTS_USER_ALL_ACCESS};
+struct generic_mapping grp_generic_mapping = {GENERIC_RIGHTS_GROUP_READ, GENERIC_RIGHTS_GROUP_WRITE, GENERIC_RIGHTS_GROUP_EXECUTE, GENERIC_RIGHTS_GROUP_ALL_ACCESS};
+struct generic_mapping ali_generic_mapping = {GENERIC_RIGHTS_ALIAS_READ, GENERIC_RIGHTS_ALIAS_WRITE, GENERIC_RIGHTS_ALIAS_EXECUTE, GENERIC_RIGHTS_ALIAS_ALL_ACCESS};
+
+static NTSTATUS samr_make_dom_obj_sd(TALLOC_CTX *ctx, SEC_DESC **psd, size_t *sd_size);
+
+/*******************************************************************
+ Checks if access to an object should be granted, and returns that
+ level of access for further checks.
+********************************************************************/
+
+NTSTATUS access_check_samr_object(SEC_DESC *psd, pipes_struct *p, uint32 des_access,
+ uint32 *acc_granted, uint32 *priv_list, const char *debug)
+{
+ NTSTATUS status = NT_STATUS_ACCESS_DENIED;
+ NT_USER_TOKEN *nt_user_token = p->pipe_user.nt_user_token;
+ int i;
+
+ if (!se_access_check(psd, nt_user_token, des_access, acc_granted, &status)) {
+ *acc_granted = des_access;
+ if (geteuid() == sec_initial_uid()) {
+ DEBUG(4,("%s: ACCESS should be DENIED (requested: %#010x)\n",
+ debug, des_access));
+ DEBUGADD(4,("but overritten by euid == sec_initial_uid()\n"));
+ return NT_STATUS_OK;
+ }
+ if (priv_list != NULL) {
+ for (i = 0; priv_list[i] != SE_NONE; i++) {
+ if (NT_STATUS_IS_OK(user_has_privilege(&(p->pipe_user), priv_list[i]))) {
+ DEBUG(3, ("%s: User should be denied access but was overridden by %s\n", debug, privs[priv_list[i]].priv));
+ return NT_STATUS_OK;
+ }
+ }
+ }
+
+ DEBUG(2,("%s: ACCESS DENIED (requested: %#010x)\n", debug, des_access));
+ }
+ return status;
+}
+
+/*******************************************************************
+ Checks if access to a function can be granted
+********************************************************************/
+
+NTSTATUS access_check_samr_function(pipes_struct *p, uint32 acc_granted, uint32 acc_required, uint32 *priv_list, const char *debug)
+{
+ int i;
+
+ DEBUG(5,("%s: access check ((granted: %#010x; required: %#010x)\n",
+ debug, acc_granted, acc_required));
+ if ((acc_granted & acc_required) != acc_required) {
+ if (geteuid() == sec_initial_uid()) {
+ DEBUG(4,("%s: ACCESS should be DENIED (granted: %#010x; required: %#010x)\n",
+ debug, acc_granted, acc_required));
+ DEBUGADD(4,("but overwritten by euid == 0\n"));
+ return NT_STATUS_OK;
+ }
+ if (priv_list != NULL) {
+ for (i = 0; priv_list[i] != SE_NONE; i++) {
+ if (NT_STATUS_IS_OK(user_has_privilege(&(p->pipe_user), priv_list[i]))) {
+ DEBUG(3, ("%s: User should be denied access but was overridden by %s\n", debug, privs[priv_list[i]].priv));
+ return NT_STATUS_OK;
+ }
+ }
+ }
+
+ DEBUG(2,("%s: ACCESS DENIED (granted: %#010x; required: %#010x)\n",
+ debug, acc_granted, acc_required));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ return NT_STATUS_OK;
+}
+
+
+/*******************************************************************
+ Create a samr_info struct.
+********************************************************************/
+
+static struct samr_info *get_samr_info_by_sid(DOM_SID *psid)
+{
+ struct samr_info *info;
+ fstring sid_str;
+ TALLOC_CTX *mem_ctx;
+
+ if (psid) {
+ sid_to_string(sid_str, psid);
+ } else {
+ fstrcpy(sid_str,"(NULL)");
+ }
+
+ mem_ctx = talloc_init("samr_info for domain sid %s", sid_str);
+
+ if ((info = (struct samr_info *)talloc(mem_ctx, sizeof(struct samr_info))) == NULL)
+ return NULL;
+
+ ZERO_STRUCTP(info);
+ DEBUG(10,("get_samr_info_by_sid: created new info for sid %s\n", sid_str));
+ if (psid) {
+ sid_copy( &info->sid, psid);
+ } else {
+ DEBUG(10,("get_samr_info_by_sid: created new info for NULL sid.\n"));
+ }
+ info->mem_ctx = mem_ctx;
+ return info;
+}
+
+/*******************************************************************
+ Function to free the per handle data.
+ ********************************************************************/
+
+static void free_samr_users(struct samr_info *info)
+{
+ int i;
+
+ if (info->disp_info.user_dbloaded){
+ for (i=0; i<info->disp_info.num_user_account; i++) {
+ SAM_ACCOUNT *sam = &info->disp_info.disp_user_info[i];
+ /* Not really a free, actually a 'clear' */
+ pdb_free_sam(&sam);
+ }
+ }
+ info->disp_info.user_dbloaded=False;
+ info->disp_info.num_user_account=0;
+}
+
+/*******************************************************************
+ Function to free the per handle data.
+ ********************************************************************/
+
+static void free_samr_db(struct samr_info *info)
+{
+ /* Groups are talloced */
+
+ free_samr_users(info);
+
+ info->disp_info.group_dbloaded=False;
+ info->disp_info.num_group_account=0;
+}
+
+static void free_samr_info(void *ptr)
+{
+ struct samr_info *info=(struct samr_info *) ptr;
+
+ free_samr_db(info);
+ talloc_destroy(info->mem_ctx);
+}
+
+/*******************************************************************
+ Ensure password info is never given out. Paranioa... JRA.
+ ********************************************************************/
+
+static void samr_clear_sam_passwd(SAM_ACCOUNT *sam_pass)
+{
+
+ if (!sam_pass)
+ return;
+
+ /* These now zero out the old password */
+
+ pdb_set_lanman_passwd(sam_pass, NULL, PDB_DEFAULT);
+ pdb_set_nt_passwd(sam_pass, NULL, PDB_DEFAULT);
+}
+
+
+static NTSTATUS load_sampwd_entries(struct samr_info *info, uint16 acb_mask, BOOL all_machines)
+{
+ SAM_ACCOUNT *pwd = NULL;
+ SAM_ACCOUNT *pwd_array = NULL;
+ NTSTATUS nt_status = NT_STATUS_OK;
+ TALLOC_CTX *mem_ctx = info->mem_ctx;
+
+ DEBUG(10,("load_sampwd_entries\n"));
+
+ /* if the snapshoot is already loaded, return */
+ if ((info->disp_info.user_dbloaded==True)
+ && (info->acb_mask == acb_mask)
+ && (info->all_machines == all_machines)) {
+ DEBUG(10,("load_sampwd_entries: already in memory\n"));
+ return NT_STATUS_OK;
+ }
+
+ free_samr_users(info);
+
+ if (!pdb_setsampwent(False)) {
+ DEBUG(0, ("load_sampwd_entries: Unable to open passdb.\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ for (; (NT_STATUS_IS_OK(nt_status = pdb_init_sam_talloc(mem_ctx, &pwd)))
+ && pdb_getsampwent(pwd) == True; pwd=NULL) {
+
+ if (all_machines) {
+ if (!((pdb_get_acct_ctrl(pwd) & ACB_WSTRUST)
+ || (pdb_get_acct_ctrl(pwd) & ACB_SVRTRUST))) {
+ DEBUG(5,("load_sampwd_entries: '%s' is not a machine account - ACB: %x - skipping\n", pdb_get_username(pwd), acb_mask));
+ pdb_free_sam(&pwd);
+ continue;
+ }
+ } else {
+ if (acb_mask != 0 && !(pdb_get_acct_ctrl(pwd) & acb_mask)) {
+ pdb_free_sam(&pwd);
+ DEBUG(5,(" acb_mask %x reject\n", acb_mask));
+ continue;
+ }
+ }
+
+ /* Realloc some memory for the array of ptr to the SAM_ACCOUNT structs */
+ if (info->disp_info.num_user_account % MAX_SAM_ENTRIES == 0) {
+
+ DEBUG(10,("load_sampwd_entries: allocating more memory\n"));
+ pwd_array=(SAM_ACCOUNT *)talloc_realloc(mem_ctx, info->disp_info.disp_user_info,
+ (info->disp_info.num_user_account+MAX_SAM_ENTRIES)*sizeof(SAM_ACCOUNT));
+
+ if (pwd_array==NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ info->disp_info.disp_user_info=pwd_array;
+ }
+
+ /* Copy the SAM_ACCOUNT into the array */
+ info->disp_info.disp_user_info[info->disp_info.num_user_account]=*pwd;
+
+ DEBUG(10,("load_sampwd_entries: entry: %d\n", info->disp_info.num_user_account));
+
+ info->disp_info.num_user_account++;
+ }
+
+ pdb_endsampwent();
+
+ /* the snapshoot is in memory, we're ready to enumerate fast */
+
+ info->acb_mask = acb_mask;
+ info->all_machines = all_machines;
+ info->disp_info.user_dbloaded=True;
+
+ DEBUG(10,("load_sampwd_entries: done\n"));
+
+ return nt_status;
+}
+
+static NTSTATUS load_group_domain_entries(struct samr_info *info, DOM_SID *sid)
+{
+ GROUP_MAP *map=NULL;
+ DOMAIN_GRP *grp_array = NULL;
+ uint32 group_entries = 0;
+ uint32 i;
+ TALLOC_CTX *mem_ctx = info->mem_ctx;
+ BOOL ret;
+
+ DEBUG(10,("load_group_domain_entries\n"));
+
+ /* if the snapshoot is already loaded, return */
+ if (info->disp_info.group_dbloaded==True) {
+ DEBUG(10,("load_group_domain_entries: already in memory\n"));
+ return NT_STATUS_OK;
+ }
+
+
+ become_root();
+ ret = pdb_enum_group_mapping(SID_NAME_DOM_GRP, &map, (int *)&group_entries, ENUM_ONLY_MAPPED);
+ unbecome_root();
+
+ if ( !ret ) {
+ DEBUG(1, ("load_group_domain_entries: pdb_enum_group_mapping() failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+
+ info->disp_info.num_group_account=group_entries;
+
+ grp_array=(DOMAIN_GRP *)talloc(mem_ctx, info->disp_info.num_group_account*sizeof(DOMAIN_GRP));
+ if (group_entries!=0 && grp_array==NULL) {
+ DEBUG(1, ("load_group_domain_entries: talloc() failed for grp_array!\n"));
+ SAFE_FREE(map);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ info->disp_info.disp_group_info=grp_array;
+
+ for (i=0; i<group_entries; i++) {
+ fstrcpy(grp_array[i].name, map[i].nt_name);
+ fstrcpy(grp_array[i].comment, map[i].comment);
+ sid_split_rid(&map[i].sid, &grp_array[i].rid);
+ grp_array[i].attr=SID_NAME_DOM_GRP;
+ }
+
+ SAFE_FREE(map);
+
+ /* the snapshoot is in memory, we're ready to enumerate fast */
+
+ info->disp_info.group_dbloaded=True;
+
+ DEBUG(10,("load_group_domain_entries: done\n"));
+
+ return NT_STATUS_OK;
+}
+
+
+/*******************************************************************
+ _samr_close_hnd
+ ********************************************************************/
+
+NTSTATUS _samr_close_hnd(pipes_struct *p, SAMR_Q_CLOSE_HND *q_u, SAMR_R_CLOSE_HND *r_u)
+{
+ r_u->status = NT_STATUS_OK;
+
+ /* close the policy handle */
+ if (!close_policy_hnd(p, &q_u->pol))
+ return NT_STATUS_OBJECT_NAME_INVALID;
+
+ DEBUG(5,("samr_reply_close_hnd: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ samr_reply_open_domain
+ ********************************************************************/
+
+NTSTATUS _samr_open_domain(pipes_struct *p, SAMR_Q_OPEN_DOMAIN *q_u, SAMR_R_OPEN_DOMAIN *r_u)
+{
+ struct samr_info *info;
+ SEC_DESC *psd = NULL;
+ uint32 acc_granted;
+ uint32 des_access = q_u->flags;
+ size_t sd_size;
+ NTSTATUS status;
+ uint32 priv_list[3] = {SE_MACHINE_ACCOUNT, SE_NONE};
+
+ r_u->status = NT_STATUS_OK;
+
+ /* find the connection policy handle. */
+ if (!find_policy_by_hnd(p, &q_u->pol, (void**)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(status = access_check_samr_function(p, info->acc_granted, SA_RIGHT_SAM_OPEN_DOMAIN, priv_list, "_samr_open_domain"))) {
+ return status;
+ }
+
+ /*check if access can be granted as requested by client. */
+ samr_make_dom_obj_sd(p->mem_ctx, &psd, &sd_size);
+ se_map_generic(&des_access,&dom_generic_mapping);
+
+ if (!NT_STATUS_IS_OK(status =
+ access_check_samr_object(psd, p, des_access, &acc_granted,
+ priv_list, "_samr_open_domain"))) {
+ return status;
+ }
+
+ /* associate the domain SID with the (unique) handle. */
+ if ((info = get_samr_info_by_sid(&q_u->dom_sid.sid))==NULL)
+ return NT_STATUS_NO_MEMORY;
+ info->acc_granted = acc_granted;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, &r_u->domain_pol, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ DEBUG(5,("samr_open_domain: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ _samr_get_usrdom_pwinfo
+ ********************************************************************/
+
+NTSTATUS _samr_get_usrdom_pwinfo(pipes_struct *p, SAMR_Q_GET_USRDOM_PWINFO *q_u, SAMR_R_GET_USRDOM_PWINFO *r_u)
+{
+ struct samr_info *info = NULL;
+
+ r_u->status = NT_STATUS_OK;
+
+ /* find the policy handle. open a policy on it. */
+ if (!find_policy_by_hnd(p, &q_u->user_pol, (void **)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!sid_check_is_in_our_domain(&info->sid))
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ init_samr_r_get_usrdom_pwinfo(r_u, NT_STATUS_OK);
+
+ DEBUG(5,("_samr_get_usrdom_pwinfo: %d\n", __LINE__));
+
+ /*
+ * NT sometimes return NT_STATUS_ACCESS_DENIED
+ * I don't know yet why.
+ */
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ samr_make_dom_obj_sd
+ ********************************************************************/
+
+static NTSTATUS samr_make_dom_obj_sd(TALLOC_CTX *ctx, SEC_DESC **psd, size_t *sd_size)
+{
+ extern DOM_SID global_sid_World;
+ DOM_SID adm_sid;
+ DOM_SID act_sid;
+
+ SEC_ACE ace[3];
+ SEC_ACCESS mask;
+
+ SEC_ACL *psa = NULL;
+
+ sid_copy(&adm_sid, &global_sid_Builtin);
+ sid_append_rid(&adm_sid, BUILTIN_ALIAS_RID_ADMINS);
+
+ sid_copy(&act_sid, &global_sid_Builtin);
+ sid_append_rid(&act_sid, BUILTIN_ALIAS_RID_ACCOUNT_OPS);
+
+ /*basic access for every one*/
+ init_sec_access(&mask, GENERIC_RIGHTS_DOMAIN_EXECUTE | GENERIC_RIGHTS_DOMAIN_READ);
+ init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ /*full access for builtin aliases Administrators and Account Operators*/
+ init_sec_access(&mask, GENERIC_RIGHTS_DOMAIN_ALL_ACCESS);
+ init_sec_ace(&ace[1], &adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+ init_sec_ace(&ace[2], &act_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 3, ace)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if ((*psd = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL, psa, sd_size)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ samr_make_usr_obj_sd
+ ********************************************************************/
+
+static NTSTATUS samr_make_usr_obj_sd(TALLOC_CTX *ctx, SEC_DESC **psd, size_t *sd_size, DOM_SID *usr_sid)
+{
+ extern DOM_SID global_sid_World;
+ DOM_SID adm_sid;
+ DOM_SID act_sid;
+
+ SEC_ACE ace[4];
+ SEC_ACCESS mask;
+
+ SEC_ACL *psa = NULL;
+
+ sid_copy(&adm_sid, &global_sid_Builtin);
+ sid_append_rid(&adm_sid, BUILTIN_ALIAS_RID_ADMINS);
+
+ sid_copy(&act_sid, &global_sid_Builtin);
+ sid_append_rid(&act_sid, BUILTIN_ALIAS_RID_ACCOUNT_OPS);
+
+ /*basic access for every one*/
+ init_sec_access(&mask, GENERIC_RIGHTS_USER_EXECUTE | GENERIC_RIGHTS_USER_READ);
+ init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ /*full access for builtin aliases Administrators and Account Operators*/
+ init_sec_access(&mask, GENERIC_RIGHTS_USER_ALL_ACCESS);
+ init_sec_ace(&ace[1], &adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+ init_sec_ace(&ace[2], &act_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ /*extended access for the user*/
+ init_sec_access(&mask,READ_CONTROL_ACCESS | SA_RIGHT_USER_CHANGE_PASSWORD | SA_RIGHT_USER_SET_LOC_COM);
+ init_sec_ace(&ace[3], usr_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 4, ace)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if ((*psd = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL, psa, sd_size)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ samr_make_grp_obj_sd
+ ********************************************************************/
+
+static NTSTATUS samr_make_grp_obj_sd(TALLOC_CTX *ctx, SEC_DESC **psd, size_t *sd_size)
+{
+ extern DOM_SID global_sid_World;
+ DOM_SID adm_sid;
+ DOM_SID act_sid;
+
+ SEC_ACE ace[3];
+ SEC_ACCESS mask;
+
+ SEC_ACL *psa = NULL;
+
+ sid_copy(&adm_sid, &global_sid_Builtin);
+ sid_append_rid(&adm_sid, BUILTIN_ALIAS_RID_ADMINS);
+
+ sid_copy(&act_sid, &global_sid_Builtin);
+ sid_append_rid(&act_sid, BUILTIN_ALIAS_RID_ACCOUNT_OPS);
+
+ /*basic access for every one*/
+ init_sec_access(&mask, GENERIC_RIGHTS_GROUP_EXECUTE | GENERIC_RIGHTS_GROUP_READ);
+ init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ /*full access for builtin aliases Administrators and Account Operators*/
+ init_sec_access(&mask, GENERIC_RIGHTS_GROUP_ALL_ACCESS);
+ init_sec_ace(&ace[1], &adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+ init_sec_ace(&ace[2], &act_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 3, ace)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if ((*psd = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL, psa, sd_size)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ samr_make_ali_obj_sd
+ ********************************************************************/
+
+static NTSTATUS samr_make_ali_obj_sd(TALLOC_CTX *ctx, SEC_DESC **psd, size_t *sd_size)
+{
+ extern DOM_SID global_sid_World;
+ DOM_SID adm_sid;
+ DOM_SID act_sid;
+
+ SEC_ACE ace[3];
+ SEC_ACCESS mask;
+
+ SEC_ACL *psa = NULL;
+
+ sid_copy(&adm_sid, &global_sid_Builtin);
+ sid_append_rid(&adm_sid, BUILTIN_ALIAS_RID_ADMINS);
+
+ sid_copy(&act_sid, &global_sid_Builtin);
+ sid_append_rid(&act_sid, BUILTIN_ALIAS_RID_ACCOUNT_OPS);
+
+ /*basic access for every one*/
+ init_sec_access(&mask, GENERIC_RIGHTS_ALIAS_EXECUTE | GENERIC_RIGHTS_ALIAS_READ);
+ init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ /*full access for builtin aliases Administrators and Account Operators*/
+ init_sec_access(&mask, GENERIC_RIGHTS_ALIAS_ALL_ACCESS);
+ init_sec_ace(&ace[1], &adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+ init_sec_ace(&ace[2], &act_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 3, ace)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if ((*psd = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL, psa, sd_size)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ return NT_STATUS_OK;
+}
+
+static BOOL get_lsa_policy_samr_sid(pipes_struct *p, POLICY_HND *pol, DOM_SID *sid, uint32 *acc_granted)
+{
+ struct samr_info *info = NULL;
+
+ /* find the policy handle. open a policy on it. */
+ if (!find_policy_by_hnd(p, pol, (void **)&info))
+ return False;
+
+ if (!info)
+ return False;
+
+ *sid = info->sid;
+ *acc_granted = info->acc_granted;
+ return True;
+}
+
+/*******************************************************************
+ _samr_set_sec_obj
+ ********************************************************************/
+
+NTSTATUS _samr_set_sec_obj(pipes_struct *p, SAMR_Q_SET_SEC_OBJ *q_u, SAMR_R_SET_SEC_OBJ *r_u)
+{
+ DEBUG(0,("_samr_set_sec_obj: Not yet implemented!\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*******************************************************************
+ _samr_query_sec_obj
+ ********************************************************************/
+
+NTSTATUS _samr_query_sec_obj(pipes_struct *p, SAMR_Q_QUERY_SEC_OBJ *q_u, SAMR_R_QUERY_SEC_OBJ *r_u)
+{
+ DOM_SID pol_sid;
+ fstring str_sid;
+ SEC_DESC * psd = NULL;
+ size_t sd_size;
+ uint32 acc_granted;
+
+ r_u->status = NT_STATUS_OK;
+
+ /* Get the SID. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->user_pol, &pol_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+
+
+ DEBUG(10,("_samr_query_sec_obj: querying security on SID: %s\n", sid_to_string(str_sid, &pol_sid)));
+
+ /* Check what typ of SID is beeing queried (e.g Domain SID, User SID, Group SID) */
+
+ /* To query the security of the SAM it self an invalid SID with S-0-0 is passed to this function */
+ if (pol_sid.sid_rev_num == 0)
+ {
+ DEBUG(5,("_samr_query_sec_obj: querying security on SAM\n"));
+ r_u->status = samr_make_sam_obj_sd(p->mem_ctx, &psd, &sd_size);
+ }
+ else if (sid_equal(&pol_sid,get_global_sam_sid())) /* check if it is our domain SID */
+
+ {
+ DEBUG(5,("_samr_query_sec_obj: querying security on Domain with SID: %s\n", sid_to_string(str_sid, &pol_sid)));
+ r_u->status = samr_make_dom_obj_sd(p->mem_ctx, &psd, &sd_size);
+ }
+ else if (sid_equal(&pol_sid,&global_sid_Builtin)) /* check if it is the Builtin Domain */
+ {
+ /* TODO: Builtin probably needs a different SD with restricted write access*/
+ DEBUG(5,("_samr_query_sec_obj: querying security on Builtin Domain with SID: %s\n", sid_to_string(str_sid, &pol_sid)));
+ r_u->status = samr_make_dom_obj_sd(p->mem_ctx, &psd, &sd_size);
+ }
+ else if (sid_check_is_in_our_domain(&pol_sid) ||
+ sid_check_is_in_builtin(&pol_sid))
+ {
+ /* TODO: different SDs have to be generated for aliases groups and users.
+ Currently all three get a default user SD */
+ DEBUG(10,("_samr_query_sec_obj: querying security on Object with SID: %s\n", sid_to_string(str_sid, &pol_sid)));
+ r_u->status = samr_make_usr_obj_sd(p->mem_ctx, &psd,&sd_size, &pol_sid);
+ }
+ else return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ if ((r_u->buf = make_sec_desc_buf(p->mem_ctx, sd_size, psd)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if (NT_STATUS_IS_OK(r_u->status))
+ r_u->ptr = 1;
+
+ return r_u->status;
+}
+
+/*******************************************************************
+makes a SAM_ENTRY / UNISTR2* structure from a user list.
+********************************************************************/
+
+static NTSTATUS make_user_sam_entry_list(TALLOC_CTX *ctx, SAM_ENTRY **sam_pp, UNISTR2 **uni_name_pp,
+ uint32 num_entries, uint32 start_idx, SAM_ACCOUNT *disp_user_info,
+ DOM_SID *domain_sid)
+{
+ uint32 i;
+ SAM_ENTRY *sam;
+ UNISTR2 *uni_name;
+ SAM_ACCOUNT *pwd = NULL;
+ UNISTR2 uni_temp_name;
+ const char *temp_name;
+ const DOM_SID *user_sid;
+ uint32 user_rid;
+ fstring user_sid_string;
+ fstring domain_sid_string;
+
+ *sam_pp = NULL;
+ *uni_name_pp = NULL;
+
+ if (num_entries == 0)
+ return NT_STATUS_OK;
+
+ sam = (SAM_ENTRY *)talloc_zero(ctx, sizeof(SAM_ENTRY)*num_entries);
+
+ uni_name = (UNISTR2 *)talloc_zero(ctx, sizeof(UNISTR2)*num_entries);
+
+ if (sam == NULL || uni_name == NULL) {
+ DEBUG(0, ("make_user_sam_entry_list: talloc_zero failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ pwd = &disp_user_info[i+start_idx];
+ temp_name = pdb_get_username(pwd);
+ init_unistr2(&uni_temp_name, temp_name, UNI_STR_TERMINATE);
+ user_sid = pdb_get_user_sid(pwd);
+
+ if (!sid_peek_check_rid(domain_sid, user_sid, &user_rid)) {
+ DEBUG(0, ("make_user_sam_entry_list: User %s has SID %s, which conflicts with "
+ "the domain sid %s. Failing operation.\n",
+ temp_name,
+ sid_to_string(user_sid_string, user_sid),
+ sid_to_string(domain_sid_string, domain_sid)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ init_sam_entry(&sam[i], &uni_temp_name, user_rid);
+ copy_unistr2(&uni_name[i], &uni_temp_name);
+ }
+
+ *sam_pp = sam;
+ *uni_name_pp = uni_name;
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ samr_reply_enum_dom_users
+ ********************************************************************/
+
+NTSTATUS _samr_enum_dom_users(pipes_struct *p, SAMR_Q_ENUM_DOM_USERS *q_u,
+ SAMR_R_ENUM_DOM_USERS *r_u)
+{
+ struct samr_info *info = NULL;
+ uint32 struct_size=0x20; /* W2K always reply that, client doesn't care */
+ int num_account;
+ uint32 enum_context=q_u->start_idx;
+ uint32 max_size=q_u->max_size;
+ uint32 temp_size;
+ enum remote_arch_types ra_type = get_remote_arch();
+ int max_sam_entries = (ra_type == RA_WIN95) ? MAX_SAM_ENTRIES_W95 : MAX_SAM_ENTRIES_W2K;
+ uint32 max_entries = max_sam_entries;
+ DOM_SID domain_sid;
+
+ r_u->status = NT_STATUS_OK;
+
+ /* find the policy handle. open a policy on it. */
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ domain_sid = info->sid;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, info->acc_granted,
+ SA_RIGHT_DOMAIN_ENUM_ACCOUNTS, NULL,
+ "_samr_enum_dom_users"))) {
+ return r_u->status;
+ }
+
+ DEBUG(5,("_samr_enum_dom_users: %d\n", __LINE__));
+
+ become_root();
+ r_u->status=load_sampwd_entries(info, q_u->acb_mask, False);
+ unbecome_root();
+
+ if (!NT_STATUS_IS_OK(r_u->status))
+ return r_u->status;
+
+ num_account = info->disp_info.num_user_account;
+
+ if (enum_context > num_account) {
+ DEBUG(5, ("_samr_enum_dom_users: enumeration handle over total entries\n"));
+ return NT_STATUS_OK;
+ }
+
+ /* verify we won't overflow */
+ if (max_entries > num_account-enum_context) {
+ max_entries = num_account-enum_context;
+ DEBUG(5, ("_samr_enum_dom_users: only %d entries to return\n", max_entries));
+ }
+
+ /* calculate the size and limit on the number of entries we will return */
+ temp_size=max_entries*struct_size;
+
+ if (temp_size>max_size) {
+ max_entries=MIN((max_size/struct_size),max_entries);;
+ DEBUG(5, ("_samr_enum_dom_users: buffer size limits to only %d entries\n", max_entries));
+ }
+
+ /*
+ * Note from JRA. total_entries is not being used here. Currently if there is a
+ * large user base then it looks like NT will enumerate until get_sampwd_entries
+ * returns False due to num_entries being zero. This will cause an access denied
+ * return. I don't think this is right and needs further investigation. Note that
+ * this is also the same in the TNG code (I don't think that has been tested with
+ * a very large user list as MAX_SAM_ENTRIES is set to 600).
+ *
+ * I also think that one of the 'num_entries' return parameters is probably
+ * the "max entries" parameter - but in the TNG code they're all currently set to the same
+ * value (again I think this is wrong).
+ */
+
+ r_u->status = make_user_sam_entry_list(p->mem_ctx, &r_u->sam, &r_u->uni_acct_name,
+ max_entries, enum_context,
+ info->disp_info.disp_user_info,
+ &domain_sid);
+
+ if (!NT_STATUS_IS_OK(r_u->status))
+ return r_u->status;
+
+ if (enum_context+max_entries < num_account)
+ r_u->status = STATUS_MORE_ENTRIES;
+
+ DEBUG(5, ("_samr_enum_dom_users: %d\n", __LINE__));
+
+ init_samr_r_enum_dom_users(r_u, q_u->start_idx + max_entries, max_entries);
+
+ DEBUG(5,("_samr_enum_dom_users: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+makes a SAM_ENTRY / UNISTR2* structure from a group list.
+********************************************************************/
+
+static void make_group_sam_entry_list(TALLOC_CTX *ctx, SAM_ENTRY **sam_pp, UNISTR2 **uni_name_pp,
+ uint32 num_sam_entries, DOMAIN_GRP *grp)
+{
+ uint32 i;
+ SAM_ENTRY *sam;
+ UNISTR2 *uni_name;
+
+ *sam_pp = NULL;
+ *uni_name_pp = NULL;
+
+ if (num_sam_entries == 0)
+ return;
+
+ sam = (SAM_ENTRY *)talloc_zero(ctx, sizeof(SAM_ENTRY)*num_sam_entries);
+
+ uni_name = (UNISTR2 *)talloc_zero(ctx, sizeof(UNISTR2)*num_sam_entries);
+
+ if (sam == NULL || uni_name == NULL) {
+ DEBUG(0, ("NULL pointers in SAMR_R_QUERY_DISPINFO\n"));
+ return;
+ }
+
+ for (i = 0; i < num_sam_entries; i++) {
+ /*
+ * JRA. I think this should include the null. TNG does not.
+ */
+ init_unistr2(&uni_name[i], grp[i].name, UNI_STR_TERMINATE);
+ init_sam_entry(&sam[i], &uni_name[i], grp[i].rid);
+ }
+
+ *sam_pp = sam;
+ *uni_name_pp = uni_name;
+}
+
+/*******************************************************************
+ Get the group entries - similar to get_sampwd_entries().
+ ******************************************************************/
+
+static NTSTATUS get_group_domain_entries( TALLOC_CTX *ctx,
+ DOMAIN_GRP **d_grp, DOM_SID *sid, uint32 start_idx,
+ uint32 *p_num_entries, uint32 max_entries )
+{
+ GROUP_MAP *map=NULL;
+ int i;
+ uint32 group_entries = 0;
+ uint32 num_entries = 0;
+
+ *p_num_entries = 0;
+
+ /* access checks for the users were performed higher up. become/unbecome_root()
+ needed for some passdb backends to enumerate groups */
+
+ become_root();
+ pdb_enum_group_mapping(SID_NAME_DOM_GRP, &map, (int *)&group_entries,
+ ENUM_ONLY_MAPPED);
+ unbecome_root();
+
+ num_entries=group_entries-start_idx;
+
+ /* limit the number of entries */
+ if (num_entries>max_entries) {
+ DEBUG(5,("Limiting to %d entries\n", max_entries));
+ num_entries=max_entries;
+ }
+
+ *d_grp=(DOMAIN_GRP *)talloc_zero(ctx, num_entries*sizeof(DOMAIN_GRP));
+ if (num_entries!=0 && *d_grp==NULL){
+ SAFE_FREE(map);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<num_entries; i++) {
+ fstrcpy((*d_grp)[i].name, map[i+start_idx].nt_name);
+ fstrcpy((*d_grp)[i].comment, map[i+start_idx].comment);
+ sid_split_rid(&map[i+start_idx].sid, &(*d_grp)[i].rid);
+ (*d_grp)[i].attr=SID_NAME_DOM_GRP;
+ }
+
+ SAFE_FREE(map);
+
+ *p_num_entries = num_entries;
+
+ DEBUG(10,("get_group_domain_entries: returning %d entries\n",
+ *p_num_entries));
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Wrapper for enumerating local groups
+ ******************************************************************/
+
+static NTSTATUS get_alias_entries( TALLOC_CTX *ctx, DOMAIN_GRP **d_grp,
+ const DOM_SID *sid, uint32 start_idx,
+ uint32 *p_num_entries, uint32 max_entries )
+{
+ struct acct_info *info;
+ int i;
+ BOOL res;
+
+ become_root();
+ res = pdb_enum_aliases(sid, start_idx, max_entries,
+ p_num_entries, &info);
+ unbecome_root();
+
+ if (!res)
+ return NT_STATUS_ACCESS_DENIED;
+
+ if (*p_num_entries == 0)
+ return NT_STATUS_OK;
+
+ *d_grp = talloc(ctx, sizeof(DOMAIN_GRP) * (*p_num_entries));
+
+ if (*d_grp == NULL) {
+ SAFE_FREE(info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<*p_num_entries; i++) {
+ fstrcpy((*d_grp)[i].name, info[i].acct_name);
+ fstrcpy((*d_grp)[i].comment, info[i].acct_desc);
+ (*d_grp)[i].rid = info[i].rid;
+ (*d_grp)[i].attr = SID_NAME_ALIAS;
+ }
+
+ SAFE_FREE(info);
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ samr_reply_enum_dom_groups
+ ********************************************************************/
+
+NTSTATUS _samr_enum_dom_groups(pipes_struct *p, SAMR_Q_ENUM_DOM_GROUPS *q_u, SAMR_R_ENUM_DOM_GROUPS *r_u)
+{
+ DOMAIN_GRP *grp=NULL;
+ uint32 num_entries;
+ DOM_SID sid;
+ uint32 acc_granted;
+
+ r_u->status = NT_STATUS_OK;
+
+ if (!get_lsa_policy_samr_sid(p, &q_u->pol, &sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_DOMAIN_ENUM_ACCOUNTS, NULL, "_samr_enum_dom_groups"))) {
+ return r_u->status;
+ }
+
+ DEBUG(5,("samr_reply_enum_dom_groups: %d\n", __LINE__));
+
+ /* the domain group array is being allocated in the function below */
+ if (!NT_STATUS_IS_OK(r_u->status = get_group_domain_entries(p->mem_ctx, &grp, &sid, q_u->start_idx, &num_entries, MAX_SAM_ENTRIES))) {
+ return r_u->status;
+ }
+
+ make_group_sam_entry_list(p->mem_ctx, &r_u->sam, &r_u->uni_grp_name, num_entries, grp);
+
+ init_samr_r_enum_dom_groups(r_u, q_u->start_idx, num_entries);
+
+ DEBUG(5,("samr_enum_dom_groups: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+
+/*******************************************************************
+ samr_reply_enum_dom_aliases
+ ********************************************************************/
+
+NTSTATUS _samr_enum_dom_aliases(pipes_struct *p, SAMR_Q_ENUM_DOM_ALIASES *q_u, SAMR_R_ENUM_DOM_ALIASES *r_u)
+{
+ DOMAIN_GRP *grp=NULL;
+ uint32 num_entries = 0;
+ fstring sid_str;
+ DOM_SID sid;
+ NTSTATUS status;
+ uint32 acc_granted;
+
+ r_u->status = NT_STATUS_OK;
+
+ if (!get_lsa_policy_samr_sid(p, &q_u->pol, &sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_DOMAIN_ENUM_ACCOUNTS, NULL, "_samr_enum_dom_aliases"))) {
+ return r_u->status;
+ }
+
+ sid_to_string(sid_str, &sid);
+ DEBUG(5,("samr_reply_enum_dom_aliases: sid %s\n", sid_str));
+
+ status = get_alias_entries(p->mem_ctx, &grp, &sid, q_u->start_idx,
+ &num_entries, MAX_SAM_ENTRIES);
+ if (!NT_STATUS_IS_OK(status)) return status;
+
+ make_group_sam_entry_list(p->mem_ctx, &r_u->sam, &r_u->uni_grp_name, num_entries, grp);
+
+ /*safe_free(grp);*/
+
+ init_samr_r_enum_dom_aliases(r_u, q_u->start_idx + num_entries, num_entries);
+
+ DEBUG(5,("samr_enum_dom_aliases: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ samr_reply_query_dispinfo
+ ********************************************************************/
+
+NTSTATUS _samr_query_dispinfo(pipes_struct *p, SAMR_Q_QUERY_DISPINFO *q_u,
+ SAMR_R_QUERY_DISPINFO *r_u)
+{
+ struct samr_info *info = NULL;
+ uint32 struct_size=0x20; /* W2K always reply that, client doesn't care */
+
+ uint32 max_entries=q_u->max_entries;
+ uint32 enum_context=q_u->start_idx;
+ uint32 max_size=q_u->max_size;
+
+ SAM_DISPINFO_CTR *ctr;
+ uint32 temp_size=0, total_data_size=0;
+ NTSTATUS disp_ret;
+ uint32 num_account = 0;
+ enum remote_arch_types ra_type = get_remote_arch();
+ int max_sam_entries = (ra_type == RA_WIN95) ? MAX_SAM_ENTRIES_W95 : MAX_SAM_ENTRIES_W2K;
+ DOM_SID domain_sid;
+
+ DEBUG(5, ("samr_reply_query_dispinfo: %d\n", __LINE__));
+ r_u->status = NT_STATUS_OK;
+
+ /* find the policy handle. open a policy on it. */
+ if (!find_policy_by_hnd(p, &q_u->domain_pol, (void **)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ domain_sid = info->sid;
+
+ /*
+ * calculate how many entries we will return.
+ * based on
+ * - the number of entries the client asked
+ * - our limit on that
+ * - the starting point (enumeration context)
+ * - the buffer size the client will accept
+ */
+
+ /*
+ * We are a lot more like W2K. Instead of reading the SAM
+ * each time to find the records we need to send back,
+ * we read it once and link that copy to the sam handle.
+ * For large user list (over the MAX_SAM_ENTRIES)
+ * it's a definitive win.
+ * second point to notice: between enumerations
+ * our sam is now the same as it's a snapshoot.
+ * third point: got rid of the static SAM_USER_21 struct
+ * no more intermediate.
+ * con: it uses much more memory, as a full copy is stored
+ * in memory.
+ *
+ * If you want to change it, think twice and think
+ * of the second point , that's really important.
+ *
+ * JFM, 12/20/2001
+ */
+
+ /* Get what we need from the password database */
+ switch (q_u->switch_level) {
+ case 0x1:
+ /* When playing with usrmgr, this is necessary
+ if you want immediate refresh after editing
+ a user. I would like to do this after the
+ setuserinfo2, but we do not have access to
+ the domain handle in that call, only to the
+ user handle. Where else does this hurt?
+ -- Volker
+ */
+#if 0
+ /* We cannot do this here - it kills performace. JRA. */
+ free_samr_users(info);
+#endif
+ case 0x2:
+ case 0x4:
+ become_root();
+ /* Level 2 is for all machines, otherwise only 'normal' users */
+ r_u->status=load_sampwd_entries(info, ACB_NORMAL, q_u->switch_level==2);
+ unbecome_root();
+ if (!NT_STATUS_IS_OK(r_u->status)) {
+ DEBUG(5, ("_samr_query_dispinfo: load_sampwd_entries failed\n"));
+ return r_u->status;
+ }
+ num_account = info->disp_info.num_user_account;
+ break;
+ case 0x3:
+ case 0x5:
+ r_u->status = load_group_domain_entries(info, &info->sid);
+ if (!NT_STATUS_IS_OK(r_u->status))
+ return r_u->status;
+ num_account = info->disp_info.num_group_account;
+ break;
+ default:
+ DEBUG(0,("_samr_query_dispinfo: Unknown info level (%u)\n", (unsigned int)q_u->switch_level ));
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ /* first limit the number of entries we will return */
+ if(max_entries > max_sam_entries) {
+ DEBUG(5, ("samr_reply_query_dispinfo: client requested %d entries, limiting to %d\n", max_entries, max_sam_entries));
+ max_entries = max_sam_entries;
+ }
+
+ if (enum_context > num_account) {
+ DEBUG(5, ("samr_reply_query_dispinfo: enumeration handle over total entries\n"));
+ return NT_STATUS_NO_MORE_ENTRIES;
+ }
+
+ /* verify we won't overflow */
+ if (max_entries > num_account-enum_context) {
+ max_entries = num_account-enum_context;
+ DEBUG(5, ("samr_reply_query_dispinfo: only %d entries to return\n", max_entries));
+ }
+
+ /* calculate the size and limit on the number of entries we will return */
+ temp_size=max_entries*struct_size;
+
+ if (temp_size>max_size) {
+ max_entries=MIN((max_size/struct_size),max_entries);;
+ DEBUG(5, ("samr_reply_query_dispinfo: buffer size limits to only %d entries\n", max_entries));
+ }
+
+ if (!(ctr = (SAM_DISPINFO_CTR *)talloc_zero(p->mem_ctx,sizeof(SAM_DISPINFO_CTR))))
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(ctr);
+
+ /* Now create reply structure */
+ switch (q_u->switch_level) {
+ case 0x1:
+ if (max_entries) {
+ if (!(ctr->sam.info1 = (SAM_DISPINFO_1 *)talloc_zero(p->mem_ctx,max_entries*sizeof(SAM_DISPINFO_1))))
+ return NT_STATUS_NO_MEMORY;
+ }
+ disp_ret = init_sam_dispinfo_1(p->mem_ctx, ctr->sam.info1, max_entries, enum_context,
+ info->disp_info.disp_user_info, &domain_sid);
+ if (!NT_STATUS_IS_OK(disp_ret))
+ return disp_ret;
+ break;
+ case 0x2:
+ if (max_entries) {
+ if (!(ctr->sam.info2 = (SAM_DISPINFO_2 *)talloc_zero(p->mem_ctx,max_entries*sizeof(SAM_DISPINFO_2))))
+ return NT_STATUS_NO_MEMORY;
+ }
+ disp_ret = init_sam_dispinfo_2(p->mem_ctx, ctr->sam.info2, max_entries, enum_context,
+ info->disp_info.disp_user_info, &domain_sid);
+ if (!NT_STATUS_IS_OK(disp_ret))
+ return disp_ret;
+ break;
+ case 0x3:
+ if (max_entries) {
+ if (!(ctr->sam.info3 = (SAM_DISPINFO_3 *)talloc_zero(p->mem_ctx,max_entries*sizeof(SAM_DISPINFO_3))))
+ return NT_STATUS_NO_MEMORY;
+ }
+ disp_ret = init_sam_dispinfo_3(p->mem_ctx, ctr->sam.info3, max_entries, enum_context, info->disp_info.disp_group_info);
+ if (!NT_STATUS_IS_OK(disp_ret))
+ return disp_ret;
+ break;
+ case 0x4:
+ if (max_entries) {
+ if (!(ctr->sam.info4 = (SAM_DISPINFO_4 *)talloc_zero(p->mem_ctx,max_entries*sizeof(SAM_DISPINFO_4))))
+ return NT_STATUS_NO_MEMORY;
+ }
+ disp_ret = init_sam_dispinfo_4(p->mem_ctx, ctr->sam.info4, max_entries, enum_context, info->disp_info.disp_user_info);
+ if (!NT_STATUS_IS_OK(disp_ret))
+ return disp_ret;
+ break;
+ case 0x5:
+ if (max_entries) {
+ if (!(ctr->sam.info5 = (SAM_DISPINFO_5 *)talloc_zero(p->mem_ctx,max_entries*sizeof(SAM_DISPINFO_5))))
+ return NT_STATUS_NO_MEMORY;
+ }
+ disp_ret = init_sam_dispinfo_5(p->mem_ctx, ctr->sam.info5, max_entries, enum_context, info->disp_info.disp_group_info);
+ if (!NT_STATUS_IS_OK(disp_ret))
+ return disp_ret;
+ break;
+
+ default:
+ ctr->sam.info = NULL;
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ /* calculate the total size */
+ total_data_size=num_account*struct_size;
+
+ if (enum_context+max_entries < num_account)
+ r_u->status = STATUS_MORE_ENTRIES;
+
+ DEBUG(5, ("_samr_query_dispinfo: %d\n", __LINE__));
+
+ init_samr_r_query_dispinfo(r_u, max_entries, total_data_size, temp_size, q_u->switch_level, ctr, r_u->status);
+
+ return r_u->status;
+
+}
+
+/*******************************************************************
+ samr_reply_query_aliasinfo
+ ********************************************************************/
+
+NTSTATUS _samr_query_aliasinfo(pipes_struct *p, SAMR_Q_QUERY_ALIASINFO *q_u, SAMR_R_QUERY_ALIASINFO *r_u)
+{
+ DOM_SID sid;
+ struct acct_info info;
+ uint32 acc_granted;
+ BOOL ret;
+
+ r_u->status = NT_STATUS_OK;
+
+ DEBUG(5,("_samr_query_aliasinfo: %d\n", __LINE__));
+
+ /* find the policy handle. open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->pol, &sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_ALIAS_LOOKUP_INFO, NULL, "_samr_query_aliasinfo"))) {
+ return r_u->status;
+ }
+
+ become_root();
+ ret = pdb_get_aliasinfo(&sid, &info);
+ unbecome_root();
+
+ if ( !ret )
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ switch (q_u->switch_level) {
+ case 1:
+ r_u->ptr = 1;
+ r_u->ctr.switch_value1 = 1;
+ init_samr_alias_info1(&r_u->ctr.alias.info1,
+ info.acct_name, 1, info.acct_desc);
+ break;
+ case 3:
+ r_u->ptr = 1;
+ r_u->ctr.switch_value1 = 3;
+ init_samr_alias_info3(&r_u->ctr.alias.info3, info.acct_desc);
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ DEBUG(5,("_samr_query_aliasinfo: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+#if 0
+/*******************************************************************
+ samr_reply_lookup_ids
+ ********************************************************************/
+
+ uint32 _samr_lookup_ids(pipes_struct *p, SAMR_Q_LOOKUP_IDS *q_u, SAMR_R_LOOKUP_IDS *r_u)
+{
+ uint32 rid[MAX_SAM_ENTRIES];
+ int num_rids = q_u->num_sids1;
+
+ r_u->status = NT_STATUS_OK;
+
+ DEBUG(5,("_samr_lookup_ids: %d\n", __LINE__));
+
+ if (num_rids > MAX_SAM_ENTRIES) {
+ num_rids = MAX_SAM_ENTRIES;
+ DEBUG(5,("_samr_lookup_ids: truncating entries to %d\n", num_rids));
+ }
+
+#if 0
+ int i;
+ SMB_ASSERT_ARRAY(q_u->uni_user_name, num_rids);
+
+ for (i = 0; i < num_rids && status == 0; i++)
+ {
+ struct sam_passwd *sam_pass;
+ fstring user_name;
+
+
+ fstrcpy(user_name, unistrn2(q_u->uni_user_name[i].buffer,
+ q_u->uni_user_name[i].uni_str_len));
+
+ /* find the user account */
+ become_root();
+ sam_pass = get_smb21pwd_entry(user_name, 0);
+ unbecome_root();
+
+ if (sam_pass == NULL)
+ {
+ status = 0xC0000000 | NT_STATUS_NO_SUCH_USER;
+ rid[i] = 0;
+ }
+ else
+ {
+ rid[i] = sam_pass->user_rid;
+ }
+ }
+#endif
+
+ num_rids = 1;
+ rid[0] = BUILTIN_ALIAS_RID_USERS;
+
+ init_samr_r_lookup_ids(&r_u, num_rids, rid, NT_STATUS_OK);
+
+ DEBUG(5,("_samr_lookup_ids: %d\n", __LINE__));
+
+ return r_u->status;
+}
+#endif
+
+/*******************************************************************
+ _samr_lookup_names
+ ********************************************************************/
+
+NTSTATUS _samr_lookup_names(pipes_struct *p, SAMR_Q_LOOKUP_NAMES *q_u, SAMR_R_LOOKUP_NAMES *r_u)
+{
+ uint32 rid[MAX_SAM_ENTRIES];
+ uint32 local_rid;
+ enum SID_NAME_USE type[MAX_SAM_ENTRIES];
+ enum SID_NAME_USE local_type;
+ int i;
+ int num_rids = q_u->num_names2;
+ DOM_SID pol_sid;
+ fstring sid_str;
+ uint32 acc_granted;
+
+ r_u->status = NT_STATUS_OK;
+
+ DEBUG(5,("_samr_lookup_names: %d\n", __LINE__));
+
+ ZERO_ARRAY(rid);
+ ZERO_ARRAY(type);
+
+ if (!get_lsa_policy_samr_sid(p, &q_u->pol, &pol_sid, &acc_granted)) {
+ init_samr_r_lookup_names(p->mem_ctx, r_u, 0, NULL, NULL, NT_STATUS_OBJECT_TYPE_MISMATCH);
+ return r_u->status;
+ }
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, 0, NULL, "_samr_lookup_names"))) { /* Don't know the acc_bits yet */
+ return r_u->status;
+ }
+
+ if (num_rids > MAX_SAM_ENTRIES) {
+ num_rids = MAX_SAM_ENTRIES;
+ DEBUG(5,("_samr_lookup_names: truncating entries to %d\n", num_rids));
+ }
+
+ DEBUG(5,("_samr_lookup_names: looking name on SID %s\n", sid_to_string(sid_str, &pol_sid)));
+
+ for (i = 0; i < num_rids; i++) {
+ fstring name;
+ DOM_SID sid;
+ int ret;
+
+ r_u->status = NT_STATUS_NONE_MAPPED;
+
+ rid [i] = 0xffffffff;
+ type[i] = SID_NAME_UNKNOWN;
+
+ ret = rpcstr_pull(name, q_u->uni_name[i].buffer, sizeof(name), q_u->uni_name[i].uni_str_len*2, 0);
+
+ /*
+ * we are only looking for a name
+ * the SID we get back can be outside
+ * the scope of the pol_sid
+ *
+ * in clear: it prevents to reply to domain\group: yes
+ * when only builtin\group exists.
+ *
+ * a cleaner code is to add the sid of the domain we're looking in
+ * to the local_lookup_name function.
+ */
+
+ if ((ret > 0) && local_lookup_name(name, &sid, &local_type)) {
+ sid_split_rid(&sid, &local_rid);
+
+ if (sid_equal(&sid, &pol_sid)) {
+ rid[i]=local_rid;
+
+ /* Windows does not return WKN_GRP here, even
+ * on lookups in builtin */
+ type[i] = (local_type == SID_NAME_WKN_GRP) ?
+ SID_NAME_ALIAS : local_type;
+
+ r_u->status = NT_STATUS_OK;
+ }
+ }
+ }
+
+ init_samr_r_lookup_names(p->mem_ctx, r_u, num_rids, rid, (uint32 *)type, r_u->status);
+
+ DEBUG(5,("_samr_lookup_names: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ _samr_chgpasswd_user
+ ********************************************************************/
+
+NTSTATUS _samr_chgpasswd_user(pipes_struct *p, SAMR_Q_CHGPASSWD_USER *q_u, SAMR_R_CHGPASSWD_USER *r_u)
+{
+ fstring user_name;
+ fstring wks;
+
+ DEBUG(5,("_samr_chgpasswd_user: %d\n", __LINE__));
+
+ r_u->status = NT_STATUS_OK;
+
+ rpcstr_pull(user_name, q_u->uni_user_name.buffer, sizeof(user_name), q_u->uni_user_name.uni_str_len*2, 0);
+ rpcstr_pull(wks, q_u->uni_dest_host.buffer, sizeof(wks), q_u->uni_dest_host.uni_str_len*2,0);
+
+ DEBUG(5,("samr_chgpasswd_user: user: %s wks: %s\n", user_name, wks));
+
+ /*
+ * Pass the user through the NT -> unix user mapping
+ * function.
+ */
+
+ (void)map_username(user_name);
+
+ /*
+ * UNIX username case mangling not required, pass_oem_change
+ * is case insensitive.
+ */
+
+ r_u->status = pass_oem_change(user_name, q_u->lm_newpass.pass, q_u->lm_oldhash.hash,
+ q_u->nt_newpass.pass, q_u->nt_oldhash.hash);
+
+ init_samr_r_chgpasswd_user(r_u, r_u->status);
+
+ DEBUG(5,("_samr_chgpasswd_user: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+makes a SAMR_R_LOOKUP_RIDS structure.
+********************************************************************/
+
+static BOOL make_samr_lookup_rids(TALLOC_CTX *ctx, uint32 num_names, fstring names[],
+ UNIHDR **pp_hdr_name, UNISTR2 **pp_uni_name)
+{
+ uint32 i;
+ UNIHDR *hdr_name=NULL;
+ UNISTR2 *uni_name=NULL;
+
+ *pp_uni_name = NULL;
+ *pp_hdr_name = NULL;
+
+ if (num_names != 0) {
+ hdr_name = (UNIHDR *)talloc_zero(ctx, sizeof(UNIHDR)*num_names);
+ if (hdr_name == NULL)
+ return False;
+
+ uni_name = (UNISTR2 *)talloc_zero(ctx,sizeof(UNISTR2)*num_names);
+ if (uni_name == NULL)
+ return False;
+ }
+
+ for (i = 0; i < num_names; i++) {
+ DEBUG(10, ("names[%d]:%s\n", i, names[i] ? names[i] : ""));
+ init_unistr2(&uni_name[i], names[i], UNI_FLAGS_NONE);
+ init_uni_hdr(&hdr_name[i], &uni_name[i]);
+ }
+
+ *pp_uni_name = uni_name;
+ *pp_hdr_name = hdr_name;
+
+ return True;
+}
+
+/*******************************************************************
+ _samr_lookup_rids
+ ********************************************************************/
+
+NTSTATUS _samr_lookup_rids(pipes_struct *p, SAMR_Q_LOOKUP_RIDS *q_u, SAMR_R_LOOKUP_RIDS *r_u)
+{
+ fstring group_names[MAX_SAM_ENTRIES];
+ uint32 *group_attrs = NULL;
+ UNIHDR *hdr_name = NULL;
+ UNISTR2 *uni_name = NULL;
+ DOM_SID pol_sid;
+ int num_rids = q_u->num_rids1;
+ int i;
+ uint32 acc_granted;
+
+ r_u->status = NT_STATUS_OK;
+
+ DEBUG(5,("_samr_lookup_rids: %d\n", __LINE__));
+
+ /* find the policy handle. open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->pol, &pol_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (num_rids > MAX_SAM_ENTRIES) {
+ num_rids = MAX_SAM_ENTRIES;
+ DEBUG(5,("_samr_lookup_rids: truncating entries to %d\n", num_rids));
+ }
+
+ if (num_rids) {
+ if ((group_attrs = (uint32 *)talloc_zero(p->mem_ctx, num_rids * sizeof(uint32))) == NULL)
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r_u->status = NT_STATUS_NONE_MAPPED;
+
+ become_root(); /* lookup_sid can require root privs */
+
+ for (i = 0; i < num_rids; i++) {
+ fstring tmpname;
+ fstring domname;
+ DOM_SID sid;
+ enum SID_NAME_USE type;
+
+ group_attrs[i] = SID_NAME_UNKNOWN;
+ *group_names[i] = '\0';
+
+ if (sid_equal(&pol_sid, get_global_sam_sid())) {
+ sid_copy(&sid, &pol_sid);
+ sid_append_rid(&sid, q_u->rid[i]);
+
+ if (lookup_sid(&sid, domname, tmpname, &type)) {
+ r_u->status = NT_STATUS_OK;
+ group_attrs[i] = (uint32)type;
+ fstrcpy(group_names[i],tmpname);
+ DEBUG(5,("_samr_lookup_rids: %s:%d\n", group_names[i], group_attrs[i]));
+ }
+ }
+ }
+
+ unbecome_root();
+
+ if(!make_samr_lookup_rids(p->mem_ctx, num_rids, group_names, &hdr_name, &uni_name))
+ return NT_STATUS_NO_MEMORY;
+
+ init_samr_r_lookup_rids(r_u, num_rids, hdr_name, uni_name, group_attrs);
+
+ DEBUG(5,("_samr_lookup_rids: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ _samr_open_user. Safe - gives out no passwd info.
+ ********************************************************************/
+
+NTSTATUS _samr_open_user(pipes_struct *p, SAMR_Q_OPEN_USER *q_u, SAMR_R_OPEN_USER *r_u)
+{
+ SAM_ACCOUNT *sampass=NULL;
+ DOM_SID sid;
+ POLICY_HND domain_pol = q_u->domain_pol;
+ POLICY_HND *user_pol = &r_u->user_pol;
+ struct samr_info *info = NULL;
+ SEC_DESC *psd = NULL;
+ uint32 acc_granted;
+ uint32 des_access = q_u->access_mask;
+ size_t sd_size;
+ BOOL ret;
+ NTSTATUS nt_status;
+ uint32 priv_list[3] = {SE_MACHINE_ACCOUNT, SE_ADD_USERS, SE_NONE};
+
+ r_u->status = NT_STATUS_OK;
+
+ /* find the domain policy handle and get domain SID / access bits in the domain policy. */
+ if (!get_lsa_policy_samr_sid(p, &domain_pol, &sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(nt_status = access_check_samr_function(p, acc_granted, SA_RIGHT_DOMAIN_OPEN_ACCOUNT, priv_list, "_samr_open_user"))) {
+ return nt_status;
+ }
+
+ nt_status = pdb_init_sam_talloc(p->mem_ctx, &sampass);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ /* append the user's RID to it */
+ if (!sid_append_rid(&sid, q_u->user_rid))
+ return NT_STATUS_NO_SUCH_USER;
+
+ /* check if access can be granted as requested by client. */
+ samr_make_usr_obj_sd(p->mem_ctx, &psd, &sd_size, &sid);
+ se_map_generic(&des_access, &usr_generic_mapping);
+ if (!NT_STATUS_IS_OK(nt_status =
+ access_check_samr_object(psd, p, des_access, &acc_granted,
+ priv_list, "_samr_open_user"))) {
+ return nt_status;
+ }
+
+ become_root();
+ ret=pdb_getsampwsid(sampass, &sid);
+ unbecome_root();
+
+ /* check that the SID exists in our domain. */
+ if (ret == False) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ pdb_free_sam(&sampass);
+
+ /* associate the user's SID and access bits with the new handle. */
+ if ((info = get_samr_info_by_sid(&sid)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+ info->acc_granted = acc_granted;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, user_pol, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ return r_u->status;
+}
+
+/*************************************************************************
+ get_user_info_10. Safe. Only gives out acb bits.
+ *************************************************************************/
+
+static NTSTATUS get_user_info_10(TALLOC_CTX *mem_ctx, SAM_USER_INFO_10 *id10, DOM_SID *user_sid)
+{
+ SAM_ACCOUNT *smbpass=NULL;
+ BOOL ret;
+ NTSTATUS nt_status;
+
+ nt_status = pdb_init_sam_talloc(mem_ctx, &smbpass);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ become_root();
+ ret = pdb_getsampwsid(smbpass, user_sid);
+ unbecome_root();
+
+ if (ret==False) {
+ DEBUG(4,("User %s not found\n", sid_string_static(user_sid)));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ DEBUG(3,("User:[%s]\n", pdb_get_username(smbpass) ));
+
+ ZERO_STRUCTP(id10);
+ init_sam_user_info10(id10, pdb_get_acct_ctrl(smbpass) );
+
+ pdb_free_sam(&smbpass);
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ get_user_info_12. OK - this is the killer as it gives out password info.
+ Ensure that this is only allowed on an encrypted connection with a root
+ user. JRA.
+ *************************************************************************/
+
+static NTSTATUS get_user_info_12(pipes_struct *p, TALLOC_CTX *mem_ctx, SAM_USER_INFO_12 * id12, DOM_SID *user_sid)
+{
+ SAM_ACCOUNT *smbpass=NULL;
+ BOOL ret;
+ NTSTATUS nt_status;
+
+ if (!p->ntlmssp_auth_validated)
+ return NT_STATUS_ACCESS_DENIED;
+
+ if (!(p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SIGN) || !(p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SEAL))
+ return NT_STATUS_ACCESS_DENIED;
+
+ /*
+ * Do *NOT* do become_root()/unbecome_root() here ! JRA.
+ */
+
+ nt_status = pdb_init_sam_talloc(mem_ctx, &smbpass);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ ret = pdb_getsampwsid(smbpass, user_sid);
+
+ if (ret == False) {
+ DEBUG(4, ("User %s not found\n", sid_string_static(user_sid)));
+ pdb_free_sam(&smbpass);
+ return (geteuid() == (uid_t)0) ? NT_STATUS_NO_SUCH_USER : NT_STATUS_ACCESS_DENIED;
+ }
+
+ DEBUG(3,("User:[%s] 0x%x\n", pdb_get_username(smbpass), pdb_get_acct_ctrl(smbpass) ));
+
+ if ( pdb_get_acct_ctrl(smbpass) & ACB_DISABLED) {
+ pdb_free_sam(&smbpass);
+ return NT_STATUS_ACCOUNT_DISABLED;
+ }
+
+ ZERO_STRUCTP(id12);
+ init_sam_user_info12(id12, pdb_get_lanman_passwd(smbpass), pdb_get_nt_passwd(smbpass));
+
+ pdb_free_sam(&smbpass);
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ get_user_info_20
+ *************************************************************************/
+
+static NTSTATUS get_user_info_20(TALLOC_CTX *mem_ctx, SAM_USER_INFO_20 *id20, DOM_SID *user_sid)
+{
+ SAM_ACCOUNT *sampass=NULL;
+ BOOL ret;
+
+ pdb_init_sam_talloc(mem_ctx, &sampass);
+
+ become_root();
+ ret = pdb_getsampwsid(sampass, user_sid);
+ unbecome_root();
+
+ if (ret == False) {
+ DEBUG(4,("User %s not found\n", sid_string_static(user_sid)));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ samr_clear_sam_passwd(sampass);
+
+ DEBUG(3,("User:[%s]\n", pdb_get_username(sampass) ));
+
+ ZERO_STRUCTP(id20);
+ init_sam_user_info20A(id20, sampass);
+
+ pdb_free_sam(&sampass);
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ get_user_info_21
+ *************************************************************************/
+
+static NTSTATUS get_user_info_21(TALLOC_CTX *mem_ctx, SAM_USER_INFO_21 *id21,
+ DOM_SID *user_sid, DOM_SID *domain_sid)
+{
+ SAM_ACCOUNT *sampass=NULL;
+ BOOL ret;
+ NTSTATUS nt_status;
+
+ nt_status = pdb_init_sam_talloc(mem_ctx, &sampass);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ become_root();
+ ret = pdb_getsampwsid(sampass, user_sid);
+ unbecome_root();
+
+ if (ret == False) {
+ DEBUG(4,("User %s not found\n", sid_string_static(user_sid)));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ samr_clear_sam_passwd(sampass);
+
+ DEBUG(3,("User:[%s]\n", pdb_get_username(sampass) ));
+
+ ZERO_STRUCTP(id21);
+ nt_status = init_sam_user_info21A(id21, sampass, domain_sid);
+
+ pdb_free_sam(&sampass);
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ _samr_query_userinfo
+ ********************************************************************/
+
+NTSTATUS _samr_query_userinfo(pipes_struct *p, SAMR_Q_QUERY_USERINFO *q_u, SAMR_R_QUERY_USERINFO *r_u)
+{
+ SAM_USERINFO_CTR *ctr;
+ struct samr_info *info = NULL;
+ DOM_SID domain_sid;
+ uint32 rid;
+
+ r_u->status=NT_STATUS_OK;
+
+ /* search for the handle */
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ domain_sid = info->sid;
+
+ sid_split_rid(&domain_sid, &rid);
+
+ if (!sid_check_is_in_our_domain(&info->sid))
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ DEBUG(5,("_samr_query_userinfo: sid:%s\n", sid_string_static(&info->sid)));
+
+ ctr = (SAM_USERINFO_CTR *)talloc_zero(p->mem_ctx, sizeof(SAM_USERINFO_CTR));
+ if (!ctr)
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(ctr);
+
+ /* ok! user info levels (lots: see MSDEV help), off we go... */
+ ctr->switch_value = q_u->switch_value;
+
+ switch (q_u->switch_value) {
+ case 0x10:
+ ctr->info.id10 = (SAM_USER_INFO_10 *)talloc_zero(p->mem_ctx, sizeof(SAM_USER_INFO_10));
+ if (ctr->info.id10 == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if (!NT_STATUS_IS_OK(r_u->status = get_user_info_10(p->mem_ctx, ctr->info.id10, &info->sid)))
+ return r_u->status;
+ break;
+
+#if 0
+/* whoops - got this wrong. i think. or don't understand what's happening. */
+ case 0x11:
+ {
+ NTTIME expire;
+ info = (void *)&id11;
+
+ expire.low = 0xffffffff;
+ expire.high = 0x7fffffff;
+
+ ctr->info.id = (SAM_USER_INFO_11 *)talloc_zero(p->mem_ctx,
+ sizeof
+ (*ctr->
+ info.
+ id11));
+ ZERO_STRUCTP(ctr->info.id11);
+ init_sam_user_info11(ctr->info.id11, &expire,
+ "BROOKFIELDS$", /* name */
+ 0x03ef, /* user rid */
+ 0x201, /* group rid */
+ 0x0080); /* acb info */
+
+ break;
+ }
+#endif
+
+ case 0x12:
+ ctr->info.id12 = (SAM_USER_INFO_12 *)talloc_zero(p->mem_ctx, sizeof(SAM_USER_INFO_12));
+ if (ctr->info.id12 == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if (!NT_STATUS_IS_OK(r_u->status = get_user_info_12(p, p->mem_ctx, ctr->info.id12, &info->sid)))
+ return r_u->status;
+ break;
+
+ case 20:
+ ctr->info.id20 = (SAM_USER_INFO_20 *)talloc_zero(p->mem_ctx,sizeof(SAM_USER_INFO_20));
+ if (ctr->info.id20 == NULL)
+ return NT_STATUS_NO_MEMORY;
+ if (!NT_STATUS_IS_OK(r_u->status = get_user_info_20(p->mem_ctx, ctr->info.id20, &info->sid)))
+ return r_u->status;
+ break;
+
+ case 21:
+ ctr->info.id21 = (SAM_USER_INFO_21 *)talloc_zero(p->mem_ctx,sizeof(SAM_USER_INFO_21));
+ if (ctr->info.id21 == NULL)
+ return NT_STATUS_NO_MEMORY;
+ if (!NT_STATUS_IS_OK(r_u->status = get_user_info_21(p->mem_ctx, ctr->info.id21,
+ &info->sid, &domain_sid)))
+ return r_u->status;
+ break;
+
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ init_samr_r_query_userinfo(r_u, ctr, r_u->status);
+
+ DEBUG(5,("_samr_query_userinfo: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ samr_reply_query_usergroups
+ ********************************************************************/
+
+NTSTATUS _samr_query_usergroups(pipes_struct *p, SAMR_Q_QUERY_USERGROUPS *q_u, SAMR_R_QUERY_USERGROUPS *r_u)
+{
+ SAM_ACCOUNT *sam_pass=NULL;
+ DOM_SID sid;
+ DOM_GID *gids = NULL;
+ int num_groups = 0;
+ uint32 acc_granted;
+ BOOL ret;
+
+ /*
+ * from the SID in the request:
+ * we should send back the list of DOMAIN GROUPS
+ * the user is a member of
+ *
+ * and only the DOMAIN GROUPS
+ * no ALIASES !!! neither aliases of the domain
+ * nor aliases of the builtin SID
+ *
+ * JFM, 12/2/2001
+ */
+
+ r_u->status = NT_STATUS_OK;
+
+ DEBUG(5,("_samr_query_usergroups: %d\n", __LINE__));
+
+ /* find the policy handle. open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->pol, &sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_USER_GET_GROUPS, NULL, "_samr_query_usergroups"))) {
+ return r_u->status;
+ }
+
+ if (!sid_check_is_in_our_domain(&sid))
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ pdb_init_sam(&sam_pass);
+
+ become_root();
+ ret = pdb_getsampwsid(sam_pass, &sid);
+ unbecome_root();
+
+ if (ret == False) {
+ pdb_free_sam(&sam_pass);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if(!get_domain_user_groups(p->mem_ctx, &num_groups, &gids, sam_pass)) {
+ pdb_free_sam(&sam_pass);
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ /* construct the response. lkclXXXX: gids are not copied! */
+ init_samr_r_query_usergroups(r_u, num_groups, gids, r_u->status);
+
+ DEBUG(5,("_samr_query_usergroups: %d\n", __LINE__));
+
+ pdb_free_sam(&sam_pass);
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ _samr_query_dom_info
+ ********************************************************************/
+
+NTSTATUS _samr_query_dom_info(pipes_struct *p, SAMR_Q_QUERY_DOMAIN_INFO *q_u, SAMR_R_QUERY_DOMAIN_INFO *r_u)
+{
+ struct samr_info *info = NULL;
+ SAM_UNK_CTR *ctr;
+ uint32 min_pass_len,pass_hist,flag;
+ time_t u_expire, u_min_age;
+ NTTIME nt_expire, nt_min_age;
+
+ time_t u_lock_duration, u_reset_time;
+ NTTIME nt_lock_duration, nt_reset_time;
+ uint32 lockout;
+
+ time_t u_logout;
+ NTTIME nt_logout;
+
+ uint32 account_policy_temp;
+
+ uint32 num_users=0, num_groups=0, num_aliases=0;
+
+ if ((ctr = (SAM_UNK_CTR *)talloc_zero(p->mem_ctx, sizeof(SAM_UNK_CTR))) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(ctr);
+
+ r_u->status = NT_STATUS_OK;
+
+ DEBUG(5,("_samr_query_dom_info: %d\n", __LINE__));
+
+ /* find the policy handle. open a policy on it. */
+ if (!find_policy_by_hnd(p, &q_u->domain_pol, (void **)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ switch (q_u->switch_value) {
+ case 0x01:
+
+ account_policy_get(AP_MIN_PASSWORD_LEN, &account_policy_temp);
+ min_pass_len = account_policy_temp;
+
+ account_policy_get(AP_PASSWORD_HISTORY, &account_policy_temp);
+ pass_hist = account_policy_temp;
+
+ account_policy_get(AP_USER_MUST_LOGON_TO_CHG_PASS, &account_policy_temp);
+ flag = account_policy_temp;
+
+ account_policy_get(AP_MAX_PASSWORD_AGE, &account_policy_temp);
+ u_expire = account_policy_temp;
+
+ account_policy_get(AP_MIN_PASSWORD_AGE, &account_policy_temp);
+ u_min_age = account_policy_temp;
+
+ unix_to_nt_time_abs(&nt_expire, u_expire);
+ unix_to_nt_time_abs(&nt_min_age, u_min_age);
+
+ init_unk_info1(&ctr->info.inf1, (uint16)min_pass_len, (uint16)pass_hist,
+ flag, nt_expire, nt_min_age);
+ break;
+ case 0x02:
+ become_root();
+ r_u->status=load_sampwd_entries(info, ACB_NORMAL, False);
+ unbecome_root();
+ if (!NT_STATUS_IS_OK(r_u->status)) {
+ DEBUG(5, ("_samr_query_dispinfo: load_sampwd_entries failed\n"));
+ return r_u->status;
+ }
+ num_users=info->disp_info.num_user_account;
+ free_samr_db(info);
+
+ r_u->status=load_group_domain_entries(info, get_global_sam_sid());
+ if (!NT_STATUS_IS_OK(r_u->status)) {
+ DEBUG(5, ("_samr_query_dispinfo: load_group_domain_entries failed\n"));
+ return r_u->status;
+ }
+ num_groups=info->disp_info.num_group_account;
+ free_samr_db(info);
+
+ /* The time call below is to get a sequence number for the sam. FIXME !!! JRA. */
+ init_unk_info2(&ctr->info.inf2, lp_workgroup(), global_myname(), (uint32) time(NULL),
+ num_users, num_groups, num_aliases);
+ break;
+ case 0x03:
+ account_policy_get(AP_TIME_TO_LOGOUT, (unsigned int *)&u_logout);
+ unix_to_nt_time_abs(&nt_logout, u_logout);
+
+ init_unk_info3(&ctr->info.inf3, nt_logout);
+ break;
+ case 0x05:
+ init_unk_info5(&ctr->info.inf5, global_myname());
+ break;
+ case 0x06:
+ init_unk_info6(&ctr->info.inf6);
+ break;
+ case 0x07:
+ init_unk_info7(&ctr->info.inf7);
+ break;
+ case 0x0c:
+ account_policy_get(AP_LOCK_ACCOUNT_DURATION, &account_policy_temp);
+ u_lock_duration = account_policy_temp * 60;
+
+ account_policy_get(AP_RESET_COUNT_TIME, &account_policy_temp);
+ u_reset_time = account_policy_temp * 60;
+
+ account_policy_get(AP_BAD_ATTEMPT_LOCKOUT, &account_policy_temp);
+ lockout = account_policy_temp;
+
+ unix_to_nt_time_abs(&nt_lock_duration, u_lock_duration);
+ unix_to_nt_time_abs(&nt_reset_time, u_reset_time);
+
+ init_unk_info12(&ctr->info.inf12, nt_lock_duration, nt_reset_time, (uint16)lockout);
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ init_samr_r_query_dom_info(r_u, q_u->switch_value, ctr, NT_STATUS_OK);
+
+ DEBUG(5,("_samr_query_dom_info: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ _samr_create_user
+ Create an account, can be either a normal user or a machine.
+ This funcion will need to be updated for bdc/domain trusts.
+ ********************************************************************/
+
+NTSTATUS _samr_create_user(pipes_struct *p, SAMR_Q_CREATE_USER *q_u, SAMR_R_CREATE_USER *r_u)
+{
+ SAM_ACCOUNT *sam_pass=NULL;
+ fstring account;
+ DOM_SID sid;
+ pstring add_script;
+ POLICY_HND dom_pol = q_u->domain_pol;
+ UNISTR2 user_account = q_u->uni_name;
+ uint16 acb_info = q_u->acb_info;
+ POLICY_HND *user_pol = &r_u->user_pol;
+ struct samr_info *info = NULL;
+ BOOL ret;
+ NTSTATUS nt_status;
+ struct passwd *pw;
+ uint32 acc_granted;
+ SEC_DESC *psd;
+ size_t sd_size;
+ uint32 new_rid = 0;
+ /* check this, when giving away 'add computer to domain' privs */
+ uint32 des_access = GENERIC_RIGHTS_USER_ALL_ACCESS;
+ uint32 priv_list[3] = {SE_MACHINE_ACCOUNT, SE_ADD_USERS, SE_NONE};
+
+ /* Get the domain SID stored in the domain policy */
+ if (!get_lsa_policy_samr_sid(p, &dom_pol, &sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(nt_status = access_check_samr_function(p, acc_granted, SA_RIGHT_DOMAIN_CREATE_USER, priv_list, "_samr_create_user"))) {
+ return nt_status;
+ }
+
+ if (!(acb_info == ACB_NORMAL || acb_info == ACB_DOMTRUST || acb_info == ACB_WSTRUST || acb_info == ACB_SVRTRUST)) {
+ /* Match Win2k, and return NT_STATUS_INVALID_PARAMETER if
+ this parameter is not an account type */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* find the account: tell the caller if it exists.
+ lkclXXXX i have *no* idea if this is a problem or not
+ or even if you are supposed to construct a different
+ reply if the account already exists...
+ */
+
+ rpcstr_pull(account, user_account.buffer, sizeof(account), user_account.uni_str_len*2, 0);
+ strlower_m(account);
+
+ pdb_init_sam(&sam_pass);
+
+ become_root();
+ ret = pdb_getsampwnam(sam_pass, account);
+ unbecome_root();
+ if (ret == True) {
+ /* this account exists: say so */
+ pdb_free_sam(&sam_pass);
+ return NT_STATUS_USER_EXISTS;
+ }
+
+ pdb_free_sam(&sam_pass);
+
+ /*
+ * NB. VERY IMPORTANT ! This call must be done as the current pipe user,
+ * *NOT* surrounded by a become_root()/unbecome_root() call. This ensures
+ * that only people with write access to the smbpasswd file will be able
+ * to create a user. JRA.
+ */
+
+ /*
+ * add the user in the /etc/passwd file or the unix authority system.
+ * We don't check if the smb_create_user() function succed or not for 2 reasons:
+ * a) local_password_change() checks for us if the /etc/passwd account really exists
+ * b) smb_create_user() would return an error if the account already exists
+ * and as it could return an error also if it can't create the account, it would be tricky.
+ *
+ * So we go the easy way, only check after if the account exists.
+ * JFM (2/3/2001), to clear any possible bad understanding (-:
+ *
+ * We now have seperate script paramaters for adding users/machines so we
+ * now have some sainity-checking to match.
+ */
+
+ DEBUG(10,("checking account %s at pos %lu for $ termination\n",account, (unsigned long)strlen(account)-1));
+
+ /*
+ * we used to have code here that made sure the acb_info flags
+ * matched with the users named (e.g. an account flags as a machine
+ * trust account ended in '$'). It has been ifdef'd out for a long
+ * time, so I replaced it with this comment. --jerry
+ */
+
+ /* the passdb lookup has failed; check to see if we need to run the
+ add user/machine script */
+
+ /*
+ * we can't check both the ending $ and the acb_info.
+ *
+ * UserManager creates trust accounts (ending in $,
+ * normal that hidden accounts) with the acb_info equals to ACB_NORMAL.
+ * JFM, 11/29/2001
+ */
+ if (account[strlen(account)-1] == '$') {
+ if (NT_STATUS_IS_OK(user_has_privilege(&(p->pipe_user), SE_MACHINE_ACCOUNT)) || geteuid() == 0) {
+ DEBUG(3, ("user [%s] has been granted Add Machines privilege!\n", p->user_name));
+ become_root();
+ pstrcpy(add_script, lp_addmachine_script());
+ } else {
+ DEBUG(3, ("user [%s] doesn't have Add Machines privilege!\n", p->user_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ } else {
+ if (NT_STATUS_IS_OK(user_has_privilege(&(p->pipe_user), SE_ADD_USERS)) || geteuid() == 0) {
+ DEBUG(3, ("user [%s] has been granted Add Users privilege!\n", p->user_name));
+ become_root();
+ pstrcpy(add_script, lp_adduser_script());
+ } else {
+ DEBUG(3, ("user [%s] doesn't have Add Users privilege!\n", p->user_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ pw = Get_Pwnam(account);
+
+ /*********************************************************************
+ * HEADS UP! If we have to create a new user account, we have to get
+ * a new RID from somewhere. This used to be done by the passdb
+ * backend. It has been moved into idmap now. Since idmap is now
+ * wrapped up behind winbind, this means you have to run winbindd if you
+ * want new accounts to get a new RID when "enable rid algorithm = no".
+ * Tough. We now have a uniform way of allocating RIDs regardless
+ * of what ever passdb backend people may use.
+ * --jerry (2003-07-10)
+ *********************************************************************/
+
+ if ( !pw ) {
+
+ if (add_script[0] != '\0') {
+ int add_ret;
+ all_string_sub(add_script, "%u", account, sizeof(account));
+ add_ret = smbrun(add_script,NULL);
+ }
+ else /* no add user script -- ask winbindd to do it */
+ {
+ DEBUG(0, ("_samr_create_user: lp_adduser_script() = %s add_script = %s\n", lp_adduser_script(), add_script));
+ if (!winbind_create_user(account, &new_rid)) {
+ DEBUG(3,("_samr_create_user: winbind_create_user(%s) failed\n",
+ account));
+ }
+ }
+
+ }
+
+ /* implicit call to getpwnam() next. we have a valid SID coming out of this call */
+
+ if ( !NT_STATUS_IS_OK(nt_status = pdb_init_sam_new(&sam_pass, account, new_rid)) )
+ goto done;
+
+ pdb_set_acct_ctrl(sam_pass, acb_info, PDB_CHANGED);
+
+ if (!pdb_add_sam_account(sam_pass)) {
+ pdb_free_sam(&sam_pass);
+ DEBUG(0, ("could not add user/computer %s to passdb !?\n",
+ account));
+ nt_status = NT_STATUS_ACCESS_DENIED;
+ goto done;
+ }
+
+ /* Get the user's SID */
+ sid_copy(&sid, pdb_get_user_sid(sam_pass));
+
+ samr_make_usr_obj_sd(p->mem_ctx, &psd, &sd_size, &sid);
+ se_map_generic(&des_access, &usr_generic_mapping);
+ if (!NT_STATUS_IS_OK(nt_status =
+ access_check_samr_object(psd, p, des_access, &acc_granted,
+ priv_list, "_samr_create_user"))) {
+ goto done;
+ }
+
+ /* associate the user's SID with the new handle. */
+ if ((info = get_samr_info_by_sid(&sid)) == NULL) {
+ pdb_free_sam(&sam_pass);
+ nt_status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ ZERO_STRUCTP(info);
+ info->sid = sid;
+ info->acc_granted = acc_granted;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, user_pol, free_samr_info, (void *)info)) {
+ pdb_free_sam(&sam_pass);
+ nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ goto done;
+ }
+
+ r_u->user_rid=pdb_get_user_rid(sam_pass);
+
+ r_u->access_granted = acc_granted;
+
+ pdb_free_sam(&sam_pass);
+
+ nt_status = NT_STATUS_OK;
+
+done:
+ unbecome_root();
+ return nt_status;
+}
+
+/*******************************************************************
+ samr_reply_connect_anon
+ ********************************************************************/
+
+NTSTATUS _samr_connect_anon(pipes_struct *p, SAMR_Q_CONNECT_ANON *q_u, SAMR_R_CONNECT_ANON *r_u)
+{
+ struct samr_info *info = NULL;
+ uint32 des_access = q_u->access_mask;
+
+ /* Access check */
+
+ if (!pipe_access_check(p)) {
+ DEBUG(3, ("access denied to samr_connect_anon\n"));
+ r_u->status = NT_STATUS_ACCESS_DENIED;
+ return r_u->status;
+ }
+
+ /* set up the SAMR connect_anon response */
+
+ r_u->status = NT_STATUS_OK;
+
+ /* associate the user's SID with the new handle. */
+ if ((info = get_samr_info_by_sid(NULL)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ /* don't give away the farm but this is probably ok. The SA_RIGHT_SAM_ENUM_DOMAINS
+ was observed from a win98 client trying to enumerate users (when configured
+ user level access control on shares) --jerry */
+
+ se_map_generic( &des_access, &sam_generic_mapping );
+ info->acc_granted = des_access & (SA_RIGHT_SAM_ENUM_DOMAINS|SA_RIGHT_SAM_OPEN_DOMAIN);
+
+ info->status = q_u->unknown_0;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, &r_u->connect_pol, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ samr_reply_connect
+ ********************************************************************/
+
+NTSTATUS _samr_connect(pipes_struct *p, SAMR_Q_CONNECT *q_u, SAMR_R_CONNECT *r_u)
+{
+ struct samr_info *info = NULL;
+ SEC_DESC *psd = NULL;
+ uint32 acc_granted;
+ uint32 des_access = q_u->access_mask;
+ size_t sd_size;
+ NTSTATUS nt_status;
+
+
+ DEBUG(5,("_samr_connect: %d\n", __LINE__));
+
+ /* Access check */
+
+ if (!pipe_access_check(p)) {
+ DEBUG(3, ("access denied to samr_connect\n"));
+ r_u->status = NT_STATUS_ACCESS_DENIED;
+ return r_u->status;
+ }
+
+ samr_make_sam_obj_sd(p->mem_ctx, &psd, &sd_size);
+ se_map_generic(&des_access, &sam_generic_mapping);
+ if (!NT_STATUS_IS_OK(nt_status =
+ access_check_samr_object(psd, p, des_access, &acc_granted,
+ NULL, "_samr_connect"))) {
+ return nt_status;
+ }
+
+ r_u->status = NT_STATUS_OK;
+
+ /* associate the user's SID and access granted with the new handle. */
+ if ((info = get_samr_info_by_sid(NULL)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ info->acc_granted = acc_granted;
+ info->status = q_u->access_mask;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, &r_u->connect_pol, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ DEBUG(5,("_samr_connect: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ samr_connect4
+ ********************************************************************/
+
+NTSTATUS _samr_connect4(pipes_struct *p, SAMR_Q_CONNECT4 *q_u, SAMR_R_CONNECT4 *r_u)
+{
+ struct samr_info *info = NULL;
+ SEC_DESC *psd = NULL;
+ uint32 acc_granted;
+ uint32 des_access = q_u->access_mask;
+ size_t sd_size;
+ NTSTATUS nt_status;
+
+
+ DEBUG(5,("_samr_connect4: %d\n", __LINE__));
+
+ /* Access check */
+
+ if (!pipe_access_check(p)) {
+ DEBUG(3, ("access denied to samr_connect4\n"));
+ r_u->status = NT_STATUS_ACCESS_DENIED;
+ return r_u->status;
+ }
+
+ samr_make_sam_obj_sd(p->mem_ctx, &psd, &sd_size);
+ se_map_generic(&des_access, &sam_generic_mapping);
+ if (!NT_STATUS_IS_OK(nt_status =
+ access_check_samr_object(psd, p, des_access, &acc_granted,
+ NULL, "_samr_connect"))) {
+ return nt_status;
+ }
+
+ r_u->status = NT_STATUS_OK;
+
+ /* associate the user's SID and access granted with the new handle. */
+ if ((info = get_samr_info_by_sid(NULL)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ info->acc_granted = acc_granted;
+ info->status = q_u->access_mask;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, &r_u->connect_pol, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ DEBUG(5,("_samr_connect: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/**********************************************************************
+ api_samr_lookup_domain
+ **********************************************************************/
+
+NTSTATUS _samr_lookup_domain(pipes_struct *p, SAMR_Q_LOOKUP_DOMAIN *q_u, SAMR_R_LOOKUP_DOMAIN *r_u)
+{
+ struct samr_info *info;
+ fstring domain_name;
+ DOM_SID sid;
+
+ r_u->status = NT_STATUS_OK;
+
+ if (!find_policy_by_hnd(p, &q_u->connect_pol, (void**)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, info->acc_granted,
+ SA_RIGHT_SAM_ENUM_DOMAINS, NULL, "_samr_lookup_domain")))
+ {
+ return r_u->status;
+ }
+
+ rpcstr_pull(domain_name, q_u->uni_domain.buffer, sizeof(domain_name), q_u->uni_domain.uni_str_len*2, 0);
+
+ ZERO_STRUCT(sid);
+
+ if (!secrets_fetch_domain_sid(domain_name, &sid)) {
+ r_u->status = NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ DEBUG(2,("Returning domain sid for domain %s -> %s\n", domain_name, sid_string_static(&sid)));
+
+ init_samr_r_lookup_domain(r_u, &sid, r_u->status);
+
+ return r_u->status;
+}
+
+/******************************************************************
+makes a SAMR_R_ENUM_DOMAINS structure.
+********************************************************************/
+
+static BOOL make_enum_domains(TALLOC_CTX *ctx, SAM_ENTRY **pp_sam,
+ UNISTR2 **pp_uni_name, uint32 num_sam_entries, fstring doms[])
+{
+ uint32 i;
+ SAM_ENTRY *sam;
+ UNISTR2 *uni_name;
+
+ DEBUG(5, ("make_enum_domains\n"));
+
+ *pp_sam = NULL;
+ *pp_uni_name = NULL;
+
+ if (num_sam_entries == 0)
+ return True;
+
+ sam = (SAM_ENTRY *)talloc_zero(ctx, sizeof(SAM_ENTRY)*num_sam_entries);
+ uni_name = (UNISTR2 *)talloc_zero(ctx, sizeof(UNISTR2)*num_sam_entries);
+
+ if (sam == NULL || uni_name == NULL)
+ return False;
+
+ for (i = 0; i < num_sam_entries; i++) {
+ init_unistr2(&uni_name[i], doms[i], UNI_FLAGS_NONE);
+ init_sam_entry(&sam[i], &uni_name[i], 0);
+ }
+
+ *pp_sam = sam;
+ *pp_uni_name = uni_name;
+
+ return True;
+}
+
+/**********************************************************************
+ api_samr_enum_domains
+ **********************************************************************/
+
+NTSTATUS _samr_enum_domains(pipes_struct *p, SAMR_Q_ENUM_DOMAINS *q_u, SAMR_R_ENUM_DOMAINS *r_u)
+{
+ struct samr_info *info;
+ uint32 num_entries = 2;
+ fstring dom[2];
+ const char *name;
+
+ r_u->status = NT_STATUS_OK;
+
+ if (!find_policy_by_hnd(p, &q_u->pol, (void**)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, info->acc_granted, SA_RIGHT_SAM_ENUM_DOMAINS, NULL, "_samr_enum_domains"))) {
+ return r_u->status;
+ }
+
+ name = get_global_sam_name();
+
+ fstrcpy(dom[0],name);
+ strupper_m(dom[0]);
+ fstrcpy(dom[1],"Builtin");
+
+ if (!make_enum_domains(p->mem_ctx, &r_u->sam, &r_u->uni_dom_name, num_entries, dom))
+ return NT_STATUS_NO_MEMORY;
+
+ init_samr_r_enum_domains(r_u, q_u->start_idx + num_entries, num_entries);
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ api_samr_open_alias
+ ********************************************************************/
+
+NTSTATUS _samr_open_alias(pipes_struct *p, SAMR_Q_OPEN_ALIAS *q_u, SAMR_R_OPEN_ALIAS *r_u)
+{
+ DOM_SID sid;
+ POLICY_HND domain_pol = q_u->dom_pol;
+ uint32 alias_rid = q_u->rid_alias;
+ POLICY_HND *alias_pol = &r_u->pol;
+ struct samr_info *info = NULL;
+ SEC_DESC *psd = NULL;
+ uint32 acc_granted;
+ uint32 des_access = q_u->access_mask;
+ size_t sd_size;
+ NTSTATUS status;
+
+ r_u->status = NT_STATUS_OK;
+
+ /* find the domain policy and get the SID / access bits stored in the domain policy */
+ if (!get_lsa_policy_samr_sid(p, &domain_pol, &sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(status = access_check_samr_function(p, acc_granted, SA_RIGHT_DOMAIN_OPEN_ACCOUNT, NULL, "_samr_open_alias"))) {
+ return status;
+ }
+
+ /* append the alias' RID to it */
+ if (!sid_append_rid(&sid, alias_rid))
+ return NT_STATUS_NO_SUCH_USER;
+
+ /*check if access can be granted as requested by client. */
+ samr_make_ali_obj_sd(p->mem_ctx, &psd, &sd_size);
+ se_map_generic(&des_access,&ali_generic_mapping);
+ if (!NT_STATUS_IS_OK(status =
+ access_check_samr_object(psd, p, des_access, &acc_granted,
+ NULL, "_samr_open_alias"))) {
+ return status;
+ }
+
+ /*
+ * we should check if the rid really exist !!!
+ * JFM.
+ */
+
+ /* associate the user's SID with the new handle. */
+ if ((info = get_samr_info_by_sid(&sid)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ info->acc_granted = acc_granted;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, alias_pol, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ set_user_info_10
+ ********************************************************************/
+
+static BOOL set_user_info_10(const SAM_USER_INFO_10 *id10, DOM_SID *sid)
+{
+ SAM_ACCOUNT *pwd =NULL;
+ BOOL ret;
+
+ pdb_init_sam(&pwd);
+
+ ret = pdb_getsampwsid(pwd, sid);
+
+ if(ret==False) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ if (id10 == NULL) {
+ DEBUG(5, ("set_user_info_10: NULL id10\n"));
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ /* FIX ME: check if the value is really changed --metze */
+ if (!pdb_set_acct_ctrl(pwd, id10->acb_info, PDB_CHANGED)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ if(!pdb_update_sam_account(pwd)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ pdb_free_sam(&pwd);
+
+ return True;
+}
+
+/*******************************************************************
+ set_user_info_12
+ ********************************************************************/
+
+static BOOL set_user_info_12(SAM_USER_INFO_12 *id12, DOM_SID *sid)
+{
+ SAM_ACCOUNT *pwd = NULL;
+
+ pdb_init_sam(&pwd);
+
+ if(!pdb_getsampwsid(pwd, sid)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ if (id12 == NULL) {
+ DEBUG(2, ("set_user_info_12: id12 is NULL\n"));
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ if (!pdb_set_lanman_passwd (pwd, id12->lm_pwd, PDB_CHANGED)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+ if (!pdb_set_nt_passwd (pwd, id12->nt_pwd, PDB_CHANGED)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+ if (!pdb_set_pass_changed_now (pwd)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ if(!pdb_update_sam_account(pwd)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ pdb_free_sam(&pwd);
+ return True;
+}
+
+/*******************************************************************
+ The GROUPSID field in the SAM_ACCOUNT changed. Try to tell unix.
+ ********************************************************************/
+static BOOL set_unix_primary_group(SAM_ACCOUNT *sampass)
+{
+ struct group *grp;
+ gid_t gid;
+
+ if (!NT_STATUS_IS_OK(sid_to_gid(pdb_get_group_sid(sampass),
+ &gid))) {
+ DEBUG(2,("Could not get gid for primary group of "
+ "user %s\n", pdb_get_username(sampass)));
+ return False;
+ }
+
+ grp = getgrgid(gid);
+
+ if (grp == NULL) {
+ DEBUG(2,("Could not find primary group %lu for "
+ "user %s\n", (unsigned long)gid,
+ pdb_get_username(sampass)));
+ return False;
+ }
+
+ if (smb_set_primary_group(grp->gr_name,
+ pdb_get_username(sampass)) != 0) {
+ DEBUG(2,("Could not set primary group for user %s to "
+ "%s\n",
+ pdb_get_username(sampass), grp->gr_name));
+ return False;
+ }
+
+ return True;
+}
+
+
+/*******************************************************************
+ set_user_info_20
+ ********************************************************************/
+
+static BOOL set_user_info_20(SAM_USER_INFO_20 *id20, DOM_SID *sid)
+{
+ SAM_ACCOUNT *pwd = NULL;
+
+ if (id20 == NULL) {
+ DEBUG(5, ("set_user_info_20: NULL id20\n"));
+ return False;
+ }
+
+ pdb_init_sam(&pwd);
+
+ if (!pdb_getsampwsid(pwd, sid)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ copy_id20_to_sam_passwd(pwd, id20);
+
+ /* write the change out */
+ if(!pdb_update_sam_account(pwd)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ pdb_free_sam(&pwd);
+
+ return True;
+}
+/*******************************************************************
+ set_user_info_21
+ ********************************************************************/
+
+static BOOL set_user_info_21(SAM_USER_INFO_21 *id21, DOM_SID *sid)
+{
+ SAM_ACCOUNT *pwd = NULL;
+
+ if (id21 == NULL) {
+ DEBUG(5, ("set_user_info_21: NULL id21\n"));
+ return False;
+ }
+
+ pdb_init_sam(&pwd);
+
+ if (!pdb_getsampwsid(pwd, sid)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ copy_id21_to_sam_passwd(pwd, id21);
+
+ /*
+ * The funny part about the previous two calls is
+ * that pwd still has the password hashes from the
+ * passdb entry. These have not been updated from
+ * id21. I don't know if they need to be set. --jerry
+ */
+
+ if (IS_SAM_CHANGED(pwd, PDB_GROUPSID))
+ set_unix_primary_group(pwd);
+
+ /* write the change out */
+ if(!pdb_update_sam_account(pwd)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ pdb_free_sam(&pwd);
+
+ return True;
+}
+
+/*******************************************************************
+ set_user_info_23
+ ********************************************************************/
+
+static BOOL set_user_info_23(SAM_USER_INFO_23 *id23, DOM_SID *sid)
+{
+ SAM_ACCOUNT *pwd = NULL;
+ pstring plaintext_buf;
+ uint32 len;
+ uint16 acct_ctrl;
+
+ if (id23 == NULL) {
+ DEBUG(5, ("set_user_info_23: NULL id23\n"));
+ return False;
+ }
+
+ pdb_init_sam(&pwd);
+
+ if (!pdb_getsampwsid(pwd, sid)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ DEBUG(5, ("Attempting administrator password change (level 23) for user %s\n",
+ pdb_get_username(pwd)));
+
+ acct_ctrl = pdb_get_acct_ctrl(pwd);
+
+ if (!decode_pw_buffer((char*)id23->pass, plaintext_buf, 256, &len, STR_UNICODE)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ if (!pdb_set_plaintext_passwd (pwd, plaintext_buf)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ copy_id23_to_sam_passwd(pwd, id23);
+
+ /* if it's a trust account, don't update /etc/passwd */
+ if ( ( (acct_ctrl & ACB_DOMTRUST) == ACB_DOMTRUST ) ||
+ ( (acct_ctrl & ACB_WSTRUST) == ACB_WSTRUST) ||
+ ( (acct_ctrl & ACB_SVRTRUST) == ACB_SVRTRUST) ) {
+ DEBUG(5, ("Changing trust account or non-unix-user password, not updating /etc/passwd\n"));
+ } else {
+ /* update the UNIX password */
+ if (lp_unix_password_sync() ) {
+ struct passwd *passwd = Get_Pwnam(pdb_get_username(pwd));
+ if (!passwd) {
+ DEBUG(1, ("chgpasswd: Username does not exist in system !?!\n"));
+ }
+
+ if(!chgpasswd(pdb_get_username(pwd), passwd, "", plaintext_buf, True)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+ }
+ }
+
+ ZERO_STRUCT(plaintext_buf);
+
+ if (IS_SAM_CHANGED(pwd, PDB_GROUPSID))
+ set_unix_primary_group(pwd);
+
+ if(!pdb_update_sam_account(pwd)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ pdb_free_sam(&pwd);
+
+ return True;
+}
+
+/*******************************************************************
+ set_user_info_pw
+ ********************************************************************/
+
+static BOOL set_user_info_pw(char *pass, DOM_SID *sid)
+{
+ SAM_ACCOUNT *pwd = NULL;
+ uint32 len;
+ pstring plaintext_buf;
+ uint16 acct_ctrl;
+
+ pdb_init_sam(&pwd);
+
+ if (!pdb_getsampwsid(pwd, sid)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ DEBUG(5, ("Attempting administrator password change for user %s\n",
+ pdb_get_username(pwd)));
+
+ acct_ctrl = pdb_get_acct_ctrl(pwd);
+
+ ZERO_STRUCT(plaintext_buf);
+
+ if (!decode_pw_buffer(pass, plaintext_buf, 256, &len, STR_UNICODE)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ if (!pdb_set_plaintext_passwd (pwd, plaintext_buf)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ /* if it's a trust account, don't update /etc/passwd */
+ if ( ( (acct_ctrl & ACB_DOMTRUST) == ACB_DOMTRUST ) ||
+ ( (acct_ctrl & ACB_WSTRUST) == ACB_WSTRUST) ||
+ ( (acct_ctrl & ACB_SVRTRUST) == ACB_SVRTRUST) ) {
+ DEBUG(5, ("Changing trust account or non-unix-user password, not updating /etc/passwd\n"));
+ } else {
+ /* update the UNIX password */
+ if (lp_unix_password_sync()) {
+ struct passwd *passwd = Get_Pwnam(pdb_get_username(pwd));
+ if (!passwd) {
+ DEBUG(1, ("chgpasswd: Username does not exist in system !?!\n"));
+ }
+
+ if(!chgpasswd(pdb_get_username(pwd), passwd, "", plaintext_buf, True)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+ }
+ }
+
+ ZERO_STRUCT(plaintext_buf);
+
+ DEBUG(5,("set_user_info_pw: pdb_update_pwd()\n"));
+
+ /* update the SAMBA password */
+ if(!pdb_update_sam_account(pwd)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ pdb_free_sam(&pwd);
+
+ return True;
+}
+
+/*******************************************************************
+ samr_reply_set_userinfo
+ ********************************************************************/
+
+NTSTATUS _samr_set_userinfo(pipes_struct *p, SAMR_Q_SET_USERINFO *q_u, SAMR_R_SET_USERINFO *r_u)
+{
+ DOM_SID sid;
+ POLICY_HND *pol = &q_u->pol;
+ uint16 switch_value = q_u->switch_value;
+ SAM_USERINFO_CTR *ctr = q_u->ctr;
+ uint32 acc_granted;
+ uint32 acc_required;
+ uint32 priv_list[3] = {SE_MACHINE_ACCOUNT, SE_ADD_USERS, SE_NONE};
+ BOOL priv_to_root = False;
+
+ DEBUG(5, ("_samr_set_userinfo: %d\n", __LINE__));
+
+ r_u->status = NT_STATUS_OK;
+
+ /* find the policy handle. open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, pol, &sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ acc_required = SA_RIGHT_USER_SET_LOC_COM | SA_RIGHT_USER_SET_ATTRIBUTES; /* This is probably wrong */
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, acc_required, priv_list, "_samr_set_userinfo"))) {
+ return r_u->status;
+ }
+
+ if (geteuid() != sec_initial_uid()) {
+ SAM_ACCOUNT *pwd = NULL;
+
+ pdb_init_sam(&pwd);
+
+ become_root();
+ if (!pdb_getsampwsid(pwd, &sid)) {
+ unbecome_root();
+ pdb_free_sam(&pwd);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (NT_STATUS_IS_OK(user_has_privilege(&(p->pipe_user), SE_ADD_USERS))) {
+ priv_to_root = True;
+
+ } else if (NT_STATUS_IS_OK(user_has_privilege(&(p->pipe_user), SE_MACHINE_ACCOUNT))) {
+ if (pdb_get_acct_ctrl(pwd) & ACB_WSTRUST) {
+ priv_to_root = True;
+ }
+ } else {
+ unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ DEBUG(5, ("_samr_set_userinfo: sid:%s, level:%d\n", sid_string_static(&sid), switch_value));
+
+ if (ctr == NULL) {
+ DEBUG(5, ("_samr_set_userinfo: NULL info level\n"));
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ /* ok! user info levels (lots: see MSDEV help), off we go... */
+ switch (switch_value) {
+ case 0x12:
+ if (!set_user_info_12(ctr->info.id12, &sid)) {
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ break;
+
+ case 24:
+ if (!p->session_key.length) {
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+ SamOEMhashBlob(ctr->info.id24->pass, 516, &p->session_key);
+
+ dump_data(100, (char *)ctr->info.id24->pass, 516);
+
+ if (!set_user_info_pw((char *)ctr->info.id24->pass, &sid)) {
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ break;
+
+ case 25:
+#if 0
+ /*
+ * Currently we don't really know how to unmarshall
+ * the level 25 struct, and the password encryption
+ * is different. This is a placeholder for when we
+ * do understand it. In the meantime just return INVALID
+ * info level and W2K SP2 drops down to level 23... JRA.
+ */
+
+ if (!p->session_key.length) {
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+ SamOEMhashBlob(ctr->info.id25->pass, 532, &p->session_key);
+
+ dump_data(100, (char *)ctr->info.id25->pass, 532);
+
+ if (!set_user_info_pw(ctr->info.id25->pass, &sid))
+ return NT_STATUS_ACCESS_DENIED;
+ break;
+#endif
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_INVALID_INFO_CLASS;
+
+ case 23:
+ if (!p->session_key.length) {
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+ SamOEMhashBlob(ctr->info.id23->pass, 516, &p->session_key);
+
+ dump_data(100, (char *)ctr->info.id23->pass, 516);
+
+ if (!set_user_info_23(ctr->info.id23, &sid)) {
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ break;
+
+ default:
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ if (priv_to_root) unbecome_root();
+ return r_u->status;
+}
+
+/*******************************************************************
+ samr_reply_set_userinfo2
+ ********************************************************************/
+
+NTSTATUS _samr_set_userinfo2(pipes_struct *p, SAMR_Q_SET_USERINFO2 *q_u, SAMR_R_SET_USERINFO2 *r_u)
+{
+ DOM_SID sid;
+ SAM_USERINFO_CTR *ctr = q_u->ctr;
+ POLICY_HND *pol = &q_u->pol;
+ uint16 switch_value = q_u->switch_value;
+ uint32 acc_granted;
+ uint32 acc_required;
+ uint32 priv_list[3] = {SE_MACHINE_ACCOUNT, SE_ADD_USERS, SE_NONE};
+ BOOL priv_to_root = False;
+
+ DEBUG(5, ("samr_reply_set_userinfo2: %d\n", __LINE__));
+
+ r_u->status = NT_STATUS_OK;
+
+ /* find the policy handle. open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, pol, &sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ acc_required = SA_RIGHT_USER_SET_LOC_COM | SA_RIGHT_USER_SET_ATTRIBUTES; /* This is probably wrong */
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, acc_required, priv_list, "_samr_set_userinfo2"))) {
+ return r_u->status;
+ }
+
+ if (geteuid() != sec_initial_uid()) {
+ SAM_ACCOUNT *pwd = NULL;
+
+ pdb_init_sam(&pwd);
+
+ become_root();
+ if (!pdb_getsampwsid(pwd, &sid)) {
+ unbecome_root();
+ pdb_free_sam(&pwd);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (NT_STATUS_IS_OK(user_has_privilege(&(p->pipe_user), SE_ADD_USERS))) {
+ priv_to_root = True;
+
+ } else if (NT_STATUS_IS_OK(user_has_privilege(&(p->pipe_user), SE_MACHINE_ACCOUNT))) {
+ if (pdb_get_acct_ctrl(pwd) & ACB_WSTRUST) {
+ priv_to_root = True;
+ }
+ } else {
+ unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ DEBUG(5, ("samr_reply_set_userinfo2: sid:%s\n", sid_string_static(&sid)));
+
+ if (ctr == NULL) {
+ DEBUG(5, ("samr_reply_set_userinfo2: NULL info level\n"));
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ switch_value=ctr->switch_value;
+
+ /* ok! user info levels (lots: see MSDEV help), off we go... */
+ switch (switch_value) {
+ case 21:
+ if (!set_user_info_21(ctr->info.id21, &sid)) {
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ break;
+ case 20:
+ if (!set_user_info_20(ctr->info.id20, &sid)) {
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ break;
+ case 16:
+ if (!set_user_info_10(ctr->info.id10, &sid)) {
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ break;
+ case 18:
+ /* Used by AS/U JRA. */
+ if (!set_user_info_12(ctr->info.id12, &sid)) {
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ break;
+ default:
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ if (priv_to_root) unbecome_root();
+ return r_u->status;
+}
+
+/*********************************************************************
+ _samr_query_aliasmem
+*********************************************************************/
+
+NTSTATUS _samr_query_useraliases(pipes_struct *p, SAMR_Q_QUERY_USERALIASES *q_u, SAMR_R_QUERY_USERALIASES *r_u)
+{
+ int num_groups = 0, tmp_num_groups=0;
+ uint32 *rids=NULL, *new_rids=NULL, *tmp_rids=NULL;
+ struct samr_info *info = NULL;
+ int i,j;
+
+ NTSTATUS ntstatus1;
+ NTSTATUS ntstatus2;
+
+ /* until i see a real useraliases query, we fack one up */
+
+ /* I have seen one, JFM 2/12/2001 */
+ /*
+ * Explanation of what this call does:
+ * for all the SID given in the request:
+ * return a list of alias (local groups)
+ * that have those SID as members.
+ *
+ * and that's the alias in the domain specified
+ * in the policy_handle
+ *
+ * if the policy handle is on an incorrect sid
+ * for example a user's sid
+ * we should reply NT_STATUS_OBJECT_TYPE_MISMATCH
+ */
+
+ r_u->status = NT_STATUS_OK;
+
+ DEBUG(5,("_samr_query_useraliases: %d\n", __LINE__));
+
+ /* find the policy handle. open a policy on it. */
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ ntstatus1 = access_check_samr_function(p, info->acc_granted, SA_RIGHT_DOMAIN_LOOKUP_ALIAS_BY_MEM, NULL, "_samr_query_useraliases");
+ ntstatus2 = access_check_samr_function(p, info->acc_granted, SA_RIGHT_DOMAIN_OPEN_ACCOUNT, NULL, "_samr_query_useraliases");
+
+ if (!NT_STATUS_IS_OK(ntstatus1) || !NT_STATUS_IS_OK(ntstatus2)) {
+ if (!(NT_STATUS_EQUAL(ntstatus1,NT_STATUS_ACCESS_DENIED) && NT_STATUS_IS_OK(ntstatus2)) &&
+ !(NT_STATUS_EQUAL(ntstatus1,NT_STATUS_ACCESS_DENIED) && NT_STATUS_IS_OK(ntstatus1))) {
+ return (NT_STATUS_IS_OK(ntstatus1)) ? ntstatus2 : ntstatus1;
+ }
+ }
+
+ if (!sid_check_is_domain(&info->sid) &&
+ !sid_check_is_builtin(&info->sid))
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+
+ for (i=0; i<q_u->num_sids1; i++) {
+
+ r_u->status=get_alias_user_groups(p->mem_ctx, &info->sid, &tmp_num_groups, &tmp_rids, &(q_u->sid[i].sid));
+
+ /*
+ * if there is an error, we just continue as
+ * it can be an unfound user or group
+ */
+ if (!NT_STATUS_IS_OK(r_u->status)) {
+ DEBUG(10,("_samr_query_useraliases: an error occured while getting groups\n"));
+ continue;
+ }
+
+ if (tmp_num_groups==0) {
+ DEBUG(10,("_samr_query_useraliases: no groups found\n"));
+ continue;
+ }
+
+ new_rids=(uint32 *)talloc_realloc(p->mem_ctx, rids, (num_groups+tmp_num_groups)*sizeof(uint32));
+ if (new_rids==NULL) {
+ DEBUG(0,("_samr_query_useraliases: could not realloc memory\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ rids=new_rids;
+
+ for (j=0; j<tmp_num_groups; j++)
+ rids[j+num_groups]=tmp_rids[j];
+
+ safe_free(tmp_rids);
+
+ num_groups+=tmp_num_groups;
+ }
+
+ init_samr_r_query_useraliases(r_u, num_groups, rids, NT_STATUS_OK);
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_query_aliasmem
+*********************************************************************/
+
+NTSTATUS _samr_query_aliasmem(pipes_struct *p, SAMR_Q_QUERY_ALIASMEM *q_u, SAMR_R_QUERY_ALIASMEM *r_u)
+{
+ int i;
+
+ int num_sids = 0;
+ DOM_SID2 *sid;
+ DOM_SID *sids=NULL;
+
+ DOM_SID alias_sid;
+
+ uint32 acc_granted;
+
+ /* find the policy handle. open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->alias_pol, &alias_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status =
+ access_check_samr_function(p, acc_granted, SA_RIGHT_ALIAS_GET_MEMBERS, NULL, "_samr_query_aliasmem"))) {
+ return r_u->status;
+ }
+
+ DEBUG(10, ("sid is %s\n", sid_string_static(&alias_sid)));
+
+ if (!pdb_enum_aliasmem(&alias_sid, &sids, &num_sids))
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ sid = (DOM_SID2 *)talloc_zero(p->mem_ctx, sizeof(DOM_SID2) * num_sids);
+ if (num_sids!=0 && sid == NULL) {
+ SAFE_FREE(sids);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < num_sids; i++) {
+ init_dom_sid2(&sid[i], &sids[i]);
+ }
+
+ init_samr_r_query_aliasmem(r_u, num_sids, sid, NT_STATUS_OK);
+
+ SAFE_FREE(sids);
+
+ return NT_STATUS_OK;
+}
+
+static void add_uid_to_array_unique(uid_t uid, uid_t **uids, int *num)
+{
+ int i;
+
+ if ((*num) >= groups_max())
+ return;
+
+ for (i=0; i<*num; i++) {
+ if ((*uids)[i] == uid)
+ return;
+ }
+
+ *uids = Realloc(*uids, (*num+1) * sizeof(uid_t));
+
+ if (*uids == NULL)
+ return;
+
+ (*uids)[*num] = uid;
+ *num += 1;
+}
+
+
+static BOOL get_memberuids(gid_t gid, uid_t **uids, int *num)
+{
+ struct group *grp;
+ char **gr;
+ struct sys_pwent *userlist, *user;
+
+ *uids = NULL;
+ *num = 0;
+
+ /* We only look at our own sam, so don't care about imported stuff */
+
+ winbind_off();
+
+ if ((grp = getgrgid(gid)) == NULL) {
+ winbind_on();
+ return False;
+ }
+
+ /* Primary group members */
+
+ userlist = getpwent_list();
+
+ for (user = userlist; user != NULL; user = user->next) {
+ if (user->pw_gid != gid)
+ continue;
+ add_uid_to_array_unique(user->pw_uid, uids, num);
+ }
+
+ pwent_free(userlist);
+
+ /* Secondary group members */
+
+ gr = grp->gr_mem;
+ while ((*gr != NULL) && ((*gr)[0] != '\0')) {
+ struct passwd *pw = getpwnam(*gr);
+
+ if (pw == NULL)
+ continue;
+
+ add_uid_to_array_unique(pw->pw_uid, uids, num);
+
+ gr += 1;
+ }
+
+ winbind_on();
+
+ return True;
+}
+
+/*********************************************************************
+ _samr_query_groupmem
+*********************************************************************/
+
+NTSTATUS _samr_query_groupmem(pipes_struct *p, SAMR_Q_QUERY_GROUPMEM *q_u, SAMR_R_QUERY_GROUPMEM *r_u)
+{
+ int final_num_rids, i;
+ DOM_SID group_sid;
+ fstring group_sid_str;
+ uid_t *uids;
+ int num;
+ gid_t gid;
+
+ uint32 *rid=NULL;
+ uint32 *attr=NULL;
+
+ uint32 acc_granted;
+
+ /* find the policy handle. open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->group_pol, &group_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_GROUP_GET_MEMBERS, NULL, "_samr_query_groupmem"))) {
+ return r_u->status;
+ }
+
+ sid_to_string(group_sid_str, &group_sid);
+ DEBUG(10, ("sid is %s\n", group_sid_str));
+
+ if (!sid_check_is_in_our_domain(&group_sid)) {
+ DEBUG(3, ("sid %s is not in our domain\n", group_sid_str));
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ DEBUG(10, ("lookup on Domain SID\n"));
+
+ if (!NT_STATUS_IS_OK(sid_to_gid(&group_sid, &gid)))
+ return NT_STATUS_NO_SUCH_GROUP;
+
+ if(!get_memberuids(gid, &uids, &num))
+ return NT_STATUS_NO_SUCH_GROUP;
+
+ rid=talloc_zero(p->mem_ctx, sizeof(uint32)*num);
+ attr=talloc_zero(p->mem_ctx, sizeof(uint32)*num);
+
+ if (num!=0 && (rid==NULL || attr==NULL))
+ return NT_STATUS_NO_MEMORY;
+
+ final_num_rids = 0;
+
+ for (i=0; i<num; i++) {
+ DOM_SID sid;
+
+ if (!NT_STATUS_IS_OK(uid_to_sid(&sid, uids[i]))) {
+ DEBUG(1, ("Could not map member uid to SID\n"));
+ continue;
+ }
+
+ if (!sid_check_is_in_our_domain(&sid)) {
+ DEBUG(1, ("Inconsistent SAM -- group member uid not "
+ "in our domain\n"));
+ continue;
+ }
+
+ sid_peek_rid(&sid, &rid[final_num_rids]);
+
+ /* Hmm. In a trace I got the constant 7 here from NT. */
+ attr[final_num_rids] = SID_NAME_USER;
+
+ final_num_rids += 1;
+ }
+
+ SAFE_FREE(uids);
+
+ init_samr_r_query_groupmem(r_u, final_num_rids, rid, attr,
+ NT_STATUS_OK);
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_add_aliasmem
+*********************************************************************/
+
+NTSTATUS _samr_add_aliasmem(pipes_struct *p, SAMR_Q_ADD_ALIASMEM *q_u, SAMR_R_ADD_ALIASMEM *r_u)
+{
+ DOM_SID alias_sid;
+ uint32 acc_granted;
+
+ /* Find the policy handle. Open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->alias_pol, &alias_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_ALIAS_ADD_MEMBER, NULL, "_samr_add_aliasmem"))) {
+ return r_u->status;
+ }
+
+ DEBUG(10, ("sid is %s\n", sid_string_static(&alias_sid)));
+
+ if (!pdb_add_aliasmem(&alias_sid, &q_u->sid.sid))
+ return NT_STATUS_ACCESS_DENIED;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_del_aliasmem
+*********************************************************************/
+
+NTSTATUS _samr_del_aliasmem(pipes_struct *p, SAMR_Q_DEL_ALIASMEM *q_u, SAMR_R_DEL_ALIASMEM *r_u)
+{
+ DOM_SID alias_sid;
+ uint32 acc_granted;
+
+ /* Find the policy handle. Open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->alias_pol, &alias_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_ALIAS_REMOVE_MEMBER, NULL, "_samr_del_aliasmem"))) {
+ return r_u->status;
+ }
+
+ DEBUG(10, ("_samr_del_aliasmem:sid is %s\n",
+ sid_string_static(&alias_sid)));
+
+ if (!pdb_del_aliasmem(&alias_sid, &q_u->sid.sid))
+ return NT_STATUS_ACCESS_DENIED;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_add_groupmem
+*********************************************************************/
+
+NTSTATUS _samr_add_groupmem(pipes_struct *p, SAMR_Q_ADD_GROUPMEM *q_u, SAMR_R_ADD_GROUPMEM *r_u)
+{
+ DOM_SID group_sid;
+ DOM_SID user_sid;
+ fstring grp_name;
+ fstring usr_name;
+ uint32 acc_granted;
+
+ /* Find the policy handle. Open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->pol, &group_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_GROUP_ADD_MEMBER, NULL, "_samr_add_groupmem"))) {
+ return r_u->status;
+ }
+
+ if (!sid_to_local_dom_grp_name(&group_sid, grp_name)) {
+ DEBUG(1, ("Could not find group for SID %s\n",
+ sid_string_static(&group_sid)));
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ sid_copy(&user_sid, get_global_sam_sid());
+ sid_append_rid(&user_sid, q_u->rid);
+
+ if (!sid_to_local_user_name(&user_sid, usr_name)) {
+ DEBUG(1, ("Could not find user for SID %s\n",
+ sid_string_static(&user_sid)));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ /* if the user is already in the group */
+ if(user_in_unix_group_list(usr_name, grp_name)) {
+ return NT_STATUS_MEMBER_IN_GROUP;
+ }
+
+ /*
+ * ok, the group exist, the user exist, the user is not in the group,
+ *
+ * we can (finally) add it to the group !
+ */
+
+ smb_add_user_group(grp_name, usr_name);
+
+ /* check if the user has been added then ... */
+ if(!user_in_unix_group_list(usr_name, grp_name)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_del_groupmem
+*********************************************************************/
+
+NTSTATUS _samr_del_groupmem(pipes_struct *p, SAMR_Q_DEL_GROUPMEM *q_u, SAMR_R_DEL_GROUPMEM *r_u)
+{
+ DOM_SID group_sid;
+ DOM_SID user_sid;
+ fstring grp_name;
+ fstring usr_name;
+ uint32 acc_granted;
+
+ /*
+ * delete the group member named q_u->rid
+ * who is a member of the sid associated with the handle
+ * the rid is a user's rid as the group is a domain group.
+ */
+
+ /* Find the policy handle. Open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->pol, &group_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_GROUP_REMOVE_MEMBER, NULL, "_samr_del_groupmem"))) {
+ return r_u->status;
+ }
+
+ if (!sid_to_local_dom_grp_name(&group_sid, grp_name)) {
+ DEBUG(1, ("Could not find group for SID %s\n",
+ sid_string_static(&group_sid)));
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ sid_copy(&user_sid, get_global_sam_sid());
+ sid_append_rid(&user_sid, q_u->rid);
+
+ if (!sid_to_local_user_name(&user_sid, usr_name)) {
+ DEBUG(1, ("Could not find user for SID %s\n",
+ sid_string_static(&user_sid)));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ /* if the user is not in the group */
+ if (!user_in_unix_group_list(usr_name, grp_name)) {
+ return NT_STATUS_MEMBER_NOT_IN_GROUP;
+ }
+
+ smb_delete_user_group(grp_name, usr_name);
+
+ /* check if the user has been removed then ... */
+ if(user_in_unix_group_list(usr_name, grp_name)) {
+ /* don't know what to reply else */
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+
+}
+
+/****************************************************************************
+ Delete a UNIX user on demand.
+****************************************************************************/
+
+static int smb_delete_user(const char *unix_user)
+{
+ pstring del_script;
+ int ret;
+
+ /* try winbindd first since it is impossible to determine where
+ a user came from via NSS. Try the delete user script if this fails
+ meaning the user did not exist in winbindd's list of accounts */
+
+ if ( winbind_delete_user( unix_user ) ) {
+ DEBUG(3,("winbind_delete_user: removed user (%s)\n", unix_user));
+ return 0;
+ }
+
+
+ /* fall back to 'delete user script' */
+
+ pstrcpy(del_script, lp_deluser_script());
+ if (! *del_script)
+ return -1;
+ all_string_sub(del_script, "%u", unix_user, sizeof(pstring));
+ ret = smbrun(del_script,NULL);
+ DEBUG(3,("smb_delete_user: Running the command `%s' gave %d\n",del_script,ret));
+
+ return ret;
+}
+
+/*********************************************************************
+ _samr_delete_dom_user
+*********************************************************************/
+
+NTSTATUS _samr_delete_dom_user(pipes_struct *p, SAMR_Q_DELETE_DOM_USER *q_u, SAMR_R_DELETE_DOM_USER *r_u )
+{
+ DOM_SID user_sid;
+ SAM_ACCOUNT *sam_pass=NULL;
+ uint32 acc_granted;
+
+ DEBUG(5, ("_samr_delete_dom_user: %d\n", __LINE__));
+
+ /* Find the policy handle. Open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->user_pol, &user_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, STD_RIGHT_DELETE_ACCESS, NULL, "_samr_delete_dom_user"))) {
+ return r_u->status;
+ }
+
+ if (!sid_check_is_in_our_domain(&user_sid))
+ return NT_STATUS_CANNOT_DELETE;
+
+ /* check if the user exists before trying to delete */
+ pdb_init_sam(&sam_pass);
+ if(!pdb_getsampwsid(sam_pass, &user_sid)) {
+ DEBUG(5,("_samr_delete_dom_user:User %s doesn't exist.\n",
+ sid_string_static(&user_sid)));
+ pdb_free_sam(&sam_pass);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ /* delete the unix side */
+ /*
+ * note: we don't check if the delete really happened
+ * as the script is not necessary present
+ * and maybe the sysadmin doesn't want to delete the unix side
+ */
+ smb_delete_user(pdb_get_username(sam_pass));
+
+ /* and delete the samba side */
+ if (!pdb_delete_sam_account(sam_pass)) {
+ DEBUG(5,("_samr_delete_dom_user:Failed to delete entry for user %s.\n", pdb_get_username(sam_pass)));
+ pdb_free_sam(&sam_pass);
+ return NT_STATUS_CANNOT_DELETE;
+ }
+
+ pdb_free_sam(&sam_pass);
+
+ if (!close_policy_hnd(p, &q_u->user_pol))
+ return NT_STATUS_OBJECT_NAME_INVALID;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_delete_dom_group
+*********************************************************************/
+
+NTSTATUS _samr_delete_dom_group(pipes_struct *p, SAMR_Q_DELETE_DOM_GROUP *q_u, SAMR_R_DELETE_DOM_GROUP *r_u)
+{
+ DOM_SID group_sid;
+ fstring grp_name;
+ struct group *grp;
+ uint32 acc_granted;
+
+ DEBUG(5, ("samr_delete_dom_group: %d\n", __LINE__));
+
+ /* Find the policy handle. Open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->group_pol, &group_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, STD_RIGHT_DELETE_ACCESS, NULL, "_samr_delete_dom_group"))) {
+ return r_u->status;
+ }
+
+ if (!sid_to_local_dom_grp_name(&group_sid, grp_name)) {
+ DEBUG(1, ("Could not find group for SID %s\n",
+ sid_string_static(&group_sid)));
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ /* we can delete the UNIX group */
+ smb_delete_group(grp_name);
+
+ /* check if the group has been successfully deleted */
+ if ( (grp=getgrnam(grp_name)) != NULL)
+ return NT_STATUS_ACCESS_DENIED;
+
+ if (!close_policy_hnd(p, &q_u->group_pol))
+ return NT_STATUS_OBJECT_NAME_INVALID;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_delete_dom_alias
+*********************************************************************/
+
+NTSTATUS _samr_delete_dom_alias(pipes_struct *p, SAMR_Q_DELETE_DOM_ALIAS *q_u, SAMR_R_DELETE_DOM_ALIAS *r_u)
+{
+ DOM_SID alias_sid;
+ uint32 acc_granted;
+
+ DEBUG(5, ("_samr_delete_dom_alias: %d\n", __LINE__));
+
+ /* Find the policy handle. Open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->alias_pol, &alias_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, STD_RIGHT_DELETE_ACCESS, NULL, "_samr_delete_dom_alias"))) {
+ return r_u->status;
+ }
+
+ DEBUG(10, ("sid is %s\n", sid_string_static(&alias_sid)));
+
+ if (!sid_check_is_in_our_domain(&alias_sid))
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ DEBUG(10, ("lookup on Local SID\n"));
+
+ /* Have passdb delete the alias */
+ if (!pdb_delete_alias(&alias_sid))
+ return NT_STATUS_ACCESS_DENIED;
+
+ if (!close_policy_hnd(p, &q_u->alias_pol))
+ return NT_STATUS_OBJECT_NAME_INVALID;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_create_dom_group
+*********************************************************************/
+
+NTSTATUS _samr_create_dom_group(pipes_struct *p, SAMR_Q_CREATE_DOM_GROUP *q_u, SAMR_R_CREATE_DOM_GROUP *r_u)
+{
+ DOM_SID dom_sid;
+ DOM_SID info_sid;
+ fstring name;
+ fstring sid_string;
+ struct group *grp;
+ struct samr_info *info;
+ uint32 acc_granted;
+ gid_t gid;
+
+ /* Find the policy handle. Open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->pol, &dom_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_DOMAIN_CREATE_GROUP, NULL, "_samr_create_dom_group"))) {
+ return r_u->status;
+ }
+
+ if (!sid_equal(&dom_sid, get_global_sam_sid()))
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* TODO: check if allowed to create group and add a become_root/unbecome_root pair.*/
+
+ unistr2_to_ascii(name, &q_u->uni_acct_desc, sizeof(name)-1);
+
+ /* check if group already exist */
+ if ((grp=getgrnam(name)) != NULL)
+ return NT_STATUS_GROUP_EXISTS;
+
+ /* we can create the UNIX group */
+ if (smb_create_group(name, &gid) != 0)
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* check if the group has been successfully created */
+ if ((grp=getgrgid(gid)) == NULL)
+ return NT_STATUS_ACCESS_DENIED;
+
+ r_u->rid=pdb_gid_to_group_rid(grp->gr_gid);
+
+ /* add the group to the mapping table */
+ sid_copy(&info_sid, get_global_sam_sid());
+ sid_append_rid(&info_sid, r_u->rid);
+ sid_to_string(sid_string, &info_sid);
+
+ if(!add_initial_entry(grp->gr_gid, sid_string, SID_NAME_DOM_GRP, name, NULL))
+ return NT_STATUS_ACCESS_DENIED;
+
+ if ((info = get_samr_info_by_sid(&info_sid)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, &r_u->pol, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_create_dom_alias
+*********************************************************************/
+
+NTSTATUS _samr_create_dom_alias(pipes_struct *p, SAMR_Q_CREATE_DOM_ALIAS *q_u, SAMR_R_CREATE_DOM_ALIAS *r_u)
+{
+ DOM_SID dom_sid;
+ DOM_SID info_sid;
+ fstring name;
+ struct group *grp;
+ struct samr_info *info;
+ uint32 acc_granted;
+ gid_t gid;
+
+ /* Find the policy handle. Open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->dom_pol, &dom_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_DOMAIN_CREATE_ALIAS, NULL, "_samr_create_alias"))) {
+ return r_u->status;
+ }
+
+ if (!sid_equal(&dom_sid, get_global_sam_sid()))
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* TODO: check if allowed to create group and add a become_root/unbecome_root pair.*/
+
+ unistr2_to_ascii(name, &q_u->uni_acct_desc, sizeof(name)-1);
+
+ /* Have passdb create the alias */
+ if (!pdb_create_alias(name, &r_u->rid))
+ return NT_STATUS_ACCESS_DENIED;
+
+ sid_copy(&info_sid, get_global_sam_sid());
+ sid_append_rid(&info_sid, r_u->rid);
+
+ if (!NT_STATUS_IS_OK(sid_to_gid(&info_sid, &gid)))
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* check if the group has been successfully created */
+ if ((grp=getgrgid(gid)) == NULL)
+ return NT_STATUS_ACCESS_DENIED;
+
+ if ((info = get_samr_info_by_sid(&info_sid)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, &r_u->alias_pol, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_query_groupinfo
+
+sends the name/comment pair of a domain group
+level 1 send also the number of users of that group
+*********************************************************************/
+
+NTSTATUS _samr_query_groupinfo(pipes_struct *p, SAMR_Q_QUERY_GROUPINFO *q_u, SAMR_R_QUERY_GROUPINFO *r_u)
+{
+ DOM_SID group_sid;
+ gid_t gid;
+ uid_t *uids;
+ int num=0;
+ GROUP_INFO_CTR *ctr;
+ uint32 acc_granted;
+ struct acct_info info;
+
+ if (!get_lsa_policy_samr_sid(p, &q_u->pol, &group_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_GROUP_LOOKUP_INFO, NULL, "_samr_query_groupinfo"))) {
+ return r_u->status;
+ }
+
+ if (!pdb_get_dom_grp_info(&group_sid, &info))
+ return NT_STATUS_NO_SUCH_GROUP;
+
+ if (!NT_STATUS_IS_OK(sid_to_gid(&group_sid, &gid)))
+ return NT_STATUS_INVALID_HANDLE;
+
+ ctr=(GROUP_INFO_CTR *)talloc_zero(p->mem_ctx, sizeof(GROUP_INFO_CTR));
+ if (ctr==NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ switch (q_u->switch_level) {
+ case 1:
+ ctr->switch_value1 = 1;
+ if(!get_memberuids(gid, &uids, &num))
+ return NT_STATUS_NO_SUCH_GROUP;
+ SAFE_FREE(uids);
+ init_samr_group_info1(&ctr->group.info1,
+ info.acct_name, info.acct_desc,
+ num);
+ break;
+ case 3:
+ ctr->switch_value1 = 3;
+ init_samr_group_info3(&ctr->group.info3);
+ break;
+ case 4:
+ ctr->switch_value1 = 4;
+ init_samr_group_info4(&ctr->group.info4,
+ info.acct_desc);
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ init_samr_r_query_groupinfo(r_u, ctr, NT_STATUS_OK);
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_set_groupinfo
+
+ update a domain group's comment.
+*********************************************************************/
+
+NTSTATUS _samr_set_groupinfo(pipes_struct *p, SAMR_Q_SET_GROUPINFO *q_u, SAMR_R_SET_GROUPINFO *r_u)
+{
+ DOM_SID group_sid;
+ GROUP_INFO_CTR *ctr;
+ uint32 acc_granted;
+ struct acct_info info;
+
+ if (!get_lsa_policy_samr_sid(p, &q_u->pol, &group_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_GROUP_SET_INFO, NULL, "_samr_set_groupinfo"))) {
+ return r_u->status;
+ }
+
+ if (!pdb_get_dom_grp_info(&group_sid, &info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ ctr=q_u->ctr;
+
+ switch (ctr->switch_value1) {
+ case 1:
+ unistr2_to_ascii(info.acct_desc,
+ &(ctr->group.info1.uni_acct_desc),
+ sizeof(info.acct_desc)-1);
+ break;
+ case 4:
+ unistr2_to_ascii(info.acct_desc,
+ &(ctr->group.info4.uni_acct_desc),
+ sizeof(info.acct_desc)-1);
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ if (!pdb_set_dom_grp_info(&group_sid, &info))
+ return NT_STATUS_ACCESS_DENIED;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_set_aliasinfo
+
+ update an alias's comment.
+*********************************************************************/
+
+NTSTATUS _samr_set_aliasinfo(pipes_struct *p, SAMR_Q_SET_ALIASINFO *q_u, SAMR_R_SET_ALIASINFO *r_u)
+{
+ DOM_SID group_sid;
+ struct acct_info info;
+ ALIAS_INFO_CTR *ctr;
+ uint32 acc_granted;
+
+ if (!get_lsa_policy_samr_sid(p, &q_u->alias_pol, &group_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_ALIAS_SET_INFO, NULL, "_samr_set_aliasinfo"))) {
+ return r_u->status;
+ }
+
+ ctr=&q_u->ctr;
+
+ switch (ctr->switch_value1) {
+ case 3:
+ unistr2_to_ascii(info.acct_desc,
+ &(ctr->alias.info3.uni_acct_desc),
+ sizeof(info.acct_desc)-1);
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ if(!pdb_set_aliasinfo(&group_sid, &info)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_get_dom_pwinfo
+*********************************************************************/
+
+NTSTATUS _samr_get_dom_pwinfo(pipes_struct *p, SAMR_Q_GET_DOM_PWINFO *q_u, SAMR_R_GET_DOM_PWINFO *r_u)
+{
+ /* Perform access check. Since this rpc does not require a
+ policy handle it will not be caught by the access checks on
+ SAMR_CONNECT or SAMR_CONNECT_ANON. */
+
+ if (!pipe_access_check(p)) {
+ DEBUG(3, ("access denied to samr_get_dom_pwinfo\n"));
+ r_u->status = NT_STATUS_ACCESS_DENIED;
+ return r_u->status;
+ }
+
+ /* Actually, returning zeros here works quite well :-). */
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_open_group
+*********************************************************************/
+
+NTSTATUS _samr_open_group(pipes_struct *p, SAMR_Q_OPEN_GROUP *q_u, SAMR_R_OPEN_GROUP *r_u)
+{
+ DOM_SID sid;
+ DOM_SID info_sid;
+ fstring grp_name;
+ struct samr_info *info;
+ SEC_DESC *psd = NULL;
+ uint32 acc_granted;
+ uint32 des_access = q_u->access_mask;
+ size_t sd_size;
+ NTSTATUS status;
+ fstring sid_string;
+
+ if (!get_lsa_policy_samr_sid(p, &q_u->domain_pol, &sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(status = access_check_samr_function(p, acc_granted, SA_RIGHT_DOMAIN_OPEN_ACCOUNT, NULL, "_samr_open_group"))) {
+ return status;
+ }
+
+ /*check if access can be granted as requested by client. */
+ samr_make_grp_obj_sd(p->mem_ctx, &psd, &sd_size);
+ se_map_generic(&des_access,&grp_generic_mapping);
+ if (!NT_STATUS_IS_OK(status =
+ access_check_samr_object(psd, p, des_access, &acc_granted,
+ NULL, "_samr_open_group"))) {
+ return status;
+ }
+
+
+ /* this should not be hard-coded like this */
+ if (!sid_equal(&sid, get_global_sam_sid()))
+ return NT_STATUS_ACCESS_DENIED;
+
+ sid_copy(&info_sid, get_global_sam_sid());
+ sid_append_rid(&info_sid, q_u->rid_group);
+ sid_to_string(sid_string, &info_sid);
+
+ if ((info = get_samr_info_by_sid(&info_sid)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ info->acc_granted = acc_granted;
+
+ DEBUG(10, ("_samr_open_group:Opening SID: %s\n", sid_string));
+
+ if (!sid_to_local_dom_grp_name(&info->sid, grp_name))
+ return NT_STATUS_NO_SUCH_GROUP;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, &r_u->pol, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_remove_sid_foreign_domain
+*********************************************************************/
+
+NTSTATUS _samr_remove_sid_foreign_domain(pipes_struct *p,
+ SAMR_Q_REMOVE_SID_FOREIGN_DOMAIN *q_u,
+ SAMR_R_REMOVE_SID_FOREIGN_DOMAIN *r_u)
+{
+ DOM_SID delete_sid, alias_sid;
+ SAM_ACCOUNT *sam_pass=NULL;
+ uint32 acc_granted;
+ GROUP_MAP map;
+ BOOL is_user = False;
+ NTSTATUS result;
+ enum SID_NAME_USE type = SID_NAME_UNKNOWN;
+
+ sid_copy( &delete_sid, &q_u->sid.sid );
+
+ DEBUG(5,("_samr_remove_sid_foreign_domain: removing SID [%s]\n",
+ sid_string_static(&delete_sid)));
+
+ /* Find the policy handle. Open a policy on it. */
+
+ if (!get_lsa_policy_samr_sid(p, &q_u->dom_pol, &alias_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ result = access_check_samr_function(p, acc_granted, STD_RIGHT_DELETE_ACCESS,
+ NULL, "_samr_remove_sid_foreign_domain");
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ DEBUG(8, ("_samr_remove_sid_foreign_domain:sid is %s\n",
+ sid_string_static(&alias_sid)));
+
+ /* make sure we can handle this */
+
+ if ( sid_check_is_domain(&alias_sid) )
+ type = SID_NAME_DOM_GRP;
+ else if ( sid_check_is_builtin(&alias_sid) )
+ type = SID_NAME_ALIAS;
+
+ if ( type == SID_NAME_UNKNOWN ) {
+ DEBUG(10, ("_samr_remove_sid_foreign_domain: can't operate on what we don't own!\n"));
+ return NT_STATUS_OK;
+ }
+
+ /* check if the user exists before trying to delete */
+
+ pdb_init_sam(&sam_pass);
+
+ if ( pdb_getsampwsid(sam_pass, &delete_sid) ) {
+ is_user = True;
+ } else {
+ /* maybe it is a group */
+ if( !pdb_getgrsid(&map, delete_sid) ) {
+ DEBUG(3,("_samr_remove_sid_foreign_domain: %s is not a user or a group!\n",
+ sid_string_static(&delete_sid)));
+ result = NT_STATUS_INVALID_SID;
+ goto done;
+ }
+ }
+
+ /* we can only delete a user from a group since we don't have
+ nested groups anyways. So in the latter case, just say OK */
+
+ if ( is_user ) {
+ GROUP_MAP *mappings = NULL;
+ int num_groups, i;
+ struct group *grp2;
+
+ if ( pdb_enum_group_mapping(type, &mappings, &num_groups, False) && num_groups>0 ) {
+
+ /* interate over the groups */
+ for ( i=0; i<num_groups; i++ ) {
+
+ grp2 = getgrgid(mappings[i].gid);
+
+ if ( !grp2 ) {
+ DEBUG(0,("_samr_remove_sid_foreign_domain: group mapping without UNIX group!\n"));
+ continue;
+ }
+
+ if ( !user_in_unix_group_list(pdb_get_username(sam_pass), grp2->gr_name) )
+ continue;
+
+ smb_delete_user_group(grp2->gr_name, pdb_get_username(sam_pass));
+
+ if ( user_in_unix_group_list(pdb_get_username(sam_pass), grp2->gr_name) ) {
+ /* should we fail here ? */
+ DEBUG(0,("_samr_remove_sid_foreign_domain: Delete user [%s] from group [%s] failed!\n",
+ pdb_get_username(sam_pass), grp2->gr_name ));
+ continue;
+ }
+
+ DEBUG(10,("_samr_remove_sid_foreign_domain: Removed user [%s] from group [%s]!\n",
+ pdb_get_username(sam_pass), grp2->gr_name ));
+ }
+
+ SAFE_FREE(mappings);
+ }
+ }
+
+ result = NT_STATUS_OK;
+done:
+
+ pdb_free_sam(&sam_pass);
+
+ return result;
+}
+
+/*******************************************************************
+ _samr_unknown_2e
+ ********************************************************************/
+
+NTSTATUS _samr_unknown_2e(pipes_struct *p, SAMR_Q_UNKNOWN_2E *q_u, SAMR_R_UNKNOWN_2E *r_u)
+{
+ struct samr_info *info = NULL;
+ SAM_UNK_CTR *ctr;
+ uint32 min_pass_len,pass_hist,flag;
+ time_t u_expire, u_min_age;
+ NTTIME nt_expire, nt_min_age;
+
+ time_t u_lock_duration, u_reset_time;
+ NTTIME nt_lock_duration, nt_reset_time;
+ uint32 lockout;
+
+ time_t u_logout;
+ NTTIME nt_logout;
+
+ uint32 num_users=0, num_groups=0, num_aliases=0;
+
+ uint32 account_policy_temp;
+
+ if ((ctr = (SAM_UNK_CTR *)talloc_zero(p->mem_ctx, sizeof(SAM_UNK_CTR))) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(ctr);
+
+ r_u->status = NT_STATUS_OK;
+
+ DEBUG(5,("_samr_unknown_2e: %d\n", __LINE__));
+
+ /* find the policy handle. open a policy on it. */
+ if (!find_policy_by_hnd(p, &q_u->domain_pol, (void **)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ switch (q_u->switch_value) {
+ case 0x01:
+ account_policy_get(AP_MIN_PASSWORD_LEN, &account_policy_temp);
+ min_pass_len = account_policy_temp;
+
+ account_policy_get(AP_PASSWORD_HISTORY, &account_policy_temp);
+ pass_hist = account_policy_temp;
+
+ account_policy_get(AP_USER_MUST_LOGON_TO_CHG_PASS, &account_policy_temp);
+ flag = account_policy_temp;
+
+ account_policy_get(AP_MAX_PASSWORD_AGE, &account_policy_temp);
+ u_expire = account_policy_temp;
+
+ account_policy_get(AP_MIN_PASSWORD_AGE, &account_policy_temp);
+ u_min_age = account_policy_temp;
+
+ unix_to_nt_time_abs(&nt_expire, u_expire);
+ unix_to_nt_time_abs(&nt_min_age, u_min_age);
+
+ init_unk_info1(&ctr->info.inf1, (uint16)min_pass_len, (uint16)pass_hist,
+ flag, nt_expire, nt_min_age);
+ break;
+ case 0x02:
+ become_root();
+ r_u->status=load_sampwd_entries(info, ACB_NORMAL, False);
+ unbecome_root();
+ if (!NT_STATUS_IS_OK(r_u->status)) {
+ DEBUG(5, ("_samr_unknown_2e: load_sampwd_entries failed\n"));
+ return r_u->status;
+ }
+ num_users=info->disp_info.num_user_account;
+ free_samr_db(info);
+
+ r_u->status=load_group_domain_entries(info, get_global_sam_sid());
+ if (NT_STATUS_IS_ERR(r_u->status)) {
+ DEBUG(5, ("_samr_unknown_2e: load_group_domain_entries failed\n"));
+ return r_u->status;
+ }
+ num_groups=info->disp_info.num_group_account;
+ free_samr_db(info);
+
+ /* The time call below is to get a sequence number for the sam. FIXME !!! JRA. */
+ init_unk_info2(&ctr->info.inf2, lp_workgroup(), global_myname(), (uint32) time(NULL),
+ num_users, num_groups, num_aliases);
+ break;
+ case 0x03:
+ account_policy_get(AP_TIME_TO_LOGOUT, &account_policy_temp);
+ u_logout = account_policy_temp;
+
+ unix_to_nt_time_abs(&nt_logout, u_logout);
+
+ init_unk_info3(&ctr->info.inf3, nt_logout);
+ break;
+ case 0x05:
+ init_unk_info5(&ctr->info.inf5, global_myname());
+ break;
+ case 0x06:
+ init_unk_info6(&ctr->info.inf6);
+ break;
+ case 0x07:
+ init_unk_info7(&ctr->info.inf7);
+ break;
+ case 0x0c:
+ account_policy_get(AP_LOCK_ACCOUNT_DURATION, &account_policy_temp);
+ u_lock_duration = account_policy_temp * 60;
+
+ account_policy_get(AP_RESET_COUNT_TIME, &account_policy_temp);
+ u_reset_time = account_policy_temp * 60;
+
+ account_policy_get(AP_BAD_ATTEMPT_LOCKOUT, &account_policy_temp);
+ lockout = account_policy_temp;
+
+ unix_to_nt_time_abs(&nt_lock_duration, u_lock_duration);
+ unix_to_nt_time_abs(&nt_reset_time, u_reset_time);
+
+ init_unk_info12(&ctr->info.inf12, nt_lock_duration, nt_reset_time, (uint16)lockout);
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ init_samr_r_samr_unknown_2e(r_u, q_u->switch_value, ctr, NT_STATUS_OK);
+
+ DEBUG(5,("_samr_unknown_2e: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ _samr_
+ ********************************************************************/
+
+NTSTATUS _samr_set_dom_info(pipes_struct *p, SAMR_Q_SET_DOMAIN_INFO *q_u, SAMR_R_SET_DOMAIN_INFO *r_u)
+{
+ time_t u_expire, u_min_age;
+ time_t u_logout;
+ time_t u_lock_duration, u_reset_time;
+
+ r_u->status = NT_STATUS_OK;
+
+ DEBUG(5,("_samr_set_dom_info: %d\n", __LINE__));
+
+ /* find the policy handle. open a policy on it. */
+ if (!find_policy_by_hnd(p, &q_u->domain_pol, NULL))
+ return NT_STATUS_INVALID_HANDLE;
+
+ DEBUG(5,("_samr_set_dom_info: switch_value: %d\n", q_u->switch_value));
+
+ switch (q_u->switch_value) {
+ case 0x01:
+ u_expire=nt_time_to_unix_abs(&q_u->ctr->info.inf1.expire);
+ u_min_age=nt_time_to_unix_abs(&q_u->ctr->info.inf1.min_passwordage);
+
+ account_policy_set(AP_MIN_PASSWORD_LEN, (uint32)q_u->ctr->info.inf1.min_length_password);
+ account_policy_set(AP_PASSWORD_HISTORY, (uint32)q_u->ctr->info.inf1.password_history);
+ account_policy_set(AP_USER_MUST_LOGON_TO_CHG_PASS, (uint32)q_u->ctr->info.inf1.flag);
+ account_policy_set(AP_MAX_PASSWORD_AGE, (int)u_expire);
+ account_policy_set(AP_MIN_PASSWORD_AGE, (int)u_min_age);
+ break;
+ case 0x02:
+ break;
+ case 0x03:
+ u_logout=nt_time_to_unix_abs(&q_u->ctr->info.inf3.logout);
+ account_policy_set(AP_TIME_TO_LOGOUT, (int)u_logout);
+ break;
+ case 0x05:
+ break;
+ case 0x06:
+ break;
+ case 0x07:
+ break;
+ case 0x0c:
+ u_lock_duration=nt_time_to_unix_abs(&q_u->ctr->info.inf12.duration)/60;
+ u_reset_time=nt_time_to_unix_abs(&q_u->ctr->info.inf12.reset_count)/60;
+
+ account_policy_set(AP_LOCK_ACCOUNT_DURATION, (int)u_lock_duration);
+ account_policy_set(AP_RESET_COUNT_TIME, (int)u_reset_time);
+ account_policy_set(AP_BAD_ATTEMPT_LOCKOUT, (uint32)q_u->ctr->info.inf12.bad_attempt_lockout);
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ init_samr_r_set_domain_info(r_u, NT_STATUS_OK);
+
+ DEBUG(5,("_samr_set_dom_info: %d\n", __LINE__));
+
+ return r_u->status;
+}
diff --git a/source/rpc_server/srv_samr_util.c b/source/rpc_server/srv_samr_util.c
new file mode 100644
index 00000000000..ae0fe84e029
--- /dev/null
+++ b/source/rpc_server/srv_samr_util.c
@@ -0,0 +1,554 @@
+/*
+ Unix SMB/CIFS implementation.
+ SAMR Pipe utility functions.
+
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+ Copyright (C) Gerald (Jerry) Carter 2000-2001
+ Copyright (C) Andrew Bartlett 2001-2002
+ Copyright (C) Stefan (metze) Metzmacher 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#define STRING_CHANGED (old_string && !new_string) ||\
+ (!old_string && new_string) ||\
+ (old_string && new_string && (strcmp(old_string, new_string) != 0))
+
+#define STRING_CHANGED_NC(s1,s2) ((s1) && !(s2)) ||\
+ (!(s1) && (s2)) ||\
+ ((s1) && (s2) && (strcmp((s1), (s2)) != 0))
+
+/*************************************************************
+ Copies a SAM_USER_INFO_20 to a SAM_ACCOUNT
+**************************************************************/
+
+void copy_id20_to_sam_passwd(SAM_ACCOUNT *to, SAM_USER_INFO_20 *from)
+{
+ const char *old_string;
+ char *new_string;
+ DATA_BLOB mung;
+
+ if (from == NULL || to == NULL)
+ return;
+
+ if (from->hdr_munged_dial.buffer) {
+ old_string = pdb_get_munged_dial(to);
+ mung.length = from->hdr_munged_dial.uni_str_len;
+ mung.data = (uint8 *) from->uni_munged_dial.buffer;
+ new_string = base64_encode_data_blob(mung);
+ DEBUG(10,("INFO_20 UNI_MUNGED_DIAL: %s -> %s\n",old_string, new_string));
+ if (STRING_CHANGED_NC(old_string,new_string))
+ pdb_set_munged_dial(to , new_string, PDB_CHANGED);
+
+ SAFE_FREE(new_string);
+ }
+}
+
+/*************************************************************
+ Copies a SAM_USER_INFO_21 to a SAM_ACCOUNT
+**************************************************************/
+
+void copy_id21_to_sam_passwd(SAM_ACCOUNT *to, SAM_USER_INFO_21 *from)
+{
+ time_t unix_time, stored_time;
+ const char *old_string, *new_string;
+ DATA_BLOB mung;
+
+ if (from == NULL || to == NULL)
+ return;
+
+ if (from->fields_present & ACCT_LAST_LOGON) {
+ unix_time=nt_time_to_unix(&from->logon_time);
+ stored_time = pdb_get_logon_time(to);
+ DEBUG(10,("INFO_21 LOGON_TIME: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+ if (stored_time != unix_time)
+ pdb_set_logon_time(to, unix_time, PDB_CHANGED);
+ }
+
+ if (from->fields_present & ACCT_LAST_LOGOFF) {
+ unix_time=nt_time_to_unix(&from->logoff_time);
+ stored_time = pdb_get_logoff_time(to);
+ DEBUG(10,("INFO_21 LOGOFF_TIME: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+ if (stored_time != unix_time)
+ pdb_set_logoff_time(to, unix_time, PDB_CHANGED);
+ }
+
+ if (from->fields_present & ACCT_EXPIRY) {
+ unix_time=nt_time_to_unix(&from->kickoff_time);
+ stored_time = pdb_get_kickoff_time(to);
+ DEBUG(10,("INFO_21 KICKOFF_TIME: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+ if (stored_time != unix_time)
+ pdb_set_kickoff_time(to, unix_time , PDB_CHANGED);
+ }
+
+ if (from->fields_present & ACCT_ALLOW_PWD_CHANGE) {
+ unix_time=nt_time_to_unix(&from->pass_can_change_time);
+ stored_time = pdb_get_pass_can_change_time(to);
+ DEBUG(10,("INFO_21 PASS_CAN_CH: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+ if (stored_time != unix_time)
+ pdb_set_pass_can_change_time(to, unix_time, PDB_CHANGED);
+ }
+
+ if (from->fields_present & ACCT_LAST_PWD_CHANGE) {
+ unix_time=nt_time_to_unix(&from->pass_last_set_time);
+ stored_time = pdb_get_pass_last_set_time(to);
+ DEBUG(10,("INFO_21 PASS_LAST_SET: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+ if (stored_time != unix_time)
+ pdb_set_pass_last_set_time(to, unix_time, PDB_CHANGED);
+ }
+
+ if (from->fields_present & ACCT_FORCE_PWD_CHANGE) {
+ unix_time=nt_time_to_unix(&from->pass_must_change_time);
+ stored_time=pdb_get_pass_must_change_time(to);
+ DEBUG(10,("INFO_21 PASS_MUST_CH: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+ if (stored_time != unix_time)
+ pdb_set_pass_must_change_time(to, unix_time, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_USERNAME) &&
+ (from->hdr_user_name.buffer)) {
+ old_string = pdb_get_username(to);
+ new_string = unistr2_static(&from->uni_user_name);
+ DEBUG(10,("INFO_21 UNI_USER_NAME: %s -> %s\n", old_string, new_string));
+ if (STRING_CHANGED)
+ pdb_set_username(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_FULL_NAME) &&
+ (from->hdr_full_name.buffer)) {
+ old_string = pdb_get_fullname(to);
+ new_string = unistr2_static(&from->uni_full_name);
+ DEBUG(10,("INFO_21 UNI_FULL_NAME: %s -> %s\n",old_string, new_string));
+ if (STRING_CHANGED)
+ pdb_set_fullname(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_HOME_DIR) &&
+ (from->hdr_home_dir.buffer)) {
+ old_string = pdb_get_homedir(to);
+ new_string = unistr2_static(&from->uni_home_dir);
+ DEBUG(10,("INFO_21 UNI_HOME_DIR: %s -> %s\n",old_string,new_string));
+ if (STRING_CHANGED)
+ pdb_set_homedir(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_HOME_DRIVE) &&
+ (from->hdr_dir_drive.buffer)) {
+ old_string = pdb_get_dir_drive(to);
+ new_string = unistr2_static(&from->uni_dir_drive);
+ DEBUG(10,("INFO_21 UNI_DIR_DRIVE: %s -> %s\n",old_string,new_string));
+ if (STRING_CHANGED)
+ pdb_set_dir_drive(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_LOGON_SCRIPT) &&
+ (from->hdr_logon_script.buffer)) {
+ old_string = pdb_get_logon_script(to);
+ new_string = unistr2_static(&from->uni_logon_script);
+ DEBUG(10,("INFO_21 UNI_LOGON_SCRIPT: %s -> %s\n",old_string,new_string));
+ if (STRING_CHANGED)
+ pdb_set_logon_script(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_PROFILE) &&
+ (from->hdr_profile_path.buffer)) {
+ old_string = pdb_get_profile_path(to);
+ new_string = unistr2_static(&from->uni_profile_path);
+ DEBUG(10,("INFO_21 UNI_PROFILE_PATH: %s -> %s\n",old_string, new_string));
+ if (STRING_CHANGED)
+ pdb_set_profile_path(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_DESCRIPTION) &&
+ (from->hdr_acct_desc.buffer)) {
+ old_string = pdb_get_acct_desc(to);
+ new_string = unistr2_static(&from->uni_acct_desc);
+ DEBUG(10,("INFO_21 UNI_ACCT_DESC: %s -> %s\n",old_string,new_string));
+ if (STRING_CHANGED)
+ pdb_set_acct_desc(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_WORKSTATIONS) &&
+ (from->hdr_workstations.buffer)) {
+ old_string = pdb_get_workstations(to);
+ new_string = unistr2_static(&from->uni_workstations);
+ DEBUG(10,("INFO_21 UNI_WORKSTATIONS: %s -> %s\n",old_string, new_string));
+ if (STRING_CHANGED)
+ pdb_set_workstations(to , new_string, PDB_CHANGED);
+ }
+
+ /* is this right? */
+ if ((from->fields_present & ACCT_ADMIN_DESC) &&
+ (from->hdr_unknown_str.buffer)) {
+ old_string = pdb_get_unknown_str(to);
+ new_string = unistr2_static(&from->uni_unknown_str);
+ DEBUG(10,("INFO_21 UNI_UNKNOWN_STR: %s -> %s\n",old_string, new_string));
+ if (STRING_CHANGED)
+ pdb_set_unknown_str(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_CALLBACK) &&
+ (from->hdr_munged_dial.buffer)) {
+ char *newstr;
+ old_string = pdb_get_munged_dial(to);
+ mung.length = from->hdr_munged_dial.uni_str_len;
+ mung.data = (uint8 *) from->uni_munged_dial.buffer;
+ newstr = base64_encode_data_blob(mung);
+ DEBUG(10,("INFO_21 UNI_MUNGED_DIAL: %s -> %s\n",old_string, newstr));
+ if (STRING_CHANGED_NC(old_string,newstr))
+ pdb_set_munged_dial(to , newstr, PDB_CHANGED);
+
+ SAFE_FREE(newstr);
+ }
+
+ if (from->fields_present & ACCT_RID) {
+ if (from->user_rid == 0) {
+ DEBUG(10, ("INFO_21: Asked to set User RID to 0 !? Skipping change!\n"));
+ } else if (from->user_rid != pdb_get_user_rid(to)) {
+ DEBUG(10,("INFO_21 USER_RID: %u -> %u NOT UPDATED!\n",pdb_get_user_rid(to),from->user_rid));
+ }
+ }
+
+ if (from->fields_present & ACCT_PRIMARY_GID) {
+ if (from->group_rid == 0) {
+ DEBUG(10, ("INFO_21: Asked to set Group RID to 0 !? Skipping change!\n"));
+ } else if (from->group_rid != pdb_get_group_rid(to)) {
+ DEBUG(10,("INFO_21 GROUP_RID: %u -> %u\n",pdb_get_group_rid(to),from->group_rid));
+ pdb_set_group_sid_from_rid(to, from->group_rid, PDB_CHANGED);
+ }
+ }
+
+ if (from->fields_present & ACCT_FLAGS) {
+ DEBUG(10,("INFO_21 ACCT_CTRL: %08X -> %08X\n",pdb_get_acct_ctrl(to),from->acb_info));
+ if (from->acb_info != pdb_get_acct_ctrl(to)) {
+ pdb_set_acct_ctrl(to, from->acb_info, PDB_CHANGED);
+ }
+ }
+
+ if (from->fields_present & ACCT_LOGON_HOURS) {
+ DEBUG(15,("INFO_21 LOGON_DIVS: %08X -> %08X\n",pdb_get_logon_divs(to),from->logon_divs));
+ if (from->logon_divs != pdb_get_logon_divs(to)) {
+ pdb_set_logon_divs(to, from->logon_divs, PDB_CHANGED);
+ }
+
+ DEBUG(15,("INFO_21 LOGON_HRS.LEN: %08X -> %08X\n",pdb_get_hours_len(to),from->logon_hrs.len));
+ if (from->logon_hrs.len != pdb_get_hours_len(to)) {
+ pdb_set_hours_len(to, from->logon_hrs.len, PDB_CHANGED);
+ }
+
+ DEBUG(15,("INFO_21 LOGON_HRS.HOURS: %s -> %s\n",pdb_get_hours(to),from->logon_hrs.hours));
+ /* Fix me: only update if it changes --metze */
+ pdb_set_hours(to, from->logon_hrs.hours, PDB_CHANGED);
+
+ /* This is max logon hours */
+ DEBUG(10,("INFO_21 UNKNOWN_6: %08X -> %08X\n",pdb_get_unknown_6(to),from->unknown_6));
+ if (from->unknown_6 != pdb_get_unknown_6(to)) {
+ pdb_set_unknown_6(to, from->unknown_6, PDB_CHANGED);
+ }
+ }
+
+ if (from->fields_present & ACCT_BAD_PWD_COUNT) {
+ DEBUG(10,("INFO_21 BAD_PASSWORD_COUNT: %08X -> %08X\n",pdb_get_bad_password_count(to),from->bad_password_count));
+ if (from->bad_password_count != pdb_get_bad_password_count(to)) {
+ pdb_set_bad_password_count(to, from->bad_password_count, PDB_CHANGED);
+ }
+ }
+
+ if (from->fields_present & ACCT_NUM_LOGONS) {
+ DEBUG(10,("INFO_21 LOGON_COUNT: %08X -> %08X\n",pdb_get_logon_count(to),from->logon_count));
+ if (from->logon_count != pdb_get_logon_count(to)) {
+ pdb_set_logon_count(to, from->logon_count, PDB_CHANGED);
+ }
+ }
+
+ DEBUG(10,("INFO_21 PASS_MUST_CHANGE_AT_NEXT_LOGON: %02X\n",from->passmustchange));
+ if (from->passmustchange==PASS_MUST_CHANGE_AT_NEXT_LOGON) {
+ pdb_set_pass_must_change_time(to,0, PDB_CHANGED);
+ } else {
+ uint32 expire;
+ time_t new_time;
+ if (pdb_get_pass_must_change_time(to) == 0) {
+ if (!account_policy_get(AP_MAX_PASSWORD_AGE, &expire)
+ || expire == (uint32)-1) {
+ new_time = get_time_t_max();
+ } else {
+ time_t old_time = pdb_get_pass_last_set_time(to);
+ new_time = old_time + expire;
+ if ((new_time) < time(0)) {
+ new_time = time(0) + expire;
+ }
+ }
+ if (!pdb_set_pass_must_change_time (to, new_time, PDB_CHANGED)) {
+ DEBUG (0, ("pdb_set_pass_must_change_time failed!\n"));
+ }
+ }
+ }
+
+ DEBUG(10,("INFO_21 PADDING_2: %02X\n",from->padding2));
+
+ DEBUG(10,("INFO_21 PADDING_4: %08X\n",from->padding4));
+}
+
+
+/*************************************************************
+ Copies a SAM_USER_INFO_23 to a SAM_ACCOUNT
+**************************************************************/
+
+void copy_id23_to_sam_passwd(SAM_ACCOUNT *to, SAM_USER_INFO_23 *from)
+{
+ time_t unix_time, stored_time;
+ const char *old_string, *new_string;
+ DATA_BLOB mung;
+
+ if (from == NULL || to == NULL)
+ return;
+
+ if (from->fields_present & ACCT_LAST_LOGON) {
+ unix_time=nt_time_to_unix(&from->logon_time);
+ stored_time = pdb_get_logon_time(to);
+ DEBUG(10,("INFO_23 LOGON_TIME: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+ if (stored_time != unix_time)
+ pdb_set_logon_time(to, unix_time, PDB_CHANGED);
+ }
+
+ if (from->fields_present & ACCT_LAST_LOGOFF) {
+ unix_time=nt_time_to_unix(&from->logoff_time);
+ stored_time = pdb_get_logoff_time(to);
+ DEBUG(10,("INFO_23 LOGOFF_TIME: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+ if (stored_time != unix_time)
+ pdb_set_logoff_time(to, unix_time, PDB_CHANGED);
+ }
+
+ if (from->fields_present & ACCT_EXPIRY) {
+ unix_time=nt_time_to_unix(&from->kickoff_time);
+ stored_time = pdb_get_kickoff_time(to);
+ DEBUG(10,("INFO_23 KICKOFF_TIME: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+ if (stored_time != unix_time)
+ pdb_set_kickoff_time(to, unix_time , PDB_CHANGED);
+ }
+
+ if (from->fields_present & ACCT_ALLOW_PWD_CHANGE) {
+ unix_time=nt_time_to_unix(&from->pass_can_change_time);
+ stored_time = pdb_get_pass_can_change_time(to);
+ DEBUG(10,("INFO_23 PASS_CAN_CH: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+ if (stored_time != unix_time)
+ pdb_set_pass_can_change_time(to, unix_time, PDB_CHANGED);
+ }
+
+ if (from->fields_present & ACCT_LAST_PWD_CHANGE) {
+ unix_time=nt_time_to_unix(&from->pass_last_set_time);
+ stored_time = pdb_get_pass_last_set_time(to);
+ DEBUG(10,("INFO_23 PASS_LAST_SET: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+ if (stored_time != unix_time)
+ pdb_set_pass_last_set_time(to, unix_time, PDB_CHANGED);
+ }
+
+ if (from->fields_present & ACCT_FORCE_PWD_CHANGE) {
+ unix_time=nt_time_to_unix(&from->pass_must_change_time);
+ stored_time=pdb_get_pass_must_change_time(to);
+ DEBUG(10,("INFO_23 PASS_MUST_CH: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+ if (stored_time != unix_time)
+ pdb_set_pass_must_change_time(to, unix_time, PDB_CHANGED);
+ }
+
+ /* Backend should check this for sanity */
+ if ((from->fields_present & ACCT_USERNAME) &&
+ (from->hdr_user_name.buffer)) {
+ old_string = pdb_get_username(to);
+ new_string = unistr2_static(&from->uni_user_name);
+ DEBUG(10,("INFO_23 UNI_USER_NAME: %s -> %s\n", old_string, new_string));
+ if (STRING_CHANGED)
+ pdb_set_username(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_FULL_NAME) &&
+ (from->hdr_full_name.buffer)) {
+ old_string = pdb_get_fullname(to);
+ new_string = unistr2_static(&from->uni_full_name);
+ DEBUG(10,("INFO_23 UNI_FULL_NAME: %s -> %s\n",old_string, new_string));
+ if (STRING_CHANGED)
+ pdb_set_fullname(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_HOME_DIR) &&
+ (from->hdr_home_dir.buffer)) {
+ old_string = pdb_get_homedir(to);
+ new_string = unistr2_static(&from->uni_home_dir);
+ DEBUG(10,("INFO_23 UNI_HOME_DIR: %s -> %s\n",old_string,new_string));
+ if (STRING_CHANGED)
+ pdb_set_homedir(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_HOME_DRIVE) &&
+ (from->hdr_dir_drive.buffer)) {
+ old_string = pdb_get_dir_drive(to);
+ new_string = unistr2_static(&from->uni_dir_drive);
+ DEBUG(10,("INFO_23 UNI_DIR_DRIVE: %s -> %s\n",old_string,new_string));
+ if (STRING_CHANGED)
+ pdb_set_dir_drive(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_LOGON_SCRIPT) &&
+ (from->hdr_logon_script.buffer)) {
+ old_string = pdb_get_logon_script(to);
+ new_string = unistr2_static(&from->uni_logon_script);
+ DEBUG(10,("INFO_23 UNI_LOGON_SCRIPT: %s -> %s\n",old_string,new_string));
+ if (STRING_CHANGED)
+ pdb_set_logon_script(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_PROFILE) &&
+ (from->hdr_profile_path.buffer)) {
+ old_string = pdb_get_profile_path(to);
+ new_string = unistr2_static(&from->uni_profile_path);
+ DEBUG(10,("INFO_23 UNI_PROFILE_PATH: %s -> %s\n",old_string, new_string));
+ if (STRING_CHANGED)
+ pdb_set_profile_path(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_DESCRIPTION) &&
+ (from->hdr_acct_desc.buffer)) {
+ old_string = pdb_get_acct_desc(to);
+ new_string = unistr2_static(&from->uni_acct_desc);
+ DEBUG(10,("INFO_23 UNI_ACCT_DESC: %s -> %s\n",old_string,new_string));
+ if (STRING_CHANGED)
+ pdb_set_acct_desc(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_WORKSTATIONS) &&
+ (from->hdr_workstations.buffer)) {
+ old_string = pdb_get_workstations(to);
+ new_string = unistr2_static(&from->uni_workstations);
+ DEBUG(10,("INFO_23 UNI_WORKSTATIONS: %s -> %s\n",old_string, new_string));
+ if (STRING_CHANGED)
+ pdb_set_workstations(to , new_string, PDB_CHANGED);
+ }
+
+ /* is this right? */
+ if ((from->fields_present & ACCT_ADMIN_DESC) &&
+ (from->hdr_unknown_str.buffer)) {
+ old_string = pdb_get_unknown_str(to);
+ new_string = unistr2_static(&from->uni_unknown_str);
+ DEBUG(10,("INFO_23 UNI_UNKNOWN_STR: %s -> %s\n",old_string, new_string));
+ if (STRING_CHANGED)
+ pdb_set_unknown_str(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_CALLBACK) &&
+ (from->hdr_munged_dial.buffer)) {
+ char *newstr;
+ old_string = pdb_get_munged_dial(to);
+ mung.length = from->hdr_munged_dial.uni_str_len;
+ mung.data = (uint8 *) from->uni_munged_dial.buffer;
+ newstr = base64_encode_data_blob(mung);
+ DEBUG(10,("INFO_23 UNI_MUNGED_DIAL: %s -> %s\n",old_string, newstr));
+ if (STRING_CHANGED_NC(old_string, newstr))
+ pdb_set_munged_dial(to , newstr, PDB_CHANGED);
+
+ SAFE_FREE(newstr);
+ }
+
+ if (from->fields_present & ACCT_RID) {
+ if (from->user_rid == 0) {
+ DEBUG(10, ("INFO_23: Asked to set User RID to 0 !? Skipping change!\n"));
+ } else if (from->user_rid != pdb_get_user_rid(to)) {
+ DEBUG(10,("INFO_23 USER_RID: %u -> %u NOT UPDATED!\n",pdb_get_user_rid(to),from->user_rid));
+ }
+ }
+
+ if (from->fields_present & ACCT_PRIMARY_GID) {
+ if (from->group_rid == 0) {
+ DEBUG(10, ("INFO_23: Asked to set Group RID to 0 !? Skipping change!\n"));
+ } else if (from->group_rid != pdb_get_group_rid(to)) {
+ DEBUG(10,("INFO_23 GROUP_RID: %u -> %u\n",pdb_get_group_rid(to),from->group_rid));
+ pdb_set_group_sid_from_rid(to, from->group_rid, PDB_CHANGED);
+ }
+ }
+
+ if (from->fields_present & ACCT_FLAGS) {
+ DEBUG(10,("INFO_23 ACCT_CTRL: %08X -> %08X\n",pdb_get_acct_ctrl(to),from->acb_info));
+ if (from->acb_info != pdb_get_acct_ctrl(to)) {
+ pdb_set_acct_ctrl(to, from->acb_info, PDB_CHANGED);
+ }
+ }
+
+ if (from->fields_present & ACCT_LOGON_HOURS) {
+ DEBUG(15,("INFO_23 LOGON_DIVS: %08X -> %08X\n",pdb_get_logon_divs(to),from->logon_divs));
+ if (from->logon_divs != pdb_get_logon_divs(to)) {
+ pdb_set_logon_divs(to, from->logon_divs, PDB_CHANGED);
+ }
+
+ DEBUG(15,("INFO_23 LOGON_HRS.LEN: %08X -> %08X\n",pdb_get_hours_len(to),from->logon_hrs.len));
+ if (from->logon_hrs.len != pdb_get_hours_len(to)) {
+ pdb_set_hours_len(to, from->logon_hrs.len, PDB_CHANGED);
+ }
+
+ DEBUG(15,("INFO_23 LOGON_HRS.HOURS: %s -> %s\n",pdb_get_hours(to),from->logon_hrs.hours));
+ /* Fix me: only update if it changes --metze */
+ pdb_set_hours(to, from->logon_hrs.hours, PDB_CHANGED);
+
+ /* This is max logon hours */
+ DEBUG(10,("INFO_23 UNKOWN_6: %08X -> %08X\n",pdb_get_unknown_6(to),from->unknown_6));
+ if (from->unknown_6 != pdb_get_unknown_6(to)) {
+ pdb_set_unknown_6(to, from->unknown_6, PDB_CHANGED);
+ }
+ }
+
+ if (from->fields_present & ACCT_BAD_PWD_COUNT) {
+ DEBUG(10,("INFO_23 BAD_PASSWORD_COUNT: %08X -> %08X\n",pdb_get_bad_password_count(to),from->bad_password_count));
+ if (from->bad_password_count != pdb_get_bad_password_count(to)) {
+ pdb_set_bad_password_count(to, from->bad_password_count, PDB_CHANGED);
+ }
+ }
+
+ if (from->fields_present & ACCT_NUM_LOGONS) {
+ DEBUG(10,("INFO_23 LOGON_COUNT: %08X -> %08X\n",pdb_get_logon_count(to),from->logon_count));
+ if (from->logon_count != pdb_get_logon_count(to)) {
+ pdb_set_logon_count(to, from->logon_count, PDB_CHANGED);
+ }
+ }
+
+ DEBUG(10,("INFO_23 PASS_MUST_CHANGE_AT_NEXT_LOGON: %02X\n",from->passmustchange));
+ if (from->passmustchange==PASS_MUST_CHANGE_AT_NEXT_LOGON) {
+ pdb_set_pass_must_change_time(to,0, PDB_CHANGED);
+ } else {
+ uint32 expire;
+ time_t new_time;
+ if (pdb_get_pass_must_change_time(to) == 0) {
+ if (!account_policy_get(AP_MAX_PASSWORD_AGE, &expire)
+ || expire == (uint32)-1) {
+ new_time = get_time_t_max();
+ } else {
+ time_t old_time = pdb_get_pass_last_set_time(to);
+ new_time = old_time + expire;
+ if ((new_time) < time(0)) {
+ new_time = time(0) + expire;
+ }
+ }
+ if (!pdb_set_pass_must_change_time (to, new_time, PDB_CHANGED)) {
+ DEBUG (0, ("pdb_set_pass_must_change_time failed!\n"));
+ }
+ }
+ }
+
+ DEBUG(10,("INFO_23 PADDING_2: %02X\n",from->padding2));
+
+ DEBUG(10,("INFO_23 PADDING_4: %08X\n",from->padding4));
+}
diff --git a/source/rpc_server/srv_spoolss.c b/source/rpc_server/srv_spoolss.c
new file mode 100755
index 00000000000..f846813a40b
--- /dev/null
+++ b/source/rpc_server/srv_spoolss.c
@@ -0,0 +1,1652 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-2000,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+ * Copyright (C) Jean François Micouleau 1998-2000,
+ * Copyright (C) Jeremy Allison 2001,
+ * Copyright (C) Gerald Carter 2001-2002,
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/********************************************************************
+ * api_spoolss_open_printer_ex (rarely seen - older call)
+ ********************************************************************/
+
+static BOOL api_spoolss_open_printer(pipes_struct *p)
+{
+ SPOOL_Q_OPEN_PRINTER q_u;
+ SPOOL_R_OPEN_PRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_open_printer("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_open_printer: unable to unmarshall SPOOL_Q_OPEN_PRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_open_printer( p, &q_u, &r_u);
+
+ if (!spoolss_io_r_open_printer("",&r_u,rdata,0)){
+ DEBUG(0,("spoolss_io_r_open_printer: unable to marshall SPOOL_R_OPEN_PRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/********************************************************************
+ * api_spoolss_open_printer_ex
+ ********************************************************************/
+
+static BOOL api_spoolss_open_printer_ex(pipes_struct *p)
+{
+ SPOOL_Q_OPEN_PRINTER_EX q_u;
+ SPOOL_R_OPEN_PRINTER_EX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_open_printer_ex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_open_printer_ex: unable to unmarshall SPOOL_Q_OPEN_PRINTER_EX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_open_printer_ex( p, &q_u, &r_u);
+
+ if (!spoolss_io_r_open_printer_ex("",&r_u,rdata,0)){
+ DEBUG(0,("spoolss_io_r_open_printer_ex: unable to marshall SPOOL_R_OPEN_PRINTER_EX.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_getprinterdata
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static BOOL api_spoolss_getprinterdata(pipes_struct *p)
+{
+ SPOOL_Q_GETPRINTERDATA q_u;
+ SPOOL_R_GETPRINTERDATA r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* read the stream and fill the struct */
+ if (!spoolss_io_q_getprinterdata("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_getprinterdata: unable to unmarshall SPOOL_Q_GETPRINTERDATA.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_getprinterdata( p, &q_u, &r_u);
+
+ if (!spoolss_io_r_getprinterdata("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_getprinterdata: unable to marshall SPOOL_R_GETPRINTERDATA.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_deleteprinterdata
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static BOOL api_spoolss_deleteprinterdata(pipes_struct *p)
+{
+ SPOOL_Q_DELETEPRINTERDATA q_u;
+ SPOOL_R_DELETEPRINTERDATA r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* read the stream and fill the struct */
+ if (!spoolss_io_q_deleteprinterdata("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_deleteprinterdata: unable to unmarshall SPOOL_Q_DELETEPRINTERDATA.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_deleteprinterdata( p, &q_u, &r_u );
+
+ if (!spoolss_io_r_deleteprinterdata("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_deleteprinterdata: unable to marshall SPOOL_R_DELETEPRINTERDATA.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_closeprinter
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static BOOL api_spoolss_closeprinter(pipes_struct *p)
+{
+ SPOOL_Q_CLOSEPRINTER q_u;
+ SPOOL_R_CLOSEPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_closeprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_closeprinter: unable to unmarshall SPOOL_Q_CLOSEPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_closeprinter(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_closeprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_closeprinter: unable to marshall SPOOL_R_CLOSEPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_abortprinter
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static BOOL api_spoolss_abortprinter(pipes_struct *p)
+{
+ SPOOL_Q_ABORTPRINTER q_u;
+ SPOOL_R_ABORTPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_abortprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_abortprinter: unable to unmarshall SPOOL_Q_ABORTPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_abortprinter(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_abortprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_abortprinter: unable to marshall SPOOL_R_ABORTPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_deleteprinter
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static BOOL api_spoolss_deleteprinter(pipes_struct *p)
+{
+ SPOOL_Q_DELETEPRINTER q_u;
+ SPOOL_R_DELETEPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_deleteprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_deleteprinter: unable to unmarshall SPOOL_Q_DELETEPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_deleteprinter(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_deleteprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_deleteprinter: unable to marshall SPOOL_R_DELETEPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/********************************************************************
+ * api_spoolss_deleteprinterdriver
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static BOOL api_spoolss_deleteprinterdriver(pipes_struct *p)
+{
+ SPOOL_Q_DELETEPRINTERDRIVER q_u;
+ SPOOL_R_DELETEPRINTERDRIVER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_deleteprinterdriver("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_deleteprinterdriver: unable to unmarshall SPOOL_Q_DELETEPRINTERDRIVER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_deleteprinterdriver(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_deleteprinterdriver("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_deleteprinter: unable to marshall SPOOL_R_DELETEPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/********************************************************************
+ * api_spoolss_rffpcnex
+ * ReplyFindFirstPrinterChangeNotifyEx
+ ********************************************************************/
+
+static BOOL api_spoolss_rffpcnex(pipes_struct *p)
+{
+ SPOOL_Q_RFFPCNEX q_u;
+ SPOOL_R_RFFPCNEX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_rffpcnex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_rffpcnex: unable to unmarshall SPOOL_Q_RFFPCNEX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_rffpcnex(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_rffpcnex("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_rffpcnex: unable to marshall SPOOL_R_RFFPCNEX.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/********************************************************************
+ * api_spoolss_rfnpcnex
+ * ReplyFindNextPrinterChangeNotifyEx
+ * called from the spoolss dispatcher
+
+ * Note - this is the *ONLY* function that breaks the RPC call
+ * symmetry in all the other calls. We need to do this to fix
+ * the massive memory allocation problem with thousands of jobs...
+ * JRA.
+ ********************************************************************/
+
+static BOOL api_spoolss_rfnpcnex(pipes_struct *p)
+{
+ SPOOL_Q_RFNPCNEX q_u;
+ SPOOL_R_RFNPCNEX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_rfnpcnex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_rfnpcnex: unable to unmarshall SPOOL_Q_RFNPCNEX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_rfnpcnex(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_rfnpcnex("", &r_u, rdata, 0)) {
+ SAFE_FREE(r_u.info.data);
+ DEBUG(0,("spoolss_io_r_rfnpcnex: unable to marshall SPOOL_R_RFNPCNEX.\n"));
+ return False;
+ }
+
+ SAFE_FREE(r_u.info.data);
+
+ return True;
+}
+
+
+/********************************************************************
+ * api_spoolss_enumprinters
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+static BOOL api_spoolss_enumprinters(pipes_struct *p)
+{
+ SPOOL_Q_ENUMPRINTERS q_u;
+ SPOOL_R_ENUMPRINTERS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_enumprinters("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumprinters: unable to unmarshall SPOOL_Q_ENUMPRINTERS.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumprinters( p, &q_u, &r_u);
+
+ if (!spoolss_io_r_enumprinters("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_enumprinters: unable to marshall SPOOL_R_ENUMPRINTERS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+static BOOL api_spoolss_getprinter(pipes_struct *p)
+{
+ SPOOL_Q_GETPRINTER q_u;
+ SPOOL_R_GETPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_getprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_getprinter: unable to unmarshall SPOOL_Q_GETPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_getprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_getprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_getprinter: unable to marshall SPOOL_R_GETPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+static BOOL api_spoolss_getprinterdriver2(pipes_struct *p)
+{
+ SPOOL_Q_GETPRINTERDRIVER2 q_u;
+ SPOOL_R_GETPRINTERDRIVER2 r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_getprinterdriver2("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_getprinterdriver2: unable to unmarshall SPOOL_Q_GETPRINTERDRIVER2.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_getprinterdriver2(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_getprinterdriver2("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_getprinterdriver2: unable to marshall SPOOL_R_GETPRINTERDRIVER2.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+static BOOL api_spoolss_startpageprinter(pipes_struct *p)
+{
+ SPOOL_Q_STARTPAGEPRINTER q_u;
+ SPOOL_R_STARTPAGEPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_startpageprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_startpageprinter: unable to unmarshall SPOOL_Q_STARTPAGEPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_startpageprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_startpageprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_startpageprinter: unable to marshall SPOOL_R_STARTPAGEPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+static BOOL api_spoolss_endpageprinter(pipes_struct *p)
+{
+ SPOOL_Q_ENDPAGEPRINTER q_u;
+ SPOOL_R_ENDPAGEPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_endpageprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_endpageprinter: unable to unmarshall SPOOL_Q_ENDPAGEPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_endpageprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_endpageprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_endpageprinter: unable to marshall SPOOL_R_ENDPAGEPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+********************************************************************/
+
+static BOOL api_spoolss_startdocprinter(pipes_struct *p)
+{
+ SPOOL_Q_STARTDOCPRINTER q_u;
+ SPOOL_R_STARTDOCPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_startdocprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_startdocprinter: unable to unmarshall SPOOL_Q_STARTDOCPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_startdocprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_startdocprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_startdocprinter: unable to marshall SPOOL_R_STARTDOCPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+********************************************************************/
+
+static BOOL api_spoolss_enddocprinter(pipes_struct *p)
+{
+ SPOOL_Q_ENDDOCPRINTER q_u;
+ SPOOL_R_ENDDOCPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_enddocprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enddocprinter: unable to unmarshall SPOOL_Q_ENDDOCPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enddocprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_enddocprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_enddocprinter: unable to marshall SPOOL_R_ENDDOCPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+********************************************************************/
+
+static BOOL api_spoolss_writeprinter(pipes_struct *p)
+{
+ SPOOL_Q_WRITEPRINTER q_u;
+ SPOOL_R_WRITEPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_writeprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_writeprinter: unable to unmarshall SPOOL_Q_WRITEPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_writeprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_writeprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_writeprinter: unable to marshall SPOOL_R_WRITEPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+
+****************************************************************************/
+
+static BOOL api_spoolss_setprinter(pipes_struct *p)
+{
+ SPOOL_Q_SETPRINTER q_u;
+ SPOOL_R_SETPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_setprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_setprinter: unable to unmarshall SPOOL_Q_SETPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_setprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_setprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_setprinter: unable to marshall SPOOL_R_SETPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_fcpn(pipes_struct *p)
+{
+ SPOOL_Q_FCPN q_u;
+ SPOOL_R_FCPN r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_fcpn("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_fcpn: unable to unmarshall SPOOL_Q_FCPN.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_fcpn(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_fcpn("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_fcpn: unable to marshall SPOOL_R_FCPN.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_addjob(pipes_struct *p)
+{
+ SPOOL_Q_ADDJOB q_u;
+ SPOOL_R_ADDJOB r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_addjob("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_addjob: unable to unmarshall SPOOL_Q_ADDJOB.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_addjob(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_addjob("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_addjob: unable to marshall SPOOL_R_ADDJOB.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumjobs(pipes_struct *p)
+{
+ SPOOL_Q_ENUMJOBS q_u;
+ SPOOL_R_ENUMJOBS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_enumjobs("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumjobs: unable to unmarshall SPOOL_Q_ENUMJOBS.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumjobs(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_enumjobs("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_enumjobs: unable to marshall SPOOL_R_ENUMJOBS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_schedulejob(pipes_struct *p)
+{
+ SPOOL_Q_SCHEDULEJOB q_u;
+ SPOOL_R_SCHEDULEJOB r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_schedulejob("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_schedulejob: unable to unmarshall SPOOL_Q_SCHEDULEJOB.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_schedulejob(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_schedulejob("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_schedulejob: unable to marshall SPOOL_R_SCHEDULEJOB.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_setjob(pipes_struct *p)
+{
+ SPOOL_Q_SETJOB q_u;
+ SPOOL_R_SETJOB r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_setjob("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_setjob: unable to unmarshall SPOOL_Q_SETJOB.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_setjob(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_setjob("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_setjob: unable to marshall SPOOL_R_SETJOB.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumprinterdrivers(pipes_struct *p)
+{
+ SPOOL_Q_ENUMPRINTERDRIVERS q_u;
+ SPOOL_R_ENUMPRINTERDRIVERS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_enumprinterdrivers("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumprinterdrivers: unable to unmarshall SPOOL_Q_ENUMPRINTERDRIVERS.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumprinterdrivers(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_enumprinterdrivers("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_enumprinterdrivers: unable to marshall SPOOL_R_ENUMPRINTERDRIVERS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_getform(pipes_struct *p)
+{
+ SPOOL_Q_GETFORM q_u;
+ SPOOL_R_GETFORM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_getform("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_getform: unable to unmarshall SPOOL_Q_GETFORM.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_getform(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_getform("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_getform: unable to marshall SPOOL_R_GETFORM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumforms(pipes_struct *p)
+{
+ SPOOL_Q_ENUMFORMS q_u;
+ SPOOL_R_ENUMFORMS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_enumforms("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumforms: unable to unmarshall SPOOL_Q_ENUMFORMS.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumforms(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_enumforms("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_enumforms: unable to marshall SPOOL_R_ENUMFORMS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumports(pipes_struct *p)
+{
+ SPOOL_Q_ENUMPORTS q_u;
+ SPOOL_R_ENUMPORTS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_enumports("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumports: unable to unmarshall SPOOL_Q_ENUMPORTS.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumports(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_enumports("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_enumports: unable to marshall SPOOL_R_ENUMPORTS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_addprinterex(pipes_struct *p)
+{
+ SPOOL_Q_ADDPRINTEREX q_u;
+ SPOOL_R_ADDPRINTEREX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_addprinterex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_addprinterex: unable to unmarshall SPOOL_Q_ADDPRINTEREX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_addprinterex(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_addprinterex("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_addprinterex: unable to marshall SPOOL_R_ADDPRINTEREX.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_addprinterdriver(pipes_struct *p)
+{
+ SPOOL_Q_ADDPRINTERDRIVER q_u;
+ SPOOL_R_ADDPRINTERDRIVER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_addprinterdriver("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_addprinterdriver: unable to unmarshall SPOOL_Q_ADDPRINTERDRIVER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_addprinterdriver(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_addprinterdriver("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_addprinterdriver: unable to marshall SPOOL_R_ADDPRINTERDRIVER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_getprinterdriverdirectory(pipes_struct *p)
+{
+ SPOOL_Q_GETPRINTERDRIVERDIR q_u;
+ SPOOL_R_GETPRINTERDRIVERDIR r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_getprinterdriverdir("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_getprinterdriverdir: unable to unmarshall SPOOL_Q_GETPRINTERDRIVERDIR.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_getprinterdriverdirectory(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_getprinterdriverdir("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_getprinterdriverdir: unable to marshall SPOOL_R_GETPRINTERDRIVERDIR.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumprinterdata(pipes_struct *p)
+{
+ SPOOL_Q_ENUMPRINTERDATA q_u;
+ SPOOL_R_ENUMPRINTERDATA r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_enumprinterdata("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumprinterdata: unable to unmarshall SPOOL_Q_ENUMPRINTERDATA.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumprinterdata(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_enumprinterdata("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_enumprinterdata: unable to marshall SPOOL_R_ENUMPRINTERDATA.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_setprinterdata(pipes_struct *p)
+{
+ SPOOL_Q_SETPRINTERDATA q_u;
+ SPOOL_R_SETPRINTERDATA r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_setprinterdata("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_setprinterdata: unable to unmarshall SPOOL_Q_SETPRINTERDATA.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_setprinterdata(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_setprinterdata("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_setprinterdata: unable to marshall SPOOL_R_SETPRINTERDATA.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+static BOOL api_spoolss_reset_printer(pipes_struct *p)
+{
+ SPOOL_Q_RESETPRINTER q_u;
+ SPOOL_R_RESETPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_resetprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_setprinterdata: unable to unmarshall SPOOL_Q_SETPRINTERDATA.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_resetprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_resetprinter("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_setprinterdata: unable to marshall SPOOL_R_RESETPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+static BOOL api_spoolss_addform(pipes_struct *p)
+{
+ SPOOL_Q_ADDFORM q_u;
+ SPOOL_R_ADDFORM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_addform("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_addform: unable to unmarshall SPOOL_Q_ADDFORM.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_addform(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_addform("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_addform: unable to marshall SPOOL_R_ADDFORM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_deleteform(pipes_struct *p)
+{
+ SPOOL_Q_DELETEFORM q_u;
+ SPOOL_R_DELETEFORM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_deleteform("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_deleteform: unable to unmarshall SPOOL_Q_DELETEFORM.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_deleteform(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_deleteform("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_deleteform: unable to marshall SPOOL_R_DELETEFORM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_setform(pipes_struct *p)
+{
+ SPOOL_Q_SETFORM q_u;
+ SPOOL_R_SETFORM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_setform("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_setform: unable to unmarshall SPOOL_Q_SETFORM.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_setform(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_setform("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_setform: unable to marshall SPOOL_R_SETFORM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumprintprocessors(pipes_struct *p)
+{
+ SPOOL_Q_ENUMPRINTPROCESSORS q_u;
+ SPOOL_R_ENUMPRINTPROCESSORS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_enumprintprocessors("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumprintprocessors: unable to unmarshall SPOOL_Q_ENUMPRINTPROCESSORS.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumprintprocessors(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_enumprintprocessors("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_enumprintprocessors: unable to marshall SPOOL_R_ENUMPRINTPROCESSORS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_addprintprocessor(pipes_struct *p)
+{
+ SPOOL_Q_ADDPRINTPROCESSOR q_u;
+ SPOOL_R_ADDPRINTPROCESSOR r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_addprintprocessor("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_addprintprocessor: unable to unmarshall SPOOL_Q_ADDPRINTPROCESSOR.\n"));
+ return False;
+ }
+
+ /* for now, just indicate success and ignore the add. We'll
+ automatically set the winprint processor for printer
+ entries later. Used to debug the LexMark Optra S 1855 PCL
+ driver --jerry */
+ r_u.status = WERR_OK;
+
+ if(!spoolss_io_r_addprintprocessor("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_addprintprocessor: unable to marshall SPOOL_R_ADDPRINTPROCESSOR.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumprintprocdatatypes(pipes_struct *p)
+{
+ SPOOL_Q_ENUMPRINTPROCDATATYPES q_u;
+ SPOOL_R_ENUMPRINTPROCDATATYPES r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_enumprintprocdatatypes("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumprintprocdatatypes: unable to unmarshall SPOOL_Q_ENUMPRINTPROCDATATYPES.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumprintprocdatatypes(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_enumprintprocdatatypes("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_enumprintprocdatatypes: unable to marshall SPOOL_R_ENUMPRINTPROCDATATYPES.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumprintmonitors(pipes_struct *p)
+{
+ SPOOL_Q_ENUMPRINTMONITORS q_u;
+ SPOOL_R_ENUMPRINTMONITORS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_enumprintmonitors("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumprintmonitors: unable to unmarshall SPOOL_Q_ENUMPRINTMONITORS.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumprintmonitors(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_enumprintmonitors("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_enumprintmonitors: unable to marshall SPOOL_R_ENUMPRINTMONITORS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_getjob(pipes_struct *p)
+{
+ SPOOL_Q_GETJOB q_u;
+ SPOOL_R_GETJOB r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ if(!spoolss_io_q_getjob("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_getjob: unable to unmarshall SPOOL_Q_GETJOB.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_getjob(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_getjob("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_getjob: unable to marshall SPOOL_R_GETJOB.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_getprinterdataex
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static BOOL api_spoolss_getprinterdataex(pipes_struct *p)
+{
+ SPOOL_Q_GETPRINTERDATAEX q_u;
+ SPOOL_R_GETPRINTERDATAEX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* read the stream and fill the struct */
+ if (!spoolss_io_q_getprinterdataex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_getprinterdataex: unable to unmarshall SPOOL_Q_GETPRINTERDATAEX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_getprinterdataex( p, &q_u, &r_u);
+
+ if (!spoolss_io_r_getprinterdataex("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_getprinterdataex: unable to marshall SPOOL_R_GETPRINTERDATAEX.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_setprinterdataex(pipes_struct *p)
+{
+ SPOOL_Q_SETPRINTERDATAEX q_u;
+ SPOOL_R_SETPRINTERDATAEX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_setprinterdataex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_setprinterdataex: unable to unmarshall SPOOL_Q_SETPRINTERDATAEX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_setprinterdataex(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_setprinterdataex("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_setprinterdataex: unable to marshall SPOOL_R_SETPRINTERDATAEX.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumprinterkey(pipes_struct *p)
+{
+ SPOOL_Q_ENUMPRINTERKEY q_u;
+ SPOOL_R_ENUMPRINTERKEY r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_enumprinterkey("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_setprinterkey: unable to unmarshall SPOOL_Q_ENUMPRINTERKEY.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumprinterkey(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_enumprinterkey("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_enumprinterkey: unable to marshall SPOOL_R_ENUMPRINTERKEY.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumprinterdataex(pipes_struct *p)
+{
+ SPOOL_Q_ENUMPRINTERDATAEX q_u;
+ SPOOL_R_ENUMPRINTERDATAEX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_enumprinterdataex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumprinterdataex: unable to unmarshall SPOOL_Q_ENUMPRINTERDATAEX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumprinterdataex(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_enumprinterdataex("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_enumprinterdataex: unable to marshall SPOOL_R_ENUMPRINTERDATAEX.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_getprintprocessordirectory(pipes_struct *p)
+{
+ SPOOL_Q_GETPRINTPROCESSORDIRECTORY q_u;
+ SPOOL_R_GETPRINTPROCESSORDIRECTORY r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_getprintprocessordirectory("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_getprintprocessordirectory: unable to unmarshall SPOOL_Q_GETPRINTPROCESSORDIRECTORY.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_getprintprocessordirectory(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_getprintprocessordirectory("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_getprintprocessordirectory: unable to marshall SPOOL_R_GETPRINTPROCESSORDIRECTORY.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_deleteprinterdataex(pipes_struct *p)
+{
+ SPOOL_Q_DELETEPRINTERDATAEX q_u;
+ SPOOL_R_DELETEPRINTERDATAEX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_deleteprinterdataex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_deleteprinterdataex: unable to unmarshall SPOOL_Q_DELETEPRINTERDATAEX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_deleteprinterdataex(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_deleteprinterdataex("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_deleteprinterdataex: unable to marshall SPOOL_R_DELETEPRINTERDATAEX.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_deleteprinterkey(pipes_struct *p)
+{
+ SPOOL_Q_DELETEPRINTERKEY q_u;
+ SPOOL_R_DELETEPRINTERKEY r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_deleteprinterkey("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_deleteprinterkey: unable to unmarshall SPOOL_Q_DELETEPRINTERKEY.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_deleteprinterkey(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_deleteprinterkey("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_deleteprinterkey: unable to marshall SPOOL_R_DELETEPRINTERKEY.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_addprinterdriverex(pipes_struct *p)
+{
+ SPOOL_Q_ADDPRINTERDRIVEREX q_u;
+ SPOOL_R_ADDPRINTERDRIVEREX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_addprinterdriverex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_addprinterdriverex: unable to unmarshall SPOOL_Q_ADDPRINTERDRIVEREX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_addprinterdriverex(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_addprinterdriverex("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_addprinterdriverex: unable to marshall SPOOL_R_ADDPRINTERDRIVEREX.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_deleteprinterdriverex(pipes_struct *p)
+{
+ SPOOL_Q_DELETEPRINTERDRIVEREX q_u;
+ SPOOL_R_DELETEPRINTERDRIVEREX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_deleteprinterdriverex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_deleteprinterdriverex: unable to unmarshall SPOOL_Q_DELETEPRINTERDRIVEREX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_deleteprinterdriverex(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_deleteprinterdriverex("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_deleteprinterdriverex: unable to marshall SPOOL_R_DELETEPRINTERDRIVEREX.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+#if 0
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_replyopenprinter(pipes_struct *p)
+{
+ SPOOL_Q_REPLYOPENPRINTER q_u;
+ SPOOL_R_REPLYOPENPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_replyopenprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_replyopenprinter: unable to unmarshall SPOOL_Q_REPLYOPENPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_replyopenprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_replyopenprinter("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_replyopenprinter: unable to marshall SPOOL_R_REPLYOPENPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_replycloseprinter(pipes_struct *p)
+{
+ SPOOL_Q_REPLYCLOSEPRINTER q_u;
+ SPOOL_R_REPLYCLOSEPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_replycloseprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_replycloseprinter: unable to unmarshall SPOOL_Q_REPLYCLOSEPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_replycloseprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_replycloseprinter("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_replycloseprinter: unable to marshall SPOOL_R_REPLYCLOSEPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+#endif
+
+/*******************************************************************
+\pipe\spoolss commands
+********************************************************************/
+
+ struct api_struct api_spoolss_cmds[] =
+ {
+ {"SPOOLSS_OPENPRINTER", SPOOLSS_OPENPRINTER, api_spoolss_open_printer },
+ {"SPOOLSS_OPENPRINTEREX", SPOOLSS_OPENPRINTEREX, api_spoolss_open_printer_ex },
+ {"SPOOLSS_GETPRINTERDATA", SPOOLSS_GETPRINTERDATA, api_spoolss_getprinterdata },
+ {"SPOOLSS_CLOSEPRINTER", SPOOLSS_CLOSEPRINTER, api_spoolss_closeprinter },
+ {"SPOOLSS_DELETEPRINTER", SPOOLSS_DELETEPRINTER, api_spoolss_deleteprinter },
+ {"SPOOLSS_ABORTPRINTER", SPOOLSS_ABORTPRINTER, api_spoolss_abortprinter },
+ {"SPOOLSS_RFFPCNEX", SPOOLSS_RFFPCNEX, api_spoolss_rffpcnex },
+ {"SPOOLSS_RFNPCNEX", SPOOLSS_RFNPCNEX, api_spoolss_rfnpcnex },
+ {"SPOOLSS_ENUMPRINTERS", SPOOLSS_ENUMPRINTERS, api_spoolss_enumprinters },
+ {"SPOOLSS_GETPRINTER", SPOOLSS_GETPRINTER, api_spoolss_getprinter },
+ {"SPOOLSS_GETPRINTERDRIVER2", SPOOLSS_GETPRINTERDRIVER2, api_spoolss_getprinterdriver2 },
+ {"SPOOLSS_STARTPAGEPRINTER", SPOOLSS_STARTPAGEPRINTER, api_spoolss_startpageprinter },
+ {"SPOOLSS_ENDPAGEPRINTER", SPOOLSS_ENDPAGEPRINTER, api_spoolss_endpageprinter },
+ {"SPOOLSS_STARTDOCPRINTER", SPOOLSS_STARTDOCPRINTER, api_spoolss_startdocprinter },
+ {"SPOOLSS_ENDDOCPRINTER", SPOOLSS_ENDDOCPRINTER, api_spoolss_enddocprinter },
+ {"SPOOLSS_WRITEPRINTER", SPOOLSS_WRITEPRINTER, api_spoolss_writeprinter },
+ {"SPOOLSS_SETPRINTER", SPOOLSS_SETPRINTER, api_spoolss_setprinter },
+ {"SPOOLSS_FCPN", SPOOLSS_FCPN, api_spoolss_fcpn },
+ {"SPOOLSS_ADDJOB", SPOOLSS_ADDJOB, api_spoolss_addjob },
+ {"SPOOLSS_ENUMJOBS", SPOOLSS_ENUMJOBS, api_spoolss_enumjobs },
+ {"SPOOLSS_SCHEDULEJOB", SPOOLSS_SCHEDULEJOB, api_spoolss_schedulejob },
+ {"SPOOLSS_SETJOB", SPOOLSS_SETJOB, api_spoolss_setjob },
+ {"SPOOLSS_ENUMFORMS", SPOOLSS_ENUMFORMS, api_spoolss_enumforms },
+ {"SPOOLSS_ENUMPORTS", SPOOLSS_ENUMPORTS, api_spoolss_enumports },
+ {"SPOOLSS_ENUMPRINTERDRIVERS", SPOOLSS_ENUMPRINTERDRIVERS, api_spoolss_enumprinterdrivers },
+ {"SPOOLSS_ADDPRINTEREX", SPOOLSS_ADDPRINTEREX, api_spoolss_addprinterex },
+ {"SPOOLSS_ADDPRINTERDRIVER", SPOOLSS_ADDPRINTERDRIVER, api_spoolss_addprinterdriver },
+ {"SPOOLSS_DELETEPRINTERDRIVER", SPOOLSS_DELETEPRINTERDRIVER, api_spoolss_deleteprinterdriver },
+ {"SPOOLSS_GETPRINTERDRIVERDIRECTORY", SPOOLSS_GETPRINTERDRIVERDIRECTORY, api_spoolss_getprinterdriverdirectory },
+ {"SPOOLSS_ENUMPRINTERDATA", SPOOLSS_ENUMPRINTERDATA, api_spoolss_enumprinterdata },
+ {"SPOOLSS_SETPRINTERDATA", SPOOLSS_SETPRINTERDATA, api_spoolss_setprinterdata },
+ {"SPOOLSS_RESETPRINTER", SPOOLSS_RESETPRINTER, api_spoolss_reset_printer },
+ {"SPOOLSS_DELETEPRINTERDATA", SPOOLSS_DELETEPRINTERDATA, api_spoolss_deleteprinterdata },
+ {"SPOOLSS_ADDFORM", SPOOLSS_ADDFORM, api_spoolss_addform },
+ {"SPOOLSS_DELETEFORM", SPOOLSS_DELETEFORM, api_spoolss_deleteform },
+ {"SPOOLSS_GETFORM", SPOOLSS_GETFORM, api_spoolss_getform },
+ {"SPOOLSS_SETFORM", SPOOLSS_SETFORM, api_spoolss_setform },
+ {"SPOOLSS_ADDPRINTPROCESSOR", SPOOLSS_ADDPRINTPROCESSOR, api_spoolss_addprintprocessor },
+ {"SPOOLSS_ENUMPRINTPROCESSORS", SPOOLSS_ENUMPRINTPROCESSORS, api_spoolss_enumprintprocessors },
+ {"SPOOLSS_ENUMMONITORS", SPOOLSS_ENUMMONITORS, api_spoolss_enumprintmonitors },
+ {"SPOOLSS_GETJOB", SPOOLSS_GETJOB, api_spoolss_getjob },
+ {"SPOOLSS_ENUMPRINTPROCDATATYPES", SPOOLSS_ENUMPRINTPROCDATATYPES, api_spoolss_enumprintprocdatatypes },
+ {"SPOOLSS_GETPRINTERDATAEX", SPOOLSS_GETPRINTERDATAEX, api_spoolss_getprinterdataex },
+ {"SPOOLSS_SETPRINTERDATAEX", SPOOLSS_SETPRINTERDATAEX, api_spoolss_setprinterdataex },
+ {"SPOOLSS_DELETEPRINTERDATAEX", SPOOLSS_DELETEPRINTERDATAEX, api_spoolss_deleteprinterdataex },
+ {"SPOOLSS_ENUMPRINTERDATAEX", SPOOLSS_ENUMPRINTERDATAEX, api_spoolss_enumprinterdataex },
+ {"SPOOLSS_ENUMPRINTERKEY", SPOOLSS_ENUMPRINTERKEY, api_spoolss_enumprinterkey },
+ {"SPOOLSS_DELETEPRINTERKEY", SPOOLSS_DELETEPRINTERKEY, api_spoolss_deleteprinterkey },
+ {"SPOOLSS_GETPRINTPROCESSORDIRECTORY",SPOOLSS_GETPRINTPROCESSORDIRECTORY,api_spoolss_getprintprocessordirectory},
+ {"SPOOLSS_ADDPRINTERDRIVEREX", SPOOLSS_ADDPRINTERDRIVEREX, api_spoolss_addprinterdriverex },
+ {"SPOOLSS_DELETEPRINTERDRIVEREX", SPOOLSS_DELETEPRINTERDRIVEREX, api_spoolss_deleteprinterdriverex },
+#if 0
+ {"SPOOLSS_REPLYOPENPRINTER", SPOOLSS_REPLYOPENPRINTER, api_spoolss_replyopenprinter },
+ {"SPOOLSS_REPLYCLOSEPRINTER", SPOOLSS_REPLYCLOSEPRINTER, api_spoolss_replycloseprinter }
+#endif
+ };
+
+void spoolss_get_pipe_fns( struct api_struct **fns, int *n_fns )
+{
+ *fns = api_spoolss_cmds;
+ *n_fns = sizeof(api_spoolss_cmds) / sizeof(struct api_struct);
+}
+
+NTSTATUS rpc_spoolss_init(void)
+{
+ return rpc_pipe_register_commands(SMB_RPC_INTERFACE_VERSION, "spoolss", "spoolss", api_spoolss_cmds,
+ sizeof(api_spoolss_cmds) / sizeof(struct api_struct));
+}
diff --git a/source/rpc_server/srv_spoolss_nt.c b/source/rpc_server/srv_spoolss_nt.c
new file mode 100644
index 00000000000..edd62fa8f62
--- /dev/null
+++ b/source/rpc_server/srv_spoolss_nt.c
@@ -0,0 +1,9188 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-2000,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+ * Copyright (C) Jean François Micouleau 1998-2000,
+ * Copyright (C) Jeremy Allison 2001-2002,
+ * Copyright (C) Gerald Carter 2000-2003,
+ * Copyright (C) Tim Potter 2001-2002.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Since the SPOOLSS rpc routines are basically DOS 16-bit calls wrapped
+ up, all the errors returned are DOS errors, not NT status codes. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#ifndef MAX_OPEN_PRINTER_EXS
+#define MAX_OPEN_PRINTER_EXS 50
+#endif
+
+#define MAGIC_DISPLAY_FREQUENCY 0xfade2bad
+#define PHANTOM_DEVMODE_KEY "_p_f_a_n_t_0_m_"
+
+
+/* Table to map the driver version */
+/* to OS */
+static const char * drv_ver_to_os[] = {
+ "WIN9X", /* driver version/cversion 0 */
+ "", /* unused ? */
+ "WINNT", /* driver version/cversion 2 */
+ "WIN2K", /* driver version/cversion 3 */
+};
+
+static const char *get_drv_ver_to_os(int ver)
+{
+ if (ver < 0 || ver > 3)
+ return "";
+ return drv_ver_to_os[ver];
+}
+
+struct table_node {
+ const char *long_archi;
+ const char *short_archi;
+ int version;
+};
+
+static Printer_entry *printers_list;
+
+typedef struct _counter_printer_0 {
+ ubi_dlNode Next;
+ ubi_dlNode Prev;
+
+ int snum;
+ uint32 counter;
+} counter_printer_0;
+
+static ubi_dlList counter_list;
+
+static struct cli_state notify_cli; /* print notify back-channel */
+static uint32 smb_connections=0;
+
+
+/* in printing/nt_printing.c */
+
+extern STANDARD_MAPPING printer_std_mapping, printserver_std_mapping;
+
+#define OUR_HANDLE(hnd) (((hnd)==NULL)?"NULL":(IVAL((hnd)->data5,4)==(uint32)sys_getpid()?"OURS":"OTHER")), \
+((unsigned int)IVAL((hnd)->data5,4)),((unsigned int)sys_getpid())
+
+/* translate between internal status numbers and NT status numbers */
+static int nt_printj_status(int v)
+{
+ switch (v) {
+ case LPQ_QUEUED:
+ return 0;
+ case LPQ_PAUSED:
+ return JOB_STATUS_PAUSED;
+ case LPQ_SPOOLING:
+ return JOB_STATUS_SPOOLING;
+ case LPQ_PRINTING:
+ return JOB_STATUS_PRINTING;
+ case LPQ_ERROR:
+ return JOB_STATUS_ERROR;
+ case LPQ_DELETING:
+ return JOB_STATUS_DELETING;
+ case LPQ_OFFLINE:
+ return JOB_STATUS_OFFLINE;
+ case LPQ_PAPEROUT:
+ return JOB_STATUS_PAPEROUT;
+ case LPQ_PRINTED:
+ return JOB_STATUS_PRINTED;
+ case LPQ_DELETED:
+ return JOB_STATUS_DELETED;
+ case LPQ_BLOCKED:
+ return JOB_STATUS_BLOCKED;
+ case LPQ_USER_INTERVENTION:
+ return JOB_STATUS_USER_INTERVENTION;
+ }
+ return 0;
+}
+
+static int nt_printq_status(int v)
+{
+ switch (v) {
+ case LPQ_PAUSED:
+ return PRINTER_STATUS_PAUSED;
+ case LPQ_QUEUED:
+ case LPQ_SPOOLING:
+ case LPQ_PRINTING:
+ return 0;
+ }
+ return 0;
+}
+
+/****************************************************************************
+ Functions to handle SPOOL_NOTIFY_OPTION struct stored in Printer_entry.
+****************************************************************************/
+
+static void free_spool_notify_option(SPOOL_NOTIFY_OPTION **pp)
+{
+ if (*pp == NULL)
+ return;
+
+ SAFE_FREE((*pp)->ctr.type);
+ SAFE_FREE(*pp);
+}
+
+/***************************************************************************
+ Disconnect from the client
+****************************************************************************/
+
+static void srv_spoolss_replycloseprinter(int snum, POLICY_HND *handle)
+{
+ WERROR result;
+
+ /*
+ * Tell the specific printing tdb we no longer want messages for this printer
+ * by deregistering our PID.
+ */
+
+ if (!print_notify_deregister_pid(snum))
+ DEBUG(0,("print_notify_register_pid: Failed to register our pid for printer %s\n", lp_const_servicename(snum) ));
+
+ /* weird if the test succeds !!! */
+ if (smb_connections==0) {
+ DEBUG(0,("srv_spoolss_replycloseprinter:Trying to close non-existant notify backchannel !\n"));
+ return;
+ }
+
+ result = cli_spoolss_reply_close_printer(&notify_cli, notify_cli.mem_ctx, handle);
+
+ if (!W_ERROR_IS_OK(result))
+ DEBUG(0,("srv_spoolss_replycloseprinter: reply_close_printer failed [%s].\n",
+ dos_errstr(result)));
+
+ /* if it's the last connection, deconnect the IPC$ share */
+ if (smb_connections==1) {
+ cli_nt_session_close(&notify_cli);
+ cli_ulogoff(&notify_cli);
+ cli_shutdown(&notify_cli);
+ message_deregister(MSG_PRINTER_NOTIFY2);
+
+ /* Tell the connections db we're no longer interested in
+ * printer notify messages. */
+
+ register_message_flags( False, FLAG_MSG_PRINTING );
+ }
+
+ smb_connections--;
+}
+
+/****************************************************************************
+ Functions to free a printer entry datastruct.
+****************************************************************************/
+
+static void free_printer_entry(void *ptr)
+{
+ Printer_entry *Printer = (Printer_entry *)ptr;
+
+ if (Printer->notify.client_connected==True) {
+ int snum = -1;
+
+ if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER) {
+ snum = -1;
+ srv_spoolss_replycloseprinter(snum, &Printer->notify.client_hnd);
+ } else if (Printer->printer_type == PRINTER_HANDLE_IS_PRINTER) {
+ snum = print_queue_snum(Printer->dev.handlename);
+ if (snum != -1)
+ srv_spoolss_replycloseprinter(snum,
+ &Printer->notify.client_hnd);
+ }
+ }
+
+ Printer->notify.flags=0;
+ Printer->notify.options=0;
+ Printer->notify.localmachine[0]='\0';
+ Printer->notify.printerlocal=0;
+ free_spool_notify_option(&Printer->notify.option);
+ Printer->notify.option=NULL;
+ Printer->notify.client_connected=False;
+
+ free_nt_devicemode( &Printer->nt_devmode );
+ free_a_printer( &Printer->printer_info, 2 );
+
+ talloc_destroy( Printer->ctx );
+
+ /* Remove from the internal list. */
+ DLIST_REMOVE(printers_list, Printer);
+
+ SAFE_FREE(Printer);
+}
+
+/****************************************************************************
+ Functions to duplicate a SPOOL_NOTIFY_OPTION struct stored in Printer_entry.
+****************************************************************************/
+
+static SPOOL_NOTIFY_OPTION *dup_spool_notify_option(SPOOL_NOTIFY_OPTION *sp)
+{
+ SPOOL_NOTIFY_OPTION *new_sp = NULL;
+
+ if (!sp)
+ return NULL;
+
+ new_sp = (SPOOL_NOTIFY_OPTION *)malloc(sizeof(SPOOL_NOTIFY_OPTION));
+ if (!new_sp)
+ return NULL;
+
+ *new_sp = *sp;
+
+ if (sp->ctr.count) {
+ new_sp->ctr.type = (SPOOL_NOTIFY_OPTION_TYPE *)memdup(sp->ctr.type, sizeof(SPOOL_NOTIFY_OPTION_TYPE) * sp->ctr.count);
+
+ if (!new_sp->ctr.type) {
+ SAFE_FREE(new_sp);
+ return NULL;
+ }
+ }
+
+ return new_sp;
+}
+
+/****************************************************************************
+ find printer index by handle
+****************************************************************************/
+
+static Printer_entry *find_printer_index_by_hnd(pipes_struct *p, POLICY_HND *hnd)
+{
+ Printer_entry *find_printer = NULL;
+
+ if(!find_policy_by_hnd(p,hnd,(void **)&find_printer)) {
+ DEBUG(2,("find_printer_index_by_hnd: Printer handle not found: "));
+ return NULL;
+ }
+
+ return find_printer;
+}
+
+/****************************************************************************
+ look for a printer object cached on an open printer handle
+****************************************************************************/
+
+WERROR find_printer_in_print_hnd_cache( TALLOC_CTX *ctx, NT_PRINTER_INFO_LEVEL_2 **info2,
+ const char *printername )
+{
+ Printer_entry *p;
+
+ DEBUG(10,("find_printer_in_print_hnd_cache: printer [%s]\n", printername));
+
+ for ( p=printers_list; p; p=p->next )
+ {
+ if ( p->printer_type==PRINTER_HANDLE_IS_PRINTER
+ && p->printer_info
+ && StrCaseCmp(p->dev.handlename, printername) == 0 )
+ {
+ DEBUG(10,("Found printer\n"));
+ *info2 = dup_printer_2( ctx, p->printer_info->info_2 );
+ if ( *info2 )
+ return WERR_OK;
+ }
+ }
+
+ return WERR_INVALID_PRINTER_NAME;
+}
+
+/****************************************************************************
+ destroy any cached printer_info_2 structures on open handles
+****************************************************************************/
+
+void invalidate_printer_hnd_cache( char *printername )
+{
+ Printer_entry *p;
+
+ DEBUG(10,("invalidate_printer_hnd_cache: printer [%s]\n", printername));
+
+ for ( p=printers_list; p; p=p->next )
+ {
+ if ( p->printer_type==PRINTER_HANDLE_IS_PRINTER
+ && StrCaseCmp(p->dev.handlename, printername)==0)
+ {
+ DEBUG(10,("invalidating printer_info cache for handl:\n"));
+ free_a_printer( &p->printer_info, 2 );
+ p->printer_info = NULL;
+ }
+ }
+
+ return;
+}
+/****************************************************************************
+ Close printer index by handle.
+****************************************************************************/
+
+static BOOL close_printer_handle(pipes_struct *p, POLICY_HND *hnd)
+{
+ Printer_entry *Printer = find_printer_index_by_hnd(p, hnd);
+
+ if (!Printer) {
+ DEBUG(2,("close_printer_handle: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(hnd)));
+ return False;
+ }
+
+ close_policy_hnd(p, hnd);
+
+ return True;
+}
+
+/****************************************************************************
+ Delete a printer given a handle.
+****************************************************************************/
+
+static WERROR delete_printer_handle(pipes_struct *p, POLICY_HND *hnd)
+{
+ Printer_entry *Printer = find_printer_index_by_hnd(p, hnd);
+
+ if (!Printer) {
+ DEBUG(2,("delete_printer_handle: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(hnd)));
+ return WERR_BADFID;
+ }
+
+ /*
+ * It turns out that Windows allows delete printer on a handle
+ * opened by an admin user, then used on a pipe handle created
+ * by an anonymous user..... but they're working on security.... riiight !
+ * JRA.
+ */
+
+ if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) {
+ DEBUG(3, ("delete_printer_handle: denied by handle\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+#if 0
+ /* Check calling user has permission to delete printer. Note that
+ since we set the snum parameter to -1 only administrators can
+ delete the printer. This stops people with the Full Control
+ permission from deleting the printer. */
+
+ if (!print_access_check(NULL, -1, PRINTER_ACCESS_ADMINISTER)) {
+ DEBUG(3, ("printer delete denied by security descriptor\n"));
+ return WERR_ACCESS_DENIED;
+ }
+#endif
+
+ if (del_a_printer(Printer->dev.handlename) != 0) {
+ DEBUG(3,("Error deleting printer %s\n", Printer->dev.handlename));
+ return WERR_BADFID;
+ }
+
+ if (*lp_deleteprinter_cmd()) {
+
+ char *cmd = lp_deleteprinter_cmd();
+ pstring command;
+ int ret;
+
+ /* Printer->dev.handlename equals portname equals sharename */
+ slprintf(command, sizeof(command)-1, "%s \"%s\"", cmd,
+ Printer->dev.handlename);
+
+ DEBUG(10,("Running [%s]\n", command));
+ ret = smbrun(command, NULL);
+ if (ret != 0) {
+ return WERR_BADFID; /* What to return here? */
+ }
+ DEBUGADD(10,("returned [%d]\n", ret));
+
+ /* Send SIGHUP to process group... is there a better way? */
+ kill(0, SIGHUP);
+
+ /* go ahead and re-read the services immediately */
+ reload_services( False );
+
+ if ( lp_servicenumber( Printer->dev.handlename ) < 0 )
+ return WERR_ACCESS_DENIED;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ Return the snum of a printer corresponding to an handle.
+****************************************************************************/
+
+static BOOL get_printer_snum(pipes_struct *p, POLICY_HND *hnd, int *number)
+{
+ Printer_entry *Printer = find_printer_index_by_hnd(p, hnd);
+
+ if (!Printer) {
+ DEBUG(2,("get_printer_snum: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(hnd)));
+ return False;
+ }
+
+ switch (Printer->printer_type) {
+ case PRINTER_HANDLE_IS_PRINTER:
+ DEBUG(4,("short name:%s\n", Printer->dev.handlename));
+ *number = print_queue_snum(Printer->dev.handlename);
+ return (*number != -1);
+ case PRINTER_HANDLE_IS_PRINTSERVER:
+ return False;
+ default:
+ return False;
+ }
+}
+
+/****************************************************************************
+ Set printer handle type.
+ Check if it's \\server or \\server\printer
+****************************************************************************/
+
+static BOOL set_printer_hnd_printertype(Printer_entry *Printer, char *handlename)
+{
+ DEBUG(3,("Setting printer type=%s\n", handlename));
+
+ if ( strlen(handlename) < 3 ) {
+ DEBUGADD(4,("A print server must have at least 1 char ! %s\n", handlename));
+ return False;
+ }
+
+ /* it's a print server */
+ if (*handlename=='\\' && *(handlename+1)=='\\' && !strchr_m(handlename+2, '\\')) {
+ DEBUGADD(4,("Printer is a print server\n"));
+ Printer->printer_type = PRINTER_HANDLE_IS_PRINTSERVER;
+ }
+ /* it's a printer */
+ else {
+ DEBUGADD(4,("Printer is a printer\n"));
+ Printer->printer_type = PRINTER_HANDLE_IS_PRINTER;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Set printer handle name.
+****************************************************************************/
+
+static BOOL set_printer_hnd_name(Printer_entry *Printer, char *handlename)
+{
+ int snum;
+ int n_services=lp_numservices();
+ char *aprinter;
+ fstring sname;
+ BOOL found=False;
+
+ DEBUG(4,("Setting printer name=%s (len=%lu)\n", handlename, (unsigned long)strlen(handlename)));
+
+ if (Printer->printer_type==PRINTER_HANDLE_IS_PRINTSERVER) {
+ ZERO_STRUCT(Printer->dev.printerservername);
+ strncpy(Printer->dev.printerservername, handlename, strlen(handlename));
+ return True;
+ }
+
+ if (Printer->printer_type!=PRINTER_HANDLE_IS_PRINTER)
+ return False;
+
+ if (*handlename=='\\') {
+ aprinter=strchr_m(handlename+2, '\\');
+ aprinter++;
+ }
+ else {
+ aprinter=handlename;
+ }
+
+ DEBUGADD(5,("searching for [%s] (len=%lu)\n", aprinter, (unsigned long)strlen(aprinter)));
+
+ /*
+ * The original code allowed smbd to store a printer name that
+ * was different from the share name. This is not possible
+ * anymore, so I've simplified this loop greatly. Here
+ * we are just verifying that the printer name is a valid
+ * printer service defined in smb.conf
+ * --jerry [Fri Feb 15 11:17:46 CST 2002]
+ */
+
+ for (snum=0; snum<n_services; snum++) {
+
+ if ( !(lp_snum_ok(snum) && lp_print_ok(snum) ) )
+ continue;
+
+ fstrcpy(sname, lp_servicename(snum));
+
+ DEBUGADD(5,("share:%s\n",sname));
+
+ if (! StrCaseCmp(sname, aprinter)) {
+ found = True;
+ break;
+ }
+
+ }
+
+
+ if (!found) {
+ DEBUGADD(4,("Printer not found\n"));
+ return False;
+ }
+
+ DEBUGADD(4,("set_printer_hnd_name: Printer found: %s -> %s\n", aprinter, sname));
+
+ ZERO_STRUCT(Printer->dev.handlename);
+ fstrcpy(Printer->dev.handlename, sname);
+
+ return True;
+}
+
+/****************************************************************************
+ Find first available printer slot. creates a printer handle for you.
+ ****************************************************************************/
+
+static BOOL open_printer_hnd(pipes_struct *p, POLICY_HND *hnd, char *name, uint32 access_granted)
+{
+ Printer_entry *new_printer;
+
+ DEBUG(10,("open_printer_hnd: name [%s]\n", name));
+
+ if((new_printer=(Printer_entry *)malloc(sizeof(Printer_entry))) == NULL)
+ return False;
+
+ ZERO_STRUCTP(new_printer);
+
+ if (!create_policy_hnd(p, hnd, free_printer_entry, new_printer)) {
+ SAFE_FREE(new_printer);
+ return False;
+ }
+
+ /* Add to the internal list. */
+ DLIST_ADD(printers_list, new_printer);
+
+ new_printer->notify.option=NULL;
+
+ if ( !(new_printer->ctx = talloc_init("Printer Entry [%p]", hnd)) ) {
+ DEBUG(0,("open_printer_hnd: talloc_init() failed!\n"));
+ close_printer_handle(p, hnd);
+ return False;
+ }
+
+ if (!set_printer_hnd_printertype(new_printer, name)) {
+ close_printer_handle(p, hnd);
+ return False;
+ }
+
+ if (!set_printer_hnd_name(new_printer, name)) {
+ close_printer_handle(p, hnd);
+ return False;
+ }
+
+ new_printer->access_granted = access_granted;
+
+ DEBUG(5, ("%d printer handles active\n", (int)p->pipe_handles->count ));
+
+ return True;
+}
+
+/****************************************************************************
+ Allocate more memory for a BUFFER.
+****************************************************************************/
+
+static BOOL alloc_buffer_size(NEW_BUFFER *buffer, uint32 buffer_size)
+{
+ prs_struct *ps;
+ uint32 extra_space;
+ uint32 old_offset;
+
+ ps= &buffer->prs;
+
+ /* damn, I'm doing the reverse operation of prs_grow() :) */
+ if (buffer_size < prs_data_size(ps))
+ extra_space=0;
+ else
+ extra_space = buffer_size - prs_data_size(ps);
+
+ /*
+ * save the offset and move to the end of the buffer
+ * prs_grow() checks the extra_space against the offset
+ */
+ old_offset=prs_offset(ps);
+ prs_set_offset(ps, prs_data_size(ps));
+
+ if (!prs_grow(ps, extra_space))
+ return False;
+
+ prs_set_offset(ps, old_offset);
+
+ buffer->string_at_end=prs_data_size(ps);
+
+ return True;
+}
+
+/***************************************************************************
+ check to see if the client motify handle is monitoring the notification
+ given by (notify_type, notify_field).
+ **************************************************************************/
+
+static BOOL is_monitoring_event_flags(uint32 flags, uint16 notify_type,
+ uint16 notify_field)
+{
+ return True;
+}
+
+static BOOL is_monitoring_event(Printer_entry *p, uint16 notify_type,
+ uint16 notify_field)
+{
+ SPOOL_NOTIFY_OPTION *option = p->notify.option;
+ uint32 i, j;
+
+ /*
+ * Flags should always be zero when the change notify
+ * is registered by the client's spooler. A user Win32 app
+ * might use the flags though instead of the NOTIFY_OPTION_INFO
+ * --jerry
+ */
+
+ if (p->notify.flags)
+ return is_monitoring_event_flags(
+ p->notify.flags, notify_type, notify_field);
+
+ for (i = 0; i < option->count; i++) {
+
+ /* Check match for notify_type */
+
+ if (option->ctr.type[i].type != notify_type)
+ continue;
+
+ /* Check match for field */
+
+ for (j = 0; j < option->ctr.type[i].count; j++) {
+ if (option->ctr.type[i].fields[j] == notify_field) {
+ return True;
+ }
+ }
+ }
+
+ DEBUG(10, ("%s is not monitoring 0x%02x/0x%02x\n",
+ (p->printer_type == PRINTER_HANDLE_IS_PRINTER) ?
+ p->dev.handlename : p->dev.printerservername,
+ notify_type, notify_field));
+
+ return False;
+}
+
+/* Convert a notification message to a SPOOL_NOTIFY_INFO_DATA struct */
+
+static void notify_one_value(struct spoolss_notify_msg *msg,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0] = msg->notify.value[0];
+ data->notify_data.value[1] = 0;
+}
+
+static void notify_string(struct spoolss_notify_msg *msg,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ TALLOC_CTX *mem_ctx)
+{
+ UNISTR2 unistr;
+
+ /* The length of the message includes the trailing \0 */
+
+ init_unistr2(&unistr, msg->notify.data, UNI_STR_TERMINATE);
+
+ data->notify_data.data.length = msg->len * 2;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, msg->len * 2);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, unistr.buffer, msg->len * 2);
+}
+
+static void notify_system_time(struct spoolss_notify_msg *msg,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ TALLOC_CTX *mem_ctx)
+{
+ SYSTEMTIME systime;
+ prs_struct ps;
+
+ if (msg->len != sizeof(time_t)) {
+ DEBUG(5, ("notify_system_time: received wrong sized message (%d)\n",
+ msg->len));
+ return;
+ }
+
+ if (!prs_init(&ps, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL)) {
+ DEBUG(5, ("notify_system_time: prs_init() failed\n"));
+ return;
+ }
+
+ if (!make_systemtime(&systime, gmtime((time_t *)msg->notify.data))) {
+ DEBUG(5, ("notify_system_time: unable to make systemtime\n"));
+ return;
+ }
+
+ if (!spoolss_io_system_time("", &ps, 0, &systime))
+ return;
+
+ data->notify_data.data.length = prs_offset(&ps);
+ data->notify_data.data.string = talloc(mem_ctx, prs_offset(&ps));
+
+ prs_copy_all_data_out((char *)data->notify_data.data.string, &ps);
+
+ prs_mem_free(&ps);
+}
+
+struct notify2_message_table {
+ const char *name;
+ void (*fn)(struct spoolss_notify_msg *msg,
+ SPOOL_NOTIFY_INFO_DATA *data, TALLOC_CTX *mem_ctx);
+};
+
+static struct notify2_message_table printer_notify_table[] = {
+ /* 0x00 */ { "PRINTER_NOTIFY_SERVER_NAME", notify_string },
+ /* 0x01 */ { "PRINTER_NOTIFY_PRINTER_NAME", notify_string },
+ /* 0x02 */ { "PRINTER_NOTIFY_SHARE_NAME", notify_string },
+ /* 0x03 */ { "PRINTER_NOTIFY_PORT_NAME", notify_string },
+ /* 0x04 */ { "PRINTER_NOTIFY_DRIVER_NAME", notify_string },
+ /* 0x05 */ { "PRINTER_NOTIFY_COMMENT", notify_string },
+ /* 0x06 */ { "PRINTER_NOTIFY_LOCATION", notify_string },
+ /* 0x07 */ { "PRINTER_NOTIFY_DEVMODE", NULL },
+ /* 0x08 */ { "PRINTER_NOTIFY_SEPFILE", notify_string },
+ /* 0x09 */ { "PRINTER_NOTIFY_PRINT_PROCESSOR", notify_string },
+ /* 0x0a */ { "PRINTER_NOTIFY_PARAMETERS", NULL },
+ /* 0x0b */ { "PRINTER_NOTIFY_DATATYPE", notify_string },
+ /* 0x0c */ { "PRINTER_NOTIFY_SECURITY_DESCRIPTOR", NULL },
+ /* 0x0d */ { "PRINTER_NOTIFY_ATTRIBUTES", notify_one_value },
+ /* 0x0e */ { "PRINTER_NOTIFY_PRIORITY", notify_one_value },
+ /* 0x0f */ { "PRINTER_NOTIFY_DEFAULT_PRIORITY", NULL },
+ /* 0x10 */ { "PRINTER_NOTIFY_START_TIME", NULL },
+ /* 0x11 */ { "PRINTER_NOTIFY_UNTIL_TIME", NULL },
+ /* 0x12 */ { "PRINTER_NOTIFY_STATUS", notify_one_value },
+};
+
+static struct notify2_message_table job_notify_table[] = {
+ /* 0x00 */ { "JOB_NOTIFY_PRINTER_NAME", NULL },
+ /* 0x01 */ { "JOB_NOTIFY_MACHINE_NAME", NULL },
+ /* 0x02 */ { "JOB_NOTIFY_PORT_NAME", NULL },
+ /* 0x03 */ { "JOB_NOTIFY_USER_NAME", notify_string },
+ /* 0x04 */ { "JOB_NOTIFY_NOTIFY_NAME", NULL },
+ /* 0x05 */ { "JOB_NOTIFY_DATATYPE", NULL },
+ /* 0x06 */ { "JOB_NOTIFY_PRINT_PROCESSOR", NULL },
+ /* 0x07 */ { "JOB_NOTIFY_PARAMETERS", NULL },
+ /* 0x08 */ { "JOB_NOTIFY_DRIVER_NAME", NULL },
+ /* 0x09 */ { "JOB_NOTIFY_DEVMODE", NULL },
+ /* 0x0a */ { "JOB_NOTIFY_STATUS", notify_one_value },
+ /* 0x0b */ { "JOB_NOTIFY_STATUS_STRING", NULL },
+ /* 0x0c */ { "JOB_NOTIFY_SECURITY_DESCRIPTOR", NULL },
+ /* 0x0d */ { "JOB_NOTIFY_DOCUMENT", notify_string },
+ /* 0x0e */ { "JOB_NOTIFY_PRIORITY", NULL },
+ /* 0x0f */ { "JOB_NOTIFY_POSITION", NULL },
+ /* 0x10 */ { "JOB_NOTIFY_SUBMITTED", notify_system_time },
+ /* 0x11 */ { "JOB_NOTIFY_START_TIME", NULL },
+ /* 0x12 */ { "JOB_NOTIFY_UNTIL_TIME", NULL },
+ /* 0x13 */ { "JOB_NOTIFY_TIME", NULL },
+ /* 0x14 */ { "JOB_NOTIFY_TOTAL_PAGES", notify_one_value },
+ /* 0x15 */ { "JOB_NOTIFY_PAGES_PRINTED", NULL },
+ /* 0x16 */ { "JOB_NOTIFY_TOTAL_BYTES", notify_one_value },
+ /* 0x17 */ { "JOB_NOTIFY_BYTES_PRINTED", NULL },
+};
+
+
+/***********************************************************************
+ Allocate talloc context for container object
+ **********************************************************************/
+
+static void notify_msg_ctr_init( SPOOLSS_NOTIFY_MSG_CTR *ctr )
+{
+ if ( !ctr )
+ return;
+
+ ctr->ctx = talloc_init("notify_msg_ctr_init %p", ctr);
+
+ return;
+}
+
+/***********************************************************************
+ release all allocated memory and zero out structure
+ **********************************************************************/
+
+static void notify_msg_ctr_destroy( SPOOLSS_NOTIFY_MSG_CTR *ctr )
+{
+ if ( !ctr )
+ return;
+
+ if ( ctr->ctx )
+ talloc_destroy(ctr->ctx);
+
+ ZERO_STRUCTP(ctr);
+
+ return;
+}
+
+/***********************************************************************
+ **********************************************************************/
+
+static TALLOC_CTX* notify_ctr_getctx( SPOOLSS_NOTIFY_MSG_CTR *ctr )
+{
+ if ( !ctr )
+ return NULL;
+
+ return ctr->ctx;
+}
+
+/***********************************************************************
+ **********************************************************************/
+
+static SPOOLSS_NOTIFY_MSG_GROUP* notify_ctr_getgroup( SPOOLSS_NOTIFY_MSG_CTR *ctr, uint32 idx )
+{
+ if ( !ctr || !ctr->msg_groups )
+ return NULL;
+
+ if ( idx >= ctr->num_groups )
+ return NULL;
+
+ return &ctr->msg_groups[idx];
+
+}
+
+/***********************************************************************
+ How many groups of change messages do we have ?
+ **********************************************************************/
+
+static int notify_msg_ctr_numgroups( SPOOLSS_NOTIFY_MSG_CTR *ctr )
+{
+ if ( !ctr )
+ return 0;
+
+ return ctr->num_groups;
+}
+
+/***********************************************************************
+ Add a SPOOLSS_NOTIFY_MSG_CTR to the correct group
+ **********************************************************************/
+
+static int notify_msg_ctr_addmsg( SPOOLSS_NOTIFY_MSG_CTR *ctr, SPOOLSS_NOTIFY_MSG *msg )
+{
+ SPOOLSS_NOTIFY_MSG_GROUP *groups = NULL;
+ SPOOLSS_NOTIFY_MSG_GROUP *msg_grp = NULL;
+ SPOOLSS_NOTIFY_MSG *msg_list = NULL;
+ int i, new_slot;
+
+ if ( !ctr || !msg )
+ return 0;
+
+ /* loop over all groups looking for a matching printer name */
+
+ for ( i=0; i<ctr->num_groups; i++ ) {
+ if ( strcmp(ctr->msg_groups[i].printername, msg->printer) == 0 )
+ break;
+ }
+
+ /* add a new group? */
+
+ if ( i == ctr->num_groups ) {
+ ctr->num_groups++;
+
+ if ( !(groups = talloc_realloc( ctr->ctx, ctr->msg_groups, sizeof(SPOOLSS_NOTIFY_MSG_GROUP)*ctr->num_groups)) ) {
+ DEBUG(0,("notify_msg_ctr_addmsg: talloc_realloc() failed!\n"));
+ return 0;
+ }
+ ctr->msg_groups = groups;
+
+ /* clear the new entry and set the printer name */
+
+ ZERO_STRUCT( ctr->msg_groups[ctr->num_groups-1] );
+ fstrcpy( ctr->msg_groups[ctr->num_groups-1].printername, msg->printer );
+ }
+
+ /* add the change messages; 'i' is the correct index now regardless */
+
+ msg_grp = &ctr->msg_groups[i];
+
+ msg_grp->num_msgs++;
+
+ if ( !(msg_list = talloc_realloc( ctr->ctx, msg_grp->msgs, sizeof(SPOOLSS_NOTIFY_MSG)*msg_grp->num_msgs )) ) {
+ DEBUG(0,("notify_msg_ctr_addmsg: talloc_realloc() failed for new message [%d]!\n", msg_grp->num_msgs));
+ return 0;
+ }
+ msg_grp->msgs = msg_list;
+
+ new_slot = msg_grp->num_msgs-1;
+ memcpy( &msg_grp->msgs[new_slot], msg, sizeof(SPOOLSS_NOTIFY_MSG) );
+
+ /* need to allocate own copy of data */
+
+ if ( msg->len != 0 )
+ msg_grp->msgs[new_slot].notify.data = talloc_memdup( ctr->ctx, msg->notify.data, msg->len );
+
+ return ctr->num_groups;
+}
+
+/***********************************************************************
+ Send a change notication message on all handles which have a call
+ back registered
+ **********************************************************************/
+
+static void send_notify2_changes( SPOOLSS_NOTIFY_MSG_CTR *ctr, uint32 idx )
+{
+ Printer_entry *p;
+ TALLOC_CTX *mem_ctx = notify_ctr_getctx( ctr );
+ SPOOLSS_NOTIFY_MSG_GROUP *msg_group = notify_ctr_getgroup( ctr, idx );
+ SPOOLSS_NOTIFY_MSG *messages;
+ int sending_msg_count;
+
+ if ( !msg_group ) {
+ DEBUG(5,("send_notify2_changes() called with no msg group!\n"));
+ return;
+ }
+
+ messages = msg_group->msgs;
+
+ if ( !messages ) {
+ DEBUG(5,("send_notify2_changes() called with no messages!\n"));
+ return;
+ }
+
+ DEBUG(8,("send_notify2_changes: Enter...[%s]\n", msg_group->printername));
+
+ /* loop over all printers */
+
+ for (p = printers_list; p; p = p->next) {
+ SPOOL_NOTIFY_INFO_DATA *data;
+ uint32 data_len = 0;
+ uint32 id;
+ int i;
+
+ /* Is there notification on this handle? */
+
+ if ( !p->notify.client_connected )
+ continue;
+
+ DEBUG(10,("Client connected! [%s]\n", p->dev.handlename));
+
+ /* For this printer? Print servers always receive
+ notifications. */
+
+ if ( ( p->printer_type == PRINTER_HANDLE_IS_PRINTER ) &&
+ ( !strequal(msg_group->printername, p->dev.handlename) ) )
+ continue;
+
+ DEBUG(10,("Our printer\n"));
+
+ /* allocate the max entries possible */
+
+ data = talloc( mem_ctx, msg_group->num_msgs*sizeof(SPOOL_NOTIFY_INFO_DATA) );
+ ZERO_STRUCTP(data);
+
+ /* build the array of change notifications */
+
+ sending_msg_count = 0;
+
+ for ( i=0; i<msg_group->num_msgs; i++ ) {
+ SPOOLSS_NOTIFY_MSG *msg = &messages[i];
+
+ /* Are we monitoring this event? */
+
+ if (!is_monitoring_event(p, msg->type, msg->field))
+ continue;
+
+ sending_msg_count++;
+
+
+ DEBUG(10,("process_notify2_message: Sending message type [%x] field [%x] for printer [%s]\n",
+ msg->type, msg->field, p->dev.handlename));
+
+ /*
+ * if the is a printer notification handle and not a job notification
+ * type, then set the id to 0. Other wise just use what was specified
+ * in the message.
+ *
+ * When registering change notification on a print server handle
+ * we always need to send back the id (snum) matching the printer
+ * for which the change took place. For change notify registered
+ * on a printer handle, this does not matter and the id should be 0.
+ *
+ * --jerry
+ */
+
+ if ( ( p->printer_type == PRINTER_HANDLE_IS_PRINTER ) && ( msg->type == PRINTER_NOTIFY_TYPE ) )
+ id = 0;
+ else
+ id = msg->id;
+
+
+ /* Convert unix jobid to smb jobid */
+
+ if (msg->flags & SPOOLSS_NOTIFY_MSG_UNIX_JOBID) {
+ id = sysjob_to_jobid(msg->id);
+
+ if (id == -1) {
+ DEBUG(3, ("no such unix jobid %d\n", msg->id));
+ goto done;
+ }
+ }
+
+ construct_info_data( &data[data_len], msg->type, msg->field, id );
+
+ switch(msg->type) {
+ case PRINTER_NOTIFY_TYPE:
+ if ( printer_notify_table[msg->field].fn )
+ printer_notify_table[msg->field].fn(msg, &data[data_len], mem_ctx);
+ break;
+
+ case JOB_NOTIFY_TYPE:
+ if ( job_notify_table[msg->field].fn )
+ job_notify_table[msg->field].fn(msg, &data[data_len], mem_ctx);
+ break;
+
+ default:
+ DEBUG(5, ("Unknown notification type %d\n", msg->type));
+ goto done;
+ }
+
+ data_len++;
+ }
+
+ if ( sending_msg_count ) {
+ cli_spoolss_rrpcn( &notify_cli, mem_ctx, &p->notify.client_hnd,
+ data_len, data, p->notify.change, 0 );
+ }
+ }
+
+done:
+ DEBUG(8,("send_notify2_changes: Exit...\n"));
+ return;
+}
+
+/***********************************************************************
+ **********************************************************************/
+
+static BOOL notify2_unpack_msg( SPOOLSS_NOTIFY_MSG *msg, struct timeval *tv, void *buf, size_t len )
+{
+
+ uint32 tv_sec, tv_usec;
+ size_t offset = 0;
+
+ /* Unpack message */
+
+ offset += tdb_unpack((char *)buf + offset, len - offset, "f",
+ msg->printer);
+
+ offset += tdb_unpack((char *)buf + offset, len - offset, "ddddddd",
+ &tv_sec, &tv_usec,
+ &msg->type, &msg->field, &msg->id, &msg->len, &msg->flags);
+
+ if (msg->len == 0)
+ tdb_unpack((char *)buf + offset, len - offset, "dd",
+ &msg->notify.value[0], &msg->notify.value[1]);
+ else
+ tdb_unpack((char *)buf + offset, len - offset, "B",
+ &msg->len, &msg->notify.data);
+
+ DEBUG(3, ("notify2_unpack_msg: got NOTIFY2 message for printer %s, jobid %u type %d, field 0x%02x, flags 0x%04x\n",
+ msg->printer, (unsigned int)msg->id, msg->type, msg->field, msg->flags));
+
+ tv->tv_sec = tv_sec;
+ tv->tv_usec = tv_usec;
+
+ if (msg->len == 0)
+ DEBUG(3, ("notify2_unpack_msg: value1 = %d, value2 = %d\n", msg->notify.value[0],
+ msg->notify.value[1]));
+ else
+ dump_data(3, msg->notify.data, msg->len);
+
+ return True;
+}
+
+/********************************************************************
+ Receive a notify2 message list
+ ********************************************************************/
+
+static void receive_notify2_message_list(int msg_type, pid_t src, void *msg, size_t len)
+{
+ size_t msg_count, i;
+ char *buf = (char *)msg;
+ char *msg_ptr;
+ size_t msg_len;
+ SPOOLSS_NOTIFY_MSG notify;
+ SPOOLSS_NOTIFY_MSG_CTR messages;
+ int num_groups;
+
+ if (len < 4) {
+ DEBUG(0,("receive_notify2_message_list: bad message format (len < 4)!\n"));
+ return;
+ }
+
+ msg_count = IVAL(buf, 0);
+ msg_ptr = buf + 4;
+
+ DEBUG(5, ("receive_notify2_message_list: got %lu messages in list\n", (unsigned long)msg_count));
+
+ if (msg_count == 0) {
+ DEBUG(0,("receive_notify2_message_list: bad message format (msg_count == 0) !\n"));
+ return;
+ }
+
+ /* initialize the container */
+
+ ZERO_STRUCT( messages );
+ notify_msg_ctr_init( &messages );
+
+ /*
+ * build message groups for each printer identified
+ * in a change_notify msg. Remember that a PCN message
+ * includes the handle returned for the srv_spoolss_replyopenprinter()
+ * call. Therefore messages are grouped according to printer handle.
+ */
+
+ for ( i=0; i<msg_count; i++ ) {
+ struct timeval msg_tv;
+
+ if (msg_ptr + 4 - buf > len) {
+ DEBUG(0,("receive_notify2_message_list: bad message format (len > buf_size) !\n"));
+ return;
+ }
+
+ msg_len = IVAL(msg_ptr,0);
+ msg_ptr += 4;
+
+ if (msg_ptr + msg_len - buf > len) {
+ DEBUG(0,("receive_notify2_message_list: bad message format (bad len) !\n"));
+ return;
+ }
+
+ /* unpack messages */
+
+ ZERO_STRUCT( notify );
+ notify2_unpack_msg( &notify, &msg_tv, msg_ptr, msg_len );
+ msg_ptr += msg_len;
+
+ /* add to correct list in container */
+
+ notify_msg_ctr_addmsg( &messages, &notify );
+
+ /* free memory that might have been allocated by notify2_unpack_msg() */
+
+ if ( notify.len != 0 )
+ SAFE_FREE( notify.notify.data );
+ }
+
+ /* process each group of messages */
+
+ num_groups = notify_msg_ctr_numgroups( &messages );
+ for ( i=0; i<num_groups; i++ )
+ send_notify2_changes( &messages, i );
+
+
+ /* cleanup */
+
+ DEBUG(10,("receive_notify2_message_list: processed %u messages\n", (uint32)msg_count ));
+
+ notify_msg_ctr_destroy( &messages );
+
+ return;
+}
+
+/********************************************************************
+ Send a message to ourself about new driver being installed
+ so we can upgrade the information for each printer bound to this
+ driver
+ ********************************************************************/
+
+static BOOL srv_spoolss_drv_upgrade_printer(char* drivername)
+{
+ int len = strlen(drivername);
+
+ if (!len)
+ return False;
+
+ DEBUG(10,("srv_spoolss_drv_upgrade_printer: Sending message about driver upgrade [%s]\n",
+ drivername));
+
+ message_send_pid(sys_getpid(), MSG_PRINTER_DRVUPGRADE, drivername, len+1, False);
+
+ return True;
+}
+
+/**********************************************************************
+ callback to receive a MSG_PRINTER_DRVUPGRADE message and interate
+ over all printers, upgrading ones as necessary
+ **********************************************************************/
+
+void do_drv_upgrade_printer(int msg_type, pid_t src, void *buf, size_t len)
+{
+ fstring drivername;
+ int snum;
+ int n_services = lp_numservices();
+
+ len = MIN(len,sizeof(drivername)-1);
+ strncpy(drivername, buf, len);
+
+ DEBUG(10,("do_drv_upgrade_printer: Got message for new driver [%s]\n", drivername ));
+
+ /* Iterate the printer list */
+
+ for (snum=0; snum<n_services; snum++)
+ {
+ if (lp_snum_ok(snum) && lp_print_ok(snum) )
+ {
+ WERROR result;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ result = get_a_printer(NULL, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(result))
+ continue;
+
+ if (printer && printer->info_2 && !strcmp(drivername, printer->info_2->drivername))
+ {
+ DEBUG(6,("Updating printer [%s]\n", printer->info_2->printername));
+
+ /* all we care about currently is the change_id */
+
+ result = mod_a_printer(*printer, 2);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(3,("do_drv_upgrade_printer: mod_a_printer() failed with status [%s]\n",
+ dos_errstr(result)));
+ }
+ }
+
+ free_a_printer(&printer, 2);
+ }
+ }
+
+ /* all done */
+}
+
+/********************************************************************
+ Update the cache for all printq's with a registered client
+ connection
+ ********************************************************************/
+
+void update_monitored_printq_cache( void )
+{
+ Printer_entry *printer = printers_list;
+ int snum;
+
+ /* loop through all printers and update the cache where
+ client_connected == True */
+ while ( printer )
+ {
+ if ( (printer->printer_type == PRINTER_HANDLE_IS_PRINTER)
+ && printer->notify.client_connected )
+ {
+ snum = print_queue_snum(printer->dev.handlename);
+ print_queue_status( snum, NULL, NULL );
+ }
+
+ printer = printer->next;
+ }
+
+ return;
+}
+/********************************************************************
+ Send a message to ourself about new driver being installed
+ so we can upgrade the information for each printer bound to this
+ driver
+ ********************************************************************/
+
+static BOOL srv_spoolss_reset_printerdata(char* drivername)
+{
+ int len = strlen(drivername);
+
+ if (!len)
+ return False;
+
+ DEBUG(10,("srv_spoolss_reset_printerdata: Sending message about resetting printerdata [%s]\n",
+ drivername));
+
+ message_send_pid(sys_getpid(), MSG_PRINTERDATA_INIT_RESET, drivername, len+1, False);
+
+ return True;
+}
+
+/**********************************************************************
+ callback to receive a MSG_PRINTERDATA_INIT_RESET message and interate
+ over all printers, resetting printer data as neessary
+ **********************************************************************/
+
+void reset_all_printerdata(int msg_type, pid_t src, void *buf, size_t len)
+{
+ fstring drivername;
+ int snum;
+ int n_services = lp_numservices();
+
+ len = MIN( len, sizeof(drivername)-1 );
+ strncpy( drivername, buf, len );
+
+ DEBUG(10,("reset_all_printerdata: Got message for new driver [%s]\n", drivername ));
+
+ /* Iterate the printer list */
+
+ for ( snum=0; snum<n_services; snum++ )
+ {
+ if ( lp_snum_ok(snum) && lp_print_ok(snum) )
+ {
+ WERROR result;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ result = get_a_printer( NULL, &printer, 2, lp_const_servicename(snum) );
+ if ( !W_ERROR_IS_OK(result) )
+ continue;
+
+ /*
+ * if the printer is bound to the driver,
+ * then reset to the new driver initdata
+ */
+
+ if ( printer && printer->info_2 && !strcmp(drivername, printer->info_2->drivername) )
+ {
+ DEBUG(6,("reset_all_printerdata: Updating printer [%s]\n", printer->info_2->printername));
+
+ if ( !set_driver_init(printer, 2) ) {
+ DEBUG(5,("reset_all_printerdata: Error resetting printer data for printer [%s], driver [%s]!\n",
+ printer->info_2->printername, printer->info_2->drivername));
+ }
+
+ result = mod_a_printer( *printer, 2 );
+ if ( !W_ERROR_IS_OK(result) ) {
+ DEBUG(3,("reset_all_printerdata: mod_a_printer() failed! (%s)\n",
+ get_dos_error_msg(result)));
+ }
+ }
+
+ free_a_printer( &printer, 2 );
+ }
+ }
+
+ /* all done */
+
+ return;
+}
+
+/********************************************************************
+ Copy routines used by convert_to_openprinterex()
+ *******************************************************************/
+
+static DEVICEMODE* dup_devicemode(TALLOC_CTX *ctx, DEVICEMODE *devmode)
+{
+ DEVICEMODE *d;
+ int len;
+
+ if (!devmode)
+ return NULL;
+
+ DEBUG (8,("dup_devmode\n"));
+
+ /* bulk copy first */
+
+ d = talloc_memdup(ctx, devmode, sizeof(DEVICEMODE));
+ if (!d)
+ return NULL;
+
+ /* dup the pointer members separately */
+
+ len = unistrlen(devmode->devicename.buffer);
+ if (len != -1) {
+ d->devicename.buffer = talloc(ctx, len*2);
+ if (unistrcpy(d->devicename.buffer, devmode->devicename.buffer) != len)
+ return NULL;
+ }
+
+
+ len = unistrlen(devmode->formname.buffer);
+ if (len != -1) {
+ d->devicename.buffer = talloc(ctx, len*2);
+ if (unistrcpy(d->formname.buffer, devmode->formname.buffer) != len)
+ return NULL;
+ }
+
+ d->private = talloc_memdup(ctx, devmode->private, devmode->driverextra);
+
+ return d;
+}
+
+static void copy_devmode_ctr(TALLOC_CTX *ctx, DEVMODE_CTR *new_ctr, DEVMODE_CTR *ctr)
+{
+ if (!new_ctr || !ctr)
+ return;
+
+ DEBUG(8,("copy_devmode_ctr\n"));
+
+ new_ctr->size = ctr->size;
+ new_ctr->devmode_ptr = ctr->devmode_ptr;
+
+ if(ctr->devmode_ptr)
+ new_ctr->devmode = dup_devicemode(ctx, ctr->devmode);
+}
+
+static void copy_printer_default(TALLOC_CTX *ctx, PRINTER_DEFAULT *new_def, PRINTER_DEFAULT *def)
+{
+ if (!new_def || !def)
+ return;
+
+ DEBUG(8,("copy_printer_defaults\n"));
+
+ new_def->datatype_ptr = def->datatype_ptr;
+
+ if (def->datatype_ptr)
+ copy_unistr2(&new_def->datatype, &def->datatype);
+
+ copy_devmode_ctr(ctx, &new_def->devmode_cont, &def->devmode_cont);
+
+ new_def->access_required = def->access_required;
+}
+
+/********************************************************************
+ * Convert a SPOOL_Q_OPEN_PRINTER structure to a
+ * SPOOL_Q_OPEN_PRINTER_EX structure
+ ********************************************************************/
+
+static void convert_to_openprinterex(TALLOC_CTX *ctx, SPOOL_Q_OPEN_PRINTER_EX *q_u_ex, SPOOL_Q_OPEN_PRINTER *q_u)
+{
+ if (!q_u_ex || !q_u)
+ return;
+
+ DEBUG(8,("convert_to_openprinterex\n"));
+
+ q_u_ex->printername_ptr = q_u->printername_ptr;
+
+ if (q_u->printername_ptr)
+ copy_unistr2(&q_u_ex->printername, &q_u->printername);
+
+ copy_printer_default(ctx, &q_u_ex->printer_default, &q_u->printer_default);
+}
+
+/********************************************************************
+ * spoolss_open_printer
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+WERROR _spoolss_open_printer(pipes_struct *p, SPOOL_Q_OPEN_PRINTER *q_u, SPOOL_R_OPEN_PRINTER *r_u)
+{
+ SPOOL_Q_OPEN_PRINTER_EX q_u_ex;
+ SPOOL_R_OPEN_PRINTER_EX r_u_ex;
+
+ if (!q_u || !r_u)
+ return WERR_NOMEM;
+
+ ZERO_STRUCT(q_u_ex);
+ ZERO_STRUCT(r_u_ex);
+
+ /* convert the OpenPrinter() call to OpenPrinterEx() */
+
+ convert_to_openprinterex(p->mem_ctx, &q_u_ex, q_u);
+
+ r_u_ex.status = _spoolss_open_printer_ex(p, &q_u_ex, &r_u_ex);
+
+ /* convert back to OpenPrinter() */
+
+ memcpy(r_u, &r_u_ex, sizeof(*r_u));
+
+ return r_u->status;
+}
+
+/********************************************************************
+ * spoolss_open_printer
+ *
+ * If the openprinterex rpc call contains a devmode,
+ * it's a per-user one. This per-user devmode is derivated
+ * from the global devmode. Openprinterex() contains a per-user
+ * devmode for when you do EMF printing and spooling.
+ * In the EMF case, the NT workstation is only doing half the job
+ * of rendering the page. The other half is done by running the printer
+ * driver on the server.
+ * The EMF file doesn't contain the page description (paper size, orientation, ...).
+ * The EMF file only contains what is to be printed on the page.
+ * So in order for the server to know how to print, the NT client sends
+ * a devicemode attached to the openprinterex call.
+ * But this devicemode is short lived, it's only valid for the current print job.
+ *
+ * If Samba would have supported EMF spooling, this devicemode would
+ * have been attached to the handle, to sent it to the driver to correctly
+ * rasterize the EMF file.
+ *
+ * As Samba only supports RAW spooling, we only receive a ready-to-print file,
+ * we just act as a pass-thru between windows and the printer.
+ *
+ * In order to know that Samba supports only RAW spooling, NT has to call
+ * getprinter() at level 2 (attribute field) or NT has to call startdoc()
+ * and until NT sends a RAW job, we refuse it.
+ *
+ * But to call getprinter() or startdoc(), you first need a valid handle,
+ * and to get an handle you have to call openprintex(). Hence why you have
+ * a devicemode in the openprinterex() call.
+ *
+ *
+ * Differences between NT4 and NT 2000.
+ * NT4:
+ * ---
+ * On NT4, you only have a global devicemode. This global devicemode can be changed
+ * by the administrator (or by a user with enough privs). Everytime a user
+ * wants to print, the devicemode is resetted to the default. In Word, everytime
+ * you print, the printer's characteristics are always reset to the global devicemode.
+ *
+ * NT 2000:
+ * -------
+ * In W2K, there is the notion of per-user devicemode. The first time you use
+ * a printer, a per-user devicemode is build from the global devicemode.
+ * If you change your per-user devicemode, it is saved in the registry, under the
+ * H_KEY_CURRENT_KEY sub_tree. So that everytime you print, you have your default
+ * printer preferences available.
+ *
+ * To change the per-user devicemode: it's the "Printing Preferences ..." button
+ * on the General Tab of the printer properties windows.
+ *
+ * To change the global devicemode: it's the "Printing Defaults..." button
+ * on the Advanced Tab of the printer properties window.
+ *
+ * JFM.
+ ********************************************************************/
+
+WERROR _spoolss_open_printer_ex( pipes_struct *p, SPOOL_Q_OPEN_PRINTER_EX *q_u, SPOOL_R_OPEN_PRINTER_EX *r_u)
+{
+ UNISTR2 *printername = NULL;
+ PRINTER_DEFAULT *printer_default = &q_u->printer_default;
+ POLICY_HND *handle = &r_u->handle;
+
+ fstring name;
+ int snum;
+ struct current_user user;
+ Printer_entry *Printer=NULL;
+
+ if (q_u->printername_ptr != 0)
+ printername = &q_u->printername;
+
+ if (printername == NULL)
+ return WERR_INVALID_PRINTER_NAME;
+
+ /* some sanity check because you can open a printer or a print server */
+ /* aka: \\server\printer or \\server */
+ unistr2_to_ascii(name, printername, sizeof(name)-1);
+
+ DEBUGADD(3,("checking name: %s\n",name));
+
+ if (!open_printer_hnd(p, handle, name, 0))
+ return WERR_INVALID_PRINTER_NAME;
+
+ Printer=find_printer_index_by_hnd(p, handle);
+ if (!Printer) {
+ DEBUG(0,(" _spoolss_open_printer_ex: logic error. \
+Can't find printer handle we created for printer %s\n", name ));
+ close_printer_handle(p,handle);
+ return WERR_INVALID_PRINTER_NAME;
+ }
+
+ get_current_user(&user, p);
+
+ /*
+ * First case: the user is opening the print server:
+ *
+ * Disallow MS AddPrinterWizard if parameter disables it. A Win2k
+ * client 1st tries an OpenPrinterEx with access==0, MUST be allowed.
+ *
+ * Then both Win2k and WinNT clients try an OpenPrinterEx with
+ * SERVER_ALL_ACCESS, which we allow only if the user is root (uid=0)
+ * or if the user is listed in the smb.conf printer admin parameter.
+ *
+ * Then they try OpenPrinterEx with SERVER_READ which we allow. This lets the
+ * client view printer folder, but does not show the MSAPW.
+ *
+ * Note: this test needs code to check access rights here too. Jeremy
+ * could you look at this?
+ *
+ * Second case: the user is opening a printer:
+ * NT doesn't let us connect to a printer if the connecting user
+ * doesn't have print permission.
+ */
+
+ if (Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER)
+ {
+ /* Printserver handles use global struct... */
+
+ snum = -1;
+
+ /* Map standard access rights to object specific access rights */
+
+ se_map_standard(&printer_default->access_required,
+ &printserver_std_mapping);
+
+ /* Deny any object specific bits that don't apply to print
+ servers (i.e printer and job specific bits) */
+
+ printer_default->access_required &= SPECIFIC_RIGHTS_MASK;
+
+ if (printer_default->access_required &
+ ~(SERVER_ACCESS_ADMINISTER | SERVER_ACCESS_ENUMERATE)) {
+ DEBUG(3, ("access DENIED for non-printserver bits"));
+ close_printer_handle(p, handle);
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* Allow admin access */
+
+ if ( printer_default->access_required & SERVER_ACCESS_ADMINISTER )
+ {
+ if (!lp_ms_add_printer_wizard()) {
+ close_printer_handle(p, handle);
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* if the user is not root and not a printer admin, then fail */
+
+ if ( user.uid != 0
+ && !user_in_list(uidtoname(user.uid), lp_printer_admin(snum), user.groups, user.ngroups) )
+ {
+ close_printer_handle(p, handle);
+ return WERR_ACCESS_DENIED;
+ }
+
+ printer_default->access_required = SERVER_ACCESS_ADMINISTER;
+ }
+ else
+ {
+ printer_default->access_required = SERVER_ACCESS_ENUMERATE;
+ }
+
+ DEBUG(4,("Setting print server access = %s\n", (printer_default->access_required == SERVER_ACCESS_ADMINISTER)
+ ? "SERVER_ACCESS_ADMINISTER" : "SERVER_ACCESS_ENUMERATE" ));
+
+ /* We fall through to return WERR_OK */
+
+ }
+ else
+ {
+ /* NT doesn't let us connect to a printer if the connecting user
+ doesn't have print permission. */
+
+ if (!get_printer_snum(p, handle, &snum)) {
+ close_printer_handle(p, handle);
+ return WERR_BADFID;
+ }
+
+ se_map_standard(&printer_default->access_required, &printer_std_mapping);
+
+ /* map an empty access mask to the minimum access mask */
+ if (printer_default->access_required == 0x0)
+ printer_default->access_required = PRINTER_ACCESS_USE;
+
+ /*
+ * If we are not serving the printer driver for this printer,
+ * map PRINTER_ACCESS_ADMINISTER to PRINTER_ACCESS_USE. This
+ * will keep NT clients happy --jerry
+ */
+
+ if (lp_use_client_driver(snum)
+ && (printer_default->access_required & PRINTER_ACCESS_ADMINISTER))
+ {
+ printer_default->access_required = PRINTER_ACCESS_USE;
+ }
+
+ /* check smb.conf parameters and the the sec_desc */
+
+ if (!user_ok(uidtoname(user.uid), snum, user.groups, user.ngroups) || !print_access_check(&user, snum, printer_default->access_required)) {
+ DEBUG(3, ("access DENIED for printer open\n"));
+ close_printer_handle(p, handle);
+ return WERR_ACCESS_DENIED;
+ }
+
+ if ((printer_default->access_required & SPECIFIC_RIGHTS_MASK)& ~(PRINTER_ACCESS_ADMINISTER|PRINTER_ACCESS_USE)) {
+ DEBUG(3, ("access DENIED for printer open - unknown bits\n"));
+ close_printer_handle(p, handle);
+ return WERR_ACCESS_DENIED;
+ }
+
+ if (printer_default->access_required & PRINTER_ACCESS_ADMINISTER)
+ printer_default->access_required = PRINTER_ACCESS_ADMINISTER;
+ else
+ printer_default->access_required = PRINTER_ACCESS_USE;
+
+ DEBUG(4,("Setting printer access = %s\n", (printer_default->access_required == PRINTER_ACCESS_ADMINISTER)
+ ? "PRINTER_ACCESS_ADMINISTER" : "PRINTER_ACCESS_USE" ));
+
+ }
+
+ Printer->access_granted = printer_default->access_required;
+
+ /*
+ * If the client sent a devmode in the OpenPrinter() call, then
+ * save it here in case we get a job submission on this handle
+ */
+
+ if ( (Printer->printer_type != PRINTER_HANDLE_IS_PRINTSERVER)
+ && q_u->printer_default.devmode_cont.devmode_ptr )
+ {
+ convert_devicemode( Printer->dev.handlename, q_u->printer_default.devmode_cont.devmode,
+ &Printer->nt_devmode );
+ }
+
+ /* HACK ALERT!!! Sleep for 1/3 of a second to try trigger a LAN/WAN
+ optimization in Windows 2000 clients --jerry */
+
+ if ( (printer_default->access_required == PRINTER_ACCESS_ADMINISTER)
+ && (RA_WIN2K == get_remote_arch()) )
+ {
+ DEBUG(10,("_spoolss_open_printer_ex: Enabling LAN/WAN hack for Win2k clients.\n"));
+ sys_usleep( 500000 );
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL convert_printer_info(const SPOOL_PRINTER_INFO_LEVEL *uni,
+ NT_PRINTER_INFO_LEVEL *printer, uint32 level)
+{
+ BOOL ret = True;
+
+ switch (level) {
+ case 2:
+ ret = uni_2_asc_printer_info_2(uni->info_2, &printer->info_2);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static BOOL convert_printer_driver_info(const SPOOL_PRINTER_DRIVER_INFO_LEVEL *uni,
+ NT_PRINTER_DRIVER_INFO_LEVEL *printer, uint32 level)
+{
+ BOOL result = True;
+
+ switch (level) {
+ case 3:
+ printer->info_3=NULL;
+ if (!uni_2_asc_printer_driver_3(uni->info_3, &printer->info_3))
+ result = False;
+ break;
+ case 6:
+ printer->info_6=NULL;
+ if (!uni_2_asc_printer_driver_6(uni->info_6, &printer->info_6))
+ result = False;
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+BOOL convert_devicemode(const char *printername, const DEVICEMODE *devmode,
+ NT_DEVICEMODE **pp_nt_devmode)
+{
+ NT_DEVICEMODE *nt_devmode = *pp_nt_devmode;
+
+ /*
+ * Ensure nt_devmode is a valid pointer
+ * as we will be overwriting it.
+ */
+
+ if (nt_devmode == NULL) {
+ DEBUG(5, ("convert_devicemode: allocating a generic devmode\n"));
+ if ((nt_devmode = construct_nt_devicemode(printername)) == NULL)
+ return False;
+ }
+
+ rpcstr_pull(nt_devmode->devicename,devmode->devicename.buffer, 31, -1, 0);
+ rpcstr_pull(nt_devmode->formname,devmode->formname.buffer, 31, -1, 0);
+
+ nt_devmode->specversion=devmode->specversion;
+ nt_devmode->driverversion=devmode->driverversion;
+ nt_devmode->size=devmode->size;
+ nt_devmode->fields=devmode->fields;
+ nt_devmode->orientation=devmode->orientation;
+ nt_devmode->papersize=devmode->papersize;
+ nt_devmode->paperlength=devmode->paperlength;
+ nt_devmode->paperwidth=devmode->paperwidth;
+ nt_devmode->scale=devmode->scale;
+ nt_devmode->copies=devmode->copies;
+ nt_devmode->defaultsource=devmode->defaultsource;
+ nt_devmode->printquality=devmode->printquality;
+ nt_devmode->color=devmode->color;
+ nt_devmode->duplex=devmode->duplex;
+ nt_devmode->yresolution=devmode->yresolution;
+ nt_devmode->ttoption=devmode->ttoption;
+ nt_devmode->collate=devmode->collate;
+
+ nt_devmode->logpixels=devmode->logpixels;
+ nt_devmode->bitsperpel=devmode->bitsperpel;
+ nt_devmode->pelswidth=devmode->pelswidth;
+ nt_devmode->pelsheight=devmode->pelsheight;
+ nt_devmode->displayflags=devmode->displayflags;
+ nt_devmode->displayfrequency=devmode->displayfrequency;
+ nt_devmode->icmmethod=devmode->icmmethod;
+ nt_devmode->icmintent=devmode->icmintent;
+ nt_devmode->mediatype=devmode->mediatype;
+ nt_devmode->dithertype=devmode->dithertype;
+ nt_devmode->reserved1=devmode->reserved1;
+ nt_devmode->reserved2=devmode->reserved2;
+ nt_devmode->panningwidth=devmode->panningwidth;
+ nt_devmode->panningheight=devmode->panningheight;
+
+ /*
+ * Only change private and driverextra if the incoming devmode
+ * has a new one. JRA.
+ */
+
+ if ((devmode->driverextra != 0) && (devmode->private != NULL)) {
+ SAFE_FREE(nt_devmode->private);
+ nt_devmode->driverextra=devmode->driverextra;
+ if((nt_devmode->private=(uint8 *)malloc(nt_devmode->driverextra * sizeof(uint8))) == NULL)
+ return False;
+ memcpy(nt_devmode->private, devmode->private, nt_devmode->driverextra);
+ }
+
+ *pp_nt_devmode = nt_devmode;
+
+ return True;
+}
+
+/********************************************************************
+ * _spoolss_enddocprinter_internal.
+ ********************************************************************/
+
+static WERROR _spoolss_enddocprinter_internal(pipes_struct *p, POLICY_HND *handle)
+{
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+ int snum;
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_enddocprinter_internal: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if (!get_printer_snum(p, handle, &snum))
+ return WERR_BADFID;
+
+ Printer->document_started=False;
+ print_job_end(snum, Printer->jobid,True);
+ /* error codes unhandled so far ... */
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * api_spoolss_closeprinter
+ ********************************************************************/
+
+WERROR _spoolss_closeprinter(pipes_struct *p, SPOOL_Q_CLOSEPRINTER *q_u, SPOOL_R_CLOSEPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+
+ if (Printer && Printer->document_started)
+ _spoolss_enddocprinter_internal(p, handle); /* print job was not closed */
+
+ if (!close_printer_handle(p, handle))
+ return WERR_BADFID;
+
+ /* clear the returned printer handle. Observed behavior
+ from Win2k server. Don't think this really matters.
+ Previous code just copied the value of the closed
+ handle. --jerry */
+
+ memset(&r_u->handle, '\0', sizeof(r_u->handle));
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * api_spoolss_deleteprinter
+
+ ********************************************************************/
+
+WERROR _spoolss_deleteprinter(pipes_struct *p, SPOOL_Q_DELETEPRINTER *q_u, SPOOL_R_DELETEPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+ WERROR result;
+
+ if (Printer && Printer->document_started)
+ _spoolss_enddocprinter_internal(p, handle); /* print job was not closed */
+
+ memcpy(&r_u->handle, &q_u->handle, sizeof(r_u->handle));
+
+ result = delete_printer_handle(p, handle);
+
+ update_c_setprinter(False);
+
+ return result;
+}
+
+/*******************************************************************
+ * static function to lookup the version id corresponding to an
+ * long architecture string
+ ******************************************************************/
+
+static int get_version_id (char * arch)
+{
+ int i;
+ struct table_node archi_table[]= {
+
+ {"Windows 4.0", "WIN40", 0 },
+ {"Windows NT x86", "W32X86", 2 },
+ {"Windows NT R4000", "W32MIPS", 2 },
+ {"Windows NT Alpha_AXP", "W32ALPHA", 2 },
+ {"Windows NT PowerPC", "W32PPC", 2 },
+ {NULL, "", -1 }
+ };
+
+ for (i=0; archi_table[i].long_archi != NULL; i++)
+ {
+ if (strcmp(arch, archi_table[i].long_archi) == 0)
+ return (archi_table[i].version);
+ }
+
+ return -1;
+}
+
+/********************************************************************
+ * _spoolss_deleteprinterdriver
+ ********************************************************************/
+
+WERROR _spoolss_deleteprinterdriver(pipes_struct *p, SPOOL_Q_DELETEPRINTERDRIVER *q_u, SPOOL_R_DELETEPRINTERDRIVER *r_u)
+{
+ fstring driver;
+ fstring arch;
+ NT_PRINTER_DRIVER_INFO_LEVEL info;
+ NT_PRINTER_DRIVER_INFO_LEVEL info_win2k;
+ int version;
+ struct current_user user;
+ WERROR status;
+ WERROR status_win2k = WERR_ACCESS_DENIED;
+
+ get_current_user(&user, p);
+
+ unistr2_to_ascii(driver, &q_u->driver, sizeof(driver)-1 );
+ unistr2_to_ascii(arch, &q_u->arch, sizeof(arch)-1 );
+
+ /* check that we have a valid driver name first */
+
+ if ((version=get_version_id(arch)) == -1)
+ return WERR_INVALID_ENVIRONMENT;
+
+ ZERO_STRUCT(info);
+ ZERO_STRUCT(info_win2k);
+
+ if (!W_ERROR_IS_OK(get_a_printer_driver(&info, 3, driver, arch, version)))
+ {
+ /* try for Win2k driver if "Windows NT x86" */
+
+ if ( version == 2 ) {
+ version = 3;
+ if (!W_ERROR_IS_OK(get_a_printer_driver(&info, 3, driver, arch, version))) {
+ status = WERR_UNKNOWN_PRINTER_DRIVER;
+ goto done;
+ }
+ }
+ /* otherwise it was a failure */
+ else {
+ status = WERR_UNKNOWN_PRINTER_DRIVER;
+ goto done;
+ }
+
+ }
+
+ if (printer_driver_in_use(info.info_3)) {
+ status = WERR_PRINTER_DRIVER_IN_USE;
+ goto done;
+ }
+
+ if ( version == 2 )
+ {
+ if (W_ERROR_IS_OK(get_a_printer_driver(&info_win2k, 3, driver, arch, 3)))
+ {
+ /* if we get to here, we now have 2 driver info structures to remove */
+ /* remove the Win2k driver first*/
+
+ status_win2k = delete_printer_driver(info_win2k.info_3, &user, 3, False );
+ free_a_printer_driver( info_win2k, 3 );
+
+ /* this should not have failed---if it did, report to client */
+ if ( !W_ERROR_IS_OK(status_win2k) )
+ goto done;
+ }
+ }
+
+ status = delete_printer_driver(info.info_3, &user, version, False);
+
+ /* if at least one of the deletes succeeded return OK */
+
+ if ( W_ERROR_IS_OK(status) || W_ERROR_IS_OK(status_win2k) )
+ status = WERR_OK;
+
+done:
+ free_a_printer_driver( info, 3 );
+
+ return status;
+}
+
+/********************************************************************
+ * spoolss_deleteprinterdriverex
+ ********************************************************************/
+
+WERROR _spoolss_deleteprinterdriverex(pipes_struct *p, SPOOL_Q_DELETEPRINTERDRIVEREX *q_u, SPOOL_R_DELETEPRINTERDRIVEREX *r_u)
+{
+ fstring driver;
+ fstring arch;
+ NT_PRINTER_DRIVER_INFO_LEVEL info;
+ NT_PRINTER_DRIVER_INFO_LEVEL info_win2k;
+ int version;
+ uint32 flags = q_u->delete_flags;
+ BOOL delete_files;
+ struct current_user user;
+ WERROR status;
+ WERROR status_win2k = WERR_ACCESS_DENIED;
+
+ get_current_user(&user, p);
+
+ unistr2_to_ascii(driver, &q_u->driver, sizeof(driver)-1 );
+ unistr2_to_ascii(arch, &q_u->arch, sizeof(arch)-1 );
+
+ /* check that we have a valid driver name first */
+ if ((version=get_version_id(arch)) == -1) {
+ /* this is what NT returns */
+ return WERR_INVALID_ENVIRONMENT;
+ }
+
+ if ( flags & DPD_DELETE_SPECIFIC_VERSION )
+ version = q_u->version;
+
+ ZERO_STRUCT(info);
+ ZERO_STRUCT(info_win2k);
+
+ status = get_a_printer_driver(&info, 3, driver, arch, version);
+
+ if ( !W_ERROR_IS_OK(status) )
+ {
+ /*
+ * if the client asked for a specific version,
+ * or this is something other than Windows NT x86,
+ * then we've failed
+ */
+
+ if ( (flags&DPD_DELETE_SPECIFIC_VERSION) || (version !=2) )
+ goto done;
+
+ /* try for Win2k driver if "Windows NT x86" */
+
+ version = 3;
+ if (!W_ERROR_IS_OK(get_a_printer_driver(&info, 3, driver, arch, version))) {
+ status = WERR_UNKNOWN_PRINTER_DRIVER;
+ goto done;
+ }
+ }
+
+ if ( printer_driver_in_use(info.info_3) ) {
+ status = WERR_PRINTER_DRIVER_IN_USE;
+ goto done;
+ }
+
+ /*
+ * we have a couple of cases to consider.
+ * (1) Are any files in use? If so and DPD_DELTE_ALL_FILE is set,
+ * then the delete should fail if **any** files overlap with
+ * other drivers
+ * (2) If DPD_DELTE_UNUSED_FILES is sert, then delete all
+ * non-overlapping files
+ * (3) If neither DPD_DELTE_ALL_FILE nor DPD_DELTE_ALL_FILES
+ * is set, the do not delete any files
+ * Refer to MSDN docs on DeletePrinterDriverEx() for details.
+ */
+
+ delete_files = flags & (DPD_DELETE_ALL_FILES|DPD_DELETE_UNUSED_FILES);
+
+ /* fail if any files are in use and DPD_DELETE_ALL_FILES is set */
+
+ if ( delete_files && printer_driver_files_in_use(info.info_3) & (flags&DPD_DELETE_ALL_FILES) ) {
+ /* no idea of the correct error here */
+ status = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+
+ /* also check for W32X86/3 if necessary; maybe we already have? */
+
+ if ( (version == 2) && ((flags&DPD_DELETE_SPECIFIC_VERSION) != DPD_DELETE_SPECIFIC_VERSION) ) {
+ if (W_ERROR_IS_OK(get_a_printer_driver(&info_win2k, 3, driver, arch, 3)))
+ {
+
+ if ( delete_files && printer_driver_files_in_use(info_win2k.info_3) & (flags&DPD_DELETE_ALL_FILES) ) {
+ /* no idea of the correct error here */
+ free_a_printer_driver( info_win2k, 3 );
+ status = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ /* if we get to here, we now have 2 driver info structures to remove */
+ /* remove the Win2k driver first*/
+
+ status_win2k = delete_printer_driver(info_win2k.info_3, &user, 3, delete_files);
+ free_a_printer_driver( info_win2k, 3 );
+
+ /* this should not have failed---if it did, report to client */
+
+ if ( !W_ERROR_IS_OK(status_win2k) )
+ goto done;
+ }
+ }
+
+ status = delete_printer_driver(info.info_3, &user, version, delete_files);
+
+ if ( W_ERROR_IS_OK(status) || W_ERROR_IS_OK(status_win2k) )
+ status = WERR_OK;
+done:
+ free_a_printer_driver( info, 3 );
+
+ return status;
+}
+
+
+/****************************************************************************
+ Internal routine for retreiving printerdata
+ ***************************************************************************/
+
+static WERROR get_printer_dataex( TALLOC_CTX *ctx, NT_PRINTER_INFO_LEVEL *printer,
+ const char *key, const char *value, uint32 *type, uint8 **data,
+ uint32 *needed, uint32 in_size )
+{
+ REGISTRY_VALUE *val;
+ int size, data_len;
+
+ if ( !(val = get_printer_data( printer->info_2, key, value)) )
+ return WERR_BADFILE;
+
+ *type = regval_type( val );
+
+ DEBUG(5,("get_printer_dataex: allocating %d\n", in_size));
+
+ size = regval_size( val );
+
+ /* copy the min(in_size, len) */
+
+ if ( in_size ) {
+ data_len = (size > in_size) ? in_size : size*sizeof(uint8);
+
+ /* special case for 0 length values */
+ if ( data_len ) {
+ if ( (*data = (uint8 *)talloc_memdup(ctx, regval_data_p(val), data_len)) == NULL )
+ return WERR_NOMEM;
+ }
+ else {
+ if ( (*data = (uint8 *)talloc_zero(ctx, in_size)) == NULL )
+ return WERR_NOMEM;
+ }
+ }
+ else
+ *data = NULL;
+
+ *needed = size;
+
+ DEBUG(5,("get_printer_dataex: copy done\n"));
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ Internal routine for removing printerdata
+ ***************************************************************************/
+
+static WERROR delete_printer_dataex( NT_PRINTER_INFO_LEVEL *printer, const char *key, const char *value )
+{
+ return delete_printer_data( printer->info_2, key, value );
+}
+
+/****************************************************************************
+ Internal routine for storing printerdata
+ ***************************************************************************/
+
+static WERROR set_printer_dataex( NT_PRINTER_INFO_LEVEL *printer, const char *key, const char *value,
+ uint32 type, uint8 *data, int real_len )
+{
+ delete_printer_data( printer->info_2, key, value );
+
+ return add_printer_data( printer->info_2, key, value, type, data, real_len );
+}
+
+/********************************************************************
+ GetPrinterData on a printer server Handle.
+********************************************************************/
+
+static WERROR getprinterdata_printer_server(TALLOC_CTX *ctx, fstring value, uint32 *type, uint8 **data, uint32 *needed, uint32 in_size)
+{
+ int i;
+
+ DEBUG(8,("getprinterdata_printer_server:%s\n", value));
+
+ if (!StrCaseCmp(value, "W3SvcInstalled")) {
+ *type = 0x4;
+ if((*data = (uint8 *)talloc_zero(ctx, 4*sizeof(uint8) )) == NULL)
+ return WERR_NOMEM;
+ *needed = 0x4;
+ return WERR_OK;
+ }
+
+ if (!StrCaseCmp(value, "BeepEnabled")) {
+ *type = 0x4;
+ if((*data = (uint8 *)talloc(ctx, 4*sizeof(uint8) )) == NULL)
+ return WERR_NOMEM;
+ SIVAL(*data, 0, 0x00);
+ *needed = 0x4;
+ return WERR_OK;
+ }
+
+ if (!StrCaseCmp(value, "EventLog")) {
+ *type = 0x4;
+ if((*data = (uint8 *)talloc(ctx, 4*sizeof(uint8) )) == NULL)
+ return WERR_NOMEM;
+ /* formally was 0x1b */
+ SIVAL(*data, 0, 0x0);
+ *needed = 0x4;
+ return WERR_OK;
+ }
+
+ if (!StrCaseCmp(value, "NetPopup")) {
+ *type = 0x4;
+ if((*data = (uint8 *)talloc(ctx, 4*sizeof(uint8) )) == NULL)
+ return WERR_NOMEM;
+ SIVAL(*data, 0, 0x00);
+ *needed = 0x4;
+ return WERR_OK;
+ }
+
+ if (!StrCaseCmp(value, "MajorVersion")) {
+ *type = 0x4;
+ if((*data = (uint8 *)talloc(ctx, 4*sizeof(uint8) )) == NULL)
+ return WERR_NOMEM;
+
+ /* Windows NT 4.0 seems to not allow uploading of drivers
+ to a server that reports 0x3 as the MajorVersion.
+ need to investigate more how Win2k gets around this .
+ -- jerry */
+
+ if ( RA_WINNT == get_remote_arch() )
+ SIVAL(*data, 0, 2);
+ else
+ SIVAL(*data, 0, 3);
+
+ *needed = 0x4;
+ return WERR_OK;
+ }
+
+ if (!StrCaseCmp(value, "MinorVersion")) {
+ *type = 0x4;
+ if((*data = (uint8 *)talloc(ctx, 4*sizeof(uint8) )) == NULL)
+ return WERR_NOMEM;
+ SIVAL(*data, 0, 0);
+ *needed = 0x4;
+ return WERR_OK;
+ }
+
+ /* REG_BINARY
+ * uint32 size = 0x114
+ * uint32 major = 5
+ * uint32 minor = [0|1]
+ * uint32 build = [2195|2600]
+ * extra unicode string = e.g. "Service Pack 3"
+ */
+ if (!StrCaseCmp(value, "OSVersion")) {
+ *type = 0x3;
+ *needed = 0x114;
+
+ if((*data = (uint8 *)talloc(ctx, (*needed)*sizeof(uint8) )) == NULL)
+ return WERR_NOMEM;
+ ZERO_STRUCTP( *data );
+
+ SIVAL(*data, 0, *needed); /* size */
+ SIVAL(*data, 4, 5); /* Windows 2000 == 5.0 */
+ SIVAL(*data, 8, 0);
+ SIVAL(*data, 12, 2195); /* build */
+
+ /* leave extra string empty */
+
+ return WERR_OK;
+ }
+
+
+ if (!StrCaseCmp(value, "DefaultSpoolDirectory")) {
+ const char *string="C:\\PRINTERS";
+ *type = 0x1;
+ *needed = 2*(strlen(string)+1);
+ if((*data = (uint8 *)talloc(ctx, ((*needed > in_size) ? *needed:in_size) *sizeof(uint8))) == NULL)
+ return WERR_NOMEM;
+ memset(*data, 0, (*needed > in_size) ? *needed:in_size);
+
+ /* it's done by hand ready to go on the wire */
+ for (i=0; i<strlen(string); i++) {
+ (*data)[2*i]=string[i];
+ (*data)[2*i+1]='\0';
+ }
+ return WERR_OK;
+ }
+
+ if (!StrCaseCmp(value, "Architecture")) {
+ const char *string="Windows NT x86";
+ *type = 0x1;
+ *needed = 2*(strlen(string)+1);
+ if((*data = (uint8 *)talloc(ctx, ((*needed > in_size) ? *needed:in_size) *sizeof(uint8))) == NULL)
+ return WERR_NOMEM;
+ memset(*data, 0, (*needed > in_size) ? *needed:in_size);
+ for (i=0; i<strlen(string); i++) {
+ (*data)[2*i]=string[i];
+ (*data)[2*i+1]='\0';
+ }
+ return WERR_OK;
+ }
+
+ if (!StrCaseCmp(value, "DsPresent")) {
+ *type = 0x4;
+ if((*data = (uint8 *)talloc(ctx, 4*sizeof(uint8) )) == NULL)
+ return WERR_NOMEM;
+ SIVAL(*data, 0, 0x01);
+ *needed = 0x4;
+ return WERR_OK;
+ }
+
+ if (!StrCaseCmp(value, "DNSMachineName")) {
+ pstring hostname;
+
+ if (!get_mydnsfullname(hostname))
+ return WERR_BADFILE;
+ *type = 0x1;
+ *needed = 2*(strlen(hostname)+1);
+ if((*data = (uint8 *)talloc(ctx, ((*needed > in_size) ? *needed:in_size) *sizeof(uint8))) == NULL)
+ return WERR_NOMEM;
+ memset(*data, 0, (*needed > in_size) ? *needed:in_size);
+ for (i=0; i<strlen(hostname); i++) {
+ (*data)[2*i]=hostname[i];
+ (*data)[2*i+1]='\0';
+ }
+ return WERR_OK;
+ }
+
+
+ return WERR_BADFILE;
+}
+
+/********************************************************************
+ * spoolss_getprinterdata
+ ********************************************************************/
+
+WERROR _spoolss_getprinterdata(pipes_struct *p, SPOOL_Q_GETPRINTERDATA *q_u, SPOOL_R_GETPRINTERDATA *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ UNISTR2 *valuename = &q_u->valuename;
+ uint32 in_size = q_u->size;
+ uint32 *type = &r_u->type;
+ uint32 *out_size = &r_u->size;
+ uint8 **data = &r_u->data;
+ uint32 *needed = &r_u->needed;
+ WERROR status;
+ fstring value;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ int snum = 0;
+
+ /*
+ * Reminder: when it's a string, the length is in BYTES
+ * even if UNICODE is negociated.
+ *
+ * JFM, 4/19/1999
+ */
+
+ *out_size = in_size;
+
+ /* in case of problem, return some default values */
+
+ *needed = 0;
+ *type = 0;
+
+ DEBUG(4,("_spoolss_getprinterdata\n"));
+
+ if ( !Printer ) {
+ DEBUG(2,("_spoolss_getprinterdata: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ status = WERR_BADFID;
+ goto done;
+ }
+
+ unistr2_to_ascii(value, valuename, sizeof(value)-1);
+
+ if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER )
+ status = getprinterdata_printer_server( p->mem_ctx, value, type, data, needed, *out_size );
+ else
+ {
+ if ( !get_printer_snum(p,handle, &snum) ) {
+ status = WERR_BADFID;
+ goto done;
+ }
+
+ status = get_a_printer(Printer, &printer, 2, lp_servicename(snum));
+ if ( !W_ERROR_IS_OK(status) )
+ goto done;
+
+ /* XP sends this and wants to change id value from the PRINTER_INFO_0 */
+
+ if ( strequal(value, "ChangeId") ) {
+ *type = REG_DWORD;
+ *needed = sizeof(uint32);
+ if ( (*data = (uint8*)talloc(p->mem_ctx, sizeof(uint32))) == NULL) {
+ status = WERR_NOMEM;
+ goto done;
+ }
+ SIVAL( *data, 0, printer->info_2->changeid );
+ status = WERR_OK;
+ }
+ else
+ status = get_printer_dataex( p->mem_ctx, printer, SPOOL_PRINTERDATA_KEY, value, type, data, needed, *out_size );
+ }
+
+ if (*needed > *out_size)
+ status = WERR_MORE_DATA;
+
+done:
+ if ( !W_ERROR_IS_OK(status) )
+ {
+ DEBUG(5, ("error %d: allocating %d\n", W_ERROR_V(status),*out_size));
+
+ /* reply this param doesn't exist */
+
+ if ( *out_size ) {
+ if((*data=(uint8 *)talloc_zero(p->mem_ctx, *out_size*sizeof(uint8))) == NULL) {
+ if ( printer )
+ free_a_printer( &printer, 2 );
+ return WERR_NOMEM;
+ }
+ }
+ else {
+ *data = NULL;
+ }
+ }
+
+ /* cleanup & exit */
+
+ if ( printer )
+ free_a_printer( &printer, 2 );
+
+ return status;
+}
+
+/*********************************************************
+ Connect to the client machine.
+**********************************************************/
+
+static BOOL spoolss_connect_to_client(struct cli_state *the_cli,
+ struct in_addr *client_ip, const char *remote_machine)
+{
+ ZERO_STRUCTP(the_cli);
+
+ if(cli_initialise(the_cli) == NULL) {
+ DEBUG(0,("spoolss_connect_to_client: unable to initialize client connection.\n"));
+ return False;
+ }
+
+ if ( is_zero_ip(*client_ip) ) {
+ if(!resolve_name( remote_machine, &the_cli->dest_ip, 0x20)) {
+ DEBUG(0,("spoolss_connect_to_client: Can't resolve address for %s\n", remote_machine));
+ cli_shutdown(the_cli);
+ return False;
+ }
+
+ if (ismyip(the_cli->dest_ip)) {
+ DEBUG(0,("spoolss_connect_to_client: Machine %s is one of our addresses. Cannot add to ourselves.\n", remote_machine));
+ cli_shutdown(the_cli);
+ return False;
+ }
+ }
+ else {
+ the_cli->dest_ip.s_addr = client_ip->s_addr;
+ DEBUG(5,("spoolss_connect_to_client: Using address %s (no name resolution necessary)\n",
+ inet_ntoa(*client_ip) ));
+ }
+
+ if (!cli_connect(the_cli, remote_machine, &the_cli->dest_ip)) {
+ DEBUG(0,("spoolss_connect_to_client: unable to connect to SMB server on machine %s. Error was : %s.\n", remote_machine, cli_errstr(the_cli) ));
+ cli_shutdown(the_cli);
+ return False;
+ }
+
+ if (!attempt_netbios_session_request(the_cli, global_myname(), remote_machine, &the_cli->dest_ip)) {
+ DEBUG(0,("spoolss_connect_to_client: machine %s rejected the NetBIOS session request.\n",
+ remote_machine));
+ cli_shutdown(the_cli);
+ return False;
+ }
+
+ the_cli->protocol = PROTOCOL_NT1;
+ cli_setup_signing_state(the_cli, lp_client_signing());
+
+ if (!cli_negprot(the_cli)) {
+ DEBUG(0,("spoolss_connect_to_client: machine %s rejected the negotiate protocol. Error was : %s.\n", remote_machine, cli_errstr(the_cli) ));
+ cli_shutdown(the_cli);
+ return False;
+ }
+
+ if (the_cli->protocol != PROTOCOL_NT1) {
+ DEBUG(0,("spoolss_connect_to_client: machine %s didn't negotiate NT protocol.\n", remote_machine));
+ cli_shutdown(the_cli);
+ return False;
+ }
+
+ /*
+ * Do an anonymous session setup.
+ */
+
+ if (!cli_session_setup(the_cli, "", "", 0, "", 0, "")) {
+ DEBUG(0,("spoolss_connect_to_client: machine %s rejected the session setup. Error was : %s.\n", remote_machine, cli_errstr(the_cli) ));
+ cli_shutdown(the_cli);
+ return False;
+ }
+
+ if (!(the_cli->sec_mode & 1)) {
+ DEBUG(0,("spoolss_connect_to_client: machine %s isn't in user level security mode\n", remote_machine));
+ cli_shutdown(the_cli);
+ return False;
+ }
+
+ if (!cli_send_tconX(the_cli, "IPC$", "IPC", "", 1)) {
+ DEBUG(0,("spoolss_connect_to_client: machine %s rejected the tconX on the IPC$ share. Error was : %s.\n", remote_machine, cli_errstr(the_cli) ));
+ cli_shutdown(the_cli);
+ return False;
+ }
+
+ /*
+ * Ok - we have an anonymous connection to the IPC$ share.
+ * Now start the NT Domain stuff :-).
+ */
+
+ if(cli_nt_session_open(the_cli, PI_SPOOLSS) == False) {
+ DEBUG(0,("spoolss_connect_to_client: unable to open the domain client session to machine %s. Error was : %s.\n", remote_machine, cli_errstr(the_cli)));
+ cli_nt_session_close(the_cli);
+ cli_ulogoff(the_cli);
+ cli_shutdown(the_cli);
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ Connect to the client.
+****************************************************************************/
+
+static BOOL srv_spoolss_replyopenprinter(int snum, const char *printer,
+ uint32 localprinter, uint32 type,
+ POLICY_HND *handle, struct in_addr *client_ip)
+{
+ WERROR result;
+
+ /*
+ * If it's the first connection, contact the client
+ * and connect to the IPC$ share anonymously
+ */
+ if (smb_connections==0) {
+ fstring unix_printer;
+
+ fstrcpy(unix_printer, printer+2); /* the +2 is to strip the leading 2 backslashs */
+
+ ZERO_STRUCT(notify_cli);
+
+ if(!spoolss_connect_to_client(&notify_cli, client_ip, unix_printer))
+ return False;
+
+ message_register(MSG_PRINTER_NOTIFY2, receive_notify2_message_list);
+ /* Tell the connections db we're now interested in printer
+ * notify messages. */
+ register_message_flags( True, FLAG_MSG_PRINTING );
+ }
+
+ /*
+ * Tell the specific printing tdb we want messages for this printer
+ * by registering our PID.
+ */
+
+ if (!print_notify_register_pid(snum))
+ DEBUG(0,("print_notify_register_pid: Failed to register our pid for printer %s\n", printer ));
+
+ smb_connections++;
+
+ result = cli_spoolss_reply_open_printer(&notify_cli, notify_cli.mem_ctx, printer, localprinter,
+ type, handle);
+
+ if (!W_ERROR_IS_OK(result))
+ DEBUG(5,("srv_spoolss_reply_open_printer: Client RPC returned [%s]\n",
+ dos_errstr(result)));
+
+ return (W_ERROR_IS_OK(result));
+}
+
+/********************************************************************
+ * _spoolss_rffpcnex
+ * ReplyFindFirstPrinterChangeNotifyEx
+ *
+ * before replying OK: status=0 a rpc call is made to the workstation
+ * asking ReplyOpenPrinter
+ *
+ * in fact ReplyOpenPrinter is the changenotify equivalent on the spoolss pipe
+ * called from api_spoolss_rffpcnex
+ ********************************************************************/
+
+WERROR _spoolss_rffpcnex(pipes_struct *p, SPOOL_Q_RFFPCNEX *q_u, SPOOL_R_RFFPCNEX *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 flags = q_u->flags;
+ uint32 options = q_u->options;
+ UNISTR2 *localmachine = &q_u->localmachine;
+ uint32 printerlocal = q_u->printerlocal;
+ int snum = -1;
+ SPOOL_NOTIFY_OPTION *option = q_u->option;
+ struct in_addr client_ip;
+
+ /* store the notify value in the printer struct */
+
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_rffpcnex: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ Printer->notify.flags=flags;
+ Printer->notify.options=options;
+ Printer->notify.printerlocal=printerlocal;
+
+ if (Printer->notify.option)
+ free_spool_notify_option(&Printer->notify.option);
+
+ Printer->notify.option=dup_spool_notify_option(option);
+
+ unistr2_to_ascii(Printer->notify.localmachine, localmachine,
+ sizeof(Printer->notify.localmachine)-1);
+
+ /* Connect to the client machine and send a ReplyOpenPrinter */
+
+ if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER)
+ snum = -1;
+ else if ( (Printer->printer_type == PRINTER_HANDLE_IS_PRINTER) &&
+ !get_printer_snum(p, handle, &snum) )
+ return WERR_BADFID;
+
+ client_ip.s_addr = inet_addr(p->conn->client_address);
+
+ if(!srv_spoolss_replyopenprinter(snum, Printer->notify.localmachine,
+ Printer->notify.printerlocal, 1,
+ &Printer->notify.client_hnd, &client_ip))
+ return WERR_SERVER_UNAVAILABLE;
+
+ Printer->notify.client_connected=True;
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the servername
+ ********************************************************************/
+
+void spoolss_notify_server_name(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ pstring temp_name, temp;
+ uint32 len;
+
+ slprintf(temp_name, sizeof(temp_name)-1, "\\\\%s", get_called_name());
+
+ len = rpcstr_push(temp, temp_name, sizeof(temp)-2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the printername (not including the servername).
+ ********************************************************************/
+
+void spoolss_notify_printer_name(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ pstring temp;
+ uint32 len;
+
+ /* the notify name should not contain the \\server\ part */
+ char *p = strrchr(printer->info_2->printername, '\\');
+
+ if (!p) {
+ p = printer->info_2->printername;
+ } else {
+ p++;
+ }
+
+ len = rpcstr_push(temp, p, sizeof(temp)-2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the servicename
+ ********************************************************************/
+
+void spoolss_notify_share_name(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ pstring temp;
+ uint32 len;
+
+ len = rpcstr_push(temp, lp_servicename(snum), sizeof(temp)-2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the port name
+ ********************************************************************/
+
+void spoolss_notify_port_name(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ pstring temp;
+ uint32 len;
+
+ /* even if it's strange, that's consistant in all the code */
+
+ len = rpcstr_push(temp, printer->info_2->portname, sizeof(temp)-2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the printername
+ * but it doesn't exist, have to see what to do
+ ********************************************************************/
+
+void spoolss_notify_driver_name(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ pstring temp;
+ uint32 len;
+
+ len = rpcstr_push(temp, printer->info_2->drivername, sizeof(temp)-2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the comment
+ ********************************************************************/
+
+void spoolss_notify_comment(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ pstring temp;
+ uint32 len;
+
+ if (*printer->info_2->comment == '\0')
+ len = rpcstr_push(temp, lp_comment(snum), sizeof(temp)-2, STR_TERMINATE);
+ else
+ len = rpcstr_push(temp, printer->info_2->comment, sizeof(temp)-2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the comment
+ * location = "Room 1, floor 2, building 3"
+ ********************************************************************/
+
+void spoolss_notify_location(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ pstring temp;
+ uint32 len;
+
+ len = rpcstr_push(temp, printer->info_2->location,sizeof(temp)-2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the device mode
+ * jfm:xxxx don't to it for know but that's a real problem !!!
+ ********************************************************************/
+
+static void spoolss_notify_devmode(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the separator file name
+ ********************************************************************/
+
+void spoolss_notify_sepfile(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ pstring temp;
+ uint32 len;
+
+ len = rpcstr_push(temp, printer->info_2->sepfile, sizeof(temp)-2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the print processor
+ * jfm:xxxx return always winprint to indicate we don't do anything to it
+ ********************************************************************/
+
+void spoolss_notify_print_processor(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ pstring temp;
+ uint32 len;
+
+ len = rpcstr_push(temp, printer->info_2->printprocessor, sizeof(temp)-2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the print processor options
+ * jfm:xxxx send an empty string
+ ********************************************************************/
+
+void spoolss_notify_parameters(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ pstring temp;
+ uint32 len;
+
+ len = rpcstr_push(temp, printer->info_2->parameters, sizeof(temp)-2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the data type
+ * jfm:xxxx always send RAW as data type
+ ********************************************************************/
+
+void spoolss_notify_datatype(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ pstring temp;
+ uint32 len;
+
+ len = rpcstr_push(temp, printer->info_2->datatype, sizeof(pstring)-2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the security descriptor
+ * jfm:xxxx send an null pointer to say no security desc
+ * have to implement security before !
+ ********************************************************************/
+
+static void spoolss_notify_security_desc(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.sd.size = printer->info_2->secdesc_buf->len;
+ data->notify_data.sd.desc = dup_sec_desc( mem_ctx, printer->info_2->secdesc_buf->sec ) ;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the attributes
+ * jfm:xxxx a samba printer is always shared
+ ********************************************************************/
+
+void spoolss_notify_attributes(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0] = printer->info_2->attributes;
+ data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the priority
+ ********************************************************************/
+
+static void spoolss_notify_priority(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0] = printer->info_2->priority;
+ data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the default priority
+ ********************************************************************/
+
+static void spoolss_notify_default_priority(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0] = printer->info_2->default_priority;
+ data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the start time
+ ********************************************************************/
+
+static void spoolss_notify_start_time(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0] = printer->info_2->starttime;
+ data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the until time
+ ********************************************************************/
+
+static void spoolss_notify_until_time(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0] = printer->info_2->untiltime;
+ data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the status
+ ********************************************************************/
+
+static void spoolss_notify_status(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ print_status_struct status;
+
+ print_queue_length(snum, &status);
+ data->notify_data.value[0]=(uint32) status.status;
+ data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the number of jobs queued
+ ********************************************************************/
+
+void spoolss_notify_cjobs(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0] = print_queue_length(snum, NULL);
+ data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the average ppm
+ ********************************************************************/
+
+static void spoolss_notify_average_ppm(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ /* always respond 8 pages per minutes */
+ /* a little hard ! */
+ data->notify_data.value[0] = printer->info_2->averageppm;
+ data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with username
+ ********************************************************************/
+
+static void spoolss_notify_username(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ pstring temp;
+ uint32 len;
+
+ len = rpcstr_push(temp, queue->fs_user, sizeof(temp)-2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with job status
+ ********************************************************************/
+
+static void spoolss_notify_job_status(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0]=nt_printj_status(queue->status);
+ data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with job name
+ ********************************************************************/
+
+static void spoolss_notify_job_name(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ pstring temp;
+ uint32 len;
+
+ len = rpcstr_push(temp, queue->fs_file, sizeof(temp)-2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with job status
+ ********************************************************************/
+
+static void spoolss_notify_job_status_string(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ /*
+ * Now we're returning job status codes we just return a "" here. JRA.
+ */
+
+ const char *p = "";
+ pstring temp;
+ uint32 len;
+
+#if 0 /* NO LONGER NEEDED - JRA. 02/22/2001 */
+ p = "unknown";
+
+ switch (queue->status) {
+ case LPQ_QUEUED:
+ p = "Queued";
+ break;
+ case LPQ_PAUSED:
+ p = ""; /* NT provides the paused string */
+ break;
+ case LPQ_SPOOLING:
+ p = "Spooling";
+ break;
+ case LPQ_PRINTING:
+ p = "Printing";
+ break;
+ }
+#endif /* NO LONGER NEEDED. */
+
+ len = rpcstr_push(temp, p, sizeof(temp) - 2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with job time
+ ********************************************************************/
+
+static void spoolss_notify_job_time(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0]=0x0;
+ data->notify_data.value[1]=0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with job size
+ ********************************************************************/
+
+static void spoolss_notify_job_size(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0]=queue->size;
+ data->notify_data.value[1]=0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with page info
+ ********************************************************************/
+static void spoolss_notify_total_pages(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0]=queue->page_count;
+ data->notify_data.value[1]=0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with pages printed info.
+ ********************************************************************/
+static void spoolss_notify_pages_printed(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0]=0; /* Add code when back-end tracks this */
+ data->notify_data.value[1]=0;
+}
+
+/*******************************************************************
+ Fill a notify_info_data with job position.
+ ********************************************************************/
+
+static void spoolss_notify_job_position(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0]=queue->job;
+ data->notify_data.value[1]=0;
+}
+
+/*******************************************************************
+ Fill a notify_info_data with submitted time.
+ ********************************************************************/
+
+static void spoolss_notify_submitted_time(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ struct tm *t;
+ uint32 len;
+ SYSTEMTIME st;
+ char *p;
+
+ t=gmtime(&queue->time);
+
+ len = sizeof(SYSTEMTIME);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ make_systemtime(&st, t);
+
+ /*
+ * Systemtime must be linearized as a set of UINT16's.
+ * Fix from Benjamin (Bj) Kuit bj@it.uts.edu.au
+ */
+
+ p = (char *)data->notify_data.data.string;
+ SSVAL(p, 0, st.year);
+ SSVAL(p, 2, st.month);
+ SSVAL(p, 4, st.dayofweek);
+ SSVAL(p, 6, st.day);
+ SSVAL(p, 8, st.hour);
+ SSVAL(p, 10, st.minute);
+ SSVAL(p, 12, st.second);
+ SSVAL(p, 14, st.milliseconds);
+}
+
+struct s_notify_info_data_table
+{
+ uint16 type;
+ uint16 field;
+ const char *name;
+ uint32 size;
+ void (*fn) (int snum, SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer, TALLOC_CTX *mem_ctx);
+};
+
+/* A table describing the various print notification constants and
+ whether the notification data is a pointer to a variable sized
+ buffer, a one value uint32 or a two value uint32. */
+
+static const struct s_notify_info_data_table notify_info_data_table[] =
+{
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SERVER_NAME, "PRINTER_NOTIFY_SERVER_NAME", NOTIFY_STRING, spoolss_notify_server_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PRINTER_NAME, "PRINTER_NOTIFY_PRINTER_NAME", NOTIFY_STRING, spoolss_notify_printer_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SHARE_NAME, "PRINTER_NOTIFY_SHARE_NAME", NOTIFY_STRING, spoolss_notify_share_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PORT_NAME, "PRINTER_NOTIFY_PORT_NAME", NOTIFY_STRING, spoolss_notify_port_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DRIVER_NAME, "PRINTER_NOTIFY_DRIVER_NAME", NOTIFY_STRING, spoolss_notify_driver_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_COMMENT, "PRINTER_NOTIFY_COMMENT", NOTIFY_STRING, spoolss_notify_comment },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_LOCATION, "PRINTER_NOTIFY_LOCATION", NOTIFY_STRING, spoolss_notify_location },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DEVMODE, "PRINTER_NOTIFY_DEVMODE", NOTIFY_POINTER, spoolss_notify_devmode },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SEPFILE, "PRINTER_NOTIFY_SEPFILE", NOTIFY_STRING, spoolss_notify_sepfile },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PRINT_PROCESSOR, "PRINTER_NOTIFY_PRINT_PROCESSOR", NOTIFY_STRING, spoolss_notify_print_processor },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PARAMETERS, "PRINTER_NOTIFY_PARAMETERS", NOTIFY_STRING, spoolss_notify_parameters },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DATATYPE, "PRINTER_NOTIFY_DATATYPE", NOTIFY_STRING, spoolss_notify_datatype },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SECURITY_DESCRIPTOR, "PRINTER_NOTIFY_SECURITY_DESCRIPTOR", NOTIFY_SECDESC, spoolss_notify_security_desc },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_ATTRIBUTES, "PRINTER_NOTIFY_ATTRIBUTES", NOTIFY_ONE_VALUE, spoolss_notify_attributes },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PRIORITY, "PRINTER_NOTIFY_PRIORITY", NOTIFY_ONE_VALUE, spoolss_notify_priority },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DEFAULT_PRIORITY, "PRINTER_NOTIFY_DEFAULT_PRIORITY", NOTIFY_ONE_VALUE, spoolss_notify_default_priority },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_START_TIME, "PRINTER_NOTIFY_START_TIME", NOTIFY_ONE_VALUE, spoolss_notify_start_time },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_UNTIL_TIME, "PRINTER_NOTIFY_UNTIL_TIME", NOTIFY_ONE_VALUE, spoolss_notify_until_time },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_STATUS, "PRINTER_NOTIFY_STATUS", NOTIFY_ONE_VALUE, spoolss_notify_status },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_STATUS_STRING, "PRINTER_NOTIFY_STATUS_STRING", NOTIFY_POINTER, NULL },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_CJOBS, "PRINTER_NOTIFY_CJOBS", NOTIFY_ONE_VALUE, spoolss_notify_cjobs },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_AVERAGE_PPM, "PRINTER_NOTIFY_AVERAGE_PPM", NOTIFY_ONE_VALUE, spoolss_notify_average_ppm },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_TOTAL_PAGES, "PRINTER_NOTIFY_TOTAL_PAGES", NOTIFY_POINTER, NULL },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PAGES_PRINTED, "PRINTER_NOTIFY_PAGES_PRINTED", NOTIFY_POINTER, NULL },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_TOTAL_BYTES, "PRINTER_NOTIFY_TOTAL_BYTES", NOTIFY_POINTER, NULL },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_BYTES_PRINTED, "PRINTER_NOTIFY_BYTES_PRINTED", NOTIFY_POINTER, NULL },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_PRINTER_NAME, "JOB_NOTIFY_PRINTER_NAME", NOTIFY_STRING, spoolss_notify_printer_name },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_MACHINE_NAME, "JOB_NOTIFY_MACHINE_NAME", NOTIFY_STRING, spoolss_notify_server_name },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_PORT_NAME, "JOB_NOTIFY_PORT_NAME", NOTIFY_STRING, spoolss_notify_port_name },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_USER_NAME, "JOB_NOTIFY_USER_NAME", NOTIFY_STRING, spoolss_notify_username },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_NOTIFY_NAME, "JOB_NOTIFY_NOTIFY_NAME", NOTIFY_STRING, spoolss_notify_username },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_DATATYPE, "JOB_NOTIFY_DATATYPE", NOTIFY_STRING, spoolss_notify_datatype },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_PRINT_PROCESSOR, "JOB_NOTIFY_PRINT_PROCESSOR", NOTIFY_STRING, spoolss_notify_print_processor },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_PARAMETERS, "JOB_NOTIFY_PARAMETERS", NOTIFY_STRING, spoolss_notify_parameters },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_DRIVER_NAME, "JOB_NOTIFY_DRIVER_NAME", NOTIFY_STRING, spoolss_notify_driver_name },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_DEVMODE, "JOB_NOTIFY_DEVMODE", NOTIFY_POINTER, spoolss_notify_devmode },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_STATUS, "JOB_NOTIFY_STATUS", NOTIFY_ONE_VALUE, spoolss_notify_job_status },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_STATUS_STRING, "JOB_NOTIFY_STATUS_STRING", NOTIFY_STRING, spoolss_notify_job_status_string },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_SECURITY_DESCRIPTOR, "JOB_NOTIFY_SECURITY_DESCRIPTOR", NOTIFY_POINTER, NULL },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_DOCUMENT, "JOB_NOTIFY_DOCUMENT", NOTIFY_STRING, spoolss_notify_job_name },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_PRIORITY, "JOB_NOTIFY_PRIORITY", NOTIFY_ONE_VALUE, spoolss_notify_priority },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_POSITION, "JOB_NOTIFY_POSITION", NOTIFY_ONE_VALUE, spoolss_notify_job_position },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_SUBMITTED, "JOB_NOTIFY_SUBMITTED", NOTIFY_POINTER, spoolss_notify_submitted_time },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_START_TIME, "JOB_NOTIFY_START_TIME", NOTIFY_ONE_VALUE, spoolss_notify_start_time },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_UNTIL_TIME, "JOB_NOTIFY_UNTIL_TIME", NOTIFY_ONE_VALUE, spoolss_notify_until_time },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_TIME, "JOB_NOTIFY_TIME", NOTIFY_ONE_VALUE, spoolss_notify_job_time },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_TOTAL_PAGES, "JOB_NOTIFY_TOTAL_PAGES", NOTIFY_ONE_VALUE, spoolss_notify_total_pages },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_PAGES_PRINTED, "JOB_NOTIFY_PAGES_PRINTED", NOTIFY_ONE_VALUE, spoolss_notify_pages_printed },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_TOTAL_BYTES, "JOB_NOTIFY_TOTAL_BYTES", NOTIFY_ONE_VALUE, spoolss_notify_job_size },
+};
+
+/*******************************************************************
+ Return the size of info_data structure.
+********************************************************************/
+
+static uint32 size_of_notify_info_data(uint16 type, uint16 field)
+{
+ int i=0;
+
+ for (i = 0; i < sizeof(notify_info_data_table); i++)
+ {
+ if ( (notify_info_data_table[i].type == type)
+ && (notify_info_data_table[i].field == field) )
+ {
+ switch(notify_info_data_table[i].size)
+ {
+ case NOTIFY_ONE_VALUE:
+ case NOTIFY_TWO_VALUE:
+ return 1;
+ case NOTIFY_STRING:
+ return 2;
+
+ /* The only pointer notify data I have seen on
+ the wire is the submitted time and this has
+ the notify size set to 4. -tpot */
+
+ case NOTIFY_POINTER:
+ return 4;
+
+ case NOTIFY_SECDESC:
+ return 5;
+ }
+ }
+ }
+
+ DEBUG(5, ("invalid notify data type %d/%d\n", type, field));
+
+ return 0;
+}
+
+/*******************************************************************
+ Return the type of notify_info_data.
+********************************************************************/
+
+static int type_of_notify_info_data(uint16 type, uint16 field)
+{
+ int i=0;
+
+ for (i = 0; i < sizeof(notify_info_data_table); i++) {
+ if (notify_info_data_table[i].type == type &&
+ notify_info_data_table[i].field == field)
+ return notify_info_data_table[i].size;
+ }
+
+ return False;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static int search_notify(uint16 type, uint16 field, int *value)
+{
+ int i;
+
+ for (i = 0; i < sizeof(notify_info_data_table); i++) {
+ if (notify_info_data_table[i].type == type &&
+ notify_info_data_table[i].field == field &&
+ notify_info_data_table[i].fn != NULL) {
+ *value = i;
+ return True;
+ }
+ }
+
+ return False;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+void construct_info_data(SPOOL_NOTIFY_INFO_DATA *info_data, uint16 type, uint16 field, int id)
+{
+ info_data->type = type;
+ info_data->field = field;
+ info_data->reserved = 0;
+
+ info_data->size = size_of_notify_info_data(type, field);
+ info_data->enc_type = type_of_notify_info_data(type, field);
+
+ info_data->id = id;
+
+}
+
+
+/*******************************************************************
+ *
+ * fill a notify_info struct with info asked
+ *
+ ********************************************************************/
+
+static BOOL construct_notify_printer_info(Printer_entry *print_hnd, SPOOL_NOTIFY_INFO *info, int
+ snum, SPOOL_NOTIFY_OPTION_TYPE
+ *option_type, uint32 id,
+ TALLOC_CTX *mem_ctx)
+{
+ int field_num,j;
+ uint16 type;
+ uint16 field;
+
+ SPOOL_NOTIFY_INFO_DATA *current_data, *tid;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ print_queue_struct *queue=NULL;
+
+ type=option_type->type;
+
+ DEBUG(4,("construct_notify_printer_info: Notify type: [%s], number of notify info: [%d] on printer: [%s]\n",
+ (option_type->type==PRINTER_NOTIFY_TYPE?"PRINTER_NOTIFY_TYPE":"JOB_NOTIFY_TYPE"),
+ option_type->count, lp_servicename(snum)));
+
+ if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &printer, 2, lp_const_servicename(snum))))
+ return False;
+
+ for(field_num=0; field_num<option_type->count; field_num++) {
+ field = option_type->fields[field_num];
+
+ DEBUG(4,("construct_notify_printer_info: notify [%d]: type [%x], field [%x]\n", field_num, type, field));
+
+ if (!search_notify(type, field, &j) )
+ continue;
+
+ if((tid=(SPOOL_NOTIFY_INFO_DATA *)Realloc(info->data, (info->count+1)*sizeof(SPOOL_NOTIFY_INFO_DATA))) == NULL) {
+ DEBUG(2,("construct_notify_printer_info: failed to enlarge buffer info->data!\n"));
+ return False;
+ } else
+ info->data = tid;
+
+ current_data = &info->data[info->count];
+
+ construct_info_data(current_data, type, field, id);
+
+ DEBUG(10,("construct_notify_printer_info: calling [%s] snum=%d printername=[%s])\n",
+ notify_info_data_table[j].name, snum, printer->info_2->printername ));
+
+ notify_info_data_table[j].fn(snum, current_data, queue,
+ printer, mem_ctx);
+
+ info->count++;
+ }
+
+ free_a_printer(&printer, 2);
+ return True;
+}
+
+/*******************************************************************
+ *
+ * fill a notify_info struct with info asked
+ *
+ ********************************************************************/
+
+static BOOL construct_notify_jobs_info(print_queue_struct *queue,
+ SPOOL_NOTIFY_INFO *info,
+ NT_PRINTER_INFO_LEVEL *printer,
+ int snum, SPOOL_NOTIFY_OPTION_TYPE
+ *option_type, uint32 id,
+ TALLOC_CTX *mem_ctx)
+{
+ int field_num,j;
+ uint16 type;
+ uint16 field;
+
+ SPOOL_NOTIFY_INFO_DATA *current_data, *tid;
+
+ DEBUG(4,("construct_notify_jobs_info\n"));
+
+ type = option_type->type;
+
+ DEBUGADD(4,("Notify type: [%s], number of notify info: [%d]\n",
+ (option_type->type==PRINTER_NOTIFY_TYPE?"PRINTER_NOTIFY_TYPE":"JOB_NOTIFY_TYPE"),
+ option_type->count));
+
+ for(field_num=0; field_num<option_type->count; field_num++) {
+ field = option_type->fields[field_num];
+
+ if (!search_notify(type, field, &j) )
+ continue;
+
+ if((tid=Realloc(info->data, (info->count+1)*sizeof(SPOOL_NOTIFY_INFO_DATA))) == NULL) {
+ DEBUG(2,("construct_notify_jobs_info: failed to enlarg buffer info->data!\n"));
+ return False;
+ }
+ else info->data = tid;
+
+ current_data=&(info->data[info->count]);
+
+ construct_info_data(current_data, type, field, id);
+ notify_info_data_table[j].fn(snum, current_data, queue,
+ printer, mem_ctx);
+ info->count++;
+ }
+
+ return True;
+}
+
+/*
+ * JFM: The enumeration is not that simple, it's even non obvious.
+ *
+ * let's take an example: I want to monitor the PRINTER SERVER for
+ * the printer's name and the number of jobs currently queued.
+ * So in the NOTIFY_OPTION, I have one NOTIFY_OPTION_TYPE structure.
+ * Its type is PRINTER_NOTIFY_TYPE and it has 2 fields NAME and CJOBS.
+ *
+ * I have 3 printers on the back of my server.
+ *
+ * Now the response is a NOTIFY_INFO structure, with 6 NOTIFY_INFO_DATA
+ * structures.
+ * Number Data Id
+ * 1 printer 1 name 1
+ * 2 printer 1 cjob 1
+ * 3 printer 2 name 2
+ * 4 printer 2 cjob 2
+ * 5 printer 3 name 3
+ * 6 printer 3 name 3
+ *
+ * that's the print server case, the printer case is even worse.
+ */
+
+/*******************************************************************
+ *
+ * enumerate all printers on the printserver
+ * fill a notify_info struct with info asked
+ *
+ ********************************************************************/
+
+static WERROR printserver_notify_info(pipes_struct *p, POLICY_HND *hnd,
+ SPOOL_NOTIFY_INFO *info,
+ TALLOC_CTX *mem_ctx)
+{
+ int snum;
+ Printer_entry *Printer=find_printer_index_by_hnd(p, hnd);
+ int n_services=lp_numservices();
+ int i;
+ SPOOL_NOTIFY_OPTION *option;
+ SPOOL_NOTIFY_OPTION_TYPE *option_type;
+
+ DEBUG(4,("printserver_notify_info\n"));
+
+ if (!Printer)
+ return WERR_BADFID;
+
+ option=Printer->notify.option;
+ info->version=2;
+ info->data=NULL;
+ info->count=0;
+
+ for (i=0; i<option->count; i++) {
+ option_type=&(option->ctr.type[i]);
+
+ if (option_type->type!=PRINTER_NOTIFY_TYPE)
+ continue;
+
+ for (snum=0; snum<n_services; snum++)
+ {
+ if ( lp_browseable(snum) && lp_snum_ok(snum) && lp_print_ok(snum) )
+ construct_notify_printer_info ( Printer, info, snum, option_type, snum, mem_ctx );
+ }
+ }
+
+#if 0
+ /*
+ * Debugging information, don't delete.
+ */
+
+ DEBUG(1,("dumping the NOTIFY_INFO\n"));
+ DEBUGADD(1,("info->version:[%d], info->flags:[%d], info->count:[%d]\n", info->version, info->flags, info->count));
+ DEBUGADD(1,("num\ttype\tfield\tres\tid\tsize\tenc_type\n"));
+
+ for (i=0; i<info->count; i++) {
+ DEBUGADD(1,("[%d]\t[%d]\t[%d]\t[%d]\t[%d]\t[%d]\t[%d]\n",
+ i, info->data[i].type, info->data[i].field, info->data[i].reserved,
+ info->data[i].id, info->data[i].size, info->data[i].enc_type));
+ }
+#endif
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ *
+ * fill a notify_info struct with info asked
+ *
+ ********************************************************************/
+
+static WERROR printer_notify_info(pipes_struct *p, POLICY_HND *hnd, SPOOL_NOTIFY_INFO *info,
+ TALLOC_CTX *mem_ctx)
+{
+ int snum;
+ Printer_entry *Printer=find_printer_index_by_hnd(p, hnd);
+ int i;
+ uint32 id;
+ SPOOL_NOTIFY_OPTION *option;
+ SPOOL_NOTIFY_OPTION_TYPE *option_type;
+ int count,j;
+ print_queue_struct *queue=NULL;
+ print_status_struct status;
+
+ DEBUG(4,("printer_notify_info\n"));
+
+ if (!Printer)
+ return WERR_BADFID;
+
+ option=Printer->notify.option;
+ id = 0x0;
+ info->version=2;
+ info->data=NULL;
+ info->count=0;
+
+ get_printer_snum(p, hnd, &snum);
+
+ for (i=0; i<option->count; i++) {
+ option_type=&option->ctr.type[i];
+
+ switch ( option_type->type ) {
+ case PRINTER_NOTIFY_TYPE:
+ if(construct_notify_printer_info(Printer, info, snum,
+ option_type, id,
+ mem_ctx))
+ id--;
+ break;
+
+ case JOB_NOTIFY_TYPE: {
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ count = print_queue_status(snum, &queue, &status);
+
+ if (!W_ERROR_IS_OK(get_a_printer(Printer, &printer, 2, lp_const_servicename(snum))))
+ goto done;
+
+ for (j=0; j<count; j++) {
+ construct_notify_jobs_info(&queue[j], info,
+ printer, snum,
+ option_type,
+ queue[j].job,
+ mem_ctx);
+ }
+
+ free_a_printer(&printer, 2);
+
+ done:
+ SAFE_FREE(queue);
+ break;
+ }
+ }
+ }
+
+ /*
+ * Debugging information, don't delete.
+ */
+ /*
+ DEBUG(1,("dumping the NOTIFY_INFO\n"));
+ DEBUGADD(1,("info->version:[%d], info->flags:[%d], info->count:[%d]\n", info->version, info->flags, info->count));
+ DEBUGADD(1,("num\ttype\tfield\tres\tid\tsize\tenc_type\n"));
+
+ for (i=0; i<info->count; i++) {
+ DEBUGADD(1,("[%d]\t[%d]\t[%d]\t[%d]\t[%d]\t[%d]\t[%d]\n",
+ i, info->data[i].type, info->data[i].field, info->data[i].reserved,
+ info->data[i].id, info->data[i].size, info->data[i].enc_type));
+ }
+ */
+ return WERR_OK;
+}
+
+/********************************************************************
+ * spoolss_rfnpcnex
+ ********************************************************************/
+
+WERROR _spoolss_rfnpcnex( pipes_struct *p, SPOOL_Q_RFNPCNEX *q_u, SPOOL_R_RFNPCNEX *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ SPOOL_NOTIFY_INFO *info = &r_u->info;
+
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+ WERROR result = WERR_BADFID;
+
+ /* we always have a NOTIFY_INFO struct */
+ r_u->info_ptr=0x1;
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_rfnpcnex: Invalid handle (%s:%u:%u).\n",
+ OUR_HANDLE(handle)));
+ goto done;
+ }
+
+ DEBUG(4,("Printer type %x\n",Printer->printer_type));
+
+ /*
+ * We are now using the change value, and
+ * I should check for PRINTER_NOTIFY_OPTIONS_REFRESH but as
+ * I don't have a global notification system, I'm sending back all the
+ * informations even when _NOTHING_ has changed.
+ */
+
+ /* We need to keep track of the change value to send back in
+ RRPCN replies otherwise our updates are ignored. */
+
+ Printer->notify.fnpcn = True;
+
+ if (Printer->notify.client_connected) {
+ DEBUG(10,("_spoolss_rfnpcnex: Saving change value in request [%x]\n", q_u->change));
+ Printer->notify.change = q_u->change;
+ }
+
+ /* just ignore the SPOOL_NOTIFY_OPTION */
+
+ switch (Printer->printer_type) {
+ case PRINTER_HANDLE_IS_PRINTSERVER:
+ result = printserver_notify_info(p, handle, info, p->mem_ctx);
+ break;
+
+ case PRINTER_HANDLE_IS_PRINTER:
+ result = printer_notify_info(p, handle, info, p->mem_ctx);
+ break;
+ }
+
+ Printer->notify.fnpcn = False;
+
+done:
+ return result;
+}
+
+/********************************************************************
+ * construct_printer_info_0
+ * fill a printer_info_0 struct
+ ********************************************************************/
+
+static BOOL construct_printer_info_0(Printer_entry *print_hnd, PRINTER_INFO_0 *printer, int snum)
+{
+ pstring chaine;
+ int count;
+ NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+ counter_printer_0 *session_counter;
+ uint32 global_counter;
+ struct tm *t;
+ time_t setuptime;
+ print_status_struct status;
+
+ if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &ntprinter, 2, lp_const_servicename(snum))))
+ return False;
+
+ count = print_queue_length(snum, &status);
+
+ /* check if we already have a counter for this printer */
+ session_counter = (counter_printer_0 *)ubi_dlFirst(&counter_list);
+
+ for(; session_counter; session_counter = (counter_printer_0 *)ubi_dlNext(session_counter)) {
+ if (session_counter->snum == snum)
+ break;
+ }
+
+ /* it's the first time, add it to the list */
+ if (session_counter==NULL) {
+ if((session_counter=(counter_printer_0 *)malloc(sizeof(counter_printer_0))) == NULL) {
+ free_a_printer(&ntprinter, 2);
+ return False;
+ }
+ ZERO_STRUCTP(session_counter);
+ session_counter->snum=snum;
+ session_counter->counter=0;
+ ubi_dlAddHead( &counter_list, (ubi_dlNode *)session_counter);
+ }
+
+ /* increment it */
+ session_counter->counter++;
+
+ /* JFM:
+ * the global_counter should be stored in a TDB as it's common to all the clients
+ * and should be zeroed on samba startup
+ */
+ global_counter=session_counter->counter;
+
+ pstrcpy(chaine,ntprinter->info_2->printername);
+
+ init_unistr(&printer->printername, chaine);
+
+ slprintf(chaine,sizeof(chaine)-1,"\\\\%s", get_called_name());
+ init_unistr(&printer->servername, chaine);
+
+ printer->cjobs = count;
+ printer->total_jobs = 0;
+ printer->total_bytes = 0;
+
+ setuptime = (time_t)ntprinter->info_2->setuptime;
+ t=gmtime(&setuptime);
+
+ printer->year = t->tm_year+1900;
+ printer->month = t->tm_mon+1;
+ printer->dayofweek = t->tm_wday;
+ printer->day = t->tm_mday;
+ printer->hour = t->tm_hour;
+ printer->minute = t->tm_min;
+ printer->second = t->tm_sec;
+ printer->milliseconds = 0;
+
+ printer->global_counter = global_counter;
+ printer->total_pages = 0;
+
+ /* in 2.2 we reported ourselves as 0x0004 and 0x0565 */
+ printer->major_version = 0x0005; /* NT 5 */
+ printer->build_version = 0x0893; /* build 2195 */
+
+ printer->unknown7 = 0x1;
+ printer->unknown8 = 0x0;
+ printer->unknown9 = 0x0;
+ printer->session_counter = session_counter->counter;
+ printer->unknown11 = 0x0;
+ printer->printer_errors = 0x0; /* number of print failure */
+ printer->unknown13 = 0x0;
+ printer->unknown14 = 0x1;
+ printer->unknown15 = 0x024a; /* 586 Pentium ? */
+ printer->unknown16 = 0x0;
+ printer->change_id = ntprinter->info_2->changeid; /* ChangeID in milliseconds*/
+ printer->unknown18 = 0x0;
+ printer->status = nt_printq_status(status.status);
+ printer->unknown20 = 0x0;
+ printer->c_setprinter = get_c_setprinter(); /* monotonically increasing sum of delta printer counts */
+ printer->unknown22 = 0x0;
+ printer->unknown23 = 0x6; /* 6 ???*/
+ printer->unknown24 = 0; /* unknown 24 to 26 are always 0 */
+ printer->unknown25 = 0;
+ printer->unknown26 = 0;
+ printer->unknown27 = 0;
+ printer->unknown28 = 0;
+ printer->unknown29 = 0;
+
+ free_a_printer(&ntprinter,2);
+ return (True);
+}
+
+/********************************************************************
+ * construct_printer_info_1
+ * fill a printer_info_1 struct
+ ********************************************************************/
+static BOOL construct_printer_info_1(Printer_entry *print_hnd, uint32 flags, PRINTER_INFO_1 *printer, int snum)
+{
+ pstring chaine;
+ pstring chaine2;
+ NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+
+ if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &ntprinter, 2, lp_const_servicename(snum))))
+ return False;
+
+ printer->flags=flags;
+
+ if (*ntprinter->info_2->comment == '\0') {
+ init_unistr(&printer->comment, lp_comment(snum));
+ slprintf(chaine,sizeof(chaine)-1,"%s,%s,%s", ntprinter->info_2->printername,
+ ntprinter->info_2->drivername, lp_comment(snum));
+ }
+ else {
+ init_unistr(&printer->comment, ntprinter->info_2->comment); /* saved comment. */
+ slprintf(chaine,sizeof(chaine)-1,"%s,%s,%s", ntprinter->info_2->printername,
+ ntprinter->info_2->drivername, ntprinter->info_2->comment);
+ }
+
+ slprintf(chaine2,sizeof(chaine)-1,"%s", ntprinter->info_2->printername);
+
+ init_unistr(&printer->description, chaine);
+ init_unistr(&printer->name, chaine2);
+
+ free_a_printer(&ntprinter,2);
+
+ return True;
+}
+
+/****************************************************************************
+ Free a DEVMODE struct.
+****************************************************************************/
+
+static void free_dev_mode(DEVICEMODE *dev)
+{
+ if (dev == NULL)
+ return;
+
+ SAFE_FREE(dev->private);
+ SAFE_FREE(dev);
+}
+
+
+/****************************************************************************
+ Convert an NT_DEVICEMODE to a DEVICEMODE structure. Both pointers
+ should be valid upon entry
+****************************************************************************/
+
+static BOOL convert_nt_devicemode( DEVICEMODE *devmode, NT_DEVICEMODE *ntdevmode )
+{
+ if ( !devmode || !ntdevmode )
+ return False;
+
+ init_unistr(&devmode->devicename, ntdevmode->devicename);
+
+ init_unistr(&devmode->formname, ntdevmode->formname);
+
+ devmode->specversion = ntdevmode->specversion;
+ devmode->driverversion = ntdevmode->driverversion;
+ devmode->size = ntdevmode->size;
+ devmode->driverextra = ntdevmode->driverextra;
+ devmode->fields = ntdevmode->fields;
+
+ devmode->orientation = ntdevmode->orientation;
+ devmode->papersize = ntdevmode->papersize;
+ devmode->paperlength = ntdevmode->paperlength;
+ devmode->paperwidth = ntdevmode->paperwidth;
+ devmode->scale = ntdevmode->scale;
+ devmode->copies = ntdevmode->copies;
+ devmode->defaultsource = ntdevmode->defaultsource;
+ devmode->printquality = ntdevmode->printquality;
+ devmode->color = ntdevmode->color;
+ devmode->duplex = ntdevmode->duplex;
+ devmode->yresolution = ntdevmode->yresolution;
+ devmode->ttoption = ntdevmode->ttoption;
+ devmode->collate = ntdevmode->collate;
+ devmode->icmmethod = ntdevmode->icmmethod;
+ devmode->icmintent = ntdevmode->icmintent;
+ devmode->mediatype = ntdevmode->mediatype;
+ devmode->dithertype = ntdevmode->dithertype;
+
+ if (ntdevmode->private != NULL) {
+ if ((devmode->private=(uint8 *)memdup(ntdevmode->private, ntdevmode->driverextra)) == NULL)
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Create a DEVMODE struct. Returns malloced memory.
+****************************************************************************/
+
+DEVICEMODE *construct_dev_mode(int snum)
+{
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ DEVICEMODE *devmode = NULL;
+
+ DEBUG(7,("construct_dev_mode\n"));
+
+ DEBUGADD(8,("getting printer characteristics\n"));
+
+ if (!W_ERROR_IS_OK(get_a_printer(NULL, &printer, 2, lp_const_servicename(snum))))
+ return NULL;
+
+ if ( !printer->info_2->devmode ) {
+ DEBUG(5, ("BONG! There was no device mode!\n"));
+ goto done;
+ }
+
+ if ((devmode = (DEVICEMODE *)malloc(sizeof(DEVICEMODE))) == NULL) {
+ DEBUG(2,("construct_dev_mode: malloc fail.\n"));
+ goto done;
+ }
+
+ ZERO_STRUCTP(devmode);
+
+ DEBUGADD(8,("loading DEVICEMODE\n"));
+
+ if ( !convert_nt_devicemode( devmode, printer->info_2->devmode ) ) {
+ free_dev_mode( devmode );
+ devmode = NULL;
+ }
+
+done:
+ free_a_printer(&printer,2);
+
+ return devmode;
+}
+
+/********************************************************************
+ * construct_printer_info_2
+ * fill a printer_info_2 struct
+ ********************************************************************/
+
+static BOOL construct_printer_info_2(Printer_entry *print_hnd, PRINTER_INFO_2 *printer, int snum)
+{
+ int count;
+ NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+
+ print_status_struct status;
+
+ if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &ntprinter, 2, lp_const_servicename(snum))))
+ return False;
+
+ count = print_queue_length(snum, &status);
+
+ init_unistr(&printer->servername, ntprinter->info_2->servername); /* servername*/
+ init_unistr(&printer->printername, ntprinter->info_2->printername); /* printername*/
+ init_unistr(&printer->sharename, lp_servicename(snum)); /* sharename */
+ init_unistr(&printer->portname, ntprinter->info_2->portname); /* port */
+ init_unistr(&printer->drivername, ntprinter->info_2->drivername); /* drivername */
+
+ if (*ntprinter->info_2->comment == '\0')
+ init_unistr(&printer->comment, lp_comment(snum)); /* comment */
+ else
+ init_unistr(&printer->comment, ntprinter->info_2->comment); /* saved comment. */
+
+ init_unistr(&printer->location, ntprinter->info_2->location); /* location */
+ init_unistr(&printer->sepfile, ntprinter->info_2->sepfile); /* separator file */
+ init_unistr(&printer->printprocessor, ntprinter->info_2->printprocessor);/* print processor */
+ init_unistr(&printer->datatype, ntprinter->info_2->datatype); /* datatype */
+ init_unistr(&printer->parameters, ntprinter->info_2->parameters); /* parameters (of print processor) */
+
+ printer->attributes = ntprinter->info_2->attributes;
+
+ printer->priority = ntprinter->info_2->priority; /* priority */
+ printer->defaultpriority = ntprinter->info_2->default_priority; /* default priority */
+ printer->starttime = ntprinter->info_2->starttime; /* starttime */
+ printer->untiltime = ntprinter->info_2->untiltime; /* untiltime */
+ printer->status = nt_printq_status(status.status); /* status */
+ printer->cjobs = count; /* jobs */
+ printer->averageppm = ntprinter->info_2->averageppm; /* average pages per minute */
+
+ if((printer->devmode = construct_dev_mode(snum)) == NULL) {
+ DEBUG(8, ("Returning NULL Devicemode!\n"));
+ }
+
+ if (ntprinter->info_2->secdesc_buf && ntprinter->info_2->secdesc_buf->len != 0) {
+ /* steal the printer info sec_desc structure. [badly done]. */
+ printer->secdesc = ntprinter->info_2->secdesc_buf->sec;
+ ntprinter->info_2->secdesc_buf->sec = NULL; /* Stolen memory. */
+ ntprinter->info_2->secdesc_buf->len = 0; /* Stolen memory. */
+ ntprinter->info_2->secdesc_buf->max_len = 0; /* Stolen memory. */
+ }
+ else {
+ printer->secdesc = NULL;
+ }
+
+ free_a_printer(&ntprinter, 2);
+ return True;
+}
+
+/********************************************************************
+ * construct_printer_info_3
+ * fill a printer_info_3 struct
+ ********************************************************************/
+
+static BOOL construct_printer_info_3(Printer_entry *print_hnd, PRINTER_INFO_3 **pp_printer, int snum)
+{
+ NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+ PRINTER_INFO_3 *printer = NULL;
+
+ if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &ntprinter, 2, lp_const_servicename(snum))))
+ return False;
+
+ *pp_printer = NULL;
+ if ((printer = (PRINTER_INFO_3 *)malloc(sizeof(PRINTER_INFO_3))) == NULL) {
+ DEBUG(2,("construct_printer_info_3: malloc fail.\n"));
+ return False;
+ }
+
+ ZERO_STRUCTP(printer);
+
+ printer->flags = 4; /* These are the components of the SD we are returning. */
+ if (ntprinter->info_2->secdesc_buf && ntprinter->info_2->secdesc_buf->len != 0) {
+ /* steal the printer info sec_desc structure. [badly done]. */
+ printer->secdesc = ntprinter->info_2->secdesc_buf->sec;
+
+#if 0
+ /*
+ * Set the flags for the components we are returning.
+ */
+
+ if (printer->secdesc->owner_sid)
+ printer->flags |= OWNER_SECURITY_INFORMATION;
+
+ if (printer->secdesc->grp_sid)
+ printer->flags |= GROUP_SECURITY_INFORMATION;
+
+ if (printer->secdesc->dacl)
+ printer->flags |= DACL_SECURITY_INFORMATION;
+
+ if (printer->secdesc->sacl)
+ printer->flags |= SACL_SECURITY_INFORMATION;
+#endif
+
+ ntprinter->info_2->secdesc_buf->sec = NULL; /* Stolen the malloced memory. */
+ ntprinter->info_2->secdesc_buf->len = 0; /* Stolen the malloced memory. */
+ ntprinter->info_2->secdesc_buf->max_len = 0; /* Stolen the malloced memory. */
+ }
+
+ free_a_printer(&ntprinter, 2);
+
+ *pp_printer = printer;
+ return True;
+}
+
+/********************************************************************
+ * construct_printer_info_4
+ * fill a printer_info_4 struct
+ ********************************************************************/
+
+static BOOL construct_printer_info_4(Printer_entry *print_hnd, PRINTER_INFO_4 *printer, int snum)
+{
+ NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+
+ if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &ntprinter, 2, lp_const_servicename(snum))))
+ return False;
+
+ init_unistr(&printer->printername, ntprinter->info_2->printername); /* printername*/
+ init_unistr(&printer->servername, ntprinter->info_2->servername); /* servername*/
+ printer->attributes = ntprinter->info_2->attributes;
+
+ free_a_printer(&ntprinter, 2);
+ return True;
+}
+
+/********************************************************************
+ * construct_printer_info_5
+ * fill a printer_info_5 struct
+ ********************************************************************/
+
+static BOOL construct_printer_info_5(Printer_entry *print_hnd, PRINTER_INFO_5 *printer, int snum)
+{
+ NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+
+ if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &ntprinter, 2, lp_const_servicename(snum))))
+ return False;
+
+ init_unistr(&printer->printername, ntprinter->info_2->printername);
+ init_unistr(&printer->portname, ntprinter->info_2->portname);
+ printer->attributes = ntprinter->info_2->attributes;
+
+ /* these two are not used by NT+ according to MSDN */
+
+ printer->device_not_selected_timeout = 0x0; /* have seen 0x3a98 */
+ printer->transmission_retry_timeout = 0x0; /* have seen 0xafc8 */
+
+ free_a_printer(&ntprinter, 2);
+
+ return True;
+}
+
+/********************************************************************
+ * construct_printer_info_7
+ * fill a printer_info_7 struct
+ ********************************************************************/
+
+static BOOL construct_printer_info_7(Printer_entry *print_hnd, PRINTER_INFO_7 *printer, int snum)
+{
+ char *guid_str = NULL;
+ UUID_FLAT guid;
+
+ if (is_printer_published(print_hnd, snum, &guid)) {
+ asprintf(&guid_str, "{%s}",
+ smb_uuid_string_static(smb_uuid_unpack_static(guid)));
+ strupper_m(guid_str);
+ init_unistr(&printer->guid, guid_str);
+ printer->action = SPOOL_DS_PUBLISH;
+ } else {
+ init_unistr(&printer->guid, "");
+ printer->action = SPOOL_DS_UNPUBLISH;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ Spoolss_enumprinters.
+********************************************************************/
+
+static WERROR enum_all_printers_info_1(uint32 flags, NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ int snum;
+ int i;
+ int n_services=lp_numservices();
+ PRINTER_INFO_1 *tp, *printers=NULL;
+ PRINTER_INFO_1 current_prt;
+
+ DEBUG(4,("enum_all_printers_info_1\n"));
+
+ for (snum=0; snum<n_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && lp_print_ok(snum) ) {
+ DEBUG(4,("Found a printer in smb.conf: %s[%x]\n", lp_servicename(snum), snum));
+
+ if (construct_printer_info_1(NULL, flags, &current_prt, snum)) {
+ if((tp=Realloc(printers, (*returned +1)*sizeof(PRINTER_INFO_1))) == NULL) {
+ DEBUG(2,("enum_all_printers_info_1: failed to enlarge printers buffer!\n"));
+ SAFE_FREE(printers);
+ *returned=0;
+ return WERR_NOMEM;
+ }
+ else printers = tp;
+ DEBUG(4,("ReAlloced memory for [%d] PRINTER_INFO_1\n", *returned));
+
+ memcpy(&printers[*returned], &current_prt, sizeof(PRINTER_INFO_1));
+ (*returned)++;
+ }
+ }
+ }
+
+ /* check the required size. */
+ for (i=0; i<*returned; i++)
+ (*needed) += spoolss_size_printer_info_1(&printers[i]);
+
+ if (!alloc_buffer_size(buffer, *needed))
+ return WERR_INSUFFICIENT_BUFFER;
+
+ /* fill the buffer with the structures */
+ for (i=0; i<*returned; i++)
+ smb_io_printer_info_1("", buffer, &printers[i], 0);
+
+ /* clear memory */
+ SAFE_FREE(printers);
+
+ if (*needed > offered) {
+ *returned=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+ else
+ return WERR_OK;
+}
+
+/********************************************************************
+ enum_all_printers_info_1_local.
+*********************************************************************/
+
+static WERROR enum_all_printers_info_1_local(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ DEBUG(4,("enum_all_printers_info_1_local\n"));
+
+ return enum_all_printers_info_1(PRINTER_ENUM_ICON8, buffer, offered, needed, returned);
+}
+
+/********************************************************************
+ enum_all_printers_info_1_name.
+*********************************************************************/
+
+static WERROR enum_all_printers_info_1_name(fstring name, NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ char *s = name;
+
+ DEBUG(4,("enum_all_printers_info_1_name\n"));
+
+ if ((name[0] == '\\') && (name[1] == '\\'))
+ s = name + 2;
+
+ if (is_myname_or_ipaddr(s)) {
+ return enum_all_printers_info_1(PRINTER_ENUM_ICON8, buffer, offered, needed, returned);
+ }
+ else
+ return WERR_INVALID_NAME;
+}
+
+/********************************************************************
+ enum_all_printers_info_1_remote.
+*********************************************************************/
+
+static WERROR enum_all_printers_info_1_remote(fstring name, NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ PRINTER_INFO_1 *printer;
+ fstring printername;
+ fstring desc;
+ fstring comment;
+ DEBUG(4,("enum_all_printers_info_1_remote\n"));
+
+ /* JFM: currently it's more a place holder than anything else.
+ * In the spooler world there is a notion of server registration.
+ * the print servers are registring (sp ?) on the PDC (in the same domain)
+ *
+ * We should have a TDB here. The registration is done thru an undocumented RPC call.
+ */
+
+ if((printer=(PRINTER_INFO_1 *)malloc(sizeof(PRINTER_INFO_1))) == NULL)
+ return WERR_NOMEM;
+
+ *returned=1;
+
+ slprintf(printername, sizeof(printername)-1,"Windows NT Remote Printers!!\\\\%s", get_called_name());
+ slprintf(desc, sizeof(desc)-1,"%s", get_called_name());
+ slprintf(comment, sizeof(comment)-1, "Logged on Domain");
+
+ init_unistr(&printer->description, desc);
+ init_unistr(&printer->name, printername);
+ init_unistr(&printer->comment, comment);
+ printer->flags=PRINTER_ENUM_ICON3|PRINTER_ENUM_CONTAINER;
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_info_1(printer);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(printer);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_info_1("", buffer, printer, 0);
+
+ /* clear memory */
+ SAFE_FREE(printer);
+
+ if (*needed > offered) {
+ *returned=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+ else
+ return WERR_OK;
+}
+
+/********************************************************************
+ enum_all_printers_info_1_network.
+*********************************************************************/
+
+static WERROR enum_all_printers_info_1_network(fstring name, NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ char *s = name;
+
+ DEBUG(4,("enum_all_printers_info_1_network\n"));
+
+ /* If we respond to a enum_printers level 1 on our name with flags
+ set to PRINTER_ENUM_REMOTE with a list of printers then these
+ printers incorrectly appear in the APW browse list.
+ Specifically the printers for the server appear at the workgroup
+ level where all the other servers in the domain are
+ listed. Windows responds to this call with a
+ WERR_CAN_NOT_COMPLETE so we should do the same. */
+
+ if (name[0] == '\\' && name[1] == '\\')
+ s = name + 2;
+
+ if (is_myname_or_ipaddr(s))
+ return WERR_CAN_NOT_COMPLETE;
+
+ return enum_all_printers_info_1(PRINTER_ENUM_UNKNOWN_8, buffer, offered, needed, returned);
+}
+
+/********************************************************************
+ * api_spoolss_enumprinters
+ *
+ * called from api_spoolss_enumprinters (see this to understand)
+ ********************************************************************/
+
+static WERROR enum_all_printers_info_2(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ int snum;
+ int i;
+ int n_services=lp_numservices();
+ PRINTER_INFO_2 *tp, *printers=NULL;
+ PRINTER_INFO_2 current_prt;
+
+ for (snum=0; snum<n_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && lp_print_ok(snum) ) {
+ DEBUG(4,("Found a printer in smb.conf: %s[%x]\n", lp_servicename(snum), snum));
+
+ if (construct_printer_info_2(NULL, &current_prt, snum)) {
+ if((tp=Realloc(printers, (*returned +1)*sizeof(PRINTER_INFO_2))) == NULL) {
+ DEBUG(2,("enum_all_printers_info_2: failed to enlarge printers buffer!\n"));
+ SAFE_FREE(printers);
+ *returned = 0;
+ return WERR_NOMEM;
+ }
+ else printers = tp;
+ DEBUG(4,("ReAlloced memory for [%d] PRINTER_INFO_2\n", *returned));
+ memcpy(&printers[*returned], &current_prt, sizeof(PRINTER_INFO_2));
+ (*returned)++;
+ }
+ }
+ }
+
+ /* check the required size. */
+ for (i=0; i<*returned; i++)
+ (*needed) += spoolss_size_printer_info_2(&printers[i]);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ for (i=0; i<*returned; i++) {
+ free_devmode(printers[i].devmode);
+ }
+ SAFE_FREE(printers);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ for (i=0; i<*returned; i++)
+ smb_io_printer_info_2("", buffer, &(printers[i]), 0);
+
+ /* clear memory */
+ for (i=0; i<*returned; i++) {
+ free_devmode(printers[i].devmode);
+ }
+ SAFE_FREE(printers);
+
+ if (*needed > offered) {
+ *returned=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+ else
+ return WERR_OK;
+}
+
+/********************************************************************
+ * handle enumeration of printers at level 1
+ ********************************************************************/
+
+static WERROR enumprinters_level1( uint32 flags, fstring name,
+ NEW_BUFFER *buffer, uint32 offered,
+ uint32 *needed, uint32 *returned)
+{
+ /* Not all the flags are equals */
+
+ if (flags & PRINTER_ENUM_LOCAL)
+ return enum_all_printers_info_1_local(buffer, offered, needed, returned);
+
+ if (flags & PRINTER_ENUM_NAME)
+ return enum_all_printers_info_1_name(name, buffer, offered, needed, returned);
+
+ if (flags & PRINTER_ENUM_REMOTE)
+ return enum_all_printers_info_1_remote(name, buffer, offered, needed, returned);
+
+ if (flags & PRINTER_ENUM_NETWORK)
+ return enum_all_printers_info_1_network(name, buffer, offered, needed, returned);
+
+ return WERR_OK; /* NT4sp5 does that */
+}
+
+/********************************************************************
+ * handle enumeration of printers at level 2
+ ********************************************************************/
+
+static WERROR enumprinters_level2( uint32 flags, fstring servername,
+ NEW_BUFFER *buffer, uint32 offered,
+ uint32 *needed, uint32 *returned)
+{
+ char *s = servername;
+
+ if (flags & PRINTER_ENUM_LOCAL) {
+ return enum_all_printers_info_2(buffer, offered, needed, returned);
+ }
+
+ if (flags & PRINTER_ENUM_NAME) {
+ if ((servername[0] == '\\') && (servername[1] == '\\'))
+ s = servername + 2;
+ if (is_myname_or_ipaddr(s))
+ return enum_all_printers_info_2(buffer, offered, needed, returned);
+ else
+ return WERR_INVALID_NAME;
+ }
+
+ if (flags & PRINTER_ENUM_REMOTE)
+ return WERR_UNKNOWN_LEVEL;
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * handle enumeration of printers at level 5
+ ********************************************************************/
+
+static WERROR enumprinters_level5( uint32 flags, fstring servername,
+ NEW_BUFFER *buffer, uint32 offered,
+ uint32 *needed, uint32 *returned)
+{
+/* return enum_all_printers_info_5(buffer, offered, needed, returned);*/
+ return WERR_OK;
+}
+
+/********************************************************************
+ * api_spoolss_enumprinters
+ *
+ * called from api_spoolss_enumprinters (see this to understand)
+ ********************************************************************/
+
+WERROR _spoolss_enumprinters( pipes_struct *p, SPOOL_Q_ENUMPRINTERS *q_u, SPOOL_R_ENUMPRINTERS *r_u)
+{
+ uint32 flags = q_u->flags;
+ UNISTR2 *servername = &q_u->servername;
+ uint32 level = q_u->level;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ uint32 *returned = &r_u->returned;
+
+ fstring name;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(4,("_spoolss_enumprinters\n"));
+
+ *needed=0;
+ *returned=0;
+
+ /*
+ * Level 1:
+ * flags==PRINTER_ENUM_NAME
+ * if name=="" then enumerates all printers
+ * if name!="" then enumerate the printer
+ * flags==PRINTER_ENUM_REMOTE
+ * name is NULL, enumerate printers
+ * Level 2: name!="" enumerates printers, name can't be NULL
+ * Level 3: doesn't exist
+ * Level 4: does a local registry lookup
+ * Level 5: same as Level 2
+ */
+
+ unistr2_to_ascii(name, servername, sizeof(name)-1);
+ strupper_m(name);
+
+ switch (level) {
+ case 1:
+ return enumprinters_level1(flags, name, buffer, offered, needed, returned);
+ case 2:
+ return enumprinters_level2(flags, name, buffer, offered, needed, returned);
+ case 5:
+ return enumprinters_level5(flags, name, buffer, offered, needed, returned);
+ case 3:
+ case 4:
+ break;
+ }
+ return WERR_UNKNOWN_LEVEL;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinter_level_0(Printer_entry *print_hnd, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ PRINTER_INFO_0 *printer=NULL;
+
+ if((printer=(PRINTER_INFO_0*)malloc(sizeof(PRINTER_INFO_0))) == NULL)
+ return WERR_NOMEM;
+
+ construct_printer_info_0(print_hnd, printer, snum);
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_info_0(printer);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(printer);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_info_0("", buffer, printer, 0);
+
+ /* clear memory */
+ SAFE_FREE(printer);
+
+ if (*needed > offered) {
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinter_level_1(Printer_entry *print_hnd, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ PRINTER_INFO_1 *printer=NULL;
+
+ if((printer=(PRINTER_INFO_1*)malloc(sizeof(PRINTER_INFO_1))) == NULL)
+ return WERR_NOMEM;
+
+ construct_printer_info_1(print_hnd, PRINTER_ENUM_ICON8, printer, snum);
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_info_1(printer);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(printer);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_info_1("", buffer, printer, 0);
+
+ /* clear memory */
+ SAFE_FREE(printer);
+
+ if (*needed > offered) {
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinter_level_2(Printer_entry *print_hnd, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ PRINTER_INFO_2 *printer=NULL;
+
+ if((printer=(PRINTER_INFO_2*)malloc(sizeof(PRINTER_INFO_2)))==NULL)
+ return WERR_NOMEM;
+
+ construct_printer_info_2(print_hnd, printer, snum);
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_info_2(printer);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ free_printer_info_2(printer);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ if (!smb_io_printer_info_2("", buffer, printer, 0)) {
+ free_printer_info_2(printer);
+ return WERR_NOMEM;
+ }
+
+ /* clear memory */
+ free_printer_info_2(printer);
+
+ if (*needed > offered) {
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinter_level_3(Printer_entry *print_hnd, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ PRINTER_INFO_3 *printer=NULL;
+
+ if (!construct_printer_info_3(print_hnd, &printer, snum))
+ return WERR_NOMEM;
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_info_3(printer);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ free_printer_info_3(printer);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_info_3("", buffer, printer, 0);
+
+ /* clear memory */
+ free_printer_info_3(printer);
+
+ if (*needed > offered) {
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinter_level_4(Printer_entry *print_hnd, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ PRINTER_INFO_4 *printer=NULL;
+
+ if((printer=(PRINTER_INFO_4*)malloc(sizeof(PRINTER_INFO_4)))==NULL)
+ return WERR_NOMEM;
+
+ if (!construct_printer_info_4(print_hnd, printer, snum))
+ return WERR_NOMEM;
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_info_4(printer);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ free_printer_info_4(printer);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_info_4("", buffer, printer, 0);
+
+ /* clear memory */
+ free_printer_info_4(printer);
+
+ if (*needed > offered) {
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinter_level_5(Printer_entry *print_hnd, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ PRINTER_INFO_5 *printer=NULL;
+
+ if((printer=(PRINTER_INFO_5*)malloc(sizeof(PRINTER_INFO_5)))==NULL)
+ return WERR_NOMEM;
+
+ if (!construct_printer_info_5(print_hnd, printer, snum))
+ return WERR_NOMEM;
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_info_5(printer);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ free_printer_info_5(printer);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_info_5("", buffer, printer, 0);
+
+ /* clear memory */
+ free_printer_info_5(printer);
+
+ if (*needed > offered) {
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR getprinter_level_7(Printer_entry *print_hnd, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ PRINTER_INFO_7 *printer=NULL;
+
+ if((printer=(PRINTER_INFO_7*)malloc(sizeof(PRINTER_INFO_7)))==NULL)
+ return WERR_NOMEM;
+
+ if (!construct_printer_info_7(print_hnd, printer, snum))
+ return WERR_NOMEM;
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_info_7(printer);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ free_printer_info_7(printer);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_info_7("", buffer, printer, 0);
+
+ /* clear memory */
+ free_printer_info_7(printer);
+
+ if (*needed > offered) {
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_getprinter(pipes_struct *p, SPOOL_Q_GETPRINTER *q_u, SPOOL_R_GETPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 level = q_u->level;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+
+ int snum;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ *needed=0;
+
+ if (!get_printer_snum(p, handle, &snum))
+ return WERR_BADFID;
+
+ switch (level) {
+ case 0:
+ return getprinter_level_0(Printer, snum, buffer, offered, needed);
+ case 1:
+ return getprinter_level_1(Printer, snum, buffer, offered, needed);
+ case 2:
+ return getprinter_level_2(Printer, snum, buffer, offered, needed);
+ case 3:
+ return getprinter_level_3(Printer, snum, buffer, offered, needed);
+ case 4:
+ return getprinter_level_4(Printer, snum, buffer, offered, needed);
+ case 5:
+ return getprinter_level_5(Printer, snum, buffer, offered, needed);
+ case 7:
+ return getprinter_level_7(Printer, snum, buffer, offered, needed);
+ }
+ return WERR_UNKNOWN_LEVEL;
+}
+
+/********************************************************************
+ * fill a DRIVER_INFO_1 struct
+ ********************************************************************/
+
+static void fill_printer_driver_info_1(DRIVER_INFO_1 *info, NT_PRINTER_DRIVER_INFO_LEVEL driver, fstring servername, fstring architecture)
+{
+ init_unistr( &info->name, driver.info_3->name);
+}
+
+/********************************************************************
+ * construct_printer_driver_info_1
+ ********************************************************************/
+
+static WERROR construct_printer_driver_info_1(DRIVER_INFO_1 *info, int snum, fstring servername, fstring architecture, uint32 version)
+{
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+
+ ZERO_STRUCT(driver);
+
+ if (!W_ERROR_IS_OK(get_a_printer(NULL, &printer, 2, lp_const_servicename(snum))))
+ return WERR_INVALID_PRINTER_NAME;
+
+ if (!W_ERROR_IS_OK(get_a_printer_driver(&driver, 3, printer->info_2->drivername, architecture, version)))
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+
+ fill_printer_driver_info_1(info, driver, servername, architecture);
+
+ free_a_printer(&printer,2);
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * construct_printer_driver_info_2
+ * fill a printer_info_2 struct
+ ********************************************************************/
+
+static void fill_printer_driver_info_2(DRIVER_INFO_2 *info, NT_PRINTER_DRIVER_INFO_LEVEL driver, fstring servername)
+{
+ pstring temp;
+
+ info->version=driver.info_3->cversion;
+
+ init_unistr( &info->name, driver.info_3->name );
+ init_unistr( &info->architecture, driver.info_3->environment );
+
+
+ if (strlen(driver.info_3->driverpath)) {
+ slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->driverpath);
+ init_unistr( &info->driverpath, temp );
+ } else
+ init_unistr( &info->driverpath, "" );
+
+ if (strlen(driver.info_3->datafile)) {
+ slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->datafile);
+ init_unistr( &info->datafile, temp );
+ } else
+ init_unistr( &info->datafile, "" );
+
+ if (strlen(driver.info_3->configfile)) {
+ slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->configfile);
+ init_unistr( &info->configfile, temp );
+ } else
+ init_unistr( &info->configfile, "" );
+}
+
+/********************************************************************
+ * construct_printer_driver_info_2
+ * fill a printer_info_2 struct
+ ********************************************************************/
+
+static WERROR construct_printer_driver_info_2(DRIVER_INFO_2 *info, int snum, fstring servername, fstring architecture, uint32 version)
+{
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+
+ ZERO_STRUCT(printer);
+ ZERO_STRUCT(driver);
+
+ if (!W_ERROR_IS_OK(get_a_printer(NULL, &printer, 2, lp_const_servicename(snum))))
+ return WERR_INVALID_PRINTER_NAME;
+
+ if (!W_ERROR_IS_OK(get_a_printer_driver(&driver, 3, printer->info_2->drivername, architecture, version)))
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+
+ fill_printer_driver_info_2(info, driver, servername);
+
+ free_a_printer(&printer,2);
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * copy a strings array and convert to UNICODE
+ *
+ * convert an array of ascii string to a UNICODE string
+ ********************************************************************/
+
+static uint32 init_unistr_array(uint16 **uni_array, fstring *char_array, const char *servername)
+{
+ int i=0;
+ int j=0;
+ const char *v;
+ pstring line;
+ uint16 *tuary;
+
+ DEBUG(6,("init_unistr_array\n"));
+ *uni_array=NULL;
+
+ while (True)
+ {
+ if ( !char_array )
+ v = "";
+ else
+ {
+ v = char_array[i];
+ if (!v)
+ v = ""; /* hack to handle null lists */
+ }
+
+ /* hack to allow this to be used in places other than when generating
+ the list of dependent files */
+
+ if ( servername )
+ slprintf( line, sizeof(line)-1, "\\\\%s%s", servername, v );
+ else
+ pstrcpy( line, v );
+
+ DEBUGADD(6,("%d:%s:%lu\n", i, line, (unsigned long)strlen(line)));
+
+ /* add one extra unit16 for the second terminating NULL */
+
+ if ( (tuary=Realloc(*uni_array, (j+1+strlen(line)+2)*sizeof(uint16))) == NULL ) {
+ DEBUG(2,("init_unistr_array: Realloc error\n" ));
+ return 0;
+ } else
+ *uni_array = tuary;
+
+ if ( !strlen(v) )
+ break;
+
+ j += (rpcstr_push((*uni_array+j), line, sizeof(uint16)*strlen(line)+2, STR_TERMINATE) / sizeof(uint16));
+ i++;
+ }
+
+ if (*uni_array) {
+ /* special case for ""; we need to add both NULL's here */
+ if (!j)
+ (*uni_array)[j++]=0x0000;
+ (*uni_array)[j]=0x0000;
+ }
+
+ DEBUGADD(6,("last one:done\n"));
+
+ /* return size of array in uint16's */
+
+ return j+1;
+}
+
+/********************************************************************
+ * construct_printer_info_3
+ * fill a printer_info_3 struct
+ ********************************************************************/
+
+static void fill_printer_driver_info_3(DRIVER_INFO_3 *info, NT_PRINTER_DRIVER_INFO_LEVEL driver, fstring servername)
+{
+ pstring temp;
+
+ ZERO_STRUCTP(info);
+
+ info->version=driver.info_3->cversion;
+
+ init_unistr( &info->name, driver.info_3->name );
+ init_unistr( &info->architecture, driver.info_3->environment );
+
+ if (strlen(driver.info_3->driverpath)) {
+ slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->driverpath);
+ init_unistr( &info->driverpath, temp );
+ } else
+ init_unistr( &info->driverpath, "" );
+
+ if (strlen(driver.info_3->datafile)) {
+ slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->datafile);
+ init_unistr( &info->datafile, temp );
+ } else
+ init_unistr( &info->datafile, "" );
+
+ if (strlen(driver.info_3->configfile)) {
+ slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->configfile);
+ init_unistr( &info->configfile, temp );
+ } else
+ init_unistr( &info->configfile, "" );
+
+ if (strlen(driver.info_3->helpfile)) {
+ slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->helpfile);
+ init_unistr( &info->helpfile, temp );
+ } else
+ init_unistr( &info->helpfile, "" );
+
+ init_unistr( &info->monitorname, driver.info_3->monitorname );
+ init_unistr( &info->defaultdatatype, driver.info_3->defaultdatatype );
+
+ info->dependentfiles=NULL;
+ init_unistr_array(&info->dependentfiles, driver.info_3->dependentfiles, servername);
+}
+
+/********************************************************************
+ * construct_printer_info_3
+ * fill a printer_info_3 struct
+ ********************************************************************/
+
+static WERROR construct_printer_driver_info_3(DRIVER_INFO_3 *info, int snum, fstring servername, fstring architecture, uint32 version)
+{
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+ WERROR status;
+ ZERO_STRUCT(driver);
+
+ status=get_a_printer(NULL, &printer, 2, lp_const_servicename(snum) );
+ DEBUG(8,("construct_printer_driver_info_3: status: %s\n", dos_errstr(status)));
+ if (!W_ERROR_IS_OK(status))
+ return WERR_INVALID_PRINTER_NAME;
+
+ status=get_a_printer_driver(&driver, 3, printer->info_2->drivername, architecture, version);
+ DEBUG(8,("construct_printer_driver_info_3: status: %s\n", dos_errstr(status)));
+
+#if 0 /* JERRY */
+
+ /*
+ * I put this code in during testing. Helpful when commenting out the
+ * support for DRIVER_INFO_6 in regards to win2k. Not needed in general
+ * as win2k always queries the driver using an infor level of 6.
+ * I've left it in (but ifdef'd out) because I'll probably
+ * use it in experimentation again in the future. --jerry 22/01/2002
+ */
+
+ if (!W_ERROR_IS_OK(status)) {
+ /*
+ * Is this a W2k client ?
+ */
+ if (version == 3) {
+ /* Yes - try again with a WinNT driver. */
+ version = 2;
+ status=get_a_printer_driver(&driver, 3, printer->info_2->drivername, architecture, version);
+ DEBUG(8,("construct_printer_driver_info_3: status: %s\n", dos_errstr(status)));
+ }
+#endif
+
+ if (!W_ERROR_IS_OK(status)) {
+ free_a_printer(&printer,2);
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+ }
+
+#if 0 /* JERRY */
+ }
+#endif
+
+
+ fill_printer_driver_info_3(info, driver, servername);
+
+ free_a_printer(&printer,2);
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * construct_printer_info_6
+ * fill a printer_info_6 struct - we know that driver is really level 3. This sucks. JRA.
+ ********************************************************************/
+
+static void fill_printer_driver_info_6(DRIVER_INFO_6 *info, NT_PRINTER_DRIVER_INFO_LEVEL driver, fstring servername)
+{
+ pstring temp;
+ fstring nullstr;
+
+ ZERO_STRUCTP(info);
+ memset(&nullstr, '\0', sizeof(fstring));
+
+ info->version=driver.info_3->cversion;
+
+ init_unistr( &info->name, driver.info_3->name );
+ init_unistr( &info->architecture, driver.info_3->environment );
+
+ if (strlen(driver.info_3->driverpath)) {
+ slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->driverpath);
+ init_unistr( &info->driverpath, temp );
+ } else
+ init_unistr( &info->driverpath, "" );
+
+ if (strlen(driver.info_3->datafile)) {
+ slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->datafile);
+ init_unistr( &info->datafile, temp );
+ } else
+ init_unistr( &info->datafile, "" );
+
+ if (strlen(driver.info_3->configfile)) {
+ slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->configfile);
+ init_unistr( &info->configfile, temp );
+ } else
+ init_unistr( &info->configfile, "" );
+
+ if (strlen(driver.info_3->helpfile)) {
+ slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->helpfile);
+ init_unistr( &info->helpfile, temp );
+ } else
+ init_unistr( &info->helpfile, "" );
+
+ init_unistr( &info->monitorname, driver.info_3->monitorname );
+ init_unistr( &info->defaultdatatype, driver.info_3->defaultdatatype );
+
+ info->dependentfiles = NULL;
+ init_unistr_array( &info->dependentfiles, driver.info_3->dependentfiles, servername );
+
+ info->previousdrivernames=NULL;
+ init_unistr_array(&info->previousdrivernames, &nullstr, servername);
+
+ info->driver_date.low=0;
+ info->driver_date.high=0;
+
+ info->padding=0;
+ info->driver_version_low=0;
+ info->driver_version_high=0;
+
+ init_unistr( &info->mfgname, "");
+ init_unistr( &info->oem_url, "");
+ init_unistr( &info->hardware_id, "");
+ init_unistr( &info->provider, "");
+}
+
+/********************************************************************
+ * construct_printer_info_6
+ * fill a printer_info_6 struct
+ ********************************************************************/
+
+static WERROR construct_printer_driver_info_6(DRIVER_INFO_6 *info, int snum,
+ fstring servername, fstring architecture, uint32 version)
+{
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+ WERROR status;
+
+ ZERO_STRUCT(driver);
+
+ status=get_a_printer(NULL, &printer, 2, lp_const_servicename(snum) );
+
+ DEBUG(8,("construct_printer_driver_info_6: status: %s\n", dos_errstr(status)));
+
+ if (!W_ERROR_IS_OK(status))
+ return WERR_INVALID_PRINTER_NAME;
+
+ status = get_a_printer_driver(&driver, 3, printer->info_2->drivername, architecture, version);
+
+ DEBUG(8,("construct_printer_driver_info_6: status: %s\n", dos_errstr(status)));
+
+ if (!W_ERROR_IS_OK(status))
+ {
+ /*
+ * Is this a W2k client ?
+ */
+
+ if (version < 3) {
+ free_a_printer(&printer,2);
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+ }
+
+ /* Yes - try again with a WinNT driver. */
+ version = 2;
+ status=get_a_printer_driver(&driver, 3, printer->info_2->drivername, architecture, version);
+ DEBUG(8,("construct_printer_driver_info_6: status: %s\n", dos_errstr(status)));
+ if (!W_ERROR_IS_OK(status)) {
+ free_a_printer(&printer,2);
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+ }
+ }
+
+ fill_printer_driver_info_6(info, driver, servername);
+
+ free_a_printer(&printer,2);
+ free_a_printer_driver(driver, 3);
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void free_printer_driver_info_3(DRIVER_INFO_3 *info)
+{
+ SAFE_FREE(info->dependentfiles);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void free_printer_driver_info_6(DRIVER_INFO_6 *info)
+{
+ SAFE_FREE(info->dependentfiles);
+
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinterdriver2_level1(fstring servername, fstring architecture, uint32 version, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ DRIVER_INFO_1 *info=NULL;
+ WERROR status;
+
+ if((info=(DRIVER_INFO_1 *)malloc(sizeof(DRIVER_INFO_1))) == NULL)
+ return WERR_NOMEM;
+
+ status=construct_printer_driver_info_1(info, snum, servername, architecture, version);
+ if (!W_ERROR_IS_OK(status)) {
+ SAFE_FREE(info);
+ return status;
+ }
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_driver_info_1(info);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(info);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_driver_info_1("", buffer, info, 0);
+
+ /* clear memory */
+ SAFE_FREE(info);
+
+ if (*needed > offered)
+ return WERR_INSUFFICIENT_BUFFER;
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinterdriver2_level2(fstring servername, fstring architecture, uint32 version, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ DRIVER_INFO_2 *info=NULL;
+ WERROR status;
+
+ if((info=(DRIVER_INFO_2 *)malloc(sizeof(DRIVER_INFO_2))) == NULL)
+ return WERR_NOMEM;
+
+ status=construct_printer_driver_info_2(info, snum, servername, architecture, version);
+ if (!W_ERROR_IS_OK(status)) {
+ SAFE_FREE(info);
+ return status;
+ }
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_driver_info_2(info);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(info);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_driver_info_2("", buffer, info, 0);
+
+ /* clear memory */
+ SAFE_FREE(info);
+
+ if (*needed > offered)
+ return WERR_INSUFFICIENT_BUFFER;
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinterdriver2_level3(fstring servername, fstring architecture, uint32 version, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ DRIVER_INFO_3 info;
+ WERROR status;
+
+ ZERO_STRUCT(info);
+
+ status=construct_printer_driver_info_3(&info, snum, servername, architecture, version);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_driver_info_3(&info);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ free_printer_driver_info_3(&info);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_driver_info_3("", buffer, &info, 0);
+
+ free_printer_driver_info_3(&info);
+
+ if (*needed > offered)
+ return WERR_INSUFFICIENT_BUFFER;
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinterdriver2_level6(fstring servername, fstring architecture, uint32 version, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ DRIVER_INFO_6 info;
+ WERROR status;
+
+ ZERO_STRUCT(info);
+
+ status=construct_printer_driver_info_6(&info, snum, servername, architecture, version);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_driver_info_6(&info);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ free_printer_driver_info_6(&info);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_driver_info_6("", buffer, &info, 0);
+
+ free_printer_driver_info_6(&info);
+
+ if (*needed > offered)
+ return WERR_INSUFFICIENT_BUFFER;
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_getprinterdriver2(pipes_struct *p, SPOOL_Q_GETPRINTERDRIVER2 *q_u, SPOOL_R_GETPRINTERDRIVER2 *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ UNISTR2 *uni_arch = &q_u->architecture;
+ uint32 level = q_u->level;
+ uint32 clientmajorversion = q_u->clientmajorversion;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ uint32 *servermajorversion = &r_u->servermajorversion;
+ uint32 *serverminorversion = &r_u->serverminorversion;
+
+ fstring servername;
+ fstring architecture;
+ int snum;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(4,("_spoolss_getprinterdriver2\n"));
+
+ *needed = 0;
+ *servermajorversion = 0;
+ *serverminorversion = 0;
+
+ fstrcpy(servername, get_called_name());
+ unistr2_to_ascii(architecture, uni_arch, sizeof(architecture)-1);
+
+ if (!get_printer_snum(p, handle, &snum))
+ return WERR_BADFID;
+
+ switch (level) {
+ case 1:
+ return getprinterdriver2_level1(servername, architecture, clientmajorversion, snum, buffer, offered, needed);
+ case 2:
+ return getprinterdriver2_level2(servername, architecture, clientmajorversion, snum, buffer, offered, needed);
+ case 3:
+ return getprinterdriver2_level3(servername, architecture, clientmajorversion, snum, buffer, offered, needed);
+ case 6:
+ return getprinterdriver2_level6(servername, architecture, clientmajorversion, snum, buffer, offered, needed);
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_startpageprinter(pipes_struct *p, SPOOL_Q_STARTPAGEPRINTER *q_u, SPOOL_R_STARTPAGEPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ if (!Printer) {
+ DEBUG(3,("Error in startpageprinter printer handle\n"));
+ return WERR_BADFID;
+ }
+
+ Printer->page_started=True;
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_endpageprinter(pipes_struct *p, SPOOL_Q_ENDPAGEPRINTER *q_u, SPOOL_R_ENDPAGEPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ int snum;
+
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_endpageprinter: Invalid handle (%s:%u:%u).\n",OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if (!get_printer_snum(p, handle, &snum))
+ return WERR_BADFID;
+
+ Printer->page_started=False;
+ print_job_endpage(snum, Printer->jobid);
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+WERROR _spoolss_startdocprinter(pipes_struct *p, SPOOL_Q_STARTDOCPRINTER *q_u, SPOOL_R_STARTDOCPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ DOC_INFO *docinfo = &q_u->doc_info_container.docinfo;
+ uint32 *jobid = &r_u->jobid;
+
+ DOC_INFO_1 *info_1 = &docinfo->doc_info_1;
+ int snum;
+ pstring jobname;
+ fstring datatype;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+ struct current_user user;
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_startdocprinter: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ get_current_user(&user, p);
+
+ /*
+ * a nice thing with NT is it doesn't listen to what you tell it.
+ * when asked to send _only_ RAW datas, it tries to send datas
+ * in EMF format.
+ *
+ * So I add checks like in NT Server ...
+ */
+
+ if (info_1->p_datatype != 0) {
+ unistr2_to_ascii(datatype, &info_1->datatype, sizeof(datatype));
+ if (strcmp(datatype, "RAW") != 0) {
+ (*jobid)=0;
+ return WERR_INVALID_DATATYPE;
+ }
+ }
+
+ /* get the share number of the printer */
+ if (!get_printer_snum(p, handle, &snum)) {
+ return WERR_BADFID;
+ }
+
+ unistr2_to_ascii(jobname, &info_1->docname, sizeof(jobname));
+
+ Printer->jobid = print_job_start(&user, snum, jobname, Printer->nt_devmode);
+
+ /* An error occured in print_job_start() so return an appropriate
+ NT error code. */
+
+ if (Printer->jobid == -1) {
+ return map_werror_from_unix(errno);
+ }
+
+ Printer->document_started=True;
+ (*jobid) = Printer->jobid;
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+WERROR _spoolss_enddocprinter(pipes_struct *p, SPOOL_Q_ENDDOCPRINTER *q_u, SPOOL_R_ENDDOCPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+
+ return _spoolss_enddocprinter_internal(p, handle);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_writeprinter(pipes_struct *p, SPOOL_Q_WRITEPRINTER *q_u, SPOOL_R_WRITEPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 buffer_size = q_u->buffer_size;
+ uint8 *buffer = q_u->buffer;
+ uint32 *buffer_written = &q_u->buffer_size2;
+ int snum;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_writeprinter: Invalid handle (%s:%u:%u)\n",OUR_HANDLE(handle)));
+ r_u->buffer_written = q_u->buffer_size2;
+ return WERR_BADFID;
+ }
+
+ if (!get_printer_snum(p, handle, &snum))
+ return WERR_BADFID;
+
+ (*buffer_written) = print_job_write(snum, Printer->jobid, (char *)buffer, buffer_size);
+ if (*buffer_written == -1) {
+ r_u->buffer_written = 0;
+ if (errno == ENOSPC)
+ return WERR_NO_SPOOL_SPACE;
+ else
+ return WERR_ACCESS_DENIED;
+ }
+
+ r_u->buffer_written = q_u->buffer_size2;
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+static WERROR control_printer(POLICY_HND *handle, uint32 command,
+ pipes_struct *p)
+{
+ struct current_user user;
+ int snum;
+ WERROR errcode = WERR_BADFUNC;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ get_current_user(&user, p);
+
+ if (!Printer) {
+ DEBUG(2,("control_printer: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if (!get_printer_snum(p, handle, &snum))
+ return WERR_BADFID;
+
+ switch (command) {
+ case PRINTER_CONTROL_PAUSE:
+ if (print_queue_pause(&user, snum, &errcode)) {
+ errcode = WERR_OK;
+ }
+ break;
+ case PRINTER_CONTROL_RESUME:
+ case PRINTER_CONTROL_UNPAUSE:
+ if (print_queue_resume(&user, snum, &errcode)) {
+ errcode = WERR_OK;
+ }
+ break;
+ case PRINTER_CONTROL_PURGE:
+ if (print_queue_purge(&user, snum, &errcode)) {
+ errcode = WERR_OK;
+ }
+ break;
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return errcode;
+}
+
+/********************************************************************
+ * api_spoolss_abortprinter
+ * From MSDN: "Deletes printer's spool file if printer is configured
+ * for spooling"
+ ********************************************************************/
+
+WERROR _spoolss_abortprinter(pipes_struct *p, SPOOL_Q_ABORTPRINTER *q_u, SPOOL_R_ABORTPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+ int snum;
+ struct current_user user;
+ WERROR errcode = WERR_OK;
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_abortprinter: Invalid handle (%s:%u:%u)\n",OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if (!get_printer_snum(p, handle, &snum))
+ return WERR_BADFID;
+
+ get_current_user( &user, p );
+
+ print_job_delete( &user, snum, Printer->jobid, &errcode );
+
+ return errcode;
+}
+
+/********************************************************************
+ * called by spoolss_api_setprinter
+ * when updating a printer description
+ ********************************************************************/
+
+static WERROR update_printer_sec(POLICY_HND *handle, uint32 level,
+ const SPOOL_PRINTER_INFO_LEVEL *info,
+ pipes_struct *p, SEC_DESC_BUF *secdesc_ctr)
+{
+ SEC_DESC_BUF *new_secdesc_ctr = NULL, *old_secdesc_ctr = NULL;
+ struct current_user user;
+ WERROR result;
+ int snum;
+
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ if (!Printer || !get_printer_snum(p, handle, &snum)) {
+ DEBUG(2,("update_printer_sec: Invalid handle (%s:%u:%u)\n",
+ OUR_HANDLE(handle)));
+
+ result = WERR_BADFID;
+ goto done;
+ }
+
+ /* NT seems to like setting the security descriptor even though
+ nothing may have actually changed. This causes annoying
+ dialog boxes when the user doesn't have permission to change
+ the security descriptor. */
+
+ nt_printing_getsec(p->mem_ctx, Printer->dev.handlename, &old_secdesc_ctr);
+
+ if (DEBUGLEVEL >= 10) {
+ SEC_ACL *the_acl;
+ int i;
+
+ the_acl = old_secdesc_ctr->sec->dacl;
+ DEBUG(10, ("old_secdesc_ctr for %s has %d aces:\n",
+ PRINTERNAME(snum), the_acl->num_aces));
+
+ for (i = 0; i < the_acl->num_aces; i++) {
+ fstring sid_str;
+
+ sid_to_string(sid_str, &the_acl->ace[i].trustee);
+
+ DEBUG(10, ("%s 0x%08x\n", sid_str,
+ the_acl->ace[i].info.mask));
+ }
+
+ the_acl = secdesc_ctr->sec->dacl;
+
+ if (the_acl) {
+ DEBUG(10, ("secdesc_ctr for %s has %d aces:\n",
+ PRINTERNAME(snum), the_acl->num_aces));
+
+ for (i = 0; i < the_acl->num_aces; i++) {
+ fstring sid_str;
+
+ sid_to_string(sid_str, &the_acl->ace[i].trustee);
+
+ DEBUG(10, ("%s 0x%08x\n", sid_str,
+ the_acl->ace[i].info.mask));
+ }
+ } else {
+ DEBUG(10, ("dacl for secdesc_ctr is NULL\n"));
+ }
+ }
+
+ new_secdesc_ctr = sec_desc_merge(p->mem_ctx, secdesc_ctr, old_secdesc_ctr);
+
+ if (sec_desc_equal(new_secdesc_ctr->sec, old_secdesc_ctr->sec)) {
+ result = WERR_OK;
+ goto done;
+ }
+
+ /* Work out which user is performing the operation */
+
+ get_current_user(&user, p);
+
+ /* Check the user has permissions to change the security
+ descriptor. By experimentation with two NT machines, the user
+ requires Full Access to the printer to change security
+ information. */
+
+ if (!print_access_check(&user, snum, PRINTER_ACCESS_ADMINISTER)) {
+ result = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ result = nt_printing_setsec(Printer->dev.handlename, new_secdesc_ctr);
+
+ done:
+
+ return result;
+}
+
+/********************************************************************
+ Do Samba sanity checks on a printer info struct.
+ this has changed purpose: it now "canonicalises" printer
+ info from a client rather than just checking it is correct
+ ********************************************************************/
+
+static BOOL check_printer_ok(NT_PRINTER_INFO_LEVEL_2 *info, int snum)
+{
+ DEBUG(5,("check_printer_ok: servername=%s printername=%s sharename=%s portname=%s drivername=%s comment=%s location=%s\n",
+ info->servername, info->printername, info->sharename, info->portname, info->drivername, info->comment, info->location));
+
+ /* we force some elements to "correct" values */
+ slprintf(info->servername, sizeof(info->servername)-1, "\\\\%s", get_called_name());
+ fstrcpy(info->sharename, lp_servicename(snum));
+ slprintf(info->printername, sizeof(info->printername)-1, "\\\\%s\\%s",
+ get_called_name(), info->sharename);
+ info->attributes = PRINTER_ATTRIBUTE_SAMBA;
+
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL add_printer_hook(NT_PRINTER_INFO_LEVEL *printer)
+{
+ extern userdom_struct current_user_info;
+ char *cmd = lp_addprinter_cmd();
+ char **qlines;
+ pstring command;
+ int numlines;
+ int ret;
+ int fd;
+ fstring remote_machine = "%m";
+
+ standard_sub_basic(current_user_info.smb_name, remote_machine,sizeof(remote_machine));
+
+ slprintf(command, sizeof(command)-1, "%s \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"",
+ cmd, printer->info_2->printername, printer->info_2->sharename,
+ printer->info_2->portname, printer->info_2->drivername,
+ printer->info_2->location, printer->info_2->comment, remote_machine);
+
+ DEBUG(10,("Running [%s]\n", command));
+ ret = smbrun(command, &fd);
+ DEBUGADD(10,("returned [%d]\n", ret));
+
+ if ( ret != 0 ) {
+ if (fd != -1)
+ close(fd);
+ return False;
+ }
+
+ numlines = 0;
+ /* Get lines and convert them back to dos-codepage */
+ qlines = fd_lines_load(fd, &numlines);
+ DEBUGADD(10,("Lines returned = [%d]\n", numlines));
+ close(fd);
+
+ if(numlines) {
+ /* Set the portname to what the script says the portname should be. */
+ strncpy(printer->info_2->portname, qlines[0], sizeof(printer->info_2->portname));
+ DEBUGADD(6,("Line[0] = [%s]\n", qlines[0]));
+
+ /* Send SIGHUP to process group... is there a better way? */
+ kill(0, SIGHUP);
+
+ /* reload our services immediately */
+ reload_services( False );
+ }
+
+ file_lines_free(qlines);
+ return True;
+}
+
+/********************************************************************
+ * Called by spoolss_api_setprinter
+ * when updating a printer description.
+ ********************************************************************/
+
+static WERROR update_printer(pipes_struct *p, POLICY_HND *handle, uint32 level,
+ const SPOOL_PRINTER_INFO_LEVEL *info,
+ DEVICEMODE *devmode)
+{
+ int snum;
+ NT_PRINTER_INFO_LEVEL *printer = NULL, *old_printer = NULL;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+ WERROR result;
+ UNISTR2 buffer;
+ fstring asc_buffer;
+
+ DEBUG(8,("update_printer\n"));
+
+ result = WERR_OK;
+
+ if (!Printer) {
+ result = WERR_BADFID;
+ goto done;
+ }
+
+ if (!get_printer_snum(p, handle, &snum)) {
+ result = WERR_BADFID;
+ goto done;
+ }
+
+ if (!W_ERROR_IS_OK(get_a_printer(Printer, &printer, 2, lp_const_servicename(snum))) ||
+ (!W_ERROR_IS_OK(get_a_printer(Printer, &old_printer, 2, lp_const_servicename(snum))))) {
+ result = WERR_BADFID;
+ goto done;
+ }
+
+ DEBUGADD(8,("Converting info_2 struct\n"));
+
+ /*
+ * convert_printer_info converts the incoming
+ * info from the client and overwrites the info
+ * just read from the tdb in the pointer 'printer'.
+ */
+
+ if (!convert_printer_info(info, printer, level)) {
+ result = WERR_NOMEM;
+ goto done;
+ }
+
+ if (devmode) {
+ /* we have a valid devmode
+ convert it and link it*/
+
+ DEBUGADD(8,("update_printer: Converting the devicemode struct\n"));
+ if (!convert_devicemode(printer->info_2->printername, devmode,
+ &printer->info_2->devmode)) {
+ result = WERR_NOMEM;
+ goto done;
+ }
+ }
+
+ /* Do sanity check on the requested changes for Samba */
+
+ if (!check_printer_ok(printer->info_2, snum)) {
+ result = WERR_INVALID_PARAM;
+ goto done;
+ }
+
+ /* FIXME!!! If the driver has changed we really should verify that
+ it is installed before doing much else --jerry */
+
+ /* Check calling user has permission to update printer description */
+
+ if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) {
+ DEBUG(3, ("update_printer: printer property change denied by handle\n"));
+ result = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ /* Call addprinter hook */
+ /* Check changes to see if this is really needed */
+
+ if ( *lp_addprinter_cmd()
+ && (!strequal(printer->info_2->drivername, old_printer->info_2->drivername)
+ || !strequal(printer->info_2->comment, old_printer->info_2->comment)
+ || !strequal(printer->info_2->portname, old_printer->info_2->portname)
+ || !strequal(printer->info_2->location, old_printer->info_2->location)) )
+ {
+ if ( !add_printer_hook(printer) ) {
+ result = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ /*
+ * make sure we actually reload the services after
+ * this as smb.conf could have a new section in it
+ * .... shouldn't .... but could
+ */
+ reload_services(False);
+ }
+
+ /*
+ * When a *new* driver is bound to a printer, the drivername is used to
+ * lookup previously saved driver initialization info, which is then
+ * bound to the printer, simulating what happens in the Windows arch.
+ */
+ if (!strequal(printer->info_2->drivername, old_printer->info_2->drivername))
+ {
+ if (!set_driver_init(printer, 2))
+ {
+ DEBUG(5,("update_printer: Error restoring driver initialization data for driver [%s]!\n",
+ printer->info_2->drivername));
+ }
+
+ DEBUG(10,("update_printer: changing driver [%s]! Sending event!\n",
+ printer->info_2->drivername));
+
+ notify_printer_driver(snum, printer->info_2->drivername);
+ }
+
+ /*
+ * flag which changes actually occured. This is a small subset of
+ * all the possible changes. We also have to update things in the
+ * DsSpooler key.
+ */
+
+ if (!strequal(printer->info_2->comment, old_printer->info_2->comment)) {
+ init_unistr2( &buffer, printer->info_2->comment, UNI_STR_TERMINATE);
+ set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "description",
+ REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+
+ notify_printer_comment(snum, printer->info_2->comment);
+ }
+
+ if (!strequal(printer->info_2->sharename, old_printer->info_2->sharename)) {
+ init_unistr2( &buffer, printer->info_2->sharename, UNI_STR_TERMINATE);
+ set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "printerName",
+ REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+ set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "shareName",
+ REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+
+ notify_printer_sharename(snum, printer->info_2->sharename);
+ }
+
+ if (!strequal(printer->info_2->portname, old_printer->info_2->portname)) {
+ init_unistr2( &buffer, printer->info_2->portname, UNI_STR_TERMINATE);
+ set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "portName",
+ REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+
+ notify_printer_port(snum, printer->info_2->portname);
+ }
+
+ if (!strequal(printer->info_2->location, old_printer->info_2->location)) {
+ init_unistr2( &buffer, printer->info_2->location, UNI_STR_TERMINATE);
+ set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "location",
+ REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+
+ notify_printer_location(snum, printer->info_2->location);
+ }
+
+ /* here we need to update some more DsSpooler keys */
+ /* uNCName, serverName, shortServerName */
+
+ init_unistr2( &buffer, global_myname(), UNI_STR_TERMINATE);
+ set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "serverName",
+ REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+ set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "shortServerName",
+ REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+
+ slprintf( asc_buffer, sizeof(asc_buffer)-1, "\\\\%s\\%s",
+ global_myname(), printer->info_2->sharename );
+ init_unistr2( &buffer, asc_buffer, UNI_STR_TERMINATE);
+ set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "uNCName",
+ REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+
+ /* Update printer info */
+ result = mod_a_printer(*printer, 2);
+
+done:
+ free_a_printer(&printer, 2);
+ free_a_printer(&old_printer, 2);
+
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+static WERROR publish_or_unpublish_printer(pipes_struct *p, POLICY_HND *handle,
+ const SPOOL_PRINTER_INFO_LEVEL *info)
+{
+#ifdef HAVE_ADS
+ SPOOL_PRINTER_INFO_LEVEL_7 *info7 = info->info_7;
+ int snum;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ DEBUG(5,("publish_or_unpublish_printer, action = %d\n",info7->action));
+
+ if (!Printer)
+ return WERR_BADFID;
+
+ if (!get_printer_snum(p, handle, &snum))
+ return WERR_BADFID;
+
+ nt_printer_publish(Printer, snum, info7->action);
+
+ return WERR_OK;
+#else
+ return WERR_UNKNOWN_LEVEL;
+#endif
+}
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_setprinter(pipes_struct *p, SPOOL_Q_SETPRINTER *q_u, SPOOL_R_SETPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 level = q_u->level;
+ SPOOL_PRINTER_INFO_LEVEL *info = &q_u->info;
+ DEVMODE_CTR devmode_ctr = q_u->devmode_ctr;
+ SEC_DESC_BUF *secdesc_ctr = q_u->secdesc_ctr;
+ uint32 command = q_u->command;
+
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_setprinter: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ /* check the level */
+ switch (level) {
+ case 0:
+ return control_printer(handle, command, p);
+ case 2:
+ return update_printer(p, handle, level, info, devmode_ctr.devmode);
+ case 3:
+ return update_printer_sec(handle, level, info, p,
+ secdesc_ctr);
+ case 7:
+ return publish_or_unpublish_printer(p, handle, info);
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_fcpn(pipes_struct *p, SPOOL_Q_FCPN *q_u, SPOOL_R_FCPN *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ Printer_entry *Printer= find_printer_index_by_hnd(p, handle);
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_fcpn: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if (Printer->notify.client_connected==True) {
+ int snum = -1;
+
+ if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER)
+ snum = -1;
+ else if ( (Printer->printer_type == PRINTER_HANDLE_IS_PRINTER) &&
+ !get_printer_snum(p, handle, &snum) )
+ return WERR_BADFID;
+
+ srv_spoolss_replycloseprinter(snum, &Printer->notify.client_hnd);
+ }
+
+ Printer->notify.flags=0;
+ Printer->notify.options=0;
+ Printer->notify.localmachine[0]='\0';
+ Printer->notify.printerlocal=0;
+ if (Printer->notify.option)
+ free_spool_notify_option(&Printer->notify.option);
+ Printer->notify.client_connected=False;
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_addjob(pipes_struct *p, SPOOL_Q_ADDJOB *q_u, SPOOL_R_ADDJOB *r_u)
+{
+ /* that's an [in out] buffer (despite appearences to the contrary) */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+
+ r_u->needed = 0;
+ return WERR_INVALID_PARAM; /* this is what a NT server
+ returns for AddJob. AddJob
+ must fail on non-local
+ printers */
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void fill_job_info_1(JOB_INFO_1 *job_info, print_queue_struct *queue,
+ int position, int snum)
+{
+ pstring temp_name;
+
+ struct tm *t;
+
+ t=gmtime(&queue->time);
+ slprintf(temp_name, sizeof(temp_name)-1, "\\\\%s", get_called_name());
+
+ job_info->jobid=queue->job;
+ init_unistr(&job_info->printername, lp_servicename(snum));
+ init_unistr(&job_info->machinename, temp_name);
+ init_unistr(&job_info->username, queue->fs_user);
+ init_unistr(&job_info->document, queue->fs_file);
+ init_unistr(&job_info->datatype, "RAW");
+ init_unistr(&job_info->text_status, "");
+ job_info->status=nt_printj_status(queue->status);
+ job_info->priority=queue->priority;
+ job_info->position=position;
+ job_info->totalpages=queue->page_count;
+ job_info->pagesprinted=0;
+
+ make_systemtime(&job_info->submitted, t);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL fill_job_info_2(JOB_INFO_2 *job_info, print_queue_struct *queue,
+ int position, int snum,
+ NT_PRINTER_INFO_LEVEL *ntprinter,
+ DEVICEMODE *devmode)
+{
+ pstring temp_name;
+ struct tm *t;
+
+ t=gmtime(&queue->time);
+ slprintf(temp_name, sizeof(temp_name)-1, "\\\\%s", get_called_name());
+
+ job_info->jobid=queue->job;
+
+ init_unistr(&job_info->printername, ntprinter->info_2->printername);
+
+ init_unistr(&job_info->machinename, temp_name);
+ init_unistr(&job_info->username, queue->fs_user);
+ init_unistr(&job_info->document, queue->fs_file);
+ init_unistr(&job_info->notifyname, queue->fs_user);
+ init_unistr(&job_info->datatype, "RAW");
+ init_unistr(&job_info->printprocessor, "winprint");
+ init_unistr(&job_info->parameters, "");
+ init_unistr(&job_info->drivername, ntprinter->info_2->drivername);
+ init_unistr(&job_info->text_status, "");
+
+/* and here the security descriptor */
+
+ job_info->status=nt_printj_status(queue->status);
+ job_info->priority=queue->priority;
+ job_info->position=position;
+ job_info->starttime=0;
+ job_info->untiltime=0;
+ job_info->totalpages=queue->page_count;
+ job_info->size=queue->size;
+ make_systemtime(&(job_info->submitted), t);
+ job_info->timeelapsed=0;
+ job_info->pagesprinted=0;
+
+ job_info->devmode = devmode;
+
+ return (True);
+}
+
+/****************************************************************************
+ Enumjobs at level 1.
+****************************************************************************/
+
+static WERROR enumjobs_level1(print_queue_struct *queue, int snum,
+ NEW_BUFFER *buffer, uint32 offered,
+ uint32 *needed, uint32 *returned)
+{
+ JOB_INFO_1 *info;
+ int i;
+
+ info=(JOB_INFO_1 *)malloc(*returned*sizeof(JOB_INFO_1));
+ if (info==NULL) {
+ SAFE_FREE(queue);
+ *returned=0;
+ return WERR_NOMEM;
+ }
+
+ for (i=0; i<*returned; i++)
+ fill_job_info_1(&info[i], &queue[i], i, snum);
+
+ SAFE_FREE(queue);
+
+ /* check the required size. */
+ for (i=0; i<*returned; i++)
+ (*needed) += spoolss_size_job_info_1(&info[i]);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(info);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ for (i=0; i<*returned; i++)
+ smb_io_job_info_1("", buffer, &info[i], 0);
+
+ /* clear memory */
+ SAFE_FREE(info);
+
+ if (*needed > offered) {
+ *returned=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ Enumjobs at level 2.
+****************************************************************************/
+
+static WERROR enumjobs_level2(print_queue_struct *queue, int snum,
+ NEW_BUFFER *buffer, uint32 offered,
+ uint32 *needed, uint32 *returned)
+{
+ NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+ JOB_INFO_2 *info = NULL;
+ int i;
+ WERROR result;
+ DEVICEMODE *devmode = NULL;
+
+ info=(JOB_INFO_2 *)malloc(*returned*sizeof(JOB_INFO_2));
+ if (info==NULL) {
+ *returned=0;
+ result = WERR_NOMEM;
+ goto done;
+ }
+
+ result = get_a_printer(NULL, &ntprinter, 2, lp_servicename(snum));
+ if (!W_ERROR_IS_OK(result)) {
+ *returned = 0;
+ goto done;
+ }
+
+ /* this should not be a failure condition if the devmode is NULL */
+
+ devmode = construct_dev_mode(snum);
+
+ for (i=0; i<*returned; i++)
+ fill_job_info_2(&(info[i]), &queue[i], i, snum, ntprinter,
+ devmode);
+
+ free_a_printer(&ntprinter, 2);
+ SAFE_FREE(queue);
+
+ /* check the required size. */
+ for (i=0; i<*returned; i++)
+ (*needed) += spoolss_size_job_info_2(&info[i]);
+
+ if (*needed > offered) {
+ *returned=0;
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto done;
+ }
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(info);
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto done;
+ }
+
+ /* fill the buffer with the structures */
+ for (i=0; i<*returned; i++)
+ smb_io_job_info_2("", buffer, &info[i], 0);
+
+ result = WERR_OK;
+
+ done:
+ free_a_printer(&ntprinter, 2);
+ free_devmode(devmode);
+ SAFE_FREE(queue);
+ SAFE_FREE(info);
+
+ return result;
+
+}
+
+/****************************************************************************
+ Enumjobs.
+****************************************************************************/
+
+WERROR _spoolss_enumjobs( pipes_struct *p, SPOOL_Q_ENUMJOBS *q_u, SPOOL_R_ENUMJOBS *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 level = q_u->level;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ uint32 *returned = &r_u->returned;
+ WERROR wret;
+
+ int snum;
+ print_status_struct prt_status;
+ print_queue_struct *queue=NULL;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(4,("_spoolss_enumjobs\n"));
+
+ *needed=0;
+ *returned=0;
+
+ if (!get_printer_snum(p, handle, &snum))
+ return WERR_BADFID;
+
+ *returned = print_queue_status(snum, &queue, &prt_status);
+ DEBUGADD(4,("count:[%d], status:[%d], [%s]\n", *returned, prt_status.status, prt_status.message));
+
+ if (*returned == 0) {
+ SAFE_FREE(queue);
+ return WERR_OK;
+ }
+
+ switch (level) {
+ case 1:
+ wret = enumjobs_level1(queue, snum, buffer, offered, needed, returned);
+ return wret;
+ case 2:
+ wret = enumjobs_level2(queue, snum, buffer, offered, needed, returned);
+ return wret;
+ default:
+ SAFE_FREE(queue);
+ *returned=0;
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_schedulejob( pipes_struct *p, SPOOL_Q_SCHEDULEJOB *q_u, SPOOL_R_SCHEDULEJOB *r_u)
+{
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_setjob(pipes_struct *p, SPOOL_Q_SETJOB *q_u, SPOOL_R_SETJOB *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 jobid = q_u->jobid;
+ uint32 command = q_u->command;
+
+ struct current_user user;
+ int snum;
+ WERROR errcode = WERR_BADFUNC;
+
+ if (!get_printer_snum(p, handle, &snum)) {
+ return WERR_BADFID;
+ }
+
+ if (!print_job_exists(snum, jobid)) {
+ return WERR_INVALID_PRINTER_NAME;
+ }
+
+ get_current_user(&user, p);
+
+ switch (command) {
+ case JOB_CONTROL_CANCEL:
+ case JOB_CONTROL_DELETE:
+ if (print_job_delete(&user, snum, jobid, &errcode)) {
+ errcode = WERR_OK;
+ }
+ break;
+ case JOB_CONTROL_PAUSE:
+ if (print_job_pause(&user, snum, jobid, &errcode)) {
+ errcode = WERR_OK;
+ }
+ break;
+ case JOB_CONTROL_RESTART:
+ case JOB_CONTROL_RESUME:
+ if (print_job_resume(&user, snum, jobid, &errcode)) {
+ errcode = WERR_OK;
+ }
+ break;
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return errcode;
+}
+
+/****************************************************************************
+ Enumerates all printer drivers at level 1.
+****************************************************************************/
+
+static WERROR enumprinterdrivers_level1(fstring servername, fstring architecture, NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ int i;
+ int ndrivers;
+ uint32 version;
+ fstring *list = NULL;
+
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+ DRIVER_INFO_1 *tdi1, *driver_info_1=NULL;
+
+ *returned=0;
+
+ for (version=0; version<DRIVER_MAX_VERSION; version++) {
+ list=NULL;
+ ndrivers=get_ntdrivers(&list, architecture, version);
+ DEBUGADD(4,("we have:[%d] drivers in environment [%s] and version [%d]\n", ndrivers, architecture, version));
+
+ if(ndrivers == -1)
+ return WERR_NOMEM;
+
+ if(ndrivers != 0) {
+ if((tdi1=(DRIVER_INFO_1 *)Realloc(driver_info_1, (*returned+ndrivers) * sizeof(DRIVER_INFO_1))) == NULL) {
+ DEBUG(0,("enumprinterdrivers_level1: failed to enlarge driver info buffer!\n"));
+ SAFE_FREE(driver_info_1);
+ SAFE_FREE(list);
+ return WERR_NOMEM;
+ }
+ else driver_info_1 = tdi1;
+ }
+
+ for (i=0; i<ndrivers; i++) {
+ WERROR status;
+ DEBUGADD(5,("\tdriver: [%s]\n", list[i]));
+ ZERO_STRUCT(driver);
+ status = get_a_printer_driver(&driver, 3, list[i],
+ architecture, version);
+ if (!W_ERROR_IS_OK(status)) {
+ SAFE_FREE(list);
+ return status;
+ }
+ fill_printer_driver_info_1(&driver_info_1[*returned+i], driver, servername, architecture );
+ free_a_printer_driver(driver, 3);
+ }
+
+ *returned+=ndrivers;
+ SAFE_FREE(list);
+ }
+
+ /* check the required size. */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding driver [%d]'s size\n",i));
+ *needed += spoolss_size_printer_driver_info_1(&driver_info_1[i]);
+ }
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(driver_info_1);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the driver structures */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding driver [%d] to buffer\n",i));
+ smb_io_printer_driver_info_1("", buffer, &driver_info_1[i], 0);
+ }
+
+ SAFE_FREE(driver_info_1);
+
+ if (*needed > offered) {
+ *returned=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ Enumerates all printer drivers at level 2.
+****************************************************************************/
+
+static WERROR enumprinterdrivers_level2(fstring servername, fstring architecture, NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ int i;
+ int ndrivers;
+ uint32 version;
+ fstring *list = NULL;
+
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+ DRIVER_INFO_2 *tdi2, *driver_info_2=NULL;
+
+ *returned=0;
+
+ for (version=0; version<DRIVER_MAX_VERSION; version++) {
+ list=NULL;
+ ndrivers=get_ntdrivers(&list, architecture, version);
+ DEBUGADD(4,("we have:[%d] drivers in environment [%s] and version [%d]\n", ndrivers, architecture, version));
+
+ if(ndrivers == -1)
+ return WERR_NOMEM;
+
+ if(ndrivers != 0) {
+ if((tdi2=(DRIVER_INFO_2 *)Realloc(driver_info_2, (*returned+ndrivers) * sizeof(DRIVER_INFO_2))) == NULL) {
+ DEBUG(0,("enumprinterdrivers_level2: failed to enlarge driver info buffer!\n"));
+ SAFE_FREE(driver_info_2);
+ SAFE_FREE(list);
+ return WERR_NOMEM;
+ }
+ else driver_info_2 = tdi2;
+ }
+
+ for (i=0; i<ndrivers; i++) {
+ WERROR status;
+
+ DEBUGADD(5,("\tdriver: [%s]\n", list[i]));
+ ZERO_STRUCT(driver);
+ status = get_a_printer_driver(&driver, 3, list[i],
+ architecture, version);
+ if (!W_ERROR_IS_OK(status)) {
+ SAFE_FREE(list);
+ return status;
+ }
+ fill_printer_driver_info_2(&driver_info_2[*returned+i], driver, servername);
+ free_a_printer_driver(driver, 3);
+ }
+
+ *returned+=ndrivers;
+ SAFE_FREE(list);
+ }
+
+ /* check the required size. */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding driver [%d]'s size\n",i));
+ *needed += spoolss_size_printer_driver_info_2(&(driver_info_2[i]));
+ }
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(driver_info_2);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the form structures */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding driver [%d] to buffer\n",i));
+ smb_io_printer_driver_info_2("", buffer, &(driver_info_2[i]), 0);
+ }
+
+ SAFE_FREE(driver_info_2);
+
+ if (*needed > offered) {
+ *returned=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ Enumerates all printer drivers at level 3.
+****************************************************************************/
+
+static WERROR enumprinterdrivers_level3(fstring servername, fstring architecture, NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ int i;
+ int ndrivers;
+ uint32 version;
+ fstring *list = NULL;
+
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+ DRIVER_INFO_3 *tdi3, *driver_info_3=NULL;
+
+ *returned=0;
+
+ for (version=0; version<DRIVER_MAX_VERSION; version++) {
+ list=NULL;
+ ndrivers=get_ntdrivers(&list, architecture, version);
+ DEBUGADD(4,("we have:[%d] drivers in environment [%s] and version [%d]\n", ndrivers, architecture, version));
+
+ if(ndrivers == -1)
+ return WERR_NOMEM;
+
+ if(ndrivers != 0) {
+ if((tdi3=(DRIVER_INFO_3 *)Realloc(driver_info_3, (*returned+ndrivers) * sizeof(DRIVER_INFO_3))) == NULL) {
+ DEBUG(0,("enumprinterdrivers_level3: failed to enlarge driver info buffer!\n"));
+ SAFE_FREE(driver_info_3);
+ SAFE_FREE(list);
+ return WERR_NOMEM;
+ }
+ else driver_info_3 = tdi3;
+ }
+
+ for (i=0; i<ndrivers; i++) {
+ WERROR status;
+
+ DEBUGADD(5,("\tdriver: [%s]\n", list[i]));
+ ZERO_STRUCT(driver);
+ status = get_a_printer_driver(&driver, 3, list[i],
+ architecture, version);
+ if (!W_ERROR_IS_OK(status)) {
+ SAFE_FREE(list);
+ return status;
+ }
+ fill_printer_driver_info_3(&driver_info_3[*returned+i], driver, servername);
+ free_a_printer_driver(driver, 3);
+ }
+
+ *returned+=ndrivers;
+ SAFE_FREE(list);
+ }
+
+ /* check the required size. */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding driver [%d]'s size\n",i));
+ *needed += spoolss_size_printer_driver_info_3(&driver_info_3[i]);
+ }
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(driver_info_3);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the driver structures */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding driver [%d] to buffer\n",i));
+ smb_io_printer_driver_info_3("", buffer, &driver_info_3[i], 0);
+ }
+
+ for (i=0; i<*returned; i++)
+ SAFE_FREE(driver_info_3[i].dependentfiles);
+
+ SAFE_FREE(driver_info_3);
+
+ if (*needed > offered) {
+ *returned=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ Enumerates all printer drivers.
+****************************************************************************/
+
+WERROR _spoolss_enumprinterdrivers( pipes_struct *p, SPOOL_Q_ENUMPRINTERDRIVERS *q_u, SPOOL_R_ENUMPRINTERDRIVERS *r_u)
+{
+ UNISTR2 *environment = &q_u->environment;
+ uint32 level = q_u->level;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ uint32 *returned = &r_u->returned;
+
+ fstring *list = NULL;
+ fstring servername;
+ fstring architecture;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(4,("_spoolss_enumprinterdrivers\n"));
+ fstrcpy(servername, get_called_name());
+ *needed=0;
+ *returned=0;
+
+ unistr2_to_ascii(architecture, environment, sizeof(architecture)-1);
+
+ switch (level) {
+ case 1:
+ return enumprinterdrivers_level1(servername, architecture, buffer, offered, needed, returned);
+ case 2:
+ return enumprinterdrivers_level2(servername, architecture, buffer, offered, needed, returned);
+ case 3:
+ return enumprinterdrivers_level3(servername, architecture, buffer, offered, needed, returned);
+ default:
+ *returned=0;
+ SAFE_FREE(list);
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void fill_form_1(FORM_1 *form, nt_forms_struct *list)
+{
+ form->flag=list->flag;
+ init_unistr(&form->name, list->name);
+ form->width=list->width;
+ form->length=list->length;
+ form->left=list->left;
+ form->top=list->top;
+ form->right=list->right;
+ form->bottom=list->bottom;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_enumforms(pipes_struct *p, SPOOL_Q_ENUMFORMS *q_u, SPOOL_R_ENUMFORMS *r_u)
+{
+ uint32 level = q_u->level;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ uint32 *numofforms = &r_u->numofforms;
+ uint32 numbuiltinforms;
+
+ nt_forms_struct *list=NULL;
+ nt_forms_struct *builtinlist=NULL;
+ FORM_1 *forms_1;
+ int buffer_size=0;
+ int i;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(4,("_spoolss_enumforms\n"));
+ DEBUGADD(5,("Offered buffer size [%d]\n", offered));
+ DEBUGADD(5,("Info level [%d]\n", level));
+
+ numbuiltinforms = get_builtin_ntforms(&builtinlist);
+ DEBUGADD(5,("Number of builtin forms [%d]\n", numbuiltinforms));
+ *numofforms = get_ntforms(&list);
+ DEBUGADD(5,("Number of user forms [%d]\n", *numofforms));
+ *numofforms += numbuiltinforms;
+
+ if (*numofforms == 0) return WERR_NO_MORE_ITEMS;
+
+ switch (level) {
+ case 1:
+ if ((forms_1=(FORM_1 *)malloc(*numofforms * sizeof(FORM_1))) == NULL) {
+ *numofforms=0;
+ return WERR_NOMEM;
+ }
+
+ /* construct the list of form structures */
+ for (i=0; i<numbuiltinforms; i++) {
+ DEBUGADD(6,("Filling form number [%d]\n",i));
+ fill_form_1(&forms_1[i], &builtinlist[i]);
+ }
+
+ SAFE_FREE(builtinlist);
+
+ for (; i<*numofforms; i++) {
+ DEBUGADD(6,("Filling form number [%d]\n",i));
+ fill_form_1(&forms_1[i], &list[i-numbuiltinforms]);
+ }
+
+ SAFE_FREE(list);
+
+ /* check the required size. */
+ for (i=0; i<numbuiltinforms; i++) {
+ DEBUGADD(6,("adding form [%d]'s size\n",i));
+ buffer_size += spoolss_size_form_1(&forms_1[i]);
+ }
+ for (; i<*numofforms; i++) {
+ DEBUGADD(6,("adding form [%d]'s size\n",i));
+ buffer_size += spoolss_size_form_1(&forms_1[i]);
+ }
+
+ *needed=buffer_size;
+
+ if (!alloc_buffer_size(buffer, buffer_size)){
+ SAFE_FREE(forms_1);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the form structures */
+ for (i=0; i<numbuiltinforms; i++) {
+ DEBUGADD(6,("adding form [%d] to buffer\n",i));
+ smb_io_form_1("", buffer, &forms_1[i], 0);
+ }
+ for (; i<*numofforms; i++) {
+ DEBUGADD(6,("adding form [%d] to buffer\n",i));
+ smb_io_form_1("", buffer, &forms_1[i], 0);
+ }
+
+ SAFE_FREE(forms_1);
+
+ if (*needed > offered) {
+ *numofforms=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+ else
+ return WERR_OK;
+
+ default:
+ SAFE_FREE(list);
+ SAFE_FREE(builtinlist);
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_getform(pipes_struct *p, SPOOL_Q_GETFORM *q_u, SPOOL_R_GETFORM *r_u)
+{
+ uint32 level = q_u->level;
+ UNISTR2 *uni_formname = &q_u->formname;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+
+ nt_forms_struct *list=NULL;
+ nt_forms_struct builtin_form;
+ BOOL foundBuiltin;
+ FORM_1 form_1;
+ fstring form_name;
+ int buffer_size=0;
+ int numofforms=0, i=0;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ unistr2_to_ascii(form_name, uni_formname, sizeof(form_name)-1);
+
+ DEBUG(4,("_spoolss_getform\n"));
+ DEBUGADD(5,("Offered buffer size [%d]\n", offered));
+ DEBUGADD(5,("Info level [%d]\n", level));
+
+ foundBuiltin = get_a_builtin_ntform(uni_formname,&builtin_form);
+ if (!foundBuiltin) {
+ numofforms = get_ntforms(&list);
+ DEBUGADD(5,("Number of forms [%d]\n", numofforms));
+
+ if (numofforms == 0)
+ return WERR_BADFID;
+ }
+
+ switch (level) {
+ case 1:
+ if (foundBuiltin) {
+ fill_form_1(&form_1, &builtin_form);
+ } else {
+
+ /* Check if the requested name is in the list of form structures */
+ for (i=0; i<numofforms; i++) {
+
+ DEBUG(4,("_spoolss_getform: checking form %s (want %s)\n", list[i].name, form_name));
+
+ if (strequal(form_name, list[i].name)) {
+ DEBUGADD(6,("Found form %s number [%d]\n", form_name, i));
+ fill_form_1(&form_1, &list[i]);
+ break;
+ }
+ }
+
+ SAFE_FREE(list);
+ if (i == numofforms) {
+ return WERR_BADFID;
+ }
+ }
+ /* check the required size. */
+
+ *needed=spoolss_size_form_1(&form_1);
+
+ if (!alloc_buffer_size(buffer, buffer_size)){
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ if (*needed > offered) {
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the form structures */
+ DEBUGADD(6,("adding form %s [%d] to buffer\n", form_name, i));
+ smb_io_form_1("", buffer, &form_1, 0);
+
+ return WERR_OK;
+
+ default:
+ SAFE_FREE(list);
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void fill_port_1(PORT_INFO_1 *port, const char *name)
+{
+ init_unistr(&port->port_name, name);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void fill_port_2(PORT_INFO_2 *port, const char *name)
+{
+ init_unistr(&port->port_name, name);
+ init_unistr(&port->monitor_name, "Local Monitor");
+ init_unistr(&port->description, "Local Port");
+ port->port_type=PORT_TYPE_WRITE;
+ port->reserved=0x0;
+}
+
+/****************************************************************************
+ enumports level 1.
+****************************************************************************/
+
+static WERROR enumports_level_1(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ PORT_INFO_1 *ports=NULL;
+ int i=0;
+
+ if (*lp_enumports_cmd()) {
+ char *cmd = lp_enumports_cmd();
+ char **qlines;
+ pstring command;
+ int numlines;
+ int ret;
+ int fd;
+
+ slprintf(command, sizeof(command)-1, "%s \"%d\"", cmd, 1);
+
+ DEBUG(10,("Running [%s]\n", command));
+ ret = smbrun(command, &fd);
+ DEBUG(10,("Returned [%d]\n", ret));
+ if (ret != 0) {
+ if (fd != -1)
+ close(fd);
+ /* Is this the best error to return here? */
+ return WERR_ACCESS_DENIED;
+ }
+
+ numlines = 0;
+ qlines = fd_lines_load(fd, &numlines);
+ DEBUGADD(10,("Lines returned = [%d]\n", numlines));
+ close(fd);
+
+ if(numlines) {
+ if((ports=(PORT_INFO_1 *)malloc( numlines * sizeof(PORT_INFO_1) )) == NULL) {
+ DEBUG(10,("Returning WERR_NOMEM [%s]\n",
+ dos_errstr(WERR_NOMEM)));
+ file_lines_free(qlines);
+ return WERR_NOMEM;
+ }
+
+ for (i=0; i<numlines; i++) {
+ DEBUG(6,("Filling port number [%d] with port [%s]\n", i, qlines[i]));
+ fill_port_1(&ports[i], qlines[i]);
+ }
+
+ file_lines_free(qlines);
+ }
+
+ *returned = numlines;
+
+ } else {
+ *returned = 1; /* Sole Samba port returned. */
+
+ if((ports=(PORT_INFO_1 *)malloc( sizeof(PORT_INFO_1) )) == NULL)
+ return WERR_NOMEM;
+
+ DEBUG(10,("enumports_level_1: port name %s\n", SAMBA_PRINTER_PORT_NAME));
+
+ fill_port_1(&ports[0], SAMBA_PRINTER_PORT_NAME);
+ }
+
+ /* check the required size. */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding port [%d]'s size\n", i));
+ *needed += spoolss_size_port_info_1(&ports[i]);
+ }
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(ports);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the ports structures */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding port [%d] to buffer\n", i));
+ smb_io_port_1("", buffer, &ports[i], 0);
+ }
+
+ SAFE_FREE(ports);
+
+ if (*needed > offered) {
+ *returned=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ enumports level 2.
+****************************************************************************/
+
+static WERROR enumports_level_2(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ PORT_INFO_2 *ports=NULL;
+ int i=0;
+
+ if (*lp_enumports_cmd()) {
+ char *cmd = lp_enumports_cmd();
+ char *path;
+ char **qlines;
+ pstring tmp_file;
+ pstring command;
+ int numlines;
+ int ret;
+ int fd;
+
+ if (*lp_pathname(lp_servicenumber(PRINTERS_NAME)))
+ path = lp_pathname(lp_servicenumber(PRINTERS_NAME));
+ else
+ path = lp_lockdir();
+
+ slprintf(tmp_file, sizeof(tmp_file)-1, "%s/smbcmd.%u.", path, (unsigned int)sys_getpid());
+ slprintf(command, sizeof(command)-1, "%s \"%d\"", cmd, 2);
+
+ unlink(tmp_file);
+ DEBUG(10,("Running [%s > %s]\n", command,tmp_file));
+ ret = smbrun(command, &fd);
+ DEBUGADD(10,("returned [%d]\n", ret));
+ if (ret != 0) {
+ if (fd != -1)
+ close(fd);
+ /* Is this the best error to return here? */
+ return WERR_ACCESS_DENIED;
+ }
+
+ numlines = 0;
+ qlines = fd_lines_load(fd, &numlines);
+ DEBUGADD(10,("Lines returned = [%d]\n", numlines));
+ close(fd);
+
+ if(numlines) {
+ if((ports=(PORT_INFO_2 *)malloc( numlines * sizeof(PORT_INFO_2) )) == NULL) {
+ file_lines_free(qlines);
+ return WERR_NOMEM;
+ }
+
+ for (i=0; i<numlines; i++) {
+ DEBUG(6,("Filling port number [%d] with port [%s]\n", i, qlines[i]));
+ fill_port_2(&(ports[i]), qlines[i]);
+ }
+
+ file_lines_free(qlines);
+ }
+
+ *returned = numlines;
+
+ } else {
+
+ *returned = 1;
+
+ if((ports=(PORT_INFO_2 *)malloc( sizeof(PORT_INFO_2) )) == NULL)
+ return WERR_NOMEM;
+
+ DEBUG(10,("enumports_level_2: port name %s\n", SAMBA_PRINTER_PORT_NAME));
+
+ fill_port_2(&ports[0], SAMBA_PRINTER_PORT_NAME);
+ }
+
+ /* check the required size. */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding port [%d]'s size\n", i));
+ *needed += spoolss_size_port_info_2(&ports[i]);
+ }
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(ports);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the ports structures */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding port [%d] to buffer\n", i));
+ smb_io_port_2("", buffer, &ports[i], 0);
+ }
+
+ SAFE_FREE(ports);
+
+ if (*needed > offered) {
+ *returned=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ enumports.
+****************************************************************************/
+
+WERROR _spoolss_enumports( pipes_struct *p, SPOOL_Q_ENUMPORTS *q_u, SPOOL_R_ENUMPORTS *r_u)
+{
+ uint32 level = q_u->level;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ uint32 *returned = &r_u->returned;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(4,("_spoolss_enumports\n"));
+
+ *returned=0;
+ *needed=0;
+
+ switch (level) {
+ case 1:
+ return enumports_level_1(buffer, offered, needed, returned);
+ case 2:
+ return enumports_level_2(buffer, offered, needed, returned);
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR spoolss_addprinterex_level_2( pipes_struct *p, const UNISTR2 *uni_srv_name,
+ const SPOOL_PRINTER_INFO_LEVEL *info,
+ DEVICEMODE *devmode, SEC_DESC_BUF *sec_desc_buf,
+ uint32 user_switch, const SPOOL_USER_CTR *user,
+ POLICY_HND *handle)
+{
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ fstring name;
+ int snum;
+ WERROR err = WERR_OK;
+
+ if ((printer = (NT_PRINTER_INFO_LEVEL *)malloc(sizeof(NT_PRINTER_INFO_LEVEL))) == NULL) {
+ DEBUG(0,("spoolss_addprinterex_level_2: malloc fail.\n"));
+ return WERR_NOMEM;
+ }
+
+ ZERO_STRUCTP(printer);
+
+ /* convert from UNICODE to ASCII - this allocates the info_2 struct inside *printer.*/
+ if (!convert_printer_info(info, printer, 2)) {
+ free_a_printer(&printer, 2);
+ return WERR_NOMEM;
+ }
+
+ /* check to see if the printer already exists */
+
+ if ((snum = print_queue_snum(printer->info_2->sharename)) != -1) {
+ DEBUG(5, ("_spoolss_addprinterex: Attempted to add a printer named [%s] when one already existed!\n",
+ printer->info_2->sharename));
+ free_a_printer(&printer, 2);
+ return WERR_PRINTER_ALREADY_EXISTS;
+ }
+
+ /* FIXME!!! smbd should check to see if the driver is installed before
+ trying to add a printer like this --jerry */
+
+ if (*lp_addprinter_cmd() ) {
+ if ( !add_printer_hook(printer) ) {
+ free_a_printer(&printer,2);
+ return WERR_ACCESS_DENIED;
+ }
+ }
+
+ slprintf(name, sizeof(name)-1, "\\\\%s\\%s", get_called_name(),
+ printer->info_2->sharename);
+
+
+ if ((snum = print_queue_snum(printer->info_2->sharename)) == -1) {
+ free_a_printer(&printer,2);
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* you must be a printer admin to add a new printer */
+ if (!print_access_check(NULL, snum, PRINTER_ACCESS_ADMINISTER)) {
+ free_a_printer(&printer,2);
+ return WERR_ACCESS_DENIED;
+ }
+
+ /*
+ * Do sanity check on the requested changes for Samba.
+ */
+
+ if (!check_printer_ok(printer->info_2, snum)) {
+ free_a_printer(&printer,2);
+ return WERR_INVALID_PARAM;
+ }
+
+ /*
+ * When a printer is created, the drivername bound to the printer is used
+ * to lookup previously saved driver initialization info, which is then
+ * bound to the new printer, simulating what happens in the Windows arch.
+ */
+
+ if (!devmode)
+ {
+ set_driver_init(printer, 2);
+ }
+ else
+ {
+ /* A valid devmode was included, convert and link it
+ */
+ DEBUGADD(10, ("spoolss_addprinterex_level_2: devmode included, converting\n"));
+
+ if (!convert_devicemode(printer->info_2->printername, devmode,
+ &printer->info_2->devmode))
+ return WERR_NOMEM;
+ }
+
+ /* write the ASCII on disk */
+ err = mod_a_printer(*printer, 2);
+ if (!W_ERROR_IS_OK(err)) {
+ free_a_printer(&printer,2);
+ return err;
+ }
+
+ if (!open_printer_hnd(p, handle, name, PRINTER_ACCESS_ADMINISTER)) {
+ /* Handle open failed - remove addition. */
+ del_a_printer(printer->info_2->sharename);
+ free_a_printer(&printer,2);
+ return WERR_ACCESS_DENIED;
+ }
+
+ update_c_setprinter(False);
+ free_a_printer(&printer,2);
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_addprinterex( pipes_struct *p, SPOOL_Q_ADDPRINTEREX *q_u, SPOOL_R_ADDPRINTEREX *r_u)
+{
+ UNISTR2 *uni_srv_name = &q_u->server_name;
+ uint32 level = q_u->level;
+ SPOOL_PRINTER_INFO_LEVEL *info = &q_u->info;
+ DEVICEMODE *devmode = q_u->devmode_ctr.devmode;
+ SEC_DESC_BUF *sdb = q_u->secdesc_ctr;
+ uint32 user_switch = q_u->user_switch;
+ SPOOL_USER_CTR *user = &q_u->user_ctr;
+ POLICY_HND *handle = &r_u->handle;
+
+ switch (level) {
+ case 1:
+ /* we don't handle yet */
+ /* but I know what to do ... */
+ return WERR_UNKNOWN_LEVEL;
+ case 2:
+ return spoolss_addprinterex_level_2(p, uni_srv_name, info,
+ devmode, sdb,
+ user_switch, user, handle);
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_addprinterdriver(pipes_struct *p, SPOOL_Q_ADDPRINTERDRIVER *q_u, SPOOL_R_ADDPRINTERDRIVER *r_u)
+{
+ uint32 level = q_u->level;
+ SPOOL_PRINTER_DRIVER_INFO_LEVEL *info = &q_u->info;
+ WERROR err = WERR_OK;
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+ struct current_user user;
+ fstring driver_name;
+ uint32 version;
+
+ ZERO_STRUCT(driver);
+
+ get_current_user(&user, p);
+
+ if (!convert_printer_driver_info(info, &driver, level)) {
+ err = WERR_NOMEM;
+ goto done;
+ }
+
+ DEBUG(5,("Cleaning driver's information\n"));
+ err = clean_up_driver_struct(driver, level, &user);
+ if (!W_ERROR_IS_OK(err))
+ goto done;
+
+ DEBUG(5,("Moving driver to final destination\n"));
+ if(!move_driver_to_download_area(driver, level, &user, &err)) {
+ if (W_ERROR_IS_OK(err))
+ err = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ if (add_a_printer_driver(driver, level)!=0) {
+ err = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ /* BEGIN_ADMIN_LOG */
+ switch(level) {
+ case 3:
+ fstrcpy(driver_name, driver.info_3->name ? driver.info_3->name : "");
+ sys_adminlog(LOG_INFO,"Added printer driver. Print driver name: %s. Print driver OS: %s. Administrator name: %s.",
+ driver_name, get_drv_ver_to_os(driver.info_3->cversion),uidtoname(user.uid));
+ break;
+ case 6:
+ fstrcpy(driver_name, driver.info_6->name ? driver.info_6->name : "");
+ sys_adminlog(LOG_INFO,"Added printer driver. Print driver name: %s. Print driver OS: %s. Administrator name: %s.",
+ driver_name, get_drv_ver_to_os(driver.info_6->version),uidtoname(user.uid));
+ break;
+ }
+ /* END_ADMIN_LOG */
+
+ /*
+ * I think this is where he DrvUpgradePrinter() hook would be
+ * be called in a driver's interface DLL on a Windows NT 4.0/2k
+ * server. Right now, we just need to send ourselves a message
+ * to update each printer bound to this driver. --jerry
+ */
+
+ if (!srv_spoolss_drv_upgrade_printer(driver_name)) {
+ DEBUG(0,("_spoolss_addprinterdriver: Failed to send message about upgrading driver [%s]!\n",
+ driver_name));
+ }
+
+ /*
+ * Based on the version (e.g. driver destination dir: 0=9x,2=Nt/2k,3=2k/Xp),
+ * decide if the driver init data should be deleted. The rules are:
+ * 1) never delete init data if it is a 9x driver, they don't use it anyway
+ * 2) delete init data only if there is no 2k/Xp driver
+ * 3) always delete init data
+ * The generalized rule is always use init data from the highest order driver.
+ * It is necessary to follow the driver install by an initialization step to
+ * finish off this process.
+ */
+ if (level == 3)
+ version = driver.info_3->cversion;
+ else if (level == 6)
+ version = driver.info_6->version;
+ else
+ version = -1;
+ switch (version) {
+ /*
+ * 9x printer driver - never delete init data
+ */
+ case 0:
+ DEBUG(10,("_spoolss_addprinterdriver: init data not deleted for 9x driver [%s]\n",
+ driver_name));
+ break;
+
+ /*
+ * Nt or 2k (compatiblity mode) printer driver - only delete init data if
+ * there is no 2k/Xp driver init data for this driver name.
+ */
+ case 2:
+ {
+ NT_PRINTER_DRIVER_INFO_LEVEL driver1;
+
+ if (!W_ERROR_IS_OK(get_a_printer_driver(&driver1, 3, driver_name, "Windows NT x86", 3))) {
+ /*
+ * No 2k/Xp driver found, delete init data (if any) for the new Nt driver.
+ */
+ if (!del_driver_init(driver_name))
+ DEBUG(6,("_spoolss_addprinterdriver: del_driver_init(%s) Nt failed!\n", driver_name));
+ } else {
+ /*
+ * a 2k/Xp driver was found, don't delete init data because Nt driver will use it.
+ */
+ free_a_printer_driver(driver1,3);
+ DEBUG(10,("_spoolss_addprinterdriver: init data not deleted for Nt driver [%s]\n",
+ driver_name));
+ }
+ }
+ break;
+
+ /*
+ * 2k or Xp printer driver - always delete init data
+ */
+ case 3:
+ if (!del_driver_init(driver_name))
+ DEBUG(6,("_spoolss_addprinterdriver: del_driver_init(%s) 2k/Xp failed!\n", driver_name));
+ break;
+
+ default:
+ DEBUG(0,("_spoolss_addprinterdriver: invalid level=%d\n", level));
+ break;
+ }
+
+
+done:
+ free_a_printer_driver(driver, level);
+ return err;
+}
+
+/********************************************************************
+ * spoolss_addprinterdriverex
+ ********************************************************************/
+
+WERROR _spoolss_addprinterdriverex(pipes_struct *p, SPOOL_Q_ADDPRINTERDRIVEREX *q_u, SPOOL_R_ADDPRINTERDRIVEREX *r_u)
+{
+ SPOOL_Q_ADDPRINTERDRIVER q_u_local;
+ SPOOL_R_ADDPRINTERDRIVER r_u_local;
+
+ /*
+ * we only support the semantics of AddPrinterDriver()
+ * i.e. only copy files that are newer than existing ones
+ */
+
+ if ( q_u->copy_flags != APD_COPY_NEW_FILES )
+ return WERR_ACCESS_DENIED;
+
+ ZERO_STRUCT(q_u_local);
+ ZERO_STRUCT(r_u_local);
+
+ /* just pass the information off to _spoolss_addprinterdriver() */
+ q_u_local.server_name_ptr = q_u->server_name_ptr;
+ copy_unistr2(&q_u_local.server_name, &q_u->server_name);
+ q_u_local.level = q_u->level;
+ memcpy( &q_u_local.info, &q_u->info, sizeof(SPOOL_PRINTER_DRIVER_INFO_LEVEL) );
+
+ return _spoolss_addprinterdriver( p, &q_u_local, &r_u_local );
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void fill_driverdir_1(DRIVER_DIRECTORY_1 *info, char *name)
+{
+ init_unistr(&info->name, name);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinterdriverdir_level_1(UNISTR2 *name, UNISTR2 *uni_environment, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ pstring path;
+ pstring long_archi;
+ const char *short_archi;
+ DRIVER_DIRECTORY_1 *info=NULL;
+
+ unistr2_to_ascii(long_archi, uni_environment, sizeof(long_archi)-1);
+
+ if (!(short_archi = get_short_archi(long_archi)))
+ return WERR_INVALID_ENVIRONMENT;
+
+ if((info=(DRIVER_DIRECTORY_1 *)malloc(sizeof(DRIVER_DIRECTORY_1))) == NULL)
+ return WERR_NOMEM;
+
+ slprintf(path, sizeof(path)-1, "\\\\%s\\print$\\%s", get_called_name(), short_archi);
+
+ DEBUG(4,("printer driver directory: [%s]\n", path));
+
+ fill_driverdir_1(info, path);
+
+ *needed += spoolss_size_driverdir_info_1(info);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(info);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ smb_io_driverdir_1("", buffer, info, 0);
+
+ SAFE_FREE(info);
+
+ if (*needed > offered)
+ return WERR_INSUFFICIENT_BUFFER;
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_getprinterdriverdirectory(pipes_struct *p, SPOOL_Q_GETPRINTERDRIVERDIR *q_u, SPOOL_R_GETPRINTERDRIVERDIR *r_u)
+{
+ UNISTR2 *name = &q_u->name;
+ UNISTR2 *uni_environment = &q_u->environment;
+ uint32 level = q_u->level;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(4,("_spoolss_getprinterdriverdirectory\n"));
+
+ *needed=0;
+
+ switch(level) {
+ case 1:
+ return getprinterdriverdir_level_1(name, uni_environment, buffer, offered, needed);
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_enumprinterdata(pipes_struct *p, SPOOL_Q_ENUMPRINTERDATA *q_u, SPOOL_R_ENUMPRINTERDATA *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 idx = q_u->index;
+ uint32 in_value_len = q_u->valuesize;
+ uint32 in_data_len = q_u->datasize;
+ uint32 *out_max_value_len = &r_u->valuesize;
+ uint16 **out_value = &r_u->value;
+ uint32 *out_value_len = &r_u->realvaluesize;
+ uint32 *out_type = &r_u->type;
+ uint32 *out_max_data_len = &r_u->datasize;
+ uint8 **data_out = &r_u->data;
+ uint32 *out_data_len = &r_u->realdatasize;
+
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ uint32 biggest_valuesize;
+ uint32 biggest_datasize;
+ uint32 data_len;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+ int snum;
+ WERROR result;
+ REGISTRY_VALUE *val = NULL;
+ NT_PRINTER_DATA *p_data;
+ int i, key_index, num_values;
+ int name_length;
+
+ ZERO_STRUCT( printer );
+
+ *out_type = 0;
+
+ *out_max_data_len = 0;
+ *data_out = NULL;
+ *out_data_len = 0;
+
+ DEBUG(5,("spoolss_enumprinterdata\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_enumprinterdata: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if (!get_printer_snum(p,handle, &snum))
+ return WERR_BADFID;
+
+ result = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(result))
+ return result;
+
+ p_data = &printer->info_2->data;
+ key_index = lookup_printerkey( p_data, SPOOL_PRINTERDATA_KEY );
+
+ result = WERR_OK;
+
+ /*
+ * The NT machine wants to know the biggest size of value and data
+ *
+ * cf: MSDN EnumPrinterData remark section
+ */
+
+ if ( !in_value_len && !in_data_len && (key_index != -1) )
+ {
+ DEBUGADD(6,("Activating NT mega-hack to find sizes\n"));
+
+ biggest_valuesize = 0;
+ biggest_datasize = 0;
+
+ num_values = regval_ctr_numvals( &p_data->keys[key_index].values );
+
+ for ( i=0; i<num_values; i++ )
+ {
+ val = regval_ctr_specific_value( &p_data->keys[key_index].values, i );
+
+ name_length = strlen(val->valuename);
+ if ( strlen(val->valuename) > biggest_valuesize )
+ biggest_valuesize = name_length;
+
+ if ( val->size > biggest_datasize )
+ biggest_datasize = val->size;
+
+ DEBUG(6,("current values: [%d], [%d]\n", biggest_valuesize,
+ biggest_datasize));
+ }
+
+ /* the value is an UNICODE string but real_value_size is the length
+ in bytes including the trailing 0 */
+
+ *out_value_len = 2 * (1+biggest_valuesize);
+ *out_data_len = biggest_datasize;
+
+ DEBUG(6,("final values: [%d], [%d]\n", *out_value_len, *out_data_len));
+
+ goto done;
+ }
+
+ /*
+ * the value len is wrong in NT sp3
+ * that's the number of bytes not the number of unicode chars
+ */
+
+ if ( key_index != -1 )
+ val = regval_ctr_specific_value( &p_data->keys[key_index].values, idx );
+
+ if ( !val )
+ {
+
+ /* out_value should default to "" or else NT4 has
+ problems unmarshalling the response */
+
+ *out_max_value_len=(in_value_len/sizeof(uint16));
+
+ if((*out_value=(uint16 *)talloc_zero(p->mem_ctx, in_value_len*sizeof(uint8))) == NULL)
+ {
+ result = WERR_NOMEM;
+ goto done;
+ }
+
+ *out_value_len = (uint32)rpcstr_push((char *)*out_value, "", in_value_len, 0);
+
+ /* the data is counted in bytes */
+
+ *out_max_data_len = in_data_len;
+ *out_data_len = in_data_len;
+
+ /* only allocate when given a non-zero data_len */
+
+ if ( in_data_len && ((*data_out=(uint8 *)talloc_zero(p->mem_ctx, in_data_len*sizeof(uint8))) == NULL) )
+ {
+ result = WERR_NOMEM;
+ goto done;
+ }
+
+ result = WERR_NO_MORE_ITEMS;
+ }
+ else
+ {
+ /*
+ * the value is:
+ * - counted in bytes in the request
+ * - counted in UNICODE chars in the max reply
+ * - counted in bytes in the real size
+ *
+ * take a pause *before* coding not *during* coding
+ */
+
+ /* name */
+ *out_max_value_len=(in_value_len/sizeof(uint16));
+ if ( (*out_value = (uint16 *)talloc_zero(p->mem_ctx, in_value_len*sizeof(uint8))) == NULL )
+ {
+ result = WERR_NOMEM;
+ goto done;
+ }
+
+ *out_value_len = (uint32)rpcstr_push((char *)*out_value, regval_name(val), in_value_len, 0);
+
+ /* type */
+
+ *out_type = regval_type( val );
+
+ /* data - counted in bytes */
+
+ *out_max_data_len = in_data_len;
+ if ( (*data_out = (uint8 *)talloc_zero(p->mem_ctx, in_data_len*sizeof(uint8))) == NULL)
+ {
+ result = WERR_NOMEM;
+ goto done;
+ }
+ data_len = (size_t)regval_size(val);
+ memcpy( *data_out, regval_data_p(val), data_len );
+ *out_data_len = data_len;
+ }
+
+done:
+ free_a_printer(&printer, 2);
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_setprinterdata( pipes_struct *p, SPOOL_Q_SETPRINTERDATA *q_u, SPOOL_R_SETPRINTERDATA *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ UNISTR2 *value = &q_u->value;
+ uint32 type = q_u->type;
+ uint8 *data = q_u->data;
+ uint32 real_len = q_u->real_len;
+
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ int snum=0;
+ WERROR status = WERR_OK;
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+ fstring valuename;
+
+ DEBUG(5,("spoolss_setprinterdata\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_setprinterdata: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER ) {
+ DEBUG(10,("_spoolss_setprinterdata: Not implemented for server handles yet\n"));
+ return WERR_INVALID_PARAM;
+ }
+
+ if (!get_printer_snum(p,handle, &snum))
+ return WERR_BADFID;
+
+ /*
+ * Access check : NT returns "access denied" if you make a
+ * SetPrinterData call without the necessary privildge.
+ * we were originally returning OK if nothing changed
+ * which made Win2k issue **a lot** of SetPrinterData
+ * when connecting to a printer --jerry
+ */
+
+ if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER)
+ {
+ DEBUG(3, ("_spoolss_setprinterdata: change denied by handle access permissions\n"));
+ status = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(status))
+ return status;
+
+ unistr2_to_ascii( valuename, value, sizeof(valuename)-1 );
+
+ /*
+ * When client side code sets a magic printer data key, detect it and save
+ * the current printer data and the magic key's data (its the DEVMODE) for
+ * future printer/driver initializations.
+ */
+ if ( (type == REG_BINARY) && strequal( valuename, PHANTOM_DEVMODE_KEY))
+ {
+ /* Set devmode and printer initialization info */
+ status = save_driver_init( printer, 2, data, real_len );
+
+ srv_spoolss_reset_printerdata( printer->info_2->drivername );
+ }
+ else
+ {
+ status = set_printer_dataex( printer, SPOOL_PRINTERDATA_KEY, valuename,
+ type, data, real_len );
+ if ( W_ERROR_IS_OK(status) )
+ status = mod_a_printer(*printer, 2);
+ }
+
+done:
+ free_a_printer(&printer, 2);
+
+ return status;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_resetprinter(pipes_struct *p, SPOOL_Q_RESETPRINTER *q_u, SPOOL_R_RESETPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+ int snum;
+
+ DEBUG(5,("_spoolss_resetprinter\n"));
+
+ /*
+ * All we do is to check to see if the handle and queue is valid.
+ * This call really doesn't mean anything to us because we only
+ * support RAW printing. --jerry
+ */
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_resetprinter: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if (!get_printer_snum(p,handle, &snum))
+ return WERR_BADFID;
+
+
+ /* blindly return success */
+ return WERR_OK;
+}
+
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_deleteprinterdata(pipes_struct *p, SPOOL_Q_DELETEPRINTERDATA *q_u, SPOOL_R_DELETEPRINTERDATA *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ UNISTR2 *value = &q_u->valuename;
+
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ int snum=0;
+ WERROR status = WERR_OK;
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+ pstring valuename;
+
+ DEBUG(5,("spoolss_deleteprinterdata\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_deleteprinterdata: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if (!get_printer_snum(p, handle, &snum))
+ return WERR_BADFID;
+
+ if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) {
+ DEBUG(3, ("_spoolss_deleteprinterdata: printer properties change denied by handle\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(status))
+ return status;
+
+ unistr2_to_ascii( valuename, value, sizeof(valuename)-1 );
+
+ status = delete_printer_dataex( printer, SPOOL_PRINTERDATA_KEY, valuename );
+
+ if ( W_ERROR_IS_OK(status) )
+ mod_a_printer( *printer, 2 );
+
+ free_a_printer(&printer, 2);
+
+ return status;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_addform( pipes_struct *p, SPOOL_Q_ADDFORM *q_u, SPOOL_R_ADDFORM *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ FORM *form = &q_u->form;
+ nt_forms_struct tmpForm;
+ int snum;
+ WERROR status = WERR_OK;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ int count=0;
+ nt_forms_struct *list=NULL;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ DEBUG(5,("spoolss_addform\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_addform: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+
+ /* forms can be added on printer of on the print server handle */
+
+ if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTER )
+ {
+ if (!get_printer_snum(p,handle, &snum))
+ return WERR_BADFID;
+
+ status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(status))
+ goto done;
+ }
+
+ if ( !(Printer->access_granted & (PRINTER_ACCESS_ADMINISTER|SERVER_ACCESS_ADMINISTER)) ) {
+ DEBUG(2,("_spoolss_addform: denied by handle permissions.\n"));
+ status = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ /* can't add if builtin */
+
+ if (get_a_builtin_ntform(&form->name,&tmpForm)) {
+ status = WERR_ALREADY_EXISTS;
+ goto done;
+ }
+
+ count = get_ntforms(&list);
+
+ if(!add_a_form(&list, form, &count)) {
+ status = WERR_NOMEM;
+ goto done;
+ }
+
+ write_ntforms(&list, count);
+
+ /*
+ * ChangeID must always be set if this is a printer
+ */
+
+ if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTER )
+ status = mod_a_printer(*printer, 2);
+
+done:
+ if ( printer )
+ free_a_printer(&printer, 2);
+ SAFE_FREE(list);
+
+ return status;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_deleteform( pipes_struct *p, SPOOL_Q_DELETEFORM *q_u, SPOOL_R_DELETEFORM *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ UNISTR2 *form_name = &q_u->name;
+ nt_forms_struct tmpForm;
+ int count=0;
+ nt_forms_struct *list=NULL;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+ int snum;
+ WERROR status = WERR_OK;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ DEBUG(5,("spoolss_deleteform\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_deleteform: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ /* forms can be deleted on printer of on the print server handle */
+
+ if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTER )
+ {
+ if (!get_printer_snum(p,handle, &snum))
+ return WERR_BADFID;
+
+ status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(status))
+ goto done;
+ }
+
+ if ( !(Printer->access_granted & (PRINTER_ACCESS_ADMINISTER|SERVER_ACCESS_ADMINISTER)) ) {
+ DEBUG(2,("_spoolss_deleteform: denied by handle permissions.\n"));
+ status = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ /* can't delete if builtin */
+
+ if (get_a_builtin_ntform(form_name,&tmpForm)) {
+ status = WERR_INVALID_PARAM;
+ goto done;
+ }
+
+ count = get_ntforms(&list);
+
+ if ( !delete_a_form(&list, form_name, &count, &status ))
+ goto done;
+
+ /*
+ * ChangeID must always be set if this is a printer
+ */
+
+ if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTER )
+ status = mod_a_printer(*printer, 2);
+
+done:
+ if ( printer )
+ free_a_printer(&printer, 2);
+ SAFE_FREE(list);
+
+ return status;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_setform(pipes_struct *p, SPOOL_Q_SETFORM *q_u, SPOOL_R_SETFORM *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ FORM *form = &q_u->form;
+ nt_forms_struct tmpForm;
+ int snum;
+ WERROR status = WERR_OK;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ int count=0;
+ nt_forms_struct *list=NULL;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ DEBUG(5,("spoolss_setform\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_setform: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ /* forms can be modified on printer of on the print server handle */
+
+ if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTER )
+ {
+ if (!get_printer_snum(p,handle, &snum))
+ return WERR_BADFID;
+
+ status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(status))
+ goto done;
+ }
+
+ if ( !(Printer->access_granted & (PRINTER_ACCESS_ADMINISTER|SERVER_ACCESS_ADMINISTER)) ) {
+ DEBUG(2,("_spoolss_setform: denied by handle permissions\n"));
+ status = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ /* can't set if builtin */
+ if (get_a_builtin_ntform(&form->name,&tmpForm)) {
+ status = WERR_INVALID_PARAM;
+ goto done;
+ }
+
+ count = get_ntforms(&list);
+ update_a_form(&list, form, count);
+ write_ntforms(&list, count);
+
+ /*
+ * ChangeID must always be set if this is a printer
+ */
+
+ if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTER )
+ status = mod_a_printer(*printer, 2);
+
+
+done:
+ if ( printer )
+ free_a_printer(&printer, 2);
+ SAFE_FREE(list);
+
+ return status;
+}
+
+/****************************************************************************
+ enumprintprocessors level 1.
+****************************************************************************/
+
+static WERROR enumprintprocessors_level_1(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ PRINTPROCESSOR_1 *info_1=NULL;
+
+ if((info_1 = (PRINTPROCESSOR_1 *)malloc(sizeof(PRINTPROCESSOR_1))) == NULL)
+ return WERR_NOMEM;
+
+ (*returned) = 0x1;
+
+ init_unistr(&info_1->name, "winprint");
+
+ *needed += spoolss_size_printprocessor_info_1(info_1);
+
+ if (!alloc_buffer_size(buffer, *needed))
+ return WERR_INSUFFICIENT_BUFFER;
+
+ smb_io_printprocessor_info_1("", buffer, info_1, 0);
+
+ SAFE_FREE(info_1);
+
+ if (*needed > offered) {
+ *returned=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_enumprintprocessors(pipes_struct *p, SPOOL_Q_ENUMPRINTPROCESSORS *q_u, SPOOL_R_ENUMPRINTPROCESSORS *r_u)
+{
+ uint32 level = q_u->level;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ uint32 *returned = &r_u->returned;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(5,("spoolss_enumprintprocessors\n"));
+
+ /*
+ * Enumerate the print processors ...
+ *
+ * Just reply with "winprint", to keep NT happy
+ * and I can use my nice printer checker.
+ */
+
+ *returned=0;
+ *needed=0;
+
+ switch (level) {
+ case 1:
+ return enumprintprocessors_level_1(buffer, offered, needed, returned);
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+ enumprintprocdatatypes level 1.
+****************************************************************************/
+
+static WERROR enumprintprocdatatypes_level_1(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ PRINTPROCDATATYPE_1 *info_1=NULL;
+
+ if((info_1 = (PRINTPROCDATATYPE_1 *)malloc(sizeof(PRINTPROCDATATYPE_1))) == NULL)
+ return WERR_NOMEM;
+
+ (*returned) = 0x1;
+
+ init_unistr(&info_1->name, "RAW");
+
+ *needed += spoolss_size_printprocdatatype_info_1(info_1);
+
+ if (!alloc_buffer_size(buffer, *needed))
+ return WERR_INSUFFICIENT_BUFFER;
+
+ smb_io_printprocdatatype_info_1("", buffer, info_1, 0);
+
+ SAFE_FREE(info_1);
+
+ if (*needed > offered) {
+ *returned=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_enumprintprocdatatypes(pipes_struct *p, SPOOL_Q_ENUMPRINTPROCDATATYPES *q_u, SPOOL_R_ENUMPRINTPROCDATATYPES *r_u)
+{
+ uint32 level = q_u->level;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ uint32 *returned = &r_u->returned;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(5,("_spoolss_enumprintprocdatatypes\n"));
+
+ *returned=0;
+ *needed=0;
+
+ switch (level) {
+ case 1:
+ return enumprintprocdatatypes_level_1(buffer, offered, needed, returned);
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+ enumprintmonitors level 1.
+****************************************************************************/
+
+static WERROR enumprintmonitors_level_1(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ PRINTMONITOR_1 *info_1=NULL;
+
+ if((info_1 = (PRINTMONITOR_1 *)malloc(sizeof(PRINTMONITOR_1))) == NULL)
+ return WERR_NOMEM;
+
+ (*returned) = 0x1;
+
+ init_unistr(&info_1->name, "Local Port");
+
+ *needed += spoolss_size_printmonitor_info_1(info_1);
+
+ if (!alloc_buffer_size(buffer, *needed))
+ return WERR_INSUFFICIENT_BUFFER;
+
+ smb_io_printmonitor_info_1("", buffer, info_1, 0);
+
+ SAFE_FREE(info_1);
+
+ if (*needed > offered) {
+ *returned=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ enumprintmonitors level 2.
+****************************************************************************/
+
+static WERROR enumprintmonitors_level_2(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ PRINTMONITOR_2 *info_2=NULL;
+
+ if((info_2 = (PRINTMONITOR_2 *)malloc(sizeof(PRINTMONITOR_2))) == NULL)
+ return WERR_NOMEM;
+
+ (*returned) = 0x1;
+
+ init_unistr(&info_2->name, "Local Port");
+ init_unistr(&info_2->environment, "Windows NT X86");
+ init_unistr(&info_2->dll_name, "localmon.dll");
+
+ *needed += spoolss_size_printmonitor_info_2(info_2);
+
+ if (!alloc_buffer_size(buffer, *needed))
+ return WERR_INSUFFICIENT_BUFFER;
+
+ smb_io_printmonitor_info_2("", buffer, info_2, 0);
+
+ SAFE_FREE(info_2);
+
+ if (*needed > offered) {
+ *returned=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_enumprintmonitors(pipes_struct *p, SPOOL_Q_ENUMPRINTMONITORS *q_u, SPOOL_R_ENUMPRINTMONITORS *r_u)
+{
+ uint32 level = q_u->level;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ uint32 *returned = &r_u->returned;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(5,("spoolss_enumprintmonitors\n"));
+
+ /*
+ * Enumerate the print monitors ...
+ *
+ * Just reply with "Local Port", to keep NT happy
+ * and I can use my nice printer checker.
+ */
+
+ *returned=0;
+ *needed=0;
+
+ switch (level) {
+ case 1:
+ return enumprintmonitors_level_1(buffer, offered, needed, returned);
+ case 2:
+ return enumprintmonitors_level_2(buffer, offered, needed, returned);
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getjob_level_1(print_queue_struct **queue, int count, int snum, uint32 jobid, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ int i=0;
+ BOOL found=False;
+ JOB_INFO_1 *info_1=NULL;
+
+ info_1=(JOB_INFO_1 *)malloc(sizeof(JOB_INFO_1));
+
+ if (info_1 == NULL) {
+ return WERR_NOMEM;
+ }
+
+ for (i=0; i<count && found==False; i++) {
+ if ((*queue)[i].job==(int)jobid)
+ found=True;
+ }
+
+ if (found==False) {
+ SAFE_FREE(info_1);
+ /* NT treats not found as bad param... yet another bad choice */
+ return WERR_INVALID_PARAM;
+ }
+
+ fill_job_info_1(info_1, &((*queue)[i-1]), i, snum);
+
+ *needed += spoolss_size_job_info_1(info_1);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(info_1);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ smb_io_job_info_1("", buffer, info_1, 0);
+
+ SAFE_FREE(info_1);
+
+ if (*needed > offered)
+ return WERR_INSUFFICIENT_BUFFER;
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getjob_level_2(print_queue_struct **queue, int count, int snum, uint32 jobid, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ int i = 0;
+ BOOL found = False;
+ JOB_INFO_2 *info_2;
+ NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+ WERROR ret;
+ DEVICEMODE *devmode = NULL;
+ NT_DEVICEMODE *nt_devmode = NULL;
+
+ info_2=(JOB_INFO_2 *)malloc(sizeof(JOB_INFO_2));
+
+ ZERO_STRUCTP(info_2);
+
+ if (info_2 == NULL) {
+ ret = WERR_NOMEM;
+ goto done;
+ }
+
+ for ( i=0; i<count && found==False; i++ )
+ {
+ if ((*queue)[i].job == (int)jobid)
+ found = True;
+ }
+
+ if ( !found )
+ {
+ /* NT treats not found as bad param... yet another bad
+ choice */
+ ret = WERR_INVALID_PARAM;
+ goto done;
+ }
+
+ ret = get_a_printer(NULL, &ntprinter, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(ret))
+ goto done;
+
+ /*
+ * if the print job does not have a DEVMODE associated with it,
+ * just use the one for the printer. A NULL devicemode is not
+ * a failure condition
+ */
+
+ if ( !(nt_devmode=print_job_devmode( snum, jobid )) )
+ devmode = construct_dev_mode(snum);
+ else {
+ if ((devmode = (DEVICEMODE *)malloc(sizeof(DEVICEMODE))) != NULL) {
+ ZERO_STRUCTP( devmode );
+ convert_nt_devicemode( devmode, nt_devmode );
+ }
+ }
+
+ fill_job_info_2(info_2, &((*queue)[i-1]), i, snum, ntprinter, devmode);
+
+ *needed += spoolss_size_job_info_2(info_2);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ ret = WERR_INSUFFICIENT_BUFFER;
+ goto done;
+ }
+
+ smb_io_job_info_2("", buffer, info_2, 0);
+
+ if (*needed > offered) {
+ ret = WERR_INSUFFICIENT_BUFFER;
+ goto done;
+ }
+
+ ret = WERR_OK;
+
+ done:
+ /* Cleanup allocated memory */
+
+ free_job_info_2(info_2); /* Also frees devmode */
+ SAFE_FREE(info_2);
+ free_a_printer(&ntprinter, 2);
+
+ return ret;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_getjob( pipes_struct *p, SPOOL_Q_GETJOB *q_u, SPOOL_R_GETJOB *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 jobid = q_u->jobid;
+ uint32 level = q_u->level;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ WERROR wstatus = WERR_OK;
+
+ int snum;
+ int count;
+ print_queue_struct *queue = NULL;
+ print_status_struct prt_status;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(5,("spoolss_getjob\n"));
+
+ *needed = 0;
+
+ if (!get_printer_snum(p, handle, &snum))
+ return WERR_BADFID;
+
+ count = print_queue_status(snum, &queue, &prt_status);
+
+ DEBUGADD(4,("count:[%d], prt_status:[%d], [%s]\n",
+ count, prt_status.status, prt_status.message));
+
+ switch ( level ) {
+ case 1:
+ wstatus = getjob_level_1(&queue, count, snum, jobid,
+ buffer, offered, needed);
+ break;
+ case 2:
+ wstatus = getjob_level_2(&queue, count, snum, jobid,
+ buffer, offered, needed);
+ break;
+ default:
+ wstatus = WERR_UNKNOWN_LEVEL;
+ break;
+ }
+
+ SAFE_FREE(queue);
+ return wstatus;
+}
+
+/********************************************************************
+ spoolss_getprinterdataex
+
+ From MSDN documentation of GetPrinterDataEx: pass request
+ to GetPrinterData if key is "PrinterDriverData".
+ ********************************************************************/
+
+WERROR _spoolss_getprinterdataex(pipes_struct *p, SPOOL_Q_GETPRINTERDATAEX *q_u, SPOOL_R_GETPRINTERDATAEX *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 in_size = q_u->size;
+ uint32 *type = &r_u->type;
+ uint32 *out_size = &r_u->size;
+ uint8 **data = &r_u->data;
+ uint32 *needed = &r_u->needed;
+ fstring keyname, valuename;
+
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ int snum = 0;
+ WERROR status = WERR_OK;
+
+ DEBUG(4,("_spoolss_getprinterdataex\n"));
+
+ unistr2_to_ascii(keyname, &q_u->keyname, sizeof(keyname) - 1);
+ unistr2_to_ascii(valuename, &q_u->valuename, sizeof(valuename) - 1);
+
+ DEBUG(10, ("_spoolss_getprinterdataex: key => [%s], value => [%s]\n",
+ keyname, valuename));
+
+ /* in case of problem, return some default values */
+
+ *needed = 0;
+ *type = 0;
+ *out_size = in_size;
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_getprinterdataex: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ status = WERR_BADFID;
+ goto done;
+ }
+
+ /* Is the handle to a printer or to the server? */
+
+ if (Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER) {
+ DEBUG(10,("_spoolss_getprinterdataex: Not implemented for server handles yet\n"));
+ status = WERR_INVALID_PARAM;
+ goto done;
+ }
+
+ if ( !get_printer_snum(p,handle, &snum) )
+ return WERR_BADFID;
+
+ status = get_a_printer(Printer, &printer, 2, lp_servicename(snum));
+ if ( !W_ERROR_IS_OK(status) )
+ goto done;
+
+ /* check to see if the keyname is valid */
+ if ( !strlen(keyname) ) {
+ status = WERR_INVALID_PARAM;
+ goto done;
+ }
+
+ if ( lookup_printerkey( &printer->info_2->data, keyname ) == -1 ) {
+ DEBUG(4,("_spoolss_getprinterdataex: Invalid keyname [%s]\n", keyname ));
+ free_a_printer( &printer, 2 );
+ status = WERR_BADFILE;
+ goto done;
+ }
+
+ /* When given a new keyname, we should just create it */
+
+ status = get_printer_dataex( p->mem_ctx, printer, keyname, valuename, type, data, needed, in_size );
+
+ if (*needed > *out_size)
+ status = WERR_MORE_DATA;
+
+done:
+ if ( !W_ERROR_IS_OK(status) )
+ {
+ DEBUG(5, ("error: allocating %d\n", *out_size));
+
+ /* reply this param doesn't exist */
+
+ if ( *out_size )
+ {
+ if( (*data=(uint8 *)talloc_zero(p->mem_ctx, *out_size*sizeof(uint8))) == NULL ) {
+ status = WERR_NOMEM;
+ goto done;
+ }
+ }
+ else {
+ *data = NULL;
+ }
+ }
+
+ if ( printer )
+ free_a_printer( &printer, 2 );
+
+ return status;
+}
+
+/********************************************************************
+ * spoolss_setprinterdataex
+ ********************************************************************/
+
+WERROR _spoolss_setprinterdataex(pipes_struct *p, SPOOL_Q_SETPRINTERDATAEX *q_u, SPOOL_R_SETPRINTERDATAEX *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 type = q_u->type;
+ uint8 *data = q_u->data;
+ uint32 real_len = q_u->real_len;
+
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ int snum = 0;
+ WERROR status = WERR_OK;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+ fstring valuename;
+ fstring keyname;
+ char *oid_string;
+
+ DEBUG(4,("_spoolss_setprinterdataex\n"));
+
+ /* From MSDN documentation of SetPrinterDataEx: pass request to
+ SetPrinterData if key is "PrinterDriverData" */
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_setprinterdataex: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER ) {
+ DEBUG(10,("_spoolss_setprinterdataex: Not implemented for server handles yet\n"));
+ return WERR_INVALID_PARAM;
+ }
+
+ if ( !get_printer_snum(p,handle, &snum) )
+ return WERR_BADFID;
+
+ /*
+ * Access check : NT returns "access denied" if you make a
+ * SetPrinterData call without the necessary privildge.
+ * we were originally returning OK if nothing changed
+ * which made Win2k issue **a lot** of SetPrinterData
+ * when connecting to a printer --jerry
+ */
+
+ if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER)
+ {
+ DEBUG(3, ("_spoolss_setprinterdataex: change denied by handle access permissions\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ status = get_a_printer(Printer, &printer, 2, lp_servicename(snum));
+ if (!W_ERROR_IS_OK(status))
+ return status;
+
+ unistr2_to_ascii( valuename, &q_u->value, sizeof(valuename) - 1);
+ unistr2_to_ascii( keyname, &q_u->key, sizeof(keyname) - 1);
+
+ /* check for OID in valuename */
+
+ if ( (oid_string = strchr( valuename, ',' )) != NULL )
+ {
+ *oid_string = '\0';
+ oid_string++;
+ }
+
+ /* save the registry data */
+
+ status = set_printer_dataex( printer, keyname, valuename, type, data, real_len );
+
+ if ( W_ERROR_IS_OK(status) )
+ {
+ /* save the OID if one was specified */
+ if ( oid_string ) {
+ fstrcat( keyname, "\\" );
+ fstrcat( keyname, SPOOL_OID_KEY );
+
+ /*
+ * I'm not checking the status here on purpose. Don't know
+ * if this is right, but I'm returning the status from the
+ * previous set_printer_dataex() call. I have no idea if
+ * this is right. --jerry
+ */
+
+ set_printer_dataex( printer, keyname, valuename,
+ REG_SZ, (void*)oid_string, strlen(oid_string)+1 );
+ }
+
+ status = mod_a_printer(*printer, 2);
+ }
+
+ free_a_printer(&printer, 2);
+
+ return status;
+}
+
+
+/********************************************************************
+ * spoolss_deleteprinterdataex
+ ********************************************************************/
+
+WERROR _spoolss_deleteprinterdataex(pipes_struct *p, SPOOL_Q_DELETEPRINTERDATAEX *q_u, SPOOL_R_DELETEPRINTERDATAEX *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ UNISTR2 *value = &q_u->valuename;
+ UNISTR2 *key = &q_u->keyname;
+
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ int snum=0;
+ WERROR status = WERR_OK;
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+ pstring valuename, keyname;
+
+ DEBUG(5,("spoolss_deleteprinterdataex\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_deleteprinterdata: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if (!get_printer_snum(p, handle, &snum))
+ return WERR_BADFID;
+
+ if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) {
+ DEBUG(3, ("_spoolss_deleteprinterdataex: printer properties change denied by handle\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(status))
+ return status;
+
+ unistr2_to_ascii( valuename, value, sizeof(valuename)-1 );
+ unistr2_to_ascii( keyname, key, sizeof(keyname)-1 );
+
+ status = delete_printer_dataex( printer, keyname, valuename );
+
+ if ( W_ERROR_IS_OK(status) )
+ mod_a_printer( *printer, 2 );
+
+ free_a_printer(&printer, 2);
+
+ return status;
+}
+
+/********************************************************************
+ * spoolss_enumprinterkey
+ ********************************************************************/
+
+
+WERROR _spoolss_enumprinterkey(pipes_struct *p, SPOOL_Q_ENUMPRINTERKEY *q_u, SPOOL_R_ENUMPRINTERKEY *r_u)
+{
+ fstring key;
+ fstring *keynames = NULL;
+ uint16 *enumkeys = NULL;
+ int num_keys;
+ int printerkey_len;
+ POLICY_HND *handle = &q_u->handle;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+ NT_PRINTER_DATA *data;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ int snum = 0;
+ WERROR status = WERR_BADFILE;
+
+
+ DEBUG(4,("_spoolss_enumprinterkey\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_enumprinterkey: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if ( !get_printer_snum(p,handle, &snum) )
+ return WERR_BADFID;
+
+ status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(status))
+ return status;
+
+ /* get the list of subkey names */
+
+ unistr2_to_ascii( key, &q_u->key, sizeof(key)-1 );
+ data = &printer->info_2->data;
+
+ num_keys = get_printer_subkeys( data, key, &keynames );
+
+ if ( num_keys == -1 ) {
+ status = WERR_BADFILE;
+ goto done;
+ }
+
+ printerkey_len = init_unistr_array( &enumkeys, keynames, NULL );
+
+ r_u->needed = printerkey_len*2;
+
+ if ( q_u->size < r_u->needed ) {
+ status = WERR_MORE_DATA;
+ goto done;
+ }
+
+ if (!make_spoolss_buffer5(p->mem_ctx, &r_u->keys, printerkey_len, enumkeys)) {
+ status = WERR_NOMEM;
+ goto done;
+ }
+
+ status = WERR_OK;
+
+ if ( q_u->size < r_u->needed )
+ status = WERR_MORE_DATA;
+
+done:
+ free_a_printer( &printer, 2 );
+ SAFE_FREE( keynames );
+
+ return status;
+}
+
+/********************************************************************
+ * spoolss_deleteprinterkey
+ ********************************************************************/
+
+WERROR _spoolss_deleteprinterkey(pipes_struct *p, SPOOL_Q_DELETEPRINTERKEY *q_u, SPOOL_R_DELETEPRINTERKEY *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, &q_u->handle);
+ fstring key;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ int snum=0;
+ WERROR status;
+
+ DEBUG(5,("spoolss_deleteprinterkey\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_deleteprinterkey: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ /* if keyname == NULL, return error */
+
+ if ( !q_u->keyname.buffer )
+ return WERR_INVALID_PARAM;
+
+ if (!get_printer_snum(p, handle, &snum))
+ return WERR_BADFID;
+
+ if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) {
+ DEBUG(3, ("_spoolss_deleteprinterkey: printer properties change denied by handle\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(status))
+ return status;
+
+ /* delete the key and all subneys */
+
+ unistr2_to_ascii(key, &q_u->keyname, sizeof(key) - 1);
+
+ status = delete_all_printer_data( printer->info_2, key );
+
+ if ( W_ERROR_IS_OK(status) )
+ status = mod_a_printer(*printer, 2);
+
+ free_a_printer( &printer, 2 );
+
+ return status;
+}
+
+
+/********************************************************************
+ * spoolss_enumprinterdataex
+ ********************************************************************/
+
+WERROR _spoolss_enumprinterdataex(pipes_struct *p, SPOOL_Q_ENUMPRINTERDATAEX *q_u, SPOOL_R_ENUMPRINTERDATAEX *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 in_size = q_u->size;
+ uint32 num_entries,
+ needed;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ PRINTER_ENUM_VALUES *enum_values = NULL;
+ NT_PRINTER_DATA *p_data;
+ fstring key;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+ int snum;
+ WERROR result;
+ int key_index;
+ int i;
+ REGISTRY_VALUE *val;
+ char *value_name;
+ int data_len;
+
+
+ DEBUG(4,("_spoolss_enumprinterdataex\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_enumprinterdataex: Invalid handle (%s:%u:%u1<).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ /*
+ * first check for a keyname of NULL or "". Win2k seems to send
+ * this a lot and we should send back WERR_INVALID_PARAM
+ * no need to spend time looking up the printer in this case.
+ * --jerry
+ */
+
+ unistr2_to_ascii(key, &q_u->key, sizeof(key) - 1);
+ if ( !strlen(key) ) {
+ result = WERR_INVALID_PARAM;
+ goto done;
+ }
+
+ /* get the printer off of disk */
+
+ if (!get_printer_snum(p,handle, &snum))
+ return WERR_BADFID;
+
+ ZERO_STRUCT(printer);
+ result = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(result))
+ return result;
+
+ /* now look for a match on the key name */
+
+ p_data = &printer->info_2->data;
+
+ unistr2_to_ascii(key, &q_u->key, sizeof(key) - 1);
+ if ( (key_index = lookup_printerkey( p_data, key)) == -1 )
+ {
+ DEBUG(10,("_spoolss_enumprinterdataex: Unknown keyname [%s]\n", key));
+ result = WERR_INVALID_PARAM;
+ goto done;
+ }
+
+ result = WERR_OK;
+ needed = 0;
+
+ /* allocate the memory for the array of pointers -- if necessary */
+
+ num_entries = regval_ctr_numvals( &p_data->keys[key_index].values );
+ if ( num_entries )
+ {
+ if ( (enum_values=talloc(p->mem_ctx, num_entries*sizeof(PRINTER_ENUM_VALUES))) == NULL )
+ {
+ DEBUG(0,("_spoolss_enumprinterdataex: talloc() failed to allocate memory for [%lu] bytes!\n",
+ (unsigned long)num_entries*sizeof(PRINTER_ENUM_VALUES)));
+ result = WERR_NOMEM;
+ goto done;
+ }
+
+ memset( enum_values, 0x0, num_entries*sizeof(PRINTER_ENUM_VALUES) );
+ }
+
+ /*
+ * loop through all params and build the array to pass
+ * back to the client
+ */
+
+ for ( i=0; i<num_entries; i++ )
+ {
+ /* lookup the registry value */
+
+ val = regval_ctr_specific_value( &p_data->keys[key_index].values, i );
+ DEBUG(10,("retrieved value number [%d] [%s]\n", i, regval_name(val) ));
+
+ /* copy the data */
+
+ value_name = regval_name( val );
+ init_unistr( &enum_values[i].valuename, value_name );
+ enum_values[i].value_len = (strlen(value_name)+1) * 2;
+ enum_values[i].type = regval_type( val );
+
+ data_len = regval_size( val );
+ if ( data_len ) {
+ if ( !(enum_values[i].data = talloc_memdup(p->mem_ctx, regval_data_p(val), data_len)) )
+ {
+ DEBUG(0,("talloc_memdup failed to allocate memory [data_len=%d] for data!\n",
+ data_len ));
+ result = WERR_NOMEM;
+ goto done;
+ }
+ }
+ enum_values[i].data_len = data_len;
+
+ /* keep track of the size of the array in bytes */
+
+ needed += spoolss_size_printer_enum_values(&enum_values[i]);
+ }
+
+ /* housekeeping information in the reply */
+
+ r_u->needed = needed;
+ r_u->returned = num_entries;
+
+ if (needed > in_size) {
+ result = WERR_MORE_DATA;
+ goto done;
+ }
+
+ /* copy data into the reply */
+
+ r_u->ctr.size = r_u->needed;
+ r_u->ctr.size_of_array = r_u->returned;
+ r_u->ctr.values = enum_values;
+
+
+
+done:
+ if ( printer )
+ free_a_printer(&printer, 2);
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void fill_printprocessordirectory_1(PRINTPROCESSOR_DIRECTORY_1 *info, char *name)
+{
+ init_unistr(&info->name, name);
+}
+
+static WERROR getprintprocessordirectory_level_1(UNISTR2 *name,
+ UNISTR2 *environment,
+ NEW_BUFFER *buffer,
+ uint32 offered,
+ uint32 *needed)
+{
+ pstring path;
+ pstring long_archi;
+ PRINTPROCESSOR_DIRECTORY_1 *info=NULL;
+
+ unistr2_to_ascii(long_archi, environment, sizeof(long_archi)-1);
+
+ if (!get_short_archi(long_archi))
+ return WERR_INVALID_ENVIRONMENT;
+
+ if((info=(PRINTPROCESSOR_DIRECTORY_1 *)malloc(sizeof(PRINTPROCESSOR_DIRECTORY_1))) == NULL)
+ return WERR_NOMEM;
+
+ pstrcpy(path, "C:\\WINNT\\System32\\spool\\PRTPROCS\\W32X86");
+
+ fill_printprocessordirectory_1(info, path);
+
+ *needed += spoolss_size_printprocessordirectory_info_1(info);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ safe_free(info);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ smb_io_printprocessordirectory_1("", buffer, info, 0);
+
+ safe_free(info);
+
+ if (*needed > offered)
+ return WERR_INSUFFICIENT_BUFFER;
+ else
+ return WERR_OK;
+}
+
+WERROR _spoolss_getprintprocessordirectory(pipes_struct *p, SPOOL_Q_GETPRINTPROCESSORDIRECTORY *q_u, SPOOL_R_GETPRINTPROCESSORDIRECTORY *r_u)
+{
+ uint32 level = q_u->level;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ WERROR result;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(5,("_spoolss_getprintprocessordirectory\n"));
+
+ *needed=0;
+
+ switch(level) {
+ case 1:
+ result = getprintprocessordirectory_level_1
+ (&q_u->name, &q_u->environment, buffer, offered, needed);
+ break;
+ default:
+ result = WERR_UNKNOWN_LEVEL;
+ }
+
+ return result;
+}
+
+#if 0
+
+WERROR _spoolss_replyopenprinter(pipes_struct *p, SPOOL_Q_REPLYOPENPRINTER *q_u,
+ SPOOL_R_REPLYOPENPRINTER *r_u)
+{
+ DEBUG(5,("_spoolss_replyopenprinter\n"));
+
+ DEBUG(10, ("replyopenprinter for localprinter %d\n", q_u->printer));
+
+ return WERR_OK;
+}
+
+WERROR _spoolss_replycloseprinter(pipes_struct *p, SPOOL_Q_REPLYCLOSEPRINTER *q_u,
+ SPOOL_R_REPLYCLOSEPRINTER *r_u)
+{
+ DEBUG(5,("_spoolss_replycloseprinter\n"));
+ return WERR_OK;
+}
+
+#endif
diff --git a/source/rpc_server/srv_srvsvc.c b/source/rpc_server/srv_srvsvc.c
new file mode 100644
index 00000000000..9d85088e568
--- /dev/null
+++ b/source/rpc_server/srv_srvsvc.c
@@ -0,0 +1,561 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997,
+ * Copyright (C) Jeremy Allison 2001,
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This is the interface to the srvsvc pipe. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*******************************************************************
+ api_srv_net_srv_get_info
+********************************************************************/
+
+static BOOL api_srv_net_srv_get_info(pipes_struct *p)
+{
+ SRV_Q_NET_SRV_GET_INFO q_u;
+ SRV_R_NET_SRV_GET_INFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the net server get info */
+ if (!srv_io_q_net_srv_get_info("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _srv_net_srv_get_info(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if (!srv_io_r_net_srv_get_info("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_srv_net_srv_get_info
+********************************************************************/
+
+static BOOL api_srv_net_srv_set_info(pipes_struct *p)
+{
+ SRV_Q_NET_SRV_SET_INFO q_u;
+ SRV_R_NET_SRV_SET_INFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the net server set info */
+ if (!srv_io_q_net_srv_set_info("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _srv_net_srv_set_info(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if (!srv_io_r_net_srv_set_info("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_srv_net_file_enum
+********************************************************************/
+
+static BOOL api_srv_net_file_enum(pipes_struct *p)
+{
+ SRV_Q_NET_FILE_ENUM q_u;
+ SRV_R_NET_FILE_ENUM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the net file enum */
+ if (!srv_io_q_net_file_enum("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _srv_net_file_enum(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!srv_io_r_net_file_enum("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_srv_net_conn_enum
+********************************************************************/
+
+static BOOL api_srv_net_conn_enum(pipes_struct *p)
+{
+ SRV_Q_NET_CONN_ENUM q_u;
+ SRV_R_NET_CONN_ENUM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the net server get enum */
+ if (!srv_io_q_net_conn_enum("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _srv_net_conn_enum(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if (!srv_io_r_net_conn_enum("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Enumerate sessions.
+********************************************************************/
+
+static BOOL api_srv_net_sess_enum(pipes_struct *p)
+{
+ SRV_Q_NET_SESS_ENUM q_u;
+ SRV_R_NET_SESS_ENUM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the net server get enum */
+ if (!srv_io_q_net_sess_enum("", &q_u, data, 0))
+ return False;
+
+ /* construct reply. always indicate success */
+ r_u.status = _srv_net_sess_enum(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if (!srv_io_r_net_sess_enum("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ RPC to enumerate shares.
+********************************************************************/
+
+static BOOL api_srv_net_share_enum_all(pipes_struct *p)
+{
+ SRV_Q_NET_SHARE_ENUM q_u;
+ SRV_R_NET_SHARE_ENUM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* Unmarshall the net server get enum. */
+ if(!srv_io_q_net_share_enum("", &q_u, data, 0)) {
+ DEBUG(0,("api_srv_net_share_enum_all: Failed to unmarshall SRV_Q_NET_SHARE_ENUM.\n"));
+ return False;
+ }
+
+ r_u.status = _srv_net_share_enum_all(p, &q_u, &r_u);
+
+ if (!srv_io_r_net_share_enum("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_srv_net_share_enum_all: Failed to marshall SRV_R_NET_SHARE_ENUM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ RPC to enumerate shares.
+********************************************************************/
+
+static BOOL api_srv_net_share_enum(pipes_struct *p)
+{
+ SRV_Q_NET_SHARE_ENUM q_u;
+ SRV_R_NET_SHARE_ENUM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* Unmarshall the net server get enum. */
+ if(!srv_io_q_net_share_enum("", &q_u, data, 0)) {
+ DEBUG(0,("api_srv_net_share_enum: Failed to unmarshall SRV_Q_NET_SHARE_ENUM.\n"));
+ return False;
+ }
+
+ r_u.status = _srv_net_share_enum(p, &q_u, &r_u);
+
+ if (!srv_io_r_net_share_enum("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_srv_net_share_enum: Failed to marshall SRV_R_NET_SHARE_ENUM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ RPC to return share information.
+********************************************************************/
+
+static BOOL api_srv_net_share_get_info(pipes_struct *p)
+{
+ SRV_Q_NET_SHARE_GET_INFO q_u;
+ SRV_R_NET_SHARE_GET_INFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* Unmarshall the net server get info. */
+ if(!srv_io_q_net_share_get_info("", &q_u, data, 0)) {
+ DEBUG(0,("api_srv_net_share_get_info: Failed to unmarshall SRV_Q_NET_SHARE_GET_INFO.\n"));
+ return False;
+ }
+
+ r_u.status = _srv_net_share_get_info(p, &q_u, &r_u);
+
+ if(!srv_io_r_net_share_get_info("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_srv_net_share_get_info: Failed to marshall SRV_R_NET_SHARE_GET_INFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ RPC to set share information.
+********************************************************************/
+
+static BOOL api_srv_net_share_set_info(pipes_struct *p)
+{
+ SRV_Q_NET_SHARE_SET_INFO q_u;
+ SRV_R_NET_SHARE_SET_INFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* Unmarshall the net server set info. */
+ if(!srv_io_q_net_share_set_info("", &q_u, data, 0)) {
+ DEBUG(0,("api_srv_net_share_set_info: Failed to unmarshall SRV_Q_NET_SHARE_SET_INFO.\n"));
+ return False;
+ }
+
+ r_u.status = _srv_net_share_set_info(p, &q_u, &r_u);
+
+ if(!srv_io_r_net_share_set_info("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_srv_net_share_set_info: Failed to marshall SRV_R_NET_SHARE_SET_INFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ RPC to add share information.
+********************************************************************/
+
+static BOOL api_srv_net_share_add(pipes_struct *p)
+{
+ SRV_Q_NET_SHARE_ADD q_u;
+ SRV_R_NET_SHARE_ADD r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* Unmarshall the net server add info. */
+ if(!srv_io_q_net_share_add("", &q_u, data, 0)) {
+ DEBUG(0,("api_srv_net_share_add: Failed to unmarshall SRV_Q_NET_SHARE_ADD.\n"));
+ return False;
+ }
+
+ r_u.status = _srv_net_share_add(p, &q_u, &r_u);
+
+ if(!srv_io_r_net_share_add("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_srv_net_share_add: Failed to marshall SRV_R_NET_SHARE_ADD.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ RPC to delete share information.
+********************************************************************/
+
+static BOOL api_srv_net_share_del(pipes_struct *p)
+{
+ SRV_Q_NET_SHARE_DEL q_u;
+ SRV_R_NET_SHARE_DEL r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* Unmarshall the net server del info. */
+ if(!srv_io_q_net_share_del("", &q_u, data, 0)) {
+ DEBUG(0,("api_srv_net_share_del: Failed to unmarshall SRV_Q_NET_SHARE_DEL.\n"));
+ return False;
+ }
+
+ r_u.status = _srv_net_share_del(p, &q_u, &r_u);
+
+ if(!srv_io_r_net_share_del("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_srv_net_share_del: Failed to marshall SRV_R_NET_SHARE_DEL.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ RPC to delete share information.
+********************************************************************/
+
+static BOOL api_srv_net_share_del_sticky(pipes_struct *p)
+{
+ SRV_Q_NET_SHARE_DEL q_u;
+ SRV_R_NET_SHARE_DEL r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* Unmarshall the net server del info. */
+ if(!srv_io_q_net_share_del("", &q_u, data, 0)) {
+ DEBUG(0,("api_srv_net_share_del_sticky: Failed to unmarshall SRV_Q_NET_SHARE_DEL.\n"));
+ return False;
+ }
+
+ r_u.status = _srv_net_share_del_sticky(p, &q_u, &r_u);
+
+ if(!srv_io_r_net_share_del("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_srv_net_share_del_sticky: Failed to marshall SRV_R_NET_SHARE_DEL.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_srv_net_remote_tod
+********************************************************************/
+
+static BOOL api_srv_net_remote_tod(pipes_struct *p)
+{
+ SRV_Q_NET_REMOTE_TOD q_u;
+ SRV_R_NET_REMOTE_TOD r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the net server get enum */
+ if(!srv_io_q_net_remote_tod("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _srv_net_remote_tod(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!srv_io_r_net_remote_tod("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ RPC to enumerate disks available on a server e.g. C:, D: ...
+*******************************************************************/
+
+static BOOL api_srv_net_disk_enum(pipes_struct *p)
+{
+ SRV_Q_NET_DISK_ENUM q_u;
+ SRV_R_NET_DISK_ENUM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* Unmarshall the net server disk enum. */
+ if(!srv_io_q_net_disk_enum("", &q_u, data, 0)) {
+ DEBUG(0,("api_srv_net_disk_enum: Failed to unmarshall SRV_Q_NET_DISK_ENUM.\n"));
+ return False;
+ }
+
+ r_u.status = _srv_net_disk_enum(p, &q_u, &r_u);
+
+ if(!srv_io_r_net_disk_enum("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_srv_net_disk_enum: Failed to marshall SRV_R_NET_DISK_ENUM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ NetValidateName (opnum 0x21)
+*******************************************************************/
+
+static BOOL api_srv_net_name_validate(pipes_struct *p)
+{
+ SRV_Q_NET_NAME_VALIDATE q_u;
+ SRV_R_NET_NAME_VALIDATE r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* Unmarshall the net server disk enum. */
+ if(!srv_io_q_net_name_validate("", &q_u, data, 0)) {
+ DEBUG(0,("api_srv_net_name_validate: Failed to unmarshall SRV_Q_NET_NAME_VALIDATE.\n"));
+ return False;
+ }
+
+ r_u.status = _srv_net_name_validate(p, &q_u, &r_u);
+
+ if(!srv_io_r_net_name_validate("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_srv_net_name_validate: Failed to marshall SRV_R_NET_NAME_VALIDATE.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ NetFileQuerySecdesc (opnum 0x27)
+*******************************************************************/
+
+static BOOL api_srv_net_file_query_secdesc(pipes_struct *p)
+{
+ SRV_Q_NET_FILE_QUERY_SECDESC q_u;
+ SRV_R_NET_FILE_QUERY_SECDESC r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* Unmarshall the net file get info from Win9x */
+ if(!srv_io_q_net_file_query_secdesc("", &q_u, data, 0)) {
+ DEBUG(0,("api_srv_net_file_query_secdesc: Failed to unmarshall SRV_Q_NET_FILE_QUERY_SECDESC.\n"));
+ return False;
+ }
+
+ r_u.status = _srv_net_file_query_secdesc(p, &q_u, &r_u);
+
+ if(!srv_io_r_net_file_query_secdesc("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_srv_net_file_query_secdesc: Failed to marshall SRV_R_NET_FILE_QUERY_SECDESC.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ NetFileSetSecdesc (opnum 0x28)
+*******************************************************************/
+
+static BOOL api_srv_net_file_set_secdesc(pipes_struct *p)
+{
+ SRV_Q_NET_FILE_SET_SECDESC q_u;
+ SRV_R_NET_FILE_SET_SECDESC r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* Unmarshall the net file set info from Win9x */
+ if(!srv_io_q_net_file_set_secdesc("", &q_u, data, 0)) {
+ DEBUG(0,("api_srv_net_file_set_secdesc: Failed to unmarshall SRV_Q_NET_FILE_SET_SECDESC.\n"));
+ return False;
+ }
+
+ r_u.status = _srv_net_file_set_secdesc(p, &q_u, &r_u);
+
+ if(!srv_io_r_net_file_set_secdesc("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_srv_net_file_set_secdesc: Failed to marshall SRV_R_NET_FILE_SET_SECDESC.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+\PIPE\srvsvc commands
+********************************************************************/
+
+static struct api_struct api_srv_cmds[] =
+{
+ { "SRV_NET_CONN_ENUM" , SRV_NET_CONN_ENUM , api_srv_net_conn_enum },
+ { "SRV_NET_SESS_ENUM" , SRV_NET_SESS_ENUM , api_srv_net_sess_enum },
+ { "SRV_NET_SHARE_ENUM_ALL" , SRV_NET_SHARE_ENUM_ALL , api_srv_net_share_enum_all },
+ { "SRV_NET_SHARE_ENUM" , SRV_NET_SHARE_ENUM , api_srv_net_share_enum },
+ { "SRV_NET_SHARE_ADD" , SRV_NET_SHARE_ADD , api_srv_net_share_add },
+ { "SRV_NET_SHARE_DEL" , SRV_NET_SHARE_DEL , api_srv_net_share_del },
+ { "SRV_NET_SHARE_DEL_STICKY" , SRV_NET_SHARE_DEL_STICKY , api_srv_net_share_del_sticky },
+ { "SRV_NET_SHARE_GET_INFO" , SRV_NET_SHARE_GET_INFO , api_srv_net_share_get_info },
+ { "SRV_NET_SHARE_SET_INFO" , SRV_NET_SHARE_SET_INFO , api_srv_net_share_set_info },
+ { "SRV_NET_FILE_ENUM" , SRV_NET_FILE_ENUM , api_srv_net_file_enum },
+ { "SRV_NET_SRV_GET_INFO" , SRV_NET_SRV_GET_INFO , api_srv_net_srv_get_info },
+ { "SRV_NET_SRV_SET_INFO" , SRV_NET_SRV_SET_INFO , api_srv_net_srv_set_info },
+ { "SRV_NET_REMOTE_TOD" , SRV_NET_REMOTE_TOD , api_srv_net_remote_tod },
+ { "SRV_NET_DISK_ENUM" , SRV_NET_DISK_ENUM , api_srv_net_disk_enum },
+ { "SRV_NET_NAME_VALIDATE" , SRV_NET_NAME_VALIDATE , api_srv_net_name_validate },
+ { "SRV_NET_FILE_QUERY_SECDESC", SRV_NET_FILE_QUERY_SECDESC, api_srv_net_file_query_secdesc },
+ { "SRV_NET_FILE_SET_SECDESC" , SRV_NET_FILE_SET_SECDESC , api_srv_net_file_set_secdesc }
+};
+
+void srvsvc_get_pipe_fns( struct api_struct **fns, int *n_fns )
+{
+ *fns = api_srv_cmds;
+ *n_fns = sizeof(api_srv_cmds) / sizeof(struct api_struct);
+}
+
+
+NTSTATUS rpc_srv_init(void)
+{
+ return rpc_pipe_register_commands(SMB_RPC_INTERFACE_VERSION, "srvsvc", "ntsvcs", api_srv_cmds,
+ sizeof(api_srv_cmds) / sizeof(struct api_struct));
+}
diff --git a/source/rpc_server/srv_srvsvc_nt.c b/source/rpc_server/srv_srvsvc_nt.c
new file mode 100644
index 00000000000..9d56e1b3858
--- /dev/null
+++ b/source/rpc_server/srv_srvsvc_nt.c
@@ -0,0 +1,2195 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Jeremy Allison 2001.
+ * Copyright (C) Nigel Williams 2001.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This is the implementation of the srvsvc pipe. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*******************************************************************
+ Utility function to get the 'type' of a share from an snum.
+ ********************************************************************/
+static uint32 get_share_type(int snum)
+{
+ char *net_name = lp_servicename(snum);
+ int len_net_name = strlen(net_name);
+
+ /* work out the share type */
+ uint32 type = STYPE_DISKTREE;
+
+ if (lp_print_ok(snum))
+ type = STYPE_PRINTQ;
+ if (strequal(lp_fstype(snum), "IPC"))
+ type = STYPE_IPC;
+ if (net_name[len_net_name] == '$')
+ type |= STYPE_HIDDEN;
+
+ return type;
+}
+
+/*******************************************************************
+ Fill in a share info level 0 structure.
+ ********************************************************************/
+
+static void init_srv_share_info_0(pipes_struct *p, SRV_SHARE_INFO_0 *sh0, int snum)
+{
+ pstring net_name;
+
+ pstrcpy(net_name, lp_servicename(snum));
+
+ init_srv_share_info0(&sh0->info_0, net_name);
+ init_srv_share_info0_str(&sh0->info_0_str, net_name);
+}
+
+/*******************************************************************
+ Fill in a share info level 1 structure.
+ ********************************************************************/
+
+static void init_srv_share_info_1(pipes_struct *p, SRV_SHARE_INFO_1 *sh1, int snum)
+{
+ pstring remark;
+
+ char *net_name = lp_servicename(snum);
+ pstrcpy(remark, lp_comment(snum));
+ standard_sub_conn(p->conn, remark,sizeof(remark));
+
+ init_srv_share_info1(&sh1->info_1, net_name, get_share_type(snum), remark);
+ init_srv_share_info1_str(&sh1->info_1_str, net_name, remark);
+}
+
+/*******************************************************************
+ Fill in a share info level 2 structure.
+ ********************************************************************/
+
+static void init_srv_share_info_2(pipes_struct *p, SRV_SHARE_INFO_2 *sh2, int snum)
+{
+ pstring remark;
+ pstring path;
+ pstring passwd;
+
+ char *net_name = lp_servicename(snum);
+ pstrcpy(remark, lp_comment(snum));
+ standard_sub_conn(p->conn, remark,sizeof(remark));
+ pstrcpy(path, "C:");
+ pstrcat(path, lp_pathname(snum));
+
+ /*
+ * Change / to \\ so that win2k will see it as a valid path. This was added to
+ * enable use of browsing in win2k add share dialog.
+ */
+
+ string_replace(path, '/', '\\');
+
+ pstrcpy(passwd, "");
+
+ init_srv_share_info2(&sh2->info_2, net_name, get_share_type(snum), remark, 0, 0xffffffff, 1, path, passwd);
+ init_srv_share_info2_str(&sh2->info_2_str, net_name, remark, path, passwd);
+}
+
+/*******************************************************************
+ What to do when smb.conf is updated.
+ ********************************************************************/
+
+static void smb_conf_updated(int msg_type, pid_t src, void *buf, size_t len)
+{
+ DEBUG(10,("smb_conf_updated: Got message saying smb.conf was updated. Reloading.\n"));
+ reload_services(False);
+}
+
+/*******************************************************************
+ Create the share security tdb.
+ ********************************************************************/
+
+static TDB_CONTEXT *share_tdb; /* used for share security descriptors */
+#define SHARE_DATABASE_VERSION_V1 1
+#define SHARE_DATABASE_VERSION_V2 2 /* version id in little endian. */
+
+BOOL share_info_db_init(void)
+{
+ static pid_t local_pid;
+ const char *vstring = "INFO/version";
+ int32 vers_id;
+
+ if (share_tdb && local_pid == sys_getpid())
+ return True;
+ share_tdb = tdb_open_log(lock_path("share_info.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+ if (!share_tdb) {
+ DEBUG(0,("Failed to open share info database %s (%s)\n",
+ lock_path("share_info.tdb"), strerror(errno) ));
+ return False;
+ }
+
+ local_pid = sys_getpid();
+
+ /* handle a Samba upgrade */
+ tdb_lock_bystring(share_tdb, vstring, 0);
+
+ /* Cope with byte-reversed older versions of the db. */
+ vers_id = tdb_fetch_int32(share_tdb, vstring);
+ if ((vers_id == SHARE_DATABASE_VERSION_V1) || (IREV(vers_id) == SHARE_DATABASE_VERSION_V1)) {
+ /* Written on a bigendian machine with old fetch_int code. Save as le. */
+ tdb_store_int32(share_tdb, vstring, SHARE_DATABASE_VERSION_V2);
+ vers_id = SHARE_DATABASE_VERSION_V2;
+ }
+
+ if (vers_id != SHARE_DATABASE_VERSION_V2) {
+ tdb_traverse(share_tdb, tdb_traverse_delete_fn, NULL);
+ tdb_store_int32(share_tdb, vstring, SHARE_DATABASE_VERSION_V2);
+ }
+ tdb_unlock_bystring(share_tdb, vstring);
+
+ message_register(MSG_SMB_CONF_UPDATED, smb_conf_updated);
+
+ return True;
+}
+
+/*******************************************************************
+ Fake up a Everyone, full access as a default.
+ ********************************************************************/
+
+static SEC_DESC *get_share_security_default( TALLOC_CTX *ctx, int snum, size_t *psize)
+{
+ extern DOM_SID global_sid_World;
+ extern struct generic_mapping file_generic_mapping;
+ SEC_ACCESS sa;
+ SEC_ACE ace;
+ SEC_ACL *psa = NULL;
+ SEC_DESC *psd = NULL;
+ uint32 def_access = GENERIC_ALL_ACCESS;
+
+ se_map_generic(&def_access, &file_generic_mapping);
+
+ init_sec_access(&sa, GENERIC_ALL_ACCESS | def_access );
+ init_sec_ace(&ace, &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, sa, 0);
+
+ if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 1, &ace)) != NULL) {
+ psd = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL, psa, psize);
+ }
+
+ if (!psd) {
+ DEBUG(0,("get_share_security: Failed to make SEC_DESC.\n"));
+ return NULL;
+ }
+
+ return psd;
+}
+
+/*******************************************************************
+ Pull a security descriptor from the share tdb.
+ ********************************************************************/
+
+static SEC_DESC *get_share_security( TALLOC_CTX *ctx, int snum, size_t *psize)
+{
+ prs_struct ps;
+ fstring key;
+ SEC_DESC *psd = NULL;
+
+ *psize = 0;
+
+ /* Fetch security descriptor from tdb */
+
+ slprintf(key, sizeof(key)-1, "SECDESC/%s", lp_servicename(snum));
+
+ if (tdb_prs_fetch(share_tdb, key, &ps, ctx)!=0 ||
+ !sec_io_desc("get_share_security", &psd, &ps, 1)) {
+
+ DEBUG(4,("get_share_security: using default secdesc for %s\n", lp_servicename(snum) ));
+
+ return get_share_security_default(ctx, snum, psize);
+ }
+
+ if (psd)
+ *psize = sec_desc_size(psd);
+
+ prs_mem_free(&ps);
+ return psd;
+}
+
+/*******************************************************************
+ Store a security descriptor in the share db.
+ ********************************************************************/
+
+static BOOL set_share_security(TALLOC_CTX *ctx, const char *share_name, SEC_DESC *psd)
+{
+ prs_struct ps;
+ TALLOC_CTX *mem_ctx = NULL;
+ fstring key;
+ BOOL ret = False;
+
+ mem_ctx = talloc_init("set_share_security");
+ if (mem_ctx == NULL)
+ return False;
+
+ prs_init(&ps, (uint32)sec_desc_size(psd), mem_ctx, MARSHALL);
+
+ if (!sec_io_desc("share_security", &psd, &ps, 1))
+ goto out;
+
+ slprintf(key, sizeof(key)-1, "SECDESC/%s", share_name);
+
+ if (tdb_prs_store(share_tdb, key, &ps)==0) {
+ ret = True;
+ DEBUG(5,("set_share_security: stored secdesc for %s\n", share_name ));
+ } else {
+ DEBUG(1,("set_share_security: Failed to store secdesc for %s\n", share_name ));
+ }
+
+ /* Free malloc'ed memory */
+
+out:
+
+ prs_mem_free(&ps);
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+ return ret;
+}
+
+/*******************************************************************
+ Delete a security descriptor.
+********************************************************************/
+
+static BOOL delete_share_security(int snum)
+{
+ TDB_DATA kbuf;
+ fstring key;
+
+ slprintf(key, sizeof(key)-1, "SECDESC/%s", lp_servicename(snum));
+ kbuf.dptr = key;
+ kbuf.dsize = strlen(key)+1;
+
+ if (tdb_delete(share_tdb, kbuf) != 0) {
+ DEBUG(0,("delete_share_security: Failed to delete entry for share %s\n",
+ lp_servicename(snum) ));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Map any generic bits to file specific bits.
+********************************************************************/
+
+void map_generic_share_sd_bits(SEC_DESC *psd)
+{
+ extern struct generic_mapping file_generic_mapping;
+ int i;
+ SEC_ACL *ps_dacl = NULL;
+
+ if (!psd)
+ return;
+
+ ps_dacl = psd->dacl;
+ if (!ps_dacl)
+ return;
+
+ for (i = 0; i < ps_dacl->num_aces; i++) {
+ SEC_ACE *psa = &ps_dacl->ace[i];
+ uint32 orig_mask = psa->info.mask;
+
+ se_map_generic(&psa->info.mask, &file_generic_mapping);
+ psa->info.mask |= orig_mask;
+ }
+}
+
+/*******************************************************************
+ Can this user access with share with the required permissions ?
+********************************************************************/
+
+BOOL share_access_check(connection_struct *conn, int snum, user_struct *vuser, uint32 desired_access)
+{
+ uint32 granted;
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx = NULL;
+ SEC_DESC *psd = NULL;
+ size_t sd_size;
+ NT_USER_TOKEN *token = NULL;
+ BOOL ret = True;
+
+ mem_ctx = talloc_init("share_access_check");
+ if (mem_ctx == NULL)
+ return False;
+
+ psd = get_share_security(mem_ctx, snum, &sd_size);
+
+ if (!psd)
+ goto out;
+
+ if (conn->nt_user_token)
+ token = conn->nt_user_token;
+ else
+ token = vuser->nt_user_token;
+
+ ret = se_access_check(psd, token, desired_access, &granted, &status);
+
+out:
+
+ talloc_destroy(mem_ctx);
+
+ return ret;
+}
+
+/*******************************************************************
+ Fill in a share info level 501 structure.
+********************************************************************/
+
+static void init_srv_share_info_501(pipes_struct *p, SRV_SHARE_INFO_501 *sh501, int snum)
+{
+ int len_net_name;
+ pstring remark;
+
+ char *net_name = lp_servicename(snum);
+ pstrcpy(remark, lp_comment(snum));
+ standard_sub_conn(p->conn, remark, sizeof(remark));
+
+ len_net_name = strlen(net_name);
+
+ init_srv_share_info501(&sh501->info_501, net_name, get_share_type(snum), remark, (lp_csc_policy(snum) << 4));
+ init_srv_share_info501_str(&sh501->info_501_str, net_name, remark);
+}
+
+/*******************************************************************
+ Fill in a share info level 502 structure.
+ ********************************************************************/
+
+static void init_srv_share_info_502(pipes_struct *p, SRV_SHARE_INFO_502 *sh502, int snum)
+{
+ int len_net_name;
+ pstring net_name;
+ pstring remark;
+ pstring path;
+ pstring passwd;
+ SEC_DESC *sd;
+ size_t sd_size;
+ TALLOC_CTX *ctx = p->mem_ctx;
+
+
+ ZERO_STRUCTP(sh502);
+
+ pstrcpy(net_name, lp_servicename(snum));
+ pstrcpy(remark, lp_comment(snum));
+ standard_sub_conn(p->conn, remark,sizeof(remark));
+ pstrcpy(path, "C:");
+ pstrcat(path, lp_pathname(snum));
+
+ /*
+ * Change / to \\ so that win2k will see it as a valid path. This was added to
+ * enable use of browsing in win2k add share dialog.
+ */
+
+ string_replace(path, '/', '\\');
+
+ pstrcpy(passwd, "");
+ len_net_name = strlen(net_name);
+
+ sd = get_share_security(ctx, snum, &sd_size);
+
+ init_srv_share_info502(&sh502->info_502, net_name, get_share_type(snum), remark, 0, 0xffffffff, 1, path, passwd, sd, sd_size);
+ init_srv_share_info502_str(&sh502->info_502_str, net_name, remark, path, passwd, sd, sd_size);
+}
+
+/***************************************************************************
+ Fill in a share info level 1004 structure.
+ ***************************************************************************/
+
+static void init_srv_share_info_1004(pipes_struct *p, SRV_SHARE_INFO_1004* sh1004, int snum)
+{
+ pstring remark;
+
+ pstrcpy(remark, lp_comment(snum));
+ standard_sub_conn(p->conn, remark, sizeof(remark));
+
+ ZERO_STRUCTP(sh1004);
+
+ init_srv_share_info1004(&sh1004->info_1004, remark);
+ init_srv_share_info1004_str(&sh1004->info_1004_str, remark);
+}
+
+/***************************************************************************
+ Fill in a share info level 1005 structure.
+ ***************************************************************************/
+
+static void init_srv_share_info_1005(pipes_struct *p, SRV_SHARE_INFO_1005* sh1005, int snum)
+{
+ sh1005->share_info_flags = 0;
+
+ if(lp_host_msdfs() && lp_msdfs_root(snum))
+ sh1005->share_info_flags |=
+ SHARE_1005_IN_DFS | SHARE_1005_DFS_ROOT;
+ sh1005->share_info_flags |=
+ lp_csc_policy(snum) << SHARE_1005_CSC_POLICY_SHIFT;
+}
+/***************************************************************************
+ Fill in a share info level 1006 structure.
+ ***************************************************************************/
+
+static void init_srv_share_info_1006(pipes_struct *p, SRV_SHARE_INFO_1006* sh1006, int snum)
+{
+ sh1006->max_uses = -1;
+}
+
+/***************************************************************************
+ Fill in a share info level 1007 structure.
+ ***************************************************************************/
+
+static void init_srv_share_info_1007(pipes_struct *p, SRV_SHARE_INFO_1007* sh1007, int snum)
+{
+ pstring alternate_directory_name = "";
+ uint32 flags = 0;
+
+ ZERO_STRUCTP(sh1007);
+
+ init_srv_share_info1007(&sh1007->info_1007, flags, alternate_directory_name);
+ init_srv_share_info1007_str(&sh1007->info_1007_str, alternate_directory_name);
+}
+
+/*******************************************************************
+ Fill in a share info level 1501 structure.
+ ********************************************************************/
+
+static void init_srv_share_info_1501(pipes_struct *p, SRV_SHARE_INFO_1501 *sh1501, int snum)
+{
+ SEC_DESC *sd;
+ size_t sd_size;
+ TALLOC_CTX *ctx = p->mem_ctx;
+
+ ZERO_STRUCTP(sh1501);
+
+ sd = get_share_security(ctx, snum, &sd_size);
+
+ sh1501->sdb = make_sec_desc_buf(p->mem_ctx, sd_size, sd);
+}
+
+/*******************************************************************
+ True if it ends in '$'.
+ ********************************************************************/
+
+static BOOL is_hidden_share(int snum)
+{
+ const char *net_name = lp_servicename(snum);
+
+ return (net_name[strlen(net_name) - 1] == '$') ? True : False;
+}
+
+/*******************************************************************
+ Fill in a share info structure.
+ ********************************************************************/
+
+static BOOL init_srv_share_info_ctr(pipes_struct *p, SRV_SHARE_INFO_CTR *ctr,
+ uint32 info_level, uint32 *resume_hnd, uint32 *total_entries, BOOL all_shares)
+{
+ int num_entries = 0;
+ int num_services = lp_numservices();
+ int snum;
+ TALLOC_CTX *ctx = p->mem_ctx;
+
+ DEBUG(5,("init_srv_share_info_ctr\n"));
+
+ ZERO_STRUCTPN(ctr);
+
+ ctr->info_level = ctr->switch_value = info_level;
+ *resume_hnd = 0;
+
+ /* Count the number of entries. */
+ for (snum = 0; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) )
+ num_entries++;
+ }
+
+ *total_entries = num_entries;
+ ctr->num_entries2 = ctr->num_entries = num_entries;
+ ctr->ptr_share_info = ctr->ptr_entries = 1;
+
+ if (!num_entries)
+ return True;
+
+ switch (info_level) {
+ case 0:
+ {
+ SRV_SHARE_INFO_0 *info0;
+ int i = 0;
+
+ info0 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_0));
+
+ for (snum = *resume_hnd; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+ init_srv_share_info_0(p, &info0[i++], snum);
+ }
+ }
+
+ ctr->share.info0 = info0;
+ break;
+
+ }
+
+ case 1:
+ {
+ SRV_SHARE_INFO_1 *info1;
+ int i = 0;
+
+ info1 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_1));
+
+ for (snum = *resume_hnd; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+ init_srv_share_info_1(p, &info1[i++], snum);
+ }
+ }
+
+ ctr->share.info1 = info1;
+ break;
+ }
+
+ case 2:
+ {
+ SRV_SHARE_INFO_2 *info2;
+ int i = 0;
+
+ info2 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_2));
+
+ for (snum = *resume_hnd; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+ init_srv_share_info_2(p, &info2[i++], snum);
+ }
+ }
+
+ ctr->share.info2 = info2;
+ break;
+ }
+
+ case 501:
+ {
+ SRV_SHARE_INFO_501 *info501;
+ int i = 0;
+
+ info501 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_501));
+
+ for (snum = *resume_hnd; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+ init_srv_share_info_501(p, &info501[i++], snum);
+ }
+ }
+
+ ctr->share.info501 = info501;
+ break;
+ }
+
+ case 502:
+ {
+ SRV_SHARE_INFO_502 *info502;
+ int i = 0;
+
+ info502 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_502));
+
+ for (snum = *resume_hnd; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+ init_srv_share_info_502(p, &info502[i++], snum);
+ }
+ }
+
+ ctr->share.info502 = info502;
+ break;
+ }
+
+ /* here for completeness but not currently used with enum (1004 - 1501)*/
+
+ case 1004:
+ {
+ SRV_SHARE_INFO_1004 *info1004;
+ int i = 0;
+
+ info1004 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_1004));
+
+ for (snum = *resume_hnd; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+ init_srv_share_info_1004(p, &info1004[i++], snum);
+ }
+ }
+
+ ctr->share.info1004 = info1004;
+ break;
+ }
+
+ case 1005:
+ {
+ SRV_SHARE_INFO_1005 *info1005;
+ int i = 0;
+
+ info1005 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_1005));
+
+ for (snum = *resume_hnd; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+ init_srv_share_info_1005(p, &info1005[i++], snum);
+ }
+ }
+
+ ctr->share.info1005 = info1005;
+ break;
+ }
+
+ case 1006:
+ {
+ SRV_SHARE_INFO_1006 *info1006;
+ int i = 0;
+
+ info1006 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_1006));
+
+ for (snum = *resume_hnd; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+ init_srv_share_info_1006(p, &info1006[i++], snum);
+ }
+ }
+
+ ctr->share.info1006 = info1006;
+ break;
+ }
+
+ case 1007:
+ {
+ SRV_SHARE_INFO_1007 *info1007;
+ int i = 0;
+
+ info1007 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_1007));
+
+ for (snum = *resume_hnd; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+ init_srv_share_info_1007(p, &info1007[i++], snum);
+ }
+ }
+
+ ctr->share.info1007 = info1007;
+ break;
+ }
+
+ case 1501:
+ {
+ SRV_SHARE_INFO_1501 *info1501;
+ int i = 0;
+
+ info1501 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_1501));
+
+ for (snum = *resume_hnd; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+ init_srv_share_info_1501(p, &info1501[i++], snum);
+ }
+ }
+
+ ctr->share.info1501 = info1501;
+ break;
+ }
+ default:
+ DEBUG(5,("init_srv_share_info_ctr: unsupported switch value %d\n", info_level));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SRV_R_NET_SHARE_ENUM structure.
+********************************************************************/
+
+static void init_srv_r_net_share_enum(pipes_struct *p, SRV_R_NET_SHARE_ENUM *r_n,
+ uint32 info_level, uint32 resume_hnd, BOOL all)
+{
+ DEBUG(5,("init_srv_r_net_share_enum: %d\n", __LINE__));
+
+ if (init_srv_share_info_ctr(p, &r_n->ctr, info_level,
+ &resume_hnd, &r_n->total_entries, all)) {
+ r_n->status = WERR_OK;
+ } else {
+ r_n->status = WERR_UNKNOWN_LEVEL;
+ }
+
+ init_enum_hnd(&r_n->enum_hnd, resume_hnd);
+}
+
+/*******************************************************************
+ Inits a SRV_R_NET_SHARE_GET_INFO structure.
+********************************************************************/
+
+static void init_srv_r_net_share_get_info(pipes_struct *p, SRV_R_NET_SHARE_GET_INFO *r_n,
+ char *share_name, uint32 info_level)
+{
+ WERROR status = WERR_OK;
+ int snum;
+
+ DEBUG(5,("init_srv_r_net_share_get_info: %d\n", __LINE__));
+
+ r_n->info.switch_value = info_level;
+
+ snum = find_service(share_name);
+
+ if (snum >= 0) {
+ switch (info_level) {
+ case 0:
+ init_srv_share_info_0(p, &r_n->info.share.info0, snum);
+ break;
+ case 1:
+ init_srv_share_info_1(p, &r_n->info.share.info1, snum);
+ break;
+ case 2:
+ init_srv_share_info_2(p, &r_n->info.share.info2, snum);
+ break;
+ case 501:
+ init_srv_share_info_501(p, &r_n->info.share.info501, snum);
+ break;
+ case 502:
+ init_srv_share_info_502(p, &r_n->info.share.info502, snum);
+ break;
+
+ /* here for completeness */
+ case 1004:
+ init_srv_share_info_1004(p, &r_n->info.share.info1004, snum);
+ break;
+ case 1005:
+ init_srv_share_info_1005(p, &r_n->info.share.info1005, snum);
+ break;
+
+ /* here for completeness 1006 - 1501 */
+ case 1006:
+ init_srv_share_info_1006(p, &r_n->info.share.info1006, snum);
+ break;
+ case 1007:
+ init_srv_share_info_1007(p, &r_n->info.share.info1007, snum);
+ break;
+ case 1501:
+ init_srv_share_info_1501(p, &r_n->info.share.info1501, snum);
+ break;
+ default:
+ DEBUG(5,("init_srv_net_share_get_info: unsupported switch value %d\n", info_level));
+ status = WERR_UNKNOWN_LEVEL;
+ break;
+ }
+ } else {
+ status = WERR_INVALID_NAME;
+ }
+
+ r_n->info.ptr_share_ctr = W_ERROR_IS_OK(status) ? 1 : 0;
+ r_n->status = status;
+}
+
+/*******************************************************************
+ fill in a sess info level 1 structure.
+ ********************************************************************/
+
+static void init_srv_sess_0_info(SESS_INFO_0 *se0, SESS_INFO_0_STR *str0, char *name)
+{
+ init_srv_sess_info0(se0, name);
+ init_srv_sess_info0_str(str0, name);
+}
+
+/*******************************************************************
+ fill in a sess info level 0 structure.
+ ********************************************************************/
+
+static void init_srv_sess_info_0(SRV_SESS_INFO_0 *ss0, uint32 *snum, uint32 *stot)
+{
+ struct sessionid *session_list;
+ uint32 num_entries = 0;
+ (*stot) = list_sessions(&session_list);
+
+ if (ss0 == NULL) {
+ (*snum) = 0;
+ SAFE_FREE(session_list);
+ return;
+ }
+
+ DEBUG(5,("init_srv_sess_0_ss0\n"));
+
+ if (snum) {
+ for (; (*snum) < (*stot) && num_entries < MAX_SESS_ENTRIES; (*snum)++) {
+ init_srv_sess_0_info(&ss0->info_0[num_entries],
+ &ss0->info_0_str[num_entries], session_list[(*snum)].remote_machine);
+
+ /* move on to creating next session */
+ /* move on to creating next sess */
+ num_entries++;
+ }
+
+ ss0->num_entries_read = num_entries;
+ ss0->ptr_sess_info = num_entries > 0 ? 1 : 0;
+ ss0->num_entries_read2 = num_entries;
+
+ if ((*snum) >= (*stot)) {
+ (*snum) = 0;
+ }
+
+ } else {
+ ss0->num_entries_read = 0;
+ ss0->ptr_sess_info = 0;
+ ss0->num_entries_read2 = 0;
+ }
+ SAFE_FREE(session_list);
+}
+
+/*******************************************************************
+ fill in a sess info level 1 structure.
+ ********************************************************************/
+
+static void init_srv_sess_1_info(SESS_INFO_1 *se1, SESS_INFO_1_STR *str1,
+ char *name, char *user,
+ uint32 num_opens,
+ uint32 open_time, uint32 idle_time,
+ uint32 usr_flgs)
+{
+ init_srv_sess_info1(se1 , name, user, num_opens, open_time, idle_time, usr_flgs);
+ init_srv_sess_info1_str(str1, name, user);
+}
+
+/*******************************************************************
+ fill in a sess info level 1 structure.
+ ********************************************************************/
+
+static void init_srv_sess_info_1(SRV_SESS_INFO_1 *ss1, uint32 *snum, uint32 *stot)
+{
+ struct sessionid *session_list;
+ uint32 num_entries = 0;
+ (*stot) = list_sessions(&session_list);
+
+ if (ss1 == NULL) {
+ (*snum) = 0;
+ SAFE_FREE(session_list);
+ return;
+ }
+
+ DEBUG(5,("init_srv_sess_1_ss1\n"));
+
+ if (snum) {
+ for (; (*snum) < (*stot) && num_entries < MAX_SESS_ENTRIES; (*snum)++) {
+ init_srv_sess_1_info(&ss1->info_1[num_entries],
+ &ss1->info_1_str[num_entries],
+ session_list[*snum].remote_machine,
+ session_list[*snum].username,
+ 1, 10, 5, 0);
+
+ /* move on to creating next session */
+ /* move on to creating next sess */
+ num_entries++;
+ }
+
+ ss1->num_entries_read = num_entries;
+ ss1->ptr_sess_info = num_entries > 0 ? 1 : 0;
+ ss1->num_entries_read2 = num_entries;
+
+ if ((*snum) >= (*stot)) {
+ (*snum) = 0;
+ }
+
+ } else {
+ ss1->num_entries_read = 0;
+ ss1->ptr_sess_info = 0;
+ ss1->num_entries_read2 = 0;
+
+ (*stot) = 0;
+ }
+}
+
+/*******************************************************************
+ makes a SRV_R_NET_SESS_ENUM structure.
+********************************************************************/
+
+static WERROR init_srv_sess_info_ctr(SRV_SESS_INFO_CTR *ctr,
+ int switch_value, uint32 *resume_hnd, uint32 *total_entries)
+{
+ WERROR status = WERR_OK;
+ DEBUG(5,("init_srv_sess_info_ctr: %d\n", __LINE__));
+
+ ctr->switch_value = switch_value;
+
+ switch (switch_value) {
+ case 0:
+ init_srv_sess_info_0(&(ctr->sess.info0), resume_hnd, total_entries);
+ ctr->ptr_sess_ctr = 1;
+ break;
+ case 1:
+ init_srv_sess_info_1(&(ctr->sess.info1), resume_hnd, total_entries);
+ ctr->ptr_sess_ctr = 1;
+ break;
+ default:
+ DEBUG(5,("init_srv_sess_info_ctr: unsupported switch value %d\n", switch_value));
+ (*resume_hnd) = 0;
+ (*total_entries) = 0;
+ ctr->ptr_sess_ctr = 0;
+ status = WERR_UNKNOWN_LEVEL;
+ break;
+ }
+
+ return status;
+}
+
+/*******************************************************************
+ makes a SRV_R_NET_SESS_ENUM structure.
+********************************************************************/
+
+static void init_srv_r_net_sess_enum(SRV_R_NET_SESS_ENUM *r_n,
+ uint32 resume_hnd, int sess_level, int switch_value)
+{
+ DEBUG(5,("init_srv_r_net_sess_enum: %d\n", __LINE__));
+
+ r_n->sess_level = sess_level;
+
+ if (sess_level == -1)
+ r_n->status = WERR_UNKNOWN_LEVEL;
+ else
+ r_n->status = init_srv_sess_info_ctr(r_n->ctr, switch_value, &resume_hnd, &r_n->total_entries);
+
+ if (!W_ERROR_IS_OK(r_n->status))
+ resume_hnd = 0;
+
+ init_enum_hnd(&r_n->enum_hnd, resume_hnd);
+}
+
+/*******************************************************************
+ fill in a conn info level 0 structure.
+ ********************************************************************/
+
+static void init_srv_conn_info_0(SRV_CONN_INFO_0 *ss0, uint32 *snum, uint32 *stot)
+{
+ uint32 num_entries = 0;
+ (*stot) = 1;
+
+ if (ss0 == NULL) {
+ (*snum) = 0;
+ return;
+ }
+
+ DEBUG(5,("init_srv_conn_0_ss0\n"));
+
+ if (snum) {
+ for (; (*snum) < (*stot) && num_entries < MAX_CONN_ENTRIES; (*snum)++) {
+
+ init_srv_conn_info0(&ss0->info_0[num_entries], (*stot));
+
+ /* move on to creating next connection */
+ /* move on to creating next conn */
+ num_entries++;
+ }
+
+ ss0->num_entries_read = num_entries;
+ ss0->ptr_conn_info = num_entries > 0 ? 1 : 0;
+ ss0->num_entries_read2 = num_entries;
+
+ if ((*snum) >= (*stot)) {
+ (*snum) = 0;
+ }
+
+ } else {
+ ss0->num_entries_read = 0;
+ ss0->ptr_conn_info = 0;
+ ss0->num_entries_read2 = 0;
+
+ (*stot) = 0;
+ }
+}
+
+/*******************************************************************
+ fill in a conn info level 1 structure.
+ ********************************************************************/
+
+static void init_srv_conn_1_info(CONN_INFO_1 *se1, CONN_INFO_1_STR *str1,
+ uint32 id, uint32 type,
+ uint32 num_opens, uint32 num_users, uint32 open_time,
+ const char *usr_name, const char *net_name)
+{
+ init_srv_conn_info1(se1 , id, type, num_opens, num_users, open_time, usr_name, net_name);
+ init_srv_conn_info1_str(str1, usr_name, net_name);
+}
+
+/*******************************************************************
+ fill in a conn info level 1 structure.
+ ********************************************************************/
+
+static void init_srv_conn_info_1(SRV_CONN_INFO_1 *ss1, uint32 *snum, uint32 *stot)
+{
+ uint32 num_entries = 0;
+ (*stot) = 1;
+
+ if (ss1 == NULL) {
+ (*snum) = 0;
+ return;
+ }
+
+ DEBUG(5,("init_srv_conn_1_ss1\n"));
+
+ if (snum) {
+ for (; (*snum) < (*stot) && num_entries < MAX_CONN_ENTRIES; (*snum)++) {
+ init_srv_conn_1_info(&ss1->info_1[num_entries],
+ &ss1->info_1_str[num_entries],
+ (*stot), 0x3, 1, 1, 3,"dummy_user", "IPC$");
+
+ /* move on to creating next connection */
+ /* move on to creating next conn */
+ num_entries++;
+ }
+
+ ss1->num_entries_read = num_entries;
+ ss1->ptr_conn_info = num_entries > 0 ? 1 : 0;
+ ss1->num_entries_read2 = num_entries;
+
+
+ if ((*snum) >= (*stot)) {
+ (*snum) = 0;
+ }
+
+ } else {
+ ss1->num_entries_read = 0;
+ ss1->ptr_conn_info = 0;
+ ss1->num_entries_read2 = 0;
+
+ (*stot) = 0;
+ }
+}
+
+/*******************************************************************
+ makes a SRV_R_NET_CONN_ENUM structure.
+********************************************************************/
+
+static WERROR init_srv_conn_info_ctr(SRV_CONN_INFO_CTR *ctr,
+ int switch_value, uint32 *resume_hnd, uint32 *total_entries)
+{
+ WERROR status = WERR_OK;
+ DEBUG(5,("init_srv_conn_info_ctr: %d\n", __LINE__));
+
+ ctr->switch_value = switch_value;
+
+ switch (switch_value) {
+ case 0:
+ init_srv_conn_info_0(&ctr->conn.info0, resume_hnd, total_entries);
+ ctr->ptr_conn_ctr = 1;
+ break;
+ case 1:
+ init_srv_conn_info_1(&ctr->conn.info1, resume_hnd, total_entries);
+ ctr->ptr_conn_ctr = 1;
+ break;
+ default:
+ DEBUG(5,("init_srv_conn_info_ctr: unsupported switch value %d\n", switch_value));
+ (*resume_hnd = 0);
+ (*total_entries) = 0;
+ ctr->ptr_conn_ctr = 0;
+ status = WERR_UNKNOWN_LEVEL;
+ break;
+ }
+
+ return status;
+}
+
+/*******************************************************************
+ makes a SRV_R_NET_CONN_ENUM structure.
+********************************************************************/
+
+static void init_srv_r_net_conn_enum(SRV_R_NET_CONN_ENUM *r_n,
+ uint32 resume_hnd, int conn_level, int switch_value)
+{
+ DEBUG(5,("init_srv_r_net_conn_enum: %d\n", __LINE__));
+
+ r_n->conn_level = conn_level;
+ if (conn_level == -1)
+ r_n->status = WERR_UNKNOWN_LEVEL;
+ else
+ r_n->status = init_srv_conn_info_ctr(r_n->ctr, switch_value, &resume_hnd, &r_n->total_entries);
+
+ if (!W_ERROR_IS_OK(r_n->status))
+ resume_hnd = 0;
+
+ init_enum_hnd(&r_n->enum_hnd, resume_hnd);
+}
+
+/*******************************************************************
+ makes a SRV_R_NET_FILE_ENUM structure.
+********************************************************************/
+
+static WERROR init_srv_file_info_ctr(pipes_struct *p, SRV_FILE_INFO_CTR *ctr,
+ int switch_value, uint32 *resume_hnd,
+ uint32 *total_entries)
+{
+ WERROR status = WERR_OK;
+ TALLOC_CTX *ctx = p->mem_ctx;
+ DEBUG(5,("init_srv_file_info_ctr: %d\n", __LINE__));
+ *total_entries = 1; /* dummy entries only, for */
+
+ ctr->switch_value = switch_value;
+ ctr->num_entries = *total_entries - *resume_hnd;
+ ctr->num_entries2 = ctr->num_entries;
+
+ switch (switch_value) {
+ case 3: {
+ int i;
+ if (*total_entries > 0) {
+ ctr->ptr_entries = 1;
+ ctr->file.info3 = talloc(ctx, ctr->num_entries *
+ sizeof(SRV_FILE_INFO_3));
+ }
+ for (i=0 ;i<ctr->num_entries;i++) {
+ init_srv_file_info3(&ctr->file.info3[i].info_3, i+*resume_hnd, 0x35, 0, "\\PIPE\\samr", "dummy user");
+ init_srv_file_info3_str(&ctr->file.info3[i].info_3_str, "\\PIPE\\samr", "dummy user");
+
+ }
+ ctr->ptr_file_info = 1;
+ *resume_hnd = 0;
+ break;
+ }
+ default:
+ DEBUG(5,("init_srv_file_info_ctr: unsupported switch value %d\n", switch_value));
+ (*resume_hnd = 0);
+ (*total_entries) = 0;
+ ctr->ptr_entries = 0;
+ status = WERR_UNKNOWN_LEVEL;
+ break;
+ }
+
+ return status;
+}
+
+/*******************************************************************
+ makes a SRV_R_NET_FILE_ENUM structure.
+********************************************************************/
+
+static void init_srv_r_net_file_enum(pipes_struct *p, SRV_R_NET_FILE_ENUM *r_n,
+ uint32 resume_hnd, int file_level, int switch_value)
+{
+ DEBUG(5,("init_srv_r_net_file_enum: %d\n", __LINE__));
+
+ r_n->file_level = file_level;
+ if (file_level == 0)
+ r_n->status = WERR_UNKNOWN_LEVEL;
+ else
+ r_n->status = init_srv_file_info_ctr(p, &r_n->ctr, switch_value, &resume_hnd, &(r_n->total_entries));
+
+ if (!W_ERROR_IS_OK(r_n->status))
+ resume_hnd = 0;
+
+ init_enum_hnd(&r_n->enum_hnd, resume_hnd);
+}
+
+/*******************************************************************
+net server get info
+********************************************************************/
+
+WERROR _srv_net_srv_get_info(pipes_struct *p, SRV_Q_NET_SRV_GET_INFO *q_u, SRV_R_NET_SRV_GET_INFO *r_u)
+{
+ WERROR status = WERR_OK;
+ SRV_INFO_CTR *ctr = (SRV_INFO_CTR *)talloc(p->mem_ctx, sizeof(SRV_INFO_CTR));
+
+ if (!ctr)
+ return WERR_NOMEM;
+
+ ZERO_STRUCTP(ctr);
+
+ DEBUG(5,("srv_net_srv_get_info: %d\n", __LINE__));
+
+ if (!pipe_access_check(p)) {
+ DEBUG(3, ("access denied to srv_net_srv_get_info\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ switch (q_u->switch_value) {
+
+ /* Technically level 102 should only be available to
+ Administrators but there isn't anything super-secret
+ here, as most of it is made up. */
+
+ case 102:
+ init_srv_info_102(&ctr->srv.sv102,
+ 500, global_myname(),
+ string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH),
+ lp_major_announce_version(), lp_minor_announce_version(),
+ lp_default_server_announce(),
+ 0xffffffff, /* users */
+ 0xf, /* disc */
+ 0, /* hidden */
+ 240, /* announce */
+ 3000, /* announce delta */
+ 100000, /* licenses */
+ "c:\\"); /* user path */
+ break;
+ case 101:
+ init_srv_info_101(&ctr->srv.sv101,
+ 500, global_myname(),
+ lp_major_announce_version(), lp_minor_announce_version(),
+ lp_default_server_announce(),
+ string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH));
+ break;
+ case 100:
+ init_srv_info_100(&ctr->srv.sv100, 500, global_myname());
+ break;
+ default:
+ status = WERR_UNKNOWN_LEVEL;
+ break;
+ }
+
+ /* set up the net server get info structure */
+ init_srv_r_net_srv_get_info(r_u, q_u->switch_value, ctr, status);
+
+ DEBUG(5,("srv_net_srv_get_info: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+net server set info
+********************************************************************/
+
+WERROR _srv_net_srv_set_info(pipes_struct *p, SRV_Q_NET_SRV_SET_INFO *q_u, SRV_R_NET_SRV_SET_INFO *r_u)
+{
+ WERROR status = WERR_OK;
+
+ DEBUG(5,("srv_net_srv_set_info: %d\n", __LINE__));
+
+ /* Set up the net server set info structure. */
+
+ init_srv_r_net_srv_set_info(r_u, 0x0, status);
+
+ DEBUG(5,("srv_net_srv_set_info: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+net file enum
+********************************************************************/
+
+WERROR _srv_net_file_enum(pipes_struct *p, SRV_Q_NET_FILE_ENUM *q_u, SRV_R_NET_FILE_ENUM *r_u)
+{
+ DEBUG(5,("srv_net_file_enum: %d\n", __LINE__));
+
+ /* set up the */
+ init_srv_r_net_file_enum(p, r_u,
+ get_enum_hnd(&q_u->enum_hnd),
+ q_u->file_level,
+ q_u->ctr.switch_value);
+
+ DEBUG(5,("srv_net_file_enum: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+net conn enum
+********************************************************************/
+
+WERROR _srv_net_conn_enum(pipes_struct *p, SRV_Q_NET_CONN_ENUM *q_u, SRV_R_NET_CONN_ENUM *r_u)
+{
+ DEBUG(5,("srv_net_conn_enum: %d\n", __LINE__));
+
+ r_u->ctr = (SRV_CONN_INFO_CTR *)talloc(p->mem_ctx, sizeof(SRV_CONN_INFO_CTR));
+ if (!r_u->ctr)
+ return WERR_NOMEM;
+
+ ZERO_STRUCTP(r_u->ctr);
+
+ /* set up the */
+ init_srv_r_net_conn_enum(r_u,
+ get_enum_hnd(&q_u->enum_hnd),
+ q_u->conn_level,
+ q_u->ctr->switch_value);
+
+ DEBUG(5,("srv_net_conn_enum: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+net sess enum
+********************************************************************/
+
+WERROR _srv_net_sess_enum(pipes_struct *p, SRV_Q_NET_SESS_ENUM *q_u, SRV_R_NET_SESS_ENUM *r_u)
+{
+ DEBUG(5,("_srv_net_sess_enum: %d\n", __LINE__));
+
+ r_u->ctr = (SRV_SESS_INFO_CTR *)talloc(p->mem_ctx, sizeof(SRV_SESS_INFO_CTR));
+ if (!r_u->ctr)
+ return WERR_NOMEM;
+
+ ZERO_STRUCTP(r_u->ctr);
+
+ /* set up the */
+ init_srv_r_net_sess_enum(r_u,
+ get_enum_hnd(&q_u->enum_hnd),
+ q_u->sess_level,
+ q_u->ctr->switch_value);
+
+ DEBUG(5,("_srv_net_sess_enum: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ Net share enum all.
+********************************************************************/
+
+WERROR _srv_net_share_enum_all(pipes_struct *p, SRV_Q_NET_SHARE_ENUM *q_u, SRV_R_NET_SHARE_ENUM *r_u)
+{
+ DEBUG(5,("_srv_net_share_enum: %d\n", __LINE__));
+
+ if (!pipe_access_check(p)) {
+ DEBUG(3, ("access denied to srv_net_share_enum_all\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* Create the list of shares for the response. */
+ init_srv_r_net_share_enum(p, r_u,
+ q_u->ctr.info_level,
+ get_enum_hnd(&q_u->enum_hnd), True);
+
+ DEBUG(5,("_srv_net_share_enum: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ Net share enum.
+********************************************************************/
+
+WERROR _srv_net_share_enum(pipes_struct *p, SRV_Q_NET_SHARE_ENUM *q_u, SRV_R_NET_SHARE_ENUM *r_u)
+{
+ DEBUG(5,("_srv_net_share_enum: %d\n", __LINE__));
+
+ if (!pipe_access_check(p)) {
+ DEBUG(3, ("access denied to srv_net_share_enum\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* Create the list of shares for the response. */
+ init_srv_r_net_share_enum(p, r_u,
+ q_u->ctr.info_level,
+ get_enum_hnd(&q_u->enum_hnd), False);
+
+ DEBUG(5,("_srv_net_share_enum: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ Net share get info.
+********************************************************************/
+
+WERROR _srv_net_share_get_info(pipes_struct *p, SRV_Q_NET_SHARE_GET_INFO *q_u, SRV_R_NET_SHARE_GET_INFO *r_u)
+{
+ fstring share_name;
+
+ DEBUG(5,("_srv_net_share_get_info: %d\n", __LINE__));
+
+ /* Create the list of shares for the response. */
+ unistr2_to_ascii(share_name, &q_u->uni_share_name, sizeof(share_name));
+ init_srv_r_net_share_get_info(p, r_u, share_name, q_u->info_level);
+
+ DEBUG(5,("_srv_net_share_get_info: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ Check a given DOS pathname is valid for a share.
+********************************************************************/
+
+static char *valid_share_pathname(char *dos_pathname)
+{
+ char *ptr;
+
+ /* Convert any '\' paths to '/' */
+ unix_format(dos_pathname);
+ unix_clean_name(dos_pathname);
+
+ /* NT is braindead - it wants a C: prefix to a pathname ! So strip it. */
+ ptr = dos_pathname;
+ if (strlen(dos_pathname) > 2 && ptr[1] == ':' && ptr[0] != '/')
+ ptr += 2;
+
+ /* Only abolute paths allowed. */
+ if (*ptr != '/')
+ return NULL;
+
+ return ptr;
+}
+
+static BOOL exist_share_pathname(char *unix_pathname)
+{
+ pstring saved_pathname;
+ int ret;
+
+ /* Can we cd to it ? */
+
+ /* First save our current directory. */
+ if (getcwd(saved_pathname, sizeof(saved_pathname)) == NULL)
+ return False;
+
+ ret = chdir(unix_pathname);
+
+ /* We *MUST* be able to chdir back. Abort if we can't. */
+ if (chdir(saved_pathname) == -1)
+ smb_panic("valid_share_pathname: Unable to restore current directory.\n");
+
+ if (ret == -1) return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Net share set info. Modify share details.
+********************************************************************/
+
+WERROR _srv_net_share_set_info(pipes_struct *p, SRV_Q_NET_SHARE_SET_INFO *q_u, SRV_R_NET_SHARE_SET_INFO *r_u)
+{
+ struct current_user user;
+ pstring command;
+ fstring share_name;
+ fstring comment;
+ pstring pathname;
+ int type;
+ int snum;
+ int ret;
+ char *path;
+ SEC_DESC *psd = NULL;
+
+ DEBUG(5,("_srv_net_share_set_info: %d\n", __LINE__));
+
+ unistr2_to_ascii(share_name, &q_u->uni_share_name, sizeof(share_name));
+
+ r_u->parm_error = 0;
+
+ if (strequal(share_name,"IPC$") || strequal(share_name,"ADMIN$") || strequal(share_name,"global"))
+ return WERR_ACCESS_DENIED;
+
+ snum = find_service(share_name);
+
+ /* Does this share exist ? */
+ if (snum < 0)
+ return WERR_INVALID_NAME;
+
+ /* No change to printer shares. */
+ if (lp_print_ok(snum))
+ return WERR_ACCESS_DENIED;
+
+ get_current_user(&user,p);
+
+ if (user.uid != sec_initial_uid())
+ return WERR_ACCESS_DENIED;
+
+ switch (q_u->info_level) {
+ case 1:
+ pstrcpy(pathname, lp_pathname(snum));
+ unistr2_to_ascii(comment, &q_u->info.share.info2.info_2_str.uni_remark, sizeof(comment));
+ type = q_u->info.share.info2.info_2.type;
+ psd = NULL;
+ break;
+ case 2:
+ unistr2_to_ascii(comment, &q_u->info.share.info2.info_2_str.uni_remark, sizeof(comment));
+ unistr2_to_ascii(pathname, &q_u->info.share.info2.info_2_str.uni_path, sizeof(pathname));
+ type = q_u->info.share.info2.info_2.type;
+ psd = NULL;
+ break;
+#if 0
+ /* not supported on set but here for completeness */
+ case 501:
+ unistr2_to_ascii(comment, &q_u->info.share.info501.info_501_str.uni_remark, sizeof(comment));
+ type = q_u->info.share.info501.info_501.type;
+ psd = NULL;
+ break;
+#endif
+ case 502:
+ unistr2_to_ascii(comment, &q_u->info.share.info502.info_502_str.uni_remark, sizeof(comment));
+ unistr2_to_ascii(pathname, &q_u->info.share.info502.info_502_str.uni_path, sizeof(pathname));
+ type = q_u->info.share.info502.info_502.type;
+ psd = q_u->info.share.info502.info_502_str.sd;
+ map_generic_share_sd_bits(psd);
+ break;
+ case 1004:
+ pstrcpy(pathname, lp_pathname(snum));
+ unistr2_to_ascii(comment, &q_u->info.share.info1004.info_1004_str.uni_remark, sizeof(comment));
+ type = STYPE_DISKTREE;
+ break;
+ case 1005:
+ /* XP re-sets the csc policy even if it wasn't changed by the
+ user, so we must compare it to see if it's what is set in
+ smb.conf, so that we can contine other ops like setting
+ ACLs on a share */
+ if (((q_u->info.share.info1005.share_info_flags &
+ SHARE_1005_CSC_POLICY_MASK) >>
+ SHARE_1005_CSC_POLICY_SHIFT) == lp_csc_policy(snum))
+ return WERR_OK;
+ else {
+ DEBUG(3, ("_srv_net_share_set_info: client is trying to change csc policy from the network; must be done with smb.conf\n"));
+ return WERR_ACCESS_DENIED;
+ }
+ break;
+ case 1006:
+ case 1007:
+ return WERR_ACCESS_DENIED;
+ break;
+ case 1501:
+ pstrcpy(pathname, lp_pathname(snum));
+ fstrcpy(comment, lp_comment(snum));
+ psd = q_u->info.share.info1501.sdb->sec;
+ map_generic_share_sd_bits(psd);
+ type = STYPE_DISKTREE;
+ break;
+ default:
+ DEBUG(5,("_srv_net_share_set_info: unsupported switch value %d\n", q_u->info_level));
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ /* We can only modify disk shares. */
+ if (type != STYPE_DISKTREE)
+ return WERR_ACCESS_DENIED;
+
+ /* Check if the pathname is valid. */
+ if (!(path = valid_share_pathname( pathname )))
+ return WERR_OBJECT_PATH_INVALID;
+
+ /* Ensure share name, pathname and comment don't contain '"' characters. */
+ string_replace(share_name, '"', ' ');
+ string_replace(path, '"', ' ');
+ string_replace(comment, '"', ' ');
+
+ DEBUG(10,("_srv_net_share_set_info: change share command = %s\n",
+ lp_change_share_cmd() ? lp_change_share_cmd() : "NULL" ));
+
+ /* Only call modify function if something changed. */
+
+ if (strcmp(path, lp_pathname(snum)) || strcmp(comment, lp_comment(snum)) ) {
+ if (!lp_change_share_cmd() || !*lp_change_share_cmd())
+ return WERR_ACCESS_DENIED;
+
+ slprintf(command, sizeof(command)-1, "%s \"%s\" \"%s\" \"%s\" \"%s\"",
+ lp_change_share_cmd(), dyn_CONFIGFILE, share_name, path, comment);
+
+ DEBUG(10,("_srv_net_share_set_info: Running [%s]\n", command ));
+ if ((ret = smbrun(command, NULL)) != 0) {
+ DEBUG(0,("_srv_net_share_set_info: Running [%s] returned (%d)\n", command, ret ));
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* Check if the new share pathname exist, if not return an error */
+ if (!exist_share_pathname(path)) {
+ DEBUG(1, ("_srv_net_share_set_info: change share command was ok but path (%s) has not been created!\n", path));
+ return WERR_OBJECT_PATH_INVALID;
+ }
+
+ /* Tell everyone we updated smb.conf. */
+ message_send_all(conn_tdb_ctx(), MSG_SMB_CONF_UPDATED, NULL, 0, False, NULL);
+
+ } else {
+ DEBUG(10,("_srv_net_share_set_info: No change to share name (%s)\n", share_name ));
+ }
+
+ /* Replace SD if changed. */
+ if (psd) {
+ SEC_DESC *old_sd;
+ size_t sd_size;
+
+ old_sd = get_share_security(p->mem_ctx, snum, &sd_size);
+
+ if (old_sd && !sec_desc_equal(old_sd, psd)) {
+ if (!set_share_security(p->mem_ctx, share_name, psd))
+ DEBUG(0,("_srv_net_share_set_info: Failed to change security info in share %s.\n",
+ share_name ));
+ }
+ }
+
+ DEBUG(5,("_srv_net_share_set_info: %d\n", __LINE__));
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ Net share add. Call 'add_share_command "sharename" "pathname" "comment" "read only = xxx"'
+********************************************************************/
+
+WERROR _srv_net_share_add(pipes_struct *p, SRV_Q_NET_SHARE_ADD *q_u, SRV_R_NET_SHARE_ADD *r_u)
+{
+ struct current_user user;
+ pstring command;
+ fstring share_name;
+ fstring comment;
+ pstring pathname;
+ int type;
+ int snum;
+ int ret;
+ char *path;
+ SEC_DESC *psd = NULL;
+
+ DEBUG(5,("_srv_net_share_add: %d\n", __LINE__));
+
+ r_u->parm_error = 0;
+
+ get_current_user(&user,p);
+
+ if (user.uid != sec_initial_uid()) {
+ DEBUG(10,("_srv_net_share_add: uid != sec_initial_uid(). Access denied.\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ if (!lp_add_share_cmd() || !*lp_add_share_cmd()) {
+ DEBUG(10,("_srv_net_share_add: No add share command\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ switch (q_u->info_level) {
+ case 0:
+ /* No path. Not enough info in a level 0 to do anything. */
+ return WERR_ACCESS_DENIED;
+ case 1:
+ /* Not enough info in a level 1 to do anything. */
+ return WERR_ACCESS_DENIED;
+ case 2:
+ unistr2_to_ascii(share_name, &q_u->info.share.info2.info_2_str.uni_netname, sizeof(share_name));
+ unistr2_to_ascii(comment, &q_u->info.share.info2.info_2_str.uni_remark, sizeof(share_name));
+ unistr2_to_ascii(pathname, &q_u->info.share.info2.info_2_str.uni_path, sizeof(share_name));
+ type = q_u->info.share.info2.info_2.type;
+ break;
+ case 501:
+ /* No path. Not enough info in a level 501 to do anything. */
+ return WERR_ACCESS_DENIED;
+ case 502:
+ unistr2_to_ascii(share_name, &q_u->info.share.info502.info_502_str.uni_netname, sizeof(share_name));
+ unistr2_to_ascii(comment, &q_u->info.share.info502.info_502_str.uni_remark, sizeof(share_name));
+ unistr2_to_ascii(pathname, &q_u->info.share.info502.info_502_str.uni_path, sizeof(share_name));
+ type = q_u->info.share.info502.info_502.type;
+ psd = q_u->info.share.info502.info_502_str.sd;
+ map_generic_share_sd_bits(psd);
+ break;
+
+ /* none of the following contain share names. NetShareAdd does not have a separate parameter for the share name */
+
+ case 1004:
+ case 1005:
+ case 1006:
+ case 1007:
+ return WERR_ACCESS_DENIED;
+ break;
+ case 1501:
+ /* DFS only level. */
+ return WERR_ACCESS_DENIED;
+ default:
+ DEBUG(5,("_srv_net_share_add: unsupported switch value %d\n", q_u->info_level));
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ if (strequal(share_name,"IPC$") || strequal(share_name,"ADMIN$") || strequal(share_name,"global"))
+ return WERR_ACCESS_DENIED;
+
+ snum = find_service(share_name);
+
+ /* Share already exists. */
+ if (snum >= 0)
+ return WERR_ALREADY_EXISTS;
+
+ /* We can only add disk shares. */
+ if (type != STYPE_DISKTREE)
+ return WERR_ACCESS_DENIED;
+
+ /* Check if the pathname is valid. */
+ if (!(path = valid_share_pathname( pathname )))
+ return WERR_OBJECT_PATH_INVALID;
+
+ /* Ensure share name, pathname and comment don't contain '"' characters. */
+ string_replace(share_name, '"', ' ');
+ string_replace(path, '"', ' ');
+ string_replace(comment, '"', ' ');
+
+ slprintf(command, sizeof(command)-1, "%s \"%s\" \"%s\" \"%s\" \"%s\"",
+ lp_add_share_cmd(), dyn_CONFIGFILE, share_name, path, comment);
+
+ DEBUG(10,("_srv_net_share_add: Running [%s]\n", command ));
+ if ((ret = smbrun(command, NULL)) != 0) {
+ DEBUG(0,("_srv_net_share_add: Running [%s] returned (%d)\n", command, ret ));
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* Check if the new share pathname exist, if not try to delete the
+ * share and return an error */
+ if (!exist_share_pathname(path)) {
+ DEBUG(1, ("_srv_net_share_add: add share command was ok but path (%s) has not been created!\n", path));
+ DEBUG(1, ("_srv_net_share_add: trying to rollback and delete the share\n"));
+
+ if (!lp_delete_share_cmd() || !*lp_delete_share_cmd()) {
+ DEBUG(1, ("_srv_net_share_add: Error! delete share command is not defined! Please check share (%s) in the config file\n", share_name));
+ return WERR_OBJECT_PATH_INVALID;
+ }
+
+ slprintf(command, sizeof(command)-1, "%s \"%s\" \"%s\"",
+ lp_delete_share_cmd(), dyn_CONFIGFILE, share_name);
+
+ DEBUG(10,("_srv_net_share_add: Running [%s]\n", command ));
+ if ((ret = smbrun(command, NULL)) != 0) {
+ DEBUG(0,("_srv_net_share_add: Running [%s] returned (%d)\n", command, ret ));
+ DEBUG(1, ("_srv_net_share_add: Error! delete share command failed! Please check share (%s) in the config file\n", share_name));
+ }
+
+ return WERR_OBJECT_PATH_INVALID;
+ }
+
+ if (psd) {
+ if (!set_share_security(p->mem_ctx, share_name, psd)) {
+ DEBUG(0,("_srv_net_share_add: Failed to add security info to share %s.\n", share_name ));
+ }
+ }
+
+ /* Tell everyone we updated smb.conf. */
+ message_send_all(conn_tdb_ctx(), MSG_SMB_CONF_UPDATED, NULL, 0, False, NULL);
+
+ /*
+ * We don't call reload_services() here, the message will
+ * cause this to be done before the next packet is read
+ * from the client. JRA.
+ */
+
+ DEBUG(5,("_srv_net_share_add: %d\n", __LINE__));
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ Net share delete. Call "delete share command" with the share name as
+ a parameter.
+********************************************************************/
+
+WERROR _srv_net_share_del(pipes_struct *p, SRV_Q_NET_SHARE_DEL *q_u, SRV_R_NET_SHARE_DEL *r_u)
+{
+ struct current_user user;
+ pstring command;
+ fstring share_name;
+ int ret;
+ int snum;
+
+ DEBUG(5,("_srv_net_share_del: %d\n", __LINE__));
+
+ unistr2_to_ascii(share_name, &q_u->uni_share_name, sizeof(share_name));
+
+ if (strequal(share_name,"IPC$") || strequal(share_name,"ADMIN$") || strequal(share_name,"global"))
+ return WERR_ACCESS_DENIED;
+
+ snum = find_service(share_name);
+
+ if (snum < 0)
+ return WERR_NO_SUCH_SHARE;
+
+ /* No change to printer shares. */
+ if (lp_print_ok(snum))
+ return WERR_ACCESS_DENIED;
+
+ get_current_user(&user,p);
+
+ if (user.uid != sec_initial_uid())
+ return WERR_ACCESS_DENIED;
+
+ if (!lp_delete_share_cmd() || !*lp_delete_share_cmd())
+ return WERR_ACCESS_DENIED;
+
+ slprintf(command, sizeof(command)-1, "%s \"%s\" \"%s\"",
+ lp_delete_share_cmd(), dyn_CONFIGFILE, lp_servicename(snum));
+
+ DEBUG(10,("_srv_net_share_del: Running [%s]\n", command ));
+ if ((ret = smbrun(command, NULL)) != 0) {
+ DEBUG(0,("_srv_net_share_del: Running [%s] returned (%d)\n", command, ret ));
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* Delete the SD in the database. */
+ delete_share_security(snum);
+
+ /* Tell everyone we updated smb.conf. */
+ message_send_all(conn_tdb_ctx(), MSG_SMB_CONF_UPDATED, NULL, 0, False, NULL);
+
+ lp_killservice(snum);
+
+ return WERR_OK;
+}
+
+WERROR _srv_net_share_del_sticky(pipes_struct *p, SRV_Q_NET_SHARE_DEL *q_u, SRV_R_NET_SHARE_DEL *r_u)
+{
+ DEBUG(5,("_srv_net_share_del_stick: %d\n", __LINE__));
+
+ return _srv_net_share_del(p, q_u, r_u);
+}
+
+/*******************************************************************
+time of day
+********************************************************************/
+
+WERROR _srv_net_remote_tod(pipes_struct *p, SRV_Q_NET_REMOTE_TOD *q_u, SRV_R_NET_REMOTE_TOD *r_u)
+{
+ TIME_OF_DAY_INFO *tod;
+ struct tm *t;
+ time_t unixdate = time(NULL);
+
+ tod = (TIME_OF_DAY_INFO *)talloc(p->mem_ctx, sizeof(TIME_OF_DAY_INFO));
+ if (!tod)
+ return WERR_NOMEM;
+
+ ZERO_STRUCTP(tod);
+
+ r_u->tod = tod;
+ r_u->ptr_srv_tod = 0x1;
+ r_u->status = WERR_OK;
+
+ DEBUG(5,("_srv_net_remote_tod: %d\n", __LINE__));
+
+ t = gmtime(&unixdate);
+
+ /* set up the */
+ init_time_of_day_info(tod,
+ unixdate,
+ 0,
+ t->tm_hour,
+ t->tm_min,
+ t->tm_sec,
+ 0,
+ TimeDiff(unixdate)/60,
+ 10000,
+ t->tm_mday,
+ t->tm_mon + 1,
+ 1900+t->tm_year,
+ t->tm_wday);
+
+ DEBUG(5,("_srv_net_remote_tod: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/***********************************************************************************
+ Win9x NT tools get security descriptor.
+***********************************************************************************/
+
+WERROR _srv_net_file_query_secdesc(pipes_struct *p, SRV_Q_NET_FILE_QUERY_SECDESC *q_u,
+ SRV_R_NET_FILE_QUERY_SECDESC *r_u)
+{
+ SEC_DESC *psd = NULL;
+ size_t sd_size;
+ DATA_BLOB null_pw;
+ pstring filename;
+ pstring qualname;
+ files_struct *fsp = NULL;
+ SMB_STRUCT_STAT st;
+ BOOL bad_path;
+ int access_mode;
+ int action;
+ NTSTATUS nt_status;
+ struct current_user user;
+ connection_struct *conn = NULL;
+ BOOL became_user = False;
+
+ ZERO_STRUCT(st);
+
+ r_u->status = WERR_OK;
+
+ unistr2_to_ascii(qualname, &q_u->uni_qual_name, sizeof(qualname));
+
+ /* Null password is ok - we are already an authenticated user... */
+ null_pw = data_blob(NULL, 0);
+
+ get_current_user(&user, p);
+
+ become_root();
+ conn = make_connection(qualname, null_pw, "A:", user.vuid, &nt_status);
+ unbecome_root();
+
+ if (conn == NULL) {
+ DEBUG(3,("_srv_net_file_query_secdesc: Unable to connect to %s\n", qualname));
+ r_u->status = ntstatus_to_werror(nt_status);
+ goto error_exit;
+ }
+
+ if (!become_user(conn, conn->vuid)) {
+ DEBUG(0,("_srv_net_file_query_secdesc: Can't become connected user!\n"));
+ r_u->status = WERR_ACCESS_DENIED;
+ goto error_exit;
+ }
+ became_user = True;
+
+ unistr2_to_ascii(filename, &q_u->uni_file_name, sizeof(filename));
+ unix_convert(filename, conn, NULL, &bad_path, &st);
+ fsp = open_file_shared(conn, filename, &st, SET_OPEN_MODE(DOS_OPEN_RDONLY),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &action);
+
+ if (!fsp) {
+ /* Perhaps it is a directory */
+ if (errno == EISDIR)
+ fsp = open_directory(conn, filename, &st,FILE_READ_ATTRIBUTES,0,
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), &action);
+
+ if (!fsp) {
+ DEBUG(3,("_srv_net_file_query_secdesc: Unable to open file %s\n", filename));
+ r_u->status = WERR_ACCESS_DENIED;
+ goto error_exit;
+ }
+ }
+
+ sd_size = SMB_VFS_GET_NT_ACL(fsp, fsp->fsp_name, (OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION), &psd);
+
+ if (sd_size == 0) {
+ DEBUG(3,("_srv_net_file_query_secdesc: Unable to get NT ACL for file %s\n", filename));
+ r_u->status = WERR_ACCESS_DENIED;
+ goto error_exit;
+ }
+
+ r_u->ptr_response = 1;
+ r_u->size_response = sd_size;
+ r_u->ptr_secdesc = 1;
+ r_u->size_secdesc = sd_size;
+ r_u->sec_desc = psd;
+
+ psd->dacl->revision = (uint16) NT4_ACL_REVISION;
+
+ close_file(fsp, True);
+ unbecome_user();
+ close_cnum(conn, user.vuid);
+ return r_u->status;
+
+error_exit:
+
+ if(fsp) {
+ close_file(fsp, True);
+ }
+
+ if (became_user)
+ unbecome_user();
+
+ if (conn)
+ close_cnum(conn, user.vuid);
+
+ return r_u->status;
+}
+
+/***********************************************************************************
+ Win9x NT tools set security descriptor.
+***********************************************************************************/
+
+WERROR _srv_net_file_set_secdesc(pipes_struct *p, SRV_Q_NET_FILE_SET_SECDESC *q_u,
+ SRV_R_NET_FILE_SET_SECDESC *r_u)
+{
+ BOOL ret;
+ pstring filename;
+ pstring qualname;
+ DATA_BLOB null_pw;
+ files_struct *fsp = NULL;
+ SMB_STRUCT_STAT st;
+ BOOL bad_path;
+ int access_mode;
+ int action;
+ NTSTATUS nt_status;
+ struct current_user user;
+ connection_struct *conn = NULL;
+ BOOL became_user = False;
+
+ ZERO_STRUCT(st);
+
+ r_u->status = WERR_OK;
+
+ unistr2_to_ascii(qualname, &q_u->uni_qual_name, sizeof(qualname));
+
+ /* Null password is ok - we are already an authenticated user... */
+ null_pw = data_blob(NULL, 0);
+
+ get_current_user(&user, p);
+
+ become_root();
+ conn = make_connection(qualname, null_pw, "A:", user.vuid, &nt_status);
+ unbecome_root();
+
+ if (conn == NULL) {
+ DEBUG(3,("_srv_net_file_set_secdesc: Unable to connect to %s\n", qualname));
+ r_u->status = ntstatus_to_werror(nt_status);
+ goto error_exit;
+ }
+
+ if (!become_user(conn, conn->vuid)) {
+ DEBUG(0,("_srv_net_file_set_secdesc: Can't become connected user!\n"));
+ r_u->status = WERR_ACCESS_DENIED;
+ goto error_exit;
+ }
+ became_user = True;
+
+ unistr2_to_ascii(filename, &q_u->uni_file_name, sizeof(filename));
+ unix_convert(filename, conn, NULL, &bad_path, &st);
+
+ fsp = open_file_shared(conn, filename, &st, SET_OPEN_MODE(DOS_OPEN_RDWR),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &action);
+
+ if (!fsp) {
+ /* Perhaps it is a directory */
+ if (errno == EISDIR)
+ fsp = open_directory(conn, filename, &st,FILE_READ_ATTRIBUTES,0,
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), &action);
+
+ if (!fsp) {
+ DEBUG(3,("_srv_net_file_set_secdesc: Unable to open file %s\n", filename));
+ r_u->status = WERR_ACCESS_DENIED;
+ goto error_exit;
+ }
+ }
+
+ ret = SMB_VFS_SET_NT_ACL(fsp, fsp->fsp_name, q_u->sec_info, q_u->sec_desc);
+
+ if (ret == False) {
+ DEBUG(3,("_srv_net_file_set_secdesc: Unable to set NT ACL on file %s\n", filename));
+ r_u->status = WERR_ACCESS_DENIED;
+ goto error_exit;
+ }
+
+ close_file(fsp, True);
+ unbecome_user();
+ close_cnum(conn, user.vuid);
+ return r_u->status;
+
+error_exit:
+
+ if(fsp) {
+ close_file(fsp, True);
+ }
+
+ if (became_user)
+ unbecome_user();
+
+ if (conn)
+ close_cnum(conn, user.vuid);
+
+ return r_u->status;
+}
+
+/***********************************************************************************
+ It may be that we want to limit users to creating shares on certain areas of the UNIX file area.
+ We could define areas by mapping Windows style disks to points on the UNIX directory hierarchy.
+ These disks would the disks listed by this function.
+ Users could then create shares relative to these disks. Watch out for moving these disks around.
+ "Nigel Williams" <nigel@veritas.com>.
+***********************************************************************************/
+
+static const char *server_disks[] = {"C:"};
+
+static uint32 get_server_disk_count(void)
+{
+ return sizeof(server_disks)/sizeof(server_disks[0]);
+}
+
+static uint32 init_server_disk_enum(uint32 *resume)
+{
+ uint32 server_disk_count = get_server_disk_count();
+
+ /*resume can be an offset into the list for now*/
+
+ if(*resume & 0x80000000)
+ *resume = 0;
+
+ if(*resume > server_disk_count)
+ *resume = server_disk_count;
+
+ return server_disk_count - *resume;
+}
+
+static const char *next_server_disk_enum(uint32 *resume)
+{
+ const char *disk;
+
+ if(init_server_disk_enum(resume) == 0)
+ return NULL;
+
+ disk = server_disks[*resume];
+
+ (*resume)++;
+
+ DEBUG(10, ("next_server_disk_enum: reporting disk %s. resume handle %d.\n", disk, *resume));
+
+ return disk;
+}
+
+WERROR _srv_net_disk_enum(pipes_struct *p, SRV_Q_NET_DISK_ENUM *q_u, SRV_R_NET_DISK_ENUM *r_u)
+{
+ uint32 i;
+ const char *disk_name;
+ TALLOC_CTX *ctx = p->mem_ctx;
+ uint32 resume=get_enum_hnd(&q_u->enum_hnd);
+
+ r_u->status=WERR_OK;
+
+ r_u->total_entries = init_server_disk_enum(&resume);
+
+ r_u->disk_enum_ctr.unknown = 0;
+
+ {
+ DISK_INFO *dinfo;
+
+ int dinfo_size = MAX_SERVER_DISK_ENTRIES * sizeof(*dinfo);
+
+ if(!(dinfo = talloc(ctx, dinfo_size))) {
+ return WERR_NOMEM;
+ }
+
+ r_u->disk_enum_ctr.disk_info = dinfo;
+ }
+
+ r_u->disk_enum_ctr.disk_info_ptr = r_u->disk_enum_ctr.disk_info ? 1 : 0;
+
+ /*allow one DISK_INFO for null terminator*/
+
+ for(i = 0; i < MAX_SERVER_DISK_ENTRIES -1 && (disk_name = next_server_disk_enum(&resume)); i++) {
+
+ r_u->disk_enum_ctr.entries_read++;
+
+ /*copy disk name into a unicode string*/
+
+ init_unistr3(&r_u->disk_enum_ctr.disk_info[i].disk_name, disk_name);
+ }
+
+ /* add a terminating null string. Is this there if there is more data to come? */
+
+ r_u->disk_enum_ctr.entries_read++;
+
+ init_unistr3(&r_u->disk_enum_ctr.disk_info[i].disk_name, "");
+
+ init_enum_hnd(&r_u->enum_hnd, resume);
+
+ return r_u->status;
+}
+
+WERROR _srv_net_name_validate(pipes_struct *p, SRV_Q_NET_NAME_VALIDATE *q_u, SRV_R_NET_NAME_VALIDATE *r_u)
+{
+ int snum;
+ fstring share_name;
+
+ r_u->status=WERR_OK;
+
+ switch(q_u->type) {
+
+ case 0x9:
+
+ /*check if share name is ok*/
+ /*also check if we already have a share with this name*/
+
+ unistr2_to_ascii(share_name, &q_u->uni_name, sizeof(share_name));
+ snum = find_service(share_name);
+
+ /* Share already exists. */
+ if (snum >= 0)
+ r_u->status = WERR_ALREADY_EXISTS;
+ break;
+
+ default:
+ /*unsupported type*/
+ r_u->status = WERR_UNKNOWN_LEVEL;
+ break;
+ }
+
+ return r_u->status;
+}
diff --git a/source/rpc_server/srv_util.c b/source/rpc_server/srv_util.c
new file mode 100644
index 00000000000..5bb8db4e062
--- /dev/null
+++ b/source/rpc_server/srv_util.c
@@ -0,0 +1,580 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1998
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ * Copyright (C) Paul Ashton 1997-1998.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* this module apparently provides an implementation of DCE/RPC over a
+ * named pipe (IPC$ connection using SMBtrans). details of DCE/RPC
+ * documentation are available (in on-line form) from the X-Open group.
+ *
+ * this module should provide a level of abstraction between SMB
+ * and DCE/RPC, while minimising the amount of mallocs, unnecessary
+ * data copies, and network traffic.
+ *
+ * in this version, which takes a "let's learn what's going on and
+ * get something running" approach, there is additional network
+ * traffic generated, but the code should be easier to understand...
+ *
+ * ... if you read the docs. or stare at packets for weeks on end.
+ *
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*
+ * A list of the rids of well known BUILTIN and Domain users
+ * and groups.
+ */
+
+rid_name builtin_alias_rids[] =
+{
+ { BUILTIN_ALIAS_RID_ADMINS , "Administrators" },
+ { BUILTIN_ALIAS_RID_USERS , "Users" },
+ { BUILTIN_ALIAS_RID_GUESTS , "Guests" },
+ { BUILTIN_ALIAS_RID_POWER_USERS , "Power Users" },
+
+ { BUILTIN_ALIAS_RID_ACCOUNT_OPS , "Account Operators" },
+ { BUILTIN_ALIAS_RID_SYSTEM_OPS , "System Operators" },
+ { BUILTIN_ALIAS_RID_PRINT_OPS , "Print Operators" },
+ { BUILTIN_ALIAS_RID_BACKUP_OPS , "Backup Operators" },
+ { BUILTIN_ALIAS_RID_REPLICATOR , "Replicator" },
+ { 0 , NULL }
+};
+
+/* array lookup of well-known Domain RID users. */
+rid_name domain_user_rids[] =
+{
+ { DOMAIN_USER_RID_ADMIN , "Administrator" },
+ { DOMAIN_USER_RID_GUEST , "Guest" },
+ { 0 , NULL }
+};
+
+/* array lookup of well-known Domain RID groups. */
+rid_name domain_group_rids[] =
+{
+ { DOMAIN_GROUP_RID_ADMINS , "Domain Admins" },
+ { DOMAIN_GROUP_RID_USERS , "Domain Users" },
+ { DOMAIN_GROUP_RID_GUESTS , "Domain Guests" },
+ { 0 , NULL }
+};
+
+/*******************************************************************
+ gets a domain user's groups
+ ********************************************************************/
+NTSTATUS get_alias_user_groups(TALLOC_CTX *ctx, DOM_SID *sid, int *numgroups, uint32 **prids, DOM_SID *q_sid)
+{
+ SAM_ACCOUNT *sam_pass=NULL;
+ int i, cur_rid=0;
+ gid_t gid;
+ gid_t *groups = NULL;
+ int num_groups;
+ GROUP_MAP map;
+ DOM_SID tmp_sid;
+ fstring user_name;
+ fstring str_domsid, str_qsid;
+ uint32 rid,grid;
+ uint32 *rids=NULL, *new_rids=NULL;
+ gid_t winbind_gid_low, winbind_gid_high;
+ BOOL ret;
+ BOOL winbind_groups_exist;
+
+ /*
+ * this code is far from perfect.
+ * first it enumerates the full /etc/group and that can be slow.
+ * second, it works only with users' SIDs
+ * whereas the day we support nested groups, it will have to
+ * support both users's SIDs and domain groups' SIDs
+ *
+ * having our own ldap backend would be so much faster !
+ * we're far from that, but hope one day ;-) JFM.
+ */
+
+ *prids=NULL;
+ *numgroups=0;
+
+ winbind_groups_exist = lp_idmap_gid(&winbind_gid_low, &winbind_gid_high);
+
+
+ DEBUG(10,("get_alias_user_groups: looking if SID %s is a member of groups in the SID domain %s\n",
+ sid_to_string(str_qsid, q_sid), sid_to_string(str_domsid, sid)));
+
+ pdb_init_sam(&sam_pass);
+ become_root();
+ ret = pdb_getsampwsid(sam_pass, q_sid);
+ unbecome_root();
+ if (ret == False) {
+ pdb_free_sam(&sam_pass);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ fstrcpy(user_name, pdb_get_username(sam_pass));
+ grid=pdb_get_group_rid(sam_pass);
+ if (!NT_STATUS_IS_OK(sid_to_gid(pdb_get_group_sid(sam_pass), &gid))) {
+ /* this should never happen */
+ DEBUG(2,("get_alias_user_groups: sid_to_gid failed!\n"));
+ pdb_free_sam(&sam_pass);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ become_root();
+ /* on some systems this must run as root */
+ num_groups = getgroups_user(user_name, &groups);
+ unbecome_root();
+ if (num_groups == -1) {
+ /* this should never happen */
+ DEBUG(2,("get_alias_user_groups: getgroups_user failed\n"));
+ pdb_free_sam(&sam_pass);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ for (i=0;i<num_groups;i++) {
+
+ become_root();
+ ret = get_group_from_gid(groups[i], &map);
+ unbecome_root();
+
+ if ( !ret ) {
+ DEBUG(10,("get_alias_user_groups: gid %d. not found\n", (int)groups[i]));
+ continue;
+ }
+
+ /* if it's not an alias, continue */
+ if (map.sid_name_use != SID_NAME_ALIAS) {
+ DEBUG(10,("get_alias_user_groups: not returing %s, not an ALIAS group.\n", map.nt_name));
+ continue;
+ }
+
+ sid_copy(&tmp_sid, &map.sid);
+ sid_split_rid(&tmp_sid, &rid);
+
+ /* if the sid is not in the correct domain, continue */
+ if (!sid_equal(&tmp_sid, sid)) {
+ DEBUG(10,("get_alias_user_groups: not returing %s, not in the domain SID.\n", map.nt_name));
+ continue;
+ }
+
+ /* Don't return winbind groups as they are not local! */
+ if (winbind_groups_exist && (groups[i] >= winbind_gid_low) && (groups[i] <= winbind_gid_high)) {
+ DEBUG(10,("get_alias_user_groups: not returing %s, not local.\n", map.nt_name));
+ continue;
+ }
+
+ /* Don't return user private groups... */
+ if (Get_Pwnam(map.nt_name) != 0) {
+ DEBUG(10,("get_alias_user_groups: not returing %s, clashes with user.\n", map.nt_name));
+ continue;
+ }
+
+ new_rids=(uint32 *)Realloc(rids, sizeof(uint32)*(cur_rid+1));
+ if (new_rids==NULL) {
+ DEBUG(10,("get_alias_user_groups: could not realloc memory\n"));
+ pdb_free_sam(&sam_pass);
+ free(groups);
+ return NT_STATUS_NO_MEMORY;
+ }
+ rids=new_rids;
+
+ sid_peek_rid(&map.sid, &(rids[cur_rid]));
+ cur_rid++;
+ break;
+ }
+
+ if(num_groups)
+ free(groups);
+
+ /* now check for the user's gid (the primary group rid) */
+ for (i=0; i<cur_rid && grid!=rids[i]; i++)
+ ;
+
+ /* the user's gid is already there */
+ if (i!=cur_rid) {
+ DEBUG(10,("get_alias_user_groups: user is already in the list. good.\n"));
+ goto done;
+ }
+
+ DEBUG(10,("get_alias_user_groups: looking for gid %d of user %s\n", (int)gid, user_name));
+
+ if(!get_group_from_gid(gid, &map)) {
+ DEBUG(0,("get_alias_user_groups: gid of user %s doesn't exist. Check your "
+ "/etc/passwd and /etc/group files\n", user_name));
+ goto done;
+ }
+
+ /* the primary group isn't an alias */
+ if (map.sid_name_use!=SID_NAME_ALIAS) {
+ DEBUG(10,("get_alias_user_groups: not returing %s, not an ALIAS group.\n", map.nt_name));
+ goto done;
+ }
+
+ sid_copy(&tmp_sid, &map.sid);
+ sid_split_rid(&tmp_sid, &rid);
+
+ /* if the sid is not in the correct domain, continue */
+ if (!sid_equal(&tmp_sid, sid)) {
+ DEBUG(10,("get_alias_user_groups: not returing %s, not in the domain SID.\n", map.nt_name));
+ goto done;
+ }
+
+ /* Don't return winbind groups as they are not local! */
+ if (winbind_groups_exist && (gid >= winbind_gid_low) && (gid <= winbind_gid_high)) {
+ DEBUG(10,("get_alias_user_groups: not returing %s, not local.\n", map.nt_name ));
+ goto done;
+ }
+
+ /* Don't return user private groups... */
+ if (Get_Pwnam(map.nt_name) != 0) {
+ DEBUG(10,("get_alias_user_groups: not returing %s, clashes with user.\n", map.nt_name ));
+ goto done;
+ }
+
+ new_rids=(uint32 *)Realloc(rids, sizeof(uint32)*(cur_rid+1));
+ if (new_rids==NULL) {
+ DEBUG(10,("get_alias_user_groups: could not realloc memory\n"));
+ pdb_free_sam(&sam_pass);
+ return NT_STATUS_NO_MEMORY;
+ }
+ rids=new_rids;
+
+ sid_peek_rid(&map.sid, &(rids[cur_rid]));
+ cur_rid++;
+
+done:
+ *prids=rids;
+ *numgroups=cur_rid;
+ pdb_free_sam(&sam_pass);
+
+ return NT_STATUS_OK;
+}
+
+
+/*******************************************************************
+ gets a domain user's groups
+ ********************************************************************/
+BOOL get_domain_user_groups(TALLOC_CTX *ctx, int *numgroups, DOM_GID **pgids, SAM_ACCOUNT *sam_pass)
+{
+ GROUP_MAP *map=NULL;
+ int i, num, num_entries, cur_gid=0;
+ struct group *grp;
+ DOM_GID *gids;
+ fstring user_name;
+ uint32 grid;
+ uint32 tmp_rid;
+ BOOL ret;
+
+ *numgroups= 0;
+
+ fstrcpy(user_name, pdb_get_username(sam_pass));
+ grid=pdb_get_group_rid(sam_pass);
+
+ DEBUG(10,("get_domain_user_groups: searching domain groups [%s] is a member of\n", user_name));
+
+ /* we must wrap this is become/unbecome root for ldap backends */
+
+ become_root();
+ /* first get the list of the domain groups */
+ ret = pdb_enum_group_mapping(SID_NAME_DOM_GRP, &map, &num_entries, ENUM_ONLY_MAPPED);
+
+ unbecome_root();
+
+ /* end wrapper for group enumeration */
+
+
+ if ( !ret )
+ return False;
+
+ DEBUG(10,("get_domain_user_groups: there are %d mapped groups\n", num_entries));
+
+
+ /*
+ * alloc memory. In the worse case, we alloc memory for nothing.
+ * but I prefer to alloc for nothing
+ * than reallocing everytime.
+ */
+ gids = (DOM_GID *)talloc(ctx, sizeof(DOM_GID) * num_entries);
+
+ /* for each group, check if the user is a member of. Only include groups
+ from this domain */
+
+ for(i=0; i<num_entries; i++) {
+
+ if ( !sid_check_is_in_our_domain(&map[i].sid) ) {
+ DEBUG(10,("get_domain_user_groups: skipping check of %s since it is not in our domain\n",
+ map[i].nt_name));
+ continue;
+ }
+
+ if ((grp=getgrgid(map[i].gid)) == NULL) {
+ /* very weird !!! */
+ DEBUG(5,("get_domain_user_groups: gid %d doesn't exist anymore !\n", (int)map[i].gid));
+ continue;
+ }
+
+ for(num=0; grp->gr_mem[num]!=NULL; num++) {
+ if(strcmp(grp->gr_mem[num], user_name)==0) {
+ /* we found the user, add the group to the list */
+ sid_peek_rid(&map[i].sid, &(gids[cur_gid].g_rid));
+ gids[cur_gid].attr=7;
+ DEBUG(10,("get_domain_user_groups: user found in group %s\n", map[i].nt_name));
+ cur_gid++;
+ break;
+ }
+ }
+ }
+
+ /* we have checked the groups */
+ /* we must now check the gid of the user or the primary group rid, that's the same */
+ for (i=0; i<cur_gid && grid!=gids[i].g_rid; i++)
+ ;
+
+ /* the user's gid is already there */
+ if (i!=cur_gid) {
+ /*
+ * the primary group of the user but be the first one in the list
+ * don't ask ! JFM.
+ */
+ gids[i].g_rid=gids[0].g_rid;
+ gids[0].g_rid=grid;
+ goto done;
+ }
+
+ for(i=0; i<num_entries; i++) {
+ sid_peek_rid(&map[i].sid, &tmp_rid);
+ if (tmp_rid==grid) {
+ /*
+ * the primary group of the user but be the first one in the list
+ * don't ask ! JFM.
+ */
+ gids[cur_gid].g_rid=gids[0].g_rid;
+ gids[0].g_rid=tmp_rid;
+ gids[cur_gid].attr=7;
+ DEBUG(10,("get_domain_user_groups: primary gid of user found in group %s\n", map[i].nt_name));
+ cur_gid++;
+ goto done; /* leave the loop early */
+ }
+ }
+
+ DEBUG(0,("get_domain_user_groups: primary gid of user [%s] is not a Domain group !\n", user_name));
+ DEBUGADD(0,("get_domain_user_groups: You should fix it, NT doesn't like that\n"));
+
+
+ done:
+ *pgids=gids;
+ *numgroups=cur_gid;
+ SAFE_FREE(map);
+
+ return True;
+}
+
+/*******************************************************************
+ gets a domain user's groups from their already-calculated NT_USER_TOKEN
+ ********************************************************************/
+NTSTATUS nt_token_to_group_list(TALLOC_CTX *mem_ctx, const DOM_SID *domain_sid,
+ const NT_USER_TOKEN *nt_token,
+ int *numgroups, DOM_GID **pgids)
+{
+ DOM_GID *gids;
+ int i;
+
+ gids = (DOM_GID *)talloc(mem_ctx, sizeof(*gids) * nt_token->num_sids);
+
+ if (!gids) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *numgroups=0;
+
+ for (i=PRIMARY_GROUP_SID_INDEX; i < nt_token->num_sids; i++) {
+ if (sid_compare_domain(domain_sid, &nt_token->user_sids[i])==0) {
+ sid_peek_rid(&nt_token->user_sids[i], &(gids[*numgroups].g_rid));
+ gids[*numgroups].attr=7;
+ (*numgroups)++;
+ }
+ }
+ *pgids = gids;
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Look up a local (domain) rid and return a name and type.
+ ********************************************************************/
+NTSTATUS local_lookup_group_name(uint32 rid, char *group_name, uint32 *type)
+{
+ int i = 0;
+ (*type) = SID_NAME_DOM_GRP;
+
+ DEBUG(5,("lookup_group_name: rid: %d", rid));
+
+ while (domain_group_rids[i].rid != rid && domain_group_rids[i].rid != 0)
+ {
+ i++;
+ }
+
+ if (domain_group_rids[i].rid != 0)
+ {
+ fstrcpy(group_name, domain_group_rids[i].name);
+ DEBUG(5,(" = %s\n", group_name));
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(5,(" none mapped\n"));
+ return NT_STATUS_NONE_MAPPED;
+}
+
+/*******************************************************************
+ Look up a local alias rid and return a name and type.
+ ********************************************************************/
+NTSTATUS local_lookup_alias_name(uint32 rid, char *alias_name, uint32 *type)
+{
+ int i = 0;
+ (*type) = SID_NAME_WKN_GRP;
+
+ DEBUG(5,("lookup_alias_name: rid: %d", rid));
+
+ while (builtin_alias_rids[i].rid != rid && builtin_alias_rids[i].rid != 0)
+ {
+ i++;
+ }
+
+ if (builtin_alias_rids[i].rid != 0)
+ {
+ fstrcpy(alias_name, builtin_alias_rids[i].name);
+ DEBUG(5,(" = %s\n", alias_name));
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(5,(" none mapped\n"));
+ return NT_STATUS_NONE_MAPPED;
+}
+
+
+#if 0 /*Nobody uses this function just now*/
+/*******************************************************************
+ Look up a local user rid and return a name and type.
+ ********************************************************************/
+NTSTATUS local_lookup_user_name(uint32 rid, char *user_name, uint32 *type)
+{
+ SAM_ACCOUNT *sampwd=NULL;
+ int i = 0;
+ BOOL ret;
+
+ (*type) = SID_NAME_USER;
+
+ DEBUG(5,("lookup_user_name: rid: %d", rid));
+
+ /* look up the well-known domain user rids first */
+ while (domain_user_rids[i].rid != rid && domain_user_rids[i].rid != 0)
+ {
+ i++;
+ }
+
+ if (domain_user_rids[i].rid != 0) {
+ fstrcpy(user_name, domain_user_rids[i].name);
+ DEBUG(5,(" = %s\n", user_name));
+ return NT_STATUS_OK;
+ }
+
+ pdb_init_sam(&sampwd);
+
+ /* ok, it's a user. find the user account */
+ become_root();
+ ret = pdb_getsampwrid(sampwd, rid);
+ unbecome_root();
+
+ if (ret == True) {
+ fstrcpy(user_name, pdb_get_username(sampwd) );
+ DEBUG(5,(" = %s\n", user_name));
+ pdb_free_sam(&sampwd);
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(5,(" none mapped\n"));
+ pdb_free_sam(&sampwd);
+ return NT_STATUS_NONE_MAPPED;
+}
+
+#endif
+
+/*******************************************************************
+ Look up a local (domain) group name and return a rid
+ ********************************************************************/
+NTSTATUS local_lookup_group_rid(char *group_name, uint32 *rid)
+{
+ const char *grp_name;
+ int i = -1; /* start do loop at -1 */
+
+ do /* find, if it exists, a group rid for the group name*/
+ {
+ i++;
+ (*rid) = domain_group_rids[i].rid;
+ grp_name = domain_group_rids[i].name;
+
+ } while (grp_name != NULL && !strequal(grp_name, group_name));
+
+ return (grp_name != NULL) ? NT_STATUS_OK : NT_STATUS_NONE_MAPPED;
+}
+
+/*******************************************************************
+ Look up a local (BUILTIN) alias name and return a rid
+ ********************************************************************/
+NTSTATUS local_lookup_alias_rid(const char *alias_name, uint32 *rid)
+{
+ const char *als_name;
+ int i = -1; /* start do loop at -1 */
+
+ do /* find, if it exists, a alias rid for the alias name*/
+ {
+ i++;
+ (*rid) = builtin_alias_rids[i].rid;
+ als_name = builtin_alias_rids[i].name;
+
+ } while (als_name != NULL && !strequal(als_name, alias_name));
+
+ return (als_name != NULL) ? NT_STATUS_OK : NT_STATUS_NONE_MAPPED;
+}
+
+/*******************************************************************
+ Look up a local user name and return a rid
+ ********************************************************************/
+NTSTATUS local_lookup_user_rid(char *user_name, uint32 *rid)
+{
+ SAM_ACCOUNT *sampass=NULL;
+ BOOL ret;
+
+ (*rid) = 0;
+
+ pdb_init_sam(&sampass);
+
+ /* find the user account */
+ become_root();
+ ret = pdb_getsampwnam(sampass, user_name);
+ unbecome_root();
+
+ if (ret == True) {
+ (*rid) = pdb_get_user_rid(sampass);
+ pdb_free_sam(&sampass);
+ return NT_STATUS_OK;
+ }
+
+ pdb_free_sam(&sampass);
+ return NT_STATUS_NONE_MAPPED;
+}
diff --git a/source/rpc_server/srv_wkssvc.c b/source/rpc_server/srv_wkssvc.c
new file mode 100644
index 00000000000..b5c1af34d9d
--- /dev/null
+++ b/source/rpc_server/srv_wkssvc.c
@@ -0,0 +1,78 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997,
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This is the interface to the wks pipe. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*******************************************************************
+ api_wks_query_info
+ ********************************************************************/
+
+static BOOL api_wks_query_info(pipes_struct *p)
+{
+ WKS_Q_QUERY_INFO q_u;
+ WKS_R_QUERY_INFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the net share enum */
+ if(!wks_io_q_query_info("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _wks_query_info(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!wks_io_r_query_info("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ \PIPE\wkssvc commands
+ ********************************************************************/
+
+static struct api_struct api_wks_cmds[] =
+{
+ { "WKS_Q_QUERY_INFO", WKS_QUERY_INFO, api_wks_query_info }
+};
+
+void wkssvc_get_pipe_fns( struct api_struct **fns, int *n_fns )
+{
+ *fns = api_wks_cmds;
+ *n_fns = sizeof(api_wks_cmds) / sizeof(struct api_struct);
+}
+
+NTSTATUS rpc_wks_init(void)
+{
+ return rpc_pipe_register_commands(SMB_RPC_INTERFACE_VERSION, "wkssvc", "ntsvcs", api_wks_cmds,
+ sizeof(api_wks_cmds) / sizeof(struct api_struct));
+}
diff --git a/source/rpc_server/srv_wkssvc_nt.c b/source/rpc_server/srv_wkssvc_nt.c
new file mode 100644
index 00000000000..25fa029237d
--- /dev/null
+++ b/source/rpc_server/srv_wkssvc_nt.c
@@ -0,0 +1,79 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997.
+ * Copyright (C) Jeremy Allison 2001.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This is the implementation of the wks interface. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*******************************************************************
+ create_wks_info_100
+ ********************************************************************/
+
+static void create_wks_info_100(WKS_INFO_100 *inf)
+{
+ pstring my_name;
+ pstring domain;
+
+ DEBUG(5,("create_wks_info_100: %d\n", __LINE__));
+
+ pstrcpy (my_name, global_myname());
+ strupper_m(my_name);
+
+ pstrcpy (domain, lp_workgroup());
+ strupper_m(domain);
+
+ init_wks_info_100(inf,
+ 0x000001f4, /* platform id info */
+ lp_major_announce_version(),
+ lp_minor_announce_version(),
+ my_name, domain);
+}
+
+/*******************************************************************
+ wks_reply_query_info
+
+ only supports info level 100 at the moment.
+
+ ********************************************************************/
+
+NTSTATUS _wks_query_info(pipes_struct *p, WKS_Q_QUERY_INFO *q_u, WKS_R_QUERY_INFO *r_u)
+{
+ WKS_INFO_100 *wks100 = NULL;
+
+ DEBUG(5,("_wks_query_info: %d\n", __LINE__));
+
+ wks100 = (WKS_INFO_100 *)talloc_zero(p->mem_ctx, sizeof(WKS_INFO_100));
+
+ if (!wks100)
+ return NT_STATUS_NO_MEMORY;
+
+ create_wks_info_100(wks100);
+ init_wks_r_query_info(r_u, q_u->switch_value, wks100, NT_STATUS_OK);
+
+ DEBUG(5,("_wks_query_info: %d\n", __LINE__));
+
+ return r_u->status;
+}
diff --git a/source/rpcclient/cmd_dfs.c b/source/rpcclient/cmd_dfs.c
new file mode 100644
index 00000000000..44e97f9881e
--- /dev/null
+++ b/source/rpcclient/cmd_dfs.c
@@ -0,0 +1,237 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ Copyright (C) Tim Potter 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "rpcclient.h"
+
+/* Check DFS is supported by the remote server */
+
+static NTSTATUS cmd_dfs_exist(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ BOOL dfs_exists;
+ NTSTATUS result;
+
+ if (argc != 1) {
+ printf("Usage: %s\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = cli_dfs_exist(cli, mem_ctx, &dfs_exists);
+
+ if (NT_STATUS_IS_OK(result))
+ printf("dfs is %spresent\n", dfs_exists ? "" : "not ");
+
+ return result;
+}
+
+static NTSTATUS cmd_dfs_add(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result;
+ const char *entrypath, *servername, *sharename, *comment;
+ uint32 flags = 0;
+
+ if (argc != 5) {
+ printf("Usage: %s entrypath servername sharename comment\n",
+ argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ entrypath = argv[1];
+ servername = argv[2];
+ sharename = argv[3];
+ comment = argv[4];
+
+ result = cli_dfs_add(cli, mem_ctx, entrypath, servername,
+ sharename, comment, flags);
+
+ return result;
+}
+
+static NTSTATUS cmd_dfs_remove(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result;
+ const char *entrypath, *servername, *sharename;
+
+ if (argc != 4) {
+ printf("Usage: %s entrypath servername sharename\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ entrypath = argv[1];
+ servername = argv[2];
+ sharename = argv[3];
+
+ result = cli_dfs_remove(cli, mem_ctx, entrypath, servername,
+ sharename);
+
+ return result;
+}
+
+/* Display a DFS_INFO_1 structure */
+
+static void display_dfs_info_1(DFS_INFO_1 *info1)
+{
+ fstring temp;
+
+ unistr2_to_ascii(temp, &info1->entrypath, sizeof(temp) - 1);
+ printf("entrypath: %s\n", temp);
+}
+
+/* Display a DFS_INFO_2 structure */
+
+static void display_dfs_info_2(DFS_INFO_2 *info2)
+{
+ fstring temp;
+
+ unistr2_to_ascii(temp, &info2->entrypath, sizeof(temp) - 1);
+ printf("entrypath: %s\n", temp);
+
+ unistr2_to_ascii(temp, &info2->comment, sizeof(temp) - 1);
+ printf("\tcomment: %s\n", temp);
+
+ printf("\tstate: %d\n", info2->state);
+ printf("\tnum_storages: %d\n", info2->num_storages);
+}
+
+/* Display a DFS_INFO_3 structure */
+
+static void display_dfs_info_3(DFS_INFO_3 *info3)
+{
+ fstring temp;
+ int i;
+
+ unistr2_to_ascii(temp, &info3->entrypath, sizeof(temp) - 1);
+ printf("entrypath: %s\n", temp);
+
+ unistr2_to_ascii(temp, &info3->comment, sizeof(temp) - 1);
+ printf("\tcomment: %s\n", temp);
+
+ printf("\tstate: %d\n", info3->state);
+ printf("\tnum_storages: %d\n", info3->num_storages);
+
+ for (i = 0; i < info3->num_storages; i++) {
+ DFS_STORAGE_INFO *dsi = &info3->storages[i];
+
+ unistr2_to_ascii(temp, &dsi->servername, sizeof(temp) - 1);
+ printf("\t\tstorage[%d] servername: %s\n", i, temp);
+
+ unistr2_to_ascii(temp, &dsi->sharename, sizeof(temp) - 1);
+ printf("\t\tstorage[%d] sharename: %s\n", i, temp);
+ }
+}
+
+/* Display a DFS_INFO_CTR structure */
+
+static void display_dfs_info_ctr(DFS_INFO_CTR *ctr)
+{
+ int i;
+
+ for (i = 0; i < ctr->num_entries; i++) {
+ switch (ctr->switch_value) {
+ case 0x01:
+ display_dfs_info_1(&ctr->dfs.info1[i]);
+ break;
+ case 0x02:
+ display_dfs_info_2(&ctr->dfs.info2[i]);
+ break;
+ case 0x03:
+ display_dfs_info_3(&ctr->dfs.info3[i]);
+ break;
+ default:
+ printf("unsupported info level %d\n",
+ ctr->switch_value);
+ break;
+ }
+ }
+}
+
+/* Enumerate dfs shares */
+
+static NTSTATUS cmd_dfs_enum(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ DFS_INFO_CTR ctr;
+ NTSTATUS result;
+ uint32 info_level = 1;
+
+ if (argc > 2) {
+ printf("Usage: %s [info_level]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc == 2)
+ info_level = atoi(argv[1]);
+
+ result = cli_dfs_enum(cli, mem_ctx, info_level, &ctr);
+
+ if (NT_STATUS_IS_OK(result))
+ display_dfs_info_ctr(&ctr);
+
+ return result;
+}
+
+static NTSTATUS cmd_dfs_getinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result;
+ const char *entrypath, *servername, *sharename;
+ uint32 info_level = 1;
+ DFS_INFO_CTR ctr;
+
+ if (argc < 4 || argc > 5) {
+ printf("Usage: %s entrypath servername sharename "
+ "[info_level]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ entrypath = argv[1];
+ servername = argv[2];
+ sharename = argv[3];
+
+ if (argc == 5)
+ info_level = atoi(argv[4]);
+
+ result = cli_dfs_get_info(cli, mem_ctx, entrypath, servername,
+ sharename, info_level, &ctr);
+
+ if (NT_STATUS_IS_OK(result))
+ display_dfs_info_ctr(&ctr);
+
+ return result;
+}
+
+/* List of commands exported by this module */
+
+struct cmd_set dfs_commands[] = {
+
+ { "DFS" },
+
+ { "dfsexist", RPC_RTYPE_NTSTATUS, cmd_dfs_exist, NULL, PI_NETDFS, "Query DFS support", "" },
+ { "dfsadd", RPC_RTYPE_NTSTATUS, cmd_dfs_add, NULL, PI_NETDFS, "Add a DFS share", "" },
+ { "dfsremove", RPC_RTYPE_NTSTATUS, cmd_dfs_remove, NULL, PI_NETDFS, "Remove a DFS share", "" },
+ { "dfsgetinfo",RPC_RTYPE_NTSTATUS, cmd_dfs_getinfo, NULL, PI_NETDFS, "Query DFS share info", "" },
+ { "dfsenum", RPC_RTYPE_NTSTATUS, cmd_dfs_enum, NULL, PI_NETDFS, "Enumerate dfs shares", "" },
+
+ { NULL }
+};
diff --git a/source/rpcclient/cmd_ds.c b/source/rpcclient/cmd_ds.c
new file mode 100644
index 00000000000..c5b12ed1503
--- /dev/null
+++ b/source/rpcclient/cmd_ds.c
@@ -0,0 +1,79 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ Copyright (C) Gerald Carter 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "rpcclient.h"
+
+/* Look up domain related information on a remote host */
+
+static NTSTATUS cmd_ds_dsrole_getprimarydominfo(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ NTSTATUS result;
+ DS_DOMINFO_CTR ctr;
+
+ result = cli_ds_getprimarydominfo( cli, mem_ctx, DsRolePrimaryDomainInfoBasic, &ctr );
+ if ( NT_STATUS_IS_OK(result) )
+ {
+ printf ("Machine Role = [%d]\n", ctr.basic->machine_role);
+
+ if ( ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING ) {
+ printf( "Directory Service is running.\n");
+ printf( "Domain is in %s mode.\n", (ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) ? "mixed" : "native" );
+ }
+ else
+ printf( "Directory Service not running on server\n");
+ }
+
+ return result;
+}
+
+static NTSTATUS cmd_ds_enum_domain_trusts(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ NTSTATUS result;
+ uint32 flags = 0x1;
+ struct ds_domain_trust *trusts = NULL;
+ unsigned int num_domains = 0;
+
+ result = cli_ds_enum_domain_trusts( cli, mem_ctx, cli->desthost, flags,
+ &trusts, &num_domains );
+
+ printf( "%d domains returned\n", num_domains );
+
+ SAFE_FREE( trusts );
+
+ return result;
+}
+
+/* List of commands exported by this module */
+
+struct cmd_set ds_commands[] = {
+
+ { "LSARPC-DS" },
+
+ { "dsroledominfo", RPC_RTYPE_NTSTATUS, cmd_ds_dsrole_getprimarydominfo, NULL, PI_LSARPC_DS, "Get Primary Domain Information", "" },
+ { "dsenumdomtrusts", RPC_RTYPE_NTSTATUS, cmd_ds_enum_domain_trusts, NULL, PI_NETLOGON, "Enumerate all trusted domains in an AD forest", "" },
+
+ { NULL }
+};
diff --git a/source/rpcclient/cmd_echo.c b/source/rpcclient/cmd_echo.c
new file mode 100644
index 00000000000..fa4e6916633
--- /dev/null
+++ b/source/rpcclient/cmd_echo.c
@@ -0,0 +1,159 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ Copyright (C) Tim Potter 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "rpcclient.h"
+
+static NTSTATUS cmd_echo_add_one(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ uint32 request = 1, response;
+ NTSTATUS result;
+
+ if (argc > 2) {
+ printf("Usage: %s [num]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc == 2)
+ request = atoi(argv[1]);
+
+ result = cli_echo_add_one(cli, mem_ctx, request, &response);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ printf("%d + 1 = %d\n", request, response);
+
+done:
+ return result;
+}
+
+static NTSTATUS cmd_echo_data(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ uint32 size, i;
+ NTSTATUS result;
+ char *in_data = NULL, *out_data = NULL;
+
+ if (argc != 2) {
+ printf("Usage: %s num\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ size = atoi(argv[1]);
+ in_data = malloc(size);
+
+ for (i = 0; i < size; i++)
+ in_data[i] = i & 0xff;
+
+ result = cli_echo_data(cli, mem_ctx, size, in_data, &out_data);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ for (i = 0; i < size; i++) {
+ if (in_data[i] != out_data[i]) {
+ printf("mismatch at offset %d, %d != %d\n",
+ i, in_data[i], out_data[i]);
+ result = NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+done:
+ SAFE_FREE(in_data);
+
+ return result;
+}
+
+static NTSTATUS cmd_echo_source_data(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ uint32 size, i;
+ NTSTATUS result;
+ char *out_data = NULL;
+
+ if (argc != 2) {
+ printf("Usage: %s num\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ size = atoi(argv[1]);
+
+ result = cli_echo_source_data(cli, mem_ctx, size, &out_data);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ for (i = 0; i < size; i++) {
+ if (out_data && out_data[i] != (i & 0xff)) {
+ printf("mismatch at offset %d, %d != %d\n",
+ i, out_data[i], i & 0xff);
+ result = NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+done:
+ return result;
+}
+
+static NTSTATUS cmd_echo_sink_data(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ uint32 size, i;
+ NTSTATUS result;
+ char *in_data = NULL;
+
+ if (argc != 2) {
+ printf("Usage: %s num\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ size = atoi(argv[1]);
+ in_data = malloc(size);
+
+ for (i = 0; i < size; i++)
+ in_data[i] = i & 0xff;
+
+ result = cli_echo_sink_data(cli, mem_ctx, size, in_data);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+done:
+ SAFE_FREE(in_data);
+
+ return result;
+}
+
+/* List of commands exported by this module */
+
+struct cmd_set echo_commands[] = {
+
+ { "ECHO" },
+
+ { "echoaddone", RPC_RTYPE_NTSTATUS, cmd_echo_add_one, NULL, PI_ECHO, "Add one to a number", "" },
+ { "echodata", RPC_RTYPE_NTSTATUS, cmd_echo_data, NULL, PI_ECHO, "Echo data", "" },
+ { "sinkdata", RPC_RTYPE_NTSTATUS, cmd_echo_sink_data, NULL, PI_ECHO, "Sink data", "" },
+ { "sourcedata", RPC_RTYPE_NTSTATUS, cmd_echo_source_data, NULL, PI_ECHO, "Source data", "" },
+ { NULL }
+};
diff --git a/source/rpcclient/cmd_epmapper.c b/source/rpcclient/cmd_epmapper.c
new file mode 100644
index 00000000000..4998286194c
--- /dev/null
+++ b/source/rpcclient/cmd_epmapper.c
@@ -0,0 +1,76 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "rpcclient.h"
+
+
+static NTSTATUS cmd_epm_map(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ EPM_HANDLE handle, entry_handle;
+ EPM_TOWER *towers;
+ EPM_FLOOR floors[5];
+ uint8 addr[4] = {0,0,0,0};
+ uint32 numtowers;
+ /* need to allow all this stuff to be passed in, but
+ for now, it demonstrates the call */
+ struct uuid if_uuid = {0xe3514235, 0x4b06, 0x11d1, \
+ { 0xab, 0x04 }, \
+ { 0x00, 0xc0, \
+ 0x4f, 0xc2, 0xdc, 0xd2 } },
+ syn_uuid = {0x8a885d04, 0x1ceb, 0x11c9, \
+ { 0x9f, 0xe8 }, \
+ { 0x08, 0x00, \
+ 0x2b, 0x10, 0x48, 0x60 } };
+
+ NTSTATUS result;
+
+ ZERO_STRUCT(handle);
+ numtowers = 1;
+ init_epm_floor_uuid(&floors[0], if_uuid, 4);
+ init_epm_floor_uuid(&floors[1], syn_uuid, 2);
+ init_epm_floor_rpc(&floors[2]);
+
+ /* sample for netbios named pipe query
+ init_epm_floor_np(&floors[3], "\\PIPE\\lsass");
+ init_epm_floor_nb(&floors[4], "\\\\psflinux");
+ */
+ init_epm_floor_tcp(&floors[3], 135);
+ init_epm_floor_ip(&floors[4], addr);
+ towers = talloc(mem_ctx, sizeof(EPM_TOWER));
+ init_epm_tower(mem_ctx, towers, floors, 5);
+
+ result = cli_epm_map(cli, mem_ctx, &handle, &towers, &entry_handle, &numtowers);
+
+ return result;
+}
+
+struct cmd_set epm_commands[] = {
+
+ { "EPMAPPER" },
+
+ { "map", RPC_RTYPE_NTSTATUS, cmd_epm_map, NULL, PI_EPM, "map endpoint", "" },
+ { NULL }
+};
+
+
diff --git a/source/rpcclient/cmd_lsarpc.c b/source/rpcclient/cmd_lsarpc.c
new file mode 100644
index 00000000000..5a646a10460
--- /dev/null
+++ b/source/rpcclient/cmd_lsarpc.c
@@ -0,0 +1,762 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Rafal Szczesniak 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "rpcclient.h"
+
+
+/* useful function to allow entering a name instead of a SID and
+ * looking it up automatically */
+static NTSTATUS name_to_sid(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ DOM_SID *sid, const char *name)
+{
+ POLICY_HND pol;
+ uint32 *sid_types;
+ NTSTATUS result;
+ DOM_SID *sids;
+
+ /* maybe its a raw SID */
+ if (strncmp(name, "S-", 2) == 0 &&
+ string_to_sid(sid, name)) {
+ return NT_STATUS_OK;
+ }
+
+ result = cli_lsa_open_policy(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_lookup_names(cli, mem_ctx, &pol, 1, &name, &sids, &sid_types);
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ cli_lsa_close(cli, mem_ctx, &pol);
+
+ *sid = sids[0];
+
+done:
+ return result;
+}
+
+
+/* Look up domain related information on a remote host */
+
+static NTSTATUS cmd_lsa_query_info_policy(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ DOM_SID *dom_sid;
+ struct uuid *dom_guid;
+ fstring sid_str;
+ char *domain_name = NULL;
+ char *dns_name = NULL;
+ char *forest_name = NULL;
+
+ uint32 info_class = 3;
+
+ if (argc > 2) {
+ printf("Usage: %s [info_class]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc == 2)
+ info_class = atoi(argv[1]);
+
+ /* Lookup info policy */
+ switch (info_class) {
+ case 12:
+ result = cli_lsa_open_policy2(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+ result = cli_lsa_query_info_policy2(cli, mem_ctx, &pol,
+ info_class, &domain_name,
+ &dns_name, &forest_name,
+ &dom_guid, &dom_sid);
+ break;
+ default:
+ result = cli_lsa_open_policy(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+ result = cli_lsa_query_info_policy(cli, mem_ctx, &pol,
+ info_class, &domain_name,
+ &dom_sid);
+ }
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ sid_to_string(sid_str, dom_sid);
+
+ if (domain_name)
+ printf("domain %s has sid %s\n", domain_name, sid_str);
+ else
+ printf("could not query info for level %d\n", info_class);
+
+ if (dns_name)
+ printf("domain dns name is %s\n", dns_name);
+ if (forest_name)
+ printf("forest name is %s\n", forest_name);
+
+ if (info_class == 12) {
+ printf("domain GUID is ");
+ smb_uuid_string_static(*dom_guid);
+ }
+ done:
+ return result;
+}
+
+/* Resolve a list of names to a list of sids */
+
+static NTSTATUS cmd_lsa_lookup_names(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ DOM_SID *sids;
+ uint32 *types;
+ int i;
+
+ if (argc == 1) {
+ printf("Usage: %s [name1 [name2 [...]]]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = cli_lsa_open_policy(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_lookup_names(cli, mem_ctx, &pol, argc - 1,
+ (const char**)(argv + 1), &sids, &types);
+
+ if (!NT_STATUS_IS_OK(result) && NT_STATUS_V(result) !=
+ NT_STATUS_V(STATUS_SOME_UNMAPPED))
+ goto done;
+
+ result = NT_STATUS_OK;
+
+ /* Print results */
+
+ for (i = 0; i < (argc - 1); i++) {
+ fstring sid_str;
+ sid_to_string(sid_str, &sids[i]);
+ printf("%s %s (%s: %d)\n", argv[i + 1], sid_str,
+ sid_type_lookup(types[i]), types[i]);
+ }
+
+ done:
+ return result;
+}
+
+/* Resolve a list of SIDs to a list of names */
+
+static NTSTATUS cmd_lsa_lookup_sids(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ DOM_SID *sids;
+ char **domains;
+ char **names;
+ uint32 *types;
+ int i;
+
+ if (argc == 1) {
+ printf("Usage: %s [sid1 [sid2 [...]]]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = cli_lsa_open_policy(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Convert arguments to sids */
+
+ sids = (DOM_SID *)talloc(mem_ctx, sizeof(DOM_SID) * (argc - 1));
+
+ if (!sids) {
+ printf("could not allocate memory for %d sids\n", argc - 1);
+ goto done;
+ }
+
+ for (i = 0; i < argc - 1; i++)
+ if (!string_to_sid(&sids[i], argv[i + 1])) {
+ result = NT_STATUS_INVALID_SID;
+ goto done;
+ }
+
+ /* Lookup the SIDs */
+
+ result = cli_lsa_lookup_sids(cli, mem_ctx, &pol, argc - 1, sids,
+ &domains, &names, &types);
+
+ if (!NT_STATUS_IS_OK(result) && NT_STATUS_V(result) !=
+ NT_STATUS_V(STATUS_SOME_UNMAPPED))
+ goto done;
+
+ result = NT_STATUS_OK;
+
+ /* Print results */
+
+ for (i = 0; i < (argc - 1); i++) {
+ fstring sid_str;
+
+ sid_to_string(sid_str, &sids[i]);
+ printf("%s %s\\%s (%d)\n", sid_str,
+ domains[i] ? domains[i] : "*unknown*",
+ names[i] ? names[i] : "*unknown*", types[i]);
+ }
+
+ done:
+ return result;
+}
+
+/* Enumerate list of trusted domains */
+
+static NTSTATUS cmd_lsa_enum_trust_dom(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ DOM_SID *domain_sids;
+ char **domain_names;
+
+ /* defaults, but may be changed using params */
+ uint32 enum_ctx = 0;
+ uint32 num_domains = 0;
+ int i;
+
+ if (argc > 2) {
+ printf("Usage: %s [enum context (0)]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc == 2 && argv[1]) {
+ enum_ctx = atoi(argv[2]);
+ }
+
+ result = cli_lsa_open_policy(cli, mem_ctx, True,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Lookup list of trusted domains */
+
+ result = cli_lsa_enum_trust_dom(cli, mem_ctx, &pol, &enum_ctx,
+ &num_domains,
+ &domain_names, &domain_sids);
+ if (!NT_STATUS_IS_OK(result) &&
+ !NT_STATUS_EQUAL(result, NT_STATUS_NO_MORE_ENTRIES) &&
+ !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES))
+ goto done;
+
+ /* Print results: list of names and sids returned in this response. */
+ for (i = 0; i < num_domains; i++) {
+ fstring sid_str;
+
+ sid_to_string(sid_str, &domain_sids[i]);
+ printf("%s %s\n", domain_names[i] ? domain_names[i] :
+ "*unknown*", sid_str);
+ }
+
+ done:
+ return result;
+}
+
+/* Enumerates privileges */
+
+static NTSTATUS cmd_lsa_enum_privilege(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ uint32 enum_context=0;
+ uint32 pref_max_length=0x1000;
+ uint32 count=0;
+ char **privs_name;
+ uint32 *privs_high;
+ uint32 *privs_low;
+ int i;
+
+ if (argc > 3) {
+ printf("Usage: %s [enum context] [max length]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc>=2)
+ enum_context=atoi(argv[1]);
+
+ if (argc==3)
+ pref_max_length=atoi(argv[2]);
+
+ result = cli_lsa_open_policy(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_enum_privilege(cli, mem_ctx, &pol, &enum_context, pref_max_length,
+ &count, &privs_name, &privs_high, &privs_low);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Print results */
+ printf("found %d privileges\n\n", count);
+
+ for (i = 0; i < count; i++) {
+ printf("%s \t\t%d:%d (0x%x:0x%x)\n", privs_name[i] ? privs_name[i] : "*unknown*",
+ privs_high[i], privs_low[i], privs_high[i], privs_low[i]);
+ }
+
+ done:
+ return result;
+}
+
+/* Get privilege name */
+
+static NTSTATUS cmd_lsa_get_dispname(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ uint16 lang_id=0;
+ uint16 lang_id_sys=0;
+ uint16 lang_id_desc;
+ fstring description;
+
+ if (argc != 2) {
+ printf("Usage: %s privilege name\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = cli_lsa_open_policy(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_get_dispname(cli, mem_ctx, &pol, argv[1], lang_id, lang_id_sys, description, &lang_id_desc);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Print results */
+ printf("%s -> %s (language: 0x%x)\n", argv[1], description, lang_id_desc);
+
+ done:
+ return result;
+}
+
+/* Enumerate the LSA SIDS */
+
+static NTSTATUS cmd_lsa_enum_sids(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ uint32 enum_context=0;
+ uint32 pref_max_length=0x1000;
+ DOM_SID *sids;
+ uint32 count=0;
+ int i;
+
+ if (argc > 3) {
+ printf("Usage: %s [enum context] [max length]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc>=2)
+ enum_context=atoi(argv[1]);
+
+ if (argc==3)
+ pref_max_length=atoi(argv[2]);
+
+ result = cli_lsa_open_policy(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_enum_sids(cli, mem_ctx, &pol, &enum_context, pref_max_length,
+ &count, &sids);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Print results */
+ printf("found %d SIDs\n\n", count);
+
+ for (i = 0; i < count; i++) {
+ fstring sid_str;
+
+ sid_to_string(sid_str, &sids[i]);
+ printf("%s\n", sid_str);
+ }
+
+ done:
+ return result;
+}
+
+/* Create a new account */
+
+static NTSTATUS cmd_lsa_create_account(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND dom_pol;
+ POLICY_HND user_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 des_access = 0x000f000f;
+
+ DOM_SID sid;
+
+ if (argc != 2 ) {
+ printf("Usage: %s SID\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = name_to_sid(cli, mem_ctx, &sid, argv[1]);
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_open_policy2(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &dom_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_create_account(cli, mem_ctx, &dom_pol, &sid, des_access, &user_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ printf("Account for SID %s successfully created\n\n", argv[1]);
+ result = NT_STATUS_OK;
+
+ done:
+ return result;
+}
+
+
+/* Enumerate the privileges of an SID */
+
+static NTSTATUS cmd_lsa_enum_privsaccounts(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND dom_pol;
+ POLICY_HND user_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 access_desired = 0x000f000f;
+
+ DOM_SID sid;
+ uint32 count=0;
+ LUID_ATTR *set;
+ int i;
+
+ if (argc != 2 ) {
+ printf("Usage: %s SID\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = name_to_sid(cli, mem_ctx, &sid, argv[1]);
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_open_policy2(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &dom_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_open_account(cli, mem_ctx, &dom_pol, &sid, access_desired, &user_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_enum_privsaccount(cli, mem_ctx, &user_pol, &count, &set);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Print results */
+ printf("found %d privileges for SID %s\n\n", count, argv[1]);
+ printf("high\tlow\tattribute\n");
+
+ for (i = 0; i < count; i++) {
+ printf("%u\t%u\t%u\n", set[i].luid.high, set[i].luid.low, set[i].attr);
+ }
+
+ done:
+ return result;
+}
+
+
+/* Enumerate the privileges of an SID via LsaEnumerateAccountRights */
+
+static NTSTATUS cmd_lsa_enum_acct_rights(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND dom_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DOM_SID sid;
+ uint32 count;
+ char **rights;
+
+ int i;
+
+ if (argc != 2 ) {
+ printf("Usage: %s SID\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = name_to_sid(cli, mem_ctx, &sid, argv[1]);
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_open_policy2(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &dom_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_enum_account_rights(cli, mem_ctx, &dom_pol, sid, &count, &rights);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ printf("found %d privileges for SID %s\n", count, sid_string_static(&sid));
+
+ for (i = 0; i < count; i++) {
+ printf("\t%s\n", rights[i]);
+ }
+
+ done:
+ return result;
+}
+
+
+/* add some privileges to a SID via LsaAddAccountRights */
+
+static NTSTATUS cmd_lsa_add_acct_rights(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND dom_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DOM_SID sid;
+
+ if (argc < 3 ) {
+ printf("Usage: %s SID [rights...]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = name_to_sid(cli, mem_ctx, &sid, argv[1]);
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_open_policy2(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &dom_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_add_account_rights(cli, mem_ctx, &dom_pol, sid,
+ argc-2, argv+2);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ done:
+ return result;
+}
+
+
+/* remove some privileges to a SID via LsaRemoveAccountRights */
+
+static NTSTATUS cmd_lsa_remove_acct_rights(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND dom_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DOM_SID sid;
+
+ if (argc < 3 ) {
+ printf("Usage: %s SID [rights...]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = name_to_sid(cli, mem_ctx, &sid, argv[1]);
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_open_policy2(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &dom_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_remove_account_rights(cli, mem_ctx, &dom_pol, sid,
+ False, argc-2, argv+2);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ done:
+ return result;
+}
+
+
+/* Get a privilege value given its name */
+
+static NTSTATUS cmd_lsa_lookupprivvalue(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ LUID luid;
+
+ if (argc != 2 ) {
+ printf("Usage: %s name\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = cli_lsa_open_policy2(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_lookupprivvalue(cli, mem_ctx, &pol, argv[1], &luid);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Print results */
+
+ printf("%u:%u (0x%x:0x%x)\n", luid.high, luid.low, luid.high, luid.low);
+
+ done:
+ return result;
+}
+
+/* Query LSA security object */
+
+static NTSTATUS cmd_lsa_query_secobj(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ SEC_DESC_BUF *sdb;
+ uint32 sec_info = 0x00000004; /* ??? */
+
+ if (argc != 1 ) {
+ printf("Usage: %s\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = cli_lsa_open_policy2(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_query_secobj(cli, mem_ctx, &pol, sec_info, &sdb);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Print results */
+
+ display_sec_desc(sdb->sec);
+
+ done:
+ return result;
+}
+
+
+/* List of commands exported by this module */
+
+struct cmd_set lsarpc_commands[] = {
+
+ { "LSARPC" },
+
+ { "lsaquery", RPC_RTYPE_NTSTATUS, cmd_lsa_query_info_policy, NULL, PI_LSARPC, "Query info policy", "" },
+ { "lookupsids", RPC_RTYPE_NTSTATUS, cmd_lsa_lookup_sids, NULL, PI_LSARPC, "Convert SIDs to names", "" },
+ { "lookupnames", RPC_RTYPE_NTSTATUS, cmd_lsa_lookup_names, NULL, PI_LSARPC, "Convert names to SIDs", "" },
+ { "enumtrust", RPC_RTYPE_NTSTATUS, cmd_lsa_enum_trust_dom, NULL, PI_LSARPC, "Enumerate trusted domains", "Usage: [preferred max number] [enum context (0)]" },
+ { "enumprivs", RPC_RTYPE_NTSTATUS, cmd_lsa_enum_privilege, NULL, PI_LSARPC, "Enumerate privileges", "" },
+ { "getdispname", RPC_RTYPE_NTSTATUS, cmd_lsa_get_dispname, NULL, PI_LSARPC, "Get the privilege name", "" },
+ { "lsaenumsid", RPC_RTYPE_NTSTATUS, cmd_lsa_enum_sids, NULL, PI_LSARPC, "Enumerate the LSA SIDS", "" },
+ { "lsacreateaccount", RPC_RTYPE_NTSTATUS, cmd_lsa_create_account, NULL, PI_LSARPC, "Create a new lsa account", "" },
+ { "lsaenumprivsaccount", RPC_RTYPE_NTSTATUS, cmd_lsa_enum_privsaccounts, NULL, PI_LSARPC, "Enumerate the privileges of an SID", "" },
+ { "lsaenumacctrights", RPC_RTYPE_NTSTATUS, cmd_lsa_enum_acct_rights, NULL, PI_LSARPC, "Enumerate the rights of an SID", "" },
+ { "lsaaddacctrights", RPC_RTYPE_NTSTATUS, cmd_lsa_add_acct_rights, NULL, PI_LSARPC, "Add rights to an account", "" },
+ { "lsaremoveacctrights", RPC_RTYPE_NTSTATUS, cmd_lsa_remove_acct_rights, NULL, PI_LSARPC, "Remove rights from an account", "" },
+ { "lsalookupprivvalue", RPC_RTYPE_NTSTATUS, cmd_lsa_lookupprivvalue, NULL, PI_LSARPC, "Get a privilege value given its name", "" },
+ { "lsaquerysecobj", RPC_RTYPE_NTSTATUS, cmd_lsa_query_secobj, NULL, PI_LSARPC, "Query LSA security object", "" },
+
+ { NULL }
+};
diff --git a/source/rpcclient/cmd_netlogon.c b/source/rpcclient/cmd_netlogon.c
new file mode 100644
index 00000000000..9e281fefce4
--- /dev/null
+++ b/source/rpcclient/cmd_netlogon.c
@@ -0,0 +1,345 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ Copyright (C) Tim Potter 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "rpcclient.h"
+
+static NTSTATUS cmd_netlogon_logon_ctrl2(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ uint32 query_level = 1;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (argc > 1) {
+ fprintf(stderr, "Usage: %s\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = cli_netlogon_logon_ctrl2(cli, mem_ctx, query_level);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Display results */
+
+ done:
+ return result;
+}
+
+static NTSTATUS cmd_netlogon_getdcname(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ fstring dcname;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s domainname\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = cli_netlogon_getdcname(cli, mem_ctx, argv[1], dcname);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Display results */
+
+ printf("%s\n", dcname);
+
+ done:
+ return result;
+}
+
+static NTSTATUS cmd_netlogon_logon_ctrl(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+#if 0
+ uint32 query_level = 1;
+#endif
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (argc > 1) {
+ fprintf(stderr, "Usage: %s\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+#if 0
+ result = cli_netlogon_logon_ctrl(cli, mem_ctx, query_level);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+#endif
+
+ /* Display results */
+
+ return result;
+}
+
+/* Display sam synchronisation information */
+
+static void display_sam_sync(uint32 num_deltas, SAM_DELTA_HDR *hdr_deltas,
+ SAM_DELTA_CTR *deltas)
+{
+ fstring name;
+ uint32 i, j;
+
+ for (i = 0; i < num_deltas; i++) {
+ switch (hdr_deltas[i].type) {
+ case SAM_DELTA_DOMAIN_INFO:
+ unistr2_to_ascii(name,
+ &deltas[i].domain_info.uni_dom_name,
+ sizeof(name) - 1);
+ printf("Domain: %s\n", name);
+ break;
+ case SAM_DELTA_GROUP_INFO:
+ unistr2_to_ascii(name,
+ &deltas[i].group_info.uni_grp_name,
+ sizeof(name) - 1);
+ printf("Group: %s\n", name);
+ break;
+ case SAM_DELTA_ACCOUNT_INFO:
+ unistr2_to_ascii(name,
+ &deltas[i].account_info.uni_acct_name,
+ sizeof(name) - 1);
+ printf("Account: %s\n", name);
+ break;
+ case SAM_DELTA_ALIAS_INFO:
+ unistr2_to_ascii(name,
+ &deltas[i].alias_info.uni_als_name,
+ sizeof(name) - 1);
+ printf("Alias: %s\n", name);
+ break;
+ case SAM_DELTA_ALIAS_MEM: {
+ SAM_ALIAS_MEM_INFO *alias = &deltas[i].als_mem_info;
+
+ for (j = 0; j < alias->num_members; j++) {
+ fstring sid_str;
+
+ sid_to_string(sid_str, &alias->sids[j].sid);
+
+ printf("%s\n", sid_str);
+ }
+ break;
+ }
+ case SAM_DELTA_GROUP_MEM: {
+ SAM_GROUP_MEM_INFO *group = &deltas[i].grp_mem_info;
+
+ for (j = 0; j < group->num_members; j++)
+ printf("rid 0x%x, attrib 0x%08x\n",
+ group->rids[j], group->attribs[j]);
+ break;
+ }
+ case SAM_DELTA_MODIFIED_COUNT: {
+ SAM_DELTA_MOD_COUNT *mc = &deltas[i].mod_count;
+
+ printf("sam sequence update: 0x%04x\n", mc->seqnum);
+ break;
+ }
+ default:
+ printf("unknown delta type 0x%02x\n",
+ hdr_deltas[i].type);
+ break;
+ }
+ }
+}
+
+/* Perform sam synchronisation */
+
+static NTSTATUS cmd_netlogon_sam_sync(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 database_id = 0, num_deltas;
+ SAM_DELTA_HDR *hdr_deltas;
+ SAM_DELTA_CTR *deltas;
+ DOM_CRED ret_creds;
+
+ if (argc > 2) {
+ fprintf(stderr, "Usage: %s [database_id]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc == 2)
+ database_id = atoi(argv[1]);
+
+ /* on first call the returnAuthenticator is empty */
+ memset(&ret_creds, 0, sizeof(ret_creds));
+
+ /* Synchronise sam database */
+
+ result = cli_netlogon_sam_sync(cli, mem_ctx, &ret_creds, database_id,
+ 0, &num_deltas, &hdr_deltas, &deltas);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Display results */
+
+ display_sam_sync(num_deltas, hdr_deltas, deltas);
+
+ done:
+ return result;
+}
+
+/* Perform sam delta synchronisation */
+
+static NTSTATUS cmd_netlogon_sam_deltas(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 database_id, num_deltas, tmp;
+ SAM_DELTA_HDR *hdr_deltas;
+ SAM_DELTA_CTR *deltas;
+ UINT64_S seqnum;
+
+ if (argc != 3) {
+ fprintf(stderr, "Usage: %s database_id seqnum\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ database_id = atoi(argv[1]);
+ tmp = atoi(argv[2]);
+
+ seqnum.low = tmp & 0xffff;
+ seqnum.high = 0;
+
+ result = cli_netlogon_sam_deltas(cli, mem_ctx, database_id,
+ seqnum, &num_deltas,
+ &hdr_deltas, &deltas);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Display results */
+
+ display_sam_sync(num_deltas, hdr_deltas, deltas);
+
+ done:
+ return result;
+}
+
+/* Log on a domain user */
+
+static NTSTATUS cmd_netlogon_sam_logon(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ int logon_type = NET_LOGON_TYPE;
+ const char *username, *password;
+ uint32 neg_flags = 0x000001ff;
+ int auth_level = 2;
+ DOM_CRED ret_creds;
+
+ /* Check arguments */
+
+ if (argc < 3 || argc > 6) {
+ fprintf(stderr, "Usage: samlogon <username> <password> "
+ "[logon_type] [neg flags] [auth level (2 or 3)]\n"
+ "neg flags being 0x000001ff or 0x6007ffff\n");
+ return NT_STATUS_OK;
+ }
+
+ username = argv[1];
+ password = argv[2];
+
+ if (argc == 4)
+ sscanf(argv[3], "%i", &logon_type);
+
+ if (argc == 5)
+ sscanf(argv[4], "%i", &neg_flags);
+
+ if (argc == 6)
+ sscanf(argv[5], "%i", &auth_level);
+
+ /* Perform the sam logon */
+
+ ZERO_STRUCT(ret_creds);
+
+ result = cli_netlogon_sam_logon(cli, mem_ctx, &ret_creds, username, password, logon_type);
+
+ clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), &ret_creds);
+
+ result = cli_netlogon_sam_logon(cli, mem_ctx, &ret_creds, username, password, logon_type);
+
+ clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), &ret_creds);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ done:
+ return result;
+}
+
+/* Change the trust account password */
+
+static NTSTATUS cmd_netlogon_change_trust_pw(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ DOM_CRED ret_creds;
+
+ /* Check arguments */
+
+ if (argc > 1) {
+ fprintf(stderr, "Usage: change_trust_pw");
+ return NT_STATUS_OK;
+ }
+
+ /* Perform the sam logon */
+
+ ZERO_STRUCT(ret_creds);
+
+ result = trust_pw_find_change_and_store_it(cli, mem_ctx,
+ lp_workgroup());
+
+ clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), &ret_creds);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ done:
+ return result;
+}
+
+
+/* List of commands exported by this module */
+
+struct cmd_set netlogon_commands[] = {
+
+ { "NETLOGON" },
+
+ { "logonctrl2", RPC_RTYPE_NTSTATUS, cmd_netlogon_logon_ctrl2, NULL, PI_NETLOGON, "Logon Control 2", "" },
+ { "getdcname", RPC_RTYPE_NTSTATUS, cmd_netlogon_getdcname, NULL, PI_NETLOGON, "Get trusted DC name", "" },
+ { "logonctrl", RPC_RTYPE_NTSTATUS, cmd_netlogon_logon_ctrl, NULL, PI_NETLOGON, "Logon Control", "" },
+ { "samsync", RPC_RTYPE_NTSTATUS, cmd_netlogon_sam_sync, NULL, PI_NETLOGON, "Sam Synchronisation", "" },
+ { "samdeltas", RPC_RTYPE_NTSTATUS, cmd_netlogon_sam_deltas, NULL, PI_NETLOGON, "Query Sam Deltas", "" },
+ { "samlogon", RPC_RTYPE_NTSTATUS, cmd_netlogon_sam_logon, NULL, PI_NETLOGON, "Sam Logon", "" },
+ { "samlogon", RPC_RTYPE_NTSTATUS, cmd_netlogon_change_trust_pw, NULL, PI_NETLOGON, "Change Trust Account Password", "" },
+
+ { NULL }
+};
diff --git a/source/rpcclient/cmd_reg.c b/source/rpcclient/cmd_reg.c
new file mode 100644
index 00000000000..bf85d217160
--- /dev/null
+++ b/source/rpcclient/cmd_reg.c
@@ -0,0 +1,1007 @@
+/*
+ Unix SMB/CIFS implementation.
+ NT Domain Authentication SMB / MSRPC client
+ Copyright (C) Andrew Tridgell 1994-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+ Copyright (C) Simo Sorce 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "rpcclient.h"
+
+/*
+ * keys. of the form:
+ * ----
+ *
+ * [HKLM]|[HKU]\[parent_keyname_components]\[subkey]|[value]
+ *
+ * reg_getsubkey() splits this down into:
+ * [HKLM]|[HKU]\[parent_keyname_components] and [subkey]|[value]
+ *
+ * do_reg_connect() splits the left side down further into:
+ * [HKLM]|[HKU] and [parent_keyname_components].
+ *
+ * HKLM is short for HKEY_LOCAL_MACHINE
+ * HKU is short for HKEY_USERS
+ *
+ * oh, and HKEY stands for "Hive Key".
+ *
+ */
+
+#if 0 /* Simo: reg functions need to be updated to the new cmd interface */
+
+/****************************************************************************
+nt registry enum
+****************************************************************************/
+static void cmd_reg_enum(struct client_info *info)
+{
+ BOOL res = True;
+ BOOL res1 = True;
+ BOOL res2 = True;
+ int i;
+
+ POLICY_HND key_pol;
+ fstring full_keyname;
+ fstring key_name;
+
+ /*
+ * query key info
+ */
+
+ fstring key_class;
+ uint32 max_class_len = 0;
+ uint32 num_subkeys;
+ uint32 max_subkeylen;
+ uint32 max_subkeysize;
+ uint32 num_values;
+ uint32 max_valnamelen;
+ uint32 max_valbufsize;
+ uint32 sec_desc;
+ NTTIME mod_time;
+
+ /*
+ * unknown 0x1a request
+ */
+
+ uint32 unk_1a_response;
+
+ DEBUG(5, ("cmd_reg_enum: smb_cli->fd:%d\n", smb_cli->fd));
+
+ if (!next_token_nr(NULL, full_keyname, NULL, sizeof(full_keyname)))
+ {
+ fprintf(out_hnd, "regenum <key_name>\n");
+ return;
+ }
+
+ /* open WINREG session. */
+ res = res ? cli_nt_session_open(smb_cli, PI_WINREG) : False;
+
+ /* open registry receive a policy handle */
+ res = res ? do_reg_connect(smb_cli, full_keyname, key_name,
+ &info->dom.reg_pol_connect) : False;
+
+ if ((*key_name) != 0)
+ {
+ /* open an entry */
+ res1 = res ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+ key_name, 0x02000000, &key_pol) : False;
+ }
+ else
+ {
+ memcpy(&key_pol, &info->dom.reg_pol_connect, sizeof(key_pol));
+ }
+
+ res1 = res1 ? do_reg_query_key(smb_cli,
+ &key_pol,
+ key_class, &max_class_len,
+ &num_subkeys, &max_subkeylen, &max_subkeysize,
+ &num_values, &max_valnamelen, &max_valbufsize,
+ &sec_desc, &mod_time) : False;
+
+ if (res1 && num_subkeys > 0)
+ {
+ fprintf(out_hnd,"Subkeys\n");
+ fprintf(out_hnd,"-------\n");
+ }
+
+ for (i = 0; i < num_subkeys; i++)
+ {
+ /*
+ * enumerate key
+ */
+
+ fstring enum_name;
+ uint32 enum_unk1;
+ uint32 enum_unk2;
+ time_t key_mod_time;
+
+ /* unknown 1a it */
+ res2 = res1 ? do_reg_unknown_1a(smb_cli, &key_pol,
+ &unk_1a_response) : False;
+
+ if (res2 && unk_1a_response != 5)
+ {
+ fprintf(out_hnd,"Unknown 1a response: %x\n", unk_1a_response);
+ }
+
+ /* enum key */
+ res2 = res2 ? do_reg_enum_key(smb_cli, &key_pol,
+ i, enum_name,
+ &enum_unk1, &enum_unk2,
+ &key_mod_time) : False;
+
+ if (res2)
+ {
+ display_reg_key_info(out_hnd, ACTION_HEADER , enum_name, key_mod_time);
+ display_reg_key_info(out_hnd, ACTION_ENUMERATE, enum_name, key_mod_time);
+ display_reg_key_info(out_hnd, ACTION_FOOTER , enum_name, key_mod_time);
+ }
+
+ }
+
+ if (num_values > 0)
+ {
+ fprintf(out_hnd,"Key Values\n");
+ fprintf(out_hnd,"----------\n");
+ }
+
+ for (i = 0; i < num_values; i++)
+ {
+ /*
+ * enumerate key
+ */
+
+ uint32 val_type;
+ BUFFER2 value;
+ fstring val_name;
+
+ /* unknown 1a it */
+ res2 = res1 ? do_reg_unknown_1a(smb_cli, &key_pol,
+ &unk_1a_response) : False;
+
+ if (res2 && unk_1a_response != 5)
+ {
+ fprintf(out_hnd,"Unknown 1a response: %x\n", unk_1a_response);
+ }
+
+ /* enum key */
+ res2 = res2 ? do_reg_enum_val(smb_cli, &key_pol,
+ i, max_valnamelen, max_valbufsize,
+ val_name, &val_type, &value) : False;
+
+ if (res2)
+ {
+ display_reg_value_info(out_hnd, ACTION_HEADER , val_name, val_type, &value);
+ display_reg_value_info(out_hnd, ACTION_ENUMERATE, val_name, val_type, &value);
+ display_reg_value_info(out_hnd, ACTION_FOOTER , val_name, val_type, &value);
+ }
+ }
+
+ /* close the handles */
+ if ((*key_name) != 0)
+ {
+ res1 = res1 ? do_reg_close(smb_cli, &key_pol) : False;
+ }
+ res = res ? do_reg_close(smb_cli, &info->dom.reg_pol_connect) : False;
+
+ /* close the session */
+ cli_nt_session_close(smb_cli);
+
+ if (res && res1 && res2)
+ {
+ DEBUG(5,("cmd_reg_enum: query succeeded\n"));
+ }
+ else
+ {
+ DEBUG(5,("cmd_reg_enum: query failed\n"));
+ }
+}
+
+/****************************************************************************
+nt registry query key
+****************************************************************************/
+static void cmd_reg_query_key(struct client_info *info)
+{
+ BOOL res = True;
+ BOOL res1 = True;
+
+ POLICY_HND key_pol;
+ fstring full_keyname;
+ fstring key_name;
+
+ /*
+ * query key info
+ */
+
+ fstring key_class;
+ uint32 key_class_len = 0;
+ uint32 num_subkeys;
+ uint32 max_subkeylen;
+ uint32 max_subkeysize;
+ uint32 num_values;
+ uint32 max_valnamelen;
+ uint32 max_valbufsize;
+ uint32 sec_desc;
+ NTTIME mod_time;
+
+ DEBUG(5, ("cmd_reg_enum: smb_cli->fd:%d\n", smb_cli->fd));
+
+ if (!next_token_nr(NULL, full_keyname, NULL, sizeof(full_keyname)))
+ {
+ fprintf(out_hnd, "regquery key_name\n");
+ return;
+ }
+
+ /* open WINREG session. */
+ res = res ? cli_nt_session_open(smb_cli, PI_WINREG) : False;
+
+ /* open registry receive a policy handle */
+ res = res ? do_reg_connect(smb_cli, full_keyname, key_name,
+ &info->dom.reg_pol_connect) : False;
+
+ if ((*key_name) != 0)
+ {
+ /* open an entry */
+ res1 = res ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+ key_name, 0x02000000, &key_pol) : False;
+ }
+ else
+ {
+ memcpy(&key_pol, &info->dom.reg_pol_connect, sizeof(key_pol));
+ }
+
+ res1 = res1 ? do_reg_query_key(smb_cli,
+ &key_pol,
+ key_class, &key_class_len,
+ &num_subkeys, &max_subkeylen, &max_subkeysize,
+ &num_values, &max_valnamelen, &max_valbufsize,
+ &sec_desc, &mod_time) : False;
+
+ if (res1 && key_class_len != 0)
+ {
+ res1 = res1 ? do_reg_query_key(smb_cli,
+ &key_pol,
+ key_class, &key_class_len,
+ &num_subkeys, &max_subkeylen, &max_subkeysize,
+ &num_values, &max_valnamelen, &max_valbufsize,
+ &sec_desc, &mod_time) : False;
+ }
+
+ if (res1)
+ {
+ fprintf(out_hnd,"Registry Query Info Key\n");
+ fprintf(out_hnd,"key class: %s\n", key_class);
+ fprintf(out_hnd,"subkeys, max_len, max_size: %d %d %d\n", num_subkeys, max_subkeylen, max_subkeysize);
+ fprintf(out_hnd,"vals, max_len, max_size: 0x%x 0x%x 0x%x\n", num_values, max_valnamelen, max_valbufsize);
+ fprintf(out_hnd,"sec desc: 0x%x\n", sec_desc);
+ fprintf(out_hnd,"mod time: %s\n", http_timestring(nt_time_to_unix(&mod_time)));
+ }
+
+ /* close the handles */
+ if ((*key_name) != 0)
+ {
+ res1 = res1 ? do_reg_close(smb_cli, &key_pol) : False;
+ }
+ res = res ? do_reg_close(smb_cli, &info->dom.reg_pol_connect) : False;
+
+ /* close the session */
+ cli_nt_session_close(smb_cli);
+
+ if (res && res1)
+ {
+ DEBUG(5,("cmd_reg_query: query succeeded\n"));
+ }
+ else
+ {
+ DEBUG(5,("cmd_reg_query: query failed\n"));
+ }
+}
+
+/****************************************************************************
+nt registry create value
+****************************************************************************/
+static void cmd_reg_create_val(struct client_info *info)
+{
+ BOOL res = True;
+ BOOL res3 = True;
+ BOOL res4 = True;
+
+ POLICY_HND parent_pol;
+ fstring full_keyname;
+ fstring keyname;
+ fstring parent_name;
+ fstring val_name;
+ fstring tmp;
+ uint32 val_type;
+ BUFFER3 value;
+
+#if 0
+ uint32 unk_0;
+ uint32 unk_1;
+ /* query it */
+ res1 = res1 ? do_reg_query_info(smb_cli, &val_pol,
+ val_name, *val_type) : False;
+#endif
+
+ DEBUG(5, ("cmd_reg_create_val: smb_cli->fd:%d\n", smb_cli->fd));
+
+ if (!next_token_nr(NULL, full_keyname, NULL, sizeof(full_keyname)))
+ {
+ fprintf(out_hnd, "regcreate <val_name> <val_type> <val>\n");
+ return;
+ }
+
+ reg_get_subkey(full_keyname, keyname, val_name);
+
+ if (keyname[0] == 0 || val_name[0] == 0)
+ {
+ fprintf(out_hnd, "invalid key name\n");
+ return;
+ }
+
+ if (!next_token_nr(NULL, tmp, NULL, sizeof(tmp)))
+ {
+ fprintf(out_hnd, "regcreate <val_name> <val_type (1|4)> <val>\n");
+ return;
+ }
+
+ val_type = atoi(tmp);
+
+ if (val_type != 1 && val_type != 3 && val_type != 4)
+ {
+ fprintf(out_hnd, "val_type 1=UNISTR, 3=BYTES, 4=DWORD supported\n");
+ return;
+ }
+
+ if (!next_token_nr(NULL, tmp, NULL, sizeof(tmp)))
+ {
+ fprintf(out_hnd, "regcreate <val_name> <val_type (1|4)> <val>\n");
+ return;
+ }
+
+ switch (val_type)
+ {
+ case 0x01: /* UNISTR */
+ {
+ init_buffer3_str(&value, tmp, strlen(tmp)+1);
+ break;
+ }
+ case 0x03: /* BYTES */
+ {
+ init_buffer3_hex(&value, tmp);
+ break;
+ }
+ case 0x04: /* DWORD */
+ {
+ uint32 tmp_val;
+ if (strnequal(tmp, "0x", 2))
+ {
+ tmp_val = strtol(tmp, (char**)NULL, 16);
+ }
+ else
+ {
+ tmp_val = strtol(tmp, (char**)NULL, 10);
+ }
+ init_buffer3_uint32(&value, tmp_val);
+ break;
+ }
+ default:
+ {
+ fprintf(out_hnd, "i told you i only deal with UNISTR, DWORD and BYTES!\n");
+ return;
+ }
+ }
+
+ DEBUG(10,("key data:\n"));
+ dump_data(10, (char *)value.buffer, value.buf_len);
+
+ /* open WINREG session. */
+ res = res ? cli_nt_session_open(smb_cli, PI_WINREG) : False;
+
+ /* open registry receive a policy handle */
+ res = res ? do_reg_connect(smb_cli, keyname, parent_name,
+ &info->dom.reg_pol_connect) : False;
+
+ if ((*val_name) != 0)
+ {
+ /* open an entry */
+ res3 = res ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+ parent_name, 0x02000000, &parent_pol) : False;
+ }
+ else
+ {
+ memcpy(&parent_pol, &info->dom.reg_pol_connect, sizeof(parent_pol));
+ }
+
+ /* create an entry */
+ res4 = res3 ? do_reg_create_val(smb_cli, &parent_pol,
+ val_name, val_type, &value) : False;
+
+ /* flush the modified key */
+ res4 = res4 ? do_reg_flush_key(smb_cli, &parent_pol) : False;
+
+ /* close the val handle */
+ if ((*val_name) != 0)
+ {
+ res3 = res3 ? do_reg_close(smb_cli, &parent_pol) : False;
+ }
+
+ /* close the registry handles */
+ res = res ? do_reg_close(smb_cli, &info->dom.reg_pol_connect) : False;
+
+ /* close the session */
+ cli_nt_session_close(smb_cli);
+
+ if (res && res3 && res4)
+ {
+ DEBUG(5,("cmd_reg_create_val: query succeeded\n"));
+ fprintf(out_hnd,"OK\n");
+ }
+ else
+ {
+ DEBUG(5,("cmd_reg_create_val: query failed\n"));
+ }
+}
+
+/****************************************************************************
+nt registry delete value
+****************************************************************************/
+static void cmd_reg_delete_val(struct client_info *info)
+{
+ BOOL res = True;
+ BOOL res3 = True;
+ BOOL res4 = True;
+
+ POLICY_HND parent_pol;
+ fstring full_keyname;
+ fstring keyname;
+ fstring parent_name;
+ fstring val_name;
+
+ DEBUG(5, ("cmd_reg_delete_val: smb_cli->fd:%d\n", smb_cli->fd));
+
+ if (!next_token_nr(NULL, full_keyname, NULL, sizeof(full_keyname)))
+ {
+ fprintf(out_hnd, "regdelete <val_name>\n");
+ return;
+ }
+
+ reg_get_subkey(full_keyname, keyname, val_name);
+
+ if (keyname[0] == 0 || val_name[0] == 0)
+ {
+ fprintf(out_hnd, "invalid key name\n");
+ return;
+ }
+
+ /* open WINREG session. */
+ res = res ? cli_nt_session_open(smb_cli, PI_WINREG) : False;
+
+ /* open registry receive a policy handle */
+ res = res ? do_reg_connect(smb_cli, keyname, parent_name,
+ &info->dom.reg_pol_connect) : False;
+
+ if ((*val_name) != 0)
+ {
+ /* open an entry */
+ res3 = res ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+ parent_name, 0x02000000, &parent_pol) : False;
+ }
+ else
+ {
+ memcpy(&parent_pol, &info->dom.reg_pol_connect, sizeof(parent_pol));
+ }
+
+ /* delete an entry */
+ res4 = res3 ? do_reg_delete_val(smb_cli, &parent_pol, val_name) : False;
+
+ /* flush the modified key */
+ res4 = res4 ? do_reg_flush_key(smb_cli, &parent_pol) : False;
+
+ /* close the key handle */
+ res3 = res3 ? do_reg_close(smb_cli, &parent_pol) : False;
+
+ /* close the registry handles */
+ res = res ? do_reg_close(smb_cli, &info->dom.reg_pol_connect) : False;
+
+ /* close the session */
+ cli_nt_session_close(smb_cli);
+
+ if (res && res3 && res4)
+ {
+ DEBUG(5,("cmd_reg_delete_val: query succeeded\n"));
+ fprintf(out_hnd,"OK\n");
+ }
+ else
+ {
+ DEBUG(5,("cmd_reg_delete_val: query failed\n"));
+ }
+}
+
+/****************************************************************************
+nt registry delete key
+****************************************************************************/
+static void cmd_reg_delete_key(struct client_info *info)
+{
+ BOOL res = True;
+ BOOL res3 = True;
+ BOOL res4 = True;
+
+ POLICY_HND parent_pol;
+ fstring full_keyname;
+ fstring parent_name;
+ fstring key_name;
+ fstring subkey_name;
+
+ DEBUG(5, ("cmd_reg_delete_key: smb_cli->fd:%d\n", smb_cli->fd));
+
+ if (!next_token_nr(NULL, full_keyname, NULL, sizeof(full_keyname)))
+ {
+ fprintf(out_hnd, "regdeletekey <key_name>\n");
+ return;
+ }
+
+ reg_get_subkey(full_keyname, parent_name, subkey_name);
+
+ if (parent_name[0] == 0 || subkey_name[0] == 0)
+ {
+ fprintf(out_hnd, "invalid key name\n");
+ return;
+ }
+
+ /* open WINREG session. */
+ res = res ? cli_nt_session_open(smb_cli, PI_WINREG) : False;
+
+ /* open registry receive a policy handle */
+ res = res ? do_reg_connect(smb_cli, parent_name, key_name,
+ &info->dom.reg_pol_connect) : False;
+
+ if ((*key_name) != 0)
+ {
+ /* open an entry */
+ res3 = res ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+ key_name, 0x02000000, &parent_pol) : False;
+ }
+ else
+ {
+ memcpy(&parent_pol, &info->dom.reg_pol_connect, sizeof(parent_pol));
+ }
+
+ /* create an entry */
+ res4 = res3 ? do_reg_delete_key(smb_cli, &parent_pol, subkey_name) : False;
+
+ /* flush the modified key */
+ res4 = res4 ? do_reg_flush_key(smb_cli, &parent_pol) : False;
+
+ /* close the key handle */
+ if ((*key_name) != 0)
+ {
+ res3 = res3 ? do_reg_close(smb_cli, &parent_pol) : False;
+ }
+
+ /* close the registry handles */
+ res = res ? do_reg_close(smb_cli, &info->dom.reg_pol_connect) : False;
+
+ /* close the session */
+ cli_nt_session_close(smb_cli);
+
+ if (res && res3 && res4)
+ {
+ DEBUG(5,("cmd_reg_delete_key: query succeeded\n"));
+ fprintf(out_hnd,"OK\n");
+ }
+ else
+ {
+ DEBUG(5,("cmd_reg_delete_key: query failed\n"));
+ }
+}
+
+/****************************************************************************
+nt registry create key
+****************************************************************************/
+static void cmd_reg_create_key(struct client_info *info)
+{
+ BOOL res = True;
+ BOOL res3 = True;
+ BOOL res4 = True;
+
+ POLICY_HND parent_pol;
+ POLICY_HND key_pol;
+ fstring full_keyname;
+ fstring parent_key;
+ fstring parent_name;
+ fstring key_name;
+ fstring key_class;
+ SEC_ACCESS sam_access;
+
+ DEBUG(5, ("cmd_reg_create_key: smb_cli->fd:%d\n", smb_cli->fd));
+
+ if (!next_token_nr(NULL, full_keyname, NULL, sizeof(full_keyname)))
+ {
+ fprintf(out_hnd, "regcreate <key_name> [key_class]\n");
+ return;
+ }
+
+ reg_get_subkey(full_keyname, parent_key, key_name);
+
+ if (parent_key[0] == 0 || key_name[0] == 0)
+ {
+ fprintf(out_hnd, "invalid key name\n");
+ return;
+ }
+
+ if (!next_token_nr(NULL, key_class, NULL, sizeof(key_class)))
+ {
+ memset(key_class, 0, sizeof(key_class));
+ }
+
+ /* set access permissions */
+ sam_access.mask = SEC_RIGHTS_READ;
+
+ /* open WINREG session. */
+ res = res ? cli_nt_session_open(smb_cli, PI_WINREG) : False;
+
+ /* open registry receive a policy handle */
+ res = res ? do_reg_connect(smb_cli, parent_key, parent_name,
+ &info->dom.reg_pol_connect) : False;
+
+ if ((*parent_name) != 0)
+ {
+ /* open an entry */
+ res3 = res ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+ parent_name, 0x02000000, &parent_pol) : False;
+ }
+ else
+ {
+ memcpy(&parent_pol, &info->dom.reg_pol_connect, sizeof(parent_pol));
+ }
+
+ /* create an entry */
+ res4 = res3 ? do_reg_create_key(smb_cli, &parent_pol,
+ key_name, key_class, &sam_access, &key_pol) : False;
+
+ /* flush the modified key */
+ res4 = res4 ? do_reg_flush_key(smb_cli, &parent_pol) : False;
+
+ /* close the key handle */
+ res4 = res4 ? do_reg_close(smb_cli, &key_pol) : False;
+
+ /* close the key handle */
+ if ((*parent_name) != 0)
+ {
+ res3 = res3 ? do_reg_close(smb_cli, &parent_pol) : False;
+ }
+
+ /* close the registry handles */
+ res = res ? do_reg_close(smb_cli, &info->dom.reg_pol_connect) : False;
+
+ /* close the session */
+ cli_nt_session_close(smb_cli);
+
+ if (res && res3 && res4)
+ {
+ DEBUG(5,("cmd_reg_create_key: query succeeded\n"));
+ fprintf(out_hnd,"OK\n");
+ }
+ else
+ {
+ DEBUG(5,("cmd_reg_create_key: query failed\n"));
+ }
+}
+
+/****************************************************************************
+nt registry security info
+****************************************************************************/
+static void cmd_reg_test_key_sec(struct client_info *info)
+{
+ BOOL res = True;
+ BOOL res3 = True;
+ BOOL res4 = True;
+
+ POLICY_HND key_pol;
+ fstring full_keyname;
+ fstring key_name;
+
+ /*
+ * security info
+ */
+
+ uint32 sec_buf_size;
+ SEC_DESC_BUF *psdb;
+
+ DEBUG(5, ("cmd_reg_get_key_sec: smb_cli->fd:%d\n", smb_cli->fd));
+
+ if (!next_token_nr(NULL, full_keyname, NULL, sizeof(full_keyname)))
+ {
+ fprintf(out_hnd, "reggetsec <key_name>\n");
+ return;
+ }
+
+ /* open WINREG session. */
+ res = res ? cli_nt_session_open(smb_cli, PI_WINREG) : False;
+
+ /* open registry receive a policy handle */
+ res = res ? do_reg_connect(smb_cli, full_keyname, key_name,
+ &info->dom.reg_pol_connect) : False;
+
+ if ((*key_name) != 0)
+ {
+ /* open an entry */
+ res3 = res ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+ key_name, 0x02000000, &key_pol) : False;
+ }
+ else
+ {
+ memcpy(&key_pol, &info->dom.reg_pol_connect, sizeof(key_pol));
+ }
+
+ /* open an entry */
+ res3 = res ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+ key_name, 0x02000000, &key_pol) : False;
+
+ /* query key sec info. first call sets sec_buf_size. */
+
+ sec_buf_size = 0;
+ res4 = res3 ? do_reg_get_key_sec(smb_cli, &key_pol,
+ &sec_buf_size, &psdb) : False;
+
+ free_sec_desc_buf(&psdb);
+
+ res4 = res4 ? do_reg_get_key_sec(smb_cli, &key_pol,
+ &sec_buf_size, &psdb) : False;
+
+ if (res4 && psdb->len > 0 && psdb->sec != NULL)
+ {
+ display_sec_desc(out_hnd, ACTION_HEADER , psdb->sec);
+ display_sec_desc(out_hnd, ACTION_ENUMERATE, psdb->sec);
+ display_sec_desc(out_hnd, ACTION_FOOTER , psdb->sec);
+
+ res4 = res4 ? do_reg_set_key_sec(smb_cli, &key_pol, psdb) : False;
+ }
+
+ free_sec_desc_buf(&psdb);
+
+ /* close the key handle */
+ if ((*key_name) != 0)
+ {
+ res3 = res3 ? do_reg_close(smb_cli, &key_pol) : False;
+ }
+
+ /* close the registry handles */
+ res = res ? do_reg_close(smb_cli, &info->dom.reg_pol_connect) : False;
+
+ /* close the session */
+ cli_nt_session_close(smb_cli);
+
+ if (res && res3 && res4)
+ {
+ DEBUG(5,("cmd_reg_test2: query succeeded\n"));
+ fprintf(out_hnd,"Registry Test2\n");
+ }
+ else
+ {
+ DEBUG(5,("cmd_reg_test2: query failed\n"));
+ }
+}
+
+/****************************************************************************
+nt registry security info
+****************************************************************************/
+static void cmd_reg_get_key_sec(struct client_info *info)
+{
+ BOOL res = True;
+ BOOL res3 = True;
+ BOOL res4 = True;
+
+ POLICY_HND key_pol;
+ fstring full_keyname;
+ fstring key_name;
+
+ /*
+ * security info
+ */
+
+ uint32 sec_buf_size;
+ SEC_DESC_BUF *psdb;
+
+ DEBUG(5, ("cmd_reg_get_key_sec: smb_cli->fd:%d\n", smb_cli->fd));
+
+ if (!next_token_nr(NULL, full_keyname, NULL, sizeof(full_keyname)))
+ {
+ fprintf(out_hnd, "reggetsec <key_name>\n");
+ return;
+ }
+
+ /* open WINREG session. */
+ res = res ? cli_nt_session_open(smb_cli, PI_WINREG) : False;
+
+ /* open registry receive a policy handle */
+ res = res ? do_reg_connect(smb_cli, full_keyname, key_name,
+ &info->dom.reg_pol_connect) : False;
+
+ if ((*key_name) != 0)
+ {
+ /* open an entry */
+ res3 = res ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+ key_name, 0x02000000, &key_pol) : False;
+ }
+ else
+ {
+ memcpy(&key_pol, &info->dom.reg_pol_connect, sizeof(key_pol));
+ }
+
+ /* open an entry */
+ res3 = res ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+ key_name, 0x02000000, &key_pol) : False;
+
+ /* Get the size. */
+ sec_buf_size = 0;
+ res4 = res3 ? do_reg_get_key_sec(smb_cli, &key_pol,
+ &sec_buf_size, &psdb) : False;
+
+ free_sec_desc_buf(&psdb);
+
+ res4 = res4 ? do_reg_get_key_sec(smb_cli, &key_pol,
+ &sec_buf_size, &psdb) : False;
+
+ if (res4 && psdb->len > 0 && psdb->sec != NULL)
+ {
+ display_sec_desc(out_hnd, ACTION_HEADER , psdb->sec);
+ display_sec_desc(out_hnd, ACTION_ENUMERATE, psdb->sec);
+ display_sec_desc(out_hnd, ACTION_FOOTER , psdb->sec);
+ }
+
+ free_sec_desc_buf(&psdb);
+
+ /* close the key handle */
+ if ((*key_name) != 0)
+ {
+ res3 = res3 ? do_reg_close(smb_cli, &key_pol) : False;
+ }
+
+ /* close the registry handles */
+ res = res ? do_reg_close(smb_cli, &info->dom.reg_pol_connect) : False;
+
+ /* close the session */
+ cli_nt_session_close(smb_cli);
+
+ if (res && res3 && res4)
+ {
+ DEBUG(5,("cmd_reg_get_key_sec: query succeeded\n"));
+ }
+ else
+ {
+ DEBUG(5,("cmd_reg_get_key_sec: query failed\n"));
+ }
+}
+
+#endif /* 0 */
+
+/****************************************************************************
+nt registry shutdown
+****************************************************************************/
+static NTSTATUS cmd_reg_shutdown(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ fstring msg;
+ uint32 timeout = 20;
+ BOOL force = False;
+ BOOL reboot = False;
+ int opt;
+
+ *msg = 0;
+ optind = 0; /* TODO: test if this hack works on other systems too --simo */
+
+ while ((opt = getopt(argc, argv, "m:t:rf")) != EOF)
+ {
+ /*fprintf (stderr, "[%s]\n", argv[argc-1]);*/
+
+ switch (opt)
+ {
+ case 'm':
+ fstrcpy(msg, optarg);
+ /*fprintf (stderr, "[%s|%s]\n", optarg, msg);*/
+ break;
+
+ case 't':
+ timeout = atoi(optarg);
+ /*fprintf (stderr, "[%s|%d]\n", optarg, timeout);*/
+ break;
+
+ case 'r':
+ reboot = True;
+ break;
+
+ case 'f':
+ force = True;
+ break;
+
+ }
+ }
+
+ /* create an entry */
+ result = cli_reg_shutdown(cli, mem_ctx, msg, timeout, reboot, force);
+
+ if (NT_STATUS_IS_OK(result))
+ DEBUG(5,("cmd_reg_shutdown: query succeeded\n"));
+ else
+ DEBUG(5,("cmd_reg_shutdown: query failed\n"));
+
+ return result;
+}
+
+/****************************************************************************
+abort a shutdown
+****************************************************************************/
+static NTSTATUS cmd_reg_abort_shutdown(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ result = cli_reg_abort_shutdown(cli, mem_ctx);
+
+ if (NT_STATUS_IS_OK(result))
+ DEBUG(5,("cmd_reg_abort_shutdown: query succeeded\n"));
+ else
+ DEBUG(5,("cmd_reg_abort_shutdown: query failed\n"));
+
+ return result;
+}
+
+
+/* List of commands exported by this module */
+struct cmd_set reg_commands[] = {
+
+ { "REG" },
+
+ { "shutdown", RPC_RTYPE_NTSTATUS, cmd_reg_shutdown, NULL, PI_WINREG, "Remote Shutdown",
+ "syntax: shutdown [-m message] [-t timeout] [-r] [-h] [-f] (-r == reboot, -h == halt, -f == force)" },
+
+ { "abortshutdown", RPC_RTYPE_NTSTATUS, cmd_reg_abort_shutdown, NULL, PI_WINREG, "Abort Shutdown",
+ "syntax: abortshutdown" },
+/*
+ { "regenum", cmd_reg_enum, "Registry Enumeration",
+ "<keyname>" },
+
+ { "regdeletekey", cmd_reg_delete_key, "Registry Key Delete",
+ "<keyname>" },
+
+ { "regcreatekey", cmd_reg_create_key, "Registry Key Create",
+ "<keyname> [keyclass]" },
+
+ { "regqueryval", cmd_reg_query_info, "Registry Value Query",
+ "<valname>" },
+
+ { "regquerykey", cmd_reg_query_key, "Registry Key Query",
+ "<keyname>" },
+
+ { "regdeleteval", cmd_reg_delete_val, "Registry Value Delete",
+ "<valname>" },
+
+ { "regcreateval", cmd_reg_create_val, "Registry Key Create",
+ "<valname> <valtype> <value>" },
+
+ { "reggetsec", cmd_reg_get_key_sec, "Registry Key Security",
+ "<keyname>" },
+
+ { "regtestsec", cmd_reg_test_key_sec, "Test Registry Key Security",
+ "<keyname>" },
+*/
+ { NULL }
+};
diff --git a/source/rpcclient/cmd_samr.c b/source/rpcclient/cmd_samr.c
new file mode 100644
index 00000000000..ed06a5a4a07
--- /dev/null
+++ b/source/rpcclient/cmd_samr.c
@@ -0,0 +1,1562 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ Copyright (C) Andrew Tridgell 1992-2000,
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+ Copyright (C) Elrond 2000,
+ Copyright (C) Tim Potter 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "rpcclient.h"
+
+extern DOM_SID domain_sid;
+
+/****************************************************************************
+ display sam_user_info_21 structure
+ ****************************************************************************/
+static void display_sam_user_info_21(SAM_USER_INFO_21 *usr)
+{
+ fstring temp;
+
+ unistr2_to_ascii(temp, &usr->uni_user_name, sizeof(temp)-1);
+ printf("\tUser Name :\t%s\n", temp);
+
+ unistr2_to_ascii(temp, &usr->uni_full_name, sizeof(temp)-1);
+ printf("\tFull Name :\t%s\n", temp);
+
+ unistr2_to_ascii(temp, &usr->uni_home_dir, sizeof(temp)-1);
+ printf("\tHome Drive :\t%s\n", temp);
+
+ unistr2_to_ascii(temp, &usr->uni_dir_drive, sizeof(temp)-1);
+ printf("\tDir Drive :\t%s\n", temp);
+
+ unistr2_to_ascii(temp, &usr->uni_profile_path, sizeof(temp)-1);
+ printf("\tProfile Path:\t%s\n", temp);
+
+ unistr2_to_ascii(temp, &usr->uni_logon_script, sizeof(temp)-1);
+ printf("\tLogon Script:\t%s\n", temp);
+
+ unistr2_to_ascii(temp, &usr->uni_acct_desc, sizeof(temp)-1);
+ printf("\tDescription :\t%s\n", temp);
+
+ unistr2_to_ascii(temp, &usr->uni_workstations, sizeof(temp)-1);
+ printf("\tWorkstations:\t%s\n", temp);
+
+ unistr2_to_ascii(temp, &usr->uni_unknown_str, sizeof(temp)-1);
+ printf("\tUnknown Str :\t%s\n", temp);
+
+ unistr2_to_ascii(temp, &usr->uni_munged_dial, sizeof(temp)-1);
+ printf("\tRemote Dial :\t%s\n", temp);
+
+ printf("\tLogon Time :\t%s\n",
+ http_timestring(nt_time_to_unix(&usr->logon_time)));
+ printf("\tLogoff Time :\t%s\n",
+ http_timestring(nt_time_to_unix(&usr->logoff_time)));
+ printf("\tKickoff Time :\t%s\n",
+ http_timestring(nt_time_to_unix(&usr->kickoff_time)));
+ printf("\tPassword last set Time :\t%s\n",
+ http_timestring(nt_time_to_unix(&usr->pass_last_set_time)));
+ printf("\tPassword can change Time :\t%s\n",
+ http_timestring(nt_time_to_unix(&usr->pass_can_change_time)));
+ printf("\tPassword must change Time:\t%s\n",
+ http_timestring(nt_time_to_unix(&usr->pass_must_change_time)));
+
+ printf("\tunknown_2[0..31]...\n"); /* user passwords? */
+
+ printf("\tuser_rid :\t0x%x\n" , usr->user_rid ); /* User ID */
+ printf("\tgroup_rid:\t0x%x\n" , usr->group_rid); /* Group ID */
+ printf("\tacb_info :\t0x%04x\n", usr->acb_info ); /* Account Control Info */
+
+ printf("\tfields_present:\t0x%08x\n", usr->fields_present); /* 0x00ff ffff */
+ printf("\tlogon_divs:\t%d\n", usr->logon_divs); /* 0x0000 00a8 which is 168 which is num hrs in a week */
+ printf("\tbad_password_count:\t0x%08x\n", usr->bad_password_count);
+ printf("\tlogon_count:\t0x%08x\n", usr->logon_count);
+
+ printf("\tpadding1[0..7]...\n");
+
+ if (usr->ptr_logon_hrs) {
+ printf("\tlogon_hrs[0..%d]...\n", usr->logon_hrs.len);
+ }
+}
+
+static const char *display_time(NTTIME nttime)
+{
+ static fstring string;
+
+ float high;
+ float low;
+ int sec;
+ int days, hours, mins, secs;
+
+ if (nttime.high==0 && nttime.low==0)
+ return "Now";
+
+ if (nttime.high==0x80000000 && nttime.low==0)
+ return "Never";
+
+ high = 65536;
+ high = high/10000;
+ high = high*65536;
+ high = high/1000;
+ high = high * (~nttime.high);
+
+ low = ~nttime.low;
+ low = low/(1000*1000*10);
+
+ sec=high+low;
+
+ days=sec/(60*60*24);
+ hours=(sec - (days*60*60*24)) / (60*60);
+ mins=(sec - (days*60*60*24) - (hours*60*60) ) / 60;
+ secs=sec - (days*60*60*24) - (hours*60*60) - (mins*60);
+
+ fstr_sprintf(string, "%u days, %u hours, %u minutes, %u seconds", days, hours, mins, secs);
+ return (string);
+}
+
+static void display_sam_unk_info_1(SAM_UNK_INFO_1 *info1)
+{
+
+ printf("Minimum password length: %d\n", info1->min_length_password);
+ printf("Password uniqueness (remember x passwords): %d\n", info1->password_history);
+ printf("flag: ");
+ if(info1->flag&&2==2) printf("users must open a session to change password ");
+ printf("\n");
+
+ printf("password expire in: %s\n", display_time(info1->expire));
+ printf("Min password age (allow changing in x days): %s\n", display_time(info1->min_passwordage));
+}
+
+static void display_sam_unk_info_2(SAM_UNK_INFO_2 *info2)
+{
+ fstring name;
+
+ unistr2_to_ascii(name, &info2->uni_domain, sizeof(name) - 1);
+ printf("Domain:\t%s\n", name);
+
+ unistr2_to_ascii(name, &info2->uni_server, sizeof(name) - 1);
+ printf("Server:\t%s\n", name);
+
+ printf("Total Users:\t%d\n", info2->num_domain_usrs);
+ printf("Total Groups:\t%d\n", info2->num_domain_grps);
+ printf("Total Aliases:\t%d\n", info2->num_local_grps);
+
+ printf("Sequence No:\t%d\n", info2->seq_num);
+
+ printf("Unknown 0:\t0x%x\n", info2->unknown_0);
+ printf("Unknown 1:\t0x%x\n", info2->unknown_1);
+ printf("Unknown 2:\t0x%x\n", info2->unknown_2);
+ printf("Unknown 3:\t0x%x\n", info2->unknown_3);
+ printf("Unknown 4:\t0x%x\n", info2->unknown_4);
+ printf("Unknown 5:\t0x%x\n", info2->unknown_5);
+ printf("Unknown 6:\t0x%x\n", info2->unknown_6);
+}
+
+static void display_sam_info_1(SAM_ENTRY1 *e1, SAM_STR1 *s1)
+{
+ fstring tmp;
+
+ printf("index: 0x%x ", e1->user_idx);
+ printf("RID: 0x%x ", e1->rid_user);
+ printf("acb: 0x%x ", e1->acb_info);
+
+ unistr2_to_ascii(tmp, &s1->uni_acct_name, sizeof(tmp)-1);
+ printf("Account: %s\t", tmp);
+
+ unistr2_to_ascii(tmp, &s1->uni_full_name, sizeof(tmp)-1);
+ printf("Name: %s\t", tmp);
+
+ unistr2_to_ascii(tmp, &s1->uni_acct_desc, sizeof(tmp)-1);
+ printf("Desc: %s\n", tmp);
+}
+
+static void display_sam_info_2(SAM_ENTRY2 *e2, SAM_STR2 *s2)
+{
+ fstring tmp;
+
+ printf("index: 0x%x ", e2->user_idx);
+ printf("RID: 0x%x ", e2->rid_user);
+ printf("acb: 0x%x ", e2->acb_info);
+
+ unistr2_to_ascii(tmp, &s2->uni_srv_name, sizeof(tmp)-1);
+ printf("Account: %s\t", tmp);
+
+ unistr2_to_ascii(tmp, &s2->uni_srv_desc, sizeof(tmp)-1);
+ printf("Name: %s\n", tmp);
+
+}
+
+static void display_sam_info_3(SAM_ENTRY3 *e3, SAM_STR3 *s3)
+{
+ fstring tmp;
+
+ printf("index: 0x%x ", e3->grp_idx);
+ printf("RID: 0x%x ", e3->rid_grp);
+ printf("attr: 0x%x ", e3->attr);
+
+ unistr2_to_ascii(tmp, &s3->uni_grp_name, sizeof(tmp)-1);
+ printf("Account: %s\t", tmp);
+
+ unistr2_to_ascii(tmp, &s3->uni_grp_desc, sizeof(tmp)-1);
+ printf("Name: %s\n", tmp);
+
+}
+
+static void display_sam_info_4(SAM_ENTRY4 *e4, SAM_STR4 *s4)
+{
+ int i;
+
+ printf("index: %d ", e4->user_idx);
+
+ printf("Account: ");
+ for (i=0; i<s4->acct_name.str_str_len; i++)
+ printf("%c", s4->acct_name.buffer[i]);
+ printf("\n");
+
+}
+
+static void display_sam_info_5(SAM_ENTRY5 *e5, SAM_STR5 *s5)
+{
+ int i;
+
+ printf("index: 0x%x ", e5->grp_idx);
+
+ printf("Account: ");
+ for (i=0; i<s5->grp_name.str_str_len; i++)
+ printf("%c", s5->grp_name.buffer[i]);
+ printf("\n");
+
+}
+
+/****************************************************************************
+ Try samr_connect4 first, then samr_conenct if it fails
+ ****************************************************************************/
+static NTSTATUS try_samr_connects(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 access_mask, POLICY_HND *connect_pol)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ result = cli_samr_connect4(cli, mem_ctx, access_mask, connect_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ result = cli_samr_connect(cli, mem_ctx, access_mask,
+ connect_pol);
+ }
+ return result;
+}
+
+/**********************************************************************
+ * Query user information
+ */
+static NTSTATUS cmd_samr_query_user(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol, user_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 info_level = 21;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+ SAM_USERINFO_CTR *user_ctr;
+ fstring server;
+ uint32 user_rid;
+
+ if ((argc < 2) || (argc > 4)) {
+ printf("Usage: %s rid [info level] [access mask] \n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ sscanf(argv[1], "%i", &user_rid);
+
+ if (argc > 2)
+ sscanf(argv[2], "%i", &info_level);
+
+ if (argc > 3)
+ sscanf(argv[3], "%x", &access_mask);
+
+
+ slprintf(server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+ strupper_m(server);
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_open_user(cli, mem_ctx, &domain_pol,
+ access_mask,
+ user_rid, &user_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ ZERO_STRUCT(user_ctr);
+
+ result = cli_samr_query_userinfo(cli, mem_ctx, &user_pol,
+ info_level, &user_ctr);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ display_sam_user_info_21(user_ctr->info.id21);
+
+done:
+ return result;
+}
+
+/****************************************************************************
+ display group info
+ ****************************************************************************/
+static void display_group_info1(GROUP_INFO1 *info1)
+{
+ fstring temp;
+
+ unistr2_to_ascii(temp, &info1->uni_acct_name, sizeof(temp)-1);
+ printf("\tGroup Name:\t%s\n", temp);
+ unistr2_to_ascii(temp, &info1->uni_acct_desc, sizeof(temp)-1);
+ printf("\tDescription:\t%s\n", temp);
+ printf("\tunk1:%d\n", info1->unknown_1);
+ printf("\tNum Members:%d\n", info1->num_members);
+}
+
+/****************************************************************************
+ display group info
+ ****************************************************************************/
+static void display_group_info4(GROUP_INFO4 *info4)
+{
+ fstring desc;
+
+ unistr2_to_ascii(desc, &info4->uni_acct_desc, sizeof(desc)-1);
+ printf("\tGroup Description:%s\n", desc);
+}
+
+/****************************************************************************
+ display sam sync structure
+ ****************************************************************************/
+static void display_group_info_ctr(GROUP_INFO_CTR *ctr)
+{
+ switch (ctr->switch_value1) {
+ case 1: {
+ display_group_info1(&ctr->group.info1);
+ break;
+ }
+ case 4: {
+ display_group_info4(&ctr->group.info4);
+ break;
+ }
+ }
+}
+
+/***********************************************************************
+ * Query group information
+ */
+static NTSTATUS cmd_samr_query_group(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol, group_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 info_level = 1;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+ GROUP_INFO_CTR *group_ctr;
+ fstring server;
+ uint32 group_rid;
+
+ if ((argc < 2) || (argc > 4)) {
+ printf("Usage: %s rid [info level] [access mask]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ sscanf(argv[1], "%i", &group_rid);
+
+ if (argc > 2)
+ sscanf(argv[2], "%i", &info_level);
+
+ if (argc > 3)
+ sscanf(argv[3], "%x", &access_mask);
+
+ slprintf(server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+ strupper_m(server);
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_open_group(cli, mem_ctx, &domain_pol,
+ access_mask,
+ group_rid, &group_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_query_groupinfo(cli, mem_ctx, &group_pol,
+ info_level, &group_ctr);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ display_group_info_ctr(group_ctr);
+
+done:
+ return result;
+}
+
+/* Query groups a user is a member of */
+
+static NTSTATUS cmd_samr_query_usergroups(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol,
+ domain_pol,
+ user_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 num_groups,
+ user_rid;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+ DOM_GID *user_gids;
+ int i;
+ fstring server;
+
+ if ((argc < 2) || (argc > 3)) {
+ printf("Usage: %s rid [access mask]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ sscanf(argv[1], "%i", &user_rid);
+
+ if (argc > 2)
+ sscanf(argv[2], "%x", &access_mask);
+
+ slprintf(server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+ strupper_m(server);
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_open_user(cli, mem_ctx, &domain_pol,
+ access_mask,
+ user_rid, &user_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_query_usergroups(cli, mem_ctx, &user_pol,
+ &num_groups, &user_gids);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ for (i = 0; i < num_groups; i++) {
+ printf("\tgroup rid:[0x%x] attr:[0x%x]\n",
+ user_gids[i].g_rid, user_gids[i].attr);
+ }
+
+ done:
+ return result;
+}
+
+/* Query aliases a user is a member of */
+
+static NTSTATUS cmd_samr_query_useraliases(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 user_rid, num_aliases, *alias_rids;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+ int i;
+ fstring server;
+ DOM_SID tmp_sid;
+ DOM_SID2 sid;
+ DOM_SID global_sid_Builtin;
+
+ string_to_sid(&global_sid_Builtin, "S-1-5-32");
+
+ if ((argc < 3) || (argc > 4)) {
+ printf("Usage: %s builtin|domain rid [access mask]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ sscanf(argv[2], "%i", &user_rid);
+
+ if (argc > 3)
+ sscanf(argv[3], "%x", &access_mask);
+
+ slprintf(server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+ strupper_m(server);
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ if (StrCaseCmp(argv[1], "domain")==0)
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ access_mask,
+ &domain_sid, &domain_pol);
+ else if (StrCaseCmp(argv[1], "builtin")==0)
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ access_mask,
+ &global_sid_Builtin, &domain_pol);
+ else
+ return NT_STATUS_OK;
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ sid_copy(&tmp_sid, &domain_sid);
+ sid_append_rid(&tmp_sid, user_rid);
+ init_dom_sid2(&sid, &tmp_sid);
+
+ result = cli_samr_query_useraliases(cli, mem_ctx, &domain_pol, 1, &sid, &num_aliases, &alias_rids);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ for (i = 0; i < num_aliases; i++) {
+ printf("\tgroup rid:[0x%x]\n", alias_rids[i]);
+ }
+
+ done:
+ return result;
+}
+
+/* Query members of a group */
+
+static NTSTATUS cmd_samr_query_groupmem(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol, group_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 num_members, *group_rids, *group_attrs, group_rid;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+ int i;
+ fstring server;
+
+ if ((argc < 2) || (argc > 3)) {
+ printf("Usage: %s rid [access mask]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ sscanf(argv[1], "%i", &group_rid);
+
+ if (argc > 2)
+ sscanf(argv[2], "%x", &access_mask);
+
+ slprintf(server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+ strupper_m(server);
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_open_group(cli, mem_ctx, &domain_pol,
+ access_mask,
+ group_rid, &group_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_query_groupmem(cli, mem_ctx, &group_pol,
+ &num_members, &group_rids,
+ &group_attrs);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ for (i = 0; i < num_members; i++) {
+ printf("\trid:[0x%x] attr:[0x%x]\n", group_rids[i],
+ group_attrs[i]);
+ }
+
+ done:
+ return result;
+}
+
+/* Enumerate domain users */
+
+static NTSTATUS cmd_samr_enum_dom_users(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 start_idx, size, num_dom_users, i;
+ char **dom_users;
+ uint32 *dom_rids;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+ uint16 acb_mask = ACB_NORMAL;
+ BOOL got_connect_pol = False, got_domain_pol = False;
+
+ if ((argc < 1) || (argc > 2)) {
+ printf("Usage: %s [access_mask]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc > 1)
+ sscanf(argv[1], "%x", &access_mask);
+
+ /* Get sam policy handle */
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_connect_pol = True;
+
+ /* Get domain policy handle */
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ access_mask,
+ &domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_domain_pol = True;
+
+ /* Enumerate domain users */
+
+ start_idx = 0;
+ size = 0xffff;
+
+ do {
+ result = cli_samr_enum_dom_users(
+ cli, mem_ctx, &domain_pol, &start_idx, acb_mask,
+ size, &dom_users, &dom_rids, &num_dom_users);
+
+ if (NT_STATUS_IS_OK(result) ||
+ NT_STATUS_V(result) == NT_STATUS_V(STATUS_MORE_ENTRIES)) {
+
+ for (i = 0; i < num_dom_users; i++)
+ printf("user:[%s] rid:[0x%x]\n",
+ dom_users[i], dom_rids[i]);
+ }
+
+ } while (NT_STATUS_V(result) == NT_STATUS_V(STATUS_MORE_ENTRIES));
+
+ done:
+ if (got_domain_pol)
+ cli_samr_close(cli, mem_ctx, &domain_pol);
+
+ if (got_connect_pol)
+ cli_samr_close(cli, mem_ctx, &connect_pol);
+
+ return result;
+}
+
+/* Enumerate domain groups */
+
+static NTSTATUS cmd_samr_enum_dom_groups(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 start_idx, size, num_dom_groups, i;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+ struct acct_info *dom_groups;
+ BOOL got_connect_pol = False, got_domain_pol = False;
+
+ if ((argc < 1) || (argc > 2)) {
+ printf("Usage: %s [access_mask]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc > 1)
+ sscanf(argv[1], "%x", &access_mask);
+
+ /* Get sam policy handle */
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_connect_pol = True;
+
+ /* Get domain policy handle */
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ access_mask,
+ &domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_domain_pol = True;
+
+ /* Enumerate domain groups */
+
+ start_idx = 0;
+ size = 0xffff;
+
+ do {
+ result = cli_samr_enum_dom_groups(
+ cli, mem_ctx, &domain_pol, &start_idx, size,
+ &dom_groups, &num_dom_groups);
+
+ if (NT_STATUS_IS_OK(result) ||
+ NT_STATUS_V(result) == NT_STATUS_V(STATUS_MORE_ENTRIES)) {
+
+ for (i = 0; i < num_dom_groups; i++)
+ printf("group:[%s] rid:[0x%x]\n",
+ dom_groups[i].acct_name,
+ dom_groups[i].rid);
+ }
+
+ } while (NT_STATUS_V(result) == NT_STATUS_V(STATUS_MORE_ENTRIES));
+
+ done:
+ if (got_domain_pol)
+ cli_samr_close(cli, mem_ctx, &domain_pol);
+
+ if (got_connect_pol)
+ cli_samr_close(cli, mem_ctx, &connect_pol);
+
+ return result;
+}
+
+/* Enumerate alias groups */
+
+static NTSTATUS cmd_samr_enum_als_groups(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 start_idx, size, num_als_groups, i;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+ struct acct_info *als_groups;
+ DOM_SID global_sid_Builtin;
+ BOOL got_connect_pol = False, got_domain_pol = False;
+
+ string_to_sid(&global_sid_Builtin, "S-1-5-32");
+
+ if ((argc < 2) || (argc > 3)) {
+ printf("Usage: %s builtin|domain [access mask]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc > 2)
+ sscanf(argv[2], "%x", &access_mask);
+
+ /* Get sam policy handle */
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_connect_pol = True;
+
+ /* Get domain policy handle */
+
+ if (StrCaseCmp(argv[1], "domain")==0)
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ access_mask,
+ &domain_sid, &domain_pol);
+ else if (StrCaseCmp(argv[1], "builtin")==0)
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ access_mask,
+ &global_sid_Builtin, &domain_pol);
+ else
+ return NT_STATUS_OK;
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_domain_pol = True;
+
+ /* Enumerate alias groups */
+
+ start_idx = 0;
+ size = 0xffff; /* Number of groups to retrieve */
+
+ do {
+ result = cli_samr_enum_als_groups(
+ cli, mem_ctx, &domain_pol, &start_idx, size,
+ &als_groups, &num_als_groups);
+
+ if (NT_STATUS_IS_OK(result) ||
+ NT_STATUS_V(result) == NT_STATUS_V(STATUS_MORE_ENTRIES)) {
+
+ for (i = 0; i < num_als_groups; i++)
+ printf("group:[%s] rid:[0x%x]\n",
+ als_groups[i].acct_name,
+ als_groups[i].rid);
+ }
+ } while (NT_STATUS_V(result) == NT_STATUS_V(STATUS_MORE_ENTRIES));
+
+ done:
+ if (got_domain_pol)
+ cli_samr_close(cli, mem_ctx, &domain_pol);
+
+ if (got_connect_pol)
+ cli_samr_close(cli, mem_ctx, &connect_pol);
+
+ return result;
+}
+
+/* Query alias membership */
+
+static NTSTATUS cmd_samr_query_aliasmem(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol, alias_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 alias_rid, num_members, i;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+ DOM_SID *alias_sids;
+ DOM_SID global_sid_Builtin;
+
+ string_to_sid(&global_sid_Builtin, "S-1-5-32");
+
+ if ((argc < 3) || (argc > 4)) {
+ printf("Usage: %s builtin|domain rid [access mask]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ sscanf(argv[2], "%i", &alias_rid);
+
+ if (argc > 3)
+ sscanf(argv[3], "%x", &access_mask);
+
+ /* Open SAMR handle */
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Open handle on domain */
+
+ if (StrCaseCmp(argv[1], "domain")==0)
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &domain_sid, &domain_pol);
+ else if (StrCaseCmp(argv[1], "builtin")==0)
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &global_sid_Builtin, &domain_pol);
+ else
+ return NT_STATUS_OK;
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Open handle on alias */
+
+ result = cli_samr_open_alias(cli, mem_ctx, &domain_pol,
+ access_mask,
+ alias_rid, &alias_pol);
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_query_aliasmem(cli, mem_ctx, &alias_pol,
+ &num_members, &alias_sids);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ for (i = 0; i < num_members; i++) {
+ fstring sid_str;
+
+ sid_to_string(sid_str, &alias_sids[i]);
+ printf("\tsid:[%s]\n", sid_str);
+ }
+
+ done:
+ return result;
+}
+
+/* Query display info */
+
+static NTSTATUS cmd_samr_query_dispinfo(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 start_idx=0, max_entries=250, max_size = 0xffff, num_entries, i;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+ uint32 info_level = 1;
+ SAM_DISPINFO_CTR ctr;
+ SAM_DISPINFO_1 info1;
+ SAM_DISPINFO_2 info2;
+ SAM_DISPINFO_3 info3;
+ SAM_DISPINFO_4 info4;
+ SAM_DISPINFO_5 info5;
+ int loop_count = 0;
+ BOOL got_params = False; /* Use get_query_dispinfo_params() or not? */
+
+ if (argc > 5) {
+ printf("Usage: %s [info level] [start index] [max entries] [max size] [access mask]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc >= 2)
+ sscanf(argv[1], "%i", &info_level);
+
+ if (argc >= 3)
+ sscanf(argv[2], "%i", &start_idx);
+
+ if (argc >= 4) {
+ sscanf(argv[3], "%i", &max_entries);
+ got_params = True;
+ }
+
+ if (argc >= 5) {
+ sscanf(argv[4], "%i", &max_size);
+ got_params = True;
+ }
+
+ if (argc >= 6)
+ sscanf(argv[5], "%x", &access_mask);
+
+ /* Get sam policy handle */
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Get domain policy handle */
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ access_mask,
+ &domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Query display info */
+
+ ZERO_STRUCT(ctr);
+ ZERO_STRUCT(info1);
+
+ switch (info_level) {
+ case 1:
+ ZERO_STRUCT(info1);
+ ctr.sam.info1 = &info1;
+ break;
+ case 2:
+ ZERO_STRUCT(info2);
+ ctr.sam.info2 = &info2;
+ break;
+ case 3:
+ ZERO_STRUCT(info3);
+ ctr.sam.info3 = &info3;
+ break;
+ case 4:
+ ZERO_STRUCT(info4);
+ ctr.sam.info4 = &info4;
+ break;
+ case 5:
+ ZERO_STRUCT(info5);
+ ctr.sam.info5 = &info5;
+ break;
+ }
+
+
+ while(1) {
+
+ if (!got_params)
+ get_query_dispinfo_params(
+ loop_count, &max_entries, &max_size);
+
+ result = cli_samr_query_dispinfo(cli, mem_ctx, &domain_pol,
+ &start_idx, info_level,
+ &num_entries, max_entries,
+ max_size, &ctr);
+
+ loop_count++;
+
+ if (!NT_STATUS_IS_OK(result) && !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES))
+ break;
+
+ if (num_entries == 0)
+ break;
+
+ for (i = 0; i < num_entries; i++) {
+ switch (info_level) {
+ case 1:
+ display_sam_info_1(&ctr.sam.info1->sam[i], &ctr.sam.info1->str[i]);
+ break;
+ case 2:
+ display_sam_info_2(&ctr.sam.info2->sam[i], &ctr.sam.info2->str[i]);
+ break;
+ case 3:
+ display_sam_info_3(&ctr.sam.info3->sam[i], &ctr.sam.info3->str[i]);
+ break;
+ case 4:
+ display_sam_info_4(&ctr.sam.info4->sam[i], &ctr.sam.info4->str[i]);
+ break;
+ case 5:
+ display_sam_info_5(&ctr.sam.info5->sam[i], &ctr.sam.info5->str[i]);
+ break;
+ }
+ }
+ }
+
+ done:
+ return result;
+}
+
+/* Query domain info */
+
+static NTSTATUS cmd_samr_query_dominfo(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 switch_level = 2;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+ SAM_UNK_CTR ctr;
+
+ if (argc > 2) {
+ printf("Usage: %s [info level] [access mask]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc > 1)
+ sscanf(argv[1], "%i", &switch_level);
+
+ if (argc > 2)
+ sscanf(argv[2], "%x", &access_mask);
+
+ /* Get sam policy handle */
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Get domain policy handle */
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ access_mask,
+ &domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Query domain info */
+
+ result = cli_samr_query_dom_info(cli, mem_ctx, &domain_pol,
+ switch_level, &ctr);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Display domain info */
+
+ switch (switch_level) {
+ case 1:
+ display_sam_unk_info_1(&ctr.info.inf1);
+ break;
+ case 2:
+ display_sam_unk_info_2(&ctr.info.inf2);
+ break;
+ default:
+ printf("cannot display domain info for switch value %d\n",
+ switch_level);
+ break;
+ }
+
+ done:
+
+ cli_samr_close(cli, mem_ctx, &domain_pol);
+ cli_samr_close(cli, mem_ctx, &connect_pol);
+ return result;
+}
+
+/* Create domain user */
+
+static NTSTATUS cmd_samr_create_dom_user(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol, user_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ const char *acct_name;
+ uint16 acb_info;
+ uint32 unknown, user_rid;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+
+ if ((argc < 2) || (argc > 3)) {
+ printf("Usage: %s username [access mask]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ acct_name = argv[1];
+
+ if (argc > 2)
+ sscanf(argv[2], "%x", &access_mask);
+
+ /* Get sam policy handle */
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Get domain policy handle */
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ access_mask,
+ &domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Create domain user */
+
+ acb_info = ACB_NORMAL;
+ unknown = 0xe005000b; /* No idea what this is - a permission mask? */
+
+ result = cli_samr_create_dom_user(cli, mem_ctx, &domain_pol,
+ acct_name, acb_info, unknown,
+ &user_pol, &user_rid);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ done:
+ return result;
+}
+
+/* Lookup sam names */
+
+static NTSTATUS cmd_samr_lookup_names(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ POLICY_HND connect_pol, domain_pol;
+ uint32 flags = 0x000003e8; /* Unknown */
+ uint32 num_rids, num_names, *name_types, *rids;
+ const char **names;
+ int i;
+ DOM_SID global_sid_Builtin;
+
+ string_to_sid(&global_sid_Builtin, "S-1-5-32");
+
+ if (argc < 3) {
+ printf("Usage: %s domain|builtin name1 [name2 [name3] [...]]\n", argv[0]);
+ printf("check on the domain SID: S-1-5-21-x-y-z\n");
+ printf("or check on the builtin SID: S-1-5-32\n");
+ return NT_STATUS_OK;
+ }
+
+ /* Get sam policy and domain handles */
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ if (StrCaseCmp(argv[1], "domain")==0)
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &domain_sid, &domain_pol);
+ else if (StrCaseCmp(argv[1], "builtin")==0)
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &global_sid_Builtin, &domain_pol);
+ else
+ return NT_STATUS_OK;
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Look up names */
+
+ num_names = argc - 2;
+ names = (const char **)talloc(mem_ctx, sizeof(char *) * num_names);
+
+ for (i = 0; i < argc - 2; i++)
+ names[i] = argv[i + 2];
+
+ result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol,
+ flags, num_names, names,
+ &num_rids, &rids, &name_types);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Display results */
+
+ for (i = 0; i < num_names; i++)
+ printf("name %s: 0x%x (%d)\n", names[i], rids[i],
+ name_types[i]);
+
+ done:
+ return result;
+}
+
+/* Lookup sam rids */
+
+static NTSTATUS cmd_samr_lookup_rids(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ POLICY_HND connect_pol, domain_pol;
+ uint32 flags = 0x000003e8; /* Unknown */
+ uint32 num_rids, num_names, *rids, *name_types;
+ char **names;
+ int i;
+
+ if (argc < 2) {
+ printf("Usage: %s rid1 [rid2 [rid3] [...]]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ /* Get sam policy and domain handles */
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Look up rids */
+
+ num_rids = argc - 1;
+ rids = (uint32 *)talloc(mem_ctx, sizeof(uint32) * num_rids);
+
+ for (i = 0; i < argc - 1; i++)
+ sscanf(argv[i + 1], "%i", &rids[i]);
+
+ result = cli_samr_lookup_rids(cli, mem_ctx, &domain_pol,
+ flags, num_rids, rids,
+ &num_names, &names, &name_types);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Display results */
+
+ for (i = 0; i < num_names; i++)
+ printf("rid 0x%x: %s (%d)\n", rids[i], names[i], name_types[i]);
+
+ done:
+ return result;
+}
+
+/* Delete domain user */
+
+static NTSTATUS cmd_samr_delete_dom_user(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ POLICY_HND connect_pol, domain_pol, user_pol;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+
+ if ((argc < 2) || (argc > 3)) {
+ printf("Usage: %s username\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc > 2)
+ sscanf(argv[2], "%x", &access_mask);
+
+ /* Get sam policy and domain handles */
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Get handle on user */
+
+ {
+ uint32 *user_rids, num_rids, *name_types;
+ uint32 flags = 0x000003e8; /* Unknown */
+
+ result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol,
+ flags, 1, (const char **)&argv[1],
+ &num_rids, &user_rids,
+ &name_types);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_open_user(cli, mem_ctx, &domain_pol,
+ access_mask,
+ user_rids[0], &user_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+ }
+
+ /* Delete user */
+
+ result = cli_samr_delete_dom_user(cli, mem_ctx, &user_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Display results */
+
+ done:
+ return result;
+}
+
+/**********************************************************************
+ * Query user security object
+ */
+static NTSTATUS cmd_samr_query_sec_obj(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol, user_pol, *pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 info_level = 4;
+ fstring server;
+ uint32 user_rid = 0;
+ TALLOC_CTX *ctx = NULL;
+ SEC_DESC_BUF *sec_desc_buf=NULL;
+ BOOL domain = False;
+
+ ctx=talloc_init("cmd_samr_query_sec_obj");
+
+ if ((argc < 1) || (argc > 2)) {
+ printf("Usage: %s [rid|-d]\n", argv[0]);
+ printf("\tSpecify rid for security on user, -d for security on domain\n");
+ return NT_STATUS_OK;
+ }
+
+ if (argc > 1) {
+ if (strcmp(argv[1], "-d") == 0)
+ domain = True;
+ else
+ sscanf(argv[1], "%i", &user_rid);
+ }
+
+ slprintf(server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+ strupper_m(server);
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ if (domain || user_rid)
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ if (user_rid)
+ result = cli_samr_open_user(cli, mem_ctx, &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ user_rid, &user_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Pick which query pol to use */
+
+ pol = &connect_pol;
+
+ if (domain)
+ pol = &domain_pol;
+
+ if (user_rid)
+ pol = &user_pol;
+
+ /* Query SAM security object */
+
+ result = cli_samr_query_sec_obj(cli, mem_ctx, pol, info_level, ctx,
+ &sec_desc_buf);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ display_sec_desc(sec_desc_buf->sec);
+
+done:
+ talloc_destroy(ctx);
+ return result;
+}
+
+static NTSTATUS cmd_samr_get_dom_pwinfo(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint16 unk_0, unk_1, unk_2;
+
+ if (argc != 1) {
+ printf("Usage: %s\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = cli_samr_get_dom_pwinfo(cli, mem_ctx, &unk_0, &unk_1, &unk_2);
+
+ if (NT_STATUS_IS_OK(result)) {
+ printf("unk_0 = 0x%08x\n", unk_0);
+ printf("unk_1 = 0x%08x\n", unk_1);
+ printf("unk_2 = 0x%08x\n", unk_2);
+ }
+
+ return result;
+}
+
+/* Look up domain name */
+
+static NTSTATUS cmd_samr_lookup_domain(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+ fstring domain_name,sid_string;
+ DOM_SID sid;
+
+ if (argc != 2) {
+ printf("Usage: %s domain_name\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ sscanf(argv[1], "%s", domain_name);
+
+ result = try_samr_connects(cli, mem_ctx, access_mask, &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ access_mask, &domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_lookup_domain(
+ cli, mem_ctx, &connect_pol, domain_name, &sid);
+
+ sid_to_string(sid_string,&sid);
+
+ if (NT_STATUS_IS_OK(result))
+ printf("SAMR_LOOKUP_DOMAIN: Domain Name: %s Domain SID: %s\n",
+ domain_name,sid_string);
+
+done:
+ return result;
+}
+
+
+/* List of commands exported by this module */
+
+struct cmd_set samr_commands[] = {
+
+ { "SAMR" },
+
+ { "queryuser", RPC_RTYPE_NTSTATUS, cmd_samr_query_user, NULL, PI_SAMR, "Query user info", "" },
+ { "querygroup", RPC_RTYPE_NTSTATUS, cmd_samr_query_group, NULL, PI_SAMR, "Query group info", "" },
+ { "queryusergroups", RPC_RTYPE_NTSTATUS, cmd_samr_query_usergroups, NULL, PI_SAMR, "Query user groups", "" },
+ { "queryuseraliases", RPC_RTYPE_NTSTATUS, cmd_samr_query_useraliases, NULL, PI_SAMR, "Query user aliases", "" },
+ { "querygroupmem", RPC_RTYPE_NTSTATUS, cmd_samr_query_groupmem, NULL, PI_SAMR, "Query group membership", "" },
+ { "queryaliasmem", RPC_RTYPE_NTSTATUS, cmd_samr_query_aliasmem, NULL, PI_SAMR, "Query alias membership", "" },
+ { "querydispinfo", RPC_RTYPE_NTSTATUS, cmd_samr_query_dispinfo, NULL, PI_SAMR, "Query display info", "" },
+ { "querydominfo", RPC_RTYPE_NTSTATUS, cmd_samr_query_dominfo, NULL, PI_SAMR, "Query domain info", "" },
+ { "enumdomusers", RPC_RTYPE_NTSTATUS, cmd_samr_enum_dom_users, NULL, PI_SAMR, "Enumerate domain users", "" },
+ { "enumdomgroups", RPC_RTYPE_NTSTATUS, cmd_samr_enum_dom_groups, NULL, PI_SAMR, "Enumerate domain groups", "" },
+ { "enumalsgroups", RPC_RTYPE_NTSTATUS, cmd_samr_enum_als_groups, NULL, PI_SAMR, "Enumerate alias groups", "" },
+
+ { "createdomuser", RPC_RTYPE_NTSTATUS, cmd_samr_create_dom_user, NULL, PI_SAMR, "Create domain user", "" },
+ { "samlookupnames", RPC_RTYPE_NTSTATUS, cmd_samr_lookup_names, NULL, PI_SAMR, "Look up names", "" },
+ { "samlookuprids", RPC_RTYPE_NTSTATUS, cmd_samr_lookup_rids, NULL, PI_SAMR, "Look up names", "" },
+ { "deletedomuser", RPC_RTYPE_NTSTATUS, cmd_samr_delete_dom_user, NULL, PI_SAMR, "Delete domain user", "" },
+ { "samquerysecobj", RPC_RTYPE_NTSTATUS, cmd_samr_query_sec_obj, NULL, PI_SAMR, "Query SAMR security object", "" },
+ { "getdompwinfo", RPC_RTYPE_NTSTATUS, cmd_samr_get_dom_pwinfo, NULL, PI_SAMR, "Retrieve domain password info", "" },
+
+ { "lookupdomain", RPC_RTYPE_NTSTATUS, cmd_samr_lookup_domain, NULL, PI_SAMR, "Lookup Domain Name", "" },
+ { NULL }
+};
diff --git a/source/rpcclient/cmd_shutdown.c b/source/rpcclient/cmd_shutdown.c
new file mode 100644
index 00000000000..e42ec30ac13
--- /dev/null
+++ b/source/rpcclient/cmd_shutdown.c
@@ -0,0 +1,112 @@
+/*
+ Unix SMB/CIFS implementation.
+ NT Domain Authentication SMB / MSRPC client
+ Copyright (C) Andrew Tridgell 1994-1997,
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ Copyright (C) Simo Sorce 2001,
+ Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "rpcclient.h"
+
+/****************************************************************************
+nt shutdown init
+****************************************************************************/
+static NTSTATUS cmd_shutdown_init(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ fstring msg;
+ uint32 timeout = 20;
+ BOOL force = False;
+ BOOL reboot = False;
+ int opt;
+
+ *msg = 0;
+ optind = 0; /* TODO: test if this hack works on other systems too --simo */
+
+ while ((opt = getopt(argc, argv, "m:t:rf")) != EOF)
+ {
+ /*fprintf (stderr, "[%s]\n", argv[argc-1]);*/
+
+ switch (opt)
+ {
+ case 'm':
+ fstrcpy(msg, optarg);
+ /*fprintf (stderr, "[%s|%s]\n", optarg, msg);*/
+ break;
+
+ case 't':
+ timeout = atoi(optarg);
+ /*fprintf (stderr, "[%s|%d]\n", optarg, timeout);*/
+ break;
+
+ case 'r':
+ reboot = True;
+ break;
+
+ case 'f':
+ force = True;
+ break;
+
+ }
+ }
+
+ /* create an entry */
+ result = cli_shutdown_init(cli, mem_ctx, msg, timeout, reboot, force);
+
+ if (NT_STATUS_IS_OK(result))
+ DEBUG(5,("cmd_shutdown_init: query succeeded\n"));
+ else
+ DEBUG(5,("cmd_shutdown_init: query failed\n"));
+
+ return result;
+}
+
+/****************************************************************************
+abort a shutdown
+****************************************************************************/
+static NTSTATUS cmd_shutdown_abort(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ result = cli_shutdown_abort(cli, mem_ctx);
+
+ if (NT_STATUS_IS_OK(result))
+ DEBUG(5,("cmd_shutdown_abort: query succeeded\n"));
+ else
+ DEBUG(5,("cmd_shutdown_abort: query failed\n"));
+
+ return result;
+}
+
+
+/* List of commands exported by this module */
+struct cmd_set shutdown_commands[] = {
+
+ { "SHUTDOWN" },
+
+ { "shutdowninit", RPC_RTYPE_NTSTATUS, cmd_shutdown_init, NULL, PI_SHUTDOWN, "Remote Shutdown (over shutdown pipe)",
+ "syntax: shutdown [-m message] [-t timeout] [-r] [-h] [-f] (-r == reboot, -h == halt, -f == force)" },
+
+ { "shutdownabort", RPC_RTYPE_NTSTATUS, cmd_shutdown_abort, NULL, PI_SHUTDOWN, "Abort Shutdown (over shutdown pipe)",
+ "syntax: shutdownabort" },
+ { NULL }
+};
diff --git a/source/rpcclient/cmd_spoolss.c b/source/rpcclient/cmd_spoolss.c
new file mode 100644
index 00000000000..f5a440c024c
--- /dev/null
+++ b/source/rpcclient/cmd_spoolss.c
@@ -0,0 +1,2338 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ Copyright (C) Gerald Carter 2001
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Andrew Tridgell 1992-1999
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1999
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "rpcclient.h"
+
+struct table_node {
+ const char *long_archi;
+ const char *short_archi;
+ int version;
+};
+
+static const struct table_node archi_table[]= {
+
+ {"Windows 4.0", "WIN40", 0 },
+ {"Windows NT x86", "W32X86", 2 },
+ {"Windows NT x86", "W32X86", 3 },
+ {"Windows NT R4000", "W32MIPS", 2 },
+ {"Windows NT Alpha_AXP", "W32ALPHA", 2 },
+ {"Windows NT PowerPC", "W32PPC", 2 },
+ {NULL, "", -1 }
+};
+
+/**
+ * @file
+ *
+ * rpcclient module for SPOOLSS rpc pipe.
+ *
+ * This generally just parses and checks command lines, and then calls
+ * a cli_spoolss function.
+ **/
+
+/****************************************************************************
+function to do the mapping between the long architecture name and
+the short one.
+****************************************************************************/
+static const char *cmd_spoolss_get_short_archi(const char *long_archi)
+{
+ int i=-1;
+
+ DEBUG(107,("Getting architecture dependant directory\n"));
+ do {
+ i++;
+ } while ( (archi_table[i].long_archi!=NULL ) &&
+ StrCaseCmp(long_archi, archi_table[i].long_archi) );
+
+ if (archi_table[i].long_archi==NULL) {
+ DEBUGADD(10,("Unknown architecture [%s] !\n", long_archi));
+ return NULL;
+ }
+
+ /* this might be client code - but shouldn't this be an fstrcpy etc? */
+
+
+ DEBUGADD(108,("index: [%d]\n", i));
+ DEBUGADD(108,("long architecture: [%s]\n", archi_table[i].long_archi));
+ DEBUGADD(108,("short architecture: [%s]\n", archi_table[i].short_archi));
+
+ return archi_table[i].short_archi;
+}
+
+#if 0
+/**********************************************************************
+ * dummy function -- placeholder
+ */
+static WERROR cmd_spoolss_not_implemented(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ printf ("(*) This command is not currently implemented.\n");
+ return WERR_OK;
+}
+#endif
+
+/***********************************************************************
+ * Get printer information
+ */
+static WERROR cmd_spoolss_open_printer_ex(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ WERROR werror;
+ fstring printername;
+ fstring servername, user;
+ POLICY_HND hnd;
+
+ if (argc != 2) {
+ printf("Usage: %s <printername>\n", argv[0]);
+ return WERR_OK;
+ }
+
+ if (!cli)
+ return WERR_GENERAL_FAILURE;
+
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ fstrcpy(user, cli->user_name);
+ fstrcpy(printername, argv[1]);
+
+ /* Open the printer handle */
+
+ werror = cli_spoolss_open_printer_ex(cli, mem_ctx, printername,
+ "", PRINTER_ALL_ACCESS,
+ servername, user, &hnd);
+
+ if (W_ERROR_IS_OK(werror)) {
+ printf("Printer %s opened successfully\n", printername);
+ werror = cli_spoolss_close_printer(cli, mem_ctx, &hnd);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ printf("Error closing printer handle! (%s)\n",
+ get_dos_error_msg(werror));
+ }
+ }
+
+ return werror;
+}
+
+
+/****************************************************************************
+printer info level 0 display function
+****************************************************************************/
+static void display_print_info_0(PRINTER_INFO_0 *i0)
+{
+ fstring name = "";
+ fstring servername = "";
+
+ if (!i0)
+ return;
+
+ rpcstr_pull(name, i0->printername.buffer, sizeof(name), -1, STR_TERMINATE);
+
+ rpcstr_pull(servername, i0->servername.buffer, sizeof(servername), -1,STR_TERMINATE);
+
+ printf("\tprintername:[%s]\n", name);
+ printf("\tservername:[%s]\n", servername);
+ printf("\tcjobs:[0x%x]\n", i0->cjobs);
+ printf("\ttotal_jobs:[0x%x]\n", i0->total_jobs);
+
+ printf("\t:date: [%d]-[%d]-[%d] (%d)\n", i0->year, i0->month,
+ i0->day, i0->dayofweek);
+ printf("\t:time: [%d]-[%d]-[%d]-[%d]\n", i0->hour, i0->minute,
+ i0->second, i0->milliseconds);
+
+ printf("\tglobal_counter:[0x%x]\n", i0->global_counter);
+ printf("\ttotal_pages:[0x%x]\n", i0->total_pages);
+
+ printf("\tmajorversion:[0x%x]\n", i0->major_version);
+ printf("\tbuildversion:[0x%x]\n", i0->build_version);
+
+ printf("\tunknown7:[0x%x]\n", i0->unknown7);
+ printf("\tunknown8:[0x%x]\n", i0->unknown8);
+ printf("\tunknown9:[0x%x]\n", i0->unknown9);
+ printf("\tsession_counter:[0x%x]\n", i0->session_counter);
+ printf("\tunknown11:[0x%x]\n", i0->unknown11);
+ printf("\tprinter_errors:[0x%x]\n", i0->printer_errors);
+ printf("\tunknown13:[0x%x]\n", i0->unknown13);
+ printf("\tunknown14:[0x%x]\n", i0->unknown14);
+ printf("\tunknown15:[0x%x]\n", i0->unknown15);
+ printf("\tunknown16:[0x%x]\n", i0->unknown16);
+ printf("\tchange_id:[0x%x]\n", i0->change_id);
+ printf("\tunknown18:[0x%x]\n", i0->unknown18);
+ printf("\tstatus:[0x%x]\n", i0->status);
+ printf("\tunknown20:[0x%x]\n", i0->unknown20);
+ printf("\tc_setprinter:[0x%x]\n", i0->c_setprinter);
+ printf("\tunknown22:[0x%x]\n", i0->unknown22);
+ printf("\tunknown23:[0x%x]\n", i0->unknown23);
+ printf("\tunknown24:[0x%x]\n", i0->unknown24);
+ printf("\tunknown25:[0x%x]\n", i0->unknown25);
+ printf("\tunknown26:[0x%x]\n", i0->unknown26);
+ printf("\tunknown27:[0x%x]\n", i0->unknown27);
+ printf("\tunknown28:[0x%x]\n", i0->unknown28);
+ printf("\tunknown29:[0x%x]\n", i0->unknown29);
+
+ printf("\n");
+}
+
+/****************************************************************************
+printer info level 1 display function
+****************************************************************************/
+static void display_print_info_1(PRINTER_INFO_1 *i1)
+{
+ fstring desc = "";
+ fstring name = "";
+ fstring comm = "";
+
+ rpcstr_pull(desc, i1->description.buffer, sizeof(desc), -1,
+ STR_TERMINATE);
+
+ rpcstr_pull(name, i1->name.buffer, sizeof(name), -1, STR_TERMINATE);
+ rpcstr_pull(comm, i1->comment.buffer, sizeof(comm), -1, STR_TERMINATE);
+
+ printf("\tflags:[0x%x]\n", i1->flags);
+ printf("\tname:[%s]\n", name);
+ printf("\tdescription:[%s]\n", desc);
+ printf("\tcomment:[%s]\n", comm);
+
+ printf("\n");
+}
+
+/****************************************************************************
+printer info level 2 display function
+****************************************************************************/
+static void display_print_info_2(PRINTER_INFO_2 *i2)
+{
+ fstring servername = "";
+ fstring printername = "";
+ fstring sharename = "";
+ fstring portname = "";
+ fstring drivername = "";
+ fstring comment = "";
+ fstring location = "";
+ fstring sepfile = "";
+ fstring printprocessor = "";
+ fstring datatype = "";
+ fstring parameters = "";
+
+ rpcstr_pull(servername, i2->servername.buffer,sizeof(servername), -1, STR_TERMINATE);
+
+ rpcstr_pull(printername, i2->printername.buffer,sizeof(printername), -1, STR_TERMINATE);
+
+ rpcstr_pull(sharename, i2->sharename.buffer,sizeof(sharename), -1, STR_TERMINATE);
+
+ rpcstr_pull(portname, i2->portname.buffer,sizeof(portname), -1, STR_TERMINATE);
+
+ rpcstr_pull(drivername, i2->drivername.buffer,sizeof(drivername), -1, STR_TERMINATE);
+
+ rpcstr_pull(comment, i2->comment.buffer,sizeof(comment), -1, STR_TERMINATE);
+
+ rpcstr_pull(location, i2->location.buffer,sizeof(location), -1, STR_TERMINATE);
+
+ rpcstr_pull(sepfile, i2->sepfile.buffer,sizeof(sepfile), -1, STR_TERMINATE);
+
+ rpcstr_pull(printprocessor, i2->printprocessor.buffer,sizeof(printprocessor), -1, STR_TERMINATE);
+
+ rpcstr_pull(datatype, i2->datatype.buffer,sizeof(datatype), -1, STR_TERMINATE);
+
+ rpcstr_pull(parameters, i2->parameters.buffer,sizeof(parameters), -1, STR_TERMINATE);
+
+ printf("\tservername:[%s]\n", servername);
+ printf("\tprintername:[%s]\n", printername);
+ printf("\tsharename:[%s]\n", sharename);
+ printf("\tportname:[%s]\n", portname);
+ printf("\tdrivername:[%s]\n", drivername);
+ printf("\tcomment:[%s]\n", comment);
+ printf("\tlocation:[%s]\n", location);
+ printf("\tsepfile:[%s]\n", sepfile);
+ printf("\tprintprocessor:[%s]\n", printprocessor);
+ printf("\tdatatype:[%s]\n", datatype);
+ printf("\tparameters:[%s]\n", parameters);
+ printf("\tattributes:[0x%x]\n", i2->attributes);
+ printf("\tpriority:[0x%x]\n", i2->priority);
+ printf("\tdefaultpriority:[0x%x]\n", i2->defaultpriority);
+ printf("\tstarttime:[0x%x]\n", i2->starttime);
+ printf("\tuntiltime:[0x%x]\n", i2->untiltime);
+ printf("\tstatus:[0x%x]\n", i2->status);
+ printf("\tcjobs:[0x%x]\n", i2->cjobs);
+ printf("\taverageppm:[0x%x]\n", i2->averageppm);
+
+ if (i2->secdesc)
+ display_sec_desc(i2->secdesc);
+
+ printf("\n");
+}
+
+/****************************************************************************
+printer info level 3 display function
+****************************************************************************/
+static void display_print_info_3(PRINTER_INFO_3 *i3)
+{
+ printf("\tflags:[0x%x]\n", i3->flags);
+
+ display_sec_desc(i3->secdesc);
+
+ printf("\n");
+}
+
+/* Enumerate printers */
+
+static WERROR cmd_spoolss_enum_printers(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ WERROR result;
+ uint32 info_level = 1;
+ PRINTER_INFO_CTR ctr;
+ uint32 i = 0, num_printers, needed;
+ fstring name;
+
+ if (argc > 3)
+ {
+ printf("Usage: %s [level] [name]\n", argv[0]);
+ return WERR_OK;
+ }
+
+ if (argc == 2)
+ info_level = atoi(argv[1]);
+
+ if (argc == 3)
+ fstrcpy(name, argv[2]);
+ else {
+ slprintf(name, sizeof(name)-1, "\\\\%s", cli->desthost);
+ strupper_m(name);
+ }
+
+ /* Enumerate printers -- Should we enumerate types other
+ than PRINTER_ENUM_LOCAL? Maybe accept as a parameter? --jerry */
+
+ ZERO_STRUCT(ctr);
+
+ result = cli_spoolss_enum_printers(
+ cli, mem_ctx, 0, &needed, name, PRINTER_ENUM_LOCAL,
+ info_level, &num_printers, &ctr);
+
+ if (W_ERROR_V(result) == ERRinsufficientbuffer)
+ result = cli_spoolss_enum_printers(
+ cli, mem_ctx, needed, NULL, name, PRINTER_ENUM_LOCAL,
+ info_level, &num_printers, &ctr);
+
+ if (W_ERROR_IS_OK(result)) {
+
+ if (!num_printers) {
+ printf ("No printers returned.\n");
+ goto done;
+ }
+
+ for (i = 0; i < num_printers; i++) {
+ switch(info_level) {
+ case 0:
+ display_print_info_0(&ctr.printers_0[i]);
+ break;
+ case 1:
+ display_print_info_1(&ctr.printers_1[i]);
+ break;
+ case 2:
+ display_print_info_2(&ctr.printers_2[i]);
+ break;
+ case 3:
+ display_print_info_3(&ctr.printers_3[i]);
+ break;
+ default:
+ printf("unknown info level %d\n", info_level);
+ goto done;
+ }
+ }
+ }
+ done:
+
+ return result;
+}
+
+/****************************************************************************
+port info level 1 display function
+****************************************************************************/
+static void display_port_info_1(PORT_INFO_1 *i1)
+{
+ fstring buffer;
+
+ rpcstr_pull(buffer, i1->port_name.buffer, sizeof(buffer), -1, STR_TERMINATE);
+ printf("\tPort Name:\t[%s]\n", buffer);
+}
+
+/****************************************************************************
+port info level 2 display function
+****************************************************************************/
+static void display_port_info_2(PORT_INFO_2 *i2)
+{
+ fstring buffer;
+
+ rpcstr_pull(buffer, i2->port_name.buffer, sizeof(buffer), -1, STR_TERMINATE);
+ printf("\tPort Name:\t[%s]\n", buffer);
+ rpcstr_pull(buffer, i2->monitor_name.buffer, sizeof(buffer), -1, STR_TERMINATE);
+
+ printf("\tMonitor Name:\t[%s]\n", buffer);
+ rpcstr_pull(buffer, i2->description.buffer, sizeof(buffer), -1, STR_TERMINATE);
+
+ printf("\tDescription:\t[%s]\n", buffer);
+ printf("\tPort Type:\t" );
+ if ( i2->port_type ) {
+ int comma = 0; /* hack */
+ printf( "[" );
+ if ( i2->port_type & PORT_TYPE_READ ) {
+ printf( "Read" );
+ comma = 1;
+ }
+ if ( i2->port_type & PORT_TYPE_WRITE ) {
+ printf( "%sWrite", comma ? ", " : "" );
+ comma = 1;
+ }
+ /* These two have slightly different interpretations
+ on 95/98/ME but I'm disregarding that for now */
+ if ( i2->port_type & PORT_TYPE_REDIRECTED ) {
+ printf( "%sRedirected", comma ? ", " : "" );
+ comma = 1;
+ }
+ if ( i2->port_type & PORT_TYPE_NET_ATTACHED ) {
+ printf( "%sNet-Attached", comma ? ", " : "" );
+ }
+ printf( "]\n" );
+ } else {
+ printf( "[Unset]\n" );
+ }
+ printf("\tReserved:\t[%d]\n", i2->reserved);
+ printf("\n");
+}
+
+/* Enumerate ports */
+
+static WERROR cmd_spoolss_enum_ports(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ WERROR result;
+ uint32 needed, info_level = 1;
+ PORT_INFO_CTR ctr;
+ int returned;
+
+ if (argc > 2) {
+ printf("Usage: %s [level]\n", argv[0]);
+ return WERR_OK;
+ }
+
+ if (argc == 2)
+ info_level = atoi(argv[1]);
+
+ /* Enumerate ports */
+
+ ZERO_STRUCT(ctr);
+
+ result = cli_spoolss_enum_ports(cli, mem_ctx, 0, &needed, info_level,
+ &returned, &ctr);
+
+ if (W_ERROR_V(result) == ERRinsufficientbuffer)
+ result = cli_spoolss_enum_ports(cli, mem_ctx, needed, NULL,
+ info_level, &returned, &ctr);
+
+ if (W_ERROR_IS_OK(result)) {
+ int i;
+
+ for (i = 0; i < returned; i++) {
+ switch (info_level) {
+ case 1:
+ display_port_info_1(&ctr.port.info_1[i]);
+ break;
+ case 2:
+ display_port_info_2(&ctr.port.info_2[i]);
+ break;
+ default:
+ printf("unknown info level %d\n", info_level);
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
+/***********************************************************************
+ * Set printer comment - use a level2 set.
+ */
+static WERROR cmd_spoolss_setprinter(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND pol;
+ WERROR result;
+ uint32 needed;
+ uint32 info_level = 2;
+ BOOL opened_hnd = False;
+ PRINTER_INFO_CTR ctr;
+ fstring printername,
+ servername,
+ user,
+ comment;
+
+ if (argc == 1 || argc > 3) {
+ printf("Usage: %s printername comment\n", argv[0]);
+
+ return WERR_OK;
+ }
+
+ /* Open a printer handle */
+ if (argc == 3) {
+ fstrcpy(comment, argv[2]);
+ }
+
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ fstrcpy(printername, argv[1]);
+ fstrcpy(user, cli->user_name);
+
+ /* get a printer handle */
+ result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername, "",
+ PRINTER_ALL_ACCESS, servername,
+ user, &pol);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ opened_hnd = True;
+
+ /* Get printer info */
+ result = cli_spoolss_getprinter(cli, mem_ctx, 0, &needed, &pol, info_level, &ctr);
+
+ if (W_ERROR_V(result) == ERRinsufficientbuffer)
+ result = cli_spoolss_getprinter(cli, mem_ctx, needed, NULL, &pol, info_level, &ctr);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+
+ /* Modify the comment. */
+ init_unistr(&ctr.printers_2->comment, comment);
+ ctr.printers_2->devmode = NULL;
+ ctr.printers_2->secdesc = NULL;
+
+ result = cli_spoolss_setprinter(cli, mem_ctx, &pol, info_level, &ctr, 0);
+ if (W_ERROR_IS_OK(result))
+ printf("Success in setting comment.\n");
+
+ done:
+ if (opened_hnd)
+ cli_spoolss_close_printer(cli, mem_ctx, &pol);
+
+ return result;
+}
+
+/***********************************************************************
+ * Get printer information
+ */
+static WERROR cmd_spoolss_getprinter(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND pol;
+ WERROR result;
+ uint32 info_level = 1;
+ BOOL opened_hnd = False;
+ PRINTER_INFO_CTR ctr;
+ fstring printername,
+ servername,
+ user;
+ uint32 needed;
+
+ if (argc == 1 || argc > 3) {
+ printf("Usage: %s <printername> [level]\n", argv[0]);
+ return WERR_OK;
+ }
+
+ /* Open a printer handle */
+ if (argc == 3) {
+ info_level = atoi(argv[2]);
+ }
+
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ slprintf(printername, sizeof(printername)-1, "%s\\%s", servername, argv[1]);
+ fstrcpy(user, cli->user_name);
+
+ /* get a printer handle */
+
+ result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername,
+ "", MAXIMUM_ALLOWED_ACCESS,
+ servername, user, &pol);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ opened_hnd = True;
+
+ /* Get printer info */
+
+ result = cli_spoolss_getprinter(cli, mem_ctx, 0, &needed,
+ &pol, info_level, &ctr);
+
+ if (W_ERROR_V(result) == ERRinsufficientbuffer)
+ result = cli_spoolss_getprinter(
+ cli, mem_ctx, needed, NULL, &pol, info_level, &ctr);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ /* Display printer info */
+
+ switch (info_level) {
+ case 0:
+ display_print_info_0(ctr.printers_0);
+ break;
+ case 1:
+ display_print_info_1(ctr.printers_1);
+ break;
+ case 2:
+ display_print_info_2(ctr.printers_2);
+ break;
+ case 3:
+ display_print_info_3(ctr.printers_3);
+ break;
+ default:
+ printf("unknown info level %d\n", info_level);
+ break;
+ }
+
+ done:
+ if (opened_hnd)
+ cli_spoolss_close_printer(cli, mem_ctx, &pol);
+
+ return result;
+}
+
+static void display_reg_value(REGISTRY_VALUE value)
+{
+ pstring text;
+
+ switch(value.type) {
+ case REG_DWORD:
+ printf("%s: REG_DWORD: 0x%08x\n", value.valuename,
+ *((uint32 *) value.data_p));
+ break;
+ case REG_SZ:
+ rpcstr_pull(text, value.data_p, sizeof(text), value.size,
+ STR_TERMINATE);
+ printf("%s: REG_SZ: %s\n", value.valuename, text);
+ break;
+ case REG_BINARY:
+ printf("%s: REG_BINARY: unknown length value not displayed\n",
+ value.valuename);
+ break;
+ case REG_MULTI_SZ: {
+ uint16 *curstr = (uint16 *) value.data_p;
+ uint8 *start = value.data_p;
+ printf("%s: REG_MULTI_SZ:\n", value.valuename);
+ while ((*curstr != 0) &&
+ ((uint8 *) curstr < start + value.size)) {
+ rpcstr_pull(text, curstr, sizeof(text), -1,
+ STR_TERMINATE);
+ printf(" %s\n", text);
+ curstr += strlen(text) + 1;
+ }
+ }
+ break;
+ default:
+ printf("%s: unknown type %d\n", value.valuename, value.type);
+ }
+
+}
+
+/***********************************************************************
+ * Get printer data
+ */
+static WERROR cmd_spoolss_getprinterdata(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND pol;
+ WERROR result;
+ BOOL opened_hnd = False;
+ fstring printername,
+ servername,
+ user;
+ uint32 needed;
+ const char *valuename;
+ REGISTRY_VALUE value;
+
+ if (argc != 3) {
+ printf("Usage: %s <printername> <valuename>\n", argv[0]);
+ printf("<printername> of . queries print server\n");
+ return WERR_OK;
+ }
+ valuename = argv[2];
+
+ /* Open a printer handle */
+
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ if (strncmp(argv[1], ".", sizeof(".")) == 0)
+ fstrcpy(printername, servername);
+ else
+ slprintf(printername, sizeof(servername)-1, "%s\\%s",
+ servername, argv[1]);
+ fstrcpy(user, cli->user_name);
+
+ /* get a printer handle */
+
+ result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername,
+ "", MAXIMUM_ALLOWED_ACCESS,
+ servername, user, &pol);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ opened_hnd = True;
+
+ /* Get printer info */
+
+ result = cli_spoolss_getprinterdata(cli, mem_ctx, 0, &needed,
+ &pol, valuename, &value);
+
+ if (W_ERROR_V(result) == ERRmoredata)
+ result = cli_spoolss_getprinterdata(
+ cli, mem_ctx, needed, NULL, &pol, valuename, &value);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ /* Display printer data */
+
+ fstrcpy(value.valuename, valuename);
+ display_reg_value(value);
+
+
+ done:
+ if (opened_hnd)
+ cli_spoolss_close_printer(cli, mem_ctx, &pol);
+
+ return result;
+}
+
+/***********************************************************************
+ * Get printer data
+ */
+static WERROR cmd_spoolss_getprinterdataex(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND pol;
+ WERROR result;
+ BOOL opened_hnd = False;
+ fstring printername,
+ servername,
+ user;
+ uint32 needed;
+ const char *valuename, *keyname;
+ REGISTRY_VALUE value;
+
+ if (argc != 4) {
+ printf("Usage: %s <printername> <keyname> <valuename>\n",
+ argv[0]);
+ printf("<printername> of . queries print server\n");
+ return WERR_OK;
+ }
+ valuename = argv[3];
+ keyname = argv[2];
+
+ /* Open a printer handle */
+
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ if (strncmp(argv[1], ".", sizeof(".")) == 0)
+ fstrcpy(printername, servername);
+ else
+ slprintf(printername, sizeof(printername)-1, "%s\\%s",
+ servername, argv[1]);
+ fstrcpy(user, cli->user_name);
+
+ /* get a printer handle */
+
+ result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername,
+ "", MAXIMUM_ALLOWED_ACCESS,
+ servername, user, &pol);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ opened_hnd = True;
+
+ /* Get printer info */
+
+ result = cli_spoolss_getprinterdataex(cli, mem_ctx, 0, &needed,
+ &pol, keyname, valuename,
+ &value);
+
+ if (W_ERROR_V(result) == ERRmoredata)
+ result = cli_spoolss_getprinterdataex(cli, mem_ctx, needed,
+ NULL, &pol, keyname,
+ valuename, &value);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ /* Display printer data */
+
+ fstrcpy(value.valuename, valuename);
+ display_reg_value(value);
+
+
+ done:
+ if (opened_hnd)
+ cli_spoolss_close_printer(cli, mem_ctx, &pol);
+
+ return result;
+}
+
+/****************************************************************************
+printer info level 0 display function
+****************************************************************************/
+static void display_print_driver_1(DRIVER_INFO_1 *i1)
+{
+ fstring name;
+ if (i1 == NULL)
+ return;
+
+ rpcstr_pull(name, i1->name.buffer, sizeof(name), -1, STR_TERMINATE);
+
+ printf ("Printer Driver Info 1:\n");
+ printf ("\tDriver Name: [%s]\n\n", name);
+
+ return;
+}
+
+/****************************************************************************
+printer info level 1 display function
+****************************************************************************/
+static void display_print_driver_2(DRIVER_INFO_2 *i1)
+{
+ fstring name;
+ fstring architecture;
+ fstring driverpath;
+ fstring datafile;
+ fstring configfile;
+ if (i1 == NULL)
+ return;
+
+ rpcstr_pull(name, i1->name.buffer, sizeof(name), -1, STR_TERMINATE);
+ rpcstr_pull(architecture, i1->architecture.buffer, sizeof(architecture), -1, STR_TERMINATE);
+ rpcstr_pull(driverpath, i1->driverpath.buffer, sizeof(driverpath), -1, STR_TERMINATE);
+ rpcstr_pull(datafile, i1->datafile.buffer, sizeof(datafile), -1, STR_TERMINATE);
+ rpcstr_pull(configfile, i1->configfile.buffer, sizeof(configfile), -1, STR_TERMINATE);
+
+ printf ("Printer Driver Info 2:\n");
+ printf ("\tVersion: [%x]\n", i1->version);
+ printf ("\tDriver Name: [%s]\n", name);
+ printf ("\tArchitecture: [%s]\n", architecture);
+ printf ("\tDriver Path: [%s]\n", driverpath);
+ printf ("\tDatafile: [%s]\n", datafile);
+ printf ("\tConfigfile: [%s]\n\n", configfile);
+
+ return;
+}
+
+/****************************************************************************
+printer info level 2 display function
+****************************************************************************/
+static void display_print_driver_3(DRIVER_INFO_3 *i1)
+{
+ fstring name = "";
+ fstring architecture = "";
+ fstring driverpath = "";
+ fstring datafile = "";
+ fstring configfile = "";
+ fstring helpfile = "";
+ fstring dependentfiles = "";
+ fstring monitorname = "";
+ fstring defaultdatatype = "";
+
+ int length=0;
+ BOOL valid = True;
+
+ if (i1 == NULL)
+ return;
+
+ rpcstr_pull(name, i1->name.buffer, sizeof(name), -1, STR_TERMINATE);
+ rpcstr_pull(architecture, i1->architecture.buffer, sizeof(architecture), -1, STR_TERMINATE);
+ rpcstr_pull(driverpath, i1->driverpath.buffer, sizeof(driverpath), -1, STR_TERMINATE);
+ rpcstr_pull(datafile, i1->datafile.buffer, sizeof(datafile), -1, STR_TERMINATE);
+ rpcstr_pull(configfile, i1->configfile.buffer, sizeof(configfile), -1, STR_TERMINATE);
+ rpcstr_pull(helpfile, i1->helpfile.buffer, sizeof(helpfile), -1, STR_TERMINATE);
+ rpcstr_pull(monitorname, i1->monitorname.buffer, sizeof(monitorname), -1, STR_TERMINATE);
+ rpcstr_pull(defaultdatatype, i1->defaultdatatype.buffer, sizeof(defaultdatatype), -1, STR_TERMINATE);
+
+ printf ("Printer Driver Info 3:\n");
+ printf ("\tVersion: [%x]\n", i1->version);
+ printf ("\tDriver Name: [%s]\n",name);
+ printf ("\tArchitecture: [%s]\n", architecture);
+ printf ("\tDriver Path: [%s]\n", driverpath);
+ printf ("\tDatafile: [%s]\n", datafile);
+ printf ("\tConfigfile: [%s]\n", configfile);
+ printf ("\tHelpfile: [%s]\n\n", helpfile);
+
+ while (valid)
+ {
+ rpcstr_pull(dependentfiles, i1->dependentfiles+length, sizeof(dependentfiles), -1, STR_TERMINATE);
+
+ length+=strlen(dependentfiles)+1;
+
+ if (strlen(dependentfiles) > 0)
+ {
+ printf ("\tDependentfiles: [%s]\n", dependentfiles);
+ }
+ else
+ {
+ valid = False;
+ }
+ }
+
+ printf ("\n");
+
+ printf ("\tMonitorname: [%s]\n", monitorname);
+ printf ("\tDefaultdatatype: [%s]\n\n", defaultdatatype);
+
+ return;
+}
+
+/***********************************************************************
+ * Get printer information
+ */
+static WERROR cmd_spoolss_getdriver(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND pol;
+ WERROR werror;
+ uint32 info_level = 3;
+ BOOL opened_hnd = False;
+ PRINTER_DRIVER_CTR ctr;
+ fstring printername,
+ servername,
+ user;
+ uint32 i;
+ BOOL success = False;
+
+ if ((argc == 1) || (argc > 3))
+ {
+ printf("Usage: %s <printername> [level]\n", argv[0]);
+ return WERR_OK;
+ }
+
+ /* get the arguments need to open the printer handle */
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ fstrcpy(user, cli->user_name);
+ fstrcpy(printername, argv[1]);
+ if (argc == 3)
+ info_level = atoi(argv[2]);
+
+ /* Open a printer handle */
+
+ werror = cli_spoolss_open_printer_ex(cli, mem_ctx, printername, "",
+ PRINTER_ACCESS_USE,
+ servername, user, &pol);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ printf("Error opening printer handle for %s!\n", printername);
+ return werror;
+ }
+
+ opened_hnd = True;
+
+ /* loop through and print driver info level for each architecture */
+
+ for (i=0; archi_table[i].long_archi!=NULL; i++) {
+ uint32 needed;
+
+ werror = cli_spoolss_getprinterdriver(
+ cli, mem_ctx, 0, &needed, &pol, info_level,
+ archi_table[i].long_archi, archi_table[i].version,
+ &ctr);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer) {
+ werror = cli_spoolss_getprinterdriver(
+ cli, mem_ctx, needed, NULL, &pol, info_level,
+ archi_table[i].long_archi, archi_table[i].version,
+ &ctr);
+ }
+
+ if (!W_ERROR_IS_OK(werror))
+ continue;
+
+ /* need at least one success */
+
+ success = True;
+
+ printf ("\n[%s]\n", archi_table[i].long_archi);
+
+ switch (info_level) {
+ case 1:
+ display_print_driver_1 (ctr.info1);
+ break;
+ case 2:
+ display_print_driver_2 (ctr.info2);
+ break;
+ case 3:
+ display_print_driver_3 (ctr.info3);
+ break;
+ default:
+ printf("unknown info level %d\n", info_level);
+ break;
+ }
+ }
+
+ /* Cleanup */
+
+ if (opened_hnd)
+ cli_spoolss_close_printer (cli, mem_ctx, &pol);
+
+ if ( success )
+ werror = WERR_OK;
+
+ return werror;
+}
+
+/***********************************************************************
+ * Get printer information
+ */
+static WERROR cmd_spoolss_enum_drivers(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ WERROR werror;
+ uint32 info_level = 1;
+ PRINTER_DRIVER_CTR ctr;
+ uint32 i, j,
+ returned;
+
+ if (argc > 2)
+ {
+ printf("Usage: enumdrivers [level]\n");
+ return WERR_OK;
+ }
+
+ if (argc == 2)
+ info_level = atoi(argv[1]);
+
+
+ /* loop through and print driver info level for each architecture */
+ for (i=0; archi_table[i].long_archi!=NULL; i++)
+ {
+ uint32 needed;
+
+ werror = cli_spoolss_enumprinterdrivers(
+ cli, mem_ctx, 0, &needed, info_level,
+ archi_table[i].long_archi, &returned, &ctr);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+ werror = cli_spoolss_enumprinterdrivers(
+ cli, mem_ctx, needed, NULL, info_level,
+ archi_table[i].long_archi, &returned, &ctr);
+
+ if (returned == 0)
+ continue;
+
+ if (!W_ERROR_IS_OK(werror)) {
+ printf ("Error getting driver for environment [%s] - %d\n",
+ archi_table[i].long_archi, W_ERROR_V(werror));
+ continue;
+ }
+
+ printf ("\n[%s]\n", archi_table[i].long_archi);
+ switch (info_level)
+ {
+
+ case 1:
+ for (j=0; j < returned; j++) {
+ display_print_driver_1 (&(ctr.info1[j]));
+ }
+ break;
+ case 2:
+ for (j=0; j < returned; j++) {
+ display_print_driver_2 (&(ctr.info2[j]));
+ }
+ break;
+ case 3:
+ for (j=0; j < returned; j++) {
+ display_print_driver_3 (&(ctr.info3[j]));
+ }
+ break;
+ default:
+ printf("unknown info level %d\n", info_level);
+ break;
+ }
+ }
+
+ return werror;
+}
+
+/****************************************************************************
+printer info level 1 display function
+****************************************************************************/
+static void display_printdriverdir_1(DRIVER_DIRECTORY_1 *i1)
+{
+ fstring name;
+ if (i1 == NULL)
+ return;
+
+ rpcstr_pull(name, i1->name.buffer, sizeof(name), -1, STR_TERMINATE);
+
+ printf ("\tDirectory Name:[%s]\n", name);
+}
+
+/***********************************************************************
+ * Get printer driver directory information
+ */
+static WERROR cmd_spoolss_getdriverdir(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ WERROR result;
+ fstring env;
+ DRIVER_DIRECTORY_CTR ctr;
+ uint32 needed;
+
+ if (argc > 2) {
+ printf("Usage: %s [environment]\n", argv[0]);
+ return WERR_OK;
+ }
+
+ /* Get the arguments need to open the printer handle */
+
+ if (argc == 2)
+ fstrcpy (env, argv[1]);
+ else
+ fstrcpy (env, "Windows NT x86");
+
+ /* Get the directory. Only use Info level 1 */
+
+ result = cli_spoolss_getprinterdriverdir(
+ cli, mem_ctx, 0, &needed, 1, env, &ctr);
+
+ if (W_ERROR_V(result) == ERRinsufficientbuffer)
+ result = cli_spoolss_getprinterdriverdir(
+ cli, mem_ctx, needed, NULL, 1, env, &ctr);
+
+ if (W_ERROR_IS_OK(result))
+ display_printdriverdir_1(ctr.info1);
+
+ return result;
+}
+
+/*******************************************************************************
+ set the version and environment fields of a DRIVER_INFO_3 struct
+ ******************************************************************************/
+void set_drv_info_3_env (DRIVER_INFO_3 *info, const char *arch)
+{
+
+ int i;
+
+ for (i=0; archi_table[i].long_archi != NULL; i++)
+ {
+ if (strcmp(arch, archi_table[i].short_archi) == 0)
+ {
+ info->version = archi_table[i].version;
+ init_unistr (&info->architecture, archi_table[i].long_archi);
+ break;
+ }
+ }
+
+ if (archi_table[i].long_archi == NULL)
+ {
+ DEBUG(0, ("set_drv_info_3_env: Unknown arch [%s]\n", arch));
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ wrapper for strtok to get the next parameter from a delimited list.
+ Needed to handle the empty parameter string denoted by "NULL"
+ *************************************************************************/
+static char* get_driver_3_param (const char* str, const char* delim, UNISTR* dest)
+{
+ char *ptr;
+
+ /* get the next token */
+ ptr = strtok(str, delim);
+
+ /* a string of 'NULL' is used to represent an empty
+ parameter because two consecutive delimiters
+ will not return an empty string. See man strtok(3)
+ for details */
+ if (ptr && (StrCaseCmp(ptr, "NULL") == 0))
+ ptr = NULL;
+
+ if (dest != NULL)
+ init_unistr(dest, ptr);
+
+ return ptr;
+}
+
+/********************************************************************************
+ fill in the members of a DRIVER_INFO_3 struct using a character
+ string in the form of
+ <Long Printer Name>:<Driver File Name>:<Data File Name>:\
+ <Config File Name>:<Help File Name>:<Language Monitor Name>:\
+ <Default Data Type>:<Comma Separated list of Files>
+ *******************************************************************************/
+static BOOL init_drv_info_3_members (
+ TALLOC_CTX *mem_ctx,
+ DRIVER_INFO_3 *info,
+ const char *args
+)
+{
+ char *str, *str2;
+ uint32 len, i;
+
+ /* fill in the UNISTR fields */
+ str = get_driver_3_param (args, ":", &info->name);
+ str = get_driver_3_param (NULL, ":", &info->driverpath);
+ str = get_driver_3_param (NULL, ":", &info->datafile);
+ str = get_driver_3_param (NULL, ":", &info->configfile);
+ str = get_driver_3_param (NULL, ":", &info->helpfile);
+ str = get_driver_3_param (NULL, ":", &info->monitorname);
+ str = get_driver_3_param (NULL, ":", &info->defaultdatatype);
+
+ /* <Comma Separated List of Dependent Files> */
+ str2 = get_driver_3_param (NULL, ":", NULL); /* save the beginning of the string */
+ str = str2;
+
+ /* begin to strip out each filename */
+ str = strtok(str, ",");
+ len = 0;
+ while (str != NULL)
+ {
+ /* keep a cumlative count of the str lengths */
+ len += strlen(str)+1;
+ str = strtok(NULL, ",");
+ }
+
+ /* allocate the space; add one extra slot for a terminating NULL.
+ Each filename is NULL terminated and the end contains a double
+ NULL */
+ if ((info->dependentfiles=(uint16*)talloc(mem_ctx, (len+1)*sizeof(uint16))) == NULL)
+ {
+ DEBUG(0,("init_drv_info_3_members: Unable to malloc memory for dependenfiles\n"));
+ return False;
+ }
+ for (i=0; i<len; i++)
+ {
+ SSVAL(&info->dependentfiles[i], 0, str2[i]);
+ }
+ info->dependentfiles[len] = '\0';
+
+ return True;
+}
+
+
+static WERROR cmd_spoolss_addprinterdriver(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ WERROR result;
+ uint32 level = 3;
+ PRINTER_DRIVER_CTR ctr;
+ DRIVER_INFO_3 info3;
+ const char *arch;
+ fstring driver_name;
+
+ /* parse the command arguements */
+ if (argc != 3 && argc != 4)
+ {
+ printf ("Usage: %s <Environment> \\\n", argv[0]);
+ printf ("\t<Long Printer Name>:<Driver File Name>:<Data File Name>:\\\n");
+ printf ("\t<Config File Name>:<Help File Name>:<Language Monitor Name>:\\\n");
+ printf ("\t<Default Data Type>:<Comma Separated list of Files> \\\n");
+ printf ("\t[version]\n");
+
+ return WERR_OK;
+ }
+
+ /* Fill in the DRIVER_INFO_3 struct */
+ ZERO_STRUCT(info3);
+ if (!(arch = cmd_spoolss_get_short_archi(argv[1])))
+ {
+ printf ("Error Unknown architechture [%s]\n", argv[1]);
+ return WERR_INVALID_PARAM;
+ }
+ else
+ set_drv_info_3_env(&info3, arch);
+
+ if (!init_drv_info_3_members(mem_ctx, &info3, argv[2]))
+ {
+ printf ("Error Invalid parameter list - %s.\n", argv[2]);
+ return WERR_INVALID_PARAM;
+ }
+
+ /* if printer driver version specified, override the default version
+ * used by the architecture. This allows installation of Windows
+ * 2000 (version 3) printer drivers. */
+ if (argc == 4)
+ {
+ info3.version = atoi(argv[3]);
+ }
+
+
+ ctr.info3 = &info3;
+ result = cli_spoolss_addprinterdriver (cli, mem_ctx, level, &ctr);
+
+ if (W_ERROR_IS_OK(result)) {
+ rpcstr_pull(driver_name, info3.name.buffer,
+ sizeof(driver_name), -1, STR_TERMINATE);
+ printf ("Printer Driver %s successfully installed.\n",
+ driver_name);
+ }
+
+ return result;
+}
+
+
+static WERROR cmd_spoolss_addprinterex(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ WERROR result;
+ uint32 level = 2;
+ PRINTER_INFO_CTR ctr;
+ PRINTER_INFO_2 info2;
+ fstring servername;
+
+ /* parse the command arguements */
+ if (argc != 5)
+ {
+ printf ("Usage: %s <name> <shared name> <driver> <port>\n", argv[0]);
+ return WERR_OK;
+ }
+
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+
+ /* Fill in the DRIVER_INFO_2 struct */
+ ZERO_STRUCT(info2);
+#if 0 /* JERRY */
+ init_unistr( &info2.servername, servername);
+#endif
+ init_unistr( &info2.printername, argv[1]);
+ init_unistr( &info2.sharename, argv[2]);
+ init_unistr( &info2.drivername, argv[3]);
+ init_unistr( &info2.portname, argv[4]);
+ init_unistr( &info2.comment, "Created by rpcclient");
+ init_unistr( &info2.printprocessor, "winprint");
+ init_unistr( &info2.datatype, "RAW");
+ info2.devmode = NULL;
+ info2.secdesc = NULL;
+ info2.attributes = PRINTER_ATTRIBUTE_SHARED;
+ info2.priority = 0;
+ info2.defaultpriority = 0;
+ info2.starttime = 0;
+ info2.untiltime = 0;
+
+ /* These three fields must not be used by AddPrinter()
+ as defined in the MS Platform SDK documentation..
+ --jerry
+ info2.status = 0;
+ info2.cjobs = 0;
+ info2.averageppm = 0;
+ */
+
+ ctr.printers_2 = &info2;
+ result = cli_spoolss_addprinterex (cli, mem_ctx, level, &ctr);
+
+ if (W_ERROR_IS_OK(result))
+ printf ("Printer %s successfully installed.\n", argv[1]);
+
+ return result;
+}
+
+static WERROR cmd_spoolss_setdriver(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND pol;
+ WERROR result;
+ uint32 level = 2;
+ BOOL opened_hnd = False;
+ PRINTER_INFO_CTR ctr;
+ PRINTER_INFO_2 info2;
+ fstring servername,
+ printername,
+ user;
+ uint32 needed;
+
+ /* parse the command arguements */
+ if (argc != 3)
+ {
+ printf ("Usage: %s <printer> <driver>\n", argv[0]);
+ return WERR_OK;
+ }
+
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ slprintf(printername, sizeof(printername)-1, "%s\\%s", servername, argv[1]);
+ fstrcpy(user, cli->user_name);
+
+ /* Get a printer handle */
+
+ result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername, "",
+ PRINTER_ALL_ACCESS,
+ servername, user, &pol);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ opened_hnd = True;
+
+ /* Get printer info */
+
+ ZERO_STRUCT (info2);
+ ctr.printers_2 = &info2;
+
+ result = cli_spoolss_getprinter(cli, mem_ctx, 0, &needed,
+ &pol, level, &ctr);
+
+ if (W_ERROR_V(result) == ERRinsufficientbuffer)
+ result = cli_spoolss_getprinter(
+ cli, mem_ctx, needed, NULL, &pol, level, &ctr);
+
+ if (!W_ERROR_IS_OK(result)) {
+ printf ("Unable to retrieve printer information!\n");
+ goto done;
+ }
+
+ /* Set the printer driver */
+
+ init_unistr(&ctr.printers_2->drivername, argv[2]);
+
+ result = cli_spoolss_setprinter(cli, mem_ctx, &pol, level, &ctr, 0);
+
+ if (!W_ERROR_IS_OK(result)) {
+ printf("SetPrinter call failed!\n");
+ goto done;;
+ }
+
+ printf("Succesfully set %s to driver %s.\n", argv[1], argv[2]);
+
+done:
+ /* Cleanup */
+
+ if (opened_hnd)
+ cli_spoolss_close_printer(cli, mem_ctx, &pol);
+
+ return result;
+}
+
+
+static WERROR cmd_spoolss_deletedriver(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ WERROR result;
+ fstring servername;
+ int i;
+
+ /* parse the command arguements */
+ if (argc != 2)
+ {
+ printf ("Usage: %s <driver>\n", argv[0]);
+ return WERR_OK;
+ }
+
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+
+ /* delete the driver for all architectures */
+ for (i=0; archi_table[i].long_archi; i++)
+ {
+ /* make the call to remove the driver */
+ result = cli_spoolss_deleteprinterdriver(
+ cli, mem_ctx, archi_table[i].long_archi, argv[1]);
+
+ if ( !W_ERROR_IS_OK(result) ) {
+ if ( !W_ERROR_EQUAL(result, WERR_UNKNOWN_PRINTER_DRIVER) ) {
+ printf ("Failed to remove driver %s for arch [%s] - error 0x%x!\n",
+ argv[1], archi_table[i].long_archi,
+ W_ERROR_V(result));
+ }
+ }
+ else
+ {
+ printf ("Driver %s removed for arch [%s].\n", argv[1],
+ archi_table[i].long_archi);
+ }
+ }
+
+ return result;
+}
+
+static WERROR cmd_spoolss_getprintprocdir(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ WERROR result;
+ char *servername = NULL, *environment = NULL;
+ fstring procdir;
+ uint32 needed;
+
+ /* parse the command arguements */
+ if (argc > 2) {
+ printf ("Usage: %s [environment]\n", argv[0]);
+ return WERR_OK;
+ }
+
+ if (asprintf(&servername, "\\\\%s", cli->desthost) < 0)
+ return WERR_NOMEM;
+ strupper_m(servername);
+
+ if (asprintf(&environment, "%s", (argc == 2) ? argv[1] :
+ PRINTER_DRIVER_ARCHITECTURE) < 0) {
+ SAFE_FREE(servername);
+ return WERR_NOMEM;
+ }
+
+ result = cli_spoolss_getprintprocessordirectory(
+ cli, mem_ctx, 0, &needed, servername, environment, procdir);
+
+ if (W_ERROR_V(result) == ERRinsufficientbuffer)
+ result = cli_spoolss_getprintprocessordirectory(
+ cli, mem_ctx, needed, NULL, servername, environment,
+ procdir);
+
+ if (W_ERROR_IS_OK(result))
+ printf("%s\n", procdir);
+
+ SAFE_FREE(servername);
+ SAFE_FREE(environment);
+
+ return result;
+}
+
+/* Add a form */
+
+static WERROR cmd_spoolss_addform(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND handle;
+ WERROR werror;
+ char *servername = NULL, *printername = NULL;
+ FORM form;
+ BOOL got_handle = False;
+
+ /* Parse the command arguements */
+
+ if (argc != 3) {
+ printf ("Usage: %s <printer> <formname>\n", argv[0]);
+ return WERR_OK;
+ }
+
+ /* Get a printer handle */
+
+ asprintf(&servername, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ asprintf(&printername, "%s\\%s", servername, argv[1]);
+
+ werror = cli_spoolss_open_printer_ex(cli, mem_ctx, printername, "",
+ MAXIMUM_ALLOWED_ACCESS,
+ servername, cli->user_name, &handle);
+
+ if (!W_ERROR_IS_OK(werror))
+ goto done;
+
+ got_handle = True;
+
+ /* Dummy up some values for the form data */
+
+ form.flags = FORM_USER;
+ form.size_x = form.size_y = 100;
+ form.left = 0;
+ form.top = 10;
+ form.right = 20;
+ form.bottom = 30;
+
+ init_unistr2(&form.name, argv[2], UNI_STR_TERMINATE);
+
+ /* Add the form */
+
+
+ werror = cli_spoolss_addform(cli, mem_ctx, &handle, 1, &form);
+
+ done:
+ if (got_handle)
+ cli_spoolss_close_printer(cli, mem_ctx, &handle);
+
+ SAFE_FREE(servername);
+ SAFE_FREE(printername);
+
+ return werror;
+}
+
+/* Set a form */
+
+static WERROR cmd_spoolss_setform(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND handle;
+ WERROR werror;
+ char *servername = NULL, *printername = NULL;
+ FORM form;
+ BOOL got_handle = False;
+
+ /* Parse the command arguements */
+
+ if (argc != 3) {
+ printf ("Usage: %s <printer> <formname>\n", argv[0]);
+ return WERR_OK;
+ }
+
+ /* Get a printer handle */
+
+ asprintf(&servername, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ asprintf(&printername, "%s\\%s", servername, argv[1]);
+
+ werror = cli_spoolss_open_printer_ex(
+ cli, mem_ctx, printername, "", MAXIMUM_ALLOWED_ACCESS,
+ servername, cli->user_name, &handle);
+
+ if (!W_ERROR_IS_OK(werror))
+ goto done;
+
+ got_handle = True;
+
+ /* Dummy up some values for the form data */
+
+ form.flags = FORM_PRINTER;
+ form.size_x = form.size_y = 100;
+ form.left = 0;
+ form.top = 1000;
+ form.right = 2000;
+ form.bottom = 3000;
+
+ init_unistr2(&form.name, argv[2], UNI_STR_TERMINATE);
+
+ /* Set the form */
+
+ werror = cli_spoolss_setform(cli, mem_ctx, &handle, 1, argv[2], &form);
+
+ done:
+ if (got_handle)
+ cli_spoolss_close_printer(cli, mem_ctx, &handle);
+
+ SAFE_FREE(servername);
+ SAFE_FREE(printername);
+
+ return werror;
+}
+
+/* Get a form */
+
+static WERROR cmd_spoolss_getform(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND handle;
+ WERROR werror;
+ char *servername = NULL, *printername = NULL;
+ FORM_1 form;
+ BOOL got_handle = False;
+ uint32 needed;
+
+ /* Parse the command arguements */
+
+ if (argc != 3) {
+ printf ("Usage: %s <printer> <formname>\n", argv[0]);
+ return WERR_OK;
+ }
+
+ /* Get a printer handle */
+
+ asprintf(&servername, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ asprintf(&printername, "%s\\%s", servername, argv[1]);
+
+ werror = cli_spoolss_open_printer_ex(
+ cli, mem_ctx, printername, "", MAXIMUM_ALLOWED_ACCESS,
+ servername, cli->user_name, &handle);
+
+ if (!W_ERROR_IS_OK(werror))
+ goto done;
+
+ got_handle = True;
+
+ /* Set the form */
+
+ werror = cli_spoolss_getform(cli, mem_ctx, 0, &needed,
+ &handle, argv[2], 1, &form);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+ werror = cli_spoolss_getform(cli, mem_ctx, needed, NULL,
+ &handle, argv[2], 1, &form);
+
+ if (!W_ERROR_IS_OK(werror))
+ goto done;
+
+ printf("width: %d\n", form.width);
+ printf("length: %d\n", form.length);
+ printf("left: %d\n", form.left);
+ printf("top: %d\n", form.top);
+ printf("right: %d\n", form.right);
+ printf("bottom: %d\n", form.bottom);
+
+ done:
+ if (got_handle)
+ cli_spoolss_close_printer(cli, mem_ctx, &handle);
+
+ SAFE_FREE(servername);
+ SAFE_FREE(printername);
+
+ return werror;
+}
+
+/* Delete a form */
+
+static WERROR cmd_spoolss_deleteform(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND handle;
+ WERROR werror;
+ char *servername = NULL, *printername = NULL;
+ BOOL got_handle = False;
+
+ /* Parse the command arguements */
+
+ if (argc != 3) {
+ printf ("Usage: %s <printer> <formname>\n", argv[0]);
+ return WERR_OK;
+ }
+
+ /* Get a printer handle */
+
+ asprintf(&servername, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ asprintf(&printername, "%s\\%s", servername, argv[1]);
+
+ werror = cli_spoolss_open_printer_ex(
+ cli, mem_ctx, printername, "", MAXIMUM_ALLOWED_ACCESS,
+ servername, cli->user_name, &handle);
+
+ if (!W_ERROR_IS_OK(werror))
+ goto done;
+
+ got_handle = True;
+
+ /* Delete the form */
+
+ werror = cli_spoolss_deleteform(cli, mem_ctx, &handle, argv[2]);
+
+ done:
+ if (got_handle)
+ cli_spoolss_close_printer(cli, mem_ctx, &handle);
+
+ SAFE_FREE(servername);
+ SAFE_FREE(printername);
+
+ return werror;
+}
+
+/* Enumerate forms */
+
+static WERROR cmd_spoolss_enum_forms(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND handle;
+ WERROR werror;
+ char *servername = NULL, *printername = NULL;
+ BOOL got_handle = False;
+ uint32 needed, num_forms, level = 1, i;
+ FORM_1 *forms;
+
+ /* Parse the command arguements */
+
+ if (argc != 2) {
+ printf ("Usage: %s <printer>\n", argv[0]);
+ return WERR_OK;
+ }
+
+ /* Get a printer handle */
+
+ asprintf(&servername, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ asprintf(&printername, "%s\\%s", servername, argv[1]);
+
+ werror = cli_spoolss_open_printer_ex(
+ cli, mem_ctx, printername, "", MAXIMUM_ALLOWED_ACCESS,
+ servername, cli->user_name, &handle);
+
+ if (!W_ERROR_IS_OK(werror))
+ goto done;
+
+ got_handle = True;
+
+ /* Enumerate forms */
+
+ werror = cli_spoolss_enumforms(
+ cli, mem_ctx, 0, &needed, &handle, level, &num_forms, &forms);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+ werror = cli_spoolss_enumforms(
+ cli, mem_ctx, needed, NULL, &handle, level,
+ &num_forms, &forms);
+
+ if (!W_ERROR_IS_OK(werror))
+ goto done;
+
+ /* Display output */
+
+ for (i = 0; i < num_forms; i++) {
+ fstring form_name;
+
+ if (forms[i].name.buffer)
+ rpcstr_pull(form_name, forms[i].name.buffer,
+ sizeof(form_name), -1, STR_TERMINATE);
+
+ printf("%s\n", form_name);
+ }
+
+ done:
+ if (got_handle)
+ cli_spoolss_close_printer(cli, mem_ctx, &handle);
+
+ SAFE_FREE(servername);
+ SAFE_FREE(printername);
+
+ return werror;
+}
+
+static WERROR cmd_spoolss_setprinterdata(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ WERROR result;
+ uint32 needed;
+ fstring servername, printername, user;
+ POLICY_HND pol;
+ BOOL opened_hnd = False;
+ PRINTER_INFO_CTR ctr;
+ PRINTER_INFO_0 info;
+ REGISTRY_VALUE value;
+
+ /* parse the command arguements */
+ if (argc != 4) {
+ printf ("Usage: %s <printer> <value> <data>\n", argv[0]);
+ return WERR_OK;
+ }
+
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ slprintf(printername, sizeof(servername)-1, "%s\\%s", servername, argv[1]);
+ fstrcpy(user, cli->user_name);
+
+ /* get a printer handle */
+ result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername, "",
+ MAXIMUM_ALLOWED_ACCESS, servername,
+ user, &pol);
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ opened_hnd = True;
+
+ ctr.printers_0 = &info;
+
+ result = cli_spoolss_getprinter(cli, mem_ctx, 0, &needed,
+ &pol, 0, &ctr);
+
+ if (W_ERROR_V(result) == ERRinsufficientbuffer)
+ result = cli_spoolss_getprinter(cli, mem_ctx, needed, NULL, &pol, 0, &ctr);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ printf("%s\n", timestring(True));
+ printf("\tchange_id (before set)\t:[0x%x]\n", info.change_id);
+
+ /* Set the printer data */
+
+ fstrcpy(value.valuename, argv[2]);
+ value.type = REG_SZ;
+ value.size = strlen(argv[3]) + 1;
+ value.data_p = talloc_memdup(mem_ctx, argv[3], value.size);
+
+ result = cli_spoolss_setprinterdata(cli, mem_ctx, &pol, &value);
+
+ if (!W_ERROR_IS_OK(result)) {
+ printf ("Unable to set [%s=%s]!\n", argv[2], argv[3]);
+ goto done;
+ }
+ printf("\tSetPrinterData succeeded [%s: %s]\n", argv[2], argv[3]);
+
+ result = cli_spoolss_getprinter(cli, mem_ctx, 0, &needed, &pol, 0, &ctr);
+
+ if (W_ERROR_V(result) == ERRinsufficientbuffer)
+ result = cli_spoolss_getprinter(cli, mem_ctx, needed, NULL, &pol, 0, &ctr);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ printf("%s\n", timestring(True));
+ printf("\tchange_id (after set)\t:[0x%x]\n", info.change_id);
+
+done:
+ /* cleanup */
+ if (opened_hnd)
+ cli_spoolss_close_printer(cli, mem_ctx, &pol);
+
+ return result;
+}
+
+static void display_job_info_1(JOB_INFO_1 *job)
+{
+ fstring username = "", document = "", text_status = "";
+
+ rpcstr_pull(username, job->username.buffer,
+ sizeof(username), -1, STR_TERMINATE);
+
+ rpcstr_pull(document, job->document.buffer,
+ sizeof(document), -1, STR_TERMINATE);
+
+ rpcstr_pull(text_status, job->text_status.buffer,
+ sizeof(text_status), -1, STR_TERMINATE);
+
+ printf("%d: jobid[%d]: %s %s %s %d/%d pages\n", job->position, job->jobid,
+ username, document, text_status, job->pagesprinted,
+ job->totalpages);
+}
+
+static void display_job_info_2(JOB_INFO_2 *job)
+{
+ fstring username = "", document = "", text_status = "";
+
+ rpcstr_pull(username, job->username.buffer,
+ sizeof(username), -1, STR_TERMINATE);
+
+ rpcstr_pull(document, job->document.buffer,
+ sizeof(document), -1, STR_TERMINATE);
+
+ rpcstr_pull(text_status, job->text_status.buffer,
+ sizeof(text_status), -1, STR_TERMINATE);
+
+ printf("%d: jobid[%d]: %s %s %s %d/%d pages, %d bytes\n", job->position, job->jobid,
+ username, document, text_status, job->pagesprinted,
+ job->totalpages, job->size);
+}
+
+/* Enumerate jobs */
+
+static WERROR cmd_spoolss_enum_jobs(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ WERROR result;
+ uint32 needed, level = 1, num_jobs, i;
+ BOOL got_hnd = False;
+ pstring printername;
+ fstring servername, user;
+ POLICY_HND hnd;
+ JOB_INFO_CTR ctr;
+
+ if (argc < 2 || argc > 3) {
+ printf("Usage: %s printername [level]\n", argv[0]);
+ return WERR_OK;
+ }
+
+ if (argc == 3)
+ level = atoi(argv[2]);
+
+ /* Open printer handle */
+
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ fstrcpy(user, cli->user_name);
+ slprintf(printername, sizeof(servername)-1, "\\\\%s\\", cli->desthost);
+ strupper_m(printername);
+ pstrcat(printername, argv[1]);
+
+ result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername,
+ "", MAXIMUM_ALLOWED_ACCESS,
+ servername, user, &hnd);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ got_hnd = True;
+
+ /* Enumerate ports */
+
+ result = cli_spoolss_enumjobs(
+ cli, mem_ctx, 0, &needed, &hnd, level, 0, 1000,
+ &num_jobs, &ctr);
+
+ if (W_ERROR_V(result) == ERRinsufficientbuffer)
+ result = cli_spoolss_enumjobs(
+ cli, mem_ctx, needed, NULL, &hnd, level, 0,
+ 1000, &num_jobs, &ctr);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ for (i = 0; i < num_jobs; i++) {
+ switch(level) {
+ case 1:
+ display_job_info_1(&ctr.job.job_info_1[i]);
+ break;
+ case 2:
+ display_job_info_2(&ctr.job.job_info_2[i]);
+ break;
+ default:
+ d_printf("unknown info level %d\n", level);
+ break;
+ }
+ }
+
+done:
+ if (got_hnd)
+ cli_spoolss_close_printer(cli, mem_ctx, &hnd);
+
+ return result;
+}
+
+/* enumerate data */
+
+static WERROR cmd_spoolss_enum_data( struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ WERROR result;
+ uint32 i=0, val_needed, data_needed;
+ BOOL got_hnd = False;
+ pstring printername;
+ fstring servername, user;
+ POLICY_HND hnd;
+
+ if (argc != 2) {
+ printf("Usage: %s printername\n", argv[0]);
+ return WERR_OK;
+ }
+
+ /* Open printer handle */
+
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ fstrcpy(user, cli->user_name);
+ slprintf(printername, sizeof(printername)-1, "\\\\%s\\", cli->desthost);
+ strupper_m(printername);
+ pstrcat(printername, argv[1]);
+
+ result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername,
+ "", MAXIMUM_ALLOWED_ACCESS,
+ servername, user, &hnd);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ got_hnd = True;
+
+ /* Enumerate data */
+
+ result = cli_spoolss_enumprinterdata(cli, mem_ctx, &hnd, i, 0, 0,
+ &val_needed, &data_needed,
+ NULL);
+ while (W_ERROR_IS_OK(result)) {
+ REGISTRY_VALUE value;
+ result = cli_spoolss_enumprinterdata(
+ cli, mem_ctx, &hnd, i++, val_needed,
+ data_needed, 0, 0, &value);
+ if (W_ERROR_IS_OK(result))
+ display_reg_value(value);
+ }
+ if (W_ERROR_V(result) == ERRnomoreitems)
+ result = W_ERROR(ERRsuccess);
+
+done:
+ if (got_hnd)
+ cli_spoolss_close_printer(cli, mem_ctx, &hnd);
+
+ return result;
+}
+
+/* enumerate data for a given key */
+
+static WERROR cmd_spoolss_enum_data_ex( struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ WERROR result;
+ uint32 needed, i;
+ BOOL got_hnd = False;
+ pstring printername;
+ fstring servername, user;
+ const char *keyname = NULL;
+ POLICY_HND hnd;
+ REGVAL_CTR ctr;
+
+ if (argc != 3) {
+ printf("Usage: %s printername <keyname>\n", argv[0]);
+ return WERR_OK;
+ }
+
+ keyname = argv[2];
+
+ /* Open printer handle */
+
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ fstrcpy(user, cli->user_name);
+ slprintf(printername, sizeof(printername)-1, "\\\\%s\\", cli->desthost);
+ strupper_m(printername);
+ pstrcat(printername, argv[1]);
+
+ result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername,
+ "", MAXIMUM_ALLOWED_ACCESS,
+ servername, user, &hnd);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ got_hnd = True;
+
+ /* Enumerate subkeys */
+
+ result = cli_spoolss_enumprinterdataex(
+ cli, mem_ctx, 0, &needed, &hnd, keyname, NULL);
+
+ if (W_ERROR_V(result) == ERRmoredata)
+ result = cli_spoolss_enumprinterdataex(
+ cli, mem_ctx, needed, NULL, &hnd, keyname, &ctr);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ for (i=0; i < ctr.num_values; i++) {
+ display_reg_value(*(ctr.values[i]));
+ }
+
+ regval_ctr_destroy(&ctr);
+
+done:
+ if (got_hnd)
+ cli_spoolss_close_printer(cli, mem_ctx, &hnd);
+
+ return result;
+}
+
+/* enumerate subkeys */
+
+static WERROR cmd_spoolss_enum_printerkey( struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ WERROR result;
+ uint32 needed, returned;
+ BOOL got_hnd = False;
+ pstring printername;
+ fstring servername, user;
+ const char *keyname = NULL;
+ POLICY_HND hnd;
+ uint16 *keylist = NULL, *curkey;
+
+ if (argc < 2 || argc > 3) {
+ printf("Usage: %s printername [keyname]\n", argv[0]);
+ return WERR_OK;
+ }
+
+ if (argc == 3)
+ keyname = argv[2];
+ else
+ keyname = "";
+
+ /* Open printer handle */
+
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ fstrcpy(user, cli->user_name);
+ slprintf(printername, sizeof(printername)-1, "\\\\%s\\", cli->desthost);
+ strupper_m(printername);
+ pstrcat(printername, argv[1]);
+
+ result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername,
+ "", MAXIMUM_ALLOWED_ACCESS,
+ servername, user, &hnd);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ got_hnd = True;
+
+ /* Enumerate subkeys */
+
+ result = cli_spoolss_enumprinterkey(
+ cli, mem_ctx, 0, &needed, &hnd, keyname, NULL, NULL);
+
+ if (W_ERROR_V(result) == ERRmoredata)
+ result = cli_spoolss_enumprinterkey(
+ cli, mem_ctx, needed, NULL, &hnd, keyname, &keylist,
+ &returned);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ curkey = keylist;
+ while (*curkey != 0) {
+ pstring subkey;
+ rpcstr_pull(subkey, curkey, sizeof(subkey), -1,
+ STR_TERMINATE);
+ printf("%s\n", subkey);
+ curkey += strlen(subkey) + 1;
+ }
+
+ safe_free(keylist);
+
+done:
+ if (got_hnd)
+ cli_spoolss_close_printer(cli, mem_ctx, &hnd);
+
+ return result;
+}
+
+static WERROR cmd_spoolss_rffpcnex(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ fstring servername, printername;
+ POLICY_HND hnd;
+ BOOL got_hnd = False;
+ WERROR result;
+ SPOOL_NOTIFY_OPTION option;
+
+ if (argc != 2) {
+ printf("Usage: %s printername\n", argv[0]);
+ result = WERR_OK;
+ goto done;
+ }
+
+ /* Open printer */
+
+ slprintf(servername, sizeof(servername) - 1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+
+ slprintf(printername, sizeof(printername) - 1, "\\\\%s\\%s", cli->desthost,
+ argv[1]);
+ strupper_m(printername);
+
+ result = cli_spoolss_open_printer_ex(
+ cli, mem_ctx, printername, "", MAXIMUM_ALLOWED_ACCESS,
+ servername, cli->user_name, &hnd);
+
+ if (!W_ERROR_IS_OK(result)) {
+ printf("Error opening %s\n", argv[1]);
+ goto done;
+ }
+
+ got_hnd = True;
+
+ /* Create spool options */
+
+ ZERO_STRUCT(option);
+
+ option.version = 2;
+ option.option_type_ptr = 1;
+ option.count = option.ctr.count = 2;
+
+ option.ctr.type = (SPOOL_NOTIFY_OPTION_TYPE *)talloc(
+ mem_ctx, sizeof(SPOOL_NOTIFY_OPTION_TYPE) * 2);
+
+ ZERO_STRUCT(option.ctr.type[0]);
+ option.ctr.type[0].type = PRINTER_NOTIFY_TYPE;
+ option.ctr.type[0].count = option.ctr.type[0].count2 = 1;
+ option.ctr.type[0].fields_ptr = 1;
+ option.ctr.type[0].fields[0] = PRINTER_NOTIFY_SERVER_NAME;
+
+ ZERO_STRUCT(option.ctr.type[1]);
+ option.ctr.type[1].type = JOB_NOTIFY_TYPE;
+ option.ctr.type[1].count = option.ctr.type[1].count2 = 1;
+ option.ctr.type[1].fields_ptr = 1;
+ option.ctr.type[1].fields[0] = JOB_NOTIFY_PRINTER_NAME;
+
+ /* Send rffpcnex */
+
+ slprintf(servername, sizeof(servername) - 1, "\\\\%s", myhostname());
+ strupper_m(servername);
+
+ result = cli_spoolss_rffpcnex(
+ cli, mem_ctx, &hnd, 0, 0, servername, 123, &option);
+
+ if (!W_ERROR_IS_OK(result)) {
+ printf("Error rffpcnex %s\n", argv[1]);
+ goto done;
+ }
+
+done:
+ if (got_hnd)
+ cli_spoolss_close_printer(cli, mem_ctx, &hnd);
+
+ return result;
+}
+
+/* List of commands exported by this module */
+struct cmd_set spoolss_commands[] = {
+
+ { "SPOOLSS" },
+
+ { "adddriver", RPC_RTYPE_WERROR, NULL, cmd_spoolss_addprinterdriver, PI_SPOOLSS, "Add a print driver", "" },
+ { "addprinter", RPC_RTYPE_WERROR, NULL, cmd_spoolss_addprinterex, PI_SPOOLSS, "Add a printer", "" },
+ { "deldriver", RPC_RTYPE_WERROR, NULL, cmd_spoolss_deletedriver, PI_SPOOLSS, "Delete a printer driver", "" },
+ { "enumdata", RPC_RTYPE_WERROR, NULL, cmd_spoolss_enum_data, PI_SPOOLSS, "Enumerate printer data", "" },
+ { "enumdataex", RPC_RTYPE_WERROR, NULL, cmd_spoolss_enum_data_ex, PI_SPOOLSS, "Enumerate printer data for a key", "" },
+ { "enumkey", RPC_RTYPE_WERROR, NULL, cmd_spoolss_enum_printerkey, PI_SPOOLSS, "Enumerate printer keys", "" },
+ { "enumjobs", RPC_RTYPE_WERROR, NULL, cmd_spoolss_enum_jobs, PI_SPOOLSS, "Enumerate print jobs", "" },
+ { "enumports", RPC_RTYPE_WERROR, NULL, cmd_spoolss_enum_ports, PI_SPOOLSS, "Enumerate printer ports", "" },
+ { "enumdrivers", RPC_RTYPE_WERROR, NULL, cmd_spoolss_enum_drivers, PI_SPOOLSS, "Enumerate installed printer drivers", "" },
+ { "enumprinters", RPC_RTYPE_WERROR, NULL, cmd_spoolss_enum_printers, PI_SPOOLSS, "Enumerate printers", "" },
+ { "getdata", RPC_RTYPE_WERROR, NULL, cmd_spoolss_getprinterdata, PI_SPOOLSS, "Get print driver data", "" },
+ { "getdataex", RPC_RTYPE_WERROR, NULL, cmd_spoolss_getprinterdataex, PI_SPOOLSS, "Get printer driver data with keyname", ""},
+ { "getdriver", RPC_RTYPE_WERROR, NULL, cmd_spoolss_getdriver, PI_SPOOLSS, "Get print driver information", "" },
+ { "getdriverdir", RPC_RTYPE_WERROR, NULL, cmd_spoolss_getdriverdir, PI_SPOOLSS, "Get print driver upload directory", "" },
+ { "getprinter", RPC_RTYPE_WERROR, NULL, cmd_spoolss_getprinter, PI_SPOOLSS, "Get printer info", "" },
+ { "getprintprocdir",RPC_RTYPE_WERROR, NULL, cmd_spoolss_getprintprocdir, PI_SPOOLSS, "Get print processor directory", "" },
+ { "openprinter", RPC_RTYPE_WERROR, NULL, cmd_spoolss_open_printer_ex, PI_SPOOLSS, "Open printer handle", "" },
+ { "setdriver", RPC_RTYPE_WERROR, NULL, cmd_spoolss_setdriver, PI_SPOOLSS, "Set printer driver", "" },
+ { "getprintprocdir", RPC_RTYPE_WERROR, NULL, cmd_spoolss_getprintprocdir, PI_SPOOLSS, "Get print processor directory", "" },
+ { "addform", RPC_RTYPE_WERROR, NULL, cmd_spoolss_addform, PI_SPOOLSS, "Add form", "" },
+ { "setform", RPC_RTYPE_WERROR, NULL, cmd_spoolss_setform, PI_SPOOLSS, "Set form", "" },
+ { "getform", RPC_RTYPE_WERROR, NULL, cmd_spoolss_getform, PI_SPOOLSS, "Get form", "" },
+ { "deleteform", RPC_RTYPE_WERROR, NULL, cmd_spoolss_deleteform, PI_SPOOLSS, "Delete form", "" },
+ { "enumforms", RPC_RTYPE_WERROR, NULL, cmd_spoolss_enum_forms, PI_SPOOLSS, "Enumerate forms", "" },
+ { "setprinter", RPC_RTYPE_WERROR, NULL, cmd_spoolss_setprinter, PI_SPOOLSS, "Set printer comment", "" },
+ { "setprinterdata", RPC_RTYPE_WERROR, NULL, cmd_spoolss_setprinterdata, PI_SPOOLSS, "Set REG_SZ printer data", "" },
+ { "rffpcnex", RPC_RTYPE_WERROR, NULL, cmd_spoolss_rffpcnex, PI_SPOOLSS, "Rffpcnex test", "" },
+
+ { NULL }
+};
diff --git a/source/rpcclient/cmd_srvsvc.c b/source/rpcclient/cmd_srvsvc.c
new file mode 100644
index 00000000000..3e569f51cea
--- /dev/null
+++ b/source/rpcclient/cmd_srvsvc.c
@@ -0,0 +1,361 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ Copyright (C) Andrew Tridgell 1992-1999
+ Copyright (C) Luke Kenneth Casson Leighton 1996 - 1999
+ Copyright (C) Tim Potter 2000,2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "rpcclient.h"
+
+/* Display server query info */
+
+static char *get_server_type_str(uint32 type)
+{
+ static fstring typestr;
+ int i;
+
+ if (type == SV_TYPE_ALL) {
+ fstrcpy(typestr, "All");
+ return typestr;
+ }
+
+ typestr[0] = 0;
+
+ for (i = 0; i < 32; i++) {
+ if (type & (1 << i)) {
+ switch (1 << i) {
+ case SV_TYPE_WORKSTATION:
+ fstrcat(typestr, "Wk ");
+ break;
+ case SV_TYPE_SERVER:
+ fstrcat(typestr, "Sv ");
+ break;
+ case SV_TYPE_SQLSERVER:
+ fstrcat(typestr, "Sql ");
+ break;
+ case SV_TYPE_DOMAIN_CTRL:
+ fstrcat(typestr, "PDC ");
+ break;
+ case SV_TYPE_DOMAIN_BAKCTRL:
+ fstrcat(typestr, "BDC ");
+ break;
+ case SV_TYPE_TIME_SOURCE:
+ fstrcat(typestr, "Tim ");
+ break;
+ case SV_TYPE_AFP:
+ fstrcat(typestr, "AFP ");
+ break;
+ case SV_TYPE_NOVELL:
+ fstrcat(typestr, "Nov ");
+ break;
+ case SV_TYPE_DOMAIN_MEMBER:
+ fstrcat(typestr, "Dom ");
+ break;
+ case SV_TYPE_PRINTQ_SERVER:
+ fstrcat(typestr, "PrQ ");
+ break;
+ case SV_TYPE_DIALIN_SERVER:
+ fstrcat(typestr, "Din ");
+ break;
+ case SV_TYPE_SERVER_UNIX:
+ fstrcat(typestr, "Unx ");
+ break;
+ case SV_TYPE_NT:
+ fstrcat(typestr, "NT ");
+ break;
+ case SV_TYPE_WFW:
+ fstrcat(typestr, "Wfw ");
+ break;
+ case SV_TYPE_SERVER_MFPN:
+ fstrcat(typestr, "Mfp ");
+ break;
+ case SV_TYPE_SERVER_NT:
+ fstrcat(typestr, "SNT ");
+ break;
+ case SV_TYPE_POTENTIAL_BROWSER:
+ fstrcat(typestr, "PtB ");
+ break;
+ case SV_TYPE_BACKUP_BROWSER:
+ fstrcat(typestr, "BMB ");
+ break;
+ case SV_TYPE_MASTER_BROWSER:
+ fstrcat(typestr, "LMB ");
+ break;
+ case SV_TYPE_DOMAIN_MASTER:
+ fstrcat(typestr, "DMB ");
+ break;
+ case SV_TYPE_SERVER_OSF:
+ fstrcat(typestr, "OSF ");
+ break;
+ case SV_TYPE_SERVER_VMS:
+ fstrcat(typestr, "VMS ");
+ break;
+ case SV_TYPE_WIN95_PLUS:
+ fstrcat(typestr, "W95 ");
+ break;
+ case SV_TYPE_ALTERNATE_XPORT:
+ fstrcat(typestr, "Xpt ");
+ break;
+ case SV_TYPE_LOCAL_LIST_ONLY:
+ fstrcat(typestr, "Dom ");
+ break;
+ case SV_TYPE_DOMAIN_ENUM:
+ fstrcat(typestr, "Loc ");
+ break;
+ }
+ }
+ }
+
+ i = strlen(typestr) - 1;
+
+ if (typestr[i] == ' ')
+ typestr[i] = 0;
+
+ return typestr;
+}
+
+static void display_server(char *sname, uint32 type, const char *comment)
+{
+ printf("\t%-15.15s%-20s %s\n", sname, get_server_type_str(type),
+ comment);
+}
+
+static void display_srv_info_101(SRV_INFO_101 *sv101)
+{
+ fstring name;
+ fstring comment;
+
+ unistr2_to_ascii(name, &sv101->uni_name, sizeof(name) - 1);
+ unistr2_to_ascii(comment, &sv101->uni_comment, sizeof(comment) - 1);
+
+ display_server(name, sv101->srv_type, comment);
+
+ printf("\tplatform_id :\t%d\n", sv101->platform_id);
+ printf("\tos version :\t%d.%d\n", sv101->ver_major,
+ sv101->ver_minor);
+
+ printf("\tserver type :\t0x%x\n", sv101->srv_type);
+}
+
+static void display_srv_info_102(SRV_INFO_102 *sv102)
+{
+ fstring name;
+ fstring comment;
+ fstring usr_path;
+
+ unistr2_to_ascii(name, &sv102->uni_name, sizeof(name) - 1);
+ unistr2_to_ascii(comment, &sv102->uni_comment, sizeof(comment) - 1);
+ unistr2_to_ascii(usr_path, &sv102->uni_usr_path, sizeof(usr_path) - 1);
+
+ display_server(name, sv102->srv_type, comment);
+
+ printf("\tplatform_id :\t%d\n", sv102->platform_id);
+ printf("\tos version :\t%d.%d\n", sv102->ver_major,
+ sv102->ver_minor);
+
+ printf("\tusers :\t%x\n", sv102->users);
+ printf("\tdisc, hidden :\t%x, %x\n", sv102->disc, sv102->hidden);
+ printf("\tannounce, delta :\t%d, %d\n", sv102->announce,
+ sv102->ann_delta);
+ printf("\tlicenses :\t%d\n", sv102->licenses);
+ printf("\tuser path :\t%s\n", usr_path);
+}
+
+/* Server query info */
+static WERROR cmd_srvsvc_srv_query_info(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ uint32 info_level = 101;
+ SRV_INFO_CTR ctr;
+ WERROR result;
+
+ if (argc > 2) {
+ printf("Usage: %s [infolevel]\n", argv[0]);
+ return WERR_OK;
+ }
+
+ if (argc == 2)
+ info_level = atoi(argv[1]);
+
+ result = cli_srvsvc_net_srv_get_info(cli, mem_ctx, info_level,
+ &ctr);
+
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Display results */
+
+ switch (info_level) {
+ case 101:
+ display_srv_info_101(&ctr.srv.sv101);
+ break;
+ case 102:
+ display_srv_info_102(&ctr.srv.sv102);
+ break;
+ default:
+ printf("unsupported info level %d\n", info_level);
+ break;
+ }
+
+ done:
+ return result;
+}
+
+static void display_share_info_1(SRV_SHARE_INFO_1 *info1)
+{
+ fstring netname = "", remark = "";
+
+ rpcstr_pull_unistr2_fstring(netname, &info1->info_1_str.uni_netname);
+ rpcstr_pull_unistr2_fstring(remark, &info1->info_1_str.uni_remark);
+
+ printf("netname: %s\n", netname);
+ printf("\tremark:\t%s\n", remark);
+}
+
+static void display_share_info_2(SRV_SHARE_INFO_2 *info2)
+{
+ fstring netname = "", remark = "", path = "", passwd = "";
+
+ rpcstr_pull_unistr2_fstring(netname, &info2->info_2_str.uni_netname);
+ rpcstr_pull_unistr2_fstring(remark, &info2->info_2_str.uni_remark);
+ rpcstr_pull_unistr2_fstring(path, &info2->info_2_str.uni_path);
+ rpcstr_pull_unistr2_fstring(passwd, &info2->info_2_str.uni_passwd);
+
+ printf("netname: %s\n", netname);
+ printf("\tremark:\t%s\n", remark);
+ printf("\tpath:\t%s\n", path);
+ printf("\tpassword:\t%s\n", passwd);
+}
+
+static WERROR cmd_srvsvc_net_share_enum(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ uint32 info_level = 2;
+ SRV_SHARE_INFO_CTR ctr;
+ WERROR result;
+ ENUM_HND hnd;
+ uint32 preferred_len = 0xffffffff, i;
+
+ if (argc > 2) {
+ printf("Usage: %s [infolevel]\n", argv[0]);
+ return WERR_OK;
+ }
+
+ if (argc == 2)
+ info_level = atoi(argv[1]);
+
+ init_enum_hnd(&hnd, 0);
+
+ result = cli_srvsvc_net_share_enum(
+ cli, mem_ctx, info_level, &ctr, preferred_len, &hnd);
+
+ if (!W_ERROR_IS_OK(result) || !ctr.num_entries)
+ goto done;
+
+ /* Display results */
+
+ switch (info_level) {
+ case 1:
+ for (i = 0; i < ctr.num_entries; i++)
+ display_share_info_1(&ctr.share.info1[i]);
+ break;
+ case 2:
+ for (i = 0; i < ctr.num_entries; i++)
+ display_share_info_2(&ctr.share.info2[i]);
+ break;
+ default:
+ printf("unsupported info level %d\n", info_level);
+ break;
+ }
+
+ done:
+ return result;
+}
+
+static WERROR cmd_srvsvc_net_remote_tod(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ TIME_OF_DAY_INFO tod;
+ WERROR result;
+
+ if (argc > 1) {
+ printf("Usage: %s\n", argv[0]);
+ return WERR_OK;
+ }
+
+ result = cli_srvsvc_net_remote_tod(
+ cli, mem_ctx, cli->srv_name_slash, &tod);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ done:
+ return result;
+}
+
+static WERROR cmd_srvsvc_net_file_enum(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ uint32 info_level = 3;
+ SRV_FILE_INFO_CTR ctr;
+ WERROR result;
+ ENUM_HND hnd;
+ uint32 preferred_len = 0;
+
+ if (argc > 2) {
+ printf("Usage: %s [infolevel]\n", argv[0]);
+ return WERR_OK;
+ }
+
+ if (argc == 2)
+ info_level = atoi(argv[1]);
+
+ init_enum_hnd(&hnd, 0);
+
+ ZERO_STRUCT(ctr);
+
+ result = cli_srvsvc_net_file_enum(
+ cli, mem_ctx, info_level, NULL, &ctr, preferred_len, &hnd);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ done:
+ return result;
+}
+
+/* List of commands exported by this module */
+
+struct cmd_set srvsvc_commands[] = {
+
+ { "SRVSVC" },
+
+ { "srvinfo", RPC_RTYPE_WERROR, NULL, cmd_srvsvc_srv_query_info, PI_SRVSVC, "Server query info", "" },
+ { "netshareenum",RPC_RTYPE_WERROR, NULL, cmd_srvsvc_net_share_enum, PI_SRVSVC, "Enumerate shares", "" },
+ { "netfileenum", RPC_RTYPE_WERROR, NULL, cmd_srvsvc_net_file_enum, PI_SRVSVC, "Enumerate open files", "" },
+ { "netremotetod",RPC_RTYPE_WERROR, NULL, cmd_srvsvc_net_remote_tod, PI_SRVSVC, "Fetch remote time of day", "" },
+
+ { NULL }
+};
diff --git a/source/rpcclient/cmd_wkssvc.c b/source/rpcclient/cmd_wkssvc.c
new file mode 100644
index 00000000000..137ff3bdae9
--- /dev/null
+++ b/source/rpcclient/cmd_wkssvc.c
@@ -0,0 +1,84 @@
+/*
+ Unix SMB/CIFS implementation.
+ NT Domain Authentication SMB / MSRPC client
+ Copyright (C) Andrew Tridgell 1994-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#define DEBUG_TESTING
+
+extern struct cli_state *smb_cli;
+
+extern FILE* out_hnd;
+
+
+/****************************************************************************
+workstation get info query
+****************************************************************************/
+void cmd_wks_query_info(struct client_info *info)
+{
+ fstring dest_wks;
+ fstring tmp;
+ WKS_INFO_100 ctr;
+ uint32 info_level = 100;
+
+ BOOL res = True;
+
+ memset((char *)&ctr, '\0', sizeof(ctr));
+
+ fstrcpy(dest_wks, "\\\\");
+ fstrcat(dest_wks, info->dest_host);
+ strupper_m(dest_wks);
+
+ if (next_token_nr(NULL, tmp, NULL, sizeof(tmp)))
+ {
+ info_level = (uint32)strtol(tmp, (char**)NULL, 10);
+ }
+
+ DEBUG(4,("cmd_wks_query_info: server:%s info level: %d\n",
+ dest_wks, info_level));
+
+ DEBUG(5, ("cmd_wks_query_info: smb_cli->fd:%d\n", smb_cli->fd));
+
+ /* open LSARPC session. */
+ res = res ? cli_nt_session_open(smb_cli, PI_WKSSVC) : False;
+
+ /* send info level: receive requested info. hopefully. */
+ res = res ? do_wks_query_info(smb_cli,
+ dest_wks, info_level, &ctr) : False;
+
+ /* close the session */
+ cli_nt_session_close(smb_cli);
+
+ if (res)
+ {
+ DEBUG(5,("cmd_wks_query_info: query succeeded\n"));
+
+#if 0
+ display_wks_info_100(out_hnd, ACTION_HEADER , &ctr);
+ display_wks_info_100(out_hnd, ACTION_ENUMERATE, &ctr);
+ display_wks_info_100(out_hnd, ACTION_FOOTER , &ctr);
+#endif
+
+ }
+ else
+ {
+ DEBUG(5,("cmd_wks_query_info: query failed\n"));
+ }
+}
diff --git a/source/rpcclient/display_sec.c b/source/rpcclient/display_sec.c
new file mode 100644
index 00000000000..2a93c915f1a
--- /dev/null
+++ b/source/rpcclient/display_sec.c
@@ -0,0 +1,144 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Andrew Tridgell 1992-1999
+ Copyright (C) Luke Kenneth Casson Leighton 1996 - 1999
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "rpcclient.h"
+
+/****************************************************************************
+convert a security permissions into a string
+****************************************************************************/
+char *get_sec_mask_str(uint32 type)
+{
+ static fstring typestr="";
+
+ typestr[0] = 0;
+
+ if (type & GENERIC_ALL_ACCESS)
+ fstrcat(typestr, "Generic all access ");
+ if (type & GENERIC_EXECUTE_ACCESS)
+ fstrcat(typestr, "Generic execute access ");
+ if (type & GENERIC_WRITE_ACCESS)
+ fstrcat(typestr, "Generic write access ");
+ if (type & GENERIC_READ_ACCESS)
+ fstrcat(typestr, "Generic read access ");
+ if (type & MAXIMUM_ALLOWED_ACCESS)
+ fstrcat(typestr, "MAXIMUM_ALLOWED_ACCESS ");
+ if (type & SYSTEM_SECURITY_ACCESS)
+ fstrcat(typestr, "SYSTEM_SECURITY_ACCESS ");
+ if (type & SYNCHRONIZE_ACCESS)
+ fstrcat(typestr, "SYNCHRONIZE_ACCESS ");
+ if (type & WRITE_OWNER_ACCESS)
+ fstrcat(typestr, "WRITE_OWNER_ACCESS ");
+ if (type & WRITE_DAC_ACCESS)
+ fstrcat(typestr, "WRITE_DAC_ACCESS ");
+ if (type & READ_CONTROL_ACCESS)
+ fstrcat(typestr, "READ_CONTROL_ACCESS ");
+ if (type & DELETE_ACCESS)
+ fstrcat(typestr, "DELETE_ACCESS ");
+
+ printf("\t\tSpecific bits: 0x%lx\n", (unsigned long)type&SPECIFIC_RIGHTS_MASK);
+
+ return typestr;
+}
+
+/****************************************************************************
+ display sec_access structure
+ ****************************************************************************/
+void display_sec_access(SEC_ACCESS *info)
+{
+ printf("\t\tPermissions: 0x%x: %s\n", info->mask, get_sec_mask_str(info->mask));
+}
+
+/****************************************************************************
+ display sec_ace structure
+ ****************************************************************************/
+void display_sec_ace(SEC_ACE *ace)
+{
+ fstring sid_str;
+
+ printf("\tACE\n\t\ttype: ");
+ switch (ace->type) {
+ case SEC_ACE_TYPE_ACCESS_ALLOWED:
+ printf("ACCESS ALLOWED");
+ break;
+ case SEC_ACE_TYPE_ACCESS_DENIED:
+ printf("ACCESS DENIED");
+ break;
+ case SEC_ACE_TYPE_SYSTEM_AUDIT:
+ printf("SYSTEM AUDIT");
+ break;
+ case SEC_ACE_TYPE_SYSTEM_ALARM:
+ printf("SYSTEM ALARM");
+ break;
+ default:
+ printf("????");
+ break;
+ }
+ printf(" (%d) flags: %d\n", ace->type, ace->flags);
+ display_sec_access(&ace->info);
+ sid_to_string(sid_str, &ace->trustee);
+ printf("\t\tSID: %s\n\n", sid_str);
+}
+
+/****************************************************************************
+ display sec_acl structure
+ ****************************************************************************/
+void display_sec_acl(SEC_ACL *sec_acl)
+{
+ int i;
+
+ printf("\tACL\tNum ACEs:\t%d\trevision:\t%x\n",
+ sec_acl->num_aces, sec_acl->revision);
+ printf("\t---\n");
+
+ if (sec_acl->size != 0 && sec_acl->num_aces != 0)
+ for (i = 0; i < sec_acl->num_aces; i++)
+ display_sec_ace(&sec_acl->ace[i]);
+
+}
+
+/****************************************************************************
+ display sec_desc structure
+ ****************************************************************************/
+void display_sec_desc(SEC_DESC *sec)
+{
+ fstring sid_str;
+
+ if (sec->sacl) {
+ printf("SACL\n");
+ display_sec_acl(sec->sacl);
+ }
+
+ if (sec->dacl) {
+ printf("DACL\n");
+ display_sec_acl(sec->dacl);
+ }
+
+ if (sec->owner_sid) {
+ sid_to_string(sid_str, sec->owner_sid);
+ printf("\tOwner SID:\t%s\n", sid_str);
+ }
+
+ if (sec->grp_sid) {
+ sid_to_string(sid_str, sec->grp_sid);
+ printf("\tParent SID:\t%s\n", sid_str);
+ }
+}
diff --git a/source/rpcclient/rpcclient.c b/source/rpcclient/rpcclient.c
new file mode 100644
index 00000000000..8372b75b4bd
--- /dev/null
+++ b/source/rpcclient/rpcclient.c
@@ -0,0 +1,803 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ Copyright (C) Tim Potter 2000-2001
+ Copyright (C) Martin Pool 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "rpcclient.h"
+
+DOM_SID domain_sid;
+
+
+/* List to hold groups of commands.
+ *
+ * Commands are defined in a list of arrays: arrays are easy to
+ * statically declare, and lists are easier to dynamically extend.
+ */
+
+static struct cmd_list {
+ struct cmd_list *prev, *next;
+ struct cmd_set *cmd_set;
+} *cmd_list;
+
+/****************************************************************************
+handle completion of commands for readline
+****************************************************************************/
+static char **completion_fn(const char *text, int start, int end)
+{
+#define MAX_COMPLETIONS 100
+ char **matches;
+ int i, count=0;
+ struct cmd_list *commands = cmd_list;
+
+#if 0 /* JERRY */
+ /* FIXME!!! -- what to do when completing argument? */
+ /* for words not at the start of the line fallback
+ to filename completion */
+ if (start)
+ return NULL;
+#endif
+
+ /* make sure we have a list of valid commands */
+ if (!commands)
+ return NULL;
+
+ matches = (char **)malloc(sizeof(matches[0])*MAX_COMPLETIONS);
+ if (!matches) return NULL;
+
+ matches[count++] = strdup(text);
+ if (!matches[0]) return NULL;
+
+ while (commands && count < MAX_COMPLETIONS-1)
+ {
+ if (!commands->cmd_set)
+ break;
+
+ for (i=0; commands->cmd_set[i].name; i++)
+ {
+ if ((strncmp(text, commands->cmd_set[i].name, strlen(text)) == 0) &&
+ (( commands->cmd_set[i].returntype == RPC_RTYPE_NTSTATUS &&
+ commands->cmd_set[i].ntfn ) ||
+ ( commands->cmd_set[i].returntype == RPC_RTYPE_WERROR &&
+ commands->cmd_set[i].wfn)))
+ {
+ matches[count] = strdup(commands->cmd_set[i].name);
+ if (!matches[count])
+ return NULL;
+ count++;
+ }
+ }
+
+ commands = commands->next;
+
+ }
+
+ if (count == 2) {
+ SAFE_FREE(matches[0]);
+ matches[0] = strdup(matches[1]);
+ }
+ matches[count] = NULL;
+ return matches;
+}
+
+static char* next_command (char** cmdstr)
+{
+ static pstring command;
+ char *p;
+
+ if (!cmdstr || !(*cmdstr))
+ return NULL;
+
+ p = strchr_m(*cmdstr, ';');
+ if (p)
+ *p = '\0';
+ pstrcpy(command, *cmdstr);
+ if (p)
+ *cmdstr = p + 1;
+ else
+ *cmdstr = NULL;
+
+ return command;
+}
+
+/* Fetch the SID for this computer */
+
+static void fetch_machine_sid(struct cli_state *cli)
+{
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_OK;
+ uint32 info_class = 5;
+ char *domain_name = NULL;
+ static BOOL got_domain_sid;
+ TALLOC_CTX *mem_ctx;
+ DOM_SID *dom_sid = NULL;
+
+ if (got_domain_sid) return;
+
+ if (!(mem_ctx=talloc_init("fetch_machine_sid")))
+ {
+ DEBUG(0,("fetch_machine_sid: talloc_init returned NULL!\n"));
+ goto error;
+ }
+
+
+ if (!cli_nt_session_open (cli, PI_LSARPC)) {
+ fprintf(stderr, "could not initialise lsa pipe\n");
+ goto error;
+ }
+
+ result = cli_lsa_open_policy(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto error;
+ }
+
+ result = cli_lsa_query_info_policy(cli, mem_ctx, &pol, info_class,
+ &domain_name, &dom_sid);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto error;
+ }
+
+ got_domain_sid = True;
+ sid_copy( &domain_sid, dom_sid );
+
+ cli_lsa_close(cli, mem_ctx, &pol);
+ cli_nt_session_close(cli);
+ talloc_destroy(mem_ctx);
+
+ return;
+
+ error:
+ fprintf(stderr, "could not obtain sid for domain %s\n", cli->domain);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ fprintf(stderr, "error: %s\n", nt_errstr(result));
+ }
+
+ exit(1);
+}
+
+/* List the available commands on a given pipe */
+
+static NTSTATUS cmd_listcommands(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ struct cmd_list *tmp;
+ struct cmd_set *tmp_set;
+ int i;
+
+ /* Usage */
+
+ if (argc != 2) {
+ printf("Usage: %s <pipe>\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ /* Help on one command */
+
+ for (tmp = cmd_list; tmp; tmp = tmp->next)
+ {
+ tmp_set = tmp->cmd_set;
+
+ if (!StrCaseCmp(argv[1], tmp_set->name))
+ {
+ printf("Available commands on the %s pipe:\n\n", tmp_set->name);
+
+ i = 0;
+ tmp_set++;
+ while(tmp_set->name) {
+ printf("%20s", tmp_set->name);
+ tmp_set++;
+ i++;
+ if (i%4 == 0)
+ printf("\n");
+ }
+
+ /* drop out of the loop */
+ break;
+ }
+ }
+ printf("\n\n");
+
+ return NT_STATUS_OK;
+}
+
+/* Display help on commands */
+
+static NTSTATUS cmd_help(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ struct cmd_list *tmp;
+ struct cmd_set *tmp_set;
+
+ /* Usage */
+
+ if (argc > 2) {
+ printf("Usage: %s [command]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ /* Help on one command */
+
+ if (argc == 2) {
+ for (tmp = cmd_list; tmp; tmp = tmp->next) {
+
+ tmp_set = tmp->cmd_set;
+
+ while(tmp_set->name) {
+ if (strequal(argv[1], tmp_set->name)) {
+ if (tmp_set->usage &&
+ tmp_set->usage[0])
+ printf("%s\n", tmp_set->usage);
+ else
+ printf("No help for %s\n", tmp_set->name);
+
+ return NT_STATUS_OK;
+ }
+
+ tmp_set++;
+ }
+ }
+
+ printf("No such command: %s\n", argv[1]);
+ return NT_STATUS_OK;
+ }
+
+ /* List all commands */
+
+ for (tmp = cmd_list; tmp; tmp = tmp->next) {
+
+ tmp_set = tmp->cmd_set;
+
+ while(tmp_set->name) {
+
+ printf("%15s\t\t%s\n", tmp_set->name,
+ tmp_set->description ? tmp_set->description:
+ "");
+
+ tmp_set++;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* Change the debug level */
+
+static NTSTATUS cmd_debuglevel(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ if (argc > 2) {
+ printf("Usage: %s [debuglevel]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc == 2) {
+ DEBUGLEVEL = atoi(argv[1]);
+ }
+
+ printf("debuglevel is %d\n", DEBUGLEVEL);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_quit(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ exit(0);
+ return NT_STATUS_OK; /* NOTREACHED */
+}
+
+static NTSTATUS cmd_sign(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ if (cli->pipe_auth_flags == (AUTH_PIPE_NTLMSSP|AUTH_PIPE_SIGN)) {
+ return NT_STATUS_OK;
+ } else {
+ /* still have session, just need to use it again */
+ cli->pipe_auth_flags = AUTH_PIPE_NTLMSSP;
+ cli->pipe_auth_flags |= AUTH_PIPE_SIGN;
+ if (cli->nt_pipe_fnum != 0)
+ cli_nt_session_close(cli);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_seal(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ if (cli->pipe_auth_flags == (AUTH_PIPE_NTLMSSP|AUTH_PIPE_SIGN|AUTH_PIPE_SEAL)) {
+ return NT_STATUS_OK;
+ } else {
+ /* still have session, just need to use it again */
+ cli->pipe_auth_flags = AUTH_PIPE_NTLMSSP;
+ cli->pipe_auth_flags |= AUTH_PIPE_SIGN;
+ cli->pipe_auth_flags |= AUTH_PIPE_SEAL;
+ if (cli->nt_pipe_fnum != 0)
+ cli_nt_session_close(cli);
+ }
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_none(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ if (cli->pipe_auth_flags == 0) {
+ return NT_STATUS_OK;
+ } else {
+ /* still have session, just need to use it again */
+ cli->pipe_auth_flags = 0;
+ if (cli->nt_pipe_fnum != 0)
+ cli_nt_session_close(cli);
+ }
+ cli->pipe_auth_flags = 0;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS setup_schannel(struct cli_state *cli, int pipe_auth_flags,
+ int argc, const char **argv)
+{
+ NTSTATUS ret;
+ static uchar zeros[16];
+ uchar trust_password[16];
+ uint32 sec_channel_type;
+ if (argc == 2) {
+ strhex_to_str((char *)cli->auth_info.sess_key,
+ strlen(argv[1]),
+ argv[1]);
+ memcpy(cli->sess_key, cli->auth_info.sess_key, sizeof(cli->sess_key));
+
+ cli->pipe_auth_flags = pipe_auth_flags;
+ return NT_STATUS_OK;
+ }
+
+ /* Cleanup */
+
+ if ((memcmp(cli->auth_info.sess_key, zeros, sizeof(cli->auth_info.sess_key)) != 0)) {
+ if (cli->pipe_auth_flags == pipe_auth_flags) {
+ /* already in this mode nothing to do */
+ return NT_STATUS_OK;
+ } else {
+ /* schannel is setup, just need to use it again with new flags */
+ cli->pipe_auth_flags = pipe_auth_flags;
+
+ if (cli->nt_pipe_fnum != 0)
+ cli_nt_session_close(cli);
+ return NT_STATUS_OK;
+ }
+ }
+
+ if (cli->nt_pipe_fnum != 0)
+ cli_nt_session_close(cli);
+
+ if (!secrets_fetch_trust_account_password(lp_workgroup(),
+ trust_password,
+ NULL, &sec_channel_type)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ret = cli_nt_setup_netsec(cli, sec_channel_type, pipe_auth_flags, trust_password);
+ if (NT_STATUS_IS_OK(ret)) {
+ char *hex_session_key;
+ hex_encode(cli->auth_info.sess_key,
+ sizeof(cli->auth_info.sess_key),
+ &hex_session_key);
+ printf("Got Session key: %s\n", hex_session_key);
+ SAFE_FREE(hex_session_key);
+ }
+ return ret;
+}
+
+
+static NTSTATUS cmd_schannel(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ d_printf("Setting schannel - sign and seal\n");
+ return setup_schannel(cli, AUTH_PIPE_NETSEC | AUTH_PIPE_SIGN | AUTH_PIPE_SEAL,
+ argc, argv);
+}
+
+static NTSTATUS cmd_schannel_sign(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ d_printf("Setting schannel - sign only\n");
+ return setup_schannel(cli, AUTH_PIPE_NETSEC | AUTH_PIPE_SIGN,
+ argc, argv);
+}
+
+
+/* Built in rpcclient commands */
+
+static struct cmd_set rpcclient_commands[] = {
+
+ { "GENERAL OPTIONS" },
+
+ { "help", RPC_RTYPE_NTSTATUS, cmd_help, NULL, -1, "Get help on commands", "[command]" },
+ { "?", RPC_RTYPE_NTSTATUS, cmd_help, NULL, -1, "Get help on commands", "[command]" },
+ { "debuglevel", RPC_RTYPE_NTSTATUS, cmd_debuglevel, NULL, -1, "Set debug level", "level" },
+ { "list", RPC_RTYPE_NTSTATUS, cmd_listcommands, NULL, -1, "List available commands on <pipe>", "pipe" },
+ { "exit", RPC_RTYPE_NTSTATUS, cmd_quit, NULL, -1, "Exit program", "" },
+ { "quit", RPC_RTYPE_NTSTATUS, cmd_quit, NULL, -1, "Exit program", "" },
+ { "sign", RPC_RTYPE_NTSTATUS, cmd_sign, NULL, -1, "Force RPC pipe connections to be signed", "" },
+ { "seal", RPC_RTYPE_NTSTATUS, cmd_seal, NULL, -1, "Force RPC pipe connections to be sealed", "" },
+ { "schannel", RPC_RTYPE_NTSTATUS, cmd_schannel, NULL, -1, "Force RPC pipe connections to be sealed with 'schannel' (NETSEC). Assumes valid machine account to this domain controller.", "" },
+ { "schannelsign", RPC_RTYPE_NTSTATUS, cmd_schannel_sign, NULL, -1, "Force RPC pipe connections to be signed (not sealed) with 'schannel' (NETSEC). Assumes valid machine account to this domain controller.", "" },
+ { "none", RPC_RTYPE_NTSTATUS, cmd_none, NULL, -1, "Force RPC pipe connections to have no special properties", "" },
+
+ { NULL }
+};
+
+static struct cmd_set separator_command[] = {
+ { "---------------", MAX_RPC_RETURN_TYPE, NULL, NULL, -1, "----------------------" },
+ { NULL }
+};
+
+
+/* Various pipe commands */
+
+extern struct cmd_set lsarpc_commands[];
+extern struct cmd_set samr_commands[];
+extern struct cmd_set spoolss_commands[];
+extern struct cmd_set netlogon_commands[];
+extern struct cmd_set srvsvc_commands[];
+extern struct cmd_set dfs_commands[];
+extern struct cmd_set reg_commands[];
+extern struct cmd_set ds_commands[];
+extern struct cmd_set echo_commands[];
+extern struct cmd_set shutdown_commands[];
+extern struct cmd_set epm_commands[];
+
+static struct cmd_set *rpcclient_command_list[] = {
+ rpcclient_commands,
+ lsarpc_commands,
+ ds_commands,
+ samr_commands,
+ spoolss_commands,
+ netlogon_commands,
+ srvsvc_commands,
+ dfs_commands,
+ reg_commands,
+ echo_commands,
+ shutdown_commands,
+ epm_commands,
+ NULL
+};
+
+static void add_command_set(struct cmd_set *cmd_set)
+{
+ struct cmd_list *entry;
+
+ if (!(entry = (struct cmd_list *)malloc(sizeof(struct cmd_list)))) {
+ DEBUG(0, ("out of memory\n"));
+ return;
+ }
+
+ ZERO_STRUCTP(entry);
+
+ entry->cmd_set = cmd_set;
+ DLIST_ADD(cmd_list, entry);
+}
+
+
+/**
+ * Call an rpcclient function, passing an argv array.
+ *
+ * @param cmd Command to run, as a single string.
+ **/
+static NTSTATUS do_cmd(struct cli_state *cli,
+ struct cmd_set *cmd_entry,
+ int argc, char **argv)
+{
+ NTSTATUS ntresult;
+ WERROR wresult;
+ uchar trust_password[16];
+
+ TALLOC_CTX *mem_ctx;
+
+ /* Create mem_ctx */
+
+ if (!(mem_ctx = talloc_init("do_cmd"))) {
+ DEBUG(0, ("talloc_init() failed\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Open pipe */
+
+ if (cmd_entry->pipe_idx != -1
+ && cmd_entry->pipe_idx != cli->pipe_idx) {
+ if (cli->nt_pipe_fnum != 0)
+ cli_nt_session_close(cli);
+
+ if (!cli_nt_session_open(cli, cmd_entry->pipe_idx)) {
+ DEBUG(0, ("Could not initialise %s\n",
+ get_pipe_name_from_index(cmd_entry->pipe_idx)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ /* some of the DsXXX commands use the netlogon pipe */
+
+ if (lp_client_schannel() && (cmd_entry->pipe_idx == PI_NETLOGON) && !(cli->pipe_auth_flags & AUTH_PIPE_NETSEC)) {
+ uint32 neg_flags = NETLOGON_NEG_AUTH2_FLAGS;
+ uint32 sec_channel_type;
+
+ if (!secrets_fetch_trust_account_password(lp_workgroup(),
+ trust_password,
+ NULL, &sec_channel_type)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ntresult = cli_nt_setup_creds(cli, sec_channel_type,
+ trust_password,
+ &neg_flags, 2);
+ if (!NT_STATUS_IS_OK(ntresult)) {
+ ZERO_STRUCT(cli->auth_info.sess_key);
+ printf("nt_setup_creds failed with %s\n", nt_errstr(ntresult));
+ return ntresult;
+ }
+
+ }
+
+ /* Run command */
+
+ if ( cmd_entry->returntype == RPC_RTYPE_NTSTATUS ) {
+ ntresult = cmd_entry->ntfn(cli, mem_ctx, argc, (const char **) argv);
+ if (!NT_STATUS_IS_OK(ntresult)) {
+ printf("result was %s\n", nt_errstr(ntresult));
+ }
+ } else {
+ wresult = cmd_entry->wfn( cli, mem_ctx, argc, (const char **) argv);
+ /* print out the DOS error */
+ if (!W_ERROR_IS_OK(wresult)) {
+ printf( "result was %s\n", dos_errstr(wresult));
+ }
+ ntresult = W_ERROR_IS_OK(wresult)?NT_STATUS_OK:NT_STATUS_UNSUCCESSFUL;
+ }
+
+
+ /* Cleanup */
+
+ talloc_destroy(mem_ctx);
+
+ return ntresult;
+}
+
+
+/**
+ * Process a command entered at the prompt or as part of -c
+ *
+ * @returns The NTSTATUS from running the command.
+ **/
+static NTSTATUS process_cmd(struct cli_state *cli, char *cmd)
+{
+ struct cmd_list *temp_list;
+ NTSTATUS result = NT_STATUS_OK;
+ int ret;
+ int argc;
+ char **argv = NULL;
+
+ if ((ret = poptParseArgvString(cmd, &argc, (const char ***) &argv)) != 0) {
+ fprintf(stderr, "rpcclient: %s\n", poptStrerror(ret));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+
+ /* Walk through a dlist of arrays of commands. */
+ for (temp_list = cmd_list; temp_list; temp_list = temp_list->next) {
+ struct cmd_set *temp_set = temp_list->cmd_set;
+
+ while (temp_set->name) {
+ if (strequal(argv[0], temp_set->name)) {
+ if (!(temp_set->returntype == RPC_RTYPE_NTSTATUS && temp_set->ntfn ) &&
+ !(temp_set->returntype == RPC_RTYPE_WERROR && temp_set->wfn )) {
+ fprintf (stderr, "Invalid command\n");
+ goto out_free;
+ }
+
+ result = do_cmd(cli, temp_set, argc, argv);
+
+ goto out_free;
+ }
+ temp_set++;
+ }
+ }
+
+ if (argv[0]) {
+ printf("command not found: %s\n", argv[0]);
+ }
+
+out_free:
+/* moved to do_cmd()
+ if (!NT_STATUS_IS_OK(result)) {
+ printf("result was %s\n", nt_errstr(result));
+ }
+*/
+
+ if (argv) {
+ /* NOTE: popt allocates the whole argv, including the
+ * strings, as a single block. So a single free is
+ * enough to release it -- we don't free the
+ * individual strings. rtfm. */
+ free(argv);
+ }
+
+ return result;
+}
+
+
+/* Main function */
+
+ int main(int argc, char *argv[])
+{
+ BOOL interactive = True;
+ int opt;
+ static char *cmdstr = NULL;
+ const char *server;
+ struct cli_state *cli;
+ static char *opt_ipaddr=NULL;
+ struct cmd_set **cmd_set;
+ struct in_addr server_ip;
+ NTSTATUS nt_status;
+
+ /* make sure the vars that get altered (4th field) are in
+ a fixed location or certain compilers complain */
+ poptContext pc;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"command", 'c', POPT_ARG_STRING, &cmdstr, 'c', "Execute semicolon separated cmds", "COMMANDS"},
+ {"dest-ip", 'I', POPT_ARG_STRING, &opt_ipaddr, 'I', "Specify destination IP address", "IP"},
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ POPT_COMMON_CREDENTIALS
+ POPT_TABLEEND
+ };
+
+ ZERO_STRUCT(server_ip);
+
+ setlinebuf(stdout);
+
+ /* the following functions are part of the Samba debugging
+ facilities. See lib/debug.c */
+ setup_logging("rpcclient", interactive);
+ if (!interactive)
+ reopen_logs();
+
+ /* Load smb.conf file */
+
+ if (!lp_load(dyn_CONFIGFILE,True,False,False))
+ fprintf(stderr, "Can't load %s\n", dyn_CONFIGFILE);
+
+ /* Parse options */
+
+ pc = poptGetContext("rpcclient", argc, (const char **) argv,
+ long_options, 0);
+
+ if (argc == 1) {
+ poptPrintHelp(pc, stderr, 0);
+ return 0;
+ }
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+
+ case 'I':
+ if ( (server_ip.s_addr=inet_addr(opt_ipaddr)) == INADDR_NONE ) {
+ fprintf(stderr, "%s not a valid IP address\n",
+ opt_ipaddr);
+ return 1;
+ }
+ }
+ }
+
+ /* Get server as remaining unparsed argument. Print usage if more
+ than one unparsed argument is present. */
+
+ server = poptGetArg(pc);
+
+ if (!server || poptGetArg(pc)) {
+ poptPrintHelp(pc, stderr, 0);
+ return 1;
+ }
+
+ poptFreeContext(pc);
+
+ load_interfaces();
+
+ if (!init_names())
+ return 1;
+
+ /*
+ * Get password
+ * from stdin if necessary
+ */
+
+ if (!cmdline_auth_info.got_pass) {
+ char *pass = getpass("Password:");
+ if (pass) {
+ pstrcpy(cmdline_auth_info.password, pass);
+ }
+ }
+
+ nt_status = cli_full_connection(&cli, global_myname(), server,
+ opt_ipaddr ? &server_ip : NULL, 0,
+ "IPC$", "IPC",
+ cmdline_auth_info.username,
+ lp_workgroup(),
+ cmdline_auth_info.password,
+ cmdline_auth_info.use_kerberos ? CLI_FULL_CONNECTION_USE_KERBEROS : 0,
+ cmdline_auth_info.signing_state,NULL);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0,("Cannot connect to server. Error was %s\n", nt_errstr(nt_status)));
+ return 1;
+ }
+
+ memset(cmdline_auth_info.password,'X',sizeof(cmdline_auth_info.password));
+
+ /* Load command lists */
+
+ cmd_set = rpcclient_command_list;
+
+ while(*cmd_set) {
+ add_command_set(*cmd_set);
+ add_command_set(separator_command);
+ cmd_set++;
+ }
+
+ fetch_machine_sid(cli);
+
+ /* Do anything specified with -c */
+ if (cmdstr && cmdstr[0]) {
+ char *cmd;
+ char *p = cmdstr;
+ int result = 0;
+
+ while((cmd=next_command(&p)) != NULL) {
+ NTSTATUS cmd_result = process_cmd(cli, cmd);
+ result = NT_STATUS_IS_ERR(cmd_result);
+ }
+
+ cli_shutdown(cli);
+ return result;
+ }
+
+ /* Loop around accepting commands */
+
+ while(1) {
+ pstring prompt;
+ char *line;
+
+ slprintf(prompt, sizeof(prompt) - 1, "rpcclient $> ");
+
+ line = smb_readline(prompt, NULL, completion_fn);
+
+ if (line == NULL)
+ break;
+
+ if (line[0] != '\n')
+ process_cmd(cli, line);
+ }
+
+ cli_shutdown(cli);
+ return 0;
+}
diff --git a/source/rpcclient/rpcclient.h b/source/rpcclient/rpcclient.h
new file mode 100644
index 00000000000..e1e61dc43d5
--- /dev/null
+++ b/source/rpcclient/rpcclient.h
@@ -0,0 +1,42 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ Copyright (C) Tim Potter 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef RPCCLIENT_H
+#define RPCCLIENT_H
+
+typedef enum {
+ RPC_RTYPE_NTSTATUS = 0,
+ RPC_RTYPE_WERROR,
+ MAX_RPC_RETURN_TYPE
+} RPC_RETURN_TYPE;
+
+struct cmd_set {
+ const char *name;
+ RPC_RETURN_TYPE returntype;
+ NTSTATUS (*ntfn)(struct cli_state *cli, TALLOC_CTX *mem_ctx, int argc,
+ const char **argv);
+ WERROR (*wfn)(struct cli_state *cli, TALLOC_CTX *mem_ctx, int argc, const char **argv);
+ int pipe_idx;
+ const char *description;
+ const char *usage;
+};
+
+#endif /* RPCCLIENT_H */
diff --git a/source/sam/account.c b/source/sam/account.c
new file mode 100644
index 00000000000..b8336146cda
--- /dev/null
+++ b/source/sam/account.c
@@ -0,0 +1,305 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Jeremy Allison 1996-2001
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+ Copyright (C) Gerald (Jerry) Carter 2000-2001
+ Copyright (C) Andrew Bartlett 2001-2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_SAM
+
+/************************************************************
+ Fill the SAM_ACCOUNT_HANDLE with default values.
+ ***********************************************************/
+
+static void sam_fill_default_account(SAM_ACCOUNT_HANDLE *account)
+{
+ ZERO_STRUCT(account->private); /* Don't touch the talloc context */
+
+ /* Don't change these timestamp settings without a good reason.
+ They are important for NT member server compatibility. */
+
+ /* FIXME: We should actually call get_nt_time_max() or sthng
+ * here */
+ unix_to_nt_time(&(account->private.logoff_time),get_time_t_max());
+ unix_to_nt_time(&(account->private.kickoff_time),get_time_t_max());
+ unix_to_nt_time(&(account->private.pass_must_change_time),get_time_t_max());
+ account->private.unknown_1 = 0x00ffffff; /* don't know */
+ account->private.logon_divs = 168; /* hours per week */
+ account->private.hours_len = 21; /* 21 times 8 bits = 168 */
+ memset(account->private.hours, 0xff, account->private.hours_len); /* available at all hours */
+ account->private.unknown_2 = 0x00000000; /* don't know */
+ account->private.unknown_3 = 0x000004ec; /* don't know */
+}
+
+static void destroy_sam_talloc(SAM_ACCOUNT_HANDLE **account)
+{
+ if (*account) {
+ data_blob_clear_free(&((*account)->private.lm_pw));
+ data_blob_clear_free(&((*account)->private.nt_pw));
+ if((*account)->private.plaintext_pw!=NULL)
+ memset((*account)->private.plaintext_pw,'\0',strlen((*account)->private.plaintext_pw));
+
+ talloc_destroy((*account)->mem_ctx);
+ *account = NULL;
+ }
+}
+
+
+/**********************************************************************
+ Alloc memory and initialises a SAM_ACCOUNT_HANDLE on supplied mem_ctx.
+***********************************************************************/
+
+NTSTATUS sam_init_account_talloc(TALLOC_CTX *mem_ctx, SAM_ACCOUNT_HANDLE **account)
+{
+ SMB_ASSERT(*account != NULL);
+
+ if (!mem_ctx) {
+ DEBUG(0,("sam_init_account_talloc: mem_ctx was NULL!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ *account=(SAM_ACCOUNT_HANDLE *)talloc(mem_ctx, sizeof(SAM_ACCOUNT_HANDLE));
+
+ if (*account==NULL) {
+ DEBUG(0,("sam_init_account_talloc: error while allocating memory\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*account)->mem_ctx = mem_ctx;
+
+ (*account)->free_fn = NULL;
+
+ sam_fill_default_account(*account);
+
+ return NT_STATUS_OK;
+}
+
+
+/*************************************************************
+ Alloc memory and initialises a struct sam_passwd.
+ ************************************************************/
+
+NTSTATUS sam_init_account(SAM_ACCOUNT_HANDLE **account)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS nt_status;
+
+ mem_ctx = talloc_init("sam internal SAM_ACCOUNT_HANDLE allocation");
+
+ if (!mem_ctx) {
+ DEBUG(0,("sam_init_account: error while doing talloc_init()\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_init_account_talloc(mem_ctx, account))) {
+ talloc_destroy(mem_ctx);
+ return nt_status;
+ }
+
+ (*account)->free_fn = destroy_sam_talloc;
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * Free the contents of the SAM_ACCOUNT_HANDLE, but not the structure.
+ *
+ * Also wipes the LM and NT hashes and plaintext password from
+ * memory.
+ *
+ * @param account SAM_ACCOUNT_HANDLE to free members of.
+ **/
+
+static void sam_free_account_contents(SAM_ACCOUNT_HANDLE *account)
+{
+
+ /* Kill off sensitive data. Free()ed by the
+ talloc mechinism */
+
+ data_blob_clear_free(&(account->private.lm_pw));
+ data_blob_clear_free(&(account->private.nt_pw));
+ if (account->private.plaintext_pw)
+ memset(account->private.plaintext_pw,'\0',strlen(account->private.plaintext_pw));
+}
+
+
+/************************************************************
+ Reset the SAM_ACCOUNT_HANDLE and free the NT/LM hashes.
+ ***********************************************************/
+
+NTSTATUS sam_reset_sam(SAM_ACCOUNT_HANDLE *account)
+{
+ SMB_ASSERT(account != NULL);
+
+ sam_free_account_contents(account);
+
+ sam_fill_default_account(account);
+
+ return NT_STATUS_OK;
+}
+
+
+/************************************************************
+ Free the SAM_ACCOUNT_HANDLE and the member pointers.
+ ***********************************************************/
+
+NTSTATUS sam_free_account(SAM_ACCOUNT_HANDLE **account)
+{
+ SMB_ASSERT(*account != NULL);
+
+ sam_free_account_contents(*account);
+
+ if ((*account)->free_fn) {
+ (*account)->free_fn(account);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/**********************************************************
+ Encode the account control bits into a string.
+ length = length of string to encode into (including terminating
+ null). length *MUST BE MORE THAN 2* !
+ **********************************************************/
+
+char *sam_encode_acct_ctrl(uint16 acct_ctrl, size_t length)
+{
+ static fstring acct_str;
+ size_t i = 0;
+
+ acct_str[i++] = '[';
+
+ if (acct_ctrl & ACB_PWNOTREQ ) acct_str[i++] = 'N';
+ if (acct_ctrl & ACB_DISABLED ) acct_str[i++] = 'D';
+ if (acct_ctrl & ACB_HOMDIRREQ) acct_str[i++] = 'H';
+ if (acct_ctrl & ACB_TEMPDUP ) acct_str[i++] = 'T';
+ if (acct_ctrl & ACB_NORMAL ) acct_str[i++] = 'U';
+ if (acct_ctrl & ACB_MNS ) acct_str[i++] = 'M';
+ if (acct_ctrl & ACB_WSTRUST ) acct_str[i++] = 'W';
+ if (acct_ctrl & ACB_SVRTRUST ) acct_str[i++] = 'S';
+ if (acct_ctrl & ACB_AUTOLOCK ) acct_str[i++] = 'L';
+ if (acct_ctrl & ACB_PWNOEXP ) acct_str[i++] = 'X';
+ if (acct_ctrl & ACB_DOMTRUST ) acct_str[i++] = 'I';
+
+ for ( ; i < length - 2 ; i++ )
+ acct_str[i] = ' ';
+
+ i = length - 2;
+ acct_str[i++] = ']';
+ acct_str[i++] = '\0';
+
+ return acct_str;
+}
+
+/**********************************************************
+ Decode the account control bits from a string.
+ **********************************************************/
+
+uint16 sam_decode_acct_ctrl(const char *p)
+{
+ uint16 acct_ctrl = 0;
+ BOOL finished = False;
+
+ /*
+ * Check if the account type bits have been encoded after the
+ * NT password (in the form [NDHTUWSLXI]).
+ */
+
+ if (*p != '[')
+ return 0;
+
+ for (p++; *p && !finished; p++) {
+ switch (*p) {
+ case 'N': { acct_ctrl |= ACB_PWNOTREQ ; break; /* 'N'o password. */ }
+ case 'D': { acct_ctrl |= ACB_DISABLED ; break; /* 'D'isabled. */ }
+ case 'H': { acct_ctrl |= ACB_HOMDIRREQ; break; /* 'H'omedir required. */ }
+ case 'T': { acct_ctrl |= ACB_TEMPDUP ; break; /* 'T'emp account. */ }
+ case 'U': { acct_ctrl |= ACB_NORMAL ; break; /* 'U'ser account (normal). */ }
+ case 'M': { acct_ctrl |= ACB_MNS ; break; /* 'M'NS logon user account. What is this ? */ }
+ case 'W': { acct_ctrl |= ACB_WSTRUST ; break; /* 'W'orkstation account. */ }
+ case 'S': { acct_ctrl |= ACB_SVRTRUST ; break; /* 'S'erver account. */ }
+ case 'L': { acct_ctrl |= ACB_AUTOLOCK ; break; /* 'L'ocked account. */ }
+ case 'X': { acct_ctrl |= ACB_PWNOEXP ; break; /* No 'X'piry on password */ }
+ case 'I': { acct_ctrl |= ACB_DOMTRUST ; break; /* 'I'nterdomain trust account. */ }
+ case ' ': { break; }
+ case ':':
+ case '\n':
+ case '\0':
+ case ']':
+ default: { finished = True; }
+ }
+ }
+
+ return acct_ctrl;
+}
+
+/*************************************************************
+ Routine to set 32 hex password characters from a 16 byte array.
+**************************************************************/
+
+void sam_sethexpwd(char *p, const unsigned char *pwd, uint16 acct_ctrl)
+{
+ if (pwd != NULL) {
+ int i;
+ for (i = 0; i < 16; i++)
+ slprintf(&p[i*2], 3, "%02X", pwd[i]);
+ } else {
+ if (acct_ctrl & ACB_PWNOTREQ)
+ safe_strcpy(p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", 33);
+ else
+ safe_strcpy(p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 33);
+ }
+}
+
+/*************************************************************
+ Routine to get the 32 hex characters and turn them
+ into a 16 byte array.
+**************************************************************/
+
+BOOL sam_gethexpwd(const char *p, unsigned char *pwd)
+{
+ int i;
+ unsigned char lonybble, hinybble;
+ char *hexchars = "0123456789ABCDEF";
+ char *p1, *p2;
+
+ if (!p)
+ return (False);
+
+ for (i = 0; i < 32; i += 2) {
+ hinybble = toupper(p[i]);
+ lonybble = toupper(p[i + 1]);
+
+ p1 = strchr(hexchars, hinybble);
+ p2 = strchr(hexchars, lonybble);
+
+ if (!p1 || !p2)
+ return (False);
+
+ hinybble = PTR_DIFF(p1, hexchars);
+ lonybble = PTR_DIFF(p2, hexchars);
+
+ pwd[i / 2] = (hinybble << 4) | lonybble;
+ }
+ return (True);
+}
diff --git a/source/sam/group.c b/source/sam/group.c
new file mode 100644
index 00000000000..101e3dd7ce1
--- /dev/null
+++ b/source/sam/group.c
@@ -0,0 +1,193 @@
+/*
+ Unix SMB/CIFS implementation.
+ SAM_GROUP_HANDLE /SAM_GROUP_ENUM helpers
+
+ Copyright (C) Stefan (metze) Metzmacher 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_SAM
+
+/************************************************************
+ Fill the SAM_GROUP_HANDLE with default values.
+ ***********************************************************/
+
+static void sam_fill_default_group(SAM_GROUP_HANDLE *group)
+{
+ ZERO_STRUCT(group->private); /* Don't touch the talloc context */
+
+}
+
+static void destroy_sam_group_handle_talloc(SAM_GROUP_HANDLE **group)
+{
+ if (*group) {
+
+ talloc_destroy((*group)->mem_ctx);
+ *group = NULL;
+ }
+}
+
+
+/**********************************************************************
+ Alloc memory and initialises a SAM_GROUP_HANDLE on supplied mem_ctx.
+***********************************************************************/
+
+NTSTATUS sam_init_group_talloc(TALLOC_CTX *mem_ctx, SAM_GROUP_HANDLE **group)
+{
+ SMB_ASSERT(*group != NULL);
+
+ if (!mem_ctx) {
+ DEBUG(0,("sam_init_group_talloc: mem_ctx was NULL!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ *group=(SAM_GROUP_HANDLE *)talloc(mem_ctx, sizeof(SAM_GROUP_HANDLE));
+
+ if (*group==NULL) {
+ DEBUG(0,("sam_init_group_talloc: error while allocating memory\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*group)->mem_ctx = mem_ctx;
+
+ (*group)->free_fn = NULL;
+
+ sam_fill_default_group(*group);
+
+ return NT_STATUS_OK;
+}
+
+
+/*************************************************************
+ Alloc memory and initialises a struct SAM_GROUP_HANDLE.
+ ************************************************************/
+
+NTSTATUS sam_init_group(SAM_GROUP_HANDLE **group)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS nt_status;
+
+ mem_ctx = talloc_init("sam internal SAM_GROUP_HANDLE allocation");
+
+ if (!mem_ctx) {
+ DEBUG(0,("sam_init_group: error while doing talloc_init()\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_init_group_talloc(mem_ctx, group))) {
+ talloc_destroy(mem_ctx);
+ return nt_status;
+ }
+
+ (*group)->free_fn = destroy_sam_group_handle_talloc;
+
+ return NT_STATUS_OK;
+}
+
+
+/************************************************************
+ Reset the SAM_GROUP_HANDLE.
+ ***********************************************************/
+
+NTSTATUS sam_reset_group(SAM_GROUP_HANDLE *group)
+{
+ SMB_ASSERT(group != NULL);
+
+ sam_fill_default_group(group);
+
+ return NT_STATUS_OK;
+}
+
+
+/************************************************************
+ Free the SAM_GROUP_HANDLE and the member pointers.
+ ***********************************************************/
+
+NTSTATUS sam_free_group(SAM_ACCOUNT_HANDLE **group)
+{
+ SMB_ASSERT(*group != NULL);
+
+ if ((*group)->free_fn) {
+ (*group)->free_fn(group);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/**********************************************************
+ Encode the group control bits into a string.
+ length = length of string to encode into (including terminating
+ null). length *MUST BE MORE THAN 2* !
+ **********************************************************/
+
+char *sam_encode_acct_ctrl(uint16 group_ctrl, size_t length)
+{
+ static fstring group_str;
+ size_t i = 0;
+
+ group_str[i++] = '[';
+
+ if (group_ctrl & GCB_LOCAL_GROUP ) group_str[i++] = 'L';
+ if (group_ctrl & GCB_GLOBAL_GROUP ) group_str[i++] = 'G';
+
+ for ( ; i < length - 2 ; i++ )
+ group_str[i] = ' ';
+
+ i = length - 2;
+ group_str[i++] = ']';
+ group_str[i++] = '\0';
+
+ return group_str;
+}
+
+/**********************************************************
+ Decode the group control bits from a string.
+ **********************************************************/
+
+uint16 sam_decode_group_ctrl(const char *p)
+{
+ uint16 group_ctrl = 0;
+ BOOL finished = False;
+
+ /*
+ * Check if the account type bits have been encoded after the
+ * NT password (in the form [NDHTUWSLXI]).
+ */
+
+ if (*p != '[')
+ return 0;
+
+ for (p++; *p && !finished; p++) {
+ switch (*p) {
+ case 'L': { group_ctrl |= GCB_LOCAL_GROUP; break; /* 'L'ocal Aliases Group. */ }
+ case 'G': { group_ctrl |= GCB_GLOBAL_GROUP; break; /* 'G'lobal Domain Group. */ }
+
+ case ' ': { break; }
+ case ':':
+ case '\n':
+ case '\0':
+ case ']':
+ default: { finished = True; }
+ }
+ }
+
+ return group_ctrl;
+}
+
diff --git a/source/sam/gums.c b/source/sam/gums.c
new file mode 100644
index 00000000000..b7191535845
--- /dev/null
+++ b/source/sam/gums.c
@@ -0,0 +1,173 @@
+/*
+ Unix SMB/CIFS implementation.
+ Grops and Users Management System initializations.
+ Copyright (C) Simo Sorce 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_SAM
+
+#define GMV_MAJOR 0
+#define GMV_MINOR 1
+
+static GUMS_FUNCTIONS *gums_backend = NULL;
+
+static struct gums_init_function_entry *backends = NULL;
+
+static void lazy_initialize_gums(void)
+{
+ static BOOL initialized = False;
+
+ if (initialized)
+ return;
+
+ static_init_gums;
+ initialized = True;
+}
+
+static struct gums_init_function_entry *gums_find_backend_entry(const char *name);
+
+NTSTATUS gums_register_module(int version, const char *name, gums_init_function init_fn)
+{
+ struct gums_init_function_entry *entry = backends;
+
+ if (version != GUMS_INTERFACE_VERSION) {
+ DEBUG(0,("Can't register gums backend!\n"
+ "You tried to register a gums module with"
+ "GUMS_INTERFACE_VERSION %d, while this version"
+ "of samba uses version %d\n", version,
+ GUMS_INTERFACE_VERSION));
+
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ if (!name || !init_fn) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(5,("Attempting to register gums backend %s\n", name));
+
+ /* Check for duplicates */
+ if (gums_find_backend_entry(name)) {
+ DEBUG(0,("There already is a gums backend registered"
+ "with the name %s!\n", name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ entry = smb_xmalloc(sizeof(struct gums_init_function_entry));
+ entry->name = smb_xstrdup(name);
+ entry->init_fn = init_fn;
+
+ DLIST_ADD(backends, entry);
+ DEBUG(5,("Successfully added gums backend '%s'\n", name));
+ return NT_STATUS_OK;
+}
+
+static struct gums_init_function_entry *gums_find_backend_entry(const char *name)
+{
+ struct gums_init_function_entry *entry = backends;
+
+ while (entry) {
+ if (strcmp(entry->name, name) == 0)
+ return entry;
+ entry = entry->next;
+ }
+
+ return NULL;
+}
+
+NTSTATUS gums_setup_backend(const char *backend)
+{
+
+ TALLOC_CTX *mem_ctx;
+ char *module_name = smb_xstrdup(backend);
+ char *p, *module_data = NULL;
+ struct gums_init_function_entry *entry;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ lazy_initialize_gums();
+
+ p = strchr(module_name, ':');
+ if (p) {
+ *p = 0;
+ module_data = p+1;
+ trim_string(module_data, " ", " ");
+ }
+
+ trim_string(module_name, " ", " ");
+
+ DEBUG(5,("Attempting to find a gums backend to match %s (%s)\n", backend, module_name));
+
+ entry = gums_find_backend_entry(module_name);
+
+ /* Try to find a module that contains this module */
+ if (!entry) {
+ DEBUG(2,("No builtin backend found, trying to load plugin\n"));
+ if(NT_STATUS_IS_OK(smb_probe_module("gums", module_name)) && !(entry = gums_find_backend_entry(module_name))) {
+ DEBUG(0,("Plugin is available, but doesn't register gums backend %s\n", module_name));
+ SAFE_FREE(module_name);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ /* No such backend found */
+ if(!entry) {
+ DEBUG(0,("No builtin nor plugin backend for %s found\n", module_name));
+ SAFE_FREE(module_name);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(5,("Found gums backend %s\n", module_name));
+
+ /* free current functions structure if any */
+ if (gums_backend) {
+ gums_backend->free_private_data(gums_backend->private_data);
+ talloc_destroy(gums_backend->mem_ctx);
+ gums_backend = NULL;
+ }
+
+ /* allocate a new GUMS_FUNCTIONS structure and memory context */
+ mem_ctx = talloc_init("gums_backend (%s)", module_name);
+ if (!mem_ctx)
+ return NT_STATUS_NO_MEMORY;
+ gums_backend = talloc(mem_ctx, sizeof(GUMS_FUNCTIONS));
+ if (!gums_backend)
+ return NT_STATUS_NO_MEMORY;
+ gums_backend->mem_ctx = mem_ctx;
+
+ /* init the requested backend module */
+ if (NT_STATUS_IS_OK(ret = entry->init_fn(gums_backend, module_data))) {
+ DEBUG(5,("gums backend %s has a valid init\n", backend));
+ } else {
+ DEBUG(0,("gums backend %s did not correctly init (error was %s)\n", backend, nt_errstr(ret)));
+ }
+ SAFE_FREE(module_name);
+ return ret;
+}
+
+NTSTATUS get_gums_fns(GUMS_FUNCTIONS **fns)
+{
+ if (gums_backend != NULL) {
+ *fns = gums_backend;
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(2, ("get_gums_fns: unable to get gums functions! backend uninitialized?\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+}
diff --git a/source/sam/gums_api.c b/source/sam/gums_api.c
new file mode 100644
index 00000000000..5aafa7695f6
--- /dev/null
+++ b/source/sam/gums_api.c
@@ -0,0 +1,1426 @@
+/*
+ Unix SMB/CIFS implementation.
+ GUMS structures
+ Copyright (C) Simo Sorce 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* Functions to get/set info from a GUMS object */
+
+NTSTATUS gums_create_object(GUMS_OBJECT **obj, uint32 type)
+{
+ TALLOC_CTX *mem_ctx;
+ GUMS_OBJECT *go;
+ NTSTATUS ret;
+
+ mem_ctx = talloc_init("gums_create_object");
+ if (!mem_ctx) {
+ DEBUG(0, ("gums_create_object: Out of memory!\n"));
+ *obj = NULL;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ go = talloc_zero(mem_ctx, sizeof(GUMS_OBJECT));
+ if (!go) {
+ DEBUG(0, ("gums_create_object: Out of memory!\n"));
+ talloc_destroy(mem_ctx);
+ *obj = NULL;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ go->mem_ctx = mem_ctx;
+ go->type = type;
+ go->version = GUMS_OBJECT_VERSION;
+
+ switch(type) {
+ case GUMS_OBJ_DOMAIN:
+ go->domain = (GUMS_DOMAIN *)talloc_zero(mem_ctx, sizeof(GUMS_DOMAIN));
+ if (!(go->domain)) {
+ ret = NT_STATUS_NO_MEMORY;
+ DEBUG(0, ("gums_create_object: Out of memory!\n"));
+ goto error;
+ }
+
+ break;
+
+/*
+ case GUMS_OBJ_WORKSTATION_TRUST:
+ case GUMS_OBJ_SERVER_TRUST:
+ case GUMS_OBJ_DOMAIN_TRUST:
+*/
+ case GUMS_OBJ_NORMAL_USER:
+ go->user = (GUMS_USER *)talloc_zero(mem_ctx, sizeof(GUMS_USER));
+ if (!(go->user)) {
+ ret = NT_STATUS_NO_MEMORY;
+ DEBUG(0, ("gums_create_object: Out of memory!\n"));
+ goto error;
+ }
+ gums_set_user_acct_ctrl(go, ACB_NORMAL);
+ gums_set_user_hours(go, 0, NULL);
+
+ break;
+
+ case GUMS_OBJ_GROUP:
+ case GUMS_OBJ_ALIAS:
+ go->group = (GUMS_GROUP *)talloc_zero(mem_ctx, sizeof(GUMS_GROUP));
+ if (!(go->group)) {
+ ret = NT_STATUS_NO_MEMORY;
+ DEBUG(0, ("gums_create_object: Out of memory!\n"));
+ goto error;
+ }
+
+ break;
+
+ default:
+ /* TODO: throw error */
+ ret = NT_STATUS_OBJECT_TYPE_MISMATCH;
+ goto error;
+ }
+
+ *obj = go;
+ return NT_STATUS_OK;
+
+error:
+ talloc_destroy(go->mem_ctx);
+ *obj = NULL;
+ return ret;
+}
+
+NTSTATUS gums_create_privilege(GUMS_PRIVILEGE **priv)
+{
+ TALLOC_CTX *mem_ctx;
+ GUMS_PRIVILEGE *pri;
+
+ mem_ctx = talloc_init("gums_create_privilege");
+ if (!mem_ctx) {
+ DEBUG(0, ("gums_create_privilege: Out of memory!\n"));
+ *priv = NULL;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ pri = talloc_zero(mem_ctx, sizeof(GUMS_PRIVILEGE));
+ if (!pri) {
+ DEBUG(0, ("gums_create_privilege: Out of memory!\n"));
+ talloc_destroy(mem_ctx);
+ *priv = NULL;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ pri->mem_ctx = mem_ctx;
+ pri->version = GUMS_PRIVILEGE_VERSION;
+
+ *priv = pri;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_destroy_object(GUMS_OBJECT **obj)
+{
+ if (!obj || !(*obj))
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if ((*obj)->mem_ctx)
+ talloc_destroy((*obj)->mem_ctx);
+ *obj = NULL;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_destroy_privilege(GUMS_PRIVILEGE **priv)
+{
+ if (!priv || !(*priv))
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if ((*priv)->mem_ctx)
+ talloc_destroy((*priv)->mem_ctx);
+ *priv = NULL;
+
+ return NT_STATUS_OK;
+}
+
+void gums_reset_object(GUMS_OBJECT *go)
+{
+ go->seq_num = 0;
+ go->sid = NULL;
+ go->name = NULL;
+ go->description = NULL;
+
+ switch(go->type) {
+ case GUMS_OBJ_DOMAIN:
+ memset(go->domain, 0, sizeof(GUMS_DOMAIN));
+ break;
+
+/*
+ case GUMS_OBJ_WORKSTATION_TRUST:
+ case GUMS_OBJ_SERVER_TRUST:
+ case GUMS_OBJ_DOMAIN_TRUST:
+*/
+ case GUMS_OBJ_NORMAL_USER:
+ memset(go->user, 0, sizeof(GUMS_USER));
+ gums_set_user_acct_ctrl(go, ACB_NORMAL);
+ break;
+
+ case GUMS_OBJ_GROUP:
+ case GUMS_OBJ_ALIAS:
+ memset(go->group, 0, sizeof(GUMS_GROUP));
+ break;
+
+ default:
+ return;
+ }
+}
+
+uint32 gums_get_object_type(const GUMS_OBJECT *obj)
+{
+ if (!obj)
+ return 0;
+
+ return obj->type;
+}
+
+uint32 gums_get_object_seq_num(const GUMS_OBJECT *obj)
+{
+ if (!obj)
+ return 0;
+
+ return obj->seq_num;
+}
+
+uint32 gums_get_object_version(const GUMS_OBJECT *obj)
+{
+ if (!obj)
+ return 0;
+
+ return obj->version;
+}
+
+const SEC_DESC *gums_get_sec_desc(const GUMS_OBJECT *obj)
+{
+ if (!obj)
+ return NULL;
+
+ return obj->sec_desc;
+}
+
+const DOM_SID *gums_get_object_sid(const GUMS_OBJECT *obj)
+{
+ if (!obj)
+ return NULL;
+
+ return obj->sid;
+}
+
+const char *gums_get_object_name(const GUMS_OBJECT *obj)
+{
+ if (!obj)
+ return NULL;
+
+ return obj->name;
+}
+
+const char *gums_get_object_description(const GUMS_OBJECT *obj)
+{
+ if (!obj)
+ return NULL;
+
+ return obj->description;
+}
+
+NTSTATUS gums_set_object_seq_num(GUMS_OBJECT *obj, uint32 seq_num)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ obj->seq_num = seq_num;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_object_version(GUMS_OBJECT *obj, uint32 version)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ obj->version = version;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_sec_desc(GUMS_OBJECT *obj, const SEC_DESC *sec_desc)
+{
+ if (!obj || !sec_desc)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ obj->sec_desc = dup_sec_desc(obj->mem_ctx, sec_desc);
+ if (!(obj->sec_desc)) return NT_STATUS_UNSUCCESSFUL;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_object_sid(GUMS_OBJECT *obj, const DOM_SID *sid)
+{
+ if (!obj || !sid)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ obj->sid = sid_dup_talloc(obj->mem_ctx, sid);
+ if (!(obj->sid)) return NT_STATUS_UNSUCCESSFUL;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_object_name(GUMS_OBJECT *obj, const char *name)
+{
+ if (!obj || !name)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ obj->name = (char *)talloc_strdup(obj->mem_ctx, name);
+ if (!(obj->name)) return NT_STATUS_UNSUCCESSFUL;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_object_description(GUMS_OBJECT *obj, const char *description)
+{
+ if (!obj || !description)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ obj->description = (char *)talloc_strdup(obj->mem_ctx, description);
+ if (!(obj->description)) return NT_STATUS_UNSUCCESSFUL;
+ return NT_STATUS_OK;
+}
+
+/*
+NTSTATUS gums_get_object_privileges(PRIVILEGE_SET **priv_set, const GUMS_OBJECT *obj)
+{
+ if (!priv_set)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ *priv_set = obj->priv_set;
+ return NT_STATUS_OK;
+}
+*/
+
+uint32 gums_get_domain_next_rid(const GUMS_OBJECT *obj)
+{
+ if (obj->type != GUMS_OBJ_DOMAIN)
+ return -1;
+
+ return obj->domain->next_rid;
+}
+
+NTSTATUS gums_set_domain_next_rid(GUMS_OBJECT *obj, uint32 rid)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_DOMAIN)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->domain->next_rid = rid;
+ return NT_STATUS_OK;
+}
+
+/* User specific functions */
+
+const DOM_SID *gums_get_user_pri_group(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->group_sid;
+}
+
+const DATA_BLOB gums_get_user_nt_pwd(const GUMS_OBJECT *obj)
+{
+ fstring p;
+
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return data_blob(NULL, 0);
+
+ pdb_sethexpwd(p, (unsigned char *)(obj->user->nt_pw.data), 0);
+ DEBUG(100, ("Reading NT Password=[%s]\n", p));
+
+ return obj->user->nt_pw;
+}
+
+const DATA_BLOB gums_get_user_lm_pwd(const GUMS_OBJECT *obj)
+{
+ fstring p;
+
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return data_blob(NULL, 0);
+
+ pdb_sethexpwd(p, (unsigned char *)(obj->user->lm_pw.data), 0);
+ DEBUG(100, ("Reading LM Password=[%s]\n", p));
+
+ return obj->user->lm_pw;
+}
+
+const char *gums_get_user_fullname(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->full_name;
+}
+
+const char *gums_get_user_homedir(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->home_dir;
+}
+
+const char *gums_get_user_dir_drive(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->dir_drive;
+}
+
+const char *gums_get_user_profile_path(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->profile_path;
+}
+
+const char *gums_get_user_logon_script(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->logon_script;
+}
+
+const char *gums_get_user_workstations(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->workstations;
+}
+
+const char *gums_get_user_unknown_str(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->unknown_str;
+}
+
+const char *gums_get_user_munged_dial(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->munged_dial;
+}
+
+NTTIME gums_get_user_logon_time(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER) {
+ NTTIME null_time;
+ init_nt_time(&null_time);
+ return null_time;
+ }
+
+ return obj->user->logon_time;
+}
+
+NTTIME gums_get_user_logoff_time(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER) {
+ NTTIME null_time;
+ init_nt_time(&null_time);
+ return null_time;
+ }
+
+ return obj->user->logoff_time;
+}
+
+NTTIME gums_get_user_kickoff_time(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER) {
+ NTTIME null_time;
+ init_nt_time(&null_time);
+ return null_time;
+ }
+
+ return obj->user->kickoff_time;
+}
+
+NTTIME gums_get_user_pass_last_set_time(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER) {
+ NTTIME null_time;
+ init_nt_time(&null_time);
+ return null_time;
+ }
+
+ return obj->user->pass_last_set_time;
+}
+
+NTTIME gums_get_user_pass_can_change_time(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER) {
+ NTTIME null_time;
+ init_nt_time(&null_time);
+ return null_time;
+ }
+
+ return obj->user->pass_can_change_time;
+}
+
+NTTIME gums_get_user_pass_must_change_time(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER) {
+ NTTIME null_time;
+ init_nt_time(&null_time);
+ return null_time;
+ }
+
+ return obj->user->pass_must_change_time;
+}
+
+uint16 gums_get_user_acct_ctrl(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return 0;
+
+ return obj->user->acct_ctrl;
+}
+
+uint16 gums_get_user_logon_divs(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return 0;
+
+ return obj->user->logon_divs;
+}
+
+uint32 gums_get_user_hours_len(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return 0;
+
+ return obj->user->hours_len;
+}
+
+const uint8 *gums_get_user_hours(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->hours;
+}
+
+uint32 gums_get_user_unknown_3(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return 0;
+
+ return obj->user->unknown_3;
+}
+
+uint16 gums_get_user_bad_password_count(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return 0;
+
+ return obj->user->bad_password_count;
+}
+
+uint16 gums_get_user_logon_count(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return 0;
+
+ return obj->user->logon_count;
+}
+
+uint32 gums_get_user_unknown_6(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return 0;
+
+ return obj->user->unknown_6;
+}
+
+NTSTATUS gums_set_user_pri_group(GUMS_OBJECT *obj, const DOM_SID *sid)
+{
+ if (!obj || !sid)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->group_sid = sid_dup_talloc(obj->mem_ctx, sid);
+ if (!(obj->user->group_sid)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_nt_pwd(GUMS_OBJECT *obj, const DATA_BLOB nt_pwd)
+{
+ fstring p;
+ unsigned char r[16];
+
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->nt_pw = data_blob_talloc(obj->mem_ctx, nt_pwd.data, nt_pwd.length);
+
+ memcpy(r, nt_pwd.data, 16);
+ pdb_sethexpwd(p, r, 0);
+ DEBUG(100, ("Setting NT Password=[%s]\n", p));
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_lm_pwd(GUMS_OBJECT *obj, const DATA_BLOB lm_pwd)
+{
+ fstring p;
+ unsigned char r[16];
+
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->lm_pw = data_blob_talloc(obj->mem_ctx, lm_pwd.data, lm_pwd.length);
+
+ memcpy(r, lm_pwd.data, 16);
+ pdb_sethexpwd(p, r, 0);
+ DEBUG(100, ("Setting LM Password=[%s]\n", p));
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_fullname(GUMS_OBJECT *obj, const char *fullname)
+{
+ if (!obj || !fullname)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->full_name = (char *)talloc_strdup(obj->mem_ctx, fullname);
+ if (!(obj->user->full_name)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_homedir(GUMS_OBJECT *obj, const char *homedir)
+{
+ if (!obj || !homedir)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->home_dir = (char *)talloc_strdup(obj->mem_ctx, homedir);
+ if (!(obj->user->home_dir)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_dir_drive(GUMS_OBJECT *obj, const char *dir_drive)
+{
+ if (!obj || !dir_drive)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->dir_drive = (char *)talloc_strdup(obj->mem_ctx, dir_drive);
+ if (!(obj->user->dir_drive)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_logon_script(GUMS_OBJECT *obj, const char *logon_script)
+{
+ if (!obj || !logon_script)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->logon_script = (char *)talloc_strdup(obj->mem_ctx, logon_script);
+ if (!(obj->user->logon_script)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_profile_path(GUMS_OBJECT *obj, const char *profile_path)
+{
+ if (!obj || !profile_path)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->profile_path = (char *)talloc_strdup(obj->mem_ctx, profile_path);
+ if (!(obj->user->profile_path)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_workstations(GUMS_OBJECT *obj, const char *workstations)
+{
+ if (!obj || !workstations)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->workstations = (char *)talloc_strdup(obj->mem_ctx, workstations);
+ if (!(obj->user->workstations)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_unknown_str(GUMS_OBJECT *obj, const char *unknown_str)
+{
+ if (!obj || !unknown_str)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->unknown_str = (char *)talloc_strdup(obj->mem_ctx, unknown_str);
+ if (!(obj->user->unknown_str)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_munged_dial(GUMS_OBJECT *obj, const char *munged_dial)
+{
+ if (!obj || !munged_dial)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->munged_dial = (char *)talloc_strdup(obj->mem_ctx, munged_dial);
+ if (!(obj->user->munged_dial)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_logon_time(GUMS_OBJECT *obj, NTTIME logon_time)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->logon_time = logon_time;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_logoff_time(GUMS_OBJECT *obj, NTTIME logoff_time)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->logoff_time = logoff_time;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_kickoff_time(GUMS_OBJECT *obj, NTTIME kickoff_time)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->kickoff_time = kickoff_time;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_pass_last_set_time(GUMS_OBJECT *obj, NTTIME pass_last_set_time)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->pass_last_set_time = pass_last_set_time;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_pass_can_change_time(GUMS_OBJECT *obj, NTTIME pass_can_change_time)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->pass_can_change_time = pass_can_change_time;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_pass_must_change_time(GUMS_OBJECT *obj, NTTIME pass_must_change_time)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->pass_must_change_time = pass_must_change_time;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_acct_ctrl(GUMS_OBJECT *obj, uint16 acct_ctrl)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->acct_ctrl = acct_ctrl;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_logon_divs(GUMS_OBJECT *obj, uint16 logon_divs)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->logon_divs = logon_divs;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_hours(GUMS_OBJECT *obj, uint32 hours_len, const uint8 *hours)
+{
+ if (!obj || !hours)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->hours_len = hours_len;
+ if (hours_len == 0)
+ DEBUG(10, ("gums_set_user_hours: Warning, hours_len is zero!\n"));
+
+ obj->user->hours = (uint8 *)talloc(obj->mem_ctx, MAX_HOURS_LEN);
+ if (!(obj->user->hours))
+ return NT_STATUS_NO_MEMORY;
+ if (hours_len)
+ memcpy(obj->user->hours, hours, hours_len);
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_unknown_3(GUMS_OBJECT *obj, uint32 unknown_3)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->unknown_3 = unknown_3;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_bad_password_count(GUMS_OBJECT *obj, uint16 bad_password_count)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->bad_password_count = bad_password_count;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_logon_count(GUMS_OBJECT *obj, uint16 logon_count)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->logon_count = logon_count;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_unknown_6(GUMS_OBJECT *obj, uint32 unknown_6)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->unknown_6 = unknown_6;
+ return NT_STATUS_OK;
+}
+
+/* Group specific functions */
+
+const DOM_SID *gums_get_group_members(int *count, const GUMS_OBJECT *obj)
+{
+ if (!count || !obj || !(obj->type == GUMS_OBJ_GROUP || obj->type == GUMS_OBJ_ALIAS)) {
+ *count = -1;
+ return NULL;
+ }
+
+ *count = obj->group->count;
+ return obj->group->members;
+}
+
+NTSTATUS gums_set_group_members(GUMS_OBJECT *obj, uint32 count, DOM_SID *members)
+{
+ uint32 n;
+
+ if (!obj || ((count > 0) && !members))
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_GROUP &&
+ obj->type != GUMS_OBJ_ALIAS)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->group->count = count;
+
+ if (count) {
+ obj->group->members = (DOM_SID *)talloc(obj->mem_ctx, count * sizeof(DOM_SID));
+ if (!(obj->group->members)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+
+ n = 0;
+ do {
+ sid_copy(&(obj->group->members[n]), &(members[n]));
+ n++;
+ } while (n < count);
+ } else {
+ obj->group->members = 0;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* Privilege specific functions */
+
+const LUID_ATTR *gums_get_priv_luid_attr(const GUMS_PRIVILEGE *priv)
+{
+ if (!priv) {
+ return NULL;
+ }
+
+ return priv->privilege;
+}
+
+const DOM_SID *gums_get_priv_members(int *count, const GUMS_PRIVILEGE *priv)
+{
+ if (!count || !priv) {
+ *count = -1;
+ return NULL;
+ }
+
+ *count = priv->count;
+ return priv->members;
+}
+
+NTSTATUS gums_set_priv_luid_attr(GUMS_PRIVILEGE *priv, LUID_ATTR *luid_attr)
+{
+ if (!luid_attr || !priv)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ priv->privilege = (LUID_ATTR *)talloc_memdup(priv->mem_ctx, luid_attr, sizeof(LUID_ATTR));
+ if (!(priv->privilege)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_priv_members(GUMS_PRIVILEGE *priv, uint32 count, DOM_SID *members)
+{
+ uint32 n;
+
+ if (!priv || !members || !members)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ priv->count = count;
+ priv->members = (DOM_SID *)talloc(priv->mem_ctx, count * sizeof(DOM_SID));
+ if (!(priv->members))
+ return NT_STATUS_NO_MEMORY;
+
+ n = 0;
+ do {
+ sid_copy(&(priv->members[n]), &(members[n]));
+ n++;
+ } while (n < count);
+
+ return NT_STATUS_OK;
+}
+
+/* data_store set functions */
+
+NTSTATUS gums_create_commit_set(GUMS_COMMIT_SET **com_set, DOM_SID *sid, uint32 type)
+{
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_init("commit_set");
+ if (mem_ctx == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ *com_set = (GUMS_COMMIT_SET *)talloc_zero(mem_ctx, sizeof(GUMS_COMMIT_SET));
+ if (*com_set == NULL) {
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*com_set)->mem_ctx = mem_ctx;
+ (*com_set)->type = type;
+ sid_copy(&((*com_set)->sid), sid);
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_cs_grow_data_set(GUMS_COMMIT_SET *com_set, int size)
+{
+ GUMS_DATA_SET *data_set;
+
+ com_set->count = com_set->count + size;
+ if (com_set->count == size) { /* data set is empty*/
+ data_set = (GUMS_DATA_SET *)talloc_zero(com_set->mem_ctx, sizeof(GUMS_DATA_SET));
+ } else {
+ data_set = (GUMS_DATA_SET *)talloc_realloc(com_set->mem_ctx, com_set->data, sizeof(GUMS_DATA_SET) * com_set->count);
+ }
+ if (data_set == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ com_set->data = data_set;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_cs_set_sec_desc(GUMS_COMMIT_SET *com_set, SEC_DESC *sec_desc)
+{
+ NTSTATUS ret;
+ GUMS_DATA_SET *data_set;
+ SEC_DESC *new_sec_desc;
+
+ if (!com_set || !sec_desc)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = gums_cs_grow_data_set(com_set, 1)))
+ return ret;
+
+ data_set = &((com_set->data)[com_set->count - 1]);
+
+ data_set->type = GUMS_SET_SEC_DESC;
+ new_sec_desc = dup_sec_desc(com_set->mem_ctx, sec_desc);
+ if (new_sec_desc == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ (SEC_DESC *)(data_set->data) = new_sec_desc;
+
+ return NT_STATUS_OK;
+}
+
+/*
+NTSTATUS gums_cs_add_privilege(GUMS_PRIV_COMMIT_SET *com_set, LUID_ATTR priv)
+{
+ NTSTATUS ret;
+ GUMS_DATA_SET *data_set;
+ LUID_ATTR *new_priv;
+
+ if (!com_set)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_OK(ret = gums_pcs_grow_data_set(com_set, 1)))
+ return ret;
+
+ data_set = ((com_set->data)[com_set->count - 1]);
+
+ data_set->type = GUMS_ADD_PRIVILEGE;
+ if (!NT_STATUS_IS_OK(ret = dupalloc_luid_attr(com_set->mem_ctx, &new_priv, priv)))
+ return ret;
+
+ (SEC_DESC *)(data_set->data) = new_priv;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_cs_del_privilege(GUMS_PRIV_COMMIT_SET *com_set, LUID_ATTR priv)
+{
+ NTSTATUS ret;
+ GUMS_DATA_SET *data_set;
+ LUID_ATTR *new_priv;
+
+ if (!com_set)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_OK(ret = gums_pcs_grow_data_set(com_set, 1)))
+ return ret;
+
+ data_set = ((com_set->data)[com_set->count - 1]);
+
+ data_set->type = GUMS_DEL_PRIVILEGE;
+ if (!NT_STATUS_IS_OK(ret = dupalloc_luid_attr(com_set->mem_ctx, &new_priv, priv)))
+ return ret;
+
+ (SEC_DESC *)(data_set->data) = new_priv;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_cs_set_privilege_set(GUMS_PRIV_COMMIT_SET *com_set, PRIVILEGE_SET *priv_set)
+{
+ NTSTATUS ret;
+ GUMS_DATA_SET *data_set;
+ PRIVILEGE_SET *new_priv_set;
+
+ if (!com_set || !priv_set)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_OK(ret = gums_pcs_grow_data_set(com_set, 1)))
+ return ret;
+
+ data_set = ((com_set->data)[com_set->count - 1]);
+
+ data_set->type = GUMS_SET_PRIVILEGE;
+ if (!NT_STATUS_IS_OK(ret = init_priv_set_with_ctx(com_set->mem_ctx, &new_priv_set)))
+ return ret;
+
+ if (!NT_STATUS_IS_OK(ret = dup_priv_set(new_priv_set, priv_set)))
+ return ret;
+
+ (SEC_DESC *)(data_set->data) = new_priv_set;
+
+ return NT_STATUS_OK;
+}
+*/
+
+NTSTATUS gums_cs_set_string(GUMS_COMMIT_SET *com_set, uint32 type, char *str)
+{
+ NTSTATUS ret;
+ GUMS_DATA_SET *data_set;
+ char *new_str;
+
+ if (!com_set || !str || type < GUMS_SET_NAME || type > GUMS_SET_MUNGED_DIAL)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = gums_cs_grow_data_set(com_set, 1)))
+ return ret;
+
+ data_set = &((com_set->data)[com_set->count - 1]);
+
+ data_set->type = type;
+ new_str = talloc_strdup(com_set->mem_ctx, str);
+ if (new_str == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ (char *)(data_set->data) = new_str;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_cs_set_name(GUMS_COMMIT_SET *com_set, char *name)
+{
+ return gums_cs_set_string(com_set, GUMS_SET_NAME, name);
+}
+
+NTSTATUS gums_cs_set_description(GUMS_COMMIT_SET *com_set, char *desc)
+{
+ return gums_cs_set_string(com_set, GUMS_SET_DESCRIPTION, desc);
+}
+
+NTSTATUS gums_cs_set_full_name(GUMS_COMMIT_SET *com_set, char *full_name)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_string(com_set, GUMS_SET_NAME, full_name);
+}
+
+NTSTATUS gums_cs_set_home_directory(GUMS_COMMIT_SET *com_set, char *home_dir)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_string(com_set, GUMS_SET_NAME, home_dir);
+}
+
+NTSTATUS gums_cs_set_drive(GUMS_COMMIT_SET *com_set, char *drive)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_string(com_set, GUMS_SET_NAME, drive);
+}
+
+NTSTATUS gums_cs_set_logon_script(GUMS_COMMIT_SET *com_set, char *logon_script)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_string(com_set, GUMS_SET_NAME, logon_script);
+}
+
+NTSTATUS gums_cs_set_profile_path(GUMS_COMMIT_SET *com_set, char *prof_path)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_string(com_set, GUMS_SET_NAME, prof_path);
+}
+
+NTSTATUS gums_cs_set_workstations(GUMS_COMMIT_SET *com_set, char *wks)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_string(com_set, GUMS_SET_NAME, wks);
+}
+
+NTSTATUS gums_cs_set_unknown_string(GUMS_COMMIT_SET *com_set, char *unkn_str)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_string(com_set, GUMS_SET_NAME, unkn_str);
+}
+
+NTSTATUS gums_cs_set_munged_dial(GUMS_COMMIT_SET *com_set, char *munged_dial)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_string(com_set, GUMS_SET_NAME, munged_dial);
+}
+
+NTSTATUS gums_cs_set_nttime(GUMS_COMMIT_SET *com_set, uint32 type, NTTIME *nttime)
+{
+ NTSTATUS ret;
+ GUMS_DATA_SET *data_set;
+ NTTIME *new_time;
+
+ if (!com_set || !nttime || type < GUMS_SET_LOGON_TIME || type > GUMS_SET_PASS_MUST_CHANGE_TIME)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = gums_cs_grow_data_set(com_set, 1)))
+ return ret;
+
+ data_set = &((com_set->data)[com_set->count - 1]);
+
+ data_set->type = type;
+ new_time = talloc(com_set->mem_ctx, sizeof(NTTIME));
+ if (new_time == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ new_time->low = nttime->low;
+ new_time->high = nttime->high;
+ (char *)(data_set->data) = new_time;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_cs_set_logon_time(GUMS_COMMIT_SET *com_set, NTTIME *logon_time)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_nttime(com_set, GUMS_SET_LOGON_TIME, logon_time);
+}
+
+NTSTATUS gums_cs_set_logoff_time(GUMS_COMMIT_SET *com_set, NTTIME *logoff_time)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_nttime(com_set, GUMS_SET_LOGOFF_TIME, logoff_time);
+}
+
+NTSTATUS gums_cs_set_kickoff_time(GUMS_COMMIT_SET *com_set, NTTIME *kickoff_time)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_nttime(com_set, GUMS_SET_KICKOFF_TIME, kickoff_time);
+}
+
+NTSTATUS gums_cs_set_pass_last_set_time(GUMS_COMMIT_SET *com_set, NTTIME *pls_time)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_nttime(com_set, GUMS_SET_LOGON_TIME, pls_time);
+}
+
+NTSTATUS gums_cs_set_pass_can_change_time(GUMS_COMMIT_SET *com_set, NTTIME *pcc_time)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_nttime(com_set, GUMS_SET_LOGON_TIME, pcc_time);
+}
+
+NTSTATUS gums_cs_set_pass_must_change_time(GUMS_COMMIT_SET *com_set, NTTIME *pmc_time)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_nttime(com_set, GUMS_SET_LOGON_TIME, pmc_time);
+}
+
+NTSTATUS gums_cs_add_sids_to_group(GUMS_COMMIT_SET *com_set, const DOM_SID **sids, const uint32 count)
+{
+ NTSTATUS ret;
+ GUMS_DATA_SET *data_set;
+ DOM_SID **new_sids;
+ int i;
+
+ if (!com_set || !sids)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = gums_cs_grow_data_set(com_set, 1)))
+ return ret;
+
+ data_set = &((com_set->data)[com_set->count - 1]);
+
+ data_set->type = GUMS_ADD_SID_LIST;
+ new_sids = (DOM_SID **)talloc(com_set->mem_ctx, (sizeof(void *) * count));
+ if (new_sids == NULL)
+ return NT_STATUS_NO_MEMORY;
+ for (i = 0; i < count; i++) {
+ new_sids[i] = sid_dup_talloc(com_set->mem_ctx, sids[i]);
+ if (new_sids[i] == NULL)
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (SEC_DESC *)(data_set->data) = new_sids;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_cs_add_users_to_group(GUMS_COMMIT_SET *com_set, const DOM_SID **sids, const uint32 count)
+{
+ if (!com_set || !sids)
+ return NT_STATUS_INVALID_PARAMETER;
+ if (com_set->type != GUMS_OBJ_GROUP || com_set->type != GUMS_OBJ_ALIAS)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_add_sids_to_group(com_set, sids, count);
+}
+
+NTSTATUS gums_cs_add_groups_to_group(GUMS_COMMIT_SET *com_set, const DOM_SID **sids, const uint32 count)
+{
+ if (!com_set || !sids)
+ return NT_STATUS_INVALID_PARAMETER;
+ if (com_set->type != GUMS_OBJ_ALIAS)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_add_sids_to_group(com_set, sids, count);
+}
+
+NTSTATUS gums_cs_del_sids_from_group(GUMS_COMMIT_SET *com_set, const DOM_SID **sids, const uint32 count)
+{
+ NTSTATUS ret;
+ GUMS_DATA_SET *data_set;
+ DOM_SID **new_sids;
+ int i;
+
+ if (!com_set || !sids)
+ return NT_STATUS_INVALID_PARAMETER;
+ if (com_set->type != GUMS_OBJ_GROUP || com_set->type != GUMS_OBJ_ALIAS)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = gums_cs_grow_data_set(com_set, 1)))
+ return ret;
+
+ data_set = &((com_set->data)[com_set->count - 1]);
+
+ data_set->type = GUMS_DEL_SID_LIST;
+ new_sids = (DOM_SID **)talloc(com_set->mem_ctx, (sizeof(void *) * count));
+ if (new_sids == NULL)
+ return NT_STATUS_NO_MEMORY;
+ for (i = 0; i < count; i++) {
+ new_sids[i] = sid_dup_talloc(com_set->mem_ctx, sids[i]);
+ if (new_sids[i] == NULL)
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (SEC_DESC *)(data_set->data) = new_sids;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_ds_set_sids_in_group(GUMS_COMMIT_SET *com_set, const DOM_SID **sids, const uint32 count)
+{
+ NTSTATUS ret;
+ GUMS_DATA_SET *data_set;
+ DOM_SID **new_sids;
+ int i;
+
+ if (!com_set || !sids)
+ return NT_STATUS_INVALID_PARAMETER;
+ if (com_set->type != GUMS_OBJ_GROUP || com_set->type != GUMS_OBJ_ALIAS)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = gums_cs_grow_data_set(com_set, 1)))
+ return ret;
+
+ data_set = &((com_set->data)[com_set->count - 1]);
+
+ data_set->type = GUMS_SET_SID_LIST;
+ new_sids = (DOM_SID **)talloc(com_set->mem_ctx, (sizeof(void *) * count));
+ if (new_sids == NULL)
+ return NT_STATUS_NO_MEMORY;
+ for (i = 0; i < count; i++) {
+ new_sids[i] = sid_dup_talloc(com_set->mem_ctx, sids[i]);
+ if (new_sids[i] == NULL)
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (SEC_DESC *)(data_set->data) = new_sids;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_commit_data(GUMS_COMMIT_SET *set)
+{
+ NTSTATUS ret;
+ GUMS_FUNCTIONS *fns;
+
+ if (!NT_STATUS_IS_OK(ret = get_gums_fns(&fns))) {
+ DEBUG(0, ("gums_commit_data: unable to get gums functions! backend uninitialized?\n"));
+ return ret;
+ }
+ return fns->set_object_values(&(set->sid), set->count, set->data);
+}
+
+NTSTATUS gums_destroy_commit_set(GUMS_COMMIT_SET **com_set)
+{
+ talloc_destroy((*com_set)->mem_ctx);
+ *com_set = NULL;
+
+ return NT_STATUS_OK;
+}
+
diff --git a/source/sam/gums_helper.c b/source/sam/gums_helper.c
new file mode 100644
index 00000000000..fcb9366cda8
--- /dev/null
+++ b/source/sam/gums_helper.c
@@ -0,0 +1,383 @@
+/*
+ Unix SMB/CIFS implementation.
+ GUMS backends helper functions
+ Copyright (C) Simo Sorce 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+extern DOM_SID global_sid_World;
+extern DOM_SID global_sid_Builtin;
+extern DOM_SID global_sid_Builtin_Administrators;
+extern DOM_SID global_sid_Builtin_Power_Users;
+extern DOM_SID global_sid_Builtin_Account_Operators;
+extern DOM_SID global_sid_Builtin_Server_Operators;
+extern DOM_SID global_sid_Builtin_Print_Operators;
+extern DOM_SID global_sid_Builtin_Backup_Operators;
+extern DOM_SID global_sid_Builtin_Replicator;
+extern DOM_SID global_sid_Builtin_Users;
+extern DOM_SID global_sid_Builtin_Guests;
+
+
+/* defines */
+
+#define ALLOC_CHECK(str, ptr, err, label) do { if ((ptr) == NULL) { DEBUG(0, ("%s: out of memory!\n", str)); err = NT_STATUS_NO_MEMORY; goto label; } } while(0)
+#define NTSTATUS_CHECK(err, label, str1, str2) do { if (NT_STATUS_IS_ERR(err)) { DEBUG(0, ("%s: %s\n", str1, str2)); } } while(0)
+
+/****************************************************************************
+ Check if a user is a mapped group.
+
+ This function will check if the group SID is mapped onto a
+ system managed gid or onto a winbind manged sid.
+ In the first case it will be threated like a mapped group
+ and the backend should take the member list with a getgrgid
+ and ignore any user that have been possibly set into the group
+ object.
+
+ In the second case, the group is a fully SAM managed group
+ served back to the system through winbind. In this case the
+ members of a Local group are "unrolled" to cope with the fact
+ that unix cannot contain groups inside groups.
+ The backend MUST never call any getgr* / getpw* function or
+ loops with winbind may happen.
+ ****************************************************************************/
+
+#if 0
+NTSTATUS is_mapped_group(BOOL *mapped, const DOM_SID *sid)
+{
+ NTSTATUS result;
+ gid_t id;
+
+ /* look if mapping exist, do not make idmap alloc an uid if SID is not found */
+ result = idmap_get_gid_from_sid(&id, sid, False);
+ if (NT_STATUS_IS_OK(result)) {
+ *mapped = gid_is_in_winbind_range(id);
+ } else {
+ *mapped = False;
+ }
+
+ return result;
+}
+#endif
+
+#define ALIAS_DEFAULT_SACL_SA_RIGHTS 0x01050013
+#define ALIAS_DEFAULT_DACL_SA_RIGHTS \
+ (READ_CONTROL_ACCESS | \
+ SA_RIGHT_ALIAS_LOOKUP_INFO | \
+ SA_RIGHT_ALIAS_GET_MEMBERS) /* 0x0002000c */
+
+#define ALIAS_DEFAULT_SACL_SEC_ACE_FLAG (SEC_ACE_FLAG_FAILED_ACCESS | SEC_ACE_FLAG_SUCCESSFUL_ACCESS) /* 0xc0 */
+
+
+NTSTATUS create_builtin_alias_default_sec_desc(SEC_DESC **sec_desc, TALLOC_CTX *ctx)
+{
+ DOM_SID *world = &global_sid_World;
+ DOM_SID *admins = &global_sid_Builtin_Administrators;
+ SEC_ACCESS sa;
+ SEC_ACE sacl_ace;
+ SEC_ACE dacl_aces[2];
+ SEC_ACL *sacl = NULL;
+ SEC_ACL *dacl = NULL;
+ size_t psize;
+
+ init_sec_access(&sa, ALIAS_DEFAULT_SACL_SA_RIGHTS);
+ init_sec_ace(&sacl_ace, world, SEC_ACE_TYPE_SYSTEM_AUDIT, sa, ALIAS_DEFAULT_SACL_SEC_ACE_FLAG);
+
+ sacl = make_sec_acl(ctx, NT4_ACL_REVISION, 1, &sacl_ace);
+ if (!sacl) {
+ DEBUG(0, ("build_init_sec_desc: Failed to make SEC_ACL.\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ init_sec_access(&sa, ALIAS_DEFAULT_DACL_SA_RIGHTS);
+ init_sec_ace(&(dacl_aces[0]), world, SEC_ACE_TYPE_ACCESS_ALLOWED, sa, 0);
+ init_sec_access(&sa, SA_RIGHT_ALIAS_ALL_ACCESS);
+ init_sec_ace(&(dacl_aces[1]), admins, SEC_ACE_TYPE_ACCESS_ALLOWED, sa, 0);
+
+ dacl = make_sec_acl(ctx, NT4_ACL_REVISION, 2, dacl_aces);
+ if (!sacl) {
+ DEBUG(0, ("build_init_sec_desc: Failed to make SEC_ACL.\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *sec_desc = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, admins, admins, sacl, dacl, &psize);
+ if (!(*sec_desc)) {
+ DEBUG(0,("get_share_security: Failed to make SEC_DESC.\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sec_desc_add_ace_to_dacl(SEC_DESC *sec_desc, TALLOC_CTX *ctx, DOM_SID *sid, uint32 mask)
+{
+ NTSTATUS result;
+ SEC_ACE *new_aces;
+ unsigned num_aces;
+ int i;
+
+ num_aces = sec_desc->dacl->num_aces + 1;
+ result = sec_ace_add_sid(ctx, &new_aces, sec_desc->dacl->ace, &num_aces, sid, mask);
+ if (NT_STATUS_IS_OK(result)) {
+ sec_desc->dacl->ace = new_aces;
+ sec_desc->dacl->num_aces = num_aces;
+ sec_desc->dacl->size = SEC_ACL_HEADER_SIZE;
+ for (i = 0; i < num_aces; i++) {
+ sec_desc->dacl->size += sec_desc->dacl->ace[i].size;
+ }
+ }
+ return result;
+}
+
+NTSTATUS gums_make_domain(DOM_SID *sid, const char *name, const char *description)
+{
+ NTSTATUS ret;
+ GUMS_OBJECT *go;
+ GUMS_FUNCTIONS *fns;
+
+ if (!NT_STATUS_IS_OK(ret = get_gums_fns(&fns)))
+ return ret;
+
+ if (!NT_STATUS_IS_OK(ret = gums_create_object(&go, GUMS_OBJ_DOMAIN)))
+ return ret;
+
+ ret = gums_set_object_sid(go, sid);
+ NTSTATUS_CHECK(ret, done, "gums_make_alias", "unable to set sid!");
+
+ ret = gums_set_object_name(go, name);
+ NTSTATUS_CHECK(ret, done, "gums_make_alias", "unable to set name!");
+
+ if (description) {
+ ret = gums_set_object_description(go, description);
+ NTSTATUS_CHECK(ret, done, "gums_make_alias", "unable to set description!");
+ }
+
+ /* make security descriptor * /
+ ret = create_builtin_alias_default_sec_desc(&((*go).sec_desc), (*go).mem_ctx);
+ NTSTATUS_CHECK(ret, error, "gums_init_backend", "create_builtin_alias_default_sec_desc");
+ */
+
+ ret = fns->set_object(go);
+
+ gums_destroy_object(&go);
+ return ret;
+}
+
+NTSTATUS gums_make_alias(DOM_SID *sid, const char *name, const char *description)
+{
+ NTSTATUS ret;
+ GUMS_OBJECT *go;
+ GUMS_FUNCTIONS *fns;
+
+ if (!NT_STATUS_IS_OK(ret = get_gums_fns(&fns)))
+ return ret;
+
+ if (!NT_STATUS_IS_OK(ret = gums_create_object(&go, GUMS_OBJ_ALIAS)))
+ return ret;
+
+ ret = gums_set_object_sid(go, sid);
+ NTSTATUS_CHECK(ret, done, "gums_make_alias", "unable to set sid!");
+
+ ret = gums_set_object_name(go, name);
+ NTSTATUS_CHECK(ret, done, "gums_make_alias", "unable to set name!");
+
+ if (description) {
+ ret = gums_set_object_description(go, description);
+ NTSTATUS_CHECK(ret, done, "gums_make_alias", "unable to set description!");
+ }
+
+ /* make security descriptor * /
+ ret = create_builtin_alias_default_sec_desc(&((*go).sec_desc), (*go).mem_ctx);
+ NTSTATUS_CHECK(ret, error, "gums_init_backend", "create_builtin_alias_default_sec_desc");
+ */
+
+ ret = fns->set_object(go);
+
+ gums_destroy_object(&go);
+ return ret;
+}
+
+NTSTATUS gums_init_domain(DOM_SID *sid, const char *name, const char * description)
+{
+ NTSTATUS ret;
+
+ /* Add the weelknown Builtin Domain */
+ if (!NT_STATUS_IS_OK(ret = gums_make_domain(
+ sid,
+ name,
+ description
+ ))) {
+ return ret;
+ }
+
+ /* Add default users and groups */
+ /* Administrator
+ Guest
+ Domain Administrators
+ Domain Users
+ Domain Guests
+ */
+
+ return ret;
+}
+
+NTSTATUS gums_init_builtin_domain(void)
+{
+ NTSTATUS ret;
+
+ generate_wellknown_sids();
+
+ /* Add the weelknown Builtin Domain */
+ if (!NT_STATUS_IS_OK(ret = gums_make_domain(
+ &global_sid_Builtin,
+ "BUILTIN",
+ "Builtin Domain"
+ ))) {
+ return ret;
+ }
+
+ /* Add the well known Builtin Local Groups */
+
+ /* Administrators */
+ if (!NT_STATUS_IS_OK(ret = gums_make_alias(
+ &global_sid_Builtin_Administrators,
+ "Administrators",
+ "Members can fully administer the computer/domain"
+ ))) {
+ return ret;
+ }
+ /* Administrator privilege set */
+ /* From BDC join trace:
+ SeSecurityPrivilege, SeBackupPrivilege, SeRestorePrivilege,
+ SeSystemtimePrivilege, SeShutdownPrivilege,
+ SeRemoteShutdownPrivilege, SeTakeOwnershipPrivilege,
+ SeDebugPrivilege, SeSystemEnvironmentPrivilege,
+ SeSystemProfilePrivilege, SeProfileSingleProcessPrivilege,
+ SeIncreaseBasePriorityPrivilege, SeLocalDriverPrivilege,
+ SeCreatePagefilePrivilege, SeIncreaseQuotaPrivilege
+ */
+
+ /* Power Users */
+ /* Domain Controllers Does NOT have Power Users (?) */
+ if (!NT_STATUS_IS_OK(ret = gums_make_alias(
+ &global_sid_Builtin_Power_Users,
+ "Power Users",
+ "Power Users"
+ ))) {
+ return ret;
+ }
+
+ /* Power Users privilege set */
+ /* (?) */
+
+ /* Account Operators */
+ if (!NT_STATUS_IS_OK(ret = gums_make_alias(
+ &global_sid_Builtin_Account_Operators,
+ "Account Operators",
+ "Members can administer domain user and group accounts"
+ ))) {
+ return ret;
+ }
+
+ /* make privilege set */
+ /* From BDC join trace:
+ SeShutdownPrivilege
+ */
+
+ /* Server Operators */
+ if (!NT_STATUS_IS_OK(ret = gums_make_alias(
+ &global_sid_Builtin_Server_Operators,
+ "Server Operators",
+ "Members can administer domain servers"
+ ))) {
+ return ret;
+ }
+
+ /* make privilege set */
+ /* From BDC join trace:
+ SeBackupPrivilege, SeRestorePrivilege, SeSystemtimePrivilege,
+ SeShutdownPrivilege, SeRemoteShutdownPrivilege
+ */
+
+ /* Print Operators */
+ if (!NT_STATUS_IS_OK(ret = gums_make_alias(
+ &global_sid_Builtin_Print_Operators,
+ "Print Operators",
+ "Members can administer domain printers"
+ ))) {
+ return ret;
+ }
+
+ /* make privilege set */
+ /* From BDC join trace:
+ SeShutdownPrivilege
+ */
+
+ /* Backup Operators */
+ if (!NT_STATUS_IS_OK(ret = gums_make_alias(
+ &global_sid_Builtin_Backup_Operators,
+ "Backup Operators",
+ "Members can bypass file security to backup files"
+ ))) {
+ return ret;
+ }
+
+ /* make privilege set */
+ /* From BDC join trace:
+ SeBackupPrivilege, SeRestorePrivilege, SeShutdownPrivilege
+ */
+
+ /* Replicator */
+ if (!NT_STATUS_IS_OK(ret = gums_make_alias(
+ &global_sid_Builtin_Replicator,
+ "Replicator",
+ "Supports file replication in a domain"
+ ))) {
+ return ret;
+ }
+
+ /* make privilege set */
+ /* From BDC join trace:
+ SeBackupPrivilege, SeRestorePrivilege, SeShutdownPrivilege
+ */
+
+ /* Users */
+ if (!NT_STATUS_IS_OK(ret = gums_make_alias(
+ &global_sid_Builtin_Users,
+ "Users",
+ "Ordinary users"
+ ))) {
+ return ret;
+ }
+
+ /* Users specific ACEs * /
+ sec_desc_add_ace_to_dacl(go->sec_desc, go->mem_ctx, &global_sid_Builtin_Account_Operators, ALIAS_DEFAULT_DACL_SA_RIGHTS);
+ sec_desc_add_ace_to_dacl(go->sec_desc, go->mem_ctx, &global_sid_Builtin_Power_Users, ALIAS_DEFAULT_DACL_SA_RIGHTS);
+ */
+
+ /* Guests */
+ if (!NT_STATUS_IS_OK(ret = gums_make_alias(
+ &global_sid_Builtin_Guests,
+ "Guests",
+ "Users granted guest access to the computer/domain"
+ ))) {
+ return ret;
+ }
+
+ return ret;
+}
+
diff --git a/source/sam/gums_tdbsam2.c b/source/sam/gums_tdbsam2.c
new file mode 100644
index 00000000000..7fb9a1a997f
--- /dev/null
+++ b/source/sam/gums_tdbsam2.c
@@ -0,0 +1,1220 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * tdbsam2 - sam backend
+ * Copyright (C) Simo Sorce 2002-2003
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 675
+ * Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+#include "tdbsam2_parse_info.h"
+
+#if 0
+static int gums_tdbsam2_debug_class = DBGC_ALL;
+#endif
+/*
+#undef DBGC_CLASS
+#define DBGC_CLASS gums_tdbsam2_debug_class
+*/
+
+#define TDBSAM_VERSION 20021215
+#define TDB_FILE_NAME "tdbsam2.tdb"
+#define NAMEPREFIX "NAME_"
+#define SIDPREFIX "SID_"
+#define PRIVILEGEPREFIX "PRIV_"
+
+#define TDB_BASIC_OBJ_STRING "ddd"
+#define TDB_FORMAT_STRING "dddB"
+#define TDB_PRIV_FORMAT_STRING "ddB"
+
+#define TALLOC_CHECK(ptr, err, label) do { if ((ptr) == NULL) { DEBUG(0, ("%s: Out of memory!\n", FUNCTION_MACRO)); err = NT_STATUS_NO_MEMORY; goto label; } } while(0)
+#define SET_OR_FAIL(func, label) do { if (!NT_STATUS_IS_OK(func)) { DEBUG(0, ("%s: Setting gums object data failed!\n", FUNCTION_MACRO)); goto label; } } while(0)
+
+
+
+struct tdbsam2_enum_objs {
+ uint32 type;
+ DOM_SID *dom_sid;
+ TDB_CONTEXT *db;
+ TDB_DATA key;
+ struct tdbsam2_enum_objs *next;
+};
+
+struct tdbsam2_private_data {
+
+ const char *storage;
+ struct tdbsam2_enum_objs *teo_handlers;
+};
+
+static struct tdbsam2_private_data *ts2_privs;
+
+static NTSTATUS init_object_from_buffer(GUMS_OBJECT **go, char *buffer, int size)
+{
+
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ TALLOC_CTX *mem_ctx;
+ int iret;
+ char *obj_data = NULL;
+ int data_size = 0;
+ int version, type, seqnum;
+ int len;
+
+ mem_ctx = talloc_init("init_object_from_buffer");
+ if (!mem_ctx) {
+ DEBUG(0, ("init_object_from_buffer: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ len = tdb_unpack (buffer, size, TDB_FORMAT_STRING,
+ &version,
+ &type,
+ &seqnum,
+ &data_size, &obj_data);
+
+ if (len == -1 || data_size <= 0)
+ goto done;
+
+ /* version is checked inside this function so that backward
+ compatibility code can be called eventually.
+ This way we can easily handle database format upgrades */
+ if (version != TDBSAM_VERSION) {
+ DEBUG(3,("init_object_from_buffer: Error, db object has wrong tdbsam version!\n"));
+ goto done;
+ }
+
+ /* be sure the string is terminated before trying to parse it */
+ if (obj_data[data_size - 1] != '\0')
+ obj_data[data_size - 1] = '\0';
+
+ *go = (GUMS_OBJECT *)talloc_zero(mem_ctx, sizeof(GUMS_OBJECT));
+ TALLOC_CHECK(*go, ret, done);
+
+ switch (type) {
+
+ case GUMS_OBJ_DOMAIN:
+ iret = gen_parse(mem_ctx, pinfo_gums_domain, (char *)(*go), obj_data);
+ break;
+
+ case GUMS_OBJ_GROUP:
+ case GUMS_OBJ_ALIAS:
+ iret = gen_parse(mem_ctx, pinfo_gums_group, (char *)(*go), obj_data);
+ break;
+
+ case GUMS_OBJ_NORMAL_USER:
+ iret = gen_parse(mem_ctx, pinfo_gums_user, (char *)(*go), obj_data);
+ break;
+
+ default:
+ DEBUG(3,("init_object_from_buffer: Error, wrong object type number!\n"));
+ goto done;
+ }
+
+ if (iret != 0) {
+ DEBUG(0, ("init_object_from_buffer: Fatal Error! Unable to parse object!\n"));
+ DEBUG(0, ("init_object_from_buffer: DB Corrupt ?"));
+ goto done;
+ }
+
+ (*go)->mem_ctx = mem_ctx;
+
+ ret = NT_STATUS_OK;
+done:
+ SAFE_FREE(obj_data);
+ return ret;
+}
+
+static NTSTATUS init_privilege_from_buffer(GUMS_PRIVILEGE **priv, char *buffer, int size)
+{
+
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ TALLOC_CTX *mem_ctx;
+ int iret;
+ char *obj_data = NULL;
+ int data_size = 0;
+ int version, seqnum;
+ int len;
+
+ mem_ctx = talloc_init("init_privilege_from_buffer");
+ if (!mem_ctx) {
+ DEBUG(0, ("init_privilege_from_buffer: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ len = tdb_unpack (buffer, size, TDB_PRIV_FORMAT_STRING,
+ &version,
+ &seqnum,
+ &data_size, &obj_data);
+
+ if (len == -1 || data_size <= 0)
+ goto done;
+
+ /* version is checked inside this function so that backward
+ compatibility code can be called eventually.
+ This way we can easily handle database format upgrades */
+ if (version != TDBSAM_VERSION) {
+ DEBUG(3,("init_privilege_from_buffer: Error, db object has wrong tdbsam version!\n"));
+ goto done;
+ }
+
+ /* be sure the string is terminated before trying to parse it */
+ if (obj_data[data_size - 1] != '\0')
+ obj_data[data_size - 1] = '\0';
+
+ *priv = (GUMS_PRIVILEGE *)talloc_zero(mem_ctx, sizeof(GUMS_PRIVILEGE));
+ TALLOC_CHECK(*priv, ret, done);
+
+ iret = gen_parse(mem_ctx, pinfo_gums_privilege, (char *)(*priv), obj_data);
+
+ if (iret != 0) {
+ DEBUG(0, ("init_privilege_from_buffer: Fatal Error! Unable to parse object!\n"));
+ DEBUG(0, ("init_privilege_from_buffer: DB Corrupt ?"));
+ goto done;
+ }
+
+ (*priv)->mem_ctx = mem_ctx;
+
+ ret = NT_STATUS_OK;
+done:
+ SAFE_FREE(obj_data);
+ return ret;
+}
+
+static NTSTATUS init_buffer_from_object(char **buffer, size_t *len, TALLOC_CTX *mem_ctx, GUMS_OBJECT *object)
+{
+
+ NTSTATUS ret;
+ char *genbuf = NULL;
+ size_t buflen;
+
+ if (!buffer)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ switch (gums_get_object_type(object)) {
+
+ case GUMS_OBJ_DOMAIN:
+ genbuf = gen_dump(mem_ctx, pinfo_gums_domain, (char *)object, 0);
+ break;
+
+ case GUMS_OBJ_GROUP:
+ case GUMS_OBJ_ALIAS:
+ genbuf = gen_dump(mem_ctx, pinfo_gums_group, (char *)object, 0);
+ break;
+
+ case GUMS_OBJ_NORMAL_USER:
+ genbuf = gen_dump(mem_ctx, pinfo_gums_user, (char *)object, 0);
+ break;
+
+ default:
+ DEBUG(3,("init_buffer_from_object: Error, wrong object type number!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (genbuf == NULL) {
+ DEBUG(0, ("init_buffer_from_object: Fatal Error! Unable to dump object!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ buflen = tdb_pack(NULL, 0, TDB_FORMAT_STRING,
+ TDBSAM_VERSION,
+ object->type,
+ object->seq_num,
+ strlen(genbuf) + 1, genbuf);
+
+ *buffer = talloc(mem_ctx, buflen);
+ TALLOC_CHECK(*buffer, ret, done);
+
+ *len = tdb_pack(*buffer, buflen, TDB_FORMAT_STRING,
+ TDBSAM_VERSION,
+ object->type,
+ object->seq_num,
+ strlen(genbuf) + 1, genbuf);
+
+ if (*len != buflen) {
+ DEBUG(0, ("init_buffer_from_object: something odd is going on here: bufflen (%d) != len (%d) in tdb_pack operations!\n",
+ buflen, *len));
+ *buffer = NULL;
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ ret = NT_STATUS_OK;
+done:
+ return ret;
+}
+
+static NTSTATUS init_buffer_from_privilege(char **buffer, size_t *len, TALLOC_CTX *mem_ctx, GUMS_PRIVILEGE *priv)
+{
+
+ NTSTATUS ret;
+ char *genbuf = NULL;
+ size_t buflen;
+
+ if (!buffer || !len || !mem_ctx || !priv)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ genbuf = gen_dump(mem_ctx, pinfo_gums_privilege, (char *)priv, 0);
+
+ if (genbuf == NULL) {
+ DEBUG(0, ("init_buffer_from_privilege: Fatal Error! Unable to dump object!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ buflen = tdb_pack(NULL, 0, TDB_PRIV_FORMAT_STRING,
+ TDBSAM_VERSION,
+ priv->seq_num,
+ strlen(genbuf) + 1, genbuf);
+
+ *buffer = talloc(mem_ctx, buflen);
+ TALLOC_CHECK(*buffer, ret, done);
+
+ *len = tdb_pack(*buffer, buflen, TDB_PRIV_FORMAT_STRING,
+ TDBSAM_VERSION,
+ priv->seq_num,
+ strlen(genbuf) + 1, genbuf);
+
+ if (*len != buflen) {
+ DEBUG(0, ("init_buffer_from_privilege: something odd is going on here: bufflen (%d) != len (%d) in tdb_pack operations!\n",
+ buflen, *len));
+ *buffer = NULL;
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ ret = NT_STATUS_OK;
+done:
+ return ret;
+}
+
+static NTSTATUS opentdb(TDB_CONTEXT **tdb, BOOL readonly)
+{
+ if (!tdb)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ *tdb = tdb_open_log(ts2_privs->storage, 0, TDB_DEFAULT, readonly?(O_RDONLY):(O_RDWR | O_CREAT), 0600);
+ if (!(*tdb))
+ {
+ DEBUG(0, ("opentdb: Unable to open database (%s)!\n", ts2_privs->storage));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS get_object_by_sid(TDB_CONTEXT *tdb, GUMS_OBJECT **obj, const DOM_SID *sid)
+{
+ NTSTATUS ret;
+ TDB_DATA data, key;
+ fstring keystr;
+
+ if (!obj || !sid)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", SIDPREFIX, sid_string_static(sid));
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ data = tdb_fetch(tdb, key);
+ if (!data.dptr) {
+ DEBUG(5, ("get_object_by_sid: Entry not found!\n"));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdb)));
+ DEBUGADD(5, (" Key: %s\n", keystr));
+ ret = NT_STATUS_NOT_FOUND;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(init_object_from_buffer(obj, data.dptr, data.dsize))) {
+ DEBUG(0, ("get_object_by_sid: Error fetching database, malformed entry!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ ret = NT_STATUS_OK;
+
+done:
+ SAFE_FREE(data.dptr);
+ return ret;
+}
+
+static NTSTATUS make_full_object_name(TDB_CONTEXT *tdb, fstring objname, GUMS_OBJECT *object)
+{
+ NTSTATUS ret;
+
+ objname[0] = '\0';
+
+ if (gums_get_object_type(object) == GUMS_OBJ_DOMAIN) {
+
+ fstrcpy(objname, gums_get_object_name(object));
+
+ } else {
+ GUMS_OBJECT *domain_object;
+ DOM_SID domain_sid;
+ uint32 *discard_rid;
+
+ sid_copy(&domain_sid, gums_get_object_sid(object));
+ sid_split_rid(&domain_sid, discard_rid);
+
+ if (!NT_STATUS_IS_OK(get_object_by_sid(tdb,
+ &domain_object,
+ &domain_sid))) {
+
+ DEBUG(3, ("Object's domain not found!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ fstrcpy(objname, gums_get_object_name(domain_object));
+ fstrcat(objname, "\\");
+ fstrcat(objname, gums_get_object_name(object));
+ }
+
+ ret = NT_STATUS_OK;
+
+done:
+ return ret;
+}
+
+/* name should be in DOMAIN\NAME format */
+static NTSTATUS get_object_by_name(TDB_CONTEXT *tdb, GUMS_OBJECT **obj, const char *fullname)
+{
+
+ NTSTATUS ret = NT_STATUS_OK;
+ TDB_DATA data, key;
+ fstring keystr;
+ fstring objname;
+ DOM_SID sid;
+ fstring sidstr;
+ int sidstr_len;
+
+ if (!obj || !fullname)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ /* Data is stored in all lower-case */
+ fstrcpy(objname, fullname);
+ strlower_m(objname);
+
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", NAMEPREFIX, objname);
+
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ data = tdb_fetch(tdb, key);
+ if (!data.dptr) {
+ DEBUG(5, ("get_object_by_name: Entry not found!\n"));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdb)));
+ DEBUGADD(5, (" Key: %s\n", keystr));
+ ret = NT_STATUS_NOT_FOUND;
+ goto done;
+ }
+
+ fstrcpy(sidstr, data.dptr);
+ sidstr_len = data.dsize;
+
+ SAFE_FREE(data.dptr);
+
+ if (sidstr_len <= 0) {
+ DEBUG(5, ("get_object_by_name: Error unpacking database object!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!string_to_sid(&sid, sidstr)) {
+ DEBUG(5, ("get_object_by_name: Error invalid sid string found in database object!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+done:
+ if (NT_STATUS_IS_OK(ret))
+ return get_object_by_sid(tdb, obj, &sid);
+ return ret;
+}
+
+/* Get object's sequence number */
+
+static NTSTATUS get_object_seq_num(TDB_CONTEXT *tdb, GUMS_OBJECT *object, int *seq_num)
+{
+
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ TDB_DATA data, key;
+ fstring keystr;
+ fstring sidstr;
+ int version, type, seqnum;
+
+ if (!object || !seq_num)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ fstrcpy(sidstr, sid_string_static(gums_get_object_sid(object)));
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", SIDPREFIX, sidstr);
+
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ data = tdb_fetch(tdb, key);
+ if (!data.dptr) {
+ DEBUG(5, ("get_object_seq_num: Entry not found!\n"));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdb)));
+ DEBUGADD(5, (" Key: %s\n", keystr));
+ ret = NT_STATUS_NOT_FOUND;
+ goto done;
+ }
+
+ if (tdb_unpack (data.dptr, data.dsize, TDB_BASIC_OBJ_STRING, &version, &type, &seqnum) == -1)
+ goto done;
+
+ *seq_num = seqnum;
+ ret = NT_STATUS_OK;
+
+done:
+ SAFE_FREE(data.dptr);
+ return ret;
+}
+
+/* store a gums object
+ * flag: TDB_REPLACE or TDB_MODIFY or TDB_INSERT
+ */
+
+static NTSTATUS store_object(TDB_CONTEXT *tdb, GUMS_OBJECT *object, int flag)
+{
+ NTSTATUS ret = NT_STATUS_OK;
+ TDB_DATA data, data2, key, key2;
+ TALLOC_CTX *mem_ctx;
+ fstring keystr;
+ fstring sidstr;
+ fstring namestr;
+ fstring objname;
+ int r;
+
+ /* TODO: on object renaming/replacing this function should
+ * check name->sid record and delete the old one
+ */
+
+ mem_ctx = talloc_init("store_object");
+ if (!mem_ctx) {
+ DEBUG(0, ("store_object: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ make_full_object_name(tdb, objname, object);
+
+ /* Data is stored in all lower-case */
+ strlower_m(objname);
+
+ if (flag == TDB_MODIFY) {
+ if (!NT_STATUS_IS_OK(ret = get_object_seq_num(tdb, object, &(object->seq_num))))
+ goto done;
+ object->seq_num += 1;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = init_buffer_from_object(&(data.dptr), &(data.dsize), mem_ctx, object)))
+ goto done;
+
+ fstrcpy(sidstr, sid_string_static(gums_get_object_sid(object)));
+ slprintf(keystr, sizeof(keystr) - 1, "%s%s", SIDPREFIX, sidstr);
+ slprintf(namestr, sizeof(namestr) - 1, "%s%s", NAMEPREFIX, objname);
+
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ if ((r = tdb_store(tdb, key, data, flag)) != TDB_SUCCESS) {
+ DEBUG(0, ("store_object: Unable to modify TDBSAM!\n"));
+ DEBUGADD(0, (" Error: %s", tdb_errorstr(tdb)));
+ DEBUGADD(0, (" occured while storing sid record (%s)\n", keystr));
+ if (r == TDB_ERR_EXISTS)
+ ret = NT_STATUS_UNSUCCESSFUL;
+ else
+ ret = NT_STATUS_INTERNAL_DB_ERROR;
+ goto done;
+ }
+
+ data2.dptr = sidstr;
+ data2.dsize = strlen(sidstr) + 1;
+ key2.dptr = namestr;
+ key2.dsize = strlen(namestr) + 1;
+
+ if ((r = tdb_store(tdb, key2, data2, flag)) != TDB_SUCCESS) {
+ DEBUG(0, ("store_object: Unable to modify TDBSAM!\n"));
+ DEBUGADD(0, (" Error: %s", tdb_errorstr(tdb)));
+ DEBUGADD(0, (" occured while storing name record (%s)\n", keystr));
+ DEBUGADD(0, (" attempting rollback operation.\n"));
+ if ((tdb_delete(tdb, key)) != TDB_SUCCESS) {
+ DEBUG(0, ("store_object: Unable to rollback! Check database consitency!\n"));
+ }
+ if (r == TDB_ERR_EXISTS)
+ ret = NT_STATUS_UNSUCCESSFUL;
+ else
+ ret = NT_STATUS_INTERNAL_DB_ERROR;
+ goto done;
+ }
+
+/* TODO: update the general database counter */
+/* TODO: update this entry counter too */
+
+done:
+ talloc_destroy(mem_ctx);
+ return ret;
+}
+
+/* GUMM object functions */
+
+static NTSTATUS tdbsam2_get_domain_sid(DOM_SID *sid, const char* name)
+{
+
+ NTSTATUS ret;
+ TDB_CONTEXT *tdb;
+ GUMS_OBJECT *go;
+ fstring domname;
+
+ if (!sid || !name)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = opentdb(&tdb, True))) {
+ return ret;
+ }
+
+ /* Data is stored in all lower-case */
+ fstrcpy(domname, name);
+ strlower_m(domname);
+
+ if (!NT_STATUS_IS_OK(ret = get_object_by_name(tdb, &go, domname))) {
+ go = NULL;
+ DEBUG(0, ("tdbsam2_get_domain_sid: Error fetching database!\n"));
+ goto done;
+ }
+
+ if (gums_get_object_type(go) != GUMS_OBJ_DOMAIN) {
+ DEBUG(5, ("tdbsam2_get_domain_sid: Requested object is not a domain!\n"));
+ ret = NT_STATUS_OBJECT_TYPE_MISMATCH;
+ goto done;
+ }
+
+ sid_copy(sid, gums_get_object_sid(go));
+
+ ret = NT_STATUS_OK;
+
+done:
+ if (go)
+ gums_destroy_object(&go);
+ tdb_close(tdb);
+ return ret;
+}
+
+static NTSTATUS get_next_sid(TDB_CONTEXT *tdb, DOM_SID *sid)
+{
+ NTSTATUS ret;
+ GUMS_OBJECT *go;
+ DOM_SID dom_sid;
+ TDB_DATA dom_sid_key;
+ fstring dom_sid_str;
+ uint32 new_rid;
+
+ /* Find the domain SID */
+ if (!NT_STATUS_IS_OK(tdbsam2_get_domain_sid(&dom_sid, global_myname()))) {
+ DEBUG(0, ("get_next_sid: cannot found the domain sid!!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Lock the domain record */
+ sid_to_string(dom_sid_str, &dom_sid);
+ dom_sid_key.dptr = dom_sid_str;
+ dom_sid_key.dsize = strlen(dom_sid_key.dptr) + 1;
+
+ if(tdb_chainlock(tdb, dom_sid_key) != 0) {
+ DEBUG(0, ("get_next_sid: unable to lock domain record!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Get the domain object */
+ ret = get_object_by_sid(tdb, &go, &dom_sid);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0, ("get_next_sid: unable to get root Domain object!\n"));
+ ret = NT_STATUS_INTERNAL_DB_ERROR;
+ goto done;
+ }
+
+ new_rid = gums_get_domain_next_rid(go);
+
+ /* Increment the RID Counter */
+ gums_set_domain_next_rid(go, new_rid+1);
+
+ /* Store back Domain object */
+ ret = store_object(tdb, go, TDB_MODIFY);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0, ("get_next_sid: unable to update root Domain object!\n"));
+ ret = NT_STATUS_INTERNAL_DB_ERROR;
+ goto done;
+ }
+
+ /* Build the Domain SID to return */
+ sid_copy(sid, &dom_sid);
+
+ if (!sid_append_rid(sid, new_rid)) {
+ DEBUG(0, ("get_next_sid: unable to build new SID !?!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ ret = NT_STATUS_OK;
+
+done:
+ /* Unlock the Domain object */
+ tdb_chainunlock(tdb, dom_sid_key);
+
+ return ret;
+}
+
+/* TODO */
+ NTSTATUS (*get_sequence_number) (void);
+
+
+extern DOM_SID global_sid_NULL;
+
+static NTSTATUS tdbsam2_new_object(DOM_SID *sid, const char *name, const int obj_type)
+{
+
+ NTSTATUS ret = NT_STATUS_OK;
+ TDB_CONTEXT *tdb;
+ GUMS_OBJECT *go;
+ NTTIME null_time;
+ DATA_BLOB pw;
+ const char *defpw = "NOPASSWORDXXXXXX";
+ uint8 defhours[21] = {255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255};
+
+ if (!name) {
+ DEBUG(0, ("tdbsam2_new_object: no NULL pointers are accepted here!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = opentdb(&tdb, False))) {
+ return ret;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = gums_create_object(&go, obj_type))) {
+ go = NULL;
+ goto done;
+ }
+
+ if (obj_type == GUMS_OBJ_DOMAIN) {
+ sid_copy(sid, get_global_sam_sid());
+ } else {
+ if (!NT_STATUS_IS_OK(ret = get_next_sid(tdb, sid)))
+ goto done;
+ }
+
+ gums_set_object_sid(go, sid);
+ gums_set_object_name(go, name);
+ gums_set_object_seq_num(go, 1);
+
+ /*obj.domain->sec_desc*/
+
+ switch (obj_type) {
+ case GUMS_OBJ_NORMAL_USER:
+
+ init_nt_time(&null_time);
+
+ gums_set_user_logon_time(go, null_time);
+ gums_set_user_logoff_time(go, null_time);
+ gums_set_user_kickoff_time(go, null_time);
+ gums_set_user_pass_last_set_time(go, null_time);
+ gums_set_user_pass_can_change_time(go, null_time);
+ gums_set_user_pass_must_change_time(go, null_time);
+
+ pw = data_blob(defpw, NT_HASH_LEN);
+ gums_set_user_nt_pwd(go, pw);
+ gums_set_user_lm_pwd(go, pw);
+ data_blob_free(&pw);
+
+ gums_set_user_logon_divs(go, 168);
+ gums_set_user_hours(go, 21, defhours);
+
+ gums_set_user_bad_password_count(go, 0);
+ gums_set_user_logon_count(go, 0);
+ gums_set_user_unknown_6(go, 0x000004ec);
+ break;
+
+ case GUMS_OBJ_GROUP:
+ case GUMS_OBJ_ALIAS:
+
+ break;
+
+ case GUMS_OBJ_DOMAIN:
+
+ gums_set_domain_next_rid(go, 0x3e9);
+
+ break;
+
+ default:
+ ret = NT_STATUS_OBJECT_TYPE_MISMATCH;
+ goto done;
+ }
+
+ ret = store_object(tdb, go, TDB_INSERT);
+
+done:
+ if (go)
+ gums_destroy_object(&go);
+ tdb_close(tdb);
+ return ret;
+}
+
+/* TODO: handle privileges objects */
+
+static NTSTATUS tdbsam2_delete_object(const DOM_SID *sid)
+{
+ /* TODO: need to address privilege deletion */
+ NTSTATUS ret = NT_STATUS_OK;
+ TDB_CONTEXT *tdb;
+ GUMS_OBJECT *go;
+ TDB_DATA data, key;
+ fstring keystr;
+
+ if (!sid) {
+ DEBUG(0, ("tdbsam2_delete_object: no NULL pointers are accepted here!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = opentdb(&tdb, False))) {
+ return ret;
+ }
+
+ slprintf(keystr, sizeof(keystr) - 1, "%s%s", SIDPREFIX, sid_string_static(sid));
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ data = tdb_fetch(tdb, key);
+ if (!data.dptr) {
+ DEBUG(5, ("tdbsam2_delete_object: Error fetching database, SID entry not found!\n"));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdb)));
+ DEBUGADD(5, (" Key: %s\n", keystr));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (tdb_delete(tdb, key) != TDB_SUCCESS) {
+ DEBUG(5, ("tdbsam2_delete_object: Error deleting object!\n"));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdb)));
+ DEBUGADD(5, (" Key: %s\n", keystr));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(init_object_from_buffer(&go, data.dptr, data.dsize))) {
+ DEBUG(0, ("tdbsam2_delete_object: Error fetching database, malformed entry!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ slprintf(keystr, sizeof(keystr) - 1, "%s%s", NAMEPREFIX, gums_get_object_name(go));
+
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ if (tdb_delete(tdb, key) != TDB_SUCCESS) {
+ DEBUG(5, ("tdbsam2_delete_object: Error deleting object!\n"));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdb)));
+ DEBUGADD(5, (" Key: %s\n", keystr));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+/* TODO: update the general database counter */
+
+done:
+ gums_destroy_object(&go);
+ SAFE_FREE(data.dptr);
+ return ret;
+}
+
+static NTSTATUS tdbsam2_get_object_from_sid(GUMS_OBJECT **object, const DOM_SID *sid, const int obj_type)
+{
+ NTSTATUS ret;
+ TDB_CONTEXT *tdb;
+
+ if (!object || !sid) {
+ DEBUG(0, ("tdbsam2_get_object_from_sid: no NULL pointers are accepted here!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = opentdb(&tdb, True))) {
+ return ret;
+ }
+
+ ret = get_object_by_sid(tdb, object, sid);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0, ("tdbsam2_get_object_from_sid: %s\n", nt_errstr(ret)));
+ goto error;
+ }
+ if (obj_type && gums_get_object_type(*object) != obj_type) {
+ DEBUG(0, ("tdbsam2_get_object_from_sid: the object is not of the rerquested type!\n"));
+ goto error;
+ }
+
+ tdb_close(tdb);
+ return NT_STATUS_OK;
+
+error:
+ gums_destroy_object(object);
+ tdb_close(tdb);
+ return ret;
+}
+
+static NTSTATUS tdbsam2_get_object_from_name(GUMS_OBJECT **object, const char *domain, const char *name, const int obj_type)
+{
+ NTSTATUS ret;
+ TDB_CONTEXT *tdb;
+ fstring objname;
+
+ if (!object || !name) {
+ DEBUG(0, ("tdbsam2_get_object_from_name: no NULL pointers are accepted here!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = opentdb(&tdb, True))) {
+ return ret;
+ }
+
+ if (obj_type == GUMS_OBJ_DOMAIN) {
+ fstrcpy(objname, name);
+ } else {
+ if (!domain) {
+ domain = global_myname();
+ }
+ fstrcpy(objname, domain);
+ fstrcat(objname, "\\");
+ fstrcat(objname, name);
+ }
+
+ *object = NULL;
+ ret = get_object_by_name(tdb, object, name);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0, ("tdbsam2_get_object_from_name: %s\n", nt_errstr(ret)));
+ goto error;
+ }
+ if (obj_type && gums_get_object_type(*object) != obj_type) {
+ DEBUG(0, ("tdbsam2_get_object_from_name: the object is not of the rerquested type!\n"));
+ goto error;
+ }
+
+ tdb_close(tdb);
+ return NT_STATUS_OK;
+
+error:
+ gums_destroy_object(object);
+ tdb_close(tdb);
+ return ret;
+}
+
+ /* This function is used to get the list of all objects changed since base_time, it is
+ used to support PDC<->BDC synchronization */
+ NTSTATUS (*get_updated_objects) (GUMS_OBJECT **objects, const NTTIME base_time);
+
+static NTSTATUS tdbsam2_enumerate_objects_start(void **handle, const DOM_SID *sid, const int obj_type)
+{
+ struct tdbsam2_enum_objs *teo, *t;
+
+ teo = (struct tdbsam2_enum_objs *)malloc(sizeof(struct tdbsam2_enum_objs));
+ if (!teo) {
+ DEBUG(0, ("tdbsam2_enumerate_objects_start: Out of Memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ memset(teo, 0, sizeof(struct tdbsam2_enum_objs));
+
+ teo->type = obj_type;
+ if (sid) {
+ teo->dom_sid = (DOM_SID *)malloc(sizeof(DOM_SID));
+ if (!teo->dom_sid) {
+ DEBUG(0, ("tdbsam2_enumerate_objects_start: Out of Memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ sid_copy(teo->dom_sid, sid);
+ }
+
+ if (!NT_STATUS_IS_OK(opentdb(&(teo->db), True)))
+ {
+ DEBUG(0, ("tdbsam2_enumerate_objects_start: Unable to open database (%s)!\n", ts2_privs->storage));
+ SAFE_FREE(teo);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!ts2_privs->teo_handlers) {
+ ts2_privs->teo_handlers = teo;
+ } else {
+ t = ts2_privs->teo_handlers;
+ while (t->next) {
+ t = t->next;
+ }
+ t->next = teo;
+ }
+
+ *handle = teo;
+
+ teo->key = tdb_firstkey(teo->db);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS tdbsam2_enumerate_objects_get_next(GUMS_OBJECT **object, void *handle)
+{
+ NTSTATUS ret;
+ TDB_DATA data;
+ struct tdbsam2_enum_objs *teo;
+ const char *prefix = SIDPREFIX;
+ const int preflen = strlen(prefix);
+ fstring dom_sid_str;
+ int dom_sid_str_len = 0;
+
+ if (!object || !handle) {
+ DEBUG(0, ("tdbsam2_get_object_from_sid: no NULL pointers are accepted here!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ teo = (struct tdbsam2_enum_objs *)handle;
+
+ if (teo->dom_sid) {
+ sid_to_string(dom_sid_str, teo->dom_sid);
+ dom_sid_str_len = strlen(dom_sid_str);
+ }
+
+ while ((teo->key.dptr != NULL)) {
+ int len, version, type, size, seqnum;
+ char *ptr;
+
+ if (strncmp(teo->key.dptr, prefix, preflen)) {
+ teo->key = tdb_nextkey(teo->db, teo->key);
+ continue;
+ }
+
+ if (dom_sid_str_len != 0) {
+ if (strncmp(&(teo->key.dptr[preflen]), dom_sid_str, dom_sid_str_len)) {
+ teo->key = tdb_nextkey(teo->db, teo->key);
+ continue;
+ }
+ }
+
+ data = tdb_fetch(teo->db, teo->key);
+ if (!data.dptr) {
+ DEBUG(5, ("tdbsam2_enumerate_objects_get_next: Error fetching database, SID entry not found!\n"));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(teo->db)));
+ DEBUGADD(5, (" Key: %s\n", teo->key.dptr));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ len = tdb_unpack (data.dptr, data.dsize, TDB_FORMAT_STRING,
+ &version,
+ &type,
+ &seqnum,
+ &size, &ptr);
+
+ if (len == -1) {
+ DEBUG(5, ("tdbsam2_enumerate_objects_get_next: Error unable to unpack data!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ SAFE_FREE(ptr);
+
+ if (teo->type && type != teo->type) {
+ SAFE_FREE(data.dptr);
+ data.dsize = 0;
+ teo->key = tdb_nextkey(teo->db, teo->key);
+ continue;
+ }
+
+ break;
+ }
+
+ if (teo->key.dptr == NULL) { /* no more objs */
+ ret = NT_STATUS_NO_MORE_ENTRIES;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = init_object_from_buffer(object, data.dptr, data.dsize))) {
+ SAFE_FREE(data.dptr);
+ DEBUG(0, ("tdbsam2_enumerate_objects_get_next: Error fetching database, malformed entry!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ SAFE_FREE(data.dptr);
+
+ /* prepare next run */
+ teo->key = tdb_nextkey(teo->db, teo->key);
+
+done:
+ return ret;
+}
+
+static NTSTATUS tdbsam2_enumerate_objects_stop(void *handle)
+{
+ struct tdbsam2_enum_objs *teo, *t, *p;
+
+ teo = (struct tdbsam2_enum_objs *)handle;
+
+ if (ts2_privs->teo_handlers == teo) {
+ ts2_privs->teo_handlers = teo->next;
+ } else {
+ t = ts2_privs->teo_handlers;
+ while (t != teo) {
+ p = t;
+ t = t->next;
+ if (t == NULL) {
+ DEBUG(0, ("tdbsam2_enumerate_objects_stop: Error, handle not found!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+ p = t->next;
+ }
+
+ tdb_close(teo->db);
+ SAFE_FREE(teo->dom_sid);
+ SAFE_FREE(teo);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS tdbsam2_set_object(GUMS_OBJECT *go)
+{
+ NTSTATUS ret;
+ TDB_CONTEXT *tdb;
+
+ if (!go)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = opentdb(&tdb, False))) {
+ return ret;
+ }
+
+ ret = store_object(tdb, go, TDB_REPLACE);
+
+ tdb_close(tdb);
+ return ret;
+}
+
+#if 0
+ /* set object values function */
+static NTSTATUS (*set_object_values) (DOM_SID *sid, uint32 count, GUMS_DATA_SET *data_set);
+
+ /* Group related functions */
+static NTSTATUS (*add_memberss_to_group) (const DOM_SID *group, const DOM_SID **members);
+ NTSTATUS (*delete_members_from_group) (const DOM_SID *group, const DOM_SID **members);
+static NTSTATUS (*enumerate_group_members) (DOM_SID **members, const DOM_SID *sid, const int type);
+
+static NTSTATUS (*get_sid_groups) (DOM_SID **groups, const DOM_SID *sid);
+
+static NTSTATUS (*lock_sid) (const DOM_SID *sid);
+static NTSTATUS (*unlock_sid) (const DOM_SID *sid);
+
+ /* privileges related functions */
+
+static NTSTATUS (*get_privilege) (GUMS_OBJECT **object, const char *name);
+static NTSTATUS (*add_members_to_privilege) (const char *name, const DOM_SID **members);
+static NTSTATUS (*delete_members_from_privilege) (const char *name, const DOM_SID **members);
+static NTSTATUS (*enumerate_privilege_members) (const char *name, DOM_SID **members);
+static NTSTATUS (*get_sid_privileges) (const DOM_SID *sid, const char **privs);
+
+ /* warning!: set_privilege will overwrite a prior existing privilege if such exist */
+static NTSTATUS (*set_privilege) (GUMS_PRIVILEGE *priv);
+#endif
+
+static void free_tdbsam2_private_data(void **vp)
+{
+ struct tdbsam2_private_data **tdb_privs = (struct tdbsam2_private_data **)vp;
+ while (ts2_privs->teo_handlers)
+ tdbsam2_enumerate_objects_stop(ts2_privs->teo_handlers);
+ *tdb_privs = NULL;
+ /* No need to free any further, as it is talloc()ed */
+}
+
+static NTSTATUS init_tdbsam2(GUMS_FUNCTIONS *fns, const char *storage)
+{
+ NTSTATUS ret;
+ TDB_CONTEXT *tdb;
+ DOM_SID dom_sid;
+
+ fns->name = talloc_strdup(fns->mem_ctx, "tdbsam2");
+
+ fns->get_domain_sid = tdbsam2_get_domain_sid;
+ /* fns->get_sequence_number = tdbsam2_get_sequence_number; */
+ fns->new_object = tdbsam2_new_object;
+ fns->delete_object = tdbsam2_delete_object;
+ fns->get_object_from_sid = tdbsam2_get_object_from_sid;
+ fns->get_object_from_name = tdbsam2_get_object_from_name;
+ /* fns->get_updated_objects = tdbsam2_get_updated_objects; */
+ fns->enumerate_objects_start = tdbsam2_enumerate_objects_start;
+ fns->enumerate_objects_get_next = tdbsam2_enumerate_objects_get_next;
+ fns->enumerate_objects_stop = tdbsam2_enumerate_objects_stop;
+ fns->set_object = tdbsam2_set_object;
+ /* fns->set_object_values = tdbsam2_set_object_values;
+ fns->add_members_to_group = tdbsam2_add_members_to_group;
+ fns->delete_members_from_group = tdbsam2_delete_members_from_group;
+ fns->enumerate_group_members = tdbsam2_enumerate_group_members;
+ fns->get_sid_groups = tdbsam2_get_sid_groups;
+ fns->lock_sid = tdbsam2_lock_sid;
+ fns->unlock_sid = tdbsam2_unlock_sid;
+ fns->get_privilege = tdbsam2_get_privilege;
+ fns->add_members_to_privilege = tdbsam2_add_members_to_privilege;
+ fns->delete_members_from_privilege = tdbsam2_delete_members_from_privilege;
+ fns->enumerate_privilege_members = tdbsam2_enumerate_privilege_members;
+ fns->get_sid_privileges = tdbsam2_get_sid_privileges;
+ fns->set_privilege = tdbsam2_set_privilege; */
+
+ ts2_privs = talloc_zero(fns->mem_ctx, sizeof(struct tdbsam2_private_data));
+ if (!ts2_privs) {
+ DEBUG(0, ("talloc() failed for tdbsam2 private_data!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (storage) {
+ ts2_privs->storage = talloc_strdup(fns->mem_ctx, storage);
+ } else {
+ pstring tdbfile;
+ get_private_directory(tdbfile);
+ pstrcat(tdbfile, "/");
+ pstrcat(tdbfile, TDB_FILE_NAME);
+ ts2_privs->storage = talloc_strdup(fns->mem_ctx, tdbfile);
+ }
+
+ /* check tdb exist (or create it) */
+
+ /* Find the domain SID */
+ if (!NT_STATUS_IS_OK(tdbsam2_get_domain_sid(&dom_sid, global_myname()))) {
+ /* db file does not exist or it is not inited */
+ /* make the tdb file */
+ if (!NT_STATUS_IS_OK(ret = opentdb(&tdb, False))) {
+ return ret;
+ }
+ tdb_close(tdb);
+
+ if (!NT_STATUS_IS_OK(tdbsam2_get_domain_sid(&dom_sid, "BUILTIN"))) {
+ gums_init_builtin_domain();
+ }
+
+ gums_init_domain(get_global_sam_sid(), global_myname(), "The Domain");
+ }
+
+ fns->private_data = &ts2_privs;
+ fns->free_private_data = free_tdbsam2_private_data;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_tdbsam2_init(void)
+{
+ /*
+ if ((gums_tdbsam2_debug_class = debug_add_class("gums_tdbsam2")) == -1) {
+ DEBUG(0, ("gums_tdbsam2: unable to register my own debug class! going on ...\n"));
+ gums_tdbsam2_debug_class = DBGC_ALL;
+ }
+ */
+ return gums_register_module(GUMS_INTERFACE_VERSION, "tdbsam2", init_tdbsam2);
+}
diff --git a/source/sam/idmap.c b/source/sam/idmap.c
new file mode 100644
index 00000000000..4d8b768c2fa
--- /dev/null
+++ b/source/sam/idmap.c
@@ -0,0 +1,317 @@
+/*
+ Unix SMB/CIFS implementation.
+ ID Mapping
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+ Copyright (C) Simo Sorce 2003
+ Copyright (C) Jeremy Allison 2003.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+struct idmap_function_entry {
+ const char *name;
+ struct idmap_methods *methods;
+ struct idmap_function_entry *prev,*next;
+};
+
+static struct idmap_function_entry *backends = NULL;
+
+static struct idmap_methods *cache_map;
+static struct idmap_methods *remote_map;
+
+/**********************************************************************
+ Get idmap methods. Don't allow tdb to be a remote method.
+**********************************************************************/
+
+static struct idmap_methods *get_methods(const char *name, BOOL cache_method)
+{
+ struct idmap_function_entry *entry = backends;
+
+ for(entry = backends; entry; entry = entry->next) {
+ if (!cache_method && strequal(entry->name, "tdb"))
+ continue; /* tdb is only cache method. */
+ if (strequal(entry->name, name))
+ return entry->methods;
+ }
+
+ return NULL;
+}
+
+/**********************************************************************
+ Allow a module to register itself as a method.
+**********************************************************************/
+
+NTSTATUS smb_register_idmap(int version, const char *name, struct idmap_methods *methods)
+{
+ struct idmap_function_entry *entry;
+
+ if ((version != SMB_IDMAP_INTERFACE_VERSION)) {
+ DEBUG(0, ("smb_register_idmap: Failed to register idmap module.\n"
+ "The module was compiled against SMB_IDMAP_INTERFACE_VERSION %d,\n"
+ "current SMB_IDMAP_INTERFACE_VERSION is %d.\n"
+ "Please recompile against the current version of samba!\n",
+ version, SMB_IDMAP_INTERFACE_VERSION));
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ if (!name || !name[0] || !methods) {
+ DEBUG(0,("smb_register_idmap: called with NULL pointer or empty name!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (get_methods(name, False)) {
+ DEBUG(0,("smb_register_idmap: idmap module %s already registered!\n", name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ entry = smb_xmalloc(sizeof(struct idmap_function_entry));
+ entry->name = smb_xstrdup(name);
+ entry->methods = methods;
+
+ DLIST_ADD(backends, entry);
+ DEBUG(5, ("smb_register_idmap: Successfully added idmap backend '%s'\n", name));
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Initialise idmap cache and a remote backend (if configured).
+**********************************************************************/
+
+BOOL idmap_init(const char *remote_backend)
+{
+ if (!backends)
+ static_init_idmap;
+
+ if (!cache_map) {
+ cache_map = get_methods("tdb", True);
+
+ if (!cache_map) {
+ DEBUG(0, ("idmap_init: could not find tdb cache backend!\n"));
+ return False;
+ }
+
+ if (!NT_STATUS_IS_OK(cache_map->init( NULL ))) {
+ DEBUG(0, ("idmap_init: could not initialise tdb cache backend!\n"));
+ return False;
+ }
+ }
+
+ if (!remote_map && remote_backend && *remote_backend != 0) {
+ char *rem_backend = smb_xstrdup(remote_backend);
+ fstring params = "";
+ char *pparams;
+
+ /* get any mode parameters passed in */
+
+ if ( (pparams = strchr( rem_backend, ':' )) != NULL ) {
+ *pparams = '\0';
+ pparams++;
+ fstrcpy( params, pparams );
+ }
+
+ DEBUG(3, ("idmap_init: using '%s' as remote backend\n", rem_backend));
+
+ if((remote_map = get_methods(rem_backend, False)) ||
+ (NT_STATUS_IS_OK(smb_probe_module("idmap", rem_backend)) &&
+ (remote_map = get_methods(rem_backend, False)))) {
+ remote_map->init(params);
+ } else {
+ DEBUG(0, ("idmap_init: could not load remote backend '%s'\n", rem_backend));
+ SAFE_FREE(rem_backend);
+ return False;
+ }
+ SAFE_FREE(rem_backend);
+ }
+
+ return True;
+}
+
+/**************************************************************************
+ This is a rare operation, designed to allow an explicit mapping to be
+ set up for a sid to a POSIX id.
+**************************************************************************/
+
+NTSTATUS idmap_set_mapping(const DOM_SID *sid, unid_t id, int id_type)
+{
+ struct idmap_methods *map = remote_map;
+ DOM_SID tmp_sid;
+
+ DEBUG(10, ("idmap_set_mapping: Set %s to %s %lu\n",
+ sid_string_static(sid),
+ ((id_type & ID_TYPEMASK) == ID_USERID) ? "UID" : "GID",
+ ((id_type & ID_TYPEMASK) == ID_USERID) ? (unsigned long)id.uid :
+ (unsigned long)id.gid));
+
+ if ( (NT_STATUS_IS_OK(cache_map->
+ get_sid_from_id(&tmp_sid, id,
+ id_type | ID_QUERY_ONLY))) &&
+ sid_equal(sid, &tmp_sid) ) {
+ /* Nothing to do, we already have that mapping */
+ DEBUG(10, ("idmap_set_mapping: Mapping already there\n"));
+ return NT_STATUS_OK;
+ }
+
+ if (map == NULL) {
+ /* Ok, we don't have a authoritative remote
+ mapping. So update our local cache only. */
+ map = cache_map;
+ }
+
+ return map->set_mapping(sid, id, id_type);
+}
+
+/**************************************************************************
+ Get ID from SID. This can create a mapping for a SID to a POSIX id.
+**************************************************************************/
+
+NTSTATUS idmap_get_id_from_sid(unid_t *id, int *id_type, const DOM_SID *sid)
+{
+ NTSTATUS ret;
+ int loc_type;
+
+ loc_type = *id_type;
+
+ if (remote_map) {
+ /* We have a central remote idmap so only look in
+ cache, don't allocate */
+ loc_type |= ID_QUERY_ONLY;
+ }
+
+ ret = cache_map->get_id_from_sid(id, &loc_type, sid);
+
+ if (NT_STATUS_IS_OK(ret)) {
+ *id_type = loc_type & ID_TYPEMASK;
+ return NT_STATUS_OK;
+ }
+
+ if (remote_map == NULL) {
+ return ret;
+ }
+
+ /* Ok, the mapping was not in the cache, give the remote map a
+ second try. */
+
+ ret = remote_map->get_id_from_sid(id, id_type, sid);
+
+ if (NT_STATUS_IS_OK(ret)) {
+ /* The remote backend gave us a valid mapping, cache it. */
+ ret = cache_map->set_mapping(sid, *id, *id_type);
+ }
+
+ return ret;
+}
+
+/**************************************************************************
+ Get SID from ID. This must have been created before.
+**************************************************************************/
+
+NTSTATUS idmap_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type)
+{
+ NTSTATUS ret;
+ int loc_type;
+
+ loc_type = id_type;
+ if (remote_map) {
+ loc_type = id_type | ID_QUERY_ONLY;
+ }
+
+ ret = cache_map->get_sid_from_id(sid, id, loc_type);
+
+ if (NT_STATUS_IS_OK(ret))
+ return ret;
+
+ if (remote_map == NULL)
+ return ret;
+
+ /* We have a second chance, ask our authoritative backend */
+
+ ret = remote_map->get_sid_from_id(sid, id, id_type);
+
+ if (NT_STATUS_IS_OK(ret)) {
+ /* The remote backend gave us a valid mapping, cache it. */
+ ret = cache_map->set_mapping(sid, id, id_type);
+ }
+
+ return ret;
+}
+
+/**************************************************************************
+ Alloocate a new UNIX uid/gid
+**************************************************************************/
+
+NTSTATUS idmap_allocate_id(unid_t *id, int id_type)
+{
+ /* we have to allocate from the authoritative backend */
+
+ if ( remote_map )
+ return remote_map->allocate_id( id, id_type );
+
+ return cache_map->allocate_id( id, id_type );
+}
+
+/**************************************************************************
+ Alloocate a new RID
+**************************************************************************/
+
+NTSTATUS idmap_allocate_rid(uint32 *rid, int type)
+{
+ /* we have to allocate from the authoritative backend */
+
+ if ( remote_map )
+ return remote_map->allocate_rid( rid, type );
+
+ return cache_map->allocate_rid( rid, type );
+}
+
+/**************************************************************************
+ Shutdown maps.
+**************************************************************************/
+
+NTSTATUS idmap_close(void)
+{
+ NTSTATUS ret;
+
+ ret = cache_map->close();
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(3, ("idmap_close: failed to close local tdb cache!\n"));
+ }
+ cache_map = NULL;
+
+ if (remote_map) {
+ ret = remote_map->close();
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(3, ("idmap_close: failed to close remote idmap repository!\n"));
+ }
+ remote_map = NULL;
+ }
+
+ return ret;
+}
+
+/**************************************************************************
+ Dump backend status.
+**************************************************************************/
+
+void idmap_status(void)
+{
+ cache_map->status();
+ if (remote_map)
+ remote_map->status();
+}
diff --git a/source/sam/idmap_ldap.c b/source/sam/idmap_ldap.c
new file mode 100644
index 00000000000..2124fb68793
--- /dev/null
+++ b/source/sam/idmap_ldap.c
@@ -0,0 +1,790 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ idmap LDAP backend
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+ Copyright (C) Simo Sorce 2003
+ Copyright (C) Gerald Carter 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+
+#include <lber.h>
+#include <ldap.h>
+
+#include "smbldap.h"
+
+struct ldap_idmap_state {
+ struct smbldap_state *smbldap_state;
+ TALLOC_CTX *mem_ctx;
+};
+
+static struct ldap_idmap_state ldap_state;
+
+/* number tries while allocating new id */
+#define LDAP_MAX_ALLOC_ID 128
+
+
+/***********************************************************************
+ This function cannot be called to modify a mapping, only set a new one
+***********************************************************************/
+
+static NTSTATUS ldap_set_mapping(const DOM_SID *sid, unid_t id, int id_type)
+{
+ pstring dn;
+ pstring id_str;
+ fstring type;
+ LDAPMod **mods = NULL;
+ int rc = -1;
+ int ldap_op;
+ fstring sid_string;
+ LDAPMessage *entry = NULL;
+
+ sid_to_string( sid_string, sid );
+
+ ldap_op = LDAP_MOD_ADD;
+ pstr_sprintf(dn, "%s=%s,%s", get_attr_key2string( sidmap_attr_list, LDAP_ATTR_SID),
+ sid_string, lp_ldap_idmap_suffix());
+
+ if ( id_type & ID_USERID )
+ fstrcpy( type, get_attr_key2string( sidmap_attr_list, LDAP_ATTR_UIDNUMBER ) );
+ else
+ fstrcpy( type, get_attr_key2string( sidmap_attr_list, LDAP_ATTR_GIDNUMBER ) );
+
+ pstr_sprintf(id_str, "%lu", ((id_type & ID_USERID) ? (unsigned long)id.uid :
+ (unsigned long)id.gid));
+
+ smbldap_set_mod( &mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_IDMAP_ENTRY );
+
+ smbldap_make_mod( ldap_state.smbldap_state->ldap_struct,
+ entry, &mods, type, id_str );
+
+ smbldap_make_mod( ldap_state.smbldap_state->ldap_struct,
+ entry, &mods,
+ get_attr_key2string(sidmap_attr_list, LDAP_ATTR_SID),
+ sid_string );
+
+ /* There may well be nothing at all to do */
+
+ if (mods) {
+ smbldap_set_mod( &mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_SID_ENTRY );
+ rc = smbldap_add(ldap_state.smbldap_state, dn, mods);
+ ldap_mods_free( mods, True );
+ } else {
+ rc = LDAP_SUCCESS;
+ }
+
+ if (rc != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(ldap_state.smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
+ &ld_error);
+ DEBUG(0,("ldap_set_mapping_internals: Failed to %s mapping from %s to %lu [%s]\n",
+ (ldap_op == LDAP_MOD_ADD) ? "add" : "replace",
+ sid_string, (unsigned long)((id_type & ID_USERID) ? id.uid : id.gid), type));
+ DEBUG(0, ("ldap_set_mapping_internals: Error was: %s (%s)\n",
+ ld_error ? ld_error : "(NULL)", ldap_err2string (rc)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(10,("ldap_set_mapping: Successfully created mapping from %s to %lu [%s]\n",
+ sid_string, ((id_type & ID_USERID) ? (unsigned long)id.uid :
+ (unsigned long)id.gid), type));
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Even if the sambaDomain attribute in LDAP tells us that this RID is
+ safe to use, always check before use.
+*********************************************************************/
+
+static BOOL sid_in_use(struct ldap_idmap_state *state,
+ const DOM_SID *sid, int *error)
+{
+ fstring filter;
+ fstring sid_string;
+ LDAPMessage *result = NULL;
+ int count;
+ int rc;
+ char *sid_attr[] = {LDAP_ATTRIBUTE_SID, NULL};
+
+ slprintf(filter, sizeof(filter)-1, "(%s=%s)", LDAP_ATTRIBUTE_SID, sid_to_string(sid_string, sid));
+
+ rc = smbldap_search_suffix(state->smbldap_state,
+ filter, sid_attr, &result);
+
+ if (rc != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING, &ld_error);
+ DEBUG(2, ("Failed to check if sid %s is alredy in use: %s\n",
+ sid_string, ld_error));
+ SAFE_FREE(ld_error);
+
+ *error = rc;
+ return True;
+ }
+
+ if ((count = ldap_count_entries(state->smbldap_state->ldap_struct, result)) > 0) {
+ DEBUG(3, ("Sid %s already in use - trying next RID\n",
+ sid_string));
+ ldap_msgfree(result);
+ return True;
+ }
+
+ ldap_msgfree(result);
+
+ /* good, sid is not in use */
+ return False;
+}
+
+/**********************************************************************
+ Set the new nextRid attribute, and return one we can use.
+
+ This also checks that this RID is actually free - in case the admin
+ manually stole it :-).
+*********************************************************************/
+
+static NTSTATUS ldap_next_rid(struct ldap_idmap_state *state, uint32 *rid,
+ int rid_type)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ int rc;
+ LDAPMessage *domain_result = NULL;
+ LDAPMessage *entry = NULL;
+ char *dn;
+ LDAPMod **mods = NULL;
+ fstring old_rid_string;
+ fstring next_rid_string;
+ fstring algorithmic_rid_base_string;
+ uint32 next_rid;
+ uint32 alg_rid_base;
+ int attempts = 0;
+ char *ld_error = NULL;
+
+ while (attempts < 10) {
+ if (!NT_STATUS_IS_OK(ret = smbldap_search_domain_info(state->smbldap_state,
+ &domain_result, get_global_sam_name(), True))) {
+ return ret;
+ }
+
+ entry = ldap_first_entry(state->smbldap_state->ldap_struct, domain_result);
+ if (!entry) {
+ DEBUG(0, ("Could not get domain info entry\n"));
+ ldap_msgfree(domain_result);
+ return ret;
+ }
+
+ if ((dn = smbldap_get_dn(state->smbldap_state->ldap_struct, entry)) == NULL) {
+ DEBUG(0, ("Could not get domain info DN\n"));
+ ldap_msgfree(domain_result);
+ return ret;
+ }
+
+ /* yes, we keep 3 seperate counters, one for rids between 1000 (BASE_RID) and
+ algorithmic_rid_base. The other two are to avoid stomping on the
+ different sets of algorithmic RIDs */
+
+ if (smbldap_get_single_pstring(state->smbldap_state->ldap_struct, entry,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_ALGORITHMIC_RID_BASE),
+ algorithmic_rid_base_string)) {
+
+ alg_rid_base = (uint32)atol(algorithmic_rid_base_string);
+ } else {
+ alg_rid_base = algorithmic_rid_base();
+ /* Try to make the modification atomically by enforcing the
+ old value in the delete mod. */
+ slprintf(algorithmic_rid_base_string, sizeof(algorithmic_rid_base_string)-1, "%d", alg_rid_base);
+ smbldap_make_mod(state->smbldap_state->ldap_struct, entry, &mods,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_ALGORITHMIC_RID_BASE),
+ algorithmic_rid_base_string);
+ }
+
+ next_rid = 0;
+
+ if (alg_rid_base > BASE_RID) {
+ /* we have a non-default 'algorithmic rid base', so we have 'low' rids that we
+ can allocate to new users */
+ if (smbldap_get_single_pstring(state->smbldap_state->ldap_struct, entry,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_RID),
+ old_rid_string)) {
+ *rid = (uint32)atol(old_rid_string);
+ } else {
+ *rid = BASE_RID;
+ }
+
+ next_rid = *rid+1;
+ if (next_rid >= alg_rid_base) {
+ ldap_msgfree(domain_result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ slprintf(next_rid_string, sizeof(next_rid_string)-1, "%d", next_rid);
+
+ /* Try to make the modification atomically by enforcing the
+ old value in the delete mod. */
+ smbldap_make_mod(state->smbldap_state->ldap_struct, entry, &mods,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_RID),
+ next_rid_string);
+ }
+
+ if (!next_rid) { /* not got one already */
+ switch (rid_type) {
+ case USER_RID_TYPE:
+ if (smbldap_get_single_pstring(state->smbldap_state->ldap_struct, entry,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_USERRID),
+ old_rid_string)) {
+ *rid = (uint32)atol(old_rid_string);
+ }
+ break;
+ case GROUP_RID_TYPE:
+ if (smbldap_get_single_pstring(state->smbldap_state->ldap_struct, entry,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_GROUPRID),
+ old_rid_string)) {
+ *rid = (uint32)atol(old_rid_string);
+ }
+ break;
+ }
+
+ /* This is the core of the whole routine. If we had
+ scheme-style closures, there would be a *lot* less code
+ duplication... */
+
+ next_rid = *rid+RID_MULTIPLIER;
+ slprintf(next_rid_string, sizeof(next_rid_string)-1, "%d", next_rid);
+
+ switch (rid_type) {
+ case USER_RID_TYPE:
+ /* Try to make the modification atomically by enforcing the
+ old value in the delete mod. */
+ smbldap_make_mod(state->smbldap_state->ldap_struct, entry, &mods,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_USERRID),
+ next_rid_string);
+ break;
+
+ case GROUP_RID_TYPE:
+ /* Try to make the modification atomically by enforcing the
+ old value in the delete mod. */
+ smbldap_make_mod(state->smbldap_state->ldap_struct, entry, &mods,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_GROUPRID),
+ next_rid_string);
+ break;
+ }
+ }
+
+ if ((rc = smbldap_modify(state->smbldap_state, dn, mods)) == LDAP_SUCCESS) {
+ DOM_SID dom_sid;
+ DOM_SID sid;
+ pstring domain_sid_string;
+ int error = 0;
+
+ if (!smbldap_get_single_pstring(state->smbldap_state->ldap_struct, domain_result,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_DOM_SID),
+ domain_sid_string)) {
+ ldap_mods_free(mods, True);
+ SAFE_FREE(dn);
+ ldap_msgfree(domain_result);
+ return ret;
+ }
+
+ if (!string_to_sid(&dom_sid, domain_sid_string)) {
+ ldap_mods_free(mods, True);
+ SAFE_FREE(dn);
+ ldap_msgfree(domain_result);
+ return ret;
+ }
+
+ ldap_mods_free(mods, True);
+ mods = NULL;
+ SAFE_FREE(dn);
+ ldap_msgfree(domain_result);
+
+ sid_copy(&sid, &dom_sid);
+ sid_append_rid(&sid, *rid);
+
+ /* check RID is not in use */
+ if (sid_in_use(state, &sid, &error)) {
+ if (error) {
+ return ret;
+ }
+ continue;
+ }
+
+ return NT_STATUS_OK;
+ }
+
+ ld_error = NULL;
+ ldap_get_option(state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING, &ld_error);
+ DEBUG(2, ("Failed to modify rid: %s\n", ld_error ? ld_error : "(NULL"));
+ SAFE_FREE(ld_error);
+
+ ldap_mods_free(mods, True);
+ mods = NULL;
+
+ SAFE_FREE(dn);
+
+ ldap_msgfree(domain_result);
+ domain_result = NULL;
+
+ {
+ /* Sleep for a random timeout */
+ unsigned sleeptime = (sys_random()*sys_getpid()*attempts);
+ attempts += 1;
+
+ sleeptime %= 100;
+ smb_msleep(sleeptime);
+ }
+ }
+
+ DEBUG(0, ("Failed to set new RID\n"));
+ return ret;
+}
+
+
+/*****************************************************************************
+ Allocate a new RID
+*****************************************************************************/
+
+static NTSTATUS ldap_allocate_rid(uint32 *rid, int rid_type)
+{
+ return ldap_next_rid( &ldap_state, rid, rid_type );
+}
+
+/*****************************************************************************
+ Allocate a new uid or gid
+*****************************************************************************/
+
+static NTSTATUS ldap_allocate_id(unid_t *id, int id_type)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ int rc = LDAP_SERVER_DOWN;
+ int count = 0;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ pstring id_str, new_id_str;
+ LDAPMod **mods = NULL;
+ const char *type;
+ char *dn = NULL;
+ char **attr_list;
+ pstring filter;
+ uid_t luid, huid;
+ gid_t lgid, hgid;
+
+
+ type = (id_type & ID_USERID) ?
+ get_attr_key2string( idpool_attr_list, LDAP_ATTR_UIDNUMBER ) :
+ get_attr_key2string( idpool_attr_list, LDAP_ATTR_GIDNUMBER );
+
+ pstr_sprintf(filter, "(objectClass=%s)", LDAP_OBJ_IDPOOL);
+
+ attr_list = get_attr_list( idpool_attr_list );
+
+ rc = smbldap_search(ldap_state.smbldap_state, lp_ldap_idmap_suffix(),
+ LDAP_SCOPE_SUBTREE, filter,
+ attr_list, 0, &result);
+ free_attr_list( attr_list );
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0,("ldap_allocate_id: %s object not found\n", LDAP_OBJ_IDPOOL));
+ goto out;
+ }
+
+ count = ldap_count_entries(ldap_state.smbldap_state->ldap_struct, result);
+ if (count != 1) {
+ DEBUG(0,("ldap_allocate_id: single %s object not found\n", LDAP_OBJ_IDPOOL));
+ goto out;
+ }
+
+ dn = smbldap_get_dn(ldap_state.smbldap_state->ldap_struct, result);
+ if (!dn) {
+ goto out;
+ }
+ entry = ldap_first_entry(ldap_state.smbldap_state->ldap_struct, result);
+
+ if (!smbldap_get_single_pstring(ldap_state.smbldap_state->ldap_struct, entry, type, id_str)) {
+ DEBUG(0,("ldap_allocate_id: %s attribute not found\n",
+ type));
+ goto out;
+ }
+
+ /* this must succeed or else we wouldn't have initialized */
+
+ lp_idmap_uid( &luid, &huid);
+ lp_idmap_gid( &lgid, &hgid);
+
+ /* make sure we still have room to grow */
+
+ if (id_type & ID_USERID) {
+ id->uid = strtoul(id_str, NULL, 10);
+ if (id->uid > huid ) {
+ DEBUG(0,("ldap_allocate_id: Cannot allocate uid above %lu!\n",
+ (unsigned long)huid));
+ goto out;
+ }
+ }
+ else {
+ id->gid = strtoul(id_str, NULL, 10);
+ if (id->gid > hgid ) {
+ DEBUG(0,("ldap_allocate_id: Cannot allocate gid above %lu!\n",
+ (unsigned long)hgid));
+ goto out;
+ }
+ }
+
+ pstr_sprintf(new_id_str, "%lu",
+ ((id_type & ID_USERID) ? (unsigned long)id->uid :
+ (unsigned long)id->gid) + 1);
+
+ smbldap_set_mod( &mods, LDAP_MOD_DELETE, type, id_str );
+ smbldap_set_mod( &mods, LDAP_MOD_ADD, type, new_id_str );
+
+ if (mods == NULL) {
+ DEBUG(0,("ldap_allocate_id: smbldap_set_mod() failed.\n"));
+ goto out;
+ }
+
+ rc = smbldap_modify(ldap_state.smbldap_state, dn, mods);
+
+ ldap_mods_free( mods, True );
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0,("ldap_allocate_id: Failed to allocate new %s. ldap_modify() failed.\n",
+ type));
+ goto out;
+ }
+
+ ret = NT_STATUS_OK;
+out:
+ SAFE_FREE(dn);
+ if (result != NULL)
+ ldap_msgfree(result);
+
+ return ret;
+}
+
+/*****************************************************************************
+ get a sid from an id
+*****************************************************************************/
+
+static NTSTATUS ldap_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type)
+{
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ pstring sid_str;
+ pstring filter;
+ pstring suffix;
+ const char *type;
+ int rc;
+ int count;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ char **attr_list;
+
+ if ( id_type & ID_USERID )
+ type = get_attr_key2string( idpool_attr_list, LDAP_ATTR_UIDNUMBER );
+ else
+ type = get_attr_key2string( idpool_attr_list, LDAP_ATTR_GIDNUMBER );
+
+ pstrcpy( suffix, lp_ldap_idmap_suffix() );
+ pstr_sprintf(filter, "(&(objectClass=%s)(%s=%lu))",
+ LDAP_OBJ_IDMAP_ENTRY, type,
+ ((id_type & ID_USERID) ? (unsigned long)id.uid : (unsigned long)id.gid));
+
+ attr_list = get_attr_list( sidmap_attr_list );
+ rc = smbldap_search(ldap_state.smbldap_state, suffix, LDAP_SCOPE_SUBTREE,
+ filter, attr_list, 0, &result);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(3,("ldap_get_isd_from_id: Failure looking up entry (%s)\n",
+ ldap_err2string(rc) ));
+ goto out;
+ }
+
+ count = ldap_count_entries(ldap_state.smbldap_state->ldap_struct, result);
+
+ if (count != 1) {
+ DEBUG(0,("ldap_get_sid_from_id: mapping not found for %s: %lu\n",
+ type, ((id_type & ID_USERID) ? (unsigned long)id.uid :
+ (unsigned long)id.gid)));
+ goto out;
+ }
+
+ entry = ldap_first_entry(ldap_state.smbldap_state->ldap_struct, result);
+
+ if ( !smbldap_get_single_pstring(ldap_state.smbldap_state->ldap_struct, entry, LDAP_ATTRIBUTE_SID, sid_str) )
+ goto out;
+
+ if (!string_to_sid(sid, sid_str))
+ goto out;
+
+ ret = NT_STATUS_OK;
+out:
+ free_attr_list( attr_list );
+
+ if (result)
+ ldap_msgfree(result);
+
+ return ret;
+}
+
+/***********************************************************************
+ Get an id from a sid
+***********************************************************************/
+
+static NTSTATUS ldap_get_id_from_sid(unid_t *id, int *id_type, const DOM_SID *sid)
+{
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ pstring sid_str;
+ pstring filter;
+ pstring id_str;
+ const char *suffix;
+ const char *type;
+ int rc;
+ int count;
+ char **attr_list;
+ char *dn = NULL;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ sid_to_string(sid_str, sid);
+
+ DEBUG(8,("ldap_get_id_from_sid: %s (%s)\n", sid_str,
+ (*id_type & ID_GROUPID ? "group" : "user") ));
+
+ suffix = lp_ldap_idmap_suffix();
+ pstr_sprintf(filter, "(&(objectClass=%s)(%s=%s))",
+ LDAP_OBJ_IDMAP_ENTRY, LDAP_ATTRIBUTE_SID, sid_str);
+
+ if ( *id_type & ID_GROUPID )
+ type = get_attr_key2string( sidmap_attr_list, LDAP_ATTR_GIDNUMBER );
+ else
+ type = get_attr_key2string( sidmap_attr_list, LDAP_ATTR_UIDNUMBER );
+
+ /* do the search and check for errors */
+
+ attr_list = get_attr_list( sidmap_attr_list );
+ rc = smbldap_search(ldap_state.smbldap_state, suffix, LDAP_SCOPE_SUBTREE,
+ filter, attr_list, 0, &result);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(3,("ldap_get_id_from_sid: Failure looking up idmap entry (%s)\n",
+ ldap_err2string(rc) ));
+ goto out;
+ }
+
+ /* check for the number of entries returned */
+
+ count = ldap_count_entries(ldap_state.smbldap_state->ldap_struct, result);
+
+ if ( count > 1 ) {
+ DEBUG(0, ("ldap_get_id_from_sid: (2nd) search %s returned [%d] entries!\n",
+ filter, count));
+ goto out;
+ }
+
+ /* try to allocate a new id if we still haven't found one */
+
+ if ( !count ) {
+ int i;
+
+ if (*id_type & ID_QUERY_ONLY) {
+ DEBUG(5,("ldap_get_id_from_sid: No matching entry found and QUERY_ONLY flag set\n"));
+ goto out;
+ }
+
+ DEBUG(8,("ldap_get_id_from_sid: Allocating new id\n"));
+
+ for (i = 0; i < LDAP_MAX_ALLOC_ID; i++) {
+ ret = ldap_allocate_id(id, *id_type);
+ if ( NT_STATUS_IS_OK(ret) )
+ break;
+ }
+
+ if ( !NT_STATUS_IS_OK(ret) ) {
+ DEBUG(0,("ldap_allocate_id: cannot acquire id lock!\n"));
+ goto out;
+ }
+
+ DEBUG(10,("ldap_get_id_from_sid: Allocated new %cid [%ul]\n",
+ (*id_type & ID_GROUPID ? 'g' : 'u'), (uint32)id->uid ));
+
+ ret = ldap_set_mapping(sid, *id, *id_type);
+
+ /* all done */
+
+ goto out;
+ }
+
+ DEBUG(10,("ldap_get_id_from_sid: success\n"));
+
+ entry = ldap_first_entry(ldap_state.smbldap_state->ldap_struct, result);
+
+ dn = smbldap_get_dn(ldap_state.smbldap_state->ldap_struct, result);
+ if (!dn)
+ goto out;
+
+ DEBUG(10, ("Found mapping entry at dn=%s, looking for %s\n", dn, type));
+
+ if ( smbldap_get_single_pstring(ldap_state.smbldap_state->ldap_struct, entry, type, id_str) ) {
+ if ( (*id_type & ID_USERID) )
+ id->uid = strtoul(id_str, NULL, 10);
+ else
+ id->gid = strtoul(id_str, NULL, 10);
+
+ ret = NT_STATUS_OK;
+ goto out;
+ }
+
+out:
+ free_attr_list( attr_list );
+ if (result)
+ ldap_msgfree(result);
+ SAFE_FREE(dn);
+
+ return ret;
+}
+
+/**********************************************************************
+ Verify the sambaUnixIdPool entry in the directiry.
+**********************************************************************/
+
+static NTSTATUS verify_idpool( void )
+{
+ fstring filter;
+ int rc;
+ char **attr_list;
+ LDAPMessage *result = NULL;
+ LDAPMod **mods = NULL;
+ int count;
+
+ fstr_sprintf( filter, "(objectclass=%s)", LDAP_OBJ_IDPOOL );
+
+ attr_list = get_attr_list( idpool_attr_list );
+ rc = smbldap_search(ldap_state.smbldap_state, lp_ldap_idmap_suffix(),
+ LDAP_SCOPE_SUBTREE, filter, attr_list, 0, &result);
+ free_attr_list ( attr_list );
+
+ if (rc != LDAP_SUCCESS)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ count = ldap_count_entries(ldap_state.smbldap_state->ldap_struct, result);
+
+ ldap_msgfree(result);
+
+ if ( count > 1 ) {
+ DEBUG(0,("ldap_idmap_init: multiple entries returned from %s (base == %s)\n",
+ filter, lp_ldap_idmap_suffix() ));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ else if (count == 0) {
+ uid_t luid, huid;
+ gid_t lgid, hgid;
+ fstring uid_str, gid_str;
+
+ if ( !lp_idmap_uid(&luid, &huid) || !lp_idmap_gid( &lgid, &hgid ) ) {
+ DEBUG(0,("ldap_idmap_init: idmap uid/gid parameters not specified\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ fstr_sprintf( uid_str, "%lu", (unsigned long)luid );
+ fstr_sprintf( gid_str, "%lu", (unsigned long)lgid );
+
+ smbldap_set_mod( &mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_IDPOOL );
+ smbldap_set_mod( &mods, LDAP_MOD_ADD,
+ get_attr_key2string(idpool_attr_list, LDAP_ATTR_UIDNUMBER), uid_str );
+ smbldap_set_mod( &mods, LDAP_MOD_ADD,
+ get_attr_key2string(idpool_attr_list, LDAP_ATTR_GIDNUMBER), gid_str );
+
+ rc = smbldap_modify(ldap_state.smbldap_state, lp_ldap_idmap_suffix(), mods);
+ }
+
+ return ( rc==LDAP_SUCCESS ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL );
+}
+
+/*****************************************************************************
+ Initialise idmap database.
+*****************************************************************************/
+
+static NTSTATUS ldap_idmap_init( char *params )
+{
+ NTSTATUS nt_status;
+
+ ldap_state.mem_ctx = talloc_init("idmap_ldap");
+ if (!ldap_state.mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* assume location is the only parameter */
+ if (!NT_STATUS_IS_OK(nt_status =
+ smbldap_init(ldap_state.mem_ctx, params,
+ &ldap_state.smbldap_state))) {
+ talloc_destroy(ldap_state.mem_ctx);
+ return nt_status;
+ }
+
+ /* see if the idmap suffix and sub entries exists */
+
+ nt_status = verify_idpool();
+ if ( !NT_STATUS_IS_OK(nt_status) )
+ return nt_status;
+
+ return NT_STATUS_OK;
+}
+
+/*****************************************************************************
+ End the LDAP session
+*****************************************************************************/
+
+static NTSTATUS ldap_idmap_close(void)
+{
+
+ smbldap_free_struct(&(ldap_state).smbldap_state);
+ talloc_destroy(ldap_state.mem_ctx);
+
+ DEBUG(5,("The connection to the LDAP server was closed\n"));
+ /* maybe free the results here --metze */
+
+ return NT_STATUS_OK;
+}
+
+
+/* This function doesn't make as much sense in an LDAP world since the calling
+ node doesn't really control the ID ranges */
+static void ldap_idmap_status(void)
+{
+ DEBUG(0, ("LDAP IDMAP Status not available\n"));
+}
+
+static struct idmap_methods ldap_methods = {
+ ldap_idmap_init,
+ ldap_allocate_rid,
+ ldap_allocate_id,
+ ldap_get_sid_from_id,
+ ldap_get_id_from_sid,
+ ldap_set_mapping,
+ ldap_idmap_close,
+ ldap_idmap_status
+
+};
+
+NTSTATUS idmap_ldap_init(void)
+{
+ return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "ldap", &ldap_methods);
+}
diff --git a/source/sam/idmap_tdb.c b/source/sam/idmap_tdb.c
new file mode 100644
index 00000000000..8ab8ec84770
--- /dev/null
+++ b/source/sam/idmap_tdb.c
@@ -0,0 +1,676 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ idmap TDB backend
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+ Copyright (C) Simo Sorce 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+/* High water mark keys */
+#define HWM_GROUP "GROUP HWM"
+#define HWM_USER "USER HWM"
+
+/* idmap version determines auto-conversion */
+#define IDMAP_VERSION 2
+
+/* Globals */
+static TDB_CONTEXT *idmap_tdb;
+
+static struct idmap_state {
+
+ /* User and group id pool */
+
+ uid_t uid_low, uid_high; /* Range of uids to allocate */
+ gid_t gid_low, gid_high; /* Range of gids to allocate */
+} idmap_state;
+
+/**********************************************************************
+ allocate a new RID; We don't care if is a user or group
+**********************************************************************/
+
+static NTSTATUS db_allocate_rid(uint32 *rid, int rid_type)
+{
+ uint32 lowrid, highrid;
+ uint32 tmp_rid;
+
+ /* can't handle group rids right now. This is such a mess.... */
+
+ if ( rid_type == GROUP_RID_TYPE )
+ return NT_STATUS_UNSUCCESSFUL;
+
+ /* cannot fail since idmap is only called winbindd */
+
+ get_free_rid_range( &lowrid, &highrid );
+
+ tmp_rid = lowrid;
+
+ if ( !tdb_change_uint32_atomic(idmap_tdb, "RID_COUNTER", &tmp_rid, RID_MULTIPLIER) ) {
+ DEBUG(3,("db_allocate_rid: Failed to locate next rid record in idmap db\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if ( tmp_rid > highrid ) {
+ DEBUG(0, ("db_allocate_rid: no RIDs available!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ *rid = tmp_rid;
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Allocate either a user or group id from the pool
+**********************************************************************/
+
+static NTSTATUS db_allocate_id(unid_t *id, int id_type)
+{
+ BOOL ret;
+ int hwm;
+
+ if (!id)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ /* Get current high water mark */
+ switch (id_type & ID_TYPEMASK) {
+ case ID_USERID:
+
+ if ((hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) {
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ /* check it is in the range */
+ if (hwm > idmap_state.uid_high) {
+ DEBUG(0, ("idmap Fatal Error: UID range full!! (max: %lu)\n",
+ (unsigned long)idmap_state.uid_high));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* fetch a new id and increment it */
+ ret = tdb_change_uint32_atomic(idmap_tdb, HWM_USER, (unsigned int *)&hwm, 1);
+ if (!ret) {
+ DEBUG(0, ("idmap_tdb: Fatal error while fetching a new id\n!"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* recheck it is in the range */
+ if (hwm > idmap_state.uid_high) {
+ DEBUG(0, ("idmap Fatal Error: UID range full!! (max: %lu)\n",
+ (unsigned long)idmap_state.uid_high));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ (*id).uid = hwm;
+ DEBUG(10,("db_allocate_id: ID_USERID (*id).uid = %d\n", (unsigned int)hwm));
+
+ break;
+ case ID_GROUPID:
+ if ((hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) {
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ /* check it is in the range */
+ if (hwm > idmap_state.gid_high) {
+ DEBUG(0, ("idmap Fatal Error: GID range full!! (max: %lu)\n",
+ (unsigned long)idmap_state.gid_high));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* fetch a new id and increment it */
+ ret = tdb_change_uint32_atomic(idmap_tdb, HWM_GROUP, (unsigned int *)&hwm, 1);
+
+ if (!ret) {
+ DEBUG(0, ("idmap_tdb: Fatal error while fetching a new id\n!"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* recheck it is in the range */
+ if (hwm > idmap_state.gid_high) {
+ DEBUG(0, ("idmap Fatal Error: GID range full!! (max: %lu)\n",
+ (unsigned long)idmap_state.gid_high));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ (*id).gid = hwm;
+ DEBUG(10,("db_allocate_id: ID_GROUPID (*id).gid = %d\n", (unsigned int)hwm));
+
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* Get a sid from an id */
+static NTSTATUS internal_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type)
+{
+ TDB_DATA key, data;
+ fstring keystr;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if (!sid)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ switch (id_type & ID_TYPEMASK) {
+ case ID_USERID:
+ slprintf(keystr, sizeof(keystr), "UID %lu", (unsigned long)id.uid);
+ break;
+ case ID_GROUPID:
+ slprintf(keystr, sizeof(keystr), "GID %lu", (unsigned long)id.gid);
+ break;
+ default:
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ DEBUG(10,("internal_get_sid_from_id: fetching record %s\n", keystr ));
+
+ data = tdb_fetch(idmap_tdb, key);
+
+ if (data.dptr) {
+ if (string_to_sid(sid, data.dptr)) {
+ DEBUG(10,("internal_get_sid_from_id: fetching record %s -> %s\n", keystr, data.dptr ));
+ ret = NT_STATUS_OK;
+ }
+ SAFE_FREE(data.dptr);
+ }
+
+ return ret;
+}
+
+/* Error codes for get_id_from_sid */
+enum getidfromsiderr { GET_ID_FROM_SID_OK = 0, GET_ID_FROM_SID_NOTFOUND, GET_ID_FROM_SID_WRONG_TYPE, GET_ID_FROM_SID_ERR };
+
+static enum getidfromsiderr internal_get_id_from_sid(unid_t *id, int *id_type, const DOM_SID *sid)
+{
+ enum getidfromsiderr ret = GET_ID_FROM_SID_ERR;
+ fstring keystr;
+ TDB_DATA key, data;
+ int type = *id_type & ID_TYPEMASK;
+
+ /* Check if sid is present in database */
+ sid_to_string(keystr, sid);
+
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ DEBUG(10,("internal_get_id_from_sid: fetching record %s of type 0x%x\n", keystr, type ));
+
+ data = tdb_fetch(idmap_tdb, key);
+ if (!data.dptr) {
+ DEBUG(10,("internal_get_id_from_sid: record %s not found\n", keystr ));
+ return GET_ID_FROM_SID_NOTFOUND;
+ } else {
+ DEBUG(10,("internal_get_id_from_sid: record %s -> %s\n", keystr, data.dptr ));
+ }
+
+ if (type == ID_EMPTY || type == ID_USERID) {
+ fstring scanstr;
+ /* Parse and return existing uid */
+ fstrcpy(scanstr, "UID %d");
+
+ if (sscanf(data.dptr, scanstr, &((*id).uid)) == 1) {
+ /* uid ok? */
+ if (type == ID_EMPTY) {
+ *id_type = ID_USERID;
+ }
+ DEBUG(10,("internal_get_id_from_sid: %s fetching record %s -> %s \n",
+ (type == ID_EMPTY) ? "ID_EMPTY" : "ID_USERID",
+ keystr, data.dptr ));
+ ret = GET_ID_FROM_SID_OK;
+ } else {
+ ret = GET_ID_FROM_SID_WRONG_TYPE;
+ }
+ }
+
+ if ((ret != GET_ID_FROM_SID_OK) && (type == ID_EMPTY || type == ID_GROUPID)) {
+ fstring scanstr;
+ /* Parse and return existing gid */
+ fstrcpy(scanstr, "GID %d");
+
+ if (sscanf(data.dptr, scanstr, &((*id).gid)) == 1) {
+ /* gid ok? */
+ if (type == ID_EMPTY) {
+ *id_type = ID_GROUPID;
+ }
+ DEBUG(10,("internal_get_id_from_sid: %s fetching record %s -> %s \n",
+ (type == ID_EMPTY) ? "ID_EMPTY" : "ID_GROUPID",
+ keystr, data.dptr ));
+ ret = GET_ID_FROM_SID_OK;
+ } else {
+ ret = GET_ID_FROM_SID_WRONG_TYPE;
+ }
+ }
+
+ SAFE_FREE(data.dptr);
+
+ return ret;
+}
+
+/* Get a sid from an id */
+static NTSTATUS db_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type_in)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ enum getidfromsiderr iderr;
+ int id_type = id_type_in & ID_TYPEMASK;
+ unid_t id_tmp = id;
+ int id_type_tmp = id_type;
+
+ DEBUG(10,("db_get_sid_from_id: id_type_in = 0x%x\n", id_type_in));
+
+ ret = internal_get_sid_from_id(sid, id, id_type);
+ if (!NT_STATUS_IS_OK(ret)) {
+ return ret;
+ }
+
+ iderr = internal_get_id_from_sid(&id_tmp, &id_type_tmp, sid);
+ if (iderr != GET_ID_FROM_SID_OK) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ if (id_type_tmp != id_type) {
+ return NT_STATUS_UNSUCCESSFUL;
+ } else if (id_type == ID_USERID) {
+ if (id_tmp.uid != id.uid) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ } else if (id_type == ID_GROUPID) {
+ if (id_tmp.gid != id.gid) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ } else {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ return ret;
+}
+/* Get an id from a sid */
+static NTSTATUS db_get_id_from_sid(unid_t *id, int *id_type, const DOM_SID *sid)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ enum getidfromsiderr iderr;
+
+ DEBUG(10,("db_get_id_from_sid\n"));
+
+ if (!sid || !id || !id_type)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ iderr = internal_get_id_from_sid(id, id_type, sid);
+ if (iderr == GET_ID_FROM_SID_OK) {
+ DOM_SID sid_tmp;
+ ret = internal_get_sid_from_id(&sid_tmp, *id, *id_type);
+ if (NT_STATUS_IS_OK(ret)) {
+ if (!sid_equal(&sid_tmp, sid)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+ } else if (iderr == GET_ID_FROM_SID_WRONG_TYPE) {
+ /* We found a record but not the type we wanted.
+ * This is an error, not an opportunity to overwrite...
+ * JRA.
+ */
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!(*id_type & ID_QUERY_ONLY) && (iderr != GET_ID_FROM_SID_OK) &&
+ (((*id_type & ID_TYPEMASK) == ID_USERID)
+ || (*id_type & ID_TYPEMASK) == ID_GROUPID)) {
+ TDB_DATA sid_data;
+ TDB_DATA ugid_data;
+ fstring sid_string;
+
+ sid_to_string(sid_string, sid);
+
+ sid_data.dptr = sid_string;
+ sid_data.dsize = strlen(sid_string)+1;
+
+ /* Lock the record for this SID. */
+ if (tdb_chainlock(idmap_tdb, sid_data) != 0) {
+ DEBUG(10,("db_get_id_from_sid: failed to lock record %s. Error %s\n",
+ sid_string, tdb_errorstr(idmap_tdb) ));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ do {
+ fstring ugid_str;
+
+ /* Allocate a new id for this sid */
+ ret = db_allocate_id(id, *id_type);
+ if (!NT_STATUS_IS_OK(ret))
+ break;
+
+ /* Store the UID side */
+ /* Store new id */
+ if (*id_type & ID_USERID) {
+ slprintf(ugid_str, sizeof(ugid_str), "UID %lu",
+ (unsigned long)((*id).uid));
+ } else {
+ slprintf(ugid_str, sizeof(ugid_str), "GID %lu",
+ (unsigned long)((*id).gid));
+ }
+
+ ugid_data.dptr = ugid_str;
+ ugid_data.dsize = strlen(ugid_str) + 1;
+
+ DEBUG(10,("db_get_id_from_sid: storing %s -> %s\n",
+ ugid_data.dptr, sid_data.dptr ));
+
+ if (tdb_store(idmap_tdb, ugid_data, sid_data, TDB_INSERT) != -1) {
+ ret = NT_STATUS_OK;
+ break;
+ }
+ if (tdb_error(idmap_tdb) != TDB_ERR_EXISTS)
+ DEBUG(10,("db_get_id_from_sid: error %s\n", tdb_errorstr(idmap_tdb) ));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ } while (tdb_error(idmap_tdb) == TDB_ERR_EXISTS);
+
+ if (NT_STATUS_IS_OK(ret)) {
+
+ DEBUG(10,("db_get_id_from_sid: storing %s -> %s\n",
+ sid_data.dptr, ugid_data.dptr ));
+
+ if (tdb_store(idmap_tdb, sid_data, ugid_data, TDB_REPLACE) == -1) {
+ DEBUG(10,("db_get_id_from_sid: error %s\n", tdb_errorstr(idmap_tdb) ));
+ /* TODO: print tdb error !! */
+ tdb_chainunlock(idmap_tdb, sid_data);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ tdb_chainunlock(idmap_tdb, sid_data);
+ }
+
+ return ret;
+}
+
+static NTSTATUS db_set_mapping(const DOM_SID *sid, unid_t id, int id_type)
+{
+ TDB_DATA ksid, kid, data;
+ fstring ksidstr;
+ fstring kidstr;
+
+ DEBUG(10,("db_set_mapping: id_type = 0x%x\n", id_type));
+
+ if (!sid)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ sid_to_string(ksidstr, sid);
+
+ ksid.dptr = ksidstr;
+ ksid.dsize = strlen(ksidstr) + 1;
+
+ if (id_type & ID_USERID) {
+ slprintf(kidstr, sizeof(kidstr), "UID %lu", (unsigned long)id.uid);
+ } else if (id_type & ID_GROUPID) {
+ slprintf(kidstr, sizeof(kidstr), "GID %lu", (unsigned long)id.gid);
+ } else {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ kid.dptr = kidstr;
+ kid.dsize = strlen(kidstr) + 1;
+
+ /* *DELETE* prevoius mappings if any.
+ * This is done both SID and [U|G]ID passed in */
+
+ /* Lock the record for this SID. */
+ if (tdb_chainlock(idmap_tdb, ksid) != 0) {
+ DEBUG(10,("db_set_mapping: failed to lock record %s. Error %s\n",
+ ksidstr, tdb_errorstr(idmap_tdb) ));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(10,("db_set_mapping: fetching %s\n", ksid.dptr));
+
+ data = tdb_fetch(idmap_tdb, ksid);
+ if (data.dptr) {
+ DEBUG(10,("db_set_mapping: deleting %s and %s\n", data.dptr, ksid.dptr ));
+ tdb_delete(idmap_tdb, data);
+ tdb_delete(idmap_tdb, ksid);
+ SAFE_FREE(data.dptr);
+ }
+ data = tdb_fetch(idmap_tdb, kid);
+ if (data.dptr) {
+ DEBUG(10,("db_set_mapping: deleting %s and %s\n", data.dptr, kid.dptr ));
+ tdb_delete(idmap_tdb, data);
+ tdb_delete(idmap_tdb, kid);
+ SAFE_FREE(data.dptr);
+ }
+
+ if (tdb_store(idmap_tdb, ksid, kid, TDB_INSERT) == -1) {
+ DEBUG(0, ("idb_set_mapping: tdb_store 1 error: %s\n", tdb_errorstr(idmap_tdb)));
+ tdb_chainunlock(idmap_tdb, ksid);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ if (tdb_store(idmap_tdb, kid, ksid, TDB_INSERT) == -1) {
+ DEBUG(0, ("idb_set_mapping: tdb_store 2 error: %s\n", tdb_errorstr(idmap_tdb)));
+ tdb_chainunlock(idmap_tdb, ksid);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ tdb_chainunlock(idmap_tdb, ksid);
+ DEBUG(10,("db_set_mapping: stored %s -> %s and %s -> %s\n", ksid.dptr, kid.dptr, kid.dptr, ksid.dptr ));
+ return NT_STATUS_OK;
+}
+
+/*****************************************************************************
+ Initialise idmap database.
+*****************************************************************************/
+
+static NTSTATUS db_idmap_init( char *params )
+{
+ SMB_STRUCT_STAT stbuf;
+ char *tdbfile = NULL;
+ int32 version;
+ BOOL tdb_is_new = False;
+
+ /* use the old database if present */
+ tdbfile = strdup(lock_path("winbindd_idmap.tdb"));
+ if (!tdbfile) {
+ DEBUG(0, ("idmap_init: out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!file_exist(tdbfile, &stbuf)) {
+ tdb_is_new = True;
+ }
+
+ DEBUG(10,("db_idmap_init: Opening tdbfile %s\n", tdbfile ));
+
+ /* Open idmap repository */
+ if (!(idmap_tdb = tdb_open_log(tdbfile, 0,
+ TDB_DEFAULT, O_RDWR | O_CREAT,
+ 0644))) {
+ DEBUG(0, ("idmap_init: Unable to open idmap database\n"));
+ SAFE_FREE(tdbfile);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ SAFE_FREE(tdbfile);
+
+ if (tdb_is_new) {
+ /* the file didn't existed before opening it, let's
+ * store idmap version as nobody else yet opened and
+ * stored it. I do not like this method but didn't
+ * found a way to understand if an opened tdb have
+ * been just created or not --- SSS */
+ tdb_store_int32(idmap_tdb, "IDMAP_VERSION", IDMAP_VERSION);
+ }
+
+ /* check against earlier versions */
+ version = tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION");
+ if (version != IDMAP_VERSION) {
+ DEBUG(0, ("idmap_init: Unable to open idmap database, it's in an old format!\n"));
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ /* Create high water marks for group and user id */
+ if (!lp_idmap_uid(&idmap_state.uid_low, &idmap_state.uid_high)) {
+ DEBUG(1, ("idmap uid range missing or invalid\n"));
+ DEBUGADD(1, ("idmap will be unable to map foreign SIDs\n"));
+ } else {
+ if (tdb_fetch_int32(idmap_tdb, HWM_USER) == -1) {
+ if (tdb_store_int32(idmap_tdb, HWM_USER, idmap_state.uid_low) == -1) {
+ DEBUG(0, ("idmap_init: Unable to initialise user hwm in idmap database\n"));
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+ }
+ }
+
+ if (!lp_idmap_gid(&idmap_state.gid_low, &idmap_state.gid_high)) {
+ DEBUG(1, ("idmap gid range missing or invalid\n"));
+ DEBUGADD(1, ("idmap will be unable to map foreign SIDs\n"));
+ } else {
+ if (tdb_fetch_int32(idmap_tdb, HWM_GROUP) == -1) {
+ if (tdb_store_int32(idmap_tdb, HWM_GROUP, idmap_state.gid_low) == -1) {
+ DEBUG(0, ("idmap_init: Unable to initialise group hwm in idmap database\n"));
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* Close the tdb */
+static NTSTATUS db_idmap_close(void)
+{
+ if (idmap_tdb) {
+ if (tdb_close(idmap_tdb) == 0) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+
+/* Dump status information to log file. Display different stuff based on
+ the debug level:
+
+ Debug Level Information Displayed
+ =================================================================
+ 0 Percentage of [ug]id range allocated
+ 0 High water marks (next allocated ids)
+*/
+
+#define DUMP_INFO 0
+
+static void db_idmap_status(void)
+{
+ int user_hwm, group_hwm;
+
+ DEBUG(0, ("winbindd idmap status:\n"));
+
+ /* Get current high water marks */
+
+ if ((user_hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) {
+ DEBUG(DUMP_INFO,
+ ("\tCould not get userid high water mark!\n"));
+ }
+
+ if ((group_hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) {
+ DEBUG(DUMP_INFO,
+ ("\tCould not get groupid high water mark!\n"));
+ }
+
+ /* Display next ids to allocate */
+
+ if (user_hwm != -1) {
+ DEBUG(DUMP_INFO,
+ ("\tNext userid to allocate is %d\n", user_hwm));
+ }
+
+ if (group_hwm != -1) {
+ DEBUG(DUMP_INFO,
+ ("\tNext groupid to allocate is %d\n", group_hwm));
+ }
+
+ /* Display percentage of id range already allocated. */
+
+ if (user_hwm != -1) {
+ int num_users = user_hwm - idmap_state.uid_low;
+ int total_users =
+ idmap_state.uid_high - idmap_state.uid_low;
+
+ DEBUG(DUMP_INFO,
+ ("\tUser id range is %d%% full (%d of %d)\n",
+ num_users * 100 / total_users, num_users,
+ total_users));
+ }
+
+ if (group_hwm != -1) {
+ int num_groups = group_hwm - idmap_state.gid_low;
+ int total_groups =
+ idmap_state.gid_high - idmap_state.gid_low;
+
+ DEBUG(DUMP_INFO,
+ ("\tGroup id range is %d%% full (%d of %d)\n",
+ num_groups * 100 / total_groups, num_groups,
+ total_groups));
+ }
+
+ /* Display complete mapping of users and groups to rids */
+}
+
+/**********************************************************************
+ Return the TDB_CONTEXT* for winbindd_idmap. I **really** feel
+ dirty doing this, but not so dirty that I want to create another
+ tdb
+***********************************************************************/
+
+TDB_CONTEXT *idmap_tdb_handle( void )
+{
+ if ( idmap_tdb )
+ return idmap_tdb;
+
+ /* go ahead an open it; db_idmap_init() doesn't use any params
+ right now */
+
+ db_idmap_init( NULL );
+ if ( idmap_tdb )
+ return idmap_tdb;
+
+ return NULL;
+}
+
+static struct idmap_methods db_methods = {
+
+ db_idmap_init,
+ db_allocate_rid,
+ db_allocate_id,
+ db_get_sid_from_id,
+ db_get_id_from_sid,
+ db_set_mapping,
+ db_idmap_close,
+ db_idmap_status
+
+};
+
+NTSTATUS idmap_tdb_init(void)
+{
+ return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "tdb", &db_methods);
+}
diff --git a/source/sam/idmap_util.c b/source/sam/idmap_util.c
new file mode 100644
index 00000000000..f28e11cde74
--- /dev/null
+++ b/source/sam/idmap_util.c
@@ -0,0 +1,295 @@
+/*
+ Unix SMB/CIFS implementation.
+ ID Mapping
+ Copyright (C) Simo Sorce 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+#if 0 /* NOT USED */
+
+/**********************************************************************
+ Get the free RID base if idmap is configured, otherwise return 0
+**********************************************************************/
+
+uint32 idmap_get_free_rid_base(void)
+{
+ uint32 low, high;
+ if (idmap_get_free_rid_range(&low, &high)) {
+ return low;
+ }
+ return 0;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+BOOL idmap_check_ugid_is_in_free_range(uint32 id)
+{
+ uint32 low, high;
+
+ if (!idmap_get_free_ugid_range(&low, &high)) {
+ return False;
+ }
+ if (id < low || id > high) {
+ return False;
+ }
+ return True;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+BOOL idmap_check_rid_is_in_free_range(uint32 rid)
+{
+ uint32 low, high;
+
+ if (!idmap_get_free_rid_range(&low, &high)) {
+ return False;
+ }
+ if (rid < algorithmic_rid_base()) {
+ return True;
+ }
+
+ if (rid < low || rid > high) {
+ return False;
+ }
+
+ return True;
+}
+
+/**********************************************************************
+ if it is a foreign SID or if the SID is in the free range, return true
+**********************************************************************/
+
+BOOL idmap_check_sid_is_in_free_range(const DOM_SID *sid)
+{
+ if (sid_compare_domain(get_global_sam_sid(), sid) == 0) {
+
+ uint32 rid;
+
+ if (sid_peek_rid(sid, &rid)) {
+ return idmap_check_rid_is_in_free_range(rid);
+ }
+
+ return False;
+ }
+
+ return True;
+}
+
+#endif /* NOT USED */
+
+/*****************************************************************
+ Returns SID pointer.
+*****************************************************************/
+
+NTSTATUS idmap_uid_to_sid(DOM_SID *sid, uid_t uid)
+{
+ unid_t id;
+ int flags;
+
+ DEBUG(10,("idmap_uid_to_sid: uid = [%lu]\n", (unsigned long)uid));
+
+ flags = ID_USERID;
+ id.uid = uid;
+
+ return idmap_get_sid_from_id(sid, id, flags);
+}
+
+/*****************************************************************
+ Group mapping is used for gids that maps to Wellknown SIDs
+ Returns SID pointer.
+*****************************************************************/
+
+NTSTATUS idmap_gid_to_sid(DOM_SID *sid, gid_t gid)
+{
+ unid_t id;
+ int flags;
+
+ DEBUG(10,("idmap_gid_to_sid: gid = [%lu]\n", (unsigned long)gid));
+
+ flags = ID_GROUPID;
+#if 0 /* JERRY */
+ if (!idmap_check_ugid_is_in_free_range(gid)) {
+ flags |= ID_QUERY_ONLY;
+ }
+#endif
+ id.gid = gid;
+ return idmap_get_sid_from_id(sid, id, flags);
+}
+
+/*****************************************************************
+ if it is a foreign sid or it is in idmap rid range check idmap,
+ otherwise falls back to the legacy algorithmic mapping.
+ Returns True if this name is a user sid and the conversion
+ was done correctly, False if not.
+*****************************************************************/
+
+NTSTATUS idmap_sid_to_uid(const DOM_SID *sid, uid_t *uid, uint32 flags)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ unid_t id;
+
+ DEBUG(10,("idmap_sid_to_uid: sid = [%s]\n", sid_string_static(sid)));
+
+ flags |= ID_USERID;
+
+ ret = idmap_get_id_from_sid(&id, (int *)&flags, sid);
+
+ if ( NT_STATUS_IS_OK(ret) ) {
+ DEBUG(10,("idmap_sid_to_uid: uid = [%lu]\n", (unsigned long)id.uid));
+ *uid = id.uid;
+ }
+
+ return ret;
+
+}
+
+/*****************************************************************
+ *THE CANONICAL* convert SID to gid function.
+ if it is a foreign sid or it is in idmap rid range check idmap,
+ otherwise falls back to the legacy algorithmic mapping.
+ Group mapping is used for gids that maps to Wellknown SIDs
+ Returns True if this name is a user sid and the conversion
+ was done correctly, False if not.
+*****************************************************************/
+
+NTSTATUS idmap_sid_to_gid(const DOM_SID *sid, gid_t *gid, uint32 flags)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ unid_t id;
+
+ DEBUG(10,("sid_to_gid: sid = [%s]\n", sid_string_static(sid)));
+
+ flags |= ID_GROUPID;
+
+ ret = idmap_get_id_from_sid(&id, (int *)&flags, sid);
+
+ if ( NT_STATUS_IS_OK(ret) )
+ {
+ DEBUG(10,("idmap_sid_to_gid: gid = [%lu]\n", (unsigned long)id.gid));
+ *gid = id.gid;
+ }
+
+ return ret;
+}
+
+
+/***************************************************************************
+ Check first, call set_mapping if it doesn't already exist.
+***************************************************************************/
+
+static NTSTATUS wellknown_id_init(DOM_SID *sid, unid_t id, int flags)
+{
+ unid_t storedid;
+ int qflags = flags | ID_QUERY_ONLY;
+
+ if (!NT_STATUS_IS_OK(idmap_get_id_from_sid(&storedid, &qflags, sid))) {
+ return idmap_set_mapping(sid, id, flags);
+ } else {
+ if (flags == ID_USERID && id.uid != storedid.uid) {
+ DEBUG(0,("wellknown_id_init: WARNING ! Stored uid %u for SID %s is not the same as the requested uid %u\n",
+ (unsigned int)storedid.uid, sid_string_static(sid), (unsigned int)id.uid ));
+ DEBUG(0,("wellknown_id_init: Attempting to overwrite old mapping with new.\n"));
+ return idmap_set_mapping(sid, id, flags);
+ } else if (flags == ID_GROUPID && id.gid != storedid.gid) {
+ DEBUG(0,("wellknown_id_init: WARNING ! Stored gid %u for SID %s is not the same as the requested gid %u\n",
+ (unsigned int)storedid.gid, sid_string_static(sid), (unsigned int)id.gid ));
+ DEBUG(0,("wellknown_id_init: Attempting to overwrite old mapping with new.\n"));
+ return idmap_set_mapping(sid, id, flags);
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Initialize idmap withWellknown SIDs like Guest, that are necessary
+ to make samba run properly.
+***************************************************************************/
+
+BOOL idmap_init_wellknown_sids(void)
+{
+ const char *guest_account = lp_guestaccount();
+ struct passwd *pass;
+ GROUP_MAP *map=NULL;
+ int num_entries=0;
+ DOM_SID sid;
+ unid_t id;
+ fstring sid_string;
+
+ if (!(guest_account && *guest_account)) {
+ DEBUG(1, ("NULL guest account!?!?\n"));
+ return False;
+ }
+
+ pass = getpwnam_alloc(guest_account);
+ if (!pass) {
+ return False;
+ }
+
+ /* Fill in the SID for the guest account. */
+ id.uid = pass->pw_uid;
+ sid_copy(&sid, get_global_sam_sid());
+ sid_append_rid(&sid, DOMAIN_USER_RID_GUEST);
+
+ if (!NT_STATUS_IS_OK(wellknown_id_init(&sid, id, ID_USERID))) {
+ DEBUG(0, ("Failed to setup UID mapping for GUEST (%s) to (%u)\n",
+ sid_to_string(sid_string, &sid), (unsigned int)id.uid));
+ passwd_free(&pass);
+ return False;
+ }
+
+ /* check if DOMAIN_GROUP_RID_GUESTS SID is set, if not store the
+ * guest account gid as mapping */
+ id.gid = pass->pw_gid;
+ sid_copy(&sid, get_global_sam_sid());
+ sid_append_rid(&sid, DOMAIN_GROUP_RID_GUESTS);
+ if (!NT_STATUS_IS_OK(wellknown_id_init(&sid, id, ID_GROUPID))) {
+ DEBUG(0, ("Failed to setup GID mapping for Group DOMAIN GUESTS (%s) to (%u)\n",
+ sid_to_string(sid_string, &sid), (unsigned int)id.gid));
+ passwd_free(&pass);
+ return False;
+ }
+
+ passwd_free(&pass);
+ /* now fill in group mappings */
+ if(pdb_enum_group_mapping(SID_NAME_UNKNOWN, &map, &num_entries, ENUM_ONLY_MAPPED)) {
+ int i;
+
+ for (i = 0; i < num_entries; i++) {
+ id.gid = map[i].gid;
+ wellknown_id_init(&map[i].sid, id, ID_GROUPID);
+ }
+ SAFE_FREE(map);
+ }
+
+ /* Fill in the SID for the administrator account. */
+ id.uid = 0;
+ sid_copy(&sid, get_global_sam_sid());
+ sid_append_rid(&sid, DOMAIN_USER_RID_ADMIN);
+
+ if (!NT_STATUS_IS_OK(wellknown_id_init(&sid, id, ID_USERID))) {
+ DEBUG(0, ("Failed to setup UID mapping for ADMINISTRATOR (%s) to (%u)\n",
+ sid_to_string(sid_string, &sid), (unsigned int)id.uid));
+ return False;
+ }
+
+ return True;
+}
diff --git a/source/sam/interface.c b/source/sam/interface.c
new file mode 100644
index 00000000000..51ae561999c
--- /dev/null
+++ b/source/sam/interface.c
@@ -0,0 +1,1338 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett 2002
+ Copyright (C) Jelmer Vernooij 2002
+ Copyright (C) Stefan (metze) Metzmacher 2002
+ Copyright (C) Kai Krüger 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_SAM
+
+extern DOM_SID global_sid_Builtin;
+
+/** List of various built-in sam modules */
+
+const struct sam_init_function_entry builtin_sam_init_functions[] = {
+ { "plugin", sam_init_plugin },
+#ifdef HAVE_LDAP
+ { "ads", sam_init_ads },
+#endif
+ { "skel", sam_init_skel },
+ { NULL, NULL}
+};
+
+
+static NTSTATUS sam_get_methods_by_sid(const SAM_CONTEXT *context, SAM_METHODS **sam_method, const DOM_SID *domainsid)
+{
+ SAM_METHODS *tmp_methods;
+
+ DEBUG(5,("sam_get_methods_by_sid: %d\n", __LINE__));
+
+ /* invalid sam_context specified */
+ SAM_ASSERT(context && context->methods);
+
+ tmp_methods = context->methods;
+
+ while (tmp_methods) {
+ if (sid_equal(domainsid, &(tmp_methods->domain_sid)))
+ {
+ (*sam_method) = tmp_methods;
+ return NT_STATUS_OK;
+ }
+ tmp_methods = tmp_methods->next;
+ }
+
+ DEBUG(3,("sam_get_methods_by_sid: There is no backend specified for domain %s\n", sid_string_static(domainsid)));
+
+ return NT_STATUS_NO_SUCH_DOMAIN;
+}
+
+static NTSTATUS sam_get_methods_by_name(const SAM_CONTEXT *context, SAM_METHODS **sam_method, const char *domainname)
+{
+ SAM_METHODS *tmp_methods;
+
+ DEBUG(5,("sam_get_methods_by_name: %d\n", __LINE__));
+
+ /* invalid sam_context specified */
+ SAM_ASSERT(context && context->methods);
+
+ tmp_methods = context->methods;
+
+ while (tmp_methods) {
+ if (strequal(domainname, tmp_methods->domain_name))
+ {
+ (*sam_method) = tmp_methods;
+ return NT_STATUS_OK;
+ }
+ tmp_methods = tmp_methods->next;
+ }
+
+ DEBUG(3,("sam_get_methods_by_sid: There is no backend specified for domain %s\n", domainname));
+
+ return NT_STATUS_NO_SUCH_DOMAIN;
+}
+
+static NTSTATUS make_sam_methods(TALLOC_CTX *mem_ctx, SAM_METHODS **methods)
+{
+ *methods = talloc(mem_ctx, sizeof(SAM_METHODS));
+
+ if (!*methods) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCTP(*methods);
+
+ return NT_STATUS_OK;
+}
+
+/******************************************************************
+ Free and cleanup a sam context, any associated data and anything
+ that the attached modules might have associated.
+ *******************************************************************/
+
+void free_sam_context(SAM_CONTEXT **context)
+{
+ SAM_METHODS *sam_selected = (*context)->methods;
+
+ while (sam_selected) {
+ if (sam_selected->free_private_data) {
+ sam_selected->free_private_data(&(sam_selected->private_data));
+ }
+ sam_selected = sam_selected->next;
+ }
+
+ talloc_destroy((*context)->mem_ctx);
+ *context = NULL;
+}
+
+/******************************************************************
+ Make a backend_entry from scratch
+ *******************************************************************/
+
+static NTSTATUS make_backend_entry(SAM_BACKEND_ENTRY *backend_entry, char *sam_backend_string)
+{
+ char *tmp = NULL;
+ char *tmp_string = sam_backend_string;
+
+ DEBUG(5,("make_backend_entry: %d\n", __LINE__));
+
+ SAM_ASSERT(sam_backend_string && backend_entry);
+
+ backend_entry->module_name = sam_backend_string;
+
+ DEBUG(5,("makeing backend_entry for %s\n", backend_entry->module_name));
+
+ if ((tmp = strrchr(tmp_string, '|')) != NULL) {
+ DEBUGADD(20,("a domain name has been specified\n"));
+ *tmp = 0;
+ backend_entry->domain_name = smb_xstrdup(tmp + 1);
+ tmp_string = tmp + 1;
+ }
+
+ if ((tmp = strchr(tmp_string, ':')) != NULL) {
+ DEBUG(20,("options for the backend have been specified\n"));
+ *tmp = 0;
+ backend_entry->module_params = smb_xstrdup(tmp + 1);
+ tmp_string = tmp + 1;
+ }
+
+ if (backend_entry->domain_name == NULL) {
+ DEBUG(10,("make_backend_entry: no domain was specified for sam module %s. Using default domain %s\n",
+ backend_entry->module_name, lp_workgroup()));
+ backend_entry->domain_name = smb_xstrdup(lp_workgroup());
+ }
+
+ if ((backend_entry->domain_sid = (DOM_SID *)malloc(sizeof(DOM_SID))) == NULL) {
+ DEBUG(0,("make_backend_entry: failed to malloc domain_sid\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DEBUG(10,("looking up sid for domain %s\n", backend_entry->domain_name));
+
+ if (!secrets_fetch_domain_sid(backend_entry->domain_name, backend_entry->domain_sid)) {
+ DEBUG(2,("make_backend_entry: There is no SID stored for domain %s. Creating a new one.\n",
+ backend_entry->domain_name));
+ DEBUG(0, ("FIXME in %s:%d\n", __FILE__, __LINE__));
+ ZERO_STRUCTP(backend_entry->domain_sid);
+ }
+
+ DEBUG(5,("make_backend_entry: module name: %s, module parameters: %s, domain name: %s, domain sid: %s\n",
+ backend_entry->module_name, backend_entry->module_params, backend_entry->domain_name, sid_string_static(backend_entry->domain_sid)));
+
+ return NT_STATUS_OK;
+}
+
+/******************************************************************
+ create sam_methods struct based on sam_backend_entry
+ *****************************************************************/
+
+static NTSTATUS make_sam_methods_backend_entry(SAM_CONTEXT *context, SAM_METHODS **methods_ptr, SAM_BACKEND_ENTRY *backend_entry)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ SAM_METHODS *methods;
+ int i;
+
+ DEBUG(5,("make_sam_methods_backend_entry: %d\n", __LINE__));
+
+ if (!NT_STATUS_IS_OK(nt_status = make_sam_methods(context->mem_ctx, methods_ptr))) {
+ return nt_status;
+ }
+
+ methods = *methods_ptr;
+ methods->backendname = talloc_strdup(context->mem_ctx, backend_entry->module_name);
+ methods->domain_name = talloc_strdup(context->mem_ctx, backend_entry->domain_name);
+ sid_copy(&methods->domain_sid, backend_entry->domain_sid);
+ methods->parent = context;
+
+ DEBUG(5,("Attempting to find sam backend %s\n", backend_entry->module_name));
+ for (i = 0; builtin_sam_init_functions[i].module_name; i++)
+ {
+ if (strequal(builtin_sam_init_functions[i].module_name, backend_entry->module_name))
+ {
+ DEBUG(5,("Found sam backend %s (at pos %d)\n", backend_entry->module_name, i));
+ DEBUGADD(5,("initialising it with options=%s for domain %s\n", backend_entry->module_params, sid_string_static(backend_entry->domain_sid)));
+ nt_status = builtin_sam_init_functions[i].init(methods, backend_entry->module_params);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(5,("sam backend %s has a valid init\n", backend_entry->module_name));
+ } else {
+ DEBUG(2,("sam backend %s did not correctly init (error was %s)\n",
+ backend_entry->module_name, nt_errstr(nt_status)));
+ }
+ return nt_status;
+ }
+ }
+
+ DEBUG(2,("could not find backend %s\n", backend_entry->module_name));
+
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+static NTSTATUS sam_context_check_default_backends(SAM_CONTEXT *context)
+{
+ SAM_BACKEND_ENTRY entry;
+ DOM_SID *global_sam_sid = get_global_sam_sid(); /* lp_workgroup doesn't play nicely with multiple domains */
+ SAM_METHODS *methods, *tmpmethods;
+ NTSTATUS ntstatus;
+
+ DEBUG(5,("sam_context_check_default_backends: %d\n", __LINE__));
+
+ /* Make sure domain lp_workgroup() is available */
+
+ ntstatus = sam_get_methods_by_sid(context, &methods, &global_sid_Builtin);
+
+ if (NT_STATUS_EQUAL(ntstatus, NT_STATUS_NO_SUCH_DOMAIN)) {
+ DEBUG(4,("There was no backend specified for domain %s(%s); using %s\n",
+ lp_workgroup(), sid_string_static(global_sam_sid), SAM_DEFAULT_BACKEND));
+
+ SAM_ASSERT(global_sam_sid);
+
+ entry.module_name = SAM_DEFAULT_BACKEND;
+ entry.module_params = NULL;
+ entry.domain_name = lp_workgroup();
+ entry.domain_sid = (DOM_SID *)malloc(sizeof(DOM_SID));
+ sid_copy(entry.domain_sid, global_sam_sid);
+
+ if (!NT_STATUS_IS_OK(ntstatus = make_sam_methods_backend_entry(context, &methods, &entry))) {
+ DEBUG(4,("make_sam_methods_backend_entry failed\n"));
+ return ntstatus;
+ }
+
+ DLIST_ADD_END(context->methods, methods, tmpmethods);
+
+ } else if (!NT_STATUS_IS_OK(ntstatus)) {
+ DEBUG(2, ("sam_get_methods_by_sid failed for %s\n", lp_workgroup()));
+ return ntstatus;
+ }
+
+ /* Make sure the BUILTIN domain is available */
+
+ ntstatus = sam_get_methods_by_sid(context, &methods, global_sam_sid);
+
+ if (NT_STATUS_EQUAL(ntstatus, NT_STATUS_NO_SUCH_DOMAIN)) {
+ DEBUG(4,("There was no backend specified for domain BUILTIN; using %s\n",
+ SAM_DEFAULT_BACKEND));
+ entry.module_name = SAM_DEFAULT_BACKEND;
+ entry.module_params = NULL;
+ entry.domain_name = "BUILTIN";
+ entry.domain_sid = (DOM_SID *)malloc(sizeof(DOM_SID));
+ sid_copy(entry.domain_sid, &global_sid_Builtin);
+
+ if (!NT_STATUS_IS_OK(ntstatus = make_sam_methods_backend_entry(context, &methods, &entry))) {
+ DEBUG(4,("make_sam_methods_backend_entry failed\n"));
+ return ntstatus;
+ }
+
+ DLIST_ADD_END(context->methods, methods, tmpmethods);
+ } else if (!NT_STATUS_IS_OK(ntstatus)) {
+ DEBUG(2, ("sam_get_methods_by_sid failed for BUILTIN\n"));
+ return ntstatus;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS check_duplicate_backend_entries(SAM_BACKEND_ENTRY **backend_entries, int *nBackends)
+{
+ int i, j;
+
+ DEBUG(5,("check_duplicate_backend_entries: %d\n", __LINE__));
+
+ for (i = 0; i < *nBackends; i++) {
+ for (j = i + 1; j < *nBackends; j++) {
+ if (sid_equal((*backend_entries)[i].domain_sid, (*backend_entries)[j].domain_sid)) {
+ DEBUG(0,("two backend modules claim the same domain %s\n",
+ sid_string_static((*backend_entries)[j].domain_sid)));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS make_sam_context_list(SAM_CONTEXT **context, char **sam_backends_param)
+{
+ int i = 0, j = 0;
+ SAM_METHODS *curmethods, *tmpmethods;
+ int nBackends = 0;
+ SAM_BACKEND_ENTRY *backends = NULL;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(5,("make_sam_context_from_conf: %d\n", __LINE__));
+
+ if (!sam_backends_param) {
+ DEBUG(1, ("no SAM backeds specified!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = make_sam_context(context))) {
+ DEBUG(4,("make_sam_context failed\n"));
+ return nt_status;
+ }
+
+ while (sam_backends_param[nBackends])
+ nBackends++;
+
+ DEBUG(6,("There are %d domains listed with their backends\n", nBackends));
+
+ if ((backends = (SAM_BACKEND_ENTRY *)malloc(sizeof(*backends)*nBackends)) == NULL) {
+ DEBUG(0,("make_sam_context_list: failed to allocate backends\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ memset(backends, '\0', sizeof(*backends)*nBackends);
+
+ for (i = 0; i < nBackends; i++) {
+ DEBUG(8,("processing %s\n",sam_backends_param[i]));
+ if (!NT_STATUS_IS_OK(nt_status = make_backend_entry(&backends[i], sam_backends_param[i]))) {
+ DEBUG(4,("make_backend_entry failed\n"));
+ for (j = 0; j < nBackends; j++) SAFE_FREE(backends[j].domain_sid);
+ SAFE_FREE(backends);
+ free_sam_context(context);
+ return nt_status;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = check_duplicate_backend_entries(&backends, &nBackends))) {
+ DEBUG(4,("check_duplicate_backend_entries failed\n"));
+ for (j = 0; j < nBackends; j++) SAFE_FREE(backends[j].domain_sid);
+ SAFE_FREE(backends);
+ free_sam_context(context);
+ return nt_status;
+ }
+
+ for (i = 0; i < nBackends; i++) {
+ if (!NT_STATUS_IS_OK(nt_status = make_sam_methods_backend_entry(*context, &curmethods, &backends[i]))) {
+ DEBUG(4,("make_sam_methods_backend_entry failed\n"));
+ for (j = 0; j < nBackends; j++) SAFE_FREE(backends[j].domain_sid);
+ SAFE_FREE(backends);
+ free_sam_context(context);
+ return nt_status;
+ }
+ DLIST_ADD_END((*context)->methods, curmethods, tmpmethods);
+ }
+
+ for (i = 0; i < nBackends; i++) SAFE_FREE(backends[i].domain_sid);
+
+ SAFE_FREE(backends);
+ return NT_STATUS_OK;
+}
+
+/******************************************************************
+ Make a sam_context from scratch.
+ *******************************************************************/
+
+NTSTATUS make_sam_context(SAM_CONTEXT **context)
+{
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_init("sam_context internal allocation context");
+
+ if (!mem_ctx) {
+ DEBUG(0, ("make_sam_context: talloc init failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *context = talloc(mem_ctx, sizeof(**context));
+ if (!*context) {
+ DEBUG(0, ("make_sam_context: talloc failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCTP(*context);
+
+ (*context)->mem_ctx = mem_ctx;
+
+ (*context)->free_fn = free_sam_context;
+
+ return NT_STATUS_OK;
+}
+
+/******************************************************************
+ Return an already initialised sam_context, to facilitate backward
+ compatibility (see functions below).
+ *******************************************************************/
+
+static struct sam_context *sam_get_static_context(BOOL reload)
+{
+ static SAM_CONTEXT *sam_context = NULL;
+
+ if ((sam_context) && (reload)) {
+ sam_context->free_fn(&sam_context);
+ sam_context = NULL;
+ }
+
+ if (!sam_context) {
+ if (!NT_STATUS_IS_OK(make_sam_context_list(&sam_context, lp_sam_backend()))) {
+ DEBUG(4,("make_sam_context_list failed\n"));
+ return NULL;
+ }
+
+ /* Make sure the required domains (default domain, builtin) are available */
+ if (!NT_STATUS_IS_OK(sam_context_check_default_backends(sam_context))) {
+ DEBUG(4,("sam_context_check_default_backends failed\n"));
+ return NULL;
+ }
+ }
+
+ return sam_context;
+}
+
+/***************************************************************
+ Initialize the static context (at smbd startup etc).
+
+ If uninitialised, context will auto-init on first use.
+ ***************************************************************/
+
+BOOL initialize_sam(BOOL reload)
+{
+ return (sam_get_static_context(reload) != NULL);
+}
+
+
+/**************************************************************
+ External API. This is what the rest of the world calls...
+***************************************************************/
+
+/******************************************************************
+ sam_* functions are used to link the external SAM interface
+ with the internal backends. These functions lookup the appropriate
+ backends for the domain and pass on to the function in sam_methods
+ in the selected backend
+
+ When the context parmater is NULL, the default is used.
+ *******************************************************************/
+
+#define SAM_SETUP_CONTEXT if (!context) \
+ context = sam_get_static_context(False);\
+ if (!context) {\
+ return NT_STATUS_UNSUCCESSFUL; \
+ }\
+
+
+
+NTSTATUS sam_get_sec_desc(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const DOM_SID *sid, SEC_DESC **sd)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_get_sec_desc: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, sid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_get_sec_desc) {
+ DEBUG(3, ("sam_get_sec_desc: sam_methods of the domain did not specify sam_get_sec_desc\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_sec_desc(tmp_methods, access_token, sid, sd))) {
+ DEBUG(4,("sam_get_sec_desc for %s in backend %s failed\n", sid_string_static(sid), tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_sec_desc(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const DOM_SID *sid, const SEC_DESC *sd)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_set_sec_desc: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, sid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_set_sec_desc) {
+ DEBUG(3, ("sam_set_sec_desc: sam_methods of the domain did not specify sam_set_sec_desc\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_set_sec_desc(tmp_methods, access_token, sid, sd))) {
+ DEBUG(4,("sam_set_sec_desc for %s in backend %s failed\n", sid_string_static(sid), tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+NTSTATUS sam_lookup_name(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const char *domain, const char *name, DOM_SID *sid, uint32 *type)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_lookup_name: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_name(context, &tmp_methods, domain))) {
+ DEBUG(4,("sam_get_methods_by_name failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_lookup_name) {
+ DEBUG(3, ("sam_lookup_name: sam_methods of the domain did not specify sam_lookup_name\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_lookup_name(tmp_methods, access_token, name, sid, type))) {
+ DEBUG(4,("sam_lookup_name for %s\\%s in backend %s failed\n",
+ tmp_methods->domain_name, name, tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_lookup_sid(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, TALLOC_CTX *mem_ctx, const DOM_SID *sid, char **name, uint32 *type)
+{
+ SAM_METHODS *tmp_methods;
+ uint32 rid;
+ NTSTATUS nt_status;
+ DOM_SID domainsid;
+
+ DEBUG(5,("sam_lookup_sid: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ sid_copy(&domainsid, sid);
+ if (!sid_split_rid(&domainsid, &rid)) {
+ DEBUG(3,("sam_lookup_sid: failed to split the sid\n"));
+ return NT_STATUS_INVALID_SID;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, &domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_lookup_sid) {
+ DEBUG(3, ("sam_lookup_sid: sam_methods of the domain did not specify sam_lookup_sid\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_lookup_sid(tmp_methods, access_token, mem_ctx, sid, name, type))) {
+ DEBUG(4,("sam_lookup_name for %s in backend %s failed\n",
+ sid_string_static(sid), tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+NTSTATUS sam_update_domain(const SAM_CONTEXT *context, const SAM_DOMAIN_HANDLE *domain)
+{
+ const SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_update_domain: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid domain specified */
+ SAM_ASSERT(domain && domain->current_sam_methods);
+
+ tmp_methods = domain->current_sam_methods;
+
+ if (!tmp_methods->sam_update_domain) {
+ DEBUG(3, ("sam_update_domain: sam_methods of the domain did not specify sam_update_domain\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_update_domain(tmp_methods, domain))){
+ DEBUG(4,("sam_update_domain in backend %s failed\n",
+ tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_enum_domains(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, int32 *domain_count, DOM_SID **domains, char ***domain_names)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ SEC_DESC *sd;
+ size_t sd_size;
+ uint32 acc_granted;
+ int i = 0;
+
+ DEBUG(5,("sam_enum_domains: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid parmaters specified */
+ SAM_ASSERT(domain_count && domains && domain_names);
+
+ if (!NT_STATUS_IS_OK(nt_status = samr_make_sam_obj_sd(context->mem_ctx, &sd, &sd_size))) {
+ DEBUG(4,("samr_make_sam_obj_sd failed\n"));
+ return nt_status;
+ }
+
+ if (!se_access_check(sd, access_token, SA_RIGHT_SAM_ENUM_DOMAINS, &acc_granted, &nt_status)) {
+ DEBUG(3,("sam_enum_domains: ACCESS DENIED\n"));
+ return nt_status;
+ }
+
+ tmp_methods= context->methods;
+ *domain_count = 0;
+
+ while (tmp_methods) {
+ (*domain_count)++;
+ tmp_methods= tmp_methods->next;
+ }
+
+ DEBUG(6,("sam_enum_domains: enumerating %d domains\n", (*domain_count)));
+
+ tmp_methods = context->methods;
+
+ if (((*domains) = malloc( sizeof(DOM_SID) * (*domain_count))) == NULL) {
+ DEBUG(0,("sam_enum_domains: Out of memory allocating domain SID list\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (((*domain_names) = malloc( sizeof(char*) * (*domain_count))) == NULL) {
+ DEBUG(0,("sam_enum_domains: Out of memory allocating domain name list\n"));
+ SAFE_FREE((*domains));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ while (tmp_methods) {
+ DEBUGADD(7,(" [%d] %s: %s\n", i, tmp_methods->domain_name, sid_string_static(&tmp_methods->domain_sid)));
+ sid_copy(domains[i],&tmp_methods->domain_sid);
+ *domain_names[i] = smb_xstrdup(tmp_methods->domain_name);
+ i++;
+ tmp_methods= tmp_methods->next;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_lookup_domain(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const char *domain, DOM_SID **domainsid)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ SEC_DESC *sd;
+ size_t sd_size;
+ uint32 acc_granted;
+
+ DEBUG(5,("sam_lookup_domain: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid paramters */
+ SAM_ASSERT(access_token && domain && domainsid);
+
+ if (!NT_STATUS_IS_OK(nt_status = samr_make_sam_obj_sd(context->mem_ctx, &sd, &sd_size))) {
+ DEBUG(4,("samr_make_sam_obj_sd failed\n"));
+ return nt_status;
+ }
+
+ if (!se_access_check(sd, access_token, SA_RIGHT_SAM_OPEN_DOMAIN, &acc_granted, &nt_status)) {
+ DEBUG(3,("sam_lookup_domain: ACCESS DENIED\n"));
+ return nt_status;
+ }
+
+ tmp_methods= context->methods;
+
+ while (tmp_methods) {
+ if (strcmp(domain, tmp_methods->domain_name) == 0) {
+ (*domainsid) = (DOM_SID *)malloc(sizeof(DOM_SID));
+ sid_copy((*domainsid), &tmp_methods->domain_sid);
+ return NT_STATUS_OK;
+ }
+ tmp_methods= tmp_methods->next;
+ }
+
+ return NT_STATUS_NO_SUCH_DOMAIN;
+}
+
+
+NTSTATUS sam_get_domain_by_sid(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *domainsid, SAM_DOMAIN_HANDLE **domain)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_get_domain_by_sid: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ SAM_ASSERT(access_token && domainsid && domain);
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_get_domain_handle) {
+ DEBUG(3, ("sam_get_domain_by_sid: sam_methods of the domain did not specify sam_get_domain_handle\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_domain_handle(tmp_methods, access_token, access_desired, domain))) {
+ DEBUG(4,("sam_get_domain_handle for %s in backend %s failed\n",
+ sid_string_static(domainsid), tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_create_account(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *domainsid, const char *account_name, uint16 acct_ctrl, SAM_ACCOUNT_HANDLE **account)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_create_account: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid parmaters */
+ SAM_ASSERT(access_token && domainsid && account_name && account);
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_create_account) {
+ DEBUG(3, ("sam_create_account: sam_methods of the domain did not specify sam_create_account\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_create_account(tmp_methods, access_token, access_desired, account_name, acct_ctrl, account))) {
+ DEBUG(4,("sam_create_account in backend %s failed\n",
+ tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_add_account(const SAM_CONTEXT *context, const SAM_ACCOUNT_HANDLE *account)
+{
+ DOM_SID domainsid;
+ const DOM_SID *accountsid;
+ SAM_METHODS *tmp_methods;
+ uint32 rid;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_add_account: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid parmaters */
+ SAM_ASSERT(account);
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_account_sid(account, &accountsid))) {
+ DEBUG(0,("Can't get account SID\n"));
+ return nt_status;
+ }
+
+ sid_copy(&domainsid, accountsid);
+ if (!sid_split_rid(&domainsid, &rid)) {
+ DEBUG(3,("sam_get_account_by_sid: failed to split the sid\n"));
+ return NT_STATUS_INVALID_SID;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, &domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_add_account) {
+ DEBUG(3, ("sam_add_account: sam_methods of the domain did not specify sam_add_account\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_add_account(tmp_methods, account))){
+ DEBUG(4,("sam_add_account in backend %s failed\n",
+ tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_update_account(const SAM_CONTEXT *context, const SAM_ACCOUNT_HANDLE *account)
+{
+ const SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_update_account: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid account specified */
+ SAM_ASSERT(account && account->current_sam_methods);
+
+ tmp_methods = account->current_sam_methods;
+
+ if (!tmp_methods->sam_update_account) {
+ DEBUG(3, ("sam_update_account: sam_methods of the domain did not specify sam_update_account\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_update_account(tmp_methods, account))){
+ DEBUG(4,("sam_update_account in backend %s failed\n",
+ tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_delete_account(const SAM_CONTEXT *context, const SAM_ACCOUNT_HANDLE *account)
+{
+ const SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_delete_account: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid account specified */
+ SAM_ASSERT(account && account->current_sam_methods);
+
+ tmp_methods = account->current_sam_methods;
+
+ if (!tmp_methods->sam_delete_account) {
+ DEBUG(3, ("sam_delete_account: sam_methods of the domain did not specify sam_delete_account\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_delete_account(tmp_methods, account))){
+ DEBUG(4,("sam_delete_account in backend %s failed\n",
+ tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_enum_accounts(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const DOM_SID *domainsid, uint16 acct_ctrl, int32 *account_count, SAM_ACCOUNT_ENUM **accounts)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_enum_accounts: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ SAM_ASSERT(access_token && domainsid && account_count && accounts);
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_enum_accounts) {
+ DEBUG(3, ("sam_enum_accounts: sam_methods of the domain did not specify sam_enum_accounts\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_enum_accounts(tmp_methods, access_token, acct_ctrl, account_count, accounts))) {
+ DEBUG(4,("sam_enum_accounts for domain %s in backend %s failed\n",
+ tmp_methods->domain_name, tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+NTSTATUS sam_get_account_by_sid(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *accountsid, SAM_ACCOUNT_HANDLE **account)
+{
+ SAM_METHODS *tmp_methods;
+ uint32 rid;
+ DOM_SID domainsid;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_get_account_by_sid: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ SAM_ASSERT(access_token && accountsid && account);
+
+ sid_copy(&domainsid, accountsid);
+ if (!sid_split_rid(&domainsid, &rid)) {
+ DEBUG(3,("sam_get_account_by_sid: failed to split the sid\n"));
+ return NT_STATUS_INVALID_SID;
+ }
+
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, &domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_get_account_by_sid) {
+ DEBUG(3, ("sam_get_account_by_sid: sam_methods of the domain did not specify sam_get_account_by_sid\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_account_by_sid(tmp_methods, access_token, access_desired, accountsid, account))) {
+ DEBUG(4,("sam_get_account_by_sid for %s in backend %s failed\n",
+ sid_string_static(accountsid), tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_by_name(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const char *domain, const char *name, SAM_ACCOUNT_HANDLE **account)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_get_account_by_name: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ SAM_ASSERT(access_token && domain && name && account);
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_name(context, &tmp_methods, domain))) {
+ DEBUG(4,("sam_get_methods_by_name failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_get_account_by_name) {
+ DEBUG(3, ("sam_get_account_by_name: sam_methods of the domain did not specify sam_get_account_by_name\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_account_by_name(tmp_methods, access_token, access_desired, name, account))) {
+ DEBUG(4,("sam_get_account_by_name for %s\\%s in backend %s failed\n",
+ domain, name, tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_create_group(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *domainsid, const char *group_name, uint16 group_ctrl, SAM_GROUP_HANDLE **group)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_create_group: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ SAM_ASSERT(access_token && domainsid && group_name && group);
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_create_group) {
+ DEBUG(3, ("sam_create_group: sam_methods of the domain did not specify sam_create_group\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_create_group(tmp_methods, access_token, access_desired, group_name, group_ctrl, group))) {
+ DEBUG(4,("sam_create_group in backend %s failed\n",
+ tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_add_group(const SAM_CONTEXT *context, const SAM_GROUP_HANDLE *group)
+{
+ DOM_SID domainsid;
+ const DOM_SID *groupsid;
+ SAM_METHODS *tmp_methods;
+ uint32 rid;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_add_group: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ SAM_ASSERT(group);
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_group_sid(group, &groupsid))) {
+ DEBUG(0,("Can't get group SID\n"));
+ return nt_status;
+ }
+
+ sid_copy(&domainsid, groupsid);
+ if (!sid_split_rid(&domainsid, &rid)) {
+ DEBUG(3,("sam_get_group_by_sid: failed to split the sid\n"));
+ return NT_STATUS_INVALID_SID;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, &domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_add_group) {
+ DEBUG(3, ("sam_add_group: sam_methods of the domain did not specify sam_add_group\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_add_group(tmp_methods, group))){
+ DEBUG(4,("sam_add_group in backend %s failed\n",
+ tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_update_group(const SAM_CONTEXT *context, const SAM_GROUP_HANDLE *group)
+{
+ const SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_update_group: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid group specified */
+ SAM_ASSERT(group && group->current_sam_methods);
+
+ tmp_methods = group->current_sam_methods;
+
+ if (!tmp_methods->sam_update_group) {
+ DEBUG(3, ("sam_update_group: sam_methods of the domain did not specify sam_update_group\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_update_group(tmp_methods, group))){
+ DEBUG(4,("sam_update_group in backend %s failed\n",
+ tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_delete_group(const SAM_CONTEXT *context, const SAM_GROUP_HANDLE *group)
+{
+ const SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_delete_group: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid group specified */
+ SAM_ASSERT(group && group->current_sam_methods);
+
+ tmp_methods = group->current_sam_methods;
+
+ if (!tmp_methods->sam_delete_group) {
+ DEBUG(3, ("sam_delete_group: sam_methods of the domain did not specify sam_delete_group\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_delete_group(tmp_methods, group))){
+ DEBUG(4,("sam_delete_group in backend %s failed\n",
+ tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_enum_groups(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const DOM_SID *domainsid, uint16 group_ctrl, uint32 *groups_count, SAM_GROUP_ENUM **groups)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_enum_groups: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ SAM_ASSERT(access_token && domainsid && groups_count && groups);
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_enum_accounts) {
+ DEBUG(3, ("sam_enum_groups: sam_methods of the domain did not specify sam_enum_groups\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_enum_groups(tmp_methods, access_token, group_ctrl, groups_count, groups))) {
+ DEBUG(4,("sam_enum_groups for domain %s in backend %s failed\n",
+ tmp_methods->domain_name, tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_group_by_sid(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *groupsid, SAM_GROUP_HANDLE **group)
+{
+ SAM_METHODS *tmp_methods;
+ uint32 rid;
+ NTSTATUS nt_status;
+ DOM_SID domainsid;
+
+ DEBUG(5,("sam_get_group_by_sid: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ SAM_ASSERT(access_token && groupsid && group);
+
+ sid_copy(&domainsid, groupsid);
+ if (!sid_split_rid(&domainsid, &rid)) {
+ DEBUG(3,("sam_get_group_by_sid: failed to split the sid\n"));
+ return NT_STATUS_INVALID_SID;
+ }
+
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, &domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_get_group_by_sid) {
+ DEBUG(3, ("sam_get_group_by_sid: sam_methods of the domain did not specify sam_get_group_by_sid\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_group_by_sid(tmp_methods, access_token, access_desired, groupsid, group))) {
+ DEBUG(4,("sam_get_group_by_sid for %s in backend %s failed\n",
+ sid_string_static(groupsid), tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_group_by_name(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const char *domain, const char *name, SAM_GROUP_HANDLE **group)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_get_group_by_name: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ SAM_ASSERT(access_token && domain && name && group);
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_name(context, &tmp_methods, domain))) {
+ DEBUG(4,("sam_get_methods_by_name failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_get_group_by_name) {
+ DEBUG(3, ("sam_get_group_by_name: sam_methods of the domain did not specify sam_get_group_by_name\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_group_by_name(tmp_methods, access_token, access_desired, name, group))) {
+ DEBUG(4,("sam_get_group_by_name for %s\\%s in backend %s failed\n",
+ domain, name, tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_add_member_to_group(const SAM_CONTEXT *context, const SAM_GROUP_HANDLE *group, const SAM_GROUP_MEMBER *member)
+{
+ const SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid group or member specified */
+ SAM_ASSERT(group && group->current_sam_methods && member);
+
+ tmp_methods = group->current_sam_methods;
+
+ if (!tmp_methods->sam_add_member_to_group) {
+ DEBUG(3, ("sam_add_member_to_group: sam_methods of the domain did not specify sam_add_member_to_group\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_add_member_to_group(tmp_methods, group, member))) {
+ DEBUG(4,("sam_add_member_to_group in backend %s failed\n", tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+
+}
+
+NTSTATUS sam_delete_member_from_group(const SAM_CONTEXT *context, const SAM_GROUP_HANDLE *group, const SAM_GROUP_MEMBER *member)
+{
+ const SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid group or member specified */
+ SAM_ASSERT(group && group->current_sam_methods && member);
+
+ tmp_methods = group->current_sam_methods;
+
+ if (!tmp_methods->sam_delete_member_from_group) {
+ DEBUG(3, ("sam_delete_member_from_group: sam_methods of the domain did not specify sam_delete_member_from_group\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_delete_member_from_group(tmp_methods, group, member))) {
+ DEBUG(4,("sam_delete_member_from_group in backend %s failed\n", tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_enum_groupmembers(const SAM_CONTEXT *context, const SAM_GROUP_HANDLE *group, uint32 *members_count, SAM_GROUP_MEMBER **members)
+{
+ const SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid group specified */
+ SAM_ASSERT(group && group->current_sam_methods && members_count && members);
+
+ tmp_methods = group->current_sam_methods;
+
+ if (!tmp_methods->sam_enum_groupmembers) {
+ DEBUG(3, ("sam_enum_groupmembers: sam_methods of the domain did not specify sam_enum_group_members\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_enum_groupmembers(tmp_methods, group, members_count, members))) {
+ DEBUG(4,("sam_enum_groupmembers in backend %s failed\n", tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_groups_of_sid(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const DOM_SID **sids, uint16 group_ctrl, uint32 *group_count, SAM_GROUP_ENUM **groups)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ uint32 tmp_group_count;
+ SAM_GROUP_ENUM *tmp_groups;
+
+ DEBUG(5,("sam_get_groups_of_sid: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid sam_context specified */
+ SAM_ASSERT(access_token && sids && context && context->methods);
+
+ *group_count = 0;
+
+ *groups = NULL;
+
+ tmp_methods= context->methods;
+
+ while (tmp_methods) {
+ DEBUG(5,("getting groups from domain \n"));
+ if (!tmp_methods->sam_get_groups_of_sid) {
+ DEBUG(3, ("sam_get_groups_of_sid: sam_methods of domain did not specify sam_get_groups_of_sid\n"));
+ SAFE_FREE(*groups);
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_groups_of_sid(tmp_methods, access_token, sids, group_ctrl, &tmp_group_count, &tmp_groups))) {
+ DEBUG(4,("sam_get_groups_of_sid in backend %s failed\n", tmp_methods->backendname));
+ SAFE_FREE(*groups);
+ return nt_status;
+ }
+
+ *groups = Realloc(*groups, ((*group_count) + tmp_group_count) * sizeof(SAM_GROUP_ENUM));
+
+ memcpy(&(*groups)[*group_count], tmp_groups, tmp_group_count);
+
+ SAFE_FREE(tmp_groups);
+
+ *group_count += tmp_group_count;
+
+ tmp_methods = tmp_methods->next;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
diff --git a/source/script/.cvsignore b/source/script/.cvsignore
new file mode 100644
index 00000000000..0464ca23351
--- /dev/null
+++ b/source/script/.cvsignore
@@ -0,0 +1,2 @@
+findsmb
+gen-8bit-gap.sh
diff --git a/source/script/build_env.sh b/source/script/build_env.sh
new file mode 100755
index 00000000000..eb54f37aeda
--- /dev/null
+++ b/source/script/build_env.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+if [ $# -lt 3 ]
+then
+ echo "Usage: $0 srcdir builddir compiler"
+ exit 1
+fi
+
+uname=`uname -a`
+date=`date`
+srcdir=$1
+builddir=$2
+compiler=$3
+
+if [ ! "x$USER" = "x" ]; then
+ whoami=$USER
+else
+ if [ ! "x$LOGNAME" = "x" ]; then
+ whoami=$LOGNAME
+ else
+ whoami=`whoami || id -un`
+ fi
+fi
+
+host=`hostname`
+
+cat <<EOF
+/* This file is automatically generated with "make include/build_env.h". DO NOT EDIT */
+
+#ifndef _BUILD_ENV_H
+#define _BUILD_ENV_H
+
+#define BUILD_ENV_UNAME "${uname}"
+#define BUILD_ENV_DATE "${date}"
+#define BUILD_ENV_SRCDIR "${srcdir}"
+#define BUILD_ENV_BUILDDIR "${builddir}"
+#define BUILD_ENV_USER "${whoami}"
+#define BUILD_ENV_HOST "${host}"
+#define BUILD_ENV_COMPILER "${compiler}"
+#endif /* _BUILD_ENV_H */
+EOF
diff --git a/source/script/creategroup b/source/script/creategroup
new file mode 100755
index 00000000000..01fb0659444
--- /dev/null
+++ b/source/script/creategroup
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# Example script for 'add group command'. Handle weird NT group
+# names. First attempt to create the group directly, if that fails
+# then create a random group and print the numeric group id.
+#
+# Note that this is only an example and assumes /dev/urandom.
+#
+# Volker
+
+GROUPNAME="$1"
+ITERS=0
+
+while ! /usr/sbin/groupadd "$GROUPNAME" > /dev/null 2>&1
+do
+ # we had difficulties creating that group. Maybe the name was
+ # too weird, or it already existed. Create a random name.
+ GROUPNAME=nt-$(dd if=/dev/urandom bs=16 count=1 2>/dev/null | md5sum | cut -b 1-5)
+ ITERS=$(expr "$ITERS" + 1)
+ if [ "$ITERS" -gt 10 ]
+ then
+ # Too many attempts
+ exit 1
+ fi
+done
+
+getent group | grep ^"$GROUPNAME": | cut -d : -f 3
diff --git a/source/script/extract_allparms.sh b/source/script/extract_allparms.sh
new file mode 100755
index 00000000000..f16068b3fdc
--- /dev/null
+++ b/source/script/extract_allparms.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+grep '{".*P_[GL]' param/loadparm.c | sed -e 's/&.*$//g' -e 's/",.*P_LOCAL.*$/ S/' -e 's/",.*P_GLOBAL.*$/ G/' -e 's/^ .*{"//g' | sort -f
diff --git a/source/script/findsmb.in b/source/script/findsmb.in
new file mode 100755
index 00000000000..546cf8ce7b4
--- /dev/null
+++ b/source/script/findsmb.in
@@ -0,0 +1,161 @@
+#!@PERL@
+#
+# Prints info on all smb responding machines on a subnet.
+# This script needs to be run on a machine without nmbd running and be
+# run as root to get correct info from WIN95 clients.
+#
+# syntax:
+# findsmb [-d|-D] [-r] [subnet broadcast address]
+#
+# with no agrument it will list machines on the current subnet
+#
+# There will be a "+" in front of the workgroup name for machines that are
+# local master browsers for that workgroup. There will be an "*" in front
+# of the workgroup name for machines that are the domain master browser for
+# that workgroup.
+#
+# Options:
+#
+# -d|-D enable debug
+# -r add -r option to nmblookup when finding netbios name
+#
+
+$SAMBABIN = "@prefix@/bin";
+
+for ($i = 0; $i < 2; $i++) { # test for -d and -r options
+ $_ = shift;
+ if (m/-d|-D/) {
+ $DEBUG = 1;
+ } elsif (m/-r/) {
+ $R_OPTION = "-r";
+ }
+}
+
+if ($_) { # set broadcast address if it was specified
+ $BCAST = "-B $_";
+}
+
+
+######################################################################
+# do numeric sort on last field of IP address
+sub ipsort
+{
+ @t1 = split(/\./,$a);
+ @t2 = split(/\./,$b);
+ @t1[3] <=> @t2[3];
+}
+######################################################################
+
+# look for all machines that respond to a name lookup
+
+open(NMBLOOKUP,"$SAMBABIN/nmblookup $BCAST '*' --debuglevel=0|") ||
+ die("Can't run nmblookup '*'.\n");
+
+# get rid of all lines that are not a response IP address,
+# strip everything but IP address and sort by last field in address
+
+@ipaddrs = sort ipsort grep(s/ \*<00>.*$//,<NMBLOOKUP>);
+
+# print header info
+print "\n *=DMB\n";
+print " +=LMB\n";
+print "IP ADDR NETBIOS NAME WORKGROUP/OS/VERSION $BCAST\n";
+print "---------------------------------------------------------------------\n";
+
+foreach $ip (@ipaddrs) # loop through each IP address found
+{
+ $ip =~ s/\n//; # strip newline from IP address
+
+ # find the netbios names registered by each machine
+
+ open(NMBLOOKUP,"$SAMBABIN/nmblookup $R_OPTION -A $ip --debuglevel=0|") ||
+ die("Can't get nmb name list.\n");
+ @nmblookup = <NMBLOOKUP>;
+ close NMBLOOKUP;
+
+ # get the first <00> name
+
+ @name = grep(/<00>/,@nmblookup);
+ $_ = @name[0];
+
+ if ($_) { # we have a netbios name
+ if (/GROUP/) { # is it a group name
+ ($name, $aliases, $type, $length, @addresses) =
+ gethostbyaddr(pack('C4',split('\.',$ip)),2);
+ if (! $name) { # could not get name
+ $name = "unknown nis name";
+ }
+ } else {
+ # The Netbios name can contain lot of characters also '<' '>'
+ # and spaces. The follwing cure inside name space but not
+ # names starting or ending with spaces
+ /(.{1,15})\s+<00>\s+/;
+ $name = $1;
+ $name =~ s/^\s+//g;
+ }
+
+ # do an smbclient command on the netbios name.
+
+ if ( "$name" ) {
+ open(SMB,"$SAMBABIN/smbclient -L $name -I $ip -N --debuglevel=1 2>&1 |") ||
+ die("Can't do smbclient command.\n");
+ @smb = <SMB>;
+ close SMB;
+
+ if ($DEBUG) { # if -d flag print results of nmblookup and smbclient
+ print "===============================================================\n";
+ print @nmblookup;
+ print @smb;
+ }
+
+ # look for the OS= string
+
+ @info = grep(/OS=/,@smb);
+ $_ = @info[0];
+ if ($_) { # we found response
+ s/Domain=|OS=|Server=|\n//g; # strip out descriptions to make line shorter
+
+ } else { # no OS= string in response (WIN95 client)
+
+ # for WIN95 clients get workgroup name from nmblookup response
+ @name = grep(/<00> - <GROUP>/,@nmblookup);
+ $_ = @name[0];
+ if ($_) {
+ # Same as before for space and characters
+ /(.{1,15})\s+<00>\s+/;
+ $_ = "[$1]";
+ } else {
+ $_ = "Unknown Workgroup";
+ }
+ }
+ }
+
+ # see if machine registered a local master browser name
+ if (grep(/<1d>/,@nmblookup)) {
+ $master = '+'; # indicate local master browser
+ if (grep(/<1b>/,@nmblookup)) { # how about domain master browser?
+ $master = '*'; # indicate domain master browser
+ }
+ } else {
+ $master = ' '; # not a browse master
+ }
+
+ # line up info in 3 columns
+
+ print "$ip".' 'x(16-length($ip))."$name".' 'x(14-length($name))."$master"."$_\n";
+
+ } else { # no netbios name found
+ # try getting the host name
+ ($name, $aliases, $type, $length, @addresses) =
+ gethostbyaddr(pack('C4',split('\.',$ip)),2);
+ if (! $name) { # could not get name
+ $name = "unknown nis name";
+ }
+ if ($DEBUG) { # if -d flag print results of nmblookup
+ print "===============================================================\n";
+ print @nmblookup;
+ }
+ print "$ip".' 'x(16-length($ip))."$name\n";
+ }
+}
+
diff --git a/source/script/findstatic.pl b/source/script/findstatic.pl
new file mode 100755
index 00000000000..43a4916435d
--- /dev/null
+++ b/source/script/findstatic.pl
@@ -0,0 +1,70 @@
+#!/usr/bin/perl -w
+# find a list of fns and variables in the code that could be static
+# usually called with something like this:
+# findstatic.pl `find . -name "*.o"`
+# Andrew Tridgell <tridge@samba.org>
+
+use strict;
+
+# use nm to find the symbols
+my($saved_delim) = $/;
+undef $/;
+my($syms) = `nm -o @ARGV`;
+$/ = $saved_delim;
+
+my(@lines) = split(/\n/s, $syms);
+
+my(%def);
+my(%undef);
+my(%stype);
+
+my(%typemap) = (
+ "T" => "function",
+ "C" => "uninitialised variable",
+ "D" => "initialised variable"
+ );
+
+
+# parse the symbols into defined and undefined
+for (my($i)=0; $i <= $#{@lines}; $i++) {
+ my($line) = $lines[$i];
+ if ($line =~ /(.*):[a-f0-9]* ([TCD]) (.*)/) {
+ my($fname) = $1;
+ my($symbol) = $3;
+ push(@{$def{$fname}}, $symbol);
+ $stype{$symbol} = $2;
+ }
+ if ($line =~ /(.*):\s* U (.*)/) {
+ my($fname) = $1;
+ my($symbol) = $2;
+ push(@{$undef{$fname}}, $symbol);
+ }
+}
+
+# look for defined symbols that are never referenced outside the place they
+# are defined
+foreach my $f (keys %def) {
+ print "Checking $f\n";
+ my($found_one) = 0;
+ foreach my $s (@{$def{$f}}) {
+ my($found) = 0;
+ foreach my $f2 (keys %undef) {
+ if ($f2 ne $f) {
+ foreach my $s2 (@{$undef{$f2}}) {
+ if ($s2 eq $s) {
+ $found = 1;
+ $found_one = 1;
+ }
+ }
+ }
+ }
+ if ($found == 0) {
+ my($t) = $typemap{$stype{$s}};
+ print " '$s' is unique to $f ($t)\n";
+ }
+ }
+ if ($found_one == 0) {
+ print " all symbols in '$f' are unused (main program?)\n";
+ }
+}
+
diff --git a/source/script/gap.awk b/source/script/gap.awk
new file mode 100644
index 00000000000..11680d10f9e
--- /dev/null
+++ b/source/script/gap.awk
@@ -0,0 +1,39 @@
+BEGIN { hv["0"] = 0; hv["1"] = 1; hv["2"] = 2; hv["3"] = 3;
+ hv["4"] = 4; hv["5"] = 5; hv["6"] = 6; hv["7"] = 7;
+ hv["8"] = 8; hv["9"] = 9; hv["A"] = 10; hv["B"] = 11;
+ hv["C"] = 12; hv["D"] = 13; hv["E"] = 14; hv["F"] = 15;
+ hv["a"] = 10; hv["b"] = 11; hv["c"] = 12; hv["d"] = 13;
+ hv["e"] = 14; hv["f"] = 15;
+
+ first = 0; last = 0; idx = 0;
+}
+
+function tonum(str)
+{
+ num=0;
+ cnt=1;
+ while (cnt <= length(str)) {
+ num *= 16;
+ num += hv[substr(str,cnt,1)];
+ ++cnt;
+ }
+ return num;
+}
+
+{
+ u = tonum($1);
+ if (u - last > 6)
+ {
+ if (last)
+ {
+ printf (" { 0x%04x, 0x%04x, %5d },\n",
+ first, last, idx);
+ idx -= u - last - 1;
+ }
+ first = u;
+ }
+ last = u;
+}
+
+END { printf (" { 0x%04x, 0x%04x, %5d },\n",
+ first, last, idx); }
diff --git a/source/script/gaptab.awk b/source/script/gaptab.awk
new file mode 100644
index 00000000000..a309089cd5b
--- /dev/null
+++ b/source/script/gaptab.awk
@@ -0,0 +1,48 @@
+BEGIN { hv["0"] = 0; hv["1"] = 1; hv["2"] = 2; hv["3"] = 3;
+ hv["4"] = 4; hv["5"] = 5; hv["6"] = 6; hv["7"] = 7;
+ hv["8"] = 8; hv["9"] = 9; hv["A"] = 10; hv["B"] = 11;
+ hv["C"] = 12; hv["D"] = 13; hv["E"] = 14; hv["F"] = 15;
+ hv["a"] = 10; hv["b"] = 11; hv["c"] = 12; hv["d"] = 13;
+ hv["e"] = 14; hv["f"] = 15;
+
+ first = 0; last = 0; idx = 0; f = 0;
+}
+
+function tonum(str)
+{
+ num=0;
+ cnt=1;
+ while (cnt <= length(str)) {
+ num *= 16;
+ num += hv[substr(str,cnt,1)];
+ ++cnt;
+ }
+ return num;
+}
+
+function fmt(val)
+{
+ if (f++ % 8 == 0)
+ { printf ("\n 0x%02x,", val); }
+ else
+ { printf (" 0x%02x,", val); }
+}
+
+{
+ u = tonum($1); c = tonum($2);
+
+ if (u - last > 6)
+ {
+ if (last) { idx += last - first + 1; }
+ first = u;
+ }
+ else
+ {
+ for (m = last+1; m < u; m++) { fmt(0); }
+ }
+
+ fmt(c);
+ last = u;
+}
+
+END { print "" }
diff --git a/source/script/gen-8bit-gap.awk b/source/script/gen-8bit-gap.awk
new file mode 100644
index 00000000000..59a1a23be07
--- /dev/null
+++ b/source/script/gen-8bit-gap.awk
@@ -0,0 +1,18 @@
+BEGIN {
+ for (i=0; i<256; i++) {
+ tbl[sprintf("%02x",i)] = "0x0000";
+ }
+}
+
+/^<U([[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]])>[[:space:]]*.x([[:xdigit:]][[:xdigit:]])[:space:]*.*$/ {
+ tbl[substr($2,3,2)]=sprintf("0x%s",substr($1,3,4));
+}
+
+END {
+ for(i=0; i<32; i++) {
+ for(j=0; j<8; j++) {
+ printf(" %s,", tbl[sprintf("%02x",i*8+j)]);
+ }
+ printf "\n"
+ }
+} \ No newline at end of file
diff --git a/source/script/gen-8bit-gap.sh.in b/source/script/gen-8bit-gap.sh.in
new file mode 100755
index 00000000000..bcf64a4464f
--- /dev/null
+++ b/source/script/gen-8bit-gap.sh.in
@@ -0,0 +1,49 @@
+#!/bin/sh
+if test $# -ne 2 ; then
+ echo "Usage: $0 <charmap file> <CHARSET NAME>"
+ exit 1
+fi
+
+CHARMAP=$1
+CHARSETNAME=$2
+
+echo "/* "
+echo " * Conversion table for $CHARSETNAME charset "
+echo " * "
+echo " * Conversion tables are generated using $CHARMAP table "
+echo " * and source/script/gen-8bit-gap.sh script "
+echo " * "
+echo " * This program is free software; you can redistribute it and/or modify "
+echo " * it under the terms of the GNU General Public License as published by "
+echo " * the Free Software Foundation; either version 2 of the License, or "
+echo " * (at your option) any later version. "
+echo " * "
+echo " * This program is distributed in the hope that it will be useful,"
+echo " * but WITHOUT ANY WARRANTY; without even the implied warranty of "
+echo " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
+echo " * GNU General Public License for more details. "
+echo " * "
+echo " * You should have received a copy of the GNU General Public License "
+echo " * along with this program; if not, write to the Free Software "
+echo " * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. "
+echo " */"
+
+echo '#include "includes.h"'
+echo
+echo "static const uint16 to_ucs2[256] = {"
+cat "$CHARMAP" | @AWK@ -f @srcdir@/script/gen-8bit-gap.awk
+echo "};"
+echo
+echo "static const struct charset_gap_table from_idx[] = {"
+sed -ne 's/^<U\(....\).*/\1/p' \
+ "$CHARMAP" | sort -u | @AWK@ -f @srcdir@/script/gap.awk
+echo " { 0xffff, 0xffff, 0 }"
+echo "};"
+echo
+echo "static const unsigned char from_ucs2[] = {"
+sed -ne 's/^<U\(....\)>[[:space:]]*.x\(..\).*/\1 \2/p' \
+ "$CHARMAP" | sort -u | @AWK@ -f @srcdir@/script/gaptab.awk
+echo "};"
+echo
+echo "SMB_GENERATE_CHARSET_MODULE_8_BIT_GAP($CHARSETNAME)"
+echo
diff --git a/source/script/genstruct.pl b/source/script/genstruct.pl
new file mode 100755
index 00000000000..a6abd718c95
--- /dev/null
+++ b/source/script/genstruct.pl
@@ -0,0 +1,299 @@
+#!/usr/bin/perl -w
+# a simple system for generating C parse info
+# this can be used to write generic C structer load/save routines
+# Copyright 2002 Andrew Tridgell <genstruct@tridgell.net>
+# released under the GNU General Public License v2 or later
+
+use strict;
+
+my(%enum_done) = ();
+my(%struct_done) = ();
+
+###################################################
+# general handler
+sub handle_general($$$$$$$$)
+{
+ my($name) = shift;
+ my($ptr_count) = shift;
+ my($size) = shift;
+ my($element) = shift;
+ my($flags) = shift;
+ my($dump_fn) = shift;
+ my($parse_fn) = shift;
+ my($tflags) = shift;
+ my($array_len) = 0;
+ my($dynamic_len) = "NULL";
+
+ # handle arrays, currently treat multidimensional arrays as 1 dimensional
+ while ($element =~ /(.*)\[(.*?)\]$/) {
+ $element = $1;
+ if ($array_len == 0) {
+ $array_len = $2;
+ } else {
+ $array_len = "$2 * $array_len";
+ }
+ }
+
+ if ($flags =~ /_LEN\((\w*?)\)/) {
+ $dynamic_len = "\"$1\"";
+ }
+
+ if ($flags =~ /_NULLTERM/) {
+ $tflags = "FLAG_NULLTERM";
+ }
+
+ print OFILE "{\"$element\", $ptr_count, $size, offsetof(struct $name, $element), $array_len, $dynamic_len, $tflags, $dump_fn, $parse_fn},\n";
+}
+
+
+####################################################
+# parse one element
+sub parse_one($$$$)
+{
+ my($name) = shift;
+ my($type) = shift;
+ my($element) = shift;
+ my($flags) = shift;
+ my($ptr_count) = 0;
+ my($size) = "sizeof($type)";
+ my($tflags) = "0";
+
+ # enums get the FLAG_ALWAYS flag
+ if ($type =~ /^enum /) {
+ $tflags = "FLAG_ALWAYS";
+ }
+
+
+ # make the pointer part of the base type
+ while ($element =~ /^\*(.*)/) {
+ $ptr_count++;
+ $element = $1;
+ }
+
+ # convert spaces to _
+ $type =~ s/ /_/g;
+
+ my($dump_fn) = "gen_dump_$type";
+ my($parse_fn) = "gen_parse_$type";
+
+ handle_general($name, $ptr_count, $size, $element, $flags, $dump_fn, $parse_fn, $tflags);
+}
+
+####################################################
+# parse one element
+sub parse_element($$$)
+{
+ my($name) = shift;
+ my($element) = shift;
+ my($flags) = shift;
+ my($type);
+ my($data);
+
+ # pull the base type
+ if ($element =~ /^struct (\S*) (.*)/) {
+ $type = "struct $1";
+ $data = $2;
+ } elsif ($element =~ /^enum (\S*) (.*)/) {
+ $type = "enum $1";
+ $data = $2;
+ } elsif ($element =~ /^unsigned (\S*) (.*)/) {
+ $type = "unsigned $1";
+ $data = $2;
+ } elsif ($element =~ /^(\S*) (.*)/) {
+ $type = $1;
+ $data = $2;
+ } else {
+ die "Can't parse element '$element'";
+ }
+
+ # handle comma separated lists
+ while ($data =~ /(\S*),[\s]?(.*)/) {
+ parse_one($name, $type, $1, $flags);
+ $data = $2;
+ }
+ parse_one($name, $type, $data, $flags);
+}
+
+
+my($first_struct) = 1;
+
+####################################################
+# parse the elements of one structure
+sub parse_elements($$)
+{
+ my($name) = shift;
+ my($elements) = shift;
+
+ if ($first_struct) {
+ $first_struct = 0;
+ print "Parsing structs: $name";
+ } else {
+ print ", $name";
+ }
+
+ print OFILE "int gen_dump_struct_$name(TALLOC_CTX *mem_ctx, struct parse_string *, const char *, unsigned);\n";
+ print OFILE "int gen_parse_struct_$name(TALLOC_CTX *mem_ctx, char *, const char *);\n";
+
+ print OFILE "static const struct parse_struct pinfo_" . $name . "[] = {\n";
+
+
+ while ($elements =~ /^.*?([a-z].*?);\s*?(\S*?)\s*?$(.*)/msi) {
+ my($element) = $1;
+ my($flags) = $2;
+ $elements = $3;
+ parse_element($name, $element, $flags);
+ }
+
+ print OFILE "{NULL, 0, 0, 0, 0, NULL, 0, NULL, NULL}};\n";
+
+ print OFILE "
+int gen_dump_struct_$name(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent) {
+ return gen_dump_struct(mem_ctx, pinfo_$name, p, ptr, indent);
+}
+int gen_parse_struct_$name(TALLOC_CTX *mem_ctx, char *ptr, const char *str) {
+ return gen_parse_struct(mem_ctx, pinfo_$name, ptr, str);
+}
+
+";
+}
+
+my($first_enum) = 1;
+
+####################################################
+# parse out the enum declarations
+sub parse_enum_elements($$)
+{
+ my($name) = shift;
+ my($elements) = shift;
+
+ if ($first_enum) {
+ $first_enum = 0;
+ print "Parsing enums: $name";
+ } else {
+ print ", $name";
+ }
+
+ print OFILE "static const struct enum_struct einfo_" . $name . "[] = {\n";
+
+ my(@enums) = split(/,/s, $elements);
+ for (my($i)=0; $i <= $#{@enums}; $i++) {
+ my($enum) = $enums[$i];
+ if ($enum =~ /\s*(\w*)/) {
+ my($e) = $1;
+ print OFILE "{\"$e\", $e},\n";
+ }
+ }
+
+ print OFILE "{NULL, 0}};\n";
+
+ print OFILE "
+int gen_dump_enum_$name(struct parse_string *p, const char *ptr, unsigned indent) {
+ return gen_dump_enum(einfo_$name, p, ptr, indent);
+}
+
+int gen_parse_enum_$name(char *ptr, const char *str) {
+ return gen_parse_enum(einfo_$name, ptr, str);
+}
+
+";
+}
+
+####################################################
+# parse out the enum declarations
+sub parse_enums($)
+{
+ my($data) = shift;
+
+ while ($data =~ /^GENSTRUCT\s+enum\s+(\w*?)\s*{(.*?)}\s*;(.*)/ms) {
+ my($name) = $1;
+ my($elements) = $2;
+ $data = $3;
+
+ if (!defined($enum_done{$name})) {
+ $enum_done{$name} = 1;
+ parse_enum_elements($name, $elements);
+ }
+ }
+
+ if (! $first_enum) {
+ print "\n";
+ }
+}
+
+####################################################
+# parse all the structures
+sub parse_structs($)
+{
+ my($data) = shift;
+
+ # parse into structures
+ while ($data =~ /^GENSTRUCT\s+struct\s+(\w+?)\s*{\s*(.*?)\s*}\s*;(.*)/ms) {
+ my($name) = $1;
+ my($elements) = $2;
+ $data = $3;
+ if (!defined($struct_done{$name})) {
+ $struct_done{$name} = 1;
+ parse_elements($name, $elements);
+ }
+ }
+
+ if (! $first_struct) {
+ print "\n";
+ } else {
+ print "No GENSTRUCT structures found?\n";
+ }
+}
+
+
+####################################################
+# parse a header file, generating a dumper structure
+sub parse_data($)
+{
+ my($data) = shift;
+
+ # collapse spaces
+ $data =~ s/[\t ]+/ /sg;
+ $data =~ s/\s*\n\s+/\n/sg;
+ # strip debug lines
+ $data =~ s/^\#.*?\n//smg;
+
+ parse_enums($data);
+ parse_structs($data);
+}
+
+
+#########################################
+# display help text
+sub ShowHelp()
+{
+ print "
+generator for C structure dumpers
+Copyright Andrew Tridgell <genstruct\@tridgell.net>
+
+Sample usage:
+ genstruct -o output.h gcc -E -O2 -g test.h
+
+Options:
+ --help this help page
+ -o OUTPUT place output in OUTPUT
+";
+ exit(0);
+}
+
+########################################
+# main program
+if ($ARGV[0] ne "-o" || $#ARGV < 2) {
+ ShowHelp();
+}
+
+shift;
+my($opt_ofile)=shift;
+
+print "creating $opt_ofile\n";
+
+open(OFILE, ">$opt_ofile") || die "can't open $opt_ofile";
+
+print OFILE "/* This is an automatically generated file - DO NOT EDIT! */\n\n";
+
+parse_data(`@ARGV -DGENSTRUCT=GENSTRUCT`);
+exit(0);
diff --git a/source/script/installbin.sh b/source/script/installbin.sh
new file mode 100755
index 00000000000..f9fd5298c09
--- /dev/null
+++ b/source/script/installbin.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+INSTALLPERMS=$1
+DESTDIR=$2
+BASEDIR=`echo $3 | sed 's/\/\//\//g'`
+BINDIR=`echo $4 | sed 's/\/\//\//g'`
+LIBDIR=`echo $5 | sed 's/\/\//\//g'`
+VARDIR=`echo $6 | sed 's/\/\//\//g'`
+shift
+shift
+shift
+shift
+shift
+shift
+
+for p in $*; do
+ p2=`basename $p`
+ echo Installing $p as $BINDIR/$p2
+ if [ -f $BINDIR/$p2 ]; then
+ rm -f $BINDIR/$p2.old
+ mv $BINDIR/$p2 $BINDIR/$p2.old
+ fi
+ cp $p $BINDIR/
+ chmod $INSTALLPERMS $BINDIR/$p2
+
+ # this is a special case, mount needs this in a specific location
+ if [ $p2 = smbmount ]; then
+ if [ ! -d $DESTDIR/sbin ]; then
+ mkdir $DESTDIR/sbin
+ fi
+ ln -sf $BINDIR/$p2 $DESTDIR/sbin/mount.smbfs
+ fi
+done
+
+
+cat << EOF
+======================================================================
+The binaries are installed. You may restore the old binaries (if there
+were any) using the command "make revert". You may uninstall the binaries
+using the command "make uninstallbin" or "make uninstall" to uninstall
+binaries, man pages and shell scripts.
+======================================================================
+EOF
+
+exit 0
diff --git a/source/script/installdat.sh b/source/script/installdat.sh
new file mode 100755
index 00000000000..4a5b1de5dc8
--- /dev/null
+++ b/source/script/installdat.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+#fist version March 2002, Herb Lewis
+
+DATDIR=`echo $1 | sed 's/\/\//\//g'`
+SRCDIR=$2/
+
+echo Installing dat files in $DATDIR
+
+for f in $SRCDIR/codepages/*.dat; do
+ FNAME=$DATDIR/`basename $f`
+ echo $FNAME
+ cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+ chmod 0644 $FNAME
+done
+
+cat << EOF
+======================================================================
+The dat files have been installed.
+======================================================================
+EOF
+
+exit 0
+
diff --git a/source/script/installdirs.sh b/source/script/installdirs.sh
new file mode 100755
index 00000000000..1db46b82ff2
--- /dev/null
+++ b/source/script/installdirs.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+while ( test -n "$1" ); do
+
+ DIRNAME=`echo $1 | sed 's/\/\//\//g'`
+ if [ ! -d $DIRNAME ]; then
+ mkdir -p $DIRNAME
+ fi
+
+ if [ ! -d $DIRNAME ]; then
+ echo Failed to make directory $1
+ exit 1
+ fi
+
+ shift;
+done
+
+
+
diff --git a/source/script/installman.sh b/source/script/installman.sh
new file mode 100755
index 00000000000..c7a8f450951
--- /dev/null
+++ b/source/script/installman.sh
@@ -0,0 +1,71 @@
+#!/bin/sh
+#5 July 96 Dan.Shearer@unisa.edu.au removed hardcoded values
+#
+# 13 Aug 2001 Rafal Szczesniak <mimir@spin.ict.pwr.wroc.pl>
+# modified to accomodate international man pages (inspired
+# by Japanese edition's approach)
+
+MANDIR=`echo $1 | sed 's/\/\//\//g'`
+SRCDIR=$2/
+langs=$3
+
+if [ $# -ge 4 ] ; then
+ GROFF=$4 # sh cmd line, including options
+fi
+
+
+for lang in $langs; do
+ if [ "X$lang" = Xen ]; then
+ echo Installing default man pages in $MANDIR/
+ lang=.
+ else
+ echo Installing \"$lang\" man pages in $MANDIR/lang/$lang
+ fi
+
+ langdir=$MANDIR/$lang
+ for d in $MANDIR $langdir $langdir/man1 $langdir/man5 $langdir/man7 $langdir/man8; do
+ if [ ! -d $d ]; then
+ mkdir $d
+ if [ ! -d $d ]; then
+ echo Failed to make directory $d, does $USER have privileges?
+ exit 1
+ fi
+ fi
+ done
+
+ for sect in 1 5 7 8 ; do
+ for m in $langdir/man$sect ; do
+ for s in $SRCDIR../docs/manpages/$lang/*$sect; do
+ FNAME=$m/`basename $s`
+
+ # Test for writability. Involves
+ # blowing away existing files.
+
+ if (rm -f $FNAME && touch $FNAME); then
+ rm $FNAME
+ if [ "x$GROFF" = x ] ; then
+ cp $s $m # Copy raw nroff
+ else
+ echo "\t$FNAME" # groff'ing can be slow, give the user
+ # a warm fuzzy.
+ $GROFF $s > $FNAME # Process nroff, because man(1) (on
+ # this system) doesn't .
+ fi
+ chmod 0644 $FNAME
+ else
+ echo Cannot create $FNAME... does $USER have privileges?
+ fi
+ done
+ done
+ done
+done
+cat << EOF
+======================================================================
+The man pages have been installed. You may uninstall them using the command
+the command "make uninstallman" or make "uninstall" to uninstall binaries,
+man pages and shell scripts.
+======================================================================
+EOF
+
+exit 0
+
diff --git a/source/script/installmodules.sh b/source/script/installmodules.sh
new file mode 100755
index 00000000000..f7c74733381
--- /dev/null
+++ b/source/script/installmodules.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+INSTALLPERMS=$1
+BASEDIR=`echo $2 | sed 's/\/\//\//g'`
+LIBDIR=`echo $3 | sed 's/\/\//\//g'`
+shift
+shift
+shift
+
+for d in $BASEDIR $LIBDIR; do
+if [ ! -d $d ]; then
+mkdir $d
+if [ ! -d $d ]; then
+ echo Failed to make directory $d
+ exit 1
+fi
+fi
+done
+
+for p in $*; do
+ p2=`basename $p`
+ echo Installing $p as $LIBDIR/$p2
+ cp -f $p $LIBDIR/
+ chmod $INSTALLPERMS $LIBDIR/$p2
+done
+
+exit 0
diff --git a/source/script/installmsg.sh b/source/script/installmsg.sh
new file mode 100644
index 00000000000..5a41fe1ca8d
--- /dev/null
+++ b/source/script/installmsg.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+# first version (Sept 2003) written by Shiro Yamada <shiro@miraclelinux.com>
+# based on the first verion (March 2002) of installdat.sh written by Herb Lewis
+
+MSGDIR=`echo $1 | sed 's/\/\//\//g'`
+SRCDIR=$2/
+
+echo Installing msg files in $MSGDIR
+
+for f in $SRCDIR/po/*.msg; do
+ FNAME=$MSGDIR/`basename $f`
+ echo $FNAME
+ cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+ chmod 0644 $FNAME
+done
+
+cat << EOF
+======================================================================
+The msg files have been installed.
+======================================================================
+EOF
+
+exit 0
diff --git a/source/script/installscripts.sh b/source/script/installscripts.sh
new file mode 100755
index 00000000000..81608c3682c
--- /dev/null
+++ b/source/script/installscripts.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+# this script courtesy of James_K._Foote.PARC@xerox.com
+# 5 July 96 Dan.Shearer@UniSA.Edu.Au Don't hardcode script names, get from Make
+
+INSTALLPERMS=$1
+BINDIR=`echo $2 | sed 's/\/\//\//g'`
+
+shift
+shift
+
+echo Installing scripts in $BINDIR
+
+for d in $BINDIR; do
+ if [ ! -d $d ]; then
+ mkdir $d
+ if [ ! -d $d ]; then
+ echo Failed to make directory $d
+ echo Have you run installbin first?
+ exit 1
+ fi
+ fi
+done
+
+for p in $*; do
+ p2=`basename $p`
+ echo Installing $BINDIR/$p2
+ if [ -f $BINDIR/$p2 ]; then
+ rm -f $BINDIR/$p2.old
+ mv $BINDIR/$p2 $BINDIR/$p2.old
+ fi
+ cp $p $BINDIR/
+ chmod $INSTALLPERMS $BINDIR/$p2
+ if [ ! -f $BINDIR/$p2 ]; then
+ echo Cannot copy $p2... does $USER have privileges?
+ fi
+done
+
+cat << EOF
+======================================================================
+The scripts have been installed. You may uninstall them using
+the command "make uninstallscripts" or "make install" to install binaries,
+man pages and shell scripts. You may recover the previous version (if any
+by "make revert".
+======================================================================
+EOF
+
+exit 0
diff --git a/source/script/installswat.sh b/source/script/installswat.sh
new file mode 100755
index 00000000000..495386e0b7a
--- /dev/null
+++ b/source/script/installswat.sh
@@ -0,0 +1,153 @@
+#!/bin/sh
+#first version March 1998, Andrew Tridgell
+
+SWATDIR=`echo $1 | sed 's/\/\//\//g'`
+SRCDIR=$2/
+BOOKDIR=$SWATDIR/using_samba
+
+echo Installing SWAT in $SWATDIR
+echo Installing the Samba Web Administration Tool
+
+LANGS=". `cd $SRCDIR../swat/; /bin/echo lang/??`"
+echo Installing langs are `cd $SRCDIR../swat/lang/; /bin/echo ??`
+
+for ln in $LANGS; do
+ SWATLANGDIR=$SWATDIR/$ln
+ for d in $SWATLANGDIR $SWATLANGDIR/help $SWATLANGDIR/images \
+ $SWATLANGDIR/include; do
+ if [ ! -d $d ]; then
+ mkdir -p $d
+ if [ ! -d $d ]; then
+ echo Failed to make directory $d, does $USER have privileges?
+ exit 1
+ fi
+ fi
+ done
+done
+
+# Install images
+for ln in $LANGS; do
+
+ for f in $SRCDIR../swat/$ln/images/*.gif; do
+ if [ ! -f $f ] ; then
+ continue
+ fi
+ FNAME=$SWATDIR/$ln/images/`basename $f`
+ echo $FNAME
+ cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+ chmod 0644 $FNAME
+ done
+
+ # Install html help
+
+ for f in $SRCDIR../swat/$ln/help/*.html; do
+ if [ ! -f $f ] ; then
+ continue
+ fi
+ FNAME=$SWATDIR/$ln/help/`basename $f`
+ echo $FNAME
+ if [ "x$BOOKDIR" = "x" ]; then
+ cat $f | sed 's/@BOOKDIR@.*$//' > $f.tmp
+ else
+ cat $f | sed 's/@BOOKDIR@//' > $f.tmp
+ fi
+ f=$f.tmp
+ cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+ rm -f $f
+ chmod 0644 $FNAME
+ done
+
+ # Install "server-side" includes
+
+ for f in $SRCDIR../swat/$ln/include/*.html; do
+ if [ ! -f $f ] ; then
+ continue
+ fi
+ FNAME=$SWATDIR/$ln/include/`basename $f`
+ echo $FNAME
+ cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+ chmod 0644 $FNAME
+ done
+
+done
+
+# Install html documentation (if html documentation tree is here)
+
+if [ -d $SRCDIR../docs/htmldocs/ ]; then
+
+ for f in $SRCDIR../docs/htmldocs/*.html; do
+ FNAME=$SWATDIR/help/`basename $f`
+ echo $FNAME
+ cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+ chmod 0644 $FNAME
+ done
+
+ if [ -d $SRCDIR../docs/htmldocs/images/ ]; then
+ if [ ! -d $SWATDIR/help/images/ ]; then
+ mkdir $SWATDIR/help/images
+ if [ ! -d $SWATDIR/help/images/ ]; then
+ echo Failed to make directory $SWATDIR/help/images, does $USER have privileges?
+ exit 1
+ fi
+ fi
+ for f in $SRCDIR../docs/htmldocs/images/*.png; do
+ FNAME=$SWATDIR/help/images/`basename $f`
+ echo $FNAME
+ cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+ chmod 0644 $FNAME
+ done
+ fi
+fi
+
+# Install Using Samba book (but only if it is there)
+
+if [ "x$BOOKDIR" != "x" -a -f $SRCDIR../docs/htmldocs/using_samba/toc.html ]; then
+
+ # Create directories
+
+ for d in $BOOKDIR $BOOKDIR/figs ; do
+ if [ ! -d $d ]; then
+ mkdir $d
+ if [ ! -d $d ]; then
+ echo Failed to make directory $d, does $USER have privileges?
+ exit 1
+ fi
+ fi
+ done
+
+ # HTML files
+
+ for f in $SRCDIR../docs/htmldocs/using_samba/*.html; do
+ FNAME=$BOOKDIR/`basename $f`
+ echo $FNAME
+ cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+ chmod 0644 $FNAME
+ done
+
+ for f in $SRCDIR../docs/htmldocs/using_samba/*.gif; do
+ FNAME=$BOOKDIR/`basename $f`
+ echo $FNAME
+ cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+ chmod 0644 $FNAME
+ done
+
+ # Figures
+
+ for f in $SRCDIR../docs/htmldocs/using_samba/figs/*.gif; do
+ FNAME=$BOOKDIR/figs/`basename $f`
+ echo $FNAME
+ cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+ chmod 0644 $FNAME
+ done
+
+fi
+
+cat << EOF
+======================================================================
+The SWAT files have been installed. Remember to read the swat/README
+for information on enabling and using SWAT
+======================================================================
+EOF
+
+exit 0
+
diff --git a/source/script/linkmodules.sh b/source/script/linkmodules.sh
new file mode 100755
index 00000000000..16a04cc064b
--- /dev/null
+++ b/source/script/linkmodules.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+cd "$1"
+test -f "$2" || exit 0
+
+for I in $3 $4 $5 $6 $7 $8
+do
+ echo "Linking $I to $2"
+ ln -s $2 $I
+done
+
+exit 0
diff --git a/source/script/makeunicodecasemap.awk b/source/script/makeunicodecasemap.awk
new file mode 100644
index 00000000000..8424b6c6725
--- /dev/null
+++ b/source/script/makeunicodecasemap.awk
@@ -0,0 +1,59 @@
+function reset_vals() {
+ upperstr = "";
+ lowerstr = "";
+ flagstr = "0";
+}
+
+function print_val() {
+ upperstr = $13;
+ lowerstr = $14;
+ if ( upperstr == "" )
+ upperstr = strval;
+ if ( lowerstr == "" )
+ lowerstr = strval;
+
+ if ( $3 == "Lu" )
+ flagstr = sprintf("%s|%s", flagstr, "UNI_UPPER");
+ if ( $3 == "Ll" )
+ flagstr = sprintf("%s|%s", flagstr, "UNI_LOWER");
+ if ( val >= 48 && val <= 57)
+ flagstr = sprintf("%s|%s", flagstr, "UNI_DIGIT");
+ if ((val >= 48 && val <= 57) || (val >= 65 && val <= 70) || (val >=97 && val <= 102))
+ flagstr = sprintf("%s|%s", flagstr, "UNI_XDIGIT");
+ if ( val == 32 || (val >=9 && val <= 13))
+ flagstr = sprintf("%s|%s", flagstr, "UNI_SPACE");
+ if( index(flagstr, "0|") == 1)
+ flagstr = substr(flagstr, 3, length(flagstr) - 2);
+ printf("{ 0x%s, 0x%s, %s }, \t\t\t/* %s %s */\n", lowerstr, upperstr, flagstr, strval, $2);
+ val++;
+ strval=sprintf("%04X", val);
+ reset_vals();
+}
+
+BEGIN {
+ val=0
+ FS=";"
+ strval=sprintf("%04X", val);
+ reset_vals();
+}
+
+{
+ if ( $1 == strval ) {
+ print_val();
+ } else {
+ while ( $1 != strval) {
+ printf("{ 0x%04X, 0x%04X, 0 }, \t\t\t/* %s NOMAP */\n", val, val, strval);
+ val++;
+ strval=sprintf("%04X", val);
+ }
+ print_val();
+ }
+}
+
+END {
+ while ( val < 65536 ) {
+ printf("{ 0x%04X, 0x%04X, 0 }, \t\t\t/* %s NOMAP */\n", val, val, strval);
+ val++;
+ strval=sprintf("%04X", val);
+ }
+}
diff --git a/source/script/mkbuildoptions.awk b/source/script/mkbuildoptions.awk
new file mode 100644
index 00000000000..9c226623109
--- /dev/null
+++ b/source/script/mkbuildoptions.awk
@@ -0,0 +1,262 @@
+BEGIN {
+ print "/* ";
+ print " Unix SMB/CIFS implementation.";
+ print " Build Options for Samba Suite";
+ print " Copyright (C) Vance Lankhaar <vlankhaar@linux.ca> 2003";
+ print " Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001";
+ print " ";
+ print " This program is free software; you can redistribute it and/or modify";
+ print " it under the terms of the GNU General Public License as published by";
+ print " the Free Software Foundation; either version 2 of the License, or";
+ print " (at your option) any later version.";
+ print " ";
+ print " This program is distributed in the hope that it will be useful,";
+ print " but WITHOUT ANY WARRANTY; without even the implied warranty of";
+ print " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the";
+ print " GNU General Public License for more details.";
+ print " ";
+ print " You should have received a copy of the GNU General Public License";
+ print " along with this program; if not, write to the Free Software";
+ print " Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.";
+ print "*/";
+ print "";
+ print "#include \"includes.h\"";
+ print "#include \"build_env.h\"";
+ print "#include \"dynconfig.h\"";
+ print "";
+ print "static void output(BOOL screen, const char *format, ...) PRINTF_ATTRIBUTE(2,3);";
+ print "";
+ print "";
+ print "/****************************************************************************";
+ print "helper function for build_options";
+ print "****************************************************************************/";
+ print "static void output(BOOL screen, const char *format, ...)";
+ print "{";
+ print " char *ptr;";
+ print " va_list ap;";
+ print " ";
+ print " va_start(ap, format);";
+ print " vasprintf(&ptr,format,ap);";
+ print " va_end(ap);";
+ print "";
+ print " if (screen) {";
+ print " d_printf(\"%s\", ptr);";
+ print " } else {";
+ print " DEBUG(4,(\"%s\", ptr));";
+ print " }";
+ print " ";
+ print " SAFE_FREE(ptr);";
+ print "}";
+ print "";
+ print "/****************************************************************************";
+ print "options set at build time for the samba suite";
+ print "****************************************************************************/";
+ print "void build_options(BOOL screen)";
+ print "{";
+ print " if ((DEBUGLEVEL < 4) && (!screen)) {";
+ print " return;";
+ print " }";
+ print "";
+ print "#ifdef _BUILD_ENV_H";
+ print " /* Output information about the build environment */";
+ print " output(screen,\"Build environment:\\n\");";
+ print " output(screen,\" Built by: %s@%s\\n\",BUILD_ENV_USER,BUILD_ENV_HOST);";
+ print " output(screen,\" Built on: %s\\n\",BUILD_ENV_DATE);";
+ print "";
+ print " output(screen,\" Built using: %s\\n\",BUILD_ENV_COMPILER);";
+ print " output(screen,\" Build host: %s\\n\",BUILD_ENV_UNAME);";
+ print " output(screen,\" SRCDIR: %s\\n\",BUILD_ENV_SRCDIR);";
+ print " output(screen,\" BUILDDIR: %s\\n\",BUILD_ENV_BUILDDIR);";
+ print "";
+ print " ";
+ print "#endif";
+ print "";
+
+ print " /* Output various paths to files and directories */";
+ print " output(screen,\"\\nPaths:\\n\");";
+
+ print " output(screen,\" SBINDIR: %s\\n\", dyn_SBINDIR);";
+ print " output(screen,\" BINDIR: %s\\n\", dyn_BINDIR);";
+ print " output(screen,\" SWATDIR: %s\\n\", dyn_SWATDIR);";
+
+ print " output(screen,\" CONFIGFILE: %s\\n\", dyn_CONFIGFILE);";
+ print " output(screen,\" LOGFILEBASE: %s\\n\", dyn_LOGFILEBASE);";
+ print " output(screen,\" LMHOSTSFILE: %s\\n\",dyn_LMHOSTSFILE);";
+
+ print " output(screen,\" LIBDIR: %s\\n\",dyn_LIBDIR);";
+ print " output(screen,\" SHLIBEXT: %s\\n\",dyn_SHLIBEXT);";
+
+ print " output(screen,\" LOCKDIR: %s\\n\",dyn_LOCKDIR);";
+ print " output(screen,\" PIDDIR: %s\\n\", dyn_PIDDIR);";
+
+ print " output(screen,\" SMB_PASSWD_FILE: %s\\n\",dyn_SMB_PASSWD_FILE);";
+ print " output(screen,\" PRIVATE_DIR: %s\\n\",dyn_PRIVATE_DIR);";
+ print "";
+
+
+##################################################
+# predefine first element of *_ary
+# predefine *_i (num of elements in *_ary)
+ with_ary[0]="";
+ with_i=0;
+ have_ary[0]="";
+ have_i=0;
+ utmp_ary[0]="";
+ utmp_i=0;
+ misc_ary[0]="";
+ misc_i=0;
+ sys_ary[0]="";
+ sys_i=0;
+ headers_ary[0]="";
+ headers_i=0;
+ in_comment = 0;
+}
+
+# capture single line comments
+/^\/\* (.*?)\*\// {
+ last_comment = $0;
+ next;
+}
+
+# end capture multi-line comments
+/(.*?)\*\// {
+ last_comment = last_comment $0;
+ in_comment = 0;
+ next;
+}
+
+# capture middle lines of multi-line comments
+in_comment {
+ last_comment = last_comment $0;
+ next;
+}
+
+# begin capture multi-line comments
+/^\/\* (.*?)/ {
+ last_comment = $0;
+ in_comment = 1;
+ next
+}
+
+##################################################
+# if we have an #undef and a last_comment, store it
+/^\#undef/ {
+ split($0,a);
+ comments_ary[a[2]] = last_comment;
+ last_comment = "";
+}
+
+##################################################
+# for each line, sort into appropriate section
+# then move on
+
+/^\#undef WITH/ {
+ with_ary[with_i++] = a[2];
+ # we want (I think) to allow --with to show up in more than one place, so no next
+}
+
+
+/^\#undef HAVE_UT_UT_/ || /^\#undef .*UTMP/ {
+ utmp_ary[utmp_i++] = a[2];
+ next;
+}
+
+/^\#undef HAVE_SYS_.*?_H$/ {
+ sys_ary[sys_i++] = a[2];
+ next;
+}
+
+/^\#undef HAVE_.*?_H$/ {
+ headers_ary[headers_i++] = a[2];
+ next;
+}
+
+/^\#undef HAVE_/ {
+ have_ary[have_i++] = a[2];
+ next;
+}
+
+/^\#undef/ {
+ misc_ary[misc_i++] = a[2];
+ next;
+}
+
+
+##################################################
+# simple sort function
+function sort(ARRAY, ELEMENTS) {
+ for (i = 1; i <= ELEMENTS; ++i) {
+ for (j = i; (j-1) in ARRAY && (j) in ARRAY && ARRAY[j-1] > ARRAY[j]; --j) {
+ temp = ARRAY[j];
+ ARRAY[j] = ARRAY[j-1];
+ ARRAY[j-1] = temp;
+ }
+ }
+ return;
+}
+
+
+##################################################
+# output code from list of defined
+# expects: ARRAY an array of things defined
+# ELEMENTS number of elements in ARRAY
+# TITLE title for section
+# returns: nothing
+function output(ARRAY, ELEMENTS, TITLE) {
+
+ # add section header
+ print "\n\t/* Show " TITLE " */";
+ print "\toutput(screen, \"\\n " TITLE ":\\n\");\n";
+
+
+ # sort element using bubble sort (slow, but easy)
+ sort(ARRAY, ELEMENTS);
+
+ # loop through array of defines, outputting code
+ for (i = 0; i < ELEMENTS; i++) {
+ print "#ifdef " ARRAY[i];
+
+ # I don't know which one to use....
+
+ print "\toutput(screen, \" " ARRAY[i] "\\n\");";
+ #printf "\toutput(screen, \" %s\\n %s\\n\\n\");\n", comments_ary[ARRAY[i]], ARRAY[i];
+ #printf "\toutput(screen, \" %-35s %s\\n\");\n", ARRAY[i], comments_ary[ARRAY[i]];
+
+ print "#endif";
+ }
+ return;
+}
+
+END {
+ ##################################################
+ # add code to show various options
+ print "/* Output various other options (as gleaned from include/config.h.in) */";
+ output(sys_ary, sys_i, "System Headers");
+ output(headers_ary, headers_i, "Headers");
+ output(utmp_ary, utmp_i, "UTMP Options");
+ output(have_ary, have_i, "HAVE_* Defines");
+ output(with_ary, with_i, "--with Options");
+ output(misc_ary, misc_i, "Build Options");
+
+ ##################################################
+ # add code to display the various type sizes
+ print " /* Output the sizes of the various types */";
+ print " output(screen, \"\\nType sizes:\\n\");";
+ print " output(screen, \" sizeof(char): %lu\\n\",(unsigned long)sizeof(char));";
+ print " output(screen, \" sizeof(int): %lu\\n\",(unsigned long)sizeof(int));";
+ print " output(screen, \" sizeof(long): %lu\\n\",(unsigned long)sizeof(long));";
+ print " output(screen, \" sizeof(uint8): %lu\\n\",(unsigned long)sizeof(uint8));";
+ print " output(screen, \" sizeof(uint16): %lu\\n\",(unsigned long)sizeof(uint16));";
+ print " output(screen, \" sizeof(uint32): %lu\\n\",(unsigned long)sizeof(uint32));";
+ print " output(screen, \" sizeof(short): %lu\\n\",(unsigned long)sizeof(short));";
+ print " output(screen, \" sizeof(void*): %lu\\n\",(unsigned long)sizeof(void*));";
+
+ ##################################################
+ # add code to give information about modules
+ print " output(screen, \"\\nBuiltin modules:\\n\");";
+ print " output(screen, \" %s\\n\", STRING_STATIC_MODULES);";
+
+ print "}";
+
+}
+
diff --git a/source/script/mkinstalldirs b/source/script/mkinstalldirs
new file mode 100755
index 00000000000..f945dbf2bce
--- /dev/null
+++ b/source/script/mkinstalldirs
@@ -0,0 +1,38 @@
+#! /bin/sh
+# mkinstalldirs --- make directory hierarchy
+# Author: Noah Friedman <friedman@prep.ai.mit.edu>
+# Created: 1993-05-16
+# Public domain
+
+errstatus=0
+
+for file
+do
+ set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
+ shift
+
+ pathcomp=
+ for d
+ do
+ pathcomp="$pathcomp$d"
+ case "$pathcomp" in
+ -* ) pathcomp=./$pathcomp ;;
+ esac
+
+ if test ! -d "$pathcomp"; then
+ echo "mkdir $pathcomp" 1>&2
+
+ mkdir "$pathcomp" || lasterr=$?
+
+ if test ! -d "$pathcomp"; then
+ errstatus=$lasterr
+ fi
+ fi
+
+ pathcomp="$pathcomp/"
+ done
+done
+
+exit $errstatus
+
+# mkinstalldirs ends here
diff --git a/source/script/mknissmbpasswd.sh b/source/script/mknissmbpasswd.sh
new file mode 100755
index 00000000000..a94c963bdce
--- /dev/null
+++ b/source/script/mknissmbpasswd.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# Copyright (C) 1998 Benny Holmgren
+#
+# Script to import smbpasswd file into the smbpasswd NIS+ table. Reads
+# from stdin the smbpasswd file.
+#
+while true
+do
+ read row
+ if [ -z "$row" ]
+ then
+ break
+ fi
+
+ if [ "`echo $row | cut -c1`" = "#" ]
+ then
+ continue
+ fi
+
+ nistbladm -a \
+ name=\"`echo $row | cut -d: -f1`\" \
+ uid=\"`echo $row | cut -d: -f2`\" \
+ lmpwd=\"`echo $row | cut -d: -f3`\" \
+ ntpwd=\"`echo $row | cut -d: -f4`\" \
+ acb=\"`echo $row | cut -d: -f5`\" \
+ pwdlset_t=\"`echo $row | cut -d: -f6`\" \
+ gcos=\"`echo $row | cut -d: -f7`\" \
+ home=\"`echo $row | cut -d: -f8`\" \
+ shell=\"`echo $row | cut -d: -f9`\" smbpasswd.org_dir.`nisdefaults -d`
+done
diff --git a/source/script/mknissmbpwdtbl.sh b/source/script/mknissmbpwdtbl.sh
new file mode 100755
index 00000000000..a9b34ff9a75
--- /dev/null
+++ b/source/script/mknissmbpwdtbl.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+#
+# Copyright (C) 1998 Benny Holmgren
+#
+# Creates smbpasswd table and smb group in NIS+
+#
+
+nistbladm \
+ -D access=og=rmcd,nw= -c \
+ -s : smbpasswd_tbl \
+ name=S,nogw=r \
+ uid=S,nogw=r \
+ user_rid=S,nogw=r \
+ smb_grpid=,nw+r \
+ group_rid=,nw+r \
+ acb=,nw+r \
+ \
+ lmpwd=C,nw=,g=r,o=rm \
+ ntpwd=C,nw=,g=r,o=rm \
+ \
+ logon_t=,nw+r \
+ logoff_t=,nw+r \
+ kick_t=,nw+r \
+ pwdlset_t=,nw+r \
+ pwdlchg_t=,nw+r \
+ pwdmchg_t=,nw+r \
+ \
+ full_name=,nw+r \
+ home_dir=,nw+r \
+ dir_drive=,nw+r \
+ logon_script=,nw+r \
+ profile_path=,nw+r \
+ acct_desc=,nw+r \
+ workstations=,nw+r \
+ \
+ hours=,nw+r \
+ smbpasswd.org_dir.`nisdefaults -d`
+
+nisgrpadm -c smb.`nisdefaults -d`
+
+nischgrp smb.`nisdefaults -d` smbpasswd.org_dir.`nisdefaults -d`
+
diff --git a/source/script/mkproto.awk b/source/script/mkproto.awk
new file mode 100644
index 00000000000..b38f405e0da
--- /dev/null
+++ b/source/script/mkproto.awk
@@ -0,0 +1,155 @@
+BEGIN {
+ inheader=0;
+# use_ldap_define = 0;
+ current_file="";
+ if (headername=="") {
+ headername="_PROTO_H_";
+ }
+
+ print "#ifndef",headername
+ print "#define",headername
+ print ""
+ print "/* This file is automatically generated with \"make proto\". DO NOT EDIT */"
+ print ""
+}
+
+END {
+ print ""
+ print "#endif /* ",headername," */"
+}
+
+{
+ if (FILENAME!=current_file) {
+# if (use_ldap_define)
+# {
+# print "#endif /* USE_LDAP */"
+# use_ldap_define = 0;
+# }
+ print ""
+ print "/* The following definitions come from",FILENAME," */"
+ print ""
+ current_file=FILENAME
+ }
+ if (inheader) {
+ if (match($0,"[)][ \t]*$")) {
+ inheader = 0;
+ printf "%s;\n",$0;
+ } else {
+ printf "%s\n",$0;
+ }
+ next;
+ }
+}
+
+# we handle the loadparm.c fns separately
+
+/^FN_LOCAL_BOOL/ {
+ split($0,a,"[,()]")
+ printf "BOOL %s(int );\n", a[2]
+}
+
+/^FN_LOCAL_LIST/ {
+ split($0,a,"[,()]")
+ printf "const char **%s(int );\n", a[2]
+}
+
+/^FN_LOCAL_STRING/ {
+ split($0,a,"[,()]")
+ printf "char *%s(int );\n", a[2]
+}
+
+/^FN_LOCAL_CONST_STRING/ {
+ split($0,a,"[,()]")
+ printf "const char *%s(int );\n", a[2]
+}
+
+/^FN_LOCAL_INT/ {
+ split($0,a,"[,()]")
+ printf "int %s(int );\n", a[2]
+}
+
+/^FN_LOCAL_CHAR/ {
+ split($0,a,"[,()]")
+ printf "char %s(int );\n", a[2]
+}
+
+/^FN_GLOBAL_BOOL/ {
+ split($0,a,"[,()]")
+ printf "BOOL %s(void);\n", a[2]
+}
+
+/^FN_GLOBAL_LIST/ {
+ split($0,a,"[,()]")
+ printf "const char **%s(void);\n", a[2]
+}
+
+/^FN_GLOBAL_STRING/ {
+ split($0,a,"[,()]")
+ printf "char *%s(void);\n", a[2]
+}
+
+/^FN_GLOBAL_CONST_STRING/ {
+ split($0,a,"[,()]")
+ printf "const char *%s(void);\n", a[2]
+}
+
+/^FN_GLOBAL_INT/ {
+ split($0,a,"[,()]")
+ printf "int %s(void);\n", a[2]
+}
+
+/^static|^extern/ || !/^[a-zA-Z]/ || /[;]/ {
+ next;
+}
+
+#
+# We have to split up the start
+# matching as we now have so many start
+# types that it can cause some versions
+# of nawk/awk to choke and fail on
+# the full match. JRA.
+#
+
+{
+ gotstart = 0;
+ if( $0 ~ /^const|^connection_struct|^pipes_struct|^smb_np_struct|^file_fd_struct|^files_struct|^connection_struct|^uid_t|^gid_t|^unsigned|^mode_t|^DIR|^user|^int|^pid_t|^ino_t|^off_t|^double/ ) {
+ gotstart = 1;
+ }
+
+ if( $0 ~ /^vuser_key|^UNISTR2|^LOCAL_GRP|^DOMAIN_GRP|^SMB_STRUCT_DIRENT|^SEC_ACL|^SEC_DESC|^SEC_DESC_BUF|^DOM_SID|^RPC_HND_NODE|^BYTE/ ) {
+ gotstart = 1;
+ }
+
+ if( $0 ~ /^ADS_STRUCT|^ADS_STATUS|^DATA_BLOB|^ASN1_DATA|^TDB_CONTEXT|^TDB_DATA|^smb_ucs2_t|^TALLOC_CTX|^hash_element|^NT_DEVICEMODE|^enum.*\(|^NT_USER_TOKEN|^SAM_ACCOUNT|^NTTIME/ ) {
+ gotstart = 1;
+ }
+
+ if( $0 ~ /^smb_iconv_t|^long|^char|^uint|^NTSTATUS|^WERROR|^CLI_POLICY_HND|^struct|^BOOL|^void|^time|^smb_shm_offset_t|^shm_offset_t|^FILE|^XFILE|^SMB_OFF_T|^size_t|^ssize_t|^SMB_BIG_UINT/ ) {
+ gotstart = 1;
+ }
+
+ if( $0 ~ /^SAM_ACCT_INFO_NODE|^SMB_ACL_T|^ADS_MODLIST|^PyObject|^SORTED_TREE|^REGISTRY_HOOK|^REGISTRY_VALUE|^DEVICEMODE|^PAC_DATA|^NET_USER_INFO_3|^smb_event_id_t/ ) {
+ gotstart = 1;
+ }
+
+ if( $0 ~ /^WINBINDD_PW|^WINBINDD_GR|^NT_PRINTER_INFO_LEVEL_2|^LOGIN_CACHE/ ) {
+ gotstart = 1;
+ }
+
+ if(!gotstart) {
+ next;
+ }
+}
+
+
+/[(].*[)][ \t]*$/ {
+ printf "%s;\n",$0;
+ next;
+}
+
+/[(]/ {
+ inheader=1;
+ printf "%s\n",$0;
+ next;
+}
+
diff --git a/source/script/mkproto.sh b/source/script/mkproto.sh
new file mode 100755
index 00000000000..62041c7e331
--- /dev/null
+++ b/source/script/mkproto.sh
@@ -0,0 +1,43 @@
+#! /bin/sh
+
+LANG=C; export LANG
+LC_ALL=C; export LC_ALL
+LC_COLLATE=C; export LC_COLLATE
+
+if [ $# -lt 3 ]
+then
+ echo "Usage: $0 awk [-h headerdefine] outputheader proto_obj"
+ exit 1
+fi
+
+awk="$1"
+shift
+
+if [ x"$1" = x-h ]
+then
+ headeropt="-v headername=$2"
+ shift; shift;
+else
+ headeropt=""
+fi
+
+header="$1"
+shift
+headertmp="$header.$$.tmp~"
+
+proto_src="`echo $@ | tr ' ' '\n' | sed -e 's/\.o/\.c/g' | sort | uniq | egrep -v 'ubiqx/|wrapped|modules/getdate'`"
+
+echo creating $header
+
+mkdir -p `dirname $header`
+
+${awk} $headeropt \
+ -f script/mkproto.awk $proto_src > $headertmp
+
+if cmp -s $header $headertmp 2>/dev/null
+then
+ echo "$header unchanged"
+ rm $headertmp
+else
+ mv $headertmp $header
+fi
diff --git a/source/script/mksmbpasswd.sh b/source/script/mksmbpasswd.sh
new file mode 100755
index 00000000000..119a55611ec
--- /dev/null
+++ b/source/script/mksmbpasswd.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+awk 'BEGIN {FS=":"
+ printf("#\n# SMB password file.\n#\n")
+ }
+{ printf( "%s:%s:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:[UD ]:LCT-00000000:%s\n", $1, $3, $5) }
+'
diff --git a/source/script/mkversion.sh b/source/script/mkversion.sh
new file mode 100755
index 00000000000..ca392979408
--- /dev/null
+++ b/source/script/mkversion.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+#
+
+VERSION_FILE=$1
+OUTPUT_FILE=$2
+
+if test -z "$VERSION_FILE";then
+ VERSION_FILE="VERSION"
+fi
+
+if test -z "$OUTPUT_FILE";then
+ OUTPUT_FILE="include/version.h"
+fi
+
+SOURCE_DIR=$3
+
+SAMBA_VERSION_MAJOR=`sed -n 's/^SAMBA_VERSION_MAJOR=//p' $SOURCE_DIR$VERSION_FILE`
+SAMBA_VERSION_MINOR=`sed -n 's/^SAMBA_VERSION_MINOR=//p' $SOURCE_DIR$VERSION_FILE`
+SAMBA_VERSION_RELEASE=`sed -n 's/^SAMBA_VERSION_RELEASE=//p' $SOURCE_DIR$VERSION_FILE`
+
+SAMBA_VERSION_REVISION=`sed -n 's/^SAMBA_VERSION_REVISION=//p' $SOURCE_DIR$VERSION_FILE`
+
+SAMBA_VERSION_PRE_RELEASE=`sed -n 's/^SAMBA_VERSION_PRE_RELEASE=//p' $SOURCE_DIR$VERSION_FILE`
+
+SAMBA_VERSION_RC_RELEASE=`sed -n 's/^SAMBA_VERSION_RC_RELEASE=//p' $SOURCE_DIR$VERSION_FILE`
+
+SAMBA_VERSION_BETA_RELEASE=`sed -n 's/^SAMBA_VERSION_BETA_RELEASE=//p' $SOURCE_DIR$VERSION_FILE`
+
+SAMBA_VERSION_ALPHA_RELEASE=`sed -n 's/^SAMBA_VERSION_ALPHA_RELEASE=//p' $SOURCE_DIR$VERSION_FILE`
+
+SAMBA_VERSION_TEST_RELEASE=`sed -n 's/^SAMBA_VERSION_TEST_RELEASE=//p' $SOURCE_DIR$VERSION_FILE`
+
+SAMBA_VERSION_IS_CVS_SNAPSHOT=`sed -n 's/^SAMBA_VERSION_IS_CVS_SNAPSHOT=//p' $SOURCE_DIR$VERSION_FILE`
+
+SAMBA_VERSION_VENDOR_SUFFIX=`sed -n 's/^SAMBA_VERSION_VENDOR_SUFFIX=//p' $SOURCE_DIR$VERSION_FILE`
+
+echo "/* Autogenerated by script/mkversion.sh */" > $OUTPUT_FILE
+
+echo "#define SAMBA_VERSION_MAJOR ${SAMBA_VERSION_MAJOR}" >> $OUTPUT_FILE
+echo "#define SAMBA_VERSION_MINOR ${SAMBA_VERSION_MINOR}" >> $OUTPUT_FILE
+echo "#define SAMBA_VERSION_RELEASE ${SAMBA_VERSION_RELEASE}" >> $OUTPUT_FILE
+
+
+SAMBA_VERSION_STRING="${SAMBA_VERSION_MAJOR}.${SAMBA_VERSION_MINOR}.${SAMBA_VERSION_RELEASE}"
+
+
+if test -n "${SAMBA_VERSION_REVISION}";then
+ SAMBA_VERSION_STRING="${SAMBA_VERSION_STRING}${SAMBA_VERSION_REVISION}"
+ echo "#define SAMBA_VERSION_REVISION \"${SAMBA_VERSION_REVISION}\"" >> $OUTPUT_FILE
+elif test -n "${SAMBA_VERSION_PRE_RELEASE}";then
+ SAMBA_VERSION_STRING="${SAMBA_VERSION_STRING}pre${SAMBA_VERSION_PRE_RELEASE}"
+ echo "#define SAMBA_VERSION_PRE_RELEASE ${SAMBA_VERSION_PRE_RELEASE}" >> $OUTPUT_FILE
+elif test -n "${SAMBA_VERSION_RC_RELEASE}";then
+ SAMBA_VERSION_STRING="${SAMBA_VERSION_STRING}rc${SAMBA_VERSION_RC_RELEASE}"
+ echo "#define SAMBA_VERSION_RC_RELEASE ${SAMBA_VERSION_RC_RELEASE}" >> $OUTPUT_FILE
+elif test -n "${SAMBA_VERSION_BETA_RELEASE}";then
+ SAMBA_VERSION_STRING="${SAMBA_VERSION_STRING}beta${SAMBA_VERSION_BETA_RELEASE}"
+ echo "#define SAMBA_VERSION_BETA_RELEASE ${SAMBA_VERSION_BETA_RELEASE}" >> $OUTPUT_FILE
+elif test -n "${SAMBA_VERSION_ALPHA_RELEASE}";then
+ SAMBA_VERSION_STRING="${SAMBA_VERSION_STRING}alpha${SAMBA_VERSION_ALPHA_RELEASE}"
+ echo "#define SAMBA_VERSION_ALPHA_RELEASE ${SAMBA_VERSION_ALPHA_RELEASE}" >> $OUTPUT_FILE
+elif test -n "${SAMBA_VERSION_TEST_RELEASE}";then
+ SAMBA_VERSION_STRING="${SAMBA_VERSION_STRING}test${SAMBA_VERSION_TEST_RELEASE}"
+ echo "#define SAMBA_VERSION_TEST_RELEASE ${SAMBA_VERSION_TEST_RELEASE}" >> $OUTPUT_FILE
+fi
+
+
+if test x"${SAMBA_VERSION_IS_CVS_SNAPSHOT}" = x"yes";then
+ SAMBA_VERSION_STRING="CVS ${SAMBA_VERSION_STRING}"
+ echo "#define SAMBA_VERSION_IS_CVS_SNAPSHOT 1" >> $OUTPUT_FILE
+fi
+
+if test -n "${SAMBA_VERSION_VENDOR_SUFFIX}";then
+ echo "#define SAMBA_VERSION_VENDOR_SUFFIX ${SAMBA_VERSION_VENDOR_SUFFIX}" >> $OUTPUT_FILE
+fi
+
+echo "#define SAMBA_VERSION_OFFICIAL_STRING \"${SAMBA_VERSION_STRING}\"" >> $OUTPUT_FILE
+
+echo "#define SAMBA_VERSION_STRING samba_version_string()" >> $OUTPUT_FILE
+
+echo "$0: 'include/version.h' created for Samba(\"${SAMBA_VERSION_STRING}\")"
+
+if test -n "${SAMBA_VERSION_VENDOR_SUFFIX}";then
+ echo "$0: with VENDOR_SUFFIX = ${SAMBA_VERSION_VENDOR_SUFFIX}"
+fi
+
+exit 0
diff --git a/source/script/revert.sh b/source/script/revert.sh
new file mode 100755
index 00000000000..8df5fd2fbde
--- /dev/null
+++ b/source/script/revert.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+BINDIR=$1
+shift
+
+for p in $*; do
+ p2=`basename $p`
+ if [ -f $BINDIR/$p2.old ]; then
+ echo Restoring $BINDIR/$p2.old
+ mv $BINDIR/$p2 $BINDIR/$p2.new
+ mv $BINDIR/$p2.old $BINDIR/$p2
+ rm -f $BINDIR/$p2.new
+ else
+ echo Not restoring $p
+ fi
+done
+
+exit 0
+
diff --git a/source/script/scancvslog.pl b/source/script/scancvslog.pl
new file mode 100755
index 00000000000..c39f9111c10
--- /dev/null
+++ b/source/script/scancvslog.pl
@@ -0,0 +1,112 @@
+#!/usr/bin/perl
+require"timelocal.pl";
+
+#
+# usage scancvslog.pl logfile starttime tag
+#
+# this will extract all entries from the specified cvs log file
+# that have a date later than or equal to starttime and a tag
+# value of tag. If starttime is not specified, all entries are
+# extracted. If tag is not specified then entries for all
+# branches are extracted. starttime must be specified as
+# "monthname day, year"
+#
+# Example to extract all entries for SAMBA_2_2 branch from the
+# log file named cvs.log
+#
+# scancvslog.pl cvs.log "" SAMBA_2_2
+#
+#
+# To extract all log entries after Jan 10, 1999 (Note month name
+# must be spelled out completely).
+#
+# scancvslog.pl cvs.log "January 10, 1999"
+#
+
+open(INFILE,@ARGV[0]) || die "Unable to open @ARGV[0]\n";
+
+%Monthnum = (
+ "January", 0,
+ "February", 1,
+ "March", 2,
+ "April", 3,
+ "May", 4,
+ "June", 5,
+ "July", 6,
+ "August", 7,
+ "September", 8,
+ "October", 9,
+ "November", 10,
+ "December", 11,
+ "Jan", 0,
+ "Feb", 1,
+ "Mar", 2,
+ "Apr", 3,
+ "May", 4,
+ "Jun", 5,
+ "Jul", 6,
+ "Aug", 7,
+ "Sep", 8,
+ "Oct", 9,
+ "Nov", 10,
+ "Dec", 11
+);
+
+$Starttime = (@ARGV[1]) ? &make_time(@ARGV[1]) : 0;
+$Tagvalue = @ARGV[2];
+
+while (&get_entry) {
+ $_=$Entry[0];
+# get rid of extra white space
+ s/\s+/ /g;
+# get rid of any time string in date
+ s/ \d\d:\d\d:\d\d/,/;
+ s/^Date:\s*\w*\s*(\w*)\s*(\w*),\s*(\w*).*/$1 $2 $3/;
+ $Testtime = &make_time($_);
+ $Testtag = &get_tag;
+ if (($Testtime >= $Starttime) && ($Tagvalue eq $Testtag)) {
+ print join("\n",@Entry),"\n";
+ }
+}
+close(INFILE);
+
+sub make_time {
+ $_ = @_[0];
+ s/,//;
+ ($month, $day, $year) = split(" ",$_);
+ if (($year < 1900)||($day < 1)||($day > 31)||not length($Monthnum{$month})) {
+ print "Bad date format @_[0]\n";
+ print "Date needs to be specified as \"Monthname day, year\"\n";
+ print "eg: \"January 10, 1999\"\n";
+ exit 1;
+ }
+ $year = ($year == 19100) ? 2000 : $year;
+ $month = $Monthnum{$month};
+ $Mytime=&timelocal((0,0,0,$day,$month,$year));
+}
+
+sub get_tag {
+ @Mytag = grep (/Tag:/,@Entry);
+ $_ = @Mytag[0];
+ s/^.*Tag:\s*(\w*).*/$1/;
+ return $_;
+}
+
+sub get_entry {
+ @Entry=();
+ if (not eof(INFILE)) {
+ while (not eof(INFILE)) {
+ $_ = <INFILE>;
+ chomp $_;
+ next if (not ($_));
+ if (/^\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/) {
+ next if ($#Entry == -1);
+ push(Entry,$_);
+ return @Entry;
+ } else {
+ push(Entry,$_);
+ }
+ }
+ }
+ return @Entry;
+}
diff --git a/source/script/smbtar b/source/script/smbtar
new file mode 100644
index 00000000000..67a794553cf
--- /dev/null
+++ b/source/script/smbtar
@@ -0,0 +1,165 @@
+#!/bin/sh
+#
+# smbtar script - front end to smbclient
+#
+# Authors: Martin.Kraemer <Martin.Kraemer@mch.sni.de>
+# and Ricky Poulten (ricky@logcam.co.uk)
+#
+# (May need to change shell to ksh for HPUX or OSF for better getopts)
+#
+# sandy nov 3 '98 added -a flag
+#
+# Richard Sharpe, added -c 'tarmode full' so that we back up all files to
+# fix a bug in clitar when a patch was added to stop system and hidden files
+# being backed up.
+
+case $0 in
+ # when called by absolute path, assume smbclient is in the same directory
+ /*)
+ SMBCLIENT="`dirname $0`/smbclient";;
+ *) # you may need to edit this to show where your smbclient is
+ SMBCLIENT="smbclient";;
+esac
+
+# These are the default values. You could fill them in if you know what
+# you're doing, but beware: better not store a plain text password!
+server=""
+service="backup" # Default: a service called "backup"
+password=""
+username=$LOGNAME # Default: same user name as in *nix
+verbose="2>/dev/null" # Default: no echo to stdout
+log="-d 2"
+newer=""
+newerarg=""
+blocksize=""
+blocksizearg=""
+clientargs="-c 'tarmode full'"
+tarcmd="c"
+tarargs=""
+cdcmd="\\"
+tapefile=${TAPE-tar.out}
+
+Usage(){
+ ex=$1
+ shift
+echo >&2 "Usage: `basename $0` [<options>] [<include/exclude files>]
+Function: backup/restore a Windows PC directories to a local tape file
+Options: (Description) (Default)
+ -r Restore from tape file to PC Save from PC to tapefile
+ -i Incremental mode Full backup mode
+ -a Reset archive bit mode Don't reset archive bit
+ -v Verbose mode: echo command Don't echo anything
+ -s <server> Specify PC Server $server
+ -p <password> Specify PC Password $password
+ -x <share> Specify PC Share $service
+ -X Exclude mode Include
+ -N <newer> File for date comparison `set -- $newer; echo $2`
+ -b <blocksize> Specify tape's blocksize `set -- $blocksize; echo $2`
+ -d <dir> Specify a directory in share $cdcmd
+ -l <log> Specify a Samba Log Level `set -- $log; echo $2`
+ -u <user> Specify User Name $username
+ -t <tape> Specify Tape device $tapefile
+"
+ echo >&2 "$@"
+ exit $ex
+}
+
+# echo Params count: $#
+
+# DEC OSF AKA Digital UNIX does not seem to return a value in OPTIND if
+# there are no command line params, so protect us against that ...
+if [ $# = 0 ]; then
+
+ Usage 2 "Please enter a command line parameter!"
+
+fi
+
+while getopts riavl:b:d:N:s:p:x:u:Xt: c; do
+ case $c in
+ r) # [r]estore to Windows (instead of the default "Save from Windows")
+ tarcmd="x"
+ ;;
+ i) # [i]ncremental
+ tarargs=${tarargs}ga
+ clientargs="-c 'tarmode inc'"
+ ;;
+ a) # [a]rchive
+ tarargs=${tarargs}a
+ ;;
+ l) # specify [l]og file
+ log="-d $OPTARG"
+ case "$OPTARG" in
+ [0-9]*) ;;
+ *) echo >&2 "$0: Error, log level not numeric: -l $OPTARG"
+ exit 1
+ esac
+ ;;
+ d) # specify [d]irectory to change to in server's share
+ cdcmd="$OPTARG"
+ ;;
+ N) # compare with a file, test if [n]ewer
+ if [ -f $OPTARG ]; then
+ newer=$OPTARG
+ newerarg="N"
+ else
+ echo >&2 $0: Warning, $OPTARG not found
+ fi
+ ;;
+ X) # Add exclude flag
+ tarargs=${tarargs}X
+ ;;
+ s) # specify [s]erver's share to connect to - this MUST be given.
+ server="$OPTARG"
+ ;;
+ b) # specify [b]locksize
+ blocksize="$OPTARG"
+ case "$OPTARG" in
+ [0-9]*) ;;
+ *) echo >&2 "$0: Error, block size not numeric: -b $OPTARG"
+ exit 1
+ esac
+ blocksizearg="b"
+ ;;
+ p) # specify [p]assword to use
+ password="$OPTARG"
+ ;;
+ x) # specify windows [s]hare to use
+ service="$OPTARG"
+ ;;
+ t) # specify [t]apefile on local host
+ tapefile="$OPTARG"
+ ;;
+ u) # specify [u]sername for connection
+ username="$OPTARG"
+ ;;
+ v) # be [v]erbose and display what's going on
+ verbose=""
+ ;;
+ '?') # any other switch
+ Usage 2 "Invalid switch specified - abort."
+ ;;
+ esac
+done
+
+shift `expr $OPTIND - 1`
+
+if [ "$server" = "" ] || [ "$service" = "" ]; then
+ Usage 1 "No server or no service specified - abort."
+fi
+
+# if the -v switch is set, the echo the current parameters
+if [ -z "$verbose" ]; then
+ echo "server is $server"
+# echo "share is $service"
+ echo "share is $service\\$cdcmd"
+ echo "tar args is $tarargs"
+# echo "password is $password" # passwords should never be sent to screen
+ echo "tape is $tapefile"
+ echo "blocksize is $blocksize"
+fi
+
+tarargs=${tarargs}${blocksizearg}${newerarg}
+
+eval $SMBCLIENT "'\\\\$server\\$service'" "'$password'" -U "'$username'" \
+-E $log -D "'$cdcmd'" ${clientargs} \
+-T${tarcmd}${tarargs} $blocksize $newer $tapefile '${1+"$@"}' $verbose
diff --git a/source/script/uninstallbin.sh b/source/script/uninstallbin.sh
new file mode 100755
index 00000000000..5de936fccfd
--- /dev/null
+++ b/source/script/uninstallbin.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+#4 July 96 Dan.Shearer@UniSA.edu.au
+
+INSTALLPERMS=$1
+BASEDIR=`echo $2 | sed 's/\/\//\//g'`
+BINDIR=`echo $3 | sed 's/\/\//\//g'`
+LIBDIR=`echo $4 | sed 's/\/\//\//g'`
+VARDIR=`echo $5 | sed 's/\/\//\//g'`
+shift
+shift
+shift
+shift
+shift
+
+if [ ! -d $BINDIR ]; then
+ echo Directory $BINDIR does not exist!
+ echo Do a "make installbin" or "make install" first.
+ exit 1
+fi
+
+for p in $*; do
+ p2=`basename $p`
+ if [ -f $BINDIR/$p2 ]; then
+ echo Removing $BINDIR/$p2
+ rm -f $BINDIR/$p2
+ if [ -f $BINDIR/$p2 ]; then
+ echo Cannot remove $BINDIR/$p2 ... does $USER have privileges?
+ fi
+ fi
+done
+
+
+cat << EOF
+======================================================================
+The binaries have been uninstalled. You may restore the binaries using
+the command "make installbin" or "make install" to install binaries,
+man pages, modules and shell scripts. You can restore a previous
+version of the binaries (if there were any) using "make revert".
+======================================================================
+EOF
+
+exit 0
diff --git a/source/script/uninstallman.sh b/source/script/uninstallman.sh
new file mode 100755
index 00000000000..0fea11cd1b2
--- /dev/null
+++ b/source/script/uninstallman.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+#4 July 96 Dan.Shearer@UniSA.edu.au
+#
+# 13 Aug 2001 Rafal Szczesniak <mimir@spin.ict.pwr.wroc.pl>
+# modified to accomodate international man pages (inspired
+# by Japanese edition's approach)
+
+
+MANDIR=`echo $1 | sed 's/\/\//\//g'`
+SRCDIR=$2
+langs=$3
+
+for lang in $langs; do
+ echo Uninstalling \"$lang\" man pages from $MANDIR/$lang
+
+ for sect in 1 5 7 8 ; do
+ for m in $MANDIR/$lang/man$sect ; do
+ for s in $SRCDIR/../docs/manpages/$lang/*$sect; do
+ FNAME=$m/`basename $s`
+ if test -f $FNAME; then
+ echo Deleting $FNAME
+ rm -f $FNAME
+ test -f $FNAME && echo Cannot remove $FNAME... does $USER have privileges?
+ fi
+ done
+ done
+ done
+done
+
+cat << EOF
+======================================================================
+The man pages have been uninstalled. You may install them again using
+the command "make installman" or make "install" to install binaries,
+man pages and shell scripts.
+======================================================================
+EOF
+exit 0
diff --git a/source/script/uninstallmodules.sh b/source/script/uninstallmodules.sh
new file mode 100755
index 00000000000..ac83af3dc90
--- /dev/null
+++ b/source/script/uninstallmodules.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+#4 July 96 Dan.Shearer@UniSA.edu.au
+
+INSTALLPERMS=$1
+BASEDIR=`echo $2 | sed 's/\/\//\//g'`
+LIBDIR=`echo $3 | sed 's/\/\//\//g'`
+shift
+shift
+shift
+
+if [ ! -d $LIBDIR ]; then
+ echo Directory $LIBDIR does not exist!
+ echo Do a "make installmodules" or "make install" first.
+ exit 1
+fi
+
+for p in $*; do
+ p2=`basename $p`
+ if [ -f $LIBDIR/$p2 ]; then
+ echo Removing $LIBDIR/$p2
+ rm -f $LIBDIR/$p2
+ if [ -f $LIBDIR/$p2 ]; then
+ echo Cannot remove $LIBDIR/$p2 ... does $USER have privileges?
+ fi
+ fi
+done
+
+
+cat << EOF
+======================================================================
+The modules have been uninstalled. You may restore the modules using
+the command "make installmodules" or "make install" to install
+binaries, modules, man pages and shell scripts.
+======================================================================
+EOF
+
+exit 0
diff --git a/source/script/uninstallscripts.sh b/source/script/uninstallscripts.sh
new file mode 100755
index 00000000000..cf7fd719993
--- /dev/null
+++ b/source/script/uninstallscripts.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+# 5 July 96 Dan.Shearer@UniSA.Edu.Au - almost identical to uninstallbin.sh
+
+INSTALLPERMS=$1
+BINDIR=`echo $2 | sed 's/\/\//\//g'`
+
+shift
+shift
+
+if [ ! -d $BINDIR ]; then
+ echo Directory $BINDIR does not exist!
+ echo Do a "make installscripts" or "make install" first.
+ exit 1
+fi
+
+for p in $*; do
+ p2=`basename $p`
+ if [ -f $BINDIR/$p2 ]; then
+ echo Removing $BINDIR/$p2
+ rm -f $BINDIR/$p2
+ if [ -f $BINDIR/$p2 ]; then
+ echo Cannot remove $BINDIR/$p2 ... does $USER have privileges?
+ fi
+ fi
+done
+
+cat << EOF
+======================================================================
+The scripts have been uninstalled. You may reinstall them using
+the command "make installscripts" or "make install" to install binaries,
+man pages and shell scripts. You may recover a previous version (if any
+with "make revert".
+======================================================================
+EOF
+
+exit 0
diff --git a/source/script/updatesmbpasswd.sh b/source/script/updatesmbpasswd.sh
new file mode 100755
index 00000000000..1d7e0d7332f
--- /dev/null
+++ b/source/script/updatesmbpasswd.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+nawk 'BEGIN {FS=":"}
+{
+ if( $0 ~ "^#" ) {
+ print $0
+ } else if( (length($4) == 32) && (($4 ~ "^[0-9A-F]*$") || ($4 ~ "^[X]*$") || ( $4 ~ "^[*]*$"))) {
+ print $0
+ } else {
+ printf( "%s:%s:%s:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:", $1, $2, $3);
+ for(i = 4; i <= NF; i++)
+ printf("%s:", $i)
+ printf("\n")
+ }
+}'
diff --git a/source/smbadduser.in b/source/smbadduser.in
new file mode 100644
index 00000000000..05da7de08ee
--- /dev/null
+++ b/source/smbadduser.in
@@ -0,0 +1,79 @@
+#!/bin/csh
+#
+# smbadduser - Written by Mike Zakharoff
+#
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+LIBDIR=@libdir@
+PRIVATEDIR=@privatedir@
+CONFIGDIR=@configdir@
+
+unalias *
+set path = ($path /usr/local/samba/bin)
+
+set smbpasswd = $PRIVATEDIR/smbpasswd
+set user_map = $CONFIGDIR/users.map
+
+#
+# Set to site specific passwd command
+#
+set passwd = "cat /etc/passwd"
+#set passwd = "niscat passwd.org_dir"
+#set passwd = "ypcat passwd"
+
+set line = "----------------------------------------------------------"
+if ($#argv == 0) then
+ echo $line
+ echo "Written: Mike Zakharoff email: michael.j.zakharoff@boeing.com"
+ echo ""
+ echo " 1) Updates $smbpasswd"
+ echo " 2) Updates $user_map"
+ echo " 3) Executes smbpasswd for each new user"
+ echo ""
+ echo "smbadduser unixid:ntid unixid:ntid ..."
+ echo ""
+ echo "Example: smbadduser zak:zakharoffm johns:smithj"
+ echo $line
+ exit 1
+endif
+
+touch $smbpasswd $user_map
+set new = ()
+foreach one ($argv)
+ echo $one | grep ':' >& /dev/null
+ if ($status != 0) then
+ echo "ERROR: Must use unixid:ntid like -> zak:zakharoffm"
+ continue
+ endif
+ set unix = `echo $one | awk -F: '{print $1}'`
+ set ntid = `echo $one | awk -F: '{print $2}'`
+
+ set usr = `eval $passwd | awk -F: '$1==USR {print $1}' USR=$unix`
+ if ($#usr != 1) then
+ echo "ERROR: $unix Not in passwd database SKIPPING..."
+ continue
+ endif
+ set tmp = `cat $smbpasswd | awk -F: '$1==USR {print $1}' USR=$unix`
+ if ($#tmp != 0) then
+ echo "ERROR: $unix is already in $smbpasswd SKIPPING..."
+ continue
+ endif
+
+ echo "Adding: $unix to $smbpasswd"
+ /usr/bin/smbpasswd -a -n $unix
+ if ($unix != $ntid) then
+ echo "Adding: {$unix = $ntid} to $user_map"
+ echo "$unix = $ntid" >> $user_map
+ endif
+ set new = ($new $unix)
+end
+
+#
+# Enter password for new users
+#
+foreach one ($new)
+ echo $line
+ echo "ENTER password for $one"
+ smbpasswd $one
+end
diff --git a/source/smbd/.cvsignore b/source/smbd/.cvsignore
new file mode 100644
index 00000000000..d2b1fd5b2ee
--- /dev/null
+++ b/source/smbd/.cvsignore
@@ -0,0 +1,3 @@
+*.po
+*.po32
+build_options.c
diff --git a/source/smbd/blocking.c b/source/smbd/blocking.c
new file mode 100644
index 00000000000..c0512d5539b
--- /dev/null
+++ b/source/smbd/blocking.c
@@ -0,0 +1,726 @@
+/*
+ Unix SMB/CIFS implementation.
+ Blocking Locking functions
+ Copyright (C) Jeremy Allison 1998-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+extern char *OutBuffer;
+
+/****************************************************************************
+ This is the structure to queue to implement blocking locks.
+ notify. It consists of the requesting SMB and the expiry time.
+*****************************************************************************/
+
+typedef struct {
+ ubi_slNode msg_next;
+ int com_type;
+ files_struct *fsp;
+ time_t expire_time;
+ int lock_num;
+ SMB_BIG_UINT offset;
+ SMB_BIG_UINT count;
+ uint16 lock_pid;
+ char *inbuf;
+ int length;
+} blocking_lock_record;
+
+static ubi_slList blocking_lock_queue = { NULL, (ubi_slNodePtr)&blocking_lock_queue, 0};
+
+/****************************************************************************
+ Destructor for the above structure.
+****************************************************************************/
+
+static void free_blocking_lock_record(blocking_lock_record *blr)
+{
+ SAFE_FREE(blr->inbuf);
+ SAFE_FREE(blr);
+}
+
+/****************************************************************************
+ Get the files_struct given a particular queued SMB.
+*****************************************************************************/
+
+static files_struct *get_fsp_from_pkt(char *inbuf)
+{
+ switch(CVAL(inbuf,smb_com)) {
+ case SMBlock:
+ case SMBlockread:
+ return file_fsp(inbuf,smb_vwv0);
+ case SMBlockingX:
+ return file_fsp(inbuf,smb_vwv2);
+ default:
+ DEBUG(0,("get_fsp_from_pkt: PANIC - unknown type on blocking lock queue - exiting.!\n"));
+ exit_server("PANIC - unknown type on blocking lock queue");
+ }
+ return NULL; /* Keep compiler happy. */
+}
+
+/****************************************************************************
+ Determine if this is a secondary element of a chained SMB.
+ **************************************************************************/
+
+static BOOL in_chained_smb(void)
+{
+ return (chain_size != 0);
+}
+
+static void received_unlock_msg(int msg_type, pid_t src, void *buf, size_t len);
+
+/****************************************************************************
+ Function to push a blocking lock request onto the lock queue.
+****************************************************************************/
+
+BOOL push_blocking_lock_request( char *inbuf, int length, int lock_timeout,
+ int lock_num, uint16 lock_pid, SMB_BIG_UINT offset, SMB_BIG_UINT count)
+{
+ static BOOL set_lock_msg;
+ blocking_lock_record *blr;
+ BOOL my_lock_ctx = False;
+ NTSTATUS status;
+
+ if(in_chained_smb() ) {
+ DEBUG(0,("push_blocking_lock_request: cannot queue a chained request (currently).\n"));
+ return False;
+ }
+
+ /*
+ * Now queue an entry on the blocking lock queue. We setup
+ * the expiration time here.
+ */
+
+ if((blr = (blocking_lock_record *)malloc(sizeof(blocking_lock_record))) == NULL) {
+ DEBUG(0,("push_blocking_lock_request: Malloc fail !\n" ));
+ return False;
+ }
+
+ if((blr->inbuf = (char *)malloc(length)) == NULL) {
+ DEBUG(0,("push_blocking_lock_request: Malloc fail (2)!\n" ));
+ SAFE_FREE(blr);
+ return False;
+ }
+
+ blr->com_type = CVAL(inbuf,smb_com);
+ blr->fsp = get_fsp_from_pkt(inbuf);
+ blr->expire_time = (lock_timeout == -1) ? (time_t)-1 : time(NULL) + (time_t)lock_timeout;
+ blr->lock_num = lock_num;
+ blr->lock_pid = lock_pid;
+ blr->offset = offset;
+ blr->count = count;
+ memcpy(blr->inbuf, inbuf, length);
+ blr->length = length;
+
+ /* Add a pending lock record for this. */
+ status = brl_lock(blr->fsp->dev, blr->fsp->inode, blr->fsp->fnum,
+ lock_pid, sys_getpid(), blr->fsp->conn->cnum,
+ offset, count, PENDING_LOCK, &my_lock_ctx);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("push_blocking_lock_request: failed to add PENDING_LOCK record.\n"));
+ free_blocking_lock_record(blr);
+ return False;
+ }
+
+ ubi_slAddTail(&blocking_lock_queue, blr);
+
+ /* Ensure we'll receive messages when this is unlocked. */
+ if (!set_lock_msg) {
+ message_register(MSG_SMB_UNLOCK, received_unlock_msg);
+ set_lock_msg = True;
+ }
+
+ DEBUG(3,("push_blocking_lock_request: lock request length=%d blocked with expiry time %d (+%d) \
+for fnum = %d, name = %s\n", length, (int)blr->expire_time, lock_timeout,
+ blr->fsp->fnum, blr->fsp->fsp_name ));
+
+ /* Push the MID of this packet on the signing queue. */
+ srv_defer_sign_response(SVAL(inbuf,smb_mid));
+
+ return True;
+}
+
+/****************************************************************************
+ Return a smd with a given size.
+*****************************************************************************/
+
+static void send_blocking_reply(char *outbuf, int outsize)
+{
+ if(outsize > 4)
+ smb_setlen(outbuf,outsize - 4);
+
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("send_blocking_reply: send_smb failed.");
+}
+
+/****************************************************************************
+ Return a lockingX success SMB.
+*****************************************************************************/
+
+static void reply_lockingX_success(blocking_lock_record *blr)
+{
+ char *outbuf = OutBuffer;
+ int bufsize = BUFFER_SIZE;
+ char *inbuf = blr->inbuf;
+ int outsize = 0;
+
+ construct_reply_common(inbuf, outbuf);
+ set_message(outbuf,2,0,True);
+
+ /*
+ * As this message is a lockingX call we must handle
+ * any following chained message correctly.
+ * This is normally handled in construct_reply(),
+ * but as that calls switch_message, we can't use
+ * that here and must set up the chain info manually.
+ */
+
+ outsize = chain_reply(inbuf,outbuf,blr->length,bufsize);
+
+ outsize += chain_size;
+
+ send_blocking_reply(outbuf,outsize);
+}
+
+/****************************************************************************
+ Return a generic lock fail error blocking call.
+*****************************************************************************/
+
+static void generic_blocking_lock_error(blocking_lock_record *blr, NTSTATUS status)
+{
+ char *outbuf = OutBuffer;
+ char *inbuf = blr->inbuf;
+ construct_reply_common(inbuf, outbuf);
+
+ /* whenever a timeout is given w2k maps LOCK_NOT_GRANTED to
+ FILE_LOCK_CONFLICT! (tridge) */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_LOCK_NOT_GRANTED)) {
+ status = NT_STATUS_FILE_LOCK_CONFLICT;
+ }
+
+ ERROR_NT(status);
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("generic_blocking_lock_error: send_smb failed.");
+}
+
+/****************************************************************************
+ Return a lock fail error for a lockingX call. Undo all the locks we have
+ obtained first.
+*****************************************************************************/
+
+static void reply_lockingX_error(blocking_lock_record *blr, NTSTATUS status)
+{
+ char *inbuf = blr->inbuf;
+ files_struct *fsp = blr->fsp;
+ connection_struct *conn = conn_find(SVAL(inbuf,smb_tid));
+ uint16 num_ulocks = SVAL(inbuf,smb_vwv6);
+ SMB_BIG_UINT count = (SMB_BIG_UINT)0, offset = (SMB_BIG_UINT) 0;
+ uint16 lock_pid;
+ unsigned char locktype = CVAL(inbuf,smb_vwv3);
+ BOOL large_file_format = (locktype & LOCKING_ANDX_LARGE_FILES);
+ char *data;
+ int i;
+
+ data = smb_buf(inbuf) + ((large_file_format ? 20 : 10)*num_ulocks);
+
+ /*
+ * Data now points at the beginning of the list
+ * of smb_lkrng structs.
+ */
+
+ /*
+ * Ensure we don't do a remove on the lock that just failed,
+ * as under POSIX rules, if we have a lock already there, we
+ * will delete it (and we shouldn't) .....
+ */
+
+ for(i = blr->lock_num - 1; i >= 0; i--) {
+ BOOL err;
+
+ lock_pid = get_lock_pid( data, i, large_file_format);
+ count = get_lock_count( data, i, large_file_format);
+ offset = get_lock_offset( data, i, large_file_format, &err);
+
+ /*
+ * We know err cannot be set as if it was the lock
+ * request would never have been queued. JRA.
+ */
+
+ do_unlock(fsp,conn,lock_pid,count,offset);
+ }
+
+ generic_blocking_lock_error(blr, status);
+}
+
+/****************************************************************************
+ Return a lock fail error.
+*****************************************************************************/
+
+static void blocking_lock_reply_error(blocking_lock_record *blr, NTSTATUS status)
+{
+ switch(blr->com_type) {
+ case SMBlock:
+ case SMBlockread:
+ generic_blocking_lock_error(blr, status);
+ break;
+ case SMBlockingX:
+ reply_lockingX_error(blr, status);
+ break;
+ default:
+ DEBUG(0,("blocking_lock_reply_error: PANIC - unknown type on blocking lock queue - exiting.!\n"));
+ exit_server("PANIC - unknown type on blocking lock queue");
+ }
+}
+
+/****************************************************************************
+ Attempt to finish off getting all pending blocking locks for a lockread call.
+ Returns True if we want to be removed from the list.
+*****************************************************************************/
+
+static BOOL process_lockread(blocking_lock_record *blr)
+{
+ char *outbuf = OutBuffer;
+ char *inbuf = blr->inbuf;
+ ssize_t nread = -1;
+ char *data, *p;
+ int outsize = 0;
+ SMB_BIG_UINT startpos;
+ size_t numtoread;
+ NTSTATUS status;
+ connection_struct *conn = conn_find(SVAL(inbuf,smb_tid));
+ files_struct *fsp = blr->fsp;
+ BOOL my_lock_ctx = False;
+
+ numtoread = SVAL(inbuf,smb_vwv1);
+ startpos = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv2);
+
+ numtoread = MIN(BUFFER_SIZE-outsize,numtoread);
+ data = smb_buf(outbuf) + 3;
+
+ status = do_lock_spin( fsp, conn, SVAL(inbuf,smb_pid), (SMB_BIG_UINT)numtoread, startpos, READ_LOCK, &my_lock_ctx);
+ if (NT_STATUS_V(status)) {
+ if (!NT_STATUS_EQUAL(status,NT_STATUS_LOCK_NOT_GRANTED) &&
+ !NT_STATUS_EQUAL(status,NT_STATUS_FILE_LOCK_CONFLICT)) {
+ /*
+ * We have other than a "can't get lock"
+ * error. Send an error.
+ * Return True so we get dequeued.
+ */
+ generic_blocking_lock_error(blr, status);
+ return True;
+ }
+
+ /*
+ * Still waiting for lock....
+ */
+
+ DEBUG(10,("process_lockread: failed to get lock for file = %s. Still waiting....\n",
+ fsp->fsp_name));
+ return False;
+ }
+
+ nread = read_file(fsp,data,startpos,numtoread);
+
+ if (nread < 0) {
+ generic_blocking_lock_error(blr,NT_STATUS_ACCESS_DENIED);
+ return True;
+ }
+
+ construct_reply_common(inbuf, outbuf);
+ outsize = set_message(outbuf,5,0,True);
+
+ outsize += nread;
+ SSVAL(outbuf,smb_vwv0,nread);
+ SSVAL(outbuf,smb_vwv5,nread+3);
+ p = smb_buf(outbuf);
+ *p++ = 1;
+ SSVAL(p,0,nread); p += 2;
+ set_message_end(outbuf, p+nread);
+
+ DEBUG(3, ( "process_lockread file = %s, fnum=%d num=%d nread=%d\n",
+ fsp->fsp_name, fsp->fnum, (int)numtoread, (int)nread ) );
+
+ send_blocking_reply(outbuf,outsize);
+ return True;
+}
+
+/****************************************************************************
+ Attempt to finish off getting all pending blocking locks for a lock call.
+ Returns True if we want to be removed from the list.
+*****************************************************************************/
+
+static BOOL process_lock(blocking_lock_record *blr)
+{
+ char *outbuf = OutBuffer;
+ char *inbuf = blr->inbuf;
+ int outsize;
+ SMB_BIG_UINT count = (SMB_BIG_UINT)0, offset = (SMB_BIG_UINT)0;
+ NTSTATUS status;
+ connection_struct *conn = conn_find(SVAL(inbuf,smb_tid));
+ files_struct *fsp = blr->fsp;
+ BOOL my_lock_ctx = False;
+
+ count = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv1);
+ offset = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv3);
+
+ errno = 0;
+ status = do_lock_spin(fsp, conn, SVAL(inbuf,smb_pid), count, offset, WRITE_LOCK, &my_lock_ctx);
+ if (NT_STATUS_IS_ERR(status)) {
+ if (!NT_STATUS_EQUAL(status,NT_STATUS_LOCK_NOT_GRANTED) &&
+ !NT_STATUS_EQUAL(status,NT_STATUS_FILE_LOCK_CONFLICT)) {
+ /*
+ * We have other than a "can't get lock"
+ * error. Send an error.
+ * Return True so we get dequeued.
+ */
+
+ blocking_lock_reply_error(blr, status);
+ return True;
+ }
+ /*
+ * Still can't get the lock - keep waiting.
+ */
+ DEBUG(10,("process_lock: failed to get lock for file = %s. Still waiting....\n",
+ fsp->fsp_name));
+ return False;
+ }
+
+ /*
+ * Success - we got the lock.
+ */
+
+ DEBUG(3,("process_lock : file=%s fnum=%d offset=%.0f count=%.0f\n",
+ fsp->fsp_name, fsp->fnum, (double)offset, (double)count));
+
+ construct_reply_common(inbuf, outbuf);
+ outsize = set_message(outbuf,0,0,True);
+ send_blocking_reply(outbuf,outsize);
+ return True;
+}
+
+/****************************************************************************
+ Attempt to finish off getting all pending blocking locks for a lockingX call.
+ Returns True if we want to be removed from the list.
+*****************************************************************************/
+
+static BOOL process_lockingX(blocking_lock_record *blr)
+{
+ char *inbuf = blr->inbuf;
+ unsigned char locktype = CVAL(inbuf,smb_vwv3);
+ files_struct *fsp = blr->fsp;
+ connection_struct *conn = conn_find(SVAL(inbuf,smb_tid));
+ uint16 num_ulocks = SVAL(inbuf,smb_vwv6);
+ uint16 num_locks = SVAL(inbuf,smb_vwv7);
+ SMB_BIG_UINT count = (SMB_BIG_UINT)0, offset = (SMB_BIG_UINT)0;
+ uint16 lock_pid;
+ BOOL large_file_format = (locktype & LOCKING_ANDX_LARGE_FILES);
+ char *data;
+ BOOL my_lock_ctx = False;
+ NTSTATUS status = NT_STATUS_OK;
+
+ data = smb_buf(inbuf) + ((large_file_format ? 20 : 10)*num_ulocks);
+
+ /*
+ * Data now points at the beginning of the list
+ * of smb_lkrng structs.
+ */
+
+ for(; blr->lock_num < num_locks; blr->lock_num++) {
+ BOOL err;
+
+ lock_pid = get_lock_pid( data, blr->lock_num, large_file_format);
+ count = get_lock_count( data, blr->lock_num, large_file_format);
+ offset = get_lock_offset( data, blr->lock_num, large_file_format, &err);
+
+ /*
+ * We know err cannot be set as if it was the lock
+ * request would never have been queued. JRA.
+ */
+ errno = 0;
+ status = do_lock_spin(fsp,conn,lock_pid,count,offset,
+ ((locktype & 1) ? READ_LOCK : WRITE_LOCK), &my_lock_ctx);
+ if (NT_STATUS_IS_ERR(status)) break;
+ }
+
+ if(blr->lock_num == num_locks) {
+ /*
+ * Success - we got all the locks.
+ */
+
+ DEBUG(3,("process_lockingX file = %s, fnum=%d type=%d num_locks=%d\n",
+ fsp->fsp_name, fsp->fnum, (unsigned int)locktype, num_locks) );
+
+ reply_lockingX_success(blr);
+ return True;
+ } else if (!NT_STATUS_EQUAL(status,NT_STATUS_LOCK_NOT_GRANTED) &&
+ !NT_STATUS_EQUAL(status,NT_STATUS_FILE_LOCK_CONFLICT)) {
+ /*
+ * We have other than a "can't get lock"
+ * error. Free any locks we had and return an error.
+ * Return True so we get dequeued.
+ */
+
+ blocking_lock_reply_error(blr, status);
+ return True;
+ }
+
+ /*
+ * Still can't get all the locks - keep waiting.
+ */
+
+ DEBUG(10,("process_lockingX: only got %d locks of %d needed for file %s, fnum = %d. \
+Waiting....\n",
+ blr->lock_num, num_locks, fsp->fsp_name, fsp->fnum));
+
+ return False;
+}
+
+/****************************************************************************
+ Process a blocking lock SMB.
+ Returns True if we want to be removed from the list.
+*****************************************************************************/
+
+static BOOL blocking_lock_record_process(blocking_lock_record *blr)
+{
+ switch(blr->com_type) {
+ case SMBlock:
+ return process_lock(blr);
+ case SMBlockread:
+ return process_lockread(blr);
+ case SMBlockingX:
+ return process_lockingX(blr);
+ default:
+ DEBUG(0,("blocking_lock_record_process: PANIC - unknown type on blocking lock queue - exiting.!\n"));
+ exit_server("PANIC - unknown type on blocking lock queue");
+ }
+ return False; /* Keep compiler happy. */
+}
+
+/****************************************************************************
+ Delete entries by fnum from the blocking lock pending queue.
+*****************************************************************************/
+
+void remove_pending_lock_requests_by_fid(files_struct *fsp)
+{
+ blocking_lock_record *blr = (blocking_lock_record *)ubi_slFirst( &blocking_lock_queue );
+ blocking_lock_record *prev = NULL;
+
+ while(blr != NULL) {
+ if(blr->fsp->fnum == fsp->fnum) {
+
+ DEBUG(10,("remove_pending_lock_requests_by_fid - removing request type %d for \
+file %s fnum = %d\n", blr->com_type, fsp->fsp_name, fsp->fnum ));
+
+ brl_unlock(blr->fsp->dev, blr->fsp->inode, blr->fsp->fnum,
+ blr->lock_pid, sys_getpid(), blr->fsp->conn->cnum,
+ blr->offset, blr->count, True, NULL, NULL);
+
+ free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev));
+ blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue));
+ continue;
+ }
+
+ prev = blr;
+ blr = (blocking_lock_record *)ubi_slNext(blr);
+ }
+}
+
+/****************************************************************************
+ Delete entries by mid from the blocking lock pending queue. Always send reply.
+*****************************************************************************/
+
+void remove_pending_lock_requests_by_mid(int mid)
+{
+ blocking_lock_record *blr = (blocking_lock_record *)ubi_slFirst( &blocking_lock_queue );
+ blocking_lock_record *prev = NULL;
+
+ while(blr != NULL) {
+ if(SVAL(blr->inbuf,smb_mid) == mid) {
+ files_struct *fsp = blr->fsp;
+
+ DEBUG(10,("remove_pending_lock_requests_by_mid - removing request type %d for \
+file %s fnum = %d\n", blr->com_type, fsp->fsp_name, fsp->fnum ));
+
+ blocking_lock_reply_error(blr,NT_STATUS_FILE_LOCK_CONFLICT);
+ brl_unlock(blr->fsp->dev, blr->fsp->inode, blr->fsp->fnum,
+ blr->lock_pid, sys_getpid(), blr->fsp->conn->cnum,
+ blr->offset, blr->count, True, NULL, NULL);
+ free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev));
+ blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue));
+ continue;
+ }
+
+ prev = blr;
+ blr = (blocking_lock_record *)ubi_slNext(blr);
+ }
+}
+
+/****************************************************************************
+ Set a flag as an unlock request affects one of our pending locks.
+*****************************************************************************/
+
+static void received_unlock_msg(int msg_type, pid_t src, void *buf, size_t len)
+{
+ DEBUG(10,("received_unlock_msg\n"));
+ process_blocking_lock_queue(time(NULL));
+}
+
+/****************************************************************************
+ Return the number of seconds to the next blocking locks timeout, or default_timeout
+*****************************************************************************/
+
+unsigned blocking_locks_timeout(unsigned default_timeout)
+{
+ unsigned timeout = default_timeout;
+ time_t t;
+ blocking_lock_record *blr = (blocking_lock_record *)ubi_slFirst(&blocking_lock_queue);
+
+ /* note that we avoid the time() syscall if there are no blocking locks */
+ if (!blr)
+ return timeout;
+
+ t = time(NULL);
+
+ while (blr) {
+ if ((blr->expire_time != (time_t)-1) &&
+ (timeout > (blr->expire_time - t))) {
+ timeout = blr->expire_time - t;
+ }
+ blr = (blocking_lock_record *)ubi_slNext(blr);
+ }
+
+ if (timeout < 1)
+ timeout = 1;
+
+ return timeout;
+}
+
+/****************************************************************************
+ Process the blocking lock queue. Note that this is only called as root.
+*****************************************************************************/
+
+void process_blocking_lock_queue(time_t t)
+{
+ blocking_lock_record *blr = (blocking_lock_record *)ubi_slFirst( &blocking_lock_queue );
+ blocking_lock_record *prev = NULL;
+
+ if(blr == NULL)
+ return;
+
+ /*
+ * Go through the queue and see if we can get any of the locks.
+ */
+
+ while(blr != NULL) {
+ connection_struct *conn = NULL;
+ uint16 vuid;
+ files_struct *fsp = NULL;
+
+ /*
+ * Ensure we don't have any old chain_fsp values
+ * sitting around....
+ */
+ chain_size = 0;
+ file_chain_reset();
+ fsp = blr->fsp;
+
+ conn = conn_find(SVAL(blr->inbuf,smb_tid));
+ vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID :
+ SVAL(blr->inbuf,smb_uid);
+
+ DEBUG(5,("process_blocking_lock_queue: examining pending lock fnum = %d for file %s\n",
+ fsp->fnum, fsp->fsp_name ));
+
+ if((blr->expire_time != -1) && (blr->expire_time <= t)) {
+ /*
+ * Lock expired - throw away all previously
+ * obtained locks and return lock error.
+ */
+ DEBUG(5,("process_blocking_lock_queue: pending lock fnum = %d for file %s timed out.\n",
+ fsp->fnum, fsp->fsp_name ));
+
+ brl_unlock(fsp->dev, fsp->inode, fsp->fnum,
+ blr->lock_pid, sys_getpid(), conn->cnum,
+ blr->offset, blr->count, True, NULL, NULL);
+
+ blocking_lock_reply_error(blr,NT_STATUS_FILE_LOCK_CONFLICT);
+ free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev));
+ blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue));
+ continue;
+ }
+
+ if(!change_to_user(conn,vuid)) {
+ DEBUG(0,("process_blocking_lock_queue: Unable to become user vuid=%d.\n",
+ vuid ));
+ /*
+ * Remove the entry and return an error to the client.
+ */
+ blocking_lock_reply_error(blr,NT_STATUS_ACCESS_DENIED);
+
+ brl_unlock(fsp->dev, fsp->inode, fsp->fnum,
+ blr->lock_pid, sys_getpid(), conn->cnum,
+ blr->offset, blr->count, True, NULL, NULL);
+
+ free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev));
+ blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue));
+ continue;
+ }
+
+ if(!set_current_service(conn,True)) {
+ DEBUG(0,("process_blocking_lock_queue: Unable to become service Error was %s.\n", strerror(errno) ));
+ /*
+ * Remove the entry and return an error to the client.
+ */
+ blocking_lock_reply_error(blr,NT_STATUS_ACCESS_DENIED);
+
+ brl_unlock(fsp->dev, fsp->inode, fsp->fnum,
+ blr->lock_pid, sys_getpid(), conn->cnum,
+ blr->offset, blr->count, True, NULL, NULL);
+
+ free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev));
+ blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue));
+ change_to_root_user();
+ continue;
+ }
+
+ /*
+ * Go through the remaining locks and try and obtain them.
+ * The call returns True if all locks were obtained successfully
+ * and False if we still need to wait.
+ */
+
+ if(blocking_lock_record_process(blr)) {
+
+ brl_unlock(fsp->dev, fsp->inode, fsp->fnum,
+ blr->lock_pid, sys_getpid(), conn->cnum,
+ blr->offset, blr->count, True, NULL, NULL);
+
+ free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev));
+ blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue));
+ change_to_root_user();
+ continue;
+ }
+
+ change_to_root_user();
+
+ /*
+ * Move to the next in the list.
+ */
+ prev = blr;
+ blr = (blocking_lock_record *)ubi_slNext(blr);
+ }
+}
diff --git a/source/smbd/change_trust_pw.c b/source/smbd/change_trust_pw.c
new file mode 100644
index 00000000000..1178400e4db
--- /dev/null
+++ b/source/smbd/change_trust_pw.c
@@ -0,0 +1,99 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Periodic Trust account password changing.
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997.
+ * Copyright (C) Jeremy Allison 1998.
+ * Copyright (C) Andrew Bartlett 2001.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+/************************************************************************
+ Change the trust account password for a domain.
+************************************************************************/
+
+NTSTATUS change_trust_account_password( const char *domain, const char *remote_machine)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct in_addr pdc_ip;
+ fstring dc_name;
+ struct cli_state *cli;
+
+ DEBUG(5,("change_trust_account_password: Attempting to change trust account password in domain %s....\n",
+ domain));
+
+ if (remote_machine == NULL || !strcmp(remote_machine, "*")) {
+ /* Use the PDC *only* for this */
+
+ if ( !get_pdc_ip(domain, &pdc_ip) ) {
+ DEBUG(0,("Can't get IP for PDC for domain %s\n", domain));
+ goto failed;
+ }
+
+ if ( !name_status_find( domain, 0x1b, 0x20, pdc_ip, dc_name) )
+ goto failed;
+ } else {
+ /* supoport old deprecated "smbpasswd -j DOMAIN -r MACHINE" behavior */
+ fstrcpy( dc_name, remote_machine );
+ }
+
+ /* if this next call fails, then give up. We can't do
+ password changes on BDC's --jerry */
+
+ if (!NT_STATUS_IS_OK(cli_full_connection(&cli, global_myname(), dc_name,
+ NULL, 0,
+ "IPC$", "IPC",
+ "", "",
+ "", 0, Undefined, NULL))) {
+ DEBUG(0,("modify_trust_password: Connection to %s failed!\n", dc_name));
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto failed;
+ }
+
+ /*
+ * Ok - we have an anonymous connection to the IPC$ share.
+ * Now start the NT Domain stuff :-).
+ */
+
+ if(cli_nt_session_open(cli, PI_NETLOGON) == False) {
+ DEBUG(0,("modify_trust_password: unable to open the domain client session to machine %s. Error was : %s.\n",
+ dc_name, cli_errstr(cli)));
+ cli_nt_session_close(cli);
+ cli_ulogoff(cli);
+ cli_shutdown(cli);
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto failed;
+ }
+
+ nt_status = trust_pw_find_change_and_store_it(cli, cli->mem_ctx, domain);
+
+ cli_nt_session_close(cli);
+ cli_ulogoff(cli);
+ cli_shutdown(cli);
+
+failed:
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0,("%s : change_trust_account_password: Failed to change password for domain %s.\n",
+ timestring(False), domain));
+ }
+ else
+ DEBUG(5,("change_trust_account_password: sucess!\n"));
+
+ return nt_status;
+}
diff --git a/source/smbd/chgpasswd.c b/source/smbd/chgpasswd.c
new file mode 100644
index 00000000000..4192cc3a239
--- /dev/null
+++ b/source/smbd/chgpasswd.c
@@ -0,0 +1,1054 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Andrew Bartlett 2001-2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* These comments regard the code to change the user's unix password: */
+
+/* fork a child process to exec passwd and write to its
+ * tty to change a users password. This is running as the
+ * user who is attempting to change the password.
+ */
+
+/*
+ * This code was copied/borrowed and stolen from various sources.
+ * The primary source was the poppasswd.c from the authors of POPMail. This software
+ * was included as a client to change passwords using the 'passwd' program
+ * on the remote machine.
+ *
+ * This code has been hacked by Bob Nance (nance@niehs.nih.gov) and Evan Patterson
+ * (patters2@niehs.nih.gov) at the National Institute of Environmental Health Sciences
+ * and rights to modify, distribute or incorporate this change to the CAP suite or
+ * using it for any other reason are granted, so long as this disclaimer is left intact.
+ */
+
+/*
+ This code was hacked considerably for inclusion in Samba, primarily
+ by Andrew.Tridgell@anu.edu.au. The biggest change was the addition
+ of the "password chat" option, which allows the easy runtime
+ specification of the expected sequence of events to change a
+ password.
+ */
+
+#include "includes.h"
+
+#ifdef HAVE_WORKING_CRACKLIB
+#include <crack.h>
+
+#ifndef HAVE_CRACKLIB_DICTPATH
+#ifndef CRACKLIB_DICTPATH
+#define CRACKLIB_DICTPATH SAMBA_CRACKLIB_DICTPATH
+#endif
+#endif
+#endif
+
+extern struct passdb_ops pdb_ops;
+
+static NTSTATUS check_oem_password(const char *user,
+ uchar password_encrypted_with_lm_hash[516],
+ const uchar old_lm_hash_encrypted[16],
+ uchar password_encrypted_with_nt_hash[516],
+ const uchar old_nt_hash_encrypted[16],
+ SAM_ACCOUNT **hnd, char *new_passwd,
+ int new_passwd_size);
+
+#if ALLOW_CHANGE_PASSWORD
+
+static int findpty(char **slave)
+{
+ int master;
+ static fstring line;
+ DIR *dirp;
+ const char *dpname;
+
+#if defined(HAVE_GRANTPT)
+ /* Try to open /dev/ptmx. If that fails, fall through to old method. */
+ if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0)
+ {
+ grantpt(master);
+ unlockpt(master);
+ *slave = (char *)ptsname(master);
+ if (*slave == NULL)
+ {
+ DEBUG(0,
+ ("findpty: Unable to create master/slave pty pair.\n"));
+ /* Stop fd leak on error. */
+ close(master);
+ return -1;
+ }
+ else
+ {
+ DEBUG(10,
+ ("findpty: Allocated slave pty %s\n", *slave));
+ return (master);
+ }
+ }
+#endif /* HAVE_GRANTPT */
+
+ fstrcpy(line, "/dev/ptyXX");
+
+ dirp = opendir("/dev");
+ if (!dirp)
+ return (-1);
+ while ((dpname = readdirname(dirp)) != NULL)
+ {
+ if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5)
+ {
+ DEBUG(3,
+ ("pty: try to open %s, line was %s\n", dpname,
+ line));
+ line[8] = dpname[3];
+ line[9] = dpname[4];
+ if ((master = sys_open(line, O_RDWR, 0)) >= 0)
+ {
+ DEBUG(3, ("pty: opened %s\n", line));
+ line[5] = 't';
+ *slave = line;
+ closedir(dirp);
+ return (master);
+ }
+ }
+ }
+ closedir(dirp);
+ return (-1);
+}
+
+static int dochild(int master, const char *slavedev, const struct passwd *pass,
+ const char *passwordprogram, BOOL as_root)
+{
+ int slave;
+ struct termios stermios;
+ gid_t gid;
+ uid_t uid;
+
+ if (pass == NULL)
+ {
+ DEBUG(0,
+ ("dochild: user doesn't exist in the UNIX password database.\n"));
+ return False;
+ }
+
+ gid = pass->pw_gid;
+ uid = pass->pw_uid;
+
+ gain_root_privilege();
+
+ /* Start new session - gets rid of controlling terminal. */
+ if (setsid() < 0)
+ {
+ DEBUG(3,
+ ("Weirdness, couldn't let go of controlling terminal\n"));
+ return (False);
+ }
+
+ /* Open slave pty and acquire as new controlling terminal. */
+ if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0)
+ {
+ DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
+ return (False);
+ }
+#ifdef I_PUSH
+ ioctl(slave, I_PUSH, "ptem");
+ ioctl(slave, I_PUSH, "ldterm");
+#elif defined(TIOCSCTTY)
+ if (ioctl(slave, TIOCSCTTY, 0) < 0)
+ {
+ DEBUG(3, ("Error in ioctl call for slave pty\n"));
+ /* return(False); */
+ }
+#endif
+
+ /* Close master. */
+ close(master);
+
+ /* Make slave stdin/out/err of child. */
+
+ if (sys_dup2(slave, STDIN_FILENO) != STDIN_FILENO)
+ {
+ DEBUG(3, ("Could not re-direct stdin\n"));
+ return (False);
+ }
+ if (sys_dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
+ {
+ DEBUG(3, ("Could not re-direct stdout\n"));
+ return (False);
+ }
+ if (sys_dup2(slave, STDERR_FILENO) != STDERR_FILENO)
+ {
+ DEBUG(3, ("Could not re-direct stderr\n"));
+ return (False);
+ }
+ if (slave > 2)
+ close(slave);
+
+ /* Set proper terminal attributes - no echo, canonical input processing,
+ no map NL to CR/NL on output. */
+
+ if (tcgetattr(0, &stermios) < 0)
+ {
+ DEBUG(3,
+ ("could not read default terminal attributes on pty\n"));
+ return (False);
+ }
+ stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
+ stermios.c_lflag |= ICANON;
+#ifdef ONLCR
+ stermios.c_oflag &= ~(ONLCR);
+#endif
+ if (tcsetattr(0, TCSANOW, &stermios) < 0)
+ {
+ DEBUG(3, ("could not set attributes of pty\n"));
+ return (False);
+ }
+
+ /* make us completely into the right uid */
+ if (!as_root)
+ {
+ become_user_permanently(uid, gid);
+ }
+
+ DEBUG(10,
+ ("Invoking '%s' as password change program.\n",
+ passwordprogram));
+
+ /* execl() password-change application */
+ if (execl("/bin/sh", "sh", "-c", passwordprogram, NULL) < 0)
+ {
+ DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
+ return (False);
+ }
+ return (True);
+}
+
+static int expect(int master, char *issue, char *expected)
+{
+ pstring buffer;
+ int attempts, timeout, nread, len;
+ BOOL match = False;
+
+ for (attempts = 0; attempts < 2; attempts++) {
+ if (!strequal(issue, ".")) {
+ if (lp_passwd_chat_debug())
+ DEBUG(100, ("expect: sending [%s]\n", issue));
+
+ if ((len = write(master, issue, strlen(issue))) != strlen(issue)) {
+ DEBUG(2,("expect: (short) write returned %d\n", len ));
+ return False;
+ }
+ }
+
+ if (strequal(expected, "."))
+ return True;
+
+ /* Initial timeout. */
+ timeout = lp_passwd_chat_timeout() * 1000;
+ nread = 0;
+ buffer[nread] = 0;
+
+ while ((len = read_socket_with_timeout(master, buffer + nread, 1,
+ sizeof(buffer) - nread - 1,
+ timeout)) > 0) {
+ nread += len;
+ buffer[nread] = 0;
+
+ {
+ /* Eat leading/trailing whitespace before match. */
+ pstring str;
+ pstrcpy( str, buffer);
+ trim_char( str, ' ', ' ');
+
+ if ((match = (unix_wild_match(expected, str) == 0))) {
+ /* Now data has started to return, lower timeout. */
+ timeout = lp_passwd_chat_timeout() * 100;
+ }
+ }
+ }
+
+ if (lp_passwd_chat_debug())
+ DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
+ expected, buffer, match ? "yes" : "no" ));
+
+ if (match)
+ break;
+
+ if (len < 0) {
+ DEBUG(2, ("expect: %s\n", strerror(errno)));
+ return False;
+ }
+ }
+
+ DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
+ return match;
+}
+
+static void pwd_sub(char *buf)
+{
+ all_string_sub(buf, "\\n", "\n", 0);
+ all_string_sub(buf, "\\r", "\r", 0);
+ all_string_sub(buf, "\\s", " ", 0);
+ all_string_sub(buf, "\\t", "\t", 0);
+}
+
+static int talktochild(int master, const char *seq)
+{
+ int count = 0;
+ fstring issue, expected;
+
+ fstrcpy(issue, ".");
+
+ while (next_token(&seq, expected, NULL, sizeof(expected)))
+ {
+ pwd_sub(expected);
+ count++;
+
+ if (!expect(master, issue, expected))
+ {
+ DEBUG(3, ("Response %d incorrect\n", count));
+ return False;
+ }
+
+ if (!next_token(&seq, issue, NULL, sizeof(issue)))
+ fstrcpy(issue, ".");
+
+ pwd_sub(issue);
+ }
+ if (!strequal(issue, ".")) {
+ /* we have one final issue to send */
+ fstrcpy(expected, ".");
+ if (!expect(master, issue, expected))
+ return False;
+ }
+
+ return (count > 0);
+}
+
+static BOOL chat_with_program(char *passwordprogram, struct passwd *pass,
+ char *chatsequence, BOOL as_root)
+{
+ char *slavedev;
+ int master;
+ pid_t pid, wpid;
+ int wstat;
+ BOOL chstat = False;
+
+ if (pass == NULL) {
+ DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
+ return False;
+ }
+
+ /* allocate a pseudo-terminal device */
+ if ((master = findpty(&slavedev)) < 0) {
+ DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass->pw_name));
+ return (False);
+ }
+
+ /*
+ * We need to temporarily stop CatchChild from eating
+ * SIGCLD signals as it also eats the exit status code. JRA.
+ */
+
+ CatchChildLeaveStatus();
+
+ if ((pid = sys_fork()) < 0) {
+ DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass->pw_name));
+ close(master);
+ CatchChild();
+ return (False);
+ }
+
+ /* we now have a pty */
+ if (pid > 0) { /* This is the parent process */
+ if ((chstat = talktochild(master, chatsequence)) == False) {
+ DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass->pw_name));
+ kill(pid, SIGKILL); /* be sure to end this process */
+ }
+
+ while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) {
+ if (errno == EINTR) {
+ errno = 0;
+ continue;
+ }
+ break;
+ }
+
+ if (wpid < 0) {
+ DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
+ close(master);
+ CatchChild();
+ return (False);
+ }
+
+ /*
+ * Go back to ignoring children.
+ */
+ CatchChild();
+
+ close(master);
+
+ if (pid != wpid) {
+ DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
+ return (False);
+ }
+ if (WIFEXITED(wstat) && (WEXITSTATUS(wstat) != 0)) {
+ DEBUG(3, ("chat_with_program: The process exited with status %d \
+while we were waiting\n", WEXITSTATUS(wstat)));
+ return (False);
+ }
+#if defined(WIFSIGNALLED) && defined(WTERMSIG)
+ else if (WIFSIGNALLED(wstat)) {
+ DEBUG(3, ("chat_with_program: The process was killed by signal %d \
+while we were waiting\n", WTERMSIG(wstat)));
+ return (False);
+ }
+#endif
+ } else {
+ /* CHILD */
+
+ /*
+ * Lose any oplock capabilities.
+ */
+ oplock_set_capability(False, False);
+
+ /* make sure it doesn't freeze */
+ alarm(20);
+
+ if (as_root)
+ become_root();
+
+ DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass->pw_name,
+ (int)getuid(), (int)getgid(), BOOLSTR(as_root) ));
+ chstat = dochild(master, slavedev, pass, passwordprogram, as_root);
+
+ if (as_root)
+ unbecome_root();
+
+ /*
+ * The child should never return from dochild() ....
+ */
+
+ DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat));
+ exit(1);
+ }
+
+ if (chstat)
+ DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
+ (chstat ? "" : "un"), pass->pw_name));
+ return (chstat);
+}
+
+BOOL chgpasswd(const char *name, const struct passwd *pass,
+ const char *oldpass, const char *newpass, BOOL as_root)
+{
+ pstring passwordprogram;
+ pstring chatsequence;
+ size_t i;
+ size_t len;
+
+ if (!oldpass) {
+ oldpass = "";
+ }
+
+ DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root), name));
+
+#if DEBUG_PASSWORD
+ DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass, newpass));
+#endif
+
+ /* Take the passed information and test it for minimum criteria */
+
+ /* Password is same as old password */
+ if (strcmp(oldpass, newpass) == 0) {
+ /* don't allow same password */
+ DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name)); /* log the attempt */
+ return (False); /* inform the user */
+ }
+
+ /*
+ * Check the old and new passwords don't contain any control
+ * characters.
+ */
+
+ len = strlen(oldpass);
+ for (i = 0; i < len; i++) {
+ if (iscntrl((int)oldpass[i])) {
+ DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
+ return False;
+ }
+ }
+
+ len = strlen(newpass);
+ for (i = 0; i < len; i++) {
+ if (iscntrl((int)newpass[i])) {
+ DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
+ return False;
+ }
+ }
+
+#ifdef WITH_PAM
+ if (lp_pam_password_change()) {
+ BOOL ret;
+
+ if (as_root)
+ become_root();
+
+ if (pass) {
+ ret = smb_pam_passchange(pass->pw_name, oldpass, newpass);
+ } else {
+ ret = smb_pam_passchange(name, oldpass, newpass);
+ }
+
+ if (as_root)
+ unbecome_root();
+
+ return ret;
+ }
+#endif
+
+ /* A non-PAM password change just doen't make sense without a valid local user */
+
+ if (pass == NULL) {
+ DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name));
+ return False;
+ }
+
+ pstrcpy(passwordprogram, lp_passwd_program());
+ pstrcpy(chatsequence, lp_passwd_chat());
+
+ if (!*chatsequence) {
+ DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
+ return (False);
+ }
+
+ if (!*passwordprogram) {
+ DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
+ return (False);
+ }
+
+ if (as_root) {
+ /* The password program *must* contain the user name to work. Fail if not. */
+ if (strstr_m(passwordprogram, "%u") == NULL) {
+ DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
+the string %%u, and the given string %s does not.\n", passwordprogram ));
+ return False;
+ }
+ }
+
+ pstring_sub(passwordprogram, "%u", name);
+ /* note that we do NOT substitute the %o and %n in the password program
+ as this would open up a security hole where the user could use
+ a new password containing shell escape characters */
+
+ pstring_sub(chatsequence, "%u", name);
+ all_string_sub(chatsequence, "%o", oldpass, sizeof(pstring));
+ all_string_sub(chatsequence, "%n", newpass, sizeof(pstring));
+ return (chat_with_program
+ (passwordprogram, pass, chatsequence, as_root));
+}
+
+#else /* ALLOW_CHANGE_PASSWORD */
+
+BOOL chgpasswd(const char *name, const struct passwd *pass,
+ const char *oldpass, const char *newpass, BOOL as_root)
+{
+ DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name));
+ return (False);
+}
+#endif /* ALLOW_CHANGE_PASSWORD */
+
+/***********************************************************
+ Code to check the lanman hashed password.
+************************************************************/
+
+BOOL check_lanman_password(char *user, uchar * pass1,
+ uchar * pass2, SAM_ACCOUNT **hnd)
+{
+ uchar unenc_new_pw[16];
+ uchar unenc_old_pw[16];
+ SAM_ACCOUNT *sampass = NULL;
+ uint16 acct_ctrl;
+ const uint8 *lanman_pw;
+ BOOL ret;
+
+ become_root();
+ ret = pdb_getsampwnam(sampass, user);
+ unbecome_root();
+
+ if (ret == False) {
+ DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
+ pdb_free_sam(&sampass);
+ return False;
+ }
+
+ acct_ctrl = pdb_get_acct_ctrl (sampass);
+ lanman_pw = pdb_get_lanman_passwd (sampass);
+
+ if (acct_ctrl & ACB_DISABLED) {
+ DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
+ pdb_free_sam(&sampass);
+ return False;
+ }
+
+ if (lanman_pw == NULL) {
+ if (acct_ctrl & ACB_PWNOTREQ) {
+ /* this saves the pointer for the caller */
+ *hnd = sampass;
+ return True;
+ } else {
+ DEBUG(0, ("check_lanman_password: no lanman password !\n"));
+ pdb_free_sam(&sampass);
+ return False;
+ }
+ }
+
+ /* Get the new lanman hash. */
+ D_P16(lanman_pw, pass2, unenc_new_pw);
+
+ /* Use this to get the old lanman hash. */
+ D_P16(unenc_new_pw, pass1, unenc_old_pw);
+
+ /* Check that the two old passwords match. */
+ if (memcmp(lanman_pw, unenc_old_pw, 16)) {
+ DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
+ pdb_free_sam(&sampass);
+ return False;
+ }
+
+ /* this saves the pointer for the caller */
+ *hnd = sampass;
+ return True;
+}
+
+/***********************************************************
+ Code to change the lanman hashed password.
+ It nulls out the NT hashed password as it will
+ no longer be valid.
+ NOTE this function is designed to be called as root. Check the old password
+ is correct before calling. JRA.
+************************************************************/
+
+BOOL change_lanman_password(SAM_ACCOUNT *sampass, uchar *pass2)
+{
+ static uchar null_pw[16];
+ uchar unenc_new_pw[16];
+ BOOL ret;
+ uint16 acct_ctrl;
+ const uint8 *pwd;
+
+ if (sampass == NULL) {
+ DEBUG(0,("change_lanman_password: no smb password entry.\n"));
+ return False;
+ }
+
+ acct_ctrl = pdb_get_acct_ctrl(sampass);
+ pwd = pdb_get_lanman_passwd(sampass);
+
+ if (acct_ctrl & ACB_DISABLED) {
+ DEBUG(0,("change_lanman_password: account %s disabled.\n",
+ pdb_get_username(sampass)));
+ return False;
+ }
+
+ if (pwd == NULL) {
+ if (acct_ctrl & ACB_PWNOTREQ) {
+ uchar no_pw[14];
+ memset(no_pw, '\0', 14);
+ E_P16(no_pw, null_pw);
+
+ /* Get the new lanman hash. */
+ D_P16(null_pw, pass2, unenc_new_pw);
+ } else {
+ DEBUG(0,("change_lanman_password: no lanman password !\n"));
+ return False;
+ }
+ } else {
+ /* Get the new lanman hash. */
+ D_P16(pwd, pass2, unenc_new_pw);
+ }
+
+ if (!pdb_set_lanman_passwd(sampass, unenc_new_pw, PDB_CHANGED)) {
+ return False;
+ }
+
+ if (!pdb_set_nt_passwd (sampass, NULL, PDB_CHANGED)) {
+ return False; /* We lose the NT hash. Sorry. */
+ }
+
+ if (!pdb_set_pass_changed_now (sampass)) {
+ pdb_free_sam(&sampass);
+ /* Not quite sure what this one qualifies as, but this will do */
+ return False;
+ }
+
+ /* Now flush the sam_passwd struct to persistent storage */
+ ret = pdb_update_sam_account (sampass);
+
+ return ret;
+}
+
+/***********************************************************
+ Code to check and change the OEM hashed password.
+************************************************************/
+
+NTSTATUS pass_oem_change(char *user,
+ uchar password_encrypted_with_lm_hash[516],
+ const uchar old_lm_hash_encrypted[16],
+ uchar password_encrypted_with_nt_hash[516],
+ const uchar old_nt_hash_encrypted[16])
+{
+ pstring new_passwd;
+ SAM_ACCOUNT *sampass = NULL;
+ NTSTATUS nt_status = check_oem_password(user, password_encrypted_with_lm_hash,
+ old_lm_hash_encrypted,
+ password_encrypted_with_nt_hash,
+ old_nt_hash_encrypted,
+ &sampass, new_passwd, sizeof(new_passwd));
+
+ if (!NT_STATUS_IS_OK(nt_status))
+ return nt_status;
+
+ /* We've already checked the old password here.... */
+ become_root();
+ nt_status = change_oem_password(sampass, NULL, new_passwd, True);
+ unbecome_root();
+
+ memset(new_passwd, 0, sizeof(new_passwd));
+
+ pdb_free_sam(&sampass);
+
+ return nt_status;
+}
+
+/***********************************************************
+ Decrypt and verify a user password change.
+
+ The 516 byte long buffers are encrypted with the old NT and
+ old LM passwords, and if the NT passwords are present, both
+ buffers contain a unicode string.
+
+ After decrypting the buffers, check the password is correct by
+ matching the old hashed passwords with the passwords in the passdb.
+
+************************************************************/
+
+static NTSTATUS check_oem_password(const char *user,
+ uchar password_encrypted_with_lm_hash[516],
+ const uchar old_lm_hash_encrypted[16],
+ uchar password_encrypted_with_nt_hash[516],
+ const uchar old_nt_hash_encrypted[16],
+ SAM_ACCOUNT **hnd, char *new_passwd,
+ int new_passwd_size)
+{
+ static uchar null_pw[16];
+ static uchar null_ntpw[16];
+ SAM_ACCOUNT *sampass = NULL;
+ char *password_encrypted;
+ const char *encryption_key;
+ const uint8 *lanman_pw, *nt_pw;
+ uint16 acct_ctrl;
+ uint32 new_pw_len;
+ uchar new_nt_hash[16];
+ uchar old_nt_hash_plain[16];
+ uchar new_lm_hash[16];
+ uchar old_lm_hash_plain[16];
+ char no_pw[2];
+ BOOL ret;
+
+ BOOL nt_pass_set = (password_encrypted_with_nt_hash && old_nt_hash_encrypted);
+ BOOL lm_pass_set = (password_encrypted_with_lm_hash && old_lm_hash_encrypted);
+
+ *hnd = NULL;
+
+ pdb_init_sam(&sampass);
+
+ become_root();
+ ret = pdb_getsampwnam(sampass, user);
+ unbecome_root();
+
+ if (ret == False) {
+ DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
+ pdb_free_sam(&sampass);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ acct_ctrl = pdb_get_acct_ctrl(sampass);
+
+ if (acct_ctrl & ACB_DISABLED) {
+ DEBUG(2,("check_lanman_password: account %s disabled.\n", user));
+ pdb_free_sam(&sampass);
+ return NT_STATUS_ACCOUNT_DISABLED;
+ }
+
+ if (acct_ctrl & ACB_PWNOTREQ && lp_null_passwords()) {
+ /* construct a null password (in case one is needed */
+ no_pw[0] = 0;
+ no_pw[1] = 0;
+ nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
+ lanman_pw = null_pw;
+ nt_pw = null_pw;
+
+ } else {
+ /* save pointers to passwords so we don't have to keep looking them up */
+ if (lp_lanman_auth()) {
+ lanman_pw = pdb_get_lanman_passwd(sampass);
+ } else {
+ lanman_pw = NULL;
+ }
+ nt_pw = pdb_get_nt_passwd(sampass);
+ }
+
+ if (nt_pw && nt_pass_set) {
+ /* IDEAL Case: passwords are in unicode, and we can
+ * read use the password encrypted with the NT hash
+ */
+ password_encrypted = password_encrypted_with_nt_hash;
+ encryption_key = nt_pw;
+ } else if (lanman_pw && lm_pass_set) {
+ /* password may still be in unicode, but use LM hash version */
+ password_encrypted = password_encrypted_with_lm_hash;
+ encryption_key = lanman_pw;
+ } else if (nt_pass_set) {
+ DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n",
+ user));
+ pdb_free_sam(&sampass);
+ return NT_STATUS_WRONG_PASSWORD;
+ } else if (lm_pass_set) {
+ DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n",
+ user));
+ pdb_free_sam(&sampass);
+ return NT_STATUS_WRONG_PASSWORD;
+ } else {
+ DEBUG(1, ("password change requested for user %s, but no password supplied!\n",
+ user));
+ pdb_free_sam(&sampass);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /*
+ * Decrypt the password with the key
+ */
+ SamOEMhash( password_encrypted, encryption_key, 516);
+
+ if ( !decode_pw_buffer(password_encrypted, new_passwd, new_passwd_size, &new_pw_len,
+ nt_pass_set ? STR_UNICODE : STR_ASCII)) {
+ pdb_free_sam(&sampass);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /*
+ * To ensure we got the correct new password, hash it and
+ * use it as a key to test the passed old password.
+ */
+
+ if (nt_pass_set) {
+ /* NT passwords, verify the NT hash. */
+
+ /* Calculate the MD4 hash (NT compatible) of the password */
+ memset(new_nt_hash, '\0', 16);
+ E_md4hash(new_passwd, new_nt_hash);
+
+ if (nt_pw) {
+ /*
+ * Now use new_nt_hash as the key to see if the old
+ * password matches.
+ */
+ D_P16(new_nt_hash, old_nt_hash_encrypted, old_nt_hash_plain);
+
+ if (memcmp(nt_pw, old_nt_hash_plain, 16)) {
+ DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
+ pdb_free_sam(&sampass);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* We could check the LM password here, but there is
+ * little point, we already know the password is
+ * correct, and the LM password might not even be
+ * present. */
+
+ /* Further, LM hash generation algorithms
+ * differ with charset, so we could
+ * incorrectly fail a perfectly valid password
+ * change */
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,
+ ("check_oem_password: password %s ok\n", new_passwd));
+#endif
+ *hnd = sampass;
+ return NT_STATUS_OK;
+ }
+
+ if (lanman_pw) {
+ /*
+ * Now use new_nt_hash as the key to see if the old
+ * LM password matches.
+ */
+ D_P16(new_nt_hash, old_lm_hash_encrypted, old_lm_hash_plain);
+
+ if (memcmp(lanman_pw, old_lm_hash_plain, 16)) {
+ DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
+ pdb_free_sam(&sampass);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,
+ ("check_oem_password: password %s ok\n", new_passwd));
+#endif
+ *hnd = sampass;
+ return NT_STATUS_OK;
+ }
+ }
+
+ if (lanman_pw && lm_pass_set) {
+
+ E_deshash(new_passwd, new_lm_hash);
+
+ /*
+ * Now use new_lm_hash as the key to see if the old
+ * password matches.
+ */
+ D_P16(new_lm_hash, old_lm_hash_encrypted, old_lm_hash_plain);
+
+ if (memcmp(lanman_pw, old_lm_hash_plain, 16)) {
+ DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
+ pdb_free_sam(&sampass);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,
+ ("check_oem_password: password %s ok\n", new_passwd));
+#endif
+ *hnd = sampass;
+ return NT_STATUS_OK;
+ }
+
+ /* should not be reached */
+ pdb_free_sam(&sampass);
+ return NT_STATUS_WRONG_PASSWORD;
+}
+
+/***********************************************************
+ Code to change the oem password. Changes both the lanman
+ and NT hashes. Old_passwd is almost always NULL.
+ NOTE this function is designed to be called as root. Check the old password
+ is correct before calling. JRA.
+************************************************************/
+
+NTSTATUS change_oem_password(SAM_ACCOUNT *hnd, char *old_passwd, char *new_passwd, BOOL as_root)
+{
+ struct passwd *pass;
+
+ BOOL ret;
+ uint32 min_len;
+
+ if (time(NULL) < pdb_get_pass_can_change_time(hnd)) {
+ DEBUG(1, ("user %s cannot change password now, must wait until %s\n",
+ pdb_get_username(hnd), http_timestring(pdb_get_pass_can_change_time(hnd))));
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+
+ if (account_policy_get(AP_MIN_PASSWORD_LEN, &min_len) && (strlen(new_passwd) < min_len)) {
+ DEBUG(1, ("user %s cannot change password - password too short\n",
+ pdb_get_username(hnd)));
+ DEBUGADD(1, (" account policy min password len = %d\n", min_len));
+ return NT_STATUS_PASSWORD_RESTRICTION;
+/* return NT_STATUS_PWD_TOO_SHORT; */
+ }
+
+ /* Take the passed information and test it for minimum criteria */
+ /* Minimum password length */
+ if (strlen(new_passwd) < lp_min_passwd_length()) {
+ /* too short, must be at least MINPASSWDLENGTH */
+ DEBUG(1, ("Password Change: user %s, New password is shorter than minimum password length = %d\n",
+ pdb_get_username(hnd), lp_min_passwd_length()));
+ return NT_STATUS_PASSWORD_RESTRICTION;
+/* return NT_STATUS_PWD_TOO_SHORT; */
+ }
+
+ pass = Get_Pwnam(pdb_get_username(hnd));
+ if (!pass) {
+ DEBUG(1, ("check_oem_password: Username does not exist in system !?!\n"));
+ }
+
+#ifdef HAVE_WORKING_CRACKLIB
+ if (pass) {
+ /* if we can, become the user to overcome internal cracklib sillyness */
+ if (!push_sec_ctx())
+ return NT_STATUS_UNSUCCESSFUL;
+
+ set_sec_ctx(pass->pw_uid, pass->pw_gid, 0, NULL, NULL, NULL);
+ set_re_uid();
+ }
+
+ if (lp_use_cracklib()) {
+ const char *crack_check_reason;
+ DEBUG(4, ("change_oem_password: Checking password for user [%s]"
+ " against cracklib. \n", pdb_get_username(hnd)));
+ DEBUGADD(4, ("If this is your last message, then something is "
+ "wrong with cracklib, it might be missing it's "
+ "dictionaries at %s\n",
+ CRACKLIB_DICTPATH));
+ dbgflush();
+
+ crack_check_reason = FascistCheck(new_passwd, (char *)CRACKLIB_DICTPATH);
+ if (crack_check_reason) {
+ DEBUG(1, ("Password Change: user [%s], "
+ "New password failed cracklib test - %s\n",
+ pdb_get_username(hnd), crack_check_reason));
+
+ /* get back to where we should be */
+ if (pass)
+ pop_sec_ctx();
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+ }
+
+ if (pass)
+ pop_sec_ctx();
+#endif
+
+ /*
+ * If unix password sync was requested, attempt to change
+ * the /etc/passwd database first. Return failure if this cannot
+ * be done.
+ *
+ * This occurs before the oem change, because we don't want to
+ * update it if chgpasswd failed.
+ *
+ * Conditional on lp_unix_password_sync() because we don't want
+ * to touch the unix db unless we have admin permission.
+ */
+
+ if(lp_unix_password_sync() &&
+ !chgpasswd(pdb_get_username(hnd), pass, old_passwd, new_passwd, as_root)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* Now write it into the file. */
+ ret = pdb_update_sam_account (hnd);
+
+ if (!ret) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source/smbd/close.c b/source/smbd/close.c
new file mode 100644
index 00000000000..0700aeaa0a6
--- /dev/null
+++ b/source/smbd/close.c
@@ -0,0 +1,311 @@
+/*
+ Unix SMB/CIFS implementation.
+ file closing
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+ Run a file if it is a magic script.
+****************************************************************************/
+
+static void check_magic(files_struct *fsp,connection_struct *conn)
+{
+ if (!*lp_magicscript(SNUM(conn)))
+ return;
+
+ DEBUG(5,("checking magic for %s\n",fsp->fsp_name));
+
+ {
+ char *p;
+ if (!(p = strrchr_m(fsp->fsp_name,'/')))
+ p = fsp->fsp_name;
+ else
+ p++;
+
+ if (!strequal(lp_magicscript(SNUM(conn)),p))
+ return;
+ }
+
+ {
+ int ret;
+ pstring magic_output;
+ pstring fname;
+ SMB_STRUCT_STAT st;
+ int tmp_fd, outfd;
+
+ pstrcpy(fname,fsp->fsp_name);
+ if (*lp_magicoutput(SNUM(conn)))
+ pstrcpy(magic_output,lp_magicoutput(SNUM(conn)));
+ else
+ slprintf(magic_output,sizeof(fname)-1, "%s.out",fname);
+
+ chmod(fname,0755);
+ ret = smbrun(fname,&tmp_fd);
+ DEBUG(3,("Invoking magic command %s gave %d\n",fname,ret));
+ unlink(fname);
+ if (ret != 0 || tmp_fd == -1) {
+ if (tmp_fd != -1)
+ close(tmp_fd);
+ return;
+ }
+ outfd = open(magic_output, O_CREAT|O_EXCL|O_RDWR, 0600);
+ if (outfd == -1) {
+ close(tmp_fd);
+ return;
+ }
+
+ if (sys_fstat(tmp_fd,&st) == -1) {
+ close(tmp_fd);
+ close(outfd);
+ return;
+ }
+
+ transfer_file(tmp_fd,outfd,(SMB_OFF_T)st.st_size);
+ close(tmp_fd);
+ close(outfd);
+ }
+}
+
+/****************************************************************************
+ Common code to close a file or a directory.
+****************************************************************************/
+
+static int close_filestruct(files_struct *fsp)
+{
+ connection_struct *conn = fsp->conn;
+ int ret = 0;
+
+ if (fsp->fd != -1) {
+ if(flush_write_cache(fsp, CLOSE_FLUSH) == -1)
+ ret = -1;
+
+ delete_write_cache(fsp);
+ }
+
+ conn->num_files_open--;
+ SAFE_FREE(fsp->wbmpx_ptr);
+
+ return ret;
+}
+
+/****************************************************************************
+ Close a file.
+
+ If normal_close is 1 then this came from a normal SMBclose (or equivalent)
+ operation otherwise it came as the result of some other operation such as
+ the closing of the connection. In the latter case printing and
+ magic scripts are not run.
+****************************************************************************/
+
+static int close_normal_file(files_struct *fsp, BOOL normal_close)
+{
+ share_mode_entry *share_entry = NULL;
+ size_t share_entry_count = 0;
+ BOOL delete_on_close = False;
+ connection_struct *conn = fsp->conn;
+ int err = 0;
+ int err1 = 0;
+
+ remove_pending_lock_requests_by_fid(fsp);
+
+ /*
+ * If we're flushing on a close we can get a write
+ * error here, we must remember this.
+ */
+
+ if (close_filestruct(fsp) == -1)
+ err1 = -1;
+
+ if (fsp->print_file) {
+ print_fsp_end(fsp, normal_close);
+ file_free(fsp);
+ return 0;
+ }
+
+ /*
+ * Lock the share entries, and determine if we should delete
+ * on close. If so delete whilst the lock is still in effect.
+ * This prevents race conditions with the file being created. JRA.
+ */
+
+ lock_share_entry_fsp(fsp);
+
+ if (fsp->delete_on_close) {
+
+ /*
+ * Modify the share mode entry for all files open
+ * on this device and inode to tell other smbds we have
+ * changed the delete on close flag. The last closer will delete the file
+ * if flag is set.
+ */
+
+ NTSTATUS status =set_delete_on_close_over_all(fsp, fsp->delete_on_close);
+ if (NT_STATUS_V(status) != NT_STATUS_V(NT_STATUS_OK))
+ DEBUG(0,("close_normal_file: failed to change delete on close flag for file %s\n",
+ fsp->fsp_name ));
+ }
+
+ share_entry_count = del_share_mode(fsp, &share_entry);
+
+ DEBUG(10,("close_normal_file: share_entry_count = %lu for file %s\n",
+ (unsigned long)share_entry_count, fsp->fsp_name ));
+
+ /*
+ * We delete on close if it's the last open, and the
+ * delete on close flag was set in the entry we just deleted.
+ */
+
+ if ((share_entry_count == 0) && share_entry &&
+ GET_DELETE_ON_CLOSE_FLAG(share_entry->share_mode) )
+ delete_on_close = True;
+
+ SAFE_FREE(share_entry);
+
+ /*
+ * NT can set delete_on_close of the last open
+ * reference to a file.
+ */
+
+ if (normal_close && delete_on_close) {
+ DEBUG(5,("close_file: file %s. Delete on close was set - deleting file.\n",
+ fsp->fsp_name));
+ if(SMB_VFS_UNLINK(conn,fsp->fsp_name) != 0) {
+ /*
+ * This call can potentially fail as another smbd may have
+ * had the file open with delete on close set and deleted
+ * it when its last reference to this file went away. Hence
+ * we log this but not at debug level zero.
+ */
+
+ DEBUG(5,("close_file: file %s. Delete on close was set and unlink failed \
+with error %s\n", fsp->fsp_name, strerror(errno) ));
+ }
+ process_pending_change_notify_queue((time_t)0);
+ }
+
+ unlock_share_entry_fsp(fsp);
+
+ if(fsp->oplock_type)
+ release_file_oplock(fsp);
+
+ locking_close_file(fsp);
+
+ err = fd_close(conn, fsp);
+
+ /* check for magic scripts */
+ if (normal_close) {
+ check_magic(fsp,conn);
+ }
+
+ /*
+ * Ensure pending modtime is set after close.
+ */
+
+ if(fsp->pending_modtime) {
+ int saved_errno = errno;
+ set_filetime(conn, fsp->fsp_name, fsp->pending_modtime);
+ errno = saved_errno;
+ }
+
+ DEBUG(2,("%s closed file %s (numopen=%d) %s\n",
+ conn->user,fsp->fsp_name,
+ conn->num_files_open, err ? strerror(err) : ""));
+
+ if (fsp->fsp_name)
+ string_free(&fsp->fsp_name);
+
+ file_free(fsp);
+
+ if (err == -1 || err1 == -1)
+ return -1;
+ else
+ return 0;
+}
+
+/****************************************************************************
+ Close a directory opened by an NT SMB call.
+****************************************************************************/
+
+static int close_directory(files_struct *fsp, BOOL normal_close)
+{
+ remove_pending_change_notify_requests_by_fid(fsp);
+
+ /*
+ * NT can set delete_on_close of the last open
+ * reference to a directory also.
+ */
+
+ if (normal_close && fsp->directory_delete_on_close) {
+ BOOL ok = rmdir_internals(fsp->conn, fsp->fsp_name);
+ DEBUG(5,("close_directory: %s. Delete on close was set - deleting directory %s.\n",
+ fsp->fsp_name, ok ? "succeeded" : "failed" ));
+
+ /*
+ * Ensure we remove any change notify requests that would
+ * now fail as the directory has been deleted.
+ */
+
+ if(ok)
+ remove_pending_change_notify_requests_by_filename(fsp);
+ process_pending_change_notify_queue((time_t)0);
+ }
+
+ /*
+ * Do the code common to files and directories.
+ */
+ close_filestruct(fsp);
+
+ if (fsp->fsp_name)
+ string_free(&fsp->fsp_name);
+
+ file_free(fsp);
+ return 0;
+}
+
+/****************************************************************************
+ Close a 'stat file' opened internally.
+****************************************************************************/
+
+static int close_stat(files_struct *fsp)
+{
+ /*
+ * Do the code common to files and directories.
+ */
+ close_filestruct(fsp);
+
+ if (fsp->fsp_name)
+ string_free(&fsp->fsp_name);
+
+ file_free(fsp);
+ return 0;
+}
+
+/****************************************************************************
+ Close a files_struct.
+****************************************************************************/
+
+int close_file(files_struct *fsp, BOOL normal_close)
+{
+ if(fsp->is_directory)
+ return close_directory(fsp, normal_close);
+ else if (fsp->is_stat)
+ return close_stat(fsp);
+ else
+ return close_normal_file(fsp, normal_close);
+}
diff --git a/source/smbd/conn.c b/source/smbd/conn.c
new file mode 100644
index 00000000000..0805f8e6902
--- /dev/null
+++ b/source/smbd/conn.c
@@ -0,0 +1,305 @@
+/*
+ Unix SMB/CIFS implementation.
+ Manage connections_struct structures
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Alexander Bokovoy 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* The connections bitmap is expanded in increments of BITMAP_BLOCK_SZ. The
+ * maximum size of the bitmap is the largest positive integer, but you will hit
+ * the "max connections" limit, looong before that.
+ */
+#define BITMAP_BLOCK_SZ 128
+
+static connection_struct *Connections;
+
+/* number of open connections */
+static struct bitmap *bmap;
+static int num_open;
+
+/****************************************************************************
+init the conn structures
+****************************************************************************/
+void conn_init(void)
+{
+ bmap = bitmap_allocate(BITMAP_BLOCK_SZ);
+}
+
+/****************************************************************************
+return the number of open connections
+****************************************************************************/
+int conn_num_open(void)
+{
+ return num_open;
+}
+
+
+/****************************************************************************
+check if a snum is in use
+****************************************************************************/
+BOOL conn_snum_used(int snum)
+{
+ connection_struct *conn;
+ for (conn=Connections;conn;conn=conn->next) {
+ if (conn->service == snum) {
+ return(True);
+ }
+ }
+ return(False);
+}
+
+
+/****************************************************************************
+find a conn given a cnum
+****************************************************************************/
+connection_struct *conn_find(unsigned cnum)
+{
+ int count=0;
+ connection_struct *conn;
+
+ for (conn=Connections;conn;conn=conn->next,count++) {
+ if (conn->cnum == cnum) {
+ if (count > 10) {
+ DLIST_PROMOTE(Connections, conn);
+ }
+ return conn;
+ }
+ }
+
+ return NULL;
+}
+
+
+/****************************************************************************
+ find first available connection slot, starting from a random position.
+The randomisation stops problems with the server dieing and clients
+thinking the server is still available.
+****************************************************************************/
+connection_struct *conn_new(void)
+{
+ TALLOC_CTX *mem_ctx;
+ connection_struct *conn;
+ int i;
+ int find_offset = 1;
+
+find_again:
+ i = bitmap_find(bmap, find_offset);
+
+ if (i == -1) {
+ /* Expand the connections bitmap. */
+ int oldsz = bmap->n;
+ int newsz = bmap->n + BITMAP_BLOCK_SZ;
+ struct bitmap * nbmap;
+
+ if (newsz <= 0) {
+ /* Integer wrap. */
+ DEBUG(0,("ERROR! Out of connection structures\n"));
+ return NULL;
+ }
+
+ DEBUG(4,("resizing connections bitmap from %d to %d\n",
+ oldsz, newsz));
+
+ nbmap = bitmap_allocate(newsz);
+
+ bitmap_copy(nbmap, bmap);
+ bitmap_free(bmap);
+
+ bmap = nbmap;
+ find_offset = oldsz; /* Start next search in the new portion. */
+
+ goto find_again;
+ }
+
+ if ((mem_ctx=talloc_init("connection_struct"))==NULL) {
+ DEBUG(0,("talloc_init(connection_struct) failed!\n"));
+ return NULL;
+ }
+
+ if ((conn=(connection_struct *)talloc_zero(mem_ctx, sizeof(*conn)))==NULL) {
+ DEBUG(0,("talloc_zero() failed!\n"));
+ return NULL;
+ }
+ conn->mem_ctx = mem_ctx;
+ conn->cnum = i;
+
+ bitmap_set(bmap, i);
+
+ num_open++;
+
+ string_set(&conn->user,"");
+ string_set(&conn->dirpath,"");
+ string_set(&conn->connectpath,"");
+ string_set(&conn->origpath,"");
+
+ DLIST_ADD(Connections, conn);
+
+ return conn;
+}
+
+/****************************************************************************
+close all conn structures
+****************************************************************************/
+void conn_close_all(void)
+{
+ connection_struct *conn, *next;
+ for (conn=Connections;conn;conn=next) {
+ next=conn->next;
+ close_cnum(conn, conn->vuid);
+ }
+}
+
+/****************************************************************************
+ Idle inactive connections.
+****************************************************************************/
+
+BOOL conn_idle_all(time_t t, int deadtime)
+{
+ pipes_struct *plist = NULL;
+ BOOL allidle = True;
+ connection_struct *conn, *next;
+
+ for (conn=Connections;conn;conn=next) {
+ next=conn->next;
+ /* close dirptrs on connections that are idle */
+ if ((t-conn->lastused) > DPTR_IDLE_TIMEOUT)
+ dptr_idlecnum(conn);
+
+ if (conn->num_files_open > 0 ||
+ (t-conn->lastused)<deadtime)
+ allidle = False;
+ }
+
+ /*
+ * Check all pipes for any open handles. We cannot
+ * idle with a handle open.
+ */
+
+ for (plist = get_first_internal_pipe(); plist; plist = get_next_internal_pipe(plist))
+ if (plist->pipe_handles && plist->pipe_handles->count)
+ allidle = False;
+
+ return allidle;
+}
+
+/****************************************************************************
+ Clear a vuid out of the validity cache, and as the 'owner' of a connection.
+****************************************************************************/
+
+void conn_clear_vuid_cache(uint16 vuid)
+{
+ connection_struct *conn;
+ unsigned int i;
+
+ for (conn=Connections;conn;conn=conn->next) {
+ if (conn->vuid == vuid) {
+ conn->vuid = UID_FIELD_INVALID;
+ }
+
+ for (i=0;i<conn->vuid_cache.entries && i< VUID_CACHE_SIZE;i++) {
+ if (conn->vuid_cache.array[i].vuid == vuid) {
+ struct vuid_cache_entry *ent = &conn->vuid_cache.array[i];
+ ent->vuid = UID_FIELD_INVALID;
+ ent->read_only = False;
+ ent->admin_user = False;
+ }
+ }
+ }
+}
+
+/****************************************************************************
+ Free a conn structure.
+****************************************************************************/
+
+void conn_free(connection_struct *conn)
+{
+ vfs_handle_struct *handle = NULL, *thandle = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+
+ /* Free vfs_connection_struct */
+ handle = conn->vfs_handles;
+ while(handle) {
+ DLIST_REMOVE(conn->vfs_handles, handle);
+ thandle = handle->next;
+ if (handle->free_data)
+ handle->free_data(&handle->data);
+ handle = thandle;
+ }
+
+ DLIST_REMOVE(Connections, conn);
+
+ if (conn->ngroups && conn->groups) {
+ SAFE_FREE(conn->groups);
+ conn->ngroups = 0;
+ }
+
+ if (conn->nt_user_token) {
+ delete_nt_token(&(conn->nt_user_token));
+ }
+
+ if (conn->privs) {
+ destroy_privilege(&(conn->privs));
+ }
+
+ free_namearray(conn->veto_list);
+ free_namearray(conn->hide_list);
+ free_namearray(conn->veto_oplock_list);
+
+ string_free(&conn->user);
+ string_free(&conn->dirpath);
+ string_free(&conn->connectpath);
+ string_free(&conn->origpath);
+
+ bitmap_clear(bmap, conn->cnum);
+ num_open--;
+
+ mem_ctx = conn->mem_ctx;
+ ZERO_STRUCTP(conn);
+ talloc_destroy(mem_ctx);
+}
+
+
+/****************************************************************************
+receive a smbcontrol message to forcibly unmount a share
+the message contains just a share name and all instances of that
+share are unmounted
+the special sharename '*' forces unmount of all shares
+****************************************************************************/
+void msg_force_tdis(int msg_type, pid_t pid, void *buf, size_t len)
+{
+ connection_struct *conn, *next;
+ fstring sharename;
+
+ fstrcpy(sharename, buf);
+
+ if (strcmp(sharename, "*") == 0) {
+ DEBUG(1,("Forcing close of all shares\n"));
+ conn_close_all();
+ return;
+ }
+
+ for (conn=Connections;conn;conn=next) {
+ next=conn->next;
+ if (strequal(lp_servicename(conn->service), sharename)) {
+ DEBUG(1,("Forcing close of share %s cnum=%d\n",
+ sharename, conn->cnum));
+ close_cnum(conn, (uint16)-1);
+ }
+ }
+}
diff --git a/source/smbd/connection.c b/source/smbd/connection.c
new file mode 100644
index 00000000000..a9ab1424615
--- /dev/null
+++ b/source/smbd/connection.c
@@ -0,0 +1,243 @@
+/*
+ Unix SMB/CIFS implementation.
+ connection claim routines
+ Copyright (C) Andrew Tridgell 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static TDB_CONTEXT *tdb;
+
+/****************************************************************************
+ Return the connection tdb context (used for message send all).
+****************************************************************************/
+
+TDB_CONTEXT *conn_tdb_ctx(void)
+{
+ if (!tdb)
+ tdb = tdb_open_ex(lock_path("connections.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT,
+ O_RDWR | O_CREAT, 0644, smbd_tdb_log);
+
+ return tdb;
+}
+
+static void make_conn_key(connection_struct *conn, const char *name, TDB_DATA *pkbuf, struct connections_key *pkey)
+{
+ ZERO_STRUCTP(pkey);
+ pkey->pid = sys_getpid();
+ pkey->cnum = conn?conn->cnum:-1;
+ fstrcpy(pkey->name, name);
+#ifdef DEVELOPER
+ /* valgrind fixer... */
+ {
+ size_t sl = strlen(pkey->name);
+ if (sizeof(fstring)-sl)
+ memset(&pkey->name[sl], '\0', sizeof(fstring)-sl);
+ }
+#endif
+
+ pkbuf->dptr = (char *)pkey;
+ pkbuf->dsize = sizeof(*pkey);
+}
+
+/****************************************************************************
+ Delete a connection record.
+****************************************************************************/
+
+BOOL yield_connection(connection_struct *conn, const char *name)
+{
+ struct connections_key key;
+ TDB_DATA kbuf;
+
+ if (!tdb)
+ return False;
+
+ DEBUG(3,("Yielding connection to %s\n",name));
+
+ make_conn_key(conn, name, &kbuf, &key);
+
+ if (tdb_delete(tdb, kbuf) != 0) {
+ int dbg_lvl = (!conn && (tdb_error(tdb) == TDB_ERR_NOEXIST)) ? 3 : 0;
+ DEBUG(dbg_lvl,("yield_connection: tdb_delete for name %s failed with error %s.\n",
+ name, tdb_errorstr(tdb) ));
+ return (False);
+ }
+
+ return(True);
+}
+
+struct count_stat {
+ pid_t mypid;
+ int curr_connections;
+ char *name;
+ BOOL Clear;
+};
+
+/****************************************************************************
+ Count the entries belonging to a service in the connection db.
+****************************************************************************/
+
+static int count_fn( TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *udp)
+{
+ struct connections_data crec;
+ struct count_stat *cs = (struct count_stat *)udp;
+
+ if (dbuf.dsize != sizeof(crec))
+ return 0;
+
+ memcpy(&crec, dbuf.dptr, sizeof(crec));
+
+ if (crec.cnum == -1)
+ return 0;
+
+ /* If the pid was not found delete the entry from connections.tdb */
+
+ if (cs->Clear && !process_exists(crec.pid) && (errno == ESRCH)) {
+ DEBUG(2,("pid %u doesn't exist - deleting connections %d [%s]\n",
+ (unsigned int)crec.pid, crec.cnum, crec.name));
+ if (tdb_delete(the_tdb, kbuf) != 0)
+ DEBUG(0,("count_fn: tdb_delete failed with error %s\n", tdb_errorstr(tdb) ));
+ return 0;
+ }
+
+ if (strequal(crec.name, cs->name))
+ cs->curr_connections++;
+
+ return 0;
+}
+
+/****************************************************************************
+ Claim an entry in the connections database.
+****************************************************************************/
+
+BOOL claim_connection(connection_struct *conn, const char *name,int max_connections,BOOL Clear, uint32 msg_flags)
+{
+ struct connections_key key;
+ struct connections_data crec;
+ TDB_DATA kbuf, dbuf;
+
+ if (!tdb)
+ tdb = tdb_open_ex(lock_path("connections.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT,
+ O_RDWR | O_CREAT, 0644, smbd_tdb_log);
+
+ if (!tdb)
+ return False;
+
+ /*
+ * Enforce the max connections parameter.
+ */
+
+ if (max_connections > 0) {
+ struct count_stat cs;
+
+ cs.mypid = sys_getpid();
+ cs.curr_connections = 0;
+ cs.name = lp_servicename(SNUM(conn));
+ cs.Clear = Clear;
+
+ /*
+ * This has a race condition, but locking the chain before hand is worse
+ * as it leads to deadlock.
+ */
+
+ if (tdb_traverse(tdb, count_fn, &cs) == -1) {
+ DEBUG(0,("claim_connection: traverse of connections.tdb failed with error %s.\n",
+ tdb_errorstr(tdb) ));
+ return False;
+ }
+
+ if (cs.curr_connections >= max_connections) {
+ DEBUG(1,("claim_connection: Max connections (%d) exceeded for %s\n",
+ max_connections, name ));
+ return False;
+ }
+ }
+
+ DEBUG(5,("claiming %s %d\n",name,max_connections));
+
+ make_conn_key(conn, name, &kbuf, &key);
+
+ /* fill in the crec */
+ ZERO_STRUCT(crec);
+ crec.magic = 0x280267;
+ crec.pid = sys_getpid();
+ crec.cnum = conn?conn->cnum:-1;
+ if (conn) {
+ crec.uid = conn->uid;
+ crec.gid = conn->gid;
+ safe_strcpy(crec.name,
+ lp_servicename(SNUM(conn)),sizeof(crec.name)-1);
+ }
+ crec.start = time(NULL);
+ crec.bcast_msg_flags = msg_flags;
+
+ safe_strcpy(crec.machine,get_remote_machine_name(),sizeof(crec.machine)-1);
+ safe_strcpy(crec.addr,conn?conn->client_address:client_addr(),sizeof(crec.addr)-1);
+
+ dbuf.dptr = (char *)&crec;
+ dbuf.dsize = sizeof(crec);
+
+ if (tdb_store(tdb, kbuf, dbuf, TDB_REPLACE) != 0) {
+ DEBUG(0,("claim_connection: tdb_store failed with error %s.\n",
+ tdb_errorstr(tdb) ));
+ return False;
+ }
+
+ return True;
+}
+
+BOOL register_message_flags(BOOL doreg, uint32 msg_flags)
+{
+ struct connections_key key;
+ struct connections_data *pcrec;
+ TDB_DATA kbuf, dbuf;
+
+ if (!tdb)
+ return False;
+
+ DEBUG(10,("register_message_flags: %s flags 0x%x\n",
+ doreg ? "adding" : "removing",
+ (unsigned int)msg_flags ));
+
+ make_conn_key(NULL, "", &kbuf, &key);
+
+ dbuf = tdb_fetch(tdb, kbuf);
+ if (!dbuf.dptr) {
+ DEBUG(0,("register_message_flags: tdb_fetch failed\n"));
+ return False;
+ }
+
+ pcrec = (struct connections_data *)dbuf.dptr;
+ pcrec->bcast_msg_flags = msg_flags;
+ if (doreg)
+ pcrec->bcast_msg_flags |= msg_flags;
+ else
+ pcrec->bcast_msg_flags &= ~msg_flags;
+
+ if (tdb_store(tdb, kbuf, dbuf, TDB_REPLACE) != 0) {
+ DEBUG(0,("register_message_flags: tdb_store failed with error %s.\n",
+ tdb_errorstr(tdb) ));
+ SAFE_FREE(dbuf.dptr);
+ return False;
+ }
+
+ DEBUG(10,("register_message_flags: new flags 0x%x\n",
+ (unsigned int)pcrec->bcast_msg_flags ));
+
+ SAFE_FREE(dbuf.dptr);
+ return True;
+}
diff --git a/source/smbd/dfree.c b/source/smbd/dfree.c
new file mode 100644
index 00000000000..f93cdf3791e
--- /dev/null
+++ b/source/smbd/dfree.c
@@ -0,0 +1,164 @@
+/*
+ Unix SMB/CIFS implementation.
+ functions to calculate the free disk space
+ Copyright (C) Andrew Tridgell 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+normalise for DOS usage
+****************************************************************************/
+static void disk_norm(BOOL small_query, SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize)
+{
+ /* check if the disk is beyond the max disk size */
+ SMB_BIG_UINT maxdisksize = lp_maxdisksize();
+ if (maxdisksize) {
+ /* convert to blocks - and don't overflow */
+ maxdisksize = ((maxdisksize*1024)/(*bsize))*1024;
+ if (*dsize > maxdisksize) *dsize = maxdisksize;
+ if (*dfree > maxdisksize) *dfree = maxdisksize-1;
+ /* the -1 should stop applications getting div by 0
+ errors */
+ }
+
+ while (*dfree > WORDMAX || *dsize > WORDMAX || *bsize < 512) {
+ *dfree /= 2;
+ *dsize /= 2;
+ *bsize *= 2;
+ if(small_query) {
+ /*
+ * Force max to fit in 16 bit fields.
+ */
+ if (*bsize > (WORDMAX*512)) {
+ *bsize = (WORDMAX*512);
+ if (*dsize > WORDMAX)
+ *dsize = WORDMAX;
+ if (*dfree > WORDMAX)
+ *dfree = WORDMAX;
+ break;
+ }
+ }
+ }
+}
+
+
+
+/****************************************************************************
+ return number of 1K blocks available on a path and total number
+****************************************************************************/
+
+static SMB_BIG_UINT disk_free(const char *path, BOOL small_query,
+ SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize)
+{
+ int dfree_retval;
+ SMB_BIG_UINT dfree_q = 0;
+ SMB_BIG_UINT bsize_q = 0;
+ SMB_BIG_UINT dsize_q = 0;
+ char *dfree_command;
+
+ (*dfree) = (*dsize) = 0;
+ (*bsize) = 512;
+
+ /*
+ * If external disk calculation specified, use it.
+ */
+
+ dfree_command = lp_dfree_command();
+ if (dfree_command && *dfree_command) {
+ const char *p;
+ char **lines;
+ pstring syscmd;
+
+ slprintf(syscmd, sizeof(syscmd)-1, "%s %s", dfree_command, path);
+ DEBUG (3, ("disk_free: Running command %s\n", syscmd));
+
+ lines = file_lines_pload(syscmd, NULL);
+ if (lines) {
+ char *line = lines[0];
+
+ DEBUG (3, ("Read input from dfree, \"%s\"\n", line));
+
+ *dsize = STR_TO_SMB_BIG_UINT(line, &p);
+ while (p && *p && isspace(*p))
+ p++;
+ if (p && *p)
+ *dfree = STR_TO_SMB_BIG_UINT(p, &p);
+ while (p && *p && isspace(*p))
+ p++;
+ if (p && *p)
+ *bsize = STR_TO_SMB_BIG_UINT(p, NULL);
+ else
+ *bsize = 1024;
+ file_lines_free(lines);
+ DEBUG (3, ("Parsed output of dfree, dsize=%u, dfree=%u, bsize=%u\n",
+ (unsigned int)*dsize, (unsigned int)*dfree, (unsigned int)*bsize));
+
+ if (!*dsize)
+ *dsize = 2048;
+ if (!*dfree)
+ *dfree = 1024;
+ } else {
+ DEBUG (0, ("disk_free: sys_popen() failed for command %s. Error was : %s\n",
+ syscmd, strerror(errno) ));
+ sys_fsusage(path, dfree, dsize);
+ }
+ } else
+ sys_fsusage(path, dfree, dsize);
+
+ if (disk_quotas(path, &bsize_q, &dfree_q, &dsize_q)) {
+ (*bsize) = bsize_q;
+ (*dfree) = MIN(*dfree,dfree_q);
+ (*dsize) = MIN(*dsize,dsize_q);
+ }
+
+ /* FIXME : Any reason for this assumption ? */
+ if (*bsize < 256) {
+ DEBUG(5,("disk_free:Warning: bsize == %d < 256 . Changing to assumed correct bsize = 512\n",(int)*bsize));
+ *bsize = 512;
+ }
+
+ if ((*dsize)<1) {
+ static int done;
+ if (!done) {
+ DEBUG(0,("WARNING: dfree is broken on this system\n"));
+ done=1;
+ }
+ *dsize = 20*1024*1024/(*bsize);
+ *dfree = MAX(1,*dfree);
+ }
+
+ disk_norm(small_query,bsize,dfree,dsize);
+
+ if ((*bsize) < 1024) {
+ dfree_retval = (*dfree)/(1024/(*bsize));
+ } else {
+ dfree_retval = ((*bsize)/1024)*(*dfree);
+ }
+
+ return(dfree_retval);
+}
+
+
+/****************************************************************************
+wrap it to get filenames right
+****************************************************************************/
+SMB_BIG_UINT sys_disk_free(const char *path, BOOL small_query,
+ SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize)
+{
+ return disk_free(path,small_query, bsize,dfree,dsize);
+}
diff --git a/source/smbd/dir.c b/source/smbd/dir.c
new file mode 100644
index 00000000000..06ef23ab8cd
--- /dev/null
+++ b/source/smbd/dir.c
@@ -0,0 +1,1096 @@
+/*
+ Unix SMB/CIFS implementation.
+ Directory handling routines
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*
+ This module implements directory related functions for Samba.
+*/
+
+typedef struct _dptr_struct {
+ struct _dptr_struct *next, *prev;
+ int dnum;
+ uint16 spid;
+ connection_struct *conn;
+ void *ptr;
+ BOOL expect_close;
+ char *wcard; /* Field only used for trans2_ searches */
+ uint16 attr; /* Field only used for trans2_ searches */
+ char *path;
+} dptr_struct;
+
+static struct bitmap *dptr_bmap;
+static dptr_struct *dirptrs;
+
+static int dptrs_open = 0;
+
+#define INVALID_DPTR_KEY (-3)
+
+/****************************************************************************
+ Initialise the dir bitmap.
+****************************************************************************/
+
+void init_dptrs(void)
+{
+ static BOOL dptrs_init=False;
+
+ if (dptrs_init)
+ return;
+
+ dptr_bmap = bitmap_allocate(MAX_DIRECTORY_HANDLES);
+
+ if (!dptr_bmap)
+ exit_server("out of memory in init_dptrs");
+
+ dptrs_init = True;
+}
+
+/****************************************************************************
+ Idle a dptr - the directory is closed but the control info is kept.
+****************************************************************************/
+
+static void dptr_idle(dptr_struct *dptr)
+{
+ if (dptr->ptr) {
+ DEBUG(4,("Idling dptr dnum %d\n",dptr->dnum));
+ dptrs_open--;
+ CloseDir(dptr->ptr);
+ dptr->ptr = NULL;
+ }
+}
+
+/****************************************************************************
+ Idle the oldest dptr.
+****************************************************************************/
+
+static void dptr_idleoldest(void)
+{
+ dptr_struct *dptr;
+
+ /*
+ * Go to the end of the list.
+ */
+ for(dptr = dirptrs; dptr && dptr->next; dptr = dptr->next)
+ ;
+
+ if(!dptr) {
+ DEBUG(0,("No dptrs available to idle ?\n"));
+ return;
+ }
+
+ /*
+ * Idle the oldest pointer.
+ */
+
+ for(; dptr; dptr = dptr->prev) {
+ if (dptr->ptr) {
+ dptr_idle(dptr);
+ return;
+ }
+ }
+}
+
+/****************************************************************************
+ Get the dptr_struct for a dir index.
+****************************************************************************/
+
+static dptr_struct *dptr_get(int key, BOOL forclose)
+{
+ dptr_struct *dptr;
+
+ for(dptr = dirptrs; dptr; dptr = dptr->next) {
+ if(dptr->dnum == key) {
+ if (!forclose && !dptr->ptr) {
+ if (dptrs_open >= MAX_OPEN_DIRECTORIES)
+ dptr_idleoldest();
+ DEBUG(4,("Reopening dptr key %d\n",key));
+ if ((dptr->ptr = OpenDir(dptr->conn, dptr->path, True)))
+ dptrs_open++;
+ }
+ DLIST_PROMOTE(dirptrs,dptr);
+ return dptr;
+ }
+ }
+ return(NULL);
+}
+
+/****************************************************************************
+ Get the dptr ptr for a dir index.
+****************************************************************************/
+
+static void *dptr_ptr(int key)
+{
+ dptr_struct *dptr = dptr_get(key, False);
+
+ if (dptr)
+ return(dptr->ptr);
+ return(NULL);
+}
+
+/****************************************************************************
+ Get the dir path for a dir index.
+****************************************************************************/
+
+char *dptr_path(int key)
+{
+ dptr_struct *dptr = dptr_get(key, False);
+
+ if (dptr)
+ return(dptr->path);
+ return(NULL);
+}
+
+/****************************************************************************
+ Get the dir wcard for a dir index (lanman2 specific).
+****************************************************************************/
+
+char *dptr_wcard(int key)
+{
+ dptr_struct *dptr = dptr_get(key, False);
+
+ if (dptr)
+ return(dptr->wcard);
+ return(NULL);
+}
+
+/****************************************************************************
+ Set the dir wcard for a dir index (lanman2 specific).
+ Returns 0 on ok, 1 on fail.
+****************************************************************************/
+
+BOOL dptr_set_wcard(int key, char *wcard)
+{
+ dptr_struct *dptr = dptr_get(key, False);
+
+ if (dptr) {
+ dptr->wcard = wcard;
+ return True;
+ }
+ return False;
+}
+
+/****************************************************************************
+ Set the dir attrib for a dir index (lanman2 specific).
+ Returns 0 on ok, 1 on fail.
+****************************************************************************/
+
+BOOL dptr_set_attr(int key, uint16 attr)
+{
+ dptr_struct *dptr = dptr_get(key, False);
+
+ if (dptr) {
+ dptr->attr = attr;
+ return True;
+ }
+ return False;
+}
+
+/****************************************************************************
+ Get the dir attrib for a dir index (lanman2 specific)
+****************************************************************************/
+
+uint16 dptr_attr(int key)
+{
+ dptr_struct *dptr = dptr_get(key, False);
+
+ if (dptr)
+ return(dptr->attr);
+ return(0);
+}
+
+/****************************************************************************
+ Close a dptr (internal func).
+****************************************************************************/
+
+static void dptr_close_internal(dptr_struct *dptr)
+{
+ DEBUG(4,("closing dptr key %d\n",dptr->dnum));
+
+ DLIST_REMOVE(dirptrs, dptr);
+
+ /*
+ * Free the dnum in the bitmap. Remember the dnum value is always
+ * biased by one with respect to the bitmap.
+ */
+
+ if(bitmap_query( dptr_bmap, dptr->dnum - 1) != True) {
+ DEBUG(0,("dptr_close_internal : Error - closing dnum = %d and bitmap not set !\n",
+ dptr->dnum ));
+ }
+
+ bitmap_clear(dptr_bmap, dptr->dnum - 1);
+
+ if (dptr->ptr) {
+ CloseDir(dptr->ptr);
+ dptrs_open--;
+ }
+
+ /* Lanman 2 specific code */
+ SAFE_FREE(dptr->wcard);
+ string_set(&dptr->path,"");
+ SAFE_FREE(dptr);
+}
+
+/****************************************************************************
+ Close a dptr given a key.
+****************************************************************************/
+
+void dptr_close(int *key)
+{
+ dptr_struct *dptr;
+
+ if(*key == INVALID_DPTR_KEY)
+ return;
+
+ /* OS/2 seems to use -1 to indicate "close all directories" */
+ if (*key == -1) {
+ dptr_struct *next;
+ for(dptr = dirptrs; dptr; dptr = next) {
+ next = dptr->next;
+ dptr_close_internal(dptr);
+ }
+ *key = INVALID_DPTR_KEY;
+ return;
+ }
+
+ dptr = dptr_get(*key, True);
+
+ if (!dptr) {
+ DEBUG(0,("Invalid key %d given to dptr_close\n", *key));
+ return;
+ }
+
+ dptr_close_internal(dptr);
+
+ *key = INVALID_DPTR_KEY;
+}
+
+/****************************************************************************
+ Close all dptrs for a cnum.
+****************************************************************************/
+
+void dptr_closecnum(connection_struct *conn)
+{
+ dptr_struct *dptr, *next;
+ for(dptr = dirptrs; dptr; dptr = next) {
+ next = dptr->next;
+ if (dptr->conn == conn)
+ dptr_close_internal(dptr);
+ }
+}
+
+/****************************************************************************
+ Idle all dptrs for a cnum.
+****************************************************************************/
+
+void dptr_idlecnum(connection_struct *conn)
+{
+ dptr_struct *dptr;
+ for(dptr = dirptrs; dptr; dptr = dptr->next) {
+ if (dptr->conn == conn && dptr->ptr)
+ dptr_idle(dptr);
+ }
+}
+
+/****************************************************************************
+ Close a dptr that matches a given path, only if it matches the spid also.
+****************************************************************************/
+
+void dptr_closepath(char *path,uint16 spid)
+{
+ dptr_struct *dptr, *next;
+ for(dptr = dirptrs; dptr; dptr = next) {
+ next = dptr->next;
+ if (spid == dptr->spid && strequal(dptr->path,path))
+ dptr_close_internal(dptr);
+ }
+}
+
+/****************************************************************************
+ Start a directory listing.
+****************************************************************************/
+
+static BOOL start_dir(connection_struct *conn, pstring directory)
+{
+ const char *dir2;
+
+ DEBUG(5,("start_dir dir=%s\n",directory));
+
+ if (!check_name(directory,conn))
+ return(False);
+
+ /* use a const pointer from here on */
+ dir2 = directory;
+
+ if (! *dir2)
+ dir2 = ".";
+
+ conn->dirptr = OpenDir(conn, directory, True);
+ if (conn->dirptr) {
+ dptrs_open++;
+ string_set(&conn->dirpath,directory);
+ return(True);
+ }
+
+ return(False);
+}
+
+/****************************************************************************
+ Try and close the oldest handle not marked for
+ expect close in the hope that the client has
+ finished with that one.
+****************************************************************************/
+
+static void dptr_close_oldest(BOOL old)
+{
+ dptr_struct *dptr;
+
+ /*
+ * Go to the end of the list.
+ */
+ for(dptr = dirptrs; dptr && dptr->next; dptr = dptr->next)
+ ;
+
+ if(!dptr) {
+ DEBUG(0,("No old dptrs available to close oldest ?\n"));
+ return;
+ }
+
+ /*
+ * If 'old' is true, close the oldest oldhandle dnum (ie. 1 < dnum < 256) that
+ * does not have expect_close set. If 'old' is false, close
+ * one of the new dnum handles.
+ */
+
+ for(; dptr; dptr = dptr->prev) {
+ if ((old && (dptr->dnum < 256) && !dptr->expect_close) ||
+ (!old && (dptr->dnum > 255))) {
+ dptr_close_internal(dptr);
+ return;
+ }
+ }
+}
+
+/****************************************************************************
+ Create a new dir ptr. If the flag old_handle is true then we must allocate
+ from the bitmap range 0 - 255 as old SMBsearch directory handles are only
+ one byte long. If old_handle is false we allocate from the range
+ 256 - MAX_DIRECTORY_HANDLES. We bias the number we return by 1 to ensure
+ a directory handle is never zero. All the above is folklore taught to
+ me at Andrew's knee.... :-) :-). JRA.
+****************************************************************************/
+
+int dptr_create(connection_struct *conn, pstring path, BOOL old_handle, BOOL expect_close,uint16 spid)
+{
+ dptr_struct *dptr;
+
+ if (!start_dir(conn,path))
+ return(-2); /* Code to say use a unix error return code. */
+
+ if (dptrs_open >= MAX_OPEN_DIRECTORIES)
+ dptr_idleoldest();
+
+ dptr = (dptr_struct *)malloc(sizeof(dptr_struct));
+ if(!dptr) {
+ DEBUG(0,("malloc fail in dptr_create.\n"));
+ return -1;
+ }
+
+ ZERO_STRUCTP(dptr);
+
+ if(old_handle) {
+
+ /*
+ * This is an old-style SMBsearch request. Ensure the
+ * value we return will fit in the range 1-255.
+ */
+
+ dptr->dnum = bitmap_find(dptr_bmap, 0);
+
+ if(dptr->dnum == -1 || dptr->dnum > 254) {
+
+ /*
+ * Try and close the oldest handle not marked for
+ * expect close in the hope that the client has
+ * finished with that one.
+ */
+
+ dptr_close_oldest(True);
+
+ /* Now try again... */
+ dptr->dnum = bitmap_find(dptr_bmap, 0);
+ if(dptr->dnum == -1 || dptr->dnum > 254) {
+ DEBUG(0,("dptr_create: returned %d: Error - all old dirptrs in use ?\n", dptr->dnum));
+ SAFE_FREE(dptr);
+ return -1;
+ }
+ }
+ } else {
+
+ /*
+ * This is a new-style trans2 request. Allocate from
+ * a range that will return 256 - MAX_DIRECTORY_HANDLES.
+ */
+
+ dptr->dnum = bitmap_find(dptr_bmap, 255);
+
+ if(dptr->dnum == -1 || dptr->dnum < 255) {
+
+ /*
+ * Try and close the oldest handle close in the hope that
+ * the client has finished with that one. This will only
+ * happen in the case of the Win98 client bug where it leaks
+ * directory handles.
+ */
+
+ dptr_close_oldest(False);
+
+ /* Now try again... */
+ dptr->dnum = bitmap_find(dptr_bmap, 255);
+
+ if(dptr->dnum == -1 || dptr->dnum < 255) {
+ DEBUG(0,("dptr_create: returned %d: Error - all new dirptrs in use ?\n", dptr->dnum));
+ SAFE_FREE(dptr);
+ return -1;
+ }
+ }
+ }
+
+ bitmap_set(dptr_bmap, dptr->dnum);
+
+ dptr->dnum += 1; /* Always bias the dnum by one - no zero dnums allowed. */
+
+ dptr->ptr = conn->dirptr;
+ string_set(&dptr->path,path);
+ dptr->conn = conn;
+ dptr->spid = spid;
+ dptr->expect_close = expect_close;
+ dptr->wcard = NULL; /* Only used in lanman2 searches */
+ dptr->attr = 0; /* Only used in lanman2 searches */
+
+ DLIST_ADD(dirptrs, dptr);
+
+ DEBUG(3,("creating new dirptr %d for path %s, expect_close = %d\n",
+ dptr->dnum,path,expect_close));
+
+ return(dptr->dnum);
+}
+
+/****************************************************************************
+ Fill the 5 byte server reserved dptr field.
+****************************************************************************/
+
+BOOL dptr_fill(char *buf1,unsigned int key)
+{
+ unsigned char *buf = (unsigned char *)buf1;
+ void *p = dptr_ptr(key);
+ uint32 offset;
+ if (!p) {
+ DEBUG(1,("filling null dirptr %d\n",key));
+ return(False);
+ }
+ offset = TellDir(p);
+ DEBUG(6,("fill on key %u dirptr 0x%lx now at %d\n",key,
+ (long)p,(int)offset));
+ buf[0] = key;
+ SIVAL(buf,1,offset | DPTR_MASK);
+ return(True);
+}
+
+/****************************************************************************
+ Fetch the dir ptr and seek it given the 5 byte server field.
+****************************************************************************/
+
+void *dptr_fetch(char *buf,int *num)
+{
+ unsigned int key = *(unsigned char *)buf;
+ void *p = dptr_ptr(key);
+ uint32 offset;
+
+ if (!p) {
+ DEBUG(3,("fetched null dirptr %d\n",key));
+ return(NULL);
+ }
+ *num = key;
+ offset = IVAL(buf,1)&~DPTR_MASK;
+ SeekDir(p,offset);
+ DEBUG(3,("fetching dirptr %d for path %s at offset %d\n",
+ key,dptr_path(key),offset));
+ return(p);
+}
+
+/****************************************************************************
+ Fetch the dir ptr.
+****************************************************************************/
+
+void *dptr_fetch_lanman2(int dptr_num)
+{
+ void *p = dptr_ptr(dptr_num);
+
+ if (!p) {
+ DEBUG(3,("fetched null dirptr %d\n",dptr_num));
+ return(NULL);
+ }
+ DEBUG(3,("fetching dirptr %d for path %s\n",dptr_num,dptr_path(dptr_num)));
+ return(p);
+}
+
+/****************************************************************************
+ Check a filetype for being valid.
+****************************************************************************/
+
+BOOL dir_check_ftype(connection_struct *conn,int mode,SMB_STRUCT_STAT *st,int dirtype)
+{
+ int mask;
+
+ /* Check the "may have" search bits. */
+ if (((mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0)
+ return False;
+
+ /* Check the "must have" bits, which are the may have bits shifted eight */
+ /* If must have bit is set, the file/dir can not be returned in search unless the matching
+ file attribute is set */
+ mask = ((dirtype >> 8) & (aDIR|aARCH|aRONLY|aHIDDEN|aSYSTEM)); /* & 0x37 */
+ if(mask) {
+ if((mask & (mode & (aDIR|aARCH|aRONLY|aHIDDEN|aSYSTEM))) == mask) /* check if matching attribute present */
+ return True;
+ else
+ return False;
+ }
+
+ return True;
+}
+
+static BOOL mangle_mask_match(connection_struct *conn, fstring filename, char *mask)
+{
+ mangle_map(filename,True,False,SNUM(conn));
+ return mask_match(filename,mask,False);
+}
+
+/****************************************************************************
+ Get an 8.3 directory entry.
+****************************************************************************/
+
+BOOL get_dir_entry(connection_struct *conn,char *mask,int dirtype, pstring fname,
+ SMB_OFF_T *size,int *mode,time_t *date,BOOL check_descend)
+{
+ const char *dname;
+ BOOL found = False;
+ SMB_STRUCT_STAT sbuf;
+ pstring path;
+ pstring pathreal;
+ BOOL isrootdir;
+ pstring filename;
+ BOOL needslash;
+
+ *path = *pathreal = *filename = 0;
+
+ isrootdir = (strequal(conn->dirpath,"./") ||
+ strequal(conn->dirpath,".") ||
+ strequal(conn->dirpath,"/"));
+
+ needslash = ( conn->dirpath[strlen(conn->dirpath) -1] != '/');
+
+ if (!conn->dirptr)
+ return(False);
+
+ while (!found) {
+ dname = ReadDirName(conn->dirptr);
+
+ DEBUG(6,("readdir on dirptr 0x%lx now at offset %d\n",
+ (long)conn->dirptr,TellDir(conn->dirptr)));
+
+ if (dname == NULL)
+ return(False);
+
+ pstrcpy(filename,dname);
+
+ /* notice the special *.* handling. This appears to be the only difference
+ between the wildcard handling in this routine and in the trans2 routines.
+ see masktest for a demo
+ */
+ if ((strcmp(mask,"*.*") == 0) ||
+ mask_match(filename,mask,False) ||
+ mangle_mask_match(conn,filename,mask)) {
+ if (isrootdir && (strequal(filename,"..") || strequal(filename,".")))
+ continue;
+
+ if (!mangle_is_8_3(filename, False))
+ mangle_map(filename,True,False,SNUM(conn));
+
+ pstrcpy(fname,filename);
+ *path = 0;
+ pstrcpy(path,conn->dirpath);
+ if(needslash)
+ pstrcat(path,"/");
+ pstrcpy(pathreal,path);
+ pstrcat(path,fname);
+ pstrcat(pathreal,dname);
+ if (SMB_VFS_STAT(conn, pathreal, &sbuf) != 0) {
+ DEBUG(5,("Couldn't stat 1 [%s]. Error = %s\n",path, strerror(errno) ));
+ continue;
+ }
+
+ *mode = dos_mode(conn,pathreal,&sbuf);
+
+ if (!dir_check_ftype(conn,*mode,&sbuf,dirtype)) {
+ DEBUG(5,("[%s] attribs didn't match %x\n",filename,dirtype));
+ continue;
+ }
+
+ *size = sbuf.st_size;
+ *date = sbuf.st_mtime;
+
+ DEBUG(3,("get_dir_entry mask=[%s] found %s fname=%s\n",mask, pathreal,fname));
+
+ found = True;
+ }
+ }
+
+ return(found);
+}
+
+typedef struct {
+ int pos;
+ int numentries;
+ int mallocsize;
+ char *data;
+ char *current;
+} Dir;
+
+/*******************************************************************
+ Check to see if a user can read a file. This is only approximate,
+ it is used as part of the "hide unreadable" option. Don't
+ use it for anything security sensitive.
+********************************************************************/
+
+static BOOL user_can_read_file(connection_struct *conn, char *name, SMB_STRUCT_STAT *pst)
+{
+ extern struct current_user current_user;
+ SEC_DESC *psd = NULL;
+ size_t sd_size;
+ files_struct *fsp;
+ int smb_action;
+ NTSTATUS status;
+ uint32 access_granted;
+
+ /*
+ * If user is a member of the Admin group
+ * we never hide files from them.
+ */
+
+ if (conn->admin_user)
+ return True;
+
+ /* If we can't stat it does not show it */
+ if (!VALID_STAT(*pst) && (SMB_VFS_STAT(conn, name, pst) != 0))
+ return False;
+
+ /* Pseudo-open the file (note - no fd's created). */
+
+ if(S_ISDIR(pst->st_mode))
+ fsp = open_directory(conn, name, pst, 0, SET_DENY_MODE(DENY_NONE), (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
+ &smb_action);
+ else
+ fsp = open_file_stat(conn, name, pst);
+
+ if (!fsp)
+ return False;
+
+ /* Get NT ACL -allocated in main loop talloc context. No free needed here. */
+ sd_size = SMB_VFS_FGET_NT_ACL(fsp, fsp->fd,
+ (OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION), &psd);
+ close_file(fsp, True);
+
+ /* No access if SD get failed. */
+ if (!sd_size)
+ return False;
+
+ return se_access_check(psd, current_user.nt_user_token, FILE_READ_DATA,
+ &access_granted, &status);
+}
+
+/*******************************************************************
+ Check to see if a user can write a file (and only files, we do not
+ check dirs on this one). This is only approximate,
+ it is used as part of the "hide unwriteable" option. Don't
+ use it for anything security sensitive.
+********************************************************************/
+
+static BOOL user_can_write_file(connection_struct *conn, char *name, SMB_STRUCT_STAT *pst)
+{
+ extern struct current_user current_user;
+ SEC_DESC *psd = NULL;
+ size_t sd_size;
+ files_struct *fsp;
+ int smb_action;
+ int access_mode;
+ NTSTATUS status;
+ uint32 access_granted;
+
+ /*
+ * If user is a member of the Admin group
+ * we never hide files from them.
+ */
+
+ if (conn->admin_user)
+ return True;
+
+ /* If we can't stat it does not show it */
+ if (!VALID_STAT(*pst) && (SMB_VFS_STAT(conn, name, pst) != 0))
+ return False;
+
+ /* Pseudo-open the file (note - no fd's created). */
+
+ if(S_ISDIR(pst->st_mode))
+ return True;
+ else
+ fsp = open_file_shared1(conn, name, pst, FILE_WRITE_ATTRIBUTES, SET_DENY_MODE(DENY_NONE),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &smb_action);
+
+ if (!fsp)
+ return False;
+
+ /* Get NT ACL -allocated in main loop talloc context. No free needed here. */
+ sd_size = SMB_VFS_FGET_NT_ACL(fsp, fsp->fd,
+ (OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION), &psd);
+ close_file(fsp, False);
+
+ /* No access if SD get failed. */
+ if (!sd_size)
+ return False;
+
+ return se_access_check(psd, current_user.nt_user_token, FILE_WRITE_DATA,
+ &access_granted, &status);
+}
+
+/*******************************************************************
+ Is a file a "special" type ?
+********************************************************************/
+
+static BOOL file_is_special(connection_struct *conn, char *name, SMB_STRUCT_STAT *pst)
+{
+ /*
+ * If user is a member of the Admin group
+ * we never hide files from them.
+ */
+
+ if (conn->admin_user)
+ return False;
+
+ /* If we can't stat it does not show it */
+ if (!VALID_STAT(*pst) && (SMB_VFS_STAT(conn, name, pst) != 0))
+ return True;
+
+ if (S_ISREG(pst->st_mode) || S_ISDIR(pst->st_mode) || S_ISLNK(pst->st_mode))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Open a directory.
+********************************************************************/
+
+void *OpenDir(connection_struct *conn, const char *name, BOOL use_veto)
+{
+ Dir *dirp;
+ const char *n;
+ DIR *p = SMB_VFS_OPENDIR(conn,name);
+ int used=0;
+
+ if (!p)
+ return(NULL);
+ dirp = (Dir *)malloc(sizeof(Dir));
+ if (!dirp) {
+ DEBUG(0,("Out of memory in OpenDir\n"));
+ SMB_VFS_CLOSEDIR(conn,p);
+ return(NULL);
+ }
+ dirp->pos = dirp->numentries = dirp->mallocsize = 0;
+ dirp->data = dirp->current = NULL;
+
+ while (True) {
+ int l;
+ BOOL normal_entry = True;
+ SMB_STRUCT_STAT st;
+ char *entry = NULL;
+
+ if (used == 0) {
+ n = ".";
+ normal_entry = False;
+ } else if (used == 2) {
+ n = "..";
+ normal_entry = False;
+ } else {
+ n = vfs_readdirname(conn, p);
+ if (n == NULL)
+ break;
+ if ((strcmp(".",n) == 0) ||(strcmp("..",n) == 0))
+ continue;
+ normal_entry = True;
+ }
+
+ ZERO_STRUCT(st);
+ l = strlen(n)+1;
+
+ /* If it's a vetoed file, pretend it doesn't even exist */
+ if (normal_entry && use_veto && conn && IS_VETO_PATH(conn, n))
+ continue;
+
+ /* Honour _hide unreadable_ option */
+ if (normal_entry && conn && lp_hideunreadable(SNUM(conn))) {
+ int ret=0;
+
+ if (entry || asprintf(&entry, "%s/%s/%s", conn->origpath, name, n) > 0) {
+ ret = user_can_read_file(conn, entry, &st);
+ }
+ if (!ret) {
+ SAFE_FREE(entry);
+ continue;
+ }
+ }
+
+ /* Honour _hide unwriteable_ option */
+ if (normal_entry && conn && lp_hideunwriteable_files(SNUM(conn))) {
+ int ret=0;
+
+ if (entry || asprintf(&entry, "%s/%s/%s", conn->origpath, name, n) > 0) {
+ ret = user_can_write_file(conn, entry, &st);
+ }
+ if (!ret) {
+ SAFE_FREE(entry);
+ continue;
+ }
+ }
+
+ /* Honour _hide_special_ option */
+ if (normal_entry && conn && lp_hide_special_files(SNUM(conn))) {
+ int ret=0;
+
+ if (entry || asprintf(&entry, "%s/%s/%s", conn->origpath, name, n) > 0) {
+ ret = file_is_special(conn, entry, &st);
+ }
+ if (ret) {
+ SAFE_FREE(entry);
+ continue;
+ }
+ }
+
+ SAFE_FREE(entry);
+
+ if (used + l > dirp->mallocsize) {
+ int s = MAX(used+l,used+2000);
+ char *r;
+ r = (char *)Realloc(dirp->data,s);
+ if (!r) {
+ DEBUG(0,("Out of memory in OpenDir\n"));
+ break;
+ }
+ dirp->data = r;
+ dirp->mallocsize = s;
+ dirp->current = dirp->data;
+ }
+
+ safe_strcpy_base(dirp->data+used,n, dirp->data, dirp->mallocsize);
+ used += l;
+ dirp->numentries++;
+ }
+
+ SMB_VFS_CLOSEDIR(conn,p);
+ return((void *)dirp);
+}
+
+
+/*******************************************************************
+ Close a directory.
+********************************************************************/
+
+void CloseDir(void *p)
+{
+ if (!p)
+ return;
+ SAFE_FREE(((Dir *)p)->data);
+ SAFE_FREE(p);
+}
+
+/*******************************************************************
+ Read from a directory.
+********************************************************************/
+
+const char *ReadDirName(void *p)
+{
+ char *ret;
+ Dir *dirp = (Dir *)p;
+
+ if (!dirp || !dirp->current || dirp->pos >= dirp->numentries)
+ return(NULL);
+
+ ret = dirp->current;
+ dirp->current = skip_string(dirp->current,1);
+ dirp->pos++;
+
+ return(ret);
+}
+
+/*******************************************************************
+ Seek a dir.
+********************************************************************/
+
+BOOL SeekDir(void *p,int pos)
+{
+ Dir *dirp = (Dir *)p;
+
+ if (!dirp)
+ return(False);
+
+ if (pos < dirp->pos) {
+ dirp->current = dirp->data;
+ dirp->pos = 0;
+ }
+
+ while (dirp->pos < pos && ReadDirName(p))
+ ;
+
+ return (dirp->pos == pos);
+}
+
+/*******************************************************************
+ Tell a dir position.
+********************************************************************/
+
+int TellDir(void *p)
+{
+ Dir *dirp = (Dir *)p;
+
+ if (!dirp)
+ return(-1);
+
+ return(dirp->pos);
+}
+
+/*******************************************************************************
+ This section manages a global directory cache.
+ (It should probably be split into a separate module. crh)
+********************************************************************************/
+
+typedef struct {
+ ubi_dlNode node;
+ char *path;
+ char *name;
+ char *dname;
+ int snum;
+} dir_cache_entry;
+
+static ubi_dlNewList( dir_cache );
+
+/*****************************************************************************
+ Add an entry to the directory cache.
+ Input: path -
+ name -
+ dname -
+ snum -
+ Output: None.
+*****************************************************************************/
+
+void DirCacheAdd( const char *path, const char *name, const char *dname, int snum )
+{
+ int pathlen;
+ int namelen;
+ dir_cache_entry *entry;
+
+ /*
+ * Allocate the structure & string space in one go so that it can be freed
+ * in one call to free().
+ */
+ pathlen = strlen(path) + 1; /* Bytes required to store path (with nul). */
+ namelen = strlen(name) + 1; /* Bytes required to store name (with nul). */
+ entry = (dir_cache_entry *)malloc( sizeof( dir_cache_entry )
+ + pathlen
+ + namelen
+ + strlen( dname ) +1 );
+ if( NULL == entry ) /* Not adding to the cache is not fatal, */
+ return; /* so just return as if nothing happened. */
+
+ /* Set pointers correctly and load values. */
+ entry->path = memcpy( (char *)&entry[1], path, strlen(path)+1 );
+ entry->name = memcpy( &(entry->path[pathlen]), name, strlen(name)+1 );
+ entry->dname = memcpy( &(entry->name[namelen]), dname, strlen(dname)+1 );
+ entry->snum = snum;
+
+ /* Add the new entry to the linked list. */
+ (void)ubi_dlAddHead( dir_cache, entry );
+ DEBUG( 4, ("Added dir cache entry %s %s -> %s\n", path, name, dname ) );
+
+ /* Free excess cache entries. */
+ while( DIRCACHESIZE < dir_cache->count )
+ safe_free( ubi_dlRemTail( dir_cache ) );
+}
+
+/*****************************************************************************
+ Search for an entry to the directory cache.
+ Input: path -
+ name -
+ snum -
+ Output: The dname string of the located entry, or NULL if the entry was
+ not found.
+
+ Notes: This uses a linear search, which is is okay because of
+ the small size of the cache. Use a splay tree or hash
+ for large caches.
+*****************************************************************************/
+
+char *DirCacheCheck( const char *path, const char *name, int snum )
+{
+ dir_cache_entry *entry;
+
+ for( entry = (dir_cache_entry *)ubi_dlFirst( dir_cache );
+ NULL != entry;
+ entry = (dir_cache_entry *)ubi_dlNext( entry ) ) {
+ if( entry->snum == snum
+ && entry->name && 0 == strcmp( name, entry->name )
+ && entry->path && 0 == strcmp( path, entry->path ) ) {
+ DEBUG(4, ("Got dir cache hit on %s %s -> %s\n",path,name,entry->dname));
+ return( entry->dname );
+ }
+ }
+
+ return(NULL);
+}
+
+/*****************************************************************************
+ Remove all cache entries which have an snum that matches the input.
+ Input: snum -
+ Output: None.
+*****************************************************************************/
+
+void DirCacheFlush(int snum)
+{
+ dir_cache_entry *entry;
+ ubi_dlNodePtr next;
+
+ for(entry = (dir_cache_entry *)ubi_dlFirst( dir_cache );
+ NULL != entry; ) {
+ next = ubi_dlNext( entry );
+ if( entry->snum == snum )
+ safe_free( ubi_dlRemThis( dir_cache, entry ) );
+ entry = (dir_cache_entry *)next;
+ }
+}
diff --git a/source/smbd/dosmode.c b/source/smbd/dosmode.c
new file mode 100644
index 00000000000..d7dc63bb2fd
--- /dev/null
+++ b/source/smbd/dosmode.c
@@ -0,0 +1,483 @@
+/*
+ Unix SMB/CIFS implementation.
+ dos mode handling functions
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+ Change a dos mode to a unix mode.
+ Base permission for files:
+ if inheriting
+ apply read/write bits from parent directory.
+ else
+ everybody gets read bit set
+ dos readonly is represented in unix by removing everyone's write bit
+ dos archive is represented in unix by the user's execute bit
+ dos system is represented in unix by the group's execute bit
+ dos hidden is represented in unix by the other's execute bit
+ if !inheriting {
+ Then apply create mask,
+ then add force bits.
+ }
+ Base permission for directories:
+ dos directory is represented in unix by unix's dir bit and the exec bit
+ if !inheriting {
+ Then apply create mask,
+ then add force bits.
+ }
+****************************************************************************/
+
+mode_t unix_mode(connection_struct *conn, int dosmode, const char *fname)
+{
+ mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
+ mode_t dir_mode = 0; /* Mode of the parent directory if inheriting. */
+
+ if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
+ result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+ }
+
+ if (fname && lp_inherit_perms(SNUM(conn))) {
+ char *dname;
+ SMB_STRUCT_STAT sbuf;
+
+ dname = parent_dirname(fname);
+ DEBUG(2,("unix_mode(%s) inheriting from %s\n",fname,dname));
+ if (SMB_VFS_STAT(conn,dname,&sbuf) != 0) {
+ DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",fname,dname,strerror(errno)));
+ return(0); /* *** shouldn't happen! *** */
+ }
+
+ /* Save for later - but explicitly remove setuid bit for safety. */
+ dir_mode = sbuf.st_mode & ~S_ISUID;
+ DEBUG(2,("unix_mode(%s) inherit mode %o\n",fname,(int)dir_mode));
+ /* Clear "result" */
+ result = 0;
+ }
+
+ if (IS_DOS_DIR(dosmode)) {
+ /* We never make directories read only for the owner as under DOS a user
+ can always create a file in a read-only directory. */
+ result |= (S_IFDIR | S_IWUSR);
+
+ if (dir_mode) {
+ /* Inherit mode of parent directory. */
+ result |= dir_mode;
+ } else {
+ /* Provisionally add all 'x' bits */
+ result |= (S_IXUSR | S_IXGRP | S_IXOTH);
+
+ /* Apply directory mask */
+ result &= lp_dir_mask(SNUM(conn));
+ /* Add in force bits */
+ result |= lp_force_dir_mode(SNUM(conn));
+ }
+ } else {
+ if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
+ result |= S_IXUSR;
+
+ if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
+ result |= S_IXGRP;
+
+ if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
+ result |= S_IXOTH;
+
+ if (dir_mode) {
+ /* Inherit 666 component of parent directory mode */
+ result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
+ } else {
+ /* Apply mode mask */
+ result &= lp_create_mask(SNUM(conn));
+ /* Add in force bits */
+ result |= lp_force_create_mode(SNUM(conn));
+ }
+ }
+
+ DEBUG(3,("unix_mode(%s) returning 0%o\n",fname,(int)result ));
+ return(result);
+}
+
+/****************************************************************************
+ Change a unix mode to a dos mode.
+****************************************************************************/
+
+uint32 dos_mode_from_sbuf(connection_struct *conn, SMB_STRUCT_STAT *sbuf)
+{
+ int result = 0;
+
+ if ((sbuf->st_mode & S_IWUSR) == 0)
+ result |= aRONLY;
+
+ if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
+ result |= aARCH;
+
+ if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0))
+ result |= aSYSTEM;
+
+ if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0))
+ result |= aHIDDEN;
+
+ if (S_ISDIR(sbuf->st_mode))
+ result = aDIR | (result & aRONLY);
+
+#if defined (HAVE_STAT_ST_BLOCKS) && defined (HAVE_STAT_ST_BLKSIZE)
+ if (sbuf->st_size > sbuf->st_blocks * (SMB_OFF_T)sbuf->st_blksize) {
+ result |= FILE_ATTRIBUTE_SPARSE;
+ }
+#endif
+
+#ifdef S_ISLNK
+#if LINKS_READ_ONLY
+ if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
+ result |= aRONLY;
+#endif
+#endif
+
+ DEBUG(8,("dos_mode_from_sbuf returning "));
+
+ if (result & aHIDDEN) DEBUG(8, ("h"));
+ if (result & aRONLY ) DEBUG(8, ("r"));
+ if (result & aSYSTEM) DEBUG(8, ("s"));
+ if (result & aDIR ) DEBUG(8, ("d"));
+ if (result & aARCH ) DEBUG(8, ("a"));
+
+ DEBUG(8,("\n"));
+ return result;
+}
+
+/****************************************************************************
+ Get DOS attributes from an EA.
+****************************************************************************/
+
+static BOOL get_ea_dos_attribute(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf, uint32 *pattr)
+{
+ ssize_t sizeret;
+ fstring attrstr;
+ unsigned int dosattr;
+
+ if (!lp_store_dos_attributes(SNUM(conn))) {
+ return False;
+ }
+
+ *pattr = 0;
+
+ sizeret = SMB_VFS_GETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, sizeof(attrstr));
+ if (sizeret == -1) {
+#if defined(ENOTSUP) && defined(ENOATTR)
+ if ((errno != ENOTSUP) && (errno != ENOATTR) && (errno != EACCES)) {
+ DEBUG(1,("get_ea_dos_attributes: Cannot get attribute from EA on file %s: Error = %s\n",
+ path, strerror(errno) ));
+ }
+#endif
+ return False;
+ }
+ /* Null terminate string. */
+ attrstr[sizeret] = 0;
+ DEBUG(10,("get_ea_dos_attribute: %s attrstr = %s\n", path, attrstr));
+
+ if (sizeret < 2 || attrstr[0] != '0' || attrstr[1] != 'x' ||
+ sscanf(attrstr, "%x", &dosattr) != 1) {
+ DEBUG(1,("get_ea_dos_attributes: Badly formed DOSATTRIB on file %s - %s\n", path, attrstr));
+ return False;
+ }
+
+ if (S_ISDIR(sbuf->st_mode)) {
+ dosattr |= aDIR;
+ }
+ *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
+
+ DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
+
+ if (dosattr & aHIDDEN) DEBUG(8, ("h"));
+ if (dosattr & aRONLY ) DEBUG(8, ("r"));
+ if (dosattr & aSYSTEM) DEBUG(8, ("s"));
+ if (dosattr & aDIR ) DEBUG(8, ("d"));
+ if (dosattr & aARCH ) DEBUG(8, ("a"));
+
+ DEBUG(8,("\n"));
+
+ return True;
+}
+
+/****************************************************************************
+ Set DOS attributes in an EA.
+****************************************************************************/
+
+static BOOL set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, uint32 dosmode)
+{
+ fstring attrstr;
+ files_struct *fsp = NULL;
+ BOOL ret = False;
+
+ snprintf(attrstr, sizeof(attrstr)-1, "0x%x", dosmode & SAMBA_ATTRIBUTES_MASK);
+ if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == -1) {
+ if((errno != EPERM) && (errno != EACCES)) {
+ return False;
+ }
+
+ /* We want DOS semantics, ie allow non owner with write permission to change the
+ bits on a file. Just like file_utime below.
+ */
+
+ /* Check if we have write access. */
+ if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
+ return False;
+
+ /*
+ * We need to open the file with write access whilst
+ * still in our current user context. This ensures we
+ * are not violating security in doing the setxattr.
+ */
+
+ fsp = open_file_fchmod(conn,path,sbuf);
+ if (!fsp)
+ return ret;
+ become_root();
+ if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == 0) {
+ ret = True;
+ }
+ unbecome_root();
+ close_file_fchmod(fsp);
+ return ret;
+ }
+ DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr, path));
+ return True;
+}
+
+/****************************************************************************
+ Change a unix mode to a dos mode.
+****************************************************************************/
+
+uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
+{
+ uint32 result = 0;
+
+ DEBUG(8,("dos_mode: %s\n", path));
+
+ if (!VALID_STAT(*sbuf)) {
+ return 0;
+ }
+
+ /* Get the DOS attributes from an EA by preference. */
+ if (get_ea_dos_attribute(conn, path, sbuf, &result)) {
+ return result;
+ }
+
+ result = dos_mode_from_sbuf(conn, sbuf);
+
+ /* Now do any modifications that depend on the path name. */
+ /* hide files with a name starting with a . */
+ if (lp_hide_dot_files(SNUM(conn))) {
+ const char *p = strrchr_m(path,'/');
+ if (p)
+ p++;
+ else
+ p = path;
+
+ if (p[0] == '.' && p[1] != '.' && p[1] != 0)
+ result |= aHIDDEN;
+ }
+
+ /* Optimization : Only call is_hidden_path if it's not already
+ hidden. */
+ if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
+ result |= aHIDDEN;
+ }
+
+ DEBUG(8,("dos_mode returning "));
+
+ if (result & aHIDDEN) DEBUG(8, ("h"));
+ if (result & aRONLY ) DEBUG(8, ("r"));
+ if (result & aSYSTEM) DEBUG(8, ("s"));
+ if (result & aDIR ) DEBUG(8, ("d"));
+ if (result & aARCH ) DEBUG(8, ("a"));
+
+ DEBUG(8,("\n"));
+
+ return(result);
+}
+
+/*******************************************************************
+ chmod a file - but preserve some bits.
+********************************************************************/
+
+int file_set_dosmode(connection_struct *conn, const char *fname, uint32 dosmode, SMB_STRUCT_STAT *st)
+{
+ SMB_STRUCT_STAT st1;
+ int mask=0;
+ mode_t tmp;
+ mode_t unixmode;
+ int ret = -1;
+
+ DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname));
+ if (!st) {
+ st = &st1;
+ if (SMB_VFS_STAT(conn,fname,st))
+ return(-1);
+ }
+
+ get_acl_group_bits(conn, fname, &st->st_mode);
+
+ if (S_ISDIR(st->st_mode))
+ dosmode |= aDIR;
+ else
+ dosmode &= ~aDIR;
+
+ if (dos_mode(conn,fname,st) == dosmode)
+ return(0);
+
+ /* Store the DOS attributes in an EA by preference. */
+ if (set_ea_dos_attribute(conn, fname, st, dosmode)) {
+ return 0;
+ }
+
+ unixmode = unix_mode(conn,dosmode,fname);
+
+ /* preserve the s bits */
+ mask |= (S_ISUID | S_ISGID);
+
+ /* preserve the t bit */
+#ifdef S_ISVTX
+ mask |= S_ISVTX;
+#endif
+
+ /* possibly preserve the x bits */
+ if (!MAP_ARCHIVE(conn))
+ mask |= S_IXUSR;
+ if (!MAP_SYSTEM(conn))
+ mask |= S_IXGRP;
+ if (!MAP_HIDDEN(conn))
+ mask |= S_IXOTH;
+
+ unixmode |= (st->st_mode & mask);
+
+ /* if we previously had any r bits set then leave them alone */
+ if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
+ unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
+ unixmode |= tmp;
+ }
+
+ /* if we previously had any w bits set then leave them alone
+ whilst adding in the new w bits, if the new mode is not rdonly */
+ if (!IS_DOS_READONLY(dosmode)) {
+ unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
+ }
+
+ if ((ret = SMB_VFS_CHMOD(conn,fname,unixmode)) == 0)
+ return 0;
+
+ if((errno != EPERM) && (errno != EACCES))
+ return -1;
+
+ if(!lp_dos_filemode(SNUM(conn)))
+ return -1;
+
+ /* We want DOS semantics, ie allow non owner with write permission to change the
+ bits on a file. Just like file_utime below.
+ */
+
+ /* Check if we have write access. */
+ if (CAN_WRITE(conn)) {
+ /*
+ * We need to open the file with write access whilst
+ * still in our current user context. This ensures we
+ * are not violating security in doing the fchmod.
+ * This file open does *not* break any oplocks we are
+ * holding. We need to review this.... may need to
+ * break batch oplocks open by others. JRA.
+ */
+ files_struct *fsp = open_file_fchmod(conn,fname,st);
+ if (!fsp)
+ return -1;
+ become_root();
+ ret = SMB_VFS_FCHMOD(fsp, fsp->fd, unixmode);
+ unbecome_root();
+ close_file_fchmod(fsp);
+ }
+
+ return( ret );
+}
+
+/*******************************************************************
+ Wrapper around dos_utime that possibly allows DOS semantics rather
+ than POSIX.
+*******************************************************************/
+
+int file_utime(connection_struct *conn, char *fname, struct utimbuf *times)
+{
+ extern struct current_user current_user;
+ SMB_STRUCT_STAT sb;
+ int ret = -1;
+
+ errno = 0;
+
+ if(SMB_VFS_UTIME(conn,fname, times) == 0)
+ return 0;
+
+ if((errno != EPERM) && (errno != EACCES))
+ return -1;
+
+ if(!lp_dos_filetimes(SNUM(conn)))
+ return -1;
+
+ /* We have permission (given by the Samba admin) to
+ break POSIX semantics and allow a user to change
+ the time on a file they don't own but can write to
+ (as DOS does).
+ */
+
+ if(SMB_VFS_STAT(conn,fname,&sb) != 0)
+ return -1;
+
+ /* Check if we have write access. */
+ if (CAN_WRITE(conn)) {
+ if (((sb.st_mode & S_IWOTH) || conn->admin_user ||
+ ((sb.st_mode & S_IWUSR) && current_user.uid==sb.st_uid) ||
+ ((sb.st_mode & S_IWGRP) &&
+ in_group(sb.st_gid,current_user.gid,
+ current_user.ngroups,current_user.groups)))) {
+ /* We are allowed to become root and change the filetime. */
+ become_root();
+ ret = SMB_VFS_UTIME(conn,fname, times);
+ unbecome_root();
+ }
+ }
+
+ return ret;
+}
+
+/*******************************************************************
+ Change a filetime - possibly allowing DOS semantics.
+*******************************************************************/
+
+BOOL set_filetime(connection_struct *conn, char *fname, time_t mtime)
+{
+ struct utimbuf times;
+
+ if (null_mtime(mtime))
+ return(True);
+
+ times.modtime = times.actime = mtime;
+
+ if (file_utime(conn, fname, &times)) {
+ DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno)));
+ return False;
+ }
+
+ return(True);
+}
diff --git a/source/smbd/error.c b/source/smbd/error.c
new file mode 100644
index 00000000000..795bf0949cc
--- /dev/null
+++ b/source/smbd/error.c
@@ -0,0 +1,137 @@
+/*
+ Unix SMB/CIFS implementation.
+ error packet handling
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* these can be set by some functions to override the error codes */
+int unix_ERR_class=SMB_SUCCESS;
+int unix_ERR_code=0;
+NTSTATUS unix_ERR_ntstatus = NT_STATUS_OK;
+
+/* From lib/error.c */
+extern struct unix_error_map unix_dos_nt_errmap[];
+
+/****************************************************************************
+ Create an error packet from a cached error.
+****************************************************************************/
+
+int cached_error_packet(char *outbuf,files_struct *fsp,int line,const char *file)
+{
+ write_bmpx_struct *wbmpx = fsp->wbmpx_ptr;
+
+ int32 eclass = wbmpx->wr_errclass;
+ int32 err = wbmpx->wr_error;
+
+ /* We can now delete the auxiliary struct */
+ free((char *)wbmpx);
+ fsp->wbmpx_ptr = NULL;
+ return error_packet(outbuf,NT_STATUS_OK,eclass,err,line,file);
+}
+
+/****************************************************************************
+ Create an error packet from errno.
+****************************************************************************/
+
+int unix_error_packet(char *outbuf,int def_class,uint32 def_code,
+ int line, const char *file)
+{
+ int eclass=def_class;
+ int ecode=def_code;
+ NTSTATUS ntstatus = NT_STATUS_OK;
+ int i=0;
+
+ if (unix_ERR_class != SMB_SUCCESS) {
+ eclass = unix_ERR_class;
+ ecode = unix_ERR_code;
+ ntstatus = unix_ERR_ntstatus;
+ unix_ERR_class = SMB_SUCCESS;
+ unix_ERR_code = 0;
+ unix_ERR_ntstatus = NT_STATUS_OK;
+ } else {
+ while (unix_dos_nt_errmap[i].dos_class != 0) {
+ if (unix_dos_nt_errmap[i].unix_error == errno) {
+ eclass = unix_dos_nt_errmap[i].dos_class;
+ ecode = unix_dos_nt_errmap[i].dos_code;
+ ntstatus = unix_dos_nt_errmap[i].nt_error;
+ break;
+ }
+ i++;
+ }
+ }
+
+ return error_packet(outbuf,ntstatus,eclass,ecode,line,file);
+}
+
+
+/****************************************************************************
+ Create an error packet. Normally called using the ERROR() macro.
+****************************************************************************/
+
+int error_packet(char *outbuf,NTSTATUS ntstatus,
+ uint8 eclass,uint32 ecode,int line, const char *file)
+{
+ int outsize = set_message(outbuf,0,0,True);
+ extern uint32 global_client_caps;
+
+ if (errno != 0)
+ DEBUG(3,("error string = %s\n",strerror(errno)));
+
+#if defined(DEVELOPER)
+ if (unix_ERR_class != SMB_SUCCESS || unix_ERR_code != 0 || !NT_STATUS_IS_OK(unix_ERR_ntstatus))
+ smb_panic("logic error in error processing");
+#endif
+
+ /*
+ * We can explicitly force 32 bit error codes even when the
+ * parameter "nt status" is set to no by pre-setting the
+ * FLAGS2_32_BIT_ERROR_CODES bit in the smb_flg2 outbuf.
+ * This is to allow work arounds for client bugs that are needed
+ * when talking with clients that normally expect nt status codes. JRA.
+ */
+
+ if ((lp_nt_status_support() || (SVAL(outbuf,smb_flg2) & FLAGS2_32_BIT_ERROR_CODES)) && (global_client_caps & CAP_STATUS32)) {
+ if (NT_STATUS_V(ntstatus) == 0 && eclass)
+ ntstatus = dos_to_ntstatus(eclass, ecode);
+ SIVAL(outbuf,smb_rcls,NT_STATUS_V(ntstatus));
+ SSVAL(outbuf,smb_flg2, SVAL(outbuf,smb_flg2)|FLAGS2_32_BIT_ERROR_CODES);
+ DEBUG(3,("error packet at %s(%d) cmd=%d (%s) %s\n",
+ file, line,
+ (int)CVAL(outbuf,smb_com),
+ smb_fn_name(CVAL(outbuf,smb_com)),
+ nt_errstr(ntstatus)));
+ return outsize;
+ }
+
+ if (eclass == 0 && NT_STATUS_V(ntstatus))
+ ntstatus_to_dos(ntstatus, &eclass, &ecode);
+
+ SSVAL(outbuf,smb_flg2, SVAL(outbuf,smb_flg2)&~FLAGS2_32_BIT_ERROR_CODES);
+ SSVAL(outbuf,smb_rcls,eclass);
+ SSVAL(outbuf,smb_err,ecode);
+
+ DEBUG(3,("error packet at %s(%d) cmd=%d (%s) eclass=%d ecode=%d\n",
+ file, line,
+ (int)CVAL(outbuf,smb_com),
+ smb_fn_name(CVAL(outbuf,smb_com)),
+ eclass,
+ ecode));
+
+ return outsize;
+}
diff --git a/source/smbd/fake_file.c b/source/smbd/fake_file.c
new file mode 100644
index 00000000000..5ccb548ba5b
--- /dev/null
+++ b/source/smbd/fake_file.c
@@ -0,0 +1,166 @@
+/*
+ Unix SMB/CIFS implementation.
+ FAKE FILE suppport, for faking up special files windows want access to
+ Copyright (C) Stefan (metze) Metzmacher 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+ Open a file with a share mode.
+****************************************************************************/
+files_struct *open_fake_file_shared1(enum FAKE_FILE_TYPE fake_file_type, connection_struct *conn,char *fname,
+ SMB_STRUCT_STAT *psbuf,
+ uint32 desired_access,
+ int share_mode,int ofun, uint32 new_dos_attr, int oplock_request,
+ int *Access,int *action)
+{
+ extern struct current_user current_user;
+ int flags=0;
+ files_struct *fsp = NULL;
+
+ if (fake_file_type == 0) {
+ return open_file_shared1(conn,fname,psbuf,desired_access,
+ share_mode,ofun,new_dos_attr,
+ oplock_request,Access,action);
+ }
+
+ /* access check */
+ if (conn->admin_user != True) {
+ DEBUG(1,("access_denied to service[%s] file[%s] user[%s]\n",
+ lp_servicename(SNUM(conn)),fname,conn->user));
+ errno = EACCES;
+ return NULL;
+ }
+
+ fsp = file_new(conn);
+ if(!fsp)
+ return NULL;
+
+ DEBUG(5,("open_fake_file_shared1: fname = %s, FID = %d, share_mode = %x, ofun = %x, oplock request = %d\n",
+ fname, fsp->fnum, share_mode, ofun, oplock_request ));
+
+ if (!check_name(fname,conn)) {
+ file_free(fsp);
+ return NULL;
+ }
+
+ fsp->fd = -1;
+ fsp->mode = psbuf->st_mode;
+ fsp->inode = psbuf->st_ino;
+ fsp->dev = psbuf->st_dev;
+ fsp->vuid = current_user.vuid;
+ fsp->size = psbuf->st_size;
+ fsp->pos = -1;
+ fsp->can_lock = True;
+ fsp->can_read = ((flags & O_WRONLY)==0);
+ fsp->can_write = ((flags & (O_WRONLY|O_RDWR))!=0);
+ fsp->share_mode = 0;
+ fsp->desired_access = desired_access;
+ fsp->print_file = False;
+ fsp->modified = False;
+ fsp->oplock_type = NO_OPLOCK;
+ fsp->sent_oplock_break = NO_BREAK_SENT;
+ fsp->is_directory = False;
+ fsp->is_stat = False;
+ fsp->directory_delete_on_close = False;
+ fsp->conn = conn;
+ string_set(&fsp->fsp_name,fname);
+ fsp->wcp = NULL; /* Write cache pointer. */
+
+ fsp->fake_file_handle = init_fake_file_handle(fake_file_type);
+
+ if (fsp->fake_file_handle==NULL) {
+ file_free(fsp);
+ return NULL;
+ }
+
+ conn->num_files_open++;
+ return fsp;
+}
+
+static FAKE_FILE fake_files[] = {
+#ifdef WITH_QUOTAS
+ {FAKE_FILE_NAME_QUOTA, FAKE_FILE_TYPE_QUOTA, init_quota_handle, destroy_quota_handle},
+#endif /* WITH_QUOTAS */
+ {NULL, FAKE_FILE_TYPE_NONE, NULL, NULL }
+};
+
+int is_fake_file(char *fname)
+{
+ int i;
+
+ if (!fname)
+ return 0;
+
+ for (i=0;fake_files[i].name!=NULL;i++) {
+ if (strncmp(fname,fake_files[i].name,strlen(fake_files[i].name))==0) {
+ DEBUG(5,("is_fake_file: [%s] is a fake file\n",fname));
+ return fake_files[i].type;
+ }
+ }
+
+ return FAKE_FILE_TYPE_NONE;
+}
+
+struct _FAKE_FILE_HANDLE *init_fake_file_handle(enum FAKE_FILE_TYPE type)
+{
+ TALLOC_CTX *mem_ctx = NULL;
+ FAKE_FILE_HANDLE *fh = NULL;
+ int i;
+
+ for (i=0;fake_files[i].name!=NULL;i++) {
+ if (fake_files[i].type==type) {
+ DEBUG(5,("init_fake_file_handle: for [%s]\n",fake_files[i].name));
+
+ if ((mem_ctx=talloc_init("fake_file_handle"))==NULL) {
+ DEBUG(0,("talloc_init(fake_file_handle) failed.\n"));
+ return NULL;
+ }
+
+ if ((fh =(FAKE_FILE_HANDLE *)talloc_zero(mem_ctx, sizeof(FAKE_FILE_HANDLE)))==NULL) {
+ DEBUG(0,("talloc_zero() failed.\n"));
+ talloc_destroy(mem_ctx);
+ return NULL;
+ }
+
+ fh->type = type;
+ fh->mem_ctx = mem_ctx;
+
+ if (fake_files[i].init_pd)
+ fh->pd = fake_files[i].init_pd(fh->mem_ctx);
+
+ fh->free_pd = fake_files[i].free_pd;
+
+ return fh;
+ }
+ }
+
+ return NULL;
+}
+
+void destroy_fake_file_handle(FAKE_FILE_HANDLE **fh)
+{
+ if (!fh||!(*fh))
+ return ;
+
+ if ((*fh)->free_pd)
+ (*fh)->free_pd(&(*fh)->pd);
+
+ talloc_destroy((*fh)->mem_ctx);
+ (*fh) = NULL;
+}
diff --git a/source/smbd/fileio.c b/source/smbd/fileio.c
new file mode 100644
index 00000000000..c2fb6e34566
--- /dev/null
+++ b/source/smbd/fileio.c
@@ -0,0 +1,773 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ read/write to a files_struct
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Jeremy Allison 2000-2002. - write cache.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static BOOL setup_write_cache(files_struct *, SMB_OFF_T);
+
+/****************************************************************************
+ Read from write cache if we can.
+****************************************************************************/
+
+
+static BOOL read_from_write_cache(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n)
+{
+ write_cache *wcp = fsp->wcp;
+
+ if(!wcp)
+ return False;
+
+ if(n > wcp->data_size || pos < wcp->offset || pos + n > wcp->offset + wcp->data_size)
+ return False;
+
+ memcpy(data, wcp->data + (pos - wcp->offset), n);
+
+ DO_PROFILE_INC(writecache_read_hits);
+
+ return True;
+}
+
+/****************************************************************************
+ Read from a file.
+****************************************************************************/
+
+ssize_t read_file(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n)
+{
+ ssize_t ret=0,readret;
+
+ /* you can't read from print files */
+ if (fsp->print_file)
+ return -1;
+
+ /*
+ * Serve from write cache if we can.
+ */
+
+ if(read_from_write_cache(fsp, data, pos, n)) {
+ fsp->pos = pos + n;
+ fsp->position_information = fsp->pos;
+ return n;
+ }
+
+ flush_write_cache(fsp, READ_FLUSH);
+
+ fsp->pos = pos;
+
+ if (n > 0) {
+#ifdef DMF_FIX
+ int numretries = 3;
+tryagain:
+ readret = SMB_VFS_PREAD(fsp,fsp->fd,data,n,pos);
+
+ if (readret == -1) {
+ if ((errno == EAGAIN) && numretries) {
+ DEBUG(3,("read_file EAGAIN retry in 10 seconds\n"));
+ (void)sleep(10);
+ --numretries;
+ goto tryagain;
+ }
+ return -1;
+ }
+#else /* NO DMF fix. */
+ readret = SMB_VFS_PREAD(fsp,fsp->fd,data,n,pos);
+
+ if (readret == -1)
+ return -1;
+#endif
+ if (readret > 0)
+ ret += readret;
+ }
+
+ DEBUG(10,("read_file (%s): pos = %.0f, size = %lu, returned %lu\n",
+ fsp->fsp_name, (double)pos, (unsigned long)n, (long)ret ));
+
+ fsp->pos += ret;
+ fsp->position_information = fsp->pos;
+
+ return(ret);
+}
+
+/* how many write cache buffers have been allocated */
+static unsigned int allocated_write_caches;
+
+/****************************************************************************
+ *Really* write to a file.
+****************************************************************************/
+
+static ssize_t real_write_file(files_struct *fsp,char *data,SMB_OFF_T pos, size_t n)
+{
+ ssize_t ret;
+
+ if (pos == -1)
+ ret = vfs_write_data(fsp, data, n);
+ else {
+ fsp->pos = pos;
+ ret = vfs_pwrite_data(fsp, data, n, pos);
+ }
+
+ DEBUG(10,("real_write_file (%s): pos = %.0f, size = %lu, returned %ld\n",
+ fsp->fsp_name, (double)pos, (unsigned long)n, (long)ret ));
+
+ if (ret != -1) {
+ fsp->pos += ret;
+
+/* Yes - this is correct - writes don't update this. JRA. */
+/* Found by Samba4 tests. */
+#if 0
+ fsp->position_information = fsp->pos;
+#endif
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+write to a file
+****************************************************************************/
+
+ssize_t write_file(files_struct *fsp, char *data, SMB_OFF_T pos, size_t n)
+{
+ write_cache *wcp = fsp->wcp;
+ ssize_t total_written = 0;
+ int write_path = -1;
+
+ if (fsp->print_file) {
+ int snum;
+ uint32 jobid;
+
+ if (!rap_to_pjobid(fsp->rap_print_jobid, &snum, &jobid)) {
+ DEBUG(3,("write_file: Unable to map RAP jobid %u to jobid.\n",
+ (unsigned int)fsp->rap_print_jobid ));
+ errno = EBADF;
+ return -1;
+ }
+
+ return print_job_write(SNUM(fsp->conn), jobid, data, n);
+ }
+
+ if (!fsp->can_write) {
+ errno = EPERM;
+ return(0);
+ }
+
+ if (!fsp->modified) {
+ SMB_STRUCT_STAT st;
+ fsp->modified = True;
+
+ if (SMB_VFS_FSTAT(fsp,fsp->fd,&st) == 0) {
+ int dosmode = dos_mode(fsp->conn,fsp->fsp_name,&st);
+ fsp->size = (SMB_BIG_UINT)st.st_size;
+ if ((lp_store_dos_attributes(SNUM(fsp->conn)) || MAP_ARCHIVE(fsp->conn)) && !IS_DOS_ARCHIVE(dosmode)) {
+ file_set_dosmode(fsp->conn,fsp->fsp_name,dosmode | aARCH,&st);
+ }
+
+ /*
+ * If this is the first write and we have an exclusive oplock then setup
+ * the write cache.
+ */
+
+ if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && !wcp) {
+ setup_write_cache(fsp, st.st_size);
+ wcp = fsp->wcp;
+ }
+ }
+ }
+
+#ifdef WITH_PROFILE
+ DO_PROFILE_INC(writecache_total_writes);
+ if (!fsp->oplock_type) {
+ DO_PROFILE_INC(writecache_non_oplock_writes);
+ }
+#endif
+
+ /*
+ * If this file is level II oplocked then we need
+ * to grab the shared memory lock and inform all
+ * other files with a level II lock that they need
+ * to flush their read caches. We keep the lock over
+ * the shared memory area whilst doing this.
+ */
+
+ release_level_2_oplocks_on_change(fsp);
+
+#ifdef WITH_PROFILE
+ if (profile_p && profile_p->writecache_total_writes % 500 == 0) {
+ DEBUG(3,("WRITECACHE: initwrites=%u abutted=%u total=%u \
+nonop=%u allocated=%u active=%u direct=%u perfect=%u readhits=%u\n",
+ profile_p->writecache_init_writes,
+ profile_p->writecache_abutted_writes,
+ profile_p->writecache_total_writes,
+ profile_p->writecache_non_oplock_writes,
+ profile_p->writecache_allocated_write_caches,
+ profile_p->writecache_num_write_caches,
+ profile_p->writecache_direct_writes,
+ profile_p->writecache_num_perfect_writes,
+ profile_p->writecache_read_hits ));
+
+ DEBUG(3,("WRITECACHE: Flushes SEEK=%d, READ=%d, WRITE=%d, READRAW=%d, OPLOCK=%d, CLOSE=%d, SYNC=%d\n",
+ profile_p->writecache_flushed_writes[SEEK_FLUSH],
+ profile_p->writecache_flushed_writes[READ_FLUSH],
+ profile_p->writecache_flushed_writes[WRITE_FLUSH],
+ profile_p->writecache_flushed_writes[READRAW_FLUSH],
+ profile_p->writecache_flushed_writes[OPLOCK_RELEASE_FLUSH],
+ profile_p->writecache_flushed_writes[CLOSE_FLUSH],
+ profile_p->writecache_flushed_writes[SYNC_FLUSH] ));
+ }
+#endif
+
+ if(!wcp) {
+ DO_PROFILE_INC(writecache_direct_writes);
+ total_written = real_write_file(fsp, data, pos, n);
+ if ((total_written != -1) && (pos + total_written > (SMB_OFF_T)fsp->size))
+ fsp->size = (SMB_BIG_UINT)(pos + total_written);
+ return total_written;
+ }
+
+ DEBUG(9,("write_file (%s)(fd=%d pos=%.0f size=%u) wcp->offset=%.0f wcp->data_size=%u\n",
+ fsp->fsp_name, fsp->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size));
+
+ fsp->pos = pos + n;
+
+ /*
+ * If we have active cache and it isn't contiguous then we flush.
+ * NOTE: There is a small problem with running out of disk ....
+ */
+
+ if (wcp->data_size) {
+
+ BOOL cache_flush_needed = False;
+
+ if ((pos >= wcp->offset) && (pos <= wcp->offset + wcp->data_size)) {
+
+ /* ASCII art.... JRA.
+
+ +--------------+-----
+ | Cached data | Rest of allocated cache buffer....
+ +--------------+-----
+
+ +-------------------+
+ | Data to write |
+ +-------------------+
+
+ */
+
+ /*
+ * Start of write overlaps or abutts the existing data.
+ */
+
+ size_t data_used = MIN((wcp->alloc_size - (pos - wcp->offset)), n);
+
+ memcpy(wcp->data + (pos - wcp->offset), data, data_used);
+
+ /*
+ * Update the current buffer size with the new data.
+ */
+
+ if(pos + data_used > wcp->offset + wcp->data_size)
+ wcp->data_size = pos + data_used - wcp->offset;
+
+ /*
+ * Update the file size if changed.
+ */
+
+ if (wcp->offset + wcp->data_size > wcp->file_size) {
+ wcp->file_size = wcp->offset + wcp->data_size;
+ fsp->size = (SMB_BIG_UINT)wcp->file_size;
+ }
+
+ /*
+ * If we used all the data then
+ * return here.
+ */
+
+ if(n == data_used)
+ return n;
+ else
+ cache_flush_needed = True;
+
+ /*
+ * Move the start of data forward by the amount used,
+ * cut down the amount left by the same amount.
+ */
+
+ data += data_used;
+ pos += data_used;
+ n -= data_used;
+
+ DO_PROFILE_INC(writecache_abutted_writes);
+ total_written = data_used;
+
+ write_path = 1;
+
+ } else if ((pos < wcp->offset) && (pos + n > wcp->offset) &&
+ (pos + n <= wcp->offset + wcp->alloc_size)) {
+
+ /* ASCII art.... JRA.
+
+ +---------------+
+ | Cache buffer |
+ +---------------+
+
+ +-------------------+
+ | Data to write |
+ +-------------------+
+
+ */
+
+ /*
+ * End of write overlaps the existing data.
+ */
+
+ size_t data_used = pos + n - wcp->offset;
+
+ memcpy(wcp->data, data + n - data_used, data_used);
+
+ /*
+ * Update the current buffer size with the new data.
+ */
+
+ if(pos + n > wcp->offset + wcp->data_size)
+ wcp->data_size = pos + n - wcp->offset;
+
+ /*
+ * Update the file size if changed.
+ */
+
+ if (wcp->offset + wcp->data_size > wcp->file_size) {
+ wcp->file_size = wcp->offset + wcp->data_size;
+ fsp->size = (SMB_BIG_UINT)wcp->file_size;
+ }
+
+ /*
+ * We don't need to move the start of data, but we
+ * cut down the amount left by the amount used.
+ */
+
+ n -= data_used;
+
+ /*
+ * We cannot have used all the data here.
+ */
+
+ cache_flush_needed = True;
+
+ DO_PROFILE_INC(writecache_abutted_writes);
+ total_written = data_used;
+
+ write_path = 2;
+
+ } else if ( (pos >= wcp->file_size) &&
+ (wcp->offset + wcp->data_size == wcp->file_size) &&
+ (pos > wcp->offset + wcp->data_size) &&
+ (pos < wcp->offset + wcp->alloc_size) ) {
+
+ /* ASCII art.... JRA.
+
+ End of file ---->|
+
+ +---------------+---------------+
+ | Cached data | Cache buffer |
+ +---------------+---------------+
+
+ +-------------------+
+ | Data to write |
+ +-------------------+
+
+ */
+
+ /*
+ * Non-contiguous write part of which fits within
+ * the cache buffer and is extending the file
+ * and the cache contents reflect the current
+ * data up to the current end of the file.
+ */
+
+ size_t data_used;
+
+ if(pos + n <= wcp->offset + wcp->alloc_size)
+ data_used = n;
+ else
+ data_used = wcp->offset + wcp->alloc_size - pos;
+
+ /*
+ * Fill in the non-continuous area with zeros.
+ */
+
+ memset(wcp->data + wcp->data_size, '\0',
+ pos - (wcp->offset + wcp->data_size) );
+
+ memcpy(wcp->data + (pos - wcp->offset), data, data_used);
+
+ /*
+ * Update the current buffer size with the new data.
+ */
+
+ if(pos + data_used > wcp->offset + wcp->data_size)
+ wcp->data_size = pos + data_used - wcp->offset;
+
+ /*
+ * Update the file size if changed.
+ */
+
+ if (wcp->offset + wcp->data_size > wcp->file_size) {
+ wcp->file_size = wcp->offset + wcp->data_size;
+ fsp->size = (SMB_BIG_UINT)wcp->file_size;
+ }
+
+ /*
+ * If we used all the data then
+ * return here.
+ */
+
+ if(n == data_used)
+ return n;
+ else
+ cache_flush_needed = True;
+
+ /*
+ * Move the start of data forward by the amount used,
+ * cut down the amount left by the same amount.
+ */
+
+ data += data_used;
+ pos += data_used;
+ n -= data_used;
+
+ DO_PROFILE_INC(writecache_abutted_writes);
+ total_written = data_used;
+
+ write_path = 3;
+
+ } else {
+
+ /* ASCII art..... JRA.
+
+ Case 1).
+
+ +---------------+---------------+
+ | Cached data | Cache buffer |
+ +---------------+---------------+
+
+ +-------------------+
+ | Data to write |
+ +-------------------+
+
+ Case 2).
+
+ +---------------+---------------+
+ | Cached data | Cache buffer |
+ +---------------+---------------+
+
+ +-------------------+
+ | Data to write |
+ +-------------------+
+
+ Case 3).
+
+ +---------------+---------------+
+ | Cached data | Cache buffer |
+ +---------------+---------------+
+
+ +-----------------------------------------------------+
+ | Data to write |
+ +-----------------------------------------------------+
+
+ */
+
+ /*
+ * Write is bigger than buffer, or there is no overlap on the
+ * low or high ends.
+ */
+
+ DEBUG(9,("write_file: non cacheable write : fd = %d, pos = %.0f, len = %u, current cache pos = %.0f \
+len = %u\n",fsp->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size ));
+
+ /*
+ * Update the file size if needed.
+ */
+
+ if(pos + n > wcp->file_size) {
+ wcp->file_size = pos + n;
+ fsp->size = (SMB_BIG_UINT)wcp->file_size;
+ }
+
+ /*
+ * If write would fit in the cache, and is larger than
+ * the data already in the cache, flush the cache and
+ * preferentially copy the data new data into it. Otherwise
+ * just write the data directly.
+ */
+
+ if ( n <= wcp->alloc_size && n > wcp->data_size) {
+ cache_flush_needed = True;
+ } else {
+ ssize_t ret = real_write_file(fsp, data, pos, n);
+
+ /*
+ * If the write overlaps the entire cache, then
+ * discard the current contents of the cache.
+ * Fix from Rasmus Borup Hansen rbh@math.ku.dk.
+ */
+
+ if ((pos <= wcp->offset) &&
+ (pos + n >= wcp->offset + wcp->data_size) ) {
+ DEBUG(9,("write_file: discarding overwritten write \
+cache: fd = %d, off=%.0f, size=%u\n", fsp->fd, (double)wcp->offset, (unsigned int)wcp->data_size ));
+ wcp->data_size = 0;
+ }
+
+ DO_PROFILE_INC(writecache_direct_writes);
+ if (ret == -1)
+ return ret;
+
+ if (pos + ret > wcp->file_size) {
+ wcp->file_size = pos + ret;
+ fsp->size = (SMB_BIG_UINT)wcp->file_size;
+ }
+
+ return ret;
+ }
+
+ write_path = 4;
+
+ }
+
+ if(wcp->data_size > wcp->file_size) {
+ wcp->file_size = wcp->data_size;
+ fsp->size = (SMB_BIG_UINT)wcp->file_size;
+ }
+
+ if (cache_flush_needed) {
+ DEBUG(3,("WRITE_FLUSH:%d: due to noncontinuous write: fd = %d, size = %.0f, pos = %.0f, \
+n = %u, wcp->offset=%.0f, wcp->data_size=%u\n",
+ write_path, fsp->fd, (double)wcp->file_size, (double)pos, (unsigned int)n,
+ (double)wcp->offset, (unsigned int)wcp->data_size ));
+
+ flush_write_cache(fsp, WRITE_FLUSH);
+ }
+ }
+
+ /*
+ * If the write request is bigger than the cache
+ * size, write it all out.
+ */
+
+ if (n > wcp->alloc_size ) {
+ ssize_t ret = real_write_file(fsp, data, pos, n);
+ if (ret == -1)
+ return -1;
+
+ if (pos + ret > wcp->file_size) {
+ wcp->file_size = pos + n;
+ fsp->size = (SMB_BIG_UINT)wcp->file_size;
+ }
+
+ DO_PROFILE_INC(writecache_direct_writes);
+ return total_written + n;
+ }
+
+ /*
+ * If there's any data left, cache it.
+ */
+
+ if (n) {
+#ifdef WITH_PROFILE
+ if (wcp->data_size) {
+ DO_PROFILE_INC(writecache_abutted_writes);
+ } else {
+ DO_PROFILE_INC(writecache_init_writes);
+ }
+#endif
+ memcpy(wcp->data+wcp->data_size, data, n);
+ if (wcp->data_size == 0) {
+ wcp->offset = pos;
+ DO_PROFILE_INC(writecache_num_write_caches);
+ }
+ wcp->data_size += n;
+
+ /*
+ * Update the file size if changed.
+ */
+
+ if (wcp->offset + wcp->data_size > wcp->file_size) {
+ wcp->file_size = wcp->offset + wcp->data_size;
+ fsp->size = (SMB_BIG_UINT)wcp->file_size;
+ }
+ DEBUG(9,("wcp->offset = %.0f wcp->data_size = %u cache return %u\n",
+ (double)wcp->offset, (unsigned int)wcp->data_size, (unsigned int)n));
+
+ total_written += n;
+ return total_written; /* .... that's a write :) */
+ }
+
+ return total_written;
+}
+
+/****************************************************************************
+ Delete the write cache structure.
+****************************************************************************/
+
+void delete_write_cache(files_struct *fsp)
+{
+ write_cache *wcp;
+
+ if(!fsp)
+ return;
+
+ if(!(wcp = fsp->wcp))
+ return;
+
+ DO_PROFILE_DEC(writecache_allocated_write_caches);
+ allocated_write_caches--;
+
+ SMB_ASSERT(wcp->data_size == 0);
+
+ SAFE_FREE(wcp->data);
+ SAFE_FREE(fsp->wcp);
+
+ DEBUG(10,("delete_write_cache: File %s deleted write cache\n", fsp->fsp_name ));
+}
+
+/****************************************************************************
+ Setup the write cache structure.
+****************************************************************************/
+
+static BOOL setup_write_cache(files_struct *fsp, SMB_OFF_T file_size)
+{
+ ssize_t alloc_size = lp_write_cache_size(SNUM(fsp->conn));
+ write_cache *wcp;
+
+ if (allocated_write_caches >= MAX_WRITE_CACHES)
+ return False;
+
+ if(alloc_size == 0 || fsp->wcp)
+ return False;
+
+ if((wcp = (write_cache *)malloc(sizeof(write_cache))) == NULL) {
+ DEBUG(0,("setup_write_cache: malloc fail.\n"));
+ return False;
+ }
+
+ wcp->file_size = file_size;
+ wcp->offset = 0;
+ wcp->alloc_size = alloc_size;
+ wcp->data_size = 0;
+ if((wcp->data = malloc(wcp->alloc_size)) == NULL) {
+ DEBUG(0,("setup_write_cache: malloc fail for buffer size %u.\n",
+ (unsigned int)wcp->alloc_size ));
+ SAFE_FREE(wcp);
+ return False;
+ }
+
+ memset(wcp->data, '\0', wcp->alloc_size );
+
+ fsp->wcp = wcp;
+ DO_PROFILE_INC(writecache_allocated_write_caches);
+ allocated_write_caches++;
+
+ DEBUG(10,("setup_write_cache: File %s allocated write cache size %lu\n",
+ fsp->fsp_name, (unsigned long)wcp->alloc_size ));
+
+ return True;
+}
+
+/****************************************************************************
+ Cope with a size change.
+****************************************************************************/
+
+void set_filelen_write_cache(files_struct *fsp, SMB_OFF_T file_size)
+{
+ fsp->size = (SMB_BIG_UINT)file_size;
+ if(fsp->wcp) {
+ /* The cache *must* have been flushed before we do this. */
+ if (fsp->wcp->data_size != 0) {
+ pstring msg;
+ slprintf(msg, sizeof(msg)-1, "set_filelen_write_cache: size change \
+on file %s with write cache size = %lu\n", fsp->fsp_name, (unsigned long)fsp->wcp->data_size );
+ smb_panic(msg);
+ }
+ fsp->wcp->file_size = file_size;
+ }
+}
+
+/*******************************************************************
+ Flush a write cache struct to disk.
+********************************************************************/
+
+ssize_t flush_write_cache(files_struct *fsp, enum flush_reason_enum reason)
+{
+ write_cache *wcp = fsp->wcp;
+ size_t data_size;
+ ssize_t ret;
+
+ if(!wcp || !wcp->data_size)
+ return 0;
+
+ data_size = wcp->data_size;
+ wcp->data_size = 0;
+
+ DO_PROFILE_DEC_INC(writecache_num_write_caches,writecache_flushed_writes[reason]);
+
+ DEBUG(9,("flushing write cache: fd = %d, off=%.0f, size=%u\n",
+ fsp->fd, (double)wcp->offset, (unsigned int)data_size));
+
+#ifdef WITH_PROFILE
+ if(data_size == wcp->alloc_size)
+ DO_PROFILE_INC(writecache_num_perfect_writes);
+#endif
+
+ ret = real_write_file(fsp, wcp->data, wcp->offset, data_size);
+
+ /*
+ * Ensure file size if kept up to date if write extends file.
+ */
+
+ if ((ret != -1) && (wcp->offset + ret > wcp->file_size))
+ wcp->file_size = wcp->offset + ret;
+
+ return ret;
+}
+
+/*******************************************************************
+sync a file
+********************************************************************/
+
+void sync_file(connection_struct *conn, files_struct *fsp)
+{
+ if(lp_strict_sync(SNUM(conn)) && fsp->fd != -1) {
+ flush_write_cache(fsp, SYNC_FLUSH);
+ SMB_VFS_FSYNC(fsp,fsp->fd);
+ }
+}
+
+
+/************************************************************
+ Perform a stat whether a valid fd or not.
+************************************************************/
+
+int fsp_stat(files_struct *fsp, SMB_STRUCT_STAT *pst)
+{
+ if (fsp->fd == -1)
+ return SMB_VFS_STAT(fsp->conn, fsp->fsp_name, pst);
+ else
+ return SMB_VFS_FSTAT(fsp,fsp->fd, pst);
+}
diff --git a/source/smbd/filename.c b/source/smbd/filename.c
new file mode 100644
index 00000000000..805af9c494a
--- /dev/null
+++ b/source/smbd/filename.c
@@ -0,0 +1,497 @@
+/*
+ Unix SMB/CIFS implementation.
+ filename handling routines
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Jeremy Allison 1999-2004
+ Copyright (C) Ying Chen 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ * New hash table stat cache code added by Ying Chen.
+ */
+
+#include "includes.h"
+
+extern BOOL case_sensitive;
+extern BOOL case_preserve;
+extern BOOL short_case_preserve;
+extern BOOL use_mangled_map;
+
+static BOOL scan_directory(const char *path, char *name,size_t maxlength,
+ connection_struct *conn,BOOL docache);
+
+/****************************************************************************
+ Check if two filenames are equal.
+ This needs to be careful about whether we are case sensitive.
+****************************************************************************/
+
+static BOOL fname_equal(const char *name1, const char *name2)
+{
+ /* Normal filename handling */
+ if (case_sensitive)
+ return(strcmp(name1,name2) == 0);
+
+ return(strequal(name1,name2));
+}
+
+/****************************************************************************
+ Mangle the 2nd name and check if it is then equal to the first name.
+****************************************************************************/
+
+static BOOL mangled_equal(const char *name1, const char *name2, int snum)
+{
+ pstring tmpname;
+
+ pstrcpy(tmpname, name2);
+ mangle_map(tmpname, True, False, snum);
+ return strequal(name1, tmpname);
+}
+
+/****************************************************************************
+This routine is called to convert names from the dos namespace to unix
+namespace. It needs to handle any case conversions, mangling, format
+changes etc.
+
+We assume that we have already done a chdir() to the right "root" directory
+for this service.
+
+The function will return False if some part of the name except for the last
+part cannot be resolved
+
+If the saved_last_component != 0, then the unmodified last component
+of the pathname is returned there. This is used in an exceptional
+case in reply_mv (so far). If saved_last_component == 0 then nothing
+is returned there.
+
+The bad_path arg is set to True if the filename walk failed. This is
+used to pick the correct error code to return between ENOENT and ENOTDIR
+as Windows applications depend on ERRbadpath being returned if a component
+of a pathname does not exist.
+
+On exit from unix_convert, if *pst was not null, then the file stat
+struct will be returned if the file exists and was found, if not this
+stat struct will be filled with zeros (and this can be detected by checking
+for nlinks = 0, which can never be true for any file).
+****************************************************************************/
+
+BOOL unix_convert(pstring name,connection_struct *conn,char *saved_last_component,
+ BOOL *bad_path, SMB_STRUCT_STAT *pst)
+{
+ SMB_STRUCT_STAT st;
+ char *start, *end;
+ pstring dirpath;
+ pstring orig_path;
+ BOOL component_was_mangled = False;
+ BOOL name_has_wildcard = False;
+
+ ZERO_STRUCTP(pst);
+
+ *dirpath = 0;
+ *bad_path = False;
+ if(saved_last_component)
+ *saved_last_component = 0;
+
+ if (conn->printer) {
+ /* we don't ever use the filenames on a printer share as a
+ filename - so don't convert them */
+ return True;
+ }
+
+ DEBUG(5, ("unix_convert called on file \"%s\"\n", name));
+
+ /*
+ * Conversion to basic unix format is already done in check_path_syntax().
+ */
+
+ /*
+ * Names must be relative to the root of the service - any leading /.
+ * and trailing /'s should have been trimmed by check_path_syntax().
+ */
+
+#ifdef DEVELOPER
+ SMB_ASSERT(*name != '/');
+#endif
+
+ /*
+ * If we trimmed down to a single '\0' character
+ * then we should use the "." directory to avoid
+ * searching the cache, but not if we are in a
+ * printing share.
+ * As we know this is valid we can return true here.
+ */
+
+ if (!*name) {
+ name[0] = '.';
+ name[1] = '\0';
+ return(True);
+ }
+
+ /*
+ * Ensure saved_last_component is valid even if file exists.
+ */
+
+ if(saved_last_component) {
+ end = strrchr_m(name, '/');
+ if(end)
+ pstrcpy(saved_last_component, end + 1);
+ else
+ pstrcpy(saved_last_component, name);
+ }
+
+ if (!case_sensitive && (!case_preserve || (mangle_is_8_3(name, False) && !short_case_preserve)))
+ strnorm(name);
+
+ start = name;
+ pstrcpy(orig_path, name);
+
+ if(!case_sensitive && stat_cache_lookup(conn, name, dirpath, &start, &st)) {
+ *pst = st;
+ return True;
+ }
+
+ /*
+ * stat the name - if it exists then we are all done!
+ */
+
+ if (SMB_VFS_STAT(conn,name,&st) == 0) {
+ stat_cache_add(orig_path, name);
+ DEBUG(5,("conversion finished %s -> %s\n",orig_path, name));
+ *pst = st;
+ return(True);
+ }
+
+ DEBUG(5,("unix_convert begin: name = %s, dirpath = %s, start = %s\n", name, dirpath, start));
+
+ /*
+ * A special case - if we don't have any mangling chars and are case
+ * sensitive then searching won't help.
+ */
+
+ if (case_sensitive && !mangle_is_mangled(name) && !use_mangled_map)
+ return(False);
+
+ name_has_wildcard = ms_has_wild(start);
+
+ /*
+ * is_mangled() was changed to look at an entire pathname, not
+ * just a component. JRA.
+ */
+
+ if (mangle_is_mangled(start))
+ component_was_mangled = True;
+
+ /*
+ * Now we need to recursively match the name against the real
+ * directory structure.
+ */
+
+ /*
+ * Match each part of the path name separately, trying the names
+ * as is first, then trying to scan the directory for matching names.
+ */
+
+ for (; start ; start = (end?end+1:(char *)NULL)) {
+ /*
+ * Pinpoint the end of this section of the filename.
+ */
+ end = strchr_m(start, '/');
+
+ /*
+ * Chop the name at this point.
+ */
+ if (end)
+ *end = 0;
+
+ if(saved_last_component != 0)
+ pstrcpy(saved_last_component, end ? end + 1 : start);
+
+ /*
+ * Check if the name exists up to this point.
+ */
+
+ if (SMB_VFS_STAT(conn,name, &st) == 0) {
+ /*
+ * It exists. it must either be a directory or this must be
+ * the last part of the path for it to be OK.
+ */
+ if (end && !(st.st_mode & S_IFDIR)) {
+ /*
+ * An intermediate part of the name isn't a directory.
+ */
+ DEBUG(5,("Not a dir %s\n",start));
+ *end = '/';
+ return(False);
+ }
+
+ if (!end) {
+ /*
+ * We just scanned for, and found the end of the path.
+ * We must return the valid stat struct.
+ * JRA.
+ */
+
+ *pst = st;
+ }
+
+ } else {
+ pstring rest;
+
+ /* Stat failed - ensure we don't use it. */
+ ZERO_STRUCT(st);
+ *rest = 0;
+
+ /*
+ * Remember the rest of the pathname so it can be restored
+ * later.
+ */
+
+ if (end)
+ pstrcpy(rest,end+1);
+
+ /*
+ * Try to find this part of the path in the directory.
+ */
+
+ if (ms_has_wild(start) ||
+ !scan_directory(dirpath, start,
+ sizeof(pstring) - 1 - (start - name),
+ conn,
+ end?True:False)) {
+ if (end) {
+ /*
+ * An intermediate part of the name can't be found.
+ */
+ DEBUG(5,("Intermediate not found %s\n",start));
+ *end = '/';
+
+ /*
+ * We need to return the fact that the intermediate
+ * name resolution failed. This is used to return an
+ * error of ERRbadpath rather than ERRbadfile. Some
+ * Windows applications depend on the difference between
+ * these two errors.
+ */
+ *bad_path = True;
+ return(False);
+ }
+
+ /*
+ * Just the last part of the name doesn't exist.
+ * We may need to strupper() or strlower() it in case
+ * this conversion is being used for file creation
+ * purposes. If the filename is of mixed case then
+ * don't normalise it.
+ */
+
+ if (!case_preserve && (!strhasupper(start) || !strhaslower(start)))
+ strnorm(start);
+
+ /*
+ * check on the mangled stack to see if we can recover the
+ * base of the filename.
+ */
+
+ if (mangle_is_mangled(start)) {
+ mangle_check_cache( start );
+ }
+
+ DEBUG(5,("New file %s\n",start));
+ return(True);
+ }
+
+ /*
+ * Restore the rest of the string. If the string was mangled the size
+ * may have changed.
+ */
+ if (end) {
+ end = start + strlen(start);
+ if (!safe_strcat(start, "/", sizeof(pstring) - 1 - (start - name)) ||
+ !safe_strcat(start, rest, sizeof(pstring) - 1 - (start - name))) {
+ return False;
+ }
+ *end = '\0';
+ } else {
+ /*
+ * We just scanned for, and found the end of the path.
+ * We must return a valid stat struct if it exists.
+ * JRA.
+ */
+
+ if (SMB_VFS_STAT(conn,name, &st) == 0) {
+ *pst = st;
+ } else {
+ ZERO_STRUCT(st);
+ }
+ }
+ } /* end else */
+
+ /*
+ * Add to the dirpath that we have resolved so far.
+ */
+ if (*dirpath)
+ pstrcat(dirpath,"/");
+
+ pstrcat(dirpath,start);
+
+ /*
+ * Don't cache a name with mangled or wildcard components
+ * as this can change the size.
+ */
+
+ if(!component_was_mangled && !name_has_wildcard)
+ stat_cache_add(orig_path, dirpath);
+
+ /*
+ * Restore the / that we wiped out earlier.
+ */
+ if (end)
+ *end = '/';
+ }
+
+ /*
+ * Don't cache a name with mangled or wildcard components
+ * as this can change the size.
+ */
+
+ if(!component_was_mangled && !name_has_wildcard)
+ stat_cache_add(orig_path, name);
+
+ /*
+ * The name has been resolved.
+ */
+
+ DEBUG(5,("conversion finished %s -> %s\n",orig_path, name));
+ return(True);
+}
+
+/****************************************************************************
+ Check a filename - possibly caling reducename.
+ This is called by every routine before it allows an operation on a filename.
+ It does any final confirmation necessary to ensure that the filename is
+ a valid one for the user to access.
+****************************************************************************/
+
+BOOL check_name(pstring name,connection_struct *conn)
+{
+ BOOL ret = True;
+
+ errno = 0;
+
+ if (IS_VETO_PATH(conn, name)) {
+ /* Is it not dot or dot dot. */
+ if (!((name[0] == '.') && (!name[1] || (name[1] == '.' && !name[2])))) {
+ DEBUG(5,("file path name %s vetoed\n",name));
+ return False;
+ }
+ }
+
+ if (!lp_widelinks(SNUM(conn))) {
+ ret = reduce_name(conn,name,conn->connectpath);
+ }
+
+ /* Check if we are allowing users to follow symlinks */
+ /* Patch from David Clerc <David.Clerc@cui.unige.ch>
+ University of Geneva */
+
+#ifdef S_ISLNK
+ if (!lp_symlinks(SNUM(conn))) {
+ SMB_STRUCT_STAT statbuf;
+ if ( (SMB_VFS_LSTAT(conn,name,&statbuf) != -1) &&
+ (S_ISLNK(statbuf.st_mode)) ) {
+ DEBUG(3,("check_name: denied: file path name %s is a symlink\n",name));
+ ret = False;
+ }
+ }
+#endif
+
+ if (!ret)
+ DEBUG(5,("check_name on %s failed\n",name));
+
+ return(ret);
+}
+
+/****************************************************************************
+ Scan a directory to find a filename, matching without case sensitivity.
+ If the name looks like a mangled name then try via the mangling functions
+****************************************************************************/
+
+static BOOL scan_directory(const char *path, char *name, size_t maxlength,
+ connection_struct *conn,BOOL docache)
+{
+ void *cur_dir;
+ const char *dname;
+ BOOL mangled;
+
+ mangled = mangle_is_mangled(name);
+
+ /* handle null paths */
+ if (*path == 0)
+ path = ".";
+
+ if (docache && (dname = DirCacheCheck(path,name,SNUM(conn)))) {
+ safe_strcpy(name, dname, maxlength);
+ return(True);
+ }
+
+ /*
+ * The incoming name can be mangled, and if we de-mangle it
+ * here it will not compare correctly against the filename (name2)
+ * read from the directory and then mangled by the mangle_map()
+ * call. We need to mangle both names or neither.
+ * (JRA).
+ */
+ if (mangled)
+ mangled = !mangle_check_cache( name );
+
+ /* open the directory */
+ if (!(cur_dir = OpenDir(conn, path, True))) {
+ DEBUG(3,("scan dir didn't open dir [%s]\n",path));
+ return(False);
+ }
+
+ /* now scan for matching names */
+ while ((dname = ReadDirName(cur_dir))) {
+
+ /* Is it dot or dot dot. */
+ if ((dname[0] == '.') && (!dname[1] || (dname[1] == '.' && !dname[2]))) {
+ continue;
+ }
+
+ /*
+ * At this point dname is the unmangled name.
+ * name is either mangled or not, depending on the state of the "mangled"
+ * variable. JRA.
+ */
+
+ /*
+ * Check mangled name against mangled name, or unmangled name
+ * against unmangled name.
+ */
+
+ if ((mangled && mangled_equal(name,dname,SNUM(conn))) || fname_equal(name, dname)) {
+ /* we've found the file, change it's name and return */
+ if (docache)
+ DirCacheAdd(path,name,dname,SNUM(conn));
+ safe_strcpy(name, dname, maxlength);
+ CloseDir(cur_dir);
+ return(True);
+ }
+ }
+
+ CloseDir(cur_dir);
+ return(False);
+}
diff --git a/source/smbd/files.c b/source/smbd/files.c
new file mode 100644
index 00000000000..80544c9a309
--- /dev/null
+++ b/source/smbd/files.c
@@ -0,0 +1,449 @@
+/*
+ Unix SMB/CIFS implementation.
+ Files[] structure handling
+ Copyright (C) Andrew Tridgell 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static int real_max_open_files;
+
+#define VALID_FNUM(fnum) (((fnum) >= 0) && ((fnum) < real_max_open_files))
+
+#define FILE_HANDLE_OFFSET 0x1000
+
+static struct bitmap *file_bmap;
+
+static files_struct *Files;
+
+/* a fsp to use when chaining */
+static files_struct *chain_fsp = NULL;
+/* a fsp to use to save when breaking an oplock. */
+static files_struct *oplock_save_chain_fsp = NULL;
+
+static int files_used;
+
+/****************************************************************************
+ Return a unique number identifying this fsp over the life of this pid.
+****************************************************************************/
+
+static unsigned long get_gen_count(void)
+{
+ static unsigned long file_gen_counter;
+
+ if ((++file_gen_counter) == 0)
+ return ++file_gen_counter;
+ return file_gen_counter;
+}
+
+/****************************************************************************
+ Find first available file slot.
+****************************************************************************/
+
+files_struct *file_new(connection_struct *conn)
+{
+ int i;
+ static int first_file;
+ files_struct *fsp, *next;
+
+ /* we want to give out file handles differently on each new
+ connection because of a common bug in MS clients where they try to
+ reuse a file descriptor from an earlier smb connection. This code
+ increases the chance that the errant client will get an error rather
+ than causing corruption */
+ if (first_file == 0) {
+ first_file = (sys_getpid() ^ (int)time(NULL)) % real_max_open_files;
+ }
+
+ i = bitmap_find(file_bmap, first_file);
+ if (i == -1) {
+ /*
+ * Before we give up, go through the open files
+ * and see if there are any files opened with a
+ * batch oplock. If so break the oplock and then
+ * re-use that entry (if it becomes closed).
+ * This may help as NT/95 clients tend to keep
+ * files batch oplocked for quite a long time
+ * after they have finished with them.
+ */
+ for (fsp=Files;fsp;fsp=next) {
+ next=fsp->next;
+ if (attempt_close_oplocked_file(fsp)) {
+ return file_new(conn);
+ }
+ }
+
+ DEBUG(0,("ERROR! Out of file structures\n"));
+ unix_ERR_class = ERRSRV;
+ unix_ERR_code = ERRnofids;
+ return NULL;
+ }
+
+ fsp = (files_struct *)malloc(sizeof(*fsp));
+ if (!fsp) {
+ unix_ERR_class = ERRSRV;
+ unix_ERR_code = ERRnofids;
+ return NULL;
+ }
+
+ ZERO_STRUCTP(fsp);
+ fsp->fd = -1;
+ fsp->conn = conn;
+ fsp->file_id = get_gen_count();
+ GetTimeOfDay(&fsp->open_time);
+
+ first_file = (i+1) % real_max_open_files;
+
+ bitmap_set(file_bmap, i);
+ files_used++;
+
+ fsp->fnum = i + FILE_HANDLE_OFFSET;
+ SMB_ASSERT(fsp->fnum < 65536);
+
+ string_set(&fsp->fsp_name,"");
+
+ DLIST_ADD(Files, fsp);
+
+ DEBUG(5,("allocated file structure %d, fnum = %d (%d used)\n",
+ i, fsp->fnum, files_used));
+
+ chain_fsp = fsp;
+
+ return fsp;
+}
+
+/****************************************************************************
+ Close all open files for a connection.
+****************************************************************************/
+
+void file_close_conn(connection_struct *conn)
+{
+ files_struct *fsp, *next;
+
+ for (fsp=Files;fsp;fsp=next) {
+ next = fsp->next;
+ if (fsp->conn == conn) {
+ close_file(fsp,False);
+ }
+ }
+}
+
+/****************************************************************************
+ Close all open files for a pid.
+****************************************************************************/
+
+void file_close_pid(uint16 smbpid)
+{
+ files_struct *fsp, *next;
+
+ for (fsp=Files;fsp;fsp=next) {
+ next = fsp->next;
+ if (fsp->file_pid == smbpid) {
+ close_file(fsp,False);
+ }
+ }
+}
+
+/****************************************************************************
+ Initialise file structures.
+****************************************************************************/
+
+#define MAX_OPEN_FUDGEFACTOR 20
+
+void file_init(void)
+{
+ int request_max_open_files = lp_max_open_files();
+ int real_lim;
+
+ /*
+ * Set the max_open files to be the requested
+ * max plus a fudgefactor to allow for the extra
+ * fd's we need such as log files etc...
+ */
+ real_lim = set_maxfiles(request_max_open_files + MAX_OPEN_FUDGEFACTOR);
+
+ real_max_open_files = real_lim - MAX_OPEN_FUDGEFACTOR;
+
+ if (real_max_open_files + FILE_HANDLE_OFFSET + MAX_OPEN_PIPES > 65536)
+ real_max_open_files = 65536 - FILE_HANDLE_OFFSET - MAX_OPEN_PIPES;
+
+ if(real_max_open_files != request_max_open_files) {
+ DEBUG(1,("file_init: Information only: requested %d \
+open files, %d are available.\n", request_max_open_files, real_max_open_files));
+ }
+
+ SMB_ASSERT(real_max_open_files > 100);
+
+ file_bmap = bitmap_allocate(real_max_open_files);
+
+ if (!file_bmap) {
+ exit_server("out of memory in file_init");
+ }
+
+ /*
+ * Ensure that pipe_handle_oppset is set correctly.
+ */
+ set_pipe_handle_offset(real_max_open_files);
+}
+
+/****************************************************************************
+ Close files open by a specified vuid.
+****************************************************************************/
+
+void file_close_user(int vuid)
+{
+ files_struct *fsp, *next;
+
+ for (fsp=Files;fsp;fsp=next) {
+ next=fsp->next;
+ if (fsp->vuid == vuid) {
+ close_file(fsp,False);
+ }
+ }
+}
+
+void file_dump_open_table(void)
+{
+ int count=0;
+ files_struct *fsp;
+
+ for (fsp=Files;fsp;fsp=fsp->next,count++) {
+ DEBUG(10,("Files[%d], fnum = %d, name %s, fd = %d, fileid = %lu, dev = %x, inode = %.0f\n",
+ count, fsp->fnum, fsp->fsp_name, fsp->fd, (unsigned long)fsp->file_id,
+ (unsigned int)fsp->dev, (double)fsp->inode ));
+ }
+}
+
+/****************************************************************************
+ Find a fsp given a file descriptor.
+****************************************************************************/
+
+files_struct *file_find_fd(int fd)
+{
+ int count=0;
+ files_struct *fsp;
+
+ for (fsp=Files;fsp;fsp=fsp->next,count++) {
+ if (fsp->fd == fd) {
+ if (count > 10) {
+ DLIST_PROMOTE(Files, fsp);
+ }
+ return fsp;
+ }
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Find a fsp given a device, inode and file_id.
+****************************************************************************/
+
+files_struct *file_find_dif(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_id)
+{
+ int count=0;
+ files_struct *fsp;
+
+ for (fsp=Files;fsp;fsp=fsp->next,count++) {
+ /* We can have a fsp->fd == -1 here as it could be a stat open. */
+ if (fsp->dev == dev &&
+ fsp->inode == inode &&
+ fsp->file_id == file_id ) {
+ if (count > 10) {
+ DLIST_PROMOTE(Files, fsp);
+ }
+ /* Paranoia check. */
+ if (fsp->fd == -1 && fsp->oplock_type != NO_OPLOCK) {
+ DEBUG(0,("file_find_dif: file %s dev = %x, inode = %.0f, file_id = %u \
+oplock_type = %u is a stat open with oplock type !\n", fsp->fsp_name, (unsigned int)fsp->dev,
+ (double)fsp->inode, (unsigned int)fsp->file_id,
+ (unsigned int)fsp->oplock_type ));
+ smb_panic("file_find_dif\n");
+ }
+ return fsp;
+ }
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Check if an fsp still exists.
+****************************************************************************/
+
+files_struct *file_find_fsp(files_struct *orig_fsp)
+{
+ files_struct *fsp;
+
+ for (fsp=Files;fsp;fsp=fsp->next) {
+ if (fsp == orig_fsp)
+ return fsp;
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Find the first fsp given a device and inode.
+****************************************************************************/
+
+files_struct *file_find_di_first(SMB_DEV_T dev, SMB_INO_T inode)
+{
+ files_struct *fsp;
+
+ for (fsp=Files;fsp;fsp=fsp->next) {
+ if ( fsp->fd != -1 &&
+ fsp->dev == dev &&
+ fsp->inode == inode )
+ return fsp;
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Find the next fsp having the same device and inode.
+****************************************************************************/
+
+files_struct *file_find_di_next(files_struct *start_fsp)
+{
+ files_struct *fsp;
+
+ for (fsp = start_fsp->next;fsp;fsp=fsp->next) {
+ if ( fsp->fd != -1 &&
+ fsp->dev == start_fsp->dev &&
+ fsp->inode == start_fsp->inode )
+ return fsp;
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Find a fsp that is open for printing.
+****************************************************************************/
+
+files_struct *file_find_print(void)
+{
+ files_struct *fsp;
+
+ for (fsp=Files;fsp;fsp=fsp->next) {
+ if (fsp->print_file) return fsp;
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Sync open files on a connection.
+****************************************************************************/
+
+void file_sync_all(connection_struct *conn)
+{
+ files_struct *fsp, *next;
+
+ for (fsp=Files;fsp;fsp=next) {
+ next=fsp->next;
+ if ((conn == fsp->conn) && (fsp->fd != -1)) {
+ sync_file(conn,fsp);
+ }
+ }
+}
+
+/****************************************************************************
+ Free up a fsp.
+****************************************************************************/
+
+void file_free(files_struct *fsp)
+{
+ DLIST_REMOVE(Files, fsp);
+
+ string_free(&fsp->fsp_name);
+
+ if (fsp->fake_file_handle) {
+ destroy_fake_file_handle(&fsp->fake_file_handle);
+ }
+
+ bitmap_clear(file_bmap, fsp->fnum - FILE_HANDLE_OFFSET);
+ files_used--;
+
+ DEBUG(5,("freed files structure %d (%d used)\n",
+ fsp->fnum, files_used));
+
+ /* this is paranoia, just in case someone tries to reuse the
+ information */
+ ZERO_STRUCTP(fsp);
+
+ if (fsp == chain_fsp) chain_fsp = NULL;
+
+ SAFE_FREE(fsp);
+}
+
+/****************************************************************************
+ Get a fsp from a packet given the offset of a 16 bit fnum.
+****************************************************************************/
+
+files_struct *file_fsp(char *buf, int where)
+{
+ int fnum, count=0;
+ files_struct *fsp;
+
+ if (chain_fsp)
+ return chain_fsp;
+
+ if (!buf)
+ return NULL;
+ fnum = SVAL(buf, where);
+
+ for (fsp=Files;fsp;fsp=fsp->next, count++) {
+ if (fsp->fnum == fnum) {
+ chain_fsp = fsp;
+ if (count > 10) {
+ DLIST_PROMOTE(Files, fsp);
+ }
+ return fsp;
+ }
+ }
+ return NULL;
+}
+
+/****************************************************************************
+ Reset the chained fsp - done at the start of a packet reply.
+****************************************************************************/
+
+void file_chain_reset(void)
+{
+ chain_fsp = NULL;
+}
+
+/****************************************************************************
+Save the chained fsp - done when about to do an oplock break.
+****************************************************************************/
+
+void file_chain_save(void)
+{
+ oplock_save_chain_fsp = chain_fsp;
+}
+
+/****************************************************************************
+Restore the chained fsp - done after an oplock break.
+****************************************************************************/
+
+void file_chain_restore(void)
+{
+ chain_fsp = oplock_save_chain_fsp;
+}
diff --git a/source/smbd/ipc.c b/source/smbd/ipc.c
new file mode 100644
index 00000000000..e5465b902c8
--- /dev/null
+++ b/source/smbd/ipc.c
@@ -0,0 +1,599 @@
+/*
+ Unix SMB/CIFS implementation.
+ Inter-process communication and named pipe handling
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ SMB Version handling
+ Copyright (C) John H Terpstra 1995-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+/*
+ This file handles the named pipe and mailslot calls
+ in the SMBtrans protocol
+ */
+
+#include "includes.h"
+
+extern int max_send;
+
+extern fstring local_machine;
+
+#define NERR_notsupported 50
+
+extern int smb_read_error;
+
+/*******************************************************************
+ copies parameters and data, as needed, into the smb buffer
+
+ *both* the data and params sections should be aligned. this
+ is fudged in the rpc pipes by
+ at present, only the data section is. this may be a possible
+ cause of some of the ipc problems being experienced. lkcl26dec97
+
+ ******************************************************************/
+
+static void copy_trans_params_and_data(char *outbuf, int align,
+ char *rparam, int param_offset, int param_len,
+ char *rdata, int data_offset, int data_len)
+{
+ char *copy_into = smb_buf(outbuf)+1;
+
+ if(param_len < 0)
+ param_len = 0;
+
+ if(data_len < 0)
+ data_len = 0;
+
+ DEBUG(5,("copy_trans_params_and_data: params[%d..%d] data[%d..%d]\n",
+ param_offset, param_offset + param_len,
+ data_offset , data_offset + data_len));
+
+ if (param_len)
+ memcpy(copy_into, &rparam[param_offset], param_len);
+
+ copy_into += param_len + align;
+
+ if (data_len )
+ memcpy(copy_into, &rdata[data_offset], data_len);
+}
+
+/****************************************************************************
+ Send a trans reply.
+ ****************************************************************************/
+
+void send_trans_reply(char *outbuf,
+ char *rparam, int rparam_len,
+ char *rdata, int rdata_len,
+ BOOL buffer_too_large)
+{
+ int this_ldata,this_lparam;
+ int tot_data_sent = 0;
+ int tot_param_sent = 0;
+ int align;
+
+ int ldata = rdata ? rdata_len : 0;
+ int lparam = rparam ? rparam_len : 0;
+
+ if (buffer_too_large)
+ DEBUG(5,("send_trans_reply: buffer %d too large\n", ldata ));
+
+ this_lparam = MIN(lparam,max_send - 500); /* hack */
+ this_ldata = MIN(ldata,max_send - (500+this_lparam));
+
+ align = ((this_lparam)%4);
+
+ if (buffer_too_large) {
+ ERROR_BOTH(STATUS_BUFFER_OVERFLOW,ERRDOS,ERRmoredata);
+ }
+
+ set_message(outbuf,10,1+align+this_ldata+this_lparam,True);
+
+ copy_trans_params_and_data(outbuf, align,
+ rparam, tot_param_sent, this_lparam,
+ rdata, tot_data_sent, this_ldata);
+
+ SSVAL(outbuf,smb_vwv0,lparam);
+ SSVAL(outbuf,smb_vwv1,ldata);
+ SSVAL(outbuf,smb_vwv3,this_lparam);
+ SSVAL(outbuf,smb_vwv4,smb_offset(smb_buf(outbuf)+1,outbuf));
+ SSVAL(outbuf,smb_vwv5,0);
+ SSVAL(outbuf,smb_vwv6,this_ldata);
+ SSVAL(outbuf,smb_vwv7,smb_offset(smb_buf(outbuf)+1+this_lparam+align,outbuf));
+ SSVAL(outbuf,smb_vwv8,0);
+ SSVAL(outbuf,smb_vwv9,0);
+
+ show_msg(outbuf);
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("send_trans_reply: send_smb failed.");
+
+ tot_data_sent = this_ldata;
+ tot_param_sent = this_lparam;
+
+ while (tot_data_sent < ldata || tot_param_sent < lparam)
+ {
+ this_lparam = MIN(lparam-tot_param_sent, max_send - 500); /* hack */
+ this_ldata = MIN(ldata -tot_data_sent, max_send - (500+this_lparam));
+
+ if(this_lparam < 0)
+ this_lparam = 0;
+
+ if(this_ldata < 0)
+ this_ldata = 0;
+
+ align = (this_lparam%4);
+
+ set_message(outbuf,10,1+this_ldata+this_lparam+align,False);
+
+ copy_trans_params_and_data(outbuf, align,
+ rparam, tot_param_sent, this_lparam,
+ rdata, tot_data_sent, this_ldata);
+
+ SSVAL(outbuf,smb_vwv3,this_lparam);
+ SSVAL(outbuf,smb_vwv4,smb_offset(smb_buf(outbuf)+1,outbuf));
+ SSVAL(outbuf,smb_vwv5,tot_param_sent);
+ SSVAL(outbuf,smb_vwv6,this_ldata);
+ SSVAL(outbuf,smb_vwv7,smb_offset(smb_buf(outbuf)+1+this_lparam+align,outbuf));
+ SSVAL(outbuf,smb_vwv8,tot_data_sent);
+ SSVAL(outbuf,smb_vwv9,0);
+
+ show_msg(outbuf);
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("send_trans_reply: send_smb failed.");
+
+ tot_data_sent += this_ldata;
+ tot_param_sent += this_lparam;
+ }
+}
+
+/****************************************************************************
+ Start the first part of an RPC reply which began with an SMBtrans request.
+****************************************************************************/
+
+static BOOL api_rpc_trans_reply(char *outbuf, smb_np_struct *p)
+{
+ BOOL is_data_outstanding;
+ char *rdata = malloc(p->max_trans_reply);
+ int data_len;
+
+ if(rdata == NULL) {
+ DEBUG(0,("api_rpc_trans_reply: malloc fail.\n"));
+ return False;
+ }
+
+ if((data_len = read_from_pipe( p, rdata, p->max_trans_reply,
+ &is_data_outstanding)) < 0) {
+ SAFE_FREE(rdata);
+ return False;
+ }
+
+ send_trans_reply(outbuf, NULL, 0, rdata, data_len, is_data_outstanding);
+
+ SAFE_FREE(rdata);
+ return True;
+}
+
+/****************************************************************************
+ WaitNamedPipeHandleState
+****************************************************************************/
+
+static BOOL api_WNPHS(char *outbuf, smb_np_struct *p, char *param, int param_len)
+{
+ uint16 priority;
+
+ if (!param || param_len < 2)
+ return False;
+
+ priority = SVAL(param,0);
+ DEBUG(4,("WaitNamedPipeHandleState priority %x\n", priority));
+
+ if (wait_rpc_pipe_hnd_state(p, priority)) {
+ /* now send the reply */
+ send_trans_reply(outbuf, NULL, 0, NULL, 0, False);
+ return True;
+ }
+ return False;
+}
+
+
+/****************************************************************************
+ SetNamedPipeHandleState
+****************************************************************************/
+
+static BOOL api_SNPHS(char *outbuf, smb_np_struct *p, char *param, int param_len)
+{
+ uint16 id;
+
+ if (!param || param_len < 2)
+ return False;
+
+ id = SVAL(param,0);
+ DEBUG(4,("SetNamedPipeHandleState to code %x\n", id));
+
+ if (set_rpc_pipe_hnd_state(p, id)) {
+ /* now send the reply */
+ send_trans_reply(outbuf, NULL, 0, NULL, 0, False);
+ return True;
+ }
+ return False;
+}
+
+
+/****************************************************************************
+ When no reply is generated, indicate unsupported.
+ ****************************************************************************/
+
+static BOOL api_no_reply(char *outbuf, int max_rdata_len)
+{
+ char rparam[4];
+
+ /* unsupported */
+ SSVAL(rparam,0,NERR_notsupported);
+ SSVAL(rparam,2,0); /* converter word */
+
+ DEBUG(3,("Unsupported API fd command\n"));
+
+ /* now send the reply */
+ send_trans_reply(outbuf, rparam, 4, NULL, 0, False);
+
+ return -1;
+}
+
+/****************************************************************************
+ Handle remote api calls delivered to a named pipe already opened.
+ ****************************************************************************/
+
+static int api_fd_reply(connection_struct *conn,uint16 vuid,char *outbuf,
+ uint16 *setup,char *data,char *params,
+ int suwcnt,int tdscnt,int tpscnt,int mdrcnt,int mprcnt)
+{
+ BOOL reply = False;
+ smb_np_struct *p = NULL;
+ int pnum;
+ int subcommand;
+
+ DEBUG(5,("api_fd_reply\n"));
+
+ /* First find out the name of this file. */
+ if (suwcnt != 2) {
+ DEBUG(0,("Unexpected named pipe transaction.\n"));
+ return(-1);
+ }
+
+ /* Get the file handle and hence the file name. */
+ /*
+ * NB. The setup array has already been transformed
+ * via SVAL and so is in gost byte order.
+ */
+ pnum = ((int)setup[1]) & 0xFFFF;
+ subcommand = ((int)setup[0]) & 0xFFFF;
+
+ if(!(p = get_rpc_pipe(pnum))) {
+ if (subcommand == TRANSACT_WAITNAMEDPIPEHANDLESTATE) {
+ /* Win9x does this call with a unicode pipe name, not a pnum. */
+ /* Just return success for now... */
+ DEBUG(3,("Got TRANSACT_WAITNAMEDPIPEHANDLESTATE on text pipe name\n"));
+ send_trans_reply(outbuf, NULL, 0, NULL, 0, False);
+ return -1;
+ }
+
+ DEBUG(1,("api_fd_reply: INVALID PIPE HANDLE: %x\n", pnum));
+ return api_no_reply(outbuf, mdrcnt);
+ }
+
+ DEBUG(3,("Got API command 0x%x on pipe \"%s\" (pnum %x)\n", subcommand, p->name, pnum));
+
+ /* record maximum data length that can be transmitted in an SMBtrans */
+ p->max_trans_reply = mdrcnt;
+
+ DEBUG(10,("api_fd_reply: p:%p max_trans_reply: %d\n", p, p->max_trans_reply));
+
+ switch (subcommand) {
+ case TRANSACT_DCERPCCMD:
+ /* dce/rpc command */
+ reply = write_to_pipe(p, data, tdscnt);
+ if (reply)
+ reply = api_rpc_trans_reply(outbuf, p);
+ break;
+ case TRANSACT_WAITNAMEDPIPEHANDLESTATE:
+ /* Wait Named Pipe Handle state */
+ reply = api_WNPHS(outbuf, p, params, tpscnt);
+ break;
+ case TRANSACT_SETNAMEDPIPEHANDLESTATE:
+ /* Set Named Pipe Handle state */
+ reply = api_SNPHS(outbuf, p, params, tpscnt);
+ break;
+ }
+
+ if (!reply)
+ return api_no_reply(outbuf, mdrcnt);
+
+ return -1;
+}
+
+/****************************************************************************
+ handle named pipe commands
+ ****************************************************************************/
+static int named_pipe(connection_struct *conn,uint16 vuid, char *outbuf,char *name,
+ uint16 *setup,char *data,char *params,
+ int suwcnt,int tdscnt,int tpscnt,
+ int msrcnt,int mdrcnt,int mprcnt)
+{
+ DEBUG(3,("named pipe command on <%s> name\n", name));
+
+ if (strequal(name,"LANMAN"))
+ return api_reply(conn,vuid,outbuf,data,params,tdscnt,tpscnt,mdrcnt,mprcnt);
+
+ if (strequal(name,"WKSSVC") ||
+ strequal(name,"SRVSVC") ||
+ strequal(name,"WINREG") ||
+ strequal(name,"SAMR") ||
+ strequal(name,"LSARPC"))
+ {
+ DEBUG(4,("named pipe command from Win95 (wow!)\n"));
+ return api_fd_reply(conn,vuid,outbuf,setup,data,params,suwcnt,tdscnt,tpscnt,mdrcnt,mprcnt);
+ }
+
+ if (strlen(name) < 1)
+ return api_fd_reply(conn,vuid,outbuf,setup,data,params,suwcnt,tdscnt,tpscnt,mdrcnt,mprcnt);
+
+ if (setup)
+ DEBUG(3,("unknown named pipe: setup 0x%X setup1=%d\n", (int)setup[0],(int)setup[1]));
+
+ return 0;
+}
+
+
+/****************************************************************************
+ Reply to a SMBtrans.
+ ****************************************************************************/
+
+int reply_trans(connection_struct *conn, char *inbuf,char *outbuf, int size, int bufsize)
+{
+ fstring name;
+ int name_offset = 0;
+ char *data=NULL,*params=NULL;
+ uint16 *setup=NULL;
+ int outsize = 0;
+ uint16 vuid = SVAL(inbuf,smb_uid);
+ unsigned int tpscnt = SVAL(inbuf,smb_vwv0);
+ unsigned int tdscnt = SVAL(inbuf,smb_vwv1);
+ unsigned int mprcnt = SVAL(inbuf,smb_vwv2);
+ unsigned int mdrcnt = SVAL(inbuf,smb_vwv3);
+ unsigned int msrcnt = CVAL(inbuf,smb_vwv4);
+ BOOL close_on_completion = BITSETW(inbuf+smb_vwv5,0);
+ BOOL one_way = BITSETW(inbuf+smb_vwv5,1);
+ unsigned int pscnt = SVAL(inbuf,smb_vwv9);
+ unsigned int psoff = SVAL(inbuf,smb_vwv10);
+ unsigned int dscnt = SVAL(inbuf,smb_vwv11);
+ unsigned int dsoff = SVAL(inbuf,smb_vwv12);
+ unsigned int suwcnt = CVAL(inbuf,smb_vwv13);
+ START_PROFILE(SMBtrans);
+
+ memset(name, '\0',sizeof(name));
+ srvstr_pull_buf(inbuf, name, smb_buf(inbuf), sizeof(name), STR_TERMINATE);
+
+ if (dscnt > tdscnt || pscnt > tpscnt)
+ goto bad_param;
+
+ if (tdscnt) {
+ if((data = (char *)malloc(tdscnt)) == NULL) {
+ DEBUG(0,("reply_trans: data malloc fail for %u bytes !\n", tdscnt));
+ END_PROFILE(SMBtrans);
+ return(ERROR_DOS(ERRDOS,ERRnomem));
+ }
+ if ((dsoff+dscnt < dsoff) || (dsoff+dscnt < dscnt))
+ goto bad_param;
+ if ((smb_base(inbuf)+dsoff+dscnt > inbuf + size) ||
+ (smb_base(inbuf)+dsoff+dscnt < smb_base(inbuf)))
+ goto bad_param;
+
+ memcpy(data,smb_base(inbuf)+dsoff,dscnt);
+ }
+
+ if (tpscnt) {
+ if((params = (char *)malloc(tpscnt)) == NULL) {
+ DEBUG(0,("reply_trans: param malloc fail for %u bytes !\n", tpscnt));
+ SAFE_FREE(data);
+ END_PROFILE(SMBtrans);
+ return(ERROR_DOS(ERRDOS,ERRnomem));
+ }
+ if ((psoff+pscnt < psoff) || (psoff+pscnt < pscnt))
+ goto bad_param;
+ if ((smb_base(inbuf)+psoff+pscnt > inbuf + size) ||
+ (smb_base(inbuf)+psoff+pscnt < smb_base(inbuf)))
+ goto bad_param;
+
+ memcpy(params,smb_base(inbuf)+psoff,pscnt);
+ }
+
+ if (suwcnt) {
+ unsigned int i;
+ if((setup = (uint16 *)malloc(suwcnt*sizeof(uint16))) == NULL) {
+ DEBUG(0,("reply_trans: setup malloc fail for %u bytes !\n", (unsigned int)(suwcnt * sizeof(uint16))));
+ SAFE_FREE(data);
+ SAFE_FREE(params);
+ END_PROFILE(SMBtrans);
+ return(ERROR_DOS(ERRDOS,ERRnomem));
+ }
+ if (inbuf+smb_vwv14+(suwcnt*SIZEOFWORD) > inbuf + size)
+ goto bad_param;
+ if ((smb_vwv14+(suwcnt*SIZEOFWORD) < smb_vwv14) || (smb_vwv14+(suwcnt*SIZEOFWORD) < (suwcnt*SIZEOFWORD)))
+ goto bad_param;
+
+ for (i=0;i<suwcnt;i++)
+ setup[i] = SVAL(inbuf,smb_vwv14+i*SIZEOFWORD);
+ }
+
+
+ srv_signing_trans_start(SVAL(inbuf,smb_mid));
+
+ if (pscnt < tpscnt || dscnt < tdscnt) {
+ /* We need to send an interim response then receive the rest
+ of the parameter/data bytes */
+ outsize = set_message(outbuf,0,0,True);
+ show_msg(outbuf);
+ srv_signing_trans_stop();
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("reply_trans: send_smb failed.");
+ }
+
+ /* receive the rest of the trans packet */
+ while (pscnt < tpscnt || dscnt < tdscnt) {
+ BOOL ret;
+ unsigned int pcnt,poff,dcnt,doff,pdisp,ddisp;
+
+ ret = receive_next_smb(inbuf,bufsize,SMB_SECONDARY_WAIT);
+
+ /*
+ * The sequence number for the trans reply is always
+ * based on the last secondary received.
+ */
+
+ srv_signing_trans_start(SVAL(inbuf,smb_mid));
+
+ if ((ret && (CVAL(inbuf, smb_com) != SMBtranss)) || !ret) {
+ if(ret) {
+ DEBUG(0,("reply_trans: Invalid secondary trans packet\n"));
+ } else {
+ DEBUG(0,("reply_trans: %s in getting secondary trans response.\n",
+ (smb_read_error == READ_ERROR) ? "error" : "timeout" ));
+ }
+ SAFE_FREE(params);
+ SAFE_FREE(data);
+ SAFE_FREE(setup);
+ END_PROFILE(SMBtrans);
+ srv_signing_trans_stop();
+ return(ERROR_DOS(ERRSRV,ERRerror));
+ }
+
+ show_msg(inbuf);
+
+ /* Revise total_params and total_data in case they have changed downwards */
+ if (SVAL(inbuf,smb_vwv0) < tpscnt)
+ tpscnt = SVAL(inbuf,smb_vwv0);
+ if (SVAL(inbuf,smb_vwv1) < tdscnt)
+ tdscnt = SVAL(inbuf,smb_vwv1);
+
+ pcnt = SVAL(inbuf,smb_vwv2);
+ poff = SVAL(inbuf,smb_vwv3);
+ pdisp = SVAL(inbuf,smb_vwv4);
+
+ dcnt = SVAL(inbuf,smb_vwv5);
+ doff = SVAL(inbuf,smb_vwv6);
+ ddisp = SVAL(inbuf,smb_vwv7);
+
+ pscnt += pcnt;
+ dscnt += dcnt;
+
+ if (dscnt > tdscnt || pscnt > tpscnt)
+ goto bad_param;
+
+ if (pcnt) {
+ if (pdisp+pcnt >= tpscnt)
+ goto bad_param;
+ if ((pdisp+pcnt < pdisp) || (pdisp+pcnt < pcnt))
+ goto bad_param;
+ if (pdisp > tpscnt)
+ goto bad_param;
+ if ((smb_base(inbuf) + poff + pcnt >= inbuf + bufsize) ||
+ (smb_base(inbuf) + poff + pcnt < smb_base(inbuf)))
+ goto bad_param;
+ if (params + pdisp < params)
+ goto bad_param;
+
+ memcpy(params+pdisp,smb_base(inbuf)+poff,pcnt);
+ }
+
+ if (dcnt) {
+ if (ddisp+dcnt >= tdscnt)
+ goto bad_param;
+ if ((ddisp+dcnt < ddisp) || (ddisp+dcnt < dcnt))
+ goto bad_param;
+ if (ddisp > tdscnt)
+ goto bad_param;
+ if ((smb_base(inbuf) + doff + dcnt >= inbuf + bufsize) ||
+ (smb_base(inbuf) + doff + dcnt < smb_base(inbuf)))
+ goto bad_param;
+ if (data + ddisp < data)
+ goto bad_param;
+
+ memcpy(data+ddisp,smb_base(inbuf)+doff,dcnt);
+ }
+ }
+
+ DEBUG(3,("trans <%s> data=%u params=%u setup=%u\n",
+ name,tdscnt,tpscnt,suwcnt));
+
+ /*
+ * WinCE wierdness....
+ */
+
+ if (name[0] == '\\' && (StrnCaseCmp(&name[1],local_machine, strlen(local_machine)) == 0) &&
+ (name[strlen(local_machine)+1] == '\\'))
+ name_offset = strlen(local_machine)+1;
+
+ if (strnequal(&name[name_offset], "\\PIPE", strlen("\\PIPE"))) {
+ name_offset += strlen("\\PIPE");
+
+ /* Win9x weirdness. When talking to a unicode server Win9x
+ only sends \PIPE instead of \PIPE\ */
+
+ if (name[name_offset] == '\\')
+ name_offset++;
+
+ DEBUG(5,("calling named_pipe\n"));
+ outsize = named_pipe(conn,vuid,outbuf,
+ name+name_offset,setup,data,params,
+ suwcnt,tdscnt,tpscnt,msrcnt,mdrcnt,mprcnt);
+ } else {
+ DEBUG(3,("invalid pipe name\n"));
+ outsize = 0;
+ }
+
+
+ SAFE_FREE(data);
+ SAFE_FREE(params);
+ SAFE_FREE(setup);
+
+ srv_signing_trans_stop();
+
+ if (close_on_completion)
+ close_cnum(conn,vuid);
+
+ if (one_way) {
+ END_PROFILE(SMBtrans);
+ return(-1);
+ }
+
+ if (outsize == 0) {
+ END_PROFILE(SMBtrans);
+ return(ERROR_DOS(ERRSRV,ERRnosupport));
+ }
+
+ END_PROFILE(SMBtrans);
+ return(outsize);
+
+
+ bad_param:
+
+ srv_signing_trans_stop();
+ DEBUG(0,("reply_trans: invalid trans parameters\n"));
+ SAFE_FREE(data);
+ SAFE_FREE(params);
+ SAFE_FREE(setup);
+ END_PROFILE(SMBtrans);
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+}
diff --git a/source/smbd/lanman.c b/source/smbd/lanman.c
new file mode 100644
index 00000000000..d715ab4ddc3
--- /dev/null
+++ b/source/smbd/lanman.c
@@ -0,0 +1,3619 @@
+/*
+ Unix SMB/CIFS implementation.
+ Inter-process communication and named pipe handling
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ SMB Version handling
+ Copyright (C) John H Terpstra 1995-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+/*
+ This file handles the named pipe and mailslot calls
+ in the SMBtrans protocol
+ */
+
+#include "includes.h"
+
+#ifdef CHECK_TYPES
+#undef CHECK_TYPES
+#endif
+#define CHECK_TYPES 0
+
+extern fstring local_machine;
+
+#define NERR_Success 0
+#define NERR_badpass 86
+#define NERR_notsupported 50
+
+#define NERR_BASE (2100)
+#define NERR_BufTooSmall (NERR_BASE+23)
+#define NERR_JobNotFound (NERR_BASE+51)
+#define NERR_DestNotFound (NERR_BASE+52)
+
+#define ACCESS_READ 0x01
+#define ACCESS_WRITE 0x02
+#define ACCESS_CREATE 0x04
+
+#define SHPWLEN 8 /* share password length */
+
+static BOOL api_Unsupported(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len);
+static BOOL api_TooSmall(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len);
+
+
+static int CopyExpanded(connection_struct *conn,
+ int snum, char** dst, char* src, int* n)
+{
+ pstring buf;
+ int l;
+
+ if (!src || !dst || !n || !(*dst)) return(0);
+
+ StrnCpy(buf,src,sizeof(buf)/2);
+ pstring_sub(buf,"%S",lp_servicename(snum));
+ standard_sub_conn(conn,buf,sizeof(buf));
+ l = push_ascii(*dst,buf,*n, STR_TERMINATE);
+ (*dst) += l;
+ (*n) -= l;
+ return l;
+}
+
+static int CopyAndAdvance(char** dst, char* src, int* n)
+{
+ int l;
+ if (!src || !dst || !n || !(*dst)) return(0);
+ l = push_ascii(*dst,src,*n, STR_TERMINATE);
+ (*dst) += l;
+ (*n) -= l;
+ return l;
+}
+
+static int StrlenExpanded(connection_struct *conn, int snum, char* s)
+{
+ pstring buf;
+ if (!s) return(0);
+ StrnCpy(buf,s,sizeof(buf)/2);
+ pstring_sub(buf,"%S",lp_servicename(snum));
+ standard_sub_conn(conn,buf,sizeof(buf));
+ return strlen(buf) + 1;
+}
+
+static char* Expand(connection_struct *conn, int snum, char* s)
+{
+ static pstring buf;
+ if (!s) return(NULL);
+ StrnCpy(buf,s,sizeof(buf)/2);
+ pstring_sub(buf,"%S",lp_servicename(snum));
+ standard_sub_conn(conn,buf,sizeof(buf));
+ return &buf[0];
+}
+
+/*******************************************************************
+ check a API string for validity when we only need to check the prefix
+ ******************************************************************/
+static BOOL prefix_ok(const char *str, const char *prefix)
+{
+ return(strncmp(str,prefix,strlen(prefix)) == 0);
+}
+
+struct pack_desc {
+ const char* format; /* formatstring for structure */
+ const char* subformat; /* subformat for structure */
+ char* base; /* baseaddress of buffer */
+ int buflen; /* remaining size for fixed part; on init: length of base */
+ int subcount; /* count of substructures */
+ char* structbuf; /* pointer into buffer for remaining fixed part */
+ int stringlen; /* remaining size for variable part */
+ char* stringbuf; /* pointer into buffer for remaining variable part */
+ int neededlen; /* total needed size */
+ int usedlen; /* total used size (usedlen <= neededlen and usedlen <= buflen) */
+ const char* curpos; /* current position; pointer into format or subformat */
+ int errcode;
+};
+
+static int get_counter(const char** p)
+{
+ int i, n;
+ if (!p || !(*p)) return(1);
+ if (!isdigit((int)**p)) return 1;
+ for (n = 0;;) {
+ i = **p;
+ if (isdigit(i))
+ n = 10 * n + (i - '0');
+ else
+ return n;
+ (*p)++;
+ }
+}
+
+static int getlen(const char* p)
+{
+ int n = 0;
+ if (!p) return(0);
+ while (*p) {
+ switch( *p++ ) {
+ case 'W': /* word (2 byte) */
+ n += 2;
+ break;
+ case 'K': /* status word? (2 byte) */
+ n += 2;
+ break;
+ case 'N': /* count of substructures (word) at end */
+ n += 2;
+ break;
+ case 'D': /* double word (4 byte) */
+ case 'z': /* offset to zero terminated string (4 byte) */
+ case 'l': /* offset to user data (4 byte) */
+ n += 4;
+ break;
+ case 'b': /* offset to data (with counter) (4 byte) */
+ n += 4;
+ get_counter(&p);
+ break;
+ case 'B': /* byte (with optional counter) */
+ n += get_counter(&p);
+ break;
+ }
+ }
+ return n;
+}
+
+static BOOL init_package(struct pack_desc* p, int count, int subcount)
+{
+ int n = p->buflen;
+ int i;
+
+ if (!p->format || !p->base) return(False);
+
+ i = count * getlen(p->format);
+ if (p->subformat) i += subcount * getlen(p->subformat);
+ p->structbuf = p->base;
+ p->neededlen = 0;
+ p->usedlen = 0;
+ p->subcount = 0;
+ p->curpos = p->format;
+ if (i > n) {
+ p->neededlen = i;
+ i = n = 0;
+#if 0
+ /*
+ * This is the old error code we used. Aparently
+ * WinNT/2k systems return ERRbuftoosmall (2123) and
+ * OS/2 needs this. I'm leaving this here so we can revert
+ * if needed. JRA.
+ */
+ p->errcode = ERRmoredata;
+#else
+ p->errcode = ERRbuftoosmall;
+#endif
+ }
+ else
+ p->errcode = NERR_Success;
+ p->buflen = i;
+ n -= i;
+ p->stringbuf = p->base + i;
+ p->stringlen = n;
+ return(p->errcode == NERR_Success);
+}
+
+static int package(struct pack_desc* p, ...)
+{
+ va_list args;
+ int needed=0, stringneeded;
+ const char* str=NULL;
+ int is_string=0, stringused;
+ int32 temp;
+
+ va_start(args,p);
+
+ if (!*p->curpos) {
+ if (!p->subcount)
+ p->curpos = p->format;
+ else {
+ p->curpos = p->subformat;
+ p->subcount--;
+ }
+ }
+#if CHECK_TYPES
+ str = va_arg(args,char*);
+ SMB_ASSERT(strncmp(str,p->curpos,strlen(str)) == 0);
+#endif
+ stringneeded = -1;
+
+ if (!p->curpos) {
+ va_end(args);
+ return(0);
+ }
+
+ switch( *p->curpos++ ) {
+ case 'W': /* word (2 byte) */
+ needed = 2;
+ temp = va_arg(args,int);
+ if (p->buflen >= needed) SSVAL(p->structbuf,0,temp);
+ break;
+ case 'K': /* status word? (2 byte) */
+ needed = 2;
+ temp = va_arg(args,int);
+ if (p->buflen >= needed) SSVAL(p->structbuf,0,temp);
+ break;
+ case 'N': /* count of substructures (word) at end */
+ needed = 2;
+ p->subcount = va_arg(args,int);
+ if (p->buflen >= needed) SSVAL(p->structbuf,0,p->subcount);
+ break;
+ case 'D': /* double word (4 byte) */
+ needed = 4;
+ temp = va_arg(args,int);
+ if (p->buflen >= needed) SIVAL(p->structbuf,0,temp);
+ break;
+ case 'B': /* byte (with optional counter) */
+ needed = get_counter(&p->curpos);
+ {
+ char *s = va_arg(args,char*);
+ if (p->buflen >= needed) StrnCpy(p->structbuf,s?s:"",needed-1);
+ }
+ break;
+ case 'z': /* offset to zero terminated string (4 byte) */
+ str = va_arg(args,char*);
+ stringneeded = (str ? strlen(str)+1 : 0);
+ is_string = 1;
+ break;
+ case 'l': /* offset to user data (4 byte) */
+ str = va_arg(args,char*);
+ stringneeded = va_arg(args,int);
+ is_string = 0;
+ break;
+ case 'b': /* offset to data (with counter) (4 byte) */
+ str = va_arg(args,char*);
+ stringneeded = get_counter(&p->curpos);
+ is_string = 0;
+ break;
+ }
+ va_end(args);
+ if (stringneeded >= 0) {
+ needed = 4;
+ if (p->buflen >= needed) {
+ stringused = stringneeded;
+ if (stringused > p->stringlen) {
+ stringused = (is_string ? p->stringlen : 0);
+ if (p->errcode == NERR_Success) p->errcode = ERRmoredata;
+ }
+ if (!stringused)
+ SIVAL(p->structbuf,0,0);
+ else {
+ SIVAL(p->structbuf,0,PTR_DIFF(p->stringbuf,p->base));
+ memcpy(p->stringbuf,str?str:"",stringused);
+ if (is_string) p->stringbuf[stringused-1] = '\0';
+ p->stringbuf += stringused;
+ p->stringlen -= stringused;
+ p->usedlen += stringused;
+ }
+ }
+ p->neededlen += stringneeded;
+ }
+ p->neededlen += needed;
+ if (p->buflen >= needed) {
+ p->structbuf += needed;
+ p->buflen -= needed;
+ p->usedlen += needed;
+ }
+ else {
+ if (p->errcode == NERR_Success) p->errcode = ERRmoredata;
+ }
+ return 1;
+}
+
+#if CHECK_TYPES
+#define PACK(desc,t,v) package(desc,t,v,0,0,0,0)
+#define PACKl(desc,t,v,l) package(desc,t,v,l,0,0,0,0)
+#else
+#define PACK(desc,t,v) package(desc,v)
+#define PACKl(desc,t,v,l) package(desc,v,l)
+#endif
+
+static void PACKI(struct pack_desc* desc, const char *t,int v)
+{
+ PACK(desc,t,v);
+}
+
+static void PACKS(struct pack_desc* desc,const char *t,const char *v)
+{
+ PACK(desc,t,v);
+}
+
+
+/****************************************************************************
+ get a print queue
+ ****************************************************************************/
+static void PackDriverData(struct pack_desc* desc)
+{
+ char drivdata[4+4+32];
+ SIVAL(drivdata,0,sizeof drivdata); /* cb */
+ SIVAL(drivdata,4,1000); /* lVersion */
+ memset(drivdata+8,0,32); /* szDeviceName */
+ push_ascii(drivdata+8,"NULL",-1, STR_TERMINATE);
+ PACKl(desc,"l",drivdata,sizeof drivdata); /* pDriverData */
+}
+
+static int check_printq_info(struct pack_desc* desc,
+ int uLevel, char *id1, char *id2)
+{
+ desc->subformat = NULL;
+ switch( uLevel ) {
+ case 0:
+ desc->format = "B13";
+ break;
+ case 1:
+ desc->format = "B13BWWWzzzzzWW";
+ break;
+ case 2:
+ desc->format = "B13BWWWzzzzzWN";
+ desc->subformat = "WB21BB16B10zWWzDDz";
+ break;
+ case 3:
+ desc->format = "zWWWWzzzzWWzzl";
+ break;
+ case 4:
+ desc->format = "zWWWWzzzzWNzzl";
+ desc->subformat = "WWzWWDDzz";
+ break;
+ case 5:
+ desc->format = "z";
+ break;
+ case 51:
+ desc->format = "K";
+ break;
+ case 52:
+ desc->format = "WzzzzzzzzN";
+ desc->subformat = "z";
+ break;
+ default: return False;
+ }
+ if (strcmp(desc->format,id1) != 0) return False;
+ if (desc->subformat && strcmp(desc->subformat,id2) != 0) return False;
+ return True;
+}
+
+
+#define RAP_JOB_STATUS_QUEUED 0
+#define RAP_JOB_STATUS_PAUSED 1
+#define RAP_JOB_STATUS_SPOOLING 2
+#define RAP_JOB_STATUS_PRINTING 3
+#define RAP_JOB_STATUS_PRINTED 4
+
+#define RAP_QUEUE_STATUS_PAUSED 1
+#define RAP_QUEUE_STATUS_ERROR 2
+
+/* turn a print job status into a on the wire status
+*/
+static int printj_status(int v)
+{
+ switch (v) {
+ case LPQ_QUEUED:
+ return RAP_JOB_STATUS_QUEUED;
+ case LPQ_PAUSED:
+ return RAP_JOB_STATUS_PAUSED;
+ case LPQ_SPOOLING:
+ return RAP_JOB_STATUS_SPOOLING;
+ case LPQ_PRINTING:
+ return RAP_JOB_STATUS_PRINTING;
+ }
+ return 0;
+}
+
+/* turn a print queue status into a on the wire status
+*/
+static int printq_status(int v)
+{
+ switch (v) {
+ case LPQ_QUEUED:
+ return 0;
+ case LPQ_PAUSED:
+ return RAP_QUEUE_STATUS_PAUSED;
+ }
+ return RAP_QUEUE_STATUS_ERROR;
+}
+
+static void fill_printjob_info(connection_struct *conn, int snum, int uLevel,
+ struct pack_desc* desc,
+ print_queue_struct* queue, int n)
+{
+ time_t t = queue->time;
+
+ /* the client expects localtime */
+ t -= TimeDiff(t);
+
+ PACKI(desc,"W",pjobid_to_rap(snum,queue->job)); /* uJobId */
+ if (uLevel == 1) {
+ PACKS(desc,"B21",queue->fs_user); /* szUserName */
+ PACKS(desc,"B",""); /* pad */
+ PACKS(desc,"B16",""); /* szNotifyName */
+ PACKS(desc,"B10","PM_Q_RAW"); /* szDataType */
+ PACKS(desc,"z",""); /* pszParms */
+ PACKI(desc,"W",n+1); /* uPosition */
+ PACKI(desc,"W",printj_status(queue->status)); /* fsStatus */
+ PACKS(desc,"z",""); /* pszStatus */
+ PACKI(desc,"D",t); /* ulSubmitted */
+ PACKI(desc,"D",queue->size); /* ulSize */
+ PACKS(desc,"z",queue->fs_file); /* pszComment */
+ }
+ if (uLevel == 2 || uLevel == 3 || uLevel == 4) {
+ PACKI(desc,"W",queue->priority); /* uPriority */
+ PACKS(desc,"z",queue->fs_user); /* pszUserName */
+ PACKI(desc,"W",n+1); /* uPosition */
+ PACKI(desc,"W",printj_status(queue->status)); /* fsStatus */
+ PACKI(desc,"D",t); /* ulSubmitted */
+ PACKI(desc,"D",queue->size); /* ulSize */
+ PACKS(desc,"z","Samba"); /* pszComment */
+ PACKS(desc,"z",queue->fs_file); /* pszDocument */
+ if (uLevel == 3) {
+ PACKS(desc,"z",""); /* pszNotifyName */
+ PACKS(desc,"z","PM_Q_RAW"); /* pszDataType */
+ PACKS(desc,"z",""); /* pszParms */
+ PACKS(desc,"z",""); /* pszStatus */
+ PACKS(desc,"z",SERVICE(snum)); /* pszQueue */
+ PACKS(desc,"z","lpd"); /* pszQProcName */
+ PACKS(desc,"z",""); /* pszQProcParms */
+ PACKS(desc,"z","NULL"); /* pszDriverName */
+ PackDriverData(desc); /* pDriverData */
+ PACKS(desc,"z",""); /* pszPrinterName */
+ } else if (uLevel == 4) { /* OS2 */
+ PACKS(desc,"z",""); /* pszSpoolFileName */
+ PACKS(desc,"z",""); /* pszPortName */
+ PACKS(desc,"z",""); /* pszStatus */
+ PACKI(desc,"D",0); /* ulPagesSpooled */
+ PACKI(desc,"D",0); /* ulPagesSent */
+ PACKI(desc,"D",0); /* ulPagesPrinted */
+ PACKI(desc,"D",0); /* ulTimePrinted */
+ PACKI(desc,"D",0); /* ulExtendJobStatus */
+ PACKI(desc,"D",0); /* ulStartPage */
+ PACKI(desc,"D",0); /* ulEndPage */
+ }
+ }
+}
+
+/********************************************************************
+ Return a driver name given an snum.
+ Returns True if from tdb, False otherwise.
+ ********************************************************************/
+
+static BOOL get_driver_name(int snum, pstring drivername)
+{
+ NT_PRINTER_INFO_LEVEL *info = NULL;
+ BOOL in_tdb = False;
+
+ get_a_printer (NULL, &info, 2, lp_servicename(snum));
+ if (info != NULL) {
+ pstrcpy( drivername, info->info_2->drivername);
+ in_tdb = True;
+ free_a_printer(&info, 2);
+ }
+
+ return in_tdb;
+}
+
+/********************************************************************
+ Respond to the DosPrintQInfo command with a level of 52
+ This is used to get printer driver information for Win9x clients
+ ********************************************************************/
+static void fill_printq_info_52(connection_struct *conn, int snum,
+ struct pack_desc* desc, int count )
+{
+ int i;
+ fstring location;
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ ZERO_STRUCT(driver);
+
+ if ( !W_ERROR_IS_OK(get_a_printer( NULL, &printer, 2, lp_servicename(snum))) ) {
+ DEBUG(3,("fill_printq_info_52: Failed to lookup printer [%s]\n",
+ lp_servicename(snum)));
+ goto err;
+ }
+
+ if ( !W_ERROR_IS_OK(get_a_printer_driver(&driver, 3, printer->info_2->drivername,
+ "Windows 4.0", 0)) )
+ {
+ DEBUG(3,("fill_printq_info_52: Failed to lookup driver [%s]\n",
+ printer->info_2->drivername));
+ goto err;
+ }
+
+ trim_string(driver.info_3->driverpath, "\\print$\\WIN40\\0\\", 0);
+ trim_string(driver.info_3->datafile, "\\print$\\WIN40\\0\\", 0);
+ trim_string(driver.info_3->helpfile, "\\print$\\WIN40\\0\\", 0);
+
+ PACKI(desc, "W", 0x0400); /* don't know */
+ PACKS(desc, "z", driver.info_3->name); /* long printer name */
+ PACKS(desc, "z", driver.info_3->driverpath); /* Driverfile Name */
+ PACKS(desc, "z", driver.info_3->datafile); /* Datafile name */
+ PACKS(desc, "z", driver.info_3->monitorname); /* language monitor */
+
+ fstrcpy(location, "\\\\");
+ fstrcat(location, get_called_name());
+ fstrcat(location, "\\print$\\WIN40\\0");
+ PACKS(desc,"z", location); /* share to retrieve files */
+
+ PACKS(desc,"z", driver.info_3->defaultdatatype); /* default data type */
+ PACKS(desc,"z", driver.info_3->helpfile); /* helpfile name */
+ PACKS(desc,"z", driver.info_3->driverpath); /* driver name */
+
+ DEBUG(3,("Printer Driver Name: %s:\n",driver.info_3->name));
+ DEBUG(3,("Driver: %s:\n",driver.info_3->driverpath));
+ DEBUG(3,("Data File: %s:\n",driver.info_3->datafile));
+ DEBUG(3,("Language Monitor: %s:\n",driver.info_3->monitorname));
+ DEBUG(3,("Driver Location: %s:\n",location));
+ DEBUG(3,("Data Type: %s:\n",driver.info_3->defaultdatatype));
+ DEBUG(3,("Help File: %s:\n",driver.info_3->helpfile));
+ PACKI(desc,"N",count); /* number of files to copy */
+
+ for ( i=0; i<count && driver.info_3->dependentfiles && *driver.info_3->dependentfiles[i]; i++)
+ {
+ trim_string(driver.info_3->dependentfiles[i], "\\print$\\WIN40\\0\\", 0);
+ PACKS(desc,"z",driver.info_3->dependentfiles[i]); /* driver files to copy */
+ DEBUG(3,("Dependent File: %s:\n",driver.info_3->dependentfiles[i]));
+ }
+
+ /* sanity check */
+ if ( i != count )
+ DEBUG(3,("fill_printq_info_52: file count specified by client [%d] != number of dependent files [%i]\n",
+ count, i));
+
+ DEBUG(3,("fill_printq_info on <%s> gave %d entries\n", SERVICE(snum),i));
+
+ desc->errcode=NERR_Success;
+ goto done;
+
+err:
+ DEBUG(3,("fill_printq_info: Can't supply driver files\n"));
+ desc->errcode=NERR_notsupported;
+
+done:
+ if ( printer )
+ free_a_printer( &printer, 2 );
+
+ if ( driver.info_3 )
+ free_a_printer_driver( driver, 3 );
+}
+
+
+static void fill_printq_info(connection_struct *conn, int snum, int uLevel,
+ struct pack_desc* desc,
+ int count, print_queue_struct* queue,
+ print_status_struct* status)
+{
+ switch (uLevel) {
+ case 1:
+ case 2:
+ PACKS(desc,"B13",SERVICE(snum));
+ break;
+ case 3:
+ case 4:
+ case 5:
+ PACKS(desc,"z",Expand(conn,snum,SERVICE(snum)));
+ break;
+ case 51:
+ PACKI(desc,"K",printq_status(status->status));
+ break;
+ }
+
+ if (uLevel == 1 || uLevel == 2) {
+ PACKS(desc,"B",""); /* alignment */
+ PACKI(desc,"W",5); /* priority */
+ PACKI(desc,"W",0); /* start time */
+ PACKI(desc,"W",0); /* until time */
+ PACKS(desc,"z",""); /* pSepFile */
+ PACKS(desc,"z","lpd"); /* pPrProc */
+ PACKS(desc,"z",SERVICE(snum)); /* pDestinations */
+ PACKS(desc,"z",""); /* pParms */
+ if (snum < 0) {
+ PACKS(desc,"z","UNKNOWN PRINTER");
+ PACKI(desc,"W",LPSTAT_ERROR);
+ }
+ else if (!status || !status->message[0]) {
+ PACKS(desc,"z",Expand(conn,snum,lp_comment(snum)));
+ PACKI(desc,"W",LPSTAT_OK); /* status */
+ } else {
+ PACKS(desc,"z",status->message);
+ PACKI(desc,"W",printq_status(status->status)); /* status */
+ }
+ PACKI(desc,(uLevel == 1 ? "W" : "N"),count);
+ }
+
+ if (uLevel == 3 || uLevel == 4) {
+ pstring drivername;
+
+ PACKI(desc,"W",5); /* uPriority */
+ PACKI(desc,"W",0); /* uStarttime */
+ PACKI(desc,"W",0); /* uUntiltime */
+ PACKI(desc,"W",5); /* pad1 */
+ PACKS(desc,"z",""); /* pszSepFile */
+ PACKS(desc,"z","WinPrint"); /* pszPrProc */
+ PACKS(desc,"z",NULL); /* pszParms */
+ PACKS(desc,"z",NULL); /* pszComment - don't ask.... JRA */
+ /* "don't ask" that it's done this way to fix corrupted
+ Win9X/ME printer comments. */
+ if (!status) {
+ PACKI(desc,"W",LPSTAT_OK); /* fsStatus */
+ } else {
+ PACKI(desc,"W",printq_status(status->status)); /* fsStatus */
+ }
+ PACKI(desc,(uLevel == 3 ? "W" : "N"),count); /* cJobs */
+ PACKS(desc,"z",SERVICE(snum)); /* pszPrinters */
+ get_driver_name(snum,drivername);
+ PACKS(desc,"z",drivername); /* pszDriverName */
+ PackDriverData(desc); /* pDriverData */
+ }
+
+ if (uLevel == 2 || uLevel == 4) {
+ int i;
+ for (i=0;i<count;i++)
+ fill_printjob_info(conn,snum,uLevel == 2 ? 1 : 2,desc,&queue[i],i);
+ }
+
+ if (uLevel==52)
+ fill_printq_info_52( conn, snum, desc, count );
+}
+
+/* This function returns the number of files for a given driver */
+static int get_printerdrivernumber(int snum)
+{
+ int result = 0;
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ ZERO_STRUCT(driver);
+
+ if ( !W_ERROR_IS_OK(get_a_printer( NULL, &printer, 2, lp_servicename(snum))) ) {
+ DEBUG(3,("get_printerdrivernumber: Failed to lookup printer [%s]\n",
+ lp_servicename(snum)));
+ goto done;
+ }
+
+ if ( !W_ERROR_IS_OK(get_a_printer_driver(&driver, 3, printer->info_2->drivername,
+ "Windows 4.0", 0)) )
+ {
+ DEBUG(3,("get_printerdrivernumber: Failed to lookup driver [%s]\n",
+ printer->info_2->drivername));
+ goto done;
+ }
+
+ /* count the number of files */
+ while ( driver.info_3->dependentfiles && *driver.info_3->dependentfiles[result] )
+ result++;
+ \
+ done:
+ if ( printer )
+ free_a_printer( &printer, 2 );
+
+ if ( driver.info_3 )
+ free_a_printer_driver( driver, 3 );
+
+ return result;
+}
+
+static BOOL api_DosPrintQGetInfo(connection_struct *conn,
+ uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ char *QueueName = p;
+ int uLevel;
+ int count=0;
+ int snum;
+ char* str3;
+ struct pack_desc desc;
+ print_queue_struct *queue=NULL;
+ print_status_struct status;
+ char* tmpdata=NULL;
+
+ memset((char *)&status,'\0',sizeof(status));
+ memset((char *)&desc,'\0',sizeof(desc));
+
+ p = skip_string(p,1);
+ uLevel = SVAL(p,0);
+ str3 = p + 4;
+
+ /* remove any trailing username */
+ if ((p = strchr_m(QueueName,'%')))
+ *p = 0;
+
+ DEBUG(3,("api_DosPrintQGetInfo uLevel=%d name=%s\n",uLevel,QueueName));
+
+ /* check it's a supported varient */
+ if (!prefix_ok(str1,"zWrLh"))
+ return False;
+ if (!check_printq_info(&desc,uLevel,str2,str3)) {
+ /*
+ * Patch from Scott Moomaw <scott@bridgewater.edu>
+ * to return the 'invalid info level' error if an
+ * unknown level was requested.
+ */
+ *rdata_len = 0;
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,ERRunknownlevel);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,0);
+ return(True);
+ }
+
+ snum = lp_servicenumber(QueueName);
+ if (snum < 0 && pcap_printername_ok(QueueName,NULL)) {
+ int pnum = lp_servicenumber(PRINTERS_NAME);
+ if (pnum >= 0) {
+ lp_add_printer(QueueName,pnum);
+ snum = lp_servicenumber(QueueName);
+ }
+ }
+
+ if (snum < 0 || !VALID_SNUM(snum))
+ return(False);
+
+ if (uLevel==52) {
+ count = get_printerdrivernumber(snum);
+ DEBUG(3,("api_DosPrintQGetInfo: Driver files count: %d\n",count));
+ } else {
+ count = print_queue_status(snum, &queue,&status);
+ }
+
+ if (mdrcnt > 0) {
+ *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ } else {
+ /*
+ * Don't return data but need to get correct length
+ * init_package will return wrong size if buflen=0
+ */
+ desc.buflen = getlen(desc.format);
+ desc.base = tmpdata = (char *) malloc (desc.buflen);
+ }
+
+ if (init_package(&desc,1,count)) {
+ desc.subcount = count;
+ fill_printq_info(conn,snum,uLevel,&desc,count,queue,&status);
+ }
+
+ *rdata_len = desc.usedlen;
+
+ /*
+ * We must set the return code to ERRbuftoosmall
+ * in order to support lanman style printing with Win NT/2k
+ * clients --jerry
+ */
+ if (!mdrcnt && lp_disable_spoolss())
+ desc.errcode = ERRbuftoosmall;
+
+ *rdata_len = desc.usedlen;
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,desc.neededlen);
+
+ DEBUG(4,("printqgetinfo: errorcode %d\n",desc.errcode));
+
+ SAFE_FREE(queue);
+ SAFE_FREE(tmpdata);
+
+ return(True);
+}
+
+/****************************************************************************
+ View list of all print jobs on all queues.
+****************************************************************************/
+
+static BOOL api_DosPrintQEnum(connection_struct *conn, uint16 vuid, char* param, char* data,
+ int mdrcnt, int mprcnt,
+ char **rdata, char** rparam,
+ int *rdata_len, int *rparam_len)
+{
+ char *param_format = param+2;
+ char *output_format1 = skip_string(param_format,1);
+ char *p = skip_string(output_format1,1);
+ int uLevel = SVAL(p,0);
+ char *output_format2 = p + 4;
+ int services = lp_numservices();
+ int i, n;
+ struct pack_desc desc;
+ print_queue_struct **queue = NULL;
+ print_status_struct *status = NULL;
+ int* subcntarr = NULL;
+ int queuecnt, subcnt=0, succnt=0;
+
+ memset((char *)&desc,'\0',sizeof(desc));
+
+ DEBUG(3,("DosPrintQEnum uLevel=%d\n",uLevel));
+
+ if (!prefix_ok(param_format,"WrLeh")) return False;
+ if (!check_printq_info(&desc,uLevel,output_format1,output_format2)) {
+ /*
+ * Patch from Scott Moomaw <scott@bridgewater.edu>
+ * to return the 'invalid info level' error if an
+ * unknown level was requested.
+ */
+ *rdata_len = 0;
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,ERRunknownlevel);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,0);
+ return(True);
+ }
+
+ queuecnt = 0;
+ for (i = 0; i < services; i++)
+ if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i))
+ queuecnt++;
+ if (uLevel > 0) {
+ if((queue = (print_queue_struct**)malloc(queuecnt*sizeof(print_queue_struct*))) == NULL) {
+ DEBUG(0,("api_DosPrintQEnum: malloc fail !\n"));
+ return False;
+ }
+ memset(queue,0,queuecnt*sizeof(print_queue_struct*));
+ if((status = (print_status_struct*)malloc(queuecnt*sizeof(print_status_struct))) == NULL) {
+ DEBUG(0,("api_DosPrintQEnum: malloc fail !\n"));
+ return False;
+ }
+ memset(status,0,queuecnt*sizeof(print_status_struct));
+ if((subcntarr = (int*)malloc(queuecnt*sizeof(int))) == NULL) {
+ DEBUG(0,("api_DosPrintQEnum: malloc fail !\n"));
+ return False;
+ }
+ subcnt = 0;
+ n = 0;
+ for (i = 0; i < services; i++)
+ if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) {
+ subcntarr[n] = print_queue_status(i, &queue[n],&status[n]);
+ subcnt += subcntarr[n];
+ n++;
+ }
+ }
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+
+ if (init_package(&desc,queuecnt,subcnt)) {
+ n = 0;
+ succnt = 0;
+ for (i = 0; i < services; i++)
+ if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) {
+ fill_printq_info(conn,i,uLevel,&desc,subcntarr[n],queue[n],&status[n]);
+ n++;
+ if (desc.errcode == NERR_Success) succnt = n;
+ }
+ }
+
+ SAFE_FREE(subcntarr);
+
+ *rdata_len = desc.usedlen;
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,succnt);
+ SSVAL(*rparam,6,queuecnt);
+
+ for (i = 0; i < queuecnt; i++) {
+ if (queue) SAFE_FREE(queue[i]);
+ }
+
+ SAFE_FREE(queue);
+ SAFE_FREE(status);
+
+ return True;
+}
+
+/****************************************************************************
+ get info level for a server list query
+ ****************************************************************************/
+static BOOL check_server_info(int uLevel, char* id)
+{
+ switch( uLevel ) {
+ case 0:
+ if (strcmp(id,"B16") != 0) return False;
+ break;
+ case 1:
+ if (strcmp(id,"B16BBDz") != 0) return False;
+ break;
+ default:
+ return False;
+ }
+ return True;
+}
+
+struct srv_info_struct
+{
+ fstring name;
+ uint32 type;
+ fstring comment;
+ fstring domain;
+ BOOL server_added;
+};
+
+
+/*******************************************************************
+ get server info lists from the files saved by nmbd. Return the
+ number of entries
+ ******************************************************************/
+static int get_server_info(uint32 servertype,
+ struct srv_info_struct **servers,
+ const char *domain)
+{
+ int count=0;
+ int alloced=0;
+ char **lines;
+ BOOL local_list_only;
+ int i;
+
+ lines = file_lines_load(lock_path(SERVER_LIST), NULL);
+ if (!lines) {
+ DEBUG(4,("Can't open %s - %s\n",lock_path(SERVER_LIST),strerror(errno)));
+ return(0);
+ }
+
+ /* request for everything is code for request all servers */
+ if (servertype == SV_TYPE_ALL)
+ servertype &= ~(SV_TYPE_DOMAIN_ENUM|SV_TYPE_LOCAL_LIST_ONLY);
+
+ local_list_only = (servertype & SV_TYPE_LOCAL_LIST_ONLY);
+
+ DEBUG(4,("Servertype search: %8x\n",servertype));
+
+ for (i=0;lines[i];i++) {
+ fstring stype;
+ struct srv_info_struct *s;
+ const char *ptr = lines[i];
+ BOOL ok = True;
+
+ if (!*ptr) continue;
+
+ if (count == alloced) {
+ struct srv_info_struct *ts;
+
+ alloced += 10;
+ ts = (struct srv_info_struct *)
+ Realloc(*servers,sizeof(**servers)*alloced);
+ if (!ts) {
+ DEBUG(0,("get_server_info: failed to enlarge servers info struct!\n"));
+ return(0);
+ }
+ else *servers = ts;
+ memset((char *)((*servers)+count),'\0',sizeof(**servers)*(alloced-count));
+ }
+ s = &(*servers)[count];
+
+ if (!next_token(&ptr,s->name , NULL, sizeof(s->name))) continue;
+ if (!next_token(&ptr,stype , NULL, sizeof(stype))) continue;
+ if (!next_token(&ptr,s->comment, NULL, sizeof(s->comment))) continue;
+ if (!next_token(&ptr,s->domain , NULL, sizeof(s->domain))) {
+ /* this allows us to cope with an old nmbd */
+ fstrcpy(s->domain,lp_workgroup());
+ }
+
+ if (sscanf(stype,"%X",&s->type) != 1) {
+ DEBUG(4,("r:host file "));
+ ok = False;
+ }
+
+ /* Filter the servers/domains we return based on what was asked for. */
+
+ /* Check to see if we are being asked for a local list only. */
+ if(local_list_only && ((s->type & SV_TYPE_LOCAL_LIST_ONLY) == 0)) {
+ DEBUG(4,("r: local list only"));
+ ok = False;
+ }
+
+ /* doesn't match up: don't want it */
+ if (!(servertype & s->type)) {
+ DEBUG(4,("r:serv type "));
+ ok = False;
+ }
+
+ if ((servertype & SV_TYPE_DOMAIN_ENUM) !=
+ (s->type & SV_TYPE_DOMAIN_ENUM))
+ {
+ DEBUG(4,("s: dom mismatch "));
+ ok = False;
+ }
+
+ if (!strequal(domain, s->domain) && !(servertype & SV_TYPE_DOMAIN_ENUM))
+ {
+ ok = False;
+ }
+
+ /* We should never return a server type with a SV_TYPE_LOCAL_LIST_ONLY set. */
+ s->type &= ~SV_TYPE_LOCAL_LIST_ONLY;
+
+ if (ok)
+ {
+ DEBUG(4,("**SV** %20s %8x %25s %15s\n",
+ s->name, s->type, s->comment, s->domain));
+
+ s->server_added = True;
+ count++;
+ }
+ else
+ {
+ DEBUG(4,("%20s %8x %25s %15s\n",
+ s->name, s->type, s->comment, s->domain));
+ }
+ }
+
+ file_lines_free(lines);
+ return(count);
+}
+
+
+/*******************************************************************
+ fill in a server info structure
+ ******************************************************************/
+static int fill_srv_info(struct srv_info_struct *service,
+ int uLevel, char **buf, int *buflen,
+ char **stringbuf, int *stringspace, char *baseaddr)
+{
+ int struct_len;
+ char* p;
+ char* p2;
+ int l2;
+ int len;
+
+ switch (uLevel) {
+ case 0: struct_len = 16; break;
+ case 1: struct_len = 26; break;
+ default: return -1;
+ }
+
+ if (!buf)
+ {
+ len = 0;
+ switch (uLevel)
+ {
+ case 1:
+ len = strlen(service->comment)+1;
+ break;
+ }
+
+ if (buflen) *buflen = struct_len;
+ if (stringspace) *stringspace = len;
+ return struct_len + len;
+ }
+
+ len = struct_len;
+ p = *buf;
+ if (*buflen < struct_len) return -1;
+ if (stringbuf)
+ {
+ p2 = *stringbuf;
+ l2 = *stringspace;
+ }
+ else
+ {
+ p2 = p + struct_len;
+ l2 = *buflen - struct_len;
+ }
+ if (!baseaddr) baseaddr = p;
+
+ switch (uLevel)
+ {
+ case 0:
+ push_ascii(p,service->name, 15, STR_TERMINATE);
+ break;
+
+ case 1:
+ push_ascii(p,service->name,15, STR_TERMINATE);
+ SIVAL(p,18,service->type);
+ SIVAL(p,22,PTR_DIFF(p2,baseaddr));
+ len += CopyAndAdvance(&p2,service->comment,&l2);
+ break;
+ }
+
+ if (stringbuf)
+ {
+ *buf = p + struct_len;
+ *buflen -= struct_len;
+ *stringbuf = p2;
+ *stringspace = l2;
+ }
+ else
+ {
+ *buf = p2;
+ *buflen -= len;
+ }
+ return len;
+}
+
+
+static BOOL srv_comp(struct srv_info_struct *s1,struct srv_info_struct *s2)
+{
+ return(strcmp(s1->name,s2->name));
+}
+
+/****************************************************************************
+ view list of servers available (or possibly domains). The info is
+ extracted from lists saved by nmbd on the local host
+ ****************************************************************************/
+static BOOL api_RNetServerEnum(connection_struct *conn, uint16 vuid, char *param, char *data,
+ int mdrcnt, int mprcnt, char **rdata,
+ char **rparam, int *rdata_len, int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel = SVAL(p,0);
+ int buf_len = SVAL(p,2);
+ uint32 servertype = IVAL(p,4);
+ char *p2;
+ int data_len, fixed_len, string_len;
+ int f_len = 0, s_len = 0;
+ struct srv_info_struct *servers=NULL;
+ int counted=0,total=0;
+ int i,missed;
+ fstring domain;
+ BOOL domain_request;
+ BOOL local_request;
+
+ /* If someone sets all the bits they don't really mean to set
+ DOMAIN_ENUM and LOCAL_LIST_ONLY, they just want all the
+ known servers. */
+
+ if (servertype == SV_TYPE_ALL)
+ servertype &= ~(SV_TYPE_DOMAIN_ENUM|SV_TYPE_LOCAL_LIST_ONLY);
+
+ /* If someone sets SV_TYPE_LOCAL_LIST_ONLY but hasn't set
+ any other bit (they may just set this bit on it's own) they
+ want all the locally seen servers. However this bit can be
+ set on its own so set the requested servers to be
+ ALL - DOMAIN_ENUM. */
+
+ if ((servertype & SV_TYPE_LOCAL_LIST_ONLY) && !(servertype & SV_TYPE_DOMAIN_ENUM))
+ servertype = SV_TYPE_ALL & ~(SV_TYPE_DOMAIN_ENUM);
+
+ domain_request = ((servertype & SV_TYPE_DOMAIN_ENUM) != 0);
+ local_request = ((servertype & SV_TYPE_LOCAL_LIST_ONLY) != 0);
+
+ p += 8;
+
+ if (!prefix_ok(str1,"WrLehD")) return False;
+ if (!check_server_info(uLevel,str2)) return False;
+
+ DEBUG(4, ("server request level: %s %8x ", str2, servertype));
+ DEBUG(4, ("domains_req:%s ", BOOLSTR(domain_request)));
+ DEBUG(4, ("local_only:%s\n", BOOLSTR(local_request)));
+
+ if (strcmp(str1, "WrLehDz") == 0) {
+ pull_ascii_fstring(domain, p);
+ } else {
+ fstrcpy(domain, lp_workgroup());
+ }
+
+ if (lp_browse_list())
+ total = get_server_info(servertype,&servers,domain);
+
+ data_len = fixed_len = string_len = 0;
+ missed = 0;
+
+ if (total > 0)
+ qsort(servers,total,sizeof(servers[0]),QSORT_CAST srv_comp);
+
+ {
+ char *lastname=NULL;
+
+ for (i=0;i<total;i++)
+ {
+ struct srv_info_struct *s = &servers[i];
+ if (lastname && strequal(lastname,s->name)) continue;
+ lastname = s->name;
+ data_len += fill_srv_info(s,uLevel,0,&f_len,0,&s_len,0);
+ DEBUG(4,("fill_srv_info %20s %8x %25s %15s\n",
+ s->name, s->type, s->comment, s->domain));
+
+ if (data_len <= buf_len) {
+ counted++;
+ fixed_len += f_len;
+ string_len += s_len;
+ } else {
+ missed++;
+ }
+ }
+ }
+
+ *rdata_len = fixed_len + string_len;
+ *rdata = REALLOC(*rdata,*rdata_len);
+ memset(*rdata,'\0',*rdata_len);
+
+ p2 = (*rdata) + fixed_len; /* auxilliary data (strings) will go here */
+ p = *rdata;
+ f_len = fixed_len;
+ s_len = string_len;
+
+ {
+ char *lastname=NULL;
+ int count2 = counted;
+ for (i = 0; i < total && count2;i++)
+ {
+ struct srv_info_struct *s = &servers[i];
+ if (lastname && strequal(lastname,s->name)) continue;
+ lastname = s->name;
+ fill_srv_info(s,uLevel,&p,&f_len,&p2,&s_len,*rdata);
+ DEBUG(4,("fill_srv_info %20s %8x %25s %15s\n",
+ s->name, s->type, s->comment, s->domain));
+ count2--;
+ }
+ }
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVAL(*rparam,0,(missed == 0 ? NERR_Success : ERRmoredata));
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,counted);
+ SSVAL(*rparam,6,counted+missed);
+
+ SAFE_FREE(servers);
+
+ DEBUG(3,("NetServerEnum domain = %s uLevel=%d counted=%d total=%d\n",
+ domain,uLevel,counted,counted+missed));
+
+ return(True);
+}
+
+/****************************************************************************
+ command 0x34 - suspected of being a "Lookup Names" stub api
+ ****************************************************************************/
+static BOOL api_RNetGroupGetUsers(connection_struct *conn, uint16 vuid, char *param, char *data,
+ int mdrcnt, int mprcnt, char **rdata,
+ char **rparam, int *rdata_len, int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel = SVAL(p,0);
+ int buf_len = SVAL(p,2);
+ int counted=0;
+ int missed=0;
+
+ DEBUG(5,("RNetGroupGetUsers: %s %s %s %d %d\n",
+ str1, str2, p, uLevel, buf_len));
+
+ if (!prefix_ok(str1,"zWrLeh")) return False;
+
+ *rdata_len = 0;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ SSVAL(*rparam,0,0x08AC); /* informational warning message */
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,counted);
+ SSVAL(*rparam,6,counted+missed);
+
+ return(True);
+}
+
+/****************************************************************************
+ get info about a share
+ ****************************************************************************/
+static BOOL check_share_info(int uLevel, char* id)
+{
+ switch( uLevel ) {
+ case 0:
+ if (strcmp(id,"B13") != 0) return False;
+ break;
+ case 1:
+ if (strcmp(id,"B13BWz") != 0) return False;
+ break;
+ case 2:
+ if (strcmp(id,"B13BWzWWWzB9B") != 0) return False;
+ break;
+ case 91:
+ if (strcmp(id,"B13BWzWWWzB9BB9BWzWWzWW") != 0) return False;
+ break;
+ default: return False;
+ }
+ return True;
+}
+
+static int fill_share_info(connection_struct *conn, int snum, int uLevel,
+ char** buf, int* buflen,
+ char** stringbuf, int* stringspace, char* baseaddr)
+{
+ int struct_len;
+ char* p;
+ char* p2;
+ int l2;
+ int len;
+
+ switch( uLevel ) {
+ case 0: struct_len = 13; break;
+ case 1: struct_len = 20; break;
+ case 2: struct_len = 40; break;
+ case 91: struct_len = 68; break;
+ default: return -1;
+ }
+
+
+ if (!buf)
+ {
+ len = 0;
+ if (uLevel > 0) len += StrlenExpanded(conn,snum,lp_comment(snum));
+ if (uLevel > 1) len += strlen(lp_pathname(snum)) + 1;
+ if (buflen) *buflen = struct_len;
+ if (stringspace) *stringspace = len;
+ return struct_len + len;
+ }
+
+ len = struct_len;
+ p = *buf;
+ if ((*buflen) < struct_len) return -1;
+ if (stringbuf)
+ {
+ p2 = *stringbuf;
+ l2 = *stringspace;
+ }
+ else
+ {
+ p2 = p + struct_len;
+ l2 = (*buflen) - struct_len;
+ }
+ if (!baseaddr) baseaddr = p;
+
+ push_ascii(p,lp_servicename(snum),13, STR_TERMINATE);
+
+ if (uLevel > 0)
+ {
+ int type;
+ SCVAL(p,13,0);
+ type = STYPE_DISKTREE;
+ if (lp_print_ok(snum)) type = STYPE_PRINTQ;
+ if (strequal("IPC",lp_fstype(snum))) type = STYPE_IPC;
+ SSVAL(p,14,type); /* device type */
+ SIVAL(p,16,PTR_DIFF(p2,baseaddr));
+ len += CopyExpanded(conn,snum,&p2,lp_comment(snum),&l2);
+ }
+
+ if (uLevel > 1)
+ {
+ SSVAL(p,20,ACCESS_READ|ACCESS_WRITE|ACCESS_CREATE); /* permissions */
+ SSVALS(p,22,-1); /* max uses */
+ SSVAL(p,24,1); /* current uses */
+ SIVAL(p,26,PTR_DIFF(p2,baseaddr)); /* local pathname */
+ len += CopyAndAdvance(&p2,lp_pathname(snum),&l2);
+ memset(p+30,0,SHPWLEN+2); /* passwd (reserved), pad field */
+ }
+
+ if (uLevel > 2)
+ {
+ memset(p+40,0,SHPWLEN+2);
+ SSVAL(p,50,0);
+ SIVAL(p,52,0);
+ SSVAL(p,56,0);
+ SSVAL(p,58,0);
+ SIVAL(p,60,0);
+ SSVAL(p,64,0);
+ SSVAL(p,66,0);
+ }
+
+ if (stringbuf)
+ {
+ (*buf) = p + struct_len;
+ (*buflen) -= struct_len;
+ (*stringbuf) = p2;
+ (*stringspace) = l2;
+ }
+ else
+ {
+ (*buf) = p2;
+ (*buflen) -= len;
+ }
+ return len;
+}
+
+static BOOL api_RNetShareGetInfo(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *netname = skip_string(str2,1);
+ char *p = skip_string(netname,1);
+ int uLevel = SVAL(p,0);
+ int snum = find_service(netname);
+
+ if (snum < 0) return False;
+
+ /* check it's a supported varient */
+ if (!prefix_ok(str1,"zWrLh")) return False;
+ if (!check_share_info(uLevel,str2)) return False;
+
+ *rdata = REALLOC(*rdata,mdrcnt);
+ p = *rdata;
+ *rdata_len = fill_share_info(conn,snum,uLevel,&p,&mdrcnt,0,0,0);
+ if (*rdata_len < 0) return False;
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+ SSVAL(*rparam,4,*rdata_len);
+
+ return(True);
+}
+
+/****************************************************************************
+ View the list of available shares.
+
+ This function is the server side of the NetShareEnum() RAP call.
+ It fills the return buffer with share names and share comments.
+ Note that the return buffer normally (in all known cases) allows only
+ twelve byte strings for share names (plus one for a nul terminator).
+ Share names longer than 12 bytes must be skipped.
+ ****************************************************************************/
+static BOOL api_RNetShareEnum( connection_struct *conn,
+ uint16 vuid,
+ char *param,
+ char *data,
+ int mdrcnt,
+ int mprcnt,
+ char **rdata,
+ char **rparam,
+ int *rdata_len,
+ int *rparam_len )
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel = SVAL(p,0);
+ int buf_len = SVAL(p,2);
+ char *p2;
+ int count=lp_numservices();
+ int total=0,counted=0;
+ BOOL missed = False;
+ int i;
+ int data_len, fixed_len, string_len;
+ int f_len = 0, s_len = 0;
+
+ if (!prefix_ok(str1,"WrLeh")) return False;
+ if (!check_share_info(uLevel,str2)) return False;
+
+ data_len = fixed_len = string_len = 0;
+ for (i=0;i<count;i++)
+ if( lp_browseable( i )
+ && lp_snum_ok( i )
+ && (strlen( lp_servicename( i ) ) < 13) ) /* Maximum name length. */
+ {
+ total++;
+ data_len += fill_share_info(conn,i,uLevel,0,&f_len,0,&s_len,0);
+ if (data_len <= buf_len)
+ {
+ counted++;
+ fixed_len += f_len;
+ string_len += s_len;
+ }
+ else
+ missed = True;
+ }
+ *rdata_len = fixed_len + string_len;
+ *rdata = REALLOC(*rdata,*rdata_len);
+ memset(*rdata,0,*rdata_len);
+
+ p2 = (*rdata) + fixed_len; /* auxiliary data (strings) will go here */
+ p = *rdata;
+ f_len = fixed_len;
+ s_len = string_len;
+ for( i = 0; i < count; i++ )
+ {
+ if( lp_browseable( i )
+ && lp_snum_ok( i )
+ && (strlen( lp_servicename( i ) ) < 13) )
+ {
+ if( fill_share_info( conn,i,uLevel,&p,&f_len,&p2,&s_len,*rdata ) < 0 )
+ break;
+ }
+ }
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVAL(*rparam,0,missed ? ERRmoredata : NERR_Success);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,counted);
+ SSVAL(*rparam,6,total);
+
+ DEBUG(3,("RNetShareEnum gave %d entries of %d (%d %d %d %d)\n",
+ counted,total,uLevel,
+ buf_len,*rdata_len,mdrcnt));
+ return(True);
+} /* api_RNetShareEnum */
+
+/****************************************************************************
+ Add a share
+ ****************************************************************************/
+static BOOL api_RNetShareAdd(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel = SVAL(p,0);
+ fstring sharename;
+ fstring comment;
+ pstring pathname;
+ char *command, *cmdname;
+ unsigned int offset;
+ int snum;
+ int res = ERRunsup;
+
+ /* check it's a supported varient */
+ if (!prefix_ok(str1, RAP_WShareAdd_REQ)) return False;
+ if (!check_share_info(uLevel, str2)) return False;
+ if (uLevel != 2) return False;
+
+ pull_ascii_fstring(sharename, data);
+ snum = find_service(sharename);
+ if (snum >= 0) { /* already exists */
+ res = ERRfilexists;
+ goto error_exit;
+ }
+
+ /* only support disk share adds */
+ if (SVAL(data,14) != STYPE_DISKTREE) return False;
+
+ offset = IVAL(data, 16);
+ if (offset >= mdrcnt) {
+ res = ERRinvalidparam;
+ goto error_exit;
+ }
+ pull_ascii_fstring(comment, offset? (data+offset) : "");
+
+ offset = IVAL(data, 26);
+ if (offset >= mdrcnt) {
+ res = ERRinvalidparam;
+ goto error_exit;
+ }
+ pull_ascii_pstring(pathname, offset? (data+offset) : "");
+
+ string_replace(sharename, '"', ' ');
+ string_replace(pathname, '"', ' ');
+ string_replace(comment, '"', ' ');
+
+ cmdname = lp_add_share_cmd();
+
+ if (!cmdname || *cmdname == '\0') return False;
+
+ asprintf(&command, "%s \"%s\" \"%s\" \"%s\" \"%s\"",
+ lp_add_share_cmd(), dyn_CONFIGFILE, sharename, pathname, comment);
+
+ if (command) {
+ DEBUG(10,("api_RNetShareAdd: Running [%s]\n", command ));
+ if ((res = smbrun(command, NULL)) != 0) {
+ DEBUG(1,("api_RNetShareAdd: Running [%s] returned (%d)\n", command, res ));
+ SAFE_FREE(command);
+ res = ERRnoaccess;
+ goto error_exit;
+ } else {
+ SAFE_FREE(command);
+ message_send_all(conn_tdb_ctx(), MSG_SMB_CONF_UPDATED, NULL, 0, False, NULL);
+ }
+ } else return False;
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam, *rparam_len);
+ SSVAL(*rparam, 0, NERR_Success);
+ SSVAL(*rparam, 2, 0); /* converter word */
+ SSVAL(*rparam, 4, *rdata_len);
+ *rdata_len = 0;
+
+ return True;
+
+error_exit:
+ *rparam_len = 4;
+ *rparam = REALLOC(*rparam, *rparam_len);
+ *rdata_len = 0;
+ SSVAL(*rparam, 0, res);
+ SSVAL(*rparam, 2, 0);
+ return True;
+
+}
+
+/****************************************************************************
+ view list of groups available
+ ****************************************************************************/
+static BOOL api_RNetGroupEnum(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ int i;
+ int errflags=0;
+ int resume_context, cli_buf_size;
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ BOOL ret;
+
+ GROUP_MAP *group_list;
+ int num_entries;
+
+ if (strcmp(str1,"WrLeh") != 0)
+ return False;
+
+ /* parameters
+ * W-> resume context (number of users to skip)
+ * r -> return parameter pointer to receive buffer
+ * L -> length of receive buffer
+ * e -> return parameter number of entries
+ * h -> return parameter total number of users
+ */
+ if (strcmp("B21",str2) != 0)
+ return False;
+
+ /* get list of domain groups SID_DOMAIN_GRP=2 */
+ become_root();
+ ret = pdb_enum_group_mapping(SID_NAME_DOM_GRP , &group_list, &num_entries, False);
+ unbecome_root();
+
+ if( !ret ) {
+ DEBUG(3,("api_RNetGroupEnum:failed to get group list"));
+ return False;
+ }
+
+ resume_context = SVAL(p,0);
+ cli_buf_size=SVAL(p+2,0);
+ DEBUG(10,("api_RNetGroupEnum:resume context: %d, client buffer size: %d\n", resume_context, cli_buf_size));
+
+ *rdata_len = cli_buf_size;
+ *rdata = REALLOC(*rdata,*rdata_len);
+
+ p = *rdata;
+
+ for(i=resume_context; i<num_entries; i++) {
+ char* name=group_list[i].nt_name;
+ if( ((PTR_DIFF(p,*rdata)+21) <= *rdata_len) ) {
+ /* truncate the name at 21 chars. */
+ memcpy(p, name, 21);
+ DEBUG(10,("adding entry %d group %s\n", i, p));
+ p += 21;
+ } else {
+ /* set overflow error */
+ DEBUG(3,("overflow on entry %d group %s\n", i, name));
+ errflags=234;
+ break;
+ }
+ }
+
+ *rdata_len = PTR_DIFF(p,*rdata);
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ SSVAL(*rparam, 0, errflags);
+ SSVAL(*rparam, 2, 0); /* converter word */
+ SSVAL(*rparam, 4, i-resume_context); /* is this right?? */
+ SSVAL(*rparam, 6, num_entries); /* is this right?? */
+
+ return(True);
+}
+
+/*******************************************************************
+ get groups that a user is a member of
+ ******************************************************************/
+static BOOL api_NetUserGetGroups(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *UserName = skip_string(str2,1);
+ char *p = skip_string(UserName,1);
+ int uLevel = SVAL(p,0);
+ const char *level_string;
+ int count=0;
+ SAM_ACCOUNT *sampw = NULL;
+ BOOL ret = False;
+ DOM_GID *gids = NULL;
+ int num_groups = 0;
+ int i;
+ fstring grp_domain;
+ fstring grp_name;
+ enum SID_NAME_USE grp_type;
+ DOM_SID sid, dom_sid;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ /* check it's a supported varient */
+
+ if ( strcmp(str1,"zWrLeh") != 0 )
+ return False;
+
+ switch( uLevel ) {
+ case 0:
+ level_string = "B21";
+ break;
+ default:
+ return False;
+ }
+
+ if (strcmp(level_string,str2) != 0)
+ return False;
+
+ *rdata_len = mdrcnt + 1024;
+ *rdata = REALLOC(*rdata,*rdata_len);
+
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ p = *rdata;
+
+ /* Lookup the user information; This should only be one of
+ our accounts (not remote domains) */
+
+ pdb_init_sam( &sampw );
+
+ become_root(); /* ROOT BLOCK */
+
+ if ( !pdb_getsampwnam(sampw, UserName) )
+ goto out;
+
+ /* this next set of code is horribly inefficient, but since
+ it is rarely called, I'm going to leave it like this since
+ it easier to follow --jerry */
+
+ /* get the list of group SIDs */
+
+ if ( !get_domain_user_groups(conn->mem_ctx, &num_groups, &gids, sampw) ) {
+ DEBUG(1,("api_NetUserGetGroups: get_domain_user_groups() failed!\n"));
+ goto out;
+ }
+
+ /* convert to names (we don't support universal groups so the domain
+ can only be ours) */
+
+ sid_copy( &dom_sid, get_global_sam_sid() );
+ for (i=0; i<num_groups; i++) {
+
+ /* make the DOM_GID into a DOM_SID and then lookup
+ the name */
+
+ sid_copy( &sid, &dom_sid );
+ sid_append_rid( &sid, gids[i].g_rid );
+
+ if ( lookup_sid(&sid, grp_domain, grp_name, &grp_type) ) {
+ pstrcpy(p, grp_name);
+ p += 21;
+ count++;
+ }
+ }
+
+ *rdata_len = PTR_DIFF(p,*rdata);
+
+ SSVAL(*rparam,4,count); /* is this right?? */
+ SSVAL(*rparam,6,count); /* is this right?? */
+
+ ret = True;
+
+out:
+ unbecome_root(); /* END ROOT BLOCK */
+
+ pdb_free_sam( &sampw );
+
+ return ret;
+}
+
+/*******************************************************************
+ get all users
+ ******************************************************************/
+static BOOL api_RNetUserEnum(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ SAM_ACCOUNT *pwd=NULL;
+ int count_sent=0;
+ int count_total=0;
+ int errflags=0;
+ int resume_context, cli_buf_size;
+
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+
+ if (strcmp(str1,"WrLeh") != 0)
+ return False;
+ /* parameters
+ * W-> resume context (number of users to skip)
+ * r -> return parameter pointer to receive buffer
+ * L -> length of receive buffer
+ * e -> return parameter number of entries
+ * h -> return parameter total number of users
+ */
+
+ resume_context = SVAL(p,0);
+ cli_buf_size=SVAL(p+2,0);
+ DEBUG(10,("api_RNetUserEnum:resume context: %d, client buffer size: %d\n", resume_context, cli_buf_size));
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ /* check it's a supported varient */
+ if (strcmp("B21",str2) != 0)
+ return False;
+
+ *rdata_len = cli_buf_size;
+ *rdata = REALLOC(*rdata,*rdata_len);
+
+ p = *rdata;
+
+ /* to get user list enumerations for NetUserEnum in B21 format */
+ pdb_init_sam(&pwd);
+
+ /* Open the passgrp file - not for update. */
+ become_root();
+ if(!pdb_setsampwent(False)) {
+ DEBUG(0, ("api_RNetUserEnum:unable to open sam database.\n"));
+ unbecome_root();
+ return False;
+ }
+ errflags=NERR_Success;
+
+ while ( pdb_getsampwent(pwd) ) {
+ const char *name=pdb_get_username(pwd);
+ if ((name) && (*(name+strlen(name)-1)!='$')) {
+ count_total++;
+ if(count_total>=resume_context) {
+ if( ((PTR_DIFF(p,*rdata)+21)<=*rdata_len)&&(strlen(name)<=21) ) {
+ pstrcpy(p,name);
+ DEBUG(10,("api_RNetUserEnum:adding entry %d username %s\n",count_sent,p));
+ p += 21;
+ count_sent++;
+ } else {
+ /* set overflow error */
+ DEBUG(10,("api_RNetUserEnum:overflow on entry %d username %s\n",count_sent,name));
+ errflags=234;
+ break;
+ }
+ }
+ }
+ } ;
+
+ pdb_endsampwent();
+ unbecome_root();
+
+ pdb_free_sam(&pwd);
+
+ *rdata_len = PTR_DIFF(p,*rdata);
+
+ SSVAL(*rparam,0,errflags);
+ SSVAL(*rparam,2,0); /* converter word */
+ SSVAL(*rparam,4,count_sent); /* is this right?? */
+ SSVAL(*rparam,6,count_total); /* is this right?? */
+
+ return True;
+}
+
+
+
+/****************************************************************************
+ get the time of day info
+ ****************************************************************************/
+static BOOL api_NetRemoteTOD(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *p;
+ *rparam_len = 4;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ *rdata_len = 21;
+ *rdata = REALLOC(*rdata,*rdata_len);
+
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ p = *rdata;
+
+ {
+ struct tm *t;
+ time_t unixdate = time(NULL);
+
+ put_dos_date3(p,0,unixdate); /* this is the time that is looked at
+ by NT in a "net time" operation,
+ it seems to ignore the one below */
+
+ /* the client expects to get localtime, not GMT, in this bit
+ (I think, this needs testing) */
+ t = LocalTime(&unixdate);
+
+ SIVAL(p,4,0); /* msecs ? */
+ SCVAL(p,8,t->tm_hour);
+ SCVAL(p,9,t->tm_min);
+ SCVAL(p,10,t->tm_sec);
+ SCVAL(p,11,0); /* hundredths of seconds */
+ SSVALS(p,12,TimeDiff(unixdate)/60); /* timezone in minutes from GMT */
+ SSVAL(p,14,10000); /* timer interval in 0.0001 of sec */
+ SCVAL(p,16,t->tm_mday);
+ SCVAL(p,17,t->tm_mon + 1);
+ SSVAL(p,18,1900+t->tm_year);
+ SCVAL(p,20,t->tm_wday);
+ }
+
+
+ return(True);
+}
+
+/****************************************************************************
+ Set the user password.
+*****************************************************************************/
+
+static BOOL api_SetUserPassword(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *p = skip_string(param+2,2);
+ fstring user;
+ fstring pass1,pass2;
+
+ pull_ascii_fstring(user,p);
+
+ p = skip_string(p,1);
+
+ memset(pass1,'\0',sizeof(pass1));
+ memset(pass2,'\0',sizeof(pass2));
+ memcpy(pass1,p,16);
+ memcpy(pass2,p+16,16);
+
+ *rparam_len = 4;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ *rdata_len = 0;
+
+ SSVAL(*rparam,0,NERR_badpass);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ DEBUG(3,("Set password for <%s>\n",user));
+
+ /*
+ * Attempt to verify the old password against smbpasswd entries
+ * Win98 clients send old and new password in plaintext for this call.
+ */
+
+ {
+ auth_serversupplied_info *server_info = NULL;
+ DATA_BLOB password = data_blob(pass1, strlen(pass1)+1);
+
+ if (NT_STATUS_IS_OK(check_plaintext_password(user,password,&server_info))) {
+
+ become_root();
+ if (NT_STATUS_IS_OK(change_oem_password(server_info->sam_account, pass1, pass2, False))) {
+ SSVAL(*rparam,0,NERR_Success);
+ }
+ unbecome_root();
+
+ free_server_info(&server_info);
+ }
+ data_blob_clear_free(&password);
+ }
+
+ /*
+ * If the plaintext change failed, attempt
+ * the old encrypted method. NT will generate this
+ * after trying the samr method. Note that this
+ * method is done as a last resort as this
+ * password change method loses the NT password hash
+ * and cannot change the UNIX password as no plaintext
+ * is received.
+ */
+
+ if(SVAL(*rparam,0) != NERR_Success) {
+ SAM_ACCOUNT *hnd = NULL;
+
+ if (check_lanman_password(user,(unsigned char *)pass1,(unsigned char *)pass2, &hnd)) {
+ become_root();
+ if (change_lanman_password(hnd,(uchar *)pass2)) {
+ SSVAL(*rparam,0,NERR_Success);
+ }
+ unbecome_root();
+ pdb_free_sam(&hnd);
+ }
+ }
+
+ memset((char *)pass1,'\0',sizeof(fstring));
+ memset((char *)pass2,'\0',sizeof(fstring));
+
+ return(True);
+}
+
+/****************************************************************************
+ Set the user password (SamOEM version - gets plaintext).
+****************************************************************************/
+
+static BOOL api_SamOEMChangePassword(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ fstring user;
+ char *p = param + 2;
+ *rparam_len = 2;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ *rdata_len = 0;
+
+ SSVAL(*rparam,0,NERR_badpass);
+
+ /*
+ * Check the parameter definition is correct.
+ */
+
+ if(!strequal(param + 2, "zsT")) {
+ DEBUG(0,("api_SamOEMChangePassword: Invalid parameter string %s\n", param + 2));
+ return False;
+ }
+ p = skip_string(p, 1);
+
+ if(!strequal(p, "B516B16")) {
+ DEBUG(0,("api_SamOEMChangePassword: Invalid data parameter string %s\n", p));
+ return False;
+ }
+ p = skip_string(p,1);
+ p += pull_ascii_fstring(user,p);
+
+ DEBUG(3,("api_SamOEMChangePassword: Change password for <%s>\n",user));
+
+ /*
+ * Pass the user through the NT -> unix user mapping
+ * function.
+ */
+
+ (void)map_username(user);
+
+ if (NT_STATUS_IS_OK(pass_oem_change(user, (uchar*) data, (uchar *)&data[516], NULL, NULL))) {
+ SSVAL(*rparam,0,NERR_Success);
+ }
+
+ return(True);
+}
+
+/****************************************************************************
+ delete a print job
+ Form: <W> <>
+ ****************************************************************************/
+static BOOL api_RDosPrintJobDel(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ int function = SVAL(param,0);
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ uint32 jobid;
+ int snum;
+ int errcode;
+ extern struct current_user current_user;
+ WERROR werr = WERR_OK;
+
+ if(!rap_to_pjobid(SVAL(p,0),&snum,&jobid))
+ return False;
+
+ /* check it's a supported varient */
+ if (!(strcsequal(str1,"W") && strcsequal(str2,"")))
+ return(False);
+
+ *rparam_len = 4;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ *rdata_len = 0;
+
+ if (!print_job_exists(snum, jobid)) {
+ errcode = NERR_JobNotFound;
+ goto out;
+ }
+
+ errcode = NERR_notsupported;
+
+ switch (function) {
+ case 81: /* delete */
+ if (print_job_delete(&current_user, snum, jobid, &werr))
+ errcode = NERR_Success;
+ break;
+ case 82: /* pause */
+ if (print_job_pause(&current_user, snum, jobid, &werr))
+ errcode = NERR_Success;
+ break;
+ case 83: /* resume */
+ if (print_job_resume(&current_user, snum, jobid, &werr))
+ errcode = NERR_Success;
+ break;
+ }
+
+ if (!W_ERROR_IS_OK(werr))
+ errcode = W_ERROR_V(werr);
+
+ out:
+ SSVAL(*rparam,0,errcode);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ return(True);
+}
+
+/****************************************************************************
+ Purge a print queue - or pause or resume it.
+ ****************************************************************************/
+static BOOL api_WPrintQueueCtrl(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ int function = SVAL(param,0);
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *QueueName = skip_string(str2,1);
+ int errcode = NERR_notsupported;
+ int snum;
+ WERROR werr = WERR_OK;
+ extern struct current_user current_user;
+
+ /* check it's a supported varient */
+ if (!(strcsequal(str1,"z") && strcsequal(str2,"")))
+ return(False);
+
+ *rparam_len = 4;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ *rdata_len = 0;
+
+ snum = print_queue_snum(QueueName);
+
+ if (snum == -1) {
+ errcode = NERR_JobNotFound;
+ goto out;
+ }
+
+ switch (function) {
+ case 74: /* Pause queue */
+ if (print_queue_pause(&current_user, snum, &werr)) errcode = NERR_Success;
+ break;
+ case 75: /* Resume queue */
+ if (print_queue_resume(&current_user, snum, &werr)) errcode = NERR_Success;
+ break;
+ case 103: /* Purge */
+ if (print_queue_purge(&current_user, snum, &werr)) errcode = NERR_Success;
+ break;
+ }
+
+ if (!W_ERROR_IS_OK(werr)) errcode = W_ERROR_V(werr);
+
+ out:
+ SSVAL(*rparam,0,errcode);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ return(True);
+}
+
+
+/****************************************************************************
+ set the property of a print job (undocumented?)
+ ? function = 0xb -> set name of print job
+ ? function = 0x6 -> move print job up/down
+ Form: <WWsTP> <WWzWWDDzzzzzzzzzzlz>
+ or <WWsTP> <WB21BB16B10zWWzDDz>
+****************************************************************************/
+static int check_printjob_info(struct pack_desc* desc,
+ int uLevel, char* id)
+{
+ desc->subformat = NULL;
+ switch( uLevel ) {
+ case 0: desc->format = "W"; break;
+ case 1: desc->format = "WB21BB16B10zWWzDDz"; break;
+ case 2: desc->format = "WWzWWDDzz"; break;
+ case 3: desc->format = "WWzWWDDzzzzzzzzzzlz"; break;
+ case 4: desc->format = "WWzWWDDzzzzzDDDDDDD"; break;
+ default: return False;
+ }
+ if (strcmp(desc->format,id) != 0) return False;
+ return True;
+}
+
+static BOOL api_PrintJobInfo(connection_struct *conn,uint16 vuid,char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ struct pack_desc desc;
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ uint32 jobid;
+ int snum;
+ int uLevel = SVAL(p,2);
+ int function = SVAL(p,4);
+ int place, errcode;
+
+ if(!rap_to_pjobid(SVAL(p,0),&snum,&jobid))
+ return False;
+ *rparam_len = 4;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ *rdata_len = 0;
+
+ /* check it's a supported varient */
+ if ((strcmp(str1,"WWsTP")) ||
+ (!check_printjob_info(&desc,uLevel,str2)))
+ return(False);
+
+ if (!print_job_exists(snum, jobid)) {
+ errcode=NERR_JobNotFound;
+ goto out;
+ }
+
+ errcode = NERR_notsupported;
+
+ switch (function) {
+ case 0x6:
+ /* change job place in the queue,
+ data gives the new place */
+ place = SVAL(data,0);
+ if (print_job_set_place(snum, jobid, place)) {
+ errcode=NERR_Success;
+ }
+ break;
+
+ case 0xb:
+ /* change print job name, data gives the name */
+ if (print_job_set_name(snum, jobid, data)) {
+ errcode=NERR_Success;
+ }
+ break;
+
+ default:
+ return False;
+ }
+
+ out:
+ SSVALS(*rparam,0,errcode);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ return(True);
+}
+
+
+/****************************************************************************
+ get info about the server
+ ****************************************************************************/
+static BOOL api_RNetServerGetInfo(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel = SVAL(p,0);
+ char *p2;
+ int struct_len;
+
+ DEBUG(4,("NetServerGetInfo level %d\n",uLevel));
+
+ /* check it's a supported varient */
+ if (!prefix_ok(str1,"WrLh")) return False;
+ switch( uLevel ) {
+ case 0:
+ if (strcmp(str2,"B16") != 0) return False;
+ struct_len = 16;
+ break;
+ case 1:
+ if (strcmp(str2,"B16BBDz") != 0) return False;
+ struct_len = 26;
+ break;
+ case 2:
+ if (strcmp(str2,"B16BBDzDDDWWzWWWWWWWBB21zWWWWWWWWWWWWWWWWWWWWWWz")
+ != 0) return False;
+ struct_len = 134;
+ break;
+ case 3:
+ if (strcmp(str2,"B16BBDzDDDWWzWWWWWWWBB21zWWWWWWWWWWWWWWWWWWWWWWzDWz")
+ != 0) return False;
+ struct_len = 144;
+ break;
+ case 20:
+ if (strcmp(str2,"DN") != 0) return False;
+ struct_len = 6;
+ break;
+ case 50:
+ if (strcmp(str2,"B16BBDzWWzzz") != 0) return False;
+ struct_len = 42;
+ break;
+ default: return False;
+ }
+
+ *rdata_len = mdrcnt;
+ *rdata = REALLOC(*rdata,*rdata_len);
+
+ p = *rdata;
+ p2 = p + struct_len;
+ if (uLevel != 20) {
+ srvstr_push(NULL, p,local_machine,16,
+ STR_ASCII|STR_UPPER|STR_TERMINATE);
+ }
+ p += 16;
+ if (uLevel > 0)
+ {
+ struct srv_info_struct *servers=NULL;
+ int i,count;
+ pstring comment;
+ uint32 servertype= lp_default_server_announce();
+
+ push_ascii(comment,lp_serverstring(), MAX_SERVER_STRING_LENGTH,STR_TERMINATE);
+
+ if ((count=get_server_info(SV_TYPE_ALL,&servers,lp_workgroup()))>0) {
+ for (i=0;i<count;i++) {
+ if (strequal(servers[i].name,local_machine)) {
+ servertype = servers[i].type;
+ push_ascii(comment,servers[i].comment,sizeof(pstring),STR_TERMINATE);
+ }
+ }
+ }
+ SAFE_FREE(servers);
+
+ SCVAL(p,0,lp_major_announce_version());
+ SCVAL(p,1,lp_minor_announce_version());
+ SIVAL(p,2,servertype);
+
+ if (mdrcnt == struct_len) {
+ SIVAL(p,6,0);
+ } else {
+ SIVAL(p,6,PTR_DIFF(p2,*rdata));
+ standard_sub_conn(conn,comment,sizeof(comment));
+ StrnCpy(p2,comment,MAX(mdrcnt - struct_len,0));
+ p2 = skip_string(p2,1);
+ }
+ }
+ if (uLevel > 1)
+ {
+ return False; /* not yet implemented */
+ }
+
+ *rdata_len = PTR_DIFF(p2,*rdata);
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+ SSVAL(*rparam,4,*rdata_len);
+
+ return(True);
+}
+
+
+/****************************************************************************
+ get info about the server
+ ****************************************************************************/
+static BOOL api_NetWkstaGetInfo(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ char *p2;
+ extern userdom_struct current_user_info;
+ int level = SVAL(p,0);
+
+ DEBUG(4,("NetWkstaGetInfo level %d\n",level));
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ /* check it's a supported varient */
+ if (!(level==10 && strcsequal(str1,"WrLh") && strcsequal(str2,"zzzBBzz")))
+ return(False);
+
+ *rdata_len = mdrcnt + 1024;
+ *rdata = REALLOC(*rdata,*rdata_len);
+
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ p = *rdata;
+ p2 = p + 22;
+
+
+ SIVAL(p,0,PTR_DIFF(p2,*rdata)); /* host name */
+ pstrcpy(p2,local_machine);
+ strupper_m(p2);
+ p2 = skip_string(p2,1);
+ p += 4;
+
+ SIVAL(p,0,PTR_DIFF(p2,*rdata));
+ pstrcpy(p2,current_user_info.smb_name);
+ p2 = skip_string(p2,1);
+ p += 4;
+
+ SIVAL(p,0,PTR_DIFF(p2,*rdata)); /* login domain */
+ pstrcpy(p2,lp_workgroup());
+ strupper_m(p2);
+ p2 = skip_string(p2,1);
+ p += 4;
+
+ SCVAL(p,0,lp_major_announce_version()); /* system version - e.g 4 in 4.1 */
+ SCVAL(p,1,lp_minor_announce_version()); /* system version - e.g .1 in 4.1 */
+ p += 2;
+
+ SIVAL(p,0,PTR_DIFF(p2,*rdata));
+ pstrcpy(p2,lp_workgroup()); /* don't know. login domain?? */
+ p2 = skip_string(p2,1);
+ p += 4;
+
+ SIVAL(p,0,PTR_DIFF(p2,*rdata)); /* don't know */
+ pstrcpy(p2,"");
+ p2 = skip_string(p2,1);
+ p += 4;
+
+ *rdata_len = PTR_DIFF(p2,*rdata);
+
+ SSVAL(*rparam,4,*rdata_len);
+
+ return(True);
+}
+
+/****************************************************************************
+ get info about a user
+
+ struct user_info_11 {
+ char usri11_name[21]; 0-20
+ char usri11_pad; 21
+ char *usri11_comment; 22-25
+ char *usri11_usr_comment; 26-29
+ unsigned short usri11_priv; 30-31
+ unsigned long usri11_auth_flags; 32-35
+ long usri11_password_age; 36-39
+ char *usri11_homedir; 40-43
+ char *usri11_parms; 44-47
+ long usri11_last_logon; 48-51
+ long usri11_last_logoff; 52-55
+ unsigned short usri11_bad_pw_count; 56-57
+ unsigned short usri11_num_logons; 58-59
+ char *usri11_logon_server; 60-63
+ unsigned short usri11_country_code; 64-65
+ char *usri11_workstations; 66-69
+ unsigned long usri11_max_storage; 70-73
+ unsigned short usri11_units_per_week; 74-75
+ unsigned char *usri11_logon_hours; 76-79
+ unsigned short usri11_code_page; 80-81
+ };
+
+where:
+
+ usri11_name specifies the user name for which information is retireved
+
+ usri11_pad aligns the next data structure element to a word boundary
+
+ usri11_comment is a null terminated ASCII comment
+
+ usri11_user_comment is a null terminated ASCII comment about the user
+
+ usri11_priv specifies the level of the privilege assigned to the user.
+ The possible values are:
+
+Name Value Description
+USER_PRIV_GUEST 0 Guest privilege
+USER_PRIV_USER 1 User privilege
+USER_PRV_ADMIN 2 Administrator privilege
+
+ usri11_auth_flags specifies the account operator privileges. The
+ possible values are:
+
+Name Value Description
+AF_OP_PRINT 0 Print operator
+
+
+Leach, Naik [Page 28]
+
+
+
+INTERNET-DRAFT CIFS Remote Admin Protocol January 10, 1997
+
+
+AF_OP_COMM 1 Communications operator
+AF_OP_SERVER 2 Server operator
+AF_OP_ACCOUNTS 3 Accounts operator
+
+
+ usri11_password_age specifies how many seconds have elapsed since the
+ password was last changed.
+
+ usri11_home_dir points to a null terminated ASCII string that contains
+ the path name of the user's home directory.
+
+ usri11_parms points to a null terminated ASCII string that is set
+ aside for use by applications.
+
+ usri11_last_logon specifies the time when the user last logged on.
+ This value is stored as the number of seconds elapsed since
+ 00:00:00, January 1, 1970.
+
+ usri11_last_logoff specifies the time when the user last logged off.
+ This value is stored as the number of seconds elapsed since
+ 00:00:00, January 1, 1970. A value of 0 means the last logoff
+ time is unknown.
+
+ usri11_bad_pw_count specifies the number of incorrect passwords
+ entered since the last successful logon.
+
+ usri11_log1_num_logons specifies the number of times this user has
+ logged on. A value of -1 means the number of logons is unknown.
+
+ usri11_logon_server points to a null terminated ASCII string that
+ contains the name of the server to which logon requests are sent.
+ A null string indicates logon requests should be sent to the
+ domain controller.
+
+ usri11_country_code specifies the country code for the user's language
+ of choice.
+
+ usri11_workstations points to a null terminated ASCII string that
+ contains the names of workstations the user may log on from.
+ There may be up to 8 workstations, with the names separated by
+ commas. A null strings indicates there are no restrictions.
+
+ usri11_max_storage specifies the maximum amount of disk space the user
+ can occupy. A value of 0xffffffff indicates there are no
+ restrictions.
+
+ usri11_units_per_week specifies the equal number of time units into
+ which a week is divided. This value must be equal to 168.
+
+ usri11_logon_hours points to a 21 byte (168 bits) string that
+ specifies the time during which the user can log on. Each bit
+ represents one unique hour in a week. The first bit (bit 0, word
+ 0) is Sunday, 0:00 to 0:59, the second bit (bit 1, word 0) is
+
+
+
+Leach, Naik [Page 29]
+
+
+
+INTERNET-DRAFT CIFS Remote Admin Protocol January 10, 1997
+
+
+ Sunday, 1:00 to 1:59 and so on. A null pointer indicates there
+ are no restrictions.
+
+ usri11_code_page specifies the code page for the user's language of
+ choice
+
+All of the pointers in this data structure need to be treated
+specially. The pointer is a 32 bit pointer. The higher 16 bits need
+to be ignored. The converter word returned in the parameters section
+needs to be subtracted from the lower 16 bits to calculate an offset
+into the return buffer where this ASCII string resides.
+
+There is no auxiliary data in the response.
+
+ ****************************************************************************/
+
+#define usri11_name 0
+#define usri11_pad 21
+#define usri11_comment 22
+#define usri11_usr_comment 26
+#define usri11_full_name 30
+#define usri11_priv 34
+#define usri11_auth_flags 36
+#define usri11_password_age 40
+#define usri11_homedir 44
+#define usri11_parms 48
+#define usri11_last_logon 52
+#define usri11_last_logoff 56
+#define usri11_bad_pw_count 60
+#define usri11_num_logons 62
+#define usri11_logon_server 64
+#define usri11_country_code 68
+#define usri11_workstations 70
+#define usri11_max_storage 74
+#define usri11_units_per_week 78
+#define usri11_logon_hours 80
+#define usri11_code_page 84
+#define usri11_end 86
+
+#define USER_PRIV_GUEST 0
+#define USER_PRIV_USER 1
+#define USER_PRIV_ADMIN 2
+
+#define AF_OP_PRINT 0
+#define AF_OP_COMM 1
+#define AF_OP_SERVER 2
+#define AF_OP_ACCOUNTS 3
+
+
+static BOOL api_RNetUserGetInfo(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *UserName = skip_string(str2,1);
+ char *p = skip_string(UserName,1);
+ int uLevel = SVAL(p,0);
+ char *p2;
+ const char *level_string;
+
+ /* get NIS home of a previously validated user - simeon */
+ /* With share level security vuid will always be zero.
+ Don't depend on vuser being non-null !!. JRA */
+ user_struct *vuser = get_valid_user_struct(vuid);
+ if(vuser != NULL)
+ DEBUG(3,(" Username of UID %d is %s\n", (int)vuser->uid,
+ vuser->user.unix_name));
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ DEBUG(4,("RNetUserGetInfo level=%d\n", uLevel));
+
+ /* check it's a supported variant */
+ if (strcmp(str1,"zWrLh") != 0) return False;
+ switch( uLevel )
+ {
+ case 0: level_string = "B21"; break;
+ case 1: level_string = "B21BB16DWzzWz"; break;
+ case 2: level_string = "B21BB16DWzzWzDzzzzDDDDWb21WWzWW"; break;
+ case 10: level_string = "B21Bzzz"; break;
+ case 11: level_string = "B21BzzzWDDzzDDWWzWzDWb21W"; break;
+ default: return False;
+ }
+
+ if (strcmp(level_string,str2) != 0) return False;
+
+ *rdata_len = mdrcnt + 1024;
+ *rdata = REALLOC(*rdata,*rdata_len);
+
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ p = *rdata;
+ p2 = p + usri11_end;
+
+ memset(p,0,21);
+ fstrcpy(p+usri11_name,UserName); /* 21 bytes - user name */
+
+ if (uLevel > 0)
+ {
+ SCVAL(p,usri11_pad,0); /* padding - 1 byte */
+ *p2 = 0;
+ }
+ if (uLevel >= 10)
+ {
+ SIVAL(p,usri11_comment,PTR_DIFF(p2,p)); /* comment */
+ pstrcpy(p2,"Comment");
+ p2 = skip_string(p2,1);
+
+ SIVAL(p,usri11_usr_comment,PTR_DIFF(p2,p)); /* user_comment */
+ pstrcpy(p2,"UserComment");
+ p2 = skip_string(p2,1);
+
+ /* EEK! the cifsrap.txt doesn't have this in!!!! */
+ SIVAL(p,usri11_full_name,PTR_DIFF(p2,p)); /* full name */
+ pstrcpy(p2,((vuser != NULL) ? vuser->user.full_name : UserName));
+ p2 = skip_string(p2,1);
+ }
+
+ if (uLevel == 11) /* modelled after NTAS 3.51 reply */
+ {
+ SSVAL(p,usri11_priv,conn->admin_user?USER_PRIV_ADMIN:USER_PRIV_USER);
+ SIVAL(p,usri11_auth_flags,AF_OP_PRINT); /* auth flags */
+ SIVALS(p,usri11_password_age,-1); /* password age */
+ SIVAL(p,usri11_homedir,PTR_DIFF(p2,p)); /* home dir */
+ pstrcpy(p2, vuser && vuser->homedir ? vuser->homedir : "");
+ p2 = skip_string(p2,1);
+ SIVAL(p,usri11_parms,PTR_DIFF(p2,p)); /* parms */
+ pstrcpy(p2,"");
+ p2 = skip_string(p2,1);
+ SIVAL(p,usri11_last_logon,0); /* last logon */
+ SIVAL(p,usri11_last_logoff,0); /* last logoff */
+ SSVALS(p,usri11_bad_pw_count,-1); /* bad pw counts */
+ SSVALS(p,usri11_num_logons,-1); /* num logons */
+ SIVAL(p,usri11_logon_server,PTR_DIFF(p2,p)); /* logon server */
+ pstrcpy(p2,"\\\\*");
+ p2 = skip_string(p2,1);
+ SSVAL(p,usri11_country_code,0); /* country code */
+
+ SIVAL(p,usri11_workstations,PTR_DIFF(p2,p)); /* workstations */
+ pstrcpy(p2,"");
+ p2 = skip_string(p2,1);
+
+ SIVALS(p,usri11_max_storage,-1); /* max storage */
+ SSVAL(p,usri11_units_per_week,168); /* units per week */
+ SIVAL(p,usri11_logon_hours,PTR_DIFF(p2,p)); /* logon hours */
+
+ /* a simple way to get logon hours at all times. */
+ memset(p2,0xff,21);
+ SCVAL(p2,21,0); /* fix zero termination */
+ p2 = skip_string(p2,1);
+
+ SSVAL(p,usri11_code_page,0); /* code page */
+ }
+ if (uLevel == 1 || uLevel == 2)
+ {
+ memset(p+22,' ',16); /* password */
+ SIVALS(p,38,-1); /* password age */
+ SSVAL(p,42,
+ conn->admin_user?USER_PRIV_ADMIN:USER_PRIV_USER);
+ SIVAL(p,44,PTR_DIFF(p2,*rdata)); /* home dir */
+ pstrcpy(p2, vuser && vuser->homedir ? vuser->homedir : "");
+ p2 = skip_string(p2,1);
+ SIVAL(p,48,PTR_DIFF(p2,*rdata)); /* comment */
+ *p2++ = 0;
+ SSVAL(p,52,0); /* flags */
+ SIVAL(p,54,PTR_DIFF(p2,*rdata)); /* script_path */
+ pstrcpy(p2,vuser && vuser->logon_script ? vuser->logon_script : "");
+ p2 = skip_string(p2,1);
+ if (uLevel == 2)
+ {
+ SIVAL(p,60,0); /* auth_flags */
+ SIVAL(p,64,PTR_DIFF(p2,*rdata)); /* full_name */
+ pstrcpy(p2,((vuser != NULL) ? vuser->user.full_name : UserName));
+ p2 = skip_string(p2,1);
+ SIVAL(p,68,0); /* urs_comment */
+ SIVAL(p,72,PTR_DIFF(p2,*rdata)); /* parms */
+ pstrcpy(p2,"");
+ p2 = skip_string(p2,1);
+ SIVAL(p,76,0); /* workstations */
+ SIVAL(p,80,0); /* last_logon */
+ SIVAL(p,84,0); /* last_logoff */
+ SIVALS(p,88,-1); /* acct_expires */
+ SIVALS(p,92,-1); /* max_storage */
+ SSVAL(p,96,168); /* units_per_week */
+ SIVAL(p,98,PTR_DIFF(p2,*rdata)); /* logon_hours */
+ memset(p2,-1,21);
+ p2 += 21;
+ SSVALS(p,102,-1); /* bad_pw_count */
+ SSVALS(p,104,-1); /* num_logons */
+ SIVAL(p,106,PTR_DIFF(p2,*rdata)); /* logon_server */
+ pstrcpy(p2,"\\\\%L");
+ standard_sub_conn(conn, p2,0);
+ p2 = skip_string(p2,1);
+ SSVAL(p,110,49); /* country_code */
+ SSVAL(p,112,860); /* code page */
+ }
+ }
+
+ *rdata_len = PTR_DIFF(p2,*rdata);
+
+ SSVAL(*rparam,4,*rdata_len); /* is this right?? */
+
+ return(True);
+}
+
+static BOOL api_WWkstaUserLogon(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel;
+ struct pack_desc desc;
+ char* name;
+ /* With share level security vuid will always be zero.
+ Don't depend on vuser being non-null !!. JRA */
+ user_struct *vuser = get_valid_user_struct(vuid);
+ if(vuser != NULL)
+ DEBUG(3,(" Username of UID %d is %s\n", (int)vuser->uid,
+ vuser->user.unix_name));
+
+ uLevel = SVAL(p,0);
+ name = p + 2;
+
+ memset((char *)&desc,'\0',sizeof(desc));
+
+ DEBUG(3,("WWkstaUserLogon uLevel=%d name=%s\n",uLevel,name));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"OOWb54WrLh") != 0) return False;
+ if (uLevel != 1 || strcmp(str2,"WB21BWDWWDDDDDDDzzzD") != 0) return False;
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ desc.subformat = NULL;
+ desc.format = str2;
+
+ if (init_package(&desc,1,0))
+ {
+ PACKI(&desc,"W",0); /* code */
+ PACKS(&desc,"B21",name); /* eff. name */
+ PACKS(&desc,"B",""); /* pad */
+ PACKI(&desc,"W",
+ conn->admin_user?USER_PRIV_ADMIN:USER_PRIV_USER);
+ PACKI(&desc,"D",0); /* auth flags XXX */
+ PACKI(&desc,"W",0); /* num logons */
+ PACKI(&desc,"W",0); /* bad pw count */
+ PACKI(&desc,"D",0); /* last logon */
+ PACKI(&desc,"D",-1); /* last logoff */
+ PACKI(&desc,"D",-1); /* logoff time */
+ PACKI(&desc,"D",-1); /* kickoff time */
+ PACKI(&desc,"D",0); /* password age */
+ PACKI(&desc,"D",0); /* password can change */
+ PACKI(&desc,"D",-1); /* password must change */
+ {
+ fstring mypath;
+ fstrcpy(mypath,"\\\\");
+ fstrcat(mypath,local_machine);
+ strupper_m(mypath);
+ PACKS(&desc,"z",mypath); /* computer */
+ }
+ PACKS(&desc,"z",lp_workgroup());/* domain */
+
+ PACKS(&desc,"z", vuser && vuser->logon_script ? vuser->logon_script :""); /* script path */
+
+ PACKI(&desc,"D",0x00000000); /* reserved */
+ }
+
+ *rdata_len = desc.usedlen;
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,desc.neededlen);
+
+ DEBUG(4,("WWkstaUserLogon: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+
+/****************************************************************************
+ api_WAccessGetUserPerms
+ ****************************************************************************/
+static BOOL api_WAccessGetUserPerms(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *user = skip_string(str2,1);
+ char *resource = skip_string(user,1);
+
+ DEBUG(3,("WAccessGetUserPerms user=%s resource=%s\n",user,resource));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"zzh") != 0) return False;
+ if (strcmp(str2,"") != 0) return False;
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,0); /* errorcode */
+ SSVAL(*rparam,2,0); /* converter word */
+ SSVAL(*rparam,4,0x7f); /* permission flags */
+
+ return(True);
+}
+
+/****************************************************************************
+ api_WPrintJobEnumerate
+ ****************************************************************************/
+static BOOL api_WPrintJobGetInfo(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel;
+ int count;
+ int i;
+ int snum;
+ uint32 jobid;
+ struct pack_desc desc;
+ print_queue_struct *queue=NULL;
+ print_status_struct status;
+ char *tmpdata=NULL;
+
+ uLevel = SVAL(p,2);
+
+ memset((char *)&desc,'\0',sizeof(desc));
+ memset((char *)&status,'\0',sizeof(status));
+
+ DEBUG(3,("WPrintJobGetInfo uLevel=%d uJobId=0x%X\n",uLevel,SVAL(p,0)));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"WWrLh") != 0) return False;
+ if (!check_printjob_info(&desc,uLevel,str2)) return False;
+
+ if(!rap_to_pjobid(SVAL(p,0),&snum,&jobid))
+ return False;
+
+ if (snum < 0 || !VALID_SNUM(snum)) return(False);
+
+ count = print_queue_status(snum,&queue,&status);
+ for (i = 0; i < count; i++) {
+ if (queue[i].job == jobid) break;
+ }
+
+ if (mdrcnt > 0) {
+ *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ } else {
+ /*
+ * Don't return data but need to get correct length
+ * init_package will return wrong size if buflen=0
+ */
+ desc.buflen = getlen(desc.format);
+ desc.base = tmpdata = (char *)malloc ( desc.buflen );
+ }
+
+ if (init_package(&desc,1,0)) {
+ if (i < count) {
+ fill_printjob_info(conn,snum,uLevel,&desc,&queue[i],i);
+ *rdata_len = desc.usedlen;
+ }
+ else {
+ desc.errcode = NERR_JobNotFound;
+ *rdata_len = 0;
+ }
+ }
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,desc.neededlen);
+
+ SAFE_FREE(queue);
+ SAFE_FREE(tmpdata);
+
+ DEBUG(4,("WPrintJobGetInfo: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+static BOOL api_WPrintJobEnumerate(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ char* name = p;
+ int uLevel;
+ int count;
+ int i, succnt=0;
+ int snum;
+ struct pack_desc desc;
+ print_queue_struct *queue=NULL;
+ print_status_struct status;
+
+ memset((char *)&desc,'\0',sizeof(desc));
+ memset((char *)&status,'\0',sizeof(status));
+
+ p = skip_string(p,1);
+ uLevel = SVAL(p,0);
+
+ DEBUG(3,("WPrintJobEnumerate uLevel=%d name=%s\n",uLevel,name));
+
+ /* check it's a supported variant */
+ if (strcmp(str1,"zWrLeh") != 0) return False;
+ if (uLevel > 2) return False; /* defined only for uLevel 0,1,2 */
+ if (!check_printjob_info(&desc,uLevel,str2)) return False;
+
+ snum = lp_servicenumber(name);
+ if (snum < 0 && pcap_printername_ok(name,NULL)) {
+ int pnum = lp_servicenumber(PRINTERS_NAME);
+ if (pnum >= 0) {
+ lp_add_printer(name,pnum);
+ snum = lp_servicenumber(name);
+ }
+ }
+
+ if (snum < 0 || !VALID_SNUM(snum)) return(False);
+
+ count = print_queue_status(snum,&queue,&status);
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+
+ if (init_package(&desc,count,0)) {
+ succnt = 0;
+ for (i = 0; i < count; i++) {
+ fill_printjob_info(conn,snum,uLevel,&desc,&queue[i],i);
+ if (desc.errcode == NERR_Success) succnt = i+1;
+ }
+ }
+
+ *rdata_len = desc.usedlen;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,succnt);
+ SSVAL(*rparam,6,count);
+
+ SAFE_FREE(queue);
+
+ DEBUG(4,("WPrintJobEnumerate: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+static int check_printdest_info(struct pack_desc* desc,
+ int uLevel, char* id)
+{
+ desc->subformat = NULL;
+ switch( uLevel ) {
+ case 0: desc->format = "B9"; break;
+ case 1: desc->format = "B9B21WWzW"; break;
+ case 2: desc->format = "z"; break;
+ case 3: desc->format = "zzzWWzzzWW"; break;
+ default: return False;
+ }
+ if (strcmp(desc->format,id) != 0) return False;
+ return True;
+}
+
+static void fill_printdest_info(connection_struct *conn, int snum, int uLevel,
+ struct pack_desc* desc)
+{
+ char buf[100];
+ strncpy(buf,SERVICE(snum),sizeof(buf)-1);
+ buf[sizeof(buf)-1] = 0;
+ strupper_m(buf);
+ if (uLevel <= 1) {
+ PACKS(desc,"B9",buf); /* szName */
+ if (uLevel == 1) {
+ PACKS(desc,"B21",""); /* szUserName */
+ PACKI(desc,"W",0); /* uJobId */
+ PACKI(desc,"W",0); /* fsStatus */
+ PACKS(desc,"z",""); /* pszStatus */
+ PACKI(desc,"W",0); /* time */
+ }
+ }
+ if (uLevel == 2 || uLevel == 3) {
+ PACKS(desc,"z",buf); /* pszPrinterName */
+ if (uLevel == 3) {
+ PACKS(desc,"z",""); /* pszUserName */
+ PACKS(desc,"z",""); /* pszLogAddr */
+ PACKI(desc,"W",0); /* uJobId */
+ PACKI(desc,"W",0); /* fsStatus */
+ PACKS(desc,"z",""); /* pszStatus */
+ PACKS(desc,"z",""); /* pszComment */
+ PACKS(desc,"z","NULL"); /* pszDrivers */
+ PACKI(desc,"W",0); /* time */
+ PACKI(desc,"W",0); /* pad1 */
+ }
+ }
+}
+
+static BOOL api_WPrintDestGetInfo(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ char* PrinterName = p;
+ int uLevel;
+ struct pack_desc desc;
+ int snum;
+ char *tmpdata=NULL;
+
+ memset((char *)&desc,'\0',sizeof(desc));
+
+ p = skip_string(p,1);
+ uLevel = SVAL(p,0);
+
+ DEBUG(3,("WPrintDestGetInfo uLevel=%d PrinterName=%s\n",uLevel,PrinterName));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"zWrLh") != 0) return False;
+ if (!check_printdest_info(&desc,uLevel,str2)) return False;
+
+ snum = lp_servicenumber(PrinterName);
+ if (snum < 0 && pcap_printername_ok(PrinterName,NULL)) {
+ int pnum = lp_servicenumber(PRINTERS_NAME);
+ if (pnum >= 0) {
+ lp_add_printer(PrinterName,pnum);
+ snum = lp_servicenumber(PrinterName);
+ }
+ }
+
+ if (snum < 0) {
+ *rdata_len = 0;
+ desc.errcode = NERR_DestNotFound;
+ desc.neededlen = 0;
+ }
+ else {
+ if (mdrcnt > 0) {
+ *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ } else {
+ /*
+ * Don't return data but need to get correct length
+ * init_package will return wrong size if buflen=0
+ */
+ desc.buflen = getlen(desc.format);
+ desc.base = tmpdata = (char *)malloc ( desc.buflen );
+ }
+ if (init_package(&desc,1,0)) {
+ fill_printdest_info(conn,snum,uLevel,&desc);
+ }
+ *rdata_len = desc.usedlen;
+ }
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,desc.neededlen);
+
+ DEBUG(4,("WPrintDestGetInfo: errorcode %d\n",desc.errcode));
+ SAFE_FREE(tmpdata);
+ return(True);
+}
+
+static BOOL api_WPrintDestEnum(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel;
+ int queuecnt;
+ int i, n, succnt=0;
+ struct pack_desc desc;
+ int services = lp_numservices();
+
+ memset((char *)&desc,'\0',sizeof(desc));
+
+ uLevel = SVAL(p,0);
+
+ DEBUG(3,("WPrintDestEnum uLevel=%d\n",uLevel));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"WrLeh") != 0) return False;
+ if (!check_printdest_info(&desc,uLevel,str2)) return False;
+
+ queuecnt = 0;
+ for (i = 0; i < services; i++)
+ if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i))
+ queuecnt++;
+
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ if (init_package(&desc,queuecnt,0)) {
+ succnt = 0;
+ n = 0;
+ for (i = 0; i < services; i++) {
+ if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) {
+ fill_printdest_info(conn,i,uLevel,&desc);
+ n++;
+ if (desc.errcode == NERR_Success) succnt = n;
+ }
+ }
+ }
+
+ *rdata_len = desc.usedlen;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,succnt);
+ SSVAL(*rparam,6,queuecnt);
+
+ DEBUG(4,("WPrintDestEnumerate: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+static BOOL api_WPrintDriverEnum(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel;
+ int succnt;
+ struct pack_desc desc;
+
+ memset((char *)&desc,'\0',sizeof(desc));
+
+ uLevel = SVAL(p,0);
+
+ DEBUG(3,("WPrintDriverEnum uLevel=%d\n",uLevel));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"WrLeh") != 0) return False;
+ if (uLevel != 0 || strcmp(str2,"B41") != 0) return False;
+
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ if (init_package(&desc,1,0)) {
+ PACKS(&desc,"B41","NULL");
+ }
+
+ succnt = (desc.errcode == NERR_Success ? 1 : 0);
+
+ *rdata_len = desc.usedlen;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,succnt);
+ SSVAL(*rparam,6,1);
+
+ DEBUG(4,("WPrintDriverEnum: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+static BOOL api_WPrintQProcEnum(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel;
+ int succnt;
+ struct pack_desc desc;
+
+ memset((char *)&desc,'\0',sizeof(desc));
+
+ uLevel = SVAL(p,0);
+
+ DEBUG(3,("WPrintQProcEnum uLevel=%d\n",uLevel));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"WrLeh") != 0) return False;
+ if (uLevel != 0 || strcmp(str2,"B13") != 0) return False;
+
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ desc.format = str2;
+ if (init_package(&desc,1,0)) {
+ PACKS(&desc,"B13","lpd");
+ }
+
+ succnt = (desc.errcode == NERR_Success ? 1 : 0);
+
+ *rdata_len = desc.usedlen;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,succnt);
+ SSVAL(*rparam,6,1);
+
+ DEBUG(4,("WPrintQProcEnum: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+static BOOL api_WPrintPortEnum(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel;
+ int succnt;
+ struct pack_desc desc;
+
+ memset((char *)&desc,'\0',sizeof(desc));
+
+ uLevel = SVAL(p,0);
+
+ DEBUG(3,("WPrintPortEnum uLevel=%d\n",uLevel));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"WrLeh") != 0) return False;
+ if (uLevel != 0 || strcmp(str2,"B9") != 0) return False;
+
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ memset((char *)&desc,'\0',sizeof(desc));
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ desc.format = str2;
+ if (init_package(&desc,1,0)) {
+ PACKS(&desc,"B13","lp0");
+ }
+
+ succnt = (desc.errcode == NERR_Success ? 1 : 0);
+
+ *rdata_len = desc.usedlen;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,succnt);
+ SSVAL(*rparam,6,1);
+
+ DEBUG(4,("WPrintPortEnum: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+
+/****************************************************************************
+ List open sessions
+ ****************************************************************************/
+static BOOL api_RNetSessionEnum(connection_struct *conn,uint16 vuid, char *param, char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel;
+ struct pack_desc desc;
+ struct sessionid *session_list;
+ int i, num_sessions;
+
+ memset((char *)&desc,'\0',sizeof(desc));
+
+ uLevel = SVAL(p,0);
+
+ DEBUG(3,("RNetSessionEnum uLevel=%d\n",uLevel));
+ DEBUG(7,("RNetSessionEnum req string=%s\n",str1));
+ DEBUG(7,("RNetSessionEnum ret string=%s\n",str2));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,RAP_NetSessionEnum_REQ) != 0) return False;
+ if (uLevel != 2 || strcmp(str2,RAP_SESSION_INFO_L2) != 0) return False;
+
+ num_sessions = list_sessions(&session_list);
+
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ memset((char *)&desc,'\0',sizeof(desc));
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ desc.format = str2;
+ if (!init_package(&desc,num_sessions,0)) {
+ return False;
+ }
+
+ for(i=0; i<num_sessions; i++) {
+ PACKS(&desc, "z", session_list[i].remote_machine);
+ PACKS(&desc, "z", session_list[i].username);
+ PACKI(&desc, "W", 1); /* num conns */
+ PACKI(&desc, "W", 0); /* num opens */
+ PACKI(&desc, "W", 1); /* num users */
+ PACKI(&desc, "D", 0); /* session time */
+ PACKI(&desc, "D", 0); /* idle time */
+ PACKI(&desc, "D", 0); /* flags */
+ PACKS(&desc, "z", "Unknown Client"); /* client type string */
+ }
+
+ *rdata_len = desc.usedlen;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0); /* converter */
+ SSVAL(*rparam,4,num_sessions); /* count */
+
+ DEBUG(4,("RNetSessionEnum: errorcode %d\n",desc.errcode));
+ return True;
+}
+
+
+/****************************************************************************
+ The buffer was too small
+ ****************************************************************************/
+
+static BOOL api_TooSmall(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ *rparam_len = MIN(*rparam_len,mprcnt);
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ *rdata_len = 0;
+
+ SSVAL(*rparam,0,NERR_BufTooSmall);
+
+ DEBUG(3,("Supplied buffer too small in API command\n"));
+
+ return(True);
+}
+
+
+/****************************************************************************
+ The request is not supported
+ ****************************************************************************/
+
+static BOOL api_Unsupported(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ *rparam_len = 4;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ *rdata_len = 0;
+
+ SSVAL(*rparam,0,NERR_notsupported);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ DEBUG(3,("Unsupported API command\n"));
+
+ return(True);
+}
+
+
+
+
+static const struct
+{
+ const char *name;
+ int id;
+ BOOL (*fn)(connection_struct *,uint16,char *,char *,
+ int,int,char **,char **,int *,int *);
+ BOOL auth_user; /* Deny anonymous access? */
+} api_commands[] = {
+ {"RNetShareEnum", RAP_WshareEnum, api_RNetShareEnum, True},
+ {"RNetShareGetInfo", RAP_WshareGetInfo, api_RNetShareGetInfo},
+ {"RNetShareAdd", RAP_WshareAdd, api_RNetShareAdd},
+ {"RNetSessionEnum", RAP_WsessionEnum, api_RNetSessionEnum, True},
+ {"RNetServerGetInfo", RAP_WserverGetInfo, api_RNetServerGetInfo},
+ {"RNetGroupEnum", RAP_WGroupEnum, api_RNetGroupEnum, True},
+ {"RNetGroupGetUsers", RAP_WGroupGetUsers, api_RNetGroupGetUsers, True},
+ {"RNetUserEnum", RAP_WUserEnum, api_RNetUserEnum, True},
+ {"RNetUserGetInfo", RAP_WUserGetInfo, api_RNetUserGetInfo},
+ {"NetUserGetGroups", RAP_WUserGetGroups, api_NetUserGetGroups},
+ {"NetWkstaGetInfo", RAP_WWkstaGetInfo, api_NetWkstaGetInfo},
+ {"DosPrintQEnum", RAP_WPrintQEnum, api_DosPrintQEnum, True},
+ {"DosPrintQGetInfo", RAP_WPrintQGetInfo, api_DosPrintQGetInfo},
+ {"WPrintQueuePause", RAP_WPrintQPause, api_WPrintQueueCtrl},
+ {"WPrintQueueResume", RAP_WPrintQContinue, api_WPrintQueueCtrl},
+ {"WPrintJobEnumerate",RAP_WPrintJobEnum, api_WPrintJobEnumerate},
+ {"WPrintJobGetInfo", RAP_WPrintJobGetInfo, api_WPrintJobGetInfo},
+ {"RDosPrintJobDel", RAP_WPrintJobDel, api_RDosPrintJobDel},
+ {"RDosPrintJobPause", RAP_WPrintJobPause, api_RDosPrintJobDel},
+ {"RDosPrintJobResume",RAP_WPrintJobContinue, api_RDosPrintJobDel},
+ {"WPrintDestEnum", RAP_WPrintDestEnum, api_WPrintDestEnum},
+ {"WPrintDestGetInfo", RAP_WPrintDestGetInfo, api_WPrintDestGetInfo},
+ {"NetRemoteTOD", RAP_NetRemoteTOD, api_NetRemoteTOD},
+ {"WPrintQueuePurge", RAP_WPrintQPurge, api_WPrintQueueCtrl},
+ {"NetServerEnum", RAP_NetServerEnum2, api_RNetServerEnum}, /* anon OK */
+ {"WAccessGetUserPerms",RAP_WAccessGetUserPerms,api_WAccessGetUserPerms},
+ {"SetUserPassword", RAP_WUserPasswordSet2, api_SetUserPassword},
+ {"WWkstaUserLogon", RAP_WWkstaUserLogon, api_WWkstaUserLogon},
+ {"PrintJobInfo", RAP_WPrintJobSetInfo, api_PrintJobInfo},
+ {"WPrintDriverEnum", RAP_WPrintDriverEnum, api_WPrintDriverEnum},
+ {"WPrintQProcEnum", RAP_WPrintQProcessorEnum,api_WPrintQProcEnum},
+ {"WPrintPortEnum", RAP_WPrintPortEnum, api_WPrintPortEnum},
+ {"SamOEMChangePassword",RAP_SamOEMChgPasswordUser2_P,api_SamOEMChangePassword}, /* anon OK */
+ {NULL, -1, api_Unsupported}};
+
+/* The following RAP calls are not implemented by Samba:
+
+ RAP_WFileEnum2 - anon not OK
+*/
+
+/****************************************************************************
+ Handle remote api calls
+ ****************************************************************************/
+
+int api_reply(connection_struct *conn,uint16 vuid,char *outbuf,char *data,char *params,
+ int tdscnt,int tpscnt,int mdrcnt,int mprcnt)
+{
+ int api_command;
+ char *rdata = NULL;
+ char *rparam = NULL;
+ int rdata_len = 0;
+ int rparam_len = 0;
+ BOOL reply=False;
+ int i;
+
+ if (!params) {
+ DEBUG(0,("ERROR: NULL params in api_reply()\n"));
+ return 0;
+ }
+
+ api_command = SVAL(params,0);
+
+ DEBUG(3,("Got API command %d of form <%s> <%s> (tdscnt=%d,tpscnt=%d,mdrcnt=%d,mprcnt=%d)\n",
+ api_command,
+ params+2,
+ skip_string(params+2,1),
+ tdscnt,tpscnt,mdrcnt,mprcnt));
+
+ for (i=0;api_commands[i].name;i++) {
+ if (api_commands[i].id == api_command && api_commands[i].fn) {
+ DEBUG(3,("Doing %s\n",api_commands[i].name));
+ break;
+ }
+ }
+
+ /* Check whether this api call can be done anonymously */
+
+ if (api_commands[i].auth_user && lp_restrict_anonymous()) {
+ user_struct *user = get_valid_user_struct(vuid);
+
+ if (!user || user->guest)
+ return ERROR_NT(NT_STATUS_ACCESS_DENIED);
+ }
+
+ rdata = (char *)malloc(1024);
+ if (rdata)
+ memset(rdata,'\0',1024);
+
+ rparam = (char *)malloc(1024);
+ if (rparam)
+ memset(rparam,'\0',1024);
+
+ if(!rdata || !rparam) {
+ DEBUG(0,("api_reply: malloc fail !\n"));
+ return -1;
+ }
+
+ reply = api_commands[i].fn(conn,vuid,params,data,mdrcnt,mprcnt,
+ &rdata,&rparam,&rdata_len,&rparam_len);
+
+
+ if (rdata_len > mdrcnt ||
+ rparam_len > mprcnt) {
+ reply = api_TooSmall(conn,vuid,params,data,mdrcnt,mprcnt,
+ &rdata,&rparam,&rdata_len,&rparam_len);
+ }
+
+ /* if we get False back then it's actually unsupported */
+ if (!reply)
+ api_Unsupported(conn,vuid,params,data,mdrcnt,mprcnt,
+ &rdata,&rparam,&rdata_len,&rparam_len);
+
+ send_trans_reply(outbuf, rparam, rparam_len, rdata, rdata_len, False);
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ return -1;
+}
diff --git a/source/smbd/mangle.c b/source/smbd/mangle.c
new file mode 100644
index 00000000000..c5d7582c033
--- /dev/null
+++ b/source/smbd/mangle.c
@@ -0,0 +1,124 @@
+/*
+ Unix SMB/CIFS implementation.
+ Name mangling interface
+ Copyright (C) Andrew Tridgell 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static struct mangle_fns *mangle_fns;
+
+/* this allows us to add more mangling backends */
+static const struct {
+ const char *name;
+ struct mangle_fns *(*init_fn)(void);
+} mangle_backends[] = {
+ { "hash", mangle_hash_init },
+ { "hash2", mangle_hash2_init },
+ /*{ "tdb", mangle_tdb_init }, */
+ { NULL, NULL }
+};
+
+/*
+ initialise the mangling subsystem
+*/
+static void mangle_init(void)
+{
+ int i;
+ char *method;
+
+ if (mangle_fns)
+ return;
+
+ method = lp_mangling_method();
+
+ /* find the first mangling method that manages to initialise and
+ matches the "mangling method" parameter */
+ for (i=0; mangle_backends[i].name && !mangle_fns; i++) {
+ if (!method || !*method || strcmp(method, mangle_backends[i].name) == 0) {
+ mangle_fns = mangle_backends[i].init_fn();
+ }
+ }
+
+ if (!mangle_fns) {
+ DEBUG(0,("Failed to initialise mangling system '%s'\n", method));
+ exit_server("mangling init failed");
+ }
+}
+
+
+/*
+ reset the cache. This is called when smb.conf has been reloaded
+*/
+void mangle_reset_cache(void)
+{
+ mangle_init();
+
+ mangle_fns->reset();
+}
+
+/*
+ see if a filename has come out of our mangling code
+*/
+BOOL mangle_is_mangled(const char *s)
+{
+ return mangle_fns->is_mangled(s);
+}
+
+/*
+ see if a filename matches the rules of a 8.3 filename
+*/
+BOOL mangle_is_8_3(const char *fname, BOOL check_case)
+{
+ return mangle_fns->is_8_3(fname, check_case, False);
+}
+
+BOOL mangle_is_8_3_wildcards(const char *fname, BOOL check_case)
+{
+ return mangle_fns->is_8_3(fname, check_case, True);
+}
+
+/*
+ try to reverse map a 8.3 name to the original filename. This doesn't have to
+ always succeed, as the directory handling code in smbd will scan the directory
+ looking for a matching name if it doesn't. It should succeed most of the time
+ or there will be a huge performance penalty
+*/
+BOOL mangle_check_cache(char *s)
+{
+ return mangle_fns->check_cache(s);
+}
+
+/*
+ map a long filename to a 8.3 name.
+ */
+
+void mangle_map(pstring OutName, BOOL need83, BOOL cache83, int snum)
+{
+ /* name mangling can be disabled for speed, in which case
+ we just truncate the string */
+ if (!lp_manglednames(snum)) {
+ if (need83) {
+ string_truncate(OutName, 12);
+ }
+ return;
+ }
+
+ /* invoke the inane "mangled map" code */
+ mangle_map_filename(OutName, snum);
+ mangle_fns->name_map(OutName, need83, cache83);
+}
diff --git a/source/smbd/mangle_hash.c b/source/smbd/mangle_hash.c
new file mode 100644
index 00000000000..16722ae6e9d
--- /dev/null
+++ b/source/smbd/mangle_hash.c
@@ -0,0 +1,783 @@
+/*
+ Unix SMB/CIFS implementation.
+ Name mangling
+ Copyright (C) Andrew Tridgell 1992-2002
+ Copyright (C) Simo Sorce 2001
+ Copyright (C) Andrew Bartlett 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+/* -------------------------------------------------------------------------- **
+ * Notable problems...
+ *
+ * March/April 1998 CRH
+ * - Many of the functions in this module overwrite string buffers passed to
+ * them. This causes a variety of problems and is, generally speaking,
+ * dangerous and scarry. See the kludge notes in name_map()
+ * below.
+ * - It seems that something is calling name_map() twice. The
+ * first call is probably some sort of test. Names which contain
+ * illegal characters are being doubly mangled. I'm not sure, but
+ * I'm guessing the problem is in server.c.
+ *
+ * -------------------------------------------------------------------------- **
+ */
+
+/* -------------------------------------------------------------------------- **
+ * History...
+ *
+ * March/April 1998 CRH
+ * Updated a bit. Rewrote is_mangled() to be a bit more selective.
+ * Rewrote the mangled name cache. Added comments here and there.
+ * &c.
+ * -------------------------------------------------------------------------- **
+ */
+
+#include "includes.h"
+
+
+/* -------------------------------------------------------------------------- **
+ * External Variables...
+ */
+
+extern int case_default; /* Are conforming 8.3 names all upper or lower? */
+extern BOOL case_mangle; /* If true, all chars in 8.3 should be same case. */
+
+/* -------------------------------------------------------------------------- **
+ * Other stuff...
+ *
+ * magic_char - This is the magic char used for mangling. It's
+ * global. There is a call to lp_magicchar() in server.c
+ * that is used to override the initial value.
+ *
+ * MANGLE_BASE - This is the number of characters we use for name mangling.
+ *
+ * basechars - The set characters used for name mangling. This
+ * is static (scope is this file only).
+ *
+ * mangle() - Macro used to select a character from basechars (i.e.,
+ * mangle(n) will return the nth digit, modulo MANGLE_BASE).
+ *
+ * chartest - array 0..255. The index range is the set of all possible
+ * values of a byte. For each byte value, the content is a
+ * two nibble pair. See BASECHAR_MASK and ILLEGAL_MASK,
+ * below.
+ *
+ * ct_initialized - False until the chartest array has been initialized via
+ * a call to init_chartest().
+ *
+ * BASECHAR_MASK - Masks the upper nibble of a one-byte value.
+ *
+ * ILLEGAL_MASK - Masks the lower nibble of a one-byte value.
+ *
+ * isbasecahr() - Given a character, check the chartest array to see
+ * if that character is in the basechars set. This is
+ * faster than using strchr_m().
+ *
+ * isillegal() - Given a character, check the chartest array to see
+ * if that character is in the illegal characters set.
+ * This is faster than using strchr_m().
+ *
+ * mangled_cache - Cache header used for storing mangled -> original
+ * reverse maps.
+ *
+ * mc_initialized - False until the mangled_cache structure has been
+ * initialized via a call to reset_mangled_cache().
+ *
+ * MANGLED_CACHE_MAX_ENTRIES - Default maximum number of entries for the
+ * cache. A value of 0 indicates "infinite".
+ *
+ * MANGLED_CACHE_MAX_MEMORY - Default maximum amount of memory for the
+ * cache. When the cache was kept as an array of 256
+ * byte strings, the default cache size was 50 entries.
+ * This required a fixed 12.5Kbytes of memory. The
+ * mangled stack parameter is no longer used (though
+ * this might change). We're now using a fixed 16Kbyte
+ * maximum cache size. This will probably be much more
+ * than 50 entries.
+ */
+
+char magic_char = '~';
+
+static char basechars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%";
+#define MANGLE_BASE (sizeof(basechars)/sizeof(char)-1)
+
+static unsigned char chartest[256] = { 0 };
+static BOOL ct_initialized = False;
+
+#define mangle(V) ((char)(basechars[(V) % MANGLE_BASE]))
+#define BASECHAR_MASK 0xf0
+#define ILLEGAL_MASK 0x0f
+#define isbasechar(C) ( (chartest[ ((C) & 0xff) ]) & BASECHAR_MASK )
+#define isillegal(C) ( (chartest[ ((C) & 0xff) ]) & ILLEGAL_MASK )
+
+static ubi_cacheRoot mangled_cache[1] = { { { 0, 0, 0, 0 }, 0, 0, 0, 0, 0, 0 } };
+static BOOL mc_initialized = False;
+#define MANGLED_CACHE_MAX_ENTRIES 1024
+#define MANGLED_CACHE_MAX_MEMORY 0
+
+/* -------------------------------------------------------------------------- **
+ * External Variables...
+ */
+
+extern int case_default; /* Are conforming 8.3 names all upper or lower? */
+extern BOOL case_mangle; /* If true, all chars in 8.3 should be same case. */
+
+/* -------------------------------------------------------------------- */
+
+static NTSTATUS has_valid_83_chars(const smb_ucs2_t *s, BOOL allow_wildcards)
+{
+ if (!s || !*s)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ /* CHECK: this should not be necessary if the ms wild chars
+ are not valid in valid.dat --- simo */
+ if (!allow_wildcards && ms_has_wild_w(s))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ while (*s) {
+ if(!isvalid83_w(*s))
+ return NT_STATUS_UNSUCCESSFUL;
+ s++;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* return False if something fail and
+ * return 2 alloced unicode strings that contain prefix and extension
+ */
+
+static NTSTATUS mangle_get_prefix(const smb_ucs2_t *ucs2_string, smb_ucs2_t **prefix,
+ smb_ucs2_t **extension, BOOL allow_wildcards)
+{
+ size_t ext_len;
+ smb_ucs2_t *p;
+
+ *extension = 0;
+ *prefix = strdup_w(ucs2_string);
+ if (!*prefix) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ if ((p = strrchr_w(*prefix, UCS2_CHAR('.')))) {
+ ext_len = strlen_w(p+1);
+ if ((ext_len > 0) && (ext_len < 4) && (p != *prefix) &&
+ (NT_STATUS_IS_OK(has_valid_83_chars(p+1,allow_wildcards)))) /* check extension */ {
+ *p = 0;
+ *extension = strdup_w(p+1);
+ if (!*extension) {
+ SAFE_FREE(*prefix);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+/* ************************************************************************** **
+ * Return NT_STATUS_UNSUCCESSFUL if a name is a special msdos reserved name.
+ *
+ * Input: fname - String containing the name to be tested.
+ *
+ * Output: NT_STATUS_UNSUCCESSFUL, if the name matches one of the list of reserved names.
+ *
+ * Notes: This is a static function called by is_8_3(), below.
+ *
+ * ************************************************************************** **
+ */
+
+static NTSTATUS is_valid_name(const smb_ucs2_t *fname, BOOL allow_wildcards, BOOL only_8_3)
+{
+ smb_ucs2_t *str, *p;
+ NTSTATUS ret = NT_STATUS_OK;
+
+ if (!fname || !*fname)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ /* . and .. are valid names. */
+ if (strcmp_wa(fname, ".")==0 || strcmp_wa(fname, "..")==0)
+ return NT_STATUS_OK;
+
+ /* Name cannot start with '.' */
+ if (*fname == UCS2_CHAR('.'))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ if (only_8_3) {
+ ret = has_valid_83_chars(fname, allow_wildcards);
+ if (!NT_STATUS_IS_OK(ret))
+ return ret;
+ }
+
+ str = strdup_w(fname);
+ p = strchr_w(str, UCS2_CHAR('.'));
+ if (p && p[1] == UCS2_CHAR(0)) {
+ /* Name cannot end in '.' */
+ SAFE_FREE(str);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ if (p)
+ *p = 0;
+ strupper_w(str);
+ p = &(str[1]);
+
+ switch(str[0])
+ {
+ case UCS2_CHAR('A'):
+ if(strcmp_wa(p, "UX") == 0)
+ ret = NT_STATUS_UNSUCCESSFUL;
+ break;
+ case UCS2_CHAR('C'):
+ if((strcmp_wa(p, "LOCK$") == 0)
+ || (strcmp_wa(p, "ON") == 0)
+ || (strcmp_wa(p, "OM1") == 0)
+ || (strcmp_wa(p, "OM2") == 0)
+ || (strcmp_wa(p, "OM3") == 0)
+ || (strcmp_wa(p, "OM4") == 0)
+ )
+ ret = NT_STATUS_UNSUCCESSFUL;
+ break;
+ case UCS2_CHAR('L'):
+ if((strcmp_wa(p, "PT1") == 0)
+ || (strcmp_wa(p, "PT2") == 0)
+ || (strcmp_wa(p, "PT3") == 0)
+ )
+ ret = NT_STATUS_UNSUCCESSFUL;
+ break;
+ case UCS2_CHAR('N'):
+ if(strcmp_wa(p, "UL") == 0)
+ ret = NT_STATUS_UNSUCCESSFUL;
+ break;
+ case UCS2_CHAR('P'):
+ if(strcmp_wa(p, "RN") == 0)
+ ret = NT_STATUS_UNSUCCESSFUL;
+ break;
+ default:
+ break;
+ }
+
+ SAFE_FREE(str);
+ return ret;
+}
+
+static NTSTATUS is_8_3_w(const smb_ucs2_t *fname, BOOL allow_wildcards)
+{
+ smb_ucs2_t *pref = 0, *ext = 0;
+ size_t plen;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if (!fname || !*fname)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (strlen_w(fname) > 12)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ if (strcmp_wa(fname, ".") == 0 || strcmp_wa(fname, "..") == 0)
+ return NT_STATUS_OK;
+
+ if (!NT_STATUS_IS_OK(is_valid_name(fname, allow_wildcards, True)))
+ goto done;
+
+ if (!NT_STATUS_IS_OK(mangle_get_prefix(fname, &pref, &ext, allow_wildcards)))
+ goto done;
+ plen = strlen_w(pref);
+
+ if (strchr_wa(pref, '.'))
+ goto done;
+ if (plen < 1 || plen > 8)
+ goto done;
+ if (ext && (strlen_w(ext) > 3))
+ goto done;
+
+ ret = NT_STATUS_OK;
+
+done:
+ SAFE_FREE(pref);
+ SAFE_FREE(ext);
+ return ret;
+}
+
+static BOOL is_8_3(const char *fname, BOOL check_case, BOOL allow_wildcards)
+{
+ const char *f;
+ smb_ucs2_t *ucs2name;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ size_t size;
+
+ if (!fname || !*fname)
+ return False;
+ if ((f = strrchr(fname, '/')) == NULL)
+ f = fname;
+ else
+ f++;
+
+ if (strlen(f) > 12)
+ return False;
+
+ size = push_ucs2_allocate(&ucs2name, f);
+ if (size == (size_t)-1) {
+ DEBUG(0,("is_8_3: internal error push_ucs2_allocate() failed!\n"));
+ goto done;
+ }
+
+ ret = is_8_3_w(ucs2name, allow_wildcards);
+
+done:
+ SAFE_FREE(ucs2name);
+
+ if (!NT_STATUS_IS_OK(ret)) {
+ return False;
+ }
+
+ return True;
+}
+
+
+
+/* -------------------------------------------------------------------------- **
+ * Functions...
+ */
+
+/* ************************************************************************** **
+ * Initialize the static character test array.
+ *
+ * Input: none
+ *
+ * Output: none
+ *
+ * Notes: This function changes (loads) the contents of the <chartest>
+ * array. The scope of <chartest> is this file.
+ *
+ * ************************************************************************** **
+ */
+static void init_chartest( void )
+{
+ const char *illegalchars = "*\\/?<>|\":";
+ const unsigned char *s;
+
+ memset( (char *)chartest, '\0', 256 );
+
+ for( s = (const unsigned char *)illegalchars; *s; s++ )
+ chartest[*s] = ILLEGAL_MASK;
+
+ for( s = (const unsigned char *)basechars; *s; s++ )
+ chartest[*s] |= BASECHAR_MASK;
+
+ ct_initialized = True;
+}
+
+/* ************************************************************************** **
+ * Return True if the name *could be* a mangled name.
+ *
+ * Input: s - A path name - in UNIX pathname format.
+ *
+ * Output: True if the name matches the pattern described below in the
+ * notes, else False.
+ *
+ * Notes: The input name is *not* tested for 8.3 compliance. This must be
+ * done separately. This function returns true if the name contains
+ * a magic character followed by excactly two characters from the
+ * basechars list (above), which in turn are followed either by the
+ * nul (end of string) byte or a dot (extension) or by a '/' (end of
+ * a directory name).
+ *
+ * ************************************************************************** **
+ */
+static BOOL is_mangled(const char *s)
+{
+ char *magic;
+
+ if( !ct_initialized )
+ init_chartest();
+
+ magic = strchr_m( s, magic_char );
+ while( magic && magic[1] && magic[2] ) { /* 3 chars, 1st is magic. */
+ if( ('.' == magic[3] || '/' == magic[3] || !(magic[3])) /* Ends with '.' or nul or '/' ? */
+ && isbasechar( toupper(magic[1]) ) /* is 2nd char basechar? */
+ && isbasechar( toupper(magic[2]) ) ) /* is 3rd char basechar? */
+ return( True ); /* If all above, then true, */
+ magic = strchr_m( magic+1, magic_char ); /* else seek next magic. */
+ }
+ return( False );
+}
+
+/* ************************************************************************** **
+ * Compare two cache keys and return a value indicating their ordinal
+ * relationship.
+ *
+ * Input: ItemPtr - Pointer to a comparison key. In this case, this will
+ * be a mangled name string.
+ * NodePtr - Pointer to a node in the cache. The node structure
+ * will be followed in memory by a mangled name string.
+ *
+ * Output: A signed integer, as follows:
+ * (x < 0) <==> Key1 less than Key2
+ * (x == 0) <==> Key1 equals Key2
+ * (x > 0) <==> Key1 greater than Key2
+ *
+ * Notes: This is a ubiqx-style comparison routine. See ubi_BinTree for
+ * more info.
+ *
+ * ************************************************************************** **
+ */
+static signed int cache_compare( ubi_btItemPtr ItemPtr, ubi_btNodePtr NodePtr )
+{
+ char *Key1 = (char *)ItemPtr;
+ char *Key2 = (char *)(((ubi_cacheEntryPtr)NodePtr) + 1);
+
+ return( StrCaseCmp( Key1, Key2 ) );
+}
+
+/* ************************************************************************** **
+ * Free a cache entry.
+ *
+ * Input: WarrenZevon - Pointer to the entry that is to be returned to
+ * Nirvana.
+ * Output: none.
+ *
+ * Notes: This function gets around the possibility that the standard
+ * free() function may be implemented as a macro, or other evil
+ * subversions (oh, so much fun).
+ *
+ * ************************************************************************** **
+ */
+static void cache_free_entry( ubi_trNodePtr WarrenZevon )
+{
+ ZERO_STRUCTP(WarrenZevon);
+ SAFE_FREE( WarrenZevon );
+}
+
+/* ************************************************************************** **
+ * Initializes or clears the mangled cache.
+ *
+ * Input: none.
+ * Output: none.
+ *
+ * Notes: There is a section below that is commented out. It shows how
+ * one might use lp_ calls to set the maximum memory and entry size
+ * of the cache. You might also want to remove the constants used
+ * in ubi_cacheInit() and replace them with lp_ calls. If so, then
+ * the calls to ubi_cacheSetMax*() would be moved into the else
+ * clause. Another option would be to pass in the max_entries and
+ * max_memory values as parameters. crh 09-Apr-1998.
+ *
+ * ************************************************************************** **
+ */
+
+static void mangle_reset( void )
+{
+ if( !mc_initialized ) {
+ (void)ubi_cacheInit( mangled_cache,
+ cache_compare,
+ cache_free_entry,
+ MANGLED_CACHE_MAX_ENTRIES,
+ MANGLED_CACHE_MAX_MEMORY );
+ mc_initialized = True;
+ } else {
+ (void)ubi_cacheClear( mangled_cache );
+ }
+
+ /*
+ (void)ubi_cacheSetMaxEntries( mangled_cache, lp_mangled_cache_entries() );
+ (void)ubi_cacheSetMaxMemory( mangled_cache, lp_mangled_cache_memory() );
+ */
+}
+
+/* ************************************************************************** **
+ * Add a mangled name into the cache.
+ *
+ * Notes: If the mangled cache has not been initialized, then the
+ * function will simply fail. It could initialize the cache,
+ * but that's not the way it was done before I changed the
+ * cache mechanism, so I'm sticking with the old method.
+ *
+ * If the extension of the raw name maps directly to the
+ * extension of the mangled name, then we'll store both names
+ * *without* extensions. That way, we can provide consistent
+ * reverse mangling for all names that match. The test here is
+ * a bit more careful than the one done in earlier versions of
+ * mangle.c:
+ *
+ * - the extension must exist on the raw name,
+ * - it must be all lower case
+ * - it must match the mangled extension (to prove that no
+ * mangling occurred).
+ *
+ * crh 07-Apr-1998
+ *
+ * ************************************************************************** **
+ */
+static void cache_mangled_name( char *mangled_name, char *raw_name )
+{
+ ubi_cacheEntryPtr new_entry;
+ char *s1;
+ char *s2;
+ size_t mangled_len;
+ size_t raw_len;
+ size_t i;
+
+ /* If the cache isn't initialized, give up. */
+ if( !mc_initialized )
+ return;
+
+ /* Init the string lengths. */
+ mangled_len = strlen( mangled_name );
+ raw_len = strlen( raw_name );
+
+ /* See if the extensions are unmangled. If so, store the entry
+ * without the extension, thus creating a "group" reverse map.
+ */
+ s1 = strrchr( mangled_name, '.' );
+ if( s1 && (s2 = strrchr( raw_name, '.' )) ) {
+ i = 1;
+ while( s1[i] && (tolower( s1[i] ) == s2[i]) )
+ i++;
+ if( !s1[i] && !s2[i] ) {
+ mangled_len -= i;
+ raw_len -= i;
+ }
+ }
+
+ /* Allocate a new cache entry. If the allocation fails, just return. */
+ i = sizeof( ubi_cacheEntry ) + mangled_len + raw_len + 2;
+ new_entry = malloc( i );
+ if( !new_entry )
+ return;
+
+ /* Fill the new cache entry, and add it to the cache. */
+ s1 = (char *)(new_entry + 1);
+ s2 = (char *)&(s1[mangled_len + 1]);
+ safe_strcpy( s1, mangled_name, mangled_len );
+ safe_strcpy( s2, raw_name, raw_len );
+ ubi_cachePut( mangled_cache, i, new_entry, s1 );
+}
+
+/* ************************************************************************** **
+ * Check for a name on the mangled name stack
+ *
+ * Input: s - Input *and* output string buffer.
+ *
+ * Output: True if the name was found in the cache, else False.
+ *
+ * Notes: If a reverse map is found, the function will overwrite the string
+ * space indicated by the input pointer <s>. This is frightening.
+ * It should be rewritten to return NULL if the long name was not
+ * found, and a pointer to the long name if it was found.
+ *
+ * ************************************************************************** **
+ */
+
+static BOOL check_cache( char *s )
+{
+ ubi_cacheEntryPtr FoundPtr;
+ char *ext_start = NULL;
+ char *found_name;
+ char *saved_ext = NULL;
+
+ /* If the cache isn't initialized, give up. */
+ if( !mc_initialized )
+ return( False );
+
+ FoundPtr = ubi_cacheGet( mangled_cache, (ubi_trItemPtr)s );
+
+ /* If we didn't find the name *with* the extension, try without. */
+ if( !FoundPtr ) {
+ ext_start = strrchr( s, '.' );
+ if( ext_start ) {
+ if((saved_ext = strdup(ext_start)) == NULL)
+ return False;
+
+ *ext_start = '\0';
+ FoundPtr = ubi_cacheGet( mangled_cache, (ubi_trItemPtr)s );
+ /*
+ * At this point s is the name without the
+ * extension. We re-add the extension if saved_ext
+ * is not null, before freeing saved_ext.
+ */
+ }
+ }
+
+ /* Okay, if we haven't found it we're done. */
+ if( !FoundPtr ) {
+ if(saved_ext) {
+ /* Replace the saved_ext as it was truncated. */
+ (void)pstrcat( s, saved_ext );
+ SAFE_FREE(saved_ext);
+ }
+ return( False );
+ }
+
+ /* If we *did* find it, we need to copy it into the string buffer. */
+ found_name = (char *)(FoundPtr + 1);
+ found_name += (strlen( found_name ) + 1);
+
+ (void)pstrcpy( s, found_name );
+ if( saved_ext ) {
+ /* Replace the saved_ext as it was truncated. */
+ (void)pstrcat( s, saved_ext );
+ SAFE_FREE(saved_ext);
+ }
+
+ return( True );
+}
+
+/*****************************************************************************
+ * do the actual mangling to 8.3 format
+ * the buffer must be able to hold 13 characters (including the null)
+ *****************************************************************************
+ */
+static void to_8_3(char *s)
+{
+ int csum;
+ char *p;
+ char extension[4];
+ char base[9];
+ int baselen = 0;
+ int extlen = 0;
+
+ extension[0] = 0;
+ base[0] = 0;
+
+ p = strrchr(s,'.');
+ if( p && (strlen(p+1) < (size_t)4) ) {
+ BOOL all_normal = ( strisnormal(p+1) ); /* XXXXXXXXX */
+
+ if( all_normal && p[1] != 0 ) {
+ *p = 0;
+ csum = str_checksum( s );
+ *p = '.';
+ } else
+ csum = str_checksum(s);
+ } else
+ csum = str_checksum(s);
+
+ strupper_m( s );
+
+ if( p ) {
+ if( p == s )
+ safe_strcpy( extension, "___", 3 );
+ else {
+ *p++ = 0;
+ while( *p && extlen < 3 ) {
+ if ( *p != '.') {
+ extension[extlen++] = p[0];
+ }
+ p++;
+ }
+ extension[extlen] = 0;
+ }
+ }
+
+ p = s;
+
+ while( *p && baselen < 5 ) {
+ if (*p != '.') {
+ base[baselen++] = p[0];
+ }
+ p++;
+ }
+ base[baselen] = 0;
+
+ csum = csum % (MANGLE_BASE*MANGLE_BASE);
+
+ (void)slprintf(s, 12, "%s%c%c%c",
+ base, magic_char, mangle( csum/MANGLE_BASE ), mangle( csum ) );
+
+ if( *extension ) {
+ (void)pstrcat( s, "." );
+ (void)pstrcat( s, extension );
+ }
+}
+
+/*****************************************************************************
+ * Convert a filename to DOS format. Return True if successful.
+ *
+ * Input: OutName - Source *and* destination buffer.
+ *
+ * NOTE that OutName must point to a memory space that
+ * is at least 13 bytes in size!
+ *
+ * need83 - If False, name mangling will be skipped unless the
+ * name contains illegal characters. Mapping will still
+ * be done, if appropriate. This is probably used to
+ * signal that a client does not require name mangling,
+ * thus skipping the name mangling even on shares which
+ * have name-mangling turned on.
+ * cache83 - If False, the mangled name cache will not be updated.
+ * This is usually used to prevent that we overwrite
+ * a conflicting cache entry prematurely, i.e. before
+ * we know whether the client is really interested in the
+ * current name. (See PR#13758). UKD.
+ *
+ * Output: Returns False only if the name wanted mangling but the share does
+ * not have name mangling turned on.
+ *
+ * ****************************************************************************
+ */
+
+static void name_map(char *OutName, BOOL need83, BOOL cache83)
+{
+ smb_ucs2_t *OutName_ucs2;
+ DEBUG(5,("name_map( %s, need83 = %s, cache83 = %s)\n", OutName,
+ need83 ? "True" : "False", cache83 ? "True" : "False"));
+
+ if (push_ucs2_allocate(&OutName_ucs2, OutName) == (size_t)-1) {
+ DEBUG(0, ("push_ucs2_allocate failed!\n"));
+ return;
+ }
+
+ if( !need83 && !NT_STATUS_IS_OK(is_valid_name(OutName_ucs2, False, False)))
+ need83 = True;
+
+ /* check if it's already in 8.3 format */
+ if (need83 && !NT_STATUS_IS_OK(is_8_3_w(OutName_ucs2, False))) {
+ char *tmp = NULL;
+
+ /* mangle it into 8.3 */
+ if (cache83)
+ tmp = strdup(OutName);
+
+ to_8_3(OutName);
+
+ if(tmp != NULL) {
+ cache_mangled_name(OutName, tmp);
+ SAFE_FREE(tmp);
+ }
+ }
+
+ DEBUG(5,("name_map() ==> [%s]\n", OutName));
+ SAFE_FREE(OutName_ucs2);
+}
+
+/*
+ the following provides the abstraction layer to make it easier
+ to drop in an alternative mangling implementation
+*/
+static struct mangle_fns mangle_fns = {
+ is_mangled,
+ is_8_3,
+ mangle_reset,
+ check_cache,
+ name_map
+};
+
+/* return the methods for this mangling implementation */
+struct mangle_fns *mangle_hash_init(void)
+{
+ mangle_reset();
+
+ return &mangle_fns;
+}
diff --git a/source/smbd/mangle_hash2.c b/source/smbd/mangle_hash2.c
new file mode 100644
index 00000000000..62087e7e593
--- /dev/null
+++ b/source/smbd/mangle_hash2.c
@@ -0,0 +1,709 @@
+/*
+ Unix SMB/CIFS implementation.
+ new hash based name mangling implementation
+ Copyright (C) Andrew Tridgell 2002
+ Copyright (C) Simo Sorce 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ this mangling scheme uses the following format
+
+ Annnn~n.AAA
+
+ where nnnnn is a base 36 hash, and A represents characters from the original string
+
+ The hash is taken of the leading part of the long filename, in uppercase
+
+ for simplicity, we only allow ascii characters in 8.3 names
+ */
+
+ /* hash alghorithm changed to FNV1 by idra@samba.org (Simo Sorce).
+ * see http://www.isthe.com/chongo/tech/comp/fnv/index.html for a
+ * discussion on Fowler / Noll / Vo (FNV) Hash by one of it's authors
+ */
+
+/*
+ ===============================================================================
+ NOTE NOTE NOTE!!!
+
+ This file deliberately uses non-multibyte string functions in many places. This
+ is *not* a mistake. This code is multi-byte safe, but it gets this property
+ through some very subtle knowledge of the way multi-byte strings are encoded
+ and the fact that this mangling algorithm only supports ascii characters in
+ 8.3 names.
+
+ please don't convert this file to use the *_m() functions!!
+ ===============================================================================
+*/
+
+
+#include "includes.h"
+
+#if 1
+#define M_DEBUG(level, x) DEBUG(level, x)
+#else
+#define M_DEBUG(level, x)
+#endif
+
+/* these flags are used to mark characters in as having particular
+ properties */
+#define FLAG_BASECHAR 1
+#define FLAG_ASCII 2
+#define FLAG_ILLEGAL 4
+#define FLAG_WILDCARD 8
+
+/* the "possible" flags are used as a fast way to find possible DOS
+ reserved filenames */
+#define FLAG_POSSIBLE1 16
+#define FLAG_POSSIBLE2 32
+#define FLAG_POSSIBLE3 64
+#define FLAG_POSSIBLE4 128
+
+/* by default have a max of 4096 entries in the cache. */
+#ifndef MANGLE_CACHE_SIZE
+#define MANGLE_CACHE_SIZE 4096
+#endif
+
+#define FNV1_PRIME 0x01000193
+/*the following number is a fnv1 of the string: idra@samba.org 2002 */
+#define FNV1_INIT 0xa6b93095
+
+/* these tables are used to provide fast tests for characters */
+static unsigned char char_flags[256];
+
+#define FLAG_CHECK(c, flag) (char_flags[(unsigned char)(c)] & (flag))
+
+/*
+ this determines how many characters are used from the original filename
+ in the 8.3 mangled name. A larger value leads to a weaker hash and more collisions.
+ The largest possible value is 6.
+*/
+static unsigned mangle_prefix;
+
+/* we will use a very simple direct mapped prefix cache. The big
+ advantage of this cache structure is speed and low memory usage
+
+ The cache is indexed by the low-order bits of the hash, and confirmed by
+ hashing the resulting cache entry to match the known hash
+*/
+static char **prefix_cache;
+static u32 *prefix_cache_hashes;
+
+/* these are the characters we use in the 8.3 hash. Must be 36 chars long */
+static const char *basechars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+static unsigned char base_reverse[256];
+#define base_forward(v) basechars[v]
+
+/* the list of reserved dos names - all of these are illegal */
+static const char *reserved_names[] =
+{ "AUX", "LOCK$", "CON", "COM1", "COM2", "COM3", "COM4",
+ "LPT1", "LPT2", "LPT3", "NUL", "PRN", NULL };
+
+/*
+ hash a string of the specified length. The string does not need to be
+ null terminated
+
+ this hash needs to be fast with a low collision rate (what hash doesn't?)
+*/
+static u32 mangle_hash(const char *key, unsigned length)
+{
+ u32 value;
+ u32 i;
+ fstring str;
+
+ /* we have to uppercase here to ensure that the mangled name
+ doesn't depend on the case of the long name. Note that this
+ is the only place where we need to use a multi-byte string
+ function */
+ strncpy(str, key, length);
+ str[length] = 0;
+ strupper_m(str);
+
+ /* the length of a multi-byte string can change after a strupper_m */
+ length = strlen(str);
+
+ /* Set the initial value from the key size. */
+ for (value = FNV1_INIT, i=0; i < length; i++) {
+ value *= (u32)FNV1_PRIME;
+ value ^= (u32)(str[i]);
+ }
+
+ /* note that we force it to a 31 bit hash, to keep within the limits
+ of the 36^6 mangle space */
+ return value & ~0x80000000;
+}
+
+/*
+ initialise (ie. allocate) the prefix cache
+ */
+static BOOL cache_init(void)
+{
+ if (prefix_cache) return True;
+
+ prefix_cache = calloc(MANGLE_CACHE_SIZE, sizeof(char *));
+ if (!prefix_cache) return False;
+
+ prefix_cache_hashes = calloc(MANGLE_CACHE_SIZE, sizeof(u32));
+ if (!prefix_cache_hashes) return False;
+
+ return True;
+}
+
+/*
+ insert an entry into the prefix cache. The string might not be null
+ terminated */
+static void cache_insert(const char *prefix, int length, u32 hash)
+{
+ int i = hash % MANGLE_CACHE_SIZE;
+
+ if (prefix_cache[i]) {
+ free(prefix_cache[i]);
+ }
+
+ prefix_cache[i] = strndup(prefix, length);
+ prefix_cache_hashes[i] = hash;
+}
+
+/*
+ lookup an entry in the prefix cache. Return NULL if not found.
+*/
+static const char *cache_lookup(u32 hash)
+{
+ int i = hash % MANGLE_CACHE_SIZE;
+
+ if (!prefix_cache[i] || hash != prefix_cache_hashes[i]) {
+ return NULL;
+ }
+
+ /* yep, it matched */
+ return prefix_cache[i];
+}
+
+
+/*
+ determine if a string is possibly in a mangled format, ignoring
+ case
+
+ In this algorithm, mangled names use only pure ascii characters (no
+ multi-byte) so we can avoid doing a UCS2 conversion
+ */
+static BOOL is_mangled_component(const char *name, size_t len)
+{
+ unsigned int i;
+
+ M_DEBUG(10,("is_mangled_component %s (len %u) ?\n", name, (unsigned int)len));
+
+ /* check the length */
+ if (len > 12 || len < 8)
+ return False;
+
+ /* the best distinguishing characteristic is the ~ */
+ if (name[6] != '~')
+ return False;
+
+ /* check extension */
+ if (len > 8) {
+ if (name[8] != '.')
+ return False;
+ for (i=9; name[i] && i < len; i++) {
+ if (! FLAG_CHECK(name[i], FLAG_ASCII)) {
+ return False;
+ }
+ }
+ }
+
+ /* check lead characters */
+ for (i=0;i<mangle_prefix;i++) {
+ if (! FLAG_CHECK(name[i], FLAG_ASCII)) {
+ return False;
+ }
+ }
+
+ /* check rest of hash */
+ if (! FLAG_CHECK(name[7], FLAG_BASECHAR)) {
+ return False;
+ }
+ for (i=mangle_prefix;i<6;i++) {
+ if (! FLAG_CHECK(name[i], FLAG_BASECHAR)) {
+ return False;
+ }
+ }
+
+ M_DEBUG(10,("is_mangled_component %s (len %u) -> yes\n", name, (unsigned int)len));
+
+ return True;
+}
+
+
+
+/*
+ determine if a string is possibly in a mangled format, ignoring
+ case
+
+ In this algorithm, mangled names use only pure ascii characters (no
+ multi-byte) so we can avoid doing a UCS2 conversion
+
+ NOTE! This interface must be able to handle a path with unix
+ directory separators. It should return true if any component is
+ mangled
+ */
+static BOOL is_mangled(const char *name)
+{
+ const char *p;
+ const char *s;
+
+ M_DEBUG(10,("is_mangled %s ?\n", name));
+
+ for (s=name; (p=strchr(s, '/')); s=p+1) {
+ if (is_mangled_component(s, PTR_DIFF(p, s))) {
+ return True;
+ }
+ }
+
+ /* and the last part ... */
+ return is_mangled_component(s,strlen(s));
+}
+
+
+/*
+ see if a filename is an allowable 8.3 name.
+
+ we are only going to allow ascii characters in 8.3 names, as this
+ simplifies things greatly (it means that we know the string won't
+ get larger when converted from UNIX to DOS formats)
+*/
+static BOOL is_8_3(const char *name, BOOL check_case, BOOL allow_wildcards)
+{
+ int len, i;
+ char *dot_p;
+
+ /* as a special case, the names '.' and '..' are allowable 8.3 names */
+ if (name[0] == '.') {
+ if (!name[1] || (name[1] == '.' && !name[2])) {
+ return True;
+ }
+ }
+
+ /* the simplest test is on the overall length of the
+ filename. Note that we deliberately use the ascii string
+ length (not the multi-byte one) as it is faster, and gives us
+ the result we need in this case. Using strlen_m would not
+ only be slower, it would be incorrect */
+ len = strlen(name);
+ if (len > 12)
+ return False;
+
+ /* find the '.'. Note that once again we use the non-multibyte
+ function */
+ dot_p = strchr(name, '.');
+
+ if (!dot_p) {
+ /* if the name doesn't contain a '.' then its length
+ must be less than 8 */
+ if (len > 8) {
+ return False;
+ }
+ } else {
+ int prefix_len, suffix_len;
+
+ /* if it does contain a dot then the prefix must be <=
+ 8 and the suffix <= 3 in length */
+ prefix_len = PTR_DIFF(dot_p, name);
+ suffix_len = len - (prefix_len+1);
+
+ if (prefix_len > 8 || suffix_len > 3 || suffix_len == 0) {
+ return False;
+ }
+
+ /* a 8.3 name cannot contain more than 1 '.' */
+ if (strchr(dot_p+1, '.')) {
+ return False;
+ }
+ }
+
+ /* the length are all OK. Now check to see if the characters themselves are OK */
+ for (i=0; name[i]; i++) {
+ /* note that we may allow wildcard petterns! */
+ if (!FLAG_CHECK(name[i], FLAG_ASCII|(allow_wildcards ? FLAG_WILDCARD : 0)) && name[i] != '.') {
+ return False;
+ }
+ }
+
+ /* it is a good 8.3 name */
+ return True;
+}
+
+
+/*
+ reset the mangling cache on a smb.conf reload. This only really makes sense for
+ mangling backends that have parameters in smb.conf, and as this backend doesn't
+ this is a NULL operation
+*/
+static void mangle_reset(void)
+{
+ /* noop */
+}
+
+
+/*
+ try to find a 8.3 name in the cache, and if found then
+ replace the string with the original long name.
+
+ The filename must be able to hold at least sizeof(fstring)
+*/
+static BOOL check_cache(char *name)
+{
+ u32 hash, multiplier;
+ unsigned int i;
+ const char *prefix;
+ char extension[4];
+
+ /* make sure that this is a mangled name from this cache */
+ if (!is_mangled(name)) {
+ M_DEBUG(10,("check_cache: %s -> not mangled\n", name));
+ return False;
+ }
+
+ /* we need to extract the hash from the 8.3 name */
+ hash = base_reverse[(unsigned char)name[7]];
+ for (multiplier=36, i=5;i>=mangle_prefix;i--) {
+ u32 v = base_reverse[(unsigned char)name[i]];
+ hash += multiplier * v;
+ multiplier *= 36;
+ }
+
+ /* now look in the prefix cache for that hash */
+ prefix = cache_lookup(hash);
+ if (!prefix) {
+ M_DEBUG(10,("check_cache: %s -> %08X -> not found\n", name, hash));
+ return False;
+ }
+
+ /* we found it - construct the full name */
+ if (name[8] == '.') {
+ strncpy(extension, name+9, 3);
+ extension[3] = 0;
+ } else {
+ extension[0] = 0;
+ }
+
+ if (extension[0]) {
+ M_DEBUG(10,("check_cache: %s -> %s.%s\n", name, prefix, extension));
+ slprintf(name, sizeof(fstring), "%s.%s", prefix, extension);
+ } else {
+ M_DEBUG(10,("check_cache: %s -> %s\n", name, prefix));
+ fstrcpy(name, prefix);
+ }
+
+ return True;
+}
+
+
+/*
+ look for a DOS reserved name
+*/
+static BOOL is_reserved_name(const char *name)
+{
+ if (FLAG_CHECK(name[0], FLAG_POSSIBLE1) &&
+ FLAG_CHECK(name[1], FLAG_POSSIBLE2) &&
+ FLAG_CHECK(name[2], FLAG_POSSIBLE3) &&
+ FLAG_CHECK(name[3], FLAG_POSSIBLE4)) {
+ /* a likely match, scan the lot */
+ int i;
+ for (i=0; reserved_names[i]; i++) {
+ int len = strlen(reserved_names[i]);
+ /* note that we match on COM1 as well as COM1.foo */
+ if (strnequal(name, reserved_names[i], len) &&
+ (name[len] == '.' || name[len] == 0)) {
+ return True;
+ }
+ }
+ }
+
+ return False;
+}
+
+/*
+ See if a filename is a legal long filename.
+ A filename ending in a '.' is not legal unless it's "." or "..". JRA.
+*/
+
+static BOOL is_legal_name(const char *name)
+{
+ const char *dot_pos = NULL;
+ BOOL alldots = True;
+ size_t numdots = 0;
+
+ while (*name) {
+ if (((unsigned int)name[0]) > 128 && (name[1] != 0)) {
+ /* Possible start of mb character. */
+ char mbc[2];
+ /*
+ * Note that if CH_UNIX is utf8 a string may be 3
+ * bytes, but this is ok as mb utf8 characters don't
+ * contain embedded ascii bytes. We are really checking
+ * for mb UNIX asian characters like Japanese (SJIS) here.
+ * JRA.
+ */
+ if (convert_string(CH_UNIX, CH_UCS2, name, 2, mbc, 2, False) == 2) {
+ /* Was a good mb string. */
+ name += 2;
+ continue;
+ }
+ }
+
+ if (FLAG_CHECK(name[0], FLAG_ILLEGAL)) {
+ return False;
+ }
+ if (name[0] == '.') {
+ dot_pos = name;
+ numdots++;
+ } else {
+ alldots = False;
+ }
+ name++;
+ }
+
+ if (dot_pos) {
+ if (alldots && (numdots == 1 || numdots == 2))
+ return True; /* . or .. is a valid name */
+
+ /* A valid long name cannot end in '.' */
+ if (dot_pos[1] == '\0')
+ return False;
+ }
+
+ return True;
+}
+
+/*
+ the main forward mapping function, which converts a long filename to
+ a 8.3 name
+
+ if need83 is not set then we only do the mangling if the name is illegal
+ as a long name
+
+ if cache83 is not set then we don't cache the result
+
+ the name parameter must be able to hold 13 bytes
+*/
+static void name_map(fstring name, BOOL need83, BOOL cache83)
+{
+ char *dot_p;
+ char lead_chars[7];
+ char extension[4];
+ unsigned int extension_length, i;
+ unsigned int prefix_len;
+ u32 hash, v;
+ char new_name[13];
+
+ /* reserved names are handled specially */
+ if (!is_reserved_name(name)) {
+ /* if the name is already a valid 8.3 name then we don't need to
+ do anything */
+ if (is_8_3(name, False, False)) {
+ return;
+ }
+
+ /* if the caller doesn't strictly need 8.3 then just check for illegal
+ filenames */
+ if (!need83 && is_legal_name(name)) {
+ return;
+ }
+ }
+
+ /* find the '.' if any */
+ dot_p = strrchr(name, '.');
+
+ if (dot_p) {
+ /* if the extension contains any illegal characters or
+ is too long or zero length then we treat it as part
+ of the prefix */
+ for (i=0; i<4 && dot_p[i+1]; i++) {
+ if (! FLAG_CHECK(dot_p[i+1], FLAG_ASCII)) {
+ dot_p = NULL;
+ break;
+ }
+ }
+ if (i == 0 || i == 4) dot_p = NULL;
+ }
+
+ /* the leading characters in the mangled name is taken from
+ the first characters of the name, if they are ascii otherwise
+ '_' is used
+ */
+ for (i=0;i<mangle_prefix && name[i];i++) {
+ lead_chars[i] = name[i];
+ if (! FLAG_CHECK(lead_chars[i], FLAG_ASCII)) {
+ lead_chars[i] = '_';
+ }
+ lead_chars[i] = toupper(lead_chars[i]);
+ }
+ for (;i<mangle_prefix;i++) {
+ lead_chars[i] = '_';
+ }
+
+ /* the prefix is anything up to the first dot */
+ if (dot_p) {
+ prefix_len = PTR_DIFF(dot_p, name);
+ } else {
+ prefix_len = strlen(name);
+ }
+
+ /* the extension of the mangled name is taken from the first 3
+ ascii chars after the dot */
+ extension_length = 0;
+ if (dot_p) {
+ for (i=1; extension_length < 3 && dot_p[i]; i++) {
+ char c = dot_p[i];
+ if (FLAG_CHECK(c, FLAG_ASCII)) {
+ extension[extension_length++] = toupper(c);
+ }
+ }
+ }
+
+ /* find the hash for this prefix */
+ v = hash = mangle_hash(name, prefix_len);
+
+ /* now form the mangled name. */
+ for (i=0;i<mangle_prefix;i++) {
+ new_name[i] = lead_chars[i];
+ }
+ new_name[7] = base_forward(v % 36);
+ new_name[6] = '~';
+ for (i=5; i>=mangle_prefix; i--) {
+ v = v / 36;
+ new_name[i] = base_forward(v % 36);
+ }
+
+ /* add the extension */
+ if (extension_length) {
+ new_name[8] = '.';
+ memcpy(&new_name[9], extension, extension_length);
+ new_name[9+extension_length] = 0;
+ } else {
+ new_name[8] = 0;
+ }
+
+ if (cache83) {
+ /* put it in the cache */
+ cache_insert(name, prefix_len, hash);
+ }
+
+ M_DEBUG(10,("name_map: %s -> %08X -> %s (cache=%d)\n",
+ name, hash, new_name, cache83));
+
+ /* and overwrite the old name */
+ fstrcpy(name, new_name);
+
+ /* all done, we've managed to mangle it */
+}
+
+
+/* initialise the flags table
+
+ we allow only a very restricted set of characters as 'ascii' in this
+ mangling backend. This isn't a significant problem as modern clients
+ use the 'long' filenames anyway, and those don't have these
+ restrictions.
+*/
+static void init_tables(void)
+{
+ int i;
+
+ memset(char_flags, 0, sizeof(char_flags));
+
+ for (i=1;i<128;i++) {
+ if ((i >= '0' && i <= '9') ||
+ (i >= 'a' && i <= 'z') ||
+ (i >= 'A' && i <= 'Z')) {
+ char_flags[i] |= (FLAG_ASCII | FLAG_BASECHAR);
+ }
+ if (strchr("_-$~", i)) {
+ char_flags[i] |= FLAG_ASCII;
+ }
+
+ if (strchr("*\\/?<>|\":", i)) {
+ char_flags[i] |= FLAG_ILLEGAL;
+ }
+
+ if (strchr("*?\"<>", i)) {
+ char_flags[i] |= FLAG_WILDCARD;
+ }
+ }
+
+ memset(base_reverse, 0, sizeof(base_reverse));
+ for (i=0;i<36;i++) {
+ base_reverse[(unsigned char)base_forward(i)] = i;
+ }
+
+ /* fill in the reserved names flags. These are used as a very
+ fast filter for finding possible DOS reserved filenames */
+ for (i=0; reserved_names[i]; i++) {
+ unsigned char c1, c2, c3, c4;
+
+ c1 = (unsigned char)reserved_names[i][0];
+ c2 = (unsigned char)reserved_names[i][1];
+ c3 = (unsigned char)reserved_names[i][2];
+ c4 = (unsigned char)reserved_names[i][3];
+
+ char_flags[c1] |= FLAG_POSSIBLE1;
+ char_flags[c2] |= FLAG_POSSIBLE2;
+ char_flags[c3] |= FLAG_POSSIBLE3;
+ char_flags[c4] |= FLAG_POSSIBLE4;
+ char_flags[tolower(c1)] |= FLAG_POSSIBLE1;
+ char_flags[tolower(c2)] |= FLAG_POSSIBLE2;
+ char_flags[tolower(c3)] |= FLAG_POSSIBLE3;
+ char_flags[tolower(c4)] |= FLAG_POSSIBLE4;
+
+ char_flags[(unsigned char)'.'] |= FLAG_POSSIBLE4;
+ }
+}
+
+
+/*
+ the following provides the abstraction layer to make it easier
+ to drop in an alternative mangling implementation */
+static struct mangle_fns mangle_fns = {
+ is_mangled,
+ is_8_3,
+ mangle_reset,
+ check_cache,
+ name_map
+};
+
+/* return the methods for this mangling implementation */
+struct mangle_fns *mangle_hash2_init(void)
+{
+ /* the mangle prefix can only be in the mange 1 to 6 */
+ mangle_prefix = lp_mangle_prefix();
+ if (mangle_prefix > 6) {
+ mangle_prefix = 6;
+ }
+ if (mangle_prefix < 1) {
+ mangle_prefix = 1;
+ }
+
+ init_tables();
+ mangle_reset();
+
+ if (!cache_init()) {
+ return NULL;
+ }
+
+ return &mangle_fns;
+}
diff --git a/source/smbd/mangle_map.c b/source/smbd/mangle_map.c
new file mode 100644
index 00000000000..9e798fd41b4
--- /dev/null
+++ b/source/smbd/mangle_map.c
@@ -0,0 +1,212 @@
+/*
+ Unix SMB/CIFS implementation.
+ Name mapping code
+ Copyright (C) Jeremy Allison 1998
+ Copyright (C) Andrew Tridgell 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+
+/* ************************************************************************** **
+ * Used only in do_fwd_mangled_map(), below.
+ * ************************************************************************** **
+ */
+static char *map_filename( char *s, /* This is null terminated */
+ const char *pattern, /* This isn't. */
+ int len ) /* This is the length of pattern. */
+ {
+ static pstring matching_bit; /* The bit of the string which matches */
+ /* a * in pattern if indeed there is a * */
+ char *sp; /* Pointer into s. */
+ char *pp; /* Pointer into p. */
+ char *match_start; /* Where the matching bit starts. */
+ pstring pat;
+
+ StrnCpy( pat, pattern, len ); /* Get pattern into a proper string! */
+ pstrcpy( matching_bit, "" ); /* Match but no star gets this. */
+ pp = pat; /* Initialize the pointers. */
+ sp = s;
+
+ if( strequal(s, ".") || strequal(s, ".."))
+ {
+ return NULL; /* Do not map '.' and '..' */
+ }
+
+ if( (len == 1) && (*pattern == '*') )
+ {
+ return NULL; /* Impossible, too ambiguous for */
+ } /* words! */
+
+ while( (*sp) /* Not the end of the string. */
+ && (*pp) /* Not the end of the pattern. */
+ && (*sp == *pp) /* The two match. */
+ && (*pp != '*') ) /* No wildcard. */
+ {
+ sp++; /* Keep looking. */
+ pp++;
+ }
+
+ if( !*sp && !*pp ) /* End of pattern. */
+ return( matching_bit ); /* Simple match. Return empty string. */
+
+ if( *pp == '*' )
+ {
+ pp++; /* Always interrested in the chacter */
+ /* after the '*' */
+ if( !*pp ) /* It is at the end of the pattern. */
+ {
+ StrnCpy( matching_bit, s, sp-s );
+ return( matching_bit );
+ }
+ else
+ {
+ /* The next character in pattern must match a character further */
+ /* along s than sp so look for that character. */
+ match_start = sp;
+ while( (*sp) /* Not the end of s. */
+ && (*sp != *pp) ) /* Not the same */
+ sp++; /* Keep looking. */
+ if( !*sp ) /* Got to the end without a match. */
+ {
+ return( NULL );
+ } /* Still hope for a match. */
+ else
+ {
+ /* Now sp should point to a matching character. */
+ StrnCpy(matching_bit, match_start, sp-match_start);
+ /* Back to needing a stright match again. */
+ while( (*sp) /* Not the end of the string. */
+ && (*pp) /* Not the end of the pattern. */
+ && (*sp == *pp) ) /* The two match. */
+ {
+ sp++; /* Keep looking. */
+ pp++;
+ }
+ if( !*sp && !*pp ) /* Both at end so it matched */
+ return( matching_bit );
+ else
+ return( NULL );
+ }
+ }
+ }
+ return( NULL ); /* No match. */
+ } /* map_filename */
+
+
+/* ************************************************************************** **
+ * MangledMap is a series of name pairs in () separated by spaces.
+ * If s matches the first of the pair then the name given is the
+ * second of the pair. A * means any number of any character and if
+ * present in the second of the pair as well as the first the
+ * matching part of the first string takes the place of the * in the
+ * second.
+ *
+ * I wanted this so that we could have RCS files which can be used
+ * by UNIX and DOS programs. My mapping string is (RCS rcs) which
+ * converts the UNIX RCS file subdirectory to lowercase thus
+ * preventing mangling.
+ *
+ * See 'mangled map' in smb.conf(5).
+ *
+ * ************************************************************************** **
+ */
+static void mangled_map(char *s, const char *MangledMap)
+{
+ const char *start=MangledMap; /* Use this to search for mappings. */
+ const char *end; /* Used to find the end of strings. */
+ char *match_string;
+ pstring new_string; /* Make up the result here. */
+ char *np; /* Points into new_string. */
+
+ DEBUG( 5, ("Mangled Mapping '%s' map '%s'\n", s, MangledMap) );
+ while( *start ) {
+ while( (*start) && (*start != '(') )
+ start++;
+ if( !*start )
+ continue; /* Always check for the end. */
+ start++; /* Skip the ( */
+ end = start; /* Search for the ' ' or a ')' */
+ DEBUG( 5, ("Start of first in pair '%s'\n", start) );
+ while( (*end) && !((*end == ' ') || (*end == ')')) )
+ end++;
+ if( !*end ) {
+ start = end;
+ continue; /* Always check for the end. */
+ }
+ DEBUG( 5, ("End of first in pair '%s'\n", end) );
+ if( (match_string = map_filename( s, start, end-start )) ) {
+ int size_left = sizeof(new_string) - 1;
+ DEBUG( 5, ("Found a match\n") );
+ /* Found a match. */
+ start = end + 1; /* Point to start of what it is to become. */
+ DEBUG( 5, ("Start of second in pair '%s'\n", start) );
+ end = start;
+ np = new_string;
+ while( (*end && size_left > 0) /* Not the end of string. */
+ && (*end != ')') /* Not the end of the pattern. */
+ && (*end != '*') ) { /* Not a wildcard. */
+ *np++ = *end++;
+ size_left--;
+ }
+
+ if( !*end ) {
+ start = end;
+ continue; /* Always check for the end. */
+ }
+ if( *end == '*' ) {
+ if (size_left > 0 )
+ safe_strcpy( np, match_string, size_left );
+ np += strlen( match_string );
+ size_left -= strlen( match_string );
+ end++; /* Skip the '*' */
+ while ((*end && size_left > 0) /* Not the end of string. */
+ && (*end != ')') /* Not the end of the pattern. */
+ && (*end != '*')) { /* Not a wildcard. */
+ *np++ = *end++;
+ size_left--;
+ }
+ }
+ if (!*end) {
+ start = end;
+ continue; /* Always check for the end. */
+ }
+ if (size_left > 0)
+ *np++ = '\0'; /* NULL terminate it. */
+ DEBUG(5,("End of second in pair '%s'\n", end));
+ new_string[sizeof(new_string)-1] = '\0';
+ pstrcpy( s, new_string ); /* Substitute with the new name. */
+ DEBUG( 5, ("s is now '%s'\n", s) );
+ }
+ start = end; /* Skip a bit which cannot be wanted anymore. */
+ start++;
+ }
+}
+
+/*
+ front end routine to the mangled map code
+ personally I think that the whole idea of "mangled map" is completely bogus
+*/
+void mangle_map_filename(fstring fname, int snum)
+{
+ char *map;
+
+ map = lp_mangled_map(snum);
+ if (!map || !*map) return;
+
+ mangled_map(fname, map);
+}
diff --git a/source/smbd/message.c b/source/smbd/message.c
new file mode 100644
index 00000000000..f853a914753
--- /dev/null
+++ b/source/smbd/message.c
@@ -0,0 +1,237 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB messaging
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+/*
+ This file handles the messaging system calls for winpopup style
+ messages
+*/
+
+
+#include "includes.h"
+
+extern userdom_struct current_user_info;
+
+/* look in server.c for some explanation of these variables */
+static char msgbuf[1600];
+static int msgpos;
+static fstring msgfrom;
+static fstring msgto;
+
+/****************************************************************************
+deliver the message
+****************************************************************************/
+static void msg_deliver(void)
+{
+ pstring name;
+ int i;
+ int fd;
+ char *msg;
+ int len;
+
+ if (! (*lp_msg_command()))
+ {
+ DEBUG(1,("no messaging command specified\n"));
+ msgpos = 0;
+ return;
+ }
+
+ /* put it in a temporary file */
+ slprintf(name,sizeof(name)-1, "%s/msg.XXXXXX",tmpdir());
+ fd = smb_mkstemp(name);
+
+ if (fd == -1) {
+ DEBUG(1,("can't open message file %s\n",name));
+ return;
+ }
+
+ /*
+ * Incoming message is in DOS codepage format. Convert to UNIX.
+ */
+
+ if ((len = (int)convert_string_allocate(NULL,CH_DOS, CH_UNIX, msgbuf, msgpos, (void **) &msg, True)) < 0 || !msg) {
+ DEBUG(3,("Conversion failed, delivering message in DOS codepage format\n"));
+ for (i = 0; i < msgpos;) {
+ if (msgbuf[i] == '\r' && i < (msgpos-1) && msgbuf[i+1] == '\n') {
+ i++; continue;
+ }
+ write(fd, &msgbuf[i++], 1);
+ }
+ } else {
+ for (i = 0; i < len;) {
+ if (msg[i] == '\r' && i < (len-1) && msg[i+1] == '\n') {
+ i++; continue;
+ }
+ write(fd, &msg[i++],1);
+ }
+ SAFE_FREE(msg);
+ }
+ close(fd);
+
+
+ /* run the command */
+ if (*lp_msg_command())
+ {
+ fstring alpha_msgfrom;
+ fstring alpha_msgto;
+ pstring s;
+
+ pstrcpy(s,lp_msg_command());
+ pstring_sub(s,"%f",alpha_strcpy(alpha_msgfrom,msgfrom,NULL,sizeof(alpha_msgfrom)));
+ pstring_sub(s,"%t",alpha_strcpy(alpha_msgto,msgto,NULL,sizeof(alpha_msgto)));
+ standard_sub_basic(current_user_info.smb_name, s, sizeof(s));
+ pstring_sub(s,"%s",name);
+ smbrun(s,NULL);
+ }
+
+ msgpos = 0;
+}
+
+
+
+/****************************************************************************
+ reply to a sends
+****************************************************************************/
+int reply_sends(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int len;
+ char *msg;
+ int outsize = 0;
+ char *p;
+
+ START_PROFILE(SMBsends);
+
+ msgpos = 0;
+
+ if (! (*lp_msg_command())) {
+ END_PROFILE(SMBsends);
+ return(ERROR_DOS(ERRSRV,ERRmsgoff));
+ }
+
+ outsize = set_message(outbuf,0,0,True);
+
+ p = smb_buf(inbuf)+1;
+ p += srvstr_pull_buf(inbuf, msgfrom, p, sizeof(msgfrom), STR_TERMINATE) + 1;
+ p += srvstr_pull_buf(inbuf, msgto, p, sizeof(msgto), STR_TERMINATE) + 1;
+
+ msg = p;
+
+ len = SVAL(msg,0);
+ len = MIN(len,sizeof(msgbuf)-msgpos);
+
+ memset(msgbuf,'\0',sizeof(msgbuf));
+
+ memcpy(&msgbuf[msgpos],msg+2,len);
+ msgpos += len;
+
+ msg_deliver();
+
+ END_PROFILE(SMBsends);
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a sendstrt
+****************************************************************************/
+int reply_sendstrt(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int outsize = 0;
+ char *p;
+
+ START_PROFILE(SMBsendstrt);
+
+ if (! (*lp_msg_command())) {
+ END_PROFILE(SMBsendstrt);
+ return(ERROR_DOS(ERRSRV,ERRmsgoff));
+ }
+
+ outsize = set_message(outbuf,1,0,True);
+
+ memset(msgbuf,'\0',sizeof(msgbuf));
+ msgpos = 0;
+
+ p = smb_buf(inbuf)+1;
+ p += srvstr_pull_buf(inbuf, msgfrom, p, sizeof(msgfrom), STR_TERMINATE) + 1;
+ p += srvstr_pull_buf(inbuf, msgto, p, sizeof(msgto), STR_TERMINATE) + 1;
+
+ DEBUG( 3, ( "SMBsendstrt (from %s to %s)\n", msgfrom, msgto ) );
+
+ END_PROFILE(SMBsendstrt);
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a sendtxt
+****************************************************************************/
+int reply_sendtxt(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int len;
+ int outsize = 0;
+ char *msg;
+ START_PROFILE(SMBsendtxt);
+
+ if (! (*lp_msg_command())) {
+ END_PROFILE(SMBsendtxt);
+ return(ERROR_DOS(ERRSRV,ERRmsgoff));
+ }
+
+ outsize = set_message(outbuf,0,0,True);
+
+ msg = smb_buf(inbuf) + 1;
+
+ len = SVAL(msg,0);
+ len = MIN(len,sizeof(msgbuf)-msgpos);
+
+ memcpy(&msgbuf[msgpos],msg+2,len);
+ msgpos += len;
+
+ DEBUG( 3, ( "SMBsendtxt\n" ) );
+
+ END_PROFILE(SMBsendtxt);
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a sendend
+****************************************************************************/
+int reply_sendend(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int outsize = 0;
+ START_PROFILE(SMBsendend);
+
+ if (! (*lp_msg_command())) {
+ END_PROFILE(SMBsendend);
+ return(ERROR_DOS(ERRSRV,ERRmsgoff));
+ }
+
+ outsize = set_message(outbuf,0,0,True);
+
+ DEBUG(3,("SMBsendend\n"));
+
+ msg_deliver();
+
+ END_PROFILE(SMBsendend);
+ return(outsize);
+}
diff --git a/source/smbd/negprot.c b/source/smbd/negprot.c
new file mode 100644
index 00000000000..96961368fb1
--- /dev/null
+++ b/source/smbd/negprot.c
@@ -0,0 +1,550 @@
+/*
+ Unix SMB/CIFS implementation.
+ negprot reply code
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+extern int Protocol;
+extern int max_recv;
+BOOL global_encrypted_passwords_negotiated = False;
+BOOL global_spnego_negotiated = False;
+struct auth_context *negprot_global_auth_context = NULL;
+
+static void get_challenge(char buff[8])
+{
+ NTSTATUS nt_status;
+ const uint8 *cryptkey;
+
+ /* We might be called more than once, muliple negprots are premitted */
+ if (negprot_global_auth_context) {
+ DEBUG(3, ("get challenge: is this a secondary negprot? negprot_global_auth_context is non-NULL!\n"));
+ (negprot_global_auth_context->free)(&negprot_global_auth_context);
+ }
+
+ DEBUG(10, ("get challenge: creating negprot_global_auth_context\n"));
+ if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(&negprot_global_auth_context))) {
+ DEBUG(0, ("make_auth_context_subsystem returned %s", nt_errstr(nt_status)));
+ smb_panic("cannot make_negprot_global_auth_context!\n");
+ }
+ DEBUG(10, ("get challenge: getting challenge\n"));
+ cryptkey = negprot_global_auth_context->get_ntlm_challenge(negprot_global_auth_context);
+ memcpy(buff, cryptkey, 8);
+}
+
+/****************************************************************************
+ Reply for the core protocol.
+****************************************************************************/
+
+static int reply_corep(char *inbuf, char *outbuf)
+{
+ int outsize = set_message(outbuf,1,0,True);
+
+ Protocol = PROTOCOL_CORE;
+
+ return outsize;
+}
+
+/****************************************************************************
+ Reply for the coreplus protocol.
+****************************************************************************/
+
+static int reply_coreplus(char *inbuf, char *outbuf)
+{
+ int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
+ int outsize = set_message(outbuf,13,0,True);
+ SSVAL(outbuf,smb_vwv5,raw); /* tell redirector we support
+ readbraw and writebraw (possibly) */
+ /* Reply, SMBlockread, SMBwritelock supported. */
+ SCVAL(outbuf,smb_flg,FLAG_REPLY|FLAG_SUPPORT_LOCKREAD);
+ SSVAL(outbuf,smb_vwv1,0x1); /* user level security, don't encrypt */
+
+ Protocol = PROTOCOL_COREPLUS;
+
+ return outsize;
+}
+
+/****************************************************************************
+ Reply for the lanman 1.0 protocol.
+****************************************************************************/
+
+static int reply_lanman1(char *inbuf, char *outbuf)
+{
+ int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
+ int secword=0;
+ time_t t = time(NULL);
+
+ global_encrypted_passwords_negotiated = lp_encrypted_passwords();
+
+ if (lp_security()>=SEC_USER)
+ secword |= NEGOTIATE_SECURITY_USER_LEVEL;
+ if (global_encrypted_passwords_negotiated)
+ secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE;
+
+ set_message(outbuf,13,global_encrypted_passwords_negotiated?8:0,True);
+ SSVAL(outbuf,smb_vwv1,secword);
+ /* Create a token value and add it to the outgoing packet. */
+ if (global_encrypted_passwords_negotiated) {
+ get_challenge(smb_buf(outbuf));
+ SSVAL(outbuf,smb_vwv11, 8);
+ }
+
+ Protocol = PROTOCOL_LANMAN1;
+
+ /* Reply, SMBlockread, SMBwritelock supported. */
+ SCVAL(outbuf,smb_flg,FLAG_REPLY|FLAG_SUPPORT_LOCKREAD);
+ SSVAL(outbuf,smb_vwv2,max_recv);
+ SSVAL(outbuf,smb_vwv3,lp_maxmux()); /* maxmux */
+ SSVAL(outbuf,smb_vwv4,1);
+ SSVAL(outbuf,smb_vwv5,raw); /* tell redirector we support
+ readbraw writebraw (possibly) */
+ SIVAL(outbuf,smb_vwv6,sys_getpid());
+ SSVAL(outbuf,smb_vwv10, TimeDiff(t)/60);
+
+ put_dos_date(outbuf,smb_vwv8,t);
+
+ return (smb_len(outbuf)+4);
+}
+
+/****************************************************************************
+ Reply for the lanman 2.0 protocol.
+****************************************************************************/
+
+static int reply_lanman2(char *inbuf, char *outbuf)
+{
+ int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
+ int secword=0;
+ time_t t = time(NULL);
+
+ global_encrypted_passwords_negotiated = lp_encrypted_passwords();
+
+ if (lp_security()>=SEC_USER)
+ secword |= NEGOTIATE_SECURITY_USER_LEVEL;
+ if (global_encrypted_passwords_negotiated)
+ secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE;
+
+ set_message(outbuf,13,global_encrypted_passwords_negotiated?8:0,True);
+ SSVAL(outbuf,smb_vwv1,secword);
+ SIVAL(outbuf,smb_vwv6,sys_getpid());
+
+ /* Create a token value and add it to the outgoing packet. */
+ if (global_encrypted_passwords_negotiated) {
+ get_challenge(smb_buf(outbuf));
+ SSVAL(outbuf,smb_vwv11, 8);
+ }
+
+ Protocol = PROTOCOL_LANMAN2;
+
+ /* Reply, SMBlockread, SMBwritelock supported. */
+ SCVAL(outbuf,smb_flg,FLAG_REPLY|FLAG_SUPPORT_LOCKREAD);
+ SSVAL(outbuf,smb_vwv2,max_recv);
+ SSVAL(outbuf,smb_vwv3,lp_maxmux());
+ SSVAL(outbuf,smb_vwv4,1);
+ SSVAL(outbuf,smb_vwv5,raw); /* readbraw and/or writebraw */
+ SSVAL(outbuf,smb_vwv10, TimeDiff(t)/60);
+ put_dos_date(outbuf,smb_vwv8,t);
+
+ return (smb_len(outbuf)+4);
+}
+
+/****************************************************************************
+ Generate the spnego negprot reply blob. Return the number of bytes used.
+****************************************************************************/
+
+static int negprot_spnego(char *p)
+{
+ DATA_BLOB blob;
+ nstring dos_name;
+ fstring unix_name;
+ uint8 guid[17];
+ const char *OIDs_krb5[] = {OID_KERBEROS5,
+ OID_KERBEROS5_OLD,
+ OID_NTLMSSP,
+ NULL};
+ const char *OIDs_plain[] = {OID_NTLMSSP, NULL};
+ char *principal;
+ int len;
+
+ global_spnego_negotiated = True;
+
+ ZERO_STRUCT(guid);
+
+ safe_strcpy(unix_name, global_myname(), sizeof(unix_name)-1);
+ strlower_m(unix_name);
+ push_ascii_nstring(dos_name, unix_name);
+ safe_strcpy((char *)guid, dos_name, sizeof(guid)-1);
+
+#ifdef DEVELOPER
+ /* valgrind fixer... */
+ {
+ size_t sl = strlen(guid);
+ if (sizeof(guid)-sl)
+ memset(&guid[sl], '\0', sizeof(guid)-sl);
+ }
+#endif
+
+#if 0
+ /* strangely enough, NT does not sent the single OID NTLMSSP when
+ not a ADS member, it sends no OIDs at all
+
+ we can't do this until we teach our sesssion setup parser to know
+ about raw NTLMSSP (clients send no ASN.1 wrapping if we do this)
+ */
+ if (lp_security() != SEC_ADS) {
+ memcpy(p, guid, 16);
+ return 16;
+ }
+#endif
+ if (lp_security() != SEC_ADS) {
+ blob = spnego_gen_negTokenInit(guid, OIDs_plain, "NONE");
+ } else {
+ asprintf(&principal, "%s$@%s", guid, lp_realm());
+ blob = spnego_gen_negTokenInit(guid, OIDs_krb5, principal);
+ free(principal);
+ }
+ memcpy(p, blob.data, blob.length);
+ len = blob.length;
+ data_blob_free(&blob);
+ return len;
+}
+
+/****************************************************************************
+ Reply for the nt protocol.
+****************************************************************************/
+
+static int reply_nt1(char *inbuf, char *outbuf)
+{
+ /* dual names + lock_and_read + nt SMBs + remote API calls */
+ int capabilities = CAP_NT_FIND|CAP_LOCK_AND_READ|
+ CAP_LEVEL_II_OPLOCKS;
+
+ int secword=0;
+ time_t t = time(NULL);
+ char *p, *q;
+ BOOL negotiate_spnego = False;
+
+ global_encrypted_passwords_negotiated = lp_encrypted_passwords();
+
+ /* do spnego in user level security if the client
+ supports it and we can do encrypted passwords */
+
+ if (global_encrypted_passwords_negotiated &&
+ (lp_security() != SEC_SHARE) &&
+ lp_use_spnego() &&
+ (SVAL(inbuf, smb_flg2) & FLAGS2_EXTENDED_SECURITY)) {
+ negotiate_spnego = True;
+ capabilities |= CAP_EXTENDED_SECURITY;
+ }
+
+ capabilities |= CAP_NT_SMBS|CAP_RPC_REMOTE_APIS;
+
+ if (lp_unix_extensions()) {
+ capabilities |= CAP_UNIX;
+ }
+
+ if (lp_large_readwrite() && (SMB_OFF_T_BITS == 64))
+ capabilities |= CAP_LARGE_READX|CAP_LARGE_WRITEX|CAP_W2K_SMBS;
+
+ if (SMB_OFF_T_BITS == 64)
+ capabilities |= CAP_LARGE_FILES;
+
+ if (lp_readraw() && lp_writeraw())
+ capabilities |= CAP_RAW_MODE;
+
+ /* allow for disabling unicode */
+ if (lp_unicode())
+ capabilities |= CAP_UNICODE;
+
+ if (lp_nt_status_support())
+ capabilities |= CAP_STATUS32;
+
+ if (lp_host_msdfs())
+ capabilities |= CAP_DFS;
+
+ if (lp_security() >= SEC_USER)
+ secword |= NEGOTIATE_SECURITY_USER_LEVEL;
+ if (global_encrypted_passwords_negotiated)
+ secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE;
+
+ if (lp_server_signing()) {
+ if (lp_security() >= SEC_USER) {
+ secword |= NEGOTIATE_SECURITY_SIGNATURES_ENABLED;
+ /* No raw mode with smb signing. */
+ capabilities &= ~CAP_RAW_MODE;
+ if (lp_server_signing() == Required)
+ secword |=NEGOTIATE_SECURITY_SIGNATURES_REQUIRED;
+ srv_set_signing_negotiated();
+ } else {
+ DEBUG(0,("reply_nt1: smb signing is incompatible with share level security !\n"));
+ if (lp_server_signing() == Required) {
+ exit_server("reply_nt1: smb signing required and share level security selected.");
+ }
+ }
+ }
+
+ set_message(outbuf,17,0,True);
+
+ SCVAL(outbuf,smb_vwv1,secword);
+
+ Protocol = PROTOCOL_NT1;
+
+ SSVAL(outbuf,smb_vwv1+1,lp_maxmux()); /* maxmpx */
+ SSVAL(outbuf,smb_vwv2+1,1); /* num vcs */
+ SIVAL(outbuf,smb_vwv3+1,max_recv); /* max buffer. LOTS! */
+ SIVAL(outbuf,smb_vwv5+1,0x10000); /* raw size. full 64k */
+ SIVAL(outbuf,smb_vwv7+1,sys_getpid()); /* session key */
+ SIVAL(outbuf,smb_vwv9+1,capabilities); /* capabilities */
+ put_long_date(outbuf+smb_vwv11+1,t);
+ SSVALS(outbuf,smb_vwv15+1,TimeDiff(t)/60);
+
+ p = q = smb_buf(outbuf);
+ if (!negotiate_spnego) {
+ /* Create a token value and add it to the outgoing packet. */
+ if (global_encrypted_passwords_negotiated) {
+ /* note that we do not send a challenge at all if
+ we are using plaintext */
+ get_challenge(p);
+ SSVALS(outbuf,smb_vwv16+1,8);
+ p += 8;
+ }
+ p += srvstr_push(outbuf, p, lp_workgroup(), -1,
+ STR_UNICODE|STR_TERMINATE|STR_NOALIGN);
+ DEBUG(3,("not using SPNEGO\n"));
+ } else {
+ int len = negprot_spnego(p);
+
+ SSVALS(outbuf,smb_vwv16+1,len);
+ p += len;
+ DEBUG(3,("using SPNEGO\n"));
+ }
+
+ SSVAL(outbuf,smb_vwv17, p - q); /* length of challenge+domain strings */
+ set_message_end(outbuf, p);
+
+ return (smb_len(outbuf)+4);
+}
+
+/* these are the protocol lists used for auto architecture detection:
+
+WinNT 3.51:
+protocol [PC NETWORK PROGRAM 1.0]
+protocol [XENIX CORE]
+protocol [MICROSOFT NETWORKS 1.03]
+protocol [LANMAN1.0]
+protocol [Windows for Workgroups 3.1a]
+protocol [LM1.2X002]
+protocol [LANMAN2.1]
+protocol [NT LM 0.12]
+
+Win95:
+protocol [PC NETWORK PROGRAM 1.0]
+protocol [XENIX CORE]
+protocol [MICROSOFT NETWORKS 1.03]
+protocol [LANMAN1.0]
+protocol [Windows for Workgroups 3.1a]
+protocol [LM1.2X002]
+protocol [LANMAN2.1]
+protocol [NT LM 0.12]
+
+Win2K:
+protocol [PC NETWORK PROGRAM 1.0]
+protocol [LANMAN1.0]
+protocol [Windows for Workgroups 3.1a]
+protocol [LM1.2X002]
+protocol [LANMAN2.1]
+protocol [NT LM 0.12]
+
+OS/2:
+protocol [PC NETWORK PROGRAM 1.0]
+protocol [XENIX CORE]
+protocol [LANMAN1.0]
+protocol [LM1.2X002]
+protocol [LANMAN2.1]
+*/
+
+/*
+ * Modified to recognize the architecture of the remote machine better.
+ *
+ * This appears to be the matrix of which protocol is used by which
+ * MS product.
+ Protocol WfWg Win95 WinNT Win2K OS/2
+ PC NETWORK PROGRAM 1.0 1 1 1 1 1
+ XENIX CORE 2 2
+ MICROSOFT NETWORKS 3.0 2 2
+ DOS LM1.2X002 3 3
+ MICROSOFT NETWORKS 1.03 3
+ DOS LANMAN2.1 4 4
+ LANMAN1.0 4 2 3
+ Windows for Workgroups 3.1a 5 5 5 3
+ LM1.2X002 6 4 4
+ LANMAN2.1 7 5 5
+ NT LM 0.12 6 8 6
+ *
+ * tim@fsg.com 09/29/95
+ * Win2K added by matty 17/7/99
+ */
+
+#define ARCH_WFWG 0x3 /* This is a fudge because WfWg is like Win95 */
+#define ARCH_WIN95 0x2
+#define ARCH_WINNT 0x4
+#define ARCH_WIN2K 0xC /* Win2K is like NT */
+#define ARCH_OS2 0x14 /* Again OS/2 is like NT */
+#define ARCH_SAMBA 0x20
+
+#define ARCH_ALL 0x3F
+
+/* List of supported protocols, most desired first */
+static const struct {
+ const char *proto_name;
+ const char *short_name;
+ int (*proto_reply_fn)(char *, char *);
+ int protocol_level;
+} supported_protocols[] = {
+ {"NT LANMAN 1.0", "NT1", reply_nt1, PROTOCOL_NT1},
+ {"NT LM 0.12", "NT1", reply_nt1, PROTOCOL_NT1},
+ {"LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
+ {"Samba", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
+ {"DOS LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
+ {"LANMAN1.0", "LANMAN1", reply_lanman1, PROTOCOL_LANMAN1},
+ {"MICROSOFT NETWORKS 3.0", "LANMAN1", reply_lanman1, PROTOCOL_LANMAN1},
+ {"MICROSOFT NETWORKS 1.03", "COREPLUS", reply_coreplus, PROTOCOL_COREPLUS},
+ {"PC NETWORK PROGRAM 1.0", "CORE", reply_corep, PROTOCOL_CORE},
+ {NULL,NULL,NULL,0},
+};
+
+/****************************************************************************
+ Reply to a negprot.
+****************************************************************************/
+
+int reply_negprot(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size,
+ int dum_buffsize)
+{
+ int outsize = set_message(outbuf,1,0,True);
+ int Index=0;
+ int choice= -1;
+ int protocol;
+ char *p;
+ int bcc = SVAL(smb_buf(inbuf),-2);
+ int arch = ARCH_ALL;
+
+ static BOOL done_negprot = False;
+
+ START_PROFILE(SMBnegprot);
+
+ if (done_negprot) {
+ END_PROFILE(SMBnegprot);
+ exit_server("multiple negprot's are not permitted");
+ }
+ done_negprot = True;
+
+ p = smb_buf(inbuf)+1;
+ while (p < (smb_buf(inbuf) + bcc)) {
+ Index++;
+ DEBUG(3,("Requested protocol [%s]\n",p));
+ if (strcsequal(p,"Windows for Workgroups 3.1a"))
+ arch &= ( ARCH_WFWG | ARCH_WIN95 | ARCH_WINNT | ARCH_WIN2K );
+ else if (strcsequal(p,"DOS LM1.2X002"))
+ arch &= ( ARCH_WFWG | ARCH_WIN95 );
+ else if (strcsequal(p,"DOS LANMAN2.1"))
+ arch &= ( ARCH_WFWG | ARCH_WIN95 );
+ else if (strcsequal(p,"NT LM 0.12"))
+ arch &= ( ARCH_WIN95 | ARCH_WINNT | ARCH_WIN2K );
+ else if (strcsequal(p,"LANMAN2.1"))
+ arch &= ( ARCH_WINNT | ARCH_WIN2K | ARCH_OS2 );
+ else if (strcsequal(p,"LM1.2X002"))
+ arch &= ( ARCH_WINNT | ARCH_WIN2K | ARCH_OS2 );
+ else if (strcsequal(p,"MICROSOFT NETWORKS 1.03"))
+ arch &= ARCH_WINNT;
+ else if (strcsequal(p,"XENIX CORE"))
+ arch &= ( ARCH_WINNT | ARCH_OS2 );
+ else if (strcsequal(p,"Samba")) {
+ arch = ARCH_SAMBA;
+ break;
+ }
+
+ p += strlen(p) + 2;
+ }
+
+ switch ( arch ) {
+ case ARCH_SAMBA:
+ set_remote_arch(RA_SAMBA);
+ break;
+ case ARCH_WFWG:
+ set_remote_arch(RA_WFWG);
+ break;
+ case ARCH_WIN95:
+ set_remote_arch(RA_WIN95);
+ break;
+ case ARCH_WINNT:
+ if(SVAL(inbuf,smb_flg2)==FLAGS2_WIN2K_SIGNATURE)
+ set_remote_arch(RA_WIN2K);
+ else
+ set_remote_arch(RA_WINNT);
+ break;
+ case ARCH_WIN2K:
+ set_remote_arch(RA_WIN2K);
+ break;
+ case ARCH_OS2:
+ set_remote_arch(RA_OS2);
+ break;
+ default:
+ set_remote_arch(RA_UNKNOWN);
+ break;
+ }
+
+ /* possibly reload - change of architecture */
+ reload_services(True);
+
+ /* Check for protocols, most desirable first */
+ for (protocol = 0; supported_protocols[protocol].proto_name; protocol++) {
+ p = smb_buf(inbuf)+1;
+ Index = 0;
+ if ((supported_protocols[protocol].protocol_level <= lp_maxprotocol()) &&
+ (supported_protocols[protocol].protocol_level >= lp_minprotocol()))
+ while (p < (smb_buf(inbuf) + bcc)) {
+ if (strequal(p,supported_protocols[protocol].proto_name))
+ choice = Index;
+ Index++;
+ p += strlen(p) + 2;
+ }
+ if(choice != -1)
+ break;
+ }
+
+ SSVAL(outbuf,smb_vwv0,choice);
+ if(choice != -1) {
+ extern fstring remote_proto;
+ fstrcpy(remote_proto,supported_protocols[protocol].short_name);
+ reload_services(True);
+ outsize = supported_protocols[protocol].proto_reply_fn(inbuf, outbuf);
+ DEBUG(3,("Selected protocol %s\n",supported_protocols[protocol].proto_name));
+ } else {
+ DEBUG(0,("No protocol supported !\n"));
+ }
+ SSVAL(outbuf,smb_vwv0,choice);
+
+ DEBUG( 5, ( "negprot index=%d\n", choice ) );
+
+ if ((lp_server_signing() == Required) && (Protocol < PROTOCOL_NT1)) {
+ exit_server("SMB signing is required and client negotiated a downlevel protocol");
+ }
+
+ END_PROFILE(SMBnegprot);
+ return(outsize);
+}
diff --git a/source/smbd/noquotas.c b/source/smbd/noquotas.c
new file mode 100644
index 00000000000..85caef57e1a
--- /dev/null
+++ b/source/smbd/noquotas.c
@@ -0,0 +1,38 @@
+/*
+ Unix SMB/CIFS implementation.
+ No support for quotas :-).
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*
+ * Needed for auto generation of proto.h.
+ */
+
+BOOL disk_quotas(const char *path,SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize)
+{
+ (*bsize) = 512; /* This value should be ignored */
+
+ /* And just to be sure we set some values that hopefully */
+ /* will be larger that any possible real-world value */
+ (*dfree) = (SMB_BIG_UINT)-1;
+ (*dsize) = (SMB_BIG_UINT)-1;
+
+ /* As we have select not to use quotas, allways fail */
+ return False;
+}
diff --git a/source/smbd/notify.c b/source/smbd/notify.c
new file mode 100644
index 00000000000..9adf827c794
--- /dev/null
+++ b/source/smbd/notify.c
@@ -0,0 +1,225 @@
+/*
+ Unix SMB/CIFS implementation.
+ change notify handling
+ Copyright (C) Andrew Tridgell 2000
+ Copyright (C) Jeremy Allison 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static struct cnotify_fns *cnotify;
+
+/****************************************************************************
+ This is the structure to queue to implement NT change
+ notify. It consists of smb_size bytes stored from the
+ transact command (to keep the mid, tid etc around).
+ Plus the fid to examine and notify private data.
+*****************************************************************************/
+
+struct change_notify {
+ struct change_notify *next, *prev;
+ files_struct *fsp;
+ connection_struct *conn;
+ uint32 flags;
+ char request_buf[smb_size];
+ void *change_data;
+};
+
+static struct change_notify *change_notify_list;
+
+/****************************************************************************
+ Setup the common parts of the return packet and send it.
+*****************************************************************************/
+
+static void change_notify_reply_packet(char *inbuf, NTSTATUS error_code)
+{
+ char outbuf[smb_size+38];
+
+ memset(outbuf, '\0', sizeof(outbuf));
+ construct_reply_common(inbuf, outbuf);
+
+ ERROR_NT(error_code);
+
+ /*
+ * Seems NT needs a transact command with an error code
+ * in it. This is a longer packet than a simple error.
+ */
+ set_message(outbuf,18,0,False);
+
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("change_notify_reply_packet: send_smb failed.");
+}
+
+/****************************************************************************
+ Remove an entry from the list and free it, also closing any
+ directory handle if necessary.
+*****************************************************************************/
+
+static void change_notify_remove(struct change_notify *cnbp)
+{
+ cnotify->remove_notify(cnbp->change_data);
+ DLIST_REMOVE(change_notify_list, cnbp);
+ ZERO_STRUCTP(cnbp);
+ SAFE_FREE(cnbp);
+}
+
+/****************************************************************************
+ Delete entries by fnum from the change notify pending queue.
+*****************************************************************************/
+
+void remove_pending_change_notify_requests_by_fid(files_struct *fsp)
+{
+ struct change_notify *cnbp, *next;
+
+ for (cnbp=change_notify_list; cnbp; cnbp=next) {
+ next=cnbp->next;
+ if (cnbp->fsp->fnum == fsp->fnum) {
+ change_notify_remove(cnbp);
+ }
+ }
+}
+
+/****************************************************************************
+ Delete entries by mid from the change notify pending queue. Always send reply.
+*****************************************************************************/
+
+void remove_pending_change_notify_requests_by_mid(int mid)
+{
+ struct change_notify *cnbp, *next;
+
+ for (cnbp=change_notify_list; cnbp; cnbp=next) {
+ next=cnbp->next;
+ if(SVAL(cnbp->request_buf,smb_mid) == mid) {
+ change_notify_reply_packet(cnbp->request_buf,NT_STATUS_CANCELLED);
+ change_notify_remove(cnbp);
+ }
+ }
+}
+
+/****************************************************************************
+ Delete entries by filename and cnum from the change notify pending queue.
+ Always send reply.
+*****************************************************************************/
+
+void remove_pending_change_notify_requests_by_filename(files_struct *fsp)
+{
+ struct change_notify *cnbp, *next;
+
+ for (cnbp=change_notify_list; cnbp; cnbp=next) {
+ next=cnbp->next;
+ /*
+ * We know it refers to the same directory if the connection number and
+ * the filename are identical.
+ */
+ if((cnbp->fsp->conn == fsp->conn) && strequal(cnbp->fsp->fsp_name,fsp->fsp_name)) {
+ change_notify_reply_packet(cnbp->request_buf,NT_STATUS_CANCELLED);
+ change_notify_remove(cnbp);
+ }
+ }
+}
+
+/****************************************************************************
+ Return true if there are pending change notifies.
+****************************************************************************/
+
+int change_notify_timeout(void)
+{
+ return cnotify->select_time;
+}
+
+/****************************************************************************
+ Process the change notify queue. Note that this is only called as root.
+ Returns True if there are still outstanding change notify requests on the
+ queue.
+*****************************************************************************/
+
+BOOL process_pending_change_notify_queue(time_t t)
+{
+ struct change_notify *cnbp, *next;
+ uint16 vuid;
+
+ for (cnbp=change_notify_list; cnbp; cnbp=next) {
+ next=cnbp->next;
+
+ vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID : SVAL(cnbp->request_buf,smb_uid);
+
+ if (cnotify->check_notify(cnbp->conn, vuid, cnbp->fsp->fsp_name, cnbp->flags, cnbp->change_data, t)) {
+ DEBUG(10,("process_pending_change_notify_queue: dir %s changed !\n", cnbp->fsp->fsp_name ));
+ change_notify_reply_packet(cnbp->request_buf,STATUS_NOTIFY_ENUM_DIR);
+ change_notify_remove(cnbp);
+ }
+ }
+
+ return (change_notify_list != NULL);
+}
+
+/****************************************************************************
+ Now queue an entry on the notify change list.
+ We only need to save smb_size bytes from this incoming packet
+ as we will always by returning a 'read the directory yourself'
+ error.
+****************************************************************************/
+
+BOOL change_notify_set(char *inbuf, files_struct *fsp, connection_struct *conn, uint32 flags)
+{
+ struct change_notify *cnbp;
+
+ if((cnbp = (struct change_notify *)malloc(sizeof(*cnbp))) == NULL) {
+ DEBUG(0,("change_notify_set: malloc fail !\n" ));
+ return -1;
+ }
+
+ ZERO_STRUCTP(cnbp);
+
+ memcpy(cnbp->request_buf, inbuf, smb_size);
+ cnbp->fsp = fsp;
+ cnbp->conn = conn;
+ cnbp->flags = flags;
+ cnbp->change_data = cnotify->register_notify(conn, fsp->fsp_name, flags);
+
+ if (!cnbp->change_data) {
+ SAFE_FREE(cnbp);
+ return False;
+ }
+
+ DLIST_ADD(change_notify_list, cnbp);
+
+ /* Push the MID of this packet on the signing queue. */
+ srv_defer_sign_response(SVAL(inbuf,smb_mid));
+
+ return True;
+}
+
+/****************************************************************************
+ Initialise the change notify subsystem.
+****************************************************************************/
+
+BOOL init_change_notify(void)
+{
+#if HAVE_KERNEL_CHANGE_NOTIFY
+ if (lp_kernel_change_notify())
+ cnotify = kernel_notify_init();
+#endif
+ if (!cnotify) cnotify = hash_notify_init();
+
+ if (!cnotify) {
+ DEBUG(0,("Failed to init change notify system\n"));
+ return False;
+ }
+
+ return True;
+}
diff --git a/source/smbd/notify_hash.c b/source/smbd/notify_hash.c
new file mode 100644
index 00000000000..ec414454f9e
--- /dev/null
+++ b/source/smbd/notify_hash.c
@@ -0,0 +1,225 @@
+/*
+ Unix SMB/CIFS implementation.
+ change notify handling - hash based implementation
+ Copyright (C) Jeremy Allison 1994-1998
+ Copyright (C) Andrew Tridgell 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+struct change_data {
+ time_t last_check_time; /* time we last checked this entry */
+ time_t modify_time; /* Info from the directory we're monitoring. */
+ time_t status_time; /* Info from the directory we're monitoring. */
+ time_t total_time; /* Total time of all directory entries - don't care if it wraps. */
+ unsigned int num_entries; /* Zero or the number of files in the directory. */
+ unsigned int mode_sum;
+ unsigned char name_hash[16];
+};
+
+/****************************************************************************
+ Create the hash we will use to determine if the contents changed.
+*****************************************************************************/
+
+static BOOL notify_hash(connection_struct *conn, char *path, uint32 flags,
+ struct change_data *data, struct change_data *old_data)
+{
+ SMB_STRUCT_STAT st;
+ pstring full_name;
+ char *p;
+ const char *fname;
+ size_t remaining_len;
+ size_t fullname_len;
+ void *dp;
+
+ ZERO_STRUCTP(data);
+
+ if(SMB_VFS_STAT(conn,path, &st) == -1)
+ return False;
+
+ data->modify_time = st.st_mtime;
+ data->status_time = st.st_ctime;
+
+ if (old_data) {
+ /*
+ * Shortcut to avoid directory scan if the time
+ * has changed - we always must return true then.
+ */
+ if (old_data->modify_time != data->modify_time ||
+ old_data->status_time != data->status_time ) {
+ return True;
+ }
+ }
+
+ /*
+ * If we are to watch for changes that are only stored
+ * in inodes of files, not in the directory inode, we must
+ * scan the directory and produce a unique identifier with
+ * which we can determine if anything changed. We use the
+ * modify and change times from all the files in the
+ * directory, added together (ignoring wrapping if it's
+ * larger than the max time_t value).
+ */
+
+ dp = OpenDir(conn, path, True);
+ if (dp == NULL)
+ return False;
+
+ data->num_entries = 0;
+
+ pstrcpy(full_name, path);
+ pstrcat(full_name, "/");
+
+ fullname_len = strlen(full_name);
+ remaining_len = sizeof(full_name) - fullname_len - 1;
+ p = &full_name[fullname_len];
+
+ while ((fname = ReadDirName(dp))) {
+ if(strequal(fname, ".") || strequal(fname, ".."))
+ continue;
+
+ data->num_entries++;
+ safe_strcpy(p, fname, remaining_len);
+
+ ZERO_STRUCT(st);
+
+ /*
+ * Do the stat - but ignore errors.
+ */
+ SMB_VFS_STAT(conn,full_name, &st);
+
+ /*
+ * Always sum the times.
+ */
+
+ data->total_time += (st.st_mtime + st.st_ctime);
+
+ /*
+ * If requested hash the names.
+ */
+
+ if (flags & (FILE_NOTIFY_CHANGE_DIR_NAME|FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_FILE)) {
+ int i;
+ unsigned char tmp_hash[16];
+ mdfour(tmp_hash, (const unsigned char *)fname, strlen(fname));
+ for (i=0;i<16;i++)
+ data->name_hash[i] ^= tmp_hash[i];
+ }
+
+ /*
+ * If requested sum the mode_t's.
+ */
+
+ if (flags & (FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_SECURITY))
+ data->mode_sum += st.st_mode;
+ }
+
+ CloseDir(dp);
+
+ return True;
+}
+
+/****************************************************************************
+ Register a change notify request.
+*****************************************************************************/
+
+static void *hash_register_notify(connection_struct *conn, char *path, uint32 flags)
+{
+ struct change_data data;
+
+ if (!notify_hash(conn, path, flags, &data, NULL))
+ return NULL;
+
+ data.last_check_time = time(NULL);
+
+ return (void *)memdup(&data, sizeof(data));
+}
+
+/****************************************************************************
+ Check if a change notify should be issued.
+ A time of zero means instantaneous check - don't modify the last check time.
+*****************************************************************************/
+
+static BOOL hash_check_notify(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *datap, time_t t)
+{
+ struct change_data *data = (struct change_data *)datap;
+ struct change_data data2;
+
+ if (t && t < data->last_check_time + lp_change_notify_timeout())
+ return False;
+
+ if (!change_to_user(conn,vuid))
+ return True;
+ if (!set_current_service(conn,True)) {
+ change_to_root_user();
+ return True;
+ }
+
+ if (!notify_hash(conn, path, flags, &data2, data) ||
+ data2.modify_time != data->modify_time ||
+ data2.status_time != data->status_time ||
+ data2.total_time != data->total_time ||
+ data2.num_entries != data->num_entries ||
+ data2.mode_sum != data->mode_sum ||
+ memcmp(data2.name_hash, data->name_hash, sizeof(data2.name_hash))) {
+ change_to_root_user();
+ return True;
+ }
+
+ if (t)
+ data->last_check_time = t;
+
+ change_to_root_user();
+
+ return False;
+}
+
+/****************************************************************************
+ Remove a change notify data structure.
+*****************************************************************************/
+
+static void hash_remove_notify(void *datap)
+{
+ free(datap);
+}
+
+/****************************************************************************
+ Setup hash based change notify.
+****************************************************************************/
+
+struct cnotify_fns *hash_notify_init(void)
+{
+ static struct cnotify_fns cnotify;
+
+ cnotify.register_notify = hash_register_notify;
+ cnotify.check_notify = hash_check_notify;
+ cnotify.remove_notify = hash_remove_notify;
+ cnotify.select_time = lp_change_notify_timeout();
+
+ return &cnotify;
+}
+
+/*
+ change_notify_reply_packet(cnbp->request_buf,ERRSRV,ERRaccess);
+ change_notify_reply_packet(cnbp->request_buf,0,NT_STATUS_NOTIFY_ENUM_DIR);
+
+ chain_size = 0;
+ file_chain_reset();
+
+ uint16 vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID :
+ SVAL(cnbp->request_buf,smb_uid);
+*/
diff --git a/source/smbd/notify_kernel.c b/source/smbd/notify_kernel.c
new file mode 100644
index 00000000000..8fcc18a09f9
--- /dev/null
+++ b/source/smbd/notify_kernel.c
@@ -0,0 +1,245 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ change notify handling - linux kernel based implementation
+ Copyright (C) Andrew Tridgell 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#if HAVE_KERNEL_CHANGE_NOTIFY
+
+#define FD_PENDING_SIZE 20
+static SIG_ATOMIC_T fd_pending_array[FD_PENDING_SIZE];
+static SIG_ATOMIC_T signals_received;
+
+#ifndef DN_ACCESS
+#define DN_ACCESS 0x00000001 /* File accessed in directory */
+#define DN_MODIFY 0x00000002 /* File modified in directory */
+#define DN_CREATE 0x00000004 /* File created in directory */
+#define DN_DELETE 0x00000008 /* File removed from directory */
+#define DN_RENAME 0x00000010 /* File renamed in directory */
+#define DN_ATTRIB 0x00000020 /* File changed attribute */
+#define DN_MULTISHOT 0x80000000 /* Don't remove notifier */
+#endif
+
+
+#ifndef RT_SIGNAL_NOTIFY
+#define RT_SIGNAL_NOTIFY (SIGRTMIN+2)
+#endif
+
+#ifndef F_SETSIG
+#define F_SETSIG 10
+#endif
+
+#ifndef F_NOTIFY
+#define F_NOTIFY 1026
+#endif
+
+/****************************************************************************
+ This is the structure to keep the information needed to
+ determine if a directory has changed.
+*****************************************************************************/
+
+struct change_data {
+ int directory_handle;
+};
+
+/****************************************************************************
+ The signal handler for change notify.
+ The Linux kernel has a bug in that we should be able to block any
+ further delivery of RT signals until the kernel_check_notify() function
+ unblocks them, but it seems that any signal mask we're setting here is
+ being overwritten on exit from this handler. I should create a standalone
+ test case for the kernel hackers. JRA.
+*****************************************************************************/
+
+static void signal_handler(int sig, siginfo_t *info, void *unused)
+{
+ if (signals_received < FD_PENDING_SIZE - 1) {
+ fd_pending_array[signals_received] = (SIG_ATOMIC_T)info->si_fd;
+ signals_received++;
+ } /* Else signal is lost. */
+ sys_select_signal();
+}
+
+/****************************************************************************
+ Check if a change notify should be issued.
+ time non-zero means timeout check (used for hash). Ignore this (async method
+ where time is zero will be used instead).
+*****************************************************************************/
+
+static BOOL kernel_check_notify(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *datap, time_t t)
+{
+ struct change_data *data = (struct change_data *)datap;
+ int i;
+ BOOL ret = False;
+
+ if (t)
+ return False;
+
+ BlockSignals(True, RT_SIGNAL_NOTIFY);
+ for (i = 0; i < signals_received; i++) {
+ if (data->directory_handle == (int)fd_pending_array[i]) {
+ DEBUG(3,("kernel_check_notify: kernel change notify on %s fd[%d]=%d (signals_received=%d)\n",
+ path, i, (int)fd_pending_array[i], (int)signals_received ));
+
+ close((int)fd_pending_array[i]);
+ fd_pending_array[i] = (SIG_ATOMIC_T)-1;
+ if (signals_received - i - 1) {
+ memmove((void *)&fd_pending_array[i], (void *)&fd_pending_array[i+1],
+ sizeof(SIG_ATOMIC_T)*(signals_received-i-1));
+ }
+ data->directory_handle = -1;
+ signals_received--;
+ ret = True;
+ break;
+ }
+ }
+ BlockSignals(False, RT_SIGNAL_NOTIFY);
+ return ret;
+}
+
+/****************************************************************************
+ Remove a change notify data structure.
+*****************************************************************************/
+
+static void kernel_remove_notify(void *datap)
+{
+ struct change_data *data = (struct change_data *)datap;
+ int fd = data->directory_handle;
+ if (fd != -1) {
+ int i;
+ BlockSignals(True, RT_SIGNAL_NOTIFY);
+ for (i = 0; i < signals_received; i++) {
+ if (fd == (int)fd_pending_array[i]) {
+ fd_pending_array[i] = (SIG_ATOMIC_T)-1;
+ if (signals_received - i - 1) {
+ memmove((void *)&fd_pending_array[i], (void *)&fd_pending_array[i+1],
+ sizeof(SIG_ATOMIC_T)*(signals_received-i-1));
+ }
+ data->directory_handle = -1;
+ signals_received--;
+ break;
+ }
+ }
+ close(fd);
+ BlockSignals(False, RT_SIGNAL_NOTIFY);
+ }
+ SAFE_FREE(data);
+ DEBUG(3,("kernel_remove_notify: fd=%d\n", fd));
+}
+
+/****************************************************************************
+ Register a change notify request.
+*****************************************************************************/
+
+static void *kernel_register_notify(connection_struct *conn, char *path, uint32 flags)
+{
+ struct change_data data;
+ int fd;
+ unsigned long kernel_flags;
+
+ fd = sys_open(path,O_RDONLY, 0);
+
+ if (fd == -1) {
+ DEBUG(3,("Failed to open directory %s for change notify\n", path));
+ return NULL;
+ }
+
+ if (sys_fcntl_long(fd, F_SETSIG, RT_SIGNAL_NOTIFY) == -1) {
+ DEBUG(3,("Failed to set signal handler for change notify\n"));
+ return NULL;
+ }
+
+ kernel_flags = DN_CREATE|DN_DELETE|DN_RENAME; /* creation/deletion changes everything! */
+ if (flags & FILE_NOTIFY_CHANGE_FILE) kernel_flags |= DN_MODIFY;
+ if (flags & FILE_NOTIFY_CHANGE_DIR_NAME) kernel_flags |= DN_RENAME|DN_DELETE;
+ if (flags & FILE_NOTIFY_CHANGE_ATTRIBUTES) kernel_flags |= DN_ATTRIB;
+ if (flags & FILE_NOTIFY_CHANGE_SIZE) kernel_flags |= DN_MODIFY;
+ if (flags & FILE_NOTIFY_CHANGE_LAST_WRITE) kernel_flags |= DN_MODIFY;
+ if (flags & FILE_NOTIFY_CHANGE_LAST_ACCESS) kernel_flags |= DN_ACCESS;
+ if (flags & FILE_NOTIFY_CHANGE_CREATION) kernel_flags |= DN_CREATE;
+ if (flags & FILE_NOTIFY_CHANGE_SECURITY) kernel_flags |= DN_ATTRIB;
+ if (flags & FILE_NOTIFY_CHANGE_EA) kernel_flags |= DN_ATTRIB;
+ if (flags & FILE_NOTIFY_CHANGE_FILE_NAME) kernel_flags |= DN_RENAME|DN_DELETE;
+
+ if (sys_fcntl_long(fd, F_NOTIFY, kernel_flags) == -1) {
+ DEBUG(3,("Failed to set async flag for change notify\n"));
+ return NULL;
+ }
+
+ data.directory_handle = fd;
+
+ DEBUG(3,("kernel change notify on %s (ntflags=0x%x flags=0x%x) fd=%d\n",
+ path, (int)flags, (int)kernel_flags, fd));
+
+ return (void *)memdup(&data, sizeof(data));
+}
+
+/****************************************************************************
+ See if the kernel supports change notify.
+****************************************************************************/
+
+static BOOL kernel_notify_available(void)
+{
+ int fd, ret;
+ fd = open("/tmp", O_RDONLY);
+ if (fd == -1)
+ return False; /* uggh! */
+ ret = sys_fcntl_long(fd, F_NOTIFY, 0);
+ close(fd);
+ return ret == 0;
+}
+
+/****************************************************************************
+ Setup kernel based change notify.
+****************************************************************************/
+
+struct cnotify_fns *kernel_notify_init(void)
+{
+ static struct cnotify_fns cnotify;
+ struct sigaction act;
+
+ ZERO_STRUCT(act);
+
+ act.sa_handler = NULL;
+ act.sa_sigaction = signal_handler;
+ act.sa_flags = SA_SIGINFO;
+ sigemptyset( &act.sa_mask );
+ if (sigaction(RT_SIGNAL_NOTIFY, &act, NULL) != 0) {
+ DEBUG(0,("Failed to setup RT_SIGNAL_NOTIFY handler\n"));
+ return NULL;
+ }
+
+ if (!kernel_notify_available())
+ return NULL;
+
+ cnotify.register_notify = kernel_register_notify;
+ cnotify.check_notify = kernel_check_notify;
+ cnotify.remove_notify = kernel_remove_notify;
+ cnotify.select_time = -1;
+
+ /* the signal can start off blocked due to a bug in bash */
+ BlockSignals(False, RT_SIGNAL_NOTIFY);
+
+ return &cnotify;
+}
+
+#else
+ void notify_kernel_dummy(void) {}
+#endif /* HAVE_KERNEL_CHANGE_NOTIFY */
diff --git a/source/smbd/ntquotas.c b/source/smbd/ntquotas.c
new file mode 100644
index 00000000000..555f32d773f
--- /dev/null
+++ b/source/smbd/ntquotas.c
@@ -0,0 +1,262 @@
+/*
+ Unix SMB/CIFS implementation.
+ NT QUOTA suppport
+ Copyright (C) Stefan (metze) Metzmacher 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_QUOTA
+
+static SMB_BIG_UINT limit_nt2unix(SMB_BIG_UINT in, SMB_BIG_UINT bsize)
+{
+ SMB_BIG_UINT ret = (SMB_BIG_UINT)0;
+
+ ret = (SMB_BIG_UINT)(in/bsize);
+ if (in>0 && ret==0) {
+ /* we have to make sure that a overflow didn't set NO_LIMIT */
+ ret = (SMB_BIG_UINT)1;
+ }
+
+ if (in == SMB_NTQUOTAS_NO_LIMIT)
+ ret = SMB_QUOTAS_NO_LIMIT;
+ else if (in == SMB_NTQUOTAS_NO_SPACE)
+ ret = SMB_QUOTAS_NO_SPACE;
+ else if (in == SMB_NTQUOTAS_NO_ENTRY)
+ ret = SMB_QUOTAS_NO_LIMIT;
+
+ return ret;
+}
+
+static SMB_BIG_UINT limit_unix2nt(SMB_BIG_UINT in, SMB_BIG_UINT bsize)
+{
+ SMB_BIG_UINT ret = (SMB_BIG_UINT)0;
+
+ ret = (SMB_BIG_UINT)(in*bsize);
+
+ if (ret < in) {
+ /* we overflow */
+ ret = SMB_NTQUOTAS_NO_LIMIT;
+ }
+
+ if (in == SMB_QUOTAS_NO_LIMIT)
+ ret = SMB_NTQUOTAS_NO_LIMIT;
+
+ return ret;
+}
+
+static SMB_BIG_UINT limit_blk2inodes(SMB_BIG_UINT in)
+{
+ SMB_BIG_UINT ret = (SMB_BIG_UINT)0;
+
+ ret = (SMB_BIG_UINT)(in/2);
+
+ if (ret == 0 && in != 0)
+ ret = (SMB_BIG_UINT)1;
+
+ return ret;
+}
+
+int vfs_get_ntquota(files_struct *fsp, enum SMB_QUOTA_TYPE qtype, DOM_SID *psid, SMB_NTQUOTA_STRUCT *qt)
+{
+ int ret;
+ SMB_DISK_QUOTA D;
+ unid_t id;
+
+ ZERO_STRUCT(D);
+
+ if (!fsp||!fsp->conn||!qt)
+ return (-1);
+
+ ZERO_STRUCT(*qt);
+
+ id.uid = -1;
+
+ if (psid && !NT_STATUS_IS_OK(sid_to_uid(psid, &id.uid))) {
+ DEBUG(0,("sid_to_uid: failed, SID[%s]\n",
+ sid_string_static(psid)));
+ }
+
+ ret = SMB_VFS_GET_QUOTA(fsp->conn, qtype, id, &D);
+
+ if (psid)
+ qt->sid = *psid;
+
+ if (ret!=0) {
+ return ret;
+ }
+
+ qt->usedspace = (SMB_BIG_UINT)D.curblocks*D.bsize;
+ qt->softlim = limit_unix2nt(D.softlimit, D.bsize);
+ qt->hardlim = limit_unix2nt(D.hardlimit, D.bsize);
+ qt->qflags = D.qflags;
+
+
+ return 0;
+}
+
+int vfs_set_ntquota(files_struct *fsp, enum SMB_QUOTA_TYPE qtype, DOM_SID *psid, SMB_NTQUOTA_STRUCT *qt)
+{
+ int ret;
+ SMB_DISK_QUOTA D;
+ unid_t id;
+ ZERO_STRUCT(D);
+
+ if (!fsp||!fsp->conn||!qt)
+ return (-1);
+
+ id.uid = -1;
+
+ D.bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
+
+ D.softlimit = limit_nt2unix(qt->softlim,D.bsize);
+ D.hardlimit = limit_nt2unix(qt->hardlim,D.bsize);
+ D.qflags = qt->qflags;
+
+ D.isoftlimit = limit_blk2inodes(D.softlimit);
+ D.ihardlimit = limit_blk2inodes(D.hardlimit);
+
+ if (psid && !NT_STATUS_IS_OK(sid_to_uid(psid, &id.uid))) {
+ DEBUG(0,("sid_to_uid: failed, SID[%s]\n",
+ sid_string_static(psid)));
+ }
+
+ ret = SMB_VFS_SET_QUOTA(fsp->conn, qtype, id, &D);
+
+ return ret;
+}
+
+static BOOL allready_in_quota_list(SMB_NTQUOTA_LIST *qt_list, uid_t uid)
+{
+ SMB_NTQUOTA_LIST *tmp_list = NULL;
+
+ if (!qt_list)
+ return False;
+
+ for (tmp_list=qt_list;tmp_list!=NULL;tmp_list=tmp_list->next) {
+ if (tmp_list->uid == uid) {
+ return True;
+ }
+ }
+
+ return False;
+}
+
+int vfs_get_user_ntquota_list(files_struct *fsp, SMB_NTQUOTA_LIST **qt_list)
+{
+ struct passwd *usr;
+ TALLOC_CTX *mem_ctx = NULL;
+
+ if (!fsp||!fsp->conn||!qt_list)
+ return (-1);
+
+ *qt_list = NULL;
+
+ if ((mem_ctx=talloc_init("SMB_USER_QUOTA_LIST"))==NULL) {
+ DEBUG(0,("talloc_init() failed\n"));
+ return (-1);
+ }
+
+ sys_setpwent();
+ while ((usr = sys_getpwent()) != NULL) {
+ SMB_NTQUOTA_STRUCT tmp_qt;
+ SMB_NTQUOTA_LIST *tmp_list_ent;
+ DOM_SID sid;
+
+ ZERO_STRUCT(tmp_qt);
+
+ if (allready_in_quota_list((*qt_list),usr->pw_uid)) {
+ DEBUG(5,("record for uid[%ld] allready in the list\n",(long)usr->pw_uid));
+ continue;
+ }
+
+ if (!NT_STATUS_IS_OK(uid_to_sid(&sid, usr->pw_uid))) {
+ DEBUG(0,("uid_to_sid failed for %ld\n",(long)usr->pw_uid));
+ continue;
+ }
+
+ if (vfs_get_ntquota(fsp, SMB_USER_QUOTA_TYPE, &sid, &tmp_qt)!=0) {
+ DEBUG(5,("no quota entry for sid[%s] path[%s]\n",
+ sid_string_static(&sid),fsp->conn->connectpath));
+ continue;
+ }
+
+ DEBUG(15,("quota entry for id[%s] path[%s]\n",
+ sid_string_static(&sid),fsp->conn->connectpath));
+
+ if ((tmp_list_ent=(SMB_NTQUOTA_LIST *)talloc_zero(mem_ctx,sizeof(SMB_NTQUOTA_LIST)))==NULL) {
+ DEBUG(0,("talloc_zero() failed\n"));
+ *qt_list = NULL;
+ talloc_destroy(mem_ctx);
+ return (-1);
+ }
+
+ if ((tmp_list_ent->quotas=(SMB_NTQUOTA_STRUCT *)talloc_zero(mem_ctx,sizeof(SMB_NTQUOTA_STRUCT)))==NULL) {
+ DEBUG(0,("talloc_zero() failed\n"));
+ *qt_list = NULL;
+ talloc_destroy(mem_ctx);
+ return (-1);
+ }
+
+ tmp_list_ent->uid = usr->pw_uid;
+ memcpy(tmp_list_ent->quotas,&tmp_qt,sizeof(tmp_qt));
+ tmp_list_ent->mem_ctx = mem_ctx;
+
+ DLIST_ADD((*qt_list),tmp_list_ent);
+
+ }
+ sys_endpwent();
+
+ return 0;
+}
+
+void *init_quota_handle(TALLOC_CTX *mem_ctx)
+{
+ SMB_NTQUOTA_HANDLE *qt_handle;
+
+ if (!mem_ctx)
+ return False;
+
+ qt_handle = (SMB_NTQUOTA_HANDLE *)talloc_zero(mem_ctx,sizeof(SMB_NTQUOTA_HANDLE));
+ if (qt_handle==NULL) {
+ DEBUG(0,("talloc_zero() failed\n"));
+ return NULL;
+ }
+
+ return (void *)qt_handle;
+}
+
+void destroy_quota_handle(void **pqt_handle)
+{
+ SMB_NTQUOTA_HANDLE *qt_handle = NULL;
+ if (!pqt_handle||!(*pqt_handle))
+ return;
+
+ qt_handle = (*pqt_handle);
+
+
+ if (qt_handle->quota_list)
+ free_ntquota_list(&qt_handle->quota_list);
+
+ qt_handle->quota_list = NULL;
+ qt_handle->tmp_list = NULL;
+ qt_handle = NULL;
+
+ return;
+}
+
diff --git a/source/smbd/nttrans.c b/source/smbd/nttrans.c
new file mode 100644
index 00000000000..018f6bbbece
--- /dev/null
+++ b/source/smbd/nttrans.c
@@ -0,0 +1,2780 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB NT transaction handling
+ Copyright (C) Jeremy Allison 1994-1998
+ Copyright (C) Stefan (metze) Metzmacher 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+extern int Protocol;
+extern int smb_read_error;
+extern int global_oplock_break;
+extern BOOL case_sensitive;
+extern BOOL case_preserve;
+extern BOOL short_case_preserve;
+extern struct current_user current_user;
+
+static const char *known_nt_pipes[] = {
+ "\\LANMAN",
+ "\\srvsvc",
+ "\\samr",
+ "\\wkssvc",
+ "\\NETLOGON",
+ "\\ntlsa",
+ "\\ntsvcs",
+ "\\lsass",
+ "\\lsarpc",
+ "\\winreg",
+ "\\spoolss",
+ "\\netdfs",
+ "\\rpcecho",
+ "\\epmapper",
+ NULL
+};
+
+/* Map generic permissions to file object specific permissions */
+
+struct generic_mapping file_generic_mapping = {
+ FILE_GENERIC_READ,
+ FILE_GENERIC_WRITE,
+ FILE_GENERIC_EXECUTE,
+ FILE_GENERIC_ALL
+};
+
+static char *nttrans_realloc(char **ptr, size_t size)
+{
+ char *tptr = NULL;
+ if (ptr==NULL)
+ smb_panic("nttrans_realloc() called with NULL ptr\n");
+
+ tptr = Realloc_zero(*ptr, size);
+ if(tptr == NULL) {
+ *ptr = NULL;
+ return NULL;
+ }
+
+ *ptr = tptr;
+
+ return tptr;
+}
+
+
+/****************************************************************************
+ Send the required number of replies back.
+ We assume all fields other than the data fields are
+ set correctly for the type of call.
+ HACK ! Always assumes smb_setup field is zero.
+****************************************************************************/
+
+static int send_nt_replies(char *inbuf, char *outbuf, int bufsize, NTSTATUS nt_error, char *params,
+ int paramsize, char *pdata, int datasize)
+{
+ extern int max_send;
+ int data_to_send = datasize;
+ int params_to_send = paramsize;
+ int useable_space;
+ char *pp = params;
+ char *pd = pdata;
+ int params_sent_thistime, data_sent_thistime, total_sent_thistime;
+ int alignment_offset = 3;
+ int data_alignment_offset = 0;
+
+ /*
+ * Initially set the wcnt area to be 18 - this is true for all
+ * transNT replies.
+ */
+
+ set_message(outbuf,18,0,True);
+
+ if (NT_STATUS_V(nt_error))
+ ERROR_NT(nt_error);
+
+ /*
+ * If there genuinely are no parameters or data to send just send
+ * the empty packet.
+ */
+
+ if(params_to_send == 0 && data_to_send == 0) {
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("send_nt_replies: send_smb failed.");
+ return 0;
+ }
+
+ /*
+ * When sending params and data ensure that both are nicely aligned.
+ * Only do this alignment when there is also data to send - else
+ * can cause NT redirector problems.
+ */
+
+ if (((params_to_send % 4) != 0) && (data_to_send != 0))
+ data_alignment_offset = 4 - (params_to_send % 4);
+
+ /*
+ * Space is bufsize minus Netbios over TCP header minus SMB header.
+ * The alignment_offset is to align the param bytes on a four byte
+ * boundary (2 bytes for data len, one byte pad).
+ * NT needs this to work correctly.
+ */
+
+ useable_space = bufsize - ((smb_buf(outbuf)+
+ alignment_offset+data_alignment_offset) -
+ outbuf);
+
+ /*
+ * useable_space can never be more than max_send minus the
+ * alignment offset.
+ */
+
+ useable_space = MIN(useable_space,
+ max_send - (alignment_offset+data_alignment_offset));
+
+
+ while (params_to_send || data_to_send) {
+
+ /*
+ * Calculate whether we will totally or partially fill this packet.
+ */
+
+ total_sent_thistime = params_to_send + data_to_send +
+ alignment_offset + data_alignment_offset;
+
+ /*
+ * We can never send more than useable_space.
+ */
+
+ total_sent_thistime = MIN(total_sent_thistime, useable_space);
+
+ set_message(outbuf, 18, total_sent_thistime, True);
+
+ /*
+ * Set total params and data to be sent.
+ */
+
+ SIVAL(outbuf,smb_ntr_TotalParameterCount,paramsize);
+ SIVAL(outbuf,smb_ntr_TotalDataCount,datasize);
+
+ /*
+ * Calculate how many parameters and data we can fit into
+ * this packet. Parameters get precedence.
+ */
+
+ params_sent_thistime = MIN(params_to_send,useable_space);
+ data_sent_thistime = useable_space - params_sent_thistime;
+ data_sent_thistime = MIN(data_sent_thistime,data_to_send);
+
+ SIVAL(outbuf,smb_ntr_ParameterCount,params_sent_thistime);
+
+ if(params_sent_thistime == 0) {
+ SIVAL(outbuf,smb_ntr_ParameterOffset,0);
+ SIVAL(outbuf,smb_ntr_ParameterDisplacement,0);
+ } else {
+ /*
+ * smb_ntr_ParameterOffset is the offset from the start of the SMB header to the
+ * parameter bytes, however the first 4 bytes of outbuf are
+ * the Netbios over TCP header. Thus use smb_base() to subtract
+ * them from the calculation.
+ */
+
+ SIVAL(outbuf,smb_ntr_ParameterOffset,
+ ((smb_buf(outbuf)+alignment_offset) - smb_base(outbuf)));
+ /*
+ * Absolute displacement of param bytes sent in this packet.
+ */
+
+ SIVAL(outbuf,smb_ntr_ParameterDisplacement,pp - params);
+ }
+
+ /*
+ * Deal with the data portion.
+ */
+
+ SIVAL(outbuf,smb_ntr_DataCount, data_sent_thistime);
+
+ if(data_sent_thistime == 0) {
+ SIVAL(outbuf,smb_ntr_DataOffset,0);
+ SIVAL(outbuf,smb_ntr_DataDisplacement, 0);
+ } else {
+ /*
+ * The offset of the data bytes is the offset of the
+ * parameter bytes plus the number of parameters being sent this time.
+ */
+
+ SIVAL(outbuf,smb_ntr_DataOffset,((smb_buf(outbuf)+alignment_offset) -
+ smb_base(outbuf)) + params_sent_thistime + data_alignment_offset);
+ SIVAL(outbuf,smb_ntr_DataDisplacement, pd - pdata);
+ }
+
+ /*
+ * Copy the param bytes into the packet.
+ */
+
+ if(params_sent_thistime)
+ memcpy((smb_buf(outbuf)+alignment_offset),pp,params_sent_thistime);
+
+ /*
+ * Copy in the data bytes
+ */
+
+ if(data_sent_thistime)
+ memcpy(smb_buf(outbuf)+alignment_offset+params_sent_thistime+
+ data_alignment_offset,pd,data_sent_thistime);
+
+ DEBUG(9,("nt_rep: params_sent_thistime = %d, data_sent_thistime = %d, useable_space = %d\n",
+ params_sent_thistime, data_sent_thistime, useable_space));
+ DEBUG(9,("nt_rep: params_to_send = %d, data_to_send = %d, paramsize = %d, datasize = %d\n",
+ params_to_send, data_to_send, paramsize, datasize));
+
+ /* Send the packet */
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("send_nt_replies: send_smb failed.");
+
+ pp += params_sent_thistime;
+ pd += data_sent_thistime;
+
+ params_to_send -= params_sent_thistime;
+ data_to_send -= data_sent_thistime;
+
+ /*
+ * Sanity check
+ */
+
+ if(params_to_send < 0 || data_to_send < 0) {
+ DEBUG(0,("send_nt_replies failed sanity check pts = %d, dts = %d\n!!!",
+ params_to_send, data_to_send));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ Save case statics.
+****************************************************************************/
+
+static BOOL saved_case_sensitive;
+static BOOL saved_case_preserve;
+static BOOL saved_short_case_preserve;
+
+/****************************************************************************
+ Save case semantics.
+****************************************************************************/
+
+static void set_posix_case_semantics(uint32 file_attributes)
+{
+ if(!(file_attributes & FILE_FLAG_POSIX_SEMANTICS))
+ return;
+
+ saved_case_sensitive = case_sensitive;
+ saved_case_preserve = case_preserve;
+ saved_short_case_preserve = short_case_preserve;
+
+ /* Set to POSIX. */
+ case_sensitive = True;
+ case_preserve = True;
+ short_case_preserve = True;
+}
+
+/****************************************************************************
+ Restore case semantics.
+****************************************************************************/
+
+static void restore_case_semantics(uint32 file_attributes)
+{
+ if(!(file_attributes & FILE_FLAG_POSIX_SEMANTICS))
+ return;
+
+ case_sensitive = saved_case_sensitive;
+ case_preserve = saved_case_preserve;
+ short_case_preserve = saved_short_case_preserve;
+}
+
+/****************************************************************************
+ Utility function to map create disposition.
+****************************************************************************/
+
+static int map_create_disposition( uint32 create_disposition)
+{
+ int ret;
+
+ switch( create_disposition ) {
+ case FILE_CREATE:
+ /* create if not exist, fail if exist */
+ ret = (FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_FAIL);
+ break;
+ case FILE_SUPERSEDE:
+ case FILE_OVERWRITE_IF:
+ /* create if not exist, trunc if exist */
+ ret = (FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_TRUNCATE);
+ break;
+ case FILE_OPEN:
+ /* fail if not exist, open if exists */
+ ret = (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN);
+ break;
+ case FILE_OPEN_IF:
+ /* create if not exist, open if exists */
+ ret = (FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_OPEN);
+ break;
+ case FILE_OVERWRITE:
+ /* fail if not exist, truncate if exists */
+ ret = (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_TRUNCATE);
+ break;
+ default:
+ DEBUG(0,("map_create_disposition: Incorrect value for create_disposition = %d\n",
+ create_disposition ));
+ return -1;
+ }
+
+ DEBUG(10,("map_create_disposition: Mapped create_disposition 0x%lx to 0x%x\n",
+ (unsigned long)create_disposition, ret ));
+
+ return ret;
+}
+
+/****************************************************************************
+ Utility function to map share modes.
+****************************************************************************/
+
+static int map_share_mode( char *fname, uint32 create_options,
+ uint32 *desired_access, uint32 share_access, uint32 file_attributes)
+{
+ int smb_open_mode = -1;
+ uint32 original_desired_access = *desired_access;
+
+ /*
+ * Convert GENERIC bits to specific bits.
+ */
+
+ se_map_generic(desired_access, &file_generic_mapping);
+
+ switch( *desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA) ) {
+ case FILE_READ_DATA:
+ smb_open_mode = DOS_OPEN_RDONLY;
+ break;
+ case FILE_WRITE_DATA:
+ case FILE_APPEND_DATA:
+ case FILE_WRITE_DATA|FILE_APPEND_DATA:
+ smb_open_mode = DOS_OPEN_WRONLY;
+ break;
+ case FILE_READ_DATA|FILE_WRITE_DATA:
+ case FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA:
+ case FILE_READ_DATA|FILE_APPEND_DATA:
+ smb_open_mode = DOS_OPEN_RDWR;
+ break;
+ }
+
+ /*
+ * NB. For DELETE_ACCESS we should really check the
+ * directory permissions, as that is what controls
+ * delete, and for WRITE_DAC_ACCESS we should really
+ * check the ownership, as that is what controls the
+ * chmod. Note that this is *NOT* a security hole (this
+ * note is for you, Andrew) as we are not *allowing*
+ * the access at this point, the actual unlink or
+ * chown or chmod call would do this. We are just helping
+ * clients out by telling them if they have a hope
+ * of any of this succeeding. POSIX acls may still
+ * deny the real call. JRA.
+ */
+
+ if (smb_open_mode == -1) {
+
+ if(*desired_access & (DELETE_ACCESS|WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS|SYNCHRONIZE_ACCESS|
+ FILE_EXECUTE|FILE_READ_ATTRIBUTES|
+ FILE_READ_EA|FILE_WRITE_EA|SYSTEM_SECURITY_ACCESS|
+ FILE_WRITE_ATTRIBUTES|READ_CONTROL_ACCESS)) {
+ smb_open_mode = DOS_OPEN_RDONLY;
+ } else if(*desired_access == 0) {
+
+ /*
+ * JRA - NT seems to sometimes send desired_access as zero. play it safe
+ * and map to a stat open.
+ */
+
+ smb_open_mode = DOS_OPEN_RDONLY;
+
+ } else {
+ DEBUG(0,("map_share_mode: Incorrect value 0x%lx for desired_access to file %s\n",
+ (unsigned long)*desired_access, fname));
+ return -1;
+ }
+ }
+
+ /*
+ * Set the special bit that means allow share delete.
+ * This is held outside the normal share mode bits at 1<<15.
+ * JRA.
+ */
+
+ if(share_access & FILE_SHARE_DELETE) {
+ smb_open_mode |= ALLOW_SHARE_DELETE;
+ DEBUG(10,("map_share_mode: FILE_SHARE_DELETE requested. open_mode = 0x%x\n", smb_open_mode));
+ }
+
+ if(*desired_access & DELETE_ACCESS) {
+ DEBUG(10,("map_share_mode: DELETE_ACCESS requested. open_mode = 0x%x\n", smb_open_mode));
+ }
+
+ /*
+ * We need to store the intent to open for Delete. This
+ * is what determines if a delete on close flag can be set.
+ * This is the wrong way (and place) to store this, but for 2.2 this
+ * is the only practical way. JRA.
+ */
+
+ if (create_options & FILE_DELETE_ON_CLOSE) {
+ /*
+ * W2K3 bug compatibility mode... To set delete on close
+ * the redirector must have *specifically* set DELETE_ACCESS
+ * in the desired_access field. Just asking for GENERIC_ALL won't do. JRA.
+ */
+
+ if (!(original_desired_access & DELETE_ACCESS)) {
+ DEBUG(5,("map_share_mode: FILE_DELETE_ON_CLOSE requested without \
+DELETE_ACCESS for file %s. (desired_access = 0x%lx)\n",
+ fname, (unsigned long)*desired_access));
+ return -1;
+ }
+ /* Implicit delete access is *NOT* requested... */
+ smb_open_mode |= DELETE_ON_CLOSE_FLAG;
+ DEBUG(10,("map_share_mode: FILE_DELETE_ON_CLOSE requested. open_mode = 0x%x\n", smb_open_mode));
+ }
+
+ /* Add in the requested share mode. */
+ switch( share_access & (FILE_SHARE_READ|FILE_SHARE_WRITE)) {
+ case FILE_SHARE_READ:
+ smb_open_mode |= SET_DENY_MODE(DENY_WRITE);
+ break;
+ case FILE_SHARE_WRITE:
+ smb_open_mode |= SET_DENY_MODE(DENY_READ);
+ break;
+ case (FILE_SHARE_READ|FILE_SHARE_WRITE):
+ smb_open_mode |= SET_DENY_MODE(DENY_NONE);
+ break;
+ case FILE_SHARE_NONE:
+ smb_open_mode |= SET_DENY_MODE(DENY_ALL);
+ break;
+ }
+
+ /*
+ * Handle an O_SYNC request.
+ */
+
+ if(file_attributes & FILE_FLAG_WRITE_THROUGH)
+ smb_open_mode |= FILE_SYNC_OPENMODE;
+
+ DEBUG(10,("map_share_mode: Mapped desired access 0x%lx, share access 0x%lx, file attributes 0x%lx \
+to open_mode 0x%x\n", (unsigned long)*desired_access, (unsigned long)share_access,
+ (unsigned long)file_attributes, smb_open_mode ));
+
+ return smb_open_mode;
+}
+
+/****************************************************************************
+ Reply to an NT create and X call on a pipe.
+****************************************************************************/
+
+static int nt_open_pipe(char *fname, connection_struct *conn,
+ char *inbuf, char *outbuf, int *ppnum)
+{
+ smb_np_struct *p = NULL;
+
+ uint16 vuid = SVAL(inbuf, smb_uid);
+ int i;
+
+ DEBUG(4,("nt_open_pipe: Opening pipe %s.\n", fname));
+
+ /* See if it is one we want to handle. */
+
+ if (lp_disable_spoolss() && strequal(fname, "\\spoolss"))
+ return(ERROR_BOTH(NT_STATUS_OBJECT_NAME_NOT_FOUND,ERRDOS,ERRbadpipe));
+
+ for( i = 0; known_nt_pipes[i]; i++ )
+ if( strequal(fname,known_nt_pipes[i]))
+ break;
+
+ if ( known_nt_pipes[i] == NULL )
+ return(ERROR_BOTH(NT_STATUS_OBJECT_NAME_NOT_FOUND,ERRDOS,ERRbadpipe));
+
+ /* Strip \\ off the name. */
+ fname++;
+
+ DEBUG(3,("nt_open_pipe: Known pipe %s opening.\n", fname));
+
+ p = open_rpc_pipe_p(fname, conn, vuid);
+ if (!p)
+ return(ERROR_DOS(ERRSRV,ERRnofids));
+
+ *ppnum = p->pnum;
+
+ return 0;
+}
+
+/****************************************************************************
+ Reply to an NT create and X call for pipes.
+****************************************************************************/
+
+static int do_ntcreate_pipe_open(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ pstring fname;
+ int ret;
+ int pnum = -1;
+ char *p = NULL;
+
+ srvstr_pull_buf(inbuf, fname, smb_buf(inbuf), sizeof(fname), STR_TERMINATE);
+
+ if ((ret = nt_open_pipe(fname, conn, inbuf, outbuf, &pnum)) != 0)
+ return ret;
+
+ /*
+ * Deal with pipe return.
+ */
+
+ set_message(outbuf,34,0,True);
+
+ p = outbuf + smb_vwv2;
+ p++;
+ SSVAL(p,0,pnum);
+ p += 2;
+ SIVAL(p,0,FILE_WAS_OPENED);
+ p += 4;
+ p += 32;
+ SIVAL(p,0,FILE_ATTRIBUTE_NORMAL); /* File Attributes. */
+ p += 20;
+ /* File type. */
+ SSVAL(p,0,FILE_TYPE_MESSAGE_MODE_PIPE);
+ /* Device state. */
+ SSVAL(p,2, 0x5FF); /* ? */
+
+ DEBUG(5,("do_ntcreate_pipe_open: open pipe = %s\n", fname));
+
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
+
+/****************************************************************************
+ Reply to an NT create and X call.
+****************************************************************************/
+
+int reply_ntcreate_and_X(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ int result;
+ pstring fname;
+ enum FAKE_FILE_TYPE fake_file_type = FAKE_FILE_TYPE_NONE;
+ uint32 flags = IVAL(inbuf,smb_ntcreate_Flags);
+ uint32 desired_access = IVAL(inbuf,smb_ntcreate_DesiredAccess);
+ uint32 file_attributes = IVAL(inbuf,smb_ntcreate_FileAttributes);
+ uint32 share_access = IVAL(inbuf,smb_ntcreate_ShareAccess);
+ uint32 create_disposition = IVAL(inbuf,smb_ntcreate_CreateDisposition);
+ uint32 create_options = IVAL(inbuf,smb_ntcreate_CreateOptions);
+ uint16 root_dir_fid = (uint16)IVAL(inbuf,smb_ntcreate_RootDirectoryFid);
+ SMB_BIG_UINT allocation_size = 0;
+ int smb_ofun;
+ int smb_open_mode;
+ /* Breakout the oplock request bits so we can set the
+ reply bits separately. */
+ int oplock_request = 0;
+ int fmode=0,rmode=0;
+ SMB_OFF_T file_len = 0;
+ SMB_STRUCT_STAT sbuf;
+ int smb_action = 0;
+ BOOL bad_path = False;
+ files_struct *fsp=NULL;
+ char *p = NULL;
+ time_t c_time;
+ BOOL extended_oplock_granted = False;
+ NTSTATUS status;
+
+ START_PROFILE(SMBntcreateX);
+
+ DEBUG(10,("reply_ntcreateX: flags = 0x%x, desired_access = 0x%x \
+file_attributes = 0x%x, share_access = 0x%x, create_disposition = 0x%x \
+create_options = 0x%x root_dir_fid = 0x%x\n", flags, desired_access, file_attributes,
+ share_access, create_disposition,
+ create_options, root_dir_fid ));
+
+ /* If it's an IPC, use the pipe handler. */
+
+ if (IS_IPC(conn)) {
+ if (lp_nt_pipe_support()) {
+ END_PROFILE(SMBntcreateX);
+ return do_ntcreate_pipe_open(conn,inbuf,outbuf,length,bufsize);
+ } else {
+ END_PROFILE(SMBntcreateX);
+ return(ERROR_DOS(ERRDOS,ERRnoaccess));
+ }
+ }
+
+ if (create_options & FILE_OPEN_BY_FILE_ID) {
+ END_PROFILE(SMBntcreateX);
+ return ERROR_NT(NT_STATUS_NOT_SUPPORTED);
+ }
+
+ /*
+ * We need to construct the open_and_X ofun value from the
+ * NT values, as that's what our code is structured to accept.
+ */
+
+ if((smb_ofun = map_create_disposition( create_disposition )) == -1) {
+ END_PROFILE(SMBntcreateX);
+ return(ERROR_DOS(ERRDOS,ERRnoaccess));
+ }
+
+ /*
+ * Get the file name.
+ */
+
+ if(root_dir_fid != 0) {
+ /*
+ * This filename is relative to a directory fid.
+ */
+ files_struct *dir_fsp = file_fsp(inbuf,smb_ntcreate_RootDirectoryFid);
+ size_t dir_name_len;
+
+ if(!dir_fsp) {
+ END_PROFILE(SMBntcreateX);
+ return(ERROR_DOS(ERRDOS,ERRbadfid));
+ }
+
+ if(!dir_fsp->is_directory) {
+
+ srvstr_get_path(inbuf, fname, smb_buf(inbuf), sizeof(fname), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBntcreateX);
+ return ERROR_NT(status);
+ }
+
+ /*
+ * Check to see if this is a mac fork of some kind.
+ */
+
+ if( strchr_m(fname, ':')) {
+ END_PROFILE(SMBntcreateX);
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ }
+
+ /*
+ we need to handle the case when we get a
+ relative open relative to a file and the
+ pathname is blank - this is a reopen!
+ (hint from demyn plantenberg)
+ */
+
+ END_PROFILE(SMBntcreateX);
+ return(ERROR_DOS(ERRDOS,ERRbadfid));
+ }
+
+ /*
+ * Copy in the base directory name.
+ */
+
+ pstrcpy( fname, dir_fsp->fsp_name );
+ dir_name_len = strlen(fname);
+
+ /*
+ * Ensure it ends in a '\'.
+ */
+
+ if(fname[dir_name_len-1] != '\\' && fname[dir_name_len-1] != '/') {
+ pstrcat(fname, "\\");
+ dir_name_len++;
+ }
+
+ srvstr_get_path(inbuf, &fname[dir_name_len], smb_buf(inbuf), sizeof(fname)-dir_name_len, 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBntcreateX);
+ return ERROR_NT(status);
+ }
+ } else {
+ srvstr_get_path(inbuf, fname, smb_buf(inbuf), sizeof(fname), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBntcreateX);
+ return ERROR_NT(status);
+ }
+
+ /*
+ * Check to see if this is a mac fork of some kind.
+ */
+
+ if( strchr_m(fname, ':')) {
+
+#ifdef HAVE_SYS_QUOTAS
+ if ((fake_file_type=is_fake_file(fname))!=FAKE_FILE_TYPE_NONE) {
+ /*
+ * here we go! support for changing the disk quotas --metze
+ *
+ * we need to fake up to open this MAGIC QUOTA file
+ * and return a valid FID
+ *
+ * w2k close this file directly after openening
+ * xp also tries a QUERY_FILE_INFO on the file and then close it
+ */
+ } else {
+#endif
+ END_PROFILE(SMBntcreateX);
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+#ifdef HAVE_SYS_QUOTAS
+ }
+#endif
+ }
+ }
+
+ /*
+ * Now contruct the smb_open_mode value from the filename,
+ * desired access and the share access.
+ */
+ RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
+
+ if((smb_open_mode = map_share_mode(fname, create_options, &desired_access,
+ share_access,
+ file_attributes)) == -1) {
+ END_PROFILE(SMBntcreateX);
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ }
+
+ oplock_request = (flags & REQUEST_OPLOCK) ? EXCLUSIVE_OPLOCK : 0;
+ if (oplock_request) {
+ oplock_request |= (flags & REQUEST_BATCH_OPLOCK) ? BATCH_OPLOCK : 0;
+ }
+
+ /*
+ * Ordinary file or directory.
+ */
+
+ /*
+ * Check if POSIX semantics are wanted.
+ */
+
+ set_posix_case_semantics(file_attributes);
+
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+
+ /*
+ * If it's a request for a directory open, deal with it separately.
+ */
+
+ if(create_options & FILE_DIRECTORY_FILE) {
+ oplock_request = 0;
+
+ /* Can't open a temp directory. IFS kit test. */
+ if (file_attributes & FILE_ATTRIBUTE_TEMPORARY) {
+ END_PROFILE(SMBntcreateX);
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ }
+
+ fsp = open_directory(conn, fname, &sbuf, desired_access, smb_open_mode, smb_ofun, &smb_action);
+
+ restore_case_semantics(file_attributes);
+
+ if(!fsp) {
+ END_PROFILE(SMBntcreateX);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
+ }
+ } else {
+ /*
+ * Ordinary file case.
+ */
+
+ /* NB. We have a potential bug here. If we
+ * cause an oplock break to ourselves, then we
+ * could end up processing filename related
+ * SMB requests whilst we await the oplock
+ * break response. As we may have changed the
+ * filename case semantics to be POSIX-like,
+ * this could mean a filename request could
+ * fail when it should succeed. This is a rare
+ * condition, but eventually we must arrange
+ * to restore the correct case semantics
+ * before issuing an oplock break request to
+ * our client. JRA. */
+
+ if (fake_file_type==FAKE_FILE_TYPE_NONE) {
+ fsp = open_file_shared1(conn,fname,&sbuf,
+ desired_access,
+ smb_open_mode,
+ smb_ofun,file_attributes,oplock_request,
+ &rmode,&smb_action);
+ } else {
+ /* to open a fake_file --metze */
+ fsp = open_fake_file_shared1(fake_file_type,conn,fname,&sbuf,
+ desired_access,
+ smb_open_mode,
+ smb_ofun,file_attributes, oplock_request,
+ &rmode,&smb_action);
+ }
+
+ if (!fsp) {
+ /* We cheat here. There are two cases we
+ * care about. One is a directory rename,
+ * where the NT client will attempt to
+ * open the source directory for
+ * DELETE access. Note that when the
+ * NT client does this it does *not*
+ * set the directory bit in the
+ * request packet. This is translated
+ * into a read/write open
+ * request. POSIX states that any open
+ * for write request on a directory
+ * will generate an EISDIR error, so
+ * we can catch this here and open a
+ * pseudo handle that is flagged as a
+ * directory. The second is an open
+ * for a permissions read only, which
+ * we handle in the open_file_stat case. JRA.
+ */
+
+ if(errno == EISDIR) {
+
+ /*
+ * Fail the open if it was explicitly a non-directory file.
+ */
+
+ if (create_options & FILE_NON_DIRECTORY_FILE) {
+ restore_case_semantics(file_attributes);
+ SSVAL(outbuf, smb_flg2,
+ SVAL(outbuf,smb_flg2) | FLAGS2_32_BIT_ERROR_CODES);
+ END_PROFILE(SMBntcreateX);
+ return ERROR_NT(NT_STATUS_FILE_IS_A_DIRECTORY);
+ }
+
+ oplock_request = 0;
+ fsp = open_directory(conn, fname, &sbuf, desired_access, smb_open_mode, smb_ofun, &smb_action);
+
+ if(!fsp) {
+ restore_case_semantics(file_attributes);
+ END_PROFILE(SMBntcreateX);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
+ }
+ } else {
+
+ restore_case_semantics(file_attributes);
+ END_PROFILE(SMBntcreateX);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
+ }
+ }
+ }
+
+ restore_case_semantics(file_attributes);
+
+ file_len = sbuf.st_size;
+ fmode = dos_mode(conn,fname,&sbuf);
+ if(fmode == 0)
+ fmode = FILE_ATTRIBUTE_NORMAL;
+ if (!fsp->is_directory && (fmode & aDIR)) {
+ close_file(fsp,False);
+ END_PROFILE(SMBntcreateX);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ /* Save the requested allocation size. */
+ allocation_size = (SMB_BIG_UINT)IVAL(inbuf,smb_ntcreate_AllocationSize);
+#ifdef LARGE_SMB_OFF_T
+ allocation_size |= (((SMB_BIG_UINT)IVAL(inbuf,smb_ntcreate_AllocationSize + 4)) << 32);
+#endif
+ if (allocation_size && (allocation_size > (SMB_BIG_UINT)file_len)) {
+ fsp->initial_allocation_size = SMB_ROUNDUP(allocation_size,SMB_ROUNDUP_ALLOCATION_SIZE);
+ if (fsp->is_directory) {
+ close_file(fsp,False);
+ END_PROFILE(SMBntcreateX);
+ /* Can't set allocation size on a directory. */
+ return ERROR_NT(NT_STATUS_ACCESS_DENIED);
+ }
+ if (vfs_allocate_file_space(fsp, fsp->initial_allocation_size) == -1) {
+ close_file(fsp,False);
+ END_PROFILE(SMBntcreateX);
+ return ERROR_NT(NT_STATUS_DISK_FULL);
+ }
+ } else {
+ fsp->initial_allocation_size = SMB_ROUNDUP(((SMB_BIG_UINT)file_len),SMB_ROUNDUP_ALLOCATION_SIZE);
+ }
+
+ /*
+ * If the caller set the extended oplock request bit
+ * and we granted one (by whatever means) - set the
+ * correct bit for extended oplock reply.
+ */
+
+ if (oplock_request && lp_fake_oplocks(SNUM(conn)))
+ extended_oplock_granted = True;
+
+ if(oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type))
+ extended_oplock_granted = True;
+
+#if 0
+ /* W2K sends back 42 words here ! If we do the same it breaks offline sync. Go figure... ? JRA. */
+ set_message(outbuf,42,0,True);
+#else
+ set_message(outbuf,34,0,True);
+#endif
+
+ p = outbuf + smb_vwv2;
+
+ /*
+ * Currently as we don't support level II oplocks we just report
+ * exclusive & batch here.
+ */
+
+ if (extended_oplock_granted) {
+ if (flags & REQUEST_BATCH_OPLOCK) {
+ SCVAL(p,0, BATCH_OPLOCK_RETURN);
+ } else {
+ SCVAL(p,0, EXCLUSIVE_OPLOCK_RETURN);
+ }
+ } else if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type)) {
+ SCVAL(p,0, LEVEL_II_OPLOCK_RETURN);
+ } else {
+ SCVAL(p,0,NO_OPLOCK_RETURN);
+ }
+
+ p++;
+ SSVAL(p,0,fsp->fnum);
+ p += 2;
+ if ((create_disposition == FILE_SUPERSEDE) && (smb_action == FILE_WAS_OVERWRITTEN))
+ SIVAL(p,0,FILE_WAS_SUPERSEDED);
+ else
+ SIVAL(p,0,smb_action);
+ p += 4;
+
+ /* Create time. */
+ c_time = get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn)));
+
+ if (lp_dos_filetime_resolution(SNUM(conn))) {
+ c_time &= ~1;
+ sbuf.st_atime &= ~1;
+ sbuf.st_mtime &= ~1;
+ sbuf.st_mtime &= ~1;
+ }
+
+ put_long_date(p,c_time);
+ p += 8;
+ put_long_date(p,sbuf.st_atime); /* access time */
+ p += 8;
+ put_long_date(p,sbuf.st_mtime); /* write time */
+ p += 8;
+ put_long_date(p,sbuf.st_mtime); /* change time */
+ p += 8;
+ SIVAL(p,0,fmode); /* File Attributes. */
+ p += 4;
+ SOFF_T(p, 0, get_allocation_size(fsp,&sbuf));
+ p += 8;
+ SOFF_T(p,0,file_len);
+ p += 8;
+ if (flags & EXTENDED_RESPONSE_REQUIRED)
+ SSVAL(p,2,0x7);
+ p += 4;
+ SCVAL(p,0,fsp->is_directory ? 1 : 0);
+
+ DEBUG(5,("reply_ntcreate_and_X: fnum = %d, open name = %s\n", fsp->fnum, fsp->fsp_name));
+
+ result = chain_reply(inbuf,outbuf,length,bufsize);
+ END_PROFILE(SMBntcreateX);
+ return result;
+}
+
+/****************************************************************************
+ Reply to a NT_TRANSACT_CREATE call to open a pipe.
+****************************************************************************/
+
+static int do_nt_transact_create_pipe( connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
+ char **ppsetup, uint32 setup_count,
+ char **ppparams, uint32 parameter_count,
+ char **ppdata, uint32 data_count)
+{
+ pstring fname;
+ char *params = *ppparams;
+ int ret;
+ int pnum = -1;
+ char *p = NULL;
+ NTSTATUS status;
+
+ /*
+ * Ensure minimum number of parameters sent.
+ */
+
+ if(parameter_count < 54) {
+ DEBUG(0,("do_nt_transact_create_pipe - insufficient parameters (%u)\n", (unsigned int)parameter_count));
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ srvstr_get_path(inbuf, fname, params+53, sizeof(fname), parameter_count-53, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+
+ if ((ret = nt_open_pipe(fname, conn, inbuf, outbuf, &pnum)) != 0)
+ return ret;
+
+ /* Realloc the size of parameters and data we will return */
+ params = nttrans_realloc(ppparams, 69);
+ if(params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+
+ p = params;
+ SCVAL(p,0,NO_OPLOCK_RETURN);
+
+ p += 2;
+ SSVAL(p,0,pnum);
+ p += 2;
+ SIVAL(p,0,FILE_WAS_OPENED);
+ p += 8;
+
+ p += 32;
+ SIVAL(p,0,FILE_ATTRIBUTE_NORMAL); /* File Attributes. */
+ p += 20;
+ /* File type. */
+ SSVAL(p,0,FILE_TYPE_MESSAGE_MODE_PIPE);
+ /* Device state. */
+ SSVAL(p,2, 0x5FF); /* ? */
+
+ DEBUG(5,("do_nt_transact_create_pipe: open name = %s\n", fname));
+
+ /* Send the required number of replies */
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, params, 69, *ppdata, 0);
+
+ return -1;
+}
+
+/****************************************************************************
+ Internal fn to set security descriptors.
+****************************************************************************/
+
+static NTSTATUS set_sd(files_struct *fsp, char *data, uint32 sd_len, uint32 security_info_sent)
+{
+ prs_struct pd;
+ SEC_DESC *psd = NULL;
+ TALLOC_CTX *mem_ctx;
+ BOOL ret;
+
+ if (sd_len == 0) {
+ return NT_STATUS_OK;
+ }
+
+ /*
+ * Init the parse struct we will unmarshall from.
+ */
+
+ if ((mem_ctx = talloc_init("set_sd")) == NULL) {
+ DEBUG(0,("set_sd: talloc_init failed.\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ prs_init(&pd, 0, mem_ctx, UNMARSHALL);
+
+ /*
+ * Setup the prs_struct to point at the memory we just
+ * allocated.
+ */
+
+ prs_give_memory( &pd, data, sd_len, False);
+
+ /*
+ * Finally, unmarshall from the data buffer.
+ */
+
+ if(!sec_io_desc( "sd data", &psd, &pd, 1)) {
+ DEBUG(0,("set_sd: Error in unmarshalling security descriptor.\n"));
+ /*
+ * Return access denied for want of a better error message..
+ */
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (psd->off_owner_sid==0)
+ security_info_sent &= ~OWNER_SECURITY_INFORMATION;
+ if (psd->off_grp_sid==0)
+ security_info_sent &= ~GROUP_SECURITY_INFORMATION;
+ if (psd->off_sacl==0)
+ security_info_sent &= ~SACL_SECURITY_INFORMATION;
+ if (psd->off_dacl==0)
+ security_info_sent &= ~DACL_SECURITY_INFORMATION;
+
+ ret = SMB_VFS_FSET_NT_ACL( fsp, fsp->fd, security_info_sent, psd);
+
+ if (!ret) {
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ talloc_destroy(mem_ctx);
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Reply to a NT_TRANSACT_CREATE call (needs to process SD's).
+****************************************************************************/
+
+static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
+ char **ppsetup, uint32 setup_count,
+ char **ppparams, uint32 parameter_count,
+ char **ppdata, uint32 data_count)
+{
+ pstring fname;
+ char *params = *ppparams;
+ char *data = *ppdata;
+ /* Breakout the oplock request bits so we can set the reply bits separately. */
+ int oplock_request = 0;
+ int fmode=0,rmode=0;
+ SMB_OFF_T file_len = 0;
+ SMB_STRUCT_STAT sbuf;
+ int smb_action = 0;
+ BOOL bad_path = False;
+ files_struct *fsp = NULL;
+ char *p = NULL;
+ BOOL extended_oplock_granted = False;
+ uint32 flags;
+ uint32 desired_access;
+ uint32 file_attributes;
+ uint32 share_access;
+ uint32 create_disposition;
+ uint32 create_options;
+ uint32 sd_len;
+ uint16 root_dir_fid;
+ SMB_BIG_UINT allocation_size = 0;
+ int smb_ofun;
+ int smb_open_mode;
+ time_t c_time;
+ NTSTATUS status;
+
+ DEBUG(5,("call_nt_transact_create\n"));
+
+ /*
+ * If it's an IPC, use the pipe handler.
+ */
+
+ if (IS_IPC(conn)) {
+ if (lp_nt_pipe_support())
+ return do_nt_transact_create_pipe(conn, inbuf, outbuf, length,
+ bufsize,
+ ppsetup, setup_count,
+ ppparams, parameter_count,
+ ppdata, data_count);
+ else
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ /*
+ * Ensure minimum number of parameters sent.
+ */
+
+ if(parameter_count < 54) {
+ DEBUG(0,("call_nt_transact_create - insufficient parameters (%u)\n", (unsigned int)parameter_count));
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ flags = IVAL(params,0);
+ desired_access = IVAL(params,8);
+ file_attributes = IVAL(params,20);
+ share_access = IVAL(params,24);
+ create_disposition = IVAL(params,28);
+ create_options = IVAL(params,32);
+ sd_len = IVAL(params,36);
+ root_dir_fid = (uint16)IVAL(params,4);
+
+ if (create_options & FILE_OPEN_BY_FILE_ID) {
+ return ERROR_NT(NT_STATUS_NOT_SUPPORTED);
+ }
+
+ /*
+ * We need to construct the open_and_X ofun value from the
+ * NT values, as that's what our code is structured to accept.
+ */
+
+ if((smb_ofun = map_create_disposition( create_disposition )) == -1)
+ return ERROR_DOS(ERRDOS,ERRbadmem);
+
+ /*
+ * Get the file name.
+ */
+
+ if(root_dir_fid != 0) {
+ /*
+ * This filename is relative to a directory fid.
+ */
+
+ files_struct *dir_fsp = file_fsp(params,4);
+ size_t dir_name_len;
+
+ if(!dir_fsp)
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+
+ if(!dir_fsp->is_directory) {
+ srvstr_get_path(inbuf, fname, params+53, sizeof(fname), parameter_count-53, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+
+ /*
+ * Check to see if this is a mac fork of some kind.
+ */
+
+ if( strchr_m(fname, ':'))
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+ }
+
+ /*
+ * Copy in the base directory name.
+ */
+
+ pstrcpy( fname, dir_fsp->fsp_name );
+ dir_name_len = strlen(fname);
+
+ /*
+ * Ensure it ends in a '\'.
+ */
+
+ if((fname[dir_name_len-1] != '\\') && (fname[dir_name_len-1] != '/')) {
+ pstrcat(fname, "\\");
+ dir_name_len++;
+ }
+
+ {
+ pstring tmpname;
+ srvstr_get_path(inbuf, tmpname, params+53, sizeof(tmpname), parameter_count-53, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+ pstrcat(fname, tmpname);
+ }
+ } else {
+ srvstr_get_path(inbuf, fname, params+53, sizeof(fname), parameter_count-53, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+
+ /*
+ * Check to see if this is a mac fork of some kind.
+ */
+
+ if( strchr_m(fname, ':'))
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ }
+
+ /*
+ * Now contruct the smb_open_mode value from the desired access
+ * and the share access.
+ */
+
+ if((smb_open_mode = map_share_mode( fname, create_options, &desired_access,
+ share_access, file_attributes)) == -1)
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+
+ oplock_request = (flags & REQUEST_OPLOCK) ? EXCLUSIVE_OPLOCK : 0;
+ oplock_request |= (flags & REQUEST_BATCH_OPLOCK) ? BATCH_OPLOCK : 0;
+
+ /*
+ * Check if POSIX semantics are wanted.
+ */
+
+ set_posix_case_semantics(file_attributes);
+
+ RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
+
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+
+ /*
+ * If it's a request for a directory open, deal with it separately.
+ */
+
+ if(create_options & FILE_DIRECTORY_FILE) {
+
+ /* Can't open a temp directory. IFS kit test. */
+ if (file_attributes & FILE_ATTRIBUTE_TEMPORARY) {
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ }
+
+ oplock_request = 0;
+
+ /*
+ * We will get a create directory here if the Win32
+ * app specified a security descriptor in the
+ * CreateDirectory() call.
+ */
+
+ fsp = open_directory(conn, fname, &sbuf, desired_access, smb_open_mode, smb_ofun, &smb_action);
+
+ if(!fsp) {
+ restore_case_semantics(file_attributes);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
+ }
+
+ } else {
+
+ /*
+ * Ordinary file case.
+ */
+
+ fsp = open_file_shared1(conn,fname,&sbuf,desired_access,
+ smb_open_mode,smb_ofun,file_attributes,
+ oplock_request,&rmode,&smb_action);
+
+ if (!fsp) {
+
+ if(errno == EISDIR) {
+
+ /*
+ * Fail the open if it was explicitly a non-directory file.
+ */
+
+ if (create_options & FILE_NON_DIRECTORY_FILE) {
+ restore_case_semantics(file_attributes);
+ SSVAL(outbuf, smb_flg2, SVAL(outbuf,smb_flg2) | FLAGS2_32_BIT_ERROR_CODES);
+ return ERROR_NT(NT_STATUS_FILE_IS_A_DIRECTORY);
+ }
+
+ oplock_request = 0;
+ fsp = open_directory(conn, fname, &sbuf, desired_access, smb_open_mode, smb_ofun, &smb_action);
+
+ if(!fsp) {
+ restore_case_semantics(file_attributes);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
+ }
+ } else {
+ restore_case_semantics(file_attributes);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
+ }
+ }
+
+ file_len = sbuf.st_size;
+ fmode = dos_mode(conn,fname,&sbuf);
+ if(fmode == 0)
+ fmode = FILE_ATTRIBUTE_NORMAL;
+
+ if (fmode & aDIR) {
+ close_file(fsp,False);
+ restore_case_semantics(file_attributes);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ /*
+ * If the caller set the extended oplock request bit
+ * and we granted one (by whatever means) - set the
+ * correct bit for extended oplock reply.
+ */
+
+ if (oplock_request && lp_fake_oplocks(SNUM(conn)))
+ extended_oplock_granted = True;
+
+ if(oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type))
+ extended_oplock_granted = True;
+ }
+
+ /*
+ * Now try and apply the desired SD.
+ */
+
+ if (sd_len && !NT_STATUS_IS_OK(status = set_sd( fsp, data, sd_len, ALL_SECURITY_INFORMATION))) {
+ close_file(fsp,False);
+ restore_case_semantics(file_attributes);
+ return ERROR_NT(status);
+ }
+
+ restore_case_semantics(file_attributes);
+
+ /* Save the requested allocation size. */
+ allocation_size = (SMB_BIG_UINT)IVAL(params,12);
+#ifdef LARGE_SMB_OFF_T
+ allocation_size |= (((SMB_BIG_UINT)IVAL(params,16)) << 32);
+#endif
+ if (allocation_size && (allocation_size > file_len)) {
+ fsp->initial_allocation_size = SMB_ROUNDUP(allocation_size,SMB_ROUNDUP_ALLOCATION_SIZE);
+ if (fsp->is_directory) {
+ close_file(fsp,False);
+ END_PROFILE(SMBntcreateX);
+ /* Can't set allocation size on a directory. */
+ return ERROR_NT(NT_STATUS_ACCESS_DENIED);
+ }
+ if (vfs_allocate_file_space(fsp, fsp->initial_allocation_size) == -1) {
+ close_file(fsp,False);
+ return ERROR_NT(NT_STATUS_DISK_FULL);
+ }
+ } else {
+ fsp->initial_allocation_size = SMB_ROUNDUP(((SMB_BIG_UINT)file_len),SMB_ROUNDUP_ALLOCATION_SIZE);
+ }
+
+ /* Realloc the size of parameters and data we will return */
+ params = nttrans_realloc(ppparams, 69);
+ if(params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+
+ p = params;
+ if (extended_oplock_granted)
+ SCVAL(p,0, BATCH_OPLOCK_RETURN);
+ else if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type))
+ SCVAL(p,0, LEVEL_II_OPLOCK_RETURN);
+ else
+ SCVAL(p,0,NO_OPLOCK_RETURN);
+
+ p += 2;
+ SSVAL(p,0,fsp->fnum);
+ p += 2;
+ if ((create_disposition == FILE_SUPERSEDE) && (smb_action == FILE_WAS_OVERWRITTEN))
+ SIVAL(p,0,FILE_WAS_SUPERSEDED);
+ else
+ SIVAL(p,0,smb_action);
+ p += 8;
+
+ /* Create time. */
+ c_time = get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn)));
+
+ if (lp_dos_filetime_resolution(SNUM(conn))) {
+ c_time &= ~1;
+ sbuf.st_atime &= ~1;
+ sbuf.st_mtime &= ~1;
+ sbuf.st_mtime &= ~1;
+ }
+
+ put_long_date(p,c_time);
+ p += 8;
+ put_long_date(p,sbuf.st_atime); /* access time */
+ p += 8;
+ put_long_date(p,sbuf.st_mtime); /* write time */
+ p += 8;
+ put_long_date(p,sbuf.st_mtime); /* change time */
+ p += 8;
+ SIVAL(p,0,fmode); /* File Attributes. */
+ p += 4;
+ SOFF_T(p, 0, get_allocation_size(fsp,&sbuf));
+ p += 8;
+ SOFF_T(p,0,file_len);
+ p += 8;
+ if (flags & EXTENDED_RESPONSE_REQUIRED)
+ SSVAL(p,2,0x7);
+ p += 4;
+ SCVAL(p,0,fsp->is_directory ? 1 : 0);
+
+ DEBUG(5,("call_nt_transact_create: open name = %s\n", fname));
+
+ /* Send the required number of replies */
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, params, 69, *ppdata, 0);
+
+ return -1;
+}
+
+/****************************************************************************
+ Reply to a NT CANCEL request.
+****************************************************************************/
+
+int reply_ntcancel(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ /*
+ * Go through and cancel any pending change notifies.
+ */
+
+ int mid = SVAL(inbuf,smb_mid);
+ START_PROFILE(SMBntcancel);
+ remove_pending_change_notify_requests_by_mid(mid);
+ remove_pending_lock_requests_by_mid(mid);
+ srv_cancel_sign_response(mid);
+
+ DEBUG(3,("reply_ntcancel: cancel called on mid = %d.\n", mid));
+
+ END_PROFILE(SMBntcancel);
+ return(-1);
+}
+
+/****************************************************************************
+ Reply to a NT rename request.
+****************************************************************************/
+
+int reply_ntrename(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ int outsize = 0;
+ pstring name;
+ pstring newname;
+ char *p;
+ NTSTATUS status;
+ uint16 attrs = SVAL(inbuf,smb_vwv0);
+ uint16 rename_type = SVAL(inbuf,smb_vwv1);
+
+ START_PROFILE(SMBntrename);
+
+ if ((rename_type != RENAME_FLAG_RENAME) && (rename_type != RENAME_FLAG_HARD_LINK)) {
+ END_PROFILE(SMBntrename);
+ return ERROR_NT(NT_STATUS_ACCESS_DENIED);
+ }
+
+ p = smb_buf(inbuf) + 1;
+ p += srvstr_get_path(inbuf, name, p, sizeof(name), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBntrename);
+ return ERROR_NT(status);
+ }
+
+ if( strchr_m(name, ':')) {
+ /* Can't rename a stream. */
+ END_PROFILE(SMBntrename);
+ return ERROR_NT(NT_STATUS_ACCESS_DENIED);
+ }
+
+ p++;
+ p += srvstr_get_path(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBntrename);
+ return ERROR_NT(status);
+ }
+
+ RESOLVE_DFSPATH(name, conn, inbuf, outbuf);
+ RESOLVE_DFSPATH(newname, conn, inbuf, outbuf);
+
+ DEBUG(3,("reply_ntrename : %s -> %s\n",name,newname));
+
+ if (rename_type == RENAME_FLAG_RENAME) {
+ status = rename_internals(conn, name, newname, attrs, False);
+ } else {
+ status = hardlink_internals(conn, name, newname);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBntrename);
+ return ERROR_NT(status);
+ }
+
+ /*
+ * Win2k needs a changenotify request response before it will
+ * update after a rename..
+ */
+ process_pending_change_notify_queue((time_t)0);
+ outsize = set_message(outbuf,0,0,True);
+
+ END_PROFILE(SMBntrename);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to an unsolicited SMBNTtranss - just ignore it!
+****************************************************************************/
+
+int reply_nttranss(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ START_PROFILE(SMBnttranss);
+ DEBUG(4,("Ignoring nttranss of length %d\n",length));
+ END_PROFILE(SMBnttranss);
+ return(-1);
+}
+
+/****************************************************************************
+ Reply to a notify change - queue the request and
+ don't allow a directory to be opened.
+****************************************************************************/
+
+static int call_nt_transact_notify_change(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
+ char **ppsetup, uint32 setup_count,
+ char **ppparams, uint32 parameter_count,
+ char **ppdata, uint32 data_count)
+{
+ char *setup = *ppsetup;
+ files_struct *fsp;
+ uint32 flags;
+
+ if(setup_count < 6)
+ return ERROR_DOS(ERRDOS,ERRbadfunc);
+
+ fsp = file_fsp(setup,4);
+ flags = IVAL(setup, 0);
+
+ DEBUG(3,("call_nt_transact_notify_change\n"));
+
+ if(!fsp)
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+
+ if((!fsp->is_directory) || (conn != fsp->conn))
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+
+ if (!change_notify_set(inbuf, fsp, conn, flags))
+ return(UNIXERROR(ERRDOS,ERRbadfid));
+
+ DEBUG(3,("call_nt_transact_notify_change: notify change called on directory \
+name = %s\n", fsp->fsp_name ));
+
+ return -1;
+}
+
+/****************************************************************************
+ Reply to an NT transact rename command.
+****************************************************************************/
+
+static int call_nt_transact_rename(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
+ char **ppsetup, uint32 setup_count,
+ char **ppparams, uint32 parameter_count,
+ char **ppdata, uint32 data_count)
+{
+ char *params = *ppparams;
+ pstring new_name;
+ files_struct *fsp = NULL;
+ BOOL replace_if_exists = False;
+ NTSTATUS status;
+
+ if(parameter_count < 4)
+ return ERROR_DOS(ERRDOS,ERRbadfunc);
+
+ fsp = file_fsp(params, 0);
+ replace_if_exists = (SVAL(params,2) & RENAME_REPLACE_IF_EXISTS) ? True : False;
+ CHECK_FSP(fsp, conn);
+ srvstr_get_path(inbuf, new_name, params+4, sizeof(new_name), -1, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+
+ status = rename_internals(conn, fsp->fsp_name,
+ new_name, 0, replace_if_exists);
+ if (!NT_STATUS_IS_OK(status))
+ return ERROR_NT(status);
+
+ /*
+ * Rename was successful.
+ */
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, NULL, 0, NULL, 0);
+
+ DEBUG(3,("nt transact rename from = %s, to = %s succeeded.\n",
+ fsp->fsp_name, new_name));
+
+ /*
+ * Win2k needs a changenotify request response before it will
+ * update after a rename..
+ */
+
+ process_pending_change_notify_queue((time_t)0);
+
+ return -1;
+}
+
+/******************************************************************************
+ Fake up a completely empty SD.
+*******************************************************************************/
+
+static size_t get_null_nt_acl(TALLOC_CTX *mem_ctx, SEC_DESC **ppsd)
+{
+ extern DOM_SID global_sid_World;
+ size_t sd_size;
+
+ *ppsd = make_standard_sec_desc( mem_ctx, &global_sid_World, &global_sid_World, NULL, &sd_size);
+ if(!*ppsd) {
+ DEBUG(0,("get_null_nt_acl: Unable to malloc space for security descriptor.\n"));
+ sd_size = 0;
+ }
+
+ return sd_size;
+}
+
+/****************************************************************************
+ Reply to query a security descriptor.
+****************************************************************************/
+
+static int call_nt_transact_query_security_desc(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
+ char **ppsetup, uint32 setup_count,
+ char **ppparams, uint32 parameter_count,
+ char **ppdata, uint32 data_count)
+{
+ uint32 max_data_count = IVAL(inbuf,smb_nt_MaxDataCount);
+ char *params = *ppparams;
+ char *data = *ppdata;
+ prs_struct pd;
+ SEC_DESC *psd = NULL;
+ size_t sd_size;
+ uint32 security_info_wanted;
+ TALLOC_CTX *mem_ctx;
+ files_struct *fsp = NULL;
+
+ if(parameter_count < 8)
+ return ERROR_DOS(ERRDOS,ERRbadfunc);
+
+ fsp = file_fsp(params,0);
+ if(!fsp)
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+
+ security_info_wanted = IVAL(params,4);
+
+ DEBUG(3,("call_nt_transact_query_security_desc: file = %s\n", fsp->fsp_name ));
+
+ params = nttrans_realloc(ppparams, 4);
+ if(params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+
+ if ((mem_ctx = talloc_init("call_nt_transact_query_security_desc")) == NULL) {
+ DEBUG(0,("call_nt_transact_query_security_desc: talloc_init failed.\n"));
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ }
+
+ /*
+ * Get the permissions to return.
+ */
+
+ if (!lp_nt_acl_support(SNUM(conn)))
+ sd_size = get_null_nt_acl(mem_ctx, &psd);
+ else
+ sd_size = SMB_VFS_FGET_NT_ACL(fsp, fsp->fd, security_info_wanted, &psd);
+
+ if (sd_size == 0) {
+ talloc_destroy(mem_ctx);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ DEBUG(3,("call_nt_transact_query_security_desc: sd_size = %d.\n",(int)sd_size));
+
+ SIVAL(params,0,(uint32)sd_size);
+
+ if(max_data_count < sd_size) {
+
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_BUFFER_TOO_SMALL,
+ params, 4, *ppdata, 0);
+ talloc_destroy(mem_ctx);
+ return -1;
+ }
+
+ /*
+ * Allocate the data we will point this at.
+ */
+
+ data = nttrans_realloc(ppdata, sd_size);
+ if(data == NULL) {
+ talloc_destroy(mem_ctx);
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ }
+
+ /*
+ * Init the parse struct we will marshall into.
+ */
+
+ prs_init(&pd, 0, mem_ctx, MARSHALL);
+
+ /*
+ * Setup the prs_struct to point at the memory we just
+ * allocated.
+ */
+
+ prs_give_memory( &pd, data, (uint32)sd_size, False);
+
+ /*
+ * Finally, linearize into the outgoing buffer.
+ */
+
+ if(!sec_io_desc( "sd data", &psd, &pd, 1)) {
+ DEBUG(0,("call_nt_transact_query_security_desc: Error in marshalling \
+security descriptor.\n"));
+ /*
+ * Return access denied for want of a better error message..
+ */
+ talloc_destroy(mem_ctx);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ /*
+ * Now we can delete the security descriptor.
+ */
+
+ talloc_destroy(mem_ctx);
+
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, params, 4, data, (int)sd_size);
+ return -1;
+}
+
+/****************************************************************************
+ Reply to set a security descriptor. Map to UNIX perms or POSIX ACLs.
+****************************************************************************/
+
+static int call_nt_transact_set_security_desc(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
+ char **ppsetup, uint32 setup_count,
+ char **ppparams, uint32 parameter_count,
+ char **ppdata, uint32 data_count)
+{
+ char *params= *ppparams;
+ char *data = *ppdata;
+ files_struct *fsp = NULL;
+ uint32 security_info_sent = 0;
+ NTSTATUS nt_status;
+
+ if(parameter_count < 8)
+ return ERROR_DOS(ERRDOS,ERRbadfunc);
+
+ if((fsp = file_fsp(params,0)) == NULL)
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+
+ if(!lp_nt_acl_support(SNUM(conn)))
+ goto done;
+
+ security_info_sent = IVAL(params,4);
+
+ DEBUG(3,("call_nt_transact_set_security_desc: file = %s, sent 0x%x\n", fsp->fsp_name,
+ (unsigned int)security_info_sent ));
+
+ if (data_count == 0)
+ return ERROR_DOS(ERRDOS, ERRnoaccess);
+
+ if (!NT_STATUS_IS_OK(nt_status = set_sd( fsp, data, data_count, security_info_sent)))
+ return ERROR_NT(nt_status);
+
+ done:
+
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, NULL, 0, NULL, 0);
+ return -1;
+}
+
+/****************************************************************************
+ Reply to NT IOCTL
+****************************************************************************/
+
+static int call_nt_transact_ioctl(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
+ char **ppsetup, uint32 setup_count,
+ char **ppparams, uint32 parameter_count,
+ char **ppdata, uint32 data_count)
+{
+ uint32 function;
+ uint16 fidnum;
+ files_struct *fsp;
+ uint8 isFSctl;
+ uint8 compfilter;
+ static BOOL logged_message;
+ char *pdata = *ppdata;
+
+ if (setup_count != 8) {
+ DEBUG(3,("call_nt_transact_ioctl: invalid setup count %d\n", setup_count));
+ return ERROR_NT(NT_STATUS_NOT_SUPPORTED);
+ }
+
+ function = IVAL(*ppsetup, 0);
+ fidnum = SVAL(*ppsetup, 4);
+ isFSctl = CVAL(*ppsetup, 6);
+ compfilter = CVAL(*ppsetup, 7);
+
+ DEBUG(10,("call_nt_transact_ioctl: function[0x%08X] FID[0x%04X] isFSctl[0x%02X] compfilter[0x%02X]\n",
+ function, fidnum, isFSctl, compfilter));
+
+ fsp=file_fsp(*ppsetup, 4);
+ /* this check is done in each implemented function case for now
+ because I don't want to break anything... --metze
+ FSP_BELONGS_CONN(fsp,conn);*/
+
+ switch (function) {
+ case FSCTL_SET_SPARSE:
+ /* pretend this succeeded - tho strictly we should
+ mark the file sparse (if the local fs supports it)
+ so we can know if we need to pre-allocate or not */
+
+ DEBUG(10,("FSCTL_SET_SPARSE: called on FID[0x%04X](but not implemented)\n", fidnum));
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, NULL, 0, NULL, 0);
+ return -1;
+
+ case FSCTL_0x000900C0:
+ /* pretend this succeeded - don't know what this really is
+ but works ok like this --metze
+ */
+
+ DEBUG(10,("FSCTL_0x000900C0: called on FID[0x%04X](but not implemented)\n",fidnum));
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, NULL, 0, NULL, 0);
+ return -1;
+
+ case FSCTL_GET_REPARSE_POINT:
+ /* pretend this fail - my winXP does it like this
+ * --metze
+ */
+
+ DEBUG(10,("FSCTL_GET_REPARSE_POINT: called on FID[0x%04X](but not implemented)\n",fidnum));
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_NOT_A_REPARSE_POINT, NULL, 0, NULL, 0);
+ return -1;
+
+ case FSCTL_SET_REPARSE_POINT:
+ /* pretend this fail - I'm assuming this because of the FSCTL_GET_REPARSE_POINT case.
+ * --metze
+ */
+
+ DEBUG(10,("FSCTL_SET_REPARSE_POINT: called on FID[0x%04X](but not implemented)\n",fidnum));
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_NOT_A_REPARSE_POINT, NULL, 0, NULL, 0);
+ return -1;
+
+ case FSCTL_GET_SHADOW_COPY_DATA: /* don't know if this name is right...*/
+ {
+ /*
+ * This is called to retrieve the number of Shadow Copies (a.k.a. snapshots)
+ * and return their volume names. If max_data_count is 16, then it is just
+ * asking for the number of volumes and length of the combined names.
+ *
+ * pdata is the data allocated by our caller, but that uses
+ * total_data_count (which is 0 in our case) rather than max_data_count.
+ * Allocate the correct amount and return the pointer to let
+ * it be deallocated when we return.
+ */
+ uint32 max_data_count = IVAL(inbuf,smb_nt_MaxDataCount);
+ SHADOW_COPY_DATA *shadow_data = NULL;
+ TALLOC_CTX *shadow_mem_ctx = NULL;
+ BOOL labels = False;
+ uint32 labels_data_count = 0;
+ uint32 i;
+ char *cur_pdata;
+
+ FSP_BELONGS_CONN(fsp,conn);
+
+ if (max_data_count < 16) {
+ DEBUG(0,("FSCTL_GET_SHADOW_COPY_DATA: max_data_count(%u) < 16 is invalid!\n",
+ max_data_count));
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ }
+
+ if (max_data_count > 16) {
+ labels = True;
+ }
+
+ shadow_mem_ctx = talloc_init("SHADOW_COPY_DATA");
+ if (shadow_mem_ctx == NULL) {
+ DEBUG(0,("talloc_init(SHADOW_COPY_DATA) failed!\n"));
+ return ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ shadow_data = (SHADOW_COPY_DATA *)talloc_zero(shadow_mem_ctx,sizeof(SHADOW_COPY_DATA));
+ if (shadow_data == NULL) {
+ DEBUG(0,("talloc_zero() failed!\n"));
+ return ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ shadow_data->mem_ctx = shadow_mem_ctx;
+
+ /*
+ * Call the VFS routine to actually do the work.
+ */
+ if (SMB_VFS_GET_SHADOW_COPY_DATA(fsp, shadow_data, labels)!=0) {
+ talloc_destroy(shadow_data->mem_ctx);
+ if (errno == ENOSYS) {
+ DEBUG(5,("FSCTL_GET_SHADOW_COPY_DATA: connectpath %s, not supported.\n",
+ conn->connectpath));
+ return ERROR_NT(NT_STATUS_NOT_SUPPORTED);
+ } else {
+ DEBUG(0,("FSCTL_GET_SHADOW_COPY_DATA: connectpath %s, failed.\n",
+ conn->connectpath));
+ return ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+ }
+ }
+
+ labels_data_count = (shadow_data->num_volumes*2*sizeof(SHADOW_COPY_LABEL))+2;
+
+ if (!labels) {
+ data_count = 16;
+ } else {
+ data_count = 12+labels_data_count+4;
+ }
+
+ if (max_data_count<data_count) {
+ DEBUG(0,("FSCTL_GET_SHADOW_COPY_DATA: max_data_count(%u) too small (%u) bytes needed!\n",
+ max_data_count,data_count));
+ talloc_destroy(shadow_data->mem_ctx);
+ return ERROR_NT(NT_STATUS_BUFFER_TOO_SMALL);
+ }
+
+ pdata = nttrans_realloc(ppdata, data_count);
+ if (pdata == NULL) {
+ talloc_destroy(shadow_data->mem_ctx);
+ return ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ cur_pdata = pdata;
+
+ /* num_volumes 4 bytes */
+ SIVAL(pdata,0,shadow_data->num_volumes);
+
+ if (labels) {
+ /* num_labels 4 bytes */
+ SIVAL(pdata,4,shadow_data->num_volumes);
+ }
+
+ /* needed_data_count 4 bytes */
+ SIVAL(pdata,8,labels_data_count);
+
+ cur_pdata+=12;
+
+ DEBUG(10,("FSCTL_GET_SHADOW_COPY_DATA: %u volumes for path[%s].\n",
+ shadow_data->num_volumes,fsp->fsp_name));
+ if (labels && shadow_data->labels) {
+ for (i=0;i<shadow_data->num_volumes;i++) {
+ srvstr_push(outbuf, cur_pdata, shadow_data->labels[i], 2*sizeof(SHADOW_COPY_LABEL), STR_UNICODE|STR_TERMINATE);
+ cur_pdata+=2*sizeof(SHADOW_COPY_LABEL);
+ DEBUGADD(10,("Label[%u]: '%s'\n",i,shadow_data->labels[i]));
+ }
+ }
+
+ talloc_destroy(shadow_data->mem_ctx);
+
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, NULL, 0, pdata, data_count);
+
+ return -1;
+ }
+
+ case FSCTL_FIND_FILES_BY_SID: /* I hope this name is right */
+ {
+ /* pretend this succeeded -
+ *
+ * we have to send back a list with all files owned by this SID
+ *
+ * but I have to check that --metze
+ */
+ DOM_SID sid;
+ uid_t uid;
+ size_t sid_len = MIN(data_count-4,SID_MAX_SIZE);
+
+ DEBUG(10,("FSCTL_FIND_FILES_BY_SID: called on FID[0x%04X]\n",fidnum));
+
+ FSP_BELONGS_CONN(fsp,conn);
+
+ /* unknown 4 bytes: this is not the length of the sid :-( */
+ /*unknown = IVAL(pdata,0);*/
+
+ sid_parse(pdata+4,sid_len,&sid);
+ DEBUGADD(10,("for SID: %s\n",sid_string_static(&sid)));
+
+ if (!NT_STATUS_IS_OK(sid_to_uid(&sid, &uid))) {
+ DEBUG(0,("sid_to_uid: failed, sid[%s] sid_len[%lu]\n",
+ sid_string_static(&sid),(unsigned long)sid_len));
+ uid = (-1);
+ }
+
+ /* we can take a look at the find source :-)
+ *
+ * find ./ -uid $uid -name '*' is what we need here
+ *
+ *
+ * and send 4bytes len and then NULL terminated unicode strings
+ * for each file
+ *
+ * but I don't know how to deal with the paged results
+ * (maybe we can hang the result anywhere in the fsp struct)
+ *
+ * we don't send all files at once
+ * and at the next we should *not* start from the beginning,
+ * so we have to cache the result
+ *
+ * --metze
+ */
+
+ /* this works for now... */
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, NULL, 0, NULL, 0);
+ return -1;
+ }
+ default:
+ if (!logged_message) {
+ logged_message = True; /* Only print this once... */
+ DEBUG(0,("call_nt_transact_ioctl(0x%x): Currently not implemented.\n",
+ function));
+ }
+ }
+
+ return ERROR_NT(NT_STATUS_NOT_SUPPORTED);
+}
+
+
+#ifdef HAVE_SYS_QUOTAS
+/****************************************************************************
+ Reply to get user quota
+****************************************************************************/
+
+static int call_nt_transact_get_user_quota(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
+ char **ppsetup, uint32 setup_count,
+ char **ppparams, uint32 parameter_count,
+ char **ppdata, uint32 data_count)
+{
+ NTSTATUS nt_status = NT_STATUS_OK;
+ uint32 max_data_count = IVAL(inbuf,smb_nt_MaxDataCount);
+ char *params = *ppparams;
+ char *pdata = *ppdata;
+ char *entry;
+ int data_len=0,param_len=0;
+ int qt_len=0;
+ int entry_len = 0;
+ files_struct *fsp = NULL;
+ uint16 level = 0;
+ size_t sid_len;
+ DOM_SID sid;
+ BOOL start_enum = True;
+ SMB_NTQUOTA_STRUCT qt;
+ SMB_NTQUOTA_LIST *tmp_list;
+ SMB_NTQUOTA_HANDLE *qt_handle = NULL;
+ extern struct current_user current_user;
+
+ ZERO_STRUCT(qt);
+
+ /* access check */
+ if (current_user.uid != 0) {
+ DEBUG(1,("set_user_quota: access_denied service [%s] user [%s]\n",
+ lp_servicename(SNUM(conn)),conn->user));
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ /*
+ * Ensure minimum number of parameters sent.
+ */
+
+ if (parameter_count < 4) {
+ DEBUG(0,("TRANSACT_GET_USER_QUOTA: requires %d >= 4 bytes parameters\n",parameter_count));
+ return ERROR_DOS(ERRDOS,ERRinvalidparam);
+ }
+
+ /* maybe we can check the quota_fnum */
+ fsp = file_fsp(params,0);
+ if (!CHECK_NTQUOTA_HANDLE_OK(fsp,conn)) {
+ DEBUG(3,("TRANSACT_GET_USER_QUOTA: no valid QUOTA HANDLE\n"));
+ return ERROR_NT(NT_STATUS_INVALID_HANDLE);
+ }
+
+ /* the NULL pointer cheking for fsp->fake_file_handle->pd
+ * is done by CHECK_NTQUOTA_HANDLE_OK()
+ */
+ qt_handle = (SMB_NTQUOTA_HANDLE *)fsp->fake_file_handle->pd;
+
+ level = SVAL(params,2);
+
+ /* unknown 12 bytes leading in params */
+
+ switch (level) {
+ case TRANSACT_GET_USER_QUOTA_LIST_CONTINUE:
+ /* seems that we should continue with the enum here --metze */
+
+ if (qt_handle->quota_list!=NULL &&
+ qt_handle->tmp_list==NULL) {
+
+ /* free the list */
+ free_ntquota_list(&(qt_handle->quota_list));
+
+ /* Realloc the size of parameters and data we will return */
+ param_len = 4;
+ params = nttrans_realloc(ppparams, param_len);
+ if(params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+
+ data_len = 0;
+ SIVAL(params,0,data_len);
+
+ break;
+ }
+
+ start_enum = False;
+
+ case TRANSACT_GET_USER_QUOTA_LIST_START:
+
+ if (qt_handle->quota_list==NULL &&
+ qt_handle->tmp_list==NULL) {
+ start_enum = True;
+ }
+
+ if (start_enum && vfs_get_user_ntquota_list(fsp,&(qt_handle->quota_list))!=0)
+ return ERROR_DOS(ERRSRV,ERRerror);
+
+ /* Realloc the size of parameters and data we will return */
+ param_len = 4;
+ params = nttrans_realloc(ppparams, param_len);
+ if(params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+
+ /* we should not trust the value in max_data_count*/
+ max_data_count = MIN(max_data_count,2048);
+
+ pdata = nttrans_realloc(ppdata, max_data_count);/* should be max data count from client*/
+ if(pdata == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+
+ entry = pdata;
+
+
+ /* set params Size of returned Quota Data 4 bytes*/
+ /* but set it later when we know it */
+
+ /* for each entry push the data */
+
+ if (start_enum) {
+ qt_handle->tmp_list = qt_handle->quota_list;
+ }
+
+ tmp_list = qt_handle->tmp_list;
+
+ for (;((tmp_list!=NULL)&&((qt_len +40+SID_MAX_SIZE)<max_data_count));
+ tmp_list=tmp_list->next,entry+=entry_len,qt_len+=entry_len) {
+
+ sid_len = sid_size(&tmp_list->quotas->sid);
+ entry_len = 40 + sid_len;
+
+ /* nextoffset entry 4 bytes */
+ SIVAL(entry,0,entry_len);
+
+ /* then the len of the SID 4 bytes */
+ SIVAL(entry,4,sid_len);
+
+ /* unknown data 8 bytes SMB_BIG_UINT */
+ SBIG_UINT(entry,8,(SMB_BIG_UINT)0); /* this is not 0 in windows...-metze*/
+
+ /* the used disk space 8 bytes SMB_BIG_UINT */
+ SBIG_UINT(entry,16,tmp_list->quotas->usedspace);
+
+ /* the soft quotas 8 bytes SMB_BIG_UINT */
+ SBIG_UINT(entry,24,tmp_list->quotas->softlim);
+
+ /* the hard quotas 8 bytes SMB_BIG_UINT */
+ SBIG_UINT(entry,32,tmp_list->quotas->hardlim);
+
+ /* and now the SID */
+ sid_linearize(entry+40, sid_len, &tmp_list->quotas->sid);
+ }
+
+ qt_handle->tmp_list = tmp_list;
+
+ /* overwrite the offset of the last entry */
+ SIVAL(entry-entry_len,0,0);
+
+ data_len = 4+qt_len;
+ /* overwrite the params quota_data_len */
+ SIVAL(params,0,data_len);
+
+ break;
+
+ case TRANSACT_GET_USER_QUOTA_FOR_SID:
+
+ /* unknown 4 bytes IVAL(pdata,0) */
+
+ if (data_count < 8) {
+ DEBUG(0,("TRANSACT_GET_USER_QUOTA_FOR_SID: requires %d >= %d bytes data\n",data_count,8));
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+
+ sid_len = IVAL(pdata,4);
+
+ if (data_count < 8+sid_len) {
+ DEBUG(0,("TRANSACT_GET_USER_QUOTA_FOR_SID: requires %d >= %lu bytes data\n",data_count,(unsigned long)(8+sid_len)));
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+
+ data_len = 4+40+sid_len;
+
+ if (max_data_count < data_len) {
+ DEBUG(0,("TRANSACT_GET_USER_QUOTA_FOR_SID: max_data_count(%d) < data_len(%d)\n",
+ max_data_count, data_len));
+ param_len = 4;
+ SIVAL(params,0,data_len);
+ data_len = 0;
+ nt_status = NT_STATUS_BUFFER_TOO_SMALL;
+ break;
+ }
+
+ sid_parse(pdata+8,sid_len,&sid);
+
+
+ if (vfs_get_ntquota(fsp, SMB_USER_QUOTA_TYPE, &sid, &qt)!=0) {
+ ZERO_STRUCT(qt);
+ /*
+ * we have to return zero's in all fields
+ * instead of returning an error here
+ * --metze
+ */
+ }
+
+ /* Realloc the size of parameters and data we will return */
+ param_len = 4;
+ params = nttrans_realloc(ppparams, param_len);
+ if(params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+
+ pdata = nttrans_realloc(ppdata, data_len);
+ if(pdata == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+
+ entry = pdata;
+
+ /* set params Size of returned Quota Data 4 bytes*/
+ SIVAL(params,0,data_len);
+
+ /* nextoffset entry 4 bytes */
+ SIVAL(entry,0,0);
+
+ /* then the len of the SID 4 bytes */
+ SIVAL(entry,4,sid_len);
+
+ /* unknown data 8 bytes SMB_BIG_UINT */
+ SBIG_UINT(entry,8,(SMB_BIG_UINT)0); /* this is not 0 in windows...-mezte*/
+
+ /* the used disk space 8 bytes SMB_BIG_UINT */
+ SBIG_UINT(entry,16,qt.usedspace);
+
+ /* the soft quotas 8 bytes SMB_BIG_UINT */
+ SBIG_UINT(entry,24,qt.softlim);
+
+ /* the hard quotas 8 bytes SMB_BIG_UINT */
+ SBIG_UINT(entry,32,qt.hardlim);
+
+ /* and now the SID */
+ sid_linearize(entry+40, sid_len, &sid);
+
+ break;
+
+ default:
+ DEBUG(0,("do_nt_transact_get_user_quota: fnum %d unknown level 0x%04hX\n",fsp->fnum,level));
+ return ERROR_DOS(ERRSRV,ERRerror);
+ break;
+ }
+
+ send_nt_replies(inbuf, outbuf, bufsize, nt_status, params, param_len, pdata, data_len);
+
+ return -1;
+}
+
+/****************************************************************************
+ Reply to set user quota
+****************************************************************************/
+
+static int call_nt_transact_set_user_quota(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
+ char **ppsetup, uint32 setup_count,
+ char **ppparams, uint32 parameter_count,
+ char **ppdata, uint32 data_count)
+{
+ char *params = *ppparams;
+ char *pdata = *ppdata;
+ int data_len=0,param_len=0;
+ SMB_NTQUOTA_STRUCT qt;
+ size_t sid_len;
+ DOM_SID sid;
+ files_struct *fsp = NULL;
+
+ ZERO_STRUCT(qt);
+
+ /* access check */
+ if (conn->admin_user != True) {
+ DEBUG(1,("set_user_quota: access_denied service [%s] user [%s]\n",
+ lp_servicename(SNUM(conn)),conn->user));
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ /*
+ * Ensure minimum number of parameters sent.
+ */
+
+ if (parameter_count < 2) {
+ DEBUG(0,("TRANSACT_SET_USER_QUOTA: requires %d >= 2 bytes parameters\n",parameter_count));
+ return ERROR_DOS(ERRDOS,ERRinvalidparam);
+ }
+
+ /* maybe we can check the quota_fnum */
+ fsp = file_fsp(params,0);
+ if (!CHECK_NTQUOTA_HANDLE_OK(fsp,conn)) {
+ DEBUG(3,("TRANSACT_GET_USER_QUOTA: no valid QUOTA HANDLE\n"));
+ return ERROR_NT(NT_STATUS_INVALID_HANDLE);
+ }
+
+ if (data_count < 40) {
+ DEBUG(0,("TRANSACT_SET_USER_QUOTA: requires %d >= %d bytes data\n",data_count,40));
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+
+ /* offset to next quota record.
+ * 4 bytes IVAL(pdata,0)
+ * unused here...
+ */
+
+ /* sid len */
+ sid_len = IVAL(pdata,4);
+
+ if (data_count < 40+sid_len) {
+ DEBUG(0,("TRANSACT_SET_USER_QUOTA: requires %d >= %lu bytes data\n",data_count,(unsigned long)40+sid_len));
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+
+ /* unknown 8 bytes in pdata
+ * maybe its the change time in NTTIME
+ */
+
+ /* the used space 8 bytes (SMB_BIG_UINT)*/
+ qt.usedspace = (SMB_BIG_UINT)IVAL(pdata,16);
+#ifdef LARGE_SMB_OFF_T
+ qt.usedspace |= (((SMB_BIG_UINT)IVAL(pdata,20)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if ((IVAL(pdata,20) != 0)&&
+ ((qt.usedspace != 0xFFFFFFFF)||
+ (IVAL(pdata,20)!=0xFFFFFFFF))) {
+ /* more than 32 bits? */
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+#endif /* LARGE_SMB_OFF_T */
+
+ /* the soft quotas 8 bytes (SMB_BIG_UINT)*/
+ qt.softlim = (SMB_BIG_UINT)IVAL(pdata,24);
+#ifdef LARGE_SMB_OFF_T
+ qt.softlim |= (((SMB_BIG_UINT)IVAL(pdata,28)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if ((IVAL(pdata,28) != 0)&&
+ ((qt.softlim != 0xFFFFFFFF)||
+ (IVAL(pdata,28)!=0xFFFFFFFF))) {
+ /* more than 32 bits? */
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+#endif /* LARGE_SMB_OFF_T */
+
+ /* the hard quotas 8 bytes (SMB_BIG_UINT)*/
+ qt.hardlim = (SMB_BIG_UINT)IVAL(pdata,32);
+#ifdef LARGE_SMB_OFF_T
+ qt.hardlim |= (((SMB_BIG_UINT)IVAL(pdata,36)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if ((IVAL(pdata,36) != 0)&&
+ ((qt.hardlim != 0xFFFFFFFF)||
+ (IVAL(pdata,36)!=0xFFFFFFFF))) {
+ /* more than 32 bits? */
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+#endif /* LARGE_SMB_OFF_T */
+
+ sid_parse(pdata+40,sid_len,&sid);
+ DEBUGADD(8,("SID: %s\n",sid_string_static(&sid)));
+
+ /* 44 unknown bytes left... */
+
+ if (vfs_set_ntquota(fsp, SMB_USER_QUOTA_TYPE, &sid, &qt)!=0) {
+ return ERROR_DOS(ERRSRV,ERRerror);
+ }
+
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, params, param_len, pdata, data_len);
+
+ return -1;
+}
+#endif /* HAVE_SYS_QUOTAS */
+
+/****************************************************************************
+ Reply to a SMBNTtrans.
+****************************************************************************/
+
+int reply_nttrans(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ int outsize = 0;
+#if 0 /* Not used. */
+ uint16 max_setup_count = CVAL(inbuf, smb_nt_MaxSetupCount);
+ uint32 max_parameter_count = IVAL(inbuf, smb_nt_MaxParameterCount);
+ uint32 max_data_count = IVAL(inbuf,smb_nt_MaxDataCount);
+#endif /* Not used. */
+ uint32 total_parameter_count = IVAL(inbuf, smb_nt_TotalParameterCount);
+ uint32 total_data_count = IVAL(inbuf, smb_nt_TotalDataCount);
+ uint32 parameter_count = IVAL(inbuf,smb_nt_ParameterCount);
+ uint32 parameter_offset = IVAL(inbuf,smb_nt_ParameterOffset);
+ uint32 data_count = IVAL(inbuf,smb_nt_DataCount);
+ uint32 data_offset = IVAL(inbuf,smb_nt_DataOffset);
+ uint16 setup_count = 2*CVAL(inbuf,smb_nt_SetupCount); /* setup count is in *words* */
+ uint16 function_code = SVAL( inbuf, smb_nt_Function);
+ char *params = NULL, *data = NULL, *setup = NULL;
+ uint32 num_params_sofar, num_data_sofar;
+ START_PROFILE(SMBnttrans);
+
+ if(global_oplock_break &&
+ ((function_code == NT_TRANSACT_CREATE) ||
+ (function_code == NT_TRANSACT_RENAME))) {
+ /*
+ * Queue this open message as we are the process of an oplock break.
+ */
+
+ DEBUG(2,("reply_nttrans: queueing message code 0x%x \
+due to being in oplock break state.\n", (unsigned int)function_code ));
+
+ push_oplock_pending_smb_message( inbuf, length);
+ END_PROFILE(SMBnttrans);
+ return -1;
+ }
+
+ if (IS_IPC(conn) && (function_code != NT_TRANSACT_CREATE)) {
+ END_PROFILE(SMBnttrans);
+ return ERROR_DOS(ERRSRV,ERRaccess);
+ }
+
+ outsize = set_message(outbuf,0,0,True);
+
+ /*
+ * All nttrans messages we handle have smb_wct == 19 + setup_count.
+ * Ensure this is so as a sanity check.
+ */
+
+ if(CVAL(inbuf, smb_wct) != 19 + (setup_count/2)) {
+ DEBUG(2,("Invalid smb_wct %d in nttrans call (should be %d)\n",
+ CVAL(inbuf, smb_wct), 19 + (setup_count/2)));
+ goto bad_param;
+ }
+
+ /* Allocate the space for the setup, the maximum needed parameters and data */
+
+ if(setup_count > 0)
+ setup = (char *)malloc(setup_count);
+ if (total_parameter_count > 0)
+ params = (char *)malloc(total_parameter_count);
+ if (total_data_count > 0)
+ data = (char *)malloc(total_data_count);
+
+ if ((total_parameter_count && !params) || (total_data_count && !data) ||
+ (setup_count && !setup)) {
+ SAFE_FREE(setup);
+ SAFE_FREE(params);
+ SAFE_FREE(data);
+ DEBUG(0,("reply_nttrans : Out of memory\n"));
+ END_PROFILE(SMBnttrans);
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ }
+
+ /* Copy the param and data bytes sent with this request into the params buffer */
+ num_params_sofar = parameter_count;
+ num_data_sofar = data_count;
+
+ if (parameter_count > total_parameter_count || data_count > total_data_count)
+ goto bad_param;
+
+ if(setup) {
+ DEBUG(10,("reply_nttrans: setup_count = %d\n", setup_count));
+ if ((smb_nt_SetupStart + setup_count < smb_nt_SetupStart) ||
+ (smb_nt_SetupStart + setup_count < setup_count))
+ goto bad_param;
+ if (smb_nt_SetupStart + setup_count > length)
+ goto bad_param;
+
+ memcpy( setup, &inbuf[smb_nt_SetupStart], setup_count);
+ dump_data(10, setup, setup_count);
+ }
+ if(params) {
+ DEBUG(10,("reply_nttrans: parameter_count = %d\n", parameter_count));
+ if ((parameter_offset + parameter_count < parameter_offset) ||
+ (parameter_offset + parameter_count < parameter_count))
+ goto bad_param;
+ if ((smb_base(inbuf) + parameter_offset + parameter_count > inbuf + length)||
+ (smb_base(inbuf) + parameter_offset + parameter_count < smb_base(inbuf)))
+ goto bad_param;
+
+ memcpy( params, smb_base(inbuf) + parameter_offset, parameter_count);
+ dump_data(10, params, parameter_count);
+ }
+ if(data) {
+ DEBUG(10,("reply_nttrans: data_count = %d\n",data_count));
+ if ((data_offset + data_count < data_offset) || (data_offset + data_count < data_count))
+ goto bad_param;
+ if ((smb_base(inbuf) + data_offset + data_count > inbuf + length) ||
+ (smb_base(inbuf) + data_offset + data_count < smb_base(inbuf)))
+ goto bad_param;
+
+ memcpy( data, smb_base(inbuf) + data_offset, data_count);
+ dump_data(10, data, data_count);
+ }
+
+ srv_signing_trans_start(SVAL(inbuf,smb_mid));
+
+ if(num_data_sofar < total_data_count || num_params_sofar < total_parameter_count) {
+ /* We need to send an interim response then receive the rest
+ of the parameter/data bytes */
+ outsize = set_message(outbuf,0,0,True);
+ srv_signing_trans_stop();
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("reply_nttrans: send_smb failed.");
+
+ while( num_data_sofar < total_data_count || num_params_sofar < total_parameter_count) {
+ BOOL ret;
+ uint32 parameter_displacement;
+ uint32 data_displacement;
+
+ ret = receive_next_smb(inbuf,bufsize,SMB_SECONDARY_WAIT);
+
+ /*
+ * The sequence number for the trans reply is always
+ * based on the last secondary received.
+ */
+
+ srv_signing_trans_start(SVAL(inbuf,smb_mid));
+
+ if((ret && (CVAL(inbuf, smb_com) != SMBnttranss)) || !ret) {
+ outsize = set_message(outbuf,0,0,True);
+ if(ret) {
+ DEBUG(0,("reply_nttrans: Invalid secondary nttrans packet\n"));
+ } else {
+ DEBUG(0,("reply_nttrans: %s in getting secondary nttrans response.\n",
+ (smb_read_error == READ_ERROR) ? "error" : "timeout" ));
+ }
+ goto bad_param;
+ }
+
+ /* Revise total_params and total_data in case they have changed downwards */
+ if (IVAL(inbuf, smb_nts_TotalParameterCount) < total_parameter_count)
+ total_parameter_count = IVAL(inbuf, smb_nts_TotalParameterCount);
+ if (IVAL(inbuf, smb_nts_TotalDataCount) < total_data_count)
+ total_data_count = IVAL(inbuf, smb_nts_TotalDataCount);
+
+ parameter_count = IVAL(inbuf,smb_nts_ParameterCount);
+ parameter_offset = IVAL(inbuf, smb_nts_ParameterOffset);
+ parameter_displacement = IVAL(inbuf, smb_nts_ParameterDisplacement);
+ num_params_sofar += parameter_count;
+
+ data_count = IVAL(inbuf, smb_nts_DataCount);
+ data_displacement = IVAL(inbuf, smb_nts_DataDisplacement);
+ data_offset = IVAL(inbuf, smb_nts_DataOffset);
+ num_data_sofar += data_count;
+
+ if (num_params_sofar > total_parameter_count || num_data_sofar > total_data_count) {
+ DEBUG(0,("reply_nttrans2: data overflow in secondary nttrans packet"));
+ goto bad_param;
+ }
+
+ if (parameter_count) {
+ if (parameter_displacement + parameter_count >= total_parameter_count)
+ goto bad_param;
+ if ((parameter_displacement + parameter_count < parameter_displacement) ||
+ (parameter_displacement + parameter_count < parameter_count))
+ goto bad_param;
+ if (parameter_displacement > total_parameter_count)
+ goto bad_param;
+ if ((smb_base(inbuf) + parameter_offset + parameter_count >= inbuf + bufsize) ||
+ (smb_base(inbuf) + parameter_offset + parameter_count < smb_base(inbuf)))
+ goto bad_param;
+ if (parameter_displacement + params < params)
+ goto bad_param;
+
+ memcpy( &params[parameter_displacement], smb_base(inbuf) + parameter_offset, parameter_count);
+ }
+
+ if (data_count) {
+ if (data_displacement + data_count >= total_data_count)
+ goto bad_param;
+ if ((data_displacement + data_count < data_displacement) ||
+ (data_displacement + data_count < data_count))
+ goto bad_param;
+ if (data_displacement > total_data_count)
+ goto bad_param;
+ if ((smb_base(inbuf) + data_offset + data_count >= inbuf + bufsize) ||
+ (smb_base(inbuf) + data_offset + data_count < smb_base(inbuf)))
+ goto bad_param;
+ if (data_displacement + data < data)
+ goto bad_param;
+
+ memcpy( &data[data_displacement], smb_base(inbuf)+ data_offset, data_count);
+ }
+ }
+ }
+
+ if (Protocol >= PROTOCOL_NT1)
+ SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2) | FLAGS2_IS_LONG_NAME);
+
+ /* Now we must call the relevant NT_TRANS function */
+ switch(function_code) {
+ case NT_TRANSACT_CREATE:
+ START_PROFILE_NESTED(NT_transact_create);
+ outsize = call_nt_transact_create(conn, inbuf, outbuf,
+ length, bufsize,
+ &setup, setup_count,
+ &params, total_parameter_count,
+ &data, total_data_count);
+ END_PROFILE_NESTED(NT_transact_create);
+ break;
+ case NT_TRANSACT_IOCTL:
+ START_PROFILE_NESTED(NT_transact_ioctl);
+ outsize = call_nt_transact_ioctl(conn, inbuf, outbuf,
+ length, bufsize,
+ &setup, setup_count,
+ &params, total_parameter_count,
+ &data, total_data_count);
+ END_PROFILE_NESTED(NT_transact_ioctl);
+ break;
+ case NT_TRANSACT_SET_SECURITY_DESC:
+ START_PROFILE_NESTED(NT_transact_set_security_desc);
+ outsize = call_nt_transact_set_security_desc(conn, inbuf, outbuf,
+ length, bufsize,
+ &setup, setup_count,
+ &params, total_parameter_count,
+ &data, total_data_count);
+ END_PROFILE_NESTED(NT_transact_set_security_desc);
+ break;
+ case NT_TRANSACT_NOTIFY_CHANGE:
+ START_PROFILE_NESTED(NT_transact_notify_change);
+ outsize = call_nt_transact_notify_change(conn, inbuf, outbuf,
+ length, bufsize,
+ &setup, setup_count,
+ &params, total_parameter_count,
+ &data, total_data_count);
+ END_PROFILE_NESTED(NT_transact_notify_change);
+ break;
+ case NT_TRANSACT_RENAME:
+ START_PROFILE_NESTED(NT_transact_rename);
+ outsize = call_nt_transact_rename(conn, inbuf, outbuf,
+ length, bufsize,
+ &setup, setup_count,
+ &params, total_parameter_count,
+ &data, total_data_count);
+ END_PROFILE_NESTED(NT_transact_rename);
+ break;
+
+ case NT_TRANSACT_QUERY_SECURITY_DESC:
+ START_PROFILE_NESTED(NT_transact_query_security_desc);
+ outsize = call_nt_transact_query_security_desc(conn, inbuf, outbuf,
+ length, bufsize,
+ &setup, setup_count,
+ &params, total_parameter_count,
+ &data, total_data_count);
+ END_PROFILE_NESTED(NT_transact_query_security_desc);
+ break;
+#ifdef HAVE_SYS_QUOTAS
+ case NT_TRANSACT_GET_USER_QUOTA:
+ START_PROFILE_NESTED(NT_transact_get_user_quota);
+ outsize = call_nt_transact_get_user_quota(conn, inbuf, outbuf,
+ length, bufsize,
+ &setup, setup_count,
+ &params, total_parameter_count,
+ &data, total_data_count);
+ END_PROFILE_NESTED(NT_transact_get_user_quota);
+ break;
+ case NT_TRANSACT_SET_USER_QUOTA:
+ START_PROFILE_NESTED(NT_transact_set_user_quota);
+ outsize = call_nt_transact_set_user_quota(conn, inbuf, outbuf,
+ length, bufsize,
+ &setup, setup_count,
+ &params, total_parameter_count,
+ &data, total_data_count);
+ END_PROFILE_NESTED(NT_transact_set_user_quota);
+ break;
+#endif /* HAVE_SYS_QUOTAS */
+ default:
+ /* Error in request */
+ DEBUG(0,("reply_nttrans: Unknown request %d in nttrans call\n", function_code));
+ SAFE_FREE(setup);
+ SAFE_FREE(params);
+ SAFE_FREE(data);
+ END_PROFILE(SMBnttrans);
+ srv_signing_trans_stop();
+ return ERROR_DOS(ERRSRV,ERRerror);
+ }
+
+ /* As we do not know how many data packets will need to be
+ returned here the various call_nt_transact_xxxx calls
+ must send their own. Thus a call_nt_transact_xxxx routine only
+ returns a value other than -1 when it wants to send
+ an error packet.
+ */
+
+ srv_signing_trans_stop();
+
+ SAFE_FREE(setup);
+ SAFE_FREE(params);
+ SAFE_FREE(data);
+ END_PROFILE(SMBnttrans);
+ return outsize; /* If a correct response was needed the call_nt_transact_xxxx
+ calls have already sent it. If outsize != -1 then it is
+ returning an error packet. */
+
+ bad_param:
+
+ srv_signing_trans_stop();
+ SAFE_FREE(params);
+ SAFE_FREE(data);
+ SAFE_FREE(setup);
+ END_PROFILE(SMBnttrans);
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+}
diff --git a/source/smbd/open.c b/source/smbd/open.c
new file mode 100644
index 00000000000..8ab5dab6ac9
--- /dev/null
+++ b/source/smbd/open.c
@@ -0,0 +1,1462 @@
+/*
+ Unix SMB/CIFS implementation.
+ file opening and share modes
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Jeremy Allison 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+extern userdom_struct current_user_info;
+extern uint16 global_oplock_port;
+extern uint16 global_smbpid;
+extern BOOL global_client_failed_oplock_break;
+
+/****************************************************************************
+ fd support routines - attempt to do a dos_open.
+****************************************************************************/
+
+static int fd_open(struct connection_struct *conn, const char *fname,
+ int flags, mode_t mode)
+{
+ int fd;
+#ifdef O_NOFOLLOW
+ if (!lp_symlinks(SNUM(conn)))
+ flags |= O_NOFOLLOW;
+#endif
+
+ fd = SMB_VFS_OPEN(conn,fname,flags,mode);
+
+ DEBUG(10,("fd_open: name %s, flags = 0%o mode = 0%o, fd = %d. %s\n", fname,
+ flags, (int)mode, fd, (fd == -1) ? strerror(errno) : "" ));
+
+ return fd;
+}
+
+/****************************************************************************
+ Close the file associated with a fsp.
+****************************************************************************/
+
+int fd_close(struct connection_struct *conn, files_struct *fsp)
+{
+ if (fsp->fd == -1)
+ return 0; /* what we used to call a stat open. */
+ return fd_close_posix(conn, fsp);
+}
+
+
+/****************************************************************************
+ Check a filename for the pipe string.
+****************************************************************************/
+
+static void check_for_pipe(const char *fname)
+{
+ /* special case of pipe opens */
+ char s[10];
+ StrnCpy(s,fname,sizeof(s)-1);
+ strlower_m(s);
+ if (strstr(s,"pipe/")) {
+ DEBUG(3,("Rejecting named pipe open for %s\n",fname));
+ unix_ERR_class = ERRSRV;
+ unix_ERR_code = ERRaccess;
+ unix_ERR_ntstatus = NT_STATUS_ACCESS_DENIED;
+ }
+}
+
+/****************************************************************************
+ Open a file.
+****************************************************************************/
+
+static BOOL open_file(files_struct *fsp,connection_struct *conn,
+ const char *fname,SMB_STRUCT_STAT *psbuf,int flags,mode_t mode, uint32 desired_access)
+{
+ extern struct current_user current_user;
+ int accmode = (flags & O_ACCMODE);
+ int local_flags = flags;
+
+ fsp->fd = -1;
+ fsp->oplock_type = NO_OPLOCK;
+ errno = EPERM;
+
+ /* Check permissions */
+
+ /*
+ * This code was changed after seeing a client open request
+ * containing the open mode of (DENY_WRITE/read-only) with
+ * the 'create if not exist' bit set. The previous code
+ * would fail to open the file read only on a read-only share
+ * as it was checking the flags parameter directly against O_RDONLY,
+ * this was failing as the flags parameter was set to O_RDONLY|O_CREAT.
+ * JRA.
+ */
+
+ if (!CAN_WRITE(conn)) {
+ /* It's a read-only share - fail if we wanted to write. */
+ if(accmode != O_RDONLY) {
+ DEBUG(3,("Permission denied opening %s\n",fname));
+ check_for_pipe(fname);
+ return False;
+ } else if(flags & O_CREAT) {
+ /* We don't want to write - but we must make sure that O_CREAT
+ doesn't create the file if we have write access into the
+ directory.
+ */
+ flags &= ~O_CREAT;
+ local_flags &= ~O_CREAT;
+ }
+ }
+
+ /*
+ * This little piece of insanity is inspired by the
+ * fact that an NT client can open a file for O_RDONLY,
+ * but set the create disposition to FILE_EXISTS_TRUNCATE.
+ * If the client *can* write to the file, then it expects to
+ * truncate the file, even though it is opening for readonly.
+ * Quicken uses this stupid trick in backup file creation...
+ * Thanks *greatly* to "David W. Chapman Jr." <dwcjr@inethouston.net>
+ * for helping track this one down. It didn't bite us in 2.0.x
+ * as we always opened files read-write in that release. JRA.
+ */
+
+ if ((accmode == O_RDONLY) && ((flags & O_TRUNC) == O_TRUNC)) {
+ DEBUG(10,("open_file: truncate requested on read-only open for file %s\n",fname ));
+ local_flags = (flags & ~O_ACCMODE)|O_RDWR;
+ }
+
+ if ((desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) ||
+ (local_flags & O_CREAT) || ((local_flags & O_TRUNC) == O_TRUNC) ) {
+
+ /*
+ * We can't actually truncate here as the file may be locked.
+ * open_file_shared will take care of the truncate later. JRA.
+ */
+
+ local_flags &= ~O_TRUNC;
+
+#if defined(O_NONBLOCK) && defined(S_ISFIFO)
+ /*
+ * We would block on opening a FIFO with no one else on the
+ * other end. Do what we used to do and add O_NONBLOCK to the
+ * open flags. JRA.
+ */
+
+ if (VALID_STAT(*psbuf) && S_ISFIFO(psbuf->st_mode))
+ local_flags |= O_NONBLOCK;
+#endif
+
+ /* Don't create files with Microsoft wildcard characters. */
+ if ((local_flags & O_CREAT) && !VALID_STAT(*psbuf) && ms_has_wild(fname)) {
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRinvalidname;
+ unix_ERR_ntstatus = NT_STATUS_OBJECT_NAME_INVALID;
+ return False;
+ }
+
+ /* Actually do the open */
+ fsp->fd = fd_open(conn, fname, local_flags, mode);
+ if (fsp->fd == -1) {
+ DEBUG(3,("Error opening file %s (%s) (local_flags=%d) (flags=%d)\n",
+ fname,strerror(errno),local_flags,flags));
+ check_for_pipe(fname);
+ return False;
+ }
+
+ /* Inherit the ACL if the file was created. */
+ if ((local_flags & O_CREAT) && !VALID_STAT(*psbuf))
+ inherit_access_acl(conn, fname, mode);
+
+ } else
+ fsp->fd = -1; /* What we used to call a stat open. */
+
+ if (!VALID_STAT(*psbuf)) {
+ int ret;
+
+ if (fsp->fd == -1)
+ ret = SMB_VFS_STAT(conn, fname, psbuf);
+ else {
+ ret = SMB_VFS_FSTAT(fsp,fsp->fd,psbuf);
+ /* If we have an fd, this stat should succeed. */
+ if (ret == -1)
+ DEBUG(0,("Error doing fstat on open file %s (%s)\n", fname,strerror(errno) ));
+ }
+
+ /* For a non-io open, this stat failing means file not found. JRA */
+ if (ret == -1) {
+ fd_close(conn, fsp);
+ return False;
+ }
+ }
+
+ /*
+ * POSIX allows read-only opens of directories. We don't
+ * want to do this (we use a different code path for this)
+ * so catch a directory open and return an EISDIR. JRA.
+ */
+
+ if(S_ISDIR(psbuf->st_mode)) {
+ fd_close(conn, fsp);
+ errno = EISDIR;
+ return False;
+ }
+
+ fsp->mode = psbuf->st_mode;
+ fsp->inode = psbuf->st_ino;
+ fsp->dev = psbuf->st_dev;
+ fsp->vuid = current_user.vuid;
+ fsp->file_pid = global_smbpid;
+ fsp->size = psbuf->st_size;
+ fsp->can_lock = True;
+ fsp->can_read = ((flags & O_WRONLY)==0);
+ fsp->can_write = ((flags & (O_WRONLY|O_RDWR))!=0);
+ fsp->share_mode = 0;
+ fsp->desired_access = desired_access;
+ fsp->print_file = False;
+ fsp->modified = False;
+ fsp->oplock_type = NO_OPLOCK;
+ fsp->sent_oplock_break = NO_BREAK_SENT;
+ fsp->is_directory = False;
+ fsp->is_stat = False;
+ fsp->directory_delete_on_close = False;
+ string_set(&fsp->fsp_name,fname);
+ fsp->wcp = NULL; /* Write cache pointer. */
+
+ DEBUG(2,("%s opened file %s read=%s write=%s (numopen=%d)\n",
+ *current_user_info.smb_name ? current_user_info.smb_name : conn->user,fsp->fsp_name,
+ BOOLSTR(fsp->can_read), BOOLSTR(fsp->can_write),
+ conn->num_files_open + 1));
+
+ return True;
+}
+
+/****************************************************************************
+ C. Hoch 11/22/95
+ Helper for open_file_shared.
+ Truncate a file after checking locking; close file if locked.
+ **************************************************************************/
+
+static int truncate_unless_locked(struct connection_struct *conn, files_struct *fsp)
+{
+ SMB_BIG_UINT mask = (SMB_BIG_UINT)-1;
+
+ if (is_locked(fsp,fsp->conn,mask,0,WRITE_LOCK,True)){
+ errno = EACCES;
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRlock;
+ unix_ERR_ntstatus = dos_to_ntstatus(ERRDOS, ERRlock);
+ return -1;
+ } else {
+ return SMB_VFS_FTRUNCATE(fsp,fsp->fd,0);
+ }
+}
+
+/*******************************************************************
+return True if the filename is one of the special executable types
+********************************************************************/
+static BOOL is_executable(const char *fname)
+{
+ if ((fname = strrchr_m(fname,'.'))) {
+ if (strequal(fname,".com") ||
+ strequal(fname,".dll") ||
+ strequal(fname,".exe") ||
+ strequal(fname,".sym")) {
+ return True;
+ }
+ }
+ return False;
+}
+
+enum {AFAIL,AREAD,AWRITE,AALL};
+
+/*******************************************************************
+reproduce the share mode access table
+this is horrendoously complex, and really can't be justified on any
+rational grounds except that this is _exactly_ what NT does. See
+the DENY1 and DENY2 tests in smbtorture for a comprehensive set of
+test routines.
+********************************************************************/
+static int access_table(int new_deny,int old_deny,int old_mode,
+ BOOL same_pid, BOOL isexe)
+{
+ if (new_deny == DENY_ALL || old_deny == DENY_ALL) return(AFAIL);
+
+ if (same_pid) {
+ if (isexe && old_mode == DOS_OPEN_RDONLY &&
+ old_deny == DENY_DOS && new_deny == DENY_READ) {
+ return AFAIL;
+ }
+ if (!isexe && old_mode == DOS_OPEN_RDONLY &&
+ old_deny == DENY_DOS && new_deny == DENY_DOS) {
+ return AREAD;
+ }
+ if (new_deny == DENY_FCB && old_deny == DENY_DOS) {
+ if (isexe) return AFAIL;
+ if (old_mode == DOS_OPEN_RDONLY) return AFAIL;
+ return AALL;
+ }
+ if (old_mode == DOS_OPEN_RDONLY && old_deny == DENY_DOS) {
+ if (new_deny == DENY_FCB || new_deny == DENY_READ) {
+ if (isexe) return AREAD;
+ return AFAIL;
+ }
+ }
+ if (old_deny == DENY_FCB) {
+ if (new_deny == DENY_DOS || new_deny == DENY_FCB) return AALL;
+ return AFAIL;
+ }
+ }
+
+ if (old_deny == DENY_DOS || new_deny == DENY_DOS ||
+ old_deny == DENY_FCB || new_deny == DENY_FCB) {
+ if (isexe) {
+ if (old_deny == DENY_FCB || new_deny == DENY_FCB) {
+ return AFAIL;
+ }
+ if (old_deny == DENY_DOS) {
+ if (new_deny == DENY_READ &&
+ (old_mode == DOS_OPEN_RDONLY ||
+ old_mode == DOS_OPEN_RDWR)) {
+ return AFAIL;
+ }
+ if (new_deny == DENY_WRITE &&
+ (old_mode == DOS_OPEN_WRONLY ||
+ old_mode == DOS_OPEN_RDWR)) {
+ return AFAIL;
+ }
+ return AALL;
+ }
+ if (old_deny == DENY_NONE) return AALL;
+ if (old_deny == DENY_READ) return AWRITE;
+ if (old_deny == DENY_WRITE) return AREAD;
+ }
+ /* it isn't a exe, dll, sym or com file */
+ if (old_deny == new_deny && same_pid)
+ return(AALL);
+
+ if (old_deny == DENY_READ || new_deny == DENY_READ) return AFAIL;
+ if (old_mode == DOS_OPEN_RDONLY) return(AREAD);
+
+ return(AFAIL);
+ }
+
+ switch (new_deny)
+ {
+ case DENY_WRITE:
+ if (old_deny==DENY_WRITE && old_mode==DOS_OPEN_RDONLY) return(AREAD);
+ if (old_deny==DENY_READ && old_mode==DOS_OPEN_RDONLY) return(AWRITE);
+ if (old_deny==DENY_NONE && old_mode==DOS_OPEN_RDONLY) return(AALL);
+ return(AFAIL);
+ case DENY_READ:
+ if (old_deny==DENY_WRITE && old_mode==DOS_OPEN_WRONLY) return(AREAD);
+ if (old_deny==DENY_READ && old_mode==DOS_OPEN_WRONLY) return(AWRITE);
+ if (old_deny==DENY_NONE && old_mode==DOS_OPEN_WRONLY) return(AALL);
+ return(AFAIL);
+ case DENY_NONE:
+ if (old_deny==DENY_WRITE) return(AREAD);
+ if (old_deny==DENY_READ) return(AWRITE);
+ if (old_deny==DENY_NONE) return(AALL);
+ return(AFAIL);
+ }
+ return(AFAIL);
+}
+
+
+/****************************************************************************
+check if we can open a file with a share mode
+****************************************************************************/
+
+static BOOL check_share_mode(connection_struct *conn, share_mode_entry *share, int share_mode, uint32 desired_access,
+ const char *fname, BOOL fcbopen, int *flags)
+{
+ int deny_mode = GET_DENY_MODE(share_mode);
+ int old_open_mode = GET_OPEN_MODE(share->share_mode);
+ int old_deny_mode = GET_DENY_MODE(share->share_mode);
+
+ /*
+ * share modes = false means don't bother to check for
+ * DENY mode conflict. This is a *really* bad idea :-). JRA.
+ */
+
+ if(!lp_share_modes(SNUM(conn)))
+ return True;
+
+ /*
+ * Don't allow any opens once the delete on close flag has been
+ * set.
+ */
+
+ if (GET_DELETE_ON_CLOSE_FLAG(share->share_mode)) {
+ DEBUG(5,("check_share_mode: Failing open on file %s as delete on close flag is set.\n",
+ fname ));
+ /* Use errno to map to correct error. */
+ unix_ERR_class = SMB_SUCCESS;
+ unix_ERR_code = 0;
+ unix_ERR_ntstatus = NT_STATUS_OK;
+ return False;
+ }
+
+ /* this is a nasty hack, but necessary until we rewrite our open
+ handling to use a NTCreateX call as the basic call.
+ NT may open a file with neither read nor write access, and in
+ this case it expects the open not to conflict with any
+ existing deny modes. This happens (for example) during a
+ "xcopy /o" where the second file descriptor is used for
+ ACL sets
+ (tridge)
+ */
+
+ /*
+ * This is a bit wierd - the test for desired access not having the
+ * critical bits seems seems odd. Firstly, if both opens have no
+ * critical bits then always ignore. Then check the "allow delete"
+ * then check for either. This probably isn't quite right yet but
+ * gets us much closer. JRA.
+ */
+
+ /*
+ * If desired_access doesn't contain READ_DATA,WRITE_DATA,APPEND_DATA or EXECUTE
+ * and the existing desired_acces then share modes don't conflict.
+ */
+
+ if ( !(desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) &&
+ !(share->desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) ) {
+
+ /*
+ * Wrinkle discovered by smbtorture....
+ * If both are non-io open and requester is asking for delete and current open has delete access
+ * but neither open has allowed file share delete then deny.... this is very strange and
+ * seems to be the only case in which non-io opens conflict. JRA.
+ */
+
+ if ((desired_access & DELETE_ACCESS) && (share->desired_access & DELETE_ACCESS) &&
+ (!GET_ALLOW_SHARE_DELETE(share->share_mode) || !GET_ALLOW_SHARE_DELETE(share_mode))) {
+ DEBUG(5,("check_share_mode: Failing open on file %s as delete access requests conflict.\n",
+ fname ));
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+ unix_ERR_ntstatus = NT_STATUS_SHARING_VIOLATION;
+
+ return False;
+ }
+
+ DEBUG(5,("check_share_mode: Allowing open on file %s as both desired access (0x%x) \
+and existing desired access (0x%x) are non-data opens\n",
+ fname, (unsigned int)desired_access, (unsigned int)share->desired_access ));
+ return True;
+ }
+
+ /*
+ * If delete access was requested and the existing share mode doesn't have
+ * ALLOW_SHARE_DELETE then deny.
+ */
+
+ if ((desired_access & DELETE_ACCESS) && !GET_ALLOW_SHARE_DELETE(share->share_mode)) {
+ DEBUG(5,("check_share_mode: Failing open on file %s as delete access requested and allow share delete not set.\n",
+ fname ));
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+ unix_ERR_ntstatus = NT_STATUS_SHARING_VIOLATION;
+
+ return False;
+ }
+
+ /*
+ * The inverse of the above.
+ * If delete access was granted and the new share mode doesn't have
+ * ALLOW_SHARE_DELETE then deny.
+ */
+
+ if ((share->desired_access & DELETE_ACCESS) && !GET_ALLOW_SHARE_DELETE(share_mode)) {
+ DEBUG(5,("check_share_mode: Failing open on file %s as delete access granted and allow share delete not requested.\n",
+ fname ));
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+ unix_ERR_ntstatus = NT_STATUS_SHARING_VIOLATION;
+
+ return False;
+ }
+
+ /*
+ * If desired_access doesn't contain READ_DATA,WRITE_DATA,APPEND_DATA or EXECUTE
+ * then share modes don't conflict. Likewise with existing desired access.
+ */
+
+ if ( !(desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) ||
+ !(share->desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) ) {
+ DEBUG(5,("check_share_mode: Allowing open on file %s as desired access (0x%x) doesn't conflict with\
+existing desired access (0x%x).\n", fname, (unsigned int)desired_access, (unsigned int)share->desired_access ));
+ return True;
+ }
+
+ {
+ int access_allowed = access_table(deny_mode,old_deny_mode,old_open_mode,
+ (share->pid == sys_getpid()),is_executable(fname));
+
+ if ((access_allowed == AFAIL) ||
+ (!fcbopen && (access_allowed == AREAD && *flags == O_RDWR)) ||
+ (access_allowed == AREAD && *flags != O_RDONLY) ||
+ (access_allowed == AWRITE && *flags != O_WRONLY)) {
+
+ DEBUG(2,("Share violation on file (%d,%d,%d,%d,%s,fcbopen = %d, flags = %d) = %d\n",
+ deny_mode,old_deny_mode,old_open_mode,
+ (int)share->pid,fname, fcbopen, *flags, access_allowed));
+
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+ unix_ERR_ntstatus = NT_STATUS_SHARING_VIOLATION;
+
+ return False;
+ }
+
+ if (access_allowed == AREAD)
+ *flags = O_RDONLY;
+
+ if (access_allowed == AWRITE)
+ *flags = O_WRONLY;
+
+ }
+
+ return True;
+}
+
+
+#if defined(DEVELOPER)
+static void validate_my_share_entries(int num, share_mode_entry *share_entry)
+{
+ files_struct *fsp;
+
+ if (share_entry->pid != sys_getpid())
+ return;
+
+ fsp = file_find_dif(share_entry->dev, share_entry->inode, share_entry->share_file_id);
+ if (!fsp) {
+ DEBUG(0,("validate_my_share_entries: PANIC : %s\n", share_mode_str(num, share_entry) ));
+ smb_panic("validate_my_share_entries: Cannot match a share entry with an open file\n");
+ }
+
+ if (((uint16)fsp->oplock_type) != share_entry->op_type) {
+ pstring str;
+ DEBUG(0,("validate_my_share_entries: PANIC : %s\n", share_mode_str(num, share_entry) ));
+ slprintf(str, sizeof(str)-1, "validate_my_share_entries: file %s, oplock_type = 0x%x, op_type = 0x%x\n",
+ fsp->fsp_name, (unsigned int)fsp->oplock_type, (unsigned int)share_entry->op_type );
+ smb_panic(str);
+ }
+}
+#endif
+
+/****************************************************************************
+ Deal with open deny mode and oplock break processing.
+ Invarient: Share mode must be locked on entry and exit.
+ Returns -1 on error, or number of share modes on success (may be zero).
+****************************************************************************/
+
+static int open_mode_check(connection_struct *conn, const char *fname, SMB_DEV_T dev,
+ SMB_INO_T inode,
+ uint32 desired_access,
+ int share_mode, int *p_flags, int *p_oplock_request,
+ BOOL *p_all_current_opens_are_level_II)
+{
+ int i;
+ int num_share_modes;
+ int oplock_contention_count = 0;
+ share_mode_entry *old_shares = 0;
+ BOOL fcbopen = False;
+ BOOL broke_oplock;
+
+ if(GET_OPEN_MODE(share_mode) == DOS_OPEN_FCB)
+ fcbopen = True;
+
+ num_share_modes = get_share_modes(conn, dev, inode, &old_shares);
+
+ if(num_share_modes == 0)
+ return 0;
+
+ /*
+ * Check if the share modes will give us access.
+ */
+
+ do {
+ share_mode_entry broken_entry;
+
+ broke_oplock = False;
+ *p_all_current_opens_are_level_II = True;
+
+ for(i = 0; i < num_share_modes; i++) {
+ share_mode_entry *share_entry = &old_shares[i];
+
+#if defined(DEVELOPER)
+ validate_my_share_entries(i, share_entry);
+#endif
+
+ /*
+ * By observation of NetBench, oplocks are broken *before* share
+ * modes are checked. This allows a file to be closed by the client
+ * if the share mode would deny access and the client has an oplock.
+ * Check if someone has an oplock on this file. If so we must break
+ * it before continuing.
+ */
+
+ if((*p_oplock_request && EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type)) ||
+ (!*p_oplock_request && (share_entry->op_type != NO_OPLOCK))) {
+
+ BOOL opb_ret;
+
+ DEBUG(5,("open_mode_check: oplock_request = %d, breaking oplock (%x) on file %s, \
+dev = %x, inode = %.0f\n", *p_oplock_request, share_entry->op_type, fname, (unsigned int)dev, (double)inode));
+
+ /* Ensure the reply for the open uses the correct sequence number. */
+ /* This isn't a real deferred packet as it's response will also increment
+ * the sequence.
+ */
+ srv_defer_sign_response(get_current_mid());
+
+ /* Oplock break - unlock to request it. */
+ unlock_share_entry(conn, dev, inode);
+
+ opb_ret = request_oplock_break(share_entry, False);
+
+ /* Now relock. */
+ lock_share_entry(conn, dev, inode);
+
+ if(opb_ret == False) {
+ DEBUG(0,("open_mode_check: FAILED when breaking oplock (%x) on file %s, \
+dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (double)inode));
+ SAFE_FREE(old_shares);
+ errno = EACCES;
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+ unix_ERR_ntstatus = NT_STATUS_SHARING_VIOLATION;
+ return -1;
+ }
+
+ broke_oplock = True;
+ broken_entry = *share_entry;
+ break;
+
+ } else if (!LEVEL_II_OPLOCK_TYPE(share_entry->op_type)) {
+ *p_all_current_opens_are_level_II = False;
+ }
+
+ /* someone else has a share lock on it, check to see if we can too */
+ if (!check_share_mode(conn, share_entry, share_mode, desired_access,
+ fname, fcbopen, p_flags)) {
+ SAFE_FREE(old_shares);
+ errno = EACCES;
+ return -1;
+ }
+
+ } /* end for */
+
+ if(broke_oplock) {
+ SAFE_FREE(old_shares);
+ num_share_modes = get_share_modes(conn, dev, inode, &old_shares);
+ oplock_contention_count++;
+
+ /* Paranoia check that this is no longer an exlusive entry. */
+ for(i = 0; i < num_share_modes; i++) {
+ share_mode_entry *share_entry = &old_shares[i];
+
+ if (share_modes_identical(&broken_entry, share_entry) &&
+ EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type) ) {
+
+ /*
+ * This should not happen. The target left this oplock
+ * as exlusive.... The process *must* be dead....
+ */
+
+ DEBUG(0,("open_mode_check: exlusive oplock left by process %d after break ! For file %s, \
+dev = %x, inode = %.0f. Deleting it to continue...\n", (int)broken_entry.pid, fname, (unsigned int)dev, (double)inode));
+
+ if (process_exists(broken_entry.pid)) {
+ DEBUG(0,("open_mode_check: Existent process %lu left active oplock.\n",
+ (unsigned long)broken_entry.pid ));
+ }
+
+ if (del_share_entry(dev, inode, &broken_entry, NULL) == -1) {
+ errno = EACCES;
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+ unix_ERR_ntstatus = NT_STATUS_SHARING_VIOLATION;
+ return -1;
+ }
+
+ /*
+ * We must reload the share modes after deleting the
+ * other process's entry.
+ */
+
+ SAFE_FREE(old_shares);
+ num_share_modes = get_share_modes(conn, dev, inode, &old_shares);
+ break;
+ }
+ } /* end for paranoia... */
+ } /* end if broke_oplock */
+
+ } while(broke_oplock);
+
+ if(old_shares != 0)
+ SAFE_FREE(old_shares);
+
+ /*
+ * Refuse to grant an oplock in case the contention limit is
+ * reached when going through the lock list multiple times.
+ */
+
+ if(oplock_contention_count >= lp_oplock_contention_limit(SNUM(conn))) {
+ *p_oplock_request = 0;
+ DEBUG(4,("open_mode_check: oplock contention = %d. Not granting oplock.\n",
+ oplock_contention_count ));
+ }
+
+ return num_share_modes;
+}
+
+/****************************************************************************
+set a kernel flock on a file for NFS interoperability
+this requires a patch to Linux
+****************************************************************************/
+static void kernel_flock(files_struct *fsp, int deny_mode)
+{
+#if HAVE_KERNEL_SHARE_MODES
+ int kernel_mode = 0;
+ if (deny_mode == DENY_READ) kernel_mode = LOCK_MAND|LOCK_WRITE;
+ else if (deny_mode == DENY_WRITE) kernel_mode = LOCK_MAND|LOCK_READ;
+ else if (deny_mode == DENY_ALL) kernel_mode = LOCK_MAND;
+ if (kernel_mode) flock(fsp->fd, kernel_mode);
+#endif
+ ;;
+}
+
+
+static BOOL open_match_attributes(connection_struct *conn, const char *path, uint32 old_dos_mode, uint32 new_dos_mode,
+ mode_t existing_mode, mode_t new_mode, mode_t *returned_mode)
+{
+ uint32 noarch_old_dos_mode, noarch_new_dos_mode;
+
+ noarch_old_dos_mode = (old_dos_mode & ~FILE_ATTRIBUTE_ARCHIVE);
+ noarch_new_dos_mode = (new_dos_mode & ~FILE_ATTRIBUTE_ARCHIVE);
+
+ if((noarch_old_dos_mode == 0 && noarch_new_dos_mode != 0) ||
+ (noarch_old_dos_mode != 0 && ((noarch_old_dos_mode & noarch_new_dos_mode) == noarch_old_dos_mode)))
+ *returned_mode = new_mode;
+ else
+ *returned_mode = (mode_t)0;
+
+ DEBUG(10,("open_match_attributes: file %s old_dos_mode = 0x%x, existing_mode = 0%o, new_dos_mode = 0x%x returned_mode = 0%o\n",
+ path,
+ old_dos_mode, (unsigned int)existing_mode, new_dos_mode, (unsigned int)*returned_mode ));
+
+ /* If we're mapping SYSTEM and HIDDEN ensure they match. */
+ if (lp_map_system(SNUM(conn)) || lp_store_dos_attributes(SNUM(conn))) {
+ if ((old_dos_mode & FILE_ATTRIBUTE_SYSTEM) && !(new_dos_mode & FILE_ATTRIBUTE_SYSTEM))
+ return False;
+ }
+ if (lp_map_hidden(SNUM(conn)) || lp_store_dos_attributes(SNUM(conn))) {
+ if ((old_dos_mode & FILE_ATTRIBUTE_HIDDEN) && !(new_dos_mode & FILE_ATTRIBUTE_HIDDEN))
+ return False;
+ }
+ return True;
+}
+
+/****************************************************************************
+ Open a file with a share mode.
+****************************************************************************/
+
+files_struct *open_file_shared(connection_struct *conn,char *fname, SMB_STRUCT_STAT *psbuf,
+ int share_mode,int ofun, uint32 new_dos_mode, int oplock_request,
+ int *Access,int *action)
+{
+ return open_file_shared1(conn, fname, psbuf, 0, share_mode, ofun, new_dos_mode,
+ oplock_request, Access, action);
+}
+
+/****************************************************************************
+ Open a file with a share mode.
+****************************************************************************/
+
+files_struct *open_file_shared1(connection_struct *conn,char *fname, SMB_STRUCT_STAT *psbuf,
+ uint32 desired_access,
+ int share_mode,int ofun, uint32 new_dos_mode,
+ int oplock_request,
+ int *Access,int *paction)
+{
+ int flags=0;
+ int flags2=0;
+ int deny_mode = GET_DENY_MODE(share_mode);
+ BOOL allow_share_delete = GET_ALLOW_SHARE_DELETE(share_mode);
+ BOOL delete_on_close = GET_DELETE_ON_CLOSE_FLAG(share_mode);
+ BOOL file_existed = VALID_STAT(*psbuf);
+ BOOL fcbopen = False;
+ BOOL def_acl = False;
+ SMB_DEV_T dev = 0;
+ SMB_INO_T inode = 0;
+ int num_share_modes = 0;
+ BOOL all_current_opens_are_level_II = False;
+ BOOL fsp_open = False;
+ files_struct *fsp = NULL;
+ int open_mode=0;
+ uint16 port = 0;
+ mode_t new_mode = (mode_t)0;
+ int action;
+ uint32 existing_dos_mode = 0;
+ /* We add aARCH to this as this mode is only used if the file is created new. */
+ mode_t mode = unix_mode(conn,new_dos_mode | aARCH,fname);
+
+ if (conn->printer) {
+ /* printers are handled completely differently. Most of the passed parameters are
+ ignored */
+ if (Access)
+ *Access = DOS_OPEN_WRONLY;
+ if (action)
+ *paction = FILE_WAS_CREATED;
+ return print_fsp_open(conn, fname);
+ }
+
+ fsp = file_new(conn);
+ if(!fsp)
+ return NULL;
+
+ DEBUG(10,("open_file_shared: fname = %s, dos_attrs = %x, share_mode = %x, ofun = %x, mode = %o, oplock request = %d\n",
+ fname, new_dos_mode, share_mode, ofun, (int)mode, oplock_request ));
+
+ if (!check_name(fname,conn)) {
+ file_free(fsp);
+ return NULL;
+ }
+
+ new_dos_mode &= SAMBA_ATTRIBUTES_MASK;
+ if (file_existed) {
+ existing_dos_mode = dos_mode(conn, fname, psbuf);
+ }
+
+ /* ignore any oplock requests if oplocks are disabled */
+ if (!lp_oplocks(SNUM(conn)) || global_client_failed_oplock_break) {
+ oplock_request = 0;
+ }
+
+ /* this is for OS/2 EAs - try and say we don't support them */
+ if (strstr(fname,".+,;=[].")) {
+ unix_ERR_class = ERRDOS;
+ /* OS/2 Workplace shell fix may be main code stream in a later release. */
+#if 1 /* OS2_WPS_FIX - Recent versions of OS/2 need this. */
+ unix_ERR_code = ERRcannotopen;
+#else /* OS2_WPS_FIX */
+ unix_ERR_code = ERROR_EAS_NOT_SUPPORTED;
+#endif /* OS2_WPS_FIX */
+
+ DEBUG(5,("open_file_shared: OS/2 EA's are not supported.\n"));
+ file_free(fsp);
+ return NULL;
+ }
+
+ if ((GET_FILE_OPEN_DISPOSITION(ofun) == FILE_EXISTS_FAIL) && file_existed) {
+ DEBUG(5,("open_file_shared: create new requested for file %s and file already exists.\n",
+ fname ));
+ file_free(fsp);
+ if (S_ISDIR(psbuf->st_mode)) {
+ errno = EISDIR;
+ } else {
+ errno = EEXIST;
+ }
+ return NULL;
+ }
+
+ if (CAN_WRITE(conn) && (GET_FILE_CREATE_DISPOSITION(ofun) == FILE_CREATE_IF_NOT_EXIST))
+ flags2 |= O_CREAT;
+
+ if (CAN_WRITE(conn) && (GET_FILE_OPEN_DISPOSITION(ofun) == FILE_EXISTS_TRUNCATE))
+ flags2 |= O_TRUNC;
+
+ /* We only care about matching attributes on file exists and truncate. */
+ if (file_existed && (GET_FILE_OPEN_DISPOSITION(ofun) == FILE_EXISTS_TRUNCATE)) {
+ if (!open_match_attributes(conn, fname, existing_dos_mode, new_dos_mode,
+ psbuf->st_mode, mode, &new_mode)) {
+ DEBUG(5,("open_file_shared: attributes missmatch for file %s (%x %x) (0%o, 0%o)\n",
+ fname, existing_dos_mode, new_dos_mode,
+ (int)psbuf->st_mode, (int)mode ));
+ file_free(fsp);
+ errno = EACCES;
+ return NULL;
+ }
+ }
+
+ if (GET_FILE_OPEN_DISPOSITION(ofun) == FILE_EXISTS_FAIL)
+ flags2 |= O_EXCL;
+
+ /* note that we ignore the append flag as
+ append does not mean the same thing under dos and unix */
+
+ switch (GET_OPEN_MODE(share_mode)) {
+ case DOS_OPEN_WRONLY:
+ flags = O_WRONLY;
+ if (desired_access == 0)
+ desired_access = FILE_WRITE_DATA;
+ break;
+ case DOS_OPEN_FCB:
+ fcbopen = True;
+ flags = O_RDWR;
+ if (desired_access == 0)
+ desired_access = FILE_READ_DATA|FILE_WRITE_DATA;
+ break;
+ case DOS_OPEN_RDWR:
+ flags = O_RDWR;
+ if (desired_access == 0)
+ desired_access = FILE_READ_DATA|FILE_WRITE_DATA;
+ break;
+ default:
+ flags = O_RDONLY;
+ if (desired_access == 0)
+ desired_access = FILE_READ_DATA;
+ break;
+ }
+
+#if defined(O_SYNC)
+ if (GET_FILE_SYNC_OPENMODE(share_mode)) {
+ flags2 |= O_SYNC;
+ }
+#endif /* O_SYNC */
+
+ if (flags != O_RDONLY && file_existed &&
+ (!CAN_WRITE(conn) || IS_DOS_READONLY(existing_dos_mode))) {
+ if (!fcbopen) {
+ DEBUG(5,("open_file_shared: read/write access requested for file %s on read only %s\n",
+ fname, !CAN_WRITE(conn) ? "share" : "file" ));
+ file_free(fsp);
+ errno = EACCES;
+ return NULL;
+ }
+ flags = O_RDONLY;
+ }
+
+ if (deny_mode > DENY_NONE && deny_mode!=DENY_FCB) {
+ DEBUG(2,("Invalid deny mode %d on file %s\n",deny_mode,fname));
+ file_free(fsp);
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (file_existed) {
+
+ dev = psbuf->st_dev;
+ inode = psbuf->st_ino;
+
+ lock_share_entry(conn, dev, inode);
+
+ num_share_modes = open_mode_check(conn, fname, dev, inode,
+ desired_access,
+ share_mode,
+ &flags, &oplock_request, &all_current_opens_are_level_II);
+ if(num_share_modes == -1) {
+
+ /*
+ * This next line is a subtlety we need for MS-Access. If a file open will
+ * fail due to share permissions and also for security (access)
+ * reasons, we need to return the access failed error, not the
+ * share error. This means we must attempt to open the file anyway
+ * in order to get the UNIX access error - even if we're going to
+ * fail the open for share reasons. This is bad, as we're burning
+ * another fd if there are existing locks but there's nothing else
+ * we can do. We also ensure we're not going to create or tuncate
+ * the file as we only want an access decision at this stage. JRA.
+ */
+ errno = 0;
+ fsp_open = open_file(fsp,conn,fname,psbuf,
+ flags|(flags2&~(O_TRUNC|O_CREAT)),mode,desired_access);
+
+ DEBUG(4,("open_file_shared : share_mode deny - calling open_file with \
+flags=0x%X flags2=0x%X mode=0%o returned %d\n",
+ flags,(flags2&~(O_TRUNC|O_CREAT)),(int)mode,(int)fsp_open ));
+
+ if (!fsp_open && errno) {
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRnoaccess;
+ unix_ERR_ntstatus = NT_STATUS_ACCESS_DENIED;
+ }
+
+ unlock_share_entry(conn, dev, inode);
+ if (fsp_open)
+ fd_close(conn, fsp);
+ file_free(fsp);
+ return NULL;
+ }
+
+ /*
+ * We exit this block with the share entry *locked*.....
+ */
+ }
+
+ /*
+ * Ensure we pay attention to default ACLs on directories if required.
+ */
+
+ if ((flags2 & O_CREAT) && lp_inherit_acls(SNUM(conn)) &&
+ (def_acl = directory_has_default_acl(conn, parent_dirname(fname))))
+ mode = 0777;
+
+ DEBUG(4,("calling open_file with flags=0x%X flags2=0x%X mode=0%o\n",
+ flags,flags2,(int)mode));
+
+ /*
+ * open_file strips any O_TRUNC flags itself.
+ */
+
+ fsp_open = open_file(fsp,conn,fname,psbuf,flags|flags2,mode,desired_access);
+
+ if (!fsp_open && (flags == O_RDWR) && (errno != ENOENT) && fcbopen) {
+ if((fsp_open = open_file(fsp,conn,fname,psbuf,O_RDONLY,mode,desired_access)) == True)
+ flags = O_RDONLY;
+ }
+
+ if (!fsp_open) {
+ if(file_existed)
+ unlock_share_entry(conn, dev, inode);
+ file_free(fsp);
+ return NULL;
+ }
+
+ /*
+ * Deal with the race condition where two smbd's detect the file doesn't
+ * exist and do the create at the same time. One of them will win and
+ * set a share mode, the other (ie. this one) should check if the
+ * requested share mode for this create is allowed.
+ */
+
+ if (!file_existed) {
+
+ /*
+ * Now the file exists and fsp is successfully opened,
+ * fsp->dev and fsp->inode are valid and should replace the
+ * dev=0,inode=0 from a non existent file. Spotted by
+ * Nadav Danieli <nadavd@exanet.com>. JRA.
+ */
+
+ dev = fsp->dev;
+ inode = fsp->inode;
+
+ lock_share_entry_fsp(fsp);
+
+ num_share_modes = open_mode_check(conn, fname, dev, inode,
+ desired_access,
+ share_mode,
+ &flags, &oplock_request, &all_current_opens_are_level_II);
+
+ if(num_share_modes == -1) {
+ unlock_share_entry_fsp(fsp);
+ fd_close(conn,fsp);
+ file_free(fsp);
+ return NULL;
+ }
+
+ /*
+ * If there are any share modes set then the file *did*
+ * exist. Ensure we return the correct value for action.
+ */
+
+ if (num_share_modes > 0)
+ file_existed = True;
+
+ /*
+ * We exit this block with the share entry *locked*.....
+ */
+ }
+
+ /* note that we ignore failure for the following. It is
+ basically a hack for NFS, and NFS will never set one of
+ these only read them. Nobody but Samba can ever set a deny
+ mode and we have already checked our more authoritative
+ locking database for permission to set this deny mode. If
+ the kernel refuses the operations then the kernel is wrong */
+ kernel_flock(fsp, deny_mode);
+
+ /*
+ * At this point onwards, we can guarentee that the share entry
+ * is locked, whether we created the file or not, and that the
+ * deny mode is compatible with all current opens.
+ */
+
+ /*
+ * If requested, truncate the file.
+ */
+
+ if (flags2&O_TRUNC) {
+ /*
+ * We are modifing the file after open - update the stat struct..
+ */
+ if ((truncate_unless_locked(conn,fsp) == -1) || (SMB_VFS_FSTAT(fsp,fsp->fd,psbuf)==-1)) {
+ unlock_share_entry_fsp(fsp);
+ fd_close(conn,fsp);
+ file_free(fsp);
+ return NULL;
+ }
+ }
+
+ switch (flags) {
+ case O_RDONLY:
+ open_mode = DOS_OPEN_RDONLY;
+ break;
+ case O_RDWR:
+ open_mode = DOS_OPEN_RDWR;
+ break;
+ case O_WRONLY:
+ open_mode = DOS_OPEN_WRONLY;
+ break;
+ }
+
+ fsp->share_mode = SET_DENY_MODE(deny_mode) |
+ SET_OPEN_MODE(open_mode) |
+ SET_ALLOW_SHARE_DELETE(allow_share_delete);
+
+ DEBUG(10,("open_file_shared : share_mode = %x\n", fsp->share_mode ));
+
+ if (Access) {
+ (*Access) = open_mode;
+ }
+
+ if (file_existed && !(flags2 & O_TRUNC))
+ action = FILE_WAS_OPENED;
+ if (file_existed && (flags2 & O_TRUNC))
+ action = FILE_WAS_OVERWRITTEN;
+ if (!file_existed)
+ action = FILE_WAS_CREATED;
+
+ if (paction) {
+ *paction = action;
+ }
+
+ /*
+ * Setup the oplock info in both the shared memory and
+ * file structs.
+ */
+
+ if(oplock_request && (num_share_modes == 0) &&
+ !IS_VETO_OPLOCK_PATH(conn,fname) && set_file_oplock(fsp, oplock_request) ) {
+ port = global_oplock_port;
+ } else if (oplock_request && all_current_opens_are_level_II) {
+ port = global_oplock_port;
+ oplock_request = LEVEL_II_OPLOCK;
+ set_file_oplock(fsp, oplock_request);
+ } else {
+ port = 0;
+ oplock_request = 0;
+ }
+
+ set_share_mode(fsp, port, oplock_request);
+
+ if (delete_on_close) {
+ NTSTATUS result = set_delete_on_close_internal(fsp, delete_on_close);
+
+ if (NT_STATUS_V(result) != NT_STATUS_V(NT_STATUS_OK)) {
+ /* Remember to delete the mode we just added. */
+ del_share_mode(fsp, NULL);
+ unlock_share_entry_fsp(fsp);
+ fd_close(conn,fsp);
+ file_free(fsp);
+ return NULL;
+ }
+ }
+
+ if (action == FILE_WAS_OVERWRITTEN || action == FILE_WAS_CREATED) {
+ /* Files should be initially set as archive */
+ if (lp_map_archive(SNUM(conn)) || lp_store_dos_attributes(SNUM(conn))) {
+ file_set_dosmode(conn, fname, new_dos_mode | aARCH, NULL);
+ }
+ }
+
+ /*
+ * Take care of inherited ACLs on created files - if default ACL not
+ * selected.
+ */
+
+ if (!file_existed && !def_acl) {
+
+ int saved_errno = errno; /* We might get ENOSYS in the next call.. */
+
+ if (SMB_VFS_FCHMOD_ACL(fsp, fsp->fd, mode) == -1 && errno == ENOSYS)
+ errno = saved_errno; /* Ignore ENOSYS */
+
+ } else if (new_mode) {
+
+ int ret = -1;
+
+ /* Attributes need changing. File already existed. */
+
+ {
+ int saved_errno = errno; /* We might get ENOSYS in the next call.. */
+ ret = SMB_VFS_FCHMOD_ACL(fsp, fsp->fd, new_mode);
+
+ if (ret == -1 && errno == ENOSYS) {
+ errno = saved_errno; /* Ignore ENOSYS */
+ } else {
+ DEBUG(5, ("open_file_shared: failed to reset attributes of file %s to 0%o\n",
+ fname, (int)new_mode));
+ ret = 0; /* Don't do the fchmod below. */
+ }
+ }
+
+ if ((ret == -1) && (SMB_VFS_FCHMOD(fsp, fsp->fd, new_mode) == -1))
+ DEBUG(5, ("open_file_shared: failed to reset attributes of file %s to 0%o\n",
+ fname, (int)new_mode));
+ }
+
+ unlock_share_entry_fsp(fsp);
+
+ conn->num_files_open++;
+
+ return fsp;
+}
+
+/****************************************************************************
+ Open a file for for write to ensure that we can fchmod it.
+****************************************************************************/
+
+files_struct *open_file_fchmod(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf)
+{
+ files_struct *fsp = NULL;
+ BOOL fsp_open;
+
+ if (!VALID_STAT(*psbuf))
+ return NULL;
+
+ fsp = file_new(conn);
+ if(!fsp)
+ return NULL;
+
+ /* note! we must use a non-zero desired access or we don't get
+ a real file descriptor. Oh what a twisted web we weave. */
+ fsp_open = open_file(fsp,conn,fname,psbuf,O_WRONLY,0,FILE_WRITE_DATA);
+
+ /*
+ * This is not a user visible file open.
+ * Don't set a share mode and don't increment
+ * the conn->num_files_open.
+ */
+
+ if (!fsp_open) {
+ file_free(fsp);
+ return NULL;
+ }
+
+ return fsp;
+}
+
+/****************************************************************************
+ Close the fchmod file fd - ensure no locks are lost.
+****************************************************************************/
+
+int close_file_fchmod(files_struct *fsp)
+{
+ int ret = fd_close(fsp->conn, fsp);
+ file_free(fsp);
+ return ret;
+}
+
+/****************************************************************************
+ Open a directory from an NT SMB call.
+****************************************************************************/
+
+files_struct *open_directory(connection_struct *conn, char *fname, SMB_STRUCT_STAT *psbuf,
+ uint32 desired_access, int share_mode, int smb_ofun, int *action)
+{
+ extern struct current_user current_user;
+ BOOL got_stat = False;
+ files_struct *fsp = file_new(conn);
+ BOOL delete_on_close = GET_DELETE_ON_CLOSE_FLAG(share_mode);
+
+ if(!fsp)
+ return NULL;
+
+ if (VALID_STAT(*psbuf))
+ got_stat = True;
+
+ if (got_stat && (GET_FILE_OPEN_DISPOSITION(smb_ofun) == FILE_EXISTS_FAIL)) {
+ file_free(fsp);
+ errno = EEXIST; /* Setup so correct error is returned to client. */
+ return NULL;
+ }
+
+ if (GET_FILE_CREATE_DISPOSITION(smb_ofun) == FILE_CREATE_IF_NOT_EXIST) {
+
+ if (got_stat) {
+
+ if(!S_ISDIR(psbuf->st_mode)) {
+ DEBUG(0,("open_directory: %s is not a directory !\n", fname ));
+ file_free(fsp);
+ errno = EACCES;
+ return NULL;
+ }
+ *action = FILE_WAS_OPENED;
+
+ } else {
+
+ /*
+ * Try and create the directory.
+ */
+
+ if(!CAN_WRITE(conn)) {
+ DEBUG(2,("open_directory: failing create on read-only share\n"));
+ file_free(fsp);
+ errno = EACCES;
+ return NULL;
+ }
+
+ if (ms_has_wild(fname)) {
+ file_free(fsp);
+ DEBUG(5,("open_directory: failing create on filename %s with wildcards\n", fname));
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRinvalidname;
+ unix_ERR_ntstatus = NT_STATUS_OBJECT_NAME_INVALID;
+ return NULL;
+ }
+
+ if( strchr_m(fname, ':')) {
+ file_free(fsp);
+ DEBUG(5,("open_directory: failing create on filename %s with colon in name\n", fname));
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRinvalidname;
+ unix_ERR_ntstatus = NT_STATUS_NOT_A_DIRECTORY;
+ return NULL;
+ }
+
+ if(vfs_MkDir(conn,fname, unix_mode(conn,aDIR, fname)) < 0) {
+ DEBUG(2,("open_directory: unable to create %s. Error was %s\n",
+ fname, strerror(errno) ));
+ file_free(fsp);
+ return NULL;
+ }
+
+ if(SMB_VFS_STAT(conn,fname, psbuf) != 0) {
+ file_free(fsp);
+ return NULL;
+ }
+
+ *action = FILE_WAS_CREATED;
+
+ }
+ } else {
+
+ /*
+ * Don't create - just check that it *was* a directory.
+ */
+
+ if(!got_stat) {
+ DEBUG(3,("open_directory: unable to stat name = %s. Error was %s\n",
+ fname, strerror(errno) ));
+ file_free(fsp);
+ return NULL;
+ }
+
+ if(!S_ISDIR(psbuf->st_mode)) {
+ DEBUG(0,("open_directory: %s is not a directory !\n", fname ));
+ file_free(fsp);
+ return NULL;
+ }
+
+ *action = FILE_WAS_OPENED;
+ }
+
+ DEBUG(5,("open_directory: opening directory %s\n", fname));
+
+ /*
+ * Setup the files_struct for it.
+ */
+
+ fsp->mode = psbuf->st_mode;
+ fsp->inode = psbuf->st_ino;
+ fsp->dev = psbuf->st_dev;
+ fsp->size = psbuf->st_size;
+ fsp->vuid = current_user.vuid;
+ fsp->file_pid = global_smbpid;
+ fsp->can_lock = True;
+ fsp->can_read = False;
+ fsp->can_write = False;
+ fsp->share_mode = share_mode;
+ fsp->desired_access = desired_access;
+ fsp->print_file = False;
+ fsp->modified = False;
+ fsp->oplock_type = NO_OPLOCK;
+ fsp->sent_oplock_break = NO_BREAK_SENT;
+ fsp->is_directory = True;
+ fsp->is_stat = False;
+ fsp->directory_delete_on_close = False;
+ string_set(&fsp->fsp_name,fname);
+
+ if (delete_on_close) {
+ NTSTATUS result = set_delete_on_close_internal(fsp, delete_on_close);
+
+ if (NT_STATUS_V(result) != NT_STATUS_V(NT_STATUS_OK)) {
+ file_free(fsp);
+ return NULL;
+ }
+ }
+ conn->num_files_open++;
+
+ return fsp;
+}
+
+/****************************************************************************
+ Open a pseudo-file (no locking checks - a 'stat' open).
+****************************************************************************/
+
+files_struct *open_file_stat(connection_struct *conn, char *fname, SMB_STRUCT_STAT *psbuf)
+{
+ extern struct current_user current_user;
+ files_struct *fsp = NULL;
+
+ if (!VALID_STAT(*psbuf))
+ return NULL;
+
+ /* Can't 'stat' open directories. */
+ if(S_ISDIR(psbuf->st_mode))
+ return NULL;
+
+ fsp = file_new(conn);
+ if(!fsp)
+ return NULL;
+
+ DEBUG(5,("open_file_stat: 'opening' file %s\n", fname));
+
+ /*
+ * Setup the files_struct for it.
+ */
+
+ fsp->mode = psbuf->st_mode;
+ /*
+ * Don't store dev or inode, we don't want any iterator
+ * to see this.
+ */
+ fsp->inode = (SMB_INO_T)0;
+ fsp->dev = (SMB_DEV_T)0;
+ fsp->size = psbuf->st_size;
+ fsp->vuid = current_user.vuid;
+ fsp->file_pid = global_smbpid;
+ fsp->can_lock = False;
+ fsp->can_read = False;
+ fsp->can_write = False;
+ fsp->share_mode = 0;
+ fsp->desired_access = 0;
+ fsp->print_file = False;
+ fsp->modified = False;
+ fsp->oplock_type = NO_OPLOCK;
+ fsp->sent_oplock_break = NO_BREAK_SENT;
+ fsp->is_directory = False;
+ fsp->is_stat = True;
+ fsp->directory_delete_on_close = False;
+ string_set(&fsp->fsp_name,fname);
+
+ conn->num_files_open++;
+
+ return fsp;
+}
diff --git a/source/smbd/oplock.c b/source/smbd/oplock.c
new file mode 100644
index 00000000000..19e6956d9ef
--- /dev/null
+++ b/source/smbd/oplock.c
@@ -0,0 +1,1260 @@
+/*
+ Unix SMB/CIFS implementation.
+ oplock processing
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Jeremy Allison 1998 - 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* Oplock ipc UDP socket. */
+static int oplock_sock = -1;
+uint16 global_oplock_port = 0;
+
+/* Current number of oplocks we have outstanding. */
+static int32 exclusive_oplocks_open = 0;
+static int32 level_II_oplocks_open = 0;
+BOOL global_client_failed_oplock_break = False;
+BOOL global_oplock_break = False;
+
+extern int smb_read_error;
+
+static struct kernel_oplocks *koplocks;
+
+static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_id, BOOL local);
+
+/****************************************************************************
+ Get the number of current exclusive oplocks.
+****************************************************************************/
+
+int32 get_number_of_exclusive_open_oplocks(void)
+{
+ return exclusive_oplocks_open;
+}
+
+/****************************************************************************
+ Return True if an oplock message is pending.
+****************************************************************************/
+
+BOOL oplock_message_waiting(fd_set *fds)
+{
+ if (koplocks && koplocks->msg_waiting(fds))
+ return True;
+
+ if (FD_ISSET(oplock_sock, fds))
+ return True;
+
+ return False;
+}
+
+/****************************************************************************
+ Read an oplock break message from either the oplock UDP fd or the
+ kernel (if kernel oplocks are supported).
+
+ If timeout is zero then *fds contains the file descriptors that
+ are ready to be read and acted upon. If timeout is non-zero then
+ *fds contains the file descriptors to be selected on for read.
+ The timeout is in milliseconds
+
+****************************************************************************/
+
+BOOL receive_local_message( char *buffer, int buffer_len, int timeout)
+{
+ struct sockaddr_in from;
+ socklen_t fromlen = sizeof(from);
+ int32 msg_len = 0;
+ fd_set fds;
+ int selrtn = -1;
+
+ FD_ZERO(&fds);
+ smb_read_error = 0;
+
+ /*
+ * We need to check for kernel oplocks before going into the select
+ * here, as the EINTR generated by the linux kernel oplock may have
+ * already been eaten. JRA.
+ */
+
+ if (koplocks && koplocks->msg_waiting(&fds)) {
+ return koplocks->receive_message(&fds, buffer, buffer_len);
+ }
+
+ while (timeout > 0 && selrtn == -1) {
+ struct timeval to;
+ int maxfd = oplock_sock;
+ time_t starttime = time(NULL);
+
+ FD_ZERO(&fds);
+ maxfd = setup_oplock_select_set(&fds);
+
+ to.tv_sec = timeout / 1000;
+ to.tv_usec = (timeout % 1000) * 1000;
+
+ DEBUG(5,("receive_local_message: doing select with timeout of %d ms\n", timeout));
+
+ selrtn = sys_select(maxfd+1,&fds,NULL,NULL,&to);
+
+ if (selrtn == -1 && errno == EINTR) {
+
+ /* could be a kernel oplock interrupt */
+ if (koplocks && koplocks->msg_waiting(&fds)) {
+ return koplocks->receive_message(&fds, buffer, buffer_len);
+ }
+
+ /*
+ * Linux 2.0.x seems to have a bug in that
+ * it can return -1, EINTR with a timeout of zero.
+ * Make sure we bail out here with a read timeout
+ * if we got EINTR on a timeout of 1 or less.
+ */
+
+ if (timeout <= 1) {
+ smb_read_error = READ_TIMEOUT;
+ return False;
+ }
+
+ /* Not a kernel interrupt - could be a SIGUSR1 message. We must restart. */
+ /* We need to decrement the timeout here. */
+ timeout -= ((time(NULL) - starttime)*1000);
+ if (timeout < 0)
+ timeout = 1;
+
+ DEBUG(5,("receive_local_message: EINTR : new timeout %d ms\n", timeout));
+ continue;
+ }
+
+ /* Check if error */
+ if(selrtn == -1) {
+ /* something is wrong. Maybe the socket is dead? */
+ smb_read_error = READ_ERROR;
+ return False;
+ }
+
+ /* Did we timeout ? */
+ if (selrtn == 0) {
+ smb_read_error = READ_TIMEOUT;
+ return False;
+ }
+ }
+
+ if (koplocks && koplocks->msg_waiting(&fds)) {
+ return koplocks->receive_message(&fds, buffer, buffer_len);
+ }
+
+ if (!FD_ISSET(oplock_sock, &fds))
+ return False;
+
+ /*
+ * From here down we deal with the smbd <--> smbd
+ * oplock break protocol only.
+ */
+
+ /*
+ * Read a loopback udp message.
+ */
+ msg_len = sys_recvfrom(oplock_sock, &buffer[OPBRK_CMD_HEADER_LEN],
+ buffer_len - OPBRK_CMD_HEADER_LEN, 0, (struct sockaddr *)&from, &fromlen);
+
+ if(msg_len < 0) {
+ DEBUG(0,("receive_local_message. Error in recvfrom. (%s).\n",strerror(errno)));
+ return False;
+ }
+
+ /* Validate message length. */
+ if(msg_len > (buffer_len - OPBRK_CMD_HEADER_LEN)) {
+ DEBUG(0,("receive_local_message: invalid msg_len (%d) max can be %d\n", msg_len,
+ buffer_len - OPBRK_CMD_HEADER_LEN));
+ return False;
+ }
+
+ /* Validate message from address (must be localhost). */
+ if(from.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
+ DEBUG(0,("receive_local_message: invalid 'from' address \
+(was %lx should be 127.0.0.1)\n", (long)from.sin_addr.s_addr));
+ return False;
+ }
+
+ /* Setup the message header */
+ SIVAL(buffer,OPBRK_CMD_LEN_OFFSET,msg_len);
+ SSVAL(buffer,OPBRK_CMD_PORT_OFFSET,ntohs(from.sin_port));
+
+ return True;
+}
+
+/****************************************************************************
+ Attempt to set an oplock on a file. Always succeeds if kernel oplocks are
+ disabled (just sets flags). Returns True if oplock set.
+****************************************************************************/
+
+BOOL set_file_oplock(files_struct *fsp, int oplock_type)
+{
+ if (koplocks && !koplocks->set_oplock(fsp, oplock_type))
+ return False;
+
+ fsp->oplock_type = oplock_type;
+ fsp->sent_oplock_break = NO_BREAK_SENT;
+ if (oplock_type == LEVEL_II_OPLOCK)
+ level_II_oplocks_open++;
+ else
+ exclusive_oplocks_open++;
+
+ DEBUG(5,("set_file_oplock: granted oplock on file %s, dev = %x, inode = %.0f, file_id = %lu, \
+tv_sec = %x, tv_usec = %x\n",
+ fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id,
+ (int)fsp->open_time.tv_sec, (int)fsp->open_time.tv_usec ));
+
+ return True;
+}
+
+/****************************************************************************
+ Attempt to release an oplock on a file. Decrements oplock count.
+****************************************************************************/
+
+void release_file_oplock(files_struct *fsp)
+{
+ if ((fsp->oplock_type != NO_OPLOCK) && koplocks)
+ koplocks->release_oplock(fsp);
+
+ if (fsp->oplock_type == LEVEL_II_OPLOCK)
+ level_II_oplocks_open--;
+ else if (fsp->oplock_type)
+ exclusive_oplocks_open--;
+
+ fsp->oplock_type = NO_OPLOCK;
+ fsp->sent_oplock_break = NO_BREAK_SENT;
+
+ flush_write_cache(fsp, OPLOCK_RELEASE_FLUSH);
+}
+
+/****************************************************************************
+ Attempt to downgrade an oplock on a file. Doesn't decrement oplock count.
+****************************************************************************/
+
+static void downgrade_file_oplock(files_struct *fsp)
+{
+ if (koplocks)
+ koplocks->release_oplock(fsp);
+ fsp->oplock_type = LEVEL_II_OPLOCK;
+ exclusive_oplocks_open--;
+ level_II_oplocks_open++;
+ fsp->sent_oplock_break = NO_BREAK_SENT;
+}
+
+/****************************************************************************
+ Remove a file oplock. Copes with level II and exclusive.
+ Locks then unlocks the share mode lock. Client can decide to go directly
+ to none even if a "break-to-level II" was sent.
+****************************************************************************/
+
+BOOL remove_oplock(files_struct *fsp, BOOL break_to_none)
+{
+ SMB_DEV_T dev = fsp->dev;
+ SMB_INO_T inode = fsp->inode;
+ BOOL ret = True;
+
+ /* Remove the oplock flag from the sharemode. */
+ if (lock_share_entry_fsp(fsp) == False) {
+ DEBUG(0,("remove_oplock: failed to lock share entry for file %s\n",
+ fsp->fsp_name ));
+ return False;
+ }
+
+ if (fsp->sent_oplock_break == EXCLUSIVE_BREAK_SENT || break_to_none) {
+ /*
+ * Deal with a reply when a break-to-none was sent.
+ */
+
+ if(remove_share_oplock(fsp)==False) {
+ DEBUG(0,("remove_oplock: failed to remove share oplock for file %s fnum %d, \
+dev = %x, inode = %.0f\n", fsp->fsp_name, fsp->fnum, (unsigned int)dev, (double)inode));
+ ret = False;
+ }
+
+ release_file_oplock(fsp);
+ } else {
+ /*
+ * Deal with a reply when a break-to-level II was sent.
+ */
+ if(downgrade_share_oplock(fsp)==False) {
+ DEBUG(0,("remove_oplock: failed to downgrade share oplock for file %s fnum %d, \
+dev = %x, inode = %.0f\n", fsp->fsp_name, fsp->fnum, (unsigned int)dev, (double)inode));
+ ret = False;
+ }
+
+ downgrade_file_oplock(fsp);
+ }
+
+ unlock_share_entry_fsp(fsp);
+ return ret;
+}
+
+/****************************************************************************
+ Setup the listening set of file descriptors for an oplock break
+ message either from the UDP socket or from the kernel. Returns the maximum
+ fd used.
+****************************************************************************/
+
+int setup_oplock_select_set( fd_set *fds)
+{
+ int maxfd = oplock_sock;
+
+ if(oplock_sock == -1)
+ return 0;
+
+ FD_SET(oplock_sock,fds);
+
+ if (koplocks && koplocks->notification_fd != -1) {
+ FD_SET(koplocks->notification_fd, fds);
+ maxfd = MAX(maxfd, koplocks->notification_fd);
+ }
+
+ return maxfd;
+}
+
+/****************************************************************************
+ Process an oplock break message - whether it came from the UDP socket
+ or from the kernel.
+****************************************************************************/
+
+BOOL process_local_message(char *buffer, int buf_size)
+{
+ int32 msg_len;
+ uint16 from_port;
+ char *msg_start;
+ pid_t remotepid;
+ SMB_DEV_T dev;
+ SMB_INO_T inode;
+ unsigned long file_id;
+ uint16 break_cmd_type;
+
+ msg_len = IVAL(buffer,OPBRK_CMD_LEN_OFFSET);
+ from_port = SVAL(buffer,OPBRK_CMD_PORT_OFFSET);
+
+ msg_start = &buffer[OPBRK_CMD_HEADER_LEN];
+
+ DEBUG(5,("process_local_message: Got a message of length %d from port (%d)\n",
+ msg_len, from_port));
+
+ /*
+ * Pull the info out of the requesting packet.
+ */
+
+ break_cmd_type = SVAL(msg_start,OPBRK_MESSAGE_CMD_OFFSET);
+
+ switch(break_cmd_type) {
+ case KERNEL_OPLOCK_BREAK_CMD:
+ if (!koplocks) {
+ DEBUG(0,("unexpected kernel oplock break!\n"));
+ break;
+ }
+ if (!koplocks->parse_message(msg_start, msg_len, &inode, &dev, &file_id)) {
+ DEBUG(0,("kernel oplock break parse failure!\n"));
+ }
+ break;
+
+ case OPLOCK_BREAK_CMD:
+ case LEVEL_II_OPLOCK_BREAK_CMD:
+ case ASYNC_LEVEL_II_OPLOCK_BREAK_CMD:
+
+ /* Ensure that the msg length is correct. */
+ if(msg_len != OPLOCK_BREAK_MSG_LEN) {
+ DEBUG(0,("process_local_message: incorrect length for OPLOCK_BREAK_CMD (was %d, should be %d).\n",
+ (int)msg_len, (int)OPLOCK_BREAK_MSG_LEN));
+ return False;
+ }
+
+ memcpy((char *)&remotepid, msg_start+OPLOCK_BREAK_PID_OFFSET,sizeof(remotepid));
+ memcpy((char *)&inode, msg_start+OPLOCK_BREAK_INODE_OFFSET,sizeof(inode));
+ memcpy((char *)&dev, msg_start+OPLOCK_BREAK_DEV_OFFSET,sizeof(dev));
+ memcpy((char *)&file_id, msg_start+OPLOCK_BREAK_FILEID_OFFSET,sizeof(file_id));
+
+ DEBUG(5,("process_local_message: (%s) oplock break request from \
+pid %d, port %d, dev = %x, inode = %.0f, file_id = %lu\n",
+ (break_cmd_type == OPLOCK_BREAK_CMD) ? "exclusive" : "level II",
+ (int)remotepid, from_port, (unsigned int)dev, (double)inode, file_id));
+ break;
+
+ /*
+ * Keep this as a debug case - eventually we can remove it.
+ */
+ case 0x8001:
+ DEBUG(0,("process_local_message: Received unsolicited break \
+reply - dumping info.\n"));
+
+ if(msg_len != OPLOCK_BREAK_MSG_LEN) {
+ DEBUG(0,("process_local_message: ubr: incorrect length for reply \
+(was %d, should be %d).\n", (int)msg_len, (int)OPLOCK_BREAK_MSG_LEN));
+ return False;
+ }
+
+ memcpy((char *)&inode, msg_start+OPLOCK_BREAK_INODE_OFFSET,sizeof(inode));
+ memcpy((char *)&remotepid, msg_start+OPLOCK_BREAK_PID_OFFSET,sizeof(remotepid));
+ memcpy((char *)&dev, msg_start+OPLOCK_BREAK_DEV_OFFSET,sizeof(dev));
+ memcpy((char *)&file_id, msg_start+OPLOCK_BREAK_FILEID_OFFSET,sizeof(file_id));
+
+ DEBUG(0,("process_local_message: unsolicited oplock break reply from \
+pid %d, port %d, dev = %x, inode = %.0f, file_id = %lu\n",
+ (int)remotepid, from_port, (unsigned int)dev, (double)inode, file_id));
+
+ return False;
+
+ default:
+ DEBUG(0,("process_local_message: unknown UDP message command code (%x) - ignoring.\n",
+ (unsigned int)SVAL(msg_start,0)));
+ return False;
+ }
+
+ /*
+ * Now actually process the break request.
+ */
+
+ if((exclusive_oplocks_open + level_II_oplocks_open) != 0) {
+ if (oplock_break(dev, inode, file_id, False) == False) {
+ DEBUG(0,("process_local_message: oplock break failed.\n"));
+ return False;
+ }
+ } else {
+ /*
+ * If we have no record of any currently open oplocks,
+ * it's not an error, as a close command may have
+ * just been issued on the file that was oplocked.
+ * Just log a message and return success in this case.
+ */
+ DEBUG(3,("process_local_message: oplock break requested with no outstanding \
+oplocks. Returning success.\n"));
+ }
+
+ /*
+ * Do the appropriate reply - none in the kernel or async level II case.
+ */
+
+ if(break_cmd_type == OPLOCK_BREAK_CMD || break_cmd_type == LEVEL_II_OPLOCK_BREAK_CMD) {
+ struct sockaddr_in toaddr;
+
+ /* Send the message back after OR'ing in the 'REPLY' bit. */
+ SSVAL(msg_start,OPBRK_MESSAGE_CMD_OFFSET,break_cmd_type | CMD_REPLY);
+
+ memset((char *)&toaddr,'\0',sizeof(toaddr));
+ toaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ toaddr.sin_port = htons(from_port);
+ toaddr.sin_family = AF_INET;
+
+ if(sys_sendto( oplock_sock, msg_start, OPLOCK_BREAK_MSG_LEN, 0,
+ (struct sockaddr *)&toaddr, sizeof(toaddr)) < 0) {
+ DEBUG(0,("process_local_message: sendto process %d failed. Errno was %s\n",
+ (int)remotepid, strerror(errno)));
+ return False;
+ }
+
+ DEBUG(5,("process_local_message: oplock break reply sent to \
+pid %d, port %d, for file dev = %x, inode = %.0f, file_id = %lu\n",
+ (int)remotepid, from_port, (unsigned int)dev, (double)inode, file_id));
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Set up an oplock break message.
+****************************************************************************/
+
+static void prepare_break_message(char *outbuf, files_struct *fsp, BOOL level2)
+{
+ memset(outbuf,'\0',smb_size);
+ set_message(outbuf,8,0,True);
+
+ SCVAL(outbuf,smb_com,SMBlockingX);
+ SSVAL(outbuf,smb_tid,fsp->conn->cnum);
+ SSVAL(outbuf,smb_pid,0xFFFF);
+ SSVAL(outbuf,smb_uid,0);
+ SSVAL(outbuf,smb_mid,0xFFFF);
+ SCVAL(outbuf,smb_vwv0,0xFF);
+ SSVAL(outbuf,smb_vwv2,fsp->fnum);
+ SCVAL(outbuf,smb_vwv3,LOCKING_ANDX_OPLOCK_RELEASE);
+ SCVAL(outbuf,smb_vwv3+1,level2 ? OPLOCKLEVEL_II : OPLOCKLEVEL_NONE);
+}
+
+/****************************************************************************
+ Function to do the waiting before sending a local break.
+****************************************************************************/
+
+static void wait_before_sending_break(BOOL local_request)
+{
+ extern struct timeval smb_last_time;
+
+ if(local_request) {
+ struct timeval cur_tv;
+ long wait_left = (long)lp_oplock_break_wait_time();
+
+ if (wait_left == 0)
+ return;
+
+ GetTimeOfDay(&cur_tv);
+
+ wait_left -= ((cur_tv.tv_sec - smb_last_time.tv_sec)*1000) +
+ ((cur_tv.tv_usec - smb_last_time.tv_usec)/1000);
+
+ if(wait_left > 0) {
+ wait_left = MIN(wait_left, 1000);
+ sys_usleep(wait_left * 1000);
+ }
+ }
+}
+
+/****************************************************************************
+ Ensure that we have a valid oplock.
+****************************************************************************/
+
+static files_struct *initial_break_processing(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_id)
+{
+ files_struct *fsp = NULL;
+
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "initial_break_processing: called for dev = %x, inode = %.0f file_id = %lu\n",
+ (unsigned int)dev, (double)inode, file_id);
+ dbgtext( "Current oplocks_open (exclusive = %d, levelII = %d)\n",
+ exclusive_oplocks_open, level_II_oplocks_open );
+ }
+
+ /*
+ * We need to search the file open table for the
+ * entry containing this dev and inode, and ensure
+ * we have an oplock on it.
+ */
+
+ fsp = file_find_dif(dev, inode, file_id);
+
+ if(fsp == NULL) {
+ /* The file could have been closed in the meantime - return success. */
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "initial_break_processing: cannot find open file with " );
+ dbgtext( "dev = %x, inode = %.0f file_id = %lu", (unsigned int)dev,
+ (double)inode, file_id);
+ dbgtext( "allowing break to succeed.\n" );
+ }
+ return NULL;
+ }
+
+ /* Ensure we have an oplock on the file */
+
+ /*
+ * There is a potential race condition in that an oplock could
+ * have been broken due to another udp request, and yet there are
+ * still oplock break messages being sent in the udp message
+ * queue for this file. So return true if we don't have an oplock,
+ * as we may have just freed it.
+ */
+
+ if(fsp->oplock_type == NO_OPLOCK) {
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "initial_break_processing: file %s ", fsp->fsp_name );
+ dbgtext( "(dev = %x, inode = %.0f, file_id = %lu) has no oplock.\n",
+ (unsigned int)dev, (double)inode, fsp->file_id );
+ dbgtext( "Allowing break to succeed regardless.\n" );
+ }
+ return NULL;
+ }
+
+ return fsp;
+}
+
+/****************************************************************************
+ Process a level II oplock break directly.
+****************************************************************************/
+
+BOOL oplock_break_level2(files_struct *fsp, BOOL local_request, int token)
+{
+ extern uint32 global_client_caps;
+ char outbuf[128];
+ BOOL got_lock = False;
+ SMB_DEV_T dev = fsp->dev;
+ SMB_INO_T inode = fsp->inode;
+
+ /*
+ * We can have a level II oplock even if the client is not
+ * level II oplock aware. In this case just remove the
+ * flags and don't send the break-to-none message to
+ * the client.
+ */
+
+ if (global_client_caps & CAP_LEVEL_II_OPLOCKS) {
+ /*
+ * If we are sending an oplock break due to an SMB sent
+ * by our own client we ensure that we wait at leat
+ * lp_oplock_break_wait_time() milliseconds before sending
+ * the packet. Sending the packet sooner can break Win9x
+ * and has reported to cause problems on NT. JRA.
+ */
+
+ wait_before_sending_break(local_request);
+
+ /* Prepare the SMBlockingX message. */
+
+ prepare_break_message( outbuf, fsp, False);
+ if (!send_smb(smbd_server_fd(), outbuf))
+ exit_server("oplock_break_level2: send_smb failed.");
+ }
+
+ /*
+ * Now we must update the shared memory structure to tell
+ * everyone else we no longer have a level II oplock on
+ * this open file. If local_request is true then token is
+ * the existing lock on the shared memory area.
+ */
+
+ if(!local_request && lock_share_entry_fsp(fsp) == False) {
+ DEBUG(0,("oplock_break_level2: unable to lock share entry for file %s\n", fsp->fsp_name ));
+ } else {
+ got_lock = True;
+ }
+
+ if(remove_share_oplock(fsp)==False) {
+ DEBUG(0,("oplock_break_level2: unable to remove level II oplock for file %s\n", fsp->fsp_name ));
+ }
+
+ release_file_oplock(fsp);
+
+ if (!local_request && got_lock)
+ unlock_share_entry_fsp(fsp);
+
+ if(level_II_oplocks_open < 0) {
+ DEBUG(0,("oplock_break_level2: level_II_oplocks_open < 0 (%d). PANIC ERROR\n",
+ level_II_oplocks_open));
+ abort();
+ }
+
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "oplock_break_level2: returning success for " );
+ dbgtext( "dev = %x, inode = %.0f, file_id = %lu\n", (unsigned int)dev, (double)inode, fsp->file_id );
+ dbgtext( "Current level II oplocks_open = %d\n", level_II_oplocks_open );
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Process an oplock break directly.
+****************************************************************************/
+
+static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_id, BOOL local_request)
+{
+ extern uint32 global_client_caps;
+ extern struct current_user current_user;
+ char *inbuf = NULL;
+ char *outbuf = NULL;
+ files_struct *fsp = NULL;
+ time_t start_time;
+ BOOL shutdown_server = False;
+ BOOL oplock_timeout = False;
+ BOOL sign_state;
+ connection_struct *saved_user_conn;
+ connection_struct *saved_fsp_conn;
+ int saved_vuid;
+ pstring saved_dir;
+ int timeout = (OPLOCK_BREAK_TIMEOUT * 1000);
+ pstring file_name;
+ BOOL using_levelII;
+
+ if((fsp = initial_break_processing(dev, inode, file_id)) == NULL)
+ return True;
+
+ /*
+ * Deal with a level II oplock going break to none separately.
+ */
+
+ if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type))
+ return oplock_break_level2(fsp, local_request, -1);
+
+ /* Mark the oplock break as sent - we don't want to send twice! */
+ if (fsp->sent_oplock_break) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "oplock_break: ERROR: oplock_break already sent for " );
+ dbgtext( "file %s ", fsp->fsp_name);
+ dbgtext( "(dev = %x, inode = %.0f, file_id = %lu)\n", (unsigned int)dev, (double)inode, fsp->file_id );
+ }
+
+ /*
+ * We have to fail the open here as we cannot send another oplock break on
+ * this file whilst we are awaiting a response from the client - neither
+ * can we allow another open to succeed while we are waiting for the client.
+ */
+ return False;
+ }
+
+ if(global_oplock_break) {
+ DEBUG(0,("ABORT : ABORT : recursion in oplock_break !!!!!\n"));
+ abort();
+ }
+
+ /*
+ * Now comes the horrid part. We must send an oplock break to the client,
+ * and then process incoming messages until we get a close or oplock release.
+ * At this point we know we need a new inbuf/outbuf buffer pair.
+ * We cannot use these staticaly as we may recurse into here due to
+ * messages crossing on the wire.
+ */
+
+ if((inbuf = (char *)malloc(BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN))==NULL) {
+ DEBUG(0,("oplock_break: malloc fail for input buffer.\n"));
+ return False;
+ }
+
+ if((outbuf = (char *)malloc(BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN))==NULL) {
+ DEBUG(0,("oplock_break: malloc fail for output buffer.\n"));
+ SAFE_FREE(inbuf);
+ return False;
+ }
+
+ /*
+ * If we are sending an oplock break due to an SMB sent
+ * by our own client we ensure that we wait at leat
+ * lp_oplock_break_wait_time() milliseconds before sending
+ * the packet. Sending the packet sooner can break Win9x
+ * and has reported to cause problems on NT. JRA.
+ */
+
+ wait_before_sending_break(local_request);
+
+ /* Prepare the SMBlockingX message. */
+
+ if ((global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
+ !koplocks && /* NOTE: we force levelII off for kernel oplocks - this will change when it is supported */
+ lp_level2_oplocks(SNUM(fsp->conn))) {
+ using_levelII = True;
+ } else {
+ using_levelII = False;
+ }
+
+ prepare_break_message( outbuf, fsp, using_levelII);
+ /* Remember if we just sent a break to level II on this file. */
+ fsp->sent_oplock_break = using_levelII? LEVEL_II_BREAK_SENT:EXCLUSIVE_BREAK_SENT;
+
+ /* Save the server smb signing state. */
+ sign_state = srv_oplock_set_signing(False);
+
+ if (!send_smb(smbd_server_fd(), outbuf)) {
+ srv_oplock_set_signing(sign_state);
+ exit_server("oplock_break: send_smb failed.");
+ }
+
+ /* Restore the sign state to what it was. */
+ srv_oplock_set_signing(sign_state);
+
+ /* We need this in case a readraw crosses on the wire. */
+ global_oplock_break = True;
+
+ /* Process incoming messages. */
+
+ /*
+ * JRA - If we don't get a break from the client in OPLOCK_BREAK_TIMEOUT
+ * seconds we should just die....
+ */
+
+ start_time = time(NULL);
+
+ /*
+ * Save the information we need to re-become the
+ * user, then unbecome the user whilst we're doing this.
+ */
+ saved_user_conn = current_user.conn;
+ saved_vuid = current_user.vuid;
+ saved_fsp_conn = fsp->conn;
+ change_to_root_user();
+ vfs_GetWd(saved_fsp_conn,saved_dir);
+ /* Save the chain fnum. */
+ file_chain_save();
+
+ /*
+ * From Charles Hoch <hoch@exemplary.com>. If the break processing
+ * code closes the file (as it often does), then the fsp pointer here
+ * points to free()'d memory. We *must* revalidate fsp each time
+ * around the loop.
+ */
+
+ pstrcpy(file_name, fsp->fsp_name);
+
+ while((fsp = initial_break_processing(dev, inode, file_id)) &&
+ OPEN_FSP(fsp) && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
+ if(receive_smb(smbd_server_fd(),inbuf, timeout) == False) {
+ /*
+ * Die if we got an error.
+ */
+
+ if (smb_read_error == READ_EOF) {
+ DEBUG( 0, ( "oplock_break: end of file from client\n" ) );
+ shutdown_server = True;
+ } else if (smb_read_error == READ_ERROR) {
+ DEBUG( 0, ("oplock_break: receive_smb error (%s)\n", strerror(errno)) );
+ shutdown_server = True;
+ } else if (smb_read_error == READ_BAD_SIG) {
+ DEBUG( 0, ("oplock_break: bad signature from client\n" ));
+ shutdown_server = True;
+ } else if (smb_read_error == READ_TIMEOUT) {
+ DEBUG( 0, ( "oplock_break: receive_smb timed out after %d seconds.\n", OPLOCK_BREAK_TIMEOUT ) );
+ oplock_timeout = True;
+ }
+
+ DEBUGADD( 0, ( "oplock_break failed for file %s ", file_name ) );
+ DEBUGADD( 0, ( "(dev = %x, inode = %.0f, file_id = %lu).\n",
+ (unsigned int)dev, (double)inode, file_id));
+
+ break;
+ }
+
+ /*
+ * There are certain SMB requests that we shouldn't allow
+ * to recurse. opens, renames and deletes are the obvious
+ * ones. This is handled in the switch_message() function.
+ * If global_oplock_break is set they will push the packet onto
+ * the pending smb queue and return -1 (no reply).
+ * JRA.
+ */
+
+ process_smb(inbuf, outbuf);
+
+ /*
+ * Die if we go over the time limit.
+ */
+
+ if((time(NULL) - start_time) > OPLOCK_BREAK_TIMEOUT) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "oplock_break: no break received from client " );
+ dbgtext( "within %d seconds.\n", OPLOCK_BREAK_TIMEOUT );
+ dbgtext( "oplock_break failed for file %s ", fsp->fsp_name );
+ dbgtext( "(dev = %x, inode = %.0f, file_id = %lu).\n",
+ (unsigned int)dev, (double)inode, file_id );
+ }
+ oplock_timeout = True;
+ break;
+ }
+ }
+
+ /*
+ * Go back to being the user who requested the oplock
+ * break.
+ */
+ if((saved_user_conn != NULL) && (saved_vuid != UID_FIELD_INVALID) && !change_to_user(saved_user_conn, saved_vuid)) {
+ DEBUG( 0, ( "oplock_break: unable to re-become user!" ) );
+ DEBUGADD( 0, ( "Shutting down server\n" ) );
+ close(oplock_sock);
+ exit_server("unable to re-become user");
+ }
+
+ /* Including the directory. */
+ vfs_ChDir(saved_fsp_conn,saved_dir);
+
+ /* Restore the chain fnum. */
+ file_chain_restore();
+
+ /* Free the buffers we've been using to recurse. */
+ SAFE_FREE(inbuf);
+ SAFE_FREE(outbuf);
+
+ /* We need this in case a readraw crossed on the wire. */
+ if(global_oplock_break)
+ global_oplock_break = False;
+
+ /*
+ * If the client timed out then clear the oplock (or go to level II)
+ * and continue. This seems to be what NT does and is better than dropping
+ * the connection.
+ */
+
+ if(oplock_timeout && (fsp = initial_break_processing(dev, inode, file_id)) &&
+ OPEN_FSP(fsp) && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
+ DEBUG(0,("oplock_break: client failure in oplock break in file %s\n", fsp->fsp_name));
+ remove_oplock(fsp,True);
+#if FASCIST_OPLOCK_BACKOFF
+ global_client_failed_oplock_break = True; /* Never grant this client an oplock again. */
+#endif
+ }
+
+ /*
+ * If the client had an error we must die.
+ */
+
+ if(shutdown_server) {
+ DEBUG( 0, ( "oplock_break: client failure in break - " ) );
+ DEBUGADD( 0, ( "shutting down this smbd.\n" ) );
+ close(oplock_sock);
+ exit_server("oplock break failure");
+ }
+
+ /* Santity check - remove this later. JRA */
+ if(exclusive_oplocks_open < 0) {
+ DEBUG(0,("oplock_break: exclusive_oplocks_open < 0 (%d). PANIC ERROR\n", exclusive_oplocks_open));
+ abort();
+ }
+
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "oplock_break: returning success for " );
+ dbgtext( "dev = %x, inode = %.0f, file_id = %lu\n", (unsigned int)dev, (double)inode, file_id );
+ dbgtext( "Current exclusive_oplocks_open = %d\n", exclusive_oplocks_open );
+ }
+
+ return True;
+}
+
+/****************************************************************************
+Send an oplock break message to another smbd process. If the oplock is held
+by the local smbd then call the oplock break function directly.
+****************************************************************************/
+
+BOOL request_oplock_break(share_mode_entry *share_entry, BOOL async)
+{
+ char op_break_msg[OPLOCK_BREAK_MSG_LEN];
+ struct sockaddr_in addr_out;
+ pid_t pid = sys_getpid();
+ time_t start_time;
+ int time_left;
+ SMB_DEV_T dev = share_entry->dev;
+ SMB_INO_T inode = share_entry->inode;
+ unsigned long file_id = share_entry->share_file_id;
+ uint16 break_cmd_type;
+
+ if(pid == share_entry->pid) {
+ /* We are breaking our own oplock, make sure it's us. */
+ if(share_entry->op_port != global_oplock_port) {
+ DEBUG(0,("request_oplock_break: corrupt share mode entry - pid = %d, port = %d \
+should be %d\n", (int)pid, share_entry->op_port, global_oplock_port));
+ return False;
+ }
+
+ DEBUG(5,("request_oplock_break: breaking our own oplock\n"));
+
+#if 1 /* JRA PARANOIA TEST.... */
+ {
+ files_struct *fsp = file_find_dif(dev, inode, file_id);
+ if (!fsp) {
+ DEBUG(0,("request_oplock_break: PANIC : breaking our own oplock requested for \
+dev = %x, inode = %.0f, file_id = %lu and no fsp found !\n",
+ (unsigned int)dev, (double)inode, file_id ));
+ smb_panic("request_oplock_break: no fsp found for our own oplock\n");
+ }
+ }
+#endif /* END JRA PARANOIA TEST... */
+
+ /* Call oplock break direct. */
+ return oplock_break(dev, inode, file_id, True);
+ }
+
+ /* We need to send a OPLOCK_BREAK_CMD message to the port in the share mode entry. */
+
+ if (LEVEL_II_OPLOCK_TYPE(share_entry->op_type)) {
+ break_cmd_type = async ? ASYNC_LEVEL_II_OPLOCK_BREAK_CMD : LEVEL_II_OPLOCK_BREAK_CMD;
+ } else {
+ break_cmd_type = OPLOCK_BREAK_CMD;
+ }
+
+ SSVAL(op_break_msg,OPBRK_MESSAGE_CMD_OFFSET,break_cmd_type);
+ memcpy(op_break_msg+OPLOCK_BREAK_PID_OFFSET,(char *)&pid,sizeof(pid));
+ memcpy(op_break_msg+OPLOCK_BREAK_DEV_OFFSET,(char *)&dev,sizeof(dev));
+ memcpy(op_break_msg+OPLOCK_BREAK_INODE_OFFSET,(char *)&inode,sizeof(inode));
+ memcpy(op_break_msg+OPLOCK_BREAK_FILEID_OFFSET,(char *)&file_id,sizeof(file_id));
+
+ /* Set the address and port. */
+ memset((char *)&addr_out,'\0',sizeof(addr_out));
+ addr_out.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ addr_out.sin_port = htons( share_entry->op_port );
+ addr_out.sin_family = AF_INET;
+
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "request_oplock_break: sending a %s oplock break message to ", async ? "asynchronous" : "synchronous" );
+ dbgtext( "pid %d on port %d ", (int)share_entry->pid, share_entry->op_port );
+ dbgtext( "for dev = %x, inode = %.0f, file_id = %lu\n",
+ (unsigned int)dev, (double)inode, file_id );
+ }
+
+ if(sys_sendto(oplock_sock,op_break_msg,OPLOCK_BREAK_MSG_LEN,0,
+ (struct sockaddr *)&addr_out,sizeof(addr_out)) < 0) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "request_oplock_break: failed when sending a oplock " );
+ dbgtext( "break message to pid %d ", (int)share_entry->pid );
+ dbgtext( "on port %d ", share_entry->op_port );
+ dbgtext( "for dev = %x, inode = %.0f, file_id = %lu\n",
+ (unsigned int)dev, (double)inode, file_id );
+ dbgtext( "Error was %s\n", strerror(errno) );
+ }
+ return False;
+ }
+
+ /*
+ * If we just sent a message to a level II oplock share entry in async mode then
+ * we are done and may return.
+ */
+
+ if (LEVEL_II_OPLOCK_TYPE(share_entry->op_type) && async) {
+ DEBUG(3,("request_oplock_break: sent async break message to level II entry.\n"));
+ return True;
+ }
+
+ /*
+ * Now we must await the oplock broken message coming back
+ * from the target smbd process. Timeout if it fails to
+ * return in (OPLOCK_BREAK_TIMEOUT + OPLOCK_BREAK_TIMEOUT_FUDGEFACTOR) seconds.
+ * While we get messages that aren't ours, loop.
+ */
+
+ start_time = time(NULL);
+ time_left = OPLOCK_BREAK_TIMEOUT+OPLOCK_BREAK_TIMEOUT_FUDGEFACTOR;
+
+ while(time_left >= 0) {
+ char op_break_reply[OPBRK_CMD_HEADER_LEN+OPLOCK_BREAK_MSG_LEN];
+ uint16 reply_from_port;
+ char *reply_msg_start;
+
+ if(receive_local_message(op_break_reply, sizeof(op_break_reply),
+ time_left ? time_left * 1000 : 1) == False) {
+ if(smb_read_error == READ_TIMEOUT) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "request_oplock_break: no response received to oplock " );
+ dbgtext( "break request to pid %d ", (int)share_entry->pid );
+ dbgtext( "on port %d ", share_entry->op_port );
+ dbgtext( "for dev = %x, inode = %.0f, file_id = %lu\n",
+ (unsigned int)dev, (double)inode, file_id );
+ }
+
+ /*
+ * This is a hack to make handling of failing clients more robust.
+ * If a oplock break response message is not received in the timeout
+ * period we may assume that the smbd servicing that client holding
+ * the oplock has died and the client changes were lost anyway, so
+ * we should continue to try and open the file.
+ */
+ break;
+ } else {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "request_oplock_break: error in response received " );
+ dbgtext( "to oplock break request to pid %d ", (int)share_entry->pid );
+ dbgtext( "on port %d ", share_entry->op_port );
+ dbgtext( "for dev = %x, inode = %.0f, file_id = %lu\n",
+ (unsigned int)dev, (double)inode, file_id );
+ dbgtext( "Error was (%s).\n", strerror(errno) );
+ }
+ }
+ return False;
+ }
+
+ reply_from_port = SVAL(op_break_reply,OPBRK_CMD_PORT_OFFSET);
+ reply_msg_start = &op_break_reply[OPBRK_CMD_HEADER_LEN];
+
+ /*
+ * Test to see if this is the reply we are awaiting (ie. the one we sent with the CMD_REPLY flag OR'ed in).
+ */
+ if((SVAL(reply_msg_start,OPBRK_MESSAGE_CMD_OFFSET) & CMD_REPLY) &&
+ ((SVAL(reply_msg_start,OPBRK_MESSAGE_CMD_OFFSET) & ~CMD_REPLY) == break_cmd_type) &&
+ (reply_from_port == share_entry->op_port) &&
+ (memcmp(&reply_msg_start[OPLOCK_BREAK_PID_OFFSET], &op_break_msg[OPLOCK_BREAK_PID_OFFSET],
+ OPLOCK_BREAK_MSG_LEN - OPLOCK_BREAK_PID_OFFSET) == 0)) {
+
+ /*
+ * This is the reply we've been waiting for.
+ */
+ break;
+ } else {
+ /*
+ * This is another message - a break request.
+ * Note that both kernel oplock break requests
+ * and UDP inter-smbd oplock break requests will
+ * be processed here.
+ *
+ * Process it to prevent potential deadlock.
+ * Note that the code in switch_message() prevents
+ * us from recursing into here as any SMB requests
+ * we might process that would cause another oplock
+ * break request to be made will be queued.
+ * JRA.
+ */
+
+ process_local_message(op_break_reply, sizeof(op_break_reply));
+ }
+
+ time_left -= (time(NULL) - start_time);
+ }
+
+ DEBUG(3,("request_oplock_break: broke oplock.\n"));
+
+ return True;
+}
+
+/****************************************************************************
+ Attempt to break an oplock on a file (if oplocked).
+ Returns True if the file was closed as a result of
+ the oplock break, False otherwise.
+ Used as a last ditch attempt to free a space in the
+ file table when we have run out.
+****************************************************************************/
+
+BOOL attempt_close_oplocked_file(files_struct *fsp)
+{
+ DEBUG(5,("attempt_close_oplocked_file: checking file %s.\n", fsp->fsp_name));
+
+ if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && !fsp->sent_oplock_break && (fsp->fd != -1)) {
+ /* Try and break the oplock. */
+ if (oplock_break(fsp->dev, fsp->inode, fsp->file_id, True)) {
+ if(file_find_fsp(fsp) == NULL) /* Did the oplock break close the file ? */
+ return True;
+ }
+ }
+
+ return False;
+}
+
+/****************************************************************************
+ This function is called on any file modification or lock request. If a file
+ is level 2 oplocked then it must tell all other level 2 holders to break to none.
+****************************************************************************/
+
+void release_level_2_oplocks_on_change(files_struct *fsp)
+{
+ share_mode_entry *share_list = NULL;
+ pid_t pid = sys_getpid();
+ int token = -1;
+ int num_share_modes = 0;
+ int i;
+
+ /*
+ * If this file is level II oplocked then we need
+ * to grab the shared memory lock and inform all
+ * other files with a level II lock that they need
+ * to flush their read caches. We keep the lock over
+ * the shared memory area whilst doing this.
+ */
+
+ if (!LEVEL_II_OPLOCK_TYPE(fsp->oplock_type))
+ return;
+
+ if (lock_share_entry_fsp(fsp) == False) {
+ DEBUG(0,("release_level_2_oplocks_on_change: failed to lock share mode entry for file %s.\n", fsp->fsp_name ));
+ }
+
+ num_share_modes = get_share_modes(fsp->conn, fsp->dev, fsp->inode, &share_list);
+
+ DEBUG(10,("release_level_2_oplocks_on_change: num_share_modes = %d\n",
+ num_share_modes ));
+
+ for(i = 0; i < num_share_modes; i++) {
+ share_mode_entry *share_entry = &share_list[i];
+
+ /*
+ * As there could have been multiple writes waiting at the lock_share_entry
+ * gate we may not be the first to enter. Hence the state of the op_types
+ * in the share mode entries may be partly NO_OPLOCK and partly LEVEL_II
+ * oplock. It will do no harm to re-send break messages to those smbd's
+ * that are still waiting their turn to remove their LEVEL_II state, and
+ * also no harm to ignore existing NO_OPLOCK states. JRA.
+ */
+
+ DEBUG(10,("release_level_2_oplocks_on_change: share_entry[%i]->op_type == %d\n",
+ i, share_entry->op_type ));
+
+ if (share_entry->op_type == NO_OPLOCK)
+ continue;
+
+ /* Paranoia .... */
+ if (EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type)) {
+ DEBUG(0,("release_level_2_oplocks_on_change: PANIC. share mode entry %d is an exlusive oplock !\n", i ));
+ unlock_share_entry(fsp->conn, fsp->dev, fsp->inode);
+ abort();
+ }
+
+ /*
+ * Check if this is a file we have open (including the
+ * file we've been called to do write_file on. If so
+ * then break it directly without releasing the lock.
+ */
+
+ if (pid == share_entry->pid) {
+ files_struct *new_fsp = file_find_dif(share_entry->dev, share_entry->inode, share_entry->share_file_id);
+
+ /* Paranoia check... */
+ if(new_fsp == NULL) {
+ DEBUG(0,("release_level_2_oplocks_on_change: PANIC. share mode entry %d is not a local file !\n", i ));
+ unlock_share_entry(fsp->conn, fsp->dev, fsp->inode);
+ abort();
+ }
+
+ DEBUG(10,("release_level_2_oplocks_on_change: breaking our own oplock.\n"));
+
+ oplock_break_level2(new_fsp, True, token);
+
+ } else {
+
+ /*
+ * This is a remote file and so we send an asynchronous
+ * message.
+ */
+
+ DEBUG(10,("release_level_2_oplocks_on_change: breaking remote oplock (async).\n"));
+ request_oplock_break(share_entry, True);
+ }
+ }
+
+ SAFE_FREE(share_list);
+ unlock_share_entry_fsp(fsp);
+
+ /* Paranoia check... */
+ if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type)) {
+ DEBUG(0,("release_level_2_oplocks_on_change: PANIC. File %s still has a level II oplock.\n", fsp->fsp_name));
+ smb_panic("release_level_2_oplocks_on_change");
+ }
+}
+
+/****************************************************************************
+setup oplocks for this process
+****************************************************************************/
+
+BOOL init_oplocks(void)
+{
+ struct sockaddr_in sock_name;
+ socklen_t len = sizeof(sock_name);
+
+ DEBUG(3,("open_oplock_ipc: opening loopback UDP socket.\n"));
+
+ /* Open a lookback UDP socket on a random port. */
+ oplock_sock = open_socket_in(SOCK_DGRAM, 0, 0, htonl(INADDR_LOOPBACK),False);
+ if (oplock_sock == -1) {
+ DEBUG(0,("open_oplock_ipc: Failed to get local UDP socket for \
+address %lx. Error was %s\n", (long)htonl(INADDR_LOOPBACK), strerror(errno)));
+ global_oplock_port = 0;
+ return(False);
+ }
+
+ /* Find out the transient UDP port we have been allocated. */
+ if(getsockname(oplock_sock, (struct sockaddr *)&sock_name, &len)<0) {
+ DEBUG(0,("open_oplock_ipc: Failed to get local UDP port. Error was %s\n",
+ strerror(errno)));
+ close(oplock_sock);
+ oplock_sock = -1;
+ global_oplock_port = 0;
+ return False;
+ }
+ global_oplock_port = ntohs(sock_name.sin_port);
+
+ if (lp_kernel_oplocks()) {
+#if HAVE_KERNEL_OPLOCKS_IRIX
+ koplocks = irix_init_kernel_oplocks();
+#elif HAVE_KERNEL_OPLOCKS_LINUX
+ koplocks = linux_init_kernel_oplocks();
+#endif
+ }
+
+ DEBUG(3,("open_oplock ipc: pid = %d, global_oplock_port = %u\n",
+ (int)sys_getpid(), global_oplock_port));
+
+ return True;
+}
diff --git a/source/smbd/oplock_irix.c b/source/smbd/oplock_irix.c
new file mode 100644
index 00000000000..ffcf3d0af4d
--- /dev/null
+++ b/source/smbd/oplock_irix.c
@@ -0,0 +1,285 @@
+/*
+ Unix SMB/CIFS implementation.
+ IRIX kernel oplock processing
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#if HAVE_KERNEL_OPLOCKS_IRIX
+
+static int oplock_pipe_write = -1;
+static int oplock_pipe_read = -1;
+
+/****************************************************************************
+ Test to see if IRIX kernel oplocks work.
+****************************************************************************/
+
+static BOOL irix_oplocks_available(void)
+{
+ int fd;
+ int pfd[2];
+ pstring tmpname;
+
+ oplock_set_capability(True, False);
+
+ slprintf(tmpname,sizeof(tmpname)-1, "%s/koplock.%d", lp_lockdir(), (int)sys_getpid());
+
+ if(pipe(pfd) != 0) {
+ DEBUG(0,("check_kernel_oplocks: Unable to create pipe. Error was %s\n",
+ strerror(errno) ));
+ return False;
+ }
+
+ if((fd = sys_open(tmpname, O_RDWR|O_CREAT|O_EXCL|O_TRUNC, 0600)) < 0) {
+ DEBUG(0,("check_kernel_oplocks: Unable to open temp test file %s. Error was %s\n",
+ tmpname, strerror(errno) ));
+ unlink( tmpname );
+ close(pfd[0]);
+ close(pfd[1]);
+ return False;
+ }
+
+ unlink(tmpname);
+
+ if(sys_fcntl_long(fd, F_OPLKREG, pfd[1]) == -1) {
+ DEBUG(0,("check_kernel_oplocks: Kernel oplocks are not available on this machine. \
+Disabling kernel oplock support.\n" ));
+ close(pfd[0]);
+ close(pfd[1]);
+ close(fd);
+ return False;
+ }
+
+ if(sys_fcntl_long(fd, F_OPLKACK, OP_REVOKE) < 0 ) {
+ DEBUG(0,("check_kernel_oplocks: Error when removing kernel oplock. Error was %s. \
+Disabling kernel oplock support.\n", strerror(errno) ));
+ close(pfd[0]);
+ close(pfd[1]);
+ close(fd);
+ return False;
+ }
+
+ close(pfd[0]);
+ close(pfd[1]);
+ close(fd);
+
+ return True;
+}
+
+/****************************************************************************
+ * Deal with the IRIX kernel <--> smbd
+ * oplock break protocol.
+****************************************************************************/
+
+static BOOL irix_oplock_receive_message(fd_set *fds, char *buffer, int buffer_len)
+{
+ extern int smb_read_error;
+ oplock_stat_t os;
+ char dummy;
+ files_struct *fsp;
+
+ /*
+ * Read one byte of zero to clear the
+ * kernel break notify message.
+ */
+
+ if(read(oplock_pipe_read, &dummy, 1) != 1) {
+ DEBUG(0,("irix_oplock_receive_message: read of kernel notification failed. \
+Error was %s.\n", strerror(errno) ));
+ smb_read_error = READ_ERROR;
+ return False;
+ }
+
+ /*
+ * Do a query to get the
+ * device and inode of the file that has the break
+ * request outstanding.
+ */
+
+ if(sys_fcntl_ptr(oplock_pipe_read, F_OPLKSTAT, &os) < 0) {
+ DEBUG(0,("irix_oplock_receive_message: fcntl of kernel notification failed. \
+Error was %s.\n", strerror(errno) ));
+ if(errno == EAGAIN) {
+ /*
+ * Duplicate kernel break message - ignore.
+ */
+ memset(buffer, '\0', KERNEL_OPLOCK_BREAK_MSG_LEN);
+ return True;
+ }
+ smb_read_error = READ_ERROR;
+ return False;
+ }
+
+ /*
+ * We only have device and inode info here - we have to guess that this
+ * is the first fsp open with this dev,ino pair.
+ */
+
+ if ((fsp = file_find_di_first((SMB_DEV_T)os.os_dev, (SMB_INO_T)os.os_ino)) == NULL) {
+ DEBUG(0,("irix_oplock_receive_message: unable to find open file with dev = %x, inode = %.0f\n",
+ (unsigned int)os.os_dev, (double)os.os_ino ));
+ return False;
+ }
+
+ DEBUG(5,("irix_oplock_receive_message: kernel oplock break request received for \
+dev = %x, inode = %.0f\n, file_id = %ul", (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id ));
+
+ /*
+ * Create a kernel oplock break message.
+ */
+
+ /* Setup the message header */
+ SIVAL(buffer,OPBRK_CMD_LEN_OFFSET,KERNEL_OPLOCK_BREAK_MSG_LEN);
+ SSVAL(buffer,OPBRK_CMD_PORT_OFFSET,0);
+
+ buffer += OPBRK_CMD_HEADER_LEN;
+
+ SSVAL(buffer,OPBRK_MESSAGE_CMD_OFFSET,KERNEL_OPLOCK_BREAK_CMD);
+
+ memcpy(buffer + KERNEL_OPLOCK_BREAK_DEV_OFFSET, (char *)&fsp->dev, sizeof(fsp->dev));
+ memcpy(buffer + KERNEL_OPLOCK_BREAK_INODE_OFFSET, (char *)&fsp->inode, sizeof(fsp->inode));
+ memcpy(buffer + KERNEL_OPLOCK_BREAK_FILEID_OFFSET, (char *)&fsp->file_id, sizeof(fsp->file_id));
+
+ return True;
+}
+
+/****************************************************************************
+ Attempt to set an kernel oplock on a file.
+****************************************************************************/
+
+static BOOL irix_set_kernel_oplock(files_struct *fsp, int oplock_type)
+{
+ if (sys_fcntl_long(fsp->fd, F_OPLKREG, oplock_pipe_write) == -1) {
+ if(errno != EAGAIN) {
+ DEBUG(0,("irix_set_kernel_oplock: Unable to get kernel oplock on file %s, dev = %x, \
+inode = %.0f, file_id = %ul. Error was %s\n",
+ fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id,
+ strerror(errno) ));
+ } else {
+ DEBUG(5,("irix_set_kernel_oplock: Refused oplock on file %s, fd = %d, dev = %x, \
+inode = %.0f, file_id = %ul. Another process had the file open.\n",
+ fsp->fsp_name, fsp->fd, (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id ));
+ }
+ return False;
+ }
+
+ DEBUG(10,("irix_set_kernel_oplock: got kernel oplock on file %s, dev = %x, inode = %.0f, file_id = %ul\n",
+ fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id));
+
+ return True;
+}
+
+/****************************************************************************
+ Release a kernel oplock on a file.
+****************************************************************************/
+
+static void irix_release_kernel_oplock(files_struct *fsp)
+{
+ if (DEBUGLVL(10)) {
+ /*
+ * Check and print out the current kernel
+ * oplock state of this file.
+ */
+ int state = sys_fcntl_long(fsp->fd, F_OPLKACK, -1);
+ dbgtext("irix_release_kernel_oplock: file %s, dev = %x, inode = %.0f file_id = %ul, has kernel \
+oplock state of %x.\n", fsp->fsp_name, (unsigned int)fsp->dev,
+ (double)fsp->inode, fsp->file_id, state );
+ }
+
+ /*
+ * Remove the kernel oplock on this file.
+ */
+ if(sys_fcntl_long(fsp->fd, F_OPLKACK, OP_REVOKE) < 0) {
+ if( DEBUGLVL( 0 )) {
+ dbgtext("irix_release_kernel_oplock: Error when removing kernel oplock on file " );
+ dbgtext("%s, dev = %x, inode = %.0f, file_id = %ul. Error was %s\n",
+ fsp->fsp_name, (unsigned int)fsp->dev,
+ (double)fsp->inode, fsp->file_id, strerror(errno) );
+ }
+ }
+}
+
+/****************************************************************************
+ Parse a kernel oplock message.
+****************************************************************************/
+
+static BOOL irix_kernel_oplock_parse(char *msg_start, int msg_len,
+ SMB_INO_T *inode, SMB_DEV_T *dev, unsigned long *file_id)
+{
+ /* Ensure that the msg length is correct. */
+ if(msg_len != KERNEL_OPLOCK_BREAK_MSG_LEN) {
+ DEBUG(0,("incorrect length for KERNEL_OPLOCK_BREAK_CMD (was %d, should be %d).\n",
+ msg_len, KERNEL_OPLOCK_BREAK_MSG_LEN));
+ return False;
+ }
+
+ memcpy((char *)inode, msg_start+KERNEL_OPLOCK_BREAK_INODE_OFFSET, sizeof(*inode));
+ memcpy((char *)dev, msg_start+KERNEL_OPLOCK_BREAK_DEV_OFFSET, sizeof(*dev));
+ memcpy((char *)file_id, msg_start+KERNEL_OPLOCK_BREAK_FILEID_OFFSET, sizeof(*file_id));
+
+ DEBUG(5,("kernel oplock break request for file dev = %x, inode = %.0f, file_id = %ul\n",
+ (unsigned int)*dev, (double)*inode, *file_id));
+
+ return True;
+}
+
+/****************************************************************************
+ Set *maxfd to include oplock read pipe.
+****************************************************************************/
+
+static BOOL irix_oplock_msg_waiting(fd_set *fds)
+{
+ if (oplock_pipe_read == -1)
+ return False;
+
+ return FD_ISSET(oplock_pipe_read,fds);
+}
+
+/****************************************************************************
+ Setup kernel oplocks.
+****************************************************************************/
+
+struct kernel_oplocks *irix_init_kernel_oplocks(void)
+{
+ int pfd[2];
+ static struct kernel_oplocks koplocks;
+
+ if (!irix_oplocks_available())
+ return NULL;
+
+ if(pipe(pfd) != 0) {
+ DEBUG(0,("setup_kernel_oplock_pipe: Unable to create pipe. Error was %s\n",
+ strerror(errno) ));
+ return False;
+ }
+
+ oplock_pipe_read = pfd[0];
+ oplock_pipe_write = pfd[1];
+
+ koplocks.receive_message = irix_oplock_receive_message;
+ koplocks.set_oplock = irix_set_kernel_oplock;
+ koplocks.release_oplock = irix_release_kernel_oplock;
+ koplocks.parse_message = irix_kernel_oplock_parse;
+ koplocks.msg_waiting = irix_oplock_msg_waiting;
+ koplocks.notification_fd = oplock_pipe_read;
+
+ return &koplocks;
+}
+#else
+ void oplock_irix_dummy(void) {}
+#endif /* HAVE_KERNEL_OPLOCKS_IRIX */
diff --git a/source/smbd/oplock_linux.c b/source/smbd/oplock_linux.c
new file mode 100644
index 00000000000..5de9dd56e68
--- /dev/null
+++ b/source/smbd/oplock_linux.c
@@ -0,0 +1,309 @@
+/*
+ Unix SMB/CIFS implementation.
+ kernel oplock processing for Linux
+ Copyright (C) Andrew Tridgell 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#if HAVE_KERNEL_OPLOCKS_LINUX
+
+static SIG_ATOMIC_T signals_received;
+#define FD_PENDING_SIZE 100
+static SIG_ATOMIC_T fd_pending_array[FD_PENDING_SIZE];
+
+#ifndef F_SETLEASE
+#define F_SETLEASE 1024
+#endif
+
+#ifndef F_GETLEASE
+#define F_GETLEASE 1025
+#endif
+
+#ifndef CAP_LEASE
+#define CAP_LEASE 28
+#endif
+
+#ifndef RT_SIGNAL_LEASE
+#define RT_SIGNAL_LEASE (SIGRTMIN+1)
+#endif
+
+#ifndef F_SETSIG
+#define F_SETSIG 10
+#endif
+
+/****************************************************************************
+ Handle a LEASE signal, incrementing the signals_received and blocking the signal.
+****************************************************************************/
+
+static void signal_handler(int sig, siginfo_t *info, void *unused)
+{
+ if (signals_received < FD_PENDING_SIZE - 1) {
+ fd_pending_array[signals_received] = (SIG_ATOMIC_T)info->si_fd;
+ signals_received++;
+ } /* Else signal is lost. */
+ sys_select_signal();
+}
+
+/****************************************************************************
+ Try to gain a linux capability.
+****************************************************************************/
+
+static void set_capability(unsigned capability)
+{
+#ifndef _LINUX_CAPABILITY_VERSION
+#define _LINUX_CAPABILITY_VERSION 0x19980330
+#endif
+ /* these can be removed when they are in glibc headers */
+ struct {
+ uint32 version;
+ int pid;
+ } header;
+ struct {
+ uint32 effective;
+ uint32 permitted;
+ uint32 inheritable;
+ } data;
+
+ header.version = _LINUX_CAPABILITY_VERSION;
+ header.pid = 0;
+
+ if (capget(&header, &data) == -1) {
+ DEBUG(3,("Unable to get kernel capabilities (%s)\n", strerror(errno)));
+ return;
+ }
+
+ data.effective |= (1<<capability);
+
+ if (capset(&header, &data) == -1) {
+ DEBUG(3,("Unable to set %d capability (%s)\n",
+ capability, strerror(errno)));
+ }
+}
+
+/****************************************************************************
+ Call SETLEASE. If we get EACCES then we try setting up the right capability and
+ try again
+****************************************************************************/
+
+static int linux_setlease(int fd, int leasetype)
+{
+ int ret;
+
+ if (fcntl(fd, F_SETSIG, RT_SIGNAL_LEASE) == -1) {
+ DEBUG(3,("Failed to set signal handler for kernel lease\n"));
+ return -1;
+ }
+
+ ret = fcntl(fd, F_SETLEASE, leasetype);
+ if (ret == -1 && errno == EACCES) {
+ set_capability(CAP_LEASE);
+ ret = fcntl(fd, F_SETLEASE, leasetype);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Deal with the Linux kernel <--> smbd
+ * oplock break protocol.
+****************************************************************************/
+
+static BOOL linux_oplock_receive_message(fd_set *fds, char *buffer, int buffer_len)
+{
+ int fd;
+ struct files_struct *fsp;
+
+ BlockSignals(True, RT_SIGNAL_LEASE);
+ fd = fd_pending_array[0];
+ fsp = file_find_fd(fd);
+ fd_pending_array[0] = (SIG_ATOMIC_T)-1;
+ if (signals_received > 1)
+ memmove((void *)&fd_pending_array[0], (void *)&fd_pending_array[1],
+ sizeof(SIG_ATOMIC_T)*(signals_received-1));
+ signals_received--;
+ /* now we can receive more signals */
+ BlockSignals(False, RT_SIGNAL_LEASE);
+
+ if (fsp == NULL) {
+ DEBUG(0,("Invalid file descriptor %d in kernel oplock break!\n", (int)fd));
+ return False;
+ }
+
+ DEBUG(3,("linux_oplock_receive_message: kernel oplock break request received for \
+dev = %x, inode = %.0f fd = %d, fileid = %lu \n", (unsigned int)fsp->dev, (double)fsp->inode,
+ fd, fsp->file_id));
+
+ /*
+ * Create a kernel oplock break message.
+ */
+
+ /* Setup the message header */
+ SIVAL(buffer,OPBRK_CMD_LEN_OFFSET,KERNEL_OPLOCK_BREAK_MSG_LEN);
+ SSVAL(buffer,OPBRK_CMD_PORT_OFFSET,0);
+
+ buffer += OPBRK_CMD_HEADER_LEN;
+
+ SSVAL(buffer,OPBRK_MESSAGE_CMD_OFFSET,KERNEL_OPLOCK_BREAK_CMD);
+
+ memcpy(buffer + KERNEL_OPLOCK_BREAK_DEV_OFFSET, (char *)&fsp->dev, sizeof(fsp->dev));
+ memcpy(buffer + KERNEL_OPLOCK_BREAK_INODE_OFFSET, (char *)&fsp->inode, sizeof(fsp->inode));
+ memcpy(buffer + KERNEL_OPLOCK_BREAK_FILEID_OFFSET, (char *)&fsp->file_id, sizeof(fsp->file_id));
+
+ return True;
+}
+
+/****************************************************************************
+ Attempt to set an kernel oplock on a file.
+****************************************************************************/
+
+static BOOL linux_set_kernel_oplock(files_struct *fsp, int oplock_type)
+{
+ if (linux_setlease(fsp->fd, F_WRLCK) == -1) {
+ DEBUG(3,("linux_set_kernel_oplock: Refused oplock on file %s, fd = %d, dev = %x, \
+inode = %.0f. (%s)\n",
+ fsp->fsp_name, fsp->fd,
+ (unsigned int)fsp->dev, (double)fsp->inode, strerror(errno)));
+ return False;
+ }
+
+ DEBUG(3,("linux_set_kernel_oplock: got kernel oplock on file %s, dev = %x, inode = %.0f, file_id = %lu\n",
+ fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id));
+
+ return True;
+}
+
+/****************************************************************************
+ Release a kernel oplock on a file.
+****************************************************************************/
+
+static void linux_release_kernel_oplock(files_struct *fsp)
+{
+ if (DEBUGLVL(10)) {
+ /*
+ * Check and print out the current kernel
+ * oplock state of this file.
+ */
+ int state = fcntl(fsp->fd, F_GETLEASE, 0);
+ dbgtext("linux_release_kernel_oplock: file %s, dev = %x, inode = %.0f file_id = %lu has kernel \
+oplock state of %x.\n", fsp->fsp_name, (unsigned int)fsp->dev,
+ (double)fsp->inode, fsp->file_id, state );
+ }
+
+ /*
+ * Remove the kernel oplock on this file.
+ */
+ if (linux_setlease(fsp->fd, F_UNLCK) == -1) {
+ if (DEBUGLVL(0)) {
+ dbgtext("linux_release_kernel_oplock: Error when removing kernel oplock on file " );
+ dbgtext("%s, dev = %x, inode = %.0f, file_id = %lu. Error was %s\n",
+ fsp->fsp_name, (unsigned int)fsp->dev,
+ (double)fsp->inode, fsp->file_id, strerror(errno) );
+ }
+ }
+}
+
+/****************************************************************************
+ Parse a kernel oplock message.
+****************************************************************************/
+
+static BOOL linux_kernel_oplock_parse(char *msg_start, int msg_len, SMB_INO_T *inode,
+ SMB_DEV_T *dev, unsigned long *file_id)
+{
+ /* Ensure that the msg length is correct. */
+ if (msg_len != KERNEL_OPLOCK_BREAK_MSG_LEN) {
+ DEBUG(0,("incorrect length for KERNEL_OPLOCK_BREAK_CMD (was %d, should be %lu).\n",
+ msg_len, (unsigned long)KERNEL_OPLOCK_BREAK_MSG_LEN));
+ return False;
+ }
+
+ memcpy((char *)inode, msg_start+KERNEL_OPLOCK_BREAK_INODE_OFFSET, sizeof(*inode));
+ memcpy((char *)dev, msg_start+KERNEL_OPLOCK_BREAK_DEV_OFFSET, sizeof(*dev));
+ memcpy((char *)file_id, msg_start+KERNEL_OPLOCK_BREAK_FILEID_OFFSET, sizeof(*file_id));
+
+ DEBUG(3,("kernel oplock break request for file dev = %x, inode = %.0f, file_id = %lu\n",
+ (unsigned int)*dev, (double)*inode, *file_id));
+
+ return True;
+}
+
+/****************************************************************************
+ See if a oplock message is waiting.
+****************************************************************************/
+
+static BOOL linux_oplock_msg_waiting(fd_set *fds)
+{
+ return signals_received != 0;
+}
+
+/****************************************************************************
+ See if the kernel supports oplocks.
+****************************************************************************/
+
+static BOOL linux_oplocks_available(void)
+{
+ int fd, ret;
+ fd = open("/dev/null", O_RDONLY);
+ if (fd == -1)
+ return False; /* uggh! */
+ ret = fcntl(fd, F_GETLEASE, 0);
+ close(fd);
+ return ret == F_UNLCK;
+}
+
+/****************************************************************************
+ Setup kernel oplocks.
+****************************************************************************/
+
+struct kernel_oplocks *linux_init_kernel_oplocks(void)
+{
+ static struct kernel_oplocks koplocks;
+ struct sigaction act;
+
+ if (!linux_oplocks_available()) {
+ DEBUG(3,("Linux kernel oplocks not available\n"));
+ return NULL;
+ }
+
+ ZERO_STRUCT(act);
+
+ act.sa_handler = NULL;
+ act.sa_sigaction = signal_handler;
+ act.sa_flags = SA_SIGINFO;
+ sigemptyset( &act.sa_mask );
+ if (sigaction(RT_SIGNAL_LEASE, &act, NULL) != 0) {
+ DEBUG(0,("Failed to setup RT_SIGNAL_LEASE handler\n"));
+ return NULL;
+ }
+
+ koplocks.receive_message = linux_oplock_receive_message;
+ koplocks.set_oplock = linux_set_kernel_oplock;
+ koplocks.release_oplock = linux_release_kernel_oplock;
+ koplocks.parse_message = linux_kernel_oplock_parse;
+ koplocks.msg_waiting = linux_oplock_msg_waiting;
+ koplocks.notification_fd = -1;
+
+ /* the signal can start off blocked due to a bug in bash */
+ BlockSignals(False, RT_SIGNAL_LEASE);
+
+ DEBUG(3,("Linux kernel oplocks enabled\n"));
+
+ return &koplocks;
+}
+#else
+ void oplock_linux_dummy(void) {}
+#endif /* HAVE_KERNEL_OPLOCKS_LINUX */
diff --git a/source/smbd/password.c b/source/smbd/password.c
new file mode 100644
index 00000000000..9f6dad423ad
--- /dev/null
+++ b/source/smbd/password.c
@@ -0,0 +1,551 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* users from session setup */
+static pstring session_users="";
+
+/* this holds info on user ids that are already validated for this VC */
+static user_struct *validated_users;
+static int next_vuid = VUID_OFFSET;
+static int num_validated_vuids;
+
+extern userdom_struct current_user_info;
+
+
+/****************************************************************************
+ Check if a uid has been validated, and return an pointer to the user_struct
+ if it has. NULL if not. vuid is biased by an offset. This allows us to
+ tell random client vuid's (normally zero) from valid vuids.
+****************************************************************************/
+
+user_struct *get_valid_user_struct(uint16 vuid)
+{
+ user_struct *usp;
+ int count=0;
+
+ if (vuid == UID_FIELD_INVALID)
+ return NULL;
+
+ for (usp=validated_users;usp;usp=usp->next,count++) {
+ if (vuid == usp->vuid) {
+ if (count > 10) {
+ DLIST_PROMOTE(validated_users, usp);
+ }
+ return usp;
+ }
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Invalidate a uid.
+****************************************************************************/
+
+void invalidate_vuid(uint16 vuid)
+{
+ user_struct *vuser = get_valid_user_struct(vuid);
+
+ if (vuser == NULL)
+ return;
+
+ SAFE_FREE(vuser->homedir);
+ SAFE_FREE(vuser->unix_homedir);
+ SAFE_FREE(vuser->logon_script);
+
+ session_yield(vuser);
+ SAFE_FREE(vuser->session_keystr);
+
+ free_server_info(&vuser->server_info);
+
+ data_blob_free(&vuser->session_key);
+
+ DLIST_REMOVE(validated_users, vuser);
+
+ /* clear the vuid from the 'cache' on each connection, and
+ from the vuid 'owner' of connections */
+ conn_clear_vuid_cache(vuid);
+
+ SAFE_FREE(vuser->groups);
+ delete_nt_token(&vuser->nt_user_token);
+ destroy_privilege(&vuser->privs);
+ SAFE_FREE(vuser);
+ num_validated_vuids--;
+}
+
+/****************************************************************************
+ Invalidate all vuid entries for this process.
+****************************************************************************/
+
+void invalidate_all_vuids(void)
+{
+ user_struct *usp, *next=NULL;
+
+ for (usp=validated_users;usp;usp=next) {
+ next = usp->next;
+
+ invalidate_vuid(usp->vuid);
+ }
+}
+
+/**
+ * register that a valid login has been performed, establish 'session'.
+ * @param server_info The token returned from the authentication process.
+ * (now 'owned' by register_vuid)
+ *
+ * @param session_key The User session key for the login session (now also 'owned' by register_vuid)
+ *
+ * @param respose_blob The NT challenge-response, if available. (May be freed after this call)
+ *
+ * @param smb_name The untranslated name of the user
+ *
+ * @return Newly allocated vuid, biased by an offset. (This allows us to
+ * tell random client vuid's (normally zero) from valid vuids.)
+ *
+ */
+
+int register_vuid(auth_serversupplied_info *server_info, DATA_BLOB session_key, DATA_BLOB response_blob, const char *smb_name)
+{
+ user_struct *vuser = NULL;
+
+ /* Ensure no vuid gets registered in share level security. */
+ if(lp_security() == SEC_SHARE) {
+ data_blob_free(&session_key);
+ return UID_FIELD_INVALID;
+ }
+
+ /* Limit allowed vuids to 16bits - VUID_OFFSET. */
+ if (num_validated_vuids >= 0xFFFF-VUID_OFFSET) {
+ data_blob_free(&session_key);
+ return UID_FIELD_INVALID;
+ }
+
+ if((vuser = (user_struct *)malloc( sizeof(user_struct) )) == NULL) {
+ DEBUG(0,("Failed to malloc users struct!\n"));
+ data_blob_free(&session_key);
+ return UID_FIELD_INVALID;
+ }
+
+ ZERO_STRUCTP(vuser);
+
+ /* Allocate a free vuid. Yes this is a linear search... :-) */
+ while( get_valid_user_struct(next_vuid) != NULL ) {
+ next_vuid++;
+ /* Check for vuid wrap. */
+ if (next_vuid == UID_FIELD_INVALID)
+ next_vuid = VUID_OFFSET;
+ }
+
+ DEBUG(10,("register_vuid: allocated vuid = %u\n", (unsigned int)next_vuid ));
+
+ vuser->vuid = next_vuid;
+
+ /* the next functions should be done by a SID mapping system (SMS) as
+ * the new real sam db won't have reference to unix uids or gids
+ */
+
+ vuser->uid = server_info->uid;
+ vuser->gid = server_info->gid;
+
+ vuser->n_groups = server_info->n_groups;
+ if (vuser->n_groups) {
+ if (!(vuser->groups = memdup(server_info->groups, sizeof(gid_t) * vuser->n_groups))) {
+ DEBUG(0,("register_vuid: failed to memdup vuser->groups\n"));
+ data_blob_free(&session_key);
+ free(vuser);
+ free_server_info(&server_info);
+ return UID_FIELD_INVALID;
+ }
+ }
+
+ vuser->guest = server_info->guest;
+ fstrcpy(vuser->user.unix_name, server_info->unix_name);
+
+ /* This is a potentially untrusted username */
+ alpha_strcpy(vuser->user.smb_name, smb_name, ". _-$", sizeof(vuser->user.smb_name));
+
+ fstrcpy(vuser->user.domain, pdb_get_domain(server_info->sam_account));
+ fstrcpy(vuser->user.full_name, pdb_get_fullname(server_info->sam_account));
+
+ {
+ /* Keep the homedir handy */
+ const char *homedir = pdb_get_homedir(server_info->sam_account);
+ const char *logon_script = pdb_get_logon_script(server_info->sam_account);
+
+ if (!IS_SAM_DEFAULT(server_info->sam_account, PDB_UNIXHOMEDIR)) {
+ const char *unix_homedir = pdb_get_unix_homedir(server_info->sam_account);
+ if (unix_homedir) {
+ vuser->unix_homedir = smb_xstrdup(unix_homedir);
+ }
+ } else {
+ struct passwd *passwd = getpwnam_alloc(vuser->user.unix_name);
+ if (passwd) {
+ vuser->unix_homedir = smb_xstrdup(passwd->pw_dir);
+ passwd_free(&passwd);
+ }
+ }
+
+ if (homedir) {
+ vuser->homedir = smb_xstrdup(homedir);
+ }
+ if (logon_script) {
+ vuser->logon_script = smb_xstrdup(logon_script);
+ }
+ }
+
+ vuser->session_key = session_key;
+
+ DEBUG(10,("register_vuid: (%u,%u) %s %s %s guest=%d\n",
+ (unsigned int)vuser->uid,
+ (unsigned int)vuser->gid,
+ vuser->user.unix_name, vuser->user.smb_name, vuser->user.domain, vuser->guest ));
+
+ DEBUG(3, ("User name: %s\tReal name: %s\n",vuser->user.unix_name,vuser->user.full_name));
+
+ if (server_info->ptok) {
+ vuser->nt_user_token = dup_nt_token(server_info->ptok);
+ } else {
+ DEBUG(1, ("server_info does not contain a user_token - cannot continue\n"));
+ free_server_info(&server_info);
+ data_blob_free(&session_key);
+ SAFE_FREE(vuser->homedir);
+ SAFE_FREE(vuser->unix_homedir);
+ SAFE_FREE(vuser->logon_script);
+
+ SAFE_FREE(vuser);
+ return UID_FIELD_INVALID;
+ }
+
+ if (server_info->privs) {
+ init_privilege(&(vuser->privs));
+ dup_priv_set(vuser->privs, server_info->privs);
+ }
+
+ /* use this to keep tabs on all our info from the authentication */
+ vuser->server_info = server_info;
+
+ DEBUG(3,("UNIX uid %d is UNIX user %s, and will be vuid %u\n",(int)vuser->uid,vuser->user.unix_name, vuser->vuid));
+
+ next_vuid++;
+ num_validated_vuids++;
+
+ DLIST_ADD(validated_users, vuser);
+
+ if (!session_claim(vuser)) {
+ DEBUG(1,("Failed to claim session for vuid=%d\n", vuser->vuid));
+ invalidate_vuid(vuser->vuid);
+ return -1;
+ }
+
+ /* Register a home dir service for this user iff
+ (a) This is not a guest connection,
+ (b) we have a home directory defined, and
+ (c) there s not an existing static share by that name */
+
+ if ( (!vuser->guest)
+ && vuser->unix_homedir
+ && *(vuser->unix_homedir)
+ && (lp_servicenumber(vuser->user.unix_name) == -1) )
+ {
+ DEBUG(3, ("Adding/updating homes service for user '%s' using home directory: '%s'\n",
+ vuser->user.unix_name, vuser->unix_homedir));
+
+ vuser->homes_snum = add_home_service(vuser->user.unix_name,
+ vuser->user.unix_name, vuser->unix_homedir);
+ } else {
+ vuser->homes_snum = -1;
+ }
+
+ if (srv_is_signing_negotiated() && !vuser->guest && !srv_signing_started()) {
+ /* Try and turn on server signing on the first non-guest sessionsetup. */
+ srv_set_signing(vuser->session_key, response_blob);
+ }
+
+ /* fill in the current_user_info struct */
+ set_current_user_info( &vuser->user );
+
+
+ return vuser->vuid;
+}
+
+/****************************************************************************
+ Add a name to the session users list.
+****************************************************************************/
+
+void add_session_user(const char *user)
+{
+ fstring suser;
+ struct passwd *passwd;
+
+ if (!(passwd = Get_Pwnam(user)))
+ return;
+
+ fstrcpy(suser,passwd->pw_name);
+
+ if (suser && *suser && !in_list(suser,session_users,False)) {
+ if (strlen(suser) + strlen(session_users) + 2 >= sizeof(pstring)) {
+ DEBUG(1,("Too many session users??\n"));
+ } else {
+ pstrcat(session_users," ");
+ pstrcat(session_users,suser);
+ }
+ }
+}
+
+/****************************************************************************
+ Check if a username is valid.
+****************************************************************************/
+
+BOOL user_ok(const char *user,int snum, gid_t *groups, size_t n_groups)
+{
+ char **valid, **invalid;
+ BOOL ret;
+
+ valid = invalid = NULL;
+ ret = True;
+
+ if (lp_invalid_users(snum)) {
+ str_list_copy(&invalid, lp_invalid_users(snum));
+ if (invalid && str_list_substitute(invalid, "%S", lp_servicename(snum))) {
+ if ( invalid && str_list_sub_basic(invalid, current_user_info.smb_name) ) {
+ ret = !user_in_list(user, (const char **)invalid, groups, n_groups);
+ }
+ }
+ }
+ if (invalid)
+ str_list_free (&invalid);
+
+ if (ret && lp_valid_users(snum)) {
+ str_list_copy(&valid, lp_valid_users(snum));
+ if ( valid && str_list_substitute(valid, "%S", lp_servicename(snum)) ) {
+ if ( valid && str_list_sub_basic(valid, current_user_info.smb_name) ) {
+ ret = user_in_list(user, (const char **)valid, groups, n_groups);
+ }
+ }
+ }
+ if (valid)
+ str_list_free (&valid);
+
+ if (ret && lp_onlyuser(snum)) {
+ char **user_list = str_list_make (lp_username(snum), NULL);
+ if (user_list && str_list_substitute(user_list, "%S", lp_servicename(snum))) {
+ ret = user_in_list(user, (const char **)user_list, groups, n_groups);
+ }
+ if (user_list) str_list_free (&user_list);
+ }
+
+ return(ret);
+}
+
+/****************************************************************************
+ Validate a group username entry. Return the username or NULL.
+****************************************************************************/
+
+static char *validate_group(char *group, DATA_BLOB password,int snum)
+{
+#ifdef HAVE_NETGROUP
+ {
+ char *host, *user, *domain;
+ setnetgrent(group);
+ while (getnetgrent(&host, &user, &domain)) {
+ if (user) {
+ if (user_ok(user, snum, NULL, 0) &&
+ password_ok(user,password)) {
+ endnetgrent();
+ return(user);
+ }
+ }
+ }
+ endnetgrent();
+ }
+#endif
+
+#ifdef HAVE_GETGRENT
+ {
+ struct group *gptr;
+ setgrent();
+ while ((gptr = (struct group *)getgrent())) {
+ if (strequal(gptr->gr_name,group))
+ break;
+ }
+
+ /*
+ * As user_ok can recurse doing a getgrent(), we must
+ * copy the member list into a pstring on the stack before
+ * use. Bug pointed out by leon@eatworms.swmed.edu.
+ */
+
+ if (gptr) {
+ pstring member_list;
+ char *member;
+ size_t copied_len = 0;
+ int i;
+
+ *member_list = '\0';
+ member = member_list;
+
+ for(i = 0; gptr->gr_mem && gptr->gr_mem[i]; i++) {
+ size_t member_len = strlen(gptr->gr_mem[i]) + 1;
+ if( copied_len + member_len < sizeof(pstring)) {
+
+ DEBUG(10,("validate_group: = gr_mem = %s\n", gptr->gr_mem[i]));
+
+ safe_strcpy(member, gptr->gr_mem[i], sizeof(pstring) - copied_len - 1);
+ copied_len += member_len;
+ member += copied_len;
+ } else {
+ *member = '\0';
+ }
+ }
+
+ endgrent();
+
+ member = member_list;
+ while (*member) {
+ static fstring name;
+ fstrcpy(name,member);
+ if (user_ok(name,snum, NULL, 0) &&
+ password_ok(name,password)) {
+ endgrent();
+ return(&name[0]);
+ }
+
+ DEBUG(10,("validate_group = member = %s\n", member));
+
+ member += strlen(member) + 1;
+ }
+ } else {
+ endgrent();
+ return NULL;
+ }
+ }
+#endif
+ return(NULL);
+}
+
+/****************************************************************************
+ Check for authority to login to a service with a given username/password.
+ Note this is *NOT* used when logging on using sessionsetup_and_X.
+****************************************************************************/
+
+BOOL authorise_login(int snum, fstring user, DATA_BLOB password,
+ BOOL *guest)
+{
+ BOOL ok = False;
+
+#if DEBUG_PASSWORD
+ DEBUG(100,("authorise_login: checking authorisation on user=%s pass=%s\n",
+ user,password.data));
+#endif
+
+ *guest = False;
+
+ /* there are several possibilities:
+ 1) login as the given user with given password
+ 2) login as a previously registered username with the given password
+ 3) login as a session list username with the given password
+ 4) login as a previously validated user/password pair
+ 5) login as the "user =" user with given password
+ 6) login as the "user =" user with no password (guest connection)
+ 7) login as guest user with no password
+
+ if the service is guest_only then steps 1 to 5 are skipped
+ */
+
+ /* now check the list of session users */
+ if (!ok) {
+ char *auser;
+ char *user_list = strdup(session_users);
+ if (!user_list)
+ return(False);
+
+ for (auser=strtok(user_list,LIST_SEP); !ok && auser;
+ auser = strtok(NULL,LIST_SEP)) {
+ fstring user2;
+ fstrcpy(user2,auser);
+ if (!user_ok(user2,snum, NULL, 0))
+ continue;
+
+ if (password_ok(user2,password)) {
+ ok = True;
+ fstrcpy(user,user2);
+ DEBUG(3,("authorise_login: ACCEPTED: session list username (%s) \
+and given password ok\n", user));
+ }
+ }
+
+ SAFE_FREE(user_list);
+ }
+
+ /* check the user= fields and the given password */
+ if (!ok && lp_username(snum)) {
+ char *auser;
+ pstring user_list;
+ pstrcpy(user_list,lp_username(snum));
+
+ pstring_sub(user_list,"%S",lp_servicename(snum));
+
+ for (auser=strtok(user_list,LIST_SEP); auser && !ok;
+ auser = strtok(NULL,LIST_SEP)) {
+ if (*auser == '@') {
+ auser = validate_group(auser+1,password,snum);
+ if (auser) {
+ ok = True;
+ fstrcpy(user,auser);
+ DEBUG(3,("authorise_login: ACCEPTED: group username \
+and given password ok (%s)\n", user));
+ }
+ } else {
+ fstring user2;
+ fstrcpy(user2,auser);
+ if (user_ok(user2,snum, NULL, 0) && password_ok(user2,password)) {
+ ok = True;
+ fstrcpy(user,user2);
+ DEBUG(3,("authorise_login: ACCEPTED: user list username \
+and given password ok (%s)\n", user));
+ }
+ }
+ }
+ }
+
+ /* check for a normal guest connection */
+ if (!ok && GUEST_OK(snum)) {
+ fstring guestname;
+ fstrcpy(guestname,lp_guestaccount());
+ if (Get_Pwnam(guestname)) {
+ fstrcpy(user,guestname);
+ ok = True;
+ DEBUG(3,("authorise_login: ACCEPTED: guest account and guest ok (%s)\n",
+ user));
+ } else {
+ DEBUG(0,("authorise_login: Invalid guest account %s??\n",guestname));
+ }
+ *guest = True;
+ }
+
+ if (ok && !user_ok(user, snum, NULL, 0)) {
+ DEBUG(0,("authorise_login: rejected invalid user %s\n",user));
+ ok = False;
+ }
+
+ return(ok);
+}
diff --git a/source/smbd/pipes.c b/source/smbd/pipes.c
new file mode 100644
index 00000000000..f7e9c595c13
--- /dev/null
+++ b/source/smbd/pipes.c
@@ -0,0 +1,264 @@
+/*
+ Unix SMB/CIFS implementation.
+ Pipe SMB reply routines
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+ Copyright (C) Paul Ashton 1997-1998.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+/*
+ This file handles reply_ calls on named pipes that the server
+ makes to handle specific protocols
+*/
+
+
+#include "includes.h"
+
+#define PIPE "\\PIPE\\"
+#define PIPELEN strlen(PIPE)
+
+extern struct pipe_id_info pipe_names[];
+
+/****************************************************************************
+ reply to an open and X on a named pipe
+
+ This code is basically stolen from reply_open_and_X with some
+ wrinkles to handle pipes.
+****************************************************************************/
+int reply_open_pipe_and_X(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ pstring fname;
+ pstring pipe_name;
+ uint16 vuid = SVAL(inbuf, smb_uid);
+ smb_np_struct *p;
+ int smb_ofun = SVAL(inbuf,smb_vwv8);
+ int size=0,fmode=0,mtime=0,rmode=0;
+ int i;
+
+ /* XXXX we need to handle passed times, sattr and flags */
+ srvstr_pull_buf(inbuf, pipe_name, smb_buf(inbuf), sizeof(pipe_name), STR_TERMINATE);
+
+ /* If the name doesn't start \PIPE\ then this is directed */
+ /* at a mailslot or something we really, really don't understand, */
+ /* not just something we really don't understand. */
+ if ( strncmp(pipe_name,PIPE,PIPELEN) != 0 )
+ return(ERROR_DOS(ERRSRV,ERRaccess));
+
+ DEBUG(4,("Opening pipe %s.\n", pipe_name));
+
+ /* See if it is one we want to handle. */
+ for( i = 0; pipe_names[i].client_pipe ; i++ )
+ if( strequal(pipe_name,pipe_names[i].client_pipe) )
+ break;
+
+ if (pipe_names[i].client_pipe == NULL)
+ return(ERROR_BOTH(NT_STATUS_OBJECT_NAME_NOT_FOUND,ERRDOS,ERRbadpipe));
+
+ /* Strip \PIPE\ off the name. */
+ pstrcpy(fname, pipe_name + PIPELEN);
+
+
+#if 0
+ /*
+ * Hack for NT printers... JRA.
+ */
+ if(should_fail_next_srvsvc_open(fname))
+ return(ERROR(ERRSRV,ERRaccess));
+#endif
+
+ /* Known pipes arrive with DIR attribs. Remove it so a regular file */
+ /* can be opened and add it in after the open. */
+ DEBUG(3,("Known pipe %s opening.\n",fname));
+ smb_ofun |= FILE_CREATE_IF_NOT_EXIST;
+
+ p = open_rpc_pipe_p(fname, conn, vuid);
+ if (!p) return(ERROR_DOS(ERRSRV,ERRnofids));
+
+ /* Prepare the reply */
+ set_message(outbuf,15,0,True);
+
+ /* Mark the opened file as an existing named pipe in message mode. */
+ SSVAL(outbuf,smb_vwv9,2);
+ SSVAL(outbuf,smb_vwv10,0xc700);
+
+ if (rmode == 2) {
+ DEBUG(4,("Resetting open result to open from create.\n"));
+ rmode = 1;
+ }
+
+ SSVAL(outbuf,smb_vwv2, p->pnum);
+ SSVAL(outbuf,smb_vwv3,fmode);
+ put_dos_date3(outbuf,smb_vwv4,mtime);
+ SIVAL(outbuf,smb_vwv6,size);
+ SSVAL(outbuf,smb_vwv8,rmode);
+ SSVAL(outbuf,smb_vwv11,0x0001);
+
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
+
+/****************************************************************************
+ reply to a write on a pipe
+****************************************************************************/
+int reply_pipe_write(char *inbuf,char *outbuf,int length,int dum_bufsize)
+{
+ smb_np_struct *p = get_rpc_pipe_p(inbuf,smb_vwv0);
+ size_t numtowrite = SVAL(inbuf,smb_vwv1);
+ int nwritten;
+ int outsize;
+ char *data;
+
+ if (!p)
+ return(ERROR_DOS(ERRDOS,ERRbadfid));
+
+ data = smb_buf(inbuf) + 3;
+
+ if (numtowrite == 0)
+ nwritten = 0;
+ else
+ nwritten = write_to_pipe(p, data, numtowrite);
+
+ if ((nwritten == 0 && numtowrite != 0) || (nwritten < 0))
+ return (UNIXERROR(ERRDOS,ERRnoaccess));
+
+ outsize = set_message(outbuf,1,0,True);
+
+ SSVAL(outbuf,smb_vwv0,nwritten);
+
+ DEBUG(3,("write-IPC pnum=%04x nwritten=%d\n",
+ p->pnum, nwritten));
+
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a write and X.
+
+ This code is basically stolen from reply_write_and_X with some
+ wrinkles to handle pipes.
+****************************************************************************/
+
+int reply_pipe_write_and_X(char *inbuf,char *outbuf,int length,int bufsize)
+{
+ smb_np_struct *p = get_rpc_pipe_p(inbuf,smb_vwv2);
+ size_t numtowrite = SVAL(inbuf,smb_vwv10);
+ int nwritten = -1;
+ int smb_doff = SVAL(inbuf, smb_vwv11);
+ BOOL pipe_start_message_raw = ((SVAL(inbuf, smb_vwv7) & (PIPE_START_MESSAGE|PIPE_RAW_MODE)) ==
+ (PIPE_START_MESSAGE|PIPE_RAW_MODE));
+ char *data;
+
+ if (!p)
+ return(ERROR_DOS(ERRDOS,ERRbadfid));
+
+ data = smb_base(inbuf) + smb_doff;
+
+ if (numtowrite == 0)
+ nwritten = 0;
+ else {
+ if(pipe_start_message_raw) {
+ /*
+ * For the start of a message in named pipe byte mode,
+ * the first two bytes are a length-of-pdu field. Ignore
+ * them (we don't trust the client. JRA.
+ */
+ if(numtowrite < 2) {
+ DEBUG(0,("reply_pipe_write_and_X: start of message set and not enough data sent.(%u)\n",
+ (unsigned int)numtowrite ));
+ return (UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ data += 2;
+ numtowrite -= 2;
+ }
+ nwritten = write_to_pipe(p, data, numtowrite);
+ }
+
+ if ((nwritten == 0 && numtowrite != 0) || (nwritten < 0))
+ return (UNIXERROR(ERRDOS,ERRnoaccess));
+
+ set_message(outbuf,6,0,True);
+
+ nwritten = (pipe_start_message_raw ? nwritten + 2 : nwritten);
+ SSVAL(outbuf,smb_vwv2,nwritten);
+
+ DEBUG(3,("writeX-IPC pnum=%04x nwritten=%d\n",
+ p->pnum, nwritten));
+
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
+
+/****************************************************************************
+ reply to a read and X
+
+ This code is basically stolen from reply_read_and_X with some
+ wrinkles to handle pipes.
+****************************************************************************/
+int reply_pipe_read_and_X(char *inbuf,char *outbuf,int length,int bufsize)
+{
+ smb_np_struct *p = get_rpc_pipe_p(inbuf,smb_vwv2);
+ int smb_maxcnt = SVAL(inbuf,smb_vwv5);
+ int smb_mincnt = SVAL(inbuf,smb_vwv6);
+ int nread = -1;
+ char *data;
+ BOOL unused;
+
+ /* we don't use the offset given to use for pipe reads. This
+ is deliberate, instead we always return the next lump of
+ data on the pipe */
+#if 0
+ uint32 smb_offs = IVAL(inbuf,smb_vwv3);
+#endif
+
+ if (!p)
+ return(ERROR_DOS(ERRDOS,ERRbadfid));
+
+ set_message(outbuf,12,0,True);
+ data = smb_buf(outbuf);
+
+ nread = read_from_pipe(p, data, smb_maxcnt, &unused);
+
+ if (nread < 0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ SSVAL(outbuf,smb_vwv5,nread);
+ SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf));
+ SSVAL(smb_buf(outbuf),-2,nread);
+
+ DEBUG(3,("readX-IPC pnum=%04x min=%d max=%d nread=%d\n",
+ p->pnum, smb_mincnt, smb_maxcnt, nread));
+
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
+
+/****************************************************************************
+ reply to a close
+****************************************************************************/
+int reply_pipe_close(connection_struct *conn, char *inbuf,char *outbuf)
+{
+ smb_np_struct *p = get_rpc_pipe_p(inbuf,smb_vwv0);
+ int outsize = set_message(outbuf,0,0,True);
+
+ if (!p)
+ return(ERROR_DOS(ERRDOS,ERRbadfid));
+
+ DEBUG(5,("reply_pipe_close: pnum:%x\n", p->pnum));
+
+ if (!close_rpc_pipe_hnd(p))
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+
+ return(outsize);
+}
diff --git a/source/smbd/posix_acls.c b/source/smbd/posix_acls.c
new file mode 100644
index 00000000000..620e123e14d
--- /dev/null
+++ b/source/smbd/posix_acls.c
@@ -0,0 +1,3382 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB NT Security Descriptor / Unix permission conversion.
+ Copyright (C) Jeremy Allison 1994-2000.
+ Copyright (C) Andreas Gruenbacher 2002.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+ Data structures representing the internal ACE format.
+****************************************************************************/
+
+enum ace_owner {UID_ACE, GID_ACE, WORLD_ACE};
+enum ace_attribute {ALLOW_ACE, DENY_ACE}; /* Used for incoming NT ACLS. */
+
+typedef union posix_id {
+ uid_t uid;
+ gid_t gid;
+ int world;
+} posix_id;
+
+typedef struct canon_ace {
+ struct canon_ace *next, *prev;
+ SMB_ACL_TAG_T type;
+ mode_t perms; /* Only use S_I(R|W|X)USR mode bits here. */
+ DOM_SID trustee;
+ enum ace_owner owner_type;
+ enum ace_attribute attr;
+ posix_id unix_ug;
+ BOOL inherited;
+} canon_ace;
+
+#define ALL_ACE_PERMS (S_IRUSR|S_IWUSR|S_IXUSR)
+
+/*
+ * EA format of user.SAMBA_PAI (Samba_Posix_Acl_Interitance)
+ * attribute on disk.
+ *
+ * | 1 | 1 | 2 | 2 | ....
+ * +------+------+-------------+---------------------+-------------+--------------------+
+ * | vers | flag | num_entries | num_default_entries | ..entries.. | default_entries... |
+ * +------+------+-------------+---------------------+-------------+--------------------+
+ */
+
+#define PAI_VERSION_OFFSET 0
+#define PAI_FLAG_OFFSET 1
+#define PAI_NUM_ENTRIES_OFFSET 2
+#define PAI_NUM_DEFAULT_ENTRIES_OFFSET 4
+#define PAI_ENTRIES_BASE 6
+
+#define PAI_VERSION 1
+#define PAI_ACL_FLAG_PROTECTED 0x1
+#define PAI_ENTRY_LENGTH 5
+
+/*
+ * In memory format of user.SAMBA_PAI attribute.
+ */
+
+struct pai_entry {
+ struct pai_entry *next, *prev;
+ enum ace_owner owner_type;
+ posix_id unix_ug;
+};
+
+struct pai_val {
+ BOOL protected;
+ unsigned int num_entries;
+ struct pai_entry *entry_list;
+ unsigned int num_def_entries;
+ struct pai_entry *def_entry_list;
+};
+
+/************************************************************************
+ Return a uint32 of the pai_entry principal.
+************************************************************************/
+
+static uint32 get_pai_entry_val(struct pai_entry *paie)
+{
+ switch (paie->owner_type) {
+ case UID_ACE:
+ DEBUG(10,("get_pai_entry_val: uid = %u\n", (unsigned int)paie->unix_ug.uid ));
+ return (uint32)paie->unix_ug.uid;
+ case GID_ACE:
+ DEBUG(10,("get_pai_entry_val: gid = %u\n", (unsigned int)paie->unix_ug.gid ));
+ return (uint32)paie->unix_ug.gid;
+ case WORLD_ACE:
+ default:
+ DEBUG(10,("get_pai_entry_val: world ace\n"));
+ return (uint32)-1;
+ }
+}
+
+/************************************************************************
+ Return a uint32 of the entry principal.
+************************************************************************/
+
+static uint32 get_entry_val(canon_ace *ace_entry)
+{
+ switch (ace_entry->owner_type) {
+ case UID_ACE:
+ DEBUG(10,("get_entry_val: uid = %u\n", (unsigned int)ace_entry->unix_ug.uid ));
+ return (uint32)ace_entry->unix_ug.uid;
+ case GID_ACE:
+ DEBUG(10,("get_entry_val: gid = %u\n", (unsigned int)ace_entry->unix_ug.gid ));
+ return (uint32)ace_entry->unix_ug.gid;
+ case WORLD_ACE:
+ default:
+ DEBUG(10,("get_entry_val: world ace\n"));
+ return (uint32)-1;
+ }
+}
+
+/************************************************************************
+ Count the inherited entries.
+************************************************************************/
+
+static unsigned int num_inherited_entries(canon_ace *ace_list)
+{
+ unsigned int num_entries = 0;
+
+ for (; ace_list; ace_list = ace_list->next)
+ if (ace_list->inherited)
+ num_entries++;
+ return num_entries;
+}
+
+/************************************************************************
+ Create the on-disk format. Caller must free.
+************************************************************************/
+
+static char *create_pai_buf(canon_ace *file_ace_list, canon_ace *dir_ace_list, BOOL protected, size_t *store_size)
+{
+ char *pai_buf = NULL;
+ canon_ace *ace_list = NULL;
+ char *entry_offset = NULL;
+ unsigned int num_entries = 0;
+ unsigned int num_def_entries = 0;
+
+ for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next)
+ if (ace_list->inherited)
+ num_entries++;
+
+ for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next)
+ if (ace_list->inherited)
+ num_def_entries++;
+
+ DEBUG(10,("create_pai_buf: num_entries = %u, num_def_entries = %u\n", num_entries, num_def_entries ));
+
+ *store_size = PAI_ENTRIES_BASE + ((num_entries + num_def_entries)*PAI_ENTRY_LENGTH);
+
+ pai_buf = malloc(*store_size);
+ if (!pai_buf) {
+ return NULL;
+ }
+
+ /* Set up the header. */
+ memset(pai_buf, '\0', PAI_ENTRIES_BASE);
+ SCVAL(pai_buf,PAI_VERSION_OFFSET,PAI_VERSION);
+ SCVAL(pai_buf,PAI_FLAG_OFFSET,(protected ? PAI_ACL_FLAG_PROTECTED : 0));
+ SSVAL(pai_buf,PAI_NUM_ENTRIES_OFFSET,num_entries);
+ SSVAL(pai_buf,PAI_NUM_DEFAULT_ENTRIES_OFFSET,num_def_entries);
+
+ entry_offset = pai_buf + PAI_ENTRIES_BASE;
+
+ for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next) {
+ if (ace_list->inherited) {
+ uint8 type_val = (unsigned char)ace_list->owner_type;
+ uint32 entry_val = get_entry_val(ace_list);
+
+ SCVAL(entry_offset,0,type_val);
+ SIVAL(entry_offset,1,entry_val);
+ entry_offset += PAI_ENTRY_LENGTH;
+ }
+ }
+
+ for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next) {
+ if (ace_list->inherited) {
+ uint8 type_val = (unsigned char)ace_list->owner_type;
+ uint32 entry_val = get_entry_val(ace_list);
+
+ SCVAL(entry_offset,0,type_val);
+ SIVAL(entry_offset,1,entry_val);
+ entry_offset += PAI_ENTRY_LENGTH;
+ }
+ }
+
+ return pai_buf;
+}
+
+/************************************************************************
+ Store the user.SAMBA_PAI attribute on disk.
+************************************************************************/
+
+static void store_inheritance_attributes(files_struct *fsp, canon_ace *file_ace_list,
+ canon_ace *dir_ace_list, BOOL protected)
+{
+ int ret;
+ size_t store_size;
+ char *pai_buf;
+
+ if (!lp_map_acl_inherit(SNUM(fsp->conn)))
+ return;
+
+ /*
+ * Don't store if this ACL isn't protected and
+ * none of the entries in it are marked as inherited.
+ */
+
+ if (!protected && num_inherited_entries(file_ace_list) == 0 && num_inherited_entries(dir_ace_list) == 0) {
+ /* Instead just remove the attribute if it exists. */
+ if (fsp->fd != -1)
+ SMB_VFS_FREMOVEXATTR(fsp, fsp->fd, SAMBA_POSIX_INHERITANCE_EA_NAME);
+ else
+ SMB_VFS_REMOVEXATTR(fsp->conn, fsp->fsp_name, SAMBA_POSIX_INHERITANCE_EA_NAME);
+ return;
+ }
+
+ pai_buf = create_pai_buf(file_ace_list, dir_ace_list, protected, &store_size);
+
+ if (fsp->fd != -1)
+ ret = SMB_VFS_FSETXATTR(fsp, fsp->fd, SAMBA_POSIX_INHERITANCE_EA_NAME,
+ pai_buf, store_size, 0);
+ else
+ ret = SMB_VFS_SETXATTR(fsp->conn,fsp->fsp_name, SAMBA_POSIX_INHERITANCE_EA_NAME,
+ pai_buf, store_size, 0);
+
+ SAFE_FREE(pai_buf);
+
+ DEBUG(10,("store_inheritance_attribute:%s for file %s\n", protected ? " (protected)" : "", fsp->fsp_name));
+ if (ret == -1 && errno != ENOSYS)
+ DEBUG(1,("store_inheritance_attribute: Error %s\n", strerror(errno) ));
+}
+
+/************************************************************************
+ Delete the in memory inheritance info.
+************************************************************************/
+
+static void free_inherited_info(struct pai_val *pal)
+{
+ if (pal) {
+ struct pai_entry *paie, *paie_next;
+ for (paie = pal->entry_list; paie; paie = paie_next) {
+ paie_next = paie->next;
+ SAFE_FREE(paie);
+ }
+ for (paie = pal->def_entry_list; paie; paie = paie_next) {
+ paie_next = paie->next;
+ SAFE_FREE(paie);
+ }
+ SAFE_FREE(pal);
+ }
+}
+
+/************************************************************************
+ Was this ACL protected ?
+************************************************************************/
+
+static BOOL get_protected_flag(struct pai_val *pal)
+{
+ if (!pal)
+ return False;
+ return pal->protected;
+}
+
+/************************************************************************
+ Was this ACE inherited ?
+************************************************************************/
+
+static BOOL get_inherited_flag(struct pai_val *pal, canon_ace *ace_entry, BOOL default_ace)
+{
+ struct pai_entry *paie;
+
+ if (!pal)
+ return False;
+
+ /* If the entry exists it is inherited. */
+ for (paie = (default_ace ? pal->def_entry_list : pal->entry_list); paie; paie = paie->next) {
+ if (ace_entry->owner_type == paie->owner_type &&
+ get_entry_val(ace_entry) == get_pai_entry_val(paie))
+ return True;
+ }
+ return False;
+}
+
+/************************************************************************
+ Ensure an attribute just read is valid.
+************************************************************************/
+
+static BOOL check_pai_ok(char *pai_buf, size_t pai_buf_data_size)
+{
+ uint16 num_entries;
+ uint16 num_def_entries;
+
+ if (pai_buf_data_size < PAI_ENTRIES_BASE) {
+ /* Corrupted - too small. */
+ return False;
+ }
+
+ if (CVAL(pai_buf,PAI_VERSION_OFFSET) != PAI_VERSION)
+ return False;
+
+ num_entries = SVAL(pai_buf,PAI_NUM_ENTRIES_OFFSET);
+ num_def_entries = SVAL(pai_buf,PAI_NUM_DEFAULT_ENTRIES_OFFSET);
+
+ /* Check the entry lists match. */
+ /* Each entry is 5 bytes (type plus 4 bytes of uid or gid). */
+
+ if (((num_entries + num_def_entries)*PAI_ENTRY_LENGTH) + PAI_ENTRIES_BASE != pai_buf_data_size)
+ return False;
+
+ return True;
+}
+
+
+/************************************************************************
+ Convert to in-memory format.
+************************************************************************/
+
+static struct pai_val *create_pai_val(char *buf, size_t size)
+{
+ char *entry_offset;
+ struct pai_val *paiv = NULL;
+ int i;
+
+ if (!check_pai_ok(buf, size))
+ return NULL;
+
+ paiv = malloc(sizeof(struct pai_val));
+ if (!paiv)
+ return NULL;
+
+ memset(paiv, '\0', sizeof(struct pai_val));
+
+ paiv->protected = (CVAL(buf,PAI_FLAG_OFFSET) == PAI_ACL_FLAG_PROTECTED);
+
+ paiv->num_entries = SVAL(buf,PAI_NUM_ENTRIES_OFFSET);
+ paiv->num_def_entries = SVAL(buf,PAI_NUM_DEFAULT_ENTRIES_OFFSET);
+
+ entry_offset = buf + PAI_ENTRIES_BASE;
+
+ DEBUG(10,("create_pai_val:%s num_entries = %u, num_def_entries = %u\n",
+ paiv->protected ? " (protected)" : "", paiv->num_entries, paiv->num_def_entries ));
+
+ for (i = 0; i < paiv->num_entries; i++) {
+ struct pai_entry *paie;
+
+ paie = malloc(sizeof(struct pai_entry));
+ if (!paie) {
+ free_inherited_info(paiv);
+ return NULL;
+ }
+
+ paie->owner_type = (enum ace_owner)CVAL(entry_offset,0);
+ switch( paie->owner_type) {
+ case UID_ACE:
+ paie->unix_ug.uid = (uid_t)IVAL(entry_offset,1);
+ DEBUG(10,("create_pai_val: uid = %u\n", (unsigned int)paie->unix_ug.uid ));
+ break;
+ case GID_ACE:
+ paie->unix_ug.gid = (gid_t)IVAL(entry_offset,1);
+ DEBUG(10,("create_pai_val: gid = %u\n", (unsigned int)paie->unix_ug.gid ));
+ break;
+ case WORLD_ACE:
+ paie->unix_ug.world = -1;
+ DEBUG(10,("create_pai_val: world ace\n"));
+ break;
+ default:
+ free_inherited_info(paiv);
+ return NULL;
+ }
+ entry_offset += PAI_ENTRY_LENGTH;
+ DLIST_ADD(paiv->entry_list, paie);
+ }
+
+ for (i = 0; i < paiv->num_def_entries; i++) {
+ struct pai_entry *paie;
+
+ paie = malloc(sizeof(struct pai_entry));
+ if (!paie) {
+ free_inherited_info(paiv);
+ return NULL;
+ }
+
+ paie->owner_type = (enum ace_owner)CVAL(entry_offset,0);
+ switch( paie->owner_type) {
+ case UID_ACE:
+ paie->unix_ug.uid = (uid_t)IVAL(entry_offset,1);
+ DEBUG(10,("create_pai_val: (def) uid = %u\n", (unsigned int)paie->unix_ug.uid ));
+ break;
+ case GID_ACE:
+ paie->unix_ug.gid = (gid_t)IVAL(entry_offset,1);
+ DEBUG(10,("create_pai_val: (def) gid = %u\n", (unsigned int)paie->unix_ug.gid ));
+ break;
+ case WORLD_ACE:
+ paie->unix_ug.world = -1;
+ DEBUG(10,("create_pai_val: (def) world ace\n"));
+ break;
+ default:
+ free_inherited_info(paiv);
+ return NULL;
+ }
+ entry_offset += PAI_ENTRY_LENGTH;
+ DLIST_ADD(paiv->def_entry_list, paie);
+ }
+
+ return paiv;
+}
+
+/************************************************************************
+ Load the user.SAMBA_PAI attribute.
+************************************************************************/
+
+static struct pai_val *load_inherited_info(files_struct *fsp)
+{
+ char *pai_buf;
+ size_t pai_buf_size = 1024;
+ struct pai_val *paiv = NULL;
+ ssize_t ret;
+
+ if (!lp_map_acl_inherit(SNUM(fsp->conn)))
+ return NULL;
+
+ if ((pai_buf = malloc(pai_buf_size)) == NULL)
+ return NULL;
+
+ do {
+ if (fsp->fd != -1)
+ ret = SMB_VFS_FGETXATTR(fsp, fsp->fd, SAMBA_POSIX_INHERITANCE_EA_NAME,
+ pai_buf, pai_buf_size);
+ else
+ ret = SMB_VFS_GETXATTR(fsp->conn,fsp->fsp_name,SAMBA_POSIX_INHERITANCE_EA_NAME,
+ pai_buf, pai_buf_size);
+
+ if (ret == -1) {
+ if (errno != ERANGE) {
+ break;
+ }
+ /* Buffer too small - enlarge it. */
+ pai_buf_size *= 2;
+ SAFE_FREE(pai_buf);
+ if ((pai_buf = malloc(pai_buf_size)) == NULL)
+ return NULL;
+ }
+ } while (ret == -1);
+
+ DEBUG(10,("load_inherited_info: ret = %lu for file %s\n", (unsigned long)ret, fsp->fsp_name));
+
+ if (ret == -1) {
+ /* No attribute or not supported. */
+#if defined(ENOATTR)
+ if (errno != ENOATTR)
+ DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) ));
+#else
+ if (errno != ENOSYS)
+ DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) ));
+#endif
+ SAFE_FREE(pai_buf);
+ return NULL;
+ }
+
+ paiv = create_pai_val(pai_buf, ret);
+
+ if (paiv && paiv->protected)
+ DEBUG(10,("load_inherited_info: ACL is protected for file %s\n", fsp->fsp_name));
+
+ SAFE_FREE(pai_buf);
+ return paiv;
+}
+
+/****************************************************************************
+ Functions to manipulate the internal ACE format.
+****************************************************************************/
+
+/****************************************************************************
+ Count a linked list of canonical ACE entries.
+****************************************************************************/
+
+static size_t count_canon_ace_list( canon_ace *list_head )
+{
+ size_t count = 0;
+ canon_ace *ace;
+
+ for (ace = list_head; ace; ace = ace->next)
+ count++;
+
+ return count;
+}
+
+/****************************************************************************
+ Free a linked list of canonical ACE entries.
+****************************************************************************/
+
+static void free_canon_ace_list( canon_ace *list_head )
+{
+ while (list_head) {
+ canon_ace *old_head = list_head;
+ DLIST_REMOVE(list_head, list_head);
+ SAFE_FREE(old_head);
+ }
+}
+
+/****************************************************************************
+ Function to duplicate a canon_ace entry.
+****************************************************************************/
+
+static canon_ace *dup_canon_ace( canon_ace *src_ace)
+{
+ canon_ace *dst_ace = (canon_ace *)malloc(sizeof(canon_ace));
+
+ if (dst_ace == NULL)
+ return NULL;
+
+ *dst_ace = *src_ace;
+ dst_ace->prev = dst_ace->next = NULL;
+ return dst_ace;
+}
+
+/****************************************************************************
+ Print out a canon ace.
+****************************************************************************/
+
+static void print_canon_ace(canon_ace *pace, int num)
+{
+ fstring str;
+
+ dbgtext( "canon_ace index %d. Type = %s ", num, pace->attr == ALLOW_ACE ? "allow" : "deny" );
+ dbgtext( "SID = %s ", sid_to_string( str, &pace->trustee));
+ if (pace->owner_type == UID_ACE) {
+ const char *u_name = uidtoname(pace->unix_ug.uid);
+ dbgtext( "uid %u (%s) ", (unsigned int)pace->unix_ug.uid, u_name );
+ } else if (pace->owner_type == GID_ACE) {
+ char *g_name = gidtoname(pace->unix_ug.gid);
+ dbgtext( "gid %u (%s) ", (unsigned int)pace->unix_ug.gid, g_name );
+ } else
+ dbgtext( "other ");
+ switch (pace->type) {
+ case SMB_ACL_USER:
+ dbgtext( "SMB_ACL_USER ");
+ break;
+ case SMB_ACL_USER_OBJ:
+ dbgtext( "SMB_ACL_USER_OBJ ");
+ break;
+ case SMB_ACL_GROUP:
+ dbgtext( "SMB_ACL_GROUP ");
+ break;
+ case SMB_ACL_GROUP_OBJ:
+ dbgtext( "SMB_ACL_GROUP_OBJ ");
+ break;
+ case SMB_ACL_OTHER:
+ dbgtext( "SMB_ACL_OTHER ");
+ break;
+ }
+ if (pace->inherited)
+ dbgtext( "(inherited) ");
+ dbgtext( "perms ");
+ dbgtext( "%c", pace->perms & S_IRUSR ? 'r' : '-');
+ dbgtext( "%c", pace->perms & S_IWUSR ? 'w' : '-');
+ dbgtext( "%c\n", pace->perms & S_IXUSR ? 'x' : '-');
+}
+
+/****************************************************************************
+ Print out a canon ace list.
+****************************************************************************/
+
+static void print_canon_ace_list(const char *name, canon_ace *ace_list)
+{
+ int count = 0;
+
+ if( DEBUGLVL( 10 )) {
+ dbgtext( "print_canon_ace_list: %s\n", name );
+ for (;ace_list; ace_list = ace_list->next, count++)
+ print_canon_ace(ace_list, count );
+ }
+}
+
+/****************************************************************************
+ Map POSIX ACL perms to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
+****************************************************************************/
+
+static mode_t convert_permset_to_mode_t(connection_struct *conn, SMB_ACL_PERMSET_T permset)
+{
+ mode_t ret = 0;
+
+ ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ) ? S_IRUSR : 0);
+ ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE) ? S_IWUSR : 0);
+ ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_EXECUTE) ? S_IXUSR : 0);
+
+ return ret;
+}
+
+/****************************************************************************
+ Map generic UNIX permissions to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
+****************************************************************************/
+
+static mode_t unix_perms_to_acl_perms(mode_t mode, int r_mask, int w_mask, int x_mask)
+{
+ mode_t ret = 0;
+
+ if (mode & r_mask)
+ ret |= S_IRUSR;
+ if (mode & w_mask)
+ ret |= S_IWUSR;
+ if (mode & x_mask)
+ ret |= S_IXUSR;
+
+ return ret;
+}
+
+/****************************************************************************
+ Map canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits) to
+ an SMB_ACL_PERMSET_T.
+****************************************************************************/
+
+static int map_acl_perms_to_permset(connection_struct *conn, mode_t mode, SMB_ACL_PERMSET_T *p_permset)
+{
+ if (SMB_VFS_SYS_ACL_CLEAR_PERMS(conn, *p_permset) == -1)
+ return -1;
+ if (mode & S_IRUSR) {
+ if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_READ) == -1)
+ return -1;
+ }
+ if (mode & S_IWUSR) {
+ if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_WRITE) == -1)
+ return -1;
+ }
+ if (mode & S_IXUSR) {
+ if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_EXECUTE) == -1)
+ return -1;
+ }
+ return 0;
+}
+/****************************************************************************
+ Function to create owner and group SIDs from a SMB_STRUCT_STAT.
+****************************************************************************/
+
+static void create_file_sids(SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid)
+{
+ uid_to_sid( powner_sid, psbuf->st_uid );
+ gid_to_sid( pgroup_sid, psbuf->st_gid );
+}
+
+/****************************************************************************
+ Merge aces with a common sid - if both are allow or deny, OR the permissions together and
+ delete the second one. If the first is deny, mask the permissions off and delete the allow
+ if the permissions become zero, delete the deny if the permissions are non zero.
+****************************************************************************/
+
+static void merge_aces( canon_ace **pp_list_head )
+{
+ canon_ace *list_head = *pp_list_head;
+ canon_ace *curr_ace_outer;
+ canon_ace *curr_ace_outer_next;
+
+ /*
+ * First, merge allow entries with identical SIDs, and deny entries
+ * with identical SIDs.
+ */
+
+ for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
+ canon_ace *curr_ace;
+ canon_ace *curr_ace_next;
+
+ curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
+
+ for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
+
+ curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
+
+ if (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
+ (curr_ace->attr == curr_ace_outer->attr)) {
+
+ if( DEBUGLVL( 10 )) {
+ dbgtext("merge_aces: Merging ACE's\n");
+ print_canon_ace( curr_ace_outer, 0);
+ print_canon_ace( curr_ace, 0);
+ }
+
+ /* Merge two allow or two deny ACE's. */
+
+ curr_ace_outer->perms |= curr_ace->perms;
+ DLIST_REMOVE(list_head, curr_ace);
+ SAFE_FREE(curr_ace);
+ curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
+ }
+ }
+ }
+
+ /*
+ * Now go through and mask off allow permissions with deny permissions.
+ * We can delete either the allow or deny here as we know that each SID
+ * appears only once in the list.
+ */
+
+ for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
+ canon_ace *curr_ace;
+ canon_ace *curr_ace_next;
+
+ curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
+
+ for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
+
+ curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
+
+ /*
+ * Subtract ACE's with different entries. Due to the ordering constraints
+ * we've put on the ACL, we know the deny must be the first one.
+ */
+
+ if (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
+ (curr_ace_outer->attr == DENY_ACE) && (curr_ace->attr == ALLOW_ACE)) {
+
+ if( DEBUGLVL( 10 )) {
+ dbgtext("merge_aces: Masking ACE's\n");
+ print_canon_ace( curr_ace_outer, 0);
+ print_canon_ace( curr_ace, 0);
+ }
+
+ curr_ace->perms &= ~curr_ace_outer->perms;
+
+ if (curr_ace->perms == 0) {
+
+ /*
+ * The deny overrides the allow. Remove the allow.
+ */
+
+ DLIST_REMOVE(list_head, curr_ace);
+ SAFE_FREE(curr_ace);
+ curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
+
+ } else {
+
+ /*
+ * Even after removing permissions, there
+ * are still allow permissions - delete the deny.
+ * It is safe to delete the deny here,
+ * as we are guarenteed by the deny first
+ * ordering that all the deny entries for
+ * this SID have already been merged into one
+ * before we can get to an allow ace.
+ */
+
+ DLIST_REMOVE(list_head, curr_ace_outer);
+ SAFE_FREE(curr_ace_outer);
+ break;
+ }
+ }
+
+ } /* end for curr_ace */
+ } /* end for curr_ace_outer */
+
+ /* We may have modified the list. */
+
+ *pp_list_head = list_head;
+}
+
+/****************************************************************************
+ Check if we need to return NT4.x compatible ACL entries.
+****************************************************************************/
+
+static BOOL nt4_compatible_acls(void)
+{
+ const char *compat = lp_acl_compatibility();
+
+ if (*compat == '\0') {
+ enum remote_arch_types ra_type = get_remote_arch();
+
+ /* Automatically adapt to client */
+ return (ra_type <= RA_WINNT);
+ } else
+ return (strequal(compat, "winnt"));
+}
+
+
+/****************************************************************************
+ Map canon_ace perms to permission bits NT.
+ The attr element is not used here - we only process deny entries on set,
+ not get. Deny entries are implicit on get with ace->perms = 0.
+****************************************************************************/
+
+static SEC_ACCESS map_canon_ace_perms(int *pacl_type, DOM_SID *powner_sid, canon_ace *ace)
+{
+ SEC_ACCESS sa;
+ uint32 nt_mask = 0;
+
+ *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
+
+ if ((ace->perms & ALL_ACE_PERMS) == ALL_ACE_PERMS) {
+ nt_mask = UNIX_ACCESS_RWX;
+ } else if ((ace->perms & ALL_ACE_PERMS) == (mode_t)0) {
+ /*
+ * Windows NT refuses to display ACEs with no permissions in them (but
+ * they are perfectly legal with Windows 2000). If the ACE has empty
+ * permissions we cannot use 0, so we use the otherwise unused
+ * WRITE_OWNER permission, which we ignore when we set an ACL.
+ * We abstract this into a #define of UNIX_ACCESS_NONE to allow this
+ * to be changed in the future.
+ */
+
+ if (nt4_compatible_acls())
+ nt_mask = UNIX_ACCESS_NONE;
+ else
+ nt_mask = 0;
+ } else {
+ nt_mask |= ((ace->perms & S_IRUSR) ? UNIX_ACCESS_R : 0 );
+ nt_mask |= ((ace->perms & S_IWUSR) ? UNIX_ACCESS_W : 0 );
+ nt_mask |= ((ace->perms & S_IXUSR) ? UNIX_ACCESS_X : 0 );
+ }
+
+ DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n",
+ (unsigned int)ace->perms, (unsigned int)nt_mask ));
+
+ init_sec_access(&sa,nt_mask);
+ return sa;
+}
+
+/****************************************************************************
+ Map NT perms to a UNIX mode_t.
+****************************************************************************/
+
+#define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
+#define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
+#define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
+
+static mode_t map_nt_perms( SEC_ACCESS sec_access, int type)
+{
+ mode_t mode = 0;
+
+ switch(type) {
+ case S_IRUSR:
+ if(sec_access.mask & GENERIC_ALL_ACCESS)
+ mode = S_IRUSR|S_IWUSR|S_IXUSR;
+ else {
+ mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0;
+ mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0;
+ mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0;
+ }
+ break;
+ case S_IRGRP:
+ if(sec_access.mask & GENERIC_ALL_ACCESS)
+ mode = S_IRGRP|S_IWGRP|S_IXGRP;
+ else {
+ mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0;
+ mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0;
+ mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0;
+ }
+ break;
+ case S_IROTH:
+ if(sec_access.mask & GENERIC_ALL_ACCESS)
+ mode = S_IROTH|S_IWOTH|S_IXOTH;
+ else {
+ mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0;
+ mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0;
+ mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0;
+ }
+ break;
+ }
+
+ return mode;
+}
+
+/****************************************************************************
+ Unpack a SEC_DESC into a UNIX owner and group.
+****************************************************************************/
+
+static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd)
+{
+ DOM_SID owner_sid;
+ DOM_SID grp_sid;
+
+ *puser = (uid_t)-1;
+ *pgrp = (gid_t)-1;
+
+ if(security_info_sent == 0) {
+ DEBUG(0,("unpack_nt_owners: no security info sent !\n"));
+ return True;
+ }
+
+ /*
+ * Validate the owner and group SID's.
+ */
+
+ memset(&owner_sid, '\0', sizeof(owner_sid));
+ memset(&grp_sid, '\0', sizeof(grp_sid));
+
+ DEBUG(5,("unpack_nt_owners: validating owner_sids.\n"));
+
+ /*
+ * Don't immediately fail if the owner sid cannot be validated.
+ * This may be a group chown only set.
+ */
+
+ if (security_info_sent & OWNER_SECURITY_INFORMATION) {
+ sid_copy(&owner_sid, psd->owner_sid);
+ if (!NT_STATUS_IS_OK(sid_to_uid(&owner_sid, puser))) {
+#if ACL_FORCE_UNMAPPABLE
+ /* this allows take ownership to work reasonably */
+ extern struct current_user current_user;
+ *puser = current_user.uid;
+#else
+ DEBUG(3,("unpack_nt_owners: unable to validate owner sid for %s\n",
+ sid_string_static(&owner_sid)));
+ return False;
+#endif
+ }
+ }
+
+ /*
+ * Don't immediately fail if the group sid cannot be validated.
+ * This may be an owner chown only set.
+ */
+
+ if (security_info_sent & GROUP_SECURITY_INFORMATION) {
+ sid_copy(&grp_sid, psd->grp_sid);
+ if (!NT_STATUS_IS_OK(sid_to_gid( &grp_sid, pgrp))) {
+#if ACL_FORCE_UNMAPPABLE
+ /* this allows take group ownership to work reasonably */
+ extern struct current_user current_user;
+ *pgrp = current_user.gid;
+#else
+ DEBUG(3,("unpack_nt_owners: unable to validate group sid.\n"));
+ return False;
+#endif
+ }
+ }
+
+ DEBUG(5,("unpack_nt_owners: owner_sids validated.\n"));
+
+ return True;
+}
+
+/****************************************************************************
+ Ensure the enforced permissions for this share apply.
+****************************************************************************/
+
+static void apply_default_perms(files_struct *fsp, canon_ace *pace, mode_t type)
+{
+ int snum = SNUM(fsp->conn);
+ mode_t and_bits = (mode_t)0;
+ mode_t or_bits = (mode_t)0;
+
+ /* Get the initial bits to apply. */
+
+ if (fsp->is_directory) {
+ and_bits = lp_dir_security_mask(snum);
+ or_bits = lp_force_dir_security_mode(snum);
+ } else {
+ and_bits = lp_security_mask(snum);
+ or_bits = lp_force_security_mode(snum);
+ }
+
+ /* Now bounce them into the S_USR space. */
+ switch(type) {
+ case S_IRUSR:
+ /* Ensure owner has read access. */
+ pace->perms |= S_IRUSR;
+ if (fsp->is_directory)
+ pace->perms |= (S_IWUSR|S_IXUSR);
+ and_bits = unix_perms_to_acl_perms(and_bits, S_IRUSR, S_IWUSR, S_IXUSR);
+ or_bits = unix_perms_to_acl_perms(or_bits, S_IRUSR, S_IWUSR, S_IXUSR);
+ break;
+ case S_IRGRP:
+ and_bits = unix_perms_to_acl_perms(and_bits, S_IRGRP, S_IWGRP, S_IXGRP);
+ or_bits = unix_perms_to_acl_perms(or_bits, S_IRGRP, S_IWGRP, S_IXGRP);
+ break;
+ case S_IROTH:
+ and_bits = unix_perms_to_acl_perms(and_bits, S_IROTH, S_IWOTH, S_IXOTH);
+ or_bits = unix_perms_to_acl_perms(or_bits, S_IROTH, S_IWOTH, S_IXOTH);
+ break;
+ }
+
+ pace->perms = ((pace->perms & and_bits)|or_bits);
+}
+
+/****************************************************************************
+ Check if a given uid/SID is in a group gid/SID. This is probably very
+ expensive and will need optimisation. A *lot* of optimisation :-). JRA.
+****************************************************************************/
+
+static BOOL uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace )
+{
+ extern DOM_SID global_sid_World;
+ fstring u_name;
+ fstring g_name;
+ extern struct current_user current_user;
+
+ /* "Everyone" always matches every uid. */
+
+ if (sid_equal(&group_ace->trustee, &global_sid_World))
+ return True;
+
+ /* Assume that the current user is in the current group (force group) */
+
+ if (uid_ace->unix_ug.uid == current_user.uid && group_ace->unix_ug.gid == current_user.gid)
+ return True;
+
+ fstrcpy(u_name, uidtoname(uid_ace->unix_ug.uid));
+ fstrcpy(g_name, gidtoname(group_ace->unix_ug.gid));
+
+ /*
+ * Due to the winbind interfaces we need to do this via names,
+ * not uids/gids.
+ */
+
+ return user_in_group_list(u_name, g_name, NULL, 0);
+}
+
+/****************************************************************************
+ A well formed POSIX file or default ACL has at least 3 entries, a
+ SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ.
+ In addition, the owner must always have at least read access.
+ When using this call on get_acl, the pst struct is valid and contains
+ the mode of the file. When using this call on set_acl, the pst struct has
+ been modified to have a mode containing the default for this file or directory
+ type.
+****************************************************************************/
+
+static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
+ files_struct *fsp,
+ DOM_SID *pfile_owner_sid,
+ DOM_SID *pfile_grp_sid,
+ SMB_STRUCT_STAT *pst,
+ BOOL setting_acl)
+{
+ extern DOM_SID global_sid_World;
+ canon_ace *pace;
+ BOOL got_user = False;
+ BOOL got_grp = False;
+ BOOL got_other = False;
+ canon_ace *pace_other = NULL;
+ canon_ace *pace_group = NULL;
+
+ for (pace = *pp_ace; pace; pace = pace->next) {
+ if (pace->type == SMB_ACL_USER_OBJ) {
+
+ if (setting_acl)
+ apply_default_perms(fsp, pace, S_IRUSR);
+ got_user = True;
+
+ } else if (pace->type == SMB_ACL_GROUP_OBJ) {
+
+ /*
+ * Ensure create mask/force create mode is respected on set.
+ */
+
+ if (setting_acl)
+ apply_default_perms(fsp, pace, S_IRGRP);
+ got_grp = True;
+ pace_group = pace;
+
+ } else if (pace->type == SMB_ACL_OTHER) {
+
+ /*
+ * Ensure create mask/force create mode is respected on set.
+ */
+
+ if (setting_acl)
+ apply_default_perms(fsp, pace, S_IROTH);
+ got_other = True;
+ pace_other = pace;
+ }
+ }
+
+ if (!got_user) {
+ if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
+ DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
+ return False;
+ }
+
+ ZERO_STRUCTP(pace);
+ pace->type = SMB_ACL_USER_OBJ;
+ pace->owner_type = UID_ACE;
+ pace->unix_ug.uid = pst->st_uid;
+ pace->trustee = *pfile_owner_sid;
+ pace->attr = ALLOW_ACE;
+
+ if (setting_acl) {
+ /* If we only got an "everyone" perm, just use that. */
+ if (!got_grp && got_other)
+ pace->perms = pace_other->perms;
+ else if (got_grp && uid_entry_in_group(pace, pace_group))
+ pace->perms = pace_group->perms;
+ else
+ pace->perms = 0;
+
+ apply_default_perms(fsp, pace, S_IRUSR);
+ } else {
+ pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRUSR, S_IWUSR, S_IXUSR);
+ }
+
+ DLIST_ADD(*pp_ace, pace);
+ }
+
+ if (!got_grp) {
+ if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
+ DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
+ return False;
+ }
+
+ ZERO_STRUCTP(pace);
+ pace->type = SMB_ACL_GROUP_OBJ;
+ pace->owner_type = GID_ACE;
+ pace->unix_ug.uid = pst->st_gid;
+ pace->trustee = *pfile_grp_sid;
+ pace->attr = ALLOW_ACE;
+ if (setting_acl) {
+ /* If we only got an "everyone" perm, just use that. */
+ if (got_other)
+ pace->perms = pace_other->perms;
+ else
+ pace->perms = 0;
+ apply_default_perms(fsp, pace, S_IRGRP);
+ } else {
+ pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRGRP, S_IWGRP, S_IXGRP);
+ }
+
+ DLIST_ADD(*pp_ace, pace);
+ }
+
+ if (!got_other) {
+ if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
+ DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
+ return False;
+ }
+
+ ZERO_STRUCTP(pace);
+ pace->type = SMB_ACL_OTHER;
+ pace->owner_type = WORLD_ACE;
+ pace->unix_ug.world = -1;
+ pace->trustee = global_sid_World;
+ pace->attr = ALLOW_ACE;
+ if (setting_acl) {
+ pace->perms = 0;
+ apply_default_perms(fsp, pace, S_IROTH);
+ } else
+ pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IROTH, S_IWOTH, S_IXOTH);
+
+ DLIST_ADD(*pp_ace, pace);
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Check if a POSIX ACL has the required SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries.
+ If it does not have them, check if there are any entries where the trustee is the
+ file owner or the owning group, and map these to SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ.
+****************************************************************************/
+
+static void check_owning_objs(canon_ace *ace, DOM_SID *pfile_owner_sid, DOM_SID *pfile_grp_sid)
+{
+ BOOL got_user_obj, got_group_obj;
+ canon_ace *current_ace;
+ int i, entries;
+
+ entries = count_canon_ace_list(ace);
+ got_user_obj = False;
+ got_group_obj = False;
+
+ for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) {
+ if (current_ace->type == SMB_ACL_USER_OBJ)
+ got_user_obj = True;
+ else if (current_ace->type == SMB_ACL_GROUP_OBJ)
+ got_group_obj = True;
+ }
+ if (got_user_obj && got_group_obj) {
+ DEBUG(10,("check_owning_objs: ACL had owning user/group entries.\n"));
+ return;
+ }
+
+ for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) {
+ if (!got_user_obj && current_ace->owner_type == UID_ACE &&
+ sid_equal(&current_ace->trustee, pfile_owner_sid)) {
+ current_ace->type = SMB_ACL_USER_OBJ;
+ got_user_obj = True;
+ }
+ if (!got_group_obj && current_ace->owner_type == GID_ACE &&
+ sid_equal(&current_ace->trustee, pfile_grp_sid)) {
+ current_ace->type = SMB_ACL_GROUP_OBJ;
+ got_group_obj = True;
+ }
+ }
+ if (!got_user_obj)
+ DEBUG(10,("check_owning_objs: ACL is missing an owner entry.\n"));
+ if (!got_group_obj)
+ DEBUG(10,("check_owning_objs: ACL is missing an owning group entry.\n"));
+}
+
+/****************************************************************************
+ Unpack a SEC_DESC into two canonical ace lists.
+****************************************************************************/
+
+static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst,
+ DOM_SID *pfile_owner_sid,
+ DOM_SID *pfile_grp_sid,
+ canon_ace **ppfile_ace, canon_ace **ppdir_ace,
+ SEC_ACL *dacl)
+{
+ extern DOM_SID global_sid_Creator_Owner;
+ extern DOM_SID global_sid_Creator_Group;
+ extern DOM_SID global_sid_World;
+ extern struct generic_mapping file_generic_mapping;
+ BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False);
+ canon_ace *file_ace = NULL;
+ canon_ace *dir_ace = NULL;
+ canon_ace *tmp_ace = NULL;
+ canon_ace *current_ace = NULL;
+ BOOL got_dir_allow = False;
+ BOOL got_file_allow = False;
+ int i, j;
+
+ *ppfile_ace = NULL;
+ *ppdir_ace = NULL;
+
+ /*
+ * Convert the incoming ACL into a more regular form.
+ */
+
+ for(i = 0; i < dacl->num_aces; i++) {
+ SEC_ACE *psa = &dacl->ace[i];
+
+ if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
+ DEBUG(3,("create_canon_ace_lists: unable to set anything but an ALLOW or DENY ACE.\n"));
+ return False;
+ }
+
+ if (nt4_compatible_acls()) {
+ /*
+ * The security mask may be UNIX_ACCESS_NONE which should map into
+ * no permissions (we overload the WRITE_OWNER bit for this) or it
+ * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
+ * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
+ */
+
+ /*
+ * Convert GENERIC bits to specific bits.
+ */
+
+ se_map_generic(&psa->info.mask, &file_generic_mapping);
+
+ psa->info.mask &= (UNIX_ACCESS_NONE|FILE_ALL_ACCESS);
+
+ if(psa->info.mask != UNIX_ACCESS_NONE)
+ psa->info.mask &= ~UNIX_ACCESS_NONE;
+ }
+ }
+
+ /*
+ * Deal with the fact that NT 4.x re-writes the canonical format
+ * that we return for default ACLs. If a directory ACE is identical
+ * to a inherited directory ACE then NT changes the bits so that the
+ * first ACE is set to OI|IO and the second ACE for this SID is set
+ * to CI. We need to repair this. JRA.
+ */
+
+ for(i = 0; i < dacl->num_aces; i++) {
+ SEC_ACE *psa1 = &dacl->ace[i];
+
+ for (j = i + 1; j < dacl->num_aces; j++) {
+ SEC_ACE *psa2 = &dacl->ace[j];
+
+ if (psa1->info.mask != psa2->info.mask)
+ continue;
+
+ if (!sid_equal(&psa1->trustee, &psa2->trustee))
+ continue;
+
+ /*
+ * Ok - permission bits and SIDs are equal.
+ * Check if flags were re-written.
+ */
+
+ if (psa1->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+
+ psa1->flags |= (psa2->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
+ psa2->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
+
+ } else if (psa2->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+
+ psa2->flags |= (psa1->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
+ psa1->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
+
+ }
+ }
+ }
+
+ for(i = 0; i < dacl->num_aces; i++) {
+ SEC_ACE *psa = &dacl->ace[i];
+
+ /*
+ * Ignore non-mappable SIDs (NT Authority, BUILTIN etc).
+ */
+
+ if (non_mappable_sid(&psa->trustee)) {
+ fstring str;
+ DEBUG(10,("create_canon_ace_lists: ignoring non-mappable SID %s\n",
+ sid_to_string(str, &psa->trustee) ));
+ continue;
+ }
+
+ /*
+ * Create a cannon_ace entry representing this NT DACL ACE.
+ */
+
+ if ((current_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ DEBUG(0,("create_canon_ace_lists: malloc fail.\n"));
+ return False;
+ }
+
+ ZERO_STRUCTP(current_ace);
+
+ sid_copy(&current_ace->trustee, &psa->trustee);
+
+ /*
+ * Try and work out if the SID is a user or group
+ * as we need to flag these differently for POSIX.
+ * Note what kind of a POSIX ACL this should map to.
+ */
+
+ if( sid_equal(&current_ace->trustee, &global_sid_World)) {
+ current_ace->owner_type = WORLD_ACE;
+ current_ace->unix_ug.world = -1;
+ current_ace->type = SMB_ACL_OTHER;
+ } else if (sid_equal(&current_ace->trustee, &global_sid_Creator_Owner)) {
+ current_ace->owner_type = UID_ACE;
+ current_ace->unix_ug.uid = pst->st_uid;
+ current_ace->type = SMB_ACL_USER_OBJ;
+
+ /*
+ * The Creator Owner entry only specifies inheritable permissions,
+ * never access permissions. WinNT doesn't always set the ACE to
+ *INHERIT_ONLY, though.
+ */
+
+ if (nt4_compatible_acls())
+ psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+ } else if (sid_equal(&current_ace->trustee, &global_sid_Creator_Group)) {
+ current_ace->owner_type = GID_ACE;
+ current_ace->unix_ug.gid = pst->st_gid;
+ current_ace->type = SMB_ACL_GROUP_OBJ;
+
+ /*
+ * The Creator Group entry only specifies inheritable permissions,
+ * never access permissions. WinNT doesn't always set the ACE to
+ *INHERIT_ONLY, though.
+ */
+ if (nt4_compatible_acls())
+ psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+
+ } else if (NT_STATUS_IS_OK(sid_to_gid( &current_ace->trustee, &current_ace->unix_ug.gid))) {
+ current_ace->owner_type = GID_ACE;
+ current_ace->type = SMB_ACL_GROUP;
+ } else if (NT_STATUS_IS_OK(sid_to_uid( &current_ace->trustee, &current_ace->unix_ug.uid))) {
+ current_ace->owner_type = UID_ACE;
+ current_ace->type = SMB_ACL_USER;
+ } else {
+ fstring str;
+
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ DEBUG(0,("create_canon_ace_lists: unable to map SID %s to uid or gid.\n",
+ sid_to_string(str, &current_ace->trustee) ));
+ SAFE_FREE(current_ace);
+ return False;
+ }
+
+ /*
+ * Map the given NT permissions into a UNIX mode_t containing only
+ * S_I(R|W|X)USR bits.
+ */
+
+ current_ace->perms |= map_nt_perms( psa->info, S_IRUSR);
+ current_ace->attr = (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) ? ALLOW_ACE : DENY_ACE;
+ current_ace->inherited = ((psa->flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False);
+
+ /*
+ * Now add the created ace to either the file list, the directory
+ * list, or both. We *MUST* preserve the order here (hence we use
+ * DLIST_ADD_END) as NT ACLs are order dependent.
+ */
+
+ if (fsp->is_directory) {
+
+ /*
+ * We can only add to the default POSIX ACE list if the ACE is
+ * designed to be inherited by both files and directories.
+ */
+
+ if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
+ (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
+
+ DLIST_ADD_END(dir_ace, current_ace, tmp_ace);
+
+ /*
+ * Note if this was an allow ace. We can't process
+ * any further deny ace's after this.
+ */
+
+ if (current_ace->attr == ALLOW_ACE)
+ got_dir_allow = True;
+
+ if ((current_ace->attr == DENY_ACE) && got_dir_allow) {
+ DEBUG(0,("create_canon_ace_lists: malformed ACL in inheritable ACL ! \
+Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ SAFE_FREE(current_ace);
+ return False;
+ }
+
+ if( DEBUGLVL( 10 )) {
+ dbgtext("create_canon_ace_lists: adding dir ACL:\n");
+ print_canon_ace( current_ace, 0);
+ }
+
+ /*
+ * If this is not an inherit only ACE we need to add a duplicate
+ * to the file acl.
+ */
+
+ if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
+ canon_ace *dup_ace = dup_canon_ace(current_ace);
+
+ if (!dup_ace) {
+ DEBUG(0,("create_canon_ace_lists: malloc fail !\n"));
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ return False;
+ }
+
+ /*
+ * We must not free current_ace here as its
+ * pointer is now owned by the dir_ace list.
+ */
+ current_ace = dup_ace;
+ } else {
+ /*
+ * We must not free current_ace here as its
+ * pointer is now owned by the dir_ace list.
+ */
+ current_ace = NULL;
+ }
+ }
+ }
+
+ /*
+ * Only add to the file ACL if not inherit only.
+ */
+
+ if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
+ DLIST_ADD_END(file_ace, current_ace, tmp_ace);
+
+ /*
+ * Note if this was an allow ace. We can't process
+ * any further deny ace's after this.
+ */
+
+ if (current_ace->attr == ALLOW_ACE)
+ got_file_allow = True;
+
+ if ((current_ace->attr == DENY_ACE) && got_file_allow) {
+ DEBUG(0,("create_canon_ace_lists: malformed ACL in file ACL ! \
+Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ SAFE_FREE(current_ace);
+ return False;
+ }
+
+ if( DEBUGLVL( 10 )) {
+ dbgtext("create_canon_ace_lists: adding file ACL:\n");
+ print_canon_ace( current_ace, 0);
+ }
+ all_aces_are_inherit_only = False;
+ /*
+ * We must not free current_ace here as its
+ * pointer is now owned by the file_ace list.
+ */
+ current_ace = NULL;
+ }
+
+ /*
+ * Free if ACE was not added.
+ */
+
+ SAFE_FREE(current_ace);
+ }
+
+ if (fsp->is_directory && all_aces_are_inherit_only) {
+ /*
+ * Windows 2000 is doing one of these weird 'inherit acl'
+ * traverses to conserve NTFS ACL resources. Just pretend
+ * there was no DACL sent. JRA.
+ */
+
+ DEBUG(10,("create_canon_ace_lists: Win2k inherit acl traverse. Ignoring DACL.\n"));
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ file_ace = NULL;
+ dir_ace = NULL;
+ } else {
+ /*
+ * Check if we have SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries in each
+ * ACL. If we don't have them, check if any SMB_ACL_USER/SMB_ACL_GROUP
+ * entries can be converted to *_OBJ. Usually we will already have these
+ * entries in the Default ACL, and the Access ACL will not have them.
+ */
+ check_owning_objs(file_ace, pfile_owner_sid, pfile_grp_sid);
+ check_owning_objs(dir_ace, pfile_owner_sid, pfile_grp_sid);
+ }
+
+ *ppfile_ace = file_ace;
+ *ppdir_ace = dir_ace;
+
+ return True;
+}
+
+/****************************************************************************
+ ASCII art time again... JRA :-).
+
+ We have 4 cases to process when moving from an NT ACL to a POSIX ACL. Firstly,
+ we insist the ACL is in canonical form (ie. all DENY entries preceede ALLOW
+ entries). Secondly, the merge code has ensured that all duplicate SID entries for
+ allow or deny have been merged, so the same SID can only appear once in the deny
+ list or once in the allow list.
+
+ We then process as follows :
+
+ ---------------------------------------------------------------------------
+ First pass - look for a Everyone DENY entry.
+
+ If it is deny all (rwx) trunate the list at this point.
+ Else, walk the list from this point and use the deny permissions of this
+ entry as a mask on all following allow entries. Finally, delete
+ the Everyone DENY entry (we have applied it to everything possible).
+
+ In addition, in this pass we remove any DENY entries that have
+ no permissions (ie. they are a DENY nothing).
+ ---------------------------------------------------------------------------
+ Second pass - only deal with deny user entries.
+
+ DENY user1 (perms XXX)
+
+ new_perms = 0
+ for all following allow group entries where user1 is in group
+ new_perms |= group_perms;
+
+ user1 entry perms = new_perms & ~ XXX;
+
+ Convert the deny entry to an allow entry with the new perms and
+ push to the end of the list. Note if the user was in no groups
+ this maps to a specific allow nothing entry for this user.
+
+ The common case from the NT ACL choser (userX deny all) is
+ optimised so we don't do the group lookup - we just map to
+ an allow nothing entry.
+
+ What we're doing here is inferring the allow permissions the
+ person setting the ACE on user1 wanted by looking at the allow
+ permissions on the groups the user is currently in. This will
+ be a snapshot, depending on group membership but is the best
+ we can do and has the advantage of failing closed rather than
+ open.
+ ---------------------------------------------------------------------------
+ Third pass - only deal with deny group entries.
+
+ DENY group1 (perms XXX)
+
+ for all following allow user entries where user is in group1
+ user entry perms = user entry perms & ~ XXX;
+
+ If there is a group Everyone allow entry with permissions YYY,
+ convert the group1 entry to an allow entry and modify its
+ permissions to be :
+
+ new_perms = YYY & ~ XXX
+
+ and push to the end of the list.
+
+ If there is no group Everyone allow entry then convert the
+ group1 entry to a allow nothing entry and push to the end of the list.
+
+ Note that the common case from the NT ACL choser (groupX deny all)
+ cannot be optimised here as we need to modify user entries who are
+ in the group to change them to a deny all also.
+
+ What we're doing here is modifying the allow permissions of
+ user entries (which are more specific in POSIX ACLs) to mask
+ out the explicit deny set on the group they are in. This will
+ be a snapshot depending on current group membership but is the
+ best we can do and has the advantage of failing closed rather
+ than open.
+ ---------------------------------------------------------------------------
+ Fourth pass - cope with cumulative permissions.
+
+ for all allow user entries, if there exists an allow group entry with
+ more permissive permissions, and the user is in that group, rewrite the
+ allow user permissions to contain both sets of permissions.
+
+ Currently the code for this is #ifdef'ed out as these semantics make
+ no sense to me. JRA.
+ ---------------------------------------------------------------------------
+
+ Note we *MUST* do the deny user pass first as this will convert deny user
+ entries into allow user entries which can then be processed by the deny
+ group pass.
+
+ The above algorithm took a *lot* of thinking about - hence this
+ explaination :-). JRA.
+****************************************************************************/
+
+/****************************************************************************
+ Process a canon_ace list entries. This is very complex code. We need
+ to go through and remove the "deny" permissions from any allow entry that matches
+ the id of this entry. We have already refused any NT ACL that wasn't in correct
+ order (DENY followed by ALLOW). If any allow entry ends up with zero permissions,
+ we just remove it (to fail safe). We have already removed any duplicate ace
+ entries. Treat an "Everyone" DENY_ACE as a special case - use it to mask all
+ allow entries.
+****************************************************************************/
+
+static void process_deny_list( canon_ace **pp_ace_list )
+{
+ extern DOM_SID global_sid_World;
+ canon_ace *ace_list = *pp_ace_list;
+ canon_ace *curr_ace = NULL;
+ canon_ace *curr_ace_next = NULL;
+
+ /* Pass 1 above - look for an Everyone, deny entry. */
+
+ for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
+ canon_ace *allow_ace_p;
+
+ curr_ace_next = curr_ace->next; /* So we can't lose the link. */
+
+ if (curr_ace->attr != DENY_ACE)
+ continue;
+
+ if (curr_ace->perms == (mode_t)0) {
+
+ /* Deny nothing entry - delete. */
+
+ DLIST_REMOVE(ace_list, curr_ace);
+ continue;
+ }
+
+ if (!sid_equal(&curr_ace->trustee, &global_sid_World))
+ continue;
+
+ /* JRATEST - assert. */
+ SMB_ASSERT(curr_ace->owner_type == WORLD_ACE);
+
+ if (curr_ace->perms == ALL_ACE_PERMS) {
+
+ /*
+ * Optimisation. This is a DENY_ALL to Everyone. Truncate the
+ * list at this point including this entry.
+ */
+
+ canon_ace *prev_entry = curr_ace->prev;
+
+ free_canon_ace_list( curr_ace );
+ if (prev_entry)
+ prev_entry->next = NULL;
+ else {
+ /* We deleted the entire list. */
+ ace_list = NULL;
+ }
+ break;
+ }
+
+ for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
+
+ /*
+ * Only mask off allow entries.
+ */
+
+ if (allow_ace_p->attr != ALLOW_ACE)
+ continue;
+
+ allow_ace_p->perms &= ~curr_ace->perms;
+ }
+
+ /*
+ * Now it's been applied, remove it.
+ */
+
+ DLIST_REMOVE(ace_list, curr_ace);
+ }
+
+ /* Pass 2 above - deal with deny user entries. */
+
+ for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
+ mode_t new_perms = (mode_t)0;
+ canon_ace *allow_ace_p;
+ canon_ace *tmp_ace;
+
+ curr_ace_next = curr_ace->next; /* So we can't lose the link. */
+
+ if (curr_ace->attr != DENY_ACE)
+ continue;
+
+ if (curr_ace->owner_type != UID_ACE)
+ continue;
+
+ if (curr_ace->perms == ALL_ACE_PERMS) {
+
+ /*
+ * Optimisation - this is a deny everything to this user.
+ * Convert to an allow nothing and push to the end of the list.
+ */
+
+ curr_ace->attr = ALLOW_ACE;
+ curr_ace->perms = (mode_t)0;
+ DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
+ continue;
+ }
+
+ for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
+
+ if (allow_ace_p->attr != ALLOW_ACE)
+ continue;
+
+ /* We process GID_ACE and WORLD_ACE entries only. */
+
+ if (allow_ace_p->owner_type == UID_ACE)
+ continue;
+
+ if (uid_entry_in_group( curr_ace, allow_ace_p))
+ new_perms |= allow_ace_p->perms;
+ }
+
+ /*
+ * Convert to a allow entry, modify the perms and push to the end
+ * of the list.
+ */
+
+ curr_ace->attr = ALLOW_ACE;
+ curr_ace->perms = (new_perms & ~curr_ace->perms);
+ DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
+ }
+
+ /* Pass 3 above - deal with deny group entries. */
+
+ for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
+ canon_ace *tmp_ace;
+ canon_ace *allow_ace_p;
+ canon_ace *allow_everyone_p = NULL;
+
+ curr_ace_next = curr_ace->next; /* So we can't lose the link. */
+
+ if (curr_ace->attr != DENY_ACE)
+ continue;
+
+ if (curr_ace->owner_type != GID_ACE)
+ continue;
+
+ for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
+
+ if (allow_ace_p->attr != ALLOW_ACE)
+ continue;
+
+ /* Store a pointer to the Everyone allow, if it exists. */
+ if (allow_ace_p->owner_type == WORLD_ACE)
+ allow_everyone_p = allow_ace_p;
+
+ /* We process UID_ACE entries only. */
+
+ if (allow_ace_p->owner_type != UID_ACE)
+ continue;
+
+ /* Mask off the deny group perms. */
+
+ if (uid_entry_in_group( allow_ace_p, curr_ace))
+ allow_ace_p->perms &= ~curr_ace->perms;
+ }
+
+ /*
+ * Convert the deny to an allow with the correct perms and
+ * push to the end of the list.
+ */
+
+ curr_ace->attr = ALLOW_ACE;
+ if (allow_everyone_p)
+ curr_ace->perms = allow_everyone_p->perms & ~curr_ace->perms;
+ else
+ curr_ace->perms = (mode_t)0;
+ DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
+
+ }
+
+ /* Doing this fourth pass allows Windows semantics to be layered
+ * on top of POSIX semantics. I'm not sure if this is desirable.
+ * For example, in W2K ACLs there is no way to say, "Group X no
+ * access, user Y full access" if user Y is a member of group X.
+ * This seems completely broken semantics to me.... JRA.
+ */
+
+#if 0
+ /* Pass 4 above - deal with allow entries. */
+
+ for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
+ canon_ace *allow_ace_p;
+
+ curr_ace_next = curr_ace->next; /* So we can't lose the link. */
+
+ if (curr_ace->attr != ALLOW_ACE)
+ continue;
+
+ if (curr_ace->owner_type != UID_ACE)
+ continue;
+
+ for (allow_ace_p = ace_list; allow_ace_p; allow_ace_p = allow_ace_p->next) {
+
+ if (allow_ace_p->attr != ALLOW_ACE)
+ continue;
+
+ /* We process GID_ACE entries only. */
+
+ if (allow_ace_p->owner_type != GID_ACE)
+ continue;
+
+ /* OR in the group perms. */
+
+ if (uid_entry_in_group( curr_ace, allow_ace_p))
+ curr_ace->perms |= allow_ace_p->perms;
+ }
+ }
+#endif
+
+ *pp_ace_list = ace_list;
+}
+
+/****************************************************************************
+ Create a default mode that will be used if a security descriptor entry has
+ no user/group/world entries.
+****************************************************************************/
+
+static mode_t create_default_mode(files_struct *fsp, BOOL interitable_mode)
+{
+ int snum = SNUM(fsp->conn);
+ mode_t and_bits = (mode_t)0;
+ mode_t or_bits = (mode_t)0;
+ mode_t mode = interitable_mode ? unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name) : S_IRUSR;
+
+ if (fsp->is_directory)
+ mode |= (S_IWUSR|S_IXUSR);
+
+ /*
+ * Now AND with the create mode/directory mode bits then OR with the
+ * force create mode/force directory mode bits.
+ */
+
+ if (fsp->is_directory) {
+ and_bits = lp_dir_security_mask(snum);
+ or_bits = lp_force_dir_security_mode(snum);
+ } else {
+ and_bits = lp_security_mask(snum);
+ or_bits = lp_force_security_mode(snum);
+ }
+
+ return ((mode & and_bits)|or_bits);
+}
+
+/****************************************************************************
+ Unpack a SEC_DESC into two canonical ace lists. We don't depend on this
+ succeeding.
+****************************************************************************/
+
+static BOOL unpack_canon_ace(files_struct *fsp,
+ SMB_STRUCT_STAT *pst,
+ DOM_SID *pfile_owner_sid,
+ DOM_SID *pfile_grp_sid,
+ canon_ace **ppfile_ace, canon_ace **ppdir_ace,
+ uint32 security_info_sent, SEC_DESC *psd)
+{
+ canon_ace *file_ace = NULL;
+ canon_ace *dir_ace = NULL;
+
+ *ppfile_ace = NULL;
+ *ppdir_ace = NULL;
+
+ if(security_info_sent == 0) {
+ DEBUG(0,("unpack_canon_ace: no security info sent !\n"));
+ return False;
+ }
+
+ /*
+ * If no DACL then this is a chown only security descriptor.
+ */
+
+ if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !psd->dacl)
+ return True;
+
+ /*
+ * Now go through the DACL and create the canon_ace lists.
+ */
+
+ if (!create_canon_ace_lists( fsp, pst, pfile_owner_sid, pfile_grp_sid,
+ &file_ace, &dir_ace, psd->dacl))
+ return False;
+
+ if ((file_ace == NULL) && (dir_ace == NULL)) {
+ /* W2K traverse DACL set - ignore. */
+ return True;
+ }
+
+ /*
+ * Go through the canon_ace list and merge entries
+ * belonging to identical users of identical allow or deny type.
+ * We can do this as all deny entries come first, followed by
+ * all allow entries (we have mandated this before accepting this acl).
+ */
+
+ print_canon_ace_list( "file ace - before merge", file_ace);
+ merge_aces( &file_ace );
+
+ print_canon_ace_list( "dir ace - before merge", dir_ace);
+ merge_aces( &dir_ace );
+
+ /*
+ * NT ACLs are order dependent. Go through the acl lists and
+ * process DENY entries by masking the allow entries.
+ */
+
+ print_canon_ace_list( "file ace - before deny", file_ace);
+ process_deny_list( &file_ace);
+
+ print_canon_ace_list( "dir ace - before deny", dir_ace);
+ process_deny_list( &dir_ace);
+
+ /*
+ * A well formed POSIX file or default ACL has at least 3 entries, a
+ * SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ
+ * and optionally a mask entry. Ensure this is the case.
+ */
+
+ print_canon_ace_list( "file ace - before valid", file_ace);
+
+ /*
+ * A default 3 element mode entry for a file should be r-- --- ---.
+ * A default 3 element mode entry for a directory should be rwx --- ---.
+ */
+
+ pst->st_mode = create_default_mode(fsp, False);
+
+ if (!ensure_canon_entry_valid(&file_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ return False;
+ }
+
+ print_canon_ace_list( "dir ace - before valid", dir_ace);
+
+ /*
+ * A default inheritable 3 element mode entry for a directory should be the
+ * mode Samba will use to create a file within. Ensure user rwx bits are set if
+ * it's a directory.
+ */
+
+ pst->st_mode = create_default_mode(fsp, True);
+
+ if (dir_ace && !ensure_canon_entry_valid(&dir_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ return False;
+ }
+
+ print_canon_ace_list( "file ace - return", file_ace);
+ print_canon_ace_list( "dir ace - return", dir_ace);
+
+ *ppfile_ace = file_ace;
+ *ppdir_ace = dir_ace;
+ return True;
+
+}
+
+/******************************************************************************
+ When returning permissions, try and fit NT display
+ semantics if possible. Note the the canon_entries here must have been malloced.
+ The list format should be - first entry = owner, followed by group and other user
+ entries, last entry = other.
+
+ Note that this doesn't exactly match the NT semantics for an ACL. As POSIX entries
+ are not ordered, and match on the most specific entry rather than walking a list,
+ then a simple POSIX permission of rw-r--r-- should really map to 5 entries,
+
+ Entry 0: owner : deny all except read and write.
+ Entry 1: group : deny all except read.
+ Entry 2: owner : allow read and write.
+ Entry 3: group : allow read.
+ Entry 4: Everyone : allow read.
+
+ But NT cannot display this in their ACL editor !
+********************************************************************************/
+
+static void arrange_posix_perms( char *filename, canon_ace **pp_list_head)
+{
+ canon_ace *list_head = *pp_list_head;
+ canon_ace *owner_ace = NULL;
+ canon_ace *other_ace = NULL;
+ canon_ace *ace = NULL;
+
+ for (ace = list_head; ace; ace = ace->next) {
+ if (ace->type == SMB_ACL_USER_OBJ)
+ owner_ace = ace;
+ else if (ace->type == SMB_ACL_OTHER) {
+ /* Last ace - this is "other" */
+ other_ace = ace;
+ }
+ }
+
+ if (!owner_ace || !other_ace) {
+ DEBUG(0,("arrange_posix_perms: Invalid POSIX permissions for file %s, missing owner or other.\n",
+ filename ));
+ return;
+ }
+
+ /*
+ * The POSIX algorithm applies to owner first, and other last,
+ * so ensure they are arranged in this order.
+ */
+
+ if (owner_ace) {
+ DLIST_PROMOTE(list_head, owner_ace);
+ }
+
+ if (other_ace) {
+ DLIST_DEMOTE(list_head, other_ace, ace);
+ }
+
+ /* We have probably changed the head of the list. */
+
+ *pp_list_head = list_head;
+}
+
+/****************************************************************************
+ Create a linked list of canonical ACE entries.
+****************************************************************************/
+
+static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf,
+ DOM_SID *powner, DOM_SID *pgroup, struct pai_val *pal, SMB_ACL_TYPE_T the_acl_type)
+{
+ extern DOM_SID global_sid_World;
+ connection_struct *conn = fsp->conn;
+ mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
+ canon_ace *list_head = NULL;
+ canon_ace *ace = NULL;
+ canon_ace *next_ace = NULL;
+ int entry_id = SMB_ACL_FIRST_ENTRY;
+ SMB_ACL_ENTRY_T entry;
+ size_t ace_count;
+
+ while ( posix_acl && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1)) {
+ SMB_ACL_TAG_T tagtype;
+ SMB_ACL_PERMSET_T permset;
+ DOM_SID sid;
+ posix_id unix_ug;
+ enum ace_owner owner_type;
+
+ /* get_next... */
+ if (entry_id == SMB_ACL_FIRST_ENTRY)
+ entry_id = SMB_ACL_NEXT_ENTRY;
+
+ /* Is this a MASK entry ? */
+ if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1)
+ continue;
+
+ if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1)
+ continue;
+
+ /* Decide which SID to use based on the ACL type. */
+ switch(tagtype) {
+ case SMB_ACL_USER_OBJ:
+ /* Get the SID from the owner. */
+ sid_copy(&sid, powner);
+ unix_ug.uid = psbuf->st_uid;
+ owner_type = UID_ACE;
+ break;
+ case SMB_ACL_USER:
+ {
+ uid_t *puid = (uid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
+ if (puid == NULL) {
+ DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
+ continue;
+ }
+ /*
+ * A SMB_ACL_USER entry for the owner is shadowed by the
+ * SMB_ACL_USER_OBJ entry and Windows also cannot represent
+ * that entry, so we ignore it. We also don't create such
+ * entries out of the blue when setting ACLs, so a get/set
+ * cycle will drop them.
+ */
+ if (the_acl_type == SMB_ACL_TYPE_ACCESS && *puid == psbuf->st_uid)
+ continue;
+ uid_to_sid( &sid, *puid);
+ unix_ug.uid = *puid;
+ owner_type = UID_ACE;
+ SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)puid,tagtype);
+ break;
+ }
+ case SMB_ACL_GROUP_OBJ:
+ /* Get the SID from the owning group. */
+ sid_copy(&sid, pgroup);
+ unix_ug.gid = psbuf->st_gid;
+ owner_type = GID_ACE;
+ break;
+ case SMB_ACL_GROUP:
+ {
+ gid_t *pgid = (gid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
+ if (pgid == NULL) {
+ DEBUG(0,("canonicalise_acl: Failed to get gid.\n"));
+ continue;
+ }
+ gid_to_sid( &sid, *pgid);
+ unix_ug.gid = *pgid;
+ owner_type = GID_ACE;
+ SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)pgid,tagtype);
+ break;
+ }
+ case SMB_ACL_MASK:
+ acl_mask = convert_permset_to_mode_t(conn, permset);
+ continue; /* Don't count the mask as an entry. */
+ case SMB_ACL_OTHER:
+ /* Use the Everyone SID */
+ sid = global_sid_World;
+ unix_ug.world = -1;
+ owner_type = WORLD_ACE;
+ break;
+ default:
+ DEBUG(0,("canonicalise_acl: Unknown tagtype %u\n", (unsigned int)tagtype));
+ continue;
+ }
+
+ /*
+ * Add this entry to the list.
+ */
+
+ if ((ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
+ goto fail;
+
+ ZERO_STRUCTP(ace);
+ ace->type = tagtype;
+ ace->perms = convert_permset_to_mode_t(conn, permset);
+ ace->attr = ALLOW_ACE;
+ ace->trustee = sid;
+ ace->unix_ug = unix_ug;
+ ace->owner_type = owner_type;
+ ace->inherited = get_inherited_flag(pal, ace, (the_acl_type == SMB_ACL_TYPE_DEFAULT));
+
+ DLIST_ADD(list_head, ace);
+ }
+
+ /*
+ * This next call will ensure we have at least a user/group/world set.
+ */
+
+ if (!ensure_canon_entry_valid(&list_head, fsp, powner, pgroup, psbuf, False))
+ goto fail;
+
+ /*
+ * Now go through the list, masking the permissions with the
+ * acl_mask. Ensure all DENY Entries are at the start of the list.
+ */
+
+ DEBUG(10,("canonicalise_acl: %s ace entries before arrange :\n", the_acl_type == SMB_ACL_TYPE_ACCESS ? "Access" : "Default" ));
+
+ for ( ace_count = 0, ace = list_head; ace; ace = next_ace, ace_count++) {
+ next_ace = ace->next;
+
+ /* Masks are only applied to entries other than USER_OBJ and OTHER. */
+ if (ace->type != SMB_ACL_OTHER && ace->type != SMB_ACL_USER_OBJ)
+ ace->perms &= acl_mask;
+
+ if (ace->perms == 0) {
+ DLIST_PROMOTE(list_head, ace);
+ }
+
+ if( DEBUGLVL( 10 ) ) {
+ print_canon_ace(ace, ace_count);
+ }
+ }
+
+ arrange_posix_perms(fsp->fsp_name,&list_head );
+
+ print_canon_ace_list( "canonicalise_acl: ace entries after arrange", list_head );
+
+ return list_head;
+
+ fail:
+
+ free_canon_ace_list(list_head);
+ return NULL;
+}
+
+/****************************************************************************
+ Attempt to apply an ACL to a file or directory.
+****************************************************************************/
+
+static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL default_ace, BOOL *pacl_set_support)
+{
+ connection_struct *conn = fsp->conn;
+ BOOL ret = False;
+ SMB_ACL_T the_acl = SMB_VFS_SYS_ACL_INIT(conn, (int)count_canon_ace_list(the_ace) + 1);
+ canon_ace *p_ace;
+ int i;
+ SMB_ACL_ENTRY_T mask_entry;
+ BOOL got_mask_entry = False;
+ SMB_ACL_PERMSET_T mask_permset;
+ SMB_ACL_TYPE_T the_acl_type = (default_ace ? SMB_ACL_TYPE_DEFAULT : SMB_ACL_TYPE_ACCESS);
+ BOOL needs_mask = False;
+ mode_t mask_perms = 0;
+
+#if defined(POSIX_ACL_NEEDS_MASK)
+ /* HP-UX always wants to have a mask (called "class" there). */
+ needs_mask = True;
+#endif
+
+ if (the_acl == NULL) {
+
+ if (errno != ENOSYS) {
+ /*
+ * Only print this error message if we have some kind of ACL
+ * support that's not working. Otherwise we would always get this.
+ */
+ DEBUG(0,("set_canon_ace_list: Unable to init %s ACL. (%s)\n",
+ default_ace ? "default" : "file", strerror(errno) ));
+ }
+ *pacl_set_support = False;
+ return False;
+ }
+
+ if( DEBUGLVL( 10 )) {
+ dbgtext("set_canon_ace_list: setting ACL:\n");
+ for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
+ print_canon_ace( p_ace, i);
+ }
+ }
+
+ for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
+ SMB_ACL_ENTRY_T the_entry;
+ SMB_ACL_PERMSET_T the_permset;
+
+ /*
+ * ACLs only "need" an ACL_MASK entry if there are any named user or
+ * named group entries. But if there is an ACL_MASK entry, it applies
+ * to ACL_USER, ACL_GROUP, and ACL_GROUP_OBJ entries. Set the mask
+ * so that it doesn't deny (i.e., mask off) any permissions.
+ */
+
+ if (p_ace->type == SMB_ACL_USER || p_ace->type == SMB_ACL_GROUP) {
+ needs_mask = True;
+ mask_perms |= p_ace->perms;
+ } else if (p_ace->type == SMB_ACL_GROUP_OBJ) {
+ mask_perms |= p_ace->perms;
+ }
+
+ /*
+ * Get the entry for this ACE.
+ */
+
+ if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &the_entry) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to create entry %d. (%s)\n",
+ i, strerror(errno) ));
+ goto done;
+ }
+
+ if (p_ace->type == SMB_ACL_MASK) {
+ mask_entry = the_entry;
+ got_mask_entry = True;
+ }
+
+ /*
+ * Ok - we now know the ACL calls should be working, don't
+ * allow fallback to chmod.
+ */
+
+ *pacl_set_support = True;
+
+ /*
+ * Initialise the entry from the canon_ace.
+ */
+
+ /*
+ * First tell the entry what type of ACE this is.
+ */
+
+ if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, the_entry, p_ace->type) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to set tag type on entry %d. (%s)\n",
+ i, strerror(errno) ));
+ goto done;
+ }
+
+ /*
+ * Only set the qualifier (user or group id) if the entry is a user
+ * or group id ACE.
+ */
+
+ if ((p_ace->type == SMB_ACL_USER) || (p_ace->type == SMB_ACL_GROUP)) {
+ if (SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, the_entry,(void *)&p_ace->unix_ug.uid) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to set qualifier on entry %d. (%s)\n",
+ i, strerror(errno) ));
+ goto done;
+ }
+ }
+
+ /*
+ * Convert the mode_t perms in the canon_ace to a POSIX permset.
+ */
+
+ if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, the_entry, &the_permset) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to get permset on entry %d. (%s)\n",
+ i, strerror(errno) ));
+ goto done;
+ }
+
+ if (map_acl_perms_to_permset(conn, p_ace->perms, &the_permset) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to create permset for mode (%u) on entry %d. (%s)\n",
+ (unsigned int)p_ace->perms, i, strerror(errno) ));
+ goto done;
+ }
+
+ /*
+ * ..and apply them to the entry.
+ */
+
+ if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, the_entry, the_permset) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to add permset on entry %d. (%s)\n",
+ i, strerror(errno) ));
+ goto done;
+ }
+
+ if( DEBUGLVL( 10 ))
+ print_canon_ace( p_ace, i);
+
+ }
+
+ if (needs_mask && !got_mask_entry) {
+ if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &mask_entry) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to create mask entry. (%s)\n", strerror(errno) ));
+ goto done;
+ }
+
+ if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, mask_entry, SMB_ACL_MASK) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to set tag type on mask entry. (%s)\n",strerror(errno) ));
+ goto done;
+ }
+
+ if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, mask_entry, &mask_permset) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to get mask permset. (%s)\n", strerror(errno) ));
+ goto done;
+ }
+
+ if (map_acl_perms_to_permset(conn, S_IRUSR|S_IWUSR|S_IXUSR, &mask_permset) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to create mask permset. (%s)\n", strerror(errno) ));
+ goto done;
+ }
+
+ if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, mask_entry, mask_permset) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to add mask permset. (%s)\n", strerror(errno) ));
+ goto done;
+ }
+ }
+
+ /*
+ * Check if the ACL is valid.
+ */
+
+ if (SMB_VFS_SYS_ACL_VALID(conn, the_acl) == -1) {
+ DEBUG(0,("set_canon_ace_list: ACL type (%s) is invalid for set (%s).\n",
+ the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
+ strerror(errno) ));
+ goto done;
+ }
+
+ /*
+ * Finally apply it to the file or directory.
+ */
+
+ if(default_ace || fsp->is_directory || fsp->fd == -1) {
+ if (SMB_VFS_SYS_ACL_SET_FILE(conn, fsp->fsp_name, the_acl_type, the_acl) == -1) {
+ /*
+ * Some systems allow all the above calls and only fail with no ACL support
+ * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
+ */
+ if (errno == ENOSYS)
+ *pacl_set_support = False;
+
+#ifdef ENOTSUP
+ if (errno == ENOTSUP)
+ *pacl_set_support = False;
+#endif
+
+ DEBUG(2,("set_canon_ace_list: sys_acl_set_file type %s failed for file %s (%s).\n",
+ the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
+ fsp->fsp_name, strerror(errno) ));
+ goto done;
+ }
+ } else {
+ if (SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fd, the_acl) == -1) {
+ /*
+ * Some systems allow all the above calls and only fail with no ACL support
+ * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
+ */
+ if (errno == ENOSYS)
+ *pacl_set_support = False;
+
+#ifdef ENOTSUP
+ if (errno == ENOTSUP)
+ *pacl_set_support = False;
+#endif
+
+ DEBUG(2,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
+ fsp->fsp_name, strerror(errno) ));
+ goto done;
+ }
+ }
+
+ ret = True;
+
+ done:
+
+ if (the_acl != NULL)
+ SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl);
+
+ return ret;
+}
+
+/****************************************************************************
+ Find a particular canon_ace entry.
+****************************************************************************/
+
+static struct canon_ace *canon_ace_entry_for(struct canon_ace *list, SMB_ACL_TAG_T type, posix_id *id)
+{
+ while (list) {
+ if (list->type == type && ((type != SMB_ACL_USER && type != SMB_ACL_GROUP) ||
+ (type == SMB_ACL_USER && id && id->uid == list->unix_ug.uid) ||
+ (type == SMB_ACL_GROUP && id && id->gid == list->unix_ug.gid)))
+ break;
+ list = list->next;
+ }
+ return list;
+}
+
+/****************************************************************************
+
+****************************************************************************/
+
+SMB_ACL_T free_empty_sys_acl(connection_struct *conn, SMB_ACL_T the_acl)
+{
+ SMB_ACL_ENTRY_T entry;
+
+ if (!the_acl)
+ return NULL;
+ if (SMB_VFS_SYS_ACL_GET_ENTRY(conn, the_acl, SMB_ACL_FIRST_ENTRY, &entry) != 1) {
+ SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl);
+ return NULL;
+ }
+ return the_acl;
+}
+
+/****************************************************************************
+ Convert a canon_ace to a generic 3 element permission - if possible.
+****************************************************************************/
+
+#define MAP_PERM(p,mask,result) (((p) & (mask)) ? (result) : 0 )
+
+static BOOL convert_canon_ace_to_posix_perms( files_struct *fsp, canon_ace *file_ace_list, mode_t *posix_perms)
+{
+ int snum = SNUM(fsp->conn);
+ size_t ace_count = count_canon_ace_list(file_ace_list);
+ canon_ace *ace_p;
+ canon_ace *owner_ace = NULL;
+ canon_ace *group_ace = NULL;
+ canon_ace *other_ace = NULL;
+ mode_t and_bits;
+ mode_t or_bits;
+
+ if (ace_count != 3) {
+ DEBUG(3,("convert_canon_ace_to_posix_perms: Too many ACE entries for file %s to convert to \
+posix perms.\n", fsp->fsp_name ));
+ return False;
+ }
+
+ for (ace_p = file_ace_list; ace_p; ace_p = ace_p->next) {
+ if (ace_p->owner_type == UID_ACE)
+ owner_ace = ace_p;
+ else if (ace_p->owner_type == GID_ACE)
+ group_ace = ace_p;
+ else if (ace_p->owner_type == WORLD_ACE)
+ other_ace = ace_p;
+ }
+
+ if (!owner_ace || !group_ace || !other_ace) {
+ DEBUG(3,("convert_canon_ace_to_posix_perms: Can't get standard entries for file %s.\n",
+ fsp->fsp_name ));
+ return False;
+ }
+
+ *posix_perms = (mode_t)0;
+
+ *posix_perms |= owner_ace->perms;
+ *posix_perms |= MAP_PERM(group_ace->perms, S_IRUSR, S_IRGRP);
+ *posix_perms |= MAP_PERM(group_ace->perms, S_IWUSR, S_IWGRP);
+ *posix_perms |= MAP_PERM(group_ace->perms, S_IXUSR, S_IXGRP);
+ *posix_perms |= MAP_PERM(other_ace->perms, S_IRUSR, S_IROTH);
+ *posix_perms |= MAP_PERM(other_ace->perms, S_IWUSR, S_IWOTH);
+ *posix_perms |= MAP_PERM(other_ace->perms, S_IXUSR, S_IXOTH);
+
+ /* The owner must have at least read access. */
+
+ *posix_perms |= S_IRUSR;
+ if (fsp->is_directory)
+ *posix_perms |= (S_IWUSR|S_IXUSR);
+
+ /* If requested apply the masks. */
+
+ /* Get the initial bits to apply. */
+
+ if (fsp->is_directory) {
+ and_bits = lp_dir_security_mask(snum);
+ or_bits = lp_force_dir_security_mode(snum);
+ } else {
+ and_bits = lp_security_mask(snum);
+ or_bits = lp_force_security_mode(snum);
+ }
+
+ *posix_perms = (((*posix_perms) & and_bits)|or_bits);
+
+ DEBUG(10,("convert_canon_ace_to_posix_perms: converted u=%o,g=%o,w=%o to perm=0%o for file %s.\n",
+ (int)owner_ace->perms, (int)group_ace->perms, (int)other_ace->perms, (int)*posix_perms,
+ fsp->fsp_name ));
+
+ return True;
+}
+
+/****************************************************************************
+ Incoming NT ACLs on a directory can be split into a default POSIX acl (CI|OI|IO) and
+ a normal POSIX acl. Win2k needs these split acls re-merging into one ACL
+ with CI|OI set so it is inherited and also applies to the directory.
+ Based on code from "Jim McDonough" <jmcd@us.ibm.com>.
+****************************************************************************/
+
+static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces)
+{
+ size_t i, j;
+
+ for (i = 0; i < num_aces; i++) {
+ for (j = i+1; j < num_aces; j++) {
+ uint32 i_flags_ni = (nt_ace_list[i].flags & ~SEC_ACE_FLAG_INHERITED_ACE);
+ uint32 j_flags_ni = (nt_ace_list[j].flags & ~SEC_ACE_FLAG_INHERITED_ACE);
+ BOOL i_inh = (nt_ace_list[i].flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False;
+ BOOL j_inh = (nt_ace_list[j].flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False;
+
+ /* We know the lower number ACE's are file entries. */
+ if ((nt_ace_list[i].type == nt_ace_list[j].type) &&
+ (nt_ace_list[i].size == nt_ace_list[j].size) &&
+ (nt_ace_list[i].info.mask == nt_ace_list[j].info.mask) &&
+ sid_equal(&nt_ace_list[i].trustee, &nt_ace_list[j].trustee) &&
+ (i_inh == j_inh) &&
+ (i_flags_ni == 0) &&
+ (j_flags_ni == (SEC_ACE_FLAG_OBJECT_INHERIT|
+ SEC_ACE_FLAG_CONTAINER_INHERIT|
+ SEC_ACE_FLAG_INHERIT_ONLY))) {
+ /*
+ * W2K wants to have access allowed zero access ACE's
+ * at the end of the list. If the mask is zero, merge
+ * the non-inherited ACE onto the inherited ACE.
+ */
+
+ if (nt_ace_list[i].info.mask == 0) {
+ nt_ace_list[j].flags = SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
+ (i_inh ? SEC_ACE_FLAG_INHERITED_ACE : 0);
+ if (num_aces - i - 1 > 0)
+ memmove(&nt_ace_list[i], &nt_ace_list[i+1], (num_aces-i-1) *
+ sizeof(SEC_ACE));
+
+ DEBUG(10,("merge_default_aces: Merging zero access ACE %u onto ACE %u.\n",
+ (unsigned int)i, (unsigned int)j ));
+ } else {
+ /*
+ * These are identical except for the flags.
+ * Merge the inherited ACE onto the non-inherited ACE.
+ */
+
+ nt_ace_list[i].flags = SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
+ (i_inh ? SEC_ACE_FLAG_INHERITED_ACE : 0);
+ if (num_aces - j - 1 > 0)
+ memmove(&nt_ace_list[j], &nt_ace_list[j+1], (num_aces-j-1) *
+ sizeof(SEC_ACE));
+
+ DEBUG(10,("merge_default_aces: Merging ACE %u onto ACE %u.\n",
+ (unsigned int)j, (unsigned int)i ));
+ }
+ num_aces--;
+ break;
+ }
+ }
+ }
+
+ return num_aces;
+}
+/****************************************************************************
+ Reply to query a security descriptor from an fsp. If it succeeds it allocates
+ the space for the return elements and returns the size needed to return the
+ security descriptor. This should be the only external function needed for
+ the UNIX style get ACL.
+****************************************************************************/
+
+size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc)
+{
+ extern DOM_SID global_sid_Builtin_Administrators;
+ extern DOM_SID global_sid_Builtin_Users;
+ extern DOM_SID global_sid_Creator_Owner;
+ extern DOM_SID global_sid_Creator_Group;
+ connection_struct *conn = fsp->conn;
+ SMB_STRUCT_STAT sbuf;
+ SEC_ACE *nt_ace_list = NULL;
+ DOM_SID owner_sid;
+ DOM_SID group_sid;
+ size_t sd_size = 0;
+ SEC_ACL *psa = NULL;
+ size_t num_acls = 0;
+ size_t num_dir_acls = 0;
+ size_t num_aces = 0;
+ SMB_ACL_T posix_acl = NULL;
+ SMB_ACL_T dir_acl = NULL;
+ canon_ace *file_ace = NULL;
+ canon_ace *dir_ace = NULL;
+ size_t num_profile_acls = 0;
+ struct pai_val *pal = NULL;
+ SEC_DESC *psd = NULL;
+
+ *ppdesc = NULL;
+
+ DEBUG(10,("get_nt_acl: called for file %s\n", fsp->fsp_name ));
+
+ if(fsp->is_directory || fsp->fd == -1) {
+
+ /* Get the stat struct for the owner info. */
+ if(SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf) != 0) {
+ return 0;
+ }
+ /*
+ * Get the ACL from the path.
+ */
+
+ posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fsp->fsp_name, SMB_ACL_TYPE_ACCESS);
+
+ /*
+ * If it's a directory get the default POSIX ACL.
+ */
+
+ if(fsp->is_directory) {
+ dir_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fsp->fsp_name, SMB_ACL_TYPE_DEFAULT);
+ dir_acl = free_empty_sys_acl(conn, dir_acl);
+ }
+
+ } else {
+
+ /* Get the stat struct for the owner info. */
+ if(SMB_VFS_FSTAT(fsp,fsp->fd,&sbuf) != 0) {
+ return 0;
+ }
+ /*
+ * Get the ACL from the fd.
+ */
+ posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fsp->fd);
+ }
+
+ DEBUG(5,("get_nt_acl : file ACL %s, directory ACL %s\n",
+ posix_acl ? "present" : "absent",
+ dir_acl ? "present" : "absent" ));
+
+ pal = load_inherited_info(fsp);
+
+ /*
+ * Get the owner, group and world SIDs.
+ */
+
+ if (lp_profile_acls(SNUM(fsp->conn))) {
+ /* For WXP SP1 the owner must be administrators. */
+ sid_copy(&owner_sid, &global_sid_Builtin_Administrators);
+ sid_copy(&group_sid, &global_sid_Builtin_Users);
+ num_profile_acls = 2;
+ } else {
+ create_file_sids(&sbuf, &owner_sid, &group_sid);
+ }
+
+ if ((security_info & DACL_SECURITY_INFORMATION) && !(security_info & PROTECTED_DACL_SECURITY_INFORMATION)) {
+
+ /*
+ * In the optimum case Creator Owner and Creator Group would be used for
+ * the ACL_USER_OBJ and ACL_GROUP_OBJ entries, respectively, but this
+ * would lead to usability problems under Windows: The Creator entries
+ * are only available in browse lists of directories and not for files;
+ * additionally the identity of the owning group couldn't be determined.
+ * We therefore use those identities only for Default ACLs.
+ */
+
+ /* Create the canon_ace lists. */
+ file_ace = canonicalise_acl( fsp, posix_acl, &sbuf, &owner_sid, &group_sid, pal, SMB_ACL_TYPE_ACCESS );
+
+ /* We must have *some* ACLS. */
+
+ if (count_canon_ace_list(file_ace) == 0) {
+ DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", fsp->fsp_name ));
+ return 0;
+ }
+
+ if (fsp->is_directory && dir_acl) {
+ dir_ace = canonicalise_acl(fsp, dir_acl, &sbuf,
+ &global_sid_Creator_Owner,
+ &global_sid_Creator_Group, pal, SMB_ACL_TYPE_DEFAULT );
+ }
+
+ /*
+ * Create the NT ACE list from the canonical ace lists.
+ */
+
+ {
+ canon_ace *ace;
+ int nt_acl_type;
+ int i;
+
+ if (nt4_compatible_acls() && dir_ace) {
+ /*
+ * NT 4 chokes if an ACL contains an INHERIT_ONLY entry
+ * but no non-INHERIT_ONLY entry for one SID. So we only
+ * remove entries from the Access ACL if the
+ * corresponding Default ACL entries have also been
+ * removed. ACEs for CREATOR-OWNER and CREATOR-GROUP
+ * are exceptions. We can do nothing
+ * intelligent if the Default ACL contains entries that
+ * are not also contained in the Access ACL, so this
+ * case will still fail under NT 4.
+ */
+
+ ace = canon_ace_entry_for(dir_ace, SMB_ACL_OTHER, NULL);
+ if (ace && !ace->perms) {
+ DLIST_REMOVE(dir_ace, ace);
+ SAFE_FREE(ace);
+
+ ace = canon_ace_entry_for(file_ace, SMB_ACL_OTHER, NULL);
+ if (ace && !ace->perms) {
+ DLIST_REMOVE(file_ace, ace);
+ SAFE_FREE(ace);
+ }
+ }
+
+ /*
+ * WinNT doesn't usually have Creator Group
+ * in browse lists, so we send this entry to
+ * WinNT even if it contains no relevant
+ * permissions. Once we can add
+ * Creator Group to browse lists we can
+ * re-enable this.
+ */
+
+#if 0
+ ace = canon_ace_entry_for(dir_ace, SMB_ACL_GROUP_OBJ, NULL);
+ if (ace && !ace->perms) {
+ DLIST_REMOVE(dir_ace, ace);
+ SAFE_FREE(ace);
+ }
+#endif
+
+ ace = canon_ace_entry_for(file_ace, SMB_ACL_GROUP_OBJ, NULL);
+ if (ace && !ace->perms) {
+ DLIST_REMOVE(file_ace, ace);
+ SAFE_FREE(ace);
+ }
+ }
+
+ num_acls = count_canon_ace_list(file_ace);
+ num_dir_acls = count_canon_ace_list(dir_ace);
+
+ /* Allocate the ace list. */
+ if ((nt_ace_list = (SEC_ACE *)malloc((num_acls + num_profile_acls + num_dir_acls)* sizeof(SEC_ACE))) == NULL) {
+ DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
+ goto done;
+ }
+
+ memset(nt_ace_list, '\0', (num_acls + num_dir_acls) * sizeof(SEC_ACE) );
+
+ /*
+ * Create the NT ACE list from the canonical ace lists.
+ */
+
+ ace = file_ace;
+
+ for (i = 0; i < num_acls; i++, ace = ace->next) {
+ SEC_ACCESS acc;
+
+ acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
+ init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc, ace->inherited ? SEC_ACE_FLAG_INHERITED_ACE : 0);
+ }
+
+ /* The User must have access to a profile share - even if we can't map the SID. */
+ if (lp_profile_acls(SNUM(fsp->conn))) {
+ SEC_ACCESS acc;
+
+ init_sec_access(&acc,FILE_GENERIC_ALL);
+ init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED,
+ acc, 0);
+ }
+
+ ace = dir_ace;
+
+ for (i = 0; i < num_dir_acls; i++, ace = ace->next) {
+ SEC_ACCESS acc;
+
+ acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
+ init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc,
+ SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
+ SEC_ACE_FLAG_INHERIT_ONLY|
+ (ace->inherited ? SEC_ACE_FLAG_INHERITED_ACE : 0));
+ }
+
+ /* The User must have access to a profile share - even if we can't map the SID. */
+ if (lp_profile_acls(SNUM(fsp->conn))) {
+ SEC_ACCESS acc;
+
+ init_sec_access(&acc,FILE_GENERIC_ALL);
+ init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED, acc,
+ SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
+ SEC_ACE_FLAG_INHERIT_ONLY|0);
+ }
+
+ /*
+ * Merge POSIX default ACLs and normal ACLs into one NT ACE.
+ * Win2K needs this to get the inheritance correct when replacing ACLs
+ * on a directory tree. Based on work by Jim @ IBM.
+ */
+
+ num_aces = merge_default_aces(nt_ace_list, num_aces);
+
+ }
+
+ if (num_aces) {
+ if((psa = make_sec_acl( main_loop_talloc_get(), ACL_REVISION, num_aces, nt_ace_list)) == NULL) {
+ DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
+ goto done;
+ }
+ }
+ } /* security_info & DACL_SECURITY_INFORMATION */
+
+ psd = make_standard_sec_desc( main_loop_talloc_get(),
+ (security_info & OWNER_SECURITY_INFORMATION) ? &owner_sid : NULL,
+ (security_info & GROUP_SECURITY_INFORMATION) ? &group_sid : NULL,
+ psa,
+ &sd_size);
+
+ if(!psd) {
+ DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
+ sd_size = 0;
+ } else {
+ /*
+ * Windows 2000: The DACL_PROTECTED flag in the security
+ * descriptor marks the ACL as non-inheriting, i.e., no
+ * ACEs from higher level directories propagate to this
+ * ACL. In the POSIX ACL model permissions are only
+ * inherited at file create time, so ACLs never contain
+ * any ACEs that are inherited dynamically. The DACL_PROTECTED
+ * flag doesn't seem to bother Windows NT.
+ */
+ if (get_protected_flag(pal))
+ psd->type |= SE_DESC_DACL_PROTECTED;
+ }
+
+ if (psd->dacl)
+ dacl_sort_into_canonical_order(psd->dacl->ace, (unsigned int)psd->dacl->num_aces);
+
+ *ppdesc = psd;
+
+ done:
+
+ if (posix_acl)
+ SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
+ if (dir_acl)
+ SMB_VFS_SYS_ACL_FREE_ACL(conn, dir_acl);
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ free_inherited_info(pal);
+ SAFE_FREE(nt_ace_list);
+
+ return sd_size;
+}
+
+/****************************************************************************
+ Try to chown a file. We will be able to chown it under the following conditions.
+
+ 1) If we have root privileges, then it will just work.
+ 2) If we have write permission to the file and dos_filemodes is set
+ then allow chown to the currently authenticated user.
+****************************************************************************/
+
+static int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid)
+{
+ int ret;
+ extern struct current_user current_user;
+ files_struct *fsp;
+ SMB_STRUCT_STAT st;
+
+ /* try the direct way first */
+ ret = SMB_VFS_CHOWN(conn, fname, uid, gid);
+ if (ret == 0)
+ return 0;
+
+ if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
+ return -1;
+
+ if (SMB_VFS_STAT(conn,fname,&st))
+ return -1;
+
+ fsp = open_file_fchmod(conn,fname,&st);
+ if (!fsp)
+ return -1;
+
+ /* only allow chown to the current user. This is more secure,
+ and also copes with the case where the SID in a take ownership ACL is
+ a local SID on the users workstation
+ */
+ uid = current_user.uid;
+
+ become_root();
+ /* Keep the current file gid the same. */
+ ret = SMB_VFS_FCHOWN(fsp, fsp->fd, uid, (gid_t)-1);
+ unbecome_root();
+
+ close_file_fchmod(fsp);
+
+ return ret;
+}
+
+/****************************************************************************
+ Reply to set a security descriptor on an fsp. security_info_sent is the
+ description of the following NT ACL.
+ This should be the only external function needed for the UNIX style set ACL.
+****************************************************************************/
+
+BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
+{
+ connection_struct *conn = fsp->conn;
+ uid_t user = (uid_t)-1;
+ gid_t grp = (gid_t)-1;
+ SMB_STRUCT_STAT sbuf;
+ DOM_SID file_owner_sid;
+ DOM_SID file_grp_sid;
+ canon_ace *file_ace_list = NULL;
+ canon_ace *dir_ace_list = NULL;
+ BOOL acl_perms = False;
+ mode_t orig_mode = (mode_t)0;
+ uid_t orig_uid;
+ gid_t orig_gid;
+ BOOL need_chown = False;
+ extern struct current_user current_user;
+
+ DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name ));
+
+ if (!CAN_WRITE(conn)) {
+ DEBUG(10,("set acl rejected on read-only share\n"));
+ return False;
+ }
+
+ /*
+ * Get the current state of the file.
+ */
+
+ if(fsp->is_directory || fsp->fd == -1) {
+ if(SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf) != 0)
+ return False;
+ } else {
+ if(SMB_VFS_FSTAT(fsp,fsp->fd,&sbuf) != 0)
+ return False;
+ }
+
+ /* Save the original elements we check against. */
+ orig_mode = sbuf.st_mode;
+ orig_uid = sbuf.st_uid;
+ orig_gid = sbuf.st_gid;
+
+ /*
+ * Unpack the user/group/world id's.
+ */
+
+ if (!unpack_nt_owners( &sbuf, &user, &grp, security_info_sent, psd))
+ return False;
+
+ /*
+ * Do we need to chown ?
+ */
+
+ if (((user != (uid_t)-1) && (orig_uid != user)) || (( grp != (gid_t)-1) && (orig_gid != grp)))
+ need_chown = True;
+
+ /*
+ * Chown before setting ACL only if we don't change the user, or
+ * if we change to the current user, but not if we want to give away
+ * the file.
+ */
+
+ if (need_chown && (user == (uid_t)-1 || user == current_user.uid)) {
+
+ DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
+ fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
+
+ if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
+ DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
+ fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
+ return False;
+ }
+
+ /*
+ * Recheck the current state of the file, which may have changed.
+ * (suid/sgid bits, for instance)
+ */
+
+ if(fsp->is_directory) {
+ if(SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
+ return False;
+ }
+ } else {
+
+ int ret;
+
+ if(fsp->fd == -1)
+ ret = SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf);
+ else
+ ret = SMB_VFS_FSTAT(fsp,fsp->fd,&sbuf);
+
+ if(ret != 0)
+ return False;
+ }
+
+ /* Save the original elements we check against. */
+ orig_mode = sbuf.st_mode;
+ orig_uid = sbuf.st_uid;
+ orig_gid = sbuf.st_gid;
+
+ /* We did it, don't try again */
+ need_chown = False;
+ }
+
+ create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
+
+ acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid,
+ &file_ace_list, &dir_ace_list, security_info_sent, psd);
+
+ /* Ignore W2K traverse DACL set. */
+ if (file_ace_list || dir_ace_list) {
+
+ if (!acl_perms) {
+ DEBUG(3,("set_nt_acl: cannot set permissions\n"));
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+ return False;
+ }
+
+ /*
+ * Only change security if we got a DACL.
+ */
+
+ if((security_info_sent & DACL_SECURITY_INFORMATION) && (psd->dacl != NULL)) {
+
+ BOOL acl_set_support = False;
+ BOOL ret = False;
+
+ /*
+ * Try using the POSIX ACL set first. Fall back to chmod if
+ * we have no ACL support on this filesystem.
+ */
+
+ if (acl_perms && file_ace_list) {
+ ret = set_canon_ace_list(fsp, file_ace_list, False, &acl_set_support);
+ if (acl_set_support && ret == False) {
+ DEBUG(3,("set_nt_acl: failed to set file acl on file %s (%s).\n", fsp->fsp_name, strerror(errno) ));
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+ return False;
+ }
+ }
+
+ if (acl_perms && acl_set_support && fsp->is_directory) {
+ if (dir_ace_list) {
+ if (!set_canon_ace_list(fsp, dir_ace_list, True, &acl_set_support)) {
+ DEBUG(3,("set_nt_acl: failed to set default acl on directory %s (%s).\n", fsp->fsp_name, strerror(errno) ));
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+ return False;
+ }
+ } else {
+
+ /*
+ * No default ACL - delete one if it exists.
+ */
+
+ if (SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, fsp->fsp_name) == -1) {
+ DEBUG(3,("set_nt_acl: sys_acl_delete_def_file failed (%s)\n", strerror(errno)));
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+ return False;
+ }
+ }
+ }
+
+ if (acl_set_support)
+ store_inheritance_attributes(fsp, file_ace_list, dir_ace_list,
+ (psd->type & SE_DESC_DACL_PROTECTED) ? True : False);
+
+ /*
+ * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
+ */
+
+ if(!acl_set_support && acl_perms) {
+ mode_t posix_perms;
+
+ if (!convert_canon_ace_to_posix_perms( fsp, file_ace_list, &posix_perms)) {
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+ DEBUG(3,("set_nt_acl: failed to convert file acl to posix permissions for file %s.\n",
+ fsp->fsp_name ));
+ return False;
+ }
+
+ if (orig_mode != posix_perms) {
+
+ DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
+ fsp->fsp_name, (unsigned int)posix_perms ));
+
+ if(SMB_VFS_CHMOD(conn,fsp->fsp_name, posix_perms) == -1) {
+ DEBUG(3,("set_nt_acl: chmod %s, 0%o failed. Error = %s.\n",
+ fsp->fsp_name, (unsigned int)posix_perms, strerror(errno) ));
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+ return False;
+ }
+ }
+ }
+ }
+
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+ }
+
+ /* Any chown pending? */
+ if (need_chown) {
+
+ DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
+ fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
+
+ if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
+ DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
+ fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Get the actual group bits stored on a file with an ACL. Has no effect if
+ the file has no ACL. Needed in dosmode code where the stat() will return
+ the mask bits, not the real group bits, for a file with an ACL.
+****************************************************************************/
+
+int get_acl_group_bits( connection_struct *conn, const char *fname, mode_t *mode )
+{
+ int entry_id = SMB_ACL_FIRST_ENTRY;
+ SMB_ACL_ENTRY_T entry;
+ SMB_ACL_T posix_acl;
+
+ posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS);
+ if (posix_acl == (SMB_ACL_T)NULL)
+ return -1;
+
+ while (SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) {
+ SMB_ACL_TAG_T tagtype;
+ SMB_ACL_PERMSET_T permset;
+
+ /* get_next... */
+ if (entry_id == SMB_ACL_FIRST_ENTRY)
+ entry_id = SMB_ACL_NEXT_ENTRY;
+
+ if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) ==-1)
+ return -1;
+
+ if (tagtype == SMB_ACL_GROUP_OBJ) {
+ if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) {
+ return -1;
+ } else {
+ *mode &= ~(S_IRGRP|S_IWGRP|S_IXGRP);
+ *mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ) ? S_IRGRP : 0);
+ *mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE) ? S_IWGRP : 0);
+ *mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_EXECUTE) ? S_IXGRP : 0);
+ return 0;;
+ }
+ }
+ }
+ return -1;
+}
+
+/****************************************************************************
+ Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
+ and set the mask to rwx. Needed to preserve complex ACLs set by NT.
+****************************************************************************/
+
+static int chmod_acl_internals( connection_struct *conn, SMB_ACL_T posix_acl, mode_t mode)
+{
+ int entry_id = SMB_ACL_FIRST_ENTRY;
+ SMB_ACL_ENTRY_T entry;
+ int num_entries = 0;
+
+ while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) {
+ SMB_ACL_TAG_T tagtype;
+ SMB_ACL_PERMSET_T permset;
+ mode_t perms;
+
+ /* get_next... */
+ if (entry_id == SMB_ACL_FIRST_ENTRY)
+ entry_id = SMB_ACL_NEXT_ENTRY;
+
+ if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1)
+ return -1;
+
+ if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1)
+ return -1;
+
+ num_entries++;
+
+ switch(tagtype) {
+ case SMB_ACL_USER_OBJ:
+ perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
+ break;
+ case SMB_ACL_GROUP_OBJ:
+ perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
+ break;
+ case SMB_ACL_MASK:
+ /*
+ * FIXME: The ACL_MASK entry permissions should really be set to
+ * the union of the permissions of all ACL_USER,
+ * ACL_GROUP_OBJ, and ACL_GROUP entries. That's what
+ * acl_calc_mask() does, but Samba ACLs doesn't provide it.
+ */
+ perms = S_IRUSR|S_IWUSR|S_IXUSR;
+ break;
+ case SMB_ACL_OTHER:
+ perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
+ break;
+ default:
+ continue;
+ }
+
+ if (map_acl_perms_to_permset(conn, perms, &permset) == -1)
+ return -1;
+
+ if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, entry, permset) == -1)
+ return -1;
+ }
+
+ /*
+ * If this is a simple 3 element ACL or no elements then it's a standard
+ * UNIX permission set. Just use chmod...
+ */
+
+ if ((num_entries == 3) || (num_entries == 0))
+ return -1;
+
+ return 0;
+}
+
+/****************************************************************************
+ Get the access ACL of FROM, do a chmod by setting the ACL USER_OBJ,
+ GROUP_OBJ and OTHER bits in an ACL and set the mask to rwx. Set the
+ resulting ACL on TO. Note that name is in UNIX character set.
+****************************************************************************/
+
+static int copy_access_acl(connection_struct *conn, const char *from, const char *to, mode_t mode)
+{
+ SMB_ACL_T posix_acl = NULL;
+ int ret = -1;
+
+ if ((posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, from, SMB_ACL_TYPE_ACCESS)) == NULL)
+ return -1;
+
+ if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
+ goto done;
+
+ ret = SMB_VFS_SYS_ACL_SET_FILE(conn, to, SMB_ACL_TYPE_ACCESS, posix_acl);
+
+ done:
+
+ SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
+ return ret;
+}
+
+/****************************************************************************
+ Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
+ and set the mask to rwx. Needed to preserve complex ACLs set by NT.
+ Note that name is in UNIX character set.
+****************************************************************************/
+
+int chmod_acl(connection_struct *conn, const char *name, mode_t mode)
+{
+ return copy_access_acl(conn, name, name, mode);
+}
+
+/****************************************************************************
+ If "inherit permissions" is set and the parent directory has no default
+ ACL but it does have an Access ACL, inherit this Access ACL to file name.
+****************************************************************************/
+
+int inherit_access_acl(connection_struct *conn, const char *name, mode_t mode)
+{
+ pstring dirname;
+ pstrcpy(dirname, parent_dirname(name));
+
+ if (!lp_inherit_perms(SNUM(conn)) || directory_has_default_acl(conn, dirname))
+ return 0;
+
+ return copy_access_acl(conn, dirname, name, mode);
+}
+
+/****************************************************************************
+ Do an fchmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
+ and set the mask to rwx. Needed to preserve complex ACLs set by NT.
+****************************************************************************/
+
+int fchmod_acl(files_struct *fsp, int fd, mode_t mode)
+{
+ connection_struct *conn = fsp->conn;
+ SMB_ACL_T posix_acl = NULL;
+ int ret = -1;
+
+ if ((posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fd)) == NULL)
+ return -1;
+
+ if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
+ goto done;
+
+ ret = SMB_VFS_SYS_ACL_SET_FD(fsp, fd, posix_acl);
+
+ done:
+
+ SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
+ return ret;
+}
+
+/****************************************************************************
+ Check for an existing default POSIX ACL on a directory.
+****************************************************************************/
+
+BOOL directory_has_default_acl(connection_struct *conn, const char *fname)
+{
+ SMB_ACL_T dir_acl = SMB_VFS_SYS_ACL_GET_FILE( conn, fname, SMB_ACL_TYPE_DEFAULT);
+ BOOL has_acl = False;
+ SMB_ACL_ENTRY_T entry;
+
+ if (dir_acl != NULL && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, dir_acl, SMB_ACL_FIRST_ENTRY, &entry) == 1))
+ has_acl = True;
+
+ if (dir_acl)
+ SMB_VFS_SYS_ACL_FREE_ACL(conn, dir_acl);
+ return has_acl;
+}
diff --git a/source/smbd/process.c b/source/smbd/process.c
new file mode 100644
index 00000000000..718d1bb67b2
--- /dev/null
+++ b/source/smbd/process.c
@@ -0,0 +1,1367 @@
+/*
+ Unix SMB/CIFS implementation.
+ process incoming packets - main loop
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+struct timeval smb_last_time;
+
+static char *InBuffer = NULL;
+char *OutBuffer = NULL;
+char *last_inbuf = NULL;
+
+/*
+ * Size of data we can send to client. Set
+ * by the client for all protocols above CORE.
+ * Set by us for CORE protocol.
+ */
+int max_send = BUFFER_SIZE;
+/*
+ * Size of the data we can receive. Set by us.
+ * Can be modified by the max xmit parameter.
+ */
+int max_recv = BUFFER_SIZE;
+
+extern int last_message;
+extern int global_oplock_break;
+extern userdom_struct current_user_info;
+extern int smb_read_error;
+SIG_ATOMIC_T reload_after_sighup = 0;
+SIG_ATOMIC_T got_sig_term = 0;
+BOOL global_machine_password_needs_changing = False;
+extern int max_send;
+
+/****************************************************************************
+ Function to return the current request mid from Inbuffer.
+****************************************************************************/
+
+uint16 get_current_mid(void)
+{
+ return SVAL(InBuffer,smb_mid);
+}
+
+/****************************************************************************
+ structure to hold a linked list of queued messages.
+ for processing.
+****************************************************************************/
+
+typedef struct {
+ ubi_slNode msg_next;
+ char *msg_buf;
+ int msg_len;
+} pending_message_list;
+
+static ubi_slList smb_oplock_queue = { NULL, (ubi_slNodePtr)&smb_oplock_queue, 0};
+
+/****************************************************************************
+ Function to push a message onto the tail of a linked list of smb messages ready
+ for processing.
+****************************************************************************/
+
+static BOOL push_message(ubi_slList *list_head, char *buf, int msg_len)
+{
+ pending_message_list *msg = (pending_message_list *)
+ malloc(sizeof(pending_message_list));
+
+ if(msg == NULL) {
+ DEBUG(0,("push_message: malloc fail (1)\n"));
+ return False;
+ }
+
+ msg->msg_buf = (char *)malloc(msg_len);
+ if(msg->msg_buf == NULL) {
+ DEBUG(0,("push_message: malloc fail (2)\n"));
+ SAFE_FREE(msg);
+ return False;
+ }
+
+ memcpy(msg->msg_buf, buf, msg_len);
+ msg->msg_len = msg_len;
+
+ ubi_slAddTail( list_head, msg);
+
+ /* Push the MID of this packet on the signing queue. */
+ srv_defer_sign_response(SVAL(buf,smb_mid));
+
+ return True;
+}
+
+/****************************************************************************
+ Function to push a smb message onto a linked list of local smb messages ready
+ for processing.
+****************************************************************************/
+
+BOOL push_oplock_pending_smb_message(char *buf, int msg_len)
+{
+ return push_message(&smb_oplock_queue, buf, msg_len);
+}
+
+/****************************************************************************
+ Do all async processing in here. This includes UDB oplock messages, kernel
+ oplock messages, change notify events etc.
+****************************************************************************/
+
+static void async_processing(char *buffer, int buffer_len)
+{
+ DEBUG(10,("async_processing: Doing async processing.\n"));
+
+ /* check for oplock messages (both UDP and kernel) */
+ if (receive_local_message(buffer, buffer_len, 1)) {
+ process_local_message(buffer, buffer_len);
+ }
+
+ if (got_sig_term) {
+ exit_server("Caught TERM signal");
+ }
+
+ /* check for async change notify events */
+ process_pending_change_notify_queue(0);
+
+ /* check for sighup processing */
+ if (reload_after_sighup) {
+ change_to_root_user();
+ DEBUG(1,("Reloading services after SIGHUP\n"));
+ reload_services(False);
+ reload_after_sighup = 0;
+ }
+}
+
+/****************************************************************************
+ Do a select on an two fd's - with timeout.
+
+ If a local udp message has been pushed onto the
+ queue (this can only happen during oplock break
+ processing) call async_processing()
+
+ If a pending smb message has been pushed onto the
+ queue (this can only happen during oplock break
+ processing) return this next.
+
+ If the first smbfd is ready then read an smb from it.
+ if the second (loopback UDP) fd is ready then read a message
+ from it and setup the buffer header to identify the length
+ and from address.
+ Returns False on timeout or error.
+ Else returns True.
+
+The timeout is in milliseconds
+****************************************************************************/
+
+static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout)
+{
+ fd_set fds;
+ int selrtn;
+ struct timeval to;
+ int maxfd;
+
+ smb_read_error = 0;
+
+ again:
+
+ /*
+ * Note that this call must be before processing any SMB
+ * messages as we need to synchronously process any messages
+ * we may have sent to ourselves from the previous SMB.
+ */
+ message_dispatch();
+
+ /*
+ * Check to see if we already have a message on the smb queue.
+ * If so - copy and return it.
+ */
+ if(ubi_slCount(&smb_oplock_queue) != 0) {
+ pending_message_list *msg = (pending_message_list *)ubi_slRemHead(&smb_oplock_queue);
+ memcpy(buffer, msg->msg_buf, MIN(buffer_len, msg->msg_len));
+
+ /* Free the message we just copied. */
+ SAFE_FREE(msg->msg_buf);
+ SAFE_FREE(msg);
+
+ DEBUG(5,("receive_message_or_smb: returning queued smb message.\n"));
+ return True;
+ }
+
+
+ /*
+ * Setup the select read fd set.
+ */
+
+ FD_ZERO(&fds);
+
+ /*
+ * Ensure we process oplock break messages by preference.
+ * We have to do this before the select, after the select
+ * and if the select returns EINTR. This is due to the fact
+ * that the selects called from async_processing can eat an EINTR
+ * caused by a signal (we can't take the break message there).
+ * This is hideously complex - *MUST* be simplified for 3.0 ! JRA.
+ */
+
+ if (oplock_message_waiting(&fds)) {
+ DEBUG(10,("receive_message_or_smb: oplock_message is waiting.\n"));
+ async_processing(buffer, buffer_len);
+ /*
+ * After async processing we must go and do the select again, as
+ * the state of the flag in fds for the server file descriptor is
+ * indeterminate - we may have done I/O on it in the oplock processing. JRA.
+ */
+ goto again;
+ }
+
+ FD_SET(smbd_server_fd(),&fds);
+ maxfd = setup_oplock_select_set(&fds);
+
+ to.tv_sec = timeout / 1000;
+ to.tv_usec = (timeout % 1000) * 1000;
+
+ selrtn = sys_select(MAX(maxfd,smbd_server_fd())+1,&fds,NULL,NULL,timeout>0?&to:NULL);
+
+ /* if we get EINTR then maybe we have received an oplock
+ signal - treat this as select returning 1. This is ugly, but
+ is the best we can do until the oplock code knows more about
+ signals */
+ if (selrtn == -1 && errno == EINTR) {
+ async_processing(buffer, buffer_len);
+ /*
+ * After async processing we must go and do the select again, as
+ * the state of the flag in fds for the server file descriptor is
+ * indeterminate - we may have done I/O on it in the oplock processing. JRA.
+ */
+ goto again;
+ }
+
+ /* Check if error */
+ if (selrtn == -1) {
+ /* something is wrong. Maybe the socket is dead? */
+ smb_read_error = READ_ERROR;
+ return False;
+ }
+
+ /* Did we timeout ? */
+ if (selrtn == 0) {
+ smb_read_error = READ_TIMEOUT;
+ return False;
+ }
+
+ /*
+ * Ensure we process oplock break messages by preference.
+ * This is IMPORTANT ! Otherwise we can starve other processes
+ * sending us an oplock break message. JRA.
+ */
+
+ if (oplock_message_waiting(&fds)) {
+ async_processing(buffer, buffer_len);
+ /*
+ * After async processing we must go and do the select again, as
+ * the state of the flag in fds for the server file descriptor is
+ * indeterminate - we may have done I/O on it in the oplock processing. JRA.
+ */
+ goto again;
+ }
+
+ return receive_smb(smbd_server_fd(), buffer, 0);
+}
+
+/****************************************************************************
+Get the next SMB packet, doing the local message processing automatically.
+****************************************************************************/
+
+BOOL receive_next_smb(char *inbuf, int bufsize, int timeout)
+{
+ BOOL got_keepalive;
+ BOOL ret;
+
+ do {
+ ret = receive_message_or_smb(inbuf,bufsize,timeout);
+
+ got_keepalive = (ret && (CVAL(inbuf,0) == SMBkeepalive));
+ } while (ret && got_keepalive);
+
+ return ret;
+}
+
+/****************************************************************************
+ We're terminating and have closed all our files/connections etc.
+ If there are any pending local messages we need to respond to them
+ before termination so that other smbds don't think we just died whilst
+ holding oplocks.
+****************************************************************************/
+
+void respond_to_all_remaining_local_messages(void)
+{
+ char buffer[1024];
+
+ /*
+ * Assert we have no exclusive open oplocks.
+ */
+
+ if(get_number_of_exclusive_open_oplocks()) {
+ DEBUG(0,("respond_to_all_remaining_local_messages: PANIC : we have %d exclusive oplocks.\n",
+ get_number_of_exclusive_open_oplocks() ));
+ return;
+ }
+
+ /*
+ * Keep doing receive_local_message with a 1 ms timeout until
+ * we have no more messages.
+ */
+
+ while(receive_local_message(buffer, sizeof(buffer), 1)) {
+ /* Deal with oplock break requests from other smbd's. */
+ process_local_message(buffer, sizeof(buffer));
+ }
+
+ return;
+}
+
+
+/*
+These flags determine some of the permissions required to do an operation
+
+Note that I don't set NEED_WRITE on some write operations because they
+are used by some brain-dead clients when printing, and I don't want to
+force write permissions on print services.
+*/
+#define AS_USER (1<<0)
+#define NEED_WRITE (1<<1)
+#define TIME_INIT (1<<2)
+#define CAN_IPC (1<<3)
+#define AS_GUEST (1<<5)
+#define QUEUE_IN_OPLOCK (1<<6)
+
+/*
+ define a list of possible SMB messages and their corresponding
+ functions. Any message that has a NULL function is unimplemented -
+ please feel free to contribute implementations!
+*/
+static const struct smb_message_struct {
+ const char *name;
+ int (*fn)(connection_struct *conn, char *, char *, int, int);
+ int flags;
+} smb_messages[256] = {
+
+/* 0x00 */ { "SMBmkdir",reply_mkdir,AS_USER | NEED_WRITE},
+/* 0x01 */ { "SMBrmdir",reply_rmdir,AS_USER | NEED_WRITE},
+/* 0x02 */ { "SMBopen",reply_open,AS_USER | QUEUE_IN_OPLOCK },
+/* 0x03 */ { "SMBcreate",reply_mknew,AS_USER},
+/* 0x04 */ { "SMBclose",reply_close,AS_USER | CAN_IPC },
+/* 0x05 */ { "SMBflush",reply_flush,AS_USER},
+/* 0x06 */ { "SMBunlink",reply_unlink,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK},
+/* 0x07 */ { "SMBmv",reply_mv,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK},
+/* 0x08 */ { "SMBgetatr",reply_getatr,AS_USER},
+/* 0x09 */ { "SMBsetatr",reply_setatr,AS_USER | NEED_WRITE},
+/* 0x0a */ { "SMBread",reply_read,AS_USER},
+/* 0x0b */ { "SMBwrite",reply_write,AS_USER | CAN_IPC },
+/* 0x0c */ { "SMBlock",reply_lock,AS_USER},
+/* 0x0d */ { "SMBunlock",reply_unlock,AS_USER},
+/* 0x0e */ { "SMBctemp",reply_ctemp,AS_USER | QUEUE_IN_OPLOCK },
+/* 0x0f */ { "SMBmknew",reply_mknew,AS_USER},
+/* 0x10 */ { "SMBchkpth",reply_chkpth,AS_USER},
+/* 0x11 */ { "SMBexit",reply_exit,0},
+/* 0x12 */ { "SMBlseek",reply_lseek,AS_USER},
+/* 0x13 */ { "SMBlockread",reply_lockread,AS_USER},
+/* 0x14 */ { "SMBwriteunlock",reply_writeunlock,AS_USER},
+/* 0x15 */ { NULL, NULL, 0 },
+/* 0x16 */ { NULL, NULL, 0 },
+/* 0x17 */ { NULL, NULL, 0 },
+/* 0x18 */ { NULL, NULL, 0 },
+/* 0x19 */ { NULL, NULL, 0 },
+/* 0x1a */ { "SMBreadbraw",reply_readbraw,AS_USER},
+/* 0x1b */ { "SMBreadBmpx",reply_readbmpx,AS_USER},
+/* 0x1c */ { "SMBreadBs",NULL,0 },
+/* 0x1d */ { "SMBwritebraw",reply_writebraw,AS_USER},
+/* 0x1e */ { "SMBwriteBmpx",reply_writebmpx,AS_USER},
+/* 0x1f */ { "SMBwriteBs",reply_writebs,AS_USER},
+/* 0x20 */ { "SMBwritec",NULL,0},
+/* 0x21 */ { NULL, NULL, 0 },
+/* 0x22 */ { "SMBsetattrE",reply_setattrE,AS_USER | NEED_WRITE },
+/* 0x23 */ { "SMBgetattrE",reply_getattrE,AS_USER },
+/* 0x24 */ { "SMBlockingX",reply_lockingX,AS_USER },
+/* 0x25 */ { "SMBtrans",reply_trans,AS_USER | CAN_IPC },
+/* 0x26 */ { "SMBtranss",NULL,AS_USER | CAN_IPC},
+/* 0x27 */ { "SMBioctl",reply_ioctl,0},
+/* 0x28 */ { "SMBioctls",NULL,AS_USER},
+/* 0x29 */ { "SMBcopy",reply_copy,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK },
+/* 0x2a */ { "SMBmove",NULL,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK },
+/* 0x2b */ { "SMBecho",reply_echo,0},
+/* 0x2c */ { "SMBwriteclose",reply_writeclose,AS_USER},
+/* 0x2d */ { "SMBopenX",reply_open_and_X,AS_USER | CAN_IPC | QUEUE_IN_OPLOCK },
+/* 0x2e */ { "SMBreadX",reply_read_and_X,AS_USER | CAN_IPC },
+/* 0x2f */ { "SMBwriteX",reply_write_and_X,AS_USER | CAN_IPC },
+/* 0x30 */ { NULL, NULL, 0 },
+/* 0x31 */ { NULL, NULL, 0 },
+/* 0x32 */ { "SMBtrans2", reply_trans2, AS_USER | CAN_IPC },
+/* 0x33 */ { "SMBtranss2", reply_transs2, AS_USER},
+/* 0x34 */ { "SMBfindclose", reply_findclose,AS_USER},
+/* 0x35 */ { "SMBfindnclose", reply_findnclose, AS_USER},
+/* 0x36 */ { NULL, NULL, 0 },
+/* 0x37 */ { NULL, NULL, 0 },
+/* 0x38 */ { NULL, NULL, 0 },
+/* 0x39 */ { NULL, NULL, 0 },
+/* 0x3a */ { NULL, NULL, 0 },
+/* 0x3b */ { NULL, NULL, 0 },
+/* 0x3c */ { NULL, NULL, 0 },
+/* 0x3d */ { NULL, NULL, 0 },
+/* 0x3e */ { NULL, NULL, 0 },
+/* 0x3f */ { NULL, NULL, 0 },
+/* 0x40 */ { NULL, NULL, 0 },
+/* 0x41 */ { NULL, NULL, 0 },
+/* 0x42 */ { NULL, NULL, 0 },
+/* 0x43 */ { NULL, NULL, 0 },
+/* 0x44 */ { NULL, NULL, 0 },
+/* 0x45 */ { NULL, NULL, 0 },
+/* 0x46 */ { NULL, NULL, 0 },
+/* 0x47 */ { NULL, NULL, 0 },
+/* 0x48 */ { NULL, NULL, 0 },
+/* 0x49 */ { NULL, NULL, 0 },
+/* 0x4a */ { NULL, NULL, 0 },
+/* 0x4b */ { NULL, NULL, 0 },
+/* 0x4c */ { NULL, NULL, 0 },
+/* 0x4d */ { NULL, NULL, 0 },
+/* 0x4e */ { NULL, NULL, 0 },
+/* 0x4f */ { NULL, NULL, 0 },
+/* 0x50 */ { NULL, NULL, 0 },
+/* 0x51 */ { NULL, NULL, 0 },
+/* 0x52 */ { NULL, NULL, 0 },
+/* 0x53 */ { NULL, NULL, 0 },
+/* 0x54 */ { NULL, NULL, 0 },
+/* 0x55 */ { NULL, NULL, 0 },
+/* 0x56 */ { NULL, NULL, 0 },
+/* 0x57 */ { NULL, NULL, 0 },
+/* 0x58 */ { NULL, NULL, 0 },
+/* 0x59 */ { NULL, NULL, 0 },
+/* 0x5a */ { NULL, NULL, 0 },
+/* 0x5b */ { NULL, NULL, 0 },
+/* 0x5c */ { NULL, NULL, 0 },
+/* 0x5d */ { NULL, NULL, 0 },
+/* 0x5e */ { NULL, NULL, 0 },
+/* 0x5f */ { NULL, NULL, 0 },
+/* 0x60 */ { NULL, NULL, 0 },
+/* 0x61 */ { NULL, NULL, 0 },
+/* 0x62 */ { NULL, NULL, 0 },
+/* 0x63 */ { NULL, NULL, 0 },
+/* 0x64 */ { NULL, NULL, 0 },
+/* 0x65 */ { NULL, NULL, 0 },
+/* 0x66 */ { NULL, NULL, 0 },
+/* 0x67 */ { NULL, NULL, 0 },
+/* 0x68 */ { NULL, NULL, 0 },
+/* 0x69 */ { NULL, NULL, 0 },
+/* 0x6a */ { NULL, NULL, 0 },
+/* 0x6b */ { NULL, NULL, 0 },
+/* 0x6c */ { NULL, NULL, 0 },
+/* 0x6d */ { NULL, NULL, 0 },
+/* 0x6e */ { NULL, NULL, 0 },
+/* 0x6f */ { NULL, NULL, 0 },
+/* 0x70 */ { "SMBtcon",reply_tcon,0},
+/* 0x71 */ { "SMBtdis",reply_tdis,0},
+/* 0x72 */ { "SMBnegprot",reply_negprot,0},
+/* 0x73 */ { "SMBsesssetupX",reply_sesssetup_and_X,0},
+/* 0x74 */ { "SMBulogoffX", reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */
+/* 0x75 */ { "SMBtconX",reply_tcon_and_X,0},
+/* 0x76 */ { NULL, NULL, 0 },
+/* 0x77 */ { NULL, NULL, 0 },
+/* 0x78 */ { NULL, NULL, 0 },
+/* 0x79 */ { NULL, NULL, 0 },
+/* 0x7a */ { NULL, NULL, 0 },
+/* 0x7b */ { NULL, NULL, 0 },
+/* 0x7c */ { NULL, NULL, 0 },
+/* 0x7d */ { NULL, NULL, 0 },
+/* 0x7e */ { NULL, NULL, 0 },
+/* 0x7f */ { NULL, NULL, 0 },
+/* 0x80 */ { "SMBdskattr",reply_dskattr,AS_USER},
+/* 0x81 */ { "SMBsearch",reply_search,AS_USER},
+/* 0x82 */ { "SMBffirst",reply_search,AS_USER},
+/* 0x83 */ { "SMBfunique",reply_search,AS_USER},
+/* 0x84 */ { "SMBfclose",reply_fclose,AS_USER},
+/* 0x85 */ { NULL, NULL, 0 },
+/* 0x86 */ { NULL, NULL, 0 },
+/* 0x87 */ { NULL, NULL, 0 },
+/* 0x88 */ { NULL, NULL, 0 },
+/* 0x89 */ { NULL, NULL, 0 },
+/* 0x8a */ { NULL, NULL, 0 },
+/* 0x8b */ { NULL, NULL, 0 },
+/* 0x8c */ { NULL, NULL, 0 },
+/* 0x8d */ { NULL, NULL, 0 },
+/* 0x8e */ { NULL, NULL, 0 },
+/* 0x8f */ { NULL, NULL, 0 },
+/* 0x90 */ { NULL, NULL, 0 },
+/* 0x91 */ { NULL, NULL, 0 },
+/* 0x92 */ { NULL, NULL, 0 },
+/* 0x93 */ { NULL, NULL, 0 },
+/* 0x94 */ { NULL, NULL, 0 },
+/* 0x95 */ { NULL, NULL, 0 },
+/* 0x96 */ { NULL, NULL, 0 },
+/* 0x97 */ { NULL, NULL, 0 },
+/* 0x98 */ { NULL, NULL, 0 },
+/* 0x99 */ { NULL, NULL, 0 },
+/* 0x9a */ { NULL, NULL, 0 },
+/* 0x9b */ { NULL, NULL, 0 },
+/* 0x9c */ { NULL, NULL, 0 },
+/* 0x9d */ { NULL, NULL, 0 },
+/* 0x9e */ { NULL, NULL, 0 },
+/* 0x9f */ { NULL, NULL, 0 },
+/* 0xa0 */ { "SMBnttrans", reply_nttrans, AS_USER | CAN_IPC | QUEUE_IN_OPLOCK},
+/* 0xa1 */ { "SMBnttranss", reply_nttranss, AS_USER | CAN_IPC },
+/* 0xa2 */ { "SMBntcreateX", reply_ntcreate_and_X, AS_USER | CAN_IPC | QUEUE_IN_OPLOCK },
+/* 0xa3 */ { NULL, NULL, 0 },
+/* 0xa4 */ { "SMBntcancel", reply_ntcancel, 0 },
+/* 0xa5 */ { "SMBntrename", reply_ntrename, AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK },
+/* 0xa6 */ { NULL, NULL, 0 },
+/* 0xa7 */ { NULL, NULL, 0 },
+/* 0xa8 */ { NULL, NULL, 0 },
+/* 0xa9 */ { NULL, NULL, 0 },
+/* 0xaa */ { NULL, NULL, 0 },
+/* 0xab */ { NULL, NULL, 0 },
+/* 0xac */ { NULL, NULL, 0 },
+/* 0xad */ { NULL, NULL, 0 },
+/* 0xae */ { NULL, NULL, 0 },
+/* 0xaf */ { NULL, NULL, 0 },
+/* 0xb0 */ { NULL, NULL, 0 },
+/* 0xb1 */ { NULL, NULL, 0 },
+/* 0xb2 */ { NULL, NULL, 0 },
+/* 0xb3 */ { NULL, NULL, 0 },
+/* 0xb4 */ { NULL, NULL, 0 },
+/* 0xb5 */ { NULL, NULL, 0 },
+/* 0xb6 */ { NULL, NULL, 0 },
+/* 0xb7 */ { NULL, NULL, 0 },
+/* 0xb8 */ { NULL, NULL, 0 },
+/* 0xb9 */ { NULL, NULL, 0 },
+/* 0xba */ { NULL, NULL, 0 },
+/* 0xbb */ { NULL, NULL, 0 },
+/* 0xbc */ { NULL, NULL, 0 },
+/* 0xbd */ { NULL, NULL, 0 },
+/* 0xbe */ { NULL, NULL, 0 },
+/* 0xbf */ { NULL, NULL, 0 },
+/* 0xc0 */ { "SMBsplopen",reply_printopen,AS_USER | QUEUE_IN_OPLOCK },
+/* 0xc1 */ { "SMBsplwr",reply_printwrite,AS_USER},
+/* 0xc2 */ { "SMBsplclose",reply_printclose,AS_USER},
+/* 0xc3 */ { "SMBsplretq",reply_printqueue,AS_USER},
+/* 0xc4 */ { NULL, NULL, 0 },
+/* 0xc5 */ { NULL, NULL, 0 },
+/* 0xc6 */ { NULL, NULL, 0 },
+/* 0xc7 */ { NULL, NULL, 0 },
+/* 0xc8 */ { NULL, NULL, 0 },
+/* 0xc9 */ { NULL, NULL, 0 },
+/* 0xca */ { NULL, NULL, 0 },
+/* 0xcb */ { NULL, NULL, 0 },
+/* 0xcc */ { NULL, NULL, 0 },
+/* 0xcd */ { NULL, NULL, 0 },
+/* 0xce */ { NULL, NULL, 0 },
+/* 0xcf */ { NULL, NULL, 0 },
+/* 0xd0 */ { "SMBsends",reply_sends,AS_GUEST},
+/* 0xd1 */ { "SMBsendb",NULL,AS_GUEST},
+/* 0xd2 */ { "SMBfwdname",NULL,AS_GUEST},
+/* 0xd3 */ { "SMBcancelf",NULL,AS_GUEST},
+/* 0xd4 */ { "SMBgetmac",NULL,AS_GUEST},
+/* 0xd5 */ { "SMBsendstrt",reply_sendstrt,AS_GUEST},
+/* 0xd6 */ { "SMBsendend",reply_sendend,AS_GUEST},
+/* 0xd7 */ { "SMBsendtxt",reply_sendtxt,AS_GUEST},
+/* 0xd8 */ { NULL, NULL, 0 },
+/* 0xd9 */ { NULL, NULL, 0 },
+/* 0xda */ { NULL, NULL, 0 },
+/* 0xdb */ { NULL, NULL, 0 },
+/* 0xdc */ { NULL, NULL, 0 },
+/* 0xdd */ { NULL, NULL, 0 },
+/* 0xde */ { NULL, NULL, 0 },
+/* 0xdf */ { NULL, NULL, 0 },
+/* 0xe0 */ { NULL, NULL, 0 },
+/* 0xe1 */ { NULL, NULL, 0 },
+/* 0xe2 */ { NULL, NULL, 0 },
+/* 0xe3 */ { NULL, NULL, 0 },
+/* 0xe4 */ { NULL, NULL, 0 },
+/* 0xe5 */ { NULL, NULL, 0 },
+/* 0xe6 */ { NULL, NULL, 0 },
+/* 0xe7 */ { NULL, NULL, 0 },
+/* 0xe8 */ { NULL, NULL, 0 },
+/* 0xe9 */ { NULL, NULL, 0 },
+/* 0xea */ { NULL, NULL, 0 },
+/* 0xeb */ { NULL, NULL, 0 },
+/* 0xec */ { NULL, NULL, 0 },
+/* 0xed */ { NULL, NULL, 0 },
+/* 0xee */ { NULL, NULL, 0 },
+/* 0xef */ { NULL, NULL, 0 },
+/* 0xf0 */ { NULL, NULL, 0 },
+/* 0xf1 */ { NULL, NULL, 0 },
+/* 0xf2 */ { NULL, NULL, 0 },
+/* 0xf3 */ { NULL, NULL, 0 },
+/* 0xf4 */ { NULL, NULL, 0 },
+/* 0xf5 */ { NULL, NULL, 0 },
+/* 0xf6 */ { NULL, NULL, 0 },
+/* 0xf7 */ { NULL, NULL, 0 },
+/* 0xf8 */ { NULL, NULL, 0 },
+/* 0xf9 */ { NULL, NULL, 0 },
+/* 0xfa */ { NULL, NULL, 0 },
+/* 0xfb */ { NULL, NULL, 0 },
+/* 0xfc */ { NULL, NULL, 0 },
+/* 0xfd */ { NULL, NULL, 0 },
+/* 0xfe */ { NULL, NULL, 0 },
+/* 0xff */ { NULL, NULL, 0 }
+
+};
+
+/*******************************************************************
+ Dump a packet to a file.
+********************************************************************/
+
+static void smb_dump(const char *name, int type, char *data, ssize_t len)
+{
+ int fd, i;
+ pstring fname;
+ if (DEBUGLEVEL < 50) return;
+
+ if (len < 4) len = smb_len(data)+4;
+ for (i=1;i<100;i++) {
+ slprintf(fname,sizeof(fname)-1, "/tmp/%s.%d.%s", name, i,
+ type ? "req" : "resp");
+ fd = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0644);
+ if (fd != -1 || errno != EEXIST) break;
+ }
+ if (fd != -1) {
+ ssize_t ret = write(fd, data, len);
+ if (ret != len)
+ DEBUG(0,("smb_dump: problem: write returned %d\n", (int)ret ));
+ close(fd);
+ DEBUG(0,("created %s len %lu\n", fname, (unsigned long)len));
+ }
+}
+
+
+/****************************************************************************
+ Do a switch on the message type, and return the response size
+****************************************************************************/
+
+static int switch_message(int type,char *inbuf,char *outbuf,int size,int bufsize)
+{
+ static pid_t pid= (pid_t)-1;
+ int outsize = 0;
+ extern uint16 global_smbpid;
+
+ type &= 0xff;
+
+ if (pid == (pid_t)-1)
+ pid = sys_getpid();
+
+ errno = 0;
+ last_message = type;
+
+ /* Make sure this is an SMB packet. smb_size contains NetBIOS header so subtract 4 from it. */
+ if ((strncmp(smb_base(inbuf),"\377SMB",4) != 0) || (size < (smb_size - 4))) {
+ DEBUG(2,("Non-SMB packet of length %d. Terminating server\n",smb_len(inbuf)));
+ exit_server("Non-SMB packet");
+ return(-1);
+ }
+
+ /* yuck! this is an interim measure before we get rid of our
+ current inbuf/outbuf system */
+ global_smbpid = SVAL(inbuf,smb_pid);
+
+ if (smb_messages[type].fn == NULL) {
+ DEBUG(0,("Unknown message type %d!\n",type));
+ smb_dump("Unknown", 1, inbuf, size);
+ outsize = reply_unknown(inbuf,outbuf);
+ } else {
+ int flags = smb_messages[type].flags;
+ static uint16 last_session_tag = UID_FIELD_INVALID;
+ /* In share mode security we must ignore the vuid. */
+ uint16 session_tag = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID : SVAL(inbuf,smb_uid);
+ connection_struct *conn = conn_find(SVAL(inbuf,smb_tid));
+
+ DEBUG(3,("switch message %s (pid %d)\n",smb_fn_name(type),(int)pid));
+
+ smb_dump(smb_fn_name(type), 1, inbuf, size);
+ if(global_oplock_break) {
+ if(flags & QUEUE_IN_OPLOCK) {
+ /*
+ * Queue this message as we are the process of an oplock break.
+ */
+
+ DEBUG( 2, ( "switch_message: queueing message due to being in " ) );
+ DEBUGADD( 2, ( "oplock break state.\n" ) );
+
+ push_oplock_pending_smb_message( inbuf, size );
+ return -1;
+ }
+ }
+
+ /* Ensure this value is replaced in the incoming packet. */
+ SSVAL(inbuf,smb_uid,session_tag);
+
+ /*
+ * Ensure the correct username is in current_user_info.
+ * This is a really ugly bugfix for problems with
+ * multiple session_setup_and_X's being done and
+ * allowing %U and %G substitutions to work correctly.
+ * There is a reason this code is done here, don't
+ * move it unless you know what you're doing... :-).
+ * JRA.
+ */
+
+ if (session_tag != last_session_tag) {
+ user_struct *vuser = NULL;
+
+ last_session_tag = session_tag;
+ if(session_tag != UID_FIELD_INVALID)
+ vuser = get_valid_user_struct(session_tag);
+ if(vuser != NULL)
+ set_current_user_info(&vuser->user);
+ }
+
+ /* does this protocol need to be run as root? */
+ if (!(flags & AS_USER))
+ change_to_root_user();
+
+ /* does this protocol need a valid tree connection? */
+ if ((flags & AS_USER) && !conn)
+ return ERROR_DOS(ERRSRV, ERRinvnid);
+
+
+ /* does this protocol need to be run as the connected user? */
+ if ((flags & AS_USER) && !change_to_user(conn,session_tag)) {
+ if (flags & AS_GUEST)
+ flags &= ~AS_USER;
+ else
+ return(ERROR_DOS(ERRSRV,ERRaccess));
+ }
+
+ /* this code is to work around a bug is MS client 3 without
+ introducing a security hole - it needs to be able to do
+ print queue checks as guest if it isn't logged in properly */
+ if (flags & AS_USER)
+ flags &= ~AS_GUEST;
+
+ /* does it need write permission? */
+ if ((flags & NEED_WRITE) && !CAN_WRITE(conn))
+ return(ERROR_DOS(ERRSRV,ERRaccess));
+
+ /* ipc services are limited */
+ if (IS_IPC(conn) && (flags & AS_USER) && !(flags & CAN_IPC))
+ return(ERROR_DOS(ERRSRV,ERRaccess));
+
+ /* load service specific parameters */
+ if (conn && !set_current_service(conn,(flags & AS_USER)?True:False))
+ return(ERROR_DOS(ERRSRV,ERRaccess));
+
+ /* does this protocol need to be run as guest? */
+ if ((flags & AS_GUEST) && (!change_to_guest() ||
+ !check_access(smbd_server_fd(), lp_hostsallow(-1), lp_hostsdeny(-1))))
+ return(ERROR_DOS(ERRSRV,ERRaccess));
+
+ last_inbuf = inbuf;
+
+ outsize = smb_messages[type].fn(conn, inbuf,outbuf,size,bufsize);
+ }
+
+ smb_dump(smb_fn_name(type), 0, outbuf, outsize);
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ Construct a reply to the incoming packet.
+****************************************************************************/
+
+static int construct_reply(char *inbuf,char *outbuf,int size,int bufsize)
+{
+ int type = CVAL(inbuf,smb_com);
+ int outsize = 0;
+ int msg_type = CVAL(inbuf,0);
+
+ GetTimeOfDay(&smb_last_time);
+
+ chain_size = 0;
+ file_chain_reset();
+ reset_chain_p();
+
+ if (msg_type != 0)
+ return(reply_special(inbuf,outbuf));
+
+ construct_reply_common(inbuf, outbuf);
+
+ outsize = switch_message(type,inbuf,outbuf,size,bufsize);
+
+ outsize += chain_size;
+
+ if(outsize > 4)
+ smb_setlen(outbuf,outsize - 4);
+ return(outsize);
+}
+
+/****************************************************************************
+ Keep track of the number of running smbd's. This functionality is used to
+ 'hard' limit Samba overhead on resource constrained systems.
+****************************************************************************/
+
+static BOOL process_count_update_successful = False;
+
+static int32 increment_smbd_process_count(void)
+{
+ int32 total_smbds;
+
+ if (lp_max_smbd_processes()) {
+ total_smbds = 0;
+ if (tdb_change_int32_atomic(conn_tdb_ctx(), "INFO/total_smbds", &total_smbds, 1) == -1)
+ return 1;
+ process_count_update_successful = True;
+ return total_smbds + 1;
+ }
+ return 1;
+}
+
+void decrement_smbd_process_count(void)
+{
+ int32 total_smbds;
+
+ if (lp_max_smbd_processes() && process_count_update_successful) {
+ total_smbds = 1;
+ tdb_change_int32_atomic(conn_tdb_ctx(), "INFO/total_smbds", &total_smbds, -1);
+ }
+}
+
+static BOOL smbd_process_limit(void)
+{
+ int32 total_smbds;
+
+ if (lp_max_smbd_processes()) {
+
+ /* Always add one to the smbd process count, as exit_server() always
+ * subtracts one.
+ */
+
+ if (!conn_tdb_ctx()) {
+ DEBUG(0,("smbd_process_limit: max smbd processes parameter set with status parameter not \
+set. Ignoring max smbd restriction.\n"));
+ return False;
+ }
+
+ total_smbds = increment_smbd_process_count();
+ return total_smbds > lp_max_smbd_processes();
+ }
+ else
+ return False;
+}
+
+/****************************************************************************
+ Process an smb from the client - split out from the smbd_process() code so
+ it can be used by the oplock break code.
+****************************************************************************/
+
+void process_smb(char *inbuf, char *outbuf)
+{
+ static int trans_num;
+ int msg_type = CVAL(inbuf,0);
+ int32 len = smb_len(inbuf);
+ int nread = len + 4;
+
+ DO_PROFILE_INC(smb_count);
+
+ if (trans_num == 0) {
+ /* on the first packet, check the global hosts allow/ hosts
+ deny parameters before doing any parsing of the packet
+ passed to us by the client. This prevents attacks on our
+ parsing code from hosts not in the hosts allow list */
+ if (smbd_process_limit() ||
+ !check_access(smbd_server_fd(), lp_hostsallow(-1), lp_hostsdeny(-1))) {
+ /* send a negative session response "not listening on calling name" */
+ static unsigned char buf[5] = {0x83, 0, 0, 1, 0x81};
+ DEBUG( 1, ( "Connection denied from %s\n", client_addr() ) );
+ (void)send_smb(smbd_server_fd(),(char *)buf);
+ exit_server("connection denied");
+ }
+ }
+
+ DEBUG( 6, ( "got message type 0x%x of len 0x%x\n", msg_type, len ) );
+ DEBUG( 3, ( "Transaction %d of length %d\n", trans_num, nread ) );
+
+ if (msg_type == 0)
+ show_msg(inbuf);
+ else if(msg_type == SMBkeepalive)
+ return; /* Keepalive packet. */
+
+ nread = construct_reply(inbuf,outbuf,nread,max_send);
+
+ if(nread > 0) {
+ if (CVAL(outbuf,0) == 0)
+ show_msg(outbuf);
+
+ if (nread != smb_len(outbuf) + 4) {
+ DEBUG(0,("ERROR: Invalid message response size! %d %d\n",
+ nread, smb_len(outbuf)));
+ } else if (!send_smb(smbd_server_fd(),outbuf)) {
+ exit_server("process_smb: send_smb failed.");
+ }
+ }
+ trans_num++;
+}
+
+/****************************************************************************
+ Return a string containing the function name of a SMB command.
+****************************************************************************/
+
+const char *smb_fn_name(int type)
+{
+ const char *unknown_name = "SMBunknown";
+
+ if (smb_messages[type].name == NULL)
+ return(unknown_name);
+
+ return(smb_messages[type].name);
+}
+
+/****************************************************************************
+ Helper functions for contruct_reply.
+****************************************************************************/
+
+static uint32 common_flags2 = FLAGS2_LONG_PATH_COMPONENTS|FLAGS2_EXTENDED_SECURITY|FLAGS2_32_BIT_ERROR_CODES;
+
+void remove_from_common_flags2(uint32 v)
+{
+ common_flags2 &= ~v;
+}
+
+void construct_reply_common(char *inbuf,char *outbuf)
+{
+ memset(outbuf,'\0',smb_size);
+
+ set_message(outbuf,0,0,True);
+ SCVAL(outbuf,smb_com,CVAL(inbuf,smb_com));
+
+ memcpy(outbuf+4,inbuf+4,4);
+ SCVAL(outbuf,smb_rcls,SMB_SUCCESS);
+ SCVAL(outbuf,smb_reh,0);
+ SCVAL(outbuf,smb_flg, FLAG_REPLY | (CVAL(inbuf,smb_flg) & FLAG_CASELESS_PATHNAMES));
+ SSVAL(outbuf,smb_flg2,
+ (SVAL(inbuf,smb_flg2) & FLAGS2_UNICODE_STRINGS) |
+ common_flags2);
+
+ SSVAL(outbuf,smb_err,SMB_SUCCESS);
+ SSVAL(outbuf,smb_tid,SVAL(inbuf,smb_tid));
+ SSVAL(outbuf,smb_pid,SVAL(inbuf,smb_pid));
+ SSVAL(outbuf,smb_uid,SVAL(inbuf,smb_uid));
+ SSVAL(outbuf,smb_mid,SVAL(inbuf,smb_mid));
+}
+
+/****************************************************************************
+ Construct a chained reply and add it to the already made reply
+****************************************************************************/
+
+int chain_reply(char *inbuf,char *outbuf,int size,int bufsize)
+{
+ static char *orig_inbuf;
+ static char *orig_outbuf;
+ int smb_com1, smb_com2 = CVAL(inbuf,smb_vwv0);
+ unsigned smb_off2 = SVAL(inbuf,smb_vwv1);
+ char *inbuf2, *outbuf2;
+ int outsize2;
+ char inbuf_saved[smb_wct];
+ char outbuf_saved[smb_wct];
+ int wct = CVAL(outbuf,smb_wct);
+ int outsize = smb_size + 2*wct + SVAL(outbuf,smb_vwv0+2*wct);
+
+ /* maybe its not chained */
+ if (smb_com2 == 0xFF) {
+ SCVAL(outbuf,smb_vwv0,0xFF);
+ return outsize;
+ }
+
+ if (chain_size == 0) {
+ /* this is the first part of the chain */
+ orig_inbuf = inbuf;
+ orig_outbuf = outbuf;
+ }
+
+ /*
+ * The original Win95 redirector dies on a reply to
+ * a lockingX and read chain unless the chain reply is
+ * 4 byte aligned. JRA.
+ */
+
+ outsize = (outsize + 3) & ~3;
+
+ /* we need to tell the client where the next part of the reply will be */
+ SSVAL(outbuf,smb_vwv1,smb_offset(outbuf+outsize,outbuf));
+ SCVAL(outbuf,smb_vwv0,smb_com2);
+
+ /* remember how much the caller added to the chain, only counting stuff
+ after the parameter words */
+ chain_size += outsize - smb_wct;
+
+ /* work out pointers into the original packets. The
+ headers on these need to be filled in */
+ inbuf2 = orig_inbuf + smb_off2 + 4 - smb_wct;
+ outbuf2 = orig_outbuf + SVAL(outbuf,smb_vwv1) + 4 - smb_wct;
+
+ /* remember the original command type */
+ smb_com1 = CVAL(orig_inbuf,smb_com);
+
+ /* save the data which will be overwritten by the new headers */
+ memcpy(inbuf_saved,inbuf2,smb_wct);
+ memcpy(outbuf_saved,outbuf2,smb_wct);
+
+ /* give the new packet the same header as the last part of the SMB */
+ memmove(inbuf2,inbuf,smb_wct);
+
+ /* create the in buffer */
+ SCVAL(inbuf2,smb_com,smb_com2);
+
+ /* create the out buffer */
+ construct_reply_common(inbuf2, outbuf2);
+
+ DEBUG(3,("Chained message\n"));
+ show_msg(inbuf2);
+
+ /* process the request */
+ outsize2 = switch_message(smb_com2,inbuf2,outbuf2,size-chain_size,
+ bufsize-chain_size);
+
+ /* copy the new reply and request headers over the old ones, but
+ preserve the smb_com field */
+ memmove(orig_outbuf,outbuf2,smb_wct);
+ SCVAL(orig_outbuf,smb_com,smb_com1);
+
+ /* restore the saved data, being careful not to overwrite any
+ data from the reply header */
+ memcpy(inbuf2,inbuf_saved,smb_wct);
+
+ {
+ int ofs = smb_wct - PTR_DIFF(outbuf2,orig_outbuf);
+ if (ofs < 0) ofs = 0;
+ memmove(outbuf2+ofs,outbuf_saved+ofs,smb_wct-ofs);
+ }
+
+ return outsize2;
+}
+
+/****************************************************************************
+ Setup the needed select timeout.
+****************************************************************************/
+
+static int setup_select_timeout(void)
+{
+ int select_timeout;
+ int t;
+
+ select_timeout = blocking_locks_timeout(SMBD_SELECT_TIMEOUT);
+ select_timeout *= 1000;
+
+ t = change_notify_timeout();
+ if (t != -1)
+ select_timeout = MIN(select_timeout, t*1000);
+
+ if (print_notify_messages_pending())
+ select_timeout = MIN(select_timeout, 1000);
+
+ return select_timeout;
+}
+
+/****************************************************************************
+ Check if services need reloading.
+****************************************************************************/
+
+void check_reload(int t)
+{
+ static time_t last_smb_conf_reload_time = 0;
+
+ if(last_smb_conf_reload_time == 0)
+ last_smb_conf_reload_time = t;
+
+ if (reload_after_sighup || (t >= last_smb_conf_reload_time+SMBD_RELOAD_CHECK)) {
+ reload_services(True);
+ reload_after_sighup = False;
+ last_smb_conf_reload_time = t;
+ }
+}
+
+/****************************************************************************
+ Process any timeout housekeeping. Return False if the caller should exit.
+****************************************************************************/
+
+static BOOL timeout_processing(int deadtime, int *select_timeout, time_t *last_timeout_processing_time)
+{
+ static time_t last_keepalive_sent_time = 0;
+ static time_t last_idle_closed_check = 0;
+ time_t t;
+ BOOL allidle = True;
+ extern int keepalive;
+
+ if (smb_read_error == READ_EOF) {
+ DEBUG(3,("timeout_processing: End of file from client (client has disconnected).\n"));
+ return False;
+ }
+
+ if (smb_read_error == READ_ERROR) {
+ DEBUG(3,("timeout_processing: receive_smb error (%s) Exiting\n",
+ strerror(errno)));
+ return False;
+ }
+
+ if (smb_read_error == READ_BAD_SIG) {
+ DEBUG(3,("timeout_processing: receive_smb error bad smb signature. Exiting\n"));
+ return False;
+ }
+
+ *last_timeout_processing_time = t = time(NULL);
+
+ if(last_keepalive_sent_time == 0)
+ last_keepalive_sent_time = t;
+
+ if(last_idle_closed_check == 0)
+ last_idle_closed_check = t;
+
+ /* become root again if waiting */
+ change_to_root_user();
+
+ /* run all registered idle events */
+ smb_run_idle_events(t);
+
+ /* check if we need to reload services */
+ check_reload(t);
+
+ /* automatic timeout if all connections are closed */
+ if (conn_num_open()==0 && (t - last_idle_closed_check) >= IDLE_CLOSED_TIMEOUT) {
+ DEBUG( 2, ( "Closing idle connection\n" ) );
+ return False;
+ } else {
+ last_idle_closed_check = t;
+ }
+
+ if (keepalive && (t - last_keepalive_sent_time)>keepalive) {
+ extern struct auth_context *negprot_global_auth_context;
+ if (!send_keepalive(smbd_server_fd())) {
+ DEBUG( 2, ( "Keepalive failed - exiting.\n" ) );
+ return False;
+ }
+
+ /* send a keepalive for a password server or the like.
+ This is attached to the auth_info created in the
+ negprot */
+ if (negprot_global_auth_context && negprot_global_auth_context->challenge_set_method
+ && negprot_global_auth_context->challenge_set_method->send_keepalive) {
+
+ negprot_global_auth_context->challenge_set_method->send_keepalive
+ (&negprot_global_auth_context->challenge_set_method->private_data);
+ }
+
+ last_keepalive_sent_time = t;
+ }
+
+ /* check for connection timeouts */
+ allidle = conn_idle_all(t, deadtime);
+
+ if (allidle && conn_num_open()>0) {
+ DEBUG(2,("Closing idle connection 2.\n"));
+ return False;
+ }
+
+ if(global_machine_password_needs_changing &&
+ /* for ADS we need to do a regular ADS password change, not a domain
+ password change */
+ lp_security() == SEC_DOMAIN) {
+
+ unsigned char trust_passwd_hash[16];
+ time_t lct;
+
+ /*
+ * We're in domain level security, and the code that
+ * read the machine password flagged that the machine
+ * password needs changing.
+ */
+
+ /*
+ * First, open the machine password file with an exclusive lock.
+ */
+
+ if (secrets_lock_trust_account_password(lp_workgroup(), True) == False) {
+ DEBUG(0,("process: unable to lock the machine account password for \
+machine %s in domain %s.\n", global_myname(), lp_workgroup() ));
+ return True;
+ }
+
+ if(!secrets_fetch_trust_account_password(lp_workgroup(), trust_passwd_hash, &lct, NULL)) {
+ DEBUG(0,("process: unable to read the machine account password for \
+machine %s in domain %s.\n", global_myname(), lp_workgroup()));
+ secrets_lock_trust_account_password(lp_workgroup(), False);
+ return True;
+ }
+
+ /*
+ * Make sure someone else hasn't already done this.
+ */
+
+ if(t < lct + lp_machine_password_timeout()) {
+ global_machine_password_needs_changing = False;
+ secrets_lock_trust_account_password(lp_workgroup(), False);
+ return True;
+ }
+
+ /* always just contact the PDC here */
+
+ change_trust_account_password( lp_workgroup(), NULL);
+ global_machine_password_needs_changing = False;
+ secrets_lock_trust_account_password(lp_workgroup(), False);
+ }
+
+ /*
+ * Check to see if we have any blocking locks
+ * outstanding on the queue.
+ */
+ process_blocking_lock_queue(t);
+
+ /* update printer queue caches if necessary */
+
+ update_monitored_printq_cache();
+
+ /*
+ * Check to see if we have any change notifies
+ * outstanding on the queue.
+ */
+ process_pending_change_notify_queue(t);
+
+ /*
+ * Now we are root, check if the log files need pruning.
+ * Force a log file check.
+ */
+ force_check_log_size();
+ check_log_size();
+
+ /* Send any queued printer notify message to interested smbd's. */
+
+ print_notify_send_messages(0);
+
+ /*
+ * Modify the select timeout depending upon
+ * what we have remaining in our queues.
+ */
+
+ *select_timeout = setup_select_timeout();
+
+ return True;
+}
+
+/****************************************************************************
+ process commands from the client
+****************************************************************************/
+
+void smbd_process(void)
+{
+ extern int smb_echo_count;
+ time_t last_timeout_processing_time = time(NULL);
+ unsigned int num_smbs = 0;
+ const size_t total_buffer_size = BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN;
+
+ InBuffer = (char *)malloc(total_buffer_size);
+ OutBuffer = (char *)malloc(total_buffer_size);
+ if ((InBuffer == NULL) || (OutBuffer == NULL))
+ return;
+
+#if defined(DEVELOPER)
+ clobber_region(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, InBuffer, total_buffer_size);
+ clobber_region(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, OutBuffer, total_buffer_size);
+#endif
+
+ max_recv = MIN(lp_maxxmit(),BUFFER_SIZE);
+
+ while (True) {
+ int deadtime = lp_deadtime()*60;
+ int select_timeout = setup_select_timeout();
+ int num_echos;
+
+ if (deadtime <= 0)
+ deadtime = DEFAULT_SMBD_TIMEOUT;
+
+ errno = 0;
+
+ /* free up temporary memory */
+ lp_talloc_free();
+ main_loop_talloc_free();
+
+ /* run all registered idle events */
+ smb_run_idle_events(time(NULL));
+
+
+ /* Did someone ask for immediate checks on things like blocking locks ? */
+ if (select_timeout == 0) {
+ if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time))
+ return;
+ num_smbs = 0; /* Reset smb counter. */
+ }
+
+#if defined(DEVELOPER)
+ clobber_region(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, InBuffer, total_buffer_size);
+#endif
+
+ while (!receive_message_or_smb(InBuffer,BUFFER_SIZE+LARGE_WRITEX_HDR_SIZE,select_timeout)) {
+ if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time))
+ return;
+ num_smbs = 0; /* Reset smb counter. */
+ }
+
+ /*
+ * Ensure we do timeout processing if the SMB we just got was
+ * only an echo request. This allows us to set the select
+ * timeout in 'receive_message_or_smb()' to any value we like
+ * without worrying that the client will send echo requests
+ * faster than the select timeout, thus starving out the
+ * essential processing (change notify, blocking locks) that
+ * the timeout code does. JRA.
+ */
+ num_echos = smb_echo_count;
+
+ clobber_region(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, OutBuffer, total_buffer_size);
+
+ process_smb(InBuffer, OutBuffer);
+
+ if (smb_echo_count != num_echos) {
+ if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time))
+ return;
+ num_smbs = 0; /* Reset smb counter. */
+ }
+
+ num_smbs++;
+
+ /*
+ * If we are getting smb requests in a constant stream
+ * with no echos, make sure we attempt timeout processing
+ * every select_timeout milliseconds - but only check for this
+ * every 200 smb requests.
+ */
+
+ if ((num_smbs % 200) == 0) {
+ time_t new_check_time = time(NULL);
+ if(new_check_time - last_timeout_processing_time >= (select_timeout/1000)) {
+ if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time))
+ return;
+ num_smbs = 0; /* Reset smb counter. */
+ last_timeout_processing_time = new_check_time; /* Reset time. */
+ }
+ }
+
+ /* The timeout_processing function isn't run nearly
+ often enough to implement 'max log size' without
+ overrunning the size of the file by many megabytes.
+ This is especially true if we are running at debug
+ level 10. Checking every 50 SMBs is a nice
+ tradeoff of performance vs log file size overrun. */
+
+ if ((num_smbs % 50) == 0 && need_to_check_log_size()) {
+ change_to_root_user();
+ check_log_size();
+ }
+ }
+}
diff --git a/source/smbd/quotas.c b/source/smbd/quotas.c
new file mode 100644
index 00000000000..e439c1e571a
--- /dev/null
+++ b/source/smbd/quotas.c
@@ -0,0 +1,1279 @@
+/*
+ Unix SMB/CIFS implementation.
+ support for quotas
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+/*
+ * This is one of the most system dependent parts of Samba, and its
+ * done a litle differently. Each system has its own way of doing
+ * things :-(
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_QUOTA
+
+#ifndef HAVE_SYS_QUOTAS
+
+/* just a quick hack because sysquotas.h is included before linux/quota.h */
+#ifdef QUOTABLOCK_SIZE
+#undef QUOTABLOCK_SIZE
+#endif
+
+#ifdef WITH_QUOTAS
+
+#if defined(VXFS_QUOTA)
+
+/*
+ * In addition to their native filesystems, some systems have Veritas VxFS.
+ * Declare here, define at end: reduces likely "include" interaction problems.
+ * David Lee <T.D.Lee@durham.ac.uk>
+ */
+BOOL disk_quotas_vxfs(const pstring name, char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize);
+
+#endif /* VXFS_QUOTA */
+
+#ifdef LINUX
+
+#include <sys/types.h>
+#include <mntent.h>
+
+/*
+ * This shouldn't be neccessary - it should be /usr/include/sys/quota.h
+ * So we include all the files has *should* be in the system into a large,
+ * grungy samba_linux_quoatas.h Sometimes I *hate* Linux :-). JRA.
+ */
+
+#include "samba_linux_quota.h"
+#include "samba_xfs_quota.h"
+
+typedef struct _LINUX_SMB_DISK_QUOTA {
+ SMB_BIG_UINT bsize;
+ SMB_BIG_UINT hardlimit; /* In bsize units. */
+ SMB_BIG_UINT softlimit; /* In bsize units. */
+ SMB_BIG_UINT curblocks; /* In bsize units. */
+ SMB_BIG_UINT ihardlimit; /* inode hard limit. */
+ SMB_BIG_UINT isoftlimit; /* inode soft limit. */
+ SMB_BIG_UINT curinodes; /* Current used inodes. */
+} LINUX_SMB_DISK_QUOTA;
+
+/****************************************************************************
+ Abstract out the XFS Quota Manager quota get call.
+****************************************************************************/
+
+static int get_smb_linux_xfs_quota(char *path, uid_t euser_id, gid_t egrp_id, LINUX_SMB_DISK_QUOTA *dp)
+{
+ struct fs_disk_quota D;
+ int ret;
+
+ ZERO_STRUCT(D);
+
+ ret = quotactl(QCMD(Q_XGETQUOTA,USRQUOTA), path, euser_id, (caddr_t)&D);
+
+ if (ret)
+ ret = quotactl(QCMD(Q_XGETQUOTA,GRPQUOTA), path, egrp_id, (caddr_t)&D);
+
+ if (ret)
+ return ret;
+
+ dp->bsize = (SMB_BIG_UINT)512;
+ dp->softlimit = (SMB_BIG_UINT)D.d_blk_softlimit;
+ dp->hardlimit = (SMB_BIG_UINT)D.d_blk_hardlimit;
+ dp->ihardlimit = (SMB_BIG_UINT)D.d_ino_hardlimit;
+ dp->isoftlimit = (SMB_BIG_UINT)D.d_ino_softlimit;
+ dp->curinodes = (SMB_BIG_UINT)D.d_icount;
+ dp->curblocks = (SMB_BIG_UINT)D.d_bcount;
+
+ return ret;
+}
+
+/****************************************************************************
+ Abstract out the old and new Linux quota get calls.
+****************************************************************************/
+
+static int get_smb_linux_v1_quota(char *path, uid_t euser_id, gid_t egrp_id, LINUX_SMB_DISK_QUOTA *dp)
+{
+ struct v1_kern_dqblk D;
+ int ret;
+
+ ZERO_STRUCT(D);
+
+ ret = quotactl(QCMD(Q_V1_GETQUOTA,USRQUOTA), path, euser_id, (caddr_t)&D);
+
+ if (ret && errno != EDQUOT)
+ ret = quotactl(QCMD(Q_V1_GETQUOTA,GRPQUOTA), path, egrp_id, (caddr_t)&D);
+
+ if (ret && errno != EDQUOT)
+ return ret;
+
+ dp->bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
+ dp->softlimit = (SMB_BIG_UINT)D.dqb_bsoftlimit;
+ dp->hardlimit = (SMB_BIG_UINT)D.dqb_bhardlimit;
+ dp->ihardlimit = (SMB_BIG_UINT)D.dqb_ihardlimit;
+ dp->isoftlimit = (SMB_BIG_UINT)D.dqb_isoftlimit;
+ dp->curinodes = (SMB_BIG_UINT)D.dqb_curinodes;
+ dp->curblocks = (SMB_BIG_UINT)D.dqb_curblocks;
+
+ return ret;
+}
+
+static int get_smb_linux_v2_quota(char *path, uid_t euser_id, gid_t egrp_id, LINUX_SMB_DISK_QUOTA *dp)
+{
+ struct v2_kern_dqblk D;
+ int ret;
+
+ ZERO_STRUCT(D);
+
+ ret = quotactl(QCMD(Q_V2_GETQUOTA,USRQUOTA), path, euser_id, (caddr_t)&D);
+
+ if (ret && errno != EDQUOT)
+ ret = quotactl(QCMD(Q_V2_GETQUOTA,GRPQUOTA), path, egrp_id, (caddr_t)&D);
+
+ if (ret && errno != EDQUOT)
+ return ret;
+
+ dp->bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
+ dp->softlimit = (SMB_BIG_UINT)D.dqb_bsoftlimit;
+ dp->hardlimit = (SMB_BIG_UINT)D.dqb_bhardlimit;
+ dp->ihardlimit = (SMB_BIG_UINT)D.dqb_ihardlimit;
+ dp->isoftlimit = (SMB_BIG_UINT)D.dqb_isoftlimit;
+ dp->curinodes = (SMB_BIG_UINT)D.dqb_curinodes;
+ dp->curblocks = ((SMB_BIG_UINT)D.dqb_curspace) / dp->bsize;
+
+ return ret;
+}
+
+/****************************************************************************
+ Brand-new generic quota interface.
+****************************************************************************/
+
+static int get_smb_linux_gen_quota(char *path, uid_t euser_id, gid_t egrp_id, LINUX_SMB_DISK_QUOTA *dp)
+{
+ struct if_dqblk D;
+ int ret;
+
+ ZERO_STRUCT(D);
+
+ ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), path, euser_id, (caddr_t)&D);
+
+ if (ret && errno != EDQUOT)
+ ret = quotactl(QCMD(Q_GETQUOTA,GRPQUOTA), path, egrp_id, (caddr_t)&D);
+
+ if (ret && errno != EDQUOT)
+ return ret;
+
+ dp->bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
+ dp->softlimit = (SMB_BIG_UINT)D.dqb_bsoftlimit;
+ dp->hardlimit = (SMB_BIG_UINT)D.dqb_bhardlimit;
+ dp->ihardlimit = (SMB_BIG_UINT)D.dqb_ihardlimit;
+ dp->isoftlimit = (SMB_BIG_UINT)D.dqb_isoftlimit;
+ dp->curinodes = (SMB_BIG_UINT)D.dqb_curinodes;
+ dp->curblocks = ((SMB_BIG_UINT)D.dqb_curspace) / dp->bsize;
+
+ return ret;
+}
+
+/****************************************************************************
+ Try to get the disk space from disk quotas (LINUX version).
+****************************************************************************/
+
+BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ int r;
+ SMB_STRUCT_STAT S;
+ FILE *fp;
+ LINUX_SMB_DISK_QUOTA D;
+ struct mntent *mnt;
+ SMB_DEV_T devno;
+ int found;
+ uid_t euser_id;
+ gid_t egrp_id;
+
+ euser_id = geteuid();
+ egrp_id = getegid();
+
+ /* find the block device file */
+
+ if ( sys_stat(path, &S) == -1 )
+ return(False) ;
+
+ devno = S.st_dev ;
+
+ fp = setmntent(MOUNTED,"r");
+ found = False ;
+
+ while ((mnt = getmntent(fp))) {
+ if ( sys_stat(mnt->mnt_dir,&S) == -1 )
+ continue ;
+
+ if (S.st_dev == devno) {
+ found = True ;
+ break;
+ }
+ }
+
+ endmntent(fp) ;
+
+ if (!found)
+ return(False);
+
+ save_re_uid();
+ set_effective_uid(0);
+
+ if (strcmp(mnt->mnt_type, "xfs")==0) {
+ r=get_smb_linux_xfs_quota(mnt->mnt_fsname, euser_id, egrp_id, &D);
+ } else {
+ r=get_smb_linux_gen_quota(mnt->mnt_fsname, euser_id, egrp_id, &D);
+ if (r == -1 && errno != EDQUOT) {
+ r=get_smb_linux_v2_quota(mnt->mnt_fsname, euser_id, egrp_id, &D);
+ if (r == -1 && errno != EDQUOT)
+ r=get_smb_linux_v1_quota(mnt->mnt_fsname, euser_id, egrp_id, &D);
+ }
+ }
+
+ restore_re_uid();
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+ *bsize = D.bsize;
+ if (r == -1) {
+ if (errno == EDQUOT) {
+ *dfree =0;
+ *dsize =D.curblocks;
+ return (True);
+ } else {
+ return(False);
+ }
+ }
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+ if (
+ (D.softlimit && D.curblocks >= D.softlimit) ||
+ (D.hardlimit && D.curblocks >= D.hardlimit) ||
+ (D.isoftlimit && D.curinodes >= D.isoftlimit) ||
+ (D.ihardlimit && D.curinodes>=D.ihardlimit)
+ ) {
+ *dfree = 0;
+ *dsize = D.curblocks;
+ } else if (D.softlimit==0 && D.hardlimit==0) {
+ return(False);
+ } else {
+ if (D.softlimit == 0)
+ D.softlimit = D.hardlimit;
+ *dfree = D.softlimit - D.curblocks;
+ *dsize = D.softlimit;
+ }
+
+ return (True);
+}
+
+#elif defined(CRAY)
+
+#include <sys/quota.h>
+#include <mntent.h>
+
+/****************************************************************************
+try to get the disk space from disk quotas (CRAY VERSION)
+****************************************************************************/
+
+BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ struct mntent *mnt;
+ FILE *fd;
+ SMB_STRUCT_STAT sbuf;
+ SMB_DEV_T devno ;
+ static SMB_DEV_T devno_cached = 0 ;
+ static pstring name;
+ struct q_request request ;
+ struct qf_header header ;
+ static int quota_default = 0 ;
+ int found ;
+
+ if ( sys_stat(path,&sbuf) == -1 )
+ return(False) ;
+
+ devno = sbuf.st_dev ;
+
+ if ( devno != devno_cached ) {
+
+ devno_cached = devno ;
+
+ if ((fd = setmntent(KMTAB)) == NULL)
+ return(False) ;
+
+ found = False ;
+
+ while ((mnt = getmntent(fd)) != NULL) {
+
+ if ( sys_stat(mnt->mnt_dir,&sbuf) == -1 )
+ continue ;
+
+ if (sbuf.st_dev == devno) {
+
+ found = True ;
+ break ;
+
+ }
+
+ }
+
+ pstrcpy(name,mnt->mnt_dir) ;
+ endmntent(fd) ;
+
+ if ( ! found )
+ return(False) ;
+ }
+
+ request.qf_magic = QF_MAGIC ;
+ request.qf_entry.id = geteuid() ;
+
+ if (quotactl(name, Q_GETQUOTA, &request) == -1)
+ return(False) ;
+
+ if ( ! request.user )
+ return(False) ;
+
+ if ( request.qf_entry.user_q.f_quota == QFV_DEFAULT ) {
+
+ if ( ! quota_default ) {
+
+ if ( quotactl(name, Q_GETHEADER, &header) == -1 )
+ return(False) ;
+ else
+ quota_default = header.user_h.def_fq ;
+ }
+
+ *dfree = quota_default ;
+
+ }else if ( request.qf_entry.user_q.f_quota == QFV_PREVENT ) {
+
+ *dfree = 0 ;
+
+ }else{
+
+ *dfree = request.qf_entry.user_q.f_quota ;
+
+ }
+
+ *dsize = request.qf_entry.user_q.f_use ;
+
+ if ( *dfree < *dsize )
+ *dfree = 0 ;
+ else
+ *dfree -= *dsize ;
+
+ *bsize = 4096 ; /* Cray blocksize */
+
+ return(True) ;
+
+}
+
+
+#elif defined(SUNOS5) || defined(SUNOS4)
+
+#include <fcntl.h>
+#include <sys/param.h>
+#if defined(SUNOS5)
+#include <sys/fs/ufs_quota.h>
+#include <sys/mnttab.h>
+#include <sys/mntent.h>
+#else /* defined(SUNOS4) */
+#include <ufs/quota.h>
+#include <mntent.h>
+#endif
+
+#if defined(SUNOS5)
+
+/****************************************************************************
+ Allows querying of remote hosts for quotas on NFS mounted shares.
+ Supports normal NFS and AMD mounts.
+ Alan Romeril <a.romeril@ic.ac.uk> July 2K.
+****************************************************************************/
+
+#include <rpc/rpc.h>
+#include <rpc/types.h>
+#include <rpcsvc/rquota.h>
+#include <rpc/nettype.h>
+#include <rpc/xdr.h>
+
+static int quotastat;
+
+static int xdr_getquota_args(XDR *xdrsp, struct getquota_args *args)
+{
+ if (!xdr_string(xdrsp, &args->gqa_pathp, RQ_PATHLEN ))
+ return(0);
+ if (!xdr_int(xdrsp, &args->gqa_uid))
+ return(0);
+ return (1);
+}
+
+static int xdr_getquota_rslt(XDR *xdrsp, struct getquota_rslt *gqr)
+{
+ if (!xdr_int(xdrsp, &quotastat)) {
+ DEBUG(6,("nfs_quotas: Status bad or zero\n"));
+ return 0;
+ }
+ if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bsize)) {
+ DEBUG(6,("nfs_quotas: Block size bad or zero\n"));
+ return 0;
+ }
+ if (!xdr_bool(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_active)) {
+ DEBUG(6,("nfs_quotas: Active bad or zero\n"));
+ return 0;
+ }
+ if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bhardlimit)) {
+ DEBUG(6,("nfs_quotas: Hardlimit bad or zero\n"));
+ return 0;
+ }
+ if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bsoftlimit)) {
+ DEBUG(6,("nfs_quotas: Softlimit bad or zero\n"));
+ return 0;
+ }
+ if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_curblocks)) {
+ DEBUG(6,("nfs_quotas: Currentblocks bad or zero\n"));
+ return 0;
+ }
+ return (1);
+}
+
+/* Restricted to SUNOS5 for the moment, I haven`t access to others to test. */
+static BOOL nfs_quotas(char *nfspath, uid_t euser_id, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ uid_t uid = euser_id;
+ struct dqblk D;
+ char *mnttype = nfspath;
+ CLIENT *clnt;
+ struct getquota_rslt gqr;
+ struct getquota_args args;
+ char *cutstr, *pathname, *host, *testpath;
+ int len;
+ static struct timeval timeout = {2,0};
+ enum clnt_stat clnt_stat;
+ BOOL ret = True;
+
+ *bsize = *dfree = *dsize = (SMB_BIG_UINT)0;
+
+ len=strcspn(mnttype, ":");
+ pathname=strstr(mnttype, ":");
+ cutstr = (char *) malloc(len+1);
+ if (!cutstr)
+ return False;
+
+ memset(cutstr, '\0', len+1);
+ host = strncat(cutstr,mnttype, sizeof(char) * len );
+ DEBUG(5,("nfs_quotas: looking for mount on \"%s\"\n", cutstr));
+ DEBUG(5,("nfs_quotas: of path \"%s\"\n", mnttype));
+ testpath=strchr_m(mnttype, ':');
+ args.gqa_pathp = testpath+1;
+ args.gqa_uid = uid;
+
+ DEBUG(5,("nfs_quotas: Asking for host \"%s\" rpcprog \"%i\" rpcvers \"%i\" network \"%s\"\n", host, RQUOTAPROG, RQUOTAVERS, "udp"));
+
+ if ((clnt = clnt_create(host, RQUOTAPROG, RQUOTAVERS, "udp")) == NULL) {
+ ret = False;
+ goto out;
+ }
+
+ clnt->cl_auth = authunix_create_default();
+ DEBUG(9,("nfs_quotas: auth_success\n"));
+
+ clnt_stat=clnt_call(clnt, RQUOTAPROC_GETQUOTA, xdr_getquota_args, (caddr_t)&args, xdr_getquota_rslt, (caddr_t)&gqr, timeout);
+
+ if (clnt_stat != RPC_SUCCESS) {
+ DEBUG(9,("nfs_quotas: clnt_call fail\n"));
+ ret = False;
+ goto out;
+ }
+
+ /*
+ * quotastat returns 0 if the rpc call fails, 1 if quotas exist, 2 if there is
+ * no quota set, and 3 if no permission to get the quota. If 0 or 3 return
+ * something sensible.
+ */
+
+ switch ( quotastat ) {
+ case 0:
+ DEBUG(9,("nfs_quotas: Remote Quotas Failed! Error \"%i\" \n", quotastat ));
+ ret = False;
+ goto out;
+
+ case 1:
+ DEBUG(9,("nfs_quotas: Good quota data\n"));
+ D.dqb_bsoftlimit = gqr.getquota_rslt_u.gqr_rquota.rq_bsoftlimit;
+ D.dqb_bhardlimit = gqr.getquota_rslt_u.gqr_rquota.rq_bhardlimit;
+ D.dqb_curblocks = gqr.getquota_rslt_u.gqr_rquota.rq_curblocks;
+ break;
+
+ case 2:
+ case 3:
+ D.dqb_bsoftlimit = 1;
+ D.dqb_curblocks = 1;
+ DEBUG(9,("nfs_quotas: Remote Quotas returned \"%i\" \n", quotastat ));
+ break;
+
+ default:
+ DEBUG(9,("nfs_quotas: Remote Quotas Questionable! Error \"%i\" \n", quotastat ));
+ break;
+ }
+
+ DEBUG(10,("nfs_quotas: Let`s look at D a bit closer... status \"%i\" bsize \"%i\" active? \"%i\" bhard \"%i\" bsoft \"%i\" curb \"%i\" \n",
+ quotastat,
+ gqr.getquota_rslt_u.gqr_rquota.rq_bsize,
+ gqr.getquota_rslt_u.gqr_rquota.rq_active,
+ gqr.getquota_rslt_u.gqr_rquota.rq_bhardlimit,
+ gqr.getquota_rslt_u.gqr_rquota.rq_bsoftlimit,
+ gqr.getquota_rslt_u.gqr_rquota.rq_curblocks));
+
+ *bsize = gqr.getquota_rslt_u.gqr_rquota.rq_bsize;
+ *dsize = D.dqb_bsoftlimit;
+
+ if (D.dqb_curblocks == D.dqb_curblocks == 1)
+ *bsize = 512;
+
+ if (D.dqb_curblocks > D.dqb_bsoftlimit) {
+ *dfree = 0;
+ *dsize = D.dqb_curblocks;
+ } else
+ *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
+
+ out:
+
+ if (clnt) {
+ if (clnt->cl_auth)
+ auth_destroy(clnt->cl_auth);
+ clnt_destroy(clnt);
+ }
+
+ DEBUG(5,("nfs_quotas: For path \"%s\" returning bsize %.0f, dfree %.0f, dsize %.0f\n",args.gqa_pathp,(double)*bsize,(double)*dfree,(double)*dsize));
+
+ SAFE_FREE(cutstr);
+ DEBUG(10,("nfs_quotas: End of nfs_quotas\n" ));
+ return ret;
+}
+#endif
+
+/****************************************************************************
+try to get the disk space from disk quotas (SunOS & Solaris2 version)
+Quota code by Peter Urbanec (amiga@cse.unsw.edu.au).
+****************************************************************************/
+
+BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ uid_t euser_id;
+ int ret;
+ struct dqblk D;
+#if defined(SUNOS5)
+ struct quotctl command;
+ int file;
+ static struct mnttab mnt;
+ static pstring name;
+ pstring devopt;
+#else /* SunOS4 */
+ struct mntent *mnt;
+ static pstring name;
+#endif
+ FILE *fd;
+ SMB_STRUCT_STAT sbuf;
+ SMB_DEV_T devno ;
+ static SMB_DEV_T devno_cached = 0 ;
+ static int found ;
+
+ euser_id = geteuid();
+
+ if ( sys_stat(path,&sbuf) == -1 )
+ return(False) ;
+
+ devno = sbuf.st_dev ;
+ DEBUG(5,("disk_quotas: looking for path \"%s\" devno=%x\n", path,(unsigned int)devno));
+ if ( devno != devno_cached ) {
+ devno_cached = devno ;
+#if defined(SUNOS5)
+ if ((fd = sys_fopen(MNTTAB, "r")) == NULL)
+ return(False) ;
+
+ found = False ;
+ slprintf(devopt, sizeof(devopt) - 1, "dev=%x", (unsigned int)devno);
+ while (getmntent(fd, &mnt) == 0) {
+ if( !hasmntopt(&mnt, devopt) )
+ continue;
+
+ DEBUG(5,("disk_quotas: testing \"%s\" %s\n", mnt.mnt_mountp,devopt));
+
+ /* quotas are only on vxfs, UFS or NFS */
+ if ( strcmp( mnt.mnt_fstype, MNTTYPE_UFS ) == 0 ||
+ strcmp( mnt.mnt_fstype, "nfs" ) == 0 ||
+ strcmp( mnt.mnt_fstype, "vxfs" ) == 0 ) {
+ found = True ;
+ break;
+ }
+ }
+
+ pstrcpy(name,mnt.mnt_mountp) ;
+ pstrcat(name,"/quotas") ;
+ fclose(fd) ;
+#else /* SunOS4 */
+ if ((fd = setmntent(MOUNTED, "r")) == NULL)
+ return(False) ;
+
+ found = False ;
+ while ((mnt = getmntent(fd)) != NULL) {
+ if ( sys_stat(mnt->mnt_dir,&sbuf) == -1 )
+ continue ;
+ DEBUG(5,("disk_quotas: testing \"%s\" devno=%x\n", mnt->mnt_dir,(unsigned int)sbuf.st_dev));
+ if (sbuf.st_dev == devno) {
+ found = True ;
+ break;
+ }
+ }
+
+ pstrcpy(name,mnt->mnt_fsname) ;
+ endmntent(fd) ;
+#endif
+ }
+
+ if ( ! found )
+ return(False) ;
+
+ save_re_uid();
+ set_effective_uid(0);
+
+#if defined(SUNOS5)
+ if ( strcmp( mnt.mnt_fstype, "nfs" ) == 0) {
+ BOOL retval;
+ DEBUG(5,("disk_quotas: looking for mountpath (NFS) \"%s\"\n", mnt.mnt_special));
+ retval = nfs_quotas(mnt.mnt_special, euser_id, bsize, dfree, dsize);
+ restore_re_uid();
+ return retval;
+ }
+
+ DEBUG(5,("disk_quotas: looking for quotas file \"%s\"\n", name));
+ if((file=sys_open(name, O_RDONLY,0))<0) {
+ restore_re_uid();
+ return(False);
+ }
+ command.op = Q_GETQUOTA;
+ command.uid = euser_id;
+ command.addr = (caddr_t) &D;
+ ret = ioctl(file, Q_QUOTACTL, &command);
+ close(file);
+#else
+ DEBUG(5,("disk_quotas: trying quotactl on device \"%s\"\n", name));
+ ret = quotactl(Q_GETQUOTA, name, euser_id, &D);
+#endif
+
+ restore_re_uid();
+
+ if (ret < 0) {
+ DEBUG(5,("disk_quotas ioctl (Solaris) failed. Error = %s\n", strerror(errno) ));
+
+#if defined(SUNOS5) && defined(VXFS_QUOTA)
+ /* If normal quotactl() fails, try vxfs private calls */
+ set_effective_uid(euser_id);
+ DEBUG(5,("disk_quotas: mount type \"%s\"\n", mnt.mnt_fstype));
+ if ( 0 == strcmp ( mnt.mnt_fstype, "vxfs" )) {
+ BOOL retval;
+ retval = disk_quotas_vxfs(name, path, bsize, dfree, dsize);
+ return(retval);
+ }
+#else
+ return(False);
+#endif
+ }
+
+ /* If softlimit is zero, set it equal to hardlimit.
+ */
+
+ if (D.dqb_bsoftlimit==0)
+ D.dqb_bsoftlimit = D.dqb_bhardlimit;
+
+ /* Use softlimit to determine disk space. A user exceeding the quota is told
+ * that there's no space left. Writes might actually work for a bit if the
+ * hardlimit is set higher than softlimit. Effectively the disk becomes
+ * made of rubber latex and begins to expand to accommodate the user :-)
+ */
+
+ if (D.dqb_bsoftlimit==0)
+ return(False);
+ *bsize = DEV_BSIZE;
+ *dsize = D.dqb_bsoftlimit;
+
+ if (D.dqb_curblocks > D.dqb_bsoftlimit) {
+ *dfree = 0;
+ *dsize = D.dqb_curblocks;
+ } else
+ *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
+
+ DEBUG(5,("disk_quotas for path \"%s\" returning bsize %.0f, dfree %.0f, dsize %.0f\n",
+ path,(double)*bsize,(double)*dfree,(double)*dsize));
+
+ return(True);
+}
+
+
+#elif defined(OSF1)
+#include <ufs/quota.h>
+
+/****************************************************************************
+try to get the disk space from disk quotas - OSF1 version
+****************************************************************************/
+
+BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ int r, save_errno;
+ struct dqblk D;
+ SMB_STRUCT_STAT S;
+ uid_t euser_id;
+
+ /*
+ * This code presumes that OSF1 will only
+ * give out quota info when the real uid
+ * matches the effective uid. JRA.
+ */
+ euser_id = geteuid();
+ save_re_uid();
+ if (set_re_uid() != 0) return False;
+
+ r= quotactl(path,QCMD(Q_GETQUOTA, USRQUOTA),euser_id,(char *) &D);
+ if (r) {
+ save_errno = errno;
+ }
+
+ restore_re_uid();
+
+ *bsize = DEV_BSIZE;
+
+ if (r)
+ {
+ if (save_errno == EDQUOT) /* disk quota exceeded */
+ {
+ *dfree = 0;
+ *dsize = D.dqb_curblocks;
+ return (True);
+ }
+ else
+ return (False);
+ }
+
+ /* If softlimit is zero, set it equal to hardlimit.
+ */
+
+ if (D.dqb_bsoftlimit==0)
+ D.dqb_bsoftlimit = D.dqb_bhardlimit;
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+
+ if (D.dqb_bsoftlimit==0)
+ return(False);
+
+ if ((D.dqb_curblocks>D.dqb_bsoftlimit)) {
+ *dfree = 0;
+ *dsize = D.dqb_curblocks;
+ } else {
+ *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
+ *dsize = D.dqb_bsoftlimit;
+ }
+ return (True);
+}
+
+#elif defined (IRIX6)
+/****************************************************************************
+try to get the disk space from disk quotas (IRIX 6.2 version)
+****************************************************************************/
+
+#include <sys/quota.h>
+#include <mntent.h>
+
+BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ uid_t euser_id;
+ int r;
+ struct dqblk D;
+ struct fs_disk_quota F;
+ SMB_STRUCT_STAT S;
+ FILE *fp;
+ struct mntent *mnt;
+ SMB_DEV_T devno;
+ int found;
+
+ /* find the block device file */
+
+ if ( sys_stat(path, &S) == -1 ) {
+ return(False) ;
+ }
+
+ devno = S.st_dev ;
+
+ fp = setmntent(MOUNTED,"r");
+ found = False ;
+
+ while ((mnt = getmntent(fp))) {
+ if ( sys_stat(mnt->mnt_dir,&S) == -1 )
+ continue ;
+ if (S.st_dev == devno) {
+ found = True ;
+ break ;
+ }
+ }
+ endmntent(fp) ;
+
+ if (!found) {
+ return(False);
+ }
+
+ euser_id=geteuid();
+ save_re_uid();
+ set_effective_uid(0);
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+
+ *bsize = 512;
+
+ if ( 0 == strcmp ( mnt->mnt_type, "efs" ))
+ {
+ r=quotactl (Q_GETQUOTA, mnt->mnt_fsname, euser_id, (caddr_t) &D);
+
+ restore_re_uid();
+
+ if (r==-1)
+ return(False);
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+ if (
+ (D.dqb_bsoftlimit && D.dqb_curblocks>=D.dqb_bsoftlimit) ||
+ (D.dqb_bhardlimit && D.dqb_curblocks>=D.dqb_bhardlimit) ||
+ (D.dqb_fsoftlimit && D.dqb_curfiles>=D.dqb_fsoftlimit) ||
+ (D.dqb_fhardlimit && D.dqb_curfiles>=D.dqb_fhardlimit)
+ )
+ {
+ *dfree = 0;
+ *dsize = D.dqb_curblocks;
+ }
+ else if (D.dqb_bsoftlimit==0 && D.dqb_bhardlimit==0)
+ {
+ return(False);
+ }
+ else
+ {
+ *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
+ *dsize = D.dqb_bsoftlimit;
+ }
+
+ }
+ else if ( 0 == strcmp ( mnt->mnt_type, "xfs" ))
+ {
+ r=quotactl (Q_XGETQUOTA, mnt->mnt_fsname, euser_id, (caddr_t) &F);
+
+ restore_re_uid();
+
+ if (r==-1)
+ return(False);
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+ if (
+ (F.d_blk_softlimit && F.d_bcount>=F.d_blk_softlimit) ||
+ (F.d_blk_hardlimit && F.d_bcount>=F.d_blk_hardlimit) ||
+ (F.d_ino_softlimit && F.d_icount>=F.d_ino_softlimit) ||
+ (F.d_ino_hardlimit && F.d_icount>=F.d_ino_hardlimit)
+ )
+ {
+ *dfree = 0;
+ *dsize = F.d_bcount;
+ }
+ else if (F.d_blk_softlimit==0 && F.d_blk_hardlimit==0)
+ {
+ return(False);
+ }
+ else
+ {
+ *dfree = (F.d_blk_softlimit - F.d_bcount);
+ *dsize = F.d_blk_softlimit;
+ }
+
+ }
+ else
+ {
+ restore_re_uid();
+ return(False);
+ }
+
+ return (True);
+
+}
+
+#else
+
+#if defined(__FreeBSD__) || defined(__OpenBSD__)
+#include <ufs/ufs/quota.h>
+#include <machine/param.h>
+#elif AIX
+/* AIX quota patch from Ole Holm Nielsen <ohnielse@fysik.dtu.dk> */
+#include <jfs/quota.h>
+/* AIX 4.X: Rename members of the dqblk structure (ohnielse@fysik.dtu.dk) */
+#define dqb_curfiles dqb_curinodes
+#define dqb_fhardlimit dqb_ihardlimit
+#define dqb_fsoftlimit dqb_isoftlimit
+#else /* !__FreeBSD__ && !AIX && !__OpenBSD__ */
+#include <sys/quota.h>
+#include <devnm.h>
+#endif
+
+/****************************************************************************
+try to get the disk space from disk quotas - default version
+****************************************************************************/
+
+BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ int r;
+ struct dqblk D;
+ uid_t euser_id;
+#if !defined(__FreeBSD__) && !defined(AIX) && !defined(__OpenBSD__)
+ char dev_disk[256];
+ SMB_STRUCT_STAT S;
+
+ /* find the block device file */
+
+#ifdef HPUX
+ /* Need to set the cache flag to 1 for HPUX. Seems
+ * to have a significant performance boost when
+ * lstat calls on /dev access this function.
+ */
+ if ((sys_stat(path, &S)<0) || (devnm(S_IFBLK, S.st_dev, dev_disk, 256, 1)<0))
+#else
+ if ((sys_stat(path, &S)<0) || (devnm(S_IFBLK, S.st_dev, dev_disk, 256, 0)<0))
+ return (False);
+#endif /* ifdef HPUX */
+
+#endif /* !defined(__FreeBSD__) && !defined(AIX) && !defined(__OpenBSD__) */
+
+ euser_id = geteuid();
+
+#ifdef HPUX
+ /* for HPUX, real uid must be same as euid to execute quotactl for euid */
+ save_re_uid();
+ if (set_re_uid() != 0) return False;
+
+ r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D);
+
+ restore_re_uid();
+#else
+#if defined(__FreeBSD__) || defined(__OpenBSD__)
+ {
+ /* FreeBSD patches from Marty Moll <martym@arbor.edu> */
+ gid_t egrp_id;
+
+ save_re_uid();
+ set_effective_uid(0);
+
+ egrp_id = getegid();
+ r= quotactl(path,QCMD(Q_GETQUOTA,USRQUOTA),euser_id,(char *) &D);
+
+ /* As FreeBSD has group quotas, if getting the user
+ quota fails, try getting the group instead. */
+ if (r) {
+ r= quotactl(path,QCMD(Q_GETQUOTA,GRPQUOTA),egrp_id,(char *) &D);
+ }
+
+ restore_re_uid();
+ }
+#elif defined(AIX)
+ /* AIX has both USER and GROUP quotas:
+ Get the USER quota (ohnielse@fysik.dtu.dk) */
+ save_re_uid();
+ if (set_re_uid() != 0)
+ return False;
+ r= quotactl(path,QCMD(Q_GETQUOTA,USRQUOTA),euser_id,(char *) &D);
+ restore_re_uid();
+#else /* !__FreeBSD__ && !AIX && !__OpenBSD__ */
+ r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D);
+#endif /* !__FreeBSD__ && !AIX && !__OpenBSD__ */
+#endif /* HPUX */
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+#if defined(__FreeBSD__) || defined(__OpenBSD__)
+ *bsize = DEV_BSIZE;
+#else /* !__FreeBSD__ && !__OpenBSD__ */
+ *bsize = 1024;
+#endif /*!__FreeBSD__ && !__OpenBSD__ */
+
+ if (r)
+ {
+ if (errno == EDQUOT)
+ {
+ *dfree =0;
+ *dsize =D.dqb_curblocks;
+ return (True);
+ }
+ else return(False);
+ }
+
+ /* If softlimit is zero, set it equal to hardlimit.
+ */
+
+ if (D.dqb_bsoftlimit==0)
+ D.dqb_bsoftlimit = D.dqb_bhardlimit;
+
+ if (D.dqb_bsoftlimit==0)
+ return(False);
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+ if ((D.dqb_curblocks>D.dqb_bsoftlimit)
+#if !defined(__FreeBSD__) && !defined(__OpenBSD__)
+||((D.dqb_curfiles>D.dqb_fsoftlimit) && (D.dqb_fsoftlimit != 0))
+#endif
+ ) {
+ *dfree = 0;
+ *dsize = D.dqb_curblocks;
+ }
+ else {
+ *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
+ *dsize = D.dqb_bsoftlimit;
+ }
+ return (True);
+}
+
+#endif
+
+#if defined(VXFS_QUOTA)
+
+/****************************************************************************
+Try to get the disk space from Veritas disk quotas.
+ David Lee <T.D.Lee@durham.ac.uk> August 1999.
+
+Background assumptions:
+ Potentially under many Operating Systems. Initially Solaris 2.
+
+ My guess is that Veritas is largely, though not entirely,
+ independent of OS. So I have separated it out.
+
+ There may be some details. For example, OS-specific "include" files.
+
+ It is understood that HPUX 10 somehow gets Veritas quotas without
+ any special effort; if so, this routine need not be compiled in.
+ Dirk De Wachter <Dirk.DeWachter@rug.ac.be>
+
+Warning:
+ It is understood that Veritas do not publicly support this ioctl interface.
+ Rather their preference would be for the user (us) to call the native
+ OS and then for the OS itself to call through to the VxFS filesystem.
+ Presumably HPUX 10, see above, does this.
+
+Hints for porting:
+ Add your OS to "IFLIST" below.
+ Get it to compile successfully:
+ Almost certainly "include"s require attention: see SUNOS5.
+ In the main code above, arrange for it to be called: see SUNOS5.
+ Test!
+
+****************************************************************************/
+
+/* "IFLIST"
+ * This "if" is a list of ports:
+ * if defined(OS1) || defined(OS2) || ...
+ */
+#if defined(SUNOS5)
+
+#if defined(SUNOS5)
+#include <sys/fs/vx_solaris.h>
+#endif
+#include <sys/fs/vx_machdep.h>
+#include <sys/fs/vx_layout.h>
+#include <sys/fs/vx_quota.h>
+#include <sys/fs/vx_aioctl.h>
+#include <sys/fs/vx_ioctl.h>
+
+BOOL disk_quotas_vxfs(const pstring name, char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ uid_t user_id, euser_id;
+ int ret;
+ struct vx_dqblk D;
+ struct vx_quotctl quotabuf;
+ struct vx_genioctl genbuf;
+ pstring qfname;
+ int file;
+
+ /*
+ * "name" may or may not include a trailing "/quotas".
+ * Arranging consistency of calling here in "quotas.c" may not be easy and
+ * it might be easier to examine and adjust it here.
+ * Fortunately, VxFS seems not to mind at present.
+ */
+ pstrcpy(qfname, name) ;
+ /* pstrcat(qfname, "/quotas") ; */ /* possibly examine and adjust "name" */
+
+ euser_id = geteuid();
+ set_effective_uid(0);
+
+ DEBUG(5,("disk_quotas: looking for VxFS quotas file \"%s\"\n", qfname));
+ if((file=sys_open(qfname, O_RDONLY,0))<0) {
+ set_effective_uid(euser_id);
+ return(False);
+ }
+ genbuf.ioc_cmd = VX_QUOTACTL;
+ genbuf.ioc_up = (void *) &quotabuf;
+
+ quotabuf.cmd = VX_GETQUOTA;
+ quotabuf.uid = euser_id;
+ quotabuf.addr = (caddr_t) &D;
+ ret = ioctl(file, VX_ADMIN_IOCTL, &genbuf);
+ close(file);
+
+ set_effective_uid(euser_id);
+
+ if (ret < 0) {
+ DEBUG(5,("disk_quotas ioctl (VxFS) failed. Error = %s\n", strerror(errno) ));
+ return(False);
+ }
+
+ /* If softlimit is zero, set it equal to hardlimit.
+ */
+
+ if (D.dqb_bsoftlimit==0)
+ D.dqb_bsoftlimit = D.dqb_bhardlimit;
+
+ /* Use softlimit to determine disk space. A user exceeding the quota is told
+ * that there's no space left. Writes might actually work for a bit if the
+ * hardlimit is set higher than softlimit. Effectively the disk becomes
+ * made of rubber latex and begins to expand to accommodate the user :-)
+ */
+ DEBUG(5,("disk_quotas for path \"%s\" block c/s/h %ld/%ld/%ld; file c/s/h %ld/%ld/%ld\n",
+ path, D.dqb_curblocks, D.dqb_bsoftlimit, D.dqb_bhardlimit,
+ D.dqb_curfiles, D.dqb_fsoftlimit, D.dqb_fhardlimit));
+
+ if (D.dqb_bsoftlimit==0)
+ return(False);
+ *bsize = DEV_BSIZE;
+ *dsize = D.dqb_bsoftlimit;
+
+ if (D.dqb_curblocks > D.dqb_bsoftlimit) {
+ *dfree = 0;
+ *dsize = D.dqb_curblocks;
+ } else
+ *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
+
+ DEBUG(5,("disk_quotas for path \"%s\" returning bsize %.0f, dfree %.0f, dsize %.0f\n",
+ path,(double)*bsize,(double)*dfree,(double)*dsize));
+
+ return(True);
+}
+
+#endif /* SUNOS5 || ... */
+
+#endif /* VXFS_QUOTA */
+
+#else /* WITH_QUOTAS */
+
+BOOL disk_quotas(const char *path,SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize)
+{
+ (*bsize) = 512; /* This value should be ignored */
+
+ /* And just to be sure we set some values that hopefully */
+ /* will be larger that any possible real-world value */
+ (*dfree) = (SMB_BIG_UINT)-1;
+ (*dsize) = (SMB_BIG_UINT)-1;
+
+ /* As we have select not to use quotas, allways fail */
+ return False;
+}
+#endif /* WITH_QUOTAS */
+
+#else /* HAVE_SYS_QUOTAS */
+/* wrapper to the new sys_quota interface
+ this file should be removed later
+ */
+BOOL disk_quotas(const char *path,SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize)
+{
+ int r;
+ SMB_DISK_QUOTA D;
+ unid_t id;
+
+ id.uid = geteuid();
+
+ ZERO_STRUCT(D);
+ r=sys_get_quota(path, SMB_USER_QUOTA_TYPE, id, &D);
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+ *bsize = D.bsize;
+ if (r == -1) {
+ if (errno == EDQUOT) {
+ *dfree =0;
+ *dsize =D.curblocks;
+ return (True);
+ } else {
+ goto try_group_quota;
+ }
+ }
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+ if (
+ (D.softlimit && D.curblocks >= D.softlimit) ||
+ (D.hardlimit && D.curblocks >= D.hardlimit) ||
+ (D.isoftlimit && D.curinodes >= D.isoftlimit) ||
+ (D.ihardlimit && D.curinodes>=D.ihardlimit)
+ ) {
+ *dfree = 0;
+ *dsize = D.curblocks;
+ } else if (D.softlimit==0 && D.hardlimit==0) {
+ goto try_group_quota;
+ } else {
+ if (D.softlimit == 0)
+ D.softlimit = D.hardlimit;
+ *dfree = D.softlimit - D.curblocks;
+ *dsize = D.softlimit;
+ }
+
+ return True;
+
+try_group_quota:
+ id.gid = getegid();
+
+ ZERO_STRUCT(D);
+ r=sys_get_quota(path, SMB_GROUP_QUOTA_TYPE, id, &D);
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+ *bsize = D.bsize;
+ if (r == -1) {
+ if (errno == EDQUOT) {
+ *dfree =0;
+ *dsize =D.curblocks;
+ return (True);
+ } else {
+ return False;
+ }
+ }
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+ if (
+ (D.softlimit && D.curblocks >= D.softlimit) ||
+ (D.hardlimit && D.curblocks >= D.hardlimit) ||
+ (D.isoftlimit && D.curinodes >= D.isoftlimit) ||
+ (D.ihardlimit && D.curinodes>=D.ihardlimit)
+ ) {
+ *dfree = 0;
+ *dsize = D.curblocks;
+ } else if (D.softlimit==0 && D.hardlimit==0) {
+ return False;
+ } else {
+ if (D.softlimit == 0)
+ D.softlimit = D.hardlimit;
+ *dfree = D.softlimit - D.curblocks;
+ *dsize = D.softlimit;
+ }
+
+ return (True);
+}
+#endif /* HAVE_SYS_QUOTAS */
diff --git a/source/smbd/reply.c b/source/smbd/reply.c
new file mode 100644
index 00000000000..ac239c7e042
--- /dev/null
+++ b/source/smbd/reply.c
@@ -0,0 +1,4974 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main SMB reply routines
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Andrew Bartlett 2001
+ Copyright (C) Jeremy Allison 1992-2004.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+/*
+ This file handles most of the reply_ calls that the server
+ makes to handle specific protocols
+*/
+
+#include "includes.h"
+
+/* look in server.c for some explanation of these variables */
+extern int Protocol;
+extern int max_send;
+extern int max_recv;
+extern char magic_char;
+extern BOOL case_sensitive;
+extern BOOL case_preserve;
+extern BOOL short_case_preserve;
+extern int global_oplock_break;
+unsigned int smb_echo_count = 0;
+
+extern BOOL global_encrypted_passwords_negotiated;
+
+/****************************************************************************
+ Ensure we check the path in *exactly* the same way as W2K.
+ We're assuming here that '/' is not the second byte in any multibyte char
+ set (a safe assumption). '\\' *may* be the second byte in a multibyte char
+ set.
+****************************************************************************/
+
+NTSTATUS check_path_syntax(pstring destname, const pstring srcname)
+{
+ char *d = destname;
+ const char *s = srcname;
+ NTSTATUS ret = NT_STATUS_OK;
+
+ while (*s) {
+ if (IS_DIRECTORY_SEP(*s)) {
+ /*
+ * Safe to assume is not the second part of a mb char as this is handled below.
+ */
+ /* Eat multiple '/' or '\\' */
+ while (IS_DIRECTORY_SEP(*s)) {
+ s++;
+ }
+ if ((s[0] == '.') && (s[1] == '\0')) {
+ ret = NT_STATUS_OBJECT_NAME_INVALID;
+ break;
+ }
+ if ((d != destname) && (*s != '\0')) {
+ /* We only care about non-leading or trailing '/' or '\\' */
+ *d++ = '/';
+ }
+ } else if ((s[0] == '.') && (s[1] == '.') && (IS_DIRECTORY_SEP(s[2]) || s[2] == '\0')) {
+ /* Uh oh - "../" or "..\\" or "..\0" ! */
+
+ /*
+ * No mb char starts with '.' so we're safe checking the directory separator here.
+ */
+
+ /* If we just added a '/', delete it. */
+
+ if ((d > destname) && (*(d-1) == '/')) {
+ *(d-1) = '\0';
+ if (d == (destname + 1)) {
+ d--;
+ } else {
+ d -= 2;
+ }
+ }
+ /* Are we at the start ? Can't go back further if so. */
+ if (d == destname) {
+ ret = NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
+ break;
+ }
+ /* Go back one level... */
+ /* We know this is safe as '/' cannot be part of a mb sequence. */
+ /* NOTE - if this assumption is invalid we are not in good shape... */
+ while (d > destname) {
+ if (*d == '/')
+ break;
+ d--;
+ }
+ s += 3;
+ } else if ((s[0] == '.') && (IS_DIRECTORY_SEP(s[1]) || (s[1] == '\0'))) {
+
+ /*
+ * No mb char starts with '.' so we're safe checking the directory separator here.
+ */
+
+ /* "./" or ".\\" fails with a different error depending on where it is... */
+
+ if (s == srcname) {
+ ret = NT_STATUS_OBJECT_NAME_INVALID;
+ break;
+ } else {
+ if (s[1] != '\0' && s[2] == '\0') {
+ ret = NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+ ret = NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ break;
+ }
+ s++;
+ } else {
+ if (!(*s & 0x80)) {
+ *d++ = *s++;
+ } else {
+ switch(next_mb_char_size(s)) {
+ case 4:
+ *d++ = *s++;
+ case 3:
+ *d++ = *s++;
+ case 2:
+ *d++ = *s++;
+ case 1:
+ *d++ = *s++;
+ break;
+ default:
+ DEBUG(0,("check_path_syntax: character length assumptions invalid !\n"));
+ *d = '\0';
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+ }
+ }
+ *d = '\0';
+ return ret;
+}
+
+/****************************************************************************
+ Pull a string and check the path - provide for error return.
+****************************************************************************/
+
+size_t srvstr_get_path(char *inbuf, char *dest, const char *src, size_t dest_len, size_t src_len, int flags, NTSTATUS *err)
+{
+ pstring tmppath;
+ char *tmppath_ptr = tmppath;
+ size_t ret;
+#ifdef DEVELOPER
+ SMB_ASSERT(dest_len == sizeof(pstring));
+#endif
+
+ if (src_len == 0) {
+ ret = srvstr_pull_buf( inbuf, tmppath_ptr, src, dest_len, flags);
+ } else {
+ ret = srvstr_pull( inbuf, tmppath_ptr, src, dest_len, src_len, flags);
+ }
+ *err = check_path_syntax(dest, tmppath);
+ return ret;
+}
+
+/****************************************************************************
+ Reply to a special message.
+****************************************************************************/
+
+int reply_special(char *inbuf,char *outbuf)
+{
+ int outsize = 4;
+ int msg_type = CVAL(inbuf,0);
+ int msg_flags = CVAL(inbuf,1);
+ fstring name1,name2;
+ char name_type = 0;
+
+ static BOOL already_got_session = False;
+
+ *name1 = *name2 = 0;
+
+ memset(outbuf,'\0',smb_size);
+
+ smb_setlen(outbuf,0);
+
+ switch (msg_type) {
+ case 0x81: /* session request */
+
+ if (already_got_session) {
+ exit_server("multiple session request not permitted");
+ }
+
+ SCVAL(outbuf,0,0x82);
+ SCVAL(outbuf,3,0);
+ if (name_len(inbuf+4) > 50 ||
+ name_len(inbuf+4 + name_len(inbuf + 4)) > 50) {
+ DEBUG(0,("Invalid name length in session request\n"));
+ return(0);
+ }
+ name_extract(inbuf,4,name1);
+ name_type = name_extract(inbuf,4 + name_len(inbuf + 4),name2);
+ DEBUG(2,("netbios connect: name1=%s name2=%s\n",
+ name1,name2));
+
+ set_local_machine_name(name1, True);
+ set_remote_machine_name(name2, True);
+
+ DEBUG(2,("netbios connect: local=%s remote=%s, name type = %x\n",
+ get_local_machine_name(), get_remote_machine_name(),
+ name_type));
+
+ if (name_type == 'R') {
+ /* We are being asked for a pathworks session ---
+ no thanks! */
+ SCVAL(outbuf, 0,0x83);
+ break;
+ }
+
+ /* only add the client's machine name to the list
+ of possibly valid usernames if we are operating
+ in share mode security */
+ if (lp_security() == SEC_SHARE) {
+ add_session_user(get_remote_machine_name());
+ }
+
+ reload_services(True);
+ reopen_logs();
+
+ claim_connection(NULL,"",0,True,FLAG_MSG_GENERAL|FLAG_MSG_SMBD);
+
+ already_got_session = True;
+ break;
+
+ case 0x89: /* session keepalive request
+ (some old clients produce this?) */
+ SCVAL(outbuf,0,SMBkeepalive);
+ SCVAL(outbuf,3,0);
+ break;
+
+ case 0x82: /* positive session response */
+ case 0x83: /* negative session response */
+ case 0x84: /* retarget session response */
+ DEBUG(0,("Unexpected session response\n"));
+ break;
+
+ case SMBkeepalive: /* session keepalive */
+ default:
+ return(0);
+ }
+
+ DEBUG(5,("init msg_type=0x%x msg_flags=0x%x\n",
+ msg_type, msg_flags));
+
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a tcon.
+****************************************************************************/
+
+int reply_tcon(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ const char *service;
+ pstring service_buf;
+ pstring password;
+ pstring dev;
+ int outsize = 0;
+ uint16 vuid = SVAL(inbuf,smb_uid);
+ int pwlen=0;
+ NTSTATUS nt_status;
+ char *p;
+ DATA_BLOB password_blob;
+
+ START_PROFILE(SMBtcon);
+
+ *service_buf = *password = *dev = 0;
+
+ p = smb_buf(inbuf)+1;
+ p += srvstr_pull_buf(inbuf, service_buf, p, sizeof(service_buf), STR_TERMINATE) + 1;
+ pwlen = srvstr_pull_buf(inbuf, password, p, sizeof(password), STR_TERMINATE) + 1;
+ p += pwlen;
+ p += srvstr_pull_buf(inbuf, dev, p, sizeof(dev), STR_TERMINATE) + 1;
+
+ p = strrchr_m(service_buf,'\\');
+ if (p) {
+ service = p+1;
+ } else {
+ service = service_buf;
+ }
+
+ password_blob = data_blob(password, pwlen+1);
+
+ conn = make_connection(service,password_blob,dev,vuid,&nt_status);
+
+ data_blob_clear_free(&password_blob);
+
+ if (!conn) {
+ END_PROFILE(SMBtcon);
+ return ERROR_NT(nt_status);
+ }
+
+ outsize = set_message(outbuf,2,0,True);
+ SSVAL(outbuf,smb_vwv0,max_recv);
+ SSVAL(outbuf,smb_vwv1,conn->cnum);
+ SSVAL(outbuf,smb_tid,conn->cnum);
+
+ DEBUG(3,("tcon service=%s cnum=%d\n",
+ service, conn->cnum));
+
+ END_PROFILE(SMBtcon);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a tcon and X.
+****************************************************************************/
+
+int reply_tcon_and_X(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize)
+{
+ fstring service;
+ DATA_BLOB password;
+
+ /* what the cleint thinks the device is */
+ fstring client_devicetype;
+ /* what the server tells the client the share represents */
+ const char *server_devicetype;
+ NTSTATUS nt_status;
+ uint16 vuid = SVAL(inbuf,smb_uid);
+ int passlen = SVAL(inbuf,smb_vwv3);
+ pstring path;
+ char *p, *q;
+ extern BOOL global_encrypted_passwords_negotiated;
+
+ START_PROFILE(SMBtconX);
+
+ *service = *client_devicetype = 0;
+
+ /* we might have to close an old one */
+ if ((SVAL(inbuf,smb_vwv2) & 0x1) && conn) {
+ close_cnum(conn,vuid);
+ }
+
+ if (passlen > MAX_PASS_LEN) {
+ return ERROR_DOS(ERRDOS,ERRbuftoosmall);
+ }
+
+ if (global_encrypted_passwords_negotiated) {
+ password = data_blob(smb_buf(inbuf),passlen);
+ } else {
+ password = data_blob(smb_buf(inbuf),passlen+1);
+ /* Ensure correct termination */
+ password.data[passlen]=0;
+ }
+
+ p = smb_buf(inbuf) + passlen;
+ p += srvstr_pull_buf(inbuf, path, p, sizeof(path), STR_TERMINATE);
+
+ /*
+ * the service name can be either: \\server\share
+ * or share directly like on the DELL PowerVault 705
+ */
+ if (*path=='\\') {
+ q = strchr_m(path+2,'\\');
+ if (!q) {
+ END_PROFILE(SMBtconX);
+ return(ERROR_DOS(ERRDOS,ERRnosuchshare));
+ }
+ fstrcpy(service,q+1);
+ }
+ else
+ fstrcpy(service,path);
+
+ p += srvstr_pull(inbuf, client_devicetype, p, sizeof(client_devicetype), 6, STR_ASCII);
+
+ DEBUG(4,("Client requested device type [%s] for share [%s]\n", client_devicetype, service));
+
+ conn = make_connection(service,password,client_devicetype,vuid,&nt_status);
+
+ data_blob_clear_free(&password);
+
+ if (!conn) {
+ END_PROFILE(SMBtconX);
+ return ERROR_NT(nt_status);
+ }
+
+ if ( IS_IPC(conn) )
+ server_devicetype = "IPC";
+ else if ( IS_PRINT(conn) )
+ server_devicetype = "LPT1:";
+ else
+ server_devicetype = "A:";
+
+ if (Protocol < PROTOCOL_NT1) {
+ set_message(outbuf,2,0,True);
+ p = smb_buf(outbuf);
+ p += srvstr_push(outbuf, p, server_devicetype, -1,
+ STR_TERMINATE|STR_ASCII);
+ set_message_end(outbuf,p);
+ } else {
+ /* NT sets the fstype of IPC$ to the null string */
+ const char *fstype = IS_IPC(conn) ? "" : lp_fstype(SNUM(conn));
+
+ set_message(outbuf,3,0,True);
+
+ p = smb_buf(outbuf);
+ p += srvstr_push(outbuf, p, server_devicetype, -1,
+ STR_TERMINATE|STR_ASCII);
+ p += srvstr_push(outbuf, p, fstype, -1,
+ STR_TERMINATE);
+
+ set_message_end(outbuf,p);
+
+ /* what does setting this bit do? It is set by NT4 and
+ may affect the ability to autorun mounted cdroms */
+ SSVAL(outbuf, smb_vwv2, SMB_SUPPORT_SEARCH_BITS|
+ (lp_csc_policy(SNUM(conn)) << 2));
+
+ init_dfsroot(conn, inbuf, outbuf);
+ }
+
+
+ DEBUG(3,("tconX service=%s \n",
+ service));
+
+ /* set the incoming and outgoing tid to the just created one */
+ SSVAL(inbuf,smb_tid,conn->cnum);
+ SSVAL(outbuf,smb_tid,conn->cnum);
+
+ END_PROFILE(SMBtconX);
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
+
+/****************************************************************************
+ Reply to an unknown type.
+****************************************************************************/
+
+int reply_unknown(char *inbuf,char *outbuf)
+{
+ int type;
+ type = CVAL(inbuf,smb_com);
+
+ DEBUG(0,("unknown command type (%s): type=%d (0x%X)\n",
+ smb_fn_name(type), type, type));
+
+ return(ERROR_DOS(ERRSRV,ERRunknownsmb));
+}
+
+/****************************************************************************
+ Reply to an ioctl.
+****************************************************************************/
+
+int reply_ioctl(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ uint16 device = SVAL(inbuf,smb_vwv1);
+ uint16 function = SVAL(inbuf,smb_vwv2);
+ uint32 ioctl_code = (device << 16) + function;
+ int replysize, outsize;
+ char *p;
+ START_PROFILE(SMBioctl);
+
+ DEBUG(4, ("Received IOCTL (code 0x%x)\n", ioctl_code));
+
+ switch (ioctl_code) {
+ case IOCTL_QUERY_JOB_INFO:
+ replysize = 32;
+ break;
+ default:
+ END_PROFILE(SMBioctl);
+ return(ERROR_DOS(ERRSRV,ERRnosupport));
+ }
+
+ outsize = set_message(outbuf,8,replysize+1,True);
+ SSVAL(outbuf,smb_vwv1,replysize); /* Total data bytes returned */
+ SSVAL(outbuf,smb_vwv5,replysize); /* Data bytes this buffer */
+ SSVAL(outbuf,smb_vwv6,52); /* Offset to data */
+ p = smb_buf(outbuf) + 1; /* Allow for alignment */
+
+ switch (ioctl_code) {
+ case IOCTL_QUERY_JOB_INFO:
+ {
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ if (!fsp) {
+ END_PROFILE(SMBioctl);
+ return(UNIXERROR(ERRDOS,ERRbadfid));
+ }
+ SSVAL(p,0,fsp->rap_print_jobid); /* Job number */
+ srvstr_push(outbuf, p+2, global_myname(), 15, STR_TERMINATE|STR_ASCII);
+ srvstr_push(outbuf, p+18, lp_servicename(SNUM(conn)), 13, STR_TERMINATE|STR_ASCII);
+ break;
+ }
+ }
+
+ END_PROFILE(SMBioctl);
+ return outsize;
+}
+
+/****************************************************************************
+ Reply to a chkpth.
+****************************************************************************/
+
+int reply_chkpth(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int outsize = 0;
+ int mode;
+ pstring name;
+ BOOL ok = False;
+ BOOL bad_path = False;
+ SMB_STRUCT_STAT sbuf;
+ NTSTATUS status;
+
+ START_PROFILE(SMBchkpth);
+
+ srvstr_get_path(inbuf, name, smb_buf(inbuf) + 1, sizeof(name), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBchkpth);
+ return ERROR_NT(status);
+ }
+
+ RESOLVE_DFSPATH(name, conn, inbuf, outbuf);
+
+ unix_convert(name,conn,0,&bad_path,&sbuf);
+
+ mode = SVAL(inbuf,smb_vwv0);
+
+ if (check_name(name,conn)) {
+ if (VALID_STAT(sbuf) || SMB_VFS_STAT(conn,name,&sbuf) == 0)
+ if (!(ok = S_ISDIR(sbuf.st_mode))) {
+ END_PROFILE(SMBchkpth);
+ return ERROR_BOTH(NT_STATUS_NOT_A_DIRECTORY,ERRDOS,ERRbadpath);
+ }
+ }
+
+ if (!ok) {
+ /* We special case this - as when a Windows machine
+ is parsing a path is steps through the components
+ one at a time - if a component fails it expects
+ ERRbadpath, not ERRbadfile.
+ */
+ if(errno == ENOENT) {
+ /*
+ * Windows returns different error codes if
+ * the parent directory is valid but not the
+ * last component - it returns NT_STATUS_OBJECT_NAME_NOT_FOUND
+ * for that case and NT_STATUS_OBJECT_PATH_NOT_FOUND
+ * if the path is invalid.
+ */
+ if (bad_path) {
+ END_PROFILE(SMBchkpth);
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ } else {
+ END_PROFILE(SMBchkpth);
+ return ERROR_NT(NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ }
+ } else if (errno == ENOTDIR) {
+ END_PROFILE(SMBchkpth);
+ return ERROR_NT(NT_STATUS_NOT_A_DIRECTORY);
+ }
+
+ END_PROFILE(SMBchkpth);
+ return(UNIXERROR(ERRDOS,ERRbadpath));
+ }
+
+ outsize = set_message(outbuf,0,0,True);
+
+ DEBUG(3,("chkpth %s mode=%d\n", name, mode));
+
+ END_PROFILE(SMBchkpth);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a getatr.
+****************************************************************************/
+
+int reply_getatr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ pstring fname;
+ int outsize = 0;
+ SMB_STRUCT_STAT sbuf;
+ BOOL ok = False;
+ int mode=0;
+ SMB_OFF_T size=0;
+ time_t mtime=0;
+ BOOL bad_path = False;
+ char *p;
+ NTSTATUS status;
+
+ START_PROFILE(SMBgetatr);
+
+ p = smb_buf(inbuf) + 1;
+ p += srvstr_get_path(inbuf, fname, p, sizeof(fname), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBgetatr);
+ return ERROR_NT(status);
+ }
+
+ RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
+
+ /* dos smetimes asks for a stat of "" - it returns a "hidden directory"
+ under WfWg - weird! */
+ if (! (*fname)) {
+ mode = aHIDDEN | aDIR;
+ if (!CAN_WRITE(conn))
+ mode |= aRONLY;
+ size = 0;
+ mtime = 0;
+ ok = True;
+ } else {
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+ if (check_name(fname,conn)) {
+ if (VALID_STAT(sbuf) || SMB_VFS_STAT(conn,fname,&sbuf) == 0) {
+ mode = dos_mode(conn,fname,&sbuf);
+ size = sbuf.st_size;
+ mtime = sbuf.st_mtime;
+ if (mode & aDIR)
+ size = 0;
+ ok = True;
+ } else {
+ DEBUG(3,("stat of %s failed (%s)\n",fname,strerror(errno)));
+ }
+ }
+ }
+
+ if (!ok) {
+ END_PROFILE(SMBgetatr);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadfile);
+ }
+
+ outsize = set_message(outbuf,10,0,True);
+
+ SSVAL(outbuf,smb_vwv0,mode);
+ if(lp_dos_filetime_resolution(SNUM(conn)) )
+ put_dos_date3(outbuf,smb_vwv1,mtime & ~1);
+ else
+ put_dos_date3(outbuf,smb_vwv1,mtime);
+ SIVAL(outbuf,smb_vwv3,(uint32)size);
+
+ if (Protocol >= PROTOCOL_NT1)
+ SSVAL(outbuf,smb_flg2,SVAL(outbuf, smb_flg2) | FLAGS2_IS_LONG_NAME);
+
+ DEBUG( 3, ( "getatr name=%s mode=%d size=%d\n", fname, mode, (uint32)size ) );
+
+ END_PROFILE(SMBgetatr);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a setatr.
+****************************************************************************/
+
+int reply_setatr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ pstring fname;
+ int outsize = 0;
+ BOOL ok=False;
+ int mode;
+ time_t mtime;
+ SMB_STRUCT_STAT sbuf;
+ BOOL bad_path = False;
+ char *p;
+ NTSTATUS status;
+
+ START_PROFILE(SMBsetatr);
+
+ p = smb_buf(inbuf) + 1;
+ p += srvstr_get_path(inbuf, fname, p, sizeof(fname), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBsetatr);
+ return ERROR_NT(status);
+ }
+
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+
+ mode = SVAL(inbuf,smb_vwv0);
+ mtime = make_unix_date3(inbuf+smb_vwv1);
+
+ if (mode != FILE_ATTRIBUTE_NORMAL) {
+ if (VALID_STAT_OF_DIR(sbuf))
+ mode |= aDIR;
+ else
+ mode &= ~aDIR;
+
+ if (check_name(fname,conn)) {
+ ok = (file_set_dosmode(conn,fname,mode,NULL) == 0);
+ }
+ } else {
+ ok = True;
+ }
+
+ if (ok)
+ ok = set_filetime(conn,fname,mtime);
+
+ if (!ok) {
+ END_PROFILE(SMBsetatr);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess);
+ }
+
+ outsize = set_message(outbuf,0,0,True);
+
+ DEBUG( 3, ( "setatr name=%s mode=%d\n", fname, mode ) );
+
+ END_PROFILE(SMBsetatr);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a dskattr.
+****************************************************************************/
+
+int reply_dskattr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int outsize = 0;
+ SMB_BIG_UINT dfree,dsize,bsize;
+ START_PROFILE(SMBdskattr);
+
+ SMB_VFS_DISK_FREE(conn,".",True,&bsize,&dfree,&dsize);
+
+ outsize = set_message(outbuf,5,0,True);
+
+ if (Protocol <= PROTOCOL_LANMAN2) {
+ double total_space, free_space;
+ /* we need to scale this to a number that DOS6 can handle. We
+ use floating point so we can handle large drives on systems
+ that don't have 64 bit integers
+
+ we end up displaying a maximum of 2G to DOS systems
+ */
+ total_space = dsize * (double)bsize;
+ free_space = dfree * (double)bsize;
+
+ dsize = (total_space+63*512) / (64*512);
+ dfree = (free_space+63*512) / (64*512);
+
+ if (dsize > 0xFFFF) dsize = 0xFFFF;
+ if (dfree > 0xFFFF) dfree = 0xFFFF;
+
+ SSVAL(outbuf,smb_vwv0,dsize);
+ SSVAL(outbuf,smb_vwv1,64); /* this must be 64 for dos systems */
+ SSVAL(outbuf,smb_vwv2,512); /* and this must be 512 */
+ SSVAL(outbuf,smb_vwv3,dfree);
+ } else {
+ SSVAL(outbuf,smb_vwv0,dsize);
+ SSVAL(outbuf,smb_vwv1,bsize/512);
+ SSVAL(outbuf,smb_vwv2,512);
+ SSVAL(outbuf,smb_vwv3,dfree);
+ }
+
+ DEBUG(3,("dskattr dfree=%d\n", (unsigned int)dfree));
+
+ END_PROFILE(SMBdskattr);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a search.
+ Can be called from SMBsearch, SMBffirst or SMBfunique.
+****************************************************************************/
+
+int reply_search(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ pstring mask;
+ pstring directory;
+ pstring fname;
+ SMB_OFF_T size;
+ int mode;
+ time_t date;
+ int dirtype;
+ int outsize = 0;
+ unsigned int numentries = 0;
+ unsigned int maxentries = 0;
+ BOOL finished = False;
+ char *p;
+ BOOL ok = False;
+ int status_len;
+ pstring path;
+ char status[21];
+ int dptr_num= -1;
+ BOOL check_descend = False;
+ BOOL expect_close = False;
+ BOOL can_open = True;
+ BOOL bad_path = False;
+ NTSTATUS nt_status;
+ START_PROFILE(SMBsearch);
+
+ *mask = *directory = *fname = 0;
+
+ /* If we were called as SMBffirst then we must expect close. */
+ if(CVAL(inbuf,smb_com) == SMBffirst)
+ expect_close = True;
+
+ outsize = set_message(outbuf,1,3,True);
+ maxentries = SVAL(inbuf,smb_vwv0);
+ dirtype = SVAL(inbuf,smb_vwv1);
+ p = smb_buf(inbuf) + 1;
+ p += srvstr_get_path(inbuf, path, p, sizeof(path), 0, STR_TERMINATE, &nt_status);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ END_PROFILE(SMBsearch);
+ return ERROR_NT(nt_status);
+ }
+ p++;
+ status_len = SVAL(p, 0);
+ p += 2;
+
+ /* dirtype &= ~aDIR; */
+
+ if (status_len == 0) {
+ SMB_STRUCT_STAT sbuf;
+ pstring dir2;
+
+ pstrcpy(directory,path);
+ pstrcpy(dir2,path);
+ unix_convert(directory,conn,0,&bad_path,&sbuf);
+ unix_format(dir2);
+
+ if (!check_name(directory,conn))
+ can_open = False;
+
+ p = strrchr_m(dir2,'/');
+ if (p == NULL) {
+ pstrcpy(mask,dir2);
+ *dir2 = 0;
+ } else {
+ *p = 0;
+ pstrcpy(mask,p+1);
+ }
+
+ p = strrchr_m(directory,'/');
+ if (!p)
+ *directory = 0;
+ else
+ *p = 0;
+
+ if (strlen(directory) == 0)
+ pstrcpy(directory,".");
+ memset((char *)status,'\0',21);
+ SCVAL(status,0,(dirtype & 0x1F));
+ } else {
+ int status_dirtype;
+
+ memcpy(status,p,21);
+ status_dirtype = CVAL(status,0) & 0x1F;
+ if (status_dirtype != (dirtype & 0x1F))
+ dirtype = status_dirtype;
+
+ conn->dirptr = dptr_fetch(status+12,&dptr_num);
+ if (!conn->dirptr)
+ goto SearchEmpty;
+ string_set(&conn->dirpath,dptr_path(dptr_num));
+ pstrcpy(mask, dptr_wcard(dptr_num));
+ }
+
+ if (can_open) {
+ p = smb_buf(outbuf) + 3;
+ ok = True;
+
+ if (status_len == 0) {
+ dptr_num = dptr_create(conn,directory,True,expect_close,SVAL(inbuf,smb_pid));
+ if (dptr_num < 0) {
+ if(dptr_num == -2) {
+ END_PROFILE(SMBsearch);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnofids);
+ }
+ END_PROFILE(SMBsearch);
+ return ERROR_DOS(ERRDOS,ERRnofids);
+ }
+ dptr_set_wcard(dptr_num, strdup(mask));
+ dptr_set_attr(dptr_num, dirtype);
+ } else {
+ dirtype = dptr_attr(dptr_num);
+ }
+
+ DEBUG(4,("dptr_num is %d\n",dptr_num));
+
+ if (ok) {
+ if ((dirtype&0x1F) == aVOLID) {
+ memcpy(p,status,21);
+ make_dir_struct(p,"???????????",volume_label(SNUM(conn)),0,aVOLID,0);
+ dptr_fill(p+12,dptr_num);
+ if (dptr_zero(p+12) && (status_len==0))
+ numentries = 1;
+ else
+ numentries = 0;
+ p += DIR_STRUCT_SIZE;
+ } else {
+ unsigned int i;
+ maxentries = MIN(maxentries, ((BUFFER_SIZE - (p - outbuf))/DIR_STRUCT_SIZE));
+
+ DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",
+ conn->dirpath,lp_dontdescend(SNUM(conn))));
+ if (in_list(conn->dirpath, lp_dontdescend(SNUM(conn)),True))
+ check_descend = True;
+
+ for (i=numentries;(i<maxentries) && !finished;i++) {
+ finished = !get_dir_entry(conn,mask,dirtype,fname,&size,&mode,&date,check_descend);
+ if (!finished) {
+ memcpy(p,status,21);
+ make_dir_struct(p,mask,fname,size,mode,date);
+ dptr_fill(p+12,dptr_num);
+ numentries++;
+ }
+ p += DIR_STRUCT_SIZE;
+ }
+ }
+ } /* if (ok ) */
+ }
+
+
+ SearchEmpty:
+
+ /* If we were called as SMBffirst with smb_search_id == NULL
+ and no entries were found then return error and close dirptr
+ (X/Open spec) */
+
+ if(ok && expect_close && numentries == 0 && status_len == 0) {
+ if (Protocol < PROTOCOL_NT1) {
+ SCVAL(outbuf,smb_rcls,ERRDOS);
+ SSVAL(outbuf,smb_err,ERRnofiles);
+ }
+ /* Also close the dptr - we know it's gone */
+ dptr_close(&dptr_num);
+ } else if (numentries == 0 || !ok) {
+ if (Protocol < PROTOCOL_NT1) {
+ SCVAL(outbuf,smb_rcls,ERRDOS);
+ SSVAL(outbuf,smb_err,ERRnofiles);
+ }
+ dptr_close(&dptr_num);
+ }
+
+ /* If we were called as SMBfunique, then we can close the dirptr now ! */
+ if(dptr_num >= 0 && CVAL(inbuf,smb_com) == SMBfunique)
+ dptr_close(&dptr_num);
+
+ SSVAL(outbuf,smb_vwv0,numentries);
+ SSVAL(outbuf,smb_vwv1,3 + numentries * DIR_STRUCT_SIZE);
+ SCVAL(smb_buf(outbuf),0,5);
+ SSVAL(smb_buf(outbuf),1,numentries*DIR_STRUCT_SIZE);
+
+ if (Protocol >= PROTOCOL_NT1)
+ SSVAL(outbuf,smb_flg2,SVAL(outbuf, smb_flg2) | FLAGS2_IS_LONG_NAME);
+
+ outsize += DIR_STRUCT_SIZE*numentries;
+ smb_setlen(outbuf,outsize - 4);
+
+ if ((! *directory) && dptr_path(dptr_num))
+ slprintf(directory, sizeof(directory)-1, "(%s)",dptr_path(dptr_num));
+
+ DEBUG( 4, ( "%s mask=%s path=%s dtype=%d nument=%u of %u\n",
+ smb_fn_name(CVAL(inbuf,smb_com)),
+ mask, directory, dirtype, numentries, maxentries ) );
+
+ END_PROFILE(SMBsearch);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a fclose (stop directory search).
+****************************************************************************/
+
+int reply_fclose(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int outsize = 0;
+ int status_len;
+ pstring path;
+ char status[21];
+ int dptr_num= -2;
+ char *p;
+ NTSTATUS err;
+
+ START_PROFILE(SMBfclose);
+
+ outsize = set_message(outbuf,1,0,True);
+ p = smb_buf(inbuf) + 1;
+ p += srvstr_get_path(inbuf, path, p, sizeof(path), 0, STR_TERMINATE, &err);
+ if (!NT_STATUS_IS_OK(err)) {
+ END_PROFILE(SMBfclose);
+ return ERROR_NT(err);
+ }
+ p++;
+ status_len = SVAL(p,0);
+ p += 2;
+
+ if (status_len == 0) {
+ END_PROFILE(SMBfclose);
+ return ERROR_DOS(ERRSRV,ERRsrverror);
+ }
+
+ memcpy(status,p,21);
+
+ if(dptr_fetch(status+12,&dptr_num)) {
+ /* Close the dptr - we know it's gone */
+ dptr_close(&dptr_num);
+ }
+
+ SSVAL(outbuf,smb_vwv0,0);
+
+ DEBUG(3,("search close\n"));
+
+ END_PROFILE(SMBfclose);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to an open.
+****************************************************************************/
+
+int reply_open(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ pstring fname;
+ int outsize = 0;
+ int fmode=0;
+ int share_mode;
+ SMB_OFF_T size = 0;
+ time_t mtime=0;
+ int rmode=0;
+ SMB_STRUCT_STAT sbuf;
+ BOOL bad_path = False;
+ files_struct *fsp;
+ int oplock_request = CORE_OPLOCK_REQUEST(inbuf);
+ uint16 dos_attr = SVAL(inbuf,smb_vwv1);
+ NTSTATUS status;
+ START_PROFILE(SMBopen);
+
+ share_mode = SVAL(inbuf,smb_vwv0);
+
+ srvstr_get_path(inbuf, fname, smb_buf(inbuf)+1, sizeof(fname), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBopen);
+ return ERROR_NT(status);
+ }
+
+ RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
+
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+
+ fsp = open_file_shared(conn,fname,&sbuf,share_mode,(FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
+ (uint32)dos_attr, oplock_request,&rmode,NULL);
+
+ if (!fsp) {
+ END_PROFILE(SMBopen);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess);
+ }
+
+ size = sbuf.st_size;
+ fmode = dos_mode(conn,fname,&sbuf);
+ mtime = sbuf.st_mtime;
+
+ if (fmode & aDIR) {
+ DEBUG(3,("attempt to open a directory %s\n",fname));
+ close_file(fsp,False);
+ END_PROFILE(SMBopen);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ outsize = set_message(outbuf,7,0,True);
+ SSVAL(outbuf,smb_vwv0,fsp->fnum);
+ SSVAL(outbuf,smb_vwv1,fmode);
+ if(lp_dos_filetime_resolution(SNUM(conn)) )
+ put_dos_date3(outbuf,smb_vwv2,mtime & ~1);
+ else
+ put_dos_date3(outbuf,smb_vwv2,mtime);
+ SIVAL(outbuf,smb_vwv4,(uint32)size);
+ SSVAL(outbuf,smb_vwv6,rmode);
+
+ if (oplock_request && lp_fake_oplocks(SNUM(conn)))
+ SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED);
+
+ if(EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type))
+ SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED);
+ END_PROFILE(SMBopen);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to an open and X.
+****************************************************************************/
+
+int reply_open_and_X(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize)
+{
+ pstring fname;
+ int smb_mode = SVAL(inbuf,smb_vwv3);
+ int smb_attr = SVAL(inbuf,smb_vwv5);
+ /* Breakout the oplock request bits so we can set the
+ reply bits separately. */
+ BOOL ex_oplock_request = EXTENDED_OPLOCK_REQUEST(inbuf);
+ BOOL core_oplock_request = CORE_OPLOCK_REQUEST(inbuf);
+ BOOL oplock_request = ex_oplock_request | core_oplock_request;
+#if 0
+ int open_flags = SVAL(inbuf,smb_vwv2);
+ int smb_sattr = SVAL(inbuf,smb_vwv4);
+ uint32 smb_time = make_unix_date3(inbuf+smb_vwv6);
+#endif
+ int smb_ofun = SVAL(inbuf,smb_vwv8);
+ SMB_OFF_T size=0;
+ int fmode=0,mtime=0,rmode=0;
+ SMB_STRUCT_STAT sbuf;
+ int smb_action = 0;
+ BOOL bad_path = False;
+ files_struct *fsp;
+ NTSTATUS status;
+ START_PROFILE(SMBopenX);
+
+ /* If it's an IPC, pass off the pipe handler. */
+ if (IS_IPC(conn)) {
+ if (lp_nt_pipe_support()) {
+ END_PROFILE(SMBopenX);
+ return reply_open_pipe_and_X(conn, inbuf,outbuf,length,bufsize);
+ } else {
+ END_PROFILE(SMBopenX);
+ return ERROR_DOS(ERRSRV,ERRaccess);
+ }
+ }
+
+ /* XXXX we need to handle passed times, sattr and flags */
+ srvstr_get_path(inbuf, fname, smb_buf(inbuf), sizeof(fname), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBopenX);
+ return ERROR_NT(status);
+ }
+
+ RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
+
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+
+ fsp = open_file_shared(conn,fname,&sbuf,smb_mode,smb_ofun,(uint32)smb_attr,
+ oplock_request, &rmode,&smb_action);
+
+ if (!fsp) {
+ END_PROFILE(SMBopenX);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess);
+ }
+
+ size = sbuf.st_size;
+ fmode = dos_mode(conn,fname,&sbuf);
+ mtime = sbuf.st_mtime;
+ if (fmode & aDIR) {
+ close_file(fsp,False);
+ END_PROFILE(SMBopenX);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ /* If the caller set the extended oplock request bit
+ and we granted one (by whatever means) - set the
+ correct bit for extended oplock reply.
+ */
+
+ if (ex_oplock_request && lp_fake_oplocks(SNUM(conn)))
+ smb_action |= EXTENDED_OPLOCK_GRANTED;
+
+ if(ex_oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type))
+ smb_action |= EXTENDED_OPLOCK_GRANTED;
+
+ /* If the caller set the core oplock request bit
+ and we granted one (by whatever means) - set the
+ correct bit for core oplock reply.
+ */
+
+ if (core_oplock_request && lp_fake_oplocks(SNUM(conn)))
+ SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED);
+
+ if(core_oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type))
+ SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED);
+
+ set_message(outbuf,15,0,True);
+ SSVAL(outbuf,smb_vwv2,fsp->fnum);
+ SSVAL(outbuf,smb_vwv3,fmode);
+ if(lp_dos_filetime_resolution(SNUM(conn)) )
+ put_dos_date3(outbuf,smb_vwv4,mtime & ~1);
+ else
+ put_dos_date3(outbuf,smb_vwv4,mtime);
+ SIVAL(outbuf,smb_vwv6,(uint32)size);
+ SSVAL(outbuf,smb_vwv8,rmode);
+ SSVAL(outbuf,smb_vwv11,smb_action);
+
+ END_PROFILE(SMBopenX);
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
+
+/****************************************************************************
+ Reply to a SMBulogoffX.
+****************************************************************************/
+
+int reply_ulogoffX(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize)
+{
+ uint16 vuid = SVAL(inbuf,smb_uid);
+ user_struct *vuser = get_valid_user_struct(vuid);
+ START_PROFILE(SMBulogoffX);
+
+ if(vuser == 0)
+ DEBUG(3,("ulogoff, vuser id %d does not map to user.\n", vuid));
+
+ /* in user level security we are supposed to close any files
+ open by this user */
+ if ((vuser != 0) && (lp_security() != SEC_SHARE))
+ file_close_user(vuid);
+
+ invalidate_vuid(vuid);
+
+ set_message(outbuf,2,0,True);
+
+ DEBUG( 3, ( "ulogoffX vuid=%d\n", vuid ) );
+
+ END_PROFILE(SMBulogoffX);
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
+
+/****************************************************************************
+ Reply to a mknew or a create.
+****************************************************************************/
+
+int reply_mknew(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ pstring fname;
+ int com;
+ int outsize = 0;
+ int createmode;
+ int ofun = 0;
+ BOOL bad_path = False;
+ files_struct *fsp;
+ int oplock_request = CORE_OPLOCK_REQUEST(inbuf);
+ SMB_STRUCT_STAT sbuf;
+ NTSTATUS status;
+ START_PROFILE(SMBcreate);
+
+ com = SVAL(inbuf,smb_com);
+
+ createmode = SVAL(inbuf,smb_vwv0);
+ srvstr_get_path(inbuf, fname, smb_buf(inbuf) + 1, sizeof(fname), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBcreate);
+ return ERROR_NT(status);
+ }
+
+ RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
+
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+
+ if (createmode & aVOLID)
+ DEBUG(0,("Attempt to create file (%s) with volid set - please report this\n",fname));
+
+ if(com == SMBmknew) {
+ /* We should fail if file exists. */
+ ofun = FILE_CREATE_IF_NOT_EXIST;
+ } else {
+ /* SMBcreate - Create if file doesn't exist, truncate if it does. */
+ ofun = FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_TRUNCATE;
+ }
+
+ /* Open file in dos compatibility share mode. */
+ fsp = open_file_shared(conn,fname,&sbuf,SET_DENY_MODE(DENY_FCB)|SET_OPEN_MODE(DOS_OPEN_FCB),
+ ofun, (uint32)createmode, oplock_request, NULL, NULL);
+
+ if (!fsp) {
+ END_PROFILE(SMBcreate);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess);
+ }
+
+ outsize = set_message(outbuf,1,0,True);
+ SSVAL(outbuf,smb_vwv0,fsp->fnum);
+
+ if (oplock_request && lp_fake_oplocks(SNUM(conn)))
+ SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED);
+
+ if(EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type))
+ SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED);
+
+ DEBUG( 2, ( "new file %s\n", fname ) );
+ DEBUG( 3, ( "mknew %s fd=%d dmode=%d\n", fname, fsp->fd, createmode ) );
+
+ END_PROFILE(SMBcreate);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a create temporary file.
+****************************************************************************/
+
+int reply_ctemp(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ pstring fname;
+ int outsize = 0;
+ int createattr;
+ BOOL bad_path = False;
+ files_struct *fsp;
+ int oplock_request = CORE_OPLOCK_REQUEST(inbuf);
+ int tmpfd;
+ SMB_STRUCT_STAT sbuf;
+ char *p, *s;
+ NTSTATUS status;
+ unsigned int namelen;
+
+ START_PROFILE(SMBctemp);
+
+ createattr = SVAL(inbuf,smb_vwv0);
+ srvstr_get_path(inbuf, fname, smb_buf(inbuf)+1, sizeof(fname), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBctemp);
+ return ERROR_NT(status);
+ }
+ if (*fname) {
+ pstrcat(fname,"/TMXXXXXX");
+ } else {
+ pstrcat(fname,"TMXXXXXX");
+ }
+
+ RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
+
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+
+ tmpfd = smb_mkstemp(fname);
+ if (tmpfd == -1) {
+ END_PROFILE(SMBctemp);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ SMB_VFS_STAT(conn,fname,&sbuf);
+
+ /* Open file in dos compatibility share mode. */
+ /* We should fail if file does not exist. */
+ fsp = open_file_shared(conn,fname,&sbuf,
+ SET_DENY_MODE(DENY_FCB)|SET_OPEN_MODE(DOS_OPEN_FCB),
+ FILE_EXISTS_OPEN|FILE_FAIL_IF_NOT_EXIST,
+ (uint32)createattr, oplock_request, NULL, NULL);
+
+ /* close fd from smb_mkstemp() */
+ close(tmpfd);
+
+ if (!fsp) {
+ END_PROFILE(SMBctemp);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess);
+ }
+
+ outsize = set_message(outbuf,1,0,True);
+ SSVAL(outbuf,smb_vwv0,fsp->fnum);
+
+ /* the returned filename is relative to the directory */
+ s = strrchr_m(fname, '/');
+ if (!s)
+ s = fname;
+ else
+ s++;
+
+ p = smb_buf(outbuf);
+#if 0
+ /* Tested vs W2K3 - this doesn't seem to be here - null terminated filename is the only
+ thing in the byte section. JRA */
+ SSVALS(p, 0, -1); /* what is this? not in spec */
+#endif
+ namelen = srvstr_push(outbuf, p, s, -1, STR_ASCII|STR_TERMINATE);
+ p += namelen;
+ outsize = set_message_end(outbuf, p);
+
+ if (oplock_request && lp_fake_oplocks(SNUM(conn)))
+ SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED);
+
+ if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type))
+ SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED);
+
+ DEBUG( 2, ( "created temp file %s\n", fname ) );
+ DEBUG( 3, ( "ctemp %s fd=%d umode=%o\n",
+ fname, fsp->fd, sbuf.st_mode ) );
+
+ END_PROFILE(SMBctemp);
+ return(outsize);
+}
+
+/*******************************************************************
+ Check if a user is allowed to rename a file.
+********************************************************************/
+
+static NTSTATUS can_rename(char *fname,connection_struct *conn, SMB_STRUCT_STAT *pst)
+{
+ int smb_action;
+ int access_mode;
+ files_struct *fsp;
+
+ if (!CAN_WRITE(conn))
+ return NT_STATUS_MEDIA_WRITE_PROTECTED;
+
+ if (S_ISDIR(pst->st_mode))
+ return NT_STATUS_OK;
+
+ /* We need a better way to return NT status codes from open... */
+ unix_ERR_class = 0;
+ unix_ERR_code = 0;
+
+ fsp = open_file_shared1(conn, fname, pst, DELETE_ACCESS, SET_DENY_MODE(DENY_ALL),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &smb_action);
+
+ if (!fsp) {
+ NTSTATUS ret = NT_STATUS_ACCESS_DENIED;
+ if (unix_ERR_class == ERRDOS && unix_ERR_code == ERRbadshare)
+ ret = NT_STATUS_SHARING_VIOLATION;
+ unix_ERR_class = 0;
+ unix_ERR_code = 0;
+ unix_ERR_ntstatus = NT_STATUS_OK;
+ return ret;
+ }
+ close_file(fsp,False);
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Check if a user is allowed to delete a file.
+********************************************************************/
+
+static NTSTATUS can_delete(char *fname,connection_struct *conn, int dirtype, BOOL bad_path)
+{
+ SMB_STRUCT_STAT sbuf;
+ int fmode;
+ int smb_action;
+ int access_mode;
+ files_struct *fsp;
+
+ DEBUG(10,("can_delete: %s, dirtype = %d\n",
+ fname, dirtype ));
+
+ if (!CAN_WRITE(conn))
+ return NT_STATUS_MEDIA_WRITE_PROTECTED;
+
+ if (SMB_VFS_LSTAT(conn,fname,&sbuf) != 0) {
+ if(errno == ENOENT) {
+ if (bad_path)
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ else
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ return map_nt_error_from_unix(errno);
+ }
+
+ fmode = dos_mode(conn,fname,&sbuf);
+
+ /* Can't delete a directory. */
+ if (fmode & aDIR)
+ return NT_STATUS_FILE_IS_A_DIRECTORY;
+#if 0 /* JRATEST */
+ else if (dirtype & aDIR) /* Asked for a directory and it isn't. */
+ return NT_STATUS_OBJECT_NAME_INVALID;
+#endif /* JRATEST */
+
+ if (!lp_delete_readonly(SNUM(conn))) {
+ if (fmode & aRONLY)
+ return NT_STATUS_CANNOT_DELETE;
+ }
+ if ((fmode & ~dirtype) & (aHIDDEN | aSYSTEM))
+ return NT_STATUS_NO_SUCH_FILE;
+
+ /* We need a better way to return NT status codes from open... */
+ unix_ERR_class = 0;
+ unix_ERR_code = 0;
+
+ fsp = open_file_shared1(conn, fname, &sbuf, DELETE_ACCESS, SET_DENY_MODE(DENY_ALL),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &smb_action);
+
+ if (!fsp) {
+ NTSTATUS ret = NT_STATUS_ACCESS_DENIED;
+ if (!NT_STATUS_IS_OK(unix_ERR_ntstatus))
+ ret = unix_ERR_ntstatus;
+ else if (unix_ERR_class == ERRDOS && unix_ERR_code == ERRbadshare)
+ ret = NT_STATUS_SHARING_VIOLATION;
+ unix_ERR_class = 0;
+ unix_ERR_code = 0;
+ unix_ERR_ntstatus = NT_STATUS_OK;
+ return ret;
+ }
+ close_file(fsp,False);
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ The guts of the unlink command, split out so it may be called by the NT SMB
+ code.
+****************************************************************************/
+
+NTSTATUS unlink_internals(connection_struct *conn, int dirtype, char *name)
+{
+ pstring directory;
+ pstring mask;
+ char *p;
+ int count=0;
+ NTSTATUS error = NT_STATUS_OK;
+ BOOL has_wild;
+ BOOL bad_path = False;
+ BOOL rc = True;
+ SMB_STRUCT_STAT sbuf;
+
+ *directory = *mask = 0;
+
+ /* We must check for wildcards in the name given
+ * directly by the client - before any unmangling.
+ * This prevents an unmangling of a UNIX name containing
+ * a DOS wildcard like '*' or '?' from unmangling into
+ * a wildcard delete which was not intended.
+ * FIX for #226. JRA.
+ */
+
+ has_wild = ms_has_wild(name);
+
+ rc = unix_convert(name,conn,0,&bad_path,&sbuf);
+
+ p = strrchr_m(name,'/');
+ if (!p) {
+ pstrcpy(directory,".");
+ pstrcpy(mask,name);
+ } else {
+ *p = 0;
+ pstrcpy(directory,name);
+ pstrcpy(mask,p+1);
+ }
+
+ /*
+ * We should only check the mangled cache
+ * here if unix_convert failed. This means
+ * that the path in 'mask' doesn't exist
+ * on the file system and so we need to look
+ * for a possible mangle. This patch from
+ * Tine Smukavec <valentin.smukavec@hermes.si>.
+ */
+
+ if (!rc && mangle_is_mangled(mask))
+ mangle_check_cache( mask );
+
+ if (!has_wild) {
+ pstrcat(directory,"/");
+ pstrcat(directory,mask);
+ error = can_delete(directory,conn,dirtype,bad_path);
+ if (!NT_STATUS_IS_OK(error))
+ return error;
+
+ if (SMB_VFS_UNLINK(conn,directory) == 0) {
+ count++;
+ }
+ } else {
+ void *dirptr = NULL;
+ const char *dname;
+
+ if (check_name(directory,conn))
+ dirptr = OpenDir(conn, directory, True);
+
+ /* XXXX the CIFS spec says that if bit0 of the flags2 field is set then
+ the pattern matches against the long name, otherwise the short name
+ We don't implement this yet XXXX
+ */
+
+ if (dirptr) {
+ error = NT_STATUS_NO_SUCH_FILE;
+
+ if (strequal(mask,"????????.???"))
+ pstrcpy(mask,"*");
+
+ while ((dname = ReadDirName(dirptr))) {
+ pstring fname;
+ BOOL sys_direntry = False;
+ pstrcpy(fname,dname);
+
+ /* Quick check for "." and ".." */
+ if (fname[0] == '.') {
+ if (!fname[1] || (fname[1] == '.' && !fname[2])) {
+ if ((dirtype & aDIR)) {
+ sys_direntry = True;
+ } else {
+ continue;
+ }
+ }
+ }
+
+ if(!mask_match(fname, mask, case_sensitive))
+ continue;
+
+ if (sys_direntry) {
+ error = NT_STATUS_OBJECT_NAME_INVALID;
+ break;
+ }
+
+ slprintf(fname,sizeof(fname)-1, "%s/%s",directory,dname);
+ error = can_delete(fname,conn,dirtype,bad_path);
+ if (!NT_STATUS_IS_OK(error)) {
+ continue;
+ }
+ if (SMB_VFS_UNLINK(conn,fname) == 0)
+ count++;
+ DEBUG(3,("unlink_internals: succesful unlink [%s]\n",fname));
+ }
+ CloseDir(dirptr);
+ }
+ }
+
+ if (count == 0 && NT_STATUS_IS_OK(error)) {
+ error = map_nt_error_from_unix(errno);
+ }
+
+ return error;
+}
+
+/****************************************************************************
+ Reply to a unlink
+****************************************************************************/
+
+int reply_unlink(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
+ int dum_buffsize)
+{
+ int outsize = 0;
+ pstring name;
+ int dirtype;
+ NTSTATUS status;
+ START_PROFILE(SMBunlink);
+
+ dirtype = SVAL(inbuf,smb_vwv0);
+
+ srvstr_get_path(inbuf, name, smb_buf(inbuf) + 1, sizeof(name), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBunlink);
+ return ERROR_NT(status);
+ }
+
+ RESOLVE_DFSPATH(name, conn, inbuf, outbuf);
+
+ DEBUG(3,("reply_unlink : %s\n",name));
+
+ status = unlink_internals(conn, dirtype, name);
+ if (!NT_STATUS_IS_OK(status))
+ return ERROR_NT(status);
+
+ /*
+ * Win2k needs a changenotify request response before it will
+ * update after a rename..
+ */
+ process_pending_change_notify_queue((time_t)0);
+
+ outsize = set_message(outbuf,0,0,True);
+
+ END_PROFILE(SMBunlink);
+ return outsize;
+}
+
+/****************************************************************************
+ Fail for readbraw.
+****************************************************************************/
+
+void fail_readraw(void)
+{
+ pstring errstr;
+ slprintf(errstr, sizeof(errstr)-1, "FAIL ! reply_readbraw: socket write fail (%s)",
+ strerror(errno) );
+ exit_server(errstr);
+}
+
+/****************************************************************************
+ Use sendfile in readbraw.
+****************************************************************************/
+
+void send_file_readbraw(connection_struct *conn, files_struct *fsp, SMB_OFF_T startpos, size_t nread,
+ ssize_t mincount, char *outbuf)
+{
+ ssize_t ret=0;
+
+#if defined(WITH_SENDFILE)
+ /*
+ * We can only use sendfile on a non-chained packet and on a file
+ * that is exclusively oplocked. reply_readbraw has already checked the length.
+ */
+
+ if ((nread > 0) && (lp_write_cache_size(SNUM(conn)) == 0) &&
+ EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && lp_use_sendfile(SNUM(conn)) ) {
+ DATA_BLOB header;
+
+ _smb_setlen(outbuf,nread);
+ header.data = outbuf;
+ header.length = 4;
+ header.free = NULL;
+
+ if ( SMB_VFS_SENDFILE( smbd_server_fd(), fsp, fsp->fd, &header, startpos, nread) == -1) {
+ /*
+ * Special hack for broken Linux with no 64 bit clean sendfile. If we
+ * return ENOSYS then pretend we just got a normal read.
+ */
+ if (errno == ENOSYS)
+ goto normal_read;
+
+ DEBUG(0,("send_file_readbraw: sendfile failed for file %s (%s). Terminating\n",
+ fsp->fsp_name, strerror(errno) ));
+ exit_server("send_file_readbraw sendfile failed");
+ }
+
+ }
+
+ normal_read:
+#endif
+
+ if (nread > 0) {
+ ret = read_file(fsp,outbuf+4,startpos,nread);
+#if 0 /* mincount appears to be ignored in a W2K server. JRA. */
+ if (ret < mincount)
+ ret = 0;
+#else
+ if (ret < nread)
+ ret = 0;
+#endif
+ }
+
+ _smb_setlen(outbuf,ret);
+ if (write_data(smbd_server_fd(),outbuf,4+ret) != 4+ret)
+ fail_readraw();
+}
+
+/****************************************************************************
+ Reply to a readbraw (core+ protocol).
+****************************************************************************/
+
+int reply_readbraw(connection_struct *conn, char *inbuf, char *outbuf, int dum_size, int dum_buffsize)
+{
+ extern struct current_user current_user;
+ ssize_t maxcount,mincount;
+ size_t nread = 0;
+ SMB_OFF_T startpos;
+ char *header = outbuf;
+ files_struct *fsp;
+ START_PROFILE(SMBreadbraw);
+
+ if (srv_is_signing_active()) {
+ exit_server("reply_readbraw: SMB signing is active - raw reads/writes are disallowed.");
+ }
+
+ /*
+ * Special check if an oplock break has been issued
+ * and the readraw request croses on the wire, we must
+ * return a zero length response here.
+ */
+
+ if(global_oplock_break) {
+ _smb_setlen(header,0);
+ if (write_data(smbd_server_fd(),header,4) != 4)
+ fail_readraw();
+ DEBUG(5,("readbraw - oplock break finished\n"));
+ END_PROFILE(SMBreadbraw);
+ return -1;
+ }
+
+ fsp = file_fsp(inbuf,smb_vwv0);
+
+ if (!FNUM_OK(fsp,conn) || !fsp->can_read) {
+ /*
+ * fsp could be NULL here so use the value from the packet. JRA.
+ */
+ DEBUG(3,("fnum %d not open in readbraw - cache prime?\n",(int)SVAL(inbuf,smb_vwv0)));
+ _smb_setlen(header,0);
+ if (write_data(smbd_server_fd(),header,4) != 4)
+ fail_readraw();
+ END_PROFILE(SMBreadbraw);
+ return(-1);
+ }
+
+ CHECK_FSP(fsp,conn);
+
+ flush_write_cache(fsp, READRAW_FLUSH);
+
+ startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv1);
+ if(CVAL(inbuf,smb_wct) == 10) {
+ /*
+ * This is a large offset (64 bit) read.
+ */
+#ifdef LARGE_SMB_OFF_T
+
+ startpos |= (((SMB_OFF_T)IVAL(inbuf,smb_vwv8)) << 32);
+
+#else /* !LARGE_SMB_OFF_T */
+
+ /*
+ * Ensure we haven't been sent a >32 bit offset.
+ */
+
+ if(IVAL(inbuf,smb_vwv8) != 0) {
+ DEBUG(0,("readbraw - large offset (%x << 32) used and we don't support \
+64 bit offsets.\n", (unsigned int)IVAL(inbuf,smb_vwv8) ));
+ _smb_setlen(header,0);
+ if (write_data(smbd_server_fd(),header,4) != 4)
+ fail_readraw();
+ END_PROFILE(SMBreadbraw);
+ return(-1);
+ }
+
+#endif /* LARGE_SMB_OFF_T */
+
+ if(startpos < 0) {
+ DEBUG(0,("readbraw - negative 64 bit readraw offset (%.0f) !\n", (double)startpos ));
+ _smb_setlen(header,0);
+ if (write_data(smbd_server_fd(),header,4) != 4)
+ fail_readraw();
+ END_PROFILE(SMBreadbraw);
+ return(-1);
+ }
+ }
+ maxcount = (SVAL(inbuf,smb_vwv3) & 0xFFFF);
+ mincount = (SVAL(inbuf,smb_vwv4) & 0xFFFF);
+
+ /* ensure we don't overrun the packet size */
+ maxcount = MIN(65535,maxcount);
+
+ if (!is_locked(fsp,conn,(SMB_BIG_UINT)maxcount,(SMB_BIG_UINT)startpos, READ_LOCK,False)) {
+ SMB_OFF_T size = fsp->size;
+ SMB_OFF_T sizeneeded = startpos + maxcount;
+
+ if (size < sizeneeded) {
+ SMB_STRUCT_STAT st;
+ if (SMB_VFS_FSTAT(fsp,fsp->fd,&st) == 0)
+ size = st.st_size;
+ if (!fsp->can_write)
+ fsp->size = size;
+ }
+
+ if (startpos >= size)
+ nread = 0;
+ else
+ nread = MIN(maxcount,(size - startpos));
+ }
+
+#if 0 /* mincount appears to be ignored in a W2K server. JRA. */
+ if (nread < mincount)
+ nread = 0;
+#endif
+
+ DEBUG( 3, ( "readbraw fnum=%d start=%.0f max=%d min=%d nread=%d\n", fsp->fnum, (double)startpos,
+ (int)maxcount, (int)mincount, (int)nread ) );
+
+ send_file_readbraw(conn, fsp, startpos, nread, mincount, outbuf);
+
+ DEBUG(5,("readbraw finished\n"));
+ END_PROFILE(SMBreadbraw);
+ return -1;
+}
+
+/****************************************************************************
+ Reply to a lockread (core+ protocol).
+****************************************************************************/
+
+int reply_lockread(connection_struct *conn, char *inbuf,char *outbuf, int length, int dum_buffsiz)
+{
+ ssize_t nread = -1;
+ char *data;
+ int outsize = 0;
+ SMB_OFF_T startpos;
+ size_t numtoread;
+ NTSTATUS status;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ BOOL my_lock_ctx = False;
+ START_PROFILE(SMBlockread);
+
+ CHECK_FSP(fsp,conn);
+ CHECK_READ(fsp);
+
+ release_level_2_oplocks_on_change(fsp);
+
+ numtoread = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv2);
+
+ outsize = set_message(outbuf,5,3,True);
+ numtoread = MIN(BUFFER_SIZE-outsize,numtoread);
+ data = smb_buf(outbuf) + 3;
+
+ /*
+ * NB. Discovered by Menny Hamburger at Mainsoft. This is a core+
+ * protocol request that predates the read/write lock concept.
+ * Thus instead of asking for a read lock here we need to ask
+ * for a write lock. JRA.
+ * Note that the requested lock size is unaffected by max_recv.
+ */
+
+ status = do_lock_spin(fsp, conn, SVAL(inbuf,smb_pid),
+ (SMB_BIG_UINT)numtoread, (SMB_BIG_UINT)startpos, WRITE_LOCK, &my_lock_ctx);
+
+ if (NT_STATUS_V(status)) {
+#if 0
+ /*
+ * We used to make lockread a blocking lock. It turns out
+ * that this isn't on W2k. Found by the Samba 4 RAW-READ torture
+ * tester. JRA.
+ */
+
+ if (lp_blocking_locks(SNUM(conn)) && !my_lock_ctx && ERROR_WAS_LOCK_DENIED(status)) {
+ /*
+ * A blocking lock was requested. Package up
+ * this smb into a queued request and push it
+ * onto the blocking lock queue.
+ */
+ if(push_blocking_lock_request(inbuf, length, -1, 0, SVAL(inbuf,smb_pid), (SMB_BIG_UINT)startpos,
+ (SMB_BIG_UINT)numtoread)) {
+ END_PROFILE(SMBlockread);
+ return -1;
+ }
+ }
+#endif
+ END_PROFILE(SMBlockread);
+ return ERROR_NT(status);
+ }
+
+ /*
+ * However the requested READ size IS affected by max_recv. Insanity.... JRA.
+ */
+
+ if (numtoread > max_recv) {
+ DEBUG(0,("reply_lockread: requested read size (%u) is greater than maximum allowed (%u). \
+Returning short read of maximum allowed for compatibility with Windows 2000.\n",
+ (unsigned int)numtoread, (unsigned int)max_recv ));
+ numtoread = MIN(numtoread,max_recv);
+ }
+ nread = read_file(fsp,data,startpos,numtoread);
+
+ if (nread < 0) {
+ END_PROFILE(SMBlockread);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ outsize += nread;
+ SSVAL(outbuf,smb_vwv0,nread);
+ SSVAL(outbuf,smb_vwv5,nread+3);
+ SSVAL(smb_buf(outbuf),1,nread);
+
+ DEBUG(3,("lockread fnum=%d num=%d nread=%d\n",
+ fsp->fnum, (int)numtoread, (int)nread));
+
+ END_PROFILE(SMBlockread);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a read.
+****************************************************************************/
+
+int reply_read(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize)
+{
+ size_t numtoread;
+ ssize_t nread = 0;
+ char *data;
+ SMB_OFF_T startpos;
+ int outsize = 0;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBread);
+
+ CHECK_FSP(fsp,conn);
+ CHECK_READ(fsp);
+
+ numtoread = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv2);
+
+ outsize = set_message(outbuf,5,3,True);
+ numtoread = MIN(BUFFER_SIZE-outsize,numtoread);
+ /*
+ * The requested read size cannot be greater than max_recv. JRA.
+ */
+ if (numtoread > max_recv) {
+ DEBUG(0,("reply_read: requested read size (%u) is greater than maximum allowed (%u). \
+Returning short read of maximum allowed for compatibility with Windows 2000.\n",
+ (unsigned int)numtoread, (unsigned int)max_recv ));
+ numtoread = MIN(numtoread,max_recv);
+ }
+
+ data = smb_buf(outbuf) + 3;
+
+ if (is_locked(fsp,conn,(SMB_BIG_UINT)numtoread,(SMB_BIG_UINT)startpos, READ_LOCK,False)) {
+ END_PROFILE(SMBread);
+ return ERROR_DOS(ERRDOS,ERRlock);
+ }
+
+ if (numtoread > 0)
+ nread = read_file(fsp,data,startpos,numtoread);
+
+ if (nread < 0) {
+ END_PROFILE(SMBread);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ outsize += nread;
+ SSVAL(outbuf,smb_vwv0,nread);
+ SSVAL(outbuf,smb_vwv5,nread+3);
+ SCVAL(smb_buf(outbuf),0,1);
+ SSVAL(smb_buf(outbuf),1,nread);
+
+ DEBUG( 3, ( "read fnum=%d num=%d nread=%d\n",
+ fsp->fnum, (int)numtoread, (int)nread ) );
+
+ END_PROFILE(SMBread);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a read and X - possibly using sendfile.
+****************************************************************************/
+
+int send_file_readX(connection_struct *conn, char *inbuf,char *outbuf,int length,
+ files_struct *fsp, SMB_OFF_T startpos, size_t smb_maxcnt)
+{
+ ssize_t nread = -1;
+ char *data = smb_buf(outbuf);
+
+#if defined(WITH_SENDFILE)
+ /*
+ * We can only use sendfile on a non-chained packet and on a file
+ * that is exclusively oplocked.
+ */
+
+ if ((CVAL(inbuf,smb_vwv0) == 0xFF) && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) &&
+ lp_use_sendfile(SNUM(conn)) && (lp_write_cache_size(SNUM(conn)) == 0) ) {
+ SMB_STRUCT_STAT sbuf;
+ DATA_BLOB header;
+
+ if(SMB_VFS_FSTAT(fsp,fsp->fd, &sbuf) == -1)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ if (startpos > sbuf.st_size)
+ goto normal_read;
+
+ if (smb_maxcnt > (sbuf.st_size - startpos))
+ smb_maxcnt = (sbuf.st_size - startpos);
+
+ if (smb_maxcnt == 0)
+ goto normal_read;
+
+ /*
+ * Set up the packet header before send. We
+ * assume here the sendfile will work (get the
+ * correct amount of data).
+ */
+
+ SSVAL(outbuf,smb_vwv2,0xFFFF); /* Remaining - must be -1. */
+ SSVAL(outbuf,smb_vwv5,smb_maxcnt);
+ SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf));
+ SSVAL(smb_buf(outbuf),-2,smb_maxcnt);
+ SCVAL(outbuf,smb_vwv0,0xFF);
+ set_message(outbuf,12,smb_maxcnt,False);
+ header.data = outbuf;
+ header.length = data - outbuf;
+ header.free = NULL;
+
+ if ( SMB_VFS_SENDFILE( smbd_server_fd(), fsp, fsp->fd, &header, startpos, smb_maxcnt) == -1) {
+ /*
+ * Special hack for broken Linux with no 64 bit clean sendfile. If we
+ * return ENOSYS then pretend we just got a normal read.
+ */
+ if (errno == ENOSYS)
+ goto normal_read;
+
+ DEBUG(0,("send_file_readX: sendfile failed for file %s (%s). Terminating\n",
+ fsp->fsp_name, strerror(errno) ));
+ exit_server("send_file_readX sendfile failed");
+ }
+
+ DEBUG( 3, ( "send_file_readX: sendfile fnum=%d max=%d nread=%d\n",
+ fsp->fnum, (int)smb_maxcnt, (int)nread ) );
+ return -1;
+ }
+
+ normal_read:
+
+#endif
+
+ nread = read_file(fsp,data,startpos,smb_maxcnt);
+
+ if (nread < 0) {
+ END_PROFILE(SMBreadX);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ SSVAL(outbuf,smb_vwv2,0xFFFF); /* Remaining - must be -1. */
+ SSVAL(outbuf,smb_vwv5,nread);
+ SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf));
+ SSVAL(smb_buf(outbuf),-2,nread);
+
+ DEBUG( 3, ( "send_file_readX fnum=%d max=%d nread=%d\n",
+ fsp->fnum, (int)smb_maxcnt, (int)nread ) );
+
+ return nread;
+}
+
+/****************************************************************************
+ Reply to a read and X.
+****************************************************************************/
+
+int reply_read_and_X(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize)
+{
+ files_struct *fsp = file_fsp(inbuf,smb_vwv2);
+ SMB_OFF_T startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv3);
+ ssize_t nread = -1;
+ size_t smb_maxcnt = SVAL(inbuf,smb_vwv5);
+#if 0
+ size_t smb_mincnt = SVAL(inbuf,smb_vwv6);
+#endif
+
+ START_PROFILE(SMBreadX);
+
+ /* If it's an IPC, pass off the pipe handler. */
+ if (IS_IPC(conn)) {
+ END_PROFILE(SMBreadX);
+ return reply_pipe_read_and_X(inbuf,outbuf,length,bufsize);
+ }
+
+ CHECK_FSP(fsp,conn);
+ CHECK_READ(fsp);
+
+ set_message(outbuf,12,0,True);
+
+ if(CVAL(inbuf,smb_wct) == 12) {
+#ifdef LARGE_SMB_OFF_T
+ /*
+ * This is a large offset (64 bit) read.
+ */
+ startpos |= (((SMB_OFF_T)IVAL(inbuf,smb_vwv10)) << 32);
+
+#else /* !LARGE_SMB_OFF_T */
+
+ /*
+ * Ensure we haven't been sent a >32 bit offset.
+ */
+
+ if(IVAL(inbuf,smb_vwv10) != 0) {
+ DEBUG(0,("reply_read_and_X - large offset (%x << 32) used and we don't support \
+64 bit offsets.\n", (unsigned int)IVAL(inbuf,smb_vwv10) ));
+ END_PROFILE(SMBreadX);
+ return ERROR_DOS(ERRDOS,ERRbadaccess);
+ }
+
+#endif /* LARGE_SMB_OFF_T */
+
+ }
+
+ if (is_locked(fsp,conn,(SMB_BIG_UINT)smb_maxcnt,(SMB_BIG_UINT)startpos, READ_LOCK,False)) {
+ END_PROFILE(SMBreadX);
+ return ERROR_DOS(ERRDOS,ERRlock);
+ }
+
+ nread = send_file_readX(conn, inbuf, outbuf, length, fsp, startpos, smb_maxcnt);
+ if (nread != -1)
+ nread = chain_reply(inbuf,outbuf,length,bufsize);
+
+ END_PROFILE(SMBreadX);
+ return nread;
+}
+
+/****************************************************************************
+ Reply to a writebraw (core+ or LANMAN1.0 protocol).
+****************************************************************************/
+
+int reply_writebraw(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize)
+{
+ ssize_t nwritten=0;
+ ssize_t total_written=0;
+ size_t numtowrite=0;
+ size_t tcount;
+ SMB_OFF_T startpos;
+ char *data=NULL;
+ BOOL write_through;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ int outsize = 0;
+ START_PROFILE(SMBwritebraw);
+
+ if (srv_is_signing_active()) {
+ exit_server("reply_writebraw: SMB signing is active - raw reads/writes are disallowed.");
+ }
+
+ CHECK_FSP(fsp,conn);
+ CHECK_WRITE(fsp);
+
+ tcount = IVAL(inbuf,smb_vwv1);
+ startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv3);
+ write_through = BITSETW(inbuf+smb_vwv7,0);
+
+ /* We have to deal with slightly different formats depending
+ on whether we are using the core+ or lanman1.0 protocol */
+
+ if(Protocol <= PROTOCOL_COREPLUS) {
+ numtowrite = SVAL(smb_buf(inbuf),-2);
+ data = smb_buf(inbuf);
+ } else {
+ numtowrite = SVAL(inbuf,smb_vwv10);
+ data = smb_base(inbuf) + SVAL(inbuf, smb_vwv11);
+ }
+
+ /* force the error type */
+ SCVAL(inbuf,smb_com,SMBwritec);
+ SCVAL(outbuf,smb_com,SMBwritec);
+
+ if (is_locked(fsp,conn,(SMB_BIG_UINT)tcount,(SMB_BIG_UINT)startpos, WRITE_LOCK,False)) {
+ END_PROFILE(SMBwritebraw);
+ return(ERROR_DOS(ERRDOS,ERRlock));
+ }
+
+ if (numtowrite>0)
+ nwritten = write_file(fsp,data,startpos,numtowrite);
+
+ DEBUG(3,("writebraw1 fnum=%d start=%.0f num=%d wrote=%d sync=%d\n",
+ fsp->fnum, (double)startpos, (int)numtowrite, (int)nwritten, (int)write_through));
+
+ if (nwritten < (ssize_t)numtowrite) {
+ END_PROFILE(SMBwritebraw);
+ return(UNIXERROR(ERRHRD,ERRdiskfull));
+ }
+
+ total_written = nwritten;
+
+ /* Return a message to the redirector to tell it to send more bytes */
+ SCVAL(outbuf,smb_com,SMBwritebraw);
+ SSVALS(outbuf,smb_vwv0,-1);
+ outsize = set_message(outbuf,Protocol>PROTOCOL_COREPLUS?1:0,0,True);
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("reply_writebraw: send_smb failed.");
+
+ /* Now read the raw data into the buffer and write it */
+ if (read_smb_length(smbd_server_fd(),inbuf,SMB_SECONDARY_WAIT) == -1) {
+ exit_server("secondary writebraw failed");
+ }
+
+ /* Even though this is not an smb message, smb_len returns the generic length of an smb message */
+ numtowrite = smb_len(inbuf);
+
+ /* Set up outbuf to return the correct return */
+ outsize = set_message(outbuf,1,0,True);
+ SCVAL(outbuf,smb_com,SMBwritec);
+ SSVAL(outbuf,smb_vwv0,total_written);
+
+ if (numtowrite != 0) {
+
+ if (numtowrite > BUFFER_SIZE) {
+ DEBUG(0,("reply_writebraw: Oversize secondary write raw requested (%u). Terminating\n",
+ (unsigned int)numtowrite ));
+ exit_server("secondary writebraw failed");
+ }
+
+ if (tcount > nwritten+numtowrite) {
+ DEBUG(3,("Client overestimated the write %d %d %d\n",
+ (int)tcount,(int)nwritten,(int)numtowrite));
+ }
+
+ if (read_data( smbd_server_fd(), inbuf+4, numtowrite) != numtowrite ) {
+ DEBUG(0,("reply_writebraw: Oversize secondary write raw read failed (%s). Terminating\n",
+ strerror(errno) ));
+ exit_server("secondary writebraw failed");
+ }
+
+ nwritten = write_file(fsp,inbuf+4,startpos+nwritten,numtowrite);
+
+ if (nwritten < (ssize_t)numtowrite) {
+ SCVAL(outbuf,smb_rcls,ERRHRD);
+ SSVAL(outbuf,smb_err,ERRdiskfull);
+ }
+
+ if (nwritten > 0)
+ total_written += nwritten;
+ }
+
+ if ((lp_syncalways(SNUM(conn)) || write_through) && lp_strict_sync(SNUM(conn)))
+ sync_file(conn,fsp);
+
+ DEBUG(3,("writebraw2 fnum=%d start=%.0f num=%d wrote=%d\n",
+ fsp->fnum, (double)startpos, (int)numtowrite,(int)total_written));
+
+ /* we won't return a status if write through is not selected - this follows what WfWg does */
+ END_PROFILE(SMBwritebraw);
+ if (!write_through && total_written==tcount) {
+
+#if RABBIT_PELLET_FIX
+ /*
+ * Fix for "rabbit pellet" mode, trigger an early TCP ack by
+ * sending a SMBkeepalive. Thanks to DaveCB at Sun for this. JRA.
+ */
+ if (!send_keepalive(smbd_server_fd()))
+ exit_server("reply_writebraw: send of keepalive failed");
+#endif
+ return(-1);
+ }
+
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a writeunlock (core+).
+****************************************************************************/
+
+int reply_writeunlock(connection_struct *conn, char *inbuf,char *outbuf,
+ int size, int dum_buffsize)
+{
+ ssize_t nwritten = -1;
+ size_t numtowrite;
+ SMB_OFF_T startpos;
+ char *data;
+ NTSTATUS status = NT_STATUS_OK;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ int outsize = 0;
+ START_PROFILE(SMBwriteunlock);
+
+ CHECK_FSP(fsp,conn);
+ CHECK_WRITE(fsp);
+
+ numtowrite = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv2);
+ data = smb_buf(inbuf) + 3;
+
+ if (numtowrite && is_locked(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos,
+ WRITE_LOCK,False)) {
+ END_PROFILE(SMBwriteunlock);
+ return ERROR_DOS(ERRDOS,ERRlock);
+ }
+
+ /* The special X/Open SMB protocol handling of
+ zero length writes is *NOT* done for
+ this call */
+ if(numtowrite == 0)
+ nwritten = 0;
+ else
+ nwritten = write_file(fsp,data,startpos,numtowrite);
+
+ if (lp_syncalways(SNUM(conn)))
+ sync_file(conn,fsp);
+
+ if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) {
+ END_PROFILE(SMBwriteunlock);
+ return(UNIXERROR(ERRHRD,ERRdiskfull));
+ }
+
+ if (numtowrite) {
+ status = do_unlock(fsp, conn, SVAL(inbuf,smb_pid), (SMB_BIG_UINT)numtowrite,
+ (SMB_BIG_UINT)startpos);
+ if (NT_STATUS_V(status)) {
+ END_PROFILE(SMBwriteunlock);
+ return ERROR_NT(status);
+ }
+ }
+
+ outsize = set_message(outbuf,1,0,True);
+
+ SSVAL(outbuf,smb_vwv0,nwritten);
+
+ DEBUG(3,("writeunlock fnum=%d num=%d wrote=%d\n",
+ fsp->fnum, (int)numtowrite, (int)nwritten));
+
+ END_PROFILE(SMBwriteunlock);
+ return outsize;
+}
+
+/****************************************************************************
+ Reply to a write.
+****************************************************************************/
+
+int reply_write(connection_struct *conn, char *inbuf,char *outbuf,int size,int dum_buffsize)
+{
+ size_t numtowrite;
+ ssize_t nwritten = -1;
+ SMB_OFF_T startpos;
+ char *data;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ int outsize = 0;
+ START_PROFILE(SMBwrite);
+
+ /* If it's an IPC, pass off the pipe handler. */
+ if (IS_IPC(conn)) {
+ END_PROFILE(SMBwrite);
+ return reply_pipe_write(inbuf,outbuf,size,dum_buffsize);
+ }
+
+ CHECK_FSP(fsp,conn);
+ CHECK_WRITE(fsp);
+
+ numtowrite = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv2);
+ data = smb_buf(inbuf) + 3;
+
+ if (is_locked(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK,False)) {
+ END_PROFILE(SMBwrite);
+ return ERROR_DOS(ERRDOS,ERRlock);
+ }
+
+ /*
+ * X/Open SMB protocol says that if smb_vwv1 is
+ * zero then the file size should be extended or
+ * truncated to the size given in smb_vwv[2-3].
+ */
+
+ if(numtowrite == 0) {
+ /*
+ * This is actually an allocate call, and set EOF. JRA.
+ */
+ nwritten = vfs_allocate_file_space(fsp, (SMB_OFF_T)startpos);
+ if (nwritten < 0) {
+ END_PROFILE(SMBwrite);
+ return ERROR_NT(NT_STATUS_DISK_FULL);
+ }
+ nwritten = vfs_set_filelen(fsp, (SMB_OFF_T)startpos);
+ if (nwritten < 0) {
+ END_PROFILE(SMBwrite);
+ return ERROR_NT(NT_STATUS_DISK_FULL);
+ }
+ } else
+ nwritten = write_file(fsp,data,startpos,numtowrite);
+
+ if (lp_syncalways(SNUM(conn)))
+ sync_file(conn,fsp);
+
+ if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) {
+ END_PROFILE(SMBwrite);
+ return(UNIXERROR(ERRHRD,ERRdiskfull));
+ }
+
+ outsize = set_message(outbuf,1,0,True);
+
+ SSVAL(outbuf,smb_vwv0,nwritten);
+
+ if (nwritten < (ssize_t)numtowrite) {
+ SCVAL(outbuf,smb_rcls,ERRHRD);
+ SSVAL(outbuf,smb_err,ERRdiskfull);
+ }
+
+ DEBUG(3,("write fnum=%d num=%d wrote=%d\n", fsp->fnum, (int)numtowrite, (int)nwritten));
+
+ END_PROFILE(SMBwrite);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a write and X.
+****************************************************************************/
+
+int reply_write_and_X(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize)
+{
+ files_struct *fsp = file_fsp(inbuf,smb_vwv2);
+ SMB_OFF_T startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv3);
+ size_t numtowrite = SVAL(inbuf,smb_vwv10);
+ BOOL write_through = BITSETW(inbuf+smb_vwv7,0);
+ ssize_t nwritten = -1;
+ unsigned int smb_doff = SVAL(inbuf,smb_vwv11);
+ unsigned int smblen = smb_len(inbuf);
+ char *data;
+ BOOL large_writeX = ((CVAL(inbuf,smb_wct) == 14) && (smblen > 0xFFFF));
+ START_PROFILE(SMBwriteX);
+
+ /* If it's an IPC, pass off the pipe handler. */
+ if (IS_IPC(conn)) {
+ END_PROFILE(SMBwriteX);
+ return reply_pipe_write_and_X(inbuf,outbuf,length,bufsize);
+ }
+
+ CHECK_FSP(fsp,conn);
+ CHECK_WRITE(fsp);
+
+ /* Deal with possible LARGE_WRITEX */
+ if (large_writeX)
+ numtowrite |= ((((size_t)SVAL(inbuf,smb_vwv9)) & 1 )<<16);
+
+ if(smb_doff > smblen || (smb_doff + numtowrite > smblen)) {
+ END_PROFILE(SMBwriteX);
+ return ERROR_DOS(ERRDOS,ERRbadmem);
+ }
+
+ data = smb_base(inbuf) + smb_doff;
+
+ if(CVAL(inbuf,smb_wct) == 14) {
+#ifdef LARGE_SMB_OFF_T
+ /*
+ * This is a large offset (64 bit) write.
+ */
+ startpos |= (((SMB_OFF_T)IVAL(inbuf,smb_vwv12)) << 32);
+
+#else /* !LARGE_SMB_OFF_T */
+
+ /*
+ * Ensure we haven't been sent a >32 bit offset.
+ */
+
+ if(IVAL(inbuf,smb_vwv12) != 0) {
+ DEBUG(0,("reply_write_and_X - large offset (%x << 32) used and we don't support \
+64 bit offsets.\n", (unsigned int)IVAL(inbuf,smb_vwv12) ));
+ END_PROFILE(SMBwriteX);
+ return ERROR_DOS(ERRDOS,ERRbadaccess);
+ }
+
+#endif /* LARGE_SMB_OFF_T */
+ }
+
+ if (is_locked(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK,False)) {
+ END_PROFILE(SMBwriteX);
+ return ERROR_DOS(ERRDOS,ERRlock);
+ }
+
+ /* X/Open SMB protocol says that, unlike SMBwrite
+ if the length is zero then NO truncation is
+ done, just a write of zero. To truncate a file,
+ use SMBwrite. */
+
+ if(numtowrite == 0)
+ nwritten = 0;
+ else
+ nwritten = write_file(fsp,data,startpos,numtowrite);
+
+ if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) {
+ END_PROFILE(SMBwriteX);
+ return(UNIXERROR(ERRHRD,ERRdiskfull));
+ }
+
+ set_message(outbuf,6,0,True);
+
+ SSVAL(outbuf,smb_vwv2,nwritten);
+ if (large_writeX)
+ SSVAL(outbuf,smb_vwv4,(nwritten>>16)&1);
+
+ if (nwritten < (ssize_t)numtowrite) {
+ SCVAL(outbuf,smb_rcls,ERRHRD);
+ SSVAL(outbuf,smb_err,ERRdiskfull);
+ }
+
+ DEBUG(3,("writeX fnum=%d num=%d wrote=%d\n",
+ fsp->fnum, (int)numtowrite, (int)nwritten));
+
+ if (lp_syncalways(SNUM(conn)) || write_through)
+ sync_file(conn,fsp);
+
+ END_PROFILE(SMBwriteX);
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
+
+/****************************************************************************
+ Reply to a lseek.
+****************************************************************************/
+
+int reply_lseek(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize)
+{
+ SMB_OFF_T startpos;
+ SMB_OFF_T res= -1;
+ int mode,umode;
+ int outsize = 0;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBlseek);
+
+ CHECK_FSP(fsp,conn);
+
+ flush_write_cache(fsp, SEEK_FLUSH);
+
+ mode = SVAL(inbuf,smb_vwv1) & 3;
+ /* NB. This doesn't use IVAL_TO_SMB_OFF_T as startpos can be signed in this case. */
+ startpos = (SMB_OFF_T)IVALS(inbuf,smb_vwv2);
+
+ switch (mode) {
+ case 0:
+ umode = SEEK_SET;
+ res = startpos;
+ break;
+ case 1:
+ umode = SEEK_CUR;
+ res = fsp->pos + startpos;
+ break;
+ case 2:
+ umode = SEEK_END;
+ break;
+ default:
+ umode = SEEK_SET;
+ res = startpos;
+ break;
+ }
+
+ if (umode == SEEK_END) {
+ if((res = SMB_VFS_LSEEK(fsp,fsp->fd,startpos,umode)) == -1) {
+ if(errno == EINVAL) {
+ SMB_OFF_T current_pos = startpos;
+ SMB_STRUCT_STAT sbuf;
+
+ if(SMB_VFS_FSTAT(fsp,fsp->fd, &sbuf) == -1) {
+ END_PROFILE(SMBlseek);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ current_pos += sbuf.st_size;
+ if(current_pos < 0)
+ res = SMB_VFS_LSEEK(fsp,fsp->fd,0,SEEK_SET);
+ }
+ }
+
+ if(res == -1) {
+ END_PROFILE(SMBlseek);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+ }
+
+ fsp->pos = res;
+
+ outsize = set_message(outbuf,2,0,True);
+ SIVAL(outbuf,smb_vwv0,res);
+
+ DEBUG(3,("lseek fnum=%d ofs=%.0f newpos = %.0f mode=%d\n",
+ fsp->fnum, (double)startpos, (double)res, mode));
+
+ END_PROFILE(SMBlseek);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a flush.
+****************************************************************************/
+
+int reply_flush(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize)
+{
+ int outsize = set_message(outbuf,0,0,True);
+ uint16 fnum = SVAL(inbuf,smb_vwv0);
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBflush);
+
+ if (fnum != 0xFFFF)
+ CHECK_FSP(fsp,conn);
+
+ if (!fsp) {
+ file_sync_all(conn);
+ } else {
+ sync_file(conn,fsp);
+ }
+
+ DEBUG(3,("flush\n"));
+ END_PROFILE(SMBflush);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a exit.
+****************************************************************************/
+
+int reply_exit(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int outsize;
+ START_PROFILE(SMBexit);
+
+ file_close_pid(SVAL(inbuf,smb_pid));
+
+ outsize = set_message(outbuf,0,0,True);
+
+ DEBUG(3,("exit\n"));
+
+ END_PROFILE(SMBexit);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a close - has to deal with closing a directory opened by NT SMB's.
+****************************************************************************/
+
+int reply_close(connection_struct *conn, char *inbuf,char *outbuf, int size,
+ int dum_buffsize)
+{
+ extern struct current_user current_user;
+ int outsize = 0;
+ time_t mtime;
+ int32 eclass = 0, err = 0;
+ files_struct *fsp = NULL;
+ START_PROFILE(SMBclose);
+
+ outsize = set_message(outbuf,0,0,True);
+
+ /* If it's an IPC, pass off to the pipe handler. */
+ if (IS_IPC(conn)) {
+ END_PROFILE(SMBclose);
+ return reply_pipe_close(conn, inbuf,outbuf);
+ }
+
+ fsp = file_fsp(inbuf,smb_vwv0);
+
+ /*
+ * We can only use CHECK_FSP if we know it's not a directory.
+ */
+
+ if(!fsp || (fsp->conn != conn) || (fsp->vuid != current_user.vuid)) {
+ END_PROFILE(SMBclose);
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+ }
+
+ if(fsp->is_directory) {
+ /*
+ * Special case - close NT SMB directory handle.
+ */
+ DEBUG(3,("close %s fnum=%d\n", fsp->is_directory ? "directory" : "stat file open", fsp->fnum));
+ close_file(fsp,True);
+ } else {
+ /*
+ * Close ordinary file.
+ */
+ int close_err;
+ pstring file_name;
+
+ /* Save the name for time set in close. */
+ pstrcpy( file_name, fsp->fsp_name);
+
+ DEBUG(3,("close fd=%d fnum=%d (numopen=%d)\n",
+ fsp->fd, fsp->fnum,
+ conn->num_files_open));
+
+ /*
+ * close_file() returns the unix errno if an error
+ * was detected on close - normally this is due to
+ * a disk full error. If not then it was probably an I/O error.
+ */
+
+ if((close_err = close_file(fsp,True)) != 0) {
+ errno = close_err;
+ END_PROFILE(SMBclose);
+ return (UNIXERROR(ERRHRD,ERRgeneral));
+ }
+
+ /*
+ * Now take care of any time sent in the close.
+ */
+
+ mtime = make_unix_date3(inbuf+smb_vwv1);
+
+ /* try and set the date */
+ set_filetime(conn, file_name, mtime);
+
+ }
+
+ /* We have a cached error */
+ if(eclass || err) {
+ END_PROFILE(SMBclose);
+ return ERROR_DOS(eclass,err);
+ }
+
+ END_PROFILE(SMBclose);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a writeclose (Core+ protocol).
+****************************************************************************/
+
+int reply_writeclose(connection_struct *conn,
+ char *inbuf,char *outbuf, int size, int dum_buffsize)
+{
+ size_t numtowrite;
+ ssize_t nwritten = -1;
+ int outsize = 0;
+ int close_err = 0;
+ SMB_OFF_T startpos;
+ char *data;
+ time_t mtime;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBwriteclose);
+
+ CHECK_FSP(fsp,conn);
+ CHECK_WRITE(fsp);
+
+ numtowrite = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv2);
+ mtime = make_unix_date3(inbuf+smb_vwv4);
+ data = smb_buf(inbuf) + 1;
+
+ if (numtowrite && is_locked(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK,False)) {
+ END_PROFILE(SMBwriteclose);
+ return ERROR_DOS(ERRDOS,ERRlock);
+ }
+
+ nwritten = write_file(fsp,data,startpos,numtowrite);
+
+ set_filetime(conn, fsp->fsp_name,mtime);
+
+ /*
+ * More insanity. W2K only closes the file if writelen > 0.
+ * JRA.
+ */
+
+ if (numtowrite) {
+ DEBUG(3,("reply_writeclose: zero length write doesn't close file %s\n",
+ fsp->fsp_name ));
+ close_err = close_file(fsp,True);
+ }
+
+ DEBUG(3,("writeclose fnum=%d num=%d wrote=%d (numopen=%d)\n",
+ fsp->fnum, (int)numtowrite, (int)nwritten,
+ conn->num_files_open));
+
+ if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) {
+ END_PROFILE(SMBwriteclose);
+ return(UNIXERROR(ERRHRD,ERRdiskfull));
+ }
+
+ if(close_err != 0) {
+ errno = close_err;
+ END_PROFILE(SMBwriteclose);
+ return(UNIXERROR(ERRHRD,ERRgeneral));
+ }
+
+ outsize = set_message(outbuf,1,0,True);
+
+ SSVAL(outbuf,smb_vwv0,nwritten);
+ END_PROFILE(SMBwriteclose);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a lock.
+****************************************************************************/
+
+int reply_lock(connection_struct *conn,
+ char *inbuf,char *outbuf, int length, int dum_buffsize)
+{
+ int outsize = set_message(outbuf,0,0,True);
+ SMB_BIG_UINT count,offset;
+ NTSTATUS status;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ BOOL my_lock_ctx = False;
+
+ START_PROFILE(SMBlock);
+
+ CHECK_FSP(fsp,conn);
+
+ release_level_2_oplocks_on_change(fsp);
+
+ count = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv1);
+ offset = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv3);
+
+ DEBUG(3,("lock fd=%d fnum=%d offset=%.0f count=%.0f\n",
+ fsp->fd, fsp->fnum, (double)offset, (double)count));
+
+ status = do_lock_spin(fsp, conn, SVAL(inbuf,smb_pid), count, offset, WRITE_LOCK, &my_lock_ctx);
+ if (NT_STATUS_V(status)) {
+#if 0
+ /* Tests using Samba4 against W2K show this call never creates a blocking lock. */
+ if (lp_blocking_locks(SNUM(conn)) && !my_lock_ctx && ERROR_WAS_LOCK_DENIED(status)) {
+ /*
+ * A blocking lock was requested. Package up
+ * this smb into a queued request and push it
+ * onto the blocking lock queue.
+ */
+ if(push_blocking_lock_request(inbuf, length, -1, 0, SVAL(inbuf,smb_pid), offset, count)) {
+ END_PROFILE(SMBlock);
+ return -1;
+ }
+ }
+#endif
+ END_PROFILE(SMBlock);
+ return ERROR_NT(status);
+ }
+
+ END_PROFILE(SMBlock);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a unlock.
+****************************************************************************/
+
+int reply_unlock(connection_struct *conn, char *inbuf,char *outbuf, int size,
+ int dum_buffsize)
+{
+ int outsize = set_message(outbuf,0,0,True);
+ SMB_BIG_UINT count,offset;
+ NTSTATUS status;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBunlock);
+
+ CHECK_FSP(fsp,conn);
+
+ count = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv1);
+ offset = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv3);
+
+ status = do_unlock(fsp, conn, SVAL(inbuf,smb_pid), count, offset);
+ if (NT_STATUS_V(status)) {
+ END_PROFILE(SMBunlock);
+ return ERROR_NT(status);
+ }
+
+ DEBUG( 3, ( "unlock fd=%d fnum=%d offset=%.0f count=%.0f\n",
+ fsp->fd, fsp->fnum, (double)offset, (double)count ) );
+
+ END_PROFILE(SMBunlock);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a tdis.
+****************************************************************************/
+
+int reply_tdis(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int outsize = set_message(outbuf,0,0,True);
+ uint16 vuid;
+ START_PROFILE(SMBtdis);
+
+ vuid = SVAL(inbuf,smb_uid);
+
+ if (!conn) {
+ DEBUG(4,("Invalid connection in tdis\n"));
+ END_PROFILE(SMBtdis);
+ return ERROR_DOS(ERRSRV,ERRinvnid);
+ }
+
+ conn->used = False;
+
+ close_cnum(conn,vuid);
+
+ END_PROFILE(SMBtdis);
+ return outsize;
+}
+
+/****************************************************************************
+ Reply to a echo.
+****************************************************************************/
+
+int reply_echo(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int smb_reverb = SVAL(inbuf,smb_vwv0);
+ int seq_num;
+ unsigned int data_len = smb_buflen(inbuf);
+ int outsize = set_message(outbuf,1,data_len,True);
+ START_PROFILE(SMBecho);
+
+ if (data_len > BUFFER_SIZE) {
+ DEBUG(0,("reply_echo: data_len too large.\n"));
+ END_PROFILE(SMBecho);
+ return -1;
+ }
+
+ /* copy any incoming data back out */
+ if (data_len > 0)
+ memcpy(smb_buf(outbuf),smb_buf(inbuf),data_len);
+
+ if (smb_reverb > 100) {
+ DEBUG(0,("large reverb (%d)?? Setting to 100\n",smb_reverb));
+ smb_reverb = 100;
+ }
+
+ for (seq_num =1 ; seq_num <= smb_reverb ; seq_num++) {
+ SSVAL(outbuf,smb_vwv0,seq_num);
+
+ smb_setlen(outbuf,outsize - 4);
+
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("reply_echo: send_smb failed.");
+ }
+
+ DEBUG(3,("echo %d times\n", smb_reverb));
+
+ smb_echo_count++;
+
+ END_PROFILE(SMBecho);
+ return -1;
+}
+
+/****************************************************************************
+ Reply to a printopen.
+****************************************************************************/
+
+int reply_printopen(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int outsize = 0;
+ files_struct *fsp;
+ START_PROFILE(SMBsplopen);
+
+ if (!CAN_PRINT(conn)) {
+ END_PROFILE(SMBsplopen);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ /* Open for exclusive use, write only. */
+ fsp = print_fsp_open(conn, NULL);
+
+ if (!fsp) {
+ END_PROFILE(SMBsplopen);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ outsize = set_message(outbuf,1,0,True);
+ SSVAL(outbuf,smb_vwv0,fsp->fnum);
+
+ DEBUG(3,("openprint fd=%d fnum=%d\n",
+ fsp->fd, fsp->fnum));
+
+ END_PROFILE(SMBsplopen);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a printclose.
+****************************************************************************/
+
+int reply_printclose(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int outsize = set_message(outbuf,0,0,True);
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ int close_err = 0;
+ START_PROFILE(SMBsplclose);
+
+ CHECK_FSP(fsp,conn);
+
+ if (!CAN_PRINT(conn)) {
+ END_PROFILE(SMBsplclose);
+ return ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+ }
+
+ DEBUG(3,("printclose fd=%d fnum=%d\n",
+ fsp->fd,fsp->fnum));
+
+ close_err = close_file(fsp,True);
+
+ if(close_err != 0) {
+ errno = close_err;
+ END_PROFILE(SMBsplclose);
+ return(UNIXERROR(ERRHRD,ERRgeneral));
+ }
+
+ END_PROFILE(SMBsplclose);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a printqueue.
+****************************************************************************/
+
+int reply_printqueue(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int outsize = set_message(outbuf,2,3,True);
+ int max_count = SVAL(inbuf,smb_vwv0);
+ int start_index = SVAL(inbuf,smb_vwv1);
+ START_PROFILE(SMBsplretq);
+
+ /* we used to allow the client to get the cnum wrong, but that
+ is really quite gross and only worked when there was only
+ one printer - I think we should now only accept it if they
+ get it right (tridge) */
+ if (!CAN_PRINT(conn)) {
+ END_PROFILE(SMBsplretq);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ SSVAL(outbuf,smb_vwv0,0);
+ SSVAL(outbuf,smb_vwv1,0);
+ SCVAL(smb_buf(outbuf),0,1);
+ SSVAL(smb_buf(outbuf),1,0);
+
+ DEBUG(3,("printqueue start_index=%d max_count=%d\n",
+ start_index, max_count));
+
+ {
+ print_queue_struct *queue = NULL;
+ print_status_struct status;
+ char *p = smb_buf(outbuf) + 3;
+ int count = print_queue_status(SNUM(conn), &queue, &status);
+ int num_to_get = ABS(max_count);
+ int first = (max_count>0?start_index:start_index+max_count+1);
+ int i;
+
+ if (first >= count)
+ num_to_get = 0;
+ else
+ num_to_get = MIN(num_to_get,count-first);
+
+
+ for (i=first;i<first+num_to_get;i++) {
+ put_dos_date2(p,0,queue[i].time);
+ SCVAL(p,4,(queue[i].status==LPQ_PRINTING?2:3));
+ SSVAL(p,5, queue[i].job);
+ SIVAL(p,7,queue[i].size);
+ SCVAL(p,11,0);
+ srvstr_push(outbuf, p+12, queue[i].fs_user, 16, STR_ASCII);
+ p += 28;
+ }
+
+ if (count > 0) {
+ outsize = set_message(outbuf,2,28*count+3,False);
+ SSVAL(outbuf,smb_vwv0,count);
+ SSVAL(outbuf,smb_vwv1,(max_count>0?first+count:first-1));
+ SCVAL(smb_buf(outbuf),0,1);
+ SSVAL(smb_buf(outbuf),1,28*count);
+ }
+
+ SAFE_FREE(queue);
+
+ DEBUG(3,("%d entries returned in queue\n",count));
+ }
+
+ END_PROFILE(SMBsplretq);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a printwrite.
+****************************************************************************/
+
+int reply_printwrite(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int numtowrite;
+ int outsize = set_message(outbuf,0,0,True);
+ char *data;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+
+ START_PROFILE(SMBsplwr);
+
+ if (!CAN_PRINT(conn)) {
+ END_PROFILE(SMBsplwr);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ CHECK_FSP(fsp,conn);
+ CHECK_WRITE(fsp);
+
+ numtowrite = SVAL(smb_buf(inbuf),1);
+ data = smb_buf(inbuf) + 3;
+
+ if (write_file(fsp,data,-1,numtowrite) != numtowrite) {
+ END_PROFILE(SMBsplwr);
+ return(UNIXERROR(ERRHRD,ERRdiskfull));
+ }
+
+ DEBUG( 3, ( "printwrite fnum=%d num=%d\n", fsp->fnum, numtowrite ) );
+
+ END_PROFILE(SMBsplwr);
+ return(outsize);
+}
+
+/****************************************************************************
+ The guts of the mkdir command, split out so it may be called by the NT SMB
+ code.
+****************************************************************************/
+
+NTSTATUS mkdir_internal(connection_struct *conn, pstring directory)
+{
+ BOOL bad_path = False;
+ SMB_STRUCT_STAT sbuf;
+ int ret= -1;
+
+ unix_convert(directory,conn,0,&bad_path,&sbuf);
+
+ if( strchr_m(directory, ':')) {
+ return NT_STATUS_NOT_A_DIRECTORY;
+ }
+
+ if (ms_has_wild(directory)) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+
+ if (check_name(directory, conn))
+ ret = vfs_MkDir(conn,directory,unix_mode(conn,aDIR,directory));
+
+ if (ret == -1) {
+ if(errno == ENOENT) {
+ if (bad_path)
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ else
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ return map_nt_error_from_unix(errno);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Reply to a mkdir.
+****************************************************************************/
+
+int reply_mkdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ pstring directory;
+ int outsize;
+ NTSTATUS status;
+ START_PROFILE(SMBmkdir);
+
+ srvstr_get_path(inbuf, directory, smb_buf(inbuf) + 1, sizeof(directory), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBmkdir);
+ return ERROR_NT(status);
+ }
+
+ RESOLVE_DFSPATH(directory, conn, inbuf, outbuf);
+
+ status = mkdir_internal(conn, directory);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBmkdir);
+ return ERROR_NT(status);
+ }
+
+ outsize = set_message(outbuf,0,0,True);
+
+ DEBUG( 3, ( "mkdir %s ret=%d\n", directory, outsize ) );
+
+ END_PROFILE(SMBmkdir);
+ return(outsize);
+}
+
+/****************************************************************************
+ Static function used by reply_rmdir to delete an entire directory
+ tree recursively. Return False on ok, True on fail.
+****************************************************************************/
+
+static BOOL recursive_rmdir(connection_struct *conn, char *directory)
+{
+ const char *dname = NULL;
+ BOOL ret = False;
+ void *dirptr = OpenDir(conn, directory, False);
+
+ if(dirptr == NULL)
+ return True;
+
+ while((dname = ReadDirName(dirptr))) {
+ pstring fullname;
+ SMB_STRUCT_STAT st;
+
+ if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0))
+ continue;
+
+ /* Construct the full name. */
+ if(strlen(directory) + strlen(dname) + 1 >= sizeof(fullname)) {
+ errno = ENOMEM;
+ ret = True;
+ break;
+ }
+
+ pstrcpy(fullname, directory);
+ pstrcat(fullname, "/");
+ pstrcat(fullname, dname);
+
+ if(SMB_VFS_LSTAT(conn,fullname, &st) != 0) {
+ ret = True;
+ break;
+ }
+
+ if(st.st_mode & S_IFDIR) {
+ if(recursive_rmdir(conn, fullname)!=0) {
+ ret = True;
+ break;
+ }
+ if(SMB_VFS_RMDIR(conn,fullname) != 0) {
+ ret = True;
+ break;
+ }
+ } else if(SMB_VFS_UNLINK(conn,fullname) != 0) {
+ ret = True;
+ break;
+ }
+ }
+ CloseDir(dirptr);
+ return ret;
+}
+
+/****************************************************************************
+ The internals of the rmdir code - called elsewhere.
+****************************************************************************/
+
+BOOL rmdir_internals(connection_struct *conn, char *directory)
+{
+ BOOL ok;
+
+ ok = (SMB_VFS_RMDIR(conn,directory) == 0);
+ if(!ok && ((errno == ENOTEMPTY)||(errno == EEXIST)) && lp_veto_files(SNUM(conn))) {
+ /*
+ * Check to see if the only thing in this directory are
+ * vetoed files/directories. If so then delete them and
+ * retry. If we fail to delete any of them (and we *don't*
+ * do a recursive delete) then fail the rmdir.
+ */
+ BOOL all_veto_files = True;
+ const char *dname;
+ void *dirptr = OpenDir(conn, directory, False);
+
+ if(dirptr != NULL) {
+ int dirpos = TellDir(dirptr);
+ while ((dname = ReadDirName(dirptr))) {
+ if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0))
+ continue;
+ if(!IS_VETO_PATH(conn, dname)) {
+ all_veto_files = False;
+ break;
+ }
+ }
+
+ if(all_veto_files) {
+ SeekDir(dirptr,dirpos);
+ while ((dname = ReadDirName(dirptr))) {
+ pstring fullname;
+ SMB_STRUCT_STAT st;
+
+ if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0))
+ continue;
+
+ /* Construct the full name. */
+ if(strlen(directory) + strlen(dname) + 1 >= sizeof(fullname)) {
+ errno = ENOMEM;
+ break;
+ }
+
+ pstrcpy(fullname, directory);
+ pstrcat(fullname, "/");
+ pstrcat(fullname, dname);
+
+ if(SMB_VFS_LSTAT(conn,fullname, &st) != 0)
+ break;
+ if(st.st_mode & S_IFDIR) {
+ if(lp_recursive_veto_delete(SNUM(conn))) {
+ if(recursive_rmdir(conn, fullname) != 0)
+ break;
+ }
+ if(SMB_VFS_RMDIR(conn,fullname) != 0)
+ break;
+ } else if(SMB_VFS_UNLINK(conn,fullname) != 0)
+ break;
+ }
+ CloseDir(dirptr);
+ /* Retry the rmdir */
+ ok = (SMB_VFS_RMDIR(conn,directory) == 0);
+ } else {
+ CloseDir(dirptr);
+ }
+ } else {
+ errno = ENOTEMPTY;
+ }
+ }
+
+ if (!ok)
+ DEBUG(3,("rmdir_internals: couldn't remove directory %s : %s\n", directory,strerror(errno)));
+
+ return ok;
+}
+
+/****************************************************************************
+ Reply to a rmdir.
+****************************************************************************/
+
+int reply_rmdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ pstring directory;
+ int outsize = 0;
+ BOOL ok = False;
+ BOOL bad_path = False;
+ SMB_STRUCT_STAT sbuf;
+ NTSTATUS status;
+ START_PROFILE(SMBrmdir);
+
+ srvstr_get_path(inbuf, directory, smb_buf(inbuf) + 1, sizeof(directory), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBrmdir);
+ return ERROR_NT(status);
+ }
+
+ RESOLVE_DFSPATH(directory, conn, inbuf, outbuf)
+
+ unix_convert(directory,conn, NULL,&bad_path,&sbuf);
+
+ if (check_name(directory,conn)) {
+ dptr_closepath(directory,SVAL(inbuf,smb_pid));
+ ok = rmdir_internals(conn, directory);
+ }
+
+ if (!ok) {
+ END_PROFILE(SMBrmdir);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRbadpath);
+ }
+
+ outsize = set_message(outbuf,0,0,True);
+
+ DEBUG( 3, ( "rmdir %s\n", directory ) );
+
+ END_PROFILE(SMBrmdir);
+ return(outsize);
+}
+
+/*******************************************************************
+ Resolve wildcards in a filename rename.
+ Note that name is in UNIX charset and thus potentially can be more
+ than fstring buffer (255 bytes) especially in default UTF-8 case.
+ Therefore, we use pstring inside and all calls should ensure that
+ name2 is at least pstring-long (they do already)
+********************************************************************/
+
+static BOOL resolve_wildcards(const char *name1, char *name2)
+{
+ pstring root1,root2;
+ pstring ext1,ext2;
+ char *p,*p2, *pname1, *pname2;
+ int available_space, actual_space;
+
+
+ pname1 = strrchr_m(name1,'/');
+ pname2 = strrchr_m(name2,'/');
+
+ if (!pname1 || !pname2)
+ return(False);
+
+ pstrcpy(root1,pname1);
+ pstrcpy(root2,pname2);
+ p = strrchr_m(root1,'.');
+ if (p) {
+ *p = 0;
+ pstrcpy(ext1,p+1);
+ } else {
+ pstrcpy(ext1,"");
+ }
+ p = strrchr_m(root2,'.');
+ if (p) {
+ *p = 0;
+ pstrcpy(ext2,p+1);
+ } else {
+ pstrcpy(ext2,"");
+ }
+
+ p = root1;
+ p2 = root2;
+ while (*p2) {
+ if (*p2 == '?') {
+ *p2 = *p;
+ p2++;
+ } else if (*p2 == '*') {
+ pstrcpy(p2, p);
+ break;
+ } else {
+ p2++;
+ }
+ if (*p)
+ p++;
+ }
+
+ p = ext1;
+ p2 = ext2;
+ while (*p2) {
+ if (*p2 == '?') {
+ *p2 = *p;
+ p2++;
+ } else if (*p2 == '*') {
+ pstrcpy(p2, p);
+ break;
+ } else {
+ p2++;
+ }
+ if (*p)
+ p++;
+ }
+
+ available_space = sizeof(pstring) - PTR_DIFF(pname2, name2);
+
+ if (ext2[0]) {
+ actual_space = snprintf(pname2, available_space - 1, "%s.%s", root2, ext2);
+ if (actual_space >= available_space - 1) {
+ DEBUG(1,("resolve_wildcards: can't fit resolved name into specified buffer (overrun by %d bytes)\n",
+ actual_space - available_space));
+ }
+ } else {
+ pstrcpy_base(pname2, root2, name2);
+ }
+
+ return(True);
+}
+
+/****************************************************************************
+ Ensure open files have their names updates.
+****************************************************************************/
+
+static void rename_open_files(connection_struct *conn, SMB_DEV_T dev, SMB_INO_T inode, char *newname)
+{
+ files_struct *fsp;
+ BOOL did_rename = False;
+
+ for(fsp = file_find_di_first(dev, inode); fsp; fsp = file_find_di_next(fsp)) {
+ DEBUG(10,("rename_open_files: renaming file fnum %d (dev = %x, inode = %.0f) from %s -> %s\n",
+ fsp->fnum, (unsigned int)fsp->dev, (double)fsp->inode,
+ fsp->fsp_name, newname ));
+ string_set(&fsp->fsp_name, newname);
+ did_rename = True;
+ }
+
+ if (!did_rename)
+ DEBUG(10,("rename_open_files: no open files on dev %x, inode %.0f for %s\n",
+ (unsigned int)dev, (double)inode, newname ));
+}
+
+/****************************************************************************
+ Rename an open file - given an fsp.
+****************************************************************************/
+
+NTSTATUS rename_internals_fsp(connection_struct *conn, files_struct *fsp, char *newname, BOOL replace_if_exists)
+{
+ SMB_STRUCT_STAT sbuf;
+ BOOL bad_path = False;
+ pstring newname_last_component;
+ NTSTATUS error = NT_STATUS_OK;
+ BOOL dest_exists;
+ BOOL rcdest = True;
+
+ ZERO_STRUCT(sbuf);
+ rcdest = unix_convert(newname,conn,newname_last_component,&bad_path,&sbuf);
+
+ /* Quick check for "." and ".." */
+ if (!bad_path && newname_last_component[0] == '.') {
+ if (!newname_last_component[1] || (newname_last_component[1] == '.' && !newname_last_component[2])) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+ if (!rcdest && bad_path) {
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+
+ /* Ensure newname contains a '/' */
+ if(strrchr_m(newname,'/') == 0) {
+ pstring tmpstr;
+
+ pstrcpy(tmpstr, "./");
+ pstrcat(tmpstr, newname);
+ pstrcpy(newname, tmpstr);
+ }
+
+ /*
+ * Check for special case with case preserving and not
+ * case sensitive. If the old last component differs from the original
+ * last component only by case, then we should allow
+ * the rename (user is trying to change the case of the
+ * filename).
+ */
+
+ if((case_sensitive == False) && (case_preserve == True) &&
+ strequal(newname, fsp->fsp_name)) {
+ char *p;
+ pstring newname_modified_last_component;
+
+ /*
+ * Get the last component of the modified name.
+ * Note that we guarantee that newname contains a '/'
+ * character above.
+ */
+ p = strrchr_m(newname,'/');
+ pstrcpy(newname_modified_last_component,p+1);
+
+ if(strcsequal(newname_modified_last_component,
+ newname_last_component) == False) {
+ /*
+ * Replace the modified last component with
+ * the original.
+ */
+ pstrcpy(p+1, newname_last_component);
+ }
+ }
+
+ /*
+ * If the src and dest names are identical - including case,
+ * don't do the rename, just return success.
+ */
+
+ if (strcsequal(fsp->fsp_name, newname)) {
+ DEBUG(3,("rename_internals_fsp: identical names in rename %s - returning success\n",
+ newname));
+ return NT_STATUS_OK;
+ }
+
+ dest_exists = vfs_object_exist(conn,newname,NULL);
+
+ if(!replace_if_exists && dest_exists) {
+ DEBUG(3,("rename_internals_fsp: dest exists doing rename %s -> %s\n",
+ fsp->fsp_name,newname));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ error = can_rename(newname,conn,&sbuf);
+
+ if (dest_exists && !NT_STATUS_IS_OK(error)) {
+ DEBUG(3,("rename_internals: Error %s rename %s -> %s\n",
+ nt_errstr(error), fsp->fsp_name,newname));
+ if (NT_STATUS_EQUAL(error,NT_STATUS_SHARING_VIOLATION))
+ error = NT_STATUS_ACCESS_DENIED;
+ return error;
+ }
+
+ if(SMB_VFS_RENAME(conn,fsp->fsp_name, newname) == 0) {
+ DEBUG(3,("rename_internals_fsp: succeeded doing rename on %s -> %s\n",
+ fsp->fsp_name,newname));
+ rename_open_files(conn, fsp->dev, fsp->inode, newname);
+ return NT_STATUS_OK;
+ }
+
+ if (errno == ENOTDIR || errno == EISDIR)
+ error = NT_STATUS_OBJECT_NAME_COLLISION;
+ else
+ error = map_nt_error_from_unix(errno);
+
+ DEBUG(3,("rename_internals_fsp: Error %s rename %s -> %s\n",
+ nt_errstr(error), fsp->fsp_name,newname));
+
+ return error;
+}
+
+/****************************************************************************
+ The guts of the rename command, split out so it may be called by the NT SMB
+ code.
+****************************************************************************/
+
+NTSTATUS rename_internals(connection_struct *conn, char *name, char *newname, uint16 attrs, BOOL replace_if_exists)
+{
+ pstring directory;
+ pstring mask;
+ pstring last_component_src;
+ pstring last_component_dest;
+ char *p;
+ BOOL has_wild;
+ BOOL bad_path_src = False;
+ BOOL bad_path_dest = False;
+ int count=0;
+ NTSTATUS error = NT_STATUS_OK;
+ BOOL rc = True;
+ BOOL rcdest = True;
+ SMB_STRUCT_STAT sbuf1, sbuf2;
+
+ *directory = *mask = 0;
+
+ ZERO_STRUCT(sbuf1);
+ ZERO_STRUCT(sbuf2);
+
+ rc = unix_convert(name,conn,last_component_src,&bad_path_src,&sbuf1);
+ if (!rc && bad_path_src) {
+ if (ms_has_wild(last_component_src))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+
+ /* Quick check for "." and ".." */
+ if (last_component_src[0] == '.') {
+ if (!last_component_src[1] || (last_component_src[1] == '.' && !last_component_src[2])) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ }
+
+ rcdest = unix_convert(newname,conn,last_component_dest,&bad_path_dest,&sbuf2);
+
+ /* Quick check for "." and ".." */
+ if (last_component_dest[0] == '.') {
+ if (!last_component_dest[1] || (last_component_dest[1] == '.' && !last_component_dest[2])) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ }
+
+ /*
+ * Split the old name into directory and last component
+ * strings. Note that unix_convert may have stripped off a
+ * leading ./ from both name and newname if the rename is
+ * at the root of the share. We need to make sure either both
+ * name and newname contain a / character or neither of them do
+ * as this is checked in resolve_wildcards().
+ */
+
+ p = strrchr_m(name,'/');
+ if (!p) {
+ pstrcpy(directory,".");
+ pstrcpy(mask,name);
+ } else {
+ *p = 0;
+ pstrcpy(directory,name);
+ pstrcpy(mask,p+1);
+ *p = '/'; /* Replace needed for exceptional test below. */
+ }
+
+ /*
+ * We should only check the mangled cache
+ * here if unix_convert failed. This means
+ * that the path in 'mask' doesn't exist
+ * on the file system and so we need to look
+ * for a possible mangle. This patch from
+ * Tine Smukavec <valentin.smukavec@hermes.si>.
+ */
+
+ if (!rc && mangle_is_mangled(mask))
+ mangle_check_cache( mask );
+
+ has_wild = ms_has_wild(mask);
+
+ if (!has_wild) {
+ /*
+ * No wildcards - just process the one file.
+ */
+ BOOL is_short_name = mangle_is_8_3(name, True);
+
+ /* Add a terminating '/' to the directory name. */
+ pstrcat(directory,"/");
+ pstrcat(directory,mask);
+
+ /* Ensure newname contains a '/' also */
+ if(strrchr_m(newname,'/') == 0) {
+ pstring tmpstr;
+
+ pstrcpy(tmpstr, "./");
+ pstrcat(tmpstr, newname);
+ pstrcpy(newname, tmpstr);
+ }
+
+ DEBUG(3,("rename_internals: case_sensitive = %d, case_preserve = %d, short case preserve = %d, \
+directory = %s, newname = %s, last_component_dest = %s, is_8_3 = %d\n",
+ case_sensitive, case_preserve, short_case_preserve, directory,
+ newname, last_component_dest, is_short_name));
+
+ /*
+ * Check for special case with case preserving and not
+ * case sensitive, if directory and newname are identical,
+ * and the old last component differs from the original
+ * last component only by case, then we should allow
+ * the rename (user is trying to change the case of the
+ * filename).
+ */
+ if((case_sensitive == False) &&
+ (((case_preserve == True) &&
+ (is_short_name == False)) ||
+ ((short_case_preserve == True) &&
+ (is_short_name == True))) &&
+ strcsequal(directory, newname)) {
+ pstring modified_last_component;
+
+ /*
+ * Get the last component of the modified name.
+ * Note that we guarantee that newname contains a '/'
+ * character above.
+ */
+ p = strrchr_m(newname,'/');
+ pstrcpy(modified_last_component,p+1);
+
+ if(strcsequal(modified_last_component,
+ last_component_dest) == False) {
+ /*
+ * Replace the modified last component with
+ * the original.
+ */
+ pstrcpy(p+1, last_component_dest);
+ }
+ }
+
+ resolve_wildcards(directory,newname);
+
+ /*
+ * The source object must exist.
+ */
+
+ if (!vfs_object_exist(conn, directory, &sbuf1)) {
+ DEBUG(3,("rename_internals: source doesn't exist doing rename %s -> %s\n",
+ directory,newname));
+
+ if (errno == ENOTDIR || errno == EISDIR || errno == ENOENT) {
+ /*
+ * Must return different errors depending on whether the parent
+ * directory existed or not.
+ */
+
+ p = strrchr_m(directory, '/');
+ if (!p)
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ *p = '\0';
+ if (vfs_object_exist(conn, directory, NULL))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+ error = map_nt_error_from_unix(errno);
+ DEBUG(3,("rename_internals: Error %s rename %s -> %s\n",
+ nt_errstr(error), directory,newname));
+
+ return error;
+ }
+
+ if (!rcdest && bad_path_dest) {
+ if (ms_has_wild(last_component_dest))
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+
+ error = can_rename(directory,conn,&sbuf1);
+
+ if (!NT_STATUS_IS_OK(error)) {
+ DEBUG(3,("rename_internals: Error %s rename %s -> %s\n",
+ nt_errstr(error), directory,newname));
+ return error;
+ }
+
+ /*
+ * If the src and dest names are identical - including case,
+ * don't do the rename, just return success.
+ */
+
+ if (strcsequal(directory, newname)) {
+ rename_open_files(conn, sbuf1.st_dev, sbuf1.st_ino, newname);
+ DEBUG(3,("rename_internals: identical names in rename %s - returning success\n", directory));
+ return NT_STATUS_OK;
+ }
+
+ if(!replace_if_exists && vfs_object_exist(conn,newname,NULL)) {
+ DEBUG(3,("rename_internals: dest exists doing rename %s -> %s\n",
+ directory,newname));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ if(SMB_VFS_RENAME(conn,directory, newname) == 0) {
+ DEBUG(3,("rename_internals: succeeded doing rename on %s -> %s\n",
+ directory,newname));
+ rename_open_files(conn, sbuf1.st_dev, sbuf1.st_ino, newname);
+ return NT_STATUS_OK;
+ }
+
+ if (errno == ENOTDIR || errno == EISDIR)
+ error = NT_STATUS_OBJECT_NAME_COLLISION;
+ else
+ error = map_nt_error_from_unix(errno);
+
+ DEBUG(3,("rename_internals: Error %s rename %s -> %s\n",
+ nt_errstr(error), directory,newname));
+
+ return error;
+ } else {
+ /*
+ * Wildcards - process each file that matches.
+ */
+ void *dirptr = NULL;
+ const char *dname;
+ pstring destname;
+
+ if (check_name(directory,conn))
+ dirptr = OpenDir(conn, directory, True);
+
+ if (dirptr) {
+ error = NT_STATUS_NO_SUCH_FILE;
+/* Was error = NT_STATUS_OBJECT_NAME_NOT_FOUND; - gentest fix. JRA */
+
+ if (strequal(mask,"????????.???"))
+ pstrcpy(mask,"*");
+
+ while ((dname = ReadDirName(dirptr))) {
+ pstring fname;
+ BOOL sysdir_entry = False;
+
+ pstrcpy(fname,dname);
+
+ /* Quick check for "." and ".." */
+ if (fname[0] == '.') {
+ if (!fname[1] || (fname[1] == '.' && !fname[2])) {
+ if (attrs & aDIR) {
+ sysdir_entry = True;
+ } else {
+ continue;
+ }
+ }
+ }
+
+ if(!mask_match(fname, mask, case_sensitive))
+ continue;
+
+ if (sysdir_entry) {
+ error = NT_STATUS_OBJECT_NAME_INVALID;
+ break;
+ }
+
+ error = NT_STATUS_ACCESS_DENIED;
+ slprintf(fname,sizeof(fname)-1,"%s/%s",directory,dname);
+ if (!vfs_object_exist(conn, fname, &sbuf1)) {
+ error = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ DEBUG(6,("rename %s failed. Error %s\n", fname, nt_errstr(error)));
+ continue;
+ }
+ error = can_rename(fname,conn,&sbuf1);
+ if (!NT_STATUS_IS_OK(error)) {
+ DEBUG(6,("rename %s refused\n", fname));
+ continue;
+ }
+ pstrcpy(destname,newname);
+
+ if (!resolve_wildcards(fname,destname)) {
+ DEBUG(6,("resolve_wildcards %s %s failed\n",
+ fname, destname));
+ continue;
+ }
+
+ if (strcsequal(fname,destname)) {
+ rename_open_files(conn, sbuf1.st_dev, sbuf1.st_ino, newname);
+ DEBUG(3,("rename_internals: identical names in wildcard rename %s - success\n", fname));
+ count++;
+ error = NT_STATUS_OK;
+ continue;
+ }
+
+ if (!replace_if_exists &&
+ vfs_file_exist(conn,destname, NULL)) {
+ DEBUG(6,("file_exist %s\n", destname));
+ error = NT_STATUS_OBJECT_NAME_COLLISION;
+ continue;
+ }
+
+ if (!SMB_VFS_RENAME(conn,fname,destname)) {
+ rename_open_files(conn, sbuf1.st_dev, sbuf1.st_ino, newname);
+ count++;
+ error = NT_STATUS_OK;
+ }
+ DEBUG(3,("rename_internals: doing rename on %s -> %s\n",fname,destname));
+ }
+ CloseDir(dirptr);
+ }
+
+ if (!NT_STATUS_EQUAL(error,NT_STATUS_NO_SUCH_FILE)) {
+ if (!rcdest && bad_path_dest) {
+ if (ms_has_wild(last_component_dest))
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+ }
+ }
+
+ if (count == 0 && NT_STATUS_IS_OK(error)) {
+ error = map_nt_error_from_unix(errno);
+ }
+
+ return error;
+}
+
+/****************************************************************************
+ Reply to a mv.
+****************************************************************************/
+
+int reply_mv(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
+ int dum_buffsize)
+{
+ int outsize = 0;
+ pstring name;
+ pstring newname;
+ char *p;
+ uint16 attrs = SVAL(inbuf,smb_vwv0);
+ NTSTATUS status;
+
+ START_PROFILE(SMBmv);
+
+ p = smb_buf(inbuf) + 1;
+ p += srvstr_get_path(inbuf, name, p, sizeof(name), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBmv);
+ return ERROR_NT(status);
+ }
+ p++;
+ p += srvstr_get_path(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBmv);
+ return ERROR_NT(status);
+ }
+
+ RESOLVE_DFSPATH(name, conn, inbuf, outbuf);
+ RESOLVE_DFSPATH(newname, conn, inbuf, outbuf);
+
+ DEBUG(3,("reply_mv : %s -> %s\n",name,newname));
+
+ status = rename_internals(conn, name, newname, attrs, False);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBmv);
+ return ERROR_NT(status);
+ }
+
+ /*
+ * Win2k needs a changenotify request response before it will
+ * update after a rename..
+ */
+ process_pending_change_notify_queue((time_t)0);
+ outsize = set_message(outbuf,0,0,True);
+
+ END_PROFILE(SMBmv);
+ return(outsize);
+}
+
+/*******************************************************************
+ Copy a file as part of a reply_copy.
+******************************************************************/
+
+static BOOL copy_file(char *src,char *dest1,connection_struct *conn, int ofun,
+ int count,BOOL target_is_directory, int *err_ret)
+{
+ int Access,action;
+ SMB_STRUCT_STAT src_sbuf, sbuf2;
+ SMB_OFF_T ret=-1;
+ files_struct *fsp1,*fsp2;
+ pstring dest;
+ uint32 dosattrs;
+
+ *err_ret = 0;
+
+ pstrcpy(dest,dest1);
+ if (target_is_directory) {
+ char *p = strrchr_m(src,'/');
+ if (p)
+ p++;
+ else
+ p = src;
+ pstrcat(dest,"/");
+ pstrcat(dest,p);
+ }
+
+ if (!vfs_file_exist(conn,src,&src_sbuf))
+ return(False);
+
+ fsp1 = open_file_shared(conn,src,&src_sbuf,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_RDONLY),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),FILE_ATTRIBUTE_NORMAL,0,&Access,&action);
+
+ if (!fsp1)
+ return(False);
+
+ if (!target_is_directory && count)
+ ofun = FILE_EXISTS_OPEN;
+
+ dosattrs = dos_mode(conn, src, &src_sbuf);
+ if (SMB_VFS_STAT(conn,dest,&sbuf2) == -1)
+ ZERO_STRUCTP(&sbuf2);
+
+ fsp2 = open_file_shared(conn,dest,&sbuf2,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_WRONLY),
+ ofun,dosattrs,0,&Access,&action);
+
+ if (!fsp2) {
+ close_file(fsp1,False);
+ return(False);
+ }
+
+ if ((ofun&3) == 1) {
+ if(SMB_VFS_LSEEK(fsp2,fsp2->fd,0,SEEK_END) == -1) {
+ DEBUG(0,("copy_file: error - vfs lseek returned error %s\n", strerror(errno) ));
+ /*
+ * Stop the copy from occurring.
+ */
+ ret = -1;
+ src_sbuf.st_size = 0;
+ }
+ }
+
+ if (src_sbuf.st_size)
+ ret = vfs_transfer_file(fsp1, fsp2, src_sbuf.st_size);
+
+ close_file(fsp1,False);
+
+ /* Ensure the modtime is set correctly on the destination file. */
+ fsp2->pending_modtime = src_sbuf.st_mtime;
+
+ /*
+ * As we are opening fsp1 read-only we only expect
+ * an error on close on fsp2 if we are out of space.
+ * Thus we don't look at the error return from the
+ * close of fsp1.
+ */
+ *err_ret = close_file(fsp2,False);
+
+ return(ret == (SMB_OFF_T)src_sbuf.st_size);
+}
+
+/****************************************************************************
+ Reply to a file copy.
+****************************************************************************/
+
+int reply_copy(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int outsize = 0;
+ pstring name;
+ pstring directory;
+ pstring mask,newname;
+ char *p;
+ int count=0;
+ int error = ERRnoaccess;
+ int err = 0;
+ BOOL has_wild;
+ BOOL exists=False;
+ int tid2 = SVAL(inbuf,smb_vwv0);
+ int ofun = SVAL(inbuf,smb_vwv1);
+ int flags = SVAL(inbuf,smb_vwv2);
+ BOOL target_is_directory=False;
+ BOOL bad_path1 = False;
+ BOOL bad_path2 = False;
+ BOOL rc = True;
+ SMB_STRUCT_STAT sbuf1, sbuf2;
+ NTSTATUS status;
+
+ START_PROFILE(SMBcopy);
+
+ *directory = *mask = 0;
+
+ p = smb_buf(inbuf);
+ p += srvstr_get_path(inbuf, name, p, sizeof(name), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBcopy);
+ return ERROR_NT(status);
+ }
+ p += srvstr_get_path(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBcopy);
+ return ERROR_NT(status);
+ }
+
+ DEBUG(3,("reply_copy : %s -> %s\n",name,newname));
+
+ if (tid2 != conn->cnum) {
+ /* can't currently handle inter share copies XXXX */
+ DEBUG(3,("Rejecting inter-share copy\n"));
+ END_PROFILE(SMBcopy);
+ return ERROR_DOS(ERRSRV,ERRinvdevice);
+ }
+
+ RESOLVE_DFSPATH(name, conn, inbuf, outbuf);
+ RESOLVE_DFSPATH(newname, conn, inbuf, outbuf);
+
+ rc = unix_convert(name,conn,0,&bad_path1,&sbuf1);
+ unix_convert(newname,conn,0,&bad_path2,&sbuf2);
+
+ target_is_directory = VALID_STAT_OF_DIR(sbuf2);
+
+ if ((flags&1) && target_is_directory) {
+ END_PROFILE(SMBcopy);
+ return ERROR_DOS(ERRDOS,ERRbadfile);
+ }
+
+ if ((flags&2) && !target_is_directory) {
+ END_PROFILE(SMBcopy);
+ return ERROR_DOS(ERRDOS,ERRbadpath);
+ }
+
+ if ((flags&(1<<5)) && VALID_STAT_OF_DIR(sbuf1)) {
+ /* wants a tree copy! XXXX */
+ DEBUG(3,("Rejecting tree copy\n"));
+ END_PROFILE(SMBcopy);
+ return ERROR_DOS(ERRSRV,ERRerror);
+ }
+
+ p = strrchr_m(name,'/');
+ if (!p) {
+ pstrcpy(directory,"./");
+ pstrcpy(mask,name);
+ } else {
+ *p = 0;
+ pstrcpy(directory,name);
+ pstrcpy(mask,p+1);
+ }
+
+ /*
+ * We should only check the mangled cache
+ * here if unix_convert failed. This means
+ * that the path in 'mask' doesn't exist
+ * on the file system and so we need to look
+ * for a possible mangle. This patch from
+ * Tine Smukavec <valentin.smukavec@hermes.si>.
+ */
+
+ if (!rc && mangle_is_mangled(mask))
+ mangle_check_cache( mask );
+
+ has_wild = ms_has_wild(mask);
+
+ if (!has_wild) {
+ pstrcat(directory,"/");
+ pstrcat(directory,mask);
+ if (resolve_wildcards(directory,newname) &&
+ copy_file(directory,newname,conn,ofun, count,target_is_directory,&err))
+ count++;
+ if(!count && err) {
+ errno = err;
+ END_PROFILE(SMBcopy);
+ return(UNIXERROR(ERRHRD,ERRgeneral));
+ }
+ if (!count) {
+ exists = vfs_file_exist(conn,directory,NULL);
+ }
+ } else {
+ void *dirptr = NULL;
+ const char *dname;
+ pstring destname;
+
+ if (check_name(directory,conn))
+ dirptr = OpenDir(conn, directory, True);
+
+ if (dirptr) {
+ error = ERRbadfile;
+
+ if (strequal(mask,"????????.???"))
+ pstrcpy(mask,"*");
+
+ while ((dname = ReadDirName(dirptr))) {
+ pstring fname;
+ pstrcpy(fname,dname);
+
+ if(!mask_match(fname, mask, case_sensitive))
+ continue;
+
+ error = ERRnoaccess;
+ slprintf(fname,sizeof(fname)-1, "%s/%s",directory,dname);
+ pstrcpy(destname,newname);
+ if (resolve_wildcards(fname,destname) &&
+ copy_file(fname,destname,conn,ofun,
+ count,target_is_directory,&err))
+ count++;
+ DEBUG(3,("reply_copy : doing copy on %s -> %s\n",fname,destname));
+ }
+ CloseDir(dirptr);
+ }
+ }
+
+ if (count == 0) {
+ if(err) {
+ /* Error on close... */
+ errno = err;
+ END_PROFILE(SMBcopy);
+ return(UNIXERROR(ERRHRD,ERRgeneral));
+ }
+
+ if (exists) {
+ END_PROFILE(SMBcopy);
+ return ERROR_DOS(ERRDOS,error);
+ } else {
+ if((errno == ENOENT) && (bad_path1 || bad_path2)) {
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadpath;
+ }
+ END_PROFILE(SMBcopy);
+ return(UNIXERROR(ERRDOS,error));
+ }
+ }
+
+ outsize = set_message(outbuf,1,0,True);
+ SSVAL(outbuf,smb_vwv0,count);
+
+ END_PROFILE(SMBcopy);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a setdir.
+****************************************************************************/
+
+int reply_setdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int snum;
+ int outsize = 0;
+ BOOL ok = False;
+ pstring newdir;
+ NTSTATUS status;
+
+ START_PROFILE(pathworks_setdir);
+
+ snum = SNUM(conn);
+ if (!CAN_SETDIR(snum)) {
+ END_PROFILE(pathworks_setdir);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ srvstr_get_path(inbuf, newdir, smb_buf(inbuf) + 1, sizeof(newdir), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(pathworks_setdir);
+ return ERROR_NT(status);
+ }
+
+ if (strlen(newdir) == 0) {
+ ok = True;
+ } else {
+ ok = vfs_directory_exist(conn,newdir,NULL);
+ if (ok)
+ string_set(&conn->connectpath,newdir);
+ }
+
+ if (!ok) {
+ END_PROFILE(pathworks_setdir);
+ return ERROR_DOS(ERRDOS,ERRbadpath);
+ }
+
+ outsize = set_message(outbuf,0,0,True);
+ SCVAL(outbuf,smb_reh,CVAL(inbuf,smb_reh));
+
+ DEBUG(3,("setdir %s\n", newdir));
+
+ END_PROFILE(pathworks_setdir);
+ return(outsize);
+}
+
+/****************************************************************************
+ Get a lock pid, dealing with large count requests.
+****************************************************************************/
+
+uint16 get_lock_pid( char *data, int data_offset, BOOL large_file_format)
+{
+ if(!large_file_format)
+ return SVAL(data,SMB_LPID_OFFSET(data_offset));
+ else
+ return SVAL(data,SMB_LARGE_LPID_OFFSET(data_offset));
+}
+
+/****************************************************************************
+ Get a lock count, dealing with large count requests.
+****************************************************************************/
+
+SMB_BIG_UINT get_lock_count( char *data, int data_offset, BOOL large_file_format)
+{
+ SMB_BIG_UINT count = 0;
+
+ if(!large_file_format) {
+ count = (SMB_BIG_UINT)IVAL(data,SMB_LKLEN_OFFSET(data_offset));
+ } else {
+
+#if defined(HAVE_LONGLONG)
+ count = (((SMB_BIG_UINT) IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset))) << 32) |
+ ((SMB_BIG_UINT) IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset)));
+#else /* HAVE_LONGLONG */
+
+ /*
+ * NT4.x seems to be broken in that it sends large file (64 bit)
+ * lockingX calls even if the CAP_LARGE_FILES was *not*
+ * negotiated. For boxes without large unsigned ints truncate the
+ * lock count by dropping the top 32 bits.
+ */
+
+ if(IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset)) != 0) {
+ DEBUG(3,("get_lock_count: truncating lock count (high)0x%x (low)0x%x to just low count.\n",
+ (unsigned int)IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset)),
+ (unsigned int)IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset)) ));
+ SIVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset),0);
+ }
+
+ count = (SMB_BIG_UINT)IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset));
+#endif /* HAVE_LONGLONG */
+ }
+
+ return count;
+}
+
+#if !defined(HAVE_LONGLONG)
+/****************************************************************************
+ Pathetically try and map a 64 bit lock offset into 31 bits. I hate Windows :-).
+****************************************************************************/
+
+static uint32 map_lock_offset(uint32 high, uint32 low)
+{
+ unsigned int i;
+ uint32 mask = 0;
+ uint32 highcopy = high;
+
+ /*
+ * Try and find out how many significant bits there are in high.
+ */
+
+ for(i = 0; highcopy; i++)
+ highcopy >>= 1;
+
+ /*
+ * We use 31 bits not 32 here as POSIX
+ * lock offsets may not be negative.
+ */
+
+ mask = (~0) << (31 - i);
+
+ if(low & mask)
+ return 0; /* Fail. */
+
+ high <<= (31 - i);
+
+ return (high|low);
+}
+#endif /* !defined(HAVE_LONGLONG) */
+
+/****************************************************************************
+ Get a lock offset, dealing with large offset requests.
+****************************************************************************/
+
+SMB_BIG_UINT get_lock_offset( char *data, int data_offset, BOOL large_file_format, BOOL *err)
+{
+ SMB_BIG_UINT offset = 0;
+
+ *err = False;
+
+ if(!large_file_format) {
+ offset = (SMB_BIG_UINT)IVAL(data,SMB_LKOFF_OFFSET(data_offset));
+ } else {
+
+#if defined(HAVE_LONGLONG)
+ offset = (((SMB_BIG_UINT) IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset))) << 32) |
+ ((SMB_BIG_UINT) IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset)));
+#else /* HAVE_LONGLONG */
+
+ /*
+ * NT4.x seems to be broken in that it sends large file (64 bit)
+ * lockingX calls even if the CAP_LARGE_FILES was *not*
+ * negotiated. For boxes without large unsigned ints mangle the
+ * lock offset by mapping the top 32 bits onto the lower 32.
+ */
+
+ if(IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset)) != 0) {
+ uint32 low = IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset));
+ uint32 high = IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset));
+ uint32 new_low = 0;
+
+ if((new_low = map_lock_offset(high, low)) == 0) {
+ *err = True;
+ return (SMB_BIG_UINT)-1;
+ }
+
+ DEBUG(3,("get_lock_offset: truncating lock offset (high)0x%x (low)0x%x to offset 0x%x.\n",
+ (unsigned int)high, (unsigned int)low, (unsigned int)new_low ));
+ SIVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset),0);
+ SIVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset),new_low);
+ }
+
+ offset = (SMB_BIG_UINT)IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset));
+#endif /* HAVE_LONGLONG */
+ }
+
+ return offset;
+}
+
+/****************************************************************************
+ Reply to a lockingX request.
+****************************************************************************/
+
+int reply_lockingX(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize)
+{
+ files_struct *fsp = file_fsp(inbuf,smb_vwv2);
+ unsigned char locktype = CVAL(inbuf,smb_vwv3);
+ unsigned char oplocklevel = CVAL(inbuf,smb_vwv3+1);
+ uint16 num_ulocks = SVAL(inbuf,smb_vwv6);
+ uint16 num_locks = SVAL(inbuf,smb_vwv7);
+ SMB_BIG_UINT count = 0, offset = 0;
+ uint16 lock_pid;
+ int32 lock_timeout = IVAL(inbuf,smb_vwv4);
+ int i;
+ char *data;
+ BOOL large_file_format = (locktype & LOCKING_ANDX_LARGE_FILES)?True:False;
+ BOOL err;
+ BOOL my_lock_ctx = False;
+ NTSTATUS status;
+
+ START_PROFILE(SMBlockingX);
+
+ CHECK_FSP(fsp,conn);
+
+ data = smb_buf(inbuf);
+
+ if (locktype & (LOCKING_ANDX_CANCEL_LOCK | LOCKING_ANDX_CHANGE_LOCKTYPE)) {
+ /* we don't support these - and CANCEL_LOCK makes w2k
+ and XP reboot so I don't really want to be
+ compatible! (tridge) */
+ return ERROR_NT(NT_STATUS_NOT_SUPPORTED);
+ }
+
+ /* Check if this is an oplock break on a file
+ we have granted an oplock on.
+ */
+ if ((locktype & LOCKING_ANDX_OPLOCK_RELEASE)) {
+ /* Client can insist on breaking to none. */
+ BOOL break_to_none = (oplocklevel == 0);
+
+ DEBUG(5,("reply_lockingX: oplock break reply (%u) from client for fnum = %d\n",
+ (unsigned int)oplocklevel, fsp->fnum ));
+
+ /*
+ * Make sure we have granted an exclusive or batch oplock on this file.
+ */
+
+ if(!EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
+ DEBUG(0,("reply_lockingX: Error : oplock break from client for fnum = %d and \
+no oplock granted on this file (%s).\n", fsp->fnum, fsp->fsp_name));
+
+ /* if this is a pure oplock break request then don't send a reply */
+ if (num_locks == 0 && num_ulocks == 0) {
+ END_PROFILE(SMBlockingX);
+ return -1;
+ } else {
+ END_PROFILE(SMBlockingX);
+ return ERROR_DOS(ERRDOS,ERRlock);
+ }
+ }
+
+ if (remove_oplock(fsp, break_to_none) == False) {
+ DEBUG(0,("reply_lockingX: error in removing oplock on file %s\n",
+ fsp->fsp_name ));
+ }
+
+ /* if this is a pure oplock break request then don't send a reply */
+ if (num_locks == 0 && num_ulocks == 0) {
+ /* Sanity check - ensure a pure oplock break is not a
+ chained request. */
+ if(CVAL(inbuf,smb_vwv0) != 0xff)
+ DEBUG(0,("reply_lockingX: Error : pure oplock break is a chained %d request !\n",
+ (unsigned int)CVAL(inbuf,smb_vwv0) ));
+ END_PROFILE(SMBlockingX);
+ return -1;
+ }
+ }
+
+ /*
+ * We do this check *after* we have checked this is not a oplock break
+ * response message. JRA.
+ */
+
+ release_level_2_oplocks_on_change(fsp);
+
+ /* Data now points at the beginning of the list
+ of smb_unlkrng structs */
+ for(i = 0; i < (int)num_ulocks; i++) {
+ lock_pid = get_lock_pid( data, i, large_file_format);
+ count = get_lock_count( data, i, large_file_format);
+ offset = get_lock_offset( data, i, large_file_format, &err);
+
+ /*
+ * There is no error code marked "stupid client bug".... :-).
+ */
+ if(err) {
+ END_PROFILE(SMBlockingX);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ DEBUG(10,("reply_lockingX: unlock start=%.0f, len=%.0f for pid %u, file %s\n",
+ (double)offset, (double)count, (unsigned int)lock_pid, fsp->fsp_name ));
+
+ status = do_unlock(fsp,conn,lock_pid,count,offset);
+ if (NT_STATUS_V(status)) {
+ END_PROFILE(SMBlockingX);
+ return ERROR_NT(status);
+ }
+ }
+
+ /* Setup the timeout in seconds. */
+
+ lock_timeout = ((lock_timeout == -1) ? -1 : (lock_timeout+999)/1000);
+
+ /* Now do any requested locks */
+ data += ((large_file_format ? 20 : 10)*num_ulocks);
+
+ /* Data now points at the beginning of the list
+ of smb_lkrng structs */
+
+ for(i = 0; i < (int)num_locks; i++) {
+ lock_pid = get_lock_pid( data, i, large_file_format);
+ count = get_lock_count( data, i, large_file_format);
+ offset = get_lock_offset( data, i, large_file_format, &err);
+
+ /*
+ * There is no error code marked "stupid client bug".... :-).
+ */
+ if(err) {
+ END_PROFILE(SMBlockingX);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ DEBUG(10,("reply_lockingX: lock start=%.0f, len=%.0f for pid %u, file %s timeout = %d\n",
+ (double)offset, (double)count, (unsigned int)lock_pid,
+ fsp->fsp_name, (int)lock_timeout ));
+
+ status = do_lock_spin(fsp,conn,lock_pid, count,offset,
+ ((locktype & 1) ? READ_LOCK : WRITE_LOCK), &my_lock_ctx);
+ if (NT_STATUS_V(status)) {
+ /*
+ * Interesting fact found by IFSTEST /t LockOverlappedTest...
+ * Even if it's our own lock context, we need to wait here as
+ * there may be an unlock on the way.
+ * So I removed a "&& !my_lock_ctx" from the following
+ * if statement. JRA.
+ */
+ if ((lock_timeout != 0) && lp_blocking_locks(SNUM(conn)) && ERROR_WAS_LOCK_DENIED(status)) {
+ /*
+ * A blocking lock was requested. Package up
+ * this smb into a queued request and push it
+ * onto the blocking lock queue.
+ */
+ if(push_blocking_lock_request(inbuf, length, lock_timeout, i, lock_pid, offset, count)) {
+ END_PROFILE(SMBlockingX);
+ return -1;
+ }
+ }
+ break;
+ }
+ }
+
+ /* If any of the above locks failed, then we must unlock
+ all of the previous locks (X/Open spec). */
+ if (i != num_locks && num_locks != 0) {
+ /*
+ * Ensure we don't do a remove on the lock that just failed,
+ * as under POSIX rules, if we have a lock already there, we
+ * will delete it (and we shouldn't) .....
+ */
+ for(i--; i >= 0; i--) {
+ lock_pid = get_lock_pid( data, i, large_file_format);
+ count = get_lock_count( data, i, large_file_format);
+ offset = get_lock_offset( data, i, large_file_format, &err);
+
+ /*
+ * There is no error code marked "stupid client bug".... :-).
+ */
+ if(err) {
+ END_PROFILE(SMBlockingX);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ do_unlock(fsp,conn,lock_pid,count,offset);
+ }
+ END_PROFILE(SMBlockingX);
+ return ERROR_NT(status);
+ }
+
+ set_message(outbuf,2,0,True);
+
+ DEBUG( 3, ( "lockingX fnum=%d type=%d num_locks=%d num_ulocks=%d\n",
+ fsp->fnum, (unsigned int)locktype, num_locks, num_ulocks ) );
+
+ END_PROFILE(SMBlockingX);
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
+
+/****************************************************************************
+ Reply to a SMBreadbmpx (read block multiplex) request.
+****************************************************************************/
+
+int reply_readbmpx(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize)
+{
+ ssize_t nread = -1;
+ ssize_t total_read;
+ char *data;
+ SMB_OFF_T startpos;
+ int outsize;
+ size_t maxcount;
+ int max_per_packet;
+ size_t tcount;
+ int pad;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBreadBmpx);
+
+ /* this function doesn't seem to work - disable by default */
+ if (!lp_readbmpx()) {
+ END_PROFILE(SMBreadBmpx);
+ return ERROR_DOS(ERRSRV,ERRuseSTD);
+ }
+
+ outsize = set_message(outbuf,8,0,True);
+
+ CHECK_FSP(fsp,conn);
+ CHECK_READ(fsp);
+
+ startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv1);
+ maxcount = SVAL(inbuf,smb_vwv3);
+
+ data = smb_buf(outbuf);
+ pad = ((long)data)%4;
+ if (pad)
+ pad = 4 - pad;
+ data += pad;
+
+ max_per_packet = bufsize-(outsize+pad);
+ tcount = maxcount;
+ total_read = 0;
+
+ if (is_locked(fsp,conn,(SMB_BIG_UINT)maxcount,(SMB_BIG_UINT)startpos, READ_LOCK,False)) {
+ END_PROFILE(SMBreadBmpx);
+ return ERROR_DOS(ERRDOS,ERRlock);
+ }
+
+ do {
+ size_t N = MIN(max_per_packet,tcount-total_read);
+
+ nread = read_file(fsp,data,startpos,N);
+
+ if (nread <= 0)
+ nread = 0;
+
+ if (nread < (ssize_t)N)
+ tcount = total_read + nread;
+
+ set_message(outbuf,8,nread,False);
+ SIVAL(outbuf,smb_vwv0,startpos);
+ SSVAL(outbuf,smb_vwv2,tcount);
+ SSVAL(outbuf,smb_vwv6,nread);
+ SSVAL(outbuf,smb_vwv7,smb_offset(data,outbuf));
+
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("reply_readbmpx: send_smb failed.");
+
+ total_read += nread;
+ startpos += nread;
+ } while (total_read < (ssize_t)tcount);
+
+ END_PROFILE(SMBreadBmpx);
+ return(-1);
+}
+
+/****************************************************************************
+ Reply to a SMBsetattrE.
+****************************************************************************/
+
+int reply_setattrE(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize)
+{
+ struct utimbuf unix_times;
+ int outsize = 0;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBsetattrE);
+
+ outsize = set_message(outbuf,0,0,True);
+
+ if(!fsp || (fsp->conn != conn)) {
+ END_PROFILE(SMBgetattrE);
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+ }
+
+ /*
+ * Convert the DOS times into unix times. Ignore create
+ * time as UNIX can't set this.
+ */
+
+ unix_times.actime = make_unix_date2(inbuf+smb_vwv3);
+ unix_times.modtime = make_unix_date2(inbuf+smb_vwv5);
+
+ /*
+ * Patch from Ray Frush <frush@engr.colostate.edu>
+ * Sometimes times are sent as zero - ignore them.
+ */
+
+ if ((unix_times.actime == 0) && (unix_times.modtime == 0)) {
+ /* Ignore request */
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "reply_setattrE fnum=%d ", fsp->fnum);
+ dbgtext( "ignoring zero request - not setting timestamps of 0\n" );
+ }
+ END_PROFILE(SMBsetattrE);
+ return(outsize);
+ } else if ((unix_times.actime != 0) && (unix_times.modtime == 0)) {
+ /* set modify time = to access time if modify time was 0 */
+ unix_times.modtime = unix_times.actime;
+ }
+
+ /* Set the date on this file */
+ if(file_utime(conn, fsp->fsp_name, &unix_times)) {
+ END_PROFILE(SMBsetattrE);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ DEBUG( 3, ( "reply_setattrE fnum=%d actime=%d modtime=%d\n",
+ fsp->fnum, (int)unix_times.actime, (int)unix_times.modtime ) );
+
+ END_PROFILE(SMBsetattrE);
+ return(outsize);
+}
+
+
+/* Back from the dead for OS/2..... JRA. */
+
+/****************************************************************************
+ Reply to a SMBwritebmpx (write block multiplex primary) request.
+****************************************************************************/
+
+int reply_writebmpx(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize)
+{
+ size_t numtowrite;
+ ssize_t nwritten = -1;
+ int outsize = 0;
+ SMB_OFF_T startpos;
+ size_t tcount;
+ BOOL write_through;
+ int smb_doff;
+ char *data;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBwriteBmpx);
+
+ CHECK_FSP(fsp,conn);
+ CHECK_WRITE(fsp);
+ CHECK_ERROR(fsp);
+
+ tcount = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv3);
+ write_through = BITSETW(inbuf+smb_vwv7,0);
+ numtowrite = SVAL(inbuf,smb_vwv10);
+ smb_doff = SVAL(inbuf,smb_vwv11);
+
+ data = smb_base(inbuf) + smb_doff;
+
+ /* If this fails we need to send an SMBwriteC response,
+ not an SMBwritebmpx - set this up now so we don't forget */
+ SCVAL(outbuf,smb_com,SMBwritec);
+
+ if (is_locked(fsp,conn,(SMB_BIG_UINT)tcount,(SMB_BIG_UINT)startpos,WRITE_LOCK,False)) {
+ END_PROFILE(SMBwriteBmpx);
+ return(ERROR_DOS(ERRDOS,ERRlock));
+ }
+
+ nwritten = write_file(fsp,data,startpos,numtowrite);
+
+ if(lp_syncalways(SNUM(conn)) || write_through)
+ sync_file(conn,fsp);
+
+ if(nwritten < (ssize_t)numtowrite) {
+ END_PROFILE(SMBwriteBmpx);
+ return(UNIXERROR(ERRHRD,ERRdiskfull));
+ }
+
+ /* If the maximum to be written to this file
+ is greater than what we just wrote then set
+ up a secondary struct to be attached to this
+ fd, we will use this to cache error messages etc. */
+
+ if((ssize_t)tcount > nwritten) {
+ write_bmpx_struct *wbms;
+ if(fsp->wbmpx_ptr != NULL)
+ wbms = fsp->wbmpx_ptr; /* Use an existing struct */
+ else
+ wbms = (write_bmpx_struct *)malloc(sizeof(write_bmpx_struct));
+ if(!wbms) {
+ DEBUG(0,("Out of memory in reply_readmpx\n"));
+ END_PROFILE(SMBwriteBmpx);
+ return(ERROR_DOS(ERRSRV,ERRnoresource));
+ }
+ wbms->wr_mode = write_through;
+ wbms->wr_discard = False; /* No errors yet */
+ wbms->wr_total_written = nwritten;
+ wbms->wr_errclass = 0;
+ wbms->wr_error = 0;
+ fsp->wbmpx_ptr = wbms;
+ }
+
+ /* We are returning successfully, set the message type back to
+ SMBwritebmpx */
+ SCVAL(outbuf,smb_com,SMBwriteBmpx);
+
+ outsize = set_message(outbuf,1,0,True);
+
+ SSVALS(outbuf,smb_vwv0,-1); /* We don't support smb_remaining */
+
+ DEBUG( 3, ( "writebmpx fnum=%d num=%d wrote=%d\n",
+ fsp->fnum, (int)numtowrite, (int)nwritten ) );
+
+ if (write_through && tcount==nwritten) {
+ /* We need to send both a primary and a secondary response */
+ smb_setlen(outbuf,outsize - 4);
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("reply_writebmpx: send_smb failed.");
+
+ /* Now the secondary */
+ outsize = set_message(outbuf,1,0,True);
+ SCVAL(outbuf,smb_com,SMBwritec);
+ SSVAL(outbuf,smb_vwv0,nwritten);
+ }
+
+ END_PROFILE(SMBwriteBmpx);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a SMBwritebs (write block multiplex secondary) request.
+****************************************************************************/
+
+int reply_writebs(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ size_t numtowrite;
+ ssize_t nwritten = -1;
+ int outsize = 0;
+ SMB_OFF_T startpos;
+ size_t tcount;
+ BOOL write_through;
+ int smb_doff;
+ char *data;
+ write_bmpx_struct *wbms;
+ BOOL send_response = False;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBwriteBs);
+
+ CHECK_FSP(fsp,conn);
+ CHECK_WRITE(fsp);
+
+ tcount = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv2);
+ numtowrite = SVAL(inbuf,smb_vwv6);
+ smb_doff = SVAL(inbuf,smb_vwv7);
+
+ data = smb_base(inbuf) + smb_doff;
+
+ /* We need to send an SMBwriteC response, not an SMBwritebs */
+ SCVAL(outbuf,smb_com,SMBwritec);
+
+ /* This fd should have an auxiliary struct attached,
+ check that it does */
+ wbms = fsp->wbmpx_ptr;
+ if(!wbms) {
+ END_PROFILE(SMBwriteBs);
+ return(-1);
+ }
+
+ /* If write through is set we can return errors, else we must cache them */
+ write_through = wbms->wr_mode;
+
+ /* Check for an earlier error */
+ if(wbms->wr_discard) {
+ END_PROFILE(SMBwriteBs);
+ return -1; /* Just discard the packet */
+ }
+
+ nwritten = write_file(fsp,data,startpos,numtowrite);
+
+ if(lp_syncalways(SNUM(conn)) || write_through)
+ sync_file(conn,fsp);
+
+ if (nwritten < (ssize_t)numtowrite) {
+ if(write_through) {
+ /* We are returning an error - we can delete the aux struct */
+ if (wbms)
+ free((char *)wbms);
+ fsp->wbmpx_ptr = NULL;
+ END_PROFILE(SMBwriteBs);
+ return(ERROR_DOS(ERRHRD,ERRdiskfull));
+ }
+ END_PROFILE(SMBwriteBs);
+ return(CACHE_ERROR(wbms,ERRHRD,ERRdiskfull));
+ }
+
+ /* Increment the total written, if this matches tcount
+ we can discard the auxiliary struct (hurrah !) and return a writeC */
+ wbms->wr_total_written += nwritten;
+ if(wbms->wr_total_written >= tcount) {
+ if (write_through) {
+ outsize = set_message(outbuf,1,0,True);
+ SSVAL(outbuf,smb_vwv0,wbms->wr_total_written);
+ send_response = True;
+ }
+
+ free((char *)wbms);
+ fsp->wbmpx_ptr = NULL;
+ }
+
+ if(send_response) {
+ END_PROFILE(SMBwriteBs);
+ return(outsize);
+ }
+
+ END_PROFILE(SMBwriteBs);
+ return(-1);
+}
+
+/****************************************************************************
+ Reply to a SMBgetattrE.
+****************************************************************************/
+
+int reply_getattrE(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize)
+{
+ SMB_STRUCT_STAT sbuf;
+ int outsize = 0;
+ int mode;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBgetattrE);
+
+ outsize = set_message(outbuf,11,0,True);
+
+ if(!fsp || (fsp->conn != conn)) {
+ END_PROFILE(SMBgetattrE);
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+ }
+
+ /* Do an fstat on this file */
+ if(fsp_stat(fsp, &sbuf)) {
+ END_PROFILE(SMBgetattrE);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ mode = dos_mode(conn,fsp->fsp_name,&sbuf);
+
+ /*
+ * Convert the times into dos times. Set create
+ * date to be last modify date as UNIX doesn't save
+ * this.
+ */
+
+ put_dos_date2(outbuf,smb_vwv0,get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn))));
+ put_dos_date2(outbuf,smb_vwv2,sbuf.st_atime);
+ put_dos_date2(outbuf,smb_vwv4,sbuf.st_mtime);
+
+ if (mode & aDIR) {
+ SIVAL(outbuf,smb_vwv6,0);
+ SIVAL(outbuf,smb_vwv8,0);
+ } else {
+ uint32 allocation_size = get_allocation_size(fsp, &sbuf);
+ SIVAL(outbuf,smb_vwv6,(uint32)sbuf.st_size);
+ SIVAL(outbuf,smb_vwv8,allocation_size);
+ }
+ SSVAL(outbuf,smb_vwv10, mode);
+
+ DEBUG( 3, ( "reply_getattrE fnum=%d\n", fsp->fnum));
+
+ END_PROFILE(SMBgetattrE);
+ return(outsize);
+}
diff --git a/source/smbd/sec_ctx.c b/source/smbd/sec_ctx.c
new file mode 100644
index 00000000000..fee71b5ec96
--- /dev/null
+++ b/source/smbd/sec_ctx.c
@@ -0,0 +1,449 @@
+/*
+ Unix SMB/CIFS implementation.
+ uid/user handling
+ Copyright (C) Tim Potter 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+extern struct current_user current_user;
+
+struct sec_ctx {
+ uid_t uid;
+ uid_t gid;
+ int ngroups;
+ gid_t *groups;
+ NT_USER_TOKEN *token;
+ PRIVILEGE_SET *privs;
+};
+
+/* A stack of security contexts. We include the current context as being
+ the first one, so there is room for another MAX_SEC_CTX_DEPTH more. */
+
+static struct sec_ctx sec_ctx_stack[MAX_SEC_CTX_DEPTH + 1];
+static int sec_ctx_stack_ndx;
+
+/****************************************************************************
+ Become the specified uid.
+****************************************************************************/
+
+static BOOL become_uid(uid_t uid)
+{
+ /* Check for dodgy uid values */
+
+ if (uid == (uid_t)-1 ||
+ ((sizeof(uid_t) == 2) && (uid == (uid_t)65535))) {
+ static int done;
+
+ if (!done) {
+ DEBUG(1,("WARNING: using uid %d is a security risk\n",
+ (int)uid));
+ done = 1;
+ }
+ }
+
+ /* Set effective user id */
+
+ set_effective_uid(uid);
+
+ DO_PROFILE_INC(uid_changes);
+ return True;
+}
+
+/****************************************************************************
+ Become the specified gid.
+****************************************************************************/
+
+static BOOL become_gid(gid_t gid)
+{
+ /* Check for dodgy gid values */
+
+ if (gid == (gid_t)-1 || ((sizeof(gid_t) == 2) &&
+ (gid == (gid_t)65535))) {
+ static int done;
+
+ if (!done) {
+ DEBUG(1,("WARNING: using gid %d is a security risk\n",
+ (int)gid));
+ done = 1;
+ }
+ }
+
+ /* Set effective group id */
+
+ set_effective_gid(gid);
+ return True;
+}
+
+/****************************************************************************
+ Become the specified uid and gid.
+****************************************************************************/
+
+static BOOL become_id(uid_t uid, gid_t gid)
+{
+ return become_gid(gid) && become_uid(uid);
+}
+
+/****************************************************************************
+ Drop back to root privileges in order to change to another user.
+****************************************************************************/
+
+static void gain_root(void)
+{
+ if (non_root_mode()) {
+ return;
+ }
+
+ if (geteuid() != 0) {
+ set_effective_uid(0);
+
+ if (geteuid() != 0) {
+ DEBUG(0,
+ ("Warning: You appear to have a trapdoor "
+ "uid system\n"));
+ }
+ }
+
+ if (getegid() != 0) {
+ set_effective_gid(0);
+
+ if (getegid() != 0) {
+ DEBUG(0,
+ ("Warning: You appear to have a trapdoor "
+ "gid system\n"));
+ }
+ }
+}
+
+/****************************************************************************
+ Get the list of current groups.
+****************************************************************************/
+
+int get_current_groups(gid_t gid, int *p_ngroups, gid_t **p_groups)
+{
+ int i;
+ gid_t grp;
+ int ngroups;
+ gid_t *groups = NULL;
+
+ (*p_ngroups) = 0;
+ (*p_groups) = NULL;
+
+ /* this looks a little strange, but is needed to cope with
+ systems that put the current egid in the group list
+ returned from getgroups() (tridge) */
+ save_re_gid();
+ set_effective_gid(gid);
+ setgid(gid);
+
+ ngroups = sys_getgroups(0,&grp);
+ if (ngroups <= 0) {
+ goto fail;
+ }
+
+ if((groups = (gid_t *)malloc(sizeof(gid_t)*(ngroups+1))) == NULL) {
+ DEBUG(0,("setup_groups malloc fail !\n"));
+ goto fail;
+ }
+
+ if ((ngroups = sys_getgroups(ngroups,groups)) == -1) {
+ goto fail;
+ }
+
+ restore_re_gid();
+
+ (*p_ngroups) = ngroups;
+ (*p_groups) = groups;
+
+ DEBUG( 3, ( "get_current_groups: user is in %u groups: ", ngroups));
+ for (i = 0; i < ngroups; i++ ) {
+ DEBUG( 3, ( "%s%d", (i ? ", " : ""), (int)groups[i] ) );
+ }
+ DEBUG( 3, ( "\n" ) );
+
+ return ngroups;
+
+fail:
+ SAFE_FREE(groups);
+ restore_re_gid();
+ return -1;
+}
+
+/****************************************************************************
+ Initialize the groups a user belongs to.
+****************************************************************************/
+
+BOOL initialise_groups(char *user, uid_t uid, gid_t gid)
+{
+ struct sec_ctx *prev_ctx_p;
+ BOOL result = True;
+
+ if (non_root_mode()) {
+ return True;
+ }
+
+ become_root();
+
+ /* Call initgroups() to get user groups */
+
+ if (winbind_initgroups(user,gid) == -1) {
+ DEBUG(0,("Unable to initgroups. Error was %s\n", strerror(errno) ));
+ if (getuid() == 0) {
+ if (gid < 0 || gid > 32767 || uid < 0 || uid > 32767) {
+ DEBUG(0,("This is probably a problem with the account %s\n", user));
+ }
+ }
+ result = False;
+ goto done;
+ }
+
+ /* Store groups in previous user's security context. This will
+ always work as the become_root() call increments the stack
+ pointer. */
+
+ prev_ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx - 1];
+
+ SAFE_FREE(prev_ctx_p->groups);
+ prev_ctx_p->ngroups = 0;
+
+ get_current_groups(gid, &prev_ctx_p->ngroups, &prev_ctx_p->groups);
+
+ done:
+ unbecome_root();
+
+ return result;
+}
+
+/****************************************************************************
+ Create a new security context on the stack. It is the same as the old
+ one. User changes are done using the set_sec_ctx() function.
+****************************************************************************/
+
+BOOL push_sec_ctx(void)
+{
+ struct sec_ctx *ctx_p;
+
+ /* Check we don't overflow our stack */
+
+ if (sec_ctx_stack_ndx == MAX_SEC_CTX_DEPTH) {
+ DEBUG(0, ("Security context stack overflow!\n"));
+ smb_panic("Security context stack overflow!\n");
+ }
+
+ /* Store previous user context */
+
+ sec_ctx_stack_ndx++;
+
+ ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
+
+ ctx_p->uid = geteuid();
+ ctx_p->gid = getegid();
+
+ DEBUG(3, ("push_sec_ctx(%u, %u) : sec_ctx_stack_ndx = %d\n",
+ (unsigned int)ctx_p->uid, (unsigned int)ctx_p->gid, sec_ctx_stack_ndx ));
+
+ ctx_p->token = dup_nt_token(sec_ctx_stack[sec_ctx_stack_ndx-1].token);
+
+ ctx_p->ngroups = sys_getgroups(0, NULL);
+
+ if (ctx_p->ngroups != 0) {
+ if (!(ctx_p->groups = malloc(ctx_p->ngroups * sizeof(gid_t)))) {
+ DEBUG(0, ("Out of memory in push_sec_ctx()\n"));
+ delete_nt_token(&ctx_p->token);
+ return False;
+ }
+
+ sys_getgroups(ctx_p->ngroups, ctx_p->groups);
+ } else {
+ ctx_p->groups = NULL;
+ }
+
+ init_privilege(&ctx_p->privs);
+ if (! NT_STATUS_IS_OK(dup_priv_set(ctx_p->privs, sec_ctx_stack[sec_ctx_stack_ndx-1].privs))) {
+ DEBUG(0, ("Out of memory on dup_priv_set() in push_sec_ctx()\n"));
+ delete_nt_token(&ctx_p->token);
+ destroy_privilege(&ctx_p->privs);
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Set the current security context to a given user.
+****************************************************************************/
+
+void set_sec_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups, NT_USER_TOKEN *token, PRIVILEGE_SET *privs)
+{
+ struct sec_ctx *ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
+
+ /* Set the security context */
+
+ DEBUG(3, ("setting sec ctx (%u, %u) - sec_ctx_stack_ndx = %d\n",
+ (unsigned int)uid, (unsigned int)gid, sec_ctx_stack_ndx));
+
+ debug_nt_user_token(DBGC_CLASS, 5, token);
+ debug_unix_user_token(DBGC_CLASS, 5, uid, gid, ngroups, groups);
+
+ gain_root();
+
+#ifdef HAVE_SETGROUPS
+ sys_setgroups(ngroups, groups);
+#endif
+
+ ctx_p->ngroups = ngroups;
+
+ SAFE_FREE(ctx_p->groups);
+ if (token && (token == ctx_p->token))
+ smb_panic("DUPLICATE_TOKEN");
+
+ delete_nt_token(&ctx_p->token);
+ if (ctx_p->privs)
+ reset_privilege(ctx_p->privs);
+ else
+ init_privilege(&ctx_p->privs);
+
+ ctx_p->groups = memdup(groups, sizeof(gid_t) * ngroups);
+ ctx_p->token = dup_nt_token(token);
+ dup_priv_set(ctx_p->privs, privs);
+
+ become_id(uid, gid);
+
+ ctx_p->uid = uid;
+ ctx_p->gid = gid;
+
+ /* Update current_user stuff */
+
+ current_user.uid = uid;
+ current_user.gid = gid;
+ current_user.ngroups = ngroups;
+ current_user.groups = groups;
+ current_user.nt_user_token = ctx_p->token;
+ current_user.privs = ctx_p->privs;
+}
+
+/****************************************************************************
+ Become root context.
+****************************************************************************/
+
+void set_root_sec_ctx(void)
+{
+ /* May need to worry about supplementary groups at some stage */
+
+ set_sec_ctx(0, 0, 0, NULL, NULL, NULL);
+}
+
+/****************************************************************************
+ Pop a security context from the stack.
+****************************************************************************/
+
+BOOL pop_sec_ctx(void)
+{
+ struct sec_ctx *ctx_p;
+ struct sec_ctx *prev_ctx_p;
+
+ /* Check for stack underflow */
+
+ if (sec_ctx_stack_ndx == 0) {
+ DEBUG(0, ("Security context stack underflow!\n"));
+ smb_panic("Security context stack underflow!\n");
+ }
+
+ ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
+
+ /* Clear previous user info */
+
+ ctx_p->uid = (uid_t)-1;
+ ctx_p->gid = (gid_t)-1;
+
+ SAFE_FREE(ctx_p->groups);
+ ctx_p->ngroups = 0;
+
+ delete_nt_token(&ctx_p->token);
+ destroy_privilege(&ctx_p->privs);
+
+ /* Pop back previous user */
+
+ sec_ctx_stack_ndx--;
+
+ gain_root();
+
+ prev_ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
+
+#ifdef HAVE_SETGROUPS
+ sys_setgroups(prev_ctx_p->ngroups, prev_ctx_p->groups);
+#endif
+
+ become_id(prev_ctx_p->uid, prev_ctx_p->gid);
+
+ /* Update current_user stuff */
+
+ current_user.uid = prev_ctx_p->uid;
+ current_user.gid = prev_ctx_p->gid;
+ current_user.ngroups = prev_ctx_p->ngroups;
+ current_user.groups = prev_ctx_p->groups;
+ current_user.nt_user_token = prev_ctx_p->token;
+ current_user.privs = prev_ctx_p->privs;
+
+ DEBUG(3, ("pop_sec_ctx (%u, %u) - sec_ctx_stack_ndx = %d\n",
+ (unsigned int)geteuid(), (unsigned int)getegid(), sec_ctx_stack_ndx));
+
+ return True;
+}
+
+/* Initialise the security context system */
+
+void init_sec_ctx(void)
+{
+ int i;
+ struct sec_ctx *ctx_p;
+
+ /* Initialise security context stack */
+
+ memset(sec_ctx_stack, 0, sizeof(struct sec_ctx) * MAX_SEC_CTX_DEPTH);
+
+ for (i = 0; i < MAX_SEC_CTX_DEPTH; i++) {
+ sec_ctx_stack[i].uid = (uid_t)-1;
+ sec_ctx_stack[i].gid = (gid_t)-1;
+ }
+
+ /* Initialise first level of stack. It is the current context */
+ ctx_p = &sec_ctx_stack[0];
+
+ ctx_p->uid = geteuid();
+ ctx_p->gid = getegid();
+
+ get_current_groups(ctx_p->gid, &ctx_p->ngroups, &ctx_p->groups);
+
+ ctx_p->token = NULL; /* Maps to guest user. */
+ ctx_p->privs = NULL;
+
+ /* Initialise current_user global */
+
+ current_user.uid = ctx_p->uid;
+ current_user.gid = ctx_p->gid;
+ current_user.ngroups = ctx_p->ngroups;
+ current_user.groups = ctx_p->groups;
+
+ /* The conn and vuid are usually taken care of by other modules.
+ We initialise them here. */
+
+ current_user.conn = NULL;
+ current_user.vuid = UID_FIELD_INVALID;
+ current_user.nt_user_token = NULL;
+ current_user.privs = NULL;
+}
diff --git a/source/smbd/server.c b/source/smbd/server.c
new file mode 100644
index 00000000000..53d07fd905c
--- /dev/null
+++ b/source/smbd/server.c
@@ -0,0 +1,909 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main SMB server routines
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Martin Pool 2002
+ Copyright (C) Jelmer Vernooij 2002-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static int am_parent = 1;
+
+/* the last message the was processed */
+int last_message = -1;
+
+/* a useful macro to debug the last message processed */
+#define LAST_MESSAGE() smb_fn_name(last_message)
+
+extern pstring user_socket_options;
+extern SIG_ATOMIC_T got_sig_term;
+extern SIG_ATOMIC_T reload_after_sighup;
+
+#ifdef WITH_DFS
+extern int dcelogin_atmost_once;
+#endif /* WITH_DFS */
+
+/* really we should have a top level context structure that has the
+ client file descriptor as an element. That would require a major rewrite :(
+
+ the following 2 functions are an alternative - they make the file
+ descriptor private to smbd
+ */
+static int server_fd = -1;
+
+int smbd_server_fd(void)
+{
+ return server_fd;
+}
+
+static void smbd_set_server_fd(int fd)
+{
+ server_fd = fd;
+ client_setfd(fd);
+}
+
+/****************************************************************************
+ Terminate signal.
+****************************************************************************/
+
+static void sig_term(void)
+{
+ got_sig_term = 1;
+ sys_select_signal();
+}
+
+/****************************************************************************
+ Catch a sighup.
+****************************************************************************/
+
+static void sig_hup(int sig)
+{
+ reload_after_sighup = 1;
+ sys_select_signal();
+}
+
+/****************************************************************************
+ Send a SIGTERM to our process group.
+*****************************************************************************/
+
+static void killkids(void)
+{
+ if(am_parent) kill(0,SIGTERM);
+}
+
+/****************************************************************************
+ Process a sam sync message - not sure whether to do this here or
+ somewhere else.
+****************************************************************************/
+
+static void msg_sam_sync(int UNUSED(msg_type), pid_t UNUSED(pid),
+ void *UNUSED(buf), size_t UNUSED(len))
+{
+ DEBUG(10, ("** sam sync message received, ignoring\n"));
+}
+
+/****************************************************************************
+ Process a sam sync replicate message - not sure whether to do this here or
+ somewhere else.
+****************************************************************************/
+
+static void msg_sam_repl(int msg_type, pid_t pid, void *buf, size_t len)
+{
+ uint32 low_serial;
+
+ if (len != sizeof(uint32))
+ return;
+
+ low_serial = *((uint32 *)buf);
+
+ DEBUG(3, ("received sam replication message, serial = 0x%04x\n",
+ low_serial));
+}
+
+/****************************************************************************
+ Open the socket communication - inetd.
+****************************************************************************/
+
+static BOOL open_sockets_inetd(void)
+{
+ /* Started from inetd. fd 0 is the socket. */
+ /* We will abort gracefully when the client or remote system
+ goes away */
+ smbd_set_server_fd(dup(0));
+
+ /* close our standard file descriptors */
+ close_low_fds(False); /* Don't close stderr */
+
+ set_socket_options(smbd_server_fd(),"SO_KEEPALIVE");
+ set_socket_options(smbd_server_fd(), user_socket_options);
+
+ return True;
+}
+
+static void msg_exit_server(int msg_type, pid_t src, void *buf, size_t len)
+{
+ exit_server("Got a SHUTDOWN message");
+}
+
+
+/****************************************************************************
+ Have we reached the process limit ?
+****************************************************************************/
+
+static BOOL allowable_number_of_smbd_processes(void)
+{
+ int max_processes = lp_max_smbd_processes();
+
+ if (!max_processes)
+ return True;
+
+ {
+ TDB_CONTEXT *tdb = conn_tdb_ctx();
+ int32 val;
+ if (!tdb) {
+ DEBUG(0,("allowable_number_of_smbd_processes: can't open connection tdb.\n" ));
+ return False;
+ }
+
+ val = tdb_fetch_int32(tdb, "INFO/total_smbds");
+ if (val == -1 && (tdb_error(tdb) != TDB_ERR_NOEXIST)) {
+ DEBUG(0,("allowable_number_of_smbd_processes: can't fetch INFO/total_smbds. Error %s\n",
+ tdb_errorstr(tdb) ));
+ return False;
+ }
+ if (val > max_processes) {
+ DEBUG(0,("allowable_number_of_smbd_processes: number of processes (%d) is over allowed limit (%d)\n",
+ val, max_processes ));
+ return False;
+ }
+ }
+ return True;
+}
+
+/****************************************************************************
+ Open the socket communication.
+****************************************************************************/
+
+static BOOL open_sockets_smbd(BOOL is_daemon, BOOL interactive, const char *smb_ports)
+{
+ int num_interfaces = iface_count();
+ int num_sockets = 0;
+ int fd_listenset[FD_SETSIZE];
+ fd_set listen_set;
+ int s;
+ int i;
+ char *ports;
+
+ if (!is_daemon) {
+ return open_sockets_inetd();
+ }
+
+
+#ifdef HAVE_ATEXIT
+ {
+ static int atexit_set;
+ if(atexit_set == 0) {
+ atexit_set=1;
+ atexit(killkids);
+ }
+ }
+#endif
+
+ /* Stop zombies */
+ CatchChild();
+
+ FD_ZERO(&listen_set);
+
+ /* use a reasonable default set of ports - listing on 445 and 139 */
+ if (!smb_ports) {
+ ports = lp_smb_ports();
+ if (!ports || !*ports) {
+ ports = smb_xstrdup(SMB_PORTS);
+ } else {
+ ports = smb_xstrdup(ports);
+ }
+ } else {
+ ports = smb_xstrdup(smb_ports);
+ }
+
+ if (lp_interfaces() && lp_bind_interfaces_only()) {
+ /* We have been given an interfaces line, and been
+ told to only bind to those interfaces. Create a
+ socket per interface and bind to only these.
+ */
+
+ /* Now open a listen socket for each of the
+ interfaces. */
+ for(i = 0; i < num_interfaces; i++) {
+ struct in_addr *ifip = iface_n_ip(i);
+ fstring tok;
+ const char *ptr;
+
+ if(ifip == NULL) {
+ DEBUG(0,("open_sockets_smbd: interface %d has NULL IP address !\n", i));
+ continue;
+ }
+
+ for (ptr=ports; next_token(&ptr, tok, NULL, sizeof(tok)); ) {
+ unsigned port = atoi(tok);
+ if (port == 0) continue;
+ s = fd_listenset[num_sockets] = open_socket_in(SOCK_STREAM, port, 0, ifip->s_addr, True);
+ if(s == -1)
+ return False;
+
+ /* ready to listen */
+ set_socket_options(s,"SO_KEEPALIVE");
+ set_socket_options(s,user_socket_options);
+
+ /* Set server socket to non-blocking for the accept. */
+ set_blocking(s,False);
+
+ if (listen(s, SMBD_LISTEN_BACKLOG) == -1) {
+ DEBUG(0,("listen: %s\n",strerror(errno)));
+ close(s);
+ return False;
+ }
+ FD_SET(s,&listen_set);
+
+ num_sockets++;
+ if (num_sockets >= FD_SETSIZE) {
+ DEBUG(0,("open_sockets_smbd: Too many sockets to bind to\n"));
+ return False;
+ }
+ }
+ }
+ } else {
+ /* Just bind to 0.0.0.0 - accept connections
+ from anywhere. */
+
+ fstring tok;
+ const char *ptr;
+
+ num_interfaces = 1;
+
+ for (ptr=ports; next_token(&ptr, tok, NULL, sizeof(tok)); ) {
+ unsigned port = atoi(tok);
+ if (port == 0) continue;
+ /* open an incoming socket */
+ s = open_socket_in(SOCK_STREAM, port, 0,
+ interpret_addr(lp_socket_address()),True);
+ if (s == -1)
+ return(False);
+
+ /* ready to listen */
+ set_socket_options(s,"SO_KEEPALIVE");
+ set_socket_options(s,user_socket_options);
+
+ /* Set server socket to non-blocking for the accept. */
+ set_blocking(s,False);
+
+ if (listen(s, SMBD_LISTEN_BACKLOG) == -1) {
+ DEBUG(0,("open_sockets_smbd: listen: %s\n",
+ strerror(errno)));
+ close(s);
+ return False;
+ }
+
+ fd_listenset[num_sockets] = s;
+ FD_SET(s,&listen_set);
+
+ num_sockets++;
+
+ if (num_sockets >= FD_SETSIZE) {
+ DEBUG(0,("open_sockets_smbd: Too many sockets to bind to\n"));
+ return False;
+ }
+ }
+ }
+
+ SAFE_FREE(ports);
+
+ /* Listen to messages */
+
+ message_register(MSG_SMB_SAM_SYNC, msg_sam_sync);
+ message_register(MSG_SMB_SAM_REPL, msg_sam_repl);
+ message_register(MSG_SHUTDOWN, msg_exit_server);
+
+ /* now accept incoming connections - forking a new process
+ for each incoming connection */
+ DEBUG(2,("waiting for a connection\n"));
+ while (1) {
+ fd_set lfds;
+ int num;
+
+ /* Free up temporary memory from the main smbd. */
+ lp_talloc_free();
+
+ /* Ensure we respond to PING and DEBUG messages from the main smbd. */
+ message_dispatch();
+
+ memcpy((char *)&lfds, (char *)&listen_set,
+ sizeof(listen_set));
+
+ num = sys_select(FD_SETSIZE,&lfds,NULL,NULL,NULL);
+
+ if (num == -1 && errno == EINTR) {
+ if (got_sig_term) {
+ exit_server("Caught TERM signal");
+ }
+
+ /* check for sighup processing */
+ if (reload_after_sighup) {
+ change_to_root_user();
+ DEBUG(1,("Reloading services after SIGHUP\n"));
+ reload_services(False);
+ reload_after_sighup = 0;
+ }
+
+ continue;
+ }
+
+ /* check if we need to reload services */
+ check_reload(time(NULL));
+
+ /* Find the sockets that are read-ready -
+ accept on these. */
+ for( ; num > 0; num--) {
+ struct sockaddr addr;
+ socklen_t in_addrlen = sizeof(addr);
+
+ s = -1;
+ for(i = 0; i < num_sockets; i++) {
+ if(FD_ISSET(fd_listenset[i],&lfds)) {
+ s = fd_listenset[i];
+ /* Clear this so we don't look
+ at it again. */
+ FD_CLR(fd_listenset[i],&lfds);
+ break;
+ }
+ }
+
+ smbd_set_server_fd(accept(s,&addr,&in_addrlen));
+
+ if (smbd_server_fd() == -1 && errno == EINTR)
+ continue;
+
+ if (smbd_server_fd() == -1) {
+ DEBUG(0,("open_sockets_smbd: accept: %s\n",
+ strerror(errno)));
+ continue;
+ }
+
+ /* Ensure child is set to blocking mode */
+ set_blocking(smbd_server_fd(),True);
+
+ if (smbd_server_fd() != -1 && interactive)
+ return True;
+
+ if (allowable_number_of_smbd_processes() && smbd_server_fd() != -1 && sys_fork()==0) {
+ /* Child code ... */
+
+ /* close the listening socket(s) */
+ for(i = 0; i < num_sockets; i++)
+ close(fd_listenset[i]);
+
+ /* close our standard file
+ descriptors */
+ close_low_fds(False);
+ am_parent = 0;
+
+ set_socket_options(smbd_server_fd(),"SO_KEEPALIVE");
+ set_socket_options(smbd_server_fd(),user_socket_options);
+
+ /* this is needed so that we get decent entries
+ in smbstatus for port 445 connects */
+ set_remote_machine_name(get_peer_addr(smbd_server_fd()), False);
+
+ /* Reset global variables in util.c so
+ that client substitutions will be
+ done correctly in the process. */
+ reset_globals_after_fork();
+
+ /* tdb needs special fork handling - remove CLEAR_IF_FIRST flags */
+ if (tdb_reopen_all() == -1) {
+ DEBUG(0,("tdb_reopen_all failed.\n"));
+ smb_panic("tdb_reopen_all failed.");
+ }
+
+ return True;
+ }
+ /* The parent doesn't need this socket */
+ close(smbd_server_fd());
+
+ /* Sun May 6 18:56:14 2001 ackley@cs.unm.edu:
+ Clear the closed fd info out of server_fd --
+ and more importantly, out of client_fd in
+ util_sock.c, to avoid a possible
+ getpeername failure if we reopen the logs
+ and use %I in the filename.
+ */
+
+ smbd_set_server_fd(-1);
+
+ /* Force parent to check log size after
+ * spawning child. Fix from
+ * klausr@ITAP.Physik.Uni-Stuttgart.De. The
+ * parent smbd will log to logserver.smb. It
+ * writes only two messages for each child
+ * started/finished. But each child writes,
+ * say, 50 messages also in logserver.smb,
+ * begining with the debug_count of the
+ * parent, before the child opens its own log
+ * file logserver.client. In a worst case
+ * scenario the size of logserver.smb would be
+ * checked after about 50*50=2500 messages
+ * (ca. 100kb).
+ * */
+ force_check_log_size();
+
+ } /* end for num */
+ } /* end while 1 */
+
+/* NOTREACHED return True; */
+}
+
+/****************************************************************************
+ Reload the services file.
+**************************************************************************/
+
+BOOL reload_services(BOOL test)
+{
+ BOOL ret;
+
+ if (lp_loaded()) {
+ pstring fname;
+ pstrcpy(fname,lp_configfile());
+ if (file_exist(fname, NULL) &&
+ !strcsequal(fname, dyn_CONFIGFILE)) {
+ pstrcpy(dyn_CONFIGFILE, fname);
+ test = False;
+ }
+ }
+
+ reopen_logs();
+
+ if (test && !lp_file_list_changed())
+ return(True);
+
+ lp_killunused(conn_snum_used);
+
+ ret = lp_load(dyn_CONFIGFILE, False, False, True);
+
+ load_printers();
+
+ /* perhaps the config filename is now set */
+ if (!test)
+ reload_services(True);
+
+ reopen_logs();
+
+ load_interfaces();
+
+ {
+ if (smbd_server_fd() != -1) {
+ set_socket_options(smbd_server_fd(),"SO_KEEPALIVE");
+ set_socket_options(smbd_server_fd(), user_socket_options);
+ }
+ }
+
+ mangle_reset_cache();
+ reset_stat_cache();
+
+ /* this forces service parameters to be flushed */
+ set_current_service(NULL,True);
+
+ return(ret);
+}
+
+
+#if DUMP_CORE
+/*******************************************************************
+prepare to dump a core file - carefully!
+********************************************************************/
+static BOOL dump_core(void)
+{
+ char *p;
+ pstring dname;
+
+ pstrcpy(dname,lp_logfile());
+ if ((p=strrchr_m(dname,'/'))) *p=0;
+ pstrcat(dname,"/corefiles");
+ mkdir(dname,0700);
+ sys_chown(dname,getuid(),getgid());
+ chmod(dname,0700);
+ if (chdir(dname)) return(False);
+ umask(~(0700));
+
+#ifdef HAVE_GETRLIMIT
+#ifdef RLIMIT_CORE
+ {
+ struct rlimit rlp;
+ getrlimit(RLIMIT_CORE, &rlp);
+ rlp.rlim_cur = MAX(4*1024*1024,rlp.rlim_cur);
+ setrlimit(RLIMIT_CORE, &rlp);
+ getrlimit(RLIMIT_CORE, &rlp);
+ DEBUG(3,("Core limits now %d %d\n",
+ (int)rlp.rlim_cur,(int)rlp.rlim_max));
+ }
+#endif
+#endif
+
+
+ DEBUG(0,("Dumping core in %s\n", dname));
+ abort();
+ return(True);
+}
+#endif
+
+/****************************************************************************
+ Exit the server.
+****************************************************************************/
+
+void exit_server(const char *reason)
+{
+ static int firsttime=1;
+ extern char *last_inbuf;
+ extern struct auth_context *negprot_global_auth_context;
+
+ if (!firsttime)
+ exit(0);
+ firsttime = 0;
+
+ change_to_root_user();
+ DEBUG(2,("Closing connections\n"));
+
+ if (negprot_global_auth_context) {
+ (negprot_global_auth_context->free)(&negprot_global_auth_context);
+ }
+
+ conn_close_all();
+
+ invalidate_all_vuids();
+
+ print_notify_send_messages(3); /* 3 second timeout. */
+
+ /* run all registered exit events */
+ smb_run_exit_events();
+
+ /* delete our entry in the connections database. */
+ yield_connection(NULL,"");
+
+ respond_to_all_remaining_local_messages();
+ decrement_smbd_process_count();
+
+#ifdef WITH_DFS
+ if (dcelogin_atmost_once) {
+ dfs_unlogin();
+ }
+#endif
+
+ if (!reason) {
+ int oldlevel = DEBUGLEVEL;
+ DEBUGLEVEL = 10;
+ DEBUG(0,("Last message was %s\n",smb_fn_name(last_message)));
+ if (last_inbuf)
+ show_msg(last_inbuf);
+ DEBUGLEVEL = oldlevel;
+ DEBUG(0,("===============================================================\n"));
+#if DUMP_CORE
+ if (dump_core()) return;
+#endif
+ }
+
+ locking_end();
+ printing_end();
+
+ DEBUG(3,("Server exit (%s)\n", (reason ? reason : "")));
+ exit(0);
+}
+
+/****************************************************************************
+ Initialise connect, service and file structs.
+****************************************************************************/
+
+static BOOL init_structs(void )
+{
+ /*
+ * Set the machine NETBIOS name if not already
+ * set from the config file.
+ */
+
+ if (!init_names())
+ return False;
+
+ conn_init();
+
+ file_init();
+
+ /* for RPC pipes */
+ init_rpc_pipe_hnd();
+
+ init_dptrs();
+
+ secrets_init();
+
+ return True;
+}
+
+/****************************************************************************
+ main program.
+****************************************************************************/
+
+/* Declare prototype for build_options() to avoid having to run it through
+ mkproto.h. Mixing $(builddir) and $(srcdir) source files in the current
+ prototype generation system is too complicated. */
+
+void build_options(BOOL screen);
+
+ int main(int argc,const char *argv[])
+{
+ /* shall I run as a daemon */
+ static BOOL is_daemon = False;
+ static BOOL interactive = False;
+ static BOOL Fork = True;
+ static BOOL log_stdout = False;
+ static char *ports = NULL;
+ int opt;
+ poptContext pc;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"daemon", 'D', POPT_ARG_VAL, &is_daemon, True, "Become a daemon (default)" },
+ {"interactive", 'i', POPT_ARG_VAL, &interactive, True, "Run interactive (not a daemon)"},
+ {"foreground", 'F', POPT_ARG_VAL, &Fork, False, "Run daemon in foreground (for daemontools & etc)" },
+ {"log-stdout", 'S', POPT_ARG_VAL, &log_stdout, True, "Log to stdout" },
+ {"build-options", 'b', POPT_ARG_NONE, NULL, 'b', "Print build options" },
+ {"port", 'p', POPT_ARG_STRING, &ports, 0, "Listen on the specified ports"},
+ POPT_COMMON_SAMBA
+ { NULL }
+ };
+
+#ifdef HAVE_SET_AUTH_PARAMETERS
+ set_auth_parameters(argc,argv);
+#endif
+
+ pc = poptGetContext("smbd", argc, argv, long_options, 0);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'b':
+ build_options(True); /* Display output to screen as well as debug */
+ exit(0);
+ break;
+ }
+ }
+
+ poptFreeContext(pc);
+
+#ifdef HAVE_SETLUID
+ /* needed for SecureWare on SCO */
+ setluid(0);
+#endif
+
+ sec_init();
+
+ load_case_tables();
+
+ set_remote_machine_name("smbd", False);
+
+ if (interactive) {
+ Fork = False;
+ log_stdout = True;
+ }
+
+ if (log_stdout && Fork) {
+ DEBUG(0,("ERROR: Can't log to stdout (-S) unless daemon is in foreground (-F) or interactive (-i)\n"));
+ exit(1);
+ }
+
+ setup_logging(argv[0],log_stdout);
+
+ /* we want to re-seed early to prevent time delays causing
+ client problems at a later date. (tridge) */
+ generate_random_buffer(NULL, 0, False);
+
+ /* make absolutely sure we run as root - to handle cases where people
+ are crazy enough to have it setuid */
+
+ gain_root_privilege();
+ gain_root_group_privilege();
+
+ fault_setup((void (*)(void *))exit_server);
+ CatchSignal(SIGTERM , SIGNAL_CAST sig_term);
+ CatchSignal(SIGHUP,SIGNAL_CAST sig_hup);
+
+ /* we are never interested in SIGPIPE */
+ BlockSignals(True,SIGPIPE);
+
+#if defined(SIGFPE)
+ /* we are never interested in SIGFPE */
+ BlockSignals(True,SIGFPE);
+#endif
+
+#if defined(SIGUSR2)
+ /* We are no longer interested in USR2 */
+ BlockSignals(True,SIGUSR2);
+#endif
+
+ /* POSIX demands that signals are inherited. If the invoking process has
+ * these signals masked, we will have problems, as we won't recieve them. */
+ BlockSignals(False, SIGHUP);
+ BlockSignals(False, SIGUSR1);
+ BlockSignals(False, SIGTERM);
+
+ /* we want total control over the permissions on created files,
+ so set our umask to 0 */
+ umask(0);
+
+ init_sec_ctx();
+
+ reopen_logs();
+
+ DEBUG(0,( "smbd version %s started.\n", SAMBA_VERSION_STRING));
+ DEBUGADD(0,( "Copyright Andrew Tridgell and the Samba Team 1992-2004\n"));
+
+ DEBUG(2,("uid=%d gid=%d euid=%d egid=%d\n",
+ (int)getuid(),(int)getgid(),(int)geteuid(),(int)getegid()));
+
+ /* Output the build options to the debug log */
+ build_options(False);
+
+ if (sizeof(uint16) < 2 || sizeof(uint32) < 4) {
+ DEBUG(0,("ERROR: Samba is not configured correctly for the word size on your machine\n"));
+ exit(1);
+ }
+
+ /*
+ * Do this before reload_services.
+ */
+
+ if (!reload_services(False))
+ return(-1);
+
+ init_structs();
+
+#ifdef WITH_PROFILE
+ if (!profile_setup(False)) {
+ DEBUG(0,("ERROR: failed to setup profiling\n"));
+ return -1;
+ }
+#endif
+
+ DEBUG(3,( "loaded services\n"));
+
+ if (!is_daemon && !is_a_socket(0)) {
+ if (!interactive)
+ DEBUG(0,("standard input is not a socket, assuming -D option\n"));
+
+ /*
+ * Setting is_daemon here prevents us from eventually calling
+ * the open_sockets_inetd()
+ */
+
+ is_daemon = True;
+ }
+
+ if (is_daemon && !interactive) {
+ DEBUG( 3, ( "Becoming a daemon.\n" ) );
+ become_daemon(Fork);
+ }
+
+#if HAVE_SETPGID
+ /*
+ * If we're interactive we want to set our own process group for
+ * signal management.
+ */
+ if (interactive)
+ setpgid( (pid_t)0, (pid_t)0);
+#endif
+
+ if (!directory_exist(lp_lockdir(), NULL))
+ mkdir(lp_lockdir(), 0755);
+
+ if (is_daemon)
+ pidfile_create("smbd");
+
+ /* Setup all the TDB's - including CLEAR_IF_FIRST tdb's. */
+ if (!message_init())
+ exit(1);
+
+ if (!session_init())
+ exit(1);
+
+ if (conn_tdb_ctx() == NULL)
+ exit(1);
+
+ if (!locking_init(0))
+ exit(1);
+
+ if (!share_info_db_init())
+ exit(1);
+
+ namecache_enable();
+
+ if (!init_registry())
+ exit(1);
+
+ if (!print_backend_init())
+ exit(1);
+
+ /* Setup the main smbd so that we can get messages. */
+ claim_connection(NULL,"",0,True,FLAG_MSG_GENERAL|FLAG_MSG_SMBD);
+
+ /*
+ DO NOT ENABLE THIS TILL YOU COPE WITH KILLING THESE TASKS AND INETD
+ THIS *killed* LOTS OF BUILD FARM MACHINES. IT CREATED HUNDREDS OF
+ smbd PROCESSES THAT NEVER DIE
+ start_background_queue();
+ */
+
+ if (!open_sockets_smbd(is_daemon, interactive, ports))
+ exit(1);
+
+ /*
+ * everything after this point is run after the fork()
+ */
+
+ /* Initialise the password backed before the global_sam_sid
+ to ensure that we fetch from ldap before we make a domain sid up */
+
+ if(!initialize_password_db(False))
+ exit(1);
+
+ if(!get_global_sam_sid()) {
+ DEBUG(0,("ERROR: Samba cannot create a SAM SID.\n"));
+ exit(1);
+ }
+
+ static_init_rpc;
+
+ init_modules();
+
+ /* possibly reload the services file. */
+ reload_services(True);
+
+ if (!init_account_policy()) {
+ DEBUG(0,("Could not open account policy tdb.\n"));
+ exit(1);
+ }
+
+ if (*lp_rootdir()) {
+ if (sys_chroot(lp_rootdir()) == 0)
+ DEBUG(2,("Changed root to %s\n", lp_rootdir()));
+ }
+
+ /* Setup oplocks */
+ if (!init_oplocks())
+ exit(1);
+
+ /* Setup change notify */
+ if (!init_change_notify())
+ exit(1);
+
+ /* re-initialise the timezone */
+ TimeInit();
+
+ /* register our message handlers */
+ message_register(MSG_SMB_FORCE_TDIS, msg_force_tdis);
+
+ smbd_process();
+
+ namecache_shutdown();
+ exit_server("normal exit");
+ return(0);
+}
diff --git a/source/smbd/service.c b/source/smbd/service.c
new file mode 100644
index 00000000000..1910ef9b72b
--- /dev/null
+++ b/source/smbd/service.c
@@ -0,0 +1,838 @@
+/*
+ Unix SMB/CIFS implementation.
+ service (connection) opening and closing
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+extern struct timeval smb_last_time;
+extern int case_default;
+extern BOOL case_preserve;
+extern BOOL short_case_preserve;
+extern BOOL case_mangle;
+extern BOOL case_sensitive;
+extern BOOL use_mangled_map;
+extern userdom_struct current_user_info;
+
+
+/****************************************************************************
+ Load parameters specific to a connection/service.
+****************************************************************************/
+
+BOOL set_current_service(connection_struct *conn,BOOL do_chdir)
+{
+ extern char magic_char;
+ static connection_struct *last_conn;
+ int snum;
+
+ if (!conn) {
+ last_conn = NULL;
+ return(False);
+ }
+
+ conn->lastused = smb_last_time.tv_sec;
+
+ snum = SNUM(conn);
+
+ if (do_chdir &&
+ vfs_ChDir(conn,conn->connectpath) != 0 &&
+ vfs_ChDir(conn,conn->origpath) != 0) {
+ DEBUG(0,("chdir (%s) failed\n",
+ conn->connectpath));
+ return(False);
+ }
+
+ if (conn == last_conn)
+ return(True);
+
+ last_conn = conn;
+
+ case_default = lp_defaultcase(snum);
+ case_preserve = lp_preservecase(snum);
+ short_case_preserve = lp_shortpreservecase(snum);
+ case_mangle = lp_casemangle(snum);
+ case_sensitive = lp_casesensitive(snum);
+ magic_char = lp_magicchar(snum);
+ use_mangled_map = (*lp_mangled_map(snum) ? True:False);
+ return(True);
+}
+
+/****************************************************************************
+ Add a home service. Returns the new service number or -1 if fail.
+****************************************************************************/
+
+int add_home_service(const char *service, const char *username, const char *homedir)
+{
+ int iHomeService;
+
+ if (!service || !homedir)
+ return -1;
+
+ if ((iHomeService = lp_servicenumber(HOMES_NAME)) < 0)
+ return -1;
+
+ /*
+ * If this is a winbindd provided username, remove
+ * the domain component before adding the service.
+ * Log a warning if the "path=" parameter does not
+ * include any macros.
+ */
+
+ {
+ const char *p = strchr(service,*lp_winbind_separator());
+
+ /* We only want the 'user' part of the string */
+ if (p) {
+ service = p + 1;
+ }
+ }
+
+ if (!lp_add_home(service, iHomeService, username, homedir)) {
+ return -1;
+ }
+
+ return lp_servicenumber(service);
+
+}
+
+
+/**
+ * Find a service entry.
+ *
+ * @param service is modified (to canonical form??)
+ **/
+
+int find_service(fstring service)
+{
+ int iService;
+
+ all_string_sub(service,"\\","/",0);
+
+ iService = lp_servicenumber(service);
+
+ /* now handle the special case of a home directory */
+ if (iService < 0) {
+ char *phome_dir = get_user_home_dir(service);
+
+ if(!phome_dir) {
+ /*
+ * Try mapping the servicename, it may
+ * be a Windows to unix mapped user name.
+ */
+ if(map_username(service))
+ phome_dir = get_user_home_dir(service);
+ }
+
+ DEBUG(3,("checking for home directory %s gave %s\n",service,
+ phome_dir?phome_dir:"(NULL)"));
+
+ iService = add_home_service(service,service /* 'username' */, phome_dir);
+ }
+
+ /* If we still don't have a service, attempt to add it as a printer. */
+ if (iService < 0) {
+ int iPrinterService;
+
+ if ((iPrinterService = lp_servicenumber(PRINTERS_NAME)) >= 0) {
+ char *pszTemp;
+
+ DEBUG(3,("checking whether %s is a valid printer name...\n", service));
+ pszTemp = lp_printcapname();
+ if ((pszTemp != NULL) && pcap_printername_ok(service, pszTemp)) {
+ DEBUG(3,("%s is a valid printer name\n", service));
+ DEBUG(3,("adding %s as a printer service\n", service));
+ lp_add_printer(service, iPrinterService);
+ iService = lp_servicenumber(service);
+ if (iService < 0) {
+ DEBUG(0,("failed to add %s as a printer service!\n", service));
+ }
+ } else {
+ DEBUG(3,("%s is not a valid printer name\n", service));
+ }
+ }
+ }
+
+ /* Check for default vfs service? Unsure whether to implement this */
+ if (iService < 0) {
+ }
+
+ /* just possibly it's a default service? */
+ if (iService < 0) {
+ char *pdefservice = lp_defaultservice();
+ if (pdefservice && *pdefservice && !strequal(pdefservice,service) && !strstr_m(service,"..")) {
+ /*
+ * We need to do a local copy here as lp_defaultservice()
+ * returns one of the rotating lp_string buffers that
+ * could get overwritten by the recursive find_service() call
+ * below. Fix from Josef Hinteregger <joehtg@joehtg.co.at>.
+ */
+ pstring defservice;
+ pstrcpy(defservice, pdefservice);
+ iService = find_service(defservice);
+ if (iService >= 0) {
+ all_string_sub(service, "_","/",0);
+ iService = lp_add_service(service, iService);
+ }
+ }
+ }
+
+ if (iService >= 0) {
+ if (!VALID_SNUM(iService)) {
+ DEBUG(0,("Invalid snum %d for %s\n",iService, service));
+ iService = -1;
+ }
+ }
+
+ if (iService < 0)
+ DEBUG(3,("find_service() failed to find service %s\n", service));
+
+ return (iService);
+}
+
+
+/****************************************************************************
+ do some basic sainity checks on the share.
+ This function modifies dev, ecode.
+****************************************************************************/
+
+static NTSTATUS share_sanity_checks(int snum, fstring dev)
+{
+
+ if (!lp_snum_ok(snum) ||
+ !check_access(smbd_server_fd(),
+ lp_hostsallow(snum), lp_hostsdeny(snum))) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (dev[0] == '?' || !dev[0]) {
+ if (lp_print_ok(snum)) {
+ fstrcpy(dev,"LPT1:");
+ } else if (strequal(lp_fstype(snum), "IPC")) {
+ fstrcpy(dev, "IPC");
+ } else {
+ fstrcpy(dev,"A:");
+ }
+ }
+
+ strupper_m(dev);
+
+ if (lp_print_ok(snum)) {
+ if (!strequal(dev, "LPT1:")) {
+ return NT_STATUS_BAD_DEVICE_TYPE;
+ }
+ } else if (strequal(lp_fstype(snum), "IPC")) {
+ if (!strequal(dev, "IPC")) {
+ return NT_STATUS_BAD_DEVICE_TYPE;
+ }
+ } else if (!strequal(dev, "A:")) {
+ return NT_STATUS_BAD_DEVICE_TYPE;
+ }
+
+ /* Behave as a printer if we are supposed to */
+ if (lp_print_ok(snum) && (strcmp(dev, "A:") == 0)) {
+ fstrcpy(dev, "LPT1:");
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Make a connection, given the snum to connect to, and the vuser of the
+ connecting user if appropriate.
+****************************************************************************/
+
+static connection_struct *make_connection_snum(int snum, user_struct *vuser,
+ DATA_BLOB password,
+ const char *pdev, NTSTATUS *status)
+{
+ struct passwd *pass = NULL;
+ BOOL guest = False;
+ connection_struct *conn;
+ struct stat st;
+ fstring user;
+ fstring dev;
+
+ *user = 0;
+ fstrcpy(dev, pdev);
+
+ if (NT_STATUS_IS_ERR(*status = share_sanity_checks(snum, dev))) {
+ return NULL;
+ }
+
+ conn = conn_new();
+ if (!conn) {
+ DEBUG(0,("Couldn't find free connection.\n"));
+ *status = NT_STATUS_INSUFFICIENT_RESOURCES;
+ return NULL;
+ }
+
+ if (lp_guest_only(snum)) {
+ const char *guestname = lp_guestaccount();
+ guest = True;
+ pass = getpwnam_alloc(guestname);
+ if (!pass) {
+ DEBUG(0,("make_connection_snum: Invalid guest account %s??\n",guestname));
+ conn_free(conn);
+ *status = NT_STATUS_NO_SUCH_USER;
+ return NULL;
+ }
+ fstrcpy(user,pass->pw_name);
+ conn->force_user = True;
+ conn->uid = pass->pw_uid;
+ conn->gid = pass->pw_gid;
+ string_set(&conn->user,pass->pw_name);
+ passwd_free(&pass);
+ DEBUG(3,("Guest only user %s\n",user));
+ } else if (vuser) {
+ if (vuser->guest) {
+ if (!lp_guest_ok(snum)) {
+ DEBUG(2, ("guest user (from session setup) not permitted to access this share (%s)\n", lp_servicename(snum)));
+ conn_free(conn);
+ *status = NT_STATUS_ACCESS_DENIED;
+ return NULL;
+ }
+ } else {
+ if (!user_ok(vuser->user.unix_name, snum, vuser->groups, vuser->n_groups)) {
+ DEBUG(2, ("user '%s' (from session setup) not permitted to access this share (%s)\n", vuser->user.unix_name, lp_servicename(snum)));
+ conn_free(conn);
+ *status = NT_STATUS_ACCESS_DENIED;
+ return NULL;
+ }
+ }
+ conn->vuid = vuser->vuid;
+ conn->uid = vuser->uid;
+ conn->gid = vuser->gid;
+ string_set(&conn->user,vuser->user.unix_name);
+ fstrcpy(user,vuser->user.unix_name);
+ guest = vuser->guest;
+ } else if (lp_security() == SEC_SHARE) {
+ /* add it as a possible user name if we
+ are in share mode security */
+ add_session_user(lp_servicename(snum));
+ /* shall we let them in? */
+ if (!authorise_login(snum,user,password,&guest)) {
+ DEBUG( 2, ( "Invalid username/password for [%s]\n",
+ lp_servicename(snum)) );
+ conn_free(conn);
+ *status = NT_STATUS_WRONG_PASSWORD;
+ return NULL;
+ }
+ pass = Get_Pwnam(user);
+ conn->force_user = True;
+ conn->uid = pass->pw_uid;
+ conn->gid = pass->pw_gid;
+ string_set(&conn->user, pass->pw_name);
+ fstrcpy(user, pass->pw_name);
+
+ } else {
+ DEBUG(0, ("invalid VUID (vuser) but not in security=share\n"));
+ conn_free(conn);
+ *status = NT_STATUS_ACCESS_DENIED;
+ return NULL;
+ }
+
+ add_session_user(user);
+
+ safe_strcpy(conn->client_address, client_addr(),
+ sizeof(conn->client_address)-1);
+ conn->num_files_open = 0;
+ conn->lastused = time(NULL);
+ conn->service = snum;
+ conn->used = True;
+ conn->printer = (strncmp(dev,"LPT",3) == 0);
+ conn->ipc = ((strncmp(dev,"IPC",3) == 0) || strequal(dev,"ADMIN$"));
+ conn->dirptr = NULL;
+ conn->veto_list = NULL;
+ conn->hide_list = NULL;
+ conn->veto_oplock_list = NULL;
+ string_set(&conn->dirpath,"");
+ string_set(&conn->user,user);
+ conn->nt_user_token = NULL;
+ conn->privs = NULL;
+
+ conn->read_only = lp_readonly(conn->service);
+ conn->admin_user = False;
+
+ /*
+ * If force user is true, then store the
+ * given userid and also the groups
+ * of the user we're forcing.
+ */
+
+ if (*lp_force_user(snum)) {
+ struct passwd *pass2;
+ pstring fuser;
+ pstrcpy(fuser,lp_force_user(snum));
+
+ /* Allow %S to be used by force user. */
+ pstring_sub(fuser,"%S",lp_servicename(snum));
+
+ pass2 = (struct passwd *)Get_Pwnam(fuser);
+ if (pass2) {
+ conn->uid = pass2->pw_uid;
+ conn->gid = pass2->pw_gid;
+ string_set(&conn->user,pass2->pw_name);
+ fstrcpy(user,pass2->pw_name);
+ conn->force_user = True;
+ DEBUG(3,("Forced user %s\n",user));
+ } else {
+ DEBUG(1,("Couldn't find user %s\n",fuser));
+ conn_free(conn);
+ *status = NT_STATUS_NO_SUCH_USER;
+ return NULL;
+ }
+ }
+
+#ifdef HAVE_GETGRNAM
+ /*
+ * If force group is true, then override
+ * any groupid stored for the connecting user.
+ */
+
+ if (*lp_force_group(snum)) {
+ gid_t gid;
+ pstring gname;
+ pstring tmp_gname;
+ BOOL user_must_be_member = False;
+
+ pstrcpy(tmp_gname,lp_force_group(snum));
+
+ if (tmp_gname[0] == '+') {
+ user_must_be_member = True;
+ /* even now, tmp_gname is null terminated */
+ pstrcpy(gname,&tmp_gname[1]);
+ } else {
+ pstrcpy(gname,tmp_gname);
+ }
+ /* default service may be a group name */
+ pstring_sub(gname,"%S",lp_servicename(snum));
+ gid = nametogid(gname);
+
+ if (gid != (gid_t)-1) {
+
+ /*
+ * If the user has been forced and the forced group starts
+ * with a '+', then we only set the group to be the forced
+ * group if the forced user is a member of that group.
+ * Otherwise, the meaning of the '+' would be ignored.
+ */
+ if (conn->force_user && user_must_be_member) {
+ if (user_in_group_list( user, gname, NULL, 0)) {
+ conn->gid = gid;
+ DEBUG(3,("Forced group %s for member %s\n",gname,user));
+ }
+ } else {
+ conn->gid = gid;
+ DEBUG(3,("Forced group %s\n",gname));
+ }
+ conn->force_group = True;
+ } else {
+ DEBUG(1,("Couldn't find group %s\n",gname));
+ conn_free(conn);
+ *status = NT_STATUS_NO_SUCH_GROUP;
+ return NULL;
+ }
+ }
+#endif /* HAVE_GETGRNAM */
+
+ {
+ pstring s;
+ pstrcpy(s,lp_pathname(snum));
+ standard_sub_conn(conn,s,sizeof(s));
+ string_set(&conn->connectpath,s);
+ DEBUG(3,("Connect path is '%s' for service [%s]\n",s, lp_servicename(snum)));
+ }
+
+ if (conn->force_user || conn->force_group) {
+
+ /* groups stuff added by ih */
+ conn->ngroups = 0;
+ conn->groups = NULL;
+
+ /* Find all the groups this uid is in and
+ store them. Used by change_to_user() */
+ initialise_groups(conn->user, conn->uid, conn->gid);
+ get_current_groups(conn->gid, &conn->ngroups,&conn->groups);
+
+ conn->nt_user_token = create_nt_token(conn->uid, conn->gid,
+ conn->ngroups, conn->groups,
+ guest);
+
+ init_privilege(&(conn->privs));
+ pdb_get_privilege_set(conn->nt_user_token->user_sids, conn->nt_user_token->num_sids, conn->privs);
+ }
+
+ /*
+ * New code to check if there's a share security descripter
+ * added from NT server manager. This is done after the
+ * smb.conf checks are done as we need a uid and token. JRA.
+ *
+ */
+
+ {
+ BOOL can_write = share_access_check(conn, snum, vuser, FILE_WRITE_DATA);
+
+ if (!can_write) {
+ if (!share_access_check(conn, snum, vuser, FILE_READ_DATA)) {
+ /* No access, read or write. */
+ DEBUG(0,( "make_connection: connection to %s denied due to security descriptor.\n",
+ lp_servicename(snum)));
+ conn_free(conn);
+ *status = NT_STATUS_ACCESS_DENIED;
+ return NULL;
+ } else {
+ conn->read_only = True;
+ }
+ }
+ }
+ /* Initialise VFS function pointers */
+
+ if (!smbd_vfs_init(conn)) {
+ DEBUG(0, ("vfs_init failed for service %s\n", lp_servicename(SNUM(conn))));
+ conn_free(conn);
+ *status = NT_STATUS_BAD_NETWORK_NAME;
+ return NULL;
+ }
+
+/* ROOT Activities: */
+ /* check number of connections */
+ if (!claim_connection(conn,
+ lp_servicename(SNUM(conn)),
+ lp_max_connections(SNUM(conn)),
+ False,0)) {
+ DEBUG(1,("too many connections - rejected\n"));
+ conn_free(conn);
+ *status = NT_STATUS_INSUFFICIENT_RESOURCES;
+ return NULL;
+ }
+
+ /* Preexecs are done here as they might make the dir we are to ChDir to below */
+ /* execute any "root preexec = " line */
+ if (*lp_rootpreexec(SNUM(conn))) {
+ int ret;
+ pstring cmd;
+ pstrcpy(cmd,lp_rootpreexec(SNUM(conn)));
+ standard_sub_conn(conn,cmd,sizeof(cmd));
+ DEBUG(5,("cmd=%s\n",cmd));
+ ret = smbrun(cmd,NULL);
+ if (ret != 0 && lp_rootpreexec_close(SNUM(conn))) {
+ DEBUG(1,("root preexec gave %d - failing connection\n", ret));
+ yield_connection(conn, lp_servicename(SNUM(conn)));
+ conn_free(conn);
+ *status = NT_STATUS_ACCESS_DENIED;
+ return NULL;
+ }
+ }
+
+/* USER Activites: */
+ if (!change_to_user(conn, conn->vuid)) {
+ /* No point continuing if they fail the basic checks */
+ DEBUG(0,("Can't become connected user!\n"));
+ conn_free(conn);
+ *status = NT_STATUS_LOGON_FAILURE;
+ return NULL;
+ }
+
+ /* Remember that a different vuid can connect later without these checks... */
+
+ /* Preexecs are done here as they might make the dir we are to ChDir to below */
+ /* execute any "preexec = " line */
+ if (*lp_preexec(SNUM(conn))) {
+ int ret;
+ pstring cmd;
+ pstrcpy(cmd,lp_preexec(SNUM(conn)));
+ standard_sub_conn(conn,cmd,sizeof(cmd));
+ ret = smbrun(cmd,NULL);
+ if (ret != 0 && lp_preexec_close(SNUM(conn))) {
+ DEBUG(1,("preexec gave %d - failing connection\n", ret));
+ change_to_root_user();
+ yield_connection(conn, lp_servicename(SNUM(conn)));
+ conn_free(conn);
+ *status = NT_STATUS_ACCESS_DENIED;
+ return NULL;
+ }
+ }
+
+#ifdef WITH_FAKE_KASERVER
+ if (lp_afs_share(SNUM(conn))) {
+ afs_login(conn);
+ }
+#endif
+
+#if CHECK_PATH_ON_TCONX
+ /* win2000 does not check the permissions on the directory
+ during the tree connect, instead relying on permission
+ check during individual operations. To match this behaviour
+ I have disabled this chdir check (tridge) */
+ if (vfs_ChDir(conn,conn->connectpath) != 0) {
+ DEBUG(0,("%s (%s) Can't change directory to %s (%s)\n",
+ get_remote_machine_name(), conn->client_address,
+ conn->connectpath,strerror(errno)));
+ change_to_root_user();
+ yield_connection(conn, lp_servicename(SNUM(conn)));
+ conn_free(conn);
+ *status = NT_STATUS_BAD_NETWORK_NAME;
+ return NULL;
+ }
+#else
+ /* the alternative is just to check the directory exists */
+ if (stat(conn->connectpath, &st) != 0 || !S_ISDIR(st.st_mode)) {
+ DEBUG(0,("'%s' does not exist or is not a directory, when connecting to [%s]\n", conn->connectpath, lp_servicename(SNUM(conn))));
+ change_to_root_user();
+ yield_connection(conn, lp_servicename(SNUM(conn)));
+ conn_free(conn);
+ *status = NT_STATUS_BAD_NETWORK_NAME;
+ return NULL;
+ }
+#endif
+
+ string_set(&conn->origpath,conn->connectpath);
+
+#if SOFTLINK_OPTIMISATION
+ /* resolve any soft links early if possible */
+ if (vfs_ChDir(conn,conn->connectpath) == 0) {
+ pstring s;
+ pstrcpy(s,conn->connectpath);
+ vfs_GetWd(conn,s);
+ string_set(&conn->connectpath,s);
+ vfs_ChDir(conn,conn->connectpath);
+ }
+#endif
+
+ /*
+ * Print out the 'connected as' stuff here as we need
+ * to know the effective uid and gid we will be using
+ * (at least initially).
+ */
+
+ if( DEBUGLVL( IS_IPC(conn) ? 3 : 1 ) ) {
+ dbgtext( "%s (%s) ", get_remote_machine_name(), conn->client_address );
+ dbgtext( "%s", srv_is_signing_active() ? "signed " : "");
+ dbgtext( "connect to service %s ", lp_servicename(SNUM(conn)) );
+ dbgtext( "initially as user %s ", user );
+ dbgtext( "(uid=%d, gid=%d) ", (int)geteuid(), (int)getegid() );
+ dbgtext( "(pid %d)\n", (int)sys_getpid() );
+ }
+
+ /* Add veto/hide lists */
+ if (!IS_IPC(conn) && !IS_PRINT(conn)) {
+ set_namearray( &conn->veto_list, lp_veto_files(SNUM(conn)));
+ set_namearray( &conn->hide_list, lp_hide_files(SNUM(conn)));
+ set_namearray( &conn->veto_oplock_list, lp_veto_oplocks(SNUM(conn)));
+ }
+
+ /* Invoke VFS make connection hook */
+
+ if (SMB_VFS_CONNECT(conn, lp_servicename(snum), user) < 0) {
+ DEBUG(0,("make_connection: VFS make connection failed!\n"));
+ change_to_root_user();
+ conn_free(conn);
+ *status = NT_STATUS_UNSUCCESSFUL;
+ return NULL;
+ }
+
+ /* we've finished with the user stuff - go back to root */
+ change_to_root_user();
+
+ return(conn);
+}
+
+/***************************************************************************************
+ Simple wrapper function for make_connection() to include a call to
+ vfs_chdir()
+ **************************************************************************************/
+
+connection_struct *make_connection_with_chdir(const char *service_in, DATA_BLOB password,
+ const char *dev, uint16 vuid, NTSTATUS *status)
+{
+ connection_struct *conn = NULL;
+
+ conn = make_connection(service_in, password, dev, vuid, status);
+
+ /*
+ * make_connection() does not change the directory for us any more
+ * so we have to do it as a separate step --jerry
+ */
+
+ if ( conn && vfs_ChDir(conn,conn->connectpath) != 0 ) {
+ DEBUG(0,("move_driver_to_download_area: Can't change directory to %s for [print$] (%s)\n",
+ conn->connectpath,strerror(errno)));
+ yield_connection(conn, lp_servicename(SNUM(conn)));
+ conn_free(conn);
+ *status = NT_STATUS_UNSUCCESSFUL;
+ return NULL;
+ }
+
+ return conn;
+}
+
+/****************************************************************************
+ Make a connection to a service.
+ *
+ * @param service
+****************************************************************************/
+
+connection_struct *make_connection(const char *service_in, DATA_BLOB password,
+ const char *pdev, uint16 vuid, NTSTATUS *status)
+{
+ uid_t euid;
+ user_struct *vuser = NULL;
+ fstring service;
+ fstring dev;
+ int snum = -1;
+
+ fstrcpy(dev, pdev);
+
+ /* This must ONLY BE CALLED AS ROOT. As it exits this function as root. */
+ if (!non_root_mode() && (euid = geteuid()) != 0) {
+ DEBUG(0,("make_connection: PANIC ERROR. Called as nonroot (%u)\n", (unsigned int)euid ));
+ smb_panic("make_connection: PANIC ERROR. Called as nonroot\n");
+ }
+
+ if(lp_security() != SEC_SHARE) {
+ vuser = get_valid_user_struct(vuid);
+ if (!vuser) {
+ DEBUG(1,("make_connection: refusing to connect with no session setup\n"));
+ *status = NT_STATUS_ACCESS_DENIED;
+ return NULL;
+ }
+ }
+
+ /* Logic to try and connect to the correct [homes] share, preferably without too many
+ getpwnam() lookups. This is particulary nasty for winbind usernames, where the
+ share name isn't the same as unix username.
+
+ The snum of the homes share is stored on the vuser at session setup time.
+ */
+
+ if (strequal(service_in,HOMES_NAME)) {
+ if(lp_security() != SEC_SHARE) {
+ DATA_BLOB no_pw = data_blob(NULL, 0);
+ if (vuser->homes_snum == -1) {
+ DEBUG(2, ("[homes] share not available for this user because it was not found or created at session setup time\n"));
+ *status = NT_STATUS_BAD_NETWORK_NAME;
+ return NULL;
+ }
+ DEBUG(5, ("making a connection to [homes] service created at session setup time\n"));
+ return make_connection_snum(vuser->homes_snum,
+ vuser, no_pw,
+ dev, status);
+ } else {
+ /* Security = share. Try with current_user_info.smb_name
+ * as the username. */
+ if (*current_user_info.smb_name) {
+ fstring unix_username;
+ fstrcpy(unix_username,
+ current_user_info.smb_name);
+ map_username(unix_username);
+ snum = find_service(unix_username);
+ }
+ if (snum != -1) {
+ DEBUG(5, ("making a connection to 'homes' service %s based on security=share\n", service_in));
+ return make_connection_snum(snum, NULL,
+ password,
+ dev, status);
+ }
+ }
+ } else if ((lp_security() != SEC_SHARE) && (vuser->homes_snum != -1)
+ && strequal(service_in, lp_servicename(vuser->homes_snum))) {
+ DATA_BLOB no_pw = data_blob(NULL, 0);
+ DEBUG(5, ("making a connection to 'homes' service [%s] created at session setup time\n", service_in));
+ return make_connection_snum(vuser->homes_snum,
+ vuser, no_pw,
+ dev, status);
+ }
+
+ fstrcpy(service, service_in);
+
+ strlower_m(service);
+
+ snum = find_service(service);
+
+ if (snum < 0) {
+ if (strequal(service,"IPC$") || strequal(service,"ADMIN$")) {
+ DEBUG(3,("refusing IPC connection to %s\n", service));
+ *status = NT_STATUS_ACCESS_DENIED;
+ return NULL;
+ }
+
+ DEBUG(0,("%s (%s) couldn't find service %s\n",
+ get_remote_machine_name(), client_addr(), service));
+ *status = NT_STATUS_BAD_NETWORK_NAME;
+ return NULL;
+ }
+
+ /* Handle non-Dfs clients attempting connections to msdfs proxy */
+ if (lp_host_msdfs() && (*lp_msdfs_proxy(snum) != '\0')) {
+ DEBUG(3, ("refusing connection to dfs proxy '%s'\n", service));
+ *status = NT_STATUS_BAD_NETWORK_NAME;
+ return NULL;
+ }
+
+ DEBUG(5, ("making a connection to 'normal' service %s\n", service));
+
+ return make_connection_snum(snum, vuser,
+ password,
+ dev, status);
+}
+
+/****************************************************************************
+close a cnum
+****************************************************************************/
+void close_cnum(connection_struct *conn, uint16 vuid)
+{
+ DirCacheFlush(SNUM(conn));
+
+ change_to_root_user();
+
+ DEBUG(IS_IPC(conn)?3:1, ("%s (%s) closed connection to service %s\n",
+ get_remote_machine_name(),conn->client_address,
+ lp_servicename(SNUM(conn))));
+
+ /* Call VFS disconnect hook */
+ SMB_VFS_DISCONNECT(conn);
+
+ yield_connection(conn, lp_servicename(SNUM(conn)));
+
+ file_close_conn(conn);
+ dptr_closecnum(conn);
+
+ /* make sure we leave the directory available for unmount */
+ vfs_ChDir(conn, "/");
+
+ /* execute any "postexec = " line */
+ if (*lp_postexec(SNUM(conn)) &&
+ change_to_user(conn, vuid)) {
+ pstring cmd;
+ pstrcpy(cmd,lp_postexec(SNUM(conn)));
+ standard_sub_conn(conn,cmd,sizeof(cmd));
+ smbrun(cmd,NULL);
+ change_to_root_user();
+ }
+
+ change_to_root_user();
+ /* execute any "root postexec = " line */
+ if (*lp_rootpostexec(SNUM(conn))) {
+ pstring cmd;
+ pstrcpy(cmd,lp_rootpostexec(SNUM(conn)));
+ standard_sub_conn(conn,cmd,sizeof(cmd));
+ smbrun(cmd,NULL);
+ }
+
+ conn_free(conn);
+}
diff --git a/source/smbd/session.c b/source/smbd/session.c
new file mode 100644
index 00000000000..61118f13dd9
--- /dev/null
+++ b/source/smbd/session.c
@@ -0,0 +1,250 @@
+/*
+ Unix SMB/CIFS implementation.
+ session handling for utmp and PAM
+ Copyright (C) tridge@samba.org 2001
+ Copyright (C) abartlet@pcug.org.au 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* a "session" is claimed when we do a SessionSetupX operation
+ and is yielded when the corresponding vuid is destroyed.
+
+ sessions are used to populate utmp and PAM session structures
+*/
+
+#include "includes.h"
+
+static TDB_CONTEXT *tdb;
+
+BOOL session_init(void)
+{
+ if (tdb)
+ return True;
+
+ tdb = tdb_open_ex(lock_path("sessionid.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT,
+ O_RDWR | O_CREAT, 0644, smbd_tdb_log);
+ if (!tdb) {
+ DEBUG(1,("session_init: failed to open sessionid tdb\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/* called when a session is created */
+BOOL session_claim(user_struct *vuser)
+{
+ int i = 0;
+ TDB_DATA data;
+ struct sockaddr sa;
+ struct in_addr *client_ip;
+ struct sessionid sessionid;
+ uint32 pid = (uint32)sys_getpid();
+ TDB_DATA key;
+ fstring keystr;
+ char * hostname;
+ int tdb_store_flag; /* If using utmp, we do an inital 'lock hold' store,
+ but we don't need this if we are just using the
+ (unique) pid/vuid combination */
+
+ vuser->session_keystr = NULL;
+
+ /* don't register sessions for the guest user - its just too
+ expensive to go through pam session code for browsing etc */
+ if (vuser->guest) {
+ return True;
+ }
+
+ if (!session_init())
+ return False;
+
+ ZERO_STRUCT(sessionid);
+
+ data.dptr = NULL;
+ data.dsize = 0;
+
+ if (lp_utmp()) {
+ for (i=1;i<MAX_SESSION_ID;i++) {
+ slprintf(keystr, sizeof(keystr)-1, "ID/%d", i);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr)+1;
+
+ if (tdb_store(tdb, key, data, TDB_INSERT) == 0) break;
+ }
+
+ if (i == MAX_SESSION_ID) {
+ DEBUG(1,("session_claim: out of session IDs (max is %d)\n",
+ MAX_SESSION_ID));
+ return False;
+ }
+ slprintf(sessionid.id_str, sizeof(sessionid.id_str)-1, SESSION_UTMP_TEMPLATE, i);
+ tdb_store_flag = TDB_MODIFY;
+ } else
+ {
+ slprintf(keystr, sizeof(keystr)-1, "ID/%lu/%u",
+ (long unsigned int)sys_getpid(),
+ vuser->vuid);
+ slprintf(sessionid.id_str, sizeof(sessionid.id_str)-1,
+ SESSION_TEMPLATE, (long unsigned int)sys_getpid(),
+ vuser->vuid);
+
+ key.dptr = keystr;
+ key.dsize = strlen(keystr)+1;
+
+ tdb_store_flag = TDB_REPLACE;
+ }
+
+ /* If 'hostname lookup' == yes, then do the DNS lookup. This is
+ needed because utmp and PAM both expect DNS names
+
+ client_name() handles this case internally.
+ */
+
+ hostname = client_name();
+ if (strcmp(hostname, "UNKNOWN") == 0) {
+ hostname = client_addr();
+ }
+
+ fstrcpy(sessionid.username, vuser->user.unix_name);
+ fstrcpy(sessionid.hostname, hostname);
+ sessionid.id_num = i; /* Only valid for utmp sessions */
+ sessionid.pid = pid;
+ sessionid.uid = vuser->uid;
+ sessionid.gid = vuser->gid;
+ fstrcpy(sessionid.remote_machine, get_remote_machine_name());
+ fstrcpy(sessionid.ip_addr, client_addr());
+
+ client_ip = client_inaddr(&sa);
+
+ if (!smb_pam_claim_session(sessionid.username, sessionid.id_str, sessionid.hostname)) {
+ DEBUG(1,("pam_session rejected the session for %s [%s]\n",
+ sessionid.username, sessionid.id_str));
+ if (tdb_store_flag == TDB_MODIFY) {
+ tdb_delete(tdb, key);
+ }
+ return False;
+ }
+
+ data.dptr = (char *)&sessionid;
+ data.dsize = sizeof(sessionid);
+ if (tdb_store(tdb, key, data, tdb_store_flag) != 0) {
+ DEBUG(1,("session_claim: unable to create session id record\n"));
+ return False;
+ }
+
+ if (lp_utmp()) {
+ sys_utmp_claim(sessionid.username, sessionid.hostname,
+ client_ip,
+ sessionid.id_str, sessionid.id_num);
+ }
+
+ vuser->session_keystr = strdup(keystr);
+ if (!vuser->session_keystr) {
+ DEBUG(0, ("session_claim: strdup() failed for session_keystr\n"));
+ return False;
+ }
+ return True;
+}
+
+/* called when a session is destroyed */
+void session_yield(user_struct *vuser)
+{
+ TDB_DATA dbuf;
+ struct sessionid sessionid;
+ struct in_addr *client_ip;
+ TDB_DATA key;
+
+ if (!tdb) return;
+
+ if (!vuser->session_keystr) {
+ return;
+ }
+
+ key.dptr = vuser->session_keystr;
+ key.dsize = strlen(vuser->session_keystr)+1;
+
+ dbuf = tdb_fetch(tdb, key);
+
+ if (dbuf.dsize != sizeof(sessionid))
+ return;
+
+ memcpy(&sessionid, dbuf.dptr, sizeof(sessionid));
+
+ client_ip = interpret_addr2(sessionid.ip_addr);
+
+ SAFE_FREE(dbuf.dptr);
+
+ if (lp_utmp()) {
+ sys_utmp_yield(sessionid.username, sessionid.hostname,
+ client_ip,
+ sessionid.id_str, sessionid.id_num);
+ }
+
+ smb_pam_close_session(sessionid.username, sessionid.id_str, sessionid.hostname);
+
+ tdb_delete(tdb, key);
+}
+
+static BOOL session_traverse(int (*fn)(TDB_CONTEXT *, TDB_DATA, TDB_DATA, void *), void *state)
+{
+ if (!session_init()) {
+ DEBUG(3, ("No tdb opened\n"));
+ return False;
+ }
+
+ tdb_traverse(tdb, fn, state);
+ return True;
+}
+
+struct session_list {
+ int count;
+ struct sessionid *sessions;
+};
+
+static int gather_sessioninfo(TDB_CONTEXT *stdb, TDB_DATA kbuf, TDB_DATA dbuf,
+ void *state)
+{
+ struct session_list *sesslist = (struct session_list *) state;
+ const struct sessionid *current = (const struct sessionid *) dbuf.dptr;
+
+ sesslist->count += 1;
+ sesslist->sessions = REALLOC(sesslist->sessions, sesslist->count *
+ sizeof(struct sessionid));
+
+ memcpy(&sesslist->sessions[sesslist->count - 1], current,
+ sizeof(struct sessionid));
+ DEBUG(7,("gather_sessioninfo session from %s@%s\n",
+ current->username, current->remote_machine));
+ return 0;
+}
+
+int list_sessions(struct sessionid **session_list)
+{
+ struct session_list sesslist;
+
+ sesslist.count = 0;
+ sesslist.sessions = NULL;
+
+ if (!session_traverse(gather_sessioninfo, (void *) &sesslist)) {
+ DEBUG(3, ("Session traverse failed\n"));
+ SAFE_FREE(sesslist.sessions);
+ *session_list = NULL;
+ return 0;
+ }
+
+ *session_list = sesslist.sessions;
+ return sesslist.count;
+}
diff --git a/source/smbd/sesssetup.c b/source/smbd/sesssetup.c
new file mode 100644
index 00000000000..b8777be6971
--- /dev/null
+++ b/source/smbd/sesssetup.c
@@ -0,0 +1,938 @@
+/*
+ Unix SMB/CIFS implementation.
+ handle SMBsessionsetup
+ Copyright (C) Andrew Tridgell 1998-2001
+ Copyright (C) Andrew Bartlett 2001
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
+ Copyright (C) Luke Howard 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+uint32 global_client_caps = 0;
+
+static struct auth_ntlmssp_state *global_ntlmssp_state;
+
+/*
+ on a logon error possibly map the error to success if "map to guest"
+ is set approriately
+*/
+static NTSTATUS do_map_to_guest(NTSTATUS status, auth_serversupplied_info **server_info,
+ const char *user, const char *domain)
+{
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
+ if ((lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_USER) ||
+ (lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_PASSWORD)) {
+ DEBUG(3,("No such user %s [%s] - using guest account\n",
+ user, domain));
+ status = make_server_info_guest(server_info);
+ }
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ if (lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_PASSWORD) {
+ DEBUG(3,("Registered username %s for guest access\n",user));
+ status = make_server_info_guest(server_info);
+ }
+ }
+
+ return status;
+}
+
+/****************************************************************************
+ Add the standard 'Samba' signature to the end of the session setup.
+****************************************************************************/
+
+static int add_signature(char *outbuf, char *p)
+{
+ char *start = p;
+ fstring lanman;
+
+ fstr_sprintf( lanman, "Samba %s", SAMBA_VERSION_STRING);
+
+ p += srvstr_push(outbuf, p, "Unix", -1, STR_TERMINATE);
+ p += srvstr_push(outbuf, p, lanman, -1, STR_TERMINATE);
+ p += srvstr_push(outbuf, p, lp_workgroup(), -1, STR_TERMINATE);
+
+ return PTR_DIFF(p, start);
+}
+
+/****************************************************************************
+ Send a security blob via a session setup reply.
+****************************************************************************/
+
+static BOOL reply_sesssetup_blob(connection_struct *conn, char *outbuf,
+ DATA_BLOB blob, NTSTATUS nt_status)
+{
+ char *p;
+
+ set_message(outbuf,4,0,True);
+
+ nt_status = nt_status_squash(nt_status);
+ SIVAL(outbuf, smb_rcls, NT_STATUS_V(nt_status));
+ SSVAL(outbuf, smb_vwv0, 0xFF); /* no chaining possible */
+ SSVAL(outbuf, smb_vwv3, blob.length);
+ p = smb_buf(outbuf);
+
+ /* should we cap this? */
+ memcpy(p, blob.data, blob.length);
+ p += blob.length;
+
+ p += add_signature( outbuf, p );
+
+ set_message_end(outbuf,p);
+
+ return send_smb(smbd_server_fd(),outbuf);
+}
+
+/****************************************************************************
+ Do a 'guest' logon, getting back the
+****************************************************************************/
+
+static NTSTATUS check_guest_password(auth_serversupplied_info **server_info)
+{
+ struct auth_context *auth_context;
+ auth_usersupplied_info *user_info = NULL;
+
+ NTSTATUS nt_status;
+ unsigned char chal[8];
+
+ ZERO_STRUCT(chal);
+
+ DEBUG(3,("Got anonymous request\n"));
+
+ if (!NT_STATUS_IS_OK(nt_status = make_auth_context_fixed(&auth_context, chal))) {
+ return nt_status;
+ }
+
+ if (!make_user_info_guest(&user_info)) {
+ (auth_context->free)(&auth_context);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nt_status = auth_context->check_ntlm_password(auth_context, user_info, server_info);
+ (auth_context->free)(&auth_context);
+ free_user_info(&user_info);
+ return nt_status;
+}
+
+
+#ifdef HAVE_KRB5
+/****************************************************************************
+reply to a session setup spnego negotiate packet for kerberos
+****************************************************************************/
+static int reply_spnego_kerberos(connection_struct *conn,
+ char *inbuf, char *outbuf,
+ int length, int bufsize,
+ DATA_BLOB *secblob)
+{
+ DATA_BLOB ticket;
+ char *client, *p, *domain;
+ fstring netbios_domain_name;
+ struct passwd *pw;
+ char *user;
+ int sess_vuid;
+ NTSTATUS ret;
+ DATA_BLOB auth_data;
+ DATA_BLOB ap_rep, ap_rep_wrapped, response;
+ auth_serversupplied_info *server_info = NULL;
+ DATA_BLOB session_key;
+ uint8 tok_id[2];
+ BOOL foreign = False;
+ DATA_BLOB nullblob = data_blob(NULL, 0);
+ fstring real_username;
+
+ ZERO_STRUCT(ticket);
+ ZERO_STRUCT(auth_data);
+ ZERO_STRUCT(ap_rep);
+ ZERO_STRUCT(ap_rep_wrapped);
+ ZERO_STRUCT(response);
+
+ if (!spnego_parse_krb5_wrap(*secblob, &ticket, tok_id)) {
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ ret = ads_verify_ticket(lp_realm(), &ticket, &client, &auth_data, &ap_rep, &session_key);
+
+ data_blob_free(&ticket);
+
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(1,("Failed to verify incoming ticket!\n"));
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ data_blob_free(&auth_data);
+
+ DEBUG(3,("Ticket name is [%s]\n", client));
+
+ p = strchr_m(client, '@');
+ if (!p) {
+ DEBUG(3,("Doesn't look like a valid principal\n"));
+ data_blob_free(&ap_rep);
+ SAFE_FREE(client);
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ *p = 0;
+ if (!strequal(p+1, lp_realm())) {
+ DEBUG(3,("Ticket for foreign realm %s@%s\n", client, p+1));
+ if (!lp_allow_trusted_domains()) {
+ data_blob_free(&ap_rep);
+ SAFE_FREE(client);
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+ foreign = True;
+ }
+
+ /* this gives a fully qualified user name (ie. with full realm).
+ that leads to very long usernames, but what else can we do? */
+
+ domain = p+1;
+
+ {
+ /* If we have winbind running, we can (and must) shorten the
+ username by using the short netbios name. Otherwise we will
+ have inconsistent user names. With Kerberos, we get the
+ fully qualified realm, with ntlmssp we get the short
+ name. And even w2k3 does use ntlmssp if you for example
+ connect to an ip address. */
+
+ struct winbindd_request wb_request;
+ struct winbindd_response wb_response;
+ NSS_STATUS wb_result;
+
+ ZERO_STRUCT(wb_request);
+ ZERO_STRUCT(wb_response);
+
+ DEBUG(10, ("Mapping [%s] to short name\n", domain));
+
+ fstrcpy(wb_request.domain_name, domain);
+
+ wb_result = winbindd_request(WINBINDD_DOMAIN_INFO,
+ &wb_request, &wb_response);
+
+ if (wb_result == NSS_STATUS_SUCCESS) {
+
+ fstrcpy(netbios_domain_name,
+ wb_response.data.domain_info.name);
+ domain = netbios_domain_name;
+
+ DEBUG(10, ("Mapped to [%s]\n", domain));
+ } else {
+ DEBUG(3, ("Could not find short name -- winbind "
+ "not running?\n"));
+ }
+ }
+
+ asprintf(&user, "%s%c%s", domain, *lp_winbind_separator(), client);
+
+ /* lookup the passwd struct, create a new user if necessary */
+
+ pw = smb_getpwnam( user, real_username, True );
+
+ if (!pw) {
+ DEBUG(1,("Username %s is invalid on this system\n",user));
+ SAFE_FREE(user);
+ SAFE_FREE(client);
+ data_blob_free(&ap_rep);
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ /* setup the string used by %U */
+
+ sub_set_smb_name( real_username );
+ reload_services(True);
+
+ if (!NT_STATUS_IS_OK(ret = make_server_info_pw(&server_info, real_username, pw)))
+ {
+ DEBUG(1,("make_server_info_from_pw failed!\n"));
+ SAFE_FREE(user);
+ SAFE_FREE(client);
+ data_blob_free(&ap_rep);
+ return ERROR_NT(ret);
+ }
+
+ /* make_server_info_pw does not set the domain. Without this we end up
+ * with the local netbios name in substitutions for %D. */
+
+ if (server_info->sam_account != NULL) {
+ pdb_set_domain(server_info->sam_account, domain, PDB_SET);
+ }
+
+ /* register_vuid keeps the server info */
+ sess_vuid = register_vuid(server_info, session_key, nullblob, client);
+
+ SAFE_FREE(user);
+ SAFE_FREE(client);
+
+ if (sess_vuid == -1) {
+ ret = NT_STATUS_LOGON_FAILURE;
+ } else {
+ /* current_user_info is changed on new vuid */
+ reload_services( True );
+
+ set_message(outbuf,4,0,True);
+ SSVAL(outbuf, smb_vwv3, 0);
+
+ if (server_info->guest) {
+ SSVAL(outbuf,smb_vwv2,1);
+ }
+
+ SSVAL(outbuf, smb_uid, sess_vuid);
+
+ if (!server_info->guest && !srv_signing_started()) {
+ /* We need to start the signing engine
+ * here but a W2K client sends the old
+ * "BSRSPYL " signature instead of the
+ * correct one. Subsequent packets will
+ * be correct.
+ */
+ srv_check_sign_mac(inbuf, False);
+ }
+ }
+
+ /* wrap that up in a nice GSS-API wrapping */
+ if (NT_STATUS_IS_OK(ret)) {
+ ap_rep_wrapped = spnego_gen_krb5_wrap(ap_rep, TOK_ID_KRB_AP_REP);
+ } else {
+ ap_rep_wrapped = data_blob(NULL, 0);
+ }
+ response = spnego_gen_auth_response(&ap_rep_wrapped, ret, OID_KERBEROS5_OLD);
+ reply_sesssetup_blob(conn, outbuf, response, ret);
+
+ data_blob_free(&ap_rep);
+ data_blob_free(&ap_rep_wrapped);
+ data_blob_free(&response);
+
+ return -1; /* already replied */
+}
+#endif
+
+/****************************************************************************
+ Send a session setup reply, wrapped in SPNEGO.
+ Get vuid and check first.
+ End the NTLMSSP exchange context if we are OK/complete fail
+***************************************************************************/
+
+static BOOL reply_spnego_ntlmssp(connection_struct *conn, char *inbuf, char *outbuf,
+ AUTH_NTLMSSP_STATE **auth_ntlmssp_state,
+ DATA_BLOB *ntlmssp_blob, NTSTATUS nt_status)
+{
+ BOOL ret;
+ DATA_BLOB response;
+ struct auth_serversupplied_info *server_info = NULL;
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ server_info = (*auth_ntlmssp_state)->server_info;
+ } else {
+ nt_status = do_map_to_guest(nt_status,
+ &server_info,
+ (*auth_ntlmssp_state)->ntlmssp_state->user,
+ (*auth_ntlmssp_state)->ntlmssp_state->domain);
+ }
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ int sess_vuid;
+ DATA_BLOB nullblob = data_blob(NULL, 0);
+ DATA_BLOB session_key = data_blob((*auth_ntlmssp_state)->ntlmssp_state->session_key.data, (*auth_ntlmssp_state)->ntlmssp_state->session_key.length);
+
+ /* register_vuid keeps the server info */
+ sess_vuid = register_vuid(server_info, session_key, nullblob, (*auth_ntlmssp_state)->ntlmssp_state->user);
+ (*auth_ntlmssp_state)->server_info = NULL;
+
+ if (sess_vuid == -1) {
+ nt_status = NT_STATUS_LOGON_FAILURE;
+ } else {
+
+ /* current_user_info is changed on new vuid */
+ reload_services( True );
+
+ set_message(outbuf,4,0,True);
+ SSVAL(outbuf, smb_vwv3, 0);
+
+ if (server_info->guest) {
+ SSVAL(outbuf,smb_vwv2,1);
+ }
+
+ SSVAL(outbuf,smb_uid,sess_vuid);
+
+ if (!server_info->guest && !srv_signing_started()) {
+ /* We need to start the signing engine
+ * here but a W2K client sends the old
+ * "BSRSPYL " signature instead of the
+ * correct one. Subsequent packets will
+ * be correct.
+ */
+
+ srv_check_sign_mac(inbuf, False);
+ }
+ }
+ }
+
+ response = spnego_gen_auth_response(ntlmssp_blob, nt_status, OID_NTLMSSP);
+ ret = reply_sesssetup_blob(conn, outbuf, response, nt_status);
+ data_blob_free(&response);
+
+ /* NT_STATUS_MORE_PROCESSING_REQUIRED from our NTLMSSP code tells us,
+ and the other end, that we are not finished yet. */
+
+ if (!ret || !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ auth_ntlmssp_end(auth_ntlmssp_state);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ Reply to a session setup spnego negotiate packet.
+****************************************************************************/
+
+static int reply_spnego_negotiate(connection_struct *conn,
+ char *inbuf,
+ char *outbuf,
+ int length, int bufsize,
+ DATA_BLOB blob1)
+{
+ char *OIDs[ASN1_MAX_OIDS];
+ DATA_BLOB secblob;
+ int i;
+ DATA_BLOB chal;
+ BOOL got_kerberos = False;
+ NTSTATUS nt_status;
+
+ /* parse out the OIDs and the first sec blob */
+ if (!parse_negTokenTarg(blob1, OIDs, &secblob)) {
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ /* only look at the first OID for determining the mechToken --
+ accoirding to RFC2478, we should choose the one we want
+ and renegotiate, but i smell a client bug here..
+
+ Problem observed when connecting to a member (samba box)
+ of an AD domain as a user in a Samba domain. Samba member
+ server sent back krb5/mskrb5/ntlmssp as mechtypes, but the
+ client (2ksp3) replied with ntlmssp/mskrb5/krb5 and an
+ NTLMSSP mechtoken. --jerry */
+
+ if (strcmp(OID_KERBEROS5, OIDs[0]) == 0 ||
+ strcmp(OID_KERBEROS5_OLD, OIDs[0]) == 0) {
+ got_kerberos = True;
+ }
+
+ for (i=0;OIDs[i];i++) {
+ DEBUG(3,("Got OID %s\n", OIDs[i]));
+ free(OIDs[i]);
+ }
+ DEBUG(3,("Got secblob of size %lu\n", (unsigned long)secblob.length));
+
+#ifdef HAVE_KRB5
+ if (got_kerberos && (SEC_ADS == lp_security())) {
+ int ret = reply_spnego_kerberos(conn, inbuf, outbuf,
+ length, bufsize, &secblob);
+ data_blob_free(&secblob);
+ return ret;
+ }
+#endif
+
+ if (global_ntlmssp_state) {
+ auth_ntlmssp_end(&global_ntlmssp_state);
+ }
+
+ nt_status = auth_ntlmssp_start(&global_ntlmssp_state);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return ERROR_NT(nt_status);
+ }
+
+ nt_status = auth_ntlmssp_update(global_ntlmssp_state,
+ secblob, &chal);
+
+ data_blob_free(&secblob);
+
+ reply_spnego_ntlmssp(conn, inbuf, outbuf, &global_ntlmssp_state,
+ &chal, nt_status);
+
+ data_blob_free(&chal);
+
+ /* already replied */
+ return -1;
+}
+
+/****************************************************************************
+ Reply to a session setup spnego auth packet.
+****************************************************************************/
+
+static int reply_spnego_auth(connection_struct *conn, char *inbuf, char *outbuf,
+ int length, int bufsize,
+ DATA_BLOB blob1)
+{
+ DATA_BLOB auth, auth_reply;
+ NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
+
+ if (!spnego_parse_auth(blob1, &auth)) {
+#if 0
+ file_save("auth.dat", blob1.data, blob1.length);
+#endif
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ }
+
+ if (!global_ntlmssp_state) {
+ /* auth before negotiatiate? */
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ }
+
+ nt_status = auth_ntlmssp_update(global_ntlmssp_state,
+ auth, &auth_reply);
+
+ data_blob_free(&auth);
+
+ reply_spnego_ntlmssp(conn, inbuf, outbuf, &global_ntlmssp_state,
+ &auth_reply, nt_status);
+
+ data_blob_free(&auth_reply);
+
+ /* and tell smbd that we have already replied to this packet */
+ return -1;
+}
+
+/****************************************************************************
+ Reply to a session setup command.
+****************************************************************************/
+
+static int reply_sesssetup_and_X_spnego(connection_struct *conn, char *inbuf,
+ char *outbuf,
+ int length,int bufsize)
+{
+ uint8 *p;
+ DATA_BLOB blob1;
+ int ret;
+ size_t bufrem;
+ fstring native_os, native_lanman, primary_domain;
+ char *p2;
+ uint16 data_blob_len = SVAL(inbuf, smb_vwv7);
+ enum remote_arch_types ra_type = get_remote_arch();
+
+ DEBUG(3,("Doing spnego session setup\n"));
+
+ if (global_client_caps == 0) {
+ global_client_caps = IVAL(inbuf,smb_vwv10);
+
+ if (!(global_client_caps & CAP_STATUS32)) {
+ remove_from_common_flags2(FLAGS2_32_BIT_ERROR_CODES);
+ }
+
+ }
+
+ p = (uint8 *)smb_buf(inbuf);
+
+ if (data_blob_len == 0) {
+ /* an invalid request */
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ bufrem = smb_bufrem(inbuf, p);
+ /* pull the spnego blob */
+ blob1 = data_blob(p, MIN(bufrem, data_blob_len));
+
+#if 0
+ file_save("negotiate.dat", blob1.data, blob1.length);
+#endif
+
+ p2 = inbuf + smb_vwv13 + data_blob_len;
+ p2 += srvstr_pull_buf(inbuf, native_os, p2, sizeof(native_os), STR_TERMINATE);
+ p2 += srvstr_pull_buf(inbuf, native_lanman, p2, sizeof(native_lanman), STR_TERMINATE);
+ p2 += srvstr_pull_buf(inbuf, primary_domain, p2, sizeof(primary_domain), STR_TERMINATE);
+ DEBUG(3,("NativeOS=[%s] NativeLanMan=[%s] PrimaryDomain=[%s]\n",
+ native_os, native_lanman, primary_domain));
+
+ if ( ra_type == RA_WIN2K ) {
+ /* Windows 2003 doesn't set the native lanman string,
+ but does set primary domain which is a bug I think */
+
+ if ( !strlen(native_lanman) )
+ ra_lanman_string( primary_domain );
+ else
+ ra_lanman_string( native_lanman );
+ }
+
+ if (blob1.data[0] == ASN1_APPLICATION(0)) {
+ /* its a negTokenTarg packet */
+ ret = reply_spnego_negotiate(conn, inbuf, outbuf, length, bufsize, blob1);
+ data_blob_free(&blob1);
+ return ret;
+ }
+
+ if (blob1.data[0] == ASN1_CONTEXT(1)) {
+ /* its a auth packet */
+ ret = reply_spnego_auth(conn, inbuf, outbuf, length, bufsize, blob1);
+ data_blob_free(&blob1);
+ return ret;
+ }
+
+ /* what sort of packet is this? */
+ DEBUG(1,("Unknown packet in reply_sesssetup_and_X_spnego\n"));
+
+ data_blob_free(&blob1);
+
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+}
+
+/****************************************************************************
+ On new VC == 0, shutdown *all* old connections and users.
+ It seems that only NT4.x does this. At W2K and above (XP etc.).
+ a new session setup with VC==0 is ignored.
+****************************************************************************/
+
+static void setup_new_vc_session(void)
+{
+ DEBUG(2,("setup_new_vc_session: New VC == 0, if NT4.x compatible we would close all old resources.\n"));
+#if 0
+ conn_close_all();
+ invalidate_all_vuids();
+#endif
+}
+
+/****************************************************************************
+ Reply to a session setup command.
+****************************************************************************/
+
+int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf,
+ int length,int bufsize)
+{
+ int sess_vuid;
+ int smb_bufsize;
+ DATA_BLOB lm_resp;
+ DATA_BLOB nt_resp;
+ DATA_BLOB plaintext_password;
+ fstring user;
+ fstring sub_user; /* Sainitised username for substituion */
+ fstring domain;
+ fstring native_os;
+ fstring native_lanman;
+ fstring primary_domain;
+ static BOOL done_sesssetup = False;
+ extern BOOL global_encrypted_passwords_negotiated;
+ extern BOOL global_spnego_negotiated;
+ extern int Protocol;
+ extern int max_send;
+
+ auth_usersupplied_info *user_info = NULL;
+ extern struct auth_context *negprot_global_auth_context;
+ auth_serversupplied_info *server_info = NULL;
+
+ NTSTATUS nt_status;
+
+ BOOL doencrypt = global_encrypted_passwords_negotiated;
+
+ DATA_BLOB session_key;
+
+ START_PROFILE(SMBsesssetupX);
+
+ ZERO_STRUCT(lm_resp);
+ ZERO_STRUCT(nt_resp);
+ ZERO_STRUCT(plaintext_password);
+
+ DEBUG(3,("wct=%d flg2=0x%x\n", CVAL(inbuf, smb_wct), SVAL(inbuf, smb_flg2)));
+
+ /* a SPNEGO session setup has 12 command words, whereas a normal
+ NT1 session setup has 13. See the cifs spec. */
+ if (CVAL(inbuf, smb_wct) == 12 &&
+ (SVAL(inbuf, smb_flg2) & FLAGS2_EXTENDED_SECURITY)) {
+ if (!global_spnego_negotiated) {
+ DEBUG(0,("reply_sesssetup_and_X: Rejecting attempt at SPNEGO session setup when it was not negoitiated.\n"));
+ return ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+ }
+
+ if (SVAL(inbuf,smb_vwv4) == 0) {
+ setup_new_vc_session();
+ }
+ return reply_sesssetup_and_X_spnego(conn, inbuf, outbuf, length, bufsize);
+ }
+
+ smb_bufsize = SVAL(inbuf,smb_vwv2);
+
+ if (Protocol < PROTOCOL_NT1) {
+ uint16 passlen1 = SVAL(inbuf,smb_vwv7);
+ if ((passlen1 > MAX_PASS_LEN) || (passlen1 > smb_bufrem(inbuf, smb_buf(inbuf)))) {
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ }
+
+ if (doencrypt) {
+ lm_resp = data_blob(smb_buf(inbuf), passlen1);
+ } else {
+ plaintext_password = data_blob(smb_buf(inbuf), passlen1+1);
+ /* Ensure null termination */
+ plaintext_password.data[passlen1] = 0;
+ }
+
+ srvstr_pull_buf(inbuf, user, smb_buf(inbuf)+passlen1, sizeof(user), STR_TERMINATE);
+ *domain = 0;
+
+ } else {
+ uint16 passlen1 = SVAL(inbuf,smb_vwv7);
+ uint16 passlen2 = SVAL(inbuf,smb_vwv8);
+ enum remote_arch_types ra_type = get_remote_arch();
+ char *p = smb_buf(inbuf);
+ char *save_p = smb_buf(inbuf);
+ uint16 byte_count;
+
+
+ if(global_client_caps == 0) {
+ global_client_caps = IVAL(inbuf,smb_vwv11);
+
+ if (!(global_client_caps & CAP_STATUS32)) {
+ remove_from_common_flags2(FLAGS2_32_BIT_ERROR_CODES);
+ }
+
+ /* client_caps is used as final determination if client is NT or Win95.
+ This is needed to return the correct error codes in some
+ circumstances.
+ */
+
+ if(ra_type == RA_WINNT || ra_type == RA_WIN2K || ra_type == RA_WIN95) {
+ if(!(global_client_caps & (CAP_NT_SMBS | CAP_STATUS32))) {
+ set_remote_arch( RA_WIN95);
+ }
+ }
+ }
+
+ if (!doencrypt) {
+ /* both Win95 and WinNT stuff up the password lengths for
+ non-encrypting systems. Uggh.
+
+ if passlen1==24 its a win95 system, and its setting the
+ password length incorrectly. Luckily it still works with the
+ default code because Win95 will null terminate the password
+ anyway
+
+ if passlen1>0 and passlen2>0 then maybe its a NT box and its
+ setting passlen2 to some random value which really stuffs
+ things up. we need to fix that one. */
+
+ if (passlen1 > 0 && passlen2 > 0 && passlen2 != 24 && passlen2 != 1)
+ passlen2 = 0;
+ }
+
+ /* check for nasty tricks */
+ if (passlen1 > MAX_PASS_LEN || passlen1 > smb_bufrem(inbuf, p)) {
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ }
+
+ if (passlen2 > MAX_PASS_LEN || passlen2 > smb_bufrem(inbuf, p+passlen1)) {
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ }
+
+ /* Save the lanman2 password and the NT md4 password. */
+
+ if ((doencrypt) && (passlen1 != 0) && (passlen1 != 24)) {
+ doencrypt = False;
+ }
+
+ if (doencrypt) {
+ lm_resp = data_blob(p, passlen1);
+ nt_resp = data_blob(p+passlen1, passlen2);
+ } else {
+ pstring pass;
+ BOOL unic=SVAL(inbuf, smb_flg2) & FLAGS2_UNICODE_STRINGS;
+
+ if ((ra_type == RA_WINNT) && (passlen2 == 0) && unic && passlen1) {
+ /* NT4.0 stuffs up plaintext unicode password lengths... */
+ srvstr_pull(inbuf, pass, smb_buf(inbuf) + 1,
+ sizeof(pass), passlen1, STR_TERMINATE);
+ } else {
+ srvstr_pull(inbuf, pass, smb_buf(inbuf),
+ sizeof(pass), unic ? passlen2 : passlen1,
+ STR_TERMINATE);
+ }
+ plaintext_password = data_blob(pass, strlen(pass)+1);
+ }
+
+ p += passlen1 + passlen2;
+ p += srvstr_pull_buf(inbuf, user, p, sizeof(user), STR_TERMINATE);
+ p += srvstr_pull_buf(inbuf, domain, p, sizeof(domain), STR_TERMINATE);
+ p += srvstr_pull_buf(inbuf, native_os, p, sizeof(native_os), STR_TERMINATE);
+ p += srvstr_pull_buf(inbuf, native_lanman, p, sizeof(native_lanman), STR_TERMINATE);
+
+ /* not documented or decoded by Ethereal but there is one more string
+ in the extra bytes which is the same as the PrimaryDomain when using
+ extended security. Windows NT 4 and 2003 use this string to store
+ the native lanman string. Windows 9x does not include a string here
+ at all so we have to check if we have any extra bytes left */
+
+ byte_count = SVAL(inbuf, smb_vwv13);
+ if ( PTR_DIFF(p, save_p) < byte_count)
+ p += srvstr_pull_buf(inbuf, primary_domain, p, sizeof(primary_domain), STR_TERMINATE);
+ else
+ fstrcpy( primary_domain, "null" );
+
+ DEBUG(3,("Domain=[%s] NativeOS=[%s] NativeLanMan=[%s] PrimaryDomain=[%s]\n",
+ domain, native_os, native_lanman, primary_domain));
+
+ if ( ra_type == RA_WIN2K ) {
+ if ( strlen(native_lanman) == 0 )
+ ra_lanman_string( primary_domain );
+ else
+ ra_lanman_string( native_lanman );
+ }
+
+ }
+
+ if (SVAL(inbuf,smb_vwv4) == 0) {
+ setup_new_vc_session();
+ }
+
+ DEBUG(3,("sesssetupX:name=[%s]\\[%s]@[%s]\n", domain, user, get_remote_machine_name()));
+
+ if (*user) {
+ if (global_spnego_negotiated) {
+
+ /* This has to be here, because this is a perfectly valid behaviour for guest logons :-( */
+
+ DEBUG(0,("reply_sesssetup_and_X: Rejecting attempt at 'normal' session setup after negotiating spnego.\n"));
+ return ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+ }
+ fstrcpy(sub_user, user);
+
+ /* setup the string used by %U */
+ sub_set_smb_name(user);
+ } else {
+ fstrcpy(sub_user, lp_guestaccount());
+ }
+
+ sub_set_smb_name(sub_user);
+
+ reload_services(True);
+
+ if (lp_security() == SEC_SHARE) {
+ /* in share level we should ignore any passwords */
+
+ data_blob_free(&lm_resp);
+ data_blob_free(&nt_resp);
+ data_blob_clear_free(&plaintext_password);
+
+ map_username(sub_user);
+ add_session_user(sub_user);
+ /* Then force it to null for the benfit of the code below */
+ *user = 0;
+ }
+
+ if (!*user) {
+
+ nt_status = check_guest_password(&server_info);
+
+ } else if (doencrypt) {
+ if (!negprot_global_auth_context) {
+ DEBUG(0, ("reply_sesssetup_and_X: Attempted encrypted session setup without negprot denied!\n"));
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+ nt_status = make_user_info_for_reply_enc(&user_info, user, domain,
+ lm_resp, nt_resp);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ nt_status = negprot_global_auth_context->check_ntlm_password(negprot_global_auth_context,
+ user_info,
+ &server_info);
+ }
+ } else {
+ struct auth_context *plaintext_auth_context = NULL;
+ const uint8 *chal;
+ if (NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(&plaintext_auth_context))) {
+ chal = plaintext_auth_context->get_ntlm_challenge(plaintext_auth_context);
+
+ if (!make_user_info_for_reply(&user_info,
+ user, domain, chal,
+ plaintext_password)) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ }
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ nt_status = plaintext_auth_context->check_ntlm_password(plaintext_auth_context,
+ user_info,
+ &server_info);
+
+ (plaintext_auth_context->free)(&plaintext_auth_context);
+ }
+ }
+ }
+
+ free_user_info(&user_info);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ nt_status = do_map_to_guest(nt_status, &server_info, user, domain);
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ data_blob_free(&nt_resp);
+ data_blob_free(&lm_resp);
+ data_blob_clear_free(&plaintext_password);
+ return ERROR_NT(nt_status_squash(nt_status));
+ }
+
+ if (server_info->nt_session_key.data) {
+ session_key = data_blob(server_info->nt_session_key.data, server_info->nt_session_key.length);
+ } else if (server_info->lm_session_key.length >= 8 && lm_resp.length == 24) {
+ session_key = data_blob(NULL, 16);
+ SMBsesskeygen_lmv1(server_info->lm_session_key.data, lm_resp.data,
+ session_key.data);
+ } else {
+ session_key = data_blob(NULL, 0);
+ }
+
+ data_blob_free(&lm_resp);
+ data_blob_clear_free(&plaintext_password);
+
+ /* it's ok - setup a reply */
+ set_message(outbuf,3,0,True);
+ if (Protocol >= PROTOCOL_NT1) {
+ char *p = smb_buf( outbuf );
+ p += add_signature( outbuf, p );
+ set_message_end( outbuf, p );
+ /* perhaps grab OS version here?? */
+ }
+
+ if (server_info->guest) {
+ SSVAL(outbuf,smb_vwv2,1);
+ }
+
+ /* register the name and uid as being validated, so further connections
+ to a uid can get through without a password, on the same VC */
+
+ /* register_vuid keeps the server info */
+ sess_vuid = register_vuid(server_info, session_key, nt_resp, sub_user);
+ data_blob_free(&nt_resp);
+
+ if (sess_vuid == -1) {
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ /* current_user_info is changed on new vuid */
+ reload_services( True );
+
+ if (!server_info->guest && !srv_signing_started() && !srv_check_sign_mac(inbuf, True)) {
+ exit_server("reply_sesssetup_and_X: bad smb signature");
+ }
+
+ SSVAL(outbuf,smb_uid,sess_vuid);
+ SSVAL(inbuf,smb_uid,sess_vuid);
+
+ if (!done_sesssetup)
+ max_send = MIN(max_send,smb_bufsize);
+
+ done_sesssetup = True;
+
+ END_PROFILE(SMBsesssetupX);
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
diff --git a/source/smbd/srvstr.c b/source/smbd/srvstr.c
new file mode 100644
index 00000000000..409fd30a679
--- /dev/null
+++ b/source/smbd/srvstr.c
@@ -0,0 +1,44 @@
+/*
+ Unix SMB/CIFS implementation.
+ server specific string routines
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+extern int max_send;
+
+/* Make sure we can't write a string past the end of the buffer */
+
+size_t srvstr_push_fn(const char *function, unsigned int line,
+ const char *base_ptr, void *dest,
+ const char *src, int dest_len, int flags)
+{
+ size_t buf_used = PTR_DIFF(dest, base_ptr);
+ if (dest_len == -1) {
+ if (((ptrdiff_t)dest < (ptrdiff_t)base_ptr) || (buf_used > (size_t)max_send)) {
+#if 0
+ DEBUG(0, ("Pushing string of 'unlimited' length into non-SMB buffer!\n"));
+#endif
+ return push_string_fn(function, line, base_ptr, dest, src, -1, flags);
+ }
+ return push_string_fn(function, line, base_ptr, dest, src, max_send - buf_used, flags);
+ }
+
+ /* 'normal' push into size-specified buffer */
+ return push_string_fn(function, line, base_ptr, dest, src, dest_len, flags);
+}
diff --git a/source/smbd/statcache.c b/source/smbd/statcache.c
new file mode 100644
index 00000000000..d996f5e4938
--- /dev/null
+++ b/source/smbd/statcache.c
@@ -0,0 +1,339 @@
+/*
+ Unix SMB/CIFS implementation.
+ stat cache code
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Jeremy Allison 1999-2000
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+extern BOOL case_sensitive;
+
+/****************************************************************************
+ Stat cache code used in unix_convert.
+*****************************************************************************/
+
+typedef struct {
+ char *original_path;
+ char *translated_path;
+ size_t translated_path_length;
+ char names[2]; /* This is extended via malloc... */
+} stat_cache_entry;
+
+#define INIT_STAT_CACHE_SIZE 512
+static hash_table stat_cache;
+
+/**
+ * Add an entry into the stat cache.
+ *
+ * @param full_orig_name The original name as specified by the client
+ * @param orig_translated_path The name on our filesystem.
+ *
+ * @note Only the first strlen(orig_translated_path) characters are stored
+ * into the cache. This means that full_orig_name will be internally
+ * truncated.
+ *
+ */
+
+void stat_cache_add( const char *full_orig_name, const char *orig_translated_path)
+{
+ stat_cache_entry *scp;
+ stat_cache_entry *found_scp;
+ char *translated_path;
+ size_t translated_path_length;
+
+ char *original_path;
+ size_t original_path_length;
+
+ hash_element *hash_elem;
+
+ if (!lp_stat_cache())
+ return;
+
+ /*
+ * Don't cache trivial valid directory entries such as . and ..
+ */
+
+ if((*full_orig_name == '\0') || (full_orig_name[0] == '.' &&
+ ((full_orig_name[1] == '\0') ||
+ (full_orig_name[1] == '.' && full_orig_name[1] == '\0'))))
+ return;
+
+ /*
+ * If we are in case insentive mode, we don't need to
+ * store names that need no translation - else, it
+ * would be a waste.
+ */
+
+ if(case_sensitive && (strcmp(full_orig_name, orig_translated_path) == 0))
+ return;
+
+ /*
+ * Remove any trailing '/' characters from the
+ * translated path.
+ */
+
+ translated_path = strdup(orig_translated_path);
+ if (!translated_path)
+ return;
+
+ translated_path_length = strlen(translated_path);
+
+ if(translated_path[translated_path_length-1] == '/') {
+ translated_path[translated_path_length-1] = '\0';
+ translated_path_length--;
+ }
+
+ if(case_sensitive) {
+ original_path = strdup(full_orig_name);
+ } else {
+ original_path = strdup_upper(full_orig_name);
+ }
+
+ if (!original_path) {
+ SAFE_FREE(translated_path);
+ return;
+ }
+
+ original_path_length = strlen(original_path);
+
+ if(original_path[original_path_length-1] == '/') {
+ original_path[original_path_length-1] = '\0';
+ original_path_length--;
+ }
+
+ if (original_path_length != translated_path_length) {
+ if (original_path_length < translated_path_length) {
+ DEBUG(0, ("OOPS - tried to store stat cache entry for weird length paths [%s] %lu and [%s] %lu)!\n",
+ original_path, (unsigned long)original_path_length, translated_path, (unsigned long)translated_path_length));
+ SAFE_FREE(original_path);
+ SAFE_FREE(translated_path);
+ return;
+ }
+
+ /* we only want to store the first part of original_path,
+ up to the length of translated_path */
+
+ original_path[translated_path_length] = '\0';
+ original_path_length = translated_path_length;
+ }
+
+ /*
+ * Check this name doesn't exist in the cache before we
+ * add it.
+ */
+
+ if ((hash_elem = hash_lookup(&stat_cache, original_path))) {
+ found_scp = (stat_cache_entry *)(hash_elem->value);
+ if (strcmp((found_scp->translated_path), orig_translated_path) == 0) {
+ /* already in hash table */
+ SAFE_FREE(original_path);
+ SAFE_FREE(translated_path);
+ return;
+ }
+ /* hash collision - remove before we re-add */
+ hash_remove(&stat_cache, hash_elem);
+ }
+
+ /*
+ * New entry.
+ */
+
+ if((scp = (stat_cache_entry *)malloc(sizeof(stat_cache_entry)
+ +original_path_length
+ +translated_path_length)) == NULL) {
+ DEBUG(0,("stat_cache_add: Out of memory !\n"));
+ SAFE_FREE(original_path);
+ SAFE_FREE(translated_path);
+ return;
+ }
+
+ scp->original_path = scp->names;
+ /* pointer into the structure... */
+ scp->translated_path = scp->names + original_path_length + 1;
+ safe_strcpy(scp->original_path, original_path, original_path_length);
+ safe_strcpy(scp->translated_path, translated_path, translated_path_length);
+ scp->translated_path_length = translated_path_length;
+
+ hash_insert(&stat_cache, (char *)scp, original_path);
+
+ SAFE_FREE(original_path);
+ SAFE_FREE(translated_path);
+
+ DEBUG(5,("stat_cache_add: Added entry %s -> %s\n", scp->original_path, scp->translated_path));
+}
+
+/**
+ * Look through the stat cache for an entry
+ *
+ * The hash-table's internals will promote it to the top if found.
+ *
+ * @param conn A connection struct to do the stat() with.
+ * @param name The path we are attempting to cache, modified by this routine
+ * to be correct as far as the cache can tell us
+ * @param dirpath The path as far as the stat cache told us.
+ * @param start A pointer into name, for where to 'start' in fixing the rest of the name up.
+ * @param psd A stat buffer, NOT from the cache, but just a side-effect.
+ *
+ * @return True if we translated (and did a scuccessful stat on) the entire name.
+ *
+ */
+
+BOOL stat_cache_lookup(connection_struct *conn, pstring name, pstring dirpath,
+ char **start, SMB_STRUCT_STAT *pst)
+{
+ stat_cache_entry *scp;
+ char *chk_name;
+ size_t namelen;
+ hash_element *hash_elem;
+ char *sp;
+ BOOL sizechanged = False;
+ unsigned int num_components = 0;
+
+ if (!lp_stat_cache())
+ return False;
+
+ namelen = strlen(name);
+
+ *start = name;
+
+ DO_PROFILE_INC(statcache_lookups);
+
+ /*
+ * Don't lookup trivial valid directory entries.
+ */
+ if((*name == '\0') || (name[0] == '.' &&
+ ((name[1] == '\0') ||
+ (name[1] == '.' && name[1] == '\0'))))
+ return False;
+
+ if (case_sensitive) {
+ chk_name = strdup(name);
+ if (!chk_name) {
+ DEBUG(0, ("stat_cache_lookup: strdup failed!\n"));
+ return False;
+ }
+
+ } else {
+ chk_name = strdup_upper(name);
+ if (!chk_name) {
+ DEBUG(0, ("stat_cache_lookup: strdup_upper failed!\n"));
+ return False;
+ }
+
+ /*
+ * In some language encodings the length changes
+ * if we uppercase. We need to treat this differently
+ * below.
+ */
+ if (strlen(chk_name) != namelen)
+ sizechanged = True;
+ }
+
+ while (1) {
+ hash_elem = hash_lookup(&stat_cache, chk_name);
+ if(hash_elem == NULL) {
+ DEBUG(10,("stat_cache_lookup: lookup failed for name [%s]\n", chk_name ));
+ /*
+ * Didn't find it - remove last component for next try.
+ */
+ sp = strrchr_m(chk_name, '/');
+ if (sp) {
+ *sp = '\0';
+ /*
+ * Count the number of times we have done this,
+ * we'll need it when reconstructing the string.
+ */
+ if (sizechanged)
+ num_components++;
+
+ } else {
+ /*
+ * We reached the end of the name - no match.
+ */
+ DO_PROFILE_INC(statcache_misses);
+ SAFE_FREE(chk_name);
+ return False;
+ }
+ if((*chk_name == '\0') || (strcmp(chk_name, ".") == 0)
+ || (strcmp(chk_name, "..") == 0)) {
+ DO_PROFILE_INC(statcache_misses);
+ SAFE_FREE(chk_name);
+ return False;
+ }
+ } else {
+ scp = (stat_cache_entry *)(hash_elem->value);
+ DEBUG(10,("stat_cache_lookup: lookup succeeded for name [%s] -> [%s]\n", chk_name, scp->translated_path ));
+ DO_PROFILE_INC(statcache_hits);
+ if(SMB_VFS_STAT(conn,scp->translated_path, pst) != 0) {
+ /* Discard this entry - it doesn't exist in the filesystem. */
+ hash_remove(&stat_cache, hash_elem);
+ SAFE_FREE(chk_name);
+ return False;
+ }
+
+ if (!sizechanged) {
+ memcpy(name, scp->translated_path, MIN(sizeof(pstring)-1, scp->translated_path_length));
+ } else if (num_components == 0) {
+ pstrcpy(name, scp->translated_path);
+ } else {
+ sp = strnrchr_m(name, '/', num_components);
+ if (sp) {
+ pstring last_component;
+ pstrcpy(last_component, sp);
+ pstrcpy(name, scp->translated_path);
+ pstrcat(name, last_component);
+ } else {
+ pstrcpy(name, scp->translated_path);
+ }
+ }
+
+ /* set pointer for 'where to start' on fixing the rest of the name */
+ *start = &name[scp->translated_path_length];
+ if(**start == '/')
+ ++*start;
+
+ pstrcpy(dirpath, scp->translated_path);
+ SAFE_FREE(chk_name);
+ return (namelen == scp->translated_path_length);
+ }
+ }
+}
+
+/*************************************************************************** **
+ * Initializes or clears the stat cache.
+ *
+ * Input: none.
+ * Output: none.
+ *
+ * ************************************************************************** **
+ */
+BOOL reset_stat_cache( void )
+{
+ static BOOL initialised;
+ if (!lp_stat_cache())
+ return True;
+
+ if (initialised) {
+ hash_clear(&stat_cache);
+ }
+
+ initialised = hash_table_init( &stat_cache, INIT_STAT_CACHE_SIZE,
+ (compare_function)(strcmp));
+ return initialised;
+}
diff --git a/source/smbd/tdbutil.c b/source/smbd/tdbutil.c
new file mode 100644
index 00000000000..cafcde20374
--- /dev/null
+++ b/source/smbd/tdbutil.c
@@ -0,0 +1,85 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main SMB server routines
+ Copyright (C) Jeremy Allison 2003
+ Copyright (C) Gerald (Jerry) Carter 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+
+/**********************************************************************
+ logging function used by smbd to detect and remove corrupted tdb's
+**********************************************************************/
+
+void smbd_tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...)
+{
+ va_list ap;
+ char *ptr = NULL;
+ BOOL decrement_smbd_count;
+
+ va_start(ap, format);
+ vasprintf(&ptr, format, ap);
+ va_end(ap);
+
+ if (!ptr || !*ptr)
+ return;
+
+ DEBUG(level, ("tdb(%s): %s", tdb->name ? tdb->name : "unnamed", ptr));
+
+ if (tdb->ecode == TDB_ERR_CORRUPT) {
+ int ret;
+
+ DEBUG(0,("tdb_log: TDB %s is corrupt. Removing file and stopping this process.\n",
+ tdb->name ));
+
+ become_root();
+ ret = unlink(tdb->name);
+ if ( ret ) {
+ DEBUG(0,("ERROR: %s\n", strerror(errno)));
+ }
+ unbecome_root();
+
+
+ /* if its not connections.tdb, then make sure we decrement the
+ smbd count. If connections.tdb is bad, there's nothing we
+ can do and everything will eventually shut down or clean
+ up anyways */
+
+ if ( strcmp(tdb->name, lock_path("connections.tdb")) == 0 )
+ decrement_smbd_count = False;
+ else
+ decrement_smbd_count = True;
+
+ /* now die */
+
+ smb_panic2("corrupt tdb\n", decrement_smbd_count );
+ }
+
+ if (tdb->ecode == TDB_ERR_IO)
+ {
+ if ( strcmp(tdb->name, lock_path("connections.tdb")) == 0 )
+ decrement_smbd_count = False;
+ else
+ decrement_smbd_count = True;
+
+ smb_panic2( "i/o error on tdb.\n", decrement_smbd_count );
+ }
+
+ SAFE_FREE(ptr);
+}
+
diff --git a/source/smbd/trans2.c b/source/smbd/trans2.c
new file mode 100644
index 00000000000..a88722edde5
--- /dev/null
+++ b/source/smbd/trans2.c
@@ -0,0 +1,4165 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB transaction2 handling
+ Copyright (C) Jeremy Allison 1994-2003
+ Copyright (C) Stefan (metze) Metzmacher 2003
+
+ Extensively modified by Andrew Tridgell, 1995
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+extern int Protocol;
+extern BOOL case_sensitive;
+extern int smb_read_error;
+extern fstring local_machine;
+extern int global_oplock_break;
+extern uint32 global_client_caps;
+extern struct current_user current_user;
+
+#define get_file_size(sbuf) ((sbuf).st_size)
+
+/* given a stat buffer return the allocated size on disk, taking into
+ account sparse files */
+SMB_BIG_UINT get_allocation_size(files_struct *fsp, SMB_STRUCT_STAT *sbuf)
+{
+ SMB_BIG_UINT ret;
+#if defined(HAVE_STAT_ST_BLOCKS) && defined(STAT_ST_BLOCKSIZE)
+ ret = (SMB_BIG_UINT)STAT_ST_BLOCKSIZE * (SMB_BIG_UINT)sbuf->st_blocks;
+#else
+ ret = (SMB_BIG_UINT)get_file_size(*sbuf);
+#endif
+ if (!ret && fsp && fsp->initial_allocation_size)
+ ret = fsp->initial_allocation_size;
+ ret = SMB_ROUNDUP(ret,SMB_ROUNDUP_ALLOCATION_SIZE);
+ return ret;
+}
+
+/****************************************************************************
+ Utility functions for dealing with extended attributes.
+****************************************************************************/
+
+static const char *prohibited_ea_names[] = {
+ SAMBA_POSIX_INHERITANCE_EA_NAME,
+ SAMBA_XATTR_DOS_ATTRIB,
+ NULL
+};
+
+/****************************************************************************
+ Refuse to allow clients to overwrite our private xattrs.
+****************************************************************************/
+
+static BOOL samba_private_attr_name(const char *unix_ea_name)
+{
+ int i;
+
+ for (i = 0; prohibited_ea_names[i]; i++) {
+ if (strequal( prohibited_ea_names[i], unix_ea_name))
+ return True;
+ }
+ return False;
+}
+
+struct ea_list {
+ struct ea_list *next, *prev;
+ struct ea_struct ea;
+};
+
+/****************************************************************************
+ Get one EA value. Fill in a struct ea_struct.
+****************************************************************************/
+
+static BOOL get_ea_value(TALLOC_CTX *mem_ctx, connection_struct *conn, files_struct *fsp,
+ const char *fname, char *ea_name, struct ea_struct *pea)
+{
+ /* Get the value of this xattr. Max size is 64k. */
+ size_t attr_size = 256;
+ char *val = NULL;
+ ssize_t sizeret;
+
+ again:
+
+ val = talloc_realloc(mem_ctx, val, attr_size);
+ if (!val) {
+ return False;
+ }
+
+ if (fsp && fsp->fd != -1) {
+ sizeret = SMB_VFS_FGETXATTR(fsp, fsp->fd, ea_name, val, attr_size);
+ } else {
+ sizeret = SMB_VFS_GETXATTR(conn, fname, ea_name, val, attr_size);
+ }
+
+ if (sizeret == -1 && errno == ERANGE && attr_size != 65536) {
+ attr_size = 65536;
+ goto again;
+ }
+
+ if (sizeret == -1) {
+ return False;
+ }
+
+ DEBUG(10,("get_ea_value: EA %s is of length %d: ", ea_name, sizeret));
+ dump_data(10, val, sizeret);
+
+ pea->flags = 0;
+ if (strnequal(ea_name, "user.", 5)) {
+ pea->name = &ea_name[5];
+ } else {
+ pea->name = ea_name;
+ }
+ pea->value.data = val;
+ pea->value.length = (size_t)sizeret;
+ return True;
+}
+
+/****************************************************************************
+ Return a linked list of the total EA's. Plus a guess as to the total size
+ (NB. The is not the total size on the wire - we need to convert to DOS
+ codepage for that).
+****************************************************************************/
+
+static struct ea_list *get_ea_list(TALLOC_CTX *mem_ctx, connection_struct *conn, files_struct *fsp, const char *fname, size_t *pea_total_len)
+{
+ /* Get a list of all xattrs. Max namesize is 64k. */
+ size_t ea_namelist_size = 1024;
+ char *ea_namelist;
+ char *p;
+ ssize_t sizeret;
+ int i;
+ struct ea_list *ea_list_head = NULL;
+
+ if (pea_total_len) {
+ *pea_total_len = 0;
+ }
+
+ if (!lp_ea_support(SNUM(conn))) {
+ return NULL;
+ }
+
+ for (i = 0, ea_namelist = talloc(mem_ctx, ea_namelist_size); i < 6;
+ ea_namelist = talloc_realloc(mem_ctx, ea_namelist, ea_namelist_size), i++) {
+ if (fsp && fsp->fd != -1) {
+ sizeret = SMB_VFS_FLISTXATTR(fsp, fsp->fd, ea_namelist, ea_namelist_size);
+ } else {
+ sizeret = SMB_VFS_LISTXATTR(conn, fname, ea_namelist, ea_namelist_size);
+ }
+
+ if (sizeret == -1 && errno == ERANGE) {
+ ea_namelist_size *= 2;
+ } else {
+ break;
+ }
+ }
+
+ if (sizeret == -1)
+ return NULL;
+
+ DEBUG(10,("get_ea_list: ea_namelist size = %d\n", sizeret ));
+
+ if (sizeret) {
+ for (p = ea_namelist; p - ea_namelist < sizeret; p += strlen(p) + 1) {
+ struct ea_list *listp, *tmp;
+
+ if (strnequal(p, "system.", 7) || samba_private_attr_name(p))
+ continue;
+
+ listp = talloc(mem_ctx, sizeof(struct ea_list));
+ if (!listp)
+ return NULL;
+
+ if (!get_ea_value(mem_ctx, conn, fsp, fname, p, &listp->ea)) {
+ return NULL;
+ }
+
+ if (pea_total_len) {
+ *pea_total_len += 4 + strlen(p) + 1 + listp->ea.value.length;
+ }
+ DLIST_ADD_END(ea_list_head, listp, tmp);
+ }
+ }
+
+ /* Add on 4 for total length. */
+ if (pea_total_len) {
+ *pea_total_len += 4;
+ }
+ return ea_list_head;
+}
+
+/****************************************************************************
+ Fill a qfilepathinfo buffer with EA's.
+****************************************************************************/
+
+static unsigned int fill_ea_buffer(char *pdata, unsigned int total_data_size,
+ connection_struct *conn, files_struct *fsp, const char *fname)
+{
+ unsigned int ret_data_size = 4;
+ char *p = pdata;
+ size_t total_ea_len;
+ TALLOC_CTX *mem_ctx = talloc_init("fill_ea_buffer");
+ struct ea_list *ea_list = get_ea_list(mem_ctx, conn, fsp, fname, &total_ea_len);
+
+ SMB_ASSERT(total_data_size >= 4);
+
+ SIVAL(pdata,0,0);
+ if (!mem_ctx) {
+ return 4;
+ }
+
+ if (!ea_list) {
+ talloc_destroy(mem_ctx);
+ return 4;
+ }
+
+ if (total_ea_len > total_data_size) {
+ talloc_destroy(mem_ctx);
+ return 4;
+ }
+
+ total_data_size -= 4;
+ for (p = pdata + 4; ea_list; ea_list = ea_list->next) {
+ size_t dos_namelen;
+ fstring dos_ea_name;
+ push_ascii_fstring(dos_ea_name, ea_list->ea.name);
+ dos_namelen = strlen(dos_ea_name);
+ if (dos_namelen > 255 || dos_namelen == 0) {
+ break;
+ }
+ if (ea_list->ea.value.length > 65535) {
+ break;
+ }
+ if (4 + dos_namelen + 1 + ea_list->ea.value.length > total_data_size) {
+ break;
+ }
+
+ /* We know we have room. */
+ SCVAL(p,0,ea_list->ea.flags);
+ SCVAL(p,1,dos_namelen);
+ SSVAL(p,2,ea_list->ea.value.length);
+ fstrcpy(p+4, dos_ea_name);
+ memcpy( p + 4 + dos_namelen + 1, ea_list->ea.value.data, ea_list->ea.value.length);
+
+ total_data_size -= 4 + dos_namelen + 1 + ea_list->ea.value.length;
+ p += 4 + dos_namelen + 1 + ea_list->ea.value.length;
+ }
+
+ ret_data_size = PTR_DIFF(p, pdata);
+ talloc_destroy(mem_ctx);
+ SIVAL(pdata,0,ret_data_size);
+ return ret_data_size;
+}
+
+static unsigned int estimate_ea_size(connection_struct *conn, files_struct *fsp, const char *fname)
+{
+ size_t total_ea_len = 0;
+ TALLOC_CTX *mem_ctx = talloc_init("estimate_ea_size");
+
+ (void)get_ea_list(mem_ctx, conn, fsp, fname, &total_ea_len);
+ talloc_destroy(mem_ctx);
+ return total_ea_len;
+}
+
+/****************************************************************************
+ Set or delete an extended attribute.
+****************************************************************************/
+
+static NTSTATUS set_ea(connection_struct *conn, files_struct *fsp, const char *fname,
+ char *pdata, int total_data)
+{
+ unsigned int namelen;
+ unsigned int ealen;
+ int ret;
+ fstring unix_ea_name;
+
+ if (!lp_ea_support(SNUM(conn))) {
+ return NT_STATUS_EAS_NOT_SUPPORTED;
+ }
+
+ if (total_data < 8) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (IVAL(pdata,0) > total_data) {
+ DEBUG(10,("set_ea: bad total data size (%u) > %u\n", IVAL(pdata,0), (unsigned int)total_data));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ pdata += 4;
+ namelen = CVAL(pdata,1);
+ ealen = SVAL(pdata,2);
+ pdata += 4;
+ if (total_data < 8 + namelen + 1 + ealen) {
+ DEBUG(10,("set_ea: bad total data size (%u) < 8 + namelen (%u) + 1 + ealen (%u)\n",
+ (unsigned int)total_data, namelen, ealen));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (pdata[namelen] != '\0') {
+ DEBUG(10,("set_ea: ea name not null terminated\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ fstrcpy(unix_ea_name, "user."); /* All EA's must start with user. */
+ pull_ascii(&unix_ea_name[5], pdata, sizeof(fstring) - 5, -1, STR_TERMINATE);
+ pdata += (namelen + 1);
+
+ DEBUG(10,("set_ea: ea_name %s ealen = %u\n", unix_ea_name, ealen));
+ if (ealen) {
+ DEBUG(10,("set_ea: data :\n"));
+ dump_data(10, pdata, ealen);
+ }
+
+ if (samba_private_attr_name(unix_ea_name)) {
+ DEBUG(10,("set_ea: ea name %s is a private Samba name.\n", unix_ea_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (ealen == 0) {
+ /* Remove the attribute. */
+ if (fsp && (fsp->fd != -1)) {
+ DEBUG(10,("set_ea: deleting ea name %s on file %s by file descriptor.\n",
+ unix_ea_name, fsp->fsp_name));
+ ret = SMB_VFS_FREMOVEXATTR(fsp, fsp->fd, unix_ea_name);
+ } else {
+ DEBUG(10,("set_ea: deleting ea name %s on file %s.\n",
+ unix_ea_name, fname));
+ ret = SMB_VFS_REMOVEXATTR(conn, fname, unix_ea_name);
+ }
+#ifdef ENOATTR
+ /* Removing a non existent attribute always succeeds. */
+ DEBUG(10,("set_ea: deleting ea name %s didn't exist - succeeding by default.\n", unix_ea_name));
+ if (ret == -1 && errno == ENOATTR) {
+ ret = 0;
+ }
+#endif
+ } else {
+ if (fsp && (fsp->fd != -1)) {
+ DEBUG(10,("set_ea: setting ea name %s on file %s by file descriptor.\n",
+ unix_ea_name, fsp->fsp_name));
+ ret = SMB_VFS_FSETXATTR(fsp, fsp->fd, unix_ea_name, pdata, ealen, 0);
+ } else {
+ DEBUG(10,("set_ea: setting ea name %s on file %s.\n",
+ unix_ea_name, fname));
+ ret = SMB_VFS_SETXATTR(conn, fname, unix_ea_name, pdata, ealen, 0);
+ }
+ }
+
+ if (ret == -1) {
+ if (errno == ENOTSUP) {
+ return NT_STATUS_EAS_NOT_SUPPORTED;
+ }
+ return map_nt_error_from_unix(errno);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Send the required number of replies back.
+ We assume all fields other than the data fields are
+ set correctly for the type of call.
+ HACK ! Always assumes smb_setup field is zero.
+****************************************************************************/
+
+static int send_trans2_replies(char *outbuf,
+ int bufsize,
+ char *params,
+ int paramsize,
+ char *pdata,
+ int datasize)
+{
+ /* As we are using a protocol > LANMAN1 then the max_send
+ variable must have been set in the sessetupX call.
+ This takes precedence over the max_xmit field in the
+ global struct. These different max_xmit variables should
+ be merged as this is now too confusing */
+
+ extern int max_send;
+ int data_to_send = datasize;
+ int params_to_send = paramsize;
+ int useable_space;
+ char *pp = params;
+ char *pd = pdata;
+ int params_sent_thistime, data_sent_thistime, total_sent_thistime;
+ int alignment_offset = 1; /* JRA. This used to be 3. Set to 1 to make netmon parse ok. */
+ int data_alignment_offset = 0;
+
+ /* Initially set the wcnt area to be 10 - this is true for all trans2 replies */
+
+ set_message(outbuf,10,0,True);
+
+ /* If there genuinely are no parameters or data to send just send the empty packet */
+
+ if(params_to_send == 0 && data_to_send == 0) {
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("send_trans2_replies: send_smb failed.");
+ return 0;
+ }
+
+ /* When sending params and data ensure that both are nicely aligned */
+ /* Only do this alignment when there is also data to send - else
+ can cause NT redirector problems. */
+
+ if (((params_to_send % 4) != 0) && (data_to_send != 0))
+ data_alignment_offset = 4 - (params_to_send % 4);
+
+ /* Space is bufsize minus Netbios over TCP header minus SMB header */
+ /* The alignment_offset is to align the param bytes on an even byte
+ boundary. NT 4.0 Beta needs this to work correctly. */
+
+ useable_space = bufsize - ((smb_buf(outbuf)+ alignment_offset+data_alignment_offset) - outbuf);
+
+ /* useable_space can never be more than max_send minus the alignment offset. */
+
+ useable_space = MIN(useable_space, max_send - (alignment_offset+data_alignment_offset));
+
+ while (params_to_send || data_to_send) {
+ /* Calculate whether we will totally or partially fill this packet */
+
+ total_sent_thistime = params_to_send + data_to_send + alignment_offset + data_alignment_offset;
+
+ /* We can never send more than useable_space */
+ /*
+ * Note that 'useable_space' does not include the alignment offsets,
+ * but we must include the alignment offsets in the calculation of
+ * the length of the data we send over the wire, as the alignment offsets
+ * are sent here. Fix from Marc_Jacobsen@hp.com.
+ */
+
+ total_sent_thistime = MIN(total_sent_thistime, useable_space+ alignment_offset + data_alignment_offset);
+
+ set_message(outbuf, 10, total_sent_thistime, True);
+
+ /* Set total params and data to be sent */
+ SSVAL(outbuf,smb_tprcnt,paramsize);
+ SSVAL(outbuf,smb_tdrcnt,datasize);
+
+ /* Calculate how many parameters and data we can fit into
+ * this packet. Parameters get precedence
+ */
+
+ params_sent_thistime = MIN(params_to_send,useable_space);
+ data_sent_thistime = useable_space - params_sent_thistime;
+ data_sent_thistime = MIN(data_sent_thistime,data_to_send);
+
+ SSVAL(outbuf,smb_prcnt, params_sent_thistime);
+
+ /* smb_proff is the offset from the start of the SMB header to the
+ parameter bytes, however the first 4 bytes of outbuf are
+ the Netbios over TCP header. Thus use smb_base() to subtract
+ them from the calculation */
+
+ SSVAL(outbuf,smb_proff,((smb_buf(outbuf)+alignment_offset) - smb_base(outbuf)));
+
+ if(params_sent_thistime == 0)
+ SSVAL(outbuf,smb_prdisp,0);
+ else
+ /* Absolute displacement of param bytes sent in this packet */
+ SSVAL(outbuf,smb_prdisp,pp - params);
+
+ SSVAL(outbuf,smb_drcnt, data_sent_thistime);
+ if(data_sent_thistime == 0) {
+ SSVAL(outbuf,smb_droff,0);
+ SSVAL(outbuf,smb_drdisp, 0);
+ } else {
+ /* The offset of the data bytes is the offset of the
+ parameter bytes plus the number of parameters being sent this time */
+ SSVAL(outbuf,smb_droff,((smb_buf(outbuf)+alignment_offset) -
+ smb_base(outbuf)) + params_sent_thistime + data_alignment_offset);
+ SSVAL(outbuf,smb_drdisp, pd - pdata);
+ }
+
+ /* Copy the param bytes into the packet */
+
+ if(params_sent_thistime)
+ memcpy((smb_buf(outbuf)+alignment_offset),pp,params_sent_thistime);
+
+ /* Copy in the data bytes */
+ if(data_sent_thistime)
+ memcpy(smb_buf(outbuf)+alignment_offset+params_sent_thistime+
+ data_alignment_offset,pd,data_sent_thistime);
+
+ DEBUG(9,("t2_rep: params_sent_thistime = %d, data_sent_thistime = %d, useable_space = %d\n",
+ params_sent_thistime, data_sent_thistime, useable_space));
+ DEBUG(9,("t2_rep: params_to_send = %d, data_to_send = %d, paramsize = %d, datasize = %d\n",
+ params_to_send, data_to_send, paramsize, datasize));
+
+ /* Send the packet */
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("send_trans2_replies: send_smb failed.");
+
+ pp += params_sent_thistime;
+ pd += data_sent_thistime;
+
+ params_to_send -= params_sent_thistime;
+ data_to_send -= data_sent_thistime;
+
+ /* Sanity check */
+ if(params_to_send < 0 || data_to_send < 0) {
+ DEBUG(0,("send_trans2_replies failed sanity check pts = %d, dts = %d\n!!!",
+ params_to_send, data_to_send));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ Reply to a TRANSACT2_OPEN.
+****************************************************************************/
+
+static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, int bufsize,
+ char **pparams, int total_params, char **ppdata, int total_data)
+{
+ char *params = *pparams;
+ int16 open_mode;
+ int16 open_attr;
+ BOOL oplock_request;
+#if 0
+ BOOL return_additional_info;
+ int16 open_sattr;
+ time_t open_time;
+#endif
+ int16 open_ofun;
+ int32 open_size;
+ char *pname;
+ pstring fname;
+ SMB_OFF_T size=0;
+ int fmode=0,mtime=0,rmode;
+ SMB_INO_T inode = 0;
+ SMB_STRUCT_STAT sbuf;
+ int smb_action = 0;
+ BOOL bad_path = False;
+ files_struct *fsp;
+ NTSTATUS status;
+
+ /*
+ * Ensure we have enough parameters to perform the operation.
+ */
+
+ if (total_params < 29)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ open_mode = SVAL(params, 2);
+ open_attr = SVAL(params,6);
+ oplock_request = (((SVAL(params,0)|(1<<1))>>1) | ((SVAL(params,0)|(1<<2))>>1));
+#if 0
+ return_additional_info = BITSETW(params,0);
+ open_sattr = SVAL(params, 4);
+ open_time = make_unix_date3(params+8);
+#endif
+ open_ofun = SVAL(params,12);
+ open_size = IVAL(params,14);
+ pname = &params[28];
+
+ if (IS_IPC(conn))
+ return(ERROR_DOS(ERRSRV,ERRaccess));
+
+ srvstr_get_path(inbuf, fname, pname, sizeof(fname), -1, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+
+ DEBUG(3,("trans2open %s mode=%d attr=%d ofun=%d size=%d\n",
+ fname,open_mode, open_attr, open_ofun, open_size));
+
+ /* XXXX we need to handle passed times, sattr and flags */
+
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+
+ if (!check_name(fname,conn)) {
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
+ }
+
+ fsp = open_file_shared(conn,fname,&sbuf,open_mode,open_ofun,(uint32)open_attr,
+ oplock_request, &rmode,&smb_action);
+
+ if (!fsp) {
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
+ }
+
+ size = get_file_size(sbuf);
+ fmode = dos_mode(conn,fname,&sbuf);
+ mtime = sbuf.st_mtime;
+ inode = sbuf.st_ino;
+ if (fmode & aDIR) {
+ close_file(fsp,False);
+ return(ERROR_DOS(ERRDOS,ERRnoaccess));
+ }
+
+ /* Realloc the size of parameters and data we will return */
+ params = Realloc(*pparams, 28);
+ if( params == NULL )
+ return(ERROR_DOS(ERRDOS,ERRnomem));
+ *pparams = params;
+
+ memset((char *)params,'\0',28);
+ SSVAL(params,0,fsp->fnum);
+ SSVAL(params,2,fmode);
+ put_dos_date2(params,4, mtime);
+ SIVAL(params,8, (uint32)size);
+ SSVAL(params,12,rmode);
+
+ if (oplock_request && lp_fake_oplocks(SNUM(conn)))
+ smb_action |= EXTENDED_OPLOCK_GRANTED;
+
+ SSVAL(params,18,smb_action);
+
+ /*
+ * WARNING - this may need to be changed if SMB_INO_T <> 4 bytes.
+ */
+ SIVAL(params,20,inode);
+
+ /* Send the required number of replies */
+ send_trans2_replies(outbuf, bufsize, params, 28, *ppdata, 0);
+
+ return -1;
+}
+
+/*********************************************************
+ Routine to check if a given string matches exactly.
+ as a special case a mask of "." does NOT match. That
+ is required for correct wildcard semantics
+ Case can be significant or not.
+**********************************************************/
+
+static BOOL exact_match(char *str,char *mask, BOOL case_sig)
+{
+ if (mask[0] == '.' && mask[1] == 0)
+ return False;
+ if (case_sig)
+ return strcmp(str,mask)==0;
+ if (StrCaseCmp(str,mask) != 0) {
+ return False;
+ }
+ if (ms_has_wild(str)) {
+ return False;
+ }
+ return True;
+}
+
+/****************************************************************************
+ Return the filetype for UNIX extensions.
+****************************************************************************/
+
+static uint32 unix_filetype(mode_t mode)
+{
+ if(S_ISREG(mode))
+ return UNIX_TYPE_FILE;
+ else if(S_ISDIR(mode))
+ return UNIX_TYPE_DIR;
+#ifdef S_ISLNK
+ else if(S_ISLNK(mode))
+ return UNIX_TYPE_SYMLINK;
+#endif
+#ifdef S_ISCHR
+ else if(S_ISCHR(mode))
+ return UNIX_TYPE_CHARDEV;
+#endif
+#ifdef S_ISBLK
+ else if(S_ISBLK(mode))
+ return UNIX_TYPE_BLKDEV;
+#endif
+#ifdef S_ISFIFO
+ else if(S_ISFIFO(mode))
+ return UNIX_TYPE_FIFO;
+#endif
+#ifdef S_ISSOCK
+ else if(S_ISSOCK(mode))
+ return UNIX_TYPE_SOCKET;
+#endif
+
+ DEBUG(0,("unix_filetype: unknown filetype %u", (unsigned)mode));
+ return UNIX_TYPE_UNKNOWN;
+}
+
+/****************************************************************************
+ Return the major devicenumber for UNIX extensions.
+****************************************************************************/
+
+static uint32 unix_dev_major(SMB_DEV_T dev)
+{
+#if defined(HAVE_DEVICE_MAJOR_FN)
+ return (uint32)major(dev);
+#else
+ return (uint32)(dev >> 8);
+#endif
+}
+
+/****************************************************************************
+ Return the minor devicenumber for UNIX extensions.
+****************************************************************************/
+
+static uint32 unix_dev_minor(SMB_DEV_T dev)
+{
+#if defined(HAVE_DEVICE_MINOR_FN)
+ return (uint32)minor(dev);
+#else
+ return (uint32)(dev & 0xff);
+#endif
+}
+
+/****************************************************************************
+ Map wire perms onto standard UNIX permissions. Obey share restrictions.
+****************************************************************************/
+
+static mode_t unix_perms_from_wire( connection_struct *conn, SMB_STRUCT_STAT *pst, uint32 perms)
+{
+ mode_t ret = 0;
+
+ if (perms == SMB_MODE_NO_CHANGE)
+ return pst->st_mode;
+
+ ret |= ((perms & UNIX_X_OTH ) ? S_IXOTH : 0);
+ ret |= ((perms & UNIX_W_OTH ) ? S_IWOTH : 0);
+ ret |= ((perms & UNIX_R_OTH ) ? S_IROTH : 0);
+ ret |= ((perms & UNIX_X_GRP ) ? S_IXGRP : 0);
+ ret |= ((perms & UNIX_W_GRP ) ? S_IWGRP : 0);
+ ret |= ((perms & UNIX_R_GRP ) ? S_IRGRP : 0);
+ ret |= ((perms & UNIX_X_USR ) ? S_IXUSR : 0);
+ ret |= ((perms & UNIX_W_USR ) ? S_IWUSR : 0);
+ ret |= ((perms & UNIX_R_USR ) ? S_IRUSR : 0);
+#ifdef S_ISVTX
+ ret |= ((perms & UNIX_STICKY ) ? S_ISVTX : 0);
+#endif
+#ifdef S_ISGID
+ ret |= ((perms & UNIX_SET_GID ) ? S_ISGID : 0);
+#endif
+#ifdef S_ISUID
+ ret |= ((perms & UNIX_SET_UID ) ? S_ISUID : 0);
+#endif
+
+ if (VALID_STAT(*pst) && S_ISDIR(pst->st_mode)) {
+ ret &= lp_dir_mask(SNUM(conn));
+ /* Add in force bits */
+ ret |= lp_force_dir_mode(SNUM(conn));
+ } else {
+ /* Apply mode mask */
+ ret &= lp_create_mask(SNUM(conn));
+ /* Add in force bits */
+ ret |= lp_force_create_mode(SNUM(conn));
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ Checks for SMB_TIME_NO_CHANGE and if not found calls interpret_long_date.
+****************************************************************************/
+
+time_t interpret_long_unix_date(char *p)
+{
+ DEBUG(1,("interpret_long_unix_date\n"));
+ if(IVAL(p,0) == SMB_TIME_NO_CHANGE_LO &&
+ IVAL(p,4) == SMB_TIME_NO_CHANGE_HI) {
+ return -1;
+ } else {
+ return interpret_long_date(p);
+ }
+}
+
+/****************************************************************************
+ Get a level dependent lanman2 dir entry.
+****************************************************************************/
+
+static BOOL get_lanman2_dir_entry(connection_struct *conn,
+ void *inbuf, void *outbuf,
+ char *path_mask,int dirtype,int info_level,
+ int requires_resume_key,
+ BOOL dont_descend,char **ppdata,
+ char *base_data, int space_remaining,
+ BOOL *out_of_space, BOOL *got_exact_match,
+ int *last_name_off)
+{
+ const char *dname;
+ BOOL found = False;
+ SMB_STRUCT_STAT sbuf;
+ pstring mask;
+ pstring pathreal;
+ pstring fname;
+ char *p, *q, *pdata = *ppdata;
+ uint32 reskey=0;
+ int prev_dirpos=0;
+ int mode=0;
+ SMB_OFF_T file_size = 0;
+ SMB_BIG_UINT allocation_size = 0;
+ uint32 len;
+ time_t mdate=0, adate=0, cdate=0;
+ char *nameptr;
+ BOOL was_8_3;
+ int nt_extmode; /* Used for NT connections instead of mode */
+ BOOL needslash = ( conn->dirpath[strlen(conn->dirpath) -1] != '/');
+
+ *fname = 0;
+ *out_of_space = False;
+ *got_exact_match = False;
+
+ if (!conn->dirptr)
+ return(False);
+
+ p = strrchr_m(path_mask,'/');
+ if(p != NULL) {
+ if(p[1] == '\0')
+ pstrcpy(mask,"*.*");
+ else
+ pstrcpy(mask, p+1);
+ } else
+ pstrcpy(mask, path_mask);
+
+ while (!found) {
+ BOOL got_match;
+
+ /* Needed if we run out of space */
+ prev_dirpos = TellDir(conn->dirptr);
+ dname = ReadDirName(conn->dirptr);
+
+ /*
+ * Due to bugs in NT client redirectors we are not using
+ * resume keys any more - set them to zero.
+ * Check out the related comments in findfirst/findnext.
+ * JRA.
+ */
+
+ reskey = 0;
+
+ DEBUG(8,("get_lanman2_dir_entry:readdir on dirptr 0x%lx now at offset %d\n",
+ (long)conn->dirptr,TellDir(conn->dirptr)));
+
+ if (!dname)
+ return(False);
+
+ pstrcpy(fname,dname);
+
+ if(!(got_match = *got_exact_match = exact_match(fname, mask, case_sensitive)))
+ got_match = mask_match(fname, mask, case_sensitive);
+
+ if(!got_match && !mangle_is_8_3(fname, False)) {
+
+ /*
+ * It turns out that NT matches wildcards against
+ * both long *and* short names. This may explain some
+ * of the wildcard wierdness from old DOS clients
+ * that some people have been seeing.... JRA.
+ */
+
+ pstring newname;
+ pstrcpy( newname, fname);
+ mangle_map( newname, True, False, SNUM(conn));
+ if(!(got_match = *got_exact_match = exact_match(newname, mask, case_sensitive)))
+ got_match = mask_match(newname, mask, case_sensitive);
+ }
+
+ if(got_match) {
+ BOOL isdots = (strequal(fname,"..") || strequal(fname,"."));
+ if (dont_descend && !isdots)
+ continue;
+
+ pstrcpy(pathreal,conn->dirpath);
+ if(needslash)
+ pstrcat(pathreal,"/");
+ pstrcat(pathreal,dname);
+
+ if (INFO_LEVEL_IS_UNIX(info_level)) {
+ if (SMB_VFS_LSTAT(conn,pathreal,&sbuf) != 0) {
+ DEBUG(5,("get_lanman2_dir_entry:Couldn't lstat [%s] (%s)\n",
+ pathreal,strerror(errno)));
+ continue;
+ }
+ } else if (SMB_VFS_STAT(conn,pathreal,&sbuf) != 0) {
+
+ /* Needed to show the msdfs symlinks as
+ * directories */
+
+ if(lp_host_msdfs() &&
+ lp_msdfs_root(SNUM(conn)) &&
+ is_msdfs_link(conn, pathreal, NULL, NULL,
+ &sbuf)) {
+
+ DEBUG(5,("get_lanman2_dir_entry: Masquerading msdfs link %s as a directory\n", pathreal));
+ sbuf.st_mode = (sbuf.st_mode & 0xFFF) | S_IFDIR;
+
+ } else {
+
+ DEBUG(5,("get_lanman2_dir_entry:Couldn't stat [%s] (%s)\n",
+ pathreal,strerror(errno)));
+ continue;
+ }
+ }
+
+ mode = dos_mode(conn,pathreal,&sbuf);
+
+ if (!dir_check_ftype(conn,mode,&sbuf,dirtype)) {
+ DEBUG(5,("[%s] attribs didn't match %x\n",fname,dirtype));
+ continue;
+ }
+
+ file_size = get_file_size(sbuf);
+ allocation_size = get_allocation_size(NULL,&sbuf);
+ mdate = sbuf.st_mtime;
+ adate = sbuf.st_atime;
+ cdate = get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn)));
+
+ if (lp_dos_filetime_resolution(SNUM(conn))) {
+ cdate &= ~1;
+ mdate &= ~1;
+ adate &= ~1;
+ }
+
+ if(mode & aDIR)
+ file_size = 0;
+
+ DEBUG(5,("get_lanman2_dir_entry found %s fname=%s\n",pathreal,fname));
+
+ found = True;
+ }
+ }
+
+ mangle_map(fname,False,True,SNUM(conn));
+
+ p = pdata;
+ nameptr = p;
+
+ nt_extmode = mode ? mode : FILE_ATTRIBUTE_NORMAL;
+
+ switch (info_level) {
+ case SMB_INFO_STANDARD:
+ if(requires_resume_key) {
+ SIVAL(p,0,reskey);
+ p += 4;
+ }
+ put_dos_date2(p,l1_fdateCreation,cdate);
+ put_dos_date2(p,l1_fdateLastAccess,adate);
+ put_dos_date2(p,l1_fdateLastWrite,mdate);
+ SIVAL(p,l1_cbFile,(uint32)file_size);
+ SIVAL(p,l1_cbFileAlloc,(uint32)allocation_size);
+ SSVAL(p,l1_attrFile,mode);
+ p += l1_achName;
+ nameptr = p;
+ p += align_string(outbuf, p, 0);
+ len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE);
+ if (SVAL(outbuf, smb_flg2) & FLAGS2_UNICODE_STRINGS)
+ SCVAL(nameptr, -1, len-2);
+ else
+ SCVAL(nameptr, -1, len-1);
+ p += len;
+ break;
+
+ case SMB_INFO_QUERY_EA_SIZE:
+ if(requires_resume_key) {
+ SIVAL(p,0,reskey);
+ p += 4;
+ }
+ put_dos_date2(p,l2_fdateCreation,cdate);
+ put_dos_date2(p,l2_fdateLastAccess,adate);
+ put_dos_date2(p,l2_fdateLastWrite,mdate);
+ SIVAL(p,l2_cbFile,(uint32)file_size);
+ SIVAL(p,l2_cbFileAlloc,(uint32)allocation_size);
+ SSVAL(p,l2_attrFile,mode);
+ SIVAL(p,l2_cbList,0); /* No extended attributes */
+ p += l2_achName;
+ nameptr = p;
+ len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE | STR_NOALIGN);
+ if (SVAL(outbuf, smb_flg2) & FLAGS2_UNICODE_STRINGS)
+ SCVAL(nameptr, -1, len-2);
+ else
+ SCVAL(nameptr, -1, len-1);
+ p += len;
+ break;
+
+ case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
+ was_8_3 = mangle_is_8_3(fname, True);
+ p += 4;
+ SIVAL(p,0,reskey); p += 4;
+ put_long_date(p,cdate); p += 8;
+ put_long_date(p,adate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ SOFF_T(p,0,file_size);
+ SOFF_T(p,8,allocation_size);
+ p += 16;
+ SIVAL(p,0,nt_extmode); p += 4;
+ q = p; p += 4;
+ SIVAL(p,0,0); p += 4;
+ /* Clear the short name buffer. This is
+ * IMPORTANT as not doing so will trigger
+ * a Win2k client bug. JRA.
+ */
+ memset(p,'\0',26);
+ if (!was_8_3 && lp_manglednames(SNUM(conn))) {
+ pstring mangled_name;
+ pstrcpy(mangled_name, fname);
+ mangle_map(mangled_name,True,True,SNUM(conn));
+ mangled_name[12] = 0;
+ len = srvstr_push(outbuf, p+2, mangled_name, 24, STR_UPPER|STR_UNICODE);
+ SSVAL(p, 0, len);
+ } else {
+ SSVAL(p,0,0);
+ *(p+2) = 0;
+ }
+ p += 2 + 24;
+ len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE_ASCII);
+ SIVAL(q,0,len);
+ p += len;
+ len = PTR_DIFF(p, pdata);
+ len = (len + 3) & ~3;
+ SIVAL(pdata,0,len);
+ p = pdata + len;
+ break;
+
+ case SMB_FIND_FILE_DIRECTORY_INFO:
+ p += 4;
+ SIVAL(p,0,reskey); p += 4;
+ put_long_date(p,cdate); p += 8;
+ put_long_date(p,adate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ SOFF_T(p,0,file_size);
+ SOFF_T(p,8,allocation_size);
+ p += 16;
+ SIVAL(p,0,nt_extmode); p += 4;
+ p += 4;
+ len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE_ASCII);
+ SIVAL(p, -4, len);
+ p += len;
+ len = PTR_DIFF(p, pdata);
+ len = (len + 3) & ~3;
+ SIVAL(pdata,0,len);
+ p = pdata + len;
+ break;
+
+ case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
+ p += 4;
+ SIVAL(p,0,reskey); p += 4;
+ put_long_date(p,cdate); p += 8;
+ put_long_date(p,adate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ SOFF_T(p,0,file_size);
+ SOFF_T(p,8,allocation_size);
+ p += 16;
+ SIVAL(p,0,nt_extmode);
+ p += 4;
+
+ SIVAL(p,4,0); /* ea size */
+ len = srvstr_push(outbuf, p+8, fname, -1, STR_TERMINATE_ASCII);
+ SIVAL(p, 0, len);
+ p += 8 + len;
+
+ len = PTR_DIFF(p, pdata);
+ len = (len + 3) & ~3;
+ SIVAL(pdata,0,len);
+ p = pdata + len;
+ break;
+
+ case SMB_FIND_FILE_NAMES_INFO:
+ p += 4;
+ SIVAL(p,0,reskey); p += 4;
+ p += 4;
+ /* this must *not* be null terminated or w2k gets in a loop trying to set an
+ acl on a dir (tridge) */
+ len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE_ASCII);
+ SIVAL(p, -4, len);
+ p += len;
+ len = PTR_DIFF(p, pdata);
+ len = (len + 3) & ~3;
+ SIVAL(pdata,0,len);
+ p = pdata + len;
+ break;
+
+ case SMB_FIND_FILE_LEVEL_261:
+ p += 4;
+ SIVAL(p,0,reskey); p += 4;
+ put_long_date(p,cdate); p += 8;
+ put_long_date(p,adate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ SOFF_T(p,0,file_size);
+ SOFF_T(p,8,allocation_size);
+ p += 16;
+ SIVAL(p,0,nt_extmode);
+ p += 4;
+ len = srvstr_push(outbuf, p + 20, fname, -1, STR_TERMINATE_ASCII);
+ SIVAL(p, 0, len);
+ memset(p+4,'\0',16); /* EA size. Unknown 0 1 2 */
+ p += 20 + len; /* Strlen, EA size. Unknown 0 1 2, string itself */
+ len = PTR_DIFF(p, pdata);
+ len = (len + 3) & ~3;
+ SIVAL(pdata,0,len);
+ p = pdata + len;
+ break;
+
+ case SMB_FIND_FILE_LEVEL_262:
+ was_8_3 = mangle_is_8_3(fname, True);
+ p += 4;
+ SIVAL(p,0,reskey); p += 4;
+ put_long_date(p,cdate); p += 8;
+ put_long_date(p,adate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ SOFF_T(p,0,file_size);
+ SOFF_T(p,8,allocation_size);
+ p += 16;
+ SIVAL(p,0,nt_extmode); p += 4;
+ q = p; p += 4;
+ SIVAL(p,0,0); p += 4;
+ /* Clear the short name buffer. This is
+ * IMPORTANT as not doing so will trigger
+ * a Win2k client bug. JRA.
+ */
+ memset(p,'\0',26);
+ if (!was_8_3 && lp_manglednames(SNUM(conn))) {
+ pstring mangled_name;
+ pstrcpy(mangled_name, fname);
+ mangle_map(mangled_name,True,True,SNUM(conn));
+ mangled_name[12] = 0;
+ len = srvstr_push(outbuf, p+2, mangled_name, 24, STR_UPPER|STR_UNICODE);
+ SSVAL(p, 0, len);
+ } else {
+ SSVAL(p,0,0);
+ *(p+2) = 0;
+ }
+ p += 2 + 24;
+ memset(p, '\0', 10); /* 2 4 byte unknowns plus a zero reserved. */
+ p += 10;
+ len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE_ASCII);
+ SIVAL(q,0,len);
+ p += len;
+ len = PTR_DIFF(p, pdata);
+ len = (len + 3) & ~3;
+ SIVAL(pdata,0,len);
+ p = pdata + len;
+ break;
+
+ /* CIFS UNIX Extension. */
+
+ case SMB_FIND_FILE_UNIX:
+ p+= 4;
+ SIVAL(p,0,reskey); p+= 4; /* Used for continuing search. */
+
+ /* Begin of SMB_QUERY_FILE_UNIX_BASIC */
+ SOFF_T(p,0,get_file_size(sbuf)); /* File size 64 Bit */
+ p+= 8;
+
+ SOFF_T(p,0,get_allocation_size(NULL,&sbuf)); /* Number of bytes used on disk - 64 Bit */
+ p+= 8;
+
+ put_long_date(p,sbuf.st_ctime); /* Creation Time 64 Bit */
+ put_long_date(p+8,sbuf.st_atime); /* Last access time 64 Bit */
+ put_long_date(p+16,sbuf.st_mtime); /* Last modification time 64 Bit */
+ p+= 24;
+
+ SIVAL(p,0,sbuf.st_uid); /* user id for the owner */
+ SIVAL(p,4,0);
+ p+= 8;
+
+ SIVAL(p,0,sbuf.st_gid); /* group id of owner */
+ SIVAL(p,4,0);
+ p+= 8;
+
+ SIVAL(p,0,unix_filetype(sbuf.st_mode));
+ p+= 4;
+
+ SIVAL(p,0,unix_dev_major(sbuf.st_rdev)); /* Major device number if type is device */
+ SIVAL(p,4,0);
+ p+= 8;
+
+ SIVAL(p,0,unix_dev_minor(sbuf.st_rdev)); /* Minor device number if type is device */
+ SIVAL(p,4,0);
+ p+= 8;
+
+ SINO_T(p,0,(SMB_INO_T)sbuf.st_ino); /* inode number */
+ p+= 8;
+
+ SIVAL(p,0, unix_perms_to_wire(sbuf.st_mode)); /* Standard UNIX file permissions */
+ SIVAL(p,4,0);
+ p+= 8;
+
+ SIVAL(p,0,sbuf.st_nlink); /* number of hard links */
+ SIVAL(p,4,0);
+ p+= 8;
+
+ len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE);
+ p += len;
+
+ len = PTR_DIFF(p, pdata);
+ len = (len + 3) & ~3;
+ SIVAL(pdata,0,len); /* Offset from this structure to the beginning of the next one */
+ p = pdata + len;
+ /* End of SMB_QUERY_FILE_UNIX_BASIC */
+
+ break;
+
+ default:
+ return(False);
+ }
+
+
+ if (PTR_DIFF(p,pdata) > space_remaining) {
+ /* Move the dirptr back to prev_dirpos */
+ SeekDir(conn->dirptr, prev_dirpos);
+ *out_of_space = True;
+ DEBUG(9,("get_lanman2_dir_entry: out of space\n"));
+ return False; /* Not finished - just out of space */
+ }
+
+ /* Setup the last_filename pointer, as an offset from base_data */
+ *last_name_off = PTR_DIFF(nameptr,base_data);
+ /* Advance the data pointer to the next slot */
+ *ppdata = p;
+
+ return(found);
+}
+
+/****************************************************************************
+ Reply to a TRANS2_FINDFIRST.
+****************************************************************************/
+
+static int call_trans2findfirst(connection_struct *conn, char *inbuf, char *outbuf, int bufsize,
+ char **pparams, int total_params, char **ppdata, int total_data)
+{
+ /* We must be careful here that we don't return more than the
+ allowed number of data bytes. If this means returning fewer than
+ maxentries then so be it. We assume that the redirector has
+ enough room for the fixed number of parameter bytes it has
+ requested. */
+ uint32 max_data_bytes = SVAL(inbuf, smb_mdrcnt);
+ char *params = *pparams;
+ char *pdata = *ppdata;
+ int dirtype = SVAL(params,0);
+ int maxentries = SVAL(params,2);
+ BOOL close_after_first = BITSETW(params+4,0);
+ BOOL close_if_end = BITSETW(params+4,1);
+ BOOL requires_resume_key = BITSETW(params+4,2);
+ int info_level = SVAL(params,6);
+ pstring directory;
+ pstring mask;
+ char *p, *wcard;
+ int last_name_off=0;
+ int dptr_num = -1;
+ int numentries = 0;
+ int i;
+ BOOL finished = False;
+ BOOL dont_descend = False;
+ BOOL out_of_space = False;
+ int space_remaining;
+ BOOL bad_path = False;
+ SMB_STRUCT_STAT sbuf;
+ NTSTATUS ntstatus = NT_STATUS_OK;
+
+ if (total_params < 12)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ *directory = *mask = 0;
+
+ DEBUG(3,("call_trans2findfirst: dirtype = %d, maxentries = %d, close_after_first=%d, \
+close_if_end = %d requires_resume_key = %d level = %d, max_data_bytes = %d\n",
+ dirtype, maxentries, close_after_first, close_if_end, requires_resume_key,
+ info_level, max_data_bytes));
+
+ switch (info_level) {
+ case SMB_INFO_STANDARD:
+ case SMB_INFO_QUERY_EA_SIZE:
+ case SMB_FIND_FILE_DIRECTORY_INFO:
+ case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
+ case SMB_FIND_FILE_NAMES_INFO:
+ case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
+ case SMB_FIND_FILE_LEVEL_261:
+ case SMB_FIND_FILE_LEVEL_262:
+ break;
+ case SMB_FIND_FILE_UNIX:
+ if (!lp_unix_extensions())
+ return(ERROR_DOS(ERRDOS,ERRunknownlevel));
+ break;
+ default:
+ return(ERROR_DOS(ERRDOS,ERRunknownlevel));
+ }
+
+ srvstr_get_path(inbuf, directory, params+12, sizeof(directory), -1, STR_TERMINATE, &ntstatus);
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ return ERROR_NT(ntstatus);
+ }
+
+ RESOLVE_FINDFIRST_DFSPATH(directory, conn, inbuf, outbuf);
+
+ unix_convert(directory,conn,0,&bad_path,&sbuf);
+ if(!check_name(directory,conn)) {
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
+ }
+
+ p = strrchr_m(directory,'/');
+ if(p == NULL) {
+ /* Windows and OS/2 systems treat search on the root '\' as if it were '\*' */
+ if((directory[0] == '.') && (directory[1] == '\0'))
+ pstrcpy(mask,"*");
+ else
+ pstrcpy(mask,directory);
+ pstrcpy(directory,"./");
+ } else {
+ pstrcpy(mask,p+1);
+ *p = 0;
+ }
+
+ DEBUG(5,("dir=%s, mask = %s\n",directory, mask));
+
+ pdata = Realloc(*ppdata, max_data_bytes + 1024);
+ if( pdata == NULL )
+ return(ERROR_DOS(ERRDOS,ERRnomem));
+
+ *ppdata = pdata;
+ memset((char *)pdata,'\0',max_data_bytes + 1024);
+
+ /* Realloc the params space */
+ params = Realloc(*pparams, 10);
+ if (params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ *pparams = params;
+
+ dptr_num = dptr_create(conn,directory, False, True ,SVAL(inbuf,smb_pid));
+ if (dptr_num < 0)
+ return(UNIXERROR(ERRDOS,ERRbadfile));
+
+ /* Save the wildcard match and attribs we are using on this directory -
+ needed as lanman2 assumes these are being saved between calls */
+
+ if(!(wcard = strdup(mask))) {
+ dptr_close(&dptr_num);
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ }
+
+ dptr_set_wcard(dptr_num, wcard);
+ dptr_set_attr(dptr_num, dirtype);
+
+ DEBUG(4,("dptr_num is %d, wcard = %s, attr = %d\n",dptr_num, wcard, dirtype));
+
+ /* We don't need to check for VOL here as this is returned by
+ a different TRANS2 call. */
+
+ DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n", conn->dirpath,lp_dontdescend(SNUM(conn))));
+ if (in_list(conn->dirpath,lp_dontdescend(SNUM(conn)),case_sensitive))
+ dont_descend = True;
+
+ p = pdata;
+ space_remaining = max_data_bytes;
+ out_of_space = False;
+
+ for (i=0;(i<maxentries) && !finished && !out_of_space;i++) {
+ BOOL got_exact_match = False;
+
+ /* this is a heuristic to avoid seeking the dirptr except when
+ absolutely necessary. It allows for a filename of about 40 chars */
+ if (space_remaining < DIRLEN_GUESS && numentries > 0) {
+ out_of_space = True;
+ finished = False;
+ } else {
+ finished = !get_lanman2_dir_entry(conn,
+ inbuf, outbuf,
+ mask,dirtype,info_level,
+ requires_resume_key,dont_descend,
+ &p,pdata,space_remaining, &out_of_space, &got_exact_match,
+ &last_name_off);
+ }
+
+ if (finished && out_of_space)
+ finished = False;
+
+ if (!finished && !out_of_space)
+ numentries++;
+
+ /*
+ * As an optimisation if we know we aren't looking
+ * for a wildcard name (ie. the name matches the wildcard exactly)
+ * then we can finish on any (first) match.
+ * This speeds up large directory searches. JRA.
+ */
+
+ if(got_exact_match)
+ finished = True;
+
+ space_remaining = max_data_bytes - PTR_DIFF(p,pdata);
+ }
+
+ /* Check if we can close the dirptr */
+ if(close_after_first || (finished && close_if_end)) {
+ DEBUG(5,("call_trans2findfirst - (2) closing dptr_num %d\n", dptr_num));
+ dptr_close(&dptr_num);
+ }
+
+ /*
+ * If there are no matching entries we must return ERRDOS/ERRbadfile -
+ * from observation of NT.
+ */
+
+ if(numentries == 0) {
+ dptr_close(&dptr_num);
+ return ERROR_DOS(ERRDOS,ERRbadfile);
+ }
+
+ /* At this point pdata points to numentries directory entries. */
+
+ /* Set up the return parameter block */
+ SSVAL(params,0,dptr_num);
+ SSVAL(params,2,numentries);
+ SSVAL(params,4,finished);
+ SSVAL(params,6,0); /* Never an EA error */
+ SSVAL(params,8,last_name_off);
+
+ send_trans2_replies( outbuf, bufsize, params, 10, pdata, PTR_DIFF(p,pdata));
+
+ if ((! *directory) && dptr_path(dptr_num))
+ slprintf(directory,sizeof(directory)-1, "(%s)",dptr_path(dptr_num));
+
+ DEBUG( 4, ( "%s mask=%s directory=%s dirtype=%d numentries=%d\n",
+ smb_fn_name(CVAL(inbuf,smb_com)),
+ mask, directory, dirtype, numentries ) );
+
+ /*
+ * Force a name mangle here to ensure that the
+ * mask as an 8.3 name is top of the mangled cache.
+ * The reasons for this are subtle. Don't remove
+ * this code unless you know what you are doing
+ * (see PR#13758). JRA.
+ */
+
+ if(!mangle_is_8_3_wildcards( mask, False))
+ mangle_map(mask, True, True, SNUM(conn));
+
+ return(-1);
+}
+
+/****************************************************************************
+ Reply to a TRANS2_FINDNEXT.
+****************************************************************************/
+
+static int call_trans2findnext(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
+ char **pparams, int total_params, char **ppdata, int total_data)
+{
+ /* We must be careful here that we don't return more than the
+ allowed number of data bytes. If this means returning fewer than
+ maxentries then so be it. We assume that the redirector has
+ enough room for the fixed number of parameter bytes it has
+ requested. */
+ int max_data_bytes = SVAL(inbuf, smb_mdrcnt);
+ char *params = *pparams;
+ char *pdata = *ppdata;
+ int dptr_num = SVAL(params,0);
+ int maxentries = SVAL(params,2);
+ uint16 info_level = SVAL(params,4);
+ uint32 resume_key = IVAL(params,6);
+ BOOL close_after_request = BITSETW(params+10,0);
+ BOOL close_if_end = BITSETW(params+10,1);
+ BOOL requires_resume_key = BITSETW(params+10,2);
+ BOOL continue_bit = BITSETW(params+10,3);
+ pstring resume_name;
+ pstring mask;
+ pstring directory;
+ char *p;
+ uint16 dirtype;
+ int numentries = 0;
+ int i, last_name_off=0;
+ BOOL finished = False;
+ BOOL dont_descend = False;
+ BOOL out_of_space = False;
+ int space_remaining;
+ NTSTATUS ntstatus = NT_STATUS_OK;
+
+ if (total_params < 12)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ *mask = *directory = *resume_name = 0;
+
+ srvstr_get_path(inbuf, resume_name, params+12, sizeof(resume_name), -1, STR_TERMINATE, &ntstatus);
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ return ERROR_NT(ntstatus);
+ }
+
+ DEBUG(3,("call_trans2findnext: dirhandle = %d, max_data_bytes = %d, maxentries = %d, \
+close_after_request=%d, close_if_end = %d requires_resume_key = %d \
+resume_key = %d resume name = %s continue=%d level = %d\n",
+ dptr_num, max_data_bytes, maxentries, close_after_request, close_if_end,
+ requires_resume_key, resume_key, resume_name, continue_bit, info_level));
+
+ switch (info_level) {
+ case SMB_INFO_STANDARD:
+ case SMB_INFO_QUERY_EA_SIZE:
+ case SMB_FIND_FILE_DIRECTORY_INFO:
+ case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
+ case SMB_FIND_FILE_NAMES_INFO:
+ case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
+ break;
+ case SMB_FIND_FILE_UNIX:
+ if (!lp_unix_extensions())
+ return(ERROR_DOS(ERRDOS,ERRunknownlevel));
+ break;
+ default:
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+
+ pdata = Realloc( *ppdata, max_data_bytes + 1024);
+ if(pdata == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+
+ *ppdata = pdata;
+ memset((char *)pdata,'\0',max_data_bytes + 1024);
+
+ /* Realloc the params space */
+ params = Realloc(*pparams, 6*SIZEOFWORD);
+ if( params == NULL )
+ return ERROR_DOS(ERRDOS,ERRnomem);
+
+ *pparams = params;
+
+ /* Check that the dptr is valid */
+ if(!(conn->dirptr = dptr_fetch_lanman2(dptr_num)))
+ return ERROR_DOS(ERRDOS,ERRnofiles);
+
+ string_set(&conn->dirpath,dptr_path(dptr_num));
+
+ /* Get the wildcard mask from the dptr */
+ if((p = dptr_wcard(dptr_num))== NULL) {
+ DEBUG(2,("dptr_num %d has no wildcard\n", dptr_num));
+ return ERROR_DOS(ERRDOS,ERRnofiles);
+ }
+
+ pstrcpy(mask, p);
+ pstrcpy(directory,conn->dirpath);
+
+ /* Get the attr mask from the dptr */
+ dirtype = dptr_attr(dptr_num);
+
+ DEBUG(3,("dptr_num is %d, mask = %s, attr = %x, dirptr=(0x%lX,%d)\n",
+ dptr_num, mask, dirtype,
+ (long)conn->dirptr,
+ TellDir(conn->dirptr)));
+
+ /* We don't need to check for VOL here as this is returned by
+ a different TRANS2 call. */
+
+ DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",conn->dirpath,lp_dontdescend(SNUM(conn))));
+ if (in_list(conn->dirpath,lp_dontdescend(SNUM(conn)),case_sensitive))
+ dont_descend = True;
+
+ p = pdata;
+ space_remaining = max_data_bytes;
+ out_of_space = False;
+
+ /*
+ * Seek to the correct position. We no longer use the resume key but
+ * depend on the last file name instead.
+ */
+
+ if(requires_resume_key && *resume_name && !continue_bit) {
+
+ /*
+ * Fix for NT redirector problem triggered by resume key indexes
+ * changing between directory scans. We now return a resume key of 0
+ * and instead look for the filename to continue from (also given
+ * to us by NT/95/smbfs/smbclient). If no other scans have been done between the
+ * findfirst/findnext (as is usual) then the directory pointer
+ * should already be at the correct place. Check this by scanning
+ * backwards looking for an exact (ie. case sensitive) filename match.
+ * If we get to the beginning of the directory and haven't found it then scan
+ * forwards again looking for a match. JRA.
+ */
+
+ int current_pos, start_pos;
+ const char *dname = NULL;
+ pstring dname_pstring;
+ void *dirptr = conn->dirptr;
+ start_pos = TellDir(dirptr);
+ for(current_pos = start_pos; current_pos >= 0; current_pos--) {
+ DEBUG(7,("call_trans2findnext: seeking to pos %d\n", current_pos));
+
+ SeekDir(dirptr, current_pos);
+ dname = ReadDirName(dirptr);
+ if (dname) {
+ /*
+ * Remember, mangle_map is called by
+ * get_lanman2_dir_entry(), so the resume name
+ * could be mangled. Ensure we do the same
+ * here.
+ */
+
+ /* make sure we get a copy that mangle_map can modify */
+
+ pstrcpy(dname_pstring, dname);
+ mangle_map( dname_pstring, False, True, SNUM(conn));
+
+ if(strcsequal( resume_name, dname_pstring)) {
+ SeekDir(dirptr, current_pos+1);
+ DEBUG(7,("call_trans2findnext: got match at pos %d\n", current_pos+1 ));
+ break;
+ }
+ }
+ }
+
+ /*
+ * Scan forward from start if not found going backwards.
+ */
+
+ if(current_pos < 0) {
+ DEBUG(7,("call_trans2findnext: notfound: seeking to pos %d\n", start_pos));
+ SeekDir(dirptr, start_pos);
+ for(current_pos = start_pos; (dname = ReadDirName(dirptr)) != NULL; SeekDir(dirptr,++current_pos)) {
+
+ /*
+ * Remember, mangle_map is called by
+ * get_lanman2_dir_entry(), so the resume name
+ * could be mangled. Ensure we do the same
+ * here.
+ */
+
+ if(dname) {
+ /* make sure we get a copy that mangle_map can modify */
+
+ pstrcpy(dname_pstring, dname);
+ mangle_map(dname_pstring, False, True, SNUM(conn));
+
+ if(strcsequal( resume_name, dname_pstring)) {
+ SeekDir(dirptr, current_pos+1);
+ DEBUG(7,("call_trans2findnext: got match at pos %d\n", current_pos+1 ));
+ break;
+ }
+ }
+ } /* end for */
+ } /* end if current_pos */
+ } /* end if requires_resume_key && !continue_bit */
+
+ for (i=0;(i<(int)maxentries) && !finished && !out_of_space ;i++) {
+ BOOL got_exact_match = False;
+
+ /* this is a heuristic to avoid seeking the dirptr except when
+ absolutely necessary. It allows for a filename of about 40 chars */
+ if (space_remaining < DIRLEN_GUESS && numentries > 0) {
+ out_of_space = True;
+ finished = False;
+ } else {
+ finished = !get_lanman2_dir_entry(conn,
+ inbuf, outbuf,
+ mask,dirtype,info_level,
+ requires_resume_key,dont_descend,
+ &p,pdata,space_remaining, &out_of_space, &got_exact_match,
+ &last_name_off);
+ }
+
+ if (finished && out_of_space)
+ finished = False;
+
+ if (!finished && !out_of_space)
+ numentries++;
+
+ /*
+ * As an optimisation if we know we aren't looking
+ * for a wildcard name (ie. the name matches the wildcard exactly)
+ * then we can finish on any (first) match.
+ * This speeds up large directory searches. JRA.
+ */
+
+ if(got_exact_match)
+ finished = True;
+
+ space_remaining = max_data_bytes - PTR_DIFF(p,pdata);
+ }
+
+ /* Check if we can close the dirptr */
+ if(close_after_request || (finished && close_if_end)) {
+ DEBUG(5,("call_trans2findnext: closing dptr_num = %d\n", dptr_num));
+ dptr_close(&dptr_num); /* This frees up the saved mask */
+ }
+
+ /* Set up the return parameter block */
+ SSVAL(params,0,numentries);
+ SSVAL(params,2,finished);
+ SSVAL(params,4,0); /* Never an EA error */
+ SSVAL(params,6,last_name_off);
+
+ send_trans2_replies( outbuf, bufsize, params, 8, pdata, PTR_DIFF(p,pdata));
+
+ if ((! *directory) && dptr_path(dptr_num))
+ slprintf(directory,sizeof(directory)-1, "(%s)",dptr_path(dptr_num));
+
+ DEBUG( 3, ( "%s mask=%s directory=%s dirtype=%d numentries=%d\n",
+ smb_fn_name(CVAL(inbuf,smb_com)),
+ mask, directory, dirtype, numentries ) );
+
+ return(-1);
+}
+
+/****************************************************************************
+ Reply to a TRANS2_QFSINFO (query filesystem info).
+****************************************************************************/
+
+static int call_trans2qfsinfo(connection_struct *conn, char *inbuf, char *outbuf,
+ int length, int bufsize,
+ char **pparams, int total_params, char **ppdata, int total_data)
+{
+ int max_data_bytes = SVAL(inbuf, smb_mdrcnt);
+ char *pdata = *ppdata;
+ char *params = *pparams;
+ uint16 info_level = SVAL(params,0);
+ int data_len, len;
+ SMB_STRUCT_STAT st;
+ char *vname = volume_label(SNUM(conn));
+ int snum = SNUM(conn);
+ char *fstype = lp_fstype(SNUM(conn));
+ int quota_flag = 0;
+
+ DEBUG(3,("call_trans2qfsinfo: level = %d\n", info_level));
+
+ if(SMB_VFS_STAT(conn,".",&st)!=0) {
+ DEBUG(2,("call_trans2qfsinfo: stat of . failed (%s)\n", strerror(errno)));
+ return ERROR_DOS(ERRSRV,ERRinvdevice);
+ }
+
+ pdata = Realloc(*ppdata, max_data_bytes + 1024);
+ if ( pdata == NULL )
+ return ERROR_DOS(ERRDOS,ERRnomem);
+
+ *ppdata = pdata;
+ memset((char *)pdata,'\0',max_data_bytes + 1024);
+
+ switch (info_level) {
+ case SMB_INFO_ALLOCATION:
+ {
+ SMB_BIG_UINT dfree,dsize,bsize,block_size,sectors_per_unit,bytes_per_sector;
+ data_len = 18;
+ SMB_VFS_DISK_FREE(conn,".",False,&bsize,&dfree,&dsize);
+ block_size = lp_block_size(snum);
+ if (bsize < block_size) {
+ SMB_BIG_UINT factor = block_size/bsize;
+ bsize = block_size;
+ dsize /= factor;
+ dfree /= factor;
+ }
+ if (bsize > block_size) {
+ SMB_BIG_UINT factor = bsize/block_size;
+ bsize = block_size;
+ dsize *= factor;
+ dfree *= factor;
+ }
+ bytes_per_sector = 512;
+ sectors_per_unit = bsize/bytes_per_sector;
+
+ DEBUG(5,("call_trans2qfsinfo : SMB_INFO_ALLOCATION id=%x, bsize=%u, cSectorUnit=%u, \
+cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)st.st_dev, (unsigned int)bsize, (unsigned int)sectors_per_unit,
+ (unsigned int)bytes_per_sector, (unsigned int)dsize, (unsigned int)dfree));
+
+ SIVAL(pdata,l1_idFileSystem,st.st_dev);
+ SIVAL(pdata,l1_cSectorUnit,sectors_per_unit);
+ SIVAL(pdata,l1_cUnit,dsize);
+ SIVAL(pdata,l1_cUnitAvail,dfree);
+ SSVAL(pdata,l1_cbSector,bytes_per_sector);
+ break;
+ }
+
+ case SMB_INFO_VOLUME:
+ /* Return volume name */
+ /*
+ * Add volume serial number - hash of a combination of
+ * the called hostname and the service name.
+ */
+ SIVAL(pdata,0,str_checksum(lp_servicename(snum)) ^ (str_checksum(local_machine)<<16) );
+ len = srvstr_push(outbuf, pdata+l2_vol_szVolLabel, vname, -1, STR_NOALIGN);
+ SCVAL(pdata,l2_vol_cch,len);
+ data_len = l2_vol_szVolLabel + len;
+ DEBUG(5,("call_trans2qfsinfo : time = %x, namelen = %d, name = %s\n",
+ (unsigned)st.st_ctime, len, vname));
+ break;
+
+ case SMB_QUERY_FS_ATTRIBUTE_INFO:
+ case SMB_FS_ATTRIBUTE_INFORMATION:
+
+
+#if defined(HAVE_SYS_QUOTAS)
+ quota_flag = FILE_VOLUME_QUOTAS;
+#endif
+
+ SIVAL(pdata,0,FILE_CASE_PRESERVED_NAMES|FILE_CASE_SENSITIVE_SEARCH|
+ (lp_nt_acl_support(SNUM(conn)) ? FILE_PERSISTENT_ACLS : 0)|
+ quota_flag); /* FS ATTRIBUTES */
+
+ SIVAL(pdata,4,255); /* Max filename component length */
+ /* NOTE! the fstype must *not* be null terminated or win98 won't recognise it
+ and will think we can't do long filenames */
+ len = srvstr_push(outbuf, pdata+12, fstype, -1, STR_UNICODE);
+ SIVAL(pdata,8,len);
+ data_len = 12 + len;
+ break;
+
+ case SMB_QUERY_FS_LABEL_INFO:
+ case SMB_FS_LABEL_INFORMATION:
+ len = srvstr_push(outbuf, pdata+4, vname, -1, 0);
+ data_len = 4 + len;
+ SIVAL(pdata,0,len);
+ break;
+
+ case SMB_QUERY_FS_VOLUME_INFO:
+ case SMB_FS_VOLUME_INFORMATION:
+
+ /*
+ * Add volume serial number - hash of a combination of
+ * the called hostname and the service name.
+ */
+ SIVAL(pdata,8,str_checksum(lp_servicename(snum)) ^
+ (str_checksum(local_machine)<<16));
+
+ len = srvstr_push(outbuf, pdata+18, vname, -1, STR_UNICODE);
+ SIVAL(pdata,12,len);
+ data_len = 18+len;
+ DEBUG(5,("call_trans2qfsinfo : SMB_QUERY_FS_VOLUME_INFO namelen = %d, vol=%s serv=%s\n",
+ (int)strlen(vname),vname, lp_servicename(snum)));
+ break;
+
+ case SMB_QUERY_FS_SIZE_INFO:
+ case SMB_FS_SIZE_INFORMATION:
+ {
+ SMB_BIG_UINT dfree,dsize,bsize,block_size,sectors_per_unit,bytes_per_sector;
+ data_len = 24;
+ SMB_VFS_DISK_FREE(conn,".",False,&bsize,&dfree,&dsize);
+ block_size = lp_block_size(snum);
+ if (bsize < block_size) {
+ SMB_BIG_UINT factor = block_size/bsize;
+ bsize = block_size;
+ dsize /= factor;
+ dfree /= factor;
+ }
+ if (bsize > block_size) {
+ SMB_BIG_UINT factor = bsize/block_size;
+ bsize = block_size;
+ dsize *= factor;
+ dfree *= factor;
+ }
+ bytes_per_sector = 512;
+ sectors_per_unit = bsize/bytes_per_sector;
+ DEBUG(5,("call_trans2qfsinfo : SMB_QUERY_FS_SIZE_INFO bsize=%u, cSectorUnit=%u, \
+cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned int)sectors_per_unit,
+ (unsigned int)bytes_per_sector, (unsigned int)dsize, (unsigned int)dfree));
+ SBIG_UINT(pdata,0,dsize);
+ SBIG_UINT(pdata,8,dfree);
+ SIVAL(pdata,16,sectors_per_unit);
+ SIVAL(pdata,20,bytes_per_sector);
+ break;
+ }
+
+ case SMB_FS_FULL_SIZE_INFORMATION:
+ {
+ SMB_BIG_UINT dfree,dsize,bsize,block_size,sectors_per_unit,bytes_per_sector;
+ data_len = 32;
+ SMB_VFS_DISK_FREE(conn,".",False,&bsize,&dfree,&dsize);
+ block_size = lp_block_size(snum);
+ if (bsize < block_size) {
+ SMB_BIG_UINT factor = block_size/bsize;
+ bsize = block_size;
+ dsize /= factor;
+ dfree /= factor;
+ }
+ if (bsize > block_size) {
+ SMB_BIG_UINT factor = bsize/block_size;
+ bsize = block_size;
+ dsize *= factor;
+ dfree *= factor;
+ }
+ bytes_per_sector = 512;
+ sectors_per_unit = bsize/bytes_per_sector;
+ DEBUG(5,("call_trans2qfsinfo : SMB_QUERY_FS_FULL_SIZE_INFO bsize=%u, cSectorUnit=%u, \
+cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned int)sectors_per_unit,
+ (unsigned int)bytes_per_sector, (unsigned int)dsize, (unsigned int)dfree));
+ SBIG_UINT(pdata,0,dsize); /* Total Allocation units. */
+ SBIG_UINT(pdata,8,dfree); /* Caller available allocation units. */
+ SBIG_UINT(pdata,16,dfree); /* Actual available allocation units. */
+ SIVAL(pdata,24,sectors_per_unit); /* Sectors per allocation unit. */
+ SIVAL(pdata,28,bytes_per_sector); /* Bytes per sector. */
+ break;
+ }
+
+ case SMB_QUERY_FS_DEVICE_INFO:
+ case SMB_FS_DEVICE_INFORMATION:
+ data_len = 8;
+ SIVAL(pdata,0,0); /* dev type */
+ SIVAL(pdata,4,0); /* characteristics */
+ break;
+
+#ifdef HAVE_SYS_QUOTAS
+ case SMB_FS_QUOTA_INFORMATION:
+ /*
+ * what we have to send --metze:
+ *
+ * Unknown1: 24 NULL bytes
+ * Soft Quota Treshold: 8 bytes seems like SMB_BIG_UINT or so
+ * Hard Quota Limit: 8 bytes seems like SMB_BIG_UINT or so
+ * Quota Flags: 2 byte :
+ * Unknown3: 6 NULL bytes
+ *
+ * 48 bytes total
+ *
+ * details for Quota Flags:
+ *
+ * 0x0020 Log Limit: log if the user exceeds his Hard Quota
+ * 0x0010 Log Warn: log if the user exceeds his Soft Quota
+ * 0x0002 Deny Disk: deny disk access when the user exceeds his Hard Quota
+ * 0x0001 Enable Quotas: enable quota for this fs
+ *
+ */
+ {
+ /* we need to fake up a fsp here,
+ * because its not send in this call
+ */
+ files_struct fsp;
+ SMB_NTQUOTA_STRUCT quotas;
+
+ ZERO_STRUCT(fsp);
+ ZERO_STRUCT(quotas);
+
+ fsp.conn = conn;
+ fsp.fnum = -1;
+ fsp.fd = -1;
+
+ /* access check */
+ if (conn->admin_user != True) {
+ DEBUG(0,("set_user_quota: access_denied service [%s] user [%s]\n",
+ lp_servicename(SNUM(conn)),conn->user));
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ if (vfs_get_ntquota(&fsp, SMB_USER_FS_QUOTA_TYPE, NULL, &quotas)!=0) {
+ DEBUG(0,("vfs_get_ntquota() failed for service [%s]\n",lp_servicename(SNUM(conn))));
+ return ERROR_DOS(ERRSRV,ERRerror);
+ }
+
+ data_len = 48;
+
+ DEBUG(10,("SMB_FS_QUOTA_INFORMATION: for service [%s]\n",lp_servicename(SNUM(conn))));
+
+ /* Unknown1 24 NULL bytes*/
+ SBIG_UINT(pdata,0,(SMB_BIG_UINT)0);
+ SBIG_UINT(pdata,8,(SMB_BIG_UINT)0);
+ SBIG_UINT(pdata,16,(SMB_BIG_UINT)0);
+
+ /* Default Soft Quota 8 bytes */
+ SBIG_UINT(pdata,24,quotas.softlim);
+
+ /* Default Hard Quota 8 bytes */
+ SBIG_UINT(pdata,32,quotas.hardlim);
+
+ /* Quota flag 2 bytes */
+ SSVAL(pdata,40,quotas.qflags);
+
+ /* Unknown3 6 NULL bytes */
+ SSVAL(pdata,42,0);
+ SIVAL(pdata,44,0);
+
+ break;
+ }
+#endif /* HAVE_SYS_QUOTAS */
+ case SMB_FS_OBJECTID_INFORMATION:
+ data_len = 64;
+ break;
+
+ /*
+ * Query the version and capabilities of the CIFS UNIX extensions
+ * in use.
+ */
+
+ case SMB_QUERY_CIFS_UNIX_INFO:
+ if (!lp_unix_extensions())
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ data_len = 12;
+ SSVAL(pdata,0,CIFS_UNIX_MAJOR_VERSION);
+ SSVAL(pdata,2,CIFS_UNIX_MINOR_VERSION);
+ SBIG_UINT(pdata,4,((SMB_BIG_UINT)0)); /* No capabilities for now... */
+ break;
+
+ case SMB_MAC_QUERY_FS_INFO:
+ /*
+ * Thursby MAC extension... ONLY on NTFS filesystems
+ * once we do streams then we don't need this
+ */
+ if (strequal(lp_fstype(SNUM(conn)),"NTFS")) {
+ data_len = 88;
+ SIVAL(pdata,84,0x100); /* Don't support mac... */
+ break;
+ }
+ /* drop through */
+ default:
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+
+
+ send_trans2_replies( outbuf, bufsize, params, 0, pdata, data_len);
+
+ DEBUG( 4, ( "%s info_level = %d\n", smb_fn_name(CVAL(inbuf,smb_com)), info_level) );
+
+ return -1;
+}
+
+#ifdef HAVE_SYS_QUOTAS
+/****************************************************************************
+ Reply to a TRANS2_SETFSINFO (set filesystem info).
+****************************************************************************/
+
+static int call_trans2setfsinfo(connection_struct *conn,
+ char *inbuf, char *outbuf, int length, int bufsize,
+ char **pparams, int total_params, char **ppdata, int total_data)
+{
+ char *pdata = *ppdata;
+ char *params = *pparams;
+ files_struct *fsp = NULL;
+ uint16 info_level;
+ int outsize;
+ SMB_NTQUOTA_STRUCT quotas;
+
+ ZERO_STRUCT(quotas);
+
+ DEBUG(10,("call_trans2setfsinfo: SET_FS_QUOTA: for service [%s]\n",lp_servicename(SNUM(conn))));
+
+ /* access check */
+ if ((conn->admin_user != True)||!CAN_WRITE(conn)) {
+ DEBUG(0,("set_user_quota: access_denied service [%s] user [%s]\n",
+ lp_servicename(SNUM(conn)),conn->user));
+ return ERROR_DOS(ERRSRV,ERRaccess);
+ }
+
+ /* */
+ if (total_params < 4) {
+ DEBUG(0,("call_trans2setfsinfo: requires total_params(%d) >= 4 bytes!\n",
+ total_params));
+ return ERROR_DOS(ERRDOS,ERRinvalidparam);
+ }
+
+ fsp = file_fsp(params,0);
+
+ if (!CHECK_NTQUOTA_HANDLE_OK(fsp,conn)) {
+ DEBUG(3,("TRANSACT_GET_USER_QUOTA: no valid QUOTA HANDLE\n"));
+ return ERROR_NT(NT_STATUS_INVALID_HANDLE);
+ }
+
+ info_level = SVAL(params,2);
+
+ switch(info_level) {
+ case SMB_FS_QUOTA_INFORMATION:
+ /* note: normaly there're 48 bytes,
+ * but we didn't use the last 6 bytes for now
+ * --metze
+ */
+ if (total_data < 42) {
+ DEBUG(0,("call_trans2setfsinfo: SET_FS_QUOTA: requires total_data(%d) >= 42 bytes!\n",
+ total_data));
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+
+ /* unknown_1 24 NULL bytes in pdata*/
+
+ /* the soft quotas 8 bytes (SMB_BIG_UINT)*/
+ quotas.softlim = (SMB_BIG_UINT)IVAL(pdata,24);
+#ifdef LARGE_SMB_OFF_T
+ quotas.softlim |= (((SMB_BIG_UINT)IVAL(pdata,28)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if ((IVAL(pdata,28) != 0)&&
+ ((quotas.softlim != 0xFFFFFFFF)||
+ (IVAL(pdata,28)!=0xFFFFFFFF))) {
+ /* more than 32 bits? */
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+#endif /* LARGE_SMB_OFF_T */
+
+ /* the hard quotas 8 bytes (SMB_BIG_UINT)*/
+ quotas.hardlim = (SMB_BIG_UINT)IVAL(pdata,32);
+#ifdef LARGE_SMB_OFF_T
+ quotas.hardlim |= (((SMB_BIG_UINT)IVAL(pdata,36)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if ((IVAL(pdata,36) != 0)&&
+ ((quotas.hardlim != 0xFFFFFFFF)||
+ (IVAL(pdata,36)!=0xFFFFFFFF))) {
+ /* more than 32 bits? */
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+#endif /* LARGE_SMB_OFF_T */
+
+ /* quota_flags 2 bytes **/
+ quotas.qflags = SVAL(pdata,40);
+
+ /* unknown_2 6 NULL bytes follow*/
+
+ /* now set the quotas */
+ if (vfs_set_ntquota(fsp, SMB_USER_FS_QUOTA_TYPE, NULL, &quotas)!=0) {
+ DEBUG(0,("vfs_set_ntquota() failed for service [%s]\n",lp_servicename(SNUM(conn))));
+ return ERROR_DOS(ERRSRV,ERRerror);
+ }
+
+ break;
+ default:
+ DEBUG(3,("call_trans2setfsinfo: unknown level (0x%X) not implemented yet.\n",
+ info_level));
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ break;
+ }
+
+ /*
+ * sending this reply works fine,
+ * but I'm not sure it's the same
+ * like windows do...
+ * --metze
+ */
+ outsize = set_message(outbuf,10,0,True);
+
+ return outsize;
+}
+#endif /* HAVE_SYS_QUOTAS */
+
+/****************************************************************************
+ * Utility function to set bad path error.
+ ****************************************************************************/
+
+int set_bad_path_error(int err, BOOL bad_path, char *outbuf, int def_class, uint32 def_code)
+{
+ DEBUG(10,("set_bad_path_error: err = %d bad_path = %d\n",
+ err, (int)bad_path ));
+
+ if(err == ENOENT) {
+ if (bad_path) {
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ } else {
+ return ERROR_NT(NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ }
+ }
+ return UNIXERROR(def_class,def_code);
+}
+
+/****************************************************************************
+ Reply to a TRANS2_QFILEPATHINFO or TRANSACT2_QFILEINFO (query file info by
+ file name or file id).
+****************************************************************************/
+
+static int call_trans2qfilepathinfo(connection_struct *conn,
+ char *inbuf, char *outbuf, int length,
+ int bufsize,
+ char **pparams, int total_params, char **ppdata, int total_data)
+{
+ int max_data_bytes = SVAL(inbuf, smb_mdrcnt);
+ char *params = *pparams;
+ char *pdata = *ppdata;
+ uint16 tran_call = SVAL(inbuf, smb_setup0);
+ uint16 info_level;
+ int mode=0;
+ SMB_OFF_T file_size=0;
+ SMB_BIG_UINT allocation_size=0;
+ unsigned int data_size;
+ unsigned int param_size = 2;
+ SMB_STRUCT_STAT sbuf;
+ pstring fname, dos_fname;
+ char *fullpathname;
+ char *base_name;
+ char *p;
+ SMB_OFF_T pos = 0;
+ BOOL bad_path = False;
+ BOOL delete_pending = False;
+ int len;
+ time_t c_time;
+ files_struct *fsp = NULL;
+ uint32 desired_access = 0x12019F; /* Default - GENERIC_EXECUTE mapping from Windows */
+
+ if (!params)
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+
+ if (tran_call == TRANSACT2_QFILEINFO) {
+ if (total_params < 4)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ fsp = file_fsp(params,0);
+ info_level = SVAL(params,2);
+
+ DEBUG(3,("call_trans2qfilepathinfo: TRANSACT2_QFILEINFO: level = %d\n", info_level));
+
+ if(fsp && (fsp->fake_file_handle)) {
+ /*
+ * This is actually for the QUOTA_FAKE_FILE --metze
+ */
+
+ pstrcpy(fname, fsp->fsp_name);
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+ if (!check_name(fname,conn)) {
+ DEBUG(3,("call_trans2qfilepathinfo: fileinfo of %s failed for fake_file(%s)\n",fname,strerror(errno)));
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
+ }
+
+ } else if(fsp && (fsp->is_directory || fsp->fd == -1)) {
+ /*
+ * This is actually a QFILEINFO on a directory
+ * handle (returned from an NT SMB). NT5.0 seems
+ * to do this call. JRA.
+ */
+ pstrcpy(fname, fsp->fsp_name);
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+ if (!check_name(fname,conn)) {
+ DEBUG(3,("call_trans2qfilepathinfo: fileinfo of %s failed (%s)\n",fname,strerror(errno)));
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
+ }
+
+ if (INFO_LEVEL_IS_UNIX(info_level)) {
+ /* Always do lstat for UNIX calls. */
+ if (SMB_VFS_LSTAT(conn,fname,&sbuf)) {
+ DEBUG(3,("call_trans2qfilepathinfo: SMB_VFS_LSTAT of %s failed (%s)\n",fname,strerror(errno)));
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
+ }
+ } else if (!VALID_STAT(sbuf) && SMB_VFS_STAT(conn,fname,&sbuf)) {
+ DEBUG(3,("call_trans2qfilepathinfo: SMB_VFS_STAT of %s failed (%s)\n",fname,strerror(errno)));
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
+ }
+
+ delete_pending = fsp->directory_delete_on_close;
+ } else {
+ /*
+ * Original code - this is an open file.
+ */
+ CHECK_FSP(fsp,conn);
+
+ pstrcpy(fname, fsp->fsp_name);
+ if (SMB_VFS_FSTAT(fsp,fsp->fd,&sbuf) != 0) {
+ DEBUG(3,("fstat of fnum %d failed (%s)\n", fsp->fnum, strerror(errno)));
+ return(UNIXERROR(ERRDOS,ERRbadfid));
+ }
+ pos = fsp->position_information;
+ delete_pending = fsp->delete_on_close;
+ desired_access = fsp->desired_access;
+ }
+ } else {
+ NTSTATUS status = NT_STATUS_OK;
+
+ /* qpathinfo */
+ if (total_params < 6)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ info_level = SVAL(params,0);
+
+ DEBUG(3,("call_trans2qfilepathinfo: TRANSACT2_QPATHINFO: level = %d\n", info_level));
+
+ srvstr_get_path(inbuf, fname, &params[6], sizeof(fname), -1, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+
+ RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
+
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+ if (!check_name(fname,conn)) {
+ DEBUG(3,("call_trans2qfilepathinfo: fileinfo of %s failed (%s)\n",fname,strerror(errno)));
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
+ }
+
+ if (INFO_LEVEL_IS_UNIX(info_level)) {
+ /* Always do lstat for UNIX calls. */
+ if (SMB_VFS_LSTAT(conn,fname,&sbuf)) {
+ DEBUG(3,("call_trans2qfilepathinfo: SMB_VFS_LSTAT of %s failed (%s)\n",fname,strerror(errno)));
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
+ }
+ } else if (!VALID_STAT(sbuf) && SMB_VFS_STAT(conn,fname,&sbuf) && (info_level != SMB_INFO_IS_NAME_VALID)) {
+ DEBUG(3,("call_trans2qfilepathinfo: SMB_VFS_STAT of %s failed (%s)\n",fname,strerror(errno)));
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
+ }
+ }
+
+ if (INFO_LEVEL_IS_UNIX(info_level) && !lp_unix_extensions())
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+
+ DEBUG(3,("call_trans2qfilepathinfo %s (fnum = %d) level=%d call=%d total_data=%d\n",
+ fname,fsp ? fsp->fnum : -1, info_level,tran_call,total_data));
+
+ p = strrchr_m(fname,'/');
+ if (!p)
+ base_name = fname;
+ else
+ base_name = p+1;
+
+ mode = dos_mode(conn,fname,&sbuf);
+ if (!mode)
+ mode = FILE_ATTRIBUTE_NORMAL;
+
+ fullpathname = fname;
+ file_size = get_file_size(sbuf);
+ allocation_size = get_allocation_size(fsp,&sbuf);
+ if (mode & aDIR)
+ file_size = 0;
+
+ params = Realloc(*pparams,2);
+ if (params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ *pparams = params;
+ memset((char *)params,'\0',2);
+ data_size = max_data_bytes + 1024;
+ pdata = Realloc(*ppdata, data_size);
+ if ( pdata == NULL )
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ *ppdata = pdata;
+
+ if (total_data > 0 && IVAL(pdata,0) == total_data) {
+ /* uggh, EAs for OS2 */
+ DEBUG(4,("Rejecting EA request with total_data=%d\n",total_data));
+ return ERROR_DOS(ERRDOS,ERReasnotsupported);
+ }
+
+ memset((char *)pdata,'\0',data_size);
+
+ c_time = get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn)));
+
+ if (lp_dos_filetime_resolution(SNUM(conn))) {
+ c_time &= ~1;
+ sbuf.st_atime &= ~1;
+ sbuf.st_mtime &= ~1;
+ sbuf.st_mtime &= ~1;
+ }
+
+ /* NT expects the name to be in an exact form of the *full*
+ filename. See the trans2 torture test */
+ if (strequal(base_name,".")) {
+ pstrcpy(dos_fname, "\\");
+ } else {
+ pstr_sprintf(dos_fname, "\\%s", fname);
+ string_replace(dos_fname, '/', '\\');
+ }
+
+ switch (info_level) {
+ case SMB_INFO_STANDARD:
+ case SMB_INFO_QUERY_EA_SIZE:
+ data_size = (info_level==1?22:26);
+ put_dos_date2(pdata,l1_fdateCreation,c_time);
+ put_dos_date2(pdata,l1_fdateLastAccess,sbuf.st_atime);
+ put_dos_date2(pdata,l1_fdateLastWrite,sbuf.st_mtime); /* write time */
+ SIVAL(pdata,l1_cbFile,(uint32)file_size);
+ SIVAL(pdata,l1_cbFileAlloc,(uint32)allocation_size);
+ SSVAL(pdata,l1_attrFile,mode);
+ SIVAL(pdata,l1_attrFile+2,0); /* this is what win2003 does */
+ break;
+
+ case SMB_INFO_IS_NAME_VALID:
+ if (tran_call == TRANSACT2_QFILEINFO) {
+ /* os/2 needs this ? really ?*/
+ return ERROR_DOS(ERRDOS,ERRbadfunc);
+ }
+ data_size = 0;
+ param_size = 0;
+ break;
+
+ case SMB_INFO_QUERY_EAS_FROM_LIST:
+ data_size = 24;
+ put_dos_date2(pdata,0,c_time);
+ put_dos_date2(pdata,4,sbuf.st_atime);
+ put_dos_date2(pdata,8,sbuf.st_mtime);
+ SIVAL(pdata,12,(uint32)file_size);
+ SIVAL(pdata,16,(uint32)allocation_size);
+ SIVAL(pdata,20,mode);
+ break;
+
+ case SMB_INFO_QUERY_ALL_EAS:
+ /* We have data_size bytes to put EA's into. */
+ data_size = fill_ea_buffer(pdata, data_size, conn, fsp, fname);
+ break;
+
+ case SMB_FILE_BASIC_INFORMATION:
+ case SMB_QUERY_FILE_BASIC_INFO:
+
+ if (info_level == SMB_QUERY_FILE_BASIC_INFO)
+ data_size = 36; /* w95 returns 40 bytes not 36 - why ?. */
+ else {
+ data_size = 40;
+ SIVAL(pdata,36,0);
+ }
+ put_long_date(pdata,c_time);
+ put_long_date(pdata+8,sbuf.st_atime);
+ put_long_date(pdata+16,sbuf.st_mtime); /* write time */
+ put_long_date(pdata+24,sbuf.st_mtime); /* change time */
+ SIVAL(pdata,32,mode);
+
+ DEBUG(5,("SMB_QFBI - "));
+ {
+ time_t create_time = c_time;
+ DEBUG(5,("create: %s ", ctime(&create_time)));
+ }
+ DEBUG(5,("access: %s ", ctime(&sbuf.st_atime)));
+ DEBUG(5,("write: %s ", ctime(&sbuf.st_mtime)));
+ DEBUG(5,("change: %s ", ctime(&sbuf.st_mtime)));
+ DEBUG(5,("mode: %x\n", mode));
+
+ break;
+
+ case SMB_FILE_STANDARD_INFORMATION:
+ case SMB_QUERY_FILE_STANDARD_INFO:
+
+ data_size = 24;
+ SOFF_T(pdata,0,allocation_size);
+ SOFF_T(pdata,8,file_size);
+ if (delete_pending & sbuf.st_nlink)
+ SIVAL(pdata,16,sbuf.st_nlink - 1);
+ else
+ SIVAL(pdata,16,sbuf.st_nlink);
+ SCVAL(pdata,20,0);
+ SCVAL(pdata,21,(mode&aDIR)?1:0);
+ break;
+
+ case SMB_FILE_EA_INFORMATION:
+ case SMB_QUERY_FILE_EA_INFO:
+ {
+ unsigned int ea_size = estimate_ea_size(conn, fsp, fname);
+ data_size = 4;
+ SIVAL(pdata,0,ea_size);
+ break;
+ }
+
+ /* Get the 8.3 name - used if NT SMB was negotiated. */
+ case SMB_QUERY_FILE_ALT_NAME_INFO:
+ case SMB_FILE_ALTERNATE_NAME_INFORMATION:
+ {
+ pstring short_name;
+
+ pstrcpy(short_name,base_name);
+ /* Mangle if not already 8.3 */
+ if(!mangle_is_8_3(short_name, True)) {
+ mangle_map(short_name,True,True,SNUM(conn));
+ }
+ len = srvstr_push(outbuf, pdata+4, short_name, -1, STR_UNICODE);
+ data_size = 4 + len;
+ SIVAL(pdata,0,len);
+ break;
+ }
+
+ case SMB_QUERY_FILE_NAME_INFO:
+ /*
+ this must be *exactly* right for ACLs on mapped drives to work
+ */
+ len = srvstr_push(outbuf, pdata+4, dos_fname, -1, STR_UNICODE);
+ data_size = 4 + len;
+ SIVAL(pdata,0,len);
+ break;
+
+ case SMB_FILE_ALLOCATION_INFORMATION:
+ case SMB_QUERY_FILE_ALLOCATION_INFO:
+ data_size = 8;
+ SOFF_T(pdata,0,allocation_size);
+ break;
+
+ case SMB_FILE_END_OF_FILE_INFORMATION:
+ case SMB_QUERY_FILE_END_OF_FILEINFO:
+ data_size = 8;
+ SOFF_T(pdata,0,file_size);
+ break;
+
+ case SMB_QUERY_FILE_ALL_INFO:
+ case SMB_FILE_ALL_INFORMATION:
+ put_long_date(pdata,c_time);
+ put_long_date(pdata+8,sbuf.st_atime);
+ put_long_date(pdata+16,sbuf.st_mtime); /* write time */
+ put_long_date(pdata+24,sbuf.st_mtime); /* change time */
+ SIVAL(pdata,32,mode);
+ pdata += 40;
+ SOFF_T(pdata,0,allocation_size);
+ SOFF_T(pdata,8,file_size);
+ if (delete_pending && sbuf.st_nlink)
+ SIVAL(pdata,16,sbuf.st_nlink - 1);
+ else
+ SIVAL(pdata,16,sbuf.st_nlink);
+ SCVAL(pdata,20,delete_pending);
+ SCVAL(pdata,21,(mode&aDIR)?1:0);
+ pdata += 24;
+ pdata += 4; /* EA info */
+ len = srvstr_push(outbuf, pdata+4, dos_fname, -1, STR_UNICODE);
+ SIVAL(pdata,0,len);
+ pdata += 4 + len;
+ data_size = PTR_DIFF(pdata,(*ppdata));
+ break;
+
+ case SMB_FILE_INTERNAL_INFORMATION:
+ /* This should be an index number - looks like
+ dev/ino to me :-)
+
+ I think this causes us to fail the IFSKIT
+ BasicFileInformationTest. -tpot */
+
+ SIVAL(pdata,0,sbuf.st_dev);
+ SIVAL(pdata,4,sbuf.st_ino);
+ data_size = 8;
+ break;
+
+ case SMB_FILE_ACCESS_INFORMATION:
+ SIVAL(pdata,0,desired_access);
+ data_size = 4;
+ break;
+
+ case SMB_FILE_NAME_INFORMATION:
+ /* Pathname with leading '\'. */
+ {
+ size_t byte_len;
+ byte_len = dos_PutUniCode(pdata+4,dos_fname,max_data_bytes,False);
+ SIVAL(pdata,0,byte_len);
+ data_size = 4 + byte_len;
+ break;
+ }
+
+ case SMB_FILE_DISPOSITION_INFORMATION:
+ data_size = 1;
+ SCVAL(pdata,0,delete_pending);
+ break;
+
+ case SMB_FILE_POSITION_INFORMATION:
+ data_size = 8;
+ SOFF_T(pdata,0,pos);
+ break;
+
+ case SMB_FILE_MODE_INFORMATION:
+ SIVAL(pdata,0,mode);
+ data_size = 4;
+ break;
+
+ case SMB_FILE_ALIGNMENT_INFORMATION:
+ SIVAL(pdata,0,0); /* No alignment needed. */
+ data_size = 4;
+ break;
+
+#if 0
+ /*
+ * NT4 server just returns "invalid query" to this - if we try to answer
+ * it then NTws gets a BSOD! (tridge).
+ * W2K seems to want this. JRA.
+ */
+ case SMB_QUERY_FILE_STREAM_INFO:
+#endif
+ case SMB_FILE_STREAM_INFORMATION:
+ if (mode & aDIR) {
+ data_size = 0;
+ } else {
+ size_t byte_len = dos_PutUniCode(pdata+24,"::$DATA", 0xE, False);
+ SIVAL(pdata,0,0); /* ??? */
+ SIVAL(pdata,4,byte_len); /* Byte length of unicode string ::$DATA */
+ SOFF_T(pdata,8,file_size);
+ SIVAL(pdata,16,allocation_size);
+ SIVAL(pdata,20,0); /* ??? */
+ data_size = 24 + byte_len;
+ }
+ break;
+
+ case SMB_QUERY_COMPRESSION_INFO:
+ case SMB_FILE_COMPRESSION_INFORMATION:
+ SOFF_T(pdata,0,file_size);
+ SIVAL(pdata,8,0); /* ??? */
+ SIVAL(pdata,12,0); /* ??? */
+ data_size = 16;
+ break;
+
+ case SMB_FILE_NETWORK_OPEN_INFORMATION:
+ put_long_date(pdata,c_time);
+ put_long_date(pdata+8,sbuf.st_atime);
+ put_long_date(pdata+16,sbuf.st_mtime); /* write time */
+ put_long_date(pdata+24,sbuf.st_mtime); /* change time */
+ SIVAL(pdata,32,allocation_size);
+ SOFF_T(pdata,40,file_size);
+ SIVAL(pdata,48,mode);
+ SIVAL(pdata,52,0); /* ??? */
+ data_size = 56;
+ break;
+
+ case SMB_FILE_ATTRIBUTE_TAG_INFORMATION:
+ SIVAL(pdata,0,mode);
+ SIVAL(pdata,4,0);
+ data_size = 8;
+ break;
+
+ /*
+ * CIFS UNIX Extensions.
+ */
+
+ case SMB_QUERY_FILE_UNIX_BASIC:
+
+ DEBUG(4,("call_trans2qfilepathinfo: st_mode=%o\n",(int)sbuf.st_mode));
+
+ SOFF_T(pdata,0,get_file_size(sbuf)); /* File size 64 Bit */
+ pdata += 8;
+
+ SOFF_T(pdata,0,get_allocation_size(fsp,&sbuf)); /* Number of bytes used on disk - 64 Bit */
+ pdata += 8;
+
+ put_long_date(pdata,sbuf.st_ctime); /* Creation Time 64 Bit */
+ put_long_date(pdata+8,sbuf.st_atime); /* Last access time 64 Bit */
+ put_long_date(pdata+16,sbuf.st_mtime); /* Last modification time 64 Bit */
+ pdata += 24;
+
+ SIVAL(pdata,0,sbuf.st_uid); /* user id for the owner */
+ SIVAL(pdata,4,0);
+ pdata += 8;
+
+ SIVAL(pdata,0,sbuf.st_gid); /* group id of owner */
+ SIVAL(pdata,4,0);
+ pdata += 8;
+
+ SIVAL(pdata,0,unix_filetype(sbuf.st_mode));
+ pdata += 4;
+
+ SIVAL(pdata,0,unix_dev_major(sbuf.st_rdev)); /* Major device number if type is device */
+ SIVAL(pdata,4,0);
+ pdata += 8;
+
+ SIVAL(pdata,0,unix_dev_minor(sbuf.st_rdev)); /* Minor device number if type is device */
+ SIVAL(pdata,4,0);
+ pdata += 8;
+
+ SINO_T(pdata,0,(SMB_INO_T)sbuf.st_ino); /* inode number */
+ pdata += 8;
+
+ SIVAL(pdata,0, unix_perms_to_wire(sbuf.st_mode)); /* Standard UNIX file permissions */
+ SIVAL(pdata,4,0);
+ pdata += 8;
+
+ SIVAL(pdata,0,sbuf.st_nlink); /* number of hard links */
+ SIVAL(pdata,4,0);
+ pdata += 8+1;
+ data_size = PTR_DIFF(pdata,(*ppdata));
+
+ {
+ int i;
+ DEBUG(4,("call_trans2qfilepathinfo: SMB_QUERY_FILE_UNIX_BASIC"));
+
+ for (i=0; i<100; i++)
+ DEBUG(4,("%d=%x, ",i, (*ppdata)[i]));
+ DEBUG(4,("\n"));
+ }
+
+ break;
+
+ case SMB_QUERY_FILE_UNIX_LINK:
+ {
+ pstring buffer;
+
+#ifdef S_ISLNK
+ if(!S_ISLNK(sbuf.st_mode))
+ return(UNIXERROR(ERRSRV,ERRbadlink));
+#else
+ return(UNIXERROR(ERRDOS,ERRbadlink));
+#endif
+ len = SMB_VFS_READLINK(conn,fullpathname, buffer, sizeof(pstring)-1); /* read link */
+ if (len == -1)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ buffer[len] = 0;
+ len = srvstr_push(outbuf, pdata, buffer, -1, STR_TERMINATE);
+ pdata += len;
+ data_size = PTR_DIFF(pdata,(*ppdata));
+
+ break;
+ }
+
+ default:
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+
+ send_trans2_replies(outbuf, bufsize, params, param_size, *ppdata, data_size);
+
+ return(-1);
+}
+
+/****************************************************************************
+ Deal with the internal needs of setting the delete on close flag. Note that
+ as the tdb locking is recursive, it is safe to call this from within
+ open_file_shared. JRA.
+****************************************************************************/
+
+NTSTATUS set_delete_on_close_internal(files_struct *fsp, BOOL delete_on_close)
+{
+ /*
+ * Only allow delete on close for writable shares.
+ */
+
+ if (delete_on_close && !CAN_WRITE(fsp->conn)) {
+ DEBUG(10,("set_delete_on_close_internal: file %s delete on close flag set but write access denied on share.\n",
+ fsp->fsp_name ));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ /*
+ * Only allow delete on close for files/directories opened with delete intent.
+ */
+
+ if (delete_on_close && !(fsp->desired_access & DELETE_ACCESS)) {
+ DEBUG(10,("set_delete_on_close_internal: file %s delete on close flag set but delete access denied.\n",
+ fsp->fsp_name ));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if(fsp->is_directory) {
+ fsp->directory_delete_on_close = delete_on_close;
+ DEBUG(10, ("set_delete_on_close_internal: %s delete on close flag for fnum = %d, directory %s\n",
+ delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name ));
+ } else {
+ fsp->delete_on_close = delete_on_close;
+ DEBUG(10, ("set_delete_on_close_internal: %s delete on close flag for fnum = %d, file %s\n",
+ delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name ));
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Sets the delete on close flag over all share modes on this file.
+ Modify the share mode entry for all files open
+ on this device and inode to tell other smbds we have
+ changed the delete on close flag. This will be noticed
+ in the close code, the last closer will delete the file
+ if flag is set.
+****************************************************************************/
+
+NTSTATUS set_delete_on_close_over_all(files_struct *fsp, BOOL delete_on_close)
+{
+ DEBUG(10,("set_delete_on_close_over_all: %s delete on close flag for fnum = %d, file %s\n",
+ delete_on_close ? "Adding" : "Removing", fsp->fnum, fsp->fsp_name ));
+
+ if (fsp->is_directory || fsp->is_stat)
+ return NT_STATUS_OK;
+
+ if (lock_share_entry_fsp(fsp) == False)
+ return NT_STATUS_ACCESS_DENIED;
+
+ if (!modify_delete_flag(fsp->dev, fsp->inode, delete_on_close)) {
+ DEBUG(0,("set_delete_on_close_internal: failed to change delete on close flag for file %s\n",
+ fsp->fsp_name ));
+ unlock_share_entry_fsp(fsp);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ unlock_share_entry_fsp(fsp);
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Returns true if this pathname is within the share, and thus safe.
+****************************************************************************/
+
+static int ensure_link_is_safe(connection_struct *conn, const char *link_dest_in, char *link_dest_out)
+{
+#ifdef PATH_MAX
+ char resolved_name[PATH_MAX+1];
+#else
+ pstring resolved_name;
+#endif
+ fstring last_component;
+ pstring link_dest;
+ pstring link_test;
+ char *p;
+ BOOL bad_path = False;
+ SMB_STRUCT_STAT sbuf;
+
+ pstrcpy(link_dest, link_dest_in);
+ unix_convert(link_dest,conn,0,&bad_path,&sbuf);
+
+ /* Store the UNIX converted path. */
+ pstrcpy(link_dest_out, link_dest);
+
+ p = strrchr(link_dest, '/');
+ if (p) {
+ fstrcpy(last_component, p+1);
+ *p = '\0';
+ } else {
+ fstrcpy(last_component, link_dest);
+ pstrcpy(link_dest, "./");
+ }
+
+ if (SMB_VFS_REALPATH(conn,link_dest,resolved_name) == NULL)
+ return -1;
+
+ pstrcpy(link_dest, resolved_name);
+ pstrcat(link_dest, "/");
+ pstrcat(link_dest, last_component);
+
+ if (*link_dest != '/') {
+ /* Relative path. */
+ pstrcpy(link_test, conn->connectpath);
+ pstrcat(link_test, "/");
+ pstrcat(link_test, link_dest);
+ } else {
+ pstrcpy(link_test, link_dest);
+ }
+
+ /*
+ * Check if the link is within the share.
+ */
+
+ if (strncmp(conn->connectpath, link_test, strlen(conn->connectpath))) {
+ errno = EACCES;
+ return -1;
+ }
+ return 0;
+}
+
+/****************************************************************************
+ Set a hard link (called by UNIX extensions and by NT rename with HARD link
+ code.
+****************************************************************************/
+
+NTSTATUS hardlink_internals(connection_struct *conn, char *name, char *newname)
+{
+ BOOL bad_path_src = False;
+ BOOL bad_path_dest = False;
+ SMB_STRUCT_STAT sbuf1, sbuf2;
+ BOOL rc, rcdest;
+ pstring last_component_src;
+ pstring last_component_dest;
+ NTSTATUS status = NT_STATUS_OK;
+
+ ZERO_STRUCT(sbuf1);
+ ZERO_STRUCT(sbuf2);
+
+ /* No wildcards. */
+ if (ms_has_wild(name) || ms_has_wild(newname)) {
+ return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+
+ rc = unix_convert(name,conn,last_component_src,&bad_path_src,&sbuf1);
+ if (!rc && bad_path_src) {
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+
+ /* Quick check for "." and ".." */
+ if (last_component_src[0] == '.') {
+ if (!last_component_src[1] || (last_component_src[1] == '.' && !last_component_src[2])) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ }
+
+ /* source must already exist. */
+ if (!VALID_STAT(sbuf1)) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ rcdest = unix_convert(newname,conn,last_component_dest,&bad_path_dest,&sbuf2);
+ if (!rcdest && bad_path_dest) {
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+
+ /* Quick check for "." and ".." */
+ if (last_component_dest[0] == '.') {
+ if (!last_component_dest[1] || (last_component_dest[1] == '.' && !last_component_dest[2])) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ }
+
+ /* Disallow if already exists. */
+ if (VALID_STAT(sbuf2)) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ /* No links from a directory. */
+ if (S_ISDIR(sbuf1.st_mode)) {
+ return NT_STATUS_FILE_IS_A_DIRECTORY;
+ }
+
+ if (ensure_link_is_safe(conn, newname, newname) != 0)
+ return NT_STATUS_ACCESS_DENIED;
+
+ DEBUG(10,("hardlink_internals: doing hard link %s -> %s\n", name, newname ));
+
+ if (SMB_VFS_LINK(conn,name,newname) != 0) {
+ status = map_nt_error_from_unix(errno);
+ DEBUG(3,("hardlink_internals: Error %s link %s -> %s\n",
+ nt_errstr(status), name,newname));
+ }
+
+ return status;
+}
+
+/****************************************************************************
+ Reply to a TRANS2_SETFILEINFO (set file info by fileid).
+****************************************************************************/
+
+static int call_trans2setfilepathinfo(connection_struct *conn,
+ char *inbuf, char *outbuf, int length, int bufsize,
+ char **pparams, int total_params, char **ppdata, int total_data)
+{
+ char *params = *pparams;
+ char *pdata = *ppdata;
+ uint16 tran_call = SVAL(inbuf, smb_setup0);
+ uint16 info_level;
+ int dosmode=0;
+ SMB_OFF_T size=0;
+ struct utimbuf tvs;
+ SMB_STRUCT_STAT sbuf;
+ pstring fname;
+ int fd = -1;
+ BOOL bad_path = False;
+ files_struct *fsp = NULL;
+ uid_t set_owner = (uid_t)SMB_UID_NO_CHANGE;
+ gid_t set_grp = (uid_t)SMB_GID_NO_CHANGE;
+ mode_t unixmode = 0;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (!params)
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+
+ if (tran_call == TRANSACT2_SETFILEINFO) {
+ if (total_params < 4)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ fsp = file_fsp(params,0);
+ info_level = SVAL(params,2);
+
+ if(fsp && (fsp->is_directory || fsp->fd == -1)) {
+ /*
+ * This is actually a SETFILEINFO on a directory
+ * handle (returned from an NT SMB). NT5.0 seems
+ * to do this call. JRA.
+ */
+ pstrcpy(fname, fsp->fsp_name);
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+ if (!check_name(fname,conn) || (!VALID_STAT(sbuf))) {
+ DEBUG(3,("call_trans2setfilepathinfo: fileinfo of %s failed (%s)\n",fname,strerror(errno)));
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
+ }
+ } else if (fsp && fsp->print_file) {
+ /*
+ * Doing a DELETE_ON_CLOSE should cancel a print job.
+ */
+ if ((info_level == SMB_SET_FILE_DISPOSITION_INFO) && CVAL(pdata,0)) {
+ fsp->share_mode = FILE_DELETE_ON_CLOSE;
+
+ DEBUG(3,("call_trans2setfilepathinfo: Cancelling print job (%s)\n", fsp->fsp_name ));
+
+ SSVAL(params,0,0);
+ send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
+ return(-1);
+ } else
+ return (UNIXERROR(ERRDOS,ERRbadpath));
+ } else {
+ /*
+ * Original code - this is an open file.
+ */
+ CHECK_FSP(fsp,conn);
+
+ pstrcpy(fname, fsp->fsp_name);
+ fd = fsp->fd;
+
+ if (SMB_VFS_FSTAT(fsp,fd,&sbuf) != 0) {
+ DEBUG(3,("call_trans2setfilepathinfo: fstat of fnum %d failed (%s)\n",fsp->fnum, strerror(errno)));
+ return(UNIXERROR(ERRDOS,ERRbadfid));
+ }
+ }
+ } else {
+ /* set path info */
+ if (total_params < 6)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ info_level = SVAL(params,0);
+ srvstr_get_path(inbuf, fname, &params[6], sizeof(fname), -1, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+
+ /*
+ * For CIFS UNIX extensions the target name may not exist.
+ */
+
+ if(!VALID_STAT(sbuf) && !INFO_LEVEL_IS_UNIX(info_level)) {
+ DEBUG(3,("call_trans2setfilepathinfo: stat of %s failed (%s)\n", fname, strerror(errno)));
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
+ }
+
+ if(!check_name(fname, conn)) {
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
+ }
+
+ }
+
+ if (!CAN_WRITE(conn))
+ return ERROR_DOS(ERRSRV,ERRaccess);
+
+ if (INFO_LEVEL_IS_UNIX(info_level) && !lp_unix_extensions())
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+
+ if (VALID_STAT(sbuf))
+ unixmode = sbuf.st_mode;
+
+ DEBUG(3,("call_trans2setfilepathinfo(%d) %s (fnum %d) info_level=%d totdata=%d\n",
+ tran_call,fname, fsp ? fsp->fnum : -1, info_level,total_data));
+
+ /* Realloc the parameter and data sizes */
+ params = Realloc(*pparams,2);
+ if(params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ *pparams = params;
+
+ SSVAL(params,0,0);
+
+ if (fsp) {
+ /* the pending modtime overrides the current modtime */
+ sbuf.st_mtime = fsp->pending_modtime;
+ }
+
+ size = get_file_size(sbuf);
+ tvs.modtime = sbuf.st_mtime;
+ tvs.actime = sbuf.st_atime;
+ dosmode = dos_mode(conn,fname,&sbuf);
+ unixmode = sbuf.st_mode;
+
+ set_owner = VALID_STAT(sbuf) ? sbuf.st_uid : (uid_t)SMB_UID_NO_CHANGE;
+ set_grp = VALID_STAT(sbuf) ? sbuf.st_gid : (gid_t)SMB_GID_NO_CHANGE;
+
+ switch (info_level) {
+ case SMB_INFO_STANDARD:
+ {
+ if (total_data < 12)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ /* access time */
+ tvs.actime = make_unix_date2(pdata+l1_fdateLastAccess);
+ /* write time */
+ tvs.modtime = make_unix_date2(pdata+l1_fdateLastWrite);
+ break;
+ }
+
+ case SMB_INFO_SET_EA:
+ status = set_ea(conn, fsp, fname, pdata, total_data);
+ if (NT_STATUS_V(status) != NT_STATUS_V(NT_STATUS_OK))
+ return ERROR_NT(status);
+ break;
+
+ /* XXXX um, i don't think this is right.
+ it's also not in the cifs6.txt spec.
+ */
+ case SMB_INFO_QUERY_EAS_FROM_LIST:
+ if (total_data < 28)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ tvs.actime = make_unix_date2(pdata+8);
+ tvs.modtime = make_unix_date2(pdata+12);
+ size = IVAL(pdata,16);
+ dosmode = IVAL(pdata,24);
+ break;
+
+ /* XXXX nor this. not in cifs6.txt, either. */
+ case SMB_INFO_QUERY_ALL_EAS:
+ if (total_data < 28)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ tvs.actime = make_unix_date2(pdata+8);
+ tvs.modtime = make_unix_date2(pdata+12);
+ size = IVAL(pdata,16);
+ dosmode = IVAL(pdata,24);
+ break;
+
+ case SMB_SET_FILE_BASIC_INFO:
+ case SMB_FILE_BASIC_INFORMATION:
+ {
+ /* Patch to do this correctly from Paul Eggert <eggert@twinsun.com>. */
+ time_t write_time;
+ time_t changed_time;
+
+ if (total_data < 36)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ /* Ignore create time at offset pdata. */
+
+ /* access time */
+ tvs.actime = interpret_long_date(pdata+8);
+
+ write_time = interpret_long_date(pdata+16);
+ changed_time = interpret_long_date(pdata+24);
+
+ tvs.modtime = MIN(write_time, changed_time);
+
+ if (write_time > tvs.modtime && write_time != 0xffffffff) {
+ tvs.modtime = write_time;
+ }
+ /* Prefer a defined time to an undefined one. */
+ if (tvs.modtime == (time_t)0 || tvs.modtime == (time_t)-1)
+ tvs.modtime = (write_time == (time_t)0 || write_time == (time_t)-1
+ ? changed_time : write_time);
+
+ /* attributes */
+ dosmode = IVAL(pdata,32);
+ break;
+ }
+
+ case SMB_FILE_ALLOCATION_INFORMATION:
+ case SMB_SET_FILE_ALLOCATION_INFO:
+ {
+ int ret = -1;
+ SMB_BIG_UINT allocation_size;
+
+ if (total_data < 8)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ allocation_size = (SMB_BIG_UINT)IVAL(pdata,0);
+#ifdef LARGE_SMB_OFF_T
+ allocation_size |= (((SMB_BIG_UINT)IVAL(pdata,4)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if (IVAL(pdata,4) != 0) /* more than 32 bits? */
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+#endif /* LARGE_SMB_OFF_T */
+ DEBUG(10,("call_trans2setfilepathinfo: Set file allocation info for file %s to %.0f\n",
+ fname, (double)allocation_size ));
+
+ if (allocation_size)
+ allocation_size = SMB_ROUNDUP(allocation_size,SMB_ROUNDUP_ALLOCATION_SIZE);
+
+ if(allocation_size != get_file_size(sbuf)) {
+ SMB_STRUCT_STAT new_sbuf;
+
+ DEBUG(10,("call_trans2setfilepathinfo: file %s : setting new allocation size to %.0f\n",
+ fname, (double)allocation_size ));
+
+ if (fd == -1) {
+ files_struct *new_fsp = NULL;
+ int access_mode = 0;
+ int action = 0;
+
+ if(global_oplock_break) {
+ /* Queue this file modify as we are the process of an oplock break. */
+
+ DEBUG(2,("call_trans2setfilepathinfo: queueing message due to being "));
+ DEBUGADD(2,( "in oplock break state.\n"));
+
+ push_oplock_pending_smb_message(inbuf, length);
+ return -1;
+ }
+
+ new_fsp = open_file_shared1(conn, fname, &sbuf,FILE_WRITE_DATA,
+ SET_OPEN_MODE(DOS_OPEN_RDWR),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
+ FILE_ATTRIBUTE_NORMAL,
+ 0, &access_mode, &action);
+
+ if (new_fsp == NULL)
+ return(UNIXERROR(ERRDOS,ERRbadpath));
+ ret = vfs_allocate_file_space(new_fsp, allocation_size);
+ if (SMB_VFS_FSTAT(new_fsp,new_fsp->fd,&new_sbuf) != 0) {
+ DEBUG(3,("call_trans2setfilepathinfo: fstat of fnum %d failed (%s)\n",
+ new_fsp->fnum, strerror(errno)));
+ ret = -1;
+ }
+ close_file(new_fsp,True);
+ } else {
+ ret = vfs_allocate_file_space(fsp, allocation_size);
+ if (SMB_VFS_FSTAT(fsp,fd,&new_sbuf) != 0) {
+ DEBUG(3,("call_trans2setfilepathinfo: fstat of fnum %d failed (%s)\n",
+ fsp->fnum, strerror(errno)));
+ ret = -1;
+ }
+ }
+ if (ret == -1)
+ return ERROR_NT(NT_STATUS_DISK_FULL);
+
+ /* Allocate can truncate size... */
+ size = get_file_size(new_sbuf);
+ }
+
+ break;
+ }
+
+ case SMB_FILE_END_OF_FILE_INFORMATION:
+ case SMB_SET_FILE_END_OF_FILE_INFO:
+ {
+ if (total_data < 8)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ size = IVAL(pdata,0);
+#ifdef LARGE_SMB_OFF_T
+ size |= (((SMB_OFF_T)IVAL(pdata,4)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if (IVAL(pdata,4) != 0) /* more than 32 bits? */
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+#endif /* LARGE_SMB_OFF_T */
+ DEBUG(10,("call_trans2setfilepathinfo: Set end of file info for file %s to %.0f\n", fname, (double)size ));
+ break;
+ }
+
+ case SMB_FILE_DISPOSITION_INFORMATION:
+ case SMB_SET_FILE_DISPOSITION_INFO: /* Set delete on close for open file. */
+ {
+ BOOL delete_on_close;
+
+ if (total_data < 1)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ delete_on_close = (CVAL(pdata,0) ? True : False);
+
+ /* Just ignore this set on a path. */
+ if (tran_call != TRANSACT2_SETFILEINFO)
+ break;
+
+ if (fsp == NULL)
+ return(UNIXERROR(ERRDOS,ERRbadfid));
+
+ status = set_delete_on_close_internal(fsp, delete_on_close);
+
+ if (NT_STATUS_V(status) != NT_STATUS_V(NT_STATUS_OK))
+ return ERROR_NT(status);
+
+ /* The set is across all open files on this dev/inode pair. */
+ status =set_delete_on_close_over_all(fsp, delete_on_close);
+ if (NT_STATUS_V(status) != NT_STATUS_V(NT_STATUS_OK))
+ return ERROR_NT(status);
+
+ break;
+ }
+
+ case SMB_FILE_POSITION_INFORMATION:
+ {
+ SMB_BIG_UINT position_information;
+
+ if (total_data < 8)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ position_information = (SMB_BIG_UINT)IVAL(pdata,0);
+#ifdef LARGE_SMB_OFF_T
+ position_information |= (((SMB_BIG_UINT)IVAL(pdata,4)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if (IVAL(pdata,4) != 0) /* more than 32 bits? */
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+#endif /* LARGE_SMB_OFF_T */
+ DEBUG(10,("call_trans2setfilepathinfo: Set file position information for file %s to %.0f\n",
+ fname, (double)position_information ));
+ if (fsp)
+ fsp->position_information = position_information;
+ break;
+ }
+
+ /*
+ * CIFS UNIX extensions.
+ */
+
+ case SMB_SET_FILE_UNIX_BASIC:
+ {
+ uint32 raw_unixmode;
+
+ if (total_data < 100)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ if(IVAL(pdata, 0) != SMB_SIZE_NO_CHANGE_LO &&
+ IVAL(pdata, 4) != SMB_SIZE_NO_CHANGE_HI) {
+ size=IVAL(pdata,0); /* first 8 Bytes are size */
+#ifdef LARGE_SMB_OFF_T
+ size |= (((SMB_OFF_T)IVAL(pdata,4)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if (IVAL(pdata,4) != 0) /* more than 32 bits? */
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+#endif /* LARGE_SMB_OFF_T */
+ }
+ pdata+=24; /* ctime & st_blocks are not changed */
+ tvs.actime = interpret_long_unix_date(pdata); /* access_time */
+ tvs.modtime = interpret_long_unix_date(pdata+8); /* modification_time */
+ pdata+=16;
+ set_owner = (uid_t)IVAL(pdata,0);
+ pdata += 8;
+ set_grp = (gid_t)IVAL(pdata,0);
+ pdata += 8;
+ raw_unixmode = IVAL(pdata,28);
+ unixmode = unix_perms_from_wire(conn, &sbuf, raw_unixmode);
+ dosmode = 0; /* Ensure dos mode change doesn't override this. */
+
+ DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_BASIC: name = %s \
+size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n",
+ fname, (double)size, (unsigned int)set_owner, (unsigned int)set_grp, (int)raw_unixmode));
+
+ if (!VALID_STAT(sbuf)) {
+
+ /*
+ * The only valid use of this is to create character and block
+ * devices, and named pipes. This is deprecated (IMHO) and
+ * a new info level should be used for mknod. JRA.
+ */
+
+#if !defined(HAVE_MAKEDEV_FN)
+ return(ERROR_DOS(ERRDOS,ERRnoaccess));
+#else /* HAVE_MAKEDEV_FN */
+ uint32 file_type = IVAL(pdata,0);
+ uint32 dev_major = IVAL(pdata,4);
+ uint32 dev_minor = IVAL(pdata,12);
+
+ uid_t myuid = geteuid();
+ gid_t mygid = getegid();
+ SMB_DEV_T dev;
+
+ if (tran_call == TRANSACT2_SETFILEINFO)
+ return(ERROR_DOS(ERRDOS,ERRnoaccess));
+
+ if (raw_unixmode == SMB_MODE_NO_CHANGE)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ dev = makedev(dev_major, dev_minor);
+
+ /* We can only create as the owner/group we are. */
+
+ if ((set_owner != myuid) && (set_owner != (uid_t)SMB_UID_NO_CHANGE))
+ return(ERROR_DOS(ERRDOS,ERRnoaccess));
+ if ((set_grp != mygid) && (set_grp != (gid_t)SMB_GID_NO_CHANGE))
+ return(ERROR_DOS(ERRDOS,ERRnoaccess));
+
+ if (file_type != UNIX_TYPE_CHARDEV && file_type != UNIX_TYPE_BLKDEV &&
+ file_type != UNIX_TYPE_FIFO)
+ return(ERROR_DOS(ERRDOS,ERRnoaccess));
+
+ DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_BASIC doing mknod dev %.0f mode \
+0%o for file %s\n", (double)dev, unixmode, fname ));
+
+ /* Ok - do the mknod. */
+ if (SMB_VFS_MKNOD(conn,dos_to_unix_static(fname), unixmode, dev) != 0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ inherit_access_acl(conn, fname, unixmode);
+
+ SSVAL(params,0,0);
+ send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
+ return(-1);
+#endif /* HAVE_MAKEDEV_FN */
+
+ }
+
+ /*
+ * Deal with the UNIX specific mode set.
+ */
+
+ if (raw_unixmode != SMB_MODE_NO_CHANGE) {
+ DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_BASIC setting mode 0%o for file %s\n",
+ (unsigned int)unixmode, fname ));
+ if (SMB_VFS_CHMOD(conn,fname,unixmode) != 0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ /*
+ * Deal with the UNIX specific uid set.
+ */
+
+ if ((set_owner != (uid_t)SMB_UID_NO_CHANGE) && (sbuf.st_uid != set_owner)) {
+ DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_BASIC changing owner %u for file %s\n",
+ (unsigned int)set_owner, fname ));
+ if (SMB_VFS_CHOWN(conn,fname,set_owner, (gid_t)-1) != 0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ /*
+ * Deal with the UNIX specific gid set.
+ */
+
+ if ((set_grp != (uid_t)SMB_GID_NO_CHANGE) && (sbuf.st_gid != set_grp)) {
+ DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_BASIC changing group %u for file %s\n",
+ (unsigned int)set_owner, fname ));
+ if (SMB_VFS_CHOWN(conn,fname,(uid_t)-1, set_grp) != 0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+ break;
+ }
+
+ case SMB_SET_FILE_UNIX_LINK:
+ {
+ pstring link_dest;
+ /* Set a symbolic link. */
+ /* Don't allow this if follow links is false. */
+
+ if (!lp_symlinks(SNUM(conn)))
+ return(ERROR_DOS(ERRDOS,ERRnoaccess));
+
+ srvstr_get_path(inbuf, link_dest, pdata, sizeof(link_dest), -1, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+
+ if (ensure_link_is_safe(conn, link_dest, link_dest) != 0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_LINK doing symlink %s -> %s\n",
+ fname, link_dest ));
+
+ if (SMB_VFS_SYMLINK(conn,link_dest,fname) != 0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ SSVAL(params,0,0);
+ send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
+ return(-1);
+ }
+
+ case SMB_SET_FILE_UNIX_HLINK:
+ {
+ pstring link_dest;
+
+ /* Set a hard link. */
+ srvstr_get_path(inbuf, link_dest, pdata, sizeof(link_dest), -1, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+
+ DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_LINK doing hard link %s -> %s\n",
+ fname, link_dest ));
+
+ status = hardlink_internals(conn, fname, link_dest);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+
+ SSVAL(params,0,0);
+ send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
+ return(-1);
+ }
+
+ case SMB_FILE_RENAME_INFORMATION:
+ {
+ BOOL overwrite;
+ uint32 root_fid;
+ uint32 len;
+ pstring newname;
+ pstring base_name;
+ char *p;
+
+ if (total_data < 12)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ overwrite = (CVAL(pdata,0) ? True : False);
+ root_fid = IVAL(pdata,4);
+ len = IVAL(pdata,8);
+ srvstr_get_path(inbuf, newname, &pdata[12], sizeof(newname), len, 0, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+
+ /* Check the new name has no '/' characters. */
+ if (strchr_m(newname, '/'))
+ return ERROR_NT(NT_STATUS_NOT_SUPPORTED);
+
+ RESOLVE_DFSPATH(newname, conn, inbuf, outbuf);
+
+ /* Create the base directory. */
+ pstrcpy(base_name, fname);
+ p = strrchr_m(base_name, '/');
+ if (p)
+ *p = '\0';
+ /* Append the new name. */
+ pstrcat(base_name, "/");
+ pstrcat(base_name, newname);
+
+ if (fsp) {
+ DEBUG(10,("call_trans2setfilepathinfo: SMB_FILE_RENAME_INFORMATION (fnum %d) %s -> %s\n",
+ fsp->fnum, fsp->fsp_name, base_name ));
+ status = rename_internals_fsp(conn, fsp, base_name, overwrite);
+ } else {
+ DEBUG(10,("call_trans2setfilepathinfo: SMB_FILE_RENAME_INFORMATION %s -> %s\n",
+ fname, newname ));
+ status = rename_internals(conn, fname, base_name, 0, overwrite);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+ process_pending_change_notify_queue((time_t)0);
+ SSVAL(params,0,0);
+ send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
+ return(-1);
+ }
+ default:
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+
+ /* get some defaults (no modifications) if any info is zero or -1. */
+ if (tvs.actime == (time_t)0 || tvs.actime == (time_t)-1)
+ tvs.actime = sbuf.st_atime;
+
+ if (tvs.modtime == (time_t)0 || tvs.modtime == (time_t)-1)
+ tvs.modtime = sbuf.st_mtime;
+
+ DEBUG(6,("actime: %s " , ctime(&tvs.actime)));
+ DEBUG(6,("modtime: %s ", ctime(&tvs.modtime)));
+ DEBUG(6,("size: %.0f ", (double)size));
+
+ if (dosmode) {
+ if (S_ISDIR(sbuf.st_mode))
+ dosmode |= aDIR;
+ else
+ dosmode &= ~aDIR;
+ }
+
+ DEBUG(6,("dosmode: %x\n" , dosmode));
+
+ if(!((info_level == SMB_SET_FILE_END_OF_FILE_INFO) ||
+ (info_level == SMB_SET_FILE_ALLOCATION_INFO) ||
+ (info_level == SMB_FILE_ALLOCATION_INFORMATION) ||
+ (info_level == SMB_FILE_END_OF_FILE_INFORMATION))) {
+
+ /*
+ * Only do this test if we are not explicitly
+ * changing the size of a file.
+ */
+ if (!size)
+ size = get_file_size(sbuf);
+ }
+
+ /*
+ * Try and set the times, size and mode of this file -
+ * if they are different from the current values
+ */
+ if (sbuf.st_mtime != tvs.modtime || sbuf.st_atime != tvs.actime) {
+ if(fsp != NULL) {
+ /*
+ * This was a setfileinfo on an open file.
+ * NT does this a lot. It's actually pointless
+ * setting the time here, as it will be overwritten
+ * on the next write, so we save the request
+ * away and will set it on file close. JRA.
+ */
+
+ if (tvs.modtime != (time_t)0 && tvs.modtime != (time_t)-1) {
+ DEBUG(10,("call_trans2setfilepathinfo: setting pending modtime to %s\n", ctime(&tvs.modtime) ));
+ fsp->pending_modtime = tvs.modtime;
+ }
+
+ } else {
+
+ DEBUG(10,("call_trans2setfilepathinfo: setting utimes to modified values.\n"));
+
+ if(file_utime(conn, fname, &tvs)!=0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+ }
+
+ /* check the mode isn't different, before changing it */
+ if ((dosmode != 0) && (dosmode != dos_mode(conn, fname, &sbuf))) {
+
+ DEBUG(10,("call_trans2setfilepathinfo: file %s : setting dos mode %x\n", fname, dosmode ));
+
+ if(file_set_dosmode(conn, fname, dosmode, NULL)) {
+ DEBUG(2,("file_set_dosmode of %s failed (%s)\n", fname, strerror(errno)));
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+ }
+
+ if (size != get_file_size(sbuf)) {
+
+ int ret;
+
+ DEBUG(10,("call_trans2setfilepathinfo: file %s : setting new size to %.0f\n",
+ fname, (double)size ));
+
+ if (fd == -1) {
+ files_struct *new_fsp = NULL;
+ int access_mode = 0;
+ int action = 0;
+
+ if(global_oplock_break) {
+ /* Queue this file modify as we are the process of an oplock break. */
+
+ DEBUG(2,("call_trans2setfilepathinfo: queueing message due to being "));
+ DEBUGADD(2,( "in oplock break state.\n"));
+
+ push_oplock_pending_smb_message(inbuf, length);
+ return -1;
+ }
+
+ new_fsp = open_file_shared(conn, fname, &sbuf,
+ SET_OPEN_MODE(DOS_OPEN_RDWR),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
+ FILE_ATTRIBUTE_NORMAL,
+ 0, &access_mode, &action);
+
+ if (new_fsp == NULL)
+ return(UNIXERROR(ERRDOS,ERRbadpath));
+ ret = vfs_set_filelen(new_fsp, size);
+ close_file(new_fsp,True);
+ } else {
+ ret = vfs_set_filelen(fsp, size);
+ }
+
+ if (ret == -1)
+ return (UNIXERROR(ERRHRD,ERRdiskfull));
+ }
+
+ SSVAL(params,0,0);
+ send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
+
+ return(-1);
+}
+
+/****************************************************************************
+ Reply to a TRANS2_MKDIR (make directory with extended attributes).
+****************************************************************************/
+
+static int call_trans2mkdir(connection_struct *conn,
+ char *inbuf, char *outbuf, int length, int bufsize,
+ char **pparams, int total_params, char **ppdata, int total_data)
+{
+ char *params = *pparams;
+ pstring directory;
+ int ret = -1;
+ SMB_STRUCT_STAT sbuf;
+ BOOL bad_path = False;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (!CAN_WRITE(conn))
+ return ERROR_DOS(ERRSRV,ERRaccess);
+
+ if (total_params < 4)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ srvstr_get_path(inbuf, directory, &params[4], sizeof(directory), -1, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+
+ DEBUG(3,("call_trans2mkdir : name = %s\n", directory));
+
+ unix_convert(directory,conn,0,&bad_path,&sbuf);
+ if (check_name(directory,conn))
+ ret = vfs_MkDir(conn,directory,unix_mode(conn,aDIR,directory));
+
+ if(ret < 0) {
+ DEBUG(5,("call_trans2mkdir error (%s)\n", strerror(errno)));
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
+ }
+
+ /* Realloc the parameter and data sizes */
+ params = Realloc(*pparams,2);
+ if(params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ *pparams = params;
+
+ SSVAL(params,0,0);
+
+ send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
+
+ return(-1);
+}
+
+/****************************************************************************
+ Reply to a TRANS2_FINDNOTIFYFIRST (start monitoring a directory for changes).
+ We don't actually do this - we just send a null response.
+****************************************************************************/
+
+static int call_trans2findnotifyfirst(connection_struct *conn,
+ char *inbuf, char *outbuf, int length, int bufsize,
+ char **pparams, int total_params, char **ppdata, int total_data)
+{
+ static uint16 fnf_handle = 257;
+ char *params = *pparams;
+ uint16 info_level;
+
+ if (total_params < 6)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ info_level = SVAL(params,4);
+ DEBUG(3,("call_trans2findnotifyfirst - info_level %d\n", info_level));
+
+ switch (info_level) {
+ case 1:
+ case 2:
+ break;
+ default:
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+
+ /* Realloc the parameter and data sizes */
+ params = Realloc(*pparams,6);
+ if(params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ *pparams = params;
+
+ SSVAL(params,0,fnf_handle);
+ SSVAL(params,2,0); /* No changes */
+ SSVAL(params,4,0); /* No EA errors */
+
+ fnf_handle++;
+
+ if(fnf_handle == 0)
+ fnf_handle = 257;
+
+ send_trans2_replies(outbuf, bufsize, params, 6, *ppdata, 0);
+
+ return(-1);
+}
+
+/****************************************************************************
+ Reply to a TRANS2_FINDNOTIFYNEXT (continue monitoring a directory for
+ changes). Currently this does nothing.
+****************************************************************************/
+
+static int call_trans2findnotifynext(connection_struct *conn,
+ char *inbuf, char *outbuf, int length, int bufsize,
+ char **pparams, int total_params, char **ppdata, int total_data)
+{
+ char *params = *pparams;
+
+ DEBUG(3,("call_trans2findnotifynext\n"));
+
+ /* Realloc the parameter and data sizes */
+ params = Realloc(*pparams,4);
+ if(params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ *pparams = params;
+
+ SSVAL(params,0,0); /* No changes */
+ SSVAL(params,2,0); /* No EA errors */
+
+ send_trans2_replies(outbuf, bufsize, params, 4, *ppdata, 0);
+
+ return(-1);
+}
+
+/****************************************************************************
+ Reply to a TRANS2_GET_DFS_REFERRAL - Shirish Kalele <kalele@veritas.com>.
+****************************************************************************/
+
+static int call_trans2getdfsreferral(connection_struct *conn, char* inbuf,
+ char* outbuf, int length, int bufsize,
+ char **pparams, int total_params, char **ppdata, int total_data)
+{
+ char *params = *pparams;
+ pstring pathname;
+ int reply_size = 0;
+ int max_referral_level;
+
+ DEBUG(10,("call_trans2getdfsreferral\n"));
+
+ if (total_params < 2)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ max_referral_level = SVAL(params,0);
+
+ if(!lp_host_msdfs())
+ return ERROR_DOS(ERRDOS,ERRbadfunc);
+
+ srvstr_pull(inbuf, pathname, &params[2], sizeof(pathname), -1, STR_TERMINATE);
+ if((reply_size = setup_dfs_referral(conn, pathname,max_referral_level,ppdata)) < 0)
+ return UNIXERROR(ERRDOS,ERRbadfile);
+
+ SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2) | FLAGS2_DFS_PATHNAMES);
+ send_trans2_replies(outbuf,bufsize,0,0,*ppdata,reply_size);
+
+ return(-1);
+}
+
+#define LMCAT_SPL 0x53
+#define LMFUNC_GETJOBID 0x60
+
+/****************************************************************************
+ Reply to a TRANS2_IOCTL - used for OS/2 printing.
+****************************************************************************/
+
+static int call_trans2ioctl(connection_struct *conn, char* inbuf,
+ char* outbuf, int length, int bufsize,
+ char **pparams, int total_params, char **ppdata, int total_data)
+{
+ char *pdata = *ppdata;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv15);
+
+ /* check for an invalid fid before proceeding */
+
+ if (!fsp)
+ return(ERROR_DOS(ERRDOS,ERRbadfid));
+
+ if ((SVAL(inbuf,(smb_setup+4)) == LMCAT_SPL) &&
+ (SVAL(inbuf,(smb_setup+6)) == LMFUNC_GETJOBID)) {
+ pdata = Realloc(*ppdata, 32);
+ if(pdata == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ *ppdata = pdata;
+
+ /* NOTE - THIS IS ASCII ONLY AT THE MOMENT - NOT SURE IF OS/2
+ CAN ACCEPT THIS IN UNICODE. JRA. */
+
+ SSVAL(pdata,0,fsp->rap_print_jobid); /* Job number */
+ srvstr_push( outbuf, pdata + 2, global_myname(), 15, STR_ASCII|STR_TERMINATE); /* Our NetBIOS name */
+ srvstr_push( outbuf, pdata+18, lp_servicename(SNUM(conn)), 13, STR_ASCII|STR_TERMINATE); /* Service name */
+ send_trans2_replies(outbuf,bufsize,*pparams,0,*ppdata,32);
+ return(-1);
+ } else {
+ DEBUG(2,("Unknown TRANS2_IOCTL\n"));
+ return ERROR_DOS(ERRSRV,ERRerror);
+ }
+}
+
+/****************************************************************************
+ Reply to a SMBfindclose (stop trans2 directory search).
+****************************************************************************/
+
+int reply_findclose(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ int outsize = 0;
+ int dptr_num=SVALS(inbuf,smb_vwv0);
+ START_PROFILE(SMBfindclose);
+
+ DEBUG(3,("reply_findclose, dptr_num = %d\n", dptr_num));
+
+ dptr_close(&dptr_num);
+
+ outsize = set_message(outbuf,0,0,True);
+
+ DEBUG(3,("SMBfindclose dptr_num = %d\n", dptr_num));
+
+ END_PROFILE(SMBfindclose);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a SMBfindnclose (stop FINDNOTIFYFIRST directory search).
+****************************************************************************/
+
+int reply_findnclose(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ int outsize = 0;
+ int dptr_num= -1;
+ START_PROFILE(SMBfindnclose);
+
+ dptr_num = SVAL(inbuf,smb_vwv0);
+
+ DEBUG(3,("reply_findnclose, dptr_num = %d\n", dptr_num));
+
+ /* We never give out valid handles for a
+ findnotifyfirst - so any dptr_num is ok here.
+ Just ignore it. */
+
+ outsize = set_message(outbuf,0,0,True);
+
+ DEBUG(3,("SMB_findnclose dptr_num = %d\n", dptr_num));
+
+ END_PROFILE(SMBfindnclose);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a SMBtranss2 - just ignore it!
+****************************************************************************/
+
+int reply_transs2(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ START_PROFILE(SMBtranss2);
+ DEBUG(4,("Ignoring transs2 of length %d\n",length));
+ END_PROFILE(SMBtranss2);
+ return(-1);
+}
+
+/****************************************************************************
+ Reply to a SMBtrans2.
+****************************************************************************/
+
+int reply_trans2(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ int outsize = 0;
+ unsigned int total_params = SVAL(inbuf, smb_tpscnt);
+ unsigned int total_data =SVAL(inbuf, smb_tdscnt);
+#if 0
+ unsigned int max_param_reply = SVAL(inbuf, smb_mprcnt);
+ unsigned int max_data_reply = SVAL(inbuf, smb_mdrcnt);
+ unsigned int max_setup_fields = SVAL(inbuf, smb_msrcnt);
+ BOOL close_tid = BITSETW(inbuf+smb_flags,0);
+ BOOL no_final_response = BITSETW(inbuf+smb_flags,1);
+ int32 timeout = IVALS(inbuf,smb_timeout);
+#endif
+ unsigned int suwcnt = SVAL(inbuf, smb_suwcnt);
+ unsigned int tran_call = SVAL(inbuf, smb_setup0);
+ char *params = NULL, *data = NULL;
+ unsigned int num_params, num_params_sofar, num_data, num_data_sofar;
+ START_PROFILE(SMBtrans2);
+
+ if(global_oplock_break && (tran_call == TRANSACT2_OPEN)) {
+ /* Queue this open message as we are the process of an
+ * oplock break. */
+
+ DEBUG(2,("reply_trans2: queueing message trans2open due to being "));
+ DEBUGADD(2,( "in oplock break state.\n"));
+
+ push_oplock_pending_smb_message(inbuf, length);
+ END_PROFILE(SMBtrans2);
+ return -1;
+ }
+
+ if (IS_IPC(conn) && (tran_call != TRANSACT2_OPEN)
+ && (tran_call != TRANSACT2_GET_DFS_REFERRAL)) {
+ END_PROFILE(SMBtrans2);
+ return ERROR_DOS(ERRSRV,ERRaccess);
+ }
+
+ outsize = set_message(outbuf,0,0,True);
+
+ /* All trans2 messages we handle have smb_sucnt == 1 - ensure this
+ is so as a sanity check */
+ if (suwcnt != 1) {
+ /*
+ * Need to have rc=0 for ioctl to get job id for OS/2.
+ * Network printing will fail if function is not successful.
+ * Similar function in reply.c will be used if protocol
+ * is LANMAN1.0 instead of LM1.2X002.
+ * Until DosPrintSetJobInfo with PRJINFO3 is supported,
+ * outbuf doesn't have to be set(only job id is used).
+ */
+ if ( (suwcnt == 4) && (tran_call == TRANSACT2_IOCTL) &&
+ (SVAL(inbuf,(smb_setup+4)) == LMCAT_SPL) &&
+ (SVAL(inbuf,(smb_setup+6)) == LMFUNC_GETJOBID)) {
+ DEBUG(2,("Got Trans2 DevIOctl jobid\n"));
+ } else {
+ DEBUG(2,("Invalid smb_sucnt in trans2 call(%u)\n",suwcnt));
+ DEBUG(2,("Transaction is %d\n",tran_call));
+ END_PROFILE(SMBtrans2);
+ ERROR_DOS(ERRDOS,ERRinvalidparam);
+ }
+ }
+
+ /* Allocate the space for the maximum needed parameters and data */
+ if (total_params > 0)
+ params = (char *)malloc(total_params);
+ if (total_data > 0)
+ data = (char *)malloc(total_data);
+
+ if ((total_params && !params) || (total_data && !data)) {
+ DEBUG(2,("Out of memory in reply_trans2\n"));
+ SAFE_FREE(params);
+ SAFE_FREE(data);
+ END_PROFILE(SMBtrans2);
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ }
+
+ /* Copy the param and data bytes sent with this request into
+ the params buffer */
+ num_params = num_params_sofar = SVAL(inbuf,smb_pscnt);
+ num_data = num_data_sofar = SVAL(inbuf, smb_dscnt);
+
+ if (num_params > total_params || num_data > total_data)
+ exit_server("invalid params in reply_trans2");
+
+ if(params) {
+ unsigned int psoff = SVAL(inbuf, smb_psoff);
+ if ((psoff + num_params < psoff) || (psoff + num_params < num_params))
+ goto bad_param;
+ if ((smb_base(inbuf) + psoff + num_params > inbuf + length) ||
+ (smb_base(inbuf) + psoff + num_params < smb_base(inbuf)))
+ goto bad_param;
+ memcpy( params, smb_base(inbuf) + psoff, num_params);
+ }
+ if(data) {
+ unsigned int dsoff = SVAL(inbuf, smb_dsoff);
+ if ((dsoff + num_data < dsoff) || (dsoff + num_data < num_data))
+ goto bad_param;
+ if ((smb_base(inbuf) + dsoff + num_data > inbuf + length) ||
+ (smb_base(inbuf) + dsoff + num_data < smb_base(inbuf)))
+ goto bad_param;
+ memcpy( data, smb_base(inbuf) + dsoff, num_data);
+ }
+
+ srv_signing_trans_start(SVAL(inbuf,smb_mid));
+
+ if(num_data_sofar < total_data || num_params_sofar < total_params) {
+ /* We need to send an interim response then receive the rest
+ of the parameter/data bytes */
+ outsize = set_message(outbuf,0,0,True);
+ srv_signing_trans_stop();
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("reply_trans2: send_smb failed.");
+
+ while (num_data_sofar < total_data ||
+ num_params_sofar < total_params) {
+ BOOL ret;
+ unsigned int param_disp;
+ unsigned int param_off;
+ unsigned int data_disp;
+ unsigned int data_off;
+
+ ret = receive_next_smb(inbuf,bufsize,SMB_SECONDARY_WAIT);
+
+ /*
+ * The sequence number for the trans reply is always
+ * based on the last secondary received.
+ */
+
+ srv_signing_trans_start(SVAL(inbuf,smb_mid));
+
+ if ((ret &&
+ (CVAL(inbuf, smb_com) != SMBtranss2)) || !ret) {
+ outsize = set_message(outbuf,0,0,True);
+ if(ret)
+ DEBUG(0,("reply_trans2: Invalid secondary trans2 packet\n"));
+ else
+ DEBUG(0,("reply_trans2: %s in getting secondary trans2 response.\n",
+ (smb_read_error == READ_ERROR) ? "error" : "timeout" ));
+ goto bad_param;
+ }
+
+ /* Revise total_params and total_data in case
+ they have changed downwards */
+ if (SVAL(inbuf, smb_tpscnt) < total_params)
+ total_params = SVAL(inbuf, smb_tpscnt);
+ if (SVAL(inbuf, smb_tdscnt) < total_data)
+ total_data = SVAL(inbuf, smb_tdscnt);
+
+ num_params = SVAL(inbuf,smb_spscnt);
+ param_off = SVAL(inbuf, smb_spsoff);
+ param_disp = SVAL(inbuf, smb_spsdisp);
+ num_params_sofar += num_params;
+
+ num_data = SVAL(inbuf, smb_sdscnt);
+ data_off = SVAL(inbuf, smb_sdsoff);
+ data_disp = SVAL(inbuf, smb_sdsdisp);
+ num_data_sofar += num_data;
+
+ if (num_params_sofar > total_params || num_data_sofar > total_data)
+ goto bad_param;
+
+ if (num_params) {
+ if (param_disp + num_params >= total_params)
+ goto bad_param;
+ if ((param_disp + num_params < param_disp) ||
+ (param_disp + num_params < num_params))
+ goto bad_param;
+ if (param_disp > total_params)
+ goto bad_param;
+ if ((smb_base(inbuf) + param_off + num_params >= inbuf + bufsize) ||
+ (smb_base(inbuf) + param_off + num_params < smb_base(inbuf)))
+ goto bad_param;
+ if (params + param_disp < params)
+ goto bad_param;
+
+ memcpy( &params[param_disp], smb_base(inbuf) + param_off, num_params);
+ }
+ if (num_data) {
+ if (data_disp + num_data >= total_data)
+ goto bad_param;
+ if ((data_disp + num_data < data_disp) ||
+ (data_disp + num_data < num_data))
+ goto bad_param;
+ if (data_disp > total_data)
+ goto bad_param;
+ if ((smb_base(inbuf) + data_off + num_data >= inbuf + bufsize) ||
+ (smb_base(inbuf) + data_off + num_data < smb_base(inbuf)))
+ goto bad_param;
+ if (data + data_disp < data)
+ goto bad_param;
+
+ memcpy( &data[data_disp], smb_base(inbuf) + data_off, num_data);
+ }
+ }
+ }
+
+ if (Protocol >= PROTOCOL_NT1) {
+ SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2) | 0x40); /* IS_LONG_NAME */
+ }
+
+ /* Now we must call the relevant TRANS2 function */
+ switch(tran_call) {
+ case TRANSACT2_OPEN:
+ START_PROFILE_NESTED(Trans2_open);
+ outsize = call_trans2open(conn, inbuf, outbuf, bufsize,
+ &params, total_params, &data, total_data);
+ END_PROFILE_NESTED(Trans2_open);
+ break;
+
+ case TRANSACT2_FINDFIRST:
+ START_PROFILE_NESTED(Trans2_findfirst);
+ outsize = call_trans2findfirst(conn, inbuf, outbuf, bufsize,
+ &params, total_params, &data, total_data);
+ END_PROFILE_NESTED(Trans2_findfirst);
+ break;
+
+ case TRANSACT2_FINDNEXT:
+ START_PROFILE_NESTED(Trans2_findnext);
+ outsize = call_trans2findnext(conn, inbuf, outbuf, length, bufsize,
+ &params, total_params, &data, total_data);
+ END_PROFILE_NESTED(Trans2_findnext);
+ break;
+
+ case TRANSACT2_QFSINFO:
+ START_PROFILE_NESTED(Trans2_qfsinfo);
+ outsize = call_trans2qfsinfo(conn, inbuf, outbuf, length, bufsize,
+ &params, total_params, &data, total_data);
+ END_PROFILE_NESTED(Trans2_qfsinfo);
+ break;
+
+#ifdef HAVE_SYS_QUOTAS
+ case TRANSACT2_SETFSINFO:
+ START_PROFILE_NESTED(Trans2_setfsinfo);
+ outsize = call_trans2setfsinfo(conn, inbuf, outbuf, length, bufsize,
+ &params, total_params, &data, total_data);
+ END_PROFILE_NESTED(Trans2_setfsinfo);
+ break;
+#endif
+ case TRANSACT2_QPATHINFO:
+ case TRANSACT2_QFILEINFO:
+ START_PROFILE_NESTED(Trans2_qpathinfo);
+ outsize = call_trans2qfilepathinfo(conn, inbuf, outbuf, length, bufsize,
+ &params, total_params, &data, total_data);
+ END_PROFILE_NESTED(Trans2_qpathinfo);
+ break;
+ case TRANSACT2_SETPATHINFO:
+ case TRANSACT2_SETFILEINFO:
+ START_PROFILE_NESTED(Trans2_setpathinfo);
+ outsize = call_trans2setfilepathinfo(conn, inbuf, outbuf, length, bufsize,
+ &params, total_params, &data, total_data);
+ END_PROFILE_NESTED(Trans2_setpathinfo);
+ break;
+
+ case TRANSACT2_FINDNOTIFYFIRST:
+ START_PROFILE_NESTED(Trans2_findnotifyfirst);
+ outsize = call_trans2findnotifyfirst(conn, inbuf, outbuf, length, bufsize,
+ &params, total_params, &data, total_data);
+ END_PROFILE_NESTED(Trans2_findnotifyfirst);
+ break;
+
+ case TRANSACT2_FINDNOTIFYNEXT:
+ START_PROFILE_NESTED(Trans2_findnotifynext);
+ outsize = call_trans2findnotifynext(conn, inbuf, outbuf, length, bufsize,
+ &params, total_params, &data, total_data);
+ END_PROFILE_NESTED(Trans2_findnotifynext);
+ break;
+ case TRANSACT2_MKDIR:
+ START_PROFILE_NESTED(Trans2_mkdir);
+ outsize = call_trans2mkdir(conn, inbuf, outbuf, length, bufsize,
+ &params, total_params, &data, total_data);
+ END_PROFILE_NESTED(Trans2_mkdir);
+ break;
+
+ case TRANSACT2_GET_DFS_REFERRAL:
+ START_PROFILE_NESTED(Trans2_get_dfs_referral);
+ outsize = call_trans2getdfsreferral(conn,inbuf,outbuf,length, bufsize,
+ &params, total_params, &data, total_data);
+ END_PROFILE_NESTED(Trans2_get_dfs_referral);
+ break;
+ case TRANSACT2_IOCTL:
+ START_PROFILE_NESTED(Trans2_ioctl);
+ outsize = call_trans2ioctl(conn,inbuf,outbuf,length, bufsize,
+ &params, total_params, &data, total_data);
+ END_PROFILE_NESTED(Trans2_ioctl);
+ break;
+ default:
+ /* Error in request */
+ DEBUG(2,("Unknown request %d in trans2 call\n", tran_call));
+ SAFE_FREE(params);
+ SAFE_FREE(data);
+ END_PROFILE(SMBtrans2);
+ srv_signing_trans_stop();
+ return ERROR_DOS(ERRSRV,ERRerror);
+ }
+
+ /* As we do not know how many data packets will need to be
+ returned here the various call_trans2xxxx calls
+ must send their own. Thus a call_trans2xxx routine only
+ returns a value other than -1 when it wants to send
+ an error packet.
+ */
+
+ srv_signing_trans_stop();
+
+ SAFE_FREE(params);
+ SAFE_FREE(data);
+ END_PROFILE(SMBtrans2);
+ return outsize; /* If a correct response was needed the
+ call_trans2xxx calls have already sent
+ it. If outsize != -1 then it is returning */
+
+ bad_param:
+
+ srv_signing_trans_stop();
+ SAFE_FREE(params);
+ SAFE_FREE(data);
+ END_PROFILE(SMBtrans2);
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+}
diff --git a/source/smbd/uid.c b/source/smbd/uid.c
new file mode 100644
index 00000000000..ff3dd1a56ef
--- /dev/null
+++ b/source/smbd/uid.c
@@ -0,0 +1,430 @@
+/*
+ Unix SMB/CIFS implementation.
+ uid/user handling
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* what user is current? */
+extern struct current_user current_user;
+
+/****************************************************************************
+ Become the guest user without changing the security context stack.
+****************************************************************************/
+
+BOOL change_to_guest(void)
+{
+ static struct passwd *pass=NULL;
+
+ if (!pass) {
+ /* Don't need to free() this as its stored in a static */
+ pass = getpwnam_alloc(lp_guestaccount());
+ if (!pass)
+ return(False);
+ }
+
+#ifdef AIX
+ /* MWW: From AIX FAQ patch to WU-ftpd: call initgroups before
+ setting IDs */
+ initgroups(pass->pw_name, pass->pw_gid);
+#endif
+
+ set_sec_ctx(pass->pw_uid, pass->pw_gid, 0, NULL, NULL, NULL);
+
+ current_user.conn = NULL;
+ current_user.vuid = UID_FIELD_INVALID;
+
+ passwd_free(&pass);
+
+ return True;
+}
+
+/****************************************************************************
+ Readonly share for this user ?
+****************************************************************************/
+
+static BOOL is_share_read_only_for_user(connection_struct *conn, user_struct *vuser)
+{
+ char **list;
+ const char *service = lp_servicename(conn->service);
+ BOOL read_only_ret = lp_readonly(conn->service);
+
+ if (!service)
+ return read_only_ret;
+
+ str_list_copy(&list, lp_readlist(conn->service));
+ if (list) {
+ if (!str_list_sub_basic(list, vuser->user.smb_name) ) {
+ DEBUG(0, ("is_share_read_only_for_user: ERROR: read list substitution failed\n"));
+ }
+ if (!str_list_substitute(list, "%S", service)) {
+ DEBUG(0, ("is_share_read_only_for_user: ERROR: read list service substitution failed\n"));
+ }
+ if (user_in_list(vuser->user.unix_name, (const char **)list, vuser->groups, vuser->n_groups)) {
+ read_only_ret = True;
+ }
+ str_list_free(&list);
+ }
+
+ str_list_copy(&list, lp_writelist(conn->service));
+ if (list) {
+ if (!str_list_sub_basic(list, vuser->user.smb_name) ) {
+ DEBUG(0, ("is_share_read_only_for_user: ERROR: write list substitution failed\n"));
+ }
+ if (!str_list_substitute(list, "%S", service)) {
+ DEBUG(0, ("is_share_read_only_for_user: ERROR: write list service substitution failed\n"));
+ }
+ if (user_in_list(vuser->user.unix_name, (const char **)list, vuser->groups, vuser->n_groups)) {
+ read_only_ret = False;
+ }
+ str_list_free(&list);
+ }
+
+ DEBUG(10,("is_share_read_only_for_user: share %s is %s for unix user %s\n",
+ service, read_only_ret ? "read-only" : "read-write", vuser->user.unix_name ));
+
+ return read_only_ret;
+}
+
+/*******************************************************************
+ Check if a username is OK.
+********************************************************************/
+
+static BOOL check_user_ok(connection_struct *conn, user_struct *vuser,int snum)
+{
+ unsigned int i;
+ struct vuid_cache_entry *ent = NULL;
+ BOOL readonly_share;
+
+ for (i=0;i<conn->vuid_cache.entries && i< VUID_CACHE_SIZE;i++) {
+ if (conn->vuid_cache.array[i].vuid == vuser->vuid) {
+ ent = &conn->vuid_cache.array[i];
+ conn->read_only = ent->read_only;
+ conn->admin_user = ent->admin_user;
+ return(True);
+ }
+ }
+
+ if (!user_ok(vuser->user.unix_name,snum, vuser->groups, vuser->n_groups))
+ return(False);
+
+ readonly_share = is_share_read_only_for_user(conn, vuser);
+
+ if (!share_access_check(conn, snum, vuser, readonly_share ? FILE_READ_DATA : FILE_WRITE_DATA)) {
+ return False;
+ }
+
+ i = conn->vuid_cache.entries % VUID_CACHE_SIZE;
+ if (conn->vuid_cache.entries < VUID_CACHE_SIZE)
+ conn->vuid_cache.entries++;
+
+ ent = &conn->vuid_cache.array[i];
+ ent->vuid = vuser->vuid;
+ ent->read_only = readonly_share;
+
+ if (user_in_list(vuser->user.unix_name ,lp_admin_users(conn->service), vuser->groups, vuser->n_groups)) {
+ ent->admin_user = True;
+ } else {
+ ent->admin_user = False;
+ }
+
+ conn->read_only = ent->read_only;
+ conn->admin_user = ent->admin_user;
+
+ return(True);
+}
+
+/****************************************************************************
+ Become the user of a connection number without changing the security context
+ stack, but modify the currnet_user entries.
+****************************************************************************/
+
+BOOL change_to_user(connection_struct *conn, uint16 vuid)
+{
+ user_struct *vuser = get_valid_user_struct(vuid);
+ int snum;
+ gid_t gid;
+ uid_t uid;
+ char group_c;
+ BOOL must_free_token_priv = False;
+ NT_USER_TOKEN *token = NULL;
+ PRIVILEGE_SET *privs = NULL;
+
+ if (!conn) {
+ DEBUG(2,("change_to_user: Connection not open\n"));
+ return(False);
+ }
+
+ /*
+ * We need a separate check in security=share mode due to vuid
+ * always being UID_FIELD_INVALID. If we don't do this then
+ * in share mode security we are *always* changing uid's between
+ * SMB's - this hurts performance - Badly.
+ */
+
+ if((lp_security() == SEC_SHARE) && (current_user.conn == conn) &&
+ (current_user.uid == conn->uid)) {
+ DEBUG(4,("change_to_user: Skipping user change - already user\n"));
+ return(True);
+ } else if ((current_user.conn == conn) &&
+ (vuser != 0) && (current_user.vuid == vuid) &&
+ (current_user.uid == vuser->uid)) {
+ DEBUG(4,("change_to_user: Skipping user change - already user\n"));
+ return(True);
+ }
+
+ snum = SNUM(conn);
+
+ if (conn->force_user) /* security = share sets this too */ {
+ uid = conn->uid;
+ gid = conn->gid;
+ current_user.groups = conn->groups;
+ current_user.ngroups = conn->ngroups;
+ token = conn->nt_user_token;
+ privs = conn->privs;
+ } else if ((vuser) && check_user_ok(conn, vuser, snum)) {
+ uid = conn->admin_user ? 0 : vuser->uid;
+ gid = vuser->gid;
+ current_user.ngroups = vuser->n_groups;
+ current_user.groups = vuser->groups;
+ token = vuser->nt_user_token;
+ privs = vuser->privs;
+ } else {
+ DEBUG(2,("change_to_user: Invalid vuid used %d or vuid not permitted access to share.\n",vuid));
+ return False;
+ }
+
+ /*
+ * See if we should force group for this service.
+ * If so this overrides any group set in the force
+ * user code.
+ */
+
+ if((group_c = *lp_force_group(snum))) {
+ BOOL is_guest = False;
+
+ if(group_c == '+') {
+
+ /*
+ * Only force group if the user is a member of
+ * the service group. Check the group memberships for
+ * this user (we already have this) to
+ * see if we should force the group.
+ */
+
+ int i;
+ for (i = 0; i < current_user.ngroups; i++) {
+ if (current_user.groups[i] == conn->gid) {
+ gid = conn->gid;
+ break;
+ }
+ }
+ } else {
+ gid = conn->gid;
+ }
+
+ /*
+ * We've changed the group list in the token - we must
+ * re-create it.
+ */
+
+ if (vuser && vuser->guest)
+ is_guest = True;
+
+ token = create_nt_token(uid, gid, current_user.ngroups, current_user.groups, is_guest);
+ if (!token) {
+ DEBUG(1, ("change_to_user: create_nt_token failed!\n"));
+ return False;
+ }
+ pdb_get_privilege_set(token->user_sids, token->num_sids, privs);
+ must_free_token_priv = True;
+ }
+
+ set_sec_ctx(uid, gid, current_user.ngroups, current_user.groups, token, privs);
+
+ /*
+ * Free the new token (as set_sec_ctx copies it).
+ */
+
+ if (must_free_token_priv) {
+ delete_nt_token(&token);
+ destroy_privilege(&privs);
+ }
+
+ current_user.conn = conn;
+ current_user.vuid = vuid;
+
+ DEBUG(5,("change_to_user uid=(%d,%d) gid=(%d,%d)\n",
+ (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid()));
+
+ return(True);
+}
+
+/****************************************************************************
+ Go back to being root without changing the security context stack,
+ but modify the current_user entries.
+****************************************************************************/
+
+BOOL change_to_root_user(void)
+{
+ set_root_sec_ctx();
+
+ DEBUG(5,("change_to_root_user: now uid=(%d,%d) gid=(%d,%d)\n",
+ (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid()));
+
+ current_user.conn = NULL;
+ current_user.vuid = UID_FIELD_INVALID;
+
+ return(True);
+}
+
+/****************************************************************************
+ Become the user of an authenticated connected named pipe.
+ When this is called we are currently running as the connection
+ user. Doesn't modify current_user.
+****************************************************************************/
+
+BOOL become_authenticated_pipe_user(pipes_struct *p)
+{
+ if (!push_sec_ctx())
+ return False;
+
+ set_sec_ctx(p->pipe_user.uid, p->pipe_user.gid,
+ p->pipe_user.ngroups, p->pipe_user.groups, p->pipe_user.nt_user_token, p->pipe_user.privs);
+
+ return True;
+}
+
+/****************************************************************************
+ Unbecome the user of an authenticated connected named pipe.
+ When this is called we are running as the authenticated pipe
+ user and need to go back to being the connection user. Doesn't modify
+ current_user.
+****************************************************************************/
+
+BOOL unbecome_authenticated_pipe_user(void)
+{
+ return pop_sec_ctx();
+}
+
+/****************************************************************************
+ Utility functions used by become_xxx/unbecome_xxx.
+****************************************************************************/
+
+struct conn_ctx {
+ connection_struct *conn;
+ uint16 vuid;
+};
+
+/* A stack of current_user connection contexts. */
+
+static struct conn_ctx conn_ctx_stack[MAX_SEC_CTX_DEPTH];
+static int conn_ctx_stack_ndx;
+
+static void push_conn_ctx(void)
+{
+ struct conn_ctx *ctx_p;
+
+ /* Check we don't overflow our stack */
+
+ if (conn_ctx_stack_ndx == MAX_SEC_CTX_DEPTH) {
+ DEBUG(0, ("Connection context stack overflow!\n"));
+ smb_panic("Connection context stack overflow!\n");
+ }
+
+ /* Store previous user context */
+ ctx_p = &conn_ctx_stack[conn_ctx_stack_ndx];
+
+ ctx_p->conn = current_user.conn;
+ ctx_p->vuid = current_user.vuid;
+
+ DEBUG(3, ("push_conn_ctx(%u) : conn_ctx_stack_ndx = %d\n",
+ (unsigned int)ctx_p->vuid, conn_ctx_stack_ndx ));
+
+ conn_ctx_stack_ndx++;
+}
+
+static void pop_conn_ctx(void)
+{
+ struct conn_ctx *ctx_p;
+
+ /* Check for stack underflow. */
+
+ if (conn_ctx_stack_ndx == 0) {
+ DEBUG(0, ("Connection context stack underflow!\n"));
+ smb_panic("Connection context stack underflow!\n");
+ }
+
+ conn_ctx_stack_ndx--;
+ ctx_p = &conn_ctx_stack[conn_ctx_stack_ndx];
+
+ current_user.conn = ctx_p->conn;
+ current_user.vuid = ctx_p->vuid;
+
+ ctx_p->conn = NULL;
+ ctx_p->vuid = UID_FIELD_INVALID;
+}
+
+/****************************************************************************
+ Temporarily become a root user. Must match with unbecome_root(). Saves and
+ restores the connection context.
+****************************************************************************/
+
+void become_root(void)
+{
+ push_sec_ctx();
+ push_conn_ctx();
+ set_root_sec_ctx();
+}
+
+/* Unbecome the root user */
+
+void unbecome_root(void)
+{
+ pop_sec_ctx();
+ pop_conn_ctx();
+}
+
+/****************************************************************************
+ Push the current security context then force a change via change_to_user().
+ Saves and restores the connection context.
+****************************************************************************/
+
+BOOL become_user(connection_struct *conn, uint16 vuid)
+{
+ if (!push_sec_ctx())
+ return False;
+
+ push_conn_ctx();
+
+ if (!change_to_user(conn, vuid)) {
+ pop_sec_ctx();
+ pop_conn_ctx();
+ return False;
+ }
+
+ return True;
+}
+
+BOOL unbecome_user(void)
+{
+ pop_sec_ctx();
+ pop_conn_ctx();
+ return True;
+}
+
diff --git a/source/smbd/utmp.c b/source/smbd/utmp.c
new file mode 100644
index 00000000000..a521d0113d4
--- /dev/null
+++ b/source/smbd/utmp.c
@@ -0,0 +1,595 @@
+/*
+ Unix SMB/CIFS implementation.
+ utmp routines
+ Copyright (C) T.D.Lee@durham.ac.uk 1999
+ Heavily modified by Andrew Bartlett and Tridge, April 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+Reflect connection status in utmp/wtmp files.
+ T.D.Lee@durham.ac.uk September 1999
+
+ With grateful thanks since then to many who have helped port it to
+ different operating systems. The variety of OS quirks thereby
+ uncovered is amazing...
+
+Hints for porting:
+ o Always attempt to use programmatic interface (pututline() etc.)
+ Indeed, at present only programmatic use is supported.
+ o The only currently supported programmatic interface to "wtmp{,x}"
+ is through "updwtmp*()" routines.
+ o The "x" (utmpx/wtmpx; HAVE_UTMPX_H) seems preferable.
+ o The HAVE_* items should identify supported features.
+ o If at all possible, avoid "if defined(MY-OS)" constructions.
+
+OS observations and status:
+ Almost every OS seems to have its own quirks.
+
+ Solaris 2.x:
+ Tested on 2.6 and 2.7; should be OK on other flavours.
+ AIX:
+ Apparently has utmpx.h but doesn't implement.
+ OSF:
+ Has utmpx.h, but (e.g.) no "getutmpx()". (Is this like AIX ?)
+ Redhat 6:
+ utmpx.h seems not to set default filenames. non-x better.
+ IRIX 6.5:
+ Not tested. Appears to have "x".
+ HP-UX 9.x:
+ Not tested. Appears to lack "x".
+ HP-UX 10.x:
+ Not tested.
+ "updwtmp*()" routines seem absent, so no current wtmp* support.
+ Has "ut_addr": probably trivial to implement (although remember
+ that IPv6 is coming...).
+
+ FreeBSD:
+ No "putut*()" type of interface.
+ No "ut_type" and associated defines.
+ Write files directly. Alternatively use its login(3)/logout(3).
+ SunOS 4:
+ Not tested. Resembles FreeBSD, but no login()/logout().
+
+lastlog:
+ Should "lastlog" files, if any, be updated?
+ BSD systems (SunOS 4, FreeBSD):
+ o Prominent mention on man pages.
+ System-V (e.g. Solaris 2):
+ o No mention on man pages, even under "man -k".
+ o Has a "/var/adm/lastlog" file, but pututxline() etc. seem
+ not to touch it.
+ o Despite downplaying (above), nevertheless has <lastlog.h>.
+ So perhaps UN*X "lastlog" facility is intended for tty/terminal only?
+
+Notes:
+ Each connection requires a small number (starting at 0, working up)
+ to represent the line. This must be unique within and across all
+ smbd processes. It is the 'id_num' from Samba's session.c code.
+
+ The 4 byte 'ut_id' component is vital to distinguish connections,
+ of which there could be several hundred or even thousand.
+ Entries seem to be printable characters, with optional NULL pads.
+
+ We need to be distinct from other entries in utmp/wtmp.
+
+ Observed things: therefore avoid them. Add to this list please.
+ From Solaris 2.x (because that's what I have):
+ 'sN' : run-levels; N: [0-9]
+ 'co' : console
+ 'CC' : arbitrary things; C: [a-z]
+ 'rXNN' : rlogin; N: [0-9]; X: [0-9a-z]
+ 'tXNN' : rlogin; N: [0-9]; X: [0-9a-z]
+ '/NNN' : Solaris CDE
+ 'ftpZ' : ftp (Z is the number 255, aka 0377, aka 0xff)
+ Mostly a record uses the same 'ut_id' in both "utmp" and "wtmp",
+ but differences have been seen.
+
+ Arbitrarily I have chosen to use a distinctive 'SM' for the
+ first two bytes.
+
+ The remaining two bytes encode the session 'id_num' (see above).
+ Our caller (session.c) should note our 16-bit limitation.
+
+****************************************************************************/
+
+#ifndef WITH_UTMP
+/*
+ * Not WITH_UTMP? Simply supply dummy routines.
+ */
+
+void sys_utmp_claim(const char *username, const char *hostname,
+ struct in_addr *ipaddr,
+ const char *id_str, int id_num)
+{}
+
+void sys_utmp_yield(const char *username, const char *hostname,
+ struct in_addr *ipaddr,
+ const char *id_str, int id_num)
+{}
+
+#else /* WITH_UTMP */
+
+#include <utmp.h>
+
+#ifdef HAVE_UTMPX_H
+#include <utmpx.h>
+#endif
+
+/* BSD systems: some may need lastlog.h (SunOS 4), some may not (FreeBSD) */
+/* Some System-V systems (e.g. Solaris 2) declare this too. */
+#ifdef HAVE_LASTLOG_H
+#include <lastlog.h>
+#endif
+
+/****************************************************************************
+ Default paths to various {u,w}tmp{,x} files.
+****************************************************************************/
+
+#ifdef HAVE_UTMPX_H
+
+static const char *ux_pathname =
+# if defined (UTMPX_FILE)
+ UTMPX_FILE ;
+# elif defined (_UTMPX_FILE)
+ _UTMPX_FILE ;
+# elif defined (_PATH_UTMPX)
+ _PATH_UTMPX ;
+# else
+ "" ;
+# endif
+
+static const char *wx_pathname =
+# if defined (WTMPX_FILE)
+ WTMPX_FILE ;
+# elif defined (_WTMPX_FILE)
+ _WTMPX_FILE ;
+# elif defined (_PATH_WTMPX)
+ _PATH_WTMPX ;
+# else
+ "" ;
+# endif
+
+#endif /* HAVE_UTMPX_H */
+
+static const char *ut_pathname =
+# if defined (UTMP_FILE)
+ UTMP_FILE ;
+# elif defined (_UTMP_FILE)
+ _UTMP_FILE ;
+# elif defined (_PATH_UTMP)
+ _PATH_UTMP ;
+# else
+ "" ;
+# endif
+
+static const char *wt_pathname =
+# if defined (WTMP_FILE)
+ WTMP_FILE ;
+# elif defined (_WTMP_FILE)
+ _WTMP_FILE ;
+# elif defined (_PATH_WTMP)
+ _PATH_WTMP ;
+# else
+ "" ;
+# endif
+
+/* BSD-like systems might want "lastlog" support. */
+/* *** Not yet implemented */
+#ifndef HAVE_PUTUTLINE /* see "pututline_my()" */
+static const char *ll_pathname =
+# if defined (_PATH_LASTLOG) /* what other names (if any?) */
+ _PATH_LASTLOG ;
+# else
+ "" ;
+# endif /* _PATH_LASTLOG */
+#endif /* HAVE_PUTUTLINE */
+
+/*
+ * Get name of {u,w}tmp{,x} file.
+ * return: fname contains filename
+ * Possibly empty if this code not yet ported to this system.
+ *
+ * utmp{,x}: try "utmp dir", then default (a define)
+ * wtmp{,x}: try "wtmp dir", then "utmp dir", then default (a define)
+ */
+static void uw_pathname(pstring fname, const char *uw_name, const char *uw_default)
+{
+ pstring dirname;
+
+ pstrcpy(dirname, "");
+
+ /* For w-files, first look for explicit "wtmp dir" */
+ if (uw_name[0] == 'w') {
+ pstrcpy(dirname,lp_wtmpdir());
+ trim_char(dirname,'\0','/');
+ }
+
+ /* For u-files and non-explicit w-dir, look for "utmp dir" */
+ if (dirname == 0 || strlen(dirname) == 0) {
+ pstrcpy(dirname,lp_utmpdir());
+ trim_char(dirname,'\0','/');
+ }
+
+ /* If explicit directory above, use it */
+ if (dirname != 0 && strlen(dirname) != 0) {
+ pstrcpy(fname, dirname);
+ pstrcat(fname, "/");
+ pstrcat(fname, uw_name);
+ return;
+ }
+
+ /* No explicit directory: attempt to use default paths */
+ if (strlen(uw_default) == 0) {
+ /* No explicit setting, no known default.
+ * Has it yet been ported to this OS?
+ */
+ DEBUG(2,("uw_pathname: unable to determine pathname\n"));
+ }
+ pstrcpy(fname, uw_default);
+}
+
+#ifndef HAVE_PUTUTLINE
+
+/****************************************************************************
+ Update utmp file directly. No subroutine interface: probably a BSD system.
+****************************************************************************/
+
+static void pututline_my(pstring uname, struct utmp *u, BOOL claim)
+{
+ DEBUG(1,("pututline_my: not yet implemented\n"));
+ /* BSD implementor: may want to consider (or not) adjusting "lastlog" */
+}
+#endif /* HAVE_PUTUTLINE */
+
+#ifndef HAVE_UPDWTMP
+
+/****************************************************************************
+ Update wtmp file directly. No subroutine interface: probably a BSD system.
+ Credit: Michail Vidiassov <master@iaas.msu.ru>
+****************************************************************************/
+
+static void updwtmp_my(pstring wname, struct utmp *u, BOOL claim)
+{
+ int fd;
+ struct stat buf;
+
+ if (! claim) {
+ /*
+ * BSD-like systems:
+ * may use empty ut_name to distinguish a logout record.
+ *
+ * May need "if defined(SUNOS4)" etc. around some of these,
+ * but try to avoid if possible.
+ *
+ * SunOS 4:
+ * man page indicates ut_name and ut_host both NULL
+ * FreeBSD 4.0:
+ * man page appears not to specify (hints non-NULL)
+ * A correspondent suggest at least ut_name should be NULL
+ */
+#if defined(HAVE_UT_UT_NAME)
+ memset((char *)&u->ut_name, '\0', sizeof(u->ut_name));
+#endif
+#if defined(HAVE_UT_UT_HOST)
+ memset((char *)&u->ut_host, '\0', sizeof(u->ut_host));
+#endif
+ }
+ /* Stolen from logwtmp function in libutil.
+ * May be more locking/blocking is needed?
+ */
+ if ((fd = open(wname, O_WRONLY|O_APPEND, 0)) < 0)
+ return;
+ if (fstat(fd, &buf) == 0) {
+ if (write(fd, (char *)u, sizeof(struct utmp)) != sizeof(struct utmp))
+ (void) ftruncate(fd, buf.st_size);
+ }
+ (void) close(fd);
+}
+#endif /* HAVE_UPDWTMP */
+
+/****************************************************************************
+ Update via utmp/wtmp (not utmpx/wtmpx).
+****************************************************************************/
+
+static void utmp_nox_update(struct utmp *u, BOOL claim)
+{
+ pstring uname, wname;
+#if defined(PUTUTLINE_RETURNS_UTMP)
+ struct utmp *urc;
+#endif /* PUTUTLINE_RETURNS_UTMP */
+
+ uw_pathname(uname, "utmp", ut_pathname);
+ DEBUG(2,("utmp_nox_update: uname:%s\n", uname));
+
+#ifdef HAVE_PUTUTLINE
+ if (strlen(uname) != 0) {
+ utmpname(uname);
+ }
+
+# if defined(PUTUTLINE_RETURNS_UTMP)
+ setutent();
+ urc = pututline(u);
+ endutent();
+ if (urc == NULL) {
+ DEBUG(2,("utmp_nox_update: pututline() failed\n"));
+ return;
+ }
+# else /* PUTUTLINE_RETURNS_UTMP */
+ setutent();
+ pututline(u);
+ endutent();
+# endif /* PUTUTLINE_RETURNS_UTMP */
+
+#else /* HAVE_PUTUTLINE */
+ if (strlen(uname) != 0) {
+ pututline_my(uname, u, claim);
+ }
+#endif /* HAVE_PUTUTLINE */
+
+ uw_pathname(wname, "wtmp", wt_pathname);
+ DEBUG(2,("utmp_nox_update: wname:%s\n", wname));
+ if (strlen(wname) != 0) {
+#ifdef HAVE_UPDWTMP
+ updwtmp(wname, u);
+ /*
+ * updwtmp() and the newer updwtmpx() may be unsymmetrical.
+ * At least one OS, Solaris 2.x declares the former in the
+ * "utmpx" (latter) file and context.
+ * In the Solaris case this is irrelevant: it has both and
+ * we always prefer the "x" case, so doesn't come here.
+ * But are there other systems, with no "x", which lack
+ * updwtmp() perhaps?
+ */
+#else
+ updwtmp_my(wname, u, claim);
+#endif /* HAVE_UPDWTMP */
+ }
+}
+
+/****************************************************************************
+ Copy a string in the utmp structure.
+****************************************************************************/
+
+static void utmp_strcpy(char *dest, const char *src, size_t n)
+{
+ size_t len = 0;
+
+ memset(dest, '\0', n);
+ if (src)
+ len = strlen(src);
+ if (len >= n) {
+ memcpy(dest, src, n);
+ } else {
+ if (len)
+ memcpy(dest, src, len);
+ }
+}
+
+/****************************************************************************
+ Update via utmpx/wtmpx (preferred) or via utmp/wtmp.
+****************************************************************************/
+
+static void sys_utmp_update(struct utmp *u, const char *hostname, BOOL claim)
+{
+#if !defined(HAVE_UTMPX_H)
+ /* No utmpx stuff. Drop to non-x stuff */
+ utmp_nox_update(u, claim);
+#elif !defined(HAVE_PUTUTXLINE)
+ /* Odd. Have utmpx.h but no "pututxline()". Drop to non-x stuff */
+ DEBUG(1,("utmp_update: have utmpx.h but no pututxline() function\n"));
+ utmp_nox_update(u, claim);
+#elif !defined(HAVE_GETUTMPX)
+ /* Odd. Have utmpx.h but no "getutmpx()". Drop to non-x stuff */
+ DEBUG(1,("utmp_update: have utmpx.h but no getutmpx() function\n"));
+ utmp_nox_update(u, claim);
+#else
+ pstring uname, wname;
+ struct utmpx ux, *uxrc;
+
+ getutmpx(u, &ux);
+
+#if defined(HAVE_UX_UT_SYSLEN)
+ if (hostname)
+ ux.ut_syslen = strlen(hostname) + 1; /* include end NULL */
+ else
+ ux.ut_syslen = 0;
+#endif
+#if defined(HAVE_UT_UT_HOST)
+ utmp_strcpy(ux.ut_host, hostname, sizeof(ux.ut_host));
+#endif
+
+ uw_pathname(uname, "utmpx", ux_pathname);
+ uw_pathname(wname, "wtmpx", wx_pathname);
+ DEBUG(2,("utmp_update: uname:%s wname:%s\n", uname, wname));
+ /*
+ * Check for either uname or wname being empty.
+ * Some systems, such as Redhat 6, have a "utmpx.h" which doesn't
+ * define default filenames.
+ * Also, our local installation has not provided an override.
+ * Drop to non-x method. (E.g. RH6 has good defaults in "utmp.h".)
+ */
+ if ((strlen(uname) == 0) || (strlen(wname) == 0)) {
+ utmp_nox_update(u, claim);
+ } else {
+ utmpxname(uname);
+ setutxent();
+ uxrc = pututxline(&ux);
+ endutxent();
+ if (uxrc == NULL) {
+ DEBUG(2,("utmp_update: pututxline() failed\n"));
+ return;
+ }
+ updwtmpx(wname, &ux);
+ }
+#endif /* HAVE_UTMPX_H */
+}
+
+#if defined(HAVE_UT_UT_ID)
+/****************************************************************************
+ Encode the unique connection number into "ut_id".
+****************************************************************************/
+
+static int ut_id_encode(int i, char *fourbyte)
+{
+ int nbase;
+ const char *ut_id_encstr = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ fourbyte[0] = 'S';
+ fourbyte[1] = 'M';
+
+/*
+ * Encode remaining 2 bytes from 'i'.
+ * 'ut_id_encstr' is the character set on which modulo arithmetic is done.
+ * Example: digits would produce the base-10 numbers from '001'.
+ */
+ nbase = strlen(ut_id_encstr);
+
+ fourbyte[3] = ut_id_encstr[i % nbase];
+ i /= nbase;
+ fourbyte[2] = ut_id_encstr[i % nbase];
+ i /= nbase;
+
+ return(i); /* 0: good; else overflow */
+}
+#endif /* defined(HAVE_UT_UT_ID) */
+
+
+/*
+ fill a system utmp structure given all the info we can gather
+*/
+static BOOL sys_utmp_fill(struct utmp *u,
+ const char *username, const char *hostname,
+ struct in_addr *ipaddr,
+ const char *id_str, int id_num)
+{
+ struct timeval timeval;
+
+ /*
+ * ut_name, ut_user:
+ * Several (all?) systems seems to define one as the other.
+ * It is easier and clearer simply to let the following take its course,
+ * rather than to try to detect and optimise.
+ */
+#if defined(HAVE_UT_UT_USER)
+ utmp_strcpy(u->ut_user, username, sizeof(u->ut_user));
+#elif defined(HAVE_UT_UT_NAME)
+ utmp_strcpy(u->ut_name, username, sizeof(u->ut_name));
+#endif
+
+ /*
+ * ut_line:
+ * If size limit proves troublesome, then perhaps use "ut_id_encode()".
+ */
+ if (strlen(id_str) > sizeof(u->ut_line)) {
+ DEBUG(1,("id_str [%s] is too long for %lu char utmp field\n",
+ id_str, (unsigned long)sizeof(u->ut_line)));
+ return False;
+ }
+ utmp_strcpy(u->ut_line, id_str, sizeof(u->ut_line));
+
+#if defined(HAVE_UT_UT_PID)
+ u->ut_pid = sys_getpid();
+#endif
+
+/*
+ * ut_time, ut_tv:
+ * Some have one, some the other. Many have both, but defined (aliased).
+ * It is easier and clearer simply to let the following take its course.
+ * But note that we do the more precise ut_tv as the final assignment.
+ */
+#if defined(HAVE_UT_UT_TIME)
+ gettimeofday(&timeval, NULL);
+ u->ut_time = timeval.tv_sec;
+#elif defined(HAVE_UT_UT_TV)
+ gettimeofday(&timeval, NULL);
+ u->ut_tv = timeval;
+#else
+#error "with-utmp must have UT_TIME or UT_TV"
+#endif
+
+#if defined(HAVE_UT_UT_HOST)
+ utmp_strcpy(u->ut_host, hostname, sizeof(u->ut_host));
+#endif
+#if defined(HAVE_UT_UT_ADDR)
+ if (ipaddr)
+ u->ut_addr = ipaddr->s_addr;
+ /*
+ * "(unsigned long) ut_addr" apparently exists on at least HP-UX 10.20.
+ * Volunteer to implement, please ...
+ */
+#endif
+
+#if defined(HAVE_UT_UT_ID)
+ if (ut_id_encode(id_num, u->ut_id) != 0) {
+ DEBUG(1,("utmp_fill: cannot encode id %d\n", id_num));
+ return False;
+ }
+#endif
+
+ return True;
+}
+
+/****************************************************************************
+ Close a connection.
+****************************************************************************/
+
+void sys_utmp_yield(const char *username, const char *hostname,
+ struct in_addr *ipaddr,
+ const char *id_str, int id_num)
+{
+ struct utmp u;
+
+ ZERO_STRUCT(u);
+
+#if defined(HAVE_UT_UT_EXIT)
+ u.ut_exit.e_termination = 0;
+ u.ut_exit.e_exit = 0;
+#endif
+
+#if defined(HAVE_UT_UT_TYPE)
+ u.ut_type = DEAD_PROCESS;
+#endif
+
+ if (!sys_utmp_fill(&u, username, hostname, ipaddr, id_str, id_num)) return;
+
+ sys_utmp_update(&u, NULL, False);
+}
+
+/****************************************************************************
+ Claim a entry in whatever utmp system the OS uses.
+****************************************************************************/
+
+void sys_utmp_claim(const char *username, const char *hostname,
+ struct in_addr *ipaddr,
+ const char *id_str, int id_num)
+{
+ struct utmp u;
+
+ ZERO_STRUCT(u);
+
+#if defined(HAVE_UT_UT_TYPE)
+ u.ut_type = USER_PROCESS;
+#endif
+
+ if (!sys_utmp_fill(&u, username, hostname, ipaddr, id_str, id_num)) return;
+
+ sys_utmp_update(&u, hostname, True);
+}
+
+#endif /* WITH_UTMP */
diff --git a/source/smbd/vfs-wrap.c b/source/smbd/vfs-wrap.c
new file mode 100644
index 00000000000..5393dfc7556
--- /dev/null
+++ b/source/smbd/vfs-wrap.c
@@ -0,0 +1,1031 @@
+/*
+ Unix SMB/CIFS implementation.
+ Wrap disk only vfs functions to sidestep dodgy compilers.
+ Copyright (C) Tim Potter 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_VFS
+
+
+/* Check for NULL pointer parameters in vfswrap_* functions */
+
+/* We don't want to have NULL function pointers lying around. Someone
+ is sure to try and execute them. These stubs are used to prevent
+ this possibility. */
+
+int vfswrap_dummy_connect(vfs_handle_struct *handle, connection_struct *conn, const char *service, const char *user)
+{
+ return 0; /* Return >= 0 for success */
+}
+
+void vfswrap_dummy_disconnect(vfs_handle_struct *handle, connection_struct *conn)
+{
+}
+
+/* Disk operations */
+
+SMB_BIG_UINT vfswrap_disk_free(vfs_handle_struct *handle, connection_struct *conn, const char *path, BOOL small_query, SMB_BIG_UINT *bsize,
+ SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ SMB_BIG_UINT result;
+
+ result = sys_disk_free(path, small_query, bsize, dfree, dsize);
+ return result;
+}
+
+int vfswrap_get_quota(struct vfs_handle_struct *handle, struct connection_struct *conn, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *qt)
+{
+#ifdef HAVE_SYS_QUOTAS
+ int result;
+
+ START_PROFILE(syscall_get_quota);
+ result = sys_get_quota(conn->connectpath, qtype, id, qt);
+ END_PROFILE(syscall_get_quota);
+ return result;
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+int vfswrap_set_quota(struct vfs_handle_struct *handle, struct connection_struct *conn, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *qt)
+{
+#ifdef HAVE_SYS_QUOTAS
+ int result;
+
+ START_PROFILE(syscall_set_quota);
+ result = sys_set_quota(conn->connectpath, qtype, id, qt);
+ END_PROFILE(syscall_set_quota);
+ return result;
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+int vfswrap_get_shadow_copy_data(struct vfs_handle_struct *handle, struct files_struct *fsp, SHADOW_COPY_DATA *shadow_copy_data, BOOL labels)
+{
+ errno = ENOSYS;
+ return -1; /* Not implemented. */
+}
+
+/* Directory operations */
+
+DIR *vfswrap_opendir(vfs_handle_struct *handle, connection_struct *conn, const char *fname)
+{
+ DIR *result;
+
+ START_PROFILE(syscall_opendir);
+ result = opendir(fname);
+ END_PROFILE(syscall_opendir);
+ return result;
+}
+
+struct dirent *vfswrap_readdir(vfs_handle_struct *handle, connection_struct *conn, DIR *dirp)
+{
+ struct dirent *result;
+
+ START_PROFILE(syscall_readdir);
+ result = readdir(dirp);
+ END_PROFILE(syscall_readdir);
+ return result;
+}
+
+int vfswrap_mkdir(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode)
+{
+ int result;
+ BOOL has_dacl = False;
+
+ START_PROFILE(syscall_mkdir);
+
+ if (lp_inherit_acls(SNUM(conn)) && (has_dacl = directory_has_default_acl(conn, parent_dirname(path))))
+ mode = 0777;
+
+ result = mkdir(path, mode);
+
+ if (result == 0 && !has_dacl) {
+ /*
+ * We need to do this as the default behavior of POSIX ACLs
+ * is to set the mask to be the requested group permission
+ * bits, not the group permission bits to be the requested
+ * group permission bits. This is not what we want, as it will
+ * mess up any inherited ACL bits that were set. JRA.
+ */
+ int saved_errno = errno; /* We may get ENOSYS */
+ if ((SMB_VFS_CHMOD_ACL(conn, path, mode) == -1) && (errno == ENOSYS))
+ errno = saved_errno;
+ }
+
+ END_PROFILE(syscall_mkdir);
+ return result;
+}
+
+int vfswrap_rmdir(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ int result;
+
+ START_PROFILE(syscall_rmdir);
+ result = rmdir(path);
+ END_PROFILE(syscall_rmdir);
+ return result;
+}
+
+int vfswrap_closedir(vfs_handle_struct *handle, connection_struct *conn, DIR *dirp)
+{
+ int result;
+
+ START_PROFILE(syscall_closedir);
+ result = closedir(dirp);
+ END_PROFILE(syscall_closedir);
+ return result;
+}
+
+/* File operations */
+
+int vfswrap_open(vfs_handle_struct *handle, connection_struct *conn, const char *fname, int flags, mode_t mode)
+{
+ int result;
+
+ START_PROFILE(syscall_open);
+ result = sys_open(fname, flags, mode);
+ END_PROFILE(syscall_open);
+ return result;
+}
+
+int vfswrap_close(vfs_handle_struct *handle, files_struct *fsp, int fd)
+{
+ int result;
+
+ START_PROFILE(syscall_close);
+
+ result = close(fd);
+ END_PROFILE(syscall_close);
+ return result;
+}
+
+ssize_t vfswrap_read(vfs_handle_struct *handle, files_struct *fsp, int fd, void *data, size_t n)
+{
+ ssize_t result;
+
+ START_PROFILE_BYTES(syscall_read, n);
+ result = sys_read(fd, data, n);
+ END_PROFILE(syscall_read);
+ return result;
+}
+
+ssize_t vfswrap_pread(vfs_handle_struct *handle, files_struct *fsp, int fd, void *data,
+ size_t n, SMB_OFF_T offset)
+{
+ ssize_t result;
+
+#if defined(HAVE_PREAD) || defined(HAVE_PREAD64)
+ START_PROFILE_BYTES(syscall_pread, n);
+ result = sys_pread(fd, data, n, offset);
+ END_PROFILE(syscall_pread);
+
+ if (result == -1 && errno == ESPIPE) {
+ /* Maintain the fiction that pipes can be seeked (sought?) on. */
+ result = SMB_VFS_READ(fsp, fd, data, n);
+ fsp->pos = 0;
+ }
+
+#else /* HAVE_PREAD */
+ SMB_OFF_T curr;
+ int lerrno;
+
+ curr = SMB_VFS_LSEEK(fsp, fd, 0, SEEK_CUR);
+ if (curr == -1 && errno == ESPIPE) {
+ /* Maintain the fiction that pipes can be seeked (sought?) on. */
+ result = SMB_VFS_READ(fsp, fd, data, n);
+ fsp->pos = 0;
+ return result;
+ }
+
+ if (SMB_VFS_LSEEK(fsp, fd, offset, SEEK_SET) == -1) {
+ return -1;
+ }
+
+ errno = 0;
+ result = SMB_VFS_READ(fsp, fd, data, n);
+ lerrno = errno;
+
+ SMB_VFS_LSEEK(fsp, fd, curr, SEEK_SET);
+ errno = lerrno;
+
+#endif /* HAVE_PREAD */
+
+ return result;
+}
+
+ssize_t vfswrap_write(vfs_handle_struct *handle, files_struct *fsp, int fd, const void *data, size_t n)
+{
+ ssize_t result;
+
+ START_PROFILE_BYTES(syscall_write, n);
+ result = sys_write(fd, data, n);
+ END_PROFILE(syscall_write);
+ return result;
+}
+
+ssize_t vfswrap_pwrite(vfs_handle_struct *handle, files_struct *fsp, int fd, const void *data,
+ size_t n, SMB_OFF_T offset)
+{
+ ssize_t result;
+
+#if defined(HAVE_PWRITE) || defined(HAVE_PRWITE64)
+ START_PROFILE_BYTES(syscall_pwrite, n);
+ result = sys_pwrite(fd, data, n, offset);
+ END_PROFILE(syscall_pwrite);
+
+ if (result == -1 && errno == ESPIPE) {
+ /* Maintain the fiction that pipes can be sought on. */
+ result = SMB_VFS_WRITE(fsp, fd, data, n);
+ }
+
+#else /* HAVE_PWRITE */
+ SMB_OFF_T curr;
+ int lerrno;
+
+ curr = SMB_VFS_LSEEK(fsp, fd, 0, SEEK_CUR);
+ if (curr == -1) {
+ return -1;
+ }
+
+ if (SMB_VFS_LSEEK(fsp, fd, offset, SEEK_SET) == -1) {
+ return -1;
+ }
+
+ result = SMB_VFS_WRITE(fsp, fd, data, n);
+ lerrno = errno;
+
+ SMB_VFS_LSEEK(fsp, fd, curr, SEEK_SET);
+ errno = lerrno;
+
+#endif /* HAVE_PWRITE */
+
+ return result;
+}
+
+SMB_OFF_T vfswrap_lseek(vfs_handle_struct *handle, files_struct *fsp, int filedes, SMB_OFF_T offset, int whence)
+{
+ SMB_OFF_T result = 0;
+
+ START_PROFILE(syscall_lseek);
+
+ /* Cope with 'stat' file opens. */
+ if (filedes != -1)
+ result = sys_lseek(filedes, offset, whence);
+
+ /*
+ * We want to maintain the fiction that we can seek
+ * on a fifo for file system purposes. This allows
+ * people to set up UNIX fifo's that feed data to Windows
+ * applications. JRA.
+ */
+
+ if((result == -1) && (errno == ESPIPE)) {
+ result = 0;
+ errno = 0;
+ }
+
+ END_PROFILE(syscall_lseek);
+ return result;
+}
+
+ssize_t vfswrap_sendfile(vfs_handle_struct *handle, int tofd, files_struct *fsp, int fromfd, const DATA_BLOB *hdr,
+ SMB_OFF_T offset, size_t n)
+{
+ ssize_t result;
+
+ START_PROFILE_BYTES(syscall_sendfile, n);
+ result = sys_sendfile(tofd, fromfd, hdr, offset, n);
+ END_PROFILE(syscall_sendfile);
+ return result;
+}
+
+/*********************************************************
+ For rename across filesystems Patch from Warren Birnbaum
+ <warrenb@hpcvscdp.cv.hp.com>
+**********************************************************/
+
+static int copy_reg(const char *source, const char *dest)
+{
+ SMB_STRUCT_STAT source_stats;
+ int saved_errno;
+ int ifd = -1;
+ int ofd = -1;
+
+ if (sys_lstat (source, &source_stats) == -1)
+ return -1;
+
+ if (!S_ISREG (source_stats.st_mode))
+ return -1;
+
+ if((ifd = sys_open (source, O_RDONLY, 0)) < 0)
+ return -1;
+
+ if (unlink (dest) && errno != ENOENT)
+ return -1;
+
+#ifdef O_NOFOLLOW
+ if((ofd = sys_open (dest, O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, 0600)) < 0 )
+#else
+ if((ofd = sys_open (dest, O_WRONLY | O_CREAT | O_TRUNC , 0600)) < 0 )
+#endif
+ goto err;
+
+ if (transfer_file(ifd, ofd, (size_t)-1) == -1)
+ goto err;
+
+ /*
+ * Try to preserve ownership. For non-root it might fail, but that's ok.
+ * But root probably wants to know, e.g. if NFS disallows it.
+ */
+
+#ifdef HAVE_FCHOWN
+ if ((fchown(ofd, source_stats.st_uid, source_stats.st_gid) == -1) && (errno != EPERM))
+#else
+ if ((chown(dest, source_stats.st_uid, source_stats.st_gid) == -1) && (errno != EPERM))
+#endif
+ goto err;
+
+ /*
+ * fchown turns off set[ug]id bits for non-root,
+ * so do the chmod last.
+ */
+
+#if defined(HAVE_FCHMOD)
+ if (fchmod (ofd, source_stats.st_mode & 07777))
+#else
+ if (chmod (dest, source_stats.st_mode & 07777))
+#endif
+ goto err;
+
+ if (close (ifd) == -1)
+ goto err;
+
+ if (close (ofd) == -1)
+ return -1;
+
+ /* Try to copy the old file's modtime and access time. */
+ {
+ struct utimbuf tv;
+
+ tv.actime = source_stats.st_atime;
+ tv.modtime = source_stats.st_mtime;
+ utime(dest, &tv);
+ }
+
+ if (unlink (source) == -1)
+ return -1;
+
+ return 0;
+
+ err:
+
+ saved_errno = errno;
+ if (ifd != -1)
+ close(ifd);
+ if (ofd != -1)
+ close(ofd);
+ errno = saved_errno;
+ return -1;
+}
+
+int vfswrap_rename(vfs_handle_struct *handle, connection_struct *conn, const char *old, const char *new)
+{
+ int result;
+
+ START_PROFILE(syscall_rename);
+ result = rename(old, new);
+ if (errno == EXDEV) {
+ /* Rename across filesystems needed. */
+ result = copy_reg(old, new);
+ }
+
+ END_PROFILE(syscall_rename);
+ return result;
+}
+
+int vfswrap_fsync(vfs_handle_struct *handle, files_struct *fsp, int fd)
+{
+#ifdef HAVE_FSYNC
+ int result;
+
+ START_PROFILE(syscall_fsync);
+ result = fsync(fd);
+ END_PROFILE(syscall_fsync);
+ return result;
+#else
+ return 0;
+#endif
+}
+
+int vfswrap_stat(vfs_handle_struct *handle, connection_struct *conn, const char *fname, SMB_STRUCT_STAT *sbuf)
+{
+ int result;
+
+ START_PROFILE(syscall_stat);
+ result = sys_stat(fname, sbuf);
+ END_PROFILE(syscall_stat);
+ return result;
+}
+
+int vfswrap_fstat(vfs_handle_struct *handle, files_struct *fsp, int fd, SMB_STRUCT_STAT *sbuf)
+{
+ int result;
+
+ START_PROFILE(syscall_fstat);
+ result = sys_fstat(fd, sbuf);
+ END_PROFILE(syscall_fstat);
+ return result;
+}
+
+int vfswrap_lstat(vfs_handle_struct *handle, connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf)
+{
+ int result;
+
+ START_PROFILE(syscall_lstat);
+ result = sys_lstat(path, sbuf);
+ END_PROFILE(syscall_lstat);
+ return result;
+}
+
+int vfswrap_unlink(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ int result;
+
+ START_PROFILE(syscall_unlink);
+ result = unlink(path);
+ END_PROFILE(syscall_unlink);
+ return result;
+}
+
+int vfswrap_chmod(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode)
+{
+ int result;
+
+ START_PROFILE(syscall_chmod);
+
+ /*
+ * We need to do this due to the fact that the default POSIX ACL
+ * chmod modifies the ACL *mask* for the group owner, not the
+ * group owner bits directly. JRA.
+ */
+
+
+ {
+ int saved_errno = errno; /* We might get ENOSYS */
+ if ((result = SMB_VFS_CHMOD_ACL(conn, path, mode)) == 0) {
+ END_PROFILE(syscall_chmod);
+ return result;
+ }
+ /* Error - return the old errno. */
+ errno = saved_errno;
+ }
+
+ result = chmod(path, mode);
+ END_PROFILE(syscall_chmod);
+ return result;
+}
+
+int vfswrap_fchmod(vfs_handle_struct *handle, files_struct *fsp, int fd, mode_t mode)
+{
+ int result;
+
+ START_PROFILE(syscall_fchmod);
+
+ /*
+ * We need to do this due to the fact that the default POSIX ACL
+ * chmod modifies the ACL *mask* for the group owner, not the
+ * group owner bits directly. JRA.
+ */
+
+ {
+ int saved_errno = errno; /* We might get ENOSYS */
+ if ((result = SMB_VFS_FCHMOD_ACL(fsp, fd, mode)) == 0) {
+ END_PROFILE(syscall_chmod);
+ return result;
+ }
+ /* Error - return the old errno. */
+ errno = saved_errno;
+ }
+
+#if defined(HAVE_FCHMOD)
+ result = fchmod(fd, mode);
+#else
+ result = -1;
+ errno = ENOSYS;
+#endif
+
+ END_PROFILE(syscall_fchmod);
+ return result;
+}
+
+int vfswrap_chown(vfs_handle_struct *handle, connection_struct *conn, const char *path, uid_t uid, gid_t gid)
+{
+ int result;
+
+ START_PROFILE(syscall_chown);
+ result = sys_chown(path, uid, gid);
+ END_PROFILE(syscall_chown);
+ return result;
+}
+
+int vfswrap_fchown(vfs_handle_struct *handle, files_struct *fsp, int fd, uid_t uid, gid_t gid)
+{
+#ifdef HAVE_FCHOWN
+ int result;
+
+ START_PROFILE(syscall_fchown);
+ result = fchown(fd, uid, gid);
+ END_PROFILE(syscall_fchown);
+ return result;
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+int vfswrap_chdir(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ int result;
+
+ START_PROFILE(syscall_chdir);
+ result = chdir(path);
+ END_PROFILE(syscall_chdir);
+ return result;
+}
+
+char *vfswrap_getwd(vfs_handle_struct *handle, connection_struct *conn, char *path)
+{
+ char *result;
+
+ START_PROFILE(syscall_getwd);
+ result = sys_getwd(path);
+ END_PROFILE(syscall_getwd);
+ return result;
+}
+
+int vfswrap_utime(vfs_handle_struct *handle, connection_struct *conn, const char *path, struct utimbuf *times)
+{
+ int result;
+
+ START_PROFILE(syscall_utime);
+ result = utime(path, times);
+ END_PROFILE(syscall_utime);
+ return result;
+}
+
+/*********************************************************************
+ A version of ftruncate that will write the space on disk if strict
+ allocate is set.
+**********************************************************************/
+
+static int strict_allocate_ftruncate(vfs_handle_struct *handle, files_struct *fsp, int fd, SMB_OFF_T len)
+{
+ SMB_STRUCT_STAT st;
+ SMB_OFF_T currpos = SMB_VFS_LSEEK(fsp, fd, 0, SEEK_CUR);
+ unsigned char zero_space[4096];
+ SMB_OFF_T space_to_write;
+
+ if (currpos == -1)
+ return -1;
+
+ if (SMB_VFS_FSTAT(fsp, fd, &st) == -1)
+ return -1;
+
+ space_to_write = len - st.st_size;
+
+#ifdef S_ISFIFO
+ if (S_ISFIFO(st.st_mode))
+ return 0;
+#endif
+
+ if (st.st_size == len)
+ return 0;
+
+ /* Shrink - just ftruncate. */
+ if (st.st_size > len)
+ return sys_ftruncate(fd, len);
+
+ /* Write out the real space on disk. */
+ if (SMB_VFS_LSEEK(fsp, fd, st.st_size, SEEK_SET) != st.st_size)
+ return -1;
+
+ space_to_write = len - st.st_size;
+
+ memset(zero_space, '\0', sizeof(zero_space));
+ while ( space_to_write > 0) {
+ SMB_OFF_T retlen;
+ SMB_OFF_T current_len_to_write = MIN(sizeof(zero_space),space_to_write);
+
+ retlen = SMB_VFS_WRITE(fsp,fsp->fd,(char *)zero_space,current_len_to_write);
+ if (retlen <= 0)
+ return -1;
+
+ space_to_write -= retlen;
+ }
+
+ /* Seek to where we were */
+ if (SMB_VFS_LSEEK(fsp, fd, currpos, SEEK_SET) != currpos)
+ return -1;
+
+ return 0;
+}
+
+int vfswrap_ftruncate(vfs_handle_struct *handle, files_struct *fsp, int fd, SMB_OFF_T len)
+{
+ int result = -1;
+ SMB_STRUCT_STAT st;
+ char c = 0;
+ SMB_OFF_T currpos;
+
+ START_PROFILE(syscall_ftruncate);
+
+ if (lp_strict_allocate(SNUM(fsp->conn))) {
+ result = strict_allocate_ftruncate(handle, fsp, fd, len);
+ END_PROFILE(syscall_ftruncate);
+ return result;
+ }
+
+ /* we used to just check HAVE_FTRUNCATE_EXTEND and only use
+ sys_ftruncate if the system supports it. Then I discovered that
+ you can have some filesystems that support ftruncate
+ expansion and some that don't! On Linux fat can't do
+ ftruncate extend but ext2 can. */
+
+ result = sys_ftruncate(fd, len);
+ if (result == 0)
+ goto done;
+
+ /* According to W. R. Stevens advanced UNIX prog. Pure 4.3 BSD cannot
+ extend a file with ftruncate. Provide alternate implementation
+ for this */
+ currpos = SMB_VFS_LSEEK(fsp, fd, 0, SEEK_CUR);
+ if (currpos == -1) {
+ goto done;
+ }
+
+ /* Do an fstat to see if the file is longer than the requested
+ size in which case the ftruncate above should have
+ succeeded or shorter, in which case seek to len - 1 and
+ write 1 byte of zero */
+ if (SMB_VFS_FSTAT(fsp, fd, &st) == -1) {
+ goto done;
+ }
+
+#ifdef S_ISFIFO
+ if (S_ISFIFO(st.st_mode)) {
+ result = 0;
+ goto done;
+ }
+#endif
+
+ if (st.st_size == len) {
+ result = 0;
+ goto done;
+ }
+
+ if (st.st_size > len) {
+ /* the sys_ftruncate should have worked */
+ goto done;
+ }
+
+ if (SMB_VFS_LSEEK(fsp, fd, len-1, SEEK_SET) != len -1)
+ goto done;
+
+ if (SMB_VFS_WRITE(fsp, fd, &c, 1)!=1)
+ goto done;
+
+ /* Seek to where we were */
+ if (SMB_VFS_LSEEK(fsp, fd, currpos, SEEK_SET) != currpos)
+ goto done;
+ result = 0;
+
+ done:
+
+ END_PROFILE(syscall_ftruncate);
+ return result;
+}
+
+BOOL vfswrap_lock(vfs_handle_struct *handle, files_struct *fsp, int fd, int op, SMB_OFF_T offset, SMB_OFF_T count, int type)
+{
+ BOOL result;
+
+ START_PROFILE(syscall_fcntl_lock);
+ result = fcntl_lock(fd, op, offset, count,type);
+ END_PROFILE(syscall_fcntl_lock);
+ return result;
+}
+
+int vfswrap_symlink(vfs_handle_struct *handle, connection_struct *conn, const char *oldpath, const char *newpath)
+{
+ int result;
+
+ START_PROFILE(syscall_symlink);
+ result = sys_symlink(oldpath, newpath);
+ END_PROFILE(syscall_symlink);
+ return result;
+}
+
+int vfswrap_readlink(vfs_handle_struct *handle, connection_struct *conn, const char *path, char *buf, size_t bufsiz)
+{
+ int result;
+
+ START_PROFILE(syscall_readlink);
+ result = sys_readlink(path, buf, bufsiz);
+ END_PROFILE(syscall_readlink);
+ return result;
+}
+
+int vfswrap_link(vfs_handle_struct *handle, connection_struct *conn, const char *oldpath, const char *newpath)
+{
+ int result;
+
+ START_PROFILE(syscall_link);
+ result = sys_link(oldpath, newpath);
+ END_PROFILE(syscall_link);
+ return result;
+}
+
+int vfswrap_mknod(vfs_handle_struct *handle, connection_struct *conn, const char *pathname, mode_t mode, SMB_DEV_T dev)
+{
+ int result;
+
+ START_PROFILE(syscall_mknod);
+ result = sys_mknod(pathname, mode, dev);
+ END_PROFILE(syscall_mknod);
+ return result;
+}
+
+char *vfswrap_realpath(vfs_handle_struct *handle, connection_struct *conn, const char *path, char *resolved_path)
+{
+ char *result;
+
+ START_PROFILE(syscall_realpath);
+ result = sys_realpath(path, resolved_path);
+ END_PROFILE(syscall_realpath);
+ return result;
+}
+
+size_t vfswrap_fget_nt_acl(vfs_handle_struct *handle, files_struct *fsp, int fd, uint32 security_info, SEC_DESC **ppdesc)
+{
+ size_t result;
+
+ START_PROFILE(fget_nt_acl);
+ result = get_nt_acl(fsp, security_info, ppdesc);
+ END_PROFILE(fget_nt_acl);
+ return result;
+}
+
+size_t vfswrap_get_nt_acl(vfs_handle_struct *handle, files_struct *fsp, const char *name, uint32 security_info, SEC_DESC **ppdesc)
+{
+ size_t result;
+
+ START_PROFILE(get_nt_acl);
+ result = get_nt_acl(fsp, security_info, ppdesc);
+ END_PROFILE(get_nt_acl);
+ return result;
+}
+
+BOOL vfswrap_fset_nt_acl(vfs_handle_struct *handle, files_struct *fsp, int fd, uint32 security_info_sent, SEC_DESC *psd)
+{
+ BOOL result;
+
+ START_PROFILE(fset_nt_acl);
+ result = set_nt_acl(fsp, security_info_sent, psd);
+ END_PROFILE(fset_nt_acl);
+ return result;
+}
+
+BOOL vfswrap_set_nt_acl(vfs_handle_struct *handle, files_struct *fsp, const char *name, uint32 security_info_sent, SEC_DESC *psd)
+{
+ BOOL result;
+
+ START_PROFILE(set_nt_acl);
+ result = set_nt_acl(fsp, security_info_sent, psd);
+ END_PROFILE(set_nt_acl);
+ return result;
+}
+
+int vfswrap_chmod_acl(vfs_handle_struct *handle, connection_struct *conn, const char *name, mode_t mode)
+{
+#ifdef HAVE_NO_ACL
+ errno = ENOSYS;
+ return -1;
+#else
+ int result;
+
+ START_PROFILE(chmod_acl);
+ result = chmod_acl(conn, name, mode);
+ END_PROFILE(chmod_acl);
+ return result;
+#endif
+}
+
+int vfswrap_fchmod_acl(vfs_handle_struct *handle, files_struct *fsp, int fd, mode_t mode)
+{
+#ifdef HAVE_NO_ACL
+ errno = ENOSYS;
+ return -1;
+#else
+ int result;
+
+ START_PROFILE(fchmod_acl);
+ result = fchmod_acl(fsp, fd, mode);
+ END_PROFILE(fchmod_acl);
+ return result;
+#endif
+}
+
+int vfswrap_sys_acl_get_entry(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_T theacl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ return sys_acl_get_entry(theacl, entry_id, entry_p);
+}
+
+int vfswrap_sys_acl_get_tag_type(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+ return sys_acl_get_tag_type(entry_d, tag_type_p);
+}
+
+int vfswrap_sys_acl_get_permset(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+ return sys_acl_get_permset(entry_d, permset_p);
+}
+
+void * vfswrap_sys_acl_get_qualifier(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_ENTRY_T entry_d)
+{
+ return sys_acl_get_qualifier(entry_d);
+}
+
+SMB_ACL_T vfswrap_sys_acl_get_file(vfs_handle_struct *handle, connection_struct *conn, const char *path_p, SMB_ACL_TYPE_T type)
+{
+ return sys_acl_get_file(path_p, type);
+}
+
+SMB_ACL_T vfswrap_sys_acl_get_fd(vfs_handle_struct *handle, files_struct *fsp, int fd)
+{
+ return sys_acl_get_fd(fd);
+}
+
+int vfswrap_sys_acl_clear_perms(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_PERMSET_T permset)
+{
+ return sys_acl_clear_perms(permset);
+}
+
+int vfswrap_sys_acl_add_perm(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+ return sys_acl_add_perm(permset, perm);
+}
+
+char * vfswrap_sys_acl_to_text(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_T theacl, ssize_t *plen)
+{
+ return sys_acl_to_text(theacl, plen);
+}
+
+SMB_ACL_T vfswrap_sys_acl_init(vfs_handle_struct *handle, connection_struct *conn, int count)
+{
+ return sys_acl_init(count);
+}
+
+int vfswrap_sys_acl_create_entry(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+ return sys_acl_create_entry(pacl, pentry);
+}
+
+int vfswrap_sys_acl_set_tag_type(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype)
+{
+ return sys_acl_set_tag_type(entry, tagtype);
+}
+
+int vfswrap_sys_acl_set_qualifier(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_ENTRY_T entry, void *qual)
+{
+ return sys_acl_set_qualifier(entry, qual);
+}
+
+int vfswrap_sys_acl_set_permset(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset)
+{
+ return sys_acl_set_permset(entry, permset);
+}
+
+int vfswrap_sys_acl_valid(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_T theacl )
+{
+ return sys_acl_valid(theacl );
+}
+
+int vfswrap_sys_acl_set_file(vfs_handle_struct *handle, connection_struct *conn, const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+ return sys_acl_set_file(name, acltype, theacl);
+}
+
+int vfswrap_sys_acl_set_fd(vfs_handle_struct *handle, files_struct *fsp, int fd, SMB_ACL_T theacl)
+{
+ return sys_acl_set_fd(fd, theacl);
+}
+
+int vfswrap_sys_acl_delete_def_file(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ return sys_acl_delete_def_file(path);
+}
+
+int vfswrap_sys_acl_get_perm(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+ return sys_acl_get_perm(permset, perm);
+}
+
+int vfswrap_sys_acl_free_text(vfs_handle_struct *handle, connection_struct *conn, char *text)
+{
+ return sys_acl_free_text(text);
+}
+
+int vfswrap_sys_acl_free_acl(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_T posix_acl)
+{
+ return sys_acl_free_acl(posix_acl);
+}
+
+int vfswrap_sys_acl_free_qualifier(vfs_handle_struct *handle, connection_struct *conn, void *qualifier, SMB_ACL_TAG_T tagtype)
+{
+ return sys_acl_free_qualifier(qualifier, tagtype);
+}
+
+/****************************************************************
+ Extended attribute operations.
+*****************************************************************/
+
+ssize_t vfswrap_getxattr(struct vfs_handle_struct *handle,struct connection_struct *conn,const char *path, const char *name, void *value, size_t size)
+{
+ return sys_getxattr(path, name, value, size);
+}
+
+ssize_t vfswrap_lgetxattr(struct vfs_handle_struct *handle,struct connection_struct *conn,const char *path, const char *name, void *value, size_t size)
+{
+ return sys_lgetxattr(path, name, value, size);
+}
+
+ssize_t vfswrap_fgetxattr(struct vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name, void *value, size_t size)
+{
+ return sys_fgetxattr(fd, name, value, size);
+}
+
+ssize_t vfswrap_listxattr(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, char *list, size_t size)
+{
+ return sys_listxattr(path, list, size);
+}
+
+ssize_t vfswrap_llistxattr(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, char *list, size_t size)
+{
+ return sys_llistxattr(path, list, size);
+}
+
+ssize_t vfswrap_flistxattr(struct vfs_handle_struct *handle, struct files_struct *fsp,int fd, char *list, size_t size)
+{
+ return sys_flistxattr(fd, list, size);
+}
+
+int vfswrap_removexattr(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name)
+{
+ return sys_removexattr(path, name);
+}
+
+int vfswrap_lremovexattr(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name)
+{
+ return sys_lremovexattr(path, name);
+}
+
+int vfswrap_fremovexattr(struct vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name)
+{
+ return sys_fremovexattr(fd, name);
+}
+
+int vfswrap_setxattr(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, const void *value, size_t size, int flags)
+{
+ return sys_setxattr(path, name, value, size, flags);
+}
+
+int vfswrap_lsetxattr(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, const void *value, size_t size, int flags)
+{
+ return sys_lsetxattr(path, name, value, size, flags);
+}
+
+int vfswrap_fsetxattr(struct vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name, const void *value, size_t size, int flags)
+{
+ return sys_fsetxattr(fd, name, value, size, flags);
+}
diff --git a/source/smbd/vfs.c b/source/smbd/vfs.c
new file mode 100644
index 00000000000..4f3234775a2
--- /dev/null
+++ b/source/smbd/vfs.c
@@ -0,0 +1,951 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ VFS initialisation and support functions
+ Copyright (C) Tim Potter 1999
+ Copyright (C) Alexander Bokovoy 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ This work was sponsored by Optifacio Software Services, Inc.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_VFS
+
+struct vfs_init_function_entry {
+ char *name;
+ vfs_op_tuple *vfs_op_tuples;
+ struct vfs_init_function_entry *prev, *next;
+};
+
+static struct vfs_init_function_entry *backends = NULL;
+
+/* Some structures to help us initialise the vfs operations table */
+
+struct vfs_syminfo {
+ char *name;
+ void *fptr;
+};
+
+/* Default vfs hooks. WARNING: The order of these initialisers is
+ very important. They must be in the same order as defined in
+ vfs.h. Change at your own peril. */
+
+static struct vfs_ops default_vfs = {
+
+ {
+ /* Disk operations */
+
+ vfswrap_dummy_connect,
+ vfswrap_dummy_disconnect,
+ vfswrap_disk_free,
+ vfswrap_get_quota,
+ vfswrap_set_quota,
+ vfswrap_get_shadow_copy_data,
+
+ /* Directory operations */
+
+ vfswrap_opendir,
+ vfswrap_readdir,
+ vfswrap_mkdir,
+ vfswrap_rmdir,
+ vfswrap_closedir,
+
+ /* File operations */
+
+ vfswrap_open,
+ vfswrap_close,
+ vfswrap_read,
+ vfswrap_pread,
+ vfswrap_write,
+ vfswrap_pwrite,
+ vfswrap_lseek,
+ vfswrap_sendfile,
+ vfswrap_rename,
+ vfswrap_fsync,
+ vfswrap_stat,
+ vfswrap_fstat,
+ vfswrap_lstat,
+ vfswrap_unlink,
+ vfswrap_chmod,
+ vfswrap_fchmod,
+ vfswrap_chown,
+ vfswrap_fchown,
+ vfswrap_chdir,
+ vfswrap_getwd,
+ vfswrap_utime,
+ vfswrap_ftruncate,
+ vfswrap_lock,
+ vfswrap_symlink,
+ vfswrap_readlink,
+ vfswrap_link,
+ vfswrap_mknod,
+ vfswrap_realpath,
+
+ /* Windows ACL operations. */
+ vfswrap_fget_nt_acl,
+ vfswrap_get_nt_acl,
+ vfswrap_fset_nt_acl,
+ vfswrap_set_nt_acl,
+
+ /* POSIX ACL operations. */
+ vfswrap_chmod_acl,
+ vfswrap_fchmod_acl,
+
+ vfswrap_sys_acl_get_entry,
+ vfswrap_sys_acl_get_tag_type,
+ vfswrap_sys_acl_get_permset,
+ vfswrap_sys_acl_get_qualifier,
+ vfswrap_sys_acl_get_file,
+ vfswrap_sys_acl_get_fd,
+ vfswrap_sys_acl_clear_perms,
+ vfswrap_sys_acl_add_perm,
+ vfswrap_sys_acl_to_text,
+ vfswrap_sys_acl_init,
+ vfswrap_sys_acl_create_entry,
+ vfswrap_sys_acl_set_tag_type,
+ vfswrap_sys_acl_set_qualifier,
+ vfswrap_sys_acl_set_permset,
+ vfswrap_sys_acl_valid,
+ vfswrap_sys_acl_set_file,
+ vfswrap_sys_acl_set_fd,
+ vfswrap_sys_acl_delete_def_file,
+ vfswrap_sys_acl_get_perm,
+ vfswrap_sys_acl_free_text,
+ vfswrap_sys_acl_free_acl,
+ vfswrap_sys_acl_free_qualifier,
+
+ /* EA operations. */
+ vfswrap_getxattr,
+ vfswrap_lgetxattr,
+ vfswrap_fgetxattr,
+ vfswrap_listxattr,
+ vfswrap_llistxattr,
+ vfswrap_flistxattr,
+ vfswrap_removexattr,
+ vfswrap_lremovexattr,
+ vfswrap_fremovexattr,
+ vfswrap_setxattr,
+ vfswrap_lsetxattr,
+ vfswrap_fsetxattr
+ }
+};
+
+/****************************************************************************
+ maintain the list of available backends
+****************************************************************************/
+
+static struct vfs_init_function_entry *vfs_find_backend_entry(const char *name)
+{
+ struct vfs_init_function_entry *entry = backends;
+
+ while(entry) {
+ if (strcmp(entry->name, name)==0) return entry;
+ entry = entry->next;
+ }
+
+ return NULL;
+}
+
+NTSTATUS smb_register_vfs(int version, const char *name, vfs_op_tuple *vfs_op_tuples)
+{
+ struct vfs_init_function_entry *entry = backends;
+
+ if ((version != SMB_VFS_INTERFACE_VERSION)) {
+ DEBUG(0, ("Failed to register vfs module.\n"
+ "The module was compiled against SMB_VFS_INTERFACE_VERSION %d,\n"
+ "current SMB_VFS_INTERFACE_VERSION is %d.\n"
+ "Please recompile against the current Samba Version!\n",
+ version, SMB_VFS_INTERFACE_VERSION));
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ if (!name || !name[0] || !vfs_op_tuples) {
+ DEBUG(0,("smb_register_vfs() called with NULL pointer or empty name!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (vfs_find_backend_entry(name)) {
+ DEBUG(0,("VFS module %s already loaded!\n", name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ entry = smb_xmalloc(sizeof(struct vfs_init_function_entry));
+ entry->name = smb_xstrdup(name);
+ entry->vfs_op_tuples = vfs_op_tuples;
+
+ DLIST_ADD(backends, entry);
+ DEBUG(5, ("Successfully added vfs backend '%s'\n", name));
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ initialise default vfs hooks
+****************************************************************************/
+
+static void vfs_init_default(connection_struct *conn)
+{
+ DEBUG(3, ("Initialising default vfs hooks\n"));
+
+ memcpy(&conn->vfs.ops, &default_vfs.ops, sizeof(default_vfs.ops));
+ memcpy(&conn->vfs_opaque.ops, &default_vfs.ops, sizeof(default_vfs.ops));
+}
+
+/****************************************************************************
+ initialise custom vfs hooks
+ ****************************************************************************/
+
+BOOL vfs_init_custom(connection_struct *conn, const char *vfs_object)
+{
+ vfs_op_tuple *ops;
+ char *module_name = NULL;
+ char *module_param = NULL, *p;
+ int i;
+ vfs_handle_struct *handle;
+ struct vfs_init_function_entry *entry;
+
+ if (!conn||!vfs_object||!vfs_object[0]) {
+ DEBUG(0,("vfs_init_custon() called with NULL pointer or emtpy vfs_object!\n"));
+ return False;
+ }
+
+ if(!backends) static_init_vfs;
+
+ DEBUG(3, ("Initialising custom vfs hooks from [%s]\n", vfs_object));
+
+ module_name = smb_xstrdup(vfs_object);
+
+ p = strchr(module_name, ':');
+
+ if (p) {
+ *p = 0;
+ module_param = p+1;
+ trim_char(module_param, ' ', ' ');
+ }
+
+ trim_char(module_name, ' ', ' ');
+
+ /* First, try to load the module with the new module system */
+ if((entry = vfs_find_backend_entry(module_name)) ||
+ (NT_STATUS_IS_OK(smb_probe_module("vfs", module_name)) &&
+ (entry = vfs_find_backend_entry(module_name)))) {
+
+ DEBUGADD(5,("Successfully loaded vfs module [%s] with the new modules system\n", vfs_object));
+
+ if ((ops = entry->vfs_op_tuples) == NULL) {
+ DEBUG(0, ("entry->vfs_op_tuples==NULL for [%s] failed\n", vfs_object));
+ SAFE_FREE(module_name);
+ return False;
+ }
+ } else {
+ DEBUG(0,("Can't find a vfs module [%s]\n",vfs_object));
+ SAFE_FREE(module_name);
+ return False;
+ }
+
+ handle = (vfs_handle_struct *)talloc_zero(conn->mem_ctx,sizeof(vfs_handle_struct));
+ if (!handle) {
+ DEBUG(0,("talloc_zero() failed!\n"));
+ SAFE_FREE(module_name);
+ return False;
+ }
+ memcpy(&handle->vfs_next, &conn->vfs, sizeof(struct vfs_ops));
+ handle->conn = conn;
+ if (module_param) {
+ handle->param = talloc_strdup(conn->mem_ctx, module_param);
+ }
+ DLIST_ADD(conn->vfs_handles, handle);
+
+ for(i=0; ops[i].op != NULL; i++) {
+ DEBUG(5, ("Checking operation #%d (type %d, layer %d)\n", i, ops[i].type, ops[i].layer));
+ if(ops[i].layer == SMB_VFS_LAYER_OPAQUE) {
+ /* Check whether this operation was already made opaque by different module */
+ if(((void**)&conn->vfs_opaque.ops)[ops[i].type] == ((void**)&default_vfs.ops)[ops[i].type]) {
+ /* No, it isn't overloaded yet. Overload. */
+ DEBUGADD(5, ("Making operation type %d opaque [module %s]\n", ops[i].type, vfs_object));
+ ((void**)&conn->vfs_opaque.ops)[ops[i].type] = ops[i].op;
+ ((vfs_handle_struct **)&conn->vfs_opaque.handles)[ops[i].type] = handle;
+ }
+ }
+ /* Change current VFS disposition*/
+ DEBUGADD(5, ("Accepting operation type %d from module %s\n", ops[i].type, vfs_object));
+ ((void**)&conn->vfs.ops)[ops[i].type] = ops[i].op;
+ ((vfs_handle_struct **)&conn->vfs.handles)[ops[i].type] = handle;
+ }
+
+ SAFE_FREE(module_name);
+ return True;
+}
+
+/*****************************************************************
+ Generic VFS init.
+******************************************************************/
+
+BOOL smbd_vfs_init(connection_struct *conn)
+{
+ const char **vfs_objects;
+ unsigned int i = 0;
+ int j = 0;
+
+ /* Normal share - initialise with disk access functions */
+ vfs_init_default(conn);
+ vfs_objects = lp_vfs_objects(SNUM(conn));
+
+ /* Override VFS functions if 'vfs object' was not specified*/
+ if (!vfs_objects || !vfs_objects[0])
+ return True;
+
+ for (i=0; vfs_objects[i] ;) {
+ i++;
+ }
+
+ for (j=i-1; j >= 0; j--) {
+ if (!vfs_init_custom(conn, vfs_objects[j])) {
+ DEBUG(0, ("smbd_vfs_init: vfs_init_custom failed for %s\n", vfs_objects[j]));
+ return False;
+ }
+ }
+ return True;
+}
+
+/*******************************************************************
+ Check if directory exists.
+********************************************************************/
+
+BOOL vfs_directory_exist(connection_struct *conn, const char *dname, SMB_STRUCT_STAT *st)
+{
+ SMB_STRUCT_STAT st2;
+ BOOL ret;
+
+ if (!st)
+ st = &st2;
+
+ if (SMB_VFS_STAT(conn,dname,st) != 0)
+ return(False);
+
+ ret = S_ISDIR(st->st_mode);
+ if(!ret)
+ errno = ENOTDIR;
+
+ return ret;
+}
+
+/*******************************************************************
+ vfs mkdir wrapper
+********************************************************************/
+
+int vfs_MkDir(connection_struct *conn, const char *name, mode_t mode)
+{
+ int ret;
+ SMB_STRUCT_STAT sbuf;
+
+ if(!(ret=SMB_VFS_MKDIR(conn, name, mode))) {
+
+ inherit_access_acl(conn, name, mode);
+
+ /*
+ * Check if high bits should have been set,
+ * then (if bits are missing): add them.
+ * Consider bits automagically set by UNIX, i.e. SGID bit from parent dir.
+ */
+ if(mode & ~(S_IRWXU|S_IRWXG|S_IRWXO) &&
+ !SMB_VFS_STAT(conn,name,&sbuf) && (mode & ~sbuf.st_mode))
+ SMB_VFS_CHMOD(conn,name,sbuf.st_mode | (mode & ~sbuf.st_mode));
+ }
+ return ret;
+}
+
+/*******************************************************************
+ Check if an object exists in the vfs.
+********************************************************************/
+
+BOOL vfs_object_exist(connection_struct *conn,const char *fname,SMB_STRUCT_STAT *sbuf)
+{
+ SMB_STRUCT_STAT st;
+
+ if (!sbuf)
+ sbuf = &st;
+
+ ZERO_STRUCTP(sbuf);
+
+ if (SMB_VFS_STAT(conn,fname,sbuf) == -1)
+ return(False);
+ return True;
+}
+
+/*******************************************************************
+ Check if a file exists in the vfs.
+********************************************************************/
+
+BOOL vfs_file_exist(connection_struct *conn, const char *fname,SMB_STRUCT_STAT *sbuf)
+{
+ SMB_STRUCT_STAT st;
+
+ if (!sbuf)
+ sbuf = &st;
+
+ ZERO_STRUCTP(sbuf);
+
+ if (SMB_VFS_STAT(conn,fname,sbuf) == -1)
+ return False;
+ return(S_ISREG(sbuf->st_mode));
+}
+
+/****************************************************************************
+ Read data from fsp on the vfs. (note: EINTR re-read differs from vfs_write_data)
+****************************************************************************/
+
+ssize_t vfs_read_data(files_struct *fsp, char *buf, size_t byte_count)
+{
+ size_t total=0;
+
+ while (total < byte_count)
+ {
+ ssize_t ret = SMB_VFS_READ(fsp, fsp->fd, buf + total,
+ byte_count - total);
+
+ if (ret == 0) return total;
+ if (ret == -1) {
+ if (errno == EINTR)
+ continue;
+ else
+ return -1;
+ }
+ total += ret;
+ }
+ return (ssize_t)total;
+}
+
+ssize_t vfs_pread_data(files_struct *fsp, char *buf,
+ size_t byte_count, SMB_OFF_T offset)
+{
+ size_t total=0;
+
+ while (total < byte_count)
+ {
+ ssize_t ret = SMB_VFS_PREAD(fsp, fsp->fd, buf + total,
+ byte_count - total, offset + total);
+
+ if (ret == 0) return total;
+ if (ret == -1) {
+ if (errno == EINTR)
+ continue;
+ else
+ return -1;
+ }
+ total += ret;
+ }
+ return (ssize_t)total;
+}
+
+/****************************************************************************
+ Write data to a fd on the vfs.
+****************************************************************************/
+
+ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N)
+{
+ size_t total=0;
+ ssize_t ret;
+
+ while (total < N) {
+ ret = SMB_VFS_WRITE(fsp,fsp->fd,buffer + total,N - total);
+
+ if (ret == -1)
+ return -1;
+ if (ret == 0)
+ return total;
+
+ total += ret;
+ }
+ return (ssize_t)total;
+}
+
+ssize_t vfs_pwrite_data(files_struct *fsp,const char *buffer,
+ size_t N, SMB_OFF_T offset)
+{
+ size_t total=0;
+ ssize_t ret;
+
+ while (total < N) {
+ ret = SMB_VFS_PWRITE(fsp, fsp->fd, buffer + total,
+ N - total, offset + total);
+
+ if (ret == -1)
+ return -1;
+ if (ret == 0)
+ return total;
+
+ total += ret;
+ }
+ return (ssize_t)total;
+}
+/****************************************************************************
+ An allocate file space call using the vfs interface.
+ Allocates space for a file from a filedescriptor.
+ Returns 0 on success, -1 on failure.
+****************************************************************************/
+
+int vfs_allocate_file_space(files_struct *fsp, SMB_BIG_UINT len)
+{
+ int ret;
+ SMB_STRUCT_STAT st;
+ connection_struct *conn = fsp->conn;
+ SMB_BIG_UINT space_avail;
+ SMB_BIG_UINT bsize,dfree,dsize;
+
+ release_level_2_oplocks_on_change(fsp);
+
+ /*
+ * Actually try and commit the space on disk....
+ */
+
+ DEBUG(10,("vfs_allocate_file_space: file %s, len %.0f\n", fsp->fsp_name, (double)len ));
+
+ if (((SMB_OFF_T)len) < 0) {
+ DEBUG(0,("vfs_allocate_file_space: %s negative len requested.\n", fsp->fsp_name ));
+ return -1;
+ }
+
+ ret = SMB_VFS_FSTAT(fsp,fsp->fd,&st);
+ if (ret == -1)
+ return ret;
+
+ if (len == (SMB_BIG_UINT)st.st_size)
+ return 0;
+
+ if (len < (SMB_BIG_UINT)st.st_size) {
+ /* Shrink - use ftruncate. */
+
+ DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n",
+ fsp->fsp_name, (double)st.st_size ));
+
+ flush_write_cache(fsp, SIZECHANGE_FLUSH);
+ if ((ret = SMB_VFS_FTRUNCATE(fsp, fsp->fd, (SMB_OFF_T)len)) != -1) {
+ set_filelen_write_cache(fsp, len);
+ }
+ return ret;
+ }
+
+ /* Grow - we need to test if we have enough space. */
+
+ if (!lp_strict_allocate(SNUM(fsp->conn)))
+ return 0;
+
+ len -= st.st_size;
+ len /= 1024; /* Len is now number of 1k blocks needed. */
+ space_avail = SMB_VFS_DISK_FREE(conn,fsp->fsp_name,False,&bsize,&dfree,&dsize);
+
+ DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f, needed blocks = %.0f, space avail = %.0f\n",
+ fsp->fsp_name, (double)st.st_size, (double)len, (double)space_avail ));
+
+ if (len > space_avail) {
+ errno = ENOSPC;
+ return -1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ A vfs set_filelen call.
+ set the length of a file from a filedescriptor.
+ Returns 0 on success, -1 on failure.
+****************************************************************************/
+
+int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len)
+{
+ int ret;
+
+ release_level_2_oplocks_on_change(fsp);
+ DEBUG(10,("vfs_set_filelen: ftruncate %s to len %.0f\n", fsp->fsp_name, (double)len));
+ flush_write_cache(fsp, SIZECHANGE_FLUSH);
+ if ((ret = SMB_VFS_FTRUNCATE(fsp, fsp->fd, len)) != -1)
+ set_filelen_write_cache(fsp, len);
+
+ return ret;
+}
+
+/****************************************************************************
+ Transfer some data (n bytes) between two file_struct's.
+****************************************************************************/
+
+static files_struct *in_fsp;
+static files_struct *out_fsp;
+
+static ssize_t read_fn(int fd, void *buf, size_t len)
+{
+ return SMB_VFS_READ(in_fsp, fd, buf, len);
+}
+
+static ssize_t write_fn(int fd, const void *buf, size_t len)
+{
+ return SMB_VFS_WRITE(out_fsp, fd, buf, len);
+}
+
+SMB_OFF_T vfs_transfer_file(files_struct *in, files_struct *out, SMB_OFF_T n)
+{
+ in_fsp = in;
+ out_fsp = out;
+
+ return transfer_file_internal(in_fsp->fd, out_fsp->fd, n, read_fn, write_fn);
+}
+
+/*******************************************************************
+ A vfs_readdir wrapper which just returns the file name.
+********************************************************************/
+
+char *vfs_readdirname(connection_struct *conn, void *p)
+{
+ struct dirent *ptr= NULL;
+ char *dname;
+
+ if (!p)
+ return(NULL);
+
+ ptr = (struct dirent *)SMB_VFS_READDIR(conn,p);
+ if (!ptr)
+ return(NULL);
+
+ dname = ptr->d_name;
+
+#ifdef NEXT2
+ if (telldir(p) < 0)
+ return(NULL);
+#endif
+
+#ifdef HAVE_BROKEN_READDIR
+ /* using /usr/ucb/cc is BAD */
+ dname = dname - 2;
+#endif
+
+ return(dname);
+}
+
+/*******************************************************************
+ A wrapper for vfs_chdir().
+********************************************************************/
+
+int vfs_ChDir(connection_struct *conn, const char *path)
+{
+ int res;
+ static pstring LastDir="";
+
+ if (strcsequal(path,"."))
+ return(0);
+
+ if (*path == '/' && strcsequal(LastDir,path))
+ return(0);
+
+ DEBUG(4,("vfs_ChDir to %s\n",path));
+
+ res = SMB_VFS_CHDIR(conn,path);
+ if (!res)
+ pstrcpy(LastDir,path);
+ return(res);
+}
+
+/* number of list structures for a caching GetWd function. */
+#define MAX_GETWDCACHE (50)
+
+static struct {
+ SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
+ SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
+ char *dos_path; /* The pathname in DOS format. */
+ BOOL valid;
+} ino_list[MAX_GETWDCACHE];
+
+extern BOOL use_getwd_cache;
+
+/****************************************************************************
+ Prompte a ptr (to make it recently used)
+****************************************************************************/
+
+static void array_promote(char *array,int elsize,int element)
+{
+ char *p;
+ if (element == 0)
+ return;
+
+ p = (char *)malloc(elsize);
+
+ if (!p) {
+ DEBUG(5,("array_promote: malloc fail\n"));
+ return;
+ }
+
+ memcpy(p,array + element * elsize, elsize);
+ memmove(array + elsize,array,elsize*element);
+ memcpy(array,p,elsize);
+ SAFE_FREE(p);
+}
+
+/*******************************************************************
+ Return the absolute current directory path - given a UNIX pathname.
+ Note that this path is returned in DOS format, not UNIX
+ format. Note this can be called with conn == NULL.
+********************************************************************/
+
+char *vfs_GetWd(connection_struct *conn, char *path)
+{
+ pstring s;
+ static BOOL getwd_cache_init = False;
+ SMB_STRUCT_STAT st, st2;
+ int i;
+
+ *s = 0;
+
+ if (!use_getwd_cache)
+ return(SMB_VFS_GETWD(conn,path));
+
+ /* init the cache */
+ if (!getwd_cache_init) {
+ getwd_cache_init = True;
+ for (i=0;i<MAX_GETWDCACHE;i++) {
+ string_set(&ino_list[i].dos_path,"");
+ ino_list[i].valid = False;
+ }
+ }
+
+ /* Get the inode of the current directory, if this doesn't work we're
+ in trouble :-) */
+
+ if (SMB_VFS_STAT(conn, ".",&st) == -1) {
+ DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path));
+ return(SMB_VFS_GETWD(conn,path));
+ }
+
+
+ for (i=0; i<MAX_GETWDCACHE; i++) {
+ if (ino_list[i].valid) {
+
+ /* If we have found an entry with a matching inode and dev number
+ then find the inode number for the directory in the cached string.
+ If this agrees with that returned by the stat for the current
+ directory then all is o.k. (but make sure it is a directory all
+ the same...) */
+
+ if (st.st_ino == ino_list[i].inode && st.st_dev == ino_list[i].dev) {
+ if (SMB_VFS_STAT(conn,ino_list[i].dos_path,&st2) == 0) {
+ if (st.st_ino == st2.st_ino && st.st_dev == st2.st_dev &&
+ (st2.st_mode & S_IFMT) == S_IFDIR) {
+ pstrcpy (path, ino_list[i].dos_path);
+
+ /* promote it for future use */
+ array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
+ return (path);
+ } else {
+ /* If the inode is different then something's changed,
+ scrub the entry and start from scratch. */
+ ino_list[i].valid = False;
+ }
+ }
+ }
+ }
+ }
+
+ /* We don't have the information to hand so rely on traditional methods.
+ The very slow getcwd, which spawns a process on some systems, or the
+ not quite so bad getwd. */
+
+ if (!SMB_VFS_GETWD(conn,s)) {
+ DEBUG(0,("vfs_GetWd: SMB_VFS_GETWD call failed, errno %s\n",strerror(errno)));
+ return (NULL);
+ }
+
+ pstrcpy(path,s);
+
+ DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s,(double)st.st_ino,(double)st.st_dev));
+
+ /* add it to the cache */
+ i = MAX_GETWDCACHE - 1;
+ string_set(&ino_list[i].dos_path,s);
+ ino_list[i].dev = st.st_dev;
+ ino_list[i].inode = st.st_ino;
+ ino_list[i].valid = True;
+
+ /* put it at the top of the list */
+ array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
+
+ return (path);
+}
+
+
+/* check if the file 'nmae' is a symlink, in that case check that it point to
+ a file that reside under the 'dir' tree */
+
+static BOOL readlink_check(connection_struct *conn, const char *dir, char *name)
+{
+ BOOL ret = True;
+ pstring flink;
+ pstring cleanlink;
+ pstring savedir;
+ pstring realdir;
+ size_t reallen;
+
+ if (!vfs_GetWd(conn, savedir)) {
+ DEBUG(0,("couldn't vfs_GetWd for %s %s\n", name, dir));
+ return False;
+ }
+
+ if (vfs_ChDir(conn, dir) != 0) {
+ DEBUG(0,("couldn't vfs_ChDir to %s\n", dir));
+ return False;
+ }
+
+ if (!vfs_GetWd(conn, realdir)) {
+ DEBUG(0,("couldn't vfs_GetWd for %s\n", dir));
+ vfs_ChDir(conn, savedir);
+ return(False);
+ }
+
+ reallen = strlen(realdir);
+ if (realdir[reallen -1] == '/') {
+ reallen--;
+ realdir[reallen] = 0;
+ }
+
+ if (SMB_VFS_READLINK(conn, name, flink, sizeof(pstring) -1) != -1) {
+ DEBUG(3,("reduce_name: file path name %s is a symlink\nChecking it's path\n", name));
+ if (*flink == '/') {
+ pstrcpy(cleanlink, flink);
+ } else {
+ pstrcpy(cleanlink, realdir);
+ pstrcat(cleanlink, "/");
+ pstrcat(cleanlink, flink);
+ }
+ unix_clean_name(cleanlink);
+
+ if (strncmp(cleanlink, realdir, reallen) != 0) {
+ DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n", name, realdir, cleanlink, (int)reallen));
+ ret = False;
+ }
+ }
+
+ vfs_ChDir(conn, savedir);
+
+ return ret;
+}
+
+/*******************************************************************
+ Reduce a file name, removing .. elements and checking that
+ it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
+ on the system that has the referenced file system.
+********************************************************************/
+
+BOOL reduce_name(connection_struct *conn, pstring s, const char *dir)
+{
+#ifndef REDUCE_PATHS
+ return True;
+#else
+ pstring dir2;
+ pstring wd;
+ pstring base_name;
+ pstring newname;
+ char *p=NULL;
+ BOOL relative = (*s != '/');
+
+ *dir2 = *wd = *base_name = *newname = 0;
+
+ DEBUG(3,("reduce_name [%s] [%s]\n",s,dir));
+
+ /* We know there are no double slashes as this comes from srvstr_get_path().
+ and has gone through check_path_syntax(). JRA */
+
+ pstrcpy(base_name,s);
+ p = strrchr_m(base_name,'/');
+
+ if (!p)
+ return readlink_check(conn, dir, s);
+
+ if (!vfs_GetWd(conn,wd)) {
+ DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s,dir));
+ return(False);
+ }
+
+ if (vfs_ChDir(conn,dir) != 0) {
+ DEBUG(0,("couldn't vfs_ChDir to %s\n",dir));
+ return(False);
+ }
+
+ if (!vfs_GetWd(conn,dir2)) {
+ DEBUG(0,("couldn't vfs_GetWd for %s\n",dir));
+ vfs_ChDir(conn,wd);
+ return(False);
+ }
+
+ if (p && (p != base_name)) {
+ *p = 0;
+ if (strcmp(p+1,".")==0)
+ p[1]=0;
+ if (strcmp(p+1,"..")==0)
+ *p = '/';
+ }
+
+ if (vfs_ChDir(conn,base_name) != 0) {
+ vfs_ChDir(conn,wd);
+ DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s,dir,base_name));
+ return(False);
+ }
+
+ if (!vfs_GetWd(conn,newname)) {
+ vfs_ChDir(conn,wd);
+ DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,base_name));
+ return(False);
+ }
+
+ if (p && (p != base_name)) {
+ pstrcat(newname,"/");
+ pstrcat(newname,p+1);
+ }
+
+ {
+ size_t l = strlen(dir2);
+ char *last_slash = strrchr_m(dir2, '/');
+
+ if (last_slash && (last_slash[1] == '\0'))
+ l--;
+
+ if (strncmp(newname,dir2,l) != 0) {
+ vfs_ChDir(conn,wd);
+ DEBUG(2,("Bad access attempt: s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,(int)l));
+ return(False);
+ }
+
+ if (!readlink_check(conn, dir, newname)) {
+ DEBUG(2, ("Bad access attemt: %s is a symlink outside the share path", s));
+ return(False);
+ }
+
+ if (relative) {
+ if (newname[l] == '/')
+ pstrcpy(s,newname + l + 1);
+ else
+ pstrcpy(s,newname+l);
+ } else
+ pstrcpy(s,newname);
+ }
+
+ vfs_ChDir(conn,wd);
+
+ if (strlen(s) == 0)
+ pstrcpy(s,"./");
+
+ DEBUG(3,("reduced to %s\n",s));
+ return(True);
+#endif
+}
diff --git a/source/smbwrapper/.cvsignore b/source/smbwrapper/.cvsignore
new file mode 100644
index 00000000000..7835612d320
--- /dev/null
+++ b/source/smbwrapper/.cvsignore
@@ -0,0 +1,8 @@
+*.po
+*.po32
+kernel_stat.h
+smbsh
+tst
+tst.c
+wrapper.h
+xstat.c
diff --git a/source/smbwrapper/PORTING b/source/smbwrapper/PORTING
new file mode 100644
index 00000000000..884246d078a
--- /dev/null
+++ b/source/smbwrapper/PORTING
@@ -0,0 +1,77 @@
+This describes how to port the smbwrapper portion of Samba to a new
+unix-like platform. Note that porting smbwrapper is considerably
+harder than porting Samba, for Samba you generally just need to run
+configure and recompile whereas for smbwrapper some extra effort is
+generally required.
+
+
+STEP 1
+------
+
+The first step is to work out how to create a shared library on your
+OS and how to compile C code to produce position independent object
+files (PIC files). You shoud be able to find this information in the
+man pages for your compiler and loader (ld). Then modify configure.in
+to give that information to Samba.
+
+
+STEP 2
+------
+
+The next step is to work out how to preload shared objects. On many
+systems this is done using a LD_PRELOAD environment variable. On
+others (shc as IRIX) it may use a _RTL_LIST variable.
+
+To make sure it works I suggest you create two C files like this:
+
+/* first C file */
+main()
+{
+ unlink("foo.txt");
+}
+
+/* second C file */
+#include <stdio.h>
+
+int unlink(char *fname)
+{
+ fprintf(stderr,"unlink(%s) called\n",fname);
+}
+
+
+then compile the first as an ordinary C program and the second as a
+shared library. Then use LD_PRELOAD to preload the resulting shared
+library. Then run the first program. It should print "unlink(foo.txt)
+called". If it doesn't then consult your man pages till you get it
+right.
+
+Once you work this out then edit smbwrapper/smbsh.in and add a section
+if necessary to correctly set the necessary preload options for your
+OS.
+
+
+STEP 3
+------
+
+The next step is to work out how to make direct system calls. On most
+machines this will work without any source code changes to
+smbwrapper. To test that it does work create the following C program:
+
+#include <sys/syscall.h>
+main()
+{
+ syscall(SYS_write, 1, "hello world\n", 12);
+}
+
+and try to compile/run it. If it produces "hello world" then syscall()
+works as expected. If not then work out what needs to be changed and
+then make that change in realcalls.h. For example, on IRIX 6.4 the
+system call numbers are wrong and need to be fixed up by getting an
+offset right.
+
+
+STEP 4
+------
+
+Try compiling smbwrapper! Then test it. Then debug it. Simple really :)
+
diff --git a/source/smbwrapper/README b/source/smbwrapper/README
new file mode 100644
index 00000000000..8d5c376f825
--- /dev/null
+++ b/source/smbwrapper/README
@@ -0,0 +1,94 @@
+This is a prelodable shared library that provides SMB client services
+for existing executables. Using this you can simulate a smb
+filesystem.
+
+*** This is code under development. Some things don't work yet ***
+
+Currently this code has been tested on:
+
+- Linux 2.0 with glibc2 (RH5.1)
+- Linux 2.1 with glibc2
+- Solaris 2.5.1 with gcc
+- Solaris 2.6 with gcc
+- SunOS 4.1.3 with gcc
+- IRIX 6.4 with cc
+- OSF1 with gcc
+
+
+It probably won't run on other systems without some porting. If you
+have a different system then see the file PORTING.
+
+To use it you need to do this:
+
+1) build smbwrapper.so using the command "make smbwrapper"
+3) run smbsh
+
+You will be asked for a username and password. After that you will be
+returned to a shell prompt. It is actually a subshell running with
+smbwrapper enabled.
+
+Now try to access /smb/SERVER for some SMB server name and see what
+happens. If you use the -W option to set your workgroup or have
+workgroup set in your smb.conf then listing /smb/ should list all SMB
+servers in your workgroup.
+
+
+OPTIONS
+-------
+
+-U username
+ specify the username and optional password (as user%password)
+
+-d debug level
+ This is an integer that controls the internal debug level of smbw. It
+ defaults to 0, which means no debug info.
+
+-l logfile
+ The place where smbw debug logs are put. If this is not set then
+ stderr is used.
+
+-P prefix
+ The root of the SMB filesystem. This defaults to /smb/ but you can
+ set it to any name you like.
+
+-W workgroup
+ This is the workgroup used for browsing (ie. listing machines in the
+ /smb/ directory). It defaults to the one set in smb.conf.
+
+-R resolve order
+ This allows you to override the setting of the name resolve order
+ from smb.conf
+
+
+ATTRIBUTE MAPPING
+-----------------
+
+smbwrapper does an inverse attribute maping to what Samba does. This
+means that the archive bit appears as the user execute bit, the system
+bit appears as the group execute bit and the hidden bit appears as the
+other execute bit. You can control these with chmod. The mapping can
+be enabled an disabled using the normal smb.conf controls (ie. "map
+archive", "map system" and "map hidden").
+
+Read-only files appear as non-writeable by everyone. Writeable files
+appear as writeable by the current user.
+
+
+WHAT WORKS
+----------
+
+Things that I have tried and do seem to work include:
+
+ emacs, tar, ls, cmp, cp, rsync, du, cat, rm, mv, less, more, wc, head,
+ tail, bash, tcsh, mkdir, rmdir, vim, xedit, diff
+
+things that I know don't work:
+
+ anything executing from the share
+ anything that uses mmap
+ redirection within shells to smbsh files
+
+If you want to help with the development of this code then join the
+samba-technical mailing list.
+
+
diff --git a/source/smbwrapper/realcalls.c b/source/smbwrapper/realcalls.c
new file mode 100644
index 00000000000..b64f4a43dbf
--- /dev/null
+++ b/source/smbwrapper/realcalls.c
@@ -0,0 +1,48 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB wrapper functions for calls that syscall() can't do
+ Copyright (C) Andrew Tridgell 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "realcalls.h"
+
+#ifdef REPLACE_UTIME
+int real_utime(const char *name, struct utimbuf *buf)
+{
+ struct timeval tv[2];
+
+ tv[0].tv_sec = buf->actime;
+ tv[0].tv_usec = 0;
+ tv[1].tv_sec = buf->modtime;
+ tv[1].tv_usec = 0;
+
+ return real_utimes(name, &tv[0]);
+}
+#endif
+
+#ifdef REPLACE_UTIMES
+int real_utimes(const char *name, struct timeval tv[2])
+{
+ struct utimbuf buf;
+
+ buf.actime = tv[0].tv_sec;
+ buf.modtime = tv[1].tv_sec;
+
+ return real_utime(name, &buf);
+}
+#endif
diff --git a/source/smbwrapper/realcalls.h b/source/smbwrapper/realcalls.h
new file mode 100644
index 00000000000..6c230dba056
--- /dev/null
+++ b/source/smbwrapper/realcalls.h
@@ -0,0 +1,263 @@
+/*
+ Unix SMB/CIFS implementation.
+ defintions of syscall entries
+ Copyright (C) Andrew Tridgell 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#if HAVE_SYS_SYSCALL_H
+#include <sys/syscall.h>
+#elif HAVE_SYSCALL_H
+#include <syscall.h>
+#endif
+
+#ifdef IRIX
+/* amazingly, IRIX gets its own syscall numbers wrong! */
+#ifdef SYSVoffset
+#if (SYSVoffset == 1)
+#undef SYSVoffset
+#define SYSVoffset 1000
+#endif
+#endif
+#endif
+
+/* this file is partly derived from zlibc by Alain Knaff */
+
+#define real_access(fn, mode) (syscall(SYS_access, (fn), (mode)))
+#define real_chdir(fn) (syscall(SYS_chdir, (fn)))
+#define real_chmod(fn, mode) (syscall(SYS_chmod,(fn), (mode)))
+#define real_chown(fn, owner, group) (syscall(SYS_chown,(fn),(owner),(group)))
+
+#ifdef SYS_getdents
+#define real_getdents(fd, dirp, count) (syscall(SYS_getdents, (fd), (dirp), (count)))
+#endif
+
+#define real_link(fn1, fn2) (syscall(SYS_link, (fn1), (fn2)))
+
+#define real_open(fn,flags,mode) (syscall(SYS_open, (fn), (flags), (mode)))
+
+#ifdef SYS_open64
+#define real_open64(fn,flags,mode) (syscall(SYS_open64, (fn), (flags), (mode)))
+#elif HAVE__OPEN64
+#define real_open64(fn,flags,mode) (_open64(fn,flags,mode))
+#define NO_OPEN64_ALIAS
+#elif HAVE___OPEN64
+#define real_open64(fn,flags,mode) (__open64(fn,flags,mode))
+#define NO_OPEN64_ALIAS
+#endif
+
+#ifdef HAVE__FORK
+#define real_fork() (_fork())
+#elif HAVE___FORK
+#define real_fork() (__fork())
+#elif SYS_fork
+#define real_fork() (syscall(SYS_fork))
+#endif
+
+#ifdef HAVE__OPENDIR
+#define real_opendir(fn) (_opendir(fn))
+#elif SYS_opendir
+#define real_opendir(fn) (syscall(SYS_opendir,(fn)))
+#elif HAVE___OPENDIR
+#define real_opendir(fn) (__opendir(fn))
+#endif
+
+#ifdef HAVE__READDIR
+#define real_readdir(d) (_readdir(d))
+#elif HAVE___READDIR
+#define real_readdir(d) (__readdir(d))
+#elif SYS_readdir
+#define real_readdir(d) (syscall(SYS_readdir,(d)))
+#endif
+
+#ifdef HAVE__CLOSEDIR
+#define real_closedir(d) (_closedir(d))
+#elif SYS_closedir
+#define real_closedir(d) (syscall(SYS_closedir,(d)))
+#elif HAVE___CLOSEDIR
+#define real_closedir(d) (__closedir(d))
+#endif
+
+#ifdef HAVE__SEEKDIR
+#define real_seekdir(d,l) (_seekdir(d,l))
+#elif SYS_seekdir
+#define real_seekdir(d,l) (syscall(SYS_seekdir,(d),(l)))
+#elif HAVE___SEEKDIR
+#define real_seekdir(d,l) (__seekdir(d,l))
+#else
+#define NO_SEEKDIR_WRAPPER
+#endif
+
+#ifdef HAVE__TELLDIR
+#define real_telldir(d) (_telldir(d))
+#elif SYS_telldir
+#define real_telldir(d) (syscall(SYS_telldir,(d)))
+#elif HAVE___TELLDIR
+#define real_telldir(d) (__telldir(d))
+#endif
+
+#ifdef HAVE__DUP
+#define real_dup(d) (_dup(d))
+#elif SYS_dup
+#define real_dup(d) (syscall(SYS_dup,(d)))
+#elif HAVE___DUP
+#define real_dup(d) (__dup(d))
+#endif
+
+#ifdef HAVE__DUP2
+#define real_dup2(d1,d2) (_dup2(d1,d2))
+#elif SYS_dup2
+#define real_dup2(d1,d2) (syscall(SYS_dup2,(d1),(d2)))
+#elif HAVE___DUP2
+#define real_dup2(d1,d2) (__dup2(d1,d2))
+#endif
+
+#ifdef HAVE__GETCWD
+#define real_getcwd(b,s) ((char *)_getcwd(b,s))
+#elif SYS_getcwd
+#define real_getcwd(b,s) ((char *)syscall(SYS_getcwd,(b),(s)))
+#elif HAVE___GETCWD
+#define real_getcwd(b,s) ((char *)__getcwd(b,s))
+#endif
+
+#ifdef HAVE__STAT
+#define real_stat(fn,st) (_stat(fn,st))
+#elif SYS_stat
+#define real_stat(fn,st) (syscall(SYS_stat,(fn),(st)))
+#elif HAVE___STAT
+#define real_stat(fn,st) (__stat(fn,st))
+#endif
+
+#ifdef HAVE__LSTAT
+#define real_lstat(fn,st) (_lstat(fn,st))
+#elif SYS_lstat
+#define real_lstat(fn,st) (syscall(SYS_lstat,(fn),(st)))
+#elif HAVE___LSTAT
+#define real_lstat(fn,st) (__lstat(fn,st))
+#endif
+
+#ifdef HAVE__FSTAT
+#define real_fstat(fd,st) (_fstat(fd,st))
+#elif SYS_fstat
+#define real_fstat(fd,st) (syscall(SYS_fstat,(fd),(st)))
+#elif HAVE___FSTAT
+#define real_fstat(fd,st) (__fstat(fd,st))
+#endif
+
+#if defined(HAVE_SYS_ACL_H) && defined(HAVE__ACL)
+#define real_acl(fn,cmd,n,buf) (_acl(fn,cmd,n,buf))
+#elif SYS_acl
+#define real_acl(fn,cmd,n,buf) (syscall(SYS_acl,(fn),(cmd),(n),(buf)))
+#elif HAVE___ACL
+#define real_acl(fn,cmd,n,buf) (__acl(fn,cmd,n,buf))
+#else
+#define NO_ACL_WRAPPER
+#endif
+
+#ifdef HAVE__FACL
+#define real_facl(fd,cmd,n,buf) (_facl(fd,cmd,n,buf))
+#elif SYS_facl
+#define real_facl(fd,cmd,n,buf) (syscall(SYS_facl,(fd),(cmd),(n),(buf)))
+#elif HAVE___FACL
+#define real_facl(fd,cmd,n,buf) (__facl(fd,cmd,n,buf))
+#else
+#define NO_FACL_WRAPPER
+#endif
+
+
+#ifdef HAVE__STAT64
+#define real_stat64(fn,st) (_stat64(fn,st))
+#elif HAVE___STAT64
+#define real_stat64(fn,st) (__stat64(fn,st))
+#endif
+
+#ifdef HAVE__LSTAT64
+#define real_lstat64(fn,st) (_lstat64(fn,st))
+#elif HAVE___LSTAT64
+#define real_lstat64(fn,st) (__lstat64(fn,st))
+#endif
+
+#ifdef HAVE__FSTAT64
+#define real_fstat64(fd,st) (_fstat64(fd,st))
+#elif HAVE___FSTAT64
+#define real_fstat64(fd,st) (__fstat64(fd,st))
+#endif
+
+#ifdef HAVE__READDIR64
+#define real_readdir64(d) (_readdir64(d))
+#elif HAVE___READDIR64
+#define real_readdir64(d) (__readdir64(d))
+#endif
+
+#ifdef HAVE__LLSEEK
+#define real_llseek(fd,ofs,whence) (_llseek(fd,ofs,whence))
+#elif HAVE___LLSEEK
+#define real_llseek(fd,ofs,whence) (__llseek(fd,ofs,whence))
+#elif HAVE___SYS_LLSEEK
+#define real_llseek(fd,ofs,whence) (__sys_llseek(fd,ofs,whence))
+#endif
+
+
+#ifdef HAVE__PREAD
+#define real_pread(fd,buf,size,ofs) (_pread(fd,buf,size,ofs))
+#elif HAVE___PREAD
+#define real_pread(fd,buf,size,ofs) (__pread(fd,buf,size,ofs))
+#endif
+
+#ifdef HAVE__PREAD64
+#define real_pread64(fd,buf,size,ofs) (_pread64(fd,buf,size,ofs))
+#elif HAVE___PREAD64
+#define real_pread64(fd,buf,size,ofs) (__pread64(fd,buf,size,ofs))
+#endif
+
+#ifdef HAVE__PWRITE
+#define real_pwrite(fd,buf,size,ofs) (_pwrite(fd,buf,size,ofs))
+#elif HAVE___PWRITE
+#define real_pwrite(fd,buf,size,ofs) (__pwrite(fd,buf,size,ofs))
+#endif
+
+#ifdef HAVE__PWRITE64
+#define real_pwrite64(fd,buf,size,ofs) (_pwrite64(fd,buf,size,ofs))
+#elif HAVE___PWRITE64
+#define real_pwrite64(fd,buf,size,ofs) (__pwrite64(fd,buf,size,ofs))
+#endif
+
+
+#define real_readlink(fn,buf,len) (syscall(SYS_readlink, (fn), (buf), (len)))
+#define real_rename(fn1, fn2) (syscall(SYS_rename, (fn1), (fn2)))
+#define real_symlink(fn1, fn2) (syscall(SYS_symlink, (fn1), (fn2)))
+#define real_read(fd, buf, count ) (syscall(SYS_read, (fd), (buf), (count)))
+#define real_lseek(fd, offset, whence) (syscall(SYS_lseek, (fd), (offset), (whence)))
+#define real_write(fd, buf, count ) (syscall(SYS_write, (fd), (buf), (count)))
+#define real_close(fd) (syscall(SYS_close, (fd)))
+#define real_fchdir(fd) (syscall(SYS_fchdir, (fd)))
+#define real_fcntl(fd,cmd,arg) (syscall(SYS_fcntl, (fd), (cmd), (arg)))
+#define real_symlink(fn1, fn2) (syscall(SYS_symlink, (fn1), (fn2)))
+#define real_unlink(fn) (syscall(SYS_unlink, (fn)))
+#define real_rmdir(fn) (syscall(SYS_rmdir, (fn)))
+#define real_mkdir(fn, mode) (syscall(SYS_mkdir, (fn), (mode)))
+
+#ifdef SYS_utime
+#define real_utime(fn, buf) (syscall(SYS_utime, (fn), (buf)))
+#else
+#define REPLACE_UTIME 1
+#endif
+
+#ifdef SYS_utimes
+#define real_utimes(fn, buf) (syscall(SYS_utimes, (fn), (buf)))
+#else
+#define REPLACE_UTIMES 1
+#endif
diff --git a/source/smbwrapper/shared.c b/source/smbwrapper/shared.c
new file mode 100644
index 00000000000..ca8df5841d1
--- /dev/null
+++ b/source/smbwrapper/shared.c
@@ -0,0 +1,203 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB wrapper functions - shared variables
+ Copyright (C) Andrew Tridgell 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static int shared_fd;
+static char *variables;
+static int shared_size;
+
+/*****************************************************
+setup the shared area
+*******************************************************/
+void smbw_setup_shared(void)
+{
+ int fd;
+ pstring name, s;
+
+ slprintf(name,sizeof(name)-1, "%s/smbw.XXXXXX",tmpdir());
+
+ fd = smb_mkstemp(name);
+
+ if (fd == -1) goto failed;
+
+ unlink(name);
+
+ shared_fd = set_maxfiles(SMBW_MAX_OPEN);
+
+ while (shared_fd && dup2(fd, shared_fd) != shared_fd) shared_fd--;
+
+ if (shared_fd == 0) goto failed;
+
+ close(fd);
+
+ DEBUG(4,("created shared_fd=%d\n", shared_fd));
+
+ slprintf(s,sizeof(s)-1,"%d", shared_fd);
+
+ setenv("SMBW_HANDLE", s, 1);
+
+ return;
+
+ failed:
+ perror("Failed to setup shared variable area ");
+ exit(1);
+}
+
+static int locked;
+
+/*****************************************************
+lock the shared variable area
+*******************************************************/
+static void lockit(void)
+{
+ if (shared_fd == 0) {
+ char *p = getenv("SMBW_HANDLE");
+ if (!p) {
+ DEBUG(0,("ERROR: can't get smbw shared handle\n"));
+ exit(1);
+ }
+ shared_fd = atoi(p);
+ }
+ if (locked==0 &&
+ fcntl_lock(shared_fd,SMB_F_SETLKW,0,1,F_WRLCK)==False) {
+ DEBUG(0,("ERROR: can't get smbw shared lock (%s)\n", strerror(errno)));
+ exit(1);
+ }
+ locked++;
+}
+
+/*****************************************************
+unlock the shared variable area
+*******************************************************/
+static void unlockit(void)
+{
+ locked--;
+ if (locked == 0) {
+ fcntl_lock(shared_fd,SMB_F_SETLK,0,1,F_UNLCK);
+ }
+}
+
+
+/*****************************************************
+get a variable from the shared area
+*******************************************************/
+char *smbw_getshared(const char *name)
+{
+ int i;
+ struct stat st;
+ char *var;
+
+ lockit();
+
+ /* maybe the area has changed */
+ if (fstat(shared_fd, &st)) goto failed;
+
+ if (st.st_size != shared_size) {
+ var = (char *)Realloc(variables, st.st_size);
+ if (!var) goto failed;
+ else variables = var;
+ shared_size = st.st_size;
+ lseek(shared_fd, 0, SEEK_SET);
+ if (read(shared_fd, variables, shared_size) != shared_size) {
+ goto failed;
+ }
+ }
+
+ unlockit();
+
+ i=0;
+ while (i < shared_size) {
+ char *n, *v;
+ int l1, l2;
+
+ l1 = SVAL(&variables[i], 0);
+ l2 = SVAL(&variables[i], 2);
+
+ n = &variables[i+4];
+ v = &variables[i+4+l1];
+ i += 4+l1+l2;
+
+ if (strcmp(name,n)) {
+ continue;
+ }
+ return v;
+ }
+
+ return NULL;
+
+ failed:
+ DEBUG(0,("smbw: shared variables corrupt (%s)\n", strerror(errno)));
+ exit(1);
+ return NULL;
+}
+
+
+
+/*****************************************************
+set a variable in the shared area
+*******************************************************/
+void smbw_setshared(const char *name, const char *val)
+{
+ int l1, l2;
+ char *var;
+
+ /* we don't allow variable overwrite */
+ if (smbw_getshared(name)) return;
+
+ lockit();
+
+ l1 = strlen(name)+1;
+ l2 = strlen(val)+1;
+
+ var = (char *)Realloc(variables, shared_size + l1+l2+4);
+
+ if (!var) {
+ DEBUG(0,("out of memory in smbw_setshared\n"));
+ exit(1);
+ }
+
+ variables = var;
+
+ SSVAL(&variables[shared_size], 0, l1);
+ SSVAL(&variables[shared_size], 2, l2);
+
+ safe_strcpy(&variables[shared_size] + 4, name, l1-1);
+ safe_strcpy(&variables[shared_size] + 4 + l1, val, l2-1);
+
+ shared_size += l1+l2+4;
+
+ lseek(shared_fd, 0, SEEK_SET);
+ if (write(shared_fd, variables, shared_size) != shared_size) {
+ DEBUG(0,("smbw_setshared failed (%s)\n", strerror(errno)));
+ exit(1);
+ }
+
+ unlockit();
+}
+
+
+/*****************************************************************
+return true if the passed fd is the SMBW_HANDLE
+*****************************************************************/
+int smbw_shared_fd(int fd)
+{
+ return (shared_fd && shared_fd == fd);
+}
diff --git a/source/smbwrapper/smbsh.c b/source/smbwrapper/smbsh.c
new file mode 100644
index 00000000000..7a2b8e09d7a
--- /dev/null
+++ b/source/smbwrapper/smbsh.c
@@ -0,0 +1,127 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB wrapper functions - frontend
+ Copyright (C) Andrew Tridgell 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static void smbsh_usage(void)
+{
+ printf("smbsh [options]\n\n");
+ printf(" -W workgroup\n");
+ printf(" -U username\n");
+ printf(" -P prefix\n");
+ printf(" -R resolve order\n");
+ printf(" -d debug level\n");
+ printf(" -l logfile\n");
+ printf(" -L libdir\n");
+ exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ char *p, *u;
+ const char *libd = dyn_LIBDIR;
+ pstring line, wd;
+ int opt;
+ extern char *optarg;
+ extern int optind;
+
+ dbf = x_stdout;
+ smbw_setup_shared();
+
+ while ((opt = getopt(argc, argv, "W:U:R:d:P:l:hL:")) != EOF) {
+ switch (opt) {
+ case 'L':
+ libd = optarg;
+ break;
+ case 'W':
+ smbw_setshared("WORKGROUP", optarg);
+ break;
+ case 'l':
+ smbw_setshared("LOGFILE", optarg);
+ break;
+ case 'P':
+ smbw_setshared("PREFIX", optarg);
+ break;
+ case 'd':
+ smbw_setshared("DEBUG", optarg);
+ break;
+ case 'U':
+ p = strchr_m(optarg,'%');
+ if (p) {
+ *p=0;
+ smbw_setshared("PASSWORD",p+1);
+ }
+ smbw_setshared("USER", optarg);
+ break;
+ case 'R':
+ smbw_setshared("RESOLVE_ORDER",optarg);
+ break;
+
+ case 'h':
+ default:
+ smbsh_usage();
+ }
+ }
+
+
+ if (!smbw_getshared("USER")) {
+ printf("Username: ");
+ u = fgets_slash(line, sizeof(line)-1, x_stdin);
+ smbw_setshared("USER", u);
+ }
+
+ if (!smbw_getshared("PASSWORD")) {
+ p = getpass("Password: ");
+ smbw_setshared("PASSWORD", p);
+ }
+
+ setenv("PS1", "smbsh$ ", 1);
+
+ sys_getwd(wd);
+
+ slprintf(line,sizeof(line)-1,"PWD_%d", (int)getpid());
+
+ smbw_setshared(line, wd);
+
+ slprintf(line,sizeof(line)-1,"%s/smbwrapper.so", libd);
+ setenv("LD_PRELOAD", line, 1);
+
+ slprintf(line,sizeof(line)-1,"%s/smbwrapper.32.so", libd);
+
+ if (file_exist(line, NULL)) {
+ slprintf(line,sizeof(line)-1,"%s/smbwrapper.32.so:DEFAULT", libd);
+ setenv("_RLD_LIST", line, 1);
+ slprintf(line,sizeof(line)-1,"%s/smbwrapper.so:DEFAULT", libd);
+ setenv("_RLDN32_LIST", line, 1);
+ } else {
+ slprintf(line,sizeof(line)-1,"%s/smbwrapper.so:DEFAULT", libd);
+ setenv("_RLD_LIST", line, 1);
+ }
+
+ {
+ char *shellpath = getenv("SHELL");
+ if(shellpath)
+ execl(shellpath,"smbsh",NULL);
+ else
+ execl("/bin/sh","smbsh",NULL);
+ }
+ printf("launch failed!\n");
+ return 1;
+}
diff --git a/source/smbwrapper/smbsh.in b/source/smbwrapper/smbsh.in
new file mode 100644
index 00000000000..323f0916993
--- /dev/null
+++ b/source/smbwrapper/smbsh.in
@@ -0,0 +1,54 @@
+#! /bin/sh
+
+SMBW_LIBDIR=${SMBW_LIBDIR-@builddir@/smbwrapper}
+
+if [ ! -f ${SMBW_LIBDIR}/smbwrapper.so ]; then
+ echo You need to set LIBDIR in smbsh
+ exit
+fi
+
+# a simple launcher for the smbwrapper.so preloadde library
+
+if [ x"${SMBW_USER+set}" != x"set" ]; then
+ echo username?
+ read user
+ SMBW_USER=$user
+ export SMBW_USER
+fi
+
+# this doesn't hide the password - we need a proper launch app for that
+if [ x"${SMBW_PASSWORD+set}" != x"set" ]; then
+ echo password?
+ read pass
+ SMBW_PASSWORD=$pass
+ export SMBW_PASSWORD
+fi
+
+PWD=`pwd`
+export PWD
+PS1='smbsh$ '
+export PS1
+
+
+host_os=@HOST_OS@
+
+case "$host_os" in
+ *irix*)
+ _RLDN32_LIST=$SMBW_LIBDIR/smbwrapper.so:DEFAULT
+ _RLD_LIST=$SMBW_LIBDIR/smbwrapper.32.so:DEFAULT
+ export _RLDN32_LIST
+ export _RLD_LIST
+ ;;
+ *osf*)
+ _RLD_LIST=$SMBW_LIBDIR/smbwrapper.so:DEFAULT
+ export _RLD_LIST
+ ;;
+ *)
+ LD_PRELOAD=$SMBW_LIBDIR/smbwrapper.so
+ export LD_PRELOAD
+ ;;
+esac
+
+echo starting smbwrapper on $host_os
+
+exec ${SMBW_SHELL-${SHELL-/bin/sh}} ${1+"$@"}
diff --git a/source/smbwrapper/smbw.c b/source/smbwrapper/smbw.c
new file mode 100644
index 00000000000..0ddacdf8ba5
--- /dev/null
+++ b/source/smbwrapper/smbw.c
@@ -0,0 +1,1562 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB wrapper functions
+ Copyright (C) Andrew Tridgell 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "realcalls.h"
+
+pstring smbw_cwd;
+
+static struct smbw_file *smbw_files;
+static struct smbw_server *smbw_srvs;
+
+struct bitmap *smbw_file_bmap;
+
+fstring smbw_prefix = SMBW_PREFIX;
+
+int smbw_busy=0;
+
+/* needs to be here because of dumb include files on some systems */
+int creat_bits = O_WRONLY|O_CREAT|O_TRUNC;
+
+
+/*****************************************************
+initialise structures
+*******************************************************/
+void smbw_init(void)
+{
+ extern BOOL in_client;
+ static int initialised;
+ char *p;
+ int eno;
+ pstring line;
+
+ if (initialised) return;
+ initialised = 1;
+
+ eno = errno;
+
+ smbw_busy++;
+
+ DEBUGLEVEL = 0;
+ setup_logging("smbsh",True);
+
+ dbf = x_stderr;
+
+ if ((p=smbw_getshared("LOGFILE"))) {
+ dbf = sys_fopen(p, "a");
+ }
+
+ smbw_file_bmap = bitmap_allocate(SMBW_MAX_OPEN);
+ if (!smbw_file_bmap) {
+ exit(1);
+ }
+
+ in_client = True;
+
+ load_interfaces();
+
+ if ((p=smbw_getshared("SERVICESF"))) {
+ pstrcpy(dyn_CONFIGFILE, p);
+ }
+
+ lp_load(dyn_CONFIGFILE,True,False,False);
+
+ if (!init_names())
+ exit(1);
+
+ if ((p=smbw_getshared("DEBUG"))) {
+ DEBUGLEVEL = atoi(p);
+ }
+
+ if ((p=smbw_getshared("RESOLVE_ORDER"))) {
+ lp_set_name_resolve_order(p);
+ }
+
+ if ((p=smbw_getshared("PREFIX"))) {
+ slprintf(smbw_prefix,sizeof(fstring)-1, "/%s/", p);
+ all_string_sub(smbw_prefix,"//", "/", 0);
+ DEBUG(2,("SMBW_PREFIX is %s\n", smbw_prefix));
+ }
+
+ slprintf(line,sizeof(line)-1,"PWD_%d", (int)getpid());
+
+ p = smbw_getshared(line);
+ if (!p) {
+ sys_getwd(smbw_cwd);
+ }
+ pstrcpy(smbw_cwd, p);
+ DEBUG(4,("Initial cwd is %s\n", smbw_cwd));
+
+ smbw_busy--;
+
+ set_maxfiles(SMBW_MAX_OPEN);
+
+ BlockSignals(True,SIGPIPE);
+
+ errno = eno;
+}
+
+/*****************************************************
+determine if a file descriptor is a smb one
+*******************************************************/
+int smbw_fd(int fd)
+{
+ if (smbw_busy) return 0;
+ return smbw_file_bmap && bitmap_query(smbw_file_bmap, fd);
+}
+
+/*****************************************************
+determine if a file descriptor is an internal smbw fd
+*******************************************************/
+int smbw_local_fd(int fd)
+{
+ struct smbw_server *srv;
+
+ smbw_init();
+
+ if (smbw_busy) return 0;
+ if (smbw_shared_fd(fd)) return 1;
+
+ for (srv=smbw_srvs;srv;srv=srv->next) {
+ if (srv->cli.fd == fd) return 1;
+ }
+
+ return 0;
+}
+
+/*****************************************************
+a crude inode number generator
+*******************************************************/
+ino_t smbw_inode(const char *name)
+{
+ if (!*name) return 2;
+ return (ino_t)str_checksum(name);
+}
+
+/*****************************************************
+remove redundent stuff from a filename
+*******************************************************/
+void clean_fname(char *name)
+{
+ char *p, *p2;
+ int l;
+ int modified = 1;
+
+ if (!name) return;
+
+ while (modified) {
+ modified = 0;
+
+ DEBUG(5,("cleaning %s\n", name));
+
+ if ((p=strstr(name,"/./"))) {
+ modified = 1;
+ while (*p) {
+ p[0] = p[2];
+ p++;
+ }
+ }
+
+ if ((p=strstr(name,"//"))) {
+ modified = 1;
+ while (*p) {
+ p[0] = p[1];
+ p++;
+ }
+ }
+
+ if (strcmp(name,"/../")==0) {
+ modified = 1;
+ name[1] = 0;
+ }
+
+ if ((p=strstr(name,"/../"))) {
+ modified = 1;
+ for (p2=(p>name?p-1:p);p2>name;p2--) {
+ if (p2[0] == '/') break;
+ }
+ while (*p2) {
+ p2[0] = p2[3];
+ p2++;
+ }
+ }
+
+ if (strcmp(name,"/..")==0) {
+ modified = 1;
+ name[1] = 0;
+ }
+
+ l = strlen(name);
+ p = l>=3?(name+l-3):name;
+ if (strcmp(p,"/..")==0) {
+ modified = 1;
+ for (p2=p-1;p2>name;p2--) {
+ if (p2[0] == '/') break;
+ }
+ if (p2==name) {
+ p[0] = '/';
+ p[1] = 0;
+ } else {
+ p2[0] = 0;
+ }
+ }
+
+ l = strlen(name);
+ p = l>=2?(name+l-2):name;
+ if (strcmp(p,"/.")==0) {
+ if (p == name) {
+ p[1] = 0;
+ } else {
+ p[0] = 0;
+ }
+ }
+
+ if (strncmp(p=name,"./",2) == 0) {
+ modified = 1;
+ do {
+ p[0] = p[2];
+ } while (*p++);
+ }
+
+ l = strlen(p=name);
+ if (l > 1 && p[l-1] == '/') {
+ modified = 1;
+ p[l-1] = 0;
+ }
+ }
+}
+
+
+
+/*****************************************************
+find a workgroup (any workgroup!) that has a master
+browser on the local network
+*******************************************************/
+static char *smbw_find_workgroup(void)
+{
+ fstring server;
+ char *p;
+ struct in_addr *ip_list = NULL;
+ int count = 0;
+ int i;
+
+ /* first off see if an existing workgroup name exists */
+ p = smbw_getshared("WORKGROUP");
+ if (!p) p = lp_workgroup();
+
+ slprintf(server, sizeof(server), "%s#1D", p);
+ if (smbw_server(server, "IPC$")) return p;
+
+ /* go looking for workgroups */
+ if (!name_resolve_bcast(MSBROWSE, 1, &ip_list, &count)) {
+ DEBUG(1,("No workgroups found!"));
+ return p;
+ }
+
+ for (i=0;i<count;i++) {
+ static fstring name;
+ if (name_status_find("*", 0, 0x1d, ip_list[i], name)) {
+ slprintf(server, sizeof(server), "%s#1D", name);
+ if (smbw_server(server, "IPC$")) {
+ smbw_setshared("WORKGROUP", name);
+ SAFE_FREE(ip_list);
+ return name;
+ }
+ }
+ }
+
+ SAFE_FREE(ip_list);
+
+ return p;
+}
+
+/*****************************************************
+parse a smb path into its components.
+server is one of
+ 1) the name of the SMB server
+ 2) WORKGROUP#1D for share listing
+ 3) WORKGROUP#__ for workgroup listing
+share is the share on the server to query
+path is the SMB path on the server
+return the full path (ie. add cwd if needed)
+*******************************************************/
+char *smbw_parse_path(const char *fname, char *server, char *share, char *path)
+{
+ static pstring s;
+ char *p;
+ int len;
+ fstring workgroup;
+
+ /* add cwd if necessary */
+ if (fname[0] != '/') {
+ slprintf(s, sizeof(s), "%s/%s", smbw_cwd, fname);
+ } else {
+ pstrcpy(s, fname);
+ }
+ clean_fname(s);
+
+ /* see if it has the right prefix */
+ len = strlen(smbw_prefix)-1;
+ if (strncmp(s,smbw_prefix,len) ||
+ (s[len] != '/' && s[len] != 0)) return s;
+
+ /* ok, its for us. Now parse out the workgroup, share etc. */
+ p = s+len;
+ if (*p == '/') p++;
+ if (!next_token(&p, workgroup, "/", sizeof(fstring))) {
+ /* we're in /smb - give a list of workgroups */
+ slprintf(server,sizeof(fstring), "%s#01", smbw_find_workgroup());
+ fstrcpy(share,"IPC$");
+ pstrcpy(path,"");
+ return s;
+ }
+
+ if (!next_token(&p, server, "/", sizeof(fstring))) {
+ /* we are in /smb/WORKGROUP */
+ slprintf(server,sizeof(fstring), "%s#1D", workgroup);
+ fstrcpy(share,"IPC$");
+ pstrcpy(path,"");
+ }
+
+ if (!next_token(&p, share, "/", sizeof(fstring))) {
+ /* we are in /smb/WORKGROUP/SERVER */
+ fstrcpy(share,"IPC$");
+ pstrcpy(path,"");
+ }
+
+ pstrcpy(path, p);
+
+ all_string_sub(path, "/", "\\", 0);
+
+ return s;
+}
+
+/*****************************************************
+determine if a path name (possibly relative) is in the
+smb name space
+*******************************************************/
+int smbw_path(const char *path)
+{
+ fstring server, share;
+ pstring s;
+ char *cwd;
+ int len;
+
+ if(!path)
+ return 0;
+
+ /* this is needed to prevent recursion with the BSD malloc which
+ opens /etc/malloc.conf on the first call */
+ if (strncmp(path,"/etc/", 5) == 0) {
+ return 0;
+ }
+
+ smbw_init();
+
+ len = strlen(smbw_prefix)-1;
+
+ if (path[0] == '/' && strncmp(path,smbw_prefix,len)) {
+ return 0;
+ }
+
+ if (smbw_busy) return 0;
+
+ DEBUG(3,("smbw_path(%s)\n", path));
+
+ cwd = smbw_parse_path(path, server, share, s);
+
+ if (strncmp(cwd,smbw_prefix,len) == 0 &&
+ (cwd[len] == '/' || cwd[len] == 0)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/*****************************************************
+return a unix errno from a SMB error pair
+*******************************************************/
+int smbw_errno(struct cli_state *c)
+{
+ return cli_errno(c);
+}
+
+/* Return a username and password given a server and share name */
+
+void get_envvar_auth_data(char *server, char *share, char **workgroup,
+ char **username, char **password)
+{
+ /* Fall back to shared memory/environment variables */
+
+ *username = smbw_getshared("USER");
+ if (!*username) *username = getenv("USER");
+ if (!*username) *username = "guest";
+
+ *workgroup = smbw_getshared("WORKGROUP");
+ if (!*workgroup) *workgroup = lp_workgroup();
+
+ *password = smbw_getshared("PASSWORD");
+ if (!*password) *password = "";
+}
+
+static smbw_get_auth_data_fn get_auth_data_fn = get_envvar_auth_data;
+
+/*****************************************************
+set the get auth data function
+******************************************************/
+void smbw_set_auth_data_fn(smbw_get_auth_data_fn fn)
+{
+ get_auth_data_fn = fn;
+}
+
+/*****************************************************
+return a connection to a server (existing or new)
+*******************************************************/
+struct smbw_server *smbw_server(char *server, char *share)
+{
+ struct smbw_server *srv=NULL;
+ struct cli_state c;
+ char *username;
+ char *password;
+ char *workgroup;
+ struct nmb_name called, calling;
+ char *p, *server_n = server;
+ fstring group;
+ pstring ipenv;
+ struct in_addr ip;
+
+ zero_ip(&ip);
+ ZERO_STRUCT(c);
+
+ get_auth_data_fn(server, share, &workgroup, &username, &password);
+
+ /* try to use an existing connection */
+ for (srv=smbw_srvs;srv;srv=srv->next) {
+ if (strcmp(server,srv->server_name)==0 &&
+ strcmp(share,srv->share_name)==0 &&
+ strcmp(workgroup,srv->workgroup)==0 &&
+ strcmp(username, srv->username) == 0)
+ return srv;
+ }
+
+ if (server[0] == 0) {
+ errno = EPERM;
+ return NULL;
+ }
+
+ make_nmb_name(&calling, global_myname(), 0x0);
+ make_nmb_name(&called , server, 0x20);
+
+ DEBUG(4,("server_n=[%s] server=[%s]\n", server_n, server));
+
+ if ((p=strchr_m(server_n,'#')) &&
+ (strcmp(p+1,"1D")==0 || strcmp(p+1,"01")==0)) {
+ struct in_addr sip;
+ pstring s;
+
+ fstrcpy(group, server_n);
+ p = strchr_m(group,'#');
+ *p = 0;
+
+ /* cache the workgroup master lookup */
+ slprintf(s,sizeof(s)-1,"MASTER_%s", group);
+ if (!(server_n = smbw_getshared(s))) {
+ if (!find_master_ip(group, &sip)) {
+ errno = ENOENT;
+ return NULL;
+ }
+ fstrcpy(group, inet_ntoa(sip));
+ server_n = group;
+ smbw_setshared(s,server_n);
+ }
+ }
+
+ DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server));
+
+ again:
+ slprintf(ipenv,sizeof(ipenv)-1,"HOST_%s", server_n);
+
+ zero_ip(&ip);
+ if ((p=smbw_getshared(ipenv))) {
+ ip = *(interpret_addr2(p));
+ }
+
+ /* have to open a new connection */
+ if (!cli_initialise(&c) || !cli_connect(&c, server_n, &ip)) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ if (!cli_session_request(&c, &calling, &called)) {
+ cli_shutdown(&c);
+ if (strcmp(called.name, "*SMBSERVER")) {
+ make_nmb_name(&called , "*SMBSERVER", 0x20);
+ goto again;
+ }
+ errno = ENOENT;
+ return NULL;
+ }
+
+ DEBUG(4,(" session request ok\n"));
+
+ if (!cli_negprot(&c)) {
+ cli_shutdown(&c);
+ errno = ENOENT;
+ return NULL;
+ }
+
+ if (!cli_session_setup(&c, username,
+ password, strlen(password),
+ password, strlen(password),
+ workgroup) &&
+ /* try an anonymous login if it failed */
+ !cli_session_setup(&c, "", "", 1,"", 0, workgroup)) {
+ cli_shutdown(&c);
+ errno = EPERM;
+ return NULL;
+ }
+
+ DEBUG(4,(" session setup ok\n"));
+
+ if (!cli_send_tconX(&c, share, "?????",
+ password, strlen(password)+1)) {
+ errno = smbw_errno(&c);
+ cli_shutdown(&c);
+ return NULL;
+ }
+
+ smbw_setshared(ipenv,inet_ntoa(ip));
+
+ DEBUG(4,(" tconx ok\n"));
+
+ srv = (struct smbw_server *)malloc(sizeof(*srv));
+ if (!srv) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ ZERO_STRUCTP(srv);
+
+ srv->cli = c;
+
+ srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share));
+
+ srv->server_name = strdup(server);
+ if (!srv->server_name) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ srv->share_name = strdup(share);
+ if (!srv->share_name) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ srv->workgroup = strdup(workgroup);
+ if (!srv->workgroup) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ srv->username = strdup(username);
+ if (!srv->username) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ /* some programs play with file descriptors fairly intimately. We
+ try to get out of the way by duping to a high fd number */
+ if (fcntl(SMBW_CLI_FD + srv->cli.fd, F_GETFD) && errno == EBADF) {
+ if (dup2(srv->cli.fd,SMBW_CLI_FD+srv->cli.fd) ==
+ srv->cli.fd+SMBW_CLI_FD) {
+ close(srv->cli.fd);
+ srv->cli.fd += SMBW_CLI_FD;
+ }
+ }
+
+ DLIST_ADD(smbw_srvs, srv);
+
+ return srv;
+
+ failed:
+ cli_shutdown(&c);
+ if (!srv) return NULL;
+
+ SAFE_FREE(srv->server_name);
+ SAFE_FREE(srv->share_name);
+ SAFE_FREE(srv);
+ return NULL;
+}
+
+
+/*****************************************************
+map a fd to a smbw_file structure
+*******************************************************/
+struct smbw_file *smbw_file(int fd)
+{
+ struct smbw_file *file;
+
+ for (file=smbw_files;file;file=file->next) {
+ if (file->fd == fd) return file;
+ }
+ return NULL;
+}
+
+/*****************************************************
+a wrapper for open()
+*******************************************************/
+int smbw_open(const char *fname, int flags, mode_t mode)
+{
+ fstring server, share;
+ pstring path;
+ struct smbw_server *srv=NULL;
+ int eno=0, fd = -1;
+ struct smbw_file *file=NULL;
+
+ smbw_init();
+
+ if (!fname) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ smbw_busy++;
+
+ /* work out what server they are after */
+ smbw_parse_path(fname, server, share, path);
+
+ /* get a connection to the server */
+ srv = smbw_server(server, share);
+ if (!srv) {
+ /* smbw_server sets errno */
+ goto failed;
+ }
+
+ if (path[strlen(path)-1] == '\\') {
+ fd = -1;
+ } else {
+ fd = cli_open(&srv->cli, path, flags, DENY_NONE);
+ }
+ if (fd == -1) {
+ /* it might be a directory. Maybe we should use chkpath? */
+ eno = smbw_errno(&srv->cli);
+ fd = smbw_dir_open(fname);
+ if (fd == -1) errno = eno;
+ smbw_busy--;
+ return fd;
+ }
+
+ file = (struct smbw_file *)malloc(sizeof(*file));
+ if (!file) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ ZERO_STRUCTP(file);
+
+ file->f = (struct smbw_filedes *)malloc(sizeof(*(file->f)));
+ if (!file->f) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ ZERO_STRUCTP(file->f);
+
+ file->f->cli_fd = fd;
+ file->f->fname = strdup(path);
+ if (!file->f->fname) {
+ errno = ENOMEM;
+ goto failed;
+ }
+ file->srv = srv;
+ file->fd = open(SMBW_DUMMY, O_WRONLY);
+ if (file->fd == -1) {
+ errno = EMFILE;
+ goto failed;
+ }
+
+ if (bitmap_query(smbw_file_bmap, file->fd)) {
+ DEBUG(0,("ERROR: fd used in smbw_open\n"));
+ errno = EIO;
+ goto failed;
+ }
+
+ file->f->ref_count=1;
+
+ bitmap_set(smbw_file_bmap, file->fd);
+
+ DLIST_ADD(smbw_files, file);
+
+ DEBUG(4,("opened %s\n", fname));
+
+ smbw_busy--;
+ return file->fd;
+
+ failed:
+ if (fd != -1) {
+ cli_close(&srv->cli, fd);
+ }
+ if (file) {
+ if (file->f) {
+ SAFE_FREE(file->f->fname);
+ SAFE_FREE(file->f);
+ }
+ SAFE_FREE(file);
+ }
+ smbw_busy--;
+ return -1;
+}
+
+
+/*****************************************************
+a wrapper for pread()
+*******************************************************/
+ssize_t smbw_pread(int fd, void *buf, size_t count, off_t ofs)
+{
+ struct smbw_file *file;
+ int ret;
+
+ smbw_busy++;
+
+ file = smbw_file(fd);
+ if (!file) {
+ errno = EBADF;
+ smbw_busy--;
+ return -1;
+ }
+
+ ret = cli_read(&file->srv->cli, file->f->cli_fd, buf, ofs, count);
+
+ if (ret == -1) {
+ errno = smbw_errno(&file->srv->cli);
+ smbw_busy--;
+ return -1;
+ }
+
+ smbw_busy--;
+ return ret;
+}
+
+/*****************************************************
+a wrapper for read()
+*******************************************************/
+ssize_t smbw_read(int fd, void *buf, size_t count)
+{
+ struct smbw_file *file;
+ int ret;
+
+ DEBUG(4,("smbw_read(%d, %d)\n", fd, (int)count));
+
+ smbw_busy++;
+
+ file = smbw_file(fd);
+ if (!file) {
+ errno = EBADF;
+ smbw_busy--;
+ return -1;
+ }
+
+ ret = cli_read(&file->srv->cli, file->f->cli_fd, buf,
+ file->f->offset, count);
+
+ if (ret == -1) {
+ errno = smbw_errno(&file->srv->cli);
+ smbw_busy--;
+ return -1;
+ }
+
+ file->f->offset += ret;
+
+ DEBUG(4,(" -> %d\n", ret));
+
+ smbw_busy--;
+ return ret;
+}
+
+
+
+/*****************************************************
+a wrapper for write()
+*******************************************************/
+ssize_t smbw_write(int fd, void *buf, size_t count)
+{
+ struct smbw_file *file;
+ int ret;
+
+ smbw_busy++;
+
+ file = smbw_file(fd);
+ if (!file) {
+ errno = EBADF;
+ smbw_busy--;
+ return -1;
+ }
+
+ ret = cli_write(&file->srv->cli, file->f->cli_fd, 0, buf, file->f->offset, count);
+
+ if (ret == -1) {
+ errno = smbw_errno(&file->srv->cli);
+ smbw_busy--;
+ return -1;
+ }
+
+ file->f->offset += ret;
+
+ smbw_busy--;
+ return ret;
+}
+
+/*****************************************************
+a wrapper for pwrite()
+*******************************************************/
+ssize_t smbw_pwrite(int fd, void *buf, size_t count, off_t ofs)
+{
+ struct smbw_file *file;
+ int ret;
+
+ smbw_busy++;
+
+ file = smbw_file(fd);
+ if (!file) {
+ errno = EBADF;
+ smbw_busy--;
+ return -1;
+ }
+
+ ret = cli_write(&file->srv->cli, file->f->cli_fd, 0, buf, ofs, count);
+
+ if (ret == -1) {
+ errno = smbw_errno(&file->srv->cli);
+ smbw_busy--;
+ return -1;
+ }
+
+ smbw_busy--;
+ return ret;
+}
+
+/*****************************************************
+a wrapper for close()
+*******************************************************/
+int smbw_close(int fd)
+{
+ struct smbw_file *file;
+
+ smbw_busy++;
+
+ file = smbw_file(fd);
+ if (!file) {
+ int ret = smbw_dir_close(fd);
+ smbw_busy--;
+ return ret;
+ }
+
+ if (file->f->ref_count == 1 &&
+ !cli_close(&file->srv->cli, file->f->cli_fd)) {
+ errno = smbw_errno(&file->srv->cli);
+ smbw_busy--;
+ return -1;
+ }
+
+
+ bitmap_clear(smbw_file_bmap, file->fd);
+ close(file->fd);
+
+ DLIST_REMOVE(smbw_files, file);
+
+ file->f->ref_count--;
+ if (file->f->ref_count == 0) {
+ SAFE_FREE(file->f->fname);
+ SAFE_FREE(file->f);
+ }
+ ZERO_STRUCTP(file);
+ SAFE_FREE(file);
+
+ smbw_busy--;
+
+ return 0;
+}
+
+
+/*****************************************************
+a wrapper for fcntl()
+*******************************************************/
+int smbw_fcntl(int fd, int cmd, long arg)
+{
+ return 0;
+}
+
+
+/*****************************************************
+a wrapper for access()
+*******************************************************/
+int smbw_access(const char *name, int mode)
+{
+ struct stat st;
+
+ DEBUG(4,("smbw_access(%s, 0x%x)\n", name, mode));
+
+ if (smbw_stat(name, &st)) return -1;
+
+ if (((mode & R_OK) && !(st.st_mode & S_IRUSR)) ||
+ ((mode & W_OK) && !(st.st_mode & S_IWUSR)) ||
+ ((mode & X_OK) && !(st.st_mode & S_IXUSR))) {
+ errno = EACCES;
+ return -1;
+ }
+
+ return 0;
+}
+
+/*****************************************************
+a wrapper for realink() - needed for correct errno setting
+*******************************************************/
+int smbw_readlink(const char *path, char *buf, size_t bufsize)
+{
+ struct stat st;
+ int ret;
+
+ ret = smbw_stat(path, &st);
+ if (ret != 0) {
+ DEBUG(4,("readlink(%s) failed\n", path));
+ return -1;
+ }
+
+ /* it exists - say it isn't a link */
+ DEBUG(4,("readlink(%s) not a link\n", path));
+
+ errno = EINVAL;
+ return -1;
+}
+
+
+/*****************************************************
+a wrapper for unlink()
+*******************************************************/
+int smbw_unlink(const char *fname)
+{
+ struct smbw_server *srv;
+ fstring server, share;
+ pstring path;
+
+ if (!fname) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ smbw_init();
+
+ smbw_busy++;
+
+ /* work out what server they are after */
+ smbw_parse_path(fname, server, share, path);
+
+ /* get a connection to the server */
+ srv = smbw_server(server, share);
+ if (!srv) {
+ /* smbw_server sets errno */
+ goto failed;
+ }
+
+ if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
+ int job = smbw_stat_printjob(srv, path, NULL, NULL);
+ if (job == -1) {
+ goto failed;
+ }
+ if (cli_printjob_del(&srv->cli, job) != 0) {
+ goto failed;
+ }
+ } else if (!cli_unlink(&srv->cli, path)) {
+ errno = smbw_errno(&srv->cli);
+ goto failed;
+ }
+
+ smbw_busy--;
+ return 0;
+
+ failed:
+ smbw_busy--;
+ return -1;
+}
+
+
+/*****************************************************
+a wrapper for rename()
+*******************************************************/
+int smbw_rename(const char *oldname, const char *newname)
+{
+ struct smbw_server *srv;
+ fstring server1, share1;
+ pstring path1;
+ fstring server2, share2;
+ pstring path2;
+
+ if (!oldname || !newname) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ smbw_init();
+
+ DEBUG(4,("smbw_rename(%s,%s)\n", oldname, newname));
+
+ smbw_busy++;
+
+ /* work out what server they are after */
+ smbw_parse_path(oldname, server1, share1, path1);
+ smbw_parse_path(newname, server2, share2, path2);
+
+ if (strcmp(server1, server2) || strcmp(share1, share2)) {
+ /* can't cross filesystems */
+ errno = EXDEV;
+ return -1;
+ }
+
+ /* get a connection to the server */
+ srv = smbw_server(server1, share1);
+ if (!srv) {
+ /* smbw_server sets errno */
+ goto failed;
+ }
+
+ if (!cli_rename(&srv->cli, path1, path2)) {
+ int eno = smbw_errno(&srv->cli);
+ if (eno != EEXIST ||
+ !cli_unlink(&srv->cli, path2) ||
+ !cli_rename(&srv->cli, path1, path2)) {
+ errno = eno;
+ goto failed;
+ }
+ }
+
+ smbw_busy--;
+ return 0;
+
+ failed:
+ smbw_busy--;
+ return -1;
+}
+
+
+/*****************************************************
+a wrapper for utime and utimes
+*******************************************************/
+static int smbw_settime(const char *fname, time_t t)
+{
+ struct smbw_server *srv;
+ fstring server, share;
+ pstring path;
+ uint16 mode;
+
+ if (!fname) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ smbw_init();
+
+ smbw_busy++;
+
+ /* work out what server they are after */
+ smbw_parse_path(fname, server, share, path);
+
+ /* get a connection to the server */
+ srv = smbw_server(server, share);
+ if (!srv) {
+ /* smbw_server sets errno */
+ goto failed;
+ }
+
+ if (!cli_getatr(&srv->cli, path, &mode, NULL, NULL)) {
+ errno = smbw_errno(&srv->cli);
+ goto failed;
+ }
+
+ if (!cli_setatr(&srv->cli, path, mode, t)) {
+ /* some servers always refuse directory changes */
+ if (!(mode & aDIR)) {
+ errno = smbw_errno(&srv->cli);
+ goto failed;
+ }
+ }
+
+ smbw_busy--;
+ return 0;
+
+ failed:
+ smbw_busy--;
+ return -1;
+}
+
+/*****************************************************
+a wrapper for utime
+*******************************************************/
+int smbw_utime(const char *fname, void *buf)
+{
+ struct utimbuf *tbuf = (struct utimbuf *)buf;
+ return smbw_settime(fname, tbuf?tbuf->modtime:time(NULL));
+}
+
+/*****************************************************
+a wrapper for utime
+*******************************************************/
+int smbw_utimes(const char *fname, void *buf)
+{
+ struct timeval *tbuf = (struct timeval *)buf;
+ return smbw_settime(fname, tbuf?tbuf->tv_sec:time(NULL));
+}
+
+
+/*****************************************************
+a wrapper for chown()
+*******************************************************/
+int smbw_chown(const char *fname, uid_t owner, gid_t group)
+{
+ struct smbw_server *srv;
+ fstring server, share;
+ pstring path;
+ uint16 mode;
+
+ if (!fname) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ smbw_init();
+
+ smbw_busy++;
+
+ /* work out what server they are after */
+ smbw_parse_path(fname, server, share, path);
+
+ /* get a connection to the server */
+ srv = smbw_server(server, share);
+ if (!srv) {
+ /* smbw_server sets errno */
+ goto failed;
+ }
+
+ if (!cli_getatr(&srv->cli, path, &mode, NULL, NULL)) {
+ errno = smbw_errno(&srv->cli);
+ goto failed;
+ }
+
+ /* assume success */
+
+ smbw_busy--;
+ return 0;
+
+ failed:
+ smbw_busy--;
+ return -1;
+}
+
+/*****************************************************
+a wrapper for chmod()
+*******************************************************/
+int smbw_chmod(const char *fname, mode_t newmode)
+{
+ struct smbw_server *srv;
+ fstring server, share;
+ pstring path;
+ uint32 mode;
+
+ if (!fname) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ smbw_init();
+
+ smbw_busy++;
+
+ /* work out what server they are after */
+ smbw_parse_path(fname, server, share, path);
+
+ /* get a connection to the server */
+ srv = smbw_server(server, share);
+ if (!srv) {
+ /* smbw_server sets errno */
+ goto failed;
+ }
+
+ mode = 0;
+
+ if (!(newmode & (S_IWUSR | S_IWGRP | S_IWOTH))) mode |= aRONLY;
+ if ((newmode & S_IXUSR) && lp_map_archive(-1)) mode |= aARCH;
+ if ((newmode & S_IXGRP) && lp_map_system(-1)) mode |= aSYSTEM;
+ if ((newmode & S_IXOTH) && lp_map_hidden(-1)) mode |= aHIDDEN;
+
+ if (!cli_setatr(&srv->cli, path, mode, 0)) {
+ errno = smbw_errno(&srv->cli);
+ goto failed;
+ }
+
+ smbw_busy--;
+ return 0;
+
+ failed:
+ smbw_busy--;
+ return -1;
+}
+
+/*****************************************************
+a wrapper for lseek()
+*******************************************************/
+off_t smbw_lseek(int fd, off_t offset, int whence)
+{
+ struct smbw_file *file;
+ size_t size;
+
+ smbw_busy++;
+
+ file = smbw_file(fd);
+ if (!file) {
+ off_t ret = smbw_dir_lseek(fd, offset, whence);
+ smbw_busy--;
+ return ret;
+ }
+
+ switch (whence) {
+ case SEEK_SET:
+ file->f->offset = offset;
+ break;
+ case SEEK_CUR:
+ file->f->offset += offset;
+ break;
+ case SEEK_END:
+ if (!cli_qfileinfo(&file->srv->cli, file->f->cli_fd,
+ NULL, &size, NULL, NULL, NULL,
+ NULL, NULL) &&
+ !cli_getattrE(&file->srv->cli, file->f->cli_fd,
+ NULL, &size, NULL, NULL, NULL)) {
+ errno = EINVAL;
+ smbw_busy--;
+ return -1;
+ }
+ file->f->offset = size + offset;
+ break;
+ }
+
+ smbw_busy--;
+ return file->f->offset;
+}
+
+
+/*****************************************************
+a wrapper for dup()
+*******************************************************/
+int smbw_dup(int fd)
+{
+ int fd2;
+ struct smbw_file *file, *file2;
+
+ smbw_busy++;
+
+ file = smbw_file(fd);
+ if (!file) {
+ errno = EBADF;
+ goto failed;
+ }
+
+ fd2 = dup(file->fd);
+ if (fd2 == -1) {
+ goto failed;
+ }
+
+ if (bitmap_query(smbw_file_bmap, fd2)) {
+ DEBUG(0,("ERROR: fd already open in dup!\n"));
+ errno = EIO;
+ goto failed;
+ }
+
+ file2 = (struct smbw_file *)malloc(sizeof(*file2));
+ if (!file2) {
+ close(fd2);
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ ZERO_STRUCTP(file2);
+
+ *file2 = *file;
+ file2->fd = fd2;
+
+ file->f->ref_count++;
+
+ bitmap_set(smbw_file_bmap, fd2);
+
+ DLIST_ADD(smbw_files, file2);
+
+ smbw_busy--;
+ return fd2;
+
+ failed:
+ smbw_busy--;
+ return -1;
+}
+
+
+/*****************************************************
+a wrapper for dup2()
+*******************************************************/
+int smbw_dup2(int fd, int fd2)
+{
+ struct smbw_file *file, *file2;
+
+ smbw_busy++;
+
+ file = smbw_file(fd);
+ if (!file) {
+ errno = EBADF;
+ goto failed;
+ }
+
+ if (bitmap_query(smbw_file_bmap, fd2)) {
+ DEBUG(0,("ERROR: fd already open in dup2!\n"));
+ errno = EIO;
+ goto failed;
+ }
+
+ if (dup2(file->fd, fd2) != fd2) {
+ goto failed;
+ }
+
+ file2 = (struct smbw_file *)malloc(sizeof(*file2));
+ if (!file2) {
+ close(fd2);
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ ZERO_STRUCTP(file2);
+
+ *file2 = *file;
+ file2->fd = fd2;
+
+ file->f->ref_count++;
+
+ bitmap_set(smbw_file_bmap, fd2);
+
+ DLIST_ADD(smbw_files, file2);
+
+ smbw_busy--;
+ return fd2;
+
+ failed:
+ smbw_busy--;
+ return -1;
+}
+
+
+/*****************************************************
+close a connection to a server
+*******************************************************/
+static void smbw_srv_close(struct smbw_server *srv)
+{
+ smbw_busy++;
+
+ cli_shutdown(&srv->cli);
+
+ SAFE_FREE(srv->server_name);
+ SAFE_FREE(srv->share_name);
+
+ DLIST_REMOVE(smbw_srvs, srv);
+
+ ZERO_STRUCTP(srv);
+
+ SAFE_FREE(srv);
+
+ smbw_busy--;
+}
+
+/*****************************************************
+when we fork we have to close all connections and files
+in the child
+*******************************************************/
+int smbw_fork(void)
+{
+ pid_t child;
+ int p[2];
+ char c=0;
+ pstring line;
+
+ struct smbw_file *file, *next_file;
+ struct smbw_server *srv, *next_srv;
+
+ if (pipe(p)) return real_fork();
+
+ child = real_fork();
+
+ if (child) {
+ /* block the parent for a moment until the sockets are
+ closed */
+ close(p[1]);
+ read(p[0], &c, 1);
+ close(p[0]);
+ return child;
+ }
+
+ close(p[0]);
+
+ /* close all files */
+ for (file=smbw_files;file;file=next_file) {
+ next_file = file->next;
+ close(file->fd);
+ }
+
+ /* close all server connections */
+ for (srv=smbw_srvs;srv;srv=next_srv) {
+ next_srv = srv->next;
+ smbw_srv_close(srv);
+ }
+
+ slprintf(line,sizeof(line)-1,"PWD_%d", (int)getpid());
+ smbw_setshared(line,smbw_cwd);
+
+ /* unblock the parent */
+ write(p[1], &c, 1);
+ close(p[1]);
+
+ /* and continue in the child */
+ return 0;
+}
+
+#ifndef NO_ACL_WRAPPER
+/*****************************************************
+say no to acls
+*******************************************************/
+ int smbw_acl(const char *pathp, int cmd, int nentries, aclent_t *aclbufp)
+{
+ if (cmd == GETACL || cmd == GETACLCNT) return 0;
+ errno = ENOSYS;
+ return -1;
+}
+#endif
+
+#ifndef NO_FACL_WRAPPER
+/*****************************************************
+say no to acls
+*******************************************************/
+ int smbw_facl(int fd, int cmd, int nentries, aclent_t *aclbufp)
+{
+ if (cmd == GETACL || cmd == GETACLCNT) return 0;
+ errno = ENOSYS;
+ return -1;
+}
+#endif
+
+#ifdef HAVE_EXPLICIT_LARGEFILE_SUPPORT
+#ifdef HAVE_STAT64
+/* this can't be in wrapped.c because of include conflicts */
+ void stat64_convert(struct stat *st, struct stat64 *st64)
+{
+ st64->st_size = st->st_size;
+ st64->st_mode = st->st_mode;
+ st64->st_ino = st->st_ino;
+ st64->st_dev = st->st_dev;
+ st64->st_rdev = st->st_rdev;
+ st64->st_nlink = st->st_nlink;
+ st64->st_uid = st->st_uid;
+ st64->st_gid = st->st_gid;
+ st64->st_atime = st->st_atime;
+ st64->st_mtime = st->st_mtime;
+ st64->st_ctime = st->st_ctime;
+#ifdef HAVE_STAT_ST_BLKSIZE
+ st64->st_blksize = st->st_blksize;
+#endif
+#ifdef HAVE_STAT_ST_BLOCKS
+ st64->st_blocks = st->st_blocks;
+#endif
+}
+#endif
+
+#ifdef HAVE_READDIR64
+ void dirent64_convert(struct dirent *d, struct dirent64 *d64)
+{
+ d64->d_ino = d->d_ino;
+ d64->d_off = d->d_off;
+ d64->d_reclen = d->d_reclen;
+ pstrcpy(d64->d_name, d->d_name);
+}
+#endif
+#endif
+
+
+#ifdef HAVE___XSTAT
+/* Definition of `struct stat' used in the linux kernel.. */
+struct kernel_stat {
+ unsigned short int st_dev;
+ unsigned short int __pad1;
+ unsigned long int st_ino;
+ unsigned short int st_mode;
+ unsigned short int st_nlink;
+ unsigned short int st_uid;
+ unsigned short int st_gid;
+ unsigned short int st_rdev;
+ unsigned short int __pad2;
+ unsigned long int st_size;
+ unsigned long int st_blksize;
+ unsigned long int st_blocks;
+ unsigned long int st_atime_;
+ unsigned long int __unused1;
+ unsigned long int st_mtime_;
+ unsigned long int __unused2;
+ unsigned long int st_ctime_;
+ unsigned long int __unused3;
+ unsigned long int __unused4;
+ unsigned long int __unused5;
+};
+
+/*
+ * Prototype for gcc in 'fussy' mode.
+ */
+ void xstat_convert(int vers, struct stat *st, struct kernel_stat *kbuf);
+ void xstat_convert(int vers, struct stat *st, struct kernel_stat *kbuf)
+{
+#ifdef _STAT_VER_LINUX_OLD
+ if (vers == _STAT_VER_LINUX_OLD) {
+ memcpy(st, kbuf, sizeof(*st));
+ return;
+ }
+#endif
+
+ ZERO_STRUCTP(st);
+
+ st->st_dev = kbuf->st_dev;
+ st->st_ino = kbuf->st_ino;
+ st->st_mode = kbuf->st_mode;
+ st->st_nlink = kbuf->st_nlink;
+ st->st_uid = kbuf->st_uid;
+ st->st_gid = kbuf->st_gid;
+ st->st_rdev = kbuf->st_rdev;
+ st->st_size = kbuf->st_size;
+#ifdef HAVE_STAT_ST_BLKSIZE
+ st->st_blksize = kbuf->st_blksize;
+#endif
+#ifdef HAVE_STAT_ST_BLOCKS
+ st->st_blocks = kbuf->st_blocks;
+#endif
+ st->st_atime = kbuf->st_atime_;
+ st->st_mtime = kbuf->st_mtime_;
+ st->st_ctime = kbuf->st_ctime_;
+}
+#endif
diff --git a/source/smbwrapper/smbw.h b/source/smbwrapper/smbw.h
new file mode 100644
index 00000000000..3f0b1cbb443
--- /dev/null
+++ b/source/smbwrapper/smbw.h
@@ -0,0 +1,71 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB wrapper functions - definitions
+ Copyright (C) Andrew Tridgell 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _SMBW_H
+#define _SMBW_H
+
+#define SMBW_PREFIX "/smb/"
+#define SMBW_DUMMY "/dev/null"
+
+#define SMBW_CLI_FD 512
+#define SMBW_MAX_OPEN 8192
+
+#define SMBW_FILE_MODE (S_IFREG | 0444)
+#define SMBW_DIR_MODE (S_IFDIR | 0555)
+
+struct smbw_server {
+ struct smbw_server *next, *prev;
+ struct cli_state cli;
+ char *server_name;
+ char *share_name;
+ char *workgroup;
+ char *username;
+ dev_t dev;
+ BOOL no_pathinfo2;
+};
+
+struct smbw_filedes {
+ int cli_fd;
+ int ref_count;
+ char *fname;
+ off_t offset;
+};
+
+struct smbw_file {
+ struct smbw_file *next, *prev;
+ struct smbw_filedes *f;
+ int fd;
+ struct smbw_server *srv;
+};
+
+struct smbw_dir {
+ struct smbw_dir *next, *prev;
+ int fd;
+ int offset, count, malloced;
+ struct smbw_server *srv;
+ struct file_info *list;
+ char *path;
+};
+
+typedef void (*smbw_get_auth_data_fn)(char *server, char *share,
+ char **workgroup, char **username,
+ char **password);
+
+#endif /* _SMBW_H */
diff --git a/source/smbwrapper/smbw_cache.c b/source/smbwrapper/smbw_cache.c
new file mode 100644
index 00000000000..fcb0eda8050
--- /dev/null
+++ b/source/smbwrapper/smbw_cache.c
@@ -0,0 +1,207 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB wrapper directory functions
+ Copyright (C) Tim Potter 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* We cache lists of workgroups, lists of servers in workgroups, and lists
+ of shares exported by servers. */
+
+#define CACHE_TIMEOUT 30
+
+struct name_list {
+ struct name_list *prev, *next;
+ char *name;
+ uint32 stype;
+ char *comment;
+};
+
+struct cached_names {
+ struct cached_names *prev, *next;
+ char *key;
+ struct name_list *name_list;
+ time_t cache_timeout;
+ int result;
+};
+
+static struct cached_names *cached_names = NULL;
+
+/* Find a list of cached name for a workgroup, server or share list */
+
+static struct cached_names *find_cached_names(char *key)
+{
+ struct cached_names *tmp;
+
+ for (tmp = cached_names; tmp; tmp = tmp->next) {
+ if (strequal(tmp->key, key)) {
+ return tmp;
+ }
+ }
+
+ return NULL;
+}
+
+/* Add a name to a list stored in the state variable */
+
+static void add_cached_names(const char *name, uint32 stype,
+ const char *comment, void *state)
+{
+ struct name_list **name_list = (struct name_list **)state;
+ struct name_list *new_name;
+
+ new_name = (struct name_list *)malloc(sizeof(struct name_list));
+ if (!new_name) return;
+
+ ZERO_STRUCTP(new_name);
+
+ new_name->name = strdup(name);
+ new_name->stype = stype;
+ new_name->comment = strdup(comment);
+
+ DLIST_ADD(*name_list, new_name);
+}
+
+static void free_name_list(struct name_list *name_list)
+{
+ struct name_list *tmp = name_list;
+
+ while(tmp) {
+ struct name_list *next;
+
+ next = tmp->next;
+
+ SAFE_FREE(tmp->name);
+ SAFE_FREE(tmp->comment);
+ SAFE_FREE(tmp);
+
+ tmp = next;
+ }
+}
+
+/* Wrapper for NetServerEnum function */
+
+BOOL smbw_NetServerEnum(struct cli_state *cli, char *workgroup, uint32 stype,
+ void (*fn)(const char *, uint32, const char *, void *),
+ void *state)
+{
+ struct cached_names *names;
+ struct name_list *tmp;
+ time_t now = time(NULL);
+ char key[PATH_MAX];
+ BOOL result = True;
+
+ slprintf(key, PATH_MAX - 1, "%s/%s#%s", cli->desthost,
+ workgroup, (stype == SV_TYPE_DOMAIN_ENUM ? "DOM" : "SRV"));
+
+ names = find_cached_names(key);
+
+ if (names == NULL || (now - names->cache_timeout) > CACHE_TIMEOUT) {
+ struct cached_names *new_names = NULL;
+
+ /* No names cached for this workgroup */
+
+ if (names == NULL) {
+ new_names = (struct cached_names *)
+ malloc(sizeof(struct cached_names));
+
+ ZERO_STRUCTP(new_names);
+ DLIST_ADD(cached_names, new_names);
+
+ } else {
+
+ /* Dispose of out of date name list */
+
+ free_name_list(names->name_list);
+ names->name_list = NULL;
+
+ new_names = names;
+ }
+
+ result = cli_NetServerEnum(cli, workgroup, stype,
+ add_cached_names,
+ &new_names->name_list);
+
+ new_names->cache_timeout = now;
+ new_names->result = result;
+ new_names->key = strdup(key);
+
+ names = new_names;
+ }
+
+ /* Return names by running callback function. */
+
+ for (tmp = names->name_list; tmp; tmp = tmp->next)
+ fn(tmp->name, stype, tmp->comment, state);
+
+ return names->result;
+}
+
+/* Wrapper for RNetShareEnum function */
+
+int smbw_RNetShareEnum(struct cli_state *cli,
+ void (*fn)(const char *, uint32, const char *, void *),
+ void *state)
+{
+ struct cached_names *names;
+ struct name_list *tmp;
+ time_t now = time(NULL);
+ char key[PATH_MAX];
+
+ slprintf(key, PATH_MAX - 1, "SHARE/%s", cli->desthost);
+
+ names = find_cached_names(key);
+
+ if (names == NULL || (now - names->cache_timeout) > CACHE_TIMEOUT) {
+ struct cached_names *new_names = NULL;
+
+ /* No names cached for this server */
+
+ if (names == NULL) {
+ new_names = (struct cached_names *)
+ malloc(sizeof(struct cached_names));
+
+ ZERO_STRUCTP(new_names);
+ DLIST_ADD(cached_names, new_names);
+
+ } else {
+
+ /* Dispose of out of date name list */
+
+ free_name_list(names->name_list);
+ names->name_list = NULL;
+
+ new_names = names;
+ }
+
+ new_names->result = cli_RNetShareEnum(cli, add_cached_names,
+ &new_names->name_list);
+
+ new_names->cache_timeout = now;
+ new_names->key = strdup(key);
+
+ names = new_names;
+ }
+
+ /* Return names by running callback function. */
+
+ for (tmp = names->name_list; tmp; tmp = tmp->next)
+ fn(tmp->name, tmp->stype, tmp->comment, state);
+
+ return names->result;
+}
diff --git a/source/smbwrapper/smbw_dir.c b/source/smbwrapper/smbw_dir.c
new file mode 100644
index 00000000000..0a6deede41f
--- /dev/null
+++ b/source/smbwrapper/smbw_dir.c
@@ -0,0 +1,689 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB wrapper directory functions
+ Copyright (C) Andrew Tridgell 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "realcalls.h"
+
+extern pstring smbw_cwd;
+extern fstring smbw_prefix;
+
+static struct smbw_dir *smbw_dirs;
+
+extern struct bitmap *smbw_file_bmap;
+
+extern int smbw_busy;
+
+/*****************************************************
+map a fd to a smbw_dir structure
+*******************************************************/
+struct smbw_dir *smbw_dir(int fd)
+{
+ struct smbw_dir *dir;
+
+ for (dir=smbw_dirs;dir;dir=dir->next) {
+ if (dir->fd == fd) return dir;
+ }
+ return NULL;
+}
+
+/*****************************************************
+check if a DIR* is one of ours
+*******************************************************/
+int smbw_dirp(DIR *dirp)
+{
+ struct smbw_dir *d = (struct smbw_dir *)dirp;
+ struct smbw_dir *dir;
+
+ for (dir=smbw_dirs;dir;dir=dir->next) {
+ if (dir == d) return 1;
+ }
+ return 0;
+}
+
+/*****************************************************
+free a smbw_dir structure and all entries
+*******************************************************/
+static void free_dir(struct smbw_dir *dir)
+{
+ if(!dir) return;
+
+ SAFE_FREE(dir->list);
+ SAFE_FREE(dir->path);
+ ZERO_STRUCTP(dir);
+ SAFE_FREE(dir);
+}
+
+static struct smbw_dir *cur_dir;
+
+/*****************************************************
+add a entry to a directory listing
+*******************************************************/
+static void smbw_dir_add(struct file_info *finfo, const char *mask,
+ void *state)
+{
+ struct file_info *cdl;
+
+ DEBUG(5,("%s\n", finfo->name));
+
+ if (cur_dir->malloced == cur_dir->count) {
+ cdl = (struct file_info *)Realloc(cur_dir->list,
+ sizeof(cur_dir->list[0])*
+ (cur_dir->count+100));
+ if (!cdl) {
+ /* oops */
+ return;
+ }
+ cur_dir->list = cdl;
+ cur_dir->malloced += 100;
+ }
+
+ cur_dir->list[cur_dir->count] = *finfo;
+ cur_dir->count++;
+}
+
+/*****************************************************
+add a entry to a directory listing
+*******************************************************/
+static void smbw_share_add(const char *share, uint32 type,
+ const char *comment, void *state)
+{
+ struct file_info finfo;
+
+ if (strcmp(share,"IPC$") == 0) return;
+
+ ZERO_STRUCT(finfo);
+
+ pstrcpy(finfo.name, share);
+ finfo.mode = aRONLY | aDIR;
+
+ smbw_dir_add(&finfo, NULL, NULL);
+}
+
+
+/*****************************************************
+add a server to a directory listing
+*******************************************************/
+static void smbw_server_add(const char *name, uint32 type,
+ const char *comment, void *state)
+{
+ struct file_info finfo;
+
+ ZERO_STRUCT(finfo);
+
+ pstrcpy(finfo.name, name);
+ finfo.mode = aRONLY | aDIR;
+
+ smbw_dir_add(&finfo, NULL, NULL);
+}
+
+
+/*****************************************************
+add a entry to a directory listing
+*******************************************************/
+static void smbw_printjob_add(struct print_job_info *job)
+{
+ struct file_info finfo;
+
+ ZERO_STRUCT(finfo);
+
+ pstrcpy(finfo.name, job->name);
+ finfo.mode = aRONLY | aDIR;
+ finfo.mtime = job->t;
+ finfo.atime = job->t;
+ finfo.ctime = job->t;
+ finfo.uid = nametouid(job->user);
+ finfo.mode = aRONLY;
+ finfo.size = job->size;
+
+ smbw_dir_add(&finfo, NULL, NULL);
+}
+
+
+/*****************************************************
+open a directory on the server
+*******************************************************/
+int smbw_dir_open(const char *fname)
+{
+ fstring server, share;
+ pstring path;
+ struct smbw_server *srv=NULL;
+ struct smbw_dir *dir=NULL;
+ pstring mask;
+ int fd;
+ char *s, *p;
+
+ if (!fname) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ smbw_init();
+
+ /* work out what server they are after */
+ s = smbw_parse_path(fname, server, share, path);
+
+ DEBUG(4,("dir_open share=%s\n", share));
+
+ /* get a connection to the server */
+ srv = smbw_server(server, share);
+ if (!srv) {
+ /* smbw_server sets errno */
+ goto failed;
+ }
+
+ dir = (struct smbw_dir *)malloc(sizeof(*dir));
+ if (!dir) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ ZERO_STRUCTP(dir);
+
+ cur_dir = dir;
+
+ slprintf(mask, sizeof(mask)-1, "%s\\*", path);
+ all_string_sub(mask,"\\\\","\\",0);
+
+ if ((p=strstr(srv->server_name,"#01"))) {
+ *p = 0;
+ smbw_server_add(".",0,"", NULL);
+ smbw_server_add("..",0,"", NULL);
+ smbw_NetServerEnum(&srv->cli, srv->server_name,
+ SV_TYPE_DOMAIN_ENUM, smbw_server_add, NULL);
+ *p = '#';
+ } else if ((p=strstr(srv->server_name,"#1D"))) {
+ DEBUG(4,("doing NetServerEnum\n"));
+ *p = 0;
+ smbw_server_add(".",0,"", NULL);
+ smbw_server_add("..",0,"", NULL);
+ smbw_NetServerEnum(&srv->cli, srv->server_name, SV_TYPE_ALL,
+ smbw_server_add, NULL);
+ *p = '#';
+ } else if ((strcmp(srv->cli.dev,"IPC") == 0) || (strequal(share,"IPC$"))) {
+ DEBUG(4,("doing NetShareEnum\n"));
+ smbw_share_add(".",0,"", NULL);
+ smbw_share_add("..",0,"", NULL);
+ if (smbw_RNetShareEnum(&srv->cli, smbw_share_add, NULL) < 0) {
+ errno = smbw_errno(&srv->cli);
+ goto failed;
+ }
+ } else if (strncmp(srv->cli.dev,"LPT",3) == 0) {
+ smbw_share_add(".",0,"", NULL);
+ smbw_share_add("..",0,"", NULL);
+ if (cli_print_queue(&srv->cli, smbw_printjob_add) < 0) {
+ errno = smbw_errno(&srv->cli);
+ goto failed;
+ }
+ } else {
+#if 0
+ if (strcmp(path,"\\") == 0) {
+ smbw_share_add(".",0,"");
+ smbw_share_add("..",0,"");
+ }
+#endif
+ if (cli_list(&srv->cli, mask, aHIDDEN|aSYSTEM|aDIR,
+ smbw_dir_add, NULL) < 0) {
+ errno = smbw_errno(&srv->cli);
+ goto failed;
+ }
+ }
+
+ cur_dir = NULL;
+
+ fd = open(SMBW_DUMMY, O_WRONLY);
+ if (fd == -1) {
+ errno = EMFILE;
+ goto failed;
+ }
+
+ if (bitmap_query(smbw_file_bmap, fd)) {
+ DEBUG(0,("ERROR: fd used in smbw_dir_open\n"));
+ errno = EIO;
+ goto failed;
+ }
+
+ DLIST_ADD(smbw_dirs, dir);
+
+ bitmap_set(smbw_file_bmap, fd);
+
+ dir->fd = fd;
+ dir->srv = srv;
+ dir->path = strdup(s);
+
+ DEBUG(4,(" -> %d\n", dir->count));
+
+ return dir->fd;
+
+ failed:
+ free_dir(dir);
+
+ return -1;
+}
+
+/*****************************************************
+a wrapper for fstat() on a directory
+*******************************************************/
+int smbw_dir_fstat(int fd, struct stat *st)
+{
+ struct smbw_dir *dir;
+
+ dir = smbw_dir(fd);
+ if (!dir) {
+ errno = EBADF;
+ return -1;
+ }
+
+ ZERO_STRUCTP(st);
+
+ smbw_setup_stat(st, "", dir->count*DIRP_SIZE, aDIR);
+
+ st->st_dev = dir->srv->dev;
+
+ return 0;
+}
+
+/*****************************************************
+close a directory handle
+*******************************************************/
+int smbw_dir_close(int fd)
+{
+ struct smbw_dir *dir;
+
+ dir = smbw_dir(fd);
+ if (!dir) {
+ errno = EBADF;
+ return -1;
+ }
+
+ bitmap_clear(smbw_file_bmap, dir->fd);
+ close(dir->fd);
+
+ DLIST_REMOVE(smbw_dirs, dir);
+
+ free_dir(dir);
+
+ return 0;
+}
+
+/*****************************************************
+a wrapper for getdents()
+*******************************************************/
+int smbw_getdents(unsigned int fd, struct dirent *dirp, int count)
+{
+ struct smbw_dir *dir;
+ int n=0;
+
+ smbw_busy++;
+
+ dir = smbw_dir(fd);
+ if (!dir) {
+ errno = EBADF;
+ smbw_busy--;
+ return -1;
+ }
+
+ while (count>=DIRP_SIZE && (dir->offset < dir->count)) {
+#if HAVE_DIRENT_D_OFF
+ dirp->d_off = (dir->offset+1)*DIRP_SIZE;
+#endif
+ dirp->d_reclen = DIRP_SIZE;
+ fstrcpy(&dirp->d_name[0], dir->list[dir->offset].name);
+ dirp->d_ino = smbw_inode(dir->list[dir->offset].name);
+ dir->offset++;
+ count -= dirp->d_reclen;
+#if HAVE_DIRENT_D_OFF
+ if (dir->offset == dir->count) {
+ dirp->d_off = -1;
+ }
+#endif
+ dirp = (struct dirent *)(((char *)dirp) + DIRP_SIZE);
+ n++;
+ }
+
+ smbw_busy--;
+ return n*DIRP_SIZE;
+}
+
+
+/*****************************************************
+a wrapper for chdir()
+*******************************************************/
+int smbw_chdir(const char *name)
+{
+ struct smbw_server *srv;
+ fstring server, share;
+ pstring path;
+ uint16 mode = aDIR;
+ char *cwd;
+ int len;
+
+ smbw_init();
+
+ len = strlen(smbw_prefix);
+
+ if (smbw_busy) return real_chdir(name);
+
+ smbw_busy++;
+
+ if (!name) {
+ errno = EINVAL;
+ goto failed;
+ }
+
+ DEBUG(4,("smbw_chdir(%s)\n", name));
+
+ /* work out what server they are after */
+ cwd = smbw_parse_path(name, server, share, path);
+
+ /* a special case - accept cd to /smb */
+ if (strncmp(cwd, smbw_prefix, len-1) == 0 &&
+ cwd[len-1] == 0) {
+ goto success1;
+ }
+
+ if (strncmp(cwd,smbw_prefix,strlen(smbw_prefix))) {
+ if (real_chdir(cwd) == 0) {
+ goto success2;
+ }
+ goto failed;
+ }
+
+ /* get a connection to the server */
+ srv = smbw_server(server, share);
+ if (!srv) {
+ /* smbw_server sets errno */
+ goto failed;
+ }
+
+ if (strncmp(srv->cli.dev,"IPC",3) &&
+ !strequal(share, "IPC$") &&
+ strncmp(srv->cli.dev,"LPT",3) &&
+ !smbw_getatr(srv, path,
+ &mode, NULL, NULL, NULL, NULL, NULL)) {
+ errno = smbw_errno(&srv->cli);
+ goto failed;
+ }
+
+ if (!(mode & aDIR)) {
+ errno = ENOTDIR;
+ goto failed;
+ }
+
+ success1:
+ /* we don't want the old directory to be busy */
+ real_chdir("/");
+
+ success2:
+
+ DEBUG(4,("set SMBW_CWD to %s\n", cwd));
+
+ pstrcpy(smbw_cwd, cwd);
+
+ smbw_busy--;
+ return 0;
+
+ failed:
+ smbw_busy--;
+ return -1;
+}
+
+
+/*****************************************************
+a wrapper for lseek() on directories
+*******************************************************/
+off_t smbw_dir_lseek(int fd, off_t offset, int whence)
+{
+ struct smbw_dir *dir;
+ off_t ret;
+
+ dir = smbw_dir(fd);
+ if (!dir) {
+ errno = EBADF;
+ return -1;
+ }
+
+ switch (whence) {
+ case SEEK_SET:
+ dir->offset = offset/DIRP_SIZE;
+ break;
+ case SEEK_CUR:
+ dir->offset += offset/DIRP_SIZE;
+ break;
+ case SEEK_END:
+ dir->offset = (dir->count * DIRP_SIZE) + offset;
+ dir->offset /= DIRP_SIZE;
+ break;
+ }
+
+ ret = dir->offset * DIRP_SIZE;
+
+ DEBUG(4,(" -> %d\n", (int)ret));
+
+ return ret;
+}
+
+
+/*****************************************************
+a wrapper for mkdir()
+*******************************************************/
+int smbw_mkdir(const char *fname, mode_t mode)
+{
+ struct smbw_server *srv;
+ fstring server, share;
+ pstring path;
+
+ if (!fname) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ smbw_init();
+
+ smbw_busy++;
+
+ /* work out what server they are after */
+ smbw_parse_path(fname, server, share, path);
+
+ /* get a connection to the server */
+ srv = smbw_server(server, share);
+ if (!srv) {
+ /* smbw_server sets errno */
+ goto failed;
+ }
+
+ if (!cli_mkdir(&srv->cli, path)) {
+ errno = smbw_errno(&srv->cli);
+ goto failed;
+ }
+
+ smbw_busy--;
+ return 0;
+
+ failed:
+ smbw_busy--;
+ return -1;
+}
+
+/*****************************************************
+a wrapper for rmdir()
+*******************************************************/
+int smbw_rmdir(const char *fname)
+{
+ struct smbw_server *srv;
+ fstring server, share;
+ pstring path;
+
+ if (!fname) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ smbw_init();
+
+ smbw_busy++;
+
+ /* work out what server they are after */
+ smbw_parse_path(fname, server, share, path);
+
+ /* get a connection to the server */
+ srv = smbw_server(server, share);
+ if (!srv) {
+ /* smbw_server sets errno */
+ goto failed;
+ }
+
+ if (!cli_rmdir(&srv->cli, path)) {
+ errno = smbw_errno(&srv->cli);
+ goto failed;
+ }
+
+ smbw_busy--;
+ return 0;
+
+ failed:
+ smbw_busy--;
+ return -1;
+}
+
+
+/*****************************************************
+a wrapper for getcwd()
+*******************************************************/
+char *smbw_getcwd(char *buf, size_t size)
+{
+ smbw_init();
+
+ if (smbw_busy) {
+ return (char *)real_getcwd(buf, size);
+ }
+
+ smbw_busy++;
+
+ if (!buf) {
+ if (size <= 0) size = strlen(smbw_cwd)+1;
+ buf = (char *)malloc(size);
+ if (!buf) {
+ errno = ENOMEM;
+ smbw_busy--;
+ return NULL;
+ }
+ }
+
+ if (strlen(smbw_cwd) > size-1) {
+ errno = ERANGE;
+ smbw_busy--;
+ return NULL;
+ }
+
+ safe_strcpy(buf, smbw_cwd, size);
+
+ smbw_busy--;
+ return buf;
+}
+
+/*****************************************************
+a wrapper for fchdir()
+*******************************************************/
+int smbw_fchdir(unsigned int fd)
+{
+ struct smbw_dir *dir;
+ int ret;
+
+ smbw_busy++;
+
+ dir = smbw_dir(fd);
+ if (dir) {
+ smbw_busy--;
+ return chdir(dir->path);
+ }
+
+ ret = real_fchdir(fd);
+ if (ret == 0) {
+ sys_getwd(smbw_cwd);
+ }
+
+ smbw_busy--;
+ return ret;
+}
+
+/*****************************************************
+open a directory on the server
+*******************************************************/
+DIR *smbw_opendir(const char *fname)
+{
+ int fd;
+
+ smbw_busy++;
+
+ fd = smbw_dir_open(fname);
+
+ if (fd == -1) {
+ smbw_busy--;
+ return NULL;
+ }
+
+ smbw_busy--;
+
+ return (DIR *)smbw_dir(fd);
+}
+
+/*****************************************************
+read one entry from a directory
+*******************************************************/
+struct dirent *smbw_readdir(DIR *dirp)
+{
+ struct smbw_dir *d = (struct smbw_dir *)dirp;
+ static union {
+ char buf[DIRP_SIZE];
+ struct dirent de;
+ } dbuf;
+
+ if (smbw_getdents(d->fd, &dbuf.de, DIRP_SIZE) > 0)
+ return &dbuf.de;
+
+ return NULL;
+}
+
+/*****************************************************
+close a DIR*
+*******************************************************/
+int smbw_closedir(DIR *dirp)
+{
+ struct smbw_dir *d = (struct smbw_dir *)dirp;
+ return smbw_close(d->fd);
+}
+
+/*****************************************************
+seek in a directory
+*******************************************************/
+void smbw_seekdir(DIR *dirp, off_t offset)
+{
+ struct smbw_dir *d = (struct smbw_dir *)dirp;
+ smbw_dir_lseek(d->fd,offset, SEEK_SET);
+}
+
+/*****************************************************
+current loc in a directory
+*******************************************************/
+off_t smbw_telldir(DIR *dirp)
+{
+ struct smbw_dir *d = (struct smbw_dir *)dirp;
+ return smbw_dir_lseek(d->fd,0,SEEK_CUR);
+}
diff --git a/source/smbwrapper/smbw_stat.c b/source/smbwrapper/smbw_stat.c
new file mode 100644
index 00000000000..bb76ef006a4
--- /dev/null
+++ b/source/smbwrapper/smbw_stat.c
@@ -0,0 +1,254 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB wrapper stat functions
+ Copyright (C) Andrew Tridgell 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+extern int smbw_busy;
+
+/*****************************************************
+setup basic info in a stat structure
+*******************************************************/
+void smbw_setup_stat(struct stat *st, char *fname, size_t size, int mode)
+{
+ st->st_mode = 0;
+
+ if (IS_DOS_DIR(mode)) {
+ st->st_mode = SMBW_DIR_MODE;
+ } else {
+ st->st_mode = SMBW_FILE_MODE;
+ }
+
+ if (IS_DOS_ARCHIVE(mode)) st->st_mode |= S_IXUSR;
+ if (IS_DOS_SYSTEM(mode)) st->st_mode |= S_IXGRP;
+ if (IS_DOS_HIDDEN(mode)) st->st_mode |= S_IXOTH;
+ if (!IS_DOS_READONLY(mode)) st->st_mode |= S_IWUSR;
+
+ st->st_size = size;
+#ifdef HAVE_STAT_ST_BLKSIZE
+ st->st_blksize = 512;
+#endif
+#ifdef HAVE_STAT_ST_BLOCKS
+ st->st_blocks = (size+511)/512;
+#endif
+ st->st_uid = getuid();
+ st->st_gid = getgid();
+ if (IS_DOS_DIR(mode)) {
+ st->st_nlink = 2;
+ } else {
+ st->st_nlink = 1;
+ }
+ if (st->st_ino == 0) {
+ st->st_ino = smbw_inode(fname);
+ }
+}
+
+
+/*****************************************************
+try to do a QPATHINFO and if that fails then do a getatr
+this is needed because win95 sometimes refuses the qpathinfo
+*******************************************************/
+BOOL smbw_getatr(struct smbw_server *srv, char *path,
+ uint16 *mode, size_t *size,
+ time_t *c_time, time_t *a_time, time_t *m_time,
+ SMB_INO_T *ino)
+{
+ DEBUG(4,("sending qpathinfo\n"));
+
+ if (!srv->no_pathinfo2 &&
+ cli_qpathinfo2(&srv->cli, path, c_time, a_time, m_time, NULL,
+ size, mode, ino)) return True;
+
+ /* if this is NT then don't bother with the getatr */
+ if (srv->cli.capabilities & CAP_NT_SMBS) return False;
+
+ if (cli_getatr(&srv->cli, path, mode, size, m_time)) {
+ a_time = c_time = m_time;
+ srv->no_pathinfo2 = True;
+ return True;
+ }
+ return False;
+}
+
+
+static struct print_job_info printjob;
+
+/*****************************************************
+gather info from a printjob listing
+*******************************************************/
+static void smbw_printjob_stat(struct print_job_info *job)
+{
+ if (strcmp(job->name, printjob.name) == 0) {
+ printjob = *job;
+ }
+}
+
+/*****************************************************
+stat a printjob
+*******************************************************/
+int smbw_stat_printjob(struct smbw_server *srv,char *path,
+ size_t *size, time_t *m_time)
+{
+ if (path[0] == '\\') path++;
+
+ ZERO_STRUCT(printjob);
+
+ fstrcpy(printjob.name, path);
+ cli_print_queue(&srv->cli, smbw_printjob_stat);
+
+ if (size) {
+ *size = printjob.size;
+ }
+ if (m_time) {
+ *m_time = printjob.t;
+ }
+ return printjob.id;
+}
+
+
+/*****************************************************
+a wrapper for fstat()
+*******************************************************/
+int smbw_fstat(int fd, struct stat *st)
+{
+ struct smbw_file *file;
+ time_t c_time, a_time, m_time;
+ size_t size;
+ uint16 mode;
+ SMB_INO_T ino = 0;
+
+ smbw_busy++;
+
+ ZERO_STRUCTP(st);
+
+ file = smbw_file(fd);
+ if (!file) {
+ int ret = smbw_dir_fstat(fd, st);
+ smbw_busy--;
+ return ret;
+ }
+
+ if (!cli_qfileinfo(&file->srv->cli, file->f->cli_fd,
+ &mode, &size, &c_time, &a_time, &m_time, NULL,
+ &ino) &&
+ !cli_getattrE(&file->srv->cli, file->f->cli_fd,
+ &mode, &size, &c_time, &a_time, &m_time)) {
+ errno = EINVAL;
+ smbw_busy--;
+ return -1;
+ }
+
+ st->st_ino = ino;
+
+ smbw_setup_stat(st, file->f->fname, size, mode);
+
+ st->st_atime = a_time;
+ st->st_ctime = c_time;
+ st->st_mtime = m_time;
+ st->st_dev = file->srv->dev;
+
+ smbw_busy--;
+ return 0;
+}
+
+
+/*****************************************************
+a wrapper for stat()
+*******************************************************/
+int smbw_stat(const char *fname, struct stat *st)
+{
+ struct smbw_server *srv;
+ fstring server, share;
+ pstring path;
+ time_t m_time=0, a_time=0, c_time=0;
+ size_t size=0;
+ uint16 mode=0;
+ SMB_INO_T ino = 0;
+ int result = 0;
+
+ ZERO_STRUCTP(st);
+
+ if (!fname) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ DEBUG(4,("stat(%s)\n", fname));
+
+ smbw_init();
+
+ smbw_busy++;
+
+ /* work out what server they are after */
+ smbw_parse_path(fname, server, share, path);
+
+ /* get a connection to the server */
+ srv = smbw_server(server, share);
+ if (!srv) {
+
+ /* For shares we aren't allowed to connect to, or no master
+ browser found, return an empty directory */
+
+ if ((server[0] && share[0] && !path[0] && errno == EACCES) ||
+ (!path[0] && errno == ENOENT)) {
+ mode = aDIR | aRONLY;
+ smbw_setup_stat(st, path, size, mode);
+ goto done;
+ }
+
+ /* smbw_server sets errno */
+ result = -1;
+ goto done;
+ }
+
+ DEBUG(4,("smbw_stat\n"));
+
+ if (strncmp(srv->cli.dev,"IPC",3) == 0) {
+ mode = aDIR | aRONLY;
+ } else if (strncmp(srv->cli.dev,"LPT",3) == 0) {
+ if (strcmp(path,"\\") == 0) {
+ mode = aDIR | aRONLY;
+ } else {
+ mode = aRONLY;
+ smbw_stat_printjob(srv, path, &size, &m_time);
+ c_time = a_time = m_time;
+ }
+ } else {
+ if (!smbw_getatr(srv, path,
+ &mode, &size, &c_time, &a_time, &m_time,
+ &ino)) {
+ errno = smbw_errno(&srv->cli);
+ result = -1;
+ goto done;
+ }
+ }
+
+ st->st_ino = ino;
+
+ smbw_setup_stat(st, path, size, mode);
+
+ st->st_atime = a_time;
+ st->st_ctime = c_time;
+ st->st_mtime = m_time;
+ st->st_dev = srv->dev;
+
+ done:
+ smbw_busy--;
+ return result;
+}
diff --git a/source/smbwrapper/wrapped.c b/source/smbwrapper/wrapped.c
new file mode 100644
index 00000000000..338ee0d5b1b
--- /dev/null
+++ b/source/smbwrapper/wrapped.c
@@ -0,0 +1,705 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB wrapper functions
+ Copyright (C) Andrew Tridgell 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* NOTE: This file WILL produce compiler warnings. They are unavoidable
+
+ Do not try and get rid of them by including other include files or
+ by including includes.h or proto.h or you will break portability.
+ */
+
+#include "config.h"
+#include <sys/types.h>
+#include <errno.h>
+#include "realcalls.h"
+
+#ifndef NULL
+# define NULL ((void *)0)
+#endif
+
+ int open(char *name, int flags, mode_t mode)
+{
+ if (smbw_path(name)) {
+ return smbw_open(name, flags, mode);
+ }
+
+ return real_open(name, flags, mode);
+}
+
+#ifdef HAVE__OPEN
+ int _open(char *name, int flags, mode_t mode)
+{
+ return open(name, flags, mode);
+}
+#elif HAVE___OPEN
+ int __open(char *name, int flags, mode_t mode)
+{
+ return open(name, flags, mode);
+}
+#endif
+
+
+#ifdef HAVE_OPEN64
+ int open64(char *name, int flags, mode_t mode)
+{
+ if (smbw_path(name)) {
+ return smbw_open(name, flags, mode);
+ }
+
+ return real_open64(name, flags, mode);
+}
+#endif
+
+#ifndef NO_OPEN64_ALIAS
+#ifdef HAVE__OPEN64
+ int _open64(char *name, int flags, mode_t mode)
+{
+ return open64(name, flags, mode);
+}
+#elif HAVE___OPEN64
+ int __open64(char *name, int flags, mode_t mode)
+{
+ return open64(name, flags, mode);
+}
+#endif
+#endif
+
+#ifdef HAVE_PREAD
+ ssize_t pread(int fd, void *buf, size_t size, off_t ofs)
+{
+ if (smbw_fd(fd)) {
+ return smbw_pread(fd, buf, size, ofs);
+ }
+
+ return real_pread(fd, buf, size, ofs);
+}
+#endif
+
+#if defined(HAVE_PREAD64) && defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT)
+ ssize_t pread64(int fd, void *buf, size_t size, off64_t ofs)
+{
+ if (smbw_fd(fd)) {
+ return smbw_pread(fd, buf, size, ofs);
+ }
+
+ return real_pread64(fd, buf, size, ofs);
+}
+#endif
+
+#ifdef HAVE_PWRITE
+ ssize_t pwrite(int fd, void *buf, size_t size, off_t ofs)
+{
+ if (smbw_fd(fd)) {
+ return smbw_pwrite(fd, buf, size, ofs);
+ }
+
+ return real_pwrite(fd, buf, size, ofs);
+}
+#endif
+
+#if defined(HAVE_PWRITE64) && defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT)
+ ssize_t pwrite64(int fd, void *buf, size_t size, off64_t ofs)
+{
+ if (smbw_fd(fd)) {
+ return smbw_pwrite(fd, buf, size, ofs);
+ }
+
+ return real_pwrite64(fd, buf, size, ofs);
+}
+#endif
+
+
+ int chdir(char *name)
+{
+ return smbw_chdir(name);
+}
+
+#ifdef HAVE___CHDIR
+ int __chdir(char *name)
+{
+ return chdir(name);
+}
+#elif HAVE__CHDIR
+ int _chdir(char *name)
+{
+ return chdir(name);
+}
+#endif
+
+
+ int close(int fd)
+{
+ if (smbw_fd(fd)) {
+ return smbw_close(fd);
+ }
+ if (smbw_local_fd(fd)) {
+ errno = EBADF;
+ return -1;
+ }
+
+ return real_close(fd);
+}
+
+#ifdef HAVE___CLOSE
+ int __close(int fd)
+{
+ return close(fd);
+}
+#elif HAVE__CLOSE
+ int _close(int fd)
+{
+ return close(fd);
+}
+#endif
+
+
+ int fchdir(int fd)
+{
+ return smbw_fchdir(fd);
+}
+
+#ifdef HAVE___FCHDIR
+ int __fchdir(int fd)
+{
+ return fchdir(fd);
+}
+#elif HAVE__FCHDIR
+ int _fchdir(int fd)
+{
+ return fchdir(fd);
+}
+#endif
+
+
+ int fcntl(int fd, int cmd, long arg)
+{
+ if (smbw_fd(fd)) {
+ return smbw_fcntl(fd, cmd, arg);
+ }
+
+ return real_fcntl(fd, cmd, arg);
+}
+
+
+#ifdef HAVE___FCNTL
+ int __fcntl(int fd, int cmd, long arg)
+{
+ return fcntl(fd, cmd, arg);
+}
+#elif HAVE__FCNTL
+ int _fcntl(int fd, int cmd, long arg)
+{
+ return fcntl(fd, cmd, arg);
+}
+#endif
+
+
+
+#ifdef real_getdents
+ int getdents(int fd, void *dirp, unsigned int count)
+{
+ if (smbw_fd(fd)) {
+ return smbw_getdents(fd, dirp, count);
+ }
+
+ return real_getdents(fd, dirp, count);
+}
+#endif
+
+#ifdef HAVE___GETDENTS
+ int __getdents(int fd, void *dirp, unsigned int count)
+{
+ return getdents(fd, dirp, count);
+}
+#elif HAVE__GETDENTS
+ int _getdents(int fd, void *dirp, unsigned int count)
+{
+ return getdents(fd, dirp, count);
+}
+#endif
+
+
+ off_t lseek(int fd, off_t offset, int whence)
+{
+ if (smbw_fd(fd)) {
+ return smbw_lseek(fd, offset, whence);
+ }
+
+ return real_lseek(fd, offset, whence);
+}
+
+#ifdef HAVE___LSEEK
+ off_t __lseek(int fd, off_t offset, int whence)
+{
+ return lseek(fd, offset, whence);
+}
+#elif HAVE__LSEEK
+ off_t _lseek(int fd, off_t offset, int whence)
+{
+ return lseek(fd, offset, whence);
+}
+#endif
+
+
+ ssize_t read(int fd, void *buf, size_t count)
+{
+ if (smbw_fd(fd)) {
+ return smbw_read(fd, buf, count);
+ }
+
+ return real_read(fd, buf, count);
+}
+
+#ifdef HAVE___READ
+ ssize_t __read(int fd, void *buf, size_t count)
+{
+ return read(fd, buf, count);
+}
+#elif HAVE__READ
+ ssize_t _read(int fd, void *buf, size_t count)
+{
+ return read(fd, buf, count);
+}
+#endif
+
+
+ ssize_t write(int fd, void *buf, size_t count)
+{
+ if (smbw_fd(fd)) {
+ return smbw_write(fd, buf, count);
+ }
+
+ return real_write(fd, buf, count);
+}
+
+#ifdef HAVE___WRITE
+ ssize_t __write(int fd, void *buf, size_t count)
+{
+ return write(fd, buf, count);
+}
+#elif HAVE__WRITE
+ ssize_t _write(int fd, void *buf, size_t count)
+{
+ return write(fd, buf, count);
+}
+#endif
+
+
+
+ int access(char *name, int mode)
+{
+ if (smbw_path(name)) {
+ return smbw_access(name, mode);
+ }
+
+ return real_access(name, mode);
+}
+
+
+
+ int chmod(char *name,mode_t mode)
+{
+ if (smbw_path(name)) {
+ return smbw_chmod(name, mode);
+ }
+
+ return real_chmod(name, mode);
+}
+
+
+
+ int chown(char *name,uid_t owner, gid_t group)
+{
+ if (smbw_path(name)) {
+ return smbw_chown(name, owner, group);
+ }
+
+ return real_chown(name, owner, group);
+}
+
+
+ char *getcwd(char *buf, size_t size)
+{
+ return (char *)smbw_getcwd(buf, size);
+}
+
+
+
+
+ int mkdir(char *name, mode_t mode)
+{
+ if (smbw_path(name)) {
+ return smbw_mkdir(name, mode);
+ }
+
+ return real_mkdir(name, mode);
+}
+
+
+#if HAVE___FXSTAT
+ int __fxstat(int vers, int fd, void *st)
+{
+ double xx[32];
+ int ret;
+
+ if (smbw_fd(fd)) {
+ return smbw_fstat(fd, st);
+ }
+
+ ret = real_fstat(fd, xx);
+ xstat_convert(vers, st, xx);
+ return ret;
+}
+#endif
+
+#if HAVE___XSTAT
+ int __xstat(int vers, char *name, void *st)
+{
+ double xx[32];
+ int ret;
+
+ if (smbw_path(name)) {
+ return smbw_stat(name, st);
+ }
+
+ ret = real_stat(name, xx);
+ xstat_convert(vers, st, xx);
+ return ret;
+}
+#endif
+
+
+#if HAVE___LXSTAT
+ int __lxstat(int vers, char *name, void *st)
+{
+ double xx[32];
+ int ret;
+
+ if (smbw_path(name)) {
+ return smbw_stat(name, st);
+ }
+
+ ret = real_lstat(name, xx);
+ xstat_convert(vers, st, xx);
+ return ret;
+}
+#endif
+
+
+ int stat(char *name, void *st)
+{
+#if HAVE___XSTAT
+ return __xstat(0, name, st);
+#else
+ if (smbw_path(name)) {
+ return smbw_stat(name, st);
+ }
+ return real_stat(name, st);
+#endif
+}
+
+ int lstat(char *name, void *st)
+{
+#if HAVE___LXSTAT
+ return __lxstat(0, name, st);
+#else
+ if (smbw_path(name)) {
+ return smbw_stat(name, st);
+ }
+ return real_lstat(name, st);
+#endif
+}
+
+ int fstat(int fd, void *st)
+{
+#if HAVE___LXSTAT
+ return __fxstat(0, fd, st);
+#else
+ if (smbw_fd(fd)) {
+ return smbw_fstat(fd, st);
+ }
+ return real_fstat(fd, st);
+#endif
+}
+
+
+ int unlink(char *name)
+{
+ if (smbw_path(name)) {
+ return smbw_unlink(name);
+ }
+
+ return real_unlink(name);
+}
+
+
+#ifdef HAVE_UTIME
+ int utime(char *name,void *tvp)
+{
+ if (smbw_path(name)) {
+ return smbw_utime(name, tvp);
+ }
+
+ return real_utime(name, tvp);
+}
+#endif
+
+#ifdef HAVE_UTIMES
+ int utimes(const char *name, const struct timeval *tvp)
+{
+ if (smbw_path(name)) {
+ return smbw_utimes(name, tvp);
+ }
+
+ return real_utimes(name, tvp);
+}
+#endif
+
+ int readlink(char *path, char *buf, size_t bufsize)
+{
+ if (smbw_path(path)) {
+ return smbw_readlink(path, buf, bufsize);
+ }
+
+ return real_readlink(path, buf, bufsize);
+}
+
+
+ int rename(char *oldname,char *newname)
+{
+ int p1, p2;
+ p1 = smbw_path(oldname);
+ p2 = smbw_path(newname);
+ if (p1 ^ p2) {
+ /* can't cross filesystem boundaries */
+ errno = EXDEV;
+ return -1;
+ }
+ if (p1 && p2) {
+ return smbw_rename(oldname, newname);
+ }
+
+ return real_rename(oldname, newname);
+}
+
+ int rmdir(char *name)
+{
+ if (smbw_path(name)) {
+ return smbw_rmdir(name);
+ }
+
+ return real_rmdir(name);
+}
+
+
+ int symlink(char *topath,char *frompath)
+{
+ int p1, p2;
+ p1 = smbw_path(topath);
+ p2 = smbw_path(frompath);
+ if (p1 || p2) {
+ /* can't handle symlinks */
+ errno = EPERM;
+ return -1;
+ }
+
+ return real_symlink(topath, frompath);
+}
+
+ int dup(int fd)
+{
+ if (smbw_fd(fd)) {
+ return smbw_dup(fd);
+ }
+
+ return real_dup(fd);
+}
+
+ int dup2(int oldfd, int newfd)
+{
+ if (smbw_fd(newfd)) {
+ close(newfd);
+ }
+
+ if (smbw_fd(oldfd)) {
+ return smbw_dup2(oldfd, newfd);
+ }
+
+ return real_dup2(oldfd, newfd);
+}
+
+#ifdef real_opendir
+ void *opendir(char *name)
+{
+ if (smbw_path(name)) {
+ return (void *)smbw_opendir(name);
+ }
+
+ return (void *)real_opendir(name);
+}
+#endif
+
+#ifdef real_readdir
+ void *readdir(void *dir)
+{
+ if (smbw_dirp(dir)) {
+ return (void *)smbw_readdir(dir);
+ }
+
+ return (void *)real_readdir(dir);
+}
+#endif
+
+#ifdef real_closedir
+ int closedir(void *dir)
+{
+ if (smbw_dirp(dir)) {
+ return smbw_closedir(dir);
+ }
+
+ return real_closedir(dir);
+}
+#endif
+
+#ifdef real_telldir
+ off_t telldir(void *dir)
+{
+ if (smbw_dirp(dir)) {
+ return smbw_telldir(dir);
+ }
+
+ return real_telldir(dir);
+}
+#endif
+
+#ifdef real_seekdir
+ int seekdir(void *dir, off_t offset)
+{
+ if (smbw_dirp(dir)) {
+ smbw_seekdir(dir, offset);
+ return 0;
+ }
+
+ real_seekdir(dir, offset);
+ return 0;
+}
+#endif
+
+
+#ifndef NO_ACL_WRAPPER
+ int acl(char *pathp, int cmd, int nentries, void *aclbufp)
+{
+ if (smbw_path(pathp)) {
+ return smbw_acl(pathp, cmd, nentries, aclbufp);
+ }
+
+ return real_acl(pathp, cmd, nentries, aclbufp);
+}
+#endif
+
+#ifndef NO_FACL_WRAPPER
+ int facl(int fd, int cmd, int nentries, void *aclbufp)
+{
+ if (smbw_fd(fd)) {
+ return smbw_facl(fd, cmd, nentries, aclbufp);
+ }
+
+ return real_facl(fd, cmd, nentries, aclbufp);
+}
+#endif
+
+ int creat(char *path, mode_t mode)
+{
+ extern int creat_bits;
+ return open(path, creat_bits, mode);
+}
+
+#ifdef HAVE_CREAT64
+ int creat64(char *path, mode_t mode)
+{
+ extern int creat_bits;
+ return open64(path, creat_bits, mode);
+}
+#endif
+
+#ifdef HAVE_STAT64
+ int stat64(char *name, void *st64)
+{
+ if (smbw_path(name)) {
+ double xx[32];
+ int ret = stat(name, xx);
+ stat64_convert(xx, st64);
+ return ret;
+ }
+ return real_stat64(name, st64);
+}
+
+ int fstat64(int fd, void *st64)
+{
+ if (smbw_fd(fd)) {
+ double xx[32];
+ int ret = fstat(fd, xx);
+ stat64_convert(xx, st64);
+ return ret;
+ }
+ return real_fstat64(fd, st64);
+}
+
+ int lstat64(char *name, void *st64)
+{
+ if (smbw_path(name)) {
+ double xx[32];
+ int ret = lstat(name, xx);
+ stat64_convert(xx, st64);
+ return ret;
+ }
+ return real_lstat64(name, st64);
+}
+#endif
+
+#ifdef HAVE_LLSEEK
+ offset_t llseek(int fd, offset_t ofs, int whence)
+{
+ if (smbw_fd(fd)) {
+ return lseek(fd, ofs, whence);
+ }
+ return real_llseek(fd, ofs, whence);
+}
+#endif
+
+#ifdef HAVE_READDIR64
+ void *readdir64(void *dir)
+{
+ if (smbw_dirp(dir)) {
+ static double xx[70];
+ void *d;
+ d = (void *)readdir(dir);
+ if (!d) return NULL;
+ dirent64_convert(d, xx);
+ return xx;
+ }
+ return (void *)real_readdir64(dir);
+}
+#endif
+
+ int fork(void)
+{
+ return smbw_fork();
+}
+
diff --git a/source/stf/.cvsignore b/source/stf/.cvsignore
new file mode 100644
index 00000000000..bcf41506063
--- /dev/null
+++ b/source/stf/.cvsignore
@@ -0,0 +1,2 @@
+*.pyc
+testtmp
diff --git a/source/stf/README.stf b/source/stf/README.stf
new file mode 100644
index 00000000000..3fbd33cb6cc
--- /dev/null
+++ b/source/stf/README.stf
@@ -0,0 +1,3 @@
+This directory contains the Samba Testing Framework, a Python-based
+system for exercising Samba in various ways. It is quite small at the
+moment.
diff --git a/source/stf/comfychair.py b/source/stf/comfychair.py
new file mode 100644
index 00000000000..522f9bedeba
--- /dev/null
+++ b/source/stf/comfychair.py
@@ -0,0 +1,445 @@
+#! /usr/bin/env python
+
+# Copyright (C) 2002, 2003 by Martin Pool <mbp@samba.org>
+# Copyright (C) 2003 by Tim Potter <tpot@samba.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+"""comfychair: a Python-based instrument of software torture.
+
+Copyright (C) 2002, 2003 by Martin Pool <mbp@samba.org>
+Copyright (C) 2003 by Tim Potter <tpot@samba.org>
+
+This is a test framework designed for testing programs written in
+Python, or (through a fork/exec interface) any other language.
+
+For more information, see the file README.comfychair.
+
+To run a test suite based on ComfyChair, just run it as a program.
+"""
+
+import sys, re
+
+
+class TestCase:
+ """A base class for tests. This class defines required functions which
+ can optionally be overridden by subclasses. It also provides some
+ utility functions for"""
+
+ def __init__(self):
+ self.test_log = ""
+ self.background_pids = []
+ self._cleanups = []
+ self._enter_rundir()
+ self._save_environment()
+ self.add_cleanup(self.teardown)
+
+
+ # --------------------------------------------------
+ # Save and restore directory
+ def _enter_rundir(self):
+ import os
+ self.basedir = os.getcwd()
+ self.add_cleanup(self._restore_directory)
+ self.rundir = os.path.join(self.basedir,
+ 'testtmp',
+ self.__class__.__name__)
+ self.tmpdir = os.path.join(self.rundir, 'tmp')
+ os.system("rm -fr %s" % self.rundir)
+ os.makedirs(self.tmpdir)
+ os.system("mkdir -p %s" % self.rundir)
+ os.chdir(self.rundir)
+
+ def _restore_directory(self):
+ import os
+ os.chdir(self.basedir)
+
+ # --------------------------------------------------
+ # Save and restore environment
+ def _save_environment(self):
+ import os
+ self._saved_environ = os.environ.copy()
+ self.add_cleanup(self._restore_environment)
+
+ def _restore_environment(self):
+ import os
+ os.environ.clear()
+ os.environ.update(self._saved_environ)
+
+
+ def setup(self):
+ """Set up test fixture."""
+ pass
+
+ def teardown(self):
+ """Tear down test fixture."""
+ pass
+
+ def runtest(self):
+ """Run the test."""
+ pass
+
+
+ def add_cleanup(self, c):
+ """Queue a cleanup to be run when the test is complete."""
+ self._cleanups.append(c)
+
+
+ def fail(self, reason = ""):
+ """Say the test failed."""
+ raise AssertionError(reason)
+
+
+ #############################################################
+ # Requisition methods
+
+ def require(self, predicate, message):
+ """Check a predicate for running this test.
+
+If the predicate value is not true, the test is skipped with a message explaining
+why."""
+ if not predicate:
+ raise NotRunError, message
+
+ def require_root(self):
+ """Skip this test unless run by root."""
+ import os
+ self.require(os.getuid() == 0,
+ "must be root to run this test")
+
+ #############################################################
+ # Assertion methods
+
+ def assert_(self, expr, reason = ""):
+ if not expr:
+ raise AssertionError(reason)
+
+ def assert_equal(self, a, b):
+ if not a == b:
+ raise AssertionError("assertEquals failed: %s" % `(a, b)`)
+
+ def assert_notequal(self, a, b):
+ if a == b:
+ raise AssertionError("assertNotEqual failed: %s" % `(a, b)`)
+
+ def assert_re_match(self, pattern, s):
+ """Assert that a string matches a particular pattern
+
+ Inputs:
+ pattern string: regular expression
+ s string: to be matched
+
+ Raises:
+ AssertionError if not matched
+ """
+ if not re.match(pattern, s):
+ raise AssertionError("string does not match regexp\n"
+ " string: %s\n"
+ " re: %s" % (`s`, `pattern`))
+
+ def assert_re_search(self, pattern, s):
+ """Assert that a string *contains* a particular pattern
+
+ Inputs:
+ pattern string: regular expression
+ s string: to be searched
+
+ Raises:
+ AssertionError if not matched
+ """
+ if not re.search(pattern, s):
+ raise AssertionError("string does not contain regexp\n"
+ " string: %s\n"
+ " re: %s" % (`s`, `pattern`))
+
+
+ def assert_no_file(self, filename):
+ import os.path
+ assert not os.path.exists(filename), ("file exists but should not: %s" % filename)
+
+
+ #############################################################
+ # Methods for running programs
+
+ def runcmd_background(self, cmd):
+ import os
+ self.test_log = self.test_log + "Run in background:\n" + `cmd` + "\n"
+ pid = os.fork()
+ if pid == 0:
+ # child
+ try:
+ os.execvp("/bin/sh", ["/bin/sh", "-c", cmd])
+ finally:
+ os._exit(127)
+ self.test_log = self.test_log + "pid: %d\n" % pid
+ return pid
+
+
+ def runcmd(self, cmd, expectedResult = 0):
+ """Run a command, fail if the command returns an unexpected exit
+ code. Return the output produced."""
+ rc, output, stderr = self.runcmd_unchecked(cmd)
+ if rc != expectedResult:
+ raise AssertionError("""command returned %d; expected %s: \"%s\"
+stdout:
+%s
+stderr:
+%s""" % (rc, expectedResult, cmd, output, stderr))
+
+ return output, stderr
+
+
+ def run_captured(self, cmd):
+ """Run a command, capturing stdout and stderr.
+
+ Based in part on popen2.py
+
+ Returns (waitstatus, stdout, stderr)."""
+ import os, types
+ pid = os.fork()
+ if pid == 0:
+ # child
+ try:
+ pid = os.getpid()
+ openmode = os.O_WRONLY|os.O_CREAT|os.O_TRUNC
+
+ outfd = os.open('%d.out' % pid, openmode, 0666)
+ os.dup2(outfd, 1)
+ os.close(outfd)
+
+ errfd = os.open('%d.err' % pid, openmode, 0666)
+ os.dup2(errfd, 2)
+ os.close(errfd)
+
+ if isinstance(cmd, types.StringType):
+ cmd = ['/bin/sh', '-c', cmd]
+
+ os.execvp(cmd[0], cmd)
+ finally:
+ os._exit(127)
+ else:
+ # parent
+ exited_pid, waitstatus = os.waitpid(pid, 0)
+ stdout = open('%d.out' % pid).read()
+ stderr = open('%d.err' % pid).read()
+ return waitstatus, stdout, stderr
+
+
+ def runcmd_unchecked(self, cmd, skip_on_noexec = 0):
+ """Invoke a command; return (exitcode, stdout, stderr)"""
+ import os
+ waitstatus, stdout, stderr = self.run_captured(cmd)
+ assert not os.WIFSIGNALED(waitstatus), \
+ ("%s terminated with signal %d" % (`cmd`, os.WTERMSIG(waitstatus)))
+ rc = os.WEXITSTATUS(waitstatus)
+ self.test_log = self.test_log + ("""Run command: %s
+Wait status: %#x (exit code %d, signal %d)
+stdout:
+%s
+stderr:
+%s""" % (cmd, waitstatus, os.WEXITSTATUS(waitstatus), os.WTERMSIG(waitstatus),
+ stdout, stderr))
+ if skip_on_noexec and rc == 127:
+ # Either we could not execute the command or the command
+ # returned exit code 127. According to system(3) we can't
+ # tell the difference.
+ raise NotRunError, "could not execute %s" % `cmd`
+ return rc, stdout, stderr
+
+
+ def explain_failure(self, exc_info = None):
+ print "test_log:"
+ print self.test_log
+
+
+ def log(self, msg):
+ """Log a message to the test log. This message is displayed if
+ the test fails, or when the runtests function is invoked with
+ the verbose option."""
+ self.test_log = self.test_log + msg + "\n"
+
+
+class NotRunError(Exception):
+ """Raised if a test must be skipped because of missing resources"""
+ def __init__(self, value = None):
+ self.value = value
+
+
+def _report_error(case, debugger):
+ """Ask the test case to explain failure, and optionally run a debugger
+
+ Input:
+ case TestCase instance
+ debugger if true, a debugger function to be applied to the traceback
+"""
+ import sys
+ ex = sys.exc_info()
+ print "-----------------------------------------------------------------"
+ if ex:
+ import traceback
+ traceback.print_exc(file=sys.stdout)
+ case.explain_failure()
+ print "-----------------------------------------------------------------"
+
+ if debugger:
+ tb = ex[2]
+ debugger(tb)
+
+
+def runtests(test_list, verbose = 0, debugger = None):
+ """Run a series of tests.
+
+ Inputs:
+ test_list sequence of TestCase classes
+ verbose print more information as testing proceeds
+ debugger debugger object to be applied to errors
+
+ Returns:
+ unix return code: 0 for success, 1 for failures, 2 for test failure
+ """
+ import traceback
+ ret = 0
+ for test_class in test_list:
+ print "%-30s" % _test_name(test_class),
+ # flush now so that long running tests are easier to follow
+ sys.stdout.flush()
+
+ obj = None
+ try:
+ try: # run test and show result
+ obj = test_class()
+ obj.setup()
+ obj.runtest()
+ print "OK"
+ except KeyboardInterrupt:
+ print "INTERRUPT"
+ _report_error(obj, debugger)
+ ret = 2
+ break
+ except NotRunError, msg:
+ print "NOTRUN, %s" % msg.value
+ except:
+ print "FAIL"
+ _report_error(obj, debugger)
+ ret = 1
+ finally:
+ while obj and obj._cleanups:
+ try:
+ apply(obj._cleanups.pop())
+ except KeyboardInterrupt:
+ print "interrupted during teardown"
+ _report_error(obj, debugger)
+ ret = 2
+ break
+ except:
+ print "error during teardown"
+ _report_error(obj, debugger)
+ ret = 1
+ # Display log file if we're verbose
+ if ret == 0 and verbose:
+ obj.explain_failure()
+
+ return ret
+
+
+def _test_name(test_class):
+ """Return a human-readable name for a test class.
+ """
+ try:
+ return test_class.__name__
+ except:
+ return `test_class`
+
+
+def print_help():
+ """Help for people running tests"""
+ import sys
+ print """%s: software test suite based on ComfyChair
+
+usage:
+ To run all tests, just run this program. To run particular tests,
+ list them on the command line.
+
+options:
+ --help show usage message
+ --list list available tests
+ --verbose, -v show more information while running tests
+ --post-mortem, -p enter Python debugger on error
+""" % sys.argv[0]
+
+
+def print_list(test_list):
+ """Show list of available tests"""
+ for test_class in test_list:
+ print " %s" % _test_name(test_class)
+
+
+def main(tests, extra_tests=[]):
+ """Main entry point for test suites based on ComfyChair.
+
+ inputs:
+ tests Sequence of TestCase subclasses to be run by default.
+ extra_tests Sequence of TestCase subclasses that are available but
+ not run by default.
+
+Test suites should contain this boilerplate:
+
+ if __name__ == '__main__':
+ comfychair.main(tests)
+
+This function handles standard options such as --help and --list, and
+by default runs all tests in the suggested order.
+
+Calls sys.exit() on completion.
+"""
+ from sys import argv
+ import getopt, sys
+
+ opt_verbose = 0
+ debugger = None
+
+ opts, args = getopt.getopt(argv[1:], 'pv',
+ ['help', 'list', 'verbose', 'post-mortem'])
+ for opt, opt_arg in opts:
+ if opt == '--help':
+ print_help()
+ return
+ elif opt == '--list':
+ print_list(tests + extra_tests)
+ return
+ elif opt == '--verbose' or opt == '-v':
+ opt_verbose = 1
+ elif opt == '--post-mortem' or opt == '-p':
+ import pdb
+ debugger = pdb.post_mortem
+
+ if args:
+ all_tests = tests + extra_tests
+ by_name = {}
+ for t in all_tests:
+ by_name[_test_name(t)] = t
+ which_tests = []
+ for name in args:
+ which_tests.append(by_name[name])
+ else:
+ which_tests = tests
+
+ sys.exit(runtests(which_tests, verbose=opt_verbose,
+ debugger=debugger))
+
+
+if __name__ == '__main__':
+ print __doc__
diff --git a/source/stf/example.py b/source/stf/example.py
new file mode 100755
index 00000000000..96e34bf3d3b
--- /dev/null
+++ b/source/stf/example.py
@@ -0,0 +1,38 @@
+#! /usr/bin/env python
+
+# Copyright (C) 2003 by Martin Pool <mbp@samba.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+
+"""example of using ComfyChair"""
+
+import comfychair
+
+class OnePlusOne(comfychair.TestCase):
+ def runtest(self):
+ self.assert_(1 + 1 == 2)
+
+class FailTest(comfychair.TestCase):
+ def runtest(self):
+ self.assert_(1 + 1 == 3)
+
+tests = [OnePlusOne]
+extra_tests = [FailTest]
+
+if __name__ == '__main__':
+ comfychair.main(tests, extra_tests=extra_tests)
+
diff --git a/source/stf/info3cache.py b/source/stf/info3cache.py
new file mode 100755
index 00000000000..96d5a1d4596
--- /dev/null
+++ b/source/stf/info3cache.py
@@ -0,0 +1,54 @@
+#!/usr/bin/python
+#
+# Upon a winbindd authentication, test that an info3 record is cached in
+# netsamlogon_cache.tdb and cache records are removed from winbindd_cache.tdb
+#
+
+import comfychair, stf
+from samba import tdb, winbind
+
+#
+# We want to implement the following test on a win2k native mode domain.
+#
+# 1. trash netsamlogon_cache.tdb
+# 2. wbinfo -r DOMAIN\Administrator [FAIL]
+# 3. wbinfo --auth-crap DOMAIN\Administrator%password [PASS]
+# 4. wbinfo -r DOMAIN\Administrator [PASS]
+#
+# Also for step 3 we want to try 'wbinfo --auth-smbd' and
+# 'wbinfo --auth-plaintext'
+#
+
+#
+# TODO: To implement this test we need to be able to
+#
+# - pass username%password combination for an invidivual winbindd request
+# (so we can get the administrator SID so we can clear the info3 cache)
+#
+# - start/restart winbindd (to trash the winbind cache)
+#
+# - from samba import dynconfig (to find location of info3 cache)
+#
+# - be able to modify the winbindd cache (to set/reset individual winbind
+# cache entries)
+#
+# - have --auth-crap present in HEAD
+#
+
+class WinbindAuthCrap(comfychair.TestCase):
+ def runtest(self):
+ raise comfychair.NotRunError, "not implemented"
+
+class WinbindAuthSmbd(comfychair.TestCase):
+ def runtest(self):
+ # Grr - winbindd in HEAD doesn't contain the auth_smbd function
+ raise comfychair.NotRunError, "no auth_smbd in HEAD"
+
+class WinbindAuthPlaintext(comfychair.TestCase):
+ def runtest(self):
+ raise comfychair.NotRunError, "not implemented"
+
+tests = [WinbindAuthCrap, WinbindAuthSmbd, WinbindAuthPlaintext]
+
+if __name__ == "__main__":
+ comfychair.main(tests)
diff --git a/source/stf/notes.txt b/source/stf/notes.txt
new file mode 100644
index 00000000000..68aca63c237
--- /dev/null
+++ b/source/stf/notes.txt
@@ -0,0 +1,175 @@
+ -*- indented-text -*-
+
+(set lotus no)
+
+
+
+Notes on using comfychair with Samba (samba testing framework units):
+
+The tests need to rely on some external resources, such as
+
+If suitable resources are not available, need to skip particular
+tests. Must include a message indicating what resources would be
+needed to run that test. (e.g. must be root.)
+
+We want to be able to select and run particular subsets of tests, such
+as "all winbind tests".
+
+We want to keep the number of configurable parameters down as much as
+possible, to make it easy on people running the tests.
+
+Wherever possible, the tests should set up their preconditions, but a
+few basic resources need to be provided by the people running the
+tests. So for example, rather than asking the user for the name of a
+non-root user, we should give the tests the administrator name and
+password, and it can create a new user to use.
+
+This makes it simpler to get the tests running, and possible also
+makes them more reproducible.
+
+In the future, rather than using NT machines provided by the test
+person, we might have a way to drive VMWare non-persistent sessions,
+to make tests even more tightly controlled.
+
+
+Another design question is how to communicate this information to the
+tests. If there's a lot of settings, then it might need to be stored
+in a configuration file.
+
+However, if we succeed in cutting down the number of parameters, then
+it might be straightforward to pass the information on the command
+line or in an environment variable.
+
+Environment variables are probably better because they can't be seen
+by other users, and they are more easily passed down through an
+invocation of "make check".
+
+
+
+Notes on Samba Testing Framework for Unittests
+----------------------------------------------
+
+This is to be read after reading the notes.txt from comfychair. I'm
+proposing a slightly more concrete description of what's described
+there.
+
+The model of having tests require named resources looks useful for
+incorporation into a framework that can be run by many people in
+widely different environments.
+
+Some possible environments for running the test framework in are:
+
+ - Casual downloader of Samba compiling from source and just wants
+ to run 'make check'. May only have one Unix machine and a
+ handful of clients.
+
+ - Samba team member with access to a small number of other
+ machines or VMware sessions.
+
+ - PSA developer who may not have intimate knowledge of Samba
+ internals and is only interested in testing against the PSA.
+
+ - Non-team hacker wanting to run test suite after making small
+ hacks.
+
+ - Build farm environment (loaner machine with no physical access
+ or root privilege).
+
+ - HP BAT.
+
+Developers in most of these environments are also potential test case
+authors. It should be easy for people unfamiliar with the framework
+to write new tests and have them work. We should provide examples and
+the existing tests should well written and understandable.
+
+Different types of tests:
+
+ - Tests that check Samba internals and link against
+ libbigballofmud.so. For example:
+
+ - Upper/lowercase string functions
+ - user_in_list() for large lists
+
+ - Tests that use the Samba Python extensions.
+
+ - Tests that execute Samba command line programs, for example
+ smbpasswd.
+
+ - Tests that require other resources on the network such as domain
+ controllers or PSAs.
+
+ - Tests that are performed on the documentation or the source code
+ such as:
+
+ - grep for common spelling mistakes made by abartlet (-:
+ - grep for company copyright (IBM, HP)
+
+ - Link to other existing testing frameworks (smbtorture,
+ abartlet's bash based build farm tests)
+
+I propose a TestResourceManager which would be instantiated by a test
+case. The test case would require("resourcename") as part of its
+constructor and raise a comfychair.NotRun exception if the resource
+was not present. A TestResource class could be defined which could
+read a configuration file or examine a environment variable and
+register a resource only if some condition was satisfied.
+
+It would be nice to be able to completely separate the PSA testing
+from the test framework. This would entail being able to define test
+resources dynamically, possibly with a plugin type system.
+
+class TestResourceManager:
+ def __init__(self, name):
+ self.resources = {}
+
+ def register(self, resource):
+ name = resource.name()
+ if self.resources.has_key(name):
+ raise "Test manager already has resource %s" % name
+ self.resources[name] = resource
+
+ def require(self, resource_name):
+ if not self.resources.has_key(resource_name):
+ raise "Test manager does not have resources %s" % resource_name
+
+class TestResource:
+ def __init__(self, name):
+ self.name = name
+
+ def name(self):
+ return self.name
+
+import os
+
+trm = TestResourceManager()
+
+if os.getuid() == 0:
+ trm.register(TestResource("root"))
+
+A config-o-matic Python module can take a list of machines and
+administrator%password entries and classify them by operating system
+version and service pack. These resources would be registered with
+the TestResourceManager.
+
+Some random thoughts about named resources for network servers:
+
+require("nt4.sp3")
+require("nt4.domaincontroller")
+require("psa")
+
+Some kind of format for location of passwords, libraries:
+
+require("exec(smbpasswd)")
+require("lib(bigballofmud)")
+
+maybe require("exec.smbpasswd") looks nicer...
+
+The require() function could return a dictionary of configuration
+information or some handle to fetch dynamic information on. We may
+need to create and destroy extra users or print queues. How to manage
+cleanup of dynamic resources?
+
+Requirements for running stf:
+
+ - Python, obviously
+ - Samba python extensions
diff --git a/source/stf/osver.py b/source/stf/osver.py
new file mode 100755
index 00000000000..68601fa7bb4
--- /dev/null
+++ b/source/stf/osver.py
@@ -0,0 +1,55 @@
+#!/usr/bin/python
+#
+# Utilities for determining the Windows operating system version remotely.
+#
+
+from samba import srvsvc
+
+# Constants
+
+PLATFORM_UNKNOWN = 0
+PLATFORM_WIN9X = 1
+PLATFORM_NT4 = 2
+PLATFORM_NT5 = 3 # Windows 2000
+
+def platform_name(platform_type):
+
+ platform_names = { PLATFORM_UNKNOWN: "Unknown",
+ PLATFORM_WIN9X: "Windows 9x",
+ PLATFORM_NT4: "Windows NT",
+ PLATFORM_NT5: "Windows 2000" }
+
+ if platform_names.has_key(platform_type):
+ return platform_names[platform_type]
+
+ return "Unknown"
+
+def platform_type(info101):
+ """Determine the operating system type from a SRV_INFO_101."""
+
+ if info101['major_version'] == 4 and info101['minor_version'] == 0:
+ return PLATFORM_NT4
+
+ if info101['major_version'] == 5 and info101['minor_version'] == 0:
+ return PLATFORM_NT5
+
+ return PLATFORM_UNKNOWN
+
+def is_domain_controller(info101):
+ """Return true if the server_type field from a SRV_INFO_101
+ indicates a domain controller."""
+ return info101['server_type'] & srvsvc.SV_TYPE_DOMAIN_CTRL
+
+def os_version(name):
+ info = srvsvc.netservergetinfo("\\\\%s" % name, 101)
+ return platform_type(info)
+
+if __name__ == "__main__":
+ import sys
+ if len(sys.argv) != 2:
+ print "Usage: osver.py server"
+ sys.exit(0)
+ info = srvsvc.netservergetinfo("\\\\%s" % sys.argv[1], 101)
+ print "platform type = %d" % platform_type(info)
+ if is_domain_controller(info):
+ print "%s is a domain controller" % sys.argv[1]
diff --git a/source/stf/pythoncheck.py b/source/stf/pythoncheck.py
new file mode 100755
index 00000000000..398bb2c3d69
--- /dev/null
+++ b/source/stf/pythoncheck.py
@@ -0,0 +1,48 @@
+#! /usr/bin/python
+
+# Comfychair test cases for Samba python extensions
+
+# Copyright (C) 2003 by Tim Potter <tpot@samba.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+"""These tests are run by Samba's "make check"."""
+
+import sys, comfychair
+
+class ImportTest(comfychair.TestCase):
+ """Check that all modules can be imported without error."""
+ def runtest(self):
+ python_modules = ['spoolss', 'lsa', 'samr', 'winbind', 'winreg',
+ 'srvsvc', 'tdb', 'smb', 'tdbpack']
+ for m in python_modules:
+ try:
+ __import__('samba.%s' % m)
+ except ImportError, msg:
+ self.log(str(msg))
+ self.fail('error importing %s module' % m)
+
+tests = [ImportTest]
+
+if __name__ == '__main__':
+ # Some magic to repend build directory to python path so we see the
+ # objects we have built and not previously installed stuff.
+ from distutils.util import get_platform
+ from os import getcwd
+ sys.path.insert(0, '%s/build/lib.%s-%s' %
+ (getcwd(), get_platform(), sys.version[0:3]))
+
+ comfychair.main(tests)
diff --git a/source/stf/sambalib.py b/source/stf/sambalib.py
new file mode 100644
index 00000000000..13d38e2a281
--- /dev/null
+++ b/source/stf/sambalib.py
@@ -0,0 +1,41 @@
+#! /usr/bin/python
+
+# Comfychair test cases for Samba string functions.
+
+# Copyright (C) 2003 by Martin Pool <mbp@samba.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+"""Tests for Samba library functions."""
+
+import sys, re, comfychair
+from unicodenames import *
+
+class snprintf_Test(comfychair.TestCase):
+ def runtest(self):
+ # Everything is built in to the test
+ out, err = self.runcmd('t_snprintf')
+
+# Define the tests exported by this module
+tests = [snprintf_Test]
+
+# Handle execution of this file as a main program
+if __name__ == '__main__':
+ comfychair.main(tests)
+
+# Local variables:
+# coding: utf-8
+# End:
diff --git a/source/stf/smbcontrol.py b/source/stf/smbcontrol.py
new file mode 100755
index 00000000000..30c331819c7
--- /dev/null
+++ b/source/stf/smbcontrol.py
@@ -0,0 +1,238 @@
+#!/usr/bin/python
+#
+# Test for smbcontrol command line argument handling.
+#
+
+import comfychair
+
+class NoArgs(comfychair.TestCase):
+ """Test no arguments produces usage message."""
+ def runtest(self):
+ out = self.runcmd("smbcontrol", expectedResult = 1)
+ self.assert_re_match("Usage: smbcontrol", out[1])
+
+class OneArg(comfychair.TestCase):
+ """Test single argument produces usage message."""
+ def runtest(self):
+ out = self.runcmd("smbcontrol foo", expectedResult = 1)
+ self.assert_re_match("Usage: smbcontrol", out[1])
+
+class SmbdDest(comfychair.TestCase):
+ """Test the broadcast destination 'smbd'."""
+ def runtest(self):
+ out = self.runcmd("smbcontrol smbd noop")
+
+class NmbdDest(comfychair.TestCase):
+ """Test the destination 'nmbd'."""
+ def runtest(self):
+ # We need a way to start/stop/whatever nmbd
+ raise comfychair.NotRunError, "not implemented"
+
+class PidDest(comfychair.TestCase):
+ """Test a pid number destination'."""
+ def runtest(self):
+ out = self.runcmd("smbcontrol 1234 noop")
+
+class SelfDest(comfychair.TestCase):
+ """Test the destination 'self'."""
+ def runtest(self):
+ out = self.runcmd("smbcontrol self noop")
+
+class WinbinddDest(comfychair.TestCase):
+ """Test the destination 'winbindd'."""
+ def runtest(self):
+ # We need a way to start/stop/whatever winbindd
+ raise comfychair.NotRunError, "not implemented"
+
+class BadDest(comfychair.TestCase):
+ """Test a bad destination."""
+ def runtest(self):
+ out = self.runcmd("smbcontrol foo noop", expectedResult = 1)
+
+class BadCmd(comfychair.TestCase):
+ """Test a bad command."""
+ def runtest(self):
+ out = self.runcmd("smbcontrol self spottyfoot", expectedResult = 1)
+ self.assert_re_match("smbcontrol: unknown command", out[1]);
+
+class NoArgCmdTest(comfychair.TestCase):
+ """A test class that tests a command with no argument."""
+ def runtest(self):
+ self.require_root()
+ out = self.runcmd("smbcontrol self %s" % self.cmd)
+ out = self.runcmd("smbcontrol self %s spottyfoot" % self.cmd,
+ expectedResult = 1)
+
+class ForceElection(NoArgCmdTest):
+ """Test a force-election message."""
+ def setup(self):
+ self.cmd = "force-election"
+
+class SamSync(NoArgCmdTest):
+ """Test a samsync message."""
+ def setup(self):
+ self.cmd = "samsync"
+
+class SamRepl(NoArgCmdTest):
+ """Test a samrepl message."""
+ def setup(self):
+ self.cmd = "samrepl"
+
+class DmallocChanged(NoArgCmdTest):
+ """Test a dmalloc-changed message."""
+ def setup(self):
+ self.cmd = "dmalloc-log-changed"
+
+class DmallocMark(NoArgCmdTest):
+ """Test a dmalloc-mark message."""
+ def setup(self):
+ self.cmd = "dmalloc-mark"
+
+class Shutdown(NoArgCmdTest):
+ """Test a shutdown message."""
+ def setup(self):
+ self.cmd = "shutdown"
+
+class Ping(NoArgCmdTest):
+ """Test a ping message."""
+ def setup(self):
+ self.cmd = "ping"
+
+class Debuglevel(NoArgCmdTest):
+ """Test a debuglevel message."""
+ def setup(self):
+ self.cmd = "debuglevel"
+
+class OneArgCmdTest(comfychair.TestCase):
+ """A test class that tests a command with one argument."""
+ def runtest(self):
+ self.require_root()
+ out = self.runcmd("smbcontrol self %s spottyfoot" % self.cmd)
+ out = self.runcmd("smbcontrol self %s" % self.cmd, expectedResult = 1)
+
+class DrvUpgrade(OneArgCmdTest):
+ """Test driver upgrade message."""
+ def setup(self):
+ self.cmd = "drvupgrade"
+
+class CloseShare(OneArgCmdTest):
+ """Test close share message."""
+ def setup(self):
+ self.cmd = "close-share"
+
+class Debug(OneArgCmdTest):
+ """Test a debug message."""
+ def setup(self):
+ self.cmd = "debug"
+
+class PrintNotify(comfychair.TestCase):
+ """Test print notification commands."""
+ def runtest(self):
+
+ # No subcommand
+
+ out = self.runcmd("smbcontrol self printnotify", expectedResult = 1)
+ self.assert_re_match("Must specify subcommand", out[1]);
+
+ # Invalid subcommand name
+
+ out = self.runcmd("smbcontrol self printnotify spottyfoot",
+ expectedResult = 1)
+ self.assert_re_match("Invalid subcommand", out[1]);
+
+ # Queue commands
+
+ for cmd in ["queuepause", "queueresume"]:
+
+ out = self.runcmd("smbcontrol self printnotify %s" % cmd,
+ expectedResult = 1)
+ self.assert_re_match("Usage:", out[1])
+
+ out = self.runcmd("smbcontrol self printnotify %s spottyfoot"
+ % cmd)
+
+ # Job commands
+
+ for cmd in ["jobpause", "jobresume", "jobdelete"]:
+
+ out = self.runcmd("smbcontrol self printnotify %s" % cmd,
+ expectedResult = 1)
+ self.assert_re_match("Usage:", out[1])
+
+ out = self.runcmd("smbcontrol self printnotify %s spottyfoot"
+ % cmd, expectedResult = 1)
+ self.assert_re_match("Usage:", out[1])
+
+ out = self.runcmd("smbcontrol self printnotify %s spottyfoot 123"
+ % cmd)
+
+ # Printer properties
+
+ out = self.runcmd("smbcontrol self printnotify printer",
+ expectedResult = 1)
+ self.assert_re_match("Usage", out[1])
+
+ out = self.runcmd("smbcontrol self printnotify printer spottyfoot",
+ expectedResult = 1)
+ self.assert_re_match("Usage", out[1])
+
+ for cmd in ["comment", "port", "driver"]:
+
+ out = self.runcmd("smbcontrol self printnotify printer spottyfoot "
+ "%s" % cmd, expectedResult = 1)
+ self.assert_re_match("Usage", out[1])
+
+ out = self.runcmd("smbcontrol self printnotify printer spottyfoot "
+ "%s value" % cmd)
+
+class Profile(comfychair.TestCase):
+ """Test setting the profiling level."""
+ def runtest(self):
+ self.require_root()
+ out = self.runcmd("smbcontrol self profile", expectedResult = 1)
+ self.assert_re_match("Usage", out[1])
+
+ out = self.runcmd("smbcontrol self profile spottyfoot",
+ expectedResult = 1)
+ self.assert_re_match("Unknown", out[1])
+
+ for cmd in ["off", "count", "on", "flush"]:
+ out = self.runcmd("smbcontrol self profile %s" % cmd)
+
+class ProfileLevel(comfychair.TestCase):
+ """Test requesting the current profiling level."""
+ def runtest(self):
+ self.require_root()
+ out = self.runcmd("smbcontrol self profilelevel spottyfoot",
+ expectedResult = 1)
+ self.assert_re_match("Usage", out[1])
+
+ out = self.runcmd("smbcontrol self profilelevel")
+
+class TimeoutArg(comfychair.TestCase):
+ """Test the --timeout argument."""
+ def runtest(self):
+ out = self.runcmd("smbcontrol --timeout 5 self noop")
+ out = self.runcmd("smbcontrol --timeout spottyfoot self noop",
+ expectedResult = 1)
+
+class ConfigFileArg(comfychair.TestCase):
+ """Test the --configfile argument."""
+ def runtest(self):
+ out = self.runcmd("smbcontrol --configfile /dev/null self noop")
+
+class BogusArg(comfychair.TestCase):
+ """Test a bogus command line argument."""
+ def runtest(self):
+ out = self.runcmd("smbcontrol --bogus self noop", expectedResult = 1)
+
+tests = [NoArgs, OneArg, SmbdDest, NmbdDest, WinbinddDest, PidDest,
+ SelfDest, BadDest, BadCmd, Debug, ForceElection, SamSync,
+ SamRepl, DmallocMark, DmallocChanged, Shutdown, DrvUpgrade,
+ CloseShare, Ping, Debuglevel, PrintNotify, Profile, ProfileLevel,
+ TimeoutArg, ConfigFileArg, BogusArg]
+
+# Handle execution of this file as a main program
+
+if __name__ == '__main__':
+ comfychair.main(tests)
diff --git a/source/stf/spoolss.py b/source/stf/spoolss.py
new file mode 100755
index 00000000000..735291508bc
--- /dev/null
+++ b/source/stf/spoolss.py
@@ -0,0 +1,288 @@
+#!/usr/bin/python
+
+import re
+import comfychair, stf
+from samba import spoolss
+
+class PrintServerTest(comfychair.TestCase):
+ """An abstract class requiring a print server."""
+ def setUp(self):
+ # TODO: create a test printer
+ self.server = stf.get_server(platform = "nt")
+ self.require(self.server != None, "print server required")
+ # TODO: remove hardcoded printer name
+ self.printername = "p"
+ self.uncname = "\\\\%s\\%s" % \
+ (self.server["hostname"], self.printername)
+
+class W2kPrintServerTest(comfychair.TestCase):
+ """An abstract class requiring a print server."""
+ def setUp(self):
+ # TODO: create a test printer
+ self.server = stf.get_server(platform = "nt5")
+ self.require(self.server != None, "print server required")
+ # TODO: remove hardcoded printer name
+ self.printername = "p"
+ self.uncname = "\\\\%s\\%s" % \
+ (self.server["hostname"], self.printername)
+
+class CredentialTest(PrintServerTest):
+ """An class that calls a function with various sets of credentials."""
+ def runTest(self):
+
+ bad_user_creds = {"username": "spotty",
+ "domain": "dog",
+ "password": "bone"}
+
+ cases = ((self.server["administrator"], "Admin credentials", 1),
+ (bad_user_creds, "Bad credentials", 0))
+
+ # TODO: add unpriv user case
+
+ for creds, testname, result in cases:
+ try:
+ self.runTestArg(creds)
+ except:
+ if result:
+ import traceback
+ traceback.print_exc()
+ self.fail("rpc with creds %s failed when it "
+ "should have suceeded" % creds)
+ return
+
+ if not result:
+ self.fail("rpc with creds %s suceeded when it should "
+ "have failed" % creds)
+
+class ArgTestServer(PrintServerTest):
+ """Test a RPC that takes a UNC print server name."""
+ def runTest(self):
+
+ # List of test cases, %s substituted for server name
+
+ cases = (("", "No server name", 0),
+ ("\\\\%s", "Valid server name", 1),
+ ("\\%s", "Invalid unc server name", 0),
+ ("\\\\%s__", "Invalid unc server name", 0))
+
+ for unc, testname, result in cases:
+ unc = re.sub("%s", self.server["hostname"], unc)
+ try:
+ self.runTestArg(unc)
+ except:
+ if result:
+ self.fail("rpc(\"%s\") failed when it should have "
+ "suceeded" % unc)
+ return
+
+ if not result:
+ # Suceeded when we should have failed
+ self.fail("rpc(\"%s\") suceeded when it should have "
+ "failed" % unc)
+
+class ArgTestServerAndPrinter(ArgTestServer):
+ """Test a RPC that takes a UNC print server or UNC printer name."""
+ def runTest(self):
+
+ ArgTestServer.runTest(self)
+
+ # List of test cases, %s substituted for server name, %p substituted
+ # for printer name.
+
+ cases = (("\\\\%s\\%p", "Valid server and printer name", 1),
+ ("\\\\%s\\%p__", "Valid server, invalid printer name", 0),
+ ("\\\\%s__\\%p", "Invalid server, valid printer name", 0))
+
+ for unc, testname, result in cases:
+ unc = re.sub("%s", self.server["hostname"], unc)
+ unc = re.sub("%p", self.printername, unc)
+ try:
+ self.runTestArg(unc)
+ except:
+ if result:
+ self.fail("openprinter(\"%s\") failed when it should have "
+ "suceeded" % unc)
+ return
+
+ if not result:
+ # Suceeded when we should have failed
+ self.fail("openprinter(\"%s\") suceeded when it should have "
+ "failed" % unc)
+
+class OpenPrinterArg(ArgTestServerAndPrinter):
+ """Test the OpenPrinter RPC with combinations of valid and invalid
+ server and printer names."""
+ def runTestArg(self, unc):
+ spoolss.openprinter(unc)
+
+class OpenPrinterCred(CredentialTest):
+ """Test opening printer with good and bad credentials."""
+ def runTestArg(self, creds):
+ spoolss.openprinter(self.uncname, creds = creds)
+
+class ClosePrinter(PrintServerTest):
+ """Test the ClosePrinter RPC on a printer handle."""
+ def runTest(self):
+ hnd = spoolss.openprinter(self.uncname)
+ spoolss.closeprinter(hnd)
+
+class ClosePrinterServer(PrintServerTest):
+ """Test the ClosePrinter RPC on a print server handle."""
+ def runTest(self):
+ hnd = spoolss.openprinter("\\\\%s" % self.server["hostname"])
+ spoolss.closeprinter(hnd)
+
+class GetPrinterInfo(PrintServerTest):
+ """Retrieve printer info at various levels."""
+
+ # Sample printer data
+
+ sample_info = {
+ 0: {'printer_errors': 0, 'unknown18': 0, 'unknown13': 0, 'unknown26': 0, 'cjobs': 0, 'unknown11': 0, 'server_name': '\\\\win2kdc1', 'total_pages': 0, 'unknown15': 586, 'unknown16': 0, 'month': 2, 'unknown20': 0, 'second': 23, 'unknown22': 983040, 'unknown25': 0, 'total_bytes': 0, 'unknown27': 0, 'year': 2003, 'build_version': 2195, 'unknown28': 0, 'global_counter': 4, 'day': 13, 'minute': 53, 'total_jobs': 0, 'unknown29': 1114112, 'name': '\\\\win2kdc1\\p', 'hour': 2, 'level': 0, 'c_setprinter': 0, 'change_id': 522454169, 'major_version': 5, 'unknown23': 15, 'day_of_week': 4, 'unknown14': 1, 'session_counter': 2, 'status': 1, 'unknown7': 1, 'unknown8': 0, 'unknown9': 0, 'milliseconds': 421, 'unknown24': 0},
+ 1: {'comment': "I'm a teapot!", 'level': 1, 'flags': 8388608, 'name': '\\\\win2kdc1\\p', 'description': '\\\\win2kdc1\\p,HP LaserJet 4,Canberra office'},
+ 2: {'comment': "I'm a teapot!", 'status': 1, 'print_processor': 'WinPrint', 'until_time': 0, 'share_name': 'p', 'start_time': 0, 'device_mode': {'icm_method': 1, 'bits_per_pel': 0, 'log_pixels': 0, 'orientation': 1, 'panning_width': 0, 'color': 2, 'pels_width': 0, 'print_quality': 600, 'driver_version': 24, 'display_flags': 0, 'y_resolution': 600, 'media_type': 0, 'display_frequency': 0, 'icm_intent': 0, 'pels_height': 0, 'reserved1': 0, 'size': 220, 'scale': 100, 'dither_type': 0, 'panning_height': 0, 'default_source': 7, 'duplex': 1, 'fields': 16131, 'spec_version': 1025, 'copies': 1, 'device_name': '\\\\win2kdc1\\p', 'paper_size': 1, 'paper_length': 0, 'private': 'private', 'collate': 0, 'paper_width': 0, 'form_name': 'Letter', 'reserved2': 0, 'tt_option': 0}, 'port_name': 'LPT1:', 'sepfile': '', 'parameters': '', 'security_descriptor': {'group_sid': 'S-1-5-21-1606980848-1677128483-854245398-513', 'sacl': None, 'dacl': {'ace_list': [{'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-544'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-544'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1121'}, {'flags': 10, 'type': 0, 'mask': 131072, 'trustee': 'S-1-3-0'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-3-0'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1124'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-1-0'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-550'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-550'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-549'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-549'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1106'}], 'revision': 2}, 'owner_sid': 'S-1-5-32-544', 'revision': 1}, 'name': '\\\\win2kdc1\\p', 'server_name': '\\\\win2kdc1', 'level': 2, 'datatype': 'RAW', 'cjobs': 0, 'average_ppm': 0, 'priority': 1, 'driver_name': 'HP LaserJet 4', 'location': 'Canberra office', 'attributes': 8776, 'default_priority': 0},
+ 3: {'flags': 4, 'security_descriptor': {'group_sid': 'S-1-5-21-1606980848-1677128483-854245398-513', 'sacl': None, 'dacl': {'ace_list': [{'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-544'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-544'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1121'}, {'flags': 10, 'type': 0, 'mask': 131072, 'trustee': 'S-1-3-0'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-3-0'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1124'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-1-0'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-550'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-550'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-549'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-549'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1106'}], 'revision': 2}, 'owner_sid': 'S-1-5-32-544', 'revision': 1}, 'level': 3}
+ }
+
+ def runTest(self):
+ self.hnd = spoolss.openprinter(self.uncname)
+
+ # Everyone should have getprinter levels 0-3
+
+ for i in (0, 1, 2, 3):
+ info = self.hnd.getprinter(level = i)
+ try:
+ stf.dict_check(self.sample_info[i], info)
+ except ValueError, msg:
+ raise "info%d: %s" % (i, msg)
+
+class EnumPrinters(PrintServerTest):
+ """Enumerate print info at various levels."""
+
+ sample_info = {
+
+ 0: {'q': {'printer_errors': 0, 'unknown18': 0, 'unknown13': 0, 'unknown26': 0, 'cjobs': 0, 'unknown11': 0, 'server_name': '', 'total_pages': 0, 'unknown15': 586, 'unknown16': 0, 'month': 2, 'unknown20': 0, 'second': 23, 'unknown22': 983040, 'unknown25': 0, 'total_bytes': 0, 'unknown27': 0, 'year': 2003, 'build_version': 2195, 'unknown28': 0, 'global_counter': 4, 'day': 13, 'minute': 53, 'total_jobs': 0, 'unknown29': -1833435136, 'name': 'q', 'hour': 2, 'level': 0, 'c_setprinter': 0, 'change_id': 522454169, 'major_version': 5, 'unknown23': 15, 'day_of_week': 4, 'unknown14': 1, 'session_counter': 1, 'status': 0, 'unknown7': 1, 'unknown8': 0, 'unknown9': 0, 'milliseconds': 421, 'unknown24': 0}, 'p': {'printer_errors': 0, 'unknown18': 0, 'unknown13': 0, 'unknown26': 0, 'cjobs': 0, 'unknown11': 0, 'server_name': '', 'total_pages': 0, 'unknown15': 586, 'unknown16': 0, 'month': 2, 'unknown20': 0, 'second': 23, 'unknown22': 983040, 'unknown25': 0, 'total_bytes': 0, 'unknown27': 0, 'year': 2003, 'build_version': 2195, 'unknown28': 0, 'global_counter': 4, 'day': 13, 'minute': 53, 'total_jobs': 0, 'unknown29': -1831337984, 'name': 'p', 'hour': 2, 'level': 0, 'c_setprinter': 0, 'change_id': 522454169, 'major_version': 5, 'unknown23': 15, 'day_of_week': 4, 'unknown14': 1, 'session_counter': 1, 'status': 1, 'unknown7': 1, 'unknown8': 0, 'unknown9': 0, 'milliseconds': 421, 'unknown24': 0}, 'magpie': {'printer_errors': 0, 'unknown18': 0, 'unknown13': 0, 'unknown26': 0, 'cjobs': 0, 'unknown11': 0, 'server_name': '', 'total_pages': 0, 'unknown15': 586, 'unknown16': 0, 'month': 2, 'unknown20': 0, 'second': 23, 'unknown22': 983040, 'unknown25': 0, 'total_bytes': 0, 'unknown27': 0, 'year': 2003, 'build_version': 2195, 'unknown28': 0, 'global_counter': 4, 'day': 13, 'minute': 53, 'total_jobs': 0, 'unknown29': 1114112, 'name': 'magpie', 'hour': 2, 'level': 0, 'c_setprinter': 0, 'change_id': 522454169, 'major_version': 5, 'unknown23': 15, 'day_of_week': 4, 'unknown14': 1, 'session_counter': 1, 'status': 0, 'unknown7': 1, 'unknown8': 0, 'unknown9': 0, 'milliseconds': 421, 'unknown24': 0}},
+
+ 1: {'q': {'comment': 'cheepy birds', 'level': 1, 'flags': 8388608, 'name': 'q', 'description': 'q,HP LaserJet 4,'}, 'p': {'comment': "I'm a teapot!", 'level': 1, 'flags': 8388608, 'name': 'p', 'description': 'p,HP LaserJet 4,Canberra office'}, 'magpie': {'comment': '', 'level': 1, 'flags': 8388608, 'name': 'magpie', 'description': 'magpie,Generic / Text Only,'}}
+ }
+
+ def runTest(self):
+ for i in (0, 1):
+ info = spoolss.enumprinters(
+ "\\\\%s" % self.server["hostname"], level = i)
+ try:
+ stf.dict_check(self.sample_info[i], info)
+ except ValueError, msg:
+ raise "info%d: %s" % (i, msg)
+
+class EnumPrintersArg(ArgTestServer):
+ def runTestArg(self, unc):
+ spoolss.enumprinters(unc)
+
+class EnumPrintersCred(CredentialTest):
+ """Test opening printer with good and bad credentials."""
+ def runTestArg(self, creds):
+ spoolss.enumprinters(
+ "\\\\%s" % self.server["hostname"], creds = creds)
+
+class EnumPrinterdrivers(PrintServerTest):
+
+ sample_info = {
+ 1: {'Okipage 10ex (PCL5E) : STANDARD': {'name': 'Okipage 10ex (PCL5E) : STANDARD', 'level': 1}, 'Generic / Text Only': {'name': 'Generic / Text Only', 'level': 1}, 'Brother HL-1030 series': {'name': 'Brother HL-1030 series', 'level': 1}, 'Brother HL-1240 series': {'name': 'Brother HL-1240 series', 'level': 1}, 'HP DeskJet 1220C Printer': {'name': 'HP DeskJet 1220C Printer', 'level': 1}, 'HP LaserJet 4100 PCL 6': {'name': 'HP LaserJet 4100 PCL 6', 'level': 1}, 'HP LaserJet 4': {'name': 'HP LaserJet 4', 'level': 1}},
+ 2: {'Okipage 10ex (PCL5E) : STANDARD': {'version': 2, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\2\\RASDDUI.DLL', 'name': 'Okipage 10ex (PCL5E) : STANDARD', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\2\\RASDD.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\2\\OKIPAGE.DLL', 'level': 2, 'architecture': 'Windows NT x86'}, 'Generic / Text Only': {'version': 3, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\UNIDRVUI.DLL', 'name': 'Generic / Text Only', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\3\\UNIDRV.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\TTY.GPD', 'level': 2, 'architecture': 'Windows NT x86'}, 'Brother HL-1030 series': {'version': 3, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BRUHL99A.DLL', 'name': 'Brother HL-1030 series', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BROHL99A.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BROHL103.PPD', 'level': 2, 'architecture': 'Windows NT x86'}, 'Brother HL-1240 series': {'version': 3, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BRUHL99A.DLL', 'name': 'Brother HL-1240 series', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BROHL99A.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BROHL124.PPD', 'level': 2, 'architecture': 'Windows NT x86'}, 'HP DeskJet 1220C Printer': {'version': 3, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPW8KMD.DLL', 'name': 'HP DeskJet 1220C Printer', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPW8KMD.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPW8KMD.DLL', 'level': 2, 'architecture': 'Windows NT x86'}, 'HP LaserJet 4100 PCL 6': {'version': 3, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPBF042E.DLL', 'name': 'HP LaserJet 4100 PCL 6', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPBF042G.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPBF042I.PMD', 'level': 2, 'architecture': 'Windows NT x86'}, 'HP LaserJet 4': {'version': 2, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\2\\hpblff0.dll', 'name': 'HP LaserJet 4', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\2\\hpblff2.dll', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\2\\hpblff39.pmd', 'level': 2, 'architecture': 'Windows NT x86'}}
+ }
+
+ def runTest(self):
+ for i in (1, 2):
+ info = spoolss.enumprinterdrivers(
+ "\\\\%s" % self.server["hostname"], level = i)
+ try:
+ if not self.sample_info.has_key(i):
+ self.log("%s" % info)
+ self.fail()
+ stf.dict_check(self.sample_info[i], info)
+ except ValueError, msg:
+ raise "info%d: %s" % (i, msg)
+
+class EnumPrinterdriversArg(ArgTestServer):
+ def runTestArg(self, unc):
+ spoolss.enumprinterdrivers(unc)
+
+class EnumPrinterdriversCred(CredentialTest):
+ """Test opening printer with good and bad credentials."""
+ def runTestArg(self, creds):
+ spoolss.enumprinterdrivers(
+ "\\\\%s" % self.server["hostname"], creds = creds)
+
+def usage():
+ print "Usage: spoolss.py [options] [test1[,test2...]]"
+ print "\t -v/--verbose Display debugging information"
+ print "\t -l/--list-tests List available tests"
+ print
+ print "A list of comma separated test names or regular expressions"
+ print "can be used to filter the tests performed."
+
+def test_match(subtest_list, test_name):
+ """Return true if a test matches a comma separated list of regular
+ expression of test names."""
+ # re.match does an implicit ^ at the start of the pattern.
+ # Explicitly anchor to end to avoid matching substrings.
+ for s in string.split(subtest_list, ","):
+ if re.match(s + "$", test_name):
+ return 1
+ return 0
+
+if __name__ == "__main__":
+ import os, sys, string
+ import getopt
+
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "vl", \
+ ["verbose", "list-tests"])
+ except getopt.GetoptError:
+ usage()
+ sys.exit(0)
+
+ verbose = 0
+ list_tests = 0
+
+ for opt, arg in opts:
+ if opt in ("-v", "--verbose"):
+ verbose = 1
+ if opt in ("-l", "--list-tests"):
+ list_tests = 1
+
+ if len(args) > 1:
+ usage()
+ sys.exit(0)
+
+ test_list = [
+ OpenPrinterArg,
+ OpenPrinterCred,
+ ClosePrinter,
+ ClosePrinterServer,
+ GetPrinterInfo,
+ EnumPrinters,
+ EnumPrintersCred,
+ EnumPrintersArg,
+ EnumPrinterdrivers,
+ EnumPrinterdriversCred,
+ EnumPrinterdriversArg,
+ ]
+
+ if len(args):
+ t = []
+ for test in test_list:
+ if test_match(args[0], test.__name__):
+ t.append(test)
+ test_list = t
+
+ if os.environ.has_key("SAMBA_DEBUG"):
+ spoolss.setup_logging(interactive = 1)
+ spoolss.set_debuglevel(10)
+
+ if list_tests:
+ for test in test_list:
+ print test.__name__
+ else:
+ comfychair.runtests(test_list, verbose = verbose)
diff --git a/source/stf/standardcheck.py b/source/stf/standardcheck.py
new file mode 100644
index 00000000000..e3292353e8b
--- /dev/null
+++ b/source/stf/standardcheck.py
@@ -0,0 +1,34 @@
+#! /usr/bin/python
+
+# Comfychair test cases for Samba
+
+# Copyright (C) 2003 by Martin Pool <mbp@samba.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+"""These tests are run by Samba's "make check"."""
+
+import strings, comfychair
+import smbcontrol, sambalib
+
+# There should not be any actual tests in here: this file just serves
+# to define the ones run by default. They're imported from other
+# modules.
+
+tests = strings.tests + smbcontrol.tests + sambalib.tests
+
+if __name__ == '__main__':
+ comfychair.main(tests)
diff --git a/source/stf/stf.py b/source/stf/stf.py
new file mode 100755
index 00000000000..ee0ff735612
--- /dev/null
+++ b/source/stf/stf.py
@@ -0,0 +1,101 @@
+#!/usr/bin/python
+#
+# Samba Testing Framework for Unit-testing
+#
+
+import os, string, re
+import osver
+
+def get_server_list_from_string(s):
+
+ server_list = []
+
+ # Format is a list of server:domain\username%password separated
+ # by commas.
+
+ for entry in string.split(s, ","):
+
+ # Parse entry
+
+ m = re.match("(.*):(.*)(\\\\|/)(.*)%(.*)", entry)
+ if not m:
+ raise "badly formed server list entry '%s'" % entry
+
+ server = m.group(1)
+ domain = m.group(2)
+ username = m.group(4)
+ password = m.group(5)
+
+ # Categorise servers
+
+ server_list.append({"platform": osver.os_version(server),
+ "hostname": server,
+ "administrator": {"username": username,
+ "domain": domain,
+ "password" : password}})
+
+ return server_list
+
+def get_server_list():
+ """Iterate through all sources of server info and append them all
+ in one big list."""
+
+ server_list = []
+
+ # The $STF_SERVERS environment variable
+
+ if os.environ.has_key("STF_SERVERS"):
+ server_list = server_list + \
+ get_server_list_from_string(os.environ["STF_SERVERS"])
+
+ return server_list
+
+def get_server(platform = None):
+ """Return configuration information for a server. The platform
+ argument can be a string either 'nt4' or 'nt5' for Windows NT or
+ Windows 2000 servers, or just 'nt' for Windows NT and higher."""
+
+ server_list = get_server_list()
+
+ for server in server_list:
+ if platform:
+ p = server["platform"]
+ if platform == "nt":
+ if (p == osver.PLATFORM_NT4 or p == osver.PLATFORM_NT5):
+ return server
+ if platform == "nt4" and p == osver.PLATFORM_NT4:
+ return server
+ if platform == "nt5" and p == osver.PLATFORM_NT5:
+ return server
+ else:
+ # No filter defined, return first in list
+ return server
+
+ return None
+
+def dict_check(sample_dict, real_dict):
+ """Check that real_dict contains all the keys present in sample_dict
+ and no extras. Also check that common keys are of them same type."""
+ tmp = real_dict.copy()
+ for key in sample_dict.keys():
+ # Check existing key and type
+ if not real_dict.has_key(key):
+ raise ValueError, "dict does not contain key '%s'" % key
+ if type(sample_dict[key]) != type(real_dict[key]):
+ raise ValueError, "dict has differing types (%s vs %s) for key " \
+ "'%s'" % (type(sample_dict[key]), type(real_dict[key]), key)
+ # Check dictionaries recursively
+ if type(sample_dict[key]) == dict:
+ dict_check(sample_dict[key], real_dict[key])
+ # Delete visited keys from copy
+ del(tmp[key])
+ # Any keys leftover are present in the real dict but not the sample
+ if len(tmp) == 0:
+ return
+ result = "dict has extra keys: "
+ for key in tmp.keys():
+ result = result + key + " "
+ raise ValueError, result
+
+if __name__ == "__main__":
+ print get_server(platform = "nt")
diff --git a/source/stf/strings.py b/source/stf/strings.py
new file mode 100755
index 00000000000..86f7acdeb47
--- /dev/null
+++ b/source/stf/strings.py
@@ -0,0 +1,151 @@
+#! /usr/bin/python
+
+# Comfychair test cases for Samba string functions.
+
+# Copyright (C) 2003 by Martin Pool <mbp@samba.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+# XXX: All this code assumes that the Unix character set is UTF-8,
+# which is the most common setting. I guess it would be better to
+# force it to that value while running the tests. I'm not sure of the
+# best way to do that yet.
+#
+# Note that this is NOT the case in C code until the loadparm table is
+# intialized -- the default seems to be ASCII, which rather lets Samba
+# off the hook. :-) The best way seems to be to put this in the test
+# harnesses:
+#
+# lp_load("/dev/null", True, False, False);
+#
+# -- mbp
+
+import sys, re, comfychair
+from unicodenames import *
+
+def signum(a):
+ if a < 0:
+ return -1
+ elif a > 0:
+ return +1
+ else:
+ return 0
+
+
+class PushUCS2_Tests(comfychair.TestCase):
+ """Conversion to/from UCS2"""
+ def runtest(self):
+ OE = LATIN_CAPITAL_LETTER_O_WITH_DIARESIS
+ oe = LATIN_CAPITAL_LETTER_O_WITH_DIARESIS
+ cases = ['hello',
+ 'hello world',
+ 'g' + OE + OE + 'gomobile',
+ 'g' + OE + oe + 'gomobile',
+ u'foo\u0100',
+ KATAKANA_LETTER_A * 20,
+ ]
+ for u8str in cases:
+ out, err = self.runcmd("t_push_ucs2 \"%s\"" % u8str.encode('utf-8'))
+ self.assert_equal(out, "0\n")
+
+
+class StrCaseCmp(comfychair.TestCase):
+ """String comparisons in simple ASCII"""
+ def run_strcmp(self, a, b, expect):
+ out, err = self.runcmd('t_strcmp \"%s\" \"%s\"' % (a.encode('utf-8'), b.encode('utf-8')))
+ if signum(int(out)) != expect:
+ self.fail("comparison failed:\n"
+ " a=%s\n"
+ " b=%s\n"
+ " expected=%s\n"
+ " result=%s\n" % (`a`, `b`, `expect`, `out`))
+
+ def runtest(self):
+ # A, B, strcasecmp(A, B)
+ cases = [('hello', 'hello', 0),
+ ('hello', 'goodbye', +1),
+ ('goodbye', 'hello', -1),
+ ('hell', 'hello', -1),
+ ('', '', 0),
+ ('a', '', +1),
+ ('', 'a', -1),
+ ('a', 'A', 0),
+ ('aa', 'aA', 0),
+ ('Aa', 'aa', 0),
+ ('longstring ' * 100, 'longstring ' * 100, 0),
+ ('longstring ' * 100, 'longstring ' * 100 + 'a', -1),
+ ('longstring ' * 100 + 'a', 'longstring ' * 100, +1),
+ (KATAKANA_LETTER_A, KATAKANA_LETTER_A, 0),
+ (KATAKANA_LETTER_A, 'a', 1),
+ ]
+ for a, b, expect in cases:
+ self.run_strcmp(a, b, expect)
+
+class strstr_m(comfychair.TestCase):
+ """String comparisons in simple ASCII"""
+ def run_strstr(self, a, b, expect):
+ out, err = self.runcmd('t_strstr \"%s\" \"%s\"' % (a.encode('utf-8'), b.encode('utf-8')))
+ if (out != (expect + '\n').encode('utf-8')):
+ self.fail("comparison failed:\n"
+ " a=%s\n"
+ " b=%s\n"
+ " expected=%s\n"
+ " result=%s\n" % (`a`, `b`, `expect+'\n'`, `out`))
+
+ def runtest(self):
+ # A, B, strstr_m(A, B)
+ cases = [('hello', 'hello', 'hello'),
+ ('hello', 'goodbye', '(null)'),
+ ('goodbye', 'hello', '(null)'),
+ ('hell', 'hello', '(null)'),
+ ('hello', 'hell', 'hello'),
+ ('', '', ''),
+ ('a', '', 'a'),
+ ('', 'a', '(null)'),
+ ('a', 'A', '(null)'),
+ ('aa', 'aA', '(null)'),
+ ('Aa', 'aa', '(null)'),
+ ('%v foo', '%v', '%v foo'),
+ ('foo %v foo', '%v', '%v foo'),
+ ('foo %v', '%v', '%v'),
+ ('longstring ' * 100, 'longstring ' * 99, 'longstring ' * 100),
+ ('longstring ' * 99, 'longstring ' * 100, '(null)'),
+ ('longstring a' * 99, 'longstring ' * 100 + 'a', '(null)'),
+ ('longstring ' * 100 + 'a', 'longstring ' * 100, 'longstring ' * 100 + 'a'),
+ (KATAKANA_LETTER_A, KATAKANA_LETTER_A + 'bcd', '(null)'),
+ (KATAKANA_LETTER_A + 'bcde', KATAKANA_LETTER_A + 'bcd', KATAKANA_LETTER_A + 'bcde'),
+ ('d'+KATAKANA_LETTER_A + 'bcd', KATAKANA_LETTER_A + 'bcd', KATAKANA_LETTER_A + 'bcd'),
+ ('d'+KATAKANA_LETTER_A + 'bd', KATAKANA_LETTER_A + 'bcd', '(null)'),
+
+ ('e'+KATAKANA_LETTER_A + 'bcdf', KATAKANA_LETTER_A + 'bcd', KATAKANA_LETTER_A + 'bcdf'),
+ (KATAKANA_LETTER_A, KATAKANA_LETTER_A + 'bcd', '(null)'),
+ (KATAKANA_LETTER_A*3, 'a', '(null)'),
+ ]
+ for a, b, expect in cases:
+ self.run_strstr(a, b, expect)
+
+# Define the tests exported by this module
+tests = [StrCaseCmp,
+ strstr_m,
+ PushUCS2_Tests]
+
+# Handle execution of this file as a main program
+if __name__ == '__main__':
+ comfychair.main(tests)
+
+# Local variables:
+# coding: utf-8
+# End:
diff --git a/source/stf/test.py b/source/stf/test.py
new file mode 100755
index 00000000000..fb57926cc3a
--- /dev/null
+++ b/source/stf/test.py
@@ -0,0 +1,33 @@
+#!/usr/bin/python
+
+# meta-test-case / example for comfychair. Should demonstrate
+# different kinds of failure.
+
+import comfychair
+
+class NormalTest(comfychair.TestCase):
+ def runtest(self):
+ pass
+
+class RootTest(comfychair.TestCase):
+ def setup(self):
+ self.require_root()
+
+ def runTest(self):
+ pass
+
+class GoodExecTest(comfychair.TestCase):
+ def runtest(self):
+ stdout = self.runcmd("ls -l")
+
+class BadExecTest(comfychair.TestCase):
+ def setup(self):
+ exit, stdout = self.runcmd_unchecked("spottyfoot --slobber",
+ skip_on_noexec = 1)
+
+
+tests = [NormalTest, RootTest, GoodExecTest, BadExecTest]
+
+if __name__ == '__main__':
+ comfychair.main(tests)
+
diff --git a/source/stf/unicodenames.py b/source/stf/unicodenames.py
new file mode 100644
index 00000000000..d4100cb7f90
--- /dev/null
+++ b/source/stf/unicodenames.py
@@ -0,0 +1,33 @@
+#! /usr/bin/python
+
+# Copyright (C) 2003 by Martin Pool <mbp@samba.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+
+"""
+Defines symbolic names for a few UNICODE characters, to make test
+source code more readable on machines that don't have all the
+necessary fonts.
+
+You can do "import *" on this file safely.
+"""
+
+LATIN_CAPITAL_LETTER_N_WITH_TILDE = u'\u004e'
+LATIN_CAPITAL_LETTER_O_WITH_DIARESIS = u'\u00d6'
+LATIN_SMALL_LETTER_O_WITH_DIARESIS = u'\u00f6'
+
+KATAKANA_LETTER_A = u'\u30a2'
diff --git a/source/tdb/.cvsignore b/source/tdb/.cvsignore
new file mode 100644
index 00000000000..66445fe2690
--- /dev/null
+++ b/source/tdb/.cvsignore
@@ -0,0 +1,11 @@
+*.po
+*.po32
+tdbbackup
+tdbdump
+tdbtest
+tdbtool
+tdbtorture
+test.db
+test.gdbm
+test.tdb
+torture.tdb
diff --git a/source/tdb/Makefile b/source/tdb/Makefile
new file mode 100644
index 00000000000..59fbb079bd0
--- /dev/null
+++ b/source/tdb/Makefile
@@ -0,0 +1,29 @@
+#
+# Makefile for tdb directory
+#
+
+CFLAGS = -DSTANDALONE -DTDB_DEBUG -g -DHAVE_MMAP=1
+CC = gcc
+
+PROGS = tdbtest tdbtool tdbtorture
+TDB_OBJ = tdb.o spinlock.o
+
+default: $(PROGS)
+
+tdbtest: tdbtest.o $(TDB_OBJ)
+ $(CC) $(CFLAGS) -o tdbtest tdbtest.o $(TDB_OBJ) -lgdbm
+
+tdbtool: tdbtool.o $(TDB_OBJ)
+ $(CC) $(CFLAGS) -o tdbtool tdbtool.o $(TDB_OBJ)
+
+tdbtorture: tdbtorture.o $(TDB_OBJ)
+ $(CC) $(CFLAGS) -o tdbtorture tdbtorture.o $(TDB_OBJ)
+
+tdbdump: tdbdump.o $(TDB_OBJ)
+ $(CC) $(CFLAGS) -o tdbdump tdbdump.o $(TDB_OBJ)
+
+tdbbackup: tdbbackup.o $(TDB_OBJ)
+ $(CC) $(CFLAGS) -o tdbbackup tdbbackup.o $(TDB_OBJ)
+
+clean:
+ rm -f $(PROGS) *.o *~ *% core test.db test.tdb test.gdbm
diff --git a/source/tdb/README b/source/tdb/README
new file mode 100644
index 00000000000..fac3eacb4db
--- /dev/null
+++ b/source/tdb/README
@@ -0,0 +1,167 @@
+tdb - a trivial database system
+tridge@linuxcare.com December 1999
+==================================
+
+This is a simple database API. It was inspired by the realisation that
+in Samba we have several ad-hoc bits of code that essentially
+implement small databases for sharing structures between parts of
+Samba. As I was about to add another I realised that a generic
+database module was called for to replace all the ad-hoc bits.
+
+I based the interface on gdbm. I couldn't use gdbm as we need to be
+able to have multiple writers to the databases at one time.
+
+Compilation
+-----------
+
+add HAVE_MMAP=1 to use mmap instead of read/write
+add TDB_DEBUG=1 for verbose debug info
+add NOLOCK=1 to disable locking code
+
+Testing
+-------
+
+Compile tdbtest.c and link with gdbm for testing. tdbtest will perform
+identical operations via tdb and gdbm then make sure the result is the
+same
+
+Also included is tdbtool, which allows simple database manipulation
+on the commandline.
+
+tdbtest and tdbtool are not built as part of Samba, but are included
+for completeness.
+
+Interface
+---------
+
+The interface is very similar to gdbm except for the following:
+
+- different open interface. The tdb_open call is more similar to a
+ traditional open()
+- no tdbm_reorganise() function
+- no tdbm_sync() function. No operations are cached in the library anyway
+- added a tdb_traverse() function for traversing the whole database
+
+A general rule for using tdb is that the caller frees any returned
+TDB_DATA structures. Just call free(p.dptr) to free a TDB_DATA
+return value called p. This is the same as gdbm.
+
+here is a full list of tdb functions with brief descriptions.
+
+
+----------------------------------------------------------------------
+TDB_CONTEXT *tdb_open(char *name, int hash_size, int tdb_flags,
+ int open_flags, mode_t mode)
+
+ open the database, creating it if necessary
+
+ The open_flags and mode are passed straight to the open call on the database
+ file. A flags value of O_WRONLY is invalid
+
+ The hash size is advisory, use zero for a default value.
+
+ return is NULL on error
+
+ possible tdb_flags are:
+ TDB_CLEAR_IF_FIRST - clear database if we are the only one with it open
+ TDB_INTERNAL - don't use a file, instaed store the data in
+ memory. The filename is ignored in this case.
+ TDB_NOLOCK - don't do any locking
+ TDB_NOMMAP - don't use mmap
+
+----------------------------------------------------------------------
+char *tdb_error(TDB_CONTEXT *tdb);
+
+ return a error string for the last tdb error
+
+----------------------------------------------------------------------
+int tdb_close(TDB_CONTEXT *tdb);
+
+ close a database
+
+----------------------------------------------------------------------
+int tdb_update(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf);
+
+ update an entry in place - this only works if the new data size
+ is <= the old data size and the key exists.
+ on failure return -1
+
+----------------------------------------------------------------------
+TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key);
+
+ fetch an entry in the database given a key
+ if the return value has a null dptr then a error occurred
+
+ caller must free the resulting data
+
+----------------------------------------------------------------------
+int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key);
+
+ check if an entry in the database exists
+
+ note that 1 is returned if the key is found and 0 is returned if not found
+ this doesn't match the conventions in the rest of this module, but is
+ compatible with gdbm
+
+----------------------------------------------------------------------
+int tdb_traverse(TDB_CONTEXT *tdb, int (*fn)(TDB_CONTEXT *tdb,
+ TDB_DATA key, TDB_DATA dbuf, void *state), void *state);
+
+ traverse the entire database - calling fn(tdb, key, data, state) on each
+ element.
+
+ return -1 on error or the record count traversed
+
+ if fn is NULL then it is not called
+
+ a non-zero return value from fn() indicates that the traversal should stop
+
+----------------------------------------------------------------------
+TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb);
+
+ find the first entry in the database and return its key
+
+ the caller must free the returned data
+
+----------------------------------------------------------------------
+TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key);
+
+ find the next entry in the database, returning its key
+
+ the caller must free the returned data
+
+----------------------------------------------------------------------
+int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key);
+
+ delete an entry in the database given a key
+
+----------------------------------------------------------------------
+int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag);
+
+ store an element in the database, replacing any existing element
+ with the same key
+
+ If flag==TDB_INSERT then don't overwrite an existing entry
+ If flag==TDB_MODIFY then don't create a new entry
+
+ return 0 on success, -1 on failure
+
+----------------------------------------------------------------------
+int tdb_writelock(TDB_CONTEXT *tdb);
+
+ lock the database. If we already have it locked then don't do anything
+
+----------------------------------------------------------------------
+int tdb_writeunlock(TDB_CONTEXT *tdb);
+ unlock the database
+
+----------------------------------------------------------------------
+int tdb_lockchain(TDB_CONTEXT *tdb, TDB_DATA key);
+
+ lock one hash chain. This is meant to be used to reduce locking
+ contention - it cannot guarantee how many records will be locked
+
+----------------------------------------------------------------------
+int tdb_unlockchain(TDB_CONTEXT *tdb, TDB_DATA key);
+
+ unlock one hash chain
diff --git a/source/tdb/spinlock.c b/source/tdb/spinlock.c
new file mode 100644
index 00000000000..24c4371decc
--- /dev/null
+++ b/source/tdb/spinlock.c
@@ -0,0 +1,481 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ trivial database library
+
+ Copyright (C) Anton Blanchard 2001
+
+ ** NOTE! The following LGPL license applies to the tdb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if STANDALONE
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <signal.h>
+#include "tdb.h"
+#include "spinlock.h"
+
+#define DEBUG
+#else
+#include "includes.h"
+#endif
+
+#ifdef USE_SPINLOCKS
+
+/*
+ * ARCH SPECIFIC
+ */
+
+#if defined(SPARC_SPINLOCKS)
+
+static inline int __spin_trylock(spinlock_t *lock)
+{
+ unsigned int result;
+
+ asm volatile("ldstub [%1], %0"
+ : "=r" (result)
+ : "r" (lock)
+ : "memory");
+
+ return (result == 0) ? 0 : EBUSY;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+ asm volatile("":::"memory");
+ *lock = 0;
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+ *lock = 0;
+}
+
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+ return (*lock != 0);
+}
+
+#elif defined(POWERPC_SPINLOCKS)
+
+static inline int __spin_trylock(spinlock_t *lock)
+{
+ unsigned int result;
+
+ __asm__ __volatile__(
+"1: lwarx %0,0,%1\n\
+ cmpwi 0,%0,0\n\
+ li %0,0\n\
+ bne- 2f\n\
+ li %0,1\n\
+ stwcx. %0,0,%1\n\
+ bne- 1b\n\
+ isync\n\
+2:" : "=&r"(result)
+ : "r"(lock)
+ : "cr0", "memory");
+
+ return (result == 1) ? 0 : EBUSY;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+ asm volatile("eieio":::"memory");
+ *lock = 0;
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+ *lock = 0;
+}
+
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+ return (*lock != 0);
+}
+
+#elif defined(INTEL_SPINLOCKS)
+
+static inline int __spin_trylock(spinlock_t *lock)
+{
+ int oldval;
+
+ asm volatile("xchgl %0,%1"
+ : "=r" (oldval), "=m" (*lock)
+ : "0" (0)
+ : "memory");
+
+ return oldval > 0 ? 0 : EBUSY;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+ asm volatile("":::"memory");
+ *lock = 1;
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+ *lock = 1;
+}
+
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+ return (*lock != 1);
+}
+
+#elif defined(MIPS_SPINLOCKS) && defined(sgi) && (_COMPILER_VERSION >= 730)
+
+/* Implement spinlocks on IRIX using the MIPSPro atomic fetch operations. See
+ * sync(3) for the details of the intrinsic operations.
+ *
+ * "sgi" and "_COMPILER_VERSION" are always defined by MIPSPro.
+ */
+
+#if defined(STANDALONE)
+
+/* MIPSPro 7.3 has "__inline" as an extension, but not "inline. */
+#define inline __inline
+
+#endif /* STANDALONE */
+
+/* Returns 0 if the lock is acquired, EBUSY otherwise. */
+static inline int __spin_trylock(spinlock_t *lock)
+{
+ unsigned int val;
+ val = __lock_test_and_set(lock, 1);
+ return val == 0 ? 0 : EBUSY;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+ __lock_release(lock);
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+ __lock_release(lock);
+}
+
+/* Returns 1 if the lock is held, 0 otherwise. */
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+ unsigned int val;
+ val = __add_and_fetch(lock, 0);
+ return val;
+}
+
+#elif defined(MIPS_SPINLOCKS)
+
+static inline unsigned int load_linked(unsigned long addr)
+{
+ unsigned int res;
+
+ __asm__ __volatile__("ll\t%0,(%1)"
+ : "=r" (res)
+ : "r" (addr));
+
+ return res;
+}
+
+static inline unsigned int store_conditional(unsigned long addr, unsigned int value)
+{
+ unsigned int res;
+
+ __asm__ __volatile__("sc\t%0,(%2)"
+ : "=r" (res)
+ : "0" (value), "r" (addr));
+ return res;
+}
+
+static inline int __spin_trylock(spinlock_t *lock)
+{
+ unsigned int mw;
+
+ do {
+ mw = load_linked(lock);
+ if (mw)
+ return EBUSY;
+ } while (!store_conditional(lock, 1));
+
+ asm volatile("":::"memory");
+
+ return 0;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+ asm volatile("":::"memory");
+ *lock = 0;
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+ *lock = 0;
+}
+
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+ return (*lock != 0);
+}
+
+#else
+#error Need to implement spinlock code in spinlock.c
+#endif
+
+/*
+ * OS SPECIFIC
+ */
+
+static void yield_cpu(void)
+{
+ struct timespec tm;
+
+#ifdef USE_SCHED_YIELD
+ sched_yield();
+#else
+ /* Linux will busy loop for delays < 2ms on real time tasks */
+ tm.tv_sec = 0;
+ tm.tv_nsec = 2000000L + 1;
+ nanosleep(&tm, NULL);
+#endif
+}
+
+static int this_is_smp(void)
+{
+#if defined(HAVE_SYSCONF) && defined(SYSCONF_SC_NPROC_ONLN)
+ return (sysconf(_SC_NPROC_ONLN) > 1) ? 1 : 0;
+#else
+ return 0;
+#endif
+}
+
+/*
+ * GENERIC
+ */
+
+static int smp_machine = 0;
+
+static inline void __spin_lock(spinlock_t *lock)
+{
+ int ntries = 0;
+
+ while(__spin_trylock(lock)) {
+ while(__spin_is_locked(lock)) {
+ if (smp_machine && ntries++ < MAX_BUSY_LOOPS)
+ continue;
+ yield_cpu();
+ }
+ }
+}
+
+static void __read_lock(tdb_rwlock_t *rwlock)
+{
+ int ntries = 0;
+
+ while(1) {
+ __spin_lock(&rwlock->lock);
+
+ if (!(rwlock->count & RWLOCK_BIAS)) {
+ rwlock->count++;
+ __spin_unlock(&rwlock->lock);
+ return;
+ }
+
+ __spin_unlock(&rwlock->lock);
+
+ while(rwlock->count & RWLOCK_BIAS) {
+ if (smp_machine && ntries++ < MAX_BUSY_LOOPS)
+ continue;
+ yield_cpu();
+ }
+ }
+}
+
+static void __write_lock(tdb_rwlock_t *rwlock)
+{
+ int ntries = 0;
+
+ while(1) {
+ __spin_lock(&rwlock->lock);
+
+ if (rwlock->count == 0) {
+ rwlock->count |= RWLOCK_BIAS;
+ __spin_unlock(&rwlock->lock);
+ return;
+ }
+
+ __spin_unlock(&rwlock->lock);
+
+ while(rwlock->count != 0) {
+ if (smp_machine && ntries++ < MAX_BUSY_LOOPS)
+ continue;
+ yield_cpu();
+ }
+ }
+}
+
+static void __write_unlock(tdb_rwlock_t *rwlock)
+{
+ __spin_lock(&rwlock->lock);
+
+#ifdef DEBUG
+ if (!(rwlock->count & RWLOCK_BIAS))
+ fprintf(stderr, "bug: write_unlock\n");
+#endif
+
+ rwlock->count &= ~RWLOCK_BIAS;
+ __spin_unlock(&rwlock->lock);
+}
+
+static void __read_unlock(tdb_rwlock_t *rwlock)
+{
+ __spin_lock(&rwlock->lock);
+
+#ifdef DEBUG
+ if (!rwlock->count)
+ fprintf(stderr, "bug: read_unlock\n");
+
+ if (rwlock->count & RWLOCK_BIAS)
+ fprintf(stderr, "bug: read_unlock\n");
+#endif
+
+ rwlock->count--;
+ __spin_unlock(&rwlock->lock);
+}
+
+/* TDB SPECIFIC */
+
+/* lock a list in the database. list -1 is the alloc list */
+int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type)
+{
+ tdb_rwlock_t *rwlocks;
+
+ if (!tdb->map_ptr) return -1;
+ rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks);
+
+ switch(rw_type) {
+ case F_RDLCK:
+ __read_lock(&rwlocks[list+1]);
+ break;
+
+ case F_WRLCK:
+ __write_lock(&rwlocks[list+1]);
+ break;
+
+ default:
+ return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+ }
+ return 0;
+}
+
+/* unlock the database. */
+int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type)
+{
+ tdb_rwlock_t *rwlocks;
+
+ if (!tdb->map_ptr) return -1;
+ rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks);
+
+ switch(rw_type) {
+ case F_RDLCK:
+ __read_unlock(&rwlocks[list+1]);
+ break;
+
+ case F_WRLCK:
+ __write_unlock(&rwlocks[list+1]);
+ break;
+
+ default:
+ return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+ }
+
+ return 0;
+}
+
+int tdb_create_rwlocks(int fd, unsigned int hash_size)
+{
+ unsigned size, i;
+ tdb_rwlock_t *rwlocks;
+
+ size = TDB_SPINLOCK_SIZE(hash_size);
+ rwlocks = malloc(size);
+ if (!rwlocks)
+ return -1;
+
+ for(i = 0; i < hash_size+1; i++) {
+ __spin_lock_init(&rwlocks[i].lock);
+ rwlocks[i].count = 0;
+ }
+
+ /* Write it out (appending to end) */
+ if (write(fd, rwlocks, size) != size) {
+ free(rwlocks);
+ return -1;
+ }
+ smp_machine = this_is_smp();
+ free(rwlocks);
+ return 0;
+}
+
+int tdb_clear_spinlocks(TDB_CONTEXT *tdb)
+{
+ tdb_rwlock_t *rwlocks;
+ unsigned i;
+
+ if (tdb->header.rwlocks == 0) return 0;
+ if (!tdb->map_ptr) return -1;
+
+ /* We're mmapped here */
+ rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks);
+ for(i = 0; i < tdb->header.hash_size+1; i++) {
+ __spin_lock_init(&rwlocks[i].lock);
+ rwlocks[i].count = 0;
+ }
+ return 0;
+}
+#else
+int tdb_create_rwlocks(int fd, unsigned int hash_size) { return 0; }
+int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type) { return -1; }
+int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type) { return -1; }
+
+/* Non-spinlock version: remove spinlock pointer */
+int tdb_clear_spinlocks(TDB_CONTEXT *tdb)
+{
+ tdb_off off = (tdb_off)((char *)&tdb->header.rwlocks
+ - (char *)&tdb->header);
+
+ tdb->header.rwlocks = 0;
+ if (lseek(tdb->fd, off, SEEK_SET) != off
+ || write(tdb->fd, (void *)&tdb->header.rwlocks,
+ sizeof(tdb->header.rwlocks))
+ != sizeof(tdb->header.rwlocks))
+ return -1;
+ return 0;
+}
+#endif
diff --git a/source/tdb/spinlock.h b/source/tdb/spinlock.h
new file mode 100644
index 00000000000..967fe37457f
--- /dev/null
+++ b/source/tdb/spinlock.h
@@ -0,0 +1,59 @@
+#ifndef __SPINLOCK_H__
+#define __SPINLOCK_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "tdb.h"
+
+#ifdef USE_SPINLOCKS
+
+#define RWLOCK_BIAS 0x1000UL
+
+/* OS SPECIFIC */
+#define MAX_BUSY_LOOPS 1000
+#undef USE_SCHED_YIELD
+
+/* ARCH SPECIFIC */
+/* We should make sure these are padded to a cache line */
+#if defined(SPARC_SPINLOCKS)
+typedef volatile char spinlock_t;
+#elif defined(POWERPC_SPINLOCKS)
+typedef volatile unsigned long spinlock_t;
+#elif defined(INTEL_SPINLOCKS)
+typedef volatile int spinlock_t;
+#elif defined(MIPS_SPINLOCKS)
+typedef volatile unsigned long spinlock_t;
+#else
+#error Need to implement spinlock code in spinlock.h
+#endif
+
+typedef struct {
+ spinlock_t lock;
+ volatile int count;
+} tdb_rwlock_t;
+
+int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type);
+int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type);
+int tdb_create_rwlocks(int fd, unsigned int hash_size);
+int tdb_clear_spinlocks(TDB_CONTEXT *tdb);
+
+#define TDB_SPINLOCK_SIZE(hash_size) (((hash_size) + 1) * sizeof(tdb_rwlock_t))
+
+#else /* !USE_SPINLOCKS */
+#if 0
+#define tdb_create_rwlocks(fd, hash_size) 0
+#define tdb_spinlock(tdb, list, rw_type) (-1)
+#define tdb_spinunlock(tdb, list, rw_type) (-1)
+#else
+int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type);
+int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type);
+int tdb_create_rwlocks(int fd, unsigned int hash_size);
+#endif
+int tdb_clear_spinlocks(TDB_CONTEXT *tdb);
+#define TDB_SPINLOCK_SIZE(hash_size) 0
+
+#endif
+
+#endif
diff --git a/source/tdb/tdb.c b/source/tdb/tdb.c
new file mode 100644
index 00000000000..cda9fc24750
--- /dev/null
+++ b/source/tdb/tdb.c
@@ -0,0 +1,2090 @@
+ /*
+ Unix SMB/CIFS implementation.
+
+ trivial database library
+
+ Copyright (C) Andrew Tridgell 1999-2004
+ Copyright (C) Paul `Rusty' Russell 2000
+ Copyright (C) Jeremy Allison 2000-2003
+
+ ** NOTE! The following LGPL license applies to the tdb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+
+/* NOTE: If you use tdbs under valgrind, and in particular if you run
+ * tdbtorture, you may get spurious "uninitialized value" warnings. I
+ * think this is because valgrind doesn't understand that the mmap'd
+ * area may be written to by other processes. Memory can, from the
+ * point of view of the grinded process, spontaneously become
+ * initialized.
+ *
+ * I can think of a few solutions. [mbp 20030311]
+ *
+ * 1 - Write suppressions for Valgrind so that it doesn't complain
+ * about this. Probably the most reasonable but people need to
+ * remember to use them.
+ *
+ * 2 - Use IO not mmap when running under valgrind. Not so nice.
+ *
+ * 3 - Use the special valgrind macros to mark memory as valid at the
+ * right time. Probably too hard -- the process just doesn't know.
+ */
+
+#ifdef STANDALONE
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include "tdb.h"
+#include "spinlock.h"
+#else
+#include "includes.h"
+#endif
+
+#define TDB_MAGIC_FOOD "TDB file\n"
+#define TDB_VERSION (0x26011967 + 6)
+#define TDB_MAGIC (0x26011999U)
+#define TDB_FREE_MAGIC (~TDB_MAGIC)
+#define TDB_DEAD_MAGIC (0xFEE1DEAD)
+#define TDB_ALIGNMENT 4
+#define MIN_REC_SIZE (2*sizeof(struct list_struct) + TDB_ALIGNMENT)
+#define DEFAULT_HASH_SIZE 131
+#define TDB_PAGE_SIZE 0x2000
+#define FREELIST_TOP (sizeof(struct tdb_header))
+#define TDB_ALIGN(x,a) (((x) + (a)-1) & ~((a)-1))
+#define TDB_BYTEREV(x) (((((x)&0xff)<<24)|((x)&0xFF00)<<8)|(((x)>>8)&0xFF00)|((x)>>24))
+#define TDB_DEAD(r) ((r)->magic == TDB_DEAD_MAGIC)
+#define TDB_BAD_MAGIC(r) ((r)->magic != TDB_MAGIC && !TDB_DEAD(r))
+#define TDB_HASH_TOP(hash) (FREELIST_TOP + (BUCKET(hash)+1)*sizeof(tdb_off))
+#define TDB_DATA_START(hash_size) (TDB_HASH_TOP(hash_size-1) + TDB_SPINLOCK_SIZE(hash_size))
+
+
+/* NB assumes there is a local variable called "tdb" that is the
+ * current context, also takes doubly-parenthesized print-style
+ * argument. */
+#define TDB_LOG(x) (tdb->log_fn?((tdb->log_fn x),0) : 0)
+
+/* lock offsets */
+#define GLOBAL_LOCK 0
+#define ACTIVE_LOCK 4
+
+#ifndef MAP_FILE
+#define MAP_FILE 0
+#endif
+
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *)-1)
+#endif
+
+/* free memory if the pointer is valid and zero the pointer */
+#ifndef SAFE_FREE
+#define SAFE_FREE(x) do { if ((x) != NULL) {free((x)); (x)=NULL;} } while(0)
+#endif
+
+#define BUCKET(hash) ((hash) % tdb->header.hash_size)
+TDB_DATA tdb_null;
+
+/* all contexts, to ensure no double-opens (fcntl locks don't nest!) */
+static TDB_CONTEXT *tdbs = NULL;
+
+static int tdb_munmap(TDB_CONTEXT *tdb)
+{
+ if (tdb->flags & TDB_INTERNAL)
+ return 0;
+
+#ifdef HAVE_MMAP
+ if (tdb->map_ptr) {
+ int ret = munmap(tdb->map_ptr, tdb->map_size);
+ if (ret != 0)
+ return ret;
+ }
+#endif
+ tdb->map_ptr = NULL;
+ return 0;
+}
+
+static void tdb_mmap(TDB_CONTEXT *tdb)
+{
+ if (tdb->flags & TDB_INTERNAL)
+ return;
+
+#ifdef HAVE_MMAP
+ if (!(tdb->flags & TDB_NOMMAP)) {
+ tdb->map_ptr = mmap(NULL, tdb->map_size,
+ PROT_READ|(tdb->read_only? 0:PROT_WRITE),
+ MAP_SHARED|MAP_FILE, tdb->fd, 0);
+
+ /*
+ * NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!!
+ */
+
+ if (tdb->map_ptr == MAP_FAILED) {
+ tdb->map_ptr = NULL;
+ TDB_LOG((tdb, 2, "tdb_mmap failed for size %d (%s)\n",
+ tdb->map_size, strerror(errno)));
+ }
+ } else {
+ tdb->map_ptr = NULL;
+ }
+#else
+ tdb->map_ptr = NULL;
+#endif
+}
+
+/* Endian conversion: we only ever deal with 4 byte quantities */
+static void *convert(void *buf, u32 size)
+{
+ u32 i, *p = buf;
+ for (i = 0; i < size / 4; i++)
+ p[i] = TDB_BYTEREV(p[i]);
+ return buf;
+}
+#define DOCONV() (tdb->flags & TDB_CONVERT)
+#define CONVERT(x) (DOCONV() ? convert(&x, sizeof(x)) : &x)
+
+/* the body of the database is made of one list_struct for the free space
+ plus a separate data list for each hash value */
+struct list_struct {
+ tdb_off next; /* offset of the next record in the list */
+ tdb_len rec_len; /* total byte length of record */
+ tdb_len key_len; /* byte length of key */
+ tdb_len data_len; /* byte length of data */
+ u32 full_hash; /* the full 32 bit hash of the key */
+ u32 magic; /* try to catch errors */
+ /* the following union is implied:
+ union {
+ char record[rec_len];
+ struct {
+ char key[key_len];
+ char data[data_len];
+ }
+ u32 totalsize; (tailer)
+ }
+ */
+};
+
+/***************************************************************
+ Allow a caller to set a "alarm" flag that tdb can check to abort
+ a blocking lock on SIGALRM.
+***************************************************************/
+
+static sig_atomic_t *palarm_fired;
+
+void tdb_set_lock_alarm(sig_atomic_t *palarm)
+{
+ palarm_fired = palarm;
+}
+
+/* a byte range locking function - return 0 on success
+ this functions locks/unlocks 1 byte at the specified offset.
+
+ On error, errno is also set so that errors are passed back properly
+ through tdb_open(). */
+static int tdb_brlock(TDB_CONTEXT *tdb, tdb_off offset,
+ int rw_type, int lck_type, int probe)
+{
+ struct flock fl;
+ int ret;
+
+ if (tdb->flags & TDB_NOLOCK)
+ return 0;
+ if ((rw_type == F_WRLCK) && (tdb->read_only)) {
+ errno = EACCES;
+ return -1;
+ }
+
+ fl.l_type = rw_type;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = offset;
+ fl.l_len = 1;
+ fl.l_pid = 0;
+
+ do {
+ ret = fcntl(tdb->fd,lck_type,&fl);
+ if (ret == -1 && errno == EINTR && palarm_fired && *palarm_fired)
+ break;
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret == -1) {
+ if (!probe && lck_type != F_SETLK) {
+ /* Ensure error code is set for log fun to examine. */
+ if (errno == EINTR && palarm_fired && *palarm_fired)
+ tdb->ecode = TDB_ERR_LOCK_TIMEOUT;
+ else
+ tdb->ecode = TDB_ERR_LOCK;
+ TDB_LOG((tdb, 5,"tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d\n",
+ tdb->fd, offset, rw_type, lck_type));
+ }
+ /* Was it an alarm timeout ? */
+ if (errno == EINTR && palarm_fired && *palarm_fired) {
+ TDB_LOG((tdb, 5, "tdb_brlock timed out (fd=%d) at offset %d rw_type=%d lck_type=%d\n",
+ tdb->fd, offset, rw_type, lck_type));
+ return TDB_ERRCODE(TDB_ERR_LOCK_TIMEOUT, -1);
+ }
+ /* Otherwise - generic lock error. errno set by fcntl.
+ * EAGAIN is an expected return from non-blocking
+ * locks. */
+ if (errno != EAGAIN) {
+ TDB_LOG((tdb, 5, "tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d: %s\n",
+ tdb->fd, offset, rw_type, lck_type,
+ strerror(errno)));
+ }
+ return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+ }
+ return 0;
+}
+
+/* lock a list in the database. list -1 is the alloc list */
+static int tdb_lock(TDB_CONTEXT *tdb, int list, int ltype)
+{
+ if (list < -1 || list >= (int)tdb->header.hash_size) {
+ TDB_LOG((tdb, 0,"tdb_lock: invalid list %d for ltype=%d\n",
+ list, ltype));
+ return -1;
+ }
+ if (tdb->flags & TDB_NOLOCK)
+ return 0;
+
+ /* Since fcntl locks don't nest, we do a lock for the first one,
+ and simply bump the count for future ones */
+ if (tdb->locked[list+1].count == 0) {
+ if (!tdb->read_only && tdb->header.rwlocks) {
+ if (tdb_spinlock(tdb, list, ltype)) {
+ TDB_LOG((tdb, 0, "tdb_lock spinlock failed on list ltype=%d\n",
+ list, ltype));
+ return -1;
+ }
+ } else if (tdb_brlock(tdb,FREELIST_TOP+4*list,ltype,F_SETLKW, 0)) {
+ TDB_LOG((tdb, 0,"tdb_lock failed on list %d ltype=%d (%s)\n",
+ list, ltype, strerror(errno)));
+ return -1;
+ }
+ tdb->locked[list+1].ltype = ltype;
+ }
+ tdb->locked[list+1].count++;
+ return 0;
+}
+
+/* unlock the database: returns void because it's too late for errors. */
+ /* changed to return int it may be interesting to know there
+ has been an error --simo */
+static int tdb_unlock(TDB_CONTEXT *tdb, int list, int ltype)
+{
+ int ret = -1;
+
+ if (tdb->flags & TDB_NOLOCK)
+ return 0;
+
+ /* Sanity checks */
+ if (list < -1 || list >= (int)tdb->header.hash_size) {
+ TDB_LOG((tdb, 0, "tdb_unlock: list %d invalid (%d)\n", list, tdb->header.hash_size));
+ return ret;
+ }
+
+ if (tdb->locked[list+1].count==0) {
+ TDB_LOG((tdb, 0, "tdb_unlock: count is 0\n"));
+ return ret;
+ }
+
+ if (tdb->locked[list+1].count == 1) {
+ /* Down to last nested lock: unlock underneath */
+ if (!tdb->read_only && tdb->header.rwlocks) {
+ ret = tdb_spinunlock(tdb, list, ltype);
+ } else {
+ ret = tdb_brlock(tdb, FREELIST_TOP+4*list, F_UNLCK, F_SETLKW, 0);
+ }
+ } else {
+ ret = 0;
+ }
+ tdb->locked[list+1].count--;
+
+ if (ret)
+ TDB_LOG((tdb, 0,"tdb_unlock: An error occurred unlocking!\n"));
+ return ret;
+}
+
+/* This is based on the hash algorithm from gdbm */
+static u32 tdb_hash(TDB_DATA *key)
+{
+ u32 value; /* Used to compute the hash value. */
+ u32 i; /* Used to cycle through random values. */
+
+ /* Set the initial value from the key size. */
+ for (value = 0x238F13AF * key->dsize, i=0; i < key->dsize; i++)
+ value = (value + (key->dptr[i] << (i*5 % 24)));
+
+ return (1103515243 * value + 12345);
+}
+
+/* check for an out of bounds access - if it is out of bounds then
+ see if the database has been expanded by someone else and expand
+ if necessary
+ note that "len" is the minimum length needed for the db
+*/
+static int tdb_oob(TDB_CONTEXT *tdb, tdb_off len, int probe)
+{
+ struct stat st;
+ if (len <= tdb->map_size)
+ return 0;
+ if (tdb->flags & TDB_INTERNAL) {
+ if (!probe) {
+ /* Ensure ecode is set for log fn. */
+ tdb->ecode = TDB_ERR_IO;
+ TDB_LOG((tdb, 0,"tdb_oob len %d beyond internal malloc size %d\n",
+ (int)len, (int)tdb->map_size));
+ }
+ return TDB_ERRCODE(TDB_ERR_IO, -1);
+ }
+
+ if (fstat(tdb->fd, &st) == -1)
+ return TDB_ERRCODE(TDB_ERR_IO, -1);
+
+ if (st.st_size < (size_t)len) {
+ if (!probe) {
+ /* Ensure ecode is set for log fn. */
+ tdb->ecode = TDB_ERR_IO;
+ TDB_LOG((tdb, 0,"tdb_oob len %d beyond eof at %d\n",
+ (int)len, (int)st.st_size));
+ }
+ return TDB_ERRCODE(TDB_ERR_IO, -1);
+ }
+
+ /* Unmap, update size, remap */
+ if (tdb_munmap(tdb) == -1)
+ return TDB_ERRCODE(TDB_ERR_IO, -1);
+ tdb->map_size = st.st_size;
+ tdb_mmap(tdb);
+ return 0;
+}
+
+/* write a lump of data at a specified offset */
+static int tdb_write(TDB_CONTEXT *tdb, tdb_off off, void *buf, tdb_len len)
+{
+ if (tdb_oob(tdb, off + len, 0) != 0)
+ return -1;
+
+ if (tdb->map_ptr)
+ memcpy(off + (char *)tdb->map_ptr, buf, len);
+#ifdef HAVE_PWRITE
+ else if (pwrite(tdb->fd, buf, len, off) != (ssize_t)len) {
+#else
+ else if (lseek(tdb->fd, off, SEEK_SET) != off
+ || write(tdb->fd, buf, len) != (ssize_t)len) {
+#endif
+ /* Ensure ecode is set for log fn. */
+ tdb->ecode = TDB_ERR_IO;
+ TDB_LOG((tdb, 0,"tdb_write failed at %d len=%d (%s)\n",
+ off, len, strerror(errno)));
+ return TDB_ERRCODE(TDB_ERR_IO, -1);
+ }
+ return 0;
+}
+
+/* read a lump of data at a specified offset, maybe convert */
+static int tdb_read(TDB_CONTEXT *tdb,tdb_off off,void *buf,tdb_len len,int cv)
+{
+ if (tdb_oob(tdb, off + len, 0) != 0)
+ return -1;
+
+ if (tdb->map_ptr)
+ memcpy(buf, off + (char *)tdb->map_ptr, len);
+#ifdef HAVE_PREAD
+ else if (pread(tdb->fd, buf, len, off) != (ssize_t)len) {
+#else
+ else if (lseek(tdb->fd, off, SEEK_SET) != off
+ || read(tdb->fd, buf, len) != (ssize_t)len) {
+#endif
+ /* Ensure ecode is set for log fn. */
+ tdb->ecode = TDB_ERR_IO;
+ TDB_LOG((tdb, 0,"tdb_read failed at %d len=%d (%s)\n",
+ off, len, strerror(errno)));
+ return TDB_ERRCODE(TDB_ERR_IO, -1);
+ }
+ if (cv)
+ convert(buf, len);
+ return 0;
+}
+
+/* read a lump of data, allocating the space for it */
+static char *tdb_alloc_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_len len)
+{
+ char *buf;
+
+ if (!(buf = malloc(len))) {
+ /* Ensure ecode is set for log fn. */
+ tdb->ecode = TDB_ERR_OOM;
+ TDB_LOG((tdb, 0,"tdb_alloc_read malloc failed len=%d (%s)\n",
+ len, strerror(errno)));
+ return TDB_ERRCODE(TDB_ERR_OOM, buf);
+ }
+ if (tdb_read(tdb, offset, buf, len, 0) == -1) {
+ SAFE_FREE(buf);
+ return NULL;
+ }
+ return buf;
+}
+
+/* read/write a tdb_off */
+static int ofs_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d)
+{
+ return tdb_read(tdb, offset, (char*)d, sizeof(*d), DOCONV());
+}
+static int ofs_write(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d)
+{
+ tdb_off off = *d;
+ return tdb_write(tdb, offset, CONVERT(off), sizeof(*d));
+}
+
+/* read/write a record */
+static int rec_read(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
+{
+ if (tdb_read(tdb, offset, rec, sizeof(*rec),DOCONV()) == -1)
+ return -1;
+ if (TDB_BAD_MAGIC(rec)) {
+ /* Ensure ecode is set for log fn. */
+ tdb->ecode = TDB_ERR_CORRUPT;
+ TDB_LOG((tdb, 0,"rec_read bad magic 0x%x at offset=%d\n", rec->magic, offset));
+ return TDB_ERRCODE(TDB_ERR_CORRUPT, -1);
+ }
+ return tdb_oob(tdb, rec->next+sizeof(*rec), 0);
+}
+static int rec_write(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
+{
+ struct list_struct r = *rec;
+ return tdb_write(tdb, offset, CONVERT(r), sizeof(r));
+}
+
+/* read a freelist record and check for simple errors */
+static int rec_free_read(TDB_CONTEXT *tdb, tdb_off off, struct list_struct *rec)
+{
+ if (tdb_read(tdb, off, rec, sizeof(*rec),DOCONV()) == -1)
+ return -1;
+
+ if (rec->magic == TDB_MAGIC) {
+ /* this happens when a app is showdown while deleting a record - we should
+ not completely fail when this happens */
+ TDB_LOG((tdb, 0,"rec_free_read non-free magic at offset=%d - fixing\n",
+ rec->magic, off));
+ rec->magic = TDB_FREE_MAGIC;
+ if (tdb_write(tdb, off, rec, sizeof(*rec)) == -1)
+ return -1;
+ }
+
+ if (rec->magic != TDB_FREE_MAGIC) {
+ /* Ensure ecode is set for log fn. */
+ tdb->ecode = TDB_ERR_CORRUPT;
+ TDB_LOG((tdb, 0,"rec_free_read bad magic 0x%x at offset=%d\n",
+ rec->magic, off));
+ return TDB_ERRCODE(TDB_ERR_CORRUPT, -1);
+ }
+ if (tdb_oob(tdb, rec->next+sizeof(*rec), 0) != 0)
+ return -1;
+ return 0;
+}
+
+/* update a record tailer (must hold allocation lock) */
+static int update_tailer(TDB_CONTEXT *tdb, tdb_off offset,
+ const struct list_struct *rec)
+{
+ tdb_off totalsize;
+
+ /* Offset of tailer from record header */
+ totalsize = sizeof(*rec) + rec->rec_len;
+ return ofs_write(tdb, offset + totalsize - sizeof(tdb_off),
+ &totalsize);
+}
+
+static tdb_off tdb_dump_record(TDB_CONTEXT *tdb, tdb_off offset)
+{
+ struct list_struct rec;
+ tdb_off tailer_ofs, tailer;
+
+ if (tdb_read(tdb, offset, (char *)&rec, sizeof(rec), DOCONV()) == -1) {
+ printf("ERROR: failed to read record at %u\n", offset);
+ return 0;
+ }
+
+ printf(" rec: offset=%u next=%d rec_len=%d key_len=%d data_len=%d full_hash=0x%x magic=0x%x\n",
+ offset, rec.next, rec.rec_len, rec.key_len, rec.data_len, rec.full_hash, rec.magic);
+
+ tailer_ofs = offset + sizeof(rec) + rec.rec_len - sizeof(tdb_off);
+ if (ofs_read(tdb, tailer_ofs, &tailer) == -1) {
+ printf("ERROR: failed to read tailer at %u\n", tailer_ofs);
+ return rec.next;
+ }
+
+ if (tailer != rec.rec_len + sizeof(rec)) {
+ printf("ERROR: tailer does not match record! tailer=%u totalsize=%u\n",
+ (unsigned)tailer, (unsigned)(rec.rec_len + sizeof(rec)));
+ }
+ return rec.next;
+}
+
+static int tdb_dump_chain(TDB_CONTEXT *tdb, int i)
+{
+ tdb_off rec_ptr, top;
+
+ top = TDB_HASH_TOP(i);
+
+ if (tdb_lock(tdb, i, F_WRLCK) != 0)
+ return -1;
+
+ if (ofs_read(tdb, top, &rec_ptr) == -1)
+ return tdb_unlock(tdb, i, F_WRLCK);
+
+ if (rec_ptr)
+ printf("hash=%d\n", i);
+
+ while (rec_ptr) {
+ rec_ptr = tdb_dump_record(tdb, rec_ptr);
+ }
+
+ return tdb_unlock(tdb, i, F_WRLCK);
+}
+
+void tdb_dump_all(TDB_CONTEXT *tdb)
+{
+ int i;
+ for (i=0;i<tdb->header.hash_size;i++) {
+ tdb_dump_chain(tdb, i);
+ }
+ printf("freelist:\n");
+ tdb_dump_chain(tdb, -1);
+}
+
+int tdb_printfreelist(TDB_CONTEXT *tdb)
+{
+ int ret;
+ long total_free = 0;
+ tdb_off offset, rec_ptr;
+ struct list_struct rec;
+
+ if ((ret = tdb_lock(tdb, -1, F_WRLCK)) != 0)
+ return ret;
+
+ offset = FREELIST_TOP;
+
+ /* read in the freelist top */
+ if (ofs_read(tdb, offset, &rec_ptr) == -1) {
+ tdb_unlock(tdb, -1, F_WRLCK);
+ return 0;
+ }
+
+ printf("freelist top=[0x%08x]\n", rec_ptr );
+ while (rec_ptr) {
+ if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec), DOCONV()) == -1) {
+ tdb_unlock(tdb, -1, F_WRLCK);
+ return -1;
+ }
+
+ if (rec.magic != TDB_FREE_MAGIC) {
+ printf("bad magic 0x%08x in free list\n", rec.magic);
+ tdb_unlock(tdb, -1, F_WRLCK);
+ return -1;
+ }
+
+ printf("entry offset=[0x%08x], rec.rec_len = [0x%08x (%d)]\n", rec.next, rec.rec_len, rec.rec_len );
+ total_free += rec.rec_len;
+
+ /* move to the next record */
+ rec_ptr = rec.next;
+ }
+ printf("total rec_len = [0x%08x (%d)]\n", (int)total_free,
+ (int)total_free);
+
+ return tdb_unlock(tdb, -1, F_WRLCK);
+}
+
+/* Remove an element from the freelist. Must have alloc lock. */
+static int remove_from_freelist(TDB_CONTEXT *tdb, tdb_off off, tdb_off next)
+{
+ tdb_off last_ptr, i;
+
+ /* read in the freelist top */
+ last_ptr = FREELIST_TOP;
+ while (ofs_read(tdb, last_ptr, &i) != -1 && i != 0) {
+ if (i == off) {
+ /* We've found it! */
+ return ofs_write(tdb, last_ptr, &next);
+ }
+ /* Follow chain (next offset is at start of record) */
+ last_ptr = i;
+ }
+ TDB_LOG((tdb, 0,"remove_from_freelist: not on list at off=%d\n", off));
+ return TDB_ERRCODE(TDB_ERR_CORRUPT, -1);
+}
+
+/* Add an element into the freelist. Merge adjacent records if
+ neccessary. */
+static int tdb_free(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
+{
+ tdb_off right, left;
+
+ /* Allocation and tailer lock */
+ if (tdb_lock(tdb, -1, F_WRLCK) != 0)
+ return -1;
+
+ /* set an initial tailer, so if we fail we don't leave a bogus record */
+ if (update_tailer(tdb, offset, rec) != 0) {
+ TDB_LOG((tdb, 0, "tdb_free: upfate_tailer failed!\n"));
+ goto fail;
+ }
+
+ /* Look right first (I'm an Australian, dammit) */
+ right = offset + sizeof(*rec) + rec->rec_len;
+ if (right + sizeof(*rec) <= tdb->map_size) {
+ struct list_struct r;
+
+ if (tdb_read(tdb, right, &r, sizeof(r), DOCONV()) == -1) {
+ TDB_LOG((tdb, 0, "tdb_free: right read failed at %u\n", right));
+ goto left;
+ }
+
+ /* If it's free, expand to include it. */
+ if (r.magic == TDB_FREE_MAGIC) {
+ if (remove_from_freelist(tdb, right, r.next) == -1) {
+ TDB_LOG((tdb, 0, "tdb_free: right free failed at %u\n", right));
+ goto left;
+ }
+ rec->rec_len += sizeof(r) + r.rec_len;
+ }
+ }
+
+left:
+ /* Look left */
+ left = offset - sizeof(tdb_off);
+ if (left > TDB_DATA_START(tdb->header.hash_size)) {
+ struct list_struct l;
+ tdb_off leftsize;
+
+ /* Read in tailer and jump back to header */
+ if (ofs_read(tdb, left, &leftsize) == -1) {
+ TDB_LOG((tdb, 0, "tdb_free: left offset read failed at %u\n", left));
+ goto update;
+ }
+ left = offset - leftsize;
+
+ /* Now read in record */
+ if (tdb_read(tdb, left, &l, sizeof(l), DOCONV()) == -1) {
+ TDB_LOG((tdb, 0, "tdb_free: left read failed at %u (%u)\n", left, leftsize));
+ goto update;
+ }
+
+ /* If it's free, expand to include it. */
+ if (l.magic == TDB_FREE_MAGIC) {
+ if (remove_from_freelist(tdb, left, l.next) == -1) {
+ TDB_LOG((tdb, 0, "tdb_free: left free failed at %u\n", left));
+ goto update;
+ } else {
+ offset = left;
+ rec->rec_len += leftsize;
+ }
+ }
+ }
+
+update:
+ if (update_tailer(tdb, offset, rec) == -1) {
+ TDB_LOG((tdb, 0, "tdb_free: update_tailer failed at %u\n", offset));
+ goto fail;
+ }
+
+ /* Now, prepend to free list */
+ rec->magic = TDB_FREE_MAGIC;
+
+ if (ofs_read(tdb, FREELIST_TOP, &rec->next) == -1 ||
+ rec_write(tdb, offset, rec) == -1 ||
+ ofs_write(tdb, FREELIST_TOP, &offset) == -1) {
+ TDB_LOG((tdb, 0, "tdb_free record write failed at offset=%d\n", offset));
+ goto fail;
+ }
+
+ /* And we're done. */
+ tdb_unlock(tdb, -1, F_WRLCK);
+ return 0;
+
+ fail:
+ tdb_unlock(tdb, -1, F_WRLCK);
+ return -1;
+}
+
+
+/* expand a file. we prefer to use ftruncate, as that is what posix
+ says to use for mmap expansion */
+static int expand_file(TDB_CONTEXT *tdb, tdb_off size, tdb_off addition)
+{
+ char buf[1024];
+#if HAVE_FTRUNCATE_EXTEND
+ if (ftruncate(tdb->fd, size+addition) != 0) {
+ TDB_LOG((tdb, 0, "expand_file ftruncate to %d failed (%s)\n",
+ size+addition, strerror(errno)));
+ return -1;
+ }
+#else
+ char b = 0;
+
+#ifdef HAVE_PWRITE
+ if (pwrite(tdb->fd, &b, 1, (size+addition) - 1) != 1) {
+#else
+ if (lseek(tdb->fd, (size+addition) - 1, SEEK_SET) != (size+addition) - 1 ||
+ write(tdb->fd, &b, 1) != 1) {
+#endif
+ TDB_LOG((tdb, 0, "expand_file to %d failed (%s)\n",
+ size+addition, strerror(errno)));
+ return -1;
+ }
+#endif
+
+ /* now fill the file with something. This ensures that the file isn't sparse, which would be
+ very bad if we ran out of disk. This must be done with write, not via mmap */
+ memset(buf, 0x42, sizeof(buf));
+ while (addition) {
+ int n = addition>sizeof(buf)?sizeof(buf):addition;
+#ifdef HAVE_PWRITE
+ int ret = pwrite(tdb->fd, buf, n, size);
+#else
+ int ret;
+ if (lseek(tdb->fd, size, SEEK_SET) != size)
+ return -1;
+ ret = write(tdb->fd, buf, n);
+#endif
+ if (ret != n) {
+ TDB_LOG((tdb, 0, "expand_file write of %d failed (%s)\n",
+ n, strerror(errno)));
+ return -1;
+ }
+ addition -= n;
+ size += n;
+ }
+ return 0;
+}
+
+
+/* expand the database at least size bytes by expanding the underlying
+ file and doing the mmap again if necessary */
+static int tdb_expand(TDB_CONTEXT *tdb, tdb_off size)
+{
+ struct list_struct rec;
+ tdb_off offset;
+
+ if (tdb_lock(tdb, -1, F_WRLCK) == -1) {
+ TDB_LOG((tdb, 0, "lock failed in tdb_expand\n"));
+ return -1;
+ }
+
+ /* must know about any previous expansions by another process */
+ tdb_oob(tdb, tdb->map_size + 1, 1);
+
+ /* always make room for at least 10 more records, and round
+ the database up to a multiple of TDB_PAGE_SIZE */
+ size = TDB_ALIGN(tdb->map_size + size*10, TDB_PAGE_SIZE) - tdb->map_size;
+
+ if (!(tdb->flags & TDB_INTERNAL))
+ tdb_munmap(tdb);
+
+ /*
+ * We must ensure the file is unmapped before doing this
+ * to ensure consistency with systems like OpenBSD where
+ * writes and mmaps are not consistent.
+ */
+
+ /* expand the file itself */
+ if (!(tdb->flags & TDB_INTERNAL)) {
+ if (expand_file(tdb, tdb->map_size, size) != 0)
+ goto fail;
+ }
+
+ tdb->map_size += size;
+
+ if (tdb->flags & TDB_INTERNAL)
+ tdb->map_ptr = realloc(tdb->map_ptr, tdb->map_size);
+ else {
+ /*
+ * We must ensure the file is remapped before adding the space
+ * to ensure consistency with systems like OpenBSD where
+ * writes and mmaps are not consistent.
+ */
+
+ /* We're ok if the mmap fails as we'll fallback to read/write */
+ tdb_mmap(tdb);
+ }
+
+ /* form a new freelist record */
+ memset(&rec,'\0',sizeof(rec));
+ rec.rec_len = size - sizeof(rec);
+
+ /* link it into the free list */
+ offset = tdb->map_size - size;
+ if (tdb_free(tdb, offset, &rec) == -1)
+ goto fail;
+
+ tdb_unlock(tdb, -1, F_WRLCK);
+ return 0;
+ fail:
+ tdb_unlock(tdb, -1, F_WRLCK);
+ return -1;
+}
+
+/* allocate some space from the free list. The offset returned points
+ to a unconnected list_struct within the database with room for at
+ least length bytes of total data
+
+ 0 is returned if the space could not be allocated
+ */
+static tdb_off tdb_allocate(TDB_CONTEXT *tdb, tdb_len length,
+ struct list_struct *rec)
+{
+ tdb_off rec_ptr, last_ptr, newrec_ptr;
+ struct list_struct newrec;
+
+ memset(&newrec, '\0', sizeof(newrec));
+
+ if (tdb_lock(tdb, -1, F_WRLCK) == -1)
+ return 0;
+
+ /* Extra bytes required for tailer */
+ length += sizeof(tdb_off);
+
+ again:
+ last_ptr = FREELIST_TOP;
+
+ /* read in the freelist top */
+ if (ofs_read(tdb, FREELIST_TOP, &rec_ptr) == -1)
+ goto fail;
+
+ /* keep looking until we find a freelist record big enough */
+ while (rec_ptr) {
+ if (rec_free_read(tdb, rec_ptr, rec) == -1)
+ goto fail;
+
+ if (rec->rec_len >= length) {
+ /* found it - now possibly split it up */
+ if (rec->rec_len > length + MIN_REC_SIZE) {
+ /* Length of left piece */
+ length = TDB_ALIGN(length, TDB_ALIGNMENT);
+
+ /* Right piece to go on free list */
+ newrec.rec_len = rec->rec_len
+ - (sizeof(*rec) + length);
+ newrec_ptr = rec_ptr + sizeof(*rec) + length;
+
+ /* And left record is shortened */
+ rec->rec_len = length;
+ } else
+ newrec_ptr = 0;
+
+ /* Remove allocated record from the free list */
+ if (ofs_write(tdb, last_ptr, &rec->next) == -1)
+ goto fail;
+
+ /* Update header: do this before we drop alloc
+ lock, otherwise tdb_free() might try to
+ merge with us, thinking we're free.
+ (Thanks Jeremy Allison). */
+ rec->magic = TDB_MAGIC;
+ if (rec_write(tdb, rec_ptr, rec) == -1)
+ goto fail;
+
+ /* Did we create new block? */
+ if (newrec_ptr) {
+ /* Update allocated record tailer (we
+ shortened it). */
+ if (update_tailer(tdb, rec_ptr, rec) == -1)
+ goto fail;
+
+ /* Free new record */
+ if (tdb_free(tdb, newrec_ptr, &newrec) == -1)
+ goto fail;
+ }
+
+ /* all done - return the new record offset */
+ tdb_unlock(tdb, -1, F_WRLCK);
+ return rec_ptr;
+ }
+ /* move to the next record */
+ last_ptr = rec_ptr;
+ rec_ptr = rec->next;
+ }
+ /* we didn't find enough space. See if we can expand the
+ database and if we can then try again */
+ if (tdb_expand(tdb, length + sizeof(*rec)) == 0)
+ goto again;
+ fail:
+ tdb_unlock(tdb, -1, F_WRLCK);
+ return 0;
+}
+
+/* initialise a new database with a specified hash size */
+static int tdb_new_database(TDB_CONTEXT *tdb, int hash_size)
+{
+ struct tdb_header *newdb;
+ int size, ret = -1;
+
+ /* We make it up in memory, then write it out if not internal */
+ size = sizeof(struct tdb_header) + (hash_size+1)*sizeof(tdb_off);
+ if (!(newdb = calloc(size, 1)))
+ return TDB_ERRCODE(TDB_ERR_OOM, -1);
+
+ /* Fill in the header */
+ newdb->version = TDB_VERSION;
+ newdb->hash_size = hash_size;
+#ifdef USE_SPINLOCKS
+ newdb->rwlocks = size;
+#endif
+ if (tdb->flags & TDB_INTERNAL) {
+ tdb->map_size = size;
+ tdb->map_ptr = (char *)newdb;
+ memcpy(&tdb->header, newdb, sizeof(tdb->header));
+ /* Convert the `ondisk' version if asked. */
+ CONVERT(*newdb);
+ return 0;
+ }
+ if (lseek(tdb->fd, 0, SEEK_SET) == -1)
+ goto fail;
+
+ if (ftruncate(tdb->fd, 0) == -1)
+ goto fail;
+
+ /* This creates an endian-converted header, as if read from disk */
+ CONVERT(*newdb);
+ memcpy(&tdb->header, newdb, sizeof(tdb->header));
+ /* Don't endian-convert the magic food! */
+ memcpy(newdb->magic_food, TDB_MAGIC_FOOD, strlen(TDB_MAGIC_FOOD)+1);
+ if (write(tdb->fd, newdb, size) != size)
+ ret = -1;
+ else
+ ret = tdb_create_rwlocks(tdb->fd, hash_size);
+
+ fail:
+ SAFE_FREE(newdb);
+ return ret;
+}
+
+/* Returns 0 on fail. On success, return offset of record, and fills
+ in rec */
+static tdb_off tdb_find(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash,
+ struct list_struct *r)
+{
+ tdb_off rec_ptr;
+
+ /* read in the hash top */
+ if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1)
+ return 0;
+
+ /* keep looking until we find the right record */
+ while (rec_ptr) {
+ if (rec_read(tdb, rec_ptr, r) == -1)
+ return 0;
+
+ if (!TDB_DEAD(r) && hash==r->full_hash && key.dsize==r->key_len) {
+ char *k;
+ /* a very likely hit - read the key */
+ k = tdb_alloc_read(tdb, rec_ptr + sizeof(*r),
+ r->key_len);
+ if (!k)
+ return 0;
+
+ if (memcmp(key.dptr, k, key.dsize) == 0) {
+ SAFE_FREE(k);
+ return rec_ptr;
+ }
+ SAFE_FREE(k);
+ }
+ rec_ptr = r->next;
+ }
+ return TDB_ERRCODE(TDB_ERR_NOEXIST, 0);
+}
+
+/* If they do lockkeys, check that this hash is one they locked */
+static int tdb_keylocked(TDB_CONTEXT *tdb, u32 hash)
+{
+ u32 i;
+ if (!tdb->lockedkeys)
+ return 1;
+ for (i = 0; i < tdb->lockedkeys[0]; i++)
+ if (tdb->lockedkeys[i+1] == hash)
+ return 1;
+ return TDB_ERRCODE(TDB_ERR_NOLOCK, 0);
+}
+
+/* As tdb_find, but if you succeed, keep the lock */
+static tdb_off tdb_find_lock_hash(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash, int locktype,
+ struct list_struct *rec)
+{
+ u32 rec_ptr;
+
+ if (!tdb_keylocked(tdb, hash))
+ return 0;
+ if (tdb_lock(tdb, BUCKET(hash), locktype) == -1)
+ return 0;
+ if (!(rec_ptr = tdb_find(tdb, key, hash, rec)))
+ tdb_unlock(tdb, BUCKET(hash), locktype);
+ return rec_ptr;
+}
+
+enum TDB_ERROR tdb_error(TDB_CONTEXT *tdb)
+{
+ return tdb->ecode;
+}
+
+static struct tdb_errname {
+ enum TDB_ERROR ecode; const char *estring;
+} emap[] = { {TDB_SUCCESS, "Success"},
+ {TDB_ERR_CORRUPT, "Corrupt database"},
+ {TDB_ERR_IO, "IO Error"},
+ {TDB_ERR_LOCK, "Locking error"},
+ {TDB_ERR_OOM, "Out of memory"},
+ {TDB_ERR_EXISTS, "Record exists"},
+ {TDB_ERR_NOLOCK, "Lock exists on other keys"},
+ {TDB_ERR_NOEXIST, "Record does not exist"} };
+
+/* Error string for the last tdb error */
+const char *tdb_errorstr(TDB_CONTEXT *tdb)
+{
+ u32 i;
+ for (i = 0; i < sizeof(emap) / sizeof(struct tdb_errname); i++)
+ if (tdb->ecode == emap[i].ecode)
+ return emap[i].estring;
+ return "Invalid error code";
+}
+
+/* update an entry in place - this only works if the new data size
+ is <= the old data size and the key exists.
+ on failure return -1.
+*/
+
+static int tdb_update_hash(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash, TDB_DATA dbuf)
+{
+ struct list_struct rec;
+ tdb_off rec_ptr;
+
+ /* find entry */
+ if (!(rec_ptr = tdb_find(tdb, key, hash, &rec)))
+ return -1;
+
+ /* must be long enough key, data and tailer */
+ if (rec.rec_len < key.dsize + dbuf.dsize + sizeof(tdb_off)) {
+ tdb->ecode = TDB_SUCCESS; /* Not really an error */
+ return -1;
+ }
+
+ if (tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len,
+ dbuf.dptr, dbuf.dsize) == -1)
+ return -1;
+
+ if (dbuf.dsize != rec.data_len) {
+ /* update size */
+ rec.data_len = dbuf.dsize;
+ return rec_write(tdb, rec_ptr, &rec);
+ }
+
+ return 0;
+}
+
+/* find an entry in the database given a key */
+/* If an entry doesn't exist tdb_err will be set to
+ * TDB_ERR_NOEXIST. If a key has no data attached
+ * tdb_err will not be set. Both will return a
+ * zero pptr and zero dsize.
+ */
+
+TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+ tdb_off rec_ptr;
+ struct list_struct rec;
+ TDB_DATA ret;
+ u32 hash;
+
+ /* find which hash bucket it is in */
+ hash = tdb_hash(&key);
+ if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec)))
+ return tdb_null;
+
+ if (rec.data_len)
+ ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec) + rec.key_len,
+ rec.data_len);
+ else
+ ret.dptr = NULL;
+ ret.dsize = rec.data_len;
+ tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
+ return ret;
+}
+
+/* check if an entry in the database exists
+
+ note that 1 is returned if the key is found and 0 is returned if not found
+ this doesn't match the conventions in the rest of this module, but is
+ compatible with gdbm
+*/
+static int tdb_exists_hash(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash)
+{
+ struct list_struct rec;
+
+ if (tdb_find_lock_hash(tdb, key, hash, F_RDLCK, &rec) == 0)
+ return 0;
+ tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
+ return 1;
+}
+
+int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+ u32 hash = tdb_hash(&key);
+ return tdb_exists_hash(tdb, key, hash);
+}
+
+/* record lock stops delete underneath */
+static int lock_record(TDB_CONTEXT *tdb, tdb_off off)
+{
+ return off ? tdb_brlock(tdb, off, F_RDLCK, F_SETLKW, 0) : 0;
+}
+/*
+ Write locks override our own fcntl readlocks, so check it here.
+ Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not
+ an error to fail to get the lock here.
+*/
+
+static int write_lock_record(TDB_CONTEXT *tdb, tdb_off off)
+{
+ struct tdb_traverse_lock *i;
+ for (i = &tdb->travlocks; i; i = i->next)
+ if (i->off == off)
+ return -1;
+ return tdb_brlock(tdb, off, F_WRLCK, F_SETLK, 1);
+}
+
+/*
+ Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not
+ an error to fail to get the lock here.
+*/
+
+static int write_unlock_record(TDB_CONTEXT *tdb, tdb_off off)
+{
+ return tdb_brlock(tdb, off, F_UNLCK, F_SETLK, 0);
+}
+/* fcntl locks don't stack: avoid unlocking someone else's */
+static int unlock_record(TDB_CONTEXT *tdb, tdb_off off)
+{
+ struct tdb_traverse_lock *i;
+ u32 count = 0;
+
+ if (off == 0)
+ return 0;
+ for (i = &tdb->travlocks; i; i = i->next)
+ if (i->off == off)
+ count++;
+ return (count == 1 ? tdb_brlock(tdb, off, F_UNLCK, F_SETLKW, 0) : 0);
+}
+
+/* actually delete an entry in the database given the offset */
+static int do_delete(TDB_CONTEXT *tdb, tdb_off rec_ptr, struct list_struct*rec)
+{
+ tdb_off last_ptr, i;
+ struct list_struct lastrec;
+
+ if (tdb->read_only) return -1;
+
+ if (write_lock_record(tdb, rec_ptr) == -1) {
+ /* Someone traversing here: mark it as dead */
+ rec->magic = TDB_DEAD_MAGIC;
+ return rec_write(tdb, rec_ptr, rec);
+ }
+ if (write_unlock_record(tdb, rec_ptr) != 0)
+ return -1;
+
+ /* find previous record in hash chain */
+ if (ofs_read(tdb, TDB_HASH_TOP(rec->full_hash), &i) == -1)
+ return -1;
+ for (last_ptr = 0; i != rec_ptr; last_ptr = i, i = lastrec.next)
+ if (rec_read(tdb, i, &lastrec) == -1)
+ return -1;
+
+ /* unlink it: next ptr is at start of record. */
+ if (last_ptr == 0)
+ last_ptr = TDB_HASH_TOP(rec->full_hash);
+ if (ofs_write(tdb, last_ptr, &rec->next) == -1)
+ return -1;
+
+ /* recover the space */
+ if (tdb_free(tdb, rec_ptr, rec) == -1)
+ return -1;
+ return 0;
+}
+
+/* Uses traverse lock: 0 = finish, -1 = error, other = record offset */
+static int tdb_next_lock(TDB_CONTEXT *tdb, struct tdb_traverse_lock *tlock,
+ struct list_struct *rec)
+{
+ int want_next = (tlock->off != 0);
+
+ /* No traversal allows if you've called tdb_lockkeys() */
+ if (tdb->lockedkeys)
+ return TDB_ERRCODE(TDB_ERR_NOLOCK, -1);
+
+ /* Lock each chain from the start one. */
+ for (; tlock->hash < tdb->header.hash_size; tlock->hash++) {
+ if (tdb_lock(tdb, tlock->hash, F_WRLCK) == -1)
+ return -1;
+
+ /* No previous record? Start at top of chain. */
+ if (!tlock->off) {
+ if (ofs_read(tdb, TDB_HASH_TOP(tlock->hash),
+ &tlock->off) == -1)
+ goto fail;
+ } else {
+ /* Otherwise unlock the previous record. */
+ if (unlock_record(tdb, tlock->off) != 0)
+ goto fail;
+ }
+
+ if (want_next) {
+ /* We have offset of old record: grab next */
+ if (rec_read(tdb, tlock->off, rec) == -1)
+ goto fail;
+ tlock->off = rec->next;
+ }
+
+ /* Iterate through chain */
+ while( tlock->off) {
+ tdb_off current;
+ if (rec_read(tdb, tlock->off, rec) == -1)
+ goto fail;
+ if (!TDB_DEAD(rec)) {
+ /* Woohoo: we found one! */
+ if (lock_record(tdb, tlock->off) != 0)
+ goto fail;
+ return tlock->off;
+ }
+ /* Try to clean dead ones from old traverses */
+ current = tlock->off;
+ tlock->off = rec->next;
+ if (!tdb->read_only &&
+ do_delete(tdb, current, rec) != 0)
+ goto fail;
+ }
+ tdb_unlock(tdb, tlock->hash, F_WRLCK);
+ want_next = 0;
+ }
+ /* We finished iteration without finding anything */
+ return TDB_ERRCODE(TDB_SUCCESS, 0);
+
+ fail:
+ tlock->off = 0;
+ if (tdb_unlock(tdb, tlock->hash, F_WRLCK) != 0)
+ TDB_LOG((tdb, 0, "tdb_next_lock: On error unlock failed!\n"));
+ return -1;
+}
+
+/* traverse the entire database - calling fn(tdb, key, data) on each element.
+ return -1 on error or the record count traversed
+ if fn is NULL then it is not called
+ a non-zero return value from fn() indicates that the traversal should stop
+ */
+int tdb_traverse(TDB_CONTEXT *tdb, tdb_traverse_func fn, void *private)
+{
+ TDB_DATA key, dbuf;
+ struct list_struct rec;
+ struct tdb_traverse_lock tl = { NULL, 0, 0 };
+ int ret, count = 0;
+
+ /* This was in the initializaton, above, but the IRIX compiler
+ * did not like it. crh
+ */
+ tl.next = tdb->travlocks.next;
+
+ /* fcntl locks don't stack: beware traverse inside traverse */
+ tdb->travlocks.next = &tl;
+
+ /* tdb_next_lock places locks on the record returned, and its chain */
+ while ((ret = tdb_next_lock(tdb, &tl, &rec)) > 0) {
+ count++;
+ /* now read the full record */
+ key.dptr = tdb_alloc_read(tdb, tl.off + sizeof(rec),
+ rec.key_len + rec.data_len);
+ if (!key.dptr) {
+ ret = -1;
+ if (tdb_unlock(tdb, tl.hash, F_WRLCK) != 0)
+ goto out;
+ if (unlock_record(tdb, tl.off) != 0)
+ TDB_LOG((tdb, 0, "tdb_traverse: key.dptr == NULL and unlock_record failed!\n"));
+ goto out;
+ }
+ key.dsize = rec.key_len;
+ dbuf.dptr = key.dptr + rec.key_len;
+ dbuf.dsize = rec.data_len;
+
+ /* Drop chain lock, call out */
+ if (tdb_unlock(tdb, tl.hash, F_WRLCK) != 0) {
+ ret = -1;
+ goto out;
+ }
+ if (fn && fn(tdb, key, dbuf, private)) {
+ /* They want us to terminate traversal */
+ ret = count;
+ if (unlock_record(tdb, tl.off) != 0) {
+ TDB_LOG((tdb, 0, "tdb_traverse: unlock_record failed!\n"));;
+ ret = -1;
+ }
+ tdb->travlocks.next = tl.next;
+ SAFE_FREE(key.dptr);
+ return count;
+ }
+ SAFE_FREE(key.dptr);
+ }
+out:
+ tdb->travlocks.next = tl.next;
+ if (ret < 0)
+ return -1;
+ else
+ return count;
+}
+
+/* find the first entry in the database and return its key */
+TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb)
+{
+ TDB_DATA key;
+ struct list_struct rec;
+
+ /* release any old lock */
+ if (unlock_record(tdb, tdb->travlocks.off) != 0)
+ return tdb_null;
+ tdb->travlocks.off = tdb->travlocks.hash = 0;
+
+ if (tdb_next_lock(tdb, &tdb->travlocks, &rec) <= 0)
+ return tdb_null;
+ /* now read the key */
+ key.dsize = rec.key_len;
+ key.dptr =tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),key.dsize);
+ if (tdb_unlock(tdb, BUCKET(tdb->travlocks.hash), F_WRLCK) != 0)
+ TDB_LOG((tdb, 0, "tdb_firstkey: error occurred while tdb_unlocking!\n"));
+ return key;
+}
+
+/* find the next entry in the database, returning its key */
+TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA oldkey)
+{
+ u32 oldhash;
+ TDB_DATA key = tdb_null;
+ struct list_struct rec;
+ char *k = NULL;
+
+ /* Is locked key the old key? If so, traverse will be reliable. */
+ if (tdb->travlocks.off) {
+ if (tdb_lock(tdb,tdb->travlocks.hash,F_WRLCK))
+ return tdb_null;
+ if (rec_read(tdb, tdb->travlocks.off, &rec) == -1
+ || !(k = tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),
+ rec.key_len))
+ || memcmp(k, oldkey.dptr, oldkey.dsize) != 0) {
+ /* No, it wasn't: unlock it and start from scratch */
+ if (unlock_record(tdb, tdb->travlocks.off) != 0)
+ return tdb_null;
+ if (tdb_unlock(tdb, tdb->travlocks.hash, F_WRLCK) != 0)
+ return tdb_null;
+ tdb->travlocks.off = 0;
+ }
+
+ SAFE_FREE(k);
+ }
+
+ if (!tdb->travlocks.off) {
+ /* No previous element: do normal find, and lock record */
+ tdb->travlocks.off = tdb_find_lock_hash(tdb, oldkey, tdb_hash(&oldkey), F_WRLCK, &rec);
+ if (!tdb->travlocks.off)
+ return tdb_null;
+ tdb->travlocks.hash = BUCKET(rec.full_hash);
+ if (lock_record(tdb, tdb->travlocks.off) != 0) {
+ TDB_LOG((tdb, 0, "tdb_nextkey: lock_record failed (%s)!\n", strerror(errno)));
+ return tdb_null;
+ }
+ }
+ oldhash = tdb->travlocks.hash;
+
+ /* Grab next record: locks chain and returned record,
+ unlocks old record */
+ if (tdb_next_lock(tdb, &tdb->travlocks, &rec) > 0) {
+ key.dsize = rec.key_len;
+ key.dptr = tdb_alloc_read(tdb, tdb->travlocks.off+sizeof(rec),
+ key.dsize);
+ /* Unlock the chain of this new record */
+ if (tdb_unlock(tdb, tdb->travlocks.hash, F_WRLCK) != 0)
+ TDB_LOG((tdb, 0, "tdb_nextkey: WARNING tdb_unlock failed!\n"));
+ }
+ /* Unlock the chain of old record */
+ if (tdb_unlock(tdb, BUCKET(oldhash), F_WRLCK) != 0)
+ TDB_LOG((tdb, 0, "tdb_nextkey: WARNING tdb_unlock failed!\n"));
+ return key;
+}
+
+/* delete an entry in the database given a key */
+static int tdb_delete_hash(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash)
+{
+ tdb_off rec_ptr;
+ struct list_struct rec;
+ int ret;
+
+ if (!(rec_ptr = tdb_find_lock_hash(tdb, key, hash, F_WRLCK, &rec)))
+ return -1;
+ ret = do_delete(tdb, rec_ptr, &rec);
+ if (tdb_unlock(tdb, BUCKET(rec.full_hash), F_WRLCK) != 0)
+ TDB_LOG((tdb, 0, "tdb_delete: WARNING tdb_unlock failed!\n"));
+ return ret;
+}
+
+int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+ u32 hash = tdb_hash(&key);
+ return tdb_delete_hash(tdb, key, hash);
+}
+
+/* store an element in the database, replacing any existing element
+ with the same key
+
+ return 0 on success, -1 on failure
+*/
+int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
+{
+ struct list_struct rec;
+ u32 hash;
+ tdb_off rec_ptr;
+ char *p = NULL;
+ int ret = 0;
+
+ /* find which hash bucket it is in */
+ hash = tdb_hash(&key);
+ if (!tdb_keylocked(tdb, hash))
+ return -1;
+ if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
+ return -1;
+
+ /* check for it existing, on insert. */
+ if (flag == TDB_INSERT) {
+ if (tdb_exists_hash(tdb, key, hash)) {
+ tdb->ecode = TDB_ERR_EXISTS;
+ goto fail;
+ }
+ } else {
+ /* first try in-place update, on modify or replace. */
+ if (tdb_update_hash(tdb, key, hash, dbuf) == 0)
+ goto out;
+ if (tdb->ecode == TDB_ERR_NOEXIST &&
+ flag == TDB_MODIFY) {
+ /* if the record doesn't exist and we are in TDB_MODIFY mode then
+ we should fail the store */
+ goto fail;
+ }
+ }
+ /* reset the error code potentially set by the tdb_update() */
+ tdb->ecode = TDB_SUCCESS;
+
+ /* delete any existing record - if it doesn't exist we don't
+ care. Doing this first reduces fragmentation, and avoids
+ coalescing with `allocated' block before it's updated. */
+ if (flag != TDB_INSERT)
+ tdb_delete_hash(tdb, key, hash);
+
+ /* Copy key+value *before* allocating free space in case malloc
+ fails and we are left with a dead spot in the tdb. */
+
+ if (!(p = (char *)malloc(key.dsize + dbuf.dsize))) {
+ tdb->ecode = TDB_ERR_OOM;
+ goto fail;
+ }
+
+ memcpy(p, key.dptr, key.dsize);
+ if (dbuf.dsize)
+ memcpy(p+key.dsize, dbuf.dptr, dbuf.dsize);
+
+ /* we have to allocate some space */
+ if (!(rec_ptr = tdb_allocate(tdb, key.dsize + dbuf.dsize, &rec)))
+ goto fail;
+
+ /* Read hash top into next ptr */
+ if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec.next) == -1)
+ goto fail;
+
+ rec.key_len = key.dsize;
+ rec.data_len = dbuf.dsize;
+ rec.full_hash = hash;
+ rec.magic = TDB_MAGIC;
+
+ /* write out and point the top of the hash chain at it */
+ if (rec_write(tdb, rec_ptr, &rec) == -1
+ || tdb_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+dbuf.dsize)==-1
+ || ofs_write(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) {
+ /* Need to tdb_unallocate() here */
+ goto fail;
+ }
+ out:
+ SAFE_FREE(p);
+ tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
+ return ret;
+fail:
+ ret = -1;
+ goto out;
+}
+
+/* Attempt to append data to an entry in place - this only works if the new data size
+ is <= the old data size and the key exists.
+ on failure return -1. Record must be locked before calling.
+*/
+static int tdb_append_inplace(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash, TDB_DATA new_dbuf)
+{
+ struct list_struct rec;
+ tdb_off rec_ptr;
+
+ /* find entry */
+ if (!(rec_ptr = tdb_find(tdb, key, hash, &rec)))
+ return -1;
+
+ /* Append of 0 is always ok. */
+ if (new_dbuf.dsize == 0)
+ return 0;
+
+ /* must be long enough for key, old data + new data and tailer */
+ if (rec.rec_len < key.dsize + rec.data_len + new_dbuf.dsize + sizeof(tdb_off)) {
+ /* No room. */
+ tdb->ecode = TDB_SUCCESS; /* Not really an error */
+ return -1;
+ }
+
+ if (tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len + rec.data_len,
+ new_dbuf.dptr, new_dbuf.dsize) == -1)
+ return -1;
+
+ /* update size */
+ rec.data_len += new_dbuf.dsize;
+ return rec_write(tdb, rec_ptr, &rec);
+}
+
+/* Append to an entry. Create if not exist. */
+
+int tdb_append(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA new_dbuf)
+{
+ struct list_struct rec;
+ u32 hash;
+ tdb_off rec_ptr;
+ char *p = NULL;
+ int ret = 0;
+ size_t new_data_size = 0;
+
+ /* find which hash bucket it is in */
+ hash = tdb_hash(&key);
+ if (!tdb_keylocked(tdb, hash))
+ return -1;
+ if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
+ return -1;
+
+ /* first try in-place. */
+ if (tdb_append_inplace(tdb, key, hash, new_dbuf) == 0)
+ goto out;
+
+ /* reset the error code potentially set by the tdb_append_inplace() */
+ tdb->ecode = TDB_SUCCESS;
+
+ /* find entry */
+ if (!(rec_ptr = tdb_find(tdb, key, hash, &rec))) {
+ if (tdb->ecode != TDB_ERR_NOEXIST)
+ goto fail;
+
+ /* Not found - create. */
+
+ ret = tdb_store(tdb, key, new_dbuf, TDB_INSERT);
+ goto out;
+ }
+
+ new_data_size = rec.data_len + new_dbuf.dsize;
+
+ /* Copy key+old_value+value *before* allocating free space in case malloc
+ fails and we are left with a dead spot in the tdb. */
+
+ if (!(p = (char *)malloc(key.dsize + new_data_size))) {
+ tdb->ecode = TDB_ERR_OOM;
+ goto fail;
+ }
+
+ /* Copy the key in place. */
+ memcpy(p, key.dptr, key.dsize);
+
+ /* Now read the old data into place. */
+ if (rec.data_len &&
+ tdb_read(tdb, rec_ptr + sizeof(rec) + rec.key_len, p + key.dsize, rec.data_len, 0) == -1)
+ goto fail;
+
+ /* Finally append the new data. */
+ if (new_dbuf.dsize)
+ memcpy(p+key.dsize+rec.data_len, new_dbuf.dptr, new_dbuf.dsize);
+
+ /* delete any existing record - if it doesn't exist we don't
+ care. Doing this first reduces fragmentation, and avoids
+ coalescing with `allocated' block before it's updated. */
+
+ tdb_delete_hash(tdb, key, hash);
+
+ if (!(rec_ptr = tdb_allocate(tdb, key.dsize + new_data_size, &rec)))
+ goto fail;
+
+ /* Read hash top into next ptr */
+ if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec.next) == -1)
+ goto fail;
+
+ rec.key_len = key.dsize;
+ rec.data_len = new_data_size;
+ rec.full_hash = hash;
+ rec.magic = TDB_MAGIC;
+
+ /* write out and point the top of the hash chain at it */
+ if (rec_write(tdb, rec_ptr, &rec) == -1
+ || tdb_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+new_data_size)==-1
+ || ofs_write(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) {
+ /* Need to tdb_unallocate() here */
+ goto fail;
+ }
+
+ out:
+ SAFE_FREE(p);
+ tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
+ return ret;
+
+fail:
+ ret = -1;
+ goto out;
+}
+
+static int tdb_already_open(dev_t device,
+ ino_t ino)
+{
+ TDB_CONTEXT *i;
+
+ for (i = tdbs; i; i = i->next) {
+ if (i->device == device && i->inode == ino) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* open the database, creating it if necessary
+
+ The open_flags and mode are passed straight to the open call on the
+ database file. A flags value of O_WRONLY is invalid. The hash size
+ is advisory, use zero for a default value.
+
+ Return is NULL on error, in which case errno is also set. Don't
+ try to call tdb_error or tdb_errname, just do strerror(errno).
+
+ @param name may be NULL for internal databases. */
+TDB_CONTEXT *tdb_open(const char *name, int hash_size, int tdb_flags,
+ int open_flags, mode_t mode)
+{
+ return tdb_open_ex(name, hash_size, tdb_flags, open_flags, mode, NULL);
+}
+
+
+TDB_CONTEXT *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
+ int open_flags, mode_t mode,
+ tdb_log_func log_fn)
+{
+ TDB_CONTEXT *tdb;
+ struct stat st;
+ int rev = 0, locked = 0;
+ unsigned char *vp;
+ u32 vertest;
+
+ if (!(tdb = calloc(1, sizeof *tdb))) {
+ /* Can't log this */
+ errno = ENOMEM;
+ goto fail;
+ }
+ tdb->fd = -1;
+ tdb->name = NULL;
+ tdb->map_ptr = NULL;
+ tdb->lockedkeys = NULL;
+ tdb->flags = tdb_flags;
+ tdb->open_flags = open_flags;
+ tdb->log_fn = log_fn;
+
+ if ((open_flags & O_ACCMODE) == O_WRONLY) {
+ TDB_LOG((tdb, 0, "tdb_open_ex: can't open tdb %s write-only\n",
+ name));
+ errno = EINVAL;
+ goto fail;
+ }
+
+ if (hash_size == 0)
+ hash_size = DEFAULT_HASH_SIZE;
+ if ((open_flags & O_ACCMODE) == O_RDONLY) {
+ tdb->read_only = 1;
+ /* read only databases don't do locking or clear if first */
+ tdb->flags |= TDB_NOLOCK;
+ tdb->flags &= ~TDB_CLEAR_IF_FIRST;
+ }
+
+ /* internal databases don't mmap or lock, and start off cleared */
+ if (tdb->flags & TDB_INTERNAL) {
+ tdb->flags |= (TDB_NOLOCK | TDB_NOMMAP);
+ tdb->flags &= ~TDB_CLEAR_IF_FIRST;
+ if (tdb_new_database(tdb, hash_size) != 0) {
+ TDB_LOG((tdb, 0, "tdb_open_ex: tdb_new_database failed!"));
+ goto fail;
+ }
+ goto internal;
+ }
+
+ if ((tdb->fd = open(name, open_flags, mode)) == -1) {
+ TDB_LOG((tdb, 5, "tdb_open_ex: could not open file %s: %s\n",
+ name, strerror(errno)));
+ goto fail; /* errno set by open(2) */
+ }
+
+ /* ensure there is only one process initialising at once */
+ if (tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0) == -1) {
+ TDB_LOG((tdb, 0, "tdb_open_ex: failed to get global lock on %s: %s\n",
+ name, strerror(errno)));
+ goto fail; /* errno set by tdb_brlock */
+ }
+
+ /* we need to zero database if we are the only one with it open */
+ if ((tdb_flags & TDB_CLEAR_IF_FIRST) &&
+ (locked = (tdb_brlock(tdb, ACTIVE_LOCK, F_WRLCK, F_SETLK, 0) == 0))) {
+ open_flags |= O_CREAT;
+ if (ftruncate(tdb->fd, 0) == -1) {
+ TDB_LOG((tdb, 0, "tdb_open_ex: "
+ "failed to truncate %s: %s\n",
+ name, strerror(errno)));
+ goto fail; /* errno set by ftruncate */
+ }
+ }
+
+ if (read(tdb->fd, &tdb->header, sizeof(tdb->header)) != sizeof(tdb->header)
+ || strcmp(tdb->header.magic_food, TDB_MAGIC_FOOD) != 0
+ || (tdb->header.version != TDB_VERSION
+ && !(rev = (tdb->header.version==TDB_BYTEREV(TDB_VERSION))))) {
+ /* its not a valid database - possibly initialise it */
+ if (!(open_flags & O_CREAT) || tdb_new_database(tdb, hash_size) == -1) {
+ errno = EIO; /* ie bad format or something */
+ goto fail;
+ }
+ rev = (tdb->flags & TDB_CONVERT);
+ }
+ vp = (unsigned char *)&tdb->header.version;
+ vertest = (((u32)vp[0]) << 24) | (((u32)vp[1]) << 16) |
+ (((u32)vp[2]) << 8) | (u32)vp[3];
+ tdb->flags |= (vertest==TDB_VERSION) ? TDB_BIGENDIAN : 0;
+ if (!rev)
+ tdb->flags &= ~TDB_CONVERT;
+ else {
+ tdb->flags |= TDB_CONVERT;
+ convert(&tdb->header, sizeof(tdb->header));
+ }
+ if (fstat(tdb->fd, &st) == -1)
+ goto fail;
+
+ /* Is it already in the open list? If so, fail. */
+ if (tdb_already_open(st.st_dev, st.st_ino)) {
+ TDB_LOG((tdb, 2, "tdb_open_ex: "
+ "%s (%d,%d) is already open in this process\n",
+ name, st.st_dev, st.st_ino));
+ errno = EBUSY;
+ goto fail;
+ }
+
+ if (!(tdb->name = (char *)strdup(name))) {
+ errno = ENOMEM;
+ goto fail;
+ }
+
+ tdb->map_size = st.st_size;
+ tdb->device = st.st_dev;
+ tdb->inode = st.st_ino;
+ tdb->locked = calloc(tdb->header.hash_size+1, sizeof(tdb->locked[0]));
+ if (!tdb->locked) {
+ TDB_LOG((tdb, 2, "tdb_open_ex: "
+ "failed to allocate lock structure for %s\n",
+ name));
+ errno = ENOMEM;
+ goto fail;
+ }
+ tdb_mmap(tdb);
+ if (locked) {
+ if (!tdb->read_only)
+ if (tdb_clear_spinlocks(tdb) != 0) {
+ TDB_LOG((tdb, 0, "tdb_open_ex: "
+ "failed to clear spinlock\n"));
+ goto fail;
+ }
+ if (tdb_brlock(tdb, ACTIVE_LOCK, F_UNLCK, F_SETLK, 0) == -1) {
+ TDB_LOG((tdb, 0, "tdb_open_ex: "
+ "failed to take ACTIVE_LOCK on %s: %s\n",
+ name, strerror(errno)));
+ goto fail;
+ }
+
+ }
+
+ /* We always need to do this if the CLEAR_IF_FIRST flag is set, even if
+ we didn't get the initial exclusive lock as we need to let all other
+ users know we're using it. */
+
+ if (tdb_flags & TDB_CLEAR_IF_FIRST) {
+ /* leave this lock in place to indicate it's in use */
+ if (tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0) == -1)
+ goto fail;
+ }
+
+
+ internal:
+ /* Internal (memory-only) databases skip all the code above to
+ * do with disk files, and resume here by releasing their
+ * global lock and hooking into the active list. */
+ if (tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0) == -1)
+ goto fail;
+ tdb->next = tdbs;
+ tdbs = tdb;
+ return tdb;
+
+ fail:
+ { int save_errno = errno;
+
+ if (!tdb)
+ return NULL;
+
+ if (tdb->map_ptr) {
+ if (tdb->flags & TDB_INTERNAL)
+ SAFE_FREE(tdb->map_ptr);
+ else
+ tdb_munmap(tdb);
+ }
+ SAFE_FREE(tdb->name);
+ if (tdb->fd != -1)
+ if (close(tdb->fd) != 0)
+ TDB_LOG((tdb, 5, "tdb_open_ex: failed to close tdb->fd on error!\n"));
+ SAFE_FREE(tdb->locked);
+ SAFE_FREE(tdb);
+ errno = save_errno;
+ return NULL;
+ }
+}
+
+/**
+ * Close a database.
+ *
+ * @returns -1 for error; 0 for success.
+ **/
+int tdb_close(TDB_CONTEXT *tdb)
+{
+ TDB_CONTEXT **i;
+ int ret = 0;
+
+ if (tdb->map_ptr) {
+ if (tdb->flags & TDB_INTERNAL)
+ SAFE_FREE(tdb->map_ptr);
+ else
+ tdb_munmap(tdb);
+ }
+ SAFE_FREE(tdb->name);
+ if (tdb->fd != -1)
+ ret = close(tdb->fd);
+ SAFE_FREE(tdb->locked);
+ SAFE_FREE(tdb->lockedkeys);
+
+ /* Remove from contexts list */
+ for (i = &tdbs; *i; i = &(*i)->next) {
+ if (*i == tdb) {
+ *i = tdb->next;
+ break;
+ }
+ }
+
+ memset(tdb, 0, sizeof(*tdb));
+ SAFE_FREE(tdb);
+
+ return ret;
+}
+
+/* lock/unlock entire database */
+int tdb_lockall(TDB_CONTEXT *tdb)
+{
+ u32 i;
+
+ /* There are no locks on read-only dbs */
+ if (tdb->read_only)
+ return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+ if (tdb->lockedkeys)
+ return TDB_ERRCODE(TDB_ERR_NOLOCK, -1);
+ for (i = 0; i < tdb->header.hash_size; i++)
+ if (tdb_lock(tdb, i, F_WRLCK))
+ break;
+
+ /* If error, release locks we have... */
+ if (i < tdb->header.hash_size) {
+ u32 j;
+
+ for ( j = 0; j < i; j++)
+ tdb_unlock(tdb, j, F_WRLCK);
+ return TDB_ERRCODE(TDB_ERR_NOLOCK, -1);
+ }
+
+ return 0;
+}
+void tdb_unlockall(TDB_CONTEXT *tdb)
+{
+ u32 i;
+ for (i=0; i < tdb->header.hash_size; i++)
+ tdb_unlock(tdb, i, F_WRLCK);
+}
+
+int tdb_lockkeys(TDB_CONTEXT *tdb, u32 number, TDB_DATA keys[])
+{
+ u32 i, j, hash;
+
+ /* Can't lock more keys if already locked */
+ if (tdb->lockedkeys)
+ return TDB_ERRCODE(TDB_ERR_NOLOCK, -1);
+ if (!(tdb->lockedkeys = malloc(sizeof(u32) * (number+1))))
+ return TDB_ERRCODE(TDB_ERR_OOM, -1);
+ /* First number in array is # keys */
+ tdb->lockedkeys[0] = number;
+
+ /* Insertion sort by bucket */
+ for (i = 0; i < number; i++) {
+ hash = tdb_hash(&keys[i]);
+ for (j = 0; j < i && BUCKET(tdb->lockedkeys[j+1]) < BUCKET(hash); j++);
+ memmove(&tdb->lockedkeys[j+2], &tdb->lockedkeys[j+1], sizeof(u32) * (i-j));
+ tdb->lockedkeys[j+1] = hash;
+ }
+ /* Finally, lock in order */
+ for (i = 0; i < number; i++)
+ if (tdb_lock(tdb, i, F_WRLCK))
+ break;
+
+ /* If error, release locks we have... */
+ if (i < number) {
+ for ( j = 0; j < i; j++)
+ tdb_unlock(tdb, j, F_WRLCK);
+ SAFE_FREE(tdb->lockedkeys);
+ return TDB_ERRCODE(TDB_ERR_NOLOCK, -1);
+ }
+ return 0;
+}
+
+/* Unlock the keys previously locked by tdb_lockkeys() */
+void tdb_unlockkeys(TDB_CONTEXT *tdb)
+{
+ u32 i;
+ if (!tdb->lockedkeys)
+ return;
+ for (i = 0; i < tdb->lockedkeys[0]; i++)
+ tdb_unlock(tdb, tdb->lockedkeys[i+1], F_WRLCK);
+ SAFE_FREE(tdb->lockedkeys);
+}
+
+/* lock/unlock one hash chain. This is meant to be used to reduce
+ contention - it cannot guarantee how many records will be locked */
+int tdb_chainlock(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+ return tdb_lock(tdb, BUCKET(tdb_hash(&key)), F_WRLCK);
+}
+
+int tdb_chainunlock(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+ return tdb_unlock(tdb, BUCKET(tdb_hash(&key)), F_WRLCK);
+}
+
+int tdb_chainlock_read(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+ return tdb_lock(tdb, BUCKET(tdb_hash(&key)), F_RDLCK);
+}
+
+int tdb_chainunlock_read(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+ return tdb_unlock(tdb, BUCKET(tdb_hash(&key)), F_RDLCK);
+}
+
+
+/* register a loging function */
+void tdb_logging_function(TDB_CONTEXT *tdb, void (*fn)(TDB_CONTEXT *, int , const char *, ...))
+{
+ tdb->log_fn = fn;
+}
+
+
+/* reopen a tdb - this can be used after a fork to ensure that we have an independent
+ seek pointer from our parent and to re-establish locks */
+int tdb_reopen(TDB_CONTEXT *tdb)
+{
+ struct stat st;
+
+ if (tdb->flags & TDB_INTERNAL)
+ return 0; /* Nothing to do. */
+ if (tdb_munmap(tdb) != 0) {
+ TDB_LOG((tdb, 0, "tdb_reopen: munmap failed (%s)\n", strerror(errno)));
+ goto fail;
+ }
+ if (close(tdb->fd) != 0)
+ TDB_LOG((tdb, 0, "tdb_reopen: WARNING closing tdb->fd failed!\n"));
+ tdb->fd = open(tdb->name, tdb->open_flags & ~(O_CREAT|O_TRUNC), 0);
+ if (tdb->fd == -1) {
+ TDB_LOG((tdb, 0, "tdb_reopen: open failed (%s)\n", strerror(errno)));
+ goto fail;
+ }
+ if (fstat(tdb->fd, &st) != 0) {
+ TDB_LOG((tdb, 0, "tdb_reopen: fstat failed (%s)\n", strerror(errno)));
+ goto fail;
+ }
+ if (st.st_ino != tdb->inode || st.st_dev != tdb->device) {
+ TDB_LOG((tdb, 0, "tdb_reopen: file dev/inode has changed!\n"));
+ goto fail;
+ }
+ tdb_mmap(tdb);
+ if ((tdb->flags & TDB_CLEAR_IF_FIRST) && (tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0) == -1)) {
+ TDB_LOG((tdb, 0, "tdb_reopen: failed to obtain active lock\n"));
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ tdb_close(tdb);
+ return -1;
+}
+
+/* reopen all tdb's */
+int tdb_reopen_all(void)
+{
+ TDB_CONTEXT *tdb;
+
+ for (tdb=tdbs; tdb; tdb = tdb->next) {
+ /* Ensure no clear-if-first. */
+ tdb->flags &= ~TDB_CLEAR_IF_FIRST;
+ if (tdb_reopen(tdb) != 0)
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/source/tdb/tdb.h b/source/tdb/tdb.h
new file mode 100644
index 00000000000..eb120a8cecd
--- /dev/null
+++ b/source/tdb/tdb.h
@@ -0,0 +1,151 @@
+#ifndef __TDB_H__
+#define __TDB_H__
+
+/*
+ Unix SMB/CIFS implementation.
+
+ trivial database library
+
+ Copyright (C) Andrew Tridgell 1999-2004
+
+ ** NOTE! The following LGPL license applies to the tdb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* flags to tdb_store() */
+#define TDB_REPLACE 1
+#define TDB_INSERT 2
+#define TDB_MODIFY 3
+
+/* flags for tdb_open() */
+#define TDB_DEFAULT 0 /* just a readability place holder */
+#define TDB_CLEAR_IF_FIRST 1
+#define TDB_INTERNAL 2 /* don't store on disk */
+#define TDB_NOLOCK 4 /* don't do any locking */
+#define TDB_NOMMAP 8 /* don't use mmap */
+#define TDB_CONVERT 16 /* convert endian (internal use) */
+#define TDB_BIGENDIAN 32 /* header is big-endian (internal use) */
+
+#define TDB_ERRCODE(code, ret) ((tdb->ecode = (code)), ret)
+
+/* error codes */
+enum TDB_ERROR {TDB_SUCCESS=0, TDB_ERR_CORRUPT, TDB_ERR_IO, TDB_ERR_LOCK,
+ TDB_ERR_OOM, TDB_ERR_EXISTS, TDB_ERR_NOLOCK, TDB_ERR_LOCK_TIMEOUT,
+ TDB_ERR_NOEXIST};
+
+#ifndef u32
+#define u32 unsigned
+#endif
+
+typedef struct {
+ char *dptr;
+ size_t dsize;
+} TDB_DATA;
+
+typedef u32 tdb_len;
+typedef u32 tdb_off;
+
+/* this is stored at the front of every database */
+struct tdb_header {
+ char magic_food[32]; /* for /etc/magic */
+ u32 version; /* version of the code */
+ u32 hash_size; /* number of hash entries */
+ tdb_off rwlocks;
+ tdb_off reserved[31];
+};
+
+struct tdb_lock_type {
+ u32 count;
+ u32 ltype;
+};
+
+struct tdb_traverse_lock {
+ struct tdb_traverse_lock *next;
+ u32 off;
+ u32 hash;
+};
+
+/* this is the context structure that is returned from a db open */
+typedef struct tdb_context {
+ char *name; /* the name of the database */
+ void *map_ptr; /* where it is currently mapped */
+ int fd; /* open file descriptor for the database */
+ tdb_len map_size; /* how much space has been mapped */
+ int read_only; /* opened read-only */
+ struct tdb_lock_type *locked; /* array of chain locks */
+ enum TDB_ERROR ecode; /* error code for last tdb error */
+ struct tdb_header header; /* a cached copy of the header */
+ u32 flags; /* the flags passed to tdb_open */
+ u32 *lockedkeys; /* array of locked keys: first is #keys */
+ struct tdb_traverse_lock travlocks; /* current traversal locks */
+ struct tdb_context *next; /* all tdbs to avoid multiple opens */
+ dev_t device; /* uniquely identifies this tdb */
+ ino_t inode; /* uniquely identifies this tdb */
+ void (*log_fn)(struct tdb_context *tdb, int level, const char *, ...); /* logging function */
+ int open_flags; /* flags used in the open - needed by reopen */
+} TDB_CONTEXT;
+
+typedef int (*tdb_traverse_func)(TDB_CONTEXT *, TDB_DATA, TDB_DATA, void *);
+typedef void (*tdb_log_func)(TDB_CONTEXT *, int , const char *, ...);
+
+TDB_CONTEXT *tdb_open(const char *name, int hash_size, int tdb_flags,
+ int open_flags, mode_t mode);
+TDB_CONTEXT *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
+ int open_flags, mode_t mode,
+ tdb_log_func log_fn);
+
+int tdb_reopen(TDB_CONTEXT *tdb);
+int tdb_reopen_all(void);
+void tdb_logging_function(TDB_CONTEXT *tdb, tdb_log_func);
+enum TDB_ERROR tdb_error(TDB_CONTEXT *tdb);
+const char *tdb_errorstr(TDB_CONTEXT *tdb);
+TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag);
+int tdb_append(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA new_dbuf);
+int tdb_close(TDB_CONTEXT *tdb);
+TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb);
+TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_traverse(TDB_CONTEXT *tdb, tdb_traverse_func fn, void *);
+int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_lockkeys(TDB_CONTEXT *tdb, u32 number, TDB_DATA keys[]);
+void tdb_unlockkeys(TDB_CONTEXT *tdb);
+int tdb_lockall(TDB_CONTEXT *tdb);
+void tdb_unlockall(TDB_CONTEXT *tdb);
+
+/* Low level locking functions: use with care */
+void tdb_set_lock_alarm(sig_atomic_t *palarm);
+int tdb_chainlock(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_chainunlock(TDB_CONTEXT *tdb, TDB_DATA key);
+
+/* Debug functions. Not used in production. */
+void tdb_dump_all(TDB_CONTEXT *tdb);
+int tdb_printfreelist(TDB_CONTEXT *tdb);
+
+extern TDB_DATA tdb_null;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* tdb.h */
diff --git a/source/tdb/tdb.magic b/source/tdb/tdb.magic
new file mode 100644
index 00000000000..f5619e7327e
--- /dev/null
+++ b/source/tdb/tdb.magic
@@ -0,0 +1,10 @@
+# Magic file(1) information about tdb files.
+#
+# Install this into /etc/magic or the corresponding location for your
+# system, or pass as a -m argument to file(1).
+
+# You may use and redistribute this file without restriction.
+
+0 string TDB\ file TDB database
+>32 lelong =0x2601196D version 6, little-endian
+>>36 lelong x hash size %d bytes
diff --git a/source/tdb/tdbback.c b/source/tdb/tdbback.c
new file mode 100644
index 00000000000..68b6fadc882
--- /dev/null
+++ b/source/tdb/tdbback.c
@@ -0,0 +1,211 @@
+/*
+ Unix SMB/CIFS implementation.
+ low level tdb backup and restore utility
+ Copyright (C) Andrew Tridgell 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifdef STANDALONE
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/mman.h>
+
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ctype.h>
+#include <signal.h>
+
+#else
+#include "includes.h"
+#endif
+
+#include "tdb.h"
+
+static int failed;
+
+char *add_suffix(const char *name, const char *suffix)
+{
+ char *ret;
+ int len = strlen(name) + strlen(suffix) + 1;
+ ret = malloc(len);
+ if (!ret) {
+ fprintf(stderr,"Out of memory!\n");
+ exit(1);
+ }
+ snprintf(ret, len, "%s%s", name, suffix);
+ return ret;
+}
+
+static int copy_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+ TDB_CONTEXT *tdb_new = (TDB_CONTEXT *)state;
+
+ if (tdb_store(tdb_new, key, dbuf, TDB_INSERT) != 0) {
+ fprintf(stderr,"Failed to insert into %s\n", tdb_new->name);
+ failed = 1;
+ return 1;
+ }
+ return 0;
+}
+
+
+static int test_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+ return 0;
+}
+
+/*
+ carefully backup a tdb, validating the contents and
+ only doing the backup if its OK
+ this function is also used for restore
+*/
+int backup_tdb(const char *old_name, const char *new_name)
+{
+ TDB_CONTEXT *tdb;
+ TDB_CONTEXT *tdb_new;
+ char *tmp_name;
+ struct stat st;
+ int count1, count2;
+
+ tmp_name = add_suffix(new_name, ".tmp");
+
+ /* stat the old tdb to find its permissions */
+ if (stat(old_name, &st) != 0) {
+ perror(old_name);
+ return 1;
+ }
+
+ /* open the old tdb */
+ tdb = tdb_open(old_name, 0, 0, O_RDWR, 0);
+ if (!tdb) {
+ printf("Failed to open %s\n", old_name);
+ return 1;
+ }
+
+ /* create the new tdb */
+ unlink(tmp_name);
+ tdb_new = tdb_open(tmp_name, tdb->header.hash_size,
+ TDB_DEFAULT, O_RDWR|O_CREAT|O_EXCL,
+ st.st_mode & 0777);
+ if (!tdb_new) {
+ perror(tmp_name);
+ free(tmp_name);
+ return 1;
+ }
+
+ /* lock the old tdb */
+ if (tdb_lockall(tdb) != 0) {
+ fprintf(stderr,"Failed to lock %s\n", old_name);
+ tdb_close(tdb);
+ tdb_close(tdb_new);
+ unlink(tmp_name);
+ free(tmp_name);
+ return 1;
+ }
+
+ failed = 0;
+
+ /* traverse and copy */
+ count1 = tdb_traverse(tdb, copy_fn, (void *)tdb_new);
+ if (count1 < 0 || failed) {
+ fprintf(stderr,"failed to copy %s\n", old_name);
+ tdb_close(tdb);
+ tdb_close(tdb_new);
+ unlink(tmp_name);
+ free(tmp_name);
+ return 1;
+ }
+
+ /* close the old tdb */
+ tdb_close(tdb);
+
+ /* close the new tdb and re-open read-only */
+ tdb_close(tdb_new);
+ tdb_new = tdb_open(tmp_name, 0, TDB_DEFAULT, O_RDONLY, 0);
+ if (!tdb_new) {
+ fprintf(stderr,"failed to reopen %s\n", tmp_name);
+ unlink(tmp_name);
+ perror(tmp_name);
+ free(tmp_name);
+ return 1;
+ }
+
+ /* traverse the new tdb to confirm */
+ count2 = tdb_traverse(tdb_new, test_fn, 0);
+ if (count2 != count1) {
+ fprintf(stderr,"failed to copy %s\n", old_name);
+ tdb_close(tdb_new);
+ unlink(tmp_name);
+ free(tmp_name);
+ return 1;
+ }
+
+ /* make sure the new tdb has reached stable storage */
+ fsync(tdb_new->fd);
+
+ /* close the new tdb and rename it to .bak */
+ tdb_close(tdb_new);
+ unlink(new_name);
+ if (rename(tmp_name, new_name) != 0) {
+ perror(new_name);
+ free(tmp_name);
+ return 1;
+ }
+
+ free(tmp_name);
+
+ return 0;
+}
+
+
+
+/*
+ verify a tdb and if it is corrupt then restore from *.bak
+*/
+int verify_tdb(const char *fname, const char *bak_name)
+{
+ TDB_CONTEXT *tdb;
+ int count = -1;
+
+ /* open the tdb */
+ tdb = tdb_open(fname, 0, 0, O_RDONLY, 0);
+
+ /* traverse the tdb, then close it */
+ if (tdb) {
+ count = tdb_traverse(tdb, test_fn, NULL);
+ tdb_close(tdb);
+ }
+
+ /* count is < 0 means an error */
+ if (count < 0) {
+ printf("restoring %s\n", fname);
+ return backup_tdb(bak_name, fname);
+ }
+
+ printf("%s : %d records\n", fname, count);
+
+ return 0;
+}
diff --git a/source/tdb/tdbback.h b/source/tdb/tdbback.h
new file mode 100644
index 00000000000..7ebeaa494d6
--- /dev/null
+++ b/source/tdb/tdbback.h
@@ -0,0 +1,23 @@
+/*
+ Unix SMB/CIFS implementation.
+ low level tdb backup and restore utility
+ Copyright (C) Andrew Tridgell 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+char *add_suffix(const char *name, const char *suffix);
+int backup_tdb(const char *old_name, const char *new_name);
+int verify_tdb(const char *fname, const char *bak_name);
diff --git a/source/tdb/tdbbackup.c b/source/tdb/tdbbackup.c
new file mode 100644
index 00000000000..1a0e1c1588f
--- /dev/null
+++ b/source/tdb/tdbbackup.c
@@ -0,0 +1,149 @@
+/*
+ Unix SMB/CIFS implementation.
+ low level tdb backup and restore utility
+ Copyright (C) Andrew Tridgell 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+
+ This program is meant for backup/restore of tdb databases. Typical usage would be:
+ tdbbackup *.tdb
+ when Samba shuts down cleanly, which will make a backup of all the local databases
+ to *.bak files. Then on Samba startup you would use:
+ tdbbackup -v *.tdb
+ and this will check the databases for corruption and if corruption is detected then
+ the backup will be restored.
+
+ You may also like to do a backup on a regular basis while Samba is
+ running, perhaps using cron.
+
+ The reason this program is needed is to cope with power failures
+ while Samba is running. A power failure could lead to database
+ corruption and Samba will then not start correctly.
+
+ Note that many of the databases in Samba are transient and thus
+ don't need to be backed up, so you can optimise the above a little
+ by only running the backup on the critical databases.
+
+ */
+
+#ifdef STANDALONE
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ctype.h>
+#include <signal.h>
+
+#else
+
+#include "includes.h"
+
+#endif
+
+#include "tdb.h"
+#include "tdbback.h"
+
+/*
+ see if one file is newer than another
+*/
+static int file_newer(const char *fname1, const char *fname2)
+{
+ struct stat st1, st2;
+ if (stat(fname1, &st1) != 0) {
+ return 0;
+ }
+ if (stat(fname2, &st2) != 0) {
+ return 1;
+ }
+ return (st1.st_mtime > st2.st_mtime);
+}
+
+static void usage(void)
+{
+ printf("Usage: tdbbackup [options] <fname...>\n\n");
+ printf(" -h this help message\n");
+ printf(" -s suffix set the backup suffix\n");
+ printf(" -v verify mode (restore if corrupt)\n");
+}
+
+
+ int main(int argc, char *argv[])
+{
+ int i;
+ int ret = 0;
+ int c;
+ int verify = 0;
+ const char *suffix = ".bak";
+ extern int optind;
+ extern char *optarg;
+
+ while ((c = getopt(argc, argv, "vhs:")) != -1) {
+ switch (c) {
+ case 'h':
+ usage();
+ exit(0);
+ case 'v':
+ verify = 1;
+ break;
+ case 's':
+ suffix = optarg;
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ usage();
+ exit(1);
+ }
+
+ for (i=0; i<argc; i++) {
+ const char *fname = argv[i];
+ char *bak_name;
+
+ bak_name = add_suffix(fname, suffix);
+
+ if (verify) {
+ if (verify_tdb(fname, bak_name) != 0) {
+ ret = 1;
+ }
+ } else {
+ if (file_newer(fname, bak_name) &&
+ backup_tdb(fname, bak_name) != 0) {
+ ret = 1;
+ }
+ }
+
+ free(bak_name);
+ }
+
+ return ret;
+}
diff --git a/source/tdb/tdbdump.c b/source/tdb/tdbdump.c
new file mode 100644
index 00000000000..d2530f9f54c
--- /dev/null
+++ b/source/tdb/tdbdump.c
@@ -0,0 +1,88 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple tdb dump util
+ Copyright (C) Andrew Tridgell 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ctype.h>
+#include <signal.h>
+#include "tdb.h"
+
+static void print_data(TDB_DATA d)
+{
+ unsigned char *p = d.dptr;
+ int len = d.dsize;
+ while (len--) {
+ if (isprint(*p) && !strchr("\"\\", *p)) {
+ fputc(*p, stdout);
+ } else {
+ printf("\\%02X", *p);
+ }
+ p++;
+ }
+}
+
+static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+ printf("{\n");
+ printf("key = \"");
+ print_data(key);
+ printf("\"\n");
+ printf("data = \"");
+ print_data(dbuf);
+ printf("\"\n");
+ printf("}\n");
+ return 0;
+}
+
+static int dump_tdb(const char *fname)
+{
+ TDB_CONTEXT *tdb;
+
+ tdb = tdb_open(fname, 0, 0, O_RDONLY, 0);
+ if (!tdb) {
+ printf("Failed to open %s\n", fname);
+ return 1;
+ }
+
+ tdb_traverse(tdb, traverse_fn, NULL);
+ return 0;
+}
+
+ int main(int argc, char *argv[])
+{
+ char *fname;
+
+ if (argc < 2) {
+ printf("Usage: tdbdump <fname>\n");
+ exit(1);
+ }
+
+ fname = argv[1];
+
+ return dump_tdb(fname);
+}
diff --git a/source/tdb/tdbtest.c b/source/tdb/tdbtest.c
new file mode 100644
index 00000000000..89295a3291f
--- /dev/null
+++ b/source/tdb/tdbtest.c
@@ -0,0 +1,263 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <signal.h>
+#include "tdb.h"
+#include <gdbm.h>
+
+/* a test program for tdb - the trivial database */
+
+
+
+#define DELETE_PROB 7
+#define STORE_PROB 5
+
+static TDB_CONTEXT *db;
+static GDBM_FILE gdbm;
+
+struct timeval tp1,tp2;
+
+static void start_timer(void)
+{
+ gettimeofday(&tp1,NULL);
+}
+
+static double end_timer(void)
+{
+ gettimeofday(&tp2,NULL);
+ return((tp2.tv_sec - tp1.tv_sec) +
+ (tp2.tv_usec - tp1.tv_usec)*1.0e-6);
+}
+
+static void fatal(char *why)
+{
+ perror(why);
+ exit(1);
+}
+
+static void tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vfprintf(stdout, format, ap);
+ va_end(ap);
+ fflush(stdout);
+}
+
+static void compare_db(void)
+{
+ TDB_DATA d, key, nextkey;
+ datum gd, gkey, gnextkey;
+
+ key = tdb_firstkey(db);
+ while (key.dptr) {
+ d = tdb_fetch(db, key);
+ gkey.dptr = key.dptr;
+ gkey.dsize = key.dsize;
+
+ gd = gdbm_fetch(gdbm, gkey);
+
+ if (!gd.dptr) fatal("key not in gdbm");
+ if (gd.dsize != d.dsize) fatal("data sizes differ");
+ if (memcmp(gd.dptr, d.dptr, d.dsize)) {
+ fatal("data differs");
+ }
+
+ nextkey = tdb_nextkey(db, key);
+ free(key.dptr);
+ free(d.dptr);
+ free(gd.dptr);
+ key = nextkey;
+ }
+
+ gkey = gdbm_firstkey(gdbm);
+ while (gkey.dptr) {
+ gd = gdbm_fetch(gdbm, gkey);
+ key.dptr = gkey.dptr;
+ key.dsize = gkey.dsize;
+
+ d = tdb_fetch(db, key);
+
+ if (!d.dptr) fatal("key not in db");
+ if (d.dsize != gd.dsize) fatal("data sizes differ");
+ if (memcmp(d.dptr, gd.dptr, gd.dsize)) {
+ fatal("data differs");
+ }
+
+ gnextkey = gdbm_nextkey(gdbm, gkey);
+ free(gkey.dptr);
+ free(gd.dptr);
+ free(d.dptr);
+ gkey = gnextkey;
+ }
+}
+
+static char *randbuf(int len)
+{
+ char *buf;
+ int i;
+ buf = (char *)malloc(len+1);
+
+ for (i=0;i<len;i++) {
+ buf[i] = 'a' + (rand() % 26);
+ }
+ buf[i] = 0;
+ return buf;
+}
+
+static void addrec_db(void)
+{
+ int klen, dlen;
+ char *k, *d;
+ TDB_DATA key, data;
+
+ klen = 1 + (rand() % 4);
+ dlen = 1 + (rand() % 100);
+
+ k = randbuf(klen);
+ d = randbuf(dlen);
+
+ key.dptr = k;
+ key.dsize = klen+1;
+
+ data.dptr = d;
+ data.dsize = dlen+1;
+
+ if (rand() % DELETE_PROB == 0) {
+ tdb_delete(db, key);
+ } else if (rand() % STORE_PROB == 0) {
+ if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
+ fatal("tdb_store failed");
+ }
+ } else {
+ data = tdb_fetch(db, key);
+ if (data.dptr) free(data.dptr);
+ }
+
+ free(k);
+ free(d);
+}
+
+static void addrec_gdbm(void)
+{
+ int klen, dlen;
+ char *k, *d;
+ datum key, data;
+
+ klen = 1 + (rand() % 4);
+ dlen = 1 + (rand() % 100);
+
+ k = randbuf(klen);
+ d = randbuf(dlen);
+
+ key.dptr = k;
+ key.dsize = klen+1;
+
+ data.dptr = d;
+ data.dsize = dlen+1;
+
+ if (rand() % DELETE_PROB == 0) {
+ gdbm_delete(gdbm, key);
+ } else if (rand() % STORE_PROB == 0) {
+ if (gdbm_store(gdbm, key, data, GDBM_REPLACE) != 0) {
+ fatal("gdbm_store failed");
+ }
+ } else {
+ data = gdbm_fetch(gdbm, key);
+ if (data.dptr) free(data.dptr);
+ }
+
+ free(k);
+ free(d);
+}
+
+static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+#if 0
+ printf("[%s] [%s]\n", key.dptr, dbuf.dptr);
+#endif
+ tdb_delete(tdb, key);
+ return 0;
+}
+
+static void merge_test(void)
+{
+ int i;
+ char keys[5][2];
+ TDB_DATA key, data;
+
+ for (i = 0; i < 5; i++) {
+ sprintf(keys[i], "%d", i);
+ key.dptr = keys[i];
+ key.dsize = 2;
+
+ data.dptr = "test";
+ data.dsize = 4;
+
+ if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
+ fatal("tdb_store failed");
+ }
+ }
+
+ key.dptr = keys[0];
+ tdb_delete(db, key);
+ key.dptr = keys[4];
+ tdb_delete(db, key);
+ key.dptr = keys[2];
+ tdb_delete(db, key);
+ key.dptr = keys[1];
+ tdb_delete(db, key);
+ key.dptr = keys[3];
+ tdb_delete(db, key);
+}
+
+int main(int argc, char *argv[])
+{
+ int i, seed=0;
+ int loops = 10000;
+
+ unlink("test.gdbm");
+
+ db = tdb_open("test.tdb", 0, TDB_CLEAR_IF_FIRST,
+ O_RDWR | O_CREAT | O_TRUNC, 0600);
+ gdbm = gdbm_open("test.gdbm", 512, GDBM_WRITER|GDBM_NEWDB|GDBM_FAST,
+ 0600, NULL);
+
+ if (!db || !gdbm) {
+ fatal("db open failed");
+ }
+
+ tdb_logging_function(db, tdb_log);
+
+#if 1
+ srand(seed);
+ start_timer();
+ for (i=0;i<loops;i++) addrec_gdbm();
+ printf("gdbm got %.2f ops/sec\n", i/end_timer());
+#endif
+
+ merge_test();
+
+ srand(seed);
+ start_timer();
+ for (i=0;i<loops;i++) addrec_db();
+ printf("tdb got %.2f ops/sec\n", i/end_timer());
+
+ compare_db();
+
+ printf("traversed %d records\n", tdb_traverse(db, traverse_fn, NULL));
+ printf("traversed %d records\n", tdb_traverse(db, traverse_fn, NULL));
+
+ tdb_close(db);
+ gdbm_close(gdbm);
+
+ return 0;
+}
diff --git a/source/tdb/tdbtool.c b/source/tdb/tdbtool.c
new file mode 100644
index 00000000000..92009dcef48
--- /dev/null
+++ b/source/tdb/tdbtool.c
@@ -0,0 +1,547 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba database functions
+ Copyright (C) Andrew Tridgell 1999-2000
+ Copyright (C) Paul `Rusty' Russell 2000
+ Copyright (C) Jeremy Allison 2000
+ Copyright (C) Andrew Esh 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ctype.h>
+#include <signal.h>
+#include "tdb.h"
+
+/* a tdb tool for manipulating a tdb database */
+
+#define FSTRING_LEN 256
+typedef char fstring[FSTRING_LEN];
+
+typedef struct connections_key {
+ pid_t pid;
+ int cnum;
+ fstring name;
+} connections_key;
+
+typedef struct connections_data {
+ int magic;
+ pid_t pid;
+ int cnum;
+ uid_t uid;
+ gid_t gid;
+ char name[24];
+ char addr[24];
+ char machine[128];
+ time_t start;
+} connections_data;
+
+static TDB_CONTEXT *tdb;
+
+static int print_rec(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
+
+static void print_asc(unsigned char *buf,int len)
+{
+ int i;
+
+ /* We're probably printing ASCII strings so don't try to display
+ the trailing NULL character. */
+
+ if (buf[len - 1] == 0)
+ len--;
+
+ for (i=0;i<len;i++)
+ printf("%c",isprint(buf[i])?buf[i]:'.');
+}
+
+static void print_data(unsigned char *buf,int len)
+{
+ int i=0;
+ if (len<=0) return;
+ printf("[%03X] ",i);
+ for (i=0;i<len;) {
+ printf("%02X ",(int)buf[i]);
+ i++;
+ if (i%8 == 0) printf(" ");
+ if (i%16 == 0) {
+ print_asc(&buf[i-16],8); printf(" ");
+ print_asc(&buf[i-8],8); printf("\n");
+ if (i<len) printf("[%03X] ",i);
+ }
+ }
+ if (i%16) {
+ int n;
+
+ n = 16 - (i%16);
+ printf(" ");
+ if (n>8) printf(" ");
+ while (n--) printf(" ");
+
+ n = i%16;
+ if (n > 8) n = 8;
+ print_asc(&buf[i-(i%16)],n); printf(" ");
+ n = (i%16) - n;
+ if (n>0) print_asc(&buf[i-n],n);
+ printf("\n");
+ }
+}
+
+static void help(void)
+{
+ printf("\n"
+"tdbtool: \n"
+" create dbname : create a database\n"
+" open dbname : open an existing database\n"
+" erase : erase the database\n"
+" dump : dump the database as strings\n"
+" insert key data : insert a record\n"
+" move key file : move a record to a destination tdb\n"
+" store key data : store a record (replace)\n"
+" show key : show a record by key\n"
+" delete key : delete a record by key\n"
+" list : print the database hash table and freelist\n"
+" free : print the database freelist\n"
+" 1 | first : print the first record\n"
+" n | next : print the next record\n"
+" q | quit : terminate\n"
+" \\n : repeat 'next' command\n"
+"\n");
+}
+
+static void terror(char *why)
+{
+ printf("%s\n", why);
+}
+
+static char *get_token(int startover)
+{
+ static char tmp[1024];
+ static char *cont = NULL;
+ char *insert, *start;
+ char *k = strtok(NULL, " ");
+
+ if (!k)
+ return NULL;
+
+ if (startover)
+ start = tmp;
+ else
+ start = cont;
+
+ strcpy(start, k);
+ insert = start + strlen(start) - 1;
+ while (*insert == '\\') {
+ *insert++ = ' ';
+ k = strtok(NULL, " ");
+ if (!k)
+ break;
+ strcpy(insert, k);
+ insert = start + strlen(start) - 1;
+ }
+
+ /* Get ready for next call */
+ cont = start + strlen(start) + 1;
+ return start;
+}
+
+static void create_tdb(void)
+{
+ char *tok = get_token(1);
+ if (!tok) {
+ help();
+ return;
+ }
+ if (tdb) tdb_close(tdb);
+ tdb = tdb_open(tok, 0, TDB_CLEAR_IF_FIRST,
+ O_RDWR | O_CREAT | O_TRUNC, 0600);
+ if (!tdb) {
+ printf("Could not create %s: %s\n", tok, strerror(errno));
+ }
+}
+
+static void open_tdb(void)
+{
+ char *tok = get_token(1);
+ if (!tok) {
+ help();
+ return;
+ }
+ if (tdb) tdb_close(tdb);
+ tdb = tdb_open(tok, 0, 0, O_RDWR, 0600);
+ if (!tdb) {
+ printf("Could not open %s: %s\n", tok, strerror(errno));
+ }
+}
+
+static void insert_tdb(void)
+{
+ char *k = get_token(1);
+ char *d = get_token(0);
+ TDB_DATA key, dbuf;
+
+ if (!k || !d) {
+ help();
+ return;
+ }
+
+ key.dptr = k;
+ key.dsize = strlen(k)+1;
+ dbuf.dptr = d;
+ dbuf.dsize = strlen(d)+1;
+
+ if (tdb_store(tdb, key, dbuf, TDB_INSERT) == -1) {
+ terror("insert failed");
+ }
+}
+
+static void store_tdb(void)
+{
+ char *k = get_token(1);
+ char *d = get_token(0);
+ TDB_DATA key, dbuf;
+
+ if (!k || !d) {
+ help();
+ return;
+ }
+
+ key.dptr = k;
+ key.dsize = strlen(k)+1;
+ dbuf.dptr = d;
+ dbuf.dsize = strlen(d)+1;
+
+ printf("Storing key:\n");
+ print_rec(tdb, key, dbuf, NULL);
+
+ if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) {
+ terror("store failed");
+ }
+}
+
+static void show_tdb(void)
+{
+ char *k = get_token(1);
+ TDB_DATA key, dbuf;
+
+ if (!k) {
+ help();
+ return;
+ }
+
+ key.dptr = k;
+ key.dsize = strlen(k)+1;
+
+ dbuf = tdb_fetch(tdb, key);
+ if (!dbuf.dptr) {
+ /* maybe it is non-NULL terminated key? */
+ key.dsize = strlen(k);
+ dbuf = tdb_fetch(tdb, key);
+
+ if ( !dbuf.dptr ) {
+ terror("fetch failed");
+ return;
+ }
+ }
+
+ /* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */
+ print_rec(tdb, key, dbuf, NULL);
+
+ free( dbuf.dptr );
+
+ return;
+}
+
+static void delete_tdb(void)
+{
+ char *k = get_token(1);
+ TDB_DATA key;
+
+ if (!k) {
+ help();
+ return;
+ }
+
+ key.dptr = k;
+ key.dsize = strlen(k)+1;
+
+ if (tdb_delete(tdb, key) != 0) {
+ terror("delete failed");
+ }
+}
+
+static void move_rec(void)
+{
+ char *k = get_token(1);
+ char *file = get_token(0);
+ TDB_DATA key, dbuf;
+ TDB_CONTEXT *dst_tdb;
+
+ if (!k) {
+ help();
+ return;
+ }
+
+ if ( !file ) {
+ terror("need destination tdb name");
+ return;
+ }
+
+ key.dptr = k;
+ key.dsize = strlen(k)+1;
+
+ dbuf = tdb_fetch(tdb, key);
+ if (!dbuf.dptr) {
+ /* maybe it is non-NULL terminated key? */
+ key.dsize = strlen(k);
+ dbuf = tdb_fetch(tdb, key);
+
+ if ( !dbuf.dptr ) {
+ terror("fetch failed");
+ return;
+ }
+ }
+
+ print_rec(tdb, key, dbuf, NULL);
+
+ dst_tdb = tdb_open(file, 0, 0, O_RDWR, 0600);
+ if ( !dst_tdb ) {
+ terror("unable to open destination tdb");
+ return;
+ }
+
+ if ( tdb_store( dst_tdb, key, dbuf, TDB_REPLACE ) == -1 ) {
+ terror("failed to move record");
+ }
+ else
+ printf("record moved\n");
+
+ tdb_close( dst_tdb );
+
+ return;
+}
+
+#if 0
+static int print_conn_key(TDB_DATA key)
+{
+ printf( "pid =%5d ", ((connections_key*)key.dptr)->pid);
+ printf( "cnum =%10d ", ((connections_key*)key.dptr)->cnum);
+ printf( "name =[%s]\n", ((connections_key*)key.dptr)->name);
+ return 0;
+}
+
+static int print_conn_data(TDB_DATA dbuf)
+{
+ printf( "pid =%5d ", ((connections_data*)dbuf.dptr)->pid);
+ printf( "cnum =%10d ", ((connections_data*)dbuf.dptr)->cnum);
+ printf( "name =[%s]\n", ((connections_data*)dbuf.dptr)->name);
+
+ printf( "uid =%5d ", ((connections_data*)dbuf.dptr)->uid);
+ printf( "addr =[%s]\n", ((connections_data*)dbuf.dptr)->addr);
+ printf( "gid =%5d ", ((connections_data*)dbuf.dptr)->gid);
+ printf( "machine=[%s]\n", ((connections_data*)dbuf.dptr)->machine);
+ printf( "start = %s\n", ctime(&((connections_data*)dbuf.dptr)->start));
+ return 0;
+}
+#endif
+
+static int print_rec(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+#if 0
+ print_conn_key(key);
+ print_conn_data(dbuf);
+ return 0;
+#else
+ printf("\nkey %d bytes\n", key.dsize);
+ print_asc(key.dptr, key.dsize);
+ printf("\ndata %d bytes\n", dbuf.dsize);
+ print_data(dbuf.dptr, dbuf.dsize);
+ return 0;
+#endif
+}
+
+static int print_key(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+ print_asc(key.dptr, key.dsize);
+ printf("\n");
+ return 0;
+}
+
+static int total_bytes;
+
+static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+ total_bytes += dbuf.dsize;
+ return 0;
+}
+
+static void info_tdb(void)
+{
+ int count;
+ total_bytes = 0;
+ if ((count = tdb_traverse(tdb, traverse_fn, NULL) == -1))
+ printf("Error = %s\n", tdb_errorstr(tdb));
+ else
+ printf("%d records totalling %d bytes\n", count, total_bytes);
+}
+
+static char *tdb_getline(char *prompt)
+{
+ static char line[1024];
+ char *p;
+ fputs(prompt, stdout);
+ line[0] = 0;
+ p = fgets(line, sizeof(line)-1, stdin);
+ if (p) p = strchr(p, '\n');
+ if (p) *p = 0;
+ return p?line:NULL;
+}
+
+static int do_delete_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf,
+ void *state)
+{
+ return tdb_delete(the_tdb, key);
+}
+
+static void first_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
+{
+ TDB_DATA dbuf;
+ *pkey = tdb_firstkey(the_tdb);
+
+ dbuf = tdb_fetch(the_tdb, *pkey);
+ if (!dbuf.dptr) terror("fetch failed");
+ else {
+ /* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */
+ print_rec(the_tdb, *pkey, dbuf, NULL);
+ }
+}
+
+static void next_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
+{
+ TDB_DATA dbuf;
+ *pkey = tdb_nextkey(the_tdb, *pkey);
+
+ dbuf = tdb_fetch(the_tdb, *pkey);
+ if (!dbuf.dptr)
+ terror("fetch failed");
+ else
+ /* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */
+ print_rec(the_tdb, *pkey, dbuf, NULL);
+}
+
+int main(int argc, char *argv[])
+{
+ int bIterate = 0;
+ char *line;
+ char *tok;
+ TDB_DATA iterate_kbuf;
+
+ if (argv[1]) {
+ static char tmp[1024];
+ sprintf(tmp, "open %s", argv[1]);
+ tok=strtok(tmp," ");
+ open_tdb();
+ }
+
+ while ((line = tdb_getline("tdb> "))) {
+
+ /* Shell command */
+
+ if (line[0] == '!') {
+ system(line + 1);
+ continue;
+ }
+
+ if ((tok = strtok(line," "))==NULL) {
+ if (bIterate)
+ next_record(tdb, &iterate_kbuf);
+ continue;
+ }
+ if (strcmp(tok,"create") == 0) {
+ bIterate = 0;
+ create_tdb();
+ continue;
+ } else if (strcmp(tok,"open") == 0) {
+ open_tdb();
+ continue;
+ } else if ((strcmp(tok, "q") == 0) ||
+ (strcmp(tok, "quit") == 0)) {
+ break;
+ }
+
+ /* all the rest require a open database */
+ if (!tdb) {
+ bIterate = 0;
+ terror("database not open");
+ help();
+ continue;
+ }
+
+ if (strcmp(tok,"insert") == 0) {
+ bIterate = 0;
+ insert_tdb();
+ } else if (strcmp(tok,"store") == 0) {
+ bIterate = 0;
+ store_tdb();
+ } else if (strcmp(tok,"show") == 0) {
+ bIterate = 0;
+ show_tdb();
+ } else if (strcmp(tok,"erase") == 0) {
+ bIterate = 0;
+ tdb_traverse(tdb, do_delete_fn, NULL);
+ } else if (strcmp(tok,"delete") == 0) {
+ bIterate = 0;
+ delete_tdb();
+ } else if (strcmp(tok,"dump") == 0) {
+ bIterate = 0;
+ tdb_traverse(tdb, print_rec, NULL);
+ } else if (strcmp(tok,"move") == 0) {
+ bIterate = 0;
+ move_rec();
+ } else if (strcmp(tok,"list") == 0) {
+ tdb_dump_all(tdb);
+ } else if (strcmp(tok, "free") == 0) {
+ tdb_printfreelist(tdb);
+ } else if (strcmp(tok,"info") == 0) {
+ info_tdb();
+ } else if ( (strcmp(tok, "1") == 0) ||
+ (strcmp(tok, "first") == 0)) {
+ bIterate = 1;
+ first_record(tdb, &iterate_kbuf);
+ } else if ((strcmp(tok, "n") == 0) ||
+ (strcmp(tok, "next") == 0)) {
+ next_record(tdb, &iterate_kbuf);
+ } else if ((strcmp(tok, "keys") == 0)) {
+ bIterate = 0;
+ tdb_traverse(tdb, print_key, NULL);
+ } else {
+ help();
+ }
+ }
+
+ if (tdb) tdb_close(tdb);
+
+ return 0;
+}
diff --git a/source/tdb/tdbtorture.c b/source/tdb/tdbtorture.c
new file mode 100644
index 00000000000..3f704e537ea
--- /dev/null
+++ b/source/tdb/tdbtorture.c
@@ -0,0 +1,227 @@
+#include <stdlib.h>
+#include <time.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include "tdb.h"
+
+/* this tests tdb by doing lots of ops from several simultaneous
+ writers - that stresses the locking code. Build with TDB_DEBUG=1
+ for best effect */
+
+
+
+#define REOPEN_PROB 30
+#define DELETE_PROB 8
+#define STORE_PROB 4
+#define APPEND_PROB 6
+#define LOCKSTORE_PROB 0
+#define TRAVERSE_PROB 20
+#define CULL_PROB 100
+#define KEYLEN 3
+#define DATALEN 100
+#define LOCKLEN 20
+
+static TDB_CONTEXT *db;
+
+static void tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vfprintf(stdout, format, ap);
+ va_end(ap);
+ fflush(stdout);
+#if 0
+ {
+ char *ptr;
+ asprintf(&ptr,"xterm -e gdb /proc/%d/exe %d", getpid(), getpid());
+ system(ptr);
+ free(ptr);
+ }
+#endif
+}
+
+static void fatal(char *why)
+{
+ perror(why);
+ exit(1);
+}
+
+static char *randbuf(int len)
+{
+ char *buf;
+ int i;
+ buf = (char *)malloc(len+1);
+
+ for (i=0;i<len;i++) {
+ buf[i] = 'a' + (rand() % 26);
+ }
+ buf[i] = 0;
+ return buf;
+}
+
+static int cull_traverse(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf,
+ void *state)
+{
+ if (random() % CULL_PROB == 0) {
+ tdb_delete(tdb, key);
+ }
+ return 0;
+}
+
+static void addrec_db(void)
+{
+ int klen, dlen, slen;
+ char *k, *d, *s;
+ TDB_DATA key, data, lockkey;
+
+ klen = 1 + (rand() % KEYLEN);
+ dlen = 1 + (rand() % DATALEN);
+ slen = 1 + (rand() % LOCKLEN);
+
+ k = randbuf(klen);
+ d = randbuf(dlen);
+ s = randbuf(slen);
+
+ key.dptr = k;
+ key.dsize = klen+1;
+
+ data.dptr = d;
+ data.dsize = dlen+1;
+
+ lockkey.dptr = s;
+ lockkey.dsize = slen+1;
+
+#if REOPEN_PROB
+ if (random() % REOPEN_PROB == 0) {
+ tdb_reopen_all();
+ goto next;
+ }
+#endif
+
+#if DELETE_PROB
+ if (random() % DELETE_PROB == 0) {
+ tdb_delete(db, key);
+ goto next;
+ }
+#endif
+
+#if STORE_PROB
+ if (random() % STORE_PROB == 0) {
+ if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
+ fatal("tdb_store failed");
+ }
+ goto next;
+ }
+#endif
+
+#if APPEND_PROB
+ if (random() % APPEND_PROB == 0) {
+ if (tdb_append(db, key, data) != 0) {
+ fatal("tdb_append failed");
+ }
+ goto next;
+ }
+#endif
+
+#if LOCKSTORE_PROB
+ if (random() % LOCKSTORE_PROB == 0) {
+ tdb_chainlock(db, lockkey);
+ data = tdb_fetch(db, key);
+ if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
+ fatal("tdb_store failed");
+ }
+ if (data.dptr) free(data.dptr);
+ tdb_chainunlock(db, lockkey);
+ goto next;
+ }
+#endif
+
+#if TRAVERSE_PROB
+ if (random() % TRAVERSE_PROB == 0) {
+ tdb_traverse(db, cull_traverse, NULL);
+ goto next;
+ }
+#endif
+
+ data = tdb_fetch(db, key);
+ if (data.dptr) free(data.dptr);
+
+next:
+ free(k);
+ free(d);
+ free(s);
+}
+
+static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf,
+ void *state)
+{
+ tdb_delete(tdb, key);
+ return 0;
+}
+
+#ifndef NPROC
+#define NPROC 6
+#endif
+
+#ifndef NLOOPS
+#define NLOOPS 200000
+#endif
+
+int main(int argc, char *argv[])
+{
+ int i, seed=0;
+ int loops = NLOOPS;
+ pid_t pids[NPROC];
+
+ pids[0] = getpid();
+
+ for (i=0;i<NPROC-1;i++) {
+ if ((pids[i+1]=fork()) == 0) break;
+ }
+
+ db = tdb_open("torture.tdb", 2, TDB_CLEAR_IF_FIRST,
+ O_RDWR | O_CREAT, 0600);
+ if (!db) {
+ fatal("db open failed");
+ }
+ tdb_logging_function(db, tdb_log);
+
+ srand(seed + getpid());
+ srandom(seed + getpid() + time(NULL));
+ for (i=0;i<loops;i++) addrec_db();
+
+ tdb_traverse(db, NULL, NULL);
+ tdb_traverse(db, traverse_fn, NULL);
+ tdb_traverse(db, traverse_fn, NULL);
+
+ tdb_close(db);
+
+ if (getpid() == pids[0]) {
+ for (i=0;i<NPROC-1;i++) {
+ int status;
+ if (waitpid(pids[i+1], &status, 0) != pids[i+1]) {
+ printf("failed to wait for %d\n",
+ (int)pids[i+1]);
+ exit(1);
+ }
+ if (WEXITSTATUS(status) != 0) {
+ printf("child %d exited with status %d\n",
+ (int)pids[i+1], WEXITSTATUS(status));
+ exit(1);
+ }
+ }
+ printf("OK\n");
+ }
+
+ return 0;
+}
diff --git a/source/tdb/tdbutil.c b/source/tdb/tdbutil.c
new file mode 100644
index 00000000000..09e55e2e702
--- /dev/null
+++ b/source/tdb/tdbutil.c
@@ -0,0 +1,829 @@
+/*
+ Unix SMB/CIFS implementation.
+ tdb utility functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Rafal Szczesniak 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include <fnmatch.h>
+
+/* these are little tdb utility functions that are meant to make
+ dealing with a tdb database a little less cumbersome in Samba */
+
+static SIG_ATOMIC_T gotalarm;
+
+/***************************************************************
+ Signal function to tell us we timed out.
+****************************************************************/
+
+static void gotalarm_sig(void)
+{
+ gotalarm = 1;
+}
+
+/***************************************************************
+ Make a TDB_DATA and keep the const warning in one place
+****************************************************************/
+
+static TDB_DATA make_tdb_data(const char *dptr, size_t dsize)
+{
+ TDB_DATA ret;
+ ret.dptr = dptr;
+ ret.dsize = dsize;
+ return ret;
+}
+
+/****************************************************************************
+ Lock a chain with timeout (in seconds).
+****************************************************************************/
+
+static int tdb_chainlock_with_timeout_internal( TDB_CONTEXT *tdb, TDB_DATA key, unsigned int timeout, int rw_type)
+{
+ /* Allow tdb_chainlock to be interrupted by an alarm. */
+ int ret;
+ gotalarm = 0;
+ tdb_set_lock_alarm(&gotalarm);
+
+ if (timeout) {
+ CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
+ alarm(timeout);
+ }
+
+ if (rw_type == F_RDLCK)
+ ret = tdb_chainlock_read(tdb, key);
+ else
+ ret = tdb_chainlock(tdb, key);
+
+ if (timeout) {
+ alarm(0);
+ CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
+ if (gotalarm) {
+ DEBUG(0,("tdb_chainlock_with_timeout_internal: alarm (%u) timed out for key %s in tdb %s\n",
+ timeout, key.dptr, tdb->name ));
+ /* TODO: If we time out waiting for a lock, it might
+ * be nice to use F_GETLK to get the pid of the
+ * process currently holding the lock and print that
+ * as part of the debugging message. -- mbp */
+ return -1;
+ }
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ Write lock a chain. Return -1 if timeout or lock failed.
+****************************************************************************/
+
+int tdb_chainlock_with_timeout( TDB_CONTEXT *tdb, TDB_DATA key, unsigned int timeout)
+{
+ return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_WRLCK);
+}
+
+/****************************************************************************
+ Lock a chain by string. Return -1 if timeout or lock failed.
+****************************************************************************/
+
+int tdb_lock_bystring(TDB_CONTEXT *tdb, const char *keyval, unsigned int timeout)
+{
+ TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1);
+
+ return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_WRLCK);
+}
+
+/****************************************************************************
+ Unlock a chain by string.
+****************************************************************************/
+
+void tdb_unlock_bystring(TDB_CONTEXT *tdb, const char *keyval)
+{
+ TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1);
+
+ tdb_chainunlock(tdb, key);
+}
+
+/****************************************************************************
+ Read lock a chain by string. Return -1 if timeout or lock failed.
+****************************************************************************/
+
+int tdb_read_lock_bystring(TDB_CONTEXT *tdb, const char *keyval, unsigned int timeout)
+{
+ TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1);
+
+ return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_RDLCK);
+}
+
+/****************************************************************************
+ Read unlock a chain by string.
+****************************************************************************/
+
+void tdb_read_unlock_bystring(TDB_CONTEXT *tdb, const char *keyval)
+{
+ TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1);
+
+ tdb_chainunlock_read(tdb, key);
+}
+
+
+/****************************************************************************
+ Fetch a int32 value by a arbitrary blob key, return -1 if not found.
+ Output is int32 in native byte order.
+****************************************************************************/
+
+int32 tdb_fetch_int32_byblob(TDB_CONTEXT *tdb, const char *keyval, size_t len)
+{
+ TDB_DATA key = make_tdb_data(keyval, len);
+ TDB_DATA data;
+ int32 ret;
+
+ data = tdb_fetch(tdb, key);
+ if (!data.dptr || data.dsize != sizeof(int32)) {
+ SAFE_FREE(data.dptr);
+ return -1;
+ }
+
+ ret = IVAL(data.dptr,0);
+ SAFE_FREE(data.dptr);
+ return ret;
+}
+
+/****************************************************************************
+ Fetch a int32 value by string key, return -1 if not found.
+ Output is int32 in native byte order.
+****************************************************************************/
+
+int32 tdb_fetch_int32(TDB_CONTEXT *tdb, const char *keystr)
+{
+ return tdb_fetch_int32_byblob(tdb, keystr, strlen(keystr) + 1);
+}
+
+/****************************************************************************
+ Store a int32 value by an arbitary blob key, return 0 on success, -1 on failure.
+ Input is int32 in native byte order. Output in tdb is in little-endian.
+****************************************************************************/
+
+int tdb_store_int32_byblob(TDB_CONTEXT *tdb, const char *keystr, size_t len, int32 v)
+{
+ TDB_DATA key = make_tdb_data(keystr, len);
+ TDB_DATA data;
+ int32 v_store;
+
+ SIVAL(&v_store,0,v);
+ data.dptr = (void *)&v_store;
+ data.dsize = sizeof(int32);
+
+ return tdb_store(tdb, key, data, TDB_REPLACE);
+}
+
+/****************************************************************************
+ Store a int32 value by string key, return 0 on success, -1 on failure.
+ Input is int32 in native byte order. Output in tdb is in little-endian.
+****************************************************************************/
+
+int tdb_store_int32(TDB_CONTEXT *tdb, const char *keystr, int32 v)
+{
+ return tdb_store_int32_byblob(tdb, keystr, strlen(keystr) + 1, v);
+}
+
+/****************************************************************************
+ Fetch a uint32 value by a arbitrary blob key, return -1 if not found.
+ Output is uint32 in native byte order.
+****************************************************************************/
+
+BOOL tdb_fetch_uint32_byblob(TDB_CONTEXT *tdb, const char *keyval, size_t len, uint32 *value)
+{
+ TDB_DATA key = make_tdb_data(keyval, len);
+ TDB_DATA data;
+
+ data = tdb_fetch(tdb, key);
+ if (!data.dptr || data.dsize != sizeof(uint32)) {
+ SAFE_FREE(data.dptr);
+ return False;
+ }
+
+ *value = IVAL(data.dptr,0);
+ SAFE_FREE(data.dptr);
+ return True;
+}
+
+/****************************************************************************
+ Fetch a uint32 value by string key, return -1 if not found.
+ Output is uint32 in native byte order.
+****************************************************************************/
+
+BOOL tdb_fetch_uint32(TDB_CONTEXT *tdb, const char *keystr, uint32 *value)
+{
+ return tdb_fetch_uint32_byblob(tdb, keystr, strlen(keystr) + 1, value);
+}
+
+/****************************************************************************
+ Store a uint32 value by an arbitary blob key, return 0 on success, -1 on failure.
+ Input is uint32 in native byte order. Output in tdb is in little-endian.
+****************************************************************************/
+
+BOOL tdb_store_uint32_byblob(TDB_CONTEXT *tdb, const char *keystr, size_t len, uint32 value)
+{
+ TDB_DATA key = make_tdb_data(keystr, len);
+ TDB_DATA data;
+ uint32 v_store;
+ BOOL ret = True;
+
+ SIVAL(&v_store, 0, value);
+ data.dptr = (void *)&v_store;
+ data.dsize = sizeof(uint32);
+
+ if (tdb_store(tdb, key, data, TDB_REPLACE) == -1)
+ ret = False;
+
+ return ret;
+}
+
+/****************************************************************************
+ Store a uint32 value by string key, return 0 on success, -1 on failure.
+ Input is uint32 in native byte order. Output in tdb is in little-endian.
+****************************************************************************/
+
+BOOL tdb_store_uint32(TDB_CONTEXT *tdb, const char *keystr, uint32 value)
+{
+ return tdb_store_uint32_byblob(tdb, keystr, strlen(keystr) + 1, value);
+}
+/****************************************************************************
+ Store a buffer by a null terminated string key. Return 0 on success, -1
+ on failure.
+****************************************************************************/
+
+int tdb_store_bystring(TDB_CONTEXT *tdb, const char *keystr, TDB_DATA data, int flags)
+{
+ TDB_DATA key = make_tdb_data(keystr, strlen(keystr)+1);
+
+ return tdb_store(tdb, key, data, flags);
+}
+
+/****************************************************************************
+ Fetch a buffer using a null terminated string key. Don't forget to call
+ free() on the result dptr.
+****************************************************************************/
+
+TDB_DATA tdb_fetch_bystring(TDB_CONTEXT *tdb, const char *keystr)
+{
+ TDB_DATA key = make_tdb_data(keystr, strlen(keystr)+1);
+
+ return tdb_fetch(tdb, key);
+}
+
+/****************************************************************************
+ Delete an entry using a null terminated string key.
+****************************************************************************/
+
+int tdb_delete_bystring(TDB_CONTEXT *tdb, const char *keystr)
+{
+ TDB_DATA key = make_tdb_data(keystr, strlen(keystr)+1);
+
+ return tdb_delete(tdb, key);
+}
+
+/****************************************************************************
+ Atomic integer change. Returns old value. To create, set initial value in *oldval.
+****************************************************************************/
+
+int32 tdb_change_int32_atomic(TDB_CONTEXT *tdb, const char *keystr, int32 *oldval, int32 change_val)
+{
+ int32 val;
+ int32 ret = -1;
+
+ if (tdb_lock_bystring(tdb, keystr,0) == -1)
+ return -1;
+
+ if ((val = tdb_fetch_int32(tdb, keystr)) == -1) {
+ /* The lookup failed */
+ if (tdb_error(tdb) != TDB_ERR_NOEXIST) {
+ /* but not because it didn't exist */
+ goto err_out;
+ }
+
+ /* Start with 'old' value */
+ val = *oldval;
+
+ } else {
+ /* It worked, set return value (oldval) to tdb data */
+ *oldval = val;
+ }
+
+ /* Increment value for storage and return next time */
+ val += change_val;
+
+ if (tdb_store_int32(tdb, keystr, val) == -1)
+ goto err_out;
+
+ ret = 0;
+
+ err_out:
+
+ tdb_unlock_bystring(tdb, keystr);
+ return ret;
+}
+
+/****************************************************************************
+ Atomic unsigned integer change. Returns old value. To create, set initial value in *oldval.
+****************************************************************************/
+
+BOOL tdb_change_uint32_atomic(TDB_CONTEXT *tdb, const char *keystr, uint32 *oldval, uint32 change_val)
+{
+ uint32 val;
+ BOOL ret = False;
+
+ if (tdb_lock_bystring(tdb, keystr,0) == -1)
+ return False;
+
+ if (!tdb_fetch_uint32(tdb, keystr, &val)) {
+ /* It failed */
+ if (tdb_error(tdb) != TDB_ERR_NOEXIST) {
+ /* and not because it didn't exist */
+ goto err_out;
+ }
+
+ /* Start with 'old' value */
+ val = *oldval;
+
+ } else {
+ /* it worked, set return value (oldval) to tdb data */
+ *oldval = val;
+
+ }
+
+ /* get a new value to store */
+ val += change_val;
+
+ if (!tdb_store_uint32(tdb, keystr, val))
+ goto err_out;
+
+ ret = True;
+
+ err_out:
+
+ tdb_unlock_bystring(tdb, keystr);
+ return ret;
+}
+
+/****************************************************************************
+ Useful pair of routines for packing/unpacking data consisting of
+ integers and strings.
+****************************************************************************/
+
+size_t tdb_pack(char *buf, int bufsize, const char *fmt, ...)
+{
+ va_list ap;
+ uint8 bt;
+ uint16 w;
+ uint32 d;
+ int i;
+ void *p;
+ int len;
+ char *s;
+ char c;
+ char *buf0 = buf;
+ const char *fmt0 = fmt;
+ int bufsize0 = bufsize;
+
+ va_start(ap, fmt);
+
+ while (*fmt) {
+ switch ((c = *fmt++)) {
+ case 'b': /* unsigned 8-bit integer */
+ len = 1;
+ bt = (uint8)va_arg(ap, int);
+ if (bufsize && bufsize >= len)
+ SSVAL(buf, 0, bt);
+ break;
+ case 'w': /* unsigned 16-bit integer */
+ len = 2;
+ w = (uint16)va_arg(ap, int);
+ if (bufsize && bufsize >= len)
+ SSVAL(buf, 0, w);
+ break;
+ case 'd': /* signed 32-bit integer (standard int in most systems) */
+ len = 4;
+ d = va_arg(ap, uint32);
+ if (bufsize && bufsize >= len)
+ SIVAL(buf, 0, d);
+ break;
+ case 'p': /* pointer */
+ len = 4;
+ p = va_arg(ap, void *);
+ d = p?1:0;
+ if (bufsize && bufsize >= len)
+ SIVAL(buf, 0, d);
+ break;
+ case 'P': /* null-terminated string */
+ s = va_arg(ap,char *);
+ w = strlen(s);
+ len = w + 1;
+ if (bufsize && bufsize >= len)
+ memcpy(buf, s, len);
+ break;
+ case 'f': /* null-terminated string */
+ s = va_arg(ap,char *);
+ w = strlen(s);
+ len = w + 1;
+ if (bufsize && bufsize >= len)
+ memcpy(buf, s, len);
+ break;
+ case 'B': /* fixed-length string */
+ i = va_arg(ap, int);
+ s = va_arg(ap, char *);
+ len = 4+i;
+ if (bufsize && bufsize >= len) {
+ SIVAL(buf, 0, i);
+ memcpy(buf+4, s, i);
+ }
+ break;
+ default:
+ DEBUG(0,("Unknown tdb_pack format %c in %s\n",
+ c, fmt));
+ len = 0;
+ break;
+ }
+
+ buf += len;
+ if (bufsize)
+ bufsize -= len;
+ if (bufsize < 0)
+ bufsize = 0;
+ }
+
+ va_end(ap);
+
+ DEBUG(18,("tdb_pack(%s, %d) -> %d\n",
+ fmt0, bufsize0, (int)PTR_DIFF(buf, buf0)));
+
+ return PTR_DIFF(buf, buf0);
+}
+
+/****************************************************************************
+ Useful pair of routines for packing/unpacking data consisting of
+ integers and strings.
+****************************************************************************/
+
+int tdb_unpack(char *buf, int bufsize, const char *fmt, ...)
+{
+ va_list ap;
+ uint8 *bt;
+ uint16 *w;
+ uint32 *d;
+ int len;
+ int *i;
+ void **p;
+ char *s, **b;
+ char c;
+ char *buf0 = buf;
+ const char *fmt0 = fmt;
+ int bufsize0 = bufsize;
+
+ va_start(ap, fmt);
+
+ while (*fmt) {
+ switch ((c=*fmt++)) {
+ case 'b':
+ len = 1;
+ bt = va_arg(ap, uint8 *);
+ if (bufsize < len)
+ goto no_space;
+ *bt = SVAL(buf, 0);
+ break;
+ case 'w':
+ len = 2;
+ w = va_arg(ap, uint16 *);
+ if (bufsize < len)
+ goto no_space;
+ *w = SVAL(buf, 0);
+ break;
+ case 'd':
+ len = 4;
+ d = va_arg(ap, uint32 *);
+ if (bufsize < len)
+ goto no_space;
+ *d = IVAL(buf, 0);
+ break;
+ case 'p':
+ len = 4;
+ p = va_arg(ap, void **);
+ if (bufsize < len)
+ goto no_space;
+ *p = (void *)IVAL(buf, 0);
+ break;
+ case 'P':
+ s = va_arg(ap,char *);
+ len = strlen(buf) + 1;
+ if (bufsize < len || len > sizeof(pstring))
+ goto no_space;
+ memcpy(s, buf, len);
+ break;
+ case 'f':
+ s = va_arg(ap,char *);
+ len = strlen(buf) + 1;
+ if (bufsize < len || len > sizeof(fstring))
+ goto no_space;
+ memcpy(s, buf, len);
+ break;
+ case 'B':
+ i = va_arg(ap, int *);
+ b = va_arg(ap, char **);
+ len = 4;
+ if (bufsize < len)
+ goto no_space;
+ *i = IVAL(buf, 0);
+ if (! *i) {
+ *b = NULL;
+ break;
+ }
+ len += *i;
+ if (bufsize < len)
+ goto no_space;
+ *b = (char *)malloc(*i);
+ if (! *b)
+ goto no_space;
+ memcpy(*b, buf+4, *i);
+ break;
+ default:
+ DEBUG(0,("Unknown tdb_unpack format %c in %s\n",
+ c, fmt));
+
+ len = 0;
+ break;
+ }
+
+ buf += len;
+ bufsize -= len;
+ }
+
+ va_end(ap);
+
+ DEBUG(18,("tdb_unpack(%s, %d) -> %d\n",
+ fmt0, bufsize0, (int)PTR_DIFF(buf, buf0)));
+
+ return PTR_DIFF(buf, buf0);
+
+ no_space:
+ return -1;
+}
+
+
+/**
+ * Pack SID passed by pointer
+ *
+ * @param pack_buf pointer to buffer which is to be filled with packed data
+ * @param bufsize size of packing buffer
+ * @param sid pointer to sid to be packed
+ *
+ * @return length of the packed representation of the whole structure
+ **/
+size_t tdb_sid_pack(char* pack_buf, int bufsize, DOM_SID* sid)
+{
+ int idx;
+ size_t len = 0;
+
+ if (!sid || !pack_buf) return -1;
+
+ len += tdb_pack(pack_buf + len, bufsize - len, "bb", sid->sid_rev_num,
+ sid->num_auths);
+
+ for (idx = 0; idx < 6; idx++) {
+ len += tdb_pack(pack_buf + len, bufsize - len, "b", sid->id_auth[idx]);
+ }
+
+ for (idx = 0; idx < MAXSUBAUTHS; idx++) {
+ len += tdb_pack(pack_buf + len, bufsize - len, "d", sid->sub_auths[idx]);
+ }
+
+ return len;
+}
+
+
+/**
+ * Unpack SID into a pointer
+ *
+ * @param pack_buf pointer to buffer with packed representation
+ * @param bufsize size of the buffer
+ * @param sid pointer to sid structure to be filled with unpacked data
+ *
+ * @return size of structure unpacked from buffer
+ **/
+size_t tdb_sid_unpack(char* pack_buf, int bufsize, DOM_SID* sid)
+{
+ int idx, len = 0;
+
+ if (!sid || !pack_buf) return -1;
+
+ len += tdb_unpack(pack_buf + len, bufsize - len, "bb",
+ &sid->sid_rev_num, &sid->num_auths);
+
+ for (idx = 0; idx < 6; idx++) {
+ len += tdb_unpack(pack_buf + len, bufsize - len, "b", &sid->id_auth[idx]);
+ }
+
+ for (idx = 0; idx < MAXSUBAUTHS; idx++) {
+ len += tdb_unpack(pack_buf + len, bufsize - len, "d", &sid->sub_auths[idx]);
+ }
+
+ return len;
+}
+
+
+/**
+ * Pack TRUSTED_DOM_PASS passed by pointer
+ *
+ * @param pack_buf pointer to buffer which is to be filled with packed data
+ * @param bufsize size of the buffer
+ * @param pass pointer to trusted domain password to be packed
+ *
+ * @return length of the packed representation of the whole structure
+ **/
+size_t tdb_trusted_dom_pass_pack(char* pack_buf, int bufsize, TRUSTED_DOM_PASS* pass)
+{
+ int idx, len = 0;
+
+ if (!pack_buf || !pass) return -1;
+
+ /* packing unicode domain name and password */
+ len += tdb_pack(pack_buf + len, bufsize - len, "d", pass->uni_name_len);
+
+ for (idx = 0; idx < 32; idx++)
+ len += tdb_pack(pack_buf + len, bufsize - len, "w", pass->uni_name[idx]);
+
+ len += tdb_pack(pack_buf + len, bufsize - len, "dPd", pass->pass_len,
+ pass->pass, pass->mod_time);
+
+ /* packing SID structure */
+ len += tdb_sid_pack(pack_buf + len, bufsize - len, &pass->domain_sid);
+
+ return len;
+}
+
+
+/**
+ * Unpack TRUSTED_DOM_PASS passed by pointer
+ *
+ * @param pack_buf pointer to buffer with packed representation
+ * @param bufsize size of the buffer
+ * @param pass pointer to trusted domain password to be filled with unpacked data
+ *
+ * @return size of structure unpacked from buffer
+ **/
+size_t tdb_trusted_dom_pass_unpack(char* pack_buf, int bufsize, TRUSTED_DOM_PASS* pass)
+{
+ int idx, len = 0;
+
+ if (!pack_buf || !pass) return -1;
+
+ /* unpack unicode domain name and plaintext password */
+ len += tdb_unpack(pack_buf, bufsize - len, "d", &pass->uni_name_len);
+
+ for (idx = 0; idx < 32; idx++)
+ len += tdb_unpack(pack_buf + len, bufsize - len, "w", &pass->uni_name[idx]);
+
+ len += tdb_unpack(pack_buf + len, bufsize - len, "dPd", &pass->pass_len, &pass->pass,
+ &pass->mod_time);
+
+ /* unpack domain sid */
+ len += tdb_sid_unpack(pack_buf + len, bufsize - len, &pass->domain_sid);
+
+ return len;
+}
+
+
+/****************************************************************************
+ Log tdb messages via DEBUG().
+****************************************************************************/
+
+static void tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...)
+{
+ va_list ap;
+ char *ptr = NULL;
+
+ va_start(ap, format);
+ vasprintf(&ptr, format, ap);
+ va_end(ap);
+
+ if (!ptr || !*ptr)
+ return;
+
+ DEBUG(level, ("tdb(%s): %s", tdb->name ? tdb->name : "unnamed", ptr));
+ SAFE_FREE(ptr);
+}
+
+/****************************************************************************
+ Like tdb_open() but also setup a logging function that redirects to
+ the samba DEBUG() system.
+****************************************************************************/
+
+TDB_CONTEXT *tdb_open_log(const char *name, int hash_size, int tdb_flags,
+ int open_flags, mode_t mode)
+{
+ TDB_CONTEXT *tdb;
+
+ if (!lp_use_mmap())
+ tdb_flags |= TDB_NOMMAP;
+
+ tdb = tdb_open_ex(name, hash_size, tdb_flags,
+ open_flags, mode, tdb_log);
+ if (!tdb)
+ return NULL;
+
+ return tdb;
+}
+
+
+/****************************************************************************
+ Allow tdb_delete to be used as a tdb_traversal_fn.
+****************************************************************************/
+
+int tdb_traverse_delete_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf,
+ void *state)
+{
+ return tdb_delete(the_tdb, key);
+}
+
+
+
+/**
+ * Search across the whole tdb for keys that match the given pattern
+ * return the result as a list of keys
+ *
+ * @param tdb pointer to opened tdb file context
+ * @param pattern searching pattern used by fnmatch(3) functions
+ *
+ * @return list of keys found by looking up with given pattern
+ **/
+TDB_LIST_NODE *tdb_search_keys(TDB_CONTEXT *tdb, const char* pattern)
+{
+ TDB_DATA key, next;
+ TDB_LIST_NODE *list = NULL;
+ TDB_LIST_NODE *rec = NULL;
+ TDB_LIST_NODE *tmp = NULL;
+
+ for (key = tdb_firstkey(tdb); key.dptr; key = next) {
+ /* duplicate key string to ensure null-termination */
+ char *key_str = (char*) strndup(key.dptr, key.dsize);
+ if (!key_str) {
+ DEBUG(0, ("tdb_search_keys: strndup() failed!\n"));
+ smb_panic("strndup failed!\n");
+ }
+
+ DEBUG(18, ("checking %s for match to pattern %s\n", key_str, pattern));
+
+ next = tdb_nextkey(tdb, key);
+
+ /* do the pattern checking */
+ if (fnmatch(pattern, key_str, 0) == 0) {
+ rec = (TDB_LIST_NODE*) malloc(sizeof(*rec));
+ ZERO_STRUCTP(rec);
+
+ rec->node_key = key;
+
+ DLIST_ADD_END(list, rec, tmp);
+
+ DEBUG(18, ("checking %s matched pattern %s\n", key_str, pattern));
+ } else {
+ free(key.dptr);
+ }
+
+ /* free duplicated key string */
+ free(key_str);
+ }
+
+ return list;
+
+}
+
+
+/**
+ * Free the list returned by tdb_search_keys
+ *
+ * @param node list of results found by tdb_search_keys
+ **/
+void tdb_search_list_free(TDB_LIST_NODE* node)
+{
+ TDB_LIST_NODE *next_node;
+
+ while (node) {
+ next_node = node->next;
+ SAFE_FREE(node->node_key.dptr);
+ SAFE_FREE(node);
+ node = next_node;
+ };
+}
diff --git a/source/tdb/tdbutil.h b/source/tdb/tdbutil.h
new file mode 100644
index 00000000000..1a181a962f2
--- /dev/null
+++ b/source/tdb/tdbutil.h
@@ -0,0 +1,38 @@
+/*
+ Unix SMB/CIFS implementation.
+ tdb utility functions
+ Copyright (C) Andrew Tridgell 1999
+ Copyright (C) Rafal Szczesniak 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __TDBUTIL_H__
+#define __TDBUTIL_H__
+
+
+/* single node of a list returned by tdb_search_keys */
+typedef struct keys_node
+{
+ struct keys_node *prev, *next;
+ TDB_DATA node_key;
+} TDB_LIST_NODE;
+
+
+TDB_LIST_NODE *tdb_search_keys(TDB_CONTEXT*, const char*);
+void tdb_search_list_free(TDB_LIST_NODE*);
+
+
+#endif /* __TDBUTIL_H__ */
diff --git a/source/tests/.cvsignore b/source/tests/.cvsignore
new file mode 100644
index 00000000000..b6c1f01120e
--- /dev/null
+++ b/source/tests/.cvsignore
@@ -0,0 +1 @@
+unixsock
diff --git a/source/tests/README b/source/tests/README
new file mode 100644
index 00000000000..cf1be8b00a7
--- /dev/null
+++ b/source/tests/README
@@ -0,0 +1,10 @@
+This directory contains autoconf test programs that are too large to
+comfortably fit in configure.in.
+
+These programs should test one feature of the OS and exit(0) if it
+works or exit(1) if it doesn't work (do _not_ use return)
+
+The programs should be kept simple and to the point. Beautiful/fast
+code is not necessary
+
+
diff --git a/source/tests/crack.c b/source/tests/crack.c
new file mode 100644
index 00000000000..36119b3bbc6
--- /dev/null
+++ b/source/tests/crack.c
@@ -0,0 +1,12 @@
+#include <crack.h>
+
+#ifndef HAVE_CRACKLIB_DICTPATH
+#ifndef CRACKLIB_DICTPATH
+#define CRACKLIB_DICTPATH SAMBA_CRACKLIB_DICTPATH
+#endif
+#endif
+
+int main(int argc, char **argv) {
+ FascistCheck("Foo", CRACKLIB_DICTPATH);
+ return 0;
+}
diff --git a/source/tests/crypttest.c b/source/tests/crypttest.c
new file mode 100644
index 00000000000..efee2e593d4
--- /dev/null
+++ b/source/tests/crypttest.c
@@ -0,0 +1,852 @@
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#include <sys/types.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#if !defined(HAVE_CRYPT)
+
+/*
+ This bit of code was derived from the UFC-crypt package which
+ carries the following copyright
+
+ Modified for use by Samba by Andrew Tridgell, October 1994
+
+ Note that this routine is only faster on some machines. Under Linux 1.1.51
+ libc 4.5.26 I actually found this routine to be slightly slower.
+
+ Under SunOS I found a huge speedup by using these routines
+ (a factor of 20 or so)
+
+ Warning: I've had a report from Steve Kennedy <steve@gbnet.org>
+ that this crypt routine may sometimes get the wrong answer. Only
+ use UFC_CRYT if you really need it.
+
+*/
+
+/*
+ * UFC-crypt: ultra fast crypt(3) implementation
+ *
+ * Copyright (C) 1991-1998, Free Software Foundation, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * @(#)crypt_util.c 2.31 02/08/92
+ *
+ * Support routines
+ *
+ */
+
+
+#ifndef long32
+#if (SIZEOF_INT == 4)
+#define long32 int
+#elif (SIZEOF_LONG == 4)
+#define long32 long
+#elif (SIZEOF_SHORT == 4)
+#define long32 short
+#else
+/* uggh - no 32 bit type?? probably a CRAY. just hope this works ... */
+#define long32 int
+#endif
+#endif
+
+#ifndef long64
+#ifdef HAVE_LONGLONG
+#define long64 long long long
+#endif
+#endif
+
+#ifndef ufc_long
+#define ufc_long unsigned
+#endif
+
+#ifndef _UFC_64_
+#define _UFC_32_
+#endif
+
+/*
+ * Permutation done once on the 56 bit
+ * key derived from the original 8 byte ASCII key.
+ */
+static int pc1[56] = {
+ 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18,
+ 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36,
+ 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22,
+ 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4
+};
+
+/*
+ * How much to rotate each 28 bit half of the pc1 permutated
+ * 56 bit key before using pc2 to give the i' key
+ */
+static int rots[16] = {
+ 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
+};
+
+/*
+ * Permutation giving the key
+ * of the i' DES round
+ */
+static int pc2[48] = {
+ 14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10,
+ 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2,
+ 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48,
+ 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32
+};
+
+/*
+ * The E expansion table which selects
+ * bits from the 32 bit intermediate result.
+ */
+static int esel[48] = {
+ 32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9,
+ 8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17,
+ 16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25,
+ 24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1
+};
+static int e_inverse[64];
+
+/*
+ * Permutation done on the
+ * result of sbox lookups
+ */
+static int perm32[32] = {
+ 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10,
+ 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25
+};
+
+/*
+ * The sboxes
+ */
+static int sbox[8][4][16]= {
+ { { 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7 },
+ { 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8 },
+ { 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0 },
+ { 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 }
+ },
+
+ { { 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10 },
+ { 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5 },
+ { 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15 },
+ { 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 }
+ },
+
+ { { 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8 },
+ { 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1 },
+ { 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7 },
+ { 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 }
+ },
+
+ { { 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15 },
+ { 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9 },
+ { 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4 },
+ { 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 }
+ },
+
+ { { 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9 },
+ { 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6 },
+ { 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14 },
+ { 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 }
+ },
+
+ { { 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11 },
+ { 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8 },
+ { 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6 },
+ { 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 }
+ },
+
+ { { 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1 },
+ { 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6 },
+ { 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2 },
+ { 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 }
+ },
+
+ { { 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7 },
+ { 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2 },
+ { 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8 },
+ { 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 }
+ }
+};
+
+/*
+ * This is the final
+ * permutation matrix
+ */
+static int final_perm[64] = {
+ 40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31,
+ 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29,
+ 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27,
+ 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25
+};
+
+/*
+ * The 16 DES keys in BITMASK format
+ */
+#ifdef _UFC_32_
+long32 _ufc_keytab[16][2];
+#endif
+
+#ifdef _UFC_64_
+long64 _ufc_keytab[16];
+#endif
+
+
+#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
+#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
+
+/* Macro to set a bit (0..23) */
+#define BITMASK(i) ( (1<<(11-(i)%12+3)) << ((i)<12?16:0) )
+
+/*
+ * sb arrays:
+ *
+ * Workhorses of the inner loop of the DES implementation.
+ * They do sbox lookup, shifting of this value, 32 bit
+ * permutation and E permutation for the next round.
+ *
+ * Kept in 'BITMASK' format.
+ */
+
+#ifdef _UFC_32_
+long32 _ufc_sb0[8192], _ufc_sb1[8192], _ufc_sb2[8192], _ufc_sb3[8192];
+static long32 *sb[4] = {_ufc_sb0, _ufc_sb1, _ufc_sb2, _ufc_sb3};
+#endif
+
+#ifdef _UFC_64_
+long64 _ufc_sb0[4096], _ufc_sb1[4096], _ufc_sb2[4096], _ufc_sb3[4096];
+static long64 *sb[4] = {_ufc_sb0, _ufc_sb1, _ufc_sb2, _ufc_sb3};
+#endif
+
+/*
+ * eperm32tab: do 32 bit permutation and E selection
+ *
+ * The first index is the byte number in the 32 bit value to be permuted
+ * - second - is the value of this byte
+ * - third - selects the two 32 bit values
+ *
+ * The table is used and generated internally in init_des to speed it up
+ */
+static ufc_long eperm32tab[4][256][2];
+
+/*
+ * do_pc1: permform pc1 permutation in the key schedule generation.
+ *
+ * The first index is the byte number in the 8 byte ASCII key
+ * - second - - the two 28 bits halfs of the result
+ * - third - selects the 7 bits actually used of each byte
+ *
+ * The result is kept with 28 bit per 32 bit with the 4 most significant
+ * bits zero.
+ */
+static ufc_long do_pc1[8][2][128];
+
+/*
+ * do_pc2: permform pc2 permutation in the key schedule generation.
+ *
+ * The first index is the septet number in the two 28 bit intermediate values
+ * - second - - - septet values
+ *
+ * Knowledge of the structure of the pc2 permutation is used.
+ *
+ * The result is kept with 28 bit per 32 bit with the 4 most significant
+ * bits zero.
+ */
+static ufc_long do_pc2[8][128];
+
+/*
+ * efp: undo an extra e selection and do final
+ * permutation giving the DES result.
+ *
+ * Invoked 6 bit a time on two 48 bit values
+ * giving two 32 bit longs.
+ */
+static ufc_long efp[16][64][2];
+
+static unsigned char bytemask[8] = {
+ 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01
+};
+
+static ufc_long longmask[32] = {
+ 0x80000000, 0x40000000, 0x20000000, 0x10000000,
+ 0x08000000, 0x04000000, 0x02000000, 0x01000000,
+ 0x00800000, 0x00400000, 0x00200000, 0x00100000,
+ 0x00080000, 0x00040000, 0x00020000, 0x00010000,
+ 0x00008000, 0x00004000, 0x00002000, 0x00001000,
+ 0x00000800, 0x00000400, 0x00000200, 0x00000100,
+ 0x00000080, 0x00000040, 0x00000020, 0x00000010,
+ 0x00000008, 0x00000004, 0x00000002, 0x00000001
+};
+
+
+/*
+ * Silly rewrite of 'bzero'. I do so
+ * because some machines don't have
+ * bzero and some don't have memset.
+ */
+
+static void clearmem(char *start, int cnt)
+ { while(cnt--)
+ *start++ = '\0';
+ }
+
+static int initialized = 0;
+
+/* lookup a 6 bit value in sbox */
+
+#define s_lookup(i,s) sbox[(i)][(((s)>>4) & 0x2)|((s) & 0x1)][((s)>>1) & 0xf];
+
+/*
+ * Initialize unit - may be invoked directly
+ * by fcrypt users.
+ */
+
+static void ufc_init_des(void)
+ { int comes_from_bit;
+ int bit, sg;
+ ufc_long j;
+ ufc_long mask1, mask2;
+
+ /*
+ * Create the do_pc1 table used
+ * to affect pc1 permutation
+ * when generating keys
+ */
+ for(bit = 0; bit < 56; bit++) {
+ comes_from_bit = pc1[bit] - 1;
+ mask1 = bytemask[comes_from_bit % 8 + 1];
+ mask2 = longmask[bit % 28 + 4];
+ for(j = 0; j < 128; j++) {
+ if(j & mask1)
+ do_pc1[comes_from_bit / 8][bit / 28][j] |= mask2;
+ }
+ }
+
+ /*
+ * Create the do_pc2 table used
+ * to affect pc2 permutation when
+ * generating keys
+ */
+ for(bit = 0; bit < 48; bit++) {
+ comes_from_bit = pc2[bit] - 1;
+ mask1 = bytemask[comes_from_bit % 7 + 1];
+ mask2 = BITMASK(bit % 24);
+ for(j = 0; j < 128; j++) {
+ if(j & mask1)
+ do_pc2[comes_from_bit / 7][j] |= mask2;
+ }
+ }
+
+ /*
+ * Now generate the table used to do combined
+ * 32 bit permutation and e expansion
+ *
+ * We use it because we have to permute 16384 32 bit
+ * longs into 48 bit in order to initialize sb.
+ *
+ * Looping 48 rounds per permutation becomes
+ * just too slow...
+ *
+ */
+
+ clearmem((char*)eperm32tab, sizeof(eperm32tab));
+
+ for(bit = 0; bit < 48; bit++) {
+ ufc_long inner_mask1,comes_from;
+
+ comes_from = perm32[esel[bit]-1]-1;
+ inner_mask1 = bytemask[comes_from % 8];
+
+ for(j = 256; j--;) {
+ if(j & inner_mask1)
+ eperm32tab[comes_from / 8][j][bit / 24] |= BITMASK(bit % 24);
+ }
+ }
+
+ /*
+ * Create the sb tables:
+ *
+ * For each 12 bit segment of an 48 bit intermediate
+ * result, the sb table precomputes the two 4 bit
+ * values of the sbox lookups done with the two 6
+ * bit halves, shifts them to their proper place,
+ * sends them through perm32 and finally E expands
+ * them so that they are ready for the next
+ * DES round.
+ *
+ */
+ for(sg = 0; sg < 4; sg++) {
+ int j1, j2;
+ int s1, s2;
+
+ for(j1 = 0; j1 < 64; j1++) {
+ s1 = s_lookup(2 * sg, j1);
+ for(j2 = 0; j2 < 64; j2++) {
+ ufc_long to_permute, inx;
+
+ s2 = s_lookup(2 * sg + 1, j2);
+ to_permute = ((s1 << 4) | s2) << (24 - 8 * sg);
+
+#ifdef _UFC_32_
+ inx = ((j1 << 6) | j2) << 1;
+ sb[sg][inx ] = eperm32tab[0][(to_permute >> 24) & 0xff][0];
+ sb[sg][inx+1] = eperm32tab[0][(to_permute >> 24) & 0xff][1];
+ sb[sg][inx ] |= eperm32tab[1][(to_permute >> 16) & 0xff][0];
+ sb[sg][inx+1] |= eperm32tab[1][(to_permute >> 16) & 0xff][1];
+ sb[sg][inx ] |= eperm32tab[2][(to_permute >> 8) & 0xff][0];
+ sb[sg][inx+1] |= eperm32tab[2][(to_permute >> 8) & 0xff][1];
+ sb[sg][inx ] |= eperm32tab[3][(to_permute) & 0xff][0];
+ sb[sg][inx+1] |= eperm32tab[3][(to_permute) & 0xff][1];
+#endif
+#ifdef _UFC_64_
+ inx = ((j1 << 6) | j2);
+ sb[sg][inx] =
+ ((long64)eperm32tab[0][(to_permute >> 24) & 0xff][0] << 32) |
+ (long64)eperm32tab[0][(to_permute >> 24) & 0xff][1];
+ sb[sg][inx] |=
+ ((long64)eperm32tab[1][(to_permute >> 16) & 0xff][0] << 32) |
+ (long64)eperm32tab[1][(to_permute >> 16) & 0xff][1];
+ sb[sg][inx] |=
+ ((long64)eperm32tab[2][(to_permute >> 8) & 0xff][0] << 32) |
+ (long64)eperm32tab[2][(to_permute >> 8) & 0xff][1];
+ sb[sg][inx] |=
+ ((long64)eperm32tab[3][(to_permute) & 0xff][0] << 32) |
+ (long64)eperm32tab[3][(to_permute) & 0xff][1];
+#endif
+ }
+ }
+ }
+
+ /*
+ * Create an inverse matrix for esel telling
+ * where to plug out bits if undoing it
+ */
+ for(bit=48; bit--;) {
+ e_inverse[esel[bit] - 1 ] = bit;
+ e_inverse[esel[bit] - 1 + 32] = bit + 48;
+ }
+
+ /*
+ * create efp: the matrix used to
+ * undo the E expansion and effect final permutation
+ */
+ clearmem((char*)efp, sizeof efp);
+ for(bit = 0; bit < 64; bit++) {
+ int o_bit, o_long;
+ ufc_long word_value, inner_mask1, inner_mask2;
+ int comes_from_f_bit, comes_from_e_bit;
+ int comes_from_word, bit_within_word;
+
+ /* See where bit i belongs in the two 32 bit long's */
+ o_long = bit / 32; /* 0..1 */
+ o_bit = bit % 32; /* 0..31 */
+
+ /*
+ * And find a bit in the e permutated value setting this bit.
+ *
+ * Note: the e selection may have selected the same bit several
+ * times. By the initialization of e_inverse, we only look
+ * for one specific instance.
+ */
+ comes_from_f_bit = final_perm[bit] - 1; /* 0..63 */
+ comes_from_e_bit = e_inverse[comes_from_f_bit]; /* 0..95 */
+ comes_from_word = comes_from_e_bit / 6; /* 0..15 */
+ bit_within_word = comes_from_e_bit % 6; /* 0..5 */
+
+ inner_mask1 = longmask[bit_within_word + 26];
+ inner_mask2 = longmask[o_bit];
+
+ for(word_value = 64; word_value--;) {
+ if(word_value & inner_mask1)
+ efp[comes_from_word][word_value][o_long] |= inner_mask2;
+ }
+ }
+ initialized++;
+ }
+
+/*
+ * Process the elements of the sb table permuting the
+ * bits swapped in the expansion by the current salt.
+ */
+
+#ifdef _UFC_32_
+static void shuffle_sb(long32 *k, ufc_long saltbits)
+ { ufc_long j;
+ long32 x;
+ for(j=4096; j--;) {
+ x = (k[0] ^ k[1]) & (long32)saltbits;
+ *k++ ^= x;
+ *k++ ^= x;
+ }
+ }
+#endif
+
+#ifdef _UFC_64_
+static void shuffle_sb(long64 *k, ufc_long saltbits)
+ { ufc_long j;
+ long64 x;
+ for(j=4096; j--;) {
+ x = ((*k >> 32) ^ *k) & (long64)saltbits;
+ *k++ ^= (x << 32) | x;
+ }
+ }
+#endif
+
+/*
+ * Setup the unit for a new salt
+ * Hopefully we'll not see a new salt in each crypt call.
+ */
+
+static unsigned char current_salt[3] = "&&"; /* invalid value */
+static ufc_long current_saltbits = 0;
+static int direction = 0;
+
+static void setup_salt(const char *s1)
+ { ufc_long i, j, saltbits;
+ const unsigned char *s2 = (const unsigned char *)s1;
+
+ if(!initialized)
+ ufc_init_des();
+
+ if(s2[0] == current_salt[0] && s2[1] == current_salt[1])
+ return;
+ current_salt[0] = s2[0]; current_salt[1] = s2[1];
+
+ /*
+ * This is the only crypt change to DES:
+ * entries are swapped in the expansion table
+ * according to the bits set in the salt.
+ */
+ saltbits = 0;
+ for(i = 0; i < 2; i++) {
+ long c=ascii_to_bin(s2[i]);
+ if(c < 0 || c > 63)
+ c = 0;
+ for(j = 0; j < 6; j++) {
+ if((c >> j) & 0x1)
+ saltbits |= BITMASK(6 * i + j);
+ }
+ }
+
+ /*
+ * Permute the sb table values
+ * to reflect the changed e
+ * selection table
+ */
+ shuffle_sb(_ufc_sb0, current_saltbits ^ saltbits);
+ shuffle_sb(_ufc_sb1, current_saltbits ^ saltbits);
+ shuffle_sb(_ufc_sb2, current_saltbits ^ saltbits);
+ shuffle_sb(_ufc_sb3, current_saltbits ^ saltbits);
+
+ current_saltbits = saltbits;
+ }
+
+static void ufc_mk_keytab(char *key)
+ { ufc_long v1, v2, *k1;
+ int i;
+#ifdef _UFC_32_
+ long32 v, *k2 = &_ufc_keytab[0][0];
+#endif
+#ifdef _UFC_64_
+ long64 v, *k2 = &_ufc_keytab[0];
+#endif
+
+ v1 = v2 = 0; k1 = &do_pc1[0][0][0];
+ for(i = 8; i--;) {
+ v1 |= k1[*key & 0x7f]; k1 += 128;
+ v2 |= k1[*key++ & 0x7f]; k1 += 128;
+ }
+
+ for(i = 0; i < 16; i++) {
+ k1 = &do_pc2[0][0];
+
+ v1 = (v1 << rots[i]) | (v1 >> (28 - rots[i]));
+ v = k1[(v1 >> 21) & 0x7f]; k1 += 128;
+ v |= k1[(v1 >> 14) & 0x7f]; k1 += 128;
+ v |= k1[(v1 >> 7) & 0x7f]; k1 += 128;
+ v |= k1[(v1 ) & 0x7f]; k1 += 128;
+
+#ifdef _UFC_32_
+ *k2++ = v;
+ v = 0;
+#endif
+#ifdef _UFC_64_
+ v <<= 32;
+#endif
+
+ v2 = (v2 << rots[i]) | (v2 >> (28 - rots[i]));
+ v |= k1[(v2 >> 21) & 0x7f]; k1 += 128;
+ v |= k1[(v2 >> 14) & 0x7f]; k1 += 128;
+ v |= k1[(v2 >> 7) & 0x7f]; k1 += 128;
+ v |= k1[(v2 ) & 0x7f];
+
+ *k2++ = v;
+ }
+
+ direction = 0;
+ }
+
+/*
+ * Undo an extra E selection and do final permutations
+ */
+
+ufc_long *_ufc_dofinalperm(ufc_long l1, ufc_long l2, ufc_long r1, ufc_long r2)
+ { ufc_long v1, v2, x;
+ static ufc_long ary[2];
+
+ x = (l1 ^ l2) & current_saltbits; l1 ^= x; l2 ^= x;
+ x = (r1 ^ r2) & current_saltbits; r1 ^= x; r2 ^= x;
+
+ v1=v2=0; l1 >>= 3; l2 >>= 3; r1 >>= 3; r2 >>= 3;
+
+ v1 |= efp[15][ r2 & 0x3f][0]; v2 |= efp[15][ r2 & 0x3f][1];
+ v1 |= efp[14][(r2 >>= 6) & 0x3f][0]; v2 |= efp[14][ r2 & 0x3f][1];
+ v1 |= efp[13][(r2 >>= 10) & 0x3f][0]; v2 |= efp[13][ r2 & 0x3f][1];
+ v1 |= efp[12][(r2 >>= 6) & 0x3f][0]; v2 |= efp[12][ r2 & 0x3f][1];
+
+ v1 |= efp[11][ r1 & 0x3f][0]; v2 |= efp[11][ r1 & 0x3f][1];
+ v1 |= efp[10][(r1 >>= 6) & 0x3f][0]; v2 |= efp[10][ r1 & 0x3f][1];
+ v1 |= efp[ 9][(r1 >>= 10) & 0x3f][0]; v2 |= efp[ 9][ r1 & 0x3f][1];
+ v1 |= efp[ 8][(r1 >>= 6) & 0x3f][0]; v2 |= efp[ 8][ r1 & 0x3f][1];
+
+ v1 |= efp[ 7][ l2 & 0x3f][0]; v2 |= efp[ 7][ l2 & 0x3f][1];
+ v1 |= efp[ 6][(l2 >>= 6) & 0x3f][0]; v2 |= efp[ 6][ l2 & 0x3f][1];
+ v1 |= efp[ 5][(l2 >>= 10) & 0x3f][0]; v2 |= efp[ 5][ l2 & 0x3f][1];
+ v1 |= efp[ 4][(l2 >>= 6) & 0x3f][0]; v2 |= efp[ 4][ l2 & 0x3f][1];
+
+ v1 |= efp[ 3][ l1 & 0x3f][0]; v2 |= efp[ 3][ l1 & 0x3f][1];
+ v1 |= efp[ 2][(l1 >>= 6) & 0x3f][0]; v2 |= efp[ 2][ l1 & 0x3f][1];
+ v1 |= efp[ 1][(l1 >>= 10) & 0x3f][0]; v2 |= efp[ 1][ l1 & 0x3f][1];
+ v1 |= efp[ 0][(l1 >>= 6) & 0x3f][0]; v2 |= efp[ 0][ l1 & 0x3f][1];
+
+ ary[0] = v1; ary[1] = v2;
+ return ary;
+ }
+
+/*
+ * crypt only: convert from 64 bit to 11 bit ASCII
+ * prefixing with the salt
+ */
+
+static char *output_conversion(ufc_long v1, ufc_long v2, const char *salt)
+ { static char outbuf[14];
+ int i, s;
+
+ outbuf[0] = salt[0];
+ outbuf[1] = salt[1] ? salt[1] : salt[0];
+
+ for(i = 0; i < 5; i++)
+ outbuf[i + 2] = bin_to_ascii((v1 >> (26 - 6 * i)) & 0x3f);
+
+ s = (v2 & 0xf) << 2;
+ v2 = (v2 >> 2) | ((v1 & 0x3) << 30);
+
+ for(i = 5; i < 10; i++)
+ outbuf[i + 2] = bin_to_ascii((v2 >> (56 - 6 * i)) & 0x3f);
+
+ outbuf[12] = bin_to_ascii(s);
+ outbuf[13] = 0;
+
+ return outbuf;
+ }
+
+/*
+ * UNIX crypt function
+ */
+
+static ufc_long *_ufc_doit(ufc_long , ufc_long, ufc_long, ufc_long, ufc_long);
+
+char *ufc_crypt(const char *key,const char *salt)
+ { ufc_long *s;
+ char ktab[9];
+
+ /*
+ * Hack DES tables according to salt
+ */
+ setup_salt(salt);
+
+ /*
+ * Setup key schedule
+ */
+ clearmem(ktab, sizeof ktab);
+ strncpy(ktab, key, 8);
+ ufc_mk_keytab(ktab);
+
+ /*
+ * Go for the 25 DES encryptions
+ */
+ s = _ufc_doit((ufc_long)0, (ufc_long)0,
+ (ufc_long)0, (ufc_long)0, (ufc_long)25);
+
+ /*
+ * And convert back to 6 bit ASCII
+ */
+ return output_conversion(s[0], s[1], salt);
+ }
+
+
+#ifdef _UFC_32_
+
+/*
+ * 32 bit version
+ */
+
+extern long32 _ufc_keytab[16][2];
+extern long32 _ufc_sb0[], _ufc_sb1[], _ufc_sb2[], _ufc_sb3[];
+
+#define SBA(sb, v) (*(long32*)((char*)(sb)+(v)))
+
+static ufc_long *_ufc_doit(ufc_long l1, ufc_long l2, ufc_long r1, ufc_long r2, ufc_long itr)
+ { int i;
+ long32 s, *k;
+
+ while(itr--) {
+ k = &_ufc_keytab[0][0];
+ for(i=8; i--; ) {
+ s = *k++ ^ r1;
+ l1 ^= SBA(_ufc_sb1, s & 0xffff); l2 ^= SBA(_ufc_sb1, (s & 0xffff)+4);
+ l1 ^= SBA(_ufc_sb0, s >>= 16); l2 ^= SBA(_ufc_sb0, (s) +4);
+ s = *k++ ^ r2;
+ l1 ^= SBA(_ufc_sb3, s & 0xffff); l2 ^= SBA(_ufc_sb3, (s & 0xffff)+4);
+ l1 ^= SBA(_ufc_sb2, s >>= 16); l2 ^= SBA(_ufc_sb2, (s) +4);
+
+ s = *k++ ^ l1;
+ r1 ^= SBA(_ufc_sb1, s & 0xffff); r2 ^= SBA(_ufc_sb1, (s & 0xffff)+4);
+ r1 ^= SBA(_ufc_sb0, s >>= 16); r2 ^= SBA(_ufc_sb0, (s) +4);
+ s = *k++ ^ l2;
+ r1 ^= SBA(_ufc_sb3, s & 0xffff); r2 ^= SBA(_ufc_sb3, (s & 0xffff)+4);
+ r1 ^= SBA(_ufc_sb2, s >>= 16); r2 ^= SBA(_ufc_sb2, (s) +4);
+ }
+ s=l1; l1=r1; r1=s; s=l2; l2=r2; r2=s;
+ }
+ return _ufc_dofinalperm(l1, l2, r1, r2);
+ }
+
+#endif
+
+#ifdef _UFC_64_
+
+/*
+ * 64 bit version
+ */
+
+extern long64 _ufc_keytab[16];
+extern long64 _ufc_sb0[], _ufc_sb1[], _ufc_sb2[], _ufc_sb3[];
+
+#define SBA(sb, v) (*(long64*)((char*)(sb)+(v)))
+
+static ufc_long *_ufc_doit(ufc_long l1, ufc_long l2, ufc_long r1, ufc_long r2, ufc_long itr)
+ { int i;
+ long64 l, r, s, *k;
+
+ l = (((long64)l1) << 32) | ((long64)l2);
+ r = (((long64)r1) << 32) | ((long64)r2);
+
+ while(itr--) {
+ k = &_ufc_keytab[0];
+ for(i=8; i--; ) {
+ s = *k++ ^ r;
+ l ^= SBA(_ufc_sb3, (s >> 0) & 0xffff);
+ l ^= SBA(_ufc_sb2, (s >> 16) & 0xffff);
+ l ^= SBA(_ufc_sb1, (s >> 32) & 0xffff);
+ l ^= SBA(_ufc_sb0, (s >> 48) & 0xffff);
+
+ s = *k++ ^ l;
+ r ^= SBA(_ufc_sb3, (s >> 0) & 0xffff);
+ r ^= SBA(_ufc_sb2, (s >> 16) & 0xffff);
+ r ^= SBA(_ufc_sb1, (s >> 32) & 0xffff);
+ r ^= SBA(_ufc_sb0, (s >> 48) & 0xffff);
+ }
+ s=l; l=r; r=s;
+ }
+
+ l1 = l >> 32; l2 = l & 0xffffffff;
+ r1 = r >> 32; r2 = r & 0xffffffff;
+ return _ufc_dofinalperm(l1, l2, r1, r2);
+ }
+
+#endif
+
+#define crypt ufc_crypt
+#endif
+
+main()
+{
+ char passwd[9];
+ char salt[9];
+ char c_out1[256];
+ char c_out2[256];
+
+ char expected_out[14];
+
+ strcpy(expected_out, "12yJ.Of/NQ.Pk");
+ strcpy(passwd, "12345678");
+ strcpy(salt, "12345678");
+
+ strcpy(c_out1, crypt(passwd, salt));
+ salt[2] = '\0';
+ strcpy(c_out2, crypt(passwd, salt));
+
+ /*
+ * If the non-trucated salt fails but the
+ * truncated salt succeeds then exit 1.
+ */
+
+ if((strcmp(c_out1, expected_out) != 0) &&
+ (strcmp(c_out2, expected_out) == 0))
+ exit(1);
+
+#ifdef HAVE_BIGCRYPT
+ /*
+ * Try the same with bigcrypt...
+ */
+
+ {
+ char big_passwd[17];
+ char big_salt[17];
+ char big_c_out1[256];
+ char big_c_out2[256];
+ char big_expected_out[27];
+
+ strcpy(big_passwd, "1234567812345678");
+ strcpy(big_salt, "1234567812345678");
+ strcpy(big_expected_out, "12yJ.Of/NQ.PklfyCuHi/rwM");
+
+ strcpy(big_c_out1, bigcrypt(big_passwd, big_salt));
+ big_salt[2] = '\0';
+ strcpy(big_c_out2, bigcrypt(big_passwd, big_salt));
+
+ /*
+ * If the non-trucated salt fails but the
+ * truncated salt succeeds then exit 1.
+ */
+
+ if((strcmp(big_c_out1, big_expected_out) != 0) &&
+ (strcmp(big_c_out2, big_expected_out) == 0))
+ exit(1);
+
+ }
+#endif
+
+ exit(0);
+}
diff --git a/source/tests/fcntl_lock.c b/source/tests/fcntl_lock.c
new file mode 100644
index 00000000000..3dc12a38973
--- /dev/null
+++ b/source/tests/fcntl_lock.c
@@ -0,0 +1,121 @@
+/* test whether fcntl locking works on this system */
+
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#include <errno.h>
+
+static int sys_waitpid(pid_t pid,int *status,int options)
+{
+#ifdef HAVE_WAITPID
+ return waitpid(pid,status,options);
+#else /* USE_WAITPID */
+ return wait4(pid, status, options, NULL);
+#endif /* USE_WAITPID */
+}
+
+#define DATA "conftest.fcntl"
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+/* lock a byte range in a open file */
+int main(int argc, char *argv[])
+{
+ struct flock lock;
+ int fd, ret, status=1;
+ pid_t pid;
+ char *testdir = NULL;
+
+ testdir = getenv("TESTDIR");
+ if (testdir) chdir(testdir);
+
+ alarm(10);
+
+ if (!(pid=fork())) {
+ sleep(2);
+ fd = open(DATA, O_RDONLY);
+
+ if (fd == -1) {
+ fprintf(stderr,"ERROR: failed to open %s (errno=%d)\n",
+ DATA, (int)errno);
+ exit(1);
+ }
+
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 4;
+ lock.l_pid = getpid();
+
+ lock.l_type = F_WRLCK;
+
+ /* check if a lock applies */
+ ret = fcntl(fd,F_GETLK,&lock);
+
+ if ((ret == -1) ||
+ (lock.l_type == F_UNLCK)) {
+ fprintf(stderr,"ERROR: lock test failed (ret=%d errno=%d)\n", ret, (int)errno);
+ exit(1);
+ } else {
+ exit(0);
+ }
+ }
+
+ unlink(DATA);
+ fd = open(DATA, O_RDWR|O_CREAT|O_EXCL, 0600);
+
+ if (fd == -1) {
+ fprintf(stderr,"ERROR: failed to open %s (errno=%d)\n",
+ DATA, (int)errno);
+ exit(1);
+ }
+
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 4;
+ lock.l_pid = getpid();
+
+ /* set a 4 byte write lock */
+ fcntl(fd,F_SETLK,&lock);
+
+ sys_waitpid(pid, &status, 0);
+
+ unlink(DATA);
+
+#if defined(WIFEXITED) && defined(WEXITSTATUS)
+ if(WIFEXITED(status)) {
+ status = WEXITSTATUS(status);
+ } else {
+ status = 1;
+ }
+#else /* defined(WIFEXITED) && defined(WEXITSTATUS) */
+ status = (status == 0) ? 0 : 1;
+#endif /* defined(WIFEXITED) && defined(WEXITSTATUS) */
+
+ if (status) {
+ fprintf(stderr,"ERROR: lock test failed with status=%d\n",
+ status);
+ }
+
+ exit(status);
+}
diff --git a/source/tests/fcntl_lock64.c b/source/tests/fcntl_lock64.c
new file mode 100644
index 00000000000..e5ecd88fd04
--- /dev/null
+++ b/source/tests/fcntl_lock64.c
@@ -0,0 +1,96 @@
+/* test whether 64 bit fcntl locking really works on this system */
+
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+
+#include <errno.h>
+
+static int sys_waitpid(pid_t pid,int *status,int options)
+{
+#ifdef HAVE_WAITPID
+ return waitpid(pid,status,options);
+#else /* USE_WAITPID */
+ return wait4(pid, status, options, NULL);
+#endif /* USE_WAITPID */
+}
+
+#define DATA "conftest.fcntl64"
+
+/* lock a byte range in a open file */
+int main(int argc, char *argv[])
+{
+ struct flock64 lock;
+ int fd, ret, status=1;
+ pid_t pid;
+
+ if (!(pid=fork())) {
+ sleep(2);
+ fd = open64(DATA, O_RDONLY);
+
+ if (fd == -1) exit(1);
+
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 4;
+ lock.l_pid = getpid();
+
+ lock.l_type = F_WRLCK;
+
+ /* check if a lock applies */
+ ret = fcntl(fd,F_GETLK64,&lock);
+
+ if ((ret == -1) ||
+ (lock.l_type == F_UNLCK)) {
+/* printf("No lock conflict\n"); */
+ exit(1);
+ } else {
+/* printf("lock conflict\n"); */
+ exit(0);
+ }
+ }
+
+ fd = open64(DATA, O_RDWR|O_CREAT|O_TRUNC, 0600);
+
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+#if defined(COMPILER_SUPPORTS_LL)
+ lock.l_start = 0x100000000LL;
+#else
+ lock.l_start = 0x100000000;
+#endif
+ lock.l_len = 4;
+ lock.l_pid = getpid();
+
+ /* set a 4 byte write lock */
+ fcntl(fd,F_SETLK64,&lock);
+
+ sys_waitpid(pid, &status, 0);
+
+#if defined(WIFEXITED) && defined(WEXITSTATUS)
+ if(WIFEXITED(status)) {
+ status = WEXITSTATUS(status);
+ } else {
+ status = 1;
+ }
+#else /* defined(WIFEXITED) && defined(WEXITSTATUS) */
+ status = (status == 0) ? 0 : 1;
+#endif /* defined(WIFEXITED) && defined(WEXITSTATUS) */
+
+ unlink(DATA);
+
+ exit(status);
+}
diff --git a/source/tests/ftruncate.c b/source/tests/ftruncate.c
new file mode 100644
index 00000000000..93282782eed
--- /dev/null
+++ b/source/tests/ftruncate.c
@@ -0,0 +1,27 @@
+/* test whether ftruncte() can extend a file */
+
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define DATA "conftest.trunc"
+#define LEN 7663
+
+main()
+{
+ int *buf;
+ int fd = open(DATA,O_RDWR|O_CREAT|O_TRUNC,0666);
+
+ ftruncate(fd, LEN);
+
+ unlink(DATA);
+
+ if (lseek(fd, 0, SEEK_END) == LEN) {
+ exit(0);
+ }
+ exit(1);
+}
diff --git a/source/tests/getgroups.c b/source/tests/getgroups.c
new file mode 100644
index 00000000000..343fd5a184f
--- /dev/null
+++ b/source/tests/getgroups.c
@@ -0,0 +1,66 @@
+/* this tests whether getgroups actually returns lists of integers
+ rather than gid_t. The test only works if the user running
+ the test is in at least 1 group
+
+ The test is designed to check for those broken OSes that define
+ getgroups() as returning an array of gid_t but actually return a
+ array of ints! Ultrix is one culprit
+ */
+
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <grp.h>
+
+main()
+{
+ int i;
+ int *igroups;
+ char *cgroups;
+ int grp = 0;
+ int ngroups = getgroups(0,&grp);
+
+ if (sizeof(gid_t) == sizeof(int)) {
+ fprintf(stderr,"gid_t and int are the same size\n");
+ exit(1);
+ }
+
+ if (ngroups <= 0)
+ ngroups = 32;
+
+ igroups = (int *)malloc(sizeof(int)*ngroups);
+
+ for (i=0;i<ngroups;i++)
+ igroups[i] = 0x42424242;
+
+ ngroups = getgroups(ngroups,(gid_t *)igroups);
+
+ if (igroups[0] == 0x42424242)
+ ngroups = 0;
+
+ if (ngroups == 0) {
+ printf("WARNING: can't determine getgroups return type\n");
+ exit(1);
+ }
+
+ cgroups = (char *)igroups;
+
+ if (ngroups == 1 &&
+ cgroups[2] == 0x42 && cgroups[3] == 0x42) {
+ fprintf(stderr,"getgroups returns gid_t\n");
+ exit(1);
+ }
+
+ for (i=0;i<ngroups;i++) {
+ if (igroups[i] == 0x42424242) {
+ fprintf(stderr,"getgroups returns gid_t\n");
+ exit(1);
+ }
+ }
+
+ exit(0);
+}
diff --git a/source/tests/shared_mmap.c b/source/tests/shared_mmap.c
new file mode 100644
index 00000000000..fcef75d0d61
--- /dev/null
+++ b/source/tests/shared_mmap.c
@@ -0,0 +1,68 @@
+/* this tests whether we can use a shared writeable mmap on a file -
+ as needed for the mmap varient of FAST_SHARE_MODES */
+
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define DATA "conftest.mmap"
+
+#ifndef MAP_FILE
+#define MAP_FILE 0
+#endif
+
+main()
+{
+ int *buf;
+ int i;
+ int fd = open(DATA,O_RDWR|O_CREAT|O_TRUNC,0666);
+ int count=7;
+
+ if (fd == -1) exit(1);
+
+ for (i=0;i<10000;i++) {
+ write(fd,&i,sizeof(i));
+ }
+
+ close(fd);
+
+ if (fork() == 0) {
+ fd = open(DATA,O_RDWR);
+ if (fd == -1) exit(1);
+
+ buf = (int *)mmap(NULL, 10000*sizeof(int),
+ (PROT_READ | PROT_WRITE),
+ MAP_FILE | MAP_SHARED,
+ fd, 0);
+
+ while (count-- && buf[9124] != 55732) sleep(1);
+
+ if (count <= 0) exit(1);
+
+ buf[1763] = 7268;
+ exit(0);
+ }
+
+ fd = open(DATA,O_RDWR);
+ if (fd == -1) exit(1);
+
+ buf = (int *)mmap(NULL, 10000*sizeof(int),
+ (PROT_READ | PROT_WRITE),
+ MAP_FILE | MAP_SHARED,
+ fd, 0);
+
+ if (buf == (int *)-1) exit(1);
+
+ buf[9124] = 55732;
+
+ while (count-- && buf[1763] != 7268) sleep(1);
+
+ unlink(DATA);
+
+ if (count > 0) exit(0);
+ exit(1);
+}
diff --git a/source/tests/shlib.c b/source/tests/shlib.c
new file mode 100644
index 00000000000..761d9fd5c57
--- /dev/null
+++ b/source/tests/shlib.c
@@ -0,0 +1,6 @@
+/* a trivial function used to test building shared libraries */
+
+int foo(void)
+{
+ return 1;
+}
diff --git a/source/tests/summary.c b/source/tests/summary.c
new file mode 100644
index 00000000000..79a530b0136
--- /dev/null
+++ b/source/tests/summary.c
@@ -0,0 +1,30 @@
+#include <stdio.h>
+
+main()
+{
+#if !(defined(HAVE_FCNTL_LOCK) || defined(HAVE_STRUCT_FLOCK64))
+ printf("ERROR: No locking available. Running Samba would be unsafe\n");
+ exit(1);
+#endif
+
+#if !(defined(HAVE_IFACE_IFCONF) || defined(HAVE_IFACE_IFREQ) || defined(HAVE_IFACE_AIX))
+ printf("WARNING: No automated network interface determination\n");
+#endif
+
+#if !(defined(USE_SETEUID) || defined(USE_SETREUID) || defined(USE_SETRESUID) || defined(USE_SETUIDX))
+ printf("ERROR: no seteuid method available\n");
+ exit(1);
+#endif
+
+#if !(defined(STAT_STATVFS) || defined(STAT_STATVFS64) || defined(STAT_STATFS3_OSF1) || defined(STAT_STATFS2_BSIZE) || defined(STAT_STATFS4) || defined(STAT_STATFS2_FSIZE) || defined(STAT_STATFS2_FS_DATA))
+ printf("ERROR: No disk free routine!\n");
+ exit(1);
+#endif
+
+#if !((defined(HAVE_RANDOM) || defined(HAVE_RAND)) && (defined(HAVE_SRANDOM) || defined(HAVE_SRAND)))
+ printf("ERROR: No random or srandom routine!\n");
+ exit(1);
+#endif
+
+ exit(0);
+}
diff --git a/source/tests/sysquotas.c b/source/tests/sysquotas.c
new file mode 100644
index 00000000000..2aa643326c0
--- /dev/null
+++ b/source/tests/sysquotas.c
@@ -0,0 +1,94 @@
+/* this test should find out what quota api is available on the os */
+
+#if defined(HAVE_QUOTACTL_4A)
+/* long quotactl(int cmd, char *special, qid_t id, caddr_t addr) */
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_ASM_TYPES_H
+#include <asm/types.h>
+#endif
+
+#if defined(HAVE_LINUX_QUOTA_H)
+# include <linux/quota.h>
+# if defined(HAVE_STRUCT_IF_DQBLK)
+# define SYS_DQBLK if_dqblk
+# elif defined(HAVE_STRUCT_MEM_DQBLK)
+# define SYS_DQBLK mem_dqblk
+# endif
+#elif defined(HAVE_SYS_QUOTA_H)
+# include <sys/quota.h>
+#endif
+
+#ifndef SYS_DQBLK
+#define SYS_DQBLK dqblk
+#endif
+
+ int autoconf_quota(void)
+{
+ int ret = -1;
+ struct SYS_DQBLK D;
+
+ ret = quotactl(Q_GETQUOTA,"/dev/hda1",0,(void *)&D);
+
+ return ret;
+}
+
+#elif defined(HAVE_QUOTACTL_4B)
+/* int quotactl(const char *path, int cmd, int id, char *addr); */
+
+#ifdef HAVE_SYS_QUOTA_H
+#include <sys/quota.h>
+#else /* *BSD */
+#include <sys/types.h>
+#include <ufs/ufs/quota.h>
+#include <machine/param.h>
+#endif
+
+ int autoconf_quota(void)
+{
+ int ret = -1;
+ struct dqblk D;
+
+ ret = quotactl("/",Q_GETQUOTA,0,(char *) &D);
+
+ return ret;
+}
+
+#elif defined(HAVE_QUOTACTL_3)
+/* int quotactl (char *spec, int request, char *arg); */
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_QUOTA_H
+#include <sys/quota.h>
+#endif
+
+ int autoconf_quota(void)
+{
+ int ret = -1;
+ struct q_request request;
+
+ ret = quotactl("/", Q_GETQUOTA, &request);
+
+ return ret;
+}
+
+#elif defined(HAVE_QUOTACTL_2)
+
+#error HAVE_QUOTACTL_2 not implemented
+
+#else
+
+#error Unknow QUOTACTL prototype
+
+#endif
+
+ int main(void)
+{
+ autoconf_quota();
+ return 0;
+}
diff --git a/source/tests/trivial.c b/source/tests/trivial.c
new file mode 100644
index 00000000000..2723637a0ff
--- /dev/null
+++ b/source/tests/trivial.c
@@ -0,0 +1,4 @@
+main()
+{
+ exit(0);
+}
diff --git a/source/tests/unixsock.c b/source/tests/unixsock.c
new file mode 100644
index 00000000000..f2765d68f67
--- /dev/null
+++ b/source/tests/unixsock.c
@@ -0,0 +1,93 @@
+/* -*- c-file-style: "linux" -*-
+ *
+ * Try creating a Unix-domain socket, opening it, and reading from it.
+ * The POSIX name for these is AF_LOCAL/PF_LOCAL.
+ *
+ * This is used by the Samba autoconf scripts to detect systems which
+ * don't have Unix-domain sockets, such as (probably) VMS, or systems
+ * on which they are broken under some conditions, such as RedHat 7.0
+ * (unpatched). We can't build WinBind there at the moment.
+ *
+ * Coding standard says to always use exit() for this, not return, so
+ * we do.
+ *
+ * Martin Pool <mbp@samba.org>, June 2000. */
+
+/* TODO: Look for AF_LOCAL (most standard), AF_UNIX, and AF_FILE. */
+
+#include <stdio.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+
+#ifdef HAVE_SYS_UN_H
+# include <sys/un.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#if HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+#if HAVE_ERRNO_DECL
+# include <errno.h>
+#else
+extern int errno;
+#endif
+
+static int bind_socket(char const *filename)
+{
+ int sock_fd;
+ struct sockaddr_un name;
+ size_t size;
+
+ /* Create the socket. */
+ if ((sock_fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
+ perror ("socket(PF_LOCAL, SOCK_STREAM)");
+ exit(1);
+ }
+
+ /* Bind a name to the socket. */
+ name.sun_family = AF_LOCAL;
+ strncpy(name.sun_path, filename, sizeof (name.sun_path));
+
+ /* The size of the address is
+ the offset of the start of the filename,
+ plus its length,
+ plus one for the terminating null byte.
+ Alternatively you can just do:
+ size = SUN_LEN (&name);
+ */
+ size = SUN_LEN(&name);
+ /* XXX: This probably won't work on unfriendly libcs */
+
+ if (bind(sock_fd, (struct sockaddr *) &name, size) < 0) {
+ perror ("bind");
+ exit(1);
+ }
+
+ return sock_fd;
+}
+
+
+int main(void)
+{
+ int sock_fd;
+ int kid;
+ char const *filename = "conftest.unixsock.sock";
+
+ /* abolish hanging */
+ alarm(15); /* secs */
+
+ if ((sock_fd = bind_socket(filename)) < 0)
+ exit(1);
+
+ /* the socket will be deleted when autoconf cleans up these
+ files. */
+
+ exit(0);
+}
diff --git a/source/torture/cmd_vfs.c b/source/torture/cmd_vfs.c
new file mode 100644
index 00000000000..bfce4b88b41
--- /dev/null
+++ b/source/torture/cmd_vfs.c
@@ -0,0 +1,1060 @@
+/*
+ Unix SMB/CIFS implementation.
+ VFS module functions
+
+ Copyright (C) Simo Sorce 2002
+ Copyright (C) Eric Lorimer 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "vfstest.h"
+
+static const char *null_string = "";
+
+static NTSTATUS cmd_load_module(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int i;
+
+ if (argc < 2) {
+ printf("Usage: load <modules>\n");
+ return NT_STATUS_OK;
+ }
+
+ for (i=argc-1;i>0;i--) {
+ if (!vfs_init_custom(vfs->conn, argv[i])) {
+ DEBUG(0, ("load: (vfs_init_custom failed for %s)\n", argv[i]));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+ printf("load: ok\n");
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_populate(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ char c;
+ size_t size;
+ if (argc != 3) {
+ printf("Usage: populate <char> <size>\n");
+ return NT_STATUS_OK;
+ }
+ c = argv[1][0];
+ size = atoi(argv[2]);
+ vfs->data = (char *)talloc(mem_ctx, size);
+ if (vfs->data == NULL) {
+ printf("populate: error=-1 (not enough memory)");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ memset(vfs->data, c, size);
+ vfs->data_size = size;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_show_data(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ size_t offset;
+ size_t len;
+ if (argc != 1 && argc != 3) {
+ printf("Usage: showdata [<offset> <len>]\n");
+ return NT_STATUS_OK;
+ }
+ if (vfs->data == NULL || vfs->data_size == 0) {
+ printf("show_data: error=-1 (buffer empty)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (argc == 3) {
+ offset = atoi(argv[1]);
+ len = atoi(argv[2]);
+ } else {
+ offset = 0;
+ len = vfs->data_size;
+ }
+ if ((offset + len) > vfs->data_size) {
+ printf("show_data: error=-1 (not enough data in buffer)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ dump_data(0, (char *)(vfs->data) + offset, len);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_connect(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ SMB_VFS_CONNECT(vfs->conn, lp_servicename(vfs->conn->service), "vfstest");
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_disconnect(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ SMB_VFS_DISCONNECT(vfs->conn);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_disk_free(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ SMB_BIG_UINT diskfree, bsize, dfree, dsize;
+ if (argc != 2) {
+ printf("Usage: disk_free <path>\n");
+ return NT_STATUS_OK;
+ }
+
+ diskfree = SMB_VFS_DISK_FREE(vfs->conn, argv[1], False, &bsize, &dfree, &dsize);
+ printf("disk_free: %lu, bsize = %lu, dfree = %lu, dsize = %lu\n",
+ (unsigned long)diskfree,
+ (unsigned long)bsize,
+ (unsigned long)dfree,
+ (unsigned long)dsize);
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_opendir(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ if (argc != 2) {
+ printf("Usage: opendir <fname>\n");
+ return NT_STATUS_OK;
+ }
+
+ vfs->currentdir = SMB_VFS_OPENDIR(vfs->conn, argv[1]);
+ if (vfs->currentdir == NULL) {
+ printf("opendir error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("opendir: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_readdir(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ struct dirent *dent;
+
+ if (vfs->currentdir == NULL) {
+ printf("readdir: error=-1 (no open directory)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ dent = SMB_VFS_READDIR(vfs->conn, vfs->currentdir);
+ if (dent == NULL) {
+ printf("readdir: NULL\n");
+ return NT_STATUS_OK;
+ }
+
+ printf("readdir: %s\n", dent->d_name);
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_mkdir(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ if (argc != 2) {
+ printf("Usage: mkdir <path>\n");
+ return NT_STATUS_OK;
+ }
+
+ if (SMB_VFS_MKDIR(vfs->conn, argv[1], 00755) == -1) {
+ printf("mkdir error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("mkdir: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_closedir(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int ret;
+
+ if (vfs->currentdir == NULL) {
+ printf("closedir: failure (no directory open)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ret = SMB_VFS_CLOSEDIR(vfs->conn, vfs->currentdir);
+ if (ret == -1) {
+ printf("closedir failure: %s\n", strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("closedir: ok\n");
+ vfs->currentdir = NULL;
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_open(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int flags, fd;
+ mode_t mode;
+ const char *flagstr;
+
+ mode = 00400;
+
+ if (argc < 3 || argc > 5) {
+ printf("Usage: open <filename> <flags> <mode>\n");
+ printf(" flags: O = O_RDONLY\n");
+ printf(" R = O_RDWR\n");
+ printf(" W = O_WRONLY\n");
+ printf(" C = O_CREAT\n");
+ printf(" E = O_EXCL\n");
+ printf(" T = O_TRUNC\n");
+ printf(" A = O_APPEND\n");
+ printf(" N = O_NONBLOCK/O_NDELAY\n");
+#ifdef O_SYNC
+ printf(" S = O_SYNC\n");
+#endif
+#ifdef O_NOFOLLOW
+ printf(" F = O_NOFOLLOW\n");
+#endif
+ printf(" mode: see open.2\n");
+ printf(" mode is ignored if C flag not present\n");
+ printf(" mode defaults to 00400\n");
+ return NT_STATUS_OK;
+ }
+ flags = 0;
+ flagstr = argv[2];
+ while (*flagstr) {
+ switch (*flagstr) {
+ case 'O':
+ flags |= O_RDONLY;
+ break;
+ case 'R':
+ flags |= O_RDWR;
+ break;
+ case 'W':
+ flags |= O_WRONLY;
+ break;
+ case 'C':
+ flags |= O_CREAT;
+ break;
+ case 'E':
+ flags |= O_EXCL;
+ break;
+ case 'T':
+ flags |= O_TRUNC;
+ break;
+ case 'A':
+ flags |= O_APPEND;
+ break;
+ case 'N':
+ flags |= O_NONBLOCK;
+ break;
+#ifdef O_SYNC
+ case 'S':
+ flags |= O_SYNC;
+ break;
+#endif
+#ifdef O_NOFOLLOW
+ case 'F':
+ flags |= O_NOFOLLOW;
+ break;
+#endif
+ default:
+ printf("open: error=-1 (invalid flag!)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ flagstr++;
+ }
+ if ((flags & O_CREAT) && argc == 4) {
+ if (sscanf(argv[3], "%o", &mode) == 0) {
+ printf("open: error=-1 (invalid mode!)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ fd = SMB_VFS_OPEN(vfs->conn, argv[1], flags, mode);
+ if (fd == -1) {
+ printf("open: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ vfs->files[fd] = (struct files_struct *)malloc(sizeof(struct files_struct));
+ vfs->files[fd]->fsp_name = strdup(argv[1]);
+ vfs->files[fd]->fd = fd;
+ vfs->files[fd]->conn = vfs->conn;
+ printf("open: fd=%d\n", fd);
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_pathfunc(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int ret = -1;
+
+ if (argc != 2) {
+ printf("Usage: %s <path>\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (strcmp("rmdir", argv[0]) == 0 ) {
+ ret = SMB_VFS_RMDIR(vfs->conn, argv[1]);
+ } else if (strcmp("unlink", argv[0]) == 0 ) {
+ ret = SMB_VFS_UNLINK(vfs->conn, argv[1]);
+ } else if (strcmp("chdir", argv[0]) == 0 ) {
+ ret = SMB_VFS_CHDIR(vfs->conn, argv[1]);
+ } else {
+ printf("%s: error=%d (invalid function name!)\n", argv[0], errno);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (ret == -1) {
+ printf("%s: error=%d (%s)\n", argv[0], errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("%s: ok\n", argv[0]);
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_close(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int fd, ret;
+
+ if (argc != 2) {
+ printf("Usage: close <fd>\n");
+ return NT_STATUS_OK;
+ }
+
+ fd = atoi(argv[1]);
+ if (vfs->files[fd] == NULL) {
+ printf("close: error=-1 (invalid file descriptor)\n");
+ return NT_STATUS_OK;
+ }
+
+ ret = SMB_VFS_CLOSE(vfs->files[fd], fd);
+ if (ret == -1 )
+ printf("close: error=%d (%s)\n", errno, strerror(errno));
+ else
+ printf("close: ok\n");
+
+ SAFE_FREE(vfs->files[fd]->fsp_name);
+ SAFE_FREE(vfs->files[fd]);
+ vfs->files[fd] = NULL;
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_read(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int fd;
+ size_t size, rsize;
+
+ if (argc != 3) {
+ printf("Usage: read <fd> <size>\n");
+ return NT_STATUS_OK;
+ }
+
+ /* do some error checking on these */
+ fd = atoi(argv[1]);
+ size = atoi(argv[2]);
+ vfs->data = (char *)talloc(mem_ctx, size);
+ if (vfs->data == NULL) {
+ printf("read: error=-1 (not enough memory)");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ vfs->data_size = size;
+
+ rsize = SMB_VFS_READ(vfs->files[fd], fd, vfs->data, size);
+ if (rsize == -1) {
+ printf("read: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("read: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_write(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int fd, size, wsize;
+
+ if (argc != 3) {
+ printf("Usage: write <fd> <size>\n");
+ return NT_STATUS_OK;
+ }
+
+ /* some error checking should go here */
+ fd = atoi(argv[1]);
+ size = atoi(argv[2]);
+ if (vfs->data == NULL) {
+ printf("write: error=-1 (buffer empty, please populate it before writing)");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (vfs->data_size < size) {
+ printf("write: error=-1 (buffer too small, please put some more data in)");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ wsize = SMB_VFS_WRITE(vfs->files[fd], fd, vfs->data, size);
+
+ if (wsize == -1) {
+ printf("write: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("write: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_lseek(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int fd, offset, whence;
+ SMB_OFF_T pos;
+
+ if (argc != 4) {
+ printf("Usage: lseek <fd> <offset> <whence>\n...where whence is 1 => SEEK_SET, 2 => SEEK_CUR, 3 => SEEK_END\n");
+ return NT_STATUS_OK;
+ }
+
+ fd = atoi(argv[1]);
+ offset = atoi(argv[2]);
+ whence = atoi(argv[3]);
+ switch (whence) {
+ case 1: whence = SEEK_SET; break;
+ case 2: whence = SEEK_CUR; break;
+ default: whence = SEEK_END;
+ }
+
+ pos = SMB_VFS_LSEEK(vfs->files[fd], fd, offset, whence);
+ if (pos == (SMB_OFF_T)-1) {
+ printf("lseek: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("lseek: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_rename(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int ret;
+ if (argc != 3) {
+ printf("Usage: rename <old> <new>\n");
+ return NT_STATUS_OK;
+ }
+
+ ret = SMB_VFS_RENAME(vfs->conn, argv[1], argv[2]);
+ if (ret == -1) {
+ printf("rename: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("rename: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_fsync(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int ret, fd;
+ if (argc != 2) {
+ printf("Usage: fsync <fd>\n");
+ return NT_STATUS_OK;
+ }
+
+ fd = atoi(argv[1]);
+ ret = SMB_VFS_FSYNC(vfs->files[fd], fd);
+ if (ret == -1) {
+ printf("fsync: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("fsync: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_stat(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int ret;
+ const char *user;
+ const char *group;
+ struct passwd *pwd = NULL;
+ struct group *grp = NULL;
+ SMB_STRUCT_STAT st;
+
+ if (argc != 2) {
+ printf("Usage: stat <fname>\n");
+ return NT_STATUS_OK;
+ }
+
+ ret = SMB_VFS_STAT(vfs->conn, argv[1], &st);
+ if (ret == -1) {
+ printf("stat: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ pwd = sys_getpwuid(st.st_uid);
+ if (pwd != NULL) user = pwd->pw_name;
+ else user = null_string;
+ grp = sys_getgrgid(st.st_gid);
+ if (grp != NULL) group = grp->gr_name;
+ else group = null_string;
+
+ printf("stat: ok\n");
+ printf(" File: %s", argv[1]);
+ if (S_ISREG(st.st_mode)) printf(" Regular File\n");
+ else if (S_ISDIR(st.st_mode)) printf(" Directory\n");
+ else if (S_ISCHR(st.st_mode)) printf(" Character Device\n");
+ else if (S_ISBLK(st.st_mode)) printf(" Block Device\n");
+ else if (S_ISFIFO(st.st_mode)) printf(" Fifo\n");
+ else if (S_ISLNK(st.st_mode)) printf(" Symbolic Link\n");
+ else if (S_ISSOCK(st.st_mode)) printf(" Socket\n");
+ printf(" Size: %10u", (unsigned int)st.st_size);
+#ifdef HAVE_STAT_ST_BLOCKS
+ printf(" Blocks: %9u", (unsigned int)st.st_blocks);
+#endif
+#ifdef HAVE_STAT_ST_BLKSIZE
+ printf(" IO Block: %u\n", (unsigned int)st.st_blksize);
+#endif
+ printf(" Device: 0x%10x", (unsigned int)st.st_dev);
+ printf(" Inode: %10u", (unsigned int)st.st_ino);
+ printf(" Links: %10u\n", (unsigned int)st.st_nlink);
+ printf(" Access: %05o", (st.st_mode) & 007777);
+ printf(" Uid: %5lu/%.16s Gid: %5lu/%.16s\n", (unsigned long)st.st_uid, user,
+ (unsigned long)st.st_gid, group);
+ printf(" Access: %s", ctime(&(st.st_atime)));
+ printf(" Modify: %s", ctime(&(st.st_mtime)));
+ printf(" Change: %s", ctime(&(st.st_ctime)));
+
+ SAFE_FREE(pwd);
+ SAFE_FREE(grp);
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_fstat(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int fd;
+ const char *user;
+ const char *group;
+ struct passwd *pwd = NULL;
+ struct group *grp = NULL;
+ SMB_STRUCT_STAT st;
+
+ if (argc != 2) {
+ printf("Usage: fstat <fd>\n");
+ return NT_STATUS_OK;
+ }
+
+ fd = atoi(argv[1]);
+ if (fd < 0 || fd > 1024) {
+ printf("fstat: error=%d (file descriptor out of range)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+
+ if (vfs->files[fd] == NULL) {
+ printf("fstat: error=%d (invalid file descriptor)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+
+ if (SMB_VFS_FSTAT(vfs->files[fd], fd, &st) == -1) {
+ printf("fstat: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ pwd = sys_getpwuid(st.st_uid);
+ if (pwd != NULL) user = pwd->pw_name;
+ else user = null_string;
+ grp = sys_getgrgid(st.st_gid);
+ if (grp != NULL) group = grp->gr_name;
+ else group = null_string;
+
+ printf("fstat: ok\n");
+ if (S_ISREG(st.st_mode)) printf(" Regular File\n");
+ else if (S_ISDIR(st.st_mode)) printf(" Directory\n");
+ else if (S_ISCHR(st.st_mode)) printf(" Character Device\n");
+ else if (S_ISBLK(st.st_mode)) printf(" Block Device\n");
+ else if (S_ISFIFO(st.st_mode)) printf(" Fifo\n");
+ else if (S_ISLNK(st.st_mode)) printf(" Symbolic Link\n");
+ else if (S_ISSOCK(st.st_mode)) printf(" Socket\n");
+ printf(" Size: %10u", (unsigned int)st.st_size);
+#ifdef HAVE_STAT_ST_BLOCKS
+ printf(" Blocks: %9u", (unsigned int)st.st_blocks);
+#endif
+#ifdef HAVE_STAT_ST_BLKSIZE
+ printf(" IO Block: %u\n", (unsigned int)st.st_blksize);
+#endif
+ printf(" Device: 0x%10x", (unsigned int)st.st_dev);
+ printf(" Inode: %10u", (unsigned int)st.st_ino);
+ printf(" Links: %10u\n", (unsigned int)st.st_nlink);
+ printf(" Access: %05o", (st.st_mode) & 007777);
+ printf(" Uid: %5lu/%.16s Gid: %5lu/%.16s\n", (unsigned long)st.st_uid, user,
+ (unsigned long)st.st_gid, group);
+ printf(" Access: %s", ctime(&(st.st_atime)));
+ printf(" Modify: %s", ctime(&(st.st_mtime)));
+ printf(" Change: %s", ctime(&(st.st_ctime)));
+
+ SAFE_FREE(pwd);
+ SAFE_FREE(grp);
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_lstat(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ const char *user;
+ const char *group;
+ struct passwd *pwd = NULL;
+ struct group *grp = NULL;
+ SMB_STRUCT_STAT st;
+
+ if (argc != 2) {
+ printf("Usage: lstat <path>\n");
+ return NT_STATUS_OK;
+ }
+
+ if (SMB_VFS_LSTAT(vfs->conn, argv[1], &st) == -1) {
+ printf("lstat: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ pwd = sys_getpwuid(st.st_uid);
+ if (pwd != NULL) user = pwd->pw_name;
+ else user = null_string;
+ grp = sys_getgrgid(st.st_gid);
+ if (grp != NULL) group = grp->gr_name;
+ else group = null_string;
+
+ printf("lstat: ok\n");
+ if (S_ISREG(st.st_mode)) printf(" Regular File\n");
+ else if (S_ISDIR(st.st_mode)) printf(" Directory\n");
+ else if (S_ISCHR(st.st_mode)) printf(" Character Device\n");
+ else if (S_ISBLK(st.st_mode)) printf(" Block Device\n");
+ else if (S_ISFIFO(st.st_mode)) printf(" Fifo\n");
+ else if (S_ISLNK(st.st_mode)) printf(" Symbolic Link\n");
+ else if (S_ISSOCK(st.st_mode)) printf(" Socket\n");
+ printf(" Size: %10u", (unsigned int)st.st_size);
+#ifdef HAVE_STAT_ST_BLOCKS
+ printf(" Blocks: %9u", (unsigned int)st.st_blocks);
+#endif
+#ifdef HAVE_STAT_ST_BLKSIZE
+ printf(" IO Block: %u\n", (unsigned int)st.st_blksize);
+#endif
+ printf(" Device: 0x%10x", (unsigned int)st.st_dev);
+ printf(" Inode: %10u", (unsigned int)st.st_ino);
+ printf(" Links: %10u\n", (unsigned int)st.st_nlink);
+ printf(" Access: %05o", (st.st_mode) & 007777);
+ printf(" Uid: %5lu/%.16s Gid: %5lu/%.16s\n", (unsigned long)st.st_uid, user,
+ (unsigned long)st.st_gid, group);
+ printf(" Access: %s", ctime(&(st.st_atime)));
+ printf(" Modify: %s", ctime(&(st.st_mtime)));
+ printf(" Change: %s", ctime(&(st.st_ctime)));
+
+ SAFE_FREE(pwd);
+ SAFE_FREE(grp);
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_chmod(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ mode_t mode;
+ if (argc != 3) {
+ printf("Usage: chmod <path> <mode>\n");
+ return NT_STATUS_OK;
+ }
+
+ mode = atoi(argv[2]);
+ if (SMB_VFS_CHMOD(vfs->conn, argv[1], mode) == -1) {
+ printf("chmod: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("chmod: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_fchmod(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int fd;
+ mode_t mode;
+ if (argc != 3) {
+ printf("Usage: fchmod <fd> <mode>\n");
+ return NT_STATUS_OK;
+ }
+
+ fd = atoi(argv[1]);
+ mode = atoi(argv[2]);
+ if (fd < 0 || fd > 1024) {
+ printf("fchmod: error=%d (file descriptor out of range)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+ if (vfs->files[fd] == NULL) {
+ printf("fchmod: error=%d (invalid file descriptor)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+
+ if (SMB_VFS_FCHMOD(vfs->files[fd], fd, mode) == -1) {
+ printf("fchmod: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("fchmod: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_chown(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ uid_t uid;
+ gid_t gid;
+ if (argc != 4) {
+ printf("Usage: chown <path> <uid> <gid>\n");
+ return NT_STATUS_OK;
+ }
+
+ uid = atoi(argv[2]);
+ gid = atoi(argv[3]);
+ if (SMB_VFS_CHOWN(vfs->conn, argv[1], uid, gid) == -1) {
+ printf("chown: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("chown: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_fchown(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ uid_t uid;
+ gid_t gid;
+ int fd;
+ if (argc != 4) {
+ printf("Usage: fchown <fd> <uid> <gid>\n");
+ return NT_STATUS_OK;
+ }
+
+ uid = atoi(argv[2]);
+ gid = atoi(argv[3]);
+ fd = atoi(argv[1]);
+ if (fd < 0 || fd > 1024) {
+ printf("fchown: faliure=%d (file descriptor out of range)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+ if (vfs->files[fd] == NULL) {
+ printf("fchown: error=%d (invalid file descriptor)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+ if (SMB_VFS_FCHOWN(vfs->files[fd], fd, uid, gid) == -1) {
+ printf("fchown error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("fchown: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_getwd(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ char buf[PATH_MAX];
+ if (SMB_VFS_GETWD(vfs->conn, buf) == NULL) {
+ printf("getwd: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("getwd: %s\n", buf);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_utime(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ struct utimbuf times;
+ if (argc != 4) {
+ printf("Usage: utime <path> <access> <modify>\n");
+ return NT_STATUS_OK;
+ }
+ times.actime = atoi(argv[2]);
+ times.modtime = atoi(argv[3]);
+ if (SMB_VFS_UTIME(vfs->conn, argv[1], &times) != 0) {
+ printf("utime: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("utime: ok\n");
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_ftruncate(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int fd;
+ SMB_OFF_T off;
+ if (argc != 3) {
+ printf("Usage: ftruncate <fd> <length>\n");
+ return NT_STATUS_OK;
+ }
+
+ fd = atoi(argv[1]);
+ off = atoi(argv[2]);
+ if (fd < 0 || fd > 1024) {
+ printf("ftruncate: error=%d (file descriptor out of range)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+ if (vfs->files[fd] == NULL) {
+ printf("ftruncate: error=%d (invalid file descriptor)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+
+ if (SMB_VFS_FTRUNCATE(vfs->files[fd], fd, off) == -1) {
+ printf("ftruncate: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("ftruncate: ok\n");
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_lock(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ BOOL ret;
+ int fd;
+ int op;
+ long offset;
+ long count;
+ int type;
+ const char *typestr;
+
+ if (argc != 6) {
+ printf("Usage: lock <fd> <op> <offset> <count> <type>\n");
+ printf(" ops: G = F_GETLK\n");
+ printf(" S = F_SETLK\n");
+ printf(" W = F_SETLKW\n");
+ printf(" type: R = F_RDLCK\n");
+ printf(" W = F_WRLCK\n");
+ printf(" U = F_UNLCK\n");
+ return NT_STATUS_OK;
+ }
+
+ if (sscanf(argv[1], "%d", &fd) == 0) {
+ printf("lock: error=-1 (error parsing fd)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ op = 0;
+ switch (*argv[2]) {
+ case 'G':
+ op = F_GETLK;
+ break;
+ case 'S':
+ op = F_SETLK;
+ break;
+ case 'W':
+ op = F_SETLKW;
+ break;
+ default:
+ printf("lock: error=-1 (invalid op flag!)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (sscanf(argv[3], "%ld", &offset) == 0) {
+ printf("lock: error=-1 (error parsing fd)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (sscanf(argv[4], "%ld", &count) == 0) {
+ printf("lock: error=-1 (error parsing fd)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ type = 0;
+ typestr = argv[5];
+ while(*typestr) {
+ switch (*typestr) {
+ case 'R':
+ type |= F_RDLCK;
+ break;
+ case 'W':
+ type |= F_WRLCK;
+ break;
+ case 'U':
+ type |= F_UNLCK;
+ break;
+ default:
+ printf("lock: error=-1 (invalid type flag!)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ typestr++;
+ }
+
+ printf("lock: debug lock(fd=%d, op=%d, offset=%ld, count=%ld, type=%d))\n", fd, op, offset, count, type);
+
+ if ((ret = SMB_VFS_LOCK(vfs->files[fd], fd, op, offset, count, type)) == False) {
+ printf("lock: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("lock: ok\n");
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_symlink(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ if (argc != 3) {
+ printf("Usage: symlink <path> <link>\n");
+ return NT_STATUS_OK;
+ }
+
+ if (SMB_VFS_SYMLINK(vfs->conn, argv[1], argv[2]) == -1) {
+ printf("symlink: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("symlink: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_readlink(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ char buffer[PATH_MAX];
+ int size;
+
+ if (argc != 2) {
+ printf("Usage: readlink <path>\n");
+ return NT_STATUS_OK;
+ }
+
+ if ((size = SMB_VFS_READLINK(vfs->conn, argv[1], buffer, PATH_MAX)) == -1) {
+ printf("readlink: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ buffer[size] = '\0';
+ printf("readlink: %s\n", buffer);
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_link(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ if (argc != 3) {
+ printf("Usage: link <path> <link>\n");
+ return NT_STATUS_OK;
+ }
+
+ if (SMB_VFS_LINK(vfs->conn, argv[1], argv[2]) == -1) {
+ printf("link: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("link: ok\n");
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_mknod(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ mode_t mode;
+ unsigned int dev_val;
+ SMB_DEV_T dev;
+
+ if (argc != 4) {
+ printf("Usage: mknod <path> <mode> <dev>\n");
+ printf(" mode is octal\n");
+ printf(" dev is hex\n");
+ return NT_STATUS_OK;
+ }
+
+ if (sscanf(argv[2], "%o", &mode) == 0) {
+ printf("open: error=-1 (invalid mode!)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (sscanf(argv[3], "%x", &dev_val) == 0) {
+ printf("open: error=-1 (invalid dev!)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ dev = (SMB_DEV_T)dev_val;
+
+ if (SMB_VFS_MKNOD(vfs->conn, argv[1], mode, dev) == -1) {
+ printf("mknod: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("mknod: ok\n");
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_realpath(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ char respath[PATH_MAX];
+
+ if (argc != 2) {
+ printf("Usage: realpath <path>\n");
+ return NT_STATUS_OK;
+ }
+
+ if (SMB_VFS_REALPATH(vfs->conn, argv[1], respath) == NULL) {
+ printf("realpath: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("realpath: ok\n");
+ return NT_STATUS_OK;
+}
+
+struct cmd_set vfs_commands[] = {
+
+ { "VFS Commands" },
+
+ { "load", cmd_load_module, "Load a module", "load <module.so>" },
+ { "populate", cmd_populate, "Populate a data buffer", "populate <char> <size>" },
+ { "showdata", cmd_show_data, "Show data currently in data buffer", "show_data [<offset> <len>]"},
+ { "connect", cmd_connect, "VFS connect()", "connect" },
+ { "disconnect", cmd_disconnect, "VFS disconnect()", "disconnect" },
+ { "disk_free", cmd_disk_free, "VFS disk_free()", "disk_free <path>" },
+ { "opendir", cmd_opendir, "VFS opendir()", "opendir <fname>" },
+ { "readdir", cmd_readdir, "VFS readdir()", "readdir" },
+ { "mkdir", cmd_mkdir, "VFS mkdir()", "mkdir <path>" },
+ { "rmdir", cmd_pathfunc, "VFS rmdir()", "rmdir <path>" },
+ { "closedir", cmd_closedir, "VFS closedir()", "closedir" },
+ { "open", cmd_open, "VFS open()", "open <fname>" },
+ { "close", cmd_close, "VFS close()", "close <fd>" },
+ { "read", cmd_read, "VFS read()", "read <fd> <size>" },
+ { "write", cmd_write, "VFS write()", "write <fd> <size>" },
+ { "lseek", cmd_lseek, "VFS lseek()", "lseek <fd> <offset> <whence>" },
+ { "rename", cmd_rename, "VFS rename()", "rename <old> <new>" },
+ { "fsync", cmd_fsync, "VFS fsync()", "fsync <fd>" },
+ { "stat", cmd_stat, "VFS stat()", "stat <fname>" },
+ { "fstat", cmd_fstat, "VFS fstat()", "fstat <fd>" },
+ { "lstat", cmd_lstat, "VFS lstat()", "lstat <fname>" },
+ { "unlink", cmd_pathfunc, "VFS unlink()", "unlink <fname>" },
+ { "chmod", cmd_chmod, "VFS chmod()", "chmod <path> <mode>" },
+ { "fchmod", cmd_fchmod, "VFS fchmod()", "fchmod <fd> <mode>" },
+ { "chown", cmd_chown, "VFS chown()", "chown <path> <uid> <gid>" },
+ { "fchown", cmd_fchown, "VFS fchown()", "fchown <fd> <uid> <gid>" },
+ { "chdir", cmd_pathfunc, "VFS chdir()", "chdir <path>" },
+ { "getwd", cmd_getwd, "VFS getwd()", "getwd" },
+ { "utime", cmd_utime, "VFS utime()", "utime <path> <access> <modify>" },
+ { "ftruncate", cmd_ftruncate, "VFS ftruncate()", "ftruncate <fd> <length>" },
+ { "lock", cmd_lock, "VFS lock()", "lock <f> <op> <offset> <count> <type>" },
+ { "symlink", cmd_symlink, "VFS symlink()", "symlink <old> <new>" },
+ { "readlink", cmd_readlink, "VFS readlink()", "readlink <path>" },
+ { "link", cmd_link, "VFS link()", "link <oldpath> <newpath>" },
+ { "mknod", cmd_mknod, "VFS mknod()", "mknod <path> <mode> <dev>" },
+ { "realpath", cmd_realpath, "VFS realpath()", "realpath <path>" },
+ { NULL }
+};
diff --git a/source/torture/denytest.c b/source/torture/denytest.c
new file mode 100644
index 00000000000..89b0fdf93f6
--- /dev/null
+++ b/source/torture/denytest.c
@@ -0,0 +1,1577 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB torture tester - scanning functions
+ Copyright (C) Andrew Tridgell 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+extern BOOL torture_showall;
+
+enum deny_result {A_0=0, A_X=1, A_R=2, A_W=3, A_RW=5};
+
+
+static const char *denystr(int denymode)
+{
+ struct {
+ int v;
+ const char *name;
+ } deny_modes[] = {
+ {DENY_DOS, "DENY_DOS"},
+ {DENY_ALL, "DENY_ALL"},
+ {DENY_WRITE, "DENY_WRITE"},
+ {DENY_READ, "DENY_READ"},
+ {DENY_NONE, "DENY_NONE"},
+ {DENY_FCB, "DENY_FCB"},
+ {-1, NULL}};
+ int i;
+ for (i=0;deny_modes[i].name;i++) {
+ if (deny_modes[i].v == denymode) return deny_modes[i].name;
+ }
+ return "DENY_XXX";
+}
+
+static const char *openstr(int mode)
+{
+ struct {
+ int v;
+ const char *name;
+ } open_modes[] = {
+ {O_RDWR, "O_RDWR"},
+ {O_RDONLY, "O_RDONLY"},
+ {O_WRONLY, "O_WRONLY"},
+ {-1, NULL}};
+ int i;
+ for (i=0;open_modes[i].name;i++) {
+ if (open_modes[i].v == mode) return open_modes[i].name;
+ }
+ return "O_XXX";
+}
+
+static const char *resultstr(enum deny_result res)
+{
+ struct {
+ enum deny_result res;
+ const char *name;
+ } results[] = {
+ {A_X, "X"},
+ {A_0, "-"},
+ {A_R, "R"},
+ {A_W, "W"},
+ {A_RW,"RW"}};
+ int i;
+ for (i=0;ARRAY_SIZE(results);i++) {
+ if (results[i].res == res) return results[i].name;
+ }
+ return "*";
+}
+
+static struct {
+ int isexe;
+ int mode1, deny1;
+ int mode2, deny2;
+ enum deny_result result;
+} denytable2[] = {
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_NONE, A_RW},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_WRITE, A_RW},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_WRITE, A_R},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_WRITE, A_W},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_NONE, A_RW},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_DOS, A_RW},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_DOS, A_W},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_READ, A_RW},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_READ, A_R},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_READ, A_W},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_NONE, A_RW},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_NONE, A_R},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_NONE, A_W},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_WRITE, A_R},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_DOS, A_R},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_READ, A_R},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_WRITE, A_W},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_DOS, A_W},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_READ, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_READ, A_W},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_WRITE, A_RW},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_WRITE, A_R},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_WRITE, A_W},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_DOS, A_RW},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_DOS, A_R},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_DOS, A_W},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_READ, A_RW},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_READ, A_R},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_READ, A_W},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_DOS, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_WRITE, A_R},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_DOS, A_R},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_WRITE, A_R},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_READ, A_R},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_WRITE, A_W},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_READ, A_W},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_DOS, A_R},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_WRITE, A_RW},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_WRITE, A_R},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_WRITE, A_W},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_READ, A_RW},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_READ, A_R},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_READ, A_W},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_FCB, A_0}
+};
+
+
+static struct {
+ int isexe;
+ int mode1, deny1;
+ int mode2, deny2;
+ enum deny_result result;
+} denytable1[] = {
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_NONE, A_RW},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_WRITE, A_RW},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_WRITE, A_R},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_WRITE, A_W},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_NONE, A_RW},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_DOS, A_RW},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_DOS, A_W},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_READ, A_RW},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_READ, A_R},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_READ, A_W},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_NONE, A_RW},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_NONE, A_R},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_NONE, A_W},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_WRITE, A_R},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_DOS, A_R},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_READ, A_R},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_WRITE, A_W},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_DOS, A_W},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_READ, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_READ, A_W},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_WRITE, A_RW},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_WRITE, A_R},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_WRITE, A_W},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_DOS, A_RW},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_DOS, A_R},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_DOS, A_W},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_READ, A_RW},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_READ, A_R},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_READ, A_W},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_FCB, A_RW},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_FCB, A_RW},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_FCB, A_RW},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_FCB, A_RW},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_FCB, A_RW},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_FCB, A_RW},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_DOS, A_RW},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_DOS, A_R},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_DOS, A_W},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_FCB, A_RW},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_FCB, A_RW},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_FCB, A_RW},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_DOS, A_RW},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_DOS, A_W},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_FCB, A_RW},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_FCB, A_RW},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_FCB, A_RW},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_WRITE, A_R},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_DOS, A_RW},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_DOS, A_W},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_FCB, A_RW},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_FCB, A_RW},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_FCB, A_RW},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_DOS, A_R},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_WRITE, A_R},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_READ, A_R},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_WRITE, A_W},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_READ, A_W},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_DOS, A_R},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_WRITE, A_RW},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_WRITE, A_R},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_WRITE, A_W},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_READ, A_RW},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_READ, A_R},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_READ, A_W},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_DOS, A_RW},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_DOS, A_R},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_DOS, A_W},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_FCB, A_RW},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_FCB, A_RW},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_FCB, A_RW},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_DOS, A_RW},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_DOS, A_R},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_DOS, A_W},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_FCB, A_RW},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_FCB, A_RW},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_FCB, A_RW},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_DOS, A_RW},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_DOS, A_R},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_DOS, A_W},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_FCB, A_RW},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_FCB, A_RW},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_FCB, A_RW}
+};
+
+
+static void progress_bar(unsigned i, unsigned total)
+{
+ if (i % 10 != 0) return;
+ printf("%5d/%5d\r", i, total);
+ fflush(stdout);
+}
+
+/*
+ this produces a matrix of deny mode behaviour for 1 connection
+ */
+BOOL torture_denytest1(int dummy)
+{
+ struct cli_state *cli1;
+ int fnum1, fnum2;
+ int i;
+ BOOL correct = True;
+ const char *fnames[2] = {"\\denytest1.dat", "\\denytest1.exe"};
+
+ if (!torture_open_connection(&cli1)) {
+ return False;
+ }
+
+ printf("starting denytest1\n");
+
+ for (i=0;i<2;i++) {
+ cli_unlink(cli1, fnames[i]);
+ fnum1 = cli_open(cli1, fnames[i], O_RDWR|O_CREAT, DENY_NONE);
+ cli_write(cli1, fnum1, 0, fnames[i], 0, strlen(fnames[i]));
+ cli_close(cli1, fnum1);
+ }
+
+ printf("testing %ld entries\n", (unsigned long)ARRAY_SIZE(denytable1));
+
+ for (i=0; i<ARRAY_SIZE(denytable1); i++) {
+ enum deny_result res;
+ const char *fname = fnames[denytable1[i].isexe];
+
+ progress_bar(i, ARRAY_SIZE(denytable1));
+
+ fnum1 = cli_open(cli1, fname,
+ denytable1[i].mode1,
+ denytable1[i].deny1);
+ fnum2 = cli_open(cli1, fname,
+ denytable1[i].mode2,
+ denytable1[i].deny2);
+
+ if (fnum1 == -1) {
+ res = A_X;
+ } else if (fnum2 == -1) {
+ res = A_0;
+ } else {
+ char x = 1;
+ res = A_0;
+ if (cli_read(cli1, fnum2, (void *)&x, 0, 1) == 1) {
+ res += A_R;
+ }
+ if (cli_write(cli1, fnum2, 0, (void *)&x, 0, 1) == 1) {
+ res += A_W;
+ }
+ }
+
+ if (res != denytable1[i].result) {
+ correct = False;
+ }
+
+ if (torture_showall || res != denytable1[i].result) {
+ printf("%s %8s %10s %8s %10s %s (correct=%s)\n",
+ fname,
+ denystr(denytable1[i].deny1),
+ openstr(denytable1[i].mode1),
+ denystr(denytable1[i].deny2),
+ openstr(denytable1[i].mode2),
+ resultstr(res),
+ resultstr(denytable1[i].result));
+ }
+
+ cli_close(cli1, fnum1);
+ cli_close(cli1, fnum2);
+ }
+
+ for (i=0;i<2;i++) {
+ cli_unlink(cli1, fnames[i]);
+ }
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+
+ printf("finshed denytest1\n");
+ return correct;
+}
+
+
+/*
+ this produces a matrix of deny mode behaviour with 2 connections
+ */
+BOOL torture_denytest2(int dummy)
+{
+ static struct cli_state *cli1, *cli2;
+ int fnum1, fnum2;
+ int i;
+ BOOL correct = True;
+ const char *fnames[2] = {"\\denytest2.dat", "\\denytest2.exe"};
+
+ if (!torture_open_connection(&cli1) || !torture_open_connection(&cli2)) {
+ return False;
+ }
+
+ printf("starting denytest2\n");
+
+ for (i=0;i<2;i++) {
+ cli_unlink(cli1, fnames[i]);
+ fnum1 = cli_open(cli1, fnames[i], O_RDWR|O_CREAT, DENY_NONE);
+ cli_write(cli1, fnum1, 0, fnames[i], 0, strlen(fnames[i]));
+ cli_close(cli1, fnum1);
+ }
+
+ for (i=0; i<ARRAY_SIZE(denytable2); i++) {
+ enum deny_result res;
+ const char *fname = fnames[denytable2[i].isexe];
+
+ progress_bar(i, ARRAY_SIZE(denytable1));
+
+ fnum1 = cli_open(cli1, fname,
+ denytable2[i].mode1,
+ denytable2[i].deny1);
+ fnum2 = cli_open(cli2, fname,
+ denytable2[i].mode2,
+ denytable2[i].deny2);
+
+ if (fnum1 == -1) {
+ res = A_X;
+ } else if (fnum2 == -1) {
+ res = A_0;
+ } else {
+ char x = 1;
+ res = A_0;
+ if (cli_read(cli2, fnum2, (void *)&x, 0, 1) == 1) {
+ res += A_R;
+ }
+ if (cli_write(cli2, fnum2, 0, (void *)&x, 0, 1) == 1) {
+ res += A_W;
+ }
+ }
+
+ if (res != denytable2[i].result) {
+ correct = False;
+ }
+
+ if (torture_showall || res != denytable2[i].result) {
+ printf("%s %8s %10s %8s %10s %s (correct=%s)\n",
+ fname,
+ denystr(denytable2[i].deny1),
+ openstr(denytable2[i].mode1),
+ denystr(denytable2[i].deny2),
+ openstr(denytable2[i].mode2),
+ resultstr(res),
+ resultstr(denytable2[i].result));
+ }
+
+ cli_close(cli1, fnum1);
+ cli_close(cli2, fnum2);
+ }
+
+ for (i=0;i<2;i++) {
+ cli_unlink(cli1, fnames[i]);
+ }
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+ if (!torture_close_connection(cli2)) {
+ correct = False;
+ }
+
+ printf("finshed denytest2\n");
+ return correct;
+}
+
diff --git a/source/torture/locktest.c b/source/torture/locktest.c
new file mode 100644
index 00000000000..86379bf3b6d
--- /dev/null
+++ b/source/torture/locktest.c
@@ -0,0 +1,678 @@
+/*
+ Unix SMB/CIFS implementation.
+ randomised byte range lock tester
+ Copyright (C) Andrew Tridgell 1999
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+static fstring password[2];
+static fstring username[2];
+static int got_user;
+static int got_pass;
+static BOOL use_kerberos;
+static int numops = 1000;
+static BOOL showall;
+static BOOL analyze;
+static BOOL hide_unlock_fails;
+static BOOL use_oplocks;
+static unsigned lock_range = 100;
+static unsigned lock_base = 0;
+static unsigned min_length = 0;
+static BOOL exact_error_codes;
+static BOOL zero_zero;
+
+#define FILENAME "\\locktest.dat"
+
+#define READ_PCT 50
+#define LOCK_PCT 45
+#define UNLOCK_PCT 70
+#define RANGE_MULTIPLE 1
+#define NSERVERS 2
+#define NCONNECTIONS 2
+#define NFILES 2
+#define LOCK_TIMEOUT 0
+
+#define NASTY_POSIX_LOCK_HACK 0
+
+enum lock_op {OP_LOCK, OP_UNLOCK, OP_REOPEN};
+
+struct record {
+ enum lock_op lock_op;
+ enum brl_type lock_type;
+ char conn, f;
+ SMB_BIG_UINT start, len;
+ char needed;
+};
+
+#define PRESETS 0
+
+#if PRESETS
+static struct record preset[] = {
+{OP_LOCK, WRITE_LOCK, 0, 0, 2, 0, 1},
+{OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1},
+{OP_LOCK, WRITE_LOCK, 0, 0, 3, 0, 1},
+{OP_UNLOCK, 0 , 0, 0, 2, 0, 1},
+{OP_REOPEN, 0, 0, 0, 0, 0, 1},
+
+{OP_LOCK, READ_LOCK, 0, 0, 2, 0, 1},
+{OP_LOCK, READ_LOCK, 0, 0, 1, 1, 1},
+{OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1},
+{OP_REOPEN, 0, 0, 0, 0, 0, 1},
+
+{OP_LOCK, READ_LOCK, 0, 0, 2, 0, 1},
+{OP_LOCK, WRITE_LOCK, 0, 0, 3, 1, 1},
+{OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1},
+{OP_REOPEN, 0, 0, 0, 0, 0, 1},
+
+{OP_LOCK, READ_LOCK, 0, 0, 2, 0, 1},
+{OP_LOCK, WRITE_LOCK, 0, 0, 1, 1, 1},
+{OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1},
+{OP_REOPEN, 0, 0, 0, 0, 0, 1},
+
+{OP_LOCK, WRITE_LOCK, 0, 0, 2, 0, 1},
+{OP_LOCK, READ_LOCK, 0, 0, 1, 1, 1},
+{OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1},
+{OP_REOPEN, 0, 0, 0, 0, 0, 1},
+
+{OP_LOCK, WRITE_LOCK, 0, 0, 2, 0, 1},
+{OP_LOCK, READ_LOCK, 0, 0, 3, 1, 1},
+{OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1},
+{OP_REOPEN, 0, 0, 0, 0, 0, 1},
+
+};
+#endif
+
+static struct record *recorded;
+
+static void print_brl(SMB_DEV_T dev, SMB_INO_T ino, int pid,
+ enum brl_type lock_type,
+ br_off start, br_off size)
+{
+#if NASTY_POSIX_LOCK_HACK
+ {
+ pstring cmd;
+ static SMB_INO_T lastino;
+
+ if (lastino != ino) {
+ slprintf(cmd, sizeof(cmd),
+ "egrep POSIX.*%u /proc/locks", (int)ino);
+ system(cmd);
+ }
+ lastino = ino;
+ }
+#endif
+
+ printf("%6d %05x:%05x %s %.0f:%.0f(%.0f)\n",
+ (int)pid, (int)dev, (int)ino,
+ lock_type==READ_LOCK?"R":"W",
+ (double)start, (double)start+size-1,(double)size);
+
+}
+
+
+static void show_locks(void)
+{
+ brl_forall(print_brl);
+ /* system("cat /proc/locks"); */
+}
+
+
+/*****************************************************
+return a connection to a server
+*******************************************************/
+static struct cli_state *connect_one(char *share, int snum)
+{
+ struct cli_state *c;
+ struct nmb_name called, calling;
+ char *server_n;
+ fstring server;
+ struct in_addr ip;
+ fstring myname;
+ static int count;
+
+ fstrcpy(server,share+2);
+ share = strchr_m(server,'\\');
+ if (!share) return NULL;
+ *share = 0;
+ share++;
+
+ server_n = server;
+
+ zero_ip(&ip);
+
+ slprintf(myname,sizeof(myname), "lock-%lu-%u", (unsigned long)getpid(), count++);
+
+ make_nmb_name(&calling, myname, 0x0);
+ make_nmb_name(&called , server, 0x20);
+
+ again:
+ zero_ip(&ip);
+
+ /* have to open a new connection */
+ if (!(c=cli_initialise(NULL)) || !cli_connect(c, server_n, &ip)) {
+ DEBUG(0,("Connection to %s failed\n", server_n));
+ return NULL;
+ }
+
+ c->use_kerberos = use_kerberos;
+
+ if (!cli_session_request(c, &calling, &called)) {
+ DEBUG(0,("session request to %s failed\n", called.name));
+ cli_shutdown(c);
+ if (strcmp(called.name, "*SMBSERVER")) {
+ make_nmb_name(&called , "*SMBSERVER", 0x20);
+ goto again;
+ }
+ return NULL;
+ }
+
+ DEBUG(4,(" session request ok\n"));
+
+ if (!cli_negprot(c)) {
+ DEBUG(0,("protocol negotiation failed\n"));
+ cli_shutdown(c);
+ return NULL;
+ }
+
+ if (!got_pass) {
+ char *pass = getpass("Password: ");
+ if (pass) {
+ fstrcpy(password[0], pass);
+ fstrcpy(password[1], pass);
+ }
+ }
+
+ if (got_pass == 1) {
+ fstrcpy(password[1], password[0]);
+ fstrcpy(username[1], username[0]);
+ }
+
+ if (!cli_session_setup(c, username[snum],
+ password[snum], strlen(password[snum]),
+ password[snum], strlen(password[snum]),
+ lp_workgroup())) {
+ DEBUG(0,("session setup failed: %s\n", cli_errstr(c)));
+ return NULL;
+ }
+
+ /*
+ * These next two lines are needed to emulate
+ * old client behaviour for people who have
+ * scripts based on client output.
+ * QUESTION ? Do we want to have a 'client compatibility
+ * mode to turn these on/off ? JRA.
+ */
+
+ if (*c->server_domain || *c->server_os || *c->server_type)
+ DEBUG(1,("Domain=[%s] OS=[%s] Server=[%s]\n",
+ c->server_domain,c->server_os,c->server_type));
+
+ DEBUG(4,(" session setup ok\n"));
+
+ if (!cli_send_tconX(c, share, "?????",
+ password[snum], strlen(password[snum])+1)) {
+ DEBUG(0,("tree connect failed: %s\n", cli_errstr(c)));
+ cli_shutdown(c);
+ return NULL;
+ }
+
+ DEBUG(4,(" tconx ok\n"));
+
+ c->use_oplocks = use_oplocks;
+
+ return c;
+}
+
+
+static void reconnect(struct cli_state *cli[NSERVERS][NCONNECTIONS], int fnum[NSERVERS][NCONNECTIONS][NFILES],
+ char *share[NSERVERS])
+{
+ int server, conn, f;
+
+ for (server=0;server<NSERVERS;server++)
+ for (conn=0;conn<NCONNECTIONS;conn++) {
+ if (cli[server][conn]) {
+ for (f=0;f<NFILES;f++) {
+ if (fnum[server][conn][f] != -1) {
+ cli_close(cli[server][conn], fnum[server][conn][f]);
+ fnum[server][conn][f] = -1;
+ }
+ }
+ cli_ulogoff(cli[server][conn]);
+ cli_shutdown(cli[server][conn]);
+ }
+ cli[server][conn] = connect_one(share[server], server);
+ if (!cli[server][conn]) {
+ DEBUG(0,("Failed to connect to %s\n", share[server]));
+ exit(1);
+ }
+ }
+}
+
+
+
+static BOOL test_one(struct cli_state *cli[NSERVERS][NCONNECTIONS],
+ int fnum[NSERVERS][NCONNECTIONS][NFILES],
+ struct record *rec)
+{
+ unsigned conn = rec->conn;
+ unsigned f = rec->f;
+ SMB_BIG_UINT start = rec->start;
+ SMB_BIG_UINT len = rec->len;
+ enum brl_type op = rec->lock_type;
+ int server;
+ BOOL ret[NSERVERS];
+ NTSTATUS status[NSERVERS];
+
+ switch (rec->lock_op) {
+ case OP_LOCK:
+ /* set a lock */
+ for (server=0;server<NSERVERS;server++) {
+ ret[server] = cli_lock64(cli[server][conn],
+ fnum[server][conn][f],
+ start, len, LOCK_TIMEOUT, op);
+ status[server] = cli_nt_error(cli[server][conn]);
+ if (!exact_error_codes &&
+ NT_STATUS_EQUAL(status[server],
+ NT_STATUS_FILE_LOCK_CONFLICT)) {
+ status[server] = NT_STATUS_LOCK_NOT_GRANTED;
+ }
+ }
+ if (showall || !NT_STATUS_EQUAL(status[0],status[1])) {
+ printf("lock conn=%u f=%u range=%.0f(%.0f) op=%s -> %s:%s\n",
+ conn, f,
+ (double)start, (double)len,
+ op==READ_LOCK?"READ_LOCK":"WRITE_LOCK",
+ nt_errstr(status[0]), nt_errstr(status[1]));
+ }
+ if (showall || !NT_STATUS_EQUAL(status[0],status[1])) show_locks();
+ if (!NT_STATUS_EQUAL(status[0],status[1])) return False;
+ break;
+
+ case OP_UNLOCK:
+ /* unset a lock */
+ for (server=0;server<NSERVERS;server++) {
+ ret[server] = cli_unlock64(cli[server][conn],
+ fnum[server][conn][f],
+ start, len);
+ status[server] = cli_nt_error(cli[server][conn]);
+ }
+ if (showall ||
+ (!hide_unlock_fails && !NT_STATUS_EQUAL(status[0],status[1]))) {
+ printf("unlock conn=%u f=%u range=%.0f(%.0f) -> %s:%s\n",
+ conn, f,
+ (double)start, (double)len,
+ nt_errstr(status[0]), nt_errstr(status[1]));
+ }
+ if (showall || !NT_STATUS_EQUAL(status[0],status[1])) show_locks();
+ if (!hide_unlock_fails && !NT_STATUS_EQUAL(status[0],status[1]))
+ return False;
+ break;
+
+ case OP_REOPEN:
+ /* reopen the file */
+ for (server=0;server<NSERVERS;server++) {
+ cli_close(cli[server][conn], fnum[server][conn][f]);
+ fnum[server][conn][f] = -1;
+ }
+ for (server=0;server<NSERVERS;server++) {
+ fnum[server][conn][f] = cli_open(cli[server][conn], FILENAME,
+ O_RDWR|O_CREAT,
+ DENY_NONE);
+ if (fnum[server][conn][f] == -1) {
+ printf("failed to reopen on share%d\n", server);
+ return False;
+ }
+ }
+ if (showall) {
+ printf("reopen conn=%u f=%u\n",
+ conn, f);
+ show_locks();
+ }
+ break;
+ }
+
+ return True;
+}
+
+static void close_files(struct cli_state *cli[NSERVERS][NCONNECTIONS],
+ int fnum[NSERVERS][NCONNECTIONS][NFILES])
+{
+ int server, conn, f;
+
+ for (server=0;server<NSERVERS;server++)
+ for (conn=0;conn<NCONNECTIONS;conn++)
+ for (f=0;f<NFILES;f++) {
+ if (fnum[server][conn][f] != -1) {
+ cli_close(cli[server][conn], fnum[server][conn][f]);
+ fnum[server][conn][f] = -1;
+ }
+ }
+ for (server=0;server<NSERVERS;server++) {
+ cli_unlink(cli[server][0], FILENAME);
+ }
+}
+
+static void open_files(struct cli_state *cli[NSERVERS][NCONNECTIONS],
+ int fnum[NSERVERS][NCONNECTIONS][NFILES])
+{
+ int server, conn, f;
+
+ for (server=0;server<NSERVERS;server++)
+ for (conn=0;conn<NCONNECTIONS;conn++)
+ for (f=0;f<NFILES;f++) {
+ fnum[server][conn][f] = cli_open(cli[server][conn], FILENAME,
+ O_RDWR|O_CREAT,
+ DENY_NONE);
+ if (fnum[server][conn][f] == -1) {
+ fprintf(stderr,"Failed to open fnum[%u][%u][%u]\n",
+ server, conn, f);
+ exit(1);
+ }
+ }
+}
+
+
+static int retest(struct cli_state *cli[NSERVERS][NCONNECTIONS],
+ int fnum[NSERVERS][NCONNECTIONS][NFILES],
+ int n)
+{
+ int i;
+ printf("testing %u ...\n", n);
+ for (i=0; i<n; i++) {
+ if (i && i % 100 == 0) {
+ printf("%u\n", i);
+ }
+
+ if (recorded[i].needed &&
+ !test_one(cli, fnum, &recorded[i])) return i;
+ }
+ return n;
+}
+
+
+/* each server has two connections open to it. Each connection has two file
+ descriptors open on the file - 8 file descriptors in total
+
+ we then do random locking ops in tamdem on the 4 fnums from each
+ server and ensure that the results match
+ */
+static void test_locks(char *share[NSERVERS])
+{
+ struct cli_state *cli[NSERVERS][NCONNECTIONS];
+ int fnum[NSERVERS][NCONNECTIONS][NFILES];
+ int n, i, n1, skip, r1, r2;
+
+ ZERO_STRUCT(fnum);
+ ZERO_STRUCT(cli);
+
+ recorded = (struct record *)malloc(sizeof(*recorded) * numops);
+
+ for (n=0; n<numops; n++) {
+#if PRESETS
+ if (n < sizeof(preset) / sizeof(preset[0])) {
+ recorded[n] = preset[n];
+ } else {
+#endif
+ recorded[n].conn = random() % NCONNECTIONS;
+ recorded[n].f = random() % NFILES;
+ recorded[n].start = lock_base + ((unsigned)random() % (lock_range-1));
+ recorded[n].len = min_length +
+ random() % (lock_range-(recorded[n].start-lock_base));
+ recorded[n].start *= RANGE_MULTIPLE;
+ recorded[n].len *= RANGE_MULTIPLE;
+ r1 = random() % 100;
+ r2 = random() % 100;
+ if (r1 < READ_PCT) {
+ recorded[n].lock_type = READ_LOCK;
+ } else {
+ recorded[n].lock_type = WRITE_LOCK;
+ }
+ if (r2 < LOCK_PCT) {
+ recorded[n].lock_op = OP_LOCK;
+ } else if (r2 < UNLOCK_PCT) {
+ recorded[n].lock_op = OP_UNLOCK;
+ } else {
+ recorded[n].lock_op = OP_REOPEN;
+ }
+ recorded[n].needed = True;
+ if (!zero_zero && recorded[n].start==0 && recorded[n].len==0) {
+ recorded[n].len = 1;
+ }
+#if PRESETS
+ }
+#endif
+ }
+
+ reconnect(cli, fnum, share);
+ open_files(cli, fnum);
+ n = retest(cli, fnum, numops);
+
+ if (n == numops || !analyze) return;
+ n++;
+
+ skip = n/2;
+
+ while (1) {
+ n1 = n;
+
+ close_files(cli, fnum);
+ reconnect(cli, fnum, share);
+ open_files(cli, fnum);
+
+ for (i=0;i<n-skip;i+=skip) {
+ int m, j;
+ printf("excluding %d-%d\n", i, i+skip-1);
+ for (j=i;j<i+skip;j++) {
+ recorded[j].needed = False;
+ }
+
+ close_files(cli, fnum);
+ open_files(cli, fnum);
+
+ m = retest(cli, fnum, n);
+ if (m == n) {
+ for (j=i;j<i+skip;j++) {
+ recorded[j].needed = True;
+ }
+ } else {
+ if (i+(skip-1) < m) {
+ memmove(&recorded[i], &recorded[i+skip],
+ (m-(i+skip-1))*sizeof(recorded[0]));
+ }
+ n = m-(skip-1);
+ i--;
+ }
+ }
+
+ if (skip > 1) {
+ skip = skip/2;
+ printf("skip=%d\n", skip);
+ continue;
+ }
+
+ if (n1 == n) break;
+ }
+
+ close_files(cli, fnum);
+ reconnect(cli, fnum, share);
+ open_files(cli, fnum);
+ showall = True;
+ n1 = retest(cli, fnum, n);
+ if (n1 != n-1) {
+ printf("ERROR - inconsistent result (%u %u)\n", n1, n);
+ }
+ close_files(cli, fnum);
+
+ for (i=0;i<n;i++) {
+ printf("{%d, %d, %u, %u, %.0f, %.0f, %u},\n",
+ recorded[i].lock_op,
+ recorded[i].lock_type,
+ recorded[i].conn,
+ recorded[i].f,
+ (double)recorded[i].start,
+ (double)recorded[i].len,
+ recorded[i].needed);
+ }
+}
+
+
+
+static void usage(void)
+{
+ printf(
+"Usage:\n\
+ locktest //server1/share1 //server2/share2 [options..]\n\
+ options:\n\
+ -U user%%pass (may be specified twice)\n\
+ -k use kerberos\n\
+ -s seed\n\
+ -o numops\n\
+ -u hide unlock fails\n\
+ -a (show all ops)\n\
+ -A analyse for minimal ops\n\
+ -O use oplocks\n\
+ -E enable exact error code checking\n\
+ -Z enable the zero/zero lock\n\
+ -R range set lock range\n\
+ -B base set lock base\n\
+ -M min set min lock length\n\
+");
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+ char *share[NSERVERS];
+ extern char *optarg;
+ extern int optind;
+ int opt;
+ char *p;
+ int seed, server;
+
+ setlinebuf(stdout);
+
+ dbf = x_stderr;
+
+ if (argc < 3 || argv[1][0] == '-') {
+ usage();
+ exit(1);
+ }
+
+ setup_logging(argv[0],True);
+
+ for (server=0;server<NSERVERS;server++) {
+ share[server] = argv[1+server];
+ all_string_sub(share[server],"/","\\",0);
+ }
+
+ argc -= NSERVERS;
+ argv += NSERVERS;
+
+ lp_load(dyn_CONFIGFILE,True,False,False);
+ load_interfaces();
+
+ if (getenv("USER")) {
+ fstrcpy(username[0],getenv("USER"));
+ fstrcpy(username[1],getenv("USER"));
+ }
+
+ seed = time(NULL);
+
+ while ((opt = getopt(argc, argv, "U:s:ho:aAW:OkR:B:M:EZ")) != EOF) {
+ switch (opt) {
+ case 'k':
+#ifdef HAVE_KRB5
+ use_kerberos = True;
+#else
+ d_printf("No kerberos support compiled in\n");
+ exit(1);
+#endif
+ break;
+ case 'U':
+ got_user = 1;
+ if (got_pass == 2) {
+ d_printf("Max of 2 usernames\n");
+ exit(1);
+ }
+ fstrcpy(username[got_pass],optarg);
+ p = strchr_m(username[got_pass],'%');
+ if (p) {
+ *p = 0;
+ fstrcpy(password[got_pass], p+1);
+ got_pass++;
+ }
+ break;
+ case 'R':
+ lock_range = strtol(optarg, NULL, 0);
+ break;
+ case 'B':
+ lock_base = strtol(optarg, NULL, 0);
+ break;
+ case 'M':
+ min_length = strtol(optarg, NULL, 0);
+ break;
+ case 's':
+ seed = atoi(optarg);
+ break;
+ case 'u':
+ hide_unlock_fails = True;
+ break;
+ case 'o':
+ numops = atoi(optarg);
+ break;
+ case 'O':
+ use_oplocks = True;
+ break;
+ case 'a':
+ showall = True;
+ break;
+ case 'A':
+ analyze = True;
+ break;
+ case 'Z':
+ zero_zero = True;
+ break;
+ case 'E':
+ exact_error_codes = True;
+ break;
+ case 'h':
+ usage();
+ exit(1);
+ default:
+ printf("Unknown option %c (%d)\n", (char)opt, opt);
+ exit(1);
+ }
+ }
+
+ if(use_kerberos && !got_user) got_pass = True;
+
+ argc -= optind;
+ argv += optind;
+
+ DEBUG(0,("seed=%u\n", seed));
+ srandom(seed);
+
+ test_locks(share);
+
+ return(0);
+}
diff --git a/source/torture/locktest2.c b/source/torture/locktest2.c
new file mode 100644
index 00000000000..5fbaf9ec584
--- /dev/null
+++ b/source/torture/locktest2.c
@@ -0,0 +1,560 @@
+/*
+ Unix SMB/CIFS implementation.
+ byte range lock tester - with local filesystem support
+ Copyright (C) Andrew Tridgell 1999
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+static fstring password;
+static fstring username;
+static int got_pass;
+static int numops = 1000;
+static BOOL showall;
+static BOOL analyze;
+static BOOL hide_unlock_fails;
+static BOOL use_oplocks;
+
+#define FILENAME "\\locktest.dat"
+#define LOCKRANGE 100
+#define LOCKBASE 0
+
+/*
+#define LOCKBASE (0x40000000 - 50)
+*/
+
+#define READ_PCT 50
+#define LOCK_PCT 25
+#define UNLOCK_PCT 65
+#define RANGE_MULTIPLE 1
+
+#define NSERVERS 2
+#define NCONNECTIONS 2
+#define NUMFSTYPES 2
+#define NFILES 2
+#define LOCK_TIMEOUT 0
+
+#define FSTYPE_SMB 0
+#define FSTYPE_NFS 1
+
+struct record {
+ char r1, r2;
+ char conn, f, fstype;
+ unsigned start, len;
+ char needed;
+};
+
+static struct record *recorded;
+
+static int try_open(struct cli_state *c, char *nfs, int fstype, const char *fname, int flags)
+{
+ pstring path;
+
+ switch (fstype) {
+ case FSTYPE_SMB:
+ return cli_open(c, fname, flags, DENY_NONE);
+
+ case FSTYPE_NFS:
+ slprintf(path, sizeof(path), "%s%s", nfs, fname);
+ pstring_sub(path,"\\", "/");
+ return open(path, flags, 0666);
+ }
+
+ return -1;
+}
+
+static BOOL try_close(struct cli_state *c, int fstype, int fd)
+{
+ switch (fstype) {
+ case FSTYPE_SMB:
+ return cli_close(c, fd);
+
+ case FSTYPE_NFS:
+ return close(fd) == 0;
+ }
+
+ return False;
+}
+
+static BOOL try_lock(struct cli_state *c, int fstype,
+ int fd, unsigned start, unsigned len,
+ enum brl_type op)
+{
+ struct flock lock;
+
+ switch (fstype) {
+ case FSTYPE_SMB:
+ return cli_lock(c, fd, start, len, LOCK_TIMEOUT, op);
+
+ case FSTYPE_NFS:
+ lock.l_type = (op==READ_LOCK) ? F_RDLCK:F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = start;
+ lock.l_len = len;
+ lock.l_pid = getpid();
+ return fcntl(fd,F_SETLK,&lock) == 0;
+ }
+
+ return False;
+}
+
+static BOOL try_unlock(struct cli_state *c, int fstype,
+ int fd, unsigned start, unsigned len)
+{
+ struct flock lock;
+
+ switch (fstype) {
+ case FSTYPE_SMB:
+ return cli_unlock(c, fd, start, len);
+
+ case FSTYPE_NFS:
+ lock.l_type = F_UNLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = start;
+ lock.l_len = len;
+ lock.l_pid = getpid();
+ return fcntl(fd,F_SETLK,&lock) == 0;
+ }
+
+ return False;
+}
+
+static void print_brl(SMB_DEV_T dev, SMB_INO_T ino, int pid,
+ enum brl_type lock_type,
+ br_off start, br_off size)
+{
+ printf("%6d %05x:%05x %s %.0f:%.0f(%.0f)\n",
+ (int)pid, (int)dev, (int)ino,
+ lock_type==READ_LOCK?"R":"W",
+ (double)start, (double)start+size-1,(double)size);
+
+}
+
+/*****************************************************
+return a connection to a server
+*******************************************************/
+static struct cli_state *connect_one(char *share)
+{
+ struct cli_state *c;
+ char *server_n;
+ fstring server;
+ fstring myname;
+ static int count;
+ NTSTATUS nt_status;
+
+ fstrcpy(server,share+2);
+ share = strchr_m(server,'\\');
+ if (!share) return NULL;
+ *share = 0;
+ share++;
+
+ server_n = server;
+
+ if (!got_pass) {
+ char *pass = getpass("Password: ");
+ if (pass) {
+ fstrcpy(password, pass);
+ }
+ }
+
+ slprintf(myname,sizeof(myname), "lock-%lu-%u", (unsigned long)getpid(), count++);
+
+ nt_status = cli_full_connection(&c, myname, server_n, NULL, 0, share, "?????",
+ username, lp_workgroup(), password, 0,
+ Undefined, NULL);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("cli_full_connection failed with error %s\n", nt_errstr(nt_status)));
+ return NULL;
+ }
+
+ c->use_oplocks = use_oplocks;
+
+ return c;
+}
+
+
+static void reconnect(struct cli_state *cli[NSERVERS][NCONNECTIONS],
+ char *nfs[NSERVERS],
+ int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES],
+ char *share1, char *share2)
+{
+ int server, conn, f, fstype;
+ char *share[2];
+ share[0] = share1;
+ share[1] = share2;
+
+ fstype = FSTYPE_SMB;
+
+ for (server=0;server<NSERVERS;server++)
+ for (conn=0;conn<NCONNECTIONS;conn++) {
+ if (cli[server][conn]) {
+ for (f=0;f<NFILES;f++) {
+ cli_close(cli[server][conn], fnum[server][fstype][conn][f]);
+ }
+ cli_ulogoff(cli[server][conn]);
+ cli_shutdown(cli[server][conn]);
+ }
+ cli[server][conn] = connect_one(share[server]);
+ if (!cli[server][conn]) {
+ DEBUG(0,("Failed to connect to %s\n", share[server]));
+ exit(1);
+ }
+ }
+}
+
+
+
+static BOOL test_one(struct cli_state *cli[NSERVERS][NCONNECTIONS],
+ char *nfs[NSERVERS],
+ int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES],
+ struct record *rec)
+{
+ unsigned conn = rec->conn;
+ unsigned f = rec->f;
+ unsigned fstype = rec->fstype;
+ unsigned start = rec->start;
+ unsigned len = rec->len;
+ unsigned r1 = rec->r1;
+ unsigned r2 = rec->r2;
+ enum brl_type op;
+ int server;
+ BOOL ret[NSERVERS];
+
+ if (r1 < READ_PCT) {
+ op = READ_LOCK;
+ } else {
+ op = WRITE_LOCK;
+ }
+
+ if (r2 < LOCK_PCT) {
+ /* set a lock */
+ for (server=0;server<NSERVERS;server++) {
+ ret[server] = try_lock(cli[server][conn], fstype,
+ fnum[server][fstype][conn][f],
+ start, len, op);
+ }
+ if (showall || ret[0] != ret[1]) {
+ printf("lock conn=%u fstype=%u f=%u range=%u:%u(%u) op=%s -> %u:%u\n",
+ conn, fstype, f,
+ start, start+len-1, len,
+ op==READ_LOCK?"READ_LOCK":"WRITE_LOCK",
+ ret[0], ret[1]);
+ }
+ if (showall) brl_forall(print_brl);
+ if (ret[0] != ret[1]) return False;
+ } else if (r2 < LOCK_PCT+UNLOCK_PCT) {
+ /* unset a lock */
+ for (server=0;server<NSERVERS;server++) {
+ ret[server] = try_unlock(cli[server][conn], fstype,
+ fnum[server][fstype][conn][f],
+ start, len);
+ }
+ if (showall || (!hide_unlock_fails && (ret[0] != ret[1]))) {
+ printf("unlock conn=%u fstype=%u f=%u range=%u:%u(%u) -> %u:%u\n",
+ conn, fstype, f,
+ start, start+len-1, len,
+ ret[0], ret[1]);
+ }
+ if (showall) brl_forall(print_brl);
+ if (!hide_unlock_fails && ret[0] != ret[1]) return False;
+ } else {
+ /* reopen the file */
+ for (server=0;server<NSERVERS;server++) {
+ try_close(cli[server][conn], fstype, fnum[server][fstype][conn][f]);
+ fnum[server][fstype][conn][f] = try_open(cli[server][conn], nfs[server], fstype, FILENAME,
+ O_RDWR|O_CREAT);
+ if (fnum[server][fstype][conn][f] == -1) {
+ printf("failed to reopen on share1\n");
+ return False;
+ }
+ }
+ if (showall) {
+ printf("reopen conn=%u fstype=%u f=%u\n",
+ conn, fstype, f);
+ brl_forall(print_brl);
+ }
+ }
+ return True;
+}
+
+static void close_files(struct cli_state *cli[NSERVERS][NCONNECTIONS],
+ char *nfs[NSERVERS],
+ int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES])
+{
+ int server, conn, f, fstype;
+
+ for (server=0;server<NSERVERS;server++)
+ for (fstype=0;fstype<NUMFSTYPES;fstype++)
+ for (conn=0;conn<NCONNECTIONS;conn++)
+ for (f=0;f<NFILES;f++) {
+ if (fnum[server][fstype][conn][f] != -1) {
+ try_close(cli[server][conn], fstype, fnum[server][fstype][conn][f]);
+ fnum[server][fstype][conn][f] = -1;
+ }
+ }
+ for (server=0;server<NSERVERS;server++) {
+ cli_unlink(cli[server][0], FILENAME);
+ }
+}
+
+static void open_files(struct cli_state *cli[NSERVERS][NCONNECTIONS],
+ char *nfs[NSERVERS],
+ int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES])
+{
+ int server, fstype, conn, f;
+
+ for (server=0;server<NSERVERS;server++)
+ for (fstype=0;fstype<NUMFSTYPES;fstype++)
+ for (conn=0;conn<NCONNECTIONS;conn++)
+ for (f=0;f<NFILES;f++) {
+ fnum[server][fstype][conn][f] = try_open(cli[server][conn], nfs[server], fstype, FILENAME,
+ O_RDWR|O_CREAT);
+ if (fnum[server][fstype][conn][f] == -1) {
+ fprintf(stderr,"Failed to open fnum[%u][%u][%u][%u]\n",
+ server, fstype, conn, f);
+ exit(1);
+ }
+ }
+}
+
+
+static int retest(struct cli_state *cli[NSERVERS][NCONNECTIONS],
+ char *nfs[NSERVERS],
+ int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES],
+ int n)
+{
+ int i;
+ printf("testing %u ...\n", n);
+ for (i=0; i<n; i++) {
+ if (i && i % 100 == 0) {
+ printf("%u\n", i);
+ }
+
+ if (recorded[i].needed &&
+ !test_one(cli, nfs, fnum, &recorded[i])) return i;
+ }
+ return n;
+}
+
+
+/* each server has two connections open to it. Each connection has two file
+ descriptors open on the file - 8 file descriptors in total
+
+ we then do random locking ops in tamdem on the 4 fnums from each
+ server and ensure that the results match
+ */
+static void test_locks(char *share1, char *share2, char *nfspath1, char *nfspath2)
+{
+ struct cli_state *cli[NSERVERS][NCONNECTIONS];
+ char *nfs[NSERVERS];
+ int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES];
+ int n, i, n1;
+
+ nfs[0] = nfspath1;
+ nfs[1] = nfspath2;
+
+ ZERO_STRUCT(fnum);
+ ZERO_STRUCT(cli);
+
+ recorded = (struct record *)malloc(sizeof(*recorded) * numops);
+
+ for (n=0; n<numops; n++) {
+ recorded[n].conn = random() % NCONNECTIONS;
+ recorded[n].fstype = random() % NUMFSTYPES;
+ recorded[n].f = random() % NFILES;
+ recorded[n].start = LOCKBASE + ((unsigned)random() % (LOCKRANGE-1));
+ recorded[n].len = 1 +
+ random() % (LOCKRANGE-(recorded[n].start-LOCKBASE));
+ recorded[n].start *= RANGE_MULTIPLE;
+ recorded[n].len *= RANGE_MULTIPLE;
+ recorded[n].r1 = random() % 100;
+ recorded[n].r2 = random() % 100;
+ recorded[n].needed = True;
+ }
+
+ reconnect(cli, nfs, fnum, share1, share2);
+ open_files(cli, nfs, fnum);
+ n = retest(cli, nfs, fnum, numops);
+
+ if (n == numops || !analyze) return;
+ n++;
+
+ while (1) {
+ n1 = n;
+
+ close_files(cli, nfs, fnum);
+ reconnect(cli, nfs, fnum, share1, share2);
+ open_files(cli, nfs, fnum);
+
+ for (i=0;i<n-1;i++) {
+ int m;
+ recorded[i].needed = False;
+
+ close_files(cli, nfs, fnum);
+ open_files(cli, nfs, fnum);
+
+ m = retest(cli, nfs, fnum, n);
+ if (m == n) {
+ recorded[i].needed = True;
+ } else {
+ if (i < m) {
+ memmove(&recorded[i], &recorded[i+1],
+ (m-i)*sizeof(recorded[0]));
+ }
+ n = m;
+ i--;
+ }
+ }
+
+ if (n1 == n) break;
+ }
+
+ close_files(cli, nfs, fnum);
+ reconnect(cli, nfs, fnum, share1, share2);
+ open_files(cli, nfs, fnum);
+ showall = True;
+ n1 = retest(cli, nfs, fnum, n);
+ if (n1 != n-1) {
+ printf("ERROR - inconsistent result (%u %u)\n", n1, n);
+ }
+ close_files(cli, nfs, fnum);
+
+ for (i=0;i<n;i++) {
+ printf("{%u, %u, %u, %u, %u, %u, %u, %u},\n",
+ recorded[i].r1,
+ recorded[i].r2,
+ recorded[i].conn,
+ recorded[i].fstype,
+ recorded[i].f,
+ recorded[i].start,
+ recorded[i].len,
+ recorded[i].needed);
+ }
+}
+
+
+
+static void usage(void)
+{
+ printf(
+"Usage:\n\
+ locktest //server1/share1 //server2/share2 /path1 /path2 [options..]\n\
+ options:\n\
+ -U user%%pass\n\
+ -s seed\n\
+ -o numops\n\
+ -u hide unlock fails\n\
+ -a (show all ops)\n\
+ -O use oplocks\n\
+");
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+ char *share1, *share2, *nfspath1, *nfspath2;
+ extern char *optarg;
+ extern int optind;
+ int opt;
+ char *p;
+ int seed;
+
+ setlinebuf(stdout);
+
+ dbf = x_stderr;
+
+ if (argc < 5 || argv[1][0] == '-') {
+ usage();
+ exit(1);
+ }
+
+ share1 = argv[1];
+ share2 = argv[2];
+ nfspath1 = argv[3];
+ nfspath2 = argv[4];
+
+ all_string_sub(share1,"/","\\",0);
+ all_string_sub(share2,"/","\\",0);
+
+ setup_logging(argv[0],True);
+
+ argc -= 4;
+ argv += 4;
+
+ lp_load(dyn_CONFIGFILE,True,False,False);
+ load_interfaces();
+
+ if (getenv("USER")) {
+ fstrcpy(username,getenv("USER"));
+ }
+
+ seed = time(NULL);
+
+ while ((opt = getopt(argc, argv, "U:s:ho:aAW:O")) != EOF) {
+ switch (opt) {
+ case 'U':
+ fstrcpy(username,optarg);
+ p = strchr_m(username,'%');
+ if (p) {
+ *p = 0;
+ fstrcpy(password, p+1);
+ got_pass = 1;
+ }
+ break;
+ case 's':
+ seed = atoi(optarg);
+ break;
+ case 'u':
+ hide_unlock_fails = True;
+ break;
+ case 'o':
+ numops = atoi(optarg);
+ break;
+ case 'O':
+ use_oplocks = True;
+ break;
+ case 'a':
+ showall = True;
+ break;
+ case 'A':
+ analyze = True;
+ break;
+ case 'h':
+ usage();
+ exit(1);
+ default:
+ printf("Unknown option %c (%d)\n", (char)opt, opt);
+ exit(1);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ DEBUG(0,("seed=%u\n", seed));
+ srandom(seed);
+
+ locking_init(1);
+ test_locks(share1, share2, nfspath1, nfspath2);
+
+ return(0);
+}
diff --git a/source/torture/mangle_test.c b/source/torture/mangle_test.c
new file mode 100644
index 00000000000..f31621b23b7
--- /dev/null
+++ b/source/torture/mangle_test.c
@@ -0,0 +1,213 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB torture tester - mangling test
+ Copyright (C) Andrew Tridgell 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static TDB_CONTEXT *tdb;
+
+#define NAME_LENGTH 20
+
+static unsigned total, collisions, failures;
+
+static BOOL test_one(struct cli_state *cli, const char *name)
+{
+ int fnum;
+ fstring shortname;
+ fstring name2;
+ NTSTATUS status;
+ TDB_DATA data;
+
+ total++;
+
+ fnum = cli_open(cli, name, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum == -1) {
+ printf("open of %s failed (%s)\n", name, cli_errstr(cli));
+ return False;
+ }
+
+ if (!cli_close(cli, fnum)) {
+ printf("close of %s failed (%s)\n", name, cli_errstr(cli));
+ return False;
+ }
+
+ /* get the short name */
+ status = cli_qpathinfo_alt_name(cli, name, shortname);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("query altname of %s failed (%s)\n", name, cli_errstr(cli));
+ return False;
+ }
+
+ fstr_sprintf(name2, "\\mangle_test\\%s", shortname);
+ if (!cli_unlink(cli, name2)) {
+ printf("unlink of %s (%s) failed (%s)\n",
+ name2, name, cli_errstr(cli));
+ return False;
+ }
+
+ /* recreate by short name */
+ fnum = cli_open(cli, name2, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum == -1) {
+ printf("open2 of %s failed (%s)\n", name2, cli_errstr(cli));
+ return False;
+ }
+ if (!cli_close(cli, fnum)) {
+ printf("close of %s failed (%s)\n", name, cli_errstr(cli));
+ return False;
+ }
+
+ /* and unlink by long name */
+ if (!cli_unlink(cli, name)) {
+ printf("unlink2 of %s (%s) failed (%s)\n",
+ name, name2, cli_errstr(cli));
+ failures++;
+ cli_unlink(cli, name2);
+ return True;
+ }
+
+ /* see if the short name is already in the tdb */
+ data = tdb_fetch_bystring(tdb, shortname);
+ if (data.dptr) {
+ /* maybe its a duplicate long name? */
+ if (!strequal(name, data.dptr)) {
+ /* we have a collision */
+ collisions++;
+ printf("Collision between %s and %s -> %s "
+ " (coll/tot: %u/%u)\n",
+ name, data.dptr, shortname, collisions, total);
+ }
+ free(data.dptr);
+ } else {
+ TDB_DATA namedata;
+ /* store it for later */
+ namedata.dptr = name;
+ namedata.dsize = strlen(name)+1;
+ tdb_store_bystring(tdb, shortname, namedata, TDB_REPLACE);
+ }
+
+ return True;
+}
+
+
+static void gen_name(char *name)
+{
+ const char *chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._-$~... ";
+ unsigned max_idx = strlen(chars);
+ unsigned len;
+ int i;
+ char *p;
+
+ fstrcpy(name, "\\mangle_test\\");
+ p = name + strlen(name);
+
+ len = 1 + random() % NAME_LENGTH;
+
+ for (i=0;i<len;i++) {
+ p[i] = chars[random() % max_idx];
+ }
+
+ p[i] = 0;
+
+ if (strcmp(p, ".") == 0 || strcmp(p, "..") == 0) {
+ p[0] = '_';
+ }
+
+ /* have a high probability of a common lead char */
+ if (random() % 2 == 0) {
+ p[0] = 'A';
+ }
+
+ /* and a medium probability of a common lead string */
+ if (random() % 10 == 0) {
+ if (strlen(p) <= 5) {
+ fstrcpy(p, "ABCDE");
+ } else {
+ /* try not to kill off the null termination */
+ memcpy(p, "ABCDE", 5);
+ }
+ }
+
+ /* and a high probability of a good extension length */
+ if (random() % 2 == 0) {
+ char *s = strrchr(p, '.');
+ if (s) {
+ s[4] = 0;
+ }
+ }
+}
+
+
+BOOL torture_mangle(int dummy)
+{
+ extern int torture_numops;
+ static struct cli_state *cli;
+ int i;
+ BOOL ret = True;
+
+ printf("starting mangle test\n");
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ /* we will use an internal tdb to store the names we have used */
+ tdb = tdb_open(NULL, 100000, TDB_INTERNAL, 0, 0);
+ if (!tdb) {
+ printf("ERROR: Failed to open tdb\n");
+ return False;
+ }
+
+ cli_unlink(cli, "\\mangle_test\\*");
+ cli_rmdir(cli, "\\mangle_test");
+
+ if (!cli_mkdir(cli, "\\mangle_test")) {
+ printf("ERROR: Failed to make directory\n");
+ return False;
+ }
+
+ for (i=0;i<torture_numops;i++) {
+ fstring name;
+ ZERO_STRUCT(name);
+
+ gen_name(name);
+
+ if (!test_one(cli, name)) {
+ ret = False;
+ break;
+ }
+ if (total && total % 100 == 0) {
+ printf("collisions %u/%u - %.2f%% (%u failures)\r",
+ collisions, total, (100.0*collisions) / total, failures);
+ }
+ }
+
+ cli_unlink(cli, "\\mangle_test\\*");
+ if (!cli_rmdir(cli, "\\mangle_test")) {
+ printf("ERROR: Failed to remove directory\n");
+ return False;
+ }
+
+ printf("\nTotal collisions %u/%u - %.2f%% (%u failures)\n",
+ collisions, total, (100.0*collisions) / total, failures);
+
+ torture_close_connection(cli);
+
+ printf("mangle test finished\n");
+ return (ret && (failures == 0));
+}
diff --git a/source/torture/masktest.c b/source/torture/masktest.c
new file mode 100644
index 00000000000..8c44f35f958
--- /dev/null
+++ b/source/torture/masktest.c
@@ -0,0 +1,538 @@
+/*
+ Unix SMB/CIFS implementation.
+ mask_match tester
+ Copyright (C) Andrew Tridgell 1999
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+static fstring password;
+static fstring username;
+static int got_pass;
+static int max_protocol = PROTOCOL_NT1;
+static BOOL showall = False;
+static BOOL old_list = False;
+static const char *maskchars = "<>\"?*abc.";
+static const char *filechars = "abcdefghijklm.";
+static int verbose;
+static int die_on_error;
+static int NumLoops = 0;
+static int ignore_dot_errors = 0;
+
+/* a test fn for LANMAN mask support */
+int ms_fnmatch_lanman_core(const char *pattern, const char *string)
+{
+ const char *p = pattern, *n = string;
+ char c;
+
+ if (strcmp(p,"?")==0 && strcmp(n,".")==0) goto match;
+
+ while ((c = *p++)) {
+ switch (c) {
+ case '.':
+ /* if (! *n && ! *p) goto match; */
+ if (*n != '.') goto nomatch;
+ n++;
+ break;
+
+ case '?':
+ if ((*n == '.' && n[1] != '.') || ! *n) goto next;
+ n++;
+ break;
+
+ case '>':
+ if (n[0] == '.') {
+ if (! n[1] && ms_fnmatch_lanman_core(p, n+1) == 0) goto match;
+ if (ms_fnmatch_lanman_core(p, n) == 0) goto match;
+ goto nomatch;
+ }
+ if (! *n) goto next;
+ n++;
+ break;
+
+ case '*':
+ if (! *p) goto match;
+ for (; *n; n++) {
+ if (ms_fnmatch_lanman_core(p, n) == 0) goto match;
+ }
+ break;
+
+ case '<':
+ for (; *n; n++) {
+ if (ms_fnmatch_lanman_core(p, n) == 0) goto match;
+ if (*n == '.' && !strchr_m(n+1,'.')) {
+ n++;
+ break;
+ }
+ }
+ break;
+
+ case '"':
+ if (*n == 0 && ms_fnmatch_lanman_core(p, n) == 0) goto match;
+ if (*n != '.') goto nomatch;
+ n++;
+ break;
+
+ default:
+ if (c != *n) goto nomatch;
+ n++;
+ }
+ }
+
+ if (! *n) goto match;
+
+ nomatch:
+ if (verbose) printf("NOMATCH pattern=[%s] string=[%s]\n", pattern, string);
+ return -1;
+
+next:
+ if (ms_fnmatch_lanman_core(p, n) == 0) goto match;
+ goto nomatch;
+
+ match:
+ if (verbose) printf("MATCH pattern=[%s] string=[%s]\n", pattern, string);
+ return 0;
+}
+
+int ms_fnmatch_lanman(const char *pattern, const char *string)
+{
+ if (!strpbrk(pattern, "?*<>\"")) {
+ if (strcmp(string,"..") == 0)
+ string = ".";
+
+ return strcmp(pattern, string);
+ }
+
+ if (strcmp(string,"..") == 0 || strcmp(string,".") == 0) {
+ return ms_fnmatch_lanman_core(pattern, "..") &&
+ ms_fnmatch_lanman_core(pattern, ".");
+ }
+
+ return ms_fnmatch_lanman_core(pattern, string);
+}
+
+static BOOL reg_match_one(struct cli_state *cli, const char *pattern, const char *file)
+{
+ /* oh what a weird world this is */
+ if (old_list && strcmp(pattern, "*.*") == 0) return True;
+
+ if (strcmp(pattern,".") == 0) return False;
+
+ if (max_protocol <= PROTOCOL_LANMAN2) {
+ return ms_fnmatch_lanman(pattern, file)==0;
+ }
+
+ if (strcmp(file,"..") == 0) file = ".";
+
+ return ms_fnmatch(pattern, file, cli->protocol, False /* not case sensitive */)==0;
+}
+
+static char *reg_test(struct cli_state *cli, char *pattern, char *long_name, char *short_name)
+{
+ static fstring ret;
+ fstrcpy(ret, "---");
+
+ pattern = 1+strrchr_m(pattern,'\\');
+
+ if (reg_match_one(cli, pattern, ".")) ret[0] = '+';
+ if (reg_match_one(cli, pattern, "..")) ret[1] = '+';
+ if (reg_match_one(cli, pattern, long_name) ||
+ (*short_name && reg_match_one(cli, pattern, short_name))) ret[2] = '+';
+ return ret;
+}
+
+
+/*****************************************************
+return a connection to a server
+*******************************************************/
+struct cli_state *connect_one(char *share)
+{
+ struct cli_state *c;
+ struct nmb_name called, calling;
+ char *server_n;
+ char *server;
+ struct in_addr ip;
+
+ server = share+2;
+ share = strchr_m(server,'\\');
+ if (!share) return NULL;
+ *share = 0;
+ share++;
+
+ server_n = server;
+
+ zero_ip(&ip);
+
+ make_nmb_name(&calling, "masktest", 0x0);
+ make_nmb_name(&called , server, 0x20);
+
+ again:
+ zero_ip(&ip);
+
+ /* have to open a new connection */
+ if (!(c=cli_initialise(NULL)) || !cli_connect(c, server_n, &ip)) {
+ DEBUG(0,("Connection to %s failed\n", server_n));
+ return NULL;
+ }
+
+ c->protocol = max_protocol;
+
+ if (!cli_session_request(c, &calling, &called)) {
+ DEBUG(0,("session request to %s failed\n", called.name));
+ cli_shutdown(c);
+ if (strcmp(called.name, "*SMBSERVER")) {
+ make_nmb_name(&called , "*SMBSERVER", 0x20);
+ goto again;
+ }
+ return NULL;
+ }
+
+ DEBUG(4,(" session request ok\n"));
+
+ if (!cli_negprot(c)) {
+ DEBUG(0,("protocol negotiation failed\n"));
+ cli_shutdown(c);
+ return NULL;
+ }
+
+ if (!got_pass) {
+ char *pass = getpass("Password: ");
+ if (pass) {
+ fstrcpy(password, pass);
+ }
+ }
+
+ if (!cli_session_setup(c, username,
+ password, strlen(password),
+ password, strlen(password),
+ lp_workgroup())) {
+ DEBUG(0,("session setup failed: %s\n", cli_errstr(c)));
+ return NULL;
+ }
+
+ /*
+ * These next two lines are needed to emulate
+ * old client behaviour for people who have
+ * scripts based on client output.
+ * QUESTION ? Do we want to have a 'client compatibility
+ * mode to turn these on/off ? JRA.
+ */
+
+ if (*c->server_domain || *c->server_os || *c->server_type)
+ DEBUG(1,("Domain=[%s] OS=[%s] Server=[%s]\n",
+ c->server_domain,c->server_os,c->server_type));
+
+ DEBUG(4,(" session setup ok\n"));
+
+ if (!cli_send_tconX(c, share, "?????",
+ password, strlen(password)+1)) {
+ DEBUG(0,("tree connect failed: %s\n", cli_errstr(c)));
+ cli_shutdown(c);
+ return NULL;
+ }
+
+ DEBUG(4,(" tconx ok\n"));
+
+ return c;
+}
+
+static char *resultp;
+static file_info *f_info;
+
+static void listfn(file_info *f, const char *s, void *state)
+{
+ if (strcmp(f->name,".") == 0) {
+ resultp[0] = '+';
+ } else if (strcmp(f->name,"..") == 0) {
+ resultp[1] = '+';
+ } else {
+ resultp[2] = '+';
+ }
+ f_info = f;
+}
+
+static void get_real_name(struct cli_state *cli,
+ pstring long_name, fstring short_name)
+{
+ /* nasty hack to force level 260 listings - tridge */
+ cli->capabilities |= CAP_NT_SMBS;
+ if (max_protocol <= PROTOCOL_LANMAN1) {
+ cli_list_new(cli, "\\masktest\\*.*", aHIDDEN | aDIR, listfn, NULL);
+ } else {
+ cli_list_new(cli, "\\masktest\\*", aHIDDEN | aDIR, listfn, NULL);
+ }
+ if (f_info) {
+ fstrcpy(short_name, f_info->short_name);
+ strlower_m(short_name);
+ pstrcpy(long_name, f_info->name);
+ strlower_m(long_name);
+ }
+
+ if (*short_name == 0) {
+ fstrcpy(short_name, long_name);
+ }
+
+#if 0
+ if (!strchr_m(short_name,'.')) {
+ fstrcat(short_name,".");
+ }
+#endif
+}
+
+static void testpair(struct cli_state *cli, char *mask, char *file)
+{
+ int fnum;
+ fstring res1;
+ char *res2;
+ static int count;
+ fstring short_name;
+ pstring long_name;
+
+ count++;
+
+ fstrcpy(res1, "---");
+
+ fnum = cli_open(cli, file, O_CREAT|O_TRUNC|O_RDWR, 0);
+ if (fnum == -1) {
+ DEBUG(0,("Can't create %s\n", file));
+ return;
+ }
+ cli_close(cli, fnum);
+
+ resultp = res1;
+ fstrcpy(short_name, "");
+ f_info = NULL;
+ get_real_name(cli, long_name, short_name);
+ f_info = NULL;
+ fstrcpy(res1, "---");
+ cli_list(cli, mask, aHIDDEN | aDIR, listfn, NULL);
+
+ res2 = reg_test(cli, mask, long_name, short_name);
+
+ if (showall ||
+ ((strcmp(res1, res2) && !ignore_dot_errors) ||
+ (strcmp(res1+2, res2+2) && ignore_dot_errors))) {
+ DEBUG(0,("%s %s %d mask=[%s] file=[%s] rfile=[%s/%s]\n",
+ res1, res2, count, mask, file, long_name, short_name));
+ if (die_on_error) exit(1);
+ }
+
+ cli_unlink(cli, file);
+
+ if (count % 100 == 0) DEBUG(0,("%d\n", count));
+}
+
+static void test_mask(int argc, char *argv[],
+ struct cli_state *cli)
+{
+ pstring mask, file;
+ int l1, l2, i, l;
+ int mc_len = strlen(maskchars);
+ int fc_len = strlen(filechars);
+
+ cli_mkdir(cli, "\\masktest");
+
+ cli_unlink(cli, "\\masktest\\*");
+
+ if (argc >= 2) {
+ while (argc >= 2) {
+ pstrcpy(mask,"\\masktest\\");
+ pstrcpy(file,"\\masktest\\");
+ pstrcat(mask, argv[0]);
+ pstrcat(file, argv[1]);
+ testpair(cli, mask, file);
+ argv += 2;
+ argc -= 2;
+ }
+ goto finished;
+ }
+
+ while (1) {
+ l1 = 1 + random() % 20;
+ l2 = 1 + random() % 20;
+ pstrcpy(mask,"\\masktest\\");
+ pstrcpy(file,"\\masktest\\");
+ l = strlen(mask);
+ for (i=0;i<l1;i++) {
+ mask[i+l] = maskchars[random() % mc_len];
+ }
+ mask[l+l1] = 0;
+
+ for (i=0;i<l2;i++) {
+ file[i+l] = filechars[random() % fc_len];
+ }
+ file[l+l2] = 0;
+
+ if (strcmp(file+l,".") == 0 ||
+ strcmp(file+l,"..") == 0 ||
+ strcmp(mask+l,"..") == 0) continue;
+
+ if (strspn(file+l, ".") == strlen(file+l)) continue;
+
+ testpair(cli, mask, file);
+ if (NumLoops && (--NumLoops == 0))
+ break;
+ }
+
+ finished:
+ cli_rmdir(cli, "\\masktest");
+}
+
+
+static void usage(void)
+{
+ printf(
+"Usage:\n\
+ masktest //server/share [options..]\n\
+ options:\n\
+ -d debuglevel\n\
+ -n numloops\n\
+ -W workgroup\n\
+ -U user%%pass\n\
+ -s seed\n\
+ -M max protocol\n\
+ -f filechars (default %s)\n\
+ -m maskchars (default %s)\n\
+ -v verbose mode\n\
+ -E die on error\n\
+ -a show all tests\n\
+ -i ignore . and .. errors\n\
+\n\
+ This program tests wildcard matching between two servers. It generates\n\
+ random pairs of filenames/masks and tests that they match in the same\n\
+ way on the servers and internally\n\
+",
+ filechars, maskchars);
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+ char *share;
+ struct cli_state *cli;
+ extern char *optarg;
+ extern int optind;
+ extern BOOL AllowDebugChange;
+ int opt;
+ char *p;
+ int seed;
+
+ setlinebuf(stdout);
+
+ dbf = x_stderr;
+
+ DEBUGLEVEL = 0;
+ AllowDebugChange = False;
+
+ if (argc < 2 || argv[1][0] == '-') {
+ usage();
+ exit(1);
+ }
+
+ share = argv[1];
+
+ all_string_sub(share,"/","\\",0);
+
+ setup_logging(argv[0],True);
+
+ argc -= 1;
+ argv += 1;
+
+ lp_load(dyn_CONFIGFILE,True,False,False);
+ load_interfaces();
+
+ if (getenv("USER")) {
+ fstrcpy(username,getenv("USER"));
+ }
+
+ seed = time(NULL);
+
+ while ((opt = getopt(argc, argv, "n:d:U:s:hm:f:aoW:M:vEi")) != EOF) {
+ switch (opt) {
+ case 'n':
+ NumLoops = atoi(optarg);
+ break;
+ case 'd':
+ DEBUGLEVEL = atoi(optarg);
+ break;
+ case 'E':
+ die_on_error = 1;
+ break;
+ case 'i':
+ ignore_dot_errors = 1;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'M':
+ max_protocol = interpret_protocol(optarg, max_protocol);
+ break;
+ case 'U':
+ fstrcpy(username,optarg);
+ p = strchr_m(username,'%');
+ if (p) {
+ *p = 0;
+ fstrcpy(password, p+1);
+ got_pass = 1;
+ }
+ break;
+ case 's':
+ seed = atoi(optarg);
+ break;
+ case 'h':
+ usage();
+ exit(1);
+ case 'm':
+ maskchars = optarg;
+ break;
+ case 'f':
+ filechars = optarg;
+ break;
+ case 'a':
+ showall = 1;
+ break;
+ case 'o':
+ old_list = True;
+ break;
+ default:
+ printf("Unknown option %c (%d)\n", (char)opt, opt);
+ exit(1);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+
+ cli = connect_one(share);
+ if (!cli) {
+ DEBUG(0,("Failed to connect to %s\n", share));
+ exit(1);
+ }
+
+ /* need to init seed after connect as clientgen uses random numbers */
+ DEBUG(0,("seed=%d\n", seed));
+ srandom(seed);
+
+ test_mask(argc, argv, cli);
+
+ return(0);
+}
diff --git a/source/torture/msgtest.c b/source/torture/msgtest.c
new file mode 100644
index 00000000000..c40973a75c8
--- /dev/null
+++ b/source/torture/msgtest.c
@@ -0,0 +1,91 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) Andrew Tridgell 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ test code for internal messaging
+ */
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+static int pong_count;
+
+/****************************************************************************
+a useful function for testing the message system
+****************************************************************************/
+void pong_message(int msg_type, pid_t src, void *buf, size_t len)
+{
+ pong_count++;
+}
+
+ int main(int argc, char *argv[])
+{
+ pid_t pid;
+ int i, n;
+ char buf[12];
+
+ setup_logging(argv[0],True);
+
+ lp_load(dyn_CONFIGFILE,False,False,False);
+
+ message_init();
+
+ if (argc != 3) {
+ fprintf(stderr, "%s: Usage - %s pid count\n", argv[0], argv[0]);
+ exit(1);
+ }
+
+ pid = atoi(argv[1]);
+ n = atoi(argv[2]);
+
+ message_register(MSG_PONG, pong_message);
+
+ for (i=0;i<n;i++) {
+ message_send_pid(pid, MSG_PING, NULL, 0, True);
+ }
+
+ while (pong_count < i) {
+ message_dispatch();
+ smb_msleep(1);
+ }
+
+ /* Now test that the duplicate filtering code works. */
+ pong_count = 0;
+
+ safe_strcpy(buf, "1234567890", sizeof(buf)-1);
+
+ for (i=0;i<n;i++) {
+ message_send_pid(getpid(), MSG_PING, NULL, 0, False);
+ message_send_pid(getpid(), MSG_PING, buf, 11, False);
+ }
+
+ for (i=0;i<n;i++) {
+ message_dispatch();
+ smb_msleep(1);
+ }
+
+ if (pong_count != 2) {
+ fprintf(stderr, "Duplicate filter failed (%d).\n", pong_count);
+ exit(1);
+ }
+
+ return (0);
+}
+
diff --git a/source/torture/nbio.c b/source/torture/nbio.c
new file mode 100644
index 00000000000..2e79584d23f
--- /dev/null
+++ b/source/torture/nbio.c
@@ -0,0 +1,318 @@
+#define NBDEBUG 0
+
+/*
+ Unix SMB/CIFS implementation.
+ SMB torture tester
+ Copyright (C) Andrew Tridgell 1997-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+#define MAX_FILES 1000
+
+static char buf[70000];
+extern int line_count;
+extern int nbio_id;
+static int nprocs;
+
+static struct {
+ int fd;
+ int handle;
+} ftable[MAX_FILES];
+
+static struct {
+ double bytes_in, bytes_out;
+ int line;
+ int done;
+} *children;
+
+double nbio_total(void)
+{
+ int i;
+ double total = 0;
+ for (i=0;i<nprocs;i++) {
+ total += children[i].bytes_out + children[i].bytes_in;
+ }
+ return total;
+}
+
+void nb_alarm(int ignore)
+{
+ int i;
+ int lines=0, num_clients=0;
+ if (nbio_id != -1) return;
+
+ for (i=0;i<nprocs;i++) {
+ lines += children[i].line;
+ if (!children[i].done) num_clients++;
+ }
+
+ printf("%4d %8d %.2f MB/sec\r", num_clients, lines/nprocs, 1.0e-6 * nbio_total() / end_timer());
+
+ signal(SIGALRM, nb_alarm);
+ alarm(1);
+}
+
+void nbio_shmem(int n)
+{
+ nprocs = n;
+ children = shm_setup(sizeof(*children) * nprocs);
+ if (!children) {
+ printf("Failed to setup shared memory!\n");
+ exit(1);
+ }
+}
+
+#if 0
+static int ne_find_handle(int handle)
+{
+ int i;
+ children[nbio_id].line = line_count;
+ for (i=0;i<MAX_FILES;i++) {
+ if (ftable[i].handle == handle) return i;
+ }
+ return -1;
+}
+#endif
+
+static int find_handle(int handle)
+{
+ int i;
+ children[nbio_id].line = line_count;
+ for (i=0;i<MAX_FILES;i++) {
+ if (ftable[i].handle == handle) return i;
+ }
+ printf("(%d) ERROR: handle %d was not found\n",
+ line_count, handle);
+ exit(1);
+
+ return -1; /* Not reached */
+}
+
+
+static struct cli_state *c;
+
+static void sigsegv(int sig)
+{
+ char line[200];
+ printf("segv at line %d\n", line_count);
+ slprintf(line, sizeof(line), "/usr/X11R6/bin/xterm -e gdb /proc/%d/exe %d",
+ (int)getpid(), (int)getpid());
+ system(line);
+ exit(1);
+}
+
+void nb_setup(struct cli_state *cli)
+{
+ signal(SIGSEGV, sigsegv);
+ c = cli;
+ start_timer();
+ children[nbio_id].done = 0;
+}
+
+
+void nb_unlink(const char *fname)
+{
+ if (!cli_unlink(c, fname)) {
+#if NBDEBUG
+ printf("(%d) unlink %s failed (%s)\n",
+ line_count, fname, cli_errstr(c));
+#endif
+ }
+}
+
+
+void nb_createx(const char *fname,
+ unsigned create_options, unsigned create_disposition, int handle)
+{
+ int fd, i;
+ uint32 desired_access;
+
+ if (create_options & FILE_DIRECTORY_FILE) {
+ desired_access = FILE_READ_DATA;
+ } else {
+ desired_access = FILE_READ_DATA | FILE_WRITE_DATA;
+ }
+
+ fd = cli_nt_create_full(c, fname, 0,
+ desired_access,
+ 0x0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ create_disposition,
+ create_options, 0);
+ if (fd == -1 && handle != -1) {
+ printf("ERROR: cli_nt_create_full failed for %s - %s\n",
+ fname, cli_errstr(c));
+ exit(1);
+ }
+ if (fd != -1 && handle == -1) {
+ printf("ERROR: cli_nt_create_full succeeded for %s\n", fname);
+ exit(1);
+ }
+ if (fd == -1) return;
+
+ for (i=0;i<MAX_FILES;i++) {
+ if (ftable[i].handle == 0) break;
+ }
+ if (i == MAX_FILES) {
+ printf("(%d) file table full for %s\n", line_count,
+ fname);
+ exit(1);
+ }
+ ftable[i].handle = handle;
+ ftable[i].fd = fd;
+}
+
+void nb_writex(int handle, int offset, int size, int ret_size)
+{
+ int i;
+
+ if (buf[0] == 0) memset(buf, 1, sizeof(buf));
+
+ i = find_handle(handle);
+ if (cli_write(c, ftable[i].fd, 0, buf, offset, size) != ret_size) {
+ printf("(%d) ERROR: write failed on handle %d, fd %d \
+errno %d (%s)\n", line_count, handle, ftable[i].fd, errno, strerror(errno));
+ exit(1);
+ }
+
+ children[nbio_id].bytes_out += ret_size;
+}
+
+void nb_readx(int handle, int offset, int size, int ret_size)
+{
+ int i, ret;
+
+ i = find_handle(handle);
+ if ((ret=cli_read(c, ftable[i].fd, buf, offset, size)) != ret_size) {
+ printf("(%d) ERROR: read failed on handle %d ofs=%d size=%d res=%d fd %d errno %d (%s)\n",
+ line_count, handle, offset, size, ret, ftable[i].fd, errno, strerror(errno));
+ exit(1);
+ }
+ children[nbio_id].bytes_in += ret_size;
+}
+
+void nb_close(int handle)
+{
+ int i;
+ i = find_handle(handle);
+ if (!cli_close(c, ftable[i].fd)) {
+ printf("(%d) close failed on handle %d\n", line_count, handle);
+ exit(1);
+ }
+ ftable[i].handle = 0;
+}
+
+void nb_rmdir(const char *fname)
+{
+ if (!cli_rmdir(c, fname)) {
+ printf("ERROR: rmdir %s failed (%s)\n",
+ fname, cli_errstr(c));
+ exit(1);
+ }
+}
+
+void nb_rename(const char *old, const char *new)
+{
+ if (!cli_rename(c, old, new)) {
+ printf("ERROR: rename %s %s failed (%s)\n",
+ old, new, cli_errstr(c));
+ exit(1);
+ }
+}
+
+
+void nb_qpathinfo(const char *fname)
+{
+ cli_qpathinfo(c, fname, NULL, NULL, NULL, NULL, NULL);
+}
+
+void nb_qfileinfo(int fnum)
+{
+ int i;
+ i = find_handle(fnum);
+ cli_qfileinfo(c, ftable[i].fd, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+}
+
+void nb_qfsinfo(int level)
+{
+ int bsize, total, avail;
+ /* this is not the right call - we need cli_qfsinfo() */
+ cli_dskattr(c, &bsize, &total, &avail);
+}
+
+static void find_fn(file_info *finfo, const char *name, void *state)
+{
+ /* noop */
+}
+
+void nb_findfirst(const char *mask)
+{
+ cli_list(c, mask, 0, find_fn, NULL);
+}
+
+void nb_flush(int fnum)
+{
+ int i;
+ i = find_handle(fnum);
+ /* hmmm, we don't have cli_flush() yet */
+}
+
+static int total_deleted;
+
+static void delete_fn(file_info *finfo, const char *name, void *state)
+{
+ char *s, *n;
+ if (finfo->name[0] == '.') return;
+
+ n = strdup(name);
+ n[strlen(n)-1] = 0;
+ asprintf(&s, "%s%s", n, finfo->name);
+ if (finfo->mode & aDIR) {
+ char *s2;
+ asprintf(&s2, "%s\\*", s);
+ cli_list(c, s2, aDIR, delete_fn, NULL);
+ nb_rmdir(s);
+ } else {
+ total_deleted++;
+ nb_unlink(s);
+ }
+ free(s);
+ free(n);
+}
+
+void nb_deltree(const char *dname)
+{
+ char *mask;
+ asprintf(&mask, "%s\\*", dname);
+
+ total_deleted = 0;
+ cli_list(c, mask, aDIR, delete_fn, NULL);
+ free(mask);
+ cli_rmdir(c, dname);
+
+ if (total_deleted) printf("WARNING: Cleaned up %d files\n", total_deleted);
+}
+
+
+void nb_cleanup(void)
+{
+ cli_rmdir(c, "clients");
+ children[nbio_id].done = 1;
+}
diff --git a/source/torture/nsstest.c b/source/torture/nsstest.c
new file mode 100644
index 00000000000..a803cd7e719
--- /dev/null
+++ b/source/torture/nsstest.c
@@ -0,0 +1,451 @@
+/*
+ Unix SMB/CIFS implementation.
+ nss tester for winbindd
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Tim Potter 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static const char *so_path = "/lib/libnss_winbind.so";
+static const char *nss_name = "winbind";
+static int nss_errno;
+static NSS_STATUS last_error;
+static int total_errors;
+
+static void *find_fn(const char *name)
+{
+ pstring s;
+ static void *h;
+ void *res;
+
+ pstr_sprintf(s, "_nss_%s_%s", nss_name, name);
+
+ if (!h) {
+ h = sys_dlopen(so_path, RTLD_LAZY);
+ }
+ if (!h) {
+ printf("Can't open shared library %s\n", so_path);
+ exit(1);
+ }
+ res = sys_dlsym(h, s);
+ if (!res) {
+ printf("Can't find function %s\n", s);
+ total_errors++;
+ return NULL;
+ }
+ return res;
+}
+
+static void report_nss_error(const char *who, NSS_STATUS status)
+{
+ last_error = status;
+ total_errors++;
+ printf("ERROR %s: NSS_STATUS=%d %d (nss_errno=%d)\n",
+ who, status, NSS_STATUS_SUCCESS, nss_errno);
+}
+
+static struct passwd *nss_getpwent(void)
+{
+ NSS_STATUS (*_nss_getpwent_r)(struct passwd *, char *,
+ size_t , int *) = find_fn("getpwent_r");
+ static struct passwd pwd;
+ static char buf[1000];
+ NSS_STATUS status;
+
+ if (!_nss_getpwent_r)
+ return NULL;
+
+ status = _nss_getpwent_r(&pwd, buf, sizeof(buf), &nss_errno);
+ if (status == NSS_STATUS_NOTFOUND) {
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getpwent", status);
+ return NULL;
+ }
+ return &pwd;
+}
+
+static struct passwd *nss_getpwnam(const char *name)
+{
+ NSS_STATUS (*_nss_getpwnam_r)(const char *, struct passwd *, char *,
+ size_t , int *) = find_fn("getpwnam_r");
+ static struct passwd pwd;
+ static char buf[1000];
+ NSS_STATUS status;
+
+ if (!_nss_getpwnam_r)
+ return NULL;
+
+ status = _nss_getpwnam_r(name, &pwd, buf, sizeof(buf), &nss_errno);
+ if (status == NSS_STATUS_NOTFOUND) {
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getpwnam", status);
+ return NULL;
+ }
+ return &pwd;
+}
+
+static struct passwd *nss_getpwuid(uid_t uid)
+{
+ NSS_STATUS (*_nss_getpwuid_r)(uid_t , struct passwd *, char *,
+ size_t , int *) = find_fn("getpwuid_r");
+ static struct passwd pwd;
+ static char buf[1000];
+ NSS_STATUS status;
+
+ if (!_nss_getpwuid_r)
+ return NULL;
+
+ status = _nss_getpwuid_r(uid, &pwd, buf, sizeof(buf), &nss_errno);
+ if (status == NSS_STATUS_NOTFOUND) {
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getpwuid", status);
+ return NULL;
+ }
+ return &pwd;
+}
+
+static void nss_setpwent(void)
+{
+ NSS_STATUS (*_nss_setpwent)(void) = find_fn("setpwent");
+ NSS_STATUS status;
+
+ if (!_nss_setpwent)
+ return;
+
+ status = _nss_setpwent();
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("setpwent", status);
+ }
+}
+
+static void nss_endpwent(void)
+{
+ NSS_STATUS (*_nss_endpwent)(void) = find_fn("endpwent");
+ NSS_STATUS status;
+
+ if (!_nss_endpwent)
+ return;
+
+ status = _nss_endpwent();
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("endpwent", status);
+ }
+}
+
+
+static struct group *nss_getgrent(void)
+{
+ NSS_STATUS (*_nss_getgrent_r)(struct group *, char *,
+ size_t , int *) = find_fn("getgrent_r");
+ static struct group grp;
+ static char *buf;
+ static int buflen = 1024;
+ NSS_STATUS status;
+
+ if (!_nss_getgrent_r)
+ return NULL;
+
+ if (!buf)
+ buf = malloc(buflen);
+
+again:
+ status = _nss_getgrent_r(&grp, buf, buflen, &nss_errno);
+ if (status == NSS_STATUS_TRYAGAIN) {
+ buflen *= 2;
+ buf = realloc(buf, buflen);
+ goto again;
+ }
+ if (status == NSS_STATUS_NOTFOUND) {
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getgrent", status);
+ return NULL;
+ }
+ return &grp;
+}
+
+static struct group *nss_getgrnam(const char *name)
+{
+ NSS_STATUS (*_nss_getgrnam_r)(const char *, struct group *, char *,
+ size_t , int *) = find_fn("getgrnam_r");
+ static struct group grp;
+ static char *buf;
+ static int buflen = 1000;
+ NSS_STATUS status;
+
+ if (!_nss_getgrnam_r)
+ return NULL;
+
+ if (!buf)
+ buf = malloc(buflen);
+again:
+ status = _nss_getgrnam_r(name, &grp, buf, buflen, &nss_errno);
+ if (status == NSS_STATUS_TRYAGAIN) {
+ buflen *= 2;
+ buf = realloc(buf, buflen);
+ goto again;
+ }
+ if (status == NSS_STATUS_NOTFOUND) {
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getgrnam", status);
+ return NULL;
+ }
+ return &grp;
+}
+
+static struct group *nss_getgrgid(gid_t gid)
+{
+ NSS_STATUS (*_nss_getgrgid_r)(gid_t , struct group *, char *,
+ size_t , int *) = find_fn("getgrgid_r");
+ static struct group grp;
+ static char *buf;
+ static int buflen = 1000;
+ NSS_STATUS status;
+
+ if (!_nss_getgrgid_r)
+ return NULL;
+
+ if (!buf)
+ buf = malloc(buflen);
+
+again:
+ status = _nss_getgrgid_r(gid, &grp, buf, buflen, &nss_errno);
+ if (status == NSS_STATUS_TRYAGAIN) {
+ buflen *= 2;
+ buf = realloc(buf, buflen);
+ goto again;
+ }
+ if (status == NSS_STATUS_NOTFOUND) {
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getgrgid", status);
+ return NULL;
+ }
+ return &grp;
+}
+
+static void nss_setgrent(void)
+{
+ NSS_STATUS (*_nss_setgrent)(void) = find_fn("setgrent");
+ NSS_STATUS status;
+
+ if (!_nss_setgrent)
+ return;
+
+ status = _nss_setgrent();
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("setgrent", status);
+ }
+}
+
+static void nss_endgrent(void)
+{
+ NSS_STATUS (*_nss_endgrent)(void) = find_fn("endgrent");
+ NSS_STATUS status;
+
+ if (!_nss_endgrent)
+ return;
+
+ status = _nss_endgrent();
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("endgrent", status);
+ }
+}
+
+static int nss_initgroups(char *user, gid_t group, gid_t **groups, long int *start, long int *size)
+{
+ NSS_STATUS (*_nss_initgroups)(char *, gid_t , long int *,
+ long int *, gid_t **, long int , int *) =
+ find_fn("initgroups_dyn");
+ NSS_STATUS status;
+
+ if (!_nss_initgroups)
+ return NSS_STATUS_UNAVAIL;
+
+ status = _nss_initgroups(user, group, start, size, groups, 0, &nss_errno);
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("initgroups", status);
+ }
+ return status;
+}
+
+static void print_passwd(struct passwd *pwd)
+{
+ printf("%s:%s:%lu:%lu:%s:%s:%s\n",
+ pwd->pw_name,
+ pwd->pw_passwd,
+ (unsigned long)pwd->pw_uid,
+ (unsigned long)pwd->pw_gid,
+ pwd->pw_gecos,
+ pwd->pw_dir,
+ pwd->pw_shell);
+}
+
+static void print_group(struct group *grp)
+{
+ int i;
+ printf("%s:%s:%lu: ",
+ grp->gr_name,
+ grp->gr_passwd,
+ (unsigned long)grp->gr_gid);
+
+ if (!grp->gr_mem[0]) {
+ printf("\n");
+ return;
+ }
+
+ for (i=0; grp->gr_mem[i+1]; i++) {
+ printf("%s, ", grp->gr_mem[i]);
+ }
+ printf("%s\n", grp->gr_mem[i]);
+}
+
+static void nss_test_initgroups(char *name, gid_t gid)
+{
+ long int size = 16;
+ long int start = 1;
+ gid_t *groups = NULL;
+ int i;
+ NSS_STATUS status;
+
+ groups = (gid_t *)malloc(size * sizeof(gid_t));
+ groups[0] = gid;
+
+ status = nss_initgroups(name, gid, &groups, &start, &size);
+ if (status == NSS_STATUS_UNAVAIL) {
+ printf("No initgroups fn\n");
+ return;
+ }
+
+ for (i=0; i<start-1; i++) {
+ printf("%lu, ", (unsigned long)groups[i]);
+ }
+ printf("%lu\n", (unsigned long)groups[i]);
+}
+
+
+static void nss_test_users(void)
+{
+ struct passwd *pwd;
+
+ nss_setpwent();
+ /* loop over all users */
+ while ((pwd = nss_getpwent())) {
+ printf("Testing user %s\n", pwd->pw_name);
+ printf("getpwent: "); print_passwd(pwd);
+ pwd = nss_getpwuid(pwd->pw_uid);
+ if (!pwd) {
+ total_errors++;
+ printf("ERROR: can't getpwuid\n");
+ continue;
+ }
+ printf("getpwuid: "); print_passwd(pwd);
+ pwd = nss_getpwnam(pwd->pw_name);
+ if (!pwd) {
+ total_errors++;
+ printf("ERROR: can't getpwnam\n");
+ continue;
+ }
+ printf("getpwnam: "); print_passwd(pwd);
+ printf("initgroups: "); nss_test_initgroups(pwd->pw_name, pwd->pw_gid);
+ printf("\n");
+ }
+ nss_endpwent();
+}
+
+static void nss_test_groups(void)
+{
+ struct group *grp;
+
+ nss_setgrent();
+ /* loop over all groups */
+ while ((grp = nss_getgrent())) {
+ printf("Testing group %s\n", grp->gr_name);
+ printf("getgrent: "); print_group(grp);
+ grp = nss_getgrnam(grp->gr_name);
+ if (!grp) {
+ total_errors++;
+ printf("ERROR: can't getgrnam\n");
+ continue;
+ }
+ printf("getgrnam: "); print_group(grp);
+ grp = nss_getgrgid(grp->gr_gid);
+ if (!grp) {
+ total_errors++;
+ printf("ERROR: can't getgrgid\n");
+ continue;
+ }
+ printf("getgrgid: "); print_group(grp);
+ printf("\n");
+ }
+ nss_endgrent();
+}
+
+static void nss_test_errors(void)
+{
+ struct passwd *pwd;
+ struct group *grp;
+
+ pwd = getpwnam("nosuchname");
+ if (pwd || last_error != NSS_STATUS_NOTFOUND) {
+ total_errors++;
+ printf("ERROR Non existant user gave error %d\n", last_error);
+ }
+
+ pwd = getpwuid(0xFFF0);
+ if (pwd || last_error != NSS_STATUS_NOTFOUND) {
+ total_errors++;
+ printf("ERROR Non existant uid gave error %d\n", last_error);
+ }
+
+ grp = getgrnam("nosuchgroup");
+ if (grp || last_error != NSS_STATUS_NOTFOUND) {
+ total_errors++;
+ printf("ERROR Non existant group gave error %d\n", last_error);
+ }
+
+ grp = getgrgid(0xFFF0);
+ if (grp || last_error != NSS_STATUS_NOTFOUND) {
+ total_errors++;
+ printf("ERROR Non existant gid gave error %d\n", last_error);
+ }
+}
+
+ int main(int argc, char *argv[])
+{
+ if (argc > 1) so_path = argv[1];
+ if (argc > 2) nss_name = argv[2];
+
+ nss_test_users();
+ nss_test_groups();
+ nss_test_errors();
+
+ printf("total_errors=%d\n", total_errors);
+
+ return total_errors;
+}
diff --git a/source/torture/rpctorture.c b/source/torture/rpctorture.c
new file mode 100644
index 00000000000..d95c0cee0fe
--- /dev/null
+++ b/source/torture/rpctorture.c
@@ -0,0 +1,541 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client
+ Copyright (C) Andrew Tridgell 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#ifndef REGISTER
+#define REGISTER 0
+#endif
+
+extern pstring global_myname;
+
+extern pstring user_socket_options;
+
+
+extern file_info def_finfo;
+
+#define CNV_LANG(s) dos2unix_format(s,False)
+#define CNV_INPUT(s) unix2dos_format(s,True)
+
+static struct cli_state smbcli;
+struct cli_state *smb_cli = &smbcli;
+
+FILE *out_hnd;
+
+static pstring password; /* local copy only, if one is entered */
+
+/****************************************************************************
+initialise smb client structure
+****************************************************************************/
+void rpcclient_init(void)
+{
+ memset((char *)smb_cli, '\0', sizeof(smb_cli));
+ cli_initialise(smb_cli);
+ smb_cli->capabilities |= CAP_NT_SMBS;
+}
+
+/****************************************************************************
+make smb client connection
+****************************************************************************/
+static BOOL rpcclient_connect(struct client_info *info)
+{
+ struct nmb_name calling;
+ struct nmb_name called;
+
+ make_nmb_name(&called , dns_to_netbios_name(info->dest_host ), info->name_type);
+ make_nmb_name(&calling, dns_to_netbios_name(info->myhostname), 0x0);
+
+ if (!cli_establish_connection(smb_cli,
+ info->dest_host, &info->dest_ip,
+ &calling, &called,
+ info->share, info->svc_type,
+ False, True))
+ {
+ DEBUG(0,("rpcclient_connect: connection failed\n"));
+ cli_shutdown(smb_cli);
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+stop the smb connection(s?)
+****************************************************************************/
+static void rpcclient_stop(void)
+{
+ cli_shutdown(smb_cli);
+}
+
+/****************************************************************************
+ log in as an nt user, log out again.
+****************************************************************************/
+void run_enums_test(int num_ops, struct client_info *cli_info, struct cli_state *cli)
+{
+ pstring cmd;
+ int i;
+
+ /* establish connections. nothing to stop these being re-established. */
+ rpcclient_connect(cli_info);
+
+ DEBUG(5,("rpcclient_connect: cli->fd:%d\n", cli->fd));
+ if (cli->fd <= 0)
+ {
+ fprintf(out_hnd, "warning: connection could not be established to %s<%02x>\n",
+ cli_info->dest_host, cli_info->name_type);
+ return;
+ }
+
+ for (i = 0; i < num_ops; i++)
+ {
+ set_first_token("");
+ cmd_srv_enum_sess(cli_info);
+ set_first_token("");
+ cmd_srv_enum_shares(cli_info);
+ set_first_token("");
+ cmd_srv_enum_files(cli_info);
+
+ if (password[0] != 0)
+ {
+ slprintf(cmd, sizeof(cmd)-1, "1");
+ set_first_token(cmd);
+ }
+ else
+ {
+ set_first_token("");
+ }
+ cmd_srv_enum_conn(cli_info);
+ }
+
+ rpcclient_stop();
+
+}
+
+/****************************************************************************
+ log in as an nt user, log out again.
+****************************************************************************/
+void run_ntlogin_test(int num_ops, struct client_info *cli_info, struct cli_state *cli)
+{
+ pstring cmd;
+ int i;
+
+ /* establish connections. nothing to stop these being re-established. */
+ rpcclient_connect(cli_info);
+
+ DEBUG(5,("rpcclient_connect: cli->fd:%d\n", cli->fd));
+ if (cli->fd <= 0)
+ {
+ fprintf(out_hnd, "warning: connection could not be established to %s<%02x>\n",
+ cli_info->dest_host, cli_info->name_type);
+ return;
+ }
+
+ for (i = 0; i < num_ops; i++)
+ {
+ slprintf(cmd, sizeof(cmd)-1, "%s %s", cli->user_name, password);
+ set_first_token(cmd);
+
+ cmd_netlogon_login_test(cli_info);
+ }
+
+ rpcclient_stop();
+
+}
+
+/****************************************************************************
+ runs n simultaneous functions.
+****************************************************************************/
+static void create_procs(int nprocs, int numops,
+ struct client_info *cli_info, struct cli_state *cli,
+ void (*fn)(int, struct client_info *, struct cli_state *))
+{
+ int i, status;
+
+ for (i=0;i<nprocs;i++)
+ {
+ if (fork() == 0)
+ {
+ pid_t mypid = getpid();
+ sys_srandom(mypid ^ time(NULL));
+ fn(numops, cli_info, cli);
+ fflush(out_hnd);
+ _exit(0);
+ }
+ }
+
+ for (i=0;i<nprocs;i++)
+ {
+ waitpid(0, &status, 0);
+ }
+}
+/****************************************************************************
+usage on the program - OUT OF DATE!
+****************************************************************************/
+static void usage(char *pname)
+{
+ fprintf(out_hnd, "Usage: %s service <password> [-d debuglevel] [-l log] ",
+ pname);
+
+ fprintf(out_hnd, "\nVersion %s\n",SAMBA_VERSION_STRING);
+ fprintf(out_hnd, "\t-d debuglevel set the debuglevel\n");
+ fprintf(out_hnd, "\t-l log basename. Basename for log/debug files\n");
+ fprintf(out_hnd, "\t-n netbios name. Use this name as my netbios name\n");
+ fprintf(out_hnd, "\t-m max protocol set the max protocol level\n");
+ fprintf(out_hnd, "\t-I dest IP use this IP to connect to\n");
+ fprintf(out_hnd, "\t-E write messages to stderr instead of stdout\n");
+ fprintf(out_hnd, "\t-U username set the network username\n");
+ fprintf(out_hnd, "\t-W workgroup set the workgroup name\n");
+ fprintf(out_hnd, "\t-t terminal code terminal i/o code {sjis|euc|jis7|jis8|junet|hex}\n");
+ fprintf(out_hnd, "\n");
+}
+
+enum client_action
+{
+ CLIENT_NONE,
+ CLIENT_IPC,
+ CLIENT_SVC
+};
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+ char *pname = argv[0];
+ int opt;
+ extern char *optarg;
+ extern int optind;
+ pstring term_code;
+ BOOL got_pass = False;
+ char *cmd_str="";
+ enum client_action cli_action = CLIENT_NONE;
+ int nprocs = 1;
+ int numops = 100;
+ pstring logfile;
+
+ struct client_info cli_info;
+
+ out_hnd = stdout;
+
+ rpcclient_init();
+
+#ifdef KANJI
+ pstrcpy(term_code, KANJI);
+#else /* KANJI */
+ *term_code = 0;
+#endif /* KANJI */
+
+ if (!lp_load(dyn_CONFIGFILE,True, False, False))
+ {
+ fprintf(stderr, "Can't load %s - run testparm to debug it\n", dyn_CONFIGFILE);
+ }
+
+ DEBUGLEVEL = 0;
+
+ cli_info.put_total_size = 0;
+ cli_info.put_total_time_ms = 0;
+ cli_info.get_total_size = 0;
+ cli_info.get_total_time_ms = 0;
+
+ cli_info.dir_total = 0;
+ cli_info.newer_than = 0;
+ cli_info.archive_level = 0;
+ cli_info.print_mode = 1;
+
+ cli_info.translation = False;
+ cli_info.recurse_dir = False;
+ cli_info.lowercase = False;
+ cli_info.prompt = True;
+ cli_info.abort_mget = True;
+
+ cli_info.dest_ip.s_addr = 0;
+ cli_info.name_type = 0x20;
+
+ pstrcpy(cli_info.cur_dir , "\\");
+ pstrcpy(cli_info.file_sel, "");
+ pstrcpy(cli_info.base_dir, "");
+ pstrcpy(smb_cli->domain, "");
+ pstrcpy(smb_cli->user_name, "");
+ pstrcpy(cli_info.myhostname, "");
+ pstrcpy(cli_info.dest_host, "");
+
+ pstrcpy(cli_info.svc_type, "A:");
+ pstrcpy(cli_info.share, "");
+ pstrcpy(cli_info.service, "");
+
+ ZERO_STRUCT(cli_info.dom.level3_sid);
+ pstrcpy(cli_info.dom.level3_dom, "");
+ ZERO_STRUCT(cli_info.dom.level5_sid);
+ pstrcpy(cli_info.dom.level5_dom, "");
+
+ smb_cli->nt_pipe_fnum = 0xffff;
+
+ setup_logging(pname, True);
+
+ if (!get_myname(global_myname))
+ {
+ fprintf(stderr, "Failed to get my hostname.\n");
+ }
+
+ password[0] = 0;
+
+ if (argc < 2)
+ {
+ usage(pname);
+ exit(1);
+ }
+
+ if (*argv[1] != '-')
+ {
+ pstrcpy(cli_info.service, argv[1]);
+ /* Convert any '/' characters in the service name to '\' characters */
+ string_replace( cli_info.service, '/','\\');
+ argc--;
+ argv++;
+
+ DEBUG(1,("service: %s\n", cli_info.service));
+
+ if (count_chars(cli_info.service,'\\') < 3)
+ {
+ usage(pname);
+ printf("\n%s: Not enough '\\' characters in service\n", cli_info.service);
+ exit(1);
+ }
+
+ /*
+ if (count_chars(cli_info.service,'\\') > 3)
+ {
+ usage(pname);
+ printf("\n%s: Too many '\\' characters in service\n", cli_info.service);
+ exit(1);
+ }
+ */
+
+ if (argc > 1 && (*argv[1] != '-'))
+ {
+ got_pass = True;
+ pstrcpy(password,argv[1]);
+ memset(argv[1],'X',strlen(argv[1]));
+ argc--;
+ argv++;
+ }
+
+ cli_action = CLIENT_SVC;
+ }
+
+ while ((opt = getopt(argc, argv,"s:O:M:S:i:N:o:n:d:l:hI:EB:U:L:t:m:W:T:D:c:")) != EOF)
+ {
+ switch (opt)
+ {
+ case 'm':
+ {
+ /* FIXME ... max_protocol seems to be funny here */
+
+ int max_protocol = 0;
+ max_protocol = interpret_protocol(optarg,max_protocol);
+ fprintf(stderr, "max protocol not currently supported\n");
+ break;
+ }
+
+ case 'O':
+ {
+ pstrcpy(user_socket_options,optarg);
+ break;
+ }
+
+ case 'S':
+ {
+ pstrcpy(cli_info.dest_host,optarg);
+ strupper_m(cli_info.dest_host);
+ cli_action = CLIENT_IPC;
+ break;
+ }
+
+ case 'i':
+ {
+ pstrcpy(scope, optarg);
+ break;
+ }
+
+ case 'U':
+ {
+ char *lp;
+ pstrcpy(smb_cli->user_name,optarg);
+ if ((lp=strchr_m(smb_cli->user_name,'%')))
+ {
+ *lp = 0;
+ pstrcpy(password,lp+1);
+ got_pass = True;
+ memset(strchr_m(optarg,'%')+1,'X',strlen(password));
+ }
+ break;
+ }
+
+ case 'W':
+ {
+ pstrcpy(smb_cli->domain,optarg);
+ break;
+ }
+
+ case 'E':
+ {
+ dbf = x_stderr;
+ break;
+ }
+
+ case 'I':
+ {
+ cli_info.dest_ip = *interpret_addr2(optarg);
+ if (is_zero_ip(cli_info.dest_ip))
+ {
+ exit(1);
+ }
+ break;
+ }
+
+ case 'N':
+ {
+ nprocs = atoi(optarg);
+ break;
+ }
+
+ case 'o':
+ {
+ numops = atoi(optarg);
+ break;
+ }
+
+ case 'n':
+ {
+ fstrcpy(global_myname, optarg);
+ break;
+ }
+
+ case 'd':
+ {
+ if (*optarg == 'A')
+ DEBUGLEVEL = 10000;
+ else
+ DEBUGLEVEL = atoi(optarg);
+ break;
+ }
+
+ case 'l':
+ {
+ slprintf(logfile, sizeof(logfile)-1,
+ "%s.client",optarg);
+ lp_set_logfile(logfile);
+ break;
+ }
+
+ case 'c':
+ {
+ cmd_str = optarg;
+ got_pass = True;
+ break;
+ }
+
+ case 'h':
+ {
+ usage(pname);
+ exit(0);
+ break;
+ }
+
+ case 's':
+ {
+ pstrcpy(dyn_CONFIGFILE, optarg);
+ break;
+ }
+
+ case 't':
+ {
+ pstrcpy(term_code, optarg);
+ break;
+ }
+
+ default:
+ {
+ usage(pname);
+ exit(1);
+ break;
+ }
+ }
+ }
+
+ if (cli_action == CLIENT_NONE)
+ {
+ usage(pname);
+ exit(1);
+ }
+
+ strupper_m(global_myname);
+ fstrcpy(cli_info.myhostname, global_myname);
+
+ DEBUG(3,("%s client started (version %s)\n",timestring(False),SAMBA_VERSION_STRING));
+
+ if (*smb_cli->domain == 0)
+ {
+ pstrcpy(smb_cli->domain,lp_workgroup());
+ }
+ strupper_m(smb_cli->domain);
+
+ load_interfaces();
+
+ if (cli_action == CLIENT_IPC)
+ {
+ pstrcpy(cli_info.share, "IPC$");
+ pstrcpy(cli_info.svc_type, "IPC");
+ }
+
+ fstrcpy(cli_info.mach_acct, cli_info.myhostname);
+ strupper_m(cli_info.mach_acct);
+ fstrcat(cli_info.mach_acct, "$");
+
+ /* set the password cache info */
+ if (got_pass)
+ {
+ if (password[0] == 0)
+ {
+ pwd_set_nullpwd(&(smb_cli->pwd));
+ }
+ else
+ {
+ pwd_make_lm_nt_16(&(smb_cli->pwd), password); /* generate 16 byte hashes */
+ }
+ }
+ else
+ {
+ char *pwd = getpass("Enter Password:");
+ safe_strcpy(password, pwd, sizeof(password));
+ pwd_make_lm_nt_16(&(smb_cli->pwd), password); /* generate 16 byte hashes */
+ }
+
+ create_procs(nprocs, numops, &cli_info, smb_cli, run_enums_test);
+
+ if (password[0] != 0)
+ {
+ create_procs(nprocs, numops, &cli_info, smb_cli, run_ntlogin_test);
+ }
+
+ fflush(out_hnd);
+
+ return(0);
+}
diff --git a/source/torture/samtest.h b/source/torture/samtest.h
new file mode 100644
index 00000000000..a136ab191e4
--- /dev/null
+++ b/source/torture/samtest.h
@@ -0,0 +1,38 @@
+/*
+ Unix SMB/CIFS implementation.
+ SAM module tester
+
+ Copyright (C) Jelmer Vernooij 2002
+
+ Most of this code was ripped off of rpcclient.
+ Copyright (C) Tim Potter 2000-2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+struct samtest_state {
+ SAM_CONTEXT *context;
+ NT_USER_TOKEN *token;
+};
+
+struct cmd_set {
+ char *name;
+ NTSTATUS (*fn)(struct samtest_state *sam, TALLOC_CTX *mem_ctx, int argc,
+ char **argv);
+ char *description;
+ char *usage;
+};
+
+
diff --git a/source/torture/scanner.c b/source/torture/scanner.c
new file mode 100644
index 00000000000..93f89c105cf
--- /dev/null
+++ b/source/torture/scanner.c
@@ -0,0 +1,430 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB torture tester - scanning functions
+ Copyright (C) Andrew Tridgell 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+#define VERBOSE 0
+#define OP_MIN 0
+#define OP_MAX 20
+
+/****************************************************************************
+look for a partial hit
+****************************************************************************/
+static void trans2_check_hit(const char *format, int op, int level, NTSTATUS status)
+{
+ if (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_INVALID_LEVEL) ||
+ NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_NOT_IMPLEMENTED) ||
+ NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_NOT_SUPPORTED) ||
+ NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) ||
+ NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_INVALID_INFO_CLASS)) {
+ return;
+ }
+#if VERBOSE
+ printf("possible %s hit op=%3d level=%5d status=%s\n",
+ format, op, level, nt_errstr(status));
+#endif
+}
+
+/****************************************************************************
+check for existance of a trans2 call
+****************************************************************************/
+static NTSTATUS try_trans2(struct cli_state *cli,
+ int op,
+ char *param, char *data,
+ int param_len, int data_len,
+ int *rparam_len, int *rdata_len)
+{
+ uint16 setup = op;
+ char *rparam=NULL, *rdata=NULL;
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL, /* name */
+ -1, 0, /* fid, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 2, /* param, length, max */
+ data, data_len, cli->max_xmit /* data, length, max */
+ )) {
+ return cli_nt_error(cli);
+ }
+
+ cli_receive_trans(cli, SMBtrans2,
+ &rparam, rparam_len,
+ &rdata, rdata_len);
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ return cli_nt_error(cli);
+}
+
+
+static NTSTATUS try_trans2_len(struct cli_state *cli,
+ const char *format,
+ int op, int level,
+ char *param, char *data,
+ int param_len, int *data_len,
+ int *rparam_len, int *rdata_len)
+{
+ NTSTATUS ret=NT_STATUS_OK;
+
+ ret = try_trans2(cli, op, param, data, param_len,
+ sizeof(pstring), rparam_len, rdata_len);
+#if VERBOSE
+ printf("op=%d level=%d ret=%s\n", op, level, nt_errstr(ret));
+#endif
+ if (!NT_STATUS_IS_OK(ret)) return ret;
+
+ *data_len = 0;
+ while (*data_len < sizeof(pstring)) {
+ ret = try_trans2(cli, op, param, data, param_len,
+ *data_len, rparam_len, rdata_len);
+ if (NT_STATUS_IS_OK(ret)) break;
+ *data_len += 2;
+ }
+ if (NT_STATUS_IS_OK(ret)) {
+ printf("found %s level=%d data_len=%d rparam_len=%d rdata_len=%d\n",
+ format, level, *data_len, *rparam_len, *rdata_len);
+ } else {
+ trans2_check_hit(format, op, level, ret);
+ }
+ return ret;
+}
+
+/****************************************************************************
+check for existance of a trans2 call
+****************************************************************************/
+static BOOL scan_trans2(struct cli_state *cli, int op, int level,
+ int fnum, int dnum, const char *fname)
+{
+ int data_len = 0;
+ int param_len = 0;
+ int rparam_len, rdata_len;
+ pstring param, data;
+ NTSTATUS status;
+
+ memset(data, 0, sizeof(data));
+ data_len = 4;
+
+ /* try with a info level only */
+ param_len = 2;
+ SSVAL(param, 0, level);
+ status = try_trans2_len(cli, "void", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ if (NT_STATUS_IS_OK(status)) return True;
+
+ /* try with a file descriptor */
+ param_len = 6;
+ SSVAL(param, 0, fnum);
+ SSVAL(param, 2, level);
+ SSVAL(param, 4, 0);
+ status = try_trans2_len(cli, "fnum", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ if (NT_STATUS_IS_OK(status)) return True;
+
+
+ /* try with a notify style */
+ param_len = 6;
+ SSVAL(param, 0, dnum);
+ SSVAL(param, 2, dnum);
+ SSVAL(param, 4, level);
+ status = try_trans2_len(cli, "notify", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ if (NT_STATUS_IS_OK(status)) return True;
+
+ /* try with a file name */
+ param_len = 6;
+ SSVAL(param, 0, level);
+ SSVAL(param, 2, 0);
+ SSVAL(param, 4, 0);
+ param_len += clistr_push(cli, &param[6], fname, -1, STR_TERMINATE);
+
+ status = try_trans2_len(cli, "fname", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ if (NT_STATUS_IS_OK(status)) return True;
+
+ /* try with a new file name */
+ param_len = 6;
+ SSVAL(param, 0, level);
+ SSVAL(param, 2, 0);
+ SSVAL(param, 4, 0);
+ param_len += clistr_push(cli, &param[6], "\\newfile.dat", -1, STR_TERMINATE);
+
+ status = try_trans2_len(cli, "newfile", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ cli_unlink(cli, "\\newfile.dat");
+ cli_rmdir(cli, "\\newfile.dat");
+ if (NT_STATUS_IS_OK(status)) return True;
+
+ /* try dfs style */
+ cli_mkdir(cli, "\\testdir");
+ param_len = 2;
+ SSVAL(param, 0, level);
+ param_len += clistr_push(cli, &param[2], "\\testdir", -1, STR_TERMINATE);
+
+ status = try_trans2_len(cli, "dfs", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ cli_rmdir(cli, "\\testdir");
+ if (NT_STATUS_IS_OK(status)) return True;
+
+ return False;
+}
+
+
+BOOL torture_trans2_scan(int dummy)
+{
+ static struct cli_state *cli;
+ int op, level;
+ const char *fname = "\\scanner.dat";
+ int fnum, dnum;
+
+ printf("starting trans2 scan test\n");
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ fnum = cli_open(cli, fname, O_RDWR | O_CREAT | O_TRUNC,
+ DENY_NONE);
+ dnum = cli_open(cli, "\\", O_RDONLY, DENY_NONE);
+
+ for (op=OP_MIN; op<=OP_MAX; op++) {
+ printf("Scanning op=%d\n", op);
+ for (level = 0; level <= 50; level++) {
+ scan_trans2(cli, op, level, fnum, dnum, fname);
+ }
+
+ for (level = 0x100; level <= 0x130; level++) {
+ scan_trans2(cli, op, level, fnum, dnum, fname);
+ }
+
+ for (level = 1000; level < 1050; level++) {
+ scan_trans2(cli, op, level, fnum, dnum, fname);
+ }
+ }
+
+ torture_close_connection(cli);
+
+ printf("trans2 scan finished\n");
+ return True;
+}
+
+
+
+
+/****************************************************************************
+look for a partial hit
+****************************************************************************/
+static void nttrans_check_hit(const char *format, int op, int level, NTSTATUS status)
+{
+ if (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_INVALID_LEVEL) ||
+ NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_NOT_IMPLEMENTED) ||
+ NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_NOT_SUPPORTED) ||
+ NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) ||
+ NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_INVALID_INFO_CLASS)) {
+ return;
+ }
+#if VERBOSE
+ printf("possible %s hit op=%3d level=%5d status=%s\n",
+ format, op, level, nt_errstr(status));
+#endif
+}
+
+/****************************************************************************
+check for existance of a nttrans call
+****************************************************************************/
+static NTSTATUS try_nttrans(struct cli_state *cli,
+ int op,
+ char *param, char *data,
+ int param_len, int data_len,
+ int *rparam_len, int *rdata_len)
+{
+ char *rparam=NULL, *rdata=NULL;
+
+ if (!cli_send_nt_trans(cli, op,
+ 0,
+ NULL, 0, 0,
+ param, param_len, 2, /* param, length, max */
+ data, data_len, cli->max_xmit /* data, length, max */
+ )) {
+ return cli_nt_error(cli);
+ }
+
+ cli_receive_nt_trans(cli,
+ &rparam, rparam_len,
+ &rdata, rdata_len);
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ return cli_nt_error(cli);
+}
+
+
+static NTSTATUS try_nttrans_len(struct cli_state *cli,
+ const char *format,
+ int op, int level,
+ char *param, char *data,
+ int param_len, int *data_len,
+ int *rparam_len, int *rdata_len)
+{
+ NTSTATUS ret=NT_STATUS_OK;
+
+ ret = try_nttrans(cli, op, param, data, param_len,
+ sizeof(pstring), rparam_len, rdata_len);
+#if VERBOSE
+ printf("op=%d level=%d ret=%s\n", op, level, nt_errstr(ret));
+#endif
+ if (!NT_STATUS_IS_OK(ret)) return ret;
+
+ *data_len = 0;
+ while (*data_len < sizeof(pstring)) {
+ ret = try_nttrans(cli, op, param, data, param_len,
+ *data_len, rparam_len, rdata_len);
+ if (NT_STATUS_IS_OK(ret)) break;
+ *data_len += 2;
+ }
+ if (NT_STATUS_IS_OK(ret)) {
+ printf("found %s level=%d data_len=%d rparam_len=%d rdata_len=%d\n",
+ format, level, *data_len, *rparam_len, *rdata_len);
+ } else {
+ nttrans_check_hit(format, op, level, ret);
+ }
+ return ret;
+}
+
+/****************************************************************************
+check for existance of a nttrans call
+****************************************************************************/
+static BOOL scan_nttrans(struct cli_state *cli, int op, int level,
+ int fnum, int dnum, const char *fname)
+{
+ int data_len = 0;
+ int param_len = 0;
+ int rparam_len, rdata_len;
+ pstring param, data;
+ NTSTATUS status;
+
+ memset(data, 0, sizeof(data));
+ data_len = 4;
+
+ /* try with a info level only */
+ param_len = 2;
+ SSVAL(param, 0, level);
+ status = try_nttrans_len(cli, "void", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ if (NT_STATUS_IS_OK(status)) return True;
+
+ /* try with a file descriptor */
+ param_len = 6;
+ SSVAL(param, 0, fnum);
+ SSVAL(param, 2, level);
+ SSVAL(param, 4, 0);
+ status = try_nttrans_len(cli, "fnum", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ if (NT_STATUS_IS_OK(status)) return True;
+
+
+ /* try with a notify style */
+ param_len = 6;
+ SSVAL(param, 0, dnum);
+ SSVAL(param, 2, dnum);
+ SSVAL(param, 4, level);
+ status = try_nttrans_len(cli, "notify", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ if (NT_STATUS_IS_OK(status)) return True;
+
+ /* try with a file name */
+ param_len = 6;
+ SSVAL(param, 0, level);
+ SSVAL(param, 2, 0);
+ SSVAL(param, 4, 0);
+ param_len += clistr_push(cli, &param[6], fname, -1, STR_TERMINATE);
+
+ status = try_nttrans_len(cli, "fname", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ if (NT_STATUS_IS_OK(status)) return True;
+
+ /* try with a new file name */
+ param_len = 6;
+ SSVAL(param, 0, level);
+ SSVAL(param, 2, 0);
+ SSVAL(param, 4, 0);
+ param_len += clistr_push(cli, &param[6], "\\newfile.dat", -1, STR_TERMINATE);
+
+ status = try_nttrans_len(cli, "newfile", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ cli_unlink(cli, "\\newfile.dat");
+ cli_rmdir(cli, "\\newfile.dat");
+ if (NT_STATUS_IS_OK(status)) return True;
+
+ /* try dfs style */
+ cli_mkdir(cli, "\\testdir");
+ param_len = 2;
+ SSVAL(param, 0, level);
+ param_len += clistr_push(cli, &param[2], "\\testdir", -1, STR_TERMINATE);
+
+ status = try_nttrans_len(cli, "dfs", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ cli_rmdir(cli, "\\testdir");
+ if (NT_STATUS_IS_OK(status)) return True;
+
+ return False;
+}
+
+
+BOOL torture_nttrans_scan(int dummy)
+{
+ static struct cli_state *cli;
+ int op, level;
+ const char *fname = "\\scanner.dat";
+ int fnum, dnum;
+
+ printf("starting nttrans scan test\n");
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ fnum = cli_open(cli, fname, O_RDWR | O_CREAT | O_TRUNC,
+ DENY_NONE);
+ dnum = cli_open(cli, "\\", O_RDONLY, DENY_NONE);
+
+ for (op=OP_MIN; op<=OP_MAX; op++) {
+ printf("Scanning op=%d\n", op);
+ for (level = 0; level <= 50; level++) {
+ scan_nttrans(cli, op, level, fnum, dnum, fname);
+ }
+
+ for (level = 0x100; level <= 0x130; level++) {
+ scan_nttrans(cli, op, level, fnum, dnum, fname);
+ }
+
+ for (level = 1000; level < 1050; level++) {
+ scan_nttrans(cli, op, level, fnum, dnum, fname);
+ }
+ }
+
+ torture_close_connection(cli);
+
+ printf("nttrans scan finished\n");
+ return True;
+}
diff --git a/source/torture/smbiconv.c b/source/torture/smbiconv.c
new file mode 100644
index 00000000000..1dd168b0bb0
--- /dev/null
+++ b/source/torture/smbiconv.c
@@ -0,0 +1,243 @@
+/*
+ Unix SMB/CIFS implementation.
+ Charset module tester
+
+ Copyright (C) Jelmer Vernooij 2003
+ Based on iconv/icon_prog.c from the GNU C Library,
+ Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static int
+process_block (smb_iconv_t cd, const char *addr, size_t len, FILE *output)
+{
+#define OUTBUF_SIZE 32768
+ const char *start = addr;
+ char outbuf[OUTBUF_SIZE];
+ char *outptr;
+ size_t outlen;
+ size_t n;
+
+ while (len > 0)
+ {
+ outptr = outbuf;
+ outlen = OUTBUF_SIZE;
+ n = smb_iconv (cd, &addr, &len, &outptr, &outlen);
+
+ if (outptr != outbuf)
+ {
+ /* We have something to write out. */
+ int errno_save = errno;
+
+ if (fwrite (outbuf, 1, outptr - outbuf, output)
+ < (size_t) (outptr - outbuf)
+ || ferror (output))
+ {
+ /* Error occurred while printing the result. */
+ DEBUG (0, ("conversion stopped due to problem in writing the output"));
+ return -1;
+ }
+
+ errno = errno_save;
+ }
+
+ if (errno != E2BIG)
+ {
+ /* iconv() ran into a problem. */
+ switch (errno)
+ {
+ case EILSEQ:
+ DEBUG(0,("illegal input sequence at position %ld",
+ (long) (addr - start)));
+ break;
+ case EINVAL:
+ DEBUG(0, ("\
+incomplete character or shift sequence at end of buffer"));
+ break;
+ case EBADF:
+ DEBUG(0, ("internal error (illegal descriptor)"));
+ break;
+ default:
+ DEBUG(0, ("unknown iconv() error %d", errno));
+ break;
+ }
+
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static int
+process_fd (iconv_t cd, int fd, FILE *output)
+{
+ /* we have a problem with reading from a descriptor since we must not
+ provide the iconv() function an incomplete character or shift
+ sequence at the end of the buffer. Since we have to deal with
+ arbitrary encodings we must read the whole text in a buffer and
+ process it in one step. */
+ static char *inbuf = NULL;
+ static size_t maxlen = 0;
+ char *inptr = NULL;
+ size_t actlen = 0;
+
+ while (actlen < maxlen)
+ {
+ ssize_t n = read (fd, inptr, maxlen - actlen);
+
+ if (n == 0)
+ /* No more text to read. */
+ break;
+
+ if (n == -1)
+ {
+ /* Error while reading. */
+ DEBUG(0, ("error while reading the input"));
+ return -1;
+ }
+
+ inptr += n;
+ actlen += n;
+ }
+
+ if (actlen == maxlen)
+ while (1)
+ {
+ ssize_t n;
+ char *new_inbuf;
+
+ /* Increase the buffer. */
+ new_inbuf = (char *) realloc (inbuf, maxlen + 32768);
+ if (new_inbuf == NULL)
+ {
+ DEBUG(0, ("unable to allocate buffer for input"));
+ return -1;
+ }
+ inbuf = new_inbuf;
+ maxlen += 32768;
+ inptr = inbuf + actlen;
+
+ do
+ {
+ n = read (fd, inptr, maxlen - actlen);
+
+ if (n == 0)
+ /* No more text to read. */
+ break;
+
+ if (n == -1)
+ {
+ /* Error while reading. */
+ DEBUG(0, ("error while reading the input"));
+ return -1;
+ }
+
+ inptr += n;
+ actlen += n;
+ }
+ while (actlen < maxlen);
+
+ if (n == 0)
+ /* Break again so we leave both loops. */
+ break;
+ }
+
+ /* Now we have all the input in the buffer. Process it in one run. */
+ return process_block (cd, inbuf, actlen, output);
+}
+
+/* Main function */
+
+int main(int argc, char *argv[])
+{
+ const char *file = NULL;
+ char *from = "";
+ char *to = "";
+ char *output = NULL;
+ const char *preload_modules[] = {NULL, NULL};
+ FILE *out = stdout;
+ int fd;
+ smb_iconv_t cd;
+
+ /* make sure the vars that get altered (4th field) are in
+ a fixed location or certain compilers complain */
+ poptContext pc;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "from-code", 'f', POPT_ARG_STRING, &from, 0, "Encoding of original text" },
+ { "to-code", 't', POPT_ARG_STRING, &to, 0, "Encoding for output" },
+ { "output", 'o', POPT_ARG_STRING, &output, 0, "Write output to this file" },
+ { "preload-modules", 'p', POPT_ARG_STRING, &preload_modules[0], 0, "Modules to load" },
+ POPT_COMMON_SAMBA
+ POPT_TABLEEND
+ };
+
+ setlinebuf(stdout);
+
+ pc = poptGetContext("smbiconv", argc, (const char **) argv,
+ long_options, 0);
+
+ poptSetOtherOptionHelp(pc, "[FILE] ...");
+
+ while(poptGetNextOpt(pc) != -1);
+
+ /* the following functions are part of the Samba debugging
+ facilities. See lib/debug.c */
+ setup_logging("smbiconv", True);
+
+ if (preload_modules[0]) smb_load_modules(preload_modules);
+
+ if(output) {
+ out = fopen(output, "w");
+
+ if(!out) {
+ DEBUG(0, ("Can't open output file '%s': %s, exiting...\n", output, strerror(errno)));
+ return 1;
+ }
+ }
+
+ cd = smb_iconv_open(to, from);
+ if((int)cd == -1) {
+ DEBUG(0,("unable to find from or to encoding, exiting...\n"));
+ return 1;
+ }
+
+ while((file = poptGetArg(pc))) {
+ if(strcmp(file, "-") == 0) fd = 0;
+ else {
+ fd = open(file, O_RDONLY);
+
+ if(!fd) {
+ DEBUG(0, ("Can't open input file '%s': %s, ignoring...\n", file, strerror(errno)));
+ continue;
+ }
+ }
+
+ /* Loop thru all arguments */
+ process_fd(cd, fd, out);
+
+ close(fd);
+ }
+ poptFreeContext(pc);
+
+ fclose(out);
+
+ return 0;
+}
diff --git a/source/torture/t_doschar.c b/source/torture/t_doschar.c
new file mode 100644
index 00000000000..41698350d6e
--- /dev/null
+++ b/source/torture/t_doschar.c
@@ -0,0 +1,42 @@
+/*
+ Samba - Unix SMB/CIFS implementation
+ Test harness for check_dos_char
+ Copyright (C) Martin Pool 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+
+/*
+ * Just print out DOS validity or not for every character.
+ *
+ * DOS validity for a Unicode character set means that it can be
+ * represented in DOS codepage, and that the DOS character maps back
+ * to the same Unicode character.
+ *
+ * This depends on which DOS codepage is configured.
+ */
+ int main(void)
+{
+ smb_ucs2_t i;
+
+ for (i = 0; i < 0xffff; i++) {
+ printf("%d %d\n", (int) i, (int) check_dos_char(i));
+ }
+
+ return 0;
+}
diff --git a/source/torture/t_push_ucs2.c b/source/torture/t_push_ucs2.c
new file mode 100644
index 00000000000..8bfc6f7ad9e
--- /dev/null
+++ b/source/torture/t_push_ucs2.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2003 by Martin Pool
+ * Copyright (C) 2003 by Andrew Bartlett
+ *
+ * Test harness for push_ucs2
+ */
+
+#include "includes.h"
+
+static int check_push_ucs2(const char *orig)
+{
+ smb_ucs2_t *dest = NULL;
+ char *orig2 = NULL;
+ int ret;
+
+ push_ucs2_allocate(&dest, orig);
+ pull_ucs2_allocate(&orig2, dest);
+ ret = strcmp(orig, orig2);
+ if (ret) {
+ fprintf(stderr, "orig: %s\n", orig);
+ fprintf(stderr, "orig (UNIX -> UCS2 -> UNIX): %s\n", orig2);
+ }
+
+ SAFE_FREE(dest);
+ SAFE_FREE(orig2);
+
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ int i, ret = 0;
+ int count = 1;
+
+ /* Needed to initialize character set */
+ lp_load("/dev/null", True, False, False);
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s STRING1 [COUNT]\n"
+ "Checks that a string translated UNIX->UCS2->UNIX is unchanged\n"
+ "Should be always 0\n",
+ argv[0]);
+ return 2;
+ }
+ if (argc >= 3)
+ count = atoi(argv[2]);
+
+ for (i = 0; ((i < count) && (!ret)); i++)
+ ret = check_push_ucs2(argv[1]);
+
+ printf("%d\n", ret);
+
+ return 0;
+}
diff --git a/source/torture/t_strcmp.c b/source/torture/t_strcmp.c
new file mode 100644
index 00000000000..bc8640ee550
--- /dev/null
+++ b/source/torture/t_strcmp.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2003 by Martin Pool
+ *
+ * Test harness for StrCaseCmp
+ */
+
+#include "includes.h"
+
+int main(int argc, char *argv[])
+{
+ int i, ret;
+ int iters = 1;
+
+ /* Needed to initialize character set */
+ lp_load("/dev/null", True, False, False);
+
+ if (argc < 3) {
+ fprintf(stderr, "usage: %s STRING1 STRING2 [ITERS]\n"
+ "Compares two strings, prints the results of StrCaseCmp\n",
+ argv[0]);
+ return 2;
+ }
+ if (argc >= 4)
+ iters = atoi(argv[3]);
+
+ for (i = 0; i < iters; i++)
+ ret = StrCaseCmp(argv[1], argv[2]);
+
+ printf("%d\n", ret);
+
+ return 0;
+}
diff --git a/source/torture/t_stringoverflow.c b/source/torture/t_stringoverflow.c
new file mode 100644
index 00000000000..ec14d81189e
--- /dev/null
+++ b/source/torture/t_stringoverflow.c
@@ -0,0 +1,23 @@
+#include "includes.h"
+
+ int main(void)
+{
+ fstring dest;
+ char *ptr = dest;
+
+ printf("running on valgrind? %d\n", RUNNING_ON_VALGRIND);
+
+ /* Try copying a string into an fstring buffer. The string
+ * will actually fit, but this is still wrong because you
+ * can't pstrcpy into an fstring. This should trap in a
+ * developer build. */
+
+#if 0
+ /* As of CVS 20030318, this will be trapped at compile time! */
+ pstrcpy(dest, "hello");
+#endif /* 0 */
+
+ pstrcpy(ptr, "hello!");
+
+ return 0;
+}
diff --git a/source/torture/t_strstr.c b/source/torture/t_strstr.c
new file mode 100644
index 00000000000..25709526fe8
--- /dev/null
+++ b/source/torture/t_strstr.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2003 by Martin Pool
+ *
+ * Test harness for strstr_m
+ */
+
+#include "includes.h"
+
+int main(int argc, char *argv[])
+{
+ int i;
+ int iters = 1;
+
+ char *ret;
+
+ /* Needed to initialize character set */
+ lp_load("/dev/null", True, False, False);
+
+ if (argc < 3) {
+ fprintf(stderr, "usage: %s STRING1 STRING2 [ITERS]\n"
+ "Compares two strings, prints the results of strstr_m\n",
+ argv[0]);
+ return 2;
+ }
+ if (argc >= 4)
+ iters = atoi(argv[3]);
+
+ for (i = 0; i < iters; i++) {
+ ret = strstr_m(argv[1], argv[2]);
+ }
+
+ printf("%s\n", ret);
+
+ return 0;
+}
diff --git a/source/torture/torture.c b/source/torture/torture.c
new file mode 100644
index 00000000000..86bdca62a86
--- /dev/null
+++ b/source/torture/torture.c
@@ -0,0 +1,4994 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB torture tester
+ Copyright (C) Andrew Tridgell 1997-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+static fstring host, workgroup, share, password, username, myname;
+static int max_protocol = PROTOCOL_NT1;
+static const char *sockops="TCP_NODELAY";
+static int nprocs=1;
+static int port_to_use=0;
+int torture_numops=100;
+static int procnum; /* records process count number when forking */
+static struct cli_state *current_cli;
+static fstring randomfname;
+static BOOL use_oplocks;
+static BOOL use_level_II_oplocks;
+static const char *client_txt = "client_oplocks.txt";
+static BOOL use_kerberos;
+
+BOOL torture_showall = False;
+
+static double create_procs(BOOL (*fn)(int), BOOL *result);
+
+
+static struct timeval tp1,tp2;
+
+void start_timer(void)
+{
+ gettimeofday(&tp1,NULL);
+}
+
+double end_timer(void)
+{
+ gettimeofday(&tp2,NULL);
+ return((tp2.tv_sec - tp1.tv_sec) +
+ (tp2.tv_usec - tp1.tv_usec)*1.0e-6);
+}
+
+
+/* return a pointer to a anonymous shared memory segment of size "size"
+ which will persist across fork() but will disappear when all processes
+ exit
+
+ The memory is not zeroed
+
+ This function uses system5 shared memory. It takes advantage of a property
+ that the memory is not destroyed if it is attached when the id is removed
+ */
+void *shm_setup(int size)
+{
+ int shmid;
+ void *ret;
+
+ shmid = shmget(IPC_PRIVATE, size, SHM_R | SHM_W);
+ if (shmid == -1) {
+ printf("can't get shared memory\n");
+ exit(1);
+ }
+ ret = (void *)shmat(shmid, 0, 0);
+ if (!ret || ret == (void *)-1) {
+ printf("can't attach to shared memory\n");
+ return NULL;
+ }
+ /* the following releases the ipc, but note that this process
+ and all its children will still have access to the memory, its
+ just that the shmid is no longer valid for other shm calls. This
+ means we don't leave behind lots of shm segments after we exit
+
+ See Stevens "advanced programming in unix env" for details
+ */
+ shmctl(shmid, IPC_RMID, 0);
+
+ return ret;
+}
+
+
+static BOOL open_nbt_connection(struct cli_state *c)
+{
+ struct nmb_name called, calling;
+ struct in_addr ip;
+
+ ZERO_STRUCTP(c);
+
+ make_nmb_name(&calling, myname, 0x0);
+ make_nmb_name(&called , host, 0x20);
+
+ zero_ip(&ip);
+
+ if (!cli_initialise(c)) {
+ printf("Failed initialize cli_struct to connect with %s\n", host);
+ return False;
+ }
+
+ c->port = port_to_use;
+
+ if (!cli_connect(c, host, &ip)) {
+ printf("Failed to connect with %s\n", host);
+ return False;
+ }
+
+ c->use_kerberos = use_kerberos;
+
+ c->timeout = 120000; /* set a really long timeout (2 minutes) */
+ if (use_oplocks) c->use_oplocks = True;
+ if (use_level_II_oplocks) c->use_level_II_oplocks = True;
+
+ if (!cli_session_request(c, &calling, &called)) {
+ /*
+ * Well, that failed, try *SMBSERVER ...
+ * However, we must reconnect as well ...
+ */
+ if (!cli_connect(c, host, &ip)) {
+ printf("Failed to connect with %s\n", host);
+ return False;
+ }
+
+ make_nmb_name(&called, "*SMBSERVER", 0x20);
+ if (!cli_session_request(c, &calling, &called)) {
+ printf("%s rejected the session\n",host);
+ printf("We tried with a called name of %s & %s\n",
+ host, "*SMBSERVER");
+ cli_shutdown(c);
+ return False;
+ }
+ }
+
+ return True;
+}
+
+BOOL torture_open_connection(struct cli_state **c)
+{
+ BOOL retry;
+ int flags = 0;
+ NTSTATUS status;
+
+ if (use_kerberos)
+ flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
+
+ status = cli_full_connection(c, myname,
+ host, NULL, port_to_use,
+ share, "?????",
+ username, workgroup,
+ password, flags, Undefined, &retry);
+ if (!NT_STATUS_IS_OK(status)) {
+ return False;
+ }
+
+ if (use_oplocks) (*c)->use_oplocks = True;
+ if (use_level_II_oplocks) (*c)->use_level_II_oplocks = True;
+ (*c)->timeout = 120000; /* set a really long timeout (2 minutes) */
+
+ return True;
+}
+
+BOOL torture_cli_session_setup2(struct cli_state *cli, uint16 *new_vuid)
+{
+ uint16 old_vuid = cli->vuid;
+ fstring old_user_name;
+ size_t passlen = strlen(password);
+ BOOL ret;
+
+ fstrcpy(old_user_name, cli->user_name);
+ cli->vuid = 0;
+ ret = cli_session_setup(cli, username, password, passlen, password, passlen, workgroup);
+ *new_vuid = cli->vuid;
+ cli->vuid = old_vuid;
+ fstrcpy(cli->user_name, old_user_name);
+ return ret;
+}
+
+
+BOOL torture_close_connection(struct cli_state *c)
+{
+ BOOL ret = True;
+ if (!cli_tdis(c)) {
+ printf("tdis failed (%s)\n", cli_errstr(c));
+ ret = False;
+ }
+
+ cli_shutdown(c);
+
+ return ret;
+}
+
+
+/* check if the server produced the expected error code */
+static BOOL check_error(int line, struct cli_state *c,
+ uint8 eclass, uint32 ecode, NTSTATUS nterr)
+{
+ if (cli_is_dos_error(c)) {
+ uint8 class;
+ uint32 num;
+
+ /* Check DOS error */
+
+ cli_dos_error(c, &class, &num);
+
+ if (eclass != class || ecode != num) {
+ printf("unexpected error code class=%d code=%d\n",
+ (int)class, (int)num);
+ printf(" expected %d/%d %s (line=%d)\n",
+ (int)eclass, (int)ecode, nt_errstr(nterr), line);
+ return False;
+ }
+
+ } else {
+ NTSTATUS status;
+
+ /* Check NT error */
+
+ status = cli_nt_error(c);
+
+ if (NT_STATUS_V(nterr) != NT_STATUS_V(status)) {
+ printf("unexpected error code %s\n", nt_errstr(status));
+ printf(" expected %s (line=%d)\n", nt_errstr(nterr), line);
+ return False;
+ }
+ }
+
+ return True;
+}
+
+
+static BOOL wait_lock(struct cli_state *c, int fnum, uint32 offset, uint32 len)
+{
+ while (!cli_lock(c, fnum, offset, len, -1, WRITE_LOCK)) {
+ if (!check_error(__LINE__, c, ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED)) return False;
+ }
+ return True;
+}
+
+
+static BOOL rw_torture(struct cli_state *c)
+{
+ const char *lockfname = "\\torture.lck";
+ fstring fname;
+ int fnum;
+ int fnum2;
+ pid_t pid2, pid = getpid();
+ int i, j;
+ char buf[1024];
+ BOOL correct = True;
+
+ fnum2 = cli_open(c, lockfname, O_RDWR | O_CREAT | O_EXCL,
+ DENY_NONE);
+ if (fnum2 == -1)
+ fnum2 = cli_open(c, lockfname, O_RDWR, DENY_NONE);
+ if (fnum2 == -1) {
+ printf("open of %s failed (%s)\n", lockfname, cli_errstr(c));
+ return False;
+ }
+
+
+ for (i=0;i<torture_numops;i++) {
+ unsigned n = (unsigned)sys_random()%10;
+ if (i % 10 == 0) {
+ printf("%d\r", i); fflush(stdout);
+ }
+ slprintf(fname, sizeof(fstring) - 1, "\\torture.%u", n);
+
+ if (!wait_lock(c, fnum2, n*sizeof(int), sizeof(int))) {
+ return False;
+ }
+
+ fnum = cli_open(c, fname, O_RDWR | O_CREAT | O_TRUNC, DENY_ALL);
+ if (fnum == -1) {
+ printf("open failed (%s)\n", cli_errstr(c));
+ correct = False;
+ break;
+ }
+
+ if (cli_write(c, fnum, 0, (char *)&pid, 0, sizeof(pid)) != sizeof(pid)) {
+ printf("write failed (%s)\n", cli_errstr(c));
+ correct = False;
+ }
+
+ for (j=0;j<50;j++) {
+ if (cli_write(c, fnum, 0, (char *)buf,
+ sizeof(pid)+(j*sizeof(buf)),
+ sizeof(buf)) != sizeof(buf)) {
+ printf("write failed (%s)\n", cli_errstr(c));
+ correct = False;
+ }
+ }
+
+ pid2 = 0;
+
+ if (cli_read(c, fnum, (char *)&pid2, 0, sizeof(pid)) != sizeof(pid)) {
+ printf("read failed (%s)\n", cli_errstr(c));
+ correct = False;
+ }
+
+ if (pid2 != pid) {
+ printf("data corruption!\n");
+ correct = False;
+ }
+
+ if (!cli_close(c, fnum)) {
+ printf("close failed (%s)\n", cli_errstr(c));
+ correct = False;
+ }
+
+ if (!cli_unlink(c, fname)) {
+ printf("unlink failed (%s)\n", cli_errstr(c));
+ correct = False;
+ }
+
+ if (!cli_unlock(c, fnum2, n*sizeof(int), sizeof(int))) {
+ printf("unlock failed (%s)\n", cli_errstr(c));
+ correct = False;
+ }
+ }
+
+ cli_close(c, fnum2);
+ cli_unlink(c, lockfname);
+
+ printf("%d\n", i);
+
+ return correct;
+}
+
+static BOOL run_torture(int dummy)
+{
+ struct cli_state *cli;
+ BOOL ret;
+
+ cli = current_cli;
+
+ cli_sockopt(cli, sockops);
+
+ ret = rw_torture(cli);
+
+ if (!torture_close_connection(cli)) {
+ ret = False;
+ }
+
+ return ret;
+}
+
+static BOOL rw_torture3(struct cli_state *c, char *lockfname)
+{
+ int fnum = -1;
+ unsigned int i = 0;
+ char buf[131072];
+ char buf_rd[131072];
+ unsigned count;
+ unsigned countprev = 0;
+ ssize_t sent = 0;
+ BOOL correct = True;
+
+ srandom(1);
+ for (i = 0; i < sizeof(buf); i += sizeof(uint32))
+ {
+ SIVAL(buf, i, sys_random());
+ }
+
+ if (procnum == 0)
+ {
+ fnum = cli_open(c, lockfname, O_RDWR | O_CREAT | O_EXCL,
+ DENY_NONE);
+ if (fnum == -1) {
+ printf("first open read/write of %s failed (%s)\n",
+ lockfname, cli_errstr(c));
+ return False;
+ }
+ }
+ else
+ {
+ for (i = 0; i < 500 && fnum == -1; i++)
+ {
+ fnum = cli_open(c, lockfname, O_RDONLY,
+ DENY_NONE);
+ smb_msleep(10);
+ }
+ if (fnum == -1) {
+ printf("second open read-only of %s failed (%s)\n",
+ lockfname, cli_errstr(c));
+ return False;
+ }
+ }
+
+ i = 0;
+ for (count = 0; count < sizeof(buf); count += sent)
+ {
+ if (count >= countprev) {
+ printf("%d %8d\r", i, count);
+ fflush(stdout);
+ i++;
+ countprev += (sizeof(buf) / 20);
+ }
+
+ if (procnum == 0)
+ {
+ sent = ((unsigned)sys_random()%(20))+ 1;
+ if (sent > sizeof(buf) - count)
+ {
+ sent = sizeof(buf) - count;
+ }
+
+ if (cli_write(c, fnum, 0, buf+count, count, (size_t)sent) != sent) {
+ printf("write failed (%s)\n", cli_errstr(c));
+ correct = False;
+ }
+ }
+ else
+ {
+ sent = cli_read(c, fnum, buf_rd+count, count,
+ sizeof(buf)-count);
+ if (sent < 0)
+ {
+ printf("read failed offset:%d size:%ld (%s)\n",
+ count, (unsigned long)sizeof(buf)-count,
+ cli_errstr(c));
+ correct = False;
+ sent = 0;
+ }
+ if (sent > 0)
+ {
+ if (memcmp(buf_rd+count, buf+count, sent) != 0)
+ {
+ printf("read/write compare failed\n");
+ printf("offset: %d req %ld recvd %ld\n", count, (unsigned long)sizeof(buf)-count, (unsigned long)sent);
+ correct = False;
+ break;
+ }
+ }
+ }
+
+ }
+
+ if (!cli_close(c, fnum)) {
+ printf("close failed (%s)\n", cli_errstr(c));
+ correct = False;
+ }
+
+ return correct;
+}
+
+static BOOL rw_torture2(struct cli_state *c1, struct cli_state *c2)
+{
+ const char *lockfname = "\\torture2.lck";
+ int fnum1;
+ int fnum2;
+ int i;
+ uchar buf[131072];
+ uchar buf_rd[131072];
+ BOOL correct = True;
+ ssize_t bytes_read;
+
+ if (!cli_unlink(c1, lockfname)) {
+ printf("unlink failed (%s) (normal, this file should not exist)\n", cli_errstr(c1));
+ }
+
+ fnum1 = cli_open(c1, lockfname, O_RDWR | O_CREAT | O_EXCL,
+ DENY_NONE);
+ if (fnum1 == -1) {
+ printf("first open read/write of %s failed (%s)\n",
+ lockfname, cli_errstr(c1));
+ return False;
+ }
+ fnum2 = cli_open(c2, lockfname, O_RDONLY,
+ DENY_NONE);
+ if (fnum2 == -1) {
+ printf("second open read-only of %s failed (%s)\n",
+ lockfname, cli_errstr(c2));
+ cli_close(c1, fnum1);
+ return False;
+ }
+
+ for (i=0;i<torture_numops;i++)
+ {
+ size_t buf_size = ((unsigned)sys_random()%(sizeof(buf)-1))+ 1;
+ if (i % 10 == 0) {
+ printf("%d\r", i); fflush(stdout);
+ }
+
+ generate_random_buffer(buf, buf_size, False);
+
+ if (cli_write(c1, fnum1, 0, buf, 0, buf_size) != buf_size) {
+ printf("write failed (%s)\n", cli_errstr(c1));
+ correct = False;
+ break;
+ }
+
+ if ((bytes_read = cli_read(c2, fnum2, buf_rd, 0, buf_size)) != buf_size) {
+ printf("read failed (%s)\n", cli_errstr(c2));
+ printf("read %d, expected %ld\n", bytes_read,
+ (unsigned long)buf_size);
+ correct = False;
+ break;
+ }
+
+ if (memcmp(buf_rd, buf, buf_size) != 0)
+ {
+ printf("read/write compare failed\n");
+ correct = False;
+ break;
+ }
+ }
+
+ if (!cli_close(c2, fnum2)) {
+ printf("close failed (%s)\n", cli_errstr(c2));
+ correct = False;
+ }
+ if (!cli_close(c1, fnum1)) {
+ printf("close failed (%s)\n", cli_errstr(c1));
+ correct = False;
+ }
+
+ if (!cli_unlink(c1, lockfname)) {
+ printf("unlink failed (%s)\n", cli_errstr(c1));
+ correct = False;
+ }
+
+ return correct;
+}
+
+static BOOL run_readwritetest(int dummy)
+{
+ static struct cli_state *cli1, *cli2;
+ BOOL test1, test2;
+
+ if (!torture_open_connection(&cli1) || !torture_open_connection(&cli2)) {
+ return False;
+ }
+ cli_sockopt(cli1, sockops);
+ cli_sockopt(cli2, sockops);
+
+ printf("starting readwritetest\n");
+
+ test1 = rw_torture2(cli1, cli2);
+ printf("Passed readwritetest v1: %s\n", BOOLSTR(test1));
+
+ if (test1) {
+ test2 = rw_torture2(cli1, cli1);
+ printf("Passed readwritetest v2: %s\n", BOOLSTR(test2));
+ }
+
+ if (!torture_close_connection(cli1)) {
+ test1 = False;
+ }
+
+ if (!torture_close_connection(cli2)) {
+ test2 = False;
+ }
+
+ return (test1 && test2);
+}
+
+static BOOL run_readwritemulti(int dummy)
+{
+ struct cli_state *cli;
+ BOOL test;
+
+ cli = current_cli;
+
+ cli_sockopt(cli, sockops);
+
+ printf("run_readwritemulti: fname %s\n", randomfname);
+ test = rw_torture3(cli, randomfname);
+
+ if (!torture_close_connection(cli)) {
+ test = False;
+ }
+
+ return test;
+}
+
+static BOOL run_readwritelarge(int dummy)
+{
+ static struct cli_state *cli1;
+ int fnum1;
+ const char *lockfname = "\\large.dat";
+ size_t fsize;
+ char buf[126*1024];
+ BOOL correct = True;
+
+ if (!torture_open_connection(&cli1)) {
+ return False;
+ }
+ cli_sockopt(cli1, sockops);
+ memset(buf,'\0',sizeof(buf));
+
+ cli1->max_xmit = 128*1024;
+
+ printf("starting readwritelarge\n");
+
+ cli_unlink(cli1, lockfname);
+
+ fnum1 = cli_open(cli1, lockfname, O_RDWR | O_CREAT | O_EXCL, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("open read/write of %s failed (%s)\n", lockfname, cli_errstr(cli1));
+ return False;
+ }
+
+ cli_write(cli1, fnum1, 0, buf, 0, sizeof(buf));
+
+ if (!cli_qfileinfo(cli1, fnum1, NULL, &fsize, NULL, NULL, NULL, NULL, NULL)) {
+ printf("qfileinfo failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ }
+
+ if (fsize == sizeof(buf))
+ printf("readwritelarge test 1 succeeded (size = %lx)\n",
+ (unsigned long)fsize);
+ else {
+ printf("readwritelarge test 1 failed (size = %lx)\n",
+ (unsigned long)fsize);
+ correct = False;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ }
+
+ if (!cli_unlink(cli1, lockfname)) {
+ printf("unlink failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ }
+
+ fnum1 = cli_open(cli1, lockfname, O_RDWR | O_CREAT | O_EXCL, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("open read/write of %s failed (%s)\n", lockfname, cli_errstr(cli1));
+ return False;
+ }
+
+ cli1->max_xmit = 4*1024;
+
+ cli_smbwrite(cli1, fnum1, buf, 0, sizeof(buf));
+
+ if (!cli_qfileinfo(cli1, fnum1, NULL, &fsize, NULL, NULL, NULL, NULL, NULL)) {
+ printf("qfileinfo failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ }
+
+ if (fsize == sizeof(buf))
+ printf("readwritelarge test 2 succeeded (size = %lx)\n",
+ (unsigned long)fsize);
+ else {
+ printf("readwritelarge test 2 failed (size = %lx)\n",
+ (unsigned long)fsize);
+ correct = False;
+ }
+
+#if 0
+ /* ToDo - set allocation. JRA */
+ if(!cli_set_allocation_size(cli1, fnum1, 0)) {
+ printf("set allocation size to zero failed (%s)\n", cli_errstr(&cli1));
+ return False;
+ }
+ if (!cli_qfileinfo(cli1, fnum1, NULL, &fsize, NULL, NULL, NULL, NULL, NULL)) {
+ printf("qfileinfo failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ }
+ if (fsize != 0)
+ printf("readwritelarge test 3 (truncate test) succeeded (size = %x)\n", fsize);
+#endif
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ }
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+ return correct;
+}
+
+int line_count = 0;
+int nbio_id;
+
+#define ival(s) strtol(s, NULL, 0)
+
+/* run a test that simulates an approximate netbench client load */
+static BOOL run_netbench(int client)
+{
+ struct cli_state *cli;
+ int i;
+ fstring fname;
+ pstring line;
+ char cname[20];
+ FILE *f;
+ const char *params[20];
+ BOOL correct = True;
+
+ cli = current_cli;
+
+ nbio_id = client;
+
+ cli_sockopt(cli, sockops);
+
+ nb_setup(cli);
+
+ slprintf(cname,sizeof(fname), "client%d", client);
+
+ f = fopen(client_txt, "r");
+
+ if (!f) {
+ perror(client_txt);
+ return False;
+ }
+
+ while (fgets(line, sizeof(line)-1, f)) {
+ line_count++;
+
+ line[strlen(line)-1] = 0;
+
+ /* printf("[%d] %s\n", line_count, line); */
+
+ all_string_sub(line,"client1", cname, sizeof(line));
+
+ /* parse the command parameters */
+ params[0] = strtok(line," ");
+ i = 0;
+ while (params[i]) params[++i] = strtok(NULL," ");
+
+ params[i] = "";
+
+ if (i < 2) continue;
+
+ if (!strncmp(params[0],"SMB", 3)) {
+ printf("ERROR: You are using a dbench 1 load file\n");
+ exit(1);
+ }
+
+ if (!strcmp(params[0],"NTCreateX")) {
+ nb_createx(params[1], ival(params[2]), ival(params[3]),
+ ival(params[4]));
+ } else if (!strcmp(params[0],"Close")) {
+ nb_close(ival(params[1]));
+ } else if (!strcmp(params[0],"Rename")) {
+ nb_rename(params[1], params[2]);
+ } else if (!strcmp(params[0],"Unlink")) {
+ nb_unlink(params[1]);
+ } else if (!strcmp(params[0],"Deltree")) {
+ nb_deltree(params[1]);
+ } else if (!strcmp(params[0],"Rmdir")) {
+ nb_rmdir(params[1]);
+ } else if (!strcmp(params[0],"QUERY_PATH_INFORMATION")) {
+ nb_qpathinfo(params[1]);
+ } else if (!strcmp(params[0],"QUERY_FILE_INFORMATION")) {
+ nb_qfileinfo(ival(params[1]));
+ } else if (!strcmp(params[0],"QUERY_FS_INFORMATION")) {
+ nb_qfsinfo(ival(params[1]));
+ } else if (!strcmp(params[0],"FIND_FIRST")) {
+ nb_findfirst(params[1]);
+ } else if (!strcmp(params[0],"WriteX")) {
+ nb_writex(ival(params[1]),
+ ival(params[2]), ival(params[3]), ival(params[4]));
+ } else if (!strcmp(params[0],"ReadX")) {
+ nb_readx(ival(params[1]),
+ ival(params[2]), ival(params[3]), ival(params[4]));
+ } else if (!strcmp(params[0],"Flush")) {
+ nb_flush(ival(params[1]));
+ } else {
+ printf("Unknown operation %s\n", params[0]);
+ exit(1);
+ }
+ }
+ fclose(f);
+
+ nb_cleanup();
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ return correct;
+}
+
+
+/* run a test that simulates an approximate netbench client load */
+static BOOL run_nbench(int dummy)
+{
+ double t;
+ BOOL correct = True;
+
+ nbio_shmem(nprocs);
+
+ nbio_id = -1;
+
+ signal(SIGALRM, nb_alarm);
+ alarm(1);
+ t = create_procs(run_netbench, &correct);
+ alarm(0);
+
+ printf("\nThroughput %g MB/sec\n",
+ 1.0e-6 * nbio_total() / t);
+ return correct;
+}
+
+
+/*
+ This test checks for two things:
+
+ 1) correct support for retaining locks over a close (ie. the server
+ must not use posix semantics)
+ 2) support for lock timeouts
+ */
+static BOOL run_locktest1(int dummy)
+{
+ struct cli_state *cli1, *cli2;
+ const char *fname = "\\lockt1.lck";
+ int fnum1, fnum2, fnum3;
+ time_t t1, t2;
+ unsigned lock_timeout;
+
+ if (!torture_open_connection(&cli1) || !torture_open_connection(&cli2)) {
+ return False;
+ }
+ cli_sockopt(cli1, sockops);
+ cli_sockopt(cli2, sockops);
+
+ printf("starting locktest1\n");
+
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+ fnum2 = cli_open(cli1, fname, O_RDWR, DENY_NONE);
+ if (fnum2 == -1) {
+ printf("open2 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+ fnum3 = cli_open(cli2, fname, O_RDWR, DENY_NONE);
+ if (fnum3 == -1) {
+ printf("open3 of %s failed (%s)\n", fname, cli_errstr(cli2));
+ return False;
+ }
+
+ if (!cli_lock(cli1, fnum1, 0, 4, 0, WRITE_LOCK)) {
+ printf("lock1 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+
+ if (cli_lock(cli2, fnum3, 0, 4, 0, WRITE_LOCK)) {
+ printf("lock2 succeeded! This is a locking bug\n");
+ return False;
+ } else {
+ if (!check_error(__LINE__, cli2, ERRDOS, ERRlock,
+ NT_STATUS_LOCK_NOT_GRANTED)) return False;
+ }
+
+
+ lock_timeout = (1 + (random() % 20));
+ printf("Testing lock timeout with timeout=%u\n", lock_timeout);
+ t1 = time(NULL);
+ if (cli_lock(cli2, fnum3, 0, 4, lock_timeout * 500, WRITE_LOCK)) {
+ printf("lock3 succeeded! This is a locking bug\n");
+ return False;
+ } else {
+ if (!check_error(__LINE__, cli2, ERRDOS, ERRlock,
+ NT_STATUS_FILE_LOCK_CONFLICT)) return False;
+ }
+ t2 = time(NULL);
+
+ if (ABS(t2 - t1) < lock_timeout-1) {
+ printf("error: This server appears not to support timed lock requests\n");
+ }
+
+ printf("server slept for %u seconds for a %u second timeout\n",
+ (unsigned int)(t2-t1), lock_timeout);
+
+ if (!cli_close(cli1, fnum2)) {
+ printf("close1 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (cli_lock(cli2, fnum3, 0, 4, 0, WRITE_LOCK)) {
+ printf("lock4 succeeded! This is a locking bug\n");
+ return False;
+ } else {
+ if (!check_error(__LINE__, cli2, ERRDOS, ERRlock,
+ NT_STATUS_FILE_LOCK_CONFLICT)) return False;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close2 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_close(cli2, fnum3)) {
+ printf("close3 failed (%s)\n", cli_errstr(cli2));
+ return False;
+ }
+
+ if (!cli_unlink(cli1, fname)) {
+ printf("unlink failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+
+ if (!torture_close_connection(cli1)) {
+ return False;
+ }
+
+ if (!torture_close_connection(cli2)) {
+ return False;
+ }
+
+ printf("Passed locktest1\n");
+ return True;
+}
+
+/*
+ this checks to see if a secondary tconx can use open files from an
+ earlier tconx
+ */
+static BOOL run_tcon_test(int dummy)
+{
+ static struct cli_state *cli;
+ const char *fname = "\\tcontest.tmp";
+ int fnum1;
+ uint16 cnum1, cnum2, cnum3;
+ uint16 vuid1, vuid2;
+ char buf[4];
+ BOOL ret = True;
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+ cli_sockopt(cli, sockops);
+
+ printf("starting tcontest\n");
+
+ cli_unlink(cli, fname);
+
+ fnum1 = cli_open(cli, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli));
+ return False;
+ }
+
+ cnum1 = cli->cnum;
+ vuid1 = cli->vuid;
+
+ if (cli_write(cli, fnum1, 0, buf, 130, 4) != 4) {
+ printf("initial write failed (%s)", cli_errstr(cli));
+ return False;
+ }
+
+ if (!cli_send_tconX(cli, share, "?????",
+ password, strlen(password)+1)) {
+ printf("%s refused 2nd tree connect (%s)\n", host,
+ cli_errstr(cli));
+ cli_shutdown(cli);
+ return False;
+ }
+
+ cnum2 = cli->cnum;
+ cnum3 = MAX(cnum1, cnum2) + 1; /* any invalid number */
+ vuid2 = cli->vuid + 1;
+
+ /* try a write with the wrong tid */
+ cli->cnum = cnum2;
+
+ if (cli_write(cli, fnum1, 0, buf, 130, 4) == 4) {
+ printf("* server allows write with wrong TID\n");
+ ret = False;
+ } else {
+ printf("server fails write with wrong TID : %s\n", cli_errstr(cli));
+ }
+
+
+ /* try a write with an invalid tid */
+ cli->cnum = cnum3;
+
+ if (cli_write(cli, fnum1, 0, buf, 130, 4) == 4) {
+ printf("* server allows write with invalid TID\n");
+ ret = False;
+ } else {
+ printf("server fails write with invalid TID : %s\n", cli_errstr(cli));
+ }
+
+ /* try a write with an invalid vuid */
+ cli->vuid = vuid2;
+ cli->cnum = cnum1;
+
+ if (cli_write(cli, fnum1, 0, buf, 130, 4) == 4) {
+ printf("* server allows write with invalid VUID\n");
+ ret = False;
+ } else {
+ printf("server fails write with invalid VUID : %s\n", cli_errstr(cli));
+ }
+
+ cli->cnum = cnum1;
+ cli->vuid = vuid1;
+
+ if (!cli_close(cli, fnum1)) {
+ printf("close failed (%s)\n", cli_errstr(cli));
+ return False;
+ }
+
+ cli->cnum = cnum2;
+
+ if (!cli_tdis(cli)) {
+ printf("secondary tdis failed (%s)\n", cli_errstr(cli));
+ return False;
+ }
+
+ cli->cnum = cnum1;
+
+ if (!torture_close_connection(cli)) {
+ return False;
+ }
+
+ return ret;
+}
+
+
+/*
+ checks for old style tcon support
+ */
+static BOOL run_tcon2_test(int dummy)
+{
+ static struct cli_state *cli;
+ uint16 cnum, max_xmit;
+ char *service;
+ NTSTATUS status;
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+ cli_sockopt(cli, sockops);
+
+ printf("starting tcon2 test\n");
+
+ asprintf(&service, "\\\\%s\\%s", host, share);
+
+ status = cli_raw_tcon(cli, service, password, "?????", &max_xmit, &cnum);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("tcon2 failed : %s\n", cli_errstr(cli));
+ } else {
+ printf("tcon OK : max_xmit=%d cnum=%d tid=%d\n",
+ (int)max_xmit, (int)cnum, SVAL(cli->inbuf, smb_tid));
+ }
+
+ if (!torture_close_connection(cli)) {
+ return False;
+ }
+
+ printf("Passed tcon2 test\n");
+ return True;
+}
+
+static BOOL tcon_devtest(struct cli_state *cli,
+ const char *myshare, const char *devtype,
+ const char *return_devtype,
+ NTSTATUS expected_error)
+{
+ BOOL status;
+ BOOL ret;
+
+ status = cli_send_tconX(cli, myshare, devtype,
+ password, strlen(password)+1);
+
+ if (NT_STATUS_IS_OK(expected_error)) {
+ if (status) {
+ if (strcmp(cli->dev, return_devtype) == 0) {
+ ret = True;
+ } else {
+ printf("tconX to share %s with type %s "
+ "succeeded but returned the wrong "
+ "device type (got [%s] but should have got [%s])\n",
+ myshare, devtype, cli->dev, return_devtype);
+ ret = False;
+ }
+ } else {
+ printf("tconX to share %s with type %s "
+ "should have succeeded but failed\n",
+ myshare, devtype);
+ ret = False;
+ }
+ cli_tdis(cli);
+ } else {
+ if (status) {
+ printf("tconx to share %s with type %s "
+ "should have failed but succeeded\n",
+ myshare, devtype);
+ ret = False;
+ } else {
+ if (NT_STATUS_EQUAL(cli_nt_error(cli),
+ expected_error)) {
+ ret = True;
+ } else {
+ printf("Returned unexpected error\n");
+ ret = False;
+ }
+ }
+ }
+ return ret;
+}
+
+/*
+ checks for correct tconX support
+ */
+static BOOL run_tcon_devtype_test(int dummy)
+{
+ static struct cli_state *cli1 = NULL;
+ BOOL retry;
+ int flags = 0;
+ NTSTATUS status;
+ BOOL ret = True;
+
+ status = cli_full_connection(&cli1, myname,
+ host, NULL, port_to_use,
+ NULL, NULL,
+ username, workgroup,
+ password, flags, Undefined, &retry);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("could not open connection\n");
+ return False;
+ }
+
+ if (!tcon_devtest(cli1, "IPC$", "A:", NULL, NT_STATUS_BAD_DEVICE_TYPE))
+ ret = False;
+
+ if (!tcon_devtest(cli1, "IPC$", "?????", "IPC", NT_STATUS_OK))
+ ret = False;
+
+ if (!tcon_devtest(cli1, "IPC$", "LPT:", NULL, NT_STATUS_BAD_DEVICE_TYPE))
+ ret = False;
+
+ if (!tcon_devtest(cli1, "IPC$", "IPC", "IPC", NT_STATUS_OK))
+ ret = False;
+
+ if (!tcon_devtest(cli1, "IPC$", "FOOBA", NULL, NT_STATUS_BAD_DEVICE_TYPE))
+ ret = False;
+
+ if (!tcon_devtest(cli1, share, "A:", "A:", NT_STATUS_OK))
+ ret = False;
+
+ if (!tcon_devtest(cli1, share, "?????", "A:", NT_STATUS_OK))
+ ret = False;
+
+ if (!tcon_devtest(cli1, share, "LPT:", NULL, NT_STATUS_BAD_DEVICE_TYPE))
+ ret = False;
+
+ if (!tcon_devtest(cli1, share, "IPC", NULL, NT_STATUS_BAD_DEVICE_TYPE))
+ ret = False;
+
+ if (!tcon_devtest(cli1, share, "FOOBA", NULL, NT_STATUS_BAD_DEVICE_TYPE))
+ ret = False;
+
+ cli_shutdown(cli1);
+
+ if (ret)
+ printf("Passed tcondevtest\n");
+
+ return ret;
+}
+
+
+/*
+ This test checks that
+
+ 1) the server supports multiple locking contexts on the one SMB
+ connection, distinguished by PID.
+
+ 2) the server correctly fails overlapping locks made by the same PID (this
+ goes against POSIX behaviour, which is why it is tricky to implement)
+
+ 3) the server denies unlock requests by an incorrect client PID
+*/
+static BOOL run_locktest2(int dummy)
+{
+ static struct cli_state *cli;
+ const char *fname = "\\lockt2.lck";
+ int fnum1, fnum2, fnum3;
+ BOOL correct = True;
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ cli_sockopt(cli, sockops);
+
+ printf("starting locktest2\n");
+
+ cli_unlink(cli, fname);
+
+ cli_setpid(cli, 1);
+
+ fnum1 = cli_open(cli, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli));
+ return False;
+ }
+
+ fnum2 = cli_open(cli, fname, O_RDWR, DENY_NONE);
+ if (fnum2 == -1) {
+ printf("open2 of %s failed (%s)\n", fname, cli_errstr(cli));
+ return False;
+ }
+
+ cli_setpid(cli, 2);
+
+ fnum3 = cli_open(cli, fname, O_RDWR, DENY_NONE);
+ if (fnum3 == -1) {
+ printf("open3 of %s failed (%s)\n", fname, cli_errstr(cli));
+ return False;
+ }
+
+ cli_setpid(cli, 1);
+
+ if (!cli_lock(cli, fnum1, 0, 4, 0, WRITE_LOCK)) {
+ printf("lock1 failed (%s)\n", cli_errstr(cli));
+ return False;
+ }
+
+ if (cli_lock(cli, fnum1, 0, 4, 0, WRITE_LOCK)) {
+ printf("WRITE lock1 succeeded! This is a locking bug\n");
+ correct = False;
+ } else {
+ if (!check_error(__LINE__, cli, ERRDOS, ERRlock,
+ NT_STATUS_LOCK_NOT_GRANTED)) return False;
+ }
+
+ if (cli_lock(cli, fnum2, 0, 4, 0, WRITE_LOCK)) {
+ printf("WRITE lock2 succeeded! This is a locking bug\n");
+ correct = False;
+ } else {
+ if (!check_error(__LINE__, cli, ERRDOS, ERRlock,
+ NT_STATUS_LOCK_NOT_GRANTED)) return False;
+ }
+
+ if (cli_lock(cli, fnum2, 0, 4, 0, READ_LOCK)) {
+ printf("READ lock2 succeeded! This is a locking bug\n");
+ correct = False;
+ } else {
+ if (!check_error(__LINE__, cli, ERRDOS, ERRlock,
+ NT_STATUS_FILE_LOCK_CONFLICT)) return False;
+ }
+
+ if (!cli_lock(cli, fnum1, 100, 4, 0, WRITE_LOCK)) {
+ printf("lock at 100 failed (%s)\n", cli_errstr(cli));
+ }
+ cli_setpid(cli, 2);
+ if (cli_unlock(cli, fnum1, 100, 4)) {
+ printf("unlock at 100 succeeded! This is a locking bug\n");
+ correct = False;
+ }
+
+ if (cli_unlock(cli, fnum1, 0, 4)) {
+ printf("unlock1 succeeded! This is a locking bug\n");
+ correct = False;
+ } else {
+ if (!check_error(__LINE__, cli,
+ ERRDOS, ERRlock,
+ NT_STATUS_RANGE_NOT_LOCKED)) return False;
+ }
+
+ if (cli_unlock(cli, fnum1, 0, 8)) {
+ printf("unlock2 succeeded! This is a locking bug\n");
+ correct = False;
+ } else {
+ if (!check_error(__LINE__, cli,
+ ERRDOS, ERRlock,
+ NT_STATUS_RANGE_NOT_LOCKED)) return False;
+ }
+
+ if (cli_lock(cli, fnum3, 0, 4, 0, WRITE_LOCK)) {
+ printf("lock3 succeeded! This is a locking bug\n");
+ correct = False;
+ } else {
+ if (!check_error(__LINE__, cli, ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED)) return False;
+ }
+
+ cli_setpid(cli, 1);
+
+ if (!cli_close(cli, fnum1)) {
+ printf("close1 failed (%s)\n", cli_errstr(cli));
+ return False;
+ }
+
+ if (!cli_close(cli, fnum2)) {
+ printf("close2 failed (%s)\n", cli_errstr(cli));
+ return False;
+ }
+
+ if (!cli_close(cli, fnum3)) {
+ printf("close3 failed (%s)\n", cli_errstr(cli));
+ return False;
+ }
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ printf("locktest2 finished\n");
+
+ return correct;
+}
+
+
+/*
+ This test checks that
+
+ 1) the server supports the full offset range in lock requests
+*/
+static BOOL run_locktest3(int dummy)
+{
+ static struct cli_state *cli1, *cli2;
+ const char *fname = "\\lockt3.lck";
+ int fnum1, fnum2, i;
+ uint32 offset;
+ BOOL correct = True;
+
+#define NEXT_OFFSET offset += (~(uint32)0) / torture_numops
+
+ if (!torture_open_connection(&cli1) || !torture_open_connection(&cli2)) {
+ return False;
+ }
+ cli_sockopt(cli1, sockops);
+ cli_sockopt(cli2, sockops);
+
+ printf("starting locktest3\n");
+
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+ fnum2 = cli_open(cli2, fname, O_RDWR, DENY_NONE);
+ if (fnum2 == -1) {
+ printf("open2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+ return False;
+ }
+
+ for (offset=i=0;i<torture_numops;i++) {
+ NEXT_OFFSET;
+ if (!cli_lock(cli1, fnum1, offset-1, 1, 0, WRITE_LOCK)) {
+ printf("lock1 %d failed (%s)\n",
+ i,
+ cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_lock(cli2, fnum2, offset-2, 1, 0, WRITE_LOCK)) {
+ printf("lock2 %d failed (%s)\n",
+ i,
+ cli_errstr(cli1));
+ return False;
+ }
+ }
+
+ for (offset=i=0;i<torture_numops;i++) {
+ NEXT_OFFSET;
+
+ if (cli_lock(cli1, fnum1, offset-2, 1, 0, WRITE_LOCK)) {
+ printf("error: lock1 %d succeeded!\n", i);
+ return False;
+ }
+
+ if (cli_lock(cli2, fnum2, offset-1, 1, 0, WRITE_LOCK)) {
+ printf("error: lock2 %d succeeded!\n", i);
+ return False;
+ }
+
+ if (cli_lock(cli1, fnum1, offset-1, 1, 0, WRITE_LOCK)) {
+ printf("error: lock3 %d succeeded!\n", i);
+ return False;
+ }
+
+ if (cli_lock(cli2, fnum2, offset-2, 1, 0, WRITE_LOCK)) {
+ printf("error: lock4 %d succeeded!\n", i);
+ return False;
+ }
+ }
+
+ for (offset=i=0;i<torture_numops;i++) {
+ NEXT_OFFSET;
+
+ if (!cli_unlock(cli1, fnum1, offset-1, 1)) {
+ printf("unlock1 %d failed (%s)\n",
+ i,
+ cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_unlock(cli2, fnum2, offset-2, 1)) {
+ printf("unlock2 %d failed (%s)\n",
+ i,
+ cli_errstr(cli1));
+ return False;
+ }
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close1 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_close(cli2, fnum2)) {
+ printf("close2 failed (%s)\n", cli_errstr(cli2));
+ return False;
+ }
+
+ if (!cli_unlink(cli1, fname)) {
+ printf("unlink failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+
+ if (!torture_close_connection(cli2)) {
+ correct = False;
+ }
+
+ printf("finished locktest3\n");
+
+ return correct;
+}
+
+#define EXPECTED(ret, v) if ((ret) != (v)) { \
+ printf("** "); correct = False; \
+ }
+
+/*
+ looks at overlapping locks
+*/
+static BOOL run_locktest4(int dummy)
+{
+ static struct cli_state *cli1, *cli2;
+ const char *fname = "\\lockt4.lck";
+ int fnum1, fnum2, f;
+ BOOL ret;
+ char buf[1000];
+ BOOL correct = True;
+
+ if (!torture_open_connection(&cli1) || !torture_open_connection(&cli2)) {
+ return False;
+ }
+
+ cli_sockopt(cli1, sockops);
+ cli_sockopt(cli2, sockops);
+
+ printf("starting locktest4\n");
+
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ fnum2 = cli_open(cli2, fname, O_RDWR, DENY_NONE);
+
+ memset(buf, 0, sizeof(buf));
+
+ if (cli_write(cli1, fnum1, 0, buf, 0, sizeof(buf)) != sizeof(buf)) {
+ printf("Failed to create file\n");
+ correct = False;
+ goto fail;
+ }
+
+ ret = cli_lock(cli1, fnum1, 0, 4, 0, WRITE_LOCK) &&
+ cli_lock(cli1, fnum1, 2, 4, 0, WRITE_LOCK);
+ EXPECTED(ret, False);
+ printf("the same process %s set overlapping write locks\n", ret?"can":"cannot");
+
+ ret = cli_lock(cli1, fnum1, 10, 4, 0, READ_LOCK) &&
+ cli_lock(cli1, fnum1, 12, 4, 0, READ_LOCK);
+ EXPECTED(ret, True);
+ printf("the same process %s set overlapping read locks\n", ret?"can":"cannot");
+
+ ret = cli_lock(cli1, fnum1, 20, 4, 0, WRITE_LOCK) &&
+ cli_lock(cli2, fnum2, 22, 4, 0, WRITE_LOCK);
+ EXPECTED(ret, False);
+ printf("a different connection %s set overlapping write locks\n", ret?"can":"cannot");
+
+ ret = cli_lock(cli1, fnum1, 30, 4, 0, READ_LOCK) &&
+ cli_lock(cli2, fnum2, 32, 4, 0, READ_LOCK);
+ EXPECTED(ret, True);
+ printf("a different connection %s set overlapping read locks\n", ret?"can":"cannot");
+
+ ret = (cli_setpid(cli1, 1), cli_lock(cli1, fnum1, 40, 4, 0, WRITE_LOCK)) &&
+ (cli_setpid(cli1, 2), cli_lock(cli1, fnum1, 42, 4, 0, WRITE_LOCK));
+ EXPECTED(ret, False);
+ printf("a different pid %s set overlapping write locks\n", ret?"can":"cannot");
+
+ ret = (cli_setpid(cli1, 1), cli_lock(cli1, fnum1, 50, 4, 0, READ_LOCK)) &&
+ (cli_setpid(cli1, 2), cli_lock(cli1, fnum1, 52, 4, 0, READ_LOCK));
+ EXPECTED(ret, True);
+ printf("a different pid %s set overlapping read locks\n", ret?"can":"cannot");
+
+ ret = cli_lock(cli1, fnum1, 60, 4, 0, READ_LOCK) &&
+ cli_lock(cli1, fnum1, 60, 4, 0, READ_LOCK);
+ EXPECTED(ret, True);
+ printf("the same process %s set the same read lock twice\n", ret?"can":"cannot");
+
+ ret = cli_lock(cli1, fnum1, 70, 4, 0, WRITE_LOCK) &&
+ cli_lock(cli1, fnum1, 70, 4, 0, WRITE_LOCK);
+ EXPECTED(ret, False);
+ printf("the same process %s set the same write lock twice\n", ret?"can":"cannot");
+
+ ret = cli_lock(cli1, fnum1, 80, 4, 0, READ_LOCK) &&
+ cli_lock(cli1, fnum1, 80, 4, 0, WRITE_LOCK);
+ EXPECTED(ret, False);
+ printf("the same process %s overlay a read lock with a write lock\n", ret?"can":"cannot");
+
+ ret = cli_lock(cli1, fnum1, 90, 4, 0, WRITE_LOCK) &&
+ cli_lock(cli1, fnum1, 90, 4, 0, READ_LOCK);
+ EXPECTED(ret, True);
+ printf("the same process %s overlay a write lock with a read lock\n", ret?"can":"cannot");
+
+ ret = (cli_setpid(cli1, 1), cli_lock(cli1, fnum1, 100, 4, 0, WRITE_LOCK)) &&
+ (cli_setpid(cli1, 2), cli_lock(cli1, fnum1, 100, 4, 0, READ_LOCK));
+ EXPECTED(ret, False);
+ printf("a different pid %s overlay a write lock with a read lock\n", ret?"can":"cannot");
+
+ ret = cli_lock(cli1, fnum1, 110, 4, 0, READ_LOCK) &&
+ cli_lock(cli1, fnum1, 112, 4, 0, READ_LOCK) &&
+ cli_unlock(cli1, fnum1, 110, 6);
+ EXPECTED(ret, False);
+ printf("the same process %s coalesce read locks\n", ret?"can":"cannot");
+
+
+ ret = cli_lock(cli1, fnum1, 120, 4, 0, WRITE_LOCK) &&
+ (cli_read(cli2, fnum2, buf, 120, 4) == 4);
+ EXPECTED(ret, False);
+ printf("this server %s strict write locking\n", ret?"doesn't do":"does");
+
+ ret = cli_lock(cli1, fnum1, 130, 4, 0, READ_LOCK) &&
+ (cli_write(cli2, fnum2, 0, buf, 130, 4) == 4);
+ EXPECTED(ret, False);
+ printf("this server %s strict read locking\n", ret?"doesn't do":"does");
+
+
+ ret = cli_lock(cli1, fnum1, 140, 4, 0, READ_LOCK) &&
+ cli_lock(cli1, fnum1, 140, 4, 0, READ_LOCK) &&
+ cli_unlock(cli1, fnum1, 140, 4) &&
+ cli_unlock(cli1, fnum1, 140, 4);
+ EXPECTED(ret, True);
+ printf("this server %s do recursive read locking\n", ret?"does":"doesn't");
+
+
+ ret = cli_lock(cli1, fnum1, 150, 4, 0, WRITE_LOCK) &&
+ cli_lock(cli1, fnum1, 150, 4, 0, READ_LOCK) &&
+ cli_unlock(cli1, fnum1, 150, 4) &&
+ (cli_read(cli2, fnum2, buf, 150, 4) == 4) &&
+ !(cli_write(cli2, fnum2, 0, buf, 150, 4) == 4) &&
+ cli_unlock(cli1, fnum1, 150, 4);
+ EXPECTED(ret, True);
+ printf("this server %s do recursive lock overlays\n", ret?"does":"doesn't");
+
+ ret = cli_lock(cli1, fnum1, 160, 4, 0, READ_LOCK) &&
+ cli_unlock(cli1, fnum1, 160, 4) &&
+ (cli_write(cli2, fnum2, 0, buf, 160, 4) == 4) &&
+ (cli_read(cli2, fnum2, buf, 160, 4) == 4);
+ EXPECTED(ret, True);
+ printf("the same process %s remove a read lock using write locking\n", ret?"can":"cannot");
+
+ ret = cli_lock(cli1, fnum1, 170, 4, 0, WRITE_LOCK) &&
+ cli_unlock(cli1, fnum1, 170, 4) &&
+ (cli_write(cli2, fnum2, 0, buf, 170, 4) == 4) &&
+ (cli_read(cli2, fnum2, buf, 170, 4) == 4);
+ EXPECTED(ret, True);
+ printf("the same process %s remove a write lock using read locking\n", ret?"can":"cannot");
+
+ ret = cli_lock(cli1, fnum1, 190, 4, 0, WRITE_LOCK) &&
+ cli_lock(cli1, fnum1, 190, 4, 0, READ_LOCK) &&
+ cli_unlock(cli1, fnum1, 190, 4) &&
+ !(cli_write(cli2, fnum2, 0, buf, 190, 4) == 4) &&
+ (cli_read(cli2, fnum2, buf, 190, 4) == 4);
+ EXPECTED(ret, True);
+ printf("the same process %s remove the first lock first\n", ret?"does":"doesn't");
+
+ cli_close(cli1, fnum1);
+ cli_close(cli2, fnum2);
+ fnum1 = cli_open(cli1, fname, O_RDWR, DENY_NONE);
+ f = cli_open(cli1, fname, O_RDWR, DENY_NONE);
+ ret = cli_lock(cli1, fnum1, 0, 8, 0, READ_LOCK) &&
+ cli_lock(cli1, f, 0, 1, 0, READ_LOCK) &&
+ cli_close(cli1, fnum1) &&
+ ((fnum1 = cli_open(cli1, fname, O_RDWR, DENY_NONE)) != -1) &&
+ cli_lock(cli1, fnum1, 7, 1, 0, WRITE_LOCK);
+ cli_close(cli1, f);
+ cli_close(cli1, fnum1);
+ EXPECTED(ret, True);
+ printf("the server %s have the NT byte range lock bug\n", !ret?"does":"doesn't");
+
+ fail:
+ cli_close(cli1, fnum1);
+ cli_close(cli2, fnum2);
+ cli_unlink(cli1, fname);
+ torture_close_connection(cli1);
+ torture_close_connection(cli2);
+
+ printf("finished locktest4\n");
+ return correct;
+}
+
+/*
+ looks at lock upgrade/downgrade.
+*/
+static BOOL run_locktest5(int dummy)
+{
+ static struct cli_state *cli1, *cli2;
+ const char *fname = "\\lockt5.lck";
+ int fnum1, fnum2, fnum3;
+ BOOL ret;
+ char buf[1000];
+ BOOL correct = True;
+
+ if (!torture_open_connection(&cli1) || !torture_open_connection(&cli2)) {
+ return False;
+ }
+
+ cli_sockopt(cli1, sockops);
+ cli_sockopt(cli2, sockops);
+
+ printf("starting locktest5\n");
+
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ fnum2 = cli_open(cli2, fname, O_RDWR, DENY_NONE);
+ fnum3 = cli_open(cli1, fname, O_RDWR, DENY_NONE);
+
+ memset(buf, 0, sizeof(buf));
+
+ if (cli_write(cli1, fnum1, 0, buf, 0, sizeof(buf)) != sizeof(buf)) {
+ printf("Failed to create file\n");
+ correct = False;
+ goto fail;
+ }
+
+ /* Check for NT bug... */
+ ret = cli_lock(cli1, fnum1, 0, 8, 0, READ_LOCK) &&
+ cli_lock(cli1, fnum3, 0, 1, 0, READ_LOCK);
+ cli_close(cli1, fnum1);
+ fnum1 = cli_open(cli1, fname, O_RDWR, DENY_NONE);
+ ret = cli_lock(cli1, fnum1, 7, 1, 0, WRITE_LOCK);
+ EXPECTED(ret, True);
+ printf("this server %s the NT locking bug\n", ret ? "doesn't have" : "has");
+ cli_close(cli1, fnum1);
+ fnum1 = cli_open(cli1, fname, O_RDWR, DENY_NONE);
+ cli_unlock(cli1, fnum3, 0, 1);
+
+ ret = cli_lock(cli1, fnum1, 0, 4, 0, WRITE_LOCK) &&
+ cli_lock(cli1, fnum1, 1, 1, 0, READ_LOCK);
+ EXPECTED(ret, True);
+ printf("the same process %s overlay a write with a read lock\n", ret?"can":"cannot");
+
+ ret = cli_lock(cli2, fnum2, 0, 4, 0, READ_LOCK);
+ EXPECTED(ret, False);
+
+ printf("a different processs %s get a read lock on the first process lock stack\n", ret?"can":"cannot");
+
+ /* Unlock the process 2 lock. */
+ cli_unlock(cli2, fnum2, 0, 4);
+
+ ret = cli_lock(cli1, fnum3, 0, 4, 0, READ_LOCK);
+ EXPECTED(ret, False);
+
+ printf("the same processs on a different fnum %s get a read lock\n", ret?"can":"cannot");
+
+ /* Unlock the process 1 fnum3 lock. */
+ cli_unlock(cli1, fnum3, 0, 4);
+
+ /* Stack 2 more locks here. */
+ ret = cli_lock(cli1, fnum1, 0, 4, 0, READ_LOCK) &&
+ cli_lock(cli1, fnum1, 0, 4, 0, READ_LOCK);
+
+ EXPECTED(ret, True);
+ printf("the same process %s stack read locks\n", ret?"can":"cannot");
+
+ /* Unlock the first process lock, then check this was the WRITE lock that was
+ removed. */
+
+ ret = cli_unlock(cli1, fnum1, 0, 4) &&
+ cli_lock(cli2, fnum2, 0, 4, 0, READ_LOCK);
+
+ EXPECTED(ret, True);
+ printf("the first unlock removes the %s lock\n", ret?"WRITE":"READ");
+
+ /* Unlock the process 2 lock. */
+ cli_unlock(cli2, fnum2, 0, 4);
+
+ /* We should have 3 stacked locks here. Ensure we need to do 3 unlocks. */
+
+ ret = cli_unlock(cli1, fnum1, 1, 1) &&
+ cli_unlock(cli1, fnum1, 0, 4) &&
+ cli_unlock(cli1, fnum1, 0, 4);
+
+ EXPECTED(ret, True);
+ printf("the same process %s unlock the stack of 4 locks\n", ret?"can":"cannot");
+
+ /* Ensure the next unlock fails. */
+ ret = cli_unlock(cli1, fnum1, 0, 4);
+ EXPECTED(ret, False);
+ printf("the same process %s count the lock stack\n", !ret?"can":"cannot");
+
+ /* Ensure connection 2 can get a write lock. */
+ ret = cli_lock(cli2, fnum2, 0, 4, 0, WRITE_LOCK);
+ EXPECTED(ret, True);
+
+ printf("a different processs %s get a write lock on the unlocked stack\n", ret?"can":"cannot");
+
+
+ fail:
+ cli_close(cli1, fnum1);
+ cli_close(cli2, fnum2);
+ cli_unlink(cli1, fname);
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+ if (!torture_close_connection(cli2)) {
+ correct = False;
+ }
+
+ printf("finished locktest5\n");
+
+ return correct;
+}
+
+/*
+ tries the unusual lockingX locktype bits
+*/
+static BOOL run_locktest6(int dummy)
+{
+ static struct cli_state *cli;
+ const char *fname[1] = { "\\lock6.txt" };
+ int i;
+ int fnum;
+ NTSTATUS status;
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ cli_sockopt(cli, sockops);
+
+ printf("starting locktest6\n");
+
+ for (i=0;i<1;i++) {
+ printf("Testing %s\n", fname[i]);
+
+ cli_unlink(cli, fname[i]);
+
+ fnum = cli_open(cli, fname[i], O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ status = cli_locktype(cli, fnum, 0, 8, 0, LOCKING_ANDX_CHANGE_LOCKTYPE);
+ cli_close(cli, fnum);
+ printf("CHANGE_LOCKTYPE gave %s\n", nt_errstr(status));
+
+ fnum = cli_open(cli, fname[i], O_RDWR, DENY_NONE);
+ status = cli_locktype(cli, fnum, 0, 8, 0, LOCKING_ANDX_CANCEL_LOCK);
+ cli_close(cli, fnum);
+ printf("CANCEL_LOCK gave %s\n", nt_errstr(status));
+
+ cli_unlink(cli, fname[i]);
+ }
+
+ torture_close_connection(cli);
+
+ printf("finished locktest6\n");
+ return True;
+}
+
+static BOOL run_locktest7(int dummy)
+{
+ struct cli_state *cli1;
+ const char *fname = "\\lockt7.lck";
+ int fnum1;
+ char buf[200];
+ BOOL correct = False;
+
+ if (!torture_open_connection(&cli1)) {
+ return False;
+ }
+
+ cli_sockopt(cli1, sockops);
+
+ printf("starting locktest7\n");
+
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+
+ memset(buf, 0, sizeof(buf));
+
+ if (cli_write(cli1, fnum1, 0, buf, 0, sizeof(buf)) != sizeof(buf)) {
+ printf("Failed to create file\n");
+ goto fail;
+ }
+
+ cli_setpid(cli1, 1);
+
+ if (!cli_lock(cli1, fnum1, 130, 4, 0, READ_LOCK)) {
+ printf("Unable to apply read lock on range 130:4, error was %s\n", cli_errstr(cli1));
+ goto fail;
+ } else {
+ printf("pid1 successfully locked range 130:4 for READ\n");
+ }
+
+ if (cli_read(cli1, fnum1, buf, 130, 4) != 4) {
+ printf("pid1 unable to read the range 130:4, error was %s\n", cli_errstr(cli1));
+ goto fail;
+ } else {
+ printf("pid1 successfully read the range 130:4\n");
+ }
+
+ if (cli_write(cli1, fnum1, 0, buf, 130, 4) != 4) {
+ printf("pid1 unable to write to the range 130:4, error was %s\n", cli_errstr(cli1));
+ if (NT_STATUS_V(cli_nt_error(cli1)) != NT_STATUS_V(NT_STATUS_FILE_LOCK_CONFLICT)) {
+ printf("Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)\n");
+ goto fail;
+ }
+ } else {
+ printf("pid1 successfully wrote to the range 130:4 (should be denied)\n");
+ goto fail;
+ }
+
+ cli_setpid(cli1, 2);
+
+ if (cli_read(cli1, fnum1, buf, 130, 4) != 4) {
+ printf("pid2 unable to read the range 130:4, error was %s\n", cli_errstr(cli1));
+ } else {
+ printf("pid2 successfully read the range 130:4\n");
+ }
+
+ if (cli_write(cli1, fnum1, 0, buf, 130, 4) != 4) {
+ printf("pid2 unable to write to the range 130:4, error was %s\n", cli_errstr(cli1));
+ if (NT_STATUS_V(cli_nt_error(cli1)) != NT_STATUS_V(NT_STATUS_FILE_LOCK_CONFLICT)) {
+ printf("Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)\n");
+ goto fail;
+ }
+ } else {
+ printf("pid2 successfully wrote to the range 130:4 (should be denied)\n");
+ goto fail;
+ }
+
+ cli_setpid(cli1, 1);
+ cli_unlock(cli1, fnum1, 130, 4);
+
+ if (!cli_lock(cli1, fnum1, 130, 4, 0, WRITE_LOCK)) {
+ printf("Unable to apply write lock on range 130:4, error was %s\n", cli_errstr(cli1));
+ goto fail;
+ } else {
+ printf("pid1 successfully locked range 130:4 for WRITE\n");
+ }
+
+ if (cli_read(cli1, fnum1, buf, 130, 4) != 4) {
+ printf("pid1 unable to read the range 130:4, error was %s\n", cli_errstr(cli1));
+ goto fail;
+ } else {
+ printf("pid1 successfully read the range 130:4\n");
+ }
+
+ if (cli_write(cli1, fnum1, 0, buf, 130, 4) != 4) {
+ printf("pid1 unable to write to the range 130:4, error was %s\n", cli_errstr(cli1));
+ goto fail;
+ } else {
+ printf("pid1 successfully wrote to the range 130:4\n");
+ }
+
+ cli_setpid(cli1, 2);
+
+ if (cli_read(cli1, fnum1, buf, 130, 4) != 4) {
+ printf("pid2 unable to read the range 130:4, error was %s\n", cli_errstr(cli1));
+ if (NT_STATUS_V(cli_nt_error(cli1)) != NT_STATUS_V(NT_STATUS_FILE_LOCK_CONFLICT)) {
+ printf("Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)\n");
+ goto fail;
+ }
+ } else {
+ printf("pid2 successfully read the range 130:4 (should be denied)\n");
+ goto fail;
+ }
+
+ if (cli_write(cli1, fnum1, 0, buf, 130, 4) != 4) {
+ printf("pid2 unable to write to the range 130:4, error was %s\n", cli_errstr(cli1));
+ if (NT_STATUS_V(cli_nt_error(cli1)) != NT_STATUS_V(NT_STATUS_FILE_LOCK_CONFLICT)) {
+ printf("Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)\n");
+ goto fail;
+ }
+ } else {
+ printf("pid2 successfully wrote to the range 130:4 (should be denied)\n");
+ goto fail;
+ }
+
+ cli_unlock(cli1, fnum1, 130, 0);
+ correct = True;
+
+fail:
+ cli_close(cli1, fnum1);
+ cli_unlink(cli1, fname);
+ torture_close_connection(cli1);
+
+ printf("finished locktest7\n");
+ return correct;
+}
+
+/*
+test whether fnums and tids open on one VC are available on another (a major
+security hole)
+*/
+static BOOL run_fdpasstest(int dummy)
+{
+ struct cli_state *cli1, *cli2;
+ const char *fname = "\\fdpass.tst";
+ int fnum1;
+ pstring buf;
+
+ if (!torture_open_connection(&cli1) || !torture_open_connection(&cli2)) {
+ return False;
+ }
+ cli_sockopt(cli1, sockops);
+ cli_sockopt(cli2, sockops);
+
+ printf("starting fdpasstest\n");
+
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ if (cli_write(cli1, fnum1, 0, "hello world\n", 0, 13) != 13) {
+ printf("write failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ cli2->vuid = cli1->vuid;
+ cli2->cnum = cli1->cnum;
+ cli2->pid = cli1->pid;
+
+ if (cli_read(cli2, fnum1, buf, 0, 13) == 13) {
+ printf("read succeeded! nasty security hole [%s]\n",
+ buf);
+ return False;
+ }
+
+ cli_close(cli1, fnum1);
+ cli_unlink(cli1, fname);
+
+ torture_close_connection(cli1);
+ torture_close_connection(cli2);
+
+ printf("finished fdpasstest\n");
+ return True;
+}
+
+static BOOL run_fdsesstest(int dummy)
+{
+ struct cli_state *cli;
+ uint16 new_vuid;
+ uint16 saved_vuid;
+ uint16 new_cnum;
+ uint16 saved_cnum;
+ const char *fname = "\\fdsess.tst";
+ const char *fname1 = "\\fdsess1.tst";
+ int fnum1;
+ int fnum2;
+ pstring buf;
+ BOOL ret = True;
+
+ if (!torture_open_connection(&cli))
+ return False;
+ cli_sockopt(cli, sockops);
+
+ if (!torture_cli_session_setup2(cli, &new_vuid))
+ return False;
+
+ saved_cnum = cli->cnum;
+ if (!cli_send_tconX(cli, share, "?????", "", 1))
+ return False;
+ new_cnum = cli->cnum;
+ cli->cnum = saved_cnum;
+
+ printf("starting fdsesstest\n");
+
+ cli_unlink(cli, fname);
+ cli_unlink(cli, fname1);
+
+ fnum1 = cli_open(cli, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli));
+ return False;
+ }
+
+ if (cli_write(cli, fnum1, 0, "hello world\n", 0, 13) != 13) {
+ printf("write failed (%s)\n", cli_errstr(cli));
+ return False;
+ }
+
+ saved_vuid = cli->vuid;
+ cli->vuid = new_vuid;
+
+ if (cli_read(cli, fnum1, buf, 0, 13) == 13) {
+ printf("read succeeded with different vuid! nasty security hole [%s]\n",
+ buf);
+ ret = False;
+ }
+ /* Try to open a file with different vuid, samba cnum. */
+ fnum2 = cli_open(cli, fname1, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum2 != -1) {
+ printf("create with different vuid, same cnum succeeded.\n");
+ cli_close(cli, fnum2);
+ cli_unlink(cli, fname1);
+ } else {
+ printf("create with different vuid, same cnum failed.\n");
+ printf("This will cause problems with service clients.\n");
+ ret = False;
+ }
+
+ cli->vuid = saved_vuid;
+
+ /* Try with same vuid, different cnum. */
+ cli->cnum = new_cnum;
+
+ if (cli_read(cli, fnum1, buf, 0, 13) == 13) {
+ printf("read succeeded with different cnum![%s]\n",
+ buf);
+ ret = False;
+ }
+
+ cli->cnum = saved_cnum;
+ cli_close(cli, fnum1);
+ cli_unlink(cli, fname);
+
+ torture_close_connection(cli);
+
+ printf("finished fdsesstest\n");
+ return ret;
+}
+
+/*
+ This test checks that
+
+ 1) the server does not allow an unlink on a file that is open
+*/
+static BOOL run_unlinktest(int dummy)
+{
+ struct cli_state *cli;
+ const char *fname = "\\unlink.tst";
+ int fnum;
+ BOOL correct = True;
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ cli_sockopt(cli, sockops);
+
+ printf("starting unlink test\n");
+
+ cli_unlink(cli, fname);
+
+ cli_setpid(cli, 1);
+
+ fnum = cli_open(cli, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli));
+ return False;
+ }
+
+ if (cli_unlink(cli, fname)) {
+ printf("error: server allowed unlink on an open file\n");
+ correct = False;
+ } else {
+ correct = check_error(__LINE__, cli, ERRDOS, ERRbadshare,
+ NT_STATUS_SHARING_VIOLATION);
+ }
+
+ cli_close(cli, fnum);
+ cli_unlink(cli, fname);
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ printf("unlink test finished\n");
+
+ return correct;
+}
+
+
+/*
+test how many open files this server supports on the one socket
+*/
+static BOOL run_maxfidtest(int dummy)
+{
+ struct cli_state *cli;
+ const char *template = "\\maxfid.%d.%d";
+ fstring fname;
+ int fnums[0x11000], i;
+ int retries=4;
+ BOOL correct = True;
+
+ cli = current_cli;
+
+ if (retries <= 0) {
+ printf("failed to connect\n");
+ return False;
+ }
+
+ cli_sockopt(cli, sockops);
+
+ for (i=0; i<0x11000; i++) {
+ slprintf(fname,sizeof(fname)-1,template, i,(int)getpid());
+ if ((fnums[i] = cli_open(cli, fname,
+ O_RDWR|O_CREAT|O_TRUNC, DENY_NONE)) ==
+ -1) {
+ printf("open of %s failed (%s)\n",
+ fname, cli_errstr(cli));
+ printf("maximum fnum is %d\n", i);
+ break;
+ }
+ printf("%6d\r", i);
+ }
+ printf("%6d\n", i);
+ i--;
+
+ printf("cleaning up\n");
+ for (;i>=0;i--) {
+ slprintf(fname,sizeof(fname)-1,template, i,(int)getpid());
+ cli_close(cli, fnums[i]);
+ if (!cli_unlink(cli, fname)) {
+ printf("unlink of %s failed (%s)\n",
+ fname, cli_errstr(cli));
+ correct = False;
+ }
+ printf("%6d\r", i);
+ }
+ printf("%6d\n", 0);
+
+ printf("maxfid test finished\n");
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+ return correct;
+}
+
+/* generate a random buffer */
+static void rand_buf(char *buf, int len)
+{
+ while (len--) {
+ *buf = (char)sys_random();
+ buf++;
+ }
+}
+
+/* send smb negprot commands, not reading the response */
+static BOOL run_negprot_nowait(int dummy)
+{
+ int i;
+ static struct cli_state cli;
+ BOOL correct = True;
+
+ printf("starting negprot nowait test\n");
+
+ if (!open_nbt_connection(&cli)) {
+ return False;
+ }
+
+ for (i=0;i<50000;i++) {
+ cli_negprot_send(&cli);
+ }
+
+ if (!torture_close_connection(&cli)) {
+ correct = False;
+ }
+
+ printf("finished negprot nowait test\n");
+
+ return correct;
+}
+
+
+/* send random IPC commands */
+static BOOL run_randomipc(int dummy)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ int rdrcnt,rprcnt;
+ pstring param;
+ int api, param_len, i;
+ struct cli_state *cli;
+ BOOL correct = True;
+ int count = 50000;
+
+ printf("starting random ipc test\n");
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ for (i=0;i<count;i++) {
+ api = sys_random() % 500;
+ param_len = (sys_random() % 64);
+
+ rand_buf(param, param_len);
+
+ SSVAL(param,0,api);
+
+ cli_api(cli,
+ param, param_len, 8,
+ NULL, 0, BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt);
+ if (i % 100 == 0) {
+ printf("%d/%d\r", i,count);
+ }
+ }
+ printf("%d/%d\n", i, count);
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ printf("finished random ipc test\n");
+
+ return correct;
+}
+
+
+
+static void browse_callback(const char *sname, uint32 stype,
+ const char *comment, void *state)
+{
+ printf("\t%20.20s %08x %s\n", sname, stype, comment);
+}
+
+
+
+/*
+ This test checks the browse list code
+
+*/
+static BOOL run_browsetest(int dummy)
+{
+ static struct cli_state *cli;
+ BOOL correct = True;
+
+ printf("starting browse test\n");
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ printf("domain list:\n");
+ cli_NetServerEnum(cli, cli->server_domain,
+ SV_TYPE_DOMAIN_ENUM,
+ browse_callback, NULL);
+
+ printf("machine list:\n");
+ cli_NetServerEnum(cli, cli->server_domain,
+ SV_TYPE_ALL,
+ browse_callback, NULL);
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ printf("browse test finished\n");
+
+ return correct;
+
+}
+
+
+/*
+ This checks how the getatr calls works
+*/
+static BOOL run_attrtest(int dummy)
+{
+ struct cli_state *cli;
+ int fnum;
+ time_t t, t2;
+ const char *fname = "\\attrib123456789.tst";
+ BOOL correct = True;
+
+ printf("starting attrib test\n");
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ cli_unlink(cli, fname);
+ fnum = cli_open(cli, fname,
+ O_RDWR | O_CREAT | O_TRUNC, DENY_NONE);
+ cli_close(cli, fnum);
+ if (!cli_getatr(cli, fname, NULL, NULL, &t)) {
+ printf("getatr failed (%s)\n", cli_errstr(cli));
+ correct = False;
+ }
+
+ if (abs(t - time(NULL)) > 60*60*24*10) {
+ printf("ERROR: SMBgetatr bug. time is %s",
+ ctime(&t));
+ t = time(NULL);
+ correct = True;
+ }
+
+ t2 = t-60*60*24; /* 1 day ago */
+
+ if (!cli_setatr(cli, fname, 0, t2)) {
+ printf("setatr failed (%s)\n", cli_errstr(cli));
+ correct = True;
+ }
+
+ if (!cli_getatr(cli, fname, NULL, NULL, &t)) {
+ printf("getatr failed (%s)\n", cli_errstr(cli));
+ correct = True;
+ }
+
+ if (t != t2) {
+ printf("ERROR: getatr/setatr bug. times are\n%s",
+ ctime(&t));
+ printf("%s", ctime(&t2));
+ correct = True;
+ }
+
+ cli_unlink(cli, fname);
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ printf("attrib test finished\n");
+
+ return correct;
+}
+
+
+/*
+ This checks a couple of trans2 calls
+*/
+static BOOL run_trans2test(int dummy)
+{
+ struct cli_state *cli;
+ int fnum;
+ size_t size;
+ time_t c_time, a_time, m_time, w_time, m_time2;
+ const char *fname = "\\trans2.tst";
+ const char *dname = "\\trans2";
+ const char *fname2 = "\\trans2\\trans2.tst";
+ pstring pname;
+ BOOL correct = True;
+
+ printf("starting trans2 test\n");
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ cli_unlink(cli, fname);
+ fnum = cli_open(cli, fname,
+ O_RDWR | O_CREAT | O_TRUNC, DENY_NONE);
+ if (!cli_qfileinfo(cli, fnum, NULL, &size, &c_time, &a_time, &m_time,
+ NULL, NULL)) {
+ printf("ERROR: qfileinfo failed (%s)\n", cli_errstr(cli));
+ correct = False;
+ }
+
+ if (!cli_qfilename(cli, fnum, pname)) {
+ printf("ERROR: qfilename failed (%s)\n", cli_errstr(cli));
+ correct = False;
+ }
+
+ if (strcmp(pname, fname)) {
+ printf("qfilename gave different name? [%s] [%s]\n",
+ fname, pname);
+ correct = False;
+ }
+
+ cli_close(cli, fnum);
+
+ sleep(2);
+
+ cli_unlink(cli, fname);
+ fnum = cli_open(cli, fname,
+ O_RDWR | O_CREAT | O_TRUNC, DENY_NONE);
+ if (fnum == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli));
+ return False;
+ }
+ cli_close(cli, fnum);
+
+ if (!cli_qpathinfo(cli, fname, &c_time, &a_time, &m_time, &size, NULL)) {
+ printf("ERROR: qpathinfo failed (%s)\n", cli_errstr(cli));
+ correct = False;
+ } else {
+ if (c_time != m_time) {
+ printf("create time=%s", ctime(&c_time));
+ printf("modify time=%s", ctime(&m_time));
+ printf("This system appears to have sticky create times\n");
+ }
+ if (a_time % (60*60) == 0) {
+ printf("access time=%s", ctime(&a_time));
+ printf("This system appears to set a midnight access time\n");
+ correct = False;
+ }
+
+ if (abs(m_time - time(NULL)) > 60*60*24*7) {
+ printf("ERROR: totally incorrect times - maybe word reversed? mtime=%s", ctime(&m_time));
+ correct = False;
+ }
+ }
+
+
+ cli_unlink(cli, fname);
+ fnum = cli_open(cli, fname,
+ O_RDWR | O_CREAT | O_TRUNC, DENY_NONE);
+ cli_close(cli, fnum);
+ if (!cli_qpathinfo2(cli, fname, &c_time, &a_time, &m_time,
+ &w_time, &size, NULL, NULL)) {
+ printf("ERROR: qpathinfo2 failed (%s)\n", cli_errstr(cli));
+ correct = False;
+ } else {
+ if (w_time < 60*60*24*2) {
+ printf("write time=%s", ctime(&w_time));
+ printf("This system appears to set a initial 0 write time\n");
+ correct = False;
+ }
+ }
+
+ cli_unlink(cli, fname);
+
+
+ /* check if the server updates the directory modification time
+ when creating a new file */
+ if (!cli_mkdir(cli, dname)) {
+ printf("ERROR: mkdir failed (%s)\n", cli_errstr(cli));
+ correct = False;
+ }
+ sleep(3);
+ if (!cli_qpathinfo2(cli, "\\trans2\\", &c_time, &a_time, &m_time,
+ &w_time, &size, NULL, NULL)) {
+ printf("ERROR: qpathinfo2 failed (%s)\n", cli_errstr(cli));
+ correct = False;
+ }
+
+ fnum = cli_open(cli, fname2,
+ O_RDWR | O_CREAT | O_TRUNC, DENY_NONE);
+ cli_write(cli, fnum, 0, (char *)&fnum, 0, sizeof(fnum));
+ cli_close(cli, fnum);
+ if (!cli_qpathinfo2(cli, "\\trans2\\", &c_time, &a_time, &m_time2,
+ &w_time, &size, NULL, NULL)) {
+ printf("ERROR: qpathinfo2 failed (%s)\n", cli_errstr(cli));
+ correct = False;
+ } else {
+ if (m_time2 == m_time) {
+ printf("This system does not update directory modification times\n");
+ correct = False;
+ }
+ }
+ cli_unlink(cli, fname2);
+ cli_rmdir(cli, dname);
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ printf("trans2 test finished\n");
+
+ return correct;
+}
+
+/*
+ This checks new W2K calls.
+*/
+
+static BOOL new_trans(struct cli_state *pcli, int fnum, int level)
+{
+ char *buf = NULL;
+ uint32 len;
+ BOOL correct = True;
+
+ if (!cli_qfileinfo_test(pcli, fnum, level, &buf, &len)) {
+ printf("ERROR: qfileinfo (%d) failed (%s)\n", level, cli_errstr(pcli));
+ correct = False;
+ } else {
+ printf("qfileinfo: level %d, len = %u\n", level, len);
+ dump_data(0, buf, len);
+ printf("\n");
+ }
+ SAFE_FREE(buf);
+ return correct;
+}
+
+static BOOL run_w2ktest(int dummy)
+{
+ struct cli_state *cli;
+ int fnum;
+ const char *fname = "\\w2ktest\\w2k.tst";
+ int level;
+ BOOL correct = True;
+
+ printf("starting w2k test\n");
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ fnum = cli_open(cli, fname,
+ O_RDWR | O_CREAT , DENY_NONE);
+
+ for (level = 1004; level < 1040; level++) {
+ new_trans(cli, fnum, level);
+ }
+
+ cli_close(cli, fnum);
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ printf("w2k test finished\n");
+
+ return correct;
+}
+
+
+/*
+ this is a harness for some oplock tests
+ */
+static BOOL run_oplock1(int dummy)
+{
+ struct cli_state *cli1;
+ const char *fname = "\\lockt1.lck";
+ int fnum1;
+ BOOL correct = True;
+
+ printf("starting oplock test 1\n");
+
+ if (!torture_open_connection(&cli1)) {
+ return False;
+ }
+
+ cli_unlink(cli1, fname);
+
+ cli_sockopt(cli1, sockops);
+
+ cli1->use_oplocks = True;
+
+ fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ cli1->use_oplocks = False;
+
+ cli_unlink(cli1, fname);
+ cli_unlink(cli1, fname);
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close2 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_unlink(cli1, fname)) {
+ printf("unlink failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+
+ printf("finished oplock test 1\n");
+
+ return correct;
+}
+
+static BOOL run_oplock2(int dummy)
+{
+ struct cli_state *cli1, *cli2;
+ const char *fname = "\\lockt2.lck";
+ int fnum1, fnum2;
+ int saved_use_oplocks = use_oplocks;
+ char buf[4];
+ BOOL correct = True;
+ volatile BOOL *shared_correct;
+
+ shared_correct = (volatile BOOL *)shm_setup(sizeof(BOOL));
+ *shared_correct = True;
+
+ use_level_II_oplocks = True;
+ use_oplocks = True;
+
+ printf("starting oplock test 2\n");
+
+ if (!torture_open_connection(&cli1)) {
+ use_level_II_oplocks = False;
+ use_oplocks = saved_use_oplocks;
+ return False;
+ }
+
+ cli1->use_oplocks = True;
+ cli1->use_level_II_oplocks = True;
+
+ if (!torture_open_connection(&cli2)) {
+ use_level_II_oplocks = False;
+ use_oplocks = saved_use_oplocks;
+ return False;
+ }
+
+ cli2->use_oplocks = True;
+ cli2->use_level_II_oplocks = True;
+
+ cli_unlink(cli1, fname);
+
+ cli_sockopt(cli1, sockops);
+ cli_sockopt(cli2, sockops);
+
+ fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ /* Don't need the globals any more. */
+ use_level_II_oplocks = False;
+ use_oplocks = saved_use_oplocks;
+
+ if (fork() == 0) {
+ /* Child code */
+ fnum2 = cli_open(cli2, fname, O_RDWR, DENY_NONE);
+ if (fnum2 == -1) {
+ printf("second open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ *shared_correct = False;
+ exit(0);
+ }
+
+ sleep(2);
+
+ if (!cli_close(cli2, fnum2)) {
+ printf("close2 failed (%s)\n", cli_errstr(cli1));
+ *shared_correct = False;
+ }
+
+ exit(0);
+ }
+
+ sleep(2);
+
+ /* Ensure cli1 processes the break. */
+
+ if (cli_read(cli1, fnum1, buf, 0, 4) != 4) {
+ printf("read on fnum1 failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ }
+
+ /* Should now be at level II. */
+ /* Test if sending a write locks causes a break to none. */
+
+ if (!cli_lock(cli1, fnum1, 0, 4, 0, READ_LOCK)) {
+ printf("lock failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ }
+
+ cli_unlock(cli1, fnum1, 0, 4);
+
+ sleep(2);
+
+ if (!cli_lock(cli1, fnum1, 0, 4, 0, WRITE_LOCK)) {
+ printf("lock failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ }
+
+ cli_unlock(cli1, fnum1, 0, 4);
+
+ sleep(2);
+
+ cli_read(cli1, fnum1, buf, 0, 4);
+
+#if 0
+ if (cli_write(cli1, fnum1, 0, buf, 0, 4) != 4) {
+ printf("write on fnum1 failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ }
+#endif
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close1 failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ }
+
+ sleep(4);
+
+ if (!cli_unlink(cli1, fname)) {
+ printf("unlink failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ }
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+
+ if (!*shared_correct) {
+ correct = False;
+ }
+
+ printf("finished oplock test 2\n");
+
+ return correct;
+}
+
+/* handler for oplock 3 tests */
+static BOOL oplock3_handler(struct cli_state *cli, int fnum, unsigned char level)
+{
+ printf("got oplock break fnum=%d level=%d\n",
+ fnum, level);
+ return cli_oplock_ack(cli, fnum, level);
+}
+
+static BOOL run_oplock3(int dummy)
+{
+ struct cli_state *cli;
+ const char *fname = "\\oplockt3.dat";
+ int fnum;
+ char buf[4] = "abcd";
+ BOOL correct = True;
+ volatile BOOL *shared_correct;
+
+ shared_correct = (volatile BOOL *)shm_setup(sizeof(BOOL));
+ *shared_correct = True;
+
+ printf("starting oplock test 3\n");
+
+ if (fork() == 0) {
+ /* Child code */
+ use_oplocks = True;
+ use_level_II_oplocks = True;
+ if (!torture_open_connection(&cli)) {
+ *shared_correct = False;
+ exit(0);
+ }
+ sleep(2);
+ /* try to trigger a oplock break in parent */
+ fnum = cli_open(cli, fname, O_RDWR, DENY_NONE);
+ cli_write(cli, fnum, 0, buf, 0, 4);
+ exit(0);
+ }
+
+ /* parent code */
+ use_oplocks = True;
+ use_level_II_oplocks = True;
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+ cli_oplock_handler(cli, oplock3_handler);
+ fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE);
+ cli_write(cli, fnum, 0, buf, 0, 4);
+ cli_close(cli, fnum);
+ fnum = cli_open(cli, fname, O_RDWR, DENY_NONE);
+ cli->timeout = 20000;
+ cli_receive_smb(cli);
+ printf("finished oplock test 3\n");
+
+ return (correct && *shared_correct);
+
+/* What are we looking for here? What's sucess and what's FAILURE? */
+}
+
+
+
+/*
+ Test delete on close semantics.
+ */
+static BOOL run_deletetest(int dummy)
+{
+ struct cli_state *cli1 = NULL;
+ struct cli_state *cli2 = NULL;
+ const char *fname = "\\delete.file";
+ int fnum1 = -1;
+ int fnum2 = -1;
+ BOOL correct = True;
+
+ printf("starting delete test\n");
+
+ if (!torture_open_connection(&cli1)) {
+ return False;
+ }
+
+ cli_sockopt(cli1, sockops);
+
+ /* Test 1 - this should delete the file on close. */
+
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, GENERIC_ALL_ACCESS|DELETE_ACCESS, FILE_ATTRIBUTE_NORMAL,
+ 0, FILE_OVERWRITE_IF,
+ FILE_DELETE_ON_CLOSE, 0);
+
+ if (fnum1 == -1) {
+ printf("[1] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+#if 0 /* JRATEST */
+ {
+ uint32 *accinfo = NULL;
+ uint32 len;
+ cli_qfileinfo_test(cli1, fnum1, SMB_FILE_ACCESS_INFORMATION, (char **)&accinfo, &len);
+ if (accinfo)
+ printf("access mode = 0x%lx\n", *accinfo);
+ SAFE_FREE(accinfo);
+ }
+#endif
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("[1] close failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ fnum1 = cli_open(cli1, fname, O_RDWR, DENY_NONE);
+ if (fnum1 != -1) {
+ printf("[1] open of %s succeeded (should fail)\n", fname);
+ correct = False;
+ goto fail;
+ }
+
+ printf("first delete on close test succeeded.\n");
+
+ /* Test 2 - this should delete the file on close. */
+
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, GENERIC_ALL_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_NONE,
+ FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("[2] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_nt_delete_on_close(cli1, fnum1, True)) {
+ printf("[2] setting delete_on_close failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("[2] close failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ fnum1 = cli_open(cli1, fname, O_RDONLY, DENY_NONE);
+ if (fnum1 != -1) {
+ printf("[2] open of %s succeeded should have been deleted on close !\n", fname);
+ if (!cli_close(cli1, fnum1)) {
+ printf("[2] close failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+ cli_unlink(cli1, fname);
+ } else
+ printf("second delete on close test succeeded.\n");
+
+ /* Test 3 - ... */
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, GENERIC_ALL_ACCESS, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("[3] open - 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ /* This should fail with a sharing violation - open for delete is only compatible
+ with SHARE_DELETE. */
+
+ fnum2 = cli_nt_create_full(cli1, fname, 0, GENERIC_READ_ACCESS, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, 0, 0);
+
+ if (fnum2 != -1) {
+ printf("[3] open - 2 of %s succeeded - should have failed.\n", fname);
+ correct = False;
+ goto fail;
+ }
+
+ /* This should succeed. */
+
+ fnum2 = cli_nt_create_full(cli1, fname, 0, GENERIC_READ_ACCESS, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, FILE_OPEN, 0, 0);
+
+ if (fnum2 == -1) {
+ printf("[3] open - 2 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_nt_delete_on_close(cli1, fnum1, True)) {
+ printf("[3] setting delete_on_close failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("[3] close 1 failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_close(cli1, fnum2)) {
+ printf("[3] close 2 failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ /* This should fail - file should no longer be there. */
+
+ fnum1 = cli_open(cli1, fname, O_RDONLY, DENY_NONE);
+ if (fnum1 != -1) {
+ printf("[3] open of %s succeeded should have been deleted on close !\n", fname);
+ if (!cli_close(cli1, fnum1)) {
+ printf("[3] close failed (%s)\n", cli_errstr(cli1));
+ }
+ cli_unlink(cli1, fname);
+ correct = False;
+ goto fail;
+ } else
+ printf("third delete on close test succeeded.\n");
+
+ /* Test 4 ... */
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, FILE_READ_DATA|FILE_WRITE_DATA|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("[4] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ /* This should succeed. */
+ fnum2 = cli_nt_create_full(cli1, fname, 0, GENERIC_READ_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, FILE_OPEN, 0, 0);
+ if (fnum2 == -1) {
+ printf("[4] open - 2 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_close(cli1, fnum2)) {
+ printf("[4] close - 1 failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_nt_delete_on_close(cli1, fnum1, True)) {
+ printf("[4] setting delete_on_close failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ /* This should fail - no more opens once delete on close set. */
+ fnum2 = cli_nt_create_full(cli1, fname, 0, GENERIC_READ_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN, 0, 0);
+ if (fnum2 != -1) {
+ printf("[4] open - 3 of %s succeeded ! Should have failed.\n", fname );
+ correct = False;
+ goto fail;
+ } else
+ printf("fourth delete on close test succeeded.\n");
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("[4] close - 2 failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ /* Test 5 ... */
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("[5] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ /* This should fail - only allowed on NT opens with DELETE access. */
+
+ if (cli_nt_delete_on_close(cli1, fnum1, True)) {
+ printf("[5] setting delete_on_close on OpenX file succeeded - should fail !\n");
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("[5] close - 2 failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ printf("fifth delete on close test succeeded.\n");
+
+ /* Test 6 ... */
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, FILE_READ_DATA|FILE_WRITE_DATA,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("[6] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ /* This should fail - only allowed on NT opens with DELETE access. */
+
+ if (cli_nt_delete_on_close(cli1, fnum1, True)) {
+ printf("[6] setting delete_on_close on file with no delete access succeeded - should fail !\n");
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("[6] close - 2 failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ printf("sixth delete on close test succeeded.\n");
+
+ /* Test 7 ... */
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, FILE_READ_DATA|FILE_WRITE_DATA|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, 0, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("[7] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_nt_delete_on_close(cli1, fnum1, True)) {
+ printf("[7] setting delete_on_close on file failed !\n");
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_nt_delete_on_close(cli1, fnum1, False)) {
+ printf("[7] unsetting delete_on_close on file failed !\n");
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("[7] close - 2 failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ /* This next open should succeed - we reset the flag. */
+
+ fnum1 = cli_open(cli1, fname, O_RDONLY, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("[5] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("[7] close - 2 failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ printf("seventh delete on close test succeeded.\n");
+
+ /* Test 7 ... */
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname);
+
+ if (!torture_open_connection(&cli2)) {
+ printf("[8] failed to open second connection.\n");
+ correct = False;
+ goto fail;
+ }
+
+ cli_sockopt(cli1, sockops);
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, FILE_READ_DATA|FILE_WRITE_DATA|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("[8] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ fnum2 = cli_nt_create_full(cli2, fname, 0, FILE_READ_DATA|FILE_WRITE_DATA|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN, 0, 0);
+
+ if (fnum2 == -1) {
+ printf("[8] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_nt_delete_on_close(cli1, fnum1, True)) {
+ printf("[8] setting delete_on_close on file failed !\n");
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("[8] close - 1 failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_close(cli2, fnum2)) {
+ printf("[8] close - 2 failed (%s)\n", cli_errstr(cli2));
+ correct = False;
+ goto fail;
+ }
+
+ /* This should fail.. */
+ fnum1 = cli_open(cli1, fname, O_RDONLY, DENY_NONE);
+ if (fnum1 != -1) {
+ printf("[8] open of %s succeeded should have been deleted on close !\n", fname);
+ goto fail;
+ correct = False;
+ } else
+ printf("eighth delete on close test succeeded.\n");
+
+ /* This should fail - we need to set DELETE_ACCESS. */
+ fnum1 = cli_nt_create_full(cli1, fname, 0,FILE_READ_DATA|FILE_WRITE_DATA,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_NONE, FILE_OVERWRITE_IF, FILE_DELETE_ON_CLOSE, 0);
+
+ if (fnum1 != -1) {
+ printf("[9] open of %s succeeded should have failed!\n", fname);
+ correct = False;
+ goto fail;
+ }
+
+ printf("ninth delete on close test succeeded.\n");
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, FILE_READ_DATA|FILE_WRITE_DATA|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_NONE, FILE_OVERWRITE_IF, FILE_DELETE_ON_CLOSE, 0);
+ if (fnum1 == -1) {
+ printf("[10] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ /* This should delete the file. */
+ if (!cli_close(cli1, fnum1)) {
+ printf("[10] close failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ /* This should fail.. */
+ fnum1 = cli_open(cli1, fname, O_RDONLY, DENY_NONE);
+ if (fnum1 != -1) {
+ printf("[10] open of %s succeeded should have been deleted on close !\n", fname);
+ goto fail;
+ correct = False;
+ } else
+ printf("tenth delete on close test succeeded.\n");
+ printf("finished delete test\n");
+
+ fail:
+ /* FIXME: This will crash if we aborted before cli2 got
+ * intialized, because these functions don't handle
+ * uninitialized connections. */
+
+ if (fnum1 != -1) cli_close(cli1, fnum1);
+ if (fnum2 != -1) cli_close(cli1, fnum2);
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname);
+
+ if (cli1 && !torture_close_connection(cli1)) {
+ correct = False;
+ }
+ if (cli2 && !torture_close_connection(cli2)) {
+ correct = False;
+ }
+ return correct;
+}
+
+
+/*
+ print out server properties
+ */
+static BOOL run_properties(int dummy)
+{
+ static struct cli_state *cli;
+ BOOL correct = True;
+
+ printf("starting properties test\n");
+
+ ZERO_STRUCT(cli);
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ cli_sockopt(cli, sockops);
+
+ d_printf("Capabilities 0x%08x\n", cli->capabilities);
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ return correct;
+}
+
+
+
+/* FIRST_DESIRED_ACCESS 0xf019f */
+#define FIRST_DESIRED_ACCESS FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|\
+ FILE_READ_EA| /* 0xf */ \
+ FILE_WRITE_EA|FILE_READ_ATTRIBUTES| /* 0x90 */ \
+ FILE_WRITE_ATTRIBUTES| /* 0x100 */ \
+ DELETE_ACCESS|READ_CONTROL_ACCESS|\
+ WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS /* 0xf0000 */
+/* SECOND_DESIRED_ACCESS 0xe0080 */
+#define SECOND_DESIRED_ACCESS FILE_READ_ATTRIBUTES| /* 0x80 */ \
+ READ_CONTROL_ACCESS|WRITE_DAC_ACCESS|\
+ WRITE_OWNER_ACCESS /* 0xe0000 */
+
+#if 0
+#define THIRD_DESIRED_ACCESS FILE_READ_ATTRIBUTES| /* 0x80 */ \
+ READ_CONTROL_ACCESS|WRITE_DAC_ACCESS|\
+ FILE_READ_DATA|\
+ WRITE_OWNER_ACCESS /* */
+#endif
+
+/*
+ Test ntcreate calls made by xcopy
+ */
+static BOOL run_xcopy(int dummy)
+{
+ static struct cli_state *cli1;
+ const char *fname = "\\test.txt";
+ BOOL correct = True;
+ int fnum1, fnum2;
+
+ printf("starting xcopy test\n");
+
+ if (!torture_open_connection(&cli1)) {
+ return False;
+ }
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0,
+ FIRST_DESIRED_ACCESS, FILE_ATTRIBUTE_ARCHIVE,
+ FILE_SHARE_NONE, FILE_OVERWRITE_IF,
+ 0x4044, 0);
+
+ if (fnum1 == -1) {
+ printf("First open failed - %s\n", cli_errstr(cli1));
+ return False;
+ }
+
+ fnum2 = cli_nt_create_full(cli1, fname, 0,
+ SECOND_DESIRED_ACCESS, 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, FILE_OPEN,
+ 0x200000, 0);
+ if (fnum2 == -1) {
+ printf("second open failed - %s\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+
+ return correct;
+}
+
+/*
+ Test rename on files open with share delete and no share delete.
+ */
+static BOOL run_rename(int dummy)
+{
+ static struct cli_state *cli1;
+ const char *fname = "\\test.txt";
+ const char *fname1 = "\\test1.txt";
+ BOOL correct = True;
+ int fnum1, fnum2;
+
+ printf("starting rename test\n");
+
+ if (!torture_open_connection(&cli1)) {
+ return False;
+ }
+
+ cli_unlink(cli1, fname);
+ cli_unlink(cli1, fname1);
+ fnum1 = cli_nt_create_full(cli1, fname, 0, GENERIC_READ_ACCESS, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("First open failed - %s\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_rename(cli1, fname, fname1)) {
+ printf("First rename failed (SHARE_READ) (this is correct) - %s\n", cli_errstr(cli1));
+ } else {
+ printf("First rename succeeded (SHARE_READ) - this should have failed !\n");
+ correct = False;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close - 1 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ cli_unlink(cli1, fname);
+ cli_unlink(cli1, fname1);
+ fnum1 = cli_nt_create_full(cli1, fname, 0, GENERIC_READ_ACCESS, FILE_ATTRIBUTE_NORMAL,
+#if 0
+ FILE_SHARE_DELETE|FILE_SHARE_NONE, FILE_OVERWRITE_IF, 0, 0);
+#else
+ FILE_SHARE_DELETE|FILE_SHARE_READ, FILE_OVERWRITE_IF, 0, 0);
+#endif
+
+ if (fnum1 == -1) {
+ printf("Second open failed - %s\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_rename(cli1, fname, fname1)) {
+ printf("Second rename failed (SHARE_DELETE | SHARE_READ) - this should have succeeded - %s\n", cli_errstr(cli1));
+ correct = False;
+ } else {
+ printf("Second rename succeeded (SHARE_DELETE | SHARE_READ)\n");
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close - 2 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ cli_unlink(cli1, fname);
+ cli_unlink(cli1, fname1);
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, READ_CONTROL_ACCESS, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("Third open failed - %s\n", cli_errstr(cli1));
+ return False;
+ }
+
+
+#if 0
+ {
+ int fnum2;
+
+ fnum2 = cli_nt_create_full(cli1, fname, 0, DELETE_ACCESS, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum2 == -1) {
+ printf("Fourth open failed - %s\n", cli_errstr(cli1));
+ return False;
+ }
+ if (!cli_nt_delete_on_close(cli1, fnum2, True)) {
+ printf("[8] setting delete_on_close on file failed !\n");
+ return False;
+ }
+
+ if (!cli_close(cli1, fnum2)) {
+ printf("close - 4 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+ }
+#endif
+
+ if (!cli_rename(cli1, fname, fname1)) {
+ printf("Third rename failed (SHARE_NONE) - this should have succeeded - %s\n", cli_errstr(cli1));
+ correct = False;
+ } else {
+ printf("Third rename succeeded (SHARE_NONE)\n");
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close - 3 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ cli_unlink(cli1, fname);
+ cli_unlink(cli1, fname1);
+
+ /*----*/
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, GENERIC_READ_ACCESS, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("Fourth open failed - %s\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_rename(cli1, fname, fname1)) {
+ printf("Fourth rename failed (SHARE_READ | SHARE_WRITE) (this is correct) - %s\n", cli_errstr(cli1));
+ } else {
+ printf("Fourth rename succeeded (SHARE_READ | SHARE_WRITE) - this should have failed !\n");
+ correct = False;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close - 4 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ cli_unlink(cli1, fname);
+ cli_unlink(cli1, fname1);
+
+ /*--*/
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, GENERIC_READ_ACCESS, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("Fifth open failed - %s\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_rename(cli1, fname, fname1)) {
+ printf("Fifth rename failed (SHARE_READ | SHARE_WRITE | SHARE_DELETE) - this should have failed ! \n");
+ correct = False;
+ } else {
+ printf("Fifth rename succeeded (SHARE_READ | SHARE_WRITE | SHARE_DELETE) (this is correct) - %s\n", cli_errstr(cli1));
+ }
+
+ /*
+ * Now check if the first name still exists ...
+ */
+
+ /*fnum2 = cli_nt_create_full(cli1, fname, 0, GENERIC_READ_ACCESS, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum2 == -1) {
+ printf("Opening original file after rename of open file fails: %s\n",
+ cli_errstr(cli1));
+ }
+ else {
+ printf("Opening original file after rename of open file works ...\n");
+ (void)cli_close(cli1, fnum2);
+ } */
+
+ /*--*/
+
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close - 5 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ cli_unlink(cli1, fname);
+ cli_unlink(cli1, fname1);
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+
+ return correct;
+}
+
+static BOOL run_pipe_number(int dummy)
+{
+ struct cli_state *cli1;
+ const char *pipe_name = "\\SPOOLSS";
+ int fnum;
+ int num_pipes = 0;
+
+ printf("starting pipenumber test\n");
+ if (!torture_open_connection(&cli1)) {
+ return False;
+ }
+
+ cli_sockopt(cli1, sockops);
+ while(1) {
+ fnum = cli_nt_create_full(cli1, pipe_name, 0, FILE_READ_DATA, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN_IF, 0, 0);
+
+ if (fnum == -1) {
+ printf("Open of pipe %s failed with error (%s)\n", pipe_name, cli_errstr(cli1));
+ break;
+ }
+ num_pipes++;
+ }
+
+ printf("pipe_number test - we can open %d %s pipes.\n", num_pipes, pipe_name );
+ torture_close_connection(cli1);
+ return True;
+}
+
+/*
+ Test open mode returns on read-only files.
+ */
+static BOOL run_opentest(int dummy)
+{
+ static struct cli_state *cli1;
+ static struct cli_state *cli2;
+ const char *fname = "\\readonly.file";
+ int fnum1, fnum2;
+ char buf[20];
+ size_t fsize;
+ BOOL correct = True;
+ char *tmp_path;
+
+ printf("starting open test\n");
+
+ if (!torture_open_connection(&cli1)) {
+ return False;
+ }
+
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname);
+
+ cli_sockopt(cli1, sockops);
+
+ fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close2 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_setatr(cli1, fname, aRONLY, 0)) {
+ printf("cli_setatr failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ fnum1 = cli_open(cli1, fname, O_RDONLY, DENY_WRITE);
+ if (fnum1 == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ /* This will fail - but the error should be ERRnoaccess, not ERRbadshare. */
+ fnum2 = cli_open(cli1, fname, O_RDWR, DENY_ALL);
+
+ if (check_error(__LINE__, cli1, ERRDOS, ERRnoaccess,
+ NT_STATUS_ACCESS_DENIED)) {
+ printf("correct error code ERRDOS/ERRnoaccess returned\n");
+ }
+
+ printf("finished open test 1\n");
+
+ cli_close(cli1, fnum1);
+
+ /* Now try not readonly and ensure ERRbadshare is returned. */
+
+ cli_setatr(cli1, fname, 0, 0);
+
+ fnum1 = cli_open(cli1, fname, O_RDONLY, DENY_WRITE);
+ if (fnum1 == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ /* This will fail - but the error should be ERRshare. */
+ fnum2 = cli_open(cli1, fname, O_RDWR, DENY_ALL);
+
+ if (check_error(__LINE__, cli1, ERRDOS, ERRbadshare,
+ NT_STATUS_SHARING_VIOLATION)) {
+ printf("correct error code ERRDOS/ERRbadshare returned\n");
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close2 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ cli_unlink(cli1, fname);
+
+ printf("finished open test 2\n");
+
+ /* Test truncate open disposition on file opened for read. */
+
+ fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("(3) open (1) of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ /* write 20 bytes. */
+
+ memset(buf, '\0', 20);
+
+ if (cli_write(cli1, fnum1, 0, buf, 0, 20) != 20) {
+ printf("write failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("(3) close1 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ /* Ensure size == 20. */
+ if (!cli_getatr(cli1, fname, NULL, &fsize, NULL)) {
+ printf("(3) getatr failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (fsize != 20) {
+ printf("(3) file size != 20\n");
+ return False;
+ }
+
+ /* Now test if we can truncate a file opened for readonly. */
+
+ fnum1 = cli_open(cli1, fname, O_RDONLY|O_TRUNC, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("(3) open (2) of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close2 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ /* Ensure size == 0. */
+ if (!cli_getatr(cli1, fname, NULL, &fsize, NULL)) {
+ printf("(3) getatr failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (fsize != 0) {
+ printf("(3) file size != 0\n");
+ return False;
+ }
+ printf("finished open test 3\n");
+
+ cli_unlink(cli1, fname);
+
+
+ printf("testing ctemp\n");
+ fnum1 = cli_ctemp(cli1, "\\", &tmp_path);
+ if (fnum1 == -1) {
+ printf("ctemp failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+ printf("ctemp gave path %s\n", tmp_path);
+ if (!cli_close(cli1, fnum1)) {
+ printf("close of temp failed (%s)\n", cli_errstr(cli1));
+ }
+ if (!cli_unlink(cli1, tmp_path)) {
+ printf("unlink of temp failed (%s)\n", cli_errstr(cli1));
+ }
+
+ /* Test the non-io opens... */
+
+ if (!torture_open_connection(&cli2)) {
+ return False;
+ }
+
+ cli_setatr(cli2, fname, 0, 0);
+ cli_unlink(cli2, fname);
+
+ cli_sockopt(cli2, sockops);
+
+ printf("TEST #1 testing 2 non-io opens (no delete)\n");
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("test 1 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ fnum2 = cli_nt_create_full(cli2, fname, 0, FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE, FILE_OPEN_IF, 0, 0);
+
+ if (fnum2 == -1) {
+ printf("test 1 open 2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+ return False;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("test 1 close 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+ if (!cli_close(cli2, fnum2)) {
+ printf("test 1 close 2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+ return False;
+ }
+
+ printf("non-io open test #1 passed.\n");
+
+ cli_unlink(cli1, fname);
+
+ printf("TEST #2 testing 2 non-io opens (first with delete)\n");
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, DELETE_ACCESS|FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("test 2 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ fnum2 = cli_nt_create_full(cli2, fname, 0, FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE, FILE_OPEN_IF, 0, 0);
+
+ if (fnum2 == -1) {
+ printf("test 2 open 2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+ return False;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("test 1 close 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+ if (!cli_close(cli2, fnum2)) {
+ printf("test 1 close 2 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ printf("non-io open test #2 passed.\n");
+
+ cli_unlink(cli1, fname);
+
+ printf("TEST #3 testing 2 non-io opens (second with delete)\n");
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("test 3 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ fnum2 = cli_nt_create_full(cli2, fname, 0, DELETE_ACCESS|FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE, FILE_OPEN_IF, 0, 0);
+
+ if (fnum2 == -1) {
+ printf("test 3 open 2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+ return False;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("test 3 close 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+ if (!cli_close(cli2, fnum2)) {
+ printf("test 3 close 2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+ return False;
+ }
+
+ printf("non-io open test #3 passed.\n");
+
+ cli_unlink(cli1, fname);
+
+ printf("TEST #4 testing 2 non-io opens (both with delete)\n");
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, DELETE_ACCESS|FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("test 4 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ fnum2 = cli_nt_create_full(cli2, fname, 0, DELETE_ACCESS|FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE, FILE_OPEN_IF, 0, 0);
+
+ if (fnum2 != -1) {
+ printf("test 4 open 2 of %s SUCCEEDED - should have failed (%s)\n", fname, cli_errstr(cli2));
+ return False;
+ }
+
+ printf("test 3 open 2 of %s gave %s (correct error should be %s)\n", fname, cli_errstr(cli2), "sharing violation");
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("test 4 close 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ printf("non-io open test #4 passed.\n");
+
+ cli_unlink(cli1, fname);
+
+ printf("TEST #5 testing 2 non-io opens (both with delete - both with file share delete)\n");
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, DELETE_ACCESS|FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_DELETE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("test 5 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ fnum2 = cli_nt_create_full(cli2, fname, 0, DELETE_ACCESS|FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_DELETE, FILE_OPEN_IF, 0, 0);
+
+ if (fnum2 == -1) {
+ printf("test 5 open 2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+ return False;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("test 5 close 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_close(cli2, fnum2)) {
+ printf("test 5 close 2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+ return False;
+ }
+
+ printf("non-io open test #5 passed.\n");
+
+ printf("TEST #6 testing 1 non-io open, one io open\n");
+
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, FILE_READ_DATA, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("test 6 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ fnum2 = cli_nt_create_full(cli2, fname, 0, FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ, FILE_OPEN_IF, 0, 0);
+
+ if (fnum2 == -1) {
+ printf("test 6 open 2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+ return False;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("test 6 close 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_close(cli2, fnum2)) {
+ printf("test 6 close 2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+ return False;
+ }
+
+ printf("non-io open test #6 passed.\n");
+
+ printf("TEST #7 testing 1 non-io open, one io open with delete\n");
+
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, FILE_READ_DATA, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("test 7 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ fnum2 = cli_nt_create_full(cli2, fname, 0, DELETE_ACCESS|FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_DELETE, FILE_OPEN_IF, 0, 0);
+
+ if (fnum2 != -1) {
+ printf("test 7 open 2 of %s SUCCEEDED - should have failed (%s)\n", fname, cli_errstr(cli2));
+ return False;
+ }
+
+ printf("test 7 open 2 of %s gave %s (correct error should be %s)\n", fname, cli_errstr(cli2), "sharing violation");
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("test 7 close 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ printf("non-io open test #7 passed.\n");
+
+ cli_unlink(cli1, fname);
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+ if (!torture_close_connection(cli2)) {
+ correct = False;
+ }
+
+ return correct;
+}
+
+static uint32 open_attrs_table[] = {
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_ATTRIBUTE_ARCHIVE,
+ FILE_ATTRIBUTE_READONLY,
+ FILE_ATTRIBUTE_HIDDEN,
+ FILE_ATTRIBUTE_SYSTEM,
+
+ FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY,
+ FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN,
+ FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM,
+ FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN,
+ FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM,
+ FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM,
+
+ FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN,
+ FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM,
+ FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM,
+ FILE_ATTRIBUTE_HIDDEN,FILE_ATTRIBUTE_SYSTEM,
+};
+
+struct trunc_open_results {
+ unsigned int num;
+ uint32 init_attr;
+ uint32 trunc_attr;
+ uint32 result_attr;
+};
+
+static struct trunc_open_results attr_results[] = {
+ { 0, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_ARCHIVE },
+ { 1, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_ARCHIVE },
+ { 2, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY },
+ { 16, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_ARCHIVE },
+ { 17, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_ARCHIVE },
+ { 18, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY },
+ { 51, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
+ { 54, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
+ { 56, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN },
+ { 68, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
+ { 71, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
+ { 73, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM },
+ { 99, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_HIDDEN,FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
+ { 102, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
+ { 104, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN },
+ { 116, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
+ { 119, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
+ { 121, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM },
+ { 170, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN },
+ { 173, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM },
+ { 227, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
+ { 230, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
+ { 232, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN },
+ { 244, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
+ { 247, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
+ { 249, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM }
+};
+
+static BOOL run_openattrtest(int dummy)
+{
+ static struct cli_state *cli1;
+ const char *fname = "\\openattr.file";
+ int fnum1;
+ BOOL correct = True;
+ uint16 attr;
+ unsigned int i, j, k, l;
+
+ printf("starting open attr test\n");
+
+ if (!torture_open_connection(&cli1)) {
+ return False;
+ }
+
+ cli_sockopt(cli1, sockops);
+
+ for (k = 0, i = 0; i < sizeof(open_attrs_table)/sizeof(uint32); i++) {
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname);
+ fnum1 = cli_nt_create_full(cli1, fname, 0, FILE_WRITE_DATA, open_attrs_table[i],
+ FILE_SHARE_NONE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("open %d (1) of %s failed (%s)\n", i, fname, cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close %d (1) of %s failed (%s)\n", i, fname, cli_errstr(cli1));
+ return False;
+ }
+
+ for (j = 0; j < sizeof(open_attrs_table)/sizeof(uint32); j++) {
+ fnum1 = cli_nt_create_full(cli1, fname, 0, FILE_READ_DATA|FILE_WRITE_DATA, open_attrs_table[j],
+ FILE_SHARE_NONE, FILE_OVERWRITE, 0, 0);
+
+ if (fnum1 == -1) {
+ for (l = 0; l < sizeof(attr_results)/sizeof(struct trunc_open_results); l++) {
+ if (attr_results[l].num == k) {
+ printf("[%d] trunc open 0x%x -> 0x%x of %s failed - should have succeeded !(0x%x:%s)\n",
+ k, open_attrs_table[i],
+ open_attrs_table[j],
+ fname, NT_STATUS_V(cli_nt_error(cli1)), cli_errstr(cli1));
+ correct = False;
+ }
+ }
+ if (NT_STATUS_V(cli_nt_error(cli1)) != NT_STATUS_V(NT_STATUS_ACCESS_DENIED)) {
+ printf("[%d] trunc open 0x%x -> 0x%x failed with wrong error code %s\n",
+ k, open_attrs_table[i], open_attrs_table[j],
+ cli_errstr(cli1));
+ correct = False;
+ }
+#if 0
+ printf("[%d] trunc open 0x%x -> 0x%x failed\n", k, open_attrs_table[i], open_attrs_table[j]);
+#endif
+ k++;
+ continue;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close %d (2) of %s failed (%s)\n", j, fname, cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_getatr(cli1, fname, &attr, NULL, NULL)) {
+ printf("getatr(2) failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+#if 0
+ printf("[%d] getatr check [0x%x] trunc [0x%x] got attr 0x%x\n",
+ k, open_attrs_table[i], open_attrs_table[j], attr );
+#endif
+
+ for (l = 0; l < sizeof(attr_results)/sizeof(struct trunc_open_results); l++) {
+ if (attr_results[l].num == k) {
+ if (attr != attr_results[l].result_attr ||
+ open_attrs_table[i] != attr_results[l].init_attr ||
+ open_attrs_table[j] != attr_results[l].trunc_attr) {
+ printf("getatr check failed. [0x%x] trunc [0x%x] got attr 0x%x, should be 0x%x\n",
+ open_attrs_table[i],
+ open_attrs_table[j],
+ (unsigned int)attr,
+ attr_results[l].result_attr);
+ correct = False;
+ }
+ break;
+ }
+ }
+ k++;
+ }
+ }
+
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname);
+
+ printf("open attr test %s.\n", correct ? "passed" : "failed");
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+ return correct;
+}
+
+static void list_fn(file_info *finfo, const char *name, void *state)
+{
+
+}
+
+/*
+ test directory listing speed
+ */
+static BOOL run_dirtest(int dummy)
+{
+ int i;
+ static struct cli_state *cli;
+ int fnum;
+ double t1;
+ BOOL correct = True;
+
+ printf("starting directory test\n");
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ cli_sockopt(cli, sockops);
+
+ srandom(0);
+ for (i=0;i<torture_numops;i++) {
+ fstring fname;
+ slprintf(fname, sizeof(fname), "\\%x", (int)random());
+ fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ fprintf(stderr,"Failed to open %s\n", fname);
+ return False;
+ }
+ cli_close(cli, fnum);
+ }
+
+ t1 = end_timer();
+
+ printf("Matched %d\n", cli_list(cli, "a*.*", 0, list_fn, NULL));
+ printf("Matched %d\n", cli_list(cli, "b*.*", 0, list_fn, NULL));
+ printf("Matched %d\n", cli_list(cli, "xyzabc", 0, list_fn, NULL));
+
+ printf("dirtest core %g seconds\n", end_timer() - t1);
+
+ srandom(0);
+ for (i=0;i<torture_numops;i++) {
+ fstring fname;
+ slprintf(fname, sizeof(fname), "\\%x", (int)random());
+ cli_unlink(cli, fname);
+ }
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ printf("finished dirtest\n");
+
+ return correct;
+}
+
+static void del_fn(file_info *finfo, const char *mask, void *state)
+{
+ struct cli_state *pcli = (struct cli_state *)state;
+ fstring fname;
+ slprintf(fname, sizeof(fname), "\\LISTDIR\\%s", finfo->name);
+
+ if (strcmp(finfo->name, ".") == 0 || strcmp(finfo->name, "..") == 0)
+ return;
+
+ if (finfo->mode & aDIR) {
+ if (!cli_rmdir(pcli, fname))
+ printf("del_fn: failed to rmdir %s\n,", fname );
+ } else {
+ if (!cli_unlink(pcli, fname))
+ printf("del_fn: failed to unlink %s\n,", fname );
+ }
+}
+
+
+/*
+ sees what IOCTLs are supported
+ */
+BOOL torture_ioctl_test(int dummy)
+{
+ static struct cli_state *cli;
+ uint16 device, function;
+ int fnum;
+ const char *fname = "\\ioctl.dat";
+ DATA_BLOB blob;
+ NTSTATUS status;
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ printf("starting ioctl test\n");
+
+ cli_unlink(cli, fname);
+
+ fnum = cli_open(cli, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli));
+ return False;
+ }
+
+ status = cli_raw_ioctl(cli, fnum, 0x2d0000 | (0x0420<<2), &blob);
+ printf("ioctl device info: %s\n", cli_errstr(cli));
+
+ status = cli_raw_ioctl(cli, fnum, IOCTL_QUERY_JOB_INFO, &blob);
+ printf("ioctl job info: %s\n", cli_errstr(cli));
+
+ for (device=0;device<0x100;device++) {
+ printf("testing device=0x%x\n", device);
+ for (function=0;function<0x100;function++) {
+ uint32 code = (device<<16) | function;
+
+ status = cli_raw_ioctl(cli, fnum, code, &blob);
+
+ if (NT_STATUS_IS_OK(status)) {
+ printf("ioctl 0x%x OK : %d bytes\n", code, blob.length);
+ data_blob_free(&blob);
+ }
+ }
+ }
+
+ if (!torture_close_connection(cli)) {
+ return False;
+ }
+
+ return True;
+}
+
+
+/*
+ tries varients of chkpath
+ */
+BOOL torture_chkpath_test(int dummy)
+{
+ static struct cli_state *cli;
+ int fnum;
+ BOOL ret;
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ printf("starting chkpath test\n");
+
+ /* cleanup from an old run */
+ cli_rmdir(cli, "\\chkpath.dir\\dir2");
+ cli_unlink(cli, "\\chkpath.dir\\*");
+ cli_rmdir(cli, "\\chkpath.dir");
+
+ if (!cli_mkdir(cli, "\\chkpath.dir")) {
+ printf("mkdir1 failed : %s\n", cli_errstr(cli));
+ return False;
+ }
+
+ if (!cli_mkdir(cli, "\\chkpath.dir\\dir2")) {
+ printf("mkdir2 failed : %s\n", cli_errstr(cli));
+ return False;
+ }
+
+ fnum = cli_open(cli, "\\chkpath.dir\\foo.txt", O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum == -1) {
+ printf("open1 failed (%s)\n", cli_errstr(cli));
+ return False;
+ }
+ cli_close(cli, fnum);
+
+ if (!cli_chkpath(cli, "\\chkpath.dir")) {
+ printf("chkpath1 failed: %s\n", cli_errstr(cli));
+ ret = False;
+ }
+
+ if (!cli_chkpath(cli, "\\chkpath.dir\\dir2")) {
+ printf("chkpath2 failed: %s\n", cli_errstr(cli));
+ ret = False;
+ }
+
+ if (!cli_chkpath(cli, "\\chkpath.dir\\foo.txt")) {
+ ret = check_error(__LINE__, cli, ERRDOS, ERRbadpath,
+ NT_STATUS_NOT_A_DIRECTORY);
+ } else {
+ printf("* chkpath on a file should fail\n");
+ ret = False;
+ }
+
+ if (!cli_chkpath(cli, "\\chkpath.dir\\bar.txt")) {
+ ret = check_error(__LINE__, cli, ERRDOS, ERRbadfile,
+ NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ } else {
+ printf("* chkpath on a non existant file should fail\n");
+ ret = False;
+ }
+
+ if (!cli_chkpath(cli, "\\chkpath.dir\\dirxx\\bar.txt")) {
+ ret = check_error(__LINE__, cli, ERRDOS, ERRbadpath,
+ NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ } else {
+ printf("* chkpath on a non existent component should fail\n");
+ ret = False;
+ }
+
+ cli_rmdir(cli, "\\chkpath.dir\\dir2");
+ cli_unlink(cli, "\\chkpath.dir\\*");
+ cli_rmdir(cli, "\\chkpath.dir");
+
+ if (!torture_close_connection(cli)) {
+ return False;
+ }
+
+ return ret;
+}
+
+static BOOL run_eatest(int dummy)
+{
+ static struct cli_state *cli;
+ const char *fname = "\\eatest.txt";
+ BOOL correct = True;
+ int fnum, i;
+ size_t num_eas;
+ struct ea_struct *ea_list = NULL;
+ TALLOC_CTX *mem_ctx = talloc_init("eatest");
+
+ printf("starting eatest\n");
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ cli_unlink(cli, fname);
+ fnum = cli_nt_create_full(cli, fname, 0,
+ FIRST_DESIRED_ACCESS, FILE_ATTRIBUTE_ARCHIVE,
+ FILE_SHARE_NONE, FILE_OVERWRITE_IF,
+ 0x4044, 0);
+
+ if (fnum == -1) {
+ printf("open failed - %s\n", cli_errstr(cli));
+ return False;
+ }
+
+ for (i = 0; i < 10; i++) {
+ fstring ea_name, ea_val;
+
+ slprintf(ea_name, sizeof(ea_name), "EA_%d", i);
+ memset(ea_val, (char)i+1, i+1);
+ if (!cli_set_ea_fnum(cli, fnum, ea_name, ea_val, i+1)) {
+ printf("ea_set of name %s failed - %s\n", ea_name, cli_errstr(cli));
+ return False;
+ }
+ }
+
+ cli_close(cli, fnum);
+ for (i = 0; i < 10; i++) {
+ fstring ea_name, ea_val;
+
+ slprintf(ea_name, sizeof(ea_name), "EA_%d", i+10);
+ memset(ea_val, (char)i+1, i+1);
+ if (!cli_set_ea_path(cli, fname, ea_name, ea_val, i+1)) {
+ printf("ea_set of name %s failed - %s\n", ea_name, cli_errstr(cli));
+ return False;
+ }
+ }
+
+ if (!cli_get_ea_list_path(cli, fname, mem_ctx, &num_eas, &ea_list)) {
+ printf("ea_get list failed - %s\n", cli_errstr(cli));
+ correct = False;
+ }
+
+ printf("num_eas = %d\n", num_eas);
+
+ if (num_eas != 20) {
+ printf("Should be 20 EA's stored... failing.\n");
+ correct = False;
+ }
+
+ for (i = 0; i < num_eas; i++) {
+ printf("%d: ea_name = %s. Val = ", i, ea_list[i].name);
+ dump_data(0, ea_list[i].value.data, ea_list[i].value.length);
+ }
+
+ /* Setting EA's to zero length deletes them. Test this */
+ printf("Now deleting all EA's....\n");
+
+ for (i = 0; i < 20; i++) {
+ fstring ea_name;
+ slprintf(ea_name, sizeof(ea_name), "EA_%d", i);
+ if (!cli_set_ea_path(cli, fname, ea_name, "", 0)) {
+ printf("ea_set of name %s failed - %s\n", ea_name, cli_errstr(cli));
+ return False;
+ }
+ }
+
+ if (!cli_get_ea_list_path(cli, fname, mem_ctx, &num_eas, &ea_list)) {
+ printf("ea_get list failed - %s\n", cli_errstr(cli));
+ correct = False;
+ }
+
+ printf("num_eas = %d\n", num_eas);
+ for (i = 0; i < num_eas; i++) {
+ printf("%d: ea_name = %s. Val = ", i, ea_list[i].name);
+ dump_data(0, ea_list[i].value.data, ea_list[i].value.length);
+ }
+
+ if (num_eas != 0) {
+ printf("deleting EA's failed.\n");
+ correct = False;
+ }
+
+ /* Try and delete a non existant EA. */
+ if (!cli_set_ea_path(cli, fname, "foo", "", 0)) {
+ printf("deleting non-existant EA 'foo' should succeed. %s\n", cli_errstr(cli));
+ correct = False;
+ }
+
+ talloc_destroy(mem_ctx);
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ return correct;
+}
+
+static BOOL run_dirtest1(int dummy)
+{
+ int i;
+ static struct cli_state *cli;
+ int fnum, num_seen;
+ BOOL correct = True;
+
+ printf("starting directory test\n");
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ cli_sockopt(cli, sockops);
+
+ cli_list(cli, "\\LISTDIR\\*", 0, del_fn, cli);
+ cli_list(cli, "\\LISTDIR\\*", aDIR, del_fn, cli);
+ cli_rmdir(cli, "\\LISTDIR");
+ cli_mkdir(cli, "\\LISTDIR");
+
+ /* Create 1000 files and 1000 directories. */
+ for (i=0;i<1000;i++) {
+ fstring fname;
+ slprintf(fname, sizeof(fname), "\\LISTDIR\\f%d", i);
+ fnum = cli_nt_create_full(cli, fname, 0, GENERIC_ALL_ACCESS, FILE_ATTRIBUTE_ARCHIVE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OVERWRITE_IF, 0, 0);
+ if (fnum == -1) {
+ fprintf(stderr,"Failed to open %s\n", fname);
+ return False;
+ }
+ cli_close(cli, fnum);
+ }
+ for (i=0;i<1000;i++) {
+ fstring fname;
+ slprintf(fname, sizeof(fname), "\\LISTDIR\\d%d", i);
+ if (!cli_mkdir(cli, fname)) {
+ fprintf(stderr,"Failed to open %s\n", fname);
+ return False;
+ }
+ }
+
+ /* Now ensure that doing an old list sees both files and directories. */
+ num_seen = cli_list_old(cli, "\\LISTDIR\\*", aDIR, list_fn, NULL);
+ printf("num_seen = %d\n", num_seen );
+ /* We should see 100 files + 1000 directories + . and .. */
+ if (num_seen != 2002)
+ correct = False;
+
+ /* Ensure if we have the "must have" bits we only see the
+ * relevent entries.
+ */
+ num_seen = cli_list_old(cli, "\\LISTDIR\\*", (aDIR<<8)|aDIR, list_fn, NULL);
+ printf("num_seen = %d\n", num_seen );
+ if (num_seen != 1002)
+ correct = False;
+
+ num_seen = cli_list_old(cli, "\\LISTDIR\\*", (aARCH<<8)|aDIR, list_fn, NULL);
+ printf("num_seen = %d\n", num_seen );
+ if (num_seen != 1000)
+ correct = False;
+
+ /* Delete everything. */
+ cli_list(cli, "\\LISTDIR\\*", 0, del_fn, cli);
+ cli_list(cli, "\\LISTDIR\\*", aDIR, del_fn, cli);
+ cli_rmdir(cli, "\\LISTDIR");
+
+#if 0
+ printf("Matched %d\n", cli_list(cli, "a*.*", 0, list_fn, NULL));
+ printf("Matched %d\n", cli_list(cli, "b*.*", 0, list_fn, NULL));
+ printf("Matched %d\n", cli_list(cli, "xyzabc", 0, list_fn, NULL));
+#endif
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ printf("finished dirtest1\n");
+
+ return correct;
+}
+
+static BOOL run_error_map_extract(int dummy) {
+
+ static struct cli_state c_dos;
+ static struct cli_state c_nt;
+
+ uint32 error;
+
+ uint32 flgs2, errnum;
+ uint8 errclass;
+
+ NTSTATUS nt_status;
+
+ fstring user;
+
+ /* NT-Error connection */
+
+ if (!open_nbt_connection(&c_nt)) {
+ return False;
+ }
+
+ c_nt.use_spnego = False;
+
+ if (!cli_negprot(&c_nt)) {
+ printf("%s rejected the NT-error negprot (%s)\n",host, cli_errstr(&c_nt));
+ cli_shutdown(&c_nt);
+ return False;
+ }
+
+ if (!cli_session_setup(&c_nt, "", "", 0, "", 0,
+ workgroup)) {
+ printf("%s rejected the NT-error initial session setup (%s)\n",host, cli_errstr(&c_nt));
+ return False;
+ }
+
+ /* DOS-Error connection */
+
+ if (!open_nbt_connection(&c_dos)) {
+ return False;
+ }
+
+ c_dos.use_spnego = False;
+ c_dos.force_dos_errors = True;
+
+ if (!cli_negprot(&c_dos)) {
+ printf("%s rejected the DOS-error negprot (%s)\n",host, cli_errstr(&c_dos));
+ cli_shutdown(&c_dos);
+ return False;
+ }
+
+ if (!cli_session_setup(&c_dos, "", "", 0, "", 0,
+ workgroup)) {
+ printf("%s rejected the DOS-error initial session setup (%s)\n",host, cli_errstr(&c_dos));
+ return False;
+ }
+
+ for (error=(0xc0000000 | 0x1); error < (0xc0000000| 0xFFF); error++) {
+ fstr_sprintf(user, "%X", error);
+
+ if (cli_session_setup(&c_nt, user,
+ password, strlen(password),
+ password, strlen(password),
+ workgroup)) {
+ printf("/** Session setup succeeded. This shouldn't happen...*/\n");
+ }
+
+ flgs2 = SVAL(c_nt.inbuf,smb_flg2);
+
+ /* Case #1: 32-bit NT errors */
+ if (flgs2 & FLAGS2_32_BIT_ERROR_CODES) {
+ nt_status = NT_STATUS(IVAL(c_nt.inbuf,smb_rcls));
+ } else {
+ printf("/** Dos error on NT connection! (%s) */\n",
+ cli_errstr(&c_nt));
+ nt_status = NT_STATUS(0xc0000000);
+ }
+
+ if (cli_session_setup(&c_dos, user,
+ password, strlen(password),
+ password, strlen(password),
+ workgroup)) {
+ printf("/** Session setup succeeded. This shouldn't happen...*/\n");
+ }
+ flgs2 = SVAL(c_dos.inbuf,smb_flg2), errnum;
+
+ /* Case #1: 32-bit NT errors */
+ if (flgs2 & FLAGS2_32_BIT_ERROR_CODES) {
+ printf("/** NT error on DOS connection! (%s) */\n",
+ cli_errstr(&c_nt));
+ errnum = errclass = 0;
+ } else {
+ cli_dos_error(&c_dos, &errclass, &errnum);
+ }
+
+ if (NT_STATUS_V(nt_status) != error) {
+ printf("/*\t{ This NT error code was 'sqashed'\n\t from %s to %s \n\t during the session setup }\n*/\n",
+ get_nt_error_c_code(NT_STATUS(error)),
+ get_nt_error_c_code(nt_status));
+ }
+
+ printf("\t{%s,\t%s,\t%s},\n",
+ smb_dos_err_class(errclass),
+ smb_dos_err_name(errclass, errnum),
+ get_nt_error_c_code(NT_STATUS(error)));
+ }
+ return True;
+}
+
+static double create_procs(BOOL (*fn)(int), BOOL *result)
+{
+ int i, status;
+ volatile pid_t *child_status;
+ volatile BOOL *child_status_out;
+ int synccount;
+ int tries = 8;
+
+ synccount = 0;
+
+ child_status = (volatile pid_t *)shm_setup(sizeof(pid_t)*nprocs);
+ if (!child_status) {
+ printf("Failed to setup shared memory\n");
+ return -1;
+ }
+
+ child_status_out = (volatile BOOL *)shm_setup(sizeof(BOOL)*nprocs);
+ if (!child_status_out) {
+ printf("Failed to setup result status shared memory\n");
+ return -1;
+ }
+
+ for (i = 0; i < nprocs; i++) {
+ child_status[i] = 0;
+ child_status_out[i] = True;
+ }
+
+ start_timer();
+
+ for (i=0;i<nprocs;i++) {
+ procnum = i;
+ if (fork() == 0) {
+ pid_t mypid = getpid();
+ sys_srandom(((int)mypid) ^ ((int)time(NULL)));
+
+ slprintf(myname,sizeof(myname),"CLIENT%d", i);
+
+ while (1) {
+ if (torture_open_connection(&current_cli)) break;
+ if (tries-- == 0) {
+ printf("pid %d failed to start\n", (int)getpid());
+ _exit(1);
+ }
+ smb_msleep(10);
+ }
+
+ child_status[i] = getpid();
+
+ while (child_status[i] && end_timer() < 5) smb_msleep(2);
+
+ child_status_out[i] = fn(i);
+ _exit(0);
+ }
+ }
+
+ do {
+ synccount = 0;
+ for (i=0;i<nprocs;i++) {
+ if (child_status[i]) synccount++;
+ }
+ if (synccount == nprocs) break;
+ smb_msleep(10);
+ } while (end_timer() < 30);
+
+ if (synccount != nprocs) {
+ printf("FAILED TO START %d CLIENTS (started %d)\n", nprocs, synccount);
+ *result = False;
+ return end_timer();
+ }
+
+ /* start the client load */
+ start_timer();
+
+ for (i=0;i<nprocs;i++) {
+ child_status[i] = 0;
+ }
+
+ printf("%d clients started\n", nprocs);
+
+ for (i=0;i<nprocs;i++) {
+ while (waitpid(0, &status, 0) == -1 && errno == EINTR) /* noop */ ;
+ }
+
+ printf("\n");
+
+ for (i=0;i<nprocs;i++) {
+ if (!child_status_out[i]) {
+ *result = False;
+ }
+ }
+ return end_timer();
+}
+
+#define FLAG_MULTIPROC 1
+
+static struct {
+ const char *name;
+ BOOL (*fn)(int);
+ unsigned flags;
+} torture_ops[] = {
+ {"FDPASS", run_fdpasstest, 0},
+ {"LOCK1", run_locktest1, 0},
+ {"LOCK2", run_locktest2, 0},
+ {"LOCK3", run_locktest3, 0},
+ {"LOCK4", run_locktest4, 0},
+ {"LOCK5", run_locktest5, 0},
+ {"LOCK6", run_locktest6, 0},
+ {"LOCK7", run_locktest7, 0},
+ {"UNLINK", run_unlinktest, 0},
+ {"BROWSE", run_browsetest, 0},
+ {"ATTR", run_attrtest, 0},
+ {"TRANS2", run_trans2test, 0},
+ {"MAXFID", run_maxfidtest, FLAG_MULTIPROC},
+ {"TORTURE",run_torture, FLAG_MULTIPROC},
+ {"RANDOMIPC", run_randomipc, 0},
+ {"NEGNOWAIT", run_negprot_nowait, 0},
+ {"NBENCH", run_nbench, 0},
+ {"OPLOCK1", run_oplock1, 0},
+ {"OPLOCK2", run_oplock2, 0},
+ {"OPLOCK3", run_oplock3, 0},
+ {"DIR", run_dirtest, 0},
+ {"DIR1", run_dirtest1, 0},
+ {"DENY1", torture_denytest1, 0},
+ {"DENY2", torture_denytest2, 0},
+ {"TCON", run_tcon_test, 0},
+ {"TCONDEV", run_tcon_devtype_test, 0},
+ {"RW1", run_readwritetest, 0},
+ {"RW2", run_readwritemulti, FLAG_MULTIPROC},
+ {"RW3", run_readwritelarge, 0},
+ {"OPEN", run_opentest, 0},
+#if 1
+ {"OPENATTR", run_openattrtest, 0},
+#endif
+ {"XCOPY", run_xcopy, 0},
+ {"RENAME", run_rename, 0},
+ {"DELETE", run_deletetest, 0},
+ {"PROPERTIES", run_properties, 0},
+ {"MANGLE", torture_mangle, 0},
+ {"W2K", run_w2ktest, 0},
+ {"TRANS2SCAN", torture_trans2_scan, 0},
+ {"NTTRANSSCAN", torture_nttrans_scan, 0},
+ {"UTABLE", torture_utable, 0},
+ {"CASETABLE", torture_casetable, 0},
+ {"ERRMAPEXTRACT", run_error_map_extract, 0},
+ {"PIPE_NUMBER", run_pipe_number, 0},
+ {"TCON2", run_tcon2_test, 0},
+ {"IOCTL", torture_ioctl_test, 0},
+ {"CHKPATH", torture_chkpath_test, 0},
+ {"FDSESS", run_fdsesstest, 0},
+ { "EATEST", run_eatest, 0},
+ {NULL, NULL, 0}};
+
+
+
+/****************************************************************************
+run a specified test or "ALL"
+****************************************************************************/
+static BOOL run_test(const char *name)
+{
+ BOOL ret = True;
+ BOOL result = True;
+ BOOL found = False;
+ int i;
+ double t;
+ if (strequal(name,"ALL")) {
+ for (i=0;torture_ops[i].name;i++) {
+ run_test(torture_ops[i].name);
+ }
+ found = True;
+ }
+
+ for (i=0;torture_ops[i].name;i++) {
+ fstr_sprintf(randomfname, "\\XX%x",
+ (unsigned)random());
+
+ if (strequal(name, torture_ops[i].name)) {
+ found = True;
+ printf("Running %s\n", name);
+ if (torture_ops[i].flags & FLAG_MULTIPROC) {
+ t = create_procs(torture_ops[i].fn, &result);
+ if (!result) {
+ ret = False;
+ printf("TEST %s FAILED!\n", name);
+ }
+
+ } else {
+ start_timer();
+ if (!torture_ops[i].fn(0)) {
+ ret = False;
+ printf("TEST %s FAILED!\n", name);
+ }
+ t = end_timer();
+ }
+ printf("%s took %g secs\n\n", name, t);
+ }
+ }
+
+ if (!found) {
+ printf("Did not find a test named %s\n", name);
+ ret = False;
+ }
+
+ return ret;
+}
+
+
+static void usage(void)
+{
+ int i;
+
+ printf("Usage: smbtorture //server/share <options> TEST1 TEST2 ...\n");
+
+ printf("\t-d debuglevel\n");
+ printf("\t-U user%%pass\n");
+ printf("\t-k use kerberos\n");
+ printf("\t-N numprocs\n");
+ printf("\t-n my_netbios_name\n");
+ printf("\t-W workgroup\n");
+ printf("\t-o num_operations\n");
+ printf("\t-O socket_options\n");
+ printf("\t-m maximum protocol\n");
+ printf("\t-L use oplocks\n");
+ printf("\t-c CLIENT.TXT specify client load file for NBENCH\n");
+ printf("\t-A showall\n");
+ printf("\t-p port\n");
+ printf("\t-s seed\n");
+ printf("\n\n");
+
+ printf("tests are:");
+ for (i=0;torture_ops[i].name;i++) {
+ printf(" %s", torture_ops[i].name);
+ }
+ printf("\n");
+
+ printf("default test is ALL\n");
+
+ exit(1);
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+ int opt, i;
+ char *p;
+ int gotuser = 0;
+ int gotpass = 0;
+ extern char *optarg;
+ extern int optind;
+ BOOL correct = True;
+
+ dbf = x_stdout;
+
+#ifdef HAVE_SETBUFFER
+ setbuffer(stdout, NULL, 0);
+#endif
+
+ lp_load(dyn_CONFIGFILE,True,False,False);
+ load_interfaces();
+
+ if (argc < 2) {
+ usage();
+ }
+
+ for(p = argv[1]; *p; p++)
+ if(*p == '\\')
+ *p = '/';
+
+ if (strncmp(argv[1], "//", 2)) {
+ usage();
+ }
+
+ fstrcpy(host, &argv[1][2]);
+ p = strchr_m(&host[2],'/');
+ if (!p) {
+ usage();
+ }
+ *p = 0;
+ fstrcpy(share, p+1);
+
+ get_myname(myname);
+
+ if (*username == 0 && getenv("LOGNAME")) {
+ fstrcpy(username,getenv("LOGNAME"));
+ }
+
+ argc--;
+ argv++;
+
+ srandom(time(NULL));
+
+ fstrcpy(workgroup, lp_workgroup());
+
+ while ((opt = getopt(argc, argv, "p:hW:U:n:N:O:o:m:Ld:Ac:ks:")) != EOF) {
+ switch (opt) {
+ case 'p':
+ port_to_use = atoi(optarg);
+ break;
+ case 's':
+ srandom(atoi(optarg));
+ break;
+ case 'W':
+ fstrcpy(workgroup,optarg);
+ break;
+ case 'm':
+ max_protocol = interpret_protocol(optarg, max_protocol);
+ break;
+ case 'N':
+ nprocs = atoi(optarg);
+ break;
+ case 'o':
+ torture_numops = atoi(optarg);
+ break;
+ case 'd':
+ DEBUGLEVEL = atoi(optarg);
+ break;
+ case 'O':
+ sockops = optarg;
+ break;
+ case 'L':
+ use_oplocks = True;
+ break;
+ case 'A':
+ torture_showall = True;
+ break;
+ case 'n':
+ fstrcpy(myname, optarg);
+ break;
+ case 'c':
+ client_txt = optarg;
+ break;
+ case 'k':
+#ifdef HAVE_KRB5
+ use_kerberos = True;
+#else
+ d_printf("No kerberos support compiled in\n");
+ exit(1);
+#endif
+ break;
+ case 'U':
+ gotuser = 1;
+ fstrcpy(username,optarg);
+ p = strchr_m(username,'%');
+ if (p) {
+ *p = 0;
+ fstrcpy(password, p+1);
+ gotpass = 1;
+ }
+ break;
+ default:
+ printf("Unknown option %c (%d)\n", (char)opt, opt);
+ usage();
+ }
+ }
+
+ if(use_kerberos && !gotuser) gotpass = True;
+
+ while (!gotpass) {
+ p = getpass("Password:");
+ if (p) {
+ fstrcpy(password, p);
+ gotpass = 1;
+ }
+ }
+
+ printf("host=%s share=%s user=%s myname=%s\n",
+ host, share, username, myname);
+
+ if (argc == optind) {
+ correct = run_test("ALL");
+ } else {
+ for (i=optind;i<argc;i++) {
+ if (!run_test(argv[i])) {
+ correct = False;
+ }
+ }
+ }
+
+ if (correct) {
+ return(0);
+ } else {
+ return(1);
+ }
+}
diff --git a/source/torture/utable.c b/source/torture/utable.c
new file mode 100644
index 00000000000..ba803a0da4f
--- /dev/null
+++ b/source/torture/utable.c
@@ -0,0 +1,193 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB torture tester - unicode table dumper
+ Copyright (C) Andrew Tridgell 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+BOOL torture_utable(int dummy)
+{
+ struct cli_state *cli;
+ fstring fname, alt_name;
+ int fnum;
+ smb_ucs2_t c2;
+ int c, len, fd;
+ int chars_allowed=0, alt_allowed=0;
+ uint8 valid[0x10000];
+
+ printf("starting utable\n");
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ memset(valid, 0, sizeof(valid));
+
+ cli_mkdir(cli, "\\utable");
+ cli_unlink(cli, "\\utable\\*");
+
+ for (c=1; c < 0x10000; c++) {
+ char *p;
+
+ SSVAL(&c2, 0, c);
+ fstrcpy(fname, "\\utable\\x");
+ p = fname+strlen(fname);
+ len = convert_string(CH_UCS2, CH_UNIX,
+ &c2, 2,
+ p, sizeof(fname)-strlen(fname), True);
+ p[len] = 0;
+ fstrcat(fname,"_a_long_extension");
+
+ fnum = cli_open(cli, fname, O_RDWR | O_CREAT | O_TRUNC,
+ DENY_NONE);
+ if (fnum == -1) continue;
+
+ chars_allowed++;
+
+ cli_qpathinfo_alt_name(cli, fname, alt_name);
+
+ if (strncmp(alt_name, "X_A_L", 5) != 0) {
+ alt_allowed++;
+ valid[c] = 1;
+ d_printf("fname=[%s] alt_name=[%s]\n", fname, alt_name);
+ }
+
+ cli_close(cli, fnum);
+ cli_unlink(cli, fname);
+
+ if (c % 100 == 0) {
+ printf("%d (%d/%d)\r", c, chars_allowed, alt_allowed);
+ }
+ }
+ printf("%d (%d/%d)\n", c, chars_allowed, alt_allowed);
+
+ cli_rmdir(cli, "\\utable");
+
+ d_printf("%d chars allowed %d alt chars allowed\n", chars_allowed, alt_allowed);
+
+ fd = open("valid.dat", O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ if (fd == -1) {
+ d_printf("Failed to create valid.dat - %s", strerror(errno));
+ return False;
+ }
+ write(fd, valid, 0x10000);
+ close(fd);
+ d_printf("wrote valid.dat\n");
+
+ return True;
+}
+
+
+static char *form_name(int c)
+{
+ static fstring fname;
+ smb_ucs2_t c2;
+ char *p;
+ int len;
+
+ fstrcpy(fname, "\\utable\\");
+ p = fname+strlen(fname);
+ SSVAL(&c2, 0, c);
+
+ len = convert_string(CH_UCS2, CH_UNIX,
+ &c2, 2,
+ p, sizeof(fname)-strlen(fname), True);
+ p[len] = 0;
+ return fname;
+}
+
+BOOL torture_casetable(int dummy)
+{
+ static struct cli_state *cli;
+ char *fname;
+ int fnum;
+ int c, i;
+#define MAX_EQUIVALENCE 8
+ smb_ucs2_t equiv[0x10000][MAX_EQUIVALENCE];
+ printf("starting casetable\n");
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ memset(equiv, 0, sizeof(equiv));
+
+ cli_unlink(cli, "\\utable\\*");
+ cli_rmdir(cli, "\\utable");
+ if (!cli_mkdir(cli, "\\utable")) {
+ printf("Failed to create utable directory!\n");
+ return False;
+ }
+
+ for (c=1; c < 0x10000; c++) {
+ size_t size;
+
+ if (c == '.' || c == '\\') continue;
+
+ printf("%04x (%c)\n", c, isprint(c)?c:'.');
+
+ fname = form_name(c);
+ fnum = cli_nt_create_full(cli, fname, 0,
+ GENERIC_ALL_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE,
+ FILE_OPEN_IF, 0, 0);
+
+ if (fnum == -1) {
+ printf("Failed to create file with char %04x\n", c);
+ continue;
+ }
+
+ size = 0;
+
+ if (!cli_qfileinfo(cli, fnum, NULL, &size,
+ NULL, NULL, NULL, NULL, NULL)) continue;
+
+ if (size > 0) {
+ /* found a character equivalence! */
+ int c2[MAX_EQUIVALENCE];
+
+ if (size/sizeof(int) >= MAX_EQUIVALENCE) {
+ printf("too many chars match?? size=%ld c=0x%04x\n",
+ (unsigned long)size, c);
+ cli_close(cli, fnum);
+ return False;
+ }
+
+ cli_read(cli, fnum, (char *)c2, 0, size);
+ printf("%04x: ", c);
+ equiv[c][0] = c;
+ for (i=0; i<size/sizeof(int); i++) {
+ printf("%04x ", c2[i]);
+ equiv[c][i+1] = c2[i];
+ }
+ printf("\n");
+ fflush(stdout);
+ }
+
+ cli_write(cli, fnum, 0, (char *)&c, size, sizeof(c));
+ cli_close(cli, fnum);
+ }
+
+ cli_unlink(cli, "\\utable\\*");
+ cli_rmdir(cli, "\\utable");
+
+ return True;
+}
diff --git a/source/torture/vfstest.c b/source/torture/vfstest.c
new file mode 100644
index 00000000000..88fe3486493
--- /dev/null
+++ b/source/torture/vfstest.c
@@ -0,0 +1,571 @@
+/*
+ Unix SMB/CIFS implementation.
+ VFS module tester
+
+ Copyright (C) Simo Sorce 2002
+ Copyright (C) Eric Lorimer 2002
+ Copyright (C) Jelmer Vernooij 2002,2003
+
+ Most of this code was ripped off of rpcclient.
+ Copyright (C) Tim Potter 2000-2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "vfstest.h"
+
+/* List to hold groups of commands */
+static struct cmd_list {
+ struct cmd_list *prev, *next;
+ struct cmd_set *cmd_set;
+} *cmd_list;
+
+extern pstring user_socket_options;
+
+/****************************************************************************
+handle completion of commands for readline
+****************************************************************************/
+static char **completion_fn(char *text, int start, int end)
+{
+#define MAX_COMPLETIONS 100
+ char **matches;
+ int i, count=0;
+ struct cmd_list *commands = cmd_list;
+
+ if (start)
+ return NULL;
+
+ /* make sure we have a list of valid commands */
+ if (!commands)
+ return NULL;
+
+ matches = (char **)malloc(sizeof(matches[0])*MAX_COMPLETIONS);
+ if (!matches) return NULL;
+
+ matches[count++] = strdup(text);
+ if (!matches[0]) return NULL;
+
+ while (commands && count < MAX_COMPLETIONS-1)
+ {
+ if (!commands->cmd_set)
+ break;
+
+ for (i=0; commands->cmd_set[i].name; i++)
+ {
+ if ((strncmp(text, commands->cmd_set[i].name, strlen(text)) == 0) &&
+ commands->cmd_set[i].fn)
+ {
+ matches[count] = strdup(commands->cmd_set[i].name);
+ if (!matches[count])
+ return NULL;
+ count++;
+ }
+ }
+
+ commands = commands->next;
+
+ }
+
+ if (count == 2) {
+ SAFE_FREE(matches[0]);
+ matches[0] = strdup(matches[1]);
+ }
+ matches[count] = NULL;
+ return matches;
+}
+
+static char* next_command(char** cmdstr)
+{
+ static pstring command;
+ char *p;
+
+ if (!cmdstr || !(*cmdstr))
+ return NULL;
+
+ p = strchr_m(*cmdstr, ';');
+ if (p)
+ *p = '\0';
+ pstrcpy(command, *cmdstr);
+ *cmdstr = p;
+
+ return command;
+}
+
+/* Load specified configuration file */
+static NTSTATUS cmd_conf(struct vfs_state *vfs, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ if (argc != 2) {
+ printf("Usage: %s <smb.conf>\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (!lp_load(argv[1], False, True, False)) {
+ printf("Error loading \"%s\"\n", argv[1]);
+ return NT_STATUS_OK;
+ }
+
+ printf("\"%s\" successfully loaded\n", argv[1]);
+ return NT_STATUS_OK;
+}
+
+/* Display help on commands */
+static NTSTATUS cmd_help(struct vfs_state *vfs, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ struct cmd_list *tmp;
+ struct cmd_set *tmp_set;
+
+ /* Usage */
+ if (argc > 2) {
+ printf("Usage: %s [command]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ /* Help on one command */
+
+ if (argc == 2) {
+ for (tmp = cmd_list; tmp; tmp = tmp->next) {
+
+ tmp_set = tmp->cmd_set;
+
+ while(tmp_set->name) {
+ if (strequal(argv[1], tmp_set->name)) {
+ if (tmp_set->usage &&
+ tmp_set->usage[0])
+ printf("%s\n", tmp_set->usage);
+ else
+ printf("No help for %s\n", tmp_set->name);
+
+ return NT_STATUS_OK;
+ }
+
+ tmp_set++;
+ }
+ }
+
+ printf("No such command: %s\n", argv[1]);
+ return NT_STATUS_OK;
+ }
+
+ /* List all commands */
+
+ for (tmp = cmd_list; tmp; tmp = tmp->next) {
+
+ tmp_set = tmp->cmd_set;
+
+ while(tmp_set->name) {
+
+ printf("%15s\t\t%s\n", tmp_set->name,
+ tmp_set->description ? tmp_set->description:
+ "");
+
+ tmp_set++;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* Change the debug level */
+static NTSTATUS cmd_debuglevel(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ if (argc > 2) {
+ printf("Usage: %s [debuglevel]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc == 2) {
+ DEBUGLEVEL = atoi(argv[1]);
+ }
+
+ printf("debuglevel is %d\n", DEBUGLEVEL);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_freemem(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ /* Cleanup */
+ talloc_destroy(mem_ctx);
+ mem_ctx = NULL;
+ vfs->data = NULL;
+ vfs->data_size = 0;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_quit(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ /* Cleanup */
+ talloc_destroy(mem_ctx);
+
+ exit(0);
+ return NT_STATUS_OK; /* NOTREACHED */
+}
+
+static struct cmd_set vfstest_commands[] = {
+
+ { "GENERAL OPTIONS" },
+
+ { "conf", cmd_conf, "Load smb configuration file", "conf <smb.conf>" },
+ { "help", cmd_help, "Get help on commands", "" },
+ { "?", cmd_help, "Get help on commands", "" },
+ { "debuglevel", cmd_debuglevel, "Set debug level", "" },
+ { "freemem", cmd_freemem, "Free currently allocated buffers", "" },
+ { "exit", cmd_quit, "Exit program", "" },
+ { "quit", cmd_quit, "Exit program", "" },
+
+ { NULL }
+};
+
+static struct cmd_set separator_command[] = {
+ { "---------------", NULL, "----------------------" },
+ { NULL }
+};
+
+
+extern struct cmd_set vfs_commands[];
+static struct cmd_set *vfstest_command_list[] = {
+ vfstest_commands,
+ vfs_commands,
+ NULL
+};
+
+static void add_command_set(struct cmd_set *cmd_set)
+{
+ struct cmd_list *entry;
+
+ if (!(entry = (struct cmd_list *)malloc(sizeof(struct cmd_list)))) {
+ DEBUG(0, ("out of memory\n"));
+ return;
+ }
+
+ ZERO_STRUCTP(entry);
+
+ entry->cmd_set = cmd_set;
+ DLIST_ADD(cmd_list, entry);
+}
+
+static NTSTATUS do_cmd(struct vfs_state *vfs, struct cmd_set *cmd_entry, char *cmd)
+{
+ const char *p = cmd;
+ char **argv = NULL;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ pstring buf;
+ TALLOC_CTX *mem_ctx = NULL;
+ int argc = 0, i;
+
+ /* Count number of arguments first time through the loop then
+ allocate memory and strdup them. */
+
+ again:
+ while(next_token(&p, buf, " ", sizeof(buf))) {
+ if (argv) {
+ argv[argc] = strdup(buf);
+ }
+
+ argc++;
+ }
+
+ if (!argv) {
+
+ /* Create argument list */
+
+ argv = (char **)malloc(sizeof(char *) * argc);
+ memset(argv, 0, sizeof(char *) * argc);
+
+ if (!argv) {
+ fprintf(stderr, "out of memory\n");
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ p = cmd;
+ argc = 0;
+
+ goto again;
+ }
+
+ /* Call the function */
+
+ if (cmd_entry->fn) {
+
+ if (mem_ctx == NULL) {
+ /* Create mem_ctx */
+ if (!(mem_ctx = talloc_init("do_cmd"))) {
+ DEBUG(0, ("talloc_init() failed\n"));
+ goto done;
+ }
+ }
+
+ /* Run command */
+ result = cmd_entry->fn(vfs, mem_ctx, argc, (const char **)argv);
+
+ } else {
+ fprintf (stderr, "Invalid command\n");
+ goto done;
+ }
+
+ done:
+
+ /* Cleanup */
+
+ if (argv) {
+ for (i = 0; i < argc; i++)
+ SAFE_FREE(argv[i]);
+
+ SAFE_FREE(argv);
+ }
+
+ return result;
+}
+
+/* Process a command entered at the prompt or as part of -c */
+static NTSTATUS process_cmd(struct vfs_state *vfs, char *cmd)
+{
+ struct cmd_list *temp_list;
+ BOOL found = False;
+ pstring buf;
+ const char *p = cmd;
+ NTSTATUS result = NT_STATUS_OK;
+ int len = 0;
+
+ if (cmd[strlen(cmd) - 1] == '\n')
+ cmd[strlen(cmd) - 1] = '\0';
+
+ if (!next_token(&p, buf, " ", sizeof(buf))) {
+ return NT_STATUS_OK;
+ }
+
+ /* strip the trainly \n if it exsists */
+ len = strlen(buf);
+ if (buf[len-1] == '\n')
+ buf[len-1] = '\0';
+
+ /* Search for matching commands */
+
+ for (temp_list = cmd_list; temp_list; temp_list = temp_list->next) {
+ struct cmd_set *temp_set = temp_list->cmd_set;
+
+ while(temp_set->name) {
+ if (strequal(buf, temp_set->name)) {
+ found = True;
+ result = do_cmd(vfs, temp_set, cmd);
+
+ goto done;
+ }
+ temp_set++;
+ }
+ }
+
+ done:
+ if (!found && buf[0]) {
+ printf("command not found: %s\n", buf);
+ return NT_STATUS_OK;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ printf("result was %s\n", nt_errstr(result));
+ }
+
+ return result;
+}
+
+static void process_file(struct vfs_state *pvfs, char *filename) {
+ FILE *file;
+ char command[3 * PATH_MAX];
+
+ if (*filename == '-') {
+ file = stdin;
+ } else {
+ file = fopen(filename, "r");
+ if (file == NULL) {
+ printf("vfstest: error reading file (%s)!", filename);
+ printf("errno n.%d: %s", errno, strerror(errno));
+ exit(-1);
+ }
+ }
+
+ while (fgets(command, 3 * PATH_MAX, file) != NULL) {
+ process_cmd(pvfs, command);
+ }
+}
+
+void exit_server(const char *reason)
+{
+ DEBUG(3,("Server exit (%s)\n", (reason ? reason : "")));
+ exit(0);
+}
+
+static int server_fd = -1;
+int last_message = -1;
+
+int smbd_server_fd(void)
+{
+ return server_fd;
+}
+
+/****************************************************************************
+ Reload the services file.
+**************************************************************************/
+
+BOOL reload_services(BOOL test)
+{
+ BOOL ret;
+
+ if (lp_loaded()) {
+ pstring fname;
+ pstrcpy(fname,lp_configfile());
+ if (file_exist(fname, NULL) &&
+ !strcsequal(fname, dyn_CONFIGFILE)) {
+ pstrcpy(dyn_CONFIGFILE, fname);
+ test = False;
+ }
+ }
+
+ reopen_logs();
+
+ if (test && !lp_file_list_changed())
+ return(True);
+
+ lp_killunused(conn_snum_used);
+
+ ret = lp_load(dyn_CONFIGFILE, False, False, True);
+
+ load_printers();
+
+ /* perhaps the config filename is now set */
+ if (!test)
+ reload_services(True);
+
+ reopen_logs();
+
+ load_interfaces();
+
+ {
+ if (smbd_server_fd() != -1) {
+ set_socket_options(smbd_server_fd(),"SO_KEEPALIVE");
+ set_socket_options(smbd_server_fd(), user_socket_options);
+ }
+ }
+
+ mangle_reset_cache();
+ reset_stat_cache();
+
+ /* this forces service parameters to be flushed */
+ set_current_service(NULL,True);
+
+ return (ret);
+}
+
+/* Main function */
+
+int main(int argc, char *argv[])
+{
+ static char *cmdstr = NULL;
+ struct cmd_set **cmd_set;
+ static struct vfs_state vfs;
+ int i;
+ static char *filename = NULL;
+
+ /* make sure the vars that get altered (4th field) are in
+ a fixed location or certain compilers complain */
+ poptContext pc;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"file", 'f', POPT_ARG_STRING, &filename, 0, },
+ {"command", 'c', POPT_ARG_STRING, &cmdstr, 0, "Execute specified list of commands" },
+ POPT_COMMON_SAMBA
+ POPT_TABLEEND
+ };
+
+
+ setlinebuf(stdout);
+
+ pc = poptGetContext("vfstest", argc, (const char **) argv,
+ long_options, 0);
+
+ while(poptGetNextOpt(pc) != -1);
+
+
+ poptFreeContext(pc);
+
+ /* TODO: check output */
+ reload_services(False);
+
+ /* the following functions are part of the Samba debugging
+ facilities. See lib/debug.c */
+ setup_logging("vfstest", True);
+
+ /* Load command lists */
+
+ cmd_set = vfstest_command_list;
+
+ while(*cmd_set) {
+ add_command_set(*cmd_set);
+ add_command_set(separator_command);
+ cmd_set++;
+ }
+
+ /* some basic initialization stuff */
+ sec_init();
+ conn_init();
+ vfs.conn = conn_new();
+ string_set(&vfs.conn->user,"vfstest");
+ for (i=0; i < 1024; i++)
+ vfs.files[i] = NULL;
+
+ /* some advanced initiliazation stuff */
+ smbd_vfs_init(vfs.conn);
+
+ /* Do we have a file input? */
+ if (filename && filename[0]) {
+ process_file(&vfs, filename);
+ return 0;
+ }
+
+ /* Do anything specified with -c */
+ if (cmdstr && cmdstr[0]) {
+ char *cmd;
+ char *p = cmdstr;
+
+ while((cmd=next_command(&p)) != NULL) {
+ process_cmd(&vfs, cmd);
+ }
+
+ return 0;
+ }
+
+ /* Loop around accepting commands */
+
+ while(1) {
+ pstring prompt;
+ char *line;
+
+ slprintf(prompt, sizeof(prompt) - 1, "vfstest $> ");
+
+ line = smb_readline(prompt, NULL, completion_fn);
+
+ if (line == NULL)
+ break;
+
+ if (line[0] != '\n')
+ process_cmd(&vfs, line);
+ }
+
+ free(vfs.conn);
+ return 0;
+}
diff --git a/source/torture/vfstest.h b/source/torture/vfstest.h
new file mode 100644
index 00000000000..1e030fad047
--- /dev/null
+++ b/source/torture/vfstest.h
@@ -0,0 +1,45 @@
+/*
+ Unix SMB/CIFS implementation.
+ VFS module tester
+
+ Copyright (C) Simo Sorce 2002
+ Copyright (C) Eric Lorimer 2002
+
+ Most of this code was ripped off of rpcclient.
+ Copyright (C) Tim Potter 2000-2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+struct func_entry {
+ char *name;
+ int (*fn)(struct connection_struct *conn, const char *path);
+};
+
+struct vfs_state {
+ struct connection_struct *conn;
+ struct files_struct *files[1024];
+ DIR *currentdir;
+ void *data;
+ size_t data_size;
+};
+
+struct cmd_set {
+ const char *name;
+ NTSTATUS (*fn)(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc,
+ const char **argv);
+ const char *description;
+ const char *usage;
+};
diff --git a/source/ubiqx/.cvsignore b/source/ubiqx/.cvsignore
new file mode 100644
index 00000000000..07da2225c72
--- /dev/null
+++ b/source/ubiqx/.cvsignore
@@ -0,0 +1,3 @@
+*.po
+*.po32
+
diff --git a/source/ubiqx/COPYING.LIB b/source/ubiqx/COPYING.LIB
new file mode 100644
index 00000000000..8c8377da464
--- /dev/null
+++ b/source/ubiqx/COPYING.LIB
@@ -0,0 +1,481 @@
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/source/ubiqx/README.UBI b/source/ubiqx/README.UBI
new file mode 100644
index 00000000000..a2c14ca62c9
--- /dev/null
+++ b/source/ubiqx/README.UBI
@@ -0,0 +1,18 @@
+Fri Apr 17 10:21:56 CDT 1998
+
+The C code files in the samba/source/ubiqx directory are licensed under
+the terms of the GNU LIBRARY GENERAL PUBLIC LICENSE (LGPL). A copy of the
+LGPL should also be included in this directory under the name COPYING.LIB.
+If this file is not present, you can obtain a copy of the LGPL by writing
+to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
+USA.
+
+The versions of the ubiqx modules distributed with Samba may have been
+modified for inclusion with Samba. The main distribution, which contains
+additional available modules, can be found at:
+
+ http://www.interads.co.uk/~crh/ubiqx/
+
+Chris Hertel
+Samba Team
+ubiqx@ubiqx.mn.org
diff --git a/source/ubiqx/debugparse.c b/source/ubiqx/debugparse.c
new file mode 100644
index 00000000000..195fc245bc0
--- /dev/null
+++ b/source/ubiqx/debugparse.c
@@ -0,0 +1,308 @@
+/* ========================================================================== **
+ * debugparse.c
+ *
+ * Copyright (C) 1998 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ *
+ * -------------------------------------------------------------------------- **
+ * This module is a very simple parser for Samba debug log files.
+ * -------------------------------------------------------------------------- **
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ * The important function in this module is dbg_char2token(). The rest is
+ * basically fluff. (Potentially useful fluff, but still fluff.)
+ * ========================================================================== **
+ */
+
+#include "debugparse.h"
+
+/* -------------------------------------------------------------------------- **
+ * Constants...
+ *
+ * DBG_BSIZE - This internal constant is used only by dbg_test(). It is the
+ * size of the read buffer. I've tested the function using a
+ * DBG_BSIZE value of 2.
+ */
+
+#define DBG_BSIZE 128
+
+/* -------------------------------------------------------------------------- **
+ * Functions...
+ */
+
+const char *dbg_token2string( dbg_Token tok )
+ /* ------------------------------------------------------------------------ **
+ * Given a token, return a string describing the token.
+ *
+ * Input: tok - One of the set of dbg_Tokens defined in debugparse.h.
+ *
+ * Output: A string identifying the token. This is useful for debugging,
+ * etc.
+ *
+ * Note: If the token is not known, this function will return the
+ * string "<unknown>".
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ switch( tok )
+ {
+ case dbg_null:
+ return( "null" );
+ case dbg_ignore:
+ return( "ignore" );
+ case dbg_header:
+ return( "header" );
+ case dbg_timestamp:
+ return( "time stamp" );
+ case dbg_level:
+ return( "level" );
+ case dbg_sourcefile:
+ return( "source file" );
+ case dbg_function:
+ return( "function" );
+ case dbg_lineno:
+ return( "line number" );
+ case dbg_message:
+ return( "message" );
+ case dbg_eof:
+ return( "[EOF]" );
+ }
+ return( "<unknown>" );
+ } /* dbg_token2string */
+
+dbg_Token dbg_char2token( dbg_Token *state, int c )
+ /* ------------------------------------------------------------------------ **
+ * Parse input one character at a time.
+ *
+ * Input: state - A pointer to a token variable. This is used to
+ * maintain the parser state between calls. For
+ * each input stream, you should set up a separate
+ * state variable and initialize it to dbg_null.
+ * Pass a pointer to it into this function with each
+ * character in the input stream. See dbg_test()
+ * for an example.
+ * c - The "current" character in the input stream.
+ *
+ * Output: A token.
+ * The token value will change when delimiters are found,
+ * which indicate a transition between syntactical objects.
+ * Possible return values are:
+ *
+ * dbg_null - The input character was an end-of-line.
+ * This resets the parser to its initial state
+ * in preparation for parsing the next line.
+ * dbg_eof - Same as dbg_null, except that the character
+ * was an end-of-file.
+ * dbg_ignore - Returned for whitespace and delimiters.
+ * These lexical tokens are only of interest
+ * to the parser.
+ * dbg_header - Indicates the start of a header line. The
+ * input character was '[' and was the first on
+ * the line.
+ * dbg_timestamp - Indicates that the input character was part
+ * of a header timestamp.
+ * dbg_level - Indicates that the input character was part
+ * of the debug-level value in the header.
+ * dbg_sourcefile - Indicates that the input character was part
+ * of the sourcefile name in the header.
+ * dbg_function - Indicates that the input character was part
+ * of the function name in the header.
+ * dbg_lineno - Indicates that the input character was part
+ * of the DEBUG call line number in the header.
+ * dbg_message - Indicates that the input character was part
+ * of the DEBUG message text.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ /* The terminating characters that we see will greatly depend upon
+ * how they are read. For example, if gets() is used instead of
+ * fgets(), then we will not see newline characters. A lot also
+ * depends on the calling function, which may handle terminators
+ * itself.
+ *
+ * '\n', '\0', and EOF are all considered line terminators. The
+ * dbg_eof token is sent back if an EOF is encountered.
+ *
+ * Warning: only allow the '\0' character to be sent if you are
+ * using gets() to read whole lines (thus replacing '\n'
+ * with '\0'). Sending '\0' at the wrong time will mess
+ * up the parsing.
+ */
+ switch( c )
+ {
+ case EOF:
+ *state = dbg_null; /* Set state to null (initial state) so */
+ return( dbg_eof ); /* that we can restart with new input. */
+ case '\n':
+ case '\0':
+ *state = dbg_null; /* A newline or eoln resets to the null state. */
+ return( dbg_null );
+ }
+
+ /* When within the body of the message, only a line terminator
+ * can cause a change of state. We've already checked for line
+ * terminators, so if the current state is dbg_msgtxt, simply
+ * return that as our current token.
+ */
+ if( dbg_message == *state )
+ return( dbg_message );
+
+ /* If we are at the start of a new line, and the input character
+ * is an opening bracket, then the line is a header line, otherwise
+ * it's a message body line.
+ */
+ if( dbg_null == *state )
+ {
+ if( '[' == c )
+ {
+ *state = dbg_timestamp;
+ return( dbg_header );
+ }
+ *state = dbg_message;
+ return( dbg_message );
+ }
+
+ /* We've taken care of terminators, text blocks and new lines.
+ * The remaining possibilities are all within the header line
+ * itself.
+ */
+
+ /* Within the header line, whitespace can be ignored *except*
+ * within the timestamp.
+ */
+ if( isspace( c ) )
+ {
+ /* Fudge. The timestamp may contain space characters. */
+ if( (' ' == c) && (dbg_timestamp == *state) )
+ return( dbg_timestamp );
+ /* Otherwise, ignore whitespace. */
+ return( dbg_ignore );
+ }
+
+ /* Okay, at this point we know we're somewhere in the header.
+ * Valid header *states* are: dbg_timestamp, dbg_level,
+ * dbg_sourcefile, dbg_function, and dbg_lineno.
+ */
+ switch( c )
+ {
+ case ',':
+ if( dbg_timestamp == *state )
+ {
+ *state = dbg_level;
+ return( dbg_ignore );
+ }
+ break;
+ case ']':
+ if( dbg_level == *state )
+ {
+ *state = dbg_sourcefile;
+ return( dbg_ignore );
+ }
+ break;
+ case ':':
+ if( dbg_sourcefile == *state )
+ {
+ *state = dbg_function;
+ return( dbg_ignore );
+ }
+ break;
+ case '(':
+ if( dbg_function == *state )
+ {
+ *state = dbg_lineno;
+ return( dbg_ignore );
+ }
+ break;
+ case ')':
+ if( dbg_lineno == *state )
+ {
+ *state = dbg_null;
+ return( dbg_ignore );
+ }
+ break;
+ }
+
+ /* If the previous block did not result in a state change, then
+ * return the current state as the current token.
+ */
+ return( *state );
+ } /* dbg_char2token */
+
+void dbg_test( void )
+ /* ------------------------------------------------------------------------ **
+ * Simple test function.
+ *
+ * Input: none.
+ * Output: none.
+ * Notes: This function was used to test dbg_char2token(). It reads a
+ * Samba log file from stdin and prints parsing info to stdout.
+ * It also serves as a simple example.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ char bufr[DBG_BSIZE];
+ int i;
+ int linecount = 1;
+ dbg_Token old = dbg_null,
+ new = dbg_null,
+ state = dbg_null;
+
+ while( fgets( bufr, DBG_BSIZE, stdin ) )
+ {
+ for( i = 0; bufr[i]; i++ )
+ {
+ old = new;
+ new = dbg_char2token( &state, bufr[i] );
+ switch( new )
+ {
+ case dbg_header:
+ if( linecount > 1 )
+ (void)putchar( '\n' );
+ break;
+ case dbg_null:
+ linecount++;
+ break;
+ case dbg_ignore:
+ break;
+ default:
+ if( old != new )
+ (void)printf( "\n[%05d]%12s: ", linecount, dbg_token2string(new) );
+ (void)putchar( bufr[i] );
+ }
+ }
+ }
+ (void)putchar( '\n' );
+ } /* dbg_test */
+
+
+/* -------------------------------------------------------------------------- **
+ * This simple main line can be uncommented and used to test the parser.
+ */
+
+/*
+ * int main( void )
+ * {
+ * dbg_test();
+ * return( 0 );
+ * }
+ */
+
+/* ========================================================================== */
diff --git a/source/ubiqx/debugparse.h b/source/ubiqx/debugparse.h
new file mode 100644
index 00000000000..458eee74558
--- /dev/null
+++ b/source/ubiqx/debugparse.h
@@ -0,0 +1,127 @@
+#ifndef DEBUGPARSE_H
+#define DEBUGPARSE_H
+/* ========================================================================== **
+ * debugparse.c
+ *
+ * Copyright (C) 1998 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ *
+ * -------------------------------------------------------------------------- **
+ * This module is a very simple parser for Samba debug log files.
+ * -------------------------------------------------------------------------- **
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ * The important function in this module is dbg_char2token(). The rest is
+ * basically fluff. (Potentially useful fluff, but still fluff.)
+ * ========================================================================== **
+ */
+
+#include "sys_include.h"
+
+/* This module compiles quite nicely outside of the Samba environment.
+ * You'll need the following headers:
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+ */
+
+/* -------------------------------------------------------------------------- **
+ * These are the tokens returned by dbg_char2token().
+ */
+
+typedef enum
+ {
+ dbg_null = 0,
+ dbg_ignore,
+ dbg_header,
+ dbg_timestamp,
+ dbg_level,
+ dbg_sourcefile,
+ dbg_function,
+ dbg_lineno,
+ dbg_message,
+ dbg_eof
+ } dbg_Token;
+
+/* -------------------------------------------------------------------------- **
+ * Function prototypes...
+ */
+
+ const char *dbg_token2string( dbg_Token tok );
+ /* ------------------------------------------------------------------------ **
+ * Given a token, return a string describing the token.
+ *
+ * Input: tok - One of the set of dbg_Tokens defined in debugparse.h.
+ *
+ * Output: A string identifying the token. This is useful for debugging,
+ * etc.
+ *
+ * Note: If the token is not known, this function will return the
+ * string "<unknown>".
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+ dbg_Token dbg_char2token( dbg_Token *state, int c );
+ /* ------------------------------------------------------------------------ **
+ * Parse input one character at a time.
+ *
+ * Input: state - A pointer to a token variable. This is used to
+ * maintain the parser state between calls. For
+ * each input stream, you should set up a separate
+ * state variable and initialize it to dbg_null.
+ * Pass a pointer to it into this function with each
+ * character in the input stream. See dbg_test()
+ * for an example.
+ * c - The "current" character in the input stream.
+ *
+ * Output: A token.
+ * The token value will change when delimiters are found,
+ * which indicate a transition between syntactical objects.
+ * Possible return values are:
+ *
+ * dbg_null - The input character was an end-of-line.
+ * This resets the parser to its initial state
+ * in preparation for parsing the next line.
+ * dbg_eof - Same as dbg_null, except that the character
+ * was an end-of-file.
+ * dbg_ignore - Returned for whitespace and delimiters.
+ * These lexical tokens are only of interest
+ * to the parser.
+ * dbg_header - Indicates the start of a header line. The
+ * input character was '[' and was the first on
+ * the line.
+ * dbg_timestamp - Indicates that the input character was part
+ * of a header timestamp.
+ * dbg_level - Indicates that the input character was part
+ * of the debug-level value in the header.
+ * dbg_sourcefile - Indicates that the input character was part
+ * of the sourcefile name in the header.
+ * dbg_function - Indicates that the input character was part
+ * of the function name in the header.
+ * dbg_lineno - Indicates that the input character was part
+ * of the DEBUG call line number in the header.
+ * dbg_message - Indicates that the input character was part
+ * of the DEBUG message text.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+
+/* -------------------------------------------------------------------------- */
+#endif /* DEBUGPARSE_H */
diff --git a/source/ubiqx/sys_include.h b/source/ubiqx/sys_include.h
new file mode 100644
index 00000000000..8ff270afe85
--- /dev/null
+++ b/source/ubiqx/sys_include.h
@@ -0,0 +1,52 @@
+#ifndef SYS_INCLUDE_H
+#define SYS_INCLUDE_H
+/* ========================================================================== **
+ * sys_include.h
+ *
+ * Copyright (C) 1998 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ * -------------------------------------------------------------------------- **
+ * This header provides system declarations and data types used internally
+ * by the ubiqx modules.
+ * -------------------------------------------------------------------------- **
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * Samba version of sys_include.h
+ *
+ * ========================================================================== **
+ */
+
+#ifndef _INCLUDES_H
+
+/* Block the inclusion of some Samba headers so that ubiqx types won't be
+ * used before the headers that define them. These headers are not needed
+ * in the ubiqx modules anyway.
+ */
+#define _PROTO_H_
+#define _NAMESERV_H_
+#define _HASH_H_
+
+/* The main Samba system-adaptive header file.
+ */
+#include "includes.h"
+
+#endif /* _INCLUDES_H */
+
+/* ================================ The End ================================= */
+#endif /* SYS_INCLUDE_H */
diff --git a/source/ubiqx/ubi_BinTree.c b/source/ubiqx/ubi_BinTree.c
new file mode 100644
index 00000000000..8a4d4612800
--- /dev/null
+++ b/source/ubiqx/ubi_BinTree.c
@@ -0,0 +1,1132 @@
+/* ========================================================================== **
+ * ubi_BinTree.c
+ *
+ * Copyright (C) 1991-1998 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ * -------------------------------------------------------------------------- **
+ *
+ * This module implements a simple binary tree.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * Log: ubi_BinTree.c,v
+ * Revision 4.10 2000/06/06 20:38:40 crh
+ * In the ReplaceNode() function, the old node header was being copied
+ * to the new node header using a byte-by-byte copy. This was causing
+ * the 'insure' software testing program to report a memory leak. The
+ * fix was to do a simple assignement: *newnode = *oldnode;
+ * This quieted the (errant) memory leak reports and is probably a bit
+ * faster than the bytewise copy.
+ *
+ * Revision 4.9 2000/01/08 23:24:30 crh
+ * Clarified a variety of if( pointer ) lines, replacing them with
+ * if( NULL != pointer ). This is more correct, and I have heard
+ * of at least one (obscure?) system out there that uses a non-zero
+ * value for NULL.
+ * Also, speed improvement in Neighbor(). It was comparing pointers
+ * when it could have compared two gender values. The pointer
+ * comparison was somewhat indirect (does pointer equal the pointer
+ * of the parent of the node pointed to by pointer). Urq.
+ *
+ * Revision 4.8 1999/09/22 03:40:30 crh
+ * Modified ubi_btTraverse() and ubi_btKillTree(). They now return an
+ * unsigned long indicating the number of nodes processed. The change
+ * is subtle. An empty tree formerly returned False, and now returns
+ * zero.
+ *
+ * Revision 4.7 1998/10/21 06:14:42 crh
+ * Fixed bugs in FirstOf() and LastOf() reported by Massimo Campostrini.
+ * See function comments.
+ *
+ * Revision 4.6 1998/07/25 17:02:10 crh
+ * Added the ubi_trNewTree() macro.
+ *
+ * Revision 4.5 1998/06/04 21:29:27 crh
+ * Upper-cased defined constants (eg UBI_BINTREE_H) in some header files.
+ * This is more "standard", and is what people expect. Weird, eh?
+ *
+ * Revision 4.4 1998/06/03 17:42:46 crh
+ * Further fiddling with sys_include.h. It's now in ubi_BinTree.h which is
+ * included by all of the binary tree files.
+ *
+ * Reminder: Some of the ubi_tr* macros in ubi_BinTree.h are redefined in
+ * ubi_AVLtree.h and ubi_SplayTree.h. This allows easy swapping
+ * of tree types by simply changing a header. Unfortunately, the
+ * macro redefinitions in ubi_AVLtree.h and ubi_SplayTree.h will
+ * conflict if used together. You must either choose a single tree
+ * type, or use the underlying function calls directly. Compare
+ * the two header files for more information.
+ *
+ * Revision 4.3 1998/06/02 01:28:43 crh
+ * Changed ubi_null.h to sys_include.h to make it more generic.
+ *
+ * Revision 4.2 1998/05/20 04:32:36 crh
+ * The C file now includes ubi_null.h. See ubi_null.h for more info.
+ * Also, the balance and gender fields of the node were declared as
+ * signed char. As I understand it, at least one SunOS or Solaris
+ * compiler doesn't like "signed char". The declarations were
+ * wrong anyway, so I changed them to simple "char".
+ *
+ * Revision 4.1 1998/03/31 06:11:57 crh
+ * Thomas Aglassinger sent E'mail pointing out errors in the
+ * dereferencing of function pointers, and a missing typecast.
+ * Thanks, Thomas!
+ *
+ * Revision 4.0 1998/03/10 03:19:22 crh
+ * Added the AVL field 'balance' to the ubi_btNode structure. This means
+ * that all BinTree modules now use the same basic node structure, which
+ * greatly simplifies the AVL module.
+ * Decided that this was a big enough change to justify a new major revision
+ * number. 3.0 was an error, so we're at 4.0.
+ *
+ * Revision 2.6 1998/01/24 06:27:46 crh
+ * Added ubi_trCount() macro.
+ *
+ * Revision 2.5 1997/12/23 03:56:29 crh
+ * In this version, all constants & macros defined in the header file have
+ * the ubi_tr prefix. Also cleaned up anything that gcc complained about
+ * when run with '-pedantic -fsyntax-only -Wall'.
+ *
+ * Revision 2.4 1997/07/26 04:11:10 crh
+ * + Just to be annoying I changed ubi_TRUE and ubi_FALSE to ubi_trTRUE
+ * and ubi_trFALSE.
+ * + There is now a type ubi_trBool to go with ubi_trTRUE and ubi_trFALSE.
+ * + There used to be something called "ubi_TypeDefs.h". I got rid of it.
+ * + Added function ubi_btLeafNode().
+ *
+ * Revision 2.3 1997/06/03 05:16:17 crh
+ * Changed TRUE and FALSE to ubi_TRUE and ubi_FALSE to avoid conflicts.
+ * Also changed the interface to function InitTree(). See the comments
+ * for this function for more information.
+ *
+ * Revision 2.2 1995/10/03 22:00:07 CRH
+ * Ubisized!
+ *
+ * Revision 2.1 95/03/09 23:37:10 CRH
+ * Added the ModuleID static string and function. These modules are now
+ * self-identifying.
+ *
+ * Revision 2.0 95/02/27 22:00:17 CRH
+ * Revision 2.0 of this program includes the following changes:
+ *
+ * 1) A fix to a major typo in the RepaceNode() function.
+ * 2) The addition of the static function Border().
+ * 3) The addition of the public functions FirstOf() and LastOf(), which
+ * use Border(). These functions are used with trees that allow
+ * duplicate keys.
+ * 4) A complete rewrite of the Locate() function. Locate() now accepts
+ * a "comparison" operator.
+ * 5) Overall enhancements to both code and comments.
+ *
+ * I decided to give this a new major rev number because the interface has
+ * changed. In particular, there are two new functions, and changes to the
+ * Locate() function.
+ *
+ * Revision 1.0 93/10/15 22:44:59 CRH
+ * With this revision, I have added a set of #define's that provide a single,
+ * standard API to all existing tree modules. Until now, each of the three
+ * existing modules had a different function and typedef prefix, as follows:
+ *
+ * Module Prefix
+ * ubi_BinTree ubi_bt
+ * ubi_AVLtree ubi_avl
+ * ubi_SplayTree ubi_spt
+ *
+ * To further complicate matters, only those portions of the base module
+ * (ubi_BinTree) that were superceeded in the new module had the new names.
+ * For example, if you were using ubi_SplayTree, the locate function was
+ * called "ubi_sptLocate", but the next and previous functions remained
+ * "ubi_btNext" and "ubi_btPrev".
+ *
+ * This was not too terrible if you were familiar with the modules and knew
+ * exactly which tree model you wanted to use. If you wanted to be able to
+ * change modules (for speed comparisons, etc), things could get messy very
+ * quickly.
+ *
+ * So, I have added a set of defined names that get redefined in any of the
+ * descendant modules. To use this standardized interface in your code,
+ * simply replace all occurances of "ubi_bt", "ubi_avl", and "ubi_spt" with
+ * "ubi_tr". The "ubi_tr" names will resolve to the correct function or
+ * datatype names for the module that you are using. Just remember to
+ * include the header for that module in your program file. Because these
+ * names are handled by the preprocessor, there is no added run-time
+ * overhead.
+ *
+ * Note that the original names do still exist, and can be used if you wish
+ * to write code directly to a specific module. This should probably only be
+ * done if you are planning to implement a new descendant type, such as
+ * red/black trees. CRH
+ *
+ * V0.0 - June, 1991 - Written by Christopher R. Hertel (CRH).
+ *
+ * ========================================================================== **
+ */
+
+#include "ubi_BinTree.h" /* Header for this module. */
+
+/* ========================================================================== **
+ * Static data.
+ */
+
+static char ModuleID[] = "ubi_BinTree\n\
+\tRevision: 4.10 \n\
+\tDate: 2000/06/06 20:38:40 \n\
+\tAuthor: crh \n";
+
+/* ========================================================================== **
+ * Internal (private) functions.
+ */
+
+static ubi_btNodePtr qFind( ubi_btCompFunc cmp,
+ ubi_btItemPtr FindMe,
+ register ubi_btNodePtr p )
+ /* ------------------------------------------------------------------------ **
+ * This function performs a non-recursive search of a tree for a node
+ * matching a specific key. It is called "qFind()" because it is
+ * faster that TreeFind (below).
+ *
+ * Input:
+ * cmp - a pointer to the tree's comparison function.
+ * FindMe - a pointer to the key value for which to search.
+ * p - a pointer to the starting point of the search. <p>
+ * is considered to be the root of a subtree, and only
+ * the subtree will be searched.
+ *
+ * Output:
+ * A pointer to a node with a key that matches the key indicated by
+ * FindMe, or NULL if no such node was found.
+ *
+ * Note: In a tree that allows duplicates, the pointer returned *might
+ * not* point to the (sequentially) first occurance of the
+ * desired key.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int tmp;
+
+ while( (NULL != p)
+ && ((tmp = ubi_trAbNormal( (*cmp)(FindMe, p) )) != ubi_trEQUAL) )
+ p = p->Link[tmp];
+
+ return( p );
+ } /* qFind */
+
+static ubi_btNodePtr TreeFind( ubi_btItemPtr findme,
+ ubi_btNodePtr p,
+ ubi_btNodePtr *parentp,
+ char *gender,
+ ubi_btCompFunc CmpFunc )
+ /* ------------------------------------------------------------------------ **
+ * TreeFind() searches a tree for a given value (findme). It will return a
+ * pointer to the target node, if found, or NULL if the target node was not
+ * found.
+ *
+ * TreeFind() also returns, via parameters, a pointer to the parent of the
+ * target node, and a LEFT or RIGHT value indicating which child of the
+ * parent is the target node. *If the target is not found*, then these
+ * values indicate the place at which the target *should be found*. This
+ * is useful when inserting a new node into a tree or searching for nodes
+ * "near" the target node.
+ *
+ * The parameters are:
+ *
+ * findme - is a pointer to the key information to be searched for.
+ * p - points to the root of the tree to be searched.
+ * parentp - will return a pointer to a pointer to the !parent! of the
+ * target node, which can be especially usefull if the target
+ * was not found.
+ * gender - returns LEFT or RIGHT to indicate which child of *parentp
+ * was last searched.
+ * CmpFunc - points to the comparison function.
+ *
+ * This function is called by ubi_btLocate() and ubi_btInsert().
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ register ubi_btNodePtr tmp_p = p;
+ ubi_btNodePtr tmp_pp = NULL;
+ char tmp_gender = ubi_trEQUAL;
+ int tmp_cmp;
+
+ while( (NULL != tmp_p)
+ && (ubi_trEQUAL != (tmp_cmp = ubi_trAbNormal((*CmpFunc)(findme, tmp_p)))) )
+ {
+ tmp_pp = tmp_p; /* Keep track of previous node. */
+ tmp_gender = (char)tmp_cmp; /* Keep track of sex of child. */
+ tmp_p = tmp_p->Link[tmp_cmp]; /* Go to child. */
+ }
+ *parentp = tmp_pp; /* Return results. */
+ *gender = tmp_gender;
+ return( tmp_p );
+ } /* TreeFind */
+
+static void ReplaceNode( ubi_btNodePtr *parent,
+ ubi_btNodePtr oldnode,
+ ubi_btNodePtr newnode )
+ /* ------------------------------------------------------------------------ **
+ * Remove node oldnode from the tree, replacing it with node newnode.
+ *
+ * Input:
+ * parent - A pointer to he parent pointer of the node to be
+ * replaced. <parent> may point to the Link[] field of
+ * a parent node, or it may indicate the root pointer at
+ * the top of the tree.
+ * oldnode - A pointer to the node that is to be replaced.
+ * newnode - A pointer to the node that is to be installed in the
+ * place of <*oldnode>.
+ *
+ * Notes: Don't forget to free oldnode.
+ * Also, this function used to have a really nasty typo
+ * bug. "oldnode" and "newnode" were swapped in the line
+ * that now reads:
+ * ((unsigned char *)newnode)[i] = ((unsigned char *)oldnode)[i];
+ * Bleah!
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ *newnode = *oldnode; /* Copy node internals to new node. */
+
+ (*parent) = newnode; /* Old node's parent points to new child. */
+ /* Now tell the children about their new step-parent. */
+ if( oldnode->Link[ubi_trLEFT] )
+ (oldnode->Link[ubi_trLEFT])->Link[ubi_trPARENT] = newnode;
+ if( oldnode->Link[ubi_trRIGHT] )
+ (oldnode->Link[ubi_trRIGHT])->Link[ubi_trPARENT] = newnode;
+ } /* ReplaceNode */
+
+static void SwapNodes( ubi_btRootPtr RootPtr,
+ ubi_btNodePtr Node1,
+ ubi_btNodePtr Node2 )
+ /* ------------------------------------------------------------------------ **
+ * This function swaps two nodes in the tree. Node1 will take the place of
+ * Node2, and Node2 will fill in the space left vacant by Node 1.
+ *
+ * Input:
+ * RootPtr - pointer to the tree header structure for this tree.
+ * Node1 - \
+ * > These are the two nodes which are to be swapped.
+ * Node2 - /
+ *
+ * Notes:
+ * This function does a three step swap, using a dummy node as a place
+ * holder. This function is used by ubi_btRemove().
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_btNodePtr *Parent;
+ ubi_btNode dummy;
+ ubi_btNodePtr dummy_p = &dummy;
+
+ /* Replace Node 1 with the dummy, thus removing Node1 from the tree. */
+ if( NULL != Node1->Link[ubi_trPARENT] )
+ Parent = &((Node1->Link[ubi_trPARENT])->Link[(int)(Node1->gender)]);
+ else
+ Parent = &(RootPtr->root);
+ ReplaceNode( Parent, Node1, dummy_p );
+
+ /* Swap Node 1 with Node 2, placing Node 1 back into the tree. */
+ if( NULL != Node2->Link[ubi_trPARENT] )
+ Parent = &((Node2->Link[ubi_trPARENT])->Link[(int)(Node2->gender)]);
+ else
+ Parent = &(RootPtr->root);
+ ReplaceNode( Parent, Node2, Node1 );
+
+ /* Swap Node 2 and the dummy, thus placing Node 2 back into the tree. */
+ if( NULL != dummy_p->Link[ubi_trPARENT] )
+ Parent = &((dummy_p->Link[ubi_trPARENT])->Link[(int)(dummy_p->gender)]);
+ else
+ Parent = &(RootPtr->root);
+ ReplaceNode( Parent, dummy_p, Node2 );
+ } /* SwapNodes */
+
+/* -------------------------------------------------------------------------- **
+ * These routines allow you to walk through the tree, forwards or backwards.
+ */
+
+static ubi_btNodePtr SubSlide( register ubi_btNodePtr P,
+ register int whichway )
+ /* ------------------------------------------------------------------------ **
+ * Slide down the side of a subtree.
+ *
+ * Given a starting node, this function returns a pointer to the LEFT-, or
+ * RIGHT-most descendent, *or* (if whichway is PARENT) to the tree root.
+ *
+ * Input: P - a pointer to a starting place.
+ * whichway - the direction (LEFT, RIGHT, or PARENT) in which to
+ * travel.
+ * Output: A pointer to a node that is either the root, or has no
+ * whichway-th child but is within the subtree of P. Note that
+ * the return value may be the same as P. The return value *will
+ * be* NULL if P is NULL.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+
+ if( NULL != P )
+ while( NULL != P->Link[ whichway ] )
+ P = P->Link[ whichway ];
+ return( P );
+ } /* SubSlide */
+
+static ubi_btNodePtr Neighbor( register ubi_btNodePtr P,
+ register int whichway )
+ /* ------------------------------------------------------------------------ **
+ * Given starting point p, return the (key order) next or preceeding node
+ * in the tree.
+ *
+ * Input: P - Pointer to our starting place node.
+ * whichway - the direction in which to travel to find the
+ * neighbor, i.e., the RIGHT neighbor or the LEFT
+ * neighbor.
+ *
+ * Output: A pointer to the neighboring node, or NULL if P was NULL.
+ *
+ * Notes: If whichway is PARENT, the results are unpredictable.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ if( P )
+ {
+ if( NULL != P->Link[ whichway ] )
+ return( SubSlide( P->Link[ whichway ], (char)ubi_trRevWay(whichway) ) );
+ else
+ while( NULL != P->Link[ ubi_trPARENT ] )
+ {
+ if( whichway == P->gender )
+ P = P->Link[ ubi_trPARENT ];
+ else
+ return( P->Link[ ubi_trPARENT ] );
+ }
+ }
+ return( NULL );
+ } /* Neighbor */
+
+static ubi_btNodePtr Border( ubi_btRootPtr RootPtr,
+ ubi_btItemPtr FindMe,
+ ubi_btNodePtr p,
+ int whichway )
+ /* ------------------------------------------------------------------------ **
+ * Given starting point p, which has a key value equal to *FindMe, locate
+ * the first (index order) node with the same key value.
+ *
+ * This function is useful in trees that have can have duplicate keys.
+ * For example, consider the following tree:
+ * Tree Traversal
+ * 2 If <p> points to the root and <whichway> is RIGHT, 3
+ * / \ then the return value will be a pointer to the / \
+ * 2 2 RIGHT child of the root node. The tree on 2 5
+ * / / \ the right shows the order of traversal. / / \
+ * 1 2 3 1 4 6
+ *
+ * Input: RootPtr - Pointer to the tree root structure.
+ * FindMe - Key value for comparisons.
+ * p - Pointer to the starting-point node.
+ * whichway - the direction in which to travel to find the
+ * neighbor, i.e., the RIGHT neighbor or the LEFT
+ * neighbor.
+ *
+ * Output: A pointer to the first (index, or "traversal", order) node with
+ * a Key value that matches *FindMe.
+ *
+ * Notes: If whichway is PARENT, or if the tree does not allow duplicate
+ * keys, this function will return <p>.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ register ubi_btNodePtr q;
+
+ /* Exit if there's nothing that can be done. */
+ if( !ubi_trDups_OK( RootPtr ) || (ubi_trPARENT == whichway) )
+ return( p );
+
+ /* First, if needed, move up the tree. We need to get to the root of the
+ * subtree that contains all of the matching nodes.
+ */
+ q = p->Link[ubi_trPARENT];
+ while( (NULL != q)
+ && (ubi_trEQUAL == ubi_trAbNormal( (*(RootPtr->cmp))(FindMe, q) )) )
+ {
+ p = q;
+ q = p->Link[ubi_trPARENT];
+ }
+
+ /* Next, move back down in the "whichway" direction. */
+ q = p->Link[whichway];
+ while( NULL != q )
+ {
+ q = qFind( RootPtr->cmp, FindMe, q );
+ if( q )
+ {
+ p = q;
+ q = p->Link[whichway];
+ }
+ }
+ return( p );
+ } /* Border */
+
+
+/* ========================================================================== **
+ * Exported utilities.
+ */
+
+long ubi_btSgn( register long x )
+ /* ------------------------------------------------------------------------ **
+ * Return the sign of x; {negative,zero,positive} ==> {-1, 0, 1}.
+ *
+ * Input: x - a signed long integer value.
+ *
+ * Output: the "sign" of x, represented as follows:
+ * -1 == negative
+ * 0 == zero (no sign)
+ * 1 == positive
+ *
+ * Note: This utility is provided in order to facilitate the conversion
+ * of C comparison function return values into BinTree direction
+ * values: {LEFT, PARENT, EQUAL}. It is INCORPORATED into the
+ * ubi_trAbNormal() conversion macro!
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ return( (x)?((x>0)?(1):(-1)):(0) );
+ } /* ubi_btSgn */
+
+ubi_btNodePtr ubi_btInitNode( ubi_btNodePtr NodePtr )
+ /* ------------------------------------------------------------------------ **
+ * Initialize a tree node.
+ *
+ * Input: a pointer to a ubi_btNode structure to be initialized.
+ * Output: a pointer to the initialized ubi_btNode structure (ie. the
+ * same as the input pointer).
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ NodePtr->Link[ ubi_trLEFT ] = NULL;
+ NodePtr->Link[ ubi_trPARENT ] = NULL;
+ NodePtr->Link[ ubi_trRIGHT ] = NULL;
+ NodePtr->gender = ubi_trEQUAL;
+ NodePtr->balance = ubi_trEQUAL;
+ return( NodePtr );
+ } /* ubi_btInitNode */
+
+ubi_btRootPtr ubi_btInitTree( ubi_btRootPtr RootPtr,
+ ubi_btCompFunc CompFunc,
+ char Flags )
+ /* ------------------------------------------------------------------------ **
+ * Initialize the fields of a Tree Root header structure.
+ *
+ * Input: RootPtr - a pointer to an ubi_btRoot structure to be
+ * initialized.
+ * CompFunc - a pointer to a comparison function that will be used
+ * whenever nodes in the tree must be compared against
+ * outside values.
+ * Flags - One bytes worth of flags. Flags include
+ * ubi_trOVERWRITE and ubi_trDUPKEY. See the header
+ * file for more info.
+ *
+ * Output: a pointer to the initialized ubi_btRoot structure (ie. the
+ * same value as RootPtr).
+ *
+ * Note: The interface to this function has changed from that of
+ * previous versions. The <Flags> parameter replaces two
+ * boolean parameters that had the same basic effect.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ if( RootPtr )
+ {
+ RootPtr->root = NULL;
+ RootPtr->count = 0L;
+ RootPtr->cmp = CompFunc;
+ RootPtr->flags = (Flags & ubi_trDUPKEY) ? ubi_trDUPKEY : Flags;
+ } /* There are only two supported flags, and they are
+ * mutually exclusive. ubi_trDUPKEY takes precedence
+ * over ubi_trOVERWRITE.
+ */
+ return( RootPtr );
+ } /* ubi_btInitTree */
+
+ubi_trBool ubi_btInsert( ubi_btRootPtr RootPtr,
+ ubi_btNodePtr NewNode,
+ ubi_btItemPtr ItemPtr,
+ ubi_btNodePtr *OldNode )
+ /* ------------------------------------------------------------------------ **
+ * This function uses a non-recursive algorithm to add a new element to the
+ * tree.
+ *
+ * Input: RootPtr - a pointer to the ubi_btRoot structure that indicates
+ * the root of the tree to which NewNode is to be added.
+ * NewNode - a pointer to an ubi_btNode structure that is NOT
+ * part of any tree.
+ * ItemPtr - A pointer to the sort key that is stored within
+ * *NewNode. ItemPtr MUST point to information stored
+ * in *NewNode or an EXACT DUPLICATE. The key data
+ * indicated by ItemPtr is used to place the new node
+ * into the tree.
+ * OldNode - a pointer to an ubi_btNodePtr. When searching
+ * the tree, a duplicate node may be found. If
+ * duplicates are allowed, then the new node will
+ * be simply placed into the tree. If duplicates
+ * are not allowed, however, then one of two things
+ * may happen.
+ * 1) if overwritting *is not* allowed, this
+ * function will return FALSE (indicating that
+ * the new node could not be inserted), and
+ * *OldNode will point to the duplicate that is
+ * still in the tree.
+ * 2) if overwritting *is* allowed, then this
+ * function will swap **OldNode for *NewNode.
+ * In this case, *OldNode will point to the node
+ * that was removed (thus allowing you to free
+ * the node).
+ * ** If you are using overwrite mode, ALWAYS **
+ * ** check the return value of this parameter! **
+ * Note: You may pass NULL in this parameter, the
+ * function knows how to cope. If you do this,
+ * however, there will be no way to return a
+ * pointer to an old (ie. replaced) node (which is
+ * a problem if you are using overwrite mode).
+ *
+ * Output: a boolean value indicating success or failure. The function
+ * will return FALSE if the node could not be added to the tree.
+ * Such failure will only occur if duplicates are not allowed,
+ * nodes cannot be overwritten, AND a duplicate key was found
+ * within the tree.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_btNodePtr OtherP,
+ parent = NULL;
+ char tmp;
+
+ if( NULL == OldNode ) /* If they didn't give us a pointer, supply our own. */
+ OldNode = &OtherP;
+
+ (void)ubi_btInitNode( NewNode ); /* Init the new node's BinTree fields. */
+
+ /* Find a place for the new node. */
+ *OldNode = TreeFind(ItemPtr, (RootPtr->root), &parent, &tmp, (RootPtr->cmp));
+
+ /* Now add the node to the tree... */
+ if( NULL == (*OldNode) ) /* The easy one: we have a space for a new node! */
+ {
+ if( NULL == parent )
+ RootPtr->root = NewNode;
+ else
+ {
+ parent->Link[(int)tmp] = NewNode;
+ NewNode->Link[ubi_trPARENT] = parent;
+ NewNode->gender = tmp;
+ }
+ (RootPtr->count)++;
+ return( ubi_trTRUE );
+ }
+
+ /* If we reach this point, we know that a duplicate node exists. This
+ * section adds the node to the tree if duplicate keys are allowed.
+ */
+ if( ubi_trDups_OK(RootPtr) ) /* Key exists, add duplicate */
+ {
+ ubi_btNodePtr q;
+
+ tmp = ubi_trRIGHT;
+ q = (*OldNode);
+ *OldNode = NULL;
+ while( NULL != q )
+ {
+ parent = q;
+ if( tmp == ubi_trEQUAL )
+ tmp = ubi_trRIGHT;
+ q = q->Link[(int)tmp];
+ if ( q )
+ tmp = ubi_trAbNormal( (*(RootPtr->cmp))(ItemPtr, q) );
+ }
+ parent->Link[(int)tmp] = NewNode;
+ NewNode->Link[ubi_trPARENT] = parent;
+ NewNode->gender = tmp;
+ (RootPtr->count)++;
+ return( ubi_trTRUE );
+ }
+
+ /* If we get to *this* point, we know that we are not allowed to have
+ * duplicate nodes, but our node keys match, so... may we replace the
+ * old one?
+ */
+ if( ubi_trOvwt_OK(RootPtr) ) /* Key exists, we replace */
+ {
+ if( NULL == parent )
+ ReplaceNode( &(RootPtr->root), *OldNode, NewNode );
+ else
+ ReplaceNode( &(parent->Link[(int)((*OldNode)->gender)]),
+ *OldNode, NewNode );
+ return( ubi_trTRUE );
+ }
+
+ return( ubi_trFALSE ); /* Failure: could not replace an existing node. */
+ } /* ubi_btInsert */
+
+ubi_btNodePtr ubi_btRemove( ubi_btRootPtr RootPtr,
+ ubi_btNodePtr DeadNode )
+ /* ------------------------------------------------------------------------ **
+ * This function removes the indicated node from the tree.
+ *
+ * Input: RootPtr - A pointer to the header of the tree that contains
+ * the node to be removed.
+ * DeadNode - A pointer to the node that will be removed.
+ *
+ * Output: This function returns a pointer to the node that was removed
+ * from the tree (ie. the same as DeadNode).
+ *
+ * Note: The node MUST be in the tree indicated by RootPtr. If not,
+ * strange and evil things will happen to your trees.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_btNodePtr p,
+ *parentp;
+ int tmp;
+
+ /* if the node has both left and right subtrees, then we have to swap
+ * it with another node. The other node we choose will be the Prev()ious
+ * node, which is garunteed to have no RIGHT child.
+ */
+ if( (NULL != DeadNode->Link[ubi_trLEFT])
+ && (NULL != DeadNode->Link[ubi_trRIGHT]) )
+ SwapNodes( RootPtr, DeadNode, ubi_btPrev( DeadNode ) );
+
+ /* The parent of the node to be deleted may be another node, or it may be
+ * the root of the tree. Since we're not sure, it's best just to have
+ * a pointer to the parent pointer, whatever it is.
+ */
+ if( NULL == DeadNode->Link[ubi_trPARENT] )
+ parentp = &( RootPtr->root );
+ else
+ parentp = &((DeadNode->Link[ubi_trPARENT])->Link[(int)(DeadNode->gender)]);
+
+ /* Now link the parent to the only grand-child and patch up the gender. */
+ tmp = ((DeadNode->Link[ubi_trLEFT])?ubi_trLEFT:ubi_trRIGHT);
+
+ p = (DeadNode->Link[tmp]);
+ if( NULL != p )
+ {
+ p->Link[ubi_trPARENT] = DeadNode->Link[ubi_trPARENT];
+ p->gender = DeadNode->gender;
+ }
+ (*parentp) = p;
+
+ /* Finished, reduce the node count and return. */
+ (RootPtr->count)--;
+ return( DeadNode );
+ } /* ubi_btRemove */
+
+ubi_btNodePtr ubi_btLocate( ubi_btRootPtr RootPtr,
+ ubi_btItemPtr FindMe,
+ ubi_trCompOps CompOp )
+ /* ------------------------------------------------------------------------ **
+ * The purpose of ubi_btLocate() is to find a node or set of nodes given
+ * a target value and a "comparison operator". The Locate() function is
+ * more flexible and (in the case of trees that may contain dupicate keys)
+ * more precise than the ubi_btFind() function. The latter is faster,
+ * but it only searches for exact matches and, if the tree contains
+ * duplicates, Find() may return a pointer to any one of the duplicate-
+ * keyed records.
+ *
+ * Input:
+ * RootPtr - A pointer to the header of the tree to be searched.
+ * FindMe - An ubi_btItemPtr that indicates the key for which to
+ * search.
+ * CompOp - One of the following:
+ * CompOp Return a pointer to the node with
+ * ------ ---------------------------------
+ * ubi_trLT - the last key value that is less
+ * than FindMe.
+ * ubi_trLE - the first key matching FindMe, or
+ * the last key that is less than
+ * FindMe.
+ * ubi_trEQ - the first key matching FindMe.
+ * ubi_trGE - the first key matching FindMe, or the
+ * first key greater than FindMe.
+ * ubi_trGT - the first key greater than FindMe.
+ * Output:
+ * A pointer to the node matching the criteria listed above under
+ * CompOp, or NULL if no node matched the criteria.
+ *
+ * Notes:
+ * In the case of trees with duplicate keys, Locate() will behave as
+ * follows:
+ *
+ * Find: 3 Find: 3
+ * Keys: 1 2 2 2 3 3 3 3 3 4 4 Keys: 1 1 2 2 2 4 4 5 5 5 6
+ * ^ ^ ^ ^ ^
+ * LT EQ GT LE GE
+ *
+ * That is, when returning a pointer to a node with a key that is LESS
+ * THAN the target key (FindMe), Locate() will return a pointer to the
+ * LAST matching node.
+ * When returning a pointer to a node with a key that is GREATER
+ * THAN the target key (FindMe), Locate() will return a pointer to the
+ * FIRST matching node.
+ *
+ * See Also: ubi_btFind(), ubi_btFirstOf(), ubi_btLastOf().
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ register ubi_btNodePtr p;
+ ubi_btNodePtr parent;
+ char whichkid;
+
+ /* Start by searching for a matching node. */
+ p = TreeFind( FindMe,
+ RootPtr->root,
+ &parent,
+ &whichkid,
+ RootPtr->cmp );
+
+ if( NULL != p ) /* If we have found a match, we can resolve as follows: */
+ {
+ switch( CompOp )
+ {
+ case ubi_trLT: /* It's just a jump to the left... */
+ p = Border( RootPtr, FindMe, p, ubi_trLEFT );
+ return( Neighbor( p, ubi_trLEFT ) );
+ case ubi_trGT: /* ...and then a jump to the right. */
+ p = Border( RootPtr, FindMe, p, ubi_trRIGHT );
+ return( Neighbor( p, ubi_trRIGHT ) );
+ default:
+ p = Border( RootPtr, FindMe, p, ubi_trLEFT );
+ return( p );
+ }
+ }
+
+ /* Else, no match. */
+ if( ubi_trEQ == CompOp ) /* If we were looking for an exact match... */
+ return( NULL ); /* ...forget it. */
+
+ /* We can still return a valid result for GT, GE, LE, and LT.
+ * <parent> points to a node with a value that is either just before or
+ * just after the target value.
+ * Remaining possibilities are LT and GT (including LE & GE).
+ */
+ if( (ubi_trLT == CompOp) || (ubi_trLE == CompOp) )
+ return( (ubi_trLEFT == whichkid) ? Neighbor( parent, whichkid ) : parent );
+ else
+ return( (ubi_trRIGHT == whichkid) ? Neighbor( parent, whichkid ) : parent );
+ } /* ubi_btLocate */
+
+ubi_btNodePtr ubi_btFind( ubi_btRootPtr RootPtr,
+ ubi_btItemPtr FindMe )
+ /* ------------------------------------------------------------------------ **
+ * This function performs a non-recursive search of a tree for any node
+ * matching a specific key.
+ *
+ * Input:
+ * RootPtr - a pointer to the header of the tree to be searched.
+ * FindMe - a pointer to the key value for which to search.
+ *
+ * Output:
+ * A pointer to a node with a key that matches the key indicated by
+ * FindMe, or NULL if no such node was found.
+ *
+ * Note: In a tree that allows duplicates, the pointer returned *might
+ * not* point to the (sequentially) first occurance of the
+ * desired key. In such a tree, it may be more useful to use
+ * ubi_btLocate().
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ return( qFind( RootPtr->cmp, FindMe, RootPtr->root ) );
+ } /* ubi_btFind */
+
+ubi_btNodePtr ubi_btNext( ubi_btNodePtr P )
+ /* ------------------------------------------------------------------------ **
+ * Given the node indicated by P, find the (sorted order) Next node in the
+ * tree.
+ * Input: P - a pointer to a node that exists in a binary tree.
+ * Output: A pointer to the "next" node in the tree, or NULL if P pointed
+ * to the "last" node in the tree or was NULL.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ return( Neighbor( P, ubi_trRIGHT ) );
+ } /* ubi_btNext */
+
+ubi_btNodePtr ubi_btPrev( ubi_btNodePtr P )
+ /* ------------------------------------------------------------------------ **
+ * Given the node indicated by P, find the (sorted order) Previous node in
+ * the tree.
+ * Input: P - a pointer to a node that exists in a binary tree.
+ * Output: A pointer to the "previous" node in the tree, or NULL if P
+ * pointed to the "first" node in the tree or was NULL.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ return( Neighbor( P, ubi_trLEFT ) );
+ } /* ubi_btPrev */
+
+ubi_btNodePtr ubi_btFirst( ubi_btNodePtr P )
+ /* ------------------------------------------------------------------------ **
+ * Given the node indicated by P, find the (sorted order) First node in the
+ * subtree of which *P is the root.
+ * Input: P - a pointer to a node that exists in a binary tree.
+ * Output: A pointer to the "first" node in a subtree that has *P as its
+ * root. This function will return NULL only if P is NULL.
+ * Note: In general, you will be passing in the value of the root field
+ * of an ubi_btRoot structure.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ return( SubSlide( P, ubi_trLEFT ) );
+ } /* ubi_btFirst */
+
+ubi_btNodePtr ubi_btLast( ubi_btNodePtr P )
+ /* ------------------------------------------------------------------------ **
+ * Given the node indicated by P, find the (sorted order) Last node in the
+ * subtree of which *P is the root.
+ * Input: P - a pointer to a node that exists in a binary tree.
+ * Output: A pointer to the "last" node in a subtree that has *P as its
+ * root. This function will return NULL only if P is NULL.
+ * Note: In general, you will be passing in the value of the root field
+ * of an ubi_btRoot structure.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ return( SubSlide( P, ubi_trRIGHT ) );
+ } /* ubi_btLast */
+
+ubi_btNodePtr ubi_btFirstOf( ubi_btRootPtr RootPtr,
+ ubi_btItemPtr MatchMe,
+ ubi_btNodePtr p )
+ /* ------------------------------------------------------------------------ **
+ * Given a tree that a allows duplicate keys, and a pointer to a node in
+ * the tree, this function will return a pointer to the first (traversal
+ * order) node with the same key value.
+ *
+ * Input: RootPtr - A pointer to the root of the tree.
+ * MatchMe - A pointer to the key value. This should probably
+ * point to the key within node *p.
+ * p - A pointer to a node in the tree.
+ * Output: A pointer to the first node in the set of nodes with keys
+ * matching <FindMe>.
+ * Notes: Node *p MUST be in the set of nodes with keys matching
+ * <FindMe>. If not, this function will return NULL.
+ *
+ * 4.7: Bug found & fixed by Massimo Campostrini,
+ * Istituto Nazionale di Fisica Nucleare, Sezione di Pisa.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ /* If our starting point is invalid, return NULL. */
+ if( (NULL == p)
+ || (ubi_trEQUAL != ubi_trAbNormal( (*(RootPtr->cmp))( MatchMe, p ) )) )
+ return( NULL );
+ return( Border( RootPtr, MatchMe, p, ubi_trLEFT ) );
+ } /* ubi_btFirstOf */
+
+ubi_btNodePtr ubi_btLastOf( ubi_btRootPtr RootPtr,
+ ubi_btItemPtr MatchMe,
+ ubi_btNodePtr p )
+ /* ------------------------------------------------------------------------ **
+ * Given a tree that a allows duplicate keys, and a pointer to a node in
+ * the tree, this function will return a pointer to the last (traversal
+ * order) node with the same key value.
+ *
+ * Input: RootPtr - A pointer to the root of the tree.
+ * MatchMe - A pointer to the key value. This should probably
+ * point to the key within node *p.
+ * p - A pointer to a node in the tree.
+ * Output: A pointer to the last node in the set of nodes with keys
+ * matching <FindMe>.
+ * Notes: Node *p MUST be in the set of nodes with keys matching
+ * <FindMe>. If not, this function will return NULL.
+ *
+ * 4.7: Bug found & fixed by Massimo Campostrini,
+ * Istituto Nazionale di Fisica Nucleare, Sezione di Pisa.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ /* If our starting point is invalid, return NULL. */
+ if( (NULL != p)
+ || (ubi_trEQUAL != ubi_trAbNormal( (*(RootPtr->cmp))( MatchMe, p ) )) )
+ return( NULL );
+ return( Border( RootPtr, MatchMe, p, ubi_trRIGHT ) );
+ } /* ubi_btLastOf */
+
+unsigned long ubi_btTraverse( ubi_btRootPtr RootPtr,
+ ubi_btActionRtn EachNode,
+ void *UserData )
+ /* ------------------------------------------------------------------------ **
+ * Traverse a tree in sorted order (non-recursively). At each node, call
+ * (*EachNode)(), passing a pointer to the current node, and UserData as the
+ * second parameter.
+ *
+ * Input: RootPtr - a pointer to an ubi_btRoot structure that indicates
+ * the tree to be traversed.
+ * EachNode - a pointer to a function to be called at each node
+ * as the node is visited.
+ * UserData - a generic pointer that may point to anything that
+ * you choose.
+ *
+ * Output: A count of the number of nodes visited. This will be zero
+ * if the tree is empty.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_btNodePtr p = ubi_btFirst( RootPtr->root );
+ unsigned long count = 0;
+
+ while( NULL != p )
+ {
+ (*EachNode)( p, UserData );
+ count++;
+ p = ubi_btNext( p );
+ }
+ return( count );
+ } /* ubi_btTraverse */
+
+unsigned long ubi_btKillTree( ubi_btRootPtr RootPtr,
+ ubi_btKillNodeRtn FreeNode )
+ /* ------------------------------------------------------------------------ **
+ * Delete an entire tree (non-recursively) and reinitialize the ubi_btRoot
+ * structure. Return a count of the number of nodes deleted.
+ *
+ * Input: RootPtr - a pointer to an ubi_btRoot structure that indicates
+ * the root of the tree to delete.
+ * FreeNode - a function that will be called for each node in the
+ * tree to deallocate the memory used by the node.
+ *
+ * Output: The number of nodes removed from the tree.
+ * A value of 0 will be returned if:
+ * - The tree actually contains 0 entries.
+ * - the value of <RootPtr> is NULL, in which case the tree is
+ * assumed to be empty
+ * - the value of <FreeNode> is NULL, in which case entries
+ * cannot be removed, so 0 is returned. *Make sure that you
+ * provide a valid value for <FreeNode>*.
+ * In all other cases, you should get a positive value equal to
+ * the value of RootPtr->count upon entry.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_btNodePtr p, q;
+ unsigned long count = 0;
+
+ if( (NULL == RootPtr) || (NULL == FreeNode) )
+ return( 0 );
+
+ p = ubi_btFirst( RootPtr->root );
+ while( NULL != p )
+ {
+ q = p;
+ while( q->Link[ubi_trRIGHT] )
+ q = SubSlide( q->Link[ubi_trRIGHT], ubi_trLEFT );
+ p = q->Link[ubi_trPARENT];
+ if( NULL != p )
+ p->Link[ ((p->Link[ubi_trLEFT] == q)?ubi_trLEFT:ubi_trRIGHT) ] = NULL;
+ (*FreeNode)((void *)q);
+ count++;
+ }
+
+ /* overkill... */
+ (void)ubi_btInitTree( RootPtr,
+ RootPtr->cmp,
+ RootPtr->flags );
+ return( count );
+ } /* ubi_btKillTree */
+
+ubi_btNodePtr ubi_btLeafNode( ubi_btNodePtr leader )
+ /* ------------------------------------------------------------------------ **
+ * Returns a pointer to a leaf node.
+ *
+ * Input: leader - Pointer to a node at which to start the descent.
+ *
+ * Output: A pointer to a leaf node selected in a somewhat arbitrary
+ * manner.
+ *
+ * Notes: I wrote this function because I was using splay trees as a
+ * database cache. The cache had a maximum size on it, and I
+ * needed a way of choosing a node to sacrifice if the cache
+ * became full. In a splay tree, less recently accessed nodes
+ * tend toward the bottom of the tree, meaning that leaf nodes
+ * are good candidates for removal. (I really can't think of
+ * any other reason to use this function.)
+ * + In a simple binary tree or an AVL tree, the most recently
+ * added nodes tend to be nearer the bottom, making this a *bad*
+ * way to choose which node to remove from the cache.
+ * + Randomizing the traversal order is probably a good idea. You
+ * can improve the randomization of leaf node selection by passing
+ * in pointers to nodes other than the root node each time. A
+ * pointer to any node in the tree will do. Of course, if you
+ * pass a pointer to a leaf node you'll get the same thing back.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_btNodePtr follower = NULL;
+ int whichway = ubi_trLEFT;
+
+ while( NULL != leader )
+ {
+ follower = leader;
+ leader = follower->Link[ whichway ];
+ if( NULL == leader )
+ {
+ whichway = ubi_trRevWay( whichway );
+ leader = follower->Link[ whichway ];
+ }
+ }
+
+ return( follower );
+ } /* ubi_btLeafNode */
+
+int ubi_btModuleID( int size, char *list[] )
+ /* ------------------------------------------------------------------------ **
+ * Returns a set of strings that identify the module.
+ *
+ * Input: size - The number of elements in the array <list>.
+ * list - An array of pointers of type (char *). This array
+ * should, initially, be empty. This function will fill
+ * in the array with pointers to strings.
+ * Output: The number of elements of <list> that were used. If this value
+ * is less than <size>, the values of the remaining elements are
+ * not guaranteed.
+ *
+ * Notes: Please keep in mind that the pointers returned indicate strings
+ * stored in static memory. Don't free() them, don't write over
+ * them, etc. Just read them.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ if( size > 0 )
+ {
+ list[0] = ModuleID;
+ if( size > 1 )
+ list[1] = NULL;
+ return( 1 );
+ }
+ return( 0 );
+ } /* ubi_btModuleID */
+
+
+/* ========================================================================== */
diff --git a/source/ubiqx/ubi_BinTree.h b/source/ubiqx/ubi_BinTree.h
new file mode 100644
index 00000000000..c0c6d593094
--- /dev/null
+++ b/source/ubiqx/ubi_BinTree.h
@@ -0,0 +1,864 @@
+#ifndef UBI_BINTREE_H
+#define UBI_BINTREE_H
+/* ========================================================================== **
+ * ubi_BinTree.h
+ *
+ * Copyright (C) 1991-1998 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ * -------------------------------------------------------------------------- **
+ *
+ * This module implements a simple binary tree.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * Log: ubi_BinTree.h,v
+ * Revision 4.10 2000/06/06 20:38:40 crh
+ * In the ReplaceNode() function, the old node header was being copied
+ * to the new node header using a byte-by-byte copy. This was causing
+ * the 'insure' software testing program to report a memory leak. The
+ * fix was to do a simple assignement: *newnode = *oldnode;
+ * This quieted the (errant) memory leak reports and is probably a bit
+ * faster than the bytewise copy.
+ *
+ * Revision 4.9 2000/01/08 23:24:30 crh
+ * Clarified a variety of if( pointer ) lines, replacing them with
+ * if( NULL != pointer ). This is more correct, and I have heard
+ * of at least one (obscure?) system out there that uses a non-zero
+ * value for NULL.
+ * Also, speed improvement in Neighbor(). It was comparing pointers
+ * when it could have compared two gender values. The pointer
+ * comparison was somewhat indirect (does pointer equal the pointer
+ * of the parent of the node pointed to by pointer). Urq.
+ *
+ * Revision 4.8 1999/09/22 03:40:30 crh
+ * Modified ubi_btTraverse() and ubi_btKillTree(). They now return an
+ * unsigned long indicating the number of nodes processed. The change
+ * is subtle. An empty tree formerly returned False, and now returns
+ * zero.
+ *
+ * Revision 4.7 1998/10/21 06:15:07 crh
+ * Fixed bugs in FirstOf() and LastOf() reported by Massimo Campostrini.
+ * See function comments.
+ *
+ * Revision 4.6 1998/07/25 17:02:10 crh
+ * Added the ubi_trNewTree() macro.
+ *
+ * Revision 4.5 1998/06/04 21:29:27 crh
+ * Upper-cased defined constants (eg UBI_BINTREE_H) in some header files.
+ * This is more "standard", and is what people expect. Weird, eh?
+ *
+ * Revision 4.4 1998/06/03 17:42:46 crh
+ * Further fiddling with sys_include.h. It's now in ubi_BinTree.h which is
+ * included by all of the binary tree files.
+ *
+ * Reminder: Some of the ubi_tr* macros in ubi_BinTree.h are redefined in
+ * ubi_AVLtree.h and ubi_SplayTree.h. This allows easy swapping
+ * of tree types by simply changing a header. Unfortunately, the
+ * macro redefinitions in ubi_AVLtree.h and ubi_SplayTree.h will
+ * conflict if used together. You must either choose a single tree
+ * type, or use the underlying function calls directly. Compare
+ * the two header files for more information.
+ *
+ * Revision 4.3 1998/06/02 01:28:43 crh
+ * Changed ubi_null.h to sys_include.h to make it more generic.
+ *
+ * Revision 4.2 1998/05/20 04:32:36 crh
+ * The C file now includes ubi_null.h. See ubi_null.h for more info.
+ * Also, the balance and gender fields of the node were declared as
+ * signed char. As I understand it, at least one SunOS or Solaris
+ * compiler doesn't like "signed char". The declarations were
+ * wrong anyway, so I changed them to simple "char".
+ *
+ * Revision 4.1 1998/03/31 06:13:47 crh
+ * Thomas Aglassinger sent E'mail pointing out errors in the
+ * dereferencing of function pointers, and a missing typecast.
+ * Thanks, Thomas!
+ *
+ * Revision 4.0 1998/03/10 03:16:04 crh
+ * Added the AVL field 'balance' to the ubi_btNode structure. This means
+ * that all BinTree modules now use the same basic node structure, which
+ * greatly simplifies the AVL module.
+ * Decided that this was a big enough change to justify a new major revision
+ * number. 3.0 was an error, so we're at 4.0.
+ *
+ * Revision 2.6 1998/01/24 06:27:30 crh
+ * Added ubi_trCount() macro.
+ *
+ * Revision 2.5 1997/12/23 03:59:21 crh
+ * In this version, all constants & macros defined in the header file have
+ * the ubi_tr prefix. Also cleaned up anything that gcc complained about
+ * when run with '-pedantic -fsyntax-only -Wall'.
+ *
+ * Revision 2.4 1997/07/26 04:11:14 crh
+ * + Just to be annoying I changed ubi_TRUE and ubi_FALSE to ubi_trTRUE
+ * and ubi_trFALSE.
+ * + There is now a type ubi_trBool to go with ubi_trTRUE and ubi_trFALSE.
+ * + There used to be something called "ubi_TypeDefs.h". I got rid of it.
+ * + Added function ubi_btLeafNode().
+ *
+ * Revision 2.3 1997/06/03 05:15:27 crh
+ * Changed TRUE and FALSE to ubi_TRUE and ubi_FALSE to avoid conflicts.
+ * Also changed the interface to function InitTree(). See the comments
+ * for this function for more information.
+ *
+ * Revision 2.2 1995/10/03 22:00:40 CRH
+ * Ubisized!
+ *
+ * Revision 2.1 95/03/09 23:43:46 CRH
+ * Added the ModuleID static string and function. These modules are now
+ * self-identifying.
+ *
+ * Revision 2.0 95/02/27 22:00:33 CRH
+ * Revision 2.0 of this program includes the following changes:
+ *
+ * 1) A fix to a major typo in the RepaceNode() function.
+ * 2) The addition of the static function Border().
+ * 3) The addition of the public functions FirstOf() and LastOf(), which
+ * use Border(). These functions are used with trees that allow
+ * duplicate keys.
+ * 4) A complete rewrite of the Locate() function. Locate() now accepts
+ * a "comparison" operator.
+ * 5) Overall enhancements to both code and comments.
+ *
+ * I decided to give this a new major rev number because the interface has
+ * changed. In particular, there are two new functions, and changes to the
+ * Locate() function.
+ *
+ * Revision 1.0 93/10/15 22:55:04 CRH
+ * With this revision, I have added a set of #define's that provide a single,
+ * standard API to all existing tree modules. Until now, each of the three
+ * existing modules had a different function and typedef prefix, as follows:
+ *
+ * Module Prefix
+ * ubi_BinTree ubi_bt
+ * ubi_AVLtree ubi_avl
+ * ubi_SplayTree ubi_spt
+ *
+ * To further complicate matters, only those portions of the base module
+ * (ubi_BinTree) that were superceeded in the new module had the new names.
+ * For example, if you were using ubi_SplayTree, the locate function was
+ * called "ubi_sptLocate", but the next and previous functions remained
+ * "ubi_btNext" and "ubi_btPrev".
+ *
+ * This was not too terrible if you were familiar with the modules and knew
+ * exactly which tree model you wanted to use. If you wanted to be able to
+ * change modules (for speed comparisons, etc), things could get messy very
+ * quickly.
+ *
+ * So, I have added a set of defined names that get redefined in any of the
+ * descendant modules. To use this standardized interface in your code,
+ * simply replace all occurances of "ubi_bt", "ubi_avl", and "ubi_spt" with
+ * "ubi_tr". The "ubi_tr" names will resolve to the correct function or
+ * datatype names for the module that you are using. Just remember to
+ * include the header for that module in your program file. Because these
+ * names are handled by the preprocessor, there is no added run-time
+ * overhead.
+ *
+ * Note that the original names do still exist, and can be used if you wish
+ * to write code directly to a specific module. This should probably only be
+ * done if you are planning to implement a new descendant type, such as
+ * red/black trees. CRH
+ *
+ * V0.0 - June, 1991 - Written by Christopher R. Hertel (CRH).
+ *
+ * ========================================================================== **
+ */
+
+#include "sys_include.h" /* Global include file, used to adapt the ubiqx
+ * modules to the host environment and the project
+ * with which the modules will be used. See
+ * sys_include.h for more info.
+ */
+
+/* -------------------------------------------------------------------------- **
+ * Macros and constants.
+ *
+ * General purpose:
+ * ubi_trTRUE - Boolean TRUE.
+ * ubi_trFALSE - Boolean FALSE.
+ *
+ * Flags used in the tree header:
+ * ubi_trOVERWRITE - This flag indicates that an existing node may be
+ * overwritten by a new node with a matching key.
+ * ubi_trDUPKEY - This flag indicates that the tree allows duplicate
+ * keys. If the tree does allow duplicates, the
+ * overwrite flag is ignored.
+ *
+ * Node link array index constants: (Each node has an array of three
+ * pointers. One to the left, one to the right, and one back to the
+ * parent.)
+ * ubi_trLEFT - Left child pointer.
+ * ubi_trPARENT - Parent pointer.
+ * ubi_trRIGHT - Right child pointer.
+ * ubi_trEQUAL - Synonym for PARENT.
+ *
+ * ubi_trCompOps: These values are used in the ubi_trLocate() function.
+ * ubi_trLT - request the first instance of the greatest key less than
+ * the search key.
+ * ubi_trLE - request the first instance of the greatest key that is less
+ * than or equal to the search key.
+ * ubi_trEQ - request the first instance of key that is equal to the
+ * search key.
+ * ubi_trGE - request the first instance of a key that is greater than
+ * or equal to the search key.
+ * ubi_trGT - request the first instance of the first key that is greater
+ * than the search key.
+ * -------------------------------------------------------------------------- **
+ */
+
+#define ubi_trTRUE 0xFF
+#define ubi_trFALSE 0x00
+
+#define ubi_trOVERWRITE 0x01 /* Turn on allow overwrite */
+#define ubi_trDUPKEY 0x02 /* Turn on allow duplicate keys */
+
+/* Pointer array index constants... */
+#define ubi_trLEFT 0x00
+#define ubi_trPARENT 0x01
+#define ubi_trRIGHT 0x02
+#define ubi_trEQUAL ubi_trPARENT
+
+typedef enum {
+ ubi_trLT = 1,
+ ubi_trLE,
+ ubi_trEQ,
+ ubi_trGE,
+ ubi_trGT
+ } ubi_trCompOps;
+
+/* -------------------------------------------------------------------------- **
+ * These three macros allow simple manipulation of pointer index values (LEFT,
+ * RIGHT, and PARENT).
+ *
+ * Normalize() - converts {LEFT, PARENT, RIGHT} into {-1, 0 ,1}. C
+ * uses {negative, zero, positive} values to indicate
+ * {less than, equal to, greater than}.
+ * AbNormal() - converts {negative, zero, positive} to {LEFT, PARENT,
+ * RIGHT} (opposite of Normalize()). Note: C comparison
+ * functions, such as strcmp(), return {negative, zero,
+ * positive} values, which are not necessarily {-1, 0,
+ * 1}. This macro uses the the ubi_btSgn() function to
+ * compensate.
+ * RevWay() - converts LEFT to RIGHT and RIGHT to LEFT. PARENT (EQUAL)
+ * is left as is.
+ * -------------------------------------------------------------------------- **
+ */
+#define ubi_trNormalize(W) ((char)( (W) - ubi_trEQUAL ))
+#define ubi_trAbNormal(W) ((char)( ((char)ubi_btSgn( (long)(W) )) \
+ + ubi_trEQUAL ))
+#define ubi_trRevWay(W) ((char)( ubi_trEQUAL - ((W) - ubi_trEQUAL) ))
+
+/* -------------------------------------------------------------------------- **
+ * These macros allow us to quickly read the values of the OVERWRITE and
+ * DUPlicate KEY bits of the tree root flags field.
+ * -------------------------------------------------------------------------- **
+ */
+#define ubi_trDups_OK(A) \
+ ((ubi_trDUPKEY & ((A)->flags))?(ubi_trTRUE):(ubi_trFALSE))
+#define ubi_trOvwt_OK(A) \
+ ((ubi_trOVERWRITE & ((A)->flags))?(ubi_trTRUE):(ubi_trFALSE))
+
+/* -------------------------------------------------------------------------- **
+ * Additional Macros...
+ *
+ * ubi_trCount() - Given a pointer to a tree root, this macro returns the
+ * number of nodes currently in the tree.
+ *
+ * ubi_trNewTree() - This macro makes it easy to declare and initialize a
+ * tree header in one step. The line
+ *
+ * static ubi_trNewTree( MyTree, cmpfn, ubi_trDUPKEY );
+ *
+ * is equivalent to
+ *
+ * static ubi_trRoot MyTree[1]
+ * = {{ NULL, cmpfn, 0, ubi_trDUPKEY }};
+ *
+ * -------------------------------------------------------------------------- **
+ */
+
+#define ubi_trCount( R ) (((ubi_trRootPtr)(R))->count)
+
+#define ubi_trNewTree( N, C, F ) ubi_trRoot (N)[1] = {{ NULL, (C), 0, (F) }}
+
+/* -------------------------------------------------------------------------- **
+ * Typedefs...
+ *
+ * ubi_trBool - Your typcial true or false...
+ *
+ * Item Pointer: The ubi_btItemPtr is a generic pointer. It is used to
+ * indicate a key that is being searched for within the tree.
+ * Searching occurs whenever the ubi_trFind(), ubi_trLocate(),
+ * or ubi_trInsert() functions are called.
+ * -------------------------------------------------------------------------- **
+ */
+
+typedef unsigned char ubi_trBool;
+
+typedef void *ubi_btItemPtr; /* A pointer to key data within a node. */
+
+/* ------------------------------------------------------------------------- **
+ * Binary Tree Node Structure: This structure defines the basic elements of
+ * the tree nodes. In general you *SHOULD NOT PLAY WITH THESE FIELDS*!
+ * But, of course, I have to put the structure into this header so that
+ * you can use it as a building block.
+ *
+ * The fields are as follows:
+ * Link - an array of pointers. These pointers are manipulated by
+ * the BT routines. The pointers indicate the left and right
+ * child nodes and the parent node. By keeping track of the
+ * parent pointer, we avoid the need for recursive routines or
+ * hand-tooled stacks to keep track of our path back to the
+ * root. The use of these pointers is subject to change without
+ * notice.
+ * gender - a one-byte field indicating whether the node is the RIGHT or
+ * LEFT child of its parent. If the node is the root of the
+ * tree, gender will be PARENT.
+ * balance - only used by the AVL tree module. This field indicates
+ * the height balance at a given node. See ubi_AVLtree for
+ * details.
+ *
+ * ------------------------------------------------------------------------- **
+ */
+typedef struct ubi_btNodeStruct {
+ struct ubi_btNodeStruct *Link[ 3 ];
+ char gender;
+ char balance;
+ } ubi_btNode;
+
+typedef ubi_btNode *ubi_btNodePtr; /* Pointer to an ubi_btNode structure. */
+
+/* ------------------------------------------------------------------------- **
+ * The next three typedefs define standard function types used by the binary
+ * tree management routines. In particular:
+ *
+ * ubi_btCompFunc is a pointer to a comparison function. Comparison
+ * functions are passed an ubi_btItemPtr and an
+ * ubi_btNodePtr. They return a value that is (<0), 0,
+ * or (>0) to indicate that the Item is (respectively)
+ * "less than", "equal to", or "greater than" the Item
+ * contained within the node. (See ubi_btInitTree()).
+ * ubi_btActionRtn is a pointer to a function that may be called for each
+ * node visited when performing a tree traversal (see
+ * ubi_btTraverse()). The function will be passed two
+ * parameters: the first is a pointer to a node in the
+ * tree, the second is a generic pointer that may point to
+ * anything that you like.
+ * ubi_btKillNodeRtn is a pointer to a function that will deallocate the
+ * memory used by a node (see ubi_btKillTree()). Since
+ * memory management is left up to you, deallocation may
+ * mean anything that you want it to mean. Just remember
+ * that the tree *will* be destroyed and that none of the
+ * node pointers will be valid any more.
+ * ------------------------------------------------------------------------- **
+ */
+
+typedef int (*ubi_btCompFunc)( ubi_btItemPtr, ubi_btNodePtr );
+
+typedef void (*ubi_btActionRtn)( ubi_btNodePtr, void * );
+
+typedef void (*ubi_btKillNodeRtn)( ubi_btNodePtr );
+
+/* -------------------------------------------------------------------------- **
+ * Tree Root Structure: This structure gives us a convenient handle for
+ * accessing whole binary trees. The fields are:
+ * root - A pointer to the root node of the tree.
+ * count - A count of the number of nodes stored in the tree.
+ * cmp - A pointer to the comparison routine to be used when building or
+ * searching the tree.
+ * flags - A set of bit flags. Two flags are currently defined:
+ *
+ * ubi_trOVERWRITE - If set, this flag indicates that a new node should
+ * (bit 0x01) overwrite an old node if the two have identical
+ * keys (ie., the keys are equal).
+ * ubi_trDUPKEY - If set, this flag indicates that the tree is
+ * (bit 0x02) allowed to contain nodes with duplicate keys.
+ *
+ * NOTE: ubi_trInsert() tests ubi_trDUPKEY before ubi_trOVERWRITE.
+ *
+ * All of these values are set when you initialize the root structure by
+ * calling ubi_trInitTree().
+ * -------------------------------------------------------------------------- **
+ */
+
+typedef struct {
+ ubi_btNodePtr root; /* A pointer to the root node of the tree */
+ ubi_btCompFunc cmp; /* A pointer to the tree's comparison function */
+ unsigned long count; /* A count of the number of nodes in the tree */
+ char flags; /* Overwrite Y|N, Duplicate keys Y|N... */
+ } ubi_btRoot;
+
+typedef ubi_btRoot *ubi_btRootPtr; /* Pointer to an ubi_btRoot structure. */
+
+
+/* -------------------------------------------------------------------------- **
+ * Function Prototypes.
+ */
+
+long ubi_btSgn( long x );
+ /* ------------------------------------------------------------------------ **
+ * Return the sign of x; {negative,zero,positive} ==> {-1, 0, 1}.
+ *
+ * Input: x - a signed long integer value.
+ *
+ * Output: the "sign" of x, represented as follows:
+ * -1 == negative
+ * 0 == zero (no sign)
+ * 1 == positive
+ *
+ * Note: This utility is provided in order to facilitate the conversion
+ * of C comparison function return values into BinTree direction
+ * values: {LEFT, PARENT, EQUAL}. It is INCORPORATED into the
+ * AbNormal() conversion macro!
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_btInitNode( ubi_btNodePtr NodePtr );
+ /* ------------------------------------------------------------------------ **
+ * Initialize a tree node.
+ *
+ * Input: a pointer to a ubi_btNode structure to be initialized.
+ * Output: a pointer to the initialized ubi_btNode structure (ie. the
+ * same as the input pointer).
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btRootPtr ubi_btInitTree( ubi_btRootPtr RootPtr,
+ ubi_btCompFunc CompFunc,
+ char Flags );
+ /* ------------------------------------------------------------------------ **
+ * Initialize the fields of a Tree Root header structure.
+ *
+ * Input: RootPtr - a pointer to an ubi_btRoot structure to be
+ * initialized.
+ * CompFunc - a pointer to a comparison function that will be used
+ * whenever nodes in the tree must be compared against
+ * outside values.
+ * Flags - One bytes worth of flags. Flags include
+ * ubi_trOVERWRITE and ubi_trDUPKEY. See the header
+ * file for more info.
+ *
+ * Output: a pointer to the initialized ubi_btRoot structure (ie. the
+ * same value as RootPtr).
+ *
+ * Note: The interface to this function has changed from that of
+ * previous versions. The <Flags> parameter replaces two
+ * boolean parameters that had the same basic effect.
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_trBool ubi_btInsert( ubi_btRootPtr RootPtr,
+ ubi_btNodePtr NewNode,
+ ubi_btItemPtr ItemPtr,
+ ubi_btNodePtr *OldNode );
+ /* ------------------------------------------------------------------------ **
+ * This function uses a non-recursive algorithm to add a new element to the
+ * tree.
+ *
+ * Input: RootPtr - a pointer to the ubi_btRoot structure that indicates
+ * the root of the tree to which NewNode is to be added.
+ * NewNode - a pointer to an ubi_btNode structure that is NOT
+ * part of any tree.
+ * ItemPtr - A pointer to the sort key that is stored within
+ * *NewNode. ItemPtr MUST point to information stored
+ * in *NewNode or an EXACT DUPLICATE. The key data
+ * indicated by ItemPtr is used to place the new node
+ * into the tree.
+ * OldNode - a pointer to an ubi_btNodePtr. When searching
+ * the tree, a duplicate node may be found. If
+ * duplicates are allowed, then the new node will
+ * be simply placed into the tree. If duplicates
+ * are not allowed, however, then one of two things
+ * may happen.
+ * 1) if overwritting *is not* allowed, this
+ * function will return FALSE (indicating that
+ * the new node could not be inserted), and
+ * *OldNode will point to the duplicate that is
+ * still in the tree.
+ * 2) if overwritting *is* allowed, then this
+ * function will swap **OldNode for *NewNode.
+ * In this case, *OldNode will point to the node
+ * that was removed (thus allowing you to free
+ * the node).
+ * ** If you are using overwrite mode, ALWAYS **
+ * ** check the return value of this parameter! **
+ * Note: You may pass NULL in this parameter, the
+ * function knows how to cope. If you do this,
+ * however, there will be no way to return a
+ * pointer to an old (ie. replaced) node (which is
+ * a problem if you are using overwrite mode).
+ *
+ * Output: a boolean value indicating success or failure. The function
+ * will return FALSE if the node could not be added to the tree.
+ * Such failure will only occur if duplicates are not allowed,
+ * nodes cannot be overwritten, AND a duplicate key was found
+ * within the tree.
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_btRemove( ubi_btRootPtr RootPtr,
+ ubi_btNodePtr DeadNode );
+ /* ------------------------------------------------------------------------ **
+ * This function removes the indicated node from the tree.
+ *
+ * Input: RootPtr - A pointer to the header of the tree that contains
+ * the node to be removed.
+ * DeadNode - A pointer to the node that will be removed.
+ *
+ * Output: This function returns a pointer to the node that was removed
+ * from the tree (ie. the same as DeadNode).
+ *
+ * Note: The node MUST be in the tree indicated by RootPtr. If not,
+ * strange and evil things will happen to your trees.
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_btLocate( ubi_btRootPtr RootPtr,
+ ubi_btItemPtr FindMe,
+ ubi_trCompOps CompOp );
+ /* ------------------------------------------------------------------------ **
+ * The purpose of ubi_btLocate() is to find a node or set of nodes given
+ * a target value and a "comparison operator". The Locate() function is
+ * more flexible and (in the case of trees that may contain dupicate keys)
+ * more precise than the ubi_btFind() function. The latter is faster,
+ * but it only searches for exact matches and, if the tree contains
+ * duplicates, Find() may return a pointer to any one of the duplicate-
+ * keyed records.
+ *
+ * Input:
+ * RootPtr - A pointer to the header of the tree to be searched.
+ * FindMe - An ubi_btItemPtr that indicates the key for which to
+ * search.
+ * CompOp - One of the following:
+ * CompOp Return a pointer to the node with
+ * ------ ---------------------------------
+ * ubi_trLT - the last key value that is less
+ * than FindMe.
+ * ubi_trLE - the first key matching FindMe, or
+ * the last key that is less than
+ * FindMe.
+ * ubi_trEQ - the first key matching FindMe.
+ * ubi_trGE - the first key matching FindMe, or the
+ * first key greater than FindMe.
+ * ubi_trGT - the first key greater than FindMe.
+ * Output:
+ * A pointer to the node matching the criteria listed above under
+ * CompOp, or NULL if no node matched the criteria.
+ *
+ * Notes:
+ * In the case of trees with duplicate keys, Locate() will behave as
+ * follows:
+ *
+ * Find: 3 Find: 3
+ * Keys: 1 2 2 2 3 3 3 3 3 4 4 Keys: 1 1 2 2 2 4 4 5 5 5 6
+ * ^ ^ ^ ^ ^
+ * LT EQ GT LE GE
+ *
+ * That is, when returning a pointer to a node with a key that is LESS
+ * THAN the target key (FindMe), Locate() will return a pointer to the
+ * LAST matching node.
+ * When returning a pointer to a node with a key that is GREATER
+ * THAN the target key (FindMe), Locate() will return a pointer to the
+ * FIRST matching node.
+ *
+ * See Also: ubi_btFind(), ubi_btFirstOf(), ubi_btLastOf().
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_btFind( ubi_btRootPtr RootPtr,
+ ubi_btItemPtr FindMe );
+ /* ------------------------------------------------------------------------ **
+ * This function performs a non-recursive search of a tree for any node
+ * matching a specific key.
+ *
+ * Input:
+ * RootPtr - a pointer to the header of the tree to be searched.
+ * FindMe - a pointer to the key value for which to search.
+ *
+ * Output:
+ * A pointer to a node with a key that matches the key indicated by
+ * FindMe, or NULL if no such node was found.
+ *
+ * Note: In a tree that allows duplicates, the pointer returned *might
+ * not* point to the (sequentially) first occurance of the
+ * desired key. In such a tree, it may be more useful to use
+ * ubi_btLocate().
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_btNext( ubi_btNodePtr P );
+ /* ------------------------------------------------------------------------ **
+ * Given the node indicated by P, find the (sorted order) Next node in the
+ * tree.
+ * Input: P - a pointer to a node that exists in a binary tree.
+ * Output: A pointer to the "next" node in the tree, or NULL if P pointed
+ * to the "last" node in the tree or was NULL.
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_btPrev( ubi_btNodePtr P );
+ /* ------------------------------------------------------------------------ **
+ * Given the node indicated by P, find the (sorted order) Previous node in
+ * the tree.
+ * Input: P - a pointer to a node that exists in a binary tree.
+ * Output: A pointer to the "previous" node in the tree, or NULL if P
+ * pointed to the "first" node in the tree or was NULL.
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_btFirst( ubi_btNodePtr P );
+ /* ------------------------------------------------------------------------ **
+ * Given the node indicated by P, find the (sorted order) First node in the
+ * subtree of which *P is the root.
+ * Input: P - a pointer to a node that exists in a binary tree.
+ * Output: A pointer to the "first" node in a subtree that has *P as its
+ * root. This function will return NULL only if P is NULL.
+ * Note: In general, you will be passing in the value of the root field
+ * of an ubi_btRoot structure.
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_btLast( ubi_btNodePtr P );
+ /* ------------------------------------------------------------------------ **
+ * Given the node indicated by P, find the (sorted order) Last node in the
+ * subtree of which *P is the root.
+ * Input: P - a pointer to a node that exists in a binary tree.
+ * Output: A pointer to the "last" node in a subtree that has *P as its
+ * root. This function will return NULL only if P is NULL.
+ * Note: In general, you will be passing in the value of the root field
+ * of an ubi_btRoot structure.
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_btFirstOf( ubi_btRootPtr RootPtr,
+ ubi_btItemPtr MatchMe,
+ ubi_btNodePtr p );
+ /* ------------------------------------------------------------------------ **
+ * Given a tree that a allows duplicate keys, and a pointer to a node in
+ * the tree, this function will return a pointer to the first (traversal
+ * order) node with the same key value.
+ *
+ * Input: RootPtr - A pointer to the root of the tree.
+ * MatchMe - A pointer to the key value. This should probably
+ * point to the key within node *p.
+ * p - A pointer to a node in the tree.
+ * Output: A pointer to the first node in the set of nodes with keys
+ * matching <FindMe>.
+ * Notes: Node *p MUST be in the set of nodes with keys matching
+ * <FindMe>. If not, this function will return NULL.
+ *
+ * 4.7: Bug found & fixed by Massimo Campostrini,
+ * Istituto Nazionale di Fisica Nucleare, Sezione di Pisa.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_btLastOf( ubi_btRootPtr RootPtr,
+ ubi_btItemPtr MatchMe,
+ ubi_btNodePtr p );
+ /* ------------------------------------------------------------------------ **
+ * Given a tree that a allows duplicate keys, and a pointer to a node in
+ * the tree, this function will return a pointer to the last (traversal
+ * order) node with the same key value.
+ *
+ * Input: RootPtr - A pointer to the root of the tree.
+ * MatchMe - A pointer to the key value. This should probably
+ * point to the key within node *p.
+ * p - A pointer to a node in the tree.
+ * Output: A pointer to the last node in the set of nodes with keys
+ * matching <FindMe>.
+ * Notes: Node *p MUST be in the set of nodes with keys matching
+ * <FindMe>. If not, this function will return NULL.
+ *
+ * 4.7: Bug found & fixed by Massimo Campostrini,
+ * Istituto Nazionale di Fisica Nucleare, Sezione di Pisa.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+unsigned long ubi_btTraverse( ubi_btRootPtr RootPtr,
+ ubi_btActionRtn EachNode,
+ void *UserData );
+ /* ------------------------------------------------------------------------ **
+ * Traverse a tree in sorted order (non-recursively). At each node, call
+ * (*EachNode)(), passing a pointer to the current node, and UserData as the
+ * second parameter.
+ *
+ * Input: RootPtr - a pointer to an ubi_btRoot structure that indicates
+ * the tree to be traversed.
+ * EachNode - a pointer to a function to be called at each node
+ * as the node is visited.
+ * UserData - a generic pointer that may point to anything that
+ * you choose.
+ *
+ * Output: A count of the number of nodes visited. This will be zero
+ * if the tree is empty.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+
+unsigned long ubi_btKillTree( ubi_btRootPtr RootPtr,
+ ubi_btKillNodeRtn FreeNode );
+ /* ------------------------------------------------------------------------ **
+ * Delete an entire tree (non-recursively) and reinitialize the ubi_btRoot
+ * structure. Return a count of the number of nodes deleted.
+ *
+ * Input: RootPtr - a pointer to an ubi_btRoot structure that indicates
+ * the root of the tree to delete.
+ * FreeNode - a function that will be called for each node in the
+ * tree to deallocate the memory used by the node.
+ *
+ * Output: The number of nodes removed from the tree.
+ * A value of 0 will be returned if:
+ * - The tree actually contains 0 entries.
+ * - the value of <RootPtr> is NULL, in which case the tree is
+ * assumed to be empty
+ * - the value of <FreeNode> is NULL, in which case entries
+ * cannot be removed, so 0 is returned. *Make sure that you
+ * provide a valid value for <FreeNode>*.
+ * In all other cases, you should get a positive value equal to
+ * the value of RootPtr->count upon entry.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_btLeafNode( ubi_btNodePtr leader );
+ /* ------------------------------------------------------------------------ **
+ * Returns a pointer to a leaf node.
+ *
+ * Input: leader - Pointer to a node at which to start the descent.
+ *
+ * Output: A pointer to a leaf node selected in a somewhat arbitrary
+ * manner.
+ *
+ * Notes: I wrote this function because I was using splay trees as a
+ * database cache. The cache had a maximum size on it, and I
+ * needed a way of choosing a node to sacrifice if the cache
+ * became full. In a splay tree, less recently accessed nodes
+ * tend toward the bottom of the tree, meaning that leaf nodes
+ * are good candidates for removal. (I really can't think of
+ * any other reason to use this function.)
+ * + In a simple binary tree or an AVL tree, the most recently
+ * added nodes tend to be nearer the bottom, making this a *bad*
+ * way to choose which node to remove from the cache.
+ * + Randomizing the traversal order is probably a good idea. You
+ * can improve the randomization of leaf node selection by passing
+ * in pointers to nodes other than the root node each time. A
+ * pointer to any node in the tree will do. Of course, if you
+ * pass a pointer to a leaf node you'll get the same thing back.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+
+int ubi_btModuleID( int size, char *list[] );
+ /* ------------------------------------------------------------------------ **
+ * Returns a set of strings that identify the module.
+ *
+ * Input: size - The number of elements in the array <list>.
+ * list - An array of pointers of type (char *). This array
+ * should, initially, be empty. This function will fill
+ * in the array with pointers to strings.
+ * Output: The number of elements of <list> that were used. If this value
+ * is less than <size>, the values of the remaining elements are
+ * not guaranteed.
+ *
+ * Notes: Please keep in mind that the pointers returned indicate strings
+ * stored in static memory. Don't free() them, don't write over
+ * them, etc. Just read them.
+ * ------------------------------------------------------------------------ **
+ */
+
+/* -------------------------------------------------------------------------- **
+ * Masquarade...
+ *
+ * This set of defines allows you to write programs that will use any of the
+ * implemented binary tree modules (currently BinTree, AVLtree, and SplayTree).
+ * Instead of using ubi_bt..., use ubi_tr..., and select the tree type by
+ * including the appropriate module header.
+ */
+
+#define ubi_trItemPtr ubi_btItemPtr
+
+#define ubi_trNode ubi_btNode
+#define ubi_trNodePtr ubi_btNodePtr
+
+#define ubi_trRoot ubi_btRoot
+#define ubi_trRootPtr ubi_btRootPtr
+
+#define ubi_trCompFunc ubi_btCompFunc
+#define ubi_trActionRtn ubi_btActionRtn
+#define ubi_trKillNodeRtn ubi_btKillNodeRtn
+
+#define ubi_trSgn( x ) ubi_btSgn( x )
+
+#define ubi_trInitNode( Np ) ubi_btInitNode( (ubi_btNodePtr)(Np) )
+
+#define ubi_trInitTree( Rp, Cf, Fl ) \
+ ubi_btInitTree( (ubi_btRootPtr)(Rp), (ubi_btCompFunc)(Cf), (Fl) )
+
+#define ubi_trInsert( Rp, Nn, Ip, On ) \
+ ubi_btInsert( (ubi_btRootPtr)(Rp), (ubi_btNodePtr)(Nn), \
+ (ubi_btItemPtr)(Ip), (ubi_btNodePtr *)(On) )
+
+#define ubi_trRemove( Rp, Dn ) \
+ ubi_btRemove( (ubi_btRootPtr)(Rp), (ubi_btNodePtr)(Dn) )
+
+#define ubi_trLocate( Rp, Ip, Op ) \
+ ubi_btLocate( (ubi_btRootPtr)(Rp), \
+ (ubi_btItemPtr)(Ip), \
+ (ubi_trCompOps)(Op) )
+
+#define ubi_trFind( Rp, Ip ) \
+ ubi_btFind( (ubi_btRootPtr)(Rp), (ubi_btItemPtr)(Ip) )
+
+#define ubi_trNext( P ) ubi_btNext( (ubi_btNodePtr)(P) )
+
+#define ubi_trPrev( P ) ubi_btPrev( (ubi_btNodePtr)(P) )
+
+#define ubi_trFirst( P ) ubi_btFirst( (ubi_btNodePtr)(P) )
+
+#define ubi_trLast( P ) ubi_btLast( (ubi_btNodePtr)(P) )
+
+#define ubi_trFirstOf( Rp, Ip, P ) \
+ ubi_btFirstOf( (ubi_btRootPtr)(Rp), \
+ (ubi_btItemPtr)(Ip), \
+ (ubi_btNodePtr)(P) )
+
+#define ubi_trLastOf( Rp, Ip, P ) \
+ ubi_btLastOf( (ubi_btRootPtr)(Rp), \
+ (ubi_btItemPtr)(Ip), \
+ (ubi_btNodePtr)(P) )
+
+#define ubi_trTraverse( Rp, En, Ud ) \
+ ubi_btTraverse((ubi_btRootPtr)(Rp), (ubi_btActionRtn)(En), (void *)(Ud))
+
+#define ubi_trKillTree( Rp, Fn ) \
+ ubi_btKillTree( (ubi_btRootPtr)(Rp), (ubi_btKillNodeRtn)(Fn) )
+
+#define ubi_trLeafNode( Nd ) \
+ ubi_btLeafNode( (ubi_btNodePtr)(Nd) )
+
+#define ubi_trModuleID( s, l ) ubi_btModuleID( s, l )
+
+/* ========================================================================== */
+#endif /* UBI_BINTREE_H */
diff --git a/source/ubiqx/ubi_Cache.c b/source/ubiqx/ubi_Cache.c
new file mode 100644
index 00000000000..f428dcefe97
--- /dev/null
+++ b/source/ubiqx/ubi_Cache.c
@@ -0,0 +1,505 @@
+/* ========================================================================== **
+ * ubi_Cache.c
+ *
+ * Copyright (C) 1997 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ * -------------------------------------------------------------------------- **
+ *
+ * This module implements a generic cache.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * This module uses a splay tree to implement a simple cache. The cache
+ * module adds a thin layer of functionality to the splay tree. In
+ * particular:
+ *
+ * - The tree (cache) may be limited in size by the number of
+ * entries permitted or the amount of memory used. When either
+ * limit is exceeded cache entries are removed until the cache
+ * conforms.
+ * - Some statistical information is kept so that an approximate
+ * "hit ratio" can be calculated.
+ * - There are several functions available that provide access to
+ * and management of cache size limits, hit ratio, and tree
+ * trimming.
+ *
+ * The splay tree is used because recently accessed items tend toward the
+ * top of the tree and less recently accessed items tend toward the bottom.
+ * This makes it easy to purge less recently used items should the cache
+ * exceed its limits.
+ *
+ * To use this module, you will need to supply a comparison function of
+ * type ubi_trCompFunc and a node-freeing function of type
+ * ubi_trKillNodeRtn. See ubi_BinTree.h for more information on
+ * these. (This is all basic ubiqx tree management stuff.)
+ *
+ * Notes:
+ *
+ * - Cache performance will start to suffer dramatically if the
+ * cache becomes large enough to force the OS to start swapping
+ * memory to disk. This is because the nodes of the underlying tree
+ * will be scattered across memory in an order that is completely
+ * unrelated to their traversal order. As more and more of the
+ * cache is placed into swap space, more and more swaps will be
+ * required for a simple traversal (...and then there's the splay
+ * operation).
+ *
+ * In one simple test under Linux, the load and dump of a cache of
+ * 400,000 entries took only 1min, 40sec of real time. The same
+ * test with 450,000 records took 2 *hours* and eight minutes.
+ *
+ * - In an effort to save memory, I considered using an unsigned
+ * short to save the per-entry entry size. I would have tucked this
+ * value into some unused space in the tree node structure. On
+ * 32-bit word aligned systems this would have saved an additional
+ * four bytes per entry. I may revisit this issue, but for now I've
+ * decided against it.
+ *
+ * Using an unsigned short would limit the size of an entry to 64K
+ * bytes. That's probably more than enough for most applications.
+ * The key word in that last sentence, however, is "probably". I
+ * really dislike imposing such limits on things.
+ *
+ * - Each entry keeps track of the amount of memory it used and the
+ * cache header keeps the total. This information is provided via
+ * the EntrySize parameter in ubi_cachePut(), so it is up to you to
+ * make sure that the numbers are accurate. (The numbers don't even
+ * have to represent bytes used.)
+ *
+ * As you consider this, note that the strdup() function--as an
+ * example--will call malloc(). The latter generally allocates a
+ * multiple of the system word size, which may be more than the
+ * number of bytes needed to store the string.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * Log: ubi_Cache.c,v
+ * Revision 0.4 1999/09/22 03:42:24 crh
+ * Fixed a minor typo.
+ *
+ * Revision 0.3 1998/06/03 18:00:15 crh
+ * Further fiddling with sys_include.h, which is no longer explicitly
+ * included by this module since it is inherited from ubi_BinTree.h.
+ *
+ * Revision 0.2 1998/06/02 01:36:18 crh
+ * Changed include name from ubi_null.h to sys_include.h to make it
+ * more generic.
+ *
+ * Revision 0.1 1998/05/20 04:36:02 crh
+ * The C file now includes ubi_null.h. See ubi_null.h for more info.
+ *
+ * Revision 0.0 1997/12/18 06:24:33 crh
+ * Initial Revision.
+ *
+ * ========================================================================== **
+ */
+
+#include "ubi_Cache.h" /* Header for *this* module. */
+
+/* -------------------------------------------------------------------------- **
+ * Static data...
+ */
+
+/* commented out until I make use of it...
+static char ModuleID[] =
+"ubi_Cache\n\
+\tRevision: 0.4 \n\
+\tDate: 1999/09/22 03:42:24 \n\
+\tAuthor: crh \n";
+*/
+
+/* -------------------------------------------------------------------------- **
+ * Internal functions...
+ */
+
+static void free_entry( ubi_cacheRootPtr CachePtr, ubi_cacheEntryPtr EntryPtr )
+ /* ------------------------------------------------------------------------ **
+ * Free a ubi_cacheEntry, and adjust the mem_used counter accordingly.
+ *
+ * Input: CachePtr - A pointer to the cache from which the entry has
+ * been removed.
+ * EntryPtr - A pointer to the already removed entry.
+ *
+ * Output: none.
+ *
+ * Notes: The entry must be removed from the cache *before* this function
+ * is called!!!!
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ CachePtr->mem_used -= EntryPtr->entry_size;
+ (*CachePtr->free_func)( (void *)EntryPtr );
+ } /* free_entry */
+
+static void cachetrim( ubi_cacheRootPtr crptr )
+ /* ------------------------------------------------------------------------ **
+ * Remove entries from the cache until the number of entries and the amount
+ * of memory used are *both* below or at the maximum.
+ *
+ * Input: crptr - pointer to the cache to be trimmed.
+ *
+ * Output: None.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ while( ( crptr->max_entries && (crptr->max_entries < crptr->root.count) )
+ || ( crptr->max_memory && (crptr->max_memory < crptr->mem_used) ) )
+ {
+ if( !ubi_cacheReduce( crptr, 1 ) )
+ return;
+ }
+ } /* cachetrim */
+
+
+/* -------------------------------------------------------------------------- **
+ * Exported functions...
+ */
+
+ubi_cacheRootPtr ubi_cacheInit( ubi_cacheRootPtr CachePtr,
+ ubi_trCompFunc CompFunc,
+ ubi_trKillNodeRtn FreeFunc,
+ unsigned long MaxEntries,
+ unsigned long MaxMemory )
+ /* ------------------------------------------------------------------------ **
+ * Initialize a cache header structure.
+ *
+ * Input: CachePtr - A pointer to a ubi_cacheRoot structure that is
+ * to be initialized.
+ * CompFunc - A pointer to the function that will be called
+ * to compare two cache values. See the module
+ * comments, above, for more information.
+ * FreeFunc - A pointer to a function that will be called
+ * to free a cache entry. If you allocated
+ * the cache entry using malloc(), then this
+ * will likely be free(). If you are allocating
+ * cache entries from a free list, then this will
+ * likely be a function that returns memory to the
+ * free list, etc.
+ * MaxEntries - The maximum number of entries that will be
+ * allowed to exist in the cache. If this limit
+ * is exceeded, then existing entries will be
+ * removed from the cache. A value of zero
+ * indicates that there is no limit on the number
+ * of cache entries. See ubi_cachePut().
+ * MaxMemory - The maximum amount of memory, in bytes, to be
+ * allocated to the cache (excluding the cache
+ * header). If this is exceeded, existing entries
+ * in the cache will be removed until enough memory
+ * has been freed to meet the condition. See
+ * ubi_cachePut().
+ *
+ * Output: A pointer to the initialized cache (i.e., the same as CachePtr).
+ *
+ * Notes: Both MaxEntries and MaxMemory may be changed after the cache
+ * has been created. See
+ * ubi_cacheSetMaxEntries()
+ * ubi_cacheSetMaxMemory()
+ * ubi_cacheGetMaxEntries()
+ * ubi_cacheGetMaxMemory() (the latter two are macros).
+ *
+ * - Memory is allocated in multiples of the word size. The
+ * return value of the strlen() function does not reflect
+ * this; it will allways be less than or equal to the amount
+ * of memory actually allocated. Keep this in mind when
+ * choosing a value for MaxMemory.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ if( CachePtr )
+ {
+ (void)ubi_trInitTree( CachePtr, CompFunc, ubi_trOVERWRITE );
+ CachePtr->free_func = FreeFunc;
+ CachePtr->max_entries = MaxEntries;
+ CachePtr->max_memory = MaxMemory;
+ CachePtr->mem_used = 0;
+ CachePtr->cache_hits = 0;
+ CachePtr->cache_trys = 0;
+ }
+ return( CachePtr );
+ } /* ubi_cacheInit */
+
+ubi_cacheRootPtr ubi_cacheClear( ubi_cacheRootPtr CachePtr )
+ /* ------------------------------------------------------------------------ **
+ * Remove and free all entries in an existing cache.
+ *
+ * Input: CachePtr - A pointer to the cache that is to be cleared.
+ *
+ * Output: A pointer to the cache header (i.e., the same as CachePtr).
+ * This function re-initializes the cache header.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ if( CachePtr )
+ {
+ (void)ubi_trKillTree( CachePtr, CachePtr->free_func );
+ CachePtr->mem_used = 0;
+ CachePtr->cache_hits = 0;
+ CachePtr->cache_trys = 0;
+ }
+ return( CachePtr );
+ } /* ubi_cacheClear */
+
+void ubi_cachePut( ubi_cacheRootPtr CachePtr,
+ unsigned long EntrySize,
+ ubi_cacheEntryPtr EntryPtr,
+ ubi_trItemPtr Key )
+ /* ------------------------------------------------------------------------ **
+ * Add an entry to the cache.
+ *
+ * Input: CachePtr - A pointer to the cache into which the entry
+ * will be added.
+ * EntrySize - The size, in bytes, of the memory block indicated
+ * by EntryPtr. This will be copied into the
+ * EntryPtr->entry_size field.
+ * EntryPtr - A pointer to a memory block that begins with a
+ * ubi_cacheEntry structure. The entry structure
+ * should be followed immediately by the data to be
+ * cached (even if that is a pointer to yet more data).
+ * Key - Pointer used to identify the lookup key within the
+ * Entry.
+ *
+ * Output: None.
+ *
+ * Notes: After adding the new node, the cache is "trimmed". This
+ * removes extra nodes if the tree has exceeded it's memory or
+ * entry count limits. It is unlikely that the newly added node
+ * will be purged from the cache (assuming a reasonably large
+ * cache), since new nodes in a splay tree (which is what this
+ * module was designed to use) are moved to the top of the tree
+ * and the cache purge process removes nodes from the bottom of
+ * the tree.
+ * - The underlying splay tree is opened in OVERWRITE mode. If
+ * the input key matches an existing key, the existing entry will
+ * be politely removed from the tree and freed.
+ * - Memory is allocated in multiples of the word size. The
+ * return value of the strlen() function does not reflect
+ * this; it will allways be less than or equal to the amount
+ * of memory actually allocated.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_trNodePtr OldNode;
+
+ EntryPtr->entry_size = EntrySize;
+ CachePtr->mem_used += EntrySize;
+ (void)ubi_trInsert( CachePtr, EntryPtr, Key, &OldNode );
+ if( OldNode )
+ free_entry( CachePtr, (ubi_cacheEntryPtr)OldNode );
+
+ cachetrim( CachePtr );
+ } /* ubi_cachePut */
+
+ubi_cacheEntryPtr ubi_cacheGet( ubi_cacheRootPtr CachePtr,
+ ubi_trItemPtr FindMe )
+ /* ------------------------------------------------------------------------ **
+ * Attempt to retrieve an entry from the cache.
+ *
+ * Input: CachePtr - A ponter to the cache that is to be searched.
+ * FindMe - A ubi_trItemPtr that indicates the key for which
+ * to search.
+ *
+ * Output: A pointer to the cache entry that was found, or NULL if no
+ * matching entry was found.
+ *
+ * Notes: This function also updates the hit ratio counters.
+ * The counters are unsigned short. If the number of cache tries
+ * reaches 32768, then both the number of tries and the number of
+ * hits are divided by two. This prevents the counters from
+ * overflowing. See the comments in ubi_cacheHitRatio() for
+ * additional notes.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_trNodePtr FoundPtr;
+
+ FoundPtr = ubi_trFind( CachePtr, FindMe );
+
+ if( FoundPtr )
+ CachePtr->cache_hits++;
+ CachePtr->cache_trys++;
+
+ if( CachePtr->cache_trys & 0x8000 )
+ {
+ CachePtr->cache_hits = CachePtr->cache_hits / 2;
+ CachePtr->cache_trys = CachePtr->cache_trys / 2;
+ }
+
+ return( (ubi_cacheEntryPtr)FoundPtr );
+ } /* ubi_cacheGet */
+
+ubi_trBool ubi_cacheDelete( ubi_cacheRootPtr CachePtr, ubi_trItemPtr DeleteMe )
+ /* ------------------------------------------------------------------------ **
+ * Find and delete the specified cache entry.
+ *
+ * Input: CachePtr - A pointer to the cache.
+ * DeleteMe - The key of the entry to be deleted.
+ *
+ * Output: TRUE if the entry was found & freed, else FALSE.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_trNodePtr FoundPtr;
+
+ FoundPtr = ubi_trFind( CachePtr, DeleteMe );
+ if( FoundPtr )
+ {
+ (void)ubi_trRemove( CachePtr, FoundPtr );
+ free_entry( CachePtr, (ubi_cacheEntryPtr)FoundPtr );
+ return( ubi_trTRUE );
+ }
+ return( ubi_trFALSE );
+ } /* ubi_cacheDelete */
+
+ubi_trBool ubi_cacheReduce( ubi_cacheRootPtr CachePtr, unsigned long count )
+ /* ------------------------------------------------------------------------ **
+ * Remove <count> entries from the bottom of the cache.
+ *
+ * Input: CachePtr - A pointer to the cache which is to be reduced in
+ * size.
+ * count - The number of entries to remove.
+ *
+ * Output: The function will return TRUE if <count> entries were removed,
+ * else FALSE. A return value of FALSE should indicate that
+ * there were less than <count> entries in the cache, and that the
+ * cache is now empty.
+ *
+ * Notes: This function forces a reduction in the number of cache entries
+ * without requiring that the MaxMemory or MaxEntries values be
+ * changed.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_trNodePtr NodePtr;
+
+ while( count )
+ {
+ NodePtr = ubi_trLeafNode( CachePtr->root.root );
+ if( NULL == NodePtr )
+ return( ubi_trFALSE );
+ else
+ {
+ (void)ubi_trRemove( CachePtr, NodePtr );
+ free_entry( CachePtr, (ubi_cacheEntryPtr)NodePtr );
+ }
+ count--;
+ }
+ return( ubi_trTRUE );
+ } /* ubi_cacheReduce */
+
+unsigned long ubi_cacheSetMaxEntries( ubi_cacheRootPtr CachePtr,
+ unsigned long NewSize )
+ /* ------------------------------------------------------------------------ **
+ * Change the maximum number of entries allowed to exist in the cache.
+ *
+ * Input: CachePtr - A pointer to the cache to be modified.
+ * NewSize - The new maximum number of cache entries.
+ *
+ * Output: The maximum number of entries previously allowed to exist in
+ * the cache.
+ *
+ * Notes: If the new size is less than the old size, this function will
+ * trim the cache (remove excess entries).
+ * - A value of zero indicates an unlimited number of entries.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ unsigned long oldsize = CachePtr->max_entries; /* Save the old value. */
+
+ CachePtr->max_entries = NewSize; /* Apply the new value. */
+ if( (NewSize < oldsize) || (NewSize && !oldsize) ) /* If size is smaller, */
+ cachetrim( CachePtr ); /* remove excess. */
+ return( oldsize );
+ } /* ubi_cacheSetMaxEntries */
+
+unsigned long ubi_cacheSetMaxMemory( ubi_cacheRootPtr CachePtr,
+ unsigned long NewSize )
+ /* ------------------------------------------------------------------------ **
+ * Change the maximum amount of memory to be used for storing cache
+ * entries.
+ *
+ * Input: CachePtr - A pointer to the cache to be modified.
+ * NewSize - The new cache memory size.
+ *
+ * Output: The previous maximum memory size.
+ *
+ * Notes: If the new size is less than the old size, this function will
+ * trim the cache (remove excess entries).
+ * - A value of zero indicates that the cache has no memory limit.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ unsigned long oldsize = CachePtr->max_memory; /* Save the old value. */
+
+ CachePtr->max_memory = NewSize; /* Apply the new value. */
+ if( (NewSize < oldsize) || (NewSize && !oldsize) ) /* If size is smaller, */
+ cachetrim( CachePtr ); /* remove excess. */
+ return( oldsize );
+ } /* ubi_cacheSetMaxMemory */
+
+int ubi_cacheHitRatio( ubi_cacheRootPtr CachePtr )
+ /* ------------------------------------------------------------------------ **
+ * Returns a value that is 10,000 times the slightly weighted average hit
+ * ratio for the cache.
+ *
+ * Input: CachePtr - Pointer to the cache to be queried.
+ *
+ * Output: An integer that is 10,000 times the number of successful
+ * cache hits divided by the number of cache lookups, or:
+ * (10000 * hits) / trys
+ * You can easily convert this to a float, or do something
+ * like this (where i is the return value of this function):
+ *
+ * printf( "Hit rate : %d.%02d%%\n", (i/100), (i%100) );
+ *
+ * Notes: I say "slightly-weighted", because the numerator and
+ * denominator are both accumulated in locations of type
+ * 'unsigned short'. If the number of cache trys becomes
+ * large enough, both are divided by two. (See function
+ * ubi_cacheGet().)
+ * Dividing both numerator and denominator by two does not
+ * change the ratio (much...it is an integer divide), but it
+ * does mean that subsequent increments to either counter will
+ * have twice as much significance as previous ones.
+ *
+ * - The value returned by this function will be in the range
+ * [0..10000] because ( 0 <= cache_hits <= cache_trys ) will
+ * always be true.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int tmp = 0;
+
+ if( CachePtr->cache_trys )
+ tmp = (int)( (10000 * (long)(CachePtr->cache_hits) )
+ / (long)(CachePtr->cache_trys) );
+ return( tmp );
+ } /* ubi_cacheHitRatio */
+
+/* -------------------------------------------------------------------------- */
diff --git a/source/ubiqx/ubi_Cache.h b/source/ubiqx/ubi_Cache.h
new file mode 100644
index 00000000000..0fc3a074f72
--- /dev/null
+++ b/source/ubiqx/ubi_Cache.h
@@ -0,0 +1,412 @@
+#ifndef UBI_CACHE_H
+#define UBI_CACHE_H
+/* ========================================================================== **
+ * ubi_Cache.h
+ *
+ * Copyright (C) 1997 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ * -------------------------------------------------------------------------- **
+ *
+ * This module implements a generic cache.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * This module uses a splay tree to implement a simple cache. The cache
+ * module adds a thin layer of functionality to the splay tree. In
+ * particular:
+ *
+ * - The tree (cache) may be limited in size by the number of
+ * entries permitted or the amount of memory used. When either
+ * limit is exceeded cache entries are removed until the cache
+ * conforms.
+ * - Some statistical information is kept so that an approximate
+ * "hit ratio" can be calculated.
+ * - There are several functions available that provide access to
+ * and management of cache size limits, hit ratio, and tree
+ * trimming.
+ *
+ * The splay tree is used because recently accessed items tend toward the
+ * top of the tree and less recently accessed items tend toward the bottom.
+ * This makes it easy to purge less recently used items should the cache
+ * exceed its limits.
+ *
+ * To use this module, you will need to supply a comparison function of
+ * type ubi_trCompFunc and a node-freeing function of type
+ * ubi_trKillNodeRtn. See ubi_BinTree.h for more information on
+ * these. (This is all basic ubiqx tree management stuff.)
+ *
+ * Notes:
+ *
+ * - Cache performance will start to suffer dramatically if the
+ * cache becomes large enough to force the OS to start swapping
+ * memory to disk. This is because the nodes of the underlying tree
+ * will be scattered across memory in an order that is completely
+ * unrelated to their traversal order. As more and more of the
+ * cache is placed into swap space, more and more swaps will be
+ * required for a simple traversal (...and then there's the splay
+ * operation).
+ *
+ * In one simple test under Linux, the load and dump of a cache of
+ * 400,000 entries took only 1min, 40sec of real time. The same
+ * test with 450,000 records took 2 *hours* and eight minutes.
+ *
+ * - In an effort to save memory, I considered using an unsigned
+ * short to save the per-entry entry size. I would have tucked this
+ * value into some unused space in the tree node structure. On
+ * 32-bit word aligned systems this would have saved an additional
+ * four bytes per entry. I may revisit this issue, but for now I've
+ * decided against it.
+ *
+ * Using an unsigned short would limit the size of an entry to 64K
+ * bytes. That's probably more than enough for most applications.
+ * The key word in that last sentence, however, is "probably". I
+ * really dislike imposing such limits on things.
+ *
+ * - Each entry keeps track of the amount of memory it used and the
+ * cache header keeps the total. This information is provided via
+ * the EntrySize parameter in ubi_cachePut(), so it is up to you to
+ * make sure that the numbers are accurate. (The numbers don't even
+ * have to represent bytes used.)
+ *
+ * As you consider this, note that the strdup() function--as an
+ * example--will call malloc(). The latter generally allocates a
+ * multiple of the system word size, which may be more than the
+ * number of bytes needed to store the string.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * Log: ubi_Cache.h,v
+ * Revision 0.4 1999/09/22 03:42:24 crh
+ * Fixed a minor typo.
+ *
+ * Revision 0.3 1998/06/03 18:00:15 crh
+ * Further fiddling with sys_include.h, which is no longer explicitly
+ * included by this module since it is inherited from ubi_BinTree.h.
+ *
+ * Revision 0.2 1998/06/02 01:36:18 crh
+ * Changed include name from ubi_null.h to sys_include.h to make it
+ * more generic.
+ *
+ * Revision 0.1 1998/05/20 04:36:02 crh
+ * The C file now includes ubi_null.h. See ubi_null.h for more info.
+ *
+ * Revision 0.0 1997/12/18 06:25:23 crh
+ * Initial Revision.
+ *
+ * ========================================================================== **
+ */
+
+#include "ubi_SplayTree.h"
+
+/* -------------------------------------------------------------------------- **
+ * Typedefs...
+ *
+ * ubi_cacheRoot - Cache header structure, which consists of a binary
+ * tree root and other required housekeeping fields, as
+ * listed below.
+ * ubi_cacheRootPtr - Pointer to a Cache.
+ *
+ * ubi_cacheEntry - A cache Entry, which consists of a tree node
+ * structure and the size (in bytes) of the entry
+ * data. The entry size should be supplied via
+ * the EntrySize parameter of the ubi_cachePut()
+ * function.
+ *
+ * ubi_cacheEntryPtr - Pointer to a ubi_cacheEntry.
+ *
+ */
+
+typedef struct
+ {
+ ubi_trRoot root; /* Splay tree control structure. */
+ ubi_trKillNodeRtn free_func; /* Function used to free entries. */
+ unsigned long max_entries; /* Max cache entries. 0 == unlimited */
+ unsigned long max_memory; /* Max memory to use. 0 == unlimited */
+ unsigned long mem_used; /* Memory currently in use (bytes). */
+ unsigned short cache_hits; /* Incremented on succesful find. */
+ unsigned short cache_trys; /* Incremented on cache lookup. */
+ } ubi_cacheRoot;
+
+typedef ubi_cacheRoot *ubi_cacheRootPtr;
+
+
+typedef struct
+ {
+ ubi_trNode node; /* Tree node structure. */
+ unsigned long entry_size; /* Entry size. Used when managing
+ * caches with maximum memory limits.
+ */
+ } ubi_cacheEntry;
+
+typedef ubi_cacheEntry *ubi_cacheEntryPtr;
+
+
+/* -------------------------------------------------------------------------- **
+ * Macros...
+ *
+ * ubi_cacheGetMaxEntries() - Report the current maximum number of entries
+ * allowed in the cache. Zero indicates no
+ * maximum.
+ * ubi_cacheGetMaxMemory() - Report the current maximum amount of memory
+ * that may be used in the cache. Zero
+ * indicates no maximum.
+ * ubi_cacheGetEntryCount() - Report the current number of entries in the
+ * cache.
+ * ubi_cacheGetMemUsed() - Report the amount of memory currently in use
+ * by the cache.
+ */
+
+#define ubi_cacheGetMaxEntries( Cptr ) (((ubi_cacheRootPtr)(Cptr))->max_entries)
+#define ubi_cacheGetMaxMemory( Cptr ) (((ubi_cacheRootPtr)(Cptr))->max_memory)
+
+#define ubi_cacheGetEntryCount( Cptr ) (((ubi_cacheRootPtr)(Cptr))->root.count)
+#define ubi_cacheGetMemUsed( Cptr ) (((ubi_cacheRootPtr)(Cptr))->mem_used)
+
+/* -------------------------------------------------------------------------- **
+ * Prototypes...
+ */
+
+ubi_cacheRootPtr ubi_cacheInit( ubi_cacheRootPtr CachePtr,
+ ubi_trCompFunc CompFunc,
+ ubi_trKillNodeRtn FreeFunc,
+ unsigned long MaxEntries,
+ unsigned long MaxMemory );
+ /* ------------------------------------------------------------------------ **
+ * Initialize a cache header structure.
+ *
+ * Input: CachePtr - A pointer to a ubi_cacheRoot structure that is
+ * to be initialized.
+ * CompFunc - A pointer to the function that will be called
+ * to compare two cache values. See the module
+ * comments, above, for more information.
+ * FreeFunc - A pointer to a function that will be called
+ * to free a cache entry. If you allocated
+ * the cache entry using malloc(), then this
+ * will likely be free(). If you are allocating
+ * cache entries from a free list, then this will
+ * likely be a function that returns memory to the
+ * free list, etc.
+ * MaxEntries - The maximum number of entries that will be
+ * allowed to exist in the cache. If this limit
+ * is exceeded, then existing entries will be
+ * removed from the cache. A value of zero
+ * indicates that there is no limit on the number
+ * of cache entries. See ubi_cachePut().
+ * MaxMemory - The maximum amount of memory, in bytes, to be
+ * allocated to the cache (excluding the cache
+ * header). If this is exceeded, existing entries
+ * in the cache will be removed until enough memory
+ * has been freed to meet the condition. See
+ * ubi_cachePut().
+ *
+ * Output: A pointer to the initialized cache (i.e., the same as CachePtr).
+ *
+ * Notes: Both MaxEntries and MaxMemory may be changed after the cache
+ * has been created. See
+ * ubi_cacheSetMaxEntries()
+ * ubi_cacheSetMaxMemory()
+ * ubi_cacheGetMaxEntries()
+ * ubi_cacheGetMaxMemory() (the latter two are macros).
+ *
+ * - Memory is allocated in multiples of the word size. The
+ * return value of the strlen() function does not reflect
+ * this; it will allways be less than or equal to the amount
+ * of memory actually allocated. Keep this in mind when
+ * choosing a value for MaxMemory.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_cacheRootPtr ubi_cacheClear( ubi_cacheRootPtr CachePtr );
+ /* ------------------------------------------------------------------------ **
+ * Remove and free all entries in an existing cache.
+ *
+ * Input: CachePtr - A pointer to the cache that is to be cleared.
+ *
+ * Output: A pointer to the cache header (i.e., the same as CachePtr).
+ * This function re-initializes the cache header.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+void ubi_cachePut( ubi_cacheRootPtr CachePtr,
+ unsigned long EntrySize,
+ ubi_cacheEntryPtr EntryPtr,
+ ubi_trItemPtr Key );
+ /* ------------------------------------------------------------------------ **
+ * Add an entry to the cache.
+ *
+ * Input: CachePtr - A pointer to the cache into which the entry
+ * will be added.
+ * EntrySize - The size, in bytes, of the memory block indicated
+ * by EntryPtr. This will be copied into the
+ * EntryPtr->entry_size field.
+ * EntryPtr - A pointer to a memory block that begins with a
+ * ubi_cacheEntry structure. The entry structure
+ * should be followed immediately by the data to be
+ * cached (even if that is a pointer to yet more data).
+ * Key - Pointer used to identify the lookup key within the
+ * Entry.
+ *
+ * Output: None.
+ *
+ * Notes: After adding the new node, the cache is "trimmed". This
+ * removes extra nodes if the tree has exceeded it's memory or
+ * entry count limits. It is unlikely that the newly added node
+ * will be purged from the cache (assuming a reasonably large
+ * cache), since new nodes in a splay tree (which is what this
+ * module was designed to use) are moved to the top of the tree
+ * and the cache purge process removes nodes from the bottom of
+ * the tree.
+ * - The underlying splay tree is opened in OVERWRITE mode. If
+ * the input key matches an existing key, the existing entry will
+ * be politely removed from the tree and freed.
+ * - Memory is allocated in multiples of the word size. The
+ * return value of the strlen() function does not reflect
+ * this; it will allways be less than or equal to the amount
+ * of memory actually allocated.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_cacheEntryPtr ubi_cacheGet( ubi_cacheRootPtr CachePtr,
+ ubi_trItemPtr FindMe );
+ /* ------------------------------------------------------------------------ **
+ * Attempt to retrieve an entry from the cache.
+ *
+ * Input: CachePtr - A ponter to the cache that is to be searched.
+ * FindMe - A ubi_trItemPtr that indicates the key for which
+ * to search.
+ *
+ * Output: A pointer to the cache entry that was found, or NULL if no
+ * matching entry was found.
+ *
+ * Notes: This function also updates the hit ratio counters.
+ * The counters are unsigned short. If the number of cache tries
+ * reaches 32768, then both the number of tries and the number of
+ * hits are divided by two. This prevents the counters from
+ * overflowing. See the comments in ubi_cacheHitRatio() for
+ * additional notes.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_trBool ubi_cacheDelete( ubi_cacheRootPtr CachePtr, ubi_trItemPtr DeleteMe );
+ /* ------------------------------------------------------------------------ **
+ * Find and delete the specified cache entry.
+ *
+ * Input: CachePtr - A pointer to the cache.
+ * DeleteMe - The key of the entry to be deleted.
+ *
+ * Output: TRUE if the entry was found & freed, else FALSE.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_trBool ubi_cacheReduce( ubi_cacheRootPtr CachePtr, unsigned long count );
+ /* ------------------------------------------------------------------------ **
+ * Remove <count> entries from the bottom of the cache.
+ *
+ * Input: CachePtr - A pointer to the cache which is to be reduced in
+ * size.
+ * count - The number of entries to remove.
+ *
+ * Output: The function will return TRUE if <count> entries were removed,
+ * else FALSE. A return value of FALSE should indicate that
+ * there were less than <count> entries in the cache, and that the
+ * cache is now empty.
+ *
+ * Notes: This function forces a reduction in the number of cache entries
+ * without requiring that the MaxMemory or MaxEntries values be
+ * changed.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+unsigned long ubi_cacheSetMaxEntries( ubi_cacheRootPtr CachePtr,
+ unsigned long NewSize );
+ /* ------------------------------------------------------------------------ **
+ * Change the maximum number of entries allowed to exist in the cache.
+ *
+ * Input: CachePtr - A pointer to the cache to be modified.
+ * NewSize - The new maximum number of cache entries.
+ *
+ * Output: The maximum number of entries previously allowed to exist in
+ * the cache.
+ *
+ * Notes: If the new size is less than the old size, this function will
+ * trim the cache (remove excess entries).
+ * - A value of zero indicates an unlimited number of entries.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+unsigned long ubi_cacheSetMaxMemory( ubi_cacheRootPtr CachePtr,
+ unsigned long NewSize );
+ /* ------------------------------------------------------------------------ **
+ * Change the maximum amount of memory to be used for storing cache
+ * entries.
+ *
+ * Input: CachePtr - A pointer to the cache to be modified.
+ * NewSize - The new cache memory size.
+ *
+ * Output: The previous maximum memory size.
+ *
+ * Notes: If the new size is less than the old size, this function will
+ * trim the cache (remove excess entries).
+ * - A value of zero indicates that the cache has no memory limit.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+int ubi_cacheHitRatio( ubi_cacheRootPtr CachePtr );
+ /* ------------------------------------------------------------------------ **
+ * Returns a value that is 10,000 times the slightly weighted average hit
+ * ratio for the cache.
+ *
+ * Input: CachePtr - Pointer to the cache to be queried.
+ *
+ * Output: An integer that is 10,000 times the number of successful
+ * cache hits divided by the number of cache lookups, or:
+ * (10000 * hits) / trys
+ * You can easily convert this to a float, or do something
+ * like this (where i is the return value of this function):
+ *
+ * printf( "Hit rate : %d.%02d%%\n", (i/100), (i%100) );
+ *
+ * Notes: I say "slightly-weighted", because the numerator and
+ * denominator are both accumulated in locations of type
+ * 'unsigned short'. If the number of cache trys becomes
+ * large enough, both are divided by two. (See function
+ * ubi_cacheGet().)
+ * Dividing both numerator and denominator by two does not
+ * change the ratio (much...it is an integer divide), but it
+ * does mean that subsequent increments to either counter will
+ * have twice as much significance as previous ones.
+ *
+ * - The value returned by this function will be in the range
+ * [0..10000] because ( 0 <= cache_hits <= cache_trys ) will
+ * always be true.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+/* -------------------------------------------------------------------------- */
+#endif /* ubi_CACHE_H */
diff --git a/source/ubiqx/ubi_SplayTree.c b/source/ubiqx/ubi_SplayTree.c
new file mode 100644
index 00000000000..222506bd06b
--- /dev/null
+++ b/source/ubiqx/ubi_SplayTree.c
@@ -0,0 +1,512 @@
+/* ========================================================================== **
+ * ubi_SplayTree.c
+ *
+ * Copyright (C) 1993-1998 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ * -------------------------------------------------------------------------- **
+ *
+ * This module implements "splay" trees. Splay trees are binary trees
+ * that are rearranged (splayed) whenever a node is accessed. The
+ * splaying process *tends* to make the tree bushier (improves balance),
+ * and the nodes that are accessed most frequently *tend* to be closer to
+ * the top.
+ *
+ * References: "Self-Adjusting Binary Search Trees", by Daniel Sleator and
+ * Robert Tarjan. Journal of the Association for Computing
+ * Machinery Vol 32, No. 3, July 1985 pp. 652-686
+ *
+ * See also: http://www.cs.cmu.edu/~sleator/
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * Log: ubi_SplayTree.c,v
+ * Revision 4.5 2000/01/08 23:26:49 crh
+ * Added ubi_trSplay() macro, which does a type cast for us.
+ *
+ * Revision 4.4 1998/06/04 21:29:27 crh
+ * Upper-cased defined constants (eg UBI_BINTREE_H) in some header files.
+ * This is more "standard", and is what people expect. Weird, eh?
+ *
+ * Revision 4.3 1998/06/03 17:45:05 crh
+ * Further fiddling with sys_include.h. It's now in ubi_BinTree.h which is
+ * included by all of the binary tree files.
+ *
+ * Also fixed some warnings produced by lint on Irix 6.2, which doesn't seem
+ * to like syntax like this:
+ *
+ * if( (a = b) )
+ *
+ * The fix was to change lines like the above to:
+ *
+ * if( 0 != (a=b) )
+ *
+ * Which means the same thing.
+ *
+ * Reminder: Some of the ubi_tr* macros in ubi_BinTree.h are redefined in
+ * ubi_AVLtree.h and ubi_SplayTree.h. This allows easy swapping
+ * of tree types by simply changing a header. Unfortunately, the
+ * macro redefinitions in ubi_AVLtree.h and ubi_SplayTree.h will
+ * conflict if used together. You must either choose a single tree
+ * type, or use the underlying function calls directly. Compare
+ * the two header files for more information.
+ *
+ * Revision 4.2 1998/06/02 01:29:14 crh
+ * Changed ubi_null.h to sys_include.h to make it more generic.
+ *
+ * Revision 4.1 1998/05/20 04:37:54 crh
+ * The C file now includes ubi_null.h. See ubi_null.h for more info.
+ *
+ * Revision 4.0 1998/03/10 03:41:33 crh
+ * Minor comment changes. The revision number is now 4.0 to match the
+ * BinTree and AVLtree modules.
+ *
+ * Revision 2.7 1998/01/24 06:37:08 crh
+ * Added a URL for more information.
+ *
+ * Revision 2.6 1997/12/23 04:01:12 crh
+ * In this version, all constants & macros defined in the header file have
+ * the ubi_tr prefix. Also cleaned up anything that gcc complained about
+ * when run with '-pedantic -fsyntax-only -Wall'.
+ *
+ * Revision 2.5 1997/07/26 04:15:42 crh
+ * + Cleaned up a few minor syntax annoyances that gcc discovered for me.
+ * + Changed ubi_TRUE and ubi_FALSE to ubi_trTRUE and ubi_trFALSE.
+ *
+ * Revision 2.4 1997/06/03 04:42:21 crh
+ * Changed TRUE and FALSE to ubi_TRUE and ubi_FALSE to avoid causing
+ * problems.
+ *
+ * Revision 2.3 1995/10/03 22:19:07 CRH
+ * Ubisized!
+ * Also, added the function ubi_sptSplay().
+ *
+ * Revision 2.1 95/03/09 23:54:42 CRH
+ * Added the ModuleID static string and function. These modules are now
+ * self-identifying.
+ *
+ * Revision 2.0 95/02/27 22:34:46 CRH
+ * This module was updated to match the interface changes made to the
+ * ubi_BinTree module. In particular, the interface to the Locate() function
+ * has changed. See ubi_BinTree for more information on changes and new
+ * functions.
+ *
+ * The revision number was also upped to match ubi_BinTree.
+ *
+ * Revision 1.1 93/10/18 20:35:16 CRH
+ * I removed the hard-coded logical device names from the include file
+ * specifications. CRH
+ *
+ * Revision 1.0 93/10/15 23:00:15 CRH
+ * With this revision, I have added a set of #define's that provide a single,
+ * standard API to all existing tree modules. Until now, each of the three
+ * existing modules had a different function and typedef prefix, as follows:
+ *
+ * Module Prefix
+ * ubi_BinTree ubi_bt
+ * ubi_AVLtree ubi_avl
+ * ubi_SplayTree ubi_spt
+ *
+ * To further complicate matters, only those portions of the base module
+ * (ubi_BinTree) that were superceeded in the new module had the new names.
+ * For example, if you were using ubi_SplayTree, the locate function was
+ * called "ubi_sptLocate", but the next and previous functions remained
+ * "ubi_btNext" and "ubi_btPrev".
+ *
+ * This was not too terrible if you were familiar with the modules and knew
+ * exactly which tree model you wanted to use. If you wanted to be able to
+ * change modules (for speed comparisons, etc), things could get messy very
+ * quickly.
+ *
+ * So, I have added a set of defined names that get redefined in any of the
+ * descendant modules. To use this standardized interface in your code,
+ * simply replace all occurances of "ubi_bt", "ubi_avl", and "ubi_spt" with
+ * "ubi_tr". The "ubi_tr" names will resolve to the correct function or
+ * datatype names for the module that you are using. Just remember to
+ * include the header for that module in your program file. Because these
+ * names are handled by the preprocessor, there is no added run-time
+ * overhead.
+ *
+ * Note that the original names do still exist, and can be used if you wish
+ * to write code directly to a specific module. This should probably only be
+ * done if you are planning to implement a new descendant type, such as
+ * red/black trees. CRH
+ *
+ * Revision 0.1 93/04/25 22:03:32 CRH
+ * Simply changed the <exec/types.h> #include reference the .c file to
+ * use <stdlib.h> instead. The latter is portable, the former is not.
+ *
+ * Revision 0.0 93/04/21 23:05:52 CRH
+ * Initial version, written by Christopher R. Hertel.
+ * This module implements Splay Trees using the ubi_BinTree module as a basis.
+ *
+ * ========================================================================== **
+ */
+
+#include "ubi_SplayTree.h" /* Header for THIS module. */
+
+/* ========================================================================== **
+ * Static data.
+ */
+
+static char ModuleID[] = "ubi_SplayTree\n\
+\tRevision: 4.5 \n\
+\tDate: 2000/01/08 23:26:49 \n\
+\tAuthor: crh \n";
+
+
+/* ========================================================================== **
+ * Private functions...
+ */
+
+static void Rotate( ubi_btNodePtr p )
+ /* ------------------------------------------------------------------------ **
+ * This function performs a single rotation, moving node *p up one level
+ * in the tree.
+ *
+ * Input: p - a pointer to an ubi_btNode in a tree.
+ *
+ * Output: None.
+ *
+ * Notes: This implements a single rotation in either direction (left
+ * or right). This is the basic building block of all splay
+ * tree rotations.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_btNodePtr parentp;
+ ubi_btNodePtr tmp;
+ char way;
+ char revway;
+
+ parentp = p->Link[ubi_trPARENT]; /* Find parent. */
+
+ if( parentp ) /* If no parent, then we're already the root. */
+ {
+ way = p->gender;
+ revway = ubi_trRevWay(way);
+ tmp = p->Link[(int)revway];
+
+ parentp->Link[(int)way] = tmp;
+ if( tmp )
+ {
+ tmp->Link[ubi_trPARENT] = parentp;
+ tmp->gender = way;
+ }
+
+ tmp = parentp->Link[ubi_trPARENT];
+ p->Link[ubi_trPARENT] = tmp;
+ p->gender = parentp->gender;
+ if( tmp )
+ tmp->Link[(int)(p->gender)] = p;
+
+ parentp->Link[ubi_trPARENT] = p;
+ parentp->gender = revway;
+ p->Link[(int)revway] = parentp;
+ }
+ } /* Rotate */
+
+static ubi_btNodePtr Splay( ubi_btNodePtr SplayWithMe )
+ /* ------------------------------------------------------------------------ **
+ * Move the node indicated by SplayWithMe to the root of the tree by
+ * splaying the tree.
+ *
+ * Input: SplayWithMe - A pointer to an ubi_btNode within a tree.
+ *
+ * Output: A pointer to the root of the splay tree (i.e., the same as
+ * SplayWithMe).
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_btNodePtr parent;
+
+ while( NULL != (parent = SplayWithMe->Link[ubi_trPARENT]) )
+ {
+ if( parent->gender == SplayWithMe->gender ) /* Zig-Zig */
+ Rotate( parent );
+ else
+ {
+ if( ubi_trEQUAL != parent->gender ) /* Zig-Zag */
+ Rotate( SplayWithMe );
+ }
+ Rotate( SplayWithMe ); /* Zig */
+ } /* while */
+ return( SplayWithMe );
+ } /* Splay */
+
+/* ========================================================================== **
+ * Exported utilities.
+ */
+
+ubi_trBool ubi_sptInsert( ubi_btRootPtr RootPtr,
+ ubi_btNodePtr NewNode,
+ ubi_btItemPtr ItemPtr,
+ ubi_btNodePtr *OldNode )
+ /* ------------------------------------------------------------------------ **
+ * This function uses a non-recursive algorithm to add a new element to the
+ * splay tree.
+ *
+ * Input: RootPtr - a pointer to the ubi_btRoot structure that indicates
+ * the root of the tree to which NewNode is to be added.
+ * NewNode - a pointer to an ubi_btNode structure that is NOT
+ * part of any tree.
+ * ItemPtr - A pointer to the sort key that is stored within
+ * *NewNode. ItemPtr MUST point to information stored
+ * in *NewNode or an EXACT DUPLICATE. The key data
+ * indicated by ItemPtr is used to place the new node
+ * into the tree.
+ * OldNode - a pointer to an ubi_btNodePtr. When searching
+ * the tree, a duplicate node may be found. If
+ * duplicates are allowed, then the new node will
+ * be simply placed into the tree. If duplicates
+ * are not allowed, however, then one of two things
+ * may happen.
+ * 1) if overwritting *is not* allowed, this
+ * function will return FALSE (indicating that
+ * the new node could not be inserted), and
+ * *OldNode will point to the duplicate that is
+ * still in the tree.
+ * 2) if overwritting *is* allowed, then this
+ * function will swap **OldNode for *NewNode.
+ * In this case, *OldNode will point to the node
+ * that was removed (thus allowing you to free
+ * the node).
+ * ** If you are using overwrite mode, ALWAYS **
+ * ** check the return value of this parameter! **
+ * Note: You may pass NULL in this parameter, the
+ * function knows how to cope. If you do this,
+ * however, there will be no way to return a
+ * pointer to an old (ie. replaced) node (which is
+ * a problem if you are using overwrite mode).
+ *
+ * Output: a boolean value indicating success or failure. The function
+ * will return FALSE if the node could not be added to the tree.
+ * Such failure will only occur if duplicates are not allowed,
+ * nodes cannot be overwritten, AND a duplicate key was found
+ * within the tree.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_btNodePtr OtherP;
+
+ if( !(OldNode) )
+ OldNode = &OtherP;
+
+ if( ubi_btInsert( RootPtr, NewNode, ItemPtr, OldNode ) )
+ {
+ RootPtr->root = Splay( NewNode );
+ return( ubi_trTRUE );
+ }
+
+ /* Splay the unreplacable, duplicate keyed, unique, old node. */
+ RootPtr->root = Splay( (*OldNode) );
+ return( ubi_trFALSE );
+ } /* ubi_sptInsert */
+
+ubi_btNodePtr ubi_sptRemove( ubi_btRootPtr RootPtr, ubi_btNodePtr DeadNode )
+ /* ------------------------------------------------------------------------ **
+ * This function removes the indicated node from the tree.
+ *
+ * Input: RootPtr - A pointer to the header of the tree that contains
+ * the node to be removed.
+ * DeadNode - A pointer to the node that will be removed.
+ *
+ * Output: This function returns a pointer to the node that was removed
+ * from the tree (ie. the same as DeadNode).
+ *
+ * Note: The node MUST be in the tree indicated by RootPtr. If not,
+ * strange and evil things will happen to your trees.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_btNodePtr p;
+
+ (void)Splay( DeadNode ); /* Move dead node to root. */
+ if( NULL != (p = DeadNode->Link[ubi_trLEFT]) )
+ { /* If left subtree exists... */
+ ubi_btNodePtr q = DeadNode->Link[ubi_trRIGHT];
+
+ p->Link[ubi_trPARENT] = NULL; /* Left subtree node becomes root.*/
+ p->gender = ubi_trPARENT;
+ p = ubi_btLast( p ); /* Find rightmost left node... */
+ p->Link[ubi_trRIGHT] = q; /* ...attach right tree. */
+ if( q )
+ q->Link[ubi_trPARENT] = p;
+ RootPtr->root = Splay( p ); /* Resplay at p. */
+ }
+ else
+ {
+ if( NULL != (p = DeadNode->Link[ubi_trRIGHT]) )
+ { /* No left, but right subtree exists... */
+ p->Link[ubi_trPARENT] = NULL; /* Right subtree root becomes... */
+ p->gender = ubi_trPARENT; /* ...overall tree root. */
+ RootPtr->root = p;
+ }
+ else
+ RootPtr->root = NULL; /* No subtrees => empty tree. */
+ }
+
+ (RootPtr->count)--; /* Decrement node count. */
+ return( DeadNode ); /* Return pointer to pruned node. */
+ } /* ubi_sptRemove */
+
+ubi_btNodePtr ubi_sptLocate( ubi_btRootPtr RootPtr,
+ ubi_btItemPtr FindMe,
+ ubi_trCompOps CompOp )
+ /* ------------------------------------------------------------------------ **
+ * The purpose of ubi_btLocate() is to find a node or set of nodes given
+ * a target value and a "comparison operator". The Locate() function is
+ * more flexible and (in the case of trees that may contain dupicate keys)
+ * more precise than the ubi_btFind() function. The latter is faster,
+ * but it only searches for exact matches and, if the tree contains
+ * duplicates, Find() may return a pointer to any one of the duplicate-
+ * keyed records.
+ *
+ * Input:
+ * RootPtr - A pointer to the header of the tree to be searched.
+ * FindMe - An ubi_btItemPtr that indicates the key for which to
+ * search.
+ * CompOp - One of the following:
+ * CompOp Return a pointer to the node with
+ * ------ ---------------------------------
+ * ubi_trLT - the last key value that is less
+ * than FindMe.
+ * ubi_trLE - the first key matching FindMe, or
+ * the last key that is less than
+ * FindMe.
+ * ubi_trEQ - the first key matching FindMe.
+ * ubi_trGE - the first key matching FindMe, or the
+ * first key greater than FindMe.
+ * ubi_trGT - the first key greater than FindMe.
+ * Output:
+ * A pointer to the node matching the criteria listed above under
+ * CompOp, or NULL if no node matched the criteria.
+ *
+ * Notes:
+ * In the case of trees with duplicate keys, Locate() will behave as
+ * follows:
+ *
+ * Find: 3 Find: 3
+ * Keys: 1 2 2 2 3 3 3 3 3 4 4 Keys: 1 1 2 2 2 4 4 5 5 5 6
+ * ^ ^ ^ ^ ^
+ * LT EQ GT LE GE
+ *
+ * That is, when returning a pointer to a node with a key that is LESS
+ * THAN the target key (FindMe), Locate() will return a pointer to the
+ * LAST matching node.
+ * When returning a pointer to a node with a key that is GREATER
+ * THAN the target key (FindMe), Locate() will return a pointer to the
+ * FIRST matching node.
+ *
+ * See Also: ubi_btFind(), ubi_btFirstOf(), ubi_btLastOf().
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_btNodePtr p;
+
+ p = ubi_btLocate( RootPtr, FindMe, CompOp );
+ if( p )
+ RootPtr->root = Splay( p );
+ return( p );
+ } /* ubi_sptLocate */
+
+ubi_btNodePtr ubi_sptFind( ubi_btRootPtr RootPtr,
+ ubi_btItemPtr FindMe )
+ /* ------------------------------------------------------------------------ **
+ * This function performs a non-recursive search of a tree for any node
+ * matching a specific key.
+ *
+ * Input:
+ * RootPtr - a pointer to the header of the tree to be searched.
+ * FindMe - a pointer to the key value for which to search.
+ *
+ * Output:
+ * A pointer to a node with a key that matches the key indicated by
+ * FindMe, or NULL if no such node was found.
+ *
+ * Note: In a tree that allows duplicates, the pointer returned *might
+ * not* point to the (sequentially) first occurance of the
+ * desired key. In such a tree, it may be more useful to use
+ * ubi_sptLocate().
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_btNodePtr p;
+
+ p = ubi_btFind( RootPtr, FindMe );
+ if( p )
+ RootPtr->root = Splay( p );
+ return( p );
+ } /* ubi_sptFind */
+
+void ubi_sptSplay( ubi_btRootPtr RootPtr,
+ ubi_btNodePtr SplayMe )
+ /* ------------------------------------------------------------------------ **
+ * This function allows you to splay the tree at a given node, thus moving
+ * the node to the top of the tree.
+ *
+ * Input:
+ * RootPtr - a pointer to the header of the tree to be splayed.
+ * SplayMe - a pointer to a node within the tree. This will become
+ * the new root node.
+ * Output: None.
+ *
+ * Notes: This is an uncharacteristic function for this group of modules
+ * in that it provides access to the internal balancing routines,
+ * which would normally be hidden.
+ * Splaying the tree will not damage it (assuming that I've done
+ * *my* job), but there is overhead involved. I don't recommend
+ * that you use this function unless you understand the underlying
+ * Splay Tree principles involved.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ RootPtr->root = Splay( SplayMe );
+ } /* ubi_sptSplay */
+
+int ubi_sptModuleID( int size, char *list[] )
+ /* ------------------------------------------------------------------------ **
+ * Returns a set of strings that identify the module.
+ *
+ * Input: size - The number of elements in the array <list>.
+ * list - An array of pointers of type (char *). This array
+ * should, initially, be empty. This function will fill
+ * in the array with pointers to strings.
+ * Output: The number of elements of <list> that were used. If this value
+ * is less than <size>, the values of the remaining elements are
+ * not guaranteed.
+ *
+ * Notes: Please keep in mind that the pointers returned indicate strings
+ * stored in static memory. Don't free() them, don't write over
+ * them, etc. Just read them.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ if( size > 0 )
+ {
+ list[0] = ModuleID;
+ if( size > 1 )
+ return( 1 + ubi_btModuleID( --size, &(list[1]) ) );
+ return( 1 );
+ }
+ return( 0 );
+ } /* ubi_sptModuleID */
+
+/* ================================ The End ================================= */
+
diff --git a/source/ubiqx/ubi_SplayTree.h b/source/ubiqx/ubi_SplayTree.h
new file mode 100644
index 00000000000..e4fac796a91
--- /dev/null
+++ b/source/ubiqx/ubi_SplayTree.h
@@ -0,0 +1,377 @@
+#ifndef UBI_SPLAYTREE_H
+#define UBI_SPLAYTREE_H
+/* ========================================================================== **
+ * ubi_SplayTree.h
+ *
+ * Copyright (C) 1993-1998 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ * -------------------------------------------------------------------------- **
+ *
+ * This module implements "splay" trees. Splay trees are binary trees
+ * that are rearranged (splayed) whenever a node is accessed. The
+ * splaying process *tends* to make the tree bushier (improves balance),
+ * and the nodes that are accessed most frequently *tend* to be closer to
+ * the top.
+ *
+ * References: "Self-Adjusting Binary Search Trees", by Daniel Sleator and
+ * Robert Tarjan. Journal of the Association for Computing
+ * Machinery Vol 32, No. 3, July 1985 pp. 652-686
+ *
+ * See also: http://www.cs.cmu.edu/~sleator/
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * Log: ubi_SplayTree.h,v
+ * Revision 4.5 2000/01/08 23:26:49 crh
+ * Added ubi_trSplay() macro, which does a type cast for us.
+ *
+ * Revision 4.4 1998/06/04 21:29:27 crh
+ * Upper-cased defined constants (eg UBI_BINTREE_H) in some header files.
+ * This is more "standard", and is what people expect. Weird, eh?
+ *
+ * Revision 4.3 1998/06/03 17:45:05 crh
+ * Further fiddling with sys_include.h. It's now in ubi_BinTree.h which is
+ * included by all of the binary tree files.
+ *
+ * Also fixed some warnings produced by lint on Irix 6.2, which doesn't seem
+ * to like syntax like this:
+ *
+ * if( (a = b) )
+ *
+ * The fix was to change lines like the above to:
+ *
+ * if( 0 != (a=b) )
+ *
+ * Which means the same thing.
+ *
+ * Reminder: Some of the ubi_tr* macros in ubi_BinTree.h are redefined in
+ * ubi_AVLtree.h and ubi_SplayTree.h. This allows easy swapping
+ * of tree types by simply changing a header. Unfortunately, the
+ * macro redefinitions in ubi_AVLtree.h and ubi_SplayTree.h will
+ * conflict if used together. You must either choose a single tree
+ * type, or use the underlying function calls directly. Compare
+ * the two header files for more information.
+ *
+ * Revision 4.2 1998/06/02 01:29:14 crh
+ * Changed ubi_null.h to sys_include.h to make it more generic.
+ *
+ * Revision 4.1 1998/05/20 04:37:54 crh
+ * The C file now includes ubi_null.h. See ubi_null.h for more info.
+ *
+ * Revision 4.0 1998/03/10 03:40:57 crh
+ * Minor comment changes. The revision number is now 4.0 to match the
+ * BinTree and AVLtree modules.
+ *
+ * Revision 2.7 1998/01/24 06:37:57 crh
+ * Added a URL for more information.
+ *
+ * Revision 2.6 1997/12/23 04:02:20 crh
+ * In this version, all constants & macros defined in the header file have
+ * the ubi_tr prefix. Also cleaned up anything that gcc complained about
+ * when run with '-pedantic -fsyntax-only -Wall'.
+ *
+ * Revision 2.5 1997/07/26 04:15:46 crh
+ * + Cleaned up a few minor syntax annoyances that gcc discovered for me.
+ * + Changed ubi_TRUE and ubi_FALSE to ubi_trTRUE and ubi_trFALSE.
+ *
+ * Revision 2.4 1997/06/03 05:22:56 crh
+ * Changed TRUE and FALSE to ubi_TRUE and ubi_FALSE to avoid causing
+ * problems.
+ *
+ * Revision 2.3 1995/10/03 22:19:37 CRH
+ * Ubisized!
+ * Also, added the function ubi_sptSplay().
+ *
+ * Revision 2.1 95/03/09 23:55:04 CRH
+ * Added the ModuleID static string and function. These modules are now
+ * self-identifying.
+ *
+ * Revision 2.0 95/02/27 22:34:55 CRH
+ * This module was updated to match the interface changes made to the
+ * ubi_BinTree module. In particular, the interface to the Locate() function
+ * has changed. See ubi_BinTree for more information on changes and new
+ * functions.
+ *
+ * The revision number was also upped to match ubi_BinTree.
+ *
+ *
+ * Revision 1.0 93/10/15 22:59:36 CRH
+ * With this revision, I have added a set of #define's that provide a single,
+ * standard API to all existing tree modules. Until now, each of the three
+ * existing modules had a different function and typedef prefix, as follows:
+ *
+ * Module Prefix
+ * ubi_BinTree ubi_bt
+ * ubi_AVLtree ubi_avl
+ * ubi_SplayTree ubi_spt
+ *
+ * To further complicate matters, only those portions of the base module
+ * (ubi_BinTree) that were superceeded in the new module had the new names.
+ * For example, if you were using ubi_SplayTree, the locate function was
+ * called "ubi_sptLocate", but the next and previous functions remained
+ * "ubi_btNext" and "ubi_btPrev".
+ *
+ * This was not too terrible if you were familiar with the modules and knew
+ * exactly which tree model you wanted to use. If you wanted to be able to
+ * change modules (for speed comparisons, etc), things could get messy very
+ * quickly.
+ *
+ * So, I have added a set of defined names that get redefined in any of the
+ * descendant modules. To use this standardized interface in your code,
+ * simply replace all occurances of "ubi_bt", "ubi_avl", and "ubi_spt" with
+ * "ubi_tr". The "ubi_tr" names will resolve to the correct function or
+ * datatype names for the module that you are using. Just remember to
+ * include the header for that module in your program file. Because these
+ * names are handled by the preprocessor, there is no added run-time
+ * overhead.
+ *
+ * Note that the original names do still exist, and can be used if you wish
+ * to write code directly to a specific module. This should probably only be
+ * done if you are planning to implement a new descendant type, such as
+ * red/black trees. CRH
+ *
+ * Revision 0.0 93/04/21 23:07:13 CRH
+ * Initial version, written by Christopher R. Hertel.
+ * This module implements Splay Trees using the ubi_BinTree module as a basis.
+ *
+ * ========================================================================== **
+ */
+
+#include "ubi_BinTree.h" /* Base binary tree functions, types, etc. */
+
+/* ========================================================================== **
+ * Function prototypes...
+ */
+
+ubi_trBool ubi_sptInsert( ubi_btRootPtr RootPtr,
+ ubi_btNodePtr NewNode,
+ ubi_btItemPtr ItemPtr,
+ ubi_btNodePtr *OldNode );
+ /* ------------------------------------------------------------------------ **
+ * This function uses a non-recursive algorithm to add a new element to the
+ * splay tree.
+ *
+ * Input: RootPtr - a pointer to the ubi_btRoot structure that indicates
+ * the root of the tree to which NewNode is to be added.
+ * NewNode - a pointer to an ubi_btNode structure that is NOT
+ * part of any tree.
+ * ItemPtr - A pointer to the sort key that is stored within
+ * *NewNode. ItemPtr MUST point to information stored
+ * in *NewNode or an EXACT DUPLICATE. The key data
+ * indicated by ItemPtr is used to place the new node
+ * into the tree.
+ * OldNode - a pointer to an ubi_btNodePtr. When searching
+ * the tree, a duplicate node may be found. If
+ * duplicates are allowed, then the new node will
+ * be simply placed into the tree. If duplicates
+ * are not allowed, however, then one of two things
+ * may happen.
+ * 1) if overwritting *is not* allowed, this
+ * function will return FALSE (indicating that
+ * the new node could not be inserted), and
+ * *OldNode will point to the duplicate that is
+ * still in the tree.
+ * 2) if overwritting *is* allowed, then this
+ * function will swap **OldNode for *NewNode.
+ * In this case, *OldNode will point to the node
+ * that was removed (thus allowing you to free
+ * the node).
+ * ** If you are using overwrite mode, ALWAYS **
+ * ** check the return value of this parameter! **
+ * Note: You may pass NULL in this parameter, the
+ * function knows how to cope. If you do this,
+ * however, there will be no way to return a
+ * pointer to an old (ie. replaced) node (which is
+ * a problem if you are using overwrite mode).
+ *
+ * Output: a boolean value indicating success or failure. The function
+ * will return FALSE if the node could not be added to the tree.
+ * Such failure will only occur if duplicates are not allowed,
+ * nodes cannot be overwritten, AND a duplicate key was found
+ * within the tree.
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_sptRemove( ubi_btRootPtr RootPtr, ubi_btNodePtr DeadNode );
+ /* ------------------------------------------------------------------------ **
+ * This function removes the indicated node from the tree.
+ *
+ * Input: RootPtr - A pointer to the header of the tree that contains
+ * the node to be removed.
+ * DeadNode - A pointer to the node that will be removed.
+ *
+ * Output: This function returns a pointer to the node that was removed
+ * from the tree (ie. the same as DeadNode).
+ *
+ * Note: The node MUST be in the tree indicated by RootPtr. If not,
+ * strange and evil things will happen to your trees.
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_sptLocate( ubi_btRootPtr RootPtr,
+ ubi_btItemPtr FindMe,
+ ubi_trCompOps CompOp );
+ /* ------------------------------------------------------------------------ **
+ * The purpose of ubi_btLocate() is to find a node or set of nodes given
+ * a target value and a "comparison operator". The Locate() function is
+ * more flexible and (in the case of trees that may contain dupicate keys)
+ * more precise than the ubi_btFind() function. The latter is faster,
+ * but it only searches for exact matches and, if the tree contains
+ * duplicates, Find() may return a pointer to any one of the duplicate-
+ * keyed records.
+ *
+ * Input:
+ * RootPtr - A pointer to the header of the tree to be searched.
+ * FindMe - An ubi_btItemPtr that indicates the key for which to
+ * search.
+ * CompOp - One of the following:
+ * CompOp Return a pointer to the node with
+ * ------ ---------------------------------
+ * ubi_trLT - the last key value that is less
+ * than FindMe.
+ * ubi_trLE - the first key matching FindMe, or
+ * the last key that is less than
+ * FindMe.
+ * ubi_trEQ - the first key matching FindMe.
+ * ubi_trGE - the first key matching FindMe, or the
+ * first key greater than FindMe.
+ * ubi_trGT - the first key greater than FindMe.
+ * Output:
+ * A pointer to the node matching the criteria listed above under
+ * CompOp, or NULL if no node matched the criteria.
+ *
+ * Notes:
+ * In the case of trees with duplicate keys, Locate() will behave as
+ * follows:
+ *
+ * Find: 3 Find: 3
+ * Keys: 1 2 2 2 3 3 3 3 3 4 4 Keys: 1 1 2 2 2 4 4 5 5 5 6
+ * ^ ^ ^ ^ ^
+ * LT EQ GT LE GE
+ *
+ * That is, when returning a pointer to a node with a key that is LESS
+ * THAN the target key (FindMe), Locate() will return a pointer to the
+ * LAST matching node.
+ * When returning a pointer to a node with a key that is GREATER
+ * THAN the target key (FindMe), Locate() will return a pointer to the
+ * FIRST matching node.
+ *
+ * See Also: ubi_btFind(), ubi_btFirstOf(), ubi_btLastOf().
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_sptFind( ubi_btRootPtr RootPtr,
+ ubi_btItemPtr FindMe );
+ /* ------------------------------------------------------------------------ **
+ * This function performs a non-recursive search of a tree for any node
+ * matching a specific key.
+ *
+ * Input:
+ * RootPtr - a pointer to the header of the tree to be searched.
+ * FindMe - a pointer to the key value for which to search.
+ *
+ * Output:
+ * A pointer to a node with a key that matches the key indicated by
+ * FindMe, or NULL if no such node was found.
+ *
+ * Note: In a tree that allows duplicates, the pointer returned *might
+ * not* point to the (sequentially) first occurance of the
+ * desired key. In such a tree, it may be more useful to use
+ * ubi_sptLocate().
+ * ------------------------------------------------------------------------ **
+ */
+
+void ubi_sptSplay( ubi_btRootPtr RootPtr,
+ ubi_btNodePtr SplayMe );
+ /* ------------------------------------------------------------------------ **
+ * This function allows you to splay the tree at a given node, thus moving
+ * the node to the top of the tree.
+ *
+ * Input:
+ * RootPtr - a pointer to the header of the tree to be splayed.
+ * SplayMe - a pointer to a node within the tree. This will become
+ * the new root node.
+ * Output: None.
+ *
+ * Notes: This is an uncharacteristic function for this group of modules
+ * in that it provides access to the internal balancing routines,
+ * which would normally be hidden.
+ * Splaying the tree will not damage it (assuming that I've done
+ * *my* job), but there is overhead involved. I don't recommend
+ * that you use this function unless you understand the underlying
+ * Splay Tree principles involved.
+ * ------------------------------------------------------------------------ **
+ */
+
+int ubi_sptModuleID( int size, char *list[] );
+ /* ------------------------------------------------------------------------ **
+ * Returns a set of strings that identify the module.
+ *
+ * Input: size - The number of elements in the array <list>.
+ * list - An array of pointers of type (char *). This array
+ * should, initially, be empty. This function will fill
+ * in the array with pointers to strings.
+ * Output: The number of elements of <list> that were used. If this value
+ * is less than <size>, the values of the remaining elements are
+ * not guaranteed.
+ *
+ * Notes: Please keep in mind that the pointers returned indicate strings
+ * stored in static memory. Don't free() them, don't write over
+ * them, etc. Just read them.
+ * ------------------------------------------------------------------------ **
+ */
+
+/* -------------------------------------------------------------------------- **
+ * Masquarade...
+ *
+ * This set of defines allows you to write programs that will use any of the
+ * implemented binary tree modules (currently BinTree, AVLtree, and SplayTree).
+ * Instead of using ubi_bt..., use ubi_tr..., and select the tree type by
+ * including the appropriate module header.
+ */
+
+#undef ubi_trInsert
+#undef ubi_trRemove
+#undef ubi_trLocate
+#undef ubi_trFind
+#undef ubi_trModuleID
+
+#define ubi_trInsert( Rp, Nn, Ip, On ) \
+ ubi_sptInsert( (ubi_btRootPtr)(Rp), (ubi_btNodePtr)(Nn), \
+ (ubi_btItemPtr)(Ip), (ubi_btNodePtr *)(On) )
+
+#define ubi_trRemove( Rp, Dn ) \
+ ubi_sptRemove( (ubi_btRootPtr)(Rp), (ubi_btNodePtr)(Dn) )
+
+#define ubi_trLocate( Rp, Ip, Op ) \
+ ubi_sptLocate( (ubi_btRootPtr)(Rp), \
+ (ubi_btItemPtr)(Ip), \
+ (ubi_trCompOps)(Op) )
+
+#define ubi_trFind( Rp, Ip ) \
+ ubi_sptFind( (ubi_btRootPtr)(Rp), (ubi_btItemPtr)(Ip) )
+
+#define ubi_trSplay( Rp, Sm ) \
+ ubi_sptSplay( (ubi_btRootPtr)(Rp), (ubi_btNodePtr)(Sm) )
+
+#define ubi_trModuleID( s, l ) ubi_sptModuleID( s, l )
+
+/* ================================ The End ================================= */
+#endif /* UBI_SPLAYTREE_H */
diff --git a/source/ubiqx/ubi_dLinkList.c b/source/ubiqx/ubi_dLinkList.c
new file mode 100644
index 00000000000..eb95033c695
--- /dev/null
+++ b/source/ubiqx/ubi_dLinkList.c
@@ -0,0 +1,171 @@
+/* ========================================================================== **
+ * ubi_dLinkList.c
+ *
+ * Copyright (C) 1997, 1998 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ * -------------------------------------------------------------------------- **
+ * This module implements simple doubly-linked lists.
+ * -------------------------------------------------------------------------- **
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * Log: ubi_dLinkList.c,v
+ * Revision 0.11 1999/06/19 16:58:06 crh
+ * Renamed the ubi_slRemove() function in ubi_sLinkList to
+ * ubi_slRemoveNext(). I was bothered by the fact that it didn't
+ * match the functionality of the ubi_dlRemove() function in
+ * ubi_dLinkList. The new name is more 'correct'.
+ *
+ * Revision 0.10 1998/07/24 07:30:20 crh
+ * Added the ubi_dlNewList() macro.
+ *
+ * Revision 0.9 1998/06/04 21:29:27 crh
+ * Upper-cased defined constants (eg UBI_BINTREE_H) in some header files.
+ * This is more "standard", and is what people expect. Weird, eh?
+ *
+ * Revision 0.8 1998/06/03 18:06:03 crh
+ * Further fiddling with sys_include.h, which has been moved from the .c file
+ * to the .h file.
+ *
+ * Revision 0.7 1998/06/02 01:38:47 crh
+ * Changed include file name from ubi_null.h to sys_include.h to make it
+ * more generic.
+ *
+ * Revision 0.6 1998/05/20 04:38:05 crh
+ * The C file now includes ubi_null.h. See ubi_null.h for more info.
+ *
+ * Revision 0.5 1998/03/10 02:55:00 crh
+ * Simplified the code and added macros for stack & queue manipulations.
+ *
+ * Revision 0.4 1998/01/03 01:53:56 crh
+ * Added ubi_dlCount() macro.
+ *
+ * Revision 0.3 1997/10/15 03:05:39 crh
+ * Added some handy type casting to the macros. Added AddHere and RemThis
+ * macros.
+ *
+ * Revision 0.2 1997/10/08 03:07:21 crh
+ * Fixed a few forgotten link-ups in Insert(), and fixed the AddHead()
+ * macro, which was passing the wrong value for <After> to Insert().
+ *
+ * Revision 0.1 1997/10/07 04:34:07 crh
+ * Initial Revision.
+ *
+ * -------------------------------------------------------------------------- **
+ * This module is similar to the ubi_sLinkList module, but it is neither a
+ * descendant type nor an easy drop-in replacement for the latter. One key
+ * difference is that the ubi_dlRemove() function removes the indicated node,
+ * while the ubi_slRemoveNext() function (in ubi_sLinkList) removes the node
+ * *following* the indicated node.
+ *
+ * ========================================================================== **
+ */
+
+#include "ubi_dLinkList.h" /* Header for *this* module. */
+
+/* ========================================================================== **
+ * Functions...
+ */
+
+ubi_dlListPtr ubi_dlInitList( ubi_dlListPtr ListPtr )
+ /* ------------------------------------------------------------------------ **
+ * Initialize a doubly-linked list header.
+ *
+ * Input: ListPtr - A pointer to the list structure that is to be
+ * initialized for use.
+ *
+ * Output: A pointer to the initialized list header (i.e., same as
+ * <ListPtr>).
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ListPtr->Head = NULL;
+ ListPtr->Tail = NULL;
+ ListPtr->count = 0;
+ return( ListPtr );
+ } /* ubi_dlInitList */
+
+ubi_dlNodePtr ubi_dlInsert( ubi_dlListPtr ListPtr,
+ ubi_dlNodePtr New,
+ ubi_dlNodePtr After )
+ /* ------------------------------------------------------------------------ **
+ * Insert a new node into the list.
+ *
+ * Input: ListPtr - A pointer to the list into which the node is to
+ * be inserted.
+ * New - Pointer to the new node.
+ * After - NULL, or a pointer to a node that is already in the
+ * list.
+ * If NULL, then <New> will be added at the head of the
+ * list, else it will be added following <After>.
+ *
+ * Output: A pointer to the node that was inserted into the list (i.e.,
+ * the same as <New>).
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_dlNodePtr PredNode = After ? After : (ubi_dlNodePtr)ListPtr;
+
+ New->Next = PredNode->Next;
+ New->Prev = After;
+ PredNode->Next = New;
+ if( New->Next )
+ New->Next->Prev = New;
+ else
+ ListPtr->Tail = New;
+
+ (ListPtr->count)++;
+
+ return( New );
+ } /* ubi_dlInsert */
+
+ubi_dlNodePtr ubi_dlRemove( ubi_dlListPtr ListPtr, ubi_dlNodePtr Old )
+ /* ------------------------------------------------------------------------ **
+ * Remove a node from the list.
+ *
+ * Input: ListPtr - A pointer to the list from which <Old> is to be
+ * removed.
+ * Old - A pointer to the node that is to be removed from the
+ * list.
+ *
+ * Output: A pointer to the node that was removed (i.e., <Old>).
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ if( Old )
+ {
+ if( Old->Next )
+ Old->Next->Prev = Old->Prev;
+ else
+ ListPtr->Tail = Old->Prev;
+
+ if( Old->Prev )
+ Old->Prev->Next = Old->Next;
+ else
+ ListPtr->Head = Old->Next;
+
+ (ListPtr->count)--;
+ }
+
+ return( Old );
+ } /* ubi_dlRemove */
+
+/* ================================ The End ================================= */
diff --git a/source/ubiqx/ubi_dLinkList.h b/source/ubiqx/ubi_dLinkList.h
new file mode 100644
index 00000000000..682e566ee67
--- /dev/null
+++ b/source/ubiqx/ubi_dLinkList.h
@@ -0,0 +1,242 @@
+#ifndef UBI_DLINKLIST_H
+#define UBI_DLINKLIST_H
+/* ========================================================================== **
+ * ubi_dLinkList.h
+ *
+ * Copyright (C) 1997, 1998 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ * -------------------------------------------------------------------------- **
+ * This module implements simple doubly-linked lists.
+ * -------------------------------------------------------------------------- **
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * Log: ubi_dLinkList.h,v
+ * Revision 0.11 1999/06/19 16:58:06 crh
+ * Renamed the ubi_slRemove() function in ubi_sLinkList to
+ * ubi_slRemoveNext(). I was bothered by the fact that it didn't
+ * match the functionality of the ubi_dlRemove() function in
+ * ubi_dLinkList. The new name is more 'correct'.
+ *
+ * Revision 0.10 1998/07/24 07:30:20 crh
+ * Added the ubi_dlNewList() macro.
+ *
+ * Revision 0.9 1998/06/04 21:29:27 crh
+ * Upper-cased defined constants (eg UBI_BINTREE_H) in some header files.
+ * This is more "standard", and is what people expect. Weird, eh?
+ *
+ * Revision 0.8 1998/06/03 18:06:03 crh
+ * Further fiddling with sys_include.h, which has been moved from the .c file
+ * to the .h file.
+ *
+ * Revision 0.7 1998/06/02 01:38:47 crh
+ * Changed include file name from ubi_null.h to sys_include.h to make it
+ * more generic.
+ *
+ * Revision 0.6 1998/05/20 04:38:05 crh
+ * The C file now includes ubi_null.h. See ubi_null.h for more info.
+ *
+ * Revision 0.5 1998/03/10 02:54:04 crh
+ * Simplified the code and added macros for stack & queue manipulations.
+ *
+ * Revision 0.4 1998/01/03 01:53:44 crh
+ * Added ubi_dlCount() macro.
+ *
+ * Revision 0.3 1997/10/15 03:04:31 crh
+ * Added some handy type casting to the macros. Added AddHere and RemThis
+ * macros.
+ *
+ * Revision 0.2 1997/10/08 03:08:16 crh
+ * Fixed a few forgotten link-ups in Insert(), and fixed the AddHead()
+ * macro, which was passing the wrong value for <After> to Insert().
+ *
+ * Revision 0.1 1997/10/07 04:34:38 crh
+ * Initial Revision.
+ *
+ * -------------------------------------------------------------------------- **
+ * This module is similar to the ubi_sLinkList module, but it is neither a
+ * descendant type nor an easy drop-in replacement for the latter. One key
+ * difference is that the ubi_dlRemove() function removes the indicated node,
+ * while the ubi_slRemoveNext() function (in ubi_sLinkList) removes the node
+ * *following* the indicated node.
+ *
+ * ========================================================================== **
+ */
+
+#include "sys_include.h" /* System-specific includes. */
+
+/* ========================================================================== **
+ * Typedefs...
+ *
+ * ubi_dlNode - This is the basic node structure.
+ * ubi_dlNodePtr - Pointer to a node.
+ * ubi_dlList - This is the list header structure.
+ * ubi_dlListPtr - Pointer to a List (i.e., a list header structure).
+ *
+ */
+
+typedef struct ubi_dlListNode
+ {
+ struct ubi_dlListNode *Next;
+ struct ubi_dlListNode *Prev;
+ } ubi_dlNode;
+
+typedef ubi_dlNode *ubi_dlNodePtr;
+
+typedef struct
+ {
+ ubi_dlNodePtr Head;
+ ubi_dlNodePtr Tail;
+ unsigned long count;
+ } ubi_dlList;
+
+typedef ubi_dlList *ubi_dlListPtr;
+
+/* ========================================================================== **
+ * Macros...
+ *
+ * ubi_dlNewList - Macro used to declare and initialize a new list in one
+ * swell foop. It is used when defining a variable of
+ * type ubi_dlList. The definition
+ * static ubi_dlNewList( gerbil );
+ * is translated to
+ * static ubi_dlList gerbil[1] = {{ NULL, NULL, 0 }};
+ *
+ * ubi_dlCount - Return the number of entries currently in the list.
+ *
+ * ubi_dlAddHead - Add a new node at the head of the list.
+ * ubi_dlAddNext - Add a node following the given node.
+ * ubi_dlAddTail - Add a new node at the tail of the list.
+ * Note: AddTail evaluates the L parameter twice.
+ *
+ * ubi_dlRemHead - Remove the node at the head of the list, if any.
+ * Note: RemHead evaluates the L parameter twice.
+ * ubi_dlRemThis - Remove the indicated node.
+ * ubi_dlRemTail - Remove the node at the tail of the list, if any.
+ * Note: RemTail evaluates the L parameter twice.
+ *
+ * ubi_dlFirst - Return a pointer to the first node in the list, if any.
+ * ubi_dlLast - Return a pointer to the last node in the list, if any.
+ * ubi_dlNext - Given a node, return a pointer to the next node.
+ * ubi_dlPrev - Given a node, return a pointer to the previous node.
+ *
+ * ubi_dlPush - Add a node at the head of the list (synonym of AddHead).
+ * ubi_dlPop - Remove a node at the head of the list (synonym of RemHead).
+ * ubi_dlEnqueue - Add a node at the tail of the list (sysnonym of AddTail).
+ * ubi_dlDequeue - Remove a node at the head of the list (synonym of RemHead).
+ *
+ * Note that all of these provide type casting of the parameters. The
+ * Add and Rem macros are nothing more than nice front-ends to the
+ * Insert and Remove operations.
+ *
+ * Also note that the First, Next and Last macros do no parameter checking!
+ *
+ */
+
+#define ubi_dlNewList( L ) ubi_dlList (L)[1] = {{ NULL, NULL, 0 }}
+
+#define ubi_dlCount( L ) (((ubi_dlListPtr)(L))->count)
+
+#define ubi_dlAddHead( L, N ) \
+ ubi_dlInsert( (ubi_dlListPtr)(L), (ubi_dlNodePtr)(N), NULL )
+
+#define ubi_dlAddNext( L, N, A ) \
+ ubi_dlInsert( (ubi_dlListPtr)(L), \
+ (ubi_dlNodePtr)(N), \
+ (ubi_dlNodePtr)(A) )
+
+#define ubi_dlAddTail( L, N ) \
+ ubi_dlInsert( (ubi_dlListPtr)(L), \
+ (ubi_dlNodePtr)(N), \
+ (((ubi_dlListPtr)(L))->Tail) )
+
+#define ubi_dlRemHead( L ) ubi_dlRemove( (ubi_dlListPtr)(L), \
+ (((ubi_dlListPtr)(L))->Head) )
+
+#define ubi_dlRemThis( L, N ) ubi_dlRemove( (ubi_dlListPtr)(L), \
+ (ubi_dlNodePtr)(N) )
+
+#define ubi_dlRemTail( L ) ubi_dlRemove( (ubi_dlListPtr)(L), \
+ (((ubi_dlListPtr)(L))->Tail) )
+
+#define ubi_dlFirst( L ) (((ubi_dlListPtr)(L))->Head)
+
+#define ubi_dlLast( L ) (((ubi_dlListPtr)(L))->Tail)
+
+#define ubi_dlNext( N ) (((ubi_dlNodePtr)(N))->Next)
+
+#define ubi_dlPrev( N ) (((ubi_dlNodePtr)(N))->Prev)
+
+#define ubi_dlPush ubi_dlAddHead
+#define ubi_dlPop ubi_dlRemHead
+#define ubi_dlEnqueue ubi_dlAddTail
+#define ubi_dlDequeue ubi_dlRemHead
+
+/* ========================================================================== **
+ * Function prototypes...
+ */
+
+ubi_dlListPtr ubi_dlInitList( ubi_dlListPtr ListPtr );
+ /* ------------------------------------------------------------------------ **
+ * Initialize a doubly-linked list header.
+ *
+ * Input: ListPtr - A pointer to the list structure that is to be
+ * initialized for use.
+ *
+ * Output: A pointer to the initialized list header (i.e., same as
+ * <ListPtr>).
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_dlNodePtr ubi_dlInsert( ubi_dlListPtr ListPtr,
+ ubi_dlNodePtr New,
+ ubi_dlNodePtr After );
+ /* ------------------------------------------------------------------------ **
+ * Insert a new node into the list.
+ *
+ * Input: ListPtr - A pointer to the list into which the node is to
+ * be inserted.
+ * New - Pointer to the new node.
+ * After - NULL, or a pointer to a node that is already in the
+ * list.
+ * If NULL, then <New> will be added at the head of the
+ * list, else it will be added following <After>.
+ *
+ * Output: A pointer to the node that was inserted into the list (i.e.,
+ * the same as <New>).
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_dlNodePtr ubi_dlRemove( ubi_dlListPtr ListPtr, ubi_dlNodePtr Old );
+ /* ------------------------------------------------------------------------ **
+ * Remove a node from the list.
+ *
+ * Input: ListPtr - A pointer to the list from which <Old> is to be
+ * removed.
+ * Old - A pointer to the node that is to be removed from the
+ * list.
+ *
+ * Output: A pointer to the node that was removed (i.e., <Old>).
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+/* ================================ The End ================================= */
+#endif /* UBI_DLINKLIST_H */
diff --git a/source/ubiqx/ubi_sLinkList.c b/source/ubiqx/ubi_sLinkList.c
new file mode 100644
index 00000000000..ff75931b470
--- /dev/null
+++ b/source/ubiqx/ubi_sLinkList.c
@@ -0,0 +1,187 @@
+/* ========================================================================== **
+ * ubi_sLinkList.c
+ *
+ * Copyright (C) 1997, 1998 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ * -------------------------------------------------------------------------- **
+ * This module implements a simple singly-linked list.
+ * -------------------------------------------------------------------------- **
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * Log: ubi_sLinkList.c,v
+ * Revision 0.10 1999/06/19 16:58:06 crh
+ * Renamed the ubi_slRemove() function in ubi_sLinkList to
+ * ubi_slRemoveNext(). I was bothered by the fact that it didn't
+ * match the functionality of the ubi_dlRemove() function in
+ * ubi_dLinkList. The new name is more 'correct'.
+ *
+ * Revision 0.9 1998/07/24 07:30:20 crh
+ * Added the ubi_slNewList() macro.
+ *
+ * Revision 0.8 1998/06/04 21:29:27 crh
+ * Upper-cased defined constants (eg UBI_BINTREE_H) in some header files.
+ * This is more "standard", and is what people expect. Weird, eh?
+ *
+ * Revision 0.7 1998/06/03 18:06:03 crh
+ * Further fiddling with sys_include.h, which has been moved from the .c file
+ * to the .h file.
+ *
+ * Revision 0.6 1998/06/02 01:38:47 crh
+ * Changed include file name from ubi_null.h to sys_include.h to make it
+ * more generic.
+ *
+ * Revision 0.5 1998/05/20 04:38:05 crh
+ * The C file now includes ubi_null.h. See ubi_null.h for more info.
+ *
+ * Revision 0.4 1998/03/10 02:23:20 crh
+ * Combined ubi_StackQueue and ubi_sLinkList into one module. Redesigned
+ * the functions and macros. Not a complete rewrite but close to it.
+ *
+ * Revision 0.3 1998/01/03 01:59:52 crh
+ * Added ubi_slCount() macro.
+ *
+ * Revision 0.2 1997/10/21 03:35:18 crh
+ * Added parameter <After> in function Insert(). Made necessary changes
+ * to macro AddHead() and added macro AddHere().
+ *
+ * Revision 0.1 1997/10/16 02:53:45 crh
+ * Initial Revision.
+ *
+ * -------------------------------------------------------------------------- **
+ * This module implements a singly-linked list which may also be used as a
+ * queue or a stack. For a queue, entries are added at the tail and removed
+ * from the head of the list. For a stack, the entries are entered and
+ * removed from the head of the list. A traversal of the list will always
+ * start at the head of the list and proceed toward the tail. This is all
+ * mind-numbingly simple, but I'm surprised by the number of programs out
+ * there which re-implement this a dozen or so times.
+ *
+ * Note: When the list header is initialized, the Tail pointer is set to
+ * point to the Head pointer. This simplifies things a great deal,
+ * except that you can't initialize a stack or queue by simply
+ * zeroing it out. One sure way to initialize the header is to call
+ * ubi_slInit(). Another option would be something like this:
+ *
+ * ubi_slNewList( MyList );
+ *
+ * Which translates to:
+ *
+ * ubi_slList MyList[1] = { NULL, (ubi_slNodePtr)MyList, 0 };
+ *
+ * See ubi_slInit(), ubi_slNewList(), and the ubi_slList structure
+ * for more info.
+ *
+ * + Also, note that this module is similar to the ubi_dLinkList
+ * module. There are three key differences:
+ * - This is a singly-linked list, the other is a doubly-linked
+ * list.
+ * - In this module, if the list is empty, the tail pointer will
+ * point back to the head of the list as described above. This
+ * is not done in ubi_dLinkList.
+ * - The ubi_slRemoveNext() function, by necessity, removes the
+ * 'next' node. In ubi_dLinkList, the ubi_dlRemove() function
+ * removes the 'current' node.
+ *
+ * ========================================================================== **
+ */
+
+#include "ubi_sLinkList.h" /* Header for *this* module. */
+
+/* ========================================================================== **
+ * Functions...
+ */
+
+ubi_slListPtr ubi_slInitList( ubi_slListPtr ListPtr )
+ /* ------------------------------------------------------------------------ **
+ * Initialize a singly-linked list header.
+ *
+ * Input: ListPtr - A pointer to the list structure that is to be
+ * initialized for use.
+ *
+ * Output: A pointer to the initialized list header (i.e., same as
+ * <ListPtr>).
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ListPtr->Head = NULL;
+ ListPtr->Tail = (ubi_slNodePtr)ListPtr;
+ ListPtr->count = 0;
+ return( ListPtr );
+ } /* ubi_slInitList */
+
+ubi_slNodePtr ubi_slInsert( ubi_slListPtr ListPtr,
+ ubi_slNodePtr New,
+ ubi_slNodePtr After )
+ /* ------------------------------------------------------------------------ **
+ * Add a node to the list.
+ *
+ * Input: ListPtr - A pointer to the list into which the node is to
+ * be inserted.
+ * New - Pointer to the node that is to be added to the list.
+ * After - Pointer to a list in a node after which the new node
+ * will be inserted. If NULL, then the new node will
+ * be added at the head of the list.
+ *
+ * Output: A pointer to the node that was inserted into the list (i.e.,
+ * the same as <New>).
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ After = After ? After : (ubi_slNodePtr)ListPtr;
+ New->Next = After->Next;
+ After->Next = New;
+ if( !(New->Next) )
+ ListPtr->Tail = New;
+ (ListPtr->count)++;
+ return( New );
+ } /* ubi_slInsert */
+
+ubi_slNodePtr ubi_slRemoveNext( ubi_slListPtr ListPtr, ubi_slNodePtr AfterMe )
+ /* ------------------------------------------------------------------------ **
+ * Remove the node followng <AfterMe>. If <AfterMe> is NULL, remove from
+ * the head of the list.
+ *
+ * Input: ListPtr - A pointer to the list from which the node is to be
+ * removed.
+ * AfterMe - Pointer to the node preceeding the node to be
+ * removed.
+ *
+ * Output: A pointer to the node that was removed, or NULL if the list is
+ * empty.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_slNodePtr DelNode;
+
+ AfterMe = AfterMe ? AfterMe : (ubi_slNodePtr)ListPtr;
+ DelNode = AfterMe->Next;
+ if( DelNode )
+ {
+ if( !(DelNode->Next) )
+ ListPtr->Tail = AfterMe;
+ AfterMe->Next = DelNode->Next;
+ (ListPtr->count)--;
+ }
+ return( DelNode );
+ } /* ubi_slRemoveNext */
+
+/* ================================ The End ================================= */
diff --git a/source/ubiqx/ubi_sLinkList.h b/source/ubiqx/ubi_sLinkList.h
new file mode 100644
index 00000000000..53bfa400671
--- /dev/null
+++ b/source/ubiqx/ubi_sLinkList.h
@@ -0,0 +1,254 @@
+#ifndef UBI_SLINKLIST_H
+#define UBI_SLINKLIST_H
+/* ========================================================================== **
+ * ubi_sLinkList.h
+ *
+ * Copyright (C) 1997, 1998 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ * -------------------------------------------------------------------------- **
+ * This module implements a simple singly-linked list.
+ * -------------------------------------------------------------------------- **
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * Log: ubi_sLinkList.h,v
+ * Revision 0.10 1999/06/19 16:58:06 crh
+ * Renamed the ubi_slRemove() function in ubi_sLinkList to
+ * ubi_slRemoveNext(). I was bothered by the fact that it didn't
+ * match the functionality of the ubi_dlRemove() function in
+ * ubi_dLinkList. The new name is more 'correct'.
+ *
+ * Revision 0.9 1998/07/24 07:30:20 crh
+ * Added the ubi_slNewList() macro.
+ *
+ * Revision 0.8 1998/06/04 21:29:27 crh
+ * Upper-cased defined constants (eg UBI_BINTREE_H) in some header files.
+ * This is more "standard", and is what people expect. Weird, eh?
+ *
+ * Revision 0.7 1998/06/03 18:06:03 crh
+ * Further fiddling with sys_include.h, which has been moved from the .c file
+ * to the .h file.
+ *
+ * Revision 0.6 1998/06/02 01:38:47 crh
+ * Changed include file name from ubi_null.h to sys_include.h to make it
+ * more generic.
+ *
+ * Revision 0.5 1998/05/20 04:38:05 crh
+ * The C file now includes ubi_null.h. See ubi_null.h for more info.
+ *
+ * Revision 0.4 1998/03/10 02:22:39 crh
+ * Combined ubi_StackQueue and ubi_sLinkList into one module. Redesigned
+ * the functions and macros. Not a complete rewrite but close to it.
+ *
+ * Revision 0.3 1998/01/03 02:00:02 crh
+ * Added ubi_slCount() macro.
+ *
+ * Revision 0.2 1997/10/21 03:36:14 crh
+ * Added parameter <After> in function Insert(). Made necessary changes
+ * to macro AddHead() and added macro AddHere().
+ *
+ * Revision 0.1 1997/10/16 02:54:08 crh
+ * Initial Revision.
+ *
+ * -------------------------------------------------------------------------- **
+ * This module implements a singly-linked list which may also be used as a
+ * queue or a stack. For a queue, entries are added at the tail and removed
+ * from the head of the list. For a stack, the entries are entered and
+ * removed from the head of the list. A traversal of the list will always
+ * start at the head of the list and proceed toward the tail. This is all
+ * mind-numbingly simple, but I'm surprised by the number of programs out
+ * there which re-implement this a dozen or so times.
+ *
+ * Note: When the list header is initialized, the Tail pointer is set to
+ * point to the Head pointer. This simplifies things a great deal,
+ * except that you can't initialize a stack or queue by simply
+ * zeroing it out. One sure way to initialize the header is to call
+ * ubi_slInit(). Another option would be something like this:
+ *
+ * ubi_slNewList( MyList );
+ *
+ * Which translates to:
+ *
+ * ubi_slList MyList[1] = { NULL, (ubi_slNodePtr)MyList, 0 };
+ *
+ * See ubi_slInit(), ubi_slNewList(), and the ubi_slList structure
+ * for more info.
+ *
+ * + Also, note that this module is similar to the ubi_dLinkList
+ * module. There are three key differences:
+ * - This is a singly-linked list, the other is a doubly-linked
+ * list.
+ * - In this module, if the list is empty, the tail pointer will
+ * point back to the head of the list as described above. This
+ * is not done in ubi_dLinkList.
+ * - The ubi_slRemoveNext() function, by necessity, removes the
+ * 'next' node. In ubi_dLinkList, the ubi_dlRemove() function
+ * removes the 'current' node.
+ *
+ * ========================================================================== **
+ */
+
+#include "sys_include.h" /* System-specific includes. */
+
+/* ========================================================================== **
+ * Typedefs...
+ *
+ * ubi_slNode - This is the basic node structure.
+ * ubi_slNodePtr - Pointer to a node.
+ * ubi_slList - This is the list header structure.
+ * ubi_slListPtr - Pointer to a List (i.e., a list header structure).
+ *
+ */
+
+typedef struct ubi_slListNode
+ {
+ struct ubi_slListNode *Next;
+ } ubi_slNode;
+
+typedef ubi_slNode *ubi_slNodePtr;
+
+typedef struct
+ {
+ ubi_slNodePtr Head;
+ ubi_slNodePtr Tail;
+ unsigned long count;
+ } ubi_slList;
+
+typedef ubi_slList *ubi_slListPtr;
+
+
+/* ========================================================================== **
+ * Macros...
+ *
+ * ubi_slNewList - Macro used to declare and initialize a list header in
+ * one step.
+ *
+ * ubi_slCount - Returns the current number of entries in the list.
+ *
+ * ubi_slAddHead - Add a new node at the head of the list.
+ * ubi_slAddNext - Add a new node following the indicated node.
+ * ubi_slAddTail - Add a new node to the tail of the list.
+ * Note: AddTail evaluates the L parameter twice.
+ *
+ * ubi_slRemHead - Remove the node at the head of the list, if any.
+ * ubi_slRemNext - Remove the node following the given node.
+ *
+ * ubi_slFirst - Return a pointer to the first node in the list, if any.
+ * ubi_slNext - Given a node, return a pointer to the next node.
+ * ubi_slLast - Return a pointer to the last node in the list, if any.
+ *
+ * ubi_slPush - Add a node at the head of the list (synonym of AddHead).
+ * ubi_slPop - Remove a node at the head of the list (synonym of RemHead).
+ * ubi_slEnqueue - Add a node at the tail of the list (sysnonym of AddTail).
+ * ubi_slDequeue - Remove a node at the head of the list (synonym of RemHead).
+ *
+ * Note that all of these provide type casting of the parameters. The
+ * Add and Rem macros are nothing more than nice front-ends to the
+ * Insert and Remove functions.
+ *
+ * Also note that the First, Next and Last macros do no parameter checking!
+ *
+ */
+
+#define ubi_slNewList( L ) ubi_slList (L)[1] = {{ NULL, (ubi_slNodePtr)(L), 0 }}
+
+#define ubi_slCount( L ) (((ubi_slListPtr)(L))->count)
+
+#define ubi_slAddHead( L, N ) \
+ ubi_slInsert( (ubi_slListPtr)(L), (ubi_slNodePtr)(N), NULL )
+
+#define ubi_slAddNext( L, N, A ) \
+ ubi_slInsert( (ubi_slListPtr)(L), \
+ (ubi_slNodePtr)(N), \
+ (ubi_slNodePtr)(A) )
+
+#define ubi_slAddTail( L, N ) \
+ ubi_slInsert( (ubi_slListPtr)(L), \
+ (ubi_slNodePtr)(N), \
+ ((ubi_slListPtr)(L))->Tail )
+
+#define ubi_slRemHead( L ) ubi_slRemoveNext( (ubi_slListPtr)(L), NULL )
+
+#define ubi_slRemNext( L, N ) \
+ ubi_slRemoveNext( (ubi_slListPtr)(L), (ubi_slNodePtr)(N) )
+
+#define ubi_slFirst( L ) (((ubi_slListPtr)(L))->Head)
+
+#define ubi_slNext( N ) (((ubi_slNodePtr)(N))->Next)
+
+#define ubi_slLast( L ) (((ubi_slListPtr)(L))->Tail)
+
+#define ubi_slPush ubi_slAddHead
+#define ubi_slPop ubi_slRemHead
+#define ubi_slEnqueue ubi_slAddTail
+#define ubi_slDequeue ubi_slRemHead
+
+/* ========================================================================== **
+ * Function prototypes...
+ */
+
+ubi_slListPtr ubi_slInitList( ubi_slListPtr ListPtr );
+ /* ------------------------------------------------------------------------ **
+ * Initialize a singly-linked list header.
+ *
+ * Input: ListPtr - A pointer to the list structure that is to be
+ * initialized for use.
+ *
+ * Output: A pointer to the initialized list header (i.e., same as
+ * <ListPtr>).
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_slNodePtr ubi_slInsert( ubi_slListPtr ListPtr,
+ ubi_slNodePtr New,
+ ubi_slNodePtr After );
+ /* ------------------------------------------------------------------------ **
+ * Add a node to the list.
+ *
+ * Input: ListPtr - A pointer to the list into which the node is to
+ * be inserted.
+ * New - Pointer to the node that is to be added to the list.
+ * After - Pointer to a list in a node after which the new node
+ * will be inserted. If NULL, then the new node will
+ * be added at the head of the list.
+ *
+ * Output: A pointer to the node that was inserted into the list (i.e.,
+ * the same as <New>).
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_slNodePtr ubi_slRemoveNext( ubi_slListPtr ListPtr, ubi_slNodePtr AfterMe );
+ /* ------------------------------------------------------------------------ **
+ * Remove the node followng <AfterMe>. If <AfterMe> is NULL, remove from
+ * the head of the list.
+ *
+ * Input: ListPtr - A pointer to the list from which the node is to be
+ * removed.
+ * AfterMe - Pointer to the node preceeding the node to be
+ * removed.
+ *
+ * Output: A pointer to the node that was removed, or NULL if the list is
+ * empty.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+/* ================================ The End ================================= */
+#endif /* UBI_SLINKLIST_H */
diff --git a/source/utils/.cvsignore b/source/utils/.cvsignore
new file mode 100644
index 00000000000..6b8749f64eb
--- /dev/null
+++ b/source/utils/.cvsignore
@@ -0,0 +1 @@
+net_proto.h \ No newline at end of file
diff --git a/source/utils/debug2html.c b/source/utils/debug2html.c
new file mode 100644
index 00000000000..f9a1f43f461
--- /dev/null
+++ b/source/utils/debug2html.c
@@ -0,0 +1,253 @@
+/* ========================================================================== **
+ * debug2html.c
+ *
+ * Copyright (C) 1998 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ *
+ * -------------------------------------------------------------------------- **
+ * Parse Samba debug logs (2.0 & greater) and output the results as HTML.
+ * -------------------------------------------------------------------------- **
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ * This program provides an example of the use of debugparse.c, and also
+ * does a decent job of converting Samba logs into HTML.
+ * -------------------------------------------------------------------------- **
+ *
+ * Revision 1.4 1998/11/13 03:37:01 tridge
+ * fixes for OSF1 compilation
+ *
+ * Revision 1.3 1998/10/28 20:33:35 crh
+ * I've moved the debugparse module files into the ubiqx directory because I
+ * know that 'make proto' will ignore them there. The debugparse.h header
+ * file is included in includes.h, and includes.h is included in debugparse.c,
+ * so all of the pieces "see" each other. I've compiled and tested this,
+ * and it does seem to work. It's the same compromise model I used when
+ * adding the ubiqx modules into the system, which is why I put it all into
+ * the same directory.
+ *
+ * Chris -)-----
+ *
+ * Revision 1.1 1998/10/26 23:21:37 crh
+ * Here is the simple debug parser and the debug2html converter. Still to do:
+ *
+ * * Debug message filtering.
+ * * I need to add all this to Makefile.in
+ * (If it looks at all strange I'll ask for help.)
+ *
+ * If you want to compile debug2html, you'll need to do it by hand until I
+ * make the changes to Makefile.in. Sorry.
+ *
+ * Chris -)-----
+ *
+ * ========================================================================== **
+ */
+
+#include "debugparse.h"
+
+/* -------------------------------------------------------------------------- **
+ * The size of the read buffer.
+ */
+
+#define DBG_BSIZE 1024
+
+/* -------------------------------------------------------------------------- **
+ * Functions...
+ */
+
+static dbg_Token modechange( dbg_Token new, dbg_Token mode )
+ /* ------------------------------------------------------------------------ **
+ * Handle a switch between header and message printing.
+ *
+ * Input: new - The token value of the current token. This indicates
+ * the lexical item currently being recognized.
+ * mode - The current mode. This is either dbg_null or
+ * dbg_message. It could really be any toggle
+ * (true/false, etc.)
+ *
+ * Output: The new mode. This will be the same as the input mode unless
+ * there was a transition in or out of message processing.
+ *
+ * Notes: The purpose of the mode value is to mark the beginning and end
+ * of the message text block. In order to show the text in its
+ * correct format, it must be included within a <PRE></PRE> block.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ switch( new )
+ {
+ case dbg_null:
+ case dbg_ignore:
+ return( mode );
+ case dbg_message:
+ if( dbg_message != mode )
+ {
+ /* Switching to message mode. */
+ (void)printf( "<PRE>\n" );
+ return( dbg_message );
+ }
+ break;
+ default:
+ if( dbg_message == mode )
+ {
+ /* Switching out of message mode. */
+ (void)printf( "</PRE>\n\n" );
+ return( dbg_null );
+ }
+ }
+
+ return( mode );
+ } /* modechange */
+
+static void newblock( dbg_Token old, dbg_Token new )
+ /* ------------------------------------------------------------------------ **
+ * Handle the transition between tokens.
+ *
+ * Input: old - The previous token.
+ * new - The current token.
+ *
+ * Output: none.
+ *
+ * Notes: This is called whenever there is a transition from one token
+ * type to another. It first prints the markup tags that close
+ * the previous token, and then the markup tags for the new
+ * token.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ switch( old )
+ {
+ case dbg_timestamp:
+ (void)printf( ",</B>" );
+ break;
+ case dbg_level:
+ (void)printf( "</FONT>]</B>\n " );
+ break;
+ case dbg_sourcefile:
+ (void)printf( ":" );
+ break;
+ case dbg_lineno:
+ (void)printf( ")" );
+ break;
+ }
+
+ switch( new )
+ {
+ case dbg_timestamp:
+ (void)printf( "<B>[" );
+ break;
+ case dbg_level:
+ (void)printf( " <B><FONT COLOR=MAROON>" );
+ break;
+ case dbg_lineno:
+ (void)printf( "(" );
+ break;
+ }
+ } /* newblock */
+
+static void charprint( dbg_Token tok, int c )
+ /* ------------------------------------------------------------------------ **
+ * Filter the input characters to determine what goes to output.
+ *
+ * Input: tok - The token value of the current character.
+ * c - The current character.
+ *
+ * Output: none.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ switch( tok )
+ {
+ case dbg_ignore:
+ case dbg_header:
+ break;
+ case dbg_null:
+ case dbg_eof:
+ (void)putchar( '\n' );
+ break;
+ default:
+ switch( c )
+ {
+ case '<':
+ (void)printf( "&lt;" );
+ break;
+ case '>':
+ (void)printf( "&gt;" );
+ break;
+ case '&':
+ (void)printf( "&amp;" );
+ break;
+ case '\"':
+ (void)printf( "&#34;" );
+ break;
+ default:
+ (void)putchar( c );
+ break;
+ }
+ }
+ } /* charprint */
+
+int main( int argc, char *argv[] )
+ /* ------------------------------------------------------------------------ **
+ * This simple program scans and parses Samba debug logs, and produces HTML
+ * output.
+ *
+ * Input: argc - Currently ignored.
+ * argv - Currently ignored.
+ *
+ * Output: Always zero.
+ *
+ * Notes: The HTML output is sent to stdout.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int i;
+ int len;
+ char bufr[DBG_BSIZE];
+ dbg_Token old = dbg_null,
+ new = dbg_null,
+ state = dbg_null,
+ mode = dbg_null;
+
+ (void)printf( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\n" );
+ (void)printf( "<HTML>\n<HEAD>\n" );
+ (void)printf( " <TITLE>Samba Debug Output</TITLE>\n</HEAD>\n\n<BODY>\n" );
+
+ while( (!feof( stdin ))
+ && ((len = fread( bufr, 1, DBG_BSIZE, stdin )) > 0) )
+ {
+ for( i = 0; i < len; i++ )
+ {
+ old = new;
+ new = dbg_char2token( &state, bufr[i] );
+ if( new != old )
+ {
+ mode = modechange( new, mode );
+ newblock( old, new );
+ }
+ charprint( new, bufr[i] );
+ }
+ }
+ (void)modechange( dbg_eof, mode );
+
+ (void)printf( "</BODY>\n</HTML>\n" );
+ return( 0 );
+ } /* main */
diff --git a/source/utils/editreg.c b/source/utils/editreg.c
new file mode 100644
index 00000000000..a0cfa2bb07d
--- /dev/null
+++ b/source/utils/editreg.c
@@ -0,0 +1,4145 @@
+/*
+ Samba Unix/Linux SMB client utility editreg.c
+ Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*************************************************************************
+
+ A utility to edit a Windows NT/2K etc registry file.
+
+ Many of the ideas in here come from other people and software.
+ I first looked in Wine in misc/registry.c and was also influenced by
+ http://www.wednesday.demon.co.uk/dosreg.html
+
+ Which seems to contain comments from someone else. I reproduce them here
+ incase the site above disappears. It actually comes from
+ http://home.eunet.no/~pnordahl/ntpasswd/WinReg.txt.
+
+ The goal here is to read the registry into memory, manipulate it, and then
+ write it out if it was changed by any actions of the user.
+
+The windows NT registry has 2 different blocks, where one can occur many
+times...
+
+the "regf"-Block
+================
+
+"regf" is obviosly the abbreviation for "Registry file". "regf" is the
+signature of the header-block which is always 4kb in size, although only
+the first 64 bytes seem to be used and a checksum is calculated over
+the first 0x200 bytes only!
+
+Offset Size Contents
+0x00000000 D-Word ID: ASCII-"regf" = 0x66676572
+0x00000004 D-Word ???? //see struct REGF
+0x00000008 D-Word ???? Always the same value as at 0x00000004
+0x0000000C Q-Word last modify date in WinNT date-format
+0x00000014 D-Word 1
+0x00000018 D-Word 3
+0x0000001C D-Word 0
+0x00000020 D-Word 1
+0x00000024 D-Word Offset of 1st key record
+0x00000028 D-Word Size of the data-blocks (Filesize-4kb)
+0x0000002C D-Word 1
+0x000001FC D-Word Sum of all D-Words from 0x00000000 to
+0x000001FB //XOR of all words. Nigel
+
+I have analyzed more registry files (from multiple machines running
+NT 4.0 german version) and could not find an explanation for the values
+marked with ???? the rest of the first 4kb page is not important...
+
+the "hbin"-Block
+================
+I don't know what "hbin" stands for, but this block is always a multiple
+of 4kb in size.
+
+Inside these hbin-blocks the different records are placed. The memory-
+management looks like a C-compiler heap management to me...
+
+hbin-Header
+===========
+Offset Size Contents
+0x0000 D-Word ID: ASCII-"hbin" = 0x6E696268
+0x0004 D-Word Offset from the 1st hbin-Block
+0x0008 D-Word Offset to the next hbin-Block
+0x001C D-Word Block-size
+
+The values in 0x0008 and 0x001C should be the same, so I don't know
+if they are correct or swapped...
+
+From offset 0x0020 inside a hbin-block data is stored with the following
+format:
+
+Offset Size Contents
+0x0000 D-Word Data-block size //this size must be a
+multiple of 8. Nigel
+0x0004 ???? Data
+
+If the size field is negative (bit 31 set), the corresponding block
+is free and has a size of -blocksize!
+
+That does not seem to be true. All block lengths seem to be negative!
+(Richard Sharpe)
+
+The data is stored as one record per block. Block size is a multiple
+of 4 and the last block reaches the next hbin-block, leaving no room.
+
+(That also seems incorrect, in that the block size if a multiple of 8.
+That is, the block, including the 4 byte header, is always a multiple of
+8 bytes. Richard Sharpe.)
+
+Records in the hbin-blocks
+==========================
+
+nk-Record
+
+ The nk-record can be treated as a kombination of tree-record and
+ key-record of the win 95 registry.
+
+lf-Record
+
+ The lf-record is the counterpart to the RGKN-record (the
+ hash-function)
+
+vk-Record
+
+ The vk-record consists information to a single value.
+
+sk-Record
+
+ sk (? Security Key ?) is the ACL of the registry.
+
+Value-Lists
+
+ The value-lists contain information about which values are inside a
+ sub-key and don't have a header.
+
+Datas
+
+ The datas of the registry are (like the value-list) stored without a
+ header.
+
+All offset-values are relative to the first hbin-block and point to the
+block-size field of the record-entry. to get the file offset, you have to add
+the header size (4kb) and the size field (4 bytes)...
+
+the nk-Record
+=============
+Offset Size Contents
+0x0000 Word ID: ASCII-"nk" = 0x6B6E
+0x0002 Word for the root-key: 0x2C, otherwise 0x20 //key symbolic links 0x10. Nigel
+0x0004 Q-Word write-date/time in windows nt notation
+0x0010 D-Word Offset of Owner/Parent key
+0x0014 D-Word number of sub-Keys
+0x001C D-Word Offset of the sub-key lf-Records
+0x0024 D-Word number of values
+0x0028 D-Word Offset of the Value-List
+0x002C D-Word Offset of the sk-Record
+
+0x0030 D-Word Offset of the Class-Name //see NK structure for the use of these fields. Nigel
+0x0044 D-Word Unused (data-trash) //some kind of run time index. Does not appear to be important. Nigel
+0x0048 Word name-length
+0x004A Word class-name length
+0x004C ???? key-name
+
+the Value-List
+==============
+Offset Size Contents
+0x0000 D-Word Offset 1st Value
+0x0004 D-Word Offset 2nd Value
+0x???? D-Word Offset nth Value
+
+To determine the number of values, you have to look at the owner-nk-record!
+
+Der vk-Record
+=============
+Offset Size Contents
+0x0000 Word ID: ASCII-"vk" = 0x6B76
+0x0002 Word name length
+0x0004 D-Word length of the data //if top bit is set when offset contains data. Nigel
+0x0008 D-Word Offset of Data
+0x000C D-Word Type of value
+0x0010 Word Flag
+0x0012 Word Unused (data-trash)
+0x0014 ???? Name
+
+If bit 0 of the flag-word is set, a name is present, otherwise the value has no name (=default)
+
+If the data-size is lower 5, the data-offset value is used to store the data itself!
+
+The data-types
+==============
+Wert Beteutung
+0x0001 RegSZ: character string (in UNICODE!)
+0x0002 ExpandSZ: string with "%var%" expanding (UNICODE!)
+0x0003 RegBin: raw-binary value
+0x0004 RegDWord: Dword
+0x0007 RegMultiSZ: multiple strings, seperated with 0
+ (UNICODE!)
+
+The "lf"-record
+===============
+Offset Size Contents
+0x0000 Word ID: ASCII-"lf" = 0x666C
+0x0002 Word number of keys
+0x0004 ???? Hash-Records
+
+Hash-Record
+===========
+Offset Size Contents
+0x0000 D-Word Offset of corresponding "nk"-Record
+0x0004 D-Word ASCII: the first 4 characters of the key-name, padded with 0's. Case sensitiv!
+
+Keep in mind, that the value at 0x0004 is used for checking the data-consistency! If you change the
+key-name you have to change the hash-value too!
+
+//These hashrecords must be sorted low to high within the lf record. Nigel.
+
+The "sk"-block
+==============
+(due to the complexity of the SAM-info, not clear jet)
+(This is just a self-relative security descriptor in the data. R Sharpe.)
+
+
+Offset Size Contents
+0x0000 Word ID: ASCII-"sk" = 0x6B73
+0x0002 Word Unused
+0x0004 D-Word Offset of previous "sk"-Record
+0x0008 D-Word Offset of next "sk"-Record
+0x000C D-Word usage-counter
+0x0010 D-Word Size of "sk"-record in bytes
+???? //standard self
+relative security desciptor. Nigel
+???? ???? Security and auditing settings...
+????
+
+The usage counter counts the number of references to this
+"sk"-record. You can use one "sk"-record for the entire registry!
+
+Windows nt date/time format
+===========================
+The time-format is a 64-bit integer which is incremented every
+0,0000001 seconds by 1 (I don't know how accurate it realy is!)
+It starts with 0 at the 1st of january 1601 0:00! All values are
+stored in GMT time! The time-zone is important to get the real
+time!
+
+Common values for win95 and win-nt
+==================================
+Offset values marking an "end of list", are either 0 or -1 (0xFFFFFFFF).
+If a value has no name (length=0, flag(bit 0)=0), it is treated as the
+"Default" entry...
+If a value has no data (length=0), it is displayed as empty.
+
+simplyfied win-3.?? registry:
+=============================
+
++-----------+
+| next rec. |---+ +----->+------------+
+| first sub | | | | Usage cnt. |
+| name | | +-->+------------+ | | length |
+| value | | | | next rec. | | | text |------->+-------+
++-----------+ | | | name rec. |--+ +------------+ | xxxxx |
+ +------------+ | | value rec. |-------->+------------+ +-------+
+ v | +------------+ | Usage cnt. |
++-----------+ | | length |
+| next rec. | | | text |------->+-------+
+| first sub |------+ +------------+ | xxxxx |
+| name | +-------+
+| value |
++-----------+
+
+Greatly simplyfied structure of the nt-registry:
+================================================
+
++---------------------------------------------------------------+
+| |
+v |
++---------+ +---------->+-----------+ +----->+---------+ |
+| "nk" | | | lf-rec. | | | nk-rec. | |
+| ID | | | # of keys | | | parent |---+
+| Date | | | 1st key |--+ | .... |
+| parent | | +-----------+ +---------+
+| suk-keys|-----+
+| values |--------------------->+----------+
+| SK-rec. |---------------+ | 1. value |--> +----------+
+| class |--+ | +----------+ | vk-rec. |
++---------+ | | | .... |
+ v | | data |--> +-------+
+ +------------+ | +----------+ | xxxxx |
+ | Class name | | +-------+
+ +------------+ |
+ v
+ +---------+ +---------+
+ +----->| next sk |--->| Next sk |--+
+ | +---| prev sk |<---| prev sk | |
+ | | | .... | | ... | |
+ | | +---------+ +---------+ |
+ | | ^ |
+ | | | |
+ | +--------------------+ |
+ +----------------------------------+
+
+---------------------------------------------------------------------------
+
+Hope this helps.... (Although it was "fun" for me to uncover this things,
+ it took me several sleepless nights ;)
+
+ B.D.
+
+*************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <fcntl.h>
+
+#define False 0
+#define True 1
+#define REG_KEY_LIST_SIZE 10
+
+/*
+ * Structures for dealing with the on-disk format of the registry
+ */
+
+#define IVAL(buf) ((unsigned int) \
+ (unsigned int)*((unsigned char *)(buf)+3)<<24| \
+ (unsigned int)*((unsigned char *)(buf)+2)<<16| \
+ (unsigned int)*((unsigned char *)(buf)+1)<<8| \
+ (unsigned int)*((unsigned char *)(buf)+0))
+
+#define SVAL(buf) ((unsigned short) \
+ (unsigned short)*((unsigned char *)(buf)+1)<<8| \
+ (unsigned short)*((unsigned char *)(buf)+0))
+
+#define CVAL(buf) ((unsigned char)*((unsigned char *)(buf)))
+
+#define SIVAL(buf, val) \
+ ((((unsigned char *)(buf))[0])=(unsigned char)((val)&0xFF),\
+ (((unsigned char *)(buf))[1])=(unsigned char)(((val)>>8)&0xFF),\
+ (((unsigned char *)(buf))[2])=(unsigned char)(((val)>>16)&0xFF),\
+ (((unsigned char *)(buf))[3])=(unsigned char)((val)>>24))
+
+#define SSVAL(buf, val) \
+ ((((unsigned char *)(buf))[0])=(unsigned char)((val)&0xFF),\
+ (((unsigned char *)(buf))[1])=(unsigned char)((val)>>8))
+
+static int verbose = 0;
+static int print_security = 0;
+static int full_print = 0;
+static const char *def_owner_sid_str = NULL;
+
+/*
+ * These definitions are for the in-memory registry structure.
+ * It is a tree structure that mimics what you see with tools like regedit
+ */
+
+/*
+ * DateTime struct for Windows
+ */
+
+typedef struct date_time_s {
+ unsigned int low, high;
+} NTTIME;
+
+/*
+ * Definition of a Key. It has a name, classname, date/time last modified,
+ * sub-keys, values, and a security descriptor
+ */
+
+#define REG_ROOT_KEY 1
+#define REG_SUB_KEY 2
+#define REG_SYM_LINK 3
+
+typedef struct key_sec_desc_s KEY_SEC_DESC;
+
+typedef struct reg_key_s {
+ char *name; /* Name of the key */
+ char *class_name;
+ int type; /* One of REG_ROOT_KEY or REG_SUB_KEY */
+ NTTIME last_mod; /* Time last modified */
+ struct reg_key_s *owner;
+ struct key_list_s *sub_keys;
+ struct val_list_s *values;
+ KEY_SEC_DESC *security;
+ unsigned int offset; /* Offset of the record in the file */
+} REG_KEY;
+
+/*
+ * The KEY_LIST struct lists sub-keys.
+ */
+
+typedef struct key_list_s {
+ int key_count;
+ int max_keys;
+ REG_KEY *keys[1];
+} KEY_LIST;
+
+typedef struct val_key_s {
+ char *name;
+ int has_name;
+ int data_type;
+ int data_len;
+ void *data_blk; /* Might want a separate block */
+} VAL_KEY;
+
+typedef struct val_list_s {
+ int val_count;
+ int max_vals;
+ VAL_KEY *vals[1];
+} VAL_LIST;
+
+#ifndef MAXSUBAUTHS
+#define MAXSUBAUTHS 15
+#endif
+
+typedef struct sid_s {
+ unsigned char ver, auths;
+ unsigned char auth[6];
+ unsigned int sub_auths[MAXSUBAUTHS];
+} sid_t;
+
+typedef struct ace_struct_s {
+ unsigned char type, flags;
+ unsigned int perms; /* Perhaps a better def is in order */
+ sid_t *trustee;
+} ACE;
+
+typedef struct acl_struct_s {
+ unsigned short rev, refcnt;
+ unsigned short num_aces;
+ ACE *aces[1];
+} ACL;
+
+typedef struct sec_desc_s {
+ unsigned int rev, type;
+ sid_t *owner, *group;
+ ACL *sacl, *dacl;
+} SEC_DESC;
+
+#define SEC_DESC_NON 0
+#define SEC_DESC_RES 1
+#define SEC_DESC_OCU 2
+#define SEC_DESC_NBK 3
+typedef struct sk_struct SK_HDR;
+struct key_sec_desc_s {
+ struct key_sec_desc_s *prev, *next;
+ int ref_cnt;
+ int state;
+ int offset;
+ SK_HDR *sk_hdr; /* This means we must keep the registry in memory */
+ SEC_DESC *sec_desc;
+};
+
+/*
+ * All of the structures below actually have a four-byte length before them
+ * which always seems to be negative. The following macro retrieves that
+ * size as an integer
+ */
+
+#define BLK_SIZE(b) ((int)*(int *)(((int *)b)-1))
+
+typedef unsigned int DWORD;
+typedef unsigned short WORD;
+
+#define REG_REGF_ID 0x66676572
+
+typedef struct regf_block {
+ DWORD REGF_ID; /* regf */
+ DWORD uk1;
+ DWORD uk2;
+ DWORD tim1, tim2;
+ DWORD uk3; /* 1 */
+ DWORD uk4; /* 3 */
+ DWORD uk5; /* 0 */
+ DWORD uk6; /* 1 */
+ DWORD first_key; /* offset */
+ unsigned int dblk_size;
+ DWORD uk7[116]; /* 1 */
+ DWORD chksum;
+} REGF_HDR;
+
+typedef struct hbin_sub_struct {
+ DWORD dblocksize;
+ char data[1];
+} HBIN_SUB_HDR;
+
+#define REG_HBIN_ID 0x6E696268
+
+typedef struct hbin_struct {
+ DWORD HBIN_ID; /* hbin */
+ DWORD off_from_first;
+ DWORD off_to_next;
+ DWORD uk1;
+ DWORD uk2;
+ DWORD uk3;
+ DWORD uk4;
+ DWORD blk_size;
+ HBIN_SUB_HDR hbin_sub_hdr;
+} HBIN_HDR;
+
+#define REG_NK_ID 0x6B6E
+
+typedef struct nk_struct {
+ WORD NK_ID;
+ WORD type;
+ DWORD t1, t2;
+ DWORD uk1;
+ DWORD own_off;
+ DWORD subk_num;
+ DWORD uk2;
+ DWORD lf_off;
+ DWORD uk3;
+ DWORD val_cnt;
+ DWORD val_off;
+ DWORD sk_off;
+ DWORD clsnam_off;
+ DWORD unk4[4];
+ DWORD unk5;
+ WORD nam_len;
+ WORD clsnam_len;
+ char key_nam[1]; /* Actual length determined by nam_len */
+} NK_HDR;
+
+#define REG_SK_ID 0x6B73
+
+struct sk_struct {
+ WORD SK_ID;
+ WORD uk1;
+ DWORD prev_off;
+ DWORD next_off;
+ DWORD ref_cnt;
+ DWORD rec_size;
+ char sec_desc[1];
+};
+
+typedef struct ace_struct {
+ unsigned char type;
+ unsigned char flags;
+ unsigned short length;
+ unsigned int perms;
+ sid_t trustee;
+} REG_ACE;
+
+typedef struct acl_struct {
+ WORD rev;
+ WORD size;
+ DWORD num_aces;
+ REG_ACE *aces; /* One or more ACEs */
+} REG_ACL;
+
+typedef struct sec_desc_rec {
+ WORD rev;
+ WORD type;
+ DWORD owner_off;
+ DWORD group_off;
+ DWORD sacl_off;
+ DWORD dacl_off;
+} REG_SEC_DESC;
+
+typedef struct hash_struct {
+ DWORD nk_off;
+ char hash[4];
+} HASH_REC;
+
+#define REG_LF_ID 0x666C
+
+typedef struct lf_struct {
+ WORD LF_ID;
+ WORD key_count;
+ struct hash_struct hr[1]; /* Array of hash records, depending on key_count */
+} LF_HDR;
+
+typedef DWORD VL_TYPE[1]; /* Value list is an array of vk rec offsets */
+
+#define REG_VK_ID 0x6B76
+
+typedef struct vk_struct {
+ WORD VK_ID;
+ WORD nam_len;
+ DWORD dat_len; /* If top-bit set, offset contains the data */
+ DWORD dat_off;
+ DWORD dat_type;
+ WORD flag; /* =1, has name, else no name (=Default). */
+ WORD unk1;
+ char dat_name[1]; /* Name starts here ... */
+} VK_HDR;
+
+#define REG_TYPE_DELETE -1
+#define REG_TYPE_NONE 0
+#define REG_TYPE_REGSZ 1
+#define REG_TYPE_EXPANDSZ 2
+#define REG_TYPE_BIN 3
+#define REG_TYPE_DWORD 4
+#define REG_TYPE_MULTISZ 7
+
+typedef struct _val_str {
+ unsigned int val;
+ const char * str;
+} VAL_STR;
+
+/* A map of sk offsets in the regf to KEY_SEC_DESCs for quick lookup etc */
+typedef struct sk_map_s {
+ int sk_off;
+ KEY_SEC_DESC *key_sec_desc;
+} SK_MAP;
+
+/*
+ * This structure keeps track of the output format of the registry
+ */
+#define REG_OUTBLK_HDR 1
+#define REG_OUTBLK_HBIN 2
+
+typedef struct hbin_blk_s {
+ int type, size;
+ struct hbin_blk_s *next;
+ char *data; /* The data block */
+ unsigned int file_offset; /* Offset in file */
+ unsigned int free_space; /* Amount of free space in block */
+ unsigned int fsp_off; /* Start of free space in block */
+ int complete, stored;
+} HBIN_BLK;
+
+/*
+ * This structure keeps all the registry stuff in one place
+ */
+typedef struct regf_struct_s {
+ int reg_type;
+ char *regfile_name, *outfile_name;
+ int fd;
+ struct stat sbuf;
+ char *base;
+ int modified;
+ NTTIME last_mod_time;
+ REG_KEY *root; /* Root of the tree for this file */
+ int sk_count, sk_map_size;
+ SK_MAP *sk_map;
+ const char *owner_sid_str;
+ SEC_DESC *def_sec_desc;
+ /*
+ * These next pointers point to the blocks used to contain the
+ * keys when we are preparing to write them to a file
+ */
+ HBIN_BLK *blk_head, *blk_tail, *free_space;
+} REGF;
+
+/*
+ * An API for accessing/creating/destroying items above
+ */
+
+/*
+ * Iterate over the keys, depth first, calling a function for each key
+ * and indicating if it is terminal or non-terminal and if it has values.
+ *
+ * In addition, for each value in the list, call a value list function
+ */
+
+typedef int (*key_print_f)(const char *path, char *key_name, char *class_name,
+ int root, int terminal, int values);
+
+typedef int (*val_print_f)(const char *path, char *val_name, int val_type,
+ int data_len, void *data_blk, int terminal,
+ int first, int last);
+
+typedef int (*sec_print_f)(SEC_DESC *sec_desc);
+
+static
+int nt_key_iterator(REGF *regf, REG_KEY *key_tree, int bf, const char *path,
+ key_print_f key_print, sec_print_f sec_print,
+ val_print_f val_print);
+
+static
+int nt_val_list_iterator(REGF *regf, VAL_LIST *val_list, int bf, char *path,
+ int terminal, val_print_f val_print)
+{
+ int i;
+
+ if (!val_list) return 1;
+
+ if (!val_print) return 1;
+
+ for (i=0; i<val_list->val_count; i++) {
+ if (!val_print(path, val_list->vals[i]->name, val_list->vals[i]->data_type,
+ val_list->vals[i]->data_len, val_list->vals[i]->data_blk,
+ terminal,
+ (i == 0),
+ (i == val_list->val_count))) {
+
+ return 0;
+
+ }
+ }
+
+ return 1;
+}
+
+static
+int nt_key_list_iterator(REGF *regf, KEY_LIST *key_list, int bf,
+ const char *path,
+ key_print_f key_print, sec_print_f sec_print,
+ val_print_f val_print)
+{
+ int i;
+
+ if (!key_list) return 1;
+
+ for (i=0; i< key_list->key_count; i++) {
+ if (!nt_key_iterator(regf, key_list->keys[i], bf, path, key_print,
+ sec_print, val_print)) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static
+int nt_key_iterator(REGF *regf, REG_KEY *key_tree, int bf, const char *path,
+ key_print_f key_print, sec_print_f sec_print,
+ val_print_f val_print)
+{
+ int path_len = strlen(path);
+ char *new_path;
+
+ if (!regf || !key_tree)
+ return -1;
+
+ /* List the key first, then the values, then the sub-keys */
+
+ if (key_print) {
+
+ if (!(*key_print)(path, key_tree->name,
+ key_tree->class_name,
+ (key_tree->type == REG_ROOT_KEY),
+ (key_tree->sub_keys == NULL),
+ (key_tree->values?(key_tree->values->val_count):0)))
+ return 0;
+ }
+
+ /*
+ * If we have a security print routine, call it
+ * If the security print routine returns false, stop.
+ */
+ if (sec_print) {
+ if (key_tree->security && !(*sec_print)(key_tree->security->sec_desc))
+ return 0;
+ }
+
+ new_path = (char *)malloc(path_len + 1 + strlen(key_tree->name) + 1);
+ if (!new_path) return 0; /* Errors? */
+ new_path[0] = '\0';
+ strcat(new_path, path);
+ strcat(new_path, key_tree->name);
+ strcat(new_path, "\\");
+
+ /*
+ * Now, iterate through the values in the val_list
+ */
+
+ if (key_tree->values &&
+ !nt_val_list_iterator(regf, key_tree->values, bf, new_path,
+ (key_tree->values!=NULL),
+ val_print)) {
+
+ free(new_path);
+ return 0;
+ }
+
+ /*
+ * Now, iterate through the keys in the key list
+ */
+
+ if (key_tree->sub_keys &&
+ !nt_key_list_iterator(regf, key_tree->sub_keys, bf, new_path, key_print,
+ sec_print, val_print)) {
+ free(new_path);
+ return 0;
+ }
+
+ free(new_path);
+ return 1;
+}
+
+static
+REG_KEY *nt_find_key_by_name(REG_KEY *tree, char *key);
+
+/*
+ * Find key by name in a list ...
+ * Take the first component and search for that in the list
+ */
+static
+REG_KEY *nt_find_key_in_list_by_name(KEY_LIST *list, char *key)
+{
+ int i;
+ REG_KEY *res = NULL;
+
+ if (!list || !key || !*key) return NULL;
+
+ for (i = 0; i < list->key_count; i++)
+ if ((res = nt_find_key_by_name(list->keys[i], key)))
+ return res;
+
+ return NULL;
+}
+
+/*
+ * Find key by name in a tree ... We will assume absolute names here, but we
+ * need the root of the tree ...
+ */
+static
+REG_KEY *nt_find_key_by_name(REG_KEY *tree, char *key)
+{
+ char *lname = NULL, *c1, *c2;
+ REG_KEY *tmp;
+
+ if (!tree || !key || !*key) return NULL;
+
+ lname = strdup(key);
+ if (!lname) return NULL;
+
+ /*
+ * Make sure that the first component is correct ...
+ */
+ c1 = lname;
+ c2 = strchr(c1, '\\');
+ if (c2) { /* Split here ... */
+ *c2 = 0;
+ c2++;
+ }
+ if (strcmp(c1, tree->name) != 0) goto error;
+
+ if (c2) {
+ tmp = nt_find_key_in_list_by_name(tree->sub_keys, c2);
+ free(lname);
+ return tmp;
+ }
+ else {
+ if (lname) free(lname);
+ return tree;
+ }
+ error:
+ if (lname) free(lname);
+ return NULL;
+}
+
+/* Make, delete keys */
+static
+int nt_delete_val_key(VAL_KEY *val_key)
+{
+
+ if (val_key) {
+ if (val_key->name) free(val_key->name);
+ if (val_key->data_blk) free(val_key->data_blk);
+ free(val_key);
+ };
+ return 1;
+}
+
+static
+int nt_delete_val_list(VAL_LIST *vl)
+{
+ int i;
+
+ if (vl) {
+ for (i=0; i<vl->val_count; i++)
+ nt_delete_val_key(vl->vals[i]);
+ free(vl);
+ }
+ return 1;
+}
+
+static
+int nt_delete_reg_key(REG_KEY *key, int delete_name);
+
+static
+int nt_delete_key_list(KEY_LIST *key_list, int delete_name)
+{
+ int i;
+
+ if (key_list) {
+ for (i=0; i<key_list->key_count; i++)
+ nt_delete_reg_key(key_list->keys[i], False);
+ free(key_list);
+ }
+ return 1;
+}
+
+/*
+ * Find the key, and if it exists, delete it ...
+ */
+static
+int nt_delete_key_by_name(REGF *regf, char *name)
+{
+ REG_KEY *key;
+
+ if (!name || !*name) return 0;
+
+ key = nt_find_key_by_name(regf->root, name);
+
+ if (key) {
+ if (key == regf->root) regf->root = NULL;
+ return nt_delete_reg_key(key, True);
+ }
+
+ return 0;
+
+}
+
+static
+int nt_delete_sid(sid_t *sid)
+{
+
+ if (sid) free(sid);
+ return 1;
+
+}
+
+static
+int nt_delete_ace(ACE *ace)
+{
+
+ if (ace) {
+ nt_delete_sid(ace->trustee);
+ free(ace);
+ }
+ return 1;
+
+}
+
+static
+int nt_delete_acl(ACL *acl)
+{
+
+ if (acl) {
+ int i;
+
+ for (i=0; i<acl->num_aces; i++)
+ nt_delete_ace(acl->aces[i]);
+
+ free(acl);
+ }
+ return 1;
+}
+
+static
+int nt_delete_sec_desc(SEC_DESC *sec_desc)
+{
+
+ if (sec_desc) {
+
+ nt_delete_sid(sec_desc->owner);
+ nt_delete_sid(sec_desc->group);
+ nt_delete_acl(sec_desc->sacl);
+ nt_delete_acl(sec_desc->dacl);
+ free(sec_desc);
+
+ }
+ return 1;
+}
+
+static
+int nt_delete_key_sec_desc(KEY_SEC_DESC *key_sec_desc)
+{
+
+ if (key_sec_desc) {
+ key_sec_desc->ref_cnt--;
+ if (key_sec_desc->ref_cnt<=0) {
+ /*
+ * There should always be a next and prev, even if they point to us
+ */
+ key_sec_desc->next->prev = key_sec_desc->prev;
+ key_sec_desc->prev->next = key_sec_desc->next;
+ nt_delete_sec_desc(key_sec_desc->sec_desc);
+ }
+ }
+ return 1;
+}
+
+static
+int nt_delete_reg_key(REG_KEY *key, int delete_name)
+{
+
+ if (key) {
+ if (key->name) free(key->name);
+ if (key->class_name) free(key->class_name);
+
+ /*
+ * We will delete the owner if we are not the root and told to ...
+ */
+
+ if (key->owner && key->owner->sub_keys && delete_name) {
+ REG_KEY *own;
+ KEY_LIST *kl;
+ int i;
+ /* Find our owner, look in keylist for us and shuffle up */
+ /* Perhaps should be a function */
+
+ own = key->owner;
+ kl = own->sub_keys;
+
+ for (i=0; i < kl->key_count && kl->keys[i] != key ; i++) {
+ /* Just find the entry ... */
+ }
+
+ if (i == kl->key_count) {
+ fprintf(stderr, "Bad data structure. Key not found in key list of owner\n");
+ }
+ else {
+ int j;
+
+ /*
+ * Shuffle up. Works for the last one also
+ */
+ for (j = i + 1; j < kl->key_count; j++) {
+ kl->keys[j - 1] = kl->keys[j];
+ }
+
+ kl->key_count--;
+ }
+ }
+
+ if (key->sub_keys) nt_delete_key_list(key->sub_keys, False);
+ if (key->values) nt_delete_val_list(key->values);
+ if (key->security) nt_delete_key_sec_desc(key->security);
+ free(key);
+ }
+ return 1;
+}
+
+/*
+ * Convert a string to a value ...
+ * FIXME: Error handling and convert this at command parse time ...
+ */
+static
+void *str_to_val(int type, char *val, int *len)
+{
+ unsigned int *dwordp = NULL;
+
+ if (!len || !val) return NULL;
+
+ switch (type) {
+ case REG_TYPE_REGSZ:
+ *len = strlen(val);
+ return (void *)val;
+
+ case REG_TYPE_DWORD:
+ dwordp = (unsigned int *)malloc(sizeof(unsigned int));
+ if (!dwordp) return NULL;
+ /* Allow for ddddd and 0xhhhhh and 0ooooo */
+ if (strncmp(val, "0x", 2) == 0 || strncmp(val, "0X", 2) == 0) {
+ sscanf(&val[2], "%X", dwordp);
+ }
+ else if (*val == '0') {
+ sscanf(&val[1], "%o", dwordp);
+ }
+ else {
+ sscanf(val, "%d", dwordp);
+ }
+ *len = sizeof(unsigned int);
+ return (void *)dwordp;
+
+ /* FIXME: Implement more of these */
+
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+/*
+ * Add a value to the key specified ... We have to parse the value some more
+ * based on the type to get it in the correct internal form
+ * An empty name will be converted to "<No Name>" before here
+ * Hmmm, maybe not. has_name is for that
+ */
+static
+VAL_KEY *nt_add_reg_value(REG_KEY *key, char *name, int type, char *value)
+{
+ int i;
+ VAL_KEY *tmp = NULL;
+
+ if (!key || !key->values || !name || !*name) return NULL;
+
+ assert(type != REG_TYPE_DELETE); /* We never process deletes here */
+
+ for (i = 0; i < key->values->val_count; i++) {
+ if ((!key->values->vals[i]->has_name && !*name) ||
+ (key->values->vals[i]->has_name &&
+ strcmp(name, key->values->vals[i]->name) == 0)){ /* Change the value */
+ free(key->values->vals[i]->data_blk);
+ key->values->vals[i]->data_blk = str_to_val(type, value, &
+ key->values->vals[i]->data_len);
+ return key->values->vals[i];
+ }
+ }
+
+ /*
+ * If we get here, the name was not found, so insert it
+ */
+
+ tmp = (VAL_KEY *)malloc(sizeof(VAL_KEY));
+ if (!tmp) goto error;
+
+ memset(tmp, 0, sizeof(VAL_KEY));
+ tmp->name = strdup(name);
+ tmp->has_name = True;
+ if (!tmp->name) goto error;
+ tmp->data_type = type;
+ tmp->data_blk = str_to_val(type, value, &tmp->data_len);
+
+ /* Now, add to val list */
+
+ if (key->values->val_count >= key->values->max_vals) {
+ /*
+ * Allocate some more space
+ */
+
+ if ((key->values = (VAL_LIST *)realloc(key->values, sizeof(VAL_LIST) +
+ key->values->val_count - 1 +
+ REG_KEY_LIST_SIZE))) {
+ key->values->max_vals += REG_KEY_LIST_SIZE;
+ }
+ else goto error;
+ }
+
+ i = key->values->val_count;
+ key->values->val_count++;
+ key->values->vals[i] = tmp;
+ return tmp;
+
+ error:
+ if (tmp) nt_delete_val_key(tmp);
+ return NULL;
+}
+
+/*
+ * Delete a value. We return the value and let the caller deal with it.
+ */
+static
+VAL_KEY *nt_delete_reg_value(REG_KEY *key, char *name)
+{
+ int i, j;
+
+ if (!key || !key->values || !name || !*name) return NULL;
+
+ /* FIXME: Allow empty value name */
+ for (i = 0; i< key->values->val_count; i++) {
+ if ((!key->values->vals[i]->has_name && !*name) ||
+ (key->values->vals[i]->has_name &&
+ strcmp(name, key->values->vals[i]->name) == 0)) {
+ VAL_KEY *val;
+
+ val = key->values->vals[i];
+
+ /* Shuffle down */
+ for (j = i + 1; j < key->values->val_count; j++)
+ key->values->vals[j - 1] = key->values->vals[j];
+
+ key->values->val_count--;
+
+ return val;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Add a key to the tree ... We walk down the components matching until
+ * we don't find any. There must be a match on the first component ...
+ * We return the key structure for the final component as that is
+ * often where we want to add values ...
+ */
+
+/*
+ * Convert a string of the form S-1-5-x[-y-z-r] to a SID
+ */
+static
+int sid_string_to_sid(sid_t **sid, const char *sid_str)
+{
+ int i = 0, auth;
+ const char *lstr;
+
+ *sid = (sid_t *)malloc(sizeof(sid_t));
+ if (!*sid) return 0;
+
+ memset(*sid, 0, sizeof(sid_t));
+
+ if (strncmp(sid_str, "S-1-5", 5)) {
+ fprintf(stderr, "Does not conform to S-1-5...: %s\n", sid_str);
+ return 0;
+ }
+
+ /* We only allow strings of form S-1-5... */
+
+ (*sid)->ver = 1;
+ (*sid)->auth[5] = 5;
+
+ lstr = sid_str + 5;
+
+ while (1) {
+ if (!lstr || !lstr[0] || sscanf(lstr, "-%u", &auth) == 0) {
+ if (i < 1) {
+ fprintf(stderr, "Not of form -d-d...: %s, %u\n", lstr, i);
+ return 0;
+ }
+ (*sid)->auths=i;
+ return 1;
+ }
+
+ (*sid)->sub_auths[i] = auth;
+ i++;
+ lstr = strchr(lstr + 1, '-');
+ }
+
+ /*return 1; */ /* Not Reached ... */
+}
+
+/*
+ * Create an ACE
+ */
+static
+ACE *nt_create_ace(int type, int flags, unsigned int perms, const char *sid)
+{
+ ACE *ace;
+
+ ace = (ACE *)malloc(sizeof(ACE));
+ if (!ace) goto error;
+ ace->type = type;
+ ace->flags = flags;
+ ace->perms = perms;
+ if (!sid_string_to_sid(&ace->trustee, sid))
+ goto error;
+ return ace;
+
+ error:
+ if (ace) nt_delete_ace(ace);
+ return NULL;
+}
+
+/*
+ * Create a default ACL
+ */
+static
+ACL *nt_create_default_acl(REGF *regf)
+{
+ ACL *acl;
+
+ acl = (ACL *)malloc(sizeof(ACL) + 7*sizeof(ACE *));
+ if (!acl) goto error;
+
+ acl->rev = 2;
+ acl->refcnt = 1;
+ acl->num_aces = 8;
+
+ acl->aces[0] = nt_create_ace(0x00, 0x0, 0xF003F, regf->owner_sid_str);
+ if (!acl->aces[0]) goto error;
+ acl->aces[1] = nt_create_ace(0x00, 0x0, 0xF003F, "S-1-5-18");
+ if (!acl->aces[1]) goto error;
+ acl->aces[2] = nt_create_ace(0x00, 0x0, 0xF003F, "S-1-5-32-544");
+ if (!acl->aces[2]) goto error;
+ acl->aces[3] = nt_create_ace(0x00, 0x0, 0x20019, "S-1-5-12");
+ if (!acl->aces[3]) goto error;
+ acl->aces[4] = nt_create_ace(0x00, 0x0B, 0x10000000, regf->owner_sid_str);
+ if (!acl->aces[4]) goto error;
+ acl->aces[5] = nt_create_ace(0x00, 0x0B, 0x10000000, "S-1-5-18");
+ if (!acl->aces[5]) goto error;
+ acl->aces[6] = nt_create_ace(0x00, 0x0B, 0x10000000, "S-1-5-32-544");
+ if (!acl->aces[6]) goto error;
+ acl->aces[7] = nt_create_ace(0x00, 0x0B, 0x80000000, "S-1-5-12");
+ if (!acl->aces[7]) goto error;
+ return acl;
+
+ error:
+ if (acl) nt_delete_acl(acl);
+ return NULL;
+}
+
+/*
+ * Create a default security descriptor. We pull in things from env
+ * if need be
+ */
+static
+SEC_DESC *nt_create_def_sec_desc(REGF *regf)
+{
+ SEC_DESC *tmp;
+
+ tmp = (SEC_DESC *)malloc(sizeof(SEC_DESC));
+ if (!tmp) return NULL;
+
+ tmp->rev = 1;
+ tmp->type = 0x8004;
+ if (!sid_string_to_sid(&tmp->owner, "S-1-5-32-544")) goto error;
+ if (!sid_string_to_sid(&tmp->group, "S-1-5-18")) goto error;
+ tmp->sacl = NULL;
+ tmp->dacl = nt_create_default_acl(regf);
+
+ return tmp;
+
+ error:
+ if (tmp) nt_delete_sec_desc(tmp);
+ return NULL;
+}
+
+/*
+ * We will implement inheritence that is based on what the parent's SEC_DESC
+ * says, but the Owner and Group SIDs can be overwridden from the command line
+ * and additional ACEs can be applied from the command line etc.
+ */
+static
+KEY_SEC_DESC *nt_inherit_security(REG_KEY *key)
+{
+
+ if (!key) return NULL;
+ return key->security;
+}
+
+/*
+ * Create an initial security descriptor and init other structures, if needed
+ * We assume that the initial security stuff is empty ...
+ */
+static
+KEY_SEC_DESC *nt_create_init_sec(REGF *regf)
+{
+ KEY_SEC_DESC *tsec = NULL;
+
+ tsec = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
+ if (!tsec) return NULL;
+
+ tsec->ref_cnt = 1;
+ tsec->state = SEC_DESC_NBK;
+ tsec->offset = 0;
+
+ tsec->sec_desc = regf->def_sec_desc;
+
+ return tsec;
+}
+
+/*
+ * Add a sub-key
+ */
+static
+REG_KEY *nt_add_reg_key_list(REGF *regf, REG_KEY *key, char * name, int create)
+{
+ int i;
+ REG_KEY *ret = NULL, *tmp = NULL;
+ KEY_LIST *list;
+ char *lname, *c1, *c2;
+
+ if (!key || !name || !*name) return NULL;
+
+ list = key->sub_keys;
+ if (!list) { /* Create an empty list */
+
+ list = (KEY_LIST *)malloc(sizeof(KEY_LIST) + (REG_KEY_LIST_SIZE - 1) * sizeof(REG_KEY *));
+ list->key_count = 0;
+ list->max_keys = REG_KEY_LIST_SIZE;
+
+ }
+
+ lname = strdup(name);
+ if (!lname) return NULL;
+
+ c1 = lname;
+ c2 = strchr(c1, '\\');
+ if (c2) { /* Split here ... */
+ *c2 = 0;
+ c2++;
+ }
+
+ for (i = 0; i < list->key_count; i++) {
+ if (strcmp(list->keys[i]->name, c1) == 0) {
+ ret = nt_add_reg_key_list(regf, list->keys[i], c2, create);
+ free(lname);
+ return ret;
+ }
+ }
+
+ /*
+ * If we reach here we could not find the the first component
+ * so create it ...
+ */
+
+ if (list->key_count < list->max_keys){
+ list->key_count++;
+ }
+ else { /* Create more space in the list ... */
+ if (!(list = (KEY_LIST *)realloc(list, sizeof(KEY_LIST) +
+ (list->max_keys + REG_KEY_LIST_SIZE - 1)
+ * sizeof(REG_KEY *))))
+ goto error;
+
+ list->max_keys += REG_KEY_LIST_SIZE;
+ list->key_count++;
+ }
+
+ /*
+ * add the new key at the new slot
+ * FIXME: Sort the list someday
+ */
+
+ /*
+ * We want to create the key, and then do the rest
+ */
+
+ tmp = (REG_KEY *)malloc(sizeof(REG_KEY));
+
+ memset(tmp, 0, sizeof(REG_KEY));
+
+ tmp->name = strdup(c1);
+ if (!tmp->name) goto error;
+ tmp->owner = key;
+ tmp->type = REG_SUB_KEY;
+ /*
+ * Next, pull security from the parent, but override with
+ * anything passed in on the command line
+ */
+ tmp->security = nt_inherit_security(key);
+
+ list->keys[list->key_count - 1] = tmp;
+
+ if (c2) {
+ ret = nt_add_reg_key_list(regf, key, c2, True);
+ }
+
+ if (lname) free(lname);
+
+ return ret;
+
+ error:
+ if (tmp) free(tmp);
+ if (lname) free(lname);
+ return NULL;
+}
+
+/*
+ * This routine only adds a key from the root down.
+ * It calls helper functions to handle sub-key lists and sub-keys
+ */
+static
+REG_KEY *nt_add_reg_key(REGF *regf, char *name, int create)
+{
+ char *lname = NULL, *c1, *c2;
+ REG_KEY * tmp = NULL;
+
+ /*
+ * Look until we hit the first component that does not exist, and
+ * then add from there. However, if the first component does not
+ * match and the path we are given is the root, then it must match
+ */
+ if (!regf || !name || !*name) return NULL;
+
+ lname = strdup(name);
+ if (!lname) return NULL;
+
+ c1 = lname;
+ c2 = strchr(c1, '\\');
+ if (c2) { /* Split here ... */
+ *c2 = 0;
+ c2++;
+ }
+
+ /*
+ * If the root does not exist, create it and make it equal to the
+ * first component ...
+ */
+
+ if (!regf->root) {
+
+ tmp = (REG_KEY *)malloc(sizeof(REG_KEY));
+ if (!tmp) goto error;
+ memset(tmp, 0, sizeof(REG_KEY));
+ tmp->name = strdup(c1);
+ if (!tmp->name) goto error;
+ tmp->security = nt_create_init_sec(regf);
+ if (!tmp->security) goto error;
+ regf->root = tmp;
+
+ }
+ else {
+ /*
+ * If we don't match, then we have to return error ...
+ * If we do match on this component, check the next one in the
+ * list, and if not found, add it ... short circuit, add all the
+ * way down
+ */
+
+ if (strcmp(c1, regf->root->name) != 0)
+ goto error;
+ }
+
+ tmp = nt_add_reg_key_list(regf, regf->root, c2, True);
+ free(lname);
+ return tmp;
+
+ error:
+ if (tmp) free(tmp);
+ if (lname) free(lname);
+ return NULL;
+}
+
+/*
+ * Load and unload a registry file.
+ *
+ * Load, loads it into memory as a tree, while unload sealizes/flattens it
+ */
+
+/*
+ * Get the starting record for NT Registry file
+ */
+
+/*
+ * Where we keep all the regf stuff for one registry.
+ * This is the structure that we use to tie the in memory tree etc
+ * together. By keeping separate structs, we can operate on different
+ * registries at the same time.
+ * Currently, the SK_MAP is an array of mapping structure.
+ * Since we only need this on input and output, we fill in the structure
+ * as we go on input. On output, we know how many SK items we have, so
+ * we can allocate the structure as we need to.
+ * If you add stuff here that is dynamically allocated, add the
+ * appropriate free statements below.
+ */
+
+#define REGF_REGTYPE_NONE 0
+#define REGF_REGTYPE_NT 1
+#define REGF_REGTYPE_W9X 2
+
+#define TTTONTTIME(r, t1, t2) (r)->last_mod_time.low = (t1); \
+ (r)->last_mod_time.high = (t2);
+
+#define REGF_HDR_BLKSIZ 0x1000
+
+#define OFF(f) ((f) + REGF_HDR_BLKSIZ + 4)
+#define LOCN(base, f) ((base) + OFF(f))
+
+const VAL_STR reg_type_names[] = {
+ { REG_TYPE_REGSZ, "REG_SZ" },
+ { REG_TYPE_EXPANDSZ, "REG_EXPAND_SZ" },
+ { REG_TYPE_BIN, "REG_BIN" },
+ { REG_TYPE_DWORD, "REG_DWORD" },
+ { REG_TYPE_MULTISZ, "REG_MULTI_SZ" },
+ { 0, NULL },
+};
+
+static
+const char *val_to_str(unsigned int val, const VAL_STR *val_array)
+{
+ int i = 0;
+
+ if (!val_array) return NULL;
+
+ while (val_array[i].val && val_array[i].str) {
+
+ if (val_array[i].val == val) return val_array[i].str;
+ i++;
+
+ }
+
+ return NULL;
+
+}
+
+/*
+ * Convert from UniCode to Ascii ... Does not take into account other lang
+ * Restrict by ascii_max if > 0
+ */
+static
+int uni_to_ascii(unsigned char *uni, unsigned char *ascii, int ascii_max,
+ int uni_max)
+{
+ int i = 0;
+
+ while (i < ascii_max && !(!uni[i*2] && !uni[i*2+1])) {
+ if (uni_max > 0 && (i*2) >= uni_max) break;
+ ascii[i] = uni[i*2];
+ i++;
+
+ }
+
+ ascii[i] = '\0';
+
+ return i;
+}
+
+/*
+ * Convert a data value to a string for display
+ */
+static
+int data_to_ascii(unsigned char *datap, int len, int type, char *ascii, int ascii_max)
+{
+ unsigned char *asciip;
+ int i;
+
+ switch (type) {
+ case REG_TYPE_REGSZ:
+ if (verbose) fprintf(stderr, "Len: %d\n", len);
+ /* FIXME. This has to be fixed. It has to be UNICODE */
+ return uni_to_ascii(datap, ascii, len, ascii_max);
+ break; /*NOTREACHED*/
+
+ case REG_TYPE_EXPANDSZ:
+ return uni_to_ascii(datap, ascii, len, ascii_max);
+ break;
+
+ case REG_TYPE_BIN:
+ asciip = ascii;
+ for (i=0; (i<len)&&(i+1)*3<ascii_max; i++) {
+ int str_rem = ascii_max - ((int)asciip - (int)ascii);
+ asciip += snprintf(asciip, str_rem, "%02x", *(unsigned char *)(datap+i));
+ if (i < len && str_rem > 0)
+ *asciip = ' '; asciip++;
+ }
+ *asciip = '\0';
+ return ((int)asciip - (int)ascii);
+ break;
+
+ case REG_TYPE_DWORD:
+ if (*(int *)datap == 0)
+ return snprintf(ascii, ascii_max, "0");
+ else
+ return snprintf(ascii, ascii_max, "0x%x", *(int *)datap);
+ break;
+
+ case REG_TYPE_MULTISZ:
+
+ break;
+
+ default:
+ return 0;
+ break;
+ }
+
+ return len;
+
+}
+
+static
+REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size, REG_KEY *parent);
+
+static
+int nt_set_regf_input_file(REGF *regf, char *filename)
+{
+ return ((regf->regfile_name = strdup(filename)) != NULL);
+}
+
+static
+int nt_set_regf_output_file(REGF *regf, char *filename)
+{
+ return ((regf->outfile_name = strdup(filename)) != NULL);
+}
+
+/* Create a regf structure and init it */
+
+static
+REGF *nt_create_regf(void)
+{
+ REGF *tmp = (REGF *)malloc(sizeof(REGF));
+ if (!tmp) return tmp;
+ memset(tmp, 0, sizeof(REGF));
+ tmp->owner_sid_str = def_owner_sid_str;
+ return tmp;
+}
+
+/* Free all the bits and pieces ... Assumes regf was malloc'd */
+/* If you add stuff to REGF, add the relevant free bits here */
+static
+int nt_free_regf(REGF *regf)
+{
+ if (!regf) return 0;
+
+ if (regf->regfile_name) free(regf->regfile_name);
+ if (regf->outfile_name) free(regf->outfile_name);
+
+ nt_delete_reg_key(regf->root, False); /* Free the tree */
+ free(regf->sk_map);
+ regf->sk_count = regf->sk_map_size = 0;
+
+ free(regf);
+
+ return 1;
+}
+
+/* Get the header of the registry. Return a pointer to the structure
+ * If the mmap'd area has not been allocated, then mmap the input file
+ */
+static
+REGF_HDR *nt_get_regf_hdr(REGF *regf)
+{
+ if (!regf)
+ return NULL; /* What about errors */
+
+ if (!regf->regfile_name)
+ return NULL; /* What about errors */
+
+ if (!regf->base) { /* Try to mmap etc the file */
+
+ if ((regf->fd = open(regf->regfile_name, O_RDONLY, 0000)) <0) {
+ return NULL; /* What about errors? */
+ }
+
+ if (fstat(regf->fd, &regf->sbuf) < 0) {
+ return NULL;
+ }
+
+ regf->base = mmap(0, regf->sbuf.st_size, PROT_READ, MAP_SHARED, regf->fd, 0);
+
+ if ((int)regf->base == 1) {
+ fprintf(stderr, "Could not mmap file: %s, %s\n", regf->regfile_name,
+ strerror(errno));
+ return NULL;
+ }
+ }
+
+ /*
+ * At this point, regf->base != NULL, and we should be able to read the
+ * header
+ */
+
+ assert(regf->base != NULL);
+
+ return (REGF_HDR *)regf->base;
+}
+
+/*
+ * Validate a regf header
+ * For now, do nothing, but we should check the checksum
+ */
+static
+int valid_regf_hdr(REGF_HDR *regf_hdr)
+{
+ if (!regf_hdr) return 0;
+
+ return 1;
+}
+
+/*
+ * Process an SK header ...
+ * Every time we see a new one, add it to the map. Otherwise, just look it up.
+ * We will do a simple linear search for the moment, since many KEYs have the
+ * same security descriptor.
+ * We allocate the map in increments of 10 entries.
+ */
+
+/*
+ * Create a new entry in the map, and increase the size of the map if needed
+ */
+static
+SK_MAP *alloc_sk_map_entry(REGF *regf, KEY_SEC_DESC *tmp, int sk_off)
+{
+ if (!regf->sk_map) { /* Allocate a block of 10 */
+ regf->sk_map = (SK_MAP *)malloc(sizeof(SK_MAP) * 10);
+ if (!regf->sk_map) {
+ free(tmp);
+ return NULL;
+ }
+ regf->sk_map_size = 10;
+ regf->sk_count = 1;
+ (regf->sk_map)[0].sk_off = sk_off;
+ (regf->sk_map)[0].key_sec_desc = tmp;
+ }
+ else { /* Simply allocate a new slot, unless we have to expand the list */
+ int ndx = regf->sk_count;
+ if (regf->sk_count >= regf->sk_map_size) {
+ regf->sk_map = (SK_MAP *)realloc(regf->sk_map,
+ (regf->sk_map_size + 10)*sizeof(SK_MAP));
+ if (!regf->sk_map) {
+ free(tmp);
+ return NULL;
+ }
+ /*
+ * ndx already points at the first entry of the new block
+ */
+ regf->sk_map_size += 10;
+ }
+ (regf->sk_map)[ndx].sk_off = sk_off;
+ (regf->sk_map)[ndx].key_sec_desc = tmp;
+ regf->sk_count++;
+ }
+ return regf->sk_map;
+}
+
+/*
+ * Search for a KEY_SEC_DESC in the sk_map, but don't create one if not
+ * found
+ */
+static
+KEY_SEC_DESC *lookup_sec_key(SK_MAP *sk_map, int count, int sk_off)
+{
+ int i;
+
+ if (!sk_map) return NULL;
+
+ for (i = 0; i < count; i++) {
+
+ if (sk_map[i].sk_off == sk_off)
+ return sk_map[i].key_sec_desc;
+
+ }
+
+ return NULL;
+
+}
+
+/*
+ * Allocate a KEY_SEC_DESC if we can't find one in the map
+ */
+static
+KEY_SEC_DESC *lookup_create_sec_key(REGF *regf, SK_MAP *sk_map, int sk_off)
+{
+ KEY_SEC_DESC *tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off);
+
+ if (tmp) {
+ return tmp;
+ }
+ else { /* Allocate a new one */
+ tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
+ if (!tmp) {
+ return NULL;
+ }
+ memset(tmp, 0, sizeof(KEY_SEC_DESC)); /* Neatly sets offset to 0 */
+ tmp->state = SEC_DESC_RES;
+ if (!alloc_sk_map_entry(regf, tmp, sk_off)) {
+ return NULL;
+ }
+ return tmp;
+ }
+}
+
+/*
+ * Allocate storage and duplicate a SID
+ * We could allocate the SID to be only the size needed, but I am too lazy.
+ */
+static
+sid_t *dup_sid(sid_t *sid)
+{
+ sid_t *tmp = (sid_t *)malloc(sizeof(sid_t));
+ int i;
+
+ if (!tmp) return NULL;
+ tmp->ver = sid->ver;
+ tmp->auths = sid->auths;
+ for (i=0; i<6; i++) {
+ tmp->auth[i] = sid->auth[i];
+ }
+ for (i=0; i<tmp->auths&&i<MAXSUBAUTHS; i++) {
+ tmp->sub_auths[i] = sid->sub_auths[i];
+ }
+ return tmp;
+}
+
+/*
+ * Allocate space for an ACE and duplicate the registry encoded one passed in
+ */
+static
+ACE *dup_ace(REG_ACE *ace)
+{
+ ACE *tmp = NULL;
+
+ tmp = (ACE *)malloc(sizeof(ACE));
+
+ if (!tmp) return NULL;
+
+ tmp->type = CVAL(&ace->type);
+ tmp->flags = CVAL(&ace->flags);
+ tmp->perms = IVAL(&ace->perms);
+ tmp->trustee = dup_sid(&ace->trustee);
+ return tmp;
+}
+
+/*
+ * Allocate space for an ACL and duplicate the registry encoded one passed in
+ */
+static
+ACL *dup_acl(REG_ACL *acl)
+{
+ ACL *tmp = NULL;
+ REG_ACE* ace;
+ int i, num_aces;
+
+ num_aces = IVAL(&acl->num_aces);
+
+ tmp = (ACL *)malloc(sizeof(ACL) + (num_aces - 1)*sizeof(ACE *));
+ if (!tmp) return NULL;
+
+ tmp->num_aces = num_aces;
+ tmp->refcnt = 1;
+ tmp->rev = SVAL(&acl->rev);
+ if (verbose) fprintf(stdout, "ACL: refcnt: %u, rev: %u\n", tmp->refcnt,
+ tmp->rev);
+ ace = (REG_ACE *)&acl->aces;
+ for (i=0; i<num_aces; i++) {
+ tmp->aces[i] = dup_ace(ace);
+ ace = (REG_ACE *)((char *)ace + SVAL(&ace->length));
+ /* XXX: FIXME, should handle malloc errors */
+ }
+
+ return tmp;
+}
+
+static
+SEC_DESC *process_sec_desc(REGF *regf, REG_SEC_DESC *sec_desc)
+{
+ SEC_DESC *tmp = NULL;
+
+ tmp = (SEC_DESC *)malloc(sizeof(SEC_DESC));
+
+ if (!tmp) {
+ return NULL;
+ }
+
+ tmp->rev = SVAL(&sec_desc->rev);
+ tmp->type = SVAL(&sec_desc->type);
+ if (verbose) fprintf(stdout, "SEC_DESC Rev: %0X, Type: %0X\n",
+ tmp->rev, tmp->type);
+ if (verbose) fprintf(stdout, "SEC_DESC Owner Off: %0X\n",
+ IVAL(&sec_desc->owner_off));
+ if (verbose) fprintf(stdout, "SEC_DESC Group Off: %0X\n",
+ IVAL(&sec_desc->group_off));
+ if (verbose) fprintf(stdout, "SEC_DESC DACL Off: %0X\n",
+ IVAL(&sec_desc->dacl_off));
+ tmp->owner = dup_sid((sid_t *)((char *)sec_desc + IVAL(&sec_desc->owner_off)));
+ if (!tmp->owner) {
+ free(tmp);
+ return NULL;
+ }
+ tmp->group = dup_sid((sid_t *)((char *)sec_desc + IVAL(&sec_desc->group_off)));
+ if (!tmp->group) {
+ free(tmp);
+ return NULL;
+ }
+
+ /* Now pick up the SACL and DACL */
+
+ if (sec_desc->sacl_off)
+ tmp->sacl = dup_acl((REG_ACL *)((char *)sec_desc + IVAL(&sec_desc->sacl_off)));
+ else
+ tmp->sacl = NULL;
+
+ if (sec_desc->dacl_off)
+ tmp->dacl = dup_acl((REG_ACL *)((char *)sec_desc + IVAL(&sec_desc->dacl_off)));
+ else
+ tmp->dacl = NULL;
+
+ return tmp;
+}
+
+static
+KEY_SEC_DESC *process_sk(REGF *regf, SK_HDR *sk_hdr, int sk_off, int size)
+{
+ KEY_SEC_DESC *tmp = NULL;
+ int sk_next_off, sk_prev_off, sk_size;
+ REG_SEC_DESC *sec_desc;
+
+ if (!sk_hdr) return NULL;
+
+ if (SVAL(&sk_hdr->SK_ID) != REG_SK_ID) {
+ fprintf(stderr, "Unrecognized SK Header ID: %08X, %s\n", (int)sk_hdr,
+ regf->regfile_name);
+ return NULL;
+ }
+
+ if (-size < (sk_size = IVAL(&sk_hdr->rec_size))) {
+ fprintf(stderr, "Incorrect SK record size: %d vs %d. %s\n",
+ -size, sk_size, regf->regfile_name);
+ return NULL;
+ }
+
+ /*
+ * Now, we need to look up the SK Record in the map, and return it
+ * Since the map contains the SK_OFF mapped to KEY_SEC_DESC, we can
+ * use that
+ */
+
+ if (regf->sk_map &&
+ ((tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off)) != NULL)
+ && (tmp->state == SEC_DESC_OCU)) {
+ tmp->ref_cnt++;
+ return tmp;
+ }
+
+ /* Here, we have an item in the map that has been reserved, or tmp==NULL. */
+
+ assert(tmp == NULL || (tmp && tmp->state != SEC_DESC_NON));
+
+ /*
+ * Now, allocate a KEY_SEC_DESC, and parse the structure here, and add the
+ * new KEY_SEC_DESC to the mapping structure, since the offset supplied is
+ * the actual offset of structure. The same offset will be used by
+ * all future references to this structure
+ * We could put all this unpleasantness in a function.
+ */
+
+ if (!tmp) {
+ tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
+ if (!tmp) return NULL;
+ memset(tmp, 0, sizeof(KEY_SEC_DESC));
+
+ /*
+ * Allocate an entry in the SK_MAP ...
+ * We don't need to free tmp, because that is done for us if the
+ * sm_map entry can't be expanded when we need more space in the map.
+ */
+
+ if (!alloc_sk_map_entry(regf, tmp, sk_off)) {
+ return NULL;
+ }
+ }
+
+ tmp->ref_cnt++;
+ tmp->state = SEC_DESC_OCU;
+
+ /*
+ * Now, process the actual sec desc and plug the values in
+ */
+
+ sec_desc = (REG_SEC_DESC *)&sk_hdr->sec_desc[0];
+ tmp->sec_desc = process_sec_desc(regf, sec_desc);
+
+ /*
+ * Now forward and back links. Here we allocate an entry in the sk_map
+ * if it does not exist, and mark it reserved
+ */
+
+ sk_prev_off = IVAL(&sk_hdr->prev_off);
+ tmp->prev = lookup_create_sec_key(regf, regf->sk_map, sk_prev_off);
+ assert(tmp->prev != NULL);
+ sk_next_off = IVAL(&sk_hdr->next_off);
+ tmp->next = lookup_create_sec_key(regf, regf->sk_map, sk_next_off);
+ assert(tmp->next != NULL);
+
+ return tmp;
+}
+
+/*
+ * Process a VK header and return a value
+ */
+static
+VAL_KEY *process_vk(REGF *regf, VK_HDR *vk_hdr, int size)
+{
+ char val_name[1024];
+ int nam_len, dat_len, flag, dat_type, dat_off, vk_id;
+ const char *val_type;
+ VAL_KEY *tmp = NULL;
+
+ if (!vk_hdr) return NULL;
+
+ if ((vk_id = SVAL(&vk_hdr->VK_ID)) != REG_VK_ID) {
+ fprintf(stderr, "Unrecognized VK header ID: %0X, block: %0X, %s\n",
+ vk_id, (int)vk_hdr, regf->regfile_name);
+ return NULL;
+ }
+
+ nam_len = SVAL(&vk_hdr->nam_len);
+ val_name[nam_len] = '\0';
+ flag = SVAL(&vk_hdr->flag);
+ dat_type = IVAL(&vk_hdr->dat_type);
+ dat_len = IVAL(&vk_hdr->dat_len); /* If top bit, offset contains data */
+ dat_off = IVAL(&vk_hdr->dat_off);
+
+ tmp = (VAL_KEY *)malloc(sizeof(VAL_KEY));
+ if (!tmp) {
+ goto error;
+ }
+ memset(tmp, 0, sizeof(VAL_KEY));
+ tmp->has_name = flag;
+ tmp->data_type = dat_type;
+
+ if (flag & 0x01) {
+ strncpy(val_name, vk_hdr->dat_name, nam_len);
+ tmp->name = strdup(val_name);
+ if (!tmp->name) {
+ goto error;
+ }
+ }
+ else
+ strncpy(val_name, "<No Name>", 10);
+
+ /*
+ * Allocate space and copy the data as a BLOB
+ */
+
+ if (dat_len) {
+
+ char *dtmp = (char *)malloc(dat_len&0x7FFFFFFF);
+
+ if (!dtmp) {
+ goto error;
+ }
+
+ tmp->data_blk = dtmp;
+
+ if ((dat_len&0x80000000) == 0) { /* The data is pointed to by the offset */
+ char *dat_ptr = LOCN(regf->base, dat_off);
+ bcopy(dat_ptr, dtmp, dat_len);
+ }
+ else { /* The data is in the offset or type */
+ /*
+ * FIXME.
+ * Some registry files seem to have wierd fields. If top bit is set,
+ * but len is 0, the type seems to be the value ...
+ * Not sure how to handle this last type for the moment ...
+ */
+ dat_len = dat_len & 0x7FFFFFFF;
+ bcopy(&dat_off, dtmp, dat_len);
+ }
+
+ tmp->data_len = dat_len;
+ }
+
+ val_type = val_to_str(dat_type, reg_type_names);
+
+ /*
+ * We need to save the data area as well
+ */
+
+ if (verbose) fprintf(stdout, " %s : %s : \n", val_name, val_type);
+
+ return tmp;
+
+ error:
+ if (tmp) nt_delete_val_key(tmp);
+ return NULL;
+
+}
+
+/*
+ * Process a VL Header and return a list of values
+ */
+static
+VAL_LIST *process_vl(REGF *regf, VL_TYPE vl, int count, int size)
+{
+ int i, vk_off;
+ VK_HDR *vk_hdr;
+ VAL_LIST *tmp = NULL;
+
+ if (!vl) return NULL;
+
+ if (-size < (count+1)*sizeof(int)){
+ fprintf(stderr, "Error in VL header format. Size less than space required. %d\n", -size);
+ return NULL;
+ }
+
+ tmp = (VAL_LIST *)malloc(sizeof(VAL_LIST) + (count - 1) * sizeof(VAL_KEY *));
+ if (!tmp) {
+ goto error;
+ }
+
+ for (i=0; i<count; i++) {
+ vk_off = IVAL(&vl[i]);
+ vk_hdr = (VK_HDR *)LOCN(regf->base, vk_off);
+ tmp->vals[i] = process_vk(regf, vk_hdr, BLK_SIZE(vk_hdr));
+ if (!tmp->vals[i]){
+ goto error;
+ }
+ }
+
+ tmp->val_count = count;
+ tmp->max_vals = count;
+
+ return tmp;
+
+ error:
+ /* XXX: FIXME, free the partially allocated structure */
+ return NULL;
+}
+
+/*
+ * Process an LF Header and return a list of sub-keys
+ */
+static
+KEY_LIST *process_lf(REGF *regf, LF_HDR *lf_hdr, int size, REG_KEY *parent)
+{
+ int count, i, nk_off;
+ unsigned int lf_id;
+ KEY_LIST *tmp;
+
+ if (!lf_hdr) return NULL;
+
+ if ((lf_id = SVAL(&lf_hdr->LF_ID)) != REG_LF_ID) {
+ fprintf(stderr, "Unrecognized LF Header format: %0X, Block: %0X, %s.\n",
+ lf_id, (int)lf_hdr, regf->regfile_name);
+ return NULL;
+ }
+
+ assert(size < 0);
+
+ count = SVAL(&lf_hdr->key_count);
+ if (verbose) fprintf(stdout, "Key Count: %u\n", count);
+ if (count <= 0) return NULL;
+
+ /* Now, we should allocate a KEY_LIST struct and fill it in ... */
+
+ tmp = (KEY_LIST *)malloc(sizeof(KEY_LIST) + (count - 1) * sizeof(REG_KEY *));
+ if (!tmp) {
+ goto error;
+ }
+
+ tmp->key_count = count;
+ tmp->max_keys = count;
+
+ for (i=0; i<count; i++) {
+ NK_HDR *nk_hdr;
+
+ nk_off = IVAL(&lf_hdr->hr[i].nk_off);
+ if (verbose) fprintf(stdout, "NK Offset: %0X\n", nk_off);
+ nk_hdr = (NK_HDR *)LOCN(regf->base, nk_off);
+ tmp->keys[i] = nt_get_key_tree(regf, nk_hdr, BLK_SIZE(nk_hdr), parent);
+ if (!tmp->keys[i]) {
+ goto error;
+ }
+ }
+
+ return tmp;
+
+ error:
+ if (tmp) nt_delete_key_list(tmp, False);
+ return NULL;
+}
+
+/*
+ * This routine is passed an NK_HDR pointer and retrieves the entire tree
+ * from there down. It returns a REG_KEY *.
+ */
+static
+REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size, REG_KEY *parent)
+{
+ REG_KEY *tmp = NULL, *own;
+ int name_len, clsname_len, lf_off, val_off, val_count, sk_off, own_off;
+ unsigned int nk_id;
+ LF_HDR *lf_hdr;
+ VL_TYPE *vl;
+ SK_HDR *sk_hdr;
+ char key_name[1024], cls_name[1024];
+
+ if (!nk_hdr) return NULL;
+
+ if ((nk_id = SVAL(&nk_hdr->NK_ID)) != REG_NK_ID) {
+ fprintf(stderr, "Unrecognized NK Header format: %08X, Block: %0X. %s\n",
+ nk_id, (int)nk_hdr, regf->regfile_name);
+ return NULL;
+ }
+
+ assert(size < 0);
+
+ name_len = SVAL(&nk_hdr->nam_len);
+ clsname_len = SVAL(&nk_hdr->clsnam_len);
+
+ /*
+ * The value of -size should be ge
+ * (sizeof(NK_HDR) - 1 + name_len)
+ * The -1 accounts for the fact that we included the first byte of
+ * the name in the structure. clsname_len is the length of the thing
+ * pointed to by clsnam_off
+ */
+
+ if (-size < (sizeof(NK_HDR) - 1 + name_len)) {
+ fprintf(stderr, "Incorrect NK_HDR size: %d, %0X\n", -size, (int)nk_hdr);
+ fprintf(stderr, "Sizeof NK_HDR: %d, name_len %d, clsname_len %d\n",
+ sizeof(NK_HDR), name_len, clsname_len);
+ /*return NULL;*/
+ }
+
+ if (verbose) fprintf(stdout, "NK HDR: Name len: %d, class name len: %d\n",
+ name_len, clsname_len);
+
+ /* Fish out the key name and process the LF list */
+
+ assert(name_len < sizeof(key_name));
+
+ /* Allocate the key struct now */
+ tmp = (REG_KEY *)malloc(sizeof(REG_KEY));
+ if (!tmp) return tmp;
+ memset(tmp, 0, sizeof(REG_KEY));
+
+ tmp->type = (SVAL(&nk_hdr->type)==0x2C?REG_ROOT_KEY:REG_SUB_KEY);
+
+ strncpy(key_name, nk_hdr->key_nam, name_len);
+ key_name[name_len] = '\0';
+
+ if (verbose) fprintf(stdout, "Key name: %s\n", key_name);
+
+ tmp->name = strdup(key_name);
+ if (!tmp->name) {
+ goto error;
+ }
+
+ /*
+ * Fish out the class name, it is in UNICODE, while the key name is
+ * ASCII :-)
+ */
+
+ if (clsname_len) { /* Just print in Ascii for now */
+ char *clsnamep;
+ int clsnam_off;
+
+ clsnam_off = IVAL(&nk_hdr->clsnam_off);
+ clsnamep = LOCN(regf->base, clsnam_off);
+ if (verbose) fprintf(stdout, "Class Name Offset: %0X\n", clsnam_off);
+
+ memset(cls_name, 0, clsname_len);
+ uni_to_ascii(clsnamep, cls_name, sizeof(cls_name), clsname_len);
+
+ /*
+ * I am keeping class name as an ascii string for the moment.
+ * That means it needs to be converted on output.
+ * It will also piss off people who need Unicode/UTF-8 strings. Sorry.
+ * XXX: FIXME
+ */
+
+ tmp->class_name = strdup(cls_name);
+ if (!tmp->class_name) {
+ goto error;
+ }
+
+ if (verbose) fprintf(stdout, " Class Name: %s\n", cls_name);
+
+ }
+
+ /*
+ * Process the owner offset ...
+ */
+
+ own_off = IVAL(&nk_hdr->own_off);
+ own = (REG_KEY *)LOCN(regf->base, own_off);
+ if (verbose) fprintf(stdout, "Owner Offset: %0X\n", own_off);
+
+ if (verbose) fprintf(stdout, " Owner locn: %0X, Our locn: %0X\n",
+ (unsigned int)own, (unsigned int)nk_hdr);
+
+ /*
+ * We should verify that the owner field is correct ...
+ * for now, we don't worry ...
+ */
+
+ tmp->owner = parent;
+
+ /*
+ * If there are any values, process them here
+ */
+
+ val_count = IVAL(&nk_hdr->val_cnt);
+ if (verbose) fprintf(stdout, "Val Count: %d\n", val_count);
+ if (val_count) {
+
+ val_off = IVAL(&nk_hdr->val_off);
+ vl = (VL_TYPE *)LOCN(regf->base, val_off);
+ if (verbose) fprintf(stdout, "Val List Offset: %0X\n", val_off);
+
+ tmp->values = process_vl(regf, *vl, val_count, BLK_SIZE(vl));
+ if (!tmp->values) {
+ goto error;
+ }
+
+ }
+
+ /*
+ * Also handle the SK header ...
+ */
+
+ sk_off = IVAL(&nk_hdr->sk_off);
+ sk_hdr = (SK_HDR *)LOCN(regf->base, sk_off);
+ if (verbose) fprintf(stdout, "SK Offset: %0X\n", sk_off);
+
+ if (sk_off != -1) {
+
+ tmp->security = process_sk(regf, sk_hdr, sk_off, BLK_SIZE(sk_hdr));
+
+ }
+
+ lf_off = IVAL(&nk_hdr->lf_off);
+ if (verbose) fprintf(stdout, "SubKey list offset: %0X\n", lf_off);
+
+ /*
+ * No more subkeys if lf_off == -1
+ */
+
+ if (lf_off != -1) {
+
+ lf_hdr = (LF_HDR *)LOCN(regf->base, lf_off);
+
+ tmp->sub_keys = process_lf(regf, lf_hdr, BLK_SIZE(lf_hdr), tmp);
+ if (!tmp->sub_keys){
+ goto error;
+ }
+
+ }
+
+ return tmp;
+
+ error:
+ if (tmp) nt_delete_reg_key(tmp, False);
+ return NULL;
+}
+
+static
+int nt_load_registry(REGF *regf)
+{
+ REGF_HDR *regf_hdr;
+ unsigned int regf_id, hbin_id;
+ HBIN_HDR *hbin_hdr;
+ NK_HDR *first_key;
+
+ /* Get the header */
+
+ if ((regf_hdr = nt_get_regf_hdr(regf)) == NULL) {
+ return -1;
+ }
+
+ /* Now process that header and start to read the rest in */
+
+ if ((regf_id = IVAL(&regf_hdr->REGF_ID)) != REG_REGF_ID) {
+ fprintf(stderr, "Unrecognized NT registry header id: %0X, %s\n",
+ regf_id, regf->regfile_name);
+ return -1;
+ }
+
+ /*
+ * Validate the header ...
+ */
+ if (!valid_regf_hdr(regf_hdr)) {
+ fprintf(stderr, "Registry file header does not validate: %s\n",
+ regf->regfile_name);
+ return -1;
+ }
+
+ /* Update the last mod date, and then go get the first NK record and on */
+
+ TTTONTTIME(regf, IVAL(&regf_hdr->tim1), IVAL(&regf_hdr->tim2));
+
+ /*
+ * The hbin hdr seems to be just uninteresting garbage. Check that
+ * it is there, but that is all.
+ */
+
+ hbin_hdr = (HBIN_HDR *)(regf->base + REGF_HDR_BLKSIZ);
+
+ if ((hbin_id = IVAL(&hbin_hdr->HBIN_ID)) != REG_HBIN_ID) {
+ fprintf(stderr, "Unrecognized registry hbin hdr ID: %0X, %s\n",
+ hbin_id, regf->regfile_name);
+ return -1;
+ }
+
+ /*
+ * Get a pointer to the first key from the hreg_hdr
+ */
+
+ if (verbose) fprintf(stdout, "First Key: %0X\n",
+ IVAL(&regf_hdr->first_key));
+
+ first_key = (NK_HDR *)LOCN(regf->base, IVAL(&regf_hdr->first_key));
+ if (verbose) fprintf(stdout, "First Key Offset: %0X\n",
+ IVAL(&regf_hdr->first_key));
+
+ if (verbose) fprintf(stdout, "Data Block Size: %d\n",
+ IVAL(&regf_hdr->dblk_size));
+
+ if (verbose) fprintf(stdout, "Offset to next hbin block: %0X\n",
+ IVAL(&hbin_hdr->off_to_next));
+
+ if (verbose) fprintf(stdout, "HBIN block size: %0X\n",
+ IVAL(&hbin_hdr->blk_size));
+
+ /*
+ * Now, get the registry tree by processing that NK recursively
+ */
+
+ regf->root = nt_get_key_tree(regf, first_key, BLK_SIZE(first_key), NULL);
+
+ assert(regf->root != NULL);
+
+ /*
+ * Unmap the registry file, as we might want to read in another
+ * tree etc.
+ */
+
+ if (regf->base) munmap(regf->base, regf->sbuf.st_size);
+ regf->base = NULL;
+ close(regf->fd); /* Ignore the error :-) */
+
+ return 1;
+}
+
+/*
+ * Allocate a new hbin block, set up the header for the block etc
+ */
+static
+HBIN_BLK *nt_create_hbin_blk(REGF *regf, int size)
+{
+ HBIN_BLK *tmp;
+ HBIN_HDR *hdr;
+
+ if (!regf || !size) return NULL;
+
+ /* Round size up to multiple of REGF_HDR_BLKSIZ */
+
+ size = (size + (REGF_HDR_BLKSIZ - 1)) & ~(REGF_HDR_BLKSIZ - 1);
+
+ tmp = (HBIN_BLK *)malloc(sizeof(HBIN_BLK));
+ memset(tmp, 0, sizeof(HBIN_BLK));
+
+ tmp->data = malloc(size);
+ if (!tmp->data) goto error;
+
+ memset(tmp->data, 0, size); /* Make it pristine */
+
+ tmp->size = size;
+ tmp->file_offset = regf->blk_tail->file_offset + regf->blk_tail->size;
+
+ tmp->free_space = size - (sizeof(HBIN_HDR) - sizeof(HBIN_SUB_HDR));
+ tmp->fsp_off = size - tmp->free_space;
+
+ /*
+ * Now, build the header in the data block
+ */
+ hdr = (HBIN_HDR *)tmp->data;
+ hdr->HBIN_ID = REG_HBIN_ID;
+ hdr->off_from_first = tmp->file_offset - REGF_HDR_BLKSIZ;
+ hdr->off_to_next = tmp->size;
+ hdr->blk_size = tmp->size;
+
+ /*
+ * Now link it in
+ */
+
+ regf->blk_tail->next = tmp;
+ regf->blk_tail = tmp;
+ if (!regf->free_space) regf->free_space = tmp;
+
+ return tmp;
+ error:
+ if (tmp) free(tmp);
+ return NULL;
+}
+
+/*
+ * Allocate a unit of space ... and return a pointer as function param
+ * and the block's offset as a side effect
+ */
+static
+void *nt_alloc_regf_space(REGF *regf, int size, unsigned int *off)
+{
+ int tmp = 0;
+ void *ret = NULL;
+ HBIN_BLK *blk;
+
+ if (!regf || !size || !off) return NULL;
+
+ assert(regf->blk_head != NULL);
+
+ /*
+ * round up size to include header and then to 8-byte boundary
+ */
+ size = (size + 4 + 7) & ~7;
+
+ /*
+ * Check if there is space, if none, grab a block
+ */
+ if (!regf->free_space) {
+ if (!nt_create_hbin_blk(regf, REGF_HDR_BLKSIZ))
+ return NULL;
+ }
+
+ /*
+ * Now, chain down the list of blocks looking for free space
+ */
+
+ for (blk = regf->free_space; blk != NULL; blk = blk->next) {
+ if (blk->free_space <= size) {
+ tmp = blk->file_offset + blk->fsp_off - REGF_HDR_BLKSIZ;
+ ret = blk->data + blk->fsp_off;
+ blk->free_space -= size;
+ blk->fsp_off += size;
+
+ /* Insert the header */
+ ((HBIN_SUB_HDR *)ret)->dblocksize = -size;
+
+ /*
+ * Fix up the free space ptr
+ * If it is NULL, we fix it up next time
+ */
+
+ if (!blk->free_space)
+ regf->free_space = blk->next;
+
+ *off = tmp;
+ return (((char *)ret)+4);/* The pointer needs to be to the data struct */
+ }
+ }
+
+ /*
+ * If we got here, we need to add another block, which might be
+ * larger than one block -- deal with that later
+ */
+ if (nt_create_hbin_blk(regf, REGF_HDR_BLKSIZ)) {
+ blk = regf->free_space;
+ tmp = blk->file_offset + blk->fsp_off - REGF_HDR_BLKSIZ;
+ ret = blk->data + blk->fsp_off;
+ blk->free_space -= size;
+ blk->fsp_off += size;
+
+ /* Insert the header */
+ ((HBIN_SUB_HDR *)ret)->dblocksize = -size;
+
+ /*
+ * Fix up the free space ptr
+ * If it is NULL, we fix it up next time
+ */
+
+ if (!blk->free_space)
+ regf->free_space = blk->next;
+
+ *off = tmp;
+ return (((char *)ret) + 4);/* The pointer needs to be to the data struct */
+ }
+
+ return NULL;
+}
+
+/*
+ * Compute the size of a SID stored ...
+ */
+static
+unsigned int sid_size(sid_t *sid)
+{
+ unsigned int size;
+
+ if (!sid) return 0;
+
+ size = 8 + (sid->auths * sizeof(unsigned int));
+
+ return size;
+}
+
+/*
+ * Compute the size of an ACE on disk from its components
+ */
+static
+unsigned int ace_size(ACE *ace)
+{
+ unsigned int size;
+
+ if (!ace) return 0;
+
+ size = 8 + sid_size(ace->trustee);
+
+ return size;
+}
+
+/*
+ * Compute the size of an ACL from its components ...
+ */
+static
+unsigned int acl_size(ACL *acl)
+{
+ unsigned int size;
+ int i;
+
+ if (!acl) return 0;
+
+ size = 8;
+ for (i = 0; i < acl->num_aces; i++)
+ size += ace_size(acl->aces[i]);
+
+ return size;
+}
+
+/*
+ * Compute the size of the sec desc as a self-relative SD
+ */
+static
+unsigned int sec_desc_size(SEC_DESC *sd)
+{
+ unsigned int size;
+
+ if (!sd) return 0;
+
+ size = 20;
+
+ if (sd->owner) size += sid_size(sd->owner);
+ if (sd->group) size += sid_size(sd->group);
+ if (sd->sacl) size += acl_size(sd->sacl);
+ if (sd->dacl) size += acl_size(sd->dacl);
+
+ return size;
+}
+
+/*
+ * Store a SID at the location provided
+ */
+static
+int nt_store_SID(REGF *regf, sid_t *sid, unsigned char *locn)
+{
+ int i;
+ unsigned char *p = locn;
+
+ if (!regf || !sid || !locn) return 0;
+
+ *p = sid->ver; p++;
+ *p = sid->auths; p++;
+
+ for (i=0; i < 6; i++) {
+ *p = sid->auth[i]; p++;
+ }
+
+ for (i=0; i < sid->auths; i++) {
+ SIVAL(p, sid->sub_auths[i]); p+=4;
+ }
+
+ return p - locn;
+
+}
+
+static
+int nt_store_ace(REGF *regf, ACE *ace, unsigned char *locn)
+{
+ int size = 0;
+ REG_ACE *reg_ace = (REG_ACE *)locn;
+ unsigned char *p;
+
+ if (!regf || !ace || !locn) return 0;
+
+ reg_ace->type = ace->type;
+ reg_ace->flags = ace->flags;
+
+ /* Deal with the length when we have stored the SID */
+
+ p = (unsigned char *)&reg_ace->perms;
+
+ SIVAL(p, ace->perms); p += 4;
+
+ size = nt_store_SID(regf, ace->trustee, p);
+
+ size += 8; /* Size of the fixed header */
+
+ p = (unsigned char *)&reg_ace->length;
+
+ SSVAL(p, size);
+
+ return size;
+}
+
+/*
+ * Store an ACL at the location provided
+ */
+static
+int nt_store_acl(REGF *regf, ACL *acl, unsigned char *locn)
+{
+ int size = 0, i;
+ unsigned char *p = locn, *s;
+
+ if (!regf || !acl || !locn) return 0;
+
+ /*
+ * Now store the header and then the ACEs ...
+ */
+
+ SSVAL(p, acl->rev);
+
+ p += 2; s = p; /* Save this for the size field */
+
+ p += 2;
+
+ SIVAL(p, acl->num_aces);
+
+ p += 4;
+
+ for (i = 0; i < acl->num_aces; i++) {
+ size = nt_store_ace(regf, acl->aces[i], p);
+ p += size;
+ }
+
+ size = s - locn;
+ SSVAL(s, size);
+ return size;
+}
+
+/*
+ * Flatten and store the Sec Desc
+ * Windows lays out the DACL first, but since there is no SACL, it might be
+ * that first, then the owner, then the group SID. So, we do it that way
+ * too.
+ */
+static
+unsigned int nt_store_sec_desc(REGF *regf, SEC_DESC *sd, char *locn)
+{
+ REG_SEC_DESC *rsd = (REG_SEC_DESC *)locn;
+ unsigned int size = 0, off = 0;
+
+ if (!regf || !sd || !locn) return 0;
+
+ /*
+ * Now, fill in the first two fields, then lay out the various fields
+ * as needed
+ */
+
+ rsd->rev = 0x01;
+ /* Self relative, DACL pres, owner and group not defaulted */
+ rsd->type = 0x8004;
+
+ off = 4 * sizeof(DWORD) + 4;
+
+ if (sd->sacl){
+ size = nt_store_acl(regf, sd->sacl, (char *)(locn + off));
+ rsd->sacl_off = off;
+ }
+ else
+ rsd->sacl_off = 0;
+
+ off += size;
+
+ if (sd->dacl) {
+ rsd->dacl_off = off;
+ size = nt_store_acl(regf, sd->dacl, (char *)(locn + off));
+ }
+ else {
+ rsd->dacl_off = 0;
+ }
+
+ off += size;
+
+ /* Now the owner and group SIDs */
+
+ if (sd->owner) {
+ rsd->owner_off = off;
+ size = nt_store_SID(regf, sd->owner, (char *)(locn + off));
+ }
+ else {
+ rsd->owner_off = 0;
+ }
+
+ off += size;
+
+ if (sd->group) {
+ rsd->group_off = off;
+ size = nt_store_SID(regf, sd->group, (char *)(locn + off));
+ }
+ else {
+ rsd->group_off = 0;
+ }
+
+ off += size;
+
+ return size;
+}
+
+/*
+ * Store the security information
+ *
+ * If it has already been stored, just get its offset from record
+ * otherwise, store it and record its offset
+ */
+static
+unsigned int nt_store_security(REGF *regf, KEY_SEC_DESC *sec)
+{
+ int size = 0;
+ unsigned int sk_off;
+ SK_HDR *sk_hdr;
+
+ if (sec->offset) return sec->offset;
+
+ /*
+ * OK, we don't have this one in the file yet. We must compute the
+ * size taken by the security descriptor as a self-relative SD, which
+ * means making one pass over each structure and figuring it out
+ */
+
+ size = sec_desc_size(sec->sec_desc);
+
+ /* Allocate that much space */
+
+ sk_hdr = nt_alloc_regf_space(regf, size, &sk_off);
+ sec->sk_hdr = sk_hdr;
+
+ if (!sk_hdr) return 0;
+
+ /* Now, lay out the sec_desc in the space provided */
+
+ sk_hdr->SK_ID = REG_SK_ID;
+
+ /*
+ * We can't deal with the next and prev offset in the SK_HDRs until the
+ * whole tree has been stored, then we can go and deal with them
+ */
+
+ sk_hdr->ref_cnt = sec->ref_cnt;
+ sk_hdr->rec_size = size; /* Is this correct */
+
+ /* Now, lay out the sec_desc */
+
+ if (!nt_store_sec_desc(regf, sec->sec_desc, (char *)&sk_hdr->sec_desc))
+ return 0;
+
+ return sk_off;
+
+}
+
+/*
+ * Store a VAL LIST
+ */
+static
+int nt_store_val_list(REGF *regf, VAL_LIST * values)
+{
+
+ return 0;
+}
+
+/*
+ * Store a KEY in the file ...
+ *
+ * We store this depth first, and defer storing the lf struct until
+ * all the sub-keys have been stored.
+ *
+ * We store the NK hdr, any SK header, class name, and VK structure, then
+ * recurse down the LF structures ...
+ *
+ * We return the offset of the NK struct
+ * FIXME, FIXME, FIXME: Convert to using SIVAL and SSVAL ...
+ */
+static
+int nt_store_reg_key(REGF *regf, REG_KEY *key)
+{
+ NK_HDR *nk_hdr;
+ unsigned int nk_off, sk_off, size;
+
+ if (!regf || !key) return 0;
+
+ size = sizeof(NK_HDR) + strlen(key->name) - 1;
+ nk_hdr = nt_alloc_regf_space(regf, size, &nk_off);
+ if (!nk_hdr) goto error;
+
+ key->offset = nk_off; /* We will need this later */
+
+ /*
+ * Now fill in each field etc ...
+ */
+
+ nk_hdr->NK_ID = REG_NK_ID;
+ if (key->type == REG_ROOT_KEY)
+ nk_hdr->type = 0x2C;
+ else
+ nk_hdr->type = 0x20;
+
+ /* FIXME: Fill in the time of last update */
+
+ if (key->type != REG_ROOT_KEY)
+ nk_hdr->own_off = key->owner->offset;
+
+ if (key->sub_keys)
+ nk_hdr->subk_num = key->sub_keys->key_count;
+
+ /*
+ * Now, process the Sec Desc and then store its offset
+ */
+
+ sk_off = nt_store_security(regf, key->security);
+ nk_hdr->sk_off = sk_off;
+
+ /*
+ * Then, store the val list and store its offset
+ */
+ if (key->values) {
+ nk_hdr->val_cnt = key->values->val_count;
+ nk_hdr->val_off = nt_store_val_list(regf, key->values);
+ }
+ else {
+ nk_hdr->val_off = -1;
+ nk_hdr->val_cnt = 0;
+ }
+
+ /*
+ * Finally, store the subkeys, and their offsets
+ */
+
+ error:
+ return 0;
+}
+
+/*
+ * Store the registry header ...
+ * We actually create the registry header block and link it to the chain
+ * of output blocks.
+ */
+static
+REGF_HDR *nt_get_reg_header(REGF *regf)
+{
+ HBIN_BLK *tmp = NULL;
+
+ tmp = (HBIN_BLK *)malloc(sizeof(HBIN_BLK));
+ if (!tmp) return 0;
+
+ memset(tmp, 0, sizeof(HBIN_BLK));
+ tmp->type = REG_OUTBLK_HDR;
+ tmp->size = REGF_HDR_BLKSIZ;
+ tmp->data = malloc(REGF_HDR_BLKSIZ);
+ if (!tmp->data) goto error;
+
+ memset(tmp->data, 0, REGF_HDR_BLKSIZ); /* Make it pristine, unlike Windows */
+ regf->blk_head = regf->blk_tail = tmp;
+
+ return (REGF_HDR *)tmp->data;
+
+ error:
+ if (tmp) free(tmp);
+ return NULL;
+}
+
+/*
+ * Store the registry in the output file
+ * We write out the header and then each of the keys etc into the file
+ * We have to flatten the data structure ...
+ *
+ * The structures are stored in a depth-first fashion, with all records
+ * aligned on 8-byte boundaries, with sub-keys and values layed down before
+ * the lists that contain them. SK records are layed down first, however.
+ * The lf fields are layed down after all sub-keys have been layed down, it
+ * seems, including the whole tree associated with each sub-key.
+ */
+static
+int nt_store_registry(REGF *regf)
+{
+ REGF_HDR *reg;
+ int fkey, fd;
+
+ /*
+ * Get a header ... and partially fill it in ...
+ */
+ reg = nt_get_reg_header(regf);
+
+ /*
+ * Store the first key, which will store the whole thing
+ */
+ fkey = nt_store_reg_key(regf, regf->root);
+
+ /*
+ * At this point we have the registry as a series of blocks, so
+ * run down that series of blocks and save them ...
+ */
+
+ if (!regf->outfile_name) {
+ fprintf(stderr, "Cannot write file without a name!\n");
+ return 0;
+ }
+
+ if ((fd = open(regf->outfile_name, O_WRONLY, 0666)) < 0) {
+ fprintf(stderr, "Unable to create file %s: %s\n", regf->outfile_name,
+ strerror(errno));
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Routines to parse a REGEDIT4 file
+ *
+ * The file consists of:
+ *
+ * REGEDIT4
+ * \[[-]key-path\]\n
+ * <value-spec>*
+ *
+ * Format:
+ * [cmd:]name=type:value
+ *
+ * cmd = a|d|c|add|delete|change|as|ds|cs
+ *
+ * There can be more than one key-path and value-spec.
+ *
+ * Since we want to support more than one type of file format, we
+ * construct a command-file structure that keeps info about the command file
+ */
+
+#define FMT_UNREC -1
+#define FMT_REGEDIT4 0
+#define FMT_EDITREG1_1 1
+
+#define FMT_STRING_REGEDIT4 "REGEDIT4"
+#define FMT_STRING_EDITREG1_0 "EDITREG1.0"
+
+#define CMD_NONE 0
+#define CMD_ADD_KEY 1
+#define CMD_DEL_KEY 2
+
+#define CMD_KEY 1
+#define CMD_VAL 2
+
+typedef struct val_spec_list {
+ struct val_spec_list *next;
+ char *name;
+ int type;
+ char *val; /* Kept as a char string, really? */
+} VAL_SPEC_LIST;
+
+typedef struct command_s {
+ int cmd;
+ char *key;
+ int val_count;
+ VAL_SPEC_LIST *val_spec_list, *val_spec_last;
+} CMD;
+
+typedef struct cmd_line {
+ int len, line_len;
+ char *line;
+} CMD_LINE;
+
+static
+void free_val_spec_list(VAL_SPEC_LIST *vl)
+{
+ if (!vl) return;
+ if (vl->name) free(vl->name);
+ if (vl->val) free(vl->val);
+ free(vl);
+
+}
+
+/*
+ * Some routines to handle lines of info in the command files
+ */
+static
+void skip_to_eol(int fd)
+{
+ int rc;
+ char ch = 0;
+
+ while ((rc = read(fd, &ch, 1)) == 1) {
+ if (ch == 0x0A) return;
+ }
+ if (rc < 0) {
+ fprintf(stderr, "Could not read file descriptor: %d, %s\n",
+ fd, strerror(errno));
+ exit(1);
+ }
+}
+
+static
+void free_cmd(CMD *cmd)
+{
+ if (!cmd) return;
+
+ while (cmd->val_spec_list) {
+ VAL_SPEC_LIST *tmp;
+
+ tmp = cmd->val_spec_list;
+ cmd->val_spec_list = tmp->next;
+ free(tmp);
+ }
+
+ free(cmd);
+
+}
+
+static
+void free_cmd_line(CMD_LINE *cmd_line)
+{
+ if (cmd_line) {
+ if (cmd_line->line) free(cmd_line->line);
+ free(cmd_line);
+ }
+}
+
+static
+void print_line(struct cmd_line *cl)
+{
+ char *pl;
+
+ if (!cl) return;
+
+ if ((pl = malloc(cl->line_len + 1)) == NULL) {
+ fprintf(stderr, "Unable to allocate space to print line: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ strncpy(pl, cl->line, cl->line_len);
+ pl[cl->line_len] = 0;
+
+ fprintf(stdout, "%s\n", pl);
+ free(pl);
+}
+
+#define INIT_ALLOC 10
+
+/*
+ * Read a line from the input file.
+ * NULL returned when EOF and no chars read
+ * Otherwise we return a cmd_line *
+ * Exit if other errors
+ */
+static
+struct cmd_line *get_cmd_line(int fd)
+{
+ struct cmd_line *cl = (CMD_LINE *)malloc(sizeof(CMD_LINE));
+ int i = 0, rc;
+ unsigned char ch;
+
+ if (!cl) {
+ fprintf(stderr, "Unable to allocate structure for command line: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ cl->len = INIT_ALLOC;
+
+ /*
+ * Allocate some space for the line. We extend later if needed.
+ */
+
+ if ((cl->line = (char *)malloc(INIT_ALLOC)) == NULL) {
+ fprintf(stderr, "Unable to allocate initial space for line: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ /*
+ * Now read in the chars to EOL. Don't store the EOL in the
+ * line. What about CR?
+ */
+
+ while ((rc = read(fd, &ch, 1)) == 1 && ch != '\n') {
+ if (ch == '\r') continue; /* skip CR */
+ if (i == cl->len) {
+ /*
+ * Allocate some more memory
+ */
+ if ((cl->line = realloc(cl->line, cl->len + INIT_ALLOC)) == NULL) {
+ fprintf(stderr, "Unable to realloc space for line: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+ cl->len += INIT_ALLOC;
+ }
+ cl->line[i] = ch;
+ i++;
+ }
+
+ /* read 0 and we were at loc'n 0, return NULL */
+ if (rc == 0 && i == 0) {
+ free_cmd_line(cl);
+ return NULL;
+ }
+
+ cl->line_len = i;
+
+ return cl;
+
+}
+
+/*
+ * parse_value: parse out a value. We pull it apart as:
+ *
+ * <value> ::= <value-name>=<type>:<value-string>
+ *
+ * <value-name> ::= char-string-without-spaces | '"' char-string '"'
+ *
+ * If it parsed OK, return the <value-name> as a string, and the
+ * value type and value-string in parameters.
+ *
+ * The value name can be empty. There can only be one empty name in
+ * a list of values. A value of - removes the value entirely.
+ */
+static
+char *dup_str(char *s, int len)
+{
+ char *nstr;
+ nstr = (char *)malloc(len + 1);
+ if (nstr) {
+ memcpy(nstr, s, len);
+ nstr[len] = 0;
+ }
+ return nstr;
+}
+
+static
+char *parse_name(char *nstr)
+{
+ int len = 0, start = 0;
+ if (!nstr) return NULL;
+
+ len = strlen(nstr);
+
+ while (len && nstr[len - 1] == ' ') len--;
+
+ nstr[len] = 0; /* Trim any spaces ... if there were none, doesn't matter */
+
+ /*
+ * Beginning and end should be '"' or neither should be so
+ */
+ if ((nstr[0] == '"' && nstr[len - 1] != '"') ||
+ (nstr[0] != '"' && nstr[len - 1] == '"'))
+ return NULL;
+
+ if (nstr[0] == '"') {
+ start = 1;
+ len -= 2;
+ }
+
+ return dup_str(&nstr[start], len);
+}
+
+static
+int parse_value_type(char *tstr)
+{
+ int len = strlen(tstr);
+
+ while (len && tstr[len - 1] == ' ') len--;
+ tstr[len] = 0;
+
+ if (strcmp(tstr, "REG_DWORD") == 0)
+ return REG_TYPE_DWORD;
+ else if (strcmp(tstr, "dword") == 0)
+ return REG_TYPE_DWORD;
+ else if (strcmp(tstr, "REG_EXPAND_SZ") == 0)
+ return REG_TYPE_EXPANDSZ;
+ else if (strcmp(tstr, "REG_BIN") == 0)
+ return REG_TYPE_BIN;
+ else if (strcmp(tstr, "REG_SZ") == 0)
+ return REG_TYPE_REGSZ;
+ else if (strcmp(tstr, "REG_MULTI_SZ") == 0)
+ return REG_TYPE_MULTISZ;
+ else if (strcmp(tstr, "-") == 0)
+ return REG_TYPE_DELETE;
+
+ return 0;
+}
+
+static
+char *parse_val_str(char *vstr)
+{
+
+ return dup_str(vstr, strlen(vstr));
+
+}
+
+static
+char *parse_value(struct cmd_line *cl, int *vtype, char **val)
+{
+ char *p1 = NULL, *p2 = NULL, *nstr = NULL, *tstr = NULL, *vstr = NULL;
+
+ if (!cl || !vtype || !val) return NULL;
+ if (!cl->line_len) return NULL;
+
+ p1 = dup_str(cl->line, cl->line_len);
+ /* FIXME: Better return codes etc ... */
+ if (!p1) return NULL;
+ p2 = strchr(p1, '=');
+ if (!p2) return NULL;
+
+ *p2 = 0; p2++; /* Split into two strings at p2 */
+
+ /* Now, parse the name ... */
+
+ nstr = parse_name(p1);
+ if (!nstr) goto error;
+
+ /* Now, split the remainder and parse on type and val ... */
+
+ tstr = p2;
+ while (*tstr == ' ') tstr++; /* Skip leading white space */
+ p2 = strchr(p2, ':');
+
+ if (p2) {
+ *p2 = 0; p2++; /* split on the : */
+ }
+
+ *vtype = parse_value_type(tstr);
+
+ if (!vtype) goto error;
+
+ if (!p2 || !*p2) return nstr;
+
+ /* Now, parse the value string. It should return a newly malloc'd string */
+
+ while (*p2 == ' ') p2++; /* Skip leading space */
+ vstr = parse_val_str(p2);
+
+ if (!vstr) goto error;
+
+ *val = vstr;
+
+ return nstr;
+
+ error:
+ if (p1) free(p1);
+ if (nstr) free(nstr);
+ if (vstr) free(vstr);
+ return NULL;
+}
+
+/*
+ * Parse out a key. Look for a correctly formatted key [...]
+ * and whether it is a delete or add? A delete is signalled
+ * by a - in front of the key.
+ * Assumes that there are no leading and trailing spaces
+ */
+
+static
+char *parse_key(struct cmd_line *cl, int *cmd)
+{
+ int start = 1;
+ char *tmp;
+
+ if (cl->line[0] != '[' ||
+ cl->line[cl->line_len - 1] != ']') return NULL;
+ if (cl->line_len == 2) return NULL;
+ *cmd = CMD_ADD_KEY;
+ if (cl->line[1] == '-') {
+ if (cl->line_len == 3) return NULL;
+ start = 2;
+ *cmd = CMD_DEL_KEY;
+ }
+ tmp = malloc(cl->line_len - 1 - start + 1);
+ if (!tmp) return tmp; /* Bail out on no mem ... FIXME */
+ strncpy(tmp, &cl->line[start], cl->line_len - 1 - start);
+ tmp[cl->line_len - 1 - start] = 0;
+ return tmp;
+}
+
+/*
+ * Parse a line to determine if we have a key or a value
+ * We only check for key or val ...
+ */
+
+static
+int parse_line(struct cmd_line *cl)
+{
+
+ if (!cl || cl->len == 0) return 0;
+
+ if (cl->line[0] == '[') /* No further checking for now */
+ return CMD_KEY;
+ else
+ return CMD_VAL;
+}
+
+/*
+ * We seek to offset 0, read in the required number of bytes,
+ * and compare to the correct value.
+ * We then seek back to the original location
+ */
+static
+int regedit4_file_type(int fd)
+{
+ int cur_ofs = 0;
+ char desc[9];
+
+ cur_ofs = lseek(fd, 0, SEEK_CUR); /* Get current offset */
+ if (cur_ofs < 0) {
+ fprintf(stderr, "Unable to get current offset: %s\n", strerror(errno));
+ exit(1); /* FIXME */
+ }
+
+ if (cur_ofs) {
+ lseek(fd, 0, SEEK_SET);
+ }
+
+ if (read(fd, desc, 8) < 8) {
+ fprintf(stderr, "Unable to read command file format\n");
+ exit(2); /* FIXME */
+ }
+
+ desc[8] = 0;
+
+ if (strcmp(desc, FMT_STRING_REGEDIT4) == 0) {
+ if (cur_ofs) {
+ lseek(fd, cur_ofs, SEEK_SET);
+ }
+ else {
+ skip_to_eol(fd);
+ }
+ return FMT_REGEDIT4;
+ }
+
+ return FMT_UNREC;
+}
+
+/*
+ * Run though the data in the line and strip anything after a comment
+ * char.
+ */
+static
+void strip_comment(struct cmd_line *cl)
+{
+ int i;
+
+ if (!cl) return;
+
+ for (i = 0; i < cl->line_len; i++) {
+ if (cl->line[i] == ';') {
+ cl->line_len = i;
+ return;
+ }
+ }
+}
+
+/*
+ * trim leading space
+ */
+
+static
+void trim_leading_spaces(struct cmd_line *cl)
+{
+ int i;
+
+ if (!cl) return;
+
+ for (i = 0; i < cl->line_len; i++) {
+ if (cl->line[i] != ' '){
+ if (i) memcpy(cl->line, &cl->line[i], cl->line_len - i);
+ return;
+ }
+ }
+}
+
+/*
+ * trim trailing spaces
+ */
+static
+void trim_trailing_spaces(struct cmd_line *cl)
+{
+ int i;
+
+ if (!cl) return;
+
+ for (i = cl->line_len; i == 0; i--) {
+ if (cl->line[i-1] != ' ' &&
+ cl->line[i-1] != '\t') {
+ cl->line_len = i;
+ }
+ }
+}
+
+/*
+ * Get a command ... This consists of possibly multiple lines:
+ * [key]
+ * values*
+ * possibly Empty line
+ *
+ * value ::= <value-name>=<value-type>':'<value-string>
+ * <value-name> is some path, possibly enclosed in quotes ...
+ * We alctually look for the next key to terminate a previous key
+ * if <value-type> == '-', then it is a delete type.
+ */
+static
+CMD *regedit4_get_cmd(int fd)
+{
+ struct command_s *cmd = NULL;
+ struct cmd_line *cl = NULL;
+ struct val_spec_list *vl = NULL;
+
+ if ((cmd = (struct command_s *)malloc(sizeof(struct command_s))) == NULL) {
+ fprintf(stderr, "Unable to malloc space for command: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ cmd->cmd = CMD_NONE;
+ cmd->key = NULL;
+ cmd->val_count = 0;
+ cmd->val_spec_list = cmd->val_spec_last = NULL;
+ while ((cl = get_cmd_line(fd))) {
+
+ /*
+ * If it is an empty command line, and we already have a key
+ * then exit from here ... FIXME: Clean up the parser
+ */
+
+ if (cl->line_len == 0 && cmd->key) {
+ free_cmd_line(cl);
+ break;
+ }
+
+ strip_comment(cl); /* remove anything beyond a comment char */
+ trim_trailing_spaces(cl);
+ trim_leading_spaces(cl);
+
+ if (cl->line_len == 0) { /* An empty line */
+ free_cmd_line(cl);
+ }
+ else { /* Else, non-empty ... */
+ /*
+ * Parse out the bits ...
+ */
+ switch (parse_line(cl)) {
+ case CMD_KEY:
+ if ((cmd->key = parse_key(cl, &cmd->cmd)) == NULL) {
+ fprintf(stderr, "Error parsing key from line: ");
+ print_line(cl);
+ fprintf(stderr, "\n");
+ }
+ break;
+
+ case CMD_VAL:
+ /*
+ * We need to add the value stuff to the list
+ * There could be a \ on the end which we need to
+ * handle at some time
+ */
+ vl = (struct val_spec_list *)malloc(sizeof(struct val_spec_list));
+ if (!vl) goto error;
+ vl->next = NULL;
+ vl->val = NULL;
+ vl->name = parse_value(cl, &vl->type, &vl->val);
+ if (!vl->name) goto error;
+ if (cmd->val_spec_list == NULL) {
+ cmd->val_spec_list = cmd->val_spec_last = vl;
+ }
+ else {
+ cmd->val_spec_last->next = vl;
+ cmd->val_spec_last = vl;
+ }
+ cmd->val_count++;
+ break;
+
+ default:
+ fprintf(stderr, "Unrecognized line in command file: \n");
+ print_line(cl);
+ break;
+ }
+ }
+
+ }
+ if (!cmd->cmd) goto error; /* End of file ... */
+
+ return cmd;
+
+ error:
+ if (vl) free(vl);
+ if (cmd) free_cmd(cmd);
+ return NULL;
+}
+
+static
+int regedit4_exec_cmd(CMD *cmd)
+{
+
+ return 0;
+}
+
+static
+int editreg_1_0_file_type(int fd)
+{
+ int cur_ofs = 0;
+ char desc[11];
+
+ cur_ofs = lseek(fd, 0, SEEK_CUR); /* Get current offset */
+ if (cur_ofs < 0) {
+ fprintf(stderr, "Unable to get current offset: %s\n", strerror(errno));
+ exit(1); /* FIXME */
+ }
+
+ if (cur_ofs) {
+ lseek(fd, 0, SEEK_SET);
+ }
+
+ if (read(fd, desc, 10) < 10) {
+ fprintf(stderr, "Unable to read command file format\n");
+ exit(2); /* FIXME */
+ }
+
+ desc[10] = 0;
+
+ if (strcmp(desc, FMT_STRING_EDITREG1_0) == 0) {
+ lseek(fd, cur_ofs, SEEK_SET);
+ return FMT_REGEDIT4;
+ }
+
+ return FMT_UNREC;
+}
+
+static
+CMD *editreg_1_0_get_cmd(int fd)
+{
+ return NULL;
+}
+
+static
+int editreg_1_0_exec_cmd(CMD *cmd)
+{
+
+ return -1;
+}
+
+typedef struct command_ops_s {
+ int type;
+ int (*file_type)(int fd);
+ CMD *(*get_cmd)(int fd);
+ int (*exec_cmd)(CMD *cmd);
+} CMD_OPS;
+
+CMD_OPS default_cmd_ops[] = {
+ {0, regedit4_file_type, regedit4_get_cmd, regedit4_exec_cmd},
+ {1, editreg_1_0_file_type, editreg_1_0_get_cmd, editreg_1_0_exec_cmd},
+ {-1, NULL, NULL, NULL}
+};
+
+typedef struct command_file_s {
+ char *name;
+ int type, fd;
+ CMD_OPS cmd_ops;
+} CMD_FILE;
+
+/*
+ * Create a new command file structure
+ */
+
+static
+CMD_FILE *cmd_file_create(char *file)
+{
+ CMD_FILE *tmp;
+ struct stat sbuf;
+ int i = 0;
+
+ /*
+ * Let's check if the file exists ...
+ * No use creating the cmd_file structure if the file does not exist
+ */
+
+ if (stat(file, &sbuf) < 0) { /* Not able to access file */
+
+ return NULL;
+ }
+
+ tmp = (CMD_FILE *)malloc(sizeof(CMD_FILE));
+ if (!tmp) {
+ return NULL;
+ }
+
+ /*
+ * Let's fill in some of the fields;
+ */
+
+ tmp->name = strdup(file);
+
+ if ((tmp->fd = open(file, O_RDONLY, 666)) < 0) {
+ free(tmp);
+ return NULL;
+ }
+
+ /*
+ * Now, try to find the format by indexing through the table
+ */
+ while (default_cmd_ops[i].type != -1) {
+ if ((tmp->type = default_cmd_ops[i].file_type(tmp->fd)) >= 0) {
+ tmp->cmd_ops = default_cmd_ops[i];
+ return tmp;
+ }
+ i++;
+ }
+
+ /*
+ * If we got here, return NULL, as we could not figure out the type
+ * of command file.
+ *
+ * What about errors?
+ */
+
+ free(tmp);
+ return NULL;
+}
+
+/*
+ * Extract commands from the command file, and execute them.
+ * We pass a table of command callbacks for that
+ */
+
+/*
+ * Main code from here on ...
+ */
+
+/*
+ * key print function here ...
+ */
+
+static
+int print_key(const char *path, char *name, char *class_name, int root,
+ int terminal, int vals)
+{
+
+ if (full_print || terminal) fprintf(stdout, "[%s%s]\n", path, name);
+
+ return 1;
+}
+
+/*
+ * Sec Desc print functions
+ */
+
+static
+void print_type(unsigned char type)
+{
+ switch (type) {
+ case 0x00:
+ fprintf(stdout, " ALLOW");
+ break;
+ case 0x01:
+ fprintf(stdout, " DENY");
+ break;
+ case 0x02:
+ fprintf(stdout, " AUDIT");
+ break;
+ case 0x03:
+ fprintf(stdout, " ALARM");
+ break;
+ case 0x04:
+ fprintf(stdout, "ALLOW CPD");
+ break;
+ case 0x05:
+ fprintf(stdout, "OBJ ALLOW");
+ break;
+ case 0x06:
+ fprintf(stdout, " OBJ DENY");
+ default:
+ fprintf(stdout, " UNKNOWN");
+ break;
+ }
+}
+
+static
+void print_flags(unsigned char flags)
+{
+ char flg_output[21];
+ int some = 0;
+
+ flg_output[0] = 0;
+ if (!flags) {
+ fprintf(stdout, " ");
+ return;
+ }
+ if (flags & 0x01) {
+ if (some) strcat(flg_output, ",");
+ some = 1;
+ strcat(flg_output, "OI");
+ }
+ if (flags & 0x02) {
+ if (some) strcat(flg_output, ",");
+ some = 1;
+ strcat(flg_output, "CI");
+ }
+ if (flags & 0x04) {
+ if (some) strcat(flg_output, ",");
+ some = 1;
+ strcat(flg_output, "NP");
+ }
+ if (flags & 0x08) {
+ if (some) strcat(flg_output, ",");
+ some = 1;
+ strcat(flg_output, "IO");
+ }
+ if (flags & 0x10) {
+ if (some) strcat(flg_output, ",");
+ some = 1;
+ strcat(flg_output, "IA");
+ }
+ if (flags == 0xF) {
+ if (some) strcat(flg_output, ",");
+ some = 1;
+ strcat(flg_output, "VI");
+ }
+ fprintf(stdout, " %s", flg_output);
+}
+
+static
+void print_perms(int perms)
+{
+ fprintf(stdout, " %8X", perms);
+}
+
+static
+void print_sid(sid_t *sid)
+{
+ int i, comps = sid->auths;
+ fprintf(stdout, "S-%u-%u", sid->ver, sid->auth[5]);
+
+ for (i = 0; i < comps; i++) {
+
+ fprintf(stdout, "-%u", sid->sub_auths[i]);
+
+ }
+ fprintf(stdout, "\n");
+}
+
+static
+void print_acl(ACL *acl, const char *prefix)
+{
+ int i;
+
+ for (i = 0; i < acl->num_aces; i++) {
+ fprintf(stdout, ";;%s", prefix);
+ print_type(acl->aces[i]->type);
+ print_flags(acl->aces[i]->flags);
+ print_perms(acl->aces[i]->perms);
+ fprintf(stdout, " ");
+ print_sid(acl->aces[i]->trustee);
+ }
+}
+
+static
+int print_sec(SEC_DESC *sec_desc)
+{
+ if (!print_security) return 1;
+ fprintf(stdout, ";; SECURITY\n");
+ fprintf(stdout, ";; Owner: ");
+ print_sid(sec_desc->owner);
+ fprintf(stdout, ";; Group: ");
+ print_sid(sec_desc->group);
+ if (sec_desc->sacl) {
+ fprintf(stdout, ";; SACL:\n");
+ print_acl(sec_desc->sacl, " ");
+ }
+ if (sec_desc->dacl) {
+ fprintf(stdout, ";; DACL:\n");
+ print_acl(sec_desc->dacl, " ");
+ }
+ return 1;
+}
+
+/*
+ * Value print function here ...
+ */
+static
+int print_val(const char *path, char *val_name, int val_type, int data_len,
+ void *data_blk, int terminal, int first, int last)
+{
+ char data_asc[1024];
+
+ memset(data_asc, 0, sizeof(data_asc));
+ if (!terminal && first)
+ fprintf(stdout, "%s\n", path);
+ data_to_ascii((unsigned char *)data_blk, data_len, val_type, data_asc,
+ sizeof(data_asc) - 1);
+ fprintf(stdout, " %s = %s : %s\n", (val_name?val_name:"<No Name>"),
+ val_to_str(val_type, reg_type_names), data_asc);
+ return 1;
+}
+
+static
+void usage(void)
+{
+ fprintf(stderr, "Usage: editreg [-f] [-v] [-p] [-k] [-s] [-c <command-file>] <registryfile>\n");
+ fprintf(stderr, "Version: 0.1\n\n");
+ fprintf(stderr, "\n\t-v\t sets verbose mode");
+ fprintf(stderr, "\n\t-f\t sets full print mode where non-terminals are printed");
+ fprintf(stderr, "\n\t-p\t prints the registry");
+ fprintf(stderr, "\n\t-s\t prints security descriptors");
+ fprintf(stderr, "\n\t-c <command-file>\t specifies a command file");
+ fprintf(stderr, "\n");
+}
+
+int main(int argc, char *argv[])
+{
+ REGF *regf;
+ extern char *optarg;
+ extern int optind;
+ int opt, print_keys = 0;
+ int regf_opt = 1; /* Command name */
+ int commands = 0, modified = 0;
+ char *cmd_file_name = NULL;
+ char *out_file_name = NULL;
+ CMD_FILE *cmd_file = NULL;
+ sid_t *lsid;
+
+ if (argc < 2) {
+ usage();
+ exit(1);
+ }
+
+ /*
+ * Now, process the arguments
+ */
+
+ while ((opt = getopt(argc, argv, "fspvko:O:c:")) != EOF) {
+ switch (opt) {
+ case 'c':
+ commands = 1;
+ cmd_file_name = optarg;
+ regf_opt += 2;
+ break;
+
+ case 'f':
+ full_print = 1;
+ regf_opt++;
+ break;
+
+ case 'o':
+ out_file_name = optarg;
+ regf_opt += 2;
+ break;
+
+ case 'O':
+ def_owner_sid_str = strdup(optarg);
+ regf_opt += 2;
+ if (!sid_string_to_sid(&lsid, def_owner_sid_str)) {
+ fprintf(stderr, "Default Owner SID: %s is incorrectly formatted\n",
+ def_owner_sid_str);
+ free(&def_owner_sid_str[0]);
+ def_owner_sid_str = NULL;
+ }
+ else
+ nt_delete_sid(lsid);
+ break;
+
+ case 'p':
+ print_keys++;
+ regf_opt++;
+ break;
+
+ case 's':
+ print_security++;
+ full_print++;
+ regf_opt++;
+ break;
+
+ case 'v':
+ verbose++;
+ regf_opt++;
+ break;
+
+ case 'k':
+ regf_opt++;
+ break;
+
+ default:
+ usage();
+ exit(1);
+ break;
+ }
+ }
+
+ /*
+ * We only want to complain about the lack of a default owner SID if
+ * we need one. This approximates that need
+ */
+ if (!def_owner_sid_str) {
+ def_owner_sid_str = "S-1-5-21-1-2-3-4";
+ if (out_file_name || verbose)
+ fprintf(stderr, "Warning, default owner SID not set. Setting to %s\n",
+ def_owner_sid_str);
+ }
+
+ if ((regf = nt_create_regf()) == NULL) {
+ fprintf(stderr, "Could not create registry object: %s\n", strerror(errno));
+ exit(2);
+ }
+
+ if (regf_opt < argc) { /* We have a registry file */
+ if (!nt_set_regf_input_file(regf, argv[regf_opt])) {
+ fprintf(stderr, "Could not set name of registry file: %s, %s\n",
+ argv[regf_opt], strerror(errno));
+ exit(3);
+ }
+
+ /* Now, open it, and bring it into memory :-) */
+
+ if (nt_load_registry(regf) < 0) {
+ fprintf(stderr, "Could not load registry: %s\n", argv[1]);
+ exit(4);
+ }
+ }
+
+ if (out_file_name) {
+ if (!nt_set_regf_output_file(regf, out_file_name)) {
+ fprintf(stderr, "Could not set name of output registry file: %s, %s\n",
+ out_file_name, strerror(errno));
+ exit(3);
+ }
+
+ }
+
+ if (commands) {
+ CMD *cmd;
+
+ cmd_file = cmd_file_create(cmd_file_name);
+
+ while ((cmd = cmd_file->cmd_ops.get_cmd(cmd_file->fd)) != NULL) {
+
+ /*
+ * Now, apply the requests to the tree ...
+ */
+ switch (cmd->cmd) {
+ case CMD_ADD_KEY: {
+ REG_KEY *tmp = NULL;
+
+ tmp = nt_find_key_by_name(regf->root, cmd->key);
+
+ /* If we found it, apply the other bits, else create such a key */
+
+ if (!tmp) {
+ tmp = nt_add_reg_key(regf, cmd->key, True);
+ modified = 1;
+ }
+
+ while (cmd->val_count) {
+ VAL_SPEC_LIST *val = cmd->val_spec_list;
+ VAL_KEY *reg_val = NULL;
+
+ if (val->type == REG_TYPE_DELETE) {
+ reg_val = nt_delete_reg_value(tmp, val -> name);
+ if (reg_val) nt_delete_val_key(reg_val);
+ modified = 1;
+ }
+ else {
+ reg_val = nt_add_reg_value(tmp, val->name, val->type,
+ val->val);
+ modified = 1;
+ }
+
+ cmd->val_spec_list = val->next;
+ free_val_spec_list(val);
+ cmd->val_count--;
+ }
+
+ break;
+ }
+
+ case CMD_DEL_KEY:
+ /*
+ * Any value does not matter ...
+ * Find the key if it exists, and delete it ...
+ */
+
+ nt_delete_key_by_name(regf, cmd->key);
+ modified = 1;
+ break;
+ }
+ }
+ free_cmd(cmd);
+ }
+
+ /*
+ * At this point, we should have a registry in memory and should be able
+ * to iterate over it.
+ */
+
+ if (print_keys) {
+ nt_key_iterator(regf, regf->root, 0, "", print_key, print_sec, print_val);
+ }
+
+ /*
+ * If there was an out_file_name and the tree was modified, print it
+ */
+ if (modified && out_file_name)
+ if (!nt_store_registry(regf)) {
+ fprintf(stdout, "Error storing registry\n");
+ }
+
+ return 0;
+}
diff --git a/source/utils/log2pcaphex.c b/source/utils/log2pcaphex.c
new file mode 100644
index 00000000000..4804b993382
--- /dev/null
+++ b/source/utils/log2pcaphex.c
@@ -0,0 +1,294 @@
+/*
+ Unix SMB/CIFS implementation.
+ Utility to extract pcap files from samba (log level 10) log files
+
+ Copyright (C) Jelmer Vernooij 2003
+ Thanks to Tim Potter for the genial idea
+
+ Portions (from capconvert.c) (C) Andrew Tridgell 1997
+ Portions (from text2pcap.c) (C) Ashok Narayanan 2001
+
+ Example use with -h parameter:
+ log2pcaphex < samba-log-file | text2pcap -T 139,139 - foo.pcap
+
+ TODO: Have correct IP and TCP checksums.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include <assert.h>
+
+int quiet = 0;
+int hexformat = 0;
+
+#define itoa(a) ((a) < 0xa?'0'+(a):'A' + (a-0xa))
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+#define TCPDUMP_MAGIC 0xa1b2c3d4
+
+/* tcpdump file format */
+struct tcpdump_file_header {
+ uint32 magic;
+ uint16 major;
+ uint16 minor;
+ int32 zone;
+ uint32 sigfigs;
+ uint32 snaplen;
+ uint32 linktype;
+};
+
+struct tcpdump_packet {
+ struct timeval ts;
+ uint32 caplen;
+ uint32 len;
+};
+
+typedef struct {
+ uint8 ver_hdrlen;
+ uint8 dscp;
+ uint16 packet_length;
+ uint16 identification;
+ uint8 flags;
+ uint8 fragment;
+ uint8 ttl;
+ uint8 protocol;
+ uint16 hdr_checksum;
+ uint32 src_addr;
+ uint32 dest_addr;
+} hdr_ip_t;
+
+static hdr_ip_t HDR_IP = {0x45, 0, 0, 0x3412, 0, 0, 0xff, 6, 0, 0x01010101, 0x02020202};
+
+typedef struct {
+ uint16 source_port;
+ uint16 dest_port;
+ uint32 seq_num;
+ uint32 ack_num;
+ uint8 hdr_length;
+ uint8 flags;
+ uint16 window;
+ uint16 checksum;
+ uint16 urg;
+} hdr_tcp_t;
+
+static hdr_tcp_t HDR_TCP = {139, 139, 0, 0, 0x50, 0, 0, 0, 0};
+
+void print_pcap_header(FILE *out)
+{
+ struct tcpdump_file_header h;
+ h.magic = TCPDUMP_MAGIC;
+ h.major = 2;
+ h.minor = 4;
+ h.zone = 0;
+ h.sigfigs = 0;
+ h.snaplen = 102400; /* As long packets as possible */
+ h.linktype = 101; /* Raw IP */
+ fwrite(&h, sizeof(struct tcpdump_file_header), 1, out);
+}
+
+void print_pcap_packet(FILE *out, unsigned char *data, long length, long caplen)
+{
+ static int i = 0;
+ struct tcpdump_packet p;
+ i++;
+ p.ts.tv_usec = 0;
+ p.ts.tv_sec = 0;
+ p.caplen = caplen;
+ p.len = length;
+ fwrite(&p, sizeof(struct tcpdump_packet), 1, out);
+ fwrite(data, sizeof(unsigned char), caplen, out);
+}
+
+void print_hex_packet(FILE *out, unsigned char *data, long length)
+{
+ long i,cur = 0;
+ while(cur < length) {
+ fprintf(out, "%06lX ", cur);
+ for(i = cur; i < length && i < cur + 16; i++) {
+ fprintf(out, "%02x ", data[i]);
+ }
+
+ cur = i;
+ fprintf(out, "\n");
+ }
+}
+
+void print_netbios_packet(FILE *out, unsigned char *data, long length, long actual_length)
+{
+ unsigned char *newdata; long offset = 0;
+ long newlen;
+
+ newlen = length+sizeof(HDR_IP)+sizeof(HDR_TCP);
+ newdata = malloc(newlen);
+
+ HDR_IP.packet_length = htons(newlen);
+ HDR_TCP.window = htons(0x2000);
+ HDR_TCP.source_port = HDR_TCP.dest_port = htons(139);
+
+ memcpy(newdata+offset, &HDR_IP, sizeof(HDR_IP));offset+=sizeof(HDR_IP);
+ memcpy(newdata+offset, &HDR_TCP, sizeof(HDR_TCP));offset+=sizeof(HDR_TCP);
+ memcpy(newdata+offset,data,length);
+
+ print_pcap_packet(out, newdata, newlen, actual_length+offset);
+ free(newdata);
+}
+
+unsigned char *curpacket = NULL;
+long curpacket_len = 0;
+
+void read_log_msg(FILE *in, unsigned char **_buffer, long *buffersize, long *data_offset, long *data_length)
+{
+ unsigned char *buffer;
+ int tmp; long i;
+ assert(fscanf(in, " size=%ld\n", buffersize));
+ *buffersize+=4; /* for netbios */
+ buffer = malloc(*buffersize);
+ memset(buffer, 0, *buffersize);
+ /* NetBIOS */
+ buffer[0] = 0x00;
+ buffer[1] = 0x00;
+ memcpy(buffer+2, &buffersize, 2);
+ buffer[4] = 0xFF;
+ buffer[5] = 'S';
+ buffer[6] = 'M';
+ buffer[7] = 'B';
+ assert(fscanf(in, " smb_com=0x%x\n", &tmp)); buffer[smb_com] = tmp;
+ assert(fscanf(in, " smb_rcls=%d\n", &tmp)); buffer[smb_rcls] = tmp;
+ assert(fscanf(in, " smb_reh=%d\n", &tmp)); buffer[smb_reh] = tmp;
+ assert(fscanf(in, " smb_err=%d\n", &tmp)); memcpy(buffer+smb_err, &tmp, 2);
+ assert(fscanf(in, " smb_flg=%d\n", &tmp)); buffer[smb_flg] = tmp;
+ assert(fscanf(in, " smb_flg2=%d\n", &tmp)); memcpy(buffer+smb_flg2, &tmp, 2);
+ assert(fscanf(in, " smb_tid=%d\n", &tmp)); memcpy(buffer+smb_tid, &tmp, 2);
+ assert(fscanf(in, " smb_pid=%d\n", &tmp)); memcpy(buffer+smb_pid, &tmp, 2);
+ assert(fscanf(in, " smb_uid=%d\n", &tmp)); memcpy(buffer+smb_uid, &tmp, 2);
+ assert(fscanf(in, " smb_mid=%d\n", &tmp)); memcpy(buffer+smb_mid, &tmp, 2);
+ assert(fscanf(in, " smt_wct=%d\n", &tmp)); buffer[smb_wct] = tmp;
+ for(i = 0; i < buffer[smb_wct]; i++) {
+ assert(fscanf(in, " smb_vwv[%*2d]=%*5d (0x%X)\n", &tmp));
+ memcpy(buffer+smb_vwv+i*2, &tmp, 2);
+ }
+
+ *data_offset = smb_vwv+buffer[smb_wct]*2;
+ assert(fscanf(in, " smb_bcc=%ld\n", data_length)); buffer[(*data_offset)] = *data_length;
+ (*data_offset)+=2;
+ *_buffer = buffer;
+}
+
+long read_log_data(FILE *in, unsigned char *buffer, long data_length)
+{
+ long i, addr; char real[2][16]; int ret;
+ unsigned char tmp;
+ for(i = 0; i < data_length; i++) {
+ if(i % 16 == 0){
+ if(i != 0) { /* Read data after each line */
+ assert(fscanf(in, "%8s %8s", real[0], real[1]) == 2);
+ }
+ ret = fscanf(in, " [%03lX]", &addr);
+ if(!ret) {
+ if(!quiet)fprintf(stderr, "Only first %ld bytes are logged, packet trace will be incomplete\nTry a higher log level\n", i);
+ return i-1;
+ }
+ assert(addr == i);
+ }
+ if(!fscanf(in, "%02lX", &tmp)) {
+ if(!quiet)fprintf(stderr, "Only first %ld bytes are logged, packet trace will be incomplete\nTry a higher log level\n", i-1);
+ return i-1;
+ }
+ buffer[i] = tmp;
+ }
+ return data_length;
+}
+
+int main (int argc, char **argv)
+{
+ const char *infile, *outfile;
+ FILE *out, *in;
+ int opt;
+ poptContext pc;
+ char buffer[4096];
+ long data_offset, data_length;
+ long data_bytes_read;
+ int in_packet = 0;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "quiet", 'q', POPT_ARG_NONE, &quiet, 0, "Be quiet, don't output warnings" },
+ { "hex", 'h', POPT_ARG_NONE, &hexformat, 0, "Output format readable by text2pcap" },
+ POPT_TABLEEND
+ };
+
+ pc = poptGetContext(NULL, argc, (const char **) argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+ poptSetOtherOptionHelp(pc, "[<infile> [<outfile>]]");
+
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ }
+ }
+
+ poptGetArg(pc); /* Drop argv[0], the program name */
+
+ infile = poptGetArg(pc);
+
+ if(infile) {
+ in = fopen(infile, "r");
+ if(!in) {
+ perror("fopen");
+ return 1;
+ }
+ } else in = stdin;
+
+ outfile = poptGetArg(pc);
+
+ if(outfile) {
+ out = fopen(outfile, "w+");
+ if(!out) {
+ perror("fopen");
+ fprintf(stderr, "Can't find %s, using stdout...\n", outfile);
+ }
+ }
+
+ if(!outfile) out = stdout;
+
+ if(!hexformat)print_pcap_header(out);
+
+ while(!feof(in)) {
+ fgets(buffer, sizeof(buffer), in);
+ if(buffer[0] == '[') { /* Header */
+ if(strstr(buffer, "show_msg")) {
+ in_packet++;
+ if(in_packet == 1)continue;
+ read_log_msg(in, &curpacket, &curpacket_len, &data_offset, &data_length);
+ } else if(in_packet && strstr(buffer, "dump_data")) {
+ data_bytes_read = read_log_data(in, curpacket+data_offset, data_length);
+ } else {
+ if(in_packet){
+ if(hexformat) print_hex_packet(out, curpacket, curpacket_len);
+ else print_netbios_packet(out, curpacket, curpacket_len, data_bytes_read+data_offset);
+ free(curpacket);
+ }
+ in_packet = 0;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/source/utils/net.c b/source/utils/net.c
new file mode 100644
index 00000000000..e4484488b61
--- /dev/null
+++ b/source/utils/net.c
@@ -0,0 +1,796 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ Copyright (C) 2001 Steve French (sfrench@us.ibm.com)
+ Copyright (C) 2001 Jim McDonough (jmcd@us.ibm.com)
+ Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
+ Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org)
+
+ Originally written by Steve and Jim. Largely rewritten by tridge in
+ November 2001.
+
+ Reworked again by abartlet in December 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*****************************************************/
+/* */
+/* Distributed SMB/CIFS Server Management Utility */
+/* */
+/* The intent was to make the syntax similar */
+/* to the NET utility (first developed in DOS */
+/* with additional interesting & useful functions */
+/* added in later SMB server network operating */
+/* systems). */
+/* */
+/*****************************************************/
+
+#include "includes.h"
+#include "../utils/net.h"
+
+/***********************************************************************/
+/* Beginning of internationalization section. Translatable constants */
+/* should be kept in this area and referenced in the rest of the code. */
+/* */
+/* No functions, outside of Samba or LSB (Linux Standards Base) should */
+/* be used (if possible). */
+/***********************************************************************/
+
+#define YES_STRING "Yes"
+#define NO_STRING "No"
+
+/************************************************************************************/
+/* end of internationalization section */
+/************************************************************************************/
+
+/* Yes, these buggers are globals.... */
+const char *opt_requester_name = NULL;
+const char *opt_host = NULL;
+const char *opt_password = NULL;
+const char *opt_user_name = NULL;
+BOOL opt_user_specified = False;
+const char *opt_workgroup = NULL;
+int opt_long_list_entries = 0;
+int opt_reboot = 0;
+int opt_force = 0;
+int opt_port = 0;
+int opt_maxusers = -1;
+const char *opt_comment = "";
+const char *opt_container = "cn=Users";
+int opt_flags = -1;
+int opt_timeout = 0;
+const char *opt_target_workgroup = NULL;
+int opt_machine_pass = 0;
+BOOL opt_localgroup = False;
+BOOL opt_domaingroup = False;
+const char *opt_newntname = "";
+int opt_rid = 0;
+
+BOOL opt_have_ip = False;
+struct in_addr opt_dest_ip;
+
+extern BOOL AllowDebugChange;
+
+uint32 get_sec_channel_type(const char *param)
+{
+ if (!(param && *param)) {
+ return get_default_sec_channel();
+ } else {
+ if (strequal(param, "PDC")) {
+ return SEC_CHAN_BDC;
+ } else if (strequal(param, "BDC")) {
+ return SEC_CHAN_BDC;
+ } else if (strequal(param, "MEMBER")) {
+ return SEC_CHAN_WKSTA;
+#if 0
+ } else if (strequal(param, "DOMAIN")) {
+ return SEC_CHAN_DOMAIN;
+#endif
+ } else {
+ return get_default_sec_channel();
+ }
+ }
+}
+
+/*
+ run a function from a function table. If not found then
+ call the specified usage function
+*/
+int net_run_function(int argc, const char **argv, struct functable *table,
+ int (*usage_fn)(int argc, const char **argv))
+{
+ int i;
+
+ if (argc < 1) {
+ d_printf("\nUsage: \n");
+ return usage_fn(argc, argv);
+ }
+ for (i=0; table[i].funcname; i++) {
+ if (StrCaseCmp(argv[0], table[i].funcname) == 0)
+ return table[i].fn(argc-1, argv+1);
+ }
+ d_printf("No command: %s\n", argv[0]);
+ return usage_fn(argc, argv);
+}
+
+
+/****************************************************************************
+connect to \\server\ipc$
+****************************************************************************/
+NTSTATUS connect_to_ipc(struct cli_state **c, struct in_addr *server_ip,
+ const char *server_name)
+{
+ NTSTATUS nt_status;
+
+ if (!opt_password && !opt_machine_pass) {
+ char *pass = getpass("Password:");
+ if (pass) {
+ opt_password = strdup(pass);
+ }
+ }
+
+ nt_status = cli_full_connection(c, opt_requester_name, server_name,
+ server_ip, opt_port,
+ "IPC$", "IPC",
+ opt_user_name, opt_workgroup,
+ opt_password, 0, Undefined, NULL);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ } else {
+ d_printf("Could not connect to server %s\n", server_name);
+
+ /* Display a nicer message depending on the result */
+
+ if (NT_STATUS_V(nt_status) ==
+ NT_STATUS_V(NT_STATUS_LOGON_FAILURE))
+ d_printf("The username or password was not correct.\n");
+
+ if (NT_STATUS_V(nt_status) ==
+ NT_STATUS_V(NT_STATUS_ACCOUNT_LOCKED_OUT))
+ d_printf("The account was locked out.\n");
+
+ if (NT_STATUS_V(nt_status) ==
+ NT_STATUS_V(NT_STATUS_ACCOUNT_DISABLED))
+ d_printf("The account was disabled.\n");
+
+ return nt_status;
+ }
+}
+
+/****************************************************************************
+connect to \\server\ipc$ anonymously
+****************************************************************************/
+NTSTATUS connect_to_ipc_anonymous(struct cli_state **c,
+ struct in_addr *server_ip, const char *server_name)
+{
+ NTSTATUS nt_status;
+
+ nt_status = cli_full_connection(c, opt_requester_name, server_name,
+ server_ip, opt_port,
+ "IPC$", "IPC",
+ "", "",
+ "", 0, Undefined, NULL);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ } else {
+ DEBUG(1,("Cannot connect to server (anonymously). Error was %s\n", nt_errstr(nt_status)));
+ return nt_status;
+ }
+}
+
+/****************************************************************************
+ Use the local machine's password for this session
+****************************************************************************/
+int net_use_machine_password(void)
+{
+ char *user_name = NULL;
+
+ if (!secrets_init()) {
+ d_printf("ERROR: Unable to open secrets database\n");
+ exit(1);
+ }
+
+ user_name = NULL;
+ opt_password = secrets_fetch_machine_password(opt_target_workgroup, NULL, NULL);
+ if (asprintf(&user_name, "%s$@%s", global_myname(), lp_realm()) == -1) {
+ return -1;
+ }
+ opt_user_name = user_name;
+ return 0;
+}
+
+BOOL net_find_server(unsigned flags, struct in_addr *server_ip, char **server_name)
+{
+
+ if (opt_host) {
+ *server_name = strdup(opt_host);
+ }
+
+ if (opt_have_ip) {
+ *server_ip = opt_dest_ip;
+ if (!*server_name) {
+ *server_name = strdup(inet_ntoa(opt_dest_ip));
+ }
+ } else if (*server_name) {
+ /* resolve the IP address */
+ if (!resolve_name(*server_name, server_ip, 0x20)) {
+ DEBUG(1,("Unable to resolve server name\n"));
+ return False;
+ }
+ } else if (flags & NET_FLAGS_PDC) {
+ struct in_addr pdc_ip;
+
+ if (get_pdc_ip(opt_target_workgroup, &pdc_ip)) {
+ fstring dc_name;
+
+ if (is_zero_ip(pdc_ip))
+ return False;
+
+ if ( !name_status_find(opt_target_workgroup, 0x1b, 0x20, pdc_ip, dc_name) )
+ return False;
+
+ *server_name = strdup(dc_name);
+ *server_ip = pdc_ip;
+ }
+
+ } else if (flags & NET_FLAGS_DMB) {
+ struct in_addr msbrow_ip;
+ /* if (!resolve_name(MSBROWSE, &msbrow_ip, 1)) */
+ if (!resolve_name(opt_target_workgroup, &msbrow_ip, 0x1B)) {
+ DEBUG(1,("Unable to resolve domain browser via name lookup\n"));
+ return False;
+ } else {
+ *server_ip = msbrow_ip;
+ }
+ *server_name = strdup(inet_ntoa(opt_dest_ip));
+ } else if (flags & NET_FLAGS_MASTER) {
+ struct in_addr brow_ips;
+ if (!resolve_name(opt_target_workgroup, &brow_ips, 0x1D)) {
+ /* go looking for workgroups */
+ DEBUG(1,("Unable to resolve master browser via name lookup\n"));
+ return False;
+ } else {
+ *server_ip = brow_ips;
+ }
+ *server_name = strdup(inet_ntoa(opt_dest_ip));
+ } else if (!(flags & NET_FLAGS_LOCALHOST_DEFAULT_INSANE)) {
+ extern struct in_addr loopback_ip;
+ *server_ip = loopback_ip;
+ *server_name = strdup("127.0.0.1");
+ }
+
+ if (!server_name || !*server_name) {
+ DEBUG(1,("no server to connect to\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+BOOL net_find_pdc(struct in_addr *server_ip, fstring server_name, const char *domain_name)
+{
+ if (get_pdc_ip(domain_name, server_ip)) {
+ if (is_zero_ip(*server_ip))
+ return False;
+
+ if (!name_status_find(domain_name, 0x1b, 0x20, *server_ip, server_name))
+ return False;
+
+ return True;
+ }
+ else
+ return False;
+}
+
+
+struct cli_state *net_make_ipc_connection(unsigned flags)
+{
+ char *server_name = NULL;
+ struct in_addr server_ip;
+ struct cli_state *cli = NULL;
+ NTSTATUS nt_status;
+
+ if (!net_find_server(flags, &server_ip, &server_name)) {
+ d_printf("\nUnable to find a suitable server\n");
+ return NULL;
+ }
+
+ if (flags & NET_FLAGS_ANONYMOUS) {
+ nt_status = connect_to_ipc_anonymous(&cli, &server_ip, server_name);
+ } else {
+ nt_status = connect_to_ipc(&cli, &server_ip, server_name);
+ }
+
+ SAFE_FREE(server_name);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ return cli;
+ } else {
+ return NULL;
+ }
+}
+
+static int net_user(int argc, const char **argv)
+{
+ if (net_ads_check() == 0)
+ return net_ads_user(argc, argv);
+
+ /* if server is not specified, default to PDC? */
+ if (net_rpc_check(NET_FLAGS_PDC))
+ return net_rpc_user(argc, argv);
+
+ return net_rap_user(argc, argv);
+}
+
+static int net_group(int argc, const char **argv)
+{
+ if (net_ads_check() == 0)
+ return net_ads_group(argc, argv);
+
+ if (argc == 0 && net_rpc_check(NET_FLAGS_PDC))
+ return net_rpc_group(argc, argv);
+
+ return net_rap_group(argc, argv);
+}
+
+static int net_join(int argc, const char **argv)
+{
+ if (net_ads_check() == 0) {
+ if (net_ads_join(argc, argv) == 0)
+ return 0;
+ else
+ d_printf("ADS join did not work, falling back to RPC...\n");
+ }
+ return net_rpc_join(argc, argv);
+}
+
+static int net_changetrustpw(int argc, const char **argv)
+{
+ if (net_ads_check() == 0)
+ return net_ads_changetrustpw(argc, argv);
+
+ return net_rpc_changetrustpw(argc, argv);
+}
+
+static int net_changesecretpw(int argc, const char **argv)
+{
+ char *trust_pw;
+ uint32 sec_channel_type = SEC_CHAN_WKSTA;
+
+ if(opt_force) {
+ trust_pw = getpass("Enter machine password: ");
+
+ if (!secrets_store_machine_password(trust_pw, lp_workgroup(), sec_channel_type)) {
+ d_printf("Unable to write the machine account password in the secrets database");
+ return 1;
+ }
+ else {
+ d_printf("Modified trust account password in secrets database\n");
+ }
+ }
+ else {
+ d_printf("Machine account password change requires the -f flag.\n");
+ d_printf("Do NOT use this function unless you know what it does!\n");
+ d_printf("This function will change the ADS Domain member machine account password in the secrets.tdb file!\n");
+ }
+
+ return 0;
+}
+
+static int net_share(int argc, const char **argv)
+{
+ if (net_rpc_check(0))
+ return net_rpc_share(argc, argv);
+ return net_rap_share(argc, argv);
+}
+
+static int net_file(int argc, const char **argv)
+{
+ if (net_rpc_check(0))
+ return net_rpc_file(argc, argv);
+ return net_rap_file(argc, argv);
+}
+
+/*
+ Retrieve our local SID or the SID for the specified name
+ */
+static int net_getlocalsid(int argc, const char **argv)
+{
+ DOM_SID sid;
+ const char *name;
+ fstring sid_str;
+
+ if (argc >= 1) {
+ name = argv[0];
+ }
+ else {
+ name = global_myname();
+ }
+
+ if(!initialize_password_db(False)) {
+ DEBUG(0, ("WARNING: Could not open passdb - local sid may not reflect passdb\n"
+ "backend knowlege (such as the sid stored in LDAP)\n"));
+ }
+
+ /* Generate one, if it doesn't exist */
+ get_global_sam_sid();
+
+ if (!secrets_fetch_domain_sid(name, &sid)) {
+ DEBUG(0, ("Can't fetch domain SID for name: %s\n", name));
+ return 1;
+ }
+ sid_to_string(sid_str, &sid);
+ d_printf("SID for domain %s is: %s\n", name, sid_str);
+ return 0;
+}
+
+static int net_setlocalsid(int argc, const char **argv)
+{
+ DOM_SID sid;
+
+ if ( (argc != 1)
+ || (strncmp(argv[0], "S-1-5-21-", strlen("S-1-5-21-")) != 0)
+ || (!string_to_sid(&sid, argv[0]))
+ || (sid.num_auths != 4)) {
+ d_printf("usage: net setlocalsid S-1-5-21-x-y-z\n");
+ return 1;
+ }
+
+ if (!secrets_store_domain_sid(global_myname(), &sid)) {
+ DEBUG(0,("Can't store domain SID as a pdc/bdc.\n"));
+ return 1;
+ }
+
+ return 0;
+}
+
+static int net_getdomainsid(int argc, const char **argv)
+{
+ DOM_SID domain_sid;
+ fstring sid_str;
+
+ if(!initialize_password_db(False)) {
+ DEBUG(0, ("WARNING: Could not open passdb - domain sid may not reflect passdb\n"
+ "backend knowlege (such as the sid stored in LDAP)\n"));
+ }
+
+ /* Generate one, if it doesn't exist */
+ get_global_sam_sid();
+
+ if (!secrets_fetch_domain_sid(global_myname(), &domain_sid)) {
+ d_printf("Could not fetch local SID\n");
+ return 1;
+ }
+ sid_to_string(sid_str, &domain_sid);
+ d_printf("SID for domain %s is: %s\n", global_myname(), sid_str);
+
+ if (!secrets_fetch_domain_sid(opt_workgroup, &domain_sid)) {
+ d_printf("Could not fetch domain SID\n");
+ return 1;
+ }
+
+ sid_to_string(sid_str, &domain_sid);
+ d_printf("SID for domain %s is: %s\n", opt_workgroup, sid_str);
+
+ return 0;
+}
+
+#ifdef WITH_FAKE_KASERVER
+
+int net_afskey_usage(int argc, const char **argv)
+{
+ d_printf(" net afskey filename\n"
+ "\tImports a OpenAFS KeyFile into our secrets.tdb\n\n");
+ return -1;
+}
+
+static int net_afskey(int argc, const char **argv)
+{
+ int fd;
+ struct afs_keyfile keyfile;
+
+ if (argc != 2) {
+ d_printf("usage: 'net afskey <keyfile> cell'\n");
+ return -1;
+ }
+
+ if (!secrets_init()) {
+ d_printf("Could not open secrets.tdb\n");
+ return -1;
+ }
+
+ if ((fd = open(argv[0], O_RDONLY, 0)) < 0) {
+ d_printf("Could not open %s\n", argv[0]);
+ return -1;
+ }
+
+ if (read(fd, &keyfile, sizeof(keyfile)) != sizeof(keyfile)) {
+ d_printf("Could not read keyfile\n");
+ return -1;
+ }
+
+ if (!secrets_store_afs_keyfile(argv[1], &keyfile)) {
+ d_printf("Could not write keyfile to secrets.tdb\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif /* WITH_FAKE_KASERVER */
+
+static uint32 get_maxrid(void)
+{
+ SAM_ACCOUNT *pwd = NULL;
+ uint32 max_rid = 0;
+ GROUP_MAP *map = NULL;
+ int num_entries = 0;
+ int i;
+
+ if (!pdb_setsampwent(False)) {
+ DEBUG(0, ("load_sampwd_entries: Unable to open passdb.\n"));
+ return 0;
+ }
+
+ for (; (NT_STATUS_IS_OK(pdb_init_sam(&pwd)))
+ && pdb_getsampwent(pwd) == True; pwd=NULL) {
+ uint32 rid;
+
+ if (!sid_peek_rid(pdb_get_user_sid(pwd), &rid)) {
+ DEBUG(0, ("can't get RID for user '%s'\n",
+ pdb_get_username(pwd)));
+ pdb_free_sam(&pwd);
+ continue;
+ }
+
+ if (rid > max_rid)
+ max_rid = rid;
+
+ DEBUG(1,("%d is user '%s'\n", rid, pdb_get_username(pwd)));
+ pdb_free_sam(&pwd);
+ }
+
+ pdb_endsampwent();
+ pdb_free_sam(&pwd);
+
+ if (!pdb_enum_group_mapping(SID_NAME_UNKNOWN, &map, &num_entries,
+ ENUM_ONLY_MAPPED))
+ return max_rid;
+
+ for (i = 0; i < num_entries; i++) {
+ uint32 rid;
+
+ if (!sid_peek_check_rid(get_global_sam_sid(), &map[i].sid,
+ &rid)) {
+ DEBUG(3, ("skipping map for group '%s', SID %s\n",
+ map[i].nt_name,
+ sid_string_static(&map[i].sid)));
+ continue;
+ }
+ DEBUG(1,("%d is group '%s'\n", rid, map[i].nt_name));
+
+ if (rid > max_rid)
+ max_rid = rid;
+ }
+
+ SAFE_FREE(map);
+
+ return max_rid;
+}
+
+static int net_maxrid(int argc, const char **argv)
+{
+ uint32 rid;
+
+ if (argc != 0) {
+ DEBUG(0, ("usage: net maxrid\n"));
+ return 1;
+ }
+
+ if ((rid = get_maxrid()) == 0) {
+ DEBUG(0, ("can't get current maximum rid\n"));
+ return 1;
+ }
+
+ d_printf("Currently used maximum rid: %d\n", rid);
+
+ return 0;
+}
+
+/* main function table */
+static struct functable net_func[] = {
+ {"RPC", net_rpc},
+ {"RAP", net_rap},
+ {"ADS", net_ads},
+
+ /* eventually these should auto-choose the transport ... */
+ {"FILE", net_file},
+ {"SHARE", net_share},
+ {"SESSION", net_rap_session},
+ {"SERVER", net_rap_server},
+ {"DOMAIN", net_rap_domain},
+ {"PRINTQ", net_rap_printq},
+ {"USER", net_user},
+ {"GROUP", net_group},
+ {"GROUPMAP", net_groupmap},
+ {"VALIDATE", net_rap_validate},
+ {"GROUPMEMBER", net_rap_groupmember},
+ {"ADMIN", net_rap_admin},
+ {"SERVICE", net_rap_service},
+ {"PASSWORD", net_rap_password},
+ {"CHANGETRUSTPW", net_changetrustpw},
+ {"CHANGESECRETPW", net_changesecretpw},
+ {"TIME", net_time},
+ {"LOOKUP", net_lookup},
+ {"JOIN", net_join},
+ {"CACHE", net_cache},
+ {"GETLOCALSID", net_getlocalsid},
+ {"SETLOCALSID", net_setlocalsid},
+ {"GETDOMAINSID", net_getdomainsid},
+ {"MAXRID", net_maxrid},
+ {"IDMAP", net_idmap},
+ {"STATUS", net_status},
+#ifdef WITH_FAKE_KASERVER
+ {"AFSKEY", net_afskey},
+#endif
+ {"PRIV", net_priv},
+
+ {"HELP", net_help},
+ {NULL, NULL}
+};
+
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc, const char **argv)
+{
+ int opt,i;
+ char *p;
+ int rc = 0;
+ int argc_new = 0;
+ const char ** argv_new;
+ poptContext pc;
+
+ struct poptOption long_options[] = {
+ {"help", 'h', POPT_ARG_NONE, 0, 'h'},
+ {"workgroup", 'w', POPT_ARG_STRING, &opt_target_workgroup},
+ {"user", 'U', POPT_ARG_STRING, &opt_user_name, 'U'},
+ {"ipaddress", 'I', POPT_ARG_STRING, 0,'I'},
+ {"port", 'p', POPT_ARG_INT, &opt_port},
+ {"myname", 'n', POPT_ARG_STRING, &opt_requester_name},
+ {"server", 'S', POPT_ARG_STRING, &opt_host},
+ {"container", 'c', POPT_ARG_STRING, &opt_container},
+ {"comment", 'C', POPT_ARG_STRING, &opt_comment},
+ {"maxusers", 'M', POPT_ARG_INT, &opt_maxusers},
+ {"flags", 'F', POPT_ARG_INT, &opt_flags},
+ {"long", 'l', POPT_ARG_NONE, &opt_long_list_entries},
+ {"reboot", 'r', POPT_ARG_NONE, &opt_reboot},
+ {"force", 'f', POPT_ARG_NONE, &opt_force},
+ {"timeout", 't', POPT_ARG_INT, &opt_timeout},
+ {"machine-pass",'P', POPT_ARG_NONE, &opt_machine_pass},
+ {"myworkgroup", 'W', POPT_ARG_STRING, &opt_workgroup},
+
+ /* Options for 'net groupmap set' */
+ {"local", 'L', POPT_ARG_NONE, &opt_localgroup},
+ {"domain", 'D', POPT_ARG_NONE, &opt_domaingroup},
+ {"ntname", 'N', POPT_ARG_STRING, &opt_newntname},
+ {"rid", 'R', POPT_ARG_INT, &opt_rid},
+
+ POPT_COMMON_SAMBA
+ { 0, 0, 0, 0}
+ };
+
+ zero_ip(&opt_dest_ip);
+
+ /* set default debug level to 0 regardless of what smb.conf sets */
+ DEBUGLEVEL_CLASS[DBGC_ALL] = 0;
+ dbf = x_stderr;
+
+ pc = poptGetContext(NULL, argc, (const char **) argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'h':
+ net_help(argc, argv);
+ exit(0);
+ break;
+ case 'I':
+ opt_dest_ip = *interpret_addr2(poptGetOptArg(pc));
+ if (is_zero_ip(opt_dest_ip))
+ d_printf("\nInvalid ip address specified\n");
+ else
+ opt_have_ip = True;
+ break;
+ case 'U':
+ opt_user_specified = True;
+ opt_user_name = strdup(opt_user_name);
+ p = strchr(opt_user_name,'%');
+ if (p) {
+ *p = 0;
+ opt_password = p+1;
+ }
+ break;
+ default:
+ d_printf("\nInvalid option %s: %s\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ net_help(argc, argv);
+ exit(1);
+ }
+ }
+
+ /*
+ * Don't load debug level from smb.conf. It should be
+ * set by cmdline arg or remain default (0)
+ */
+ AllowDebugChange = False;
+ lp_load(dyn_CONFIGFILE,True,False,False);
+
+ argv_new = (const char **)poptGetArgs(pc);
+
+ argc_new = argc;
+ for (i=0; i<argc; i++) {
+ if (argv_new[i] == NULL) {
+ argc_new = i;
+ break;
+ }
+ }
+
+ if (!opt_requester_name) {
+ static fstring myname;
+ get_myname(myname);
+ opt_requester_name = myname;
+ }
+
+ if (!opt_user_name && getenv("LOGNAME")) {
+ opt_user_name = getenv("LOGNAME");
+ }
+
+ if (!opt_workgroup) {
+ opt_workgroup = smb_xstrdup(lp_workgroup());
+ }
+
+ if (!opt_target_workgroup) {
+ opt_target_workgroup = smb_xstrdup(lp_workgroup());
+ }
+
+ if (!init_names())
+ exit(1);
+
+ load_interfaces();
+
+ /* this makes sure that when we do things like call scripts,
+ that it won't assert becouse we are not root */
+ sec_init();
+
+ if (opt_machine_pass) {
+ /* it is very useful to be able to make ads queries as the
+ machine account for testing purposes and for domain leave */
+
+ net_use_machine_password();
+ }
+
+ if (!opt_password) {
+ opt_password = getenv("PASSWD");
+ }
+
+ rc = net_run_function(argc_new-1, argv_new+1, net_func, net_help);
+
+ DEBUG(2,("return code = %d\n", rc));
+ return rc;
+}
diff --git a/source/utils/net.h b/source/utils/net.h
new file mode 100644
index 00000000000..62d5a742375
--- /dev/null
+++ b/source/utils/net.h
@@ -0,0 +1,69 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "../utils/net_proto.h"
+
+#define NET_FLAGS_MASTER 1
+#define NET_FLAGS_DMB 2
+
+/* Would it be insane to set 'localhost' as the default
+ remote host for this operation?
+
+ For example, localhost is insane for a 'join' operation.
+*/
+#define NET_FLAGS_LOCALHOST_DEFAULT_INSANE 4
+
+/* We want to find the PDC only */
+#define NET_FLAGS_PDC 8
+
+/* We want an anonymous connection */
+#define NET_FLAGS_ANONYMOUS 16
+
+/* don't open an RPC pipe */
+#define NET_FLAGS_NO_PIPE 32
+
+extern int opt_maxusers;
+extern const char *opt_comment;
+extern const char *opt_container;
+extern int opt_flags;
+
+extern const char *opt_comment;
+
+extern const char *opt_target_workgroup;
+extern const char *opt_workgroup;
+extern int opt_long_list_entries;
+extern int opt_reboot;
+extern int opt_force;
+extern int opt_machine_pass;
+extern int opt_timeout;
+extern const char *opt_host;
+extern const char *opt_user_name;
+extern const char *opt_password;
+extern BOOL opt_user_specified;
+
+extern BOOL opt_localgroup;
+extern BOOL opt_domaingroup;
+extern const char *opt_newntname;
+extern int opt_rid;
+
+extern BOOL opt_have_ip;
+extern struct in_addr opt_dest_ip;
+
+extern const char *share_type[];
+
diff --git a/source/utils/net_ads.c b/source/utils/net_ads.c
new file mode 100644
index 00000000000..6eec71aedf3
--- /dev/null
+++ b/source/utils/net_ads.c
@@ -0,0 +1,1328 @@
+/*
+ Samba Unix/Linux SMB client library
+ net ads commands
+ Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
+ Copyright (C) 2001 Remus Koos (remuskoos@yahoo.com)
+ Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "../utils/net.h"
+
+#ifdef HAVE_ADS
+
+int net_ads_usage(int argc, const char **argv)
+{
+ d_printf(
+"\nnet ads join <org_unit>"\
+"\n\tjoins the local machine to a ADS realm\n"\
+"\nnet ads leave"\
+"\n\tremoves the local machine from a ADS realm\n"\
+"\nnet ads testjoin"\
+"\n\ttests that an exiting join is OK\n"\
+"\nnet ads user"\
+"\n\tlist, add, or delete users in the realm\n"\
+"\nnet ads group"\
+"\n\tlist, add, or delete groups in the realm\n"\
+"\nnet ads info"\
+"\n\tshows some info on the server\n"\
+"\nnet ads status"\
+"\n\tdump the machine account details to stdout\n"
+"\nnet ads lookup"\
+"\n\tperform a CLDAP search on the server\n"
+"\nnet ads password <username@realm> <password> -Uadmin_username@realm%%admin_pass"\
+"\n\tchange a user's password using an admin account"\
+"\n\t(note: use realm in UPPERCASE, prompts if password is obmitted)\n"\
+"\nnet ads changetrustpw"\
+"\n\tchange the trust account password of this machine in the AD tree\n"\
+"\nnet ads printer [info | publish | remove] <printername> <servername>"\
+"\n\t lookup, add, or remove directory entry for a printer\n"\
+"\nnet ads search"\
+"\n\tperform a raw LDAP search and dump the results\n"
+"\nnet ads dn"\
+"\n\tperform a raw LDAP search and dump attributes of a particular DN\n"
+ );
+ return -1;
+}
+
+
+/*
+ this implements the CLDAP based netlogon lookup requests
+ for finding the domain controller of a ADS domain
+*/
+static int net_ads_lookup(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+
+ ads = ads_init(NULL, opt_target_workgroup, opt_host);
+ if (ads) {
+ ads->auth.flags |= ADS_AUTH_NO_BIND;
+ }
+
+ ads_connect(ads);
+
+ if (!ads || !ads->config.realm) {
+ d_printf("Didn't find the cldap server!\n");
+ return -1;
+ }
+
+ return ads_cldap_netlogon(ads);
+}
+
+
+
+static int net_ads_info(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+
+ ads = ads_init(NULL, opt_target_workgroup, opt_host);
+
+ if (ads) {
+ ads->auth.flags |= ADS_AUTH_NO_BIND;
+ }
+
+ ads_connect(ads);
+
+ if (!ads || !ads->config.realm) {
+ d_printf("Didn't find the ldap server!\n");
+ return -1;
+ }
+
+ d_printf("LDAP server: %s\n", inet_ntoa(ads->ldap_ip));
+ d_printf("LDAP server name: %s\n", ads->config.ldap_server_name);
+ d_printf("Realm: %s\n", ads->config.realm);
+ d_printf("Bind Path: %s\n", ads->config.bind_path);
+ d_printf("LDAP port: %d\n", ads->ldap_port);
+ d_printf("Server time: %s\n", http_timestring(ads->config.current_time));
+
+ d_printf("KDC server: %s\n", ads->auth.kdc_server );
+ d_printf("Server time offset: %d\n", ads->auth.time_offset );
+
+ return 0;
+}
+
+static void use_in_memory_ccache(void) {
+ /* Use in-memory credentials cache so we do not interfere with
+ * existing credentials */
+ setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads", 1);
+}
+
+static ADS_STRUCT *ads_startup(void)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS status;
+ BOOL need_password = False;
+ BOOL second_time = False;
+ char *cp;
+
+ /* lp_realm() should be handled by a command line param,
+ However, the join requires that realm be set in smb.conf
+ and compares our realm with the remote server's so this is
+ ok until someone needs more flexibility */
+
+ ads = ads_init(lp_realm(), opt_target_workgroup, opt_host);
+
+ if (!opt_user_name) {
+ opt_user_name = "administrator";
+ }
+
+ if (opt_user_specified) {
+ need_password = True;
+ }
+
+retry:
+ if (!opt_password && need_password && !opt_machine_pass) {
+ char *prompt;
+ asprintf(&prompt,"%s's password: ", opt_user_name);
+ opt_password = getpass(prompt);
+ free(prompt);
+ }
+
+ if (opt_password) {
+ use_in_memory_ccache();
+ ads->auth.password = smb_xstrdup(opt_password);
+ }
+
+ ads->auth.user_name = smb_xstrdup(opt_user_name);
+
+ /*
+ * If the username is of the form "name@realm",
+ * extract the realm and convert to upper case.
+ * This is only used to establish the connection.
+ */
+ if ((cp = strchr(ads->auth.user_name, '@'))!=0) {
+ *cp++ = '\0';
+ ads->auth.realm = smb_xstrdup(cp);
+ strupper_m(ads->auth.realm);
+ }
+
+ status = ads_connect(ads);
+
+ if (!ADS_ERR_OK(status)) {
+ if (!need_password && !second_time) {
+ need_password = True;
+ second_time = True;
+ goto retry;
+ } else {
+ DEBUG(1,("ads_connect: %s\n", ads_errstr(status)));
+ return NULL;
+ }
+ }
+ return ads;
+}
+
+
+/*
+ Check to see if connection can be made via ads.
+ ads_startup() stores the password in opt_password if it needs to so
+ that rpc or rap can use it without re-prompting.
+*/
+int net_ads_check(void)
+{
+ ADS_STRUCT *ads;
+
+ ads = ads_startup();
+ if (!ads)
+ return -1;
+ ads_destroy(&ads);
+ return 0;
+}
+
+/*
+ determine the netbios workgroup name for a domain
+ */
+static int net_ads_workgroup(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ TALLOC_CTX *ctx;
+ const char *workgroup;
+
+ if (!(ads = ads_startup())) return -1;
+
+ if (!(ctx = talloc_init("net_ads_workgroup"))) {
+ return -1;
+ }
+
+ if (!ADS_ERR_OK(ads_workgroup_name(ads, ctx, &workgroup))) {
+ d_printf("Failed to find workgroup for realm '%s'\n",
+ ads->config.realm);
+ talloc_destroy(ctx);
+ return -1;
+ }
+
+ d_printf("Workgroup: %s\n", workgroup);
+
+ talloc_destroy(ctx);
+
+ return 0;
+}
+
+
+
+static BOOL usergrp_display(char *field, void **values, void *data_area)
+{
+ char **disp_fields = (char **) data_area;
+
+ if (!field) { /* must be end of record */
+ if (!strchr_m(disp_fields[0], '$')) {
+ if (disp_fields[1])
+ d_printf("%-21.21s %s\n",
+ disp_fields[0], disp_fields[1]);
+ else
+ d_printf("%s\n", disp_fields[0]);
+ }
+ SAFE_FREE(disp_fields[0]);
+ SAFE_FREE(disp_fields[1]);
+ return True;
+ }
+ if (!values) /* must be new field, indicate string field */
+ return True;
+ if (StrCaseCmp(field, "sAMAccountName") == 0) {
+ disp_fields[0] = strdup((char *) values[0]);
+ }
+ if (StrCaseCmp(field, "description") == 0)
+ disp_fields[1] = strdup((char *) values[0]);
+ return True;
+}
+
+static int net_ads_user_usage(int argc, const char **argv)
+{
+ return net_help_user(argc, argv);
+}
+
+static int ads_user_add(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS status;
+ char *upn, *userdn;
+ void *res=NULL;
+ int rc = -1;
+
+ if (argc < 1) return net_ads_user_usage(argc, argv);
+
+ if (!(ads = ads_startup())) return -1;
+
+ status = ads_find_user_acct(ads, &res, argv[0]);
+
+ if (!ADS_ERR_OK(status)) {
+ d_printf("ads_user_add: %s\n", ads_errstr(status));
+ goto done;
+ }
+
+ if (ads_count_replies(ads, res)) {
+ d_printf("ads_user_add: User %s already exists\n", argv[0]);
+ goto done;
+ }
+
+ status = ads_add_user_acct(ads, argv[0], opt_container, opt_comment);
+
+ if (!ADS_ERR_OK(status)) {
+ d_printf("Could not add user %s: %s\n", argv[0],
+ ads_errstr(status));
+ goto done;
+ }
+
+ /* if no password is to be set, we're done */
+ if (argc == 1) {
+ d_printf("User %s added\n", argv[0]);
+ rc = 0;
+ goto done;
+ }
+
+ /* try setting the password */
+ asprintf(&upn, "%s@%s", argv[0], ads->config.realm);
+ status = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1],
+ ads->auth.time_offset);
+ safe_free(upn);
+ if (ADS_ERR_OK(status)) {
+ d_printf("User %s added\n", argv[0]);
+ rc = 0;
+ goto done;
+ }
+
+ /* password didn't set, delete account */
+ d_printf("Could not add user %s. Error setting password %s\n",
+ argv[0], ads_errstr(status));
+ ads_msgfree(ads, res);
+ status=ads_find_user_acct(ads, &res, argv[0]);
+ if (ADS_ERR_OK(status)) {
+ userdn = ads_get_dn(ads, res);
+ ads_del_dn(ads, userdn);
+ ads_memfree(ads, userdn);
+ }
+
+ done:
+ if (res)
+ ads_msgfree(ads, res);
+ ads_destroy(&ads);
+ return rc;
+}
+
+static int ads_user_info(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ void *res;
+ const char *attrs[] = {"memberOf", NULL};
+ char *searchstring=NULL;
+ char **grouplist;
+ char *escaped_user = escape_ldap_string_alloc(argv[0]);
+
+ if (argc < 1) return net_ads_user_usage(argc, argv);
+
+ if (!(ads = ads_startup())) return -1;
+
+ if (!escaped_user) {
+ d_printf("ads_user_info: failed to escape user %s\n", argv[0]);
+ return -1;
+ }
+
+ asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user);
+ rc = ads_search(ads, &res, searchstring, attrs);
+ safe_free(searchstring);
+
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("ads_search: %s\n", ads_errstr(rc));
+ return -1;
+ }
+
+ grouplist = ldap_get_values(ads->ld, res, "memberOf");
+
+ if (grouplist) {
+ int i;
+ char **groupname;
+ for (i=0;grouplist[i];i++) {
+ groupname = ldap_explode_dn(grouplist[i], 1);
+ d_printf("%s\n", groupname[0]);
+ ldap_value_free(groupname);
+ }
+ ldap_value_free(grouplist);
+ }
+
+ ads_msgfree(ads, res);
+
+ ads_destroy(&ads);
+ return 0;
+}
+
+static int ads_user_delete(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ void *res;
+ char *userdn;
+
+ if (argc < 1) return net_ads_user_usage(argc, argv);
+
+ if (!(ads = ads_startup())) return -1;
+
+ rc = ads_find_user_acct(ads, &res, argv[0]);
+ if (!ADS_ERR_OK(rc)) {
+ DEBUG(0, ("User %s does not exist\n", argv[0]));
+ return -1;
+ }
+ userdn = ads_get_dn(ads, res);
+ ads_msgfree(ads, res);
+ rc = ads_del_dn(ads, userdn);
+ ads_memfree(ads, userdn);
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("User %s deleted\n", argv[0]);
+ return 0;
+ }
+ d_printf("Error deleting user %s: %s\n", argv[0],
+ ads_errstr(rc));
+ return -1;
+}
+
+int net_ads_user(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"ADD", ads_user_add},
+ {"INFO", ads_user_info},
+ {"DELETE", ads_user_delete},
+ {NULL, NULL}
+ };
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ const char *shortattrs[] = {"sAMAccountName", NULL};
+ const char *longattrs[] = {"sAMAccountName", "description", NULL};
+ char *disp_fields[2] = {NULL, NULL};
+
+ if (argc == 0) {
+ if (!(ads = ads_startup())) return -1;
+
+ if (opt_long_list_entries)
+ d_printf("\nUser name Comment"\
+ "\n-----------------------------\n");
+
+ rc = ads_do_search_all_fn(ads, ads->config.bind_path,
+ LDAP_SCOPE_SUBTREE,
+ "(objectclass=user)",
+ opt_long_list_entries ? longattrs :
+ shortattrs, usergrp_display,
+ disp_fields);
+ ads_destroy(&ads);
+ return 0;
+ }
+
+ return net_run_function(argc, argv, func, net_ads_user_usage);
+}
+
+static int net_ads_group_usage(int argc, const char **argv)
+{
+ return net_help_group(argc, argv);
+}
+
+static int ads_group_add(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS status;
+ void *res=NULL;
+ int rc = -1;
+
+ if (argc < 1) return net_ads_group_usage(argc, argv);
+
+ if (!(ads = ads_startup())) return -1;
+
+ status = ads_find_user_acct(ads, &res, argv[0]);
+
+ if (!ADS_ERR_OK(status)) {
+ d_printf("ads_group_add: %s\n", ads_errstr(status));
+ goto done;
+ }
+
+ if (ads_count_replies(ads, res)) {
+ d_printf("ads_group_add: Group %s already exists\n", argv[0]);
+ ads_msgfree(ads, res);
+ goto done;
+ }
+
+ status = ads_add_group_acct(ads, argv[0], opt_container, opt_comment);
+
+ if (ADS_ERR_OK(status)) {
+ d_printf("Group %s added\n", argv[0]);
+ rc = 0;
+ } else {
+ d_printf("Could not add group %s: %s\n", argv[0],
+ ads_errstr(status));
+ }
+
+ done:
+ if (res)
+ ads_msgfree(ads, res);
+ ads_destroy(&ads);
+ return rc;
+}
+
+static int ads_group_delete(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ void *res;
+ char *groupdn;
+
+ if (argc < 1) return net_ads_group_usage(argc, argv);
+
+ if (!(ads = ads_startup())) return -1;
+
+ rc = ads_find_user_acct(ads, &res, argv[0]);
+ if (!ADS_ERR_OK(rc)) {
+ DEBUG(0, ("Group %s does not exist\n", argv[0]));
+ return -1;
+ }
+ groupdn = ads_get_dn(ads, res);
+ ads_msgfree(ads, res);
+ rc = ads_del_dn(ads, groupdn);
+ ads_memfree(ads, groupdn);
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("Group %s deleted\n", argv[0]);
+ return 0;
+ }
+ d_printf("Error deleting group %s: %s\n", argv[0],
+ ads_errstr(rc));
+ return -1;
+}
+
+int net_ads_group(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"ADD", ads_group_add},
+ {"DELETE", ads_group_delete},
+ {NULL, NULL}
+ };
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ const char *shortattrs[] = {"sAMAccountName", NULL};
+ const char *longattrs[] = {"sAMAccountName", "description", NULL};
+ char *disp_fields[2] = {NULL, NULL};
+
+ if (argc == 0) {
+ if (!(ads = ads_startup())) return -1;
+
+ if (opt_long_list_entries)
+ d_printf("\nGroup name Comment"\
+ "\n-----------------------------\n");
+ rc = ads_do_search_all_fn(ads, ads->config.bind_path,
+ LDAP_SCOPE_SUBTREE,
+ "(objectclass=group)",
+ opt_long_list_entries ? longattrs :
+ shortattrs, usergrp_display,
+ disp_fields);
+
+ ads_destroy(&ads);
+ return 0;
+ }
+ return net_run_function(argc, argv, func, net_ads_group_usage);
+}
+
+static int net_ads_status(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ void *res;
+
+ if (!(ads = ads_startup())) return -1;
+
+ rc = ads_find_machine_acct(ads, &res, global_myname());
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("ads_find_machine_acct: %s\n", ads_errstr(rc));
+ return -1;
+ }
+
+ if (ads_count_replies(ads, res) == 0) {
+ d_printf("No machine account for '%s' found\n", global_myname());
+ return -1;
+ }
+
+ ads_dump(ads, res);
+
+ return 0;
+}
+
+static int net_ads_leave(int argc, const char **argv)
+{
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS rc;
+
+ if (!secrets_init()) {
+ DEBUG(1,("Failed to initialise secrets database\n"));
+ return -1;
+ }
+
+ if (!opt_password) {
+ net_use_machine_password();
+ }
+
+ if (!(ads = ads_startup())) {
+ return -1;
+ }
+
+ rc = ads_leave_realm(ads, global_myname());
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("Failed to delete host '%s' from the '%s' realm.\n",
+ global_myname(), ads->config.realm);
+ return -1;
+ }
+
+ d_printf("Removed '%s' from realm '%s'\n", global_myname(), ads->config.realm);
+
+ return 0;
+}
+
+static int net_ads_join_ok(void)
+{
+ ADS_STRUCT *ads = NULL;
+
+ if (!secrets_init()) {
+ DEBUG(1,("Failed to initialise secrets database\n"));
+ return -1;
+ }
+
+ net_use_machine_password();
+
+ if (!(ads = ads_startup())) {
+ return -1;
+ }
+
+ ads_destroy(&ads);
+ return 0;
+}
+
+/*
+ check that an existing join is OK
+ */
+int net_ads_testjoin(int argc, const char **argv)
+{
+ use_in_memory_ccache();
+
+ /* Display success or failure */
+ if (net_ads_join_ok() != 0) {
+ fprintf(stderr,"Join to domain is not valid\n");
+ return -1;
+ }
+
+ printf("Join is OK\n");
+ return 0;
+}
+
+/*
+ join a domain using ADS
+ */
+int net_ads_join(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ char *password;
+ char *machine_account = NULL;
+ char *tmp_password;
+ const char *org_unit = "Computers";
+ char *dn;
+ void *res;
+ DOM_SID dom_sid;
+ char *ou_str;
+ uint32 sec_channel_type = SEC_CHAN_WKSTA;
+ uint32 account_type = UF_WORKSTATION_TRUST_ACCOUNT;
+ const char *short_domain_name = NULL;
+ TALLOC_CTX *ctx = NULL;
+
+ if (argc > 0) org_unit = argv[0];
+
+ if (!secrets_init()) {
+ DEBUG(1,("Failed to initialise secrets database\n"));
+ return -1;
+ }
+
+ tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
+ password = strdup(tmp_password);
+
+ if (!(ads = ads_startup())) return -1;
+
+ if (!*lp_realm()) {
+ d_printf("realm must be set in in smb.conf for ADS join to succeed.\n");
+ return -1;
+ }
+
+ if (strcmp(ads->config.realm, lp_realm()) != 0) {
+ d_printf("realm of remote server (%s) and realm in smb.conf (%s) DO NOT match. Aborting join\n", ads->config.realm, lp_realm());
+ return -1;
+ }
+
+ ou_str = ads_ou_string(org_unit);
+ asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path);
+ free(ou_str);
+
+ rc = ads_search_dn(ads, &res, dn, NULL);
+ ads_msgfree(ads, res);
+
+ if (rc.error_type == ENUM_ADS_ERROR_LDAP && rc.err.rc == LDAP_NO_SUCH_OBJECT) {
+ d_printf("ads_join_realm: organizational unit %s does not exist (dn:%s)\n",
+ org_unit, dn);
+ return -1;
+ }
+ free(dn);
+
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("ads_join_realm: %s\n", ads_errstr(rc));
+ return -1;
+ }
+
+ rc = ads_join_realm(ads, global_myname(), account_type, org_unit);
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("ads_join_realm: %s\n", ads_errstr(rc));
+ return -1;
+ }
+
+ rc = ads_domain_sid(ads, &dom_sid);
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("ads_domain_sid: %s\n", ads_errstr(rc));
+ return -1;
+ }
+
+ if (asprintf(&machine_account, "%s$", global_myname()) == -1) {
+ d_printf("asprintf failed\n");
+ return -1;
+ }
+
+ rc = ads_set_machine_password(ads, machine_account, password);
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("ads_set_machine_password: %s\n", ads_errstr(rc));
+ return -1;
+ }
+
+ /* make sure we get the right workgroup */
+
+ if ( !(ctx = talloc_init("net ads join")) ) {
+ d_printf("talloc_init() failed!\n");
+ return -1;
+ }
+
+ rc = ads_workgroup_name(ads, ctx, &short_domain_name);
+ if ( ADS_ERR_OK(rc) ) {
+ if ( !strequal(lp_workgroup(), short_domain_name) ) {
+ d_printf("The workgroup in smb.conf does not match the short\n");
+ d_printf("domain name obtained from the server.\n");
+ d_printf("Using the name [%s] from the server.\n", short_domain_name);
+ d_printf("You should set \"workgroup = %s\" in smb.conf.\n", short_domain_name);
+ }
+ }
+ else
+ short_domain_name = lp_workgroup();
+
+ d_printf("Using short domain name -- %s\n", short_domain_name);
+
+ /* HACK ALRET! Store the sid and password under bother the lp_workgroup()
+ value from smb.conf and the string returned from the server. The former is
+ neede to bootstrap winbindd's first connection to the DC to get the real
+ short domain name --jerry */
+
+ if (!secrets_store_domain_sid(lp_workgroup(), &dom_sid)) {
+ DEBUG(1,("Failed to save domain sid\n"));
+ return -1;
+ }
+
+ if (!secrets_store_machine_password(password, lp_workgroup(), sec_channel_type)) {
+ DEBUG(1,("Failed to save machine password\n"));
+ return -1;
+ }
+
+ if (!secrets_store_domain_sid(short_domain_name, &dom_sid)) {
+ DEBUG(1,("Failed to save domain sid\n"));
+ return -1;
+ }
+
+ if (!secrets_store_machine_password(password, short_domain_name, sec_channel_type)) {
+ DEBUG(1,("Failed to save machine password\n"));
+ return -1;
+ }
+
+ d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->config.realm);
+
+ SAFE_FREE(password);
+ SAFE_FREE(machine_account);
+ if ( ctx )
+ talloc_destroy(ctx);
+ return 0;
+}
+
+int net_ads_printer_usage(int argc, const char **argv)
+{
+ d_printf(
+"\nnet ads printer search <printer>"
+"\n\tsearch for a printer in the directory"
+"\nnet ads printer info <printer> <server>"
+"\n\tlookup info in directory for printer on server"
+"\n\t(note: printer defaults to \"*\", server defaults to local)\n"
+"\nnet ads printer publish <printername>"
+"\n\tpublish printer in directory"
+"\n\t(note: printer name is required)\n"
+"\nnet ads printer remove <printername>"
+"\n\tremove printer from directory"
+"\n\t(note: printer name is required)\n");
+ return -1;
+}
+
+static int net_ads_printer_search(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ void *res = NULL;
+
+ if (!(ads = ads_startup()))
+ return -1;
+
+ rc = ads_find_printers(ads, &res);
+
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("ads_find_printer: %s\n", ads_errstr(rc));
+ ads_msgfree(ads, res);
+ return -1;
+ }
+
+ if (ads_count_replies(ads, res) == 0) {
+ d_printf("No results found\n");
+ ads_msgfree(ads, res);
+ return -1;
+ }
+
+ ads_dump(ads, res);
+ ads_msgfree(ads, res);
+
+ return 0;
+}
+
+static int net_ads_printer_info(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ const char *servername, *printername;
+ void *res = NULL;
+
+ if (!(ads = ads_startup())) return -1;
+
+ if (argc > 0)
+ printername = argv[0];
+ else
+ printername = "*";
+
+ if (argc > 1)
+ servername = argv[1];
+ else
+ servername = global_myname();
+
+ rc = ads_find_printer_on_server(ads, &res, printername, servername);
+
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("ads_find_printer_on_server: %s\n", ads_errstr(rc));
+ ads_msgfree(ads, res);
+ return -1;
+ }
+
+ if (ads_count_replies(ads, res) == 0) {
+ d_printf("Printer '%s' not found\n", printername);
+ ads_msgfree(ads, res);
+ return -1;
+ }
+
+ ads_dump(ads, res);
+ ads_msgfree(ads, res);
+
+ return 0;
+}
+
+void do_drv_upgrade_printer(int msg_type, pid_t src, void *buf, size_t len)
+{
+ return;
+}
+
+static int net_ads_printer_publish(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ const char *servername, *printername;
+ struct cli_state *cli;
+ struct in_addr server_ip;
+ NTSTATUS nt_status;
+ TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
+ ADS_MODLIST mods = ads_init_mods(mem_ctx);
+ char *prt_dn, *srv_dn, **srv_cn;
+ void *res = NULL;
+
+ if (!(ads = ads_startup())) return -1;
+
+ if (argc < 1)
+ return net_ads_printer_usage(argc, argv);
+
+ printername = argv[0];
+
+ if (argc == 2)
+ servername = argv[1];
+ else
+ servername = global_myname();
+
+ /* Get printer data from SPOOLSS */
+
+ resolve_name(servername, &server_ip, 0x20);
+
+ nt_status = cli_full_connection(&cli, global_myname(), servername,
+ &server_ip, 0,
+ "IPC$", "IPC",
+ opt_user_name, opt_workgroup,
+ opt_password ? opt_password : "",
+ CLI_FULL_CONNECTION_USE_KERBEROS,
+ Undefined, NULL);
+
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ d_printf("Unable to open a connnection to %s to obtain data "
+ "for %s\n", servername, printername);
+ return -1;
+ }
+
+ /* Publish on AD server */
+
+ ads_find_machine_acct(ads, &res, servername);
+
+ if (ads_count_replies(ads, res) == 0) {
+ d_printf("Could not find machine account for server %s\n",
+ servername);
+ return -1;
+ }
+
+ srv_dn = ldap_get_dn(ads->ld, res);
+ srv_cn = ldap_explode_dn(srv_dn, 1);
+
+ asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn[0], printername, srv_dn);
+
+ cli_nt_session_open(cli, PI_SPOOLSS);
+ get_remote_printer_publishing_data(cli, mem_ctx, &mods, printername);
+
+ rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("ads_publish_printer: %s\n", ads_errstr(rc));
+ return -1;
+ }
+
+ d_printf("published printer\n");
+
+ return 0;
+}
+
+static int net_ads_printer_remove(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ const char *servername;
+ char *prt_dn;
+ void *res = NULL;
+
+ if (!(ads = ads_startup())) return -1;
+
+ if (argc < 1)
+ return net_ads_printer_usage(argc, argv);
+
+ if (argc > 1)
+ servername = argv[1];
+ else
+ servername = global_myname();
+
+ rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
+
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("ads_find_printer_on_server: %s\n", ads_errstr(rc));
+ ads_msgfree(ads, res);
+ return -1;
+ }
+
+ if (ads_count_replies(ads, res) == 0) {
+ d_printf("Printer '%s' not found\n", argv[1]);
+ ads_msgfree(ads, res);
+ return -1;
+ }
+
+ prt_dn = ads_get_dn(ads, res);
+ ads_msgfree(ads, res);
+ rc = ads_del_dn(ads, prt_dn);
+ ads_memfree(ads, prt_dn);
+
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("ads_del_dn: %s\n", ads_errstr(rc));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int net_ads_printer(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"SEARCH", net_ads_printer_search},
+ {"INFO", net_ads_printer_info},
+ {"PUBLISH", net_ads_printer_publish},
+ {"REMOVE", net_ads_printer_remove},
+ {NULL, NULL}
+ };
+
+ return net_run_function(argc, argv, func, net_ads_printer_usage);
+}
+
+
+static int net_ads_password(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ const char *auth_principal = opt_user_name;
+ const char *auth_password = opt_password;
+ char *realm = NULL;
+ char *new_password = NULL;
+ char *c, *prompt;
+ const char *user;
+ ADS_STATUS ret;
+
+ if (opt_user_name == NULL || opt_password == NULL) {
+ d_printf("You must supply an administrator username/password\n");
+ return -1;
+ }
+
+
+ if (argc < 1) {
+ d_printf("ERROR: You must say which username to change password for\n");
+ return -1;
+ }
+
+ user = argv[0];
+ if (!strchr(user, '@')) {
+ asprintf(&c, "%s@%s", argv[0], lp_realm());
+ user = c;
+ }
+
+ use_in_memory_ccache();
+ c = strchr(auth_principal, '@');
+ if (c) {
+ realm = ++c;
+ } else {
+ realm = lp_realm();
+ }
+
+ /* use the realm so we can eventually change passwords for users
+ in realms other than default */
+ if (!(ads = ads_init(realm, NULL, NULL))) return -1;
+
+ /* we don't actually need a full connect, but it's the easy way to
+ fill in the KDC's addresss */
+ ads_connect(ads);
+
+ if (!ads || !ads->config.realm) {
+ d_printf("Didn't find the kerberos server!\n");
+ return -1;
+ }
+
+ if (argv[1]) {
+ new_password = (char *)argv[1];
+ } else {
+ asprintf(&prompt, "Enter new password for %s:", user);
+ new_password = getpass(prompt);
+ free(prompt);
+ }
+
+ ret = kerberos_set_password(ads->auth.kdc_server, auth_principal,
+ auth_password, user, new_password, ads->auth.time_offset);
+ if (!ADS_ERR_OK(ret)) {
+ d_printf("Password change failed :-( ...\n");
+ ads_destroy(&ads);
+ return -1;
+ }
+
+ d_printf("Password change for %s completed.\n", user);
+ ads_destroy(&ads);
+
+ return 0;
+}
+
+
+int net_ads_changetrustpw(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ char *host_principal;
+ char *hostname;
+ ADS_STATUS ret;
+
+ if (!secrets_init()) {
+ DEBUG(1,("Failed to initialise secrets database\n"));
+ return -1;
+ }
+
+ net_use_machine_password();
+
+ use_in_memory_ccache();
+
+ if (!(ads = ads_startup())) {
+ return -1;
+ }
+
+ hostname = strdup(global_myname());
+ strlower_m(hostname);
+ asprintf(&host_principal, "%s@%s", hostname, ads->config.realm);
+ SAFE_FREE(hostname);
+ d_printf("Changing password for principal: HOST/%s\n", host_principal);
+
+ ret = ads_change_trust_account_password(ads, host_principal);
+
+ if (!ADS_ERR_OK(ret)) {
+ d_printf("Password change failed :-( ...\n");
+ ads_destroy(&ads);
+ SAFE_FREE(host_principal);
+ return -1;
+ }
+
+ d_printf("Password change for principal HOST/%s succeeded.\n", host_principal);
+ ads_destroy(&ads);
+ SAFE_FREE(host_principal);
+
+ return 0;
+}
+
+/*
+ help for net ads search
+*/
+static int net_ads_search_usage(int argc, const char **argv)
+{
+ d_printf(
+ "\nnet ads search <expression> <attributes...>\n"\
+ "\nperform a raw LDAP search on a ADS server and dump the results\n"\
+ "The expression is a standard LDAP search expression, and the\n"\
+ "attributes are a list of LDAP fields to show in the results\n\n"\
+ "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
+ );
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+
+/*
+ general ADS search function. Useful in diagnosing problems in ADS
+*/
+static int net_ads_search(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ const char *ldap_exp;
+ const char **attrs;
+ void *res = NULL;
+
+ if (argc < 1) {
+ return net_ads_search_usage(argc, argv);
+ }
+
+ if (!(ads = ads_startup())) {
+ return -1;
+ }
+
+ ldap_exp = argv[0];
+ attrs = (argv + 1);
+
+ rc = ads_do_search_all(ads, ads->config.bind_path,
+ LDAP_SCOPE_SUBTREE,
+ ldap_exp, attrs, &res);
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("search failed: %s\n", ads_errstr(rc));
+ return -1;
+ }
+
+ d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
+
+ /* dump the results */
+ ads_dump(ads, res);
+
+ ads_msgfree(ads, res);
+ ads_destroy(&ads);
+
+ return 0;
+}
+
+
+/*
+ help for net ads search
+*/
+static int net_ads_dn_usage(int argc, const char **argv)
+{
+ d_printf(
+ "\nnet ads dn <dn> <attributes...>\n"\
+ "\nperform a raw LDAP search on a ADS server and dump the results\n"\
+ "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\
+ "to show in the results\n\n"\
+ "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n"
+ );
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+
+/*
+ general ADS search function. Useful in diagnosing problems in ADS
+*/
+static int net_ads_dn(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ const char *dn;
+ const char **attrs;
+ void *res = NULL;
+
+ if (argc < 1) {
+ return net_ads_dn_usage(argc, argv);
+ }
+
+ if (!(ads = ads_startup())) {
+ return -1;
+ }
+
+ dn = argv[0];
+ attrs = (argv + 1);
+
+ rc = ads_do_search_all(ads, dn,
+ LDAP_SCOPE_BASE,
+ "(objectclass=*)", attrs, &res);
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("search failed: %s\n", ads_errstr(rc));
+ return -1;
+ }
+
+ d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
+
+ /* dump the results */
+ ads_dump(ads, res);
+
+ ads_msgfree(ads, res);
+ ads_destroy(&ads);
+
+ return 0;
+}
+
+
+int net_ads_help(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"USER", net_ads_user_usage},
+ {"GROUP", net_ads_group_usage},
+ {"PRINTER", net_ads_printer_usage},
+ {"SEARCH", net_ads_search_usage},
+#if 0
+ {"INFO", net_ads_info},
+ {"JOIN", net_ads_join},
+ {"LEAVE", net_ads_leave},
+ {"STATUS", net_ads_status},
+ {"PASSWORD", net_ads_password},
+ {"CHANGETRUSTPW", net_ads_changetrustpw},
+#endif
+ {NULL, NULL}
+ };
+
+ return net_run_function(argc, argv, func, net_ads_usage);
+}
+
+int net_ads(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"INFO", net_ads_info},
+ {"JOIN", net_ads_join},
+ {"TESTJOIN", net_ads_testjoin},
+ {"LEAVE", net_ads_leave},
+ {"STATUS", net_ads_status},
+ {"USER", net_ads_user},
+ {"GROUP", net_ads_group},
+ {"PASSWORD", net_ads_password},
+ {"CHANGETRUSTPW", net_ads_changetrustpw},
+ {"PRINTER", net_ads_printer},
+ {"SEARCH", net_ads_search},
+ {"DN", net_ads_dn},
+ {"WORKGROUP", net_ads_workgroup},
+ {"LOOKUP", net_ads_lookup},
+ {"HELP", net_ads_help},
+ {NULL, NULL}
+ };
+
+ return net_run_function(argc, argv, func, net_ads_usage);
+}
+
+#else
+
+static int net_ads_noads(void)
+{
+ d_printf("ADS support not compiled in\n");
+ return -1;
+}
+
+int net_ads_usage(int argc, const char **argv)
+{
+ return net_ads_noads();
+}
+
+int net_ads_help(int argc, const char **argv)
+{
+ return net_ads_noads();
+}
+
+int net_ads_changetrustpw(int argc, const char **argv)
+{
+ return net_ads_noads();
+}
+
+int net_ads_join(int argc, const char **argv)
+{
+ return net_ads_noads();
+}
+
+int net_ads_user(int argc, const char **argv)
+{
+ return net_ads_noads();
+}
+
+int net_ads_group(int argc, const char **argv)
+{
+ return net_ads_noads();
+}
+
+/* this one shouldn't display a message */
+int net_ads_check(void)
+{
+ return -1;
+}
+
+int net_ads(int argc, const char **argv)
+{
+ return net_ads_usage(argc, argv);
+}
+
+#endif
diff --git a/source/utils/net_ads_cldap.c b/source/utils/net_ads_cldap.c
new file mode 100644
index 00000000000..1903172cf75
--- /dev/null
+++ b/source/utils/net_ads_cldap.c
@@ -0,0 +1,364 @@
+/*
+ Samba Unix/Linux SMB client library
+ net ads cldap functions
+ Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
+ Copyright (C) 2003 Jim McDonough (jmcd@us.ibm.com)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "../utils/net.h"
+
+#ifdef HAVE_ADS
+
+#define MAX_DNS_LABEL 255 + 1
+
+struct cldap_netlogon_reply {
+ uint32 type;
+ uint32 flags;
+ UUID_FLAT guid;
+
+ char forest[MAX_DNS_LABEL];
+ char domain[MAX_DNS_LABEL];
+ char hostname[MAX_DNS_LABEL];
+
+ char netbios_domain[MAX_DNS_LABEL];
+ char netbios_hostname[MAX_DNS_LABEL];
+
+ char unk[MAX_DNS_LABEL];
+ char user_name[MAX_DNS_LABEL];
+ char site_name[MAX_DNS_LABEL];
+ char site_name_2[MAX_DNS_LABEL];
+
+ uint32 version;
+ uint16 lmnt_token;
+ uint16 lm20_token;
+};
+
+/*
+ These seem to be strings as described in RFC1035 4.1.4 and can be:
+
+ - a sequence of labels ending in a zero octet
+ - a pointer
+ - a sequence of labels ending with a pointer
+
+ A label is a byte where the first two bits must be zero and the remaining
+ bits represent the length of the label followed by the label itself.
+ Therefore, the length of a label is at max 64 bytes. Under RFC1035, a
+ sequence of labels cannot exceed 255 bytes.
+
+ A pointer consists of a 14 bit offset from the beginning of the data.
+
+ struct ptr {
+ unsigned ident:2; // must be 11
+ unsigned offset:14; // from the beginning of data
+ };
+
+ This is used as a method to compress the packet by eliminated duplicate
+ domain components. Since a UDP packet should probably be < 512 bytes and a
+ DNS name can be up to 255 bytes, this actually makes a lot of sense.
+*/
+static unsigned pull_netlogon_string(char *ret, const char *ptr,
+ const char *data)
+{
+ char *pret = ret;
+ int followed_ptr = 0;
+ unsigned ret_len = 0;
+
+ memset(pret, 0, MAX_DNS_LABEL);
+ do {
+ if ((*ptr & 0xc0) == 0xc0) {
+ uint16 len;
+
+ if (!followed_ptr) {
+ ret_len += 2;
+ followed_ptr = 1;
+ }
+ len = ((ptr[0] & 0x3f) << 8) | ptr[1];
+ ptr = data + len;
+ } else if (*ptr) {
+ uint8 len = (uint8)*(ptr++);
+
+ if ((pret - ret + len + 1) >= MAX_DNS_LABEL) {
+ d_printf("DC returning too long DNS name\n");
+ return 0;
+ }
+
+ if (pret != ret) {
+ *pret = '.';
+ pret++;
+ }
+ memcpy(pret, ptr, len);
+ pret += len;
+ ptr += len;
+
+ if (!followed_ptr) {
+ ret_len += (len + 1);
+ }
+ }
+ } while (*ptr);
+
+ return followed_ptr ? ret_len : ret_len + 1;
+}
+
+/*
+ do a cldap netlogon query
+*/
+static int send_cldap_netlogon(int sock, const char *domain,
+ const char *hostname, unsigned ntversion)
+{
+ ASN1_DATA data;
+ char ntver[4];
+#ifdef CLDAP_USER_QUERY
+ char aac[4];
+
+ SIVAL(aac, 0, 0x00000180);
+#endif
+ SIVAL(ntver, 0, ntversion);
+
+ memset(&data, 0, sizeof(data));
+
+ asn1_push_tag(&data,ASN1_SEQUENCE(0));
+ asn1_write_Integer(&data, 4);
+ asn1_push_tag(&data, ASN1_APPLICATION(3));
+ asn1_write_OctetString(&data, NULL, 0);
+ asn1_write_enumerated(&data, 0);
+ asn1_write_enumerated(&data, 0);
+ asn1_write_Integer(&data, 0);
+ asn1_write_Integer(&data, 0);
+ asn1_write_BOOLEAN2(&data, False);
+ asn1_push_tag(&data, ASN1_CONTEXT(0));
+
+ asn1_push_tag(&data, ASN1_CONTEXT(3));
+ asn1_write_OctetString(&data, "DnsDomain", 9);
+ asn1_write_OctetString(&data, domain, strlen(domain));
+ asn1_pop_tag(&data);
+
+ asn1_push_tag(&data, ASN1_CONTEXT(3));
+ asn1_write_OctetString(&data, "Host", 4);
+ asn1_write_OctetString(&data, hostname, strlen(hostname));
+ asn1_pop_tag(&data);
+
+#ifdef CLDAP_USER_QUERY
+ asn1_push_tag(&data, ASN1_CONTEXT(3));
+ asn1_write_OctetString(&data, "User", 4);
+ asn1_write_OctetString(&data, "SAMBA$", 6);
+ asn1_pop_tag(&data);
+
+ asn1_push_tag(&data, ASN1_CONTEXT(3));
+ asn1_write_OctetString(&data, "AAC", 4);
+ asn1_write_OctetString(&data, aac, 4);
+ asn1_pop_tag(&data);
+#endif
+
+ asn1_push_tag(&data, ASN1_CONTEXT(3));
+ asn1_write_OctetString(&data, "NtVer", 5);
+ asn1_write_OctetString(&data, ntver, 4);
+ asn1_pop_tag(&data);
+
+ asn1_pop_tag(&data);
+
+ asn1_push_tag(&data,ASN1_SEQUENCE(0));
+ asn1_write_OctetString(&data, "NetLogon", 8);
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ if (data.has_error) {
+ d_printf("Failed to build cldap netlogon at offset %d\n", (int)data.ofs);
+ asn1_free(&data);
+ return -1;
+ }
+
+ if (write(sock, data.data, data.length) != (ssize_t)data.length) {
+ d_printf("failed to send cldap query (%s)\n", strerror(errno));
+ }
+
+ asn1_free(&data);
+
+ return 0;
+}
+
+
+/*
+ receive a cldap netlogon reply
+*/
+static int recv_cldap_netlogon(int sock, struct cldap_netlogon_reply *reply)
+{
+ int ret;
+ ASN1_DATA data;
+ DATA_BLOB blob;
+ DATA_BLOB os1, os2, os3;
+ uint32 i1;
+ char *p;
+
+ blob = data_blob(NULL, 8192);
+
+ ret = read(sock, blob.data, blob.length);
+
+ if (ret <= 0) {
+ d_printf("no reply received to cldap netlogon\n");
+ return -1;
+ }
+ blob.length = ret;
+
+ asn1_load(&data, blob);
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+ asn1_read_Integer(&data, &i1);
+ asn1_start_tag(&data, ASN1_APPLICATION(4));
+ asn1_read_OctetString(&data, &os1);
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+ asn1_read_OctetString(&data, &os2);
+ asn1_start_tag(&data, ASN1_SET);
+ asn1_read_OctetString(&data, &os3);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ if (data.has_error) {
+ d_printf("Failed to parse cldap reply\n");
+ return -1;
+ }
+
+ p = (char *)os3.data;
+
+ reply->type = IVAL(p, 0); p += 4;
+ reply->flags = IVAL(p, 0); p += 4;
+
+ memcpy(&reply->guid.info, p, UUID_FLAT_SIZE);
+ p += UUID_FLAT_SIZE;
+
+ p += pull_netlogon_string(reply->forest, p, (const char *)os3.data);
+ p += pull_netlogon_string(reply->domain, p, (const char *)os3.data);
+ p += pull_netlogon_string(reply->hostname, p, (const char *)os3.data);
+ p += pull_netlogon_string(reply->netbios_domain, p, (const char *)os3.data);
+ p += pull_netlogon_string(reply->netbios_hostname, p, (const char *)os3.data);
+ p += pull_netlogon_string(reply->unk, p, (const char *)os3.data);
+
+ if (reply->type == SAMLOGON_AD_R) {
+ p += pull_netlogon_string(reply->user_name, p, (const char *)os3.data);
+ } else {
+ *reply->user_name = 0;
+ }
+
+ p += pull_netlogon_string(reply->site_name, p, (const char *)os3.data);
+ p += pull_netlogon_string(reply->site_name_2, p, (const char *)os3.data);
+
+ reply->version = IVAL(p, 0);
+ reply->lmnt_token = SVAL(p, 4);
+ reply->lm20_token = SVAL(p, 6);
+
+ data_blob_free(&os1);
+ data_blob_free(&os2);
+ data_blob_free(&os3);
+ data_blob_free(&blob);
+
+ return 0;
+}
+
+/*
+ do a cldap netlogon query
+*/
+int ads_cldap_netlogon(ADS_STRUCT *ads)
+{
+ int sock;
+ int ret;
+ struct cldap_netlogon_reply reply;
+
+ sock = open_udp_socket(inet_ntoa(ads->ldap_ip), ads->ldap_port);
+ if (sock == -1) {
+ d_printf("Failed to open udp socket to %s:%u\n",
+ inet_ntoa(ads->ldap_ip),
+ ads->ldap_port);
+ return -1;
+
+ }
+
+ ret = send_cldap_netlogon(sock, ads->config.realm, global_myname(), 6);
+ if (ret != 0) {
+ return ret;
+ }
+ ret = recv_cldap_netlogon(sock, &reply);
+ close(sock);
+
+ if (ret == -1) {
+ return -1;
+ }
+
+ d_printf("Information for Domain Controller: %s\n\n",
+ ads->config.ldap_server_name);
+
+ d_printf("Response Type: ");
+ switch (reply.type) {
+ case SAMLOGON_AD_UNK_R:
+ d_printf("SAMLOGON\n");
+ break;
+ case SAMLOGON_AD_R:
+ d_printf("SAMLOGON_USER\n");
+ break;
+ default:
+ d_printf("0x%x\n", reply.type);
+ break;
+ }
+ d_printf("GUID: %s\n",
+ smb_uuid_string_static(smb_uuid_unpack_static(reply.guid)));
+ d_printf("Flags:\n"
+ "\tIs a PDC: %s\n"
+ "\tIs a GC of the forest: %s\n"
+ "\tIs an LDAP server: %s\n"
+ "\tSupports DS: %s\n"
+ "\tIs running a KDC: %s\n"
+ "\tIs running time services: %s\n"
+ "\tIs the closest DC: %s\n"
+ "\tIs writable: %s\n"
+ "\tHas a hardware clock: %s\n"
+ "\tIs a non-domain NC serviced by LDAP server: %s\n",
+ (reply.flags & ADS_PDC) ? "yes" : "no",
+ (reply.flags & ADS_GC) ? "yes" : "no",
+ (reply.flags & ADS_LDAP) ? "yes" : "no",
+ (reply.flags & ADS_DS) ? "yes" : "no",
+ (reply.flags & ADS_KDC) ? "yes" : "no",
+ (reply.flags & ADS_TIMESERV) ? "yes" : "no",
+ (reply.flags & ADS_CLOSEST) ? "yes" : "no",
+ (reply.flags & ADS_WRITABLE) ? "yes" : "no",
+ (reply.flags & ADS_GOOD_TIMESERV) ? "yes" : "no",
+ (reply.flags & ADS_NDNC) ? "yes" : "no");
+
+ printf("Forest:\t\t\t%s\n", reply.forest);
+ printf("Domain:\t\t\t%s\n", reply.domain);
+ printf("Domain Controller:\t%s\n", reply.hostname);
+
+ printf("Pre-Win2k Domain:\t%s\n", reply.netbios_domain);
+ printf("Pre-Win2k Hostname:\t%s\n", reply.netbios_hostname);
+
+ if (*reply.unk) printf("Unk:\t\t\t%s\n", reply.unk);
+ if (*reply.user_name) printf("User name:\t%s\n", reply.user_name);
+
+ printf("Site Name:\t\t%s\n", reply.site_name);
+ printf("Site Name (2):\t\t%s\n", reply.site_name_2);
+
+ d_printf("NT Version: %d\n", reply.version);
+ d_printf("LMNT Token: %.2x\n", reply.lmnt_token);
+ d_printf("LM20 Token: %.2x\n", reply.lm20_token);
+
+ return ret;
+}
+
+
+#endif
diff --git a/source/utils/net_cache.c b/source/utils/net_cache.c
new file mode 100644
index 00000000000..a9559164587
--- /dev/null
+++ b/source/utils/net_cache.c
@@ -0,0 +1,348 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ Copyright (C) Rafal Szczesniak 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "includes.h"
+#include "net.h"
+
+/**
+ * @file net_cache.c
+ * @brief This is part of the net tool which is basically command
+ * line wrapper for gencache.c functions (mainly for testing)
+ *
+ **/
+
+
+/*
+ * These routines are used via gencache_iterate() to display the cache's contents
+ * (print_cache_entry) and to flush it (delete_cache_entry).
+ * Both of them are defined by first arg of gencache_iterate() routine.
+ */
+static void print_cache_entry(const char* keystr, const char* datastr,
+ const time_t timeout, void* dptr)
+{
+ char* timeout_str;
+ time_t now_t = time(NULL);
+ struct tm timeout_tm, *now_tm;
+ /* localtime returns statically allocated pointer, so timeout_tm
+ has to be copied somewhere else */
+ memcpy(&timeout_tm, localtime(&timeout), sizeof(struct tm));
+ now_tm = localtime(&now_t);
+
+ /* form up timeout string depending whether it's today's date or not */
+ if (timeout_tm.tm_year != now_tm->tm_year ||
+ timeout_tm.tm_mon != now_tm->tm_mon ||
+ timeout_tm.tm_mday != now_tm->tm_mday) {
+
+ timeout_str = asctime(&timeout_tm);
+ timeout_str[strlen(timeout_str) - 1] = '\0'; /* remove tailing CR */
+ } else
+ asprintf(&timeout_str, "%.2d:%.2d:%.2d", timeout_tm.tm_hour,
+ timeout_tm.tm_min, timeout_tm.tm_sec);
+
+ d_printf("Key: %s\t Timeout: %s\t Value: %s %s\n", keystr,
+ timeout_str, datastr, timeout > now_t ? "": "(expired)");
+}
+
+static void delete_cache_entry(const char* keystr, const char* datastr,
+ const time_t timeout, void* dptr)
+{
+ if (!gencache_del(keystr))
+ d_printf("Couldn't delete entry! key = %s\n", keystr);
+}
+
+
+/**
+ * Parse text representation of timeout value
+ *
+ * @param timeout_str string containing text representation of the timeout
+ * @return numeric timeout of time_t type
+ **/
+static time_t parse_timeout(const char* timeout_str)
+{
+ char sign = '\0', *number = NULL, unit = '\0';
+ int len, number_begin, number_end;
+ time_t timeout;
+
+ /* sign detection */
+ if (timeout_str[0] == '!' || timeout_str[0] == '+') {
+ sign = timeout_str[0];
+ number_begin = 1;
+ } else {
+ number_begin = 0;
+ }
+
+ /* unit detection */
+ len = strlen(timeout_str);
+ switch (timeout_str[len - 1]) {
+ case 's':
+ case 'm':
+ case 'h':
+ case 'd':
+ case 'w': unit = timeout_str[len - 1];
+ }
+
+ /* number detection */
+ len = (sign) ? strlen(&timeout_str[number_begin]) : len;
+ number_end = (unit) ? len - 1 : len;
+ number = strndup(&timeout_str[number_begin], number_end);
+
+ /* calculate actual timeout value */
+ timeout = (time_t)atoi(number);
+
+ switch (unit) {
+ case 'm': timeout *= 60; break;
+ case 'h': timeout *= 60*60; break;
+ case 'd': timeout *= 60*60*24; break;
+ case 'w': timeout *= 60*60*24*7; break; /* that's fair enough, I think :) */
+ }
+
+ switch (sign) {
+ case '!': timeout = time(NULL) - timeout; break;
+ case '+':
+ default: timeout += time(NULL); break;
+ }
+
+ if (number) SAFE_FREE(number);
+ return timeout;
+}
+
+
+/**
+ * Add an entry to the cache. If it does exist, then set it.
+ *
+ * @param argv key, value and timeout are passed in command line
+ * @return 0 on success, otherwise failure
+ **/
+static int net_cache_add(int argc, const char **argv)
+{
+ const char *keystr, *datastr, *timeout_str;
+ time_t timeout;
+
+ if (argc < 3) {
+ d_printf("\nUsage: net cache add <key string> <data string> <timeout>\n");
+ return -1;
+ }
+
+ keystr = argv[0];
+ datastr = argv[1];
+ timeout_str = argv[2];
+
+ /* parse timeout given in command line */
+ timeout = parse_timeout(timeout_str);
+ if (!timeout) {
+ d_printf("Invalid timeout argument.\n");
+ return -1;
+ }
+
+ if (gencache_set(keystr, datastr, timeout)) {
+ d_printf("New cache entry stored successfully.\n");
+ gencache_shutdown();
+ return 0;
+ }
+
+ d_printf("Entry couldn't be added. Perhaps there's already such a key.\n");
+ gencache_shutdown();
+ return -1;
+}
+
+
+/**
+ * Set new value of an existing entry in the cache. Fail If the entry doesn't
+ * exist.
+ *
+ * @param argv key being searched and new value and timeout to set in the entry
+ * @return 0 on success, otherwise failure
+ **/
+static int net_cache_set(int argc, const char **argv)
+{
+ const char *keystr, *datastr, *timeout_str;
+ time_t timeout;
+
+ if (argc < 3) {
+ d_printf("\nUsage: net cache set <key string> <data string> <timeout>\n");
+ return -1;
+ }
+
+ keystr = argv[0];
+ datastr = argv[1];
+ timeout_str = argv[2];
+
+ /* parse timeout given in command line */
+ timeout = parse_timeout(timeout_str);
+ if (!timeout) {
+ d_printf("Invalid timeout argument.\n");
+ return -1;
+ }
+
+ if (gencache_set_only(keystr, datastr, timeout)) {
+ d_printf("Cache entry set successfully.\n");
+ gencache_shutdown();
+ return 0;
+ }
+
+ d_printf("Entry couldn't be set. Perhaps there's no such a key.\n");
+ gencache_shutdown();
+ return -1;
+}
+
+
+/**
+ * Delete an entry in the cache
+ *
+ * @param argv key to delete an entry of
+ * @return 0 on success, otherwise failure
+ **/
+static int net_cache_del(int argc, const char **argv)
+{
+ const char *keystr = argv[0];
+
+ if (argc < 1) {
+ d_printf("\nUsage: net cache del <key string>\n");
+ return -1;
+ }
+
+ if(gencache_del(keystr)) {
+ d_printf("Entry deleted.\n");
+ return 0;
+ }
+
+ d_printf("Couldn't delete specified entry\n");
+ return -1;
+}
+
+
+/**
+ * Get and display an entry from the cache
+ *
+ * @param argv key to search an entry of
+ * @return 0 on success, otherwise failure
+ **/
+static int net_cache_get(int argc, const char **argv)
+{
+ const char* keystr = argv[0];
+ char* valuestr;
+ time_t timeout;
+
+ if (argc < 1) {
+ d_printf("\nUsage: net cache get <key>\n");
+ return -1;
+ }
+
+ if (gencache_get(keystr, &valuestr, &timeout)) {
+ print_cache_entry(keystr, valuestr, timeout, NULL);
+ return 0;
+ }
+
+ d_printf("Failed to find entry\n");
+ return -1;
+}
+
+
+/**
+ * Search an entry/entries in the cache
+ *
+ * @param argv key pattern to match the entries to
+ * @return 0 on success, otherwise failure
+ **/
+static int net_cache_search(int argc, const char **argv)
+{
+ const char* pattern;
+
+ if (argc < 1) {
+ d_printf("Usage: net cache search <pattern>\n");
+ return -1;
+ }
+
+ pattern = argv[0];
+ gencache_iterate(print_cache_entry, NULL, pattern);
+ return 0;
+}
+
+
+/**
+ * List the contents of the cache
+ *
+ * @param argv ignored in this functionailty
+ * @return always returns 0
+ **/
+static int net_cache_list(int argc, const char **argv)
+{
+ const char* pattern = "*";
+ gencache_iterate(print_cache_entry, NULL, pattern);
+ gencache_shutdown();
+ return 0;
+}
+
+
+/**
+ * Flush the whole cache
+ *
+ * @param argv ignored in this functionality
+ * @return always returns 0
+ **/
+static int net_cache_flush(int argc, const char **argv)
+{
+ const char* pattern = "*";
+ gencache_iterate(delete_cache_entry, NULL, pattern);
+ gencache_shutdown();
+ return 0;
+}
+
+
+/**
+ * Short help
+ *
+ * @param argv ignored in this functionality
+ * @return always returns -1
+ **/
+static int net_cache_usage(int argc, const char **argv)
+{
+ d_printf(" net cache add \t add add new cache entry\n");
+ d_printf(" net cache set \t set new value for existing cache entry\n");
+ d_printf(" net cache del \t delete existing cache entry by key\n");
+ d_printf(" net cache flush \t delete all entries existing in the cache\n");
+ d_printf(" net cache get \t get cache entry by key\n");
+ d_printf(" net cache search \t search for entries in the cache, by given pattern\n");
+ d_printf(" net cache list \t list all cache entries (just like search for \"*\")\n");
+ return -1;
+}
+
+
+/**
+ * Entry point to 'net cache' subfunctionality
+ *
+ * @param argv arguments passed to further called functions
+ * @return whatever further functions return
+ **/
+int net_cache(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"add", net_cache_add},
+ {"set", net_cache_set},
+ {"del", net_cache_del},
+ {"get", net_cache_get},
+ {"search", net_cache_search},
+ {"list", net_cache_list},
+ {"flush", net_cache_flush},
+ {NULL, NULL}
+ };
+
+ return net_run_function(argc, argv, func, net_cache_usage);
+}
diff --git a/source/utils/net_groupmap.c b/source/utils/net_groupmap.c
new file mode 100644
index 00000000000..78e763e1818
--- /dev/null
+++ b/source/utils/net_groupmap.c
@@ -0,0 +1,767 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-2000,
+ * Copyright (C) Jean François Micouleau 1998-2001.
+ * Copyright (C) Gerald Carter 2003.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include "includes.h"
+#include "../utils/net.h"
+
+
+/*********************************************************
+ utility function to parse an integer parameter from
+ "parameter = value"
+**********************************************************/
+static uint32 get_int_param( const char* param )
+{
+ char *p;
+
+ p = strchr( param, '=' );
+ if ( !p )
+ return 0;
+
+ return atoi(p+1);
+}
+
+/*********************************************************
+ utility function to parse an integer parameter from
+ "parameter = value"
+**********************************************************/
+static char* get_string_param( const char* param )
+{
+ char *p;
+
+ p = strchr( param, '=' );
+ if ( !p )
+ return NULL;
+
+ return (p+1);
+}
+
+/*********************************************************
+ Figure out if the input was an NT group or a SID string.
+ Return the SID.
+**********************************************************/
+static BOOL get_sid_from_input(DOM_SID *sid, char *input)
+{
+ GROUP_MAP map;
+
+ if (StrnCaseCmp( input, "S-", 2)) {
+ /* Perhaps its the NT group name? */
+ if (!pdb_getgrnam(&map, input)) {
+ printf("NT Group %s doesn't exist in mapping DB\n", input);
+ return False;
+ } else {
+ *sid = map.sid;
+ }
+ } else {
+ if (!string_to_sid(sid, input)) {
+ printf("converting sid %s from a string failed!\n", input);
+ return False;
+ }
+ }
+ return True;
+}
+
+/*********************************************************
+ Dump a GROUP_MAP entry to stdout (long or short listing)
+**********************************************************/
+
+static void print_map_entry ( GROUP_MAP map, BOOL long_list )
+{
+ fstring string_sid;
+ fstring group_type;
+
+ decode_sid_name_use(group_type, map.sid_name_use);
+ sid_to_string(string_sid, &map.sid);
+
+ if (!long_list)
+ d_printf("%s (%s) -> %s\n", map.nt_name, string_sid, gidtoname(map.gid));
+ else {
+ d_printf("%s\n", map.nt_name);
+ d_printf("\tSID : %s\n", string_sid);
+ d_printf("\tUnix group: %s\n", gidtoname(map.gid));
+ d_printf("\tGroup type: %s\n", group_type);
+ d_printf("\tComment : %s\n", map.comment);
+ }
+
+}
+/*********************************************************
+ List the groups.
+**********************************************************/
+static int net_groupmap_list(int argc, const char **argv)
+{
+ int entries;
+ BOOL long_list = False;
+ int i;
+ fstring ntgroup = "";
+ fstring sid_string = "";
+
+ /* get the options */
+ for ( i=0; i<argc; i++ ) {
+ if ( !StrCaseCmp(argv[i], "verbose")) {
+ long_list = True;
+ }
+ else if ( !StrnCaseCmp(argv[i], "ntgroup", strlen("ntgroup")) ) {
+ fstrcpy( ntgroup, get_string_param( argv[i] ) );
+ if ( !ntgroup[0] ) {
+ d_printf("must supply a name\n");
+ return -1;
+ }
+ }
+ else if ( !StrnCaseCmp(argv[i], "sid", strlen("sid")) ) {
+ fstrcpy( sid_string, get_string_param( argv[i] ) );
+ if ( !sid_string[0] ) {
+ d_printf("must supply a SID\n");
+ return -1;
+ }
+ }
+ else {
+ d_printf("Bad option: %s\n", argv[i]);
+ return -1;
+ }
+ }
+
+ /* list a single group is given a name */
+ if ( ntgroup[0] || sid_string[0] ) {
+ DOM_SID sid;
+ GROUP_MAP map;
+
+ if ( sid_string[0] )
+ fstrcpy( ntgroup, sid_string);
+
+ if (!get_sid_from_input(&sid, ntgroup)) {
+ return -1;
+ }
+
+ /* Get the current mapping from the database */
+ if(!pdb_getgrsid(&map, sid)) {
+ d_printf("Failure to local group SID in the database\n");
+ return -1;
+ }
+
+ print_map_entry( map, long_list );
+ }
+ else {
+ GROUP_MAP *map=NULL;
+ /* enumerate all group mappings */
+ if (!pdb_enum_group_mapping(SID_NAME_UNKNOWN, &map, &entries, ENUM_ALL_MAPPED))
+ return -1;
+
+ for (i=0; i<entries; i++) {
+ print_map_entry( map[i], long_list );
+ }
+
+ SAFE_FREE(map);
+ }
+
+ return 0;
+}
+
+/*********************************************************
+ Add a new group mapping entry
+**********************************************************/
+
+static int net_groupmap_add(int argc, const char **argv)
+{
+ DOM_SID sid;
+ fstring ntgroup = "";
+ fstring unixgrp = "";
+ fstring string_sid = "";
+ fstring type = "";
+ fstring ntcomment = "";
+ enum SID_NAME_USE sid_type = SID_NAME_DOM_GRP;
+ uint32 rid = 0;
+ gid_t gid;
+ int i;
+
+ /* get the options */
+ for ( i=0; i<argc; i++ ) {
+ if ( !StrnCaseCmp(argv[i], "rid", strlen("rid")) ) {
+ rid = get_int_param(argv[i]);
+ if ( rid < DOMAIN_GROUP_RID_ADMINS ) {
+ d_printf("RID must be greater than %d\n", (uint32)DOMAIN_GROUP_RID_ADMINS-1);
+ return -1;
+ }
+ }
+ else if ( !StrnCaseCmp(argv[i], "unixgroup", strlen("unixgroup")) ) {
+ fstrcpy( unixgrp, get_string_param( argv[i] ) );
+ if ( !unixgrp[0] ) {
+ d_printf("must supply a name\n");
+ return -1;
+ }
+ }
+ else if ( !StrnCaseCmp(argv[i], "ntgroup", strlen("ntgroup")) ) {
+ fstrcpy( ntgroup, get_string_param( argv[i] ) );
+ if ( !ntgroup[0] ) {
+ d_printf("must supply a name\n");
+ return -1;
+ }
+ }
+ else if ( !StrnCaseCmp(argv[i], "sid", strlen("sid")) ) {
+ fstrcpy( string_sid, get_string_param( argv[i] ) );
+ if ( !string_sid[0] ) {
+ d_printf("must supply a SID\n");
+ return -1;
+ }
+ }
+ else if ( !StrnCaseCmp(argv[i], "comment", strlen("comment")) ) {
+ fstrcpy( ntcomment, get_string_param( argv[i] ) );
+ if ( !ntcomment[0] ) {
+ d_printf("must supply a comment string\n");
+ return -1;
+ }
+ }
+ else if ( !StrnCaseCmp(argv[i], "type", strlen("type")) ) {
+ fstrcpy( type, get_string_param( argv[i] ) );
+ switch ( type[0] ) {
+ case 'b':
+ case 'B':
+ sid_type = SID_NAME_WKN_GRP;
+ break;
+ case 'd':
+ case 'D':
+ sid_type = SID_NAME_DOM_GRP;
+ break;
+ case 'l':
+ case 'L':
+ sid_type = SID_NAME_ALIAS;
+ break;
+ }
+ }
+ else {
+ d_printf("Bad option: %s\n", argv[i]);
+ return -1;
+ }
+ }
+
+ if ( !unixgrp[0] ) {
+ d_printf("Usage: net groupmap add {rid=<int>|sid=<string>} unixgroup=<string> [type=<domain|local|builtin>] [ntgroup=<string>] [comment=<string>]\n");
+ return -1;
+ }
+
+ if ( (gid = nametogid(unixgrp)) == (gid_t)-1 ) {
+ d_printf("Can't lookup UNIX group %s\n", unixgrp);
+ return -1;
+ }
+
+ if ( (rid == 0) && (string_sid[0] == '\0') ) {
+ d_printf("No rid or sid specified, choosing algorithmic mapping\n");
+ rid = pdb_gid_to_group_rid(gid);
+ }
+
+ /* append the rid to our own domain/machine SID if we don't have a full SID */
+ if ( !string_sid[0] ) {
+ sid_copy(&sid, get_global_sam_sid());
+ sid_append_rid(&sid, rid);
+ sid_to_string(string_sid, &sid);
+ }
+
+ if (!ntcomment[0])
+ fstrcpy(ntcomment, "Local Unix group");
+
+ if (!ntgroup[0] )
+ fstrcpy( ntgroup, unixgrp );
+
+
+ if (!add_initial_entry(gid, string_sid, sid_type, ntgroup, ntcomment)) {
+ d_printf("adding entry for group %s failed!\n", ntgroup);
+ return -1;
+ }
+
+ d_printf("Successully added group %s to the mapping db\n", ntgroup);
+ return 0;
+}
+
+static int net_groupmap_modify(int argc, const char **argv)
+{
+ DOM_SID sid;
+ GROUP_MAP map;
+ fstring ntcomment = "";
+ fstring type = "";
+ fstring ntgroup = "";
+ fstring unixgrp = "";
+ fstring sid_string = "";
+ enum SID_NAME_USE sid_type = SID_NAME_UNKNOWN;
+ int i;
+ gid_t gid;
+
+ /* get the options */
+ for ( i=0; i<argc; i++ ) {
+ if ( !StrnCaseCmp(argv[i], "ntgroup", strlen("ntgroup")) ) {
+ fstrcpy( ntgroup, get_string_param( argv[i] ) );
+ if ( !ntgroup[0] ) {
+ d_printf("must supply a name\n");
+ return -1;
+ }
+ }
+ else if ( !StrnCaseCmp(argv[i], "sid", strlen("sid")) ) {
+ fstrcpy( sid_string, get_string_param( argv[i] ) );
+ if ( !sid_string[0] ) {
+ d_printf("must supply a name\n");
+ return -1;
+ }
+ }
+ else if ( !StrnCaseCmp(argv[i], "comment", strlen("comment")) ) {
+ fstrcpy( ntcomment, get_string_param( argv[i] ) );
+ if ( !ntcomment[0] ) {
+ d_printf("must supply a comment string\n");
+ return -1;
+ }
+ }
+ else if ( !StrnCaseCmp(argv[i], "unixgroup", strlen("unixgroup")) ) {
+ fstrcpy( unixgrp, get_string_param( argv[i] ) );
+ if ( !unixgrp[0] ) {
+ d_printf("must supply a group name\n");
+ return -1;
+ }
+ }
+ else if ( !StrnCaseCmp(argv[i], "type", strlen("type")) ) {
+ fstrcpy( type, get_string_param( argv[i] ) );
+ switch ( type[0] ) {
+ case 'd':
+ case 'D':
+ sid_type = SID_NAME_DOM_GRP;
+ break;
+ case 'l':
+ case 'L':
+ sid_type = SID_NAME_ALIAS;
+ break;
+ }
+ }
+ else {
+ d_printf("Bad option: %s\n", argv[i]);
+ return -1;
+ }
+ }
+
+ if ( !ntgroup[0] && !sid_string[0] ) {
+ d_printf("Usage: net groupmap modify {ntgroup=<string>|sid=<SID>} [comment=<string>] [unixgroup=<string>] [type=<domain|local>]\n");
+ return -1;
+ }
+
+ /* give preference to the SID; if both the ntgroup name and SID
+ are defined, use the SID and assume that the group name could be a
+ new name */
+
+ if ( sid_string[0] ) {
+ if (!get_sid_from_input(&sid, sid_string)) {
+ return -1;
+ }
+ }
+ else {
+ if (!get_sid_from_input(&sid, ntgroup)) {
+ return -1;
+ }
+ }
+
+ /* Get the current mapping from the database */
+ if(!pdb_getgrsid(&map, sid)) {
+ d_printf("Failure to local group SID in the database\n");
+ return -1;
+ }
+
+ /*
+ * Allow changing of group type only between domain and local
+ * We disallow changing Builtin groups !!! (SID problem)
+ */
+ if (sid_type != SID_NAME_UNKNOWN) {
+ if (map.sid_name_use == SID_NAME_WKN_GRP) {
+ d_printf("You can only change between domain and local groups.\n");
+ return -1;
+ }
+
+ map.sid_name_use=sid_type;
+ }
+
+ /* Change comment if new one */
+ if ( ntcomment[0] )
+ fstrcpy( map.comment, ntcomment );
+
+ if ( ntgroup[0] )
+ fstrcpy( map.nt_name, ntgroup );
+
+ if ( unixgrp[0] ) {
+ gid = nametogid( unixgrp );
+ if ( gid == -1 ) {
+ d_printf("Unable to lookup UNIX group %s. Make sure the group exists.\n",
+ unixgrp);
+ return -1;
+ }
+
+ map.gid = gid;
+ }
+
+ if ( !pdb_update_group_mapping_entry(&map) ) {
+ d_printf("Could not update group database\n");
+ return -1;
+ }
+
+ d_printf("Updated mapping entry for %s\n", map.nt_name);
+
+ return 0;
+}
+
+static int net_groupmap_delete(int argc, const char **argv)
+{
+ DOM_SID sid;
+ fstring ntgroup = "";
+ fstring sid_string = "";
+ int i;
+
+ /* get the options */
+ for ( i=0; i<argc; i++ ) {
+ if ( !StrnCaseCmp(argv[i], "ntgroup", strlen("ntgroup")) ) {
+ fstrcpy( ntgroup, get_string_param( argv[i] ) );
+ if ( !ntgroup[0] ) {
+ d_printf("must supply a name\n");
+ return -1;
+ }
+ }
+ else if ( !StrnCaseCmp(argv[i], "sid", strlen("sid")) ) {
+ fstrcpy( sid_string, get_string_param( argv[i] ) );
+ if ( !sid_string[0] ) {
+ d_printf("must supply a SID\n");
+ return -1;
+ }
+ }
+ else {
+ d_printf("Bad option: %s\n", argv[i]);
+ return -1;
+ }
+ }
+
+ if ( !ntgroup[0] && !sid_string[0]) {
+ d_printf("Usage: net groupmap delete {ntgroup=<string>|sid=<SID>}\n");
+ return -1;
+ }
+
+ /* give preference to the SID if we have that */
+
+ if ( sid_string[0] )
+ fstrcpy( ntgroup, sid_string );
+
+ if ( !get_sid_from_input(&sid, ntgroup) ) {
+ d_printf("Unable to resolve group %s to a SID\n", ntgroup);
+ return -1;
+ }
+
+ if ( !pdb_delete_group_mapping_entry(sid) ) {
+ printf("Failed to removing group %s from the mapping db!\n", ntgroup);
+ return -1;
+ }
+
+ d_printf("Sucessfully removed %s from the mapping db\n", ntgroup);
+
+ return 0;
+}
+
+static int net_groupmap_set(int argc, const char **argv)
+{
+ const char *ntgroup = NULL;
+ struct group *grp = NULL;
+ GROUP_MAP map;
+ BOOL have_map = False;
+
+ if ((argc < 1) || (argc > 2)) {
+ d_printf("Usage: net groupmap set \"NT Group\" "
+ "[\"unix group\"] [-C \"comment\"] [-L] [-D]\n");
+ return -1;
+ }
+
+ if ( opt_localgroup && opt_domaingroup ) {
+ d_printf("Can only specify -L or -D, not both\n");
+ return -1;
+ }
+
+ ntgroup = argv[0];
+
+ if (argc == 2) {
+ grp = getgrnam(argv[1]);
+
+ if (grp == NULL) {
+ d_printf("Could not find unix group %s\n", argv[1]);
+ return -1;
+ }
+ }
+
+ have_map = pdb_getgrnam(&map, ntgroup);
+
+ if (!have_map) {
+ DOM_SID sid;
+ have_map = ( (strncmp(ntgroup, "S-", 2) == 0) &&
+ string_to_sid(&sid, ntgroup) &&
+ pdb_getgrsid(&map, sid) );
+ }
+
+ if (!have_map) {
+
+ /* Ok, add it */
+
+ if (grp == NULL) {
+ d_printf("Could not find group mapping for %s\n",
+ ntgroup);
+ return -1;
+ }
+
+ map.gid = grp->gr_gid;
+
+ if (opt_rid == 0) {
+ opt_rid = pdb_gid_to_group_rid(map.gid);
+ }
+
+ sid_copy(&map.sid, get_global_sam_sid());
+ sid_append_rid(&map.sid, opt_rid);
+
+ map.sid_name_use = SID_NAME_DOM_GRP;
+ fstrcpy(map.nt_name, ntgroup);
+ fstrcpy(map.comment, "");
+
+ if (!pdb_add_group_mapping_entry(&map)) {
+ d_printf("Could not add mapping entry for %s\n",
+ ntgroup);
+ return -1;
+ }
+ }
+
+ /* Now we have a mapping entry, update that stuff */
+
+ if ( opt_localgroup || opt_domaingroup ) {
+ if (map.sid_name_use == SID_NAME_WKN_GRP) {
+ d_printf("Can't change type of the BUILTIN group %s\n",
+ map.nt_name);
+ return -1;
+ }
+ }
+
+ if (opt_localgroup)
+ map.sid_name_use = SID_NAME_ALIAS;
+
+ if (opt_domaingroup)
+ map.sid_name_use = SID_NAME_DOM_GRP;
+
+ /* The case (opt_domaingroup && opt_localgroup) was tested for above */
+
+ if (strlen(opt_comment) > 0)
+ fstrcpy(map.comment, opt_comment);
+
+ if (strlen(opt_newntname) > 0)
+ fstrcpy(map.nt_name, opt_newntname);
+
+ if (grp != NULL)
+ map.gid = grp->gr_gid;
+
+ if (!pdb_update_group_mapping_entry(&map)) {
+ d_printf("Could not update group mapping for %s\n", ntgroup);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int net_groupmap_cleanup(int argc, const char **argv)
+{
+ GROUP_MAP *map = NULL;
+ int i, entries;
+
+ if (!pdb_enum_group_mapping(SID_NAME_UNKNOWN, &map, &entries,
+ ENUM_ALL_MAPPED)) {
+ d_printf("Could not list group mappings\n");
+ return -1;
+ }
+
+ for (i=0; i<entries; i++) {
+
+ if (map[i].sid_name_use == SID_NAME_WKN_GRP)
+ continue;
+
+ if (map[i].gid == -1)
+ printf("Group %s is not mapped\n", map[i].nt_name);
+
+ if (!sid_check_is_in_our_domain(&map[i].sid)) {
+ printf("Deleting mapping for NT Group %s, sid %s\n",
+ map[i].nt_name,
+ sid_string_static(&map[i].sid));
+ pdb_delete_group_mapping_entry(map[i].sid);
+ }
+ }
+
+ SAFE_FREE(map);
+
+ return 0;
+}
+
+static int net_groupmap_addmem(int argc, const char **argv)
+{
+ DOM_SID alias, member;
+
+ if ( (argc != 2) ||
+ !string_to_sid(&alias, argv[0]) ||
+ !string_to_sid(&member, argv[1]) ) {
+ d_printf("Usage: net groupmap addmem alias-sid member-sid\n");
+ return -1;
+ }
+
+ if (!pdb_add_aliasmem(&alias, &member)) {
+ d_printf("Could not add sid %s to alias %s\n",
+ argv[1], argv[0]);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int net_groupmap_delmem(int argc, const char **argv)
+{
+ DOM_SID alias, member;
+
+ if ( (argc != 2) ||
+ !string_to_sid(&alias, argv[0]) ||
+ !string_to_sid(&member, argv[1]) ) {
+ d_printf("Usage: net groupmap delmem alias-sid member-sid\n");
+ return -1;
+ }
+
+ if (!pdb_del_aliasmem(&alias, &member)) {
+ d_printf("Could not delete sid %s from alias %s\n",
+ argv[1], argv[0]);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int net_groupmap_listmem(int argc, const char **argv)
+{
+ DOM_SID alias;
+ DOM_SID *members;
+ int i, num;
+ NTSTATUS result;
+
+ if ( (argc != 1) ||
+ !string_to_sid(&alias, argv[0]) ) {
+ d_printf("Usage: net groupmap listmem alias-sid\n");
+ return -1;
+ }
+
+ if (!pdb_enum_aliasmem(&alias, &members, &num)) {
+ d_printf("Could not list members for sid %s: %s\n",
+ argv[0], nt_errstr(result));
+ return -1;
+ }
+
+ for (i = 0; i < num; i++) {
+ printf("%s\n", sid_string_static(&(members[i])));
+ }
+
+ SAFE_FREE(members);
+
+ return 0;
+}
+
+static int net_groupmap_memberships(int argc, const char **argv)
+{
+ DOM_SID member;
+ DOM_SID *aliases;
+ int i, num;
+ NTSTATUS result;
+
+ if ( (argc != 1) ||
+ !string_to_sid(&member, argv[0]) ) {
+ d_printf("Usage: net groupmap memberof sid\n");
+ return -1;
+ }
+
+ if (!pdb_enum_alias_memberships(&member, &aliases, &num)) {
+ d_printf("Could not list memberships for sid %s: %s\n",
+ argv[0], nt_errstr(result));
+ return -1;
+ }
+
+ for (i = 0; i < num; i++) {
+ printf("%s\n", sid_string_static(&(aliases[i])));
+ }
+
+ SAFE_FREE(aliases);
+
+ return 0;
+}
+
+int net_help_groupmap(int argc, const char **argv)
+{
+ d_printf("net groupmap add"\
+ "\n Create a new group mapping\n");
+ d_printf("net groupmap modify"\
+ "\n Update a group mapping\n");
+ d_printf("net groupmap delete"\
+ "\n Remove a group mapping\n");
+ d_printf("net groupmap addmember"\
+ "\n Add a foreign alias member\n");
+ d_printf("net groupmap delmember"\
+ "\n Delete a foreign alias member\n");
+ d_printf("net groupmap listmembers"\
+ "\n List foreign group members\n");
+ d_printf("net groupmap memberships"\
+ "\n List foreign group memberships\n");
+ d_printf("net groupmap list"\
+ "\n List current group map\n");
+ d_printf("net groupmap set"\
+ "\n Set group mapping\n");
+ d_printf("net groupmap cleanup"\
+ "\n Remove foreign group mapping entries\n");
+
+ return -1;
+}
+
+
+/***********************************************************
+ migrated functionality from smbgroupedit
+ **********************************************************/
+int net_groupmap(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"add", net_groupmap_add},
+ {"modify", net_groupmap_modify},
+ {"delete", net_groupmap_delete},
+ {"set", net_groupmap_set},
+ {"cleanup", net_groupmap_cleanup},
+ {"addmem", net_groupmap_addmem},
+ {"delmem", net_groupmap_delmem},
+ {"listmem", net_groupmap_listmem},
+ {"memberships", net_groupmap_memberships},
+ {"list", net_groupmap_list},
+ {"help", net_help_groupmap},
+ {NULL, NULL}
+ };
+
+ /* we shouldn't have silly checks like this */
+#if 0
+ if (getuid() != 0) {
+ d_printf("You must be root to edit group mappings.\nExiting...\n");
+ return -1;
+ }
+#endif
+
+ if ( argc )
+ return net_run_function(argc, argv, func, net_help_groupmap);
+
+ return net_help_groupmap( argc, argv );
+}
+
diff --git a/source/utils/net_help.c b/source/utils/net_help.c
new file mode 100644
index 00000000000..38261be90a7
--- /dev/null
+++ b/source/utils/net_help.c
@@ -0,0 +1,220 @@
+/*
+ Samba Unix/Linux SMB client library
+ net help commands
+ Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "../utils/net.h"
+
+int net_common_methods_usage(int argc, const char**argv)
+{
+ d_printf("Valid methods: (auto-detected if not specified)\n");
+ d_printf("\tads\t\t\t\tActive Directory (LDAP/Kerberos)\n");
+ d_printf("\trpc\t\t\t\tDCE-RPC\n");
+ d_printf("\trap\t\t\t\tRAP (older systems)\n");
+ d_printf("\n");
+ return 0;
+}
+
+int net_common_flags_usage(int argc, const char **argv)
+{
+ d_printf("Valid targets: choose one (none defaults to localhost)\n");
+ d_printf("\t-S or --server=<server>\t\tserver name\n");
+ d_printf("\t-I or --ipaddress=<ipaddr>\taddress of target server\n");
+ d_printf("\t-w or --workgroup=<wg>\t\ttarget workgroup or domain\n");
+
+ d_printf("\n");
+ d_printf("Valid miscellaneous options are:\n"); /* misc options */
+ d_printf("\t-p or --port=<port>\t\tconnection port on target\n");
+ d_printf("\t-W or --myworkgroup=<wg>\tclient workgroup\n");
+ d_printf("\t-d or --debuglevel=<level>\tdebug level (0-10)\n");
+ d_printf("\t-n or --myname=<name>\t\tclient name\n");
+ d_printf("\t-U or --user=<name>\t\tuser name\n");
+ d_printf("\t-s or --configfile=<path>\tpathname of smb.conf file\n");
+ d_printf("\t-l or --long\t\t\tDisplay full information\n");
+ d_printf("\t-V or --version\t\t\tPrint samba version information\n");
+ d_printf("\t-P or --machine-pass\t\tAuthenticate as machine account\n");
+ return -1;
+}
+
+static int help_usage(int argc, const char **argv)
+{
+ d_printf(
+"\n"\
+"Usage: net help <function>\n"\
+"\n"\
+"Valid functions are:\n"\
+" RPC RAP ADS FILE SHARE SESSION SERVER DOMAIN PRINTQ USER GROUP VALIDATE\n"\
+" GROUPMEMBER ADMIN SERVICE PASSWORD TIME LOOKUP GETLOCALSID SETLOCALSID\n"\
+" CHANGESCRETPW\n");
+ return -1;
+}
+
+int net_help_user(int argc, const char **argv)
+{
+ d_printf("\nnet [<method>] user [misc. options] [targets]"\
+ "\n\tList users\n\n");
+ d_printf("net [<method>] user DELETE <name> [misc. options] [targets]"\
+ "\n\tDelete specified user\n");
+ d_printf("\nnet [<method>] user INFO <name> [misc. options] [targets]"\
+ "\n\tList the domain groups of the specified user\n");
+ d_printf("\nnet [<method>] user ADD <name> [password] [-c container] "\
+ "[-F user flags] [misc. options]"\
+ " [targets]\n\tAdd specified user\n");
+
+ net_common_methods_usage(argc, argv);
+ net_common_flags_usage(argc, argv);
+ d_printf("\t-C or --comment=<comment>\tdescriptive comment (for add only)\n");
+ d_printf("\t-c or --container=<container>\tLDAP container, defaults to cn=Users (for add in ADS only)\n");
+ return -1;
+}
+
+int net_help_group(int argc, const char **argv)
+{
+ d_printf("net [<method>] group [misc. options] [targets]"\
+ "\n\tList user groups\n\n");
+ d_printf("net rpc group LIST [global|local|builtin]* [misc. options]"\
+ "\n\tList specific user groups\n\n");
+ d_printf("net [<method>] group DELETE <name> "\
+ "[misc. options] [targets]"\
+ "\n\tDelete specified group\n");
+ d_printf("\nnet [<method>] group ADD <name> [-C comment] [-c container]"\
+ " [misc. options] [targets]\n\tCreate specified group\n");
+ d_printf("\nnet rpc group MEMBERS <name>\n\tList Group Members\n\n");
+ net_common_methods_usage(argc, argv);
+ net_common_flags_usage(argc, argv);
+ d_printf("\t-C or --comment=<comment>\tdescriptive comment (for add only)\n");
+ d_printf("\t-c or --container=<container>\tLDAP container, defaults to cn=Users (for add in ADS only)\n");
+ return -1;
+}
+
+int net_help_join(int argc, const char **argv)
+{
+ d_printf("\nnet [<method>] join [misc. options]\n"
+ "\tjoins this server to a domain\n");
+ d_printf("Valid methods: (auto-detected if not specified)\n");
+ d_printf("\tads\t\t\t\tActive Directory (LDAP/Kerberos)\n");
+ d_printf("\trpc\t\t\t\tDCE-RPC\n");
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+int net_help_share(int argc, const char **argv)
+{
+ d_printf(
+ "\nnet [<method>] share [misc. options] [targets] \n"
+ "\tenumerates all exported resources (network shares) "
+ "on target server\n\n"
+ "net [<method>] share ADD <name=serverpath> [misc. options] [targets]"
+ "\n\tAdds a share from a server (makes the export active)\n\n"
+ "net [<method>] share DELETE <sharename> [misc. options] [targets]\n"
+ "\n\tDeletes a share from a server (makes the export inactive)\n");
+ net_common_methods_usage(argc, argv);
+ net_common_flags_usage(argc, argv);
+ d_printf(
+ "\t-C or --comment=<comment>\tdescriptive comment (for add only)\n"
+ "\t-M or --maxusers=<num>\t\tmax users allowed for share\n");
+ return -1;
+}
+
+int net_help_file(int argc, const char **argv)
+{
+ d_printf("net [<method>] file [misc. options] [targets]\n"\
+ "\tlists all open files on file server\n\n");
+ d_printf("net [<method>] file USER <username> "\
+ "[misc. options] [targets]"\
+ "\n\tlists all files opened by username on file server\n\n");
+ d_printf("net [<method>] file CLOSE <id> [misc. options] [targets]\n"\
+ "\tcloses specified file on target server\n\n");
+ d_printf("net [rap] file INFO <id> [misc. options] [targets]\n"\
+ "\tdisplays information about the specified open file\n");
+
+ net_common_methods_usage(argc, argv);
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+int net_help_status(int argc, const char **argv)
+{
+ d_printf(" net status sessions [parseable] "
+ "Show list of open sessions\n");
+ d_printf(" net status shares [parseable] "
+ "Show list of open shares\n");
+ return -1;
+}
+
+static int net_usage(int argc, const char **argv)
+{
+ d_printf(" net time\t\tto view or set time information\n"\
+ " net lookup\t\tto lookup host name or ip address\n"\
+ " net user\t\tto manage users\n"\
+ " net group\t\tto manage groups\n"\
+ " net groupmap\t\tto manage group mappings\n"\
+ " net join\t\tto join a domain\n"\
+ " net cache\t\tto operate on cache tdb file\n"\
+ " net getlocalsid [NAME]\tto get the SID for local name\n"\
+ " net setlocalsid SID\tto set the local domain SID\n"\
+ " net changesecretpw\tto change the machine password in the local secrets database only\n"\
+ " \tthis requires the -f flag as a safety barrier\n"\
+ " net status\t\tShow server status\n"\
+ "\n"\
+ " net ads <command>\tto run ADS commands\n"\
+ " net rap <command>\tto run RAP (pre-RPC) commands\n"\
+ " net rpc <command>\tto run RPC commands\n"\
+ "\n"\
+ "Type \"net help <option>\" to get more information on that option\n");
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+/*
+ handle "net help *" subcommands
+*/
+int net_help(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"ADS", net_ads_help},
+ {"RAP", net_rap_help},
+ {"RPC", net_rpc_help},
+
+ {"FILE", net_help_file},
+ {"SHARE", net_help_share},
+ {"SESSION", net_rap_session_usage},
+ {"SERVER", net_rap_server_usage},
+ {"DOMAIN", net_rap_domain_usage},
+ {"PRINTQ", net_rap_printq_usage},
+ {"USER", net_help_user},
+ {"GROUP", net_help_group},
+ {"GROUPMAP", net_help_groupmap},
+ {"JOIN", net_help_join},
+ {"VALIDATE", net_rap_validate_usage},
+ {"GROUPMEMBER", net_rap_groupmember_usage},
+ {"ADMIN", net_rap_admin_usage},
+ {"SERVICE", net_rap_service_usage},
+ {"PASSWORD", net_rap_password_usage},
+ {"TIME", net_time_usage},
+ {"LOOKUP", net_lookup_usage},
+#ifdef WITH_FAKE_KASERVER
+ {"AFSKEY", net_afskey_usage},
+#endif
+
+ {"HELP", help_usage},
+ {NULL, NULL}};
+
+ return net_run_function(argc, argv, func, net_usage);
+}
diff --git a/source/utils/net_idmap.c b/source/utils/net_idmap.c
new file mode 100644
index 00000000000..f5b4bf1b4a7
--- /dev/null
+++ b/source/utils/net_idmap.c
@@ -0,0 +1,264 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ Copyright (C) 2003 Andrew Bartlett (abartlet@samba.org)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "includes.h"
+#include "../utils/net.h"
+
+
+/***********************************************************
+ Helper function for net_idmap_dump. Dump one entry.
+ **********************************************************/
+static int net_idmap_dump_one_entry(TDB_CONTEXT *tdb,
+ TDB_DATA key,
+ TDB_DATA data,
+ void *unused)
+{
+ if (strcmp(key.dptr, "USER HWM") == 0) {
+ printf("USER HWM %d\n", IVAL(data.dptr,0));
+ return 0;
+ }
+
+ if (strcmp(key.dptr, "GROUP HWM") == 0) {
+ printf("GROUP HWM %d\n", IVAL(data.dptr,0));
+ return 0;
+ }
+
+ if (strncmp(key.dptr, "S-", 2) != 0)
+ return 0;
+
+ printf("%s %s\n", data.dptr, key.dptr);
+ return 0;
+}
+
+/***********************************************************
+ Dump the current idmap
+ **********************************************************/
+static int net_idmap_dump(int argc, const char **argv)
+{
+ TDB_CONTEXT *idmap_tdb;
+
+ if ( argc != 1 )
+ return net_help_idmap( argc, argv );
+
+ idmap_tdb = tdb_open_log(argv[0], 0, TDB_DEFAULT, O_RDONLY, 0);
+
+ if (idmap_tdb == NULL) {
+ d_printf("Could not open idmap: %s\n", argv[0]);
+ return -1;
+ }
+
+ tdb_traverse(idmap_tdb, net_idmap_dump_one_entry, NULL);
+
+ tdb_close(idmap_tdb);
+
+ return 0;
+}
+
+/***********************************************************
+ Fix up the HWMs after a idmap restore.
+ **********************************************************/
+
+struct hwms {
+ BOOL ok;
+ int user_hwm;
+ int group_hwm;
+};
+
+static int net_idmap_find_max_id(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA data,
+ void *handle)
+{
+ struct hwms *hwms = (struct hwms *)handle;
+ int *idptr = NULL;
+ int id;
+
+ if (strncmp(key.dptr, "S-", 2) != 0)
+ return 0;
+
+ if (sscanf(data.dptr, "GID %d", &id) == 1) {
+ idptr = &hwms->group_hwm;
+ }
+
+ if (sscanf(data.dptr, "UID %d", &id) == 1) {
+ idptr = &hwms->user_hwm;
+ }
+
+ if (idptr == NULL) {
+ d_printf("Illegal idmap entry: [%s]->[%s]\n",
+ key.dptr, data.dptr);
+ hwms->ok = False;
+ return -1;
+ }
+
+ if (*idptr <= id)
+ *idptr = id+1;
+
+ return 0;
+}
+
+static NTSTATUS net_idmap_fixup_hwm(void)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ TDB_CONTEXT *idmap_tdb;
+ char *tdbfile = NULL;
+
+ struct hwms hwms;
+ struct hwms highest;
+
+ if (!lp_idmap_uid(&hwms.user_hwm, &highest.user_hwm) ||
+ !lp_idmap_gid(&hwms.group_hwm, &highest.group_hwm)) {
+ d_printf("idmap range missing\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ tdbfile = strdup(lock_path("winbindd_idmap.tdb"));
+ if (!tdbfile) {
+ DEBUG(0, ("idmap_init: out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ idmap_tdb = tdb_open_log(tdbfile, 0, TDB_DEFAULT, O_RDWR, 0);
+
+ if (idmap_tdb == NULL) {
+ d_printf("Could not open idmap: %s\n", tdbfile);
+ return NT_STATUS_NO_SUCH_FILE;
+ }
+
+ hwms.ok = True;
+
+ tdb_traverse(idmap_tdb, net_idmap_find_max_id, &hwms);
+
+ if (!hwms.ok) {
+ goto done;
+ }
+
+ d_printf("USER HWM: %d GROUP HWM: %d\n",
+ hwms.user_hwm, hwms.group_hwm);
+
+ if (hwms.user_hwm >= highest.user_hwm) {
+ d_printf("Highest UID out of uid range\n");
+ goto done;
+ }
+
+ if (hwms.group_hwm >= highest.group_hwm) {
+ d_printf("Highest GID out of gid range\n");
+ goto done;
+ }
+
+ if ((tdb_store_int32(idmap_tdb, "USER HWM", hwms.user_hwm) != 0) ||
+ (tdb_store_int32(idmap_tdb, "GROUP HWM", hwms.group_hwm) != 0)) {
+ d_printf("Could not store HWMs\n");
+ goto done;
+ }
+
+ result = NT_STATUS_OK;
+ done:
+ tdb_close(idmap_tdb);
+ return result;
+}
+
+/***********************************************************
+ Write entries from stdin to current local idmap
+ **********************************************************/
+static int net_idmap_restore(int argc, const char **argv)
+{
+ if (!idmap_init(lp_idmap_backend())) {
+ d_printf("Could not init idmap\n");
+ return -1;
+ }
+
+ while (!feof(stdin)) {
+ fstring line, sid_string;
+ int len;
+ unid_t id;
+ int type = ID_EMPTY;
+ DOM_SID sid;
+
+ if (fgets(line, sizeof(line)-1, stdin) == NULL)
+ break;
+
+ len = strlen(line);
+
+ if ( (len > 0) && (line[len-1] == '\n') )
+ line[len-1] = '\0';
+
+ /* Yuck - this is broken for sizeof(gid_t) != sizeof(int) */
+
+ if (sscanf(line, "GID %d %s", &id.gid, sid_string) == 2) {
+ type = ID_GROUPID;
+ }
+
+ /* Yuck - this is broken for sizeof(uid_t) != sizeof(int) */
+
+ if (sscanf(line, "UID %d %s", &id.uid, sid_string) == 2) {
+ type = ID_USERID;
+ }
+
+ if (type == ID_EMPTY) {
+ d_printf("ignoring invalid line [%s]\n", line);
+ continue;
+ }
+
+ if (!string_to_sid(&sid, sid_string)) {
+ d_printf("ignoring invalid sid [%s]\n", sid_string);
+ continue;
+ }
+
+ if (!NT_STATUS_IS_OK(idmap_set_mapping(&sid, id, type))) {
+ d_printf("Could not set mapping of %s %lu to sid %s\n",
+ (type == ID_GROUPID) ? "GID" : "UID",
+ (type == ID_GROUPID) ? (unsigned long)id.gid:
+ (unsigned long)id.uid,
+ sid_string_static(&sid));
+ continue;
+ }
+
+ }
+
+ idmap_close();
+
+ return NT_STATUS_IS_OK(net_idmap_fixup_hwm()) ? 0 : -1;
+}
+
+int net_help_idmap(int argc, const char **argv)
+{
+ d_printf("net idmap dump filename"\
+ "\n Dump current id mapping\n");
+
+ d_printf("net idmap restore"\
+ "\n Restore entries from stdin to current local idmap\n");
+
+ return -1;
+}
+
+/***********************************************************
+ Look at the current idmap
+ **********************************************************/
+int net_idmap(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"dump", net_idmap_dump},
+ {"restore", net_idmap_restore},
+ {"help", net_help_idmap},
+ {NULL, NULL}
+ };
+
+ return net_run_function(argc, argv, func, net_help_idmap);
+}
+
+
diff --git a/source/utils/net_lookup.c b/source/utils/net_lookup.c
new file mode 100644
index 00000000000..cef0ea5fbed
--- /dev/null
+++ b/source/utils/net_lookup.c
@@ -0,0 +1,258 @@
+/*
+ Samba Unix/Linux SMB client library
+ net lookup command
+ Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "includes.h"
+#include "../utils/net.h"
+
+int net_lookup_usage(int argc, const char **argv)
+{
+ d_printf(
+" net lookup [host] HOSTNAME[#<type>]\n\tgives IP for a hostname\n\n"
+" net lookup ldap [domain]\n\tgives IP of domain's ldap server\n\n"
+" net lookup kdc [realm]\n\tgives IP of realm's kerberos KDC\n\n"
+" net lookup dc [domain]\n\tgives IP of domains Domain Controllers\n\n"
+" net lookup master [domain|wg]\n\tgive IP of master browser\n\n"
+);
+ return -1;
+}
+
+/* lookup a hostname giving an IP */
+static int net_lookup_host(int argc, const char **argv)
+{
+ struct in_addr ip;
+ int name_type = 0x20;
+ const char *name = argv[0];
+ char *p;
+
+ if (argc == 0)
+ return net_lookup_usage(argc, argv);
+
+ p = strchr_m(name,'#');
+ if (p) {
+ *p = '\0';
+ sscanf(++p,"%x",&name_type);
+ }
+
+ if (!resolve_name(name, &ip, name_type)) {
+ /* we deliberately use DEBUG() here to send it to stderr
+ so scripts aren't mucked up */
+ DEBUG(0,("Didn't find %s#%02x\n", name, name_type));
+ return -1;
+ }
+
+ d_printf("%s\n", inet_ntoa(ip));
+ return 0;
+}
+
+static void print_ldap_srvlist(char *srvlist)
+{
+ char *cur, *next;
+ struct in_addr ip;
+ BOOL printit;
+
+ cur = srvlist;
+ do {
+ next = strchr(cur,':');
+ if (next) *next++='\0';
+ printit = resolve_name(cur, &ip, 0x20);
+ cur=next;
+ next=cur ? strchr(cur,' ') :NULL;
+ if (next)
+ *next++='\0';
+ if (printit)
+ d_printf("%s:%s\n", inet_ntoa(ip), cur?cur:"");
+ cur = next;
+ } while (next);
+}
+
+
+static int net_lookup_ldap(int argc, const char **argv)
+{
+#ifdef HAVE_LDAP
+ char *srvlist;
+ const char *domain;
+ int rc;
+ struct in_addr addr;
+ struct hostent *hostent;
+
+ if (argc > 0)
+ domain = argv[0];
+ else
+ domain = opt_target_workgroup;
+
+ DEBUG(9, ("Lookup up ldap for domain %s\n", domain));
+ rc = ldap_domain2hostlist(domain, &srvlist);
+ if ((rc == LDAP_SUCCESS) && srvlist) {
+ print_ldap_srvlist(srvlist);
+ return 0;
+ }
+
+ DEBUG(9, ("Looking up DC for domain %s\n", domain));
+ if (!get_pdc_ip(domain, &addr))
+ return -1;
+
+ hostent = gethostbyaddr((char *) &addr.s_addr, sizeof(addr.s_addr),
+ AF_INET);
+ if (!hostent)
+ return -1;
+
+ DEBUG(9, ("Found DC with DNS name %s\n", hostent->h_name));
+ domain = strchr(hostent->h_name, '.');
+ if (!domain)
+ return -1;
+ domain++;
+
+ DEBUG(9, ("Looking up ldap for domain %s\n", domain));
+ rc = ldap_domain2hostlist(domain, &srvlist);
+ if ((rc == LDAP_SUCCESS) && srvlist) {
+ print_ldap_srvlist(srvlist);
+ return 0;
+ }
+ return -1;
+#endif
+ DEBUG(1,("No LDAP support\n"));
+ return -1;
+}
+
+static int net_lookup_dc(int argc, const char **argv)
+{
+ struct ip_service *ip_list;
+ struct in_addr addr;
+ char *pdc_str = NULL;
+ const char *domain=opt_target_workgroup;
+ int count, i;
+
+ if (argc > 0)
+ domain=argv[0];
+
+ /* first get PDC */
+ if (!get_pdc_ip(domain, &addr))
+ return -1;
+
+ asprintf(&pdc_str, "%s", inet_ntoa(addr));
+ d_printf("%s\n", pdc_str);
+
+ if (!get_sorted_dc_list(domain, &ip_list, &count, False)) {
+ SAFE_FREE(pdc_str);
+ return 0;
+ }
+ for (i=0;i<count;i++) {
+ char *dc_str = inet_ntoa(ip_list[i].ip);
+ if (!strequal(pdc_str, dc_str))
+ d_printf("%s\n", dc_str);
+ }
+ SAFE_FREE(pdc_str);
+ return 0;
+}
+
+static int net_lookup_master(int argc, const char **argv)
+{
+ struct in_addr master_ip;
+ const char *domain=opt_target_workgroup;
+
+ if (argc > 0)
+ domain=argv[0];
+
+ if (!find_master_ip(domain, &master_ip))
+ return -1;
+ d_printf("%s\n", inet_ntoa(master_ip));
+ return 0;
+}
+
+static int net_lookup_kdc(int argc, const char **argv)
+{
+#ifdef HAVE_KRB5
+ krb5_error_code rc;
+ krb5_context ctx;
+ struct sockaddr_in *addrs;
+ int num_kdcs,i;
+ krb5_data realm;
+ char **realms;
+
+ rc = krb5_init_context(&ctx);
+ if (rc) {
+ DEBUG(1,("krb5_init_context failed (%s)\n",
+ error_message(rc)));
+ return -1;
+ }
+
+ if (argc>0) {
+ realm.data = (krb5_pointer) argv[0];
+ realm.length = strlen(argv[0]);
+ } else if (lp_realm() && *lp_realm()) {
+ realm.data = (krb5_pointer) lp_realm();
+ realm.length = strlen(realm.data);
+ } else {
+ rc = krb5_get_host_realm(ctx, NULL, &realms);
+ if (rc) {
+ DEBUG(1,("krb5_gethost_realm failed (%s)\n",
+ error_message(rc)));
+ return -1;
+ }
+ realm.data = (krb5_pointer) *realms;
+ realm.length = strlen(realm.data);
+ }
+
+ rc = krb5_locate_kdc(ctx, &realm, &addrs, &num_kdcs, 0);
+ if (rc) {
+ DEBUG(1, ("krb5_locate_kdc failed (%s)\n", error_message(rc)));
+ return -1;
+ }
+ for (i=0;i<num_kdcs;i++)
+ if (addrs[i].sin_family == AF_INET)
+ d_printf("%s:%hd\n", inet_ntoa(addrs[i].sin_addr),
+ ntohs(addrs[i].sin_port));
+ return 0;
+
+#endif
+ DEBUG(1, ("No kerberos support\n"));
+ return -1;
+}
+
+
+/* lookup hosts or IP addresses using internal samba lookup fns */
+int net_lookup(int argc, const char **argv)
+{
+ int i;
+
+ struct functable table[] = {
+ {"HOST", net_lookup_host},
+ {"LDAP", net_lookup_ldap},
+ {"DC", net_lookup_dc},
+ {"MASTER", net_lookup_master},
+ {"KDC", net_lookup_kdc},
+ {NULL, NULL}
+ };
+
+ if (argc < 1) {
+ d_printf("\nUsage: \n");
+ return net_lookup_usage(argc, argv);
+ }
+ for (i=0; table[i].funcname; i++) {
+ if (StrCaseCmp(argv[0], table[i].funcname) == 0)
+ return table[i].fn(argc-1, argv+1);
+ }
+
+ /* Default to lookup a hostname so 'net lookup foo#1b' can be
+ used instead of 'net lookup host foo#1b'. The host syntax
+ is a bit confusing as non #00 names can't really be
+ considered hosts as such. */
+
+ return net_lookup_host(argc, argv);
+}
diff --git a/source/utils/net_privileges.c b/source/utils/net_privileges.c
new file mode 100644
index 00000000000..95a3326ce3e
--- /dev/null
+++ b/source/utils/net_privileges.c
@@ -0,0 +1,362 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-2000,
+ * Copyright (C) Jean François Micouleau 1998-2001.
+ * Copyright (C) Gerald Carter 2003.
+ * Copyright (C) Simo Sorce 2003.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include "includes.h"
+#include "../utils/net.h"
+
+extern PRIVS privs[];
+
+/*********************************************************
+ utility function to parse an integer parameter from
+ "parameter = value"
+**********************************************************/
+static uint32 get_int_param( const char* param )
+{
+ char *p;
+
+ p = strchr( param, '=' );
+ if ( !p )
+ return 0;
+
+ return atoi(p+1);
+}
+
+/*********************************************************
+ utility function to parse an integer parameter from
+ "parameter = value"
+**********************************************************/
+static char* get_string_param( const char* param )
+{
+ char *p;
+
+ p = strchr( param, '=' );
+ if ( !p )
+ return NULL;
+
+ return (p+1);
+}
+
+/*********************************************************
+ Dump a GROUP_MAP entry to stdout (long or short listing)
+**********************************************************/
+
+static void print_priv_entry(const char *privname, const char *description, const char *sid_list)
+{
+ d_printf("%s\n", privname);
+
+ if (description) {
+ d_printf("\tdescription: %s\n", description);
+ }
+
+ if (sid_list) {
+ d_printf("\tSIDs: %s\n", sid_list);
+ } else {
+ d_printf("\tNo SIDs in this privilege\n");
+ }
+}
+
+/*********************************************************
+ List the groups.
+**********************************************************/
+static int net_priv_list(int argc, const char **argv)
+{
+ fstring privname = "";
+ fstring sid_string = "";
+ int i;
+ BOOL verbose = False;
+
+ /* get the options */
+ for ( i=0; i<argc; i++ ) {
+ if (StrnCaseCmp(argv[i], "privname", strlen("privname")) == 0) {
+ fstrcpy(privname, get_string_param(argv[i]));
+ if (!privname[0]) {
+ d_printf("must supply a name\n");
+ return -1;
+ }
+ }
+ else if (StrnCaseCmp(argv[i], "sid", strlen("sid")) == 0) {
+ fstrcpy(sid_string, get_string_param(argv[i]));
+ if (!sid_string[0]) {
+ d_printf("must supply a SID\n");
+ return -1;
+ }
+ }
+ else if (StrnCaseCmp(argv[i], "verbose", strlen("verbose")) == 0) {
+ verbose = True;
+ }
+ else {
+ d_printf("Bad option: %s\n", argv[i]);
+ return -1;
+ }
+ }
+
+ if (sid_string[0] != '\0') {
+ /* list all privileges of a single sid */
+
+ } else {
+ char *sid_list = NULL;
+
+ if (privname[0] != '\0') {
+ const char *description = NULL;
+
+ BOOL found = False;
+
+ for (i=0; privs[i].se_priv != SE_ALL_PRIVS; i++) {
+ if (StrCaseCmp(privs[i].priv, privname) == 0) {
+ description = privs[i].description;
+ found = True;
+ break;
+ }
+ }
+ if (!found) {
+ d_printf("No such privilege!\n");
+ return -1;
+ }
+
+ /* Get the current privilege from the database */
+ pdb_get_privilege_entry(privname, &sid_list);
+ print_priv_entry(privname, description, sid_list);
+
+ SAFE_FREE(sid_list);
+
+ } else for (i=0; privs[i].se_priv != SE_ALL_PRIVS; i++) {
+
+ if (!pdb_get_privilege_entry(privs[i].priv, &sid_list)) {
+ if (!verbose)
+ continue;
+
+ sid_list = NULL;
+ }
+
+ print_priv_entry(privs[i].priv, privs[i].description, sid_list);
+
+ SAFE_FREE(sid_list);
+ }
+ }
+
+ return 0;
+}
+
+/*********************************************************
+ Add a sid to a privilege entry
+**********************************************************/
+
+static int net_priv_add(int argc, const char **argv)
+{
+ DOM_SID sid;
+ fstring privname = "";
+ fstring sid_string = "";
+ uint32 rid = 0;
+ int i;
+
+ /* get the options */
+ for ( i=0; i<argc; i++ ) {
+ if (StrnCaseCmp(argv[i], "rid", strlen("rid")) == 0) {
+ rid = get_int_param(argv[i]);
+ if (rid < DOMAIN_GROUP_RID_ADMINS) {
+ d_printf("RID must be greater than %d\n", (uint32)DOMAIN_GROUP_RID_ADMINS-1);
+ return -1;
+ }
+ }
+ else if (StrnCaseCmp(argv[i], "privilege", strlen("privilege")) == 0) {
+ BOOL found = False;
+ int j;
+
+ fstrcpy(privname, get_string_param(argv[i]));
+ if (!privname[0]) {
+ d_printf("must supply a name\n");
+ return -1;
+ }
+ for (j=0; privs[j].se_priv != SE_ALL_PRIVS; j++) {
+ if (StrCaseCmp(privs[j].priv, privname) == 0) {
+ found = True;
+ }
+ }
+ if (!found) {
+ d_printf("unknown privilege name");
+ return -1;
+ }
+ }
+ else if (StrnCaseCmp(argv[i], "sid", strlen("sid")) == 0) {
+ fstrcpy(sid_string, get_string_param(argv[i]));
+ if (!sid_string[0]) {
+ d_printf("must supply a SID\n");
+ return -1;
+ }
+ }
+ else {
+ d_printf("Bad option: %s\n", argv[i]);
+ return -1;
+ }
+ }
+
+ if (privname[0] == '\0') {
+ d_printf("Usage: net priv add {rid=<int>|sid=<string>} privilege=<string>\n");
+ return -1;
+ }
+
+ if ((rid == 0) && (sid_string[0] == '\0')) {
+ d_printf("No rid or sid specified\n");
+ d_printf("Usage: net priv add {rid=<int>|sid=<string>} privilege=<string>\n");
+ return -1;
+ }
+
+ /* append the rid to our own domain/machine SID if we don't have a full SID */
+ if (sid_string[0] == '\0') {
+ sid_copy(&sid, get_global_sam_sid());
+ sid_append_rid(&sid, rid);
+ sid_to_string(sid_string, &sid);
+ } else {
+ string_to_sid(&sid, sid_string);
+ }
+
+ if (!pdb_add_sid_to_privilege(privname, &sid)) {
+ d_printf("adding sid %s to privilege %s failed!\n", sid_string, privname);
+ return -1;
+ }
+
+ d_printf("Successully added SID %s to privilege %s\n", sid_string, privname);
+ return 0;
+}
+
+/*********************************************************
+ Remove a SID froma privilege entry
+**********************************************************/
+
+static int net_priv_remove(int argc, const char **argv)
+{
+ DOM_SID sid;
+ fstring privname = "";
+ fstring sid_string = "";
+ uint32 rid = 0;
+ int i;
+
+ /* get the options */
+ for ( i=0; i<argc; i++ ) {
+ if (StrnCaseCmp(argv[i], "rid", strlen("rid")) == 0) {
+ rid = get_int_param(argv[i]);
+ if (rid < DOMAIN_GROUP_RID_ADMINS) {
+ d_printf("RID must be greater than %d\n", (uint32)DOMAIN_GROUP_RID_ADMINS-1);
+ return -1;
+ }
+ }
+ else if (StrnCaseCmp(argv[i], "privilege", strlen("privilege")) == 0) {
+ BOOL found = False;
+ int j;
+
+ fstrcpy(privname, get_string_param(argv[i]));
+ if (!privname[0]) {
+ d_printf("must supply a name\n");
+ return -1;
+ }
+ for (j=0; privs[j].se_priv != SE_ALL_PRIVS; j++) {
+ if (StrCaseCmp(privs[j].priv, privname) == 0) {
+ found = True;
+ }
+ }
+ if (!found) {
+ d_printf("unknown privilege name");
+ return -1;
+ }
+ }
+ else if (StrnCaseCmp(argv[i], "sid", strlen("sid")) == 0) {
+ fstrcpy(sid_string, get_string_param(argv[i]));
+ if (!sid_string[0]) {
+ d_printf("must supply a SID\n");
+ return -1;
+ }
+ }
+ else {
+ d_printf("Bad option: %s\n", argv[i]);
+ return -1;
+ }
+ }
+
+ if (privname[0] == '\0') {
+ d_printf("Usage: net priv remove {rid=<int>|sid=<string>} privilege=<string>\n");
+ return -1;
+ }
+
+ if ((rid == 0) && (sid_string[0] == '\0')) {
+ d_printf("No rid or sid specified\n");
+ d_printf("Usage: net priv remove {rid=<int>|sid=<string>} privilege=<string>\n");
+ return -1;
+ }
+
+ /* append the rid to our own domain/machine SID if we don't have a full SID */
+ if (sid_string[0] == '\0') {
+ sid_copy(&sid, get_global_sam_sid());
+ sid_append_rid(&sid, rid);
+ sid_to_string(sid_string, &sid);
+ } else {
+ string_to_sid(&sid, sid_string);
+ }
+
+ if (!pdb_remove_sid_from_privilege(privname, &sid)) {
+ d_printf("removing sid %s from privilege %s failed!\n", sid_string, privname);
+ return -1;
+ }
+
+ d_printf("Successully removed SID %s from privilege %s\n", sid_string, privname);
+ return 0;
+}
+
+int net_help_priv(int argc, const char **argv)
+{
+ d_printf("net priv add sid\n" \
+ " Add sid to privilege\n");
+ d_printf("net priv remove sid\n"\
+ " Remove sid from privilege\n");
+ d_printf("net priv list\n"\
+ " List sids per privilege\n");
+
+ return -1;
+}
+
+
+/***********************************************************
+ migrated functionality from smbgroupedit
+ **********************************************************/
+int net_priv(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"add", net_priv_add},
+ {"remove", net_priv_remove},
+ {"list", net_priv_list},
+ {"help", net_help_priv},
+ {NULL, NULL}
+ };
+
+ /* we shouldn't have silly checks like this */
+ if (getuid() != 0) {
+ d_printf("You must be root to edit privilege mappings.\nExiting...\n");
+ return -1;
+ }
+
+ if ( argc )
+ return net_run_function(argc, argv, func, net_help_priv);
+
+ return net_help_priv(argc, argv);
+}
+
diff --git a/source/utils/net_rap.c b/source/utils/net_rap.c
new file mode 100644
index 00000000000..39254641abf
--- /dev/null
+++ b/source/utils/net_rap.c
@@ -0,0 +1,1054 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ Copyright (C) 2001 Steve French (sfrench@us.ibm.com)
+ Copyright (C) 2001 Jim McDonough (jmcd@us.ibm.com)
+ Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
+ Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org)
+
+ Originally written by Steve and Jim. Largely rewritten by tridge in
+ November 2001.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "includes.h"
+#include "../utils/net.h"
+
+/* The following messages were for error checking that is not properly
+ reported at the moment. Which should be reinstated? */
+#define ERRMSG_TARGET_WG_NOT_VALID "\nTarget workgroup option not valid "\
+ "except on net rap server command, ignored"
+#define ERRMSG_INVALID_HELP_OPTION "\nInvalid help option\n"
+
+#define ERRMSG_BOTH_SERVER_IPADDRESS "\nTarget server and IP address both "\
+ "specified. Do not set both at the same time. The target IP address was used\n"
+
+const char *share_type[] = {
+ "Disk",
+ "Print",
+ "Dev",
+ "IPC"
+};
+
+static int errmsg_not_implemented(void)
+{
+ d_printf("\nNot implemented\n");
+ return 0;
+}
+
+int net_rap_file_usage(int argc, const char **argv)
+{
+ return net_help_file(argc, argv);
+}
+
+/***************************************************************************
+ list info on an open file
+***************************************************************************/
+static void file_fn(const char * pPath, const char * pUser, uint16 perms,
+ uint16 locks, uint32 id)
+{
+ d_printf("%-7.1d %-20.20s 0x%-4.2x %-6.1d %s\n",
+ id, pUser, perms, locks, pPath);
+}
+
+static void one_file_fn(const char *pPath, const char *pUser, uint16 perms,
+ uint16 locks, uint32 id)
+{
+ d_printf("File ID %d\n"\
+ "User name %s\n"\
+ "Locks 0x%-4.2x\n"\
+ "Path %s\n"\
+ "Permissions 0x%x\n",
+ id, pUser, locks, pPath, perms);
+}
+
+
+static int rap_file_close(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+ if (argc == 0) {
+ d_printf("\nMissing fileid of file to close\n\n");
+ return net_rap_file_usage(argc, argv);
+ }
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ ret = cli_NetFileClose(cli, atoi(argv[0]));
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_file_info(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+ if (argc == 0)
+ return net_rap_file_usage(argc, argv);
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ ret = cli_NetFileGetInfo(cli, atoi(argv[0]), one_file_fn);
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_file_user(int argc, const char **argv)
+{
+ if (argc == 0)
+ return net_rap_file_usage(argc, argv);
+
+ d_printf("net rap file user not implemented yet\n");
+ return -1;
+}
+
+int net_rap_file(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"CLOSE", rap_file_close},
+ {"USER", rap_file_user},
+ {"INFO", rap_file_info},
+ {NULL, NULL}
+ };
+
+ if (argc == 0) {
+ struct cli_state *cli;
+ int ret;
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ /* list open files */
+ d_printf(
+ "\nEnumerating open files on remote server:\n\n"\
+ "\nFileId Opened by Perms Locks Path \n"\
+ "------ --------- ----- ----- ---- \n");
+ ret = cli_NetFileEnum(cli, NULL, NULL, file_fn);
+ cli_shutdown(cli);
+ return ret;
+ }
+
+ return net_run_function(argc, argv, func, net_rap_file_usage);
+}
+
+int net_rap_share_usage(int argc, const char **argv)
+{
+ return net_help_share(argc, argv);
+}
+
+static void long_share_fn(const char *share_name, uint32 type,
+ const char *comment, void *state)
+{
+ d_printf("%-12s %-8.8s %-50s\n",
+ share_name, share_type[type], comment);
+}
+
+static void share_fn(const char *share_name, uint32 type,
+ const char *comment, void *state)
+{
+ d_printf("%s\n", share_name);
+}
+
+static int rap_share_delete(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ if (argc == 0) {
+ d_printf("\n\nShare name not specified\n");
+ return net_rap_share_usage(argc, argv);
+ }
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ ret = cli_NetShareDelete(cli, argv[0]);
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_share_add(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ RAP_SHARE_INFO_2 sinfo;
+ char *p;
+ char *sharename;
+
+ if (argc == 0) {
+ d_printf("\n\nShare name not specified\n");
+ return net_rap_share_usage(argc, argv);
+ }
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ sharename = strdup(argv[0]);
+ p = strchr(sharename, '=');
+ *p = 0;
+ strlcpy(sinfo.share_name, sharename, sizeof(sinfo.share_name));
+ sinfo.reserved1 = '\0';
+ sinfo.share_type = 0;
+ sinfo.comment = smb_xstrdup(opt_comment);
+ sinfo.perms = 0;
+ sinfo.maximum_users = opt_maxusers;
+ sinfo.active_users = 0;
+ sinfo.path = p+1;
+ memset(sinfo.password, '\0', sizeof(sinfo.password));
+ sinfo.reserved2 = '\0';
+
+ ret = cli_NetShareAdd(cli, &sinfo);
+ cli_shutdown(cli);
+ return ret;
+}
+
+
+int net_rap_share(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"DELETE", rap_share_delete},
+ {"CLOSE", rap_share_delete},
+ {"ADD", rap_share_add},
+ {NULL, NULL}
+ };
+
+ if (argc == 0) {
+ struct cli_state *cli;
+ int ret;
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ if (opt_long_list_entries) {
+ d_printf(
+ "\nEnumerating shared resources (exports) on remote server:\n\n"\
+ "\nShare name Type Description\n"\
+ "---------- ---- -----------\n");
+ ret = cli_RNetShareEnum(cli, long_share_fn, NULL);
+ } else {
+ ret = cli_RNetShareEnum(cli, share_fn, NULL);
+ }
+ cli_shutdown(cli);
+ return ret;
+ }
+
+ return net_run_function(argc, argv, func, net_rap_share_usage);
+}
+
+
+int net_rap_session_usage(int argc, const char **argv)
+{
+ d_printf(
+ "\nnet rap session [misc. options] [targets]"\
+ "\n\tenumerates all active SMB/CIFS sessions on target server\n");
+ d_printf(
+ "\nnet rap session DELETE <client_name> [misc. options] [targets] \n"\
+ "\tor"\
+ "\nnet rap session CLOSE <client_name> [misc. options] [targets]"\
+ "\n\tDeletes (closes) a session from specified client to server\n");
+ d_printf(
+ "\nnet rap session INFO <client_name>"\
+ "\n\tEnumerates all open files in specified session\n");
+
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+static void list_sessions_func(char *wsname, char *username, uint16 conns,
+ uint16 opens, uint16 users, uint32 sess_time,
+ uint32 idle_time, uint32 user_flags, char *clitype)
+{
+ int hrs = idle_time / 3600;
+ int min = (idle_time / 60) % 60;
+ int sec = idle_time % 60;
+
+ d_printf("\\\\%-18.18s %-20.20s %-18.18s %5d %2.2d:%2.2d:%2.2d\n",
+ wsname, username, clitype, opens, hrs, min, sec);
+}
+
+static void display_session_func(const char *wsname, const char *username,
+ uint16 conns, uint16 opens, uint16 users,
+ uint32 sess_time, uint32 idle_time,
+ uint32 user_flags, const char *clitype)
+{
+ int ihrs = idle_time / 3600;
+ int imin = (idle_time / 60) % 60;
+ int isec = idle_time % 60;
+ int shrs = sess_time / 3600;
+ int smin = (sess_time / 60) % 60;
+ int ssec = sess_time % 60;
+ d_printf("User name %-20.20s\n"\
+ "Computer %-20.20s\n"\
+ "Guest logon %-20.20s\n"\
+ "Client Type %-40.40s\n"\
+ "Sess time %2.2d:%2.2d:%2.2d\n"\
+ "Idle time %2.2d:%2.2d:%2.2d\n",
+ username, wsname,
+ (user_flags&0x0)?"yes":"no", clitype,
+ shrs, smin, ssec, ihrs, imin, isec);
+}
+
+static void display_conns_func(uint16 conn_id, uint16 conn_type, uint16 opens,
+ uint16 users, uint32 conn_time,
+ const char *username, const char *netname)
+{
+ d_printf("%-14.14s %-8.8s %5d\n",
+ netname, share_type[conn_type], opens);
+}
+
+static int rap_session_info(int argc, const char **argv)
+{
+ const char *sessname;
+ struct cli_state *cli;
+ int ret;
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ if (argc == 0)
+ return net_rap_session_usage(argc, argv);
+
+ sessname = argv[0];
+
+ ret = cli_NetSessionGetInfo(cli, sessname, display_session_func);
+ if (ret < 0) {
+ cli_shutdown(cli);
+ return ret;
+ }
+
+ d_printf("Share name Type # Opens\n-------------------------"\
+ "-----------------------------------------------------\n");
+ ret = cli_NetConnectionEnum(cli, sessname, display_conns_func);
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_session_delete(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ if (argc == 0)
+ return net_rap_session_usage(argc, argv);
+
+ ret = cli_NetSessionDel(cli, argv[0]);
+ cli_shutdown(cli);
+ return ret;
+}
+
+int net_rap_session(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"INFO", rap_session_info},
+ {"DELETE", rap_session_delete},
+ {"CLOSE", rap_session_delete},
+ {NULL, NULL}
+ };
+
+ if (argc == 0) {
+ struct cli_state *cli;
+ int ret;
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ d_printf("Computer User name "\
+ "Client Type Opens Idle time\n"\
+ "------------------------------------------"\
+ "------------------------------------\n");
+ ret = cli_NetSessionEnum(cli, list_sessions_func);
+
+ cli_shutdown(cli);
+ return ret;
+ }
+
+ return net_run_function(argc, argv, func, net_rap_session_usage);
+}
+
+/****************************************************************************
+list a server name
+****************************************************************************/
+static void display_server_func(const char *name, uint32 m,
+ const char *comment, void * reserved)
+{
+ d_printf("\t%-16.16s %s\n", name, comment);
+}
+
+
+int net_rap_server_usage(int argc, const char **argv)
+{
+ d_printf("net rap server [misc. options] [target]\n\t"\
+ "lists the servers in the specified domain or workgroup.\n");
+ d_printf("\n\tIf domain is not specified, it uses the current"\
+ " domain or workgroup as\n\tthe default.\n");
+
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+int net_rap_server(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ d_printf("\nEnumerating servers in this domain or workgroup: \n\n"\
+ "\tServer name Server description\n"\
+ "\t------------- ----------------------------\n");
+
+ ret = cli_NetServerEnum(cli, cli->server_domain, SV_TYPE_ALL,
+ display_server_func,NULL);
+ cli_shutdown(cli);
+ return ret;
+}
+
+int net_rap_domain_usage(int argc, const char **argv)
+{
+ d_printf("net rap domain [misc. options] [target]\n\tlists the"\
+ " domains or workgroups visible on the current network\n");
+
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+
+int net_rap_domain(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ d_printf("\nEnumerating domains:\n\n"\
+ "\tDomain name Server name of Browse Master\n"\
+ "\t------------- ----------------------------\n");
+
+ ret = cli_NetServerEnum(cli, cli->server_domain, SV_TYPE_DOMAIN_ENUM,
+ display_server_func,NULL);
+ cli_shutdown(cli);
+ return ret;
+}
+
+int net_rap_printq_usage(int argc, const char **argv)
+{
+ d_printf(
+ "net rap printq [misc. options] [targets]\n"\
+ "\tor\n"\
+ "net rap printq list [<queue_name>] [misc. options] [targets]\n"\
+ "\tlists the specified queue and jobs on the target server.\n"\
+ "\tIf the queue name is not specified, all queues are listed.\n\n");
+ d_printf(
+ "net rap printq delete [<queue name>] [misc. options] [targets]\n"\
+ "\tdeletes the specified job number on the target server, or the\n"\
+ "\tprinter queue if no job number is specified\n");
+
+ net_common_flags_usage(argc, argv);
+
+ return -1;
+}
+
+static void enum_queue(const char *queuename, uint16 pri, uint16 start,
+ uint16 until, const char *sep, const char *pproc,
+ const char *dest, const char *qparms,
+ const char *qcomment, uint16 status, uint16 jobcount)
+{
+ d_printf("%-17.17s Queue %5d jobs ",
+ queuename, jobcount);
+
+ switch (status) {
+ case 0:
+ d_printf("*Printer Active*\n");
+ break;
+ case 1:
+ d_printf("*Printer Paused*\n");
+ break;
+ case 2:
+ d_printf("*Printer error*\n");
+ break;
+ case 3:
+ d_printf("*Delete Pending*\n");
+ break;
+ default:
+ d_printf("**UNKNOWN STATUS**\n");
+ }
+}
+
+static void enum_jobs(uint16 jobid, const char *ownername,
+ const char *notifyname, const char *datatype,
+ const char *jparms, uint16 pos, uint16 status,
+ const char *jstatus, unsigned int submitted, unsigned int jobsize,
+ const char *comment)
+{
+ d_printf(" %-23.23s %5d %9d ",
+ ownername, jobid, jobsize);
+ switch (status) {
+ case 0:
+ d_printf("Waiting\n");
+ break;
+ case 1:
+ d_printf("Held in queue\n");
+ break;
+ case 2:
+ d_printf("Spooling\n");
+ break;
+ case 3:
+ d_printf("Printing\n");
+ break;
+ default:
+ d_printf("**UNKNOWN STATUS**\n");
+ }
+}
+
+#define PRINTQ_ENUM_DISPLAY \
+ "Print queues at \\\\%s\n\n"\
+ "Name Job # Size Status\n\n"\
+ "------------------------------------------------------------------"\
+ "-------------\n"
+
+static int rap_printq_info(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ if (argc == 0)
+ return net_rap_printq_usage(argc, argv);
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ d_printf(PRINTQ_ENUM_DISPLAY, cli->desthost); /* list header */
+ ret = cli_NetPrintQGetInfo(cli, argv[0], enum_queue, enum_jobs);
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_printq_delete(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ if (argc == 0)
+ return net_rap_printq_usage(argc, argv);
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ ret = cli_printjob_del(cli, atoi(argv[0]));
+ cli_shutdown(cli);
+ return ret;
+}
+
+int net_rap_printq(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ struct functable func[] = {
+ {"INFO", rap_printq_info},
+ {"DELETE", rap_printq_delete},
+ {NULL, NULL}
+ };
+
+ if (argc == 0) {
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ d_printf(PRINTQ_ENUM_DISPLAY, cli->desthost); /* list header */
+ ret = cli_NetPrintQEnum(cli, enum_queue, enum_jobs);
+ cli_shutdown(cli);
+ return ret;
+ }
+
+ return net_run_function(argc, argv, func, net_rap_printq_usage);
+}
+
+
+static int net_rap_user_usage(int argc, const char **argv)
+{
+ return net_help_user(argc, argv);
+}
+
+static void user_fn(const char *user_name, const char *comment,
+ const char * home_dir, const char * logon_script,
+ void *state)
+{
+ d_printf("%-21.21s\n", user_name);
+}
+
+static void long_user_fn(const char *user_name, const char *comment,
+ const char * home_dir, const char * logon_script,
+ void *state)
+{
+ d_printf("%-21.21s %s\n",
+ user_name, comment);
+}
+
+static void group_member_fn(const char *user_name, void *state)
+{
+ d_printf("%-21.21s\n", user_name);
+}
+
+static int rap_user_delete(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ if (argc == 0) {
+ d_printf("\n\nUser name not specified\n");
+ return net_rap_user_usage(argc, argv);
+ }
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ ret = cli_NetUserDelete(cli, argv[0]);
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_user_add(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+ RAP_USER_INFO_1 userinfo;
+
+ if (argc == 0) {
+ d_printf("\n\nUser name not specified\n");
+ return net_rap_user_usage(argc, argv);
+ }
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ safe_strcpy(userinfo.user_name, argv[0], sizeof(userinfo.user_name)-1);
+ if (opt_flags == -1)
+ opt_flags = 0x21;
+
+ userinfo.userflags = opt_flags;
+ userinfo.reserved1 = '\0';
+ userinfo.comment = smb_xstrdup(opt_comment);
+ userinfo.priv = 1;
+ userinfo.home_dir = NULL;
+ userinfo.logon_script = NULL;
+
+ ret = cli_NetUserAdd(cli, &userinfo);
+
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_user_info(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+ if (argc == 0) {
+ d_printf("\n\nUser name not specified\n");
+ return net_rap_user_usage(argc, argv);
+ }
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ ret = cli_NetUserGetGroups(cli, argv[0], group_member_fn, NULL);
+ cli_shutdown(cli);
+ return ret;
+}
+
+int net_rap_user(int argc, const char **argv)
+{
+ int ret = -1;
+ struct functable func[] = {
+ {"ADD", rap_user_add},
+ {"INFO", rap_user_info},
+ {"DELETE", rap_user_delete},
+ {NULL, NULL}
+ };
+
+ if (argc == 0) {
+ struct cli_state *cli;
+ if (!(cli = net_make_ipc_connection(0)))
+ goto done;
+ if (opt_long_list_entries) {
+ d_printf("\nUser name Comment"\
+ "\n-----------------------------\n");
+ ret = cli_RNetUserEnum(cli, long_user_fn, NULL);
+ cli_shutdown(cli);
+ goto done;
+ }
+ ret = cli_RNetUserEnum(cli, user_fn, NULL);
+ cli_shutdown(cli);
+ goto done;
+ }
+
+ ret = net_run_function(argc, argv, func, net_rap_user_usage);
+ done:
+ if (ret != 0) {
+ DEBUG(1, ("Net user returned: %d\n", ret));
+ }
+ return ret;
+}
+
+
+int net_rap_group_usage(int argc, const char **argv)
+{
+ return net_help_group(argc, argv);
+}
+
+static void long_group_fn(const char *group_name, const char *comment,
+ void *state)
+{
+ d_printf("%-21.21s %s\n", group_name, comment);
+}
+
+static void group_fn(const char *group_name, const char *comment, void *state)
+{
+ d_printf("%-21.21s\n", group_name);
+}
+
+static int rap_group_delete(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+ if (argc == 0) {
+ d_printf("\n\nGroup name not specified\n");
+ return net_rap_group_usage(argc, argv);
+ }
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ ret = cli_NetGroupDelete(cli, argv[0]);
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_group_add(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+ RAP_GROUP_INFO_1 grinfo;
+
+ if (argc == 0) {
+ d_printf("\n\nGroup name not specified\n");
+ return net_rap_group_usage(argc, argv);
+ }
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ /* BB check for length 21 or smaller explicitly ? BB */
+ safe_strcpy(grinfo.group_name, argv[0], sizeof(grinfo.group_name)-1);
+ grinfo.reserved1 = '\0';
+ grinfo.comment = smb_xstrdup(opt_comment);
+
+ ret = cli_NetGroupAdd(cli, &grinfo);
+ cli_shutdown(cli);
+ return ret;
+}
+
+int net_rap_group(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"ADD", rap_group_add},
+ {"DELETE", rap_group_delete},
+ {NULL, NULL}
+ };
+
+ if (argc == 0) {
+ struct cli_state *cli;
+ int ret;
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+ if (opt_long_list_entries) {
+ d_printf("Group name Comment\n");
+ d_printf("-----------------------------\n");
+ ret = cli_RNetGroupEnum(cli, long_group_fn, NULL);
+ cli_shutdown(cli);
+ return ret;
+ }
+ ret = cli_RNetGroupEnum(cli, group_fn, NULL);
+ cli_shutdown(cli);
+ return ret;
+ }
+
+ return net_run_function(argc, argv, func, net_rap_group_usage);
+}
+
+int net_rap_groupmember_usage(int argc, const char **argv)
+{
+ d_printf(
+ "net rap groupmember LIST <group> [misc. options] [targets]"\
+ "\n\t Enumerate users in a group\n"\
+ "\nnet rap groupmember DELETE <group> <user> [misc. options] "\
+ "[targets]\n\t Delete sepcified user from specified group\n"\
+ "\nnet rap groupmember ADD <group> <user> [misc. options] [targets]"\
+ "\n\t Add specified user to specified group\n");
+
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+
+static int rap_groupmember_add(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+ if (argc != 2) {
+ d_printf("\n\nGroup or user name not specified\n");
+ return net_rap_groupmember_usage(argc, argv);
+ }
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ ret = cli_NetGroupAddUser(cli, argv[0], argv[1]);
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_groupmember_delete(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+ if (argc != 2) {
+ d_printf("\n\nGroup or user name not specified\n");
+ return net_rap_groupmember_usage(argc, argv);
+ }
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ ret = cli_NetGroupDelUser(cli, argv[0], argv[1]);
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_groupmember_list(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+ if (argc == 0) {
+ d_printf("\n\nGroup name not specified\n");
+ return net_rap_groupmember_usage(argc, argv);
+ }
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ ret = cli_NetGroupGetUsers(cli, argv[0], group_member_fn, NULL );
+ cli_shutdown(cli);
+ return ret;
+}
+
+int net_rap_groupmember(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"ADD", rap_groupmember_add},
+ {"LIST", rap_groupmember_list},
+ {"DELETE", rap_groupmember_delete},
+ {NULL, NULL}
+ };
+
+ return net_run_function(argc, argv, func, net_rap_groupmember_usage);
+}
+
+int net_rap_validate_usage(int argc, const char **argv)
+{
+ d_printf("net rap validate <username> [password]\n"\
+ "\tValidate user and password to check whether they"\
+ " can access target server or domain\n");
+
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+int net_rap_validate(int argc, const char **argv)
+{
+ return errmsg_not_implemented();
+}
+
+int net_rap_service_usage(int argc, const char **argv)
+{
+ d_printf("net rap service [misc. options] [targets] \n"\
+ "\tlists all running service daemons on target server\n");
+ d_printf("\nnet rap service START <name> [service startup arguments]"\
+ " [misc. options] [targets]"\
+ "\n\tStart named service on remote server\n");
+ d_printf("\nnet rap service STOP <name> [misc. options] [targets]\n"\
+ "\n\tStop named service on remote server\n");
+
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+static int rap_service_start(int argc, const char **argv)
+{
+ return errmsg_not_implemented();
+}
+
+static int rap_service_stop(int argc, const char **argv)
+{
+ return errmsg_not_implemented();
+}
+
+int net_rap_service(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"START", rap_service_start},
+ {"STOP", rap_service_stop},
+ {NULL, NULL}
+ };
+
+ if (argc == 0) {
+ struct cli_state *cli;
+ int ret;
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ if (opt_long_list_entries) {
+ d_printf("Service name Comment\n");
+ d_printf("-----------------------------\n");
+ ret = cli_RNetServiceEnum(cli, long_group_fn, NULL);
+ }
+ ret = cli_RNetServiceEnum(cli, group_fn, NULL);
+ cli_shutdown(cli);
+ return ret;
+ }
+
+ return net_run_function(argc, argv, func, net_rap_service_usage);
+}
+
+int net_rap_password_usage(int argc, const char **argv)
+{
+ d_printf(
+ "net rap password <user> <oldpwo> <newpw> [misc. options] [target]\n"\
+ "\tchanges the password for the specified user at target\n");
+
+ return -1;
+}
+
+
+int net_rap_password(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ if (argc < 3)
+ return net_rap_password_usage(argc, argv);
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ /* BB Add check for password lengths? */
+ ret = cli_oem_change_password(cli, argv[0], argv[2], argv[1]);
+ cli_shutdown(cli);
+ return ret;
+}
+
+int net_rap_admin_usage(int argc, const char **argv)
+{
+ d_printf(
+ "net rap admin <remote command> [cmd args [env]] [misc. options] [targets]"\
+ "\n\texecutes a remote command on an os/2 target server\n");
+
+ return -1;
+}
+
+
+int net_rap_admin(int argc, const char **argv)
+{
+ return errmsg_not_implemented();
+}
+
+/* The help subsystem for the RAP subcommand */
+
+int net_rap_usage(int argc, const char **argv)
+{
+ d_printf(" net rap domain \tto list domains \n"\
+ " net rap file \t\tto list open files on a server \n"\
+ " net rap group \tto list user groups \n"\
+ " net rap groupmember \tto list users in a group \n"\
+ " net rap password \tto change the password of a user\n"\
+ " net rap printq \tto list the print queues on a server\n"\
+ " net rap server \tto list servers in a domain\n"\
+ " net rap session \tto list clients with open sessions to a server\n"\
+ " net rap share \tto list shares exported by a server\n"\
+ " net rap user \t\tto list users\n"\
+ " net rap validate \tto check whether a user and the corresponding password are valid\n"\
+ " net rap help\n"\
+ "\nType \"net help <option>\" to get more information on that option\n\n");
+
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+/*
+ handle "net rap help *" subcommands
+*/
+int net_rap_help(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"FILE", net_rap_file_usage},
+ {"SHARE", net_rap_share_usage},
+ {"SESSION", net_rap_session_usage},
+ {"SERVER", net_rap_server_usage},
+ {"DOMAIN", net_rap_domain_usage},
+ {"PRINTQ", net_rap_printq_usage},
+ {"USER", net_rap_user_usage},
+ {"GROUP", net_rap_group_usage},
+ {"VALIDATE", net_rap_validate_usage},
+ {"GROUPMEMBER", net_rap_groupmember_usage},
+ {"ADMIN", net_rap_admin_usage},
+ {"SERVICE", net_rap_service_usage},
+ {"PASSWORD", net_rap_password_usage},
+ {NULL, NULL}};
+
+ return net_run_function(argc, argv, func, net_rap_usage);
+}
+
+/* Entry-point for all the RAP functions. */
+
+int net_rap(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"FILE", net_rap_file},
+ {"SHARE", net_rap_share},
+ {"SESSION", net_rap_session},
+ {"SERVER", net_rap_server},
+ {"DOMAIN", net_rap_domain},
+ {"PRINTQ", net_rap_printq},
+ {"USER", net_rap_user},
+ {"GROUP", net_rap_group},
+ {"VALIDATE", net_rap_validate},
+ {"GROUPMEMBER", net_rap_groupmember},
+ {"ADMIN", net_rap_admin},
+ {"SERVICE", net_rap_service},
+ {"PASSWORD", net_rap_password},
+ {"HELP", net_rap_help},
+ {NULL, NULL}
+ };
+
+ return net_run_function(argc, argv, func, net_rap_usage);
+}
+
diff --git a/source/utils/net_rpc.c b/source/utils/net_rpc.c
new file mode 100644
index 00000000000..afb94a616a3
--- /dev/null
+++ b/source/utils/net_rpc.c
@@ -0,0 +1,3396 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org)
+ Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "includes.h"
+#include "../utils/net.h"
+
+/**
+ * @file net_rpc.c
+ *
+ * @brief RPC based subcommands for the 'net' utility.
+ *
+ * This file should contain much of the functionality that used to
+ * be found in rpcclient, execpt that the commands should change
+ * less often, and the fucntionality should be sane (the user is not
+ * expected to know a rid/sid before they conduct an operation etc.)
+ *
+ * @todo Perhaps eventually these should be split out into a number
+ * of files, as this could get quite big.
+ **/
+
+
+/* A function of this type is passed to the 'run_rpc_command' wrapper */
+typedef NTSTATUS (*rpc_command_fn)(const DOM_SID *, const char *,
+ struct cli_state *, TALLOC_CTX *, int, const char **);
+
+/**
+ * Many of the RPC functions need the domain sid. This function gets
+ * it at the start of every run
+ *
+ * @param cli A cli_state already connected to the remote machine
+ *
+ * @return The Domain SID of the remote machine.
+ **/
+
+static DOM_SID *net_get_remote_domain_sid(struct cli_state *cli, TALLOC_CTX *mem_ctx, char **domain_name)
+{
+ DOM_SID *domain_sid;
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_OK;
+ uint32 info_class = 5;
+
+ if (!cli_nt_session_open (cli, PI_LSARPC)) {
+ fprintf(stderr, "could not initialise lsa pipe\n");
+ goto error;
+ }
+
+ result = cli_lsa_open_policy(cli, mem_ctx, False,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto error;
+ }
+
+ result = cli_lsa_query_info_policy(cli, mem_ctx, &pol, info_class,
+ domain_name, &domain_sid);
+ if (!NT_STATUS_IS_OK(result)) {
+ error:
+ fprintf(stderr, "could not obtain sid for domain %s\n", cli->domain);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ fprintf(stderr, "error: %s\n", nt_errstr(result));
+ }
+
+ exit(1);
+ }
+
+ cli_lsa_close(cli, mem_ctx, &pol);
+ cli_nt_session_close(cli);
+
+ return domain_sid;
+}
+
+/**
+ * Run a single RPC command, from start to finish.
+ *
+ * @param pipe_name the pipe to connect to (usually a PIPE_ constant)
+ * @param conn_flag a NET_FLAG_ combination. Passed to
+ * net_make_ipc_connection.
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ * @return A shell status integer (0 for success)
+ */
+
+static int run_rpc_command(struct cli_state *cli_arg, const int pipe_idx, int conn_flags,
+ rpc_command_fn fn,
+ int argc, const char **argv)
+{
+ struct cli_state *cli = NULL;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS nt_status;
+ DOM_SID *domain_sid;
+ char *domain_name;
+
+ /* make use of cli_state handed over as an argument, if possible */
+ if (!cli_arg)
+ cli = net_make_ipc_connection(conn_flags);
+ else
+ cli = cli_arg;
+
+ if (!cli) {
+ return -1;
+ }
+
+ /* Create mem_ctx */
+
+ if (!(mem_ctx = talloc_init("run_rpc_command"))) {
+ DEBUG(0, ("talloc_init() failed\n"));
+ cli_shutdown(cli);
+ return -1;
+ }
+
+ domain_sid = net_get_remote_domain_sid(cli, mem_ctx, &domain_name);
+
+ if (!(conn_flags & NET_FLAGS_NO_PIPE)) {
+ if (!cli_nt_session_open(cli, pipe_idx)) {
+ DEBUG(0, ("Could not initialise pipe\n"));
+ }
+ }
+
+ nt_status = fn(domain_sid, domain_name, cli, mem_ctx, argc, argv);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("rpc command function failed! (%s)\n", nt_errstr(nt_status)));
+ } else {
+ DEBUG(5, ("rpc command function succedded\n"));
+ }
+
+ if (!(conn_flags & NET_FLAGS_NO_PIPE)) {
+ if (cli->nt_pipe_fnum)
+ cli_nt_session_close(cli);
+ }
+
+ /* close the connection only if it was opened here */
+ if (!cli_arg)
+ cli_shutdown(cli);
+
+ talloc_destroy(mem_ctx);
+
+ return (!NT_STATUS_IS_OK(nt_status));
+}
+
+
+/****************************************************************************/
+
+
+/**
+ * Force a change of the trust acccount password.
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation of the function.
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_changetrustpw_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv) {
+
+ return trust_pw_find_change_and_store_it(cli, mem_ctx, opt_target_workgroup);
+}
+
+/**
+ * Force a change of the trust acccount password.
+ *
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+
+int net_rpc_changetrustpw(int argc, const char **argv)
+{
+ return run_rpc_command(NULL, PI_NETLOGON, NET_FLAGS_ANONYMOUS | NET_FLAGS_PDC,
+ rpc_changetrustpw_internals,
+ argc, argv);
+}
+
+
+/****************************************************************************/
+
+
+/**
+ * Join a domain, the old way.
+ *
+ * This uses 'machinename' as the inital password, and changes it.
+ *
+ * The password should be created with 'server manager' or equiv first.
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation of the function.
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_oldjoin_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv) {
+
+ fstring trust_passwd;
+ unsigned char orig_trust_passwd_hash[16];
+ NTSTATUS result;
+ uint32 sec_channel_type;
+
+ /*
+ check what type of join - if the user want's to join as
+ a BDC, the server must agree that we are a BDC.
+ */
+ if (argc >= 0) {
+ sec_channel_type = get_sec_channel_type(argv[0]);
+ } else {
+ sec_channel_type = get_sec_channel_type(NULL);
+ }
+
+ fstrcpy(trust_passwd, global_myname());
+ strlower_m(trust_passwd);
+
+ /*
+ * Machine names can be 15 characters, but the max length on
+ * a password is 14. --jerry
+ */
+
+ trust_passwd[14] = '\0';
+
+ E_md4hash(trust_passwd, orig_trust_passwd_hash);
+
+ result = trust_pw_change_and_store_it(cli, mem_ctx, opt_target_workgroup,
+ orig_trust_passwd_hash,
+ sec_channel_type);
+
+ if (NT_STATUS_IS_OK(result))
+ printf("Joined domain %s.\n",opt_target_workgroup);
+
+
+ if (!secrets_store_domain_sid(opt_target_workgroup, domain_sid)) {
+ DEBUG(0, ("error storing domain sid for %s\n", opt_target_workgroup));
+ result = NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return result;
+}
+
+/**
+ * Join a domain, the old way.
+ *
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+
+static int net_rpc_perform_oldjoin(int argc, const char **argv)
+{
+ return run_rpc_command(NULL, PI_NETLOGON,
+ NET_FLAGS_ANONYMOUS | NET_FLAGS_PDC,
+ rpc_oldjoin_internals,
+ argc, argv);
+}
+
+/**
+ * Join a domain, the old way. This function exists to allow
+ * the message to be displayed when oldjoin was explicitly
+ * requested, but not when it was implied by "net rpc join"
+ *
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+
+static int net_rpc_oldjoin(int argc, const char **argv)
+{
+ int rc = net_rpc_perform_oldjoin(argc, argv);
+
+ if (rc) {
+ d_printf("Failed to join domain\n");
+ }
+
+ return rc;
+}
+
+/**
+ * Basic usage function for 'net rpc join'
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ **/
+
+static int rpc_join_usage(int argc, const char **argv)
+{
+ d_printf("net rpc join -U <username>[%%password] <type>[options]\n"\
+ "\t to join a domain with admin username & password\n"\
+ "\t\t password will be prompted if needed and none is specified\n"\
+ "\t <type> can be (default MEMBER)\n"\
+ "\t\t BDC - Join as a BDC\n"\
+ "\t\t PDC - Join as a PDC\n"\
+ "\t\t MEMBER - Join as a MEMBER server\n");
+
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+/**
+ * 'net rpc join' entrypoint.
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * Main 'net_rpc_join()' (where the admain username/password is used) is
+ * in net_rpc_join.c
+ * Try to just change the password, but if that doesn't work, use/prompt
+ * for a username/password.
+ **/
+
+int net_rpc_join(int argc, const char **argv)
+{
+ if ((net_rpc_perform_oldjoin(argc, argv) == 0))
+ return 0;
+
+ return net_rpc_join_newstyle(argc, argv);
+}
+
+
+
+/**
+ * display info about a rpc domain
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS
+rpc_info_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ SAM_UNK_CTR ctr;
+ fstring sid_str;
+
+ sid_to_string(sid_str, domain_sid);
+
+ /* Get sam policy handle */
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Get domain policy handle */
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ domain_sid, &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ ZERO_STRUCT(ctr);
+ result = cli_samr_query_dom_info(cli, mem_ctx, &domain_pol,
+ 2, &ctr);
+ if (NT_STATUS_IS_OK(result)) {
+ TALLOC_CTX *ctx = talloc_init("rpc_info_internals");
+ d_printf("Domain Name: %s\n", unistr2_tdup(ctx, &ctr.info.inf2.uni_domain));
+ d_printf("Domain SID: %s\n", sid_str);
+ d_printf("Sequence number: %u\n", ctr.info.inf2.seq_num);
+ d_printf("Num users: %u\n", ctr.info.inf2.num_domain_usrs);
+ d_printf("Num domain groups: %u\n", ctr.info.inf2.num_domain_grps);
+ d_printf("Num local groups: %u\n", ctr.info.inf2.num_local_grps);
+ talloc_destroy(ctx);
+ }
+
+ done:
+ return result;
+}
+
+
+/**
+ * 'net rpc info' entrypoint.
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ **/
+int net_rpc_info(int argc, const char **argv)
+{
+ return run_rpc_command(NULL, PI_SAMR, NET_FLAGS_ANONYMOUS | NET_FLAGS_PDC,
+ rpc_info_internals,
+ argc, argv);
+}
+
+
+/**
+ * Fetch domain SID into the local secrets.tdb
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS
+rpc_getsid_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ fstring sid_str;
+
+ sid_to_string(sid_str, domain_sid);
+ d_printf("Storing SID %s for Domain %s in secrets.tdb\n",
+ sid_str, domain_name);
+
+ if (!secrets_store_domain_sid(domain_name, domain_sid)) {
+ DEBUG(0,("Can't store domain SID\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/**
+ * 'net rpc getsid' entrypoint.
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ **/
+int net_rpc_getsid(int argc, const char **argv)
+{
+ return run_rpc_command(NULL, PI_SAMR, NET_FLAGS_ANONYMOUS | NET_FLAGS_PDC,
+ rpc_getsid_internals,
+ argc, argv);
+}
+
+
+/****************************************************************************/
+
+/**
+ * Basic usage function for 'net rpc user'
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ **/
+
+static int rpc_user_usage(int argc, const char **argv)
+{
+ return net_help_user(argc, argv);
+}
+
+/**
+ * Add a new user to a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_user_add_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv) {
+
+ POLICY_HND connect_pol, domain_pol, user_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ const char *acct_name;
+ uint16 acb_info;
+ uint32 unknown, user_rid;
+
+ if (argc != 1) {
+ d_printf("User must be specified\n");
+ rpc_user_usage(argc, argv);
+ return NT_STATUS_OK;
+ }
+
+ acct_name = argv[0];
+
+ /* Get sam policy handle */
+
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Get domain policy handle */
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ domain_sid, &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Create domain user */
+
+ acb_info = ACB_NORMAL;
+ unknown = 0xe005000b; /* No idea what this is - a permission mask? */
+
+ result = cli_samr_create_dom_user(cli, mem_ctx, &domain_pol,
+ acct_name, acb_info, unknown,
+ &user_pol, &user_rid);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ done:
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Failed to add user %s - %s\n", acct_name,
+ nt_errstr(result));
+ } else {
+ d_printf("Added user %s\n", acct_name);
+ }
+ return result;
+}
+
+/**
+ * Add a new user to a remote RPC server
+ *
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+
+static int rpc_user_add(int argc, const char **argv)
+{
+ return run_rpc_command(NULL, PI_SAMR, 0, rpc_user_add_internals,
+ argc, argv);
+}
+
+/**
+ * Delete a user from a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_user_del_internals(const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ POLICY_HND connect_pol, domain_pol, user_pol;
+
+ if (argc < 1) {
+ d_printf("User must be specified\n");
+ rpc_user_usage(argc, argv);
+ return NT_STATUS_OK;
+ }
+ /* Get sam policy and domain handles */
+
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Get handle on user */
+
+ {
+ uint32 *user_rids, num_rids, *name_types;
+ uint32 flags = 0x000003e8; /* Unknown */
+
+ result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol,
+ flags, 1, &argv[0],
+ &num_rids, &user_rids,
+ &name_types);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = cli_samr_open_user(cli, mem_ctx, &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ user_rids[0], &user_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+ /* Delete user */
+
+ result = cli_samr_delete_dom_user(cli, mem_ctx, &user_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Display results */
+
+ done:
+ return result;
+
+}
+
+/**
+ * Delete a user from a remote RPC server
+ *
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+
+static int rpc_user_delete(int argc, const char **argv)
+{
+ return run_rpc_command(NULL, PI_SAMR, 0, rpc_user_del_internals,
+ argc, argv);
+}
+
+/**
+ * Set a password for a user on a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_user_password_internals(const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ POLICY_HND connect_pol, domain_pol, user_pol;
+ SAM_USERINFO_CTR ctr;
+ SAM_USER_INFO_24 p24;
+ uchar pwbuf[516];
+ const char *user;
+ const char *new_password;
+ char *prompt = NULL;
+
+ if (argc < 1) {
+ d_printf("User must be specified\n");
+ rpc_user_usage(argc, argv);
+ return NT_STATUS_OK;
+ }
+
+ user = argv[0];
+
+ if (argv[1]) {
+ new_password = argv[1];
+ } else {
+ asprintf(&prompt, "Enter new password for %s:", user);
+ new_password = getpass(prompt);
+ SAFE_FREE(prompt);
+ }
+
+ /* Get sam policy and domain handles */
+
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Get handle on user */
+
+ {
+ uint32 *user_rids, num_rids, *name_types;
+ uint32 flags = 0x000003e8; /* Unknown */
+
+ result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol,
+ flags, 1, &user,
+ &num_rids, &user_rids,
+ &name_types);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = cli_samr_open_user(cli, mem_ctx, &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ user_rids[0], &user_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+ /* Set password on account */
+
+ ZERO_STRUCT(ctr);
+ ZERO_STRUCT(p24);
+
+ encode_pw_buffer(pwbuf, new_password, STR_UNICODE);
+
+ init_sam_user_info24(&p24, (char *)pwbuf,24);
+
+ ctr.switch_value = 24;
+ ctr.info.id24 = &p24;
+
+ result = cli_samr_set_userinfo(cli, mem_ctx, &user_pol, 24,
+ &cli->user_session_key, &ctr);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Display results */
+
+ done:
+ return result;
+
+}
+
+/**
+ * Set a user's password on a remote RPC server
+ *
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+
+static int rpc_user_password(int argc, const char **argv)
+{
+ return run_rpc_command(NULL, PI_SAMR, 0, rpc_user_password_internals,
+ argc, argv);
+}
+
+/**
+ * List user's groups on a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS
+rpc_user_info_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol, user_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 *rids, num_rids, *name_types, num_names;
+ uint32 flags = 0x000003e8; /* Unknown */
+ int i;
+ char **names;
+ DOM_GID *user_gids;
+
+ if (argc < 1) {
+ d_printf("User must be specified\n");
+ rpc_user_usage(argc, argv);
+ return NT_STATUS_OK;
+ }
+ /* Get sam policy handle */
+
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) goto done;
+
+ /* Get domain policy handle */
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ domain_sid, &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) goto done;
+
+ /* Get handle on user */
+
+ result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol,
+ flags, 1, &argv[0],
+ &num_rids, &rids, &name_types);
+
+ if (!NT_STATUS_IS_OK(result)) goto done;
+
+ result = cli_samr_open_user(cli, mem_ctx, &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ rids[0], &user_pol);
+ if (!NT_STATUS_IS_OK(result)) goto done;
+
+ result = cli_samr_query_usergroups(cli, mem_ctx, &user_pol,
+ &num_rids, &user_gids);
+
+ /* Look up rids */
+
+ rids = (uint32 *)talloc(mem_ctx, sizeof(uint32) * num_rids);
+
+ for (i = 0; i < num_rids; i++)
+ rids[i] = user_gids[i].g_rid;
+
+ result = cli_samr_lookup_rids(cli, mem_ctx, &domain_pol,
+ flags, num_rids, rids,
+ &num_names, &names, &name_types);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Display results */
+
+ for (i = 0; i < num_names; i++)
+ printf("%s\n", names[i]);
+
+ done:
+ return result;
+}
+
+/**
+ * List a user's groups from a remote RPC server
+ *
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+
+static int rpc_user_info(int argc, const char **argv)
+{
+ return run_rpc_command(NULL, PI_SAMR, 0, rpc_user_info_internals,
+ argc, argv);
+}
+
+/**
+ * List users on a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS
+rpc_user_list_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 start_idx=0, num_entries, i, loop_count = 0;
+ SAM_DISPINFO_CTR ctr;
+ SAM_DISPINFO_1 info1;
+
+ /* Get sam policy handle */
+
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Get domain policy handle */
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ domain_sid, &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Query domain users */
+ ZERO_STRUCT(ctr);
+ ZERO_STRUCT(info1);
+ ctr.sam.info1 = &info1;
+ if (opt_long_list_entries)
+ d_printf("\nUser name Comment"\
+ "\n-----------------------------\n");
+ do {
+ fstring user, desc;
+ uint32 max_entries, max_size;
+
+ get_query_dispinfo_params(
+ loop_count, &max_entries, &max_size);
+
+ result = cli_samr_query_dispinfo(cli, mem_ctx, &domain_pol,
+ &start_idx, 1, &num_entries,
+ max_entries, max_size, &ctr);
+ loop_count++;
+
+ for (i = 0; i < num_entries; i++) {
+ unistr2_to_ascii(user, &(&ctr.sam.info1->str[i])->uni_acct_name, sizeof(user)-1);
+ if (opt_long_list_entries)
+ unistr2_to_ascii(desc, &(&ctr.sam.info1->str[i])->uni_acct_desc, sizeof(desc)-1);
+
+ if (opt_long_list_entries)
+ printf("%-21.21s %s\n", user, desc);
+ else
+ printf("%s\n", user);
+ }
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+ done:
+ return result;
+}
+
+/**
+ * 'net rpc user' entrypoint.
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ **/
+
+int net_rpc_user(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"add", rpc_user_add},
+ {"info", rpc_user_info},
+ {"delete", rpc_user_delete},
+ {"password", rpc_user_password},
+ {NULL, NULL}
+ };
+
+ if (argc == 0) {
+ if (opt_long_list_entries) {
+ } else {
+ }
+ return run_rpc_command(NULL,PI_SAMR, 0,
+ rpc_user_list_internals,
+ argc, argv);
+ }
+
+ return net_run_function(argc, argv, func, rpc_user_usage);
+}
+
+
+/****************************************************************************/
+
+/**
+ * Basic usage function for 'net rpc group'
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ **/
+
+static int rpc_group_usage(int argc, const char **argv)
+{
+ return net_help_group(argc, argv);
+}
+
+static NTSTATUS
+rpc_group_add_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol, group_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ GROUP_INFO_CTR group_info;
+
+ if (argc != 1) {
+ d_printf("Group name must be specified\n");
+ rpc_group_usage(argc, argv);
+ return NT_STATUS_OK;
+ }
+
+ /* Get sam policy handle */
+
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) goto done;
+
+ /* Get domain policy handle */
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ domain_sid, &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) goto done;
+
+ /* Create the group */
+
+ result = cli_samr_create_dom_group(cli, mem_ctx, &domain_pol,
+ argv[0], MAXIMUM_ALLOWED_ACCESS,
+ &group_pol);
+ if (!NT_STATUS_IS_OK(result)) goto done;
+
+ if (strlen(opt_comment) == 0) goto done;
+
+ /* We've got a comment to set */
+
+ group_info.switch_value1 = 4;
+ init_samr_group_info4(&group_info.group.info4, opt_comment);
+
+ result = cli_samr_set_groupinfo(cli, mem_ctx, &group_pol, &group_info);
+ if (!NT_STATUS_IS_OK(result)) goto done;
+
+ done:
+ if (NT_STATUS_IS_OK(result))
+ DEBUG(5, ("add group succeeded\n"));
+ else
+ d_printf("add group failed: %s\n", nt_errstr(result));
+
+ return result;
+}
+
+static NTSTATUS
+rpc_alias_add_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol, alias_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ ALIAS_INFO_CTR alias_info;
+
+ if (argc != 1) {
+ d_printf("Group name must be specified\n");
+ rpc_group_usage(argc, argv);
+ return NT_STATUS_OK;
+ }
+
+ /* Get sam policy handle */
+
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) goto done;
+
+ /* Get domain policy handle */
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ domain_sid, &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) goto done;
+
+ /* Create the group */
+
+ result = cli_samr_create_dom_alias(cli, mem_ctx, &domain_pol,
+ argv[0], &alias_pol);
+ if (!NT_STATUS_IS_OK(result)) goto done;
+
+ if (strlen(opt_comment) == 0) goto done;
+
+ /* We've got a comment to set */
+
+ alias_info.switch_value1 = 3;
+ alias_info.switch_value2 = 3;
+ init_samr_alias_info3(&alias_info.alias.info3, opt_comment);
+
+ result = cli_samr_set_aliasinfo(cli, mem_ctx, &alias_pol, &alias_info);
+ if (!NT_STATUS_IS_OK(result)) goto done;
+
+ done:
+ if (NT_STATUS_IS_OK(result))
+ DEBUG(5, ("add group succeeded\n"));
+ else
+ d_printf("add group failed: %s\n", nt_errstr(result));
+
+ return result;
+}
+
+static int rpc_group_add(int argc, const char **argv)
+{
+ if (opt_localgroup)
+ return run_rpc_command(NULL, PI_SAMR, 0,
+ rpc_alias_add_internals,
+ argc, argv);
+
+ return run_rpc_command(NULL, PI_SAMR, 0,
+ rpc_group_add_internals,
+ argc, argv);
+}
+
+static NTSTATUS
+get_sid_from_name(struct cli_state *cli, TALLOC_CTX *mem_ctx, const char *name,
+ DOM_SID *sid, enum SID_NAME_USE *type)
+{
+ int current_pipe = cli->pipe_idx;
+
+ DOM_SID *sids = NULL;
+ uint32 *types = NULL;
+ POLICY_HND lsa_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (current_pipe != PI_LSARPC) {
+
+ if (current_pipe != -1)
+ cli_nt_session_close(cli);
+
+ if (!cli_nt_session_open(cli, PI_LSARPC))
+ goto done;
+ }
+
+ result = cli_lsa_open_policy(cli, mem_ctx, False,
+ SEC_RIGHTS_MAXIMUM_ALLOWED, &lsa_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_lookup_names(cli, mem_ctx, &lsa_pol, 1,
+ &name, &sids, &types);
+
+ if (NT_STATUS_IS_OK(result)) {
+ sid_copy(sid, &sids[0]);
+ *type = types[0];
+ }
+
+ cli_lsa_close(cli, mem_ctx, &lsa_pol);
+
+ done:
+ if (current_pipe != PI_LSARPC) {
+ cli_nt_session_close(cli);
+ if (current_pipe != -1)
+ cli_nt_session_open(cli, current_pipe);
+ }
+
+ if (!NT_STATUS_IS_OK(result) && (StrnCaseCmp(name, "S-", 2) == 0)) {
+
+ /* Try as S-1-5-whatever */
+
+ DOM_SID tmp_sid;
+
+ if (string_to_sid(&tmp_sid, name)) {
+ sid_copy(sid, &tmp_sid);
+ *type = SID_NAME_UNKNOWN;
+ result = NT_STATUS_OK;
+ }
+ }
+
+ return result;
+}
+
+static NTSTATUS
+rpc_add_groupmem(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const DOM_SID *group_sid, const char *member)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result;
+ uint32 group_rid;
+ POLICY_HND group_pol;
+
+ uint32 num_rids;
+ uint32 *rids = NULL;
+ uint32 *rid_types = NULL;
+
+ DOM_SID sid;
+
+ sid_copy(&sid, group_sid);
+
+ if (!sid_split_rid(&sid, &group_rid))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ /* Get sam policy handle */
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ /* Get domain policy handle */
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &sid, &domain_pol);
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol, 1000,
+ 1, &member,
+ &num_rids, &rids, &rid_types);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Could not lookup up group member %s\n", member);
+ goto done;
+ }
+
+ result = cli_samr_open_group(cli, mem_ctx, &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ group_rid, &group_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_add_groupmem(cli, mem_ctx, &group_pol, rids[0]);
+
+ done:
+ cli_samr_close(cli, mem_ctx, &connect_pol);
+ return result;
+}
+
+static NTSTATUS
+rpc_add_aliasmem(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const DOM_SID *alias_sid, const char *member)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result;
+ uint32 alias_rid;
+ POLICY_HND alias_pol;
+
+ DOM_SID member_sid;
+ enum SID_NAME_USE member_type;
+
+ DOM_SID sid;
+
+ sid_copy(&sid, alias_sid);
+
+ if (!sid_split_rid(&sid, &alias_rid))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ result = get_sid_from_name(cli, mem_ctx, member,
+ &member_sid, &member_type);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Could not lookup up group member %s\n", member);
+ return result;
+ }
+
+ /* Get sam policy handle */
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Get domain policy handle */
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &sid, &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = cli_samr_open_alias(cli, mem_ctx, &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ alias_rid, &alias_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ result = cli_samr_add_aliasmem(cli, mem_ctx, &alias_pol, &member_sid);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ done:
+ cli_samr_close(cli, mem_ctx, &connect_pol);
+ return result;
+}
+
+static NTSTATUS
+rpc_group_addmem_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ DOM_SID group_sid;
+ enum SID_NAME_USE group_type;
+
+ if (argc != 2) {
+ d_printf("Usage: 'net rpc group addmem <group> <member>\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!NT_STATUS_IS_OK(get_sid_from_name(cli, mem_ctx, argv[0],
+ &group_sid, &group_type))) {
+ d_printf("Could not lookup group name %s\n", argv[0]);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (group_type == SID_NAME_DOM_GRP) {
+ NTSTATUS result = rpc_add_groupmem(cli, mem_ctx,
+ &group_sid, argv[1]);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Could not add %s to %s: %s\n",
+ argv[1], argv[0], nt_errstr(result));
+ }
+ return result;
+ }
+
+ if (group_type == SID_NAME_ALIAS) {
+ NTSTATUS result = rpc_add_aliasmem(cli, mem_ctx,
+ &group_sid, argv[1]);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Could not add %s to %s: %s\n",
+ argv[1], argv[0], nt_errstr(result));
+ }
+ return result;
+ }
+
+ d_printf("Can only add members to global or local groups which "
+ "%s is not\n", argv[0]);
+
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+static int rpc_group_addmem(int argc, const char **argv)
+{
+ return run_rpc_command(NULL, PI_SAMR, 0,
+ rpc_group_addmem_internals,
+ argc, argv);
+}
+
+static NTSTATUS
+rpc_del_groupmem(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const DOM_SID *group_sid, const char *member)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result;
+ uint32 group_rid;
+ POLICY_HND group_pol;
+
+ uint32 num_rids;
+ uint32 *rids = NULL;
+ uint32 *rid_types = NULL;
+
+ DOM_SID sid;
+
+ sid_copy(&sid, group_sid);
+
+ if (!sid_split_rid(&sid, &group_rid))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ /* Get sam policy handle */
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ /* Get domain policy handle */
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &sid, &domain_pol);
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol, 1000,
+ 1, &member,
+ &num_rids, &rids, &rid_types);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Could not lookup up group member %s\n", member);
+ goto done;
+ }
+
+ result = cli_samr_open_group(cli, mem_ctx, &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ group_rid, &group_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_del_groupmem(cli, mem_ctx, &group_pol, rids[0]);
+
+ done:
+ cli_samr_close(cli, mem_ctx, &connect_pol);
+ return result;
+}
+
+static NTSTATUS
+rpc_del_aliasmem(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const DOM_SID *alias_sid, const char *member)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result;
+ uint32 alias_rid;
+ POLICY_HND alias_pol;
+
+ DOM_SID member_sid;
+ enum SID_NAME_USE member_type;
+
+ DOM_SID sid;
+
+ sid_copy(&sid, alias_sid);
+
+ if (!sid_split_rid(&sid, &alias_rid))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ result = get_sid_from_name(cli, mem_ctx, member,
+ &member_sid, &member_type);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Could not lookup up group member %s\n", member);
+ return result;
+ }
+
+ /* Get sam policy handle */
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Get domain policy handle */
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &sid, &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = cli_samr_open_alias(cli, mem_ctx, &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ alias_rid, &alias_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ result = cli_samr_del_aliasmem(cli, mem_ctx, &alias_pol, &member_sid);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ done:
+ cli_samr_close(cli, mem_ctx, &connect_pol);
+ return result;
+}
+
+static NTSTATUS
+rpc_group_delmem_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ DOM_SID group_sid;
+ enum SID_NAME_USE group_type;
+
+ if (argc != 2) {
+ d_printf("Usage: 'net rpc group delmem <group> <member>\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!NT_STATUS_IS_OK(get_sid_from_name(cli, mem_ctx, argv[0],
+ &group_sid, &group_type))) {
+ d_printf("Could not lookup group name %s\n", argv[0]);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (group_type == SID_NAME_DOM_GRP) {
+ NTSTATUS result = rpc_del_groupmem(cli, mem_ctx,
+ &group_sid, argv[1]);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Could not del %s from %s: %s\n",
+ argv[1], argv[0], nt_errstr(result));
+ }
+ return result;
+ }
+
+ if (group_type == SID_NAME_ALIAS) {
+ NTSTATUS result = rpc_del_aliasmem(cli, mem_ctx,
+ &group_sid, argv[1]);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Could not del %s from %s: %s\n",
+ argv[1], argv[0], nt_errstr(result));
+ }
+ return result;
+ }
+
+ d_printf("Can only delete members from global or local groups which "
+ "%s is not\n", argv[0]);
+
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+static int rpc_group_delmem(int argc, const char **argv)
+{
+ return run_rpc_command(NULL, PI_SAMR, 0,
+ rpc_group_delmem_internals,
+ argc, argv);
+}
+
+/**
+ * List groups on a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS
+rpc_group_list_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 start_idx=0, max_entries=250, num_entries, i, loop_count = 0;
+ struct acct_info *groups;
+ DOM_SID global_sid_Builtin;
+ BOOL global = False;
+ BOOL local = False;
+ BOOL builtin = False;
+
+ if (argc == 0) {
+ global = True;
+ local = True;
+ builtin = True;
+ }
+
+ for (i=0; i<argc; i++) {
+ if (strequal(argv[i], "global"))
+ global = True;
+
+ if (strequal(argv[i], "local"))
+ local = True;
+
+ if (strequal(argv[i], "builtin"))
+ builtin = True;
+ }
+
+ string_to_sid(&global_sid_Builtin, "S-1-5-32");
+
+ /* Get sam policy handle */
+
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Get domain policy handle */
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ domain_sid, &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Query domain groups */
+ if (opt_long_list_entries)
+ d_printf("\nGroup name Comment"\
+ "\n-----------------------------\n");
+ do {
+ SAM_DISPINFO_CTR ctr;
+ SAM_DISPINFO_3 info3;
+ uint32 max_size;
+
+ ZERO_STRUCT(ctr);
+ ZERO_STRUCT(info3);
+ ctr.sam.info3 = &info3;
+
+ if (!global) break;
+
+ get_query_dispinfo_params(
+ loop_count, &max_entries, &max_size);
+
+ result = cli_samr_query_dispinfo(cli, mem_ctx, &domain_pol,
+ &start_idx, 3, &num_entries,
+ max_entries, max_size, &ctr);
+
+ if (!NT_STATUS_IS_OK(result) &&
+ !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES))
+ break;
+
+ for (i = 0; i < num_entries; i++) {
+
+ fstring group, desc;
+
+ unistr2_to_ascii(group, &(&ctr.sam.info3->str[i])->uni_grp_name, sizeof(group)-1);
+ unistr2_to_ascii(desc, &(&ctr.sam.info3->str[i])->uni_grp_desc, sizeof(desc)-1);
+
+ if (opt_long_list_entries)
+ printf("%-21.21s %-50.50s\n",
+ group, desc);
+ else
+ printf("%s\n", group);
+ }
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+ /* query domain aliases */
+ start_idx = 0;
+ do {
+ if (!local) break;
+
+ result = cli_samr_enum_als_groups(cli, mem_ctx, &domain_pol,
+ &start_idx, max_entries,
+ &groups, &num_entries);
+
+ if (!NT_STATUS_IS_OK(result) &&
+ !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES))
+ break;
+
+ for (i = 0; i < num_entries; i++) {
+
+ char *description = NULL;
+
+ if (opt_long_list_entries) {
+
+ POLICY_HND alias_pol;
+ ALIAS_INFO_CTR ctr;
+
+ if ((NT_STATUS_IS_OK(cli_samr_open_alias(cli, mem_ctx,
+ &domain_pol,
+ 0x8,
+ groups[i].rid,
+ &alias_pol))) &&
+ (NT_STATUS_IS_OK(cli_samr_query_alias_info(cli, mem_ctx,
+ &alias_pol, 3,
+ &ctr))) &&
+ (NT_STATUS_IS_OK(cli_samr_close(cli, mem_ctx,
+ &alias_pol)))) {
+ description = unistr2_tdup(mem_ctx,
+ &ctr.alias.info3.uni_acct_desc);
+ }
+ }
+
+ if (description != NULL) {
+ printf("%-21.21s %-50.50s\n",
+ groups[i].acct_name,
+ description);
+ } else {
+ printf("%s\n", groups[i].acct_name);
+ }
+ }
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+ cli_samr_close(cli, mem_ctx, &domain_pol);
+ /* Get builtin policy handle */
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &global_sid_Builtin, &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+ /* query builtin aliases */
+ start_idx = 0;
+ do {
+ if (!builtin) break;
+
+ result = cli_samr_enum_als_groups(cli, mem_ctx, &domain_pol,
+ &start_idx, max_entries,
+ &groups, &num_entries);
+
+ if (!NT_STATUS_IS_OK(result) &&
+ !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES))
+ break;
+
+ for (i = 0; i < num_entries; i++) {
+
+ char *description = NULL;
+
+ if (opt_long_list_entries) {
+
+ POLICY_HND alias_pol;
+ ALIAS_INFO_CTR ctr;
+
+ if ((NT_STATUS_IS_OK(cli_samr_open_alias(cli, mem_ctx,
+ &domain_pol,
+ 0x8,
+ groups[i].rid,
+ &alias_pol))) &&
+ (NT_STATUS_IS_OK(cli_samr_query_alias_info(cli, mem_ctx,
+ &alias_pol, 3,
+ &ctr))) &&
+ (NT_STATUS_IS_OK(cli_samr_close(cli, mem_ctx,
+ &alias_pol)))) {
+ description = unistr2_tdup(mem_ctx,
+ &ctr.alias.info3.uni_acct_desc);
+ }
+ }
+
+ if (description != NULL) {
+ printf("%-21.21s %-50.50s\n",
+ groups[i].acct_name,
+ description);
+ } else {
+ printf("%s\n", groups[i].acct_name);
+ }
+ }
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+ done:
+ return result;
+}
+
+static int rpc_group_list(int argc, const char **argv)
+{
+ return run_rpc_command(NULL, PI_SAMR, 0,
+ rpc_group_list_internals,
+ argc, argv);
+}
+
+static NTSTATUS
+rpc_list_group_members(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const char *domain_name, const DOM_SID *domain_sid,
+ POLICY_HND *domain_pol, uint32 rid)
+{
+ NTSTATUS result;
+ POLICY_HND group_pol;
+ uint32 num_members, *group_rids, *group_attrs;
+ uint32 num_names;
+ char **names;
+ uint32 *name_types;
+ int i;
+
+ fstring sid_str;
+ sid_to_string(sid_str, domain_sid);
+
+ result = cli_samr_open_group(cli, mem_ctx, domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ rid, &group_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ result = cli_samr_query_groupmem(cli, mem_ctx, &group_pol,
+ &num_members, &group_rids,
+ &group_attrs);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ while (num_members > 0) {
+ int this_time = 512;
+
+ if (num_members < this_time)
+ this_time = num_members;
+
+ result = cli_samr_lookup_rids(cli, mem_ctx, domain_pol, 1000,
+ this_time, group_rids,
+ &num_names, &names, &name_types);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ /* We only have users as members, but make the output
+ the same as the output of alias members */
+
+ for (i = 0; i < this_time; i++) {
+
+ if (opt_long_list_entries) {
+ printf("%s-%d %s\\%s %d\n", sid_str,
+ group_rids[i], domain_name, names[i],
+ SID_NAME_USER);
+ } else {
+ printf("%s\\%s\n", domain_name, names[i]);
+ }
+ }
+
+ num_members -= this_time;
+ group_rids += 512;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS
+rpc_list_alias_members(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *domain_pol, uint32 rid)
+{
+ NTSTATUS result;
+ POLICY_HND alias_pol, lsa_pol;
+ uint32 num_members;
+ DOM_SID *alias_sids;
+ char **domains;
+ char **names;
+ uint32 *types;
+ int i;
+
+ result = cli_samr_open_alias(cli, mem_ctx, domain_pol,
+ MAXIMUM_ALLOWED_ACCESS, rid, &alias_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ result = cli_samr_query_aliasmem(cli, mem_ctx, &alias_pol,
+ &num_members, &alias_sids);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Couldn't list alias members\n");
+ return result;
+ }
+
+ if (num_members == 0) {
+ return NT_STATUS_OK;
+ }
+
+ cli_nt_session_close(cli);
+
+ if (!cli_nt_session_open(cli, PI_LSARPC)) {
+ d_printf("Couldn't open LSA pipe\n");
+ return result;
+ }
+
+ result = cli_lsa_open_policy(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED, &lsa_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Couldn't open LSA policy handle\n");
+ return result;
+ }
+
+ result = cli_lsa_lookup_sids(cli, mem_ctx, &lsa_pol, num_members,
+ alias_sids,
+ &domains, &names, &types);
+
+ if (!NT_STATUS_IS_OK(result) &&
+ !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
+ d_printf("Couldn't lookup SIDs\n");
+ return result;
+ }
+
+ for (i = 0; i < num_members; i++) {
+ fstring sid_str;
+ sid_to_string(sid_str, &alias_sids[i]);
+
+ if (opt_long_list_entries) {
+ printf("%s %s\\%s %d\n", sid_str,
+ domains[i] ? domains[i] : "*unknown*",
+ names[i] ? names[i] : "*unknown*", types[i]);
+ } else {
+ if (domains[i])
+ printf("%s\\%s\n", domains[i], names[i]);
+ else
+ printf("%s\n", sid_str);
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS
+rpc_group_members_internals(const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ NTSTATUS result;
+ POLICY_HND connect_pol, domain_pol;
+ uint32 num_rids, *rids, *rid_types;
+
+ /* Get sam policy handle */
+
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ /* Get domain policy handle */
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol, 1000,
+ 1, argv, &num_rids, &rids, &rid_types);
+
+ if (!NT_STATUS_IS_OK(result)) {
+
+ /* Ok, did not find it in the global sam, try with builtin */
+
+ DOM_SID sid_Builtin;
+
+ cli_samr_close(cli, mem_ctx, &domain_pol);
+
+ string_to_sid(&sid_Builtin, "S-1-5-32");
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &sid_Builtin, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Couldn't find group %s\n", argv[0]);
+ return result;
+ }
+
+ result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol, 1000,
+ 1, argv, &num_rids,
+ &rids, &rid_types);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Couldn't find group %s\n", argv[0]);
+ return result;
+ }
+ }
+
+ if (num_rids != 1) {
+ d_printf("Couldn't find group %s\n", argv[0]);
+ return result;
+ }
+
+ if (rid_types[0] == SID_NAME_DOM_GRP) {
+ return rpc_list_group_members(cli, mem_ctx, domain_name,
+ domain_sid, &domain_pol,
+ rids[0]);
+ }
+
+ if (rid_types[0] == SID_NAME_ALIAS) {
+ return rpc_list_alias_members(cli, mem_ctx, &domain_pol,
+ rids[0]);
+ }
+
+ return NT_STATUS_NO_SUCH_GROUP;
+}
+
+static int rpc_group_members(int argc, const char **argv)
+{
+ if (argc != 1) {
+ return rpc_group_usage(argc, argv);
+ }
+
+ return run_rpc_command(NULL, PI_SAMR, 0,
+ rpc_group_members_internals,
+ argc, argv);
+}
+
+/**
+ * 'net rpc group' entrypoint.
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ **/
+
+int net_rpc_group(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"add", rpc_group_add},
+ {"addmem", rpc_group_addmem},
+ {"delmem", rpc_group_delmem},
+#if 0
+ {"delete", rpc_group_delete},
+#endif
+ {"list", rpc_group_list},
+ {"members", rpc_group_members},
+ {NULL, NULL}
+ };
+
+ if (argc == 0) {
+ if (opt_long_list_entries) {
+ } else {
+ }
+ return run_rpc_command(NULL, PI_SAMR, 0,
+ rpc_group_list_internals,
+ argc, argv);
+ }
+
+ return net_run_function(argc, argv, func, rpc_group_usage);
+}
+
+/****************************************************************************/
+
+static int rpc_share_usage(int argc, const char **argv)
+{
+ return net_help_share(argc, argv);
+}
+
+/**
+ * Add a share on a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+static NTSTATUS
+rpc_share_add_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,int argc, const char **argv)
+{
+ WERROR result;
+ char *sharename=talloc_strdup(mem_ctx, argv[0]);
+ char *path;
+ uint32 type=0; /* only allow disk shares to be added */
+ uint32 num_users=0, perms=0;
+ char *password=NULL; /* don't allow a share password */
+
+ path = strchr(sharename, '=');
+ if (!path)
+ return NT_STATUS_UNSUCCESSFUL;
+ *path++ = '\0';
+
+ result = cli_srvsvc_net_share_add(cli, mem_ctx, sharename, type,
+ opt_comment, perms, opt_maxusers,
+ num_users, path, password);
+ return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+static int rpc_share_add(int argc, const char **argv)
+{
+ if ((argc < 1) || !strchr(argv[0], '=')) {
+ DEBUG(1,("Sharename or path not specified on add\n"));
+ return rpc_share_usage(argc, argv);
+ }
+ return run_rpc_command(NULL, PI_SRVSVC, 0,
+ rpc_share_add_internals,
+ argc, argv);
+}
+
+/**
+ * Delete a share on a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+static NTSTATUS
+rpc_share_del_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,int argc, const char **argv)
+{
+ WERROR result;
+
+ result = cli_srvsvc_net_share_del(cli, mem_ctx, argv[0]);
+ return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/**
+ * Delete a share on a remote RPC server
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+static int rpc_share_delete(int argc, const char **argv)
+{
+ if (argc < 1) {
+ DEBUG(1,("Sharename not specified on delete\n"));
+ return rpc_share_usage(argc, argv);
+ }
+ return run_rpc_command(NULL, PI_SRVSVC, 0,
+ rpc_share_del_internals,
+ argc, argv);
+}
+
+/**
+ * Formatted print of share info
+ *
+ * @param info1 pointer to SRV_SHARE_INFO_1 to format
+ **/
+
+static void display_share_info_1(SRV_SHARE_INFO_1 *info1)
+{
+ fstring netname = "", remark = "";
+
+ rpcstr_pull_unistr2_fstring(netname, &info1->info_1_str.uni_netname);
+ rpcstr_pull_unistr2_fstring(remark, &info1->info_1_str.uni_remark);
+
+ if (opt_long_list_entries) {
+ d_printf("%-12s %-8.8s %-50s\n",
+ netname, share_type[info1->info_1.type], remark);
+ } else {
+ d_printf("%s\n", netname);
+ }
+
+}
+
+/**
+ * List shares on a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS
+rpc_share_list_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ SRV_SHARE_INFO_CTR ctr;
+ WERROR result;
+ ENUM_HND hnd;
+ uint32 preferred_len = 0xffffffff, i;
+
+ init_enum_hnd(&hnd, 0);
+
+ result = cli_srvsvc_net_share_enum(
+ cli, mem_ctx, 1, &ctr, preferred_len, &hnd);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ /* Display results */
+
+ if (opt_long_list_entries) {
+ d_printf(
+ "\nEnumerating shared resources (exports) on remote server:\n\n"\
+ "\nShare name Type Description\n"\
+ "---------- ---- -----------\n");
+ }
+ for (i = 0; i < ctr.num_entries; i++)
+ display_share_info_1(&ctr.share.info1[i]);
+ done:
+ return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/**
+ * 'net rpc share' entrypoint.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ **/
+
+int net_rpc_share(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"add", rpc_share_add},
+ {"delete", rpc_share_delete},
+ {NULL, NULL}
+ };
+
+ if (argc == 0)
+ return run_rpc_command(NULL, PI_SRVSVC, 0,
+ rpc_share_list_internals,
+ argc, argv);
+
+ return net_run_function(argc, argv, func, rpc_share_usage);
+}
+
+/****************************************************************************/
+
+static int rpc_file_usage(int argc, const char **argv)
+{
+ return net_help_file(argc, argv);
+}
+
+/**
+ * Close a file on a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+static NTSTATUS
+rpc_file_close_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ WERROR result;
+ result = cli_srvsvc_net_file_close(cli, mem_ctx, atoi(argv[0]));
+ return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/**
+ * Close a file on a remote RPC server
+ *
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+static int rpc_file_close(int argc, const char **argv)
+{
+ if (argc < 1) {
+ DEBUG(1, ("No fileid given on close\n"));
+ return(rpc_file_usage(argc, argv));
+ }
+
+ return run_rpc_command(NULL, PI_SRVSVC, 0,
+ rpc_file_close_internals,
+ argc, argv);
+}
+
+/**
+ * Formatted print of open file info
+ *
+ * @param info3 FILE_INFO_3 contents
+ * @param str3 strings for FILE_INFO_3
+ **/
+
+static void display_file_info_3(FILE_INFO_3 *info3, FILE_INFO_3_STR *str3)
+{
+ fstring user = "", path = "";
+
+ rpcstr_pull_unistr2_fstring(user, &str3->uni_user_name);
+ rpcstr_pull_unistr2_fstring(path, &str3->uni_path_name);
+
+ d_printf("%-7.1d %-20.20s 0x%-4.2x %-6.1d %s\n",
+ info3->id, user, info3->perms, info3->num_locks, path);
+}
+
+/**
+ * List open files on a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS
+rpc_file_list_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ SRV_FILE_INFO_CTR ctr;
+ WERROR result;
+ ENUM_HND hnd;
+ uint32 preferred_len = 0xffffffff, i;
+ const char *username=NULL;
+
+ init_enum_hnd(&hnd, 0);
+
+ /* if argc > 0, must be user command */
+ if (argc > 0)
+ username = smb_xstrdup(argv[0]);
+
+ result = cli_srvsvc_net_file_enum(
+ cli, mem_ctx, 3, username, &ctr, preferred_len, &hnd);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ /* Display results */
+
+ d_printf(
+ "\nEnumerating open files on remote server:\n\n"\
+ "\nFileId Opened by Perms Locks Path"\
+ "\n------ --------- ----- ----- ---- \n");
+ for (i = 0; i < ctr.num_entries; i++)
+ display_file_info_3(&ctr.file.info3[i].info_3,
+ &ctr.file.info3[i].info_3_str);
+ done:
+ return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+
+/**
+ * List files for a user on a remote RPC server
+ *
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+static int rpc_file_user(int argc, const char **argv)
+{
+ if (argc < 1) {
+ DEBUG(1, ("No username given\n"));
+ return(rpc_file_usage(argc, argv));
+ }
+
+ return run_rpc_command(NULL, PI_SRVSVC, 0,
+ rpc_file_list_internals,
+ argc, argv);
+}
+
+
+/**
+ * 'net rpc file' entrypoint.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ **/
+
+int net_rpc_file(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"close", rpc_file_close},
+ {"user", rpc_file_user},
+#if 0
+ {"info", rpc_file_info},
+#endif
+ {NULL, NULL}
+ };
+
+ if (argc == 0)
+ return run_rpc_command(NULL, PI_SRVSVC, 0,
+ rpc_file_list_internals,
+ argc, argv);
+
+ return net_run_function(argc, argv, func, rpc_file_usage);
+}
+
+/****************************************************************************/
+
+
+
+/**
+ * ABORT the shutdown of a remote RPC Server over, initshutdown pipe
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through.
+ *
+ * @param domain_sid The domain sid aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_shutdown_abort_internals(const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ result = cli_shutdown_abort(cli, mem_ctx);
+
+ if (NT_STATUS_IS_OK(result))
+ DEBUG(5,("cmd_shutdown_abort: query succeeded\n"));
+ else
+ DEBUG(5,("cmd_shutdown_abort: query failed\n"));
+
+ return result;
+}
+
+
+/**
+ * ABORT the shutdown of a remote RPC Server, over winreg pipe
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through.
+ *
+ * @param domain_sid The domain sid aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_reg_shutdown_abort_internals(const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ result = cli_reg_abort_shutdown(cli, mem_ctx);
+
+ if (NT_STATUS_IS_OK(result))
+ DEBUG(5,("cmd_reg_abort_shutdown: query succeeded\n"));
+ else
+ DEBUG(5,("cmd_reg_abort_shutdown: query failed\n"));
+
+ return result;
+}
+
+/**
+ * ABORT the Shut down of a remote RPC server
+ *
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+
+static int rpc_shutdown_abort(int argc, const char **argv)
+{
+ int rc = run_rpc_command(NULL, PI_SHUTDOWN, 0,
+ rpc_shutdown_abort_internals,
+ argc, argv);
+
+ if (rc == 0)
+ return rc;
+
+ DEBUG(1, ("initshutdown pipe didn't work, trying winreg pipe\n"));
+
+ return run_rpc_command(NULL, PI_WINREG, 0,
+ rpc_reg_shutdown_abort_internals,
+ argc, argv);
+}
+
+/**
+ * Shut down a remote RPC Server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation of the function.
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_shutdown_internals(const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ const char *msg = "This machine will be shutdown shortly";
+ uint32 timeout = 20;
+#if 0
+ poptContext pc;
+ int rc;
+
+ struct poptOption long_options[] = {
+ {"message", 'm', POPT_ARG_STRING, &msg},
+ {"timeout", 't', POPT_ARG_INT, &timeout},
+ {"reboot", 'r', POPT_ARG_NONE, &reboot},
+ {"force", 'f', POPT_ARG_NONE, &force},
+ { 0, 0, 0, 0}
+ };
+
+ pc = poptGetContext(NULL, argc, (const char **) argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ rc = poptGetNextOpt(pc);
+
+ if (rc < -1) {
+ /* an error occurred during option processing */
+ DEBUG(0, ("%s: %s\n",
+ poptBadOption(pc, POPT_BADOPTION_NOALIAS),
+ poptStrerror(rc)));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+#endif
+ if (opt_comment) {
+ msg = opt_comment;
+ }
+ if (opt_timeout) {
+ timeout = opt_timeout;
+ }
+
+ /* create an entry */
+ result = cli_reg_shutdown(cli, mem_ctx, msg, timeout, opt_reboot, opt_force);
+
+ if (NT_STATUS_IS_OK(result))
+ DEBUG(5,("Shutdown of remote machine succeeded\n"));
+ else
+ DEBUG(0,("Shutdown of remote machine failed!\n"));
+
+ return result;
+}
+
+/**
+ * Shut down a remote RPC server
+ *
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+
+static int rpc_shutdown(int argc, const char **argv)
+{
+ return run_rpc_command(NULL, PI_WINREG, 0, rpc_shutdown_internals,
+ argc, argv);
+}
+
+/***************************************************************************
+ NT Domain trusts code (i.e. 'net rpc trustdom' functionality)
+
+ ***************************************************************************/
+
+/**
+ * Add interdomain trust account to the RPC server.
+ * All parameters (except for argc and argv) are passed by run_rpc_command
+ * function.
+ *
+ * @param domain_sid The domain sid acquired from the server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return normal NTSTATUS return code
+ */
+
+static NTSTATUS rpc_trustdom_add_internals(const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv) {
+
+ POLICY_HND connect_pol, domain_pol, user_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ char *acct_name;
+ uint16 acb_info;
+ uint32 unknown, user_rid;
+
+ if (argc != 2) {
+ d_printf("Usage: net rpc trustdom add <domain_name> <pw>\n");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /*
+ * Make valid trusting domain account (ie. uppercased and with '$' appended)
+ */
+
+ if (asprintf(&acct_name, "%s$", argv[0]) < 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ strupper_m(acct_name);
+
+ /* Get samr policy handle */
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Get domain policy handle */
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ domain_sid, &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Create trusting domain's account */
+ acb_info = ACB_DOMTRUST;
+ unknown = 0xe00500b0; /* No idea what this is - a permission mask?
+ mimir: yes, most probably it is */
+
+ result = cli_samr_create_dom_user(cli, mem_ctx, &domain_pol,
+ acct_name, acb_info, unknown,
+ &user_pol, &user_rid);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ {
+ SAM_USERINFO_CTR ctr;
+ SAM_USER_INFO_24 p24;
+ uchar pwbuf[516];
+
+ encode_pw_buffer((char *)pwbuf, argv[1], STR_UNICODE);
+
+ ZERO_STRUCT(ctr);
+ ZERO_STRUCT(p24);
+
+ init_sam_user_info24(&p24, (char *)pwbuf, 24);
+
+ ctr.switch_value = 24;
+ ctr.info.id24 = &p24;
+
+ result = cli_samr_set_userinfo(cli, mem_ctx, &user_pol, 24,
+ &cli->user_session_key, &ctr);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0,("Could not set trust account password: %s\n",
+ nt_errstr(result)));
+ goto done;
+ }
+ }
+
+ done:
+ SAFE_FREE(acct_name);
+ return result;
+}
+
+/**
+ * Create interdomain trust account for a remote domain.
+ *
+ * @param argc standard argc
+ * @param argv standard argv without initial components
+ *
+ * @return Integer status (0 means success)
+ **/
+
+static int rpc_trustdom_add(int argc, const char **argv)
+{
+ if (argc > 0) {
+ return run_rpc_command(NULL, PI_SAMR, 0, rpc_trustdom_add_internals,
+ argc, argv);
+ } else {
+ d_printf("Usage: net rpc trustdom add <domain>\n");
+ return -1;
+ }
+}
+
+
+/**
+ * Delete interdomain trust account for a remote domain.
+ *
+ * @param argc standard argc
+ * @param argv standard argv without initial components
+ *
+ * @return Integer status (0 means success)
+ **/
+
+static int rpc_trustdom_del(int argc, const char **argv)
+{
+ d_printf("Sorry, not yet implemented.\n");
+ d_printf("Use 'smbpasswd -x -i' instead.\n");
+ return -1;
+}
+
+
+/**
+ * Establish trust relationship to a trusting domain.
+ * Interdomain account must already be created on remote PDC.
+ *
+ * @param argc standard argc
+ * @param argv standard argv without initial components
+ *
+ * @return Integer status (0 means success)
+ **/
+
+static int rpc_trustdom_establish(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ struct in_addr server_ip;
+ POLICY_HND connect_hnd;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS nt_status;
+ DOM_SID *domain_sid;
+ WKS_INFO_100 wks_info;
+
+ char* domain_name;
+ char* domain_name_pol;
+ char* acct_name;
+ fstring pdc_name;
+
+ /*
+ * Connect to \\server\ipc$ as 'our domain' account with password
+ */
+
+ if (argc != 1) {
+ d_printf("Usage: net rpc trustdom establish <domain_name>\n");
+ return -1;
+ }
+
+ domain_name = smb_xstrdup(argv[0]);
+ strupper_m(domain_name);
+
+ /* account name used at first is our domain's name with '$' */
+ asprintf(&acct_name, "%s$", lp_workgroup());
+ strupper_m(acct_name);
+
+ /*
+ * opt_workgroup will be used by connection functions further,
+ * hence it should be set to remote domain name instead of ours
+ */
+ if (opt_workgroup) {
+ opt_workgroup = smb_xstrdup(domain_name);
+ };
+
+ opt_user_name = acct_name;
+
+ /* find the domain controller */
+ if (!net_find_pdc(&server_ip, pdc_name, domain_name)) {
+ DEBUG(0, ("Couldn't find domain controller for domain %s\n", domain_name));
+ return -1;
+ }
+
+ /* connect to ipc$ as username/password */
+ nt_status = connect_to_ipc(&cli, &server_ip, pdc_name);
+ if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT)) {
+
+ /* Is it trusting domain account for sure ? */
+ DEBUG(0, ("Couldn't verify trusting domain account. Error was %s\n",
+ nt_errstr(nt_status)));
+ return -1;
+ }
+
+ /*
+ * Connect to \\server\ipc$ again (this time anonymously)
+ */
+
+ nt_status = connect_to_ipc_anonymous(&cli, &server_ip, (char*)pdc_name);
+
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("Couldn't connect to domain %s controller. Error was %s.\n",
+ domain_name, nt_errstr(nt_status)));
+ }
+
+ /*
+ * Use NetServerEnum2 to make sure we're talking to a proper server
+ */
+
+ if (!cli_get_pdc_name(cli, domain_name, (char*)pdc_name)) {
+ DEBUG(0, ("NetServerEnum2 error: Couldn't find primary domain controller\
+ for domain %s\n", domain_name));
+ }
+
+ /*
+ * Call WksQueryInfo to check remote server's capabilities
+ * note: It is now used only to get unicode domain name
+ */
+
+ if (!cli_nt_session_open(cli, PI_WKSSVC)) {
+ DEBUG(0, ("Couldn't not initialise wkssvc pipe\n"));
+ return -1;
+ }
+
+ if (!(mem_ctx = talloc_init("establishing trust relationship to domain %s",
+ domain_name))) {
+ DEBUG(0, ("talloc_init() failed\n"));
+ cli_shutdown(cli);
+ return -1;
+ }
+
+ nt_status = cli_wks_query_info(cli, mem_ctx, &wks_info);
+
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("WksQueryInfo call failed.\n"));
+ return -1;
+ }
+
+ if (cli->nt_pipe_fnum)
+ cli_nt_session_close(cli);
+
+
+ /*
+ * Call LsaOpenPolicy and LsaQueryInfo
+ */
+
+ if (!(mem_ctx = talloc_init("rpc_trustdom_establish"))) {
+ DEBUG(0, ("talloc_init() failed\n"));
+ cli_shutdown(cli);
+ return -1;
+ }
+
+ if (!cli_nt_session_open(cli, PI_LSARPC)) {
+ DEBUG(0, ("Could not initialise lsa pipe\n"));
+ cli_shutdown(cli);
+ return -1;
+ }
+
+ nt_status = cli_lsa_open_policy2(cli, mem_ctx, True, SEC_RIGHTS_QUERY_VALUE,
+ &connect_hnd);
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("Couldn't open policy handle. Error was %s\n",
+ nt_errstr(nt_status)));
+ return -1;
+ }
+
+ /* Querying info level 5 */
+
+ nt_status = cli_lsa_query_info_policy(cli, mem_ctx, &connect_hnd,
+ 5 /* info level */, &domain_name_pol,
+ &domain_sid);
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("LSA Query Info failed. Returned error was %s\n",
+ nt_errstr(nt_status)));
+ return -1;
+ }
+
+
+
+
+ /* There should be actually query info level 3 (following nt serv behaviour),
+ but I still don't know if it's _really_ necessary */
+
+ /*
+ * Store the password in secrets db
+ */
+
+ if (!secrets_store_trusted_domain_password(domain_name, wks_info.uni_lan_grp.buffer,
+ wks_info.uni_lan_grp.uni_str_len, opt_password,
+ *domain_sid)) {
+ DEBUG(0, ("Storing password for trusted domain failed.\n"));
+ return -1;
+ }
+
+ /*
+ * Close the pipes and clean up
+ */
+
+ nt_status = cli_lsa_close(cli, mem_ctx, &connect_hnd);
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("Couldn't close LSA pipe. Error was %s\n",
+ nt_errstr(nt_status)));
+ return -1;
+ }
+
+ if (cli->nt_pipe_fnum)
+ cli_nt_session_close(cli);
+
+ talloc_destroy(mem_ctx);
+
+ d_printf("Trust to domain %s established\n", domain_name);
+ return 0;
+}
+
+/**
+ * Revoke trust relationship to the remote domain
+ *
+ * @param argc standard argc
+ * @param argv standard argv without initial components
+ *
+ * @return Integer status (0 means success)
+ **/
+
+static int rpc_trustdom_revoke(int argc, const char **argv)
+{
+ char* domain_name;
+
+ if (argc < 1) return -1;
+
+ /* generate upper cased domain name */
+ domain_name = smb_xstrdup(argv[0]);
+ strupper_m(domain_name);
+
+ /* delete password of the trust */
+ if (!trusted_domain_password_delete(domain_name)) {
+ DEBUG(0, ("Failed to revoke relationship to the trusted domain %s\n",
+ domain_name));
+ return -1;
+ };
+
+ return 0;
+}
+
+/**
+ * Usage for 'net rpc trustdom' command
+ *
+ * @param argc standard argc
+ * @param argv standard argv without inital components
+ *
+ * @return Integer status returned to shell
+ **/
+
+static int rpc_trustdom_usage(int argc, const char **argv)
+{
+ d_printf(" net rpc trustdom add \t\t add trusting domain's account\n");
+ d_printf(" net rpc trustdom del \t\t delete trusting domain's account\n");
+ d_printf(" net rpc trustdom establish \t establish relationship to trusted domain\n");
+ d_printf(" net rpc trustdom revoke \t abandon relationship to trusted domain\n");
+ d_printf(" net rpc trustdom list \t show current interdomain trust relationships\n");
+ return -1;
+}
+
+
+static NTSTATUS rpc_query_domain_sid(const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ fstring str_sid;
+ sid_to_string(str_sid, domain_sid);
+ d_printf("%s\n", str_sid);
+ return NT_STATUS_OK;
+}
+
+
+static int rpc_trustdom_list(int argc, const char **argv)
+{
+ /* common variables */
+ TALLOC_CTX* mem_ctx;
+ struct cli_state *cli, *remote_cli;
+ NTSTATUS nt_status;
+ const char *domain_name = NULL;
+ DOM_SID *queried_dom_sid;
+ fstring ascii_sid, padding;
+ int ascii_dom_name_len;
+ POLICY_HND connect_hnd;
+
+ /* trusted domains listing variables */
+ unsigned int num_domains, enum_ctx = 0;
+ int i, pad_len, col_len = 20;
+ DOM_SID *domain_sids;
+ char **trusted_dom_names;
+ fstring pdc_name;
+ char *dummy;
+
+ /* trusting domains listing variables */
+ POLICY_HND domain_hnd;
+ char **trusting_dom_names;
+ uint32 *trusting_dom_rids;
+
+ /*
+ * Listing trusted domains (stored in secrets.tdb, if local)
+ */
+
+ mem_ctx = talloc_init("trust relationships listing");
+
+ /*
+ * set domain and pdc name to local samba server (default)
+ * or to remote one given in command line
+ */
+
+ if (StrCaseCmp(opt_workgroup, lp_workgroup())) {
+ domain_name = opt_workgroup;
+ opt_target_workgroup = opt_workgroup;
+ } else {
+ fstrcpy(pdc_name, global_myname());
+ domain_name = talloc_strdup(mem_ctx, lp_workgroup());
+ opt_target_workgroup = domain_name;
+ };
+
+ /* open \PIPE\lsarpc and open policy handle */
+ if (!(cli = net_make_ipc_connection(NET_FLAGS_PDC))) {
+ DEBUG(0, ("Couldn't connect to domain controller\n"));
+ return -1;
+ };
+
+ if (!cli_nt_session_open(cli, PI_LSARPC)) {
+ DEBUG(0, ("Could not initialise lsa pipe\n"));
+ return -1;
+ };
+
+ nt_status = cli_lsa_open_policy2(cli, mem_ctx, False, SEC_RIGHTS_QUERY_VALUE,
+ &connect_hnd);
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("Couldn't open policy handle. Error was %s\n",
+ nt_errstr(nt_status)));
+ return -1;
+ };
+
+ /* query info level 5 to obtain sid of a domain being queried */
+ nt_status = cli_lsa_query_info_policy(
+ cli, mem_ctx, &connect_hnd, 5 /* info level */,
+ &dummy, &queried_dom_sid);
+
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("LSA Query Info failed. Returned error was %s\n",
+ nt_errstr(nt_status)));
+ return -1;
+ }
+
+ /*
+ * Keep calling LsaEnumTrustdom over opened pipe until
+ * the end of enumeration is reached
+ */
+
+ d_printf("Trusted domains list:\n\n");
+
+ do {
+ nt_status = cli_lsa_enum_trust_dom(cli, mem_ctx, &connect_hnd, &enum_ctx,
+ &num_domains,
+ &trusted_dom_names, &domain_sids);
+
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("Couldn't enumerate trusted domains. Error was %s\n",
+ nt_errstr(nt_status)));
+ return -1;
+ };
+
+ for (i = 0; i < num_domains; i++) {
+ /* convert sid into ascii string */
+ sid_to_string(ascii_sid, &(domain_sids[i]));
+
+ /* calculate padding space for d_printf to look nicer */
+ pad_len = col_len - strlen(trusted_dom_names[i]);
+ padding[pad_len] = 0;
+ do padding[--pad_len] = ' '; while (pad_len);
+
+ d_printf("%s%s%s\n", trusted_dom_names[i], padding, ascii_sid);
+ };
+
+ /*
+ * in case of no trusted domains say something rather
+ * than just display blank line
+ */
+ if (!num_domains) d_printf("none\n");
+
+ } while (NT_STATUS_EQUAL(nt_status, STATUS_MORE_ENTRIES));
+
+ /* close this connection before doing next one */
+ nt_status = cli_lsa_close(cli, mem_ctx, &connect_hnd);
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("Couldn't properly close lsa policy handle. Error was %s\n",
+ nt_errstr(nt_status)));
+ return -1;
+ };
+
+ cli_nt_session_close(cli);
+
+ /*
+ * Listing trusting domains (stored in passdb backend, if local)
+ */
+
+ d_printf("\nTrusting domains list:\n\n");
+
+ /*
+ * Open \PIPE\samr and get needed policy handles
+ */
+ if (!cli_nt_session_open(cli, PI_SAMR)) {
+ DEBUG(0, ("Could not initialise samr pipe\n"));
+ return -1;
+ };
+
+ /* SamrConnect */
+ nt_status = cli_samr_connect(cli, mem_ctx, SA_RIGHT_SAM_OPEN_DOMAIN,
+ &connect_hnd);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Couldn't open SAMR policy handle. Error was %s\n",
+ nt_errstr(nt_status)));
+ return -1;
+ };
+
+ /* SamrOpenDomain - we have to open domain policy handle in order to be
+ able to enumerate accounts*/
+ nt_status = cli_samr_open_domain(cli, mem_ctx, &connect_hnd,
+ SA_RIGHT_DOMAIN_ENUM_ACCOUNTS,
+ queried_dom_sid, &domain_hnd);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Couldn't open domain object. Error was %s\n",
+ nt_errstr(nt_status)));
+ return -1;
+ };
+
+ /*
+ * perform actual enumeration
+ */
+
+ enum_ctx = 0; /* reset enumeration context from last enumeration */
+ do {
+
+ nt_status = cli_samr_enum_dom_users(cli, mem_ctx, &domain_hnd,
+ &enum_ctx, ACB_DOMTRUST, 0xffff,
+ &trusting_dom_names, &trusting_dom_rids,
+ &num_domains);
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("Couldn't enumerate accounts. Error was: %s\n",
+ nt_errstr(nt_status)));
+ return -1;
+ };
+
+ for (i = 0; i < num_domains; i++) {
+
+ /*
+ * get each single domain's sid (do we _really_ need this ?):
+ * 1) connect to domain's pdc
+ * 2) query the pdc for domain's sid
+ */
+
+ /* get rid of '$' tail */
+ ascii_dom_name_len = strlen(trusting_dom_names[i]);
+ if (ascii_dom_name_len && ascii_dom_name_len < FSTRING_LEN)
+ trusting_dom_names[i][ascii_dom_name_len - 1] = '\0';
+
+ /* calculate padding space for d_printf to look nicer */
+ pad_len = col_len - strlen(trusting_dom_names[i]);
+ padding[pad_len] = 0;
+ do padding[--pad_len] = ' '; while (pad_len);
+
+ /* set opt_* variables to remote domain */
+ strupper_m(trusting_dom_names[i]);
+ opt_workgroup = talloc_strdup(mem_ctx, trusting_dom_names[i]);
+ opt_target_workgroup = opt_workgroup;
+
+ d_printf("%s%s", trusting_dom_names[i], padding);
+
+ /* connect to remote domain controller */
+ remote_cli = net_make_ipc_connection(NET_FLAGS_PDC | NET_FLAGS_ANONYMOUS);
+ if (remote_cli) {
+ /* query for domain's sid */
+ if (run_rpc_command(remote_cli, PI_LSARPC, 0, rpc_query_domain_sid, argc, argv))
+ d_printf("couldn't get domain's sid\n");
+
+ cli_shutdown(remote_cli);
+
+ } else {
+ d_printf("domain controller is not responding\n");
+ };
+ };
+
+ if (!num_domains) d_printf("none\n");
+
+ } while (NT_STATUS_EQUAL(nt_status, STATUS_MORE_ENTRIES));
+
+ /* close opened samr and domain policy handles */
+ nt_status = cli_samr_close(cli, mem_ctx, &domain_hnd);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Couldn't properly close domain policy handle for domain %s\n", domain_name));
+ };
+
+ nt_status = cli_samr_close(cli, mem_ctx, &connect_hnd);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Couldn't properly close samr policy handle for domain %s\n", domain_name));
+ };
+
+ /* close samr pipe and connection to IPC$ */
+ cli_nt_session_close(cli);
+ cli_shutdown(cli);
+
+ talloc_destroy(mem_ctx);
+ return 0;
+}
+
+/**
+ * Entrypoint for 'net rpc trustdom' code
+ *
+ * @param argc standard argc
+ * @param argv standard argv without initial components
+ *
+ * @return Integer status (0 means success)
+ */
+
+static int rpc_trustdom(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"add", rpc_trustdom_add},
+ {"del", rpc_trustdom_del},
+ {"establish", rpc_trustdom_establish},
+ {"revoke", rpc_trustdom_revoke},
+ {"help", rpc_trustdom_usage},
+ {"list", rpc_trustdom_list},
+ {NULL, NULL}
+ };
+
+ if (argc == 0) {
+ rpc_trustdom_usage(argc, argv);
+ return -1;
+ }
+
+ return (net_run_function(argc, argv, func, rpc_user_usage));
+}
+
+/**
+ * Check if a server will take rpc commands
+ * @param flags Type of server to connect to (PDC, DMB, localhost)
+ * if the host is not explicitly specified
+ * @return BOOL (true means rpc supported)
+ */
+BOOL net_rpc_check(unsigned flags)
+{
+ struct cli_state cli;
+ BOOL ret = False;
+ struct in_addr server_ip;
+ char *server_name = NULL;
+
+ /* flags (i.e. server type) may depend on command */
+ if (!net_find_server(flags, &server_ip, &server_name))
+ return False;
+
+ ZERO_STRUCT(cli);
+ if (cli_initialise(&cli) == False)
+ return False;
+
+ if (!cli_connect(&cli, server_name, &server_ip))
+ goto done;
+ if (!attempt_netbios_session_request(&cli, global_myname(),
+ server_name, &server_ip))
+ goto done;
+ if (!cli_negprot(&cli))
+ goto done;
+ if (cli.protocol < PROTOCOL_NT1)
+ goto done;
+
+ ret = True;
+ done:
+ cli_shutdown(&cli);
+ return ret;
+}
+
+/* dump sam database via samsync rpc calls */
+static int rpc_samdump(int argc, const char **argv) {
+ return run_rpc_command(NULL, PI_NETLOGON, NET_FLAGS_ANONYMOUS, rpc_samdump_internals,
+ argc, argv);
+}
+
+/* syncronise sam database via samsync rpc calls */
+static int rpc_vampire(int argc, const char **argv) {
+ return run_rpc_command(NULL, PI_NETLOGON, NET_FLAGS_ANONYMOUS, rpc_vampire_internals,
+ argc, argv);
+}
+/****************************************************************************/
+
+
+/**
+ * Basic usage function for 'net rpc'
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ **/
+
+int net_rpc_usage(int argc, const char **argv)
+{
+ d_printf(" net rpc info \t\t\tshow basic info about a domain \n");
+ d_printf(" net rpc join \t\t\tto join a domain \n");
+ d_printf(" net rpc oldjoin \t\t\tto join a domain created in server manager\n\n\n");
+ d_printf(" net rpc testjoin \t\ttests that a join is valid\n");
+ d_printf(" net rpc user \t\t\tto add, delete and list users\n");
+ d_printf(" net rpc password <username> [<password>] -Uadmin_username%%admin_pass");
+ d_printf(" net rpc group \t\tto list groups\n");
+ d_printf(" net rpc share \t\tto add, delete, and list shares\n");
+ d_printf(" net rpc file \t\t\tto list open files\n");
+ d_printf(" net rpc changetrustpw \tto change the trust account password\n");
+ d_printf(" net rpc getsid \t\tfetch the domain sid into the local secrets.tdb\n");
+ d_printf(" net rpc vampire \t\tsyncronise an NT PDC's users and groups into the local passdb\n");
+ d_printf(" net rpc samdump \t\tdiplay an NT PDC's users, groups and other data\n");
+ d_printf(" net rpc trustdom \t\tto create trusting domain's account\n"
+ "\t\t\t\t\tor establish trust\n");
+ d_printf(" net rpc abortshutdown \tto abort the shutdown of a remote server\n");
+ d_printf(" net rpc shutdown \t\tto shutdown a remote server\n");
+ d_printf("\n");
+ d_printf("'net rpc shutdown' also accepts the following miscellaneous options:\n"); /* misc options */
+ d_printf("\t-r or --reboot\trequest remote server reboot on shutdown\n");
+ d_printf("\t-f or --force\trequest the remote server force its shutdown\n");
+ d_printf("\t-t or --timeout=<timeout>\tnumber of seconds before shutdown\n");
+ d_printf("\t-c or --comment=<message>\ttext message to display on impending shutdown\n");
+ return -1;
+}
+
+
+/**
+ * Help function for 'net rpc'. Calls command specific help if requested
+ * or displays usage of net rpc
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ **/
+
+int net_rpc_help(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"join", rpc_join_usage},
+ {"user", rpc_user_usage},
+ {"group", rpc_group_usage},
+ {"share", rpc_share_usage},
+ /*{"changetrustpw", rpc_changetrustpw_usage}, */
+ {"trustdom", rpc_trustdom_usage},
+ /*{"abortshutdown", rpc_shutdown_abort_usage},*/
+ /*{"shutdown", rpc_shutdown_usage}, */
+ {NULL, NULL}
+ };
+
+ if (argc == 0) {
+ net_rpc_usage(argc, argv);
+ return -1;
+ }
+
+ return (net_run_function(argc, argv, func, rpc_user_usage));
+}
+
+
+/**
+ * 'net rpc' entrypoint.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ **/
+
+int net_rpc(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"info", net_rpc_info},
+ {"join", net_rpc_join},
+ {"oldjoin", net_rpc_oldjoin},
+ {"testjoin", net_rpc_testjoin},
+ {"user", net_rpc_user},
+ {"password", rpc_user_password},
+ {"group", net_rpc_group},
+ {"share", net_rpc_share},
+ {"file", net_rpc_file},
+ {"changetrustpw", net_rpc_changetrustpw},
+ {"trustdom", rpc_trustdom},
+ {"abortshutdown", rpc_shutdown_abort},
+ {"shutdown", rpc_shutdown},
+ {"samdump", rpc_samdump},
+ {"vampire", rpc_vampire},
+ {"getsid", net_rpc_getsid},
+ {"help", net_rpc_help},
+ {NULL, NULL}
+ };
+ return net_run_function(argc, argv, func, net_rpc_usage);
+}
diff --git a/source/utils/net_rpc_join.c b/source/utils/net_rpc_join.c
new file mode 100644
index 00000000000..52e295949e0
--- /dev/null
+++ b/source/utils/net_rpc_join.c
@@ -0,0 +1,389 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org)
+ Copyright (C) Tim Potter 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "includes.h"
+#include "../utils/net.h"
+
+/* Macro for checking RPC error codes to make things more readable */
+
+#define CHECK_RPC_ERR(rpc, msg) \
+ if (!NT_STATUS_IS_OK(result = rpc)) { \
+ DEBUG(0, (msg ": %s\n", nt_errstr(result))); \
+ goto done; \
+ }
+
+#define CHECK_RPC_ERR_DEBUG(rpc, debug_args) \
+ if (!NT_STATUS_IS_OK(result = rpc)) { \
+ DEBUG(0, debug_args); \
+ goto done; \
+ }
+
+
+/**
+ * confirm that a domain join is still valid
+ *
+ * @return A shell status integer (0 for success)
+ *
+ **/
+static int net_rpc_join_ok(const char *domain)
+{
+ struct cli_state *cli;
+ uchar stored_md4_trust_password[16];
+ int retval = 1;
+ uint32 channel;
+ NTSTATUS result;
+
+ /* Connect to remote machine */
+ if (!(cli = net_make_ipc_connection(NET_FLAGS_ANONYMOUS | NET_FLAGS_PDC))) {
+ return 1;
+ }
+
+ if (!cli_nt_session_open(cli, PI_NETLOGON)) {
+ DEBUG(0,("Error connecting to NETLOGON pipe\n"));
+ goto done;
+ }
+
+ if (!secrets_fetch_trust_account_password(domain,
+ stored_md4_trust_password,
+ NULL, &channel)) {
+ DEBUG(0,("Could not retreive domain trust secret"));
+ goto done;
+ }
+
+ /* ensure that schannel uses the right domain */
+ fstrcpy(cli->domain, domain);
+ if (! NT_STATUS_IS_OK(result = cli_nt_establish_netlogon(cli, channel, stored_md4_trust_password))) {
+ DEBUG(0,("Error in domain join verfication (fresh connection)\n"));
+ goto done;
+ }
+
+ retval = 0; /* Success! */
+
+done:
+ /* Close down pipe - this will clean up open policy handles */
+ if (cli->nt_pipe_fnum)
+ cli_nt_session_close(cli);
+
+ cli_shutdown(cli);
+
+ return retval;
+}
+
+/**
+ * Join a domain using the administrator username and password
+ *
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped. Currently not used.
+ * @return A shell status integer (0 for success)
+ *
+ **/
+
+int net_rpc_join_newstyle(int argc, const char **argv)
+{
+
+ /* libsmb variables */
+
+ struct cli_state *cli;
+ TALLOC_CTX *mem_ctx;
+ uint32 acb_info = ACB_WSTRUST;
+ uint32 sec_channel_type;
+
+ /* rpc variables */
+
+ POLICY_HND lsa_pol, sam_pol, domain_pol, user_pol;
+ DOM_SID *domain_sid;
+ uint32 user_rid;
+
+ /* Password stuff */
+
+ char *clear_trust_password = NULL;
+ uchar pwbuf[516];
+ SAM_USERINFO_CTR ctr;
+ SAM_USER_INFO_24 p24;
+ SAM_USER_INFO_10 p10;
+ uchar md4_trust_password[16];
+
+ /* Misc */
+
+ NTSTATUS result;
+ int retval = 1;
+ char *domain;
+ uint32 num_rids, *name_types, *user_rids;
+ uint32 flags = 0x3e8;
+ char *acct_name;
+ const char *const_acct_name;
+
+ /* check what type of join */
+ if (argc >= 0) {
+ sec_channel_type = get_sec_channel_type(argv[0]);
+ } else {
+ sec_channel_type = get_sec_channel_type(NULL);
+ }
+
+ switch (sec_channel_type) {
+ case SEC_CHAN_WKSTA:
+ acb_info = ACB_WSTRUST;
+ break;
+ case SEC_CHAN_BDC:
+ acb_info = ACB_SVRTRUST;
+ break;
+#if 0
+ case SEC_CHAN_DOMAIN:
+ acb_info = ACB_DOMTRUST;
+ break;
+#endif
+ }
+
+ /* Connect to remote machine */
+
+ if (!(cli = net_make_ipc_connection(NET_FLAGS_PDC)))
+ return 1;
+
+ if (!(mem_ctx = talloc_init("net_rpc_join_newstyle"))) {
+ DEBUG(0, ("Could not initialise talloc context\n"));
+ goto done;
+ }
+
+ /* Fetch domain sid */
+
+ if (!cli_nt_session_open(cli, PI_LSARPC)) {
+ DEBUG(0, ("Error connecting to LSA pipe\n"));
+ goto done;
+ }
+
+
+ CHECK_RPC_ERR(cli_lsa_open_policy(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &lsa_pol),
+ "error opening lsa policy handle");
+
+ CHECK_RPC_ERR(cli_lsa_query_info_policy(cli, mem_ctx, &lsa_pol,
+ 5, &domain, &domain_sid),
+ "error querying info policy");
+
+ cli_lsa_close(cli, mem_ctx, &lsa_pol);
+
+ cli_nt_session_close(cli); /* Done with this pipe */
+
+ /* Create domain user */
+ if (!cli_nt_session_open(cli, PI_SAMR)) {
+ DEBUG(0, ("Error connecting to SAM pipe\n"));
+ goto done;
+ }
+
+ CHECK_RPC_ERR(cli_samr_connect(cli, mem_ctx,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &sam_pol),
+ "could not connect to SAM database");
+
+
+ CHECK_RPC_ERR(cli_samr_open_domain(cli, mem_ctx, &sam_pol,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ domain_sid, &domain_pol),
+ "could not open domain");
+
+ /* Create domain user */
+ acct_name = talloc_asprintf(mem_ctx, "%s$", global_myname());
+ strlower_m(acct_name);
+ const_acct_name = acct_name;
+
+ result = cli_samr_create_dom_user(cli, mem_ctx, &domain_pol,
+ acct_name, acb_info,
+ 0xe005000b, &user_pol,
+ &user_rid);
+
+ if (!NT_STATUS_IS_OK(result) &&
+ !NT_STATUS_EQUAL(result, NT_STATUS_USER_EXISTS)) {
+ d_printf("Create of workstation account failed\n");
+
+ /* If NT_STATUS_ACCESS_DENIED then we have a valid
+ username/password combo but the user does not have
+ administrator access. */
+
+ if (NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED))
+ d_printf("User specified does not have administrator privileges\n");
+
+ goto done;
+ }
+
+ /* We *must* do this.... don't ask... */
+
+ if (NT_STATUS_IS_OK(result))
+ cli_samr_close(cli, mem_ctx, &user_pol);
+
+ CHECK_RPC_ERR_DEBUG(cli_samr_lookup_names(cli, mem_ctx,
+ &domain_pol, flags,
+ 1, &const_acct_name,
+ &num_rids,
+ &user_rids, &name_types),
+ ("error looking up rid for user %s: %s\n",
+ acct_name, nt_errstr(result)));
+
+ if (name_types[0] != SID_NAME_USER) {
+ DEBUG(0, ("%s is not a user account (type=%d)\n", acct_name, name_types[0]));
+ goto done;
+ }
+
+ user_rid = user_rids[0];
+
+ /* Open handle on user */
+
+ CHECK_RPC_ERR_DEBUG(
+ cli_samr_open_user(cli, mem_ctx, &domain_pol,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ user_rid, &user_pol),
+ ("could not re-open existing user %s: %s\n",
+ acct_name, nt_errstr(result)));
+
+ /* Create a random machine account password */
+
+ {
+ char *str;
+ str = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
+ clear_trust_password = strdup(str);
+ E_md4hash(clear_trust_password, md4_trust_password);
+ }
+
+ encode_pw_buffer(pwbuf, clear_trust_password, STR_UNICODE);
+
+ /* Set password on machine account */
+
+ ZERO_STRUCT(ctr);
+ ZERO_STRUCT(p24);
+
+ init_sam_user_info24(&p24, (char *)pwbuf,24);
+
+ ctr.switch_value = 24;
+ ctr.info.id24 = &p24;
+
+ CHECK_RPC_ERR(cli_samr_set_userinfo(cli, mem_ctx, &user_pol, 24,
+ &cli->user_session_key, &ctr),
+ "error setting trust account password");
+
+ /* Why do we have to try to (re-)set the ACB to be the same as what
+ we passed in the samr_create_dom_user() call? When a NT
+ workstation is joined to a domain by an administrator the
+ acb_info is set to 0x80. For a normal user with "Add
+ workstations to the domain" rights the acb_info is 0x84. I'm
+ not sure whether it is supposed to make a difference or not. NT
+ seems to cope with either value so don't bomb out if the set
+ userinfo2 level 0x10 fails. -tpot */
+
+ ZERO_STRUCT(ctr);
+ ctr.switch_value = 0x10;
+ ctr.info.id10 = &p10;
+
+ init_sam_user_info10(&p10, acb_info);
+
+ /* Ignoring the return value is necessary for joining a domain
+ as a normal user with "Add workstation to domain" privilege. */
+
+ result = cli_samr_set_userinfo2(cli, mem_ctx, &user_pol, 0x10,
+ &cli->user_session_key, &ctr);
+
+ /* Now check the whole process from top-to-bottom */
+ cli_samr_close(cli, mem_ctx, &user_pol);
+ cli_nt_session_close(cli); /* Done with this pipe */
+
+ if (!cli_nt_session_open(cli, PI_NETLOGON)) {
+ DEBUG(0,("Error connecting to NETLOGON pipe\n"));
+ goto done;
+ }
+
+ /* ensure that schannel uses the right domain */
+ fstrcpy(cli->domain, domain);
+
+ result = cli_nt_establish_netlogon(cli, sec_channel_type,
+ md4_trust_password);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0, ("Error domain join verification (reused connection): %s\n\n",
+ nt_errstr(result)));
+
+ if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) &&
+ (sec_channel_type == SEC_CHAN_BDC) ) {
+ d_printf("Please make sure that no computer account\n"
+ "named like this machine (%s) exists in the domain\n",
+ global_myname());
+ }
+
+ goto done;
+ }
+
+ /* Now store the secret in the secrets database */
+
+ strupper_m(domain);
+
+ if (!secrets_store_domain_sid(domain, domain_sid)) {
+ DEBUG(0, ("error storing domain sid for %s\n", domain));
+ goto done;
+ }
+
+ if (!secrets_store_machine_password(clear_trust_password, domain, sec_channel_type)) {
+ DEBUG(0, ("error storing plaintext domain secrets for %s\n", domain));
+ }
+
+ /* double-check, connection from scratch */
+ retval = net_rpc_join_ok(domain);
+
+done:
+ /* Close down pipe - this will clean up open policy handles */
+
+ if (cli->nt_pipe_fnum)
+ cli_nt_session_close(cli);
+
+ /* Display success or failure */
+
+ if (retval != 0) {
+ fprintf(stderr,"Unable to join domain %s.\n",domain);
+ } else {
+ printf("Joined domain %s.\n",domain);
+ }
+
+ cli_shutdown(cli);
+
+ SAFE_FREE(clear_trust_password);
+
+ return retval;
+}
+
+
+/**
+ * check that a join is OK
+ *
+ * @return A shell status integer (0 for success)
+ *
+ **/
+int net_rpc_testjoin(int argc, const char **argv)
+{
+ char *domain = smb_xstrdup(opt_target_workgroup);
+
+ /* Display success or failure */
+ if (net_rpc_join_ok(domain) != 0) {
+ fprintf(stderr,"Join to domain '%s' is not valid\n",domain);
+ free(domain);
+ return -1;
+ }
+
+ printf("Join to '%s' is OK\n",domain);
+ free(domain);
+ return 0;
+}
diff --git a/source/utils/net_rpc_samsync.c b/source/utils/net_rpc_samsync.c
new file mode 100644
index 00000000000..882f3a02bc2
--- /dev/null
+++ b/source/utils/net_rpc_samsync.c
@@ -0,0 +1,1080 @@
+/*
+ Unix SMB/CIFS implementation.
+ dump the remote SAM using rpc samsync operations
+
+ Copyright (C) Andrew Tridgell 2002
+ Copyright (C) Tim Potter 2001,2002
+ Modified by Volker Lendecke 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "../utils/net.h"
+
+extern DOM_SID global_sid_Builtin;
+
+static void display_group_mem_info(uint32 rid, SAM_GROUP_MEM_INFO *g)
+{
+ int i;
+ d_printf("Group mem %u: ", rid);
+ for (i=0;i<g->num_members;i++) {
+ d_printf("%u ", g->rids[i]);
+ }
+ d_printf("\n");
+}
+
+static void display_alias_info(uint32 rid, SAM_ALIAS_INFO *a)
+{
+ d_printf("Alias '%s' ", unistr2_static(&a->uni_als_name));
+ d_printf("desc='%s' rid=%u\n", unistr2_static(&a->uni_als_desc), a->als_rid);
+}
+
+static void display_alias_mem(uint32 rid, SAM_ALIAS_MEM_INFO *a)
+{
+ int i;
+ d_printf("Alias rid %u: ", rid);
+ for (i=0;i<a->num_members;i++) {
+ d_printf("%s ", sid_string_static(&a->sids[i].sid));
+ }
+ d_printf("\n");
+}
+
+static void display_account_info(uint32 rid, SAM_ACCOUNT_INFO *a)
+{
+ fstring hex_nt_passwd, hex_lm_passwd;
+ uchar lm_passwd[16], nt_passwd[16];
+ static uchar zero_buf[16];
+
+ /* Decode hashes from password hash (if they are not NULL) */
+
+ if (memcmp(a->pass.buf_lm_pwd, zero_buf, 16) != 0) {
+ sam_pwd_hash(a->user_rid, a->pass.buf_lm_pwd, lm_passwd, 0);
+ pdb_sethexpwd(hex_lm_passwd, lm_passwd, a->acb_info);
+ } else {
+ pdb_sethexpwd(hex_lm_passwd, NULL, 0);
+ }
+
+ if (memcmp(a->pass.buf_nt_pwd, zero_buf, 16) != 0) {
+ sam_pwd_hash(a->user_rid, a->pass.buf_nt_pwd, nt_passwd, 0);
+ pdb_sethexpwd(hex_nt_passwd, nt_passwd, a->acb_info);
+ } else {
+ pdb_sethexpwd(hex_nt_passwd, NULL, 0);
+ }
+
+ printf("%s:%d:%s:%s:%s:LCT-0\n", unistr2_static(&a->uni_acct_name),
+ a->user_rid, hex_lm_passwd, hex_nt_passwd,
+ pdb_encode_acct_ctrl(a->acb_info, NEW_PW_FORMAT_SPACE_PADDED_LEN));
+}
+
+static void display_domain_info(SAM_DOMAIN_INFO *a)
+{
+ d_printf("Domain name: %s\n", unistr2_static(&a->uni_dom_name));
+}
+
+static void display_group_info(uint32 rid, SAM_GROUP_INFO *a)
+{
+ d_printf("Group '%s' ", unistr2_static(&a->uni_grp_name));
+ d_printf("desc='%s', rid=%u\n", unistr2_static(&a->uni_grp_desc), rid);
+}
+
+static void display_sam_entry(SAM_DELTA_HDR *hdr_delta, SAM_DELTA_CTR *delta)
+{
+ switch (hdr_delta->type) {
+ case SAM_DELTA_ACCOUNT_INFO:
+ display_account_info(hdr_delta->target_rid, &delta->account_info);
+ break;
+ case SAM_DELTA_GROUP_MEM:
+ display_group_mem_info(hdr_delta->target_rid, &delta->grp_mem_info);
+ break;
+ case SAM_DELTA_ALIAS_INFO:
+ display_alias_info(hdr_delta->target_rid, &delta->alias_info);
+ break;
+ case SAM_DELTA_ALIAS_MEM:
+ display_alias_mem(hdr_delta->target_rid, &delta->als_mem_info);
+ break;
+ case SAM_DELTA_DOMAIN_INFO:
+ display_domain_info(&delta->domain_info);
+ break;
+ case SAM_DELTA_GROUP_INFO:
+ display_group_info(hdr_delta->target_rid, &delta->group_info);
+ break;
+ /* The following types are recognised but not handled */
+ case SAM_DELTA_RENAME_GROUP:
+ d_printf("SAM_DELTA_RENAME_GROUP not handled\n");
+ break;
+ case SAM_DELTA_RENAME_USER:
+ d_printf("SAM_DELTA_RENAME_USER not handled\n");
+ break;
+ case SAM_DELTA_RENAME_ALIAS:
+ d_printf("SAM_DELTA_RENAME_ALIAS not handled\n");
+ break;
+ case SAM_DELTA_POLICY_INFO:
+ d_printf("SAM_DELTA_POLICY_INFO not handled\n");
+ break;
+ case SAM_DELTA_TRUST_DOMS:
+ d_printf("SAM_DELTA_TRUST_DOMS not handled\n");
+ break;
+ case SAM_DELTA_PRIVS_INFO:
+ d_printf("SAM_DELTA_PRIVS_INFO not handled\n");
+ break;
+ case SAM_DELTA_SECRET_INFO:
+ d_printf("SAM_DELTA_SECRET_INFO not handled\n");
+ break;
+ case SAM_DELTA_DELETE_GROUP:
+ d_printf("SAM_DELTA_DELETE_GROUP not handled\n");
+ break;
+ case SAM_DELTA_DELETE_USER:
+ d_printf("SAM_DELTA_DELETE_USER not handled\n");
+ break;
+ case SAM_DELTA_MODIFIED_COUNT:
+ d_printf("SAM_DELTA_MODIFIED_COUNT not handled\n");
+ break;
+ default:
+ d_printf("Unknown delta record type %d\n", hdr_delta->type);
+ break;
+ }
+}
+
+
+static void dump_database(struct cli_state *cli, unsigned db_type, DOM_CRED *ret_creds)
+{
+ unsigned sync_context = 0;
+ NTSTATUS result;
+ int i;
+ TALLOC_CTX *mem_ctx;
+ SAM_DELTA_HDR *hdr_deltas;
+ SAM_DELTA_CTR *deltas;
+ uint32 num_deltas;
+
+ if (!(mem_ctx = talloc_init("dump_database"))) {
+ return;
+ }
+
+ switch( db_type ) {
+ case SAM_DATABASE_DOMAIN:
+ d_printf("Dumping DOMAIN database\n");
+ break;
+ case SAM_DATABASE_BUILTIN:
+ d_printf("Dumping BUILTIN database\n");
+ break;
+ case SAM_DATABASE_PRIVS:
+ d_printf("Dumping PRIVS databases\n");
+ break;
+ default:
+ d_printf("Dumping unknown database type %u\n", db_type );
+ break;
+ }
+
+ do {
+ result = cli_netlogon_sam_sync(cli, mem_ctx, ret_creds, db_type,
+ sync_context,
+ &num_deltas, &hdr_deltas, &deltas);
+ if (NT_STATUS_IS_ERR(result))
+ break;
+
+ clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), ret_creds);
+ for (i = 0; i < num_deltas; i++) {
+ display_sam_entry(&hdr_deltas[i], &deltas[i]);
+ }
+ sync_context += 1;
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+ talloc_destroy(mem_ctx);
+}
+
+/* dump sam database via samsync rpc calls */
+NTSTATUS rpc_samdump_internals(const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ uchar trust_password[16];
+ DOM_CRED ret_creds;
+ uint32 sec_channel;
+
+ ZERO_STRUCT(ret_creds);
+
+ fstrcpy(cli->domain, domain_name);
+
+ if (!secrets_fetch_trust_account_password(domain_name,
+ trust_password,
+ NULL, &sec_channel)) {
+ DEBUG(0,("Could not fetch trust account password\n"));
+ goto fail;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = cli_nt_establish_netlogon(cli, sec_channel,
+ trust_password))) {
+ DEBUG(0,("Error connecting to NETLOGON pipe\n"));
+ goto fail;
+ }
+
+ dump_database(cli, SAM_DATABASE_DOMAIN, &ret_creds);
+ dump_database(cli, SAM_DATABASE_BUILTIN, &ret_creds);
+ dump_database(cli, SAM_DATABASE_PRIVS, &ret_creds);
+
+ nt_status = NT_STATUS_OK;
+
+fail:
+ cli_nt_session_close(cli);
+ return nt_status;
+}
+
+/* Convert a SAM_ACCOUNT_DELTA to a SAM_ACCOUNT. */
+#define STRING_CHANGED (old_string && !new_string) ||\
+ (!old_string && new_string) ||\
+ (old_string && new_string && (strcmp(old_string, new_string) != 0))
+
+static NTSTATUS
+sam_account_from_delta(SAM_ACCOUNT *account, SAM_ACCOUNT_INFO *delta)
+{
+ const char *old_string, *new_string;
+ time_t unix_time, stored_time;
+ uchar lm_passwd[16], nt_passwd[16];
+ static uchar zero_buf[16];
+
+ /* Username, fullname, home dir, dir drive, logon script, acct
+ desc, workstations, profile. */
+
+ if (delta->hdr_acct_name.buffer) {
+ old_string = pdb_get_nt_username(account);
+ new_string = unistr2_static(&delta->uni_acct_name);
+
+ if (STRING_CHANGED) {
+ pdb_set_nt_username(account, new_string, PDB_CHANGED);
+
+ }
+
+ /* Unix username is the same - for sanity */
+ old_string = pdb_get_username( account );
+ if (STRING_CHANGED) {
+ pdb_set_username(account, new_string, PDB_CHANGED);
+ }
+ }
+
+ if (delta->hdr_full_name.buffer) {
+ old_string = pdb_get_fullname(account);
+ new_string = unistr2_static(&delta->uni_full_name);
+
+ if (STRING_CHANGED)
+ pdb_set_fullname(account, new_string, PDB_CHANGED);
+ }
+
+ if (delta->hdr_home_dir.buffer) {
+ old_string = pdb_get_homedir(account);
+ new_string = unistr2_static(&delta->uni_home_dir);
+
+ if (STRING_CHANGED)
+ pdb_set_homedir(account, new_string, PDB_CHANGED);
+ }
+
+ if (delta->hdr_dir_drive.buffer) {
+ old_string = pdb_get_dir_drive(account);
+ new_string = unistr2_static(&delta->uni_dir_drive);
+
+ if (STRING_CHANGED)
+ pdb_set_dir_drive(account, new_string, PDB_CHANGED);
+ }
+
+ if (delta->hdr_logon_script.buffer) {
+ old_string = pdb_get_logon_script(account);
+ new_string = unistr2_static(&delta->uni_logon_script);
+
+ if (STRING_CHANGED)
+ pdb_set_logon_script(account, new_string, PDB_CHANGED);
+ }
+
+ if (delta->hdr_acct_desc.buffer) {
+ old_string = pdb_get_acct_desc(account);
+ new_string = unistr2_static(&delta->uni_acct_desc);
+
+ if (STRING_CHANGED)
+ pdb_set_acct_desc(account, new_string, PDB_CHANGED);
+ }
+
+ if (delta->hdr_workstations.buffer) {
+ old_string = pdb_get_workstations(account);
+ new_string = unistr2_static(&delta->uni_workstations);
+
+ if (STRING_CHANGED)
+ pdb_set_workstations(account, new_string, PDB_CHANGED);
+ }
+
+ if (delta->hdr_profile.buffer) {
+ old_string = pdb_get_profile_path(account);
+ new_string = unistr2_static(&delta->uni_profile);
+
+ if (STRING_CHANGED)
+ pdb_set_profile_path(account, new_string, PDB_CHANGED);
+ }
+
+ /* User and group sid */
+ if (pdb_get_user_rid(account) != delta->user_rid)
+ pdb_set_user_sid_from_rid(account, delta->user_rid, PDB_CHANGED);
+ if (pdb_get_group_rid(account) != delta->group_rid)
+ pdb_set_group_sid_from_rid(account, delta->group_rid, PDB_CHANGED);
+
+ /* Logon and password information */
+ if (!nt_time_is_zero(&delta->logon_time)) {
+ unix_time = nt_time_to_unix(&delta->logon_time);
+ stored_time = pdb_get_logon_time(account);
+ if (stored_time != unix_time)
+ pdb_set_logon_time(account, unix_time, PDB_CHANGED);
+ }
+
+ if (!nt_time_is_zero(&delta->logoff_time)) {
+ unix_time = nt_time_to_unix(&delta->logoff_time);
+ stored_time = pdb_get_logoff_time(account);
+ if (stored_time != unix_time)
+ pdb_set_logoff_time(account, unix_time,PDB_CHANGED);
+ }
+
+ if (pdb_get_logon_divs(account) != delta->logon_divs)
+ pdb_set_logon_divs(account, delta->logon_divs, PDB_CHANGED);
+
+ /* TODO: logon hours */
+ /* TODO: bad password count */
+ /* TODO: logon count */
+
+ if (!nt_time_is_zero(&delta->pwd_last_set_time)) {
+ unix_time = nt_time_to_unix(&delta->pwd_last_set_time);
+ stored_time = pdb_get_pass_last_set_time(account);
+ if (stored_time != unix_time)
+ pdb_set_pass_last_set_time(account, unix_time, PDB_CHANGED);
+ }
+
+#if 0
+/* No kickoff time in the delta? */
+ if (!nt_time_is_zero(&delta->kickoff_time)) {
+ unix_time = nt_time_to_unix(&delta->kickoff_time);
+ stored_time = pdb_get_kickoff_time(account);
+ if (stored_time != unix_time)
+ pdb_set_kickoff_time(account, unix_time, PDB_CHANGED);
+ }
+#endif
+
+ /* Decode hashes from password hash
+ Note that win2000 may send us all zeros for the hashes if it doesn't
+ think this channel is secure enough - don't set the passwords at all
+ in that case
+ */
+ if (memcmp(delta->pass.buf_lm_pwd, zero_buf, 16) != 0) {
+ sam_pwd_hash(delta->user_rid, delta->pass.buf_lm_pwd, lm_passwd, 0);
+ pdb_set_lanman_passwd(account, lm_passwd, PDB_CHANGED);
+ }
+
+ if (memcmp(delta->pass.buf_nt_pwd, zero_buf, 16) != 0) {
+ sam_pwd_hash(delta->user_rid, delta->pass.buf_nt_pwd, nt_passwd, 0);
+ pdb_set_nt_passwd(account, nt_passwd, PDB_CHANGED);
+ }
+
+ /* TODO: account expiry time */
+
+ if (pdb_get_acct_ctrl(account) != delta->acb_info)
+ pdb_set_acct_ctrl(account, delta->acb_info, PDB_CHANGED);
+
+ pdb_set_domain(account, lp_workgroup(), PDB_CHANGED);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS fetch_account_info(uint32 rid, SAM_ACCOUNT_INFO *delta)
+{
+ NTSTATUS nt_ret;
+ fstring account;
+ pstring add_script;
+ SAM_ACCOUNT *sam_account=NULL;
+ GROUP_MAP map;
+ struct group *grp;
+ DOM_SID user_sid;
+ DOM_SID group_sid;
+ struct passwd *passwd;
+ fstring sid_string;
+
+ fstrcpy(account, unistr2_static(&delta->uni_acct_name));
+ d_printf("Creating account: %s\n", account);
+
+ if (!NT_STATUS_IS_OK(nt_ret = pdb_init_sam(&sam_account)))
+ return nt_ret;
+
+ if (!(passwd = Get_Pwnam(account))) {
+ /* Create appropriate user */
+ if (delta->acb_info & ACB_NORMAL) {
+ pstrcpy(add_script, lp_adduser_script());
+ } else if ( (delta->acb_info & ACB_WSTRUST) ||
+ (delta->acb_info & ACB_SVRTRUST) ||
+ (delta->acb_info & ACB_DOMTRUST) ) {
+ pstrcpy(add_script, lp_addmachine_script());
+ } else {
+ DEBUG(1, ("Unknown user type: %s\n",
+ pdb_encode_acct_ctrl(delta->acb_info, NEW_PW_FORMAT_SPACE_PADDED_LEN)));
+ nt_ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ if (*add_script) {
+ int add_ret;
+ all_string_sub(add_script, "%u", account,
+ sizeof(account));
+ add_ret = smbrun(add_script,NULL);
+ DEBUG(1,("fetch_account: Running the command `%s' "
+ "gave %d\n", add_script, add_ret));
+ } else {
+ DEBUG(8,("fetch_account_info: no add user/machine script. Asking winbindd\n"));
+
+ /* don't need a RID allocated since the user already has a SID */
+ if ( !winbind_create_user( account, NULL ) )
+ DEBUG(4,("fetch_account_info: winbind_create_user() failed\n"));
+ }
+
+ /* try and find the possible unix account again */
+ if ( !(passwd = Get_Pwnam(account)) ) {
+ d_printf("Could not create posix account info for '%s'\n", account);
+ nt_ret = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+ }
+
+ sid_copy(&user_sid, get_global_sam_sid());
+ sid_append_rid(&user_sid, delta->user_rid);
+
+ DEBUG(3, ("Attempting to find SID %s for user %s in the passdb\n", sid_to_string(sid_string, &user_sid), account));
+ if (!pdb_getsampwsid(sam_account, &user_sid)) {
+ sam_account_from_delta(sam_account, delta);
+ DEBUG(3, ("Attempting to add user SID %s for user %s in the passdb\n",
+ sid_to_string(sid_string, &user_sid), pdb_get_username(sam_account)));
+ if (!pdb_add_sam_account(sam_account)) {
+ DEBUG(1, ("SAM Account for %s failed to be added to the passdb!\n",
+ account));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ } else {
+ sam_account_from_delta(sam_account, delta);
+ DEBUG(3, ("Attempting to update user SID %s for user %s in the passdb\n",
+ sid_to_string(sid_string, &user_sid), pdb_get_username(sam_account)));
+ if (!pdb_update_sam_account(sam_account)) {
+ DEBUG(1, ("SAM Account for %s failed to be updated in the passdb!\n",
+ account));
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ group_sid = *pdb_get_group_sid(sam_account);
+
+ if (!pdb_getgrsid(&map, group_sid)) {
+ DEBUG(0, ("Primary group of %s has no mapping!\n",
+ pdb_get_username(sam_account)));
+ } else {
+ if (map.gid != passwd->pw_gid) {
+ if (!(grp = getgrgid(map.gid))) {
+ DEBUG(0, ("Could not find unix group %lu for user %s (group SID=%s)\n",
+ (unsigned long)map.gid, pdb_get_username(sam_account), sid_string_static(&group_sid)));
+ } else {
+ smb_set_primary_group(grp->gr_name, pdb_get_username(sam_account));
+ }
+ }
+ }
+
+ if ( !passwd ) {
+ DEBUG(1, ("No unix user for this account (%s), cannot adjust mappings\n",
+ pdb_get_username(sam_account)));
+ }
+
+ done:
+ pdb_free_sam(&sam_account);
+ return nt_ret;
+}
+
+static NTSTATUS
+fetch_group_info(uint32 rid, SAM_GROUP_INFO *delta)
+{
+ fstring name;
+ fstring comment;
+ struct group *grp = NULL;
+ DOM_SID group_sid;
+ fstring sid_string;
+ GROUP_MAP map;
+ BOOL insert = True;
+
+ unistr2_to_ascii(name, &delta->uni_grp_name, sizeof(name)-1);
+ unistr2_to_ascii(comment, &delta->uni_grp_desc, sizeof(comment)-1);
+
+ /* add the group to the mapping table */
+ sid_copy(&group_sid, get_global_sam_sid());
+ sid_append_rid(&group_sid, rid);
+ sid_to_string(sid_string, &group_sid);
+
+ if (pdb_getgrsid(&map, group_sid)) {
+ if ( map.gid != -1 )
+ grp = getgrgid(map.gid);
+ insert = False;
+ }
+
+ if (grp == NULL) {
+ gid_t gid;
+
+ /* No group found from mapping, find it from its name. */
+ if ((grp = getgrnam(name)) == NULL) {
+
+ /* No appropriate group found, create one */
+
+ d_printf("Creating unix group: '%s'\n", name);
+
+ if (smb_create_group(name, &gid) != 0)
+ return NT_STATUS_ACCESS_DENIED;
+
+ if ((grp = getgrnam(name)) == NULL)
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ map.gid = grp->gr_gid;
+ map.sid = group_sid;
+ map.sid_name_use = SID_NAME_DOM_GRP;
+ fstrcpy(map.nt_name, name);
+ if (delta->hdr_grp_desc.buffer) {
+ fstrcpy(map.comment, comment);
+ } else {
+ fstrcpy(map.comment, "");
+ }
+
+ if (insert)
+ pdb_add_group_mapping_entry(&map);
+ else
+ pdb_update_group_mapping_entry(&map);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS
+fetch_group_mem_info(uint32 rid, SAM_GROUP_MEM_INFO *delta)
+{
+ int i;
+ TALLOC_CTX *t = NULL;
+ char **nt_members = NULL;
+ char **unix_members;
+ DOM_SID group_sid;
+ GROUP_MAP map;
+ struct group *grp;
+
+ if (delta->num_members == 0) {
+ return NT_STATUS_OK;
+ }
+
+ sid_copy(&group_sid, get_global_sam_sid());
+ sid_append_rid(&group_sid, rid);
+
+ if (!get_domain_group_from_sid(group_sid, &map)) {
+ DEBUG(0, ("Could not find global group %d\n", rid));
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ if (!(grp = getgrgid(map.gid))) {
+ DEBUG(0, ("Could not find unix group %lu\n", (unsigned long)map.gid));
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ d_printf("Group members of %s: ", grp->gr_name);
+
+ if (!(t = talloc_init("fetch_group_mem_info"))) {
+ DEBUG(0, ("could not talloc_init\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nt_members = talloc_zero(t, sizeof(char *) * delta->num_members);
+
+ for (i=0; i<delta->num_members; i++) {
+ NTSTATUS nt_status;
+ SAM_ACCOUNT *member = NULL;
+ DOM_SID member_sid;
+
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam_talloc(t, &member))) {
+ talloc_destroy(t);
+ return nt_status;
+ }
+
+ sid_copy(&member_sid, get_global_sam_sid());
+ sid_append_rid(&member_sid, delta->rids[i]);
+
+ if (!pdb_getsampwsid(member, &member_sid)) {
+ DEBUG(1, ("Found bogus group member: %d (member_sid=%s group=%s)\n",
+ delta->rids[i], sid_string_static(&member_sid), grp->gr_name));
+ pdb_free_sam(&member);
+ continue;
+ }
+
+ if (pdb_get_group_rid(member) == rid) {
+ d_printf("%s(primary),", pdb_get_username(member));
+ pdb_free_sam(&member);
+ continue;
+ }
+
+ d_printf("%s,", pdb_get_username(member));
+ nt_members[i] = talloc_strdup(t, pdb_get_username(member));
+ pdb_free_sam(&member);
+ }
+
+ d_printf("\n");
+
+ unix_members = grp->gr_mem;
+
+ while (*unix_members) {
+ BOOL is_nt_member = False;
+ for (i=0; i<delta->num_members; i++) {
+ if (nt_members[i] == NULL) {
+ /* This was a primary group */
+ continue;
+ }
+
+ if (strcmp(*unix_members, nt_members[i]) == 0) {
+ is_nt_member = True;
+ break;
+ }
+ }
+ if (!is_nt_member) {
+ /* We look at a unix group member that is not
+ an nt group member. So, remove it. NT is
+ boss here. */
+ smb_delete_user_group(grp->gr_name, *unix_members);
+ }
+ unix_members += 1;
+ }
+
+ for (i=0; i<delta->num_members; i++) {
+ BOOL is_unix_member = False;
+
+ if (nt_members[i] == NULL) {
+ /* This was the primary group */
+ continue;
+ }
+
+ unix_members = grp->gr_mem;
+
+ while (*unix_members) {
+ if (strcmp(*unix_members, nt_members[i]) == 0) {
+ is_unix_member = True;
+ break;
+ }
+ unix_members += 1;
+ }
+
+ if (!is_unix_member) {
+ /* We look at a nt group member that is not a
+ unix group member currently. So, add the nt
+ group member. */
+ smb_add_user_group(grp->gr_name, nt_members[i]);
+ }
+ }
+
+ talloc_destroy(t);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS fetch_alias_info(uint32 rid, SAM_ALIAS_INFO *delta,
+ DOM_SID dom_sid)
+{
+ fstring name;
+ fstring comment;
+ struct group *grp = NULL;
+ DOM_SID alias_sid;
+ fstring sid_string;
+ GROUP_MAP map;
+ BOOL insert = True;
+
+ unistr2_to_ascii(name, &delta->uni_als_name, sizeof(name)-1);
+ unistr2_to_ascii(comment, &delta->uni_als_desc, sizeof(comment)-1);
+
+ /* Find out whether the group is already mapped */
+ sid_copy(&alias_sid, &dom_sid);
+ sid_append_rid(&alias_sid, rid);
+ sid_to_string(sid_string, &alias_sid);
+
+ if (pdb_getgrsid(&map, alias_sid)) {
+ grp = getgrgid(map.gid);
+ insert = False;
+ }
+
+ if (grp == NULL) {
+ gid_t gid;
+
+ /* No group found from mapping, find it from its name. */
+ if ((grp = getgrnam(name)) == NULL) {
+ /* No appropriate group found, create one */
+ d_printf("Creating unix group: '%s'\n", name);
+ if (smb_create_group(name, &gid) != 0)
+ return NT_STATUS_ACCESS_DENIED;
+ if ((grp = getgrgid(gid)) == NULL)
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ map.gid = grp->gr_gid;
+ map.sid = alias_sid;
+
+ if (sid_equal(&dom_sid, &global_sid_Builtin))
+ map.sid_name_use = SID_NAME_WKN_GRP;
+ else
+ map.sid_name_use = SID_NAME_ALIAS;
+
+ fstrcpy(map.nt_name, name);
+ fstrcpy(map.comment, comment);
+
+ if (insert)
+ pdb_add_group_mapping_entry(&map);
+ else
+ pdb_update_group_mapping_entry(&map);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS
+fetch_alias_mem(uint32 rid, SAM_ALIAS_MEM_INFO *delta, DOM_SID dom_sid)
+{
+#if 0 /*
+ * commented out right now after talking to Volker. Can't
+ * do much with the membership but seemed a shame to waste
+ * somewhat working code. Needs testing because the membership
+ * that shows up surprises me. Also can't do much with groups
+ * in groups (e.g. Domain Admins being a member of Adminsitrators).
+ * --jerry
+ */
+
+ int i;
+ TALLOC_CTX *t = NULL;
+ char **nt_members = NULL;
+ char **unix_members;
+ DOM_SID group_sid;
+ GROUP_MAP map;
+ struct group *grp;
+ enum SID_NAME_USE sid_type;
+
+ if (delta->num_members == 0) {
+ return NT_STATUS_OK;
+ }
+
+ sid_copy(&group_sid, &dom_sid);
+ sid_append_rid(&group_sid, rid);
+
+ if (sid_equal(&dom_sid, &global_sid_Builtin)) {
+ sid_type = SID_NAME_WKN_GRP;
+ if (!get_builtin_group_from_sid(&group_sid, &map, False)) {
+ DEBUG(0, ("Could not find builtin group %s\n", sid_string_static(&group_sid)));
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+ } else {
+ sid_type = SID_NAME_ALIAS;
+ if (!get_local_group_from_sid(&group_sid, &map, False)) {
+ DEBUG(0, ("Could not find local group %s\n", sid_string_static(&group_sid)));
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+ }
+
+ if (!(grp = getgrgid(map.gid))) {
+ DEBUG(0, ("Could not find unix group %d\n", map.gid));
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ d_printf("Group members of %s: ", grp->gr_name);
+
+ if (!(t = talloc_init("fetch_group_mem_info"))) {
+ DEBUG(0, ("could not talloc_init\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nt_members = talloc_zero(t, sizeof(char *) * delta->num_members);
+
+ for (i=0; i<delta->num_members; i++) {
+ NTSTATUS nt_status;
+ SAM_ACCOUNT *member = NULL;
+ DOM_SID member_sid;
+
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam_talloc(t, &member))) {
+ talloc_destroy(t);
+ return nt_status;
+ }
+
+ sid_copy(&member_sid, &delta->sids[i].sid);
+
+ if (!pdb_getsampwsid(member, &member_sid)) {
+ DEBUG(1, ("Found bogus group member: (member_sid=%s group=%s)\n",
+ sid_string_static(&member_sid), grp->gr_name));
+ pdb_free_sam(&member);
+ continue;
+ }
+
+ if (pdb_get_group_rid(member) == rid) {
+ d_printf("%s(primary),", pdb_get_username(member));
+ pdb_free_sam(&member);
+ continue;
+ }
+
+ d_printf("%s,", pdb_get_username(member));
+ nt_members[i] = talloc_strdup(t, pdb_get_username(member));
+ pdb_free_sam(&member);
+ }
+
+ d_printf("\n");
+
+ unix_members = grp->gr_mem;
+
+ while (*unix_members) {
+ BOOL is_nt_member = False;
+ for (i=0; i<delta->num_members; i++) {
+ if (nt_members[i] == NULL) {
+ /* This was a primary group */
+ continue;
+ }
+
+ if (strcmp(*unix_members, nt_members[i]) == 0) {
+ is_nt_member = True;
+ break;
+ }
+ }
+ if (!is_nt_member) {
+ /* We look at a unix group member that is not
+ an nt group member. So, remove it. NT is
+ boss here. */
+ smb_delete_user_group(grp->gr_name, *unix_members);
+ }
+ unix_members += 1;
+ }
+
+ for (i=0; i<delta->num_members; i++) {
+ BOOL is_unix_member = False;
+
+ if (nt_members[i] == NULL) {
+ /* This was the primary group */
+ continue;
+ }
+
+ unix_members = grp->gr_mem;
+
+ while (*unix_members) {
+ if (strcmp(*unix_members, nt_members[i]) == 0) {
+ is_unix_member = True;
+ break;
+ }
+ unix_members += 1;
+ }
+
+ if (!is_unix_member) {
+ /* We look at a nt group member that is not a
+ unix group member currently. So, add the nt
+ group member. */
+ smb_add_user_group(grp->gr_name, nt_members[i]);
+ }
+ }
+
+ talloc_destroy(t);
+
+#endif /* end of fetch_alias_mem() */
+
+ return NT_STATUS_OK;
+}
+
+static void
+fetch_sam_entry(SAM_DELTA_HDR *hdr_delta, SAM_DELTA_CTR *delta,
+ DOM_SID dom_sid)
+{
+ switch(hdr_delta->type) {
+ case SAM_DELTA_ACCOUNT_INFO:
+ fetch_account_info(hdr_delta->target_rid,
+ &delta->account_info);
+ break;
+ case SAM_DELTA_GROUP_INFO:
+ fetch_group_info(hdr_delta->target_rid,
+ &delta->group_info);
+ break;
+ case SAM_DELTA_GROUP_MEM:
+ fetch_group_mem_info(hdr_delta->target_rid,
+ &delta->grp_mem_info);
+ break;
+ case SAM_DELTA_ALIAS_INFO:
+ fetch_alias_info(hdr_delta->target_rid,
+ &delta->alias_info, dom_sid);
+ break;
+ case SAM_DELTA_ALIAS_MEM:
+ fetch_alias_mem(hdr_delta->target_rid,
+ &delta->als_mem_info, dom_sid);
+ break;
+ /* The following types are recognised but not handled */
+ case SAM_DELTA_DOMAIN_INFO:
+ d_printf("SAM_DELTA_DOMAIN_INFO not handled\n");
+ break;
+ case SAM_DELTA_RENAME_GROUP:
+ d_printf("SAM_DELTA_RENAME_GROUP not handled\n");
+ break;
+ case SAM_DELTA_RENAME_USER:
+ d_printf("SAM_DELTA_RENAME_USER not handled\n");
+ break;
+ case SAM_DELTA_RENAME_ALIAS:
+ d_printf("SAM_DELTA_RENAME_ALIAS not handled\n");
+ break;
+ case SAM_DELTA_POLICY_INFO:
+ d_printf("SAM_DELTA_POLICY_INFO not handled\n");
+ break;
+ case SAM_DELTA_TRUST_DOMS:
+ d_printf("SAM_DELTA_TRUST_DOMS not handled\n");
+ break;
+ case SAM_DELTA_PRIVS_INFO:
+ d_printf("SAM_DELTA_PRIVS_INFO not handled\n");
+ break;
+ case SAM_DELTA_SECRET_INFO:
+ d_printf("SAM_DELTA_SECRET_INFO not handled\n");
+ break;
+ case SAM_DELTA_DELETE_GROUP:
+ d_printf("SAM_DELTA_DELETE_GROUP not handled\n");
+ break;
+ case SAM_DELTA_DELETE_USER:
+ d_printf("SAM_DELTA_DELETE_USER not handled\n");
+ break;
+ case SAM_DELTA_MODIFIED_COUNT:
+ d_printf("SAM_DELTA_MODIFIED_COUNT not handled\n");
+ break;
+ default:
+ d_printf("Unknown delta record type %d\n", hdr_delta->type);
+ break;
+ }
+}
+
+static NTSTATUS
+fetch_database(struct cli_state *cli, unsigned db_type, DOM_CRED *ret_creds,
+ DOM_SID dom_sid)
+{
+ unsigned sync_context = 0;
+ NTSTATUS result;
+ int i;
+ TALLOC_CTX *mem_ctx;
+ SAM_DELTA_HDR *hdr_deltas;
+ SAM_DELTA_CTR *deltas;
+ uint32 num_deltas;
+
+ if (!(mem_ctx = talloc_init("fetch_database")))
+ return NT_STATUS_NO_MEMORY;
+
+ switch( db_type ) {
+ case SAM_DATABASE_DOMAIN:
+ d_printf("Fetching DOMAIN database\n");
+ break;
+ case SAM_DATABASE_BUILTIN:
+ d_printf("Fetching BUILTIN database\n");
+ break;
+ case SAM_DATABASE_PRIVS:
+ d_printf("Fetching PRIVS databases\n");
+ break;
+ default:
+ d_printf("Fetching unknown database type %u\n", db_type );
+ break;
+ }
+
+ do {
+ result = cli_netlogon_sam_sync(cli, mem_ctx, ret_creds,
+ db_type, sync_context,
+ &num_deltas,
+ &hdr_deltas, &deltas);
+
+ if (NT_STATUS_IS_OK(result) ||
+ NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) {
+
+ clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred),
+ ret_creds);
+
+ for (i = 0; i < num_deltas; i++) {
+ fetch_sam_entry(&hdr_deltas[i], &deltas[i], dom_sid);
+ }
+ } else
+ return result;
+
+ sync_context += 1;
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+/* dump sam database via samsync rpc calls */
+NTSTATUS rpc_vampire_internals(const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result;
+ uchar trust_password[16];
+ DOM_CRED ret_creds;
+ fstring my_dom_sid_str;
+ fstring rem_dom_sid_str;
+ uint32 sec_channel;
+
+ ZERO_STRUCT(ret_creds);
+
+ if (!sid_equal(domain_sid, get_global_sam_sid())) {
+ d_printf("Cannot import users from %s at this time, "
+ "as the current domain:\n\t%s: %s\nconflicts "
+ "with the remote domain\n\t%s: %s\n"
+ "Perhaps you need to set: \n\n\tsecurity=user\n\tworkgroup=%s\n\n in your smb.conf?\n",
+ domain_name,
+ get_global_sam_name(), sid_to_string(my_dom_sid_str,
+ get_global_sam_sid()),
+ domain_name, sid_to_string(rem_dom_sid_str, domain_sid),
+ domain_name);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ fstrcpy(cli->domain, domain_name);
+
+ if (!secrets_fetch_trust_account_password(domain_name,
+ trust_password, NULL,
+ &sec_channel)) {
+ result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ d_printf("Could not retrieve domain trust secret\n");
+ goto fail;
+ }
+
+ result = cli_nt_establish_netlogon(cli, sec_channel, trust_password);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Failed to setup BDC creds\n");
+ goto fail;
+ }
+
+ result = fetch_database(cli, SAM_DATABASE_DOMAIN, &ret_creds, *domain_sid);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Failed to fetch domain database: %s\n",
+ nt_errstr(result));
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED))
+ d_printf("Perhaps %s is a Windows 2000 native mode "
+ "domain?\n", domain_name);
+ goto fail;
+ }
+
+ result = fetch_database(cli, SAM_DATABASE_BUILTIN, &ret_creds,
+ global_sid_Builtin);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Failed to fetch builtin database: %s\n",
+ nt_errstr(result));
+ goto fail;
+ }
+
+ /* Currently we crash on PRIVS somewhere in unmarshalling */
+ /* Dump_database(cli, SAM_DATABASE_PRIVS, &ret_creds); */
+
+fail:
+ return result;
+}
diff --git a/source/utils/net_status.c b/source/utils/net_status.c
new file mode 100644
index 00000000000..0543f457cfc
--- /dev/null
+++ b/source/utils/net_status.c
@@ -0,0 +1,257 @@
+/*
+ Samba Unix/Linux SMB client library
+ net status command -- possible replacement for smbstatus
+ Copyright (C) 2003 Volker Lendecke (vl@samba.org)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "includes.h"
+#include "../utils/net.h"
+
+static int show_session(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf,
+ void *state)
+{
+ BOOL *parseable = (BOOL *)state;
+ struct sessionid sessionid;
+
+ if (dbuf.dsize != sizeof(sessionid))
+ return 0;
+
+ memcpy(&sessionid, dbuf.dptr, sizeof(sessionid));
+
+ if (!process_exists(sessionid.pid)) {
+ return 0;
+ }
+
+ if (*parseable) {
+ d_printf("%d\\%s\\%s\\%s\\%s\n",
+ (int)sessionid.pid, uidtoname(sessionid.uid),
+ gidtoname(sessionid.gid),
+ sessionid.remote_machine, sessionid.hostname);
+ } else {
+ d_printf("%5d %-12s %-12s %-12s (%s)\n",
+ (int)sessionid.pid, uidtoname(sessionid.uid),
+ gidtoname(sessionid.gid),
+ sessionid.remote_machine, sessionid.hostname);
+ }
+
+ return 0;
+}
+
+static int net_status_sessions(int argc, const char **argv)
+{
+ TDB_CONTEXT *tdb;
+ BOOL parseable;
+
+ if (argc == 0) {
+ parseable = False;
+ } else if ((argc == 1) && strequal(argv[0], "parseable")) {
+ parseable = True;
+ } else {
+ return net_help_status(argc, argv);
+ }
+
+ if (!parseable) {
+ d_printf("PID Username Group Machine"
+ " \n");
+ d_printf("-------------------------------------------"
+ "------------------------\n");
+ }
+
+ tdb = tdb_open_log(lock_path("sessionid.tdb"), 0,
+ TDB_DEFAULT, O_RDONLY, 0);
+
+ if (tdb == NULL) {
+ d_printf("%s not initialised\n", lock_path("sessionid.tdb"));
+ return -1;
+ }
+
+ tdb_traverse(tdb, show_session, &parseable);
+ tdb_close(tdb);
+
+ return 0;
+}
+
+static int show_share(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf,
+ void *state)
+{
+ struct connections_data crec;
+
+ if (dbuf.dsize != sizeof(crec))
+ return 0;
+
+ memcpy(&crec, dbuf.dptr, sizeof(crec));
+
+ if (crec.cnum == -1)
+ return 0;
+
+ if (!process_exists(crec.pid)) {
+ return 0;
+ }
+
+ d_printf("%-10.10s %5d %-12s %s",
+ crec.name,(int)crec.pid,
+ crec.machine,
+ asctime(LocalTime(&crec.start)));
+
+ return 0;
+}
+
+struct sessionids {
+ int num_entries;
+ struct sessionid *entries;
+};
+
+static int collect_pid(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf,
+ void *state)
+{
+ struct sessionids *ids = (struct sessionids *)state;
+ struct sessionid sessionid;
+
+ if (dbuf.dsize != sizeof(sessionid))
+ return 0;
+
+ memcpy(&sessionid, dbuf.dptr, sizeof(sessionid));
+
+ if (!process_exists(sessionid.pid))
+ return 0;
+
+ ids->num_entries += 1;
+ ids->entries = Realloc(ids->entries,
+ sizeof(struct sessionid) * ids->num_entries);
+ ids->entries[ids->num_entries-1] = sessionid;
+
+ return 0;
+}
+
+static int show_share_parseable(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf,
+ void *state)
+{
+ struct sessionids *ids = (struct sessionids *)state;
+ struct connections_data crec;
+ int i;
+ BOOL guest = True;
+
+ if (dbuf.dsize != sizeof(crec))
+ return 0;
+
+ memcpy(&crec, dbuf.dptr, sizeof(crec));
+
+ if (crec.cnum == -1)
+ return 0;
+
+ if (!process_exists(crec.pid)) {
+ return 0;
+ }
+
+ for (i=0; i<ids->num_entries; i++) {
+ if (ids->entries[i].pid == crec.pid) {
+ guest = False;
+ break;
+ }
+ }
+
+ d_printf("%s\\%d\\%s\\%s\\%s\\%s\\%s",
+ crec.name,(int)crec.pid,
+ guest ? "" : uidtoname(ids->entries[i].uid),
+ guest ? "" : gidtoname(ids->entries[i].gid),
+ crec.machine,
+ guest ? "" : ids->entries[i].hostname,
+ asctime(LocalTime(&crec.start)));
+
+ return 0;
+}
+
+static int net_status_shares_parseable(int argc, const char **argv)
+{
+ struct sessionids ids;
+ TDB_CONTEXT *tdb;
+
+ ids.num_entries = 0;
+ ids.entries = NULL;
+
+ tdb = tdb_open_log(lock_path("sessionid.tdb"), 0,
+ TDB_DEFAULT, O_RDONLY, 0);
+
+ if (tdb == NULL) {
+ d_printf("%s not initialised\n", lock_path("sessionid.tdb"));
+ return -1;
+ }
+
+ tdb_traverse(tdb, collect_pid, &ids);
+ tdb_close(tdb);
+
+ tdb = tdb_open_log(lock_path("connections.tdb"), 0,
+ TDB_DEFAULT, O_RDONLY, 0);
+
+ if (tdb == NULL) {
+ d_printf("%s not initialised\n", lock_path("connections.tdb"));
+ d_printf("This is normal if no SMB client has ever connected "
+ "to your server.\n");
+ return -1;
+ }
+
+ tdb_traverse(tdb, show_share_parseable, &ids);
+ tdb_close(tdb);
+
+ SAFE_FREE(ids.entries);
+
+ return 0;
+}
+
+static int net_status_shares(int argc, const char **argv)
+{
+ TDB_CONTEXT *tdb;
+
+ if (argc == 0) {
+
+ d_printf("\nService pid machine "
+ "Connected at\n");
+ d_printf("-------------------------------------"
+ "------------------\n");
+
+ tdb = tdb_open_log(lock_path("connections.tdb"), 0,
+ TDB_DEFAULT, O_RDONLY, 0);
+
+ if (tdb == NULL) {
+ d_printf("%s not initialised\n",
+ lock_path("connections.tdb"));
+ d_printf("This is normal if no SMB client has ever "
+ "connected to your server.\n");
+ return -1;
+ }
+
+ tdb_traverse(tdb, show_share, NULL);
+ tdb_close(tdb);
+
+ return 0;
+ }
+
+ if ((argc != 1) || !strequal(argv[0], "parseable")) {
+ return net_help_status(argc, argv);
+ }
+
+ return net_status_shares_parseable(argc, argv);
+}
+
+int net_status(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"sessions", net_status_sessions},
+ {"shares", net_status_shares},
+ {NULL, NULL}
+ };
+ return net_run_function(argc, argv, func, net_help_status);
+}
diff --git a/source/utils/net_time.c b/source/utils/net_time.c
new file mode 100644
index 00000000000..45c17838055
--- /dev/null
+++ b/source/utils/net_time.c
@@ -0,0 +1,180 @@
+/*
+ Samba Unix/Linux SMB client library
+ net time command
+ Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "includes.h"
+#include "../utils/net.h"
+
+
+/*
+ return the time on a server. This does not require any authentication
+*/
+static time_t cli_servertime(const char *host, struct in_addr *ip, int *zone)
+{
+ struct nmb_name calling, called;
+ time_t ret = 0;
+ struct cli_state *cli = NULL;
+
+ cli = cli_initialise(NULL);
+ if (!cli) goto done;
+
+ if (!cli_connect(cli, host, ip)) {
+ fprintf(stderr,"Can't contact server\n");
+ goto done;
+ }
+
+ make_nmb_name(&calling, global_myname(), 0x0);
+ if (host) {
+ make_nmb_name(&called, host, 0x20);
+ } else {
+ make_nmb_name(&called, "*SMBSERVER", 0x20);
+ }
+
+ if (!cli_session_request(cli, &calling, &called)) {
+ fprintf(stderr,"Session request failed\n");
+ goto done;
+ }
+ if (!cli_negprot(cli)) {
+ fprintf(stderr,"Protocol negotiation failed\n");
+ goto done;
+ }
+
+ ret = cli->servertime;
+ if (zone) *zone = cli->serverzone;
+
+done:
+ if (cli) cli_shutdown(cli);
+ return ret;
+}
+
+/* find the servers time on the opt_host host */
+static time_t nettime(int *zone)
+{
+ return cli_servertime(opt_host, opt_have_ip? &opt_dest_ip : NULL, zone);
+}
+
+/* return a time as a string ready to be passed to /bin/date */
+static char *systime(time_t t)
+{
+ static fstring s;
+ struct tm *tm;
+
+ tm = localtime(&t);
+
+ fstr_sprintf(s, "%02d%02d%02d%02d%04d.%02d",
+ tm->tm_mon+1, tm->tm_mday, tm->tm_hour,
+ tm->tm_min, tm->tm_year + 1900, tm->tm_sec);
+ return s;
+}
+
+int net_time_usage(int argc, const char **argv)
+{
+ d_printf(
+"net time\n\tdisplays time on a server\n\n"\
+"net time system\n\tdisplays time on a server in a format ready for /bin/date\n\n"\
+"net time set\n\truns /bin/date with the time from the server\n\n"\
+"net time zone\n\tdisplays the timezone in hours from GMT on the remote computer\n\n"\
+"\n");
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+/* try to set the system clock using /bin/date */
+static int net_time_set(int argc, const char **argv)
+{
+ time_t t = nettime(NULL);
+ char *cmd;
+
+ if (t == 0) return -1;
+
+ /* yes, I know this is cheesy. Use "net time system" if you want to
+ roll your own. I'm putting this in as it works on a large number
+ of systems and the user has a choice in whether its used or not */
+ asprintf(&cmd, "/bin/date %s", systime(t));
+ system(cmd);
+ free(cmd);
+
+ return 0;
+}
+
+/* display the time on a remote box in a format ready for /bin/date */
+static int net_time_system(int argc, const char **argv)
+{
+ time_t t = nettime(NULL);
+
+ if (t == 0) return -1;
+
+ printf("%s\n", systime(t));
+
+ return 0;
+}
+
+/* display the time on a remote box in a format ready for /bin/date */
+static int net_time_zone(int argc, const char **argv)
+{
+ int zone = 0;
+ int hours, mins;
+ char zsign;
+ time_t t;
+
+ t = nettime(&zone);
+
+ if (t == 0) return -1;
+
+ zsign = (zone > 0) ? '-' : '+';
+ if (zone < 0) zone = -zone;
+
+ zone /= 60;
+ hours = zone / 60;
+ mins = zone % 60;
+
+ printf("%c%02d%02d\n", zsign, hours, mins);
+
+ return 0;
+}
+
+/* display or set the time on a host */
+int net_time(int argc, const char **argv)
+{
+ time_t t;
+ struct functable func[] = {
+ {"SYSTEM", net_time_system},
+ {"SET", net_time_set},
+ {"ZONE", net_time_zone},
+ {NULL, NULL}
+ };
+
+ if (!opt_host && !opt_have_ip &&
+ !find_master_ip(opt_target_workgroup, &opt_dest_ip)) {
+ d_printf("Could not locate a time server. Try "\
+ "specifying a target host.\n");
+ net_time_usage(argc,argv);
+ return -1;
+ }
+
+ if (argc != 0) {
+ return net_run_function(argc, argv, func, net_time_usage);
+ }
+
+ /* default - print the time */
+ t = cli_servertime(opt_host, opt_have_ip? &opt_dest_ip : NULL, NULL);
+ if (t == 0) return -1;
+
+ d_printf("%s", ctime(&t));
+ return 0;
+}
diff --git a/source/utils/nmblookup.c b/source/utils/nmblookup.c
new file mode 100644
index 00000000000..3c5a22841ea
--- /dev/null
+++ b/source/utils/nmblookup.c
@@ -0,0 +1,292 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT client - used to lookup netbios names
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Jelmer Vernooij 2003 (Conversion to popt)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+extern BOOL AllowDebugChange;
+
+static BOOL give_flags = False;
+static BOOL use_bcast = True;
+static BOOL got_bcast = False;
+static struct in_addr bcast_addr;
+static BOOL recursion_desired = False;
+static BOOL translate_addresses = False;
+static int ServerFD= -1;
+static int RootPort = False;
+static BOOL find_status=False;
+
+/****************************************************************************
+ open the socket communication
+ **************************************************************************/
+static BOOL open_sockets(void)
+{
+ ServerFD = open_socket_in( SOCK_DGRAM,
+ (RootPort ? 137 : 0),
+ (RootPort ? 0 : 3),
+ interpret_addr(lp_socket_address()), True );
+
+ if (ServerFD == -1)
+ return(False);
+
+ set_socket_options( ServerFD, "SO_BROADCAST" );
+
+ DEBUG(3, ("Socket opened.\n"));
+ return True;
+}
+
+/****************************************************************************
+turn a node status flags field into a string
+****************************************************************************/
+static char *node_status_flags(unsigned char flags)
+{
+ static fstring ret;
+ fstrcpy(ret,"");
+
+ fstrcat(ret, (flags & 0x80) ? "<GROUP> " : " ");
+ if ((flags & 0x60) == 0x00) fstrcat(ret,"B ");
+ if ((flags & 0x60) == 0x20) fstrcat(ret,"P ");
+ if ((flags & 0x60) == 0x40) fstrcat(ret,"M ");
+ if ((flags & 0x60) == 0x60) fstrcat(ret,"H ");
+ if (flags & 0x10) fstrcat(ret,"<DEREGISTERING> ");
+ if (flags & 0x08) fstrcat(ret,"<CONFLICT> ");
+ if (flags & 0x04) fstrcat(ret,"<ACTIVE> ");
+ if (flags & 0x02) fstrcat(ret,"<PERMANENT> ");
+
+ return ret;
+}
+
+/****************************************************************************
+turn the NMB Query flags into a string
+****************************************************************************/
+static char *query_flags(int flags)
+{
+ static fstring ret1;
+ fstrcpy(ret1, "");
+
+ if (flags & NM_FLAGS_RS) fstrcat(ret1, "Response ");
+ if (flags & NM_FLAGS_AA) fstrcat(ret1, "Authoritative ");
+ if (flags & NM_FLAGS_TC) fstrcat(ret1, "Truncated ");
+ if (flags & NM_FLAGS_RD) fstrcat(ret1, "Recursion_Desired ");
+ if (flags & NM_FLAGS_RA) fstrcat(ret1, "Recursion_Available ");
+ if (flags & NM_FLAGS_B) fstrcat(ret1, "Broadcast ");
+
+ return ret1;
+}
+
+/****************************************************************************
+do a node status query
+****************************************************************************/
+static void do_node_status(int fd, const char *name, int type, struct in_addr ip)
+{
+ struct nmb_name nname;
+ int count, i, j;
+ struct node_status *status;
+ fstring cleanname;
+
+ d_printf("Looking up status of %s\n",inet_ntoa(ip));
+ make_nmb_name(&nname, name, type);
+ status = node_status_query(fd,&nname,ip, &count);
+ if (status) {
+ for (i=0;i<count;i++) {
+ pull_ascii_fstring(cleanname, status[i].name);
+ for (j=0;cleanname[j];j++) {
+ if (!isprint((int)cleanname[j])) cleanname[j] = '.';
+ }
+ d_printf("\t%-15s <%02x> - %s\n",
+ cleanname,status[i].type,
+ node_status_flags(status[i].flags));
+ }
+ SAFE_FREE(status);
+ }
+ d_printf("\n");
+}
+
+
+/****************************************************************************
+send out one query
+****************************************************************************/
+static BOOL query_one(const char *lookup, unsigned int lookup_type)
+{
+ int j, count, flags = 0;
+ struct in_addr *ip_list=NULL;
+
+ if (got_bcast) {
+ d_printf("querying %s on %s\n", lookup, inet_ntoa(bcast_addr));
+ ip_list = name_query(ServerFD,lookup,lookup_type,use_bcast,
+ use_bcast?True:recursion_desired,
+ bcast_addr,&count, &flags, NULL);
+ } else {
+ struct in_addr *bcast;
+ for (j=iface_count() - 1;
+ !ip_list && j >= 0;
+ j--) {
+ bcast = iface_n_bcast(j);
+ d_printf("querying %s on %s\n",
+ lookup, inet_ntoa(*bcast));
+ ip_list = name_query(ServerFD,lookup,lookup_type,
+ use_bcast,
+ use_bcast?True:recursion_desired,
+ *bcast,&count, &flags, NULL);
+ }
+ }
+
+ if (!ip_list) return False;
+
+ if (give_flags)
+ d_printf("Flags: %s\n", query_flags(flags));
+
+ for (j=0;j<count;j++) {
+ if (translate_addresses) {
+ struct hostent *host = gethostbyaddr((char *)&ip_list[j], sizeof(ip_list[j]), AF_INET);
+ if (host) {
+ d_printf("%s, ", host -> h_name);
+ }
+ }
+ d_printf("%s %s<%02x>\n",inet_ntoa(ip_list[j]),lookup, lookup_type);
+ }
+
+ /* We can only do find_status if the ip address returned
+ was valid - ie. name_query returned true.
+ */
+ if (find_status) {
+ do_node_status(ServerFD, lookup, lookup_type, ip_list[0]);
+ }
+
+ safe_free(ip_list);
+
+ return (ip_list != NULL);
+}
+
+
+/****************************************************************************
+ main program
+****************************************************************************/
+int main(int argc,char *argv[])
+{
+ int opt;
+ unsigned int lookup_type = 0x0;
+ fstring lookup;
+ static BOOL find_master=False;
+ static BOOL lookup_by_ip = False;
+ poptContext pc;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "broadcast", 'B', POPT_ARG_STRING, NULL, 'B', "Specify address to use for broadcasts", "BROADCAST-ADDRESS" },
+ { "flags", 'f', POPT_ARG_VAL, &give_flags, True, "List the NMB flags returned" },
+ { "unicast", 'U', POPT_ARG_STRING, NULL, 'U', "Specify address to use for unicast" },
+ { "master-browser", 'M', POPT_ARG_VAL, &find_master, True, "Search for a master browser" },
+ { "recursion", 'R', POPT_ARG_VAL, &recursion_desired, True, "Set recursion desired in package" },
+ { "status", 'S', POPT_ARG_VAL, &find_status, True, "Lookup node status as well" },
+ { "translate", 'T', POPT_ARG_NONE, NULL, 'T', "Translate IP addresses into names" },
+ { "root-port", 'r', POPT_ARG_VAL, &RootPort, True, "Use root port 137 (Win95 only replies to this)" },
+ { "lookup-by-ip", 'A', POPT_ARG_VAL, &lookup_by_ip, True, "Do a node status on <name> as an IP Address" },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ { 0, 0, 0, 0 }
+ };
+
+ *lookup = 0;
+
+ setup_logging(argv[0],True);
+
+ pc = poptGetContext("nmblookup", argc, (const char **)argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ poptSetOtherOptionHelp(pc, "<NODE> ...");
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'B':
+ bcast_addr = *interpret_addr2(poptGetOptArg(pc));
+ got_bcast = True;
+ use_bcast = True;
+ break;
+ case 'U':
+ bcast_addr = *interpret_addr2(poptGetOptArg(pc));
+ got_bcast = True;
+ use_bcast = False;
+ break;
+ case 'T':
+ translate_addresses = !translate_addresses;
+ break;
+ }
+ }
+
+ poptGetArg(pc); /* Remove argv[0] */
+
+ if(!poptPeekArg(pc)) {
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+
+ if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
+ fprintf(stderr, "Can't load %s - run testparm to debug it\n", dyn_CONFIGFILE);
+ }
+
+ load_interfaces();
+ if (!open_sockets()) return(1);
+
+ while(poptPeekArg(pc))
+ {
+ char *p;
+ struct in_addr ip;
+
+ fstrcpy(lookup,poptGetArg(pc));
+
+ if(lookup_by_ip)
+ {
+ ip = *interpret_addr2(lookup);
+ fstrcpy(lookup,"*");
+ do_node_status(ServerFD, lookup, lookup_type, ip);
+ continue;
+ }
+
+ if (find_master) {
+ if (*lookup == '-') {
+ fstrcpy(lookup,"\01\02__MSBROWSE__\02");
+ lookup_type = 1;
+ } else {
+ lookup_type = 0x1d;
+ }
+ }
+
+ p = strchr_m(lookup,'#');
+ if (p) {
+ *p = '\0';
+ sscanf(++p,"%x",&lookup_type);
+ }
+
+ if (!query_one(lookup, lookup_type)) {
+ d_printf( "name_query failed to find name %s", lookup );
+ if( 0 != lookup_type )
+ d_printf( "#%02x", lookup_type );
+ d_printf( "\n" );
+ }
+ }
+
+ poptFreeContext(pc);
+
+ return(0);
+}
diff --git a/source/utils/ntlm_auth.c b/source/utils/ntlm_auth.c
new file mode 100644
index 00000000000..2213a9bae37
--- /dev/null
+++ b/source/utils/ntlm_auth.c
@@ -0,0 +1,2182 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind status program.
+
+ Copyright (C) Tim Potter 2000-2002
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
+ Copyright (C) Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it> 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+#define SQUID_BUFFER_SIZE 2010
+
+enum stdio_helper_mode {
+ SQUID_2_4_BASIC,
+ SQUID_2_5_BASIC,
+ SQUID_2_5_NTLMSSP,
+ NTLMSSP_CLIENT_1,
+ GSS_SPNEGO,
+ GSS_SPNEGO_CLIENT,
+ NUM_HELPER_MODES
+};
+
+enum ntlm_break {
+ BREAK_NONE,
+ BREAK_LM,
+ BREAK_NT,
+ NO_LM,
+ NO_NT
+};
+
+typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length);
+
+static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length);
+
+static void manage_squid_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length);
+
+static void manage_client_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length);
+
+static void manage_gss_spnego_request (enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length);
+
+static void manage_gss_spnego_client_request (enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length);
+
+static const struct {
+ enum stdio_helper_mode mode;
+ const char *name;
+ stdio_helper_function fn;
+} stdio_helper_protocols[] = {
+ { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request},
+ { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request},
+ { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_squid_ntlmssp_request},
+ { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_client_ntlmssp_request},
+ { GSS_SPNEGO, "gss-spnego", manage_gss_spnego_request},
+ { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request},
+ { NUM_HELPER_MODES, NULL, NULL}
+};
+
+extern int winbindd_fd;
+
+static const char *opt_username;
+static const char *opt_domain;
+static const char *opt_workstation;
+static const char *opt_password;
+static DATA_BLOB opt_challenge;
+static DATA_BLOB opt_lm_response;
+static DATA_BLOB opt_nt_response;
+static int request_lm_key;
+static int request_nt_key;
+
+
+static char winbind_separator(void)
+{
+ struct winbindd_response response;
+ static BOOL got_sep;
+ static char sep;
+
+ if (got_sep)
+ return sep;
+
+ ZERO_STRUCT(response);
+
+ /* Send off request */
+
+ if (winbindd_request(WINBINDD_INFO, NULL, &response) !=
+ NSS_STATUS_SUCCESS) {
+ d_printf("could not obtain winbind separator!\n");
+ return '\\';
+ }
+
+ sep = response.data.info.winbind_separator;
+ got_sep = True;
+
+ if (!sep) {
+ d_printf("winbind separator was NULL!\n");
+ return '\\';
+ }
+
+ return sep;
+}
+
+static const char *get_winbind_domain(void)
+{
+ struct winbindd_response response;
+
+ static fstring winbind_domain;
+ if (*winbind_domain) {
+ return winbind_domain;
+ }
+
+ ZERO_STRUCT(response);
+
+ /* Send off request */
+
+ if (winbindd_request(WINBINDD_DOMAIN_NAME, NULL, &response) !=
+ NSS_STATUS_SUCCESS) {
+ DEBUG(0, ("could not obtain winbind domain name!\n"));
+ return NULL;
+ }
+
+ fstrcpy(winbind_domain, response.data.domain_name);
+
+ return winbind_domain;
+
+}
+
+static const char *get_winbind_netbios_name(void)
+{
+ struct winbindd_response response;
+
+ static fstring winbind_netbios_name;
+
+ if (*winbind_netbios_name) {
+ return winbind_netbios_name;
+ }
+
+ ZERO_STRUCT(response);
+
+ /* Send off request */
+
+ if (winbindd_request(WINBINDD_NETBIOS_NAME, NULL, &response) !=
+ NSS_STATUS_SUCCESS) {
+ DEBUG(0, ("could not obtain winbind netbios name!\n"));
+ return NULL;
+ }
+
+ fstrcpy(winbind_netbios_name, response.data.netbios_name);
+
+ return winbind_netbios_name;
+
+}
+
+/* Authenticate a user with a plaintext password */
+
+static BOOL check_plaintext_auth(const char *user, const char *pass, BOOL stdout_diagnostics)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.data.auth.user, user);
+ fstrcpy(request.data.auth.pass, pass);
+
+ result = winbindd_request(WINBINDD_PAM_AUTH, &request, &response);
+
+ /* Display response */
+
+ if (stdout_diagnostics) {
+ if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
+ d_printf("Reading winbind reply failed! (0x01)\n");
+ }
+
+ d_printf("%s: %s (0x%x)\n",
+ response.data.auth.nt_status_string,
+ response.data.auth.error_string,
+ response.data.auth.nt_status);
+ } else {
+ if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
+ DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
+ }
+
+ DEBUG(3, ("%s: %s (0x%x)\n",
+ response.data.auth.nt_status_string,
+ response.data.auth.error_string,
+ response.data.auth.nt_status));
+ }
+
+ return (result == NSS_STATUS_SUCCESS);
+}
+
+/* authenticate a user with an encrypted username/password */
+
+static NTSTATUS contact_winbind_auth_crap(const char *username,
+ const char *domain,
+ const char *workstation,
+ const DATA_BLOB *challenge,
+ const DATA_BLOB *lm_response,
+ const DATA_BLOB *nt_response,
+ uint32 flags,
+ uint8 lm_key[8],
+ uint8 nt_key[16],
+ char **error_string,
+ char **unix_name)
+{
+ NTSTATUS nt_status;
+ NSS_STATUS result;
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ static uint8 zeros[16];
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.flags = flags;
+
+ if (push_utf8_fstring(request.data.auth_crap.user, username) == -1) {
+ *error_string = smb_xstrdup(
+ "unable to create utf8 string for username");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (push_utf8_fstring(request.data.auth_crap.domain, domain) == -1) {
+ *error_string = smb_xstrdup(
+ "unable to create utf8 string for domain");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (push_utf8_fstring(request.data.auth_crap.workstation,
+ workstation) == -1) {
+ *error_string = smb_xstrdup(
+ "unable to create utf8 string for workstation");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8));
+
+ if (lm_response && lm_response->length) {
+ memcpy(request.data.auth_crap.lm_resp, lm_response->data, MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp)));
+ request.data.auth_crap.lm_resp_len = lm_response->length;
+ }
+
+ if (nt_response && nt_response->length) {
+ memcpy(request.data.auth_crap.nt_resp, nt_response->data, MIN(nt_response->length, sizeof(request.data.auth_crap.nt_resp)));
+ request.data.auth_crap.nt_resp_len = nt_response->length;
+ }
+
+ result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response);
+
+ /* Display response */
+
+ if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ if (error_string)
+ *error_string = smb_xstrdup("Reading winbind reply failed!");
+ return nt_status;
+ }
+
+ nt_status = (NT_STATUS(response.data.auth.nt_status));
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ if (error_string)
+ *error_string = smb_xstrdup(response.data.auth.error_string);
+ return nt_status;
+ }
+
+ if ((flags & WBFLAG_PAM_LMKEY) && lm_key
+ && (memcmp(zeros, response.data.auth.first_8_lm_hash,
+ sizeof(response.data.auth.first_8_lm_hash)) != 0)) {
+ memcpy(lm_key, response.data.auth.first_8_lm_hash,
+ sizeof(response.data.auth.first_8_lm_hash));
+ }
+ if ((flags & WBFLAG_PAM_NTKEY) && nt_key
+ && (memcmp(zeros, response.data.auth.nt_session_key,
+ sizeof(response.data.auth.nt_session_key)) != 0)) {
+ memcpy(nt_key, response.data.auth.nt_session_key,
+ sizeof(response.data.auth.nt_session_key));
+ }
+
+ if (flags & WBFLAG_PAM_UNIX_NAME) {
+ if (pull_utf8_allocate(unix_name, (char *)response.extra_data) == -1) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ return nt_status;
+}
+
+static NTSTATUS winbind_pw_check(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *nt_session_key, DATA_BLOB *lm_session_key)
+{
+ static const char zeros[16];
+ NTSTATUS nt_status;
+ char *error_string;
+ uint8 lm_key[8];
+ uint8 nt_key[16];
+ char *unix_name;
+
+ nt_status = contact_winbind_auth_crap(ntlmssp_state->user, ntlmssp_state->domain,
+ ntlmssp_state->workstation,
+ &ntlmssp_state->chal,
+ &ntlmssp_state->lm_resp,
+ &ntlmssp_state->nt_resp,
+ WBFLAG_PAM_LMKEY | WBFLAG_PAM_NTKEY | WBFLAG_PAM_UNIX_NAME,
+ lm_key, nt_key,
+ &error_string, &unix_name);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ if (memcmp(lm_key, zeros, 8) != 0) {
+ *lm_session_key = data_blob(NULL, 16);
+ memcpy(lm_session_key->data, lm_key, 8);
+ memset(lm_session_key->data+8, '\0', 8);
+ }
+
+ if (memcmp(nt_key, zeros, 16) != 0) {
+ *nt_session_key = data_blob(nt_key, 16);
+ }
+ ntlmssp_state->auth_context = talloc_strdup(ntlmssp_state->mem_ctx, unix_name);
+ SAFE_FREE(unix_name);
+ } else {
+ DEBUG(NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED) ? 0 : 3,
+ ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
+ ntlmssp_state->domain, ntlmssp_state->user, ntlmssp_state->workstation, error_string ? error_string : "unknown error (NULL)"));
+ ntlmssp_state->auth_context = NULL;
+ }
+ return nt_status;
+}
+
+static NTSTATUS local_pw_check(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *nt_session_key, DATA_BLOB *lm_session_key)
+{
+ static const char zeros[16];
+ NTSTATUS nt_status;
+ uint8 lm_key[8];
+ uint8 nt_key[16];
+ uint8 lm_pw[16], nt_pw[16];
+
+ nt_lm_owf_gen (opt_password, nt_pw, lm_pw);
+
+ nt_status = ntlm_password_check(ntlmssp_state->mem_ctx,
+ &ntlmssp_state->chal,
+ &ntlmssp_state->lm_resp,
+ &ntlmssp_state->nt_resp,
+ ntlmssp_state->user,
+ ntlmssp_state->user,
+ ntlmssp_state->domain,
+ lm_pw, nt_pw, nt_session_key, lm_session_key);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ if (memcmp(lm_key, zeros, 8) != 0) {
+ *lm_session_key = data_blob(NULL, 16);
+ memcpy(lm_session_key->data, lm_key, 8);
+ memset(lm_session_key->data+8, '\0', 8);
+ }
+
+ if (memcmp(nt_key, zeros, 16) != 0) {
+ *nt_session_key = data_blob(nt_key, 16);
+ }
+ ntlmssp_state->auth_context = talloc_asprintf(ntlmssp_state->mem_ctx, "%s%c%s", ntlmssp_state->domain, *lp_winbind_separator(), ntlmssp_state->user);
+ } else {
+ DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
+ ntlmssp_state->domain, ntlmssp_state->user, ntlmssp_state->workstation,
+ nt_errstr(nt_status)));
+ ntlmssp_state->auth_context = NULL;
+ }
+ return nt_status;
+}
+
+static NTSTATUS ntlm_auth_start_ntlmssp_client(NTLMSSP_STATE **client_ntlmssp_state)
+{
+ NTSTATUS status;
+ if ( (opt_username == NULL) || (opt_domain == NULL) ) {
+ DEBUG(1, ("Need username and domain for NTLMSSP\n"));
+ return status;
+ }
+
+ status = ntlmssp_client_start(client_ntlmssp_state);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not start NTLMSSP client: %s\n",
+ nt_errstr(status)));
+ ntlmssp_end(client_ntlmssp_state);
+ return status;
+ }
+
+ status = ntlmssp_set_username(*client_ntlmssp_state, opt_username);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not set username: %s\n",
+ nt_errstr(status)));
+ ntlmssp_end(client_ntlmssp_state);
+ return status;
+ }
+
+ status = ntlmssp_set_domain(*client_ntlmssp_state, opt_domain);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not set domain: %s\n",
+ nt_errstr(status)));
+ ntlmssp_end(client_ntlmssp_state);
+ return status;
+ }
+
+ status = ntlmssp_set_password(*client_ntlmssp_state, opt_password);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not set password: %s\n",
+ nt_errstr(status)));
+ ntlmssp_end(client_ntlmssp_state);
+ return status;
+ }
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ntlm_auth_start_ntlmssp_server(NTLMSSP_STATE **ntlmssp_state)
+{
+ NTSTATUS status = ntlmssp_server_start(ntlmssp_state);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not start NTLMSSP client: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ /* Have we been given a local password, or should we ask winbind? */
+ if (opt_password) {
+ (*ntlmssp_state)->check_password = local_pw_check;
+ (*ntlmssp_state)->get_domain = lp_workgroup;
+ (*ntlmssp_state)->get_global_myname = global_myname;
+ } else {
+ (*ntlmssp_state)->check_password = winbind_pw_check;
+ (*ntlmssp_state)->get_domain = get_winbind_domain;
+ (*ntlmssp_state)->get_global_myname = get_winbind_netbios_name;
+ }
+ return NT_STATUS_OK;
+}
+
+static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length)
+{
+ static NTLMSSP_STATE *ntlmssp_state = NULL;
+ DATA_BLOB request, reply;
+ NTSTATUS nt_status;
+
+ if (strlen(buf) < 2) {
+ DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (strlen(buf) > 3) {
+ request = base64_decode_data_blob(buf + 3);
+ } else {
+ request = data_blob(NULL, 0);
+ }
+
+ if ((strncmp(buf, "PW ", 3) == 0)) {
+ /* The calling application wants us to use a local password (rather than winbindd) */
+
+ opt_password = strndup((const char *)request.data, request.length);
+
+ if (opt_password == NULL) {
+ DEBUG(1, ("Out of memory\n"));
+ x_fprintf(x_stdout, "BH\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ x_fprintf(x_stdout, "OK\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ if (strncmp(buf, "YR", 2) == 0) {
+ if (ntlmssp_state)
+ ntlmssp_end(&ntlmssp_state);
+ } else if (strncmp(buf, "KK", 2) == 0) {
+
+ } else {
+ DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (!ntlmssp_state) {
+ if (!NT_STATUS_IS_OK(nt_status = ntlm_auth_start_ntlmssp_server(&ntlmssp_state))) {
+ x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
+ return;
+ }
+ }
+
+ DEBUG(10, ("got NTLMSSP packet:\n"));
+ dump_data(10, (const char *)request.data, request.length);
+
+ nt_status = ntlmssp_update(ntlmssp_state, request, &reply);
+
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ char *reply_base64 = base64_encode_data_blob(reply);
+ x_fprintf(x_stdout, "TT %s\n", reply_base64);
+ SAFE_FREE(reply_base64);
+ data_blob_free(&reply);
+ DEBUG(10, ("NTLMSSP challenge\n"));
+ } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
+ x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
+ DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
+
+ ntlmssp_end(&ntlmssp_state);
+ } else if (!NT_STATUS_IS_OK(nt_status)) {
+ x_fprintf(x_stdout, "NA %s\n", nt_errstr(nt_status));
+ DEBUG(10, ("NTLMSSP %s\n", nt_errstr(nt_status)));
+ } else {
+ x_fprintf(x_stdout, "AF %s\n", (char *)ntlmssp_state->auth_context);
+ DEBUG(10, ("NTLMSSP OK!\n"));
+ }
+
+ data_blob_free(&request);
+}
+
+static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length)
+{
+ static NTLMSSP_STATE *ntlmssp_state = NULL;
+ DATA_BLOB request, reply;
+ NTSTATUS nt_status;
+ BOOL first = False;
+
+ if (strlen(buf) < 2) {
+ DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (strlen(buf) > 3) {
+ request = base64_decode_data_blob(buf + 3);
+ } else {
+ request = data_blob(NULL, 0);
+ }
+
+ if (strncmp(buf, "PW ", 3) == 0) {
+ /* We asked for a password and obviously got it :-) */
+
+ opt_password = strndup((const char *)request.data, request.length);
+
+ if (opt_password == NULL) {
+ DEBUG(1, ("Out of memory\n"));
+ x_fprintf(x_stdout, "BH\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ x_fprintf(x_stdout, "OK\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ if (opt_password == NULL) {
+
+ /* Request a password from the calling process. After
+ sending it, the calling process should retry asking for the negotiate. */
+
+ DEBUG(10, ("Requesting password\n"));
+ x_fprintf(x_stdout, "PW\n");
+ return;
+ }
+
+ if (strncmp(buf, "YR", 2) == 0) {
+ if (ntlmssp_state)
+ ntlmssp_end(&ntlmssp_state);
+ } else if (strncmp(buf, "TT", 2) == 0) {
+
+ } else {
+ DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (!ntlmssp_state) {
+ if (!NT_STATUS_IS_OK(nt_status = ntlm_auth_start_ntlmssp_client(&ntlmssp_state))) {
+ x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
+ return;
+ }
+ first = True;
+ }
+
+ DEBUG(10, ("got NTLMSSP packet:\n"));
+ dump_data(10, (const char *)request.data, request.length);
+
+ nt_status = ntlmssp_update(ntlmssp_state, request, &reply);
+
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ char *reply_base64 = base64_encode_data_blob(reply);
+ if (first) {
+ x_fprintf(x_stdout, "YR %s\n", reply_base64);
+ } else {
+ x_fprintf(x_stdout, "KK %s\n", reply_base64);
+ }
+ SAFE_FREE(reply_base64);
+ data_blob_free(&reply);
+ DEBUG(10, ("NTLMSSP challenge\n"));
+ } else if (NT_STATUS_IS_OK(nt_status)) {
+ x_fprintf(x_stdout, "AF\n");
+ DEBUG(10, ("NTLMSSP OK!\n"));
+ if (ntlmssp_state)
+ ntlmssp_end(&ntlmssp_state);
+ } else {
+ x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
+ DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
+ if (ntlmssp_state)
+ ntlmssp_end(&ntlmssp_state);
+ }
+
+ data_blob_free(&request);
+}
+
+static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length)
+{
+ char *user, *pass;
+ user=buf;
+
+ pass=memchr(buf,' ',length);
+ if (!pass) {
+ DEBUG(2, ("Password not found. Denying access\n"));
+ x_fprintf(x_stderr, "ERR\n");
+ return;
+ }
+ *pass='\0';
+ pass++;
+
+ if (stdio_helper_mode == SQUID_2_5_BASIC) {
+ rfc1738_unescape(user);
+ rfc1738_unescape(pass);
+ }
+
+ if (check_plaintext_auth(user, pass, False)) {
+ x_fprintf(x_stdout, "OK\n");
+ } else {
+ x_fprintf(x_stdout, "ERR\n");
+ }
+}
+
+static void offer_gss_spnego_mechs(void) {
+
+ DATA_BLOB token;
+ SPNEGO_DATA spnego;
+ ssize_t len;
+ char *reply_base64;
+
+ pstring principal;
+ pstring myname_lower;
+
+ ZERO_STRUCT(spnego);
+
+ pstrcpy(myname_lower, global_myname());
+ strlower_m(myname_lower);
+
+ pstr_sprintf(principal, "%s$@%s", myname_lower, lp_realm());
+
+ /* Server negTokenInit (mech offerings) */
+ spnego.type = SPNEGO_NEG_TOKEN_INIT;
+ spnego.negTokenInit.mechTypes = smb_xmalloc(sizeof(char *) * 3);
+#ifdef HAVE_KRB5
+ spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_KERBEROS5_OLD);
+ spnego.negTokenInit.mechTypes[1] = smb_xstrdup(OID_NTLMSSP);
+ spnego.negTokenInit.mechTypes[2] = NULL;
+#else
+ spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_NTLMSSP);
+ spnego.negTokenInit.mechTypes[1] = NULL;
+#endif
+
+
+ spnego.negTokenInit.mechListMIC = data_blob(principal,
+ strlen(principal));
+
+ len = write_spnego_data(&token, &spnego);
+ free_spnego_data(&spnego);
+
+ if (len == -1) {
+ DEBUG(1, ("Could not write SPNEGO data blob\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ reply_base64 = base64_encode_data_blob(token);
+ x_fprintf(x_stdout, "TT %s *\n", reply_base64);
+
+ SAFE_FREE(reply_base64);
+ data_blob_free(&token);
+ DEBUG(10, ("sent SPNEGO negTokenInit\n"));
+ return;
+}
+
+static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length)
+{
+ static NTLMSSP_STATE *ntlmssp_state = NULL;
+ SPNEGO_DATA request, response;
+ DATA_BLOB token;
+ NTSTATUS status;
+ ssize_t len;
+
+ char *user = NULL;
+ char *domain = NULL;
+
+ const char *reply_code;
+ char *reply_base64;
+ pstring reply_argument;
+
+ if (strlen(buf) < 2) {
+
+ if (ntlmssp_state != NULL) {
+ DEBUG(1, ("Request for initial SPNEGO request where "
+ "we already have a state\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if ( (strlen(buf) == 2) && (strcmp(buf, "YR") == 0) ) {
+
+ /* Initial request, get the negTokenInit offering
+ mechanisms */
+
+ offer_gss_spnego_mechs();
+ return;
+ }
+
+ /* All subsequent requests are "KK" (Knock, Knock ;)) and have
+ a blob. This might be negTokenInit or negTokenTarg */
+
+ if ( (strlen(buf) <= 3) || (strncmp(buf, "KK", 2) != 0) ) {
+ DEBUG(1, ("GSS-SPNEGO query [%s] invalid\n", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ token = base64_decode_data_blob(buf + 3);
+ len = read_spnego_data(token, &request);
+ data_blob_free(&token);
+
+ if (len == -1) {
+ DEBUG(1, ("GSS-SPNEGO query [%s] invalid", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (request.type == SPNEGO_NEG_TOKEN_INIT) {
+
+ /* Second request from Client. This is where the
+ client offers its mechanism to use. */
+
+ if ( (request.negTokenInit.mechTypes == NULL) ||
+ (request.negTokenInit.mechTypes[0] == NULL) ) {
+ DEBUG(1, ("Client did not offer any mechanism"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (strcmp(request.negTokenInit.mechTypes[0], OID_NTLMSSP) == 0) {
+
+ if ( request.negTokenInit.mechToken.data == NULL ) {
+ DEBUG(1, ("Client did not provide NTLMSSP data\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if ( ntlmssp_state != NULL ) {
+ DEBUG(1, ("Client wants a new NTLMSSP challenge, but "
+ "already got one\n"));
+ x_fprintf(x_stdout, "BH\n");
+ ntlmssp_end(&ntlmssp_state);
+ return;
+ }
+
+ if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_server(&ntlmssp_state))) {
+ x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
+ return;
+ }
+
+ DEBUG(10, ("got NTLMSSP packet:\n"));
+ dump_data(10, (const char *)request.negTokenInit.mechToken.data,
+ request.negTokenInit.mechToken.length);
+
+ response.type = SPNEGO_NEG_TOKEN_TARG;
+ response.negTokenTarg.supportedMech = strdup(OID_NTLMSSP);
+ response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
+
+ status = ntlmssp_update(ntlmssp_state,
+ request.negTokenInit.mechToken,
+ &response.negTokenTarg.responseToken);
+ }
+
+#ifdef HAVE_KRB5
+ if (strcmp(request.negTokenInit.mechTypes[0], OID_KERBEROS5_OLD) == 0) {
+
+ char *principal;
+ DATA_BLOB auth_data;
+ DATA_BLOB ap_rep;
+ DATA_BLOB session_key;
+
+ if ( request.negTokenInit.mechToken.data == NULL ) {
+ DEBUG(1, ("Client did not provide Kerberos data\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ response.type = SPNEGO_NEG_TOKEN_TARG;
+ response.negTokenTarg.supportedMech = strdup(OID_KERBEROS5_OLD);
+ response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
+ response.negTokenTarg.responseToken = data_blob(NULL, 0);
+
+ status = ads_verify_ticket(lp_realm(),
+ &request.negTokenInit.mechToken,
+ &principal, &auth_data, &ap_rep,
+ &session_key);
+
+ /* Now in "principal" we have the name we are
+ authenticated as. */
+
+ if (NT_STATUS_IS_OK(status)) {
+
+ domain = strchr(principal, '@');
+
+ if (domain == NULL) {
+ DEBUG(1, ("Did not get a valid principal "
+ "from ads_verify_ticket\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ *domain++ = '\0';
+ domain = strdup(domain);
+ user = strdup(principal);
+
+ data_blob_free(&ap_rep);
+ data_blob_free(&auth_data);
+
+ SAFE_FREE(principal);
+ }
+ }
+#endif
+
+ } else {
+
+ if ( (request.negTokenTarg.supportedMech == NULL) ||
+ ( strcmp(request.negTokenTarg.supportedMech, OID_NTLMSSP) != 0 ) ) {
+ /* Kerberos should never send a negTokenTarg, OID_NTLMSSP
+ is the only one we support that sends this stuff */
+ DEBUG(1, ("Got a negTokenTarg for something non-NTLMSSP: %s\n",
+ request.negTokenTarg.supportedMech));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (request.negTokenTarg.responseToken.data == NULL) {
+ DEBUG(1, ("Got a negTokenTarg without a responseToken!\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ status = ntlmssp_update(ntlmssp_state,
+ request.negTokenTarg.responseToken,
+ &response.negTokenTarg.responseToken);
+
+ response.type = SPNEGO_NEG_TOKEN_TARG;
+ response.negTokenTarg.supportedMech = strdup(OID_NTLMSSP);
+ response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
+
+ if (NT_STATUS_IS_OK(status)) {
+ user = strdup(ntlmssp_state->user);
+ domain = strdup(ntlmssp_state->domain);
+ ntlmssp_end(&ntlmssp_state);
+ }
+ }
+
+ free_spnego_data(&request);
+
+ if (NT_STATUS_IS_OK(status)) {
+ response.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
+ reply_code = "AF";
+ pstr_sprintf(reply_argument, "%s\\%s", domain, user);
+ } else if (NT_STATUS_EQUAL(status,
+ NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ response.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
+ reply_code = "TT";
+ pstr_sprintf(reply_argument, "*");
+ } else {
+ response.negTokenTarg.negResult = SPNEGO_REJECT;
+ reply_code = "NA";
+ pstrcpy(reply_argument, nt_errstr(status));
+ }
+
+ SAFE_FREE(user);
+ SAFE_FREE(domain);
+
+ len = write_spnego_data(&token, &response);
+ free_spnego_data(&response);
+
+ if (len == -1) {
+ DEBUG(1, ("Could not write SPNEGO data blob\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ reply_base64 = base64_encode_data_blob(token);
+
+ x_fprintf(x_stdout, "%s %s %s\n",
+ reply_code, reply_base64, reply_argument);
+
+ SAFE_FREE(reply_base64);
+ data_blob_free(&token);
+
+ return;
+}
+
+static NTLMSSP_STATE *client_ntlmssp_state = NULL;
+
+static BOOL manage_client_ntlmssp_init(SPNEGO_DATA spnego)
+{
+ NTSTATUS status;
+ DATA_BLOB null_blob = data_blob(NULL, 0);
+ DATA_BLOB to_server;
+ char *to_server_base64;
+ const char *my_mechs[] = {OID_NTLMSSP, NULL};
+
+ DEBUG(10, ("Got spnego negTokenInit with NTLMSSP\n"));
+
+ if (client_ntlmssp_state != NULL) {
+ DEBUG(1, ("Request for initial SPNEGO request where "
+ "we already have a state\n"));
+ return False;
+ }
+
+ if (!client_ntlmssp_state) {
+ if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_client(&client_ntlmssp_state))) {
+ x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
+ return False;
+ }
+ }
+
+
+ if (opt_password == NULL) {
+
+ /* Request a password from the calling process. After
+ sending it, the calling process should retry with
+ the negTokenInit. */
+
+ DEBUG(10, ("Requesting password\n"));
+ x_fprintf(x_stdout, "PW\n");
+ return True;
+ }
+
+ spnego.type = SPNEGO_NEG_TOKEN_INIT;
+ spnego.negTokenInit.mechTypes = my_mechs;
+ spnego.negTokenInit.reqFlags = 0;
+ spnego.negTokenInit.mechListMIC = null_blob;
+
+ status = ntlmssp_update(client_ntlmssp_state, null_blob,
+ &spnego.negTokenInit.mechToken);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED, got: %s\n",
+ nt_errstr(status)));
+ ntlmssp_end(&client_ntlmssp_state);
+ return False;
+ }
+
+ write_spnego_data(&to_server, &spnego);
+ data_blob_free(&spnego.negTokenInit.mechToken);
+
+ to_server_base64 = base64_encode_data_blob(to_server);
+ data_blob_free(&to_server);
+ x_fprintf(x_stdout, "KK %s\n", to_server_base64);
+ SAFE_FREE(to_server_base64);
+ return True;
+}
+
+static void manage_client_ntlmssp_targ(SPNEGO_DATA spnego)
+{
+ NTSTATUS status;
+ DATA_BLOB null_blob = data_blob(NULL, 0);
+ DATA_BLOB request;
+ DATA_BLOB to_server;
+ char *to_server_base64;
+
+ DEBUG(10, ("Got spnego negTokenTarg with NTLMSSP\n"));
+
+ if (client_ntlmssp_state == NULL) {
+ DEBUG(1, ("Got NTLMSSP tArg without a client state\n"));
+ x_fprintf(x_stdout, "BH\n");
+ ntlmssp_end(&client_ntlmssp_state);
+ return;
+ }
+
+ if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
+ x_fprintf(x_stdout, "NA\n");
+ ntlmssp_end(&client_ntlmssp_state);
+ return;
+ }
+
+ if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
+ x_fprintf(x_stdout, "AF\n");
+ ntlmssp_end(&client_ntlmssp_state);
+ return;
+ }
+
+ status = ntlmssp_update(client_ntlmssp_state,
+ spnego.negTokenTarg.responseToken,
+ &request);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED from "
+ "ntlmssp_client_update, got: %s\n",
+ nt_errstr(status)));
+ x_fprintf(x_stdout, "BH\n");
+ data_blob_free(&request);
+ ntlmssp_end(&client_ntlmssp_state);
+ return;
+ }
+
+ spnego.type = SPNEGO_NEG_TOKEN_TARG;
+ spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
+ spnego.negTokenTarg.supportedMech = OID_NTLMSSP;
+ spnego.negTokenTarg.responseToken = request;
+ spnego.negTokenTarg.mechListMIC = null_blob;
+
+ write_spnego_data(&to_server, &spnego);
+ data_blob_free(&request);
+
+ to_server_base64 = base64_encode_data_blob(to_server);
+ data_blob_free(&to_server);
+ x_fprintf(x_stdout, "KK %s\n", to_server_base64);
+ SAFE_FREE(to_server_base64);
+ return;
+}
+
+#ifdef HAVE_KRB5
+
+static BOOL manage_client_krb5_init(SPNEGO_DATA spnego)
+{
+ char *principal;
+ DATA_BLOB tkt, to_server;
+ DATA_BLOB session_key_krb5;
+ SPNEGO_DATA reply;
+ char *reply_base64;
+ int retval;
+
+ const char *my_mechs[] = {OID_KERBEROS5_OLD, NULL};
+ ssize_t len;
+
+ if ( (spnego.negTokenInit.mechListMIC.data == NULL) ||
+ (spnego.negTokenInit.mechListMIC.length == 0) ) {
+ DEBUG(1, ("Did not get a principal for krb5\n"));
+ return False;
+ }
+
+ principal = malloc(spnego.negTokenInit.mechListMIC.length+1);
+
+ if (principal == NULL) {
+ DEBUG(1, ("Could not malloc principal\n"));
+ return False;
+ }
+
+ memcpy(principal, spnego.negTokenInit.mechListMIC.data,
+ spnego.negTokenInit.mechListMIC.length);
+ principal[spnego.negTokenInit.mechListMIC.length] = '\0';
+
+ retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5);
+
+ if (retval) {
+
+ pstring user;
+
+ /* Let's try to first get the TGT, for that we need a
+ password. */
+
+ if (opt_password == NULL) {
+ DEBUG(10, ("Requesting password\n"));
+ x_fprintf(x_stdout, "PW\n");
+ return True;
+ }
+
+ pstr_sprintf(user, "%s@%s", opt_username, opt_domain);
+
+ if ((retval = kerberos_kinit_password(user, opt_password,
+ 0, NULL))) {
+ DEBUG(10, ("Requesting TGT failed: %s\n", error_message(retval)));
+ x_fprintf(x_stdout, "NA\n");
+ return True;
+ }
+
+ retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5);
+
+ if (retval) {
+ DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval)));
+ }
+ }
+
+ data_blob_free(&session_key_krb5);
+
+ ZERO_STRUCT(reply);
+
+ reply.type = SPNEGO_NEG_TOKEN_INIT;
+ reply.negTokenInit.mechTypes = my_mechs;
+ reply.negTokenInit.reqFlags = 0;
+ reply.negTokenInit.mechToken = tkt;
+ reply.negTokenInit.mechListMIC = data_blob(NULL, 0);
+
+ len = write_spnego_data(&to_server, &reply);
+ data_blob_free(&tkt);
+
+ if (len == -1) {
+ DEBUG(1, ("Could not write SPNEGO data blob\n"));
+ return False;
+ }
+
+ reply_base64 = base64_encode_data_blob(to_server);
+ x_fprintf(x_stdout, "KK %s *\n", reply_base64);
+
+ SAFE_FREE(reply_base64);
+ data_blob_free(&to_server);
+ DEBUG(10, ("sent GSS-SPNEGO KERBEROS5 negTokenInit\n"));
+ return True;
+}
+
+static void manage_client_krb5_targ(SPNEGO_DATA spnego)
+{
+ switch (spnego.negTokenTarg.negResult) {
+ case SPNEGO_ACCEPT_INCOMPLETE:
+ DEBUG(1, ("Got a Kerberos negTokenTarg with ACCEPT_INCOMPLETE\n"));
+ x_fprintf(x_stdout, "BH\n");
+ break;
+ case SPNEGO_ACCEPT_COMPLETED:
+ DEBUG(10, ("Accept completed\n"));
+ x_fprintf(x_stdout, "AF\n");
+ break;
+ case SPNEGO_REJECT:
+ DEBUG(10, ("Rejected\n"));
+ x_fprintf(x_stdout, "NA\n");
+ break;
+ default:
+ DEBUG(1, ("Got an invalid negTokenTarg\n"));
+ x_fprintf(x_stdout, "AF\n");
+ }
+}
+
+#endif
+
+static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length)
+{
+ DATA_BLOB request;
+ SPNEGO_DATA spnego;
+ ssize_t len;
+
+ if (strlen(buf) <= 3) {
+ DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ request = base64_decode_data_blob(buf+3);
+
+ if (strncmp(buf, "PW ", 3) == 0) {
+
+ /* We asked for a password and obviously got it :-) */
+
+ opt_password = strndup((const char *)request.data, request.length);
+
+ if (opt_password == NULL) {
+ DEBUG(1, ("Out of memory\n"));
+ x_fprintf(x_stdout, "BH\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ x_fprintf(x_stdout, "OK\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ if ( (strncmp(buf, "TT ", 3) != 0) &&
+ (strncmp(buf, "AF ", 3) != 0) &&
+ (strncmp(buf, "NA ", 3) != 0) ) {
+ DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
+ x_fprintf(x_stdout, "BH\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ /* So we got a server challenge to generate a SPNEGO
+ client-to-server request... */
+
+ len = read_spnego_data(request, &spnego);
+ data_blob_free(&request);
+
+ if (len == -1) {
+ DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
+
+ /* The server offers a list of mechanisms */
+
+ const char **mechType = spnego.negTokenInit.mechTypes;
+
+ while (*mechType != NULL) {
+
+#ifdef HAVE_KRB5
+ if ( (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) ||
+ (strcmp(*mechType, OID_KERBEROS5) == 0) ) {
+ if (manage_client_krb5_init(spnego))
+ goto out;
+ }
+#endif
+
+ if (strcmp(*mechType, OID_NTLMSSP) == 0) {
+ if (manage_client_ntlmssp_init(spnego))
+ goto out;
+ }
+
+ mechType++;
+ }
+
+ DEBUG(1, ("Server offered no compatible mechanism\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (spnego.type == SPNEGO_NEG_TOKEN_TARG) {
+
+ if (spnego.negTokenTarg.supportedMech == NULL) {
+ /* On accept/reject Windows does not send the
+ mechanism anymore. Handle that here and
+ shut down the mechanisms. */
+
+ switch (spnego.negTokenTarg.negResult) {
+ case SPNEGO_ACCEPT_COMPLETED:
+ x_fprintf(x_stdout, "AF\n");
+ break;
+ case SPNEGO_REJECT:
+ x_fprintf(x_stdout, "NA\n");
+ break;
+ default:
+ DEBUG(1, ("Got a negTokenTarg with no mech and an "
+ "unknown negResult: %d\n",
+ spnego.negTokenTarg.negResult));
+ x_fprintf(x_stdout, "BH\n");
+ }
+
+ ntlmssp_end(&client_ntlmssp_state);
+ goto out;
+ }
+
+ if (strcmp(spnego.negTokenTarg.supportedMech,
+ OID_NTLMSSP) == 0) {
+ manage_client_ntlmssp_targ(spnego);
+ goto out;
+ }
+
+#if HAVE_KRB5
+ if (strcmp(spnego.negTokenTarg.supportedMech,
+ OID_KERBEROS5_OLD) == 0) {
+ manage_client_krb5_targ(spnego);
+ goto out;
+ }
+#endif
+
+ }
+
+ DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+
+ out:
+ free_spnego_data(&spnego);
+ return;
+}
+
+static void manage_squid_request(enum stdio_helper_mode helper_mode, stdio_helper_function fn)
+{
+ char buf[SQUID_BUFFER_SIZE+1];
+ int length;
+ char *c;
+ static BOOL err;
+
+ /* this is not a typo - x_fgets doesn't work too well under squid */
+ if (fgets(buf, sizeof(buf)-1, stdin) == NULL) {
+ DEBUG(1, ("fgets() failed! dying..... errno=%d (%s)\n", ferror(stdin),
+ strerror(ferror(stdin))));
+ exit(1); /* BIIG buffer */
+ }
+
+ c=memchr(buf,'\n',sizeof(buf)-1);
+ if (c) {
+ *c = '\0';
+ length = c-buf;
+ } else {
+ err = 1;
+ return;
+ }
+ if (err) {
+ DEBUG(2, ("Oversized message\n"));
+ x_fprintf(x_stderr, "ERR\n");
+ err = 0;
+ return;
+ }
+
+ DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
+
+ if (buf[0] == '\0') {
+ DEBUG(2, ("Invalid Request\n"));
+ x_fprintf(x_stderr, "ERR\n");
+ return;
+ }
+
+ fn(helper_mode, buf, length);
+}
+
+
+static void squid_stream(enum stdio_helper_mode stdio_mode, stdio_helper_function fn) {
+ /* initialize FDescs */
+ x_setbuf(x_stdout, NULL);
+ x_setbuf(x_stderr, NULL);
+ while(1) {
+ manage_squid_request(stdio_mode, fn);
+ }
+}
+
+
+/* Authenticate a user with a challenge/response */
+
+static BOOL check_auth_crap(void)
+{
+ NTSTATUS nt_status;
+ uint32 flags = 0;
+ char lm_key[8];
+ char nt_key[16];
+ char *hex_lm_key;
+ char *hex_nt_key;
+ char *error_string;
+ static uint8 zeros[16];
+
+ x_setbuf(x_stdout, NULL);
+
+ if (request_lm_key)
+ flags |= WBFLAG_PAM_LMKEY;
+
+ if (request_nt_key)
+ flags |= WBFLAG_PAM_NTKEY;
+
+ nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
+ opt_workstation,
+ &opt_challenge,
+ &opt_lm_response,
+ &opt_nt_response,
+ flags,
+ (unsigned char *)lm_key,
+ (unsigned char *)nt_key,
+ &error_string, NULL);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ x_fprintf(x_stdout, "%s (0x%x)\n",
+ error_string,
+ NT_STATUS_V(nt_status));
+ SAFE_FREE(error_string);
+ return False;
+ }
+
+ if (request_lm_key
+ && (memcmp(zeros, lm_key,
+ sizeof(lm_key)) != 0)) {
+ hex_encode((const unsigned char *)lm_key,
+ sizeof(lm_key),
+ &hex_lm_key);
+ x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
+ SAFE_FREE(hex_lm_key);
+ }
+ if (request_nt_key
+ && (memcmp(zeros, nt_key,
+ sizeof(nt_key)) != 0)) {
+ hex_encode((const unsigned char *)nt_key,
+ sizeof(nt_key),
+ &hex_nt_key);
+ x_fprintf(x_stdout, "NT_KEY: %s\n", hex_nt_key);
+ SAFE_FREE(hex_nt_key);
+ }
+
+ return True;
+}
+
+/*
+ Authenticate a user with a challenge/response, checking session key
+ and valid authentication types
+*/
+
+static DATA_BLOB get_challenge(void)
+{
+ static DATA_BLOB chal;
+ if (opt_challenge.length)
+ return opt_challenge;
+
+ chal = data_blob(NULL, 8);
+
+ generate_random_buffer(chal.data, chal.length, False);
+ return chal;
+}
+
+/*
+ * Test the normal 'LM and NTLM' combination
+ */
+
+static BOOL test_lm_ntlm_broken(enum ntlm_break break_which)
+{
+ BOOL pass = True;
+ NTSTATUS nt_status;
+ uint32 flags = 0;
+ DATA_BLOB lm_response = data_blob(NULL, 24);
+ DATA_BLOB nt_response = data_blob(NULL, 24);
+ DATA_BLOB session_key = data_blob(NULL, 16);
+
+ uchar lm_key[8];
+ uchar nt_key[16];
+ uchar lm_hash[16];
+ uchar nt_hash[16];
+ DATA_BLOB chall = get_challenge();
+ char *error_string;
+
+ ZERO_STRUCT(lm_key);
+ ZERO_STRUCT(nt_key);
+
+ flags |= WBFLAG_PAM_LMKEY;
+ flags |= WBFLAG_PAM_NTKEY;
+
+ SMBencrypt(opt_password,chall.data,lm_response.data);
+ E_deshash(opt_password, lm_hash);
+
+ SMBNTencrypt(opt_password,chall.data,nt_response.data);
+
+ E_md4hash(opt_password, nt_hash);
+ SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
+
+ switch (break_which) {
+ case BREAK_NONE:
+ break;
+ case BREAK_LM:
+ lm_response.data[0]++;
+ break;
+ case BREAK_NT:
+ nt_response.data[0]++;
+ break;
+ case NO_LM:
+ data_blob_free(&lm_response);
+ break;
+ case NO_NT:
+ data_blob_free(&nt_response);
+ break;
+ }
+
+ nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
+ opt_workstation,
+ &chall,
+ &lm_response,
+ &nt_response,
+ flags,
+ lm_key,
+ nt_key,
+ &error_string, NULL);
+
+ data_blob_free(&lm_response);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_printf("%s (0x%x)\n",
+ error_string,
+ NT_STATUS_V(nt_status));
+ SAFE_FREE(error_string);
+ return break_which == BREAK_NT;
+ }
+
+ if (memcmp(lm_hash, lm_key,
+ sizeof(lm_key)) != 0) {
+ DEBUG(1, ("LM Key does not match expectations!\n"));
+ DEBUG(1, ("lm_key:\n"));
+ dump_data(1, (const char *)lm_key, 8);
+ DEBUG(1, ("expected:\n"));
+ dump_data(1, (const char *)lm_hash, 8);
+ pass = False;
+ }
+
+ if (break_which == NO_NT) {
+ if (memcmp(lm_hash, nt_key,
+ 8) != 0) {
+ DEBUG(1, ("NT Session Key does not match expectations (should be LM hash)!\n"));
+ DEBUG(1, ("nt_key:\n"));
+ dump_data(1, (const char *)nt_key, sizeof(nt_key));
+ DEBUG(1, ("expected:\n"));
+ dump_data(1, (const char *)lm_hash, sizeof(lm_hash));
+ pass = False;
+ }
+ } else {
+ if (memcmp(session_key.data, nt_key,
+ sizeof(nt_key)) != 0) {
+ DEBUG(1, ("NT Session Key does not match expectations!\n"));
+ DEBUG(1, ("nt_key:\n"));
+ dump_data(1, (const char *)nt_key, 16);
+ DEBUG(1, ("expected:\n"));
+ dump_data(1, (const char *)session_key.data, session_key.length);
+ pass = False;
+ }
+ }
+ return pass;
+}
+
+/*
+ * Test LM authentication, no NT response supplied
+ */
+
+static BOOL test_lm(void)
+{
+
+ return test_lm_ntlm_broken(NO_NT);
+}
+
+/*
+ * Test the NTLM response only, no LM.
+ */
+
+static BOOL test_ntlm(void)
+{
+ return test_lm_ntlm_broken(NO_LM);
+}
+
+/*
+ * Test the NTLM response only, but in the LM field.
+ */
+
+static BOOL test_ntlm_in_lm(void)
+{
+ BOOL pass = True;
+ NTSTATUS nt_status;
+ uint32 flags = 0;
+ DATA_BLOB nt_response = data_blob(NULL, 24);
+
+ uchar lm_key[8];
+ uchar lm_hash[16];
+ uchar nt_key[16];
+ DATA_BLOB chall = get_challenge();
+ char *error_string;
+
+ ZERO_STRUCT(nt_key);
+
+ flags |= WBFLAG_PAM_LMKEY;
+ flags |= WBFLAG_PAM_NTKEY;
+
+ SMBNTencrypt(opt_password,chall.data,nt_response.data);
+
+ E_deshash(opt_password, lm_hash);
+
+ nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
+ opt_workstation,
+ &chall,
+ &nt_response,
+ NULL,
+ flags,
+ lm_key,
+ nt_key,
+ &error_string, NULL);
+
+ data_blob_free(&nt_response);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_printf("%s (0x%x)\n",
+ error_string,
+ NT_STATUS_V(nt_status));
+ SAFE_FREE(error_string);
+ return False;
+ }
+
+ if (memcmp(lm_hash, lm_key,
+ sizeof(lm_key)) != 0) {
+ DEBUG(1, ("LM Key does not match expectations!\n"));
+ DEBUG(1, ("lm_key:\n"));
+ dump_data(1, (const char *)lm_key, 8);
+ DEBUG(1, ("expected:\n"));
+ dump_data(1, (const char *)lm_hash, 8);
+ pass = False;
+ }
+ if (memcmp(lm_hash, nt_key, 8) != 0) {
+ DEBUG(1, ("Session Key (first 8 lm hash) does not match expectations!\n"));
+ DEBUG(1, ("nt_key:\n"));
+ dump_data(1, (const char *)nt_key, 16);
+ DEBUG(1, ("expected:\n"));
+ dump_data(1, (const char *)lm_hash, 8);
+ pass = False;
+ }
+ return pass;
+}
+
+/*
+ * Test the NTLM response only, but in the both the NT and LM fields.
+ */
+
+static BOOL test_ntlm_in_both(void)
+{
+ BOOL pass = True;
+ NTSTATUS nt_status;
+ uint32 flags = 0;
+ DATA_BLOB nt_response = data_blob(NULL, 24);
+ DATA_BLOB session_key = data_blob(NULL, 16);
+
+ char lm_key[8];
+ char lm_hash[16];
+ char nt_key[16];
+ char nt_hash[16];
+ DATA_BLOB chall = get_challenge();
+ char *error_string;
+
+ ZERO_STRUCT(lm_key);
+ ZERO_STRUCT(nt_key);
+
+ flags |= WBFLAG_PAM_LMKEY;
+ flags |= WBFLAG_PAM_NTKEY;
+
+ SMBNTencrypt(opt_password,chall.data,nt_response.data);
+ E_md4hash(opt_password, (unsigned char *)nt_hash);
+ SMBsesskeygen_ntv1((const unsigned char *)nt_hash, NULL, session_key.data);
+
+ E_deshash(opt_password, (unsigned char *)lm_hash);
+
+ nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
+ opt_workstation,
+ &chall,
+ &nt_response,
+ &nt_response,
+ flags,
+ (unsigned char *)lm_key,
+ (unsigned char *)nt_key,
+ &error_string, NULL);
+
+ data_blob_free(&nt_response);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_printf("%s (0x%x)\n",
+ error_string,
+ NT_STATUS_V(nt_status));
+ SAFE_FREE(error_string);
+ return False;
+ }
+
+ if (memcmp(lm_hash, lm_key,
+ sizeof(lm_key)) != 0) {
+ DEBUG(1, ("LM Key does not match expectations!\n"));
+ DEBUG(1, ("lm_key:\n"));
+ dump_data(1, lm_key, 8);
+ DEBUG(1, ("expected:\n"));
+ dump_data(1, lm_hash, 8);
+ pass = False;
+ }
+ if (memcmp(session_key.data, nt_key,
+ sizeof(nt_key)) != 0) {
+ DEBUG(1, ("NT Session Key does not match expectations!\n"));
+ DEBUG(1, ("nt_key:\n"));
+ dump_data(1, nt_key, 16);
+ DEBUG(1, ("expected:\n"));
+ dump_data(1, (const char *)session_key.data, session_key.length);
+ pass = False;
+ }
+
+
+ return pass;
+}
+
+/*
+ * Test the NTLMv2 and LMv2 responses
+ */
+
+static BOOL test_lmv2_ntlmv2_broken(enum ntlm_break break_which)
+{
+ BOOL pass = True;
+ NTSTATUS nt_status;
+ uint32 flags = 0;
+ DATA_BLOB ntlmv2_response = data_blob(NULL, 0);
+ DATA_BLOB lmv2_response = data_blob(NULL, 0);
+ DATA_BLOB nt_session_key = data_blob(NULL, 0);
+ DATA_BLOB names_blob = NTLMv2_generate_names_blob(get_winbind_netbios_name(), get_winbind_domain());
+
+ uchar nt_key[16];
+ DATA_BLOB chall = get_challenge();
+ char *error_string;
+
+ ZERO_STRUCT(nt_key);
+
+ flags |= WBFLAG_PAM_NTKEY;
+
+ if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall,
+ &names_blob,
+ &lmv2_response, &ntlmv2_response,
+ &nt_session_key)) {
+ data_blob_free(&names_blob);
+ return False;
+ }
+ data_blob_free(&names_blob);
+
+ switch (break_which) {
+ case BREAK_NONE:
+ break;
+ case BREAK_LM:
+ lmv2_response.data[0]++;
+ break;
+ case BREAK_NT:
+ ntlmv2_response.data[0]++;
+ break;
+ case NO_LM:
+ data_blob_free(&lmv2_response);
+ break;
+ case NO_NT:
+ data_blob_free(&ntlmv2_response);
+ break;
+ }
+
+ nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
+ opt_workstation,
+ &chall,
+ &lmv2_response,
+ &ntlmv2_response,
+ flags,
+ NULL,
+ nt_key,
+ &error_string, NULL);
+
+ data_blob_free(&lmv2_response);
+ data_blob_free(&ntlmv2_response);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_printf("%s (0x%x)\n",
+ error_string,
+ NT_STATUS_V(nt_status));
+ SAFE_FREE(error_string);
+ return break_which == BREAK_NT;
+ }
+
+ if (break_which != NO_NT && break_which != BREAK_NT && memcmp(nt_session_key.data, nt_key,
+ sizeof(nt_key)) != 0) {
+ DEBUG(1, ("NT Session Key does not match expectations!\n"));
+ DEBUG(1, ("nt_key:\n"));
+ dump_data(1, (const char *)nt_key, 16);
+ DEBUG(1, ("expected:\n"));
+ dump_data(1, (const char *)nt_session_key.data, nt_session_key.length);
+ pass = False;
+ }
+ return pass;
+}
+
+/*
+ * Test the NTLMv2 and LMv2 responses
+ */
+
+static BOOL test_lmv2_ntlmv2(void)
+{
+ return test_lmv2_ntlmv2_broken(BREAK_NONE);
+}
+
+/*
+ * Test the LMv2 response only
+ */
+
+static BOOL test_lmv2(void)
+{
+ return test_lmv2_ntlmv2_broken(NO_NT);
+}
+
+/*
+ * Test the NTLMv2 response only
+ */
+
+static BOOL test_ntlmv2(void)
+{
+ return test_lmv2_ntlmv2_broken(NO_LM);
+}
+
+static BOOL test_lm_ntlm(void)
+{
+ return test_lm_ntlm_broken(BREAK_NONE);
+}
+
+static BOOL test_ntlm_lm_broken(void)
+{
+ return test_lm_ntlm_broken(BREAK_LM);
+}
+
+static BOOL test_ntlm_ntlm_broken(void)
+{
+ return test_lm_ntlm_broken(BREAK_NT);
+}
+
+static BOOL test_ntlmv2_lmv2_broken(void)
+{
+ return test_lmv2_ntlmv2_broken(BREAK_LM);
+}
+
+static BOOL test_ntlmv2_ntlmv2_broken(void)
+{
+ return test_lmv2_ntlmv2_broken(BREAK_NT);
+}
+
+static BOOL test_plaintext(enum ntlm_break break_which)
+{
+ NTSTATUS nt_status;
+ uint32 flags = 0;
+ DATA_BLOB nt_response = data_blob(NULL, 0);
+ DATA_BLOB lm_response = data_blob(NULL, 0);
+ char *password;
+
+ uchar nt_key[16];
+ uchar lm_key[16];
+ static const uchar zeros[8];
+ DATA_BLOB chall = data_blob(zeros, sizeof(zeros));
+ char *error_string;
+
+ ZERO_STRUCT(nt_key);
+
+ flags |= WBFLAG_PAM_NTKEY;
+ flags |= WBFLAG_PAM_LMKEY;
+
+ if ((push_ucs2_allocate((smb_ucs2_t **)&nt_response.data, opt_password)) == -1) {
+ DEBUG(0, ("push_ucs2_allocate failed!\n"));
+ exit(1);
+ }
+
+ nt_response.length = strlen_w(((void *)nt_response.data))*sizeof(smb_ucs2_t);
+
+ password = strdup_upper(opt_password);
+
+ if ((convert_string_allocate(NULL, CH_UNIX,
+ CH_DOS, password,
+ strlen(password)+1,
+ (void**)&lm_response.data,True)) == -1) {
+ DEBUG(0, ("push_ascii_allocate failed!\n"));
+ exit(1);
+ }
+
+ SAFE_FREE(password);
+
+ lm_response.length = strlen(lm_response.data);
+
+ switch (break_which) {
+ case BREAK_NONE:
+ break;
+ case BREAK_LM:
+ lm_response.data[0]++;
+ break;
+ case BREAK_NT:
+ nt_response.data[0]++;
+ break;
+ case NO_LM:
+ SAFE_FREE(lm_response.data);
+ lm_response.length = 0;
+ break;
+ case NO_NT:
+ SAFE_FREE(nt_response.data);
+ nt_response.length = 0;
+ break;
+ }
+
+ nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
+ opt_workstation,
+ &chall,
+ &lm_response,
+ &nt_response,
+ flags,
+ lm_key,
+ nt_key,
+ &error_string, NULL);
+
+ SAFE_FREE(nt_response.data);
+ SAFE_FREE(lm_response.data);
+ data_blob_free(&chall);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_printf("%s (0x%x)\n",
+ error_string,
+ NT_STATUS_V(nt_status));
+ SAFE_FREE(error_string);
+ return break_which == BREAK_NT;
+ }
+
+ return break_which != BREAK_NT;
+}
+
+static BOOL test_plaintext_none_broken(void) {
+ return test_plaintext(BREAK_NONE);
+}
+
+static BOOL test_plaintext_lm_broken(void) {
+ return test_plaintext(BREAK_LM);
+}
+
+static BOOL test_plaintext_nt_broken(void) {
+ return test_plaintext(BREAK_NT);
+}
+
+static BOOL test_plaintext_nt_only(void) {
+ return test_plaintext(NO_LM);
+}
+
+static BOOL test_plaintext_lm_only(void) {
+ return test_plaintext(NO_NT);
+}
+
+/*
+ Tests:
+
+ - LM only
+ - NT and LM
+ - NT
+ - NT in LM field
+ - NT in both fields
+ - NTLMv2
+ - NTLMv2 and LMv2
+ - LMv2
+ - plaintext tests (in challenge-response feilds)
+
+ check we get the correct session key in each case
+ check what values we get for the LM session key
+
+*/
+
+static const struct ntlm_tests {
+ BOOL (*fn)(void);
+ const char *name;
+} test_table[] = {
+ {test_lm, "LM"},
+ {test_lm_ntlm, "LM and NTLM"},
+ {test_ntlm, "NTLM"},
+ {test_ntlm_in_lm, "NTLM in LM"},
+ {test_ntlm_in_both, "NTLM in both"},
+ {test_ntlmv2, "NTLMv2"},
+ {test_lmv2_ntlmv2, "NTLMv2 and LMv2"},
+ {test_lmv2, "LMv2"},
+ {test_ntlmv2_lmv2_broken, "NTLMv2 and LMv2, LMv2 broken"},
+ {test_ntlmv2_ntlmv2_broken, "NTLMv2 and LMv2, NTLMv2 broken"},
+ {test_ntlm_lm_broken, "NTLM and LM, LM broken"},
+ {test_ntlm_ntlm_broken, "NTLM and LM, NTLM broken"},
+ {test_plaintext_none_broken, "Plaintext"},
+ {test_plaintext_lm_broken, "Plaintext LM broken"},
+ {test_plaintext_nt_broken, "Plaintext NT broken"},
+ {test_plaintext_nt_only, "Plaintext NT only"},
+ {test_plaintext_lm_only, "Plaintext LM only"}
+};
+
+static BOOL diagnose_ntlm_auth(void)
+{
+ unsigned int i;
+ BOOL pass = True;
+
+ for (i=0; test_table[i].fn; i++) {
+ if (!test_table[i].fn()) {
+ DEBUG(1, ("Test %s failed!\n", test_table[i].name));
+ pass = False;
+ }
+ }
+
+ return pass;
+}
+
+/* Main program */
+
+enum {
+ OPT_USERNAME = 1000,
+ OPT_DOMAIN,
+ OPT_WORKSTATION,
+ OPT_CHALLENGE,
+ OPT_RESPONSE,
+ OPT_LM,
+ OPT_NT,
+ OPT_PASSWORD,
+ OPT_LM_KEY,
+ OPT_NT_KEY,
+ OPT_DIAGNOSTICS
+};
+
+ int main(int argc, const char **argv)
+{
+ int opt;
+ static const char *helper_protocol;
+ static int diagnostics;
+
+ static const char *hex_challenge;
+ static const char *hex_lm_response;
+ static const char *hex_nt_response;
+ char *challenge;
+ char *lm_response;
+ char *nt_response;
+ size_t challenge_len;
+ size_t lm_response_len;
+ size_t nt_response_len;
+
+ poptContext pc;
+
+ /* NOTE: DO NOT change this interface without considering the implications!
+ This is an external interface, which other programs will use to interact
+ with this helper.
+ */
+
+ /* We do not use single-letter command abbreviations, because they harm future
+ interface stability. */
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
+ { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
+ { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
+ { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
+ { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
+ { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
+ { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
+ { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
+ { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retreive LM session key"},
+ { "request-nt-key", 0, POPT_ARG_NONE, &request_nt_key, OPT_NT_KEY, "Retreive NT session key"},
+ { "diagnostics", 0, POPT_ARG_NONE, &diagnostics, OPT_DIAGNOSTICS, "Perform diagnostics on the authentictaion chain"},
+ POPT_COMMON_SAMBA
+ POPT_TABLEEND
+ };
+
+ /* Samba client initialisation */
+
+ dbf = x_stderr;
+
+ /* Samba client initialisation */
+
+ if (!lp_load(dyn_CONFIGFILE, True, False, False)) {
+ d_fprintf(stderr, "wbinfo: error opening config file %s. Error was %s\n",
+ dyn_CONFIGFILE, strerror(errno));
+ exit(1);
+ }
+
+ /* Parse options */
+
+ pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
+
+ /* Parse command line options */
+
+ if (argc == 1) {
+ poptPrintHelp(pc, stderr, 0);
+ return 1;
+ }
+
+ pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case OPT_CHALLENGE:
+ challenge = smb_xmalloc((strlen(hex_challenge))/2+1);
+ if ((challenge_len = strhex_to_str(challenge,
+ strlen(hex_challenge),
+ hex_challenge)) != 8) {
+ x_fprintf(x_stderr, "hex decode of %s failed (only got %lu bytes)!\n",
+ hex_challenge, (unsigned long)challenge_len);
+ exit(1);
+ }
+ opt_challenge = data_blob(challenge, challenge_len);
+ SAFE_FREE(challenge);
+ break;
+ case OPT_LM:
+ lm_response = smb_xmalloc((strlen(hex_lm_response))/2+1);
+ lm_response_len = strhex_to_str(lm_response,
+ strlen(hex_lm_response),
+ hex_lm_response);
+ if (lm_response_len != 24) {
+ x_fprintf(x_stderr, "hex decode of %s failed!\n", hex_lm_response);
+ exit(1);
+ }
+ opt_lm_response = data_blob(lm_response, lm_response_len);
+ SAFE_FREE(lm_response);
+ break;
+ case OPT_NT:
+ nt_response = smb_xmalloc((strlen(hex_nt_response)+2)/2+1);
+ nt_response_len = strhex_to_str(nt_response,
+ strlen(hex_nt_response),
+ hex_nt_response);
+ if (nt_response_len < 24) {
+ x_fprintf(x_stderr, "hex decode of %s failed!\n", hex_nt_response);
+ exit(1);
+ }
+ opt_nt_response = data_blob(nt_response, nt_response_len);
+ SAFE_FREE(nt_response);
+ break;
+ }
+ }
+
+ if (helper_protocol) {
+ int i;
+ for (i=0; i<NUM_HELPER_MODES; i++) {
+ if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
+ squid_stream(stdio_helper_protocols[i].mode, stdio_helper_protocols[i].fn);
+ exit(0);
+ }
+ }
+ x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
+
+ for (i=0; i<NUM_HELPER_MODES; i++) {
+ x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
+ }
+
+ exit(1);
+ }
+
+ if (!opt_username) {
+ x_fprintf(x_stderr, "username must be specified!\n\n");
+ poptPrintHelp(pc, stderr, 0);
+ exit(1);
+ }
+
+ if (opt_domain == NULL) {
+ opt_domain = get_winbind_domain();
+ }
+
+ if (opt_workstation == NULL) {
+ opt_workstation = "";
+ }
+
+ if (opt_challenge.length) {
+ if (!check_auth_crap()) {
+ exit(1);
+ }
+ exit(0);
+ }
+
+ if (!opt_password) {
+ opt_password = getpass("password: ");
+ }
+
+ if (diagnostics) {
+ if (!diagnose_ntlm_auth()) {
+ exit(1);
+ }
+ } else {
+ fstring user;
+
+ fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
+ if (!check_plaintext_auth(user, opt_password, True)) {
+ exit(1);
+ }
+ }
+
+ /* Exit code */
+
+ poptFreeContext(pc);
+ return 0;
+}
diff --git a/source/utils/pdbedit.c b/source/utils/pdbedit.c
new file mode 100644
index 00000000000..af96413c5ae
--- /dev/null
+++ b/source/utils/pdbedit.c
@@ -0,0 +1,1179 @@
+/*
+ Unix SMB/CIFS implementation.
+ passdb editing frontend
+
+ Copyright (C) Simo Sorce 2000
+ Copyright (C) Andrew Bartlett 2001
+ Copyright (C) Jelmer Vernooij 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#define BIT_BACKEND 0x00000004
+#define BIT_VERBOSE 0x00000008
+#define BIT_SPSTYLE 0x00000010
+#define BIT_RESERV_1 0x00000020
+#define BIT_RESERV_2 0x00000040
+#define BIT_RESERV_3 0x00000080
+#define BIT_FULLNAME 0x00000100
+#define BIT_HOMEDIR 0x00000200
+#define BIT_HDIRDRIVE 0x00000400
+#define BIT_LOGSCRIPT 0x00000800
+#define BIT_PROFILE 0x00001000
+#define BIT_MACHINE 0x00002000
+#define BIT_RESERV_4 0x00004000
+#define BIT_USER 0x00008000
+#define BIT_LIST 0x00010000
+#define BIT_MODIFY 0x00020000
+#define BIT_CREATE 0x00040000
+#define BIT_DELETE 0x00080000
+#define BIT_ACCPOLICY 0x00100000
+#define BIT_ACCPOLVAL 0x00200000
+#define BIT_ACCTCTRL 0x00400000
+#define BIT_RESERV_7 0x00800000
+#define BIT_IMPORT 0x01000000
+#define BIT_EXPORT 0x02000000
+#define BIT_FIX_INIT 0x04000000
+#define BIT_BADPWRESET 0x08000000
+#define BIT_TRUSTDOM 0x10000000
+#define BIT_TRUSTPW 0x20000000
+#define BIT_TRUSTSID 0x40000000
+#define BIT_TRUSTFLAGS 0x80000000
+
+#define MASK_ALWAYS_GOOD 0x0000001F
+#define MASK_USER_GOOD 0x00401F00
+
+/*********************************************************
+ Add all currently available users to another db
+ ********************************************************/
+
+static int export_database (struct pdb_context *in, struct pdb_context *out) {
+ SAM_ACCOUNT *user = NULL;
+
+ if (NT_STATUS_IS_ERR(in->pdb_setsampwent(in, 0))) {
+ fprintf(stderr, "Can't sampwent!\n");
+ return 1;
+ }
+
+ if (!NT_STATUS_IS_OK(pdb_init_sam(&user))) {
+ fprintf(stderr, "Can't initialize new SAM_ACCOUNT!\n");
+ return 1;
+ }
+
+ while (NT_STATUS_IS_OK(in->pdb_getsampwent(in, user))) {
+ out->pdb_add_sam_account(out, user);
+ if (!NT_STATUS_IS_OK(pdb_reset_sam(user))){
+ fprintf(stderr, "Can't reset SAM_ACCOUNT!\n");
+ return 1;
+ }
+ }
+
+ in->pdb_endsampwent(in);
+
+ return 0;
+}
+
+/*********************************************************
+ Add all currently available group mappings to another db
+ ********************************************************/
+
+static int export_groups (struct pdb_context *in, struct pdb_context *out) {
+ GROUP_MAP *maps = NULL;
+ int i, entries = 0;
+
+ if (NT_STATUS_IS_ERR(in->pdb_enum_group_mapping(in, SID_NAME_UNKNOWN,
+ &maps, &entries,
+ False))) {
+ fprintf(stderr, "Can't get group mappings!\n");
+ return 1;
+ }
+
+ for (i=0; i<entries; i++) {
+ out->pdb_add_group_mapping_entry(out, &(maps[i]));
+ }
+
+ SAFE_FREE(maps);
+
+ return 0;
+}
+
+/*********************************************************
+ Print info from sam structure
+**********************************************************/
+
+static int print_sam_info (SAM_ACCOUNT *sam_pwent, BOOL verbosity, BOOL smbpwdstyle)
+{
+ uid_t uid;
+ time_t tmp;
+
+ /* TODO: chaeck if entry is a user or a workstation */
+ if (!sam_pwent) return -1;
+
+ if (verbosity) {
+ printf ("Unix username: %s\n", pdb_get_username(sam_pwent));
+ printf ("NT username: %s\n", pdb_get_nt_username(sam_pwent));
+ printf ("Account Flags: %s\n", pdb_encode_acct_ctrl(pdb_get_acct_ctrl(sam_pwent), NEW_PW_FORMAT_SPACE_PADDED_LEN));
+ printf ("User SID: %s\n",
+ sid_string_static(pdb_get_user_sid(sam_pwent)));
+ printf ("Primary Group SID: %s\n",
+ sid_string_static(pdb_get_group_sid(sam_pwent)));
+ printf ("Full Name: %s\n", pdb_get_fullname(sam_pwent));
+ printf ("Home Directory: %s\n", pdb_get_homedir(sam_pwent));
+ printf ("HomeDir Drive: %s\n", pdb_get_dir_drive(sam_pwent));
+ printf ("Logon Script: %s\n", pdb_get_logon_script(sam_pwent));
+ printf ("Profile Path: %s\n", pdb_get_profile_path(sam_pwent));
+ printf ("Domain: %s\n", pdb_get_domain(sam_pwent));
+ printf ("Account desc: %s\n", pdb_get_acct_desc(sam_pwent));
+ printf ("Workstations: %s\n", pdb_get_workstations(sam_pwent));
+ printf ("Munged dial: %s\n", pdb_get_munged_dial(sam_pwent));
+
+ tmp = pdb_get_logon_time(sam_pwent);
+ printf ("Logon time: %s\n", tmp ? http_timestring(tmp) : "0");
+
+ tmp = pdb_get_logoff_time(sam_pwent);
+ printf ("Logoff time: %s\n", tmp ? http_timestring(tmp) : "0");
+
+ tmp = pdb_get_kickoff_time(sam_pwent);
+ printf ("Kickoff time: %s\n", tmp ? http_timestring(tmp) : "0");
+
+ tmp = pdb_get_pass_last_set_time(sam_pwent);
+ printf ("Password last set: %s\n", tmp ? http_timestring(tmp) : "0");
+
+ tmp = pdb_get_pass_can_change_time(sam_pwent);
+ printf ("Password can change: %s\n", tmp ? http_timestring(tmp) : "0");
+
+ tmp = pdb_get_pass_must_change_time(sam_pwent);
+ printf ("Password must change: %s\n", tmp ? http_timestring(tmp) : "0");
+
+ tmp = pdb_get_bad_password_time(sam_pwent);
+ printf ("Last bad password : %s\n", tmp ? http_timestring(tmp) : "0");
+ printf ("Bad password count : %d\n",
+ pdb_get_bad_password_count(sam_pwent));
+
+ } else if (smbpwdstyle) {
+ char lm_passwd[33];
+ char nt_passwd[33];
+
+ uid = nametouid(pdb_get_username(sam_pwent));
+ pdb_sethexpwd(lm_passwd, pdb_get_lanman_passwd(sam_pwent), pdb_get_acct_ctrl(sam_pwent));
+ pdb_sethexpwd(nt_passwd, pdb_get_nt_passwd(sam_pwent), pdb_get_acct_ctrl(sam_pwent));
+
+ printf("%s:%lu:%s:%s:%s:LCT-%08X:\n",
+ pdb_get_username(sam_pwent),
+ (unsigned long)uid,
+ lm_passwd,
+ nt_passwd,
+ pdb_encode_acct_ctrl(pdb_get_acct_ctrl(sam_pwent),NEW_PW_FORMAT_SPACE_PADDED_LEN),
+ (uint32)pdb_get_pass_last_set_time(sam_pwent));
+ } else {
+ uid = nametouid(pdb_get_username(sam_pwent));
+ printf ("%s:%lu:%s\n", pdb_get_username(sam_pwent), (unsigned long)uid,
+ pdb_get_fullname(sam_pwent));
+ }
+
+ return 0;
+}
+
+/*********************************************************
+ Get an Print User Info
+**********************************************************/
+
+static int print_user_info (struct pdb_context *in, const char *username, BOOL verbosity, BOOL smbpwdstyle)
+{
+ SAM_ACCOUNT *sam_pwent=NULL;
+ BOOL ret;
+ BOOL updated_autolock = False, updated_badpw = False;
+
+ if (!NT_STATUS_IS_OK(pdb_init_sam (&sam_pwent))) {
+ return -1;
+ }
+
+ ret = NT_STATUS_IS_OK(in->pdb_getsampwnam (in, sam_pwent, username));
+
+ if (ret==False) {
+ fprintf (stderr, "Username not found!\n");
+ pdb_free_sam(&sam_pwent);
+ return -1;
+ }
+
+ if (!pdb_update_autolock_flag(sam_pwent, &updated_autolock))
+ DEBUG(2,("pdb_update_autolock_flag failed.\n"));
+
+ if (!pdb_update_bad_password_count(sam_pwent, &updated_badpw))
+ DEBUG(2,("pdb_update_bad_password_count failed.\n"));
+
+ if (updated_autolock || updated_badpw) {
+ become_root();
+ if(!pdb_update_sam_account(sam_pwent))
+ DEBUG(1, ("Failed to modify entry.\n"));
+ unbecome_root();
+ }
+
+ ret=print_sam_info (sam_pwent, verbosity, smbpwdstyle);
+ pdb_free_sam(&sam_pwent);
+
+ return ret;
+}
+
+
+/**
+ * Trust password flag name to flag conversion
+ *
+ * @param flag_name SAM_TRUST_PASSWD structure flag name
+ * @return flag value
+ **/
+
+static int trustpw_flag(const char* flag_name)
+{
+ const int flag_num = 5;
+ typedef struct { const char *name; int val; } flag_conv;
+ flag_conv flags[] = {{ "PASS_MACHINE_TRUST_NT", PASS_MACHINE_TRUST_NT },
+ { "PASS_SERVER_TRUST_NT", PASS_SERVER_TRUST_NT },
+ { "PASS_DOMAIN_TRUST_NT", PASS_DOMAIN_TRUST_NT },
+ { "PASS_MACHINE_TRUST_ADS",PASS_MACHINE_TRUST_ADS },
+ { "PASS_DOMAIN_TRUST_ADS", PASS_DOMAIN_TRUST_ADS }};
+ int i;
+
+ for (i = 0; i < flag_num; i++) {
+ if (!StrCaseCmp(flags[i].name, flag_name)) {
+ return flags[i].val;
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * Trust password flag to flag name conversion
+ *
+ * @param val SAM_TRUST_PASSWD structure flag
+ * @return passed flag name
+ **/
+
+static char* trustpw_flag_name(const int val)
+{
+ const int flag_num = 5;
+ typedef struct { const char *name; int val; } flag_conv;
+ flag_conv flags[] = {{ "PASS_MACHINE_TRUST_NT", PASS_MACHINE_TRUST_NT },
+ { "PASS_SERVER_TRUST_NT", PASS_SERVER_TRUST_NT },
+ { "PASS_DOMAIN_TRUST_NT", PASS_DOMAIN_TRUST_NT },
+ { "PASS_MACHINE_TRUST_ADS",PASS_MACHINE_TRUST_ADS },
+ { "PASS_DOMAIN_TRUST_ADS", PASS_DOMAIN_TRUST_ADS }};
+ int i;
+
+ for (i = 0; i < flag_num; i++) {
+ if (flags[i].val == val) {
+ return strdup(flags[i].name);
+ }
+ }
+
+ return strdup("unknown flag");
+}
+
+
+/**
+ * Print trust password structure information
+ *
+ * @param mem_ctx memory context (for unicode name conversion)
+ * @param trust SAM_TRUST_PASSWD structure
+ * @param verbose verbose mode on/off
+ * @return 0 on success, otherwise failure
+ **/
+
+static int print_trustpw_info(TALLOC_CTX *mem_ctx, SAM_TRUST_PASSWD *trust, BOOL verbose)
+{
+ char *dom_name;
+ if (!mem_ctx || !trust) return -1;
+
+ /* convert unicode domain name to char* */
+ if (!pull_ucs2_talloc(mem_ctx, &dom_name, trust->private.uni_name)) return -1;
+ dom_name[trust->private.uni_name_len] = 0;
+
+ /* different output depending on level of verbosity */
+ if (verbose) {
+ printf("Domain name: %s\n", dom_name);
+ printf("Domain SID: %s\n", sid_string_static(&trust->private.domain_sid));
+ printf("Trust password %s\n", trust->private.pass);
+ printf("Trust type: %s\n", trustpw_flag_name(trust->private.flags));
+ printf("Last modified %s\n", trust->private.mod_time ? http_timestring(trust->private.mod_time) : "0");
+
+ } else {
+ printf("%s:%s\n", dom_name, sid_string_static(&trust->private.domain_sid));
+ }
+
+ return 0;
+}
+
+
+/**
+ * Print trust password information by given name
+ *
+ * @param in initialised pdb_context
+ * @param name domain name of the trust password
+ * @param verbose verbose mode on/off
+ * @param smbpwdstyle smbpassword-style output (ignored here)
+ * @return 0 on success, otherwise failure
+ **/
+
+static int print_trust_info(struct pdb_context *in, const char *name, BOOL verbose, BOOL smbpwdstyle)
+{
+ SAM_TRUST_PASSWD trust;
+ TALLOC_CTX *mem_ctx = NULL;
+
+ mem_ctx = talloc_init("pdbedit: trust passwords listing");
+
+ if (NT_STATUS_IS_OK(in->pdb_gettrustpwnam(in, &trust, name))) {
+ return print_trustpw_info(mem_ctx, &trust, verbose);
+ }
+
+ return -1;
+}
+
+/*********************************************************
+ List Users
+**********************************************************/
+static int print_users_list (struct pdb_context *in, BOOL verbosity, BOOL smbpwdstyle)
+{
+ SAM_ACCOUNT *sam_pwent=NULL;
+ BOOL check, ret;
+
+ check = NT_STATUS_IS_OK(in->pdb_setsampwent(in, False));
+ if (!check) {
+ return 1;
+ }
+
+ check = True;
+ if (!(NT_STATUS_IS_OK(pdb_init_sam(&sam_pwent)))) return 1;
+
+ while (check && (ret = NT_STATUS_IS_OK(in->pdb_getsampwent (in, sam_pwent)))) {
+ if (verbosity)
+ printf ("---------------\n");
+ print_sam_info (sam_pwent, verbosity, smbpwdstyle);
+ pdb_free_sam(&sam_pwent);
+ check = NT_STATUS_IS_OK(pdb_init_sam(&sam_pwent));
+ }
+ if (check) pdb_free_sam(&sam_pwent);
+
+ in->pdb_endsampwent(in);
+ return 0;
+}
+
+
+/**
+ * List trust passwords
+ *
+ * @param in initialised pdb context
+ * @param verbose turn on/off verbose mode
+ * @param smbpwdstyle ignored here (there was no trust passwords in smbpasswd file)
+ * @return 0 on success, otherwise failure
+ **/
+
+static int print_trustpw_list(struct pdb_context *in, BOOL verbose, BOOL smbpwdstyle)
+{
+ SAM_TRUST_PASSWD trust;
+ TALLOC_CTX *mem_ctx = NULL;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+
+ /* start enumeration and initialise memory context */
+ status = in->pdb_settrustpwent(in);
+ if (NT_STATUS_IS_ERR(status)) return -1;
+ mem_ctx = talloc_init("pdbedit: trust passwords listing");
+
+ /* small separation to make it clear these are not regular accounts */
+ if (!verbose) printf("---\n");
+
+ do {
+ /* fetch next trust password */
+ status = in->pdb_gettrustpwent(in, &trust);
+
+ if (trust.private.uni_name_len) {
+ /* print trust password info */
+ if (verbose) printf ("---------------\n");
+ print_trustpw_info(mem_ctx, &trust, verbose);
+ }
+
+ } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) || NT_STATUS_EQUAL(status, NT_STATUS_OK));
+
+ talloc_destroy(mem_ctx);
+ return 0;
+}
+
+
+/*********************************************************
+ Fix a list of Users for uninitialised passwords
+**********************************************************/
+static int fix_users_list (struct pdb_context *in)
+{
+ SAM_ACCOUNT *sam_pwent=NULL;
+ BOOL check, ret;
+
+ check = NT_STATUS_IS_OK(in->pdb_setsampwent(in, False));
+ if (!check) {
+ return 1;
+ }
+
+ check = True;
+ if (!(NT_STATUS_IS_OK(pdb_init_sam(&sam_pwent)))) return 1;
+
+ while (check && (ret = NT_STATUS_IS_OK(in->pdb_getsampwent (in, sam_pwent)))) {
+ printf("Updating record for user %s\n", pdb_get_username(sam_pwent));
+
+ if (!pdb_update_sam_account(sam_pwent)) {
+ printf("Update of user %s failed!\n", pdb_get_username(sam_pwent));
+ }
+ pdb_free_sam(&sam_pwent);
+ check = NT_STATUS_IS_OK(pdb_init_sam(&sam_pwent));
+ if (!check) {
+ fprintf(stderr, "Failed to initialise new SAM_ACCOUNT structure (out of memory?)\n");
+ }
+
+ }
+ if (check) pdb_free_sam(&sam_pwent);
+
+ in->pdb_endsampwent(in);
+ return 0;
+}
+
+/*********************************************************
+ Set User Info
+**********************************************************/
+
+static int set_user_info (struct pdb_context *in, const char *username,
+ const char *fullname, const char *homedir,
+ const char *drive, const char *script,
+ const char *profile, const char *account_control,
+ const char *user_sid, const char *group_sid,
+ const BOOL badpw)
+{
+ SAM_ACCOUNT *sam_pwent=NULL;
+ BOOL ret;
+
+ pdb_init_sam(&sam_pwent);
+
+ ret = NT_STATUS_IS_OK(in->pdb_getsampwnam (in, sam_pwent, username));
+ if (ret==False) {
+ fprintf (stderr, "Username not found!\n");
+ pdb_free_sam(&sam_pwent);
+ return -1;
+ }
+
+ if (fullname)
+ pdb_set_fullname(sam_pwent, fullname, PDB_CHANGED);
+ if (homedir)
+ pdb_set_homedir(sam_pwent, homedir, PDB_CHANGED);
+ if (drive)
+ pdb_set_dir_drive(sam_pwent,drive, PDB_CHANGED);
+ if (script)
+ pdb_set_logon_script(sam_pwent, script, PDB_CHANGED);
+ if (profile)
+ pdb_set_profile_path (sam_pwent, profile, PDB_CHANGED);
+
+ if (account_control) {
+ uint16 not_settable = ~(ACB_DISABLED|ACB_HOMDIRREQ|ACB_PWNOTREQ|
+ ACB_PWNOEXP|ACB_AUTOLOCK);
+
+ uint16 newflag = pdb_decode_acct_ctrl(account_control);
+
+ if (newflag & not_settable) {
+ fprintf(stderr, "Can only set [NDHLX] flags\n");
+ pdb_free_sam(&sam_pwent);
+ return -1;
+ }
+
+ pdb_set_acct_ctrl(sam_pwent,
+ (pdb_get_acct_ctrl(sam_pwent) & not_settable) | newflag,
+ PDB_CHANGED);
+ }
+ if (user_sid) {
+ DOM_SID u_sid;
+ if (!string_to_sid(&u_sid, user_sid)) {
+ /* not a complete sid, may be a RID, try building a SID */
+ int u_rid;
+
+ if (sscanf(user_sid, "%d", &u_rid) != 1) {
+ fprintf(stderr, "Error passed string is not a complete user SID or RID!\n");
+ return -1;
+ }
+ sid_copy(&u_sid, get_global_sam_sid());
+ sid_append_rid(&u_sid, u_rid);
+ }
+ pdb_set_user_sid (sam_pwent, &u_sid, PDB_CHANGED);
+ }
+ if (group_sid) {
+ DOM_SID g_sid;
+ if (!string_to_sid(&g_sid, group_sid)) {
+ /* not a complete sid, may be a RID, try building a SID */
+ int g_rid;
+
+ if (sscanf(group_sid, "%d", &g_rid) != 1) {
+ fprintf(stderr, "Error passed string is not a complete group SID or RID!\n");
+ return -1;
+ }
+ sid_copy(&g_sid, get_global_sam_sid());
+ sid_append_rid(&g_sid, g_rid);
+ }
+ pdb_set_group_sid (sam_pwent, &g_sid, PDB_CHANGED);
+ }
+
+ if (badpw) {
+ pdb_set_bad_password_count(sam_pwent, 0, PDB_CHANGED);
+ pdb_set_bad_password_time(sam_pwent, 0, PDB_CHANGED);
+ }
+
+ if (NT_STATUS_IS_OK(in->pdb_update_sam_account (in, sam_pwent)))
+ print_user_info (in, username, True, False);
+ else {
+ fprintf (stderr, "Unable to modify entry!\n");
+ pdb_free_sam(&sam_pwent);
+ return -1;
+ }
+ pdb_free_sam(&sam_pwent);
+ return 0;
+}
+
+/*********************************************************
+ Add New User
+**********************************************************/
+static int new_user (struct pdb_context *in, const char *username,
+ const char *fullname, const char *homedir,
+ const char *drive, const char *script,
+ const char *profile, char *user_sid, char *group_sid)
+{
+ SAM_ACCOUNT *sam_pwent=NULL;
+ NTSTATUS nt_status;
+ char *password1, *password2, *staticpass;
+
+ get_global_sam_sid();
+
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam_new(&sam_pwent, username, 0))) {
+ DEBUG(0, ("could not create account to add new user %s\n", username));
+ return -1;
+ }
+
+ staticpass = getpass("new password:");
+ password1 = strdup(staticpass);
+ memset(staticpass, 0, strlen(staticpass));
+ staticpass = getpass("retype new password:");
+ password2 = strdup(staticpass);
+ memset(staticpass, 0, strlen(staticpass));
+ if (strcmp (password1, password2)) {
+ fprintf (stderr, "Passwords does not match!\n");
+ memset(password1, 0, strlen(password1));
+ SAFE_FREE(password1);
+ memset(password2, 0, strlen(password2));
+ SAFE_FREE(password2);
+ pdb_free_sam (&sam_pwent);
+ return -1;
+ }
+
+ pdb_set_plaintext_passwd(sam_pwent, password1);
+ memset(password1, 0, strlen(password1));
+ SAFE_FREE(password1);
+ memset(password2, 0, strlen(password2));
+ SAFE_FREE(password2);
+
+ if (fullname)
+ pdb_set_fullname(sam_pwent, fullname, PDB_CHANGED);
+ if (homedir)
+ pdb_set_homedir (sam_pwent, homedir, PDB_CHANGED);
+ if (drive)
+ pdb_set_dir_drive (sam_pwent, drive, PDB_CHANGED);
+ if (script)
+ pdb_set_logon_script(sam_pwent, script, PDB_CHANGED);
+ if (profile)
+ pdb_set_profile_path (sam_pwent, profile, PDB_CHANGED);
+ if (user_sid) {
+ DOM_SID u_sid;
+ if (!string_to_sid(&u_sid, user_sid)) {
+ /* not a complete sid, may be a RID, try building a SID */
+ int u_rid;
+
+ if (sscanf(user_sid, "%d", &u_rid) != 1) {
+ fprintf(stderr, "Error passed string is not a complete user SID or RID!\n");
+ return -1;
+ }
+ sid_copy(&u_sid, get_global_sam_sid());
+ sid_append_rid(&u_sid, u_rid);
+ }
+ pdb_set_user_sid (sam_pwent, &u_sid, PDB_CHANGED);
+ }
+ if (group_sid) {
+ DOM_SID g_sid;
+ if (!string_to_sid(&g_sid, group_sid)) {
+ /* not a complete sid, may be a RID, try building a SID */
+ int g_rid;
+
+ if (sscanf(group_sid, "%d", &g_rid) != 1) {
+ fprintf(stderr, "Error passed string is not a complete group SID or RID!\n");
+ return -1;
+ }
+ sid_copy(&g_sid, get_global_sam_sid());
+ sid_append_rid(&g_sid, g_rid);
+ }
+ pdb_set_group_sid (sam_pwent, &g_sid, PDB_CHANGED);
+ }
+
+ pdb_set_acct_ctrl (sam_pwent, ACB_NORMAL, PDB_CHANGED);
+
+ if (NT_STATUS_IS_OK(in->pdb_add_sam_account (in, sam_pwent))) {
+ print_user_info (in, username, True, False);
+ } else {
+ fprintf (stderr, "Unable to add user! (does it already exist?)\n");
+ pdb_free_sam (&sam_pwent);
+ return -1;
+ }
+ pdb_free_sam (&sam_pwent);
+ return 0;
+}
+
+/*********************************************************
+ Add New Machine
+**********************************************************/
+
+static int new_machine (struct pdb_context *in, const char *machine_in)
+{
+ SAM_ACCOUNT *sam_pwent=NULL;
+ fstring machinename;
+ fstring machineaccount;
+ struct passwd *pwd = NULL;
+
+ get_global_sam_sid();
+
+ fstrcpy(machinename, machine_in);
+ machinename[15]= '\0';
+
+ if (machinename[strlen (machinename) -1] == '$')
+ machinename[strlen (machinename) -1] = '\0';
+
+ strlower_m(machinename);
+
+ fstrcpy(machineaccount, machinename);
+ fstrcat(machineaccount, "$");
+
+ if ((pwd = getpwnam_alloc(machineaccount))) {
+ if (!NT_STATUS_IS_OK(pdb_init_sam_pw( &sam_pwent, pwd))) {
+ fprintf(stderr, "Could not init sam from pw\n");
+ passwd_free(&pwd);
+ return -1;
+ }
+ passwd_free(&pwd);
+ } else {
+ if (!NT_STATUS_IS_OK(pdb_init_sam (&sam_pwent))) {
+ fprintf(stderr, "Could not init sam from pw\n");
+ return -1;
+ }
+ }
+
+ pdb_set_plaintext_passwd (sam_pwent, machinename);
+
+ pdb_set_username (sam_pwent, machineaccount, PDB_CHANGED);
+
+ pdb_set_acct_ctrl (sam_pwent, ACB_WSTRUST, PDB_CHANGED);
+
+ pdb_set_group_sid_from_rid(sam_pwent, DOMAIN_GROUP_RID_COMPUTERS, PDB_CHANGED);
+
+ if (NT_STATUS_IS_OK(in->pdb_add_sam_account (in, sam_pwent))) {
+ print_user_info (in, machineaccount, True, False);
+ } else {
+ fprintf (stderr, "Unable to add machine! (does it already exist?)\n");
+ pdb_free_sam (&sam_pwent);
+ return -1;
+ }
+ pdb_free_sam (&sam_pwent);
+ return 0;
+}
+
+
+/**
+ * Add new trusting domain account
+ *
+ * @param in initialised pdb_context
+ * @param dom_name trusted domain name given in command line
+ *
+ * @return 0 on success, -1 otherwise
+ **/
+
+static int new_trustdom(struct pdb_context *in, const char *dom_name)
+{
+ /* TODO */
+ return -1;
+}
+
+
+/**
+ * Add new trust relationship password
+ *
+ * @param in initialised pdb_context
+ * @param dom_name trusting domain name given in command line
+ * @param dom_sid domain sid given in command line
+ * @param flag trust password type flag given in command line
+ *
+ * @return 0 on success, -1 otherwise
+ **/
+
+static int new_trustpw(struct pdb_context *in, const char *dom_name,
+ const char *dom_sid, const char* flag)
+{
+ TALLOC_CTX *mem_ctx = NULL;
+ SAM_TRUST_PASSWD trust;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ POLICY_HND connect_hnd;
+ DOM_SID *domain_sid = NULL;
+ smb_ucs2_t *uni_name = NULL;
+ char *givenpass, *domain_name = NULL;
+ struct in_addr srv_ip;
+ fstring srv_name, myname;
+ struct cli_state *cli;
+ time_t lct;
+
+ if (!dom_name) return -1;
+
+ mem_ctx = talloc_init("pdbedit: adding new trust password");
+
+ /* unicode name */
+ trust.private.uni_name_len = strnlen(dom_name, 32);
+ push_ucs2_talloc(mem_ctx, &uni_name, dom_name);
+ strncpy_w(trust.private.uni_name, uni_name, 32);
+
+ /* flags */
+ trust.private.flags = trustpw_flag(flag);
+
+ /* trusting SID */
+ if (!dom_sid) {
+ /* if sid is not specified in command line, do our best
+ to establish it */
+
+ /* find domain PDC */
+ if (!get_pdc_ip(dom_name, &srv_ip))
+ return -1;
+ if (is_zero_ip(srv_ip))
+ return -1;
+ if (!name_status_find(dom_name, 0x1b, 0x20, srv_ip, srv_name))
+ return -1;
+
+ get_myname(myname);
+
+ /* Connect the domain pdc... */
+ nt_status = cli_full_connection(&cli, myname, srv_name, &srv_ip, 139,
+ "IPC$", "IPC", "", "", "", 0, Undefined, NULL);
+ if (NT_STATUS_IS_ERR(nt_status))
+ return -1;
+ if (!cli_nt_session_open(cli, PI_LSARPC))
+ return -1;
+
+ /* ...and query the domain sid */
+ nt_status = cli_lsa_open_policy2(cli, mem_ctx, True, SEC_RIGHTS_QUERY_VALUE,
+ &connect_hnd);
+ if (NT_STATUS_IS_ERR(nt_status)) return -1;
+
+ nt_status = cli_lsa_query_info_policy(cli, mem_ctx, &connect_hnd,
+ 5, &domain_name, &domain_sid);
+ if (NT_STATUS_IS_ERR(nt_status)) return -1;
+
+ nt_status = cli_lsa_close(cli, mem_ctx, &connect_hnd);
+ if (NT_STATUS_IS_ERR(nt_status)) return -1;
+
+ cli_nt_session_close(cli);
+ cli_shutdown(cli);
+
+ /* copying sid to trust password structure */
+ sid_copy(&trust.private.domain_sid, domain_sid);
+
+ } else {
+ if (!string_to_sid(&trust.private.domain_sid, dom_sid)) {
+ printf("Error: wrong SID specified !\n");
+ return -1;
+ }
+ }
+
+ /* password */
+ givenpass = getpass("password:");
+ memset(trust.private.pass, '\0', FSTRING_LEN);
+ strncpy(trust.private.pass, givenpass, FSTRING_LEN);
+
+ /* last change time */
+ lct = time(NULL);
+ trust.private.mod_time = lct;
+
+ /* store trust password in passdb */
+ nt_status = in->pdb_add_trust_passwd(in, &trust);
+
+ talloc_destroy(mem_ctx);
+ if (NT_STATUS_IS_OK(nt_status))
+ return 0;
+
+ return -1;
+}
+
+
+/*********************************************************
+ Delete user entry
+**********************************************************/
+
+static int delete_user_entry (struct pdb_context *in, const char *username)
+{
+ SAM_ACCOUNT *samaccount = NULL;
+
+ if (!NT_STATUS_IS_OK(pdb_init_sam (&samaccount))) {
+ return -1;
+ }
+
+ if (!NT_STATUS_IS_OK(in->pdb_getsampwnam(in, samaccount, username))) {
+ fprintf (stderr, "user %s does not exist in the passdb\n", username);
+ return -1;
+ }
+
+ if (!NT_STATUS_IS_OK(in->pdb_delete_sam_account (in, samaccount))) {
+ fprintf (stderr, "Unable to delete user %s\n", username);
+ return -1;
+ }
+ return 0;
+}
+
+/*********************************************************
+ Delete machine entry
+**********************************************************/
+
+static int delete_machine_entry (struct pdb_context *in, const char *machinename)
+{
+ fstring name;
+ SAM_ACCOUNT *samaccount = NULL;
+
+ fstrcpy(name, machinename);
+ name[15] = '\0';
+ if (name[strlen(name)-1] != '$')
+ fstrcat (name, "$");
+
+ if (!NT_STATUS_IS_OK(pdb_init_sam (&samaccount))) {
+ return -1;
+ }
+
+ if (!NT_STATUS_IS_OK(in->pdb_getsampwnam(in, samaccount, name))) {
+ fprintf (stderr, "machine %s does not exist in the passdb\n", name);
+ return -1;
+ }
+
+ if (!NT_STATUS_IS_OK(in->pdb_delete_sam_account (in, samaccount))) {
+ fprintf (stderr, "Unable to delete machine %s\n", name);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*********************************************************
+ Start here.
+**********************************************************/
+
+int main (int argc, char **argv)
+{
+ static BOOL list_users = False;
+ static BOOL verbose = False;
+ static BOOL spstyle = False;
+ static BOOL machine = False;
+ static BOOL trustdom = False;
+ static BOOL add_user = False;
+ static BOOL delete_user = False;
+ static BOOL modify_user = False;
+ uint32 setparms, checkparms;
+ int opt;
+ static char *full_name = NULL;
+ static const char *user_name = NULL;
+ static char *home_dir = NULL;
+ static char *home_drive = NULL;
+ static char *backend = NULL;
+ static char *backend_in = NULL;
+ static char *backend_out = NULL;
+ static BOOL transfer_groups = False;
+ static BOOL force_initialised_password = False;
+ static char *logon_script = NULL;
+ static char *profile_path = NULL;
+ static char *account_control = NULL;
+ static char *account_policy = NULL;
+ static char *user_sid = NULL;
+ static char *group_sid = NULL;
+ static long int account_policy_value = 0;
+ BOOL account_policy_value_set = False;
+ static BOOL badpw_reset = False;
+ /* trust password parameters */
+ static char *trustpw = NULL;
+ static char *trustsid = NULL;
+ static char *trustflags = NULL;
+
+ struct pdb_context *bin;
+ struct pdb_context *bout;
+ struct pdb_context *bdef;
+ poptContext pc;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"list", 'L', POPT_ARG_NONE, &list_users, 0, "list all users", NULL},
+ {"verbose", 'v', POPT_ARG_NONE, &verbose, 0, "be verbose", NULL },
+ {"smbpasswd-style", 'w',POPT_ARG_NONE, &spstyle, 0, "give output in smbpasswd style", NULL},
+ {"user", 'u', POPT_ARG_STRING, &user_name, 0, "use username", "USER" },
+ {"fullname", 'f', POPT_ARG_STRING, &full_name, 0, "set full name", NULL},
+ {"homedir", 'h', POPT_ARG_STRING, &home_dir, 0, "set home directory", NULL},
+ {"drive", 'D', POPT_ARG_STRING, &home_drive, 0, "set home drive", NULL},
+ {"script", 'S', POPT_ARG_STRING, &logon_script, 0, "set logon script", NULL},
+ {"profile", 'p', POPT_ARG_STRING, &profile_path, 0, "set profile path", NULL},
+ {"user SID", 'U', POPT_ARG_STRING, &user_sid, 0, "set user SID or RID", NULL},
+ {"group SID", 'G', POPT_ARG_STRING, &group_sid, 0, "set group SID or RID", NULL},
+ {"create", 'a', POPT_ARG_NONE, &add_user, 0, "create user", NULL},
+ {"modify", 'r', POPT_ARG_NONE, &modify_user, 0, "modify user", NULL},
+ {"delete", 'x', POPT_ARG_NONE, &delete_user, 0, "delete user", NULL},
+ {"machine", 'm', POPT_ARG_NONE, &machine, 0, "account is a machine account", NULL},
+ {"trustdom", 'I', POPT_ARG_NONE, &trustdom, 0, "account is a domain trust account", NULL},
+ {"trustpw", 'N', POPT_ARG_STRING, &trustpw, 0, "trust password's domain name", NULL},
+ {"trustsid", 'T', POPT_ARG_STRING, &trustsid, 0, "trust password's domain sid", NULL},
+ {"trustflags", 'F', POPT_ARG_STRING, &trustflags, 0, "trust password flags", NULL},
+ {"backend", 'b', POPT_ARG_STRING, &backend, 0, "use different passdb backend as default backend", NULL},
+ {"import", 'i', POPT_ARG_STRING, &backend_in, 0, "import user accounts from this backend", NULL},
+ {"export", 'e', POPT_ARG_STRING, &backend_out, 0, "export user accounts to this backend", NULL},
+ {"group", 'g', POPT_ARG_NONE, &transfer_groups, 0, "use -i and -e for groups", NULL},
+ {"account-policy", 'P', POPT_ARG_STRING, &account_policy, 0,"value of an account policy (like maximum password age)",NULL},
+ {"value", 'C', POPT_ARG_LONG, &account_policy_value, 'C',"set the account policy to this value", NULL},
+ {"account-control", 'c', POPT_ARG_STRING, &account_control, 0, "Values of account control", NULL},
+ {"force-initialized-passwords", 0, POPT_ARG_NONE, &force_initialised_password, 0, "Force initialization of corrupt password strings in a passdb backend", NULL},
+ {"bad-password-count-reset", 'z', POPT_ARG_NONE, &badpw_reset, 0, "reset bad password count", NULL},
+ POPT_COMMON_SAMBA
+ POPT_TABLEEND
+ };
+
+ setup_logging("pdbedit", True);
+
+ pc = poptGetContext(NULL, argc, (const char **) argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'C':
+ account_policy_value_set = True;
+ break;
+ }
+ }
+
+ poptGetArg(pc); /* Drop argv[0], the program name */
+
+ if (user_name == NULL)
+ user_name = poptGetArg(pc);
+
+ if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
+ fprintf(stderr, "Can't load %s - run testparm to debug it\n", dyn_CONFIGFILE);
+ exit(1);
+ }
+
+ if(!initialize_password_db(False))
+ exit(1);
+
+ if (!init_names())
+ exit(1);
+
+ setparms = (backend ? BIT_BACKEND : 0) +
+ (verbose ? BIT_VERBOSE : 0) +
+ (spstyle ? BIT_SPSTYLE : 0) +
+ (full_name ? BIT_FULLNAME : 0) +
+ (home_dir ? BIT_HOMEDIR : 0) +
+ (home_drive ? BIT_HDIRDRIVE : 0) +
+ (logon_script ? BIT_LOGSCRIPT : 0) +
+ (profile_path ? BIT_PROFILE : 0) +
+ (machine ? BIT_MACHINE : 0) +
+ (trustdom ? BIT_TRUSTDOM : 0) +
+ (trustpw ? BIT_TRUSTPW : 0) +
+ (trustsid ? BIT_TRUSTSID : 0) +
+ (trustflags ? BIT_TRUSTFLAGS : 0) +
+ (user_name ? BIT_USER : 0) +
+ (list_users ? BIT_LIST : 0) +
+ (force_initialised_password ? BIT_FIX_INIT : 0) +
+ (modify_user ? BIT_MODIFY : 0) +
+ (add_user ? BIT_CREATE : 0) +
+ (delete_user ? BIT_DELETE : 0) +
+ (account_control ? BIT_ACCTCTRL : 0) +
+ (account_policy ? BIT_ACCPOLICY : 0) +
+ (account_policy_value_set ? BIT_ACCPOLVAL : 0) +
+ (backend_in ? BIT_IMPORT : 0) +
+ (backend_out ? BIT_EXPORT : 0) +
+ (badpw_reset ? BIT_BADPWRESET : 0);
+
+ if (setparms & BIT_BACKEND) {
+ if (!NT_STATUS_IS_OK(make_pdb_context_string(&bdef, backend))) {
+ fprintf(stderr, "Can't initialize passdb backend.\n");
+ return 1;
+ }
+ } else {
+ if (!NT_STATUS_IS_OK(make_pdb_context_list(&bdef, lp_passdb_backend()))) {
+ fprintf(stderr, "Can't initialize passdb backend.\n");
+ return 1;
+ }
+ }
+
+ /* the lowest bit options are always accepted */
+ checkparms = setparms & ~MASK_ALWAYS_GOOD;
+
+ if (checkparms & BIT_FIX_INIT) {
+ return fix_users_list(bdef);
+ }
+
+ /* account policy operations */
+ if ((checkparms & BIT_ACCPOLICY) && !(checkparms & ~(BIT_ACCPOLICY + BIT_ACCPOLVAL))) {
+ uint32 value;
+ int field = account_policy_name_to_fieldnum(account_policy);
+ if (field == 0) {
+ fprintf(stderr, "No account policy by that name\n");
+ exit(1);
+ }
+ if (!account_policy_get(field, &value)) {
+ fprintf(stderr, "valid account policy, but unable to fetch value!\n");
+ exit(1);
+ }
+ if (account_policy_value_set) {
+ printf("account policy value for %s was %u\n", account_policy, value);
+ if (!account_policy_set(field, account_policy_value)) {
+ fprintf(stderr, "valid account policy, but unable to set value!\n");
+ exit(1);
+ }
+ printf("account policy value for %s is now %lu\n", account_policy, account_policy_value);
+ exit(0);
+ } else {
+ printf("account policy value for %s is %u\n", account_policy, value);
+ exit(0);
+ }
+ }
+
+ /* import and export operations */
+ if (((checkparms & BIT_IMPORT) || (checkparms & BIT_EXPORT))
+ && !(checkparms & ~(BIT_IMPORT +BIT_EXPORT))) {
+ if (backend_in) {
+ if (!NT_STATUS_IS_OK(make_pdb_context_string(&bin, backend_in))) {
+ fprintf(stderr, "Can't initialize passdb backend.\n");
+ return 1;
+ }
+ } else {
+ bin = bdef;
+ }
+ if (backend_out) {
+ if (!NT_STATUS_IS_OK(make_pdb_context_string(&bout, backend_out))) {
+ fprintf(stderr, "Can't initialize %s.\n", backend_out);
+ return 1;
+ }
+ } else {
+ bout = bdef;
+ }
+ if (transfer_groups) {
+ return export_groups(bin, bout);
+ } else {
+ return export_database(bin, bout);
+ }
+ }
+
+ /* if BIT_USER is defined but nothing else then threat it as -l -u for compatibility */
+ /* fake up BIT_LIST if only BIT_USER is defined */
+ if ((checkparms & BIT_USER) && !(checkparms & ~BIT_USER)) {
+ checkparms += BIT_LIST;
+ }
+
+ /* modify flag is optional to maintain backwards compatibility */
+ /* fake up BIT_MODIFY if BIT_USER and at least one of MASK_USER_GOOD is defined */
+ if (!((checkparms & ~MASK_USER_GOOD) & ~BIT_USER) && (checkparms & MASK_USER_GOOD)) {
+ checkparms += BIT_MODIFY;
+ }
+
+ /* list users operations */
+ if (checkparms & BIT_LIST) {
+ if (!(checkparms & ~BIT_LIST)) {
+ print_users_list (bdef, verbose, spstyle);
+ return print_trustpw_list(bdef, verbose, spstyle);
+ }
+ if (!(checkparms & ~(BIT_USER + BIT_LIST))) {
+ return print_user_info (bdef, user_name, verbose, spstyle);
+
+ } else if (!(checkparms & ~(BIT_TRUSTPW + BIT_LIST))) {
+ return print_trust_info(bdef, trustpw, verbose, spstyle);
+ }
+ }
+
+ /* mask out users options */
+ checkparms &= ~MASK_USER_GOOD;
+
+ /* if bad password count is reset, we must be modifying */
+ if (checkparms & BIT_BADPWRESET) {
+ checkparms |= BIT_MODIFY;
+ checkparms &= ~BIT_BADPWRESET;
+ }
+
+ /* account operation */
+ if ((checkparms & BIT_CREATE) || (checkparms & BIT_MODIFY) || (checkparms & BIT_DELETE)) {
+ /* check use of -u option */
+ if (!(checkparms & (BIT_USER + BIT_TRUSTPW))) {
+ fprintf (stderr, "Username not specified! (use -u option)\n");
+ return -1;
+ }
+
+ /* account creation operations */
+ if (!(checkparms & ~(BIT_CREATE + BIT_USER + BIT_MACHINE + BIT_TRUSTDOM))) {
+ /* machine trust account */
+ if (checkparms & BIT_MACHINE) {
+ return new_machine (bdef, user_name);
+ /* interdomain trust account */
+ } else if (checkparms & BIT_TRUSTDOM) {
+ return new_trustdom(bdef, user_name);
+
+ /* ordinary user account */
+ } else {
+ return new_user (bdef, user_name, full_name, home_dir,
+ home_drive, logon_script,
+ profile_path, user_sid, group_sid);
+ }
+ }
+
+ /* account deletion operations */
+ if (!(checkparms & ~(BIT_DELETE + BIT_USER + BIT_MACHINE))) {
+ if (checkparms & BIT_MACHINE) {
+ return delete_machine_entry (bdef, user_name);
+ } else {
+ return delete_user_entry (bdef, user_name);
+ }
+ }
+
+ /* account modification operations */
+ if (!(checkparms & ~(BIT_MODIFY + BIT_USER))) {
+ return set_user_info (bdef, user_name, full_name,
+ home_dir,
+ home_drive,
+ logon_script,
+ profile_path, account_control,
+ user_sid, group_sid,
+ badpw_reset);
+ }
+ }
+
+ /* trust password operation */
+ if ((checkparms & BIT_CREATE) || (checkparms & BIT_MODIFY) || (checkparms & BIT_DELETE)) {
+ /* trust password creation */
+ if (!(checkparms & ~(BIT_CREATE + BIT_TRUSTPW + BIT_TRUSTSID + BIT_TRUSTFLAGS))) {
+ return new_trustpw(bdef, trustpw, trustsid, trustflags);
+ }
+ }
+
+
+ if (setparms >= 0x20) {
+ fprintf (stderr, "Incompatible or insufficient options on command line!\n");
+ }
+ poptPrintHelp(pc, stderr, 0);
+
+ return 1;
+}
+
diff --git a/source/utils/profiles.c b/source/utils/profiles.c
new file mode 100644
index 00000000000..a31674dfb2e
--- /dev/null
+++ b/source/utils/profiles.c
@@ -0,0 +1,742 @@
+/*
+ Samba Unix/Linux SMB client utility profiles.c
+ Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com
+ Copyright (C) 2003 Jelmer Vernooij (conversion to popt)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*************************************************************************
+
+ A utility to report and change SIDs in registry files
+
+ Many of the ideas in here come from other people and software.
+ I first looked in Wine in misc/registry.c and was also influenced by
+ http://www.wednesday.demon.co.uk/dosreg.html
+
+ Which seems to contain comments from someone else. I reproduce them here
+ incase the site above disappears. It actually comes from
+ http://home.eunet.no/~pnordahl/ntpasswd/WinReg.txt.
+
+The windows NT registry has 2 different blocks, where one can occure many
+times...
+
+the "regf"-Block
+================
+
+"regf" is obviously the abbreviation for "Registry file". "regf" is the
+signature of the header-block which is always 4kb in size, although only
+the first 64 bytes seem to be used and a checksum is calculated over
+the first 0x200 bytes only!
+
+Offset Size Contents
+0x00000000 D-Word ID: ASCII-"regf" = 0x66676572
+0x00000004 D-Word ???? //see struct REGF
+0x00000008 D-Word ???? Always the same value as at 0x00000004
+0x0000000C Q-Word last modify date in WinNT date-format
+0x00000014 D-Word 1
+0x00000018 D-Word 3
+0x0000001C D-Word 0
+0x00000020 D-Word 1
+0x00000024 D-Word Offset of 1st key record
+0x00000028 D-Word Size of the data-blocks (Filesize-4kb)
+0x0000002C D-Word 1
+0x000001FC D-Word Sum of all D-Words from 0x00000000 to
+0x000001FB //XOR of all words. Nigel
+
+I have analyzed more registry files (from multiple machines running
+NT 4.0 german version) and could not find an explanation for the values
+marked with ???? the rest of the first 4kb page is not important...
+
+the "hbin"-Block
+================
+I don't know what "hbin" stands for, but this block is always a multiple
+of 4kb in size.
+
+Inside these hbin-blocks the different records are placed. The memory-
+management looks like a C-compiler heap management to me...
+
+hbin-Header
+===========
+Offset Size Contents
+0x0000 D-Word ID: ASCII-"hbin" = 0x6E696268
+0x0004 D-Word Offset from the 1st hbin-Block
+0x0008 D-Word Offset to the next hbin-Block
+0x001C D-Word Block-size
+
+The values in 0x0008 and 0x001C should be the same, so I don't know
+if they are correct or swapped...
+
+From offset 0x0020 inside a hbin-block data is stored with the following
+format:
+
+Offset Size Contents
+0x0000 D-Word Data-block size //this size must be a
+multiple of 8. Nigel
+0x0004 ???? Data
+
+If the size field is negative (bit 31 set), the corresponding block
+is free and has a size of -blocksize!
+
+The data is stored as one record per block. Block size is a multiple
+of 4 and the last block reaches the next hbin-block, leaving no room.
+
+Records in the hbin-blocks
+==========================
+
+nk-Record
+
+ The nk-record can be treated as a kombination of tree-record and
+ key-record of the win 95 registry.
+
+lf-Record
+
+ The lf-record is the counterpart to the RGKN-record (the
+ hash-function)
+
+vk-Record
+
+ The vk-record consists information to a single value.
+
+sk-Record
+
+ sk (? Security Key ?) is the ACL of the registry.
+
+Value-Lists
+
+ The value-lists contain information about which values are inside a
+ sub-key and don't have a header.
+
+Datas
+
+ The datas of the registry are (like the value-list) stored without a
+ header.
+
+All offset-values are relative to the first hbin-block and point to the
+block-size field of the record-entry. to get the file offset, you have to add
+the header size (4kb) and the size field (4 bytes)...
+
+the nk-Record
+=============
+Offset Size Contents
+0x0000 Word ID: ASCII-"nk" = 0x6B6E
+0x0002 Word for the root-key: 0x2C, otherwise 0x20 //key symbolic links 0x10. Nigel
+0x0004 Q-Word write-date/time in windows nt notation
+0x0010 D-Word Offset of Owner/Parent key
+0x0014 D-Word number of sub-Keys
+0x001C D-Word Offset of the sub-key lf-Records
+0x0024 D-Word number of values
+0x0028 D-Word Offset of the Value-List
+0x002C D-Word Offset of the sk-Record
+
+0x0030 D-Word Offset of the Class-Name //see NK structure for the use of these fields. Nigel
+0x0044 D-Word Unused (data-trash) //some kind of run time index. Does not appear to be important. Nigel
+0x0048 Word name-length
+0x004A Word class-name length
+0x004C ???? key-name
+
+the Value-List
+==============
+Offset Size Contents
+0x0000 D-Word Offset 1st Value
+0x0004 D-Word Offset 2nd Value
+0x???? D-Word Offset nth Value
+
+To determine the number of values, you have to look at the owner-nk-record!
+
+Der vk-Record
+=============
+Offset Size Contents
+0x0000 Word ID: ASCII-"vk" = 0x6B76
+0x0002 Word name length
+0x0004 D-Word length of the data //if top bit is set when offset contains data. Nigel
+0x0008 D-Word Offset of Data
+0x000C D-Word Type of value
+0x0010 Word Flag
+0x0012 Word Unused (data-trash)
+0x0014 ???? Name
+
+If bit 0 of the flag-word is set, a name is present, otherwise the value has no name (=default)
+
+If the data-size is lower 5, the data-offset value is used to store the data itself!
+
+The data-types
+==============
+Wert Beteutung
+0x0001 RegSZ: character string (in UNICODE!)
+0x0002 ExpandSZ: string with "%var%" expanding (UNICODE!)
+0x0003 RegBin: raw-binary value
+0x0004 RegDWord: Dword
+0x0007 RegMultiSZ: multiple strings, separated with 0
+ (UNICODE!)
+
+The "lf"-record
+===============
+Offset Size Contents
+0x0000 Word ID: ASCII-"lf" = 0x666C
+0x0002 Word number of keys
+0x0004 ???? Hash-Records
+
+Hash-Record
+===========
+Offset Size Contents
+0x0000 D-Word Offset of corresponding "nk"-Record
+0x0004 D-Word ASCII: the first 4 characters of the key-name, padded with 0's. Case sensitiv!
+
+Keep in mind, that the value at 0x0004 is used for checking the data-consistency! If you change the
+key-name you have to change the hash-value too!
+
+//These hashrecords must be sorted low to high within the lf record. Nigel.
+
+The "sk"-block
+==============
+(due to the complexity of the SAM-info, not clear jet)
+
+Offset Size Contents
+0x0000 Word ID: ASCII-"sk" = 0x6B73
+0x0002 Word Unused
+0x0004 D-Word Offset of previous "sk"-Record
+0x0008 D-Word Offset of next "sk"-Record
+0x000C D-Word usage-counter
+0x0010 D-Word Size of "sk"-record in bytes
+???? //standard self
+relative security desciptor. Nigel
+???? ???? Security and auditing settings...
+????
+
+The usage counter counts the number of references to this
+"sk"-record. You can use one "sk"-record for the entire registry!
+
+Windows nt date/time format
+===========================
+The time-format is a 64-bit integer which is incremented every
+0,0000001 seconds by 1 (I don't know how accurate it realy is!)
+It starts with 0 at the 1st of january 1601 0:00! All values are
+stored in GMT time! The time-zone is important to get the real
+time!
+
+Common values for win95 and win-nt
+==================================
+Offset values marking an "end of list", are either 0 or -1 (0xFFFFFFFF).
+If a value has no name (length=0, flag(bit 0)=0), it is treated as the
+"Default" entry...
+If a value has no data (length=0), it is displayed as empty.
+
+simplyfied win-3.?? registry:
+=============================
+
++-----------+
+| next rec. |---+ +----->+------------+
+| first sub | | | | Usage cnt. |
+| name | | +-->+------------+ | | length |
+| value | | | | next rec. | | | text |------->+-------+
++-----------+ | | | name rec. |--+ +------------+ | xxxxx |
+ +------------+ | | value rec. |-------->+------------+ +-------+
+ v | +------------+ | Usage cnt. |
++-----------+ | | length |
+| next rec. | | | text |------->+-------+
+| first sub |------+ +------------+ | xxxxx |
+| name | +-------+
+| value |
++-----------+
+
+Greatly simplyfied structure of the nt-registry:
+================================================
+
++---------------------------------------------------------------+
+| |
+v |
++---------+ +---------->+-----------+ +----->+---------+ |
+| "nk" | | | lf-rec. | | | nk-rec. | |
+| ID | | | # of keys | | | parent |---+
+| Date | | | 1st key |--+ | .... |
+| parent | | +-----------+ +---------+
+| suk-keys|-----+
+| values |--------------------->+----------+
+| SK-rec. |---------------+ | 1. value |--> +----------+
+| class |--+ | +----------+ | vk-rec. |
++---------+ | | | .... |
+ v | | data |--> +-------+
+ +------------+ | +----------+ | xxxxx |
+ | Class name | | +-------+
+ +------------+ |
+ v
+ +---------+ +---------+
+ +----->| next sk |--->| Next sk |--+
+ | +---| prev sk |<---| prev sk | |
+ | | | .... | | ... | |
+ | | +---------+ +---------+ |
+ | | ^ | |
+ | +----------+ |
+ +-------------------------------+
+
+---------------------------------------------------------------------------
+
+Hope this helps.... (Although it was "fun" for me to uncover this things,
+ it took me several sleepless nights ;)
+
+ B.D.
+
+*************************************************************************/
+#include "includes.h"
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+typedef unsigned int DWORD;
+typedef unsigned short WORD;
+
+#define REG_REGF_ID 0x66676572
+
+typedef struct regf_block {
+ DWORD REGF_ID; /* regf */
+ DWORD uk1;
+ DWORD uk2;
+ DWORD tim1, tim2;
+ DWORD uk3; /* 1 */
+ DWORD uk4; /* 3 */
+ DWORD uk5; /* 0 */
+ DWORD uk6; /* 1 */
+ DWORD first_key; /* offset */
+ unsigned int dblk_size;
+ DWORD uk7[116]; /* 1 */
+ DWORD chksum;
+} REGF_HDR;
+
+typedef struct hbin_sub_struct {
+ DWORD dblocksize;
+ char data[1];
+} HBIN_SUB_HDR;
+
+#define REG_HBIN_ID 0x6E696268
+
+typedef struct hbin_struct {
+ DWORD HBIN_ID; /* hbin */
+ DWORD next_off;
+ DWORD prev_off;
+ DWORD uk1;
+ DWORD uk2;
+ DWORD uk3;
+ DWORD uk4;
+ DWORD blk_size;
+ HBIN_SUB_HDR hbin_sub_hdr;
+} HBIN_HDR;
+
+#define REG_NK_ID 0x6B6E
+
+typedef struct nk_struct {
+ WORD NK_ID;
+ WORD type;
+ DWORD t1, t2;
+ DWORD uk1;
+ DWORD own_off;
+ DWORD subk_num;
+ DWORD uk2;
+ DWORD lf_off;
+ DWORD uk3;
+ DWORD val_cnt;
+ DWORD val_off;
+ DWORD sk_off;
+ DWORD clsnam_off;
+} NK_HDR;
+
+#define REG_SK_ID 0x6B73
+
+typedef struct sk_struct {
+ WORD SK_ID;
+ WORD uk1;
+ DWORD prev_off;
+ DWORD next_off;
+ DWORD ref_cnt;
+ DWORD rec_size;
+ char sec_desc[1];
+} SK_HDR;
+
+typedef struct sec_desc_rec {
+ WORD rev;
+ WORD type;
+ DWORD owner_off;
+ DWORD group_off;
+ DWORD sacl_off;
+ DWORD dacl_off;
+} MY_SEC_DESC;
+
+typedef struct ace_struct {
+ unsigned char type;
+ unsigned char flags;
+ unsigned short length;
+ unsigned int perms;
+ DOM_SID trustee;
+} ACE;
+
+typedef struct acl_struct {
+ WORD rev;
+ WORD size;
+ DWORD num_aces;
+ ACE *aces; /* One or more ACEs */
+} ACL;
+
+#define OFF(f) (0x1000 + (f) + 4)
+
+static void print_sid(DOM_SID *sid);
+
+int verbose = 1;
+DOM_SID old_sid, new_sid;
+int change = 0, new = 0;
+
+/* Compare two SIDs for equality */
+static int my_sid_equal(DOM_SID *s1, DOM_SID *s2)
+{
+ int sa1, sa2;
+
+ if (s1->sid_rev_num != s2->sid_rev_num) return 0;
+
+ sa1 = s1->num_auths; sa2 = s2->num_auths;
+
+ if (sa1 != sa2) return 0;
+
+ return !memcmp((char *)&s1->id_auth, (char *)&s2->id_auth,
+ 6 + sa1 * 4);
+
+}
+
+/*
+ * Quick and dirty to read a SID in S-1-5-21-x-y-z-rid format and
+ * construct a DOM_SID
+ */
+static int get_sid(DOM_SID *sid, const unsigned char *sid_str)
+{
+ int i = 0, auth;
+ const unsigned char *lstr;
+
+ if (strncmp(sid_str, "S-1-5", 5)) {
+ fprintf(stderr, "Does not conform to S-1-5...: %s\n", sid_str);
+ return 0;
+ }
+
+ /* We only allow strings of form S-1-5... */
+
+ sid->sid_rev_num = 1;
+ sid->id_auth[5] = 5;
+
+ lstr = sid_str + 5;
+
+ while (1) {
+ if (!lstr || !lstr[0] || sscanf(lstr, "-%u", &auth) == 0) {
+ if (i < 4) {
+ fprintf(stderr, "Not of form -d-d...: %s, %u\n", lstr, i);
+ return 0;
+ }
+ sid->num_auths=i;
+ print_sid(sid);
+ return 1;
+ }
+
+ SIVAL(&sid->sub_auths[i], 0, auth);
+ i++;
+ lstr = (const unsigned char *)strchr(lstr + 1, '-');
+ }
+
+ return 1;
+}
+
+#if 0
+
+/*
+ * Replace SID1, component by component with SID2
+ * Assumes will never be called with unequal length SIDS
+ * so only touches 21-x-y-z-rid portion
+ * This routine does not need to deal with endianism as
+ * long as the incoming SIDs are both in the same (LE) format.
+ */
+static void change_sid(DOM_SID *s1, DOM_SID *s2)
+{
+ int i;
+
+ for (i=0; i<s1->num_auths; i++) {
+ s1->sub_auths[i] = s2->sub_auths[i];
+ }
+}
+
+#endif
+
+static void print_sid(DOM_SID *sid)
+{
+ int i, comps = sid->num_auths;
+ fprintf(stdout, "S-%u-%u", sid->sid_rev_num, sid->id_auth[5]);
+
+ for (i = 0; i < comps; i++) {
+
+ fprintf(stdout, "-%u", IVAL(&sid->sub_auths[i],0));
+
+ }
+ fprintf(stdout, "\n");
+}
+
+static void process_sid(DOM_SID *sid, DOM_SID *o_sid, DOM_SID *n_sid)
+{
+ int i;
+ if (my_sid_equal(sid, o_sid)) {
+
+ for (i=0; i<sid->num_auths; i++) {
+ sid->sub_auths[i] = n_sid->sub_auths[i];
+
+ }
+
+ }
+
+}
+
+static void process_acl(ACL *acl, const char *prefix)
+{
+ int ace_cnt, i;
+ ACE *ace;
+
+ ace_cnt = IVAL(&acl->num_aces, 0);
+ ace = (ACE *)&acl->aces;
+ if (verbose) fprintf(stdout, "%sACEs: %u\n", prefix, ace_cnt);
+ for (i=0; i<ace_cnt; i++) {
+ if (verbose) fprintf(stdout, "%s Perms: %08X, SID: ", prefix,
+ IVAL(&ace->perms, 0));
+ if (change)
+ process_sid(&ace->trustee, &old_sid, &new_sid);
+ print_sid(&ace->trustee);
+ ace = (ACE *)((char *)ace + SVAL(&ace->length, 0));
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int opt;
+ int fd, start = 0;
+ char *base;
+ struct stat sbuf;
+ REGF_HDR *regf_hdr;
+ HBIN_HDR *hbin_hdr;
+ NK_HDR *nk_hdr;
+ SK_HDR *sk_hdr;
+ DWORD first_sk_off, sk_off;
+ MY_SEC_DESC *sec_desc;
+ int *ptr;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "verbose", 'v', POPT_ARG_NONE, NULL, 'v', "Sets verbose mode" },
+ { "change-sid", 'c', POPT_ARG_STRING, NULL, 'c', "Provides SID to change" },
+ { "new-sid", 'n', POPT_ARG_STRING, NULL, 'n', "Provides SID to change to" },
+ { 0, 0, 0, 0 }
+ };
+
+ poptContext pc;
+
+ pc = poptGetContext("profiles", argc, (const char **)argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ poptSetOtherOptionHelp(pc, "<profilefile>");
+
+ /*
+ * Now, process the arguments
+ */
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'c':
+ change = 1;
+ if (!get_sid(&old_sid, poptGetOptArg(pc))) {
+ fprintf(stderr, "Argument to -c should be a SID in form of S-1-5-...\n");
+ poptPrintUsage(pc, stderr, 0);
+ exit(254);
+ }
+ break;
+
+ case 'n':
+ new = 1;
+ if (!get_sid(&new_sid, poptGetOptArg(pc))) {
+ fprintf(stderr, "Argument to -n should be a SID in form of S-1-5-...\n");
+ poptPrintUsage(pc, stderr, 0);
+ exit(253);
+ }
+
+ break;
+
+ case 'v':
+ verbose++;
+ break;
+ }
+ }
+
+ if (!poptPeekArg(pc)) {
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+
+ if ((!change & new) || (change & !new)) {
+ fprintf(stderr, "You must specify both -c and -n if one or the other is set!\n");
+ poptPrintUsage(pc, stderr, 0);
+ exit(252);
+ }
+
+ poptGetArg(pc); /* To get argv[0] */
+
+ fd = open(poptPeekArg(pc), O_RDWR, 0000);
+
+ if (fd < 0) {
+ fprintf(stderr, "Could not open %s: %s\n", poptPeekArg(pc),
+ strerror(errno));
+ exit(2);
+ }
+
+ if (fstat(fd, &sbuf) < 0) {
+ fprintf(stderr, "Could not stat file %s, %s\n", poptPeekArg(pc),
+ strerror(errno));
+ exit(3);
+ }
+
+ /*
+ * Now, mmap the file into memory, check the header and start
+ * dealing with the records. We are interested in the sk record
+ */
+ start = 0;
+
+#ifdef HAVE_MMAP
+ base = mmap(&start, sbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+#else
+ base = (char *)-1;
+ errno = ENOSYS;
+#endif
+
+ if ((int)base == -1) {
+ fprintf(stderr, "Could not mmap file: %s, %s\n", poptPeekArg(pc),
+ strerror(errno));
+ exit(4);
+ }
+
+ /*
+ * In what follows, and in places above, in order to work on both LE and
+ * BE platforms, we have to use the Samba macros to extract SHORT, LONG
+ * and associated UNSIGNED quantities from the data in the mmap'd file.
+ * NOTE, however, that we do not need to do anything with memory
+ * addresses that we construct from pointers in our address space.
+ * For example,
+ *
+ * sec_desc = (MY_SEC_DESC *)&(sk_hdr->sec_desc[0]);
+ *
+ * is simply taking the address of a structure we already have the address
+ * of in our address space, while, the fields within it, will have to
+ * be accessed with the macros:
+ *
+ * owner_sid = (DOM_SID *)(&sk_hdr->sec_desc[0] +
+ * IVAL(&sec_desc->owner_off, 0));
+ *
+ * Which is pulling out an offset and adding it to an existing pointer.
+ *
+ */
+
+ regf_hdr = (REGF_HDR *)base;
+
+ if (verbose) fprintf(stdout, "Registry file size: %u\n", (unsigned int)sbuf.st_size);
+
+ if (IVAL(&regf_hdr->REGF_ID, 0) != REG_REGF_ID) {
+ fprintf(stderr, "Incorrect Registry file (doesn't have header ID): %s\n", poptPeekArg(pc));
+ exit(5);
+ }
+
+ if (verbose) fprintf(stdout, "First Key Off: %u, Data Block Size: %u\n",
+ IVAL(&regf_hdr->first_key, 0),
+ IVAL(&regf_hdr->dblk_size, 0));
+
+ hbin_hdr = (HBIN_HDR *)(base + 0x1000); /* No need for Endian stuff */
+
+ /*
+ * This should be the hbin_hdr
+ */
+
+ if (IVAL(&hbin_hdr->HBIN_ID, 0) != REG_HBIN_ID) {
+ fprintf(stderr, "Incorrect hbin hdr: %s\n", poptPeekArg(pc));
+ exit(6);
+ }
+
+ if (verbose) fprintf(stdout, "Next Off: %u, Prev Off: %u\n",
+ IVAL(&hbin_hdr->next_off, 0),
+ IVAL(&hbin_hdr->prev_off, 0));
+
+ nk_hdr = (NK_HDR *)(base + 0x1000 + IVAL(&regf_hdr->first_key, 0) + 4);
+
+ if (SVAL(&nk_hdr->NK_ID, 0) != REG_NK_ID) {
+ fprintf(stderr, "Incorrect NK Header: %s\n", poptPeekArg(pc));
+ exit(7);
+ }
+
+ sk_off = first_sk_off = IVAL(&nk_hdr->sk_off, 0);
+ if (verbose) {
+ fprintf(stdout, "Type: %0x\n", SVAL(&nk_hdr->type, 0));
+ fprintf(stdout, "SK Off : %o\n", (0x1000 + sk_off + 4));
+ }
+
+ sk_hdr = (SK_HDR *)(base + 0x1000 + sk_off + 4);
+
+ do {
+ DOM_SID *owner_sid, *group_sid;
+ ACL *sacl, *dacl;
+ if (SVAL(&sk_hdr->SK_ID, 0) != REG_SK_ID) {
+ fprintf(stderr, "Incorrect SK Header format: %08X\n",
+ (0x1000 + sk_off + 4));
+ exit(8);
+ }
+ ptr = (int *)sk_hdr;
+ if (verbose) fprintf(stdout, "Off: %08X, Refs: %u, Size: %u\n",
+ sk_off, IVAL(&sk_hdr->ref_cnt, 0),
+ IVAL(&sk_hdr->rec_size, 0));
+
+ sec_desc = (MY_SEC_DESC *)&(sk_hdr->sec_desc[0]);
+ owner_sid = (DOM_SID *)(&sk_hdr->sec_desc[0] +
+ IVAL(&sec_desc->owner_off, 0));
+ group_sid = (DOM_SID *)(&sk_hdr->sec_desc[0] +
+ IVAL(&sec_desc->group_off, 0));
+ sacl = (ACL *)(&sk_hdr->sec_desc[0] +
+ IVAL(&sec_desc->sacl_off, 0));
+ dacl = (ACL *)(&sk_hdr->sec_desc[0] +
+ IVAL(&sec_desc->dacl_off, 0));
+ if (verbose)fprintf(stdout, " Owner SID: ");
+ if (change) process_sid(owner_sid, &old_sid, &new_sid);
+ if (verbose) print_sid(owner_sid);
+ if (verbose) fprintf(stdout, " Group SID: ");
+ if (change) process_sid(group_sid, &old_sid, &new_sid);
+ if (verbose) print_sid(group_sid);
+ fprintf(stdout, " SACL: ");
+ if (!sec_desc->sacl_off) { /* LE zero == BE zero */
+ if (verbose) fprintf(stdout, "NONE\n");
+ }
+ else
+ process_acl(sacl, " ");
+ if (verbose) fprintf(stdout, " DACL: ");
+ if (!sec_desc->dacl_off) {
+ if (verbose) fprintf(stdout, "NONE\n");
+ }
+ else
+ process_acl(dacl, " ");
+ sk_off = IVAL(&sk_hdr->prev_off, 0);
+ sk_hdr = (SK_HDR *)(base + OFF(IVAL(&sk_hdr->prev_off, 0)));
+ } while (sk_off != first_sk_off);
+
+#ifdef HAVE_MMAP
+ munmap(base, sbuf.st_size);
+#endif
+
+ poptFreeContext(pc);
+
+ close(fd);
+ return 0;
+}
diff --git a/source/utils/rpccheck.c b/source/utils/rpccheck.c
new file mode 100644
index 00000000000..ae109f69b65
--- /dev/null
+++ b/source/utils/rpccheck.c
@@ -0,0 +1,62 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Jean François Micouleau 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+main()
+{
+ char filter[]="0123456789ABCDEF";
+
+ char s[128];
+ char d=0;
+ int x=0;
+ prs_struct ps;
+ TALLOC_CTX *ctx;
+
+ /* change that struct */
+ SAMR_R_QUERY_USERINFO rpc_stub;
+
+ ZERO_STRUCT(rpc_stub);
+
+ setup_logging("", True);
+ DEBUGLEVEL=10;
+
+ ctx=talloc_init("main");
+ if (!ctx) exit(1);
+
+ prs_init(&ps, 1600, 4, ctx, MARSHALL);
+
+ while (scanf("%s", s)!=-1) {
+ if (strlen(s)==2 && strchr_m(filter, *s)!=NULL && strchr_m(filter, *(s+1))!=NULL) {
+ d=strtol(s, NULL, 16);
+ if(!prs_append_data(&ps, &d, 1))
+ printf("error while reading data\n");
+ }
+ }
+
+ prs_switch_type(&ps, UNMARSHALL);
+ prs_set_offset(&ps, 0);
+
+ /* change that call */
+ if(!samr_io_r_query_userinfo("", &rpc_stub, &ps, 0))
+ printf("error while UNMARSHALLING the data\n");
+
+ printf("\n");
+}
diff --git a/source/utils/smbcacls.c b/source/utils/smbcacls.c
new file mode 100644
index 00000000000..5a70d168842
--- /dev/null
+++ b/source/utils/smbcacls.c
@@ -0,0 +1,877 @@
+/*
+ Unix SMB/CIFS implementation.
+ ACL get/set utility
+
+ Copyright (C) Andrew Tridgell 2000
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jeremy Allison 2000
+ Copyright (C) Jelmer Vernooij 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static pstring owner_username;
+static fstring server;
+static int test_args = False;
+static TALLOC_CTX *ctx;
+
+#define CREATE_ACCESS_READ READ_CONTROL_ACCESS
+
+/* numeric is set when the user wants numeric SIDs and ACEs rather
+ than going via LSA calls to resolve them */
+static BOOL numeric = False;
+
+enum acl_mode {SMB_ACL_SET, SMB_ACL_DELETE, SMB_ACL_MODIFY, SMB_ACL_ADD };
+enum chown_mode {REQUEST_NONE, REQUEST_CHOWN, REQUEST_CHGRP};
+enum exit_values {EXIT_OK, EXIT_FAILED, EXIT_PARSE_ERROR};
+
+struct perm_value {
+ const char *perm;
+ uint32 mask;
+};
+
+/* These values discovered by inspection */
+
+static const struct perm_value special_values[] = {
+ { "R", 0x00120089 },
+ { "W", 0x00120116 },
+ { "X", 0x001200a0 },
+ { "D", 0x00010000 },
+ { "P", 0x00040000 },
+ { "O", 0x00080000 },
+ { NULL, 0 },
+};
+
+static const struct perm_value standard_values[] = {
+ { "READ", 0x001200a9 },
+ { "CHANGE", 0x001301bf },
+ { "FULL", 0x001f01ff },
+ { NULL, 0 },
+};
+
+static struct cli_state *global_hack_cli;
+static POLICY_HND pol;
+static BOOL got_policy_hnd;
+
+static struct cli_state *connect_one(const char *share);
+
+/* Open cli connection and policy handle */
+
+static BOOL cacls_open_policy_hnd(void)
+{
+ /* Initialise cli LSA connection */
+
+ if (!global_hack_cli) {
+ global_hack_cli = connect_one("IPC$");
+ if (!cli_nt_session_open (global_hack_cli, PI_LSARPC)) {
+ return False;
+ }
+ }
+
+ /* Open policy handle */
+
+ if (!got_policy_hnd) {
+
+ /* Some systems don't support SEC_RIGHTS_MAXIMUM_ALLOWED,
+ but NT sends 0x2000000 so we might as well do it too. */
+
+ if (!NT_STATUS_IS_OK(cli_lsa_open_policy(global_hack_cli, global_hack_cli->mem_ctx, True,
+ GENERIC_EXECUTE_ACCESS, &pol))) {
+ return False;
+ }
+
+ got_policy_hnd = True;
+ }
+
+ return True;
+}
+
+/* convert a SID to a string, either numeric or username/group */
+static void SidToString(fstring str, DOM_SID *sid)
+{
+ char **domains = NULL;
+ char **names = NULL;
+ uint32 *types = NULL;
+
+ sid_to_string(str, sid);
+
+ if (numeric) return;
+
+ /* Ask LSA to convert the sid to a name */
+
+ if (!cacls_open_policy_hnd() ||
+ !NT_STATUS_IS_OK(cli_lsa_lookup_sids(global_hack_cli, global_hack_cli->mem_ctx,
+ &pol, 1, sid, &domains,
+ &names, &types)) ||
+ !domains || !domains[0] || !names || !names[0]) {
+ return;
+ }
+
+ /* Converted OK */
+
+ slprintf(str, sizeof(fstring) - 1, "%s%s%s",
+ domains[0], lp_winbind_separator(),
+ names[0]);
+
+}
+
+/* convert a string to a SID, either numeric or username/group */
+static BOOL StringToSid(DOM_SID *sid, const char *str)
+{
+ uint32 *types = NULL;
+ DOM_SID *sids = NULL;
+ BOOL result = True;
+
+ if (strncmp(str, "S-", 2) == 0) {
+ return string_to_sid(sid, str);
+ }
+
+ if (!cacls_open_policy_hnd() ||
+ !NT_STATUS_IS_OK(cli_lsa_lookup_names(global_hack_cli, global_hack_cli->mem_ctx,
+ &pol, 1, &str, &sids,
+ &types))) {
+ result = False;
+ goto done;
+ }
+
+ sid_copy(sid, &sids[0]);
+ done:
+
+ return result;
+}
+
+
+/* print an ACE on a FILE, using either numeric or ascii representation */
+static void print_ace(FILE *f, SEC_ACE *ace)
+{
+ const struct perm_value *v;
+ fstring sidstr;
+ int do_print = 0;
+ uint32 got_mask;
+
+ SidToString(sidstr, &ace->trustee);
+
+ fprintf(f, "%s:", sidstr);
+
+ if (numeric) {
+ fprintf(f, "%d/%d/0x%08x",
+ ace->type, ace->flags, ace->info.mask);
+ return;
+ }
+
+ /* Ace type */
+
+ if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED) {
+ fprintf(f, "ALLOWED");
+ } else if (ace->type == SEC_ACE_TYPE_ACCESS_DENIED) {
+ fprintf(f, "DENIED");
+ } else {
+ fprintf(f, "%d", ace->type);
+ }
+
+ /* Not sure what flags can be set in a file ACL */
+
+ fprintf(f, "/%d/", ace->flags);
+
+ /* Standard permissions */
+
+ for (v = standard_values; v->perm; v++) {
+ if (ace->info.mask == v->mask) {
+ fprintf(f, "%s", v->perm);
+ return;
+ }
+ }
+
+ /* Special permissions. Print out a hex value if we have
+ leftover bits in the mask. */
+
+ got_mask = ace->info.mask;
+
+ again:
+ for (v = special_values; v->perm; v++) {
+ if ((ace->info.mask & v->mask) == v->mask) {
+ if (do_print) {
+ fprintf(f, "%s", v->perm);
+ }
+ got_mask &= ~v->mask;
+ }
+ }
+
+ if (!do_print) {
+ if (got_mask != 0) {
+ fprintf(f, "0x%08x", ace->info.mask);
+ } else {
+ do_print = 1;
+ goto again;
+ }
+ }
+}
+
+
+/* parse an ACE in the same format as print_ace() */
+static BOOL parse_ace(SEC_ACE *ace, char *str)
+{
+ char *p;
+ const char *cp;
+ fstring tok;
+ unsigned atype, aflags, amask;
+ DOM_SID sid;
+ SEC_ACCESS mask;
+ const struct perm_value *v;
+
+ ZERO_STRUCTP(ace);
+ p = strchr_m(str,':');
+ if (!p) return False;
+ *p = '\0';
+ p++;
+ /* Try to parse numeric form */
+
+ if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 &&
+ StringToSid(&sid, str)) {
+ goto done;
+ }
+
+ /* Try to parse text form */
+
+ if (!StringToSid(&sid, str)) {
+ return False;
+ }
+
+ cp = p;
+ if (!next_token(&cp, tok, "/", sizeof(fstring))) {
+ return False;
+ }
+
+ if (strncmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
+ atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
+ } else if (strncmp(tok, "DENIED", strlen("DENIED")) == 0) {
+ atype = SEC_ACE_TYPE_ACCESS_DENIED;
+ } else {
+ return False;
+ }
+
+ /* Only numeric form accepted for flags at present */
+
+ if (!(next_token(&cp, tok, "/", sizeof(fstring)) &&
+ sscanf(tok, "%i", &aflags))) {
+ return False;
+ }
+
+ if (!next_token(&cp, tok, "/", sizeof(fstring))) {
+ return False;
+ }
+
+ if (strncmp(tok, "0x", 2) == 0) {
+ if (sscanf(tok, "%i", &amask) != 1) {
+ return False;
+ }
+ goto done;
+ }
+
+ for (v = standard_values; v->perm; v++) {
+ if (strcmp(tok, v->perm) == 0) {
+ amask = v->mask;
+ goto done;
+ }
+ }
+
+ p = tok;
+
+ while(*p) {
+ BOOL found = False;
+
+ for (v = special_values; v->perm; v++) {
+ if (v->perm[0] == *p) {
+ amask |= v->mask;
+ found = True;
+ }
+ }
+
+ if (!found) return False;
+ p++;
+ }
+
+ if (*p) {
+ return False;
+ }
+
+ done:
+ mask.mask = amask;
+ init_sec_ace(ace, &sid, atype, mask, aflags);
+ return True;
+}
+
+/* add an ACE to a list of ACEs in a SEC_ACL */
+static BOOL add_ace(SEC_ACL **the_acl, SEC_ACE *ace)
+{
+ SEC_ACL *new;
+ SEC_ACE *aces;
+ if (! *the_acl) {
+ (*the_acl) = make_sec_acl(ctx, 3, 1, ace);
+ return True;
+ }
+
+ aces = calloc(1+(*the_acl)->num_aces,sizeof(SEC_ACE));
+ memcpy(aces, (*the_acl)->ace, (*the_acl)->num_aces * sizeof(SEC_ACE));
+ memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE));
+ new = make_sec_acl(ctx,(*the_acl)->revision,1+(*the_acl)->num_aces, aces);
+ SAFE_FREE(aces);
+ (*the_acl) = new;
+ return True;
+}
+
+/* parse a ascii version of a security descriptor */
+static SEC_DESC *sec_desc_parse(char *str)
+{
+ const char *p = str;
+ fstring tok;
+ SEC_DESC *ret;
+ size_t sd_size;
+ DOM_SID *grp_sid=NULL, *owner_sid=NULL;
+ SEC_ACL *dacl=NULL;
+ int revision=1;
+
+ while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) {
+
+ if (strncmp(tok,"REVISION:", 9) == 0) {
+ revision = strtol(tok+9, NULL, 16);
+ continue;
+ }
+
+ if (strncmp(tok,"OWNER:", 6) == 0) {
+ owner_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
+ if (!owner_sid ||
+ !StringToSid(owner_sid, tok+6)) {
+ printf("Failed to parse owner sid\n");
+ return NULL;
+ }
+ continue;
+ }
+
+ if (strncmp(tok,"GROUP:", 6) == 0) {
+ grp_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
+ if (!grp_sid ||
+ !StringToSid(grp_sid, tok+6)) {
+ printf("Failed to parse group sid\n");
+ return NULL;
+ }
+ continue;
+ }
+
+ if (strncmp(tok,"ACL:", 4) == 0) {
+ SEC_ACE ace;
+ if (!parse_ace(&ace, tok+4)) {
+ printf("Failed to parse ACL %s\n", tok);
+ return NULL;
+ }
+ if(!add_ace(&dacl, &ace)) {
+ printf("Failed to add ACL %s\n", tok);
+ return NULL;
+ }
+ continue;
+ }
+
+ printf("Failed to parse security descriptor\n");
+ return NULL;
+ }
+
+ ret = make_sec_desc(ctx,revision, SEC_DESC_SELF_RELATIVE, owner_sid, grp_sid,
+ NULL, dacl, &sd_size);
+
+ SAFE_FREE(grp_sid);
+ SAFE_FREE(owner_sid);
+
+ return ret;
+}
+
+
+/* print a ascii version of a security descriptor on a FILE handle */
+static void sec_desc_print(FILE *f, SEC_DESC *sd)
+{
+ fstring sidstr;
+ uint32 i;
+
+ fprintf(f, "REVISION:%d\n", sd->revision);
+
+ /* Print owner and group sid */
+
+ if (sd->owner_sid) {
+ SidToString(sidstr, sd->owner_sid);
+ } else {
+ fstrcpy(sidstr, "");
+ }
+
+ fprintf(f, "OWNER:%s\n", sidstr);
+
+ if (sd->grp_sid) {
+ SidToString(sidstr, sd->grp_sid);
+ } else {
+ fstrcpy(sidstr, "");
+ }
+
+ fprintf(f, "GROUP:%s\n", sidstr);
+
+ /* Print aces */
+ for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
+ SEC_ACE *ace = &sd->dacl->ace[i];
+ fprintf(f, "ACL:");
+ print_ace(f, ace);
+ fprintf(f, "\n");
+ }
+
+}
+
+/*****************************************************
+dump the acls for a file
+*******************************************************/
+static int cacl_dump(struct cli_state *cli, char *filename)
+{
+ int result = EXIT_FAILED;
+ int fnum = -1;
+ SEC_DESC *sd;
+
+ if (test_args)
+ return EXIT_OK;
+
+ fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
+
+ if (fnum == -1) {
+ printf("Failed to open %s: %s\n", filename, cli_errstr(cli));
+ goto done;
+ }
+
+ sd = cli_query_secdesc(cli, fnum, ctx);
+
+ if (!sd) {
+ printf("ERROR: secdesc query failed: %s\n", cli_errstr(cli));
+ goto done;
+ }
+
+ sec_desc_print(stdout, sd);
+
+ result = EXIT_OK;
+
+done:
+ if (fnum != -1)
+ cli_close(cli, fnum);
+
+ return result;
+}
+
+/*****************************************************
+Change the ownership or group ownership of a file. Just
+because the NT docs say this can't be done :-). JRA.
+*******************************************************/
+
+static int owner_set(struct cli_state *cli, enum chown_mode change_mode,
+ char *filename, char *new_username)
+{
+ int fnum;
+ DOM_SID sid;
+ SEC_DESC *sd, *old;
+ size_t sd_size;
+
+ fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
+
+ if (fnum == -1) {
+ printf("Failed to open %s: %s\n", filename, cli_errstr(cli));
+ return EXIT_FAILED;
+ }
+
+ if (!StringToSid(&sid, new_username))
+ return EXIT_PARSE_ERROR;
+
+ old = cli_query_secdesc(cli, fnum, ctx);
+
+ cli_close(cli, fnum);
+
+ if (!old) {
+ printf("owner_set: Failed to query old descriptor\n");
+ return EXIT_FAILED;
+ }
+
+ sd = make_sec_desc(ctx,old->revision, old->type,
+ (change_mode == REQUEST_CHOWN) ? &sid : NULL,
+ (change_mode == REQUEST_CHGRP) ? &sid : NULL,
+ NULL, NULL, &sd_size);
+
+ fnum = cli_nt_create(cli, filename, WRITE_OWNER_ACCESS);
+
+ if (fnum == -1) {
+ printf("Failed to open %s: %s\n", filename, cli_errstr(cli));
+ return EXIT_FAILED;
+ }
+
+ if (!cli_set_secdesc(cli, fnum, sd)) {
+ printf("ERROR: secdesc set failed: %s\n", cli_errstr(cli));
+ }
+
+ cli_close(cli, fnum);
+
+ return EXIT_OK;
+}
+
+
+/* The MSDN is contradictory over the ordering of ACE entries in an ACL.
+ However NT4 gives a "The information may have been modified by a
+ computer running Windows NT 5.0" if denied ACEs do not appear before
+ allowed ACEs. */
+
+static int ace_compare(SEC_ACE *ace1, SEC_ACE *ace2)
+{
+ if (sec_ace_equal(ace1, ace2))
+ return 0;
+
+ if (ace1->type != ace2->type)
+ return ace2->type - ace1->type;
+
+ if (sid_compare(&ace1->trustee, &ace2->trustee))
+ return sid_compare(&ace1->trustee, &ace2->trustee);
+
+ if (ace1->flags != ace2->flags)
+ return ace1->flags - ace2->flags;
+
+ if (ace1->info.mask != ace2->info.mask)
+ return ace1->info.mask - ace2->info.mask;
+
+ if (ace1->size != ace2->size)
+ return ace1->size - ace2->size;
+
+ return memcmp(ace1, ace2, sizeof(SEC_ACE));
+}
+
+static void sort_acl(SEC_ACL *the_acl)
+{
+ uint32 i;
+ if (!the_acl) return;
+
+ qsort(the_acl->ace, the_acl->num_aces, sizeof(the_acl->ace[0]), QSORT_CAST ace_compare);
+
+ for (i=1;i<the_acl->num_aces;) {
+ if (sec_ace_equal(&the_acl->ace[i-1], &the_acl->ace[i])) {
+ int j;
+ for (j=i; j<the_acl->num_aces-1; j++) {
+ the_acl->ace[j] = the_acl->ace[j+1];
+ }
+ the_acl->num_aces--;
+ } else {
+ i++;
+ }
+ }
+}
+
+/*****************************************************
+set the ACLs on a file given an ascii description
+*******************************************************/
+static int cacl_set(struct cli_state *cli, char *filename,
+ char *the_acl, enum acl_mode mode)
+{
+ int fnum;
+ SEC_DESC *sd, *old;
+ uint32 i, j;
+ size_t sd_size;
+ int result = EXIT_OK;
+
+ sd = sec_desc_parse(the_acl);
+
+ if (!sd) return EXIT_PARSE_ERROR;
+ if (test_args) return EXIT_OK;
+
+ /* The desired access below is the only one I could find that works
+ with NT4, W2KP and Samba */
+
+ fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
+
+ if (fnum == -1) {
+ printf("cacl_set failed to open %s: %s\n", filename, cli_errstr(cli));
+ return EXIT_FAILED;
+ }
+
+ old = cli_query_secdesc(cli, fnum, ctx);
+
+ if (!old) {
+ printf("calc_set: Failed to query old descriptor\n");
+ return EXIT_FAILED;
+ }
+
+ cli_close(cli, fnum);
+
+ /* the logic here is rather more complex than I would like */
+ switch (mode) {
+ case SMB_ACL_DELETE:
+ for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
+ BOOL found = False;
+
+ for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
+ if (sec_ace_equal(&sd->dacl->ace[i],
+ &old->dacl->ace[j])) {
+ uint32 k;
+ for (k=j; k<old->dacl->num_aces-1;k++) {
+ old->dacl->ace[k] = old->dacl->ace[k+1];
+ }
+ old->dacl->num_aces--;
+ if (old->dacl->num_aces == 0) {
+ SAFE_FREE(old->dacl->ace);
+ SAFE_FREE(old->dacl);
+ old->off_dacl = 0;
+ }
+ found = True;
+ break;
+ }
+ }
+
+ if (!found) {
+ printf("ACL for ACE:");
+ print_ace(stdout, &sd->dacl->ace[i]);
+ printf(" not found\n");
+ }
+ }
+ break;
+
+ case SMB_ACL_MODIFY:
+ for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
+ BOOL found = False;
+
+ for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
+ if (sid_equal(&sd->dacl->ace[i].trustee,
+ &old->dacl->ace[j].trustee)) {
+ old->dacl->ace[j] = sd->dacl->ace[i];
+ found = True;
+ }
+ }
+
+ if (!found) {
+ fstring str;
+
+ SidToString(str, &sd->dacl->ace[i].trustee);
+ printf("ACL for SID %s not found\n", str);
+ }
+ }
+
+ break;
+
+ case SMB_ACL_ADD:
+ for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
+ add_ace(&old->dacl, &sd->dacl->ace[i]);
+ }
+ break;
+
+ case SMB_ACL_SET:
+ old = sd;
+ break;
+ }
+
+ /* Denied ACE entries must come before allowed ones */
+ sort_acl(old->dacl);
+
+ /* Create new security descriptor and set it */
+ sd = make_sec_desc(ctx,old->revision, old->type, NULL, NULL,
+ NULL, old->dacl, &sd_size);
+
+ fnum = cli_nt_create(cli, filename, WRITE_DAC_ACCESS);
+
+ if (fnum == -1) {
+ printf("cacl_set failed to open %s: %s\n", filename, cli_errstr(cli));
+ return EXIT_FAILED;
+ }
+
+ if (!cli_set_secdesc(cli, fnum, sd)) {
+ printf("ERROR: secdesc set failed: %s\n", cli_errstr(cli));
+ result = EXIT_FAILED;
+ }
+
+ /* Clean up */
+
+ cli_close(cli, fnum);
+
+ return result;
+}
+
+
+/*****************************************************
+return a connection to a server
+*******************************************************/
+static struct cli_state *connect_one(const char *share)
+{
+ struct cli_state *c;
+ struct in_addr ip;
+ NTSTATUS nt_status;
+ zero_ip(&ip);
+
+ if (!cmdline_auth_info.got_pass) {
+ char *pass = getpass("Password: ");
+ if (pass) {
+ pstrcpy(cmdline_auth_info.password, pass);
+ cmdline_auth_info.got_pass = True;
+ }
+ }
+
+ if (NT_STATUS_IS_OK(nt_status = cli_full_connection(&c, global_myname(), server,
+ &ip, 0,
+ share, "?????",
+ cmdline_auth_info.username, lp_workgroup(),
+ cmdline_auth_info.password, 0,
+ cmdline_auth_info.signing_state, NULL))) {
+ return c;
+ } else {
+ DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status)));
+ return NULL;
+ }
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc, const char *argv[])
+{
+ char *share;
+ int opt;
+ enum acl_mode mode = SMB_ACL_SET;
+ static char *the_acl = NULL;
+ enum chown_mode change_mode = REQUEST_NONE;
+ int result;
+ fstring path;
+ pstring filename;
+ poptContext pc;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "delete", 'D', POPT_ARG_STRING, NULL, 'D', "Delete an acl", "ACL" },
+ { "modify", 'M', POPT_ARG_STRING, NULL, 'M', "Modify an acl", "ACL" },
+ { "add", 'a', POPT_ARG_STRING, NULL, 'a', "Add an acl", "ACL" },
+ { "set", 'S', POPT_ARG_STRING, NULL, 'S', "Set acls", "ACLS" },
+ { "chown", 'C', POPT_ARG_STRING, NULL, 'C', "Change ownership of a file", "USERNAME" },
+ { "chgrp", 'G', POPT_ARG_STRING, NULL, 'G', "Change group ownership of a file", "GROUPNAME" },
+ { "numeric", 0, POPT_ARG_NONE, &numeric, True, "Don't resolve sids or masks to names" },
+ { "test-args", 't', POPT_ARG_NONE, &test_args, True, "Test arguments"},
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CREDENTIALS
+ { NULL }
+ };
+
+ struct cli_state *cli;
+
+ ctx=talloc_init("main");
+
+ setlinebuf(stdout);
+
+ dbf = x_stderr;
+
+ setup_logging(argv[0],True);
+
+ lp_load(dyn_CONFIGFILE,True,False,False);
+ load_interfaces();
+
+ pc = poptGetContext("smbcacls", argc, argv, long_options, 0);
+
+ poptSetOtherOptionHelp(pc, "//server1/share1 filename");
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'S':
+ the_acl = smb_xstrdup(poptGetOptArg(pc));
+ mode = SMB_ACL_SET;
+ break;
+
+ case 'D':
+ the_acl = smb_xstrdup(poptGetOptArg(pc));
+ mode = SMB_ACL_DELETE;
+ break;
+
+ case 'M':
+ the_acl = smb_xstrdup(poptGetOptArg(pc));
+ mode = SMB_ACL_MODIFY;
+ break;
+
+ case 'a':
+ the_acl = smb_xstrdup(poptGetOptArg(pc));
+ mode = SMB_ACL_ADD;
+ break;
+
+ case 'C':
+ pstrcpy(owner_username,poptGetOptArg(pc));
+ change_mode = REQUEST_CHOWN;
+ break;
+
+ case 'G':
+ pstrcpy(owner_username,poptGetOptArg(pc));
+ change_mode = REQUEST_CHGRP;
+ break;
+ }
+ }
+
+ /* Make connection to server */
+ if(!poptPeekArg(pc)) {
+ poptPrintUsage(pc, stderr, 0);
+ return -1;
+ }
+
+ fstrcpy(path, poptGetArg(pc));
+
+ if(!poptPeekArg(pc)) {
+ poptPrintUsage(pc, stderr, 0);
+ return -1;
+ }
+
+ pstrcpy(filename, poptGetArg(pc));
+
+ all_string_sub(path,"/","\\",0);
+
+ fstrcpy(server,path+2);
+ share = strchr_m(server,'\\');
+ if (!share) {
+ share = strchr_m(server,'/');
+ if (!share) {
+ printf("Invalid argument: %s\n", share);
+ return -1;
+ }
+ }
+
+ *share = 0;
+ share++;
+
+ if (!test_args) {
+ cli = connect_one(share);
+ if (!cli) {
+ talloc_destroy(ctx);
+ exit(EXIT_FAILED);
+ }
+ } else {
+ exit(0);
+ }
+
+ all_string_sub(filename, "/", "\\", 0);
+ if (filename[0] != '\\') {
+ pstring s;
+ s[0] = '\\';
+ safe_strcpy(&s[1], filename, sizeof(pstring)-2);
+ pstrcpy(filename, s);
+ }
+
+ /* Perform requested action */
+
+ if (change_mode != REQUEST_NONE) {
+ result = owner_set(cli, change_mode, filename, owner_username);
+ } else if (the_acl) {
+ result = cacl_set(cli, filename, the_acl, mode);
+ } else {
+ result = cacl_dump(cli, filename);
+ }
+
+ talloc_destroy(ctx);
+
+ return result;
+}
diff --git a/source/utils/smbcontrol.c b/source/utils/smbcontrol.c
new file mode 100644
index 00000000000..8a27684a4d3
--- /dev/null
+++ b/source/utils/smbcontrol.c
@@ -0,0 +1,747 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Send messages to other Samba daemons
+
+ Copyright (C) Tim Potter 2003
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Martin Pool 2001-2002
+ Copyright (C) Simo Sorce 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* Default timeout value when waiting for replies (in seconds) */
+
+#define DEFAULT_TIMEOUT 10
+
+static int timeout = DEFAULT_TIMEOUT;
+static int num_replies; /* Used by message callback fns */
+
+/* Send a message to a destination pid. Zero means broadcast smbd. */
+
+static BOOL send_message(pid_t pid, int msg_type, const void *buf, int len,
+ BOOL duplicates)
+{
+ TDB_CONTEXT *tdb;
+ BOOL ret;
+ int n_sent = 0;
+
+ if (!message_init())
+ return False;
+
+ if (pid != 0)
+ return message_send_pid(pid, msg_type, buf, len, duplicates);
+
+ tdb = tdb_open_log(lock_path("connections.tdb"), 0,
+ TDB_DEFAULT, O_RDWR, 0);
+ if (!tdb) {
+ fprintf(stderr,"Failed to open connections database"
+ ": %s\n", strerror(errno));
+ return False;
+ }
+
+ ret = message_send_all(tdb,msg_type, buf, len, duplicates,
+ &n_sent);
+ DEBUG(10,("smbcontrol/send_message: broadcast message to "
+ "%d processes\n", n_sent));
+
+ tdb_close(tdb);
+
+ return ret;
+}
+
+/* Wait for one or more reply messages */
+
+static void wait_replies(BOOL multiple_replies)
+{
+ time_t start_time = time(NULL);
+
+ /* Wait around a bit. This is pretty disgusting - we have to
+ busy-wait here as there is no nicer way to do it. */
+
+ do {
+ message_dispatch();
+ if (num_replies > 0 && !multiple_replies)
+ break;
+ sleep(1);
+ } while (timeout - (time(NULL) - start_time) > 0);
+}
+
+/* Message handler callback that displays a string on stdout */
+
+static void print_string_cb(int msg_type, pid_t pid, void *buf, size_t len)
+{
+ printf("%.*s", (int)len, (const char *)buf);
+ num_replies++;
+}
+
+/* Send no message. Useful for testing. */
+
+static BOOL do_noop(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> noop\n");
+ return False;
+ }
+
+ /* Move along, nothing to see here */
+
+ return True;
+}
+
+/* Send a debug string */
+
+static BOOL do_debug(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "Usage: smbcontrol <dest> debug "
+ "<debug-string>\n");
+ return False;
+ }
+
+ return send_message(
+ pid, MSG_DEBUG, argv[1], strlen(argv[1]) + 1, False);
+}
+
+/* Force a browser election */
+
+static BOOL do_election(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> force-election\n");
+ return False;
+ }
+
+ return send_message(
+ pid, MSG_FORCE_ELECTION, NULL, 0, False);
+}
+
+/* Ping a samba daemon process */
+
+static void pong_cb(int msg_type, pid_t pid, void *buf, size_t len)
+{
+ printf("PONG from pid %u\n", (unsigned int)pid);
+ num_replies++;
+}
+
+static BOOL do_ping(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> ping\n");
+ return False;
+ }
+
+ /* Send a message and register our interest in a reply */
+
+ if (!send_message(pid, MSG_PING, NULL, 0, False))
+ return False;
+
+ message_register(MSG_PONG, pong_cb);
+
+ wait_replies(pid == 0);
+
+ /* No replies were received within the timeout period */
+
+ if (num_replies == 0)
+ printf("No replies received\n");
+
+ message_deregister(MSG_PONG);
+
+ return num_replies;
+}
+
+/* Set profiling options */
+
+static BOOL do_profile(const pid_t pid, const int argc, const char **argv)
+{
+ int v;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: smbcontrol <dest> profile "
+ "<off|count|on|flush>\n");
+ return False;
+ }
+
+ if (strcmp(argv[1], "off") == 0) {
+ v = 0;
+ } else if (strcmp(argv[1], "count") == 0) {
+ v = 1;
+ } else if (strcmp(argv[1], "on") == 0) {
+ v = 2;
+ } else if (strcmp(argv[1], "flush") == 0) {
+ v = 3;
+ } else {
+ fprintf(stderr, "Unknown profile command '%s'\n", argv[1]);
+ return False;
+ }
+
+ return send_message(pid, MSG_PROFILE, &v, sizeof(int), False);
+}
+
+/* Return the profiling level */
+
+static void profilelevel_cb(int msg_type, pid_t pid, void *buf, size_t len)
+{
+ int level;
+ const char *s;
+
+ num_replies++;
+
+ if (len != sizeof(int)) {
+ fprintf(stderr, "invalid message length %ld returned\n",
+ (unsigned long)len);
+ return;
+ }
+
+ memcpy(&level, buf, sizeof(int));
+
+ switch (level) {
+ case 0:
+ s = "not enabled";
+ break;
+ case 1:
+ s = "off";
+ break;
+ case 3:
+ s = "count only";
+ break;
+ case 7:
+ s = "count and time";
+ break;
+ default:
+ s = "BOGUS";
+ break;
+ }
+
+ printf("Profiling %s on pid %u\n",s,(unsigned int)pid);
+}
+
+static void profilelevel_rqst(int msg_type, pid_t pid, void *buf, size_t len)
+{
+ int v = 0;
+
+ /* Send back a dummy reply */
+
+ send_message(pid, MSG_PROFILELEVEL, &v, sizeof(int), False);
+}
+
+static BOOL do_profilelevel(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> profilelevel\n");
+ return False;
+ }
+
+ /* Send a message and register our interest in a reply */
+
+ if (!send_message(pid, MSG_REQ_PROFILELEVEL, NULL, 0, False))
+ return False;
+
+ message_register(MSG_PROFILELEVEL, profilelevel_cb);
+ message_register(MSG_REQ_PROFILELEVEL, profilelevel_rqst);
+
+ wait_replies(pid == 0);
+
+ /* No replies were received within the timeout period */
+
+ if (num_replies == 0)
+ printf("No replies received\n");
+
+ message_deregister(MSG_PROFILE);
+
+ return num_replies;
+}
+
+/* Display debug level settings */
+
+static BOOL do_debuglevel(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> debuglevel\n");
+ return False;
+ }
+
+ /* Send a message and register our interest in a reply */
+
+ if (!send_message(pid, MSG_REQ_DEBUGLEVEL, NULL, 0, False))
+ return False;
+
+ message_register(MSG_DEBUGLEVEL, print_string_cb);
+
+ wait_replies(pid == 0);
+
+ /* No replies were received within the timeout period */
+
+ if (num_replies == 0)
+ printf("No replies received\n");
+
+ message_deregister(MSG_DEBUGLEVEL);
+
+ return num_replies;
+}
+
+/* Send a print notify message */
+
+static BOOL do_printnotify(const pid_t pid, const int argc, const char **argv)
+{
+ const char *cmd;
+
+ /* Check for subcommand */
+
+ if (argc == 1) {
+ fprintf(stderr, "Must specify subcommand:\n");
+ fprintf(stderr, "\tqueuepause <printername>\n");
+ fprintf(stderr, "\tqueueresume <printername>\n");
+ fprintf(stderr, "\tjobpause <printername> <unix jobid>\n");
+ fprintf(stderr, "\tjobresume <printername> <unix jobid>\n");
+ fprintf(stderr, "\tjobdelete <printername> <unix jobid>\n");
+ fprintf(stderr, "\tprinter <printername> <comment|port|"
+ "driver> <value>\n");
+
+ return False;
+ }
+
+ cmd = argv[1];
+
+ if (strcmp(cmd, "queuepause") == 0) {
+
+ if (argc != 3) {
+ fprintf(stderr, "Usage: smbcontrol <dest> printnotify"
+ " queuepause <printername>\n");
+ return False;
+ }
+
+ notify_printer_status_byname(argv[2], PRINTER_STATUS_PAUSED);
+
+ goto send;
+
+ } else if (strcmp(cmd, "queueresume") == 0) {
+
+ if (argc != 3) {
+ fprintf(stderr, "Usage: smbcontrol <dest> printnotify"
+ " queuereume <printername>\n");
+ return False;
+ }
+
+ notify_printer_status_byname(argv[2], PRINTER_STATUS_OK);
+
+ goto send;
+
+ } else if (strcmp(cmd, "jobpause") == 0) {
+ int jobid;
+
+ if (argc != 4) {
+ fprintf(stderr, "Usage: smbcontrol <dest> printnotify"
+ " jobpause <printername> <unix-jobid>\n");
+ return False;
+ }
+
+ jobid = atoi(argv[3]);
+
+ notify_job_status_byname(
+ argv[2], jobid, JOB_STATUS_PAUSED,
+ SPOOLSS_NOTIFY_MSG_UNIX_JOBID);
+
+ goto send;
+
+ } else if (strcmp(cmd, "jobresume") == 0) {
+ int jobid;
+
+ if (argc != 4) {
+ fprintf(stderr, "Usage: smbcontrol <dest> printnotify"
+ " jobpause <printername> <unix-jobid>\n");
+ return False;
+ }
+
+ jobid = atoi(argv[3]);
+
+ notify_job_status_byname(
+ argv[2], jobid, JOB_STATUS_QUEUED,
+ SPOOLSS_NOTIFY_MSG_UNIX_JOBID);
+
+ goto send;
+
+ } else if (strcmp(cmd, "jobdelete") == 0) {
+ int jobid;
+
+ if (argc != 4) {
+ fprintf(stderr, "Usage: smbcontrol <dest> printnotify"
+ " jobpause <printername> <unix-jobid>\n");
+ return False;
+ }
+
+ jobid = atoi(argv[3]);
+
+ notify_job_status_byname(
+ argv[2], jobid, JOB_STATUS_DELETING,
+ SPOOLSS_NOTIFY_MSG_UNIX_JOBID);
+
+ notify_job_status_byname(
+ argv[2], jobid, JOB_STATUS_DELETING|
+ JOB_STATUS_DELETED,
+ SPOOLSS_NOTIFY_MSG_UNIX_JOBID);
+
+ goto send;
+
+ } else if (strcmp(cmd, "printer") == 0) {
+ uint32 attribute;
+
+ if (argc != 5) {
+ fprintf(stderr, "Usage: smbcontrol <dest> printnotify "
+ "printer <printername> <comment|port|driver> "
+ "<value>\n");
+ return False;
+ }
+
+ if (strcmp(argv[3], "comment") == 0) {
+ attribute = PRINTER_NOTIFY_COMMENT;
+ } else if (strcmp(argv[3], "port") == 0) {
+ attribute = PRINTER_NOTIFY_PORT_NAME;
+ } else if (strcmp(argv[3], "driver") == 0) {
+ attribute = PRINTER_NOTIFY_DRIVER_NAME;
+ } else {
+ fprintf(stderr, "Invalid printer command '%s'\n",
+ argv[3]);
+ return False;
+ }
+
+ notify_printer_byname(argv[2], attribute, argv[4]);
+
+ goto send;
+ }
+
+ fprintf(stderr, "Invalid subcommand '%s'\n", cmd);
+ return False;
+
+send:
+ print_notify_send_messages(0);
+ return True;
+}
+
+/* Close a share */
+
+static BOOL do_closeshare(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "Usage: smbcontrol <dest> close-share "
+ "<sharename>\n");
+ return False;
+ }
+
+ return send_message(
+ pid, MSG_SMB_FORCE_TDIS, argv[1], strlen(argv[1]) + 1, False);
+}
+
+/* Force a SAM synchronisation */
+
+static BOOL do_samsync(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> samsync\n");
+ return False;
+ }
+
+ return send_message(
+ pid, MSG_SMB_SAM_SYNC, NULL, 0, False);
+}
+
+/* Force a SAM replication */
+
+static BOOL do_samrepl(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> samrepl\n");
+ return False;
+ }
+
+ return send_message(
+ pid, MSG_SMB_SAM_REPL, NULL, 0, False);
+}
+
+/* Display talloc pool usage */
+
+static BOOL do_poolusage(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> pool-usage\n");
+ return False;
+ }
+
+ /* Send a message and register our interest in a reply */
+
+ if (!send_message(pid, MSG_REQ_POOL_USAGE, NULL, 0, False))
+ return False;
+
+ message_register(MSG_POOL_USAGE, print_string_cb);
+
+ wait_replies(pid == 0);
+
+ /* No replies were received within the timeout period */
+
+ if (num_replies == 0)
+ printf("No replies received\n");
+
+ message_deregister(MSG_POOL_USAGE);
+
+ return num_replies;
+}
+
+/* Perform a dmalloc mark */
+
+static BOOL do_dmalloc_mark(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> dmalloc-mark\n");
+ return False;
+ }
+
+ return send_message(
+ pid, MSG_REQ_DMALLOC_MARK, NULL, 0, False);
+}
+
+/* Perform a dmalloc changed */
+
+static BOOL do_dmalloc_changed(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> "
+ "dmalloc-log-changed\n");
+ return False;
+ }
+
+ return send_message(
+ pid, MSG_REQ_DMALLOC_LOG_CHANGED, NULL, 0, False);
+}
+
+/* Shutdown a server process */
+
+static BOOL do_shutdown(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> shutdown\n");
+ return False;
+ }
+
+ return send_message(pid, MSG_SHUTDOWN, NULL, 0, False);
+}
+
+/* Notify a driver upgrade */
+
+static BOOL do_drvupgrade(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "Usage: smbcontrol <dest> drvupgrade "
+ "<driver-name>\n");
+ return False;
+ }
+
+ return send_message(
+ pid, MSG_DEBUG, argv[1], strlen(argv[1]) + 1, False);
+}
+
+static BOOL do_reload_config(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> reload-config\n");
+ return False;
+ }
+
+ return send_message(pid, MSG_SMB_CONF_UPDATED, NULL, 0, False);
+}
+
+/* A list of message type supported */
+
+static const struct {
+ const char *name; /* Option name */
+ BOOL (*fn)(const pid_t pid, const int argc, const char **argv);
+ const char *help; /* Short help text */
+} msg_types[] = {
+ { "debug", do_debug, "Set debuglevel" },
+ { "force-election", do_election,
+ "Force a browse election" },
+ { "ping", do_ping, "Elicit a response" },
+ { "profile", do_profile, "" },
+ { "profilelevel", do_profilelevel, "" },
+ { "debuglevel", do_debuglevel, "Display current debuglevels" },
+ { "printnotify", do_printnotify, "Send a print notify message" },
+ { "close-share", do_closeshare, "Forcibly disconnect a share" },
+ { "samsync", do_samsync, "Initiate SAM synchronisation" },
+ { "samrepl", do_samrepl, "Initiate SAM replication" },
+ { "pool-usage", do_poolusage, "Display talloc memory usage" },
+ { "dmalloc-mark", do_dmalloc_mark, "" },
+ { "dmalloc-log-changed", do_dmalloc_changed, "" },
+ { "shutdown", do_shutdown, "Shut down daemon" },
+ { "drvupgrade", do_drvupgrade, "Notify a printer driver has changed" },
+ { "reload-config", do_reload_config, "Force smbd or winbindd to reload config file"},
+ { "noop", do_noop, "Do nothing" },
+ { NULL }
+};
+
+/* Display usage information */
+
+static void usage(poptContext *pc)
+{
+ int i;
+
+ poptPrintHelp(*pc, stderr, 0);
+
+ fprintf(stderr, "\n");
+ fprintf(stderr, "<destination> is one of \"nmbd\", \"smbd\" or a "
+ "process ID\n");
+
+ fprintf(stderr, "\n");
+ fprintf(stderr, "<message-type> is one of:\n");
+
+ for (i = 0; msg_types[i].name; i++)
+ fprintf(stderr, "\t%-30s%s\n", msg_types[i].name,
+ msg_types[i].help);
+
+ fprintf(stderr, "\n");
+
+ exit(1);
+}
+
+/* Return the pid number for a string destination */
+
+static pid_t parse_dest(const char *dest)
+{
+ pid_t pid;
+
+ /* Zero is a special return value for broadcast smbd */
+
+ if (strequal(dest, "smbd"))
+ return 0;
+
+ /* Try self - useful for testing */
+
+ if (strequal(dest, "self"))
+ return sys_getpid();
+
+ /* Check for numeric pid number */
+
+ if ((pid = atoi(dest)) != 0)
+ return pid;
+
+ /* Look up other destinations in pidfile directory */
+
+ if ((pid = pidfile_pid(dest)) != 0)
+ return pid;
+
+ fprintf(stderr,"Can't find pid for destination '%s'\n", dest);
+
+ return -1;
+}
+
+/* Execute smbcontrol command */
+
+static BOOL do_command(int argc, const char **argv)
+{
+ const char *dest = argv[0], *command = argv[1];
+ pid_t pid;
+ int i;
+
+ /* Check destination */
+
+ if ((pid = parse_dest(dest)) == -1)
+ return False;
+
+ /* Check command */
+
+ for (i = 0; msg_types[i].name; i++) {
+ if (strequal(command, msg_types[i].name))
+ return msg_types[i].fn(pid, argc - 1, argv + 1);
+ }
+
+ fprintf(stderr, "smbcontrol: unknown command '%s'\n", command);
+
+ return False;
+}
+
+/* Main program */
+
+int main(int argc, const char **argv)
+{
+ poptContext pc;
+ int opt;
+
+ static struct poptOption wbinfo_options[] = {
+ { "timeout", 't', POPT_ARG_INT, &timeout, 't',
+ "Set timeout value in seconds", "TIMEOUT" },
+
+ { "configfile", 's', POPT_ARG_STRING, NULL, 's',
+ "Use alternative configuration file", "CONFIGFILE" },
+
+ POPT_TABLEEND
+ };
+
+ struct poptOption options[] = {
+ { NULL, 0, POPT_ARG_INCLUDE_TABLE, wbinfo_options, 0,
+ "Options" },
+
+ POPT_AUTOHELP
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ setup_logging(argv[0],True);
+
+ /* Parse command line arguments using popt */
+
+ pc = poptGetContext(
+ "smbcontrol", argc, (const char **)argv, options, 0);
+
+ poptSetOtherOptionHelp(pc, "[OPTION...] <destination> <message-type> "
+ "<parameters>");
+
+ if (argc == 1)
+ usage(&pc);
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch(opt) {
+ case 't': /* --timeout */
+ argc -= 2;
+ break;
+ case 's': /* --configfile */
+ pstrcpy(dyn_CONFIGFILE, poptGetOptArg(pc));
+ argc -= 2;
+ break;
+ default:
+ fprintf(stderr, "Invalid option\n");
+ poptPrintHelp(pc, stderr, 0);
+ break;
+ }
+ }
+
+ /* We should now have the remaining command line arguments in
+ argv. The argc parameter should have been decremented to the
+ correct value in the above switch statement. */
+
+ argv = (const char **)poptGetArgs(pc);
+ argc--; /* Don't forget about argv[0] */
+
+ if (argc == 1)
+ usage(&pc);
+
+ lp_load(dyn_CONFIGFILE,False,False,False);
+
+ /* Need to invert sense of return code -- samba
+ * routines mostly return True==1 for success, but
+ * shell needs 0. */
+
+ return !do_command(argc, argv);
+}
diff --git a/source/utils/smbcquotas.c b/source/utils/smbcquotas.c
new file mode 100644
index 00000000000..0bd87554209
--- /dev/null
+++ b/source/utils/smbcquotas.c
@@ -0,0 +1,546 @@
+/*
+ Unix SMB/CIFS implementation.
+ QUOTA get/set utility
+
+ Copyright (C) Andrew Tridgell 2000
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jeremy Allison 2000
+ Copyright (C) Stefan (metze) Metzmacher 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static pstring server;
+
+/* numeric is set when the user wants numeric SIDs and ACEs rather
+ than going via LSA calls to resolve them */
+static BOOL numeric;
+static BOOL verbose;
+
+enum todo_values {NOOP_QUOTA=0,FS_QUOTA,USER_QUOTA,LIST_QUOTA,SET_QUOTA};
+enum exit_values {EXIT_OK, EXIT_FAILED, EXIT_PARSE_ERROR};
+
+static struct cli_state *cli_ipc = NULL;
+static POLICY_HND pol;
+static BOOL got_policy_hnd;
+
+static struct cli_state *connect_one(const char *share);
+
+/* Open cli connection and policy handle */
+
+static BOOL cli_open_policy_hnd(void)
+{
+ /* Initialise cli LSA connection */
+
+ if (!cli_ipc) {
+ cli_ipc = connect_one("IPC$");
+ if (!cli_nt_session_open (cli_ipc, PI_LSARPC)) {
+ return False;
+ }
+ }
+
+ /* Open policy handle */
+
+ if (!got_policy_hnd) {
+
+ /* Some systems don't support SEC_RIGHTS_MAXIMUM_ALLOWED,
+ but NT sends 0x2000000 so we might as well do it too. */
+
+ if (!NT_STATUS_IS_OK(cli_lsa_open_policy(cli_ipc, cli_ipc->mem_ctx, True,
+ GENERIC_EXECUTE_ACCESS, &pol))) {
+ return False;
+ }
+
+ got_policy_hnd = True;
+ }
+
+ return True;
+}
+
+/* convert a SID to a string, either numeric or username/group */
+static void SidToString(fstring str, DOM_SID *sid, BOOL _numeric)
+{
+ char **domains = NULL;
+ char **names = NULL;
+ uint32 *types = NULL;
+
+ sid_to_string(str, sid);
+
+ if (_numeric) return;
+
+ /* Ask LSA to convert the sid to a name */
+
+ if (!cli_open_policy_hnd() ||
+ !NT_STATUS_IS_OK(cli_lsa_lookup_sids(cli_ipc, cli_ipc->mem_ctx,
+ &pol, 1, sid, &domains,
+ &names, &types)) ||
+ !domains || !domains[0] || !names || !names[0]) {
+ return;
+ }
+
+ /* Converted OK */
+
+ slprintf(str, sizeof(fstring) - 1, "%s%s%s",
+ domains[0], lp_winbind_separator(),
+ names[0]);
+
+}
+
+/* convert a string to a SID, either numeric or username/group */
+static BOOL StringToSid(DOM_SID *sid, const char *str)
+{
+ uint32 *types = NULL;
+ DOM_SID *sids = NULL;
+ BOOL result = True;
+
+ if (strncmp(str, "S-", 2) == 0) {
+ return string_to_sid(sid, str);
+ }
+
+ if (!cli_open_policy_hnd() ||
+ !NT_STATUS_IS_OK(cli_lsa_lookup_names(cli_ipc, cli_ipc->mem_ctx,
+ &pol, 1, &str, &sids,
+ &types))) {
+ result = False;
+ goto done;
+ }
+
+ sid_copy(sid, &sids[0]);
+ done:
+
+ return result;
+}
+
+#define QUOTA_GET 1
+#define QUOTA_SETLIM 2
+#define QUOTA_SETFLAGS 3
+#define QUOTA_LIST 4
+
+enum {PARSE_FLAGS,PARSE_LIM};
+
+static int parse_quota_set(pstring set_str, pstring username_str, enum SMB_QUOTA_TYPE *qtype, int *cmd, SMB_NTQUOTA_STRUCT *pqt)
+{
+ char *p = set_str,*p2;
+ int todo;
+ BOOL stop = False;
+ BOOL enable = False;
+ BOOL deny = False;
+
+ if (strnequal(set_str,"UQLIM:",6)) {
+ p += 6;
+ *qtype = SMB_USER_QUOTA_TYPE;
+ *cmd = QUOTA_SETLIM;
+ todo = PARSE_LIM;
+ if ((p2=strstr(p,":"))==NULL) {
+ return -1;
+ }
+
+ *p2 = '\0';
+ p2++;
+
+ fstrcpy(username_str,p);
+ p = p2;
+ } else if (strnequal(set_str,"FSQLIM:",7)) {
+ p +=7;
+ *qtype = SMB_USER_FS_QUOTA_TYPE;
+ *cmd = QUOTA_SETLIM;
+ todo = PARSE_LIM;
+ } else if (strnequal(set_str,"FSQFLAGS:",9)) {
+ p +=9;
+ todo = PARSE_FLAGS;
+ *qtype = SMB_USER_FS_QUOTA_TYPE;
+ *cmd = QUOTA_SETFLAGS;
+ } else {
+ return -1;
+ }
+
+ switch (todo) {
+ case PARSE_LIM:
+#if defined(HAVE_LONGLONG)
+ if (sscanf(p,"%llu/%llu",&pqt->softlim,&pqt->hardlim)!=2) {
+#else
+ if (sscanf(p,"%lu/%lu",&pqt->softlim,&pqt->hardlim)!=2) {
+#endif
+ return -1;
+ }
+
+ break;
+ case PARSE_FLAGS:
+ while (!stop) {
+
+ if ((p2=strstr(p,"/"))==NULL) {
+ stop = True;
+ } else {
+ *p2 = '\0';
+ p2++;
+ }
+
+ if (strnequal(p,"QUOTA_ENABLED",13)) {
+ enable = True;
+ } else if (strnequal(p,"DENY_DISK",9)) {
+ deny = True;
+ } else if (strnequal(p,"LOG_SOFTLIMIT",13)) {
+ pqt->qflags |= QUOTAS_LOG_THRESHOLD;
+ } else if (strnequal(p,"LOG_HARDLIMIT",13)) {
+ pqt->qflags |= QUOTAS_LOG_LIMIT;
+ } else {
+ return -1;
+ }
+
+ p=p2;
+ }
+
+ if (deny) {
+ pqt->qflags |= QUOTAS_DENY_DISK;
+ } else if (enable) {
+ pqt->qflags |= QUOTAS_ENABLED;
+ }
+
+ break;
+ }
+
+ return 0;
+}
+
+static int do_quota(struct cli_state *cli, enum SMB_QUOTA_TYPE qtype, uint16 cmd, pstring username_str, SMB_NTQUOTA_STRUCT *pqt)
+{
+ uint32 fs_attrs = 0;
+ int quota_fnum = 0;
+ SMB_NTQUOTA_LIST *qtl = NULL;
+ SMB_NTQUOTA_STRUCT qt;
+ ZERO_STRUCT(qt);
+
+ if (!cli_get_fs_attr_info(cli, &fs_attrs)) {
+ d_printf("Failed to get the filesystem attributes %s.\n",
+ cli_errstr(cli));
+ return -1;
+ }
+
+ if (!(fs_attrs & FILE_VOLUME_QUOTAS)) {
+ d_printf("Quotas are not supported by the server.\n");
+ return 0;
+ }
+
+ if (!cli_get_quota_handle(cli, &quota_fnum)) {
+ d_printf("Failed to open \\%s %s.\n",
+ FAKE_FILE_NAME_QUOTA,cli_errstr(cli));
+ return -1;
+ }
+
+ switch(qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ if (!StringToSid(&qt.sid, username_str)) {
+ d_printf("StringToSid() failed for [%s]\n",username_str);
+ return -1;
+ }
+
+ switch(cmd) {
+ case QUOTA_GET:
+ if (!cli_get_user_quota(cli, quota_fnum, &qt)) {
+ d_printf("%s cli_get_user_quota %s\n",
+ cli_errstr(cli),username_str);
+ return -1;
+ }
+ dump_ntquota(&qt,verbose,numeric,SidToString);
+ break;
+ case QUOTA_SETLIM:
+ pqt->sid = qt.sid;
+ if (!cli_set_user_quota(cli, quota_fnum, pqt)) {
+ d_printf("%s cli_set_user_quota %s\n",
+ cli_errstr(cli),username_str);
+ return -1;
+ }
+ if (!cli_get_user_quota(cli, quota_fnum, &qt)) {
+ d_printf("%s cli_get_user_quota %s\n",
+ cli_errstr(cli),username_str);
+ return -1;
+ }
+ dump_ntquota(&qt,verbose,numeric,SidToString);
+ break;
+ case QUOTA_LIST:
+ if (!cli_list_user_quota(cli, quota_fnum, &qtl)) {
+ d_printf("%s cli_set_user_quota %s\n",
+ cli_errstr(cli),username_str);
+ return -1;
+ }
+ dump_ntquota_list(&qtl,verbose,numeric,SidToString);
+ free_ntquota_list(&qtl);
+ break;
+ default:
+ d_printf("Unknown Error\n");
+ return -1;
+ }
+ break;
+ case SMB_USER_FS_QUOTA_TYPE:
+ switch(cmd) {
+ case QUOTA_GET:
+ if (!cli_get_fs_quota_info(cli, quota_fnum, &qt)) {
+ d_printf("%s cli_get_fs_quota_info\n",
+ cli_errstr(cli));
+ return -1;
+ }
+ dump_ntquota(&qt,True,numeric,NULL);
+ break;
+ case QUOTA_SETLIM:
+ if (!cli_get_fs_quota_info(cli, quota_fnum, &qt)) {
+ d_printf("%s cli_get_fs_quota_info\n",
+ cli_errstr(cli));
+ return -1;
+ }
+ qt.softlim = pqt->softlim;
+ qt.hardlim = pqt->hardlim;
+ if (!cli_set_fs_quota_info(cli, quota_fnum, &qt)) {
+ d_printf("%s cli_set_fs_quota_info\n",
+ cli_errstr(cli));
+ return -1;
+ }
+ if (!cli_get_fs_quota_info(cli, quota_fnum, &qt)) {
+ d_printf("%s cli_get_fs_quota_info\n",
+ cli_errstr(cli));
+ return -1;
+ }
+ dump_ntquota(&qt,True,numeric,NULL);
+ break;
+ case QUOTA_SETFLAGS:
+ if (!cli_get_fs_quota_info(cli, quota_fnum, &qt)) {
+ d_printf("%s cli_get_fs_quota_info\n",
+ cli_errstr(cli));
+ return -1;
+ }
+ qt.qflags = pqt->qflags;
+ if (!cli_set_fs_quota_info(cli, quota_fnum, &qt)) {
+ d_printf("%s cli_set_fs_quota_info\n",
+ cli_errstr(cli));
+ return -1;
+ }
+ if (!cli_get_fs_quota_info(cli, quota_fnum, &qt)) {
+ d_printf("%s cli_get_fs_quota_info\n",
+ cli_errstr(cli));
+ return -1;
+ }
+ dump_ntquota(&qt,True,numeric,NULL);
+ break;
+ default:
+ d_printf("Unknown Error\n");
+ return -1;
+ }
+ break;
+ default:
+ d_printf("Unknown Error\n");
+ return -1;
+ }
+
+ cli_close(cli, quota_fnum);
+
+ return 0;
+}
+
+/*****************************************************
+return a connection to a server
+*******************************************************/
+static struct cli_state *connect_one(const char *share)
+{
+ struct cli_state *c;
+ struct in_addr ip;
+ NTSTATUS nt_status;
+ zero_ip(&ip);
+
+ if (!cmdline_auth_info.got_pass) {
+ char *pass = getpass("Password: ");
+ if (pass) {
+ pstrcpy(cmdline_auth_info.password, pass);
+ cmdline_auth_info.got_pass = True;
+ }
+ }
+
+ if (NT_STATUS_IS_OK(nt_status = cli_full_connection(&c, global_myname(), server,
+ &ip, 0,
+ share, "?????",
+ cmdline_auth_info.username, lp_workgroup(),
+ cmdline_auth_info.password, 0,
+ cmdline_auth_info.signing_state, NULL))) {
+ return c;
+ } else {
+ DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status)));
+ return NULL;
+ }
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc, const char *argv[])
+{
+ char *share;
+ int opt;
+ int result;
+ int todo = 0;
+ pstring username_str = {0};
+ pstring path = {0};
+ pstring set_str = {0};
+ enum SMB_QUOTA_TYPE qtype;
+ int cmd = 0;
+ static BOOL test_args = False;
+ struct cli_state *cli;
+ BOOL fix_user = False;
+ SMB_NTQUOTA_STRUCT qt;
+ poptContext pc;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "user", 'u', POPT_ARG_STRING, NULL, 'u', "Show quotas for user", "user" },
+ { "list", 'L', POPT_ARG_NONE, NULL, 'L', "List user quotas" },
+ { "fs", 'F', POPT_ARG_NONE, NULL, 'F', "Show filesystem quotas" },
+ { "set", 'S', POPT_ARG_STRING, NULL, 'S', "Set acls\n\
+SETSTRING:\n\
+UQLIM:<username>/<softlimit>/<hardlimit> for user quotas\n\
+FSQLIM:<softlimit>/<hardlimit> for filesystem defaults\n\
+FSQFLAGS:QUOTA_ENABLED/DENY_DISK/LOG_SOFTLIMIT/LOG_HARD_LIMIT", "SETSTRING" },
+ { "numeric", 'n', POPT_ARG_NONE, &numeric, True, "Don't resolve sids or limits to names" },
+ { "verbose", 'v', POPT_ARG_NONE, &verbose, True, "be verbose" },
+ { "test-args", 't', POPT_ARG_NONE, &test_args, True, "Test arguments"},
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CREDENTIALS
+ { NULL }
+ };
+
+ ZERO_STRUCT(qt);
+
+ setlinebuf(stdout);
+
+ dbf = x_stderr;
+
+ fault_setup(NULL);
+
+ setup_logging(argv[0],True);
+
+
+ lp_load(dyn_CONFIGFILE,True,False,False);
+ load_interfaces();
+
+ pc = poptGetContext("smbcquotas", argc, argv, long_options, 0);
+
+ poptSetOtherOptionHelp(pc, "//server1/share1");
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'L':
+ if (todo != 0) {
+ d_printf("Please specify only one option of <-L|-F|-S|-u>\n");
+ exit(EXIT_PARSE_ERROR);
+ }
+ todo = LIST_QUOTA;
+ break;
+
+ case 'F':
+ if (todo != 0) {
+ d_printf("Please specify only one option of <-L|-F|-S|-u>\n");
+ exit(EXIT_PARSE_ERROR);
+ }
+ todo = FS_QUOTA;
+ break;
+
+ case 'u':
+ if (todo != 0) {
+ d_printf("Please specify only one option of <-L|-F|-S|-u>\n");
+ exit(EXIT_PARSE_ERROR);
+ }
+ pstrcpy(username_str,poptGetOptArg(pc));
+ todo = USER_QUOTA;
+ fix_user = True;
+ break;
+
+ case 'S':
+ if (todo != 0) {
+ d_printf("Please specify only one option of <-L|-F|-S|-u>\n");
+ exit(EXIT_PARSE_ERROR);
+ }
+ pstrcpy(set_str,poptGetOptArg(pc));
+ todo = SET_QUOTA;
+ break;
+ }
+ }
+
+ if (todo == 0)
+ todo = USER_QUOTA;
+
+ if (!fix_user)
+ pstrcpy(username_str,cmdline_auth_info.username);
+
+ /* Make connection to server */
+ if(!poptPeekArg(pc)) {
+ poptPrintUsage(pc, stderr, 0);
+ exit(EXIT_PARSE_ERROR);
+ }
+
+ pstrcpy(path, poptGetArg(pc));
+
+ all_string_sub(path,"/","\\",0);
+
+ pstrcpy(server,path+2);
+ share = strchr_m(server,'\\');
+ if (!share) {
+ share = strchr_m(server,'/');
+ if (!share) {
+ printf("Invalid argument: %s\n", share);
+ exit(EXIT_PARSE_ERROR);
+ }
+ }
+
+ *share = 0;
+ share++;
+
+ if (todo == SET_QUOTA) {
+ if (parse_quota_set(set_str, username_str, &qtype, &cmd, &qt)) {
+ printf("Invalid argument: -S %s\n", set_str);
+ exit(EXIT_PARSE_ERROR);
+ }
+ }
+
+ if (!test_args) {
+ cli = connect_one(share);
+ if (!cli) {
+ exit(EXIT_FAILED);
+ }
+ } else {
+ exit(EXIT_OK);
+ }
+
+
+ /* Perform requested action */
+
+ switch (todo) {
+ case FS_QUOTA:
+ result = do_quota(cli,SMB_USER_FS_QUOTA_TYPE, QUOTA_GET, username_str, NULL);
+ break;
+ case LIST_QUOTA:
+ result = do_quota(cli,SMB_USER_QUOTA_TYPE, QUOTA_LIST, username_str, NULL);
+ break;
+ case USER_QUOTA:
+ result = do_quota(cli,SMB_USER_QUOTA_TYPE, QUOTA_GET, username_str, NULL);
+ break;
+ case SET_QUOTA:
+ result = do_quota(cli, qtype, cmd, username_str, &qt);
+ break;
+ default:
+
+ result = EXIT_FAILED;
+ break;
+ }
+
+ return result;
+}
+
diff --git a/source/utils/smbfilter.c b/source/utils/smbfilter.c
new file mode 100644
index 00000000000..5d67c8fc7cf
--- /dev/null
+++ b/source/utils/smbfilter.c
@@ -0,0 +1,245 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB filter/socket plugin
+ Copyright (C) Andrew Tridgell 1999
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#define SECURITY_MASK 0
+#define SECURITY_SET 0
+
+/* this forces non-unicode */
+#define CAPABILITY_MASK 0
+#define CAPABILITY_SET 0
+
+/* and non-unicode for the client too */
+#define CLI_CAPABILITY_MASK 0
+#define CLI_CAPABILITY_SET 0
+
+static char *netbiosname;
+static char packet[BUFFER_SIZE];
+
+static void save_file(const char *fname, void *packet, size_t length)
+{
+ int fd;
+ fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ if (fd == -1) {
+ perror(fname);
+ return;
+ }
+ if (write(fd, packet, length) != length) {
+ fprintf(stderr,"Failed to write %s\n", fname);
+ return;
+ }
+ close(fd);
+ printf("Wrote %ld bytes to %s\n", (unsigned long)length, fname);
+}
+
+static void filter_reply(char *buf)
+{
+ int msg_type = CVAL(buf,0);
+ int type = CVAL(buf,smb_com);
+ unsigned x;
+
+ if (msg_type) return;
+
+ switch (type) {
+
+ case SMBnegprot:
+ /* force the security bits */
+ x = CVAL(buf, smb_vwv1);
+ x = (x | SECURITY_SET) & ~SECURITY_MASK;
+ SCVAL(buf, smb_vwv1, x);
+
+ /* force the capabilities */
+ x = IVAL(buf,smb_vwv9+1);
+ x = (x | CAPABILITY_SET) & ~CAPABILITY_MASK;
+ SIVAL(buf, smb_vwv9+1, x);
+ break;
+
+ }
+}
+
+static void filter_request(char *buf)
+{
+ int msg_type = CVAL(buf,0);
+ int type = CVAL(buf,smb_com);
+ pstring name1,name2;
+ unsigned x;
+
+ if (msg_type) {
+ /* it's a netbios special */
+ switch (msg_type) {
+ case 0x81:
+ /* session request */
+ name_extract(buf,4,name1);
+ name_extract(buf,4 + name_len(buf + 4),name2);
+ d_printf("sesion_request: %s -> %s\n",
+ name1, name2);
+ if (netbiosname) {
+ /* replace the destination netbios name */
+ name_mangle(netbiosname, buf+4, 0x20);
+ }
+ }
+ return;
+ }
+
+ /* it's an ordinary SMB request */
+ switch (type) {
+ case SMBsesssetupX:
+ /* force the client capabilities */
+ x = IVAL(buf,smb_vwv11);
+ d_printf("SMBsesssetupX cap=0x%08x\n", x);
+ d_printf("pwlen=%d/%d\n", SVAL(buf, smb_vwv7), SVAL(buf, smb_vwv8));
+ system("mv sessionsetup.dat sessionsetup1.dat");
+ save_file("sessionsetup.dat", smb_buf(buf), SVAL(buf, smb_vwv7));
+ x = (x | CLI_CAPABILITY_SET) & ~CLI_CAPABILITY_MASK;
+ SIVAL(buf, smb_vwv11, x);
+ break;
+ }
+
+}
+
+
+static void filter_child(int c, struct in_addr dest_ip)
+{
+ int s;
+
+ /* we have a connection from a new client, now connect to the server */
+ s = open_socket_out(SOCK_STREAM, &dest_ip, 445, LONG_CONNECT_TIMEOUT);
+
+ if (s == -1) {
+ d_printf("Unable to connect to %s\n", inet_ntoa(dest_ip));
+ exit(1);
+ }
+
+ while (c != -1 || s != -1) {
+ fd_set fds;
+ int num;
+
+ FD_ZERO(&fds);
+ if (s != -1) FD_SET(s, &fds);
+ if (c != -1) FD_SET(c, &fds);
+
+ num = sys_select_intr(MAX(s+1, c+1),&fds,NULL,NULL,NULL);
+ if (num <= 0) continue;
+
+ if (c != -1 && FD_ISSET(c, &fds)) {
+ if (!receive_smb(c, packet, 0)) {
+ d_printf("client closed connection\n");
+ exit(0);
+ }
+ filter_request(packet);
+ if (!send_smb(s, packet)) {
+ d_printf("server is dead\n");
+ exit(1);
+ }
+ }
+ if (s != -1 && FD_ISSET(s, &fds)) {
+ if (!receive_smb(s, packet, 0)) {
+ d_printf("server closed connection\n");
+ exit(0);
+ }
+ filter_reply(packet);
+ if (!send_smb(c, packet)) {
+ d_printf("client is dead\n");
+ exit(1);
+ }
+ }
+ }
+ d_printf("Connection closed\n");
+ exit(0);
+}
+
+
+static void start_filter(char *desthost)
+{
+ int s, c;
+ struct in_addr dest_ip;
+
+ CatchChild();
+
+ /* start listening on port 445 locally */
+ s = open_socket_in(SOCK_STREAM, 445, 0, 0, True);
+
+ if (s == -1) {
+ d_printf("bind failed\n");
+ exit(1);
+ }
+
+ if (listen(s, 5) == -1) {
+ d_printf("listen failed\n");
+ }
+
+ if (!resolve_name(desthost, &dest_ip, 0x20)) {
+ d_printf("Unable to resolve host %s\n", desthost);
+ exit(1);
+ }
+
+ while (1) {
+ fd_set fds;
+ int num;
+ struct sockaddr addr;
+ socklen_t in_addrlen = sizeof(addr);
+
+ FD_ZERO(&fds);
+ FD_SET(s, &fds);
+
+ num = sys_select_intr(s+1,&fds,NULL,NULL,NULL);
+ if (num > 0) {
+ c = accept(s, &addr, &in_addrlen);
+ if (c != -1) {
+ if (fork() == 0) {
+ close(s);
+ filter_child(c, dest_ip);
+ exit(0);
+ } else {
+ close(c);
+ }
+ }
+ }
+ }
+}
+
+
+int main(int argc, char *argv[])
+{
+ char *desthost;
+ pstring configfile;
+
+ setup_logging(argv[0],True);
+
+ pstrcpy(configfile,dyn_CONFIGFILE);
+
+ if (argc < 2) {
+ fprintf(stderr,"smbfilter <desthost> <netbiosname>\n");
+ exit(1);
+ }
+
+ desthost = argv[1];
+ if (argc > 2) {
+ netbiosname = argv[2];
+ }
+
+ if (!lp_load(configfile,True,False,False)) {
+ d_printf("Unable to load config file\n");
+ }
+
+ start_filter(desthost);
+ return 0;
+}
diff --git a/source/utils/smbget.c b/source/utils/smbget.c
new file mode 100644
index 00000000000..64630bba8fd
--- /dev/null
+++ b/source/utils/smbget.c
@@ -0,0 +1,589 @@
+/*
+ smbget: a wget-like utility with support for recursive downloading and
+ smb:// urls
+ Copyright (C) 2003-2004 Jelmer Vernooij <jelmer@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "includes.h"
+#include "libsmbclient.h"
+
+#if _FILE_OFFSET_BITS==64
+#define OFF_T_FORMAT "%lld"
+#else
+#define OFF_T_FORMAT "%ld"
+#endif
+
+int columns = 0;
+
+time_t total_start_time = 0;
+off_t total_bytes = 0;
+
+#define SMB_MAXPATHLEN MAXPATHLEN
+
+/* Number of bytes to read when checking whether local and remote file are really the same file */
+#define RESUME_CHECK_SIZE 512
+#define RESUME_DOWNLOAD_OFFSET 1024
+#define RESUME_CHECK_OFFSET RESUME_DOWNLOAD_OFFSET+RESUME_CHECK_SIZE
+/* Number of bytes to read at once */
+#define SMB_DEFAULT_BLOCKSIZE 64000
+
+const char *username = NULL, *password = NULL, *workgroup = NULL;
+int nonprompt = 0, quiet = 0, dots = 0, keep_permissions = 0, verbose = 0, send_stdout = 0;
+int blocksize = SMB_DEFAULT_BLOCKSIZE;
+
+int smb_download_file(const char *base, const char *name, int recursive, int resume, char *outfile);
+
+int get_num_cols(void)
+{
+#ifdef TIOCGWINSZ
+ struct winsize ws;
+ if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0) {
+ return 0;
+ }
+ return ws.ws_col;
+#else
+#warning No support for TIOCGWINSZ
+ char *cols = getenv("COLUMNS");
+ if(!cols) return 0;
+ return atoi(cols);
+#endif
+}
+
+void change_columns(int sig)
+{
+ columns = get_num_cols();
+}
+
+void human_readable(off_t s, char *buffer, int l)
+{
+ if(s > 1024 * 1024 * 1024) snprintf(buffer, l, "%.2fGb", 1.0 * s / (1024 * 1024 * 1024));
+ else if(s > 1024 * 1024) snprintf(buffer, l, "%.2fMb", 1.0 * s / (1024 * 1024));
+ else if(s > 1024) snprintf(buffer, l, "%.2fkb", 1.0 * s / 1024);
+ else snprintf(buffer, l, OFF_T_FORMAT"b", s);
+}
+
+void get_auth_data(const char *srv, const char *shr, char *wg, int wglen, char *un, int unlen, char *pw, int pwlen)
+{
+ static char hasasked = 0;
+ char *wgtmp, *usertmp;
+ char tmp[128];
+
+ if(hasasked) return;
+ hasasked = 1;
+
+ if(!nonprompt && !username) {
+ printf("Username for %s at %s [guest] ", shr, srv);
+ fgets(tmp, sizeof(tmp), stdin);
+ if(tmp[strlen(tmp)-1] == '\n')tmp[strlen(tmp)-1] = '\0';
+ strncpy(un, tmp, unlen-1);
+ } else if(username) strncpy(un, username, unlen-1);
+
+ if(!nonprompt && !password) {
+ char *prompt, *pass;
+ asprintf(&prompt, "Password for %s at %s: ", shr, srv);
+ pass = getpass(prompt);
+ free(prompt);
+ strncpy(pw, pass, pwlen-1);
+ } else if(password) strncpy(pw, password, pwlen-1);
+
+ if(workgroup)strncpy(wg, workgroup, wglen-1);
+
+ wgtmp = strndup(wg, wglen);
+ usertmp = strndup(un, unlen);
+ if(!quiet)printf("Using workgroup %s, %s%s\n", wgtmp, *usertmp?"user ":"guest user", usertmp);
+ free(wgtmp); free(usertmp);
+}
+
+int smb_download_dir(const char *base, const char *name, int resume)
+{
+ char path[SMB_MAXPATHLEN];
+ int dirhandle;
+ struct smbc_dirent *dirent;
+ const char *relname = name;
+ char *tmpname;
+ struct stat remotestat;
+ snprintf(path, SMB_MAXPATHLEN-1, "%s%s%s", base, (base[0] && name[0] && name[0] != '/' && base[strlen(base)-1] != '/')?"/":"", name);
+
+ /* List files in directory and call smb_download_file on them */
+ dirhandle = smbc_opendir(path);
+ if(dirhandle < 1) {
+ if(errno == ENOTDIR) return smb_download_file(base, name, 1, resume, NULL);
+ fprintf(stderr, "Can't open directory %s: %s\n", path, strerror(errno));
+ return 0;
+ }
+
+ while(*relname == '/')relname++;
+ mkdir(relname, 0755);
+
+ tmpname = strdup(name);
+
+ while((dirent = smbc_readdir(dirhandle))) {
+ char *newname;
+ if(!strcmp(dirent->name, ".") || !strcmp(dirent->name, ".."))continue;
+ asprintf(&newname, "%s/%s", tmpname, dirent->name);
+ switch(dirent->smbc_type) {
+ case SMBC_DIR:
+ smb_download_dir(base, newname, resume);
+ break;
+
+ case SMBC_WORKGROUP:
+ smb_download_dir("smb://", dirent->name, resume);
+ break;
+
+ case SMBC_SERVER:
+ smb_download_dir("smb://", dirent->name, resume);
+ break;
+
+ case SMBC_FILE:
+ smb_download_file(base, newname, 1, resume, NULL);
+ break;
+
+ case SMBC_FILE_SHARE:
+ smb_download_dir(base, newname, resume);
+ break;
+
+ case SMBC_PRINTER_SHARE:
+ if(!quiet)printf("Ignoring printer share %s\n", dirent->name);
+ break;
+
+ case SMBC_COMMS_SHARE:
+ if(!quiet)printf("Ignoring comms share %s\n", dirent->name);
+ break;
+
+ case SMBC_IPC_SHARE:
+ if(!quiet)printf("Ignoring ipc$ share %s\n", dirent->name);
+ break;
+
+ default:
+ fprintf(stderr, "Ignoring file '%s' of type '%d'\n", newname, dirent->smbc_type);
+ break;
+ }
+ free(newname);
+ }
+ free(tmpname);
+
+ if(keep_permissions) {
+ if(smbc_fstat(dirhandle, &remotestat) < 0) {
+ fprintf(stderr, "Unable to get stats on %s on remote server\n", path);
+ smbc_closedir(dirhandle);
+ return 0;
+ }
+
+ if(chmod(relname, remotestat.st_mode) < 0) {
+ fprintf(stderr, "Unable to change mode of local dir %s to %o\n", relname, remotestat.st_mode);
+ smbc_closedir(dirhandle);
+ return 0;
+ }
+ }
+
+ smbc_closedir(dirhandle);
+ return 1;
+}
+
+char *print_time(long t)
+{
+ static char buffer[100];
+ int secs, mins, hours;
+ if(t < -1) {
+ strncpy(buffer, "Unknown", sizeof(buffer));
+ return buffer;
+ }
+
+ secs = (int)t % 60;
+ mins = (int)t / 60 % 60;
+ hours = (int)t / (60 * 60);
+ snprintf(buffer, sizeof(buffer)-1, "%02d:%02d:%02d", hours, mins, secs);
+ return buffer;
+}
+
+void print_progress(const char *name, time_t start, time_t now, off_t start_pos, off_t pos, off_t total)
+{
+ double avg = 0.0;
+ long eta = -1;
+ double prcnt = 0.0;
+ char hpos[20], htotal[20], havg[20];
+ char *status, *filename;
+ int len;
+ if(now - start)avg = 1.0 * (pos - start_pos) / (now - start);
+ eta = (total - pos) / avg;
+ if(total)prcnt = 100.0 * pos / total;
+
+ human_readable(pos, hpos, sizeof(hpos));
+ human_readable(total, htotal, sizeof(htotal));
+ human_readable(avg, havg, sizeof(havg));
+
+ len = asprintf(&status, "%s of %s (%.2f%%) at %s/s ETA: %s", hpos, htotal, prcnt, havg, print_time(eta));
+
+ if(columns) {
+ int required = strlen(name), available = columns - len - strlen("[] ");
+ if(required > available) asprintf(&filename, "...%s", name + required - available + 3);
+ else filename = strndup(name, available);
+ } else filename = strdup(name);
+
+ fprintf(stderr, "\r[%s] %s", filename, status);
+
+ free(filename); free(status);
+}
+
+int smb_download_file(const char *base, const char *name, int recursive, int resume, char *outfile) {
+ int remotehandle, localhandle;
+ time_t start_time = time(NULL);
+ const char *newpath;
+ char path[SMB_MAXPATHLEN];
+ char checkbuf[2][RESUME_CHECK_SIZE];
+ char *readbuf = NULL;
+ off_t offset_download = 0, offset_check = 0, curpos = 0, start_offset = 0;
+ struct stat localstat, remotestat;
+
+ snprintf(path, SMB_MAXPATHLEN-1, "%s%s%s", base, (*base && *name && name[0] != '/' && base[strlen(base)-1] != '/')?"/":"", name);
+
+ remotehandle = smbc_open(path, O_RDONLY, 0755);
+
+ if(remotehandle < 0) {
+ switch(errno) {
+ case EISDIR:
+ if(!recursive) {
+ fprintf(stderr, "%s is a directory. Specify -R to download recursively\n", path);
+ return 0;
+ }
+ smb_download_dir(base, name, resume);
+ return 0;
+
+ case ENOENT:
+ fprintf(stderr, "%s can't be found on the remote server\n", path);
+ return 0;
+
+ case ENOMEM:
+ fprintf(stderr, "Not enough memory\n");
+ exit(1);
+ return 0;
+
+ case ENODEV:
+ fprintf(stderr, "The share name used in %s does not exist\n", path);
+ return 0;
+
+ case EACCES:
+ fprintf(stderr, "You don't have enough permissions to access %s\n", path);
+ return 0;
+
+ default:
+ perror("smbc_open");
+ return 0;
+ }
+ }
+
+ if(smbc_fstat(remotehandle, &remotestat) < 0) {
+ fprintf(stderr, "Can't stat %s: %s\n", path, strerror(errno));
+ return 0;
+ }
+
+ if(outfile) newpath = outfile;
+ else if(!name[0]) {
+ newpath = strrchr(base, '/');
+ if(newpath)newpath++; else newpath = base;
+ } else newpath = name;
+
+ if(newpath[0] == '/')newpath++;
+
+ /* Open local file and, if necessary, resume */
+ if(!send_stdout) {
+ localhandle = open(newpath, O_CREAT | O_NONBLOCK | O_RDWR | (!resume?O_EXCL:0), 0755);
+ if(localhandle < 0) {
+ fprintf(stderr, "Can't open %s: %s\n", newpath, strerror(errno));
+ smbc_close(remotehandle);
+ return 0;
+ }
+
+ fstat(localhandle, &localstat);
+
+ start_offset = localstat.st_size;
+
+ if(localstat.st_size && localstat.st_size == remotestat.st_size) {
+ if(verbose)fprintf(stderr, "%s is already downloaded completely.\n", path);
+ else if(!quiet)fprintf(stderr, "%s\n", path);
+ smbc_close(remotehandle);
+ close(localhandle);
+ return 1;
+ }
+
+ if(localstat.st_size > RESUME_CHECK_OFFSET && remotestat.st_size > RESUME_CHECK_OFFSET) {
+ offset_download = localstat.st_size - RESUME_DOWNLOAD_OFFSET;
+ offset_check = localstat.st_size - RESUME_CHECK_OFFSET;
+ if(verbose)printf("Trying to start resume of %s at "OFF_T_FORMAT"\n"
+ "At the moment "OFF_T_FORMAT" of "OFF_T_FORMAT" bytes have been retrieved\n", newpath, offset_check,
+ localstat.st_size, remotestat.st_size);
+ }
+
+ if(offset_check) {
+ off_t off1, off2;
+ /* First, check all bytes from offset_check to offset_download */
+ off1 = lseek(localhandle, offset_check, SEEK_SET);
+ if(off1 < 0) {
+ fprintf(stderr, "Can't seek to "OFF_T_FORMAT" in local file %s\n", offset_check, newpath);
+ smbc_close(remotehandle); close(localhandle);
+ return 0;
+ }
+
+ off2 = smbc_lseek(remotehandle, offset_check, SEEK_SET);
+ if(off2 < 0) {
+ fprintf(stderr, "Can't seek to "OFF_T_FORMAT" in remote file %s\n", offset_check, newpath);
+ smbc_close(remotehandle); close(localhandle);
+ return 0;
+ }
+
+ if(off1 != off2) {
+ fprintf(stderr, "Offset in local and remote files is different (local: "OFF_T_FORMAT", remote: "OFF_T_FORMAT")\n", off1, off2);
+ return 0;
+ }
+
+ if(smbc_read(remotehandle, checkbuf[0], RESUME_CHECK_SIZE) != RESUME_CHECK_SIZE) {
+ fprintf(stderr, "Can't read %d bytes from remote file %s\n", RESUME_CHECK_SIZE, path);
+ smbc_close(remotehandle); close(localhandle);
+ return 0;
+ }
+
+ if(read(localhandle, checkbuf[1], RESUME_CHECK_SIZE) != RESUME_CHECK_SIZE) {
+ fprintf(stderr, "Can't read %d bytes from local file %s\n", RESUME_CHECK_SIZE, name);
+ smbc_close(remotehandle); close(localhandle);
+ return 0;
+ }
+
+ if(memcmp(checkbuf[0], checkbuf[1], RESUME_CHECK_SIZE) == 0) {
+ if(verbose)printf("Current local and remote file appear to be the same. Starting download from offset "OFF_T_FORMAT"\n", offset_download);
+ } else {
+ fprintf(stderr, "Local and remote file appear to be different, not doing resume for %s\n", path);
+ smbc_close(remotehandle); close(localhandle);
+ return 0;
+ }
+ }
+ } else {
+ localhandle = STDOUT_FILENO;
+ start_offset = 0;
+ offset_download = 0;
+ offset_check = 0;
+ }
+
+ readbuf = malloc(blocksize);
+
+ /* Now, download all bytes from offset_download to the end */
+ for(curpos = offset_download; curpos < remotestat.st_size; curpos+=blocksize) {
+ ssize_t bytesread = smbc_read(remotehandle, readbuf, blocksize);
+ if(bytesread < 0) {
+ fprintf(stderr, "Can't read %d bytes at offset "OFF_T_FORMAT", file %s\n", blocksize, curpos, path);
+ smbc_close(remotehandle);
+ if (localhandle != STDOUT_FILENO) close(localhandle);
+ free(readbuf);
+ return 0;
+ }
+
+ total_bytes += bytesread;
+
+ if(write(localhandle, readbuf, bytesread) < 0) {
+ fprintf(stderr, "Can't write %d bytes to local file %s at offset "OFF_T_FORMAT"\n", bytesread, path, curpos);
+ free(readbuf);
+ smbc_close(remotehandle);
+ if (localhandle != STDOUT_FILENO) close(localhandle);
+ return 0;
+ }
+
+ if(dots)fputc('.', stderr);
+ else if(!quiet) {
+ print_progress(newpath, start_time, time(NULL), start_offset, curpos, remotestat.st_size);
+ }
+ }
+
+ free(readbuf);
+
+ if(dots){
+ fputc('\n', stderr);
+ printf("%s downloaded\n", path);
+ } else if(!quiet) {
+ int i;
+ fprintf(stderr, "\r%s", path);
+ if(columns) {
+ for(i = strlen(path); i < columns; i++) {
+ fputc(' ', stderr);
+ }
+ }
+ fputc('\n', stderr);
+ }
+
+ if(keep_permissions && !send_stdout) {
+ if(fchmod(localhandle, remotestat.st_mode) < 0) {
+ fprintf(stderr, "Unable to change mode of local file %s to %o\n", path, remotestat.st_mode);
+ smbc_close(remotehandle);
+ close(localhandle);
+ return 0;
+ }
+ }
+
+ smbc_close(remotehandle);
+ if (localhandle != STDOUT_FILENO) close(localhandle);
+ return 1;
+}
+
+void clean_exit(void)
+{
+ char bs[100];
+ human_readable(total_bytes, bs, sizeof(bs));
+ if(!quiet)fprintf(stderr, "Downloaded %s in %lu seconds\n", bs, time(NULL) - total_start_time);
+ exit(0);
+}
+
+void signal_quit(int v)
+{
+ clean_exit();
+}
+
+int readrcfile(const char *name, const struct poptOption long_options[])
+{
+ FILE *fd = fopen(name, "r");
+ int lineno = 0, i;
+ char var[101], val[101];
+ char found;
+ int *intdata; char **stringdata;
+ if(!fd) {
+ fprintf(stderr, "Can't open RC file %s\n", name);
+ return 1;
+ }
+
+ while(!feof(fd)) {
+ lineno++;
+ if(fscanf(fd, "%100s %100s\n", var, val) < 2) {
+ fprintf(stderr, "Can't parse line %d of %s, ignoring.\n", lineno, name);
+ continue;
+ }
+
+ found = 0;
+
+ for(i = 0; long_options[i].shortName; i++) {
+ if(!long_options[i].longName)continue;
+ if(strcmp(long_options[i].longName, var)) continue;
+ if(!long_options[i].arg)continue;
+
+ switch(long_options[i].argInfo) {
+ case POPT_ARG_NONE:
+ intdata = (int *)long_options[i].arg;
+ if(!strcmp(val, "on")) *intdata = 1;
+ else if(!strcmp(val, "off")) *intdata = 0;
+ else fprintf(stderr, "Illegal value %s for %s at line %d in %s\n", val, var, lineno, name);
+ break;
+ case POPT_ARG_INT:
+ intdata = (int *)long_options[i].arg;
+ *intdata = atoi(val);
+ break;
+ case POPT_ARG_STRING:
+ stringdata = (char **)long_options[i].arg;
+ *stringdata = strdup(val);
+ break;
+ default:
+ fprintf(stderr, "Invalid variable %s at line %d in %s\n", var, lineno, name);
+ break;
+ }
+
+ found = 1;
+ }
+ if(!found) {
+ fprintf(stderr, "Invalid variable %s at line %d in %s\n", var, lineno, name);
+ }
+ }
+
+ fclose(fd);
+ return 0;
+}
+
+int main(int argc, const char **argv)
+{
+ int resume = 0, recursive = 0;
+ int c = 0;
+ int debuglevel = 0;
+ const char *file = NULL;
+ char *rcfile = NULL;
+ char *outputfile = NULL;
+ struct poptOption long_options[] = {
+ {"guest", 'a', POPT_ARG_NONE, NULL, 'a', "Work as user guest" },
+ {"resume", 'r', POPT_ARG_NONE, &resume, 0, "Automatically resume aborted files" },
+ {"recursive", 'R', POPT_ARG_NONE, &recursive, 0, "Recursively download files" },
+ {"username", 'u', POPT_ARG_STRING, &username, 'u', "Username to use" },
+ {"password", 'p', POPT_ARG_STRING, &password, 'p', "Password to use" },
+ {"workgroup", 'w', POPT_ARG_STRING, &workgroup, 'w', "Workgroup to use (optional)" },
+ {"nonprompt", 'n', POPT_ARG_NONE, &nonprompt, 'n', "Don't ask anything (non-interactive)" },
+ {"debuglevel", 'd', POPT_ARG_INT, &debuglevel, 'd', "Debuglevel to use" },
+ {"outputfile", 'o', POPT_ARG_STRING, &outputfile, 'o', "Write downloaded data to specified file" },
+ {"stdout", 'O', POPT_ARG_NONE, &send_stdout, 'O', "Write data to stdout" },
+ {"dots", 'D', POPT_ARG_NONE, &dots, 'D', "Show dots as progress indication" },
+ {"quiet", 'q', POPT_ARG_NONE, &quiet, 'q', "Be quiet" },
+ {"verbose", 'v', POPT_ARG_NONE, &verbose, 'v', "Be verbose" },
+ {"keep-permissions", 'P', POPT_ARG_NONE, &keep_permissions, 'P', "Keep permissions" },
+ {"blocksize", 'b', POPT_ARG_INT, &blocksize, 'b', "Change number of bytes in a block"},
+ {"rcfile", 'f', POPT_ARG_STRING, NULL, 0, "Use specified rc file"},
+ POPT_AUTOHELP
+ POPT_TABLEEND
+ };
+ poptContext pc;
+
+ /* only read rcfile if it exists */
+ asprintf(&rcfile, "%s/.smbgetrc", getenv("HOME"));
+ if(access(rcfile, F_OK) == 0) readrcfile(rcfile, long_options);
+ free(rcfile);
+
+#ifdef SIGWINCH
+ signal(SIGWINCH, change_columns);
+#endif
+ signal(SIGINT, signal_quit);
+ signal(SIGTERM, signal_quit);
+
+ pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+
+ while((c = poptGetNextOpt(pc)) >= 0) {
+ switch(c) {
+ case 'f':
+ readrcfile(poptGetOptArg(pc), long_options);
+ break;
+ case 'a':
+ username = ""; password = "";
+ break;
+ }
+ }
+
+ if((send_stdout || outputfile) && recursive) {
+ fprintf(stderr, "The -o or -O and -R options can not be used together.\n");
+ return 1;
+ }
+
+ if(outputfile && send_stdout) {
+ fprintf(stderr, "The -o and -O options cannot be used together.\n");
+ return 1;
+ }
+
+ if(smbc_init(get_auth_data, debuglevel) < 0) {
+ fprintf(stderr, "Unable to initialize libsmbclient\n");
+ return 1;
+ }
+
+ columns = get_num_cols();
+
+ total_start_time = time(NULL);
+
+ while((file = poptGetArg(pc))) {
+ if(!recursive) return smb_download_file(file, "", recursive, resume, outputfile);
+ else return smb_download_dir(file, "", resume);
+ }
+
+ clean_exit();
+
+ return 0;
+}
diff --git a/source/utils/smbpasswd.c b/source/utils/smbpasswd.c
new file mode 100644
index 00000000000..0476a2e39c0
--- /dev/null
+++ b/source/utils/smbpasswd.c
@@ -0,0 +1,614 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Copyright (C) Jeremy Allison 1995-1998
+ * Copyright (C) Tim Potter 2001
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 675
+ * Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "includes.h"
+
+extern BOOL AllowDebugChange;
+
+/*
+ * Next two lines needed for SunOS and don't
+ * hurt anything else...
+ */
+extern char *optarg;
+extern int optind;
+
+/* forced running in root-mode */
+static BOOL got_pass = False, got_username = False;
+static BOOL stdin_passwd_get = False;
+static fstring user_name, user_password;
+static char *new_passwd = NULL;
+static const char *remote_machine = NULL;
+
+static fstring ldap_secret;
+
+
+/*********************************************************
+ Print command usage on stderr and die.
+**********************************************************/
+static void usage(void)
+{
+ printf("When run by root:\n");
+ printf(" smbpasswd [options] [username] [password]\n");
+ printf("otherwise:\n");
+ printf(" smbpasswd [options] [password]\n\n");
+
+ printf("options:\n");
+ printf(" -L local mode (must be first option)\n");
+ printf(" -h print this usage message\n");
+ printf(" -s use stdin for password prompt\n");
+ printf(" -c smb.conf file Use the given path to the smb.conf file\n");
+ printf(" -D LEVEL debug level\n");
+ printf(" -r MACHINE remote machine\n");
+ printf(" -U USER remote username\n");
+
+ printf("extra options when run by root or in local mode:\n");
+ printf(" -a add user\n");
+ printf(" -d disable user\n");
+ printf(" -e enable user\n");
+ printf(" -i interdomain trust account\n");
+ printf(" -m machine trust account\n");
+ printf(" -n set no password\n");
+ printf(" -w PASSWORD ldap admin password\n");
+ printf(" -x delete user\n");
+ printf(" -R ORDER name resolve order\n");
+
+ exit(1);
+}
+
+static void set_line_buffering(FILE *f)
+{
+ setvbuf(f, NULL, _IOLBF, 0);
+}
+
+/*******************************************************************
+ Process command line options
+ ******************************************************************/
+static int process_options(int argc, char **argv, int local_flags)
+{
+ int ch;
+ pstring configfile;
+ pstrcpy(configfile, dyn_CONFIGFILE);
+
+ local_flags |= LOCAL_SET_PASSWORD;
+
+ ZERO_STRUCT(user_name);
+ ZERO_STRUCT(user_password);
+
+ user_name[0] = '\0';
+
+ while ((ch = getopt(argc, argv, "c:axdehminjr:sw:R:D:U:L")) != EOF) {
+ switch(ch) {
+ case 'L':
+ local_flags |= LOCAL_AM_ROOT;
+ break;
+ case 'c':
+ pstrcpy(configfile,optarg);
+ break;
+ case 'a':
+ local_flags |= LOCAL_ADD_USER;
+ break;
+ case 'x':
+ local_flags |= LOCAL_DELETE_USER;
+ local_flags &= ~LOCAL_SET_PASSWORD;
+ break;
+ case 'd':
+ local_flags |= LOCAL_DISABLE_USER;
+ local_flags &= ~LOCAL_SET_PASSWORD;
+ break;
+ case 'e':
+ local_flags |= LOCAL_ENABLE_USER;
+ local_flags &= ~LOCAL_SET_PASSWORD;
+ break;
+ case 'm':
+ local_flags |= LOCAL_TRUST_ACCOUNT;
+ break;
+ case 'i':
+ local_flags |= LOCAL_INTERDOM_ACCOUNT;
+ break;
+ case 'j':
+ d_printf("See 'net join' for this functionality\n");
+ exit(1);
+ break;
+ case 'n':
+ local_flags |= LOCAL_SET_NO_PASSWORD;
+ local_flags &= ~LOCAL_SET_PASSWORD;
+ new_passwd = smb_xstrdup("NO PASSWORD");
+ break;
+ case 'r':
+ remote_machine = optarg;
+ break;
+ case 's':
+ set_line_buffering(stdin);
+ set_line_buffering(stdout);
+ set_line_buffering(stderr);
+ stdin_passwd_get = True;
+ break;
+ case 'w':
+ local_flags |= LOCAL_SET_LDAP_ADMIN_PW;
+ fstrcpy(ldap_secret, optarg);
+ break;
+ case 'R':
+ lp_set_name_resolve_order(optarg);
+ break;
+ case 'D':
+ DEBUGLEVEL = atoi(optarg);
+ break;
+ case 'U': {
+ char *lp;
+
+ got_username = True;
+ fstrcpy(user_name, optarg);
+
+ if ((lp = strchr(user_name, '%'))) {
+ *lp = 0;
+ fstrcpy(user_password, lp + 1);
+ got_pass = True;
+ memset(strchr_m(optarg, '%') + 1, 'X',
+ strlen(user_password));
+ }
+
+ break;
+ }
+ case 'h':
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ switch(argc) {
+ case 0:
+ if (!got_username)
+ fstrcpy(user_name, "");
+ break;
+ case 1:
+ if (!(local_flags & LOCAL_AM_ROOT)) {
+ new_passwd = argv[0];
+ } else {
+ if (got_username) {
+ usage();
+ } else {
+ fstrcpy(user_name, argv[0]);
+ }
+ }
+ break;
+ case 2:
+ if (!(local_flags & LOCAL_AM_ROOT) || got_username || got_pass) {
+ usage();
+ }
+
+ fstrcpy(user_name, argv[0]);
+ new_passwd = smb_xstrdup(argv[1]);
+ break;
+ default:
+ usage();
+ }
+
+ if (!lp_load(configfile,True,False,False)) {
+ fprintf(stderr, "Can't load %s - run testparm to debug it\n",
+ dyn_CONFIGFILE);
+ exit(1);
+ }
+
+ return local_flags;
+}
+
+/*************************************************************
+ Utility function to prompt for passwords from stdin. Each
+ password entered must end with a newline.
+*************************************************************/
+static char *stdin_new_passwd(void)
+{
+ static fstring new_pw;
+ size_t len;
+
+ ZERO_ARRAY(new_pw);
+
+ /*
+ * if no error is reported from fgets() and string at least contains
+ * the newline that ends the password, then replace the newline with
+ * a null terminator.
+ */
+ if ( fgets(new_pw, sizeof(new_pw), stdin) != NULL) {
+ if ((len = strlen(new_pw)) > 0) {
+ if(new_pw[len-1] == '\n')
+ new_pw[len - 1] = 0;
+ }
+ }
+ return(new_pw);
+}
+
+
+/*************************************************************
+ Utility function to get passwords via tty or stdin
+ Used if the '-s' option is set to silently get passwords
+ to enable scripting.
+*************************************************************/
+static char *get_pass( const char *prompt, BOOL stdin_get)
+{
+ char *p;
+ if (stdin_get) {
+ p = stdin_new_passwd();
+ } else {
+ p = getpass(prompt);
+ }
+ return smb_xstrdup(p);
+}
+
+/*************************************************************
+ Utility function to prompt for new password.
+*************************************************************/
+static char *prompt_for_new_password(BOOL stdin_get)
+{
+ char *p;
+ fstring new_pw;
+
+ ZERO_ARRAY(new_pw);
+
+ p = get_pass("New SMB password:", stdin_get);
+
+ fstrcpy(new_pw, p);
+ SAFE_FREE(p);
+
+ p = get_pass("Retype new SMB password:", stdin_get);
+
+ if (strcmp(p, new_pw)) {
+ fprintf(stderr, "Mismatch - password unchanged.\n");
+ ZERO_ARRAY(new_pw);
+ SAFE_FREE(p);
+ return NULL;
+ }
+
+ return p;
+}
+
+
+/*************************************************************
+ Change a password either locally or remotely.
+*************************************************************/
+
+static BOOL password_change(const char *remote_mach, char *username,
+ char *old_passwd, char *new_pw, int local_flags)
+{
+ BOOL ret;
+ pstring err_str;
+ pstring msg_str;
+
+ if (remote_mach != NULL) {
+ if (local_flags & (LOCAL_ADD_USER|LOCAL_DELETE_USER|LOCAL_DISABLE_USER|LOCAL_ENABLE_USER|
+ LOCAL_TRUST_ACCOUNT|LOCAL_SET_NO_PASSWORD)) {
+ /* these things can't be done remotely yet */
+ return False;
+ }
+ ret = remote_password_change(remote_mach, username,
+ old_passwd, new_pw, err_str, sizeof(err_str));
+ if(*err_str)
+ fprintf(stderr, err_str);
+ return ret;
+ }
+
+ ret = local_password_change(username, local_flags, new_pw,
+ err_str, sizeof(err_str), msg_str, sizeof(msg_str));
+
+ if(*msg_str)
+ printf(msg_str);
+ if(*err_str)
+ fprintf(stderr, err_str);
+
+ return ret;
+}
+
+/*******************************************************************
+ Store the LDAP admin password in secrets.tdb
+ ******************************************************************/
+static BOOL store_ldap_admin_pw (char* pw)
+{
+ if (!pw)
+ return False;
+
+ if (!secrets_init())
+ return False;
+
+ return secrets_store_ldap_pw(lp_ldap_admin_dn(), pw);
+}
+
+
+/*************************************************************
+ Handle password changing for root.
+*************************************************************/
+
+static int process_root(int local_flags)
+{
+ struct passwd *pwd;
+ int result = 0;
+ char *old_passwd = NULL;
+
+ if (local_flags & LOCAL_SET_LDAP_ADMIN_PW) {
+ printf("Setting stored password for \"%s\" in secrets.tdb\n",
+ lp_ldap_admin_dn());
+ if (!store_ldap_admin_pw(ldap_secret))
+ DEBUG(0,("ERROR: Failed to store the ldap admin password!\n"));
+ goto done;
+ }
+
+ /* Ensure passdb startup(). */
+ if(!initialize_password_db(False)) {
+ DEBUG(0, ("Failed to open passdb!\n"));
+ exit(1);
+ }
+
+ /* Ensure we have a SAM sid. */
+ get_global_sam_sid();
+
+ /*
+ * Ensure both add/delete user are not set
+ * Ensure add/delete user and either remote machine or join domain are
+ * not both set.
+ */
+ if(((local_flags & (LOCAL_ADD_USER|LOCAL_DELETE_USER)) == (LOCAL_ADD_USER|LOCAL_DELETE_USER)) ||
+ ((local_flags & (LOCAL_ADD_USER|LOCAL_DELETE_USER)) &&
+ (remote_machine != NULL))) {
+ usage();
+ }
+
+ /* Only load interfaces if we are doing network operations. */
+
+ if (remote_machine) {
+ load_interfaces();
+ }
+
+ if (!user_name[0] && (pwd = getpwuid_alloc(geteuid()))) {
+ fstrcpy(user_name, pwd->pw_name);
+ passwd_free(&pwd);
+ }
+
+ if (!user_name[0]) {
+ fprintf(stderr,"You must specify a username\n");
+ exit(1);
+ }
+
+ if (local_flags & LOCAL_TRUST_ACCOUNT) {
+ /* add the $ automatically */
+ static fstring buf;
+
+ /*
+ * Remove any trailing '$' before we
+ * generate the initial machine password.
+ */
+
+ if (user_name[strlen(user_name)-1] == '$') {
+ user_name[strlen(user_name)-1] = 0;
+ }
+
+ if (local_flags & LOCAL_ADD_USER) {
+ SAFE_FREE(new_passwd);
+ new_passwd = smb_xstrdup(user_name);
+ strlower_m(new_passwd);
+ }
+
+ /*
+ * Now ensure the username ends in '$' for
+ * the machine add.
+ */
+
+ slprintf(buf, sizeof(buf)-1, "%s$", user_name);
+ fstrcpy(user_name, buf);
+ } else if (local_flags & LOCAL_INTERDOM_ACCOUNT) {
+ static fstring buf;
+
+ if ((local_flags & LOCAL_ADD_USER) && (new_passwd == NULL)) {
+ /*
+ * Prompt for trusting domain's account password
+ */
+ new_passwd = prompt_for_new_password(stdin_passwd_get);
+ if(!new_passwd) {
+ fprintf(stderr, "Unable to get newpassword.\n");
+ exit(1);
+ }
+ }
+
+ /* prepare uppercased and '$' terminated username */
+ slprintf(buf, sizeof(buf) - 1, "%s$", user_name);
+ fstrcpy(user_name, buf);
+
+ } else {
+
+ if (remote_machine != NULL) {
+ old_passwd = get_pass("Old SMB password:",stdin_passwd_get);
+ }
+
+ if (!(local_flags & LOCAL_SET_PASSWORD)) {
+
+ /*
+ * If we are trying to enable a user, first we need to find out
+ * if they are using a modern version of the smbpasswd file that
+ * disables a user by just writing a flag into the file. If so
+ * then we can re-enable a user without prompting for a new
+ * password. If not (ie. they have a no stored password in the
+ * smbpasswd file) then we need to prompt for a new password.
+ */
+
+ if(local_flags & LOCAL_ENABLE_USER) {
+ SAM_ACCOUNT *sampass = NULL;
+ BOOL ret;
+
+ pdb_init_sam(&sampass);
+ ret = pdb_getsampwnam(sampass, user_name);
+ if((sampass != False) && (pdb_get_lanman_passwd(sampass) == NULL)) {
+ local_flags |= LOCAL_SET_PASSWORD;
+ }
+ pdb_free_sam(&sampass);
+ }
+ }
+
+ if((local_flags & LOCAL_SET_PASSWORD) && (new_passwd == NULL)) {
+ new_passwd = prompt_for_new_password(stdin_passwd_get);
+
+ if(!new_passwd) {
+ fprintf(stderr, "Unable to get new password.\n");
+ exit(1);
+ }
+ }
+ }
+
+ if (!password_change(remote_machine, user_name, old_passwd, new_passwd, local_flags)) {
+ fprintf(stderr,"Failed to modify password entry for user %s\n", user_name);
+ result = 1;
+ goto done;
+ }
+
+ if(remote_machine) {
+ printf("Password changed for user %s on %s.\n", user_name, remote_machine );
+ } else if(!(local_flags & (LOCAL_ADD_USER|LOCAL_DISABLE_USER|LOCAL_ENABLE_USER|LOCAL_DELETE_USER|LOCAL_SET_NO_PASSWORD|LOCAL_SET_PASSWORD))) {
+ SAM_ACCOUNT *sampass = NULL;
+ BOOL ret;
+
+ pdb_init_sam(&sampass);
+ ret = pdb_getsampwnam(sampass, user_name);
+
+ printf("Password changed for user %s.", user_name );
+ if( (ret != False) && (pdb_get_acct_ctrl(sampass)&ACB_DISABLED) )
+ printf(" User has disabled flag set.");
+ if((ret != False) && (pdb_get_acct_ctrl(sampass) & ACB_PWNOTREQ) )
+ printf(" User has no password flag set.");
+ printf("\n");
+ pdb_free_sam(&sampass);
+ }
+
+ done:
+ SAFE_FREE(new_passwd);
+ return result;
+}
+
+
+/*************************************************************
+ Handle password changing for non-root.
+*************************************************************/
+
+static int process_nonroot(int local_flags)
+{
+ struct passwd *pwd = NULL;
+ int result = 0;
+ char *old_pw = NULL;
+ char *new_pw = NULL;
+
+ if (local_flags & ~(LOCAL_AM_ROOT | LOCAL_SET_PASSWORD)) {
+ /* Extra flags that we can't honor non-root */
+ usage();
+ }
+
+ if (!user_name[0]) {
+ pwd = getpwuid_alloc(getuid());
+ if (pwd) {
+ fstrcpy(user_name,pwd->pw_name);
+ passwd_free(&pwd);
+ } else {
+ fprintf(stderr, "smbpasswd: you don't exist - go away\n");
+ exit(1);
+ }
+ }
+
+ /*
+ * A non-root user is always setting a password
+ * via a remote machine (even if that machine is
+ * localhost).
+ */
+
+ load_interfaces(); /* Delayed from main() */
+
+ if (remote_machine == NULL) {
+ remote_machine = "127.0.0.1";
+ }
+
+ if (remote_machine != NULL) {
+ old_pw = get_pass("Old SMB password:",stdin_passwd_get);
+ }
+
+ if (!new_passwd) {
+ new_pw = prompt_for_new_password(stdin_passwd_get);
+ }
+ else
+ new_pw = smb_xstrdup(new_passwd);
+
+ if (!new_pw) {
+ fprintf(stderr, "Unable to get new password.\n");
+ exit(1);
+ }
+
+ if (!password_change(remote_machine, user_name, old_pw, new_pw, 0)) {
+ fprintf(stderr,"Failed to change password for %s\n", user_name);
+ result = 1;
+ goto done;
+ }
+
+ printf("Password changed for user %s\n", user_name);
+
+ done:
+ SAFE_FREE(old_pw);
+ SAFE_FREE(new_pw);
+
+ return result;
+}
+
+
+
+/*********************************************************
+ Start here.
+**********************************************************/
+int main(int argc, char **argv)
+{
+ int local_flags = 0;
+
+ AllowDebugChange = False;
+
+#if defined(HAVE_SET_AUTH_PARAMETERS)
+ set_auth_parameters(argc, argv);
+#endif /* HAVE_SET_AUTH_PARAMETERS */
+
+ if (getuid() == 0) {
+ local_flags = LOCAL_AM_ROOT;
+ }
+
+ local_flags = process_options(argc, argv, local_flags);
+
+ setup_logging("smbpasswd", True);
+
+ /*
+ * Set the machine NETBIOS name if not already
+ * set from the config file.
+ */
+
+ if (!init_names())
+ return 1;
+
+ /* Check the effective uid - make sure we are not setuid */
+ if (is_setuid_root()) {
+ fprintf(stderr, "smbpasswd must *NOT* be setuid root.\n");
+ exit(1);
+ }
+
+ if (local_flags & LOCAL_AM_ROOT) {
+ secrets_init();
+ return process_root(local_flags);
+ }
+
+ return process_nonroot(local_flags);
+}
diff --git a/source/utils/smbtree.c b/source/utils/smbtree.c
new file mode 100644
index 00000000000..cbe1bd448f8
--- /dev/null
+++ b/source/utils/smbtree.c
@@ -0,0 +1,237 @@
+/*
+ Unix SMB/CIFS implementation.
+ Network neighbourhood browser.
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jelmer Vernooij 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static BOOL use_bcast;
+
+/* How low can we go? */
+
+enum tree_level {LEV_WORKGROUP, LEV_SERVER, LEV_SHARE};
+static enum tree_level level = LEV_SHARE;
+
+/* Holds a list of workgroups or servers */
+
+struct name_list {
+ struct name_list *prev, *next;
+ pstring name, comment;
+ uint32 server_type;
+};
+
+static struct name_list *workgroups, *servers, *shares;
+
+static void free_name_list(struct name_list *list)
+{
+ while(list)
+ DLIST_REMOVE(list, list);
+}
+
+static void add_name(const char *machine_name, uint32 server_type,
+ const char *comment, void *state)
+{
+ struct name_list **name_list = (struct name_list **)state;
+ struct name_list *new_name;
+
+ new_name = (struct name_list *)malloc(sizeof(struct name_list));
+
+ if (!new_name)
+ return;
+
+ ZERO_STRUCTP(new_name);
+
+ pstrcpy(new_name->name, machine_name);
+ pstrcpy(new_name->comment, comment);
+ new_name->server_type = server_type;
+
+ DLIST_ADD(*name_list, new_name);
+}
+
+/****************************************************************************
+ display tree of smb workgroups, servers and shares
+****************************************************************************/
+static BOOL get_workgroups(struct user_auth_info *user_info)
+{
+ struct cli_state *cli;
+ struct in_addr server_ip;
+ pstring master_workgroup;
+
+ /* Try to connect to a #1d name of our current workgroup. If that
+ doesn't work broadcast for a master browser and then jump off
+ that workgroup. */
+
+ pstrcpy(master_workgroup, lp_workgroup());
+
+ if (!use_bcast && !find_master_ip(lp_workgroup(), &server_ip)) {
+ DEBUG(4, ("Unable to find master browser for workgroup %s, falling back to broadcast\n",
+ master_workgroup));
+ use_bcast = True;
+ } else if(!use_bcast) {
+ if (!(cli = get_ipc_connect(inet_ntoa(server_ip), &server_ip, user_info)))
+ return False;
+ }
+
+ if (!(cli = get_ipc_connect_master_ip_bcast(master_workgroup, user_info))) {
+ DEBUG(4, ("Unable to find master browser by "
+ "broadcast\n"));
+ return False;
+ }
+
+ if (!cli_NetServerEnum(cli, master_workgroup,
+ SV_TYPE_DOMAIN_ENUM, add_name, &workgroups))
+ return False;
+
+ return True;
+}
+
+/* Retrieve the list of servers for a given workgroup */
+
+static BOOL get_servers(char *workgroup, struct user_auth_info *user_info)
+{
+ struct cli_state *cli;
+ struct in_addr server_ip;
+
+ /* Open an IPC$ connection to the master browser for the workgroup */
+
+ if (!find_master_ip(workgroup, &server_ip)) {
+ DEBUG(4, ("Cannot find master browser for workgroup %s\n",
+ workgroup));
+ return False;
+ }
+
+ if (!(cli = get_ipc_connect(inet_ntoa(server_ip), &server_ip, user_info)))
+ return False;
+
+ if (!cli_NetServerEnum(cli, workgroup, SV_TYPE_ALL, add_name,
+ &servers))
+ return False;
+
+ return True;
+}
+
+static BOOL get_shares(char *server_name, struct user_auth_info *user_info)
+{
+ struct cli_state *cli;
+
+ if (!(cli = get_ipc_connect(server_name, NULL, user_info)))
+ return False;
+
+ if (!cli_RNetShareEnum(cli, add_name, &shares))
+ return False;
+
+ return True;
+}
+
+static BOOL print_tree(struct user_auth_info *user_info)
+{
+ struct name_list *wg, *sv, *sh;
+
+ /* List workgroups */
+
+ if (!get_workgroups(user_info))
+ return False;
+
+ for (wg = workgroups; wg; wg = wg->next) {
+
+ printf("%s\n", wg->name);
+
+ /* List servers */
+
+ free_name_list(servers);
+ servers = NULL;
+
+ if (level == LEV_WORKGROUP ||
+ !get_servers(wg->name, user_info))
+ continue;
+
+ for (sv = servers; sv; sv = sv->next) {
+
+ printf("\t\\\\%-15s\t\t%s\n",
+ sv->name, sv->comment);
+
+ /* List shares */
+
+ free_name_list(shares);
+ shares = NULL;
+
+ if (level == LEV_SERVER ||
+ !get_shares(sv->name, user_info))
+ continue;
+
+ for (sh = shares; sh; sh = sh->next) {
+ printf("\t\t\\\\%s\\%-15s\t%s\n",
+ sv->name, sh->name, sh->comment);
+ }
+ }
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "broadcast", 'b', POPT_ARG_VAL, &use_bcast, True, "Use broadcast instead of using the master browser" },
+ { "domains", 'D', POPT_ARG_VAL, &level, LEV_WORKGROUP, "List only domains (workgroups) of tree" },
+ { "servers", 'S', POPT_ARG_VAL, &level, LEV_SERVER, "List domains(workgroups) and servers of tree" },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CREDENTIALS
+ POPT_TABLEEND
+ };
+ poptContext pc;
+
+ /* Initialise samba stuff */
+
+ setlinebuf(stdout);
+
+ dbf = x_stderr;
+
+ setup_logging(argv[0],True);
+
+ pc = poptGetContext("smbtree", argc, (const char **)argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+ while(poptGetNextOpt(pc) != -1);
+ poptFreeContext(pc);
+
+ lp_load(dyn_CONFIGFILE,True,False,False);
+ load_interfaces();
+
+ /* Parse command line args */
+
+ if (!cmdline_auth_info.got_pass) {
+ char *pass = getpass("Password: ");
+ if (pass) {
+ pstrcpy(cmdline_auth_info.password, pass);
+ }
+ cmdline_auth_info.got_pass = True;
+ }
+
+ /* Now do our stuff */
+
+ if (!print_tree(&cmdline_auth_info))
+ return 1;
+
+ return 0;
+}
diff --git a/source/utils/smbw_sample.c b/source/utils/smbw_sample.c
new file mode 100644
index 00000000000..5cd792df7a2
--- /dev/null
+++ b/source/utils/smbw_sample.c
@@ -0,0 +1,94 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+static void usage(void)
+{
+ printf("
+smbw_sample - a sample program that uses smbw
+
+smbw_sample <options> path
+
+ options:
+ -W workgroup
+ -l logfile
+ -P prefix
+ -d debuglevel
+ -U username%%password
+ -R resolve order
+
+note that path must start with /smb/
+");
+}
+
+int main(int argc, char *argv[])
+{
+ DIR *dir;
+ struct dirent *dent;
+ int opt;
+ char *p;
+ extern char *optarg;
+ extern int optind;
+ char *path;
+
+ lp_load(dyn_CONFIGFILE,1,0,0);
+ smbw_setup_shared();
+
+ while ((opt = getopt(argc, argv, "W:U:R:d:P:l:hL:")) != EOF) {
+ switch (opt) {
+ case 'W':
+ smbw_setshared("WORKGROUP", optarg);
+ break;
+ case 'l':
+ smbw_setshared("LOGFILE", optarg);
+ break;
+ case 'P':
+ smbw_setshared("PREFIX", optarg);
+ break;
+ case 'd':
+ smbw_setshared("DEBUG", optarg);
+ break;
+ case 'U':
+ p = strchr_m(optarg,'%');
+ if (p) {
+ *p=0;
+ smbw_setshared("PASSWORD",p+1);
+ }
+ smbw_setshared("USER", optarg);
+ break;
+ case 'R':
+ smbw_setshared("RESOLVE_ORDER",optarg);
+ break;
+ case 'h':
+ default:
+ usage();
+ exit(1);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ usage();
+ exit(1);
+ }
+
+ path = argv[0];
+
+ smbw_init();
+
+ dir = smbw_opendir(path);
+ if (!dir) {
+ printf("failed to open %s\n", path);
+ exit(1);
+ }
+
+ while ((dent = smbw_readdir(dir))) {
+ printf("%s\n", dent->d_name);
+ }
+ smbw_closedir(dir);
+ return 0;
+}
diff --git a/source/utils/status.c b/source/utils/status.c
new file mode 100644
index 00000000000..4585b101b28
--- /dev/null
+++ b/source/utils/status.c
@@ -0,0 +1,693 @@
+/*
+ Unix SMB/CIFS implementation.
+ status reporting
+ Copyright (C) Andrew Tridgell 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Revision History:
+
+ 12 aug 96: Erik.Devriendt@te6.siemens.be
+ added support for shared memory implementation of share mode locking
+
+ 21-Jul-1998: rsharpe@ns.aus.com (Richard Sharpe)
+ Added -L (locks only) -S (shares only) flags and code
+
+*/
+
+/*
+ * This program reports current SMB connections
+ */
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+#define SMB_MAXPIDS 2048
+static pstring Ucrit_username = ""; /* added by OH */
+static pid_t Ucrit_pid[SMB_MAXPIDS]; /* Ugly !!! */ /* added by OH */
+static int Ucrit_MaxPid=0; /* added by OH */
+static unsigned int Ucrit_IsActive = 0; /* added by OH */
+
+static int verbose, brief;
+static int shares_only = 0; /* Added by RJS */
+static int locks_only = 0; /* Added by RJS */
+static BOOL processes_only=False;
+static int show_brl;
+
+/* added by OH */
+static void Ucrit_addUsername(const char *username)
+{
+ pstrcpy(Ucrit_username, username);
+
+ if ( strlen(Ucrit_username) > 0 )
+ Ucrit_IsActive = 1;
+}
+
+static unsigned int Ucrit_checkUsername(const char *username)
+{
+ if ( !Ucrit_IsActive )
+ return 1;
+
+ if ( strcmp(Ucrit_username,username) == 0 )
+ return 1;
+
+ return 0;
+}
+
+static unsigned int Ucrit_checkPid(pid_t pid)
+{
+ int i;
+
+ if ( !Ucrit_IsActive )
+ return 1;
+
+ for (i=0;i<Ucrit_MaxPid;i++) {
+ if( pid == Ucrit_pid[i] )
+ return 1;
+ }
+
+ return 0;
+}
+
+static BOOL Ucrit_addPid( pid_t pid )
+{
+ if ( !Ucrit_IsActive )
+ return True;
+
+ if ( Ucrit_MaxPid >= SMB_MAXPIDS ) {
+ d_printf("ERROR: More than %d pids for user %s!\n",
+ SMB_MAXPIDS, Ucrit_username);
+
+ return False;
+ }
+
+ Ucrit_pid[Ucrit_MaxPid++] = pid;
+
+ return True;
+}
+
+static void print_share_mode(share_mode_entry *e, char *fname)
+{
+ static int count;
+ if (count==0) {
+ d_printf("Locked files:\n");
+ d_printf("Pid DenyMode Access R/W Oplock Name\n");
+ d_printf("--------------------------------------------------------------\n");
+ }
+ count++;
+
+ if (Ucrit_checkPid(e->pid)) {
+ d_printf("%-5d ",(int)e->pid);
+ switch (GET_DENY_MODE(e->share_mode)) {
+ case DENY_NONE: d_printf("DENY_NONE "); break;
+ case DENY_ALL: d_printf("DENY_ALL "); break;
+ case DENY_DOS: d_printf("DENY_DOS "); break;
+ case DENY_READ: d_printf("DENY_READ "); break;
+ case DENY_WRITE:printf("DENY_WRITE "); break;
+ case DENY_FCB: d_printf("DENY_FCB "); break;
+ }
+ d_printf("0x%-8x ",(unsigned int)e->desired_access);
+ switch (e->share_mode&0xF) {
+ case 0: d_printf("RDONLY "); break;
+ case 1: d_printf("WRONLY "); break;
+ case 2: d_printf("RDWR "); break;
+ }
+
+ if((e->op_type &
+ (EXCLUSIVE_OPLOCK|BATCH_OPLOCK)) ==
+ (EXCLUSIVE_OPLOCK|BATCH_OPLOCK))
+ d_printf("EXCLUSIVE+BATCH ");
+ else if (e->op_type & EXCLUSIVE_OPLOCK)
+ d_printf("EXCLUSIVE ");
+ else if (e->op_type & BATCH_OPLOCK)
+ d_printf("BATCH ");
+ else if (e->op_type & LEVEL_II_OPLOCK)
+ d_printf("LEVEL_II ");
+ else
+ d_printf("NONE ");
+
+ d_printf(" %s %s",fname,
+ asctime(LocalTime((time_t *)&e->time.tv_sec)));
+ }
+}
+
+static void print_brl(SMB_DEV_T dev, SMB_INO_T ino, int pid,
+ enum brl_type lock_type,
+ br_off start, br_off size)
+{
+ static int count;
+ if (count==0) {
+ d_printf("Byte range locks:\n");
+ d_printf(" Pid dev:inode R/W start size\n");
+ d_printf("------------------------------------------------\n");
+ }
+ count++;
+
+ d_printf("%6d %05x:%05x %s %9.0f %9.0f\n",
+ (int)pid, (int)dev, (int)ino,
+ lock_type==READ_LOCK?"R":"W",
+ (double)start, (double)size);
+}
+
+
+/*******************************************************************
+ dump the elements of the profile structure
+ ******************************************************************/
+static int profile_dump(void)
+{
+#ifdef WITH_PROFILE
+ if (!profile_setup(True)) {
+ fprintf(stderr,"Failed to initialise profile memory\n");
+ return -1;
+ }
+
+ d_printf("smb_count: %u\n", profile_p->smb_count);
+ d_printf("uid_changes: %u\n", profile_p->uid_changes);
+ d_printf("************************ System Calls ****************************\n");
+ d_printf("opendir_count: %u\n", profile_p->syscall_opendir_count);
+ d_printf("opendir_time: %u\n", profile_p->syscall_opendir_time);
+ d_printf("readdir_count: %u\n", profile_p->syscall_readdir_count);
+ d_printf("readdir_time: %u\n", profile_p->syscall_readdir_time);
+ d_printf("mkdir_count: %u\n", profile_p->syscall_mkdir_count);
+ d_printf("mkdir_time: %u\n", profile_p->syscall_mkdir_time);
+ d_printf("rmdir_count: %u\n", profile_p->syscall_rmdir_count);
+ d_printf("rmdir_time: %u\n", profile_p->syscall_rmdir_time);
+ d_printf("closedir_count: %u\n", profile_p->syscall_closedir_count);
+ d_printf("closedir_time: %u\n", profile_p->syscall_closedir_time);
+ d_printf("open_count: %u\n", profile_p->syscall_open_count);
+ d_printf("open_time: %u\n", profile_p->syscall_open_time);
+ d_printf("close_count: %u\n", profile_p->syscall_close_count);
+ d_printf("close_time: %u\n", profile_p->syscall_close_time);
+ d_printf("read_count: %u\n", profile_p->syscall_read_count);
+ d_printf("read_time: %u\n", profile_p->syscall_read_time);
+ d_printf("read_bytes: %u\n", profile_p->syscall_read_bytes);
+ d_printf("write_count: %u\n", profile_p->syscall_write_count);
+ d_printf("write_time: %u\n", profile_p->syscall_write_time);
+ d_printf("write_bytes: %u\n", profile_p->syscall_write_bytes);
+ d_printf("pread_count: %u\n", profile_p->syscall_pread_count);
+ d_printf("pread_time: %u\n", profile_p->syscall_pread_time);
+ d_printf("pread_bytes: %u\n", profile_p->syscall_pread_bytes);
+ d_printf("pwrite_count: %u\n", profile_p->syscall_pwrite_count);
+ d_printf("pwrite_time: %u\n", profile_p->syscall_pwrite_time);
+ d_printf("pwrite_bytes: %u\n", profile_p->syscall_pwrite_bytes);
+#ifdef WITH_SENDFILE
+ d_printf("sendfile_count: %u\n", profile_p->syscall_sendfile_count);
+ d_printf("sendfile_time: %u\n", profile_p->syscall_sendfile_time);
+ d_printf("sendfile_bytes: %u\n", profile_p->syscall_sendfile_bytes);
+#endif
+ d_printf("lseek_count: %u\n", profile_p->syscall_lseek_count);
+ d_printf("lseek_time: %u\n", profile_p->syscall_lseek_time);
+ d_printf("rename_count: %u\n", profile_p->syscall_rename_count);
+ d_printf("rename_time: %u\n", profile_p->syscall_rename_time);
+ d_printf("fsync_count: %u\n", profile_p->syscall_fsync_count);
+ d_printf("fsync_time: %u\n", profile_p->syscall_fsync_time);
+ d_printf("stat_count: %u\n", profile_p->syscall_stat_count);
+ d_printf("stat_time: %u\n", profile_p->syscall_stat_time);
+ d_printf("fstat_count: %u\n", profile_p->syscall_fstat_count);
+ d_printf("fstat_time: %u\n", profile_p->syscall_fstat_time);
+ d_printf("lstat_count: %u\n", profile_p->syscall_lstat_count);
+ d_printf("lstat_time: %u\n", profile_p->syscall_lstat_time);
+ d_printf("unlink_count: %u\n", profile_p->syscall_unlink_count);
+ d_printf("unlink_time: %u\n", profile_p->syscall_unlink_time);
+ d_printf("chmod_count: %u\n", profile_p->syscall_chmod_count);
+ d_printf("chmod_time: %u\n", profile_p->syscall_chmod_time);
+ d_printf("fchmod_count: %u\n", profile_p->syscall_fchmod_count);
+ d_printf("fchmod_time: %u\n", profile_p->syscall_fchmod_time);
+ d_printf("chown_count: %u\n", profile_p->syscall_chown_count);
+ d_printf("chown_time: %u\n", profile_p->syscall_chown_time);
+ d_printf("fchown_count: %u\n", profile_p->syscall_fchown_count);
+ d_printf("fchown_time: %u\n", profile_p->syscall_fchown_time);
+ d_printf("chdir_count: %u\n", profile_p->syscall_chdir_count);
+ d_printf("chdir_time: %u\n", profile_p->syscall_chdir_time);
+ d_printf("getwd_count: %u\n", profile_p->syscall_getwd_count);
+ d_printf("getwd_time: %u\n", profile_p->syscall_getwd_time);
+ d_printf("utime_count: %u\n", profile_p->syscall_utime_count);
+ d_printf("utime_time: %u\n", profile_p->syscall_utime_time);
+ d_printf("ftruncate_count: %u\n", profile_p->syscall_ftruncate_count);
+ d_printf("ftruncate_time: %u\n", profile_p->syscall_ftruncate_time);
+ d_printf("fcntl_lock_count: %u\n", profile_p->syscall_fcntl_lock_count);
+ d_printf("fcntl_lock_time: %u\n", profile_p->syscall_fcntl_lock_time);
+ d_printf("readlink_count: %u\n", profile_p->syscall_readlink_count);
+ d_printf("readlink_time: %u\n", profile_p->syscall_readlink_time);
+ d_printf("symlink_count: %u\n", profile_p->syscall_symlink_count);
+ d_printf("symlink_time: %u\n", profile_p->syscall_symlink_time);
+ d_printf("************************ Statcache *******************************\n");
+ d_printf("lookups: %u\n", profile_p->statcache_lookups);
+ d_printf("misses: %u\n", profile_p->statcache_misses);
+ d_printf("hits: %u\n", profile_p->statcache_hits);
+ d_printf("************************ Writecache ******************************\n");
+ d_printf("read_hits: %u\n", profile_p->writecache_read_hits);
+ d_printf("abutted_writes: %u\n", profile_p->writecache_abutted_writes);
+ d_printf("total_writes: %u\n", profile_p->writecache_total_writes);
+ d_printf("non_oplock_writes: %u\n", profile_p->writecache_non_oplock_writes);
+ d_printf("direct_writes: %u\n", profile_p->writecache_direct_writes);
+ d_printf("init_writes: %u\n", profile_p->writecache_init_writes);
+ d_printf("flushed_writes[SEEK]: %u\n", profile_p->writecache_flushed_writes[SEEK_FLUSH]);
+ d_printf("flushed_writes[READ]: %u\n", profile_p->writecache_flushed_writes[READ_FLUSH]);
+ d_printf("flushed_writes[WRITE]: %u\n", profile_p->writecache_flushed_writes[WRITE_FLUSH]);
+ d_printf("flushed_writes[READRAW]: %u\n", profile_p->writecache_flushed_writes[READRAW_FLUSH]);
+ d_printf("flushed_writes[OPLOCK_RELEASE]: %u\n", profile_p->writecache_flushed_writes[OPLOCK_RELEASE_FLUSH]);
+ d_printf("flushed_writes[CLOSE]: %u\n", profile_p->writecache_flushed_writes[CLOSE_FLUSH]);
+ d_printf("flushed_writes[SYNC]: %u\n", profile_p->writecache_flushed_writes[SYNC_FLUSH]);
+ d_printf("flushed_writes[SIZECHANGE]: %u\n", profile_p->writecache_flushed_writes[SIZECHANGE_FLUSH]);
+ d_printf("num_perfect_writes: %u\n", profile_p->writecache_num_perfect_writes);
+ d_printf("num_write_caches: %u\n", profile_p->writecache_num_write_caches);
+ d_printf("allocated_write_caches: %u\n", profile_p->writecache_allocated_write_caches);
+ d_printf("************************ SMB Calls *******************************\n");
+ d_printf("mkdir_count: %u\n", profile_p->SMBmkdir_count);
+ d_printf("mkdir_time: %u\n", profile_p->SMBmkdir_time);
+ d_printf("rmdir_count: %u\n", profile_p->SMBrmdir_count);
+ d_printf("rmdir_time: %u\n", profile_p->SMBrmdir_time);
+ d_printf("open_count: %u\n", profile_p->SMBopen_count);
+ d_printf("open_time: %u\n", profile_p->SMBopen_time);
+ d_printf("create_count: %u\n", profile_p->SMBcreate_count);
+ d_printf("create_time: %u\n", profile_p->SMBcreate_time);
+ d_printf("close_count: %u\n", profile_p->SMBclose_count);
+ d_printf("close_time: %u\n", profile_p->SMBclose_time);
+ d_printf("flush_count: %u\n", profile_p->SMBflush_count);
+ d_printf("flush_time: %u\n", profile_p->SMBflush_time);
+ d_printf("unlink_count: %u\n", profile_p->SMBunlink_count);
+ d_printf("unlink_time: %u\n", profile_p->SMBunlink_time);
+ d_printf("mv_count: %u\n", profile_p->SMBmv_count);
+ d_printf("mv_time: %u\n", profile_p->SMBmv_time);
+ d_printf("getatr_count: %u\n", profile_p->SMBgetatr_count);
+ d_printf("getatr_time: %u\n", profile_p->SMBgetatr_time);
+ d_printf("setatr_count: %u\n", profile_p->SMBsetatr_count);
+ d_printf("setatr_time: %u\n", profile_p->SMBsetatr_time);
+ d_printf("read_count: %u\n", profile_p->SMBread_count);
+ d_printf("read_time: %u\n", profile_p->SMBread_time);
+ d_printf("write_count: %u\n", profile_p->SMBwrite_count);
+ d_printf("write_time: %u\n", profile_p->SMBwrite_time);
+ d_printf("lock_count: %u\n", profile_p->SMBlock_count);
+ d_printf("lock_time: %u\n", profile_p->SMBlock_time);
+ d_printf("unlock_count: %u\n", profile_p->SMBunlock_count);
+ d_printf("unlock_time: %u\n", profile_p->SMBunlock_time);
+ d_printf("ctemp_count: %u\n", profile_p->SMBctemp_count);
+ d_printf("ctemp_time: %u\n", profile_p->SMBctemp_time);
+ d_printf("mknew_count: %u\n", profile_p->SMBmknew_count);
+ d_printf("mknew_time: %u\n", profile_p->SMBmknew_time);
+ d_printf("chkpth_count: %u\n", profile_p->SMBchkpth_count);
+ d_printf("chkpth_time: %u\n", profile_p->SMBchkpth_time);
+ d_printf("exit_count: %u\n", profile_p->SMBexit_count);
+ d_printf("exit_time: %u\n", profile_p->SMBexit_time);
+ d_printf("lseek_count: %u\n", profile_p->SMBlseek_count);
+ d_printf("lseek_time: %u\n", profile_p->SMBlseek_time);
+ d_printf("lockread_count: %u\n", profile_p->SMBlockread_count);
+ d_printf("lockread_time: %u\n", profile_p->SMBlockread_time);
+ d_printf("writeunlock_count: %u\n", profile_p->SMBwriteunlock_count);
+ d_printf("writeunlock_time: %u\n", profile_p->SMBwriteunlock_time);
+ d_printf("readbraw_count: %u\n", profile_p->SMBreadbraw_count);
+ d_printf("readbraw_time: %u\n", profile_p->SMBreadbraw_time);
+ d_printf("readBmpx_count: %u\n", profile_p->SMBreadBmpx_count);
+ d_printf("readBmpx_time: %u\n", profile_p->SMBreadBmpx_time);
+ d_printf("readBs_count: %u\n", profile_p->SMBreadBs_count);
+ d_printf("readBs_time: %u\n", profile_p->SMBreadBs_time);
+ d_printf("writebraw_count: %u\n", profile_p->SMBwritebraw_count);
+ d_printf("writebraw_time: %u\n", profile_p->SMBwritebraw_time);
+ d_printf("writeBmpx_count: %u\n", profile_p->SMBwriteBmpx_count);
+ d_printf("writeBmpx_time: %u\n", profile_p->SMBwriteBmpx_time);
+ d_printf("writeBs_count: %u\n", profile_p->SMBwriteBs_count);
+ d_printf("writeBs_time: %u\n", profile_p->SMBwriteBs_time);
+ d_printf("writec_count: %u\n", profile_p->SMBwritec_count);
+ d_printf("writec_time: %u\n", profile_p->SMBwritec_time);
+ d_printf("setattrE_count: %u\n", profile_p->SMBsetattrE_count);
+ d_printf("setattrE_time: %u\n", profile_p->SMBsetattrE_time);
+ d_printf("getattrE_count: %u\n", profile_p->SMBgetattrE_count);
+ d_printf("getattrE_time: %u\n", profile_p->SMBgetattrE_time);
+ d_printf("lockingX_count: %u\n", profile_p->SMBlockingX_count);
+ d_printf("lockingX_time: %u\n", profile_p->SMBlockingX_time);
+ d_printf("trans_count: %u\n", profile_p->SMBtrans_count);
+ d_printf("trans_time: %u\n", profile_p->SMBtrans_time);
+ d_printf("transs_count: %u\n", profile_p->SMBtranss_count);
+ d_printf("transs_time: %u\n", profile_p->SMBtranss_time);
+ d_printf("ioctl_count: %u\n", profile_p->SMBioctl_count);
+ d_printf("ioctl_time: %u\n", profile_p->SMBioctl_time);
+ d_printf("ioctls_count: %u\n", profile_p->SMBioctls_count);
+ d_printf("ioctls_time: %u\n", profile_p->SMBioctls_time);
+ d_printf("copy_count: %u\n", profile_p->SMBcopy_count);
+ d_printf("copy_time: %u\n", profile_p->SMBcopy_time);
+ d_printf("move_count: %u\n", profile_p->SMBmove_count);
+ d_printf("move_time: %u\n", profile_p->SMBmove_time);
+ d_printf("echo_count: %u\n", profile_p->SMBecho_count);
+ d_printf("echo_time: %u\n", profile_p->SMBecho_time);
+ d_printf("writeclose_count: %u\n", profile_p->SMBwriteclose_count);
+ d_printf("writeclose_time: %u\n", profile_p->SMBwriteclose_time);
+ d_printf("openX_count: %u\n", profile_p->SMBopenX_count);
+ d_printf("openX_time: %u\n", profile_p->SMBopenX_time);
+ d_printf("readX_count: %u\n", profile_p->SMBreadX_count);
+ d_printf("readX_time: %u\n", profile_p->SMBreadX_time);
+ d_printf("writeX_count: %u\n", profile_p->SMBwriteX_count);
+ d_printf("writeX_time: %u\n", profile_p->SMBwriteX_time);
+ d_printf("trans2_count: %u\n", profile_p->SMBtrans2_count);
+ d_printf("trans2_time: %u\n", profile_p->SMBtrans2_time);
+ d_printf("transs2_count: %u\n", profile_p->SMBtranss2_count);
+ d_printf("transs2_time: %u\n", profile_p->SMBtranss2_time);
+ d_printf("findclose_count: %u\n", profile_p->SMBfindclose_count);
+ d_printf("findclose_time: %u\n", profile_p->SMBfindclose_time);
+ d_printf("findnclose_count: %u\n", profile_p->SMBfindnclose_count);
+ d_printf("findnclose_time: %u\n", profile_p->SMBfindnclose_time);
+ d_printf("tcon_count: %u\n", profile_p->SMBtcon_count);
+ d_printf("tcon_time: %u\n", profile_p->SMBtcon_time);
+ d_printf("tdis_count: %u\n", profile_p->SMBtdis_count);
+ d_printf("tdis_time: %u\n", profile_p->SMBtdis_time);
+ d_printf("negprot_count: %u\n", profile_p->SMBnegprot_count);
+ d_printf("negprot_time: %u\n", profile_p->SMBnegprot_time);
+ d_printf("sesssetupX_count: %u\n", profile_p->SMBsesssetupX_count);
+ d_printf("sesssetupX_time: %u\n", profile_p->SMBsesssetupX_time);
+ d_printf("ulogoffX_count: %u\n", profile_p->SMBulogoffX_count);
+ d_printf("ulogoffX_time: %u\n", profile_p->SMBulogoffX_time);
+ d_printf("tconX_count: %u\n", profile_p->SMBtconX_count);
+ d_printf("tconX_time: %u\n", profile_p->SMBtconX_time);
+ d_printf("dskattr_count: %u\n", profile_p->SMBdskattr_count);
+ d_printf("dskattr_time: %u\n", profile_p->SMBdskattr_time);
+ d_printf("search_count: %u\n", profile_p->SMBsearch_count);
+ d_printf("search_time: %u\n", profile_p->SMBsearch_time);
+ d_printf("ffirst_count: %u\n", profile_p->SMBffirst_count);
+ d_printf("ffirst_time: %u\n", profile_p->SMBffirst_time);
+ d_printf("funique_count: %u\n", profile_p->SMBfunique_count);
+ d_printf("funique_time: %u\n", profile_p->SMBfunique_time);
+ d_printf("fclose_count: %u\n", profile_p->SMBfclose_count);
+ d_printf("fclose_time: %u\n", profile_p->SMBfclose_time);
+ d_printf("nttrans_count: %u\n", profile_p->SMBnttrans_count);
+ d_printf("nttrans_time: %u\n", profile_p->SMBnttrans_time);
+ d_printf("nttranss_count: %u\n", profile_p->SMBnttranss_count);
+ d_printf("nttranss_time: %u\n", profile_p->SMBnttranss_time);
+ d_printf("ntcreateX_count: %u\n", profile_p->SMBntcreateX_count);
+ d_printf("ntcreateX_time: %u\n", profile_p->SMBntcreateX_time);
+ d_printf("ntcancel_count: %u\n", profile_p->SMBntcancel_count);
+ d_printf("ntcancel_time: %u\n", profile_p->SMBntcancel_time);
+ d_printf("splopen_count: %u\n", profile_p->SMBsplopen_count);
+ d_printf("splopen_time: %u\n", profile_p->SMBsplopen_time);
+ d_printf("splwr_count: %u\n", profile_p->SMBsplwr_count);
+ d_printf("splwr_time: %u\n", profile_p->SMBsplwr_time);
+ d_printf("splclose_count: %u\n", profile_p->SMBsplclose_count);
+ d_printf("splclose_time: %u\n", profile_p->SMBsplclose_time);
+ d_printf("splretq_count: %u\n", profile_p->SMBsplretq_count);
+ d_printf("splretq_time: %u\n", profile_p->SMBsplretq_time);
+ d_printf("sends_count: %u\n", profile_p->SMBsends_count);
+ d_printf("sends_time: %u\n", profile_p->SMBsends_time);
+ d_printf("sendb_count: %u\n", profile_p->SMBsendb_count);
+ d_printf("sendb_time: %u\n", profile_p->SMBsendb_time);
+ d_printf("fwdname_count: %u\n", profile_p->SMBfwdname_count);
+ d_printf("fwdname_time: %u\n", profile_p->SMBfwdname_time);
+ d_printf("cancelf_count: %u\n", profile_p->SMBcancelf_count);
+ d_printf("cancelf_time: %u\n", profile_p->SMBcancelf_time);
+ d_printf("getmac_count: %u\n", profile_p->SMBgetmac_count);
+ d_printf("getmac_time: %u\n", profile_p->SMBgetmac_time);
+ d_printf("sendstrt_count: %u\n", profile_p->SMBsendstrt_count);
+ d_printf("sendstrt_time: %u\n", profile_p->SMBsendstrt_time);
+ d_printf("sendend_count: %u\n", profile_p->SMBsendend_count);
+ d_printf("sendend_time: %u\n", profile_p->SMBsendend_time);
+ d_printf("sendtxt_count: %u\n", profile_p->SMBsendtxt_count);
+ d_printf("sendtxt_time: %u\n", profile_p->SMBsendtxt_time);
+ d_printf("invalid_count: %u\n", profile_p->SMBinvalid_count);
+ d_printf("invalid_time: %u\n", profile_p->SMBinvalid_time);
+ d_printf("************************ Pathworks Calls *************************\n");
+ d_printf("setdir_count: %u\n", profile_p->pathworks_setdir_count);
+ d_printf("setdir_time: %u\n", profile_p->pathworks_setdir_time);
+ d_printf("************************ Trans2 Calls ****************************\n");
+ d_printf("open_count: %u\n", profile_p->Trans2_open_count);
+ d_printf("open_time: %u\n", profile_p->Trans2_open_time);
+ d_printf("findfirst_count: %u\n", profile_p->Trans2_findfirst_count);
+ d_printf("findfirst_time: %u\n", profile_p->Trans2_findfirst_time);
+ d_printf("findnext_count: %u\n", profile_p->Trans2_findnext_count);
+ d_printf("findnext_time: %u\n", profile_p->Trans2_findnext_time);
+ d_printf("qfsinfo_count: %u\n", profile_p->Trans2_qfsinfo_count);
+ d_printf("qfsinfo_time: %u\n", profile_p->Trans2_qfsinfo_time);
+ d_printf("setfsinfo_count: %u\n", profile_p->Trans2_setfsinfo_count);
+ d_printf("setfsinfo_time: %u\n", profile_p->Trans2_setfsinfo_time);
+ d_printf("qpathinfo_count: %u\n", profile_p->Trans2_qpathinfo_count);
+ d_printf("qpathinfo_time: %u\n", profile_p->Trans2_qpathinfo_time);
+ d_printf("setpathinfo_count: %u\n", profile_p->Trans2_setpathinfo_count);
+ d_printf("setpathinfo_time: %u\n", profile_p->Trans2_setpathinfo_time);
+ d_printf("qfileinfo_count: %u\n", profile_p->Trans2_qfileinfo_count);
+ d_printf("qfileinfo_time: %u\n", profile_p->Trans2_qfileinfo_time);
+ d_printf("setfileinfo_count: %u\n", profile_p->Trans2_setfileinfo_count);
+ d_printf("setfileinfo_time: %u\n", profile_p->Trans2_setfileinfo_time);
+ d_printf("fsctl_count: %u\n", profile_p->Trans2_fsctl_count);
+ d_printf("fsctl_time: %u\n", profile_p->Trans2_fsctl_time);
+ d_printf("ioctl_count: %u\n", profile_p->Trans2_ioctl_count);
+ d_printf("ioctl_time: %u\n", profile_p->Trans2_ioctl_time);
+ d_printf("findnotifyfirst_count: %u\n", profile_p->Trans2_findnotifyfirst_count);
+ d_printf("findnotifyfirst_time: %u\n", profile_p->Trans2_findnotifyfirst_time);
+ d_printf("findnotifynext_count: %u\n", profile_p->Trans2_findnotifynext_count);
+ d_printf("findnotifynext_time: %u\n", profile_p->Trans2_findnotifynext_time);
+ d_printf("mkdir_count: %u\n", profile_p->Trans2_mkdir_count);
+ d_printf("mkdir_time: %u\n", profile_p->Trans2_mkdir_time);
+ d_printf("session_setup_count: %u\n", profile_p->Trans2_session_setup_count);
+ d_printf("session_setup_time: %u\n", profile_p->Trans2_session_setup_time);
+ d_printf("get_dfs_referral_count: %u\n", profile_p->Trans2_get_dfs_referral_count);
+ d_printf("get_dfs_referral_time: %u\n", profile_p->Trans2_get_dfs_referral_time);
+ d_printf("report_dfs_inconsistancy_count: %u\n", profile_p->Trans2_report_dfs_inconsistancy_count);
+ d_printf("report_dfs_inconsistancy_time: %u\n", profile_p->Trans2_report_dfs_inconsistancy_time);
+ d_printf("************************ NT Transact Calls ***********************\n");
+ d_printf("create_count: %u\n", profile_p->NT_transact_create_count);
+ d_printf("create_time: %u\n", profile_p->NT_transact_create_time);
+ d_printf("ioctl_count: %u\n", profile_p->NT_transact_ioctl_count);
+ d_printf("ioctl_time: %u\n", profile_p->NT_transact_ioctl_time);
+ d_printf("set_security_desc_count: %u\n", profile_p->NT_transact_set_security_desc_count);
+ d_printf("set_security_desc_time: %u\n", profile_p->NT_transact_set_security_desc_time);
+ d_printf("notify_change_count: %u\n", profile_p->NT_transact_notify_change_count);
+ d_printf("notify_change_time: %u\n", profile_p->NT_transact_notify_change_time);
+ d_printf("rename_count: %u\n", profile_p->NT_transact_rename_count);
+ d_printf("rename_time: %u\n", profile_p->NT_transact_rename_time);
+ d_printf("query_security_desc_count: %u\n", profile_p->NT_transact_query_security_desc_count);
+ d_printf("query_security_desc_time: %u\n", profile_p->NT_transact_query_security_desc_time);
+ d_printf("************************ ACL Calls *******************************\n");
+ d_printf("get_nt_acl_count: %u\n", profile_p->get_nt_acl_count);
+ d_printf("get_nt_acl_time: %u\n", profile_p->get_nt_acl_time);
+ d_printf("fget_nt_acl_count: %u\n", profile_p->fget_nt_acl_count);
+ d_printf("fget_nt_acl_time: %u\n", profile_p->fget_nt_acl_time);
+ d_printf("set_nt_acl_count: %u\n", profile_p->set_nt_acl_count);
+ d_printf("set_nt_acl_time: %u\n", profile_p->set_nt_acl_time);
+ d_printf("fset_nt_acl_count: %u\n", profile_p->fset_nt_acl_count);
+ d_printf("fset_nt_acl_time: %u\n", profile_p->fset_nt_acl_time);
+ d_printf("chmod_acl_count: %u\n", profile_p->chmod_acl_count);
+ d_printf("chmod_acl_time: %u\n", profile_p->chmod_acl_time);
+ d_printf("fchmod_acl_count: %u\n", profile_p->fchmod_acl_count);
+ d_printf("fchmod_acl_time: %u\n", profile_p->fchmod_acl_time);
+ d_printf("************************ NMBD Calls ****************************\n");
+ d_printf("name_release_count: %u\n", profile_p->name_release_count);
+ d_printf("name_release_time: %u\n", profile_p->name_release_time);
+ d_printf("name_refresh_count: %u\n", profile_p->name_refresh_count);
+ d_printf("name_refresh_time: %u\n", profile_p->name_refresh_time);
+ d_printf("name_registration_count: %u\n", profile_p->name_registration_count);
+ d_printf("name_registration_time: %u\n", profile_p->name_registration_time);
+ d_printf("node_status_count: %u\n", profile_p->node_status_count);
+ d_printf("node_status_time: %u\n", profile_p->node_status_time);
+ d_printf("name_query_count: %u\n", profile_p->name_query_count);
+ d_printf("name_query_time: %u\n", profile_p->name_query_time);
+ d_printf("host_announce_count: %u\n", profile_p->host_announce_count);
+ d_printf("host_announce_time: %u\n", profile_p->host_announce_time);
+ d_printf("workgroup_announce_count: %u\n", profile_p->workgroup_announce_count);
+ d_printf("workgroup_announce_time: %u\n", profile_p->workgroup_announce_time);
+ d_printf("local_master_announce_count: %u\n", profile_p->local_master_announce_count);
+ d_printf("local_master_announce_time: %u\n", profile_p->local_master_announce_time);
+ d_printf("master_browser_announce_count: %u\n", profile_p->master_browser_announce_count);
+ d_printf("master_browser_announce_time: %u\n", profile_p->master_browser_announce_time);
+ d_printf("lm_host_announce_count: %u\n", profile_p->lm_host_announce_count);
+ d_printf("lm_host_announce_time: %u\n", profile_p->lm_host_announce_time);
+ d_printf("get_backup_list_count: %u\n", profile_p->get_backup_list_count);
+ d_printf("get_backup_list_time: %u\n", profile_p->get_backup_list_time);
+ d_printf("reset_browser_count: %u\n", profile_p->reset_browser_count);
+ d_printf("reset_browser_time: %u\n", profile_p->reset_browser_time);
+ d_printf("announce_request_count: %u\n", profile_p->announce_request_count);
+ d_printf("announce_request_time: %u\n", profile_p->announce_request_time);
+ d_printf("lm_announce_request_count: %u\n", profile_p->lm_announce_request_count);
+ d_printf("lm_announce_request_time: %u\n", profile_p->lm_announce_request_time);
+ d_printf("domain_logon_count: %u\n", profile_p->domain_logon_count);
+ d_printf("domain_logon_time: %u\n", profile_p->domain_logon_time);
+ d_printf("sync_browse_lists_count: %u\n", profile_p->sync_browse_lists_count);
+ d_printf("sync_browse_lists_time: %u\n", profile_p->sync_browse_lists_time);
+ d_printf("run_elections_count: %u\n", profile_p->run_elections_count);
+ d_printf("run_elections_time: %u\n", profile_p->run_elections_time);
+ d_printf("election_count: %u\n", profile_p->election_count);
+ d_printf("election_time: %u\n", profile_p->election_time);
+#else /* WITH_PROFILE */
+ fprintf(stderr, "Profile data unavailable\n");
+#endif /* WITH_PROFILE */
+
+ return 0;
+}
+
+
+static int traverse_fn1(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
+{
+ struct connections_data crec;
+
+ if (dbuf.dsize != sizeof(crec))
+ return 0;
+
+ memcpy(&crec, dbuf.dptr, sizeof(crec));
+
+ if (crec.cnum == -1)
+ return 0;
+
+ if (!process_exists(crec.pid) || !Ucrit_checkUsername(uidtoname(crec.uid))) {
+ return 0;
+ }
+
+ d_printf("%-10.10s %5d %-12s %s",
+ crec.name,(int)crec.pid,
+ crec.machine,
+ asctime(LocalTime(&crec.start)));
+
+ return 0;
+}
+
+static int traverse_sessionid(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
+{
+ struct sessionid sessionid;
+
+ if (dbuf.dsize != sizeof(sessionid))
+ return 0;
+
+ memcpy(&sessionid, dbuf.dptr, sizeof(sessionid));
+
+ if (!process_exists(sessionid.pid) || !Ucrit_checkUsername(uidtoname(sessionid.uid))) {
+ return 0;
+ }
+
+ Ucrit_addPid( sessionid.pid );
+
+ d_printf("%5d %-12s %-12s %-12s (%s)\n",
+ (int)sessionid.pid, uidtoname(sessionid.uid), gidtoname(sessionid.gid),
+ sessionid.remote_machine, sessionid.hostname);
+
+ return 0;
+}
+
+
+
+
+ int main(int argc, char *argv[])
+{
+ int c;
+ static int profile_only = 0;
+ TDB_CONTEXT *tdb;
+ poptContext pc;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"processes", 'p', POPT_ARG_NONE, &processes_only, 'p', "Show processes only" },
+ {"verbose", 'v', POPT_ARG_NONE, &verbose, 'v', "Be verbose" },
+ {"locks", 'L', POPT_ARG_NONE, &locks_only, 'L', "Show locks only" },
+ {"shares", 'S', POPT_ARG_NONE, &shares_only, 'S', "Show shares only" },
+ {"user", 'u', POPT_ARG_STRING, 0, 'u', "Switch to user" },
+ {"brief", 'b', POPT_ARG_NONE, &brief, 'b', "Be brief" },
+#ifdef WITH_PROFILE
+ {"profile", 'P', POPT_ARG_NONE, &profile_only, 'P', "Do profiling" },
+#endif /* WITH_PROFILE */
+ {"byterange", 'B', POPT_ARG_NONE, &show_brl, 'B', "Include byte range locks"},
+ POPT_COMMON_SAMBA
+ POPT_TABLEEND
+ };
+
+ setup_logging(argv[0],True);
+
+ dbf = x_stderr;
+
+ if (getuid() != geteuid()) {
+ d_printf("smbstatus should not be run setuid\n");
+ return(1);
+ }
+
+ pc = poptGetContext(NULL, argc, (const char **) argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ while ((c = poptGetNextOpt(pc)) != -1) {
+ switch (c) {
+ case 'u':
+ Ucrit_addUsername(poptGetOptArg(pc));
+ break;
+ }
+ }
+
+ if (verbose) {
+ d_printf("using configfile = %s\n", dyn_CONFIGFILE);
+ }
+
+ if (!lp_load(dyn_CONFIGFILE,False,False,False)) {
+ fprintf(stderr, "Can't load %s - run testparm to debug it\n", dyn_CONFIGFILE);
+ return (-1);
+ }
+
+ if (profile_only) {
+ return profile_dump();
+ }
+
+ tdb = tdb_open_log(lock_path("sessionid.tdb"), 0, TDB_DEFAULT, O_RDONLY, 0);
+ if (!tdb) {
+ d_printf("sessionid.tdb not initialised\n");
+ } else {
+ if (locks_only) goto locks;
+
+ d_printf("\nSamba version %s\n",SAMBA_VERSION_STRING);
+ d_printf("PID Username Group Machine \n");
+ d_printf("-------------------------------------------------------------------\n");
+
+ tdb_traverse(tdb, traverse_sessionid, NULL);
+ tdb_close(tdb);
+ }
+
+ tdb = tdb_open_log(lock_path("connections.tdb"), 0, TDB_DEFAULT, O_RDONLY, 0);
+ if (!tdb) {
+ d_printf("%s not initialised\n", lock_path("connections.tdb"));
+ d_printf("This is normal if an SMB client has never connected to your server.\n");
+ } else {
+ if (verbose) {
+ d_printf("Opened %s\n", lock_path("connections.tdb"));
+ }
+
+ if (brief)
+ exit(0);
+
+ d_printf("\nService pid machine Connected at\n");
+ d_printf("-------------------------------------------------------\n");
+
+ tdb_traverse(tdb, traverse_fn1, NULL);
+ tdb_close(tdb);
+ }
+
+ locks:
+ if (processes_only) exit(0);
+
+ if (!shares_only) {
+ int ret;
+
+ if (!locking_init(1)) {
+ d_printf("Can't initialise locking module - exiting\n");
+ exit(1);
+ }
+
+ ret = share_mode_forall(print_share_mode);
+
+ if (ret == 0) {
+ d_printf("No locked files\n");
+ } else if (ret == -1) {
+ d_printf("locked file list truncated\n");
+ }
+
+ d_printf("\n");
+
+ if (show_brl) {
+ brl_forall(print_brl);
+ }
+
+ locking_end();
+ }
+
+ return (0);
+}
diff --git a/source/utils/testparm.c b/source/utils/testparm.c
new file mode 100644
index 00000000000..9db6d538d24
--- /dev/null
+++ b/source/utils/testparm.c
@@ -0,0 +1,367 @@
+/*
+ Unix SMB/CIFS implementation.
+ Test validity of smb.conf
+ Copyright (C) Karl Auer 1993, 1994-1998
+
+ Extensively modified by Andrew Tridgell, 1995
+ Converted to popt by Jelmer Vernooij (jelmer@nl.linux.org), 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ * Testbed for loadparm.c/params.c
+ *
+ * This module simply loads a specified configuration file and
+ * if successful, dumps it's contents to stdout. Note that the
+ * operation is performed with DEBUGLEVEL at 3.
+ *
+ * Useful for a quick 'syntax check' of a configuration file.
+ *
+ */
+
+#include "includes.h"
+
+extern BOOL AllowDebugChange;
+
+/***********************************************
+ Here we do a set of 'hard coded' checks for bad
+ configuration settings.
+************************************************/
+
+static int do_global_checks(void)
+{
+ int ret = 0;
+ SMB_STRUCT_STAT st;
+
+ if (lp_security() >= SEC_DOMAIN && !lp_encrypted_passwords()) {
+ fprintf(stderr, "ERROR: in 'security=domain' mode the 'encrypt passwords' parameter must always be set to 'true'.\n");
+ ret = 1;
+ }
+
+ if (lp_wins_support() && lp_wins_server_list()) {
+ fprintf(stderr, "ERROR: both 'wins support = true' and 'wins server = <server list>' \
+cannot be set in the smb.conf file. nmbd will abort with this setting.\n");
+ ret = 1;
+ }
+
+ if (!directory_exist(lp_lockdir(), &st)) {
+ fprintf(stderr, "ERROR: lock directory %s does not exist\n",
+ lp_lockdir());
+ ret = 1;
+ } else if ((st.st_mode & 0777) != 0755) {
+ fprintf(stderr, "WARNING: lock directory %s should have permissions 0755 for browsing to work\n",
+ lp_lockdir());
+ ret = 1;
+ }
+
+ if (!directory_exist(lp_piddir(), &st)) {
+ fprintf(stderr, "ERROR: pid directory %s does not exist\n",
+ lp_piddir());
+ ret = 1;
+ }
+
+ /*
+ * Password server sanity checks.
+ */
+
+ if((lp_security() == SEC_SERVER || lp_security() >= SEC_DOMAIN) && !lp_passwordserver()) {
+ pstring sec_setting;
+ if(lp_security() == SEC_SERVER)
+ pstrcpy(sec_setting, "server");
+ else if(lp_security() == SEC_DOMAIN)
+ pstrcpy(sec_setting, "domain");
+
+ fprintf(stderr, "ERROR: The setting 'security=%s' requires the 'password server' parameter be set \
+to a valid password server.\n", sec_setting );
+ ret = 1;
+ }
+
+
+ /*
+ * Check 'hosts equiv' and 'use rhosts' compatibility with 'hostname lookup' value.
+ */
+
+ if(*lp_hosts_equiv() && !lp_hostname_lookups()) {
+ fprintf(stderr, "ERROR: The setting 'hosts equiv = %s' requires that 'hostname lookups = yes'.\n", lp_hosts_equiv());
+ ret = 1;
+ }
+
+ /*
+ * Password chat sanity checks.
+ */
+
+ if(lp_security() == SEC_USER && lp_unix_password_sync()) {
+
+ /*
+ * Check that we have a valid lp_passwd_program() if not using pam.
+ */
+
+#ifdef WITH_PAM
+ if (!lp_pam_password_change()) {
+#endif
+
+ if(lp_passwd_program() == NULL) {
+ fprintf( stderr, "ERROR: the 'unix password sync' parameter is set and there is no valid 'passwd program' \
+parameter.\n" );
+ ret = 1;
+ } else {
+ pstring passwd_prog;
+ pstring truncated_prog;
+ const char *p;
+
+ pstrcpy( passwd_prog, lp_passwd_program());
+ p = passwd_prog;
+ *truncated_prog = '\0';
+ next_token(&p, truncated_prog, NULL, sizeof(pstring));
+
+ if(access(truncated_prog, F_OK) == -1) {
+ fprintf(stderr, "ERROR: the 'unix password sync' parameter is set and the 'passwd program' (%s) \
+cannot be executed (error was %s).\n", truncated_prog, strerror(errno) );
+ ret = 1;
+ }
+ }
+
+#ifdef WITH_PAM
+ }
+#endif
+
+ if(lp_passwd_chat() == NULL) {
+ fprintf(stderr, "ERROR: the 'unix password sync' parameter is set and there is no valid 'passwd chat' \
+parameter.\n");
+ ret = 1;
+ }
+
+ /*
+ * Check that we have a valid script and that it hasn't
+ * been written to expect the old password.
+ */
+
+ if(lp_encrypted_passwords()) {
+ if(strstr_m( lp_passwd_chat(), "%o")!=NULL) {
+ fprintf(stderr, "ERROR: the 'passwd chat' script [%s] expects to use the old plaintext password \
+via the %%o substitution. With encrypted passwords this is not possible.\n", lp_passwd_chat() );
+ ret = 1;
+ }
+ }
+ }
+
+ if (strlen(lp_winbind_separator()) != 1) {
+ fprintf(stderr,"ERROR: the 'winbind separator' parameter must be a single character.\n");
+ ret = 1;
+ }
+
+ if (*lp_winbind_separator() == '+') {
+ fprintf(stderr,"'winbind separator = +' might cause problems with group membership.\n");
+ }
+
+ if (lp_algorithmic_rid_base() < BASE_RID) {
+ /* Try to prevent admin foot-shooting, we can't put algorithmic
+ rids below 1000, that's the 'well known RIDs' on NT */
+ fprintf(stderr,"'algorithmic rid base' must be equal to or above %lu\n", BASE_RID);
+ }
+
+ if (lp_algorithmic_rid_base() & 1) {
+ fprintf(stderr,"'algorithmic rid base' must be even.\n");
+ }
+
+#ifndef HAVE_DLOPEN
+ if (lp_preload_modules()) {
+ fprintf(stderr,"WARNING: 'preload modules = ' set while loading plugins not supported.\n");
+ }
+#endif
+
+ if (!lp_passdb_backend()) {
+ fprintf(stderr,"ERROR: passdb backend must have a value or be left out\n");
+ }
+
+ return ret;
+}
+
+ int main(int argc, const char *argv[])
+{
+ const char *config_file = dyn_CONFIGFILE;
+ int s;
+ static BOOL silent_mode = False;
+ int ret = 0;
+ poptContext pc;
+ static const char *term_code = "";
+ static char *new_local_machine = NULL;
+ const char *cname;
+ const char *caddr;
+ static int show_defaults;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"suppress-prompt", 's', POPT_ARG_VAL, &silent_mode, 1, "Suppress prompt for enter"},
+ {"verbose", 'v', POPT_ARG_NONE, &show_defaults, 1, "Show default options too"},
+ {"server", 'L',POPT_ARG_STRING, &new_local_machine, 0, "Set %%L macro to servername\n"},
+ {"encoding", 't', POPT_ARG_STRING, &term_code, 0, "Print parameters with encoding"},
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ pc = poptGetContext(NULL, argc, argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+ poptSetOtherOptionHelp(pc, "[OPTION...] <config-file> [host-name] [host-ip]");
+
+ while(poptGetNextOpt(pc) != -1);
+
+ setup_logging(poptGetArg(pc), True);
+
+ if (poptPeekArg(pc))
+ config_file = poptGetArg(pc);
+
+ cname = poptGetArg(pc);
+ caddr = poptGetArg(pc);
+
+ if ( cname && ! caddr ) {
+ printf ( "ERROR: You must specify both a machine name and an IP address.\n" );
+ return(1);
+ }
+
+ if (new_local_machine) {
+ set_local_machine_name(new_local_machine, True);
+ }
+
+ dbf = x_stderr;
+ DEBUGLEVEL = 2;
+ AllowDebugChange = False;
+
+ fprintf(stderr,"Load smb config files from %s\n",config_file);
+
+ if (!lp_load(config_file,False,True,False)) {
+ fprintf(stderr,"Error loading services.\n");
+ return(1);
+ }
+
+ fprintf(stderr,"Loaded services file OK.\n");
+
+ ret = do_global_checks();
+
+ for (s=0;s<1000;s++) {
+ if (VALID_SNUM(s))
+ if (strlen(lp_servicename(s)) > 12) {
+ fprintf(stderr, "WARNING: You have some share names that are longer than 12 characters.\n" );
+ fprintf(stderr, "These may not be accessible to some older clients.\n" );
+ fprintf(stderr, "(Eg. Windows9x, WindowsMe, and smbclient prior to Samba 3.0.)\n" );
+ break;
+ }
+ }
+
+ for (s=0;s<1000;s++) {
+ if (VALID_SNUM(s)) {
+ const char **deny_list = lp_hostsdeny(s);
+ const char **allow_list = lp_hostsallow(s);
+ int i;
+ if(deny_list) {
+ for (i=0; deny_list[i]; i++) {
+ char *hasstar = strchr_m(deny_list[i], '*');
+ char *hasquery = strchr_m(deny_list[i], '?');
+ if(hasstar || hasquery) {
+ fprintf(stderr,"Invalid character %c in hosts deny list (%s) for service %s.\n",
+ hasstar ? *hasstar : *hasquery, deny_list[i], lp_servicename(s) );
+ }
+ }
+ }
+
+ if(allow_list) {
+ for (i=0; allow_list[i]; i++) {
+ char *hasstar = strchr_m(allow_list[i], '*');
+ char *hasquery = strchr_m(allow_list[i], '?');
+ if(hasstar || hasquery) {
+ fprintf(stderr,"Invalid character %c in hosts allow list (%s) for service %s.\n",
+ hasstar ? *hasstar : *hasquery, allow_list[i], lp_servicename(s) );
+ }
+ }
+ }
+
+ if(lp_level2_oplocks(s) && !lp_oplocks(s)) {
+ fprintf(stderr,"Invalid combination of parameters for service %s. \
+ Level II oplocks can only be set if oplocks are also set.\n",
+ lp_servicename(s) );
+ }
+
+ if (lp_map_hidden(s) && !(lp_create_mask(s) & S_IXOTH)) {
+ fprintf(stderr,"Invalid combination of parameters for service %s. \
+ Map hidden can only work if create mask includes octal 01 (S_IXOTH).\n",
+ lp_servicename(s) );
+ }
+ if (lp_map_hidden(s) && (lp_force_create_mode(s) & S_IXOTH)) {
+ fprintf(stderr,"Invalid combination of parameters for service %s. \
+ Map hidden can only work if force create mode excludes octal 01 (S_IXOTH).\n",
+ lp_servicename(s) );
+ }
+ if (lp_map_system(s) && !(lp_create_mask(s) & S_IXGRP)) {
+ fprintf(stderr,"Invalid combination of parameters for service %s. \
+ Map system can only work if create mask includes octal 010 (S_IXGRP).\n",
+ lp_servicename(s) );
+ }
+ if (lp_map_system(s) && (lp_force_create_mode(s) & S_IXGRP)) {
+ fprintf(stderr,"Invalid combination of parameters for service %s. \
+ Map system can only work if force create mode excludes octal 010 (S_IXGRP).\n",
+ lp_servicename(s) );
+ }
+ }
+ }
+
+
+ if (!silent_mode) {
+ fprintf(stderr,"Server role: ");
+ switch(lp_server_role()) {
+ case ROLE_STANDALONE:
+ fprintf(stderr,"ROLE_STANDALONE\n");
+ break;
+ case ROLE_DOMAIN_MEMBER:
+ fprintf(stderr,"ROLE_DOMAIN_MEMBER\n");
+ break;
+ case ROLE_DOMAIN_BDC:
+ fprintf(stderr,"ROLE_DOMAIN_BDC\n");
+ break;
+ case ROLE_DOMAIN_PDC:
+ fprintf(stderr,"ROLE_DOMAIN_PDC\n");
+ break;
+ default:
+ fprintf(stderr,"Unknown -- internal error?\n");
+ break;
+ }
+ }
+
+ if (!cname) {
+ if (!silent_mode) {
+ fprintf(stderr,"Press enter to see a dump of your service definitions\n");
+ fflush(stdout);
+ getc(stdin);
+ }
+ lp_dump(stdout, show_defaults, lp_numservices());
+ }
+
+ if(cname && caddr){
+ /* this is totally ugly, a real `quick' hack */
+ for (s=0;s<1000;s++) {
+ if (VALID_SNUM(s)) {
+ if (allow_access(lp_hostsdeny(-1), lp_hostsallow(-1), cname, caddr)
+ && allow_access(lp_hostsdeny(s), lp_hostsallow(s), cname, caddr)) {
+ fprintf(stderr,"Allow connection from %s (%s) to %s\n",
+ cname,caddr,lp_servicename(s));
+ } else {
+ fprintf(stderr,"Deny connection from %s (%s) to %s\n",
+ cname,caddr,lp_servicename(s));
+ }
+ }
+ }
+ }
+ return(ret);
+}
diff --git a/source/utils/testprns.c b/source/utils/testprns.c
new file mode 100644
index 00000000000..7e52b86afb6
--- /dev/null
+++ b/source/utils/testprns.c
@@ -0,0 +1,61 @@
+/*
+ Unix SMB/CIFS implementation.
+ test printer setup
+ Copyright (C) Karl Auer 1993, 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ * Testbed for pcap.c
+ *
+ * This module simply checks a given printer name against the compiled-in
+ * printcap file.
+ *
+ * The operation is performed with DEBUGLEVEL at 3.
+ *
+ * Useful for a quick check of a printcap file.
+ *
+ */
+
+#include "includes.h"
+
+int main(int argc, char *argv[])
+{
+ const char *pszTemp;
+
+ setup_logging(argv[0],True);
+
+ if (argc < 2 || argc > 3)
+ printf("Usage: testprns printername [printcapfile]\n");
+ else
+ {
+ dbf = x_fopen("test.log", O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ if (dbf == NULL) {
+ printf("Unable to open logfile.\n");
+ } else {
+ DEBUGLEVEL = 3;
+ pszTemp = (argc < 3) ? PRINTCAP_NAME : argv[2];
+ printf("Looking for printer %s in printcap file %s\n",
+ argv[1], pszTemp);
+ if (!pcap_printername_ok(argv[1], pszTemp))
+ printf("Printer name %s is not valid.\n", argv[1]);
+ else
+ printf("Printer name %s is valid.\n", argv[1]);
+ x_fclose(dbf);
+ }
+ }
+ return (0);
+}
diff --git a/source/web/.cvsignore b/source/web/.cvsignore
new file mode 100644
index 00000000000..ed29eafc6b1
--- /dev/null
+++ b/source/web/.cvsignore
@@ -0,0 +1 @@
+swat_proto.h \ No newline at end of file
diff --git a/source/web/cgi.c b/source/web/cgi.c
new file mode 100644
index 00000000000..07b9f52ff77
--- /dev/null
+++ b/source/web/cgi.c
@@ -0,0 +1,626 @@
+/*
+ some simple CGI helper routines
+ Copyright (C) Andrew Tridgell 1997-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+#include "includes.h"
+#include "../web/swat_proto.h"
+
+#define MAX_VARIABLES 10000
+
+/* set the expiry on fixed pages */
+#define EXPIRY_TIME (60*60*24*7)
+
+#ifdef DEBUG_COMMENTS
+extern void print_title(char *fmt, ...);
+#endif
+
+struct var {
+ char *name;
+ char *value;
+};
+
+static struct var variables[MAX_VARIABLES];
+static int num_variables;
+static int content_length;
+static int request_post;
+static char *query_string;
+static const char *baseurl;
+static char *pathinfo;
+static char *C_user;
+static BOOL inetd_server;
+static BOOL got_request;
+
+static char *grab_line(FILE *f, int *cl)
+{
+ char *ret = NULL;
+ int i = 0;
+ int len = 0;
+
+ while ((*cl)) {
+ int c;
+
+ if (i == len) {
+ char *ret2;
+ if (len == 0) len = 1024;
+ else len *= 2;
+ ret2 = (char *)Realloc(ret, len);
+ if (!ret2) return ret;
+ ret = ret2;
+ }
+
+ c = fgetc(f);
+ (*cl)--;
+
+ if (c == EOF) {
+ (*cl) = 0;
+ break;
+ }
+
+ if (c == '\r') continue;
+
+ if (strchr_m("\n&", c)) break;
+
+ ret[i++] = c;
+
+ }
+
+
+ ret[i] = 0;
+ return ret;
+}
+
+/**
+ URL encoded strings can have a '+', which should be replaced with a space
+
+ (This was in rfc1738_unescape(), but that broke the squid helper)
+**/
+
+static void plus_to_space_unescape(char *buf)
+{
+ char *p=buf;
+
+ while ((p=strchr_m(p,'+')))
+ *p = ' ';
+}
+
+/***************************************************************************
+ load all the variables passed to the CGI program. May have multiple variables
+ with the same name and the same or different values. Takes a file parameter
+ for simulating CGI invocation eg loading saved preferences.
+ ***************************************************************************/
+void cgi_load_variables(void)
+{
+ static char *line;
+ char *p, *s, *tok;
+ int len, i;
+ FILE *f = stdin;
+
+#ifdef DEBUG_COMMENTS
+ char dummy[100]="";
+ print_title(dummy);
+ d_printf("<!== Start dump in cgi_load_variables() %s ==>\n",__FILE__);
+#endif
+
+ if (!content_length) {
+ p = getenv("CONTENT_LENGTH");
+ len = p?atoi(p):0;
+ } else {
+ len = content_length;
+ }
+
+
+ if (len > 0 &&
+ (request_post ||
+ ((s=getenv("REQUEST_METHOD")) &&
+ strequal(s,"POST")))) {
+ while (len && (line=grab_line(f, &len))) {
+ p = strchr_m(line,'=');
+ if (!p) continue;
+
+ *p = 0;
+
+ variables[num_variables].name = strdup(line);
+ variables[num_variables].value = strdup(p+1);
+
+ SAFE_FREE(line);
+
+ if (!variables[num_variables].name ||
+ !variables[num_variables].value)
+ continue;
+
+ plus_to_space_unescape(variables[num_variables].value);
+ rfc1738_unescape(variables[num_variables].value);
+ plus_to_space_unescape(variables[num_variables].name);
+ rfc1738_unescape(variables[num_variables].name);
+
+#ifdef DEBUG_COMMENTS
+ printf("<!== POST var %s has value \"%s\" ==>\n",
+ variables[num_variables].name,
+ variables[num_variables].value);
+#endif
+
+ num_variables++;
+ if (num_variables == MAX_VARIABLES) break;
+ }
+ }
+
+ fclose(stdin);
+ open("/dev/null", O_RDWR);
+
+ if ((s=query_string) || (s=getenv("QUERY_STRING"))) {
+ for (tok=strtok(s,"&;");tok;tok=strtok(NULL,"&;")) {
+ p = strchr_m(tok,'=');
+ if (!p) continue;
+
+ *p = 0;
+
+ variables[num_variables].name = strdup(tok);
+ variables[num_variables].value = strdup(p+1);
+
+ if (!variables[num_variables].name ||
+ !variables[num_variables].value)
+ continue;
+
+ plus_to_space_unescape(variables[num_variables].value);
+ rfc1738_unescape(variables[num_variables].value);
+ plus_to_space_unescape(variables[num_variables].name);
+ rfc1738_unescape(variables[num_variables].name);
+
+#ifdef DEBUG_COMMENTS
+ printf("<!== Commandline var %s has value \"%s\" ==>\n",
+ variables[num_variables].name,
+ variables[num_variables].value);
+#endif
+ num_variables++;
+ if (num_variables == MAX_VARIABLES) break;
+ }
+
+ }
+#ifdef DEBUG_COMMENTS
+ printf("<!== End dump in cgi_load_variables() ==>\n");
+#endif
+
+ /* variables from the client are in display charset - convert them
+ to our internal charset before use */
+ for (i=0;i<num_variables;i++) {
+ pstring dest;
+
+ convert_string(CH_DISPLAY, CH_UNIX,
+ variables[i].name, -1,
+ dest, sizeof(dest), True);
+ free(variables[i].name);
+ variables[i].name = strdup(dest);
+
+ convert_string(CH_DISPLAY, CH_UNIX,
+ variables[i].value, -1,
+ dest, sizeof(dest), True);
+ free(variables[i].value);
+ variables[i].value = strdup(dest);
+ }
+}
+
+
+/***************************************************************************
+ find a variable passed via CGI
+ Doesn't quite do what you think in the case of POST text variables, because
+ if they exist they might have a value of "" or even " ", depending on the
+ browser. Also doesn't allow for variables[] containing multiple variables
+ with the same name and the same or different values.
+ ***************************************************************************/
+const char *cgi_variable(const char *name)
+{
+ int i;
+
+ for (i=0;i<num_variables;i++)
+ if (strcmp(variables[i].name, name) == 0)
+ return variables[i].value;
+ return NULL;
+}
+
+/***************************************************************************
+tell a browser about a fatal error in the http processing
+ ***************************************************************************/
+static void cgi_setup_error(const char *err, const char *header, const char *info)
+{
+ if (!got_request) {
+ /* damn browsers don't like getting cut off before they give a request */
+ char line[1024];
+ while (fgets(line, sizeof(line)-1, stdin)) {
+ if (strnequal(line,"GET ", 4) ||
+ strnequal(line,"POST ", 5) ||
+ strnequal(line,"PUT ", 4)) {
+ break;
+ }
+ }
+ }
+
+ d_printf("HTTP/1.0 %s\r\n%sConnection: close\r\nContent-Type: text/html\r\n\r\n<HTML><HEAD><TITLE>%s</TITLE></HEAD><BODY><H1>%s</H1>%s<p></BODY></HTML>\r\n\r\n", err, header, err, err, info);
+ fclose(stdin);
+ fclose(stdout);
+ exit(0);
+}
+
+
+/***************************************************************************
+tell a browser about a fatal authentication error
+ ***************************************************************************/
+static void cgi_auth_error(void)
+{
+ if (inetd_server) {
+ cgi_setup_error("401 Authorization Required",
+ "WWW-Authenticate: Basic realm=\"SWAT\"\r\n",
+ "You must be authenticated to use this service");
+ } else {
+ printf("Content-Type: text/html\r\n");
+
+ printf("\r\n<HTML><HEAD><TITLE>SWAT</TITLE></HEAD>\n");
+ printf("<BODY><H1>Installation Error</H1>\n");
+ printf("SWAT must be installed via inetd. It cannot be run as a CGI script<p>\n");
+ printf("</BODY></HTML>\r\n");
+ }
+ exit(0);
+}
+
+/***************************************************************************
+authenticate when we are running as a CGI
+ ***************************************************************************/
+static void cgi_web_auth(void)
+{
+ const char *user = getenv("REMOTE_USER");
+ struct passwd *pwd;
+ const char *head = "Content-Type: text/html\r\n\r\n<HTML><BODY><H1>SWAT installation Error</H1>\n";
+ const char *tail = "</BODY></HTML>\r\n";
+
+ if (!user) {
+ printf("%sREMOTE_USER not set. Not authenticated by web server.<br>%s\n",
+ head, tail);
+ exit(0);
+ }
+
+ pwd = getpwnam_alloc(user);
+ if (!pwd) {
+ printf("%sCannot find user %s<br>%s\n", head, user, tail);
+ exit(0);
+ }
+
+ setuid(0);
+ setuid(pwd->pw_uid);
+ if (geteuid() != pwd->pw_uid || getuid() != pwd->pw_uid) {
+ printf("%sFailed to become user %s - uid=%d/%d<br>%s\n",
+ head, user, (int)geteuid(), (int)getuid(), tail);
+ exit(0);
+ }
+ passwd_free(&pwd);
+}
+
+
+/***************************************************************************
+handle a http authentication line
+ ***************************************************************************/
+static BOOL cgi_handle_authorization(char *line)
+{
+ char *p;
+ fstring user, user_pass;
+ struct passwd *pass = NULL;
+
+ if (!strnequal(line,"Basic ", 6)) {
+ goto err;
+ }
+ line += 6;
+ while (line[0] == ' ') line++;
+ base64_decode_inplace(line);
+ if (!(p=strchr_m(line,':'))) {
+ /*
+ * Always give the same error so a cracker
+ * cannot tell why we fail.
+ */
+ goto err;
+ }
+ *p = 0;
+
+ convert_string(CH_DISPLAY, CH_UNIX,
+ line, -1,
+ user, sizeof(user), True);
+
+ convert_string(CH_DISPLAY, CH_UNIX,
+ p+1, -1,
+ user_pass, sizeof(user_pass), True);
+
+ /*
+ * Try and get the user from the UNIX password file.
+ */
+
+ pass = getpwnam_alloc(user);
+
+ /*
+ * Validate the password they have given.
+ */
+
+ if NT_STATUS_IS_OK(pass_check(pass, user, user_pass,
+ strlen(user_pass), NULL, False)) {
+
+ if (pass) {
+ /*
+ * Password was ok.
+ */
+
+ if ( initgroups(pass->pw_name, pass->pw_gid) != 0 )
+ goto err;
+
+ become_user_permanently(pass->pw_uid, pass->pw_gid);
+
+ /* Save the users name */
+ C_user = strdup(user);
+ passwd_free(&pass);
+ return True;
+ }
+ }
+
+err:
+ cgi_setup_error("401 Bad Authorization",
+ "WWW-Authenticate: Basic realm=\"SWAT\"\r\n",
+ "username or password incorrect");
+
+ passwd_free(&pass);
+ return False;
+}
+
+/***************************************************************************
+is this root?
+ ***************************************************************************/
+BOOL am_root(void)
+{
+ if (geteuid() == 0) {
+ return( True);
+ } else {
+ return( False);
+ }
+}
+
+/***************************************************************************
+return a ptr to the users name
+ ***************************************************************************/
+char *cgi_user_name(void)
+{
+ return(C_user);
+}
+
+
+/***************************************************************************
+handle a file download
+ ***************************************************************************/
+static void cgi_download(char *file)
+{
+ SMB_STRUCT_STAT st;
+ char buf[1024];
+ int fd, l, i;
+ char *p;
+ char *lang;
+
+ /* sanitise the filename */
+ for (i=0;file[i];i++) {
+ if (!isalnum((int)file[i]) && !strchr_m("/.-_", file[i])) {
+ cgi_setup_error("404 File Not Found","",
+ "Illegal character in filename");
+ }
+ }
+
+ if (!file_exist(file, &st)) {
+ cgi_setup_error("404 File Not Found","",
+ "The requested file was not found");
+ }
+
+ fd = web_open(file,O_RDONLY,0);
+ if (fd == -1) {
+ cgi_setup_error("404 File Not Found","",
+ "The requested file was not found");
+ }
+ printf("HTTP/1.0 200 OK\r\n");
+ if ((p=strrchr_m(file,'.'))) {
+ if (strcmp(p,".gif")==0) {
+ printf("Content-Type: image/gif\r\n");
+ } else if (strcmp(p,".jpg")==0) {
+ printf("Content-Type: image/jpeg\r\n");
+ } else if (strcmp(p,".txt")==0) {
+ printf("Content-Type: text/plain\r\n");
+ } else {
+ printf("Content-Type: text/html\r\n");
+ }
+ }
+ printf("Expires: %s\r\n", http_timestring(time(NULL)+EXPIRY_TIME));
+
+ lang = lang_tdb_current();
+ if (lang) {
+ printf("Content-Language: %s\r\n", lang);
+ }
+
+ printf("Content-Length: %d\r\n\r\n", (int)st.st_size);
+ while ((l=read(fd,buf,sizeof(buf)))>0) {
+ fwrite(buf, 1, l, stdout);
+ }
+ close(fd);
+ exit(0);
+}
+
+
+
+
+/**
+ * @brief Setup the CGI framework.
+ *
+ * Setup the cgi framework, handling the possibility that this program
+ * is either run as a true CGI program with a gateway to a web server, or
+ * is itself a mini web server.
+ **/
+void cgi_setup(const char *rootdir, int auth_required)
+{
+ BOOL authenticated = False;
+ char line[1024];
+ char *url=NULL;
+ char *p;
+ char *lang;
+
+ if (chdir(rootdir)) {
+ cgi_setup_error("500 Server Error", "",
+ "chdir failed - the server is not configured correctly");
+ }
+
+ /* Handle the possibility we might be running as non-root */
+ sec_init();
+
+ if ((lang=getenv("HTTP_ACCEPT_LANGUAGE"))) {
+ /* if running as a cgi program */
+ web_set_lang(lang);
+ }
+
+ /* maybe we are running under a web server */
+ if (getenv("CONTENT_LENGTH") || getenv("REQUEST_METHOD")) {
+ if (auth_required) {
+ cgi_web_auth();
+ }
+ return;
+ }
+
+ inetd_server = True;
+
+ if (!check_access(1, lp_hostsallow(-1), lp_hostsdeny(-1))) {
+ cgi_setup_error("403 Forbidden", "",
+ "Samba is configured to deny access from this client\n<br>Check your \"hosts allow\" and \"hosts deny\" options in smb.conf ");
+ }
+
+ /* we are a mini-web server. We need to read the request from stdin
+ and handle authentication etc */
+ while (fgets(line, sizeof(line)-1, stdin)) {
+ if (line[0] == '\r' || line[0] == '\n') break;
+ if (strnequal(line,"GET ", 4)) {
+ got_request = True;
+ url = strdup(&line[4]);
+ } else if (strnequal(line,"POST ", 5)) {
+ got_request = True;
+ request_post = 1;
+ url = strdup(&line[5]);
+ } else if (strnequal(line,"PUT ", 4)) {
+ got_request = True;
+ cgi_setup_error("400 Bad Request", "",
+ "This server does not accept PUT requests");
+ } else if (strnequal(line,"Authorization: ", 15)) {
+ authenticated = cgi_handle_authorization(&line[15]);
+ } else if (strnequal(line,"Content-Length: ", 16)) {
+ content_length = atoi(&line[16]);
+ } else if (strnequal(line,"Accept-Language: ", 17)) {
+ web_set_lang(&line[17]);
+ }
+ /* ignore all other requests! */
+ }
+
+ if (auth_required && !authenticated) {
+ cgi_auth_error();
+ }
+
+ if (!url) {
+ cgi_setup_error("400 Bad Request", "",
+ "You must specify a GET or POST request");
+ }
+
+ /* trim the URL */
+ if ((p = strchr_m(url,' ')) || (p=strchr_m(url,'\t'))) {
+ *p = 0;
+ }
+ while (*url && strchr_m("\r\n",url[strlen(url)-1])) {
+ url[strlen(url)-1] = 0;
+ }
+
+ /* anything following a ? in the URL is part of the query string */
+ if ((p=strchr_m(url,'?'))) {
+ query_string = p+1;
+ *p = 0;
+ }
+
+ string_sub(url, "/swat/", "", 0);
+
+ if (url[0] != '/' && strstr(url,"..")==0 && file_exist(url, NULL)) {
+ cgi_download(url);
+ }
+
+ printf("HTTP/1.0 200 OK\r\nConnection: close\r\n");
+ printf("Date: %s\r\n", http_timestring(time(NULL)));
+ baseurl = "";
+ pathinfo = url+1;
+}
+
+
+/***************************************************************************
+return the current pages URL
+ ***************************************************************************/
+const char *cgi_baseurl(void)
+{
+ if (inetd_server) {
+ return baseurl;
+ }
+ return getenv("SCRIPT_NAME");
+}
+
+/***************************************************************************
+return the current pages path info
+ ***************************************************************************/
+const char *cgi_pathinfo(void)
+{
+ char *r;
+ if (inetd_server) {
+ return pathinfo;
+ }
+ r = getenv("PATH_INFO");
+ if (!r) return "";
+ if (*r == '/') r++;
+ return r;
+}
+
+/***************************************************************************
+return the hostname of the client
+ ***************************************************************************/
+char *cgi_remote_host(void)
+{
+ if (inetd_server) {
+ return get_peer_name(1,False);
+ }
+ return getenv("REMOTE_HOST");
+}
+
+/***************************************************************************
+return the hostname of the client
+ ***************************************************************************/
+char *cgi_remote_addr(void)
+{
+ if (inetd_server) {
+ return get_peer_addr(1);
+ }
+ return getenv("REMOTE_ADDR");
+}
+
+
+/***************************************************************************
+return True if the request was a POST
+ ***************************************************************************/
+BOOL cgi_waspost(void)
+{
+ if (inetd_server) {
+ return request_post;
+ }
+ return strequal(getenv("REQUEST_METHOD"), "POST");
+}
diff --git a/source/web/diagnose.c b/source/web/diagnose.c
new file mode 100644
index 00000000000..46432c41f85
--- /dev/null
+++ b/source/web/diagnose.c
@@ -0,0 +1,79 @@
+/*
+ Unix SMB/CIFS implementation.
+ diagnosis tools for web admin
+ Copyright (C) Andrew Tridgell 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "../web/swat_proto.h"
+
+#ifdef WITH_WINBIND
+
+NSS_STATUS winbindd_request(int req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response);
+
+/* check to see if winbind is running by pinging it */
+
+BOOL winbindd_running(void)
+{
+ return winbind_ping();
+}
+#endif
+
+/* check to see if nmbd is running on localhost by looking for a __SAMBA__
+ response */
+BOOL nmbd_running(void)
+{
+ extern struct in_addr loopback_ip;
+ int fd, count, flags;
+ struct in_addr *ip_list;
+
+ if ((fd = open_socket_in(SOCK_DGRAM, 0, 3,
+ interpret_addr("127.0.0.1"), True)) != -1) {
+ if ((ip_list = name_query(fd, "__SAMBA__", 0,
+ True, True, loopback_ip,
+ &count, &flags, NULL)) != NULL) {
+ SAFE_FREE(ip_list);
+ close(fd);
+ return True;
+ }
+ close (fd);
+ }
+
+ return False;
+}
+
+
+/* check to see if smbd is running on localhost by trying to open a connection
+ then closing it */
+BOOL smbd_running(void)
+{
+ static struct cli_state cli;
+ extern struct in_addr loopback_ip;
+
+ if (!cli_initialise(&cli))
+ return False;
+
+ if (!cli_connect(&cli, global_myname(), &loopback_ip)) {
+ cli_shutdown(&cli);
+ return False;
+ }
+
+ cli_shutdown(&cli);
+ return True;
+}
diff --git a/source/web/neg_lang.c b/source/web/neg_lang.c
new file mode 100644
index 00000000000..da974f78a4a
--- /dev/null
+++ b/source/web/neg_lang.c
@@ -0,0 +1,117 @@
+/*
+ Unix SMB/CIFS implementation.
+ SWAT language handling
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Created by Ryo Kawahara <rkawa@lbe.co.jp>
+*/
+
+#include "includes.h"
+#include "../web/swat_proto.h"
+
+/*
+ during a file download we first check to see if there is a language
+ specific file available. If there is then use that, otherwise
+ just open the specified file
+*/
+int web_open(const char *fname, int flags, mode_t mode)
+{
+ char *p = NULL;
+ char *lang = lang_tdb_current();
+ int fd;
+ if (lang) {
+ asprintf(&p, "lang/%s/%s", lang, fname);
+ if (p) {
+ fd = sys_open(p, flags, mode);
+ free(p);
+ if (fd != -1) {
+ return fd;
+ }
+ }
+ }
+
+ /* fall through to default name */
+ return sys_open(fname, flags, mode);
+}
+
+
+struct pri_list {
+ float pri;
+ char *string;
+};
+
+static int qsort_cmp_list(const void *x, const void *y) {
+ struct pri_list *a = (struct pri_list *)x;
+ struct pri_list *b = (struct pri_list *)y;
+ if (a->pri > b->pri) return -1;
+ if (a->pri == b->pri) return 0;
+ return 1;
+}
+
+/*
+ choose from a list of languages. The list can be comma or space
+ separated
+ Keep choosing until we get a hit
+ Changed to habdle priority -- Simo
+*/
+
+void web_set_lang(const char *lang_string)
+{
+ char **lang_list, **count;
+ struct pri_list *pl;
+ int lang_num, i;
+
+ /* build the lang list */
+ lang_list = str_list_make(lang_string, ", \t\r\n");
+ if (!lang_list) return;
+
+ /* sort the list by priority */
+ lang_num = 0;
+ count = lang_list;
+ while (*count && **count) {
+ count++;
+ lang_num++;
+ }
+ pl = (struct pri_list *)malloc(sizeof(struct pri_list) * lang_num);
+ for (i = 0; i < lang_num; i++) {
+ char *pri_code;
+ if ((pri_code=strstr(lang_list[i], ";q="))) {
+ *pri_code = '\0';
+ pri_code += 3;
+ sscanf(pri_code, "%f", &(pl[i].pri));
+ } else {
+ pl[i].pri = 1;
+ }
+ pl[i].string = strdup(lang_list[i]);
+ }
+ str_list_free(&lang_list);
+
+ qsort(pl, lang_num, sizeof(struct pri_list), &qsort_cmp_list);
+
+ /* it's not an error to not initialise - we just fall back to
+ the default */
+
+ for (i = 0; i < lang_num; i++) {
+ if (lang_tdb_init(pl[i].string)) break;
+ }
+
+ for (i = 0; i < lang_num; i++) {
+ SAFE_FREE(pl[i].string);
+ }
+ SAFE_FREE(pl);
+
+ return;
+}
diff --git a/source/web/startstop.c b/source/web/startstop.c
new file mode 100644
index 00000000000..93e8650c2bc
--- /dev/null
+++ b/source/web/startstop.c
@@ -0,0 +1,131 @@
+/*
+ Unix SMB/CIFS implementation.
+ start/stop nmbd and smbd
+ Copyright (C) Andrew Tridgell 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "../web/swat_proto.h"
+#include "dynconfig.h"
+
+
+/** Startup smbd from web interface. */
+void start_smbd(void)
+{
+ pstring binfile;
+
+ if (geteuid() != 0) return;
+
+ if (fork()) {
+ return;
+ }
+
+ slprintf(binfile, sizeof(pstring) - 1, "%s/smbd", dyn_SBINDIR);
+
+ become_daemon(True);
+
+ execl(binfile, binfile, "-D", NULL);
+
+ exit(0);
+}
+
+/* startup nmbd */
+void start_nmbd(void)
+{
+ pstring binfile;
+
+ if (geteuid() != 0) return;
+
+ if (fork()) {
+ return;
+ }
+
+ slprintf(binfile, sizeof(pstring) - 1, "%s/nmbd", dyn_SBINDIR);
+
+ become_daemon(True);
+
+ execl(binfile, binfile, "-D", NULL);
+
+ exit(0);
+}
+
+/** Startup winbindd from web interface. */
+void start_winbindd(void)
+{
+ pstring binfile;
+
+ if (geteuid() != 0) return;
+
+ if (fork()) {
+ return;
+ }
+
+ slprintf(binfile, sizeof(pstring) - 1, "%s/winbindd", dyn_SBINDIR);
+
+ become_daemon(True);
+
+ execl(binfile, binfile, NULL);
+
+ exit(0);
+}
+
+
+/* stop smbd */
+void stop_smbd(void)
+{
+ pid_t pid = pidfile_pid("smbd");
+
+ if (geteuid() != 0) return;
+
+ if (pid <= 0) return;
+
+ kill(pid, SIGTERM);
+}
+
+/* stop nmbd */
+void stop_nmbd(void)
+{
+ pid_t pid = pidfile_pid("nmbd");
+
+ if (geteuid() != 0) return;
+
+ if (pid <= 0) return;
+
+ kill(pid, SIGTERM);
+}
+#ifdef WITH_WINBIND
+/* stop winbindd */
+void stop_winbindd(void)
+{
+ pid_t pid = pidfile_pid("winbindd");
+
+ if (geteuid() != 0) return;
+
+ if (pid <= 0) return;
+
+ kill(pid, SIGTERM);
+}
+#endif
+/* kill a specified process */
+void kill_pid(pid_t pid)
+{
+ if (geteuid() != 0) return;
+
+ if (pid <= 0) return;
+
+ kill(pid, SIGTERM);
+}
diff --git a/source/web/statuspage.c b/source/web/statuspage.c
new file mode 100644
index 00000000000..3d70796830d
--- /dev/null
+++ b/source/web/statuspage.c
@@ -0,0 +1,441 @@
+/*
+ Unix SMB/CIFS implementation.
+ web status page
+ Copyright (C) Andrew Tridgell 1997-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "../web/swat_proto.h"
+
+#define PIDMAP struct PidMap
+
+/* how long to wait for start/stops to take effect */
+#define SLEEP_TIME 3
+
+PIDMAP {
+ PIDMAP *next, *prev;
+ pid_t pid;
+ char *machine;
+};
+
+static PIDMAP *pidmap;
+static int PID_or_Machine; /* 0 = show PID, else show Machine name */
+
+static pid_t smbd_pid;
+
+/* from 2nd call on, remove old list */
+static void initPid2Machine (void)
+{
+ /* show machine name rather PID on table "Open Files"? */
+ if (PID_or_Machine) {
+ PIDMAP *p;
+
+ for (p = pidmap; p != NULL; ) {
+ DLIST_REMOVE(pidmap, p);
+ SAFE_FREE(p->machine);
+ SAFE_FREE(p);
+ }
+
+ pidmap = NULL;
+ }
+}
+
+/* add new PID <-> Machine name mapping */
+static void addPid2Machine (pid_t pid, char *machine)
+{
+ /* show machine name rather PID on table "Open Files"? */
+ if (PID_or_Machine) {
+ PIDMAP *newmap;
+
+ if ((newmap = (PIDMAP *) malloc (sizeof (PIDMAP))) == NULL) {
+ /* XXX need error message for this?
+ if malloc fails, PID is always shown */
+ return;
+ }
+
+ newmap->pid = pid;
+ newmap->machine = strdup (machine);
+
+ DLIST_ADD(pidmap, newmap);
+ }
+}
+
+/* lookup PID <-> Machine name mapping */
+static char *mapPid2Machine (pid_t pid)
+{
+ static char pidbuf [64];
+ PIDMAP *map;
+
+ /* show machine name rather PID on table "Open Files"? */
+ if (PID_or_Machine) {
+ for (map = pidmap; map != NULL; map = map->next) {
+ if (pid == map->pid) {
+ if (map->machine == NULL) /* no machine name */
+ break; /* show PID */
+
+ return map->machine;
+ }
+ }
+ }
+
+ /* PID not in list or machine name NULL? return pid as string */
+ snprintf (pidbuf, sizeof (pidbuf) - 1, "%lu", (unsigned long)pid);
+ return pidbuf;
+}
+
+static char *tstring(time_t t)
+{
+ static pstring buf;
+ pstrcpy(buf, asctime(LocalTime(&t)));
+ all_string_sub(buf," ","&nbsp;",sizeof(buf));
+ return buf;
+}
+
+static void print_share_mode(share_mode_entry *e, char *fname)
+{
+ d_printf("<tr><td>%s</td>",_(mapPid2Machine(e->pid)));
+ d_printf("<td>");
+ switch ((e->share_mode>>4)&0xF) {
+ case DENY_NONE: d_printf("DENY_NONE"); break;
+ case DENY_ALL: d_printf("DENY_ALL "); break;
+ case DENY_DOS: d_printf("DENY_DOS "); break;
+ case DENY_READ: d_printf("DENY_READ "); break;
+ case DENY_WRITE:d_printf("DENY_WRITE "); break;
+ }
+ d_printf("</td>");
+
+ d_printf("<td>");
+ switch (e->share_mode&0xF) {
+ case 0: d_printf("%s", _("RDONLY ")); break;
+ case 1: d_printf("%s", _("WRONLY ")); break;
+ case 2: d_printf("%s", _("RDWR ")); break;
+ }
+ d_printf("</td>");
+
+ d_printf("<td>");
+ if((e->op_type &
+ (EXCLUSIVE_OPLOCK|BATCH_OPLOCK)) ==
+ (EXCLUSIVE_OPLOCK|BATCH_OPLOCK))
+ d_printf("EXCLUSIVE+BATCH ");
+ else if (e->op_type & EXCLUSIVE_OPLOCK)
+ d_printf("EXCLUSIVE ");
+ else if (e->op_type & BATCH_OPLOCK)
+ d_printf("BATCH ");
+ else if (e->op_type & LEVEL_II_OPLOCK)
+ d_printf("LEVEL_II ");
+ else
+ d_printf("NONE ");
+ d_printf("</td>");
+
+ d_printf("<td>%s</td><td>%s</td></tr>\n",
+ fname,tstring(e->time.tv_sec));
+}
+
+
+/* kill off any connections chosen by the user */
+static int traverse_fn1(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, void* state)
+{
+ struct connections_data crec;
+
+ if (dbuf.dsize != sizeof(crec))
+ return 0;
+
+ memcpy(&crec, dbuf.dptr, sizeof(crec));
+
+ if (crec.cnum == -1 && process_exists(crec.pid)) {
+ char buf[30];
+ slprintf(buf,sizeof(buf)-1,"kill_%d", (int)crec.pid);
+ if (cgi_variable(buf)) {
+ kill_pid(crec.pid);
+ sleep(SLEEP_TIME);
+ }
+ }
+ return 0;
+}
+
+/* traversal fn for showing machine connections */
+static int traverse_fn2(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, void* state)
+{
+ struct connections_data crec;
+
+ if (dbuf.dsize != sizeof(crec))
+ return 0;
+
+ memcpy(&crec, dbuf.dptr, sizeof(crec));
+
+ if (crec.cnum == -1 || !process_exists(crec.pid) || (crec.pid == smbd_pid))
+ return 0;
+
+ addPid2Machine (crec.pid, crec.machine);
+
+ d_printf("<tr><td>%d</td><td>%s</td><td>%s</td><td>%s</td>\n",
+ (int)crec.pid,
+ crec.machine,crec.addr,
+ tstring(crec.start));
+ if (geteuid() == 0) {
+ d_printf("<td><input type=submit value=\"X\" name=\"kill_%d\"></td>\n",
+ (int)crec.pid);
+ }
+ d_printf("</tr>\n");
+
+ return 0;
+}
+
+/* traversal fn for showing share connections */
+static int traverse_fn3(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, void* state)
+{
+ struct connections_data crec;
+
+ if (dbuf.dsize != sizeof(crec))
+ return 0;
+
+ memcpy(&crec, dbuf.dptr, sizeof(crec));
+
+ if (crec.cnum == -1 || !process_exists(crec.pid))
+ return 0;
+
+ d_printf("<tr><td>%s</td><td>%s</td><td>%s</td><td>%d</td><td>%s</td><td>%s</td></tr>\n",
+ crec.name,uidtoname(crec.uid),
+ gidtoname(crec.gid),(int)crec.pid,
+ crec.machine,
+ tstring(crec.start));
+ return 0;
+}
+
+
+/* show the current server status */
+void status_page(void)
+{
+ const char *v;
+ int autorefresh=0;
+ int refresh_interval=30;
+ TDB_CONTEXT *tdb;
+ int nr_running=0;
+ BOOL waitup = False;
+
+ smbd_pid = pidfile_pid("smbd");
+
+ if (cgi_variable("smbd_restart") || cgi_variable("all_restart")) {
+ stop_smbd();
+ start_smbd();
+ waitup=True;
+ }
+
+ if (cgi_variable("smbd_start") || cgi_variable("all_start")) {
+ start_smbd();
+ waitup=True;
+ }
+
+ if (cgi_variable("smbd_stop") || cgi_variable("all_stop")) {
+ stop_smbd();
+ waitup=True;
+ }
+
+ if (cgi_variable("nmbd_restart") || cgi_variable("all_restart")) {
+ stop_nmbd();
+ start_nmbd();
+ waitup=True;
+ }
+ if (cgi_variable("nmbd_start") || cgi_variable("all_start")) {
+ start_nmbd();
+ waitup=True;
+ }
+
+ if (cgi_variable("nmbd_stop")|| cgi_variable("all_stop")) {
+ stop_nmbd();
+ waitup=True;
+ }
+
+#ifdef WITH_WINBIND
+ if (cgi_variable("winbindd_restart") || cgi_variable("all_restart")) {
+ stop_winbindd();
+ start_winbindd();
+ waitup=True;
+ }
+
+ if (cgi_variable("winbindd_start") || cgi_variable("all_start")) {
+ start_winbindd();
+ waitup=True;
+ }
+
+ if (cgi_variable("winbindd_stop") || cgi_variable("all_stop")) {
+ stop_winbindd();
+ waitup=True;
+ }
+#endif
+ /* wait for daemons to start/stop */
+ if (waitup)
+ sleep(SLEEP_TIME);
+
+ if (cgi_variable("autorefresh")) {
+ autorefresh = 1;
+ } else if (cgi_variable("norefresh")) {
+ autorefresh = 0;
+ } else if (cgi_variable("refresh")) {
+ autorefresh = 1;
+ }
+
+ if ((v=cgi_variable("refresh_interval"))) {
+ refresh_interval = atoi(v);
+ }
+
+ if (cgi_variable("show_client_in_col_1")) {
+ PID_or_Machine = 1;
+ }
+
+ if (cgi_variable("show_pid_in_col_1")) {
+ PID_or_Machine = 0;
+ }
+
+ tdb = tdb_open_log(lock_path("connections.tdb"), 0, TDB_DEFAULT, O_RDONLY, 0);
+ if (tdb) tdb_traverse(tdb, traverse_fn1, NULL);
+
+ initPid2Machine ();
+
+ d_printf("<H2>%s</H2>\n", _("Server Status"));
+
+ d_printf("<FORM method=post>\n");
+
+ if (!autorefresh) {
+ d_printf("<input type=submit value=\"%s\" name=\"autorefresh\">\n", _("Auto Refresh"));
+ d_printf("<br>%s", _("Refresh Interval: "));
+ d_printf("<input type=text size=2 name=\"refresh_interval\" value=\"%d\">\n",
+ refresh_interval);
+ } else {
+ d_printf("<input type=submit value=\"%s\" name=\"norefresh\">\n", _("Stop Refreshing"));
+ d_printf("<br>%s%d\n", _("Refresh Interval: "), refresh_interval);
+ d_printf("<input type=hidden name=\"refresh\" value=\"1\">\n");
+ }
+
+ d_printf("<p>\n");
+
+ if (!tdb) {
+ /* open failure either means no connections have been
+ made */
+ }
+
+
+ d_printf("<table>\n");
+
+ d_printf("<tr><td>%s</td><td>%s</td></tr>", _("version:"), SAMBA_VERSION_STRING);
+
+ fflush(stdout);
+ d_printf("<tr><td>%s</td><td>%s</td>\n", _("smbd:"), smbd_running()?_("running"):_("not running"));
+ if (geteuid() == 0) {
+ if (smbd_running()) {
+ nr_running++;
+ d_printf("<td><input type=submit name=\"smbd_stop\" value=\"%s\"></td>\n", _("Stop smbd"));
+ } else {
+ d_printf("<td><input type=submit name=\"smbd_start\" value=\"%s\"></td>\n", _("Start smbd"));
+ }
+ d_printf("<td><input type=submit name=\"smbd_restart\" value=\"%s\"></td>\n", _("Restart smbd"));
+ }
+ d_printf("</tr>\n");
+
+ fflush(stdout);
+ d_printf("<tr><td>%s</td><td>%s</td>\n", _("nmbd:"), nmbd_running()?_("running"):_("not running"));
+ if (geteuid() == 0) {
+ if (nmbd_running()) {
+ nr_running++;
+ d_printf("<td><input type=submit name=\"nmbd_stop\" value=\"%s\"></td>\n", _("Stop nmbd"));
+ } else {
+ d_printf("<td><input type=submit name=\"nmbd_start\" value=\"%s\"></td>\n", _("Start nmbd"));
+ }
+ d_printf("<td><input type=submit name=\"nmbd_restart\" value=\"%s\"></td>\n", _("Restart nmbd"));
+ }
+ d_printf("</tr>\n");
+
+#ifdef WITH_WINBIND
+ fflush(stdout);
+ d_printf("<tr><td>%s</td><td>%s</td>\n", _("winbindd:"), winbindd_running()?_("running"):_("not running"));
+ if (geteuid() == 0) {
+ if (winbindd_running()) {
+ nr_running++;
+ d_printf("<td><input type=submit name=\"winbindd_stop\" value=\"%s\"></td>\n", _("Stop winbindd"));
+ } else {
+ d_printf("<td><input type=submit name=\"winbindd_start\" value=\"%s\"></td>\n", _("Start winbindd"));
+ }
+ d_printf("<td><input type=submit name=\"winbindd_restart\" value=\"%s\"></td>\n", _("Restart winbindd"));
+ }
+ d_printf("</tr>\n");
+#endif
+
+ if (geteuid() == 0) {
+ d_printf("<tr><td></td><td></td>\n");
+ if (nr_running >= 1) {
+ /* stop, restart all */
+ d_printf("<td><input type=submit name=\"all_stop\" value=\"%s\"></td>\n", _("Stop All"));
+ d_printf("<td><input type=submit name=\"all_restart\" value=\"%s\"></td>\n", _("Restart All"));
+ }
+ else if (nr_running == 0) {
+ /* start all */
+ d_printf("<td><input type=submit name=\"all_start\" value=\"%s\"></td>\n", _("Start All"));
+ }
+ d_printf("</tr>\n");
+ }
+ d_printf("</table>\n");
+ fflush(stdout);
+
+ d_printf("<p><h3>%s</h3>\n", _("Active Connections"));
+ d_printf("<table border=1>\n");
+ d_printf("<tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th>\n", _("PID"), _("Client"), _("IP address"), _("Date"));
+ if (geteuid() == 0) {
+ d_printf("<th>%s</th>\n", _("Kill"));
+ }
+ d_printf("</tr>\n");
+
+ if (tdb) tdb_traverse(tdb, traverse_fn2, NULL);
+
+ d_printf("</table><p>\n");
+
+ d_printf("<p><h3>%s</h3>\n", _("Active Shares"));
+ d_printf("<table border=1>\n");
+ d_printf("<tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr>\n\n",
+ _("Share"), _("User"), _("Group"), _("PID"), _("Client"), _("Date"));
+
+ if (tdb) tdb_traverse(tdb, traverse_fn3, NULL);
+
+ d_printf("</table><p>\n");
+
+ d_printf("<h3>%s</h3>\n", _("Open Files"));
+ d_printf("<table border=1>\n");
+ d_printf("<tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr>\n", _("PID"), _("Sharing"), _("R/W"), _("Oplock"), _("File"), _("Date"));
+
+ locking_init(1);
+ share_mode_forall(print_share_mode);
+ locking_end();
+ d_printf("</table>\n");
+
+ if (tdb) tdb_close(tdb);
+
+ d_printf("<br><input type=submit name=\"show_client_in_col_1\" value=\"%s\">\n", _("Show Client in col 1"));
+ d_printf("<input type=submit name=\"show_pid_in_col_1\" value=\"%s\">\n", _("Show PID in col 1"));
+
+ d_printf("</FORM>\n");
+
+ if (autorefresh) {
+ /* this little JavaScript allows for automatic refresh
+ of the page. There are other methods but this seems
+ to be the best alternative */
+ d_printf("<script language=\"JavaScript\">\n");
+ d_printf("<!--\nsetTimeout('window.location.replace(\"%s/status?refresh_interval=%d&refresh=1\")', %d)\n",
+ cgi_baseurl(),
+ refresh_interval,
+ refresh_interval*1000);
+ d_printf("//-->\n</script>\n");
+ }
+}
diff --git a/source/web/swat.c b/source/web/swat.c
new file mode 100644
index 00000000000..e535106f260
--- /dev/null
+++ b/source/web/swat.c
@@ -0,0 +1,1389 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba Web Administration Tool
+ Version 3.0.0
+ Copyright (C) Andrew Tridgell 1997-2002
+ Copyright (C) John H Terpstra 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/**
+ * @defgroup swat SWAT - Samba Web Administration Tool
+ * @{
+ * @file swat.c
+ *
+ * @brief Samba Web Administration Tool.
+ **/
+
+#include "includes.h"
+#include "../web/swat_proto.h"
+
+static BOOL demo_mode = False;
+static BOOL have_write_access = False;
+static BOOL have_read_access = False;
+static int iNumNonAutoPrintServices = 0;
+
+/*
+ * Password Management Globals
+ */
+#define SWAT_USER "username"
+#define OLD_PSWD "old_passwd"
+#define NEW_PSWD "new_passwd"
+#define NEW2_PSWD "new2_passwd"
+#define CHG_S_PASSWD_FLAG "chg_s_passwd_flag"
+#define CHG_R_PASSWD_FLAG "chg_r_passwd_flag"
+#define ADD_USER_FLAG "add_user_flag"
+#define DELETE_USER_FLAG "delete_user_flag"
+#define DISABLE_USER_FLAG "disable_user_flag"
+#define ENABLE_USER_FLAG "enable_user_flag"
+#define RHOST "remote_host"
+
+
+/****************************************************************************
+****************************************************************************/
+static int enum_index(int value, const struct enum_list *enumlist)
+{
+ int i;
+ for (i=0;enumlist[i].name;i++)
+ if (value == enumlist[i].value) break;
+ return(i);
+}
+
+static char *fix_backslash(const char *str)
+{
+ static char newstring[1024];
+ char *p = newstring;
+
+ while (*str) {
+ if (*str == '\\') {*p++ = '\\';*p++ = '\\';}
+ else *p++ = *str;
+ ++str;
+ }
+ *p = '\0';
+ return newstring;
+}
+
+static char *stripspaceupper(const char *str)
+{
+ static char newstring[1024];
+ char *p = newstring;
+
+ while (*str) {
+ if (*str != ' ') *p++ = toupper(*str);
+ ++str;
+ }
+ *p = '\0';
+ return newstring;
+}
+
+static char *make_parm_name(const char *label)
+{
+ static char parmname[1024];
+ char *p = parmname;
+
+ while (*label) {
+ if (*label == ' ') *p++ = '_';
+ else *p++ = *label;
+ ++label;
+ }
+ *p = '\0';
+ return parmname;
+}
+
+/****************************************************************************
+ include a lump of html in a page
+****************************************************************************/
+static int include_html(const char *fname)
+{
+ int fd;
+ char buf[1024];
+ int ret;
+
+ fd = web_open(fname, O_RDONLY, 0);
+
+ if (fd == -1) {
+ d_printf(_("ERROR: Can't open %s"), fname);
+ d_printf("\n");
+ return 0;
+ }
+
+ while ((ret = read(fd, buf, sizeof(buf))) > 0) {
+ write(1, buf, ret);
+ }
+
+ close(fd);
+ return 1;
+}
+
+/****************************************************************************
+ start the page with standard stuff
+****************************************************************************/
+static void print_header(void)
+{
+ if (!cgi_waspost()) {
+ d_printf("Expires: 0\r\n");
+ }
+ d_printf("Content-type: text/html\r\n\r\n");
+
+ if (!include_html("include/header.html")) {
+ d_printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\n");
+ d_printf("<HTML>\n<HEAD>\n<TITLE>Samba Web Administration Tool</TITLE>\n</HEAD>\n<BODY background=\"/swat/images/background.jpg\">\n\n");
+ }
+}
+
+/* *******************************************************************
+ show parameter label with translated name in the following form
+ because showing original and translated label in one line looks
+ too long, and showing translated label only is unusable for
+ heavy users.
+ -------------------------------
+ HELP security [combo box][button]
+ SECURITY
+ -------------------------------
+ (capital words are translated by gettext.)
+ if no translation is available, then same form as original is
+ used.
+ "i18n_translated_parm" class is used to change the color of the
+ translated parameter with CSS.
+ **************************************************************** */
+static const char* get_parm_translated(
+ const char* pAnchor, const char* pHelp, const char* pLabel)
+{
+ const char* pTranslated = _(pLabel);
+ static pstring output;
+ if(strcmp(pLabel, pTranslated) != 0)
+ {
+ pstr_sprintf(output,
+ "<A HREF=\"/swat/help/smb.conf.5.html#%s\" target=\"docs\"> %s</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %s <br><span class=\"i18n_translated_parm\">%s</span>",
+ pAnchor, pHelp, pLabel, pTranslated);
+ return output;
+ }
+ pstr_sprintf(output,
+ "<A HREF=\"/swat/help/smb.conf.5.html#%s\" target=\"docs\"> %s</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %s",
+ pAnchor, pHelp, pLabel);
+ return output;
+}
+/****************************************************************************
+ finish off the page
+****************************************************************************/
+static void print_footer(void)
+{
+ if (!include_html("include/footer.html")) {
+ d_printf("\n</BODY>\n</HTML>\n");
+ }
+}
+
+/****************************************************************************
+ display one editable parameter in a form
+****************************************************************************/
+static void show_parameter(int snum, struct parm_struct *parm)
+{
+ int i;
+ void *ptr = parm->ptr;
+
+ if (parm->class == P_LOCAL && snum >= 0) {
+ ptr = lp_local_ptr(snum, ptr);
+ }
+
+ d_printf("<tr><td>%s</td><td>", get_parm_translated(stripspaceupper(parm->label), _("Help"), parm->label));
+ switch (parm->type) {
+ case P_CHAR:
+ d_printf("<input type=text size=2 name=\"parm_%s\" value=\"%c\">",
+ make_parm_name(parm->label), *(char *)ptr);
+ d_printf("<input type=button value=\"%s\" onClick=\"swatform.parm_%s.value=\'%c\'\">",
+ _("Set Default"), make_parm_name(parm->label),(char)(parm->def.cvalue));
+ break;
+
+ case P_LIST:
+ d_printf("<input type=text size=40 name=\"parm_%s\" value=\"",
+ make_parm_name(parm->label));
+ if ((char ***)ptr && *(char ***)ptr && **(char ***)ptr) {
+ char **list = *(char ***)ptr;
+ for (;*list;list++) {
+ /* enclose in quotes if the string contains a space */
+ if ( strchr_m(*list, ' ') )
+ d_printf("\'%s\'%s", *list, ((*(list+1))?", ":""));
+ else
+ d_printf("%s%s", *list, ((*(list+1))?", ":""));
+ }
+ }
+ d_printf("\">");
+ d_printf("<input type=button value=\"%s\" onClick=\"swatform.parm_%s.value=\'",
+ _("Set Default"), make_parm_name(parm->label));
+ if (parm->def.lvalue) {
+ char **list = (char **)(parm->def.lvalue);
+ for (; *list; list++) {
+ /* enclose in quotes if the string contains a space */
+ if ( strchr_m(*list, ' ') )
+ d_printf("\'%s\'%s", *list, ((*(list+1))?", ":""));
+ else
+ d_printf("%s%s", *list, ((*(list+1))?", ":""));
+ }
+ }
+ d_printf("\'\">");
+ break;
+
+ case P_STRING:
+ case P_USTRING:
+ d_printf("<input type=text size=40 name=\"parm_%s\" value=\"%s\">",
+ make_parm_name(parm->label), *(char **)ptr);
+ d_printf("<input type=button value=\"%s\" onClick=\"swatform.parm_%s.value=\'%s\'\">",
+ _("Set Default"), make_parm_name(parm->label),fix_backslash((char *)(parm->def.svalue)));
+ break;
+
+ case P_GSTRING:
+ case P_UGSTRING:
+ d_printf("<input type=text size=40 name=\"parm_%s\" value=\"%s\">",
+ make_parm_name(parm->label), (char *)ptr);
+ d_printf("<input type=button value=\"%s\" onClick=\"swatform.parm_%s.value=\'%s\'\">",
+ _("Set Default"), make_parm_name(parm->label),fix_backslash((char *)(parm->def.svalue)));
+ break;
+
+ case P_BOOL:
+ d_printf("<select name=\"parm_%s\">",make_parm_name(parm->label));
+ d_printf("<option %s>Yes", (*(BOOL *)ptr)?"selected":"");
+ d_printf("<option %s>No", (*(BOOL *)ptr)?"":"selected");
+ d_printf("</select>");
+ d_printf("<input type=button value=\"%s\" onClick=\"swatform.parm_%s.selectedIndex=\'%d\'\">",
+ _("Set Default"), make_parm_name(parm->label),(BOOL)(parm->def.bvalue)?0:1);
+ break;
+
+ case P_BOOLREV:
+ d_printf("<select name=\"parm_%s\">",make_parm_name(parm->label));
+ d_printf("<option %s>Yes", (*(BOOL *)ptr)?"":"selected");
+ d_printf("<option %s>No", (*(BOOL *)ptr)?"selected":"");
+ d_printf("</select>");
+ d_printf("<input type=button value=\"%s\" onClick=\"swatform.parm_%s.selectedIndex=\'%d\'\">",
+ _("Set Default"), make_parm_name(parm->label),(BOOL)(parm->def.bvalue)?1:0);
+ break;
+
+ case P_INTEGER:
+ d_printf("<input type=text size=8 name=\"parm_%s\" value=\"%d\">", make_parm_name(parm->label), *(int *)ptr);
+ d_printf("<input type=button value=\"%s\" onClick=\"swatform.parm_%s.value=\'%d\'\">",
+ _("Set Default"), make_parm_name(parm->label),(int)(parm->def.ivalue));
+ break;
+
+ case P_OCTAL:
+ d_printf("<input type=text size=8 name=\"parm_%s\" value=%s>", make_parm_name(parm->label), octal_string(*(int *)ptr));
+ d_printf("<input type=button value=\"%s\" onClick=\"swatform.parm_%s.value=\'%s\'\">",
+ _("Set Default"), make_parm_name(parm->label),
+ octal_string((int)(parm->def.ivalue)));
+ break;
+
+ case P_ENUM:
+ d_printf("<select name=\"parm_%s\">",make_parm_name(parm->label));
+ for (i=0;parm->enum_list[i].name;i++) {
+ if (i == 0 || parm->enum_list[i].value != parm->enum_list[i-1].value) {
+ d_printf("<option %s>%s",(*(int *)ptr)==parm->enum_list[i].value?"selected":"",parm->enum_list[i].name);
+ }
+ }
+ d_printf("</select>");
+ d_printf("<input type=button value=\"%s\" onClick=\"swatform.parm_%s.selectedIndex=\'%d\'\">",
+ _("Set Default"), make_parm_name(parm->label),enum_index((int)(parm->def.ivalue),parm->enum_list));
+ break;
+ case P_SEP:
+ break;
+ }
+ d_printf("</td></tr>\n");
+}
+
+/****************************************************************************
+ display a set of parameters for a service
+****************************************************************************/
+static void show_parameters(int snum, int allparameters, unsigned int parm_filter, int printers)
+{
+ int i = 0;
+ struct parm_struct *parm;
+ const char *heading = NULL;
+ const char *last_heading = NULL;
+
+ while ((parm = lp_next_parameter(snum, &i, allparameters))) {
+ if (snum < 0 && parm->class == P_LOCAL && !(parm->flags & FLAG_GLOBAL))
+ continue;
+ if (parm->class == P_SEPARATOR) {
+ heading = parm->label;
+ continue;
+ }
+ if (parm->flags & FLAG_HIDE) continue;
+ if (snum >= 0) {
+ if (printers & !(parm->flags & FLAG_PRINT)) continue;
+ if (!printers & !(parm->flags & FLAG_SHARE)) continue;
+ }
+
+ if (!( parm_filter & FLAG_ADVANCED )) {
+ if (!(parm->flags & FLAG_BASIC)) {
+ void *ptr = parm->ptr;
+
+ if (parm->class == P_LOCAL && snum >= 0) {
+ ptr = lp_local_ptr(snum, ptr);
+ }
+
+ switch (parm->type) {
+ case P_CHAR:
+ if (*(char *)ptr == (char)(parm->def.cvalue)) continue;
+ break;
+
+ case P_LIST:
+ if (!str_list_compare(*(char ***)ptr, (char **)(parm->def.lvalue))) continue;
+ break;
+
+ case P_STRING:
+ case P_USTRING:
+ if (!strcmp(*(char **)ptr,(char *)(parm->def.svalue))) continue;
+ break;
+
+ case P_GSTRING:
+ case P_UGSTRING:
+ if (!strcmp((char *)ptr,(char *)(parm->def.svalue))) continue;
+ break;
+
+ case P_BOOL:
+ case P_BOOLREV:
+ if (*(BOOL *)ptr == (BOOL)(parm->def.bvalue)) continue;
+ break;
+
+ case P_INTEGER:
+ case P_OCTAL:
+ if (*(int *)ptr == (int)(parm->def.ivalue)) continue;
+ break;
+
+
+ case P_ENUM:
+ if (*(int *)ptr == (int)(parm->def.ivalue)) continue;
+ break;
+ case P_SEP:
+ continue;
+ }
+ }
+ if (printers && !(parm->flags & FLAG_PRINT)) continue;
+ }
+
+ if ((parm_filter & FLAG_WIZARD) && !(parm->flags & FLAG_WIZARD)) continue;
+
+ if ((parm_filter & FLAG_ADVANCED) && !(parm->flags & FLAG_ADVANCED)) continue;
+
+ if (heading && heading != last_heading) {
+ d_printf("<tr><td></td></tr><tr><td><b><u>%s</u></b></td></tr>\n", _(heading));
+ last_heading = heading;
+ }
+ show_parameter(snum, parm);
+ }
+}
+
+/****************************************************************************
+ load the smb.conf file into loadparm.
+****************************************************************************/
+static BOOL load_config(BOOL save_def)
+{
+ lp_resetnumservices();
+ return lp_load(dyn_CONFIGFILE,False,save_def,False);
+}
+
+/****************************************************************************
+ write a config file
+****************************************************************************/
+static void write_config(FILE *f, BOOL show_defaults)
+{
+ fprintf(f, "# Samba config file created using SWAT\n");
+ fprintf(f, "# from %s (%s)\n", cgi_remote_host(), cgi_remote_addr());
+ fprintf(f, "# Date: %s\n\n", timestring(False));
+
+ lp_dump(f, show_defaults, iNumNonAutoPrintServices);
+}
+
+/****************************************************************************
+ save and reload the smb.conf config file
+****************************************************************************/
+static int save_reload(int snum)
+{
+ FILE *f;
+ struct stat st;
+
+ f = sys_fopen(dyn_CONFIGFILE,"w");
+ if (!f) {
+ d_printf(_("failed to open %s for writing"), dyn_CONFIGFILE);
+ d_printf("\n");
+ return 0;
+ }
+
+ /* just in case they have used the buggy xinetd to create the file */
+ if (fstat(fileno(f), &st) == 0 &&
+ (st.st_mode & S_IWOTH)) {
+#if defined HAVE_FCHMOD
+ fchmod(fileno(f), S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
+#else
+ chmod(dyn_CONFIGFILE, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
+#endif
+ }
+
+ write_config(f, False);
+ if (snum)
+ lp_dump_one(f, False, snum);
+ fclose(f);
+
+ lp_killunused(NULL);
+
+ if (!load_config(False)) {
+ d_printf(_("Can't reload %s"), dyn_CONFIGFILE);
+ d_printf("\n");
+ return 0;
+ }
+ iNumNonAutoPrintServices = lp_numservices();
+ load_printers();
+
+ return 1;
+}
+
+/****************************************************************************
+ commit one parameter
+****************************************************************************/
+static void commit_parameter(int snum, struct parm_struct *parm, const char *v)
+{
+ int i;
+ char *s;
+
+ if (snum < 0 && parm->class == P_LOCAL) {
+ /* this handles the case where we are changing a local
+ variable globally. We need to change the parameter in
+ all shares where it is currently set to the default */
+ for (i=0;i<lp_numservices();i++) {
+ s = lp_servicename(i);
+ if (s && (*s) && lp_is_default(i, parm)) {
+ lp_do_parameter(i, parm->label, v);
+ }
+ }
+ }
+
+ lp_do_parameter(snum, parm->label, v);
+}
+
+/****************************************************************************
+ commit a set of parameters for a service
+****************************************************************************/
+static void commit_parameters(int snum)
+{
+ int i = 0;
+ struct parm_struct *parm;
+ pstring label;
+ const char *v;
+
+ while ((parm = lp_next_parameter(snum, &i, 1))) {
+ slprintf(label, sizeof(label)-1, "parm_%s", make_parm_name(parm->label));
+ if ((v = cgi_variable(label))) {
+ if (parm->flags & FLAG_HIDE) continue;
+ commit_parameter(snum, parm, v);
+ }
+ }
+}
+
+/****************************************************************************
+ spit out the html for a link with an image
+****************************************************************************/
+static void image_link(const char *name, const char *hlink, const char *src)
+{
+ d_printf("<A HREF=\"%s/%s\"><img border=\"0\" src=\"/swat/%s\" alt=\"%s\"></A>\n",
+ cgi_baseurl(), hlink, src, name);
+}
+
+/****************************************************************************
+ display the main navigation controls at the top of each page along
+ with a title
+****************************************************************************/
+static void show_main_buttons(void)
+{
+ char *p;
+
+ if ((p = cgi_user_name()) && strcmp(p, "root")) {
+ d_printf(_("Logged in as <b>%s</b>"), p);
+ d_printf("<p>\n");
+ }
+
+ image_link(_("Home"), "", "images/home.gif");
+ if (have_write_access) {
+ image_link(_("Globals"), "globals", "images/globals.gif");
+ image_link(_("Shares"), "shares", "images/shares.gif");
+ image_link(_("Printers"), "printers", "images/printers.gif");
+ image_link(_("Wizard"), "wizard", "images/wizard.gif");
+ }
+ if (have_read_access) {
+ image_link(_("Status"), "status", "images/status.gif");
+ image_link(_("View Config"), "viewconfig", "images/viewconfig.gif");
+ }
+ image_link(_("Password Management"), "passwd", "images/passwd.gif");
+
+ d_printf("<HR>\n");
+}
+
+/****************************************************************************
+ * Handle Display/Edit Mode CGI
+ ****************************************************************************/
+static void ViewModeBoxes(int mode)
+{
+ d_printf("<p>%s:&nbsp;\n", _("Current View Is"));
+ d_printf("<input type=radio name=\"ViewMode\" value=0 %s>%s\n", ((mode == 0) ? "checked" : ""), _("Basic"));
+ d_printf("<input type=radio name=\"ViewMode\" value=1 %s>%s\n", ((mode == 1) ? "checked" : ""), _("Advanced"));
+ d_printf("<br>%s:&nbsp;\n", _("Change View To"));
+ d_printf("<input type=submit name=\"BasicMode\" value=\"%s\">\n", _("Basic"));
+ d_printf("<input type=submit name=\"AdvMode\" value=\"%s\">\n", _("Advanced"));
+ d_printf("</p><br>\n");
+}
+
+/****************************************************************************
+ display a welcome page
+****************************************************************************/
+static void welcome_page(void)
+{
+ include_html("help/welcome.html");
+}
+
+/****************************************************************************
+ display the current smb.conf
+****************************************************************************/
+static void viewconfig_page(void)
+{
+ int full_view=0;
+
+ if (cgi_variable("full_view")) {
+ full_view = 1;
+ }
+
+ d_printf("<H2>%s</H2>\n", _("Current Config"));
+ d_printf("<form method=post>\n");
+
+ if (full_view) {
+ d_printf("<input type=submit name=\"normal_view\" value=\"%s\">\n", _("Normal View"));
+ } else {
+ d_printf("<input type=submit name=\"full_view\" value=\"%s\">\n", _("Full View"));
+ }
+
+ d_printf("<p><pre>");
+ write_config(stdout, full_view);
+ d_printf("</pre>");
+ d_printf("</form>\n");
+}
+
+/****************************************************************************
+ second screen of the wizard ... Fetch Configuration Parameters
+****************************************************************************/
+static void wizard_params_page(void)
+{
+ unsigned int parm_filter = FLAG_WIZARD;
+
+ /* Here we first set and commit all the parameters that were selected
+ in the previous screen. */
+
+ d_printf("<H2>%s</H2>\n", _("Wizard Parameter Edit Page"));
+
+ if (cgi_variable("Commit")) {
+ commit_parameters(GLOBAL_SECTION_SNUM);
+ save_reload(0);
+ }
+
+ d_printf("<form name=\"swatform\" method=post action=wizard_params>\n");
+
+ if (have_write_access) {
+ d_printf("<input type=submit name=\"Commit\" value=\"Commit Changes\">\n");
+ }
+
+ d_printf("<input type=reset name=\"Reset Values\" value=\"Reset\">\n");
+ d_printf("<p>\n");
+
+ d_printf("<table>\n");
+ show_parameters(GLOBAL_SECTION_SNUM, 1, parm_filter, 0);
+ d_printf("</table>\n");
+ d_printf("</form>\n");
+}
+
+/****************************************************************************
+ Utility to just rewrite the smb.conf file - effectively just cleans it up
+****************************************************************************/
+static void rewritecfg_file(void)
+{
+ commit_parameters(GLOBAL_SECTION_SNUM);
+ save_reload(0);
+ d_printf("<H2>%s</H2>\n", _("Note: smb.conf file has been read and rewritten"));
+}
+
+/****************************************************************************
+ wizard to create/modify the smb.conf file
+****************************************************************************/
+static void wizard_page(void)
+{
+ /* Set some variables to collect data from smb.conf */
+ int role = 0;
+ int winstype = 0;
+ int have_home = -1;
+ int HomeExpo = 0;
+ int SerType = 0;
+
+ if (cgi_variable("Rewrite")) {
+ (void) rewritecfg_file();
+ return;
+ }
+
+ if (cgi_variable("GetWizardParams")){
+ (void) wizard_params_page();
+ return;
+ }
+
+ if (cgi_variable("Commit")){
+ SerType = atoi(cgi_variable("ServerType"));
+ winstype = atoi(cgi_variable("WINSType"));
+ have_home = lp_servicenumber(HOMES_NAME);
+ HomeExpo = atoi(cgi_variable("HomeExpo"));
+
+ /* Plain text passwords are too badly broken - use encrypted passwords only */
+ lp_do_parameter( GLOBAL_SECTION_SNUM, "encrypt passwords", "Yes");
+
+ switch ( SerType ){
+ case 0:
+ /* Stand-alone Server */
+ lp_do_parameter( GLOBAL_SECTION_SNUM, "security", "USER" );
+ lp_do_parameter( GLOBAL_SECTION_SNUM, "domain logons", "No" );
+ break;
+ case 1:
+ /* Domain Member */
+ lp_do_parameter( GLOBAL_SECTION_SNUM, "security", "DOMAIN" );
+ lp_do_parameter( GLOBAL_SECTION_SNUM, "domain logons", "No" );
+ break;
+ case 2:
+ /* Domain Controller */
+ lp_do_parameter( GLOBAL_SECTION_SNUM, "security", "USER" );
+ lp_do_parameter( GLOBAL_SECTION_SNUM, "domain logons", "Yes" );
+ break;
+ }
+ switch ( winstype ) {
+ case 0:
+ lp_do_parameter( GLOBAL_SECTION_SNUM, "wins support", "No" );
+ lp_do_parameter( GLOBAL_SECTION_SNUM, "wins server", "" );
+ break;
+ case 1:
+ lp_do_parameter( GLOBAL_SECTION_SNUM, "wins support", "Yes" );
+ lp_do_parameter( GLOBAL_SECTION_SNUM, "wins server", "" );
+ break;
+ case 2:
+ lp_do_parameter( GLOBAL_SECTION_SNUM, "wins support", "No" );
+ lp_do_parameter( GLOBAL_SECTION_SNUM, "wins server", cgi_variable("WINSAddr"));
+ break;
+ }
+
+ /* Have to create Homes share? */
+ if ((HomeExpo == 1) && (have_home == -1)) {
+ pstring unix_share;
+
+ pstrcpy(unix_share,HOMES_NAME);
+ load_config(False);
+ lp_copy_service(GLOBAL_SECTION_SNUM, unix_share);
+ iNumNonAutoPrintServices = lp_numservices();
+ have_home = lp_servicenumber(HOMES_NAME);
+ lp_do_parameter( have_home, "read only", "No");
+ lp_do_parameter( have_home, "valid users", "%S");
+ lp_do_parameter( have_home, "browseable", "No");
+ commit_parameters(have_home);
+ }
+
+ /* Need to Delete Homes share? */
+ if ((HomeExpo == 0) && (have_home != -1)) {
+ lp_remove_service(have_home);
+ have_home = -1;
+ }
+
+ commit_parameters(GLOBAL_SECTION_SNUM);
+ save_reload(0);
+ }
+ else
+ {
+ /* Now determine smb.conf WINS settings */
+ if (lp_wins_support())
+ winstype = 1;
+ if (lp_wins_server_list() && strlen(*lp_wins_server_list()))
+ winstype = 2;
+
+
+ /* Do we have a homes share? */
+ have_home = lp_servicenumber(HOMES_NAME);
+ }
+ if ((winstype == 2) && lp_wins_support())
+ winstype = 3;
+
+ role = lp_server_role();
+
+ /* Here we go ... */
+ d_printf("<H2>%s</H2>\n", _("Samba Configuration Wizard"));
+ d_printf("<form method=post action=wizard>\n");
+
+ if (have_write_access) {
+ d_printf("%s\n", _("The \"Rewrite smb.conf file\" button will clear the smb.conf file of all default values and of comments."));
+ d_printf("%s", _("The same will happen if you press the commit button."));
+ d_printf("<br><br>\n");
+ d_printf("<center>");
+ d_printf("<input type=submit name=\"Rewrite\" value=\"%s\"> &nbsp;&nbsp;",_("Rewrite smb.conf file"));
+ d_printf("<input type=submit name=\"Commit\" value=\"%s\"> &nbsp;&nbsp;",_("Commit"));
+ d_printf("<input type=submit name=\"GetWizardParams\" value=\"%s\">", _("Edit Parameter Values"));
+ d_printf("</center>\n");
+ }
+
+ d_printf("<hr>");
+ d_printf("<center><table border=0>");
+ d_printf("<tr><td><b>%s:&nbsp;</b></td>\n", _("Server Type"));
+ d_printf("<td><input type=radio name=\"ServerType\" value=\"0\" %s> %s&nbsp;</td>", ((role == ROLE_STANDALONE) ? "checked" : ""), _("Stand Alone"));
+ d_printf("<td><input type=radio name=\"ServerType\" value=\"1\" %s> %s&nbsp;</td>", ((role == ROLE_DOMAIN_MEMBER) ? "checked" : ""), _("Domain Member"));
+ d_printf("<td><input type=radio name=\"ServerType\" value=\"2\" %s> %s&nbsp;</td>", ((role == ROLE_DOMAIN_PDC) ? "checked" : ""), _("Domain Controller"));
+ d_printf("</tr>\n");
+ if (role == ROLE_DOMAIN_BDC) {
+ d_printf("<tr><td></td><td colspan=3><font color=\"#ff0000\">%s</font></td></tr>\n", _("Unusual Type in smb.conf - Please Select New Mode"));
+ }
+ d_printf("<tr><td><b>%s:&nbsp;</b></td>\n", _("Configure WINS As"));
+ d_printf("<td><input type=radio name=\"WINSType\" value=\"0\" %s> %s&nbsp;</td>", ((winstype == 0) ? "checked" : ""), _("Not Used"));
+ d_printf("<td><input type=radio name=\"WINSType\" value=\"1\" %s> %s&nbsp;</td>", ((winstype == 1) ? "checked" : ""), _("Server for client use"));
+ d_printf("<td><input type=radio name=\"WINSType\" value=\"2\" %s> %s&nbsp;</td>", ((winstype == 2) ? "checked" : ""), _("Client of another WINS server"));
+ d_printf("</tr>\n");
+ d_printf("<tr><td></td><td></td><td></td><td>%s&nbsp;<input type=text size=\"16\" name=\"WINSAddr\" value=\"", _("Remote WINS Server"));
+
+ /* Print out the list of wins servers */
+ if(lp_wins_server_list()) {
+ int i;
+ const char **wins_servers = lp_wins_server_list();
+ for(i = 0; wins_servers[i]; i++) d_printf("%s ", wins_servers[i]);
+ }
+
+ d_printf("\"></td></tr>\n");
+ if (winstype == 3) {
+ d_printf("<tr><td></td><td colspan=3><font color=\"#ff0000\">%s</font></td></tr>\n", _("Error: WINS Server Mode and WINS Support both set in smb.conf"));
+ d_printf("<tr><td></td><td colspan=3><font color=\"#ff0000\">%s</font></td></tr>\n", _("Please Select desired WINS mode above."));
+ }
+ d_printf("<tr><td><b>%s:&nbsp;</b></td>\n", _("Expose Home Directories"));
+ d_printf("<td><input type=radio name=\"HomeExpo\" value=\"1\" %s> Yes</td>", (have_home == -1) ? "" : "checked ");
+ d_printf("<td><input type=radio name=\"HomeExpo\" value=\"0\" %s> No</td>", (have_home == -1 ) ? "checked" : "");
+ d_printf("<td></td></tr>\n");
+
+ /* Enable this when we are ready ....
+ * d_printf("<tr><td><b>%s:&nbsp;</b></td>\n", _("Is Print Server"));
+ * d_printf("<td><input type=radio name=\"PtrSvr\" value=\"1\" %s> Yes</td>");
+ * d_printf("<td><input type=radio name=\"PtrSvr\" value=\"0\" %s> No</td>");
+ * d_printf("<td></td></tr>\n");
+ */
+
+ d_printf("</table></center>");
+ d_printf("<hr>");
+
+ d_printf("%s\n", _("The above configuration options will set multiple parameters and will generally assist with rapid Samba deployment."));
+ d_printf("</form>\n");
+}
+
+
+/****************************************************************************
+ display a globals editing page
+****************************************************************************/
+static void globals_page(void)
+{
+ unsigned int parm_filter = FLAG_BASIC;
+ int mode = 0;
+
+ d_printf("<H2>%s</H2>\n", _("Global Parameters"));
+
+ if (cgi_variable("Commit")) {
+ commit_parameters(GLOBAL_SECTION_SNUM);
+ save_reload(0);
+ }
+
+ if ( cgi_variable("ViewMode") )
+ mode = atoi(cgi_variable("ViewMode"));
+ if ( cgi_variable("BasicMode"))
+ mode = 0;
+ if ( cgi_variable("AdvMode"))
+ mode = 1;
+
+ d_printf("<form name=\"swatform\" method=post action=globals>\n");
+
+ ViewModeBoxes( mode );
+ switch ( mode ) {
+ case 0:
+ parm_filter = FLAG_BASIC;
+ break;
+ case 1:
+ parm_filter = FLAG_ADVANCED;
+ break;
+ }
+ d_printf("<br>\n");
+ if (have_write_access) {
+ d_printf("<input type=submit name=\"Commit\" value=\"%s\">\n",
+ _("Commit Changes"));
+ }
+
+ d_printf("<input type=reset name=\"Reset Values\" value=\"%s\">\n",
+ _("Reset Values"));
+
+ d_printf("<p>\n");
+ d_printf("<table>\n");
+ show_parameters(GLOBAL_SECTION_SNUM, 1, parm_filter, 0);
+ d_printf("</table>\n");
+ d_printf("</form>\n");
+}
+
+/****************************************************************************
+ display a shares editing page. share is in unix codepage, and must be in
+ dos codepage. FIXME !!! JRA.
+****************************************************************************/
+static void shares_page(void)
+{
+ const char *share = cgi_variable("share");
+ char *s;
+ int snum = -1;
+ int i;
+ int mode = 0;
+ unsigned int parm_filter = FLAG_BASIC;
+
+ if (share)
+ snum = lp_servicenumber(share);
+
+ d_printf("<H2>%s</H2>\n", _("Share Parameters"));
+
+ if (cgi_variable("Commit") && snum >= 0) {
+ commit_parameters(snum);
+ save_reload(0);
+ }
+
+ if (cgi_variable("Delete") && snum >= 0) {
+ lp_remove_service(snum);
+ save_reload(0);
+ share = NULL;
+ snum = -1;
+ }
+
+ if (cgi_variable("createshare") && (share=cgi_variable("newshare"))) {
+ load_config(False);
+ lp_copy_service(GLOBAL_SECTION_SNUM, share);
+ iNumNonAutoPrintServices = lp_numservices();
+ save_reload(0);
+ snum = lp_servicenumber(share);
+ }
+
+ d_printf("<FORM name=\"swatform\" method=post>\n");
+
+ d_printf("<table>\n");
+
+ if ( cgi_variable("ViewMode") )
+ mode = atoi(cgi_variable("ViewMode"));
+ if ( cgi_variable("BasicMode"))
+ mode = 0;
+ if ( cgi_variable("AdvMode"))
+ mode = 1;
+
+ ViewModeBoxes( mode );
+ switch ( mode ) {
+ case 0:
+ parm_filter = FLAG_BASIC;
+ break;
+ case 1:
+ parm_filter = FLAG_ADVANCED;
+ break;
+ }
+ d_printf("<br><tr>\n");
+ d_printf("<td><input type=submit name=selectshare value=\"%s\"></td>\n", _("Choose Share"));
+ d_printf("<td><select name=share>\n");
+ if (snum < 0)
+ d_printf("<option value=\" \"> \n");
+ for (i=0;i<lp_numservices();i++) {
+ s = lp_servicename(i);
+ if (s && (*s) && strcmp(s,"IPC$") && !lp_print_ok(i)) {
+ d_printf("<option %s value=\"%s\">%s\n",
+ (share && strcmp(share,s)==0)?"SELECTED":"",
+ s, s);
+ }
+ }
+ d_printf("</select></td>\n");
+ if (have_write_access) {
+ d_printf("<td><input type=submit name=\"Delete\" value=\"%s\"></td>\n", _("Delete Share"));
+ }
+ d_printf("</tr>\n");
+ d_printf("</table>");
+ d_printf("<table>");
+ if (have_write_access) {
+ d_printf("<tr>\n");
+ d_printf("<td><input type=submit name=createshare value=\"%s\"></td>\n", _("Create Share"));
+ d_printf("<td><input type=text size=30 name=newshare></td></tr>\n");
+ }
+ d_printf("</table>");
+
+
+ if (snum >= 0) {
+ if (have_write_access) {
+ d_printf("<input type=submit name=\"Commit\" value=\"%s\">\n", _("Commit Changes"));
+ }
+
+ d_printf("<input type=reset name=\"Reset Values\" value=\"%s\">\n", _("Reset Values"));
+ d_printf("<p>\n");
+ }
+
+ if (snum >= 0) {
+ d_printf("<table>\n");
+ show_parameters(snum, 1, parm_filter, 0);
+ d_printf("</table>\n");
+ }
+
+ d_printf("</FORM>\n");
+}
+
+/*************************************************************
+change a password either locally or remotely
+*************************************************************/
+static BOOL change_password(const char *remote_machine, const char *user_name,
+ const char *old_passwd, const char *new_passwd,
+ int local_flags)
+{
+ BOOL ret = False;
+ pstring err_str;
+ pstring msg_str;
+
+ if (demo_mode) {
+ d_printf("%s\n<p>", _("password change in demo mode rejected"));
+ return False;
+ }
+
+ if (remote_machine != NULL) {
+ ret = remote_password_change(remote_machine, user_name, old_passwd,
+ new_passwd, err_str, sizeof(err_str));
+ if(*err_str)
+ d_printf("%s\n<p>", err_str);
+ return ret;
+ }
+
+ if(!initialize_password_db(True)) {
+ d_printf("%s\n<p>", _("Can't setup password database vectors."));
+ return False;
+ }
+
+ ret = local_password_change(user_name, local_flags, new_passwd, err_str, sizeof(err_str),
+ msg_str, sizeof(msg_str));
+
+ if(*msg_str)
+ d_printf("%s\n<p>", msg_str);
+ if(*err_str)
+ d_printf("%s\n<p>", err_str);
+
+ return ret;
+}
+
+/****************************************************************************
+ do the stuff required to add or change a password
+****************************************************************************/
+static void chg_passwd(void)
+{
+ const char *host;
+ BOOL rslt;
+ int local_flags = 0;
+
+ /* Make sure users name has been specified */
+ if (strlen(cgi_variable(SWAT_USER)) == 0) {
+ d_printf("<p>%s\n", _(" Must specify \"User Name\" "));
+ return;
+ }
+
+ /*
+ * smbpasswd doesn't require anything but the users name to delete, disable or enable the user,
+ * so if that's what we're doing, skip the rest of the checks
+ */
+ if (!cgi_variable(DISABLE_USER_FLAG) && !cgi_variable(ENABLE_USER_FLAG) && !cgi_variable(DELETE_USER_FLAG)) {
+
+ /*
+ * If current user is not root, make sure old password has been specified
+ * If REMOTE change, even root must provide old password
+ */
+ if (((!am_root()) && (strlen( cgi_variable(OLD_PSWD)) <= 0)) ||
+ ((cgi_variable(CHG_R_PASSWD_FLAG)) && (strlen( cgi_variable(OLD_PSWD)) <= 0))) {
+ d_printf("<p>%s\n", _(" Must specify \"Old Password\" "));
+ return;
+ }
+
+ /* If changing a users password on a remote hosts we have to know what host */
+ if ((cgi_variable(CHG_R_PASSWD_FLAG)) && (strlen( cgi_variable(RHOST)) <= 0)) {
+ d_printf("<p>%s\n", _(" Must specify \"Remote Machine\" "));
+ return;
+ }
+
+ /* Make sure new passwords have been specified */
+ if ((strlen( cgi_variable(NEW_PSWD)) <= 0) ||
+ (strlen( cgi_variable(NEW2_PSWD)) <= 0)) {
+ d_printf("<p>%s\n", _(" Must specify \"New, and Re-typed Passwords\" "));
+ return;
+ }
+
+ /* Make sure new passwords was typed correctly twice */
+ if (strcmp(cgi_variable(NEW_PSWD), cgi_variable(NEW2_PSWD)) != 0) {
+ d_printf("<p>%s\n", _(" Re-typed password didn't match new password "));
+ return;
+ }
+ }
+
+ if (cgi_variable(CHG_R_PASSWD_FLAG)) {
+ host = cgi_variable(RHOST);
+ } else if (am_root()) {
+ host = NULL;
+ } else {
+ host = "127.0.0.1";
+ }
+
+ /*
+ * Set up the local flags.
+ */
+
+ local_flags |= (cgi_variable(ADD_USER_FLAG) ? LOCAL_ADD_USER : 0);
+ local_flags |= (cgi_variable(DELETE_USER_FLAG) ? LOCAL_DELETE_USER : 0);
+ local_flags |= (cgi_variable(ENABLE_USER_FLAG) ? LOCAL_ENABLE_USER : 0);
+ local_flags |= (cgi_variable(DISABLE_USER_FLAG) ? LOCAL_DISABLE_USER : 0);
+
+ rslt = change_password(host,
+ cgi_variable(SWAT_USER),
+ cgi_variable(OLD_PSWD), cgi_variable(NEW_PSWD),
+ local_flags);
+
+ if(local_flags == 0) {
+ d_printf("<p>");
+ if (rslt == True) {
+ d_printf(_(" The passwd for '%s' has been changed."), cgi_variable(SWAT_USER));
+ d_printf("\n");
+ } else {
+ d_printf(_(" The passwd for '%s' has NOT been changed."), cgi_variable(SWAT_USER));
+ d_printf("\n");
+ }
+ }
+
+ return;
+}
+
+/****************************************************************************
+ display a password editing page
+****************************************************************************/
+static void passwd_page(void)
+{
+ const char *new_name = cgi_user_name();
+
+ /*
+ * After the first time through here be nice. If the user
+ * changed the User box text to another users name, remember it.
+ */
+ if (cgi_variable(SWAT_USER)) {
+ new_name = cgi_variable(SWAT_USER);
+ }
+
+ if (!new_name) new_name = "";
+
+ d_printf("<H2>%s</H2>\n", _("Server Password Management"));
+
+ d_printf("<FORM name=\"swatform\" method=post>\n");
+
+ d_printf("<table>\n");
+
+ /*
+ * Create all the dialog boxes for data collection
+ */
+ d_printf("<tr><td> %s : </td>\n", _("User Name"));
+ d_printf("<td><input type=text size=30 name=%s value=%s></td></tr> \n", SWAT_USER, new_name);
+ if (!am_root()) {
+ d_printf("<tr><td> %s : </td>\n", _("Old Password"));
+ d_printf("<td><input type=password size=30 name=%s></td></tr> \n",OLD_PSWD);
+ }
+ d_printf("<tr><td> %s : </td>\n", _("New Password"));
+ d_printf("<td><input type=password size=30 name=%s></td></tr>\n",NEW_PSWD);
+ d_printf("<tr><td> %s : </td>\n", _("Re-type New Password"));
+ d_printf("<td><input type=password size=30 name=%s></td></tr>\n",NEW2_PSWD);
+ d_printf("</table>\n");
+
+ /*
+ * Create all the control buttons for requesting action
+ */
+ d_printf("<input type=submit name=%s value=\"%s\">\n",
+ CHG_S_PASSWD_FLAG, _("Change Password"));
+ if (demo_mode || am_root()) {
+ d_printf("<input type=submit name=%s value=\"%s\">\n",
+ ADD_USER_FLAG, _("Add New User"));
+ d_printf("<input type=submit name=%s value=\"%s\">\n",
+ DELETE_USER_FLAG, _("Delete User"));
+ d_printf("<input type=submit name=%s value=\"%s\">\n",
+ DISABLE_USER_FLAG, _("Disable User"));
+ d_printf("<input type=submit name=%s value=\"%s\">\n",
+ ENABLE_USER_FLAG, _("Enable User"));
+ }
+ d_printf("<p></FORM>\n");
+
+ /*
+ * Do some work if change, add, disable or enable was
+ * requested. It could be this is the first time through this
+ * code, so there isn't anything to do. */
+ if ((cgi_variable(CHG_S_PASSWD_FLAG)) || (cgi_variable(ADD_USER_FLAG)) || (cgi_variable(DELETE_USER_FLAG)) ||
+ (cgi_variable(DISABLE_USER_FLAG)) || (cgi_variable(ENABLE_USER_FLAG))) {
+ chg_passwd();
+ }
+
+ d_printf("<H2>%s</H2>\n", _("Client/Server Password Management"));
+
+ d_printf("<FORM name=\"swatform\" method=post>\n");
+
+ d_printf("<table>\n");
+
+ /*
+ * Create all the dialog boxes for data collection
+ */
+ d_printf("<tr><td> %s : </td>\n", _("User Name"));
+ d_printf("<td><input type=text size=30 name=%s value=%s></td></tr>\n",SWAT_USER, new_name);
+ d_printf("<tr><td> %s : </td>\n", _("Old Password"));
+ d_printf("<td><input type=password size=30 name=%s></td></tr>\n",OLD_PSWD);
+ d_printf("<tr><td> %s : </td>\n", _("New Password"));
+ d_printf("<td><input type=password size=30 name=%s></td></tr>\n",NEW_PSWD);
+ d_printf("<tr><td> %s : </td>\n", _("Re-type New Password"));
+ d_printf("<td><input type=password size=30 name=%s></td></tr>\n",NEW2_PSWD);
+ d_printf("<tr><td> %s : </td>\n", _("Remote Machine"));
+ d_printf("<td><input type=text size=30 name=%s></td></tr>\n",RHOST);
+
+ d_printf("</table>");
+
+ /*
+ * Create all the control buttons for requesting action
+ */
+ d_printf("<input type=submit name=%s value=\"%s\">",
+ CHG_R_PASSWD_FLAG, _("Change Password"));
+
+ d_printf("<p></FORM>\n");
+
+ /*
+ * Do some work if a request has been made to change the
+ * password somewhere other than the server. It could be this
+ * is the first time through this code, so there isn't
+ * anything to do. */
+ if (cgi_variable(CHG_R_PASSWD_FLAG)) {
+ chg_passwd();
+ }
+
+}
+
+/****************************************************************************
+ display a printers editing page
+****************************************************************************/
+static void printers_page(void)
+{
+ const char *share = cgi_variable("share");
+ char *s;
+ int snum=-1;
+ int i;
+ int mode = 0;
+ unsigned int parm_filter = FLAG_BASIC;
+
+ if (share)
+ snum = lp_servicenumber(share);
+
+ d_printf("<H2>%s</H2>\n", _("Printer Parameters"));
+
+ d_printf("<H3>%s</H3>\n", _("Important Note:"));
+ d_printf(_("Printer names marked with [*] in the Choose Printer drop-down box "));
+ d_printf(_("are autoloaded printers from "));
+ d_printf("<A HREF=\"/swat/help/smb.conf.5.html#printcapname\" target=\"docs\">%s</A>\n", _("Printcap Name"));
+ d_printf("%s\n", _("Attempting to delete these printers from SWAT will have no effect."));
+
+ if (cgi_variable("Commit") && snum >= 0) {
+ commit_parameters(snum);
+ if (snum >= iNumNonAutoPrintServices)
+ save_reload(snum);
+ else
+ save_reload(0);
+ }
+
+ if (cgi_variable("Delete") && snum >= 0) {
+ lp_remove_service(snum);
+ save_reload(0);
+ share = NULL;
+ snum = -1;
+ }
+
+ if (cgi_variable("createshare") && (share=cgi_variable("newshare"))) {
+ load_config(False);
+ lp_copy_service(GLOBAL_SECTION_SNUM, share);
+ iNumNonAutoPrintServices = lp_numservices();
+ snum = lp_servicenumber(share);
+ lp_do_parameter(snum, "print ok", "Yes");
+ save_reload(0);
+ snum = lp_servicenumber(share);
+ }
+
+ d_printf("<FORM name=\"swatform\" method=post>\n");
+
+ if ( cgi_variable("ViewMode") )
+ mode = atoi(cgi_variable("ViewMode"));
+ if ( cgi_variable("BasicMode"))
+ mode = 0;
+ if ( cgi_variable("AdvMode"))
+ mode = 1;
+
+ ViewModeBoxes( mode );
+ switch ( mode ) {
+ case 0:
+ parm_filter = FLAG_BASIC;
+ break;
+ case 1:
+ parm_filter = FLAG_ADVANCED;
+ break;
+ }
+ d_printf("<table>\n");
+ d_printf("<tr><td><input type=submit name=\"selectshare\" value=\"%s\"></td>\n", _("Choose Printer"));
+ d_printf("<td><select name=\"share\">\n");
+ if (snum < 0 || !lp_print_ok(snum))
+ d_printf("<option value=\" \"> \n");
+ for (i=0;i<lp_numservices();i++) {
+ s = lp_servicename(i);
+ if (s && (*s) && strcmp(s,"IPC$") && lp_print_ok(i)) {
+ if (i >= iNumNonAutoPrintServices)
+ d_printf("<option %s value=\"%s\">[*]%s\n",
+ (share && strcmp(share,s)==0)?"SELECTED":"",
+ s, s);
+ else
+ d_printf("<option %s value=\"%s\">%s\n",
+ (share && strcmp(share,s)==0)?"SELECTED":"",
+ s, s);
+ }
+ }
+ d_printf("</select></td>");
+ if (have_write_access) {
+ d_printf("<td><input type=submit name=\"Delete\" value=\"%s\"></td>\n", _("Delete Printer"));
+ }
+ d_printf("</tr>");
+ d_printf("</table>\n");
+
+ if (have_write_access) {
+ d_printf("<table>\n");
+ d_printf("<tr><td><input type=submit name=\"createshare\" value=\"%s\"></td>\n", _("Create Printer"));
+ d_printf("<td><input type=text size=30 name=\"newshare\"></td></tr>\n");
+ d_printf("</table>");
+ }
+
+
+ if (snum >= 0) {
+ if (have_write_access) {
+ d_printf("<input type=submit name=\"Commit\" value=\"%s\">\n", _("Commit Changes"));
+ }
+ d_printf("<input type=reset name=\"Reset Values\" value=\"%s\">\n", _("Reset Values"));
+ d_printf("<p>\n");
+ }
+
+ if (snum >= 0) {
+ d_printf("<table>\n");
+ show_parameters(snum, 1, parm_filter, 1);
+ d_printf("</table>\n");
+ }
+ d_printf("</FORM>\n");
+}
+
+
+/**
+ * main function for SWAT.
+ **/
+ int main(int argc, char *argv[])
+{
+ int opt;
+ const char *page;
+ poptContext pc;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "disable-authentication", 'a', POPT_ARG_VAL, &demo_mode, True, "Disable authentication (demo mode)" },
+ POPT_COMMON_SAMBA
+ POPT_TABLEEND
+ };
+
+ fault_setup(NULL);
+ umask(S_IWGRP | S_IWOTH);
+
+#if defined(HAVE_SET_AUTH_PARAMETERS)
+ set_auth_parameters(argc, argv);
+#endif /* HAVE_SET_AUTH_PARAMETERS */
+
+ /* just in case it goes wild ... */
+ alarm(300);
+
+ setlinebuf(stdout);
+
+ /* we don't want any SIGPIPE messages */
+ BlockSignals(True,SIGPIPE);
+
+ dbf = x_fopen("/dev/null", O_WRONLY, 0);
+ if (!dbf) dbf = x_stderr;
+
+ /* we don't want stderr screwing us up */
+ close(2);
+ open("/dev/null", O_WRONLY);
+
+ pc = poptGetContext("swat", argc, (const char **) argv, long_options, 0);
+
+ /* Parse command line options */
+
+ while((opt = poptGetNextOpt(pc)) != -1) { }
+
+ poptFreeContext(pc);
+
+ setup_logging(argv[0],False);
+ load_config(True);
+ iNumNonAutoPrintServices = lp_numservices();
+ load_printers();
+
+ cgi_setup(dyn_SWATDIR, !demo_mode);
+
+ print_header();
+
+ cgi_load_variables();
+
+ if (!file_exist(dyn_CONFIGFILE, NULL)) {
+ have_read_access = True;
+ have_write_access = True;
+ } else {
+ /* check if the authenticated user has write access - if not then
+ don't show write options */
+ have_write_access = (access(dyn_CONFIGFILE,W_OK) == 0);
+
+ /* if the user doesn't have read access to smb.conf then
+ don't let them view it */
+ have_read_access = (access(dyn_CONFIGFILE,R_OK) == 0);
+ }
+
+ show_main_buttons();
+
+ page = cgi_pathinfo();
+
+ /* Root gets full functionality */
+ if (have_read_access && strcmp(page, "globals")==0) {
+ globals_page();
+ } else if (have_read_access && strcmp(page,"shares")==0) {
+ shares_page();
+ } else if (have_read_access && strcmp(page,"printers")==0) {
+ printers_page();
+ } else if (have_read_access && strcmp(page,"status")==0) {
+ status_page();
+ } else if (have_read_access && strcmp(page,"viewconfig")==0) {
+ viewconfig_page();
+ } else if (strcmp(page,"passwd")==0) {
+ passwd_page();
+ } else if (have_read_access && strcmp(page,"wizard")==0) {
+ wizard_page();
+ } else if (have_read_access && strcmp(page,"wizard_params")==0) {
+ wizard_params_page();
+ } else if (have_read_access && strcmp(page,"rewritecfg")==0) {
+ rewritecfg_file();
+ } else {
+ welcome_page();
+ }
+
+ print_footer();
+ return 0;
+}
+
+/** @} **/
diff --git a/source/wrepld/parser.c b/source/wrepld/parser.c
new file mode 100644
index 00000000000..b619cb0cef5
--- /dev/null
+++ b/source/wrepld/parser.c
@@ -0,0 +1,759 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Jean François Micouleau 1998-2002.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+#include "wins_repl.h"
+
+extern TALLOC_CTX *mem_ctx;
+
+/****************************************************************************
+grow the send buffer if necessary
+****************************************************************************/
+BOOL grow_buffer(struct BUFFER *buffer, int more)
+{
+ char *temp;
+
+ DEBUG(10,("grow_buffer: size is: %d offet is:%d growing by %d\n", buffer->length, buffer->offset, more));
+
+ /* grow by at least 256 bytes */
+ if (more<256)
+ more=256;
+
+ if (buffer->offset+more >= buffer->length) {
+ temp=(char *)talloc_realloc(mem_ctx, buffer->buffer, sizeof(char)* (buffer->length+more) );
+ if (temp==NULL) {
+ DEBUG(0,("grow_buffer: can't grow buffer\n"));
+ return False;
+ }
+ buffer->length+=more;
+ buffer->buffer=temp;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+check if the buffer has that much data
+****************************************************************************/
+static BOOL check_buffer(struct BUFFER *buffer, int more)
+{
+ DEBUG(10,("check_buffer: size is: %d offet is:%d growing by %d\n", buffer->length, buffer->offset, more));
+
+ if (buffer->offset+more > buffer->length) {
+ DEBUG(10,("check_buffer: buffer smaller than requested, size is: %d needed: %d\n", buffer->length, buffer->offset+more));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+decode a WINS_OWNER struct
+****************************************************************************/
+static void decode_wins_owner(struct BUFFER *inbuf, WINS_OWNER *wins_owner)
+{
+ if(!check_buffer(inbuf, 24))
+ return;
+
+ wins_owner->address.s_addr=IVAL(inbuf->buffer, inbuf->offset);
+ wins_owner->max_version=((SMB_BIG_UINT)RIVAL(inbuf->buffer, inbuf->offset+4))<<32;
+ wins_owner->max_version|=RIVAL(inbuf->buffer, inbuf->offset+8);
+ wins_owner->min_version=((SMB_BIG_UINT)RIVAL(inbuf->buffer, inbuf->offset+12))<<32;
+ wins_owner->min_version|=RIVAL(inbuf->buffer, inbuf->offset+16);
+ wins_owner->type=RIVAL(inbuf->buffer, inbuf->offset+20);
+ inbuf->offset+=24;
+
+}
+
+/****************************************************************************
+decode a WINS_NAME struct
+****************************************************************************/
+static void decode_wins_name(struct BUFFER *outbuf, WINS_NAME *wins_name)
+{
+ char *p;
+ int i;
+
+ if(!check_buffer(outbuf, 40))
+ return;
+
+ wins_name->name_len=RIVAL(outbuf->buffer, outbuf->offset);
+ outbuf->offset+=4;
+ memcpy(wins_name->name,outbuf->buffer+outbuf->offset, 15);
+ wins_name->name[15]='\0';
+ if((p = strchr(wins_name->name,' ')) != NULL)
+ *p = 0;
+
+ outbuf->offset+=15;
+
+ wins_name->type=(int)outbuf->buffer[outbuf->offset++];
+
+ /*
+ * fix to bug in WINS replication,
+ * present in all versions including W2K SP2 !
+ */
+ if (wins_name->name[0]==0x1B) {
+ wins_name->name[0]=(char)wins_name->type;
+ wins_name->type=0x1B;
+ }
+
+ wins_name->empty=RIVAL(outbuf->buffer, outbuf->offset);
+ outbuf->offset+=4;
+
+ wins_name->name_flag=RIVAL(outbuf->buffer, outbuf->offset);
+ outbuf->offset+=4;
+ wins_name->group_flag=RIVAL(outbuf->buffer, outbuf->offset);
+ outbuf->offset+=4;
+ wins_name->id=((SMB_BIG_UINT)RIVAL(outbuf->buffer, outbuf->offset))<<32;
+ outbuf->offset+=4;
+ wins_name->id|=RIVAL(outbuf->buffer, outbuf->offset);
+ outbuf->offset+=4;
+
+ /* special groups have multiple address */
+ if (wins_name->name_flag & 2) {
+ if(!check_buffer(outbuf, 4))
+ return;
+ wins_name->num_ip=IVAL(outbuf->buffer, outbuf->offset);
+ outbuf->offset+=4;
+ }
+ else
+ wins_name->num_ip=1;
+
+ if(!check_buffer(outbuf, 4))
+ return;
+ wins_name->owner.s_addr=IVAL(outbuf->buffer, outbuf->offset);
+ outbuf->offset+=4;
+
+ if (wins_name->name_flag & 2) {
+ wins_name->others=(struct in_addr *)talloc(mem_ctx, sizeof(struct in_addr)*wins_name->num_ip);
+ if (wins_name->others==NULL)
+ return;
+
+ if(!check_buffer(outbuf, 4*wins_name->num_ip))
+ return;
+ for (i=0; i<wins_name->num_ip; i++) {
+ wins_name->others[i].s_addr=IVAL(outbuf->buffer, outbuf->offset);
+ outbuf->offset+=4;
+ }
+ }
+
+ if(!check_buffer(outbuf, 4))
+ return;
+ wins_name->foo=RIVAL(outbuf->buffer, outbuf->offset);
+ outbuf->offset+=4;
+
+}
+
+/****************************************************************************
+decode a update notification request
+****************************************************************************/
+static void decode_update_notify_request(struct BUFFER *inbuf, UPDATE_NOTIFY_REQUEST *un_rq)
+{
+ int i;
+
+ if(!check_buffer(inbuf, 4))
+ return;
+ un_rq->partner_count=RIVAL(inbuf->buffer, inbuf->offset);
+ inbuf->offset+=4;
+
+ un_rq->wins_owner=(WINS_OWNER *)talloc(mem_ctx, un_rq->partner_count*sizeof(WINS_OWNER));
+ if (un_rq->wins_owner==NULL)
+ return;
+
+ for (i=0; i<un_rq->partner_count; i++)
+ decode_wins_owner(inbuf, &un_rq->wins_owner[i]);
+
+ if(!check_buffer(inbuf, 4))
+ return;
+ un_rq->initiating_wins_server.s_addr=IVAL(inbuf->buffer, inbuf->offset);
+ inbuf->offset+=4;
+}
+
+/****************************************************************************
+decode a send entries request
+****************************************************************************/
+static void decode_send_entries_request(struct BUFFER *inbuf, SEND_ENTRIES_REQUEST *se_rq)
+{
+ decode_wins_owner(inbuf, &se_rq->wins_owner);
+}
+
+/****************************************************************************
+decode a send entries reply
+****************************************************************************/
+static void decode_send_entries_reply(struct BUFFER *inbuf, SEND_ENTRIES_REPLY *se_rp)
+{
+ int i;
+
+ if(!check_buffer(inbuf, 4))
+ return;
+ se_rp->max_names = RIVAL(inbuf->buffer, inbuf->offset);
+ inbuf->offset+=4;
+
+ se_rp->wins_name=(WINS_NAME *)talloc(mem_ctx, se_rp->max_names*sizeof(WINS_NAME));
+ if (se_rp->wins_name==NULL)
+ return;
+
+ for (i=0; i<se_rp->max_names; i++)
+ decode_wins_name(inbuf, &se_rp->wins_name[i]);
+}
+
+/****************************************************************************
+decode a add version number map table reply
+****************************************************************************/
+static void decode_add_version_number_map_table_reply(struct BUFFER *inbuf, AVMT_REP *avmt_rep)
+{
+ int i;
+
+ if(!check_buffer(inbuf, 4))
+ return;
+
+ avmt_rep->partner_count=RIVAL(inbuf->buffer, inbuf->offset);
+ inbuf->offset+=4;
+
+ avmt_rep->wins_owner=(WINS_OWNER *)talloc(mem_ctx, avmt_rep->partner_count*sizeof(WINS_OWNER));
+ if (avmt_rep->wins_owner==NULL)
+ return;
+
+ for (i=0; i<avmt_rep->partner_count; i++)
+ decode_wins_owner(inbuf, &avmt_rep->wins_owner[i]);
+
+ if(!check_buffer(inbuf, 4))
+ return;
+ avmt_rep->initiating_wins_server.s_addr=IVAL(inbuf->buffer, inbuf->offset);
+ inbuf->offset+=4;
+}
+
+/****************************************************************************
+decode a replicate packet and fill a structure
+****************************************************************************/
+static void decode_replicate(struct BUFFER *inbuf, REPLICATE *rep)
+{
+ if(!check_buffer(inbuf, 4))
+ return;
+
+ rep->msg_type = RIVAL(inbuf->buffer, inbuf->offset);
+
+ inbuf->offset+=4;
+
+ switch (rep->msg_type) {
+ case 0:
+ break;
+ case 1:
+ /* add version number map table reply */
+ decode_add_version_number_map_table_reply(inbuf, &rep->avmt_rep);
+ break;
+ case 2:
+ /* send entry request */
+ decode_send_entries_request(inbuf, &rep->se_rq);
+ break;
+ case 3:
+ /* send entry request */
+ decode_send_entries_reply(inbuf, &rep->se_rp);
+ break;
+ case 4:
+ /* update notification request */
+ decode_update_notify_request(inbuf, &rep->un_rq);
+ break;
+ default:
+ DEBUG(0,("decode_replicate: unknown message type:%d\n", rep->msg_type));
+ break;
+ }
+}
+
+/****************************************************************************
+read the generic header and fill the struct.
+****************************************************************************/
+static void read_generic_header(struct BUFFER *inbuf, generic_header *q)
+{
+ if(!check_buffer(inbuf, 16))
+ return;
+
+ q->data_size = RIVAL(inbuf->buffer, inbuf->offset+0);
+ q->opcode = RIVAL(inbuf->buffer, inbuf->offset+4);
+ q->assoc_ctx = RIVAL(inbuf->buffer, inbuf->offset+8);
+ q->mess_type = RIVAL(inbuf->buffer, inbuf->offset+12);
+}
+
+/*******************************************************************
+decode a start association request
+********************************************************************/
+static void decode_start_assoc_request(struct BUFFER *inbuf, START_ASSOC_REQUEST *q)
+{
+ if(!check_buffer(inbuf, 8))
+ return;
+
+ q->assoc_ctx = RIVAL(inbuf->buffer, inbuf->offset+0);
+ q->min_ver = RSVAL(inbuf->buffer, inbuf->offset+4);
+ q->maj_ver = RSVAL(inbuf->buffer, inbuf->offset+6);
+}
+
+/*******************************************************************
+decode a start association reply
+********************************************************************/
+static void decode_start_assoc_reply(struct BUFFER *inbuf, START_ASSOC_REPLY *r)
+{
+ if(!check_buffer(inbuf, 8))
+ return;
+
+ r->assoc_ctx=RIVAL(inbuf->buffer, inbuf->offset+0);
+ r->min_ver = RSVAL(inbuf->buffer, inbuf->offset+4);
+ r->maj_ver = RSVAL(inbuf->buffer, inbuf->offset+6);
+}
+
+/*******************************************************************
+decode a start association reply
+********************************************************************/
+static void decode_stop_assoc(struct BUFFER *inbuf, STOP_ASSOC *r)
+{
+ if(!check_buffer(inbuf, 4))
+ return;
+
+ r->reason=RIVAL(inbuf->buffer, inbuf->offset);
+}
+
+/****************************************************************************
+decode a packet and fill a generic structure
+****************************************************************************/
+void decode_generic_packet(struct BUFFER *inbuf, GENERIC_PACKET *q)
+{
+ read_generic_header(inbuf, &q->header);
+
+ inbuf->offset+=16;
+
+ switch (q->header.mess_type) {
+ case 0:
+ decode_start_assoc_request(inbuf, &q->sa_rq);
+ break;
+ case 1:
+ decode_start_assoc_reply(inbuf, &q->sa_rp);
+ break;
+ case 2:
+ decode_stop_assoc(inbuf, &q->so);
+ break;
+ case 3:
+ decode_replicate(inbuf, &q->rep);
+ break;
+ default:
+ DEBUG(0,("decode_generic_packet: unknown message type:%d\n", q->header.mess_type));
+ break;
+ }
+}
+
+/****************************************************************************
+encode a WINS_OWNER struct
+****************************************************************************/
+static void encode_wins_owner(struct BUFFER *outbuf, WINS_OWNER *wins_owner)
+{
+ if (!grow_buffer(outbuf, 24))
+ return;
+
+ SIVAL(outbuf->buffer, outbuf->offset, wins_owner->address.s_addr);
+ outbuf->offset+=4;
+ RSIVAL(outbuf->buffer, outbuf->offset, (int)(wins_owner->max_version>>32));
+ outbuf->offset+=4;
+ RSIVAL(outbuf->buffer, outbuf->offset, (int)(wins_owner->max_version&0xffffffff));
+ outbuf->offset+=4;
+ RSIVAL(outbuf->buffer, outbuf->offset, wins_owner->min_version>>32);
+ outbuf->offset+=4;
+ RSIVAL(outbuf->buffer, outbuf->offset, wins_owner->min_version&0xffffffff);
+ outbuf->offset+=4;
+ RSIVAL(outbuf->buffer, outbuf->offset, wins_owner->type);
+ outbuf->offset+=4;
+
+}
+
+/****************************************************************************
+encode a WINS_NAME struct
+****************************************************************************/
+static void encode_wins_name(struct BUFFER *outbuf, WINS_NAME *wins_name)
+{
+ int i;
+
+ if (!grow_buffer(outbuf, 48+(4*wins_name->num_ip)))
+ return;
+
+ RSIVAL(outbuf->buffer, outbuf->offset, wins_name->name_len);
+ outbuf->offset+=4;
+
+ memset(outbuf->buffer+outbuf->offset, ' ', 15);
+
+ /* to prevent copying the leading \0 */
+ memcpy(outbuf->buffer+outbuf->offset, wins_name->name, strlen(wins_name->name));
+ outbuf->offset+=15;
+
+ outbuf->buffer[outbuf->offset++]=(char)wins_name->type;
+
+ RSIVAL(outbuf->buffer, outbuf->offset, wins_name->empty);
+ outbuf->offset+=4;
+
+ RSIVAL(outbuf->buffer, outbuf->offset, wins_name->name_flag);
+ outbuf->offset+=4;
+ RSIVAL(outbuf->buffer, outbuf->offset, wins_name->group_flag);
+ outbuf->offset+=4;
+ RSIVAL(outbuf->buffer, outbuf->offset, wins_name->id>>32);
+ outbuf->offset+=4;
+ RSIVAL(outbuf->buffer, outbuf->offset, wins_name->id);
+ outbuf->offset+=4;
+
+ if (wins_name->name_flag & 2) {
+ SIVAL(outbuf->buffer, outbuf->offset, wins_name->num_ip);
+ outbuf->offset+=4;
+ }
+
+ SIVAL(outbuf->buffer, outbuf->offset, wins_name->owner.s_addr);
+ outbuf->offset+=4;
+
+ if (wins_name->name_flag & 2) {
+ for (i=0;i<wins_name->num_ip;i++) {
+ SIVAL(outbuf->buffer, outbuf->offset, wins_name->others[i].s_addr);
+ outbuf->offset+=4;
+ }
+ }
+
+ RSIVAL(outbuf->buffer, outbuf->offset, wins_name->foo);
+ outbuf->offset+=4;
+}
+
+/****************************************************************************
+encode a update notification request
+****************************************************************************/
+static void encode_update_notify_request(struct BUFFER *outbuf, UPDATE_NOTIFY_REQUEST *un_rq)
+{
+ int i;
+
+ if (!grow_buffer(outbuf, 8))
+ return;
+
+ RSIVAL(outbuf->buffer, outbuf->offset, un_rq->partner_count);
+ outbuf->offset+=4;
+
+ for (i=0; i<un_rq->partner_count; i++)
+ encode_wins_owner(outbuf, &un_rq->wins_owner[i]);
+
+ SIVAL(outbuf->buffer, outbuf->offset, un_rq->initiating_wins_server.s_addr);
+ outbuf->offset+=4;
+
+}
+
+/****************************************************************************
+decode a send entries request
+****************************************************************************/
+static void encode_send_entries_request(struct BUFFER *outbuf, SEND_ENTRIES_REQUEST *se_rq)
+{
+ encode_wins_owner(outbuf, &se_rq->wins_owner);
+}
+
+/****************************************************************************
+decode a send entries reply
+****************************************************************************/
+static void encode_send_entries_reply(struct BUFFER *outbuf, SEND_ENTRIES_REPLY *se_rp)
+{
+ int i;
+
+ if (!grow_buffer(outbuf, 4))
+ return;
+
+ RSIVAL(outbuf->buffer, outbuf->offset, se_rp->max_names);
+ outbuf->offset+=4;
+
+ for (i=0; i<se_rp->max_names; i++)
+ encode_wins_name(outbuf, &se_rp->wins_name[i]);
+
+}
+
+/****************************************************************************
+encode a add version number map table reply
+****************************************************************************/
+static void encode_add_version_number_map_table_reply(struct BUFFER *outbuf, AVMT_REP *avmt_rep)
+{
+ int i;
+
+ if (!grow_buffer(outbuf, 8))
+ return;
+
+ RSIVAL(outbuf->buffer, outbuf->offset, avmt_rep->partner_count);
+ outbuf->offset+=4;
+
+ for (i=0; i<avmt_rep->partner_count; i++)
+ encode_wins_owner(outbuf, &avmt_rep->wins_owner[i]);
+
+ SIVAL(outbuf->buffer, outbuf->offset, avmt_rep->initiating_wins_server.s_addr);
+ outbuf->offset+=4;
+
+}
+
+/****************************************************************************
+decode a replicate packet and fill a structure
+****************************************************************************/
+static void encode_replicate(struct BUFFER *outbuf, REPLICATE *rep)
+{
+ if (!grow_buffer(outbuf, 4))
+ return;
+
+ RSIVAL(outbuf->buffer, outbuf->offset, rep->msg_type);
+ outbuf->offset+=4;
+
+ switch (rep->msg_type) {
+ case 0:
+ break;
+ case 1:
+ /* add version number map table reply */
+ encode_add_version_number_map_table_reply(outbuf, &rep->avmt_rep);
+ break;
+ case 2:
+ /* send entry request */
+ encode_send_entries_request(outbuf, &rep->se_rq);
+ break;
+ case 3:
+ /* send entry request */
+ encode_send_entries_reply(outbuf, &rep->se_rp);
+ break;
+ case 4:
+ /* update notification request */
+ encode_update_notify_request(outbuf, &rep->un_rq);
+ break;
+ default:
+ DEBUG(0,("encode_replicate: unknown message type:%d\n", rep->msg_type));
+ break;
+ }
+}
+
+/****************************************************************************
+write the generic header.
+****************************************************************************/
+static void write_generic_header(struct BUFFER *outbuf, generic_header *r)
+{
+ RSIVAL(outbuf->buffer, 0, r->data_size);
+ RSIVAL(outbuf->buffer, 4, r->opcode);
+ RSIVAL(outbuf->buffer, 8, r->assoc_ctx);
+ RSIVAL(outbuf->buffer,12, r->mess_type);
+}
+
+/*******************************************************************
+decode a start association request
+********************************************************************/
+static void encode_start_assoc_request(struct BUFFER *outbuf, START_ASSOC_REQUEST *q)
+{
+ if (!grow_buffer(outbuf, 45))
+ return;
+
+ RSIVAL(outbuf->buffer, outbuf->offset, q->assoc_ctx);
+ RSSVAL(outbuf->buffer, outbuf->offset+4, q->min_ver);
+ RSSVAL(outbuf->buffer, outbuf->offset+6, q->maj_ver);
+
+ outbuf->offset=45;
+}
+
+/*******************************************************************
+decode a start association reply
+********************************************************************/
+static void encode_start_assoc_reply(struct BUFFER *outbuf, START_ASSOC_REPLY *r)
+{
+ if (!grow_buffer(outbuf, 45))
+ return;
+
+ RSIVAL(outbuf->buffer, outbuf->offset, r->assoc_ctx);
+ RSSVAL(outbuf->buffer, outbuf->offset+4, r->min_ver);
+ RSSVAL(outbuf->buffer, outbuf->offset+6, r->maj_ver);
+
+ outbuf->offset=45;
+}
+
+/*******************************************************************
+decode a start association reply
+********************************************************************/
+static void encode_stop_assoc(struct BUFFER *outbuf, STOP_ASSOC *r)
+{
+ if (!grow_buffer(outbuf, 44))
+ return;
+
+ RSIVAL(outbuf->buffer, outbuf->offset, r->reason);
+
+ outbuf->offset=44;
+}
+
+/****************************************************************************
+write the generic header size.
+****************************************************************************/
+static void write_generic_header_size(generic_header *r, int size)
+{
+ /* the buffer size is the total size minus the size field */
+ r->data_size=size-4;
+}
+
+/****************************************************************************
+encode a packet and read a generic structure
+****************************************************************************/
+void encode_generic_packet(struct BUFFER *outbuf, GENERIC_PACKET *q)
+{
+ if (!grow_buffer(outbuf, 16))
+ return;
+
+ outbuf->offset=16;
+
+ switch (q->header.mess_type) {
+ case 0:
+ encode_start_assoc_request(outbuf, &q->sa_rq);
+ break;
+ case 1:
+ encode_start_assoc_reply(outbuf, &q->sa_rp);
+ break;
+ case 2:
+ encode_stop_assoc(outbuf, &q->so);
+ break;
+ case 3:
+ encode_replicate(outbuf, &q->rep);
+ break;
+ default:
+ DEBUG(0,("encode_generic_packet: unknown message type:%d\n", q->header.mess_type));
+ break;
+ }
+
+ write_generic_header_size(&q->header, outbuf->offset);
+ write_generic_header(outbuf, &q->header);
+}
+
+
+/****************************************************************************
+dump a WINS_OWNER structure
+****************************************************************************/
+static void dump_wins_owner(WINS_OWNER *wins_owner)
+{
+ DEBUGADD(10,("\t\t\t\taddress : %s\n", inet_ntoa(wins_owner->address)));
+ DEBUGADD(10,("\t\t\t\tmax version: %d\n", (int)wins_owner->max_version));
+ DEBUGADD(10,("\t\t\t\tmin version: %d\n", (int)wins_owner->min_version));
+ DEBUGADD(10,("\t\t\t\ttype : %d\n", wins_owner->type));
+}
+
+/****************************************************************************
+dump a WINS_NAME structure
+****************************************************************************/
+static void dump_wins_name(WINS_NAME *wins_name)
+{
+ fstring name;
+ int i;
+
+ strncpy(name, wins_name->name, 15);
+
+ DEBUGADD(10,("name: %d, %s<%02x> %x,%x, %d %s %d ", wins_name->name_len, name, wins_name->type,
+ wins_name->name_flag, wins_name->group_flag, (int)wins_name->id,
+ inet_ntoa(wins_name->owner), wins_name->num_ip));
+
+ if (wins_name->num_ip!=1)
+ for (i=0; i<wins_name->num_ip; i++)
+ DEBUGADD(10,("%s ", inet_ntoa(wins_name->others[i])));
+
+ DEBUGADD(10,("\n"));
+}
+
+/****************************************************************************
+dump a replicate structure
+****************************************************************************/
+static void dump_replicate(REPLICATE *rep)
+{
+ int i;
+
+ DEBUGADD(5,("\t\tmsg_type: %d ", rep->msg_type));
+
+ switch (rep->msg_type) {
+ case 0:
+ DEBUGADD(5,("(Add Version Map Table Request)\n"));
+ break;
+ case 1:
+ DEBUGADD(5,("(Add Version Map Table Reply)\n"));
+ DEBUGADD(5,("\t\t\tpartner_count : %d\n", rep->avmt_rep.partner_count));
+ for (i=0; i<rep->avmt_rep.partner_count; i++)
+ dump_wins_owner(&rep->avmt_rep.wins_owner[i]);
+ DEBUGADD(5,("\t\t\tinitiating_wins_server: %s\n", inet_ntoa(rep->avmt_rep.initiating_wins_server)));
+ break;
+ case 2:
+ DEBUGADD(5,("(Send Entries Request)\n"));
+ dump_wins_owner(&rep->se_rq.wins_owner);
+ break;
+ case 3:
+ DEBUGADD(5,("(Send Entries Reply)\n"));
+ DEBUGADD(5,("\t\t\tmax_names : %d\n", rep->se_rp.max_names));
+ for (i=0; i<rep->se_rp.max_names; i++)
+ dump_wins_name(&rep->se_rp.wins_name[i]);
+ break;
+ case 4:
+ DEBUGADD(5,("(Update Notify Request)\n"));
+ DEBUGADD(5,("\t\t\tpartner_count : %d\n", rep->un_rq.partner_count));
+ for (i=0; i<rep->un_rq.partner_count; i++)
+ dump_wins_owner(&rep->un_rq.wins_owner[i]);
+ DEBUGADD(5,("\t\t\tinitiating_wins_server: %s\n", inet_ntoa(rep->un_rq.initiating_wins_server)));
+ break;
+ default:
+ DEBUG(5,("\n"));
+ break;
+ }
+}
+
+/****************************************************************************
+dump a generic structure
+****************************************************************************/
+void dump_generic_packet(GENERIC_PACKET *q)
+{
+ DEBUG(5,("dump_generic_packet:\n"));
+ DEBUGADD(5,("\tdata_size: %08x\n", q->header.data_size));
+ DEBUGADD(5,("\topcode : %08x\n", q->header.opcode));
+ DEBUGADD(5,("\tassoc_ctx: %08x\n", q->header.assoc_ctx));
+ DEBUGADD(5,("\tmess_type: %08x ", q->header.mess_type));
+
+ switch (q->header.mess_type) {
+ case 0:
+ DEBUGADD(5,("(Start Association Request)\n"));
+ DEBUGADD(5,("\t\tassoc_ctx: %08x\n", q->sa_rq.assoc_ctx));
+ DEBUGADD(5,("\t\tmin_ver : %04x\n", q->sa_rq.min_ver));
+ DEBUGADD(5,("\t\tmaj_ver : %04x\n", q->sa_rq.maj_ver));
+ break;
+ case 1:
+ DEBUGADD(5,("(Start Association Reply)\n"));
+ DEBUGADD(5,("\t\tassoc_ctx: %08x\n", q->sa_rp.assoc_ctx));
+ DEBUGADD(5,("\t\tmin_ver : %04x\n", q->sa_rp.min_ver));
+ DEBUGADD(5,("\t\tmaj_ver : %04x\n", q->sa_rp.maj_ver));
+ break;
+ case 2:
+ DEBUGADD(5,("(Stop Association)\n"));
+ DEBUGADD(5,("\t\treason: %08x\n", q->so.reason));
+ break;
+ case 3:
+ DEBUGADD(5,("(Replication Message)\n"));
+ dump_replicate(&q->rep);
+ break;
+ default:
+ DEBUG(5,("\n"));
+ break;
+ }
+
+}
+
+/****************************************************************************
+generate a stop packet
+****************************************************************************/
+void stop_packet(GENERIC_PACKET *q, GENERIC_PACKET *r, int reason)
+{
+ r->header.opcode=OPCODE_NON_NBT;
+ r->header.assoc_ctx=get_server_assoc(q->header.assoc_ctx);
+ r->header.mess_type=MESSAGE_TYPE_STOP_ASSOC;
+ r->so.reason=reason;
+
+}
+
+
diff --git a/source/wrepld/partners.c b/source/wrepld/partners.c
new file mode 100644
index 00000000000..2387f5b45f6
--- /dev/null
+++ b/source/wrepld/partners.c
@@ -0,0 +1,200 @@
+/*
+ Unix SMB/CIFS implementation.
+ process incoming packets - main loop
+ Copyright (C) Jean François Micouleau 1998-2002.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "wins_repl.h"
+
+/* we can exchange info with 64 partners at any given time */
+WINS_PARTNER current_partners[64];
+int total_current_partners;
+
+/*******************************************************************
+verify if we know this partner
+********************************************************************/
+BOOL check_partner(int assoc)
+{
+ int i;
+
+ DEBUG(5,("check_partner: total_current_partners: %d\n", total_current_partners));
+
+ for (i=0; i<total_current_partners; i++)
+ if (current_partners[i].client_assoc==assoc)
+ return True;
+
+ return False;
+}
+
+/*******************************************************************
+add a new entry to the list
+********************************************************************/
+BOOL add_partner(int client_assoc, int server_assoc, BOOL pull, BOOL push)
+{
+ DEBUG(5,("add_partner: total_current_partners: %d\n", total_current_partners));
+
+ if (total_current_partners==64)
+ return False;
+
+ current_partners[total_current_partners].client_assoc=client_assoc;
+ current_partners[total_current_partners].server_assoc=server_assoc;
+ current_partners[total_current_partners].pull_partner=pull;
+ current_partners[total_current_partners].push_partner=push;
+
+ total_current_partners++;
+
+ return True;
+}
+
+/*******************************************************************
+remove an entry to the list
+********************************************************************/
+BOOL remove_partner(int client_assoc)
+{
+ int i,j;
+
+ DEBUG(5,("remove_partner: total_current_partners: %d\n", total_current_partners));
+
+ for (i=0; current_partners[i].client_assoc!=client_assoc && i<total_current_partners; i++)
+ ;
+
+ if (i==total_current_partners)
+ return False;
+
+ for (j=i+1; j<total_current_partners; j++) {
+ current_partners[j-1].client_assoc=current_partners[j].client_assoc;
+ current_partners[j-1].server_assoc=current_partners[j].server_assoc;
+ current_partners[j-1].pull_partner=current_partners[j].pull_partner;
+ current_partners[j-1].push_partner=current_partners[j].push_partner;
+ current_partners[j-1].partner_server.s_addr=current_partners[j].partner_server.s_addr;
+ current_partners[j-1].other_server.s_addr=current_partners[j].other_server.s_addr;
+ }
+
+ total_current_partners--;
+
+ return True;
+}
+
+/*******************************************************************
+link the client and server context
+********************************************************************/
+BOOL update_server_partner(int client_assoc, int server_assoc)
+{
+ int i;
+
+ DEBUG(5,("update_server_partner: total_current_partners: %d\n", total_current_partners));
+
+ for (i=0; i<total_current_partners; i++)
+ if (current_partners[i].client_assoc==client_assoc) {
+ current_partners[i].server_assoc=server_assoc;
+ return True;
+ }
+
+ return False;
+}
+
+/*******************************************************************
+verify if it's a pull partner
+********************************************************************/
+BOOL check_pull_partner(int assoc)
+{
+ int i;
+
+ DEBUG(5,("check_pull_partner: total_current_partners: %d\n", total_current_partners));
+
+ for (i=0; i<total_current_partners; i++)
+ if (current_partners[i].client_assoc==assoc &&
+ current_partners[i].pull_partner==True)
+ return True;
+
+ return False;
+}
+
+/*******************************************************************
+verify if it's a push partner
+********************************************************************/
+BOOL check_push_partner(int assoc)
+{
+ int i;
+
+ DEBUG(5,("check_push_partner: total_current_partners: %d\n", total_current_partners));
+
+ for (i=0; i<total_current_partners; i++)
+ if (current_partners[i].client_assoc==assoc &&
+ current_partners[i].push_partner==True)
+ return True;
+
+ return False;
+}
+
+/*******************************************************************
+return the server ctx linked to the client ctx
+********************************************************************/
+int get_server_assoc(int assoc)
+{
+ int i;
+
+ DEBUG(5,("get_server_assoc: total_current_partners: %d\n", total_current_partners));
+
+ for (i=0; i<total_current_partners; i++)
+ if (current_partners[i].client_assoc==assoc)
+ return current_partners[i].server_assoc;
+
+ return 0;
+}
+
+
+/*******************************************************************
+link the client and server context
+********************************************************************/
+BOOL write_server_assoc_table(int client_assoc, struct in_addr partner, struct in_addr server)
+{
+ int i;
+
+ DEBUG(5,("write_server_assoc_table: total_current_partners: %d\n", total_current_partners));
+
+ for (i=0; i<total_current_partners; i++)
+ if (current_partners[i].client_assoc==client_assoc) {
+ current_partners[i].partner_server=partner;
+ current_partners[i].other_server=server;
+ return True;
+ }
+
+ return False;
+}
+
+/*******************************************************************
+link the client and server context
+********************************************************************/
+BOOL get_server_assoc_table(int client_assoc, struct in_addr *partner, struct in_addr *server)
+{
+ int i;
+
+ DEBUG(5,("get_server_assoc_table: total_current_partners: %d\n", total_current_partners));
+
+ for (i=0; i<total_current_partners; i++)
+ if (current_partners[i].client_assoc==client_assoc) {
+ partner->s_addr=current_partners[i].partner_server.s_addr;
+ server->s_addr=current_partners[i].other_server.s_addr;
+ return True;
+ }
+
+ return False;
+}
+
+
diff --git a/source/wrepld/process.c b/source/wrepld/process.c
new file mode 100644
index 00000000000..0e9a9b34610
--- /dev/null
+++ b/source/wrepld/process.c
@@ -0,0 +1,983 @@
+/*
+ Unix SMB/CIFS implementation.
+ process incoming packets - main loop
+ Copyright (C) Jean François Micouleau 1998-2002.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "wins_repl.h"
+
+extern fd_set *listen_set;
+extern int listen_number;
+extern int *sock_array;
+
+WINS_OWNER global_wins_table[64][64];
+int partner_count;
+
+TALLOC_CTX *mem_ctx;
+
+#define WINS_LIST "wins.tdb"
+#define INFO_VERSION "INFO/version"
+#define INFO_COUNT "INFO/num_entries"
+#define INFO_ID_HIGH "INFO/id_high"
+#define INFO_ID_LOW "INFO/id_low"
+#define ENTRY_PREFIX "ENTRY/"
+
+
+/*******************************************************************
+fill the header of a reply.
+********************************************************************/
+static void fill_header(GENERIC_PACKET *g, int opcode, int ctx, int mess)
+{
+ if (g==NULL)
+ return;
+
+ g->header.opcode=opcode;
+ g->header.assoc_ctx=ctx;
+ g->header.mess_type=mess;
+}
+
+/*******************************************************************
+dump the global table, that's a debug code.
+********************************************************************/
+static void dump_global_table(void)
+{
+ int i,j;
+
+ for (i=0;i<partner_count;i++) {
+ DEBUG(10,("\n%d ", i));
+ for (j=0; global_wins_table[i][j].address.s_addr!=0; j++)
+ DEBUG(10,("%s:%d \t", inet_ntoa(global_wins_table[i][j].address),
+ (int)global_wins_table[i][j].max_version));
+ }
+ DEBUG(10,("\n"));
+}
+
+/*******************************************************************
+start association
+********************************************************************/
+static void start_assoc_process(GENERIC_PACKET *q, GENERIC_PACKET *r)
+{
+ /*
+ * add this request to our current wins partners list
+ * this list is used to know with who we are in contact
+ *
+ */
+ r->sa_rp.assoc_ctx=time(NULL);
+ fill_header(r, OPCODE_NON_NBT, q->sa_rq.assoc_ctx, MESSAGE_TYPE_START_ASSOC_REPLY);
+
+ /* reply we are a NT4 server */
+
+ /* w2K is min=2, maj=5 */
+
+ r->sa_rp.min_ver=1;
+ r->sa_rp.maj_ver=1;
+
+ add_partner(r->sa_rp.assoc_ctx, q->sa_rq.assoc_ctx, False, False);
+}
+
+/*******************************************************************
+start association reply
+********************************************************************/
+static void start_assoc_reply(GENERIC_PACKET *q, GENERIC_PACKET *r)
+{
+ int i;
+
+ /* check if we have already registered this client */
+ if (!check_partner(q->header.assoc_ctx)) {
+ DEBUG(0,("start_assoc_reply: unknown client\n"));
+ stop_packet(q, r, STOP_REASON_USER_REASON);
+ return;
+ }
+
+ if (!update_server_partner(q->header.assoc_ctx, q->sa_rp.assoc_ctx)) {
+ DEBUG(0,("start_assoc_reply: can't update server ctx\n"));
+ stop_packet(q, r, STOP_REASON_USER_REASON);
+ return;
+ }
+
+ /* if pull, request map table */
+ if (check_pull_partner(q->header.assoc_ctx)) {
+ fill_header(r, OPCODE_NON_NBT, get_server_assoc(q->header.assoc_ctx), MESSAGE_TYPE_REPLICATE);
+
+ r->rep.msg_type=MESSAGE_REP_ADD_VERSION_REQUEST;
+ DEBUG(5,("start_assoc_reply: requesting map table\n"));
+
+ return;
+ }
+
+ /* if push, send our table */
+ if (check_push_partner(q->header.assoc_ctx)) {
+ fill_header(r, OPCODE_NON_NBT, get_server_assoc(q->header.assoc_ctx), MESSAGE_TYPE_REPLICATE);
+ r->rep.msg_type=MESSAGE_REP_UPDATE_NOTIFY_REQUEST;
+ r->rep.un_rq.partner_count=partner_count;
+
+ r->rep.un_rq.wins_owner=(WINS_OWNER *)talloc(mem_ctx, partner_count*sizeof(WINS_OWNER));
+ if (r->rep.un_rq.wins_owner==NULL) {
+ DEBUG(0,("start_assoc_reply: can't alloc memory\n"));
+ stop_packet(q, r, STOP_REASON_USER_REASON);
+ return;
+ }
+
+ for (i=0; i<partner_count; i++)
+ r->rep.un_rq.wins_owner[i]=global_wins_table[0][i];
+
+ DEBUG(5,("start_assoc_reply: sending update table\n"));
+ return;
+ }
+
+ /* neither push/pull, stop */
+ /* we should not come here */
+ DEBUG(0,("we have a partner which is neither push nor pull !\n"));
+ stop_packet(q, r, STOP_REASON_USER_REASON);
+}
+
+/****************************************************************************
+initialise and fill the in-memory partner table.
+****************************************************************************/
+int init_wins_partner_table(void)
+{
+ int i=1,j=0,k;
+ char **partner = str_list_make(lp_wins_partners(), NULL);
+
+ if (partner==NULL) {
+ DEBUG(0,("wrepld: no partner list in smb.conf, exiting\n"));
+ exit_server("normal exit");
+ return(0);
+ }
+
+ DEBUG(4, ("init_wins_partner_table: partners: %s\n", lp_wins_partners()));
+
+ global_wins_table[0][0].address=*iface_n_ip(0);
+ global_wins_table[0][0].max_version=0;
+ global_wins_table[0][0].min_version=0;
+ global_wins_table[0][0].type=0;
+
+ while (partner[j]!=NULL) {
+ DEBUG(3,("init_wins_partner_table, adding partner: %s\n", partner[j]));
+
+ global_wins_table[0][i].address=*interpret_addr2(partner[j]);
+ global_wins_table[0][i].max_version=0;
+ global_wins_table[0][i].min_version=0;
+ global_wins_table[0][i].type=0;
+ global_wins_table[0][i].last_pull=0;
+ global_wins_table[0][i].last_push=0;
+
+ i++;
+ j++;
+ }
+
+ for (k=1; k<i;k++)
+ for (j=0; j<i; j++)
+ global_wins_table[k][j]=global_wins_table[0][j];
+
+ str_list_free (&partner);
+
+ return i;
+}
+
+/****************************************************************************
+read the last ID from the wins tdb file.
+****************************************************************************/
+static void get_our_last_id(WINS_OWNER *wins_owner)
+{
+ TDB_CONTEXT *tdb;
+
+ tdb = tdb_open_log(lock_path(WINS_LIST), 0, TDB_DEFAULT, O_RDONLY, 0600);
+ if (!tdb) {
+ DEBUG(2,("get_our_last_id: Can't open wins database file %s. Error was %s\n", WINS_LIST, strerror(errno) ));
+ return;
+ }
+
+ wins_owner->max_version=((SMB_BIG_UINT)tdb_fetch_int32(tdb, INFO_ID_HIGH))<<32 |
+ (SMB_BIG_UINT)tdb_fetch_int32(tdb, INFO_ID_LOW);
+
+ tdb_close(tdb);
+}
+
+/****************************************************************************
+send the list of wins server we know.
+****************************************************************************/
+static void send_version_number_map_table(GENERIC_PACKET *q, GENERIC_PACKET *r)
+{
+ int i;
+ int s_ctx=get_server_assoc(q->header.assoc_ctx);
+
+ if (s_ctx==0) {
+ DEBUG(5, ("send_version_number_map_table: request for a partner not in our table\n"));
+ stop_packet(q, r, STOP_REASON_USER_REASON);
+ return;
+ }
+
+ /*
+ * return an array of wins servers, we are partner with.
+ * each entry contains the IP address and the version info
+ * version: ID of the last entry we've got
+ */
+
+ /* the first wins server must be self */
+
+ /*
+ * get our last ID from the wins database
+ * it can have been updated since last read
+ * as nmbd got registration/release.
+ */
+ get_our_last_id(&global_wins_table[0][0]);
+
+ r->rep.avmt_rep.wins_owner=(WINS_OWNER *)talloc(mem_ctx, partner_count*sizeof(WINS_OWNER));
+ if (r->rep.avmt_rep.wins_owner==NULL) {
+ stop_packet(q, r, STOP_REASON_USER_REASON);
+ return;
+ }
+
+ DEBUG(5,("send_version_number_map_table: partner_count: %d\n", partner_count));
+
+ for (i=0; i<partner_count; i++) {
+ DEBUG(5,("send_version_number_map_table, partner: %d -> %s, \n", i, inet_ntoa(global_wins_table[0][i].address)));
+ r->rep.avmt_rep.wins_owner[i]=global_wins_table[0][i];
+ }
+
+ r->rep.msg_type=1;
+ r->rep.avmt_rep.partner_count=partner_count;
+ r->rep.avmt_rep.initiating_wins_server.s_addr=0; /* blatant lie, NT4/w2K do the same ! */
+ fill_header(r, OPCODE_NON_NBT, s_ctx, MESSAGE_TYPE_REPLICATE);
+}
+
+/****************************************************************************
+for a given partner, ask it to send entries we don't have.
+****************************************************************************/
+static BOOL check_partners_and_send_entries(GENERIC_PACKET *q, GENERIC_PACKET *r, int partner)
+{
+ int server;
+ int other;
+ SMB_BIG_UINT temp;
+ SMB_BIG_UINT current;
+
+
+ /*
+ * we check if our partner has more records than us.
+ * we need to check more than our direct partners as
+ * we can have this case:
+ * us: A, partners: B,C, indirect partner: D
+ * A<->B, A<->C, B<->D, C<->D
+ *
+ * So if we're talking to B, we need to check if between
+ * B and C, which one have more records about D.
+ * and also check if we don't already have the records.
+ */
+
+
+ /* check all servers even indirect */
+ for (server=1; global_wins_table[0][server].address.s_addr!=0; server++) {
+ current = global_wins_table[partner][server].max_version;
+
+ temp=0;
+
+ for (other=1; other<partner_count; other++) {
+ /* skip the partner itself */
+ if (other==partner)
+ continue;
+
+ if (global_wins_table[other][server].max_version > temp)
+ temp=global_wins_table[other][server].max_version;
+ }
+
+ if (current >= temp && current > global_wins_table[0][server].max_version) {
+ /*
+ * it has more records than every body else and more than us,
+ * ask it the difference between what we have and what it has
+ */
+ fill_header(r, OPCODE_NON_NBT, get_server_assoc(q->header.assoc_ctx), MESSAGE_TYPE_REPLICATE);
+
+ r->rep.msg_type=MESSAGE_REP_SEND_ENTRIES_REQUEST;
+ r->rep.se_rq.wins_owner.address=global_wins_table[partner][server].address;
+
+ r->rep.se_rq.wins_owner.max_version=global_wins_table[partner][server].max_version;
+ r->rep.se_rq.wins_owner.min_version=global_wins_table[0][server].max_version;
+ r->rep.se_rq.wins_owner.type=0;
+
+ write_server_assoc_table(q->header.assoc_ctx, global_wins_table[0][partner].address, global_wins_table[partner][server].address);
+
+ /*
+ * and we update our version for this server
+ * as we can't use the IDs returned in the send_entries function
+ * the max ID can be larger than the largest ID returned
+ */
+
+ global_wins_table[0][server].max_version=global_wins_table[partner][server].max_version;
+
+ return True;
+ }
+ }
+ return False;
+}
+
+/****************************************************************************
+receive the list of wins server we know.
+****************************************************************************/
+static void receive_version_number_map_table(GENERIC_PACKET *q, GENERIC_PACKET *r)
+{
+ fstring peer;
+ struct in_addr addr;
+ int i,j,k,l;
+ int s_ctx=get_server_assoc(q->header.assoc_ctx);
+
+ if (s_ctx==0) {
+ DEBUG(5, ("receive_version_number_map_table: request for a partner not in our table\n"));
+ stop_packet(q, r, STOP_REASON_USER_REASON);
+ return;
+ }
+
+ fstrcpy(peer,get_peer_addr(q->fd));
+ addr=*interpret_addr2(peer);
+
+ get_our_last_id(&global_wins_table[0][0]);
+
+ DEBUG(5,("receive_version_number_map_table: received a map of %d server from: %s\n",
+ q->rep.avmt_rep.partner_count ,inet_ntoa(q->rep.avmt_rep.initiating_wins_server)));
+ DEBUG(5,("real peer is: %s\n", peer));
+
+ for (i=0; global_wins_table[0][i].address.s_addr!=addr.s_addr && i<partner_count;i++)
+ ;
+
+ if (i==partner_count) {
+ DEBUG(5,("receive_version_number_map_table: unknown partner: %s\n", peer));
+ stop_packet(q, r, STOP_REASON_USER_REASON);
+ return;
+ }
+
+ for (j=0; j<q->rep.avmt_rep.partner_count;j++) {
+ /*
+ * search if we already have this entry or if it's a new one
+ * it can be a new one in case of propagation
+ */
+ for (k=0; global_wins_table[0][k].address.s_addr!=0 &&
+ global_wins_table[0][k].address.s_addr!=q->rep.avmt_rep.wins_owner[j].address.s_addr; k++);
+
+ global_wins_table[i][k].address.s_addr=q->rep.avmt_rep.wins_owner[j].address.s_addr;
+ global_wins_table[i][k].max_version=q->rep.avmt_rep.wins_owner[j].max_version;
+ global_wins_table[i][k].min_version=q->rep.avmt_rep.wins_owner[j].min_version;
+ global_wins_table[i][k].type=q->rep.avmt_rep.wins_owner[j].type;
+
+ /*
+ * in case it's a new one, rewrite the address for all the partner
+ * to reserve the slot.
+ */
+
+ for(l=0; l<partner_count; l++)
+ global_wins_table[l][k].address.s_addr=q->rep.avmt_rep.wins_owner[j].address.s_addr;
+ }
+
+ dump_global_table();
+
+ /*
+ * if this server have newer records than what we have
+ * for several wins servers, we need to ask it.
+ * Alas a send entry request is only on one server.
+ * So in the send entry reply, we'll ask for the next server if required.
+ */
+
+ if (check_partners_and_send_entries(q, r, i))
+ return;
+
+ /* it doesn't have more entries than us */
+ stop_packet(q, r, STOP_REASON_USER_REASON);
+}
+
+/****************************************************************************
+add an entry to the wins list we'll send.
+****************************************************************************/
+static BOOL add_record_to_winsname(WINS_NAME **wins_name, int *max_names, char *name, int type, int wins_flags, int id, struct in_addr *ip_list, int num_ips)
+{
+ WINS_NAME *temp_list;
+ int i;
+ int current=*max_names;
+
+ temp_list=talloc_realloc(mem_ctx, *wins_name, (current+1)*sizeof(WINS_NAME));
+ if (temp_list==NULL)
+ return False;
+
+ temp_list[current].name_len=0x11;
+
+ safe_strcpy(temp_list[current].name, name, 15);
+
+ temp_list[current].type=type;
+ temp_list[current].empty=0;
+
+ temp_list[current].name_flag=wins_flags;
+
+ if ( (wins_flags&0x03) == 1 || (wins_flags&0x03)==2)
+ temp_list[current].group_flag=0x01000000;
+ else
+ temp_list[current].group_flag=0x00000000;
+
+ temp_list[current].id=id;
+
+ temp_list[current].owner.s_addr=ip_list[0].s_addr;
+
+ if (temp_list[current].name_flag & 2) {
+ temp_list[current].num_ip=num_ips;
+ temp_list[current].others=(struct in_addr *)talloc(mem_ctx, sizeof(struct in_addr)*num_ips);
+ if (temp_list[current].others==NULL)
+ return False;
+
+ for (i=0; i<num_ips; i++)
+ temp_list[current].others[i].s_addr=ip_list[i].s_addr;
+
+ } else
+ temp_list[current].num_ip=1;
+
+ temp_list[current].foo=0xffffffff;
+
+ *wins_name=temp_list;
+
+ return True;
+}
+
+/****************************************************************************
+send the list of name we have.
+****************************************************************************/
+static void send_entry_request(GENERIC_PACKET *q, GENERIC_PACKET *r)
+{
+ int max_names=0;
+ int i;
+ time_t time_now = time(NULL);
+ WINS_OWNER *wins_owner;
+ TDB_CONTEXT *tdb;
+ TDB_DATA kbuf, dbuf, newkey;
+ int s_ctx=get_server_assoc(q->header.assoc_ctx);
+ int num_interfaces = iface_count();
+
+ if (s_ctx==0) {
+ DEBUG(1, ("send_entry_request: request for a partner not in our table\n"));
+ stop_packet(q, r, STOP_REASON_USER_REASON);
+ return;
+ }
+
+
+ wins_owner=&q->rep.se_rq.wins_owner;
+ r->rep.se_rp.wins_name=NULL;
+
+ DEBUG(3,("send_entry_request: we have been asked to send the list of wins records\n"));
+ DEBUGADD(3,("owned by: %s and between min: %d and max: %d\n", inet_ntoa(wins_owner->address),
+ (int)wins_owner->min_version, (int)wins_owner->max_version));
+
+ /*
+ * if we are asked to send records owned by us
+ * we overwrite the wins ip with 0.0.0.0
+ * to make it easy in case of multihomed
+ */
+
+ for (i=0; i<num_interfaces; i++)
+ if (ip_equal(wins_owner->address, *iface_n_ip(i))) {
+ wins_owner->address=*interpret_addr2("0.0.0.0");
+ break;
+ }
+
+
+ tdb = tdb_open_log(lock_path(WINS_LIST), 0, TDB_DEFAULT, O_RDONLY, 0600);
+ if (!tdb) {
+ DEBUG(2,("send_entry_request: Can't open wins database file %s. Error was %s\n", WINS_LIST, strerror(errno) ));
+ return;
+ }
+
+ for (kbuf = tdb_firstkey(tdb);
+ kbuf.dptr;
+ newkey = tdb_nextkey(tdb, kbuf), safe_free(kbuf.dptr), kbuf=newkey) {
+ fstring name_type;
+ pstring name, ip_str;
+ char *p;
+ int type = 0;
+ int nb_flags;
+ int ttl;
+ unsigned int num_ips;
+ int low, high;
+ SMB_BIG_UINT version;
+ struct in_addr wins_ip;
+ struct in_addr *ip_list;
+ int wins_flags;
+ int len;
+
+ if (strncmp(kbuf.dptr, ENTRY_PREFIX, strlen(ENTRY_PREFIX)) != 0)
+ continue;
+
+
+ dbuf = tdb_fetch(tdb, kbuf);
+ if (!dbuf.dptr)
+ continue;
+
+ fstrcpy(name_type, kbuf.dptr+strlen(ENTRY_PREFIX));
+ pstrcpy(name, name_type);
+
+ if((p = strchr(name,'#')) != NULL) {
+ *p = 0;
+ sscanf(p+1,"%x",&type);
+ }
+
+ len = tdb_unpack(dbuf.dptr, dbuf.dsize, "dddfddd",
+ &nb_flags,
+ &high,
+ &low,
+ ip_str,
+ &ttl,
+ &num_ips,
+ &wins_flags);
+
+ wins_ip=*interpret_addr2(ip_str);
+
+ /* Allocate the space for the ip_list. */
+ if((ip_list = (struct in_addr *)talloc(mem_ctx, num_ips * sizeof(struct in_addr))) == NULL) {
+ SAFE_FREE(dbuf.dptr);
+ DEBUG(0,("initialise_wins: talloc fail !\n"));
+ return;
+ }
+
+ for (i = 0; i < num_ips; i++) {
+ len += tdb_unpack(dbuf.dptr+len, dbuf.dsize-len, "f", ip_str);
+ ip_list[i] = *interpret_addr2(ip_str);
+ }
+
+ SAFE_FREE(dbuf.dptr);
+
+ /* add all entries that have 60 seconds or more to live */
+ if ((ttl - 60) > time_now || ttl == PERMANENT_TTL) {
+ if(ttl != PERMANENT_TTL)
+ ttl -= time_now;
+
+ DEBUG( 4, ("send_entry_request: add name: %s#%02x ttl = %d first IP %s flags = %2x\n",
+ name, type, ttl, inet_ntoa(ip_list[0]), nb_flags));
+
+ /* add the record to the list to send */
+ version=((SMB_BIG_UINT)high)<<32 | low;
+
+ if (wins_owner->min_version<=version && wins_owner->max_version>=version &&
+ wins_owner->address.s_addr==wins_ip.s_addr) {
+ if(!add_record_to_winsname(&r->rep.se_rp.wins_name, &max_names, name, type, wins_flags, version, ip_list, num_ips))
+ return;
+ max_names++;
+ }
+
+ } else {
+ DEBUG(4, ("send_entry_request: not adding name (ttl problem) %s#%02x ttl = %d first IP %s flags = %2x\n",
+ name, type, ttl, inet_ntoa(ip_list[0]), nb_flags));
+ }
+ }
+
+ tdb_close(tdb);
+
+ DEBUG(4,("send_entry_request, sending %d records\n", max_names));
+ fill_header(r, OPCODE_NON_NBT, s_ctx, MESSAGE_TYPE_REPLICATE);
+ r->rep.msg_type=MESSAGE_REP_SEND_ENTRIES_REPLY; /* reply */
+ r->rep.se_rp.max_names=max_names;
+}
+
+
+/****************************************************************************
+.
+****************************************************************************/
+static void update_notify_request(GENERIC_PACKET *q, GENERIC_PACKET *r)
+{
+ int i,j,k,l;
+ UPDATE_NOTIFY_REQUEST *u;
+ int s_ctx=get_server_assoc(q->header.assoc_ctx);
+
+ if (s_ctx==0) {
+ DEBUG(4, ("update_notify_request: request for a partner not in our table\n"));
+ stop_packet(q, r, STOP_REASON_USER_REASON);
+ return;
+ }
+
+ u=&q->rep.un_rq;
+
+ /* check if we already have the range of records */
+
+ DEBUG(5,("update_notify_request: wins server: %s offered this list of %d records:\n",
+ inet_ntoa(u->initiating_wins_server), u->partner_count));
+
+ get_our_last_id(&global_wins_table[0][0]);
+
+ for (i=0; i<partner_count; i++) {
+ if (global_wins_table[0][i].address.s_addr==u->initiating_wins_server.s_addr) {
+ DEBUG(5,("update_notify_request: found initiator at index %d\n", i));
+ break;
+ }
+ }
+
+ /*
+ * some explanation is required, before someone say it's crap.
+ *
+ * let's take an example, we have 2 wins partners, we already now
+ * that our max id is 10, partner 1 ID is 20 and partner 2 ID is 30
+ * the array looks like:
+ *
+ * 0 1 2
+ * 0 10 20 30
+ * 1
+ * 2
+ *
+ * we receive an update from partner 2 saying he has: 1:15, 2:40, 3:50
+ * we must enlarge the array to add partner 3, it will look like:
+ *
+ * 0 1 2 3
+ * 0 10 20 30
+ * 1
+ * 2 15 40 50
+ *
+ * now we know, we should pull from partner 2, the records 30->40 of 2 and 0->50 of 3.
+ * once the pull will be over, our table will look like:
+ *
+ * 0 1 2 3
+ * 0 10 20 40 50
+ * 1
+ * 2 15 40 50
+ *
+ *
+ */
+
+ for (j=0; j<u->partner_count;j++) {
+ /*
+ * search if we already have this entry or if it's a new one
+ * it can be a new one in case of propagation
+ */
+
+ for (k=0; global_wins_table[0][k].address.s_addr!=0 &&
+ global_wins_table[0][k].address.s_addr!=u->wins_owner[j].address.s_addr; k++);
+
+ global_wins_table[i][k].address.s_addr=u->wins_owner[j].address.s_addr;
+ global_wins_table[i][k].max_version=u->wins_owner[j].max_version;
+ global_wins_table[i][k].min_version=u->wins_owner[j].min_version;
+ global_wins_table[i][k].type=u->wins_owner[j].type;
+
+ /*
+ * in case it's a new one, rewrite the address for all the partner
+ * to reserve the slot.
+ */
+
+ for(l=0; l<partner_count; l++)
+ global_wins_table[l][k].address.s_addr=u->wins_owner[j].address.s_addr;
+ }
+
+ dump_global_table();
+
+ stop_packet(q, r, STOP_REASON_USER_REASON);
+}
+
+/****************************************************************************
+.
+****************************************************************************/
+static void send_entry_reply(GENERIC_PACKET *q, GENERIC_PACKET *r)
+{
+ int i,j,k;
+ struct in_addr partner, server;
+ pid_t pid;
+ int s_ctx=get_server_assoc(q->header.assoc_ctx);
+ WINS_RECORD record;
+
+ if (s_ctx==0) {
+ DEBUG(1, ("send_entry_reply: request for a partner not in our table\n"));
+ stop_packet(q, r, STOP_REASON_USER_REASON);
+ return;
+ }
+
+ DEBUG(5,("send_entry_reply:got %d new records\n", q->rep.se_rp.max_names));
+
+ /* we got records from a wins partner but that can be from another wins server */
+ /* hopefully we track that */
+
+ /* and the only doc available from MS is wrong ! */
+
+ get_server_assoc_table(q->header.assoc_ctx, &partner, &server);
+
+ for (j=0; global_wins_table[0][j].address.s_addr!=0; j++) {
+ if (global_wins_table[0][j].address.s_addr==server.s_addr) {
+ DEBUG(5,("send_entry_reply: found server at index %d\n", j));
+ break;
+ }
+ }
+
+ pid = pidfile_pid("nmbd");
+ if (pid == 0) {
+ DEBUG(0,("send_entry_reply: Can't find pid for nmbd\n"));
+ return;
+ }
+
+ for (k=0; k<q->rep.se_rp.max_names; k++) {
+ DEBUG(5,("send_entry_reply: %s<%02x> %d\n", q->rep.se_rp.wins_name[k].name, q->rep.se_rp.wins_name[k].type,
+ (int)q->rep.se_rp.wins_name[k].id));
+
+ safe_strcpy(record.name, q->rep.se_rp.wins_name[k].name, 16);
+ record.type=q->rep.se_rp.wins_name[k].type;
+ record.id=q->rep.se_rp.wins_name[k].id;
+ record.wins_flags=q->rep.se_rp.wins_name[k].name_flag&0x00ff;
+ record.num_ips=q->rep.se_rp.wins_name[k].num_ip;
+
+ record.wins_ip.s_addr=server.s_addr;
+
+ if (record.num_ips==1)
+ record.ip[0]=q->rep.se_rp.wins_name[k].owner;
+ else
+ for (i=0; i<record.num_ips; i++)
+ record.ip[i]=q->rep.se_rp.wins_name[k].others[i];
+
+ record.nb_flags=0;
+
+ if (record.wins_flags&WINS_NGROUP || record.wins_flags&WINS_SGROUP)
+ record.nb_flags|=NB_GROUP;
+
+ if (record.wins_flags&WINS_ACTIVE)
+ record.nb_flags|=NB_ACTIVE;
+
+ record.nb_flags|=record.wins_flags&WINS_HNODE;
+
+ message_send_pid(pid, MSG_WINS_NEW_ENTRY, &record, sizeof(record), False);
+
+ }
+
+ dump_global_table();
+
+ /*
+ * we got some entries,
+ * ask the partner to send us the map table again
+ * to get the other servers entries.
+ *
+ * we're getting the map table 1 time more than really
+ * required. We could remove that call, but that
+ * would complexify the code. I prefer this trade-of.
+ */
+ fill_header(r, OPCODE_NON_NBT, s_ctx, MESSAGE_TYPE_REPLICATE);
+
+ r->rep.msg_type=MESSAGE_REP_ADD_VERSION_REQUEST;
+}
+
+/****************************************************************************
+decode the replication message and reply.
+****************************************************************************/
+static void replicate(GENERIC_PACKET *q, GENERIC_PACKET *r)
+{
+ switch (q->rep.msg_type) {
+ case 0:
+ /* add version number map table request */
+ send_version_number_map_table(q, r);
+ break;
+ case 1:
+ receive_version_number_map_table(q, r);
+ break;
+ case 2:
+ /* send entry request */
+ send_entry_request(q, r);
+ break;
+ case 3:
+ /* send entry reply */
+ send_entry_reply(q, r);
+ break;
+ case 4:
+ /* update notification request */
+ update_notify_request(q, r);
+ break;
+ }
+}
+
+/****************************************************************************
+do a switch on the message type, and return the response size
+****************************************************************************/
+static BOOL switch_message(GENERIC_PACKET *q, GENERIC_PACKET *r)
+{
+ switch (q->header.mess_type) {
+ case 0:
+ /* Start association type */
+ start_assoc_process(q, r);
+ return True;
+ break;
+ case 1:
+ /* start association reply */
+ start_assoc_reply(q, r);
+ return True;
+ break;
+ case 2:
+ /* stop association message */
+ return False;
+ break;
+ case 3:
+ /* replication message */
+ replicate(q, r);
+ return True;
+ break;
+ }
+
+ return False;
+}
+
+
+/****************************************************************************
+ construct a reply to the incoming packet
+****************************************************************************/
+void construct_reply(struct wins_packet_struct *p)
+{
+ GENERIC_PACKET r;
+ struct BUFFER buffer;
+
+ buffer.buffer=NULL;
+ buffer.offset=0;
+ buffer.length=0;
+
+ DEBUG(5,("dump: received packet\n"));
+ dump_generic_packet(p->packet);
+
+ /* Verify if the request we got is from a listed partner */
+ if (!check_partner(p->packet->header.assoc_ctx)) {
+ fstring peer;
+ struct in_addr addr;
+ int i;
+ fstrcpy(peer,get_peer_addr(p->fd));
+ addr=*interpret_addr2(peer);
+
+ for (i=1; i<partner_count; i++)
+ if (ip_equal(addr, global_wins_table[0][i].address))
+ break;
+
+ if (i==partner_count) {
+ DEBUG(1,("construct_reply: got a request from a non peer machine: %s\n", peer));
+ stop_packet(p->packet, &r, STOP_REASON_AUTH_FAILED);
+ p->stop_packet=True;
+ encode_generic_packet(&buffer, &r);
+ if (!send_smb(p->fd, buffer.buffer))
+ exit_server("process_smb: send_smb failed.");
+ return;
+ }
+ }
+
+ if (switch_message(p->packet, &r)) {
+ encode_generic_packet(&buffer, &r);
+ DEBUG(5,("dump: sending packet\n"));
+ dump_generic_packet(&r);
+
+ if(buffer.offset > 0) {
+ if (!send_smb(p->fd, buffer.buffer))
+ exit_server("process_smb: send_smb failed.");
+ }
+ }
+
+ /* if we got a stop assoc or if we send a stop assoc, close the fd after */
+ if (p->packet->header.mess_type==MESSAGE_TYPE_STOP_ASSOC ||
+ r.header.mess_type==MESSAGE_TYPE_STOP_ASSOC) {
+ remove_partner(p->packet->header.assoc_ctx);
+ p->stop_packet=True;
+ }
+}
+
+/****************************************************************************
+ contact periodically our wins partner to do a pull replication
+****************************************************************************/
+void run_pull_replication(time_t t)
+{
+ /* we pull every 30 minutes to query about new records*/
+ int i, s;
+ struct BUFFER buffer;
+ GENERIC_PACKET p;
+
+ buffer.buffer=NULL;
+ buffer.offset=0;
+ buffer.length=0;
+
+ for (i=1; i<partner_count; i++) {
+ if (global_wins_table[0][i].last_pull < t) {
+ global_wins_table[0][i].last_pull=t+30*60; /* next in 30 minutes */
+
+ /* contact the wins server */
+ p.header.mess_type=MESSAGE_TYPE_START_ASSOC_REQUEST;
+ p.header.opcode=OPCODE_NON_NBT;
+ p.header.assoc_ctx=0;
+ p.sa_rq.assoc_ctx=(int)t;
+ p.sa_rq.min_ver=1;
+ p.sa_rq.maj_ver=1;
+
+ DEBUG(3,("run_pull_replication: contacting wins server %s.\n", inet_ntoa(global_wins_table[0][i].address)));
+ encode_generic_packet(&buffer, &p);
+ dump_generic_packet(&p);
+
+ /* send the packet to the server and add the descriptor to receive answers */
+ s=open_socket_out(SOCK_STREAM, &global_wins_table[0][i].address, 42, LONG_CONNECT_TIMEOUT);
+ if (s==-1) {
+ DEBUG(0,("run_pull_replication: can't contact wins server %s.\n", inet_ntoa(global_wins_table[0][i].address)));
+ return;
+ }
+
+ if(buffer.offset > 0) {
+ if (!send_smb(s, buffer.buffer))
+ exit_server("run_pull_replication: send_smb failed.");
+ }
+
+ add_fd_to_sock_array(s);
+ FD_SET(s, listen_set);
+
+ /* add ourself as a client */
+ add_partner((int)t, 0, True, False);
+ }
+ }
+}
+
+/****************************************************************************
+ contact periodically our wins partner to do a push replication
+****************************************************************************/
+void run_push_replication(time_t t)
+{
+ /* we push every 30 minutes or 25 new entries */
+ int i, s;
+ struct BUFFER buffer;
+ GENERIC_PACKET p;
+
+ buffer.buffer=NULL;
+ buffer.offset=0;
+ buffer.length=0;
+
+ for (i=1; i<partner_count; i++) {
+ if (global_wins_table[0][i].last_pull < t) {
+ global_wins_table[0][i].last_pull=t+30*60; /* next in 30 minutes */
+
+ /* contact the wins server */
+ p.header.mess_type=MESSAGE_TYPE_START_ASSOC_REQUEST;
+ p.header.opcode=OPCODE_NON_NBT;
+ p.header.assoc_ctx=0;
+ p.sa_rq.assoc_ctx=(int)t;
+ p.sa_rq.min_ver=1;
+ p.sa_rq.maj_ver=1;
+
+ DEBUG(3,("run_push_replication: contacting wins server %s.\n", inet_ntoa(global_wins_table[0][i].address)));
+ encode_generic_packet(&buffer, &p);
+ dump_generic_packet(&p);
+
+ /* send the packet to the server and add the descriptor to receive answers */
+ s=open_socket_out(SOCK_STREAM, &global_wins_table[0][i].address, 42, LONG_CONNECT_TIMEOUT);
+ if (s==-1) {
+ DEBUG(0,("run_push_replication: can't contact wins server %s.\n", inet_ntoa(global_wins_table[0][i].address)));
+ return;
+ }
+
+ if(buffer.offset > 0) {
+ if (!send_smb(s, buffer.buffer))
+ exit_server("run_push_replication: send_smb failed.");
+ }
+
+ add_fd_to_sock_array(s);
+ FD_SET(s, listen_set);
+
+ /* add ourself as a client */
+ add_partner((int)t, 0, False, True);
+ }
+ }
+}
+
diff --git a/source/wrepld/server.c b/source/wrepld/server.c
new file mode 100644
index 00000000000..80694a616bb
--- /dev/null
+++ b/source/wrepld/server.c
@@ -0,0 +1,666 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main SMB server routines
+ Copyright (C) Jean François Micouleau 1998-2002.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "wins_repl.h"
+
+extern pstring user_socket_options;
+
+extern WINS_OWNER *global_wins_table;
+extern int partner_count;
+
+extern fd_set *listen_set;
+extern int listen_number;
+extern int *sock_array;
+
+extern TALLOC_CTX *mem_ctx;
+
+int wins_port = 42;
+
+/****************************************************************************
+ when exiting, take the whole family
+****************************************************************************/
+static void *dflt_sig(void)
+{
+ exit_server("caught signal");
+ return NULL;
+}
+
+/****************************************************************************
+ reload the services file
+ **************************************************************************/
+BOOL reload_services(BOOL test)
+{
+ BOOL ret;
+
+ if (lp_loaded()) {
+ pstring fname;
+ pstrcpy(fname,lp_configfile());
+ if (file_exist(fname,NULL) && !strcsequal(fname,dyn_CONFIGFILE)) {
+ pstrcpy(dyn_CONFIGFILE,fname);
+ test = False;
+ }
+ }
+
+ reopen_logs();
+
+ if (test && !lp_file_list_changed())
+ return(True);
+
+ ret = lp_load(dyn_CONFIGFILE,False,False,True);
+
+
+ /* perhaps the config filename is now set */
+ if (!test)
+ reload_services(True);
+
+ reopen_logs();
+
+ load_interfaces();
+
+ return(ret);
+}
+
+/****************************************************************************
+ Catch a sighup.
+****************************************************************************/
+
+VOLATILE sig_atomic_t reload_after_sighup = False;
+
+static void sig_hup(int sig)
+{
+ BlockSignals(True,SIGHUP);
+ DEBUG(0,("Got SIGHUP\n"));
+
+ sys_select_signal();
+ reload_after_sighup = True;
+ BlockSignals(False,SIGHUP);
+}
+
+#if DUMP_CORE
+/*******************************************************************
+prepare to dump a core file - carefully!
+********************************************************************/
+static BOOL dump_core(void)
+{
+ char *p;
+ pstring dname;
+ pstrcpy(dname,lp_logfile());
+ if ((p=strrchr_m(dname,'/'))) *p=0;
+ pstrcat(dname,"/corefiles");
+ mkdir(dname,0700);
+ sys_chown(dname,getuid(),getgid());
+ chmod(dname,0700);
+ if (chdir(dname)) return(False);
+ umask(~(0700));
+
+#ifdef HAVE_GETRLIMIT
+#ifdef RLIMIT_CORE
+ {
+ struct rlimit rlp;
+ getrlimit(RLIMIT_CORE, &rlp);
+ rlp.rlim_cur = MAX(4*1024*1024,rlp.rlim_cur);
+ setrlimit(RLIMIT_CORE, &rlp);
+ getrlimit(RLIMIT_CORE, &rlp);
+ DEBUG(3,("Core limits now %d %d\n",
+ (int)rlp.rlim_cur,(int)rlp.rlim_max));
+ }
+#endif
+#endif
+
+
+ DEBUG(0,("Dumping core in %s\n",dname));
+ abort();
+ return(True);
+}
+#endif
+
+/****************************************************************************
+exit the server
+****************************************************************************/
+void exit_server(const char *reason)
+{
+ static int firsttime=1;
+
+ if (!firsttime)
+ exit(0);
+ firsttime = 0;
+
+ DEBUG(2,("Closing connections\n"));
+
+ if (!reason) {
+ int oldlevel = DEBUGLEVEL;
+ DEBUGLEVEL = 10;
+ DEBUGLEVEL = oldlevel;
+ DEBUG(0,("===============================================================\n"));
+#if DUMP_CORE
+ if (dump_core()) return;
+#endif
+ }
+
+ DEBUG(3,("Server exit (%s)\n", (reason ? reason : "")));
+ exit(0);
+}
+
+/****************************************************************************
+ Create an fd_set containing all the sockets in the subnet structures,
+ plus the broadcast sockets.
+***************************************************************************/
+
+static BOOL create_listen_fdset(void)
+{
+ int i;
+ int num_interfaces = iface_count();
+ int s;
+
+ listen_set = (fd_set *)malloc(sizeof(fd_set));
+ if(listen_set == NULL) {
+ DEBUG(0,("create_listen_fdset: malloc fail !\n"));
+ return True;
+ }
+
+#ifdef HAVE_ATEXIT
+ {
+ static int atexit_set;
+ if(atexit_set == 0) {
+ atexit_set=1;
+ }
+ }
+#endif
+
+ FD_ZERO(listen_set);
+
+ if(lp_interfaces() && lp_bind_interfaces_only()) {
+ /* We have been given an interfaces line, and been
+ told to only bind to those interfaces. Create a
+ socket per interface and bind to only these.
+ */
+
+ if(num_interfaces > FD_SETSIZE) {
+ DEBUG(0,("create_listen_fdset: Too many interfaces specified to bind to. Number was %d max can be %d\n", num_interfaces, FD_SETSIZE));
+ return False;
+ }
+
+ /* Now open a listen socket for each of the interfaces. */
+ for(i = 0; i < num_interfaces; i++) {
+ struct in_addr *ifip = iface_n_ip(i);
+
+ if(ifip == NULL) {
+ DEBUG(0,("create_listen_fdset: interface %d has NULL IP address !\n", i));
+ continue;
+ }
+ s = open_socket_in(SOCK_STREAM, wins_port, 0, ifip->s_addr, True);
+ if(s == -1)
+ return False;
+
+ /* ready to listen */
+ set_socket_options(s,"SO_KEEPALIVE");
+ set_socket_options(s,user_socket_options);
+
+ if (listen(s, 5) == -1) {
+ DEBUG(5,("listen: %s\n",strerror(errno)));
+ close(s);
+ return False;
+ }
+ add_fd_to_sock_array(s);
+ FD_SET(s, listen_set);
+ }
+ } else {
+ /* Just bind to 0.0.0.0 - accept connections from anywhere. */
+ num_interfaces = 1;
+
+ /* open an incoming socket */
+ s = open_socket_in(SOCK_STREAM, wins_port, 0, interpret_addr(lp_socket_address()),True);
+ if (s == -1)
+ return(False);
+
+ /* ready to listen */
+ set_socket_options(s,"SO_KEEPALIVE");
+ set_socket_options(s,user_socket_options);
+
+ if (listen(s, 5) == -1) {
+ DEBUG(0,("create_listen_fdset: listen: %s\n", strerror(errno)));
+ close(s);
+ return False;
+ }
+
+ add_fd_to_sock_array(s);
+ FD_SET(s, listen_set);
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ read a packet from a socket and parse it, returning a packet ready
+ to be used or put on the queue. This assumes a UDP socket
+ ******************************************************************/
+static struct wins_packet_struct *read_wins_packet(int fd, int timeout)
+{
+ struct wins_packet_struct *p;
+ GENERIC_PACKET *q;
+ struct BUFFER inbuf;
+ ssize_t len=0;
+ size_t total=0;
+ ssize_t ret;
+ BOOL ok = False;
+
+ inbuf.buffer=NULL;
+ inbuf.length=0;
+ inbuf.offset=0;
+
+ if(!grow_buffer(&inbuf, 4))
+ return NULL;
+
+ ok = (read(fd, inbuf.buffer,4) == 4);
+ if (!ok)
+ return NULL;
+ len = smb_len(inbuf.buffer);
+
+ if (len<=0)
+ return NULL;
+
+ if(!grow_buffer(&inbuf, len))
+ return NULL;
+
+ while (total < len) {
+ ret = read(fd, inbuf.buffer + total + 4, len - total);
+ if (ret == 0) {
+ DEBUG(10,("read_socket_data: recv of %d returned 0. Error = %s\n", (int)(len - total), strerror(errno) ));
+ return NULL;
+ }
+ if (ret == -1) {
+ DEBUG(0,("read_socket_data: recv failure for %d. Error = %s\n", (int)(len - total), strerror(errno) ));
+ return NULL;
+ }
+ total += ret;
+ }
+
+ q = (GENERIC_PACKET *)talloc(mem_ctx, sizeof(GENERIC_PACKET));
+ p = (struct wins_packet_struct *)talloc(mem_ctx, sizeof(*p));
+ if (q==NULL || p==NULL)
+ return NULL;
+
+ decode_generic_packet(&inbuf, q);
+
+ q->fd=fd;
+
+ p->next = NULL;
+ p->prev = NULL;
+ p->stop_packet = False;
+ p->timestamp = time(NULL);
+ p->fd = fd;
+ p->packet=q;
+
+ return p;
+}
+
+static struct wins_packet_struct *packet_queue = NULL;
+
+/*******************************************************************
+ Queue a packet into a packet queue
+******************************************************************/
+static void queue_packet(struct wins_packet_struct *packet)
+{
+ struct wins_packet_struct *p;
+
+ if (!packet_queue) {
+ packet->prev = NULL;
+ packet->next = NULL;
+ packet_queue = packet;
+ return;
+ }
+
+ /* find the bottom */
+ for (p=packet_queue;p->next;p=p->next)
+ ;
+
+ p->next = packet;
+ packet->next = NULL;
+ packet->prev = p;
+}
+
+/****************************************************************************
+ Listens for NMB or DGRAM packets, and queues them.
+ return True if the socket is dead
+***************************************************************************/
+static BOOL listen_for_wins_packets(void)
+{
+ int num_interfaces = iface_count();
+ fd_set fds;
+ int i, num, s, new_s;
+ struct timeval timeout;
+
+ if(listen_set == NULL) {
+ if(!create_listen_fdset()) {
+ DEBUG(0,("listen_for_packets: Fatal error. unable to create listen set. Exiting.\n"));
+ return True;
+ }
+ }
+
+ memcpy((char *)&fds, (char *)listen_set, sizeof(fd_set));
+
+ timeout.tv_sec = NMBD_SELECT_LOOP;
+ timeout.tv_usec = 0;
+
+ /* Prepare for the select - allow certain signals. */
+
+ BlockSignals(False, SIGTERM);
+
+ num = sys_select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
+
+ /* We can only take signals when we are in the select - block them again here. */
+
+ BlockSignals(True, SIGTERM);
+
+ if(num == -1)
+ return False;
+
+ for (; num > 0; num--) {
+ s = -1;
+ /* check the sockets we are only listening on, waiting to accept */
+ for (i=0; i<num_interfaces; i++) {
+ struct sockaddr addr;
+ socklen_t in_addrlen = sizeof(addr);
+
+ if(FD_ISSET(sock_array[i], &fds)) {
+ s = sock_array[i];
+ /* Clear this so we don't look at it again. */
+ FD_CLR(sock_array[i], &fds);
+
+ /* accept and add the new socket to the listen set */
+ new_s=accept(s, &addr, &in_addrlen);
+
+ if (new_s < 0)
+ continue;
+
+ DEBUG(5,("listen_for_wins_packets: new connection, old: %d, new : %d\n", s, new_s));
+
+ set_socket_options(new_s, "SO_KEEPALIVE");
+ set_socket_options(new_s, user_socket_options);
+ FD_SET(new_s, listen_set);
+ add_fd_to_sock_array(new_s);
+ }
+ }
+
+ /*
+ * check for the sockets we are waiting data from
+ * either client sending datas
+ * or reply to our requests
+ */
+ for (i=num_interfaces; i<listen_number; i++) {
+ if(FD_ISSET(sock_array[i], &fds)) {
+ struct wins_packet_struct *packet = read_wins_packet(sock_array[i], timeout.tv_sec);
+ if (packet) {
+ packet->fd = sock_array[i];
+ queue_packet(packet);
+ }
+ DEBUG(2,("listen_for_wins_packets: some data on fd %d\n", sock_array[i]));
+ FD_CLR(sock_array[i], &fds);
+ break;
+ }
+
+ }
+
+ }
+
+ return False;
+}
+
+
+/*******************************************************************
+ Run elements off the packet queue till its empty
+******************************************************************/
+
+static void run_wins_packet_queue(void)
+{
+ struct wins_packet_struct *p;
+
+ while ((p = packet_queue)) {
+ packet_queue = p->next;
+ if (packet_queue)
+ packet_queue->prev = NULL;
+ p->next = p->prev = NULL;
+
+ construct_reply(p);
+
+ /* if it was a stop assoc, close the connection */
+ if (p->stop_packet) {
+ FD_CLR(p->fd, listen_set);
+ remove_fd_from_sock_array(p->fd);
+ close(p->fd);
+ }
+ }
+}
+
+/**************************************************************************** **
+ The main select loop.
+ **************************************************************************** */
+static void process(void)
+{
+
+ while( True ) {
+ time_t t = time(NULL);
+
+ /* check for internal messages */
+ message_dispatch();
+
+ if(listen_for_wins_packets())
+ return;
+
+ run_wins_packet_queue();
+
+ run_pull_replication(t);
+
+ run_push_replication(t);
+
+ /*
+ * Reload the services file if we got a sighup.
+ */
+
+ if(reload_after_sighup) {
+ reload_services( True );
+ reopen_logs();
+ reload_after_sighup = False;
+ }
+
+ /* free temp memory */
+ talloc_destroy_pool(mem_ctx);
+
+ /* free up temp memory */
+ lp_talloc_free();
+ }
+} /* process */
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+ /* shall I run as a daemon */
+ static BOOL is_daemon = False;
+ static BOOL interactive = False;
+ static BOOL Fork = True;
+ static BOOL log_stdout = False;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "daemon", 'D', POPT_ARG_VAL, &is_daemon, True, "Become a daemon (default)" },
+ { "foreground", 'F', POPT_ARG_VAL, &Fork, False, "Run daemon in foreground (for daemontools, etc)" },
+ { "stdout", 'S', POPT_ARG_VAL, &log_stdout, True, "Log to stdout" },
+ { "interactive", 'i', POPT_ARG_NONE, NULL, 'i', "Run interactive (not a daemon)" },
+ { "port", 'p', POPT_ARG_INT, &wins_port, 'p', "Listen on the specified port" },
+ POPT_COMMON_SAMBA
+ POPT_TABLEEND
+ };
+ int opt;
+ poptContext pc;
+
+#ifdef HAVE_SET_AUTH_PARAMETERS
+ set_auth_parameters(argc,argv);
+#endif
+
+ pc = poptGetContext("wrepld", argc, (const char **)argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'i':
+ interactive = True;
+ Fork = False;
+ log_stdout = True;
+ break;
+ }
+ }
+
+
+ if (log_stdout && Fork) {
+ d_printf("Can't log to stdout (-S) unless daemon is in foreground (-F) or interactive (-i)\n");
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+
+#ifdef HAVE_SETLUID
+ /* needed for SecureWare on SCO */
+ setluid(0);
+#endif
+
+ sec_init();
+
+ load_case_tables();
+
+ set_remote_machine_name("wrepld", False);
+
+ setup_logging(argv[0],log_stdout);
+
+ /* we want to re-seed early to prevent time delays causing
+ client problems at a later date. (tridge) */
+ generate_random_buffer(NULL, 0, False);
+
+ /* make absolutely sure we run as root - to handle cases where people
+ are crazy enough to have it setuid */
+
+ gain_root_privilege();
+ gain_root_group_privilege();
+
+ fault_setup((void (*)(void *))exit_server);
+ CatchSignal(SIGTERM , SIGNAL_CAST dflt_sig);
+
+ /* we are never interested in SIGPIPE */
+ BlockSignals(True,SIGPIPE);
+
+#if defined(SIGFPE)
+ /* we are never interested in SIGFPE */
+ BlockSignals(True,SIGFPE);
+#endif
+
+#if defined(SIGUSR2)
+ /* We are no longer interested in USR2 */
+ BlockSignals(True,SIGUSR2);
+#endif
+
+ /* POSIX demands that signals are inherited. If the invoking process has
+ * these signals masked, we will have problems, as we won't recieve them. */
+ BlockSignals(False, SIGHUP);
+ BlockSignals(False, SIGUSR1);
+
+ /* we want total control over the permissions on created files,
+ so set our umask to 0 */
+ umask(0);
+
+ reopen_logs();
+
+ DEBUG(1,( "wrepld version %s started.\n", SAMBA_VERSION_STRING));
+ DEBUGADD(1,( "Copyright Andrew Tridgell and the Samba Team 1992-2004\n"));
+
+ DEBUG(2,("uid=%d gid=%d euid=%d egid=%d\n",
+ (int)getuid(),(int)getgid(),(int)geteuid(),(int)getegid()));
+
+ if (sizeof(uint16) < 2 || sizeof(uint32) < 4) {
+ DEBUG(0,("ERROR: Samba is not configured correctly for the word size on your machine\n"));
+ exit(1);
+ }
+
+ /*
+ * Do this before reload_services.
+ */
+
+ if (!reload_services(False))
+ return(-1);
+
+ if (!init_names())
+ return -1;
+
+#ifdef WITH_PROFILE
+ if (!profile_setup(False)) {
+ DEBUG(0,("ERROR: failed to setup profiling\n"));
+ return -1;
+ }
+#endif
+
+ CatchSignal(SIGHUP,SIGNAL_CAST sig_hup);
+
+ DEBUG(3,( "loaded services\n"));
+
+ if (!is_daemon && !is_a_socket(0)) {
+ DEBUG(0,("standard input is not a socket, assuming -D option\n"));
+ is_daemon = True;
+ }
+
+ if (is_daemon && !interactive) {
+ DEBUG( 3, ( "Becoming a daemon.\n" ) );
+ become_daemon(Fork);
+ }
+
+#if HAVE_SETPGID
+ /*
+ * If we're interactive we want to set our own process group for
+ * signal management.
+ */
+ if (interactive)
+ setpgid( (pid_t)0, (pid_t)0);
+#endif
+
+ if (!directory_exist(lp_lockdir(), NULL)) {
+ mkdir(lp_lockdir(), 0755);
+ }
+
+ if (is_daemon) {
+ pidfile_create("wrepld");
+ }
+
+ if (!message_init()) {
+ exit(1);
+ }
+
+ /* Initialise the memory context */
+ mem_ctx=talloc_init("wins repl talloc ctx");
+
+ /* initialise the global partners table */
+ partner_count=init_wins_partner_table();
+
+ /* We can only take signals in the select. */
+ BlockSignals( True, SIGTERM );
+
+ process();
+
+ poptFreeContext(pc);
+ exit_server("normal exit");
+ return(0);
+}
diff --git a/source/wrepld/socket.c b/source/wrepld/socket.c
new file mode 100644
index 00000000000..3d759f0ab89
--- /dev/null
+++ b/source/wrepld/socket.c
@@ -0,0 +1,69 @@
+/*
+ Unix SMB/CIFS implementation.
+ process incoming packets - main loop
+ Copyright (C) Jean François Micouleau 1998-2002.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "wins_repl.h"
+
+fd_set *listen_set = NULL;
+int listen_number = 0;
+int *sock_array = NULL;
+
+/*******************************************************************
+ Add an fd from the sock_array
+******************************************************************/
+void add_fd_to_sock_array(int fd)
+{
+ int *temp_sock=NULL;
+
+ temp_sock=(int *)Realloc(sock_array, (listen_number+1)*sizeof(int));
+ if (temp_sock==NULL)
+ return;
+
+ sock_array=temp_sock;
+ sock_array[listen_number]=fd;
+ listen_number++;
+}
+
+
+/*******************************************************************
+ Remove an fd from the sock_array
+******************************************************************/
+void remove_fd_from_sock_array(int fd)
+{
+ int i,j;
+
+ for (i=0; sock_array[i]!=fd && i<listen_number; i++)
+ ;
+
+ if (i==listen_number) {
+ DEBUG(0,("remove_fd_from_sock_array: unknown fd: %d\n", fd));
+ return;
+ }
+
+ if (i==listen_number-1) {
+ sock_array=(int *)Realloc(sock_array, --listen_number*sizeof(int));
+ return;
+ }
+
+ for (j=i; j<listen_number-1; j++)
+ sock_array[j]=sock_array[j+1];
+
+ sock_array=(int *)Realloc(sock_array, --listen_number*sizeof(int));
+}
diff --git a/source/wrepld/wins_repl.h b/source/wrepld/wins_repl.h
new file mode 100644
index 00000000000..25b44422121
--- /dev/null
+++ b/source/wrepld/wins_repl.h
@@ -0,0 +1,161 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Jean François Micouleau 1998-2002.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define OPCODE_NON_NBT 0x00007800
+
+/* the messages */
+#define MESSAGE_TYPE_START_ASSOC_REQUEST 0
+#define MESSAGE_TYPE_START_ASSOC_REPLY 1
+#define MESSAGE_TYPE_STOP_ASSOC 2
+#define MESSAGE_TYPE_REPLICATE 3
+
+/* the replication sub-message */
+#define MESSAGE_REP_ADD_VERSION_REQUEST 0
+#define MESSAGE_REP_ADD_VERSION_REPLY 1
+#define MESSAGE_REP_SEND_ENTRIES_REQUEST 2
+#define MESSAGE_REP_SEND_ENTRIES_REPLY 3
+#define MESSAGE_REP_UPDATE_NOTIFY_REQUEST 4
+
+/* stop reasons */
+#define STOP_REASON_USER_REASON 0
+#define STOP_REASON_AUTH_FAILED 1
+#define STOP_REASON_INCOMPLETE_VERSION 2
+#define STOP_REASON_BUG_CHECK 3
+#define STOP_REASON_MESSAGE_ERROR 4
+
+
+typedef struct _WINS_OWNER {
+ struct in_addr address;
+ SMB_BIG_UINT max_version;
+ SMB_BIG_UINT min_version;
+ int type;
+ time_t last_pull;
+ time_t last_push;
+} WINS_OWNER;
+
+typedef struct _WINS_NAME {
+ int name_len; /* always 0x11 */
+ char name[16];
+ char type;
+ int empty;
+ int name_flag;
+ int group_flag;
+ SMB_BIG_UINT id;
+ int num_ip;
+ struct in_addr owner;
+ struct in_addr *others;
+ int foo; /* 0xffffff */
+} WINS_NAME;
+
+typedef struct _WINS_PARTNERS
+{
+ int client_assoc;
+ int server_assoc;
+ BOOL pull_partner;
+ BOOL push_partner;
+ struct in_addr partner_server;
+ struct in_addr other_server;
+} WINS_PARTNER;
+
+typedef struct _generic_header{
+ int data_size;
+ int opcode;
+ int assoc_ctx;
+ int mess_type;
+} generic_header;
+
+typedef struct _START_ASSOC_REQUEST {
+ int assoc_ctx;
+ int min_ver;
+ int maj_ver;
+} START_ASSOC_REQUEST;
+
+typedef struct _START_ASSOC_REPLY {
+ int assoc_ctx;
+ int min_ver;
+ int maj_ver;
+} START_ASSOC_REPLY;
+
+typedef struct _STOP_ASSOC {
+ int reason;
+} STOP_ASSOC;
+
+typedef struct _AVMT_REP {
+ int partner_count;
+ WINS_OWNER *wins_owner;
+ struct in_addr initiating_wins_server;
+} AVMT_REP;
+
+typedef struct _SEND_ENTRIES_REQUEST {
+ WINS_OWNER wins_owner;
+} SEND_ENTRIES_REQUEST;
+
+typedef struct _SEND_ENTRIES_REPLY {
+ int max_names;
+ WINS_NAME *wins_name;
+} SEND_ENTRIES_REPLY;
+
+typedef struct _UPDATE_NOTIFY_REQUEST {
+ int partner_count;
+ WINS_OWNER *wins_owner;
+ struct in_addr initiating_wins_server;
+} UPDATE_NOTIFY_REQUEST;
+
+typedef struct _REPLICATE {
+ int msg_type;
+
+ AVMT_REP avmt_rep;
+ SEND_ENTRIES_REQUEST se_rq;
+ SEND_ENTRIES_REPLY se_rp;
+ UPDATE_NOTIFY_REQUEST un_rq;
+} REPLICATE;
+
+
+typedef struct _GENERIC_PACKET {
+ int fd;
+
+ generic_header header;
+
+ START_ASSOC_REQUEST sa_rq;
+ START_ASSOC_REPLY sa_rp;
+ STOP_ASSOC so;
+ REPLICATE rep;
+} GENERIC_PACKET;
+
+struct wins_packet_struct
+{
+ struct wins_packet_struct *next;
+ struct wins_packet_struct *prev;
+ BOOL stop_packet;
+ int fd;
+ time_t timestamp;
+ GENERIC_PACKET *packet;
+};
+
+struct BUFFER {
+ char *buffer;
+ int offset;
+ int length;
+};
+
+
+
+#include "wrepld_proto.h"
+
diff --git a/swat/help/welcome.html b/swat/help/welcome.html
new file mode 100644
index 00000000000..59429ba47df
--- /dev/null
+++ b/swat/help/welcome.html
@@ -0,0 +1,67 @@
+<h3>Welcome to SWAT!</h3>
+
+Please choose a configuration action using one of the above buttons
+
+<h3><a href="/swat/help/samba.7.html" target="docs">Samba</a> Documentation</h3>
+
+<ul>
+ <li><b>Daemons</b>
+ <ul>
+ <li><a href="/swat/help/smbd.8.html" target="docs">smbd</a> - the SMB daemon
+ <li><a href="/swat/help/nmbd.8.html" target="docs">nmbd</a> - the NetBIOS nameserver
+ <li><a href="/swat/help/winbindd.8.html" target="docs">winbindd</a> - the winbind daemon
+ </ul>
+ <li><b>Configuration Files</b>
+ <ul>
+ <li><a href="/swat/help/smb.conf.5.html" target="docs">smb.conf</a> - the main Samba configuration file
+ <li><a href="/swat/help/lmhosts.5.html" target="docs">lmhosts</a> - NetBIOS hosts file
+ <li><a href="/swat/help/smbpasswd.5.html" target="docs">smbpasswd</a> - SMB password file
+ </ul>
+ <li><b>Administrative Utilities</b>
+ <ul>
+ <li><a href="/swat/help/smbcontrol.1.html" target="docs">smbcontrol</a> - send control messages to Samba daemons
+ <li><a href="/swat/help/smbpasswd.8.html" target="docs">smbpasswd</a> - managing SMB passwords
+ <li><a href="/swat/help/swat.8.html" target="docs">SWAT</a> - web configuration tool
+ <li><a href="/swat/help/net.8.html" target="docs">net</a> - tool for administration of Samba and remote CIFS servers
+ <li><a href="/swat/help/pdbedit.8.html" target="docs">pdbedit</a> - Samba user account management tool
+ <li><a href="/swat/help/tdbbackup.8.html" target="docs">tdbbackup</a> - Tool for backing up TDB databases
+ </ul>
+ <li><b>Client Tools</b>
+ <ul>
+ <li><a href="/swat/help/rpcclient.1.html" target="docs">rpcclient</a> - command line MS-RPC client
+ <li><a href="/swat/help/smbtar.1.html" target="docs">smbtar</a> - SMB backup tool
+ <li><a href="/swat/help/smbclient.1.html" target="docs">smbclient</a> - command line SMB client
+ <li><a href="/swat/help/smbmnt.8.html" target="docs">smbmnt</a> - helper utility for mounting SMB filesystems on Linux hosts
+ <li><a href="/swat/help/smbmount.8.html" target="docs">smbmount</a> - user space tool for mounting SMB filesystems under Linux
+ <li><a href="/swat/help/smbumount.8.html" target="docs">smbumount</a> - user space tool for umounting SMB filesystems under Linux
+ <li><a href="/swat/help/ntlm_auth.1.html" target="docs">ntlm_auth</a> - allow external programs to use NTLM authentication
+ <li><a href="/swat/help/smbcquotas.1.html" target="docs">smbcquotas</a> - get or set quotas on NTFS 5 shares
+ <li><a href="/swat/help/smbsh.1.html" target="docs">smbsh</a> - Allow access to remote SMB shares using a UNIX shell
+ <li><a href="/swat/help/smbspool.8.html" target="docs">smbspool</a> - Send a print job to an SMB printer
+ <li><a href="/swat/help/smbtree.1.html" target="docs">smbtree</a> - Text-based SMB network browsing
+ </ul>
+ <li><b>Diagnostic Utilities</b>
+ <ul>
+ <li><a href="/swat/help/smbstatus.1.html" target="docs">smbstatus</a> - monitoring Samba
+ <li><a href="/swat/help/testparm.1.html" target="docs">testparm</a> - validating your config file
+ <li><a href="/swat/help/testprns.1.html" target="docs">testprns</a> - testing printer configuration
+ <li><a href="/swat/help/nmblookup.1.html" target="docs">nmblookup</a> - NetBIOS name query tool
+ <li><a href="/swat/help/wbinfo.1.html" target="docs">wbinfo</a> - Tool for getting winbind information
+ </ul>
+ <li><b>Misc. Utilities</b>
+ <ul>
+ <li><a href="/swat/help/profiles.1.html" target="docs">profiles</a> - migrating profiles from one domain to another
+ <li><a href="/swat/help/editreg.1.html" target="docs">editreg</a> - editing windows registry files
+ <li><a href="/swat/help/log2pcap.1.html" target="docs">log2pcap</a> - generate pcap files from samba log files
+ </ul>
+ <li><b>Books</b>
+ <ul>
+ <li><a href="/swat/using_samba/toc.html" target="docs">Using Samba, 2ed.</a> - by Jay Ts, Robert Eckstein, and David Collier-Brown
+ <li><a href="/swat/help/Samba-HOWTO-Collection.html">The Samba HOWTO Collection</a>
+ </ul>
+</ul>
+
+ <h3>Feedback</h3>
+
+ Please join the <A HREF="http://lists.samba.org/">samba</A> mailing
+ list if you want to discuss issues with this release of SWAT.
diff --git a/swat/images/globals.gif b/swat/images/globals.gif
new file mode 100644
index 00000000000..7a9a8510d77
--- /dev/null
+++ b/swat/images/globals.gif
Binary files differ
diff --git a/swat/images/home.gif b/swat/images/home.gif
new file mode 100644
index 00000000000..84b1dd032f8
--- /dev/null
+++ b/swat/images/home.gif
Binary files differ
diff --git a/swat/images/passwd.gif b/swat/images/passwd.gif
new file mode 100644
index 00000000000..17393a14020
--- /dev/null
+++ b/swat/images/passwd.gif
Binary files differ
diff --git a/swat/images/printers.gif b/swat/images/printers.gif
new file mode 100644
index 00000000000..db9ec3f0e0a
--- /dev/null
+++ b/swat/images/printers.gif
Binary files differ
diff --git a/swat/images/samba.gif b/swat/images/samba.gif
new file mode 100644
index 00000000000..3ec3d2195ff
--- /dev/null
+++ b/swat/images/samba.gif
Binary files differ
diff --git a/swat/images/shares.gif b/swat/images/shares.gif
new file mode 100644
index 00000000000..b3af15e35d6
--- /dev/null
+++ b/swat/images/shares.gif
Binary files differ
diff --git a/swat/images/status.gif b/swat/images/status.gif
new file mode 100644
index 00000000000..81ef0bcf16f
--- /dev/null
+++ b/swat/images/status.gif
Binary files differ
diff --git a/swat/images/viewconfig.gif b/swat/images/viewconfig.gif
new file mode 100644
index 00000000000..bdeac1cf967
--- /dev/null
+++ b/swat/images/viewconfig.gif
Binary files differ
diff --git a/swat/images/wizard.gif b/swat/images/wizard.gif
new file mode 100644
index 00000000000..a4614e936bb
--- /dev/null
+++ b/swat/images/wizard.gif
Binary files differ
diff --git a/swat/include/footer.html b/swat/include/footer.html
new file mode 100644
index 00000000000..7c3b483684c
--- /dev/null
+++ b/swat/include/footer.html
@@ -0,0 +1,3 @@
+</TD></TR></TABLE></CENTER>
+</BODY>
+</HTML>
diff --git a/swat/include/header.html b/swat/include/header.html
new file mode 100644
index 00000000000..f964133316a
--- /dev/null
+++ b/swat/include/header.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<HTML>
+<HEAD>
+<TITLE>Samba Web Administration Tool</TITLE>
+</HEAD>
+<BODY bgcolor="white">
+<CENTER>
+<IMG SRC="/swat/images/samba.gif" ALT="[ Samba ]" border=0><BR>
+<TABLE WIDTH="98%" CELLSPACING=1 CELLPADDING=4 BORDER=1>
+<TR><TD BGCOLOR="#ddddd0">
diff --git a/swat/lang/ja/help/welcome.html b/swat/lang/ja/help/welcome.html
new file mode 100644
index 00000000000..240ad22ceff
--- /dev/null
+++ b/swat/lang/ja/help/welcome.html
@@ -0,0 +1,69 @@
+<h3>SWAT ‚ւ悤‚±‚»!</h3>
+
+ã‚̃{ƒ^ƒ“‚ðƒNƒŠƒbƒN‚µ‚ÄÝ’è‚ðs‚Á‚Ä‚­‚¾‚³‚¢B
+
+<h3><a href="/swat/help/samba.7.html" target="docs">Samba</a> ƒhƒLƒ…ƒƒ“ƒg</h3>
+
+<ul>
+ <li><b>ƒf[ƒ‚ƒ“</b>
+ <ul>
+ <li><a href="/swat/help/smbd.8.html" target="docs">smbd</a> - SMB ƒf[ƒ‚ƒ“
+ <li><a href="/swat/help/nmbd.8.html" target="docs">nmbd</a> - NetBIOS ƒl[ƒ€ƒT[ƒo
+ <li><a href="/swat/help/winbindd.8.html" target="docs">winbindd</a> - winbind ƒf[ƒ‚ƒ“
+ </ul>
+ <li><b>Ý’èƒtƒ@ƒCƒ‹</b>
+ <ul>
+ <li><a href="/swat/help/smb.conf.5.html" target="docs">smb.conf</a> - Samba Ý’èƒtƒ@ƒCƒ‹
+ <li><a href="/swat/help/lmhosts.5.html" target="docs">lmhosts</a> - NetBIOS ‚Ì hosts ƒtƒ@ƒCƒ‹
+ <li><a href="/swat/help/smbpasswd.5.html" target="docs">smbpasswd</a> - SMB ƒpƒXƒ[ƒhƒtƒ@ƒCƒ‹
+ </ul>
+ <li><b>ŠÇ—ƒ†[ƒeƒBƒŠƒeƒB</b>
+ <ul>
+ <li><a href="/swat/help/smbcontrol.1.html" target="docs">smbcontrol</a> - Samba ƒf[ƒ‚ƒ“‚ւ̃Rƒ“ƒgƒ[ƒ‹ƒƒbƒZ[ƒW‚Ì‘—o
+ <li><a href="/swat/help/smbpasswd.8.html" target="docs">smbpasswd</a> - SMB ƒpƒXƒ[ƒh‚ÌŠÇ—
+ <li><a href="/swat/help/swat.8.html" target="docs">SWAT</a> - Web Ý’èƒc[ƒ‹
+ <li><a href="/swat/help/net.8.html" target="docs">net</a> - Samba ‚¨‚æ‚уŠƒ‚[ƒg‚Ì CIFS ƒT[ƒo‚ÌŠÇ—ƒc[ƒ‹
+ <li><a href="/swat/help/pdbedit.8.html" target="docs">pdbedit</a> - Samba ‚̃†[ƒUƒAƒJƒEƒ“ƒgŠÇ—ƒc[ƒ‹
+ <li><a href="/swat/help/tdbbackup.8.html" target="docs">tdbbackup</a> - TDB ƒf[ƒ^ƒx[ƒX‚̃oƒbƒNƒAƒbƒvƒc[ƒ‹
+ </ul>
+ <li><b>ƒNƒ‰ƒCƒAƒ“ƒgƒc[ƒ‹</b>
+ <ul>
+ <li><a href="/swat/help/rpcclient.1.html" target="docs">rpcclient</a> - ƒRƒ}ƒ“ƒhƒ‰ƒCƒ“‚Ì MS-RPC ƒNƒ‰ƒCƒAƒ“ƒg
+ <li><a href="/swat/help/smbtar.1.html" target="docs">smbtar</a> - SMB ƒoƒbƒNƒAƒbƒvƒc[ƒ‹
+ <li><a href="/swat/help/smbclient.1.html" target="docs">smbclient</a> - ƒRƒ}ƒ“ƒhƒ‰ƒCƒ“‚Ì SMB ƒNƒ‰ƒCƒAƒ“ƒg
+ <li><a href="/swat/help/smbmnt.8.html" target="docs">smbmnt</a> - Linux ã‚Ì SMB ƒtƒ@ƒCƒ‹ƒVƒXƒeƒ€‚ðƒ}ƒEƒ“ƒg‚·‚éۂ̕╃c[ƒ‹
+ <li><a href="/swat/help/smbmount.8.html" target="docs">smbmount</a> - Linux ‚É‚¨‚¯‚é SMB ƒtƒ@ƒCƒ‹ƒVƒXƒeƒ€‚̃}ƒEƒ“ƒg‚ðŽÀŒ»‚·‚郆[ƒU‹óŠÔ‚̃c[ƒ‹
+ <li><a href="/swat/help/smbspool.8.html" target="docs">smbspool</a> - ƒRƒ}ƒ“ƒhƒ‰ƒCƒ“‚Ì SMB ˆóüƒNƒ‰ƒCƒAƒ“ƒg
+ <li><a href="/swat/help/smbumount.8.html" target="docs">smbumount</a> - Linux ‚É‚¨‚¯‚é SMB ƒtƒ@ƒCƒ‹ƒVƒXƒeƒ€‚̃Aƒ“ƒ}ƒEƒ“ƒg‚ðŽÀŒ»‚·‚郆[ƒU‹óŠÔ‚̃c[ƒ‹
+ <li><a href="/swat/help/ntlm_auth.1.html" target="docs">ntlm_auth</a> - •ÊƒvƒƒOƒ‰ƒ€‚©‚ç‚Ì NTLM ”FØ‚ÌŽg—p‚ðŽÀŒ»
+ <li><a href="/swat/help/smbcquotas.1.html" target="docs">smbcquotas</a> - NTFS 5 ‹¤—L‚̃NƒH[ƒ^î•ñ‚ÌÝ’è‚Ǝ擾
+ <li><a href="/swat/help/smbsh.1.html" target="docs">smbsh</a> - UNIX ƒVƒFƒ‹‚ð—p‚¢‚½ƒŠƒ‚[ƒg SMB ‹¤—L‚ւ̃AƒNƒZƒX‚ðŽÀŒ»
+ <li><a href="/swat/help/smbtree.1.html" target="docs">smbtree</a> - ƒeƒLƒXƒgƒx[ƒX‚Ì SMB ƒlƒbƒgƒ[ƒNƒuƒ‰ƒEƒWƒ“ƒO
+ <li><a href="/swat/help/smbspool.8.html" target="docs">smbspool</a> - ˆóüƒWƒ‡ƒu‚ð SMB ƒvƒŠƒ“ƒ^‚É‘—M
+ </ul>
+ <li><b>f’fƒ†[ƒeƒBƒŠƒeƒB</b>
+ <ul>
+ <li><a href="/swat/help/smbstatus.1.html" target="docs">smbstatus</a> - Samba ‚ÌŠÄŽ‹
+ <li><a href="/swat/help/testparm.1.html" target="docs">testparm</a> - Ý’èƒtƒ@ƒCƒ‹‚Ì®‡«‚ÌŒŸ¸
+ <li><a href="/swat/help/testprns.1.html" target="docs">testprns</a> - ƒvƒŠƒ“ƒ^Ý’è‚ÌŒŸ¸
+ <li><a href="/swat/help/nmblookup.1.html" target="docs">nmblookup</a> - NetBIOS –¼‚ÌŒŸõƒc[ƒ‹
+ <li><a href="/swat/help/wbinfo.1.html" target="docs">wbinfo</a> - winbind î•ñ‚̎擾ƒc[ƒ‹
+ </ul>
+ <li><b>‚»‚Ì‘¼‚̃†[ƒeƒBƒŠƒeƒB</b>
+ <ul>
+ <li><a href="/swat/help/profiles.1.html" target="docs">profiles</a> - •ÊƒhƒƒCƒ“‚ւ̃vƒƒtƒ@ƒCƒ‹‚̈Ús
+ <li><a href="/swat/help/editreg.1.html" target="docs">editreg</a> - Windows ƒŒƒWƒXƒgƒŠƒtƒ@ƒCƒ‹‚Ì•ÒW
+ <li><a href="/swat/help/log2pcap.1.html" target="docs">log2pcap</a> - Samba ‚̃ƒOƒtƒ@ƒCƒ‹‚©‚ç pcap ƒtƒ@ƒCƒ‹‚̶¬
+ </ul>
+ <li><b>‘Ð</b>
+ <ul>
+ <li><a href="/swat/using_samba/toc.html" target="docs">Using Samba ‘æ“ñ”Å</a> - ’˜ŽÒ: Jay Ts, Robert Eckstein, David Collier-Brown
+ <li><a href="/swat/help/Samba-HOWTO-Collection.html">Samba HOWTO ƒRƒŒƒNƒVƒ‡ƒ“</a>
+ </ul>
+</ul>
+
+ <h3>ƒtƒB[ƒhƒoƒbƒN</h3>
+
+ ‚±‚̃o[ƒWƒ‡ƒ“‚Ì SWAT ‚ÉŠÖ‚·‚é–â‘è‚ɂ‚¢‚Ä‚Ì‹c˜_‚ðs‚È‚¢‚½‚¢•û‚ÍA
+ <A HREF="http://lists.samba.org/">samba</A> ƒ[ƒŠƒ“ƒOƒŠƒXƒg‚Ö‚ÌŽQ‰Á‚ð‚¨Šè‚¢‚µ‚Ü‚·B
+<small>(‚à‚µ‚­‚Í <A HREF="http://www.samba.gr.jp/ml/">sugj-tech</A> ƒ[ƒŠƒ“ƒOƒŠƒXƒg‚É“ú–{Œê‚Å‚¨Šè‚¢‚µ‚Ü‚·)</small>
diff --git a/swat/lang/ja/include/footer.html b/swat/lang/ja/include/footer.html
new file mode 100644
index 00000000000..7c3b483684c
--- /dev/null
+++ b/swat/lang/ja/include/footer.html
@@ -0,0 +1,3 @@
+</TD></TR></TABLE></CENTER>
+</BODY>
+</HTML>
diff --git a/swat/lang/ja/include/header.html b/swat/lang/ja/include/header.html
new file mode 100644
index 00000000000..2fbe9bb7546
--- /dev/null
+++ b/swat/lang/ja/include/header.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<HTML>
+<HEAD>
+<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
+<TITLE>Samba Web Administration Tool</TITLE>
+<link rel="STYLESHEET" type="text/css" href="/swat/include/header_css.html">
+</HEAD>
+<BODY bgcolor="white">
+<CENTER>
+<IMG SRC="/swat/images/samba.gif" ALT="[ Samba ]" border=0><BR>
+<TABLE WIDTH="98%" CELLSPACING=1 CELLPADDING=4 BORDER=1>
+<TR><TD BGCOLOR="#ddddd0">
diff --git a/swat/lang/ja/include/header.nocss.html b/swat/lang/ja/include/header.nocss.html
new file mode 100644
index 00000000000..56d13dbc6d1
--- /dev/null
+++ b/swat/lang/ja/include/header.nocss.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<HTML>
+<HEAD>
+<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
+<TITLE>Samba Web Administration Tool</TITLE>
+</HEAD>
+<BODY bgcolor="white">
+<CENTER>
+<IMG SRC="/swat/images/samba.gif" ALT="[ Samba ]" border=0><BR>
+<TABLE WIDTH="98%" CELLSPACING=1 CELLPADDING=4 BORDER=1>
+<TR><TD BGCOLOR="#ddddd0">
diff --git a/swat/lang/ja/include/header_css.html b/swat/lang/ja/include/header_css.html
new file mode 100644
index 00000000000..b70876b6f82
--- /dev/null
+++ b/swat/lang/ja/include/header_css.html
@@ -0,0 +1 @@
+.i18n_translated_parm {color: #555555}
diff --git a/swat/lang/tr/help/welcome.html b/swat/lang/tr/help/welcome.html
new file mode 100644
index 00000000000..617f3678d39
--- /dev/null
+++ b/swat/lang/tr/help/welcome.html
@@ -0,0 +1,69 @@
+<h3>Swat'a Hoþgeldiniz!</h3>
+
+Yukarýdaki düðmelerden birini kullanarak bir ayar eylemi seçin.
+
+<h3><a href="/swat/help/samba.7.html" target="docs">Samba</a> Dökümantasyon</h3>
+
+<ul>
+ <li><b>Sunucular</b>
+ <ul>
+ <li><a href="/swat/help/smbd.8.html" target="docs">smbd</a> - SMB sunucusu
+ <li><a href="/swat/help/nmbd.8.html" target="docs">nmbd</a> - NetBIOS isim sunucusu
+ <li><a href="/swat/help/winbindd.8.html" target="docs">winbindd</a> - winbind sunucusu
+ </ul>
+ <li><b>Ayar dosyalarý</b>
+ <ul>
+ <li><a href="/swat/help/smb.conf.5.html" target="docs">smb.conf</a> - ana Samba ayar dosyasý
+ <li><a href="/swat/help/lmhosts.5.html" target="docs">lmhosts</a> - NetBIOS hosts dosyasý
+ <li><a href="/swat/help/smbpasswd.5.html" target="docs">smbpasswd</a> - SMB þifre dosyasý
+ </ul>
+ <li><b>Yönetim Araçlarý</b>
+ <ul>
+ <li><a href="/swat/help/smbcontrol.1.html" target="docs">smbcontrol</a> - Samba sunucularýna kontrol iletileri gönderir
+ <li><a href="/swat/help/smbpasswd.8.html" target="docs">smbpasswd</a> - SMB þifrelerini düzenler
+ <li><a href="/swat/help/swat.8.html" target="docs">SWAT</a> - web arayüzlü ayar aracý
+ <li><a href="/swat/help/make_smbcodepage.1.html" target="docs">make_smbcodepage</a> - kod sayfasý oluþturur
+ <li><a href="/swat/help/make_unicodemap.1.html" target="docs">make_unicodemap</a> - unicode eþleþme dosyasý oluþturur
+ <li><a href="/swat/help/smbrun.1.html" target="docs">smbrun</a> - içsel smbd aracý
+ </ul>
+ <li><b>Ýstemci Araçlarý</b>
+ <ul>
+ <li><a href="/swat/help/rpcclient.1.html" target="docs">rpcclient</a> - komut satýrý MS-RPC istemcisi
+ <li><a href="/swat/help/smbtar.1.html" target="docs">smbtar</a> - SMB yedekleme aracý
+ <li><a href="/swat/help/smbclient.1.html" target="docs">smbclient</a> - komut satýrý SMB istemcisi
+ <li><a href="/swat/help/smbmnt.8.html" target="docs">smbmnt</a> - Linux makinalarýna SMB dosya sistemlerini baðlamak için yardýmcý araç
+ <li><a href="/swat/help/smbmount.8.html" target="docs">smbmount</a> - Linux altýnda SMB dosya sistemlerini baðlamak için kullanýcý aracý
+ <li><a href="/swat/help/smbspool.8.html" target="docs">smbspool</a> - komut satýrý SMB yazýcý istemcisi
+ <li><a href="/swat/help/smbumount.8.html" target="docs">smbumount</a> - Linux altýnda SMB dosya sistemlerini çözmek için kullanýcý aracý
+ </ul>
+ <li><b>Teþhis Araçlarý</b>
+ <ul>
+ <li><a href="/swat/help/smbstatus.1.html" target="docs">smbstatus</a> - Samba gözlemcisi
+ <li><a href="/swat/help/testparm.1.html" target="docs">testparm</a> - ayar dosyasýný kontrol eder
+ <li><a href="/swat/help/testprns.1.html" target="docs">testprns</a> - yazýcý ayarlarýný kontrol eder
+ <li><a href="/swat/help/nmblookup.1.html" target="docs">nmblookup</a> - NetBIOS isim sorgulama aracý
+ </ul>
+ <li><b>Kitaplar</b>
+ <ul>
+ <li><a href="/swat/using_samba/index.html" target="docs">Samba'yý Kullanmak</a> - Yazan: Robert Eckstein, David Collier-Brown ve Peter Kelly
+ </ul>
+ <li><b>Samba HOWTO (Nasýl Yapýlýr?) Koleksiyonu</b></li>
+ <ul>
+ <li><a href="/swat/help/Samba-HOWTO-Collection.html">Bütün koleksiyon (tek dosya)</a>
+ <li><a href="/swat/help/DOMAIN_MEMBER.html">Samba 2.x'de security = domain </a>
+ <li><a href="/swat/help/winbind.html">Winbind Kullanarak Windows NT ve UNIX Arasýnda Birleþik Sistem Giriþi</a>
+ <li><a href="/swat/help/msdfs_setup.html">Samba'yý bir MS-DFS Sunucusu Olarak Ayarlamak</a>
+ <li><a href="/swat/help/NT_Security.html">UNIX Ýzin Bitleri ve Samba 2.x</a>
+ <li><a href="/swat/help/OS2-Client-HOWTO.html">OS/2 Ýstemcileri ve Samba</a>
+ <li><a href="/swat/help/printer_driver2.html">Samba 2.2.x Altýnda Yazýcý Kullanýmý</a>
+ <li><a href="/swat/help/UNIX_INSTALL.html">Samba Nasýl Kurulur ve Kontrol Edilir?</a>
+ <li><a href="/swat/help/Integrating-with-Windows.html">Ýsim Çözünme ve Yetkilendirme Ýþlemlerini Birleþtirmek</a>
+ <li><a href="/swat/help/CVS-Access.html">Samba yazýlýmlarýna CVS Eriþimi</a>
+ </ul>
+</ul>
+
+ <h3>Ýletiþim</h3>
+
+ Eðer SWAT'ýn bu sürümü ile ilgili konularý tartýþmak istiyorsanýz, lütfen
+<A HREF="http://lists.samba.org/">samba</A> eposta listesine üye olun.
+ \ No newline at end of file
diff --git a/swat/lang/tr/images/globals.gif b/swat/lang/tr/images/globals.gif
new file mode 100644
index 00000000000..c0722d2df52
--- /dev/null
+++ b/swat/lang/tr/images/globals.gif
Binary files differ
diff --git a/swat/lang/tr/images/home.gif b/swat/lang/tr/images/home.gif
new file mode 100644
index 00000000000..119b6da6edf
--- /dev/null
+++ b/swat/lang/tr/images/home.gif
Binary files differ
diff --git a/swat/lang/tr/images/passwd.gif b/swat/lang/tr/images/passwd.gif
new file mode 100644
index 00000000000..25652f211b1
--- /dev/null
+++ b/swat/lang/tr/images/passwd.gif
Binary files differ
diff --git a/swat/lang/tr/images/printers.gif b/swat/lang/tr/images/printers.gif
new file mode 100644
index 00000000000..a3caf2543b2
--- /dev/null
+++ b/swat/lang/tr/images/printers.gif
Binary files differ
diff --git a/swat/lang/tr/images/samba.gif b/swat/lang/tr/images/samba.gif
new file mode 100644
index 00000000000..3ec3d2195ff
--- /dev/null
+++ b/swat/lang/tr/images/samba.gif
Binary files differ
diff --git a/swat/lang/tr/images/shares.gif b/swat/lang/tr/images/shares.gif
new file mode 100644
index 00000000000..bbac9d5411e
--- /dev/null
+++ b/swat/lang/tr/images/shares.gif
Binary files differ
diff --git a/swat/lang/tr/images/status.gif b/swat/lang/tr/images/status.gif
new file mode 100644
index 00000000000..c606d6fcb07
--- /dev/null
+++ b/swat/lang/tr/images/status.gif
Binary files differ
diff --git a/swat/lang/tr/images/viewconfig.gif b/swat/lang/tr/images/viewconfig.gif
new file mode 100644
index 00000000000..3261ab4d8ba
--- /dev/null
+++ b/swat/lang/tr/images/viewconfig.gif
Binary files differ
diff --git a/swat/lang/tr/include/header.html b/swat/lang/tr/include/header.html
new file mode 100644
index 00000000000..7c11fd0ef9f
--- /dev/null
+++ b/swat/lang/tr/include/header.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<HTML>
+<HEAD>
+<TITLE>Samba Web Yönetim Aracý</TITLE>
+</HEAD>
+<BODY bgcolor="white">
+<CENTER>
+<IMG SRC="/swat/images/samba.gif" ALT="[ Samba ]" border=0><BR>
+<TABLE WIDTH="98%" CELLSPACING=1 CELLPADDING=4 BORDER=1>
+<TR><TD BGCOLOR="#ddddd0">
diff --git a/testsuite/README b/testsuite/README
new file mode 100644
index 00000000000..0d5157325a9
--- /dev/null
+++ b/testsuite/README
@@ -0,0 +1,19 @@
+README for testsuite directory
+------------------------------
+
+The Samba testsuite is divided up into the following subdirectories.
+
+ - config Configuration for DejaGnu program
+ - lib Various library files used by tool directories
+
+ - nsswitch Tests for nsswitch extensions
+ - server Miscellaneous server tests
+ - rpc_client Tests for the RPC client library code
+ - rpcclient Tests for the rpcclient program
+
+ - build_farm Tests designed to run automatically on the build farm
+
+All the scripts except those in build_farm require an unreleased
+version of DejaGNU, and although they contain some useful tests they
+are not so useful at the moment. All scripts are migrating to a
+single test framework, Satyr. <cvs://cvs.samba.org/data/cvs/satyr>
diff --git a/testsuite/build_farm/backtrace b/testsuite/build_farm/backtrace
new file mode 100755
index 00000000000..efaa9f2dcde
--- /dev/null
+++ b/testsuite/build_farm/backtrace
@@ -0,0 +1,15 @@
+#!/bin/sh
+# Modified version of tridge's backtrace script.
+# we want everything on stderr, so the program is not disturbed
+exec 1>&2
+
+PID=$1
+TMPFILE=$prefix/backtrace.$$
+cat << EOF > $TMPFILE
+set height 0
+up 8
+bt full
+quit
+EOF
+gdb -x $TMPFILE $prefix/sbin/smbd $PID
+/bin/rm -f $TMPFILE
diff --git a/testsuite/build_farm/basicsmb-domainsec-nt4.test b/testsuite/build_farm/basicsmb-domainsec-nt4.test
new file mode 100644
index 00000000000..4e68acabec5
--- /dev/null
+++ b/testsuite/build_farm/basicsmb-domainsec-nt4.test
@@ -0,0 +1,28 @@
+. basicsmb.fns
+
+test_joindomain_nt4() {
+
+ echo $prefix/bin/smbpasswd -L -a -m buildfarm$
+ $prefix/bin/smbpasswd -L -a -m buildfarm$
+
+ echo $prefix/bin/net rpc oldjoin -S localhost
+ $prefix/bin/net rpc oldjoin -S localhost
+ status=$?
+ if [ $status = 0 ]; then
+ echo "'net rpc oldjoin' correctly joined the domain"
+ else
+ echo "'net rpc oldjoin' failed to join the domain! (status $status)"
+ return 1
+ fi
+ return 0
+}
+
+password=samba
+security=DOMAIN
+(test_smb_conf_setup && test_smbpasswd $password ) || exit 1
+
+test_joindomain_nt4 $password || exit 1
+
+test_listfilesauth $security || exit 1
+test_listfilesnpw $security || exit 1
+
diff --git a/testsuite/build_farm/basicsmb-domainsec.test b/testsuite/build_farm/basicsmb-domainsec.test
new file mode 100644
index 00000000000..eb0a5c81aa1
--- /dev/null
+++ b/testsuite/build_farm/basicsmb-domainsec.test
@@ -0,0 +1,27 @@
+
+. basicsmb.fns
+
+test_joindomain() {
+ test_join_domain_password="$1"
+
+ echo "$prefix/bin/net rpc join -S localhost -U $whoami%$test_join_domain_password"
+ $prefix/bin/net rpc join -S localhost -U $whoami%$test_join_domain_password
+ status=$?
+ if [ $status = 0 ]; then
+ echo "'net rpc join' correctly joined the domain"
+ else
+ echo "'net rpc join' failed to join the domain! (status $status)"
+ return 1
+ fi
+ return 0
+}
+
+password=samba
+(test_smb_conf_setup && test_smbpasswd $password ) || exit 1
+
+test_joindomain $password || exit 1
+
+security=DOMAIN
+test_listfilesauth $security || exit 1
+test_listfilesnpw $security || exit 1
+
diff --git a/testsuite/build_farm/basicsmb-hostsdeny.test b/testsuite/build_farm/basicsmb-hostsdeny.test
new file mode 100644
index 00000000000..c7f6bec62f7
--- /dev/null
+++ b/testsuite/build_farm/basicsmb-hostsdeny.test
@@ -0,0 +1,18 @@
+. basicsmb.fns
+
+password="samba"
+security="hostsdeny"
+(test_smb_conf_setup && test_smbpasswd $password) || exit 1
+
+(test_listfilesauth_should_deny $security) || exit 1
+
+
+
+
+
+
+
+
+
+
+
diff --git a/testsuite/build_farm/basicsmb-hostsequiv.test b/testsuite/build_farm/basicsmb-hostsequiv.test
new file mode 100644
index 00000000000..d424743d116
--- /dev/null
+++ b/testsuite/build_farm/basicsmb-hostsequiv.test
@@ -0,0 +1,26 @@
+if [ $whoami = "root" ]; then
+ exit 0;
+fi
+
+. basicsmb.fns
+
+test_listfilesrootnpw() {
+ remote_name="$1"
+ echo $prefix/bin/smbclient //$remote_name/samba -n buildclient -Uroot% -c 'ls'
+ $prefix/bin/smbclient //$remote_name/samba -n buildclient -Uroot% -c 'ls'
+ status=$?
+ if [ $status = 0 ]; then
+ echo "smbd listed files AS ROOT with NO PASSWORD (hosts equiv test)!"
+ return 1
+ else
+ echo "listing files with smbd failed with status $status (correct)"
+ fi
+ return 0
+}
+
+password="not-a-valid-password"
+security="hostsequiv"
+(test_smb_conf_setup ) || exit 1
+
+(test_listfilesauth $security) || exit 1
+(test_listfilesrootnpw $security) || exit 1
diff --git a/testsuite/build_farm/basicsmb-invalidusers.test b/testsuite/build_farm/basicsmb-invalidusers.test
new file mode 100644
index 00000000000..7d67cc2fc71
--- /dev/null
+++ b/testsuite/build_farm/basicsmb-invalidusers.test
@@ -0,0 +1,10 @@
+. basicsmb.fns
+
+password="samba"
+security="invalidusers"
+(test_smb_conf_setup && test_smbpasswd $password) || exit 1
+
+(test_listfilesauth_should_deny $security) || exit 1
+
+security="validusers"
+(test_listfilesauth $security) || exit 1
diff --git a/testsuite/build_farm/basicsmb-local-pass-change.test b/testsuite/build_farm/basicsmb-local-pass-change.test
new file mode 100644
index 00000000000..432376c6662
--- /dev/null
+++ b/testsuite/build_farm/basicsmb-local-pass-change.test
@@ -0,0 +1,10 @@
+. basicsmb.fns
+
+passwordold=samba
+password=samba2
+security=USER
+(test_smb_conf_setup && test_smbpasswd $passwordold && test_smbpasswd_local $passwordold $password) || exit 1
+
+test_listfilesauth $security || exit 1
+test_listfilesnpw $security || exit 1
+
diff --git a/testsuite/build_farm/basicsmb-preexec.test b/testsuite/build_farm/basicsmb-preexec.test
new file mode 100644
index 00000000000..fc072e5fdb6
--- /dev/null
+++ b/testsuite/build_farm/basicsmb-preexec.test
@@ -0,0 +1,28 @@
+. basicsmb.fns
+
+password=samba
+(test_smb_conf_setup && test_smbpasswd $password ) || exit 1
+
+rm -f $prefix/testdir/preexec_touch
+
+mode=PREEXEC
+(test_listfilesauth $mode) || exit 1
+
+if [ -f $prefix/testdir/preexec_touch ]; then
+ rm -f $prefix/testdir/preexec_touch
+else
+ exit 1;
+fi
+
+mode=PREEXEC_close
+(test_listfilesauth $mode) || exit 1
+
+if [ -f $prefix/testdir/preexec_touch ]; then
+ rm -f $prefix/testdir/preexec_touch
+else
+ exit 1;
+fi
+
+mode=PREEXEC_cl_fl
+(test_listfilesauth_should_deny $mode) || exit 1
+
diff --git a/testsuite/build_farm/basicsmb-remote-pass-change.test b/testsuite/build_farm/basicsmb-remote-pass-change.test
new file mode 100644
index 00000000000..adfe0dc3ebc
--- /dev/null
+++ b/testsuite/build_farm/basicsmb-remote-pass-change.test
@@ -0,0 +1,10 @@
+. basicsmb.fns
+
+passwordold=samba
+password=samba2
+security=USER
+(test_smb_conf_setup && test_smbpasswd $passwordold && test_smbpasswd_remote $passwordold $password) || exit 1
+
+test_listfilesauth $security || exit 1
+test_listfilesnpw $security || exit 1
+
diff --git a/testsuite/build_farm/basicsmb-serversec.test b/testsuite/build_farm/basicsmb-serversec.test
new file mode 100644
index 00000000000..b45899ab774
--- /dev/null
+++ b/testsuite/build_farm/basicsmb-serversec.test
@@ -0,0 +1,9 @@
+. basicsmb.fns
+
+password=samba
+security=SERVER
+(test_smb_conf_setup && test_smbpasswd $password ) || exit 1
+
+test_listfilesauth $security || exit 1
+test_listfilesnpw $security || exit 1
+
diff --git a/testsuite/build_farm/basicsmb-shareguest.test b/testsuite/build_farm/basicsmb-shareguest.test
new file mode 100644
index 00000000000..4ba445a17e3
--- /dev/null
+++ b/testsuite/build_farm/basicsmb-shareguest.test
@@ -0,0 +1,20 @@
+. basicsmb.fns
+
+test_listfilesguestshare() {
+ remote_name=$1
+ echo $prefix/bin/smbclient //$remote_name/guest_share -n buildclient -U$whoami% -c 'ls'
+ $prefix/bin/smbclient //$remote_name/guest_share -n buildclient -U$whoami% -c 'ls'
+ status=$?
+ if [ $status = 0 ]; then
+ echo "smbd listed files correctly (guest share)"
+ else
+ echo "listing files on a guest share failed with status $status"
+ return 1
+ fi
+ return 0
+}
+
+security=SHARE
+( test_smb_conf_setup ) || exit 1
+
+( test_listfilesguestshare $security ) || exit 1 \ No newline at end of file
diff --git a/testsuite/build_farm/basicsmb-sharelist.test b/testsuite/build_farm/basicsmb-sharelist.test
new file mode 100644
index 00000000000..188e985658d
--- /dev/null
+++ b/testsuite/build_farm/basicsmb-sharelist.test
@@ -0,0 +1,22 @@
+. basicsmb.fns
+test_sharelist() {
+ echo $prefix/bin/smbclient -U$whoami% -L localhost
+ $prefix/bin/smbclient -U$whoami% -L localhost
+ status=$?
+ if [ $status = 0 ]; then
+ echo "smbd listed shares OK"
+ else
+ echo "listing shares with smbd failed with status $status"
+ return 1
+ fi
+ return 0
+}
+
+# Need guest account
+password=samba
+security=USER
+(test_smb_conf_setup && test_smbpasswd $password ) || exit 1
+
+test_sharelist || exit 1
+
+
diff --git a/testsuite/build_farm/basicsmb-sharesec.test b/testsuite/build_farm/basicsmb-sharesec.test
new file mode 100644
index 00000000000..edef6a91230
--- /dev/null
+++ b/testsuite/build_farm/basicsmb-sharesec.test
@@ -0,0 +1,9 @@
+. basicsmb.fns
+
+password="samba"
+security="SHARE"
+(test_smb_conf_setup && ( test_smbpasswd $password ) ) || exit 1
+
+(test_listfilesauth $security) || exit 1
+(test_listfilesnpw $security) || exit 1
+
diff --git a/testsuite/build_farm/basicsmb-usersec.test b/testsuite/build_farm/basicsmb-usersec.test
new file mode 100644
index 00000000000..06f2a1c4040
--- /dev/null
+++ b/testsuite/build_farm/basicsmb-usersec.test
@@ -0,0 +1,9 @@
+. basicsmb.fns
+
+password=samba
+security=USER
+(test_smb_conf_setup && test_smbpasswd $password ) || exit 1
+
+test_listfilesauth $security || exit 1
+test_listfilesnpw $security || exit 1
+
diff --git a/testsuite/build_farm/basicsmb.fns b/testsuite/build_farm/basicsmb.fns
new file mode 100644
index 00000000000..3a9080f473b
--- /dev/null
+++ b/testsuite/build_farm/basicsmb.fns
@@ -0,0 +1,204 @@
+#! /bin/sh
+
+# Common functions for Samba build scripts.
+
+# Copyright (C) 2001 by Martin Pool <mbp@samba.org> and others
+
+# The following variables are passed in by the calling script. They
+# originate in either the buildfarm scripts or the configured
+# Makefile.
+
+# PREFIX = Installed prefix of samba test installation. Used to
+# locate binaries, configuration files, etc.
+
+# XXX: It's pretty bad to clobber the installed configuration file and
+# other data in $prefix, because somebody might unwittingly run this
+# with prefix=/usr.
+
+# Really what we want is a consistent way to pass the location of the
+# configuration and all other files into *all* Samba programs
+# (smbclient, smd, ...) and be able to set them to a temporary
+# directory when testing. Some of them take a -c parameter, but tpot
+# says it's not done consistently.
+
+template_setup() {
+ cat template/$1 | \
+ sed "s|PREFIX|$prefix|g" | \
+ sed "s|BUILD_FARM|$test_root|g" | \
+ sed "s|WHOAMI|$whoami|g" | \
+ sed "s|LOGLEVEL|$loglevel|g" \
+ > $prefix/$2
+ echo "template_setup: Created $prefix/$2"
+}
+
+template_smb_conf_setup() {
+ template_setup "basicsmb.smb.conf$1" "lib/smb.conf$1"
+}
+
+test_smb_conf_setup() {
+ echo "test_smb_conf_setup: Configuring: "
+ echo " PREFIX=$prefix"
+ echo " BUILD_FARM=$test_root"
+ echo " WHOAMI=$whoami"
+ echo " LOGLEVEL=$loglevel"
+ echo " TREE=$tree"
+
+ case "$prefix" in
+ /usr*|/|//)
+ echo "** I don't want to clobber your installation in "
+ echo "** $prefix"
+ echo "** by running tests there. Please reconfigure this source tree to"
+ echo "** use a different prefix."
+ exit 1
+ esac
+
+# Please keep these names under 15 characters,
+# so that the final name is 31 characters or fewer.
+
+ template_smb_conf_setup
+ template_smb_conf_setup .hostsequiv
+ template_smb_conf_setup .validusers
+ template_smb_conf_setup .invalidusers
+ template_smb_conf_setup .preexec
+ template_smb_conf_setup .preexec_close
+ template_smb_conf_setup .preexec_cl_fl
+
+ template_smb_conf_setup .share
+ template_smb_conf_setup .user
+ template_smb_conf_setup .server
+ template_smb_conf_setup .domain
+
+ template_setup preexec lib/preexec
+
+ touch $prefix/lib/smb.conf.
+ touch $prefix/lib/smb.conf.localhost
+
+ echo "127.0.0.1 localhost">$prefix/lib/lmhosts
+ echo "127.0.0.2 BUILDFARM">>$prefix/lib/lmhosts
+ echo "127.0.0.3 SHARE">>$prefix/lib/lmhosts
+ echo "127.0.0.4 USER">>$prefix/lib/lmhosts
+ echo "127.0.0.5 SERVER">>$prefix/lib/lmhosts
+ echo "127.0.0.6 DOMAIN">>$prefix/lib/lmhosts
+ echo "127.0.0.7 HOSTSEQUIV">>$prefix/lib/lmhosts
+ echo "127.0.0.7 VALIDUSERS">>$prefix/lib/lmhosts
+ echo "127.0.0.7 INVALIDUSERS">>$prefix/lib/lmhosts
+ echo "127.0.0.7 PREEXEC">>$prefix/lib/lmhosts
+ echo "127.0.0.7 PREEXEC_CLOSE">>$prefix/lib/lmhosts
+ echo "127.0.0.7 PREEXEC_CL_FL">>$prefix/lib/lmhosts
+
+
+ echo "127.0.0.1" > $prefix/lib/hosts.equiv
+
+}
+
+test_smbpasswd() {
+ test_smbpasswd_password="$1"
+ rm -f $prefix/private/smbpasswd
+ echo "( echo $test_smbpasswd_password ; echo $test_smbpasswd_password; ) | $prefix/bin/smbpasswd -L -D $loglevel -s -a $whoami"
+ ( echo $test_smbpasswd_password; echo $test_smbpasswd_password; ) | $prefix/bin/smbpasswd -L -D $loglevel -s -a $whoami
+ status=$?
+ if [ $status = 0 ]; then
+ echo "smbpasswd correctly set initial password ($test_smbpasswd_password)"
+ else
+ echo "smbpasswd failed to set initial password ($test_smbpasswd_password)! (status $status)"
+ return 1
+ fi
+ return 0
+}
+
+test_smbpasswd_remote() {
+ test_smbpasswd_rem_password="$1"
+ test_smbpasswd_rem_newpassword="$2"
+ echo "( echo $test_smbpasswd_rem_password; echo $test_smbpasswd_rem_newpassword; echo $test_smbpasswd_rem_newpassword; ) | $prefix/bin/smbpasswd -r localhost -s -U $whoami"
+ ( echo $test_smbpasswd_rem_password; echo $test_smbpasswd_rem_newpassword; echo $test_smbpasswd_rem_newpassword; ) | $prefix/bin/smbpasswd -r localhost -s -U $whoami
+ status=$?
+ if [ $status = 0 ]; then
+ echo "smbpasswd correctly remotely changed password ($test_smbpasswd_rem_password -> $test_smbpasswd_rem_newpassword)"
+ else
+ echo "smbpasswd failed to remotely changed password ($test_smbpasswd_rem_password -> $test_smbpasswd_rem_newpassword)! (status $status)"
+ return 1
+ fi
+ return 0
+}
+
+test_smbpasswd_local() {
+ test_smbpasswd_newpassword="$2"
+ echo "( echo $test_smbpasswd_newpassword ; echo $test_smbpasswd_newpassword; ) | $prefix/bin/smbpasswd -L -s $whoami"
+ ( echo $test_smbpasswd_newpassword ; echo $test_smbpasswd_newpassword; ) | $prefix/bin/smbpasswd -L -s $whoami
+ status=$?
+ if [ $status = 0 ]; then
+ echo "smbpasswd correctly locally changed password ($test_smbpasswd_password -> $test_smbpasswd_newpassword)"
+ else
+ echo "smbpasswd failed to locallly changed password ($test_smbpasswd_password -> $test_smbpasswd_newpassword)! (status $status)"
+ return 1
+ fi
+ return 0
+}
+
+test_listfilesauth() {
+ remote_name="$1"
+ echo $prefix/bin/smbclient //$remote_name/samba -n buildclient -U$whoami%$password -c 'ls'
+ $prefix/bin/smbclient //$remote_name/samba -n buildclient -U$whoami%$password -c 'ls'
+ status=$?
+ if [ $status = 0 ]; then
+ echo "listed files OK"
+ else
+ echo "listing files with smbd failed with status $status"
+ return 1
+ fi
+ return 0
+}
+
+test_listfilesnpw() {
+ remote_name="$1"
+ echo $prefix/bin/smbclient //$remote_name/samba -n buildclient -U$whoami% -c 'ls'
+ $prefix/bin/smbclient //$remote_name/samba -n buildclient -U$whoami% -c 'ls'
+ status=$?
+ if [ $status = 0 ]; then
+ echo "smbd listed files with NO PASSWORD on an authenticated share!"
+ return 1
+ else
+ echo "listing files with smbd failed with status $status (correct)"
+ fi
+ return 0
+}
+
+test_listfilesauth_should_deny() {
+ remote_name="$1"
+ echo $prefix/bin/smbclient //$remote_name/samba -n buildclient -U$whoami%$password -c 'ls'
+ $prefix/bin/smbclient //$remote_name/samba -n buildclient -U$whoami%$password -c 'ls'
+ status=$?
+ if [ $status = 0 ]; then
+ echo "smbd LISTED FILES despite smb.conf entires to the contary!"
+ return 1
+ else
+ echo "listing files with smbd failed with status $status (correct)"
+ fi
+ return 0
+}
+
+echo "LIBSMB_PROG=$LIBSMB_PROG" >&2
+
+
+
+# Give sensible defaults to some variables.
+
+# "What's my age again?"
+
+if [ ! $USER = "" ]; then
+ whoami=$USER
+else
+ if [ ! $LOGNAME = "" ]; then
+ whoami=$LOGNAME
+ else
+ whoami=build
+ fi
+fi
+
+
+
+if test -z "$loglevel"
+then
+ loglevel=1
+fi
+
diff --git a/testsuite/build_farm/runlist b/testsuite/build_farm/runlist
new file mode 100644
index 00000000000..594c172b6ff
--- /dev/null
+++ b/testsuite/build_farm/runlist
@@ -0,0 +1,18 @@
+TEST_ALL="basicsmb-sharelist basicsmb-local-pass-change \
+basicsmb-sharesec basicsmb-usersec \
+basicsmb-serversec \
+basicsmb-shareguest basicsmb-hostsequiv basicsmb-invalidusers \
+basicsmb-hostsdeny basicsmb-remote-pass-change \
+basicsmb-preexec \
+torture-FDPASS torture-LOCK1 torture-LOCK2 \
+torture-LOCK3 torture-LOCK4 torture-LOCK5 \
+torture-LOCK6 torture-LOCK7 \
+torture-UNLINK torture-BROWSE torture-ATTR \
+torture-TRANS2 torture-TORTURE torture-OPLOCK1 \
+torture-OPLOCK3 torture-DIR torture-DIR1 torture-DENY1 \
+torture-DENY2 torture-TCON torture-TCON2 torture-TCONDEV \
+torture-RW1 torture-RW2 torture-OPEN torture-XCOPY \
+torture-RENAME torture-DELETE torture-PROPERTIES \
+torture-MANGLE torture-FDSESS"
+
+#basicsmb-domainsec basicsmb-domainsec-nt4 \ No newline at end of file
diff --git a/testsuite/build_farm/template/basicsmb.smb.conf b/testsuite/build_farm/template/basicsmb.smb.conf
new file mode 100644
index 00000000000..234419b6885
--- /dev/null
+++ b/testsuite/build_farm/template/basicsmb.smb.conf
@@ -0,0 +1,49 @@
+[global]
+ netbios name = BUILDFARM
+ workgroup = TESTWG
+ log level = LOGLEVEL
+ debug timestamp = no
+ encrypt passwords = yes
+ server string = Samba %v Build Farm Tests
+ name resolve order = lmhosts
+ guest account = WHOAMI
+ domain logons = yes
+
+ strict locking = yes
+
+ include = PREFIX/lib/smb.conf.%L
+
+ add machine script = useradd %u -d /dev/null -s /bin/false
+
+ panic action = /bin/sh BUILD_FARM/samba/testsuite/build_farm/backtrace %d
+
+ passdb backend = smbpasswd
+
+ idmap uid = 10000-200000
+ map hidden = yes
+ create mask = 0777
+
+[test]
+ path = PREFIX/testdir
+ read only = no
+
+[samba]
+ path = BUILD_FARM/samba
+ read only = yes
+ comment = Samba HEAD Sources
+
+[samba_2_2]
+ path = BUILD_FARM/samba_2_2
+ read only = yes
+ comment = Samba 2.2. Sources
+
+[rsync]
+ path = BUILD_FARM/rsync
+ read only = yes
+ comment = Rsync Sources
+
+[guest_share]
+ path = PREFIX
+ guest ok = yes
+ read only = yes
+ comment = Unauthenticated share for use in share level test
diff --git a/testsuite/build_farm/template/basicsmb.smb.conf.domain b/testsuite/build_farm/template/basicsmb.smb.conf.domain
new file mode 100644
index 00000000000..8b9728838e1
--- /dev/null
+++ b/testsuite/build_farm/template/basicsmb.smb.conf.domain
@@ -0,0 +1,2 @@
+security=domain
+password server=user
diff --git a/testsuite/build_farm/template/basicsmb.smb.conf.hostsdeny b/testsuite/build_farm/template/basicsmb.smb.conf.hostsdeny
new file mode 100644
index 00000000000..3fce0bdbf4e
--- /dev/null
+++ b/testsuite/build_farm/template/basicsmb.smb.conf.hostsdeny
@@ -0,0 +1 @@
+ hosts deny = 127. \ No newline at end of file
diff --git a/testsuite/build_farm/template/basicsmb.smb.conf.hostsequiv b/testsuite/build_farm/template/basicsmb.smb.conf.hostsequiv
new file mode 100644
index 00000000000..750af74f59c
--- /dev/null
+++ b/testsuite/build_farm/template/basicsmb.smb.conf.hostsequiv
@@ -0,0 +1,3 @@
+ hostname lookups = no
+ hosts equiv=PREFIX/lib/hosts.equiv
+ auth methods = hostsequiv
diff --git a/testsuite/build_farm/template/basicsmb.smb.conf.invalidusers b/testsuite/build_farm/template/basicsmb.smb.conf.invalidusers
new file mode 100644
index 00000000000..a96a316db9f
--- /dev/null
+++ b/testsuite/build_farm/template/basicsmb.smb.conf.invalidusers
@@ -0,0 +1 @@
+ invalid users = WHOAMI
diff --git a/testsuite/build_farm/template/basicsmb.smb.conf.preexec b/testsuite/build_farm/template/basicsmb.smb.conf.preexec
new file mode 100644
index 00000000000..cc34872c5df
--- /dev/null
+++ b/testsuite/build_farm/template/basicsmb.smb.conf.preexec
@@ -0,0 +1 @@
+preexec = /bin/sh PREFIX/lib/preexec
diff --git a/testsuite/build_farm/template/basicsmb.smb.conf.preexec_cl_fl b/testsuite/build_farm/template/basicsmb.smb.conf.preexec_cl_fl
new file mode 100644
index 00000000000..4a6fae57bc0
--- /dev/null
+++ b/testsuite/build_farm/template/basicsmb.smb.conf.preexec_cl_fl
@@ -0,0 +1,2 @@
+preexec close = yes
+preexec = PREFIX/lib/preexec_does_not_exist
diff --git a/testsuite/build_farm/template/basicsmb.smb.conf.preexec_close b/testsuite/build_farm/template/basicsmb.smb.conf.preexec_close
new file mode 100644
index 00000000000..3aac6998bfc
--- /dev/null
+++ b/testsuite/build_farm/template/basicsmb.smb.conf.preexec_close
@@ -0,0 +1,2 @@
+preexec close = yes
+preexec = /bin/sh PREFIX/lib/preexec
diff --git a/testsuite/build_farm/template/basicsmb.smb.conf.server b/testsuite/build_farm/template/basicsmb.smb.conf.server
new file mode 100644
index 00000000000..016f84cd353
--- /dev/null
+++ b/testsuite/build_farm/template/basicsmb.smb.conf.server
@@ -0,0 +1,3 @@
+security=server
+password server=user
+smb passwd file=NON_EXISTANT_FILE
diff --git a/testsuite/build_farm/template/basicsmb.smb.conf.share b/testsuite/build_farm/template/basicsmb.smb.conf.share
new file mode 100644
index 00000000000..8e69cc199a5
--- /dev/null
+++ b/testsuite/build_farm/template/basicsmb.smb.conf.share
@@ -0,0 +1 @@
+ security = share
diff --git a/testsuite/build_farm/template/basicsmb.smb.conf.user b/testsuite/build_farm/template/basicsmb.smb.conf.user
new file mode 100644
index 00000000000..9d294b9c396
--- /dev/null
+++ b/testsuite/build_farm/template/basicsmb.smb.conf.user
@@ -0,0 +1 @@
+ security = user
diff --git a/testsuite/build_farm/template/basicsmb.smb.conf.validusers b/testsuite/build_farm/template/basicsmb.smb.conf.validusers
new file mode 100644
index 00000000000..d4a85e0a028
--- /dev/null
+++ b/testsuite/build_farm/template/basicsmb.smb.conf.validusers
@@ -0,0 +1 @@
+ valid users = WHOAMI
diff --git a/testsuite/build_farm/template/preexec b/testsuite/build_farm/template/preexec
new file mode 100644
index 00000000000..23809aaf893
--- /dev/null
+++ b/testsuite/build_farm/template/preexec
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo "Test worked" > PREFIX/testdir/preexec_touch
diff --git a/testsuite/build_farm/torture-ATTR.test b/testsuite/build_farm/torture-ATTR.test
new file mode 100644
index 00000000000..db6d5e87824
--- /dev/null
+++ b/testsuite/build_farm/torture-ATTR.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "ATTR"
diff --git a/testsuite/build_farm/torture-BROWSE.test b/testsuite/build_farm/torture-BROWSE.test
new file mode 100644
index 00000000000..da758977da5
--- /dev/null
+++ b/testsuite/build_farm/torture-BROWSE.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "BROWSE"
diff --git a/testsuite/build_farm/torture-DELETE.test b/testsuite/build_farm/torture-DELETE.test
new file mode 100644
index 00000000000..395f449d1e1
--- /dev/null
+++ b/testsuite/build_farm/torture-DELETE.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "DELETE"
diff --git a/testsuite/build_farm/torture-DENY1.test b/testsuite/build_farm/torture-DENY1.test
new file mode 100644
index 00000000000..99ce7ea8869
--- /dev/null
+++ b/testsuite/build_farm/torture-DENY1.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "DENY1"
diff --git a/testsuite/build_farm/torture-DENY2.test b/testsuite/build_farm/torture-DENY2.test
new file mode 100644
index 00000000000..17c8f707d87
--- /dev/null
+++ b/testsuite/build_farm/torture-DENY2.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "DENY2"
diff --git a/testsuite/build_farm/torture-DIR.test b/testsuite/build_farm/torture-DIR.test
new file mode 100644
index 00000000000..085ce59c3b0
--- /dev/null
+++ b/testsuite/build_farm/torture-DIR.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "DIR"
diff --git a/testsuite/build_farm/torture-DIR1.test b/testsuite/build_farm/torture-DIR1.test
new file mode 100644
index 00000000000..6cc075e9ba9
--- /dev/null
+++ b/testsuite/build_farm/torture-DIR1.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "DIR1"
diff --git a/testsuite/build_farm/torture-FDPASS.test b/testsuite/build_farm/torture-FDPASS.test
new file mode 100644
index 00000000000..40ffdb666d7
--- /dev/null
+++ b/testsuite/build_farm/torture-FDPASS.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "FDSESS"
diff --git a/testsuite/build_farm/torture-FDSESS.test b/testsuite/build_farm/torture-FDSESS.test
new file mode 100644
index 00000000000..e8af277d430
--- /dev/null
+++ b/testsuite/build_farm/torture-FDSESS.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "FDPASS"
diff --git a/testsuite/build_farm/torture-LOCK1.test b/testsuite/build_farm/torture-LOCK1.test
new file mode 100644
index 00000000000..fd01c492f16
--- /dev/null
+++ b/testsuite/build_farm/torture-LOCK1.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "LOCK1"
diff --git a/testsuite/build_farm/torture-LOCK2.test b/testsuite/build_farm/torture-LOCK2.test
new file mode 100644
index 00000000000..66b671d8010
--- /dev/null
+++ b/testsuite/build_farm/torture-LOCK2.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "LOCK2"
diff --git a/testsuite/build_farm/torture-LOCK3.test b/testsuite/build_farm/torture-LOCK3.test
new file mode 100644
index 00000000000..dcf14019d8e
--- /dev/null
+++ b/testsuite/build_farm/torture-LOCK3.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "LOCK3"
diff --git a/testsuite/build_farm/torture-LOCK4.test b/testsuite/build_farm/torture-LOCK4.test
new file mode 100644
index 00000000000..8fdc9b66615
--- /dev/null
+++ b/testsuite/build_farm/torture-LOCK4.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "LOCK4"
diff --git a/testsuite/build_farm/torture-LOCK5.test b/testsuite/build_farm/torture-LOCK5.test
new file mode 100644
index 00000000000..a04f83c8491
--- /dev/null
+++ b/testsuite/build_farm/torture-LOCK5.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "LOCK5"
diff --git a/testsuite/build_farm/torture-LOCK6.test b/testsuite/build_farm/torture-LOCK6.test
new file mode 100644
index 00000000000..78e139e3103
--- /dev/null
+++ b/testsuite/build_farm/torture-LOCK6.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "LOCK6"
diff --git a/testsuite/build_farm/torture-LOCK7.test b/testsuite/build_farm/torture-LOCK7.test
new file mode 100644
index 00000000000..fc967fca57d
--- /dev/null
+++ b/testsuite/build_farm/torture-LOCK7.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "LOCK7"
diff --git a/testsuite/build_farm/torture-MANGLE.test b/testsuite/build_farm/torture-MANGLE.test
new file mode 100644
index 00000000000..5a3d478a456
--- /dev/null
+++ b/testsuite/build_farm/torture-MANGLE.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "MANGLE"
diff --git a/testsuite/build_farm/torture-OPEN.test b/testsuite/build_farm/torture-OPEN.test
new file mode 100644
index 00000000000..ee3e55f0890
--- /dev/null
+++ b/testsuite/build_farm/torture-OPEN.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "OPEN"
diff --git a/testsuite/build_farm/torture-OPLOCK1.test b/testsuite/build_farm/torture-OPLOCK1.test
new file mode 100644
index 00000000000..bb606ad3bc9
--- /dev/null
+++ b/testsuite/build_farm/torture-OPLOCK1.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "OPLOCK1"
diff --git a/testsuite/build_farm/torture-OPLOCK3.test b/testsuite/build_farm/torture-OPLOCK3.test
new file mode 100644
index 00000000000..f8dfb3f8e9b
--- /dev/null
+++ b/testsuite/build_farm/torture-OPLOCK3.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "OPLOCK3"
diff --git a/testsuite/build_farm/torture-PROPERTIES.test b/testsuite/build_farm/torture-PROPERTIES.test
new file mode 100644
index 00000000000..91fde27f8af
--- /dev/null
+++ b/testsuite/build_farm/torture-PROPERTIES.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "PROPERTIES"
diff --git a/testsuite/build_farm/torture-RANDOMIPC.test b/testsuite/build_farm/torture-RANDOMIPC.test
new file mode 100644
index 00000000000..e510b6b6672
--- /dev/null
+++ b/testsuite/build_farm/torture-RANDOMIPC.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "RANDOMIPC"
diff --git a/testsuite/build_farm/torture-RENAME.test b/testsuite/build_farm/torture-RENAME.test
new file mode 100644
index 00000000000..58cb8eb466f
--- /dev/null
+++ b/testsuite/build_farm/torture-RENAME.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "RENAME"
diff --git a/testsuite/build_farm/torture-RW1.test b/testsuite/build_farm/torture-RW1.test
new file mode 100644
index 00000000000..6be4a897d91
--- /dev/null
+++ b/testsuite/build_farm/torture-RW1.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "RW1"
diff --git a/testsuite/build_farm/torture-RW2.test b/testsuite/build_farm/torture-RW2.test
new file mode 100644
index 00000000000..dc457decab1
--- /dev/null
+++ b/testsuite/build_farm/torture-RW2.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "RW2"
diff --git a/testsuite/build_farm/torture-TCON.test b/testsuite/build_farm/torture-TCON.test
new file mode 100644
index 00000000000..7d1aba0f29f
--- /dev/null
+++ b/testsuite/build_farm/torture-TCON.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "TCON"
diff --git a/testsuite/build_farm/torture-TCON1.test b/testsuite/build_farm/torture-TCON1.test
new file mode 100644
index 00000000000..3c9267640de
--- /dev/null
+++ b/testsuite/build_farm/torture-TCON1.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "TCON1"
diff --git a/testsuite/build_farm/torture-TCON2.test b/testsuite/build_farm/torture-TCON2.test
new file mode 100644
index 00000000000..1f30a975daa
--- /dev/null
+++ b/testsuite/build_farm/torture-TCON2.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "TCON2"
diff --git a/testsuite/build_farm/torture-TCONDEV.test b/testsuite/build_farm/torture-TCONDEV.test
new file mode 100644
index 00000000000..18bd5345fb7
--- /dev/null
+++ b/testsuite/build_farm/torture-TCONDEV.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "TCONDEV"
diff --git a/testsuite/build_farm/torture-TORTURE.test b/testsuite/build_farm/torture-TORTURE.test
new file mode 100644
index 00000000000..bc97e94e850
--- /dev/null
+++ b/testsuite/build_farm/torture-TORTURE.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "TORTURE"
diff --git a/testsuite/build_farm/torture-TRANS2.test b/testsuite/build_farm/torture-TRANS2.test
new file mode 100644
index 00000000000..d2a387f1afc
--- /dev/null
+++ b/testsuite/build_farm/torture-TRANS2.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "TRANS2"
diff --git a/testsuite/build_farm/torture-UNLINK.test b/testsuite/build_farm/torture-UNLINK.test
new file mode 100644
index 00000000000..b7086bbc838
--- /dev/null
+++ b/testsuite/build_farm/torture-UNLINK.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "UNLINK"
diff --git a/testsuite/build_farm/torture-XCOPY.test b/testsuite/build_farm/torture-XCOPY.test
new file mode 100644
index 00000000000..94cc7979769
--- /dev/null
+++ b/testsuite/build_farm/torture-XCOPY.test
@@ -0,0 +1,2 @@
+. torture_setup.fns
+test_torture "XCOPY"
diff --git a/testsuite/build_farm/torture_setup.fns b/testsuite/build_farm/torture_setup.fns
new file mode 100644
index 00000000000..a42be94129b
--- /dev/null
+++ b/testsuite/build_farm/torture_setup.fns
@@ -0,0 +1,19 @@
+. basicsmb.fns
+
+test_torture() {
+ torture_test=$1
+ password=samba
+ security=USER
+ (test_smb_conf_setup && test_smbpasswd $password ) || return 1
+
+ echo $srcdir/bin/smbtorture //localhost/test -U$whoami%$password $torture_test
+ $srcdir/bin/smbtorture //localhost/test -U$whoami%$password $torture_test
+ status=$?
+ if [ $status = 0 ]; then
+ echo "smbtorture test $torture_test worked"
+ else
+ echo "smbtorture test $torture_test FAILED (status $status)!"
+ return 1
+ fi
+ return 0
+}
diff --git a/testsuite/config/unix.exp b/testsuite/config/unix.exp
new file mode 100644
index 00000000000..c8b3b28779d
--- /dev/null
+++ b/testsuite/config/unix.exp
@@ -0,0 +1,27 @@
+# Copyright (C) 1988, 1990, 1991, 1992, 1994, 1997 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You 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.
+
+# Please email any bugs, comments, and/or additions to this file to:
+# bug-gdb@prep.ai.mit.edu
+
+# This file was written by Rob Savoye. (rob@cygnus.com)
+
+# Set a default timeout to be used for the tests under UNIX, rather than
+# accepting whatever default dejagnu gives us (apparently 10 seconds).
+# When running the tests over NFS, under somewhat heavy load, 10 seconds
+# does not seem to be enough. Try starting with 60.
+set timeout 60
+verbose "Timeout is now $timeout seconds" 2
diff --git a/testsuite/lib/compile.exp b/testsuite/lib/compile.exp
new file mode 100644
index 00000000000..070498d9589
--- /dev/null
+++ b/testsuite/lib/compile.exp
@@ -0,0 +1,79 @@
+#
+# Compilation utility functions
+#
+
+#
+# Unix SMB/Netbios implementation.
+# 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.
+#
+
+# Compile a program consisting of one .c file. For example
+# simple_compile "foo" will compile foo.c to the executable foo.exe
+# Use a second argument to specify link libraries.
+
+proc simple_compile { args } {
+ global srcdir
+ global subdir
+
+ # Compile up program
+
+ set program [lindex $args 0]
+ set libs [lindex $args 1]
+
+ if { $libs == "" } {
+
+ set output [target_compile "$srcdir/$subdir/$program.c" \
+ "$srcdir/$subdir/$program" executable \
+ {additional_flags="-g"}]
+ } else {
+
+ set output [target_compile "$srcdir/$subdir/$program.c" \
+ "$srcdir/$subdir/$program" executable \
+ [list libs=$libs additional_flags="-g"]]
+ }
+
+ # Check for errors
+
+ if {$output != ""} {
+ perror "compile $program"
+ puts $output
+ return -1
+ }
+}
+
+# Compile a program from a Makefile.suffix
+
+proc simple_make { args } {
+ global srcdir
+ global subdir
+
+ # Compile up program with make
+
+ set suffix [lindex $args 0]
+ set program [lindex $args 1]
+
+ set output [util_start "make" \
+ "-C $srcdir/$subdir -f Makefile.$suffix $program"]
+
+ # Check for errors
+
+ if { [regexp "Error" $output] } {
+ perror "make $program"
+ puts $output
+ return -1
+ }
+}
diff --git a/testsuite/lib/default-nt-names.exp b/testsuite/lib/default-nt-names.exp
new file mode 100644
index 00000000000..5d01d2a5bb3
--- /dev/null
+++ b/testsuite/lib/default-nt-names.exp
@@ -0,0 +1,20 @@
+#
+# A list of default domain/local users/groups. Unfortunately this is tied
+# to the English language version of Windows NT.
+#
+
+global domain
+
+# Domain users and groups
+
+set domain_users [list "$domain/Administrator" "$domain/Guest"]
+
+set domain_groups [list "$domain/Domain Admins" "$domain/Domain Guests" \
+ "$domain/Domain Users"]
+
+# Local groups
+
+set local_groups [list "BUILTIN/Replicator" "BUILTIN/Server Operators" \
+ "BUILTIN/Account Operators" "BUILTIN/Backup Operators" \
+ "BUILTIN/Print Operators" "BUILTIN/Guests" "BUILTIN/Users" \
+ "BUILTIN/Administrators"]
diff --git a/testsuite/lib/env-single.exp b/testsuite/lib/env-single.exp
new file mode 100644
index 00000000000..6cd7f94a869
--- /dev/null
+++ b/testsuite/lib/env-single.exp
@@ -0,0 +1,36 @@
+#
+# Environment variables for a single machine test. We look for the
+# following environment variables:
+#
+# $TEST_SERVER The SMB server to contact for the test
+# $TEST_SHARE Share name on $TEST_SERVER to contact
+# $TEST_USER The username to connect to $TEST_SHARE as
+#
+# These are stored in the Tcl variables $server, $share and $user
+# respectively.
+#
+# An error will be produced and the test will exit if any of these
+# variables are not present.
+#
+
+verbose "Loading single machine environment variables"
+
+catch {set server "$env(TEST_SERVER)"} tmp
+
+if {[regexp "^can't read" $tmp]} {
+ error "Environment variable TEST_SERVER not set"
+}
+
+catch {set share "$env(TEST_SHARE)"} tmp
+
+if {[regexp "^can't read" $tmp]} {
+ error "Environment variable TEST_SHARE not set"
+}
+
+catch {set user "$env(TEST_USER)"} tmp
+
+if {[regexp "^can't read" $tmp]} {
+ error "Environment variable TEST_USER not set"
+}
+
+verbose "Single machine is //$server/$share -U $user"
diff --git a/testsuite/lib/nsswitch-config.exp b/testsuite/lib/nsswitch-config.exp
new file mode 100644
index 00000000000..38342685dfa
--- /dev/null
+++ b/testsuite/lib/nsswitch-config.exp
@@ -0,0 +1,21 @@
+#
+# Load environment variables
+#
+
+global tool
+
+if { [file exists "deja-$tool.tcl"] } {
+ source "deja-$tool.tcl"
+}
+
+# Required options
+
+if { ![info exists WORKGROUP] } {
+ error "\$WORKGROUP not set in config file"
+}
+
+if { ![info exists PDC] } {
+ error "\$PDC not set in config file"
+}
+
+set domain $WORKGROUP
diff --git a/testsuite/lib/smbclient.exp b/testsuite/lib/smbclient.exp
new file mode 100644
index 00000000000..dc55d72139c
--- /dev/null
+++ b/testsuite/lib/smbclient.exp
@@ -0,0 +1,54 @@
+#
+# Utilities for driving smbclient
+#
+
+# Variables
+
+set smb_prompt "smb: \\\\>"
+
+# Spawn smbclient and wait for a prompt
+
+proc spawn_smbclient { args } {
+ set result 0
+ global smb_prompt
+ global spawn_id
+
+ # Spawn smbclient
+
+ spawn smbclient [lindex $args 0] [lindex $args 1] [lindex $args 2] \
+ [lindex $args 3] [lindex $args 4] [lindex $args 5] \
+ [lindex $args 6]
+
+ # Wait for prompt
+
+ expect {
+ $smb_prompt { set result 1 }
+ timeout { perror "timed out spawning smbclient" }
+ eof { perror "end of file spawning smbclient" }
+ }
+
+ return $result
+}
+
+# Run a command and wait for a prompt
+
+proc do_smbclient { args } {
+ set action [lindex $args 0]
+ set description [lindex $args 1]
+ global smb_prompt
+
+ # Send command
+
+ verbose $action
+
+ send $action
+
+ expect {
+ $smb_prompt {}
+ timeout { perror "timed out $description"; return -1}
+ eof { perror "end of file $description"; return -1 }
+ }
+
+ verbose $expect_out(buffer)
+ return $expect_out(buffer)
+}
diff --git a/testsuite/libsmbclient/src/.cvsignore b/testsuite/libsmbclient/src/.cvsignore
new file mode 100644
index 00000000000..ba077a4031a
--- /dev/null
+++ b/testsuite/libsmbclient/src/.cvsignore
@@ -0,0 +1 @@
+bin
diff --git a/testsuite/libsmbclient/src/Makefile b/testsuite/libsmbclient/src/Makefile
new file mode 100644
index 00000000000..a59f1342801
--- /dev/null
+++ b/testsuite/libsmbclient/src/Makefile
@@ -0,0 +1,817 @@
+CC = gcc
+CFLAGS = -Wall -W -O2 -g
+LFLAGS = -L/root/samba-head-old/source/bin
+
+LIBS= -L/usr/lib -lsmbclient
+INCPATH= -I. -I/usr/include -I./include
+BIN_DIR=bin
+
+SUB_DIRS=init fstat open unlink chown close opendir closedir rename lseek lseekdir stat \
+ getdents creat read readdir mkdir rmdir write chmod open_print_job list_print_jobs \
+ print_file telldir unlink_print_job
+
+
+G_INIT = $(BIN_DIR)/init_1 \
+ $(BIN_DIR)/init_2 \
+ $(BIN_DIR)/init_3 \
+ $(BIN_DIR)/init_4
+
+G_FSTAT = $(BIN_DIR)/fstat_1 \
+ $(BIN_DIR)/fstat_2 \
+ $(BIN_DIR)/fstat_3 \
+ $(BIN_DIR)/fstat_4 \
+ $(BIN_DIR)/fstat_5 \
+ $(BIN_DIR)/fstat_6
+
+G_OPEN = $(BIN_DIR)/open_1 \
+ $(BIN_DIR)/open_2 \
+ $(BIN_DIR)/open_3 \
+ $(BIN_DIR)/open_4 \
+ $(BIN_DIR)/open_5
+
+G_UNLINK = $(BIN_DIR)/unlink_1 \
+ $(BIN_DIR)/unlink_2 \
+ $(BIN_DIR)/unlink_3 \
+ $(BIN_DIR)/unlink_4 \
+ $(BIN_DIR)/unlink_5 \
+ $(BIN_DIR)/unlink_6 \
+ $(BIN_DIR)/unlink_7 \
+ $(BIN_DIR)/unlink_8 \
+ $(BIN_DIR)/unlink_9 \
+ $(BIN_DIR)/unlink_10 \
+ $(BIN_DIR)/unlink_11 \
+ $(BIN_DIR)/unlink_12
+
+
+G_CLOSE = $(BIN_DIR)/close_1 \
+ $(BIN_DIR)/close_2
+
+G_OPENDIR = $(BIN_DIR)/opendir_1 \
+ $(BIN_DIR)/opendir_2 \
+ $(BIN_DIR)/opendir_3 \
+ $(BIN_DIR)/opendir_4
+
+G_CLOSEDIR = $(BIN_DIR)/closedir_1 \
+ $(BIN_DIR)/closedir_2 \
+ $(BIN_DIR)/closedir_3 \
+ $(BIN_DIR)/closedir_4
+
+G_RENAME = $(BIN_DIR)/rename_1 \
+ $(BIN_DIR)/rename_2 \
+ $(BIN_DIR)/rename_3 \
+ $(BIN_DIR)/rename_4 \
+ $(BIN_DIR)/rename_5 \
+ $(BIN_DIR)/rename_6 \
+ $(BIN_DIR)/rename_7 \
+ $(BIN_DIR)/rename_8 \
+ $(BIN_DIR)/rename_9 \
+ $(BIN_DIR)/rename_10 \
+ $(BIN_DIR)/rename_11 \
+ $(BIN_DIR)/rename_12 \
+ $(BIN_DIR)/rename_13 \
+ $(BIN_DIR)/rename_14
+
+G_LSEEK = $(BIN_DIR)/lseek_1 \
+ $(BIN_DIR)/lseek_2 \
+ $(BIN_DIR)/lseek_3 \
+ $(BIN_DIR)/lseek_4 \
+ $(BIN_DIR)/lseek_5 \
+ $(BIN_DIR)/lseek_6 \
+ $(BIN_DIR)/lseek_7 \
+ $(BIN_DIR)/lseek_8
+
+G_LSEEKDIR = $(BIN_DIR)/lseekdir_1 \
+ $(BIN_DIR)/lseekdir_2 \
+ $(BIN_DIR)/lseekdir_3 \
+ $(BIN_DIR)/lseekdir_4 \
+ $(BIN_DIR)/lseekdir_5 \
+ $(BIN_DIR)/lseekdir_6
+
+G_STAT = $(BIN_DIR)/stat_1 \
+ $(BIN_DIR)/stat_2 \
+ $(BIN_DIR)/stat_3 \
+ $(BIN_DIR)/stat_4 \
+ $(BIN_DIR)/stat_5 \
+ $(BIN_DIR)/stat_6
+
+G_GETDENTS = $(BIN_DIR)/getdents_1 \
+ $(BIN_DIR)/getdents_2 \
+ $(BIN_DIR)/getdents_3 \
+ $(BIN_DIR)/getdents_4 \
+ $(BIN_DIR)/getdents_5
+
+G_CREAT = $(BIN_DIR)/creat_1 \
+ $(BIN_DIR)/creat_2 \
+ $(BIN_DIR)/creat_3
+
+G_READ = $(BIN_DIR)/read_1 \
+ $(BIN_DIR)/read_2 \
+ $(BIN_DIR)/read_3 \
+ $(BIN_DIR)/read_4 \
+ $(BIN_DIR)/read_5 \
+ $(BIN_DIR)/read_6 \
+ $(BIN_DIR)/read_7 \
+ $(BIN_DIR)/read_8 \
+ $(BIN_DIR)/read_9 \
+ $(BIN_DIR)/read_10 \
+ $(BIN_DIR)/read_11 \
+ $(BIN_DIR)/read_12 \
+ $(BIN_DIR)/read_13
+
+G_MKDIR = $(BIN_DIR)/mkdir_1 \
+ $(BIN_DIR)/mkdir_2 \
+ $(BIN_DIR)/mkdir_3 \
+ $(BIN_DIR)/mkdir_4
+
+G_RMDIR = $(BIN_DIR)/rmdir_1 \
+ $(BIN_DIR)/rmdir_2 \
+ $(BIN_DIR)/rmdir_3 \
+ $(BIN_DIR)/rmdir_4 \
+ $(BIN_DIR)/rmdir_5 \
+ $(BIN_DIR)/rmdir_6
+
+G_READDIR = $(BIN_DIR)/readdir_1 \
+ $(BIN_DIR)/readdir_2 \
+ $(BIN_DIR)/readdir_3 \
+ $(BIN_DIR)/readdir_4 \
+ $(BIN_DIR)/readdir_5
+
+G_WRITE = $(BIN_DIR)/write_1 \
+ $(BIN_DIR)/write_2 \
+ $(BIN_DIR)/write_3 \
+ $(BIN_DIR)/write_4 \
+ $(BIN_DIR)/write_5 \
+ $(BIN_DIR)/write_6 \
+ $(BIN_DIR)/write_7 \
+ $(BIN_DIR)/write_8 \
+ $(BIN_DIR)/write_9 \
+ $(BIN_DIR)/write_10 \
+ $(BIN_DIR)/write_11 \
+ $(BIN_DIR)/write_12 \
+ $(BIN_DIR)/write_13
+
+G_TELLDIR = $(BIN_DIR)/telldir_1 \
+ $(BIN_DIR)/telldir_2 \
+ $(BIN_DIR)/telldir_3 \
+ $(BIN_DIR)/telldir_4 \
+ $(BIN_DIR)/telldir_5
+
+G_CHMOD = $(BIN_DIR)/chmod_1
+
+G_CHOWN = $(BIN_DIR)/chown_1
+
+G_PRINT_FILE = $(BIN_DIR)/print_file_1 \
+ $(BIN_DIR)/print_file_2 \
+ $(BIN_DIR)/print_file_3 \
+ $(BIN_DIR)/print_file_4
+
+G_OPEN_PRINT_JOB = $(BIN_DIR)/open_print_job_1 \
+ $(BIN_DIR)/open_print_job_2
+
+G_LIST_PRINT_JOBS = $(BIN_DIR)/list_print_jobs_1 \
+ $(BIN_DIR)/list_print_jobs_2 \
+ $(BIN_DIR)/list_print_jobs_3 \
+ $(BIN_DIR)/list_print_jobs_4 \
+ $(BIN_DIR)/list_print_jobs_5 \
+ $(BIN_DIR)/list_print_jobs_6 \
+ $(BIN_DIR)/list_print_jobs_7
+
+G_UNLINK_PRINT_JOB = $(BIN_DIR)/unlink_print_job_1 \
+ $(BIN_DIR)/unlink_print_job_2 \
+ $(BIN_DIR)/unlink_print_job_3 \
+ $(BIN_DIR)/unlink_print_job_4 \
+ $(BIN_DIR)/unlink_print_job_5
+
+GROUPS= $(G_CHMOD) $(G_CHOWN) $(G_CLOSE) $(G_CLOSEDIR) $(G_CREAT) $(G_FSTAT)\
+ $(G_GETDENTS) $(G_INIT) $(G_LIST_PRINT_JOBS) $(G_LSEEK) $(G_LSEEKDIR) \
+ $(G_MKDIR) $(G_OPEN) $(G_OPENDIR) $(G_OPEN_PRINT_JOB) $(G_PRINT_FILE)\
+ $(G_READ) $(G_READDIR) $(G_RENAME) $(G_RMDIR) $(G_STAT) $(G_TELLDIR) $(G_UNLINK) \
+ $(G_UNLINK_PRINT_JOB) $(G_WRITE)
+
+.c.o:
+ @echo Compiling $*.c
+ @$(CC) -c $(CFLAGS) -o $@ $(INCPATH) $<
+
+
+all: $(GROUPS)
+
+init: $(G_INIT)
+fstat: $(G_FSTAT)
+open: $(G_OPEN)
+unlink: $(G_UNLINK)
+chown: $(G_CHOWN)
+close: $(G_CLOSE)
+opendir: $(G_OPENDIR)
+closedir: $(G_CLOSEDIR)
+rename: $(G_RENAME)
+readdir: $(G_READDIR)
+lseek: $(G_LSEEK)
+lseekdir: $(G_LSEEKDIR)
+stat: $(G_STAT)
+getdents: $(G_GETDENTS)
+creat: $(G_CREAT)
+read: $(G_READ)
+mkdir: $(G_MKDIR)
+rmdir: $(G_RMDIR)
+write: $(G_WRITE)
+chmod: $(G_CHMOD)
+telldir: $(G_TELLDIR)
+print_file: $(G_PRINT_FILE)
+open_print_job: $(G_OPEN_PRINT_JOB)
+list_print_jobs: $(G_LIST_PRINT_JOBS)
+unlink_print_job: $(G_UNLINK_PRINT_JOB)
+
+
+$(BIN_DIR)/testsmbc: testsmbc.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ testsmbc.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/init_1: init/init_1.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ init/init_1.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/init_2: init/init_2.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ init/init_2.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/init_3: init/init_3.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ init/init_3.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/init_4: init/init_4.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ init/init_4.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/fstat_1: fstat/fstat_1.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ fstat/fstat_1.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/fstat_2: fstat/fstat_2.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ fstat/fstat_2.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/fstat_3: fstat/fstat_3.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ fstat/fstat_3.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/fstat_4: fstat/fstat_4.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ fstat/fstat_4.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/fstat_5: fstat/fstat_5.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ fstat/fstat_5.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/fstat_6: fstat/fstat_6.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ fstat/fstat_6.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/open_1: open/open_1.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ open/open_1.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/open_2: open/open_2.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ open/open_2.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/open_3: open/open_3.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ open/open_3.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/open_4: open/open_4.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ open/open_4.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/open_5: open/open_5.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ open/open_5.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/unlink_1: unlink/unlink_1.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ unlink/unlink_1.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/unlink_2: unlink/unlink_2.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ unlink/unlink_2.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/unlink_3: unlink/unlink_3.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ unlink/unlink_3.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/unlink_4: unlink/unlink_4.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ unlink/unlink_4.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/unlink_5: unlink/unlink_5.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ unlink/unlink_5.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/unlink_6: unlink/unlink_6.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ unlink/unlink_6.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/unlink_7: unlink/unlink_7.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ unlink/unlink_7.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/unlink_8: unlink/unlink_8.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ unlink/unlink_8.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/unlink_9: unlink/unlink_9.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ unlink/unlink_9.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/unlink_10: unlink/unlink_10.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ unlink/unlink_10.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/unlink_11: unlink/unlink_11.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ unlink/unlink_11.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/unlink_12: unlink/unlink_12.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ unlink/unlink_12.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/chown_1: chown/chown_1.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ chown/chown_1.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/close_1: close/close_1.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ close/close_1.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/close_2: close/close_2.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ close/close_2.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/opendir_1: opendir/opendir_1.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ opendir/opendir_1.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/opendir_2: opendir/opendir_2.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ opendir/opendir_2.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/opendir_3: opendir/opendir_3.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ opendir/opendir_3.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/opendir_4: opendir/opendir_4.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ opendir/opendir_4.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/closedir_1: closedir/closedir_1.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ closedir/closedir_1.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/closedir_2: closedir/closedir_2.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ closedir/closedir_2.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/closedir_3: closedir/closedir_3.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ closedir/closedir_3.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/closedir_4: closedir/closedir_4.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ closedir/closedir_4.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/rename_1: rename/rename_1.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ rename/rename_1.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/rename_2: rename/rename_2.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ rename/rename_2.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/rename_3: rename/rename_3.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ rename/rename_3.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/rename_4: rename/rename_4.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ rename/rename_4.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/rename_5: rename/rename_5.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ rename/rename_5.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/rename_6: rename/rename_6.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ rename/rename_6.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/rename_7: rename/rename_7.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ rename/rename_7.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/rename_8: rename/rename_8.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ rename/rename_8.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/rename_9: rename/rename_9.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ rename/rename_9.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/rename_10: rename/rename_10.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ rename/rename_10.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/rename_11: rename/rename_11.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ rename/rename_11.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/rename_12: rename/rename_12.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ rename/rename_12.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/rename_13: rename/rename_13.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ rename/rename_13.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/rename_14: rename/rename_14.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ rename/rename_14.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/lseek_1: lseek/lseek_1.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ lseek/lseek_1.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/lseek_2: lseek/lseek_2.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ lseek/lseek_2.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/lseek_3: lseek/lseek_3.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ lseek/lseek_3.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/lseek_4: lseek/lseek_4.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ lseek/lseek_4.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/lseek_5: lseek/lseek_5.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ lseek/lseek_5.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/lseek_6: lseek/lseek_6.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ lseek/lseek_6.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/lseek_7: lseek/lseek_7.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ lseek/lseek_7.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/lseek_8: lseek/lseek_8.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ lseek/lseek_8.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/lseekdir_1: lseekdir/lseekdir_1.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ lseekdir/lseekdir_1.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/lseekdir_2: lseekdir/lseekdir_2.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ lseekdir/lseekdir_2.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/lseekdir_3: lseekdir/lseekdir_3.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ lseekdir/lseekdir_3.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/lseekdir_4: lseekdir/lseekdir_4.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ lseekdir/lseekdir_4.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/lseekdir_5: lseekdir/lseekdir_5.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ lseekdir/lseekdir_5.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/lseekdir_6: lseekdir/lseekdir_6.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ lseekdir/lseekdir_6.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/stat_1: stat/stat_1.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ stat/stat_1.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/stat_2: stat/stat_2.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ stat/stat_2.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/stat_3: stat/stat_3.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ stat/stat_3.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/stat_4: stat/stat_4.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ stat/stat_4.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/stat_5: stat/stat_5.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ stat/stat_5.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/stat_6: stat/stat_6.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ stat/stat_6.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/getdents_1: getdents/getdents_1.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ getdents/getdents_1.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/getdents_2: getdents/getdents_2.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ getdents/getdents_2.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/getdents_3: getdents/getdents_3.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ getdents/getdents_3.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/getdents_4: getdents/getdents_4.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ getdents/getdents_4.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/getdents_5: getdents/getdents_5.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ getdents/getdents_5.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/creat_1: creat/creat_1.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ creat/creat_1.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/creat_2: creat/creat_2.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ creat/creat_2.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/creat_3: creat/creat_3.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ creat/creat_3.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/mkdir_1: mkdir/mkdir_1.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ mkdir/mkdir_1.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/mkdir_2: mkdir/mkdir_2.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ mkdir/mkdir_2.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/mkdir_3: mkdir/mkdir_3.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ mkdir/mkdir_3.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/mkdir_4: mkdir/mkdir_4.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ mkdir/mkdir_4.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/readdir_1: readdir/readdir_1.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ readdir/readdir_1.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/readdir_2: readdir/readdir_2.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ readdir/readdir_2.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/readdir_3: readdir/readdir_3.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ readdir/readdir_3.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/readdir_4: readdir/readdir_4.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ readdir/readdir_4.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/readdir_5: readdir/readdir_5.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ readdir/readdir_5.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/rmdir_1: rmdir/rmdir_1.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ rmdir/rmdir_1.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/rmdir_2: rmdir/rmdir_2.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ rmdir/rmdir_2.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/rmdir_3: rmdir/rmdir_3.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ rmdir/rmdir_3.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/rmdir_4: rmdir/rmdir_4.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ rmdir/rmdir_4.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/rmdir_5: rmdir/rmdir_5.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ rmdir/rmdir_5.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/rmdir_6: rmdir/rmdir_6.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ rmdir/rmdir_6.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/write_1: write/write_1.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ write/write_1.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/write_2: write/write_2.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ write/write_2.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/write_3: write/write_3.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ write/write_3.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/write_4: write/write_4.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ write/write_4.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/write_5: write/write_5.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ write/write_5.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/write_6: write/write_6.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ write/write_6.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/write_7: write/write_7.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ write/write_7.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/write_8: write/write_8.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ write/write_8.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/write_9: write/write_9.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ write/write_9.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/write_10: write/write_10.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ write/write_10.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/write_11: write/write_11.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ write/write_11.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/write_12: write/write_12.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ write/write_12.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/write_13: write/write_13.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ write/write_13.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/read_1: read/read_1.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ read/read_1.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/read_2: read/read_2.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ read/read_2.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/read_3: read/read_3.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ read/read_3.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/read_4: read/read_4.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ read/read_4.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/read_5: read/read_5.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ read/read_5.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/read_6: read/read_6.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ read/read_6.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/read_7: read/read_7.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ read/read_7.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/read_8: read/read_8.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ read/read_8.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/read_9: read/read_9.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ read/read_9.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/read_10: read/read_10.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ read/read_10.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/read_11: read/read_11.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ read/read_11.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/read_12: read/read_12.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ read/read_12.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/read_13: read/read_13.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ read/read_13.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/chmod_1: chmod/chmod_1.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ chmod/chmod_1.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/telldir_1: telldir/telldir_1.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ telldir/telldir_1.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/telldir_2: telldir/telldir_2.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ telldir/telldir_2.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/telldir_3: telldir/telldir_3.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ telldir/telldir_3.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/telldir_4: telldir/telldir_4.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ telldir/telldir_4.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/telldir_5: telldir/telldir_5.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ telldir/telldir_5.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/print_file_1: print_file/print_file_1.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ print_file/print_file_1.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/print_file_2: print_file/print_file_2.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ print_file/print_file_2.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/print_file_3: print_file/print_file_3.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ print_file/print_file_3.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/print_file_4: print_file/print_file_4.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ print_file/print_file_4.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/open_print_job_1: open_print_job/open_print_job_1.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ open_print_job/open_print_job_1.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/open_print_job_2: open_print_job/open_print_job_2.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ open_print_job/open_print_job_2.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/list_print_jobs_1: list_print_jobs/list_print_jobs_1.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ list_print_jobs/list_print_jobs_1.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/list_print_jobs_2: list_print_jobs/list_print_jobs_2.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ list_print_jobs/list_print_jobs_2.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/list_print_jobs_3: list_print_jobs/list_print_jobs_3.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ list_print_jobs/list_print_jobs_3.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/list_print_jobs_4: list_print_jobs/list_print_jobs_4.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ list_print_jobs/list_print_jobs_4.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/list_print_jobs_5: list_print_jobs/list_print_jobs_5.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ list_print_jobs/list_print_jobs_5.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/list_print_jobs_6: list_print_jobs/list_print_jobs_6.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ list_print_jobs/list_print_jobs_6.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/list_print_jobs_7: list_print_jobs/list_print_jobs_7.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ list_print_jobs/list_print_jobs_7.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/unlink_print_job_1: unlink_print_job/unlink_print_job_1.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ unlink_print_job/unlink_print_job_1.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/unlink_print_job_2: unlink_print_job/unlink_print_job_2.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ unlink_print_job/unlink_print_job_2.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/unlink_print_job_3: unlink_print_job/unlink_print_job_3.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ unlink_print_job/unlink_print_job_3.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/unlink_print_job_4: unlink_print_job/unlink_print_job_4.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ unlink_print_job/unlink_print_job_4.o $(INCPATH) $(LIBS)
+
+$(BIN_DIR)/unlink_print_job_5: unlink_print_job/unlink_print_job_5.o
+ @echo Linking $@
+ @$(CC) $(LFLAGS) -o $@ unlink_print_job/unlink_print_job_5.o $(INCPATH) $(LIBS)
+
+
+clean:
+ @for i in $(SUB_DIRS); do \
+ rm -f $$i/*.o; \
+ done
+
+ @rm -f $(GROUPS)
+ @echo "Done"
+
diff --git a/testsuite/libsmbclient/src/chmod/chmod_1.c b/testsuite/libsmbclient/src/chmod/chmod_1.c
new file mode 100644
index 00000000000..47c86b1f809
--- /dev/null
+++ b/testsuite/libsmbclient/src/chmod/chmod_1.c
@@ -0,0 +1,59 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ fd = smbc_init(auth_fn, 0);
+
+ if ( fd < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/chown/chown_1.c b/testsuite/libsmbclient/src/chown/chown_1.c
new file mode 100644
index 00000000000..47c86b1f809
--- /dev/null
+++ b/testsuite/libsmbclient/src/chown/chown_1.c
@@ -0,0 +1,59 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ fd = smbc_init(auth_fn, 0);
+
+ if ( fd < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/close/close_1.c b/testsuite/libsmbclient/src/close/close_1.c
new file mode 100644
index 00000000000..983e627d897
--- /dev/null
+++ b/testsuite/libsmbclient/src/close/close_1.c
@@ -0,0 +1,59 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ fd = smbc_open(url, O_RDWR | O_CREAT, 0666);
+ err = smbc_close(fd);
+
+ if ( err < 0 )
+ err = 1;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/close/close_2.c b/testsuite/libsmbclient/src/close/close_2.c
new file mode 100644
index 00000000000..b0319e41d9d
--- /dev/null
+++ b/testsuite/libsmbclient/src/close/close_2.c
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ fd = smbc_open(url, O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ err = errno;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/closedir/closedir_1.c b/testsuite/libsmbclient/src/closedir/closedir_1.c
new file mode 100644
index 00000000000..702b92c24ab
--- /dev/null
+++ b/testsuite/libsmbclient/src/closedir/closedir_1.c
@@ -0,0 +1,65 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int dh = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ smbc_rmdir(url);
+ smbc_mkdir(url,0666);
+ dh = smbc_opendir(url);
+ /* printf("directory handle: %i\n",dh); */
+ err = smbc_closedir(dh);
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/closedir/closedir_2.c b/testsuite/libsmbclient/src/closedir/closedir_2.c
new file mode 100644
index 00000000000..88b6e2ce7e8
--- /dev/null
+++ b/testsuite/libsmbclient/src/closedir/closedir_2.c
@@ -0,0 +1,61 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int dh = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ smbc_rmdir(url);
+ smbc_mkdir(url,0666);
+ dh = smbc_opendir(url);
+ /* printf("directory handle: %i\n",dh); */
+ smbc_closedir(dh);
+
+ err = errno;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/closedir/closedir_3.c b/testsuite/libsmbclient/src/closedir/closedir_3.c
new file mode 100644
index 00000000000..4fdc9c849c5
--- /dev/null
+++ b/testsuite/libsmbclient/src/closedir/closedir_3.c
@@ -0,0 +1,63 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int dh = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ dh = smbc_opendir(url);
+ /* printf("directory handle: %i\n",dh); */
+ err = smbc_closedir(dh);
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/closedir/closedir_4.c b/testsuite/libsmbclient/src/closedir/closedir_4.c
new file mode 100644
index 00000000000..ee1d72bae39
--- /dev/null
+++ b/testsuite/libsmbclient/src/closedir/closedir_4.c
@@ -0,0 +1,59 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int dh = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ dh = smbc_opendir(url);
+ /* printf("directory handle: %i\n",dh); */
+ smbc_closedir(dh);
+
+ err = errno;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/creat/creat_1.c b/testsuite/libsmbclient/src/creat/creat_1.c
new file mode 100644
index 00000000000..9ccab002c13
--- /dev/null
+++ b/testsuite/libsmbclient/src/creat/creat_1.c
@@ -0,0 +1,60 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ fd = smbc_creat(url, 0666);
+
+ if ( fd < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/creat/creat_2.c b/testsuite/libsmbclient/src/creat/creat_2.c
new file mode 100644
index 00000000000..599a1845d34
--- /dev/null
+++ b/testsuite/libsmbclient/src/creat/creat_2.c
@@ -0,0 +1,63 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ fd = smbc_open(url, O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+ fd = 0;
+ fd = smbc_creat(url, 0666);
+
+ if ( fd < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/creat/creat_3.c b/testsuite/libsmbclient/src/creat/creat_3.c
new file mode 100644
index 00000000000..ae27a2a1c90
--- /dev/null
+++ b/testsuite/libsmbclient/src/creat/creat_3.c
@@ -0,0 +1,56 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ fd = smbc_creat(url, 0666);
+
+ err = errno;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/fstat/fstat_1.c b/testsuite/libsmbclient/src/fstat/fstat_1.c
new file mode 100644
index 00000000000..fd63400869d
--- /dev/null
+++ b/testsuite/libsmbclient/src/fstat/fstat_1.c
@@ -0,0 +1,62 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ struct stat st;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+
+ if ( argc == 4 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+
+ smbc_init(auth_fn, 0);
+
+ fd = 11234;
+ err = smbc_fstat(fd,&st);
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/fstat/fstat_2.c b/testsuite/libsmbclient/src/fstat/fstat_2.c
new file mode 100644
index 00000000000..ea2e7e3145f
--- /dev/null
+++ b/testsuite/libsmbclient/src/fstat/fstat_2.c
@@ -0,0 +1,58 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ struct stat st;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+
+ if ( argc == 4 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+
+ smbc_init(auth_fn, 0);
+
+ fd = 11234;
+ smbc_fstat(fd,&st);
+
+ err = errno;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/fstat/fstat_3.c b/testsuite/libsmbclient/src/fstat/fstat_3.c
new file mode 100644
index 00000000000..57bb3b7557d
--- /dev/null
+++ b/testsuite/libsmbclient/src/fstat/fstat_3.c
@@ -0,0 +1,69 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+ struct stat st;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ fd = smbc_open(url, O_RDWR, 0666);
+
+ err = smbc_fstat(fd, &st);
+ smbc_close(fd);
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/fstat/fstat_4.c b/testsuite/libsmbclient/src/fstat/fstat_4.c
new file mode 100644
index 00000000000..57bb3b7557d
--- /dev/null
+++ b/testsuite/libsmbclient/src/fstat/fstat_4.c
@@ -0,0 +1,69 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+ struct stat st;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ fd = smbc_open(url, O_RDWR, 0666);
+
+ err = smbc_fstat(fd, &st);
+ smbc_close(fd);
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/fstat/fstat_5.c b/testsuite/libsmbclient/src/fstat/fstat_5.c
new file mode 100644
index 00000000000..9ebdd602646
--- /dev/null
+++ b/testsuite/libsmbclient/src/fstat/fstat_5.c
@@ -0,0 +1,77 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+ struct stat st;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_write(fd, message, msg_len);
+
+ smbc_fstat(fd,&st);
+
+ smbc_close(fd);
+ free(message);
+
+ if ( st.st_size != msg_len )
+ err = 1;
+
+ else
+ err = 0;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/fstat/fstat_6.c b/testsuite/libsmbclient/src/fstat/fstat_6.c
new file mode 100644
index 00000000000..8a38e5be346
--- /dev/null
+++ b/testsuite/libsmbclient/src/fstat/fstat_6.c
@@ -0,0 +1,80 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ unsigned int mode_mask = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+ struct stat st;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_write(fd, message, msg_len);
+
+ smbc_fstat(fd,&st);
+
+ smbc_close(fd);
+ free(message);
+
+ mode_mask = mode_mask | S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; /* 0666 or 33206 */
+
+ if ( st.st_mode != mode_mask )
+ err = 1;
+
+ else
+ err = 0;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/getdents/getdents_1.c b/testsuite/libsmbclient/src/getdents/getdents_1.c
new file mode 100644
index 00000000000..6e253f78c5c
--- /dev/null
+++ b/testsuite/libsmbclient/src/getdents/getdents_1.c
@@ -0,0 +1,72 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int dh = 0;
+ /* int dirsize = 0; */
+ /* int dircount = 0; */
+
+ struct smbc_dirent *dirptr;
+
+ char url[MAX_BUFF_SIZE];
+ char dirbuff[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ dirptr = (struct smbc_dirent *) dirbuff;
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+
+ dh = smbc_opendir(url);
+ err = smbc_getdents( dh, dirptr, sizeof(dirbuff));
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/getdents/getdents_2.c b/testsuite/libsmbclient/src/getdents/getdents_2.c
new file mode 100644
index 00000000000..e7eb959a8f5
--- /dev/null
+++ b/testsuite/libsmbclient/src/getdents/getdents_2.c
@@ -0,0 +1,67 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int dh = 0;
+ /* int dirsize = 0; */
+ /* int dircount = 0; */
+
+ struct smbc_dirent *dirptr;
+
+ char url[MAX_BUFF_SIZE];
+ char dirbuff[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ dirptr = (struct smbc_dirent *) dirbuff;
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+
+ dh = smbc_opendir(url);
+ err = smbc_getdents( dh, dirptr, sizeof(dirbuff));
+ err = errno;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/getdents/getdents_3.c b/testsuite/libsmbclient/src/getdents/getdents_3.c
new file mode 100644
index 00000000000..58c8f709a96
--- /dev/null
+++ b/testsuite/libsmbclient/src/getdents/getdents_3.c
@@ -0,0 +1,155 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int dh = 0;
+ int entry_num = 0;
+ int i = 0;
+ int j = 0;
+ char *file_name;
+ char *tmp_file_ptr;
+ int direntsize = 0;
+ int diramount = 0;
+
+ struct smbc_dirent *dirptr;
+
+
+ char buff[MAX_BUFF_SIZE];
+ char url[MAX_BUFF_SIZE];
+ char file_url[MAX_BUFF_SIZE];
+ char dir_url[MAX_BUFF_SIZE];
+ char dirbuff[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+ bzero(file_url,MAX_BUFF_SIZE);
+ bzero(dir_url,MAX_BUFF_SIZE);
+ bzero(buff,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ dirptr = (struct smbc_dirent *) dirbuff;
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+ smbc_init(auth_fn, 0);
+
+ strncpy(file_url,"tempfile-",9);
+ tmp_file_ptr = file_url;
+ tmp_file_ptr += 9;
+
+ smbc_rmdir(url);
+ smbc_mkdir(url,0666);
+
+ entry_num = atoi(argv[5]);
+ strcat(dir_url,url);
+ strcat(dir_url,"/");
+
+ file_name = dir_url;
+ file_name += strlen(dir_url);
+
+ for ( i = 0; i < entry_num; i++ )
+ {
+ sprintf(buff,"%d",i);
+ memcpy(tmp_file_ptr,buff,strlen(buff)+4);
+ strncat(tmp_file_ptr,".txt",4);
+ strcpy(file_name,file_url);
+ fd = smbc_open(dir_url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ }
+
+ dh = smbc_opendir(url);
+ diramount = smbc_getdents( dh, dirptr, sizeof(dirbuff));
+
+ err = 0;
+ i = 0;
+ bzero(buff,MAX_BUFF_SIZE);
+ bzero(tmp_file_ptr,MAX_BUFF_SIZE-9);
+
+ while ( diramount > 0 )
+ {
+ direntsize = dirptr->dirlen;
+ /* printf("Name: %s\n",dirptr->name); */
+ if ( j == 0 )
+ {
+ if ( !(( strncmp(dirptr->name,".",1) == 0 )) )
+ {
+ break;
+ err = 1;
+ }
+
+ } else if ( j == 1 ) {
+
+ if ( !(( strncmp(dirptr->name,"..",2) == 0 )) )
+ {
+ break;
+ err = 1;
+ }
+
+ } else if ( j > 1 ) {
+
+ sprintf(buff,"%d",i);
+ memcpy(tmp_file_ptr,buff,strlen(buff)+4);
+ strncat(tmp_file_ptr,".txt",4);
+
+ if ( !((strcmp(dirptr->name,file_url) == 0 )) ) /* make sure entries match */
+ {
+ err = 1;
+ break;
+ }
+
+ i++;
+
+ }
+
+ (char *)dirptr += direntsize;
+ (char *)diramount -= direntsize;
+ j++;
+
+ }
+
+ if ( ! err )
+ {
+ if ( (j - 2) != entry_num ) /* Make sure that all entries created are counted and returned - minus . and .. */
+ err = 1;
+ }
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/getdents/getdents_4.c b/testsuite/libsmbclient/src/getdents/getdents_4.c
new file mode 100644
index 00000000000..4afd3539b8e
--- /dev/null
+++ b/testsuite/libsmbclient/src/getdents/getdents_4.c
@@ -0,0 +1,101 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int dh = 0;
+ int entry_num = 0;
+ int i = 0;
+ char *file_name;
+ char *tmp_file_ptr;
+ int diramount = 0;
+
+ struct smbc_dirent *dirptr;
+
+ char buff[MAX_BUFF_SIZE];
+ char url[MAX_BUFF_SIZE];
+ char file_url[MAX_BUFF_SIZE];
+ char dir_url[MAX_BUFF_SIZE];
+ char dirbuff[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+ bzero(file_url,MAX_BUFF_SIZE);
+ bzero(dir_url,MAX_BUFF_SIZE);
+ bzero(buff,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ dirptr = (struct smbc_dirent *) dirbuff;
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+ smbc_init(auth_fn, 0);
+
+ strncpy(file_url,"tempfile-",9);
+ tmp_file_ptr = file_url;
+ tmp_file_ptr += 9;
+
+ smbc_rmdir(url);
+ smbc_mkdir(url,0666);
+
+ entry_num = atoi(argv[5]);
+ strcat(dir_url,url);
+ strcat(dir_url,"/");
+
+ file_name = dir_url;
+ file_name += strlen(dir_url);
+
+ for ( i = 0; i < entry_num; i++ )
+ {
+ sprintf(buff,"%d",i);
+ memcpy(tmp_file_ptr,buff,strlen(buff)+4);
+ strncat(tmp_file_ptr,".txt",4);
+ strcpy(file_name,file_url);
+ fd = smbc_open(dir_url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ }
+
+ dh = smbc_opendir(url);
+ diramount = smbc_getdents( dh, dirptr, 20 /*sizeof(dirbuff)*/ );
+
+ err = errno;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/getdents/getdents_5.c b/testsuite/libsmbclient/src/getdents/getdents_5.c
new file mode 100644
index 00000000000..5cf7c1b9282
--- /dev/null
+++ b/testsuite/libsmbclient/src/getdents/getdents_5.c
@@ -0,0 +1,106 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int dh = 0;
+ int entry_num = 0;
+ int i = 0;
+ char *file_name;
+ char *tmp_file_ptr;
+ int diramount = 0;
+
+ struct smbc_dirent *dirptr;
+
+ char buff[MAX_BUFF_SIZE];
+ char url[MAX_BUFF_SIZE];
+ char file_url[MAX_BUFF_SIZE];
+ char dir_url[MAX_BUFF_SIZE];
+ char dirbuff[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+ bzero(file_url,MAX_BUFF_SIZE);
+ bzero(dir_url,MAX_BUFF_SIZE);
+ bzero(buff,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ dirptr = (struct smbc_dirent *) dirbuff;
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+ smbc_init(auth_fn, 0);
+
+ strncpy(file_url,"tempfile-",9);
+ tmp_file_ptr = file_url;
+ tmp_file_ptr += 9;
+
+ smbc_rmdir(url);
+ smbc_mkdir(url,0666);
+
+ entry_num = atoi(argv[5]);
+ strcat(dir_url,url);
+ strcat(dir_url,"/");
+
+ file_name = dir_url;
+ file_name += strlen(dir_url);
+
+ for ( i = 0; i < entry_num; i++ )
+ {
+ sprintf(buff,"%d",i);
+ memcpy(tmp_file_ptr,buff,strlen(buff)+4);
+ strncat(tmp_file_ptr,".txt",4);
+ strcpy(file_name,file_url);
+ fd = smbc_open(dir_url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ }
+
+ dh = smbc_opendir(url);
+ diramount = smbc_getdents( dh, dirptr, 20 /* sizeof(dirbuff)*/);
+
+ if ( diramount < 0 )
+
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/init/init_1.c b/testsuite/libsmbclient/src/init/init_1.c
new file mode 100644
index 00000000000..bc928ac3d5c
--- /dev/null
+++ b/testsuite/libsmbclient/src/init/init_1.c
@@ -0,0 +1,18 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+
+int main()
+{
+ int err = -1;
+
+ err = smbc_init(NULL, 0);
+
+ if ( err < 0 )
+ err = 1;
+
+ return err;
+
+}
diff --git a/testsuite/libsmbclient/src/init/init_2.c b/testsuite/libsmbclient/src/init/init_2.c
new file mode 100644
index 00000000000..120160297b6
--- /dev/null
+++ b/testsuite/libsmbclient/src/init/init_2.c
@@ -0,0 +1,23 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+
+int main(int argc, char **argv )
+{
+ int err = -1;
+
+ if ( argc > 1 )
+ {
+ err = smbc_init(NULL, atoi(argv[1]));
+
+ if ( err < 0 )
+ err = 1;
+
+ }
+
+ return err;
+
+}
diff --git a/testsuite/libsmbclient/src/init/init_3.c b/testsuite/libsmbclient/src/init/init_3.c
new file mode 100644
index 00000000000..f49ed982e18
--- /dev/null
+++ b/testsuite/libsmbclient/src/init/init_3.c
@@ -0,0 +1,58 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+
+ err = smbc_init(auth_fn, atoi(argv[4]));
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/init/init_4.c b/testsuite/libsmbclient/src/init/init_4.c
new file mode 100644
index 00000000000..d8e44b50eed
--- /dev/null
+++ b/testsuite/libsmbclient/src/init/init_4.c
@@ -0,0 +1,18 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+
+int main( )
+{
+ int err = -1;
+
+ err = smbc_init(NULL, 0);
+
+ err = errno;
+
+ return err;
+
+}
diff --git a/testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_1.c b/testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_1.c
new file mode 100644
index 00000000000..714f08fd723
--- /dev/null
+++ b/testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_1.c
@@ -0,0 +1,109 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+char g_print_user[MAX_BUFF_SIZE];
+char g_print_name[MAX_BUFF_SIZE];
+unsigned int g_print_id;
+unsigned int g_print_priority;
+unsigned int g_print_size;
+
+int call_back_flag;
+int print_queue_empty;
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+void print_list_fn_2(struct print_job_info *pji)
+{
+ print_queue_empty = 0;
+ g_print_id = pji->id;
+}
+
+void print_list_fn(struct print_job_info *pji)
+{
+
+ call_back_flag = 1;
+
+ g_print_id = pji->id;
+ g_print_priority = pji->priority;
+ g_print_size = pji->size;
+ strcpy(g_print_user,pji->user);
+ strcpy(g_print_name,pji->name);
+
+ /* fprintf(stdout, "Print job: ID: %u, Prio: %u, Size: %u, User: %s, Name: %s\n",
+ pji->id, pji->priority, pji->size, pji->user, pji->name); */
+
+}
+
+int main(int argc, char** argv)
+{
+
+ int err = -1;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+ bzero(g_print_user,MAX_BUFF_SIZE);
+ bzero(g_print_name,MAX_BUFF_SIZE);
+
+ g_print_id = 0;
+ g_print_priority = 0;
+ g_print_size = 0;
+ call_back_flag = 0;
+ print_queue_empty = 0;
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+
+ while ( ! print_queue_empty ) /* Wait until the queue is empty */
+ {
+ sleep(1);
+ print_queue_empty = 1;
+ smbc_list_print_jobs(url,print_list_fn_2);
+ }
+
+ smbc_list_print_jobs(url,print_list_fn);
+
+ if ( call_back_flag )
+
+ err = 0;
+
+ else
+ err = 1;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_2.c b/testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_2.c
new file mode 100644
index 00000000000..6d6874716cc
--- /dev/null
+++ b/testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_2.c
@@ -0,0 +1,105 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+char g_print_user[MAX_BUFF_SIZE];
+char g_print_name[MAX_BUFF_SIZE];
+unsigned int g_print_id;
+unsigned int g_print_priority;
+unsigned int g_print_size;
+
+int print_queue_empty;
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+void print_list_fn_2(struct print_job_info *pji)
+{
+ print_queue_empty = 0;
+ g_print_id = pji->id;
+}
+
+void print_list_fn(struct print_job_info *pji)
+{
+
+ g_print_id = pji->id;
+ g_print_priority = pji->priority;
+ g_print_size = pji->size;
+ strcpy(g_print_user,pji->user);
+ strcpy(g_print_name,pji->name);
+
+ /* fprintf(stdout, "Print job: ID: %u, Prio: %u, Size: %u, User: %s, Name: %s\n",
+ pji->id, pji->priority, pji->size, pji->user, pji->name); */
+
+}
+
+int main(int argc, char** argv)
+{
+
+ int err = -1;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+ bzero(g_print_user,MAX_BUFF_SIZE);
+ bzero(g_print_name,MAX_BUFF_SIZE);
+
+ g_print_id = 0;
+ g_print_priority = 0;
+ g_print_size = 0;
+
+ print_queue_empty = 0;
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+
+ while ( ! print_queue_empty ) /* Wait until the queue is empty */
+ {
+ sleep(1);
+ print_queue_empty = 1;
+ smbc_list_print_jobs(url,print_list_fn_2);
+ }
+
+ err = smbc_list_print_jobs(url,print_list_fn);
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_3.c b/testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_3.c
new file mode 100644
index 00000000000..1cb52d08941
--- /dev/null
+++ b/testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_3.c
@@ -0,0 +1,103 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+char g_print_user[MAX_BUFF_SIZE];
+char g_print_name[MAX_BUFF_SIZE];
+unsigned int g_print_id;
+unsigned int g_print_priority;
+unsigned int g_print_size;
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+void print_list_fn(struct print_job_info *pji)
+{
+
+ g_print_id = pji->id;
+ g_print_priority = pji->priority;
+ g_print_size = pji->size;
+ strcpy(g_print_user,pji->user);
+ strcpy(g_print_name,pji->name);
+
+ /* fprintf(stdout, "Print job: ID: %u, Prio: %u, Size: %u, User: %s, Name: %s\n",
+ pji->id, pji->priority, pji->size, pji->user, pji->name); */
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+ bzero(g_print_user,MAX_BUFF_SIZE);
+ bzero(g_print_name,MAX_BUFF_SIZE);
+
+ g_print_id = 0;
+ g_print_priority = 0;
+ g_print_size = 0;
+
+ if ( argc == 7 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+ /* printf("Message: %s\n",message); */
+ /* printf("Message len: %i\n",msg_len); */
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ free(message);
+ smbc_print_file(url,argv[6]);
+ err = smbc_list_print_jobs(argv[6],print_list_fn);
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_4.c b/testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_4.c
new file mode 100644
index 00000000000..ad207724b46
--- /dev/null
+++ b/testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_4.c
@@ -0,0 +1,99 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+char g_print_user[MAX_BUFF_SIZE];
+char g_print_name[MAX_BUFF_SIZE];
+unsigned int g_print_id;
+unsigned int g_print_priority;
+unsigned int g_print_size;
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+void print_list_fn(struct print_job_info *pji)
+{
+
+ g_print_id = pji->id;
+ g_print_priority = pji->priority;
+ g_print_size = pji->size;
+ strcpy(g_print_user,pji->user);
+ strcpy(g_print_name,pji->name);
+
+ /* fprintf(stdout, "Print job: ID: %u, Prio: %u, Size: %u, User: %s, Name: %s\n",
+ pji->id, pji->priority, pji->size, pji->user, pji->name); */
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+ bzero(g_print_user,MAX_BUFF_SIZE);
+ bzero(g_print_name,MAX_BUFF_SIZE);
+
+ g_print_id = 0;
+ g_print_priority = 0;
+ g_print_size = 0;
+
+ if ( argc == 7 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+ /* printf("Message: %s\n",message); */
+ /* printf("Message len: %i\n",msg_len); */
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ free(message);
+ smbc_print_file(url,argv[6]);
+ err = smbc_list_print_jobs(argv[6],print_list_fn);
+
+ err = errno;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_5.c b/testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_5.c
new file mode 100644
index 00000000000..874bc896d06
--- /dev/null
+++ b/testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_5.c
@@ -0,0 +1,101 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+char g_print_user[MAX_BUFF_SIZE];
+char g_print_name[MAX_BUFF_SIZE];
+unsigned int g_print_id;
+unsigned int g_print_priority;
+unsigned int g_print_size;
+
+int print_queue_empty;
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+void print_list_fn_2(struct print_job_info *pji)
+{
+ print_queue_empty = 0;
+ g_print_id = pji->id;
+}
+
+void print_list_fn(struct print_job_info *pji)
+{
+
+ g_print_id = pji->id;
+ g_print_priority = pji->priority;
+ g_print_size = pji->size;
+ strcpy(g_print_user,pji->user);
+ strcpy(g_print_name,pji->name);
+
+ /* fprintf(stdout, "Print job: ID: %u, Prio: %u, Size: %u, User: %s, Name: %s\n",
+ pji->id, pji->priority, pji->size, pji->user, pji->name); */
+
+}
+
+int main(int argc, char** argv)
+{
+
+ int err = -1;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+ bzero(g_print_user,MAX_BUFF_SIZE);
+ bzero(g_print_name,MAX_BUFF_SIZE);
+
+ g_print_id = 0;
+ g_print_priority = 0;
+ g_print_size = 0;
+
+ print_queue_empty = 0;
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+
+ while ( ! print_queue_empty ) /* Wait until the queue is empty */
+ {
+ sleep(1);
+ print_queue_empty = 1;
+ smbc_list_print_jobs(url,print_list_fn_2);
+ }
+
+ smbc_list_print_jobs(url,print_list_fn);
+
+ err = errno;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_6.c b/testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_6.c
new file mode 100644
index 00000000000..7b691ae75ac
--- /dev/null
+++ b/testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_6.c
@@ -0,0 +1,110 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+char g_print_user[MAX_BUFF_SIZE];
+char g_print_name[MAX_BUFF_SIZE];
+unsigned int g_print_id;
+unsigned int g_print_priority;
+unsigned int g_print_size;
+
+int print_fn_call_flag;
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+void print_list_fn(struct print_job_info *pji)
+{
+
+ g_print_id = pji->id;
+ g_print_priority = pji->priority;
+ g_print_size = pji->size;
+ strcpy(g_print_user,pji->user);
+ strcpy(g_print_name,pji->name);
+
+ /* fprintf(stdout, "Print job: ID: %u, Prio: %u, Size: %u, User: %s, Name: %s\n",
+ pji->id, pji->priority, pji->size, pji->user, pji->name); */
+
+
+ print_fn_call_flag = 1;
+
+}
+
+int main(int argc, char** argv)
+{
+
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char * message;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+ bzero(g_print_user,MAX_BUFF_SIZE);
+ bzero(g_print_name,MAX_BUFF_SIZE);
+
+ g_print_id = 0;
+ g_print_priority = 0;
+ g_print_size = 0;
+
+ print_fn_call_flag = 0;
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+ /* printf("Message: %s\n",message); */
+ /* printf("Message len: %i\n",msg_len); */
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ free(message);
+ smbc_print_file(url,argv[6]);
+ smbc_list_print_jobs(url,print_list_fn);
+
+ if ( print_fn_call_flag == 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_7.c b/testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_7.c
new file mode 100644
index 00000000000..d25d20721b1
--- /dev/null
+++ b/testsuite/libsmbclient/src/list_print_jobs/list_print_jobs_7.c
@@ -0,0 +1,106 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+char g_print_user[MAX_BUFF_SIZE];
+char g_print_name[MAX_BUFF_SIZE];
+unsigned int g_print_id;
+unsigned int g_print_priority;
+unsigned int g_print_size;
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+void print_list_fn(struct print_job_info *pji)
+{
+
+ g_print_id = pji->id;
+ g_print_priority = pji->priority;
+ g_print_size = pji->size;
+ strcpy(g_print_user,pji->user);
+ strcpy(g_print_name,pji->name);
+
+ /* fprintf(stdout, "Print job: ID: %u, Prio: %u, Size: %u, User: %s, Name: %s\n",
+ pji->id, pji->priority, pji->size, pji->user, pji->name); */
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+ bzero(g_print_user,MAX_BUFF_SIZE);
+ bzero(g_print_name,MAX_BUFF_SIZE);
+
+ g_print_id = 0;
+ g_print_priority = 0;
+ g_print_size = 0;
+
+ if ( argc == 7 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+ /* printf("Message: %s\n",message); */
+ /* printf("Message len: %i\n",msg_len); */
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ free(message);
+
+ smbc_print_file(url,argv[6]);
+ smbc_list_print_jobs(argv[6],print_list_fn);
+
+
+ if (( (g_print_size > 0) && (g_print_size > 0) && (strcasecmp(g_username,g_print_user)==0) ))
+ /* && (strlen(g_print_name) > 0) )) */
+ err = 0;
+
+ else
+ err = 1;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/lseek/lseek_1.c b/testsuite/libsmbclient/src/lseek/lseek_1.c
new file mode 100644
index 00000000000..c358565d31c
--- /dev/null
+++ b/testsuite/libsmbclient/src/lseek/lseek_1.c
@@ -0,0 +1,61 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+
+ if ( argc == 4 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+
+ smbc_init(auth_fn, 0);
+
+ fd = -1;
+ err = smbc_lseek(fd, 0, SEEK_SET);
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/lseek/lseek_2.c b/testsuite/libsmbclient/src/lseek/lseek_2.c
new file mode 100644
index 00000000000..8b58ade0836
--- /dev/null
+++ b/testsuite/libsmbclient/src/lseek/lseek_2.c
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+
+ if ( argc == 4 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+
+ smbc_init(auth_fn, 0);
+
+ fd = -1;
+ smbc_lseek(fd, 0, SEEK_SET);
+
+ err = errno;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/lseek/lseek_3.c b/testsuite/libsmbclient/src/lseek/lseek_3.c
new file mode 100644
index 00000000000..c8a62e682b2
--- /dev/null
+++ b/testsuite/libsmbclient/src/lseek/lseek_3.c
@@ -0,0 +1,80 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ fd = smbc_open(url, O_RDWR, 0666);
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ fd = smbc_open(url, O_RDWR, 0666);
+ err = smbc_lseek(fd,msg_len,SEEK_SET);
+ smbc_close(fd);
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+ free(message);
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/lseek/lseek_4.c b/testsuite/libsmbclient/src/lseek/lseek_4.c
new file mode 100644
index 00000000000..c8a62e682b2
--- /dev/null
+++ b/testsuite/libsmbclient/src/lseek/lseek_4.c
@@ -0,0 +1,80 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ fd = smbc_open(url, O_RDWR, 0666);
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ fd = smbc_open(url, O_RDWR, 0666);
+ err = smbc_lseek(fd,msg_len,SEEK_SET);
+ smbc_close(fd);
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+ free(message);
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/lseek/lseek_5.c b/testsuite/libsmbclient/src/lseek/lseek_5.c
new file mode 100644
index 00000000000..2e40c96410c
--- /dev/null
+++ b/testsuite/libsmbclient/src/lseek/lseek_5.c
@@ -0,0 +1,80 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ fd = smbc_open(url, O_RDWR, 0666);
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ fd = smbc_open(url, O_RDWR, 0666);
+ err = smbc_lseek(fd,msg_len,SEEK_SET);
+ smbc_close(fd);
+
+ if ( err != msg_len )
+ err = 1;
+
+ else
+ err = 0;
+
+ free(message);
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/lseek/lseek_6.c b/testsuite/libsmbclient/src/lseek/lseek_6.c
new file mode 100644
index 00000000000..b5236b8537e
--- /dev/null
+++ b/testsuite/libsmbclient/src/lseek/lseek_6.c
@@ -0,0 +1,75 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ fd = smbc_open(url, O_RDWR, 0666);
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ fd = smbc_open(url, O_RDWR, 0666);
+ err = smbc_lseek(fd,msg_len,SEEK_SET);
+ err = errno;
+ smbc_close(fd);
+
+ free(message);
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/lseek/lseek_7.c b/testsuite/libsmbclient/src/lseek/lseek_7.c
new file mode 100644
index 00000000000..e3ef6cdd544
--- /dev/null
+++ b/testsuite/libsmbclient/src/lseek/lseek_7.c
@@ -0,0 +1,80 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ fd = smbc_open(url, O_RDWR, 0666);
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ fd = smbc_open(url, O_RDWR, 0666);
+ err = smbc_lseek(fd,0,SEEK_END);
+ smbc_close(fd);
+
+ if ( err != msg_len )
+ err = 1;
+
+ else
+ err = 0;
+
+ free(message);
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/lseek/lseek_8.c b/testsuite/libsmbclient/src/lseek/lseek_8.c
new file mode 100644
index 00000000000..e70f3ca6b55
--- /dev/null
+++ b/testsuite/libsmbclient/src/lseek/lseek_8.c
@@ -0,0 +1,75 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ fd = smbc_open(url, O_RDWR, 0666);
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ fd = smbc_open(url, O_RDWR, 0666);
+ err = smbc_lseek(fd,0,SEEK_END);
+ err = errno;
+ smbc_close(fd);
+
+ free(message);
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/lseekdir/lseekdir_1.c b/testsuite/libsmbclient/src/lseekdir/lseekdir_1.c
new file mode 100644
index 00000000000..010ca382a59
--- /dev/null
+++ b/testsuite/libsmbclient/src/lseekdir/lseekdir_1.c
@@ -0,0 +1,102 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int dh = 0;
+ int entry_num = 0;
+ int i = 0;
+ char *file_name;
+ char *tmp_file_ptr;
+
+ char buff[MAX_BUFF_SIZE];
+ char url[MAX_BUFF_SIZE];
+ char file_url[MAX_BUFF_SIZE];
+ char dir_url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+ bzero(file_url,MAX_BUFF_SIZE);
+ bzero(dir_url,MAX_BUFF_SIZE);
+ bzero(buff,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+ smbc_init(auth_fn, 0);
+
+ strncpy(file_url,"tempfile-",9);
+ tmp_file_ptr = file_url;
+ tmp_file_ptr += 9;
+
+ smbc_rmdir(url);
+ smbc_mkdir(url,0666);
+
+ entry_num = atoi(argv[5]);
+ strcat(dir_url,url);
+ strcat(dir_url,"/");
+
+ file_name = dir_url;
+ file_name += strlen(dir_url);
+
+ for ( i = 0; i < entry_num; i++ )
+ {
+ sprintf(buff,"%d",i);
+ memcpy(tmp_file_ptr,buff,strlen(buff)+4);
+ strncat(tmp_file_ptr,".txt",4);
+ strcpy(file_name,file_url);
+ fd = smbc_open(dir_url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ }
+
+ dh = smbc_opendir(url);
+ /* printf("directory handle: %i\n",dh); */
+ err = smbc_lseekdir(dh,0);
+ /* printf("err: %i\n",err); */
+
+ if ( err < 0 )
+
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/lseekdir/lseekdir_2.c b/testsuite/libsmbclient/src/lseekdir/lseekdir_2.c
new file mode 100644
index 00000000000..cb729d8642e
--- /dev/null
+++ b/testsuite/libsmbclient/src/lseekdir/lseekdir_2.c
@@ -0,0 +1,95 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int dh = 0;
+ int entry_num = 0;
+ int i = 0;
+ char *file_name;
+ char *tmp_file_ptr;
+
+ char buff[MAX_BUFF_SIZE];
+ char url[MAX_BUFF_SIZE];
+ char file_url[MAX_BUFF_SIZE];
+ char dir_url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+ bzero(file_url,MAX_BUFF_SIZE);
+ bzero(dir_url,MAX_BUFF_SIZE);
+ bzero(buff,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+ smbc_init(auth_fn, 0);
+
+ strncpy(file_url,"tempfile-",9);
+ tmp_file_ptr = file_url;
+ tmp_file_ptr += 9;
+
+ smbc_rmdir(url);
+ smbc_mkdir(url,0666);
+
+ entry_num = atoi(argv[5]);
+ strcat(dir_url,url);
+ strcat(dir_url,"/");
+
+ file_name = dir_url;
+ file_name += strlen(dir_url);
+
+ for ( i = 0; i < entry_num; i++ )
+ {
+ sprintf(buff,"%d",i);
+ memcpy(tmp_file_ptr,buff,strlen(buff)+4);
+ strncat(tmp_file_ptr,".txt",4);
+ strcpy(file_name,file_url);
+ fd = smbc_open(dir_url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ }
+
+ dh = smbc_opendir(url);
+ smbc_lseekdir(dh,0);
+
+ err = errno;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/lseekdir/lseekdir_3.c b/testsuite/libsmbclient/src/lseekdir/lseekdir_3.c
new file mode 100644
index 00000000000..ede9cef0815
--- /dev/null
+++ b/testsuite/libsmbclient/src/lseekdir/lseekdir_3.c
@@ -0,0 +1,67 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int dh = 0;
+
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+
+ dh = smbc_opendir(url);
+ /* printf("directory handle: %i\n",dh); */
+ err = smbc_lseekdir(dh,0);
+ /* printf("err: %i\n",err); */
+
+ if ( err < 0 )
+
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/lseekdir/lseekdir_4.c b/testsuite/libsmbclient/src/lseekdir/lseekdir_4.c
new file mode 100644
index 00000000000..1bc9a7e45d0
--- /dev/null
+++ b/testsuite/libsmbclient/src/lseekdir/lseekdir_4.c
@@ -0,0 +1,61 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int dh = 0;
+
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+
+ dh = smbc_opendir(url);
+ /* printf("directory handle: %i\n",dh); */
+ smbc_lseekdir(dh,0);
+ err = errno;
+ /* printf("err: %i\n",err); */
+
+
+ }
+
+ return err;
+
+}
diff --git a/testsuite/libsmbclient/src/lseekdir/lseekdir_5.c b/testsuite/libsmbclient/src/lseekdir/lseekdir_5.c
new file mode 100644
index 00000000000..08af806f5fe
--- /dev/null
+++ b/testsuite/libsmbclient/src/lseekdir/lseekdir_5.c
@@ -0,0 +1,119 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int dh = 0;
+ int entry_num = 0;
+ int i = 0;
+ int dirsize = 0;
+ char *file_name;
+ char *tmp_file_ptr;
+
+ struct smbc_dirent *dirptr;
+
+
+ char buff[MAX_BUFF_SIZE];
+ char url[MAX_BUFF_SIZE];
+ char file_url[MAX_BUFF_SIZE];
+ char dir_url[MAX_BUFF_SIZE];
+ char dirbuff[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+ bzero(file_url,MAX_BUFF_SIZE);
+ bzero(dir_url,MAX_BUFF_SIZE);
+ bzero(buff,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ dirptr = (struct smbc_dirent *) dirbuff;
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+ smbc_init(auth_fn, 0);
+
+ strncpy(file_url,"tempfile-",9);
+ tmp_file_ptr = file_url;
+ tmp_file_ptr += 9;
+
+ smbc_rmdir(url);
+ smbc_mkdir(url,0666);
+
+ entry_num = atoi(argv[5]);
+ strcat(dir_url,url);
+ strcat(dir_url,"/");
+
+ file_name = dir_url;
+ file_name += strlen(dir_url);
+
+ for ( i = 0; i < entry_num; i++ )
+ {
+ sprintf(buff,"%d",i);
+ memcpy(tmp_file_ptr,buff,strlen(buff)+4);
+ strncat(tmp_file_ptr,".txt",4);
+ strcpy(file_name,file_url);
+ fd = smbc_open(dir_url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ }
+
+ dh = smbc_opendir(url);
+ err = 0;
+
+ while ( 1 )
+ {
+ dirptr = smbc_readdir(dh);
+
+ if ( dirptr == NULL )
+ {
+ break;
+ }
+
+ (char*)dirsize += dirptr->dirlen;
+
+ }
+
+ smbc_lseekdir(dh,0);
+ err = smbc_telldir(dh);
+
+ if ( err != 0 )
+ err = 1;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/lseekdir/lseekdir_6.c b/testsuite/libsmbclient/src/lseekdir/lseekdir_6.c
new file mode 100644
index 00000000000..543cbafe140
--- /dev/null
+++ b/testsuite/libsmbclient/src/lseekdir/lseekdir_6.c
@@ -0,0 +1,124 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int dh = 0;
+ int entry_num = 0;
+ int i = 0;
+ int offset = 0;
+ int dirsize = 0;
+ char *file_name;
+ char *tmp_file_ptr;
+
+ struct smbc_dirent *dirptr;
+
+
+ char buff[MAX_BUFF_SIZE];
+ char url[MAX_BUFF_SIZE];
+ char file_url[MAX_BUFF_SIZE];
+ char dir_url[MAX_BUFF_SIZE];
+ char dirbuff[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+ bzero(file_url,MAX_BUFF_SIZE);
+ bzero(dir_url,MAX_BUFF_SIZE);
+ bzero(buff,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ dirptr = (struct smbc_dirent *) dirbuff;
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+ smbc_init(auth_fn, 0);
+
+ strncpy(file_url,"tempfile-",9);
+ tmp_file_ptr = file_url;
+ tmp_file_ptr += 9;
+
+ smbc_rmdir(url);
+ smbc_mkdir(url,0666);
+
+ entry_num = atoi(argv[5]);
+ strcat(dir_url,url);
+ strcat(dir_url,"/");
+
+ file_name = dir_url;
+ file_name += strlen(dir_url);
+
+ for ( i = 0; i < entry_num; i++ )
+ {
+ sprintf(buff,"%d",i);
+ memcpy(tmp_file_ptr,buff,strlen(buff)+4);
+ strncat(tmp_file_ptr,".txt",4);
+ strcpy(file_name,file_url);
+ fd = smbc_open(dir_url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ }
+
+ dh = smbc_opendir(url);
+ err = 0;
+
+ while ( 1 )
+ {
+ dirptr = smbc_readdir(dh);
+
+ if ( dirptr == NULL )
+ {
+ break;
+ }
+
+ (char*)dirsize += dirptr->dirlen;
+
+ }
+
+ smbc_lseekdir(dh,0); /* move to front */
+ smbc_lseekdir(dh,dirsize); /* move to end */
+ offset = smbc_telldir(dh);
+
+ if ( offset != dirsize )
+ {
+ /* printf("offset: %i dirsize: %i\n",offset,dirsize); */
+ err = 1;
+ }
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/mkdir/mkdir_1.c b/testsuite/libsmbclient/src/mkdir/mkdir_1.c
new file mode 100644
index 00000000000..20389bcb9b6
--- /dev/null
+++ b/testsuite/libsmbclient/src/mkdir/mkdir_1.c
@@ -0,0 +1,60 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+ strncpy( g_workgroup, argv[1], strlen(argv[1]) );
+ strncpy( g_username, argv[2], strlen(argv[2]) );
+ strncpy( g_password, argv[3], strlen(argv[3]) );
+ strncpy( url, argv[4], strlen(argv[4]) );
+
+ smbc_init( auth_fn, 0 );
+ smbc_rmdir( url );
+ err = smbc_mkdir( url, 0666 );
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/mkdir/mkdir_2.c b/testsuite/libsmbclient/src/mkdir/mkdir_2.c
new file mode 100644
index 00000000000..ebbc94a18bb
--- /dev/null
+++ b/testsuite/libsmbclient/src/mkdir/mkdir_2.c
@@ -0,0 +1,56 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+ strncpy( g_workgroup, argv[1], strlen(argv[1]) );
+ strncpy( g_username, argv[2], strlen(argv[2]) );
+ strncpy( g_password, argv[3], strlen(argv[3]) );
+ strncpy( url, argv[4], strlen(argv[4]) );
+
+ smbc_init( auth_fn, 0 );
+ smbc_rmdir( url );
+ smbc_mkdir( url, 0666 );
+
+ err = errno;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/mkdir/mkdir_3.c b/testsuite/libsmbclient/src/mkdir/mkdir_3.c
new file mode 100644
index 00000000000..e5a0ce2561f
--- /dev/null
+++ b/testsuite/libsmbclient/src/mkdir/mkdir_3.c
@@ -0,0 +1,58 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+ strncpy( g_workgroup, argv[1], strlen(argv[1]) );
+ strncpy( g_username, argv[2], strlen(argv[2]) );
+ strncpy( g_password, argv[3], strlen(argv[3]) );
+ strncpy( url, argv[4], strlen(argv[4]) );
+
+ smbc_init( auth_fn, 0 );
+ smbc_rmdir( url );
+
+ smbc_mkdir( url, 0666 );
+ smbc_mkdir( url, 0666 );
+
+ err = errno;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/mkdir/mkdir_4.c b/testsuite/libsmbclient/src/mkdir/mkdir_4.c
new file mode 100644
index 00000000000..ed1aa70f33f
--- /dev/null
+++ b/testsuite/libsmbclient/src/mkdir/mkdir_4.c
@@ -0,0 +1,62 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+ strncpy( g_workgroup, argv[1], strlen(argv[1]) );
+ strncpy( g_username, argv[2], strlen(argv[2]) );
+ strncpy( g_password, argv[3], strlen(argv[3]) );
+ strncpy( url, argv[4], strlen(argv[4]) );
+
+ smbc_init( auth_fn, 0 );
+ smbc_rmdir( url );
+
+ smbc_mkdir( url, 0666 );
+ err = smbc_mkdir( url, 0666 );
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/open/open_1.c b/testsuite/libsmbclient/src/open/open_1.c
new file mode 100644
index 00000000000..9f72985cec7
--- /dev/null
+++ b/testsuite/libsmbclient/src/open/open_1.c
@@ -0,0 +1,60 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ fd = smbc_open(url, O_RDWR | O_CREAT, 0666);
+
+ if ( fd < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/open/open_2.c b/testsuite/libsmbclient/src/open/open_2.c
new file mode 100644
index 00000000000..8903ee912b0
--- /dev/null
+++ b/testsuite/libsmbclient/src/open/open_2.c
@@ -0,0 +1,56 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ fd = smbc_open(url, O_RDWR | O_CREAT, 0666);
+
+ err = errno;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/open/open_3.c b/testsuite/libsmbclient/src/open/open_3.c
new file mode 100644
index 00000000000..25a663d6ab7
--- /dev/null
+++ b/testsuite/libsmbclient/src/open/open_3.c
@@ -0,0 +1,60 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ fd = smbc_open(url, O_RDWR, 0666);
+
+ if ( fd < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/open/open_4.c b/testsuite/libsmbclient/src/open/open_4.c
new file mode 100644
index 00000000000..2bfe30f1af1
--- /dev/null
+++ b/testsuite/libsmbclient/src/open/open_4.c
@@ -0,0 +1,63 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ fd = smbc_open(url, O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+ fd = 0;
+ fd = smbc_open(url, O_RDWR, 0666);
+
+ if ( fd < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/open/open_5.c b/testsuite/libsmbclient/src/open/open_5.c
new file mode 100644
index 00000000000..93ffb4891ed
--- /dev/null
+++ b/testsuite/libsmbclient/src/open/open_5.c
@@ -0,0 +1,58 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ fd = smbc_open(url, O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+ fd = smbc_open(url, O_RDWR, 0666);
+
+ err = errno;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/open_print_job/open_print_job_1.c b/testsuite/libsmbclient/src/open_print_job/open_print_job_1.c
new file mode 100644
index 00000000000..585fdee377f
--- /dev/null
+++ b/testsuite/libsmbclient/src/open_print_job/open_print_job_1.c
@@ -0,0 +1,60 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ fd = smbc_open_print_job(url);
+
+ if ( fd < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/open_print_job/open_print_job_2.c b/testsuite/libsmbclient/src/open_print_job/open_print_job_2.c
new file mode 100644
index 00000000000..f737f19bfec
--- /dev/null
+++ b/testsuite/libsmbclient/src/open_print_job/open_print_job_2.c
@@ -0,0 +1,56 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ fd = smbc_open_print_job(url);
+
+ err = errno;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/opendir/opendir_1.c b/testsuite/libsmbclient/src/opendir/opendir_1.c
new file mode 100644
index 00000000000..1bd01b5feeb
--- /dev/null
+++ b/testsuite/libsmbclient/src/opendir/opendir_1.c
@@ -0,0 +1,62 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int dh = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ dh = smbc_opendir(url);
+ /* printf("directory handle: %i\n",dh); */
+
+ if ( dh < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/opendir/opendir_2.c b/testsuite/libsmbclient/src/opendir/opendir_2.c
new file mode 100644
index 00000000000..60658d2cd56
--- /dev/null
+++ b/testsuite/libsmbclient/src/opendir/opendir_2.c
@@ -0,0 +1,55 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ smbc_opendir(url);
+
+ err = errno;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/opendir/opendir_3.c b/testsuite/libsmbclient/src/opendir/opendir_3.c
new file mode 100644
index 00000000000..3b6f28eaf34
--- /dev/null
+++ b/testsuite/libsmbclient/src/opendir/opendir_3.c
@@ -0,0 +1,65 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR|O_CREAT,0666);
+ smbc_close(fd);
+
+ err = smbc_opendir(url);
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/opendir/opendir_4.c b/testsuite/libsmbclient/src/opendir/opendir_4.c
new file mode 100644
index 00000000000..23b25e55063
--- /dev/null
+++ b/testsuite/libsmbclient/src/opendir/opendir_4.c
@@ -0,0 +1,61 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR|O_CREAT,0666);
+ smbc_close(fd);
+
+ smbc_opendir(url);
+
+ err = errno;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/print_file/print_file_1.c b/testsuite/libsmbclient/src/print_file/print_file_1.c
new file mode 100644
index 00000000000..19932aa3192
--- /dev/null
+++ b/testsuite/libsmbclient/src/print_file/print_file_1.c
@@ -0,0 +1,76 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 7 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+ /* printf("Message: %s\n",message); */
+ /* printf("Message len: %i\n",msg_len); */
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ free(message);
+ err = smbc_print_file(url,argv[6]);
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/print_file/print_file_2.c b/testsuite/libsmbclient/src/print_file/print_file_2.c
new file mode 100644
index 00000000000..a569cd8fe97
--- /dev/null
+++ b/testsuite/libsmbclient/src/print_file/print_file_2.c
@@ -0,0 +1,72 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 7 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+ /* printf("Message: %s\n",message); */
+ /* printf("Message len: %i\n",msg_len); */
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ free(message);
+
+ smbc_print_file(url,argv[6]);
+ err = errno;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/print_file/print_file_3.c b/testsuite/libsmbclient/src/print_file/print_file_3.c
new file mode 100644
index 00000000000..4b2a6af84f2
--- /dev/null
+++ b/testsuite/libsmbclient/src/print_file/print_file_3.c
@@ -0,0 +1,59 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+
+ char url[MAX_BUFF_SIZE];
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ err = smbc_print_file(url,argv[6]);
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
diff --git a/testsuite/libsmbclient/src/print_file/print_file_4.c b/testsuite/libsmbclient/src/print_file/print_file_4.c
new file mode 100644
index 00000000000..1650f7340df
--- /dev/null
+++ b/testsuite/libsmbclient/src/print_file/print_file_4.c
@@ -0,0 +1,55 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+
+ char url[MAX_BUFF_SIZE];
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ smbc_print_file(url,argv[6]);
+
+ err = errno;
+
+ }
+
+ return err;
+
+}
diff --git a/testsuite/libsmbclient/src/read/read_1.c b/testsuite/libsmbclient/src/read/read_1.c
new file mode 100644
index 00000000000..accf0bf8721
--- /dev/null
+++ b/testsuite/libsmbclient/src/read/read_1.c
@@ -0,0 +1,83 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+ char* response;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ response = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ fd = smbc_open(url,O_RDWR, 0666);
+ err = smbc_read(fd,response,msg_len);
+
+ free(message);
+ free(response);
+
+ if ( err < 0 )
+ err = 1;
+
+ else if ( err != msg_len )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/read/read_10.c b/testsuite/libsmbclient/src/read/read_10.c
new file mode 100644
index 00000000000..d5b66dfe635
--- /dev/null
+++ b/testsuite/libsmbclient/src/read/read_10.c
@@ -0,0 +1,68 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message = NULL;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ msg_len = 10;
+ fd = smbc_open(url, O_RDWR, 0666);
+ smbc_write(fd, message, msg_len);
+
+ err = errno;
+
+ smbc_close(fd);
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/read/read_11.c b/testsuite/libsmbclient/src/read/read_11.c
new file mode 100644
index 00000000000..3f9ae3f97da
--- /dev/null
+++ b/testsuite/libsmbclient/src/read/read_11.c
@@ -0,0 +1,83 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+ char* response;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ response = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ fd = smbc_open(url, O_RDWR, 0666);
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ fd = smbc_open(url, O_RDONLY, 0666);
+ smbc_read(fd,response,msg_len);
+ smbc_close(fd);
+
+ if ( memcmp ( message, response, msg_len) == 0 )
+ err = 0;
+
+ else
+ err = 1;
+
+ free(message);
+ free(response);
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/read/read_12.c b/testsuite/libsmbclient/src/read/read_12.c
new file mode 100644
index 00000000000..2747f62e1c8
--- /dev/null
+++ b/testsuite/libsmbclient/src/read/read_12.c
@@ -0,0 +1,87 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+ char* response;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ response = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ fd = smbc_open(url, O_RDWR, 0666);
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ strncpy(g_username,"xxxxxxxx",8);
+ strncpy(g_password,"xxxxxxxx",8);
+
+ fd = smbc_open(url, O_RDONLY, 0666);
+
+ err = smbc_read(fd,response,msg_len);
+ smbc_close(fd);
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+ free(message);
+ free(response);
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/read/read_13.c b/testsuite/libsmbclient/src/read/read_13.c
new file mode 100644
index 00000000000..89bc68f915b
--- /dev/null
+++ b/testsuite/libsmbclient/src/read/read_13.c
@@ -0,0 +1,91 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+ char* response;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ response = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ fd = smbc_open(url, O_RDWR, 0666);
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ strncpy(g_username,"xxxxxxxx",8);
+ strncpy(g_password,"xxxxxxxx",8);
+
+ fd = smbc_open(url, O_RDONLY, 0666);
+
+ if (fd < 0)
+
+ err = errno;
+
+ else {
+
+ err = smbc_read(fd,response,msg_len);
+ err = errno;
+
+ smbc_close(fd);
+
+ }
+
+ free(message);
+ free(response);
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/read/read_2.c b/testsuite/libsmbclient/src/read/read_2.c
new file mode 100644
index 00000000000..4b3dc9439c1
--- /dev/null
+++ b/testsuite/libsmbclient/src/read/read_2.c
@@ -0,0 +1,76 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+ char* response;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ response = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ fd = smbc_open(url,O_RDWR, 0666);
+ smbc_read(fd,response,msg_len);
+ err = errno;
+
+ free(message);
+ free(response);
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/read/read_3.c b/testsuite/libsmbclient/src/read/read_3.c
new file mode 100644
index 00000000000..3f4493487e7
--- /dev/null
+++ b/testsuite/libsmbclient/src/read/read_3.c
@@ -0,0 +1,83 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+ char* response;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ response = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ fd = smbc_open(url,O_WRONLY, 0666);
+ err = smbc_read(fd,response,msg_len);
+
+ free(message);
+ free(response);
+
+ if ( err < 0 )
+ err = 1;
+
+ else if ( err != msg_len )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/read/read_4.c b/testsuite/libsmbclient/src/read/read_4.c
new file mode 100644
index 00000000000..dbacfa392e8
--- /dev/null
+++ b/testsuite/libsmbclient/src/read/read_4.c
@@ -0,0 +1,77 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+ char* response;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ response = malloc(msg_len);
+ bzero(response,msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ fd = smbc_open(url,O_WRONLY, 0666);
+ smbc_read(fd,response,msg_len);
+ err = errno;
+
+ free(message);
+ free(response);
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/read/read_5.c b/testsuite/libsmbclient/src/read/read_5.c
new file mode 100644
index 00000000000..96891ef8da6
--- /dev/null
+++ b/testsuite/libsmbclient/src/read/read_5.c
@@ -0,0 +1,83 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+ char* response;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ response = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ fd = smbc_open(url,O_RDONLY, 0666);
+ err = smbc_read(fd,response,msg_len);
+
+ free(message);
+ free(response);
+
+ if ( err < 0 )
+ err = 1;
+
+ else if ( err != msg_len )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/read/read_6.c b/testsuite/libsmbclient/src/read/read_6.c
new file mode 100644
index 00000000000..7839a964ee4
--- /dev/null
+++ b/testsuite/libsmbclient/src/read/read_6.c
@@ -0,0 +1,77 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+ char* response;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ response = malloc(msg_len);
+ bzero(response,msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ fd = smbc_open(url,O_RDONLY, 0666);
+ smbc_read(fd,response,msg_len);
+ err = errno;
+
+ free(message);
+ free(response);
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/read/read_7.c b/testsuite/libsmbclient/src/read/read_7.c
new file mode 100644
index 00000000000..fa8a783d7fa
--- /dev/null
+++ b/testsuite/libsmbclient/src/read/read_7.c
@@ -0,0 +1,60 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char* message = "Testing";
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+
+ if ( argc == 4 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+
+ fd = 10345; /* Random value for File Descriptor */
+ smbc_init(auth_fn, 0);
+ err = smbc_read(fd, message, sizeof(message));
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/read/read_8.c b/testsuite/libsmbclient/src/read/read_8.c
new file mode 100644
index 00000000000..e3f7055d3f4
--- /dev/null
+++ b/testsuite/libsmbclient/src/read/read_8.c
@@ -0,0 +1,56 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char* message = "Testing";
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+
+ if ( argc == 4 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+
+ fd = 10345; /* Random value for File Descriptor */
+ smbc_init(auth_fn, 0);
+ err = smbc_read(fd, message, sizeof(message));
+
+ err = errno;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/read/read_9.c b/testsuite/libsmbclient/src/read/read_9.c
new file mode 100644
index 00000000000..77468317d92
--- /dev/null
+++ b/testsuite/libsmbclient/src/read/read_9.c
@@ -0,0 +1,70 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message = NULL;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ msg_len = 10;
+ fd = smbc_open(url, O_RDWR, 0666);
+ err = smbc_read(fd, message, msg_len);
+ smbc_close(fd);
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/readdir/readdir_1.c b/testsuite/libsmbclient/src/readdir/readdir_1.c
new file mode 100644
index 00000000000..5ca7e38bf8d
--- /dev/null
+++ b/testsuite/libsmbclient/src/readdir/readdir_1.c
@@ -0,0 +1,107 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int dh = 0;
+ int entry_num = 0;
+ int i = 0;
+ char *file_name;
+ char *tmp_file_ptr;
+
+ struct smbc_dirent *dirptr;
+
+
+ char buff[MAX_BUFF_SIZE];
+ char url[MAX_BUFF_SIZE];
+ char file_url[MAX_BUFF_SIZE];
+ char dir_url[MAX_BUFF_SIZE];
+ char dirbuff[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+ bzero(file_url,MAX_BUFF_SIZE);
+ bzero(dir_url,MAX_BUFF_SIZE);
+ bzero(buff,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ dirptr = (struct smbc_dirent *) dirbuff;
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+ smbc_init(auth_fn, 0);
+
+ strncpy(file_url,"tempfile-",9);
+ tmp_file_ptr = file_url;
+ tmp_file_ptr += 9;
+
+ smbc_rmdir(url);
+ smbc_mkdir(url,0666);
+
+ entry_num = atoi(argv[5]);
+ strcat(dir_url,url);
+ strcat(dir_url,"/");
+
+ file_name = dir_url;
+ file_name += strlen(dir_url);
+
+ for ( i = 0; i < entry_num; i++ )
+ {
+ sprintf(buff,"%d",i);
+ memcpy(tmp_file_ptr,buff,strlen(buff)+4);
+ strncat(tmp_file_ptr,".txt",4);
+ strcpy(file_name,file_url);
+ fd = smbc_open(dir_url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ }
+
+ dh = smbc_opendir(url);
+ dirptr = smbc_readdir( dh );
+
+ if ( dirptr == NULL )
+
+ err = 1;
+
+ else
+ err = 0;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/readdir/readdir_2.c b/testsuite/libsmbclient/src/readdir/readdir_2.c
new file mode 100644
index 00000000000..bc4f53dd859
--- /dev/null
+++ b/testsuite/libsmbclient/src/readdir/readdir_2.c
@@ -0,0 +1,102 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int dh = 0;
+ int entry_num = 0;
+ int i = 0;
+ char *file_name;
+ char *tmp_file_ptr;
+
+ struct smbc_dirent *dirptr;
+
+
+ char buff[MAX_BUFF_SIZE];
+ char url[MAX_BUFF_SIZE];
+ char file_url[MAX_BUFF_SIZE];
+ char dir_url[MAX_BUFF_SIZE];
+ char dirbuff[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+ bzero(file_url,MAX_BUFF_SIZE);
+ bzero(dir_url,MAX_BUFF_SIZE);
+ bzero(buff,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ dirptr = (struct smbc_dirent *) dirbuff;
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+ smbc_init(auth_fn, 0);
+
+ strncpy(file_url,"tempfile-",9);
+ tmp_file_ptr = file_url;
+ tmp_file_ptr += 9;
+
+ smbc_rmdir(url);
+ smbc_mkdir(url,0666);
+
+ entry_num = atoi(argv[5]);
+ strcat(dir_url,url);
+ strcat(dir_url,"/");
+
+ file_name = dir_url;
+ file_name += strlen(dir_url);
+
+ for ( i = 0; i < entry_num; i++ )
+ {
+ sprintf(buff,"%d",i);
+ memcpy(tmp_file_ptr,buff,strlen(buff)+4);
+ strncat(tmp_file_ptr,".txt",4);
+ strcpy(file_name,file_url);
+ fd = smbc_open(dir_url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ }
+
+ dh = smbc_opendir(url);
+ dirptr = smbc_readdir( dh );
+
+ err = errno;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/readdir/readdir_3.c b/testsuite/libsmbclient/src/readdir/readdir_3.c
new file mode 100644
index 00000000000..06a4c9eded1
--- /dev/null
+++ b/testsuite/libsmbclient/src/readdir/readdir_3.c
@@ -0,0 +1,71 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int dh = 0;
+
+ struct smbc_dirent *dirptr;
+
+
+ char url[MAX_BUFF_SIZE];
+ char dirbuff[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ dirptr = (struct smbc_dirent *) dirbuff;
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+ smbc_init(auth_fn, 0);
+
+ dh = smbc_opendir(url);
+ dirptr = smbc_readdir( dh );
+
+ if ( dirptr == NULL )
+
+ err = 1;
+
+ else
+ err = 0;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/readdir/readdir_4.c b/testsuite/libsmbclient/src/readdir/readdir_4.c
new file mode 100644
index 00000000000..42b18aaa48b
--- /dev/null
+++ b/testsuite/libsmbclient/src/readdir/readdir_4.c
@@ -0,0 +1,67 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int dh = 0;
+
+ struct smbc_dirent *dirptr;
+
+
+ char url[MAX_BUFF_SIZE];
+ char dirbuff[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ dirptr = (struct smbc_dirent *) dirbuff;
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+ smbc_init(auth_fn, 0);
+
+
+ dh = smbc_opendir(url);
+ dirptr = smbc_readdir( dh );
+
+ err = errno;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/readdir/readdir_5.c b/testsuite/libsmbclient/src/readdir/readdir_5.c
new file mode 100644
index 00000000000..de94c4698e3
--- /dev/null
+++ b/testsuite/libsmbclient/src/readdir/readdir_5.c
@@ -0,0 +1,155 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int dh = 0;
+ int entry_num = 0;
+ int i = 0;
+ int j = 0;
+ char *file_name;
+ char *tmp_file_ptr;
+
+ struct smbc_dirent *dirptr;
+
+
+ char buff[MAX_BUFF_SIZE];
+ char url[MAX_BUFF_SIZE];
+ char file_url[MAX_BUFF_SIZE];
+ char dir_url[MAX_BUFF_SIZE];
+ char dirbuff[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+ bzero(file_url,MAX_BUFF_SIZE);
+ bzero(dir_url,MAX_BUFF_SIZE);
+ bzero(buff,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ dirptr = (struct smbc_dirent *) dirbuff;
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+ smbc_init(auth_fn, 0);
+
+ strncpy(file_url,"tempfile-",9);
+ tmp_file_ptr = file_url;
+ tmp_file_ptr += 9;
+
+ smbc_rmdir(url);
+ smbc_mkdir(url,0666);
+
+ entry_num = atoi(argv[5]);
+ strcat(dir_url,url);
+ strcat(dir_url,"/");
+
+ file_name = dir_url;
+ file_name += strlen(dir_url);
+
+ for ( i = 0; i < entry_num; i++ )
+ {
+ sprintf(buff,"%d",i);
+ memcpy(tmp_file_ptr,buff,strlen(buff)+4);
+ strncat(tmp_file_ptr,".txt",4);
+ strcpy(file_name,file_url);
+ fd = smbc_open(dir_url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ }
+
+ dh = smbc_opendir(url);
+
+ err = 0;
+ i = 0;
+ bzero(buff,MAX_BUFF_SIZE);
+ bzero(tmp_file_ptr,MAX_BUFF_SIZE-9);
+
+ while ( 1 )
+ {
+ dirptr = smbc_readdir( dh );
+ if ( dirptr == NULL )
+ {
+ break;
+ }
+
+ /* printf("Name: %s\n",dirptr->name); */
+ if ( j == 0 )
+ {
+ if ( !(( strncmp(dirptr->name,".",1) == 0 )) )
+ {
+ break;
+ err = 1;
+ }
+
+ } else if ( j == 1 ) {
+
+ if ( !(( strncmp(dirptr->name,"..",2) == 0 )) )
+ {
+ break;
+ err = 1;
+ }
+
+ } else if ( j > 1 ) {
+
+ sprintf(buff,"%d",i);
+ memcpy(tmp_file_ptr,buff,strlen(buff)+4);
+ strncat(tmp_file_ptr,".txt",4);
+
+ if ( !(( strcmp(dirptr->name,file_url) == 0 )) ) /* make sure entries match */
+ {
+ err = 1;
+ break;
+ }
+
+ i++;
+
+ }
+
+ j++;
+
+ }
+
+ if ( ! err )
+ {
+ if ( (j - 2) != entry_num ) /* Make sure that all entries created are counted and returned - minus . and .. */
+ err = 1;
+ }
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/rename/rename_1.c b/testsuite/libsmbclient/src/rename/rename_1.c
new file mode 100644
index 00000000000..c3e1377c293
--- /dev/null
+++ b/testsuite/libsmbclient/src/rename/rename_1.c
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ err = smbc_rename( url, argv[5] );
+
+ if ( err < 0 )
+ err = 1;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/rename/rename_10.c b/testsuite/libsmbclient/src/rename/rename_10.c
new file mode 100644
index 00000000000..01fb144c593
--- /dev/null
+++ b/testsuite/libsmbclient/src/rename/rename_10.c
@@ -0,0 +1,64 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd1 = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 9 )
+ {
+ strncpy( g_workgroup, argv[1], strlen(argv[1]) );
+ strncpy( g_username, argv[2], strlen(argv[2]) );
+ strncpy( g_password, argv[3], strlen(argv[3]) );
+ strncpy( url, argv[6], strlen(argv[6]) );
+
+ smbc_init( auth_fn, 0 );
+ fd1 = smbc_open( url, O_RDWR | O_CREAT, 0666 );
+ smbc_close( fd1 );
+
+ smbc_mkdir( argv[7], 0700 );
+
+ strncpy( g_username, argv[4], strlen(argv[4]) );
+ strncpy( g_password, argv[5], strlen(argv[5]) );
+
+ smbc_rename( url, argv[8] );
+
+ err = errno;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/rename/rename_11.c b/testsuite/libsmbclient/src/rename/rename_11.c
new file mode 100644
index 00000000000..0c341088026
--- /dev/null
+++ b/testsuite/libsmbclient/src/rename/rename_11.c
@@ -0,0 +1,63 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd1 = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+ strncpy( g_workgroup, argv[1], strlen(argv[1]) );
+ strncpy( g_username, argv[2], strlen(argv[2]) );
+ strncpy( g_password, argv[3], strlen(argv[3]) );
+ strncpy( url, argv[4], strlen(argv[4]) );
+
+ smbc_init( auth_fn, 0 );
+ fd1 = smbc_open( url, O_RDWR | O_CREAT, 0666 );
+ smbc_close( fd1 );
+
+ err = smbc_rename( NULL, url );
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/rename/rename_12.c b/testsuite/libsmbclient/src/rename/rename_12.c
new file mode 100644
index 00000000000..cc34e079e18
--- /dev/null
+++ b/testsuite/libsmbclient/src/rename/rename_12.c
@@ -0,0 +1,58 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd1 = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+ strncpy( g_workgroup, argv[1], strlen(argv[1]) );
+ strncpy( g_username, argv[2], strlen(argv[2]) );
+ strncpy( g_password, argv[3], strlen(argv[3]) );
+ strncpy( url, argv[4], strlen(argv[4]) );
+
+ smbc_init( auth_fn, 0 );
+ fd1 = smbc_open( url, O_RDWR | O_CREAT, 0666 );
+ smbc_close( fd1 );
+
+ smbc_rename( NULL, url );
+
+ err = errno;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/rename/rename_13.c b/testsuite/libsmbclient/src/rename/rename_13.c
new file mode 100644
index 00000000000..30b33499d60
--- /dev/null
+++ b/testsuite/libsmbclient/src/rename/rename_13.c
@@ -0,0 +1,63 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd1 = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+ strncpy( g_workgroup, argv[1], strlen(argv[1]) );
+ strncpy( g_username, argv[2], strlen(argv[2]) );
+ strncpy( g_password, argv[3], strlen(argv[3]) );
+ strncpy( url, argv[4], strlen(argv[4]) );
+
+ smbc_init( auth_fn, 0 );
+ fd1 = smbc_open( url, O_RDWR | O_CREAT, 0666 );
+ smbc_close( fd1 );
+
+ err = smbc_rename( url, NULL );
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/rename/rename_14.c b/testsuite/libsmbclient/src/rename/rename_14.c
new file mode 100644
index 00000000000..61aad34e722
--- /dev/null
+++ b/testsuite/libsmbclient/src/rename/rename_14.c
@@ -0,0 +1,58 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd1 = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+ strncpy( g_workgroup, argv[1], strlen(argv[1]) );
+ strncpy( g_username, argv[2], strlen(argv[2]) );
+ strncpy( g_password, argv[3], strlen(argv[3]) );
+ strncpy( url, argv[4], strlen(argv[4]) );
+
+ smbc_init( auth_fn, 0 );
+ fd1 = smbc_open( url, O_RDWR | O_CREAT, 0666 );
+ smbc_close( fd1 );
+
+ smbc_rename( url, NULL );
+
+ err = errno;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/rename/rename_2.c b/testsuite/libsmbclient/src/rename/rename_2.c
new file mode 100644
index 00000000000..b3d340348c8
--- /dev/null
+++ b/testsuite/libsmbclient/src/rename/rename_2.c
@@ -0,0 +1,54 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ smbc_rename( url, argv[5] );
+
+ err = errno;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/rename/rename_3.c b/testsuite/libsmbclient/src/rename/rename_3.c
new file mode 100644
index 00000000000..a7508e969c8
--- /dev/null
+++ b/testsuite/libsmbclient/src/rename/rename_3.c
@@ -0,0 +1,56 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ smbc_open(url,O_RDWR | O_CREAT,0666);
+ err = smbc_rename( url, argv[5] );
+
+ if ( err < 0 )
+ err = 1;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/rename/rename_4.c b/testsuite/libsmbclient/src/rename/rename_4.c
new file mode 100644
index 00000000000..a4c26470017
--- /dev/null
+++ b/testsuite/libsmbclient/src/rename/rename_4.c
@@ -0,0 +1,55 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ smbc_open(url,O_RDWR | O_CREAT,0666);
+ smbc_rename( url, argv[5] );
+
+ err = errno;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/rename/rename_5.c b/testsuite/libsmbclient/src/rename/rename_5.c
new file mode 100644
index 00000000000..97fb8fe6830
--- /dev/null
+++ b/testsuite/libsmbclient/src/rename/rename_5.c
@@ -0,0 +1,59 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+ strncpy( g_workgroup, argv[1], strlen(argv[1]) );
+ strncpy( g_username, argv[2], strlen(argv[2]) );
+ strncpy( g_password, argv[3], strlen(argv[3]) );
+ strncpy( url, argv[4], strlen(argv[4]) );
+
+ smbc_init( auth_fn, 0 );
+ fd = smbc_open( url, O_RDWR | O_CREAT, 0666 );
+ smbc_close( fd );
+ err = smbc_rename( url, argv[5] );
+
+ if ( err < 0 )
+ err = 1;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/rename/rename_6.c b/testsuite/libsmbclient/src/rename/rename_6.c
new file mode 100644
index 00000000000..c9c349427e4
--- /dev/null
+++ b/testsuite/libsmbclient/src/rename/rename_6.c
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ fd = smbc_open(url,O_RDWR | O_CREAT,0666);
+ smbc_close( fd );
+ smbc_rename( url, argv[5] );
+
+ err = errno;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/rename/rename_7.c b/testsuite/libsmbclient/src/rename/rename_7.c
new file mode 100644
index 00000000000..67188abea96
--- /dev/null
+++ b/testsuite/libsmbclient/src/rename/rename_7.c
@@ -0,0 +1,66 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd1 = 0;
+ int fd2 = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+ strncpy( g_workgroup, argv[1], strlen(argv[1]) );
+ strncpy( g_username, argv[2], strlen(argv[2]) );
+ strncpy( g_password, argv[3], strlen(argv[3]) );
+ strncpy( url, argv[4], strlen(argv[4]) );
+
+ smbc_init( auth_fn, 0 );
+ fd1 = smbc_open( url, O_RDWR | O_CREAT, 0666 );
+ smbc_close( fd1 );
+
+ smbc_rename( url, argv[5] );
+ fd1 = smbc_open( url, O_RDWR, 0666 );
+ fd2 = smbc_open( argv[5], O_RDWR, 0666 );
+
+ if ( fd1 == -1 && fd2 != -1 )
+ err = 0;
+
+ else
+ err = 1;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/rename/rename_8.c b/testsuite/libsmbclient/src/rename/rename_8.c
new file mode 100644
index 00000000000..efbef5c30cd
--- /dev/null
+++ b/testsuite/libsmbclient/src/rename/rename_8.c
@@ -0,0 +1,68 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd1 = 0;
+ int fd2 = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 7 )
+ {
+ strncpy( g_workgroup, argv[1], strlen(argv[1]) );
+ strncpy( g_username, argv[2], strlen(argv[2]) );
+ strncpy( g_password, argv[3], strlen(argv[3]) );
+ strncpy( url, argv[4], strlen(argv[4]) );
+
+ smbc_init( auth_fn, 0 );
+ fd1 = smbc_open( url, O_RDWR | O_CREAT, 0666 );
+ smbc_close( fd1 );
+
+ smbc_mkdir( argv[5], 0666 );
+ smbc_rename( url, argv[6] );
+
+ fd1 = smbc_open( url, O_RDWR, 0666 );
+ fd2 = smbc_open( argv[6], O_RDWR, 0666 );
+
+ if ( fd1 == -1 && fd2 != -1 )
+ err = 0;
+
+ else
+ err = 1;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/rename/rename_9.c b/testsuite/libsmbclient/src/rename/rename_9.c
new file mode 100644
index 00000000000..1c1876dbb00
--- /dev/null
+++ b/testsuite/libsmbclient/src/rename/rename_9.c
@@ -0,0 +1,68 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd1 = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 9 )
+ {
+ strncpy( g_workgroup, argv[1], strlen(argv[1]) );
+ strncpy( g_username, argv[2], strlen(argv[2]) );
+ strncpy( g_password, argv[3], strlen(argv[3]) );
+ strncpy( url, argv[6], strlen(argv[6]) );
+
+ smbc_init( auth_fn, 0 );
+ fd1 = smbc_open( url, O_RDWR | O_CREAT, 0666 );
+ smbc_close( fd1 );
+
+ smbc_mkdir( argv[7], 0700 );
+
+ strncpy( g_username, argv[4], strlen(argv[4]) );
+ strncpy( g_password, argv[5], strlen(argv[5]) );
+
+ err = smbc_rename( url, argv[8] );
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/rmdir/rmdir_1.c b/testsuite/libsmbclient/src/rmdir/rmdir_1.c
new file mode 100644
index 00000000000..2c64052d8be
--- /dev/null
+++ b/testsuite/libsmbclient/src/rmdir/rmdir_1.c
@@ -0,0 +1,59 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+ strncpy( g_workgroup, argv[1], strlen(argv[1]) );
+ strncpy( g_username, argv[2], strlen(argv[2]) );
+ strncpy( g_password, argv[3], strlen(argv[3]) );
+ strncpy( url, argv[4], strlen(argv[4]) );
+
+ smbc_init( auth_fn, 0 );
+ err = smbc_rmdir( url );
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/rmdir/rmdir_2.c b/testsuite/libsmbclient/src/rmdir/rmdir_2.c
new file mode 100644
index 00000000000..700b1d7a97f
--- /dev/null
+++ b/testsuite/libsmbclient/src/rmdir/rmdir_2.c
@@ -0,0 +1,55 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+ strncpy( g_workgroup, argv[1], strlen(argv[1]) );
+ strncpy( g_username, argv[2], strlen(argv[2]) );
+ strncpy( g_password, argv[3], strlen(argv[3]) );
+ strncpy( url, argv[4], strlen(argv[4]) );
+
+ smbc_init( auth_fn, 0 );
+ smbc_rmdir( url );
+
+ err = errno;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/rmdir/rmdir_3.c b/testsuite/libsmbclient/src/rmdir/rmdir_3.c
new file mode 100644
index 00000000000..c1b881d4d4d
--- /dev/null
+++ b/testsuite/libsmbclient/src/rmdir/rmdir_3.c
@@ -0,0 +1,61 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+ strncpy( g_workgroup, argv[1], strlen(argv[1]) );
+ strncpy( g_username, argv[2], strlen(argv[2]) );
+ strncpy( g_password, argv[3], strlen(argv[3]) );
+ strncpy( url, argv[4], strlen(argv[4]) );
+
+ smbc_init( auth_fn, 0 );
+ smbc_mkdir( url, 700 );
+
+ err = smbc_rmdir( url );
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/rmdir/rmdir_4.c b/testsuite/libsmbclient/src/rmdir/rmdir_4.c
new file mode 100644
index 00000000000..a7723037256
--- /dev/null
+++ b/testsuite/libsmbclient/src/rmdir/rmdir_4.c
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+ strncpy( g_workgroup, argv[1], strlen(argv[1]) );
+ strncpy( g_username, argv[2], strlen(argv[2]) );
+ strncpy( g_password, argv[3], strlen(argv[3]) );
+ strncpy( url, argv[4], strlen(argv[4]) );
+
+ smbc_init( auth_fn, 0 );
+ smbc_mkdir( url, 700 );
+
+ smbc_rmdir( url );
+
+ err = errno;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/rmdir/rmdir_5.c b/testsuite/libsmbclient/src/rmdir/rmdir_5.c
new file mode 100644
index 00000000000..c4f787f63bc
--- /dev/null
+++ b/testsuite/libsmbclient/src/rmdir/rmdir_5.c
@@ -0,0 +1,63 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd1 = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+ strncpy( g_workgroup, argv[1], strlen(argv[1]) );
+ strncpy( g_username, argv[2], strlen(argv[2]) );
+ strncpy( g_password, argv[3], strlen(argv[3]) );
+ strncpy( url, argv[4], strlen(argv[4]) );
+
+ smbc_init( auth_fn, 0 );
+ fd1 = smbc_open( url, O_RDWR | O_CREAT, 0666 );
+ smbc_close( fd1 );
+
+ err = smbc_rmdir( url );
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/rmdir/rmdir_6.c b/testsuite/libsmbclient/src/rmdir/rmdir_6.c
new file mode 100644
index 00000000000..a44e6778322
--- /dev/null
+++ b/testsuite/libsmbclient/src/rmdir/rmdir_6.c
@@ -0,0 +1,59 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd1 = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+ strncpy( g_workgroup, argv[1], strlen(argv[1]) );
+ strncpy( g_username, argv[2], strlen(argv[2]) );
+ strncpy( g_password, argv[3], strlen(argv[3]) );
+ strncpy( url, argv[4], strlen(argv[4]) );
+
+ smbc_init( auth_fn, 0 );
+ fd1 = smbc_open( url, O_RDWR | O_CREAT, 0666 );
+ smbc_close( fd1 );
+
+ smbc_rmdir( url );
+
+ err = errno;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/stat/stat_1.c b/testsuite/libsmbclient/src/stat/stat_1.c
new file mode 100644
index 00000000000..08382acd74a
--- /dev/null
+++ b/testsuite/libsmbclient/src/stat/stat_1.c
@@ -0,0 +1,62 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ char url[MAX_BUFF_SIZE];
+ struct stat st;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ err = smbc_stat(url, &st);
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/stat/stat_2.c b/testsuite/libsmbclient/src/stat/stat_2.c
new file mode 100644
index 00000000000..80ee5dd8713
--- /dev/null
+++ b/testsuite/libsmbclient/src/stat/stat_2.c
@@ -0,0 +1,58 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ char url[MAX_BUFF_SIZE];
+ struct stat st;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ smbc_stat(url, &st);
+
+ err = errno;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/stat/stat_3.c b/testsuite/libsmbclient/src/stat/stat_3.c
new file mode 100644
index 00000000000..1220577a3c7
--- /dev/null
+++ b/testsuite/libsmbclient/src/stat/stat_3.c
@@ -0,0 +1,66 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+ struct stat st;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ err = smbc_stat(url, &st);
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/stat/stat_4.c b/testsuite/libsmbclient/src/stat/stat_4.c
new file mode 100644
index 00000000000..8bc544833e7
--- /dev/null
+++ b/testsuite/libsmbclient/src/stat/stat_4.c
@@ -0,0 +1,62 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+ struct stat st;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ smbc_stat(url, &st);
+
+ err = errno;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/stat/stat_5.c b/testsuite/libsmbclient/src/stat/stat_5.c
new file mode 100644
index 00000000000..86028f0598a
--- /dev/null
+++ b/testsuite/libsmbclient/src/stat/stat_5.c
@@ -0,0 +1,77 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+ struct stat st;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ smbc_stat(url,&st);
+
+ free(message);
+
+ if ( st.st_size != msg_len )
+ err = 1;
+
+ else
+ err = 0;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/stat/stat_6.c b/testsuite/libsmbclient/src/stat/stat_6.c
new file mode 100644
index 00000000000..071bdd0f2f0
--- /dev/null
+++ b/testsuite/libsmbclient/src/stat/stat_6.c
@@ -0,0 +1,80 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ unsigned int mode_mask = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+ struct stat st;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ smbc_stat(url,&st);
+
+ free(message);
+
+ mode_mask = mode_mask | S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; /* 0666 or 33206 */
+
+ if ( st.st_mode != mode_mask )
+ err = 1;
+
+ else
+ err = 0;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/telldir/telldir_1.c b/testsuite/libsmbclient/src/telldir/telldir_1.c
new file mode 100644
index 00000000000..725033f453c
--- /dev/null
+++ b/testsuite/libsmbclient/src/telldir/telldir_1.c
@@ -0,0 +1,102 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int dh = 0;
+ int entry_num = 0;
+ int i = 0;
+ char *file_name;
+ char *tmp_file_ptr;
+
+ char buff[MAX_BUFF_SIZE];
+ char url[MAX_BUFF_SIZE];
+ char file_url[MAX_BUFF_SIZE];
+ char dir_url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+ bzero(file_url,MAX_BUFF_SIZE);
+ bzero(dir_url,MAX_BUFF_SIZE);
+ bzero(buff,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+ smbc_init(auth_fn, 0);
+
+ strncpy(file_url,"tempfile-",9);
+ tmp_file_ptr = file_url;
+ tmp_file_ptr += 9;
+
+ smbc_rmdir(url);
+ smbc_mkdir(url,0666);
+
+ entry_num = atoi(argv[5]);
+ strcat(dir_url,url);
+ strcat(dir_url,"/");
+
+ file_name = dir_url;
+ file_name += strlen(dir_url);
+
+ for ( i = 0; i < entry_num; i++ )
+ {
+ sprintf(buff,"%d",i);
+ memcpy(tmp_file_ptr,buff,strlen(buff)+4);
+ strncat(tmp_file_ptr,".txt",4);
+ strcpy(file_name,file_url);
+ fd = smbc_open(dir_url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ }
+
+ dh = smbc_opendir(url);
+ /* printf("directory handle: %i\n",dh); */
+ err = smbc_telldir(dh);
+ /* printf("err: %i\n",err); */
+
+ if ( err < 0 )
+
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/telldir/telldir_2.c b/testsuite/libsmbclient/src/telldir/telldir_2.c
new file mode 100644
index 00000000000..6971b79b0f9
--- /dev/null
+++ b/testsuite/libsmbclient/src/telldir/telldir_2.c
@@ -0,0 +1,95 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int dh = 0;
+ int entry_num = 0;
+ int i = 0;
+ char *file_name;
+ char *tmp_file_ptr;
+
+ char buff[MAX_BUFF_SIZE];
+ char url[MAX_BUFF_SIZE];
+ char file_url[MAX_BUFF_SIZE];
+ char dir_url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+ bzero(file_url,MAX_BUFF_SIZE);
+ bzero(dir_url,MAX_BUFF_SIZE);
+ bzero(buff,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+ smbc_init(auth_fn, 0);
+
+ strncpy(file_url,"tempfile-",9);
+ tmp_file_ptr = file_url;
+ tmp_file_ptr += 9;
+
+ smbc_rmdir(url);
+ smbc_mkdir(url,0666);
+
+ entry_num = atoi(argv[5]);
+ strcat(dir_url,url);
+ strcat(dir_url,"/");
+
+ file_name = dir_url;
+ file_name += strlen(dir_url);
+
+ for ( i = 0; i < entry_num; i++ )
+ {
+ sprintf(buff,"%d",i);
+ memcpy(tmp_file_ptr,buff,strlen(buff)+4);
+ strncat(tmp_file_ptr,".txt",4);
+ strcpy(file_name,file_url);
+ fd = smbc_open(dir_url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ }
+
+ dh = smbc_opendir(url);
+ smbc_telldir(dh);
+
+ err = errno;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/telldir/telldir_3.c b/testsuite/libsmbclient/src/telldir/telldir_3.c
new file mode 100644
index 00000000000..06ba59538ae
--- /dev/null
+++ b/testsuite/libsmbclient/src/telldir/telldir_3.c
@@ -0,0 +1,67 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int dh = 0;
+
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+
+ dh = smbc_opendir(url);
+ /* printf("directory handle: %i\n",dh); */
+ err = smbc_telldir(dh);
+ /* printf("err: %i\n",err); */
+
+ if ( err < 0 )
+
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/telldir/telldir_4.c b/testsuite/libsmbclient/src/telldir/telldir_4.c
new file mode 100644
index 00000000000..14184237d76
--- /dev/null
+++ b/testsuite/libsmbclient/src/telldir/telldir_4.c
@@ -0,0 +1,61 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int dh = 0;
+
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+
+ dh = smbc_opendir(url);
+ /* printf("directory handle: %i\n",dh); */
+ smbc_telldir(dh);
+ err = errno;
+ /* printf("err: %i\n",err); */
+
+
+ }
+
+ return err;
+
+}
diff --git a/testsuite/libsmbclient/src/telldir/telldir_5.c b/testsuite/libsmbclient/src/telldir/telldir_5.c
new file mode 100644
index 00000000000..9172b2d1aba
--- /dev/null
+++ b/testsuite/libsmbclient/src/telldir/telldir_5.c
@@ -0,0 +1,122 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int dh = 0;
+ int entry_num = 0;
+ int i = 0;
+ int offset = 0;
+ int dirsize = 0;
+ char *file_name;
+ char *tmp_file_ptr;
+
+ struct smbc_dirent *dirptr;
+
+
+ char buff[MAX_BUFF_SIZE];
+ char url[MAX_BUFF_SIZE];
+ char file_url[MAX_BUFF_SIZE];
+ char dir_url[MAX_BUFF_SIZE];
+ char dirbuff[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+ bzero(file_url,MAX_BUFF_SIZE);
+ bzero(dir_url,MAX_BUFF_SIZE);
+ bzero(buff,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ dirptr = (struct smbc_dirent *) dirbuff;
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+ smbc_init(auth_fn, 0);
+
+ strncpy(file_url,"tempfile-",9);
+ tmp_file_ptr = file_url;
+ tmp_file_ptr += 9;
+
+ smbc_rmdir(url);
+ smbc_mkdir(url,0666);
+
+ entry_num = atoi(argv[5]);
+ strcat(dir_url,url);
+ strcat(dir_url,"/");
+
+ file_name = dir_url;
+ file_name += strlen(dir_url);
+
+ for ( i = 0; i < entry_num; i++ )
+ {
+ sprintf(buff,"%d",i);
+ memcpy(tmp_file_ptr,buff,strlen(buff)+4);
+ strncat(tmp_file_ptr,".txt",4);
+ strcpy(file_name,file_url);
+ fd = smbc_open(dir_url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ }
+
+ dh = smbc_opendir(url);
+ err = 0;
+
+ while ( 1 )
+ {
+ dirptr = smbc_readdir(dh);
+
+ if ( dirptr == NULL )
+ {
+ break;
+ }
+
+ (char*)dirsize += dirptr->dirlen;
+
+ }
+
+ offset = smbc_telldir(dh);
+
+ if ( offset != dirsize )
+ {
+ /* printf("offset: %i dirsize: %i\n",offset,dirsize); */
+ err = 1;
+ }
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/unlink/unlink_1.c b/testsuite/libsmbclient/src/unlink/unlink_1.c
new file mode 100644
index 00000000000..12e9d1d0aa8
--- /dev/null
+++ b/testsuite/libsmbclient/src/unlink/unlink_1.c
@@ -0,0 +1,61 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ fd = smbc_open(url, O_RDWR | O_CREAT, 0666);
+ err = smbc_unlink(url);
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/unlink/unlink_10.c b/testsuite/libsmbclient/src/unlink/unlink_10.c
new file mode 100644
index 00000000000..58f541c69a1
--- /dev/null
+++ b/testsuite/libsmbclient/src/unlink/unlink_10.c
@@ -0,0 +1,62 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ fd = smbc_open(url, O_RDWR | O_CREAT, 0000);
+ smbc_close(fd);
+
+ strncpy(g_username, "xxxxxx", 6);
+ strncpy(g_password, "xxxxxx", 6);
+
+ smbc_unlink(url);
+
+ err = errno;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/unlink/unlink_11.c b/testsuite/libsmbclient/src/unlink/unlink_11.c
new file mode 100644
index 00000000000..f94b93d0482
--- /dev/null
+++ b/testsuite/libsmbclient/src/unlink/unlink_11.c
@@ -0,0 +1,66 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ fd = smbc_open(url, O_RDWR | O_CREAT, 0000);
+ smbc_close(fd);
+
+ strncpy(g_username, "xxxxxx", 6);
+ strncpy(g_password, "xxxxxx", 6);
+
+ err = smbc_unlink(url);
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/unlink/unlink_12.c b/testsuite/libsmbclient/src/unlink/unlink_12.c
new file mode 100644
index 00000000000..f6082595a35
--- /dev/null
+++ b/testsuite/libsmbclient/src/unlink/unlink_12.c
@@ -0,0 +1,65 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ fd = smbc_open(url, O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ smbc_unlink(url);
+
+ fd = smbc_open(url, O_RDWR, 0666);
+
+ if ( fd < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/unlink/unlink_2.c b/testsuite/libsmbclient/src/unlink/unlink_2.c
new file mode 100644
index 00000000000..3980704847f
--- /dev/null
+++ b/testsuite/libsmbclient/src/unlink/unlink_2.c
@@ -0,0 +1,61 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ fd = smbc_mkdir(url, 0666);
+ err = smbc_unlink(url);
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/unlink/unlink_3.c b/testsuite/libsmbclient/src/unlink/unlink_3.c
new file mode 100644
index 00000000000..551a651ae45
--- /dev/null
+++ b/testsuite/libsmbclient/src/unlink/unlink_3.c
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ fd = smbc_mkdir(url, 0666);
+ smbc_unlink(url);
+
+ err = errno;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/unlink/unlink_4.c b/testsuite/libsmbclient/src/unlink/unlink_4.c
new file mode 100644
index 00000000000..309dc39639d
--- /dev/null
+++ b/testsuite/libsmbclient/src/unlink/unlink_4.c
@@ -0,0 +1,64 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 7 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[6],strlen(argv[6]));
+
+ smbc_init(auth_fn, 0);
+ fd = smbc_open(url, O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ strncpy(g_username,argv[4],strlen(argv[4]));
+ strncpy(g_password,argv[5],strlen(argv[5]));
+
+ err = smbc_unlink(url);
+
+ if ( err < 0 )
+ err = 1;
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/unlink/unlink_5.c b/testsuite/libsmbclient/src/unlink/unlink_5.c
new file mode 100644
index 00000000000..73a8e057a6b
--- /dev/null
+++ b/testsuite/libsmbclient/src/unlink/unlink_5.c
@@ -0,0 +1,62 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ fd = smbc_open(url, O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+ err = smbc_unlink(url);
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/unlink/unlink_6.c b/testsuite/libsmbclient/src/unlink/unlink_6.c
new file mode 100644
index 00000000000..334c3653d1f
--- /dev/null
+++ b/testsuite/libsmbclient/src/unlink/unlink_6.c
@@ -0,0 +1,58 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ fd = smbc_open(url, O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+ smbc_unlink(url);
+
+ err = errno;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/unlink/unlink_7.c b/testsuite/libsmbclient/src/unlink/unlink_7.c
new file mode 100644
index 00000000000..cda3c9d7b07
--- /dev/null
+++ b/testsuite/libsmbclient/src/unlink/unlink_7.c
@@ -0,0 +1,62 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 7 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[6],strlen(argv[6]));
+
+ smbc_init(auth_fn, 0);
+ fd = smbc_open(url, O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ strncpy(g_username,argv[4],strlen(argv[4]));
+ strncpy(g_password,argv[5],strlen(argv[5]));
+
+ smbc_unlink(url);
+
+ err = errno;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/unlink/unlink_8.c b/testsuite/libsmbclient/src/unlink/unlink_8.c
new file mode 100644
index 00000000000..2053be3c961
--- /dev/null
+++ b/testsuite/libsmbclient/src/unlink/unlink_8.c
@@ -0,0 +1,55 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+
+ err = errno;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/unlink/unlink_9.c b/testsuite/libsmbclient/src/unlink/unlink_9.c
new file mode 100644
index 00000000000..78f9c297481
--- /dev/null
+++ b/testsuite/libsmbclient/src/unlink/unlink_9.c
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ char url[MAX_BUFF_SIZE];
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ err = smbc_unlink(url);
+
+ if ( err < 0 )
+ err = 1;
+
+
+ }
+
+ return 1;
+
+}
+
diff --git a/testsuite/libsmbclient/src/unlink_print_job/unlink_print_job_1.c b/testsuite/libsmbclient/src/unlink_print_job/unlink_print_job_1.c
new file mode 100644
index 00000000000..b19bf228de1
--- /dev/null
+++ b/testsuite/libsmbclient/src/unlink_print_job/unlink_print_job_1.c
@@ -0,0 +1,107 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+char g_print_user[MAX_BUFF_SIZE];
+char g_print_name[MAX_BUFF_SIZE];
+unsigned int g_print_id;
+unsigned int g_print_priority;
+unsigned int g_print_size;
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+void print_list_fn(struct print_job_info *pji)
+{
+
+ g_print_id = pji->id;
+ g_print_priority = pji->priority;
+ g_print_size = pji->size;
+ strcpy(g_print_user,pji->user);
+ strcpy(g_print_name,pji->name);
+
+ /* fprintf(stdout, "Print job: ID: %u, Prio: %u, Size: %u, User: %s, Name: %s\n",
+ pji->id, pji->priority, pji->size, pji->user, pji->name); */
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+ bzero(g_print_user,MAX_BUFF_SIZE);
+ bzero(g_print_name,MAX_BUFF_SIZE);
+
+ g_print_id = 0;
+ g_print_priority = 0;
+ g_print_size = 0;
+
+ if ( argc == 7 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+ /* printf("Message: %s\n",message); */
+ /* printf("Message len: %i\n",msg_len); */
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ free(message);
+ smbc_print_file(url,argv[6]);
+ smbc_list_print_jobs(argv[6],print_list_fn);
+
+ g_print_id = -1;
+ err = smbc_unlink_print_job(argv[6],g_print_id);
+
+ if ( err < 0 )
+
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/unlink_print_job/unlink_print_job_2.c b/testsuite/libsmbclient/src/unlink_print_job/unlink_print_job_2.c
new file mode 100644
index 00000000000..52f127c3629
--- /dev/null
+++ b/testsuite/libsmbclient/src/unlink_print_job/unlink_print_job_2.c
@@ -0,0 +1,102 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+char g_print_user[MAX_BUFF_SIZE];
+char g_print_name[MAX_BUFF_SIZE];
+unsigned int g_print_id;
+unsigned int g_print_priority;
+unsigned int g_print_size;
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+void print_list_fn(struct print_job_info *pji)
+{
+
+ g_print_id = pji->id;
+ g_print_priority = pji->priority;
+ g_print_size = pji->size;
+ strcpy(g_print_user,pji->user);
+ strcpy(g_print_name,pji->name);
+
+ /* fprintf(stdout, "Print job: ID: %u, Prio: %u, Size: %u, User: %s, Name: %s\n",
+ pji->id, pji->priority, pji->size, pji->user, pji->name); */
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+ bzero(g_print_user,MAX_BUFF_SIZE);
+ bzero(g_print_name,MAX_BUFF_SIZE);
+
+ g_print_id = 0;
+ g_print_priority = 0;
+ g_print_size = 0;
+
+ if ( argc == 7 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+ /* printf("Message: %s\n",message); */
+ /* printf("Message len: %i\n",msg_len); */
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ free(message);
+ smbc_print_file(url,argv[6]);
+ smbc_list_print_jobs(argv[6],print_list_fn);
+
+ g_print_id = -1;
+ smbc_unlink_print_job(argv[6],g_print_id);
+
+ err = errno;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/unlink_print_job/unlink_print_job_3.c b/testsuite/libsmbclient/src/unlink_print_job/unlink_print_job_3.c
new file mode 100644
index 00000000000..5686b3f53ed
--- /dev/null
+++ b/testsuite/libsmbclient/src/unlink_print_job/unlink_print_job_3.c
@@ -0,0 +1,106 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+char g_print_user[MAX_BUFF_SIZE];
+char g_print_name[MAX_BUFF_SIZE];
+unsigned int g_print_id;
+unsigned int g_print_priority;
+unsigned int g_print_size;
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+void print_list_fn(struct print_job_info *pji)
+{
+
+ g_print_id = pji->id;
+ g_print_priority = pji->priority;
+ g_print_size = pji->size;
+ strcpy(g_print_user,pji->user);
+ strcpy(g_print_name,pji->name);
+
+ /* fprintf(stdout, "Print job: ID: %u, Prio: %u, Size: %u, User: %s, Name: %s\n",
+ pji->id, pji->priority, pji->size, pji->user, pji->name); */
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+ bzero(g_print_user,MAX_BUFF_SIZE);
+ bzero(g_print_name,MAX_BUFF_SIZE);
+
+ g_print_id = 0;
+ g_print_priority = 0;
+ g_print_size = 0;
+
+ if ( argc == 7 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+ /* printf("Message: %s\n",message); */
+ /* printf("Message len: %i\n",msg_len); */
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ free(message);
+ smbc_print_file(url,argv[6]);
+ smbc_list_print_jobs(argv[6],print_list_fn);
+
+ err = smbc_unlink_print_job(argv[6],g_print_id);
+
+ if ( err < 0 )
+
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/unlink_print_job/unlink_print_job_4.c b/testsuite/libsmbclient/src/unlink_print_job/unlink_print_job_4.c
new file mode 100644
index 00000000000..28966f8af4d
--- /dev/null
+++ b/testsuite/libsmbclient/src/unlink_print_job/unlink_print_job_4.c
@@ -0,0 +1,101 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+char g_print_user[MAX_BUFF_SIZE];
+char g_print_name[MAX_BUFF_SIZE];
+unsigned int g_print_id;
+unsigned int g_print_priority;
+unsigned int g_print_size;
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+void print_list_fn(struct print_job_info *pji)
+{
+
+ g_print_id = pji->id;
+ g_print_priority = pji->priority;
+ g_print_size = pji->size;
+ strcpy(g_print_user,pji->user);
+ strcpy(g_print_name,pji->name);
+
+ /* fprintf(stdout, "Print job: ID: %u, Prio: %u, Size: %u, User: %s, Name: %s\n",
+ pji->id, pji->priority, pji->size, pji->user, pji->name); */
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+ bzero(g_print_user,MAX_BUFF_SIZE);
+ bzero(g_print_name,MAX_BUFF_SIZE);
+
+ g_print_id = 0;
+ g_print_priority = 0;
+ g_print_size = 0;
+
+ if ( argc == 7 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+ /* printf("Message: %s\n",message); */
+ /* printf("Message len: %i\n",msg_len); */
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ free(message);
+ smbc_print_file(url,argv[6]);
+ smbc_list_print_jobs(argv[6],print_list_fn);
+
+ smbc_unlink_print_job(argv[6],g_print_id);
+
+ err = errno;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/unlink_print_job/unlink_print_job_5.c b/testsuite/libsmbclient/src/unlink_print_job/unlink_print_job_5.c
new file mode 100644
index 00000000000..4fb3e30017b
--- /dev/null
+++ b/testsuite/libsmbclient/src/unlink_print_job/unlink_print_job_5.c
@@ -0,0 +1,141 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+char g_print_user[MAX_BUFF_SIZE];
+char g_print_name[MAX_BUFF_SIZE];
+unsigned int g_print_id;
+unsigned int g_print_priority;
+unsigned int g_print_size;
+
+unsigned int print_ids[MAX_BUFF_SIZE];
+unsigned int print_id_count;
+int call_flag;
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+void print_list_fn_2(struct print_job_info *pji)
+{
+
+ print_ids[print_id_count] = pji->id;
+ print_id_count++;
+
+ /* fprintf(stdout, "Call to Second Print Function - Print job: ID: %u, Prio: %u, Size: %u, User: %s, Name: %s\n",
+ pji->id, pji->priority, pji->size, pji->user, pji->name); */
+
+}
+
+void print_list_fn(struct print_job_info *pji)
+{
+
+ g_print_id = pji->id;
+ g_print_priority = pji->priority;
+ g_print_size = pji->size;
+ strcpy(g_print_user,pji->user);
+ strcpy(g_print_name,pji->name);
+
+ /* fprintf(stdout, "Call to First Print Function - Print job: ID: %u, Prio: %u, Size: %u, User: %s, Name: %s\n",
+ pji->id, pji->priority, pji->size, pji->user, pji->name); */
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ unsigned int i = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+ bzero(g_print_user,MAX_BUFF_SIZE);
+ bzero(g_print_name,MAX_BUFF_SIZE);
+
+ g_print_id = 0;
+ g_print_priority = 0;
+ g_print_size = 0;
+
+ print_id_count = 0;
+
+ if ( argc == 7 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+ /* printf("Message: %s\n",message); */
+ /* printf("Message len: %i\n",msg_len); */
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ free(message);
+
+ smbc_print_file(url,argv[6]);
+ smbc_print_file(url,argv[6]);
+ smbc_print_file(url,argv[6]);
+
+ smbc_list_print_jobs(argv[6],print_list_fn);
+
+ if ( smbc_unlink_print_job(argv[6],g_print_id) == 0 )
+ {
+ if ( smbc_list_print_jobs(argv[6],print_list_fn_2) == 0 )
+ {
+ err = 0;
+
+ for ( i=0; i<print_id_count; i++ )
+ {
+ if ( g_print_id == print_ids[i] )
+ {
+ err = 1;
+ break;
+ }
+
+ }
+ }
+
+ } else
+ err = 1;
+
+
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/write/write_1.c b/testsuite/libsmbclient/src/write/write_1.c
new file mode 100644
index 00000000000..fa147f8f503
--- /dev/null
+++ b/testsuite/libsmbclient/src/write/write_1.c
@@ -0,0 +1,77 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+
+ err = smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ free(message);
+
+ if ( err < 0 )
+ err = 1;
+
+ else if ( err != msg_len )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/write/write_10.c b/testsuite/libsmbclient/src/write/write_10.c
new file mode 100644
index 00000000000..1f43c60bb75
--- /dev/null
+++ b/testsuite/libsmbclient/src/write/write_10.c
@@ -0,0 +1,70 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message = NULL;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ msg_len = 10;
+ fd = smbc_open(url, O_RDWR, 0666);
+ smbc_write(fd, message, msg_len);
+
+ err = errno;
+
+ smbc_close(fd);
+
+ free(message);
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/write/write_11.c b/testsuite/libsmbclient/src/write/write_11.c
new file mode 100644
index 00000000000..3f9ae3f97da
--- /dev/null
+++ b/testsuite/libsmbclient/src/write/write_11.c
@@ -0,0 +1,83 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+ char* response;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ response = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ fd = smbc_open(url, O_RDWR, 0666);
+ smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ fd = smbc_open(url, O_RDONLY, 0666);
+ smbc_read(fd,response,msg_len);
+ smbc_close(fd);
+
+ if ( memcmp ( message, response, msg_len) == 0 )
+ err = 0;
+
+ else
+ err = 1;
+
+ free(message);
+ free(response);
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/write/write_12.c b/testsuite/libsmbclient/src/write/write_12.c
new file mode 100644
index 00000000000..3528dbb36e2
--- /dev/null
+++ b/testsuite/libsmbclient/src/write/write_12.c
@@ -0,0 +1,83 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+ char* response;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ response = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ strncpy(g_username,"xxxxxxxx",8);
+ strncpy(g_password,"xxxxxxxx",8);
+
+ fd = smbc_open(url, O_RDWR, 0666);
+
+ err = smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+ free(message);
+ free(response);
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/write/write_13.c b/testsuite/libsmbclient/src/write/write_13.c
new file mode 100644
index 00000000000..f3c2ad5bc3f
--- /dev/null
+++ b/testsuite/libsmbclient/src/write/write_13.c
@@ -0,0 +1,87 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+ char* response;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ response = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ strncpy(g_username,"xxxxxxxx",8);
+ strncpy(g_password,"xxxxxxxx",8);
+
+ fd = smbc_open(url, O_RDWR, 0666);
+
+ if (fd < 0)
+
+ err = errno;
+
+ else {
+
+ smbc_write(fd, message, msg_len);
+ err = errno;
+
+ smbc_close(fd);
+
+ }
+
+ free(message);
+ free(response);
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/write/write_2.c b/testsuite/libsmbclient/src/write/write_2.c
new file mode 100644
index 00000000000..49763ebe0cd
--- /dev/null
+++ b/testsuite/libsmbclient/src/write/write_2.c
@@ -0,0 +1,71 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+
+ smbc_write(fd, message, msg_len);
+ err = errno;
+
+ smbc_close(fd);
+
+ free(message);
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/write/write_3.c b/testsuite/libsmbclient/src/write/write_3.c
new file mode 100644
index 00000000000..254782946d7
--- /dev/null
+++ b/testsuite/libsmbclient/src/write/write_3.c
@@ -0,0 +1,79 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ fd = smbc_open(url, O_RDONLY, 0666);
+ err = smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ free(message);
+
+ if ( err < 0 )
+ err = 1;
+
+ else if ( err != msg_len )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/write/write_4.c b/testsuite/libsmbclient/src/write/write_4.c
new file mode 100644
index 00000000000..6ee585f0210
--- /dev/null
+++ b/testsuite/libsmbclient/src/write/write_4.c
@@ -0,0 +1,74 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ fd = smbc_open(url, O_RDONLY, 0666);
+
+ smbc_write(fd, message, msg_len);
+ err = errno;
+
+ smbc_close(fd);
+
+ free(message);
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/write/write_5.c b/testsuite/libsmbclient/src/write/write_5.c
new file mode 100644
index 00000000000..84cb50123cc
--- /dev/null
+++ b/testsuite/libsmbclient/src/write/write_5.c
@@ -0,0 +1,79 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ fd = smbc_open(url, O_WRONLY, 0666);
+ err = smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ free(message);
+
+ if ( err < 0 )
+ err = 1;
+
+ else if ( err != msg_len )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/write/write_6.c b/testsuite/libsmbclient/src/write/write_6.c
new file mode 100644
index 00000000000..c139ff81379
--- /dev/null
+++ b/testsuite/libsmbclient/src/write/write_6.c
@@ -0,0 +1,74 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 6 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ msg_len = strlen(argv[5])+1;
+ message = malloc(msg_len);
+ message[msg_len - 1] = 0;
+ strncpy(message,argv[5],msg_len);
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ fd = smbc_open(url, O_WRONLY, 0666);
+
+ smbc_write(fd, message, msg_len);
+ err = errno;
+
+ smbc_close(fd);
+
+ free(message);
+
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/write/write_7.c b/testsuite/libsmbclient/src/write/write_7.c
new file mode 100644
index 00000000000..bc509f1efd3
--- /dev/null
+++ b/testsuite/libsmbclient/src/write/write_7.c
@@ -0,0 +1,60 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char* message = "Testing";
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+
+ if ( argc == 4 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+
+ fd = 10345; /* Random value for File Descriptor */
+ smbc_init(auth_fn, 0);
+ err = smbc_write(fd, message, sizeof(message));
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/write/write_8.c b/testsuite/libsmbclient/src/write/write_8.c
new file mode 100644
index 00000000000..acc20f19586
--- /dev/null
+++ b/testsuite/libsmbclient/src/write/write_8.c
@@ -0,0 +1,56 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ char* message = "Testing";
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+
+ if ( argc == 4 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+
+ fd = 10345; /* Random value for File Descriptor */
+ smbc_init(auth_fn, 0);
+ err = smbc_write(fd, message, sizeof(message));
+
+ err = errno;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/libsmbclient/src/write/write_9.c b/testsuite/libsmbclient/src/write/write_9.c
new file mode 100644
index 00000000000..b707d5c688e
--- /dev/null
+++ b/testsuite/libsmbclient/src/write/write_9.c
@@ -0,0 +1,72 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libsmbclient.h>
+
+#define MAX_BUFF_SIZE 255
+char g_workgroup[MAX_BUFF_SIZE];
+char g_username[MAX_BUFF_SIZE];
+char g_password[MAX_BUFF_SIZE];
+char g_server[MAX_BUFF_SIZE];
+char g_share[MAX_BUFF_SIZE];
+
+
+void auth_fn(const char *server, const char *share, char *workgroup, int wgmaxlen,
+ char *username, int unmaxlen, char *password, int pwmaxlen)
+{
+
+ strncpy(workgroup, g_workgroup, wgmaxlen - 1);
+
+ strncpy(username, g_username, unmaxlen - 1);
+
+ strncpy(password, g_password, pwmaxlen - 1);
+
+ strcpy(g_server, server);
+ strcpy(g_share, share);
+
+}
+
+int main(int argc, char** argv)
+{
+ int err = -1;
+ int fd = 0;
+ int msg_len = 0;
+ char url[MAX_BUFF_SIZE];
+ char* message = NULL;
+
+ bzero(g_workgroup,MAX_BUFF_SIZE);
+ bzero(url,MAX_BUFF_SIZE);
+
+ if ( argc == 5 )
+ {
+
+ strncpy(g_workgroup,argv[1],strlen(argv[1]));
+ strncpy(g_username,argv[2],strlen(argv[2]));
+ strncpy(g_password,argv[3],strlen(argv[3]));
+ strncpy(url,argv[4],strlen(argv[4]));
+
+ smbc_init(auth_fn, 0);
+ smbc_unlink(url);
+ fd = smbc_open(url,O_RDWR | O_CREAT, 0666);
+ smbc_close(fd);
+
+ msg_len = 10;
+ fd = smbc_open(url, O_RDWR, 0666);
+ err = smbc_write(fd, message, msg_len);
+ smbc_close(fd);
+
+ free(message);
+
+ if ( err < 0 )
+ err = 1;
+
+ else
+ err = 0;
+
+ }
+
+ return err;
+
+}
+
diff --git a/testsuite/nsswitch/.cvsignore b/testsuite/nsswitch/.cvsignore
new file mode 100644
index 00000000000..1c30875a884
--- /dev/null
+++ b/testsuite/nsswitch/.cvsignore
@@ -0,0 +1,12 @@
+initgroups
+nss_winbind_syms
+getgrent_r
+getgrgid
+getgrnam
+getpwent_r
+getpwnam
+wbtorture
+leaktest?
+getpwuid
+getent_pwent
+getent_grent
diff --git a/testsuite/nsswitch/Makefile.longarg b/testsuite/nsswitch/Makefile.longarg
new file mode 100644
index 00000000000..6cc7ef8306d
--- /dev/null
+++ b/testsuite/nsswitch/Makefile.longarg
@@ -0,0 +1,5 @@
+#
+# Makefile for null tests
+#
+
+longarg_getpwnam: longarg_getpwnam.o \ No newline at end of file
diff --git a/testsuite/nsswitch/bigfd.c b/testsuite/nsswitch/bigfd.c
new file mode 100644
index 00000000000..99e402e0f33
--- /dev/null
+++ b/testsuite/nsswitch/bigfd.c
@@ -0,0 +1,38 @@
+/*
+ * Test maximum number of file descriptors winbind daemon can handle
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <pwd.h>
+#include <sys/types.h>
+
+int main(int argc, char **argv)
+{
+ struct passwd *pw;
+ int i;
+
+ while(1) {
+
+ /* Start getpwent until we get an NT user. This way we know we
+ have at least opened a connection to the winbind daemon */
+
+ setpwent();
+
+ while((pw = getpwent()) != NULL) {
+ if (strchr(pw->pw_name, '/') != NULL) {
+ break;
+ }
+ }
+
+ if (pw != NULL) {
+ i++;
+ printf("got pwent handle %d\n", i);
+ } else {
+ printf("winbind daemon not running?\n");
+ exit(1);
+ }
+
+ sleep(1);
+ }
+}
diff --git a/testsuite/nsswitch/bigfd.exp b/testsuite/nsswitch/bigfd.exp
new file mode 100644
index 00000000000..62fc9ea7f35
--- /dev/null
+++ b/testsuite/nsswitch/bigfd.exp
@@ -0,0 +1,28 @@
+#
+# @(#) Test maximum number of clients (file descriptors) for winbindd
+#
+
+load_lib util-defs.exp
+
+# Unimplemented - eek!
+
+untested "bigfd"
+return
+
+# Compile bigfd.c
+
+set output [target_compile "$srcdir/$subdir/bigfd.c" \
+ "$srcdir/$subdir/bigfd" executable {additional_flags="-g"}]
+
+if {$output != ""} {
+ perror "compile bigfd"
+ puts $output
+ return
+}
+
+# Run bigfd
+
+set output [util_start "$srcdir/$subdir/bigfd" "" ""]
+puts $output
+
+pass "bigfd"
diff --git a/testsuite/nsswitch/domusers.exp b/testsuite/nsswitch/domusers.exp
new file mode 100644
index 00000000000..3b291ab398b
--- /dev/null
+++ b/testsuite/nsswitch/domusers.exp
@@ -0,0 +1,38 @@
+#
+# @(#) Test that all users are members of the Domain Users group.
+#
+# Note that this isn't necessarily true all the time but you have to
+# explicitly move people out of that group so it should be OK for te
+#
+
+load_lib util-defs.exp
+load_lib $srcdir/lib/nsswitch-config.exp
+
+# Get list of users and stick usernames in a hash
+
+set user_list [util_start "getent" "passwd" ""]
+
+foreach { user } [split $user_list "\n"] {
+ set user_elts [split $user ":"]
+ set users([lindex $user_elts 0]) 1
+}
+
+# Get list of groups
+
+set group_list [util_start "getent" "group" ""]
+
+foreach { group } [split $group_list "\n"] {
+ set group_elts [split $group ":"]
+
+ # Look for domain users group
+
+ if { ![regexp "Domain Users" [lindex $group_elts 0]] } {
+ continue
+ }
+
+ # Check each member of group was found in getent passwd
+
+ foreach { mem } [split [lindex $group_elts 3] ","] {
+ set mems($mem) 1
+ }
+}
diff --git a/testsuite/nsswitch/envvar.exp b/testsuite/nsswitch/envvar.exp
new file mode 100644
index 00000000000..134a8b37a85
--- /dev/null
+++ b/testsuite/nsswitch/envvar.exp
@@ -0,0 +1,282 @@
+#
+# @(#) Test operation of WINBINDD_DOMAIN environment variable
+#
+
+load_lib "util-defs.exp"
+load_lib "$srcdir/lib/nsswitch-config.exp"
+
+#
+# @(#) Test that there is at least one domain user and domain group
+# @(#) in the output of getent passwd and getent group.
+#
+
+# Get list of users and groups
+
+set user_list [util_start "getent passwd"]
+set group_list [util_start "getent group"]
+
+verbose "user list is:\n$user_list"
+verbose "group list is:\n$group_list"
+
+# Check for domain users
+
+set no_dom 0
+
+if { ![regexp "$domain/" $user_list] } {
+ fail "no domain users in getent"
+ set no_dom 1
+}
+
+# Check for domain groups
+
+if { ![regexp "$domain/" $group_list] } {
+ fail "no domain groups in getent group"
+ set no_dom 1
+}
+
+if { $no_dom } {
+ return
+}
+
+#
+# @(#) Check for "leakage" between different domains using the
+# @(#) WINBINDD_DOMAIN environment variable.
+#
+
+verbose "Domain is $domain"
+
+set output [util_start "bin/wbinfo" "-m"]
+verbose "Trusted domains are $output"
+set trusted_domain_list [split $output "\n"]
+
+# Test simple inclusion by setting $WINBINDD_DOMAIN to each trusted domain
+# in turn and checking there are no users/groups from other domains in the
+# output of getent.
+
+set domain_list $trusted_domain_list
+lappend domain_list $domain
+
+foreach { the_domain } $domain_list {
+
+ set env(WINBINDD_DOMAIN) $the_domain
+
+ set user_out [util_start "getent passwd"]
+ set group_out [util_start "getent group"]
+
+ verbose "users in $the_domain:\n$user_out\n"
+ verbose "groups in $the_domain:\n$group_out\n"
+
+ # Users
+
+ set test_desc "users in WINBINDD_DOMAIN $the_domain"
+ set failed 0
+
+ foreach { user } [split $user_out "\n"] {
+ set user_name [lindex [split $user ":"] 0]
+ if { [regexp "/" $user_name] && ![regexp $the_domain $user_name]} {
+ set failed 1
+ }
+ }
+
+ if { $failed } {
+ fail $test_desc
+ } else {
+ pass $test_desc
+ }
+
+ # Groups
+
+ set test_desc "groups in WINBINDD_DOMAIN $the_domain"
+ set failed 0
+
+ foreach { group } [split $group_out "\n"] {
+ set group_name [lindex [split $group ":"] 0]
+ if { [regexp "/" $group_name] && ![regexp $the_domain $group_name]} {
+ set failed 1
+ }
+ }
+
+ if { $failed } {
+ fail $test_desc
+ } else {
+ pass $test_desc
+ }
+}
+
+#
+# @(#) Test inclusion of a dummy domain doesn't generate users/groups
+# @(#) from that domain.
+#
+
+set env(WINBINDD_DOMAIN) "asmithee"
+set user_out [util_start "getent passwd"]
+set group_out [util_start "getent group"]
+
+# Users
+
+set test_desc "users in different WINBINDD_DOMAIN"
+if { [regexp $domain $user_out] } {
+ fail $test_desc
+} else {
+ pass $test_desc
+}
+
+# Groups
+
+set test_desc "groups in different WINBINDD_DOMAIN"
+if { [regexp $domain $group_out] } {
+ fail $test_desc
+} else {
+ pass $test_desc
+}
+
+#
+# @(#) Test comma separated inclusion of dummy domain doesn't generate
+# @(#) users/groups in the dummy domain.
+#
+
+foreach { the_domain } $domain_list {
+ set env(WINBINDD_DOMAIN) "$the_domain,asmithee"
+ set user_out [util_start "getent passwd"]
+ set group_out [util_start "getent group"]
+
+ verbose "users in $the_domain:\n$user_out\n"
+ verbose "groups in $the_domain:\n$group_out\n"
+
+ # Users
+
+ set test_desc "users in comma separated WINBINDD_DOMAIN $the_domain"
+ set failed 0
+
+ foreach { user } [split $user_out "\n"] {
+ set user_name [lindex [split $user ":"] 0]
+ if { [regexp "/" $user_name] && ![regexp $the_domain $user_name]} {
+ set failed 1
+ }
+ }
+
+ if { $failed } {
+ fail $test_desc
+ } else {
+ pass $test_desc
+ }
+
+ # Groups
+
+ set test_desc "groups in comma separated WINBINDD_DOMAIN $the_domain"
+ set failed 0
+
+ foreach { group } [split $group_out "\n"] {
+ set group_name [lindex [split $group ":"] 0]
+ if { [regexp "/" $group_name] && ![regexp $the_domain $group_name]} {
+ set failed 1
+ }
+ }
+
+ if { $failed } {
+ fail $test_desc
+ } else {
+ pass $test_desc
+ }
+}
+
+#
+# @(#) Test two comma separated dummy domains do not generate any domain
+# @(#) users or groups.
+#
+
+foreach { the_domain } $domain_list {
+
+ set env(WINBINDD_DOMAIN) "moose,asmithee"
+ set user_out [util_start "getent passwd"]
+ set group_out [util_start "getent group"]
+
+ verbose "users in $the_domain:\n$user_out\n"
+ verbose "groups in $the_domain:\n$group_out\n"
+
+ # Users
+
+ set test_desc "users in comma separated invalid WINBINDD_DOMAIN"
+ if { [regexp $the_domain $user_out] } {
+ fail $test_desc
+ } else {
+ pass $test_desc
+ }
+
+ # Groups
+
+ set test_desc "groups in comma separated invalid WINBINDD_DOMAIN"
+ if { [regexp $the_domain $group_out] } {
+ fail $test_desc
+ } else {
+ pass $test_desc
+ }
+}
+
+set env(WINBINDD_DOMAIN) ""
+
+#
+# @(#) Test _NO_WINBINDD doesn't return any domain users or groups
+#
+
+set env(_NO_WINBINDD) "1"
+set user_out [util_start "getent passwd"]
+set group_out [util_start "getent group"]
+
+verbose "users with _NO_WINBINDD:\n$user_out\n"
+verbose "groups with _NO_WINBINDD:\n$group_out\n"
+
+foreach { the_domain } $domain_list {
+
+ # Users
+
+ set test_desc "users found with _NO_WINBINDD environment variable set"
+ if { [regexp $the_domain $user_out] } {
+ fail $test_desc
+ } else {
+ pass $test_desc
+ }
+
+ # Groups
+
+ set test_desc "groups found with _NO_WINBINDD environment variable set"
+ if { [regexp $the_domain $group_out] } {
+ fail $test_desc
+ } else {
+ pass $test_desc
+ }
+}
+
+# Unset _NO_WINBINDD and make sure everything still works
+
+unset env(_NO_WINBINDD)
+
+set user_out [util_start "getent passwd"]
+set group_out [util_start "getent group"]
+
+verbose "users with _NO_WINBINDD unset:\n$user_out\n"
+verbose "groups with _NO_WINBINDD unset:\n$group_out\n"
+
+# Users
+
+set test_desc "no users found with _NO_WINBINDD environment variable set"
+if { $user_out != $user_list } {
+ fail $test_desc
+} else {
+ pass $test_desc
+}
+
+# Groups
+
+set test_desc "no groups found with _NO_WINBINDD environment variable set"
+if { $group_out != $group_list } {
+ fail $test_desc
+} else {
+ pass $test_desc
+}
+
+# Make sure we unset the environment vars so we don't cause subsequent tests
+# any grief.
+
+catch { unset env(WINBINDD_DOMAIN) } tmp
+catch { unset env(_NO_WINBINDD) } tmp
diff --git a/testsuite/nsswitch/finger.exp b/testsuite/nsswitch/finger.exp
new file mode 100644
index 00000000000..36bab8e1990
--- /dev/null
+++ b/testsuite/nsswitch/finger.exp
@@ -0,0 +1,39 @@
+#
+# @(#) Test default domain users resolve using the finger command
+#
+
+load_lib util-defs.exp
+
+set output [util_start "bin/wbinfo" "-u"]
+if { [regexp "Error" $output] } {
+ fail "error running wbinfo"
+ return
+}
+
+set user_list [split $output "\n"]
+
+# Look up all users using finger -m. This should test getpwnam()
+
+foreach { user } $user_list {
+ set output [util_start "finger" "-m \"$user\"" "" "no such user"]
+ verbose $output
+
+ if { [regexp "no such user" $output] } {
+ fail "finger -m $user"
+ } else {
+ pass "finger -m $user"
+ }
+}
+
+# Run finger without the -m to also test set/get/endpwent()
+
+foreach { user } $user_list {
+ set output [util_start "finger" "\"$user\""]
+ verbose $output
+
+ if { [regexp "no such user" $output] } {
+ fail "finger $user"
+ } else {
+ pass "finger $user"
+ }
+}
diff --git a/testsuite/nsswitch/getent.c b/testsuite/nsswitch/getent.c
new file mode 100644
index 00000000000..b4c4e50c6fe
--- /dev/null
+++ b/testsuite/nsswitch/getent.c
@@ -0,0 +1,151 @@
+/* Cut down version of getent which only returns passwd and group database
+ entries and seems to compile on most systems without too much fuss.
+ Original copyright notice below. */
+
+/* Copyright (c) 1998, 1999, 2000 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998.
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <stdio.h>
+#include <pwd.h>
+#include <grp.h>
+
+group_keys (int number, char *key[])
+{
+ int result = 0;
+ int i;
+
+ for (i = 0; i < number; ++i)
+ {
+ struct group *grp;
+
+ if (isdigit (key[i][0]))
+ grp = getgrgid (atol (key[i]));
+ else
+ grp = getgrnam (key[i]);
+
+ if (grp == NULL)
+ result = 2;
+ else
+ print_group (grp);
+ }
+
+ return result;
+}
+
+passwd_keys (int number, char *key[])
+{
+ int result = 0;
+ int i;
+
+ for (i = 0; i < number; ++i)
+ {
+ struct passwd *pwd;
+
+ if (isdigit (key[i][0]))
+ pwd = getpwuid (atol (key[i]));
+ else
+ pwd = getpwnam (key[i]);
+
+ if (pwd == NULL)
+ result = 2;
+ else
+ print_passwd (pwd);
+ }
+
+ return result;
+}
+
+print_group (struct group *grp)
+{
+ unsigned int i = 0;
+
+ printf ("%s:%s:%ld:", grp->gr_name ? grp->gr_name : "",
+ grp->gr_passwd ? grp->gr_passwd : "",
+ (unsigned long)grp->gr_gid);
+
+ while (grp->gr_mem[i] != NULL)
+ {
+ fputs (grp->gr_mem[i], stdout);
+ ++i;
+ if (grp->gr_mem[i] != NULL)
+ fputs (",", stdout);
+ }
+ fputs ("\n", stdout);
+}
+
+print_passwd (struct passwd *pwd)
+{
+ printf ("%s:%s:%ld:%ld:%s:%s:%s\n",
+ pwd->pw_name ? pwd->pw_name : "",
+ pwd->pw_passwd ? pwd->pw_passwd : "",
+ (unsigned long)pwd->pw_uid,
+ (unsigned long)pwd->pw_gid,
+ pwd->pw_gecos ? pwd->pw_gecos : "",
+ pwd->pw_dir ? pwd->pw_dir : "",
+ pwd->pw_shell ? pwd->pw_shell : "");
+}
+
+int main(int argc, char **argv)
+{
+ switch(argv[1][0])
+ {
+ case 'g': /* group */
+ if (strcmp (argv[1], "group") == 0)
+ {
+ if (argc == 2)
+ {
+ struct group *grp;
+
+ setgrent ();
+ while ((grp = getgrent()) != NULL)
+ print_group (grp);
+ endgrent ();
+ }
+ else
+ return group_keys (argc - 2, &argv[2]);
+ }
+ else
+ goto error;
+ break;
+
+ case 'p': /* passwd, protocols */
+ if (strcmp (argv[1], "passwd") == 0)
+ {
+ if (argc == 2)
+ {
+ struct passwd *pwd;
+
+ setpwent ();
+ while ((pwd = getpwent()) != NULL)
+ print_passwd (pwd);
+ endpwent ();
+ }
+ else
+ return passwd_keys (argc - 2, &argv[2]);
+ }
+ else
+ goto error;
+ break;
+ default:
+ error:
+ fprintf (stderr, "Unknown database: %s\n", argv[1]);
+ return 1;
+ }
+ return 0;
+}
diff --git a/testsuite/nsswitch/getent.exp b/testsuite/nsswitch/getent.exp
new file mode 100644
index 00000000000..72bf2ea1ebe
--- /dev/null
+++ b/testsuite/nsswitch/getent.exp
@@ -0,0 +1,148 @@
+#
+# @(#) Test the getent command returns domain/local users and groups
+#
+
+load_lib util-defs.exp
+load_lib compile.exp
+load_lib $srcdir/lib/nsswitch-config.exp
+
+#
+# @(#) Test getent passwd returns domain users
+#
+
+set wbinfo_output [util_start "bin/wbinfo" "-u"]
+set getent_output [util_start "getent" "passwd" ""]
+
+if { ![regexp "$domain/" $getent_output] } {
+ fail "no domain users in getent passwd"
+ return
+}
+
+if { [regexp "Error" $wbinfo_output] } {
+ fail "wbinfo -u failed"
+ return
+}
+
+#
+# @(#) Test each user in the output of wbinfo is also in the output of
+# @(#) getent.
+#
+
+# Test wbinfo user names are in getent user names
+
+foreach { user } [split $wbinfo_output "\n"] {
+
+ verbose "looking for $user"
+
+ set test_desc "getent passwd does not contain $user"
+
+ if { ![regexp "$user" $getent_output] } {
+ fail $test_desc
+ } else {
+ pass $test_desc
+ }
+}
+
+# Test getent user names are in wbinfo user names
+
+foreach { user } [split $getent_output "\n"] {
+
+ set user_info [split $user ":"]
+ set username [lindex $user_info 0]
+
+ if { [regexp {^[^/]+/} $username] } {
+
+ set test_desc "wbinfo -u does not contain $username"
+
+ if { ![regexp "$username" $wbinfo_output] } {
+ fail $test_desc
+ } else {
+ pass $test_desc
+ }
+ } else {
+ verbose "ignoring non-domain user $username"
+ }
+}
+
+#
+# @(#) Test each group in the output of wbinfo is also in the output of
+# @(#) getent.
+#
+
+set wbinfo_output [util_start "bin/wbinfo" "-g"]
+set getent_output [util_start "getent" "group" ""]
+
+if { ![regexp "$domain/" $getent_output] } {
+ fail "no domain groups in getent passwd"
+ return
+}
+
+if { [regexp "Error" $wbinfo_output] } {
+ fail "wbinfo -g failed"
+ return
+}
+
+# Test wbinfo group names are in getent group names
+
+foreach { group } [split $wbinfo_output "\n"] {
+
+ verbose "looking for $group"
+
+ set test_desc "getent group does not contain $group"
+
+ if { ![regexp "$group" $getent_output] } {
+ fail $test_desc
+ } else {
+ pass $test_desc
+ }
+}
+
+# Test getent group names are in wbinfo group names
+
+foreach { group } [split $getent_output "\n"] {
+
+ set group_info [split $group ":"]
+ set groupname [lindex $group_info 0]
+
+ if { [regexp {^[^/]+/} $groupname] } {
+
+ set test_desc "wbinfo -g does not contain $groupname"
+
+ if { ![regexp "$groupname" $wbinfo_output] } {
+ fail $test_desc
+ } else {
+ pass $test_desc
+ }
+ } else {
+ verbose "ignoring non-domain group $groupname"
+ }
+}
+
+#
+# @(#) Test out of order and repeat calls of pwent functions
+# @(#) Test out of order and repeat calls of grent functions
+#
+
+set getent_tests [list \
+ { "out of order pwent operations" "getent_pwent" } \
+ { "out of order grent operations" "getent_grent" } \
+ ]
+
+# Compile and run each test
+
+foreach { test } $getent_tests {
+ set test_desc [lindex $test 0]
+ set test_file [lindex $test 1]
+
+ simple_compile $test_file
+ set output [util_start "$srcdir/$subdir/$test_file" ]
+
+ if { [regexp "PASS" $output] } {
+ pass $test_desc
+ file delete "$srcdir/$subdir/$test_file" "$srcdir/$subdir/$test_file.o"
+ } else {
+ fail $test_desc
+ puts $output
+ }
+
+}
diff --git a/testsuite/nsswitch/getent_grent.c b/testsuite/nsswitch/getent_grent.c
new file mode 100644
index 00000000000..782cc0c86b7
--- /dev/null
+++ b/testsuite/nsswitch/getent_grent.c
@@ -0,0 +1,101 @@
+/* Test out of order operations with {set,get,end}grent */
+
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Security context tests
+ 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 <stdio.h>
+#include <grp.h>
+
+int main (int argc, char **argv)
+{
+ struct group *gr;
+ int found = 0;
+ int num_users, i;
+
+ /* Test getgrent() without setgrent() */
+
+ for (i = 0; i < 100; i++) {
+ gr = getgrent();
+
+ /* This is supposed to work */
+
+#if 0
+ if (gr != NULL) {
+ printf("FAIL: getgrent() with no setgrent()\n");
+ return 1;
+ }
+#endif
+ }
+
+ /* Work out how many user till first domain group */
+
+ num_users = 0;
+ setgrent();
+
+ while (1) {
+ gr = getgrent();
+ num_users++;
+
+ if (gr == NULL) break;
+
+ if (strchr(gr->gr_name, '/')) {
+ found = 1;
+ break;
+ }
+
+ }
+
+ if (!found) {
+ printf("FAIL: could not find any domain groups\n");
+ return 1;
+ }
+
+ /* Test stopping getgrent in the middle of a set of users */
+
+ endgrent();
+
+ /* Test setgrent() without any getgrent() calls */
+
+ setgrent();
+
+ for (i = 0; i < (num_users - 1); i++) {
+ getgrent();
+ }
+
+ endgrent();
+
+ /* Test lots of setgrent() calls */
+
+ for (i = 0; i < 100; i++) {
+ setgrent();
+ }
+
+ /* Test lots of endgrent() calls */
+
+ for (i = 0; i < 100; i++) {
+ endgrent();
+ }
+
+ /* Everything's cool */
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/testsuite/nsswitch/getent_pwent.c b/testsuite/nsswitch/getent_pwent.c
new file mode 100644
index 00000000000..96c804433a4
--- /dev/null
+++ b/testsuite/nsswitch/getent_pwent.c
@@ -0,0 +1,113 @@
+/* Test out of order operations with {set,get,end}pwent */
+
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Security context tests
+ 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 <stdio.h>
+#include <pwd.h>
+
+int main (int argc, char **argv)
+{
+ struct passwd *pw;
+ int found = 0;
+ int num_users, i;
+
+ /* Test getpwent() without setpwent() */
+
+ for (i = 0; i < 100; i++) {
+ pw = getpwent();
+
+ /* This is supposed to work */
+
+#if 0
+ if (pw != NULL) {
+ printf("FAIL: getpwent() with no setpwent()\n");
+ return 1;
+ }
+#endif
+ }
+
+ /* Work out how many user till first domain user */
+
+ num_users = 0;
+ setpwent();
+
+ while (1) {
+ pw = getpwent();
+ num_users++;
+
+ if (pw == NULL) break;
+
+ if (strchr(pw->pw_name, '/')) {
+ found = 1;
+ break;
+ }
+
+ }
+
+ if (!found) {
+ printf("FAIL: could not find any domain users\n");
+ return 1;
+ }
+
+ /* Test stopping getpwent in the middle of a set of users */
+
+ endpwent();
+
+ /* Test setpwent() without any getpwent() calls */
+
+ setpwent();
+
+ for (i = 0; i < (num_users - 1); i++) {
+ getpwent();
+ }
+
+ endpwent();
+
+ /* Test lots of setpwent() calls */
+
+ setpwent();
+
+ for (i = 0; i < (num_users - 1); i++) {
+ getpwent();
+ }
+
+ for (i = 0; i < 100; i++) {
+ setpwent();
+ }
+
+ /* Test lots of endpwent() calls */
+
+ setpwent();
+
+ for (i = 0; i < (num_users - 1); i++) {
+ getpwent();
+ }
+
+ for (i = 0; i < 100; i++) {
+ endpwent();
+ }
+
+ /* Everything's cool */
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/testsuite/nsswitch/getent_r.sh b/testsuite/nsswitch/getent_r.sh
new file mode 100755
index 00000000000..75dc603f829
--- /dev/null
+++ b/testsuite/nsswitch/getent_r.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+#
+# Verify test output. Basically we check to see if all the files generated
+# in /tmp by the get{pw,gr}ent_r.c and program are identical. If there is
+# some problem with the re-entrancy of the code then the information in the
+# two files will be different.
+#
+
+TYPE=$1
+ID=$2
+FILES="/tmp/${TYPE}_r-${ID}.out-*"
+
+# Sort files
+
+for file in $FILES; do
+ cat $file | sort > $file.sorted
+done
+
+# Diff files
+
+SORTED="/tmp/${TYPE}_r-${ID}.out-*.sorted"
+failed=0
+
+for file1 in $SORTED; do
+ for file2 in $SORTED; do
+ if [ $file1 != $file2 ]; then
+ diff $file1 $file2
+ fi
+ done
+done
+
+# Clean up
+
+rm -f $SORTED
+
diff --git a/testsuite/nsswitch/getgrent_r.c b/testsuite/nsswitch/getgrent_r.c
new file mode 100644
index 00000000000..3eac8aa218d
--- /dev/null
+++ b/testsuite/nsswitch/getgrent_r.c
@@ -0,0 +1,84 @@
+/*
+ * Use set/get/endgrent calls from two processes to iterate over the
+ * password database. This checks the multithreaded stuff works.
+ */
+
+#include <stdio.h>
+#include <grp.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <wait.h>
+
+void dump_grent(char *id)
+{
+ struct group *gr;
+ char fname[255];
+ FILE *fptr;
+
+ /* Open results file */
+
+ sprintf(fname, "/tmp/getgrent_r-%s.out-%d", id, getpid());
+
+ if ((fptr = fopen(fname, "w")) < 0) {
+ fprintf(stderr, "ERROR: could not open file %s: %s\n", fname,
+ sys_errlist[errno]);
+ return;
+ }
+
+ /* Dump group database */
+
+ setgrent();
+
+ while((gr = getgrent()) != NULL) {
+ fprintf(fptr,"%s:%s:%d:%d\n", gr->gr_name, gr->gr_passwd,
+ gr->gr_gid);
+ }
+
+ endgrent();
+
+ /* Close results file */
+
+ fclose(fptr);
+}
+
+int main(int argc, char **argv)
+{
+ pid_t pid;
+
+ /* Check args */
+
+ if (argc != 2) {
+ printf("ERROR: must specify output file identifier\n");
+ return 1;
+ }
+
+ /* Fork child process */
+
+ if ((pid = fork()) == -1) {
+ printf("ERROR: unable to fork\n");
+ return 1;
+ }
+
+ /* Handle test case */
+
+ if (pid > 0) {
+ int status;
+
+ /* Parent */
+
+ dump_grent(argv[1]);
+ wait(&status);
+
+ } else {
+
+ /* Child */
+
+ dump_grent(argv[1]);
+ return 0;
+ }
+
+ printf("PASS: run getgrent_r.c\n");
+ return 0;
+}
diff --git a/testsuite/nsswitch/getgrent_r.exp b/testsuite/nsswitch/getgrent_r.exp
new file mode 100644
index 00000000000..c03237c2ad1
--- /dev/null
+++ b/testsuite/nsswitch/getgrent_r.exp
@@ -0,0 +1,41 @@
+#
+# @(#) Test multiple threads can enumerate groups correctly
+#
+
+load_lib util-defs.exp
+load_lib "$srcdir/lib/nsswitch-config.exp"
+
+# Compile getgrent_r.c
+
+set output [target_compile "$srcdir/$subdir/getgrent_r.c" \
+ "$srcdir/$subdir/getgrent_r" executable {additional_flags="-g"}]
+
+if {$output != ""} {
+ perror "compile getgrent_r"
+ puts $output
+ return
+}
+
+# Clean up output from previous tests
+
+set pid [pid]
+file delete [glob -nocomplain "/tmp/getgrent_r-$pid.out-*"]
+
+# Run test proggy
+
+set output [util_start "$srcdir/$subdir/getgrent_r" "$pid" ""]
+if {![regexp "^PASS:" $output]} {
+ perror "run getgrent_r"
+ puts $output
+ return -1
+}
+
+# Sort and compare output
+
+set output [util_start "$srcdir/$subdir/getent_r.sh" "getgrent $pid" ""]
+if {$output == ""} {
+ pass "getgrent_r"
+} else {
+ fail "getgrent_r"
+ puts $output
+}
diff --git a/testsuite/nsswitch/getgrgid.c b/testsuite/nsswitch/getgrgid.c
new file mode 100644
index 00000000000..947dd0ac4c7
--- /dev/null
+++ b/testsuite/nsswitch/getgrgid.c
@@ -0,0 +1,57 @@
+/*
+ * Lookup a group by gid.
+ */
+
+#include <stdio.h>
+#include <grp.h>
+#include <sys/types.h>
+
+int main(int argc, char **argv)
+{
+ struct group *gr;
+ gid_t gid;
+
+ /* Check args */
+
+ if (argc != 2) {
+ printf("ERROR: no arg specified\n");
+ exit(1);
+ }
+
+ if ((gid = atoi(argv[1])) == 0) {
+ printf("ERROR: invalid gid specified\n");
+ exit(1);
+ }
+
+ /* Do getgrgid() */
+
+ if ((gr = getgrgid(gid)) == NULL) {
+ printf("FAIL: gid %d does not exist\n", gid);
+ exit(1);
+ }
+
+ /* Print group info */
+
+ printf("PASS: gid %d exists\n", gid);
+ printf("gr_name = %s\n", gr->gr_name);
+ printf("gr_passwd = %s\n", gr->gr_passwd);
+ printf("gr_gid = %d\n", gr->gr_gid);
+
+ /* Group membership */
+
+ if (gr->gr_mem != NULL) {
+ int i = 0;
+
+ printf("gr_mem = ");
+ while(gr->gr_mem[i] != NULL) {
+ printf("%s", gr->gr_mem[i]);
+ i++;
+ if (gr->gr_mem != NULL) {
+ printf(",");
+ }
+ }
+ printf("\n");
+ }
+
+ exit(0);
+}
diff --git a/testsuite/nsswitch/getgrgid.exp b/testsuite/nsswitch/getgrgid.exp
new file mode 100644
index 00000000000..c53749f2629
--- /dev/null
+++ b/testsuite/nsswitch/getgrgid.exp
@@ -0,0 +1,50 @@
+#
+# @(#) Test reverse lookup of group ids from getent match getgrgid() output
+#
+
+load_lib util-defs.exp
+load_lib compile.exp
+
+# Compile getgruid.c
+
+simple_compile "getgrgid"
+
+# Get list of gids using getent
+
+set output [util_start "getent" "group" ""]
+set got_entries 0
+
+verbose $output
+
+foreach {line} [split $output "\n"] {
+
+ # Process user
+
+ set grp_entry [split $line ":"]
+ set group [lindex $grp_entry 0]
+
+ if {[regexp {^[^/]+/} $group]} {
+
+ set got_entries 1
+
+ # Only lookup winbindd users
+
+ set gid [lindex $grp_entry 2]
+
+ # Test lookup of gid succeeds
+
+ set output [util_start "$srcdir/$subdir/getgrgid" "$gid" ""]
+ verbose $output
+
+ if {[regexp "PASS:" $output]} {
+ pass "getgrgid $gid ($group)"
+ } else {
+ fail "getgrgid $gid ($group)"
+ }
+ }
+
+}
+
+if {!$got_entries} {
+ perror "No domain groups returned from getent"
+}
diff --git a/testsuite/nsswitch/getgrnam.c b/testsuite/nsswitch/getgrnam.c
new file mode 100644
index 00000000000..8ab4046bd9e
--- /dev/null
+++ b/testsuite/nsswitch/getgrnam.c
@@ -0,0 +1,51 @@
+/*
+ * Lookup a group by name
+ */
+
+#include <stdio.h>
+#include <grp.h>
+#include <sys/types.h>
+
+int main(int argc, char **argv)
+{
+ struct group *gr;
+
+ /* Check args */
+
+ if (argc != 2) {
+ printf("ERROR: no arg specified\n");
+ exit(1);
+ }
+
+ /* Do getgrnam() */
+
+ if ((gr = getgrnam(argv[1])) == NULL) {
+ printf("FAIL: group %s does not exist\n", argv[1]);
+ exit(1);
+ }
+
+ /* Print group info */
+
+ printf("PASS: group %s exists\n", argv[1]);
+ printf("gr_name = %s\n", gr->gr_name);
+ printf("gr_passwd = %s\n", gr->gr_passwd);
+ printf("gr_gid = %d\n", gr->gr_gid);
+
+ /* Group membership */
+
+ if (gr->gr_mem != NULL) {
+ int i = 0;
+
+ printf("gr_mem = ");
+ while(gr->gr_mem[i] != NULL) {
+ printf("%s", gr->gr_mem[i]);
+ i++;
+ if (gr->gr_mem != NULL) {
+ printf(",");
+ }
+ }
+ printf("\n");
+ }
+
+ exit(0);
+}
diff --git a/testsuite/nsswitch/getgrnam.exp b/testsuite/nsswitch/getgrnam.exp
new file mode 100644
index 00000000000..92c5b76742e
--- /dev/null
+++ b/testsuite/nsswitch/getgrnam.exp
@@ -0,0 +1,28 @@
+#
+# @(#) Test domain groups resolve using getgrnam()
+#
+
+load_lib "util-defs.exp"
+load_lib "compile.exp"
+
+# Compile getgrnam.c
+
+simple_compile "getgrnam"
+
+# Test domain groups
+
+set group_list [split [util_start "bin/wbinfo" "-g"] "\n"]
+
+verbose $group_list
+
+foreach {group} $group_list {
+ set output [util_start "$srcdir/$subdir/getgrnam" "\"$group\"" ""]
+
+ verbose $output
+
+ if {[regexp "PASS:" $output]} {
+ pass "getgrnam $group"
+ } else {
+ fail "getgrnam $group"
+ }
+}
diff --git a/testsuite/nsswitch/getpwent_r.c b/testsuite/nsswitch/getpwent_r.c
new file mode 100644
index 00000000000..2ba7ea96f1e
--- /dev/null
+++ b/testsuite/nsswitch/getpwent_r.c
@@ -0,0 +1,85 @@
+/*
+ * Use set/get/endpwent calls from two processes to iterate over the
+ * password database. This checks the multithreaded stuff works.
+ */
+
+#include <stdio.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <wait.h>
+
+void dump_pwent(char *id)
+{
+ struct passwd *pw;
+ char fname[255];
+ FILE *fptr;
+
+ /* Open results file */
+
+ sprintf(fname, "/tmp/getpwent_r-%s.out-%d", id, getpid());
+
+ if ((fptr = fopen(fname, "w")) < 0) {
+ fprintf(stderr, "ERROR: could not open file %s: %s\n", fname,
+ sys_errlist[errno]);
+ return;
+ }
+
+ /* Dump passwd database */
+
+ setpwent();
+
+ while((pw = getpwent()) != NULL) {
+ fprintf(fptr,"%s:%s:%s:%d:%d\n", pw->pw_name, pw->pw_passwd,
+ pw->pw_gecos, pw->pw_uid, pw->pw_gid);
+ }
+
+ endpwent();
+
+ /* Close results file */
+
+ fclose(fptr);
+}
+
+#define NUM_FORKS 2
+
+int main(int argc, char **argv)
+{
+ pid_t pids[NUM_FORKS];
+ int i, status;
+
+ /* Check args */
+
+ if (argc != 2) {
+ printf("ERROR: must specify output file identifier\n");
+ return 1;
+ }
+
+ for(i = 0; i < NUM_FORKS; i++) {
+
+ /* Fork off lots */
+
+ if ((pids[i] = fork()) == -1) {
+ perror("fork");
+ return 1;
+ }
+
+ /* Child does tests */
+
+ if (pids[i] == 0) {
+ dump_pwent(argv[1]);
+ return 0;
+ }
+ }
+
+ /* Wait for everyone to finish */
+
+ for (i = 0; i < NUM_FORKS; i++) {
+ waitpid(pids[i], &status, 0);
+ }
+
+ printf("PASS: getpwent_r.c\n");
+ return 0;
+}
diff --git a/testsuite/nsswitch/getpwent_r.exp b/testsuite/nsswitch/getpwent_r.exp
new file mode 100644
index 00000000000..95c155d78cc
--- /dev/null
+++ b/testsuite/nsswitch/getpwent_r.exp
@@ -0,0 +1,41 @@
+#
+# @(#) Test multiple threads can enumerate users correctly
+#
+
+load_lib util-defs.exp
+load_lib "$srcdir/lib/nsswitch-config.exp"
+
+# Compile getpwent_r.c
+
+set output [target_compile "$srcdir/$subdir/getpwent_r.c" \
+ "$srcdir/$subdir/getpwent_r" executable {additional_flags="-g"}]
+
+if {$output != ""} {
+ perror "compile getpwent_r"
+ puts $output
+ fail ""
+}
+
+# Clean up output from previous tests
+
+set pid [pid]
+file delete [glob -nocomplain "/tmp/getpwent_r-$pid.out-*"]
+
+# Run test proggy
+
+set output [util_start "$srcdir/$subdir/getpwent_r" "$pid" ""]
+if {![regexp "^PASS:" $output]} {
+ perror "run getpwent_r"
+ puts $output
+ return -1
+}
+
+# Sort and compare output
+
+set output [util_start "$srcdir/$subdir/getent_r.sh" "getpwent $pid" ""]
+if {$output == ""} {
+ pass "getpwent_r"
+} else {
+ fail "getpwent_r"
+ puts $output
+}
diff --git a/testsuite/nsswitch/getpwnam.c b/testsuite/nsswitch/getpwnam.c
new file mode 100644
index 00000000000..e7dd2910b66
--- /dev/null
+++ b/testsuite/nsswitch/getpwnam.c
@@ -0,0 +1,37 @@
+/*
+ * Lookup a user by name
+ */
+
+#include <stdio.h>
+#include <pwd.h>
+#include <sys/types.h>
+
+int main(int argc, char **argv)
+{
+ struct passwd *pw;
+
+ /* Check args */
+
+ if (argc != 2) {
+ printf("ERROR: no arg specified\n");
+ exit(1);
+ }
+
+ /* Do getpwnam() */
+
+ if ((pw = getpwnam(argv[1])) == NULL) {
+ printf("FAIL: user %s does not exist\n", argv[1]);
+ exit(1);
+ }
+
+ printf("PASS: user %s exists\n", argv[1]);
+ printf("pw_name = %s\n", pw->pw_name);
+ printf("pw_passwd = %s\n", pw->pw_passwd);
+ printf("pw_uid = %d\n", pw->pw_uid);
+ printf("pw_gid = %d\n", pw->pw_gid);
+ printf("pw_gecos = %s\n", pw->pw_gecos);
+ printf("pw_dir = %s\n", pw->pw_dir);
+ printf("pw_shell = %s\n", pw->pw_shell);
+
+ exit(0);
+}
diff --git a/testsuite/nsswitch/getpwnam.exp b/testsuite/nsswitch/getpwnam.exp
new file mode 100644
index 00000000000..5f6b2343392
--- /dev/null
+++ b/testsuite/nsswitch/getpwnam.exp
@@ -0,0 +1,28 @@
+#
+# @(#) Test domain users resolve using getpwnam()
+#
+
+load_lib util-defs.exp
+load_lib "compile.exp"
+
+# Compile getpwnam.c
+
+simple_compile "getpwnam"
+
+# Test domain users
+
+set user_list [split [util_start "bin/wbinfo" "-u"] "\n"]
+
+verbose $user_list
+
+foreach { user } $user_list {
+ set output [util_start "$srcdir/$subdir/getpwnam" "\"$user\"" ""]
+
+ verbose $output
+
+ if {[regexp "PASS:" $output]} {
+ pass "getpwnam $user"
+ } else {
+ fail "getpwnam $user"
+ }
+}
diff --git a/testsuite/nsswitch/getpwuid.c b/testsuite/nsswitch/getpwuid.c
new file mode 100644
index 00000000000..3f364df29d5
--- /dev/null
+++ b/testsuite/nsswitch/getpwuid.c
@@ -0,0 +1,43 @@
+/*
+ * Lookup a user by uid.
+ */
+
+#include <stdio.h>
+#include <pwd.h>
+#include <sys/types.h>
+
+int main(int argc, char **argv)
+{
+ struct passwd *pw;
+ uid_t uid;
+
+ /* Check args */
+
+ if (argc != 2) {
+ printf("ERROR: no arg specified\n");
+ exit(1);
+ }
+
+ if ((uid = atoi(argv[1])) == 0) {
+ printf("ERROR: invalid uid specified\n");
+ exit(1);
+ }
+
+ /* Do getpwuid() */
+
+ if ((pw = getpwuid(uid)) == NULL) {
+ printf("FAIL: uid %d does not exist\n", uid);
+ exit(1);
+ }
+
+ printf("PASS: uid %d exists\n", uid);
+ printf("pw_name = %s\n", pw->pw_name);
+ printf("pw_passwd = %s\n", pw->pw_passwd);
+ printf("pw_uid = %d\n", pw->pw_uid);
+ printf("pw_gid = %d\n", pw->pw_gid);
+ printf("pw_gecos = %s\n", pw->pw_gecos);
+ printf("pw_dir = %s\n", pw->pw_dir);
+ printf("pw_shell = %s\n", pw->pw_shell);
+
+ exit(0);
+}
diff --git a/testsuite/nsswitch/getpwuid.exp b/testsuite/nsswitch/getpwuid.exp
new file mode 100644
index 00000000000..be6a01cb9e1
--- /dev/null
+++ b/testsuite/nsswitch/getpwuid.exp
@@ -0,0 +1,59 @@
+#
+# @(#) Test reverse lookup of user ids from getent match getpwuid() output
+#
+
+load_lib util-defs.exp
+
+# Compile getpwuid.c
+
+set output [target_compile "$srcdir/$subdir/getpwuid.c" \
+ "$srcdir/$subdir/getpwuid" executable {additional_flags="-g"}]
+
+if {$output != ""} {
+ perror "compile getpwuid"
+ puts $output
+ return
+}
+
+# Get list of uids using getent
+
+set output [util_start "getent" "passwd" ""]
+set got_entries 0
+
+verbose $output
+
+foreach {line} [split $output "\n"] {
+
+ # Process user
+
+ set pwd_entry [split $line ":"]
+ set user [lindex $pwd_entry 0]
+
+ if {[regexp {^[^/]+/} $user]} {
+
+ set got_entries 1
+
+ # Only lookup winbindd users
+
+ set uid [lindex $pwd_entry 2]
+ set gid [lindex $pwd_entry 3]
+
+ # Test lookup of uid succeeds
+
+ set output [util_start "$srcdir/$subdir/getpwuid" "$uid" ""]
+
+ verbose $output
+
+ set test_desc "getpwuid $uid ($user)"
+
+ if {[regexp "PASS:" $output]} {
+ pass $test_desc
+ } else {
+ fail $test_desc
+ }
+ }
+}
+
+if {!$got_entries} {
+ perror "No domain users returned from getent"
+}
diff --git a/testsuite/nsswitch/groupmem_dom.exp b/testsuite/nsswitch/groupmem_dom.exp
new file mode 100644
index 00000000000..3ba34bb810e
--- /dev/null
+++ b/testsuite/nsswitch/groupmem_dom.exp
@@ -0,0 +1,33 @@
+#
+# @(#) Test whether members of domain groups all have domain names
+#
+
+load_lib util-defs.exp
+
+set group_list [split [util_start "getent group" ""] "\n"]
+set failed 0
+
+foreach { group } $group_list {
+ set group_entry [split $group ":"]
+
+ set group_name [lindex $group_entry 0]
+ set group_members [split [lindex $group_entry 3] ","]
+
+ if { [regexp {^[^/]+/} $group_name] } {
+
+ verbose "group $group_name has members $group_members"
+
+ foreach { user } $group_members {
+ if { ![regexp {^[^/]+/} $user] } {
+ fail "group $group has non-domain user $user"
+ set failed 1
+ }
+ }
+ } else {
+ verbose "ignoring non-domain group $group_name"
+ }
+}
+
+if { !$failed } {
+ pass "domain groups contain only domain members"
+}
diff --git a/testsuite/nsswitch/initgroups.c b/testsuite/nsswitch/initgroups.c
new file mode 100644
index 00000000000..b7d9c50eaa3
--- /dev/null
+++ b/testsuite/nsswitch/initgroups.c
@@ -0,0 +1,42 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <grp.h>
+#include <pwd.h>
+#include <sys/types.h>
+
+int main(int argc, char **argv)
+{
+ int result, ngroups, i;
+ gid_t *groups;
+ struct passwd *pw;
+
+ if (!(pw = getpwnam(argv[1]))) {
+ printf("FAIL: no passwd entry for %s\n", argv[1]);
+ return 1;
+ }
+
+ result = initgroups(argv[1], pw->pw_gid);
+
+ if (result == -1) {
+ printf("FAIL");
+ return 1;
+ }
+
+ ngroups = getgroups(0, NULL);
+
+ groups = (gid_t *)malloc(sizeof(gid_t) * ngroups);
+ ngroups = getgroups(ngroups, groups);
+
+ printf("%s is a member of groups:\n", argv[1]);
+
+ for (i = 0; i < ngroups; i++) {
+ struct group *grp;
+
+ grp = getgrgid(groups[i]);
+
+ printf("%d (%s)\n", groups[i], grp ? grp->gr_name : "?");
+ }
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/testsuite/nsswitch/initgroups.exp b/testsuite/nsswitch/initgroups.exp
new file mode 100644
index 00000000000..ab21bcc9e7b
--- /dev/null
+++ b/testsuite/nsswitch/initgroups.exp
@@ -0,0 +1,37 @@
+#
+# @(#) Test initgroups function
+#
+
+load_lib util-defs.exp
+load_lib compile.exp
+
+if { [util_start "id -u"] != 0 } {
+ set test_desc "must be userid 0 to run"
+ note $test_desc
+ untested $test_desc
+ return
+}
+
+# Compile test program
+
+simple_compile "initgroups"
+
+# Test domain users
+
+set user_list [split [util_start "bin/wbinfo" "-u"] "\n"]
+
+verbose $user_list
+
+foreach { user } $user_list {
+ set output [util_start "$srcdir/$subdir/initgroups" "\"$user\"" ""]
+
+ verbose $output
+
+ set test_desc "initgroups $user"
+
+ if { [regexp "PASS" $output] } {
+ pass $test_desc
+ } else {
+ fail $test_desc
+ }
+}
diff --git a/testsuite/nsswitch/login.exp b/testsuite/nsswitch/login.exp
new file mode 100644
index 00000000000..c2bb0e5a40a
--- /dev/null
+++ b/testsuite/nsswitch/login.exp
@@ -0,0 +1,102 @@
+#
+# @(#) Test logins using pam_winbind.so module using telnet
+#
+
+load_lib util-defs.exp
+load_lib nsswitch-config.exp
+
+#
+# @(#) Test user can login
+#
+
+spawn telnet localhost
+
+set test_desc "telnet localhost (login)"
+
+expect {
+ "login:" { }
+ timeout { fail "timed out in $test_desc"; return }
+ eof { fail "end of file in $test_desc"; return }
+}
+
+send "$domain/$USER\r"
+
+set test_desc "telnet localhost (password)"
+
+expect {
+ "Password:" { }
+ timeout { fail "timed out in $test_desc"; return }
+ eof { fail "end of file in $test_desc"; return }
+}
+
+send "$PASSWORD\r"
+
+expect {
+ "$ " { }
+ "Login incorrect" { fail "login incorrect"; return }
+ timeout { fail "timed out in $test_desc"; return }
+ eof { fail "end of file in $test_desc"; return }
+}
+
+pass "login $domain/$USER"
+
+#
+# @(#) Check supplementary group membership
+#
+
+set test_desc "supplementary groups"
+
+# Get list of groups
+
+send "id -G\r"
+
+expect {
+ -re "((\[0-9]+ )*\[0-9]+\r)" { exp_continue; }
+ "$ " { }
+ timeout { fail "timed out in $test_desc"; return }
+ eof { fail "end of file in $test_desc"; return }
+}
+
+set groups $expect_out(1,string)
+set wb_groups [util_start "bin/wbinfo" "-r $domain/$USER"]
+
+verbose "id groups are $groups"
+verbose "wbinfo groups are $wb_groups"
+
+# Check all groups from id are in wbinfo and vice-versa
+
+set failed 0
+
+foreach { group } $groups {
+ set got_group 0
+ foreach { wb_group } $wb_groups {
+ if { $wb_group == $group } {
+ set got_group 1
+ break
+ }
+ }
+
+ if { !$got_group } {
+ fail "group $group not in output of wbinfo -r"
+ set failed 1
+ }
+}
+
+foreach { wb_group } $wb_groups {
+ set got_group 0
+ foreach { group } $groups {
+ if { $group == $wb_group } {
+ set got_group 1
+ break
+ }
+ }
+
+ if { !$got_group } {
+ fail "group $group not in output of id -G"
+ set failed 1
+ }
+}
+
+if { !$failed } {
+ pass "id/wbinfo groups match"
+}
diff --git a/testsuite/nsswitch/longarg.exp b/testsuite/nsswitch/longarg.exp
new file mode 100644
index 00000000000..e1d0eda9ccb
--- /dev/null
+++ b/testsuite/nsswitch/longarg.exp
@@ -0,0 +1,29 @@
+#
+# @(#) Test handling of long arguments passed to various nss functions
+#
+
+load_lib compile.exp
+load_lib util-defs.exp
+
+# Run tests from C source files
+
+set longarg_tests [list \
+ { "long arg to getpwnam()" "longarg_getpwnam" } \
+ { "long arg to getgrnam()" "longarg_getgrnam" } \
+ ]
+
+foreach { test } $longarg_tests {
+ set test_desc [lindex $test 0]
+ set test_file [lindex $test 1]
+
+ simple_make "longarg" $test_file
+ set output [util_start "$srcdir/$subdir/$test_file" ]
+
+ if { [regexp "PASS" $output] } {
+ pass $test_desc
+ file delete "$srcdir/$subdir/$test_file" "$srcdir/$subdir/$test_file.o"
+ } else {
+ fail $test_desc
+ puts $output
+ }
+}
diff --git a/testsuite/nsswitch/longarg_getgrnam.c b/testsuite/nsswitch/longarg_getgrnam.c
new file mode 100644
index 00000000000..84083d2620e
--- /dev/null
+++ b/testsuite/nsswitch/longarg_getgrnam.c
@@ -0,0 +1,42 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Security context tests
+ 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 <stdio.h>
+#include <stdlib.h>
+#include <grp.h>
+#include <sys/types.h>
+
+#include "longarg_utils.h"
+
+int main(void)
+{
+ struct group *grp;
+ char *domain = getenv("TEST_WORKGROUP");
+ char long_name[65535];
+ int failed = 0;
+
+ sprintf(long_name, "%s/%s", domain, LONG_STRING);
+
+ grp = getgrnam(long_name);
+ printf("%s\n", !grp ? "PASS" : "FAIL");
+
+ return grp == NULL;
+}
diff --git a/testsuite/nsswitch/longarg_getpwnam.c b/testsuite/nsswitch/longarg_getpwnam.c
new file mode 100644
index 00000000000..f2a0a73ddca
--- /dev/null
+++ b/testsuite/nsswitch/longarg_getpwnam.c
@@ -0,0 +1,42 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Security context tests
+ 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 <stdio.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <sys/types.h>
+
+#include "longarg_utils.h"
+
+int main(void)
+{
+ struct passwd *pwd;
+ char *domain = getenv("TEST_WORKGROUP");
+ char long_name[65535];
+ int failed = 0;
+
+ sprintf(long_name, "%s/%s", domain, LONG_STRING);
+
+ pwd = getpwnam(long_name);
+ printf("%s\n", !pwd ? "PASS" : "FAIL");
+
+ return pwd == NULL;
+}
diff --git a/testsuite/nsswitch/longarg_utils.h b/testsuite/nsswitch/longarg_utils.h
new file mode 100644
index 00000000000..1f2f2a7065d
--- /dev/null
+++ b/testsuite/nsswitch/longarg_utils.h
@@ -0,0 +1,27 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Security context tests
+ 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 _LONGARG_UTILS_H
+#define _LONGARG_UTILS_H
+
+#define LONG_STRING "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+
+#endif
diff --git a/testsuite/nsswitch/nss_winbind_syms.c b/testsuite/nsswitch/nss_winbind_syms.c
new file mode 100644
index 00000000000..29d1da9d499
--- /dev/null
+++ b/testsuite/nsswitch/nss_winbind_syms.c
@@ -0,0 +1,63 @@
+/*
+ * Test required functions are exported from the libnss_winbind.so library
+ */
+
+#include <stdio.h>
+#include <dlfcn.h>
+
+/* Symbol list to check */
+
+static char *symlist[] = {
+ "_nss_winbind_getgrent_r",
+ "_nss_winbind_endgrent",
+ "_nss_winbind_endpwent",
+ "_nss_winbind_getgrgid_r",
+ "_nss_winbind_getgrnam_r",
+ "_nss_winbind_getpwent_r",
+ "_nss_winbind_getpwnam_r",
+ "_nss_winbind_getpwuid_r",
+ "_nss_winbind_setgrent",
+ "_nss_winbind_setpwent",
+ "_nss_winbind_initgroups",
+ NULL
+};
+
+/* Main function */
+
+int main(int argc, char **argv)
+{
+ void *handle, *sym;
+ int i, y;
+
+ /* Open library */
+
+ if (argc != 2) {
+ printf("FAIL: usage '%s sharedlibname'\n", argv[0]);
+ return 1;
+ }
+
+ handle = dlopen(argv[1], RTLD_NOW);
+
+ if (handle == NULL) {
+ printf("FAIL: could not dlopen library: %s\n", dlerror());
+ return 1;
+ }
+
+ /* Read symbols */
+
+ for (i = 0; symlist[i] != NULL; i++) {
+ sym = dlsym(handle, symlist[i]);
+ if (sym == NULL) {
+ printf("FAIL: could not resolve symbol '%s': %s\n",
+ symlist[i], dlerror());
+ return 1;
+ } else {
+ printf("loaded symbol '%s' ok\n", symlist[i]);
+ }
+ }
+
+ /* Clean up */
+
+ dlclose(handle);
+ return 0;
+}
diff --git a/testsuite/nsswitch/nss_winbind_syms.exp b/testsuite/nsswitch/nss_winbind_syms.exp
new file mode 100644
index 00000000000..ab84cc5c757
--- /dev/null
+++ b/testsuite/nsswitch/nss_winbind_syms.exp
@@ -0,0 +1,42 @@
+#
+# @(#) Test nss functions are exported from the libnss_winbind.so library
+# @(#) Test there are no external dependencies in the libnss_winbind.so library
+#
+# We expect the following symbols to be exported:
+#
+# _nss_winbind_getgrent_r
+# _nss_winbind_endgrent
+# _nss_winbind_endpwent
+# _nss_winbind_getgrgid_r
+# _nss_winbind_getgrnam_r
+# _nss_winbind_getpwent_r
+# _nss_winbind_getpwnam_r
+# _nss_winbind_getpwuid_r
+# _nss_winbind_setgrent
+# _nss_winbind_setpwent
+# _nss_winbind_initgroups
+#
+# This test also has the nice side-effect of showing any unresolved symbols
+# in the library.
+#
+
+load_lib util-defs.exp
+load_lib compile.exp
+
+simple_compile "nss_winbind_syms" "-ldl"
+
+set output [util_start "$srcdir/$subdir/nss_winbind_syms" \
+ "nsswitch/libnss_winbind.so"]
+
+verbose $output
+
+if { [regexp "FAIL:" $output] } {
+ fail "run nss_winbind_syms"
+ return
+}
+
+pass "nss_winbind_syms"
+
+# Clean up
+
+file delete "$srcdir/$subdir/nss_winbind_syms"
diff --git a/testsuite/nsswitch/pam_winbind_syms.c b/testsuite/nsswitch/pam_winbind_syms.c
new file mode 100644
index 00000000000..1264bdb23e2
--- /dev/null
+++ b/testsuite/nsswitch/pam_winbind_syms.c
@@ -0,0 +1,55 @@
+/*
+ * Test required functions are exported from the pam_winbind.so library
+ */
+
+#include <stdio.h>
+#include <dlfcn.h>
+
+/* Symbol list to check */
+
+static char *symlist[] = {
+ "pam_sm_acct_mgmt",
+ "pam_sm_authenticate",
+ "pam_sm_setcred",
+ NULL
+};
+
+/* Main function */
+
+int main(int argc, char **argv)
+{
+ void *handle, *sym;
+ int i, y;
+
+ /* Open library */
+
+ if (argc != 2) {
+ printf("FAIL: usage '%s sharedlibname'\n", argv[0]);
+ return 1;
+ }
+
+ handle = dlopen(argv[1], RTLD_NOW);
+
+ if (handle == NULL) {
+ printf("FAIL: could not dlopen library: %s\n", dlerror());
+ return 1;
+ }
+
+ /* Read symbols */
+
+ for (i = 0; symlist[i] != NULL; i++) {
+ sym = dlsym(handle, symlist[i]);
+ if (sym == NULL) {
+ printf("FAIL: could not resolve symbol '%s': %s\n",
+ symlist[i], dlerror());
+ return 1;
+ } else {
+ printf("loaded symbol '%s' ok\n", symlist[i]);
+ }
+ }
+
+ /* Clean up */
+
+ dlclose(handle);
+ return 0;
+}
diff --git a/testsuite/nsswitch/pam_winbind_syms.exp b/testsuite/nsswitch/pam_winbind_syms.exp
new file mode 100644
index 00000000000..f95274cdd9d
--- /dev/null
+++ b/testsuite/nsswitch/pam_winbind_syms.exp
@@ -0,0 +1,44 @@
+#
+# @(#) Test nss functions are exported from the pam_winbind.so library
+# @(#) Test there are no external dependencies in the pam_winbind.so library
+#
+# We expect the following symbols to be exported:
+#
+# pam_sm_acct_mgmt
+# pam_sm_authenticate
+# pam_sm_setcred
+#
+# This test also has the nice side-effect of showing any unresolved symbols
+# in the library.
+#
+
+load_lib util-defs.exp
+
+# Compile pam_winbind_syms.c
+
+set output [target_compile "$srcdir/$subdir/pam_winbind_syms.c" \
+ "$srcdir/$subdir/pam_winbind_syms" executable \
+ {"libs=-ldl -lpam" "additional_flags=-g"}]
+
+if {$output != ""} {
+ perror "compile pam_winbind_syms.c"
+ puts $output
+ return
+}
+
+# Run load-dl.c
+
+set output [util_start "$srcdir/$subdir/pam_winbind_syms" \
+ "nsswitch/pam_winbind.so"]
+
+if {[regexp "FAIL:" $output]} {
+ fail "run pam_winbind_syms"
+ puts $output
+ return
+}
+
+pass "pam_winbind_syms"
+
+# Clean up
+
+file delete "$srcdir/$subdir/pam_winbind_syms"
diff --git a/testsuite/nsswitch/wbinfo.exp b/testsuite/nsswitch/wbinfo.exp
new file mode 100644
index 00000000000..8be25b2a0f1
--- /dev/null
+++ b/testsuite/nsswitch/wbinfo.exp
@@ -0,0 +1,360 @@
+#
+# @(#) Test wbinfo client access to winbind daemon
+#
+
+load_lib "util-defs.exp"
+load_lib "$srcdir/lib/nsswitch-config.exp"
+load_lib "$srcdir/lib/default-nt-names.exp"
+
+# Name types
+
+set SID_NAME_USER 1
+set SID_NAME_DOM_GRP 2
+set SID_NAME_DOMAIN 3
+set SID_NAME_ALIAS 4
+set SID_NAME_UNKNOWN 8
+
+# Get list of users and groups
+
+set user_list [util_start "bin/wbinfo" "-u"]
+set group_list [util_start "bin/wbinfo" "-g"]
+
+verbose "user list is:\n$user_list"
+verbose "group list is:\n$group_list"
+
+set user_list [split $user_list "\n"]
+set group_list [split $group_list "\n"]
+
+#
+# @(#) Check list of users and groups contain default NT user and group
+# @(#) names
+#
+
+# Users
+
+foreach { user } $domain_users {
+ set test_desc "user $user in wbinfo domain users"
+ if {![regexp $user $user_list]} {
+ fail $test_desc
+ } else {
+ pass $test_desc
+ }
+}
+
+# Groups
+
+foreach { group } $domain_groups {
+ set test_desc "group $group in wbinfo domain groups"
+ if {![regexp $group $group_list]} {
+ fail $test_desc
+ } else {
+ pass $test_desc
+ }
+}
+
+#
+# @(#) Lookup sids for all user and group names returned by wbinfo
+#
+
+# Users
+
+foreach { user } $user_list {
+ set test_desc "get sid for user $user"
+ set output [util_start "bin/wbinfo" "-n \"$user\""]
+
+ verbose $output
+
+ # Split output into name and name_type
+
+ set list [split $output " "]
+ set sid_type [lindex $list [expr [llength $list] - 1]]
+ set sid [join [lrange $list 0 [expr [llength $list] - 2]] " "]
+
+ if { ![regexp "S-" $sid] } {
+ fail $test_desc
+ } else {
+ pass $test_desc
+ }
+
+ set test_desc "sid type for user $user"
+ if { $sid_type != $SID_NAME_USER } {
+ fail $test_desc
+ } else {
+ pass $test_desc
+ }
+
+ lappend user_sid_list $sid
+}
+
+# Groups
+
+foreach { group } $group_list {
+ set test_desc "get sid for group $group"
+ set output [util_start "bin/wbinfo" "-n \"$group\""]
+
+ verbose $output
+
+ # Split output into sid and sid type
+
+ set list [split $output " "]
+ set sid_type [lindex $list [expr [llength $list] - 1]]
+ set sid [join [lrange $list 0 [expr [llength $list] - 2]] " "]
+
+ if { ![regexp "S-" $sid] } {
+ fail $test_desc
+ } else {
+ pass $test_desc
+ }
+
+ set test_desc "sid type for group group"
+ if { $sid_type != $SID_NAME_DOM_GRP } {
+ fail $test_desc
+ } else {
+ pass $test_desc
+ }
+
+ lappend group_sid_list $sid
+}
+
+#
+# @(#) Check reverse lookup of sids to names
+#
+
+# Users
+
+set count 0
+
+foreach { sid } $user_sid_list {
+ set test_desc "reverse user name lookup for sid $sid"
+ set output [util_start "bin/wbinfo" "-s $sid"]
+
+ verbose $output
+
+ # Split output into name and name_type
+
+ set list [split $output " "]
+ set name_type [lindex $list [expr [llength $list] - 1]]
+ set name [join [lrange $list 0 [expr [llength $list] - 2]] " "]
+
+ if { $name != [lindex $user_list $count] } {
+ fail $test_desc
+ } else {
+ pass $test_desc
+ }
+
+ set test_desc "reverse user name type lookup for sid $sid"
+
+ if { $name_type != 1 } {
+ fail $test_desc
+ } else {
+ pass $test_desc
+ }
+
+ incr count
+}
+
+# Groups
+
+set count 0
+
+foreach { sid } $group_sid_list {
+ set test_desc "reverse group name lookup for sid $sid"
+ set output [util_start "bin/wbinfo" "-s $sid"]
+
+ verbose $output
+
+ # Split output into name and name_type
+
+ set list [split $output " "]
+ set name_type [lindex $list [expr [llength $list] - 1]]
+ set name [join [lrange $list 0 [expr [llength $list] - 2]] " "]
+
+ if { $name != [lindex $group_list $count] } {
+ fail $test_desc
+ } else {
+ pass $test_desc
+ }
+
+ set test_desc "reverse group name type lookup for sid $sid"
+
+ if { $name_type != 2 } {
+ fail $test_desc
+ } else {
+ pass $test_desc
+ }
+
+ incr count
+}
+
+#
+# @(#) Cross-check the output of wbinfo -n, getent passwd/group and
+# @(#) wbinfo -S
+#
+
+# Get mapped list of uids from winbindd
+
+set output [util_start "getent" "passwd"]
+set user_list [split $output "\n"]
+
+foreach { user_entry } $user_list {
+ if { [regexp $domain $user_entry] } {
+ set field_list [split $user_entry ":"]
+ set name_output [util_start "bin/wbinfo" \
+ "-n \"[lindex $field_list 0]\""]
+ set list [split $name_output " "]
+ set name_type [lindex $list [expr [llength $list] - 1]]
+ set name [join [lrange $list 0 [expr [llength $list] - 2]] " "]
+ set username_uid_sid [lappend username_uid_sid [list \
+ [lindex $field_list 0] \
+ [lindex $field_list 2] \
+ $name]]
+ }
+}
+
+# Get mapped list of gids from winbindd
+
+set output [util_start "getent" "group"]
+set group_list [split $output "\n"]
+
+foreach { group_entry } $group_list {
+ if { [regexp $domain $group_entry] } {
+ set field_list [split $group_entry ":"]
+ set groupname_gid_sid [lappend groupname_gid_sid [list \
+ [lindex $field_list 0] \
+ [lindex $field_list 2] \
+ [util_start "bin/wbinfo" "-n \"[lindex $field_list 0]\""]]]
+ }
+}
+
+# OK, now we have enough info to cross-check the uid/gid -> sid and
+# sid -> uid/gid functions
+
+foreach { user } $username_uid_sid {
+ set sid [util_start "bin/wbinfo" "-U [lindex $user 1]"]
+ set uid [util_start "bin/wbinfo" "-S [lindex $user 2]"]
+
+ set test_desc "lookup sid by uid [lindex $user 1]"
+
+ if { $sid != [lindex $user 2] } {
+ fail $test_desc
+ } else {
+ pass $test_desc
+ }
+
+ set test_desc "lookup uid by sid [lindex $user 2]"
+
+ if { $uid != [lindex $user 1] } {
+ fail $test_desc
+ } else {
+ pass $test_desc
+ }
+}
+
+foreach { group } $groupname_gid_sid {
+ set sid [util_start "bin/wbinfo" "-G [lindex $group 1]"]
+ set gid [util_start "bin/wbinfo" "-Y [lindex $group 2]"]
+
+ set test_desc "lookup sid by gid [lindex $group 1]"
+
+ if { $sid != [lindex [split [lindex $group 2] " "] 0] ||
+ [lindex [split [lindex $group 2] " " ] 1] != 2 } {
+ fail $test_desc
+ } else {
+ pass $test_desc
+ }
+
+ set test_desc "lookup gid by sid [lindex $group 2]"
+
+ if { $gid != [lindex $group 1] } {
+ fail $test_desc
+ } else {
+ pass $test_desc
+ }
+}
+
+# Check exit codes
+
+proc check_errcode { args } {
+ global errorCode
+ set test_desc [lindex $args 0]
+ set cmd [lindex $args 1]
+ set result [lindex $args 2]
+
+ set errorCode ""
+ verbose "Spawning $cmd"
+ catch "exec $cmd" output
+ set exit_code [lindex $errorCode 2]
+ if { $exit_code == "" } { set exit_code 0 }
+
+ if { $exit_code == $result } {
+ verbose "process returned correct exit code $exit_code"
+ pass $test_desc
+ } else {
+ verbose "process returned bad exit code $exit_code instead of $result"
+ fail $test_desc
+ }
+}
+
+set gooduser_name [lindex [split [lindex $user_list 0] ":"] 0]
+set gooduser_sid [util_start "bin/wbinfo" "-n $gooduser_name"]
+
+set goodgroup_name [lindex [split [lindex $group_list 0] ":"] 0]
+set goodgroup_sid [util_start "bin/wbinfo" "-n $goodgroup_name"]
+
+# Some conditions not tested:
+# - bad list users/groups
+# - good uid/gid to sid
+
+set errcode_tests [list \
+ { "exit code, no arg" "bin/wbinfo" 1 } \
+ { "exit code, invalid arg" "bin/wbinfo -@" 1 } \
+ { "exit code, list users" "bin/wbinfo -u" 0 } \
+ { "exit code, list groups" "bin/wbinfo -g" 0 } \
+ { "exit code, good name to sid" "bin/wbinfo -n $gooduser_name" 0 } \
+ { "exit code, bad name to sid" "bin/wbinfo -n asmithee" 1 } \
+ { "exit code, good sid to name" "bin/wbinfo -s $gooduser_sid" 0 } \
+ { "exit code, bad sid to name" "bin/wbinfo -s S-1234" 1 } \
+ { "exit code, bad uid to sid" "bin/wbinfo -U 0" 1 } \
+ { "exit code, bad gid to sid" "bin/wbinfo -G 0" 1} \
+ { "exit code, good sid to uid" "bin/wbinfo -S $gooduser_sid" 0 } \
+ { "exit code, bad sid to uid" "bin/wbinfo -S S-1234" 1 } \
+ { "exit code, good sid to gid" "bin/wbinfo -Y $goodgroup_sid" 0 } \
+ { "exit code, bad sid to gid" "bin/wbinfo -Y S-1234" 1 } \
+ ]
+
+foreach { test } $errcode_tests {
+ check_errcode [lindex $test 0] [lindex $test 1] [lindex $test 2]
+}
+
+# Test enumerate trusted domains
+
+set test_desc "enumerate trusted domains"
+set output [util_start "bin/wbinfo" "-m"]
+
+verbose $output
+
+foreach { the_domain } $output {
+ if { $the_domain == $domain} {
+ fail "own domain appears in trusted list"
+ }
+}
+
+if {[regexp "Usage" $output] || [regexp "Could not" $output]} {
+ fail $test_desc
+} else {
+ pass $test_desc
+}
+
+# Test check machine account
+
+set test_desc "check machine account"
+set output [util_start "bin/wbinfo" "-t"]
+
+verbose $output
+
+if {[regexp "Usage" $output] || [regexp "Could not" $output] || \
+ ![regexp "(good|bad)" $output]} {
+ fail $test_desc
+} else {
+ pass $test_desc
+}
diff --git a/testsuite/printing/.cvsignore b/testsuite/printing/.cvsignore
new file mode 100644
index 00000000000..392f2777686
--- /dev/null
+++ b/testsuite/printing/.cvsignore
@@ -0,0 +1,2 @@
+vlp
+psec
diff --git a/testsuite/printing/Makefile.psec b/testsuite/printing/Makefile.psec
new file mode 100644
index 00000000000..1410c9e0099
--- /dev/null
+++ b/testsuite/printing/Makefile.psec
@@ -0,0 +1,22 @@
+#
+# Makefile for psec utility
+#
+
+#
+# NOTE: Samba must be configured with the --srcdir option before this Makefile
+# will work: ./configure --srcdir=`pwd`
+#
+# Compile with: make -f Makefile.psec psec
+#
+
+psec_default: psec
+
+include ../../source/Makefile
+
+PSEC_OBJ1 = $(LIB_OBJ) $(PARAM_OBJ) $(UBIQX_OBJ) $(RPC_PARSE_OBJ) \
+ $(LIBSMB_OBJ) $(PASSDB_OBJ) $(RPC_CLIENT_OBJ)
+
+PSEC_OBJS = $(PSEC_OBJ1:%=$(srcdir)/%)
+
+psec: $(PSEC_OBJS) psec.o
+ $(CC) -o $@ psec.o $(PSEC_OBJS) $(LIBS)
diff --git a/testsuite/printing/Makefile.vlp b/testsuite/printing/Makefile.vlp
new file mode 100644
index 00000000000..142082ebfe6
--- /dev/null
+++ b/testsuite/printing/Makefile.vlp
@@ -0,0 +1,14 @@
+#
+# Makefile for vlp utility
+#
+
+vlp_default: vlp
+
+include ../../source/Makefile
+
+VLP_OBJ1 = $(LIB_OBJ) $(PARAM_OBJ) $(UBIQX_OBJ)
+
+VLP_OBJS = $(VLP_OBJ1:%=$(srcdir)/%)
+
+vlp: $(VLP_OBJS) vlp.o
+ $(CC) -o $@ vlp.o $(VLP_OBJS) $(LIBS)
diff --git a/testsuite/printing/README.vlp b/testsuite/printing/README.vlp
new file mode 100644
index 00000000000..48d2c8c0a2b
--- /dev/null
+++ b/testsuite/printing/README.vlp
@@ -0,0 +1,35 @@
+Virtual line printer test program (vlp)
+=======================================
+
+This can be useful for testing/debugging Samba print code. It gives you a
+virtual full-function printer.
+
+Setup
+
+1) Configure and build Samba.
+ For this to work, you need to add:
+ -DDEVELOPER
+ to your CFLAGS, and add:
+ --srcdir=<wherever your source is>
+ when running configure. Generally
+ ./configure --srcdir=`pwd` <other configure options>
+ should work.
+
+2) Build and install vlp.
+ # cd testsuite/printing
+ # make -f Makefile.vlp vlp
+ # su
+ # cp vlp /usr/local/samba/bin
+
+3) Set up Samba to use vlp.
+ In your smb.conf file under [global], add the following option:
+ printing = vlp
+ and then add any number of print shares, without needing to make them
+ really exist.
+
+ [testprinter]
+ printable = yes
+
+ is all you need for the most basic virtual printer.
+
+
diff --git a/testsuite/printing/psec.c b/testsuite/printing/psec.c
new file mode 100644
index 00000000000..7ba40b18a31
--- /dev/null
+++ b/testsuite/printing/psec.c
@@ -0,0 +1,436 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 2.0
+
+ Printer security permission manipulation.
+
+ 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.
+*/
+
+/* This program can get or set NT printer security permissions from the
+ command line. Usage: psec getsec|setsec printername. You must have
+ write access to the ntdrivers.tdb file to set permissions and read
+ access to get permissions.
+
+ For this program to compile using the supplied Makefile.psec, Samba
+ must be configured with the --srcdir option
+
+ For getsec, output like the following is sent to standard output:
+
+ S-1-5-21-1067277791-1719175008-3000797951-500
+
+ 1 9 0x10000000 S-1-5-21-1067277791-1719175008-3000797951-501
+ 1 2 0x10000000 S-1-5-21-1067277791-1719175008-3000797951-501
+ 0 9 0x10000000 S-1-5-21-1067277791-1719175008-3000797951-500
+ 0 2 0x10000000 S-1-5-21-1067277791-1719175008-3000797951-500
+ 0 9 0x10000000 S-1-5-21-1067277791-1719175008-3000797951-513
+ 0 2 0x00020000 S-1-5-21-1067277791-1719175008-3000797951-513
+ 0 2 0xe0000000 S-1-1-0
+
+ The first two lines describe the owner user and owner group of the printer.
+ If either of these lines are blank then the respective owner property is
+ not set. The remaining lines list the printer permissions or ACE entries,
+ one per line. Each column describes a different property of the ACE:
+
+ Column Description
+ -------------------------------------------------------------------
+ 1 ACE type (allow/deny etc) defined in rpc_secdes.h
+ 2 ACE flags defined in rpc_secdes.h
+ 3 ACE mask - printer ACE masks are defined in rpc_spoolss.h
+ 4 SID the ACE applies to
+
+ The above example describes the following permissions in order:
+
+ - The guest user has No Access to the printer
+ - The domain administrator has Full Access
+ - Domain Users can Manage Documents
+ - Everyone has Print access
+
+ The setsec command takes the output format but sets the security descriptor
+ appropriately. */
+
+#include "includes.h"
+
+TDB_CONTEXT *tdb;
+
+/* ACE type conversions */
+
+char *ace_type_to_str(uint ace_type)
+{
+ static fstring temp;
+
+ switch(ace_type) {
+ case SEC_ACE_TYPE_ACCESS_DENIED:
+ return "DENY";
+ case SEC_ACE_TYPE_ACCESS_ALLOWED:
+ return "ALLOW";
+ }
+
+ slprintf(temp, sizeof(temp) - 1, "0x%02x", ace_type);
+ return temp;
+}
+
+uint str_to_ace_type(char *ace_type)
+{
+ if (strcmp(ace_type, "ALLOWED") == 0)
+ return SEC_ACE_TYPE_ACCESS_ALLOWED;
+
+ if (strcmp(ace_type, "DENIED") == 0)
+ return SEC_ACE_TYPE_ACCESS_DENIED;
+
+ return -1;
+}
+
+/* ACE mask (permission) conversions */
+
+char *ace_mask_to_str(uint32 ace_mask)
+{
+ static fstring temp;
+
+ switch (ace_mask) {
+ case PRINTER_ACE_FULL_CONTROL:
+ return "Full Control";
+ case PRINTER_ACE_MANAGE_DOCUMENTS:
+ return "Manage Documents";
+ case PRINTER_ACE_PRINT:
+ return "Print";
+ }
+
+ slprintf(temp, sizeof(temp) - 1, "0x%08x", ace_mask);
+ return temp;
+}
+
+uint32 str_to_ace_mask(char *ace_mask)
+{
+ if (strcmp(ace_mask, "Full Control") == 0)
+ return PRINTER_ACE_FULL_CONTROL;
+
+ if (strcmp(ace_mask, "Manage Documents") == 0)
+ return PRINTER_ACE_MANAGE_DOCUMENTS;
+
+ if (strcmp(ace_mask, "Print") == 0)
+ return PRINTER_ACE_PRINT;
+
+ return -1;
+}
+
+/* ACE conversions */
+
+char *ace_to_str(SEC_ACE *ace)
+{
+ static pstring temp;
+ fstring sidstr;
+
+ sid_to_string(sidstr, &ace->sid);
+
+ slprintf(temp, sizeof(temp) - 1, "%s %d %s %s",
+ ace_type_to_str(ace->type), ace->flags,
+ ace_mask_to_str(ace->info.mask), sidstr);
+
+ return temp;
+}
+
+void str_to_ace(SEC_ACE *ace, char *ace_str)
+{
+ SEC_ACCESS sa;
+ DOM_SID sid;
+ uint32 mask;
+ uint8 type, flags;
+
+ init_sec_access(&sa, mask);
+ init_sec_ace(ace, &sid, type, sa, flags);
+}
+
+/* Get a printer security descriptor */
+
+int psec_getsec(char *printer)
+{
+ SEC_DESC_BUF *secdesc_ctr = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+ fstring keystr, sidstr, tdb_path;
+ prs_struct ps;
+ int result = 0, i;
+
+ ZERO_STRUCT(ps);
+
+ /* Open tdb for reading */
+
+ slprintf(tdb_path, sizeof(tdb_path) - 1, "%s/ntdrivers.tdb",
+ lp_lockdir());
+
+ tdb = tdb_open(tdb_path, 0, 0, O_RDONLY, 0600);
+
+ if (!tdb) {
+ printf("psec: failed to open nt drivers database: %s\n",
+ sys_errlist[errno]);
+ return 1;
+ }
+
+ /* Get security blob from tdb */
+
+ slprintf(keystr, sizeof(keystr) - 1, "SECDESC/%s", printer);
+
+ mem_ctx = talloc_init();
+
+ if (!mem_ctx) {
+ printf("memory allocation error\n");
+ result = 1;
+ goto done;
+ }
+
+ if (tdb_prs_fetch(tdb, keystr, &ps, mem_ctx) != 0) {
+ printf("error fetching descriptor for printer %s\n",
+ printer);
+ /* cannot do a prs_mem_free() when tdb_prs_fetch fails */
+ /* as the prs structure has not been initialized */
+ tdb_close(tdb);
+ talloc_destroy(mem_ctx);
+ return 1;
+ }
+
+ /* Unpack into security descriptor buffer */
+
+ if (!sec_io_desc_buf("nt_printing_getsec", &secdesc_ctr, &ps, 1)) {
+ printf("error unpacking sec_desc_buf\n");
+ result = 1;
+ goto done;
+ }
+
+ /* Print owner and group sid */
+
+ if (secdesc_ctr->sec->owner_sid) {
+ sid_to_string(sidstr, secdesc_ctr->sec->owner_sid);
+ } else {
+ fstrcpy(sidstr, "");
+ }
+
+ printf("%s\n", sidstr);
+
+ if (secdesc_ctr->sec->grp_sid) {
+ sid_to_string(sidstr, secdesc_ctr->sec->grp_sid);
+ } else {
+ fstrcpy(sidstr, "");
+ }
+
+ printf("%s\n", sidstr);
+
+ /* Print aces */
+
+ if (!secdesc_ctr->sec->dacl) {
+ result = 0;
+ goto done;
+ }
+
+ for (i = 0; i < secdesc_ctr->sec->dacl->num_aces; i++) {
+ SEC_ACE *ace = &secdesc_ctr->sec->dacl->ace[i];
+
+ sid_to_string(sidstr, &ace->sid);
+
+ printf("%d %d 0x%08x %s\n", ace->type, ace->flags,
+ ace->info.mask, sidstr);
+ }
+
+ done:
+ if (tdb) tdb_close(tdb);
+ if (mem_ctx) talloc_destroy(mem_ctx);
+ if (secdesc_ctr) free_sec_desc_buf(&secdesc_ctr);
+ prs_mem_free(&ps);
+
+ return result;
+}
+
+/* Set a printer security descriptor */
+
+int psec_setsec(char *printer)
+{
+ DOM_SID user_sid, group_sid;
+ SEC_ACE *ace_list = NULL;
+ SEC_ACL *dacl = NULL;
+ SEC_DESC *sd;
+ SEC_DESC_BUF *sdb = NULL;
+ int result = 0, num_aces = 0;
+ fstring line, keystr, tdb_path;
+ size_t size;
+ prs_struct ps;
+ TALLOC_CTX *mem_ctx = NULL;
+ BOOL has_user_sid = False, has_group_sid = False;
+
+ ZERO_STRUCT(ps);
+
+ /* Open tdb for reading */
+
+ slprintf(tdb_path, sizeof(tdb_path) - 1, "%s/ntdrivers.tdb",
+ lp_lockdir());
+
+ tdb = tdb_open(tdb_path, 0, 0, O_RDWR, 0600);
+
+ if (!tdb) {
+ printf("psec: failed to open nt drivers database: %s\n",
+ sys_errlist[errno]);
+ result = 1;
+ goto done;
+ }
+
+ /* Read owner and group sid */
+
+ fgets(line, sizeof(fstring), stdin);
+ if (line[0] != '\n') {
+ string_to_sid(&user_sid, line);
+ has_user_sid = True;
+ }
+
+ fgets(line, sizeof(fstring), stdin);
+ if (line[0] != '\n') {
+ string_to_sid(&group_sid, line);
+ has_group_sid = True;
+ }
+
+ /* Read ACEs from standard input for discretionary ACL */
+
+ while(fgets(line, sizeof(fstring), stdin)) {
+ int ace_type, ace_flags;
+ uint32 ace_mask;
+ fstring sidstr;
+ DOM_SID sid;
+ SEC_ACCESS sa;
+
+ if (sscanf(line, "%d %d 0x%x %s", &ace_type, &ace_flags,
+ &ace_mask, sidstr) != 4) {
+ continue;
+ }
+
+ string_to_sid(&sid, sidstr);
+
+ ace_list = Realloc(ace_list, sizeof(SEC_ACE) *
+ (num_aces + 1));
+
+ init_sec_access(&sa, ace_mask);
+ init_sec_ace(&ace_list[num_aces], &sid, ace_type, sa,
+ ace_flags);
+
+ num_aces++;
+ }
+
+ dacl = make_sec_acl(ACL_REVISION, num_aces, ace_list);
+ free(ace_list);
+
+ /* Create security descriptor */
+
+ sd = make_sec_desc(SEC_DESC_REVISION,
+ has_user_sid ? &user_sid : NULL,
+ has_group_sid ? &group_sid : NULL,
+ NULL, /* System ACL */
+ dacl, /* Discretionary ACL */
+ &size);
+
+ free_sec_acl(&dacl);
+
+ sdb = make_sec_desc_buf(size, sd);
+
+ free_sec_desc(&sd);
+
+ /* Write security descriptor to tdb */
+
+ mem_ctx = talloc_init();
+
+ if (!mem_ctx) {
+ printf("memory allocation error\n");
+ result = 1;
+ goto done;
+ }
+
+ prs_init(&ps, (uint32)sec_desc_size(sdb->sec) +
+ sizeof(SEC_DESC_BUF), 4, mem_ctx, MARSHALL);
+
+ if (!sec_io_desc_buf("nt_printing_setsec", &sdb, &ps, 1)) {
+ printf("sec_io_desc_buf failed\n");
+ goto done;
+ }
+
+ slprintf(keystr, sizeof(keystr) - 1, "SECDESC/%s", printer);
+
+ if (!tdb_prs_store(tdb, keystr, &ps)==0) {
+ printf("Failed to store secdesc for %s\n", printer);
+ goto done;
+ }
+
+ done:
+ if (tdb) tdb_close(tdb);
+ if (sdb) free_sec_desc_buf(&sdb);
+ if (mem_ctx) talloc_destroy(mem_ctx);
+ prs_mem_free(&ps);
+
+ return result;
+}
+
+/* Help */
+
+void usage(void)
+{
+ printf("Usage: psec getsec|setsec printername\n");
+}
+
+/* Main function */
+
+int main(int argc, char **argv)
+{
+ pstring servicesf = CONFIGFILE;
+
+ /* Argument check */
+
+ if (argc == 1) {
+ usage();
+ return 1;
+ }
+
+ /* Load smb.conf file */
+
+ charset_initialise();
+
+ if (!lp_load(servicesf,False,False,True)) {
+ fprintf(stderr, "Couldn't load confiuration file %s\n",
+ servicesf);
+ return 1;
+ }
+
+ /* Do commands */
+
+ if (strcmp(argv[1], "setsec") == 0) {
+
+ if (argc != 3) {
+ usage();
+ return 1;
+ }
+
+ return psec_setsec(argv[2]);
+ }
+
+ if (strcmp(argv[1], "getsec") == 0) {
+
+ if (argc != 3) {
+ usage();
+ return 1;
+ }
+
+ return psec_getsec(argv[2]);
+ }
+
+ /* An unknown command */
+
+ printf("psec: unknown command %s\n", argv[1]);
+ return 1;
+}
diff --git a/testsuite/printing/vlp.c b/testsuite/printing/vlp.c
new file mode 100644
index 00000000000..2dd028fcd38
--- /dev/null
+++ b/testsuite/printing/vlp.c
@@ -0,0 +1,426 @@
+/*
+ Unix SMB/Netbios implementation.
+
+ Virtual lp system for printer testing
+
+ 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"
+
+#define PRINT_TDB "/tmp/vlp.tdb"
+#define PRINT_FIRSTJOB "100"
+
+static TDB_CONTEXT *tdb;
+
+struct vlp_job {
+ fstring owner;
+ int jobid;
+ fstring jobname;
+ int size;
+ int status;
+ time_t submit_time;
+ int deleted;
+};
+
+/* Print usage */
+
+static void usage(void)
+{
+ printf("Usage: print-test lpq|lprm|print|queuepause|queueresume|"
+ "lppause|lpresume [args]\n");
+}
+
+/* Return an array of vlp jobs that is the printer queue */
+
+static void get_job_list(char *printer, struct vlp_job **job_list,
+ int *num_jobs)
+{
+ fstring keystr;
+ TDB_DATA data;
+
+ slprintf(keystr, sizeof(keystr) - 1, "LPQ/%s", printer);
+ data = tdb_fetch_by_string(tdb, keystr);
+
+ *job_list = (struct vlp_job *)data.dptr;
+ *num_jobs = data.dsize / sizeof(struct vlp_job);
+}
+
+/* Store an array of vl jobs for the queue */
+
+static void set_job_list(char *printer, struct vlp_job *job_list,
+ int num_jobs)
+{
+ fstring keystr;
+
+ slprintf(keystr, sizeof(keystr) - 1, "LPQ/%s", printer);
+
+ tdb_store_by_string(tdb, keystr, job_list,
+ num_jobs * sizeof(struct vlp_job));
+}
+
+/* Return the next job number for a printer */
+
+static int next_jobnum(char *printer)
+{
+ fstring keystr;
+ int jobnum;
+
+ slprintf(keystr, sizeof(keystr) - 1, "JOBNUM/%s", printer);
+
+ tdb_lock_bystring(tdb, keystr);
+
+ jobnum = tdb_fetch_int(tdb, keystr);
+
+ /* Create next job index if none exists */
+
+ if (jobnum == -1) {
+ jobnum = atoi(PRINT_FIRSTJOB);
+ }
+
+ jobnum++;
+ tdb_store_int(tdb, keystr, jobnum);
+
+ tdb_unlock_bystring(tdb, keystr);
+
+ return jobnum;
+}
+
+static void set_printer_status(char *printer, int status)
+{
+ fstring keystr;
+ int result;
+
+ slprintf(keystr, sizeof(keystr) - 1, "STATUS/%s", printer);
+ result = tdb_store_int(tdb, keystr, status);
+}
+
+static int get_printer_status(char *printer)
+{
+ fstring keystr;
+ TDB_DATA data;
+
+ slprintf(keystr, sizeof(keystr) - 1, "STATUS/%s", printer);
+
+ data.dptr = keystr;
+ data.dsize = strlen(keystr) + 1;
+
+ if (!tdb_exists(tdb, data)) {
+ set_printer_status(printer, LPSTAT_OK);
+ return LPSTAT_OK;
+ }
+
+ return tdb_fetch_int(tdb, keystr);
+}
+
+/* Display printer queue */
+
+static int lpq_command(int argc, char **argv)
+{
+ char *printer;
+ struct vlp_job *job_list = NULL;
+ int i, num_jobs, job_count = 0;
+
+ if (argc != 2) {
+ printf("Usage: lpq <printername>\n");
+ return 1;
+ }
+
+ printer = argv[1];
+
+ /* Display printer status */
+
+ switch (get_printer_status(printer)) {
+ case LPSTAT_OK:
+ printf("enabled\n");
+ break;
+ case LPSTAT_STOPPED:
+ printf("disabled\n");
+ break;
+ case LPSTAT_ERROR:
+ default:
+ printf("error\n");
+ break;
+ }
+
+ /* Print queued documents */
+
+ get_job_list(printer, &job_list, &num_jobs);
+
+ for (i = 0; i < num_jobs; i++) {
+ if (job_list[i].deleted) continue;
+ printf("%d\t%d\t%d\t%ld\t%s\t%s\n", job_list[i].jobid,
+ job_list[i].size,
+ (i == 0 && job_list[i].status == LPQ_QUEUED) ?
+ LPQ_SPOOLING : job_list[i].status,
+ job_list[i].submit_time, job_list[i].owner,
+ job_list[i].jobname);
+ job_count++;
+ }
+
+ free(job_list);
+
+ return 0;
+}
+
+/* Remove a job */
+
+static int lprm_command(int argc, char **argv)
+{
+ char *printer;
+ int jobid, num_jobs, i;
+ struct vlp_job *job_list;
+
+ if (argc < 3) {
+ printf("Usage: lprm <printername> <jobid>\n");
+ return 1;
+ }
+
+ printer = argv[1];
+ jobid = atoi(argv[2]);
+
+ get_job_list(printer, &job_list, &num_jobs);
+
+ for (i = 0; i < num_jobs; i++) {
+ if (job_list[i].jobid == jobid) {
+ job_list[i].deleted = 1;
+ set_job_list(printer, job_list, num_jobs);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/* print command = print-test %p %s */
+
+static int print_command(int argc, char **argv)
+{
+ char *printer;
+ fstring keystr;
+ struct passwd *pw;
+ TDB_DATA value;
+ struct vlp_job job;
+ int i;
+
+ if (argc < 3) {
+ printf("Usage: print <printername> <jobname>\n");
+ return 1;
+ }
+
+ printer = argv[1];
+
+ ZERO_STRUCT(job);
+
+ /* Create a job record */
+
+ for (i = 2; i < argc; i++) {
+ fstrcat(job.jobname, argv[i]);
+ if (i < argc - 1) {
+ fstrcat(job.jobname, " ");
+ }
+ }
+
+ if (!(pw = getpwuid(getuid()))) {
+ return 1;
+ }
+
+ fstrcpy(job.owner, pw->pw_name);
+
+ job.jobid = next_jobnum(printer);
+ job.size = 666;
+ job.submit_time = time(NULL);
+
+ /* Store job entry in queue */
+
+ slprintf(keystr, sizeof(keystr) - 1, "LPQ/%s", printer);
+
+ value = tdb_fetch_by_string(tdb, keystr);
+
+ if (value.dptr) {
+
+ /* Add job to end of queue */
+
+ value.dptr = realloc(value.dptr, value.dsize +
+ sizeof(struct vlp_job));
+ if (!value.dptr) return 1;
+
+ memcpy(value.dptr + value.dsize, &job, sizeof(struct vlp_job));
+
+ tdb_store_by_string(tdb, keystr, value.dptr, value.dsize +
+ sizeof(struct vlp_job));
+
+ free(value.dptr);
+
+ } else {
+
+ /* Create new queue */
+
+ tdb_store_by_string(tdb, keystr, &job, sizeof(struct vlp_job));
+ }
+
+ return 0;
+}
+
+/* Pause the queue */
+
+static int queuepause_command(int argc, char **argv)
+{
+ char *printer;
+
+ if (argc != 2) {
+ printf("Usage: queuepause <printername>\n");
+ return 1;
+ }
+
+ printer = argv[1];
+ set_printer_status(printer, LPSTAT_STOPPED);
+
+ return 0;
+}
+
+/* Resume the queue */
+
+static int queueresume_command(int argc, char **argv)
+{
+ char *printer;
+
+ if (argc != 2) {
+ printf("Usage: queueresume <printername>\n");
+ return 1;
+ }
+
+ printer = argv[1];
+ set_printer_status(printer, LPSTAT_OK);
+
+ return 0;
+}
+
+/* Pause a job */
+
+static int lppause_command(int argc, char **argv)
+{
+ struct vlp_job *job_list;
+ char *printer;
+ int jobid, num_jobs, i;
+
+ if (argc != 3) {
+ printf("Usage: lppause <printername> <jobid>\n");
+ return 1;
+ }
+
+ printer = argv[1];
+ jobid = atoi(argv[2]);
+
+ get_job_list(printer, &job_list, &num_jobs);
+
+ for (i = 0; i < num_jobs; i++) {
+ if (job_list[i].jobid == jobid) {
+ job_list[i].status = LPQ_PAUSED;
+ set_job_list(printer, job_list, num_jobs);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* Resume a job */
+
+static int lpresume_command(int argc, char **argv)
+{
+ struct vlp_job *job_list;
+ char *printer;
+ int jobid, num_jobs, i;
+
+ if (argc != 3) {
+ printf("Usage: lpresume <printername> <jobid>\n");
+ return 1;
+ }
+
+ printer = argv[1];
+ jobid = atoi(argv[2]);
+
+ get_job_list(printer, &job_list, &num_jobs);
+
+ for (i = 0; i < num_jobs; i++) {
+ if (job_list[i].jobid == jobid) {
+ job_list[i].status = LPQ_QUEUED;
+ set_job_list(printer, job_list, num_jobs);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+int main(int argc, char **argv)
+{
+ /* Parameter check */
+
+ if (argc == 1) {
+ usage();
+ return 1;
+ }
+
+ /* Initialise */
+
+ if (!(tdb = tdb_open(PRINT_TDB, 0, 0, O_RDWR | O_CREAT,
+ 0666))) {
+ printf("%s: unable to open %s\n", argv[0], PRINT_TDB);
+ return 1;
+ }
+
+ /* Ensure we are modes 666 */
+
+ chmod(PRINT_TDB, 0666);
+
+ /* Do commands */
+
+ if (strcmp(argv[1], "lpq") == 0) {
+ return lpq_command(argc - 1, &argv[1]);
+ }
+
+ if (strcmp(argv[1], "lprm") == 0) {
+ return lprm_command(argc - 1, &argv[1]);
+ }
+
+ if (strcmp(argv[1], "print") == 0) {
+ return print_command(argc - 1, &argv[1]);
+ }
+
+ if (strcmp(argv[1], "queuepause") == 0) {
+ return queuepause_command(argc - 1, &argv[1]);
+ }
+
+ if (strcmp(argv[1], "queueresume") == 0) {
+ return queueresume_command(argc - 1, &argv[1]);
+ }
+
+ if (strcmp(argv[1], "lppause") == 0) {
+ return lppause_command(argc - 1, &argv[1]);
+ }
+
+ if (strcmp(argv[1], "lpresume") == 0) {
+ return lpresume_command(argc - 1, &argv[1]);
+ }
+
+ /* Unknown command */
+
+ printf("%s: invalid command %s\n", argv[0], argv[1]);
+ return 1;
+}
diff --git a/testsuite/server/ipc.exp b/testsuite/server/ipc.exp
new file mode 100644
index 00000000000..ae0688872a8
--- /dev/null
+++ b/testsuite/server/ipc.exp
@@ -0,0 +1,44 @@
+#
+# Test operations on IPC$ share
+#
+
+# Initialisation
+
+load_lib env-single.exp
+load_lib smbclient.exp
+
+set timeout 10
+
+# Spawn a smbclient
+
+if {![spawn_smbclient //$server/ipc\$ -U $user]} {
+ perror "error spawning smbclient"
+ return -1
+}
+
+#
+# Start performing tests
+#
+
+global smb_prompt
+
+do_smbclient "lcd /tmp\r" "lcd /tmp"
+do_smbclient "!touch test.out\r" "touch test.out"
+
+foreach { op } { "dir\r" "put test.out\r" "get test.out\r" \
+ "get /etc/passwd\r" "mkdir foo\r" "print test.out\r" } {
+
+ set action "doing $op"
+ set output [do_smbclient $op $action]
+
+ if {![regexp "ERR" $output]} {
+ fail $action
+ puts $output
+ } else {
+ pass $action
+ }
+}
+
+# Clean up
+
+file delete test.out
diff --git a/testsuite/server/masktest.exp b/testsuite/server/masktest.exp
new file mode 100644
index 00000000000..532291bffb8
--- /dev/null
+++ b/testsuite/server/masktest.exp
@@ -0,0 +1,57 @@
+#
+# Test various things using the masktest program
+#
+
+# Initialisation
+
+load_lib env-single.exp
+
+set timeout 10
+
+# Test each wildcard individually, then all together at once
+
+set testlist {{"abc" "<"} {"abc" ">"} {"abc" "\""} {"abc" "?"} {"abc" "*"} \
+ {"abc" "."} {"abc" "<>\"?*."}}
+
+foreach { test } $testlist {
+
+ set got_output 0
+ set fail 0
+
+ # Spawn masktest
+
+ spawn bin/masktest //$server/$share -U $user -n 1000 -a \
+ -f [lindex $test 0] -m [concat [lindex $test 0] [lindex $test 1]]
+
+ # Check output
+
+ while 1 {
+ expect {
+ -re "(...) (...) \[0-9\]+ mask=" {
+ if { $expect_out(1,string) != $expect_out(2,string) } {
+ fail "masktest [lindex $test 1]"
+ puts $expect_out(0,string);
+ set fail 1
+ break;
+ } else {
+ set got_output 1
+ }
+ }
+ eof { break }
+ }
+ }
+
+ # Produce result
+
+ set testname "[lindex $test 0] [lindex $test 1]"
+
+ if {$got_output} {
+ if {$fail} {
+ fail "masktest $testname"
+ } else {
+ pass "masktest $testname"
+ }
+ } else {
+ perror "no output seen for test $testname"
+ }
+}
diff --git a/testsuite/server/rename.exp b/testsuite/server/rename.exp
new file mode 100644
index 00000000000..77e7297776b
--- /dev/null
+++ b/testsuite/server/rename.exp
@@ -0,0 +1,59 @@
+#
+# Test misc file operations
+#
+
+# Initialisation
+
+load_lib env-single.exp
+load_lib smbclient.exp
+
+set timeout 10
+
+# Spawn a connection
+
+if {![spawn_smbclient //$server/$share -U $user]} {
+ perror "error spawning smbclient"
+ return -1
+}
+
+# Do wildcard rename test
+
+foreach { op } {"!touch /tmp/test.out\r" "lcd /tmp\r" "rm test.out\r" \
+ "put test.out\r"} {
+
+ set action "doing $op"
+ set output [do_smbclient $op $action]
+
+ if {[regexp "ERR" $output]} {
+ perror $action
+ puts $output
+ return -1;
+ }
+}
+
+file delete "/tmp/test.out"
+
+# Perform rename
+
+set output [do_smbclient "rename *.out *.dat\r" "wildcard rename"]
+
+if {[regexp "ERR" $output]} {
+ perror "wildcard rename didn't work"
+ return -1
+}
+
+# Check it worked
+
+set testname "wildcard match"
+set output [do_smbclient "dir\r" "wildcard rename check"]
+
+if {[regexp "test.dat" $output]} {
+ pass $testname
+} else {
+ fail $testname
+}
+
+# Clean up
+
+set op "rm test.dat\r"
+do_smbclient $op "doing $op"
diff --git a/testsuite/server/xfer.exp b/testsuite/server/xfer.exp
new file mode 100644
index 00000000000..6d00b29885c
--- /dev/null
+++ b/testsuite/server/xfer.exp
@@ -0,0 +1,48 @@
+#
+# Test file transfer
+#
+
+# Initialisation
+
+load_lib util-defs.exp
+load_lib smbclient.exp
+load_lib env-single.exp
+
+set timeout 60
+
+# Spawn a connection
+
+if {![spawn_smbclient //$server/$share -U $user]} {
+ perror "error spawning smbclient"
+ return -1
+}
+
+# Create a big file, store it and fetch it again
+
+foreach { op } { "!dd if=/dev/urandom of=/tmp/file bs=1048576 count=1\r" \
+ "lcd /tmp\r" "put file\r" "get file /tmp/file2\r" } {
+
+ set action "doing $op"
+ set output [do_smbclient $op $action]
+
+ if {[regexp "ERR" $output]} {
+ error $action
+ puts $output
+ return -1
+ }
+}
+
+# Compare the two files
+
+set output [util_start "diff" "/tmp/file /tmp/file2" ""]
+
+if {[regexp "differ" $output]} {
+ fail "xfertest"
+ puts $output
+} else {
+ pass "xfertest"
+}
+
+# Clean up temporary files
+
+file delete /tmp/file /tmp/file2
diff --git a/testsuite/smbd/Makefile.se_access_check b/testsuite/smbd/Makefile.se_access_check
new file mode 100644
index 00000000000..5637fa2f2fc
--- /dev/null
+++ b/testsuite/smbd/Makefile.se_access_check
@@ -0,0 +1,24 @@
+#
+# Makefile for se_access_check tests
+#
+
+include ../../source/Makefile
+
+# Objects common to all tests
+
+SE_ACCESS_CHECK_OBJ1 = $(LIB_OBJ) $(UBIQX_OBJ) $(PARAM_OBJ) $(RPC_PARSE_OBJ) \
+ $(LIBSMB_OBJ) lib/util_seaccess.o nsswitch/common.o
+
+SE_ACCESS_CHECK_OBJS = $(SE_ACCESS_CHECK_OBJ1:%=$(srcdir)/%) \
+ se_access_check_utils.o
+
+# Targets for individual tests
+
+se_access_check_nullsd: $(SE_ACCESS_CHECK_OBJS) se_access_check_nullsd.o
+se_access_check_everyone: $(SE_ACCESS_CHECK_OBJS) se_access_check_everyone.o
+se_access_check_allowall: $(SE_ACCESS_CHECK_OBJS) se_access_check_allowall.o
+se_access_check_denyall: $(SE_ACCESS_CHECK_OBJS) se_access_check_denyall.o
+se_access_check_allowsome: $(SE_ACCESS_CHECK_OBJS) se_access_check_allowsome.o
+se_access_check_denysome: $(SE_ACCESS_CHECK_OBJS) se_access_check_denysome.o
+se_access_check_empty: $(SE_ACCESS_CHECK_OBJS) se_access_check_empty.o
+se_access_check_printer: $(SE_ACCESS_CHECK_OBJS) se_access_check_printer.o
diff --git a/testsuite/smbd/Makefile.sec_ctx b/testsuite/smbd/Makefile.sec_ctx
new file mode 100644
index 00000000000..c45ab5bb5e8
--- /dev/null
+++ b/testsuite/smbd/Makefile.sec_ctx
@@ -0,0 +1,57 @@
+#
+# Makefile for sec_ctx tests
+#
+
+include ../../source/Makefile
+
+# Objects common to all tests
+
+SEC_CTX_OBJ1 = $(RPC_CLIENT_OBJ) $(LIB_OBJ) $(RPC_PARSE_OBJ) $(PARAM_OBJ) \
+ $(LIBSMB_OBJ) $(PASSDB_OBJ) $(UBIQX_OBJ) smbd/password.o smbd/uid.o \
+ smbd/chgpasswd.o smbd/sec_ctx.o
+
+SEC_CTX_OBJS = $(SEC_CTX_OBJ1:%=$(srcdir)/%) sec_ctx_utils.o
+
+# Targets for tests
+
+SEC_CTX_NONROOT_OBJS = $(SEC_CTX_OBJS) sec_ctx_nonroot.o
+
+sec_ctx_nonroot: $(SEC_CTX_NONROOT_OBJS)
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SEC_CTX_NONROOT_OBJS) $(LIBS)
+
+SEC_CTX_STACK_OBJS = $(SEC_CTX_OBJS) sec_ctx_stack.o
+
+sec_ctx_stack: $(SEC_CTX_STACK_OBJS)
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SEC_CTX_STACK_OBJS) $(LIBS)
+
+SEC_CTX_FLOW_OBJS = $(SEC_CTX_OBJS) sec_ctx_flow.o
+
+sec_ctx_flow: $(SEC_CTX_FLOW_OBJS)
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SEC_CTX_FLOW_OBJS) $(LIBS)
+
+SEC_CTX_TORTURE_OBJS = $(SEC_CTX_OBJS) sec_ctx_torture.o
+
+sec_ctx_torture: $(SEC_CTX_TORTURE_OBJS)
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SEC_CTX_TORTURE_OBJS) $(LIBS)
+
+SEC_CTX_CURRENT_USER_OBJS = $(SEC_CTX_OBJS) sec_ctx_current_user.o
+
+sec_ctx_current_user: $(SEC_CTX_CURRENT_USER_OBJS)
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SEC_CTX_CURRENT_USER_OBJS) $(LIBS)
+
+SEC_CTX_GROUPS_OBJS = $(SEC_CTX_OBJS) sec_ctx_groups.o
+
+sec_ctx_groups: $(SEC_CTX_GROUPS_OBJS)
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SEC_CTX_GROUPS_OBJS) $(LIBS)
+
+SEC_CTX_ROOT_OBJS = $(SEC_CTX_OBJS) sec_ctx_root.o
+
+sec_ctx_root: $(SEC_CTX_ROOT_OBJS)
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SEC_CTX_ROOT_OBJS) $(LIBS)
diff --git a/testsuite/smbd/se_access_check.exp b/testsuite/smbd/se_access_check.exp
new file mode 100644
index 00000000000..cd84ab8ee08
--- /dev/null
+++ b/testsuite/smbd/se_access_check.exp
@@ -0,0 +1,54 @@
+#
+# @(#) Test se_access_check() function
+#
+
+#
+# Unix SMB/Netbios implementation.
+# 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.
+#
+
+load_lib "compile.exp"
+load_lib "util-defs.exp"
+
+# Run tests from C source files
+
+set se_access_check_tests [list \
+ { "null security descriptor" "se_access_check_nullsd" } \
+ { "security descriptor allow everyone" "se_access_check_allowall" } \
+ { "security descriptor allow everyone" "se_access_check_allowall" } \
+ { "security descriptor deny everyone" "se_access_check_denyall" } \
+ { "empty security descriptor" "se_access_check_empty" } \
+ { "allow some users access" "se_access_check_allowsome" } \
+ { "deny some users access" "se_access_check_denysome" } \
+ { "printer access permissions" "se_access_check_printer" } \
+ ]
+
+foreach { test } $se_access_check_tests {
+ set test_desc [lindex $test 0]
+ set test_file [lindex $test 1]
+
+ simple_make "se_access_check" $test_file
+ set output [util_start "$srcdir/$subdir/$test_file" ]
+
+ if { [regexp "PASS" $output] } {
+ pass $test_desc
+ file delete "$srcdir/$subdir/$test_file" "$srcdir/$subdir/$test_file.o"
+ } else {
+ fail $test_desc
+ puts $output
+ }
+}
diff --git a/testsuite/smbd/se_access_check_allowall.c b/testsuite/smbd/se_access_check_allowall.c
new file mode 100644
index 00000000000..b49e8e52e7f
--- /dev/null
+++ b/testsuite/smbd/se_access_check_allowall.c
@@ -0,0 +1,87 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Security context tests
+ 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 "se_access_check_utils.h"
+
+/* Globals */
+
+BOOL failed;
+SEC_DESC *sd;
+
+struct ace_entry acl_allowall[] = {
+ { SEC_ACE_TYPE_ACCESS_ALLOWED, SEC_ACE_FLAG_CONTAINER_INHERIT,
+ GENERIC_ALL_ACCESS, "S-1-1-0" },
+ { 0, 0, 0, NULL}
+};
+
+/* Check that access is always allowed for a NULL security descriptor */
+
+BOOL allowall_check(struct passwd *pw, int ngroups, gid_t *groups)
+{
+ uint32 acc_granted, status;
+ BOOL result;
+
+ result = se_access_check(sd, pw->pw_uid, pw->pw_gid,
+ ngroups, groups,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &acc_granted, &status);
+
+ if (!result || status != NT_STATUS_NO_PROBLEMO ||
+ acc_granted != GENERIC_ALL_ACCESS) {
+ printf("FAIL: allowall se_access_check %d/%d\n",
+ pw->pw_uid, pw->pw_gid);
+ failed = True;
+ }
+
+ return True;
+}
+
+/* Main function */
+
+int main(int argc, char **argv)
+{
+ /* Initialisation */
+
+ generate_wellknown_sids();
+
+ /* Create security descriptor */
+
+ sd = build_sec_desc(acl_allowall, NULL, NULL_SID, NULL_SID);
+
+ if (!sd) {
+ printf("FAIL: could not build security descriptor\n");
+ return 1;
+ }
+
+ /* Run test */
+
+ visit_pwdb(allowall_check);
+
+ /* Return */
+
+ if (!failed) {
+ printf("PASS\n");
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/testsuite/smbd/se_access_check_allowsome.c b/testsuite/smbd/se_access_check_allowsome.c
new file mode 100644
index 00000000000..529b2007622
--- /dev/null
+++ b/testsuite/smbd/se_access_check_allowsome.c
@@ -0,0 +1,104 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Security context tests
+ 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 "se_access_check_utils.h"
+
+/* Globals */
+
+BOOL failed;
+SEC_DESC *sd;
+
+struct ace_entry acl_allowsome[] = {
+ { SEC_ACE_TYPE_ACCESS_ALLOWED, SEC_ACE_FLAG_CONTAINER_INHERIT,
+ GENERIC_ALL_ACCESS, "user0" },
+ { SEC_ACE_TYPE_ACCESS_ALLOWED, SEC_ACE_FLAG_CONTAINER_INHERIT,
+ GENERIC_ALL_ACCESS, "user2" },
+ { 0, 0, 0, NULL}
+};
+
+BOOL allowsome_check(struct passwd *pw, int ngroups, gid_t *groups)
+{
+ uint32 acc_granted, status;
+ fstring name;
+ BOOL result;
+ int len1, len2;
+
+ /* Check only user0 and user2 allowed access */
+
+ result = se_access_check(sd, pw->pw_uid, pw->pw_gid,
+ ngroups, groups,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &acc_granted, &status);
+
+ len1 = (int)strlen(pw->pw_name) - strlen("user0");
+ len2 = (int)strlen(pw->pw_name) - strlen("user2");
+
+ if ((strncmp("user0", &pw->pw_name[MAX(len1, 0)],
+ strlen("user0")) == 0) ||
+ (strncmp("user2", &pw->pw_name[MAX(len2, 0)],
+ strlen("user2")) == 0)) {
+ if (!result || acc_granted != GENERIC_ALL_ACCESS) {
+ printf("FAIL: access not granted for %s\n",
+ pw->pw_name);
+ }
+ } else {
+ if (result || acc_granted != 0) {
+ printf("FAIL: access granted for %s\n", pw->pw_name);
+ }
+ }
+
+ printf("result %s for user %s\n", result ? "allowed" : "denied",
+ pw->pw_name);
+
+ return True;
+}
+
+/* Main function */
+
+int main(int argc, char **argv)
+{
+ /* Initialisation */
+
+ generate_wellknown_sids();
+
+ /* Create security descriptor */
+
+ sd = build_sec_desc(acl_allowsome, NULL, NULL_SID, NULL_SID);
+
+ if (!sd) {
+ printf("FAIL: could not build security descriptor\n");
+ return 1;
+ }
+
+ /* Run test */
+
+ visit_pwdb(allowsome_check);
+
+ /* Return */
+
+ if (!failed) {
+ printf("PASS\n");
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/testsuite/smbd/se_access_check_denyall.c b/testsuite/smbd/se_access_check_denyall.c
new file mode 100644
index 00000000000..016e6f6d549
--- /dev/null
+++ b/testsuite/smbd/se_access_check_denyall.c
@@ -0,0 +1,86 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Security context tests
+ 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 "se_access_check_utils.h"
+
+/* Globals */
+
+BOOL failed;
+SEC_DESC *sd;
+
+struct ace_entry acl_denyall[] = {
+ { SEC_ACE_TYPE_ACCESS_DENIED, SEC_ACE_FLAG_CONTAINER_INHERIT,
+ GENERIC_ALL_ACCESS, "S-1-1-0" },
+ { 0, 0, 0, NULL}
+};
+
+/* Check that access is always allowed for a NULL security descriptor */
+
+BOOL denyall_check(struct passwd *pw, int ngroups, gid_t *groups)
+{
+ uint32 acc_granted, status;
+ BOOL result;
+
+ result = se_access_check(sd, pw->pw_uid, pw->pw_gid,
+ ngroups, groups,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &acc_granted, &status);
+
+ if (result || acc_granted != 0) {
+ printf("FAIL: denyall se_access_check %d/%d\n",
+ pw->pw_uid, pw->pw_gid);
+ failed = True;
+ }
+
+ return True;
+}
+
+/* Main function */
+
+int main(int argc, char **argv)
+{
+ /* Initialisation */
+
+ generate_wellknown_sids();
+
+ /* Create security descriptor */
+
+ sd = build_sec_desc(acl_denyall, NULL, NULL_SID, NULL_SID);
+
+ if (!sd) {
+ printf("FAIL: could not build security descriptor\n");
+ return 1;
+ }
+
+ /* Run test */
+
+ visit_pwdb(denyall_check);
+
+ /* Return */
+
+ if (!failed) {
+ printf("PASS\n");
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/testsuite/smbd/se_access_check_denysome.c b/testsuite/smbd/se_access_check_denysome.c
new file mode 100644
index 00000000000..1f0aaaf1392
--- /dev/null
+++ b/testsuite/smbd/se_access_check_denysome.c
@@ -0,0 +1,106 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Security context tests
+ 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 "se_access_check_utils.h"
+
+/* Globals */
+
+BOOL failed;
+SEC_DESC *sd;
+
+struct ace_entry acl_denysome[] = {
+ { SEC_ACE_TYPE_ACCESS_DENIED, SEC_ACE_FLAG_CONTAINER_INHERIT,
+ GENERIC_ALL_ACCESS, "user1" },
+ { SEC_ACE_TYPE_ACCESS_DENIED, SEC_ACE_FLAG_CONTAINER_INHERIT,
+ GENERIC_ALL_ACCESS, "user3" },
+ { SEC_ACE_TYPE_ACCESS_ALLOWED, SEC_ACE_FLAG_CONTAINER_INHERIT,
+ GENERIC_ALL_ACCESS, "S-1-1-0" },
+ { 0, 0, 0, NULL}
+};
+
+BOOL denysome_check(struct passwd *pw, int ngroups, gid_t *groups)
+{
+ uint32 acc_granted, status;
+ fstring name;
+ BOOL result;
+ int len1, len2;
+
+ /* Check only user1 and user3 denied access */
+
+ result = se_access_check(sd, pw->pw_uid, pw->pw_gid,
+ ngroups, groups,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &acc_granted, &status);
+
+ len1 = (int)strlen(pw->pw_name) - strlen("user1");
+ len2 = (int)strlen(pw->pw_name) - strlen("user3");
+
+ if ((strncmp("user1", &pw->pw_name[MAX(len1, 0)],
+ strlen("user1")) == 0) ||
+ (strncmp("user3", &pw->pw_name[MAX(len2, 0)],
+ strlen("user3")) == 0)) {
+ if (result || acc_granted != 0) {
+ printf("FAIL: access not denied for %s\n",
+ pw->pw_name);
+ }
+ } else {
+ if (!result || acc_granted != GENERIC_ALL_ACCESS) {
+ printf("FAIL: access denied for %s\n", pw->pw_name);
+ }
+ }
+
+ printf("result %s for user %s\n", result ? "allowed" : "denied",
+ pw->pw_name);
+
+ return True;
+}
+
+/* Main function */
+
+int main(int argc, char **argv)
+{
+ /* Initialisation */
+
+ generate_wellknown_sids();
+
+ /* Create security descriptor */
+
+ sd = build_sec_desc(acl_denysome, NULL, NULL_SID, NULL_SID);
+
+ if (!sd) {
+ printf("FAIL: could not build security descriptor\n");
+ return 1;
+ }
+
+ /* Run test */
+
+ visit_pwdb(denysome_check);
+
+ /* Return */
+
+ if (!failed) {
+ printf("PASS\n");
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/testsuite/smbd/se_access_check_empty.c b/testsuite/smbd/se_access_check_empty.c
new file mode 100644
index 00000000000..6602b1147ee
--- /dev/null
+++ b/testsuite/smbd/se_access_check_empty.c
@@ -0,0 +1,109 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Security context tests
+ 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 "se_access_check_utils.h"
+
+/* Globals */
+
+BOOL failed;
+SEC_DESC *sd;
+
+struct ace_entry acl_empty[] = {
+ { 0, 0, 0, NULL}
+};
+
+/* Check that access is always allowed for a NULL security descriptor */
+
+BOOL emptysd_check(struct passwd *pw, int ngroups, gid_t *groups)
+{
+ uint32 acc_granted, status;
+ BOOL result;
+
+ /* For no DACL, access is allowed and the desired access mask is
+ returned */
+
+ result = se_access_check(sd, pw->pw_uid, pw->pw_gid,
+ ngroups, groups,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &acc_granted, &status);
+
+ if (!result || !(acc_granted == SEC_RIGHTS_MAXIMUM_ALLOWED)) {
+ printf("FAIL: no dacl for %s (%d/%d)\n", pw->pw_name,
+ pw->pw_uid, pw->pw_gid);
+ failed = True;
+ }
+
+ result = se_access_check(sd, pw->pw_uid, pw->pw_gid,
+ ngroups, groups, 0x1234,
+ &acc_granted, &status);
+
+ if (!result || !(acc_granted == 0x1234)) {
+ printf("FAIL: no dacl2 for %s (%d/%d)\n", pw->pw_name,
+ pw->pw_uid, pw->pw_gid);
+ failed = True;
+ }
+
+ /* If desired access mask is empty then no access is allowed */
+
+ result = se_access_check(sd, pw->pw_uid, pw->pw_gid,
+ ngroups, groups, 0,
+ &acc_granted, &status);
+
+ if (result) {
+ printf("FAIL: zero desired access for %s (%d/%d)\n",
+ pw->pw_name, pw->pw_uid, pw->pw_gid);
+ failed = True;
+ }
+
+ return True;
+}
+
+/* Main function */
+
+int main(int argc, char **argv)
+{
+ /* Initialisation */
+
+ generate_wellknown_sids();
+
+ /* Create security descriptor */
+
+ sd = build_sec_desc(acl_empty, NULL, NULL_SID, NULL_SID);
+
+ if (!sd) {
+ printf("FAIL: could not build security descriptor\n");
+ return 1;
+ }
+
+ /* Run test */
+
+ visit_pwdb(emptysd_check);
+
+ /* Return */
+
+ if (!failed) {
+ printf("PASS\n");
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/testsuite/smbd/se_access_check_nullsd.c b/testsuite/smbd/se_access_check_nullsd.c
new file mode 100644
index 00000000000..c042c2b1485
--- /dev/null
+++ b/testsuite/smbd/se_access_check_nullsd.c
@@ -0,0 +1,74 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Security context tests
+ 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 "se_access_check_utils.h"
+
+/* Globals */
+
+BOOL failed;
+
+/* Check that access is always allowed for a NULL security descriptor */
+
+BOOL nullsd_check(struct passwd *pw, int ngroups, gid_t *groups)
+{
+ uint32 acc_granted, status;
+ BOOL result;
+
+ result = se_access_check(NULL, pw->pw_uid, pw->pw_gid,
+ ngroups, groups,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &acc_granted, &status);
+
+ if (!result || status != NT_STATUS_NO_PROBLEMO ||
+ acc_granted != SEC_RIGHTS_MAXIMUM_ALLOWED) {
+ printf("FAIL: null se_access_check %d/%d\n",
+ pw->pw_uid, pw->pw_gid);
+ failed = True;
+ }
+
+ printf("access check passed for user %s (%d/%d)\n",
+ pw->pw_name, pw->pw_uid, pw->pw_gid);
+
+ return True;
+}
+
+/* Main function */
+
+int main(int argc, char **argv)
+{
+ /* Initialisation */
+
+ generate_wellknown_sids();
+
+ /* Run test */
+
+ visit_pwdb(nullsd_check);
+
+ /* Return */
+
+ if (!failed) {
+ printf("PASS\n");
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/testsuite/smbd/se_access_check_printer.c b/testsuite/smbd/se_access_check_printer.c
new file mode 100644
index 00000000000..e73a23ce21b
--- /dev/null
+++ b/testsuite/smbd/se_access_check_printer.c
@@ -0,0 +1,212 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Security context tests
+ 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 "se_access_check_utils.h"
+
+/* Globals */
+
+BOOL failed;
+SEC_DESC *sd;
+
+struct ace_entry acl_printer[] = {
+
+ /* Everyone is allowed to print */
+
+ { SEC_ACE_TYPE_ACCESS_ALLOWED, SEC_ACE_FLAG_CONTAINER_INHERIT,
+ PRINTER_ACE_PRINT, "S-1-1-0" },
+
+ /* Except for user0 who uses too much paper */
+
+ { SEC_ACE_TYPE_ACCESS_DENIED, SEC_ACE_FLAG_CONTAINER_INHERIT,
+ PRINTER_ACE_FULL_CONTROL, "user0" },
+
+ /* Users 1 and 2 can manage documents */
+
+ { SEC_ACE_TYPE_ACCESS_ALLOWED, SEC_ACE_FLAG_CONTAINER_INHERIT,
+ PRINTER_ACE_MANAGE_DOCUMENTS, "user1" },
+
+ { SEC_ACE_TYPE_ACCESS_ALLOWED, SEC_ACE_FLAG_CONTAINER_INHERIT,
+ PRINTER_ACE_MANAGE_DOCUMENTS, "user2" },
+
+ /* Domain Admins can also manage documents */
+
+ { SEC_ACE_TYPE_ACCESS_ALLOWED, SEC_ACE_FLAG_CONTAINER_INHERIT,
+ PRINTER_ACE_MANAGE_DOCUMENTS, "Domain Admins" },
+
+ /* User 3 is da man */
+
+ { SEC_ACE_TYPE_ACCESS_ALLOWED, SEC_ACE_FLAG_CONTAINER_INHERIT,
+ PRINTER_ACE_FULL_CONTROL, "user3" },
+
+ { 0, 0, 0, NULL}
+};
+
+BOOL test_user(char *username, uint32 acc_desired, uint32 *acc_granted)
+{
+ struct passwd *pw;
+ uint32 status;
+
+ if (!(pw = getpwnam(username))) {
+ printf("FAIL: could not lookup user info for %s\n",
+ username);
+ exit(1);
+ }
+
+ return se_access_check(sd, pw->pw_uid, pw->pw_gid, 0, NULL,
+ acc_desired, acc_granted, &status);
+}
+
+static char *pace_str(uint32 ace_flags)
+{
+ if ((ace_flags & PRINTER_ACE_FULL_CONTROL) ==
+ PRINTER_ACE_FULL_CONTROL) return "full control";
+
+ if ((ace_flags & PRINTER_ACE_MANAGE_DOCUMENTS) ==
+ PRINTER_ACE_MANAGE_DOCUMENTS) return "manage documents";
+
+ if ((ace_flags & PRINTER_ACE_PRINT) == PRINTER_ACE_PRINT)
+ return "print";
+
+ return "UNKNOWN";
+}
+
+uint32 perms[] = {
+ PRINTER_ACE_PRINT,
+ PRINTER_ACE_FULL_CONTROL,
+ PRINTER_ACE_MANAGE_DOCUMENTS,
+ 0
+};
+
+void runtest(void)
+{
+ uint32 acc_granted;
+ BOOL result;
+ int i, j;
+
+ for (i = 0; perms[i]; i++) {
+
+ /* Test 10 users */
+
+ for (j = 0; j < 10; j++) {
+ fstring name;
+
+ /* Test user against ACL */
+
+ snprintf(name, sizeof(fstring), "%s/user%d",
+ getenv("TEST_WORKGROUP"), j);
+
+ result = test_user(name, perms[i], &acc_granted);
+
+ printf("%s: %s %s 0x%08x\n", name,
+ pace_str(perms[i]),
+ result ? "TRUE " : "FALSE", acc_granted);
+
+ /* Check results */
+
+ switch (perms[i]) {
+
+ case PRINTER_ACE_PRINT: {
+ if (!result || acc_granted !=
+ PRINTER_ACE_PRINT) {
+ printf("FAIL: user %s can't print\n",
+ name);
+ failed = True;
+ }
+ break;
+ }
+
+ case PRINTER_ACE_FULL_CONTROL: {
+ if (j == 3) {
+ if (!result || acc_granted !=
+ PRINTER_ACE_FULL_CONTROL) {
+ printf("FAIL: user %s doesn't "
+ "have full control\n",
+ name);
+ failed = True;
+ }
+ } else {
+ if (result || acc_granted != 0) {
+ printf("FAIL: user %s has full "
+ "control\n", name);
+ failed = True;
+ }
+ }
+ break;
+ }
+ case PRINTER_ACE_MANAGE_DOCUMENTS: {
+ if (j == 1 || j == 2) {
+ if (!result || acc_granted !=
+ PRINTER_ACE_MANAGE_DOCUMENTS) {
+ printf("FAIL: user %s can't "
+ "manage documents\n",
+ name);
+ failed = True;
+ }
+ } else {
+ if (result || acc_granted != 0) {
+ printf("FAIL: user %s can "
+ "manage documents\n",
+ name);
+ failed = True;
+ }
+ }
+ break;
+ }
+
+ default:
+ printf("FAIL: internal error\n");
+ exit(1);
+ }
+ }
+ }
+}
+
+/* Main function */
+
+int main(int argc, char **argv)
+{
+ /* Initialisation */
+
+ generate_wellknown_sids();
+
+ /* Create security descriptor */
+
+ sd = build_sec_desc(acl_printer, NULL, NULL_SID, NULL_SID);
+
+ if (!sd) {
+ printf("FAIL: could not build security descriptor\n");
+ return 1;
+ }
+
+ /* Run test */
+
+ runtest();
+
+ /* Return */
+
+ if (!failed) {
+ printf("PASS\n");
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/testsuite/smbd/se_access_check_utils.c b/testsuite/smbd/se_access_check_utils.c
new file mode 100644
index 00000000000..316bb5d905a
--- /dev/null
+++ b/testsuite/smbd/se_access_check_utils.c
@@ -0,0 +1,158 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Security context tests
+ 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 "se_access_check_utils.h"
+
+void char_to_sid(DOM_SID *sid, char *sid_str)
+{
+ /* If it looks like a SID, call string_to_sid() else look it up
+ using wbinfo. */
+
+ if (strncmp(sid_str, "S-", 2) == 0) {
+ string_to_sid(sid, sid_str);
+ } else {
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.data.name, sid_str);
+ if (winbindd_request(WINBINDD_LOOKUPNAME, &request,
+ &response) != NSS_STATUS_SUCCESS) {
+ printf("FAIL: unable to look up sid for name %s\n",
+ sid_str);
+ exit(1);
+ }
+
+ string_to_sid(sid, response.data.sid.sid);
+ printf("converted char %s to sid %s\n", sid_str,
+ response.data.sid.sid);
+ }
+}
+
+/* Construct an ACL from a list of ace_entry structures */
+
+SEC_ACL *build_acl(struct ace_entry *ace_list)
+{
+ SEC_ACE *aces = NULL;
+ SEC_ACL *result;
+ int num_aces = 0;
+
+ if (ace_list == NULL) return NULL;
+
+ /* Create aces */
+
+ while(ace_list->sid) {
+ SEC_ACCESS sa;
+ DOM_SID sid;
+
+ /* Create memory for new ACE */
+
+ if (!(aces = Realloc(aces,
+ sizeof(SEC_ACE) * (num_aces + 1)))) {
+ return NULL;
+ }
+
+ /* Create ace */
+
+ init_sec_access(&sa, ace_list->mask);
+
+ char_to_sid(&sid, ace_list->sid);
+ init_sec_ace(&aces[num_aces], &sid, ace_list->type,
+ sa, ace_list->flags);
+
+ num_aces++;
+ ace_list++;
+ }
+
+ /* Create ACL from list of ACEs */
+
+ result = make_sec_acl(ACL_REVISION, num_aces, aces);
+ free(aces);
+
+ return result;
+}
+
+/* Make a security descriptor */
+
+SEC_DESC *build_sec_desc(struct ace_entry *dacl, struct ace_entry *sacl,
+ char *owner_sid, char *group_sid)
+{
+ DOM_SID the_owner_sid, the_group_sid;
+ SEC_ACL *the_dacl, *the_sacl;
+ SEC_DESC *result;
+ size_t size;
+
+ /* Build up bits of security descriptor */
+
+ char_to_sid(&the_owner_sid, owner_sid);
+ char_to_sid(&the_group_sid, group_sid);
+
+ the_dacl = build_acl(dacl);
+ the_sacl = build_acl(sacl);
+
+ result = make_sec_desc(SEC_DESC_REVISION,
+ SEC_DESC_SELF_RELATIVE | SEC_DESC_DACL_PRESENT,
+ &the_owner_sid, &the_group_sid,
+ the_sacl, the_dacl, &size);
+
+ free_sec_acl(&the_dacl);
+ free_sec_acl(&the_sacl);
+
+ return result;
+}
+
+/* Iterate over password database and call a user-specified function */
+
+void visit_pwdb(BOOL (*fn)(struct passwd *pw, int ngroups, gid_t *groups))
+{
+ struct passwd *pw;
+ int ngroups;
+ gid_t *groups;
+
+ setpwent();
+
+ while ((pw = getpwent())) {
+ BOOL result;
+
+ /* Get grouplist */
+
+ ngroups = getgroups(0, NULL);
+
+ groups = malloc(sizeof(gid_t) * ngroups);
+ getgroups(ngroups, groups);
+
+ /* Call function */
+
+ result = fn(pw, ngroups, groups);
+ if (!result) break;
+
+ /* Clean up */
+
+ free(groups);
+ }
+
+ endpwent();
+}
diff --git a/testsuite/smbd/se_access_check_utils.h b/testsuite/smbd/se_access_check_utils.h
new file mode 100644
index 00000000000..bae0d171abf
--- /dev/null
+++ b/testsuite/smbd/se_access_check_utils.h
@@ -0,0 +1,46 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Security context tests
+ 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 _SE_ACCESS_CHECK_UTILS_H
+#define _SE_ACCESS_CHECK_UTILS_H
+
+#include "includes.h"
+
+/* Structure to build ACE lists from */
+
+struct ace_entry {
+ uint8 type, flags;
+ uint32 mask;
+ char *sid;
+};
+
+#define NULL_SID "S-1-0-0"
+#define WORLD_SID "S-1-1-0"
+
+/* Function prototypes */
+
+SEC_ACL *build_acl(struct ace_entry *ace_list);
+SEC_DESC *build_sec_desc(struct ace_entry *dacl, struct ace_entry *sacl,
+ char *owner_sid, char *group_sid);
+
+void visit_pwdb(BOOL (*fn)(struct passwd *pw, int ngroups, gid_t *groups));
+
+#endif
diff --git a/testsuite/smbd/sec_ctx.exp b/testsuite/smbd/sec_ctx.exp
new file mode 100644
index 00000000000..0831400e702
--- /dev/null
+++ b/testsuite/smbd/sec_ctx.exp
@@ -0,0 +1,67 @@
+#
+# @(#) Test the push_sec_ctx() and pop_sec_ctx() functions
+#
+
+#
+# Unix SMB/Netbios implementation.
+# 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.
+#
+
+load_lib "compile.exp"
+load_lib "util-defs.exp"
+
+# Non-root test
+
+set test_desc "change sec_ctx as non-root"
+set test_prog "sec_ctx_nonroot"
+simple_make "sec_ctx" $test_prog
+set output [util_start "$srcdir/$subdir/$test_prog"]
+
+if { [regexp "child killed" $output] } {
+ pass $test_desc
+ file delete "$srcdir/$subdir/$test_prog" "$srcdir/$subdir/$test_prog.o"
+} else {
+ fail $test_desc
+}
+
+# Run tests from C files as root
+
+set sec_ctx_tests [list \
+ { "security contexts are stackable" "sec_ctx_stack" } \
+ { "over/underflow tests" "sec_ctx_flow" } \
+ { "torture test" "sec_ctx_torture" } \
+ { "current_user global" "sec_ctx_current_user" } \
+ { "group membership" "sec_ctx_groups" } \
+ { "become root" "sec_ctx_root" } \
+ ]
+
+foreach { test } $sec_ctx_tests {
+ set test_desc [lindex $test 0]
+ set test_file [lindex $test 1]
+
+ simple_make "sec_ctx" $test_file
+ set output [util_start "sudo $srcdir/$subdir/$test_file" ]
+
+ if { [regexp "PASS" $output] } {
+ pass $test_desc
+ file delete "$srcdir/$subdir/$test_file" "$srcdir/$subdir/$test_file.o"
+ } else {
+ fail $test_desc
+ puts $output
+ }
+
+}
diff --git a/testsuite/smbd/sec_ctx1.c b/testsuite/smbd/sec_ctx1.c
new file mode 100644
index 00000000000..b74b6ed9009
--- /dev/null
+++ b/testsuite/smbd/sec_ctx1.c
@@ -0,0 +1,40 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Security context tests
+ 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"
+
+void exit_server(char *reason) {}
+
+int main (int argc, char **argv)
+{
+ /* Become a non-root user */
+
+ setuid(1);
+ setgid(1);
+
+ /* Try to push a security context. This should fail with a
+ smb_assert() error. */
+
+ push_sec_ctx(2, 2);
+ printf("FAIL\n");
+
+ return 0;
+}
diff --git a/testsuite/smbd/sec_ctx_current_user.c b/testsuite/smbd/sec_ctx_current_user.c
new file mode 100644
index 00000000000..5b7da0ef464
--- /dev/null
+++ b/testsuite/smbd/sec_ctx_current_user.c
@@ -0,0 +1,114 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Security context tests
+ 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 "sec_ctx_utils.h"
+
+int main(int argc, char **argv)
+{
+ extern struct current_user current_user;
+ uid_t initial_uid = current_user.uid;
+ gid_t initial_gid = current_user.gid;
+ int ngroups;
+ gid_t *groups;
+
+ init_sec_ctx();
+
+ /* Check initial id */
+
+ if (initial_uid != 0 || initial_gid != 0) {
+ printf("FAIL: current_user not initialised to root\n");
+ return 1;
+ }
+
+ /* Push a context and check current user is updated */
+
+ if (!push_sec_ctx()) {
+ printf("FAIL: push_sec_ctx\n");
+ return 1;
+ }
+
+ set_sec_ctx(1, 2, 0, NULL);
+
+ if (current_user.uid != 1 || current_user.gid != 2) {
+ printf("FAIL: current_user id not updated after push\n");
+ return 1;
+ }
+
+ if (current_user.ngroups != 0 || current_user.groups) {
+ printf("FAIL: current_user groups not updated after push\n");
+ return 1;
+ }
+
+ /* Push another */
+
+ get_random_grouplist(&ngroups, &groups);
+
+ if (!push_sec_ctx()) {
+ printf("FAIL: push_sec_ctx\n");
+ return 1;
+ }
+
+ set_sec_ctx(2, 3, ngroups, groups);
+
+ if (current_user.uid != 2 || current_user.gid != 3) {
+ printf("FAIL: current_user id not updated after second "
+ "push\n");
+ return 1;
+ }
+
+ if (current_user.ngroups != ngroups ||
+ (memcmp(current_user.groups, groups,
+ sizeof(gid_t) * ngroups) != 0)) {
+ printf("FAIL: current_user groups not updated\n");
+ return 1;
+ }
+
+ /* Pop them both off */
+
+ if (!pop_sec_ctx()) {
+ printf("FAIL: pop_sec_ctx\n");
+ return 1;
+ }
+
+ if (current_user.uid != 1 || current_user.gid != 2) {
+ printf("FAIL: current_user not updaded pop\n");
+ return 1;
+ }
+
+ if (!pop_sec_ctx()) {
+ printf("FAIL: pop_sec_ctx\n");
+ return 1;
+ }
+
+ /* Check initial state was returned */
+
+ if (current_user.uid != initial_uid ||
+ current_user.gid != initial_gid) {
+ printf("FAIL: current_user not updaded pop\n");
+ return 1;
+ }
+
+ /* Everything's cool */
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/testsuite/smbd/sec_ctx_flow.c b/testsuite/smbd/sec_ctx_flow.c
new file mode 100644
index 00000000000..7b251784671
--- /dev/null
+++ b/testsuite/smbd/sec_ctx_flow.c
@@ -0,0 +1,73 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Security context tests
+ 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 "sec_ctx_utils.h"
+
+int main (int argc, char **argv)
+{
+ int i;
+
+ init_sec_ctx();
+
+ /* Check for underflow */
+
+ if (!push_sec_ctx()) {
+ printf("FAIL: push_sec_ctx\n");
+ return 1;
+ }
+
+ set_sec_ctx(1, 1, 0, NULL);
+
+ if (!pop_sec_ctx()) {
+ printf("FAIL: pop_sec_ctx\n");
+ return 1;
+ }
+
+ if (pop_sec_ctx()) {
+ printf("FAIL: underflow push_sec_ctx\n");
+ return 1;
+ }
+
+ /* Check for overflow */
+
+ for (i = 0; i < MAX_SEC_CTX_DEPTH + 1; i++) {
+ BOOL result;
+
+ result = push_sec_ctx();
+ set_sec_ctx(i, i, 0, NULL);
+
+ if ((i < MAX_SEC_CTX_DEPTH) && !result) {
+ printf("FAIL: push_sec_ctx(%d)\n", i);
+ return 1;
+ }
+
+ if ((i == MAX_SEC_CTX_DEPTH + 1) && result) {
+ printf("FAIL: overflow push_sec_ctx(%d)\n", i);
+ return 1;
+ }
+ }
+
+ /* Everything's cool */
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/testsuite/smbd/sec_ctx_groups.c b/testsuite/smbd/sec_ctx_groups.c
new file mode 100644
index 00000000000..61d77f6f4f0
--- /dev/null
+++ b/testsuite/smbd/sec_ctx_groups.c
@@ -0,0 +1,131 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Security context tests
+ 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 "sec_ctx_utils.h"
+
+int main (int argc, char **argv)
+{
+ int ngroups, initial_ngroups, check_ngroups, final_ngroups;
+ gid_t *groups, *initial_groups, *check_groups, *final_groups;
+ int i;
+
+ init_sec_ctx();
+
+ /* Save current groups */
+
+ initial_ngroups = sys_getgroups(0, NULL);
+ initial_groups = malloc(sizeof(gid_t) * initial_ngroups);
+ sys_getgroups(initial_ngroups, initial_groups);
+
+ printf("Initial groups are: ");
+ for (i = 0; i < initial_ngroups; i++) {
+ printf("%d, ", initial_groups[i]);
+ }
+ printf("\n");
+
+ /* Push a context plus groups */
+
+ get_random_grouplist(&ngroups, &groups);
+
+ printf("Random groups are: ");
+ for (i = 0; i < ngroups; i++) {
+ printf("%d, ", groups[i]);
+ }
+ printf("\n");
+
+ if (!push_sec_ctx()) {
+ printf("FAIL: push_sec_ctx\n");
+ return 1;
+ }
+
+ set_sec_ctx(1, 2, ngroups, groups);
+
+ /* Check grouplist stuck */
+
+ check_ngroups = sys_getgroups(0, NULL);
+ check_groups = malloc(sizeof(gid_t) * check_ngroups);
+ sys_getgroups(check_ngroups, check_groups);
+
+ printf("Actual groups are: ");
+ for (i = 0; i < check_ngroups; i++) {
+ printf("%d, ", check_groups[i]);
+ }
+ printf("\n");
+
+ if (ngroups != check_ngroups) {
+ printf("FAIL: number of groups differs\n");
+ return 1;
+ }
+
+ for (i = 0; i < ngroups; i++) {
+ if (groups[i] != check_groups[i]) {
+ printf("FAIL: group %d differs\n", i);
+ return 1;
+ }
+ }
+
+ safe_free(groups);
+ safe_free(check_groups);
+
+ /* Pop and check initial groups are back */
+
+ if (!pop_sec_ctx()) {
+ printf("FAIL: pop_sec_ctx\n");
+ return 1;
+ }
+
+ final_ngroups = sys_getgroups(0, NULL);
+ final_groups = malloc(sizeof(gid_t) * final_ngroups);
+ sys_getgroups(final_ngroups, final_groups);
+
+ printf("Final groups are: ");
+ for (i = 0; i < final_ngroups; i++) {
+ printf("%d, ", final_groups[i]);
+ }
+ printf("\n");
+
+ if (initial_ngroups != final_ngroups) {
+ printf("FAIL: final number of groups differ\n");
+ return 1;
+ }
+
+ for (i = 0; i < initial_ngroups; i++) {
+ if (initial_groups[i] != final_groups[i]) {
+ printf("FAIL: final group %d differs\n", i);
+ return 1;
+ }
+ }
+
+ printf("Final groups are: ");
+ for (i = 0; i < final_ngroups; i++) {
+ printf("%d, ", final_groups[i]);
+ }
+ printf("\n");
+
+ safe_free(initial_groups);
+ safe_free(final_groups);
+
+ /* Everything's cool */
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/testsuite/smbd/sec_ctx_nonroot.c b/testsuite/smbd/sec_ctx_nonroot.c
new file mode 100644
index 00000000000..18bba7e2806
--- /dev/null
+++ b/testsuite/smbd/sec_ctx_nonroot.c
@@ -0,0 +1,42 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Security context tests
+ 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 "sec_ctx_utils.h"
+
+int main (int argc, char **argv)
+{
+ init_sec_ctx();
+
+ /* Become a non-root user */
+
+ setuid(1);
+ setgid(1);
+
+ /* Try to push a security context. This should fail with a
+ smb_assert() error. */
+
+ push_sec_ctx();
+ set_sec_ctx(2, 2, 0, NULL);
+ printf("FAIL\n");
+
+ return 0;
+}
diff --git a/testsuite/smbd/sec_ctx_root.c b/testsuite/smbd/sec_ctx_root.c
new file mode 100644
index 00000000000..f2e46f0a5cd
--- /dev/null
+++ b/testsuite/smbd/sec_ctx_root.c
@@ -0,0 +1,61 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Security context tests
+ 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 "sec_ctx_utils.h"
+
+int main (int argc, char **argv)
+{
+ int ngroups, actual_ngroups;
+ gid_t *groups, *actual_groups;
+ extern struct current_user current_user;
+
+ init_sec_ctx();
+
+ /* Initialise a security context */
+
+ get_random_grouplist(&ngroups, &groups);
+ set_sec_ctx(1, 1, ngroups, groups);
+
+ /* Become root and check */
+
+ set_root_sec_ctx();
+
+ actual_ngroups = getgroups(0, NULL);
+ actual_groups = (gid_t *)malloc(actual_ngroups * sizeof(gid_t));
+
+ getgroups(actual_ngroups, actual_groups);
+
+ if (geteuid() != 0 || getegid() != 0 || actual_ngroups != 0) {
+ printf("FAIL: root id not set\n");
+ return 1;
+ }
+
+ if (current_user.uid != 0 || current_user.gid != 0 ||
+ current_user.ngroups != 0 || current_user.groups) {
+ printf("FAIL: current_user not set correctly\n");
+ return 1;
+ }
+
+ printf("PASS\n");
+
+ return 0;
+}
diff --git a/testsuite/smbd/sec_ctx_stack.c b/testsuite/smbd/sec_ctx_stack.c
new file mode 100644
index 00000000000..f6952fabb4e
--- /dev/null
+++ b/testsuite/smbd/sec_ctx_stack.c
@@ -0,0 +1,86 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Security context tests
+ 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 "sec_ctx_utils.h"
+
+int main (int argc, char **argv)
+{
+ BOOL result;
+ int i;
+
+ init_sec_ctx();
+
+ /* Push a whole bunch of security contexts */
+
+ for (i = 0; i < MAX_SEC_CTX_DEPTH; i++) {
+
+ result = push_sec_ctx();
+ set_sec_ctx(i + 1, i + 2, 0, NULL);
+
+ if (!result) {
+ printf("FAIL: push_sec_ctx(%d)\n", i);
+ return 1;
+ }
+
+ printf("pushed context (%d, %d) eff=(%d, %d)\n",
+ getuid(), getgid(), geteuid(), getegid());
+
+ if ((geteuid() != i + 1) || (getegid() != i + 2)) {
+ printf("FAIL: incorrect context pushed\n");
+ return 1;
+ }
+ }
+
+ /* Pop them all off */
+
+ for (i = MAX_SEC_CTX_DEPTH; i > 0; i--) {
+
+ result = pop_sec_ctx();
+
+ if (!result) {
+ printf("FAIL: pop_sec_ctx(%d)\n", i);
+ return 1;
+ }
+
+ printf("popped context (%d, %d) eff=(%d, %d)\n",
+ getuid(), getgid(), geteuid(), getegid());
+
+ printf("i = %d\n",i);
+
+ if (i > 1) {
+ if ((geteuid() != i - 1) || (getegid() != i)) {
+ printf("FAIL: incorrect context popped\n");
+ return 1;
+ }
+ } else {
+ if ((geteuid() != 0) || (getegid() != 0)) {
+ printf("FAIL: incorrect context popped\n");
+ return 1;
+ }
+ }
+ }
+
+ /* Everything's cool */
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/testsuite/smbd/sec_ctx_torture.c b/testsuite/smbd/sec_ctx_torture.c
new file mode 100644
index 00000000000..effee069ef9
--- /dev/null
+++ b/testsuite/smbd/sec_ctx_torture.c
@@ -0,0 +1,103 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Security context tests
+ 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 "sec_ctx_utils.h"
+
+#define NUM_TESTS 10000
+
+int main (int argc, char **argv)
+{
+ int seed, level = 0, num_tests = 0;
+
+ init_sec_ctx();
+
+ if (argc == 1) {
+ seed = time(NULL);
+ } else {
+ seed = atoi(argv[1]);
+ }
+
+ printf("seed = %d\n", seed);
+
+ while(num_tests < NUM_TESTS) {
+ switch (random() % 2) {
+
+ /* Push a random context */
+
+ case 0:
+ if (level < MAX_SEC_CTX_DEPTH) {
+ int ngroups;
+ gid_t *groups;
+
+ if (!push_sec_ctx()) {
+ printf("FAIL: push random ctx\n");
+ return 1;
+ }
+
+ get_random_grouplist(&ngroups, &groups);
+
+ set_sec_ctx(random() % 32767,
+ random() % 32767,
+ ngroups, groups);
+
+ if (!verify_current_groups(ngroups,
+ groups)) {
+ printf("FAIL: groups did not stick\n");
+ return 1;
+ }
+
+ printf("pushed (%d, %d) eff=(%d, %d)\n",
+ getuid(), getgid(), geteuid(),
+ getegid());
+
+ level++;
+ num_tests++;
+
+ free(groups);
+ }
+ break;
+
+ /* Pop a random context */
+
+ case 1:
+ if (level > 0) {
+ if (!pop_sec_ctx()) {
+ printf("FAIL: pop random ctx\n");
+ return 1;
+ }
+
+ printf("popped (%d, %d) eff=(%d, %d)\n",
+ getuid(), getgid(), geteuid(),
+ getegid());
+
+ level--;
+ num_tests++;
+ }
+ break;
+ }
+ }
+
+ /* Everything's cool */
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/testsuite/smbd/sec_ctx_utils.c b/testsuite/smbd/sec_ctx_utils.c
new file mode 100644
index 00000000000..fbda6352c46
--- /dev/null
+++ b/testsuite/smbd/sec_ctx_utils.c
@@ -0,0 +1,65 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Security context tests
+ 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"
+
+/* Keep linker happy */
+
+void exit_server(char *reason) {}
+
+/* Generate random list of groups */
+
+void get_random_grouplist(int *ngroups, gid_t **groups)
+{
+ int i;
+
+ *ngroups = random() % groups_max();
+ *groups = malloc(*ngroups * sizeof(gid_t));
+
+ if (!groups) {
+ printf("FAIL: malloc random grouplist\n");
+ return;
+ }
+
+ for (i = 0; i < *ngroups; i++) {
+ (*groups)[i] = random() % 32767;
+ }
+}
+
+/* Check a list of groups with current groups */
+
+BOOL verify_current_groups(int ngroups, gid_t *groups)
+{
+ int actual_ngroups;
+ gid_t *actual_groups;
+
+ actual_ngroups = getgroups(0, NULL);
+ actual_groups = (gid_t *)malloc(actual_ngroups * sizeof(gid_t));
+
+ getgroups(actual_ngroups, actual_groups);
+
+ if (actual_ngroups != ngroups) {
+ return False;
+ }
+
+ return memcmp(actual_groups, groups, actual_ngroups *
+ sizeof(gid_t)) == 0;
+}
diff --git a/testsuite/smbd/sec_ctx_utils.h b/testsuite/smbd/sec_ctx_utils.h
new file mode 100644
index 00000000000..1f4bde841ed
--- /dev/null
+++ b/testsuite/smbd/sec_ctx_utils.h
@@ -0,0 +1,30 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Security context tests
+ 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 _SEC_CTX_UTILS_H
+#define _SEC_CTX_UTILS_H
+
+/* Function prototypes */
+
+void get_random_grouplist(int *ngroups, gid_t **groups);
+BOOL verify_current_groups(int ngroups, gid_t *groups);
+
+#endif /* _SEC_CTX_UTILS_H */
diff --git a/testsuite/smbd/sighup.exp b/testsuite/smbd/sighup.exp
new file mode 100644
index 00000000000..a9e1bffe11d
--- /dev/null
+++ b/testsuite/smbd/sighup.exp
@@ -0,0 +1,107 @@
+#
+# @(#) Check services file reloaded after SIGHUP
+#
+
+load_lib "util-defs.exp"
+
+# Create a smb.conf file from a list of sections. Each section consists of
+# a name and a list of lines which are the contents of that section.
+# Returns a temporary filename which must be deleted after use.
+
+proc write_smb_conf { args } {
+
+ # Set up temporary file
+
+ set name "/tmp/smb.conf-test-[pid]"
+ set f [open $name "w"]
+
+ # Parse sections
+
+ foreach section [lindex $args 0] {
+ set secname [lindex $section 0]
+ set contents [lindex $section 1]
+
+ puts $f "\[$secname]"
+
+ foreach { line } $contents {
+ puts $f "\t$line"
+ }
+
+ puts $f ""
+ }
+
+ close $f
+
+ # Return filename of smb.conf file
+
+ return $name
+}
+
+proc append_smb_conf { args } {
+
+ set name [lindex $args 0]
+ set f [open $name "a"]
+
+ foreach section [lindex $args 1] {
+ set secname [lindex $section 0]
+ set contents [lindex $section 1]
+
+ puts $f "\[$secname]"
+
+ foreach { line } $contents {
+ puts $f "\t$line"
+ }
+
+ puts $f ""
+ }
+
+ close $f
+}
+
+# Create a smb.conf file
+
+set smb_conf [list \
+ [list "global" \
+ [list "netbios name = testing" \
+ "guest ok = true"]]]
+
+set name [write_smb_conf $smb_conf]
+
+# Run smbd and smbclient output
+
+set smbd_output [util_start "bin/smbd" "-s $name"]
+set nmbd_output [util_start "bin/nmbd" "-s $name"]
+
+sleep 5
+
+set smbclient_output [util_start "bin/smbclient -L //testing -N"]
+verbose $smbclient_output
+
+if { ![regexp "Anonymous login successful" $smbclient_output] } {
+ untested "smbd could not be started"
+ util_start "killall" "smbd nmbd"
+ file delete $name
+ return
+}
+
+# Append another share and sighup
+
+append_smb_conf $name [list [list "tmp" [list "browseable = true"]]]
+set output [util_start "killall" "-HUP smbd"]
+verbose $output
+
+sleep 2
+
+set smbclient_output2 [util_start "bin/smbclient -L //testing -N"]
+verbose $smbclient_output2
+
+if { [regexp "tmp.*Disk" $smbclient_output2] } {
+ pass "sighup reload"
+} else {
+ fail "sighup reload"
+}
+
+# Clean up
+
+util_start "killall" "smbd nmbd"
+file delete $name